Why are the dimensions printed the wrong size? - itext

I want a 6 cm (60 millimeter) wide rectangle.
As I understand it iText works with points as units, so I converted 60 millimeters to points and got 170 points. (link to converter)
However when I print out on a A4 paper and measure it with a ruler the rectangle is 5,7 cm (57 millimeters) wide. Why is this?
Below is the code I use:
private static byte[] CreateRectangle()
{
var stream = new MemoryStream();
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(stream));
PdfCanvas canvas = new PdfCanvas(pdfDoc.AddNewPage());
Color greenColor = new DeviceCmyk(1f, 0f, 1f, 0.176f);
canvas.SetFillColor(greenColor);
canvas.Rectangle(150, 600, 170, 56); /* 170 width, 56 height */
canvas.FillStroke();
pdfDoc.Close();
return stream.ToArray();
}

Your calculation is correct, 6 cm are about 170 points.
Your code is correct.
I used your code to generate a PDF. When I applied the measure tool, the 6 cm were confirmed:
I printed that PDF and measured the printout, again 6 cm.
Thus, either your ruler is wrong (which I doubt) or your printing was not at "Actual size" but probably instead at "Fit" or "Shrink oversized pages" (which both here correspond to 96%).

Related

Scale image to fill multiple pages with iText

I am trying to scale an image with iText (on a new PDF document) in order to make it fill the width of the page without streching, so that it could take several pages.
I've found a lot of solutions but they were pretty complicated and I don't really like coding like that. The best solution I've found till now (from another question on SO) is using PdfTable but it always uses a single page, scaling the image.
// Load image from external storage
Image image = Image.getInstance(path + "/img.png");
// Calculate ratio
float width = PageSize.A4.getWidth();
float heightRatio = image.getHeight() * width / image.getWidth();
Document document = new Document();
document.open();
PdfPTable table = new PdfPTable(1);
table.setWidthPercentage(100);
PdfPCell c = new PdfPCell(image, true);
c.setBorder(PdfPCell.NO_BORDER);
c.setPadding(0);
// Set image dimensions
c.getImage().scaleToFit(width, heightRatio);
table.addCell(c);
document.add(table);
// Write PDF file
document.close();
Any suggestions?
Ok I finally decided to go the way I didn't want to go, since it seems to be the only way: adding the same image to every page and setting the proper vertical offset to each one. The offset gets calculated as the number of pages left to draw + the gap the remains blank. For each step I decrement the number of pages until there's nothing left to draw.
// Open new PDF file
Document document = new Document();
PdfWriter pdfWriter = PdfWriter.getInstance(document, new FileOutputStream(getSharedDirPath() + File.separator + "file.pdf"));
document.open();
PdfContentByte content = pdfWriter.getDirectContent();
// Load image from external folder
Image image = Image.getInstance(path + "/img.png");
image.scaleAbsolute(PageSize.A4);
image.setAbsolutePosition(0, 0);
float width = PageSize.A4.getWidth();
float heightRatio = image.getHeight() * width / image.getWidth();
int nPages = (int) (heightRatio / PageSize.A4.getHeight());
float difference = heightRatio % PageSize.A4.getHeight();
while (nPages >= 0) {
document.newPage();
content.addImage(image, width, 0, 0, heightRatio, 0, -((--nPages * PageSize.A4.getHeight()) + difference));
}
// Write PDF file
document.close();
Honestly I don't like this solution, I thought it was possible to auto-adjust dimensions as I do in a text editor, but after all it was not very difficult.....it just took me three days to figure out how the whole PDF thing worked.

How to create a multi page TOC using itext?

I need to create TOC in a PDF. It can be of 1 page or multi pages depending on the number of pages in PDF. I have learnt that PdfStamper, PdfAction, PdfAnnotaion can be used to achieve this.
I am currently merging more than one document and creating both bookmarks and TOC for all the documents in JAVA. I have got rid of bookmarks but got stuck on multi-pages TOC.
Also, please explain this line of your code - link = new PdfAnnotation(copy, 36, ct.getYLine(), 559, y, action);... What I have understood is you are passing the dimensions of a rectangle on the page on which it will be going after clicking the link (, 36, ct.getYLine(), 559, y, ).. And thus I am facing an issue of not going to correct position of the page on clicking the link, in the case if page size is different from US Letter Portrait.
Here is the snippet -
int tocPages = 1;
Document tocDocument = new Document();
String tocFilename ="toc-filename";
Phrase tocPhrase =
new Phrase("Table of Contents", new Font(Font.FontFamily.HELVETICA, 20, Font.BOLD, BaseColor.BLACK));
PdfWriter writer = PdfWriter.getInstance(tocDocument, new FileOutputStream(tocFilename));
tocDocument.open();
tocDocument.add(new Paragraph(tocPhrase));
PdfReader reader = new PdfReader(tocFilename);
page = copy.getImportedPage(reader, tocPages);
stamp = copy.createPageStamp(page);
float y = 770;
ColumnText ct = new ColumnText(stamp.getOverContent());
ct.setSimpleColumn(36, 36, 559, y);
for (Map.Entry<Integer, String> entry : toc.entrySet()) {
if (y <= 20) {
copy.addPage(page);
copy.newPage(); //(tried with writer.newPage() and tocDocument.newPage(), not working )
page = copy.getImportedPage(reader, ++tocPages);
}
p = new Paragraph(entry.getValue());
p.add(new Chunk(new DottedLineSeparator()));
p.add(String.valueOf(entry.getKey() + 1));
ct.addElement(p);
ct.go();
action = PdfAction.gotoLocalPage("p" + entry.getKey(), false);
link = new PdfAnnotation(copy, 36, ct.getYLine(), 559, y, action);
stamp.addAnnotation(link);
y = ct.getYLine();
}
ct.go();
stamp.alterContents();
copy.addPage(page);
tocDocument.close();
reader.close();
com.itextpdf.text.exceptions.InvalidPdfException: PDF header signature not found.
at com.itextpdf.text.pdf.PRTokeniser.getHeaderOffset(PRTokeniser.java:227)
at com.itextpdf.text.pdf.PdfReader.getOffsetTokeniser(PdfReader.java:442)
at com.itextpdf.text.pdf.PdfReader.(PdfReader.java:176)
at com.itextpdf.text.pdf.PdfReader.(PdfReader.java:219)
at com.itextpdf.text.pdf.PdfReader.(PdfReader.java:207)
at com.itextpdf.text.pdf.PdfReader.(PdfReader.java:197)
Excetion at line - PdfReader reader = new PdfReader(tocFilename);
You have adapted your question so that it became a question about this snippet from my answer to Create Index File(TOC) for merged pdf using itext library in java
Paragraph p;
PdfAction action;
PdfAnnotation link;
float y = 770;
ColumnText ct = new ColumnText(stamp.getOverContent());
ct.setSimpleColumn(36, 36, 559, y);
for (Map.Entry<Integer, String> entry : toc.entrySet()) {
p = new Paragraph(entry.getValue());
p.add(new Chunk(new DottedLineSeparator()));
p.add(String.valueOf(entry.getKey()));
ct.addElement(p);
ct.go();
action = PdfAction.gotoLocalPage("p" + entry.getKey(), false);
link = new PdfAnnotation(copy, 36, ct.getYLine(), 559, y, action);
stamp.addAnnotation(link);
y = ct.getYLine();
}
ct.go();
Your question was reduced to: please explain this line of your code:
link = new PdfAnnotation(copy, 36, ct.getYLine(), 559, y, action);
I have pasted the complete snippet, because this line can not be explained out of context,
In the snippet, we create a link annotation using the PdfAnnotation class. A link annotation is a area somewhere on a page, that triggers an action when clicked.
Which action is triggered in this case? That's what the action object is about, in this case, it jumps to a local page that is defined by a named destination. This is very similar to HTML where you have <a name="dest" /> and Jump to a specific destination on the current page.
When creating a PdfAnotation, you always need a PdfWriter instance. In this case, we are merging documents using a PdfCopy instance named copy. As PdfCopy extends PdfWriter, we can pass the copy instance as a parameter.
Finally, we define the clickable area. This is always a rectangle that is defined using two coordinates: the coordinate of the lower-left corner and the coordinate of the upper-right corner.
In the above snippet, we add paragraphs using ColumnText. ColumnText allows you to get information about the current y position after adding content. For instance, when we do this:
ct.addElement(p);
ct.go();
We can do this to get the current Y-coordinate:
float y = ct.getYLine();
In our code snippet, we keep track of the previous y value (the Y position before we add p) and we use the current value of ct.getYLine() to get the current y position.
This way, I can define the coordinate of the lower-left corner like this:
float llx = 36;
float lly = ct.getYLine();
And the coordinate of the upper-right corner like this:
float urx = 559;
float ury = y;
These are the values that you can see when we construct the link annotation.
I have hardcode the x values. They are based on the fact that I am creating a document with pages of size A4 and margins of half an inch. An A-4 page is 595 user units wide. To the left, I have a margin of 36 user units; to the right I also have a margin of 36 user units, which I have to subtract from the width of the page: 595 - 36 = 559.
If you have a page of which the format is LETTER, you need to adapt these values. However: it's better to calculate them based on the actual value of the MediaBox / CropBox of the existing page. This way, your code keeps working when you're accidentally confronted with documents that have a different page size.
You can read more about the MediaBox and the CropBox in my answer to this question: How to get dimensions of each page of a pdf file

How to scale a page less then 1 percent?

I want to scale a full pdf-page and insert into an new document. This works fine. But i have problems with the scale-factor when the factor is less than 1%. for example with a factor of 0,5%, no scale is happening. With a factor from 0,7%, the document (rectagle) has the correct size, but the scaled page is bigger then the calculated new size. Is there a way to scale a page continuously? It looks like, that in such cases with very small scale factors, the scaling works only in stages. Or maybe is there a problem with the internal matrix-calculation?
float scale = 1.005f; //100,5%
var newWidth = originWidth*scale;
var newHeight = originHeight*scale;
PdfReader reader = new PdfReader(inputfile);
var newMediaBox = new Rectangle(newWidth, newHeight);
Document doc = new Document(newMediaBox);
PdfWriter writer = PdfWriter.GetInstance(doc, new FileStream(outputfile,FileMode.Create));
doc.Open();
PdfContentByte cb = writer.DirectContent;
PdfImportedPage page = writer.GetImportedPage(reader, 1); //page #1
cb.AddTemplate(page, scale, 0, 0, scale, 0, 0);
doc.Close();
The other way: Can i calculate or read out the new size after the scaling?
Thanks.
iTextSharp by default will use two decimal places rounded when adding the template. You can change this by setting the HIGH_PRECISION static variable of the ByteBuffer class to true which will give you six decimal places. I do not know if this will affect your overall application performance but I'm fairly confident that it won't.
iTextSharp.text.pdf.ByteBuffer.HIGH_PRECISION = true;

Reduce border width on QR Codes generated by ZXing?

I'm using com.google.zxing.qrcode.QRCodeWriter to encode data and com.google.zxing.client.j2se.MatrixToImageWriter to generate the QR Code image. On a 400x400 image, there is about a 52 pixel wide border around the code. I'd like this border to be narrower, maybe 15 pixels, but I don't see anything in the API for doing that. Am I missing something in the documenation? Or would I need to process the image myself?
For reference, here is an example 400x400 QR Code produced with the ZXing library:
The QR spec requires a four module quiet zone and that's what zxing creates. (See QUIET_ZONE_SIZE in QRCodeWriter.renderResult.)
More recent versions of ZXing allow you to set the size of the quiet zone (basically the intrinsic padding of the QR code) by supplying an int value with the EncodeHintType.MARGIN key. Simply include it in the hints Map you supply to the Writer's encode(...) method, e.g.:
Map<EncodeHintType, Object> hints = new EnumMap<EncodeHintType, Object>(EncodeHintType.class);
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
hints.put(EncodeHintType.MARGIN, 2); /* default = 4 */
If you change this, you risk lowering the decode success rate.
Even by setting EncodeHintType.MARGIN to 0, the algorithm that convert the QRCode "dot" matrix to pixels data can generate a small margin (the algorithm enforce a constant number of pixels per dots, so the margin pixel size is the remainder of the integer division of pixels size by QR-Code dot size).
However you can completely bypass this "dot to pixel" generation: you compute the QRCode dot matrix directly by calling the public com.google.zxing.qrcode.encoder.Encoder class, and generate the pixel image yourself. Code below:
// Step 1 - generate the QRCode dot array
Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>(1);
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
QRCode qrCode = Encoder.encode(what, ErrorCorrectionLevel.L, hints);
// Step 2 - create a BufferedImage out of this array
int width = qrCode.getMatrix().getWidth();
int height = qrCode.getMatrix().getHeight();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
int[] rgbArray = new int[width * height];
int i = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
rgbArray[i] = qrCode.getMatrix().get(x, y) > 0 ? 0xFFFFFF : 0x000000;
i++;
} }
image.setRGB(0, 0, width, height, rgbArray, 0, width);
The conversion of the BufferedImage to PNG data is left as an exercise to the reader. You can also scale the image by setting a fixed number of pixels per dots.
It's usually more optimized that way, the generated image size is the smallest possible. If you rely on client to scale the image (w/o blur) you do not need more than 1 pixel per dot.
HashMap hintMap = new HashMap();
hintMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.Q);
hintMap.put(EncodeHintType.MARGIN, -1);
no margin
UPDATE
Add dependencies (from comments)
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.2.0</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.2.0</version>
</dependency>
In swift you can:
let hints = ZXEncodeHints()
hints!.margin = NSNumber(int: 0)
let result = try writer.encode(code, format: format, width: Int32(size.width), height: Int32(size.height), hints: hints)
let cgImage = ZXImage(matrix: result, onColor: UIColor.blackColor().CGColor, offColor: UIColor.clearColor().CGColor).cgimage
let QRImage = UIImage(CGImage: cgImage)
My problem is that I need to generate a PNG image with a transparent background fixed to x * x pixels.
I find that whatever I do with EncodeHintType.MARGIN, these is always some unexpected margin.
After reading its source code, I find a way to fix my problem, this is my code. There is no margin in the output BufferedImage.
BufferedImage oriQrImg = getQrImg(CONTENT_PREFIX+userInfo, ErrorCorrectionLevel.L,BLACK);
BufferedImage scaledImg = getScaledImg(oriQrImg,REQUIRED_QR_WIDTH,REQUIRED_QR_HEIGHT);
private static BufferedImage getQrImg(String content, ErrorCorrectionLevel level, int qrColor) throws WriterException {
QRCode qrCode = Encoder.encode(content, level, QR_HINTS);
ByteMatrix input = qrCode.getMatrix();
int w=input.getWidth(),h=input.getHeight();
BufferedImage qrImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = qrImg.createGraphics();
qrImg = g2d.getDeviceConfiguration().createCompatibleImage(w,h, Transparency.BITMASK);
g2d.dispose();
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
if (input.get(x,y) == 1) {
qrImg.setRGB(x, y, qrColor);
}else{
qrImg.setRGB(x, y, Transparency.BITMASK);
}
}
}
return qrImg;
}
static BufferedImage getScaledImg(BufferedImage oriImg,int aimWidth,int aimHeight){
Image scaled = oriImg.getScaledInstance(aimWidth,aimHeight,SCALE_DEFAULT);
Graphics2D g2d = new BufferedImage(aimWidth,aimHeight, BufferedImage.TYPE_INT_RGB).createGraphics();
BufferedImage scaledImg = g2d.getDeviceConfiguration().createCompatibleImage(aimWidth,aimHeight, Transparency.BITMASK);
g2d.dispose();
scaledImg.createGraphics().drawImage(scaled, 0, 0,null);
return scaledImg;
}

How to calculate the correct image size in out pdf using itextsharp?

I' am trying to add an image to a pdf using itextsharp, regardless of the image size it always appears to be mapped to a different greater size inside the pdf ?
The image I add is 624x500 pixel (DPI:72):
alt text http://www.freeimagehosting.net/uploads/727711dc70.png
And here is a screen of the output pdf:
alt text http://www.freeimagehosting.net/uploads/313d49044d.png
And here is how I created the document:
Document document = new Document();
System.IO.MemoryStream stream = new MemoryStream();
PdfWriter writer = PdfWriter.GetInstance(document, stream);
document.Open();
System.Drawing.Image pngImage = System.Drawing.Image.FromFile("test.png");
Image pdfImage = Image.GetInstance(pngImage, System.Drawing.Imaging.ImageFormat.Png);
document.Add(pdfImage);
document.Close();
byte[] buffer = stream.GetBuffer();
FileStream fs = new FileStream("test.pdf", FileMode.Create);
fs.Write(buffer, 0, buffer.Length);
fs.Close();
Any idea on how to calculate the correct size ?
I alreay tried ScaleAbsolute and the image still renders with incorrect dimensions.
I forget to mention that I' am using itextsharp 5.0.2.
It turned out that PDF DPI = 110, which means 110 pixels per inch, and since itextsharp uses points as measurment unit then :
n pixels = n/110 inches.
n inches = n * 72 points.
Having a helper method to convert pixels to points is all I needed:
public static float PixelsToPoints(float value,int dpi)
{
return value / dpi * 72;
}
By using the above formula and passing a dpi value of 110 it worked perfectly:
Note: Since you can create pdf documents in any size you want, this may lead to incorrect scaling when printing out your documents. To overcome this issue all you need to do is to have the correct aspect ratio between width and height [approximately 1:1.4142] (see : Paper Size - The international standard: ISO 216 ).
Multiply the image's height and width by 72 and divide them by the dpi(ppi): points = pixels * 72 / dpi.