I am trying to complete an example that draws graphics and writes them to PDF, but I keep getting errors that the PDF has no pages. if I add something simple with document.add() after opening it works fine, I just never see the graphics. Here is my code:
Document document = new Document();
PdfWriter writer = new PdfWriter();
response.setContentType("application/pdf");
response.setHeader("Content-Disposition",
" attachment; filename=\"Design.pdf\"");
writer = PdfWriter.getInstance(document, response.getOutputStream());
document.open();
PdfContentByte cb = writer.getDirectContent();
Graphics2D graphics2D = cb.createGraphics(36, 54);
graphics2D.drawString("Hello World", 36, 54);
graphics2D.dispose();
document.close();
Do I have to do something else to add the graphic to the document or is my syntax incorrect?
I am not an expert in IText, but last week I tryed to draw some circles with it. So this is what I have noticed during my tests:
If you draw graphics, you must (or lets say I must when I tryed it) "wrap" the graphics commands in a section starting with saveState() and ending with restoreState(), es well as I needed to invoke fillStroke() -- if I do not invoke fillStroke() then nothing was drawn.
Example
private void circle(float x, float y, PdfWriter writer) {
PdfContentByte canvas = writer.getDirectContent();
canvas.saveState();
canvas.setColorStroke(GrayColor.BLACK);
canvas.setColorFill(GrayColor.BLACK);
canvas.circle(x, y, 2);
canvas.fillStroke();
canvas.restoreState();
}
#Test
public void testPossition() throws DocumentException, IOException {
OutputStream outputStream = FileUtil.openOutputStream("testPosition.pdf");
//this is my personal file util, but it does not anything more
//then creating a file and opening the file stream.
Document document = new Document(PageSize.A4, 50, 50, 50, 50);
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
document.open();
markPosition(100, 100, writer);
document.add(new Paragraph("Total: 595 x 842 -- 72pt (1 inch)"));
document.close();
outputStream.flush();
outputStream.close();
}
private void markPosition(float x, float y, PdfWriter writer)
throws DocumentException, IOException {
placeChunck("x: " + x + " y: " + y, x, y, writer);
circle(x, y, writer);
}
private void placeChunck(String text, float x, float y, PdfWriter writer)
throws DocumentException, IOException {
PdfContentByte canvas = writer.getDirectContent();
BaseFont font = BaseFont.createFont(BaseFont.HELVETICA,
BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
canvas.saveState();
canvas.beginText();
canvas.moveText(x, y);
canvas.setFontAndSize(font, 9);
canvas.showText(text);
canvas.endText();
canvas.restoreState();
}
But PdfContentByte (canvas) has much more functions, for example rectangle.
Does Document doc = new Document(PageSize.A4);
make any difference?
I don't know if you need to add a Paragraph like this:
doc.add(new Paragraph(...));
Also we use doc.add(ImgRaw); to add images.
Without going too far into it, I think your general approach is fine. I think what might be happening here is that the Graphics2D origin is different from the PDF origin, so maybe you need to change the call to drawString() so it uses 0,0 as the location??
I think the problem is that directcontent writes directly to the page object. This way you can add backgrounds or backdrop images. Try adding a new page (doc.newPage()) before writing to the directcontent.
Have you tried drawing operations on the g2d object that just use shapes instead of text? That would eliminate the possibility of something odd going on with font selection or something like that.
iText In Action Chapter 12 has exactly what you are looking for - it really is worth picking up. Preview of Chapter 12
I just put together the following unit test against the latest HEAD of iText:
Document document = new Document();
PdfWriter writer = new PdfWriter();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
writer = PdfWriter.getInstance(document, baos);
document.open();
PdfContentByte cb = writer.getDirectContent();
Graphics2D graphics2D = cb.createGraphics(36, 54);
graphics2D.setColor(Color.black);
graphics2D.drawRect(0, 0, 18, 27);
Font font = new Font("Serif", Font.PLAIN, 10);
graphics2D.setFont(font);
graphics2D.drawString("Yo Adrienne", 0, 54);
graphics2D.dispose();
document.close();
TestResourceUtils.openBytesAsPdf(baos.toByteArray());
And it works fine - I get a small black rectangle in the lower left hand corner of the page, plus text. Note that I am specifying X=0 for my drawString method (you were specifying 36 which causes the text to render outside of the image bounds). Note also that I explicitly specified a font - if I leave that out, it still renders, but it's usually a great idea to not trust the defaults for that sort of thing. Finally, I explicitly set the foreground color - again, not truly necessary, but trusting defaults can be scary.
So I'd have to say that the core issue here was the placement of the text at x=36.
In none of my tests was I able to create an error saying that the PDF has no pages - can you post the stack trace of the exception you are getting?
I can't imagine that adding a paragraph to the document makes any difference to this (that's the sort of bug that would have gotten taken care of long, long ago)
Related
I have problem with size document. I pass vertical orientation(595.0x842.0) pdf and I change orientation pdf to horizontal(842.0x595.0) but When I return ByteArrayOutputStream as ByteArray and I try read this ByteArray I get vertical orientation(595.0x842.0) instead horizontal(842.0x595.0)
To write new content I use PdfWriter and I send Document with new size and ByteArrayOutputStream as second parametr.
PdfReader reader = new PdfReader(pdf.getFile());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Document document = new Document(PageSize.A4.rotate(), 0, 0, 0, 0);
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
I expect the output horizontal(842.0x595.0) instead (595.0x842.0)
I need to this dimension in this code:
PdfReader reader = new PdfReader(pdf.getFile());
int n = reader.getNumberOfPages();
PdfStamper stamper = new PdfStamper(reader,outputStream);
PdfContentByte pageContent;
for (int i = 0; i < n;) {
pageContent = stamper.getOverContent(++i);
System.out.println(reader.getPageSize(i));
ColumnText.showTextAligned(pageContent, Element.ALIGN_RIGHT,
new Phrase(String.format("page %s of %s", i, n)),
reader.getPageSize(i).getWidth()- 20, 20, 0);
}
This variable pdf.getFile() is a ByteArray(ByteArrayOutputStream ) from previous code.
I expect page number in right down corner(x point 842) but actual is 595.
For better understand I send screenshot.
You use the PdfReader method getPageSize but you should use getPageSizeWithRotation, i.e. you should replace
System.out.println(reader.getPageSize(i));
ColumnText.showTextAligned(pageContent, Element.ALIGN_RIGHT,
new Phrase(String.format("page %s of %s", i, n)),
reader.getPageSize(i).getWidth()- 20, 20, 0);
by
System.out.println(reader.getPageSizeWithRotation(i));
ColumnText.showTextAligned(pageContent, Element.ALIGN_RIGHT,
new Phrase(String.format("page %s of %s", i, n)),
reader.getPageSizeWithRotation(i).getWidth()- 20, 20, 0);
The Backgrounds
There are two properties of a page responsible for the final displayed page dimension: MediaBox and Rotate. (Let's for the moment ignore the crop box and all the other boxes which also exist.)
A landscape A4 page, therefore, can be created in two conceptually different ways, either as a 842x595 media box with 0° (or 180°) rotation or as a 595x842 media box with 90° (or 270°) rotation.
If you instantiate a Document with PageSize.A4.rotate(), iText uses the latter way. (If you had instantiated it with new RectangleReadOnly(842,595), iText would have used the former way.)
PdfReader.getPageSize only inspects the media box. Thus, it returns a rectangle with width 595 for your PDF page with 595x842 media box and 90° rotation.
PdfReader.getPageSizeWithRotation also inspects the page rotation. Thus, it returns a rectangle with width 842 for your PDF page with 595x842 media box and 90° rotation.
I am generate Barcode128 with library iText-2.1.3. This is code which I am using:
private void createBarcode(String kodDokumentu, String idSadowka, String projekt) throws IOException, DocumentException
{
File barcodePdf = new File(pathToPdf);
Files.deleteIfExists(barcodePdf.toPath());
Document document = new Document();
Rectangle size = new Rectangle(151,60);
document.setMargins(5, 1, -6, 0);
document.setPageSize(size);
FileOutputStream fos = new FileOutputStream(pathToPdf);
PdfWriter writer = PdfWriter.getInstance(document, fos);
document.open();
PdfContentByte cb = writer.getDirectContent();
Barcode barcode128 = new Barcode128();
barcode128.setBarHeight(40);
barcode128.setX(1.04f);
barcode128.setCode("VL#"+kodDokumentu.toUpperCase());
barcode128.setCodeType(Barcode.CODE128);
Image code128Image = barcode128.createImageWithBarcode(cb, null, null);
Font code = new Font(FontFamily.TIMES_ROMAN, 8, Font.NORMAL, BaseColor.BLACK);
Paragraph p = new Paragraph("ID: "+idSadowka+", Projekt: "+projekt.substring(0, 2), code);
document.add(p);
document.add(code128Image);
document.close();
fos.close();
}
I want to achive as small h (take a look at image) as it is possible, best if h=0.01 because I want to save more place for ID: xxxxx, Projekt: xx to make it bigger and easier to read by human.
First (bottom barcode) I used font size 8, then (upper barcode) I tried to change font size to 10 but when I did it h is bigger than previous. I know that font size is connected with gap between barcode and text above it but is it possible to use bigger font size and set this gap really small?
Not sure if it's available in the version of iText you use, but on iText 5 at least, you have the option to set the "spacing after" on Paragraphs. It would be exactly what you need, i.e. you specify a fixed space under the paragraph, and any elements you add to the document after that paragraph, will go under that space.
p.setSpacingAfter(x)
Where x being the space you need, in user units or "points".
I got a problem I do not really know how to proceed with... When I draw a rectangle on a page (the blue one in the picture) and then draw the same rectangle on a template on the same page (the green one), the rectangle on the template is larger. Has anybody a clue WHY?
Run the following class:
public class RectangleTemplate {
public static void main(String[] args){
try {
File file = new File("rectagnleTemplate_" + System.currentTimeMillis() + ".pdf");
FileOutputStream fileout = new FileOutputStream(file);
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, fileout);
document.open();
Rectangle rectangleOnPage = new Rectangle(20, 20, 100, 100);
rectangleOnPage.setBorderColor(BaseColor.BLUE);
rectangleOnPage.setBorder(Rectangle.BOX);
rectangleOnPage.setBorderWidth(2);
PdfContentByte canvas = writer.getDirectContent();
canvas.rectangle(rectangleOnPage);
canvas.stroke();
PdfTemplate template = canvas.createTemplate(document.getPageSize().getWidth(), document.getPageSize()
.getHeight());
template.rectangle(rectangleOnPage.getLeft(), rectangleOnPage.getBottom(), rectangleOnPage.getRight(),
rectangleOnPage.getTop());
template.setColorFill(BaseColor.GREEN
);
template.fill();
template.stroke();
canvas.addTemplate(template, -10,-10);
canvas.sanityCheck();
canvas.stroke();
document.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
}
}
}
the green rectangle should be as large as the blue one:
This is indeed confusing: iText 1, 2 and 5 grew organically: different people contributed code and as a result, there's a difference between the way a Rectangle object is created and the way you define a rectangle using the rectangle() method.
Take a look at the API docs:
The Rectangle constructor looks like this:
public Rectangle(float llx, float lly, float urx, float ury)
You need the coordinates of the lower-left and the upper-right corner.
The rectangle method looks like this:
public void rectangle(float x, float y, float w, float h)
In this case, you only pass the coordinate of the lower-left corner. The other two parameters are the width and the height.
This line in your code is wrong:
template.rectangle(
rectangleOnPage.getLeft(), rectangleOnPage.getBottom(),
rectangleOnPage.getRight(), rectangleOnPage.getTop());
It should be:
template.rectangle(
rectangleOnPage.getLeft(), rectangleOnPage.getBottom(),
rectangleOnPage.getWidth(), rectangleOnPage.getHeight());
This problem is fixed in iText 7. iText 7 is a complete rewrite of iText by a coordinated team.
I'm using iText to create barcodes on a PDF with the same format as this one:
The problem is the the left number, the first zero digits must be smaller, while the rest of the numbers must also be bold. "T.T.C." also has to be even smaller (it doesn't have to be on another line).
I was able to rotate the number with the following code:
String price = "23000 T.T.C.";
PdfContentByte cb = docWriter.getDirectContent();
PdfTemplate textTemplate = cb.createTemplate(50, 50);
ColumnText columnText = new ColumnText(textTemplate);
columnText.setSimpleColumn(0, 0, 50, 50);
columnText.addElement(new Paragraph(price));
columnText.go();
Image image;
image = Image.getInstance(textTemplate);
image.setAlignment(Image.MIDDLE);
image.setRotationDegrees(90);
doc.add(image);
The problem is that I cannot find a way online to change the font of certain characters of the String price when it is printed on the PDF.
I have created a small Proof of Concept that results in a PDF that looks like this:
As you can see, it has text in different sizes and styles. It also has a bar code that is rotated.
Take a look at the RotatedText example:
public void createPdf(String dest) throws IOException, DocumentException {
// step 1
Document document = new Document(new Rectangle(60, 120), 5, 5, 5, 5);
// step 2
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(dest));
// step 3
document.open();
// step 4
PdfContentByte canvas = writer.getDirectContent();
Font big_bold = new Font(FontFamily.HELVETICA, 12, Font.BOLD);
Font small_bold = new Font(FontFamily.HELVETICA, 6, Font.BOLD);
Font regular = new Font(FontFamily.HELVETICA, 6);
Paragraph p1 = new Paragraph();
p1.add(new Chunk("23", big_bold));
p1.add(new Chunk("000", small_bold));
document.add(p1);
Paragraph p2 = new Paragraph("T.T.C.", regular);
p2.setAlignment(Element.ALIGN_RIGHT);
document.add(p2);
BarcodeEAN barcode = new BarcodeEAN();
barcode.setCodeType(Barcode.EAN8);
barcode.setCode("12345678");
Rectangle rect = barcode.getBarcodeSize();
PdfTemplate template = canvas.createTemplate(rect.getWidth(), rect.getHeight() + 10);
ColumnText.showTextAligned(template, Element.ALIGN_LEFT,
new Phrase("DARK GRAY", regular), 0, rect.getHeight() + 2, 0);
barcode.placeBarcode(template, BaseColor.BLACK, BaseColor.BLACK);
Image image = Image.getInstance(template);
image.setRotationDegrees(90);
document.add(image);
Paragraph p3 = new Paragraph("SMALL", regular);
p3.setAlignment(Element.ALIGN_CENTER);
document.add(p3);
// step 5
document.close();
}
This example solves all of your issues:
You want a Paragraph to use different fonts: compose a Paragraph using different Chunk objects.
You want to add extra text on top of a bar code: add the bar code to a PdfTemplate and add the extra text using ColumnText.showTextAligned() (not that you can also compose a Phrase using different Chunk objects if you need more than one font in that extra text).
You want to rotate the bar code: wrap the PdfTemplate inside an Image object and rotate the image.
You can check the result: rotated_text.pdf
I hope this helps.
Im using PdfTemplate.createTemplate with following code,
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("C:\\Report.pdf"));
document.open();
document.add(new Paragraph("A Hello World PDF new TEXT document."));
PdfContentByte contentByte = writer.getDirectContent();
PdfTemplate template = contentByte.createTemplate(50,50);
template.beginText();
BaseFont bf=BaseFont.createFont(BaseFont.HELVETICA,BaseFont.CP1252,BaseFont.NOT_EMBEDDED);
template.setFontAndSize(bf,10);
template.setTextMatrix(100,100);
template.showText("Text at the position 100,100 (relative to the template!)");
template.endText();
contentByte.addTemplate(template, 10, 100);
document.close();
But the text is not visible in the pdf
When you do this:
PdfTemplate template = contentByte.createTemplate(50,50);
You create a canvas that measures 50 user units by 50 user units. All the content that you add to this canvas will be clipped to that size.
When you do this:
template.setTextMatrix(100,100);
You deliberately move outside the visible area of the small square with lower-left corner 0,0 and upper-right corner 50,50. Whatever you add in this area will be clipped.
You are correct when you say: the text is not visible in the pdf. If the text were visible, you would have found a bug.