Using iText7, how do I fill an otherwise empty column with a rectangle? - itext

Using iText7 I wish to fill an otherwise empty column with a bordered rectangle headed by some text. The border methods seem to have disappeared from Rectangle in iText7 and the only examples I can find use them. If Rectangle is the correct approach how do I do this? If not, what is the correct approach?

Please take a look at Chapter 2 of the tutorial "iText 7: Building Blocks"
In this tutorial, we create a Rectangle object and we draw it to a PdfCanvas object:
Rectangle rectangle = new Rectangle(36, 650, 100, 100);
pdfCanvas.rectangle(rectangle);
pdfCanvas.stroke();
How to get a PdfCanvas object?
Either you create it from a PdfPage object you've created yourself:
OutputStream fos = new FileOutputStream(dest);
PdfWriter writer = new PdfWriter(fos);
PdfDocument pdf = new PdfDocument(writer);
PdfPage page = pdf.addNewPage();
PdfCanvas pdfCanvas = new PdfCanvas(page);
Or you get an existing page from the PdfDocument object:
PdfCanvas canvas = new PdfCanvas(pdf, pdf.getNumberOfPages());
You can tweak the line width, dash pattern, line color,... using different methods in the PdfCanvas object.
There are other ways to draw a rectangle, but in one of your previous questions, you mentioned a ColumnDocumentRenderer. If your current question is part of the same context, you already have Rectangle objects and if you have a ColumnDocumentRenderer, you have access to a PdfCanvas object. You could easily automate your app to make it draw a rectangle around every column that is rendered.
Of course: since you never accepted my previous answer, my assumption could be wrong.

For better or worse this seemed to achieve my objective:
AreaBreak nextArea = new AreaBreak(AreaBreakType.NEXT_AREA);
document.add(nextArea);
float h = document.getRenderer().getCurrentArea().getBBox().getHeight();
float w = document.getRenderer().getCurrentArea().getBBox().getWidth();
Paragraph endB = new Paragraph(" ");
endB.setHeight(h);
endB.setWidth(w);
SolidBorder b = new SolidBorder(2);
endB.setBorder(b);
document.add(endB);

Related

Box Cloud Annotation Appearance in iText

This is my sample code to draw a box cloud annotation. I used code in PDFBox's implementation to draw a box cloud but i have a little problem when used in iText. I modified the border class and some parts to be usable in iText.
you can find the border class here.
My problem is, the top and right border clouds are not drawn. it seems their location is drawn beyond the rect difference. I figure the issue is with drawing the curves in cloudyPolygonImpl(). maybe itext has different ways to draw in PdfAppearance? I am not sure.
This is the what i have so far.
public class Test {
public static void main(String[] args) throws Exception {
PdfReader reader = new PdfReader("src.pdf");
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream("result.pdf"));
PdfDictionary be = new PdfDictionary();
be.put(PdfName.S, PdfName.C);
be.put(PdfName.I, new PdfNumber(1));
Rectangle location = new Rectangle(123.6f, 584.4f, 252.6f, 653.4f);
PdfAnnotation stamp = PdfAnnotation.createSquareCircle(stamper.getWriter(), location, "", true);
stamp.setBorderStyle(new PdfBorderDictionary(1, PdfBorderDictionary.STYLE_SOLID));
stamp.put(new PdfName("BE"), be);
stamp.setColor(BaseColor.RED);
PdfContentByte cb = stamper.getOverContent(1);
PdfAppearance app = cb.createAppearance(location.getWidth(), location.getHeight());
stamp.setAppearance(PdfName.N, app);
PdfArray stickyRect = stamp.getAsArray(PdfName.RECT);
Rectangle annotRect = new Rectangle(stickyRect.getAsNumber(0).floatValue(),
stickyRect.getAsNumber(1).floatValue(),
stickyRect.getAsNumber(2).floatValue(),
stickyRect.getAsNumber(3).floatValue());
PdfArray arrDiff = annotation.getAsArray(PdfName.RD);
Rectangle annotRectDiff = null;
if (arrDiff != null) {
annotRectDiff = new Rectangle(arrDiff.getAsNumber(0).floatValue(), arrDiff.getAsNumber(1).floatValue(),
arrDiff.getAsNumber(2).floatValue(), arrDiff.getAsNumber(3).floatValue()
}
// Create cloud appearance
CBorder cborder = new CBorder(app, 1, 1, annotRect);
cborder.createCloudyRectangle(annotRectDiff);
stamp.put(PdfName.RECT, new PdfRectangle(cborder.getRectangle()));
stamp.put(PdfName.RD, new PdfArray(new float[] {
cborder.getRectDifference().getLeft(),
cborder.getRectDifference().getBottom(),
cborder.getRectDifference().getRight(),
cborder.getRectDifference().getTop() }));
app.rectangle(cborder.getBBox());
app.transform(cborder.getMatrix());
app.setColorStroke(BaseColor.RED);
app.setLineWidth(1);
app.stroke();
stamper.addAnnotation(stamp, 1);
stamper.close();
reader.close();
}
}
The correct output should be that all borders be drawn with cloud but currently only the left and bottom are drawn.
(This answer is based on the code in revision 3 of your question as the changes in revision 4 introduced multiple errors.)
Your code here creates an invalid annotation appearance stream:
CBorder cborder = new CBorder(app, 1, 1, annotRect);
cborder.createCloudyRectangle(null);
stamp.put(PdfName.RECT, new PdfRectangle(cborder.getRectangle()));
stamp.put(PdfName.RD, new PdfArray(new float[] {
cborder.getRectDifference().getLeft(),
cborder.getRectDifference().getBottom(),
cborder.getRectDifference().getRight(),
cborder.getRectDifference().getTop() }));
app.rectangle(cborder.getBBox());
app.transform(cborder.getMatrix());
app.setColorStroke(BaseColor.RED);
app.setLineWidth(1);
app.stroke();
Its upper part creates a path:
2 j
121.58 588.63 m
122.06 588.95 122.6 589.18 123.16 589.3 c
120.73 588.78 119.18 586.4 119.7 583.96 c
120.19 581.67 122.35 580.14 124.68 580.44 c
...
122.06 596.42 122.6 596.64 123.16 596.76 c
121.09 596.32 119.6 594.49 119.6 592.36 c
119.6 590.87 120.34 589.47 121.58 588.63 c
h
Then app.rectangle(cborder.getBBox()) does not create anything (beware, this rectangle overload does not what you expect it to do!).
Then app.transform(cborder.getMatrix()) adds a change to the current transformation matrix, app.setColorStroke(BaseColor.RED) adds a change of the stroking color, and app.setLineWidth(1) adds a change of the line width:
1 0 0 1 -118.68 -579.48 cm
1 0 0 RG
1 w
And finally app.stroke() adds the command to stroke the path:
S
But between the definition of a path and the corresponding path drawing command, only clipping path instructions are allowed! Cf. Figure 9 – Graphics Objects – in the PDF specification ISO 32000-1.
You can fix this code like this, pulling up color and line width changes, and directly using the cloud bounding box:
// Create cloud appearance
app.setColorStroke(BaseColor.RED);
app.setLineWidth(1);
CBorder cborder = new CBorder(app, 1, 1, annotRect);
cborder.createCloudyRectangle(null);
stamp.put(PdfName.RECT, new PdfRectangle(cborder.getRectangle()));
stamp.put(PdfName.RD, new PdfArray(new float[] {
cborder.getRectDifference().getLeft(),
cborder.getRectDifference().getBottom(),
cborder.getRectDifference().getRight(),
cborder.getRectDifference().getTop() }));
app.stroke();
app.setBoundingBox(cborder.getBBox());
(CloudBoxAnnotation test testDrawLikeChitgoksImproved)
This in particular changes the result (as seen in Adobe Acrobat) from
to

Copying annotations with PdfWriter instead of PdfCopy

I need to copy annotations using PdfWriter instead of PdfCopy because at the time of the copy I need to resize/rotate the page. Can anyone tell me how to do this?
You think you need to use a plain PdfWriter instead of a PdfCopy for copying PDFs because you need to resize/rotate the page and iText in Action, 2nd Ed, says doing so is not possible with the PdfCopy class. Thus, you look for a way to copy annotations in such a context.
What you should look for instead is a way to rotate or resize pages and at the same time use PdfCopy nonetheless!
While it is true that the PdfCopy class itself does not allow resizing or rotating pages, you can manipulate a PDF loaded into a PdfReader and resize and/or rotate its pages before using the PdfCopy class. If you then copy the pages from this manipulated PdfReader into a PdfCopy, you get a result with resized or rotated pages (due to the manipulated PdfReader) and all the annotations present (due to the use of a PdfCopy).
E.g. you can resize all the pages in a PdfReader like this:
void resize(PdfReader pdfReader, float width, float height) {
for (int i = 1; i <= pdfReader.getNumberOfPages(); i++) {
boolean switched = pdfReader.getPageRotation(i) % 180 != 0;
float widthHere = switched ? height : width;
float heightHere = switched ? width : height;
Rectangle cropBox = pdfReader.getCropBox(i);
float halfWidthGain = (widthHere - cropBox.getWidth()) / 2;
float halfHeightGain = (heightHere - cropBox.getHeight()) / 2;
Rectangle newCropBox = new Rectangle(cropBox.getLeft() - halfWidthGain, cropBox.getBottom() - halfHeightGain,
cropBox.getRight() + halfWidthGain, cropBox.getTop() + halfHeightGain);
Rectangle mediaBox = pdfReader.getPageSize(i);
Rectangle newMediaBox = new Rectangle(Math.min(newCropBox.getLeft(), mediaBox.getLeft()),
Math.min(newCropBox.getBottom(), mediaBox.getBottom()),
Math.max(newCropBox.getRight(), mediaBox.getRight()),
Math.max(newCropBox.getTop(), mediaBox.getTop()));
PdfDictionary pageDictionary = pdfReader.getPageN(i);
pageDictionary.put(PdfName.MEDIABOX, new PdfArray(new float[] {newMediaBox.getLeft(), newMediaBox.getBottom(),
newMediaBox.getRight(), newMediaBox.getTop()}));
pageDictionary.put(PdfName.CROPBOX, new PdfArray(new float[] {newCropBox.getLeft(), newCropBox.getBottom(),
newCropBox.getRight(), newCropBox.getTop()}));
}
}
(CopyWithResizeRotate helper method)
and you can rotate all the pages in a PdfReader like this:
void rotate(PdfReader pdfReader) {
for (int i = 1; i <= pdfReader.getNumberOfPages(); i++) {
int rotation = pdfReader.getPageRotation(i);
int newRotation = rotation + 90 % 360;
PdfDictionary pageDictionary = pdfReader.getPageN(i);
if (newRotation == 0)
pageDictionary.remove(PdfName.ROTATE);
else
pageDictionary.put(PdfName.ROTATE, new PdfNumber(newRotation));
}
}
(CopyWithResizeRotate helper method)
Using these helpers, you can e.g. create a PDF from the rotated and/or resized pages of some source PDF and copy them like this:
byte[] wildPdf = RETRIEVE_SOURCE_PDF;
PdfReader pdfReaderOriginal = new PdfReader(wildPdf);
PdfReader pdfReaderRotate = new PdfReader(wildPdf);
rotate(pdfReaderRotate);
PdfReader pdfReaderResize = new PdfReader(wildPdf);
resize(pdfReaderResize, PageSize.LETTER.getWidth(), PageSize.LETTER.getHeight());
PdfReader pdfReaderRotateResize = new PdfReader(wildPdf);
rotate(pdfReaderRotateResize);
resize(pdfReaderRotateResize, PageSize.LETTER.getWidth(), PageSize.LETTER.getHeight());
try ( OutputStream os = new FileOutputStream(new File(RESULT_FOLDER, "wild-rotated-resized.pdf"))) {
Document document = new Document();
PdfCopy pdfCopy = new PdfCopy(document, os);
document.open();
pdfCopy.addDocument(pdfReaderOriginal);
pdfCopy.addDocument(pdfReaderRotate);
pdfCopy.addDocument(pdfReaderResize);
pdfCopy.addDocument(pdfReaderRotateResize);
document.close();
}
(CopyWithResizeRotate test method testRotateResizeAndCopy)
The result can look as follows, the first row the original pages (#1 A4, #2 HALFLETTER, #3 A5, #4 A5 rotated, #5 500x700), the second row the rotated ones, the third row the resized ones (to LETTER), and the fourth row the rotated and resized ones (to LETTER). The Adobe Reader thumbnails unfortunately are not at all to scale:
Manipulating a single PDF only
If you actually only want to resize/rotate pages of a single input PDF, you should not use a PdfCopy instance but instead a PdfStamper:
PdfReader pdfReader = new PdfReader(SOURCE);
[...manipulate properties of the pdfReader like above...]
new PdfStamper(pdfReader, TARGET_STREAM).close();
The advantage here is that not only page-level data but also document-level data of the original document are retained.
Special annotations
There is one type of annotations which will behave in an unexpected manner with the code above: annotations with the NoRotate flag set. Such annotations will behave like this when their host page is rotated:
(ISO 32000-2 section 12.5.3 — Annotation flags)

iText - How to set stroke width and opacity for PdfAnnotationInk

What functions should I call to set the stroke width and opacity when drawing ink type annotation?
I have go through the class API for PdfAnnotation and PDFStamp, but it seems there are no functions to set the width and opacity directly. Any suggestions? Thanks.
My sample program:
final String sourceFile = "C:\\PdfAnnotation\\sample.pdf";
final String destFile = "C:\\PdfAnnotation\\output\\output.pdf";
PdfReader reader = new PdfReader(sourceFile);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(destFile));
Rectangle rect = new Rectangle(52.92f, 397.56f, 173.36f, 530.67f);
float[][] inkList = {{61.736111f,530.669250f,61.295139f,525.820984f,61.295139f,518.768860f,
61.295139f,505.986969f,61.295139f,490.560547f,61.295139f,470.726562f,59.972221f,452.214844f,
57.767361f,434.143890f,56.003471f,418.276703f,53.357639f,404.172516f,51.593750f,391.390625f,
50.711807f,382.134766f,49.829861f,376.845703f},
{68.350693f,453.537109f,73.201385f,453.977875f,79.375000f,453.977875f,85.107635f,453.977875f,92.163193f,453.977875f,
100.541664f,453.977875f,108.038193f,453.977875f,117.298615f,453.977875f},
{112.447914f,509.072266f,112.006943f,505.105469f,112.006943f,498.053375f,112.006943f,488.797516f,112.006943f,472.930328f,
112.006943f,457.503906f,112.006943f,441.636719f,112.006943f,426.210297f,111.565971f,412.106110f,
111.125000f,401.968750f,111.125000f,391.831390f},
{161.836807f,454.859375f,161.836807f,449.129547f,161.836807f,441.636719f,161.836807f,433.262360f,161.836807f,
423.125000f,161.836807f,412.546875f,161.836807f,405.054047f,161.836807f,398.442719f,161.836807f,392.712891f,
161.836807f,389.627594f},
{163.159729f,485.712250f,170.215271f,469.845062f}
};
PdfAnnotation an = PdfAnnotation.createInk(stamper.getWriter(), rect, "", inkList);
an.setColor(new BaseColor(30, 89, 255));
an.setFlags(PdfAnnotation.FLAGS_PRINT);
stamper.addAnnotation(an, 1);
stamper.close();
reader.close();
What functions should I call to set the stroke width and opacity when drawing ink type annotation?
There are two answers:
If the PDF viewer creates the appearance
The PDF specification mentions
BS dictionary (Optional) A border style dictionary (see Table 166) specifying the line
width and dash pattern that shall be used in drawing the paths.
as another entry specific to the Ink annotation dictionary. This at least allows you to set the stroke width but not the opacity. Simply add a line like this
PdfAnnotation an = PdfAnnotation.createInk(stamper.getWriter(), rect, "", inkList);
an.setColor(new BaseColor(30, 89, 255));
an.setFlags(PdfAnnotation.FLAGS_PRINT);
// vvv set line width to 5:
an.setBorderStyle(new PdfBorderDictionary(5, PdfBorderDictionary.STYLE_SOLID));
// ^^^ set line width to 5:
stamper.addAnnotation(an, 1);
to set the stroke width to 5 and get a result like this:
If the PDF supplies the appearance
The PDF specification also mentions
The annotation dictionary’s AP entry, if present, shall take precedence
over the InkList and BS entries; see Table 168 and 12.5.5, “Appearance
Streams.”
Thus, you can create a PdfAppearance, use its methods to create an appearance exactly as you want it, including transparency, and set it as the normal appearance of the annotation. PDF viewers then shall display the annotation just like you want.

Add image to iText dynamically on PDF file

Can some one please help me what is missing in my code, I am trying to add image in to PDF generation
fillFieldValue(stamper.getAcroFields(),agntCertBean);
Image image1 = Image.getInstance(bb);
image1.scaleAbsolute(25f, 25f);
PdfContentByte overContent = stamper.getOverContent(1);
AcroFields form = stamper.getAcroFields();
AcroFields.FieldPosition fldPos = (AcroFields.FieldPosition)
form.getFieldPositions("ProfilePciture");
overContent.addImage(image1);
stamper.close();
reader.close();
Looking at your code without paying too much attending, I see two major mistakes:
[1.] There's something wrong with this line:
AcroFields.FieldPosition fldPos = (AcroFields.FieldPosition)form.getFieldPositions("ProfilePciture");
The getFieldPositions() method returns a List of FieldPosition elements and you're casting that list to a FieldPosition object. That won't work, you need something like this:
AcroFields.FieldPosition fldPos = form.getFieldPositions("ProfilePicture").get(0);
[2.] You get the position of the picture field, but you're not doing anything with it! You're not setting the position of the image!
Remove these two lines:
image1.scaleAbsolute(25f, 25f);
PdfContentByte overContent = stamper.getOverContent(1);
Add these lines after you've obtained the field position:
Rectangle rect = fldPos.position;
image1.scaleToFit(rect.getWidth(), rect.getHeight());
image1.setAbsolutePosition(rect.getLeft(), rect.getBottom());
PdfContentByte overContent = stamper.getOverContent(fldPos.page);
In these lines you scale the image so that it fits the field and you set the coordinates of the image. You also get the PdfContentByte instance for the correct page instead of from the first page.
You may have other errors, but please fix these first!

iTextSharp z-index

I'm using itextSharp to add anotations in a pdf document.
I have a pdf document that already contains an image saved in it, it's a stamp.
So I draw some stroke on this pdf in the stamp and everything is fine when I draw them in my WPF but when I send the pdf by email using iTextSharp for the conversion the line I drawed is now below the stamp.
How I can solve this problem ?
Thank you
The explanation you posted as an answer (BTW, more apropos would have been to edit your question to contain that data) explains the issue.
There are two principal types of objects visible on a PDF page:
the PDF page content;
annotations associated with the page.
The annotations are always displayed above the page content if they are displayed at all.
In your case you add the image to the PDF page content (using OverContent or UnderContent only changes where in relation to other PDF page content material your additions appear). The stamp, on the other hand, most likely is realized by means of an annotation. Thus, the stamp annotation always is above your additions.
If you want to have your additions appear above the stamp, you either have to add your additions as some kind of annotation, too, or you have to flatten the stamp annotation into the page content before adding your stuff.
Which of these varients is better, depends on the requirements you have. Are there any requirements forcing the stamp to remain a stamp annotation? Are there any requirements forcing your additions to remain part of the content? Please elaborate your requirements. As content and annotations have some different properties when displayed or printed, please state all requirements.
And furthermore, please supply sample documents.
So like I said the original pdf have a stamp saved inside it, if I open the pdf with acrobat reader I can move the stamp.
So here my code to write some strokes :
using (var outputStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write, FileShare.Read))
using (var intputStream = new FileStream(pathPdf, FileMode.Open, FileAccess.Read, FileShare.Read))
{
PdfReader reader = new PdfReader(intputStream);
using (var pdfStamper = new PdfStamper(reader, outputStream))
{
foreach (var page in pages)
{
if (page != null && page.ExportedImages.HasItems())
{
PdfContentByte pdfContent = pdfStamper.GetOverContent(page.PageIndex);
Rectangle pageSize = reader.GetPageSizeWithRotation(page.PageIndex);
PdfLayer pdfLayer = new PdfLayer(string.Format(ANNOTATIONNAMEWITHPAGENAME, page.PageIndex), pdfContent.PdfWriter);
foreach (ExporterEditPageInfoImage exportedInfo in page.ExportedImages)
{
Image image = PngImage.GetImage(exportedInfo.Path);
image.Layer = pdfLayer;
if (quality == PublishQuality.Normal || quality == PublishQuality.Medium || quality == PublishQuality.High)
{
float width = (float)Math.Ceiling((image.Width / image.DpiX) * 72);
float height = (float)Math.Ceiling((image.Height / image.DpiY) * 72);
image.ScaleAbsolute(width, height);
float x = (float)(exportedInfo.HorizontalTile * (page.TileSize * (72 / 96d)));
float y = (float)Math.Max(0, (pageSize.Height - ((exportedInfo.VerticalTile + 1) * (page.TileSize * (72 / 96d)))));
image.SetAbsolutePosition(x, y);
}
else
throw new NotSupportedException();
pdfContent.AddImage(image);
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
}
pdfStamper.Close();
}
}
So my strokes are saved good in the pdf the problem the stamp is always on top of everything and I think is normal so can I do a workaround for this ?