If I use font size appearance.setLayer2FontSize(6.0f); it sets font size for both Name and Description.
PdfReader reader = null;
PdfSigner signer = null;
try {
reader = new PdfReader(inStream);
signer = new PdfSigner(reader, pdfos, false);
} catch (IOException e) {
LOGGER.error("Error while loading PDF");
throw new DigitalSignException("Error while loading PDF", e);
}
int noOfPages = signer.getDocument().getNumberOfPages();
PdfSignatureAppearance appearance = signer.getSignatureAppearance().setReason(reason).setLocation(loc)
.setReuseAppearance(false);
Rectangle rect = new Rectangle(250, 100, 200, 80);
appearance.setRenderingMode(RenderingMode.NAME_AND_DESCRIPTION);
appearance.setLayer2FontSize(6.0f);
appearance.setPageRect(rect).setPageNumber(noOfPages);
signer.setFieldName("sign");
// Creating the signature
IExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, bouncyCastleProvider.getName());
IExternalDigest digest = new BouncyCastleDigest();
try {
signer.signDetached(digest, pks, chain, null, null, null, 0, subfilter);
} catch (IOException | GeneralSecurityException e) {
LOGGER.error("Error while adding digital signature to PDF");
throw new DigitalSignException("Error while adding digital signature to PDF", e);
}
Is there a way to set different font sizes for Name and description
(Name should be little bigger than description)
The whole Layer2Text is a single String, whether you set it or iText builds it, and it is typeset as a single paragraph using a single font and font size. Thus, NO, you cannot ask iText to draw your Layer2Text or its default text using multiple styles for different parts of it.
What you can do, though, is to retrieve the PdfFormXObject Layer2 before iText has created its appearance on it, and you can draw anything in any style on it.
So, instead of
appearance.setRenderingMode(RenderingMode.NAME_AND_DESCRIPTION);
appearance.setLayer2FontSize(6.0f);
appearance.setPageRect(rect).setPageNumber(noOfPages);
you'd do
appearance.setPageRect(rect).setPageNumber(noOfPages);
PdfFormXObject layer2 = getLayer2();
[...shape the layer2 contents as you desire...]
Of course you can use the source of the PdfSignatureAppearance method getAppearance for inspiration, in particular if you don't want your design to deviate much from the default.
Thus, YES, you can completely customize the signature appearance.
For example
An example customized layer2 content might be shaped like this:
PdfFormXObject layer2 = appearance.getLayer2();
PdfCanvas canvas = new PdfCanvas(layer2, signer.getDocument());
float MARGIN = 2;
PdfFont font = PdfFontFactory.createFont();
String name = null;
CertificateInfo.X500Name x500name = CertificateInfo.getSubjectFields((X509Certificate)chain[0]);
if (x500name != null) {
name = x500name.getField("CN");
if (name == null)
name = x500name.getField("E");
}
if (name == null)
name = "";
Rectangle dataRect = new Rectangle(rect.getWidth() / 2 + MARGIN / 2, MARGIN, rect.getWidth() / 2 - MARGIN, rect.getHeight() - 2 * MARGIN);
Rectangle signatureRect = new Rectangle(MARGIN, MARGIN, rect.getWidth() / 2 - 2 * MARGIN, rect.getHeight() - 2 * MARGIN);
try (Canvas layoutCanvas = new Canvas(canvas, signer.getDocument(), signatureRect);) {
Paragraph paragraph = new Paragraph(name).setFont(font).setMargin(0).setMultipliedLeading(0.9f).setFontSize(20);
layoutCanvas.add(paragraph);
}
try (Canvas layoutCanvas = new Canvas(canvas, signer.getDocument(), dataRect);) {
Paragraph paragraph = new Paragraph().setFont(font).setMargin(0).setMultipliedLeading(0.9f);
paragraph.add(new Text("Digitally signed by ").setFontSize(6));
paragraph.add(new Text(name + '\n').setFontSize(9));
paragraph.add(new Text("Date: " + new SimpleDateFormat("yyyy.MM.dd HH:mm:ss z").format(signer.getSignDate().getTime()) + '\n').setFontSize(6));
paragraph.add(new Text("Reason: " + appearance.getReason() + '\n').setFontSize(6));
paragraph.add(new Text("Location: " + appearance.getLocation()).setFontSize(6));
layoutCanvas.add(paragraph);
}
This essentially is a copy&paste&refactoring of the iText code creating its default appearance with different font sizes for different text parts of it.
Related
I am having html content store as a raw string in my database and I like to print it in pdf, but with custom size, for example page size to be 10cm width and 7 com height, not standard A4 format.
Can someone gives me some examples if it is possible.
ByteArrayOutputStream out = new ByteArrayOutputStream();
PDRectangle rec = new PDRectangle(recWidth, recHeight);
PDPage page = new PDPage(rec);
try (PDDocument document = new PDDocument()) {
PdfRendererBuilder builder = new PdfRendererBuilder();
builder.defaultTextDirection(BaseRendererBuilder.TextDirection.LTR);
String htmlContent = "<b>Hello world</b>" + content;
builder.withHtmlContent(htmlContent, "");
document.addPage(page);
builder.usePDDocument(document);
PdfBoxRenderer renderer = builder.buildPdfRenderer();
renderer.createPDFWithoutClosing();
document.save(out);
} catch (Exception e) {
ex.printStackTrace();
}
return new ByteArrayInputStream(out.toByteArray());
This code generates for me 2 files, one small and one A4.
UPDATE:
I tried this one:
try (PDDocument document = new PDDocument()) {
PdfRendererBuilder builder = new PdfRendererBuilder();
builder.defaultTextDirection(BaseRendererBuilder.TextDirection.LTR);
builder.useDefaultPageSize(210, 297, PdfRendererBuilder.PageSizeUnits.MM);
builder.usePdfAConformance(PdfRendererBuilder.PdfAConformance.PDFA_3_A);
String htmlContent = "<b>content</b>";
builder.withHtmlContent(htmlContent, "");
builder.usePDDocument(document);
PdfBoxRenderer renderer = builder.buildPdfRenderer();
renderer.createPDFWithoutClosing();
document.save(out);
} catch (Exception e) {
log.error(">>> The creation of PDF is invalid!");
}
But in this case content is not shown, if I remove useDefaultPageSize, content will be shown
I didn't check this solution before, but try initialise the builder object with your desired page size and document type like below
builder.useDefaultPageSize(210, 297, PdfRendererBuilder.PageSizeUnits.MM);
builder.usePdfAConformance(PdfRendererBuilder.PdfAConformance.PDFA_3_A);
the lib include many PDF format next is PdfAConformance Enum with possible values
PdfAConformance Enum
I have created a PDF with Adobe, which contains an image field called "logo"
Now if i want to add a picture using PDFBox it won't be displayed in the created pdf.
However no error message is thrown and debugging looks completely fine with a correctly created PDImageXObject object.
The used code is mostly adapted from this question:
public static void setImageField(PDDocument pdfDocument, PDAcroForm acroForm, String fieldKey, byte[] imageData)
{
final PDField retrievedField = acroForm.getField(fieldKey);
if (!(retrievedField instanceof PDPushButton)) {
throw new RuntimeException("The field: " + fieldKey + " is not of the correct type");
}
LOGGER.info("Received field: " + retrievedField.getPartialName()); // correct field is being logged
final PDPushButton imageField = (PDPushButton) retrievedField;
final List<PDAnnotationWidget> fieldWidgets = imageField.getWidgets(); // it has exactly one widget, which would be the action to set the image
if (fieldWidgets == null || fieldWidgets.size() <= 0) {
throw new RuntimeException("Misconfiguration for field: " + fieldKey + ", it has no widgets(actions)");
}
final PDAnnotationWidget imageWidget = fieldWidgets.get(0);
PDImageXObject imageForm;
try {
// This test data is resized to be smaller than the bounding box of the image element in the PDF. Just for testing purposes
final String testImage = "iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAARUlEQVR42u3PMREAAAgEIO2fzkRvBlcPGtCVTD3QIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIXC7VGjKHva+IvAAAAAElFTkSuQmCC";
final byte[] testData = Base64.getDecoder().decode(testImage);
imageForm = PDImageXObject.createFromByteArray(pdfDocument, testData, "logo"); // imageForm is being populated with data and no error thrown
}
catch (IOException e) {
throw new RuntimeException("Error creating Image from image data", e);
}
final float imageScaleRatio = (float) (imageForm.getHeight() / imageForm.getWidth());
final PDRectangle imagePosition = getFieldArea(imageField);
LOGGER.info("Received image position: " + imagePosition.toString());
// Retrieve the height and width and position of the rectangle where the picture will be
final float imageHeight = imagePosition.getHeight();
LOGGER.info("Image height: " + imageHeight);
final float imageWidth = imageHeight / imageScaleRatio;
LOGGER.info("Image width: " + imageWidth);
final float imageXPosition = imagePosition.getLowerLeftX();
LOGGER.info("Image X position: " + imageXPosition);
final float imageYPosition = imagePosition.getLowerLeftY();
LOGGER.info("Image Y position: " + imageYPosition);
final PDAppearanceStream documentAppearance = new PDAppearanceStream(pdfDocument);
documentAppearance.setResources(new PDResources()); // not sure why this is done
// Weird "bug" in pdfbox forces to create the contentStream always after the object to add
try (PDPageContentStream documentContentStream = new PDPageContentStream(pdfDocument, documentAppearance)) {
documentContentStream.drawImage(imageForm, imageXPosition, imageYPosition, imageWidth, imageHeight);
}
catch (IOException e) {
throw new RuntimeException("Error drawing the picture in the document", e);
}
// Setting the boundary box is mandatory for the image field! Do not remove or the flatten call on the acroform will NPE
documentAppearance.setBBox(new PDRectangle(imageXPosition, imageYPosition, imageWidth, imageHeight));
// Apply the appearance settings of the document onto the image widget ( No border )
setImageAppearance(imageWidget, documentAppearance);
// This code is normally somewhere else but for SO copied in this method to show how the pdf is being created
acroForm.flatten();
AccessPermission ap = new AccessPermission();
ap.setCanModify(false);
ap.setCanExtractContent(false);
ap.setCanFillInForm(false);
ap.setCanModifyAnnotations(false);
ap.setReadOnly();
StandardProtectionPolicy spp = new StandardProtectionPolicy("", "", ap);
spp.setEncryptionKeyLength(PdfBuildConstants.ENCRYPTION_KEY_LENTGH);
pdfDocument.protect(spp);
pdfDocument.save(pdfFile);
pdfDocument.close();
}
/**
* Applies the appearance settings of the document onto the widget to ensure a consistent look of the document.
*
* #param imageWidget The {#link PDAnnotationWidget Widget} to apply the settings to
* #param documentAppearance The {#link PDAppearanceStream Appearance settings} of the document
*/
private static void setImageAppearance(final PDAnnotationWidget imageWidget,
final PDAppearanceStream documentAppearance)
{
PDAppearanceDictionary widgetAppearance = imageWidget.getAppearance();
if (widgetAppearance == null) {
widgetAppearance = new PDAppearanceDictionary();
imageWidget.setAppearance(widgetAppearance);
}
widgetAppearance.setNormalAppearance(documentAppearance);
}
/**
* Retrieves the dimensions of the given {#link PDField Field} and creates an {#link PDRectangle Rectangle} with the
* same dimensions.
*
* #param field The {#link PDField Field} to create the rectangle for
* #return The created {#link PDRectangle Rectangle} with the dimensions of the field
*/
private static PDRectangle getFieldArea(PDField field) {
final COSDictionary fieldDictionary = field.getCOSObject();
final COSBase fieldAreaValue = fieldDictionary.getDictionaryObject(COSName.RECT);
if (!(fieldAreaValue instanceof COSArray)) {
throw new RuntimeException("The field: " + field.getMappingName() + " has no position values");
}
final COSArray fieldAreaArray = (COSArray) fieldAreaValue;
return new PDRectangle(fieldAreaArray);
}
I also looked at other questions such as this, but I can't use PDJpeg since it is not available in the current version. Additionally the original image to use can be anything from jpeg,png to gif.
I validated that the position and dimension variables being logged have the same value as the image field in the pdf file. (properties of the field)
UPDATE:
Here is an example zip containing the template pdf and the generated pdf which fills in the form fields: file upload or Dropbox
The example picture was a 50x50 png with plain green color generated from online png pixel
I have successfully converted an Image to Pdf. My issue is that the pdf is displaying half of the width
My Code:
#FXML
private void print() {
try {
WritableImage nodeshot = stackPane.snapshot(new SnapshotParameters(), null);
File file = new File("C:/Users/Andre Kelvin/Desktop/TheNode.png");
ImageIO.write(SwingFXUtils.fromFXImage(nodeshot, null), "png", file);
PDDocument doc = new PDDocument();
PDPage page = new PDPage();
PDImageXObject pdimage;
PDPageContentStream content;
pdimage = PDImageXObject.createFromFile("C:/Users/Andre Kelvin/Desktop/TheNode.png", doc);
content = new PDPageContentStream(doc, page);
content.drawImage(pdimage, 0, 0);
content.close();
doc.addPage(page);
doc.save("C:/Users/Andre Kelvin/Desktop/PDFNode.pdf");
doc.close();
file.delete();
//This Line Automatically Opens the user defualt pdf file viewer
Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + "C:/Users/Andre Kelvin/Desktop/PDFNode.pdf");
} catch (Exception e) {
}
}
I have tried getting the root node width and height by using this line:
content.drawImage(pdimage, 0, 0,(float)stackPane.getPrefWidth(),(float)stackPane.getPrefHeight());
and this:
content.drawImage(pdimage, 0, 0,(float)stackPane.getMaxWidth(),(float)stackPane.getMaxHeight());
it will just display a blank white page.
This is the actual image that is Converted to pdf:
And this is the pdf of the image:
Neither the preferred size properties nor their min/max counterparts allow you to reliably determine the size of a Region. Those are just indicators and the calculated values may not match. Furthermore the Region may be resized to sizes other than the preferred size. Last but not least those properties may contain special values Region.USE_PREF_SIZE(=Double.NEGATIVE_INFINITY) and Region.USE_COMPUTED_SIZE(=-1) and even do so by default.
If you need to get the size of a node, use the boundsInLocal property:
Bounds bounds = stackPane.getBoundsInLocal();
In this case it's simpler to get the size of the snapshot instead though.
Furthermore the page size of the PDPage may not be large enough to contain the whole image. You need to scale the image instead or change the page size of the PDPage.
Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + "C:/Users/Andre Kelvin/Desktop/PDFNode.pdf");
This can be done platform-independent using the HostServices available via the Application instance.
Example
#Override
public void start(Stage primaryStage) {
Button button = new Button("print");
StackPane root = new StackPane(button);
button.setOnAction(evt -> {
try {
WritableImage nodeshot = root.snapshot(new SnapshotParameters(), null);
// store image in-memory
ByteArrayOutputStream output = new ByteArrayOutputStream();
ImageIO.write(SwingFXUtils.fromFXImage(nodeshot, null), "png", output);
output.close();
PDDocument doc = new PDDocument();
PDPage page = new PDPage();
PDImageXObject pdimage;
PDPageContentStream content;
pdimage = PDImageXObject.createFromByteArray(doc, output.toByteArray(), "png");
content = new PDPageContentStream(doc, page);
// fit image to media box of page
PDRectangle box = page.getMediaBox();
double factor = Math.min(box.getWidth() / nodeshot.getWidth(), box.getHeight() / nodeshot.getHeight());
float height = (float) (nodeshot.getHeight() * factor);
// beware of inverted y axis here
content.drawImage(pdimage, 0, box.getHeight() - height, (float) (nodeshot.getWidth() * factor), height);
content.close();
doc.addPage(page);
File outputFile = new File("C:/Users/Andre Kelvin/Desktop/PDFNode.pdf");
doc.save(outputFile);
doc.close();
getHostServices().showDocument(outputFile.toURI().toString());
} catch (Exception e) {
}
});
Scene scene = new Scene(root, 300, 300);
primaryStage.setScene(scene);
primaryStage.show();
}
I state that I use NetBeans 8.2. I need to transfer a jframe (which represents an invoice) on a paragraph of IText. Everything works and is displayed. However, I get that the writings and details of the pdf I created are not well defined and displayed.
For example, the strings seem to have a shaded effect so that instead of being only black, they have gray shades. It is particularly annoying when I print: the smaller text is not readable. The same effect does not occur in the video display.
I tried modifying the jframe size and scaling the graphic2d instance. Then I returned to normal size and I modified the scale of the paragraph and so I got a result a bit 'better but not satisfactory.
Any suggestions?
Edit: this is the code of the method that manages the creation of the paragraph:
private Paragraph creaParPdf(float scaleFactorW, float scaleFactorH) throws BadElementException, IOException {
BufferedImage img = new BufferedImage(fatt.getWidth(), fatt.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics2D img2D = img.createGraphics();
img2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
fatt.paint(img2D);
Paragraph p = new Paragraph();
com.itextpdf.text.Image itextImg =
com.itextpdf.text.Image.getInstance(img, Color.white, false);
itextImg.scaleAbsolute(PageSize.A4.getWidth()*scaleFactorW, PageSize.A4.getHeight()*scaleFactorH);
p.add(itextImg);
return p;
}
And this is the code of the method that manages the creation of the pdf:
public void printPDF() throws FileNotFoundException, DocumentException, SQLException, ClassNotFoundException, IOException{
fatt = this;
Document d = new Document(PageSize.A4,10,10,10,10);
d.setMargins(10, 10, 0, 10);
String salvPath = "C:\\"+cliente+".pdf";
filePDF = new File(salvPath);
filePDF.getParentFile().mkdirs();
if (filePDF.exists())
out.println("Il file " + salvPath + " esiste");
else try {
if (filePDF.createNewFile())
out.println("Il file " + salvPath + " è stato creato");
else
out.println("Il file " + salvPath + " non può essere creato");
} catch (IOException ex) {
getLogger(JFrameStart.class.getName()).log(SEVERE, null, ex);
}
FileOutputStream fs = new FileOutputStream (salvPath);
PdfWriter writer = PdfWriter.getInstance(d, fs);
writer.setFullCompression();
d.open ();
try {
float scaleFactorW = 0.98f;
float scaleFactorH = 0.98f;
float offset = 0.0f;
p = creaParPdf(scaleFactorW,scaleFactorH);
} catch (BadElementException ex) {
Logger.getLogger(JFrameTracc.class.getName()).log(Level.SEVERE, null, ex);
}
d.add(p);
d.close();
}
This is the final result, by printing the pdf you can see how the smaller parts are practically illegible:
enter image description here
I'm having trouble to set underline and overline by using PdfContentByte in iText. I want to set underline to all field in sectionArea == 1 || section Area == 3 as mentioned in getFontForFormat. So far i can only do bold style and i need it to be underlined and overlined too.
Here is the code:
public void doOutputField(Field field) {
String fieldAsString = field.toString();
BaseFont baseFont = getFontForFormat(field);
float fontSize = 11;
Point bottomLeft = bottomLeftOfField(field, 11, baseFont);
int align;
align = PdfContentByte.ALIGN_LEFT;
//PdfContentByte content
content.beginText();
content.setFontAndSize(baseFont, fontSize);
content.setColorFill(Color.BLACK);
double lineHeight = field.getOutputHeight();
content.showTextAligned(align, fieldAsString, (float) bottomLeft.x,
(float) bottomLeft.y, 0f);
bottomLeft.y -= lineHeight;
content.endText();
}
public BaseFont getFontForFormat(Field field) {
try {
if (field.getSection().getArea().getArea() == 1
|| field.getSection().getArea().getArea() == 3) {
BaseFont bf = BaseFont.createFont(BaseFont.TIMES_BOLD,
BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
return bf;
} else {
BaseFont bf = BaseFont.createFont("Times-Roman",
BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
return bf;
}
} catch (Exception e) {
}
return null;
}
Thanks in advance
Edit (Solved by Bruno Lowagie):
This problem can be solved by utilizing ColumnText.
if (field.getSection().getArea().getArea() == 1
|| field.getSection().getArea().getArea() == 3) {
Chunk chunk = new Chunk(fieldAsString);
chunk.setUnderline(+1f, -2f);
if (field.getSection().getArea().getArea() == 3) {
chunk.setUnderline(+1f, (float) field.getBoundHeight());
}
Font font = new Font();
font.setFamily("Times Roman");
font.setStyle(Font.BOLD);
font.setSize((float) 11);
chunk.setFont(font);
Paragraph p = new Paragraph();
p.add(chunk);
ColumnText ct = new ColumnText(content);
ct.setSimpleColumn(p, (float)bottomLeft.x, (float)bottomLeft.y,
(float)field.getBoundWidth() + (float)bottomLeft.x,
(float)field.getBoundHeight() + (float)bottomLeft.y,
(float)lineHeight, align);
try {
ct.go();
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Thanks
You're making it yourself difficult by using PdfContentByte.showTextAligned(). Is there any reason why you don't want to use ColumnText?
With PdfContentByte, you have to handle the text state —beginText() and endText()—, the font —setFontAndSize()—, and you can only add String values. If you want to add lines (e.g. to underline), you need moveTo(), lineTo(), stroke() operations. These operators need coordinates, so you'll need to measure the size of the line using the BaseFont in combination with the String and the font size. There's some math involved.
If you use ColumnText, you have the option of adding one line at a time using ColumnText.showTextAligned(). Or you can define a column using setSimpleColumn() and let iText take care of distributing the text over different lines. In both cases, you don't have to worry about handling the text state, nor about the font and size. ColumnText accepts Phrase objects, and these objects consists of Chunk objects for which you can define underline and overline values. In this case, iText does all the math for you.