I have to generate a pdf file depending on some input .Each time the code runs , the input length may vary , so how can I add pages to the document dynamically depending on my input content .
public class pdfproject
{
static int lineno=768;
public static void main (String[] args) throws Exception
{
PDDocument doc= new PDDocument();
PDPage page = new PDPage();
doc.addPage(page);
PDPageContentStream cos = new PDPageContentStream(doc, page);
for(int i=0;i<2000;i++)
{
renderText("hello"+i,cos,60);
}
cos.close();
doc.save("test.pdf");
doc.close();
}
static void renderText(String Info,PDPageContentStream cos,int marginwidth) throws Exception
{
lineno-=12;
System.out.print("lineno="+lineno);
PDFont fontPlain = PDType1Font.HELVETICA;
cos.beginText();
cos.setFont(fontPlain, 10);
cos.moveTextPositionByAmount(marginwidth,lineno);
cos.drawString(Info);
cos.endText();
}
}
How do i ensure that the content is rendered on the next page by adding a new page dynamically when there is no space on the current page ?
Pdfbox does not include any automatic layouting support. Thus, you have to keep track of how full a page is, and you have to close the current page, create a new one, reset fill indicators, etc
This obviously should not be done in static members in some project class but instead in some dedicated class and its instance members. E.g.
public class PdfRenderingSimple implements AutoCloseable
{
//
// rendering
//
public void renderText(String Info, int marginwidth) throws IOException
{
if (content == null || textRenderingLineY < 12)
newPage();
textRenderingLineY-=12;
System.out.print("lineno=" + textRenderingLineY);
PDFont fontPlain = PDType1Font.HELVETICA;
content.beginText();
content.setFont(fontPlain, 10);
content.moveTextPositionByAmount(marginwidth, textRenderingLineY);
content.drawString(Info);
content.endText();
}
//
// constructor
//
public PdfRenderingSimple(PDDocument doc)
{
this.doc = doc;
}
//
// AutoCloseable implementation
//
/**
* Closes the current page
*/
#Override
public void close() throws IOException
{
if (content != null)
{
content.close();
content = null;
}
}
//
// helper methods
//
void newPage() throws IOException
{
close();
PDPage page = new PDPage();
doc.addPage(page);
content = new PDPageContentStream(doc, page);
content.setNonStrokingColor(Color.BLACK);
textRenderingLineY = 768;
}
//
// members
//
final PDDocument doc;
private PDPageContentStream content = null;
private int textRenderingLineY = 0;
}
(PdfRenderingSimple.java)
You can use it like this
PDDocument doc = new PDDocument();
PdfRenderingSimple renderer = new PdfRenderingSimple(doc);
for (int i = 0; i < 2000; i++)
{
renderer.renderText("hello" + i, 60);
}
renderer.close();
doc.save(new File("renderSimple.pdf"));
doc.close();
(RenderSimple.java)
For more specialized rendering support you will implement improved rendering classes, e.g. PdfRenderingEndorsementAlternative.java from this answer.
Related
Good morning
I try to create a .docx documento using docx4j.
In the document I need to insert the footer only in the first page.
This is my code
private WordprocessingMLPackage wordMLPackage;
private ObjectFactory factory;
private FooterPart footerPart;
private Ftr footer;
public void creaDocumentoConFooterImmagine() throws Exception {
wordMLPackage = WordprocessingMLPackage.createPackage();
factory = Context.getWmlObjectFactory();
Relationship relationship = this.createFooterPart();
this.createFooterReference(relationship);
wordMLPackage.getMainDocumentPart().addParagraphOfText("Hello Word!");
URL logo = getClass().getClassLoader().getResource("Footer.jpg");
InputStream is = logo.openStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] byteChunk = new byte[4096]; // Or whatever size you want to read in at a time.
int n;
while ( (n = is.read(byteChunk)) > 0 ) {
baos.write(byteChunk, 0, n);
}
this.addImageInline(baos.toByteArray());
addPageBreak();
wordMLPackage.getMainDocumentPart().addParagraphOfText("This is page 2!");
File exportFile = new File("C:\\footerConImmagine.docx");
wordMLPackage.save(exportFile);
}
private void addImageInline(byte[] byteArray) throws Exception {
BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, footerPart, byteArray);
int docPrId = 1;
int cNvPrId = 2;
Inline inLine = imagePart.createImageInline("Filename hint", "Alternative text", docPrId, cNvPrId, false);
if (footer != null) {
this.addInlineImageToFooter(inLine);
}
}
private void addInlineImageToFooter(Inline inLine) {
// Now add the in-line image to a paragraph
ObjectFactory factory2 = new ObjectFactory();
P paragraph2 = factory2.createP();
R run = factory.createR();
paragraph2.getContent().add(run);
Drawing drawing = factory.createDrawing();
run.getContent().add(drawing);
drawing.getAnchorOrInline().add(inLine);
footer.getContent().add(paragraph2);
}
private void createFooterReference(Relationship relationship) {
List<SectionWrapper> sections = wordMLPackage.getDocumentModel().getSections();
SectPr sectionProperties = sections.get(sections.size() - 1).getSectPr();
// There is always a section wrapper, but it might not contain a sectPr
if (sectionProperties == null) {
sectionProperties = factory.createSectPr();
wordMLPackage.getMainDocumentPart().addObject(sectionProperties);
sections.get(0).setSectPr(sectionProperties);
}
FooterReference footerReference = factory.createFooterReference();
footerReference.setId(relationship.getId());
footerReference.setType(HdrFtrRef.FIRST);
sectionProperties.getEGHdrFtrReferences().add(footerReference);
}
private Relationship createFooterPart() throws InvalidFormatException {
footerPart = new FooterPart();
footerPart.setPackage(wordMLPackage);
footerPart.setJaxbElement(this.createFooter("Text"));
return wordMLPackage.getMainDocumentPart().addTargetPart(footerPart);
}
//inserisco il testo del footer
private Ftr createFooter(String content) {
footer = factory.createFtr();
P paragraph = factory.createP();
R run = factory.createR();
Text text = new Text();
text.setValue(content);
run.getContent().add(text);
paragraph.getContent().add(run);
footer.getContent().add(paragraph);
return footer;
}
/**
* Adds a page break to the document.
*/
private void addPageBreak() {
MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();
Br breakObj = new Br();
breakObj.setType(STBrType.PAGE);
P paragraph = factory.createP();
paragraph.getContent().add(breakObj);
documentPart.getJaxbElement().getBody().getContent().add(paragraph);
}
If I set
footerReference.setType(HdrFtrRef.DEFAULT);
the footer is created properly, so the code seems correct.
I use this version of docx4j library: 6.1.2
How can I debug the problem?
Is there same example in the documentation of the library?
Thanks
Regards
When running this code with the PdfDocument not having a read source, it works properly. When I try reading from a premade pdf it stops creating the form/widgets, but still adds the paragraph as expected. There is no error given. Does anyone understand why this is happening?
Here is the code I'm running:
public class HelloWorld {
public static final String DEST = "sampleOutput.pdf";
public static final String SRC = "sample.pdf";
public static void main(String args[]) throws IOException {
File file = new File(DEST);
new HelloWorld().createPdf(SRC, DEST);
}
public void createPdf(String src, String dest) throws IOException {
//Initialize PDF reader and writer
PdfReader reader = new PdfReader(src);
PdfWriter writer = new PdfWriter(dest);
//Initialize PDF document
PdfDocument pdf = new PdfDocument(writer); //if i do (reader, writer) the widget isn't added to the first page anymore.
// Initialize document
Document document = new Document(pdf);
HelloWorld.addAcroForm(pdf, document);
//Close document
document.close();
}
public static PdfAcroForm addAcroForm(PdfDocument pdf, Document doc) throws IOException {
Paragraph title = new Paragraph("Test Form")
.setTextAlignment(TextAlignment.CENTER)
.setFontSize(16);
doc.add(title);
doc.add(new Paragraph("Full name:").setFontSize(12));
//Add acroform
PdfAcroForm form = PdfAcroForm.getAcroForm(doc.getPdfDocument(), true);
//Create text field
PdfTextFormField nameField = PdfFormField.createText(doc.getPdfDocument(),
new Rectangle(99, 753, 425, 15), "name", "");
form.addField(nameField);
return form;
}
}
I adapted your code like this:
public static PdfAcroForm addAcroForm(PdfDocument pdf, Document doc) throws IOException {
Paragraph title = new Paragraph("Test Form")
.setTextAlignment(TextAlignment.CENTER)
.setFontSize(16);
doc.add(title);
doc.add(new Paragraph("Full name:").setFontSize(12));
PdfAcroForm form = PdfAcroForm.getAcroForm(pdf, true);
PdfTextFormField nameField = PdfFormField.createText(pdf,
new Rectangle(99, 525, 425, 15), "name", "");
form.addField(nameField, pdf.getPage(1));
return form;
}
You'll notice two changes:
I change the Y offset of the field (525 instead of 753). Now the field is added inside the visible area of the page. In your code, the field was added, but it wasn't visible.
I defined to which page the fields needs to be added by adding pdf.getPage(1) as second parameter for the addField() method.
I want to create PDF table from HTML string. I can create that table, but instead of Text, I'm getting question marks. Here is my code:
public class ExportReportsToPdf implements StreamSource {
private static final long serialVersionUID = 1L;
private ByteArrayOutputStream byteArrayOutputStream;
public static final String FILE_LOC = "C:/Users/KiKo/CasesWorkspace/case/Export.pdf";
private static final String CSS = ""
+ "table {text-align:center; margin-top:20px; border-collapse:collapse; border-spacing:0; border-width:1px;}"
+ "th {font-size:14px; font-weight:normal; padding:10px; border-style:solid; overflow:hidden; word-break:normal;}"
+ "td {padding:10px; border-style:solid; overflow:hidden; word-break:normal;}"
+ "table-header {font-weight:bold; background-color:#EAEAEA; color:#000000;}";
public void createReportPdf(String tableHtml, Integer type) throws IOException, DocumentException {
// step 1
Document document = new Document(PageSize.A4, 20, 20, 50, 20);
// step 2
PdfWriter.getInstance(document, new FileOutputStream(FILE_LOC));
// step 3
byteArrayOutputStream = new ByteArrayOutputStream();
PdfWriter writer = PdfWriter.getInstance(document, byteArrayOutputStream);
if (type != null) {
writer.setPageEvent(new Watermark());
}
// step 4
document.open();
// step 5
document.add(getTable(tableHtml));
// step 6
document.close();
}
private PdfPTable getTable(String tableHtml) throws IOException {
// CSS
CSSResolver cssResolver = new StyleAttrCSSResolver();
CssFile cssFile = XMLWorkerHelper.getCSS(new ByteArrayInputStream(CSS.getBytes()));
cssResolver.addCss(cssFile);
// HTML
HtmlPipelineContext htmlContext = new HtmlPipelineContext(null);
htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
// Pipelines
ElementList elements = new ElementList();
ElementHandlerPipeline pdf = new ElementHandlerPipeline(elements, null);
HtmlPipeline html = new HtmlPipeline(htmlContext, pdf);
CssResolverPipeline css = new CssResolverPipeline(cssResolver, html);
// XML Worker
XMLWorker worker = new XMLWorker(css, true);
XMLParser parser = new XMLParser(worker);
InputStream inputStream = new byteArrayInputStream(tableHtml.getBytes());
parser.parse(inputStream);
return (PdfPTable) elements.get(0);
}
private static class Watermark extends PdfPageEventHelper {
#Override
public void onEndPage(PdfWriter writer, Document document) {
try {
URL url = Thread.currentThread().getContextClassLoader().getResource("/images/memotemp.jpg");
Image background = Image.getInstance(url);
float width = document.getPageSize().getWidth();
float height = document.getPageSize().getHeight();
writer.getDirectContentUnder().addImage(background, width, 0, 0, height, 0, 0);
} catch (DocumentException | IOException e) {
e.printStackTrace();
}
}
}
#Override
public InputStream getStream() {
return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
}
}
This code is working, and I'm getting this:
I've try to add UTF-8,
InputStream inputStream = new byteArrayInputStream(tableHtml.getBytes("UTF-8"));
but than I'm getting this:
I want to get something like this:
I think the problem is with the encoding, but I don't know how to solve this bug. Any suggestions...?
To get bytes from a (Unicode) String in some encoding, specify it,
otherwise the default system encoding is used.
tableHtml.getBytes(StandardCharsets.UTF_8)
In your case however "Windows-1251" seems a better match as the PDF does not seem to use UTF-8.
Maybe the original tableHTML String was read with the wrong encoding. Might check that, if it came from file or database.
You need to tell iText what encoding to use by creating an instance of the BaseFont class. Then in your document.add(getTable(tableHtml)); you can add a call to the font. Example at http://itextpdf.com/examples/iia.php?id=199.
I can't tell how you create a table but the class PdfPTable has a method addCell(PdfCell) and one constructor for PdfCell takes a Phrase. The Phrase can be constructed with a String and a Font. The font class takes a BaseFont as a constructor argument.
If you look around the Javadoc for iText you will see various classes take a Font as a constructor argument.
I am creating a PDF and writing the stream in response. Before writing in the stream, I want to add a background image as watermark in all the pages so that PDF document flushed through response is the final one with watermark.
Hi this is my code sample. Any help would be much appriciated
private static String generatePDF(HttpServletRequest request, HttpServletResponse response, String fileName) throws Exception
{
Document document = null;
PdfWriter writer = null;
FileOutputStream fos = null;
try
{
fos = new FileOutputStream(fileName);
Document document = new Document(PageSize.A4);
writer = PdfWriter.getInstance(document, fos);
document.open();
/**
* Adding tables and cells and other stuff required
**/
return pdfFileName;
} catch (Exception e) {
FileUtil.deleteFile(fileName);
throw e
} finally {
if (document != null) {
document.close();
}
fos.flush();
}
}
I now would like to add a background image using the below code and write the output PDF to the same stream
PdfReader sourcePDFReader = null;
try
{
sourcePDFReader = new PdfReader(sourcePdfFileName);
int noOfPages = sourcePDFReader.getNumberOfPages();
PdfStamper stamp = new PdfStamper(sourcePDFReader, new FileOutputStream(destPdfFileName));
int i = 0;
Image templateImage = Image.getInstance(templateImageFile);
templateImage.setAbsolutePosition(0, 0);
PdfContentByte tempalteBytes;
while (i < noOfPages) {
i++;
tempalteBytes = stamp.getUnderContent(i);
tempalteBytes.addImage(templateImage);
}
stamp.close();
return destPdfFileName;
} catch (Exception ex) {
LOGGER.log(Level.INFO, "Error when applying tempalte image as watermark");
} finally {
if (sourcePDFReader != null) {
sourcePDFReader.close();
}
}
I solved this using Bruno's first (recommended) approach.
1) Create a page event helper with an onEndPage event:
class PDFBackground extends PdfPageEventHelper {
#Override
void onEndPage(PdfWriter writer, Document document) {
Image background = Image.getInstance("myimage.png");
// This scales the image to the page,
// use the image's width & height if you don't want to scale.
float width = document.getPageSize().getWidth();
float height = document.getPageSize().getHeight();
writer.getDirectContentUnder()
.addImage(background, width, 0, 0, height, 0, 0);
}
}
2) When creating your writer, register your page event helper:
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(file));
writer.setPageEvent(new PDFBackground());
I have solved this with Bruno's second option. Here is the code.
public static String addBackgroundImageToPDF(ByteArrayOutputStream bos, String destPdfFileName, String templateImageFile)
{
PdfReader sourcePDFReader = null;
try
{
sourcePDFReader = new PdfReader(bos.toByteArray());
int noOfPages = sourcePDFReader.getNumberOfPages();
PdfStamper stamp = new PdfStamper(sourcePDFReader, new FileOutputStream(destPdfFileName));
int i = 0;
Image templateImage = Image.getInstance(templateImageFile);
templateImage.setAbsolutePosition(0, 0);
PdfContentByte tempalteBytes;
while (i < noOfPages)
{
i++;
tempalteBytes = stamp.getUnderContent(i);
tempalteBytes.addImage(templateImage);
}
stamp.close();
return destPdfFileName;
}
catch (Exception ex)
{
LOGGER.log(Level.INFO, "Error when applying template image as watermark");
}
finally
{
if (sourcePDFReader != null)
{
sourcePDFReader.close();
}
}
}
You can choose between two options:
Use the background image in a page event (to the 'under' content in the onEndPage() method)/
Create the first PDF in memory, then add the background image in a second pass using the code you posted.
I prefer option 1.
I need to crop PDF file using this code. cb will output the needed coordinates for the cropping.
How can efficiently do this for every page in a PDF file?
Did this code, but does not work for every pdf input:
public class CropPages {
public static final String PREFACE = "input.pdf";
public static final String RESULT = "cropped.pdf";
public void addMarginRectangle(String src, String dest)
throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(RESULT));
TextMarginFinder finder;
PdfDictionary pageDict;
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
finder = parser.processContent(i, new TextMarginFinder());
//PdfContentByte cb = stamper.getOverContent(i);
PdfRectangle rect = new PdfRectangle((finder.getLlx()-5), (finder.getLly()-5), (finder.getUrx()+5), (finder.getUry()+5));
if (i <= 10){
System.out.println(rect);
}
pageDict = reader.getPageN(i);
pageDict.put(PdfName.CROPBOX, rect);
}
stamper.close();
}
public static void main(String[] args) throws IOException, DocumentException {
new CropPages().addMarginRectangle(PREFACE, RESULT);
}
}