I am using java and iText 5 to produce a PDF. One of my input lines is from a WYSIWYG editor containing html with a base64 image imbedded (i.e., not the link to the image). The WYSIWYG can have zero to many images.
WYSIWYG contains:
This "Description" is processed by my code:
Document document = new Document(PageSize.A4, 72f, 72f, 72f, 72f);
PdfWriter.getInstance(document, resourceImage);
document.open();
String ppDescription = "";
if(activityDtl.getPPDescription() == null || activityDtl.getPPDescription().isEmpty()){
ppDescription = "";
}else{
//Clean the HTML to be correct XHTML
String cleanDesc = cleanHTML(activityDtl.getPPDescription());
InputStream inputStream1 = new ByteArrayInputStream (cleanDesc.getBytes("UTF-8"));
ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
Tidy tidy1 = new Tidy();
tidy1.setXHTML(true);
tidy1.setQuiet(true);
tidy1.setShowWarnings(false);
tidy1.parseDOM(inputStream1, baos1);
ppDescription = baos1.toString();
// System.out.println("ppDescription: " + ppDescription);
}
p6.add(new Chunk("Description: ", smallBold));
if(ppDescription == null || ppDescription.isEmpty()){
p6.add("");
}else{
ElementList list1 = XMLWorkerHelper.parseToElementList(ppDescription, null);
System.out.println("list1: " + list1);
for (Element element : list1) {
p6.add(element);
}
}
cell.addElement(p6);
This is what is received in the input for this field (Description) is:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="generator"
content="HTML Tidy for Java (vers. 2009-12-01), see jtidy.sourceforge.net" />
<title></title>
</head>
<body>
<p>Cooking instructions:</p>
<p><img
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAggAAAC .... H3BNquwQYUAAAAASUVORK5CYII="
alt="" /></p>
<p>Cook the fish.</p>
</body>
</html>
And this is what is in the PDF:
What I would like is to have in the PDF the same as the first image in the WYSIWYG (i.e., the image between the two instruction lines).
Thanks to mkl and this link https://stackoverflow.com/a/20938015/1729265 I have:
Document document = new Document(PageSize.A4, 72f, 72f, 72f, 72f);
PdfWriter.getInstance(document, resourceImage);
document.open();
for (final PPActityDetail activityDtl : activityList) {
PdfPCell cell = new PdfPCell();
cell = new PdfPCell();
Paragraph p6 = new Paragraph("");
p6.add(new Chunk("Description: ", smallBold));
final TagProcessorFactory tagProcessorFactory =
Tags.getHtmlTagProcessorFactory();
tagProcessorFactory.removeProcessor(HTML.Tag.IMG);
tagProcessorFactory.addProcessor(new ImageTagProcessor(), HTML.Tag.IMG);
final CssFilesImpl cssFiles = new CssFilesImpl();
cssFiles.add(XMLWorkerHelper.getInstance().getDefaultCSS());
final StyleAttrCSSResolver cssResolver = new StyleAttrCSSResolver(cssFiles);
final HtmlPipelineContext hpc = new HtmlPipelineContext(new CssAppliersImpl());
hpc.setAcceptUnknown(true).autoBookmark(true).setTagFactory(tagProcessorFactory);
final HtmlPipeline htmlPipeline = new HtmlPipeline(hpc, new PdfWriterPipeline(document, pdfWriter));
final Pipeline<?> pipeline = new CssResolverPipeline(cssResolver, htmlPipeline);
final XMLWorker worker = new XMLWorker(pipeline, true);
final Charset charset = Charset.forName("UTF-8");
final XMLParser xmlParser = new XMLParser(true, worker, charset);
final InputStream is = new ByteArrayInputStream (ppDescription.getBytes("UTF-8"));
xmlParser.parse(is, charset);
cell.addElement(p6);
Now I need to add the output of above (xmlParser.parse(is, charset)) to p6 to include it in the table. I tried:
p6.add(xmlParser.parse(is, charset));
However, that gives me an error message:
The method add(Element) in the type Paragraph is not applicable for the arguments (void)
This is the modified class:
class ImageTagProcessor extends com.itextpdf.tool.xml.html.Image {
private final Logger logger = LoggerFactory.getLogger(getClass());
/*
* (non-Javadoc)
*
* #see com.itextpdf.tool.xml.TagProcessor#endElement(com.itextpdf.tool.xml.Tag, java.util.List, com.itextpdf.text.Document)
*/
public List<Element> end(final WorkerContext ctx, final Tag tag, final List<Element> currentContent) {
final Map<String, String> attributes = tag.getAttributes();
String src = attributes.get(HTML.Attribute.SRC);
List<Element> elements = new ArrayList<Element>(1);
if (null != src && src.length() > 0) {
Image img = null;
if (src.startsWith("data:image/")) {
final String base64Data = src.substring(src.indexOf(",") + 1);
try {
img = Image.getInstance(Base64.decode(base64Data));
} catch (Exception e) {
if (logger.isLogging(Level.ERROR)) {
logger.error(String.format(LocaleMessages.getInstance().getMessage(LocaleMessages.HTML_IMG_RETRIEVE_FAIL), src), e);
}
}
if (img != null) {
try {
final HtmlPipelineContext htmlPipelineContext = getHtmlPipelineContext(ctx);
elements.add(getCssAppliers().apply(new Chunk((com.itextpdf.text.Image) getCssAppliers().apply(img, tag, htmlPipelineContext), 0, 0, true), tag,
htmlPipelineContext));
} catch (NoCustomContextException e) {
throw new RuntimeWorkerException(e);
}
}
}
if (img == null) {
elements = super.end(ctx, tag, currentContent);
}
}
return elements;
}
}
Related
Getting this Error when called from Soap UI "Pdf indirect object belongs to other PDF document". Below is my code for pdf generation :
Main Method from where execution of print starts:
printFolioActivity:
public void printFolioActivity(final FolioActivityTO folio,
final String printerName, final MediaTray mediaTray,
final ServiceContext context) throws ServiceException {
// S-845164 - Changes done to use new itext library
String fileName = " ";
folioActivityDocumentAssemblerTemplate = getTemplateBean();
final String method = "printFolioActivity";
logEntering(method, context);
LOGGER.info("Print Services printFolioActivity using " + "folio : "
+ folio + "printerName : " + printerName + " mediaTray : "
+ mediaTray);
if (!DocumentServiceUtil.isNullOrEmpty(folio)
&& !DocumentServiceUtil.isNullOrEmpty(printerName)
&& !DocumentServiceUtil.isNullOrEmpty(mediaTray)) {
//FolioActivityDocumentAssemblerTemplate template = new FolioActivityDocumentAssemblerTemplate();
// Calling Folio Acitivity Template to generate the pdf
final byte[] document = folioActivityDocumentAssemblerTemplate
.generateFolioActivityDocument(folio);
if (save_gen_pdf) {
fileName = saveLocalService.save(
DocumentServiceConstant.FILE_NAME_FOLIO_ACTIVITY,
document);
}
PrintServiceFacade.getInstance().print(printerName, mediaTray,
document, context);
if(Objects.nonNull(fileName)) {
deleteFile(fileName);
}
logExiting(method);
} else {
LOGGER.error("In printFolioActivity FolioActivityTO is null or Empty OR printerName is null or Empty OR mediaTray is null or Empty");
}
}
generateFolioActivityDocument:
public byte[] generateFolioActivityDocument(FolioActivityTO folioActivityTO) {
LOGGER.info("FolioActivityDocumentAssemblerTemplate generateFolioActivityDocument :: START");
FacilityServiceFacade facilityServiceFacade = getBeanFacade();
currentDate =facilityServiceFacade.getCurrentDate();
OutputStream out = new ByteArrayOutputStream();
PdfWriter writer = new PdfWriter(out);
PdfDocument pdfDoc = new PdfDocument(writer);
Document document = new Document(pdfDoc);
pdfDoc.setDefaultPageSize(FolioActivityTemplate.PAGE_SIZE_LANDSCAPE);
PdfCanvas pdfCanvas = new PdfCanvas(
document.getPdfDocument().addNewPage(FolioActivityTemplate.PAGE_SIZE_LANDSCAPE));
FolioActivityTemplate template = new FolioActivityTemplate(false);
generateFolioActivity(pdfCanvas, document, folioActivityTO, template);
document.close(); // throwing exception here
totalPDFPages = 0;
LOGGER.info("FolioActivityDocumentAssemblerTemplate generateFolioActivityDocument :: END");
return ((ByteArrayOutputStream) out).toByteArray();
}
FolioActivityTemplate class has all the constants declared for styles,fonts.
GenerateFolioActivity:
public void generateFolioActivity(PdfCanvas pdfCanvas, Document document, FolioActivityTO folioActivityTO,
FolioActivityTemplate template) {
LOGGER.info("FolioActivityDocumentAssemblerTemplate generateFolioActivity :: START");
insertTitle(pdfCanvas, folioActivityTO.getGuestInformation(), template.activityTitleStyle,
template.packageInfoStyle);
LOGGER.info("FolioActivityDocumentAssemblerTemplate generateFolioActivity :: END");
}
insertTitle:
protected static void insertTitle(PdfCanvas pdfCanvas, FolioActivityGuestInformation guestInfo, Style titleStyle,
Style packageStyle) {
LOGGER.info("FolioActivityDocumentAssemblerTemplate insertTitle :: START : GuestInfo : " + guestInfo);
drawElementFromStyle(pdfCanvas, FolioActivityTemplate.ACTIVITY_TITLE, titleStyle, 0);
if (guestInfo == null) {
return;
}
drawElementFromStyle(pdfCanvas, guestInfo.getPackageCode() + FolioActivityTemplate.TEXT_PACKAGE_CODE_SEPARATOR
+ guestInfo.getPackageCodeDescription(), packageStyle, 0);
drawElementFromStyle(pdfCanvas,
FolioActivityTemplate.GI_ARRIVAL_TEXT + getPackageEffectiveDate(guestInfo.getPackageDateEffectiveFrom())
+ FolioActivityTemplate.TEXT_DATE_EFFECTIVE_SEPARATOR + FolioActivityTemplate.GI_DEPARTURE_TEXT
+ getPackageEffectiveDate(guestInfo.getPackageDateEffectiveTo()),
packageStyle, 1);
LOGGER.info("FolioActivityDocumentAssemblerTemplate insertTitle :: END");
}
drawElementFromStyle:
protected static void drawElementFromStyle(final PdfCanvas at, final String element, final Style style,
final int offsetCount) {
at.beginText();
float fontSize = style.getFontSize().floatValue();
if (!DocumentServiceUtil.isNullOrEmpty(element) && element.length() > 50 && fontSize > 17) {
fontSize = Float.parseFloat("17.0");
}
at.setFontAndSize(style.getFontType(), fontSize);
if (element != null) {
at.moveText(style.getX().floatValue(),
(style.getY().floatValue() - (offsetCount * style.getCapHeight().floatValue()))).showText(element);
}
at.endText();
}
Exception:
soap:Faultsoap:ServerUnexpected Error occurred : printFolioActivity : Pdf indirect object belongs to other PDF document. Copy object to current pdf document.<ServiceException xmlns:ns2="http://exception.service.com/" EXCEPTIONprintFolioActivity : Pdf indirect object belongs to other PDF document. Copy object to current pdf document.
I'm applying header on a pdf/a document
I embed my font in document, no problem if I have hundreds of page, in pdf created I see just one font embedded in document properties.
Now I worked on adding an header using canvas as seen in many examples.
At first it throw error because it says font must be embedded.
When I embed the font it works, but in document properties I see the font repeated for every page.
So the document kb size grows a lot.
I want font embedded just one time.
Spring boot, but i think it does not matter.
The code of header:
#Slf4j
public class Header implements IEventHandler {
private IFontProviderService fontProviderService;
private String logoPath;
private String header;
private List<String> subHeaders;
public Header(IFontProviderService fontProviderService, String logoPath, String header, List<String> subHeaders) {
this.fontProviderService = fontProviderService;
this.header = header;
this.subHeaders = subHeaders;
this.logoPath = logoPath;
}
#Override
public void handleEvent(Event event) {
try {
PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
PdfDocument pdf = docEvent.getDocument();
PdfPage page = docEvent.getPage();
PdfDictionary pdfObject = page.getPdfObject();
PdfCanvas headerPdfCanvas = new PdfCanvas(page.newContentStreamBefore(), page.getResources(), pdf);
Rectangle headerRect = new Rectangle(35, 740, 495, 96);
Canvas headerCanvas = new Canvas(headerPdfCanvas, headerRect);
headerCanvas.setFont(fontProviderService.getDefaultPdfFont());
headerCanvas.enableAutoTagging(page);
CreateHeaderContent(headerCanvas);
headerCanvas.close();
} catch (Exception e) {
log.error("Error in header settings.", e);
}
}
private void CreateHeaderContent(Canvas canvas) throws MalformedURLException {
Table table = new Table(UnitValue.createPercentArray(new float[] { 80, 20 }));
table.setWidth(UnitValue.createPercentValue(100));
Cell cell1 = new Cell().add(new Paragraph(header).setBold().setFontSize(11).setTextAlignment(TextAlignment.LEFT));
for (int i = 0; i < subHeaders.size(); i++) {
cell1.add(new Paragraph(subHeaders.get(i)).setFontSize(8).setTextAlignment(TextAlignment.LEFT));
}
cell1.setBorder(Border.NO_BORDER);
table.addCell(cell1);
ImageData imageData = ImageDataFactory.create(logoPath);
Image image = new Image(imageData);
image.getAccessibilityProperties().setAlternateDescription("...");
Cell cell2 = new Cell().add(image.setTextAlignment(TextAlignment.RIGHT));
cell2.setBorder(Border.NO_BORDER);
cell2.setVerticalAlignment(VerticalAlignment.MIDDLE);
table.addCell(cell2);
canvas.add(table);
}
}
#Slf4j
#Service
public class FontProviderService implements IFontProviderService {
#Value("${pdf.resources.external.path}")
private String staticResourcesPath;
#Override
public FontProvider getFontProvider() throws IOException {
FontProvider fontProvider = new FontProvider("DMSans-Regular");
fontProvider.addFont(FontProgramFactory.createFont(staticResourcesPath + "pdf/fonts/DMSans-Regular.ttf"));
fontProvider.addFont(FontProgramFactory.createFont(staticResourcesPath + "pdf/fonts/DMSans-Bold.ttf"));
fontProvider.addFont(FontProgramFactory.createFont(staticResourcesPath + "pdf/fonts/DMSans-Italic.ttf"));
fontProvider.addFont(FontProgramFactory.createFont(staticResourcesPath + "pdf/fonts/DMSans-BoldItalic.ttf"));
fontProvider.addFont(FontProgramFactory.createFont(staticResourcesPath + "pdf/fonts/DMSans-Medium.ttf"));
fontProvider.addFont(FontProgramFactory.createFont(staticResourcesPath + "pdf/fonts/DMSans-MediumItalic.ttf"));
return fontProvider;
}
#Override
public PdfFont getDefaultPdfFont() throws IOException {
String defaultFontFamily = this.getFontProvider().getDefaultFontFamily();
Collection<FontInfo> fonts = this.getFontProvider().getFontSet().getFonts();
FontInfo fontInfo = fonts.stream().filter(f -> f.getFontName().equals(defaultFontFamily)).findFirst().get();
PdfFont pdfFont = this.getFontProvider().getPdfFont(fontInfo);
return pdfFont;
}
}
...Spring had something to do with it in some way ...at least in the manner i wrote the service...
Well... Actually in my case the problem was just how I used the service sprint to retrieve the fontProvider and the font.
I'm passing to my handler the entire fontProviderService:
public Header(IFontProviderService fontProviderService, String logoPath, String header, List<String> subHeaders)
and calling every time:
headerCanvas.setFont(fontProviderService.getDefaultPdfFont());
That's equal to create every time a new provider:
FontProvider fontProvider = new FontProvider("DMSans-Regular");
The point to focus is that the FontProvider object and the PdfFont objects must be always the same
So modified my Handler like this:
#Slf4j
public class Header implements IEventHandler {
private FontProvider fontProvider;
private PdfFont pdfFont;
private String logoPath;
private String header;
private List<String> subHeaders;
public Header(FontProvider fontProvider, PdfFont pdfFont, String logoPath, String header, List<String> subHeaders) {
this.fontProvider = fontProvider;
this.pdfFont = pdfFont;
this.header = header;
this.subHeaders = subHeaders;
this.logoPath = logoPath;
}
#Override
public void handleEvent(Event event) {
try {
PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
PdfDocument pdf = docEvent.getDocument();
PdfPage page = docEvent.getPage();
PdfDictionary pdfObject = page.getPdfObject();
PdfCanvas headerPdfCanvas = new PdfCanvas(page.newContentStreamBefore(), page.getResources(), pdf);
Rectangle headerRect = new Rectangle(35, 740, 495, 96);
Canvas headerCanvas = new Canvas(headerPdfCanvas, headerRect);
headerCanvas.setFontProvider(fontProvider);
headerCanvas.setFont(pdfFont);
headerCanvas.enableAutoTagging(page);
CreateHeaderContent(headerCanvas);
headerCanvas.close();
} catch (Exception e) {
log.error("Error in header settings.", e);
}
}
private void CreateHeaderContent(Canvas canvas) throws MalformedURLException {
Table table = new Table(UnitValue.createPercentArray(new float[] { 80, 20 }));
table.setWidth(UnitValue.createPercentValue(100));
Cell cell1 = new Cell().add(new Paragraph(header).setBold().setFontSize(11).setTextAlignment(TextAlignment.LEFT));
for (int i = 0; i < subHeaders.size(); i++) {
cell1.add(new Paragraph(subHeaders.get(i)).setFontSize(8).setTextAlignment(TextAlignment.LEFT));
}
cell1.setBorder(Border.NO_BORDER);
table.addCell(cell1);
ImageData imageData = ImageDataFactory.create(logoPath);
Image image = new Image(imageData);
image.getAccessibilityProperties().setAlternateDescription("...");
Cell cell2 = new Cell().add(image.setTextAlignment(TextAlignment.RIGHT));
cell2.setBorder(Border.NO_BORDER);
cell2.setVerticalAlignment(VerticalAlignment.MIDDLE);
table.addCell(cell2);
canvas.add(table);
}
}
Just for complete, I removed the getDefaultFont from Service and this is an extract of the pdf creator calling the Handler:
FontProvider fontProvider = fontProviderService.getFontProvider();
String defaultFontFamily = fontProvider.getDefaultFontFamily();
Collection<FontInfo> fonts = fontProvider.getFontSet().getFonts();
FontInfo fontInfo = fonts.stream().filter(f -> f.getFontName().equals(defaultFontFamily)).findFirst().get();
PdfFont defaultPdfFont = fontProvider.getPdfFont(fontInfo);
...
converterProperties.setFontProvider(fontProvider);
Header headerHandler = new Header(fontProvider, defaultPdfFont, staticResourcesPath + "....png", "...", "...");
pdf.addEventHandler(PdfDocumentEvent.START_PAGE, headerHandler);
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
I have simple piece of code that writes a PDF sometime this PDF will contain RTL languages like Hebrew or Arabic.
I was able to manipulate the text and mirror it using Bidi (Ibm lib)
But the text is still running in reverse
In English it would be something like:
instead of:
The quick
brown fox
jumps over
the lazy dog
It appears as:
the lazy dog
jumps over
brown fox
The quick
Complete code:
#Test
public void generatePdf() {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-hh.mm.ss");
String dest = "c:\\temp\\" + formatter.format(Calendar.getInstance().getTime()) + ".pdf";
String fontPath = "C:\\Windows\\Fonts\\ARIALUNI.TTF";
FontProgramFactory.registerFont(fontPath, "arialUnicode");
OutputStream pdfFile = null;
Document doc = null;
try {
ByteArrayOutputStream output = new ByteArrayOutputStream();
PdfFont PdfFont = PdfFontFactory.createRegisteredFont("arialUnicode", PdfEncodings.IDENTITY_H, true);
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(output));
pdfDoc.setDefaultPageSize(PageSize.A4);
pdfDoc.addFont(PdfFont);
doc = new Document(pdfDoc);
doc.setBaseDirection(BaseDirection.RIGHT_TO_LEFT);
String txt = "בתשרי נתן הדקל פרי שחום נחמד בחשוון ירד יורה ועל גגי רקד בכסלו נרקיס הופיע בטבת ברד ובשבט חמה הפציעה ליום אחד. 1234 באדר עלה ניחוח מן הפרדסים בניסן הונפו בכוח כל החרמשים";
Bidi bidi = new Bidi();
bidi.setPara(txt, Bidi.RTL, null);
String mirrTxt = bidi.writeReordered(Bidi.DO_MIRRORING);
Paragraph paragraph1 = new Paragraph(mirrTxt)
.setFont(PdfFont)
.setFontSize(9)
.setTextAlignment(TextAlignment.CENTER)
.setHeight(200)
.setWidth(70);
paragraph1.setBorder(new SolidBorder(3));
doc.add(paragraph1);
Paragraph paragraph2 = new Paragraph(txt)
.setFont(PdfFont)
.setFontSize(9)
.setTextAlignment(TextAlignment.CENTER)
.setHeight(200)
.setWidth(70);
paragraph2.setBorder(new SolidBorder(3));
doc.add(paragraph2);
doc.close();
doc.flush();
pdfFile = new FileOutputStream(dest);
pdfFile.write(output.toByteArray());
ProcessBuilder b = new ProcessBuilder("cmd.exe","/C","explorer " + dest);
b.start();
} catch (Exception e) {
e.printStackTrace();
}finally {
try {pdfFile.close();} catch (IOException e) {e.printStackTrace();}
}
}
The only solution that I have found with iText7 and IBM ICU4J without any other third party libraries is to first render the lines and then mirror them one by one. This requires a helper class LineMirroring and it's not precisely the most elegant solution, but will produce the output that you expect.
Lines mirroring class:
public class LineMirroring {
private final PageSize pageSize;
private final String fontName;
private final int fontSize;
public LineMirroring(PageSize pageSize, String fontName, int fontSize) {
this.pageSize = pageSize;
this.fontName = fontName;
this.fontSize = fontSize;
}
public String mirrorParagraph(String input, int height, int width, Border border) {
final StringBuilder mirrored = new StringBuilder();
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
PdfFont font = PdfFontFactory.createRegisteredFont(fontName, PdfEncodings.IDENTITY_H, true);
final PdfWriter writer = new PdfWriter(output);
final PdfDocument pdfDoc = new PdfDocument(writer);
pdfDoc.setDefaultPageSize(pageSize);
pdfDoc.addFont(font);
final Document doc = new Document(pdfDoc);
doc.setBaseDirection(BaseDirection.RIGHT_TO_LEFT);
final LineTrackingParagraph paragraph = new LineTrackingParagraph(input);
paragraph.setFont(font)
.setFontSize(fontSize)
.setTextAlignment(TextAlignment.RIGHT)
.setHeight(height)
.setWidth(width)
.setBorder(border);
LineTrackingParagraphRenderer renderer = new LineTrackingParagraphRenderer(paragraph);
doc.add(paragraph);
Bidi bidi;
for (LineRenderer lr : paragraph.getWrittenLines()) {
bidi = new Bidi(((TextRenderer) lr.getChildRenderers().get(0)).getText().toString(), Bidi.RTL);
mirrored.append(bidi.writeReordered(Bidi.DO_MIRRORING));
}
doc.close();
pdfDoc.close();
writer.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
return mirrored.toString();
}
private class LineTrackingParagraph extends Paragraph {
private List<LineRenderer> lines;
public LineTrackingParagraph(String text) {
super(text);
}
public void addWrittenLines(List<LineRenderer> lines) {
this.lines = lines;
}
public List<LineRenderer> getWrittenLines() {
return lines;
}
#Override
protected IRenderer makeNewRenderer() {
return new LineTrackingParagraphRenderer(this);
}
}
private class LineTrackingParagraphRenderer extends ParagraphRenderer {
public LineTrackingParagraphRenderer(LineTrackingParagraph modelElement) {
super(modelElement);
}
#Override
public void drawChildren(DrawContext drawContext) {
((LineTrackingParagraph)modelElement).addWrittenLines(lines);
super.drawChildren(drawContext);
}
#Override
public IRenderer getNextRenderer() {
return new LineTrackingParagraphRenderer((LineTrackingParagraph) modelElement);
}
}
}
Minimal JUnit Test:
public class Itext7HebrewTest {
#Test
public void generatePdf() {
final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-hh.mm.ss");
final String dest = "F:\\Temp\\" + formatter.format(Calendar.getInstance().getTime()) + ".pdf";
final String fontPath = "C:\\Windows\\Fonts\\ARIALUNI.TTF";
final String fontName = "arialUnicode";
FontProgramFactory.registerFont(fontPath, "arialUnicode");
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
PdfFont arial = PdfFontFactory.createRegisteredFont(fontName, PdfEncodings.IDENTITY_H, true);
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(output));
pdfDoc.setDefaultPageSize(PageSize.A4);
pdfDoc.addFont(arial);
LineMirroring mirroring = new LineMirroring(pdfDoc.getDefaultPageSize(), fontName,9);
Document doc = new Document(pdfDoc);
doc.setBaseDirection(BaseDirection.RIGHT_TO_LEFT);
final String txt = "בתשרי נתן הדקל פרי שחום נחמד בחשוון ירד יורה ועל גגי רקד בכסלו נרקיס הופיע בטבת ברד ובשבט חמה הפציעה ליום אחד. 1234 באדר עלה ניחוח מן הפרדסים בניסן הונפו בכוח כל החרמשים";
final int height = 200;
final int width = 70;
final Border border = new SolidBorder(3);
Paragraph paragraph1 = new Paragraph(mirroring.mirrorParagraph(txt, height, width, border));
paragraph1.setFont(arial)
.setFontSize(9)
.setTextAlignment(TextAlignment.RIGHT)
.setHeight(height)
.setWidth(width)
.setBorder(border);
doc.add(paragraph1);
doc.close();
doc.flush();
try (FileOutputStream pdfFile = new FileOutputStream(dest)) {
pdfFile.write(output.toByteArray());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
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.