I can set diferent setFixedLeading on specific page.
First i add All paragraphs in list then with help recursive i calculate e height of each paragraph and compare with document height, then i add it to div.
But it dont work.
Maybe there is another way?
public static void main(String[] args) throws FileNotFoundException {
PdfDocument pdf = new PdfDocument(new PdfWriter(DEST));
Document document = new Document(pdf);
pdf.setDefaultPageSize(PageSize.A5);
float bottomMarin = 0;
document.setMargins(0, 25, bottomMarin, 25);
Div div = new Div();
div.setPadding(0);
div.setMargin(0);
maxHeight = document.getPdfDocument().getDefaultPageSize().getHeight()- bottomMarin ;/* (mainPdf_model.getLeftMargin() +mainPdf_model.getRightMargin());*/
String line = "Hello! Welcome to iTextPdf";
List<Paragraph> paragraphs = new ArrayList<>();
for (int i = 0; i < 130; i++) {
Paragraph element = new Paragraph();
element.add(line + " " + i);
element.setMargin(0);
element.setPadding(0);
element.setFixedLeading(100);
paragraphs.add(element);
}
getListParagraphOnPage(paragraphs, document, 1);
Map<Integer, List<Paragraph>> listParagraphOnPage = paragraph;
listParagraphOnPage.forEach((k,v)->{
if(k ==1){
for(Paragraph paragraph : v){
paragraph.setFixedLeading(120);
paragraph.setBackgroundColor(red);
div.add(paragraph);
}
}
if(k ==2){
for(Paragraph paragraph : v){
paragraph.setFixedLeading(150);
paragraph.setBackgroundColor(yellow);
div.add(paragraph);
}
}
if(k ==3){
for(Paragraph paragraph : v){
paragraph.setFixedLeading(30);
paragraph.setBackgroundColor(green);
div.add(paragraph);
}
}
});
Map<Integer, List<Paragraph>> paragraph = new HashMap<>();
document.add(div);
document.close();
}
static Map<Integer,List<Paragraph>> paragraph = new HashMap<>();
//This is a method that is created in the map, in which paragraphs are placed with a certain Leading
public static Map<Integer,List<Paragraph>> getListParagraphOnPage(List<Paragraph> paragraphs, Document document,int page){
float height = 0;
List<Paragraph> ps = new ArrayList<>();
for(Paragraph par: paragraphs){
if(page==1){
par.setFixedLeading(120);
}else if(page==2){
par.setFixedLeading(150);
}else if(page==3){
par.setFixedLeading(30);
}
IRenderer rendererSubTree = par.createRendererSubTree();
LayoutResult result = rendererSubTree.setParent(document.getRenderer()).
layout(new LayoutContext(new LayoutArea(1, new Rectangle(10000, 1000))));
height+=result.getOccupiedArea().getBBox().getHeight();
if(height<maxHeight){
ps.add(par);
}else {
var pageCurrent = page;
paragraph.put(page,ps);
int size = paragraph.get(pageCurrent).size();
page++;
return getListParagraphOnPage(paragraphs.subList(size,paragraphs.size()),document,page);
}
}
return paragraph;
}
I'm trying to add an empty signature field to an existing digitally signed pdf (certify signature).
I have a workflow where many users will sign the document (approval signature), the document is created with "n" empty signature fields, one for each user, our application first apply a invisible certify signature, then each user can sign the document in respective field, but due to changes unexpected in the workflow, other users might want to sign, so we want to add the respective empty signature field and then apply the signature.
I tried to add the empty field (a table with a cell event) to the certified document but when I want to add it and associate the field, it breaks the signature, I cannot make it work correctly.
Here is the methods used to sign,add signature field, and set the signature field options.
I don't know what I'm doing wrong.
public static String sign(SignRequest signRequest, File certificate, File unsignedDocument, File image, File icon)
throws FileNotFoundException, IOException, DocumentException, StreamParsingException, OCSPException,
OperatorException, URISyntaxException, WriterException, GeneralSecurityException, FontFormatException {
SignatureType sigType = Optional
.ofNullable(SignatureType.get(signRequest.getSignatureProperties().getSignatureType()))
.orElse(SignatureType.APPROVAL_SIGNATURE);
File signedDocument = File.createTempFile("signed",".pdf");
char[] pass = signRequest.getKeyStore().getPassword().toCharArray();
// Load certificate chain
BouncyCastleProvider provider = new BouncyCastleProvider();
Security.addProvider(provider);
KeyStore ks = KeyStore.getInstance("PKCS12", provider.getName());
ks.load(new FileInputStream(certificate.getAbsolutePath()), pass);
String alias = getAliasFromKeyStore(ks);
PrivateKey pk = (PrivateKey) ks.getKey(alias, pass);
Certificate[] chain = ks.getCertificateChain(alias);
// Creating the reader and the stamper
PdfReader reader = new PdfReader(FileUtils.openInputStream(unsignedDocument));
FileOutputStream os = new FileOutputStream(signedDocument);
PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0', null, true);
PdfSignatureAppearance appearance = null;
// Certify o approval signature (approval is the default signature type)
switch (sigType) {
case CERTIFY_SIGNATURE:
if (reader.getAcroFields().getSignatureNames().size() <= 0) {
appearance = setSignatureFieldOptions(stamper.getSignatureAppearance(), reader, chain,
signRequest, image, icon, Boolean.TRUE);
} else {
appearance = setSignatureFieldOptions(stamper.getSignatureAppearance(), reader, chain,
signRequest, image, icon, Boolean.FALSE);
}
break;
case APPROVAL_SIGNATURE:
default:
appearance = setSignatureFieldOptions(stamper.getSignatureAppearance(), reader, chain, signRequest,
image, icon, Boolean.FALSE);
break;
}
// Adding LTV (optional)
OcspClient ocspClient = null;
List<CrlClient> crlList = null;
if (signRequest.getSignatureProperties().getLtv() == Boolean.TRUE) {
ocspClient = new OcspClientBouncyCastle(new OCSPVerifier(null, null));
CrlClient crlClient = new CrlClientOnline(chain);
crlList = new ArrayList<CrlClient>();
crlList.add(crlClient);
}
// Adding timestamp (optional)
TSAClient tsaClient = null;
if (signRequest.getTimestamp() != null
&& StringUtils.isNotBlank(signRequest.getTimestamp().getUrl())) {
tsaClient = new TSAClientBouncyCastle(signRequest.getTimestamp().getUrl(),
signRequest.getTimestamp().getUser(), signRequest.getTimestamp().getPassword());
}
// Creating the signature
ExternalSignature pks = new PrivateKeySignature(pk, signtRequest.getSignatureProperties().getAlgorithm(),
provider.getName());
ExternalDigest digest = new BouncyCastleDigest();
MakeSignature
.signDetached(appearance, digest, pks, chain, crlList, ocspClient, tsaClient,
calculateEstimatedSize(chain, ocspClient, tsaClient, crlList, getEstimatedSizeBonus()), CryptoStandard.CMS);
return signedDocument.getAbsolutePath();
}
private static PdfSignatureAppearance setSignatureFieldOptions(PdfSignatureAppearance appearance, PdfReader reader, Certificate[] chain, SignRequest signRequest, File image, File icon, Boolean certifySignature) throws MalformedURLException, IOException, DocumentException {
SignatureProperties sigProperties = signRequest.getSignatureProperties();
SignatureField sigField = sigProperties.getSignatureField();
// Creating the appearance
appearance.setSignatureCreator(Constant.SIGNATURE_CREATOR);
Optional.ofNullable(sigProperties.getReason()).ifPresent(appearance::setReason);
Optional.ofNullable(sigProperties.getLocation()).ifPresent(appearance::setLocation);
if (certifySignature) {
appearance.setCertificationLevel(PdfSignatureAppearance.CERTIFIED_FORM_FILLING_AND_ANNOTATIONS);
} else {
appearance.setCertificationLevel(PdfSignatureAppearance.NOT_CERTIFIED);
}
/**
* Signature Field Name
*/
BoundingBox box = sigProperties.getSignatureField().getBoundingBox();
String fieldName = sigField.getName();
int pageNumber = sigProperties.getSignatureField().getPage();
if (!sigField.isVisible()) {
if (StringUtils.isBlank(sigField.getName())) {
fieldName = generateFieldName();
appearance.setVisibleSignature(new Rectangle(0, 0, 0, 0), pageNumber, fieldName);
} else {
appearance.setVisibleSignature(new Rectangle(0, 0, 0, 0), pageNumber, fieldName);
}
} else {
Font font = FontFactory.getFont(Optional.ofNullable(sigField.getFontName()).orElse(BaseFont.HELVETICA),
Optional.ofNullable(sigField.getFontSize()).orElse(6));
Rectangle rect = null;
FieldPosition fieldPosition = null;
//ADD EMPTY FIELD
if (StringUtils.isBlank(sigField.getName()) && box != null) {
fieldName = generateFieldName();
rect = new Rectangle(box.getLowerLeftX(), box.getLowerLeftY(), box.getLowerLeftX() + box.getWidth(),
box.getLowerLeftY() + box.getHeight());
appearance.setVisibleSignature(rect, pageNumber, fieldName);
////////////////////////////////// TRY TO ADD EXTRA SIGNATURE FIELD///////////////////////////////////////////
Rectangle documentRectangle = reader.getPageSize(pageNumber);
PdfStamper stamper = appearance.getStamper();
float pageMargin = 10;
float tableMargin = 15;
int numberOfFields = 1; // 1 sigField
float headerWidth = (documentRectangle.getWidth() - (pageMargin * 2));
// Table with signature field
PdfPTable table = new PdfPTable(1);
table.setTotalWidth(headerWidth - (tableMargin * 4));
table.setLockedWidth(Boolean.TRUE);
table.setWidthPercentage(100);
float posXTable = (pageMargin + (headerWidth - table.getTotalWidth()) / 2);
float posYTable = 400; // custom y position
int height = 70; // custom height
for (int i = 0; i < numberOfFields; i++) {
String sigFieldName = String.format(Constant.SIGNATURE_FIELD_PREFIX + "%s", (i + 1));
table.addCell(createSignatureFieldCell(stamper, sigFieldName, height, pageNumber));
}
table.writeSelectedRows(0, -1, posXTable, posYTable, stamper.getOverContent(pageNumber));
////////////////////////////////// END TRY TO ADD EXTRA SIGNATURE FIELD///////////////////////////////////////////
} else {
//APPLY SIGNATURE TO EXISTING EMPTY FIELD
List<FieldPosition> acroFields = reader.getAcroFields().getFieldPositions(sigField.getName());
fieldPosition = acroFields.get(0);
appearance.setVisibleSignature(fieldName);
}
// --------------------------- Custom signature appearance ---------------------
PdfTemplate t = appearance.getLayer(2);
Rectangle sigRect = null;
if (fieldPosition != null) {
sigRect = fieldPosition.position;
} else {
sigRect = new Rectangle(box.getLowerLeftX(), box.getLowerLeftY(), box.getLowerLeftX() + box.getWidth(),
box.getLowerLeftY() + box.getHeight());
}
// Left rectangle
Rectangle leftRect = new Rectangle(0, 0, (sigRect.getWidth() / 5), (sigRect.getHeight() / 2));
ColumnText ct1 = new ColumnText(t);
ct1.setSimpleColumn(leftRect);
Image im1 = Image.getInstance(icon.getAbsolutePath());
float ratio1 = leftRect.getHeight() / im1.getHeight();
im1.scaleToFit(im1.getWidth() * ratio1, im1.getHeight() * ratio1);
Paragraph p = createParagraph("Digital sign", font, Constant.PARAGRAPH_LEADING, Constant.MARGIN * 9);
ct1.addElement(new Chunk(im1, Constant.MARGIN * 10, 0));
ct1.addElement(p);
ct1.go();
// Middle rectangle
Rectangle middleRect = new Rectangle((sigRect.getWidth() / 5), 0,
(leftRect.getWidth() + sigRect.getWidth() / 5), (sigRect.getHeight() / 2));
ColumnText ct2 = new ColumnText(t);
ct2.setSimpleColumn(middleRect);
if (visibleSignatureImage != null) {
Image im2 = Image.getInstance(image.getAbsolutePath());
float ratio2 = sigRect.getHeight() / im2.getHeight();
im2.scaleToFit(im2.getWidth() * ratio2, im2.getHeight() * ratio2);
ct2.addElement(new Chunk(im2, 0, 0));
ct2.go();
}
// TextFields
List<TextField> textFields = fillSignatureFieldText(chain, sigProperties, font);
// Right rectangle - Names
Rectangle rightRectNames = new Rectangle(
(Constant.MARGIN * 5 + leftRect.getWidth() + middleRect.getWidth()), 0,
(leftRect.getWidth() + middleRect.getWidth() + sigRect.getWidth() / 4),
sigRect.getHeight() - Constant.MARGIN);
ColumnText ct31 = new ColumnText(t);
ct31.setSimpleColumn(rightRectNames);
List<Paragraph> paragraphsNames = textFields.stream()
.map(e -> createParagraph(e.getName(), font, Constant.PARAGRAPH_LEADING, 0))
.collect(Collectors.toList());
paragraphsNames.forEach(ct31::addElement);
ct31.go();
// Right rectangle - Values
Rectangle rightRectValues = new Rectangle(
(Constant.MARGIN * 4 + leftRect.getWidth() + middleRect.getWidth() + rightRectNames.getWidth()), 0,
sigRect.getWidth(), (sigRect.getHeight() - Constant.MARGIN));
ColumnText ct32 = new ColumnText(t);
ct32.setSimpleColumn(rightRectValues);
List<Paragraph> paragraphsValues = textFields.stream()
.map(e -> createParagraph(e.getValue(), font, Constant.PARAGRAPH_LEADING, 0))
.collect(Collectors.toList());
paragraphsValues.forEach(ct32::addElement);
ct32.go();
// --------------------------- Custom signature appearance ---------------------
}
return appearance;
}
//this is used to first create the empty fields
protected static PdfPCell createSignatureFieldCell(PdfWriter writer, String name, int height) {
PdfPCell cell = new PdfPCell();
cell.setMinimumHeight(height);
cell.setBackgroundColor(BaseColor.WHITE);
PdfFormField field = PdfFormField.createSignature(writer);
field.setFieldName(name);
field.setFlags(PdfAnnotation.FLAGS_PRINT);
cell.setCellEvent(new MySignatureFieldEvent(field, null, 0));
return cell;
}
//this is used to try to add the extra empty field to signed document
protected static PdfPCell createSignatureFieldCell(PdfStamper stamper, String name, int height, int pageNumber) {
PdfPCell cell = new PdfPCell();
cell.setMinimumHeight(height);
cell.setBackgroundColor(BaseColor.WHITE);
PdfFormField field = PdfFormField.createSignature(stamper.getWriter());
field.setFieldName(name);
field.setFlags(PdfAnnotation.FLAGS_PRINT);
cell.setCellEvent(new MySignatureFieldEvent(field, stamper, pageNumber));
return cell;
}
public static class MySignatureFieldEvent implements PdfPCellEvent {
public PdfFormField field;
public PdfStamper stamper;
public int pageField;
public MySignatureFieldEvent(PdfFormField field, PdfStamper stamper, int pageField) {
this.field = field;
this.stamper = stamper;
this.pageField = pageField;
}
public void cellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases) {
PdfWriter writer = canvases[0].getPdfWriter();
field.setPage();
field.setWidget(position, PdfAnnotation.HIGHLIGHT_INVERT);
if (stamper == null) {
writer.addAnnotation(field);
}else {
stamper.addAnnotation(field, pageField);
}
}
}
First of all, originally Adobe interpreted the certification levels as described in this answer. In particular, if a document has a certification signature, then adding new fields is forbidden, even signature fields.
This strictness appears to have been lost along the way but may again be applied any time after updates, in particular after the recently published certification attacks on pdf-insecurity.org exploited this relaxed option.
That been said, though, what you never are allowed to do is changing the static page content! In your code, though, you add the additional signature fields by adding a table (with events) to the document. This table will change the static page content.
Thus, try to only add a new signature field.
i have a problem. I create application JavaFX management for data base. I decided to convert data from tableview to pdf. For this I used a pdfbox. I can't change font in my converter pdf. How can do it? Because helvetica does not support the UTF-8 (ą, ę, ź ...).
enter code here public enum Orientation{
PORTRAIT, LANDSCAPE
};
public boolean doPrintToPdf(List<List> list, File saveLoc,Orientation orientation) {
try {
if (saveLoc == null) {
return false;
}
if (!saveLoc.getName().endsWith(".pdf")) {
saveLoc = new File(saveLoc.getAbsoluteFile() + ".pdf");
}
//Inicjalizacja dokumentu
PDDocument doc = new PDDocument();
PDPage page = new PDPage();
if (orientation == Orientation.LANDSCAPE) {
page.setMediaBox(new PDRectangle(PDRectangle.A4.getHeight(), PDRectangle.A4.getWidth()));
} else {
page.setMediaBox(new PDRectangle(PDRectangle.A4.getWidth(), PDRectangle.A4.getHeight()));
}
doc.addPage(page);
float margin = 10;
float tableWidth = page.getMediaBox().getWidth() - (2 * margin);
float yStartNewPage = page.getMediaBox().getHeight() - (2 * margin);
float yStart = yStartNewPage;
float bottomMargin = 0;
BaseTable dataTable = new BaseTable(yStart, yStartNewPage, bottomMargin, tableWidth, margin, doc, page, true,
true);
DataTable t = new DataTable(dataTable, page);
t.addListToTable(list, DataTable.HASHEADER);
dataTable.draw();
doc.save(saveLoc);
doc.close();
return true;
} catch (IOException e) {
DialogsUtils.errorDialog(e.getMessage());
}
return false;
Assuming I have a PDF document with a text field with some font and size defined, is there a way to determine if some text will fit inside the field rectangle using PDFBox?
I'm trying to avoid cases where text is not fully displayed inside the field, so in case the text overflows given the font and size, I would like to change the font size to Auto (0).
This code recreates the appearance stream to be sure that it exists so that there is a bbox (which can be a little bit smaller than the rectangle).
public static void main(String[] args) throws IOException
{
// file can be found at https://issues.apache.org/jira/browse/PDFBOX-142
// https://issues.apache.org/jira/secure/attachment/12742551/Testformular1.pdf
try (PDDocument doc = PDDocument.load(new File("Testformular1.pdf")))
{
PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
PDTextField field = (PDTextField) acroForm.getField("Name");
PDAnnotationWidget widget = field.getWidgets().get(0);
// force generation of appearance stream
field.setValue(field.getValue());
PDRectangle rectangle = widget.getRectangle();
PDAppearanceEntry ap = widget.getAppearance().getNormalAppearance();
PDAppearanceStream appearanceStream = ap.getAppearanceStream();
PDRectangle bbox = appearanceStream.getBBox();
float fieldWidth = Math.min(bbox.getWidth(), rectangle.getWidth());
String defaultAppearance = field.getDefaultAppearance();
System.out.println(defaultAppearance);
// Pattern must be improved, font may have numbers
// /Helv 12 Tf 0 g
final Pattern p = Pattern.compile("\\/([A-z]+) (\\d+).+");
Matcher m = p.matcher(defaultAppearance);
if (!m.find() || m.groupCount() != 2)
{
System.out.println("oh-oh");
System.exit(-1);
}
String fontName = m.group(1);
int fontSize = Integer.parseInt(m.group(2));
PDResources resources = appearanceStream.getResources();
if (resources == null)
{
resources = acroForm.getDefaultResources();
}
PDFont font = resources.getFont(COSName.getPDFName(fontName));
float stringWidth = font.getStringWidth("Tilman Hausherr Tilman Hausherr");
System.out.println("stringWidth: " + stringWidth * fontSize / 1000);
System.out.println("field width: " + fieldWidth);
}
}
The output is:
/Helv 12 Tf 0 g
stringWidth: 180.7207
field width: 169.29082
I am in requirement of creating an index for PDF document & based on the index i want to display the page.
problem is page number are created dynamically so static page number cannot be used, can anyone suggest me links or examples
here is my code and it works for me to redirect into different pages while clicking in the page number on index page
public class LinkPdf {
static final float INCH = 72;
public static List<PDAnnotation> annotations;
#SuppressWarnings("null")
public static void main(String[] args) throws Exception {
PDRectangle position = null;
PDDocument document = new PDDocument();
try {
for (int i = 0; i < 10; i++) {
PDPage page = new PDPage();
document.addPage(page);
}
PDPage page1 = document.getPage(0);
HashMap<Integer, String> pgs = new HashMap<Integer, String>();
for (int i = 0; i < document.getNumberOfPages(); i++) {
pgs.put(i, "Jump to Page" + i);
}
PDFont font = PDType1Font.HELVETICA_BOLD;
PDPageContentStream contents = new PDPageContentStream(document,
page1);
contents.beginText();
contents.setFont(font, 18);
contents.newLineAtOffset(50, 600);
contents.setLeading(20f);
contents.showText("PDFBox");
position = new PDRectangle();
for (int i = 0; i < document.getNumberOfPages(); i++) {
contents.newLine();
contents.showText(pgs.get(i));
contents.newLine();
}
contents.endText();
contents.close();
PDRectangle[] pos1 = new PDRectangle[document.getNumberOfPages()];
for(int i=0;i<document.getNumberOfPages();i++){
pos1[i]=new PDRectangle();
pos1[i].setLowerLeftX(50);
pos1[i].setLowerLeftY(575-(i*40));
pos1[i].setUpperRightX(INCH + 100);
pos1[i].setUpperRightY(585-(i*40));
getPage(document, page1, pgs.get(i), i, pos1[i]);
}
document.save("D:/link.pdf");
System.out.println("Completed");
} finally {
document.close();
}
}
public static void getPage(PDDocument document, PDPage page1, String txt,
int pageno, PDRectangle position) throws IOException {
annotations = page1.getAnnotations();
PDBorderStyleDictionary borderThick = new PDBorderStyleDictionary();
borderThick.setWidth(INCH / 12); // 12th inch
PDBorderStyleDictionary borderThin = new PDBorderStyleDictionary();
borderThin.setWidth(INCH / 72); // 1 point
PDBorderStyleDictionary borderULine = new PDBorderStyleDictionary();
borderULine.setStyle(PDBorderStyleDictionary.STYLE_UNDERLINE);
borderULine.setWidth(INCH / 72); // 1 point
float pw = page1.getMediaBox().getUpperRightX();
float ph = page1.getMediaBox().getUpperRightY();
float textWidth = PDType1Font.TIMES_BOLD.getStringWidth("PDFBox") / 1000 * 18;
PDAnnotationLink pageLink = new PDAnnotationLink();
pageLink.setBorderStyle(borderULine);
textWidth = PDType1Font.TIMES_BOLD.getStringWidth(txt) / 1000 * 18;
pageLink.setRectangle(position);
PDActionGoTo actionGoto = new PDActionGoTo();
PDPageDestination dest = new PDPageFitWidthDestination();
dest.setPage(document.getPage(pageno));
actionGoto.setDestination(dest);
pageLink.setAction(actionGoto);
annotations.add(pageLink);
}
}