I'm using itextpdf 5.5, can't change it to 7.
I have the problem with background image.
I have a document (text and tables) without stamp and I want to add stamp to it.
This is how I download existing doc.
PdfReader pdfReader = new PdfReader("/doc.pdf");
PdfImportedPage page = writer.getImportedPage(pdfReader, 1);
PdfContentByte pcb = writer.getDirectContent();
pcb.addTemplate(page, 0,0);
And this is how I download stamp image and add it to my doc.
PdfContentByte canvas = writer.getDirectContentUnder();
URL resource = getClass().getResource(getStamp());
Image background = new Jpeg(resource);
background.scaleToFit(463F, 132F);
background.setAbsolutePosition(275F, 100F);
canvas.addImage(background);
But when I download my document - I don't see the stamp. I tried to change getDirectContent() to getDirectContentUnder() when I download my doc but this leads to the opposite situation - my stamp isn't in background.
My first doc is generated this way.
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Document document = new Document(PageSize.A4);
try {
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
document.open();
Paragraph title = new Paragraph(formatUtil.msg("my.header"), fontBold);
title.setAlignment(Element.ALIGN_CENTER);
document.add(title);
Template tmpl = fmConfig.getConfiguration().getTemplate("template.ftl");
Map<String, Object> params = new HashMap<>();
StringWriter writer = new StringWriter();
params.put("param", "param");
tmpl.process(params, writer);
document.add(new Paragraph(writer.toString(), fontCommon));
PdfPTable table = new PdfPTable(2);
document.add(table);
PdfContentByte canvas = writer.getDirectContentUnder();
Image background = new Jpeg(getClass().getResource("background.jpg"));
background.scaleAbsolute(PageSize.A4);
background.setAbsolutePosition(0,0);
canvas.addImage(background);
} finally {
if (document.isOpen()) {
document.close();
}
}
In comments it became clear that the task was to add some content (a bitmap image) to a PDF so that it is over the background (another bitmap image) added to the UnderContent during generation and under the text in the original DirectContent.
iText does not contain high-level code for such content manipulation. While the structure of iText generated PDFs would allow for such code, PDFs generated or manipulated by other PDF libraries may have a different structure; the structure in iText generated PDFs may even be changed during post-processing using other libraries. Thus, it is understandable that no high-level feature for this is provided by iText.
To implement the task nonetheless, therefore, we have to base our code on lower level iText APIs. In this context we can make use of the PdfContentStreamEditor helper class from this answer which already abstracts some details away. (That question originally is about iTextSharp for C# but further down in the answer Java versions of the code also are provided.)
In detail, we extend the PdfContentStreamEditor to remove the former UnderContent (and provide it as list of instructions). Now we can in a first step apply this editor to your file and in a second step add this former UnderContent plus an image over it to the intermediary file. (This could also be done in a single step but that would require a more complex, less maintainable editor class.)
First the new UnderContentRemover content stream editor class:
public class UnderContentRemover extends PdfContentStreamEditor {
/**
* Clears state of {#link UnderContentRemover}, in particular
* the collected content. Use this if you use this instance for
* multiple edit runs.
*/
public void clear() {
afterUnderContent = false;
underContent.clear();
depth = 0;
}
/**
* Retrieves the collected UnderContent instructions
*/
public List<List<PdfObject>> getUnderContent() {
return new ArrayList<List<PdfObject>>(underContent);
}
/**
* Adds the given instructions (which may previously have been
* retrieved using {#link #getUnderContent()}) to the given
* {#link PdfContentByte} instance.
*/
public static void write (PdfContentByte canvas, List<List<PdfObject>> operations) throws IOException {
for (List<PdfObject> operands : operations) {
int index = 0;
for (PdfObject object : operands) {
object.toPdf(canvas.getPdfWriter(), canvas.getInternalBuffer());
canvas.getInternalBuffer().append(operands.size() > ++index ? (byte) ' ' : (byte) '\n');
}
}
}
protected void write(PdfContentStreamProcessor processor, PdfLiteral operator, List<PdfObject> operands) throws IOException {
String operatorString = operator.toString();
if (afterUnderContent) {
super.write(processor, operator, operands);
return;
} else if ("q".equals(operatorString)) {
depth++;
} else if ("Q".equals(operatorString)) {
depth--;
if (depth < 1)
afterUnderContent = true;
} else if (depth == 0) {
afterUnderContent = true;
super.write(processor, operator, operands);
return;
}
underContent.add(new ArrayList<>(operands));
}
boolean afterUnderContent = false;
List<List<PdfObject>> underContent = new ArrayList<>();
int depth = 0;
}
(UnderContentRemover)
As you see, its write method stores the leading instructions forwarded to it in the underContent list until it finds the restore-graphics-state (Q) instruction matching the initial save-graphics-state instruction (q). After that it instead forwards all further instructions to the parent write implementation which writes them to the edited page content.
We can use this for the task at hand as follows:
PdfReader pdfReader = new PdfReader(YOUR_DOCUMENT);
List<List<List<PdfObject>>> underContentByPage = new ArrayList<>();
byte[] sourceWithoutUnderContent = null;
try ( ByteArrayOutputStream outputStream = new ByteArrayOutputStream() ) {
PdfStamper pdfStamper = new PdfStamper(pdfReader, outputStream);
UnderContentRemover underContentRemover = new UnderContentRemover();
for (int i = 1; i <= pdfReader.getNumberOfPages(); i++) {
underContentRemover.clear();
underContentRemover.editPage(pdfStamper, i);
underContentByPage.add(underContentRemover.getUnderContent());
}
pdfStamper.close();
pdfReader.close();
sourceWithoutUnderContent = outputStream.toByteArray();
}
Image background = YOUR_IMAGE_TO_ADD_INBETWEEN;
background.scaleToFit(463F, 132F);
background.setAbsolutePosition(275F, 100F);
pdfReader = new PdfReader(sourceWithoutUnderContent);
byte[] sourceWithStampInbetween = null;
try ( ByteArrayOutputStream outputStream = new ByteArrayOutputStream() ) {
PdfStamper pdfStamper = new PdfStamper(pdfReader, outputStream);
for (int i = 1; i <= pdfReader.getNumberOfPages(); i++) {
PdfContentByte canvas = pdfStamper.getUnderContent(i);
UnderContentRemover.write(canvas, underContentByPage.get(i-1));
canvas.addImage(background);
}
pdfStamper.close();
pdfReader.close();
sourceWithStampInbetween = outputStream.toByteArray();
}
Files.write(new File("PdfLikeVladimirSafonov-WithStampInbetween.pdf").toPath(), sourceWithStampInbetween);
(AddImageInBetween test testForVladimirSafonov)
Related
hey guys sorry for long post and bad language and if there is unnecessary details
i created multiple 1page pdfs from one pdf template using excel document
i have now
something like this
tempfile0.pdf
tempfile1.pdf
tempfile2.pdf
...
im trying to merge all files in one single pdf using itext5
but it semmes that the pages in the resulted pdf are not in the order i wanted
per exemple
tempfile0.pdf in the first page
tempfile1. int the 2000 page
here is the code im using.
the procedure im using is:
1 filling a from from a hashmap
2 saving the filled form as one pdf
3 merging all the files in one single pdf
public void fillPdfitext(int debut,int fin) throws IOException, DocumentException {
for (int i =debut; i < fin; i++) {
HashMap<String, String> currentData = dataextracted[i];
// textArea.appendText("\n"+pdfoutputname +" en cours de preparation\n ");
PdfReader reader = new PdfReader(this.sourcePdfTemplateFile.toURI().getPath());
String outputfolder = this.destinationOutputFolder.toURI().getPath();
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(outputfolder+"\\"+"tempcontrat"+debut+"-" +i+ "_.pdf"));
// get the document catalog
AcroFields acroForm = stamper.getAcroFields();
// as there might not be an AcroForm entry a null check is necessary
if (acroForm != null) {
for (String key : currentData.keySet()) {
try {
String fieldvalue=currentData.get(key);
if (key=="ADRESSE1"){
fieldvalue = currentData.get("ADRESSE1")+" "+currentData.get("ADRESSE2") ;
acroForm.setField("ADRESSE", fieldvalue);
}
if (key == "IMEI"){
acroForm.setField("NUM_SERIE_PACK", fieldvalue);
}
acroForm.setField(key, fieldvalue);
// textArea.appendText(key + ": "+fieldvalue+"\t\t");
} catch (Exception e) {
// e.printStackTrace();
}
}
stamper.setFormFlattening(true);
}
stamper.close();
}
}
this is the code for merging
public void Merge() throws IOException, DocumentException
{
File[] documentPaths = Main.objetapp.destinationOutputFolder.listFiles((dir, name) -> name.matches( "tempcontrat.*\\.pdf" ));
Arrays.sort(documentPaths, NameFileComparator.NAME_INSENSITIVE_COMPARATOR);
byte[] mergedDocument;
try (ByteArrayOutputStream memoryStream = new ByteArrayOutputStream())
{
Document document = new Document();
PdfSmartCopy pdfSmartCopy = new PdfSmartCopy(document, memoryStream);
document.open();
for (File docPath : documentPaths)
{
PdfReader reader = new PdfReader(docPath.toURI().getPath());
try
{
reader.consolidateNamedDestinations();
PdfImportedPage pdfImportedPage = pdfSmartCopy.getImportedPage(reader, 1);
pdfSmartCopy.addPage(pdfImportedPage);
}
finally
{
pdfSmartCopy.freeReader(reader);
reader.close();
}
}
document.close();
mergedDocument = memoryStream.toByteArray();
}
FileOutputStream stream = new FileOutputStream(this.destinationOutputFolder.toURI().getPath()+"\\"+
this.sourceDataFile.getName().replaceFirst("[.][^.]+$", "")+".pdf");
try {
stream.write(mergedDocument);
} finally {
stream.close();
}
documentPaths=null;
Runtime r = Runtime.getRuntime();
r.gc();
}
my question is how to keep the order of the files the same in the resulting pdf
It is because of naming of files. Your code
new FileOutputStream(outputfolder + "\\" + "tempcontrat" + debut + "-" + i + "_.pdf")
will produce:
tempcontrat0-0_.pdf
tempcontrat0-1_.pdf
...
tempcontrat0-10_.pdf
tempcontrat0-11_.pdf
...
tempcontrat0-1000_.pdf
Where tempcontrat0-1000_.pdf will be placed before tempcontrat0-11_.pdf, because you are sorting it alphabetically before merge.
It will be better to left pad file number with 0 character using leftPad() method of org.apache.commons.lang.StringUtils or java.text.DecimalFormat and have it like this tempcontrat0-000000.pdf, tempcontrat0-000001.pdf, ... tempcontrat0-9999999.pdf.
And you can also do it much simpler and skip writing into file and then reading from file steps and merge documents right after the form fill and it will be faster. But it depends how many and how big documents you are merging and how much memory do you have.
So you can save the filled document into ByteArrayOutputStream and after stamper.close() create new PdfReader for bytes from that stream and call pdfSmartCopy.getImportedPage() for that reader. In short cut it can look like:
// initialize
PdfSmartCopy pdfSmartCopy = new PdfSmartCopy(document, memoryStream);
for (int i = debut; i < fin; i++) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
// fill in the form here
stamper.close();
PdfReader reader = new PdfReader(out.toByteArray());
reader.consolidateNamedDestinations();
PdfImportedPage pdfImportedPage = pdfSmartCopy.getImportedPage(reader, 1);
pdfSmartCopy.addPage(pdfImportedPage);
// other actions ...
}
I'm useing iText to fill a template PDF which contains a AcroForm.
Now I want to use this template to create a new PDF with dynamically pages.
My idea is it to fill the template PDF, copy the page with the written fields and add it to a new file. They main Problem is that our customer want to designe the template by them self. So I'm not sure if I try the right way to solve this Problem.
So I've created this code which don't work right now I get the error com.itextpdf.io.IOException: PDF header not found.
My Code
x = 1;
try (PdfDocument finalDoc = new PdfDocument(new PdfWriter("C:\\Users\\...Final.pdf"))) {
for (HashMap<String, String> map : testValues) {
String path1 = "C:\\Users\\.....Temp.pdf"
InputStream template = templateValues.get("Template");
PdfWriter writer = new PdfWriter(path1);
try (PdfDocument pdfDoc = new PdfDocument(new PdfReader(template), writer)) {
PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
for (HashMap.Entry<String, String> map2 : map.entrySet()) {
if (form.getField(map2.getKey()) != null) {
Map<String, PdfFormField> fields = form.getFormFields();
fields.get(map2.getKey()).setValue(map2.getValue());
}
}
} catch (IOException | PdfException ex) {
System.err.println("Ex2: " + ex.getMessage());
}
if (x != 0 && (x % 5) == 0) {
try (PdfDocument tempDoc = new PdfDocument(new PdfReader(path1))) {
PdfPage page = tempDoc.getFirstPage();
finalDoc.addPage(page.copyTo(finalDoc));
} catch (IOException | PdfException ex) {
System.err.println("Ex3: " + ex.getMessage());
}
}
x++;
}
} catch (IOException | PdfException ex) {
System.err.println("Ex: " + ex.getMessage());
}
Part 1 - PDF Header is Missing
this appears to be caused by you attempting to re-read an InputStream w/in a loop that has already been read (and, depending on the configuration of the PdfReader, closed). Solving for this depends on the specific type of InputStream being used - if you want to leave it as a simple InputStream (vs. a more specific yet more capable InputStream type) then you'll need to first slurp up the bytes from the stream into memory (e.g. a ByteArrayOutputStream) then create your PDFReaders based on those bytes.
i.e.
ByteArrayOutputStream templateBuffer = new ByteArrayOutputStream();
while ((int c = template.read()) > 0) templateBuffer.write(c);
for (/* your loop */) {
...
PdfDocument filledInAcroFormTemplate = new PdfDocument(new PdfReader(new ByteArrayInputStream(templateBuffer.toByteArray())), new PdfWriter(tmp))
...
Part 2 - other problems
Couple of things
make sure to grab the recently released 7.0.1 version of iText since it included a couple of fixes wrt/ AcroForm handling
you can probably get away with using ByteArrayOutputStreams for your temporary PDFs (vs. writing them out to files) - i'll use this approach in the example below
PdfDocument/PdfPage is in the "kernel" module, yet AcroForms are in the "form" module (meaning PdfPage is intentionally unaware of AcroForms) - IPdfPageExtraCopier is sortof the bridge between the modules. In order to properly copy AcroForms, you need to use the two-arg copyTo() version, passing an instance of PdfPageFormCopier
field names must be unique in the document (the "absolute" field name that is - i'll skip field hierarcies for now). Since we're looping through and adding the fields from the template multiple times, we need to come up with a strategy to rename the fields to ensure uniqueness (the current API is actually a little bit clunky in this area)
File acroFormTemplate = new File("someTemplate.pdf");
Map<String, String> someMapOfFieldToValues = new HashMap<>();
try (
PdfDocument finalOutput = new PdfDocument(new PdfWriter(new FileOutputStream(new File("finalOutput.pdf")));
) {
for (/* some looping condition */int x = 0; x < 5; x++) {
// for each iteration of the loop, create a temporary in-memory
// PDF to handle form field edits.
ByteArrayOutputStream tmp = new ByteArrayOutputStream();
try (
PdfDocument filledInAcroFormTemplate = new PdfDocument(new PdfReader(new FileInputStream(acroFormTemplate)), new PdfWriter(tmp));
) {
PdfAcroForm acroForm = PdfAcroForm.getAcroForm(filledInAcroFormTemplate, true);
for (PdfFormField field : acroForm.getFormFields().values()) {
if (someMapOfFieldToValues.containsKey(field.getFieldName())) {
field.setValue(someMapOfFieldToValues.get(field.getFieldName()));
}
}
// NOTE that because we're adding the template multiple times
// we need to adopt a field renaming strategy to ensure field
// uniqueness in the final document. For demonstration's sake
// we'll just rename them prefixed w/ our loop counter
List<String> fieldNames = new ArrayList<>();
fieldNames.addAll(acroForm.getFormFields().keySet()); // avoid ConfurrentModification
for (String fieldName : fieldNames) {
acroForm.renameField(fieldName, x+"_"+fieldName);
}
}
// the temp PDF needs to be "closed" for all the PDF finalization
// magic to happen...so open up new read-only version to act as
// the source for the merging from our in-memory bucket-o-bytes
try (
PdfDocument readOnlyFilledInAcroFormTemplate = new PdfDocument(new PdfReader(new ByteArrayInputStream(tmp.toByteArray())));
) {
// although PdfPage.copyTo will probably work for simple pages, PdfDocument.copyPagesTo
// is a more comprehensive copy (wider support for copying Outlines and Tagged content)
// so it's more suitable for general page-copy use. Also, since we're copying AcroForm
// content, we need to use the PdfPageFormCopier
readOnlyFilledInAcroFormTemplate.copyPagesTo(1, 1, finalOutput, new PdfPageFormCopier());
}
}
}
Close your PdfDocuments when you are done with adding content to them.
I use iText for generating PDF, from a XML file, with content in HTML. Everything is working, except one little thing.
When I have a bloc of text containing a part in BOLD, the BOLD doesn't appear in the resulting PDF file. If I have a complete phrase in BOLD, it's working fine.
Examples :
<DIV><FONT face='Arial' size='10'><B>The BOLD for this phrase works</B></FONT></DIV>
<DIV><FONT face='Arial' size='10'>The BOLD for <B>this part of the phrase </B> doesn't work</FONT></DIV>
With 'Italic' or 'Underline', I can do the same test but I don't have the problem. It's working...
A little precision : if I use a tag <B> combined with a tag <U> or <I>, for a part of bloc of text, it's working too.
Example :
<DIV><FONT face='Arial' size='10'>The combination of <B><I>BOLD and something else (U or I)</I></B> works fine.</FONT></DIV>
For the context : WebApp with struts, the PDF is not saved as a file but sent to the navigator as a response. As suggested by an answer, I update my version of iText from 1.4.8 to 5.5.7.
For the HTML code saved in a xml file, you can see examples above.
For the Java code (I picked up the code from severals long methods. I hope I forgot nothing...).
ByteArrayOutputStream baoutLettre = new ByteArrayOutputStream();
Document document = new Document();
PdfWriter myWriter = PdfWriter.getInstance(document, baoutLettre);
handleHeaderFooter(request, response, document, Constantes.Type_LETTRE);
document.open();
String lettreContent = FileHelper.readFile("myLetter.xml");
XmlParser.parse(document, new ByteArrayInputStream(lettreContent.getBytes("UTF-8")), getTagMap());
document.close();
ByteArrayOutputStream outTmp = new ByteArrayOutputStream(64000);
PdfCopyMerge pdfCM = new PdfCopyMerge(outTmp);
pdfCM.addDocument(baoutLettre.toByteArray());
pdfCM.close();
ByteArrayOutputStream outPDF = addPageNumber(outTmp.toByteArray(), soc, dicoEdition, request);
outPDF.writeTo(request.getOutputStream());
And for the class PdfCopyMerge :
public class PdfCopyMerge {
private ByteArrayOutputStream outStream = new ByteArrayOutputStream();
private Document document = null;
private PdfCopy writer = null;
public PdfCopyMerge(ByteArrayOutputStream stream) {
super();
outStream = stream;
}
public int addDocument(byte[] pdfByteArray) {
int numberOfPages = 0;
try {
PdfReader reader = new PdfReader(pdfByteArray);
numberOfPages = reader.getNumberOfPages();
if (this.document == null) {
this.document = new Document(reader.getPageSizeWithRotation(1));
this.writer = new PdfCopy(this.document, this.getOutputStream());
this.document.open();
}
PdfImportedPage page;
for (int i = 0; i < numberOfPages;) {
++i;
page = this.writer.getImportedPage(reader, i);
this.writer.addPage(page);
}
PRAcroForm form = reader.getAcroForm();
if (form != null) {
this.writer.copyAcroForm(reader);
}
} catch (Exception e) {
logger.error(e.getMessage(),e);
}
return numberOfPages;
}
Does anybody face the same problem ? I look for any helping ideas ...
Thanks.
Try the lastest version 5.5.7. Everything works fine.
https://github.com/itext/itextpdf/tags
I'd like to have a program that removes all rectangles from a PDF file. One use case for this is to unblacken a given PDF file to see if there is any hidden information behind the rectangles. The rest of the PDF file should be kept as-is.
Which PDF library is suitable to this task? In Java, I would like the code to look like this:
PdfDocument doc = PdfDocument.load(new File("original.pdf"));
PdfDocument unblackened = doc.transform(new CopyingPdfVisitor() {
public void visitRectangle(PdfRect rect) {
if (rect.getFillColor().getBrightness() >= 0.1) {
super.visitRectangle(rect);
}
}
});
unblackened.save(new File("unblackened.pdf"));
The CopyingPdfVisitor would copy a PDF document exactly as-is, and my custom code would leave out all the dark rectangles.
Itext pdf library have ways to modify pdf content.
The *ITEXT CONTENTPARSER Example * may give you any idea. "qname" parameter (qualified name) may be used to detected rectangle element.
http://itextpdf.com/book/chapter.php?id=15
Other option, if you want obtain the text on the document use the PdfReaderContentParser to extract text content
public void parsePdf(String pdf, String txt) throws IOException {
PdfReader reader = new PdfReader(pdf);
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
PrintWriter out = new PrintWriter(new FileOutputStream(txt));
TextExtractionStrategy strategy;
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
strategy = parser.processContent(i, new SimpleTextExtractionStrategy());
out.println(strategy.getResultantText());
}
out.flush();
out.close();
reader.close();
}
example at http://itextpdf.com/examples/iia.php?id=277
Is there a way I can edit a PDF from Java?
I have a PDF document which contains placeholders for text that I need to be replaced using Java, but all the libraries that I saw created PDF from scratch and small editing functionality.
Is there anyway I can edit a PDF or is this impossible?
You can do it with iText. I tested it with following code. It adds a chunk of text and a red circle over each page of an existing PDF.
/* requires itextpdf-5.1.2.jar or similar */
import java.io.*;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.*;
public class AddContentToPDF {
public static void main(String[] args) throws IOException, DocumentException {
/* example inspired from "iText in action" (2006), chapter 2 */
PdfReader reader = new PdfReader("C:/temp/Bubi.pdf"); // input PDF
PdfStamper stamper = new PdfStamper(reader,
new FileOutputStream("C:/temp/Bubi_modified.pdf")); // output PDF
BaseFont bf = BaseFont.createFont(
BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED); // set font
//loop on pages (1-based)
for (int i=1; i<=reader.getNumberOfPages(); i++){
// get object for writing over the existing content;
// you can also use getUnderContent for writing in the bottom layer
PdfContentByte over = stamper.getOverContent(i);
// write text
over.beginText();
over.setFontAndSize(bf, 10); // set font and size
over.setTextMatrix(107, 740); // set x,y position (0,0 is at the bottom left)
over.showText("I can write at page " + i); // set text
over.endText();
// draw a red circle
over.setRGBColorStroke(0xFF, 0x00, 0x00);
over.setLineWidth(5f);
over.ellipse(250, 450, 350, 550);
over.stroke();
}
stamper.close();
}
}
I modified the code found a bit and it was working as follows
public class Principal {
public static final String SRC = "C:/tmp/244558.pdf";
public static final String DEST = "C:/tmp/244558-2.pdf";
public static void main(String[] args) throws IOException, DocumentException {
File file = new File(DEST);
file.getParentFile().mkdirs();
new Principal().manipulatePdf(SRC, DEST);
}
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfDictionary dict = reader.getPageN(1);
PdfObject object = dict.getDirectObject(PdfName.CONTENTS);
PdfArray refs = null;
if (dict.get(PdfName.CONTENTS).isArray()) {
refs = dict.getAsArray(PdfName.CONTENTS);
} else if (dict.get(PdfName.CONTENTS).isIndirect()) {
refs = new PdfArray(dict.get(PdfName.CONTENTS));
}
for (int i = 0; i < refs.getArrayList().size(); i++) {
PRStream stream = (PRStream) refs.getDirectObject(i);
byte[] data = PdfReader.getStreamBytes(stream);
stream.setData(new String(data).replace("NULA", "Nulo").getBytes());
}
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.close();
reader.close();
}
}
Take a look at iText and this sample code
Take a look at aspose and this sample code
I've done this using LibreOffice Draw.
You start by manually opening a pdf in Draw, checking that it renders OK, and saving it as a Draw .odg file.
That's a zipped xml file, so you can modify it in code to find and replace the placeholders.
Next (from code) you use a command line call to Draw to generate the pdf.
Success!
The main issue is that Draw doesn't handle fonts embedded in a pdf. If the font isn't also installed on your system - then it will render oddly, as Draw will replace it with a standard one that inevitably has different sizing.
If this approach is of interest, I'll put together some shareable code.