generate zip file grouping multiple pdf, using servlet and itext 7 - java

I'm trying to put multiple generated pdf into a zip from servlet using itext7, I've managed to put one pdf in a zip file but not more. Here is the code:
private void printMore(HttpServletRequest req, HttpServletResponse resp) throws SQLException {
String masterPath = req.getServletContext().getRealPath("/assets/template/templateStatement.pdf");
try (PdfReader reader = new PdfReader(masterPath);
ZipOutputStream zipFile = new ZipOutputStream(resp.getOutputStream());
PdfWriter writer = new PdfWriter(zipFile);
PdfDocument pdf = new PdfDocument(reader, writer);
Document doc = new Document(pdf)) {
List<Student> studentList = getFactoryDAO().getStatementDAO().selectStudentHasBalance();
for (Student student : studentList){
// Generate PDF for the student
PdfPage page = pdf.getPage(1);
PdfCanvas canvas = new PdfCanvas(page);
FontProgram fontProgram = FontProgramFactory.createFont();
PdfFont font = PdfFontFactory.createFont(fontProgram, "utf-8", true);
canvas.setFontAndSize(font, 10);
canvas.beginText();
canvas.setTextMatrix(178, 650); // student code
canvas.showText(student.getS_Code());
canvas.setTextMatrix(200, 610); // Date of Statement
canvas.showText(new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
canvas.endText();
float[] pointsWidth = {60f,120f,70f,70f};
Table table = new Table(pointsWidth);
table.setMarginTop(280);
table.setMarginLeft(70);
table.setFont(font);
table.setFontSize(10);
table.setTextAlignment(TextAlignment.CENTER);
//Header Table
table.addCell(new Cell().add("Date Inscription"));
table.addCell(new Cell().add("Name"));
table.addCell(new Cell().add("Fees"));
table.addCell(new Cell().add("Observation"));
//Detail Table
table.addCell(new Cell().add(student.getTxnDate()));
table.addCell(new Cell().add(student.getS_FullName));
table.addCell(new Cell().add(student.getFees));
table.addCell(new Cell().add(student.getObservation));
doc.add(table);
ZipEntry zipEntry = new ZipEntry(student.getS_Code() + "_" + student.getS_LName() + ".pdf");
zipFile.putNextEntry(zipEntry);
//zipFile.write(); Shall I use it?
}
} catch (IOException e) {
e.printStackTrace();
}
resp.setHeader("Content-disposition","attachement; filename=test.zip");
resp.setContentType("application/zip");
}
I've based on this this post and this post but doesn't work. I already check more post like this but the version of itext7 has no PdfWriter.getInstance as mentionned. I've tried more thing but can't managed to go furthermore.
UPDATED :
After Enterman suggestion i updated it like this :
String masterPath = req.getServletContext().getRealPath("/assets/template/templateStatement.pdf");
try (PdfReader reader = new PdfReader(masterPath);
ZipOutputStream zipFile = new ZipOutputStream(resp.getOutputStream());
PdfWriter writer = new PdfWriter(zipFile);
PdfDocument pdf = new PdfDocument(reader, writer)
) {
List<Student> studentList = getFactoryDAO().getStatementDAO().selectStudentHasBalance();
for (Student student : studentList){
try (Document doc = new Document(pdf)){
// Generate PDF for the student
PdfPage page = pdf.getPage(1);
PdfCanvas canvas = new PdfCanvas(page);
FontProgram fontProgram = FontProgramFactory.createFont();
PdfFont font = PdfFontFactory.createFont(fontProgram, "utf-8", true);
canvas.setFontAndSize(font, 10);
canvas.beginText();
canvas.setTextMatrix(178, 650); // student code
canvas.showText(student.getS_Code());
canvas.setTextMatrix(200, 610); // Date of Statement
canvas.showText(new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
canvas.endText();
float[] pointsWidth = {60f,120f,70f,70f};
Table table = new Table(pointsWidth);
table.setMarginTop(280);
table.setMarginLeft(70);
table.setFont(font);
table.setFontSize(10);
table.setTextAlignment(TextAlignment.CENTER);
//Header Table
table.addCell(new Cell().add("Date Inscription"));
table.addCell(new Cell().add("Name"));
table.addCell(new Cell().add("Fees"));
table.addCell(new Cell().add("Observation"));
//Detail Table
table.addCell(new Cell().add(student.getTxnDate()));
table.addCell(new Cell().add(student.getS_FullName));
table.addCell(new Cell().add(student.getFees));
table.addCell(new Cell().add(student.getObservation));
doc.add(table);
ZipEntry zipEntry = new ZipEntry(student.getS_Code() + "_" + student.getS_LName() + ".pdf");
zipFile.putNextEntry(zipEntry);
//zipFile.write(); Shall I use it?
}
}
} catch (IOException e) {
e.printStackTrace();
}
resp.setHeader("Content-disposition","attachement; filename=test.zip");
resp.setContentType("application/zip");
But still no luck.
Your help is welcome.

You should try using a FileInputStream, like below (code of my own, working well in production)
private File createZip(List<File> forZip, LocalDate date) {
String zipName = Constants.ORDER_FILE_PATH + date.getYear() +"-" + date.getMonth().getValue() + "-" + date.getMonth().getDisplayName(TextStyle.FULL, Locale.FRENCH) + ".zip";
FileOutputStream fos = null;
try {
fos = new FileOutputStream(zipName);
ZipOutputStream zipOut = new ZipOutputStream(fos);
for (File srcFile : forZip) {
FileInputStream fis = new FileInputStream(srcFile);
ZipEntry zipEntry = new ZipEntry(srcFile.getName());
zipOut.putNextEntry(zipEntry);
byte[] bytes = new byte[1024];
int length;
while((length = fis.read(bytes)) >= 0) {
zipOut.write(bytes, 0, length);
}
fis.close();
//srcFile.delete();
}
zipOut.close();
fos.close();
} catch (IOException e) {
logger.error("createZip", e);
}
return new File(zipName);
}

Special thanks for BenjaminD, Enterman for their support and all stack community.
I post this answer for those who has faced the same problem as me. (I've been stuck for 4 days). "Generating multiple PDF with itext 7 and servlets"
As Enterman said : "need new Instance of Document". In addition need PdfDocument also. And as BenjaminD instruction : "Generate a file PDF first, after put them into a zip".
String masterPath = req.getServletContext().getRealPath("/assets/template/templateStatement.pdf");
try (ZipOutputStream zipOut = new ZipOutputStream(resp.getOutputStream())) {
Liste<File> listFile = new ArrayList<>();
List<Student> studentList = getFactoryDAO().getStatementDAO().selectStudentHasBalance();
for (Student student : studentList){
File file = new File(student.getS_Code()+"_"+new SimpleDateFormat("yyyy-MM-dd").format(new Date())+".pdf");
try (PdfDocument pdf = new PdfDocument(new PdfReader(masterPath), new PdfWriter(file.getName()))
Document doc = new Document(pdf)){
// Generate PDF for the student
PdfPage page = pdf.getPage(1);
PdfCanvas canvas = new PdfCanvas(page);
FontProgram fontProgram = FontProgramFactory.createFont();
PdfFont font = PdfFontFactory.createFont(fontProgram, "utf-8", true);
canvas.setFontAndSize(font, 10);
canvas.beginText();
canvas.setTextMatrix(178, 650); // student code
canvas.showText(student.getS_Code());
canvas.setTextMatrix(200, 610); // Date of Statement
canvas.showText(new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
canvas.endText();
float[] pointsWidth = {60f,120f,70f,70f};
Table table = new Table(pointsWidth);
table.setMarginTop(280);
table.setMarginLeft(70);
table.setFont(font);
table.setFontSize(10);
table.setTextAlignment(TextAlignment.CENTER);
//Header Table
table.addCell(new Cell().add("Date Inscription"));
table.addCell(new Cell().add("Name"));
table.addCell(new Cell().add("Fees"));
table.addCell(new Cell().add("Observation"));
//Detail Table
table.addCell(new Cell().add(student.getTxnDate()));
table.addCell(new Cell().add(student.getS_FullName));
table.addCell(new Cell().add(student.getFees));
table.addCell(new Cell().add(student.getObservation));
doc.add(table);
listFile.add(file);
}
}
File zipFile = createZip(listFile,
newSimpleDateFormat("yyyy-MM-dd").format(new Date())); //BenjaminD source in answer
ZipEntry zipEntry = new ZipEntry(zipFile.getName);
zipEntry.putNextEntry(zipEntry);
fileInputStream fis = new FileInputStream(zipFile);
byte[] bytes = new byte[1024];
int length;
while((length = fis.read(bytes)) >= 0) {
zipOut.write(bytes, 0, length);
}
fis.close();
resp.setHeader("Content-disposition","attachement; filename=" + zipFile);
resp.setContentType("application/zip");
} catch (IOException e) {
e.printStackTrace();
}
It will put the zip in a zip (can Directly use FileOutpuStream to not put it again in a zip).
Thank you again.

Related

java.io.IOException: The document has no pages. Working fine locally but not working after deploying in aws

Hi I am trying to create an PDF using itextpdf.
It is working fine when tested locally.
But after aws deployment I am getting 406 Not Acceptable with java.io.IOException: The document has no pages excpetion.
Many answers are there but those are not related to deployment issues.
Do I need to check any network configuration or problem lies in the pdf generation code?
Following is my code implementation:
Please suggest some solution.
public byte[] createPdf(List<Participant> participantList) throws IOException,
DocumentException, com.google.zxing.WriterException {
Document document = new Document();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
PdfWriter.getInstance(document, byteArrayOutputStream);
document.open();
document.add(createMainTable(participantList));
document.close();
return byteArrayOutputStream.toByteArray();
}
public static PdfPTable createMainTable(List<Participant> optionalParticipant) throws BadElementException,
IOException, com.google.zxing.WriterException {
PdfPTable table = new PdfPTable(2);
logger.info("Main Table was created");
for (int i = 0; i < optionalParticipant.size(); i++) {
PdfPCell cell1 = new PdfPCell();
cell1.setBorderWidth(0);
cell1.setPadding(10f);
cell1.addElement(createSubTable(optionalParticipant.get(i)));
table.addCell(cell1);
}
return table;
}
public static PdfPTable createSubTable(Participant participant) throws BadElementException,
IOException, com.google.zxing.WriterException {
BaseColor baseColor = new BaseColor(150, 150, 150);
PdfPTable table = new PdfPTable(2);
table.setWidthPercentage(100);
PdfPCell cell, cell1, cell2;
Font font = new Font();
font.setSize(10f);
font.setColor(BaseColor.WHITE);
String participantName = participant.getFirstName() + " " + participant.getLastName();
Chunk chunk = new Chunk(participantName, font);
Paragraph head = new Paragraph(" "+chunk);
head.setFont(font);
cell = new PdfPCell(head);
cell.setColspan(2);
cell.setBackgroundColor(baseColor);
cell.setPadding(2f);
table.addCell(cell);
String qrData = participant.getQrCodeData();
Image img = getQRCodeImage(qrData);
font = new Font();
font.setSize(5f);
chunk = new Chunk("\n" + "Event ID: " + participant.getEvent().getEventId() +
"\n\n" + "Unique ID: " + participant.getUniqueId() +
"\n\n" + "Email ID: " + participant.getEmail(), font);
Paragraph body = new Paragraph(chunk);
cell1 = new PdfPCell(body);
cell1.setBorderWidthRight(0);
cell1.setPadding(10f);
cell2 = new PdfPCell();
cell2.addElement(img);
cell2.setBorderWidthLeft(1);
cell2.setPadding(10f);
table.addCell(cell1);
table.addCell(cell2);
logger.info("Sub Table was created");
return table;
}
Please check participantList is not null and contains elements. You can use logger to print size of list before start using it.
logger.info("No of participants: "+participantList.size());
Also, Immediately after opening document, always add an empty chunk to document so that you can avoid this exception.
document.open();
document.add(new Chunk(""));

concatenate n pdf reports into one pdf (jasper report) [duplicate]

I have a web application with a dropdown from where user could select the type of report viz. report1, report2, report3, etc.
Based on the report selected, a Jasper report is compiled on the server and opens as a pop up in PDF format.
On the server side, I am implementing each report in a separate method using below code say for e.g. for report1:
JRBeanCollectionDataSource report1DataSource = new JRBeanCollectionDataSource(resultSetBeanListReport1);
InputStream inputStreamReport1 = new FileInputStream(request.getSession().getServletContext ().getRealPath(jrxmlFilePath + "report1.jrxml"));
JasperDesign jasperDesignReport1 = JRXmlLoader.load(inputStreamReport1);
JasperReport jasperReportReport1 = JasperCompileManager.compileReport(jasperDesignReport1);
bytes = JasperRunManager.runReportToPdf(jasperReportReport1, titleMapReport1, report1DataSource);
Similarly, report2 is in a separate method with below code:
JRBeanCollectionDataSource invstSummDataSource = new JRBeanCollectionDataSource(resultSetBeanListInvstOfSumm);
InputStream inputStreamInvstSumm = new FileInputStream(request.getSession().getServletContext().getRealPath(jrxmlFilePath + "investSummary.jrxml"));
JasperDesign jasperDesignInvstSumm = JRXmlLoader.load(inputStreamInvstSumm);
JasperReport jasperReportInvstSumm = JasperCompileManager.compileReport(jasperDesignInvstSumm);
bytes = JasperRunManager.runReportToPdf(jasperReportInvstSumm, titleMapInvstSumm, invstSummDataSource);
Now I have a requirement that if report1 is selected from the dropdown, the resulting PDF should contain all the reports one after other in the same PDF.
How can I combine above two lines of codes to finally generate a single PDF?
Here is sample code for combining multiple jasper prints
List<JasperPrint> jasperPrints = new ArrayList<JasperPrint>();
// Your code to get Jasperreport objects
JasperReport jasperReportReport1 = JasperCompileManager.compileReport(jasperDesignReport1);
jasperPrints.add(jasperReportReport1);
JasperReport jasperReportReport2 = JasperCompileManager.compileReport(jasperDesignReport2);
jasperPrints.add(jasperReportReport2);
JasperReport jasperReportReport3 = JasperCompileManager.compileReport(jasperDesignReport3);
jasperPrints.add(jasperReportReport3);
JRPdfExporter exporter = new JRPdfExporter();
//Create new FileOutputStream or you can use Http Servlet Response.getOutputStream() to get Servlet output stream
// Or if you want bytes create ByteArrayOutputStream
ByteArrayOutputStream out = new ByteArrayOutputStream();
exporter.setParameter(JRExporterParameter.JASPER_PRINT_LIST, jasperPrints);
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, out);
exporter.exportReport();
byte[] bytes = out.toByteArray();
This answer is to help user using latest version of Jasper-Report. In
#Sangram Jadhav accept answer the
JRExporterParameter.JASPER_PRINT_LIST is deprecated
The current code would be:
Map<String, Object> paramMap = new HashMap<String, Object>();
List<JasperPrint> jasperPrintList = new ArrayList<JasperPrint>();
JasperPrint jasperPrint1 = JasperFillManager.fillReport(report1, paramMap);
jasperPrintList.add(jasperPrint1);
JasperPrint jasperPrint2 = JasperFillManager.fillReport(report2, paramMap);
jasperPrintList.add(jasperPrint2);
JRPdfExporter exporter = new JRPdfExporter();
exporter.setExporterInput(SimpleExporterInput.getInstance(jasperPrintList)); //Set as export input my list with JasperPrint s
exporter.setExporterOutput(new SimpleOutputStreamExporterOutput("pdf/output.pdf")); //or any other out streaam
SimplePdfExporterConfiguration configuration = new SimplePdfExporterConfiguration();
configuration.setCreatingBatchModeBookmarks(true); //add this so your bookmarks work, you may set other parameters
exporter.setConfiguration(configuration);
exporter.exportReport();
You can either merge reports before generating PDFs using JasperPrint or after generating PDFs using iText.
For the JasperPrint solution: you will generate the 2 (or more) JasperPrints then get the content pages and concat them.
JasperPrint jp1 = JasperFillManager.fillReport(url.openStream(), parameters,
new JRBeanCollectionDataSource(inspBean));
JasperPrint jp2 = JasperFillManager.fillReport(url.openStream(), parameters,
new JRBeanCollectionDataSource(inspBean));
List pages = jp2 .getPages();
for (int j = 0; j < pages.size(); j++) {
JRPrintPage object = (JRPrintPage)pages.get(j);
jp1.addPage(object);
}
JasperViewer.viewReport(jp1,false);
For the iText solution after generating the PDFs:
void concatPDFs(List<InputStream> streamOfPDFFiles, OutputStream outputStream, boolean paginate) {
Document document = new Document();
try {
List<InputStream> pdfs = streamOfPDFFiles;
List<PdfReader> readers = new ArrayList<PdfReader>();
int totalPages = 0;
Iterator<InputStream> iteratorPDFs = pdfs.iterator();
// Create Readers for the pdfs.
while (iteratorPDFs.hasNext()) {
InputStream pdf = iteratorPDFs.next();
PdfReader pdfReader = new PdfReader(pdf);
readers.add(pdfReader);
totalPages += pdfReader.getNumberOfPages();
}
// Create a writer for the outputstream
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
document.open();
BaseFont bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
PdfContentByte cb = writer.getDirectContent(); // Holds the PDF
// data
PdfImportedPage page;
int currentPageNumber = 0;
int pageOfCurrentReaderPDF = 0;
Iterator<PdfReader> iteratorPDFReader = readers.iterator();
// Loop through the PDF files and add to the output.
while (iteratorPDFReader.hasNext()) {
PdfReader pdfReader = iteratorPDFReader.next();
// Create a new page in the target for each source page.
while (pageOfCurrentReaderPDF < pdfReader.getNumberOfPages()) {
document.newPage();
pageOfCurrentReaderPDF++;
currentPageNumber++;
page = writer.getImportedPage(pdfReader, pageOfCurrentReaderPDF);
cb.addTemplate(page, 0, 0);
// Code for pagination.
if (paginate) {
cb.beginText();
cb.setFontAndSize(bf, 9);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, "" + currentPageNumber + " of " + totalPages, 520, 5, 0);
cb.endText();
}
}
pageOfCurrentReaderPDF = 0;
}
outputStream.flush();
document.close();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (document.isOpen())
document.close();
try {
if (outputStream != null)
outputStream.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
Here is my code which i use on grails code .Its gives me two different report in one pdf.
String reportDir = Util.getReportDirectory() // my report directory
Map reportParams = new LinkedHashMap()
Map reportParams1 = new LinkedHashMap()
String outputReportName="Test_Output_copy"
reportParams.put('parameter name',"parameter")
reportParams1.put('copy',"Customer's Copy")
JasperReportDef reportDef1 = new JasperReportDef(name: 'testBillReport.jasper', fileFormat: JasperExportFormat.PDF_FORMAT,
parameters: reportParams, folder: reportDir)
JasperReportDef reportDef2 = new JasperReportDef(name: 'testBillReport.jasper', fileFormat: JasperExportFormat.PDF_FORMAT,
parameters: reportParams1, folder: reportDir)
List<JasperReportDef> jasperPrintList = new ArrayList<JasperReportDef>();
jasperPrintList.add(reportDef1);
jasperPrintList.add(reportDef2);
ByteArrayOutputStream report1 = jasperService.generateReport(jasperPrintList);
response.setHeader("Content-disposition", "inline;filename="+outputReportName+'.pdf')
response.contentType = "application/pdf"
response.outputStream << report1.toByteArray()
You can try this one this is work for me
JasperPrint jp1 = JasperFillManager.fillReport(reportFile1,reportParams,Connection);
JasperPrint jp2 = JasperFillManager.fillReport(reportFile2,reportParams,Connection);
for (int j = 0; j < jp1.getPages().size(); j++) {
//Add First report to second report
jp2.addPage((JRPrintPage) jp1.getPages().get(j));
}

How to get the OutputStream of a written PDF from iText

I need your help in getting and storing the written PDF from iText in an OutputStream and then to convert it to an InputStream.
The code of writing the PDF is below:
public void CreatePDF() throws IOException {
try{
Document doc = new Document(PageSize.A4, 50, 50, 50, 50);
OutputStream out = new ByteArrayOutputStream();
PdfWriter writer = PdfWriter.getInstance(doc, out);
doc.open();
PdfPTable table = new PdfPTable(1);
PdfPCell cell = new PdfPCell(new Phrase("First PDF"));
cell.setBorder(Rectangle.NO_BORDER);
cell.setRunDirection(PdfWriter.RUN_DIRECTION_LTR);
table.addCell(cell);
doc.add(table);
doc.close();
}
catch (Exception e) {
e.printStackTrace();
}
}
So I am seeking your help to write that PDF in an OutputStream and then to convert it to InputStream.
I need to get the InputStream value so I can pass it to the line for the file download:
StreamedContent file = new DefaultStreamedContent(InputStream, "application/pdf", "xxx.pdf");
Updated Jon Answer:
public InputStream createPdf1() throws IOException {
Document doc = new Document(PageSize.A4, 50, 50, 50, 50);
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PdfWriter writer;
try {
writer = PdfWriter.getInstance(doc, out);
} catch (DocumentException e) {
}
doc.open();
PdfPTable table = new PdfPTable(1);
PdfPCell cell = new PdfPCell(new Phrase("First PDF"));
cell.setBorder(Rectangle.NO_BORDER);
cell.setRunDirection(PdfWriter.RUN_DIRECTION_LTR);
table.addCell(cell);
doc.add(table);
}
catch ( Exception e)
{
e.printStackTrace();
}
return new ByteArrayInputStream(out.toByteArray());
}
You should change the declaration of out to be of type ByteArrayOutputStream rather than just OutputStream. Then you can call ByteArrayOutputStream.toByteArray() to get the bytes, and construct a ByteArrayInputStream wrapping that.
As an aside, I wouldn't catch Exception like that, and I'd use a try-with-resources statement to close the document, assuming it implements AutoCloseable. It's also a good idea to follow Java naming conventions. So for example, you might have:
public InputStream createPdf() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try (Document doc = new Document(PageSize.A4, 50, 50, 50, 50)) {
PdfWriter writer = PdfWriter.getInstance(doc, out);
doc.open();
PdfPTable table = new PdfPTable(1);
PdfPCell cell = new PdfPCell(new Phrase("First PDF"));
cell.setBorder(Rectangle.NO_BORDER);
cell.setRunDirection(PdfWriter.RUN_DIRECTION_LTR);
table.addCell(cell);
doc.add(table);
}
return new ByteArrayInputStream(out.toByteArray());
}

Merging files into single pdf generate different view

I´m trying to merge into one file with one or more PDF files stored files and a JasperReports file using iText.
I did this method.
Map<String, Object> parameters = new HashMap<String, Object>();
List<String> arquivos = new LinkedList<String>();
arquivos.add("/Formulario/teste.jrxml");
arquivos.add("/Formulario/teste.pdf");
try {
Document document = new Document(PageSize.A4);
document.setMargins(0F, 0F, 0F, 0F);
PdfCopy pdfCopy = new PdfCopy(document, new FileOutputStream("c:\\labels\\teste.pdf"));
pdfCopy.setMargins(0, 0, 0, 0);
document.open();
PdfReader pdfReader = null;
InputStream inputStream = null;
for (String arquivo : arquivos) {
inputStream = ReportService.class.getResourceAsStream(arquivo);
if (FilenameUtils.getExtension(arquivo).equals("jrxml")) {
JasperReport report = JasperCompileManager.compileReport(inputStream);
JasperPrint jasperPrint = JasperFillManager.fillReport(report, parameters, new JREmptyDataSource());
inputStream = new ByteArrayInputStream(JasperExportManager.exportReportToPdf(jasperPrint));
}
pdfReader = new PdfReader(inputStream);
for (int i = 1; i <= pdfReader.getNumberOfPages(); i++) {
pdfCopy.addPage(pdfCopy.getImportedPage(pdfReader, i));
}
pdfCopy.freeReader(pdfReader);
pdfReader.close();
}
document.close();
} catch (DocumentException | IOException | JRException e) {
e.printStackTrace();
}
I looked at google, but didn't found any answer. So whats wrong? It's possible to generate these pages in the same margins?

RTL not working in pdf generation with itext 5.5 for Arabic text

I have java code that writes arabic characters with the help of itext 5.5 and xmlworker jars, but its writing left to right even after writer.setRunDirection(PdfWriter.RUN_DIRECTION_RTL) is used.
Code used is:
public class CreateArabic extends DefaultHandler {
/** Paths to and encodings of fonts we're going to use in this example */
public static String[][] FONTS = {
{"C:/arialuni.ttf", BaseFont.IDENTITY_H},
{"C:/abserif4_5.ttf", BaseFont.IDENTITY_H},
{"C:/damase.ttf", BaseFont.IDENTITY_H},
{"C:/fsex2p00_public.ttf", BaseFont.IDENTITY_H}
};
/** Holds he fonts that can be used for the peace message. */
public FontSelector fs;
public CreateArabic() {
fs = new FontSelector();
for (int i = 0; i < FONTS.length; i++) {
fs.addFont(FontFactory.getFont(FONTS[i][0], FONTS[i][1], BaseFont.EMBEDDED));
}
}
public static void main(String args[]) {
try {
// step 1
Rectangle pagesize = new Rectangle(8.5f * 72, 11 * 72);
Document document = new Document();//pagesize, 72, 72, 72, 72);// step1
PdfWriter writer = PdfWriter.getInstance(document,
new FileOutputStream("c:\\report.pdf"));
writer.setInitialLeading(200.5f);
//writer.getAcroForm().setNeedAppearances(true);
//writer.setRunDirection(PdfWriter.RUN_DIRECTION_RTL);
document.open();
FontFactory.registerDirectories();
Font font = FontFactory.getFont("C:\\damase.ttf",
BaseFont.IDENTITY_H, true, 22, Font.BOLD);
// step 3
document.open();
// step 4
XMLWorkerHelper helper = XMLWorkerHelper.getInstance();
// CSS
CSSResolver cssResolver = new StyleAttrCSSResolver();
CssFile cssFile = helper.getCSS(new FileInputStream(
"D:\\Itext_Test\\Test\\src\\test.css"));
cssResolver.addCss(cssFile);
// HTML
XMLWorkerFontProvider fontProvider = new XMLWorkerFontProvider();
// fontProvider.addFontSubstitute("lowagie", "garamond");
CssAppliers cssAppliers = new CssAppliersImpl(fontProvider);
HtmlPipelineContext htmlContext = new HtmlPipelineContext(
cssAppliers);
htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
// // Pipelines
PdfWriterPipeline pdf = new PdfWriterPipeline(document, writer);
HtmlPipeline html = new HtmlPipeline(htmlContext, pdf);
CssResolverPipeline css = new CssResolverPipeline(cssResolver,
html);
writer.setRunDirection(PdfWriter.RUN_DIRECTION_RTL);
System.out.println("RUN DIRECTION --> "+writer.getRunDirection());
XMLWorker worker = new XMLWorker(css, true);
XMLParser p = new XMLParser(worker,Charset.forName("UTF-8"));
String htmlString2 = "<html><body style=\"color:red;\">Hello"+"??"+"</body></html>";
String htmlString = "<body style='font-family:arial;'>h"+"??"+"<p style='font-family:arial;' > ????? </p></body>";
String html1 ="<html><head></head><body>Hello <p style=\"color:red\" >oo ??</p> World! \u062a\u0639\u0637\u064a \u064a\u0648\u0646\u064a\u0643\u0648\u062f \u0631\u0642\u0645\u0627 \u0641\u0631\u064a\u062f\u0627 \u0644\u0643\u0644 \u062d\u0631\u0641 "+htmlString+"Testing</body></html>";
ByteArrayInputStream is = new ByteArrayInputStream(htmlString.getBytes("UTF-8"));
p.detectEncoding(is);
p.parse(is, Charset.forName("UTF-8"));//.parse(is, "UTF-8");//parse(is);//ASMO-708
// step 5
document.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Output file is also attached.
As documented, this is not supposed to work:
writer.setRunDirection(PdfWriter.RUN_DIRECTION_RTL);
Arabic (and Hebrew) can only be rendered correctly in the context of ColumnText and PdfPCell. In other words: if you want to use Arabic from XML Worker, you need to create an ElementList and then add the elments to a ColumnText object as is done here.
You need to set the run direction at the level of the ColumnText object.
//This solution works for me: :)
// document
Document document = new Document(PageSize.LEGAL);
//fonts
XMLWorkerFontProvider fontProvider = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
fontProvider.register("/Users/ibrahimbakhsh/Library/Fonts/tradbdo.ttf", BaseFont.IDENTITY_H);
fontProvider.register("/Users/ibrahimbakhsh/Library/Fonts/trado.otf", BaseFont.IDENTITY_H);
fontProvider.register("/Users/ibrahimbakhsh/Library/Fonts/tahoma.ttf", BaseFont.IDENTITY_H);
CssAppliers cssAppliers = new CssAppliersImpl(fontProvider);
HtmlPipelineContext htmlContext = new HtmlPipelineContext(cssAppliers);
htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
// CSS
CSSResolver cssResolver =
XMLWorkerHelper.getInstance().getDefaultCssResolver(true);
// Pipelines
ElementList elements = new ElementList();
ElementHandlerPipeline end = new ElementHandlerPipeline(elements, null);
HtmlPipeline html = new HtmlPipeline(htmlContext, end);
CssResolverPipeline css = new CssResolverPipeline(cssResolver, html);
// HTML
htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
htmlContext.autoBookmark(false);
// XML Worker
XMLWorker worker = new XMLWorker(css, true);
XMLParser p = new XMLParser(worker);
p.parse(new FileInputStream(HTML));
//writer
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(file));
writer.setInitialLeading(12.5f);
writer.setRunDirection(PdfWriter.RUN_DIRECTION_RTL);
// step 4
document.open();
// step 5
for (Element e : elements) {
//out.println(e.toString());
if(e instanceof PdfPTable){
PdfPTable t = (PdfPTable) e;
t.setRunDirection(PdfWriter.RUN_DIRECTION_RTL);
ArrayList<PdfPRow> rows = t.getRows();
for(PdfPRow row:rows){
PdfPCell[] cells = row.getCells();
for(PdfPCell cell:cells){
cell.setRunDirection(PdfWriter.RUN_DIRECTION_RTL);
}
}
e = t;
}
document.add(e);
}
//try adding new table
PdfPTable table = new PdfPTable(1);
table.setRunDirection(PdfWriter.RUN_DIRECTION_RTL);
Font f = new Font(BaseFont.createFont("/Users/ibrahimbakhsh/Library/Fonts/trado.otf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED));
PdfPCell cell = new PdfPCell(new Paragraph("تجربة نص عربي",f));
table.addCell(cell);
document.add(table);
// step 6
document.close();
For developers that need an straightforward solution
I used this trick and output is very clean and nice!
create a PDFPTable with 1 column
for every paragraph of your content, create a Paragraph object and set its alignment to Paragraph.ALIGN_JUSTIFIED (I don't know why but it causes to paragraph align to right of page!!!)
create a PDFPCell and remove its borders using setBorder(Rectangle.NO_BORDER) and add the paragraph to cell
add the cell to the table
here is a code sample to your convenience.
public void main(){
/*
* create and initiate document
* */
// repeat this for all your paragraphs
PdfPTable pTable = new PdfPTable(1);
Paragraph paragraph = getCellParagraph();
paragraph.add("your RTL content");
PdfPCell cell = getPdfPCellNoBorder(paragraph);
pTable.addCell(cell);
// after add all your content
document.add(pTable);
}
private Paragraph getCellParagraph() {
Paragraph paragraph = new Paragraph();
paragraph.setAlignment(Paragraph.ALIGN_JUSTIFIED);
// set other styles you need like custom font
return paragraph;
}
private PdfPCell getPdfPCellNoBorder(Paragraph paragraph) {
PdfPCell cell = new PdfPCell();
cell.setRunDirection(PdfWriter.RUN_DIRECTION_RTL);
cell.setPaddingBottom(8);
cell.setBorder(Rectangle.NO_BORDER);
cell.addElement(paragraph);
return cell;
}

Categories