/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* $Id$ */ package org.apache.fop.render.ps; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.util.HashMap; import java.util.HashSet; import java.util.Locale; import java.util.Map; import java.util.Set; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.xmlgraphics.fonts.Glyphs; import org.apache.xmlgraphics.ps.DSCConstants; import org.apache.xmlgraphics.ps.PSGenerator; import org.apache.xmlgraphics.ps.PSResource; import org.apache.xmlgraphics.ps.dsc.ResourceTracker; import org.apache.fop.fonts.Base14Font; import org.apache.fop.fonts.CIDFontType; import org.apache.fop.fonts.CIDSubset; import org.apache.fop.fonts.CMapSegment; import org.apache.fop.fonts.CustomFont; import org.apache.fop.fonts.EmbeddingMode; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontType; import org.apache.fop.fonts.LazyFont; import org.apache.fop.fonts.MultiByteFont; import org.apache.fop.fonts.SingleByteEncoding; import org.apache.fop.fonts.SingleByteFont; import org.apache.fop.fonts.Typeface; import org.apache.fop.fonts.truetype.FontFileReader; import org.apache.fop.fonts.truetype.TTFFile; import org.apache.fop.fonts.truetype.TTFFile.PostScriptVersion; import org.apache.fop.fonts.truetype.TTFOutputStream; import org.apache.fop.fonts.truetype.TTFSubSetFile; import org.apache.fop.render.ps.fonts.PSTTFOutputStream; import org.apache.fop.util.HexEncoder; /** * Utility code for font handling in PostScript. */ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { /** logging instance */ protected static final Log log = LogFactory.getLog(PSFontUtils.class); /** * Generates the PostScript code for the font dictionary. This method should only be * used if no "resource optimization" is performed, i.e. when the fonts are not embedded * in a second pass. * @param gen PostScript generator to use for output * @param fontInfo available fonts * @return a Map of PSResource instances representing all defined fonts (key: font key) * @throws IOException in case of an I/O problem */ public static Map writeFontDict(PSGenerator gen, FontInfo fontInfo) throws IOException { return writeFontDict(gen, fontInfo, null); } /** * Generates the PostScript code for the font dictionary. This method should only be * used if no "resource optimization" is performed, i.e. when the fonts are not embedded * in a second pass. * @param gen PostScript generator to use for output * @param fontInfo available fonts * @param eventProducer to report events * @return a Map of PSResource instances representing all defined fonts (key: font key) * @throws IOException in case of an I/O problem */ public static Map writeFontDict(PSGenerator gen, FontInfo fontInfo, PSEventProducer eventProducer) throws IOException { return writeFontDict(gen, fontInfo, fontInfo.getFonts(), true, eventProducer); } /** * Generates the PostScript code for the font dictionary. This method assumes all used * fonts and characters are known, i.e. when PostScript is generated with resource * optimization turned on. * @param gen PostScript generator to use for output * @param fontInfo available fonts * @param fonts the set of fonts to work with * @param eventProducer the event producer * @return a Map of PSResource instances representing all defined fonts (key: font key) * @throws IOException in case of an I/O problem */ public static Map writeFontDict(PSGenerator gen, FontInfo fontInfo, Map fonts, PSEventProducer eventProducer) throws IOException { return writeFontDict(gen, fontInfo, fonts, false, eventProducer); } /** * Generates the PostScript code for the font dictionary. * @param gen PostScript generator to use for output * @param fontInfo available fonts * @param fonts the set of fonts to work with * @param encodeAllCharacters true if all characters shall be encoded using additional, * generated encodings. * @return a Map of PSResource instances representing all defined fonts (key: font key) * @throws IOException in case of an I/O problem */ private static Map writeFontDict(PSGenerator gen, FontInfo fontInfo, Map fonts, boolean encodeAllCharacters, PSEventProducer eventProducer) throws IOException { gen.commentln("%FOPBeginFontDict"); Map fontResources = new HashMap(); for (String key : fonts.keySet()) { Typeface tf = getTypeFace(fontInfo, fonts, key); PSResource fontRes = new PSResource(PSResource.TYPE_FONT, tf.getEmbedFontName()); PSFontResource fontResource = embedFont(gen, tf, fontRes, eventProducer); fontResources.put(key, fontResource); if (tf instanceof SingleByteFont) { SingleByteFont sbf = (SingleByteFont)tf; if (encodeAllCharacters) { sbf.encodeAllUnencodedCharacters(); } for (int i = 0, c = sbf.getAdditionalEncodingCount(); i < c; i++) { SingleByteEncoding encoding = sbf.getAdditionalEncoding(i); defineEncoding(gen, encoding); String postFix = "_" + (i + 1); PSResource derivedFontRes; if (tf.getFontType() == FontType.TRUETYPE && sbf.getTrueTypePostScriptVersion() != PostScriptVersion.V2) { derivedFontRes = defineDerivedTrueTypeFont(gen, eventProducer, tf.getEmbedFontName(), tf.getEmbedFontName() + postFix, encoding, sbf.getCMap()); } else { derivedFontRes = defineDerivedFont(gen, tf.getEmbedFontName(), tf.getEmbedFontName() + postFix, encoding.getName()); } fontResources.put(key + postFix, PSFontResource.createFontResource(derivedFontRes)); } } } gen.commentln("%FOPEndFontDict"); reencodeFonts(gen, fonts); return fontResources; } private static void reencodeFonts(PSGenerator gen, Map fonts) throws IOException { ResourceTracker tracker = gen.getResourceTracker(); if (!tracker.isResourceSupplied(WINANSI_ENCODING_RESOURCE)) { //Only out Base 14 fonts still use that defineWinAnsiEncoding(gen); } gen.commentln("%FOPBeginFontReencode"); //Rewrite font encodings for (String key : fonts.keySet()) { Typeface tf = fonts.get(key); if (tf instanceof LazyFont) { tf = ((LazyFont)tf).getRealFont(); if (tf == null) { continue; } } if (null == tf.getEncodingName()) { //ignore (ZapfDingbats and Symbol used to run through here, kept for safety reasons) } else if ("SymbolEncoding".equals(tf.getEncodingName())) { //ignore (no encoding redefinition) } else if ("ZapfDingbatsEncoding".equals(tf.getEncodingName())) { //ignore (no encoding redefinition) } else { if (tf instanceof Base14Font) { //Our Base 14 fonts don't use the default encoding redefineFontEncoding(gen, tf.getEmbedFontName(), tf.getEncodingName()); } else if (tf instanceof SingleByteFont) { SingleByteFont sbf = (SingleByteFont)tf; if (!sbf.isUsingNativeEncoding()) { //Font has been configured to use an encoding other than the default one redefineFontEncoding(gen, tf.getEmbedFontName(), tf.getEncodingName()); } } } } gen.commentln("%FOPEndFontReencode"); } private static Typeface getTypeFace(FontInfo fontInfo, Map fonts, String key) { Typeface tf = fonts.get(key); if (tf instanceof LazyFont) { tf = ((LazyFont)tf).getRealFont(); } if (tf == null) { //This is to avoid an NPE if a malconfigured font is in the configuration but not //used in the document. If it were used, we wouldn't get this far. String fallbackKey = fontInfo.getInternalFontKey(Font.DEFAULT_FONT); tf = fonts.get(fallbackKey); } return tf; } private static PSFontResource embedFont(PSGenerator gen, Typeface tf, PSResource fontRes, PSEventProducer eventProducer) throws IOException { FontType fontType = tf.getFontType(); PSFontResource fontResource = null; if (!(fontType == FontType.TYPE1 || fontType == FontType.TRUETYPE || fontType == FontType.TYPE0) || !(tf instanceof CustomFont)) { gen.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, fontRes); fontResource = PSFontResource.createFontResource(fontRes); return fontResource; } CustomFont cf = (CustomFont)tf; if (isEmbeddable(cf)) { InputStream in = getInputStreamOnFont(gen, cf); if (in == null) { gen.commentln("%WARNING: Could not embed font: " + cf.getEmbedFontName()); log.warn("Font " + cf.getEmbedFontName() + " is marked as supplied in the" + " PostScript file but could not be embedded!"); gen.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, fontRes); fontResource = PSFontResource.createFontResource(fontRes); return fontResource; } if (fontType == FontType.TYPE0) { if (gen.embedIdentityH()) { checkPostScriptLevel3(gen, eventProducer); /* * First CID-keyed font to be embedded; add * %%IncludeResource: comment for ProcSet CIDInit. */ gen.includeProcsetCIDInitResource(); } PSResource cidFontResource = embedType2CIDFont(gen, (MultiByteFont) tf, in); fontResource = PSFontResource.createFontResource(fontRes, gen.getProcsetCIDInitResource(), gen.getIdentityHCMapResource(), cidFontResource); } gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, fontRes); if (fontType == FontType.TYPE1) { embedType1Font(gen, in); fontResource = PSFontResource.createFontResource(fontRes); } else if (fontType == FontType.TRUETYPE) { embedTrueTypeFont(gen, (SingleByteFont) tf, in); fontResource = PSFontResource.createFontResource(fontRes); } else { composeType0Font(gen, (MultiByteFont) tf, in); } gen.writeDSCComment(DSCConstants.END_RESOURCE); gen.getResourceTracker().registerSuppliedResource(fontRes); } return fontResource; } private static void checkPostScriptLevel3(PSGenerator gen, PSEventProducer eventProducer) { if (gen.getPSLevel() < 3) { if (eventProducer != null) { eventProducer.postscriptLevel3Needed(gen); } else { throw new IllegalStateException("PostScript Level 3 is" + " required to use TrueType fonts," + " configured level is " + gen.getPSLevel()); } } } private static void embedTrueTypeFont(PSGenerator gen, SingleByteFont font, InputStream fontStream) throws IOException { /* See Adobe Technical Note #5012, "The Type 42 Font Format Specification" */ gen.commentln("%!PS-TrueTypeFont-65536-65536-1"); // TODO TrueType & font versions gen.writeln("11 dict begin"); if (font.getEmbeddingMode() == EmbeddingMode.AUTO) { font.setEmbeddingMode(EmbeddingMode.SUBSET); } FontFileReader reader = new FontFileReader(fontStream); TTFFile ttfFile = new TTFFile(); ttfFile.readFont(reader, font.getFullName()); createType42DictionaryEntries(gen, font, font.getCMap(), ttfFile); gen.writeln("FontName currentdict end definefont pop"); } private static void createType42DictionaryEntries(PSGenerator gen, CustomFont font, CMapSegment[] cmap, TTFFile ttfFile) throws IOException { gen.write("/FontName /"); gen.write(font.getEmbedFontName()); gen.writeln(" def"); gen.writeln("/PaintType 0 def"); gen.writeln("/FontMatrix [1 0 0 1 0 0] def"); writeFontBBox(gen, font); gen.writeln("/FontType 42 def"); gen.writeln("/Encoding 256 array"); gen.writeln("0 1 255{1 index exch/.notdef put}for"); boolean buildCharStrings; Set glyphNames = new HashSet(); if (font.getFontType() == FontType.TYPE0 && font.getEmbeddingMode() != EmbeddingMode.FULL) { //"/Encoding" is required but ignored for CID fonts //so we keep it minimal to save space buildCharStrings = false; } else { buildCharStrings = true; for (int i = 0; i < Glyphs.WINANSI_ENCODING.length; i++) { gen.write("dup "); gen.write(i); gen.write(" /"); String glyphName = Glyphs.charToGlyphName(Glyphs.WINANSI_ENCODING[i]); if (glyphName.equals("")) { gen.write(Glyphs.NOTDEF); } else { gen.write(glyphName); glyphNames.add(glyphName); } gen.writeln(" put"); } } gen.writeln("readonly def"); TTFOutputStream ttfOut = new PSTTFOutputStream(gen); ttfFile.stream(ttfOut); buildCharStrings(gen, buildCharStrings, cmap, glyphNames, font); } private static void buildCharStrings(PSGenerator gen, boolean buildCharStrings, CMapSegment[] cmap, Set glyphNames, CustomFont font) throws IOException { gen.write("/CharStrings "); if (!buildCharStrings) { gen.write(1); } else if (font.getEmbeddingMode() != EmbeddingMode.FULL) { int charCount = 1; //1 for .notdef for (CMapSegment segment : cmap) { charCount += segment.getUnicodeEnd() - segment.getUnicodeStart() + 1; } gen.write(charCount); } else { gen.write(font.getCMap().length); } gen.writeln(" dict dup begin"); gen.write("/"); gen.write(Glyphs.NOTDEF); gen.writeln(" 0 def"); // .notdef always has to be at index 0 if (!buildCharStrings) { // If we're not building the full CharStrings we can end here gen.writeln("end readonly def"); return; } if (font.getEmbeddingMode() != EmbeddingMode.FULL) { //Only performed in singly-byte mode, ignored for CID fonts for (CMapSegment segment : cmap) { int glyphIndex = segment.getGlyphStartIndex(); for (int ch = segment.getUnicodeStart(); ch <= segment.getUnicodeEnd(); ch++) { char ch16 = (char)ch; //TODO Handle Unicode characters beyond 16bit String glyphName = Glyphs.charToGlyphName(ch16); if ("".equals(glyphName)) { glyphName = "u" + Integer.toHexString(ch).toUpperCase(Locale.ENGLISH); } writeGlyphDefs(gen, glyphName, glyphIndex); glyphIndex++; } } } else { for (String name : glyphNames) { writeGlyphDefs(gen, name, getGlyphIndex(Glyphs.getUnicodeSequenceForGlyphName(name).charAt(0), font.getCMap())); } } gen.writeln("end readonly def"); } private static void writeGlyphDefs(PSGenerator gen, String glyphName, int glyphIndex) throws IOException { gen.write("/"); gen.write(glyphName); gen.write(" "); gen.write(glyphIndex); gen.writeln(" def"); } private static int getGlyphIndex(char c, CMapSegment[] cmap) { for (CMapSegment segment : cmap) { if (segment.getUnicodeStart() <= c && c <= segment.getUnicodeEnd()) { return segment.getGlyphStartIndex() + c - segment.getUnicodeStart(); } } return 0; } private static void composeType0Font(PSGenerator gen, MultiByteFont font, InputStream fontStream) throws IOException { String psName = font.getEmbedFontName(); gen.write("/"); gen.write(psName); gen.write(" /Identity-H [/"); gen.write(psName); gen.writeln("] composefont pop"); } private static PSResource embedType2CIDFont(PSGenerator gen, MultiByteFont font, InputStream fontStream) throws IOException { assert font.getCIDType() == CIDFontType.CIDTYPE2; String psName = font.getEmbedFontName(); gen.write("%%BeginResource: CIDFont "); gen.writeln(psName); gen.write("%%Title: ("); gen.write(psName); gen.writeln(" Adobe Identity 0)"); gen.writeln("%%Version: 1"); // TODO use font revision? gen.writeln("/CIDInit /ProcSet findresource begin"); gen.writeln("20 dict begin"); gen.write("/CIDFontName /"); gen.write(psName); gen.writeln(" def"); gen.writeln("/CIDFontVersion 1 def"); // TODO same as %%Version above gen.write("/CIDFontType "); gen.write(font.getCIDType().getValue()); gen.writeln(" def"); gen.writeln("/CIDSystemInfo 3 dict dup begin"); gen.writeln(" /Registry (Adobe) def"); gen.writeln(" /Ordering (Identity) def"); gen.writeln(" /Supplement 0 def"); gen.writeln("end def"); // TODO UIDBase (and UIDOffset in CMap) necessary if PostScript Level 1 & 2 // interpreters are to be supported // (Level 1: with composite font extensions; Level 2: those that do not offer // native mode support for CID-keyed fonts) // TODO XUID (optional but strongly recommended) // TODO /FontInfo gen.write("/CIDCount "); CIDSubset cidSubset = font.getCIDSubset(); int subsetSize = cidSubset.getSubsetSize(); gen.write(subsetSize); gen.writeln(" def"); gen.writeln("/GDBytes 2 def"); // TODO always 2? gen.writeln("/CIDMap [<"); int colCount = 0; int lineCount = 1; for (int cid = 0; cid < subsetSize; cid++) { if (colCount++ == 20) { gen.newLine(); colCount = 1; if (lineCount++ == 800) { gen.writeln("> <"); lineCount = 1; } } String gid; if (font.getEmbeddingMode() != EmbeddingMode.FULL) { gid = HexEncoder.encode(cid, 4); } else { gid = HexEncoder.encode(cidSubset.getGlyphIndexForSubsetIndex(cid), 4); } gen.write(gid); } gen.writeln(">] def"); FontFileReader reader = new FontFileReader(fontStream); TTFFile ttfFile; if (font.getEmbeddingMode() != EmbeddingMode.FULL) { ttfFile = new TTFSubSetFile(); ttfFile.readFont(reader, font.getTTCName(), font.getUsedGlyphs()); } else { ttfFile = new TTFFile(); ttfFile.readFont(reader, font.getTTCName()); } createType42DictionaryEntries(gen, font, new CMapSegment[0], ttfFile); gen.writeln("CIDFontName currentdict end /CIDFont defineresource pop"); gen.writeln("end"); gen.writeln("%%EndResource"); PSResource cidFontResource = new PSResource(PSResource.TYPE_CIDFONT, psName); gen.getResourceTracker().registerSuppliedResource(cidFontResource); return cidFontResource; } private static void writeFontBBox(PSGenerator gen, CustomFont font) throws IOException { int[] bbox = font.getFontBBox(); gen.write("/FontBBox["); for (int i = 0; i < 4; i++) { gen.write(" "); gen.write(bbox[i]); } gen.writeln(" ] def"); } private static boolean isEmbeddable(CustomFont font) { return font.isEmbeddable(); } private static InputStream getInputStreamOnFont(PSGenerator gen, CustomFont font) throws IOException { if (isEmbeddable(font)) { Source source = font.getEmbedFileSource(); if (source == null && font.getEmbedResourceName() != null) { source = new StreamSource(PSFontUtils.class .getResourceAsStream(font.getEmbedResourceName())); } if (source == null) { return null; } InputStream in = null; if (source instanceof StreamSource) { in = ((StreamSource) source).getInputStream(); } if (in == null && source.getSystemId() != null) { try { in = new java.net.URL(source.getSystemId()).openStream(); } catch (MalformedURLException e) { new FileNotFoundException( "File not found. URL could not be resolved: " + e.getMessage()); } } if (in == null) { return null; } //Make sure the InputStream is decorated with a BufferedInputStream if (!(in instanceof java.io.BufferedInputStream)) { in = new java.io.BufferedInputStream(in); } return in; } else { return null; } } /** * Determines the set of fonts that will be supplied with the PS file and registers them * with the resource tracker. All the fonts that are being processed are returned as a Map. * @param resTracker the resource tracker * @param fontInfo available fonts * @param fonts the set of fonts to work with * @return a Map of PSResource instances representing all defined fonts (key: font key) */ public static Map determineSuppliedFonts(ResourceTracker resTracker, FontInfo fontInfo, Map fonts) { Map fontResources = new java.util.HashMap(); for (String key : fonts.keySet()) { Typeface tf = getTypeFace(fontInfo, fonts, key); PSResource fontRes = new PSResource("font", tf.getEmbedFontName()); fontResources.put(key, fontRes); FontType fontType = tf.getFontType(); if (fontType == FontType.TYPE1 || fontType == FontType.TRUETYPE || fontType == FontType.TYPE0) { if (tf instanceof CustomFont) { CustomFont cf = (CustomFont)tf; if (isEmbeddable(cf)) { if (fontType == FontType.TYPE0) { resTracker.registerSuppliedResource( new PSResource(PSResource.TYPE_CIDFONT, tf.getEmbedFontName())); resTracker.registerSuppliedResource( new PSResource(PSResource.TYPE_CMAP, "Identity-H")); } resTracker.registerSuppliedResource(fontRes); } if (tf instanceof SingleByteFont) { SingleByteFont sbf = (SingleByteFont)tf; for (int i = 0, c = sbf.getAdditionalEncodingCount(); i < c; i++) { SingleByteEncoding encoding = sbf.getAdditionalEncoding(i); PSResource encodingRes = new PSResource( PSResource.TYPE_ENCODING, encoding.getName()); resTracker.registerSuppliedResource(encodingRes); PSResource derivedFontRes = new PSResource( PSResource.TYPE_FONT, tf.getEmbedFontName() + "_" + (i + 1)); resTracker.registerSuppliedResource(derivedFontRes); } } } } } return fontResources; } /** * Defines the single-byte encoding for use in PostScript files. * @param gen the PostScript generator * @param encoding the single-byte encoding * @return the PSResource instance that represents the encoding * @throws IOException In case of an I/O problem */ public static PSResource defineEncoding(PSGenerator gen, SingleByteEncoding encoding) throws IOException { PSResource res = new PSResource(PSResource.TYPE_ENCODING, encoding.getName()); gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, res); gen.writeln("/" + encoding.getName() + " ["); String[] charNames = encoding.getCharNameMap(); for (int i = 0; i < 256; i++) { if (i > 0) { if ((i % 5) == 0) { gen.newLine(); } else { gen.write(" "); } } String glyphname = null; if (i < charNames.length) { glyphname = charNames[i]; } if (glyphname == null || "".equals(glyphname)) { glyphname = Glyphs.NOTDEF; } gen.write("/"); gen.write(glyphname); } gen.newLine(); gen.writeln("] def"); gen.writeDSCComment(DSCConstants.END_RESOURCE); gen.getResourceTracker().registerSuppliedResource(res); return res; } /** * Derives a new font based on an existing font with a given encoding. The encoding must * have been registered before. * @param gen the PostScript generator * @param baseFontName the font name of the font to derive from * @param fontName the font name of the new font to be define * @param encoding the new encoding (must be predefined in the PS file) * @return the PSResource representing the derived font * @throws IOException In case of an I/O problem */ public static PSResource defineDerivedFont (PSGenerator gen, String baseFontName, String fontName, String encoding) throws IOException { PSResource res = new PSResource(PSResource.TYPE_FONT, fontName); gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, res); gen.commentln("%XGCDependencies: font " + baseFontName); gen.commentln("%XGC+ encoding " + encoding); gen.writeln("/" + baseFontName + " findfont"); gen.writeln("dup length dict begin"); gen.writeln(" {1 index /FID ne {def} {pop pop} ifelse} forall"); gen.writeln(" /Encoding " + encoding + " def"); gen.writeln(" currentdict"); gen.writeln("end"); gen.writeln("/" + fontName + " exch definefont pop"); gen.writeDSCComment(DSCConstants.END_RESOURCE); gen.getResourceTracker().registerSuppliedResource(res); return res; } private static PSResource defineDerivedTrueTypeFont(PSGenerator gen, PSEventProducer eventProducer, String baseFontName, String fontName, SingleByteEncoding encoding, CMapSegment[] cmap) throws IOException { checkPostScriptLevel3(gen, eventProducer); PSResource res = new PSResource(PSResource.TYPE_FONT, fontName); gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, res); gen.commentln("%XGCDependencies: font " + baseFontName); gen.commentln("%XGC+ encoding " + encoding.getName()); gen.writeln("/" + baseFontName + " findfont"); gen.writeln("dup length dict begin"); gen.writeln(" {1 index /FID ne {def} {pop pop} ifelse} forall"); gen.writeln(" /Encoding " + encoding.getName() + " def"); gen.writeln(" /CharStrings 256 dict dup begin"); String[] charNameMap = encoding.getCharNameMap(); char[] unicodeCharMap = encoding.getUnicodeCharMap(); assert charNameMap.length == unicodeCharMap.length; for (int i = 0; i < charNameMap.length; i++) { String glyphName = charNameMap[i]; gen.write(" /"); gen.write(glyphName); gen.write(" "); if (glyphName.equals(".notdef")) { gen.write(0); } else { gen.write(getGlyphIndex(unicodeCharMap[i], cmap)); } gen.writeln(" def"); } gen.writeln(" end readonly def"); gen.writeln(" currentdict"); gen.writeln("end"); gen.writeln("/" + fontName + " exch definefont pop"); gen.writeDSCComment(DSCConstants.END_RESOURCE); gen.getResourceTracker().registerSuppliedResource(res); return res; } }