protected static String getKerningPairAsSVG(KerningPair kp, PostTable post) { String leftGlyphName = post.getGlyphName(kp.getLeft()); String rightGlyphName = post.getGlyphName(kp.getRight()); StringBuffer sb = new StringBuffer(); // sb.append("<hkern "); sb.append(XML_OPEN_TAG_START).append(SVG_HKERN_TAG).append(XML_SPACE); if (leftGlyphName == null) { sb.append(SVG_U1_ATTRIBUTE).append(XML_EQUAL_QUOT); sb.append(kp.getLeft()); } else { // sb.append("g1=\""); sb.append(SVG_G1_ATTRIBUTE).append(XML_EQUAL_QUOT); sb.append(leftGlyphName); } // sb.append("\" "); sb.append(XML_CHAR_QUOT).append(XML_SPACE); if (rightGlyphName == null) { // sb.append("u2=\""); sb.append(SVG_U2_ATTRIBUTE).append(XML_EQUAL_QUOT); sb.append(kp.getRight()); } else { // sb.append("g2=\""); sb.append(SVG_G2_ATTRIBUTE).append(XML_EQUAL_QUOT); sb.append(rightGlyphName); } // sb.append("\" k=\""); sb.append(XML_CHAR_QUOT).append(XML_SPACE).append(SVG_K_ATTRIBUTE).append(XML_EQUAL_QUOT); // SVG kerning values are inverted from TrueType's. sb.append(-kp.getValue()); // sb.append("\"/>"); sb.append(XML_CHAR_QUOT).append(XML_OPEN_TAG_END_NO_CHILDREN); return sb.toString(); }
/** * Returns a <font>...</font> block, defining the specified font. * * @param font The TrueType font to be converted to SVG * @param id An XML id attribute for the font element * @param first The first character in the output range * @param last The last character in the output range * @param forceAscii Force the use of the ASCII character map */ protected static void writeFontAsSVGFragment( PrintStream ps, Font font, String id, int first, int last, boolean autoRange, boolean forceAscii) throws Exception { // StringBuffer sb = new StringBuffer(); // int horiz_advance_x = font.getHmtxTable().getAdvanceWidth( // font.getHheaTable().getNumberOfHMetrics() - 1); int horiz_advance_x = font.getOS2Table().getAvgCharWidth(); ps.print(XML_OPEN_TAG_START); ps.print(SVG_FONT_TAG); ps.print(XML_SPACE); // ps.print("<font "); if (id != null) { ps.print(SVG_ID_ATTRIBUTE); ps.print(XML_EQUAL_QUOT); // ps.print("id=\""); ps.print(id); ps.print(XML_CHAR_QUOT); ps.print(XML_SPACE); // ps.print("\" "); } ps.print(SVG_HORIZ_ADV_X_ATTRIBUTE); ps.print(XML_EQUAL_QUOT); // ps.print("horiz-adv-x=\""); ps.print(horiz_advance_x); ps.print(XML_CHAR_QUOT); ps.print(XML_OPEN_TAG_END_CHILDREN); // ps.println("\">"); ps.print(getSVGFontFaceElement(font)); // Decide upon a cmap table to use for our character to glyph look-up CmapFormat cmapFmt = null; if (forceAscii) { // We've been asked to use the ASCII/Macintosh cmap format cmapFmt = font.getCmapTable().getCmapFormat(Table.platformMacintosh, Table.encodingRoman); } else { // The default behaviour is to use the Unicode cmap encoding cmapFmt = font.getCmapTable().getCmapFormat(Table.platformMicrosoft, Table.encodingUGL); if (cmapFmt == null) { // This might be a symbol font, so we'll look for an "undefined" encoding cmapFmt = font.getCmapTable().getCmapFormat(Table.platformMicrosoft, Table.encodingUndefined); } } if (cmapFmt == null) { throw new Exception("Cannot find a suitable cmap table"); } // If this font includes arabic script, we want to specify // substitutions for initial, medial, terminal & isolated // cases. GsubTable gsub = (GsubTable) font.getTable(Table.GSUB); SingleSubst initialSubst = null; SingleSubst medialSubst = null; SingleSubst terminalSubst = null; if (gsub != null) { Script s = gsub.getScriptList().findScript(SCRIPT_TAG_ARAB); if (s != null) { LangSys ls = s.getDefaultLangSys(); if (ls != null) { Feature init = gsub.getFeatureList().findFeature(ls, FEATURE_TAG_INIT); Feature medi = gsub.getFeatureList().findFeature(ls, FEATURE_TAG_MEDI); Feature fina = gsub.getFeatureList().findFeature(ls, FEATURE_TAG_FINA); if (init != null) { initialSubst = (SingleSubst) gsub.getLookupList().getLookup(init, 0).getSubtable(0); } if (medi != null) { medialSubst = (SingleSubst) gsub.getLookupList().getLookup(medi, 0).getSubtable(0); } if (fina != null) { terminalSubst = (SingleSubst) gsub.getLookupList().getLookup(fina, 0).getSubtable(0); } } } } // Include the missing glyph ps.println( getGlyphAsSVG( font, font.getGlyph(0), 0, horiz_advance_x, initialSubst, medialSubst, terminalSubst, "")); try { if (first == -1) { if (!autoRange) first = DEFAULT_FIRST; else first = cmapFmt.getFirst(); } if (last == -1) { if (!autoRange) last = DEFAULT_LAST; else last = cmapFmt.getLast(); } // Include our requested range Set glyphSet = new HashSet(); for (int i = first; i <= last; i++) { int glyphIndex = cmapFmt.mapCharCode(i); // ps.println(String.valueOf(i) + " -> " + String.valueOf(glyphIndex)); // if (font.getGlyphs()[glyphIndex] != null) // sb.append(font.getGlyphs()[glyphIndex].toString() + "\n"); if (glyphIndex > 0) { // add glyph ID to set so we can filter later glyphSet.add(glyphIndex); ps.println( getGlyphAsSVG( font, font.getGlyph(glyphIndex), glyphIndex, horiz_advance_x, initialSubst, medialSubst, terminalSubst, (32 <= i && i <= 127) ? encodeEntities(String.valueOf((char) i)) : XML_CHAR_REF_PREFIX + Integer.toHexString(i) + XML_CHAR_REF_SUFFIX)); } } // Output kerning pairs from the requested range KernTable kern = (KernTable) font.getTable(Table.kern); if (kern != null) { KernSubtable kst = kern.getSubtable(0); PostTable post = (PostTable) font.getTable(Table.post); for (int i = 0; i < kst.getKerningPairCount(); i++) { KerningPair kpair = kst.getKerningPair(i); // check if left and right are both in our glyph set if (glyphSet.contains(kpair.getLeft()) && glyphSet.contains(kpair.getRight())) { ps.println(getKerningPairAsSVG(kpair, post)); } } } } catch (Exception e) { System.err.println(e.getMessage()); } ps.print(XML_CLOSE_TAG_START); ps.print(SVG_FONT_TAG); ps.println(XML_CLOSE_TAG_END); // ps.println("</font>"); }