Creating pages dynamically in PDFbox - Java for Android - java

I coded an app that stores texts in Room db and the user can create a pdf file contains selected texts retrieved from the db. I'm using PDFbox for creating the pdf files but PDFbox doesn't create new pages automatically as needed. I want the app to create pages in a given pdf file dynamically.
Here is my code:
public void writeDataIntoFile() {
if (adapter.getItemCount()> 0) {
String filePath = createFile();
Log.d("saving pdf", filePath);
PDFBoxResourceLoader.init(getApplicationContext());
PDDocument document = new PDDocument();
PDPage page = new PDPage(PDRectangle.A4);
PDFont font = PDType1Font.TIMES_ROMAN;
document.addPage(page);
PDPageContentStream contentStream;
try {
// Define a content stream for adding to the PDF
contentStream = new PDPageContentStream(document, page);
contentStream.beginText();
contentStream.setStrokingColor(44, 61, 79);
contentStream.setFont(font, 18);
contentStream.setLeading(14.5f);
ExecutorService executor = Executors.newSingleThreadExecutor();
Handler handler = new Handler(Looper.getMainLooper());
showProgressBar();
executor.execute(() -> {
try { //Background work here - function goes here
//TODO I need to add functions to return words by category
contentStream.newLineAtOffset(25, 770);
contentStream.showText("Category: " + categoryName);
contentStream.newLine();
contentStream.newLine();
contentStream.setNonStrokingColor(15, 38, 192);
contentStream.setFont(font, 12);
for (Word word : adapter.getCurrentList()) {
showMultiLineText(word.getWord() + ": "
+ word.getTranslation(),
550,
contentStream,font,
12);
contentStream.newLine();
count++;
}
contentStream.endText();
contentStream.close();
document.save(filePath);
saved = true;
document.close();
} catch (IOException e) {
Log.d("error", String.valueOf(e));
}
handler.post(() -> {
//UI Thread work here - progress bar goes here
hideProgressBar();
openingPDF(filePath);
Toast.makeText(this, "the file has been saved in /Download/MemorizeWords", Toast.LENGTH_SHORT).show();
});
});
executor.shutdown();
} catch (IOException e) {
Log.d("error", String.valueOf(e));
}
}
else {
Toast.makeText(this, "This category has no words!", Toast.LENGTH_SHORT).show();
}
}
Here is a function checks whether text longer than page width and splits texts if needed:
private void showMultiLineText(String text, int allowedWidth, PDPageContentStream contentStream,
PDFont font,
int fontSize) throws IOException {
List<String> lines = new ArrayList<String>();
String line = "";
// split the text on spaces
String[] words = text.split(" ");
for(String word : words) {
if(!line.isEmpty()) {
line += " ";
}
// check if adding the word to the line surpasses the width of the page
int size = (int) (fontSize * font.getStringWidth(line + word) / 1000);
if(size > allowedWidth) {
// if line + word surpasses the width of the page, add the line without the current word
lines.add(line);
// start new line with the current word
line = word;
} else {
// if line + word fits the page width, add the current word to the line
line += word;
}
}
lines.add(line);
for(String ln : lines) {
count = lines.size();
contentStream.showText(ln);
contentStream.newLine();
}
}```

Related

Issues with enclosing PDDoucment

I have implemented a program that will print data into a pdf however I am facing this issue
java.io.IOException: COSStream has been closed and cannot be read. Perhaps its enclosing PDDocument has been closed?
I know there have been similar issues posted but I could not find the solution to my problem from them. What I am doing is that I initialized two documents one of them being the main document(doc) the other for the rest of the pages and then I add to the main one(activeDocument), the service calls three different functions to add a new page one for the first page one for any pages in between, one for before the last and finally the last page.a Here is my code most of the logic can be ignored its mainly just the things relating to PDDocuments since thats where the issue lies
private InputStream generateCardsPDF(String cardNumber,
String generationDate,
StatementSummaryResTypeStatementSummaryResBody resBody,
String pdf) throws IOException, ParseException {
List<StatementSummaryResTypeStatementSummaryResBodyTransactionsTransaction> transactionList = resBody.getTransactions().getTransaction().stream().limit(50).collect(Collectors.toList());
PDDocument doc = new PDDocument().load(getClass().getClassLoader().getResourceAsStream(pdf));
PDDocumentCatalog docCatalog = doc.getDocumentCatalog();
PDPage page = docCatalog.getPages().get(0);
try {
String fullName = resBody.getStatementHeader().getAddress().getTitle() +
" " + resBody.getStatementHeader().getAddress().getFirstName() + " ";
String fullAddress = "";
if (resBody.getStatementHeader().getAddress().getMiddleName() != null) {
fullName = fullName + resBody.getStatementHeader().getAddress().getMiddleName() + " ";
}
fullName = fullName + resBody.getStatementHeader().getAddress().getLastName();
String countryAddress = resBody.getStatementHeader().getAddress().getCity() != null ? resBody.getStatementHeader().getAddress().getCity() : "";
countryAddress += resBody.getStatementHeader().getAddress().getCountry() != null &&
countryAddress.length() > 0 ? ", " + resBody.getStatementHeader().getAddress().getCountry() : "";
countryAddress += resBody.getStatementHeader().getAddress().getCountry() != null &&
countryAddress.length() == 0 ? resBody.getStatementHeader().getAddress().getCountry() : "";
fullAddress += resBody.getStatementHeader().getAddress().getAddress1() + ONE_LINE;
fullAddress += resBody.getStatementHeader().getAddress().getAddress2() != null ? resBody.getStatementHeader().getAddress().getAddress2() + ONE_LINE : "";
fullAddress += resBody.getStatementHeader().getAddress().getAddress3() != null ? resBody.getStatementHeader().getAddress().getAddress3() + ONE_LINE : "";
fullAddress += countryAddress;
String header = fullName + ONE_LINE + fullAddress;
Integer currentIndex = 0;
Integer count = 0;
Integer cutOff = 0;
Integer pageNumber = 1;
Integer numberOfPages = statementsUtils.calculateNumberOfPages(transactionList, currentIndex, cutOff, count);
String pageOf;
PDDocument activeDocument = new PDDocument();
statementsUtils.setFirstPage(transactionList, resBody, doc, header, maskedCard(cardNumber), generationDate, numberOfPages);
try {
while (currentIndex < transactionList.size()) {
Boolean fourOrFive = false;
Boolean checkpoint = false;
Boolean finalPrint = false;
if (count == 4) {
checkpoint = statementsUtils.reachedCheckpoint(currentIndex, transactionList);
if (checkpoint) {
cutOff = currentIndex;
fourOrFive = true;
pageNumber++;
pageOf = "Page " + pageNumber.toString() + " of " + numberOfPages.toString();
statementsUtils.addExtraPage(doc, activeDocument, cutOff, fourOrFive, transactionList, resBody, pageOf, pdf, header, maskedCard(cardNumber), generationDate);
count = 0;
}
} else if (count == 5) {
cutOff = currentIndex;
fourOrFive = false;
pageNumber++;
pageOf = "Page " + pageNumber.toString() + " of " + numberOfPages.toString();
statementsUtils.addExtraPage(doc, activeDocument, cutOff, fourOrFive, transactionList, resBody, pageOf, pdf, header, maskedCard(cardNumber), generationDate);
count = 0;
} else if (transactionList.size() <= 2) {
finalPrint = true;
} else if (transactionList.size() - currentIndex + 1 <= 5) { // count <4? print last page or count >=4 print 4 and check homuch left
finalPrint = true;
pageNumber++;
pageOf = "Page " + pageNumber.toString() + " of " + numberOfPages.toString();
if (transactionList.size() - currentIndex + 1 == 5) {
statementsUtils.setPreLastPage(doc, activeDocument, currentIndex, transactionList, resBody, pageOf, pdf, header, maskedCard(cardNumber), generationDate);
pageNumber++;
pageOf = "Page " + pageNumber.toString() + " of " + numberOfPages.toString();
statementsUtils.setLastPage(doc, activeDocument, transactionList.size() - 1, transactionList, resBody, pageOf, pdf, header, maskedCard(cardNumber), generationDate);
} else {
statementsUtils.setLastPage(doc, activeDocument, currentIndex - count + 1, transactionList, resBody, pageOf, pdf, header, maskedCard(cardNumber), generationDate);
}
}
if (fourOrFive && !finalPrint)
currentIndex = cutOff + 1;
else if (finalPrint)
currentIndex = transactionList.size();
else
currentIndex++;
if (currentIndex >= 2) count++;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (activeDocument != null) {
activeDocument.close();
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
PDPageContentStream contentStream = new PDPageContentStream(doc, page, PDPageContentStream.AppendMode.APPEND, true);
contentStream.close();
ByteArrayOutputStream out = new ByteArrayOutputStream();
if (doc != null) {
doc.save(out);
doc.close();
}
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
return in;
}
}
public void setFirstPage( PDDocument doc,....){
PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
...
acroForm.flatten();
}
public void setPreLastPage( PDDocument doc, PDDocument activeDocument....){
activeDocument = new PDDocument().load(getClass().getClassLoader().getResourceAsStream(pdf));
PDDocumentCatalog docCatalog = activeDocument.getDocumentCatalog();
PDPage p1 = docCatalog.getPages().get(0);
PDAcroForm acroForm = docCatalog.getAcroForm();
...
acroForm.flatten();
PDPageContentStream cs = new PDPageContentStream(activeDocument, p1,PDPageContentStream.AppendMode.APPEND, true);
cs.close();
ByteArrayOutputStream out = new ByteArrayOutputStream();
activeDocument.save(out);
doc.addPage(p1);
}
public void setLastPage( PDDocument doc, PDDocument activeDocument....){
activeDocument = new PDDocument().load(getClass().getClassLoader().getResourceAsStream(pdf));
PDDocumentCatalog docCatalog = activeDocument.getDocumentCatalog();
PDPage p1 = docCatalog.getPages().get(0);
PDAcroForm acroForm = docCatalog.getAcroForm();
...
acroForm.flatten();
PDPageContentStream cs = new PDPageContentStream(activeDocument, p1,PDPageContentStream.AppendMode.APPEND, true);
cs.close();
ByteArrayOutputStream out = new ByteArrayOutputStream();
activeDocument.save(out);
doc.addPage(p1);
}
public void addExtraPage( PDDocument doc, PDDocument activeDocument....){
activeDocument = new PDDocument().load(getClass().getClassLoader().getResourceAsStream(pdf));
PDDocumentCatalog docCatalog = activeDocument.getDocumentCatalog();
PDPage p1 = docCatalog.getPages().get(0);
PDAcroForm acroForm = docCatalog.getAcroForm();
...
acroForm.flatten();
PDPageContentStream cs = new PDPageContentStream(activeDocument, p1,PDPageContentStream.AppendMode.APPEND, true);
cs.close();
ByteArrayOutputStream out = new ByteArrayOutputStream();
activeDocument.save(out);
doc.addPage(p1);
}
There is a lot more logic in these functions but I tried adding only what I thought is relevant.
I believe the issue is concerning either the content stream of one of the pdfs or the order in which I close, save etc.... but I couldn't figure out what the exact issue is so any advice would be appreciated.
Three errors are in setPreLastPage, setLastPage, and addExtraPage: in each of these methods you load a new PDDocument in the respective local variable activeDocument, take a page from it and add it to the same PDDocument doc, and then drop all references to that PDDocument in activeDocument when leaving the respective method.
Dropping all references allows the garbage collection to pick these PDDocument instances up and close and remove them. This pulls away the data underneath the pages copied into the PDDocument doc, resulting in the error you observe when trying to save doc.
To prevent this either clone the page objects into doc before adding them or keep all these temporary documents open until you save doc.
In case anyone is curious of the actual implementation of the solution I created an empty list.
List<PDDocument> activeDocuments = new ArrayList<PDDocument>();
And then everytime I called one of the methods I returned the document and stored it inside of the list and then closed them all at the end after I saved the original document.
while (currentIndex < transactionList.size()) {
Boolean fourOrFive = false;
Boolean checkpoint = false;
Boolean finalPrint = false;
if (count == 4) {
checkpoint = statementsUtils.reachedCheckpoint(currentIndex, transactionList);
if (checkpoint) {
cutOff = currentIndex;
fourOrFive = true;
pageNumber++;
pageOf = "Page " + pageNumber.toString() + " of " + numberOfPages.toString();
activeDocuments.add(statementsUtils.addExtraPage(doc, activeDocument, cutOff, fourOrFive, transactionList, resBody, pageOf, pdf, header, maskedCard(cardNumber), generationDate));
count = 0;
}
} else if (count == 5) {
cutOff = currentIndex;
fourOrFive = false;
pageNumber++;
pageOf = "Page " + pageNumber.toString() + " of " + numberOfPages.toString();
activeDocuments.add(statementsUtils.addExtraPage(doc, activeDocument, cutOff, fourOrFive, transactionList, resBody, pageOf, pdf, header, maskedCard(cardNumber), generationDate));
count = 0;
} else if (transactionList.size() <= 2) {
finalPrint = true;
} else if (transactionList.size() - currentIndex + 1 <= 5) { // count <4? print last page or count >=4 print 4 and check homuch left
finalPrint = true;
pageNumber++;
pageOf = "Page " + pageNumber.toString() + " of " + numberOfPages.toString();
if (transactionList.size() - currentIndex + 1 == 5) {
activeDocuments.add(statementsUtils.setPreLastPage(doc, activeDocument, currentIndex, transactionList, resBody, pageOf, pdf, header, maskedCard(cardNumber), generationDate));
pageNumber++;
pageOf = "Page " + pageNumber.toString() + " of " + numberOfPages.toString();
activeDocuments.add(statementsUtils.setLastPage(doc, activeDocument, transactionList.size() - 1, transactionList, resBody, pageOf, pdf, header, maskedCard(cardNumber), generationDate));
} else {
activeDocuments.add(statementsUtils.setLastPage(doc, activeDocument, currentIndex - count + 1, transactionList, resBody, pageOf, pdf, header, maskedCard(cardNumber), generationDate));
}
}
if (fourOrFive && !finalPrint)
currentIndex = cutOff + 1;
else if (finalPrint)
currentIndex = transactionList.size();
else
currentIndex++;
if (currentIndex >= 2) count++;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
PDPageContentStream contentStream = new PDPageContentStream(doc, page, PDPageContentStream.AppendMode.APPEND, true);
contentStream.close();
ByteArrayOutputStream out = new ByteArrayOutputStream();
if (doc != null) {
doc.save(out);
doc.close();
}
for (PDDocument a : activeDocuments)
a.close();
if (activeDocument != null) {
activeDocument.close();
}
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
return in;
}

PDFBox Error After Deleting Image - An error exists on this page. Acrobat may not display the page correctly

I am using the pdfbox library 2.0 version. I have to remove the selected image from the page and add another image. It works properly. But when I open that file it shows a warning message: An error exists on this page. Acrobat may not display the page correctly. and the screenshot is as below:
Herewith sharing the code to delete an image and add other image:(EDITTED with Fix)
public static void removeImages(String pdfFile) throws Exception {
PDDocument document = PDDocument.load(new File(pdfFile));
for (PDPage page : document.getPages()) {
PDResources pdResources = page.getResources();
String[] qrCodeCosName = new String[1];
pdResources.getXObjectNames().forEach(propertyName -> {
if (!pdResources.isImageXObject(propertyName)) {
return;
}
PDXObject o;
try {
o = pdResources.getXObject(propertyName);
if (o instanceof PDImageXObject) {
PDImageXObject pdImageXObject = (PDImageXObject) o;
if (pdImageXObject.getMetadata() != null) {
// TO REMOVE FROM RESOURCE
((COSDictionary) pdResources.getCOSObject().getDictionaryObject(COSName.XOBJECT))
.removeItem(propertyName);
qrCodeCosName[0] = propertyName.getName();
}
}
} catch (IOException e) {
e.printStackTrace();
}
});
PDFStreamParser parser = new PDFStreamParser(page);
parser.parse();
List<Object> tokens = parser.getTokens();
System.out.println("Original tokens size" + tokens.size());
List<Object> newTokens = new ArrayList<Object>();
for (int j = 0; j < tokens.size(); j++) {
Object token = tokens.get(j);
if (token instanceof Operator) {
Operator op = (Operator) token;
// find image - remove it
if (op.getName().equals("Do")) {
COSName cosName = (COSName) tokens.get(j - 1);
if (cosName.getName().equals(qrCodeCosName[0])) {
newTokens.remove(newTokens.size() - 1);
continue;
}
}
newTokens.add(token);
}
System.out.println("tokens size" + newTokens.size());
PDStream newContents = new PDStream(document);
OutputStream out = newContents.createOutputStream();
ContentStreamWriter writer = new ContentStreamWriter(out);
writer.writeTokens(newTokens);
out.close();
page.setContents(newContents);
// ADD OTHER IMAGE
PDImageXObject pdImage = PDImageXObject.createFromFile("D:\\copy\\ind.png", document);
PDPageContentStream contents = new PDPageContentStream(document, page,
PDPageContentStream.AppendMode.PREPEND, true, true);
contents.saveGraphicsState();
// Drawing the image in the PDF document
contents.drawImage(pdImage, 0, 0, 50, 30);
contents.restoreGraphicsState();
System.out.println("Image inserted Successfully.");
// Closing the PDPageContentStream object
contents.close();
}
document.save("RemoveImage.pdf");
document.close();
}
}
Kindly help me with this.
Also, looking forward for other code review comments about required changes to make this operation properly. :)
As per #Tilman Hausherr suggestion below code works for me:
public static void removeImages(String pdfFile) throws Exception {
PDDocument document = PDDocument.load(new File(pdfFile));
for (PDPage page : document.getPages()) {
PDResources pdResources = page.getResources();
String[] qrCodeCosName = new String[1];
pdResources.getXObjectNames().forEach(propertyName -> {
if (!pdResources.isImageXObject(propertyName)) {
return;
}
PDXObject o;
try {
o = pdResources.getXObject(propertyName);
if (o instanceof PDImageXObject) {
PDImageXObject pdImageXObject = (PDImageXObject) o;
if (pdImageXObject.getMetadata() != null) {
// TO REMOVE FROM RESOURCE
((COSDictionary) pdResources.getCOSObject().getDictionaryObject(COSName.XOBJECT))
.removeItem(propertyName);
qrCodeCosName[0] = propertyName.getName();
}
}
} catch (IOException e) {
e.printStackTrace();
}
});
PDFStreamParser parser = new PDFStreamParser(page);
parser.parse();
List<Object> tokens = parser.getTokens();
System.out.println("Original tokens size" + tokens.size());
List<Object> newTokens = new ArrayList<Object>();
for (int j = 0; j < tokens.size(); j++) {
Object token = tokens.get(j);
if (token instanceof Operator) {
Operator op = (Operator) token;
// find image - remove it
if (op.getName().equals("Do")) {
COSName cosName = (COSName) tokens.get(j - 1);
if (cosName.getName().equals(qrCodeCosName[0])) {
newTokens.remove(newTokens.size() - 1);
continue;
}
}
newTokens.add(token);
}
System.out.println("tokens size" + newTokens.size());
PDStream newContents = new PDStream(document);
OutputStream out = newContents.createOutputStream();
ContentStreamWriter writer = new ContentStreamWriter(out);
writer.writeTokens(newTokens);
out.close();
page.setContents(newContents);
// ADD OTHER IMAGE
PDImageXObject pdImage = PDImageXObject.createFromFile("D:\\copy\\ind.png", document);
PDPageContentStream contents = new PDPageContentStream(document, page,
PDPageContentStream.AppendMode.PREPEND, true, true);
contents.saveGraphicsState();
// Drawing the image in the PDF document
contents.drawImage(pdImage, 0, 0, 50, 30);
contents.restoreGraphicsState();
System.out.println("Image inserted Successfully.");
// Closing the PDPageContentStream object
contents.close();
}
document.save("RemoveImage.pdf");
document.close();
}
}

How to merge PDF documents with correct orientation? [duplicate]

How to merge multiple pdf files (generated on run time) through ItextSharp then printing them.
I found the following link but that method requires the pdf names considering that the pdf files stored and this is not my case .
I have multiple reports i'll convert them to pdf files through this method :
private void AddReportToResponse(LocalReport followsReport)
{
string mimeType;
string encoding;
string extension;
string[] streams = new string[100];
Warning[] warnings = new Warning[100];
byte[] pdfStream = followsReport.Render("PDF", "", out mimeType, out encoding, out extension, out streams, out warnings);
//Response.Clear();
//Response.ContentType = mimeType;
//Response.AddHeader("content-disposition", "attachment; filename=Application." + extension);
//Response.BinaryWrite(pdfStream);
//Response.End();
}
Now i want to merge all those generated files (Bytes) in one pdf file to print it
If you want to merge source documents using iText(Sharp), there are two basic situations:
You really want to merge the documents, acquiring the pages in their original format, transfering as much of their content and their interactive annotations as possible. In this case you should use a solution based on a member of the Pdf*Copy* family of classes.
You actually want to integrate pages from the source documents into a new document but want the new document to govern the general format and don't care for the interactive features (annotations...) in the original documents (or even want to get rid of them). In this case you should use a solution based on the PdfWriter class.
You can find details in chapter 6 (especially section 6.4) of iText in Action — 2nd Edition. The Java sample code can be accessed here and the C#'ified versions here.
A simple sample using PdfCopy is Concatenate.java / Concatenate.cs. The central piece of code is:
byte[] mergedPdf = null;
using (MemoryStream ms = new MemoryStream())
{
using (Document document = new Document())
{
using (PdfCopy copy = new PdfCopy(document, ms))
{
document.Open();
for (int i = 0; i < pdf.Count; ++i)
{
PdfReader reader = new PdfReader(pdf[i]);
// loop over the pages in that document
int n = reader.NumberOfPages;
for (int page = 0; page < n; )
{
copy.AddPage(copy.GetImportedPage(reader, ++page));
}
}
}
}
mergedPdf = ms.ToArray();
}
Here pdf can either be defined as a List<byte[]> immediately containing the source documents (appropriate for your use case of merging intermediate in-memory documents) or as a List<String> containing the names of source document files (appropriate if you merge documents from disk).
An overview at the end of the referenced chapter summarizes the usage of the classes mentioned:
PdfCopy: Copies pages from one or more existing PDF documents. Major downsides: PdfCopy doesn’t detect redundant content, and it fails when concatenating forms.
PdfCopyFields: Puts the fields of the different forms into one form. Can be used to avoid the problems encountered with form fields when concatenating forms using PdfCopy. Memory use can be an issue.
PdfSmartCopy: Copies pages from one or more existing PDF documents. PdfSmartCopy is able to detect redundant content, but it needs more memory and CPU than PdfCopy.
PdfWriter: Generates PDF documents from scratch. Can import pages from other PDF documents. The major downside is that all interactive features of the imported page (annotations, bookmarks, fields, and so forth) are lost in the process.
I used iTextsharp with c# to combine pdf files. This is the code I used.
string[] lstFiles=new string[3];
lstFiles[0]=#"C:/pdf/1.pdf";
lstFiles[1]=#"C:/pdf/2.pdf";
lstFiles[2]=#"C:/pdf/3.pdf";
PdfReader reader = null;
Document sourceDocument = null;
PdfCopy pdfCopyProvider = null;
PdfImportedPage importedPage;
string outputPdfPath=#"C:/pdf/new.pdf";
sourceDocument = new Document();
pdfCopyProvider = new PdfCopy(sourceDocument, new System.IO.FileStream(outputPdfPath, System.IO.FileMode.Create));
//Open the output file
sourceDocument.Open();
try
{
//Loop through the files list
for (int f = 0; f < lstFiles.Length-1; f++)
{
int pages =get_pageCcount(lstFiles[f]);
reader = new PdfReader(lstFiles[f]);
//Add pages of current file
for (int i = 1; i <= pages; i++)
{
importedPage = pdfCopyProvider.GetImportedPage(reader, i);
pdfCopyProvider.AddPage(importedPage);
}
reader.Close();
}
//At the end save the output file
sourceDocument.Close();
}
catch (Exception ex)
{
throw ex;
}
private int get_pageCcount(string file)
{
using (StreamReader sr = new StreamReader(File.OpenRead(file)))
{
Regex regex = new Regex(#"/Type\s*/Page[^s]");
MatchCollection matches = regex.Matches(sr.ReadToEnd());
return matches.Count;
}
}
Here is some code I pulled out of an old project I had. It was a web application but I was using iTextSharp to merge pdf files then print them.
public static class PdfMerger
{
/// <summary>
/// Merge pdf files.
/// </summary>
/// <param name="sourceFiles">PDF files being merged.</param>
/// <returns></returns>
public static byte[] MergeFiles(List<Stream> sourceFiles)
{
Document document = new Document();
MemoryStream output = new MemoryStream();
try
{
// Initialize pdf writer
PdfWriter writer = PdfWriter.GetInstance(document, output);
writer.PageEvent = new PdfPageEvents();
// Open document to write
document.Open();
PdfContentByte content = writer.DirectContent;
// Iterate through all pdf documents
for (int fileCounter = 0; fileCounter < sourceFiles.Count; fileCounter++)
{
// Create pdf reader
PdfReader reader = new PdfReader(sourceFiles[fileCounter]);
int numberOfPages = reader.NumberOfPages;
// Iterate through all pages
for (int currentPageIndex = 1; currentPageIndex <=
numberOfPages; currentPageIndex++)
{
// Determine page size for the current page
document.SetPageSize(
reader.GetPageSizeWithRotation(currentPageIndex));
// Create page
document.NewPage();
PdfImportedPage importedPage =
writer.GetImportedPage(reader, currentPageIndex);
// Determine page orientation
int pageOrientation = reader.GetPageRotation(currentPageIndex);
if ((pageOrientation == 90) || (pageOrientation == 270))
{
content.AddTemplate(importedPage, 0, -1f, 1f, 0, 0,
reader.GetPageSizeWithRotation(currentPageIndex).Height);
}
else
{
content.AddTemplate(importedPage, 1f, 0, 0, 1f, 0, 0);
}
}
}
}
catch (Exception exception)
{
throw new Exception("There has an unexpected exception" +
" occured during the pdf merging process.", exception);
}
finally
{
document.Close();
}
return output.GetBuffer();
}
}
/// <summary>
/// Implements custom page events.
/// </summary>
internal class PdfPageEvents : IPdfPageEvent
{
#region members
private BaseFont _baseFont = null;
private PdfContentByte _content;
#endregion
#region IPdfPageEvent Members
public void OnOpenDocument(PdfWriter writer, Document document)
{
_baseFont = BaseFont.CreateFont(BaseFont.HELVETICA,
BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
_content = writer.DirectContent;
}
public void OnStartPage(PdfWriter writer, Document document)
{ }
public void OnEndPage(PdfWriter writer, Document document)
{ }
public void OnCloseDocument(PdfWriter writer, Document document)
{ }
public void OnParagraph(PdfWriter writer,
Document document, float paragraphPosition)
{ }
public void OnParagraphEnd(PdfWriter writer,
Document document, float paragraphPosition)
{ }
public void OnChapter(PdfWriter writer, Document document,
float paragraphPosition, Paragraph title)
{ }
public void OnChapterEnd(PdfWriter writer,
Document document, float paragraphPosition)
{ }
public void OnSection(PdfWriter writer, Document document,
float paragraphPosition, int depth, Paragraph title)
{ }
public void OnSectionEnd(PdfWriter writer,
Document document, float paragraphPosition)
{ }
public void OnGenericTag(PdfWriter writer, Document document,
Rectangle rect, string text)
{ }
#endregion
private float GetCenterTextPosition(string text, PdfWriter writer)
{
return writer.PageSize.Width / 2 - _baseFont.GetWidthPoint(text, 8) / 2;
}
}
I didn't write this, but made some modifications. I can't remember where I found it. After I merged the PDFs I would call this method to insert javascript to open the print dialog when the PDF is opened. If you change bSilent to true then it should print silently to their default printer.
public Stream addPrintJStoPDF(Stream thePDF)
{
MemoryStream outPutStream = null;
PRStream finalStream = null;
PdfDictionary page = null;
string content = null;
//Open the stream with iTextSharp
var reader = new PdfReader(thePDF);
outPutStream = new MemoryStream(finalStream.GetBytes());
var stamper = new PdfStamper(reader, (MemoryStream)outPutStream);
var jsText = "var res = app.setTimeOut('this.print({bUI: true, bSilent: false, bShrinkToFit: false});', 200);";
//Add the javascript to the PDF
stamper.JavaScript = jsText;
stamper.FormFlattening = true;
stamper.Writer.CloseStream = false;
stamper.Close();
//Set the stream to the beginning
outPutStream.Position = 0;
return outPutStream;
}
Not sure how well the above code is written since I pulled it from somewhere else and I haven't worked in depth at all with iTextSharp but I do know that it did work at merging PDFs that I was generating at runtime.
Tested with iTextSharp-LGPL 4.1.6:
public static byte[] ConcatenatePdfs(IEnumerable<byte[]> documents)
{
using (var ms = new MemoryStream())
{
var outputDocument = new Document();
var writer = new PdfCopy(outputDocument, ms);
outputDocument.Open();
foreach (var doc in documents)
{
var reader = new PdfReader(doc);
for (var i = 1; i <= reader.NumberOfPages; i++)
{
writer.AddPage(writer.GetImportedPage(reader, i));
}
writer.FreeReader(reader);
reader.Close();
}
writer.Close();
outputDocument.Close();
var allPagesContent = ms.GetBuffer();
ms.Flush();
return allPagesContent;
}
}
To avoid the memory issues mentioned, I used file stream instead of memory stream(mentioned in ITextSharp Out of memory exception merging multiple pdf) to merge pdf files:
var parentDirectory = Directory.GetParent(SelectedDocuments[0].FilePath);
var savePath = parentDirectory + "\\MergedDocument.pdf";
using (var fs = new FileStream(savePath, FileMode.Create))
{
using (var document = new Document())
{
using (var pdfCopy = new PdfCopy(document, fs))
{
document.Open();
for (var i = 0; i < SelectedDocuments.Count; i++)
{
using (var pdfReader = new PdfReader(SelectedDocuments[i].FilePath))
{
for (var page = 0; page < pdfReader.NumberOfPages;)
{
pdfCopy.AddPage(pdfCopy.GetImportedPage(pdfReader, ++page));
}
}
}
}
}
}
****/*For Multiple PDF Print..!!*/****
<button type="button" id="btnPrintMultiplePdf" runat="server" class="btn btn-primary btn-border btn-sm"
onserverclick="btnPrintMultiplePdf_click">
<i class="fa fa-file-pdf-o"></i>Print Multiple pdf</button>
protected void btnPrintMultiplePdf_click(object sender, EventArgs e)
{
if (ValidateForMultiplePDF() == true)
{
#region Declare Temp Variables..!!
CheckBox chkList = new CheckBox();
HiddenField HidNo = new HiddenField();
string Multi_fofile, Multi_listfile;
Multi_fofile = Multi_listfile = "";
Multi_fofile = Server.MapPath("PDFRNew");
#endregion
for (int i = 0; i < grdRnew.Rows.Count; i++)
{
#region Find Grd Controls..!!
CheckBox Chk_One = (CheckBox)grdRnew.Rows[i].FindControl("chkOne");
Label lbl_Year = (Label)grdRnew.Rows[i].FindControl("lblYear");
Label lbl_No = (Label)grdRnew.Rows[i].FindControl("lblCode");
#endregion
if (Chk_One.Checked == true)
{
HidNo .Value = llbl_No .Text.Trim()+ lbl_Year .Text;
if (File.Exists(Multi_fofile + "\\" + HidNo.Value.ToString() + ".pdf"))
{
#region Get Multiple Files Name And Paths..!!
if (Multi_listfile != "")
{
Multi_listfile = Multi_listfile + ",";
}
Multi_listfile = Multi_listfile + Multi_fofile + "\\" + HidNo.Value.ToString() + ".pdf";
#endregion
}
}
}
#region For Generate Multiple Pdf..!!
if (Multi_listfile != "")
{
String[] Multifiles = Multi_listfile.Split(',');
string DestinationFile = Server.MapPath("PDFRNew") + "\\Multiple.Pdf";
MergeFiles(DestinationFile, Multifiles);
Response.ContentType = "pdf";
Response.AddHeader("Content-Disposition", "attachment;filename=\"" + DestinationFile + "\"");
Response.TransmitFile(DestinationFile);
Response.End();
}
else
{
}
#endregion
}
}
private void MergeFiles(string DestinationFile, string[] SourceFiles)
{
try
{
int f = 0;
/**we create a reader for a certain Document**/
PdfReader reader = new PdfReader(SourceFiles[f]);
/**we retrieve the total number of pages**/
int n = reader.NumberOfPages;
/**Console.WriteLine("There are " + n + " pages in the original file.")**/
/**Step 1: creation of a document-object**/
Document document = new Document(reader.GetPageSizeWithRotation(1));
/**Step 2: we create a writer that listens to the Document**/
PdfWriter writer = PdfWriter.GetInstance(document, new FileStream(DestinationFile, FileMode.Create));
/**Step 3: we open the Document**/
document.Open();
PdfContentByte cb = writer.DirectContent;
PdfImportedPage page;
int rotation;
/**Step 4: We Add Content**/
while (f < SourceFiles.Length)
{
int i = 0;
while (i < n)
{
i++;
document.SetPageSize(reader.GetPageSizeWithRotation(i));
document.NewPage();
page = writer.GetImportedPage(reader, i);
rotation = reader.GetPageRotation(i);
if (rotation == 90 || rotation == 270)
{
cb.AddTemplate(page, 0, -1f, 1f, 0, 0, reader.GetPageSizeWithRotation(i).Height);
}
else
{
cb.AddTemplate(page, 1f, 0, 0, 1f, 0, 0);
}
/**Console.WriteLine("Processed page " + i)**/
}
f++;
if (f < SourceFiles.Length)
{
reader = new PdfReader(SourceFiles[f]);
/**we retrieve the total number of pages**/
n = reader.NumberOfPages;
/**Console.WriteLine("There are"+n+"pages in the original file.")**/
}
}
/**Step 5: we Close the Document**/
document.Close();
}
catch (Exception e)
{
string strOb = e.Message;
}
}
private bool ValidateForMultiplePDF()
{
bool chkList = false;
foreach (GridViewRow gvr in grdRnew.Rows)
{
CheckBox Chk_One = (CheckBox)gvr.FindControl("ChkSelectOne");
if (Chk_One.Checked == true)
{
chkList = true;
}
}
if (chkList == false)
{
divStatusMsg.Style.Add("display", "");
divStatusMsg.Attributes.Add("class", "alert alert-danger alert-dismissable");
divStatusMsg.InnerText = "ERROR !!...Please Check At Least On CheckBox.";
grdRnew.Focus();
set_timeout();
return false;
}
return true;
}

XWPFDocument replace paragraphe in a loop

I have a table that contains items. I want to set the names of items in the word document but each one in a new line.
So I created the void below:
When my text contain "P01" I replace the text by the name, add a new line and set another text "P01".
public void findAndRemplaceString(XWPFDocument doc, String champs) throws IOException {
for (XWPFParagraph p : doc.getParagraphs()) {
java.util.List<XWPFRun> runs = p.getRuns();
if (runs != null) {
for (XWPFRun r : runs) {
String text = r.getText(0);
if (text != null && text.contains("P01")) {
text = text.replace("P01", champs);
System.out.println("text replaced");
r.setText(text, 0);
//add new line
r.addBreak();
//new "P01" added
r.setText("P01");
}
}
}
}
}
So that the next name of item will be replaced in the paragraph below.
#FXML
void endButton(ActionEvent event) {
String file = "model";
for (Person item : table.getItems()) {
//get the name of item
String a = item.getName();
// get the index of item
int ind0 = table.getItems().indexOf(item);
int ind1 = table.getItems().indexOf(item) + 1;
try {
XWPFDocument doc = new XWPFDocument(new FileInputStream(new File(file + ind0 + ".docx")));
findAndRemplaceString(doc, a);
FileOutputStream fileOutputStream = new FileOutputStream(new File(file + ind1 + ".docx"));
doc.write(fileOutputStream);
fileOutputStream.close();
doc.close();
} catch (Exception e) {
System.out.println("erreur " + e);
}
}
}
The problem is:
It replace only the first name of item and not the others. It doesn't read the new "P01" that I set.
I found the answer, it's not the best but it works.
I changed the type of String[] instead of String, so that i can do it this way :
public void findAndRemplaceString(XWPFDocument doc,String champs[]){
for (XWPFParagraph p : doc.getParagraphs()) {
java.util.List<XWPFRun> runs = p.getRuns();
if (runs != null) {
for (XWPFRun r : runs) {
String text = r.getText(0);
if (text != null && text.contains("P01") ) {
for (int i=0;i<champs.length;i++){
text = text.replace("P01","");
r.setText(text,0); //Replace the old text
r.setText(champs[i]);//add the new text
r.addBreak(); //new line
}
}
}
}
}
}
And when I click the button, the void findAndReplaceString is called only once instead of looping, so I put all the item names in a list like that:
#FXML void endButton(ActionEvent event) {
List<String> list = new ArrayList<String>();
for (Person item : table.getItems()) {
String a = item.getName();
list.add(a);
}
String[] simpleArray = new String[list.size()];
list.toArray(simpleArray);
try{
XWPFDocument doc = new XWPFDocument(new FileInputStream(new File("input.docx")));
findAndRemplaceString(doc,simpleArray);
FileOutputStream fileOutputStream = new FileOutputStream(new File("output.docx"));
doc.write(fileOutputStream);
fileOutputStream.close();
doc.close();
}catch (Exception e) {
System.out.println("erreur " + e);
}
}

Strange combobox behavior in PDFBox

I have this code which creates a combobox in a PDF file. There are two problems with it.
Special characters (like ö) are displayed properly when the combobox is opened but then are not displayable when the combobox is closed.
When I open the PDF in Acrobat, change the value and save the PDF, the combobox is somehow gone. When I open the PDF again it is not displayed anymore.
Did I mess up something with the PDFBox classes or what may be the problem?
Here is a picture in the opened state:
and here is one in the closed state:
public class ComboTest {
public static void main(String[] args) {
PDFont font = PDType1Font.HELVETICA;
Color color = Color.BLACK;
float fontSize = 12;
PDDocument document = new PDDocument();
PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);
PDAcroForm acroForm = new PDAcroForm(document);
PDComboBox comboBox = new PDComboBox(acroForm);
comboBox.setPartialName("test");
String defaultAppearanceString = "/" + font.getName() + " " + fontSize + " Tf "
+ 0 + " " + 0 + " " + 0 + " rg";
comboBox.setDefaultAppearance(defaultAppearanceString);
PDAnnotationWidget widget = new PDAnnotationWidget();
widget.setRectangle(new PDRectangle(200, 200, 100, 20));
widget.setAnnotationFlags(4);
widget.setPage(page);
widget.setParent(comboBox);
List<String> exportValues = new ArrayList<>();
List<String> displayValues = new ArrayList<>();
displayValues.add("öne");
displayValues.add("two");
displayValues.add("thrée");
exportValues.add("1");
exportValues.add("2");
exportValues.add("3");
comboBox.setOptions(exportValues, displayValues);
List<PDAnnotationWidget> widgets = new ArrayList<>();
widgets.add(widget);
try {
page.getAnnotations().add(widget);
} catch (IOException e) {
e.printStackTrace();
}
comboBox.setWidgets(widgets);
try {
FileOutputStream output = new FileOutputStream("test.pdf");
document.save(output);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Near the end of your code, add this:
acroForm.getFields().add(comboBox);
document.getDocumentCatalog().setAcroForm(acroForm);
this makes sure that your acroform and its field is known to the PDF.
Re the special character, replace the name of the Helvetica font with "Helv", which is a standard name for Adobe.
Better, cleaner solution: set up the default resources.
PDResources dr = new PDResources();
dr.put(COSName.getPDFName("Helv"), font);
acroForm.setDefaultResources(dr);
Instead of "Helv" you can also use COSName.getPDFName(font.getName()), but it has to be the same in your default appearance string.
So the full code is now:
public class ComboTest
{
public static void main(String[] args)
{
PDFont font = PDType1Font.HELVETICA;
Color color = Color.BLACK;
float fontSize = 12;
PDDocument document = new PDDocument();
PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);
PDAcroForm acroForm = new PDAcroForm(document);
PDComboBox comboBox = new PDComboBox(acroForm);
comboBox.setPartialName("test");
// Helv instead of Helvetica
String defaultAppearanceString = "/Helv " + fontSize + " Tf "
+ 0 + " " + 0 + " " + 0 + " rg";
comboBox.setDefaultAppearance(defaultAppearanceString);
PDAnnotationWidget widget = new PDAnnotationWidget();
widget.setRectangle(new PDRectangle(200, 200, 100, 20));
widget.setAnnotationFlags(4);
widget.setPage(page);
widget.setParent(comboBox);
List<String> exportValues = new ArrayList<>();
List<String> displayValues = new ArrayList<>();
displayValues.add("öne");
displayValues.add("two");
displayValues.add("thrée");
exportValues.add("1");
exportValues.add("2");
exportValues.add("3");
comboBox.setOptions(exportValues, displayValues);
List<PDAnnotationWidget> widgets = new ArrayList<>();
widgets.add(widget);
try
{
page.getAnnotations().add(widget);
}
catch (IOException e)
{
e.printStackTrace();
}
comboBox.setWidgets(widgets);
// new
acroForm.getFields().add(comboBox);
document.getDocumentCatalog().setAcroForm(acroForm);
PDResources dr = new PDResources();
dr.put(COSName.getPDFName("Helv"), font);
acroForm.setDefaultResources(dr);
try
{
FileOutputStream output = new FileOutputStream("test.pdf");
document.save(output);
}
catch (IOException e)
{
e.printStackTrace();
}
}
}

Categories

Resources