I have an application that I want to export high-resolution (or rather, high pixel density?) images for printing - for example, I want images that print at 250 dots per inch (DPI), instead of the default, which I understand to be 72 DPI.
I'm using a BufferedImage with a Graphics2D object to draw the image, then ImageIO.write() to save the image.
Any idea how I can set the DPI?
Kurt's answer showed the way, still it took me quite some time to get it run, so here is the code that sets DPI when saving a PNG. There is a lot to do to get the proper writers and such...
private BufferedImage gridImage;
...
private void saveGridImage(File output) throws IOException {
output.delete();
final String formatName = "png";
for (Iterator<ImageWriter> iw = ImageIO.getImageWritersByFormatName(formatName); iw.hasNext();) {
ImageWriter writer = iw.next();
ImageWriteParam writeParam = writer.getDefaultWriteParam();
ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
IIOMetadata metadata = writer.getDefaultImageMetadata(typeSpecifier, writeParam);
if (metadata.isReadOnly() || !metadata.isStandardMetadataFormatSupported()) {
continue;
}
setDPI(metadata);
final ImageOutputStream stream = ImageIO.createImageOutputStream(output);
try {
writer.setOutput(stream);
writer.write(metadata, new IIOImage(gridImage, null, metadata), writeParam);
} finally {
stream.close();
}
break;
}
}
private void setDPI(IIOMetadata metadata) throws IIOInvalidTreeException {
// for PMG, it's dots per millimeter
double dotsPerMilli = 1.0 * DPI / 10 / INCH_2_CM;
IIOMetadataNode horiz = new IIOMetadataNode("HorizontalPixelSize");
horiz.setAttribute("value", Double.toString(dotsPerMilli));
IIOMetadataNode vert = new IIOMetadataNode("VerticalPixelSize");
vert.setAttribute("value", Double.toString(dotsPerMilli));
IIOMetadataNode dim = new IIOMetadataNode("Dimension");
dim.appendChild(horiz);
dim.appendChild(vert);
IIOMetadataNode root = new IIOMetadataNode("javax_imageio_1.0");
root.appendChild(dim);
metadata.mergeTree("javax_imageio_1.0", root);
}
Seting up TIFF DPI
If you want to set dpi for TIFF, try to do that by next steps:
private static IIOMetadata createMetadata(ImageWriter writer, ImageWriteParam writerParams, int resolution) throws
IIOInvalidTreeException
{
// Get default metadata from writer
ImageTypeSpecifier type = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY);
IIOMetadata meta = writer.getDefaultImageMetadata(type, writerParams);
// Convert default metadata to TIFF metadata
TIFFDirectory dir = TIFFDirectory.createFromMetadata(meta);
// Get {X,Y} resolution tags
BaselineTIFFTagSet base = BaselineTIFFTagSet.getInstance();
TIFFTag tagXRes = base.getTag(BaselineTIFFTagSet.TAG_X_RESOLUTION);
TIFFTag tagYRes = base.getTag(BaselineTIFFTagSet.TAG_Y_RESOLUTION);
// Create {X,Y} resolution fields
TIFFField fieldXRes = new TIFFField(tagXRes, TIFFTag.TIFF_RATIONAL, 1, new long[][] { { resolution, 1 } });
TIFFField fieldYRes = new TIFFField(tagYRes, TIFFTag.TIFF_RATIONAL, 1, new long[][] { { resolution, 1 } });
// Add {X,Y} resolution fields to TIFFDirectory
dir.addTIFFField(fieldXRes);
dir.addTIFFField(fieldYRes);
// Add unit field to TIFFDirectory (change to RESOLUTION_UNIT_CENTIMETER if necessary)
dir.addTIFFField(new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT), BaselineTIFFTagSet.RESOLUTION_UNIT_INCH));
// Return TIFF metadata so it can be picked up by the IIOImage
return dir.getAsMetadata();
}
Also, similar way you can setting up any TIFF tag.
Read more at the source
i am using this code for tiff file in my project and it works well..
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.media.jai.NullOpImage;
import javax.media.jai.OpImage;
import javax.media.jai.PlanarImage;
import com.sun.media.jai.codec.FileSeekableStream;
import com.sun.media.jai.codec.ImageCodec;
import com.sun.media.jai.codec.ImageDecoder;
import com.sun.media.jai.codec.ImageEncoder;
import com.sun.media.jai.codec.SeekableStream;
import com.sun.media.jai.codec.TIFFEncodeParam;
import com.sun.media.jai.codec.TIFFField;
class SetDDPI
{
static void tiff_Maker(List<BufferedImage> output, String result) throws IOException
{
TIFFEncodeParam params = new TIFFEncodeParam();
OutputStream out = new FileOutputStream(result);
List<BufferedImage> imageList = new ArrayList<BufferedImage>();
for (int i = 1; i < output.size(); i++)
{
imageList.add(output.get(i));
}
params.setWriteTiled(true);
params.setCompression(TIFFEncodeParam.COMPRESSION_GROUP4);
params.setExtraImages(imageList.iterator());
TIFFField[] extras = new TIFFField[2];
extras[0] = new TIFFField(282, TIFFField.TIFF_RATIONAL, 1, (Object) new long[][] { { (long) 300, (long) 1 },
{ (long) 0, (long) 0 } });
extras[1] = new TIFFField(283, TIFFField.TIFF_RATIONAL, 1, (Object) new long[][] { { (long) 300, (long) 1 },
{ (long) 0, (long) 0 } });
params.setExtraFields(extras);
ImageEncoder encoder = ImageCodec.createImageEncoder("tiff", out, params);
encoder.encode(output.get(0));
out.close();
}
static List<BufferedImage> tiff_Extractor(File tiff) throws IOException
{
List<BufferedImage> images = new ArrayList<BufferedImage>();
SeekableStream ss = new FileSeekableStream(tiff);
ImageDecoder decoder = ImageCodec.createImageDecoder("tiff", ss, null);
int numPages = decoder.getNumPages();
for (int j = 0; j < numPages; j++)
{
PlanarImage op = new NullOpImage(decoder.decodeAsRenderedImage(j), null, null, OpImage.OP_IO_BOUND);
images.add(op.getAsBufferedImage());
}
return images;
}
}
this is to set 300 DPI of Tiff image. you can change it according to your need.
extras[0] = new TIFFField(282, TIFFField.TIFF_RATIONAL, 1, (Object) new
long[][] { { (long) 300, (long) 1 },{ (long) 0, (long) 0 } });
extras[1] = new TIFFField(283, TIFFField.TIFF_RATIONAL, 1, (Object) new
long[][] { { (long) 300, (long) 1 },{ (long) 0, (long) 0 } });
Related
I'm trying to read a multi page tiff and blackout some content and then write it again.
Code :-
public void blackOut(File file, File outputTiff, String compressionType) throws IOException
{
ImageReader reader = getImageReader(file);
int pageCount = reader.getNumImages(true);
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(TIFF_FORMAT);
if (!writers.hasNext())
{
throw new RuntimeException(JAI_IMAGE_WRITER_MESSAGE);
}
ImageWriter writer = writers.next();
TIFFImageWriteParam tiffWriteParam = new TIFFImageWriteParam(Locale.US);
if (compressionType != null)
{
tiffWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
tiffWriteParam.setCompressionType(compressionType);
}
IIOMetadata streamMetadata = writer.getDefaultStreamMetadata(tiffWriteParam);
try (ImageOutputStream ios = ImageIO.createImageOutputStream(outputTiff))
{
writer.setOutput(ios);
int dpiX = 300;
int dpiY = 300;
for (int i = 0; i < pageCount; i++)
{
BufferedImage bufferedImage = reader.read(i);
Graphics2D g2d = bufferedImage.createGraphics();
g2d.setColor(Color.BLACK);
g2d.setStroke(new BasicStroke(10));
g2d.fillRect(X, Y, Width, Height);
g2d.dispose();
IIOImage iioImage = new IIOImage(bufferedImage, null, null);
ImageTypeSpecifier imageType = ImageTypeSpecifier.createFromRenderedImage(iioImage.getRenderedImage());
ImageWriteParam param = writer.getDefaultWriteParam();
IIOMetadata imageMetadata = writer.getDefaultImageMetadata(imageType, param);
imageMetadata = setDPIViaAPI(imageMetadata, dpiX, dpiY);
iioImage.setMetadata(imageMetadata);
if (i == 0)
{
IIOImage firstIioImage = iioImage;
writer.write(streamMetadata, firstIioImage, tiffWriteParam);
}
else
{
writer.writeInsert(i, iioImage, tiffWriteParam);
}
}
}
finally
{
writer.dispose();
}
}
private static IIOMetadata setDPIViaAPI(IIOMetadata imageMetadata, int dpiX, int dpiY)
throws IIOInvalidTreeException
{
// Derive the TIFFDirectory from the metadata.
TIFFDirectory dir = TIFFDirectory.createFromMetadata(imageMetadata);
// Get {X,Y}Resolution tags.
BaselineTIFFTagSet base = BaselineTIFFTagSet.getInstance();
TIFFTag tagXRes = base.getTag(BaselineTIFFTagSet.TAG_X_RESOLUTION);
TIFFTag tagYRes = base.getTag(BaselineTIFFTagSet.TAG_Y_RESOLUTION);
// Create {X,Y}Resolution fields.
TIFFField fieldXRes = new TIFFField(tagXRes, TIFFTag.TIFF_RATIONAL,
1, new long[][] {{dpiX, 1}});
TIFFField fieldYRes = new TIFFField(tagYRes, TIFFTag.TIFF_RATIONAL,
1, new long[][] {{dpiY, 1}});
// Append {X,Y}Resolution fields to directory.
dir.addTIFFField(fieldXRes);
dir.addTIFFField(fieldYRes);
// Convert to metadata object.
IIOMetadata metadata = dir.getAsMetadata();
// Add other metadata.
IIOMetadataNode root = new IIOMetadataNode("javax_imageio_1.0");
IIOMetadataNode horiz = new IIOMetadataNode("HorizontalPixelSize");
horiz.setAttribute("value", Double.toString(25.4f / dpiX));
IIOMetadataNode vert = new IIOMetadataNode("VerticalPixelSize");
vert.setAttribute("value", Double.toString(25.4f / dpiY));
IIOMetadataNode dim = new IIOMetadataNode("Dimension");
dim.appendChild(horiz);
dim.appendChild(vert);
root.appendChild(dim);
metadata.mergeTree("javax_imageio_1.0", root);
return metadata;
}
while writing the file again I'm getting the file double (sometimes triple) the size of actual and that too after applying the compression 'LZW'.
Any way that I can get the same file size as previous?
Thanks.
Recently I am trying to implement an image object detection tool based on YOLO. To start with, I have used the codes here. Things sounds fine except the fact the program doesnt pass the following line of code (line 72) and will not go into the loop. :
if (cap.read(frame))
In other words, if a break point is placed at that line, the program wont go to next step.. Any idea how to fix this?
package yoloexample;
import org.opencv.core.*;
import org.opencv.dnn.*;
import org.opencv.utils.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import org.opencv.videoio.VideoCapture;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.WritableRaster;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class Yoloexample {
private static List<String> getOutputNames(Net net) {
List<String> names = new ArrayList<>();
List<Integer> outLayers = net.getUnconnectedOutLayers().toList();
List<String> layersNames = net.getLayerNames();
outLayers.forEach((item) -> names.add(layersNames.get(item - 1)));//unfold and create R-CNN layers from the loaded YOLO model//
System.out.println(names);
return names;
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
System.load("\\opencv\\opencv\\build\\java\\x64\\opencv_java420.dll"); // Load the openCV 4.0 dll //
String modelWeights = "g:\\yolov3.weights"; //Download and load only wights for YOLO , this is obtained from official YOLO site//
String modelConfiguration = "g:\\yolov3.cfg";//Download and load cfg file for YOLO , can be obtained from official site//
String filePath = "test.mp4"; //My video file to be analysed//
VideoCapture cap = new VideoCapture(filePath);// Load video using the videocapture method//
Mat frame = new Mat(); // define a matrix to extract and store pixel info from video//
//cap.read(frame);
JFrame jframe = new JFrame("Video"); // the lines below create a frame to display the resultant video with object detection and localization//
JLabel vidpanel = new JLabel();
jframe.setContentPane(vidpanel);
jframe.setSize(600, 600);
jframe.setVisible(true);// we instantiate the frame here//
Net net = Dnn.readNetFromDarknet(modelConfiguration, modelWeights); //OpenCV DNN supports models trained from various frameworks like Caffe and TensorFlow. It also supports various networks architectures based on YOLO//
//Thread.sleep(5000);
//Mat image = Imgcodecs.imread("D:\\yolo-object-detection\\yolo-object-detection\\images\\soccer.jpg");
Size sz = new Size(288, 288);
List<Mat> result = new ArrayList<>();
List<String> outBlobNames = getOutputNames(net);
while (true) {
if (cap.read(frame)) {
Mat blob = Dnn.blobFromImage(frame, 0.00392, sz, new Scalar(0), true, false); // We feed one frame of video into the network at a time, we have to convert the image to a blob. A blob is a pre-processed image that serves as the input.//
net.setInput(blob);
net.forward(result, outBlobNames); //Feed forward the model to get output //
// outBlobNames.forEach(System.out::println);
// result.forEach(System.out::println);
float confThreshold = 0.6f; //Insert thresholding beyond which the model will detect objects//
List<Integer> clsIds = new ArrayList<>();
List<Float> confs = new ArrayList<>();
List<Rect> rects = new ArrayList<>();
for (int i = 0; i < result.size(); ++i) {
// each row is a candidate detection, the 1st 4 numbers are
// [center_x, center_y, width, height], followed by (N-4) class probabilities
Mat level = result.get(i);
for (int j = 0; j < level.rows(); ++j) {
Mat row = level.row(j);
Mat scores = row.colRange(5, level.cols());
Core.MinMaxLocResult mm = Core.minMaxLoc(scores);
float confidence = (float) mm.maxVal;
Point classIdPoint = mm.maxLoc;
if (confidence > confThreshold) {
int centerX = (int) (row.get(0, 0)[0] * frame.cols()); //scaling for drawing the bounding boxes//
int centerY = (int) (row.get(0, 1)[0] * frame.rows());
int width = (int) (row.get(0, 2)[0] * frame.cols());
int height = (int) (row.get(0, 3)[0] * frame.rows());
int left = centerX - width / 2;
int top = centerY - height / 2;
clsIds.add((int) classIdPoint.x);
confs.add((float) confidence);
rects.add(new Rect(left, top, width, height));
}
}
}
float nmsThresh = 0.5f;
MatOfFloat confidences = new MatOfFloat(Converters.vector_float_to_Mat(confs));
Rect[] boxesArray = rects.toArray(new Rect[0]);
MatOfRect boxes = new MatOfRect(boxesArray);
MatOfInt indices = new MatOfInt();
Dnn.NMSBoxes(boxes, confidences, confThreshold, nmsThresh, indices); //We draw the bounding boxes for objects here//
int[] ind = indices.toArray();
int j = 0;
for (int i = 0; i < ind.length; ++i) {
int idx = ind[i];
Rect box = boxesArray[idx];
Imgproc.rectangle(frame, box.tl(), box.br(), new Scalar(0, 0, 255), 2);
//i=j;
System.out.println(idx);
}
// Imgcodecs.imwrite("D://out.png", image);
//System.out.println("Image Loaded");
ImageIcon image = new ImageIcon(Mat2bufferedImage(frame)); //setting the results into a frame and initializing it //
vidpanel.setIcon(image);
vidpanel.repaint();
System.out.println(j);
System.out.println("Done");
}
}
}
private static BufferedImage Mat2bufferedImage(Mat image) { // The class described here takes in matrix and renders the video to the frame //
MatOfByte bytemat = new MatOfByte();
Imgcodecs.imencode(".jpg", image, bytemat);
byte[] bytes = bytemat.toArray();
InputStream in = new ByteArrayInputStream(bytes);
BufferedImage img = null;
try {
img = ImageIO.read(in);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return img;
}
}
I am new to itext7.
I saw an example about columndocumentrenderer. code below.
package com.itextpdf.highlevel.chapter02;
import com.itextpdf.io.font.FontConstants;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.ColumnDocumentRenderer;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.AreaBreak;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.hyphenation.HyphenationConfig;
import com.itextpdf.layout.property.AreaBreakType;
import com.itextpdf.layout.property.TextAlignment;
import com.itextpdf.test.annotations.WrapToTest;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
/**
*
* #author RNDPC-03
*/#WrapToTest
public class C02E08_JekyllHydeV4 {
public static final String SRC = "src/main/resources/txt/jekyll_hyde.txt";
public static final String DEST = "results/chapter02/jekyll_hyde_v4.pdf";
public static void main(String args[]) throws IOException {
File file = new File(DEST);
file.getParentFile().mkdirs();
new C02E08_JekyllHydeV4().createPdf(DEST);
}
public void createPdf(String dest) throws IOException {
//Initialize PDF document
PdfDocument pdf = new PdfDocument(new PdfWriter(dest));
// Initialize document
Document document = new Document(pdf);
//Set column parameters
float offSet = 36;
float gutter = 23;
float columnWidth = (PageSize.A4.getWidth() - offSet * 2) / 2 - gutter;
float columnHeight = PageSize.A4.getHeight() - offSet * 2;
//Define column areas
Rectangle[] columns = {
new Rectangle(offSet, offSet, columnWidth, columnHeight),
new Rectangle(offSet + columnWidth + gutter, offSet, columnWidth, columnHeight)};
document.setRenderer(new ColumnDocumentRenderer(document, columns));
PdfFont font = PdfFontFactory.createFont(FontConstants.TIMES_ROMAN);
PdfFont bold = PdfFontFactory.createFont(FontConstants.HELVETICA_BOLD);
document.setTextAlignment(TextAlignment.JUSTIFIED)
.setFont(font)
.setHyphenation(new HyphenationConfig("en", "uk", 3, 3));
BufferedReader br = new BufferedReader(new FileReader(SRC));
String line;
Paragraph p;
boolean title = true;
AreaBreak nextArea = new AreaBreak(AreaBreakType.NEXT_AREA);
while ((line = br.readLine()) != null) {
p = new Paragraph(line);
if (title) {
p.setFont(bold).setFontSize(12);
title = false;
}
else {
p.setFirstLineIndent(36);
}
if (line.isEmpty()) {
document.add(nextArea);
title = true;
}
document.add(p);
}
//Close document
document.close();
}
}
now this will have a result of this . picture below
now I want to make it into a three(3) column. how to do this? please anyone? or just guide me. thank you!
Here is where you define two columns:
float offSet = 36;
float gutter = 23;
float columnWidth = (PageSize.A4.getWidth() - offSet * 2) / 2 - gutter;
float columnHeight = PageSize.A4.getHeight() - offSet * 2;
//Define column areas
Rectangle[] columns = {
new Rectangle(offSet, offSet, columnWidth, columnHeight),
new Rectangle(offSet + columnWidth + gutter, offSet, columnWidth, columnHeight)};
Changing this to three columns is a no-brainer.
Just replace all of the above by something like this:
Rectangle[] columns = {
new Rectangle(20, 20, 180, 802),
new Rectangle(207, 15, 180, 802),
new Rectangle(394, 15, 180, 802) };
Now that columns has three Rectangle elements, three columns will be drawn. Feel free to adapt the values of x, y, width and height in the Rectangle constructor if the three columns don't have the desired size.
I´m trying to save a BufferedImage as TIFF. This works fine, but it's taking too long per TIFF, up to 5 sec. 5 sec might not sound like much, but if you want to save up to 20 images it´s too long.
Here is my code:
public void convertToTif() throws ConvertionFailedException {
final int dpi = 300;
try {
ImageOutputStream ios = null;
TIFFImageWriterSpi tiffspi = new TIFFImageWriterSpi();
ImageWriter writer = tiffspi.createWriterInstance();
// setup writer
ios = ImageIO.createImageOutputStream(this.tifFile);
writer.setOutput(ios);
BufferedImage tiffImage = create1BitTif(this.imageToConvert);
// get new Metadata
ImageTypeSpecifier imageType = ImageTypeSpecifier.createFromBufferedImageType(tiffImage.getType());
IIOMetadata imageMetadata = writer.getDefaultImageMetadata(imageType, null);
TIFFDirectory dir = TIFFDirectory.createFromMetadata(imageMetadata);
BaselineTIFFTagSet base = BaselineTIFFTagSet.getInstance();
TIFFTag tagXRes = base.getTag(BaselineTIFFTagSet.TAG_X_RESOLUTION);
TIFFTag tagYRes = base.getTag(BaselineTIFFTagSet.TAG_Y_RESOLUTION);
TIFFTag tagNewSubfile = base.getTag(BaselineTIFFTagSet.TAG_NEW_SUBFILE_TYPE);
TIFFTag tagBitsPerSample = base.getTag(BaselineTIFFTagSet.TAG_BITS_PER_SAMPLE);
TIFFTag tagResolutionUnit = base.getTag(BaselineTIFFTagSet.TAG_RESOLUTION_UNIT);
// set X DPI
long[][] X_DPI = new long[][] { { dpi, 1 } };
TIFFField xDPI = new TIFFField(tagXRes, TIFFTag.TIFF_RATIONAL, 1, X_DPI);
// set Y DPI
long[][] Y_DPI = new long[][] { { dpi, 1 } };
TIFFField yDPI = new TIFFField(tagYRes, TIFFTag.TIFF_RATIONAL, 1, Y_DPI);
// setzt Photometric von BlackIsZero auf WhiteIsZero
TIFFField photometricInterpretationField = new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_PHOTOMETRIC_INTERPRETATION), BaselineTIFFTagSet.PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO);
// setzt NewSubfileType auf 0
TIFFField newSubfile = new TIFFField(tagNewSubfile, 0);
// setzt BitsPerSample auf 1
TIFFField bitsPerSample = new TIFFField(tagBitsPerSample, 1);
// setzt ResolutionUnit auf 2
TIFFField resolutionUnit = new TIFFField(tagResolutionUnit, 2);
dir.addTIFFField(xDPI);
dir.addTIFFField(yDPI);
dir.addTIFFField(photometricInterpretationField);
dir.addTIFFField(newSubfile);
dir.addTIFFField(bitsPerSample);
dir.addTIFFField(resolutionUnit);
TIFFImageWriteParam param = (TIFFImageWriteParam) writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setTIFFCompressor(new TIFFT6Compressor());
param.setCompressionType("CCITT T.6");
param.setCompressionQuality(0.0f);
IIOMetadata streamMetadata = writer.getDefaultStreamMetadata(null);
String tiffMetadataFormatName = streamMetadata.getNativeMetadataFormatName();
IIOMetadataNode newTree = new IIOMetadataNode(tiffMetadataFormatName);
// create little endian
IIOMetadataNode endianNode = new IIOMetadataNode("ByteOrder");
endianNode.setAttribute("value", "LITTLE_ENDIAN");
newTree.appendChild(endianNode);
streamMetadata.setFromTree(tiffMetadataFormatName, newTree);
writer.prepareWriteSequence(streamMetadata);
writer.writeToSequence(new IIOImage(tiffImage, null, dir.getAsMetadata()), param);
ios.close();
} catch (IOException e) {
throw new ConvertionFailedException(e);
}
}
I have an image and some text below the image in an excel sheet. when i am applying autoSizeColumn() to the column where text present the image is also getting streched .
i am also setting the anchortype to 2 but this is not protecting the image to resize.
I am posting some sample code here.
public static void main(String[] args) {
try{
XSSFWorkbook book = new XSSFWorkbook();
XSSFSheet sheet = book.createSheet("Test Sheet");
InputStream is = new FileInputStream("D:\\RPM_Eclipse_Workspaces\\B6.9\\00POI\\Chrysanthemum.jpg");
byte[] bytes = IOUtils.toByteArray(is);
int pictureIdx = book.addPicture(bytes, Workbook.PICTURE_TYPE_PNG);
Drawing drawing = sheet.createDrawingPatriarch();
ClientAnchor anchor = new XSSFClientAnchor(0, 0, 1023, 255, 2,2,10,10);
//Image should not get Resized while doing Autosize
anchor.setAnchorType(ClientAnchor.DONT_MOVE_AND_RESIZE);
Picture pict = drawing.createPicture(anchor, pictureIdx);
XSSFRow row = sheet.createRow(12);
for(int i = 2 ; i < 11 ; i++){
XSSFCell cell = row.createCell(i);
cell.setCellValue("oval (although anchor's type is set to MOVE_DONT_RESIZE ). ... But the one way to ");
}
sheet.autoSizeColumn(2);
book.write(new FileOutputStream(new File("D:\\auto.xlsx")));
System.out.println("=== DONE ===");
}catch (Exception e){
}
}
POI uses TwoCellAnchors for adding pictures ... so with a bit of nasty reflections, you can add a Picture with a OneCellAnchor
In the example the drawing.createAnchor(10, 10, 110, 110, 2, 2, 0, 0) is used to position the image 10x10 from the top left corner of the cell(2,2) and scale the picture to 100x100 pixels, i.e. 110-10.
(tested with Libre Office 4.0, Excel Viewer 2010)
import java.io.*;
import java.lang.reflect.*;
import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.usermodel.*;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.*;
public class Automation {
public static void main(String[] args) throws Exception {
XSSFWorkbook book = new XSSFWorkbook();
XSSFSheet sheet = book.createSheet("Test Sheet");
InputStream is = new FileInputStream("src/test/resources/smiley.jpg");
byte[] bytes = IOUtils.toByteArray(is);
int pictureIdx = book.addPicture(bytes, Workbook.PICTURE_TYPE_PNG);
XSSFDrawing drawing = sheet.createDrawingPatriarch();
XSSFClientAnchor anchor = drawing.createAnchor(10, 10, 110, 110, 2, 2, 0, 0);
createPicture(anchor, pictureIdx, drawing);
XSSFRow row = sheet.createRow(12);
for (int i = 2; i < 11; i++) {
XSSFCell cell = row.createCell(i);
cell.setCellValue("oval (although anchor's type is set to MOVE_DONT_RESIZE ). ... But the one way to ");
}
sheet.autoSizeColumn(2);
book.write(new FileOutputStream(new File("auto.xlsx")));
}
public static XSSFPicture createPicture(XSSFClientAnchor anchor, int pictureIndex, XSSFDrawing drawing)
throws Exception
{
Method m = XSSFDrawing.class.getDeclaredMethod("addPictureReference", int.class);
m.setAccessible(true);
PackageRelationship rel = (PackageRelationship)m.invoke(drawing, (Integer)pictureIndex);
long shapeId = 1000+drawing.getCTDrawing().sizeOfOneCellAnchorArray();
CTOneCellAnchor ctAnchor = createOneCellAnchor(drawing, anchor);
CTPicture ctShape = ctAnchor.addNewPic();
m = XSSFPicture.class.getDeclaredMethod("prototype");
m.setAccessible(true);
CTPicture ctp = (CTPicture)m.invoke(null);
ctShape.set(ctp);
ctShape.getNvPicPr().getCNvPr().setId(shapeId);
Constructor<XSSFPicture> picCon = XSSFPicture.class
.getDeclaredConstructor(XSSFDrawing.class, CTPicture.class);
picCon.setAccessible(true);
XSSFPicture shape = picCon.newInstance(drawing, ctShape);
Field f = XSSFShape.class.getDeclaredField("anchor");
f.setAccessible(true);
f.set(shape, anchor);
m = XSSFPicture.class.getDeclaredMethod("setPictureReference", PackageRelationship.class);
m.setAccessible(true);
m.invoke(shape, rel);
return shape;
}
public static CTOneCellAnchor createOneCellAnchor(XSSFDrawing drawing, XSSFClientAnchor anchor) {
final int pixel2emu = 12700;
CTOneCellAnchor ctAnchor = drawing.getCTDrawing().addNewOneCellAnchor();
long cx = (anchor.getTo().getRowOff()-anchor.getFrom().getRowOff())*pixel2emu;
long cy = (anchor.getTo().getColOff()-anchor.getFrom().getColOff())*pixel2emu;
CTPositiveSize2D size = CTPositiveSize2D.Factory.newInstance();
size.setCx(cx);
size.setCy(cy);
ctAnchor.setExt(size);
ctAnchor.setFrom(anchor.getFrom());
CTMarker m = ctAnchor.getFrom();
m.setColOff(m.getColOff()*pixel2emu);
m.setRowOff(m.getRowOff()*pixel2emu);
ctAnchor.addNewClientData();
try {
Method mt = XSSFClientAnchor.class.getDeclaredMethod("setFrom", CTMarker.class);
mt.setAccessible(true);
mt.invoke(anchor, ctAnchor.getFrom());
} catch (Exception e) {
throw new RuntimeException("handle me", e);
}
return ctAnchor;
}
}
In picture.resize () method, to calculate the original height of the image, it use the row.getHeight value.
The problem is, the row height value !
Depending on the contents of the cell, the row height is increased automatically, but you cannot obtain the increased height value by row.getHeight() method.
So, you need to set the height of row using row.setHeigth (height) manually.
then you can be obtained for the normal image size and the picture.resize () method will work properly.