I need to create PDF using some java API which has the capabilities like HTML. Basically, I want to create a form which can take input from users, perform some basic client side validations and also generate this PDF programmatically using Java. I am also looking for rich HTML like features like expand, collapse, hyperlinks, add a section on a button click etc. So basically I am trying to create an HTML like page but within PDF.
I have tried using itext but able to only to do only handful of things and not able to add dynamism into PDF. Is there any tool/API which supports this?
PDF itself allows you to embed (a subset of) javascript.
This embedded code can be linked to document events (e.g. opening the document) or specific form elements (e.g. clicking a button, changing the text in a text-input field).
This is a page from their website entitled 'Making a PDF interactive' which focusses on adding form elements.
The book (iText in Action) by Bruno Lowagie (original founder of iText) also goes in great detail. It even shows how to program a calculator in a PDF document, page 232.
I'm just going to copy-paste the relevant section here.
Listing 7.29 Calculator
public void addTextField(PdfWriter writer, Rectangle rect, String name) {
PdfFormField field = PdfFormField.createTextField(writer, false, false, 0);
field.setFieldName(name);
field.setWidget(rect, PdfAnnotation.HIGHLIGHT_NONE);
field.setQuadding(PdfFormField.Q_RIGHT);
field.setFieldFlags(PdfFormField.FF_READ_ONLY);
writer.addAnnotation(field);
}
public void addPushButton(PdfWriter writer, Rectangle rect, String btn, String script) {
float w = rect.getWidth();
float h = rect.getHeight();
PdfFormField pushbutton = PdfFormField.createPushButton(writer);
pushbutton.setFieldName("btn_" + btn);
pushbutton.setWidget(rect, PdfAnnotation.HIGHLIGHT_PUSH);
PdfContentByte cb = writer.getDirectContent();
pushbutton.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, createAppearance(cb, btn, BaseColor.GRAY, w, h));
pushbutton.setAppearance(PdfAnnotation.APPEARANCE_ROLLOVER, createAppearance(cb, btn, BaseColor.RED, w, h));
pushbutton.setAppearance(PdfAnnotation.APPEARANCE_DOWN, createAppearance(cb, btn, BaseColor.BLUE, w, h));
pushbutton.setAdditionalActions(PdfName.U, PdfAction.javaScript(script, writer));
pushbutton.setAdditionalActions(PdfName.E, PdfAction.javaScript( "this.showMove('" + btn + "');", writer));
pushbutton.setAdditionalActions(PdfName.X, PdfAction.javaScript( "this.showMove(' ');", writer));
writer.addAnnotation(pushbutton);
}
public PdfAppearance createAppearance(PdfContentByte cb, String btn, BaseColor color, float w, float h) {
PdfAppearance app = cb.createAppearance(w, h);
app.setColorFill(color);
app.rectangle(2, 2, w - 4, h - 4);
app.fill();
app.beginText();
app.setColorFill(BaseColor.BLACK);
app.setFontAndSize(bf, h / 2);
app.showTextAligned(Element.ALIGN_CENTER, btn, w / 2, h / 4, 0);
app.endText();
return app;
}
Related
I'm trying to create a PDF that's PAC3 approved for accessibility but I'm having an issue when it comes to the links I'm writing. This is the code I have one function that draws the text and tags it given the structType (this function is from https://github.com/chris271/UAPDFBox/blob/a8b280bcbc838722ba872d6b382b8fd45bd35303/src/com/wi/test/util/PDFormBuilder.java)
public PDStructureElement drawElement(Cell textCell, float x, float y, float height,PDStructureElement parent,
String structType, int pageIndex) throws IOException {
//Set up the next marked content element with an MCID and create the containing H1 structure element.
PDPageContentStream contents = new PDPageContentStream(pdf, pages.get(pageIndex),
PDPageContentStream.AppendMode.APPEND, false);
currentElem = addContentToParent(null, structType, parent);
setNextMarkedContentDictionary();
contents.beginMarkedContent(COSName.ARTIFACT, PDPropertyList.create(currentMarkedContentDictionary));
//Draws the cell itself with the given colors and location.
drawDataCell(textCell, x, y + height, height * 2, contents);
contents.endMarkedContent();
addContentToParent(COSName.ARTIFACT, null, currentElem);
contents.close();
//Set up the next marked content element with an MCID and create the containing P structure element.
contents = new PDPageContentStream(pdf, pages.get(pageIndex), PDPageContentStream.AppendMode.APPEND, false);
setNextMarkedContentDictionary();
contents.beginMarkedContent(COSName.P, PDPropertyList.create(currentMarkedContentDictionary));
//Draws the given text centered within the current table cell.
drawCellText(textCell, x, y + height + textCell.getFontSize(), contents, pages.get(pageIndex).getResources());
//End the marked content and append it's P structure element to the containing P structure element.
contents.endMarkedContent();
addContentToParent(COSName.P, null, currentElem);
contents.close();
return currentElem;
}
After it's written to the page I call a separate function to add the underline for hyperlinks
public void drawLink(float firstX,float firstY,float lastX,float width, float height,int pageNum, float lastCharWidth) throws IOException {
PDAnnotationTextMarkup markup = new PDAnnotationTextMarkup(PDAnnotationTextMarkup.SUB_TYPE_UNDERLINE);
PDRectangle position = new PDRectangle();
position.setLowerLeftX(firstX);
position.setLowerLeftY(firstY);
position.setUpperRightX(firstX + width);
position.setUpperRightY(firstY + height);
markup.setRectangle(position);
//need to modify quadpoints lastX so it's inline with link
float quadPoints[] = {firstX, firstY + height + 2,
lastX + lastCharWidth, firstY + height + 2,
firstX, firstY - 5,
lastX + lastCharWidth, firstY - 5};
markup.setQuadPoints(quadPoints);
PDColor color = new PDColor(new float[]{ 1, 15/ 255F, 1 }, PDDeviceRGB.INSTANCE);
markup.setColor(color);
pdf.getPage(pageNum).getAnnotations().add(markup);
}
The result of calling these 2 functions are below, it draws the link properly but I'm missing information for the annotation. My question is how do I utilize the PDAnnotation object to create a PAC3 approved annotation?
Image of PAC3 detailed results
Following the given examples pdfClown can both highlight a specific text and draw a rectangle around the respective words.
Is there a possibility to make this reactangle editable afterwards with the Adobe Acrobat?
My current workflow (as planned):
Import a document
Search document for Highlightings
Determine the Hightlighting's color
Draw a rectangle around the outer boundaries of the rectangle
add a callout to a another rectangle containing a letter, depending on the determined color
I can not (e.g.) drag the rectangle around the formerly highlighted word with Acrobat Reader, as far as I can see. I used the provided example from pdfClown's webpage to draw a reactangle around every character.
Is there something that I forgot to consider?
File inFile = null;
String inFilePath = "/path/to/inputFile/input_highlight.pdf";
String outDirPath = "/tmp";
try {
inFile = new File(inFilePath);
} catch (Exception e) {
throw new RuntimeException(inFilePath + " file access error.", e);
}
Document document = inFile.getDocument();
Pages pages = document.getPages();
PageStamper stamper = new PageStamper();
for (Page page : pages) {
stamper.setPage(page);
PageAnnotations annotations = page.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation.getColor() == null) {
continue;
}
Rectangle2D textStringBox = annotation.getBox();
PrimitiveComposer composer = stamper.getBackground();
composer.setStrokeColor(DeviceRGBColor.Black);
textStringBox.setRect(annotation.getBox().getX(), annotation.getBox().getY(), annotation.getBox().getWidth(), annotation.getBox().getHeight());
composer.drawRectangle(textStringBox);
composer.stroke();
composer.beginLocalState();
composer.setStrokeColor(DeviceRGBColor.Black);
composer.end();
stamper.flush();
System.out.println("Text: " + annotation.getText());
System.out.println("Color: " + annotation.getColor());
System.out.println("Coordinates: " + annotation.getBox().toString());
annotation.setColor(DeviceRGBColor.White);
}
}
As it seems your main issue is that
I can not (e.g.) drag the rectangle around the formerly highlighted word with Acrobat Reader
The reason is that you draw your rectangle in the page content (the PageStamper you use is documented as Tool for content insertion into existing pages). The page content is fixed, in particular as far as Acrobat Reader is concerned; Acrobat Reader only allows you to move annotations.
If you want to have a rectangle you can drag around, therefore, you have to use a rectangle annotation. Rectangle annotations can be created like this:
new org.pdfclown.documents.interaction.annotations.Rectangle(
page,
new Rectangle(50, 370, 100, 30),
"Text of the Rectangle annotation"
).withColor(DeviceRGBColor.get(Color.RED))
.withBorder(new Border(1, new LineDash(new double[]{5})))
.withAuthor("Stefano")
.withSubject("Rectangle")
.withPopup(new Popup(
page,
new Rectangle2D.Double(200, 325, 200, 75),
"Text of the Popup annotation (this text won't be visible as associating popups to markup annotations overrides the former's properties with the latter's)"
));
(AnnotationSample.java)
You also mention that you want to add a callout; a callout annotation can be created like this:
new StaticNote(
page,
new Rectangle(250, 90, 150, 70),
"Text of the Callout note annotation"
).withLine(
new StaticNote.CalloutLine(
page,
new Point(250,125),
new Point(150,125),
new Point(100,100)
)
)
.withLineEndStyle(LineEndStyleEnum.OpenArrow)
.withBorder(new Border(1))
.withColor(DeviceRGBColor.get(Color.YELLOW));
(AnnotationSample.java)
When I extract an image using pdfbox I am getting incorrect dpi of the image for some PDFs. When I extract an image using Photoshop or Acrobat Reader Pro I can see that the dpi of the image is 200 using windows photo viewer, but when I extract the image using pdfbox the dpi is 72.
For extracting the image I am using following code :
Not able to extract images from PDFA1-a format document
When I check the logs I see an unusual entry:
2015-01-23-main--DEBUG-org.apache.pdfbox.util.TIFFUtil:
<?xml version="1.0" encoding="UTF-8"?><javax_imageio_jpeg_image_1.0>
<JPEGvariety>
<app0JFIF majorVersion="1" minorVersion="2" resUnits="0" Xdensity="1" Ydensity="1" thumbWidth="0" thumbHeight="0"/>
</JPEGvariety>
<markerSequence>
<dqt>
<dqtable elementPrecision="0" qtableId="0"/>
<dqtable elementPrecision="0" qtableId="1"/>
</dqt>
<dht>
<dhtable class="0" htableId="0"/>
<dhtable class="0" htableId="1"/>
<dhtable class="1" htableId="0"/>
<dhtable class="1" htableId="1"/>
</dht>
<sof process="0" samplePrecision="8" numLines="0" samplesPerLine="0" numFrameComponents="3">
<componentSpec componentId="1" HsamplingFactor="2" VsamplingFactor="2" QtableSelector="0"/>
<componentSpec componentId="2" HsamplingFactor="1" VsamplingFactor="1" QtableSelector="1"/>
<componentSpec componentId="3" HsamplingFactor="1" VsamplingFactor="1" QtableSelector="1"/>
</sof>
<sos numScanComponents="3" startSpectralSelection="0" endSpectralSelection="63" approxHigh="0" approxLow="0">
<scanComponentSpec componentSelector="1" dcHuffTable="0" acHuffTable="0"/>
<scanComponentSpec componentSelector="2" dcHuffTable="1" acHuffTable="1"/>
<scanComponentSpec componentSelector="3" dcHuffTable="1" acHuffTable="1"/>
</sos>
</markerSequence>
</javax_imageio_jpeg_image_1.0>
I tried to google but I can see to find out what pdfbox means by this log. What does this mean?
You can download a sample pdf with this problem from this link:
http://myslams.com/test/1.pdf
I have even tried itext but it is extracting image with 96 dpi.
Am I doing something wrong? Or pdfbox and itext have this limitation?
After some digging I found your 1.pdf. Thus,...
PDFBox
In comments to this recent answer #Tilman and you were discussing this older answer in which #Tilman pointed towards the PrintImageLocations PDFBox example. I ran it for your file and got:
Processing page: 0
*******************************************************************
Found image [Im0]
position = 0.0, 0.0
size = 1704px, 888px
size = 613.44, 319.68
size = 8.52in, 4.44in
size = 216.408mm, 112.776mm
Processing page: 1
*******************************************************************
Found image [Im0]
position = 0.0, 0.0
size = 1704px, 2800px
size = 613.44, 1008.0
size = 8.52in, 14.0in
size = 216.408mm, 355.6mm
Processing page: 2
*******************************************************************
Found image [Im0]
position = 0.0, 0.0
size = 1704px, 2800px
size = 613.44, 1008.0
size = 8.52in, 14.0in
size = 216.408mm, 355.6mm
Processing page: 3
*******************************************************************
Found image [Im0]
position = 0.0, 0.0
size = 1704px, 1464px
size = 613.44, 527.04
size = 8.52in, 7.3199997in
size = 216.408mm, 185.928mm
On all pages this amounts to 200 dpi both in x and y directions (1704px / 8.52in = 888px / 4.44in = 2800px / 14.0in = 1464px / 7.32in = 200 dpi).
So PDFBox gives you the dpi values you are after.
(#Tilman: The current 2.0.0-SNAPSHOT version of that sample returns utter nonsense; you might want to fix this.)
iText
A simplified iText version of that PDFBox example would be this:
public void printImageLocations(InputStream stream) throws IOException
{
PdfReader reader = new PdfReader(stream);
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
ImageRenderListener listener = new ImageRenderListener();
for (int page = 1; page <= reader.getNumberOfPages(); page++)
{
System.out.printf("\nPage %s:\n", page);
parser.processContent(page, listener);
}
}
static class ImageRenderListener implements RenderListener
{
public void beginTextBlock() { }
public void renderText(TextRenderInfo renderInfo) { }
public void endTextBlock() { }
public void renderImage(ImageRenderInfo renderInfo)
{
try
{
PdfDictionary imageDict = renderInfo.getImage().getDictionary();
float widthPx = imageDict.getAsNumber(PdfName.WIDTH).floatValue();
float heightPx = imageDict.getAsNumber(PdfName.HEIGHT).floatValue();
float widthUu = renderInfo.getImageCTM().get(Matrix.I11);
float heigthUu = renderInfo.getImageCTM().get(Matrix.I22);
System.out.printf("Image %.0fpx*%.0fpx, %.0fuu*%.0fuu, %.2fin*%.2fin\n", widthPx, heightPx, widthUu, heigthUu, widthUu/72, heigthUu/72);
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
(Beware: I assumed unrotated and unskewed images.)
The results for your file:
Page 1:
Image 1704px*888px, 613uu*320uu, 8,52in*4,44in
Page 2:
Image 1704px*2800px, 613uu*1008uu, 8,52in*14,00in
Page 3:
Image 1704px*2800px, 613uu*1008uu, 8,52in*14,00in
Page 4:
Image 1704px*1464px, 613uu*527uu, 8,52in*7,32in
Thus, also 200dpi all along. So iText, too, gives you the dpi values you are after.
Your code
Obviously the code you referenced had no chance to report a dpi value sensible in the context of the PDF because it only extracts the images as found in the resources but ignores how the respective image resource is used on the page.
An image resource can be stretched, rotated, skewed, ... any way the author likes when he uses it in the page content.
BTW, a dpi value only makes sense if the author did not skew and rotated only by a multiple of 90°.
Is there an image within the JRE that can be used for testing purposes? I'm looking for either an Image, BufferImage or Icon object. I did find a PNG file in the JRE path that I am currently using but looking to see what others have found or are using.
Try the following. This code will generate a test image of any resolution. It does not use a built-in image but I think this will work best for you. Tweak as necessary to meet your needs.
static private Image createTestImage(final int resolution) {
final Image image = new BufferedImage(resolution, resolution, BufferedImage.TYPE_INT_ARGB);
final Graphics g = image.getGraphics();
final int points = (resolution * 72) / 96;
g.setColor(new Color(.42f, .42f, 1.0f, .5242f));
g.setFont(new Font("Dialog", Font.BOLD, points));
g.drawString("!X!", 2, points);
g.setColor(Color.BLACK);
g.drawOval(0, 0, image.getWidth(null) - 1, image.getHeight(null) - 1);
g.drawOval(11, 11, image.getWidth(null) - 23, image.getHeight(null) - 23);
g.drawOval(22, 22, image.getWidth(null) - 45, image.getHeight(null) - 45);
return image;
}
Using
Image image = createTestImage(1024);
Produces a hi res image like:
Using
Image image = createTestImage(64);
Produces a lo res image like:
Depending on the OS, there are a number of image files bundled with the JRE...
There are images in C:\Program Files\Java\jre7\lib\images\cursors on Windows, and on Linux I found:
denis#laptop:~/Programs/jdk1.7.0_11/jre$ find | grep png
./lib/deploy/mixcode_s.png
./lib/images/icons/sun-java.png
./lib/images/icons/sun-java_HighContrast.png
./lib/images/icons/sun-java_HighContrastInverse.png
./lib/images/icons/sun-java_LowContrast.png
... (many others) ...
I'm generating a PDF using iText and wanted to include an iText Form and TextFields so that users can fill in the PDF Form electronically, rather than printing it out.
I've followed the Examples here: http://itextpdf.com/examples/iia.php?id=161
and here: http://itextpdf.com/examples/iia.php?id=162
So far, I have a TextField on my Landscape PDF, and I can click and enter text.
I have two issues:
TextField alignment. To click on the text field, I have to position my mouse to the right of the cell that I added the TextField too. How can I align the TextField with the cell?
Entered Text Rotation. Once I've entered text, it's displayed rotated and 90 degrees to the rest of the page. How can I set the rotation of this displayed text?
The rotation of the entered text is the thing that bugs me th most. I've tried setting the rotation on the cell and the TextField but that seems to have no effect.
Any suggestions?
Thanks
Here is my code:
int rotation = document.getPageSize().getRotation();
PdfFormField pdfForm = PdfFormField.createEmpty(docWriter);
coverSheetPdfForm.setFieldName("Form");
coverSheetPdfForm.setRotate(rotation);
...
cell = new PdfPCell(borderedTable("Cell Title:", tblHeadingFont, "", borderColor));
cell.setColspan(1);
cell.setPadding(5f);
cell.setBorderWidth(0f);
cell.setHorizontalAlignment(PdfPCell.ALIGN_LEFT);
cell.setMinimumHeight(100f);
TextField textField = new TextField(docWriter, new Rectangle(50, 50,rotation), "cell_text_field");
textField.setFontSize(12);
textField.setRotation(rotation);
textField.setVisibility(TextField.VISIBLE);
textField.setBorderColor(borderColor);
textField.setBorderWidth(BORDER_WIDTH);
cell.setCellEvent(new ChildFieldEvent(pdfForm, textField.getTextField(), 1, rotation));
.....
docWriter.addAnnotation(pdfForm);
and the ChildFieldEvent code that I've borrowed from the example page, and added a extra parameter for rotation:
/**
* Creates a ChildFieldEvent.
* #param parent the parent field
* #param kid the child field
* #param padding a padding
* #param rotation
*/
public ChildFieldEvent(PdfFormField parent, PdfFormField kid, float padding, int rotation) {
this.parent = parent;
this.kid = kid;
this.padding = padding;
this.rotation = rotation;
}
/**
* Add the child field to the parent, and sets the coordinates of the child field.
* #param cell
* #param rect
* #param cb
* #see com.lowagie.text.pdf.PdfPCellEvent#cellLayout(com.lowagie.text.pdf.PdfPCell,
* com.lowagie.text.Rectangle, com.lowagie.text.pdf.PdfContentByte[])
*/
#Override
public void cellLayout(PdfPCell cell, Rectangle rect, PdfContentByte[] cb) {
try {
parent.addKid(kid);
kid.setWidget(new Rectangle(rect.getLeft(padding), rect.getBottom(padding),
rect.getRight(padding), rect.getTop(padding),rotation),
PdfAnnotation.HIGHLIGHT_INVERT);
} catch (Exception e) {
throw new ExceptionConverter(e);
}
}
I've done some more testing and it looks like the Ubuntu PDF fiewer (Document Viewer) is not rendering the PDF correctly as viewing the same file in Windows Adobe PDF Viewer, the form looks fine.
I'll do more testing and report back.