package com.tom_roush.pdfbox.rendering; import android.graphics.Path; import android.util.Log; import com.tom_roush.fontbox.ttf.HeaderTable; import com.tom_roush.fontbox.ttf.TrueTypeFont; import com.tom_roush.pdfbox.pdmodel.font.PDCIDFontType2; import com.tom_roush.pdfbox.pdmodel.font.PDFont; import com.tom_roush.pdfbox.pdmodel.font.PDTrueTypeFont; import com.tom_roush.pdfbox.pdmodel.font.PDType0Font; import com.tom_roush.pdfbox.pdmodel.font.PDVectorFont; import com.tom_roush.pdfbox.util.awt.AffineTransform; import java.io.IOException; import java.util.HashMap; import java.util.Map; /** * This class provides a glyph to GeneralPath conversion for TrueType and OpenType fonts. */ final class TTFGlyph2D implements Glyph2D { private final PDFont font; private final TrueTypeFont ttf; private PDVectorFont vectorFont; private float scale = 1.0f; private boolean hasScaling; private final Map<Integer, Path> glyphs = new HashMap<Integer, Path>(); private final boolean isCIDFont; /** * Constructor. * * @param ttfFont TrueType font */ TTFGlyph2D(PDTrueTypeFont ttfFont) throws IOException { this(ttfFont.getTrueTypeFont(), ttfFont, false); vectorFont = ttfFont; } /** * Constructor. * * @param type0Font Type0 font, with CIDFontType2 descendant */ TTFGlyph2D(PDType0Font type0Font) throws IOException { this(((PDCIDFontType2)type0Font.getDescendantFont()).getTrueTypeFont(), type0Font, true); vectorFont = type0Font; } private TTFGlyph2D(TrueTypeFont ttf, PDFont font, boolean isCIDFont) throws IOException { this.font = font; this.ttf = ttf; this.isCIDFont = isCIDFont; // get units per em, which is used as scaling factor HeaderTable header = this.ttf.getHeader(); if (header != null && header.getUnitsPerEm() != 1000) { // in most case the scaling factor is set to 1.0f // due to the fact that units per em is set to 1000 scale = 1000f / header.getUnitsPerEm(); hasScaling = true; } } @Override public Path getPathForCharacterCode(int code) throws IOException { int gid = getGIDForCharacterCode(code); return getPathForGID(gid, code); } // Try to map the given code to the corresponding glyph-ID private int getGIDForCharacterCode(int code) throws IOException { if (isCIDFont) { return ((PDType0Font)font).codeToGID(code); } else { return ((PDTrueTypeFont)font).codeToGID(code); } } /** * Returns the path describing the glyph for the given glyphId. * * @param gid the GID * @param code the character code * * @return the GeneralPath for the given glyphId */ public Path getPathForGID(int gid, int code) throws IOException { Path glyphPath; if (glyphs.containsKey(gid)) { glyphPath = glyphs.get(gid); } else { if (gid == 0 || gid >= ttf.getMaximumProfile().getNumGlyphs()) { if (isCIDFont) { int cid = ((PDType0Font) font).codeToCID(code); String cidHex = String.format("%04x", cid); Log.w("PdfBox-Android", "No glyph for " + code + " (CID " + cidHex + ") in font " + font.getName()); } else { Log.w("PdfBox-Android", "No glyph for " + code + " in font " + font.getName()); } } Path glyph = vectorFont.getPath(code); // Acrobat only draws GID 0 for embedded or "Standard 14" fonts, see PDFBOX-2372 if (gid == 0 && !font.isEmbedded() && !font.isStandard14()) { glyph = null; } if (glyph == null) { // empty glyph (e.g. space, newline) glyphPath = new Path(); glyphs.put(gid, glyphPath); } else { glyphPath = glyph; if (hasScaling) { AffineTransform atScale = AffineTransform.getScaleInstance(scale, scale); glyphPath.transform(atScale.toMatrix()); } glyphs.put(gid, glyphPath); } } return glyphPath != null ? new Path(glyphPath) : null; // todo: expensive } @Override public void dispose() { glyphs.clear(); } }