I use the following Setup to create PDF-Documents using iText (commercial license, iText Version: 5.4.5):
HTML Templates are generated with jmustache, during that step internationalization is performed, i.e. documents can contain european languages but also Japanese and Chinese (also a mix of both, as some text parts might remain English)
The final HTML is rendered using XMLWorker like so:
final float marginPt = 28.35f;//1cm == 28.35pt
final Document document = new Document(PageSize.A4, marginPt, marginPt, marginPt, 0);
final PdfWriter writer = PdfWriter.getInstance(document, output);
//we write multiple documents to a ZipOutputStream, so we close the output stream later
writer.setCloseStream(false);
document.open();
final HtmlPipelineContext htmlContext = new HtmlPipelineContext(null);
htmlContext.setImageProvider(new DynamicImageProvider(privateStorageFolder));
final CSSResolver cssResolver = XMLWorkerHelper.getInstance().getDefaultCssResolver(true);
final Pipeline<?> pipeline = new CssResolverPipeline(cssResolver,
new HtmlPipeline(htmlContext, new PdfWriterPipeline(document, writer)));
final XMLWorker worker = new XMLWorker(pipeline, true);
final XMLParser p = new XMLParser(worker);
p.parse(new StringReader(input));
document.close();
Everything works fine for European characters (Latin letters, umlauts, accents etc.). For CJK characters (e.g. Japanese) however, the resulting text in the PDF is not displayed, also, there is no message prompting me to install additional fonts.
I tried adding itext-asian.jar from extrajars-2.3.zip to the android libraries (http://sourceforge.net/projects/itext/files/extrajars/) but this did not help.
I am looking for a solution to properly add CJK text to the resulting PDF by either:
1.) Using the PDF CJK feature (i.e. end-user needs a Reader with CJK fonts installed)
2.) Embed a font into the PDF that contains both latin & CJK chars.
Solution 1 would be preferable, but a fix using approach 2 would also be much appreciated.
Solution 1 should work with the itext-asian.jar, but it does not in my setup (does that not work for the Android itext version for some reason?)
For Solution 2 I would need to find a way to add multiple fonts to XMLWorker in Android, as most fonts supporting multiple scripts are split up in different ttf files (e.g. the google noto fonts https://www.google.com/get/noto/)
After a lot of trial-and-error I came up with the following solution which works for Android and Desktop:
1.) Add the appropriate itext-asian.jar
for your itext version to your classpath
2.) Implement a custom FontFactory
like so:
public class MyFontFactory extends FontFactoryImp {
@Override
public Font getFont(final String fontname, final String encoding, final boolean embedded, final float size, final int style, final BaseColor color, final boolean cached) {
if ("CJK".equals(fontname)){
//these parameters were found out via trial-and-error, it is the asian font that looked best for our needs
//look into the itext-asian.jar for alternatives
return FontFactory.getFont("HeiseiKakuGo-W5","UniJIS-UCS2-H", BaseFont.NOT_EMBEDDED, size, style, color, cached);
} else {
return super.getFont(fontname, encoding, embedded, size, style, color, cached);
}
}
3.) Add the font factory to your XMLWorker
chain like this:
final MyFontFactory fontFactory = new MyFontFactory();
//this sets the custom font factory for everything *but* the XMLWorker
//(now that's amazing API design -.- )
FontFactory.setFontImp(fontFactory);
//this is the trick to get our fontFactory into the XmlWorker:
//build the processing pipeline manually and inject the factory along the way
final HtmlPipelineContext htmlContext = new HtmlPipelineContext(new CssAppliersImpl(fontFactory));
final CSSResolver cssResolver = XMLWorkerHelper.getInstance().getDefaultCssResolver(true);
final Pipeline<?> pipeline = new CssResolverPipeline(cssResolver, new HtmlPipeline(htmlContext, new PdfWriterPipeline(document, writer)));
final XMLWorker worker = new XMLWorker(pipeline, true);
final XMLParser p = new XMLParser(worker);
p.parse(new StringReader(input));
Now you can use the pseudo font "CJK" in your HTML/CSS and you get asian characters without embedding the whole font:
* {
font-family:CJK;
font-size: 8pt;
}
If you want to embed a font, just build your fonts differently in your FontFactory, i.e. like so:
return FontFactory.getFont("/system/fonts/DroidSans.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, size, style, color, cached);
Of course you have to make sure that you have the ttf files available, as soon as you start embedding hard-coded font files your code is not very portable anymore.