Skip to content

Commit

Permalink
Merge pull request #89 from rototor/pdf-fonts-handle-collections
Browse files Browse the repository at this point in the history
PDF Fonts: Handle TrueTypeCollections
  • Loading branch information
danfickle authored Apr 23, 2017
2 parents 46c4865 + 525728a commit 37033fe
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 81 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ public class Java2DRendererBuilder {
private short _pagingMode = Layer.PAGED_MODE_PRINT;
private FSObjectDrawerFactory _objectDrawerFactory;

public static enum TextDirection { RTL, LTR; }
public static enum PageSizeUnits { MM, INCHES }
public static enum FontStyle { NORMAL, ITALIC, OBLIQUE }
public enum TextDirection { RTL, LTR; }
public enum PageSizeUnits { MM, INCHES }
public enum FontStyle { NORMAL, ITALIC, OBLIQUE }

private static class AddedFont {
private final FSSupplier<InputStream> supplier;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,6 @@
*/
package com.openhtmltopdf.pdfboxout;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDFontDescriptor;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.font.PDType1Font;

import com.openhtmltopdf.css.constants.CSSName;
import com.openhtmltopdf.css.constants.IdentValue;
import com.openhtmltopdf.css.sheet.FontFaceRule;
Expand All @@ -36,16 +30,30 @@
import com.openhtmltopdf.layout.SharedContext;
import com.openhtmltopdf.outputdevice.helper.FontFaceFontSupplier;
import com.openhtmltopdf.outputdevice.helper.FontFamily;
import com.openhtmltopdf.outputdevice.helper.FontFileFontSupplier;
import com.openhtmltopdf.outputdevice.helper.FontResolverHelper;
import com.openhtmltopdf.outputdevice.helper.MinimalFontDescription;
import com.openhtmltopdf.render.FSFont;
import com.openhtmltopdf.util.XRLog;
import org.apache.fontbox.ttf.TrueTypeCollection;
import org.apache.fontbox.ttf.TrueTypeCollection.TrueTypeFontProcessor;
import org.apache.fontbox.ttf.TrueTypeFont;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDFontDescriptor;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.font.PDType1Font;

import java.io.*;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.logging.Level;

/**
* This class handles all font resolving for the PDF generation. Please note that at the moment only subsetting/embedding
* of fonts work. So you should always set embedded/subset=true for now.
*/
public class PdfBoxFontResolver implements FontResolver {
private Map<String, FontFamily<FontDescription>> _fontFamilies = createInitialFontMap();
private Map<String, FontDescription> _fontCache = new HashMap<String, FontDescription>();
Expand Down Expand Up @@ -123,33 +131,46 @@ public void importFontFaces(List<FontFaceRule> fontFaces) {
}
}

/**
* Add all fonts in the given directory
*/
public void addFontDirectory(String dir, boolean embedded) throws IOException {
File f = new File(dir);
if (f.isDirectory()) {
File[] files = f.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
String lower = name.toLowerCase(Locale.US);
return lower.endsWith(".ttf");
return lower.endsWith(".ttf") || lower.endsWith(".ttc");
}
});
for (int i = 0; i < files.length; i++) {
addFont(new FontFileFontSupplier(files[i].getAbsolutePath()), files[i].getName(),
400, IdentValue.NORMAL, true);

assert files != null;
for (File file : files) {
addFont(file, file.getName(), 400, IdentValue.NORMAL, embedded);
}
}
}

public void addFont(FSSupplier<InputStream> supplier, String fontFamilyNameOverride,
Integer fontWeightOverride, IdentValue fontStyleOverride, boolean subset) {
/**
* Add a font using a FontBox TrueTypeFont.
*/
private void addFont(TrueTypeFont trueTypeFont, String fontFamilyNameOverride,
Integer fontWeightOverride, IdentValue fontStyleOverride, boolean subset) throws IOException {


PDFont font = PDType0Font.load(_doc, trueTypeFont, subset);

addFont(font, fontFamilyNameOverride, fontWeightOverride, fontStyleOverride, subset);
}

private void addFont(PDFont font, String fontFamilyNameOverride, Integer fontWeightOverride, IdentValue fontStyleOverride, boolean subset) {
FontFamily<FontDescription> fontFamily = getFontFamily(fontFamilyNameOverride);

FontDescription descr = new FontDescription(
_doc,
supplier,
fontWeightOverride != null ? fontWeightOverride : 400,
fontStyleOverride != null ? fontStyleOverride : IdentValue.NORMAL);
font,
fontStyleOverride != null ? fontStyleOverride : IdentValue.NORMAL,
fontWeightOverride != null ? fontWeightOverride : 400);

if (!subset) {
if (descr.realizeFont(subset))
fontFamily.addFontDescription(descr);
Expand All @@ -158,6 +179,86 @@ public void addFont(FSSupplier<InputStream> supplier, String fontFamilyNameOverr
}
}

/**
* Add fonts using a FontBox TrueTypeCollection.
*/
private void addFontCollection(TrueTypeCollection collection, final String fontFamilyNameOverride,
final Integer fontWeightOverride, final IdentValue fontStyleOverride, final boolean subset)
throws IOException {
collection.processAllFonts(new TrueTypeFontProcessor() {
@Override
public void process(TrueTypeFont ttf) throws IOException {
addFont(ttf, fontFamilyNameOverride, fontWeightOverride, fontStyleOverride, subset);
}
});
}

/**
* Add fonts using a .ttc TrueTypeCollection
*/
public void addFontCollection(FSSupplier<InputStream> supplier, final String fontFamilyNameOverride,
final Integer fontWeightOverride, final IdentValue fontStyleOverride, final boolean subset)
throws IOException {
InputStream inputStream = supplier.supply();
try {
TrueTypeCollection collection = new TrueTypeCollection(inputStream);
addFontCollection(collection, fontFamilyNameOverride, fontWeightOverride, fontStyleOverride, subset);
} finally {
inputStream.close();
}
}

/**
* Add fonts using a .ttc TrueTypeCollection
*/
public void addFontCollection(File file, final String fontFamilyNameOverride,
final Integer fontWeightOverride, final IdentValue fontStyleOverride, final boolean subset)
throws IOException {
TrueTypeCollection collection = new TrueTypeCollection(file);
addFontCollection(collection, fontFamilyNameOverride, fontWeightOverride, fontStyleOverride, subset);
}

/**
* Add a font using a existing file. If the file is a TrueTypeCollection, it
* will be handled as such.
*/
public void addFont(File fontFile, final String fontFamilyNameOverride, final Integer fontWeightOverride,
final IdentValue fontStyleOverride, final boolean subset) throws IOException {
/*
* Specialcase for TrueTypeCollections
*/
if (fontFile.getName().toLowerCase(Locale.US).endsWith(".ttc")) {
addFontCollection(fontFile, fontFamilyNameOverride, fontWeightOverride, fontStyleOverride, subset);
return;
}

/*
* We load the font using the file.
*/
addFont(PDType0Font.load(_doc, fontFile), fontFamilyNameOverride, fontWeightOverride, fontStyleOverride, subset);
}


/**
* Add a font using a InputStream. The given file must be a TrueType Font
* (.ttf). If you know the underlying stream is a .ttc file you should use
* {@link #addFontCollection(FSSupplier, String, Integer, IdentValue, boolean)}
*/
public void addFont(FSSupplier<InputStream> supplier, String fontFamilyNameOverride, Integer fontWeightOverride,
IdentValue fontStyleOverride, boolean subset) {
FontFamily<FontDescription> fontFamily = getFontFamily(fontFamilyNameOverride);

FontDescription descr = new FontDescription(_doc, supplier, fontWeightOverride != null ? fontWeightOverride
: 400, fontStyleOverride != null ? fontStyleOverride : IdentValue.NORMAL);

if (!subset) {
if (descr.realizeFont(subset))
fontFamily.addFontDescription(descr);
} else {
fontFamily.addFontDescription(descr);
}
}

private void addFontFaceFont(
String fontFamilyNameOverride, IdentValue fontWeightOverride, IdentValue fontStyleOverride,
String uri, boolean subset) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,19 @@
import com.openhtmltopdf.bidi.BidiReorderer;
import com.openhtmltopdf.bidi.BidiSplitterFactory;
import com.openhtmltopdf.css.constants.IdentValue;
import com.openhtmltopdf.extend.FSCache;
import com.openhtmltopdf.extend.FSObjectDrawerFactory;
import com.openhtmltopdf.extend.FSSupplier;
import com.openhtmltopdf.extend.FSTextBreaker;
import com.openhtmltopdf.extend.FSTextTransformer;
import com.openhtmltopdf.extend.FSUriResolver;
import com.openhtmltopdf.extend.HttpStreamFactory;
import com.openhtmltopdf.extend.SVGDrawer;
import com.openhtmltopdf.extend.*;
import com.openhtmltopdf.outputdevice.helper.BaseDocument;
import com.openhtmltopdf.outputdevice.helper.PageDimensions;
import com.openhtmltopdf.outputdevice.helper.UnicodeImplementation;
import com.openhtmltopdf.util.XRLog;
import org.w3c.dom.Document;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;

public class PdfRendererBuilder
{
Expand Down Expand Up @@ -61,21 +56,23 @@ public static enum FontStyle { NORMAL, ITALIC, OBLIQUE }

private static class AddedFont {
private final FSSupplier<InputStream> supplier;
private final File fontFile;
private final Integer weight;
private final String family;
private final boolean subset;
private final FontStyle style;

private AddedFont(FSSupplier<InputStream> supplier, Integer weight, String family, boolean subset, FontStyle style) {
private AddedFont(FSSupplier<InputStream> supplier, File fontFile, Integer weight, String family, boolean subset, FontStyle style) {
this.supplier = supplier;
this.fontFile = fontFile;
this.weight = weight;
this.family = family;
this.subset = subset;
this.style = style;
}
}

private List<AddedFont> _fonts = new ArrayList<AddedFont>();
private final List<AddedFont> _fonts = new ArrayList<AddedFont>();

/**
* Run the XHTML/XML to PDF conversion and output to an output stream set by toStream.
Expand All @@ -85,25 +82,6 @@ public void run() throws Exception {
PdfBoxRenderer renderer = null;
try {
renderer = this.buildPdfRenderer();

if (!_fonts.isEmpty()) {
PdfBoxFontResolver resolver = renderer.getFontResolver();

for (AddedFont font : _fonts) {
IdentValue fontStyle = null;

if (font.style == FontStyle.NORMAL) {
fontStyle = IdentValue.NORMAL;
} else if (font.style == FontStyle.ITALIC) {
fontStyle = IdentValue.ITALIC;
} else if (font.style == FontStyle.OBLIQUE) {
fontStyle = IdentValue.OBLIQUE;
}

resolver.addFont(font.supplier, font.family, font.weight, fontStyle, font.subset);
}
}

renderer.layout();
renderer.createPDF();
} finally {
Expand All @@ -124,8 +102,37 @@ public PdfBoxRenderer buildPdfRenderer() {
PageDimensions pageSize = new PageDimensions(_pageWidth, _pageHeight, _isPageSizeInches);

BaseDocument doc = new BaseDocument(_baseUri, _html, _document, _file, _uri);

return new PdfBoxRenderer(doc, unicode, _httpStreamFactory, _os, _resolver, _cache, _svgImpl, pageSize, _pdfVersion, _replacementText, _testMode, _objectDrawerFactory);

PdfBoxRenderer renderer = new PdfBoxRenderer(doc, unicode, _httpStreamFactory, _os, _resolver, _cache, _svgImpl, pageSize, _pdfVersion, _replacementText, _testMode, _objectDrawerFactory);

/*
* Register all Fonts
*/
PdfBoxFontResolver resolver = renderer.getFontResolver();
for (AddedFont font : _fonts) {
IdentValue fontStyle = null;

if (font.style == FontStyle.NORMAL) {
fontStyle = IdentValue.NORMAL;
} else if (font.style == FontStyle.ITALIC) {
fontStyle = IdentValue.ITALIC;
} else if (font.style == FontStyle.OBLIQUE) {
fontStyle = IdentValue.OBLIQUE;
}

if( font.supplier != null) {
resolver.addFont(font.supplier, font.family, font.weight, fontStyle, font.subset);
}
else {
try {
resolver.addFont(font.fontFile, font.family, font.weight, fontStyle, font.subset);
} catch (Exception e) {
XRLog.init(Level.WARNING, "Font " + font.fontFile + " could not be loaded", e);
}
}
}

return renderer;
}

/**
Expand Down Expand Up @@ -382,7 +389,7 @@ public PdfRendererBuilder useUnicodeToTitleTransformer(FSTextTransformer tr) {
* @return
*/
public PdfRendererBuilder useFont(FSSupplier<InputStream> supplier, String fontFamily, Integer fontWeight, FontStyle fontStyle, boolean subset) {
this._fonts.add(new AddedFont(supplier, fontWeight, fontFamily, subset, fontStyle));
this._fonts.add(new AddedFont(supplier, null, fontWeight, fontFamily, subset, fontStyle));
return this;
}

Expand All @@ -396,6 +403,25 @@ public PdfRendererBuilder useFont(FSSupplier<InputStream> supplier, String fontF
return this.useFont(supplier, fontFamily, 400, FontStyle.NORMAL, true);
}

/**
* Like {@link #useFont(FSSupplier, String, Integer, FontStyle, boolean)}, but allows to supply a font file. If the font file
* is a .ttc file it is handled as TrueTypeCollection. If you have the font in file form you should use this API.
*/
public PdfRendererBuilder useFont(File fontFile, String fontFamily, Integer fontWeight, FontStyle fontStyle, boolean subset) {
this._fonts.add(new AddedFont(null, fontFile, fontWeight, fontFamily, subset, fontStyle));
return this;
}

/**
* Simpler overload for {@link #useFont(File, String, Integer, FontStyle, boolean)}
* @param fontFile
* @param fontFamily
* @return
*/
public PdfRendererBuilder useFont(File fontFile, String fontFamily) {
return this.useFont(fontFile, fontFamily, 400, FontStyle.NORMAL, true);
}

/**
* Set a factory for &lt;object&gt; drawers
* @param objectDrawerFactory Object Drawer Factory
Expand Down

0 comments on commit 37033fe

Please sign in to comment.