123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519 |
- /*
- * 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.fonts.apps;
-
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.util.Map;
- import java.util.Set;
-
- import javax.xml.parsers.DocumentBuilderFactory;
-
- import org.w3c.dom.Document;
- import org.w3c.dom.Element;
- import org.xml.sax.Attributes;
- import org.xml.sax.SAXException;
-
- import org.apache.commons.logging.LogFactory;
-
- import org.apache.fop.Version;
- import org.apache.fop.fonts.CMapSegment;
- import org.apache.fop.fonts.FontUtil;
- import org.apache.fop.fonts.truetype.FontFileReader;
- import org.apache.fop.fonts.truetype.TTFFile;
- import org.apache.fop.util.CommandLineLogger;
-
- // CSOFF: InnerAssignmentCheck
- // CSOFF: LineLengthCheck
-
- /**
- * A tool which reads TTF files and generates
- * XML font metrics file for use in FOP.
- */
- public class TTFReader extends AbstractFontReader {
-
- /** Used to detect incompatible versions of the generated XML files */
- public static final String METRICS_VERSION_ATTR = "metrics-version";
- /** Current version number for the metrics file */
- public static final int METRICS_VERSION = 2;
-
- /**
- * Main constructor.
- */
- public TTFReader() {
- super();
- }
-
- private static void displayUsage() {
- System.out.println(
- "java " + TTFReader.class.getName() + " [options] fontfile.ttf xmlfile.xml");
- System.out.println();
- System.out.println("where options can be:");
- System.out.println("-t Trace mode");
- System.out.println("-d Debug mode");
- System.out.println("-q Quiet mode");
- System.out.println("-enc ansi");
- System.out.println(" With this option you create a WinAnsi encoded font.");
- System.out.println(" The default is to create a CID keyed font.");
- System.out.println(" If you're not going to use characters outside the");
- System.out.println(" pdfencoding range (almost the same as iso-8889-1)");
- System.out.println(" you can add this option.");
- System.out.println("-ttcname <fontname>");
- System.out.println(" If you're reading data from a TrueType Collection");
- System.out.println(" (.ttc file) you must specify which font from the");
- System.out.println(" collection you will read metrics from. If you read");
- System.out.println(" from a .ttc file without this option, the fontnames");
- System.out.println(" will be listed for you.");
- System.out.println(" -fn <fontname>");
- System.out.println(" default is to use the fontname in the .ttf file, but");
- System.out.println(" you can override that name to make sure that the");
- System.out.println(" embedded font is used (if you're embedding fonts)");
- System.out.println(" instead of installed fonts when viewing documents ");
- System.out.println(" with Acrobat Reader.");
- }
-
-
- /**
- * The main method for the TTFReader tool.
- *
- * @param args Command-line arguments: [options] fontfile.ttf xmlfile.xml
- * where options can be:
- * -fn <fontname>
- * default is to use the fontname in the .ttf file, but you can override
- * that name to make sure that the embedded font is used instead of installed
- * fonts when viewing documents with Acrobat Reader.
- * -cn <classname>
- * default is to use the fontname
- * -ef <path to the truetype fontfile>
- * will add the possibility to embed the font. When running fop, fop will look
- * for this file to embed it
- * -er <path to truetype fontfile relative to org/apache/fop/render/pdf/fonts>
- * you can also include the fontfile in the fop.jar file when building fop.
- * You can use both -ef and -er. The file specified in -ef will be searched first,
- * then the -er file.
- * -nocs
- * if complex script features are disabled
- */
- public static void main(String[] args) {
- String embFile = null;
- String embResource = null;
- String className = null;
- String fontName = null;
- String ttcName = null;
- boolean isCid = true;
-
- Map options = new java.util.HashMap();
- String[] arguments = parseArguments(options, args);
-
- // Enable the simple command line logging when no other logger is
- // defined.
- LogFactory logFactory = LogFactory.getFactory();
- if (System.getProperty("org.apache.commons.logging.Log") == null) {
- logFactory.setAttribute("org.apache.commons.logging.Log",
- CommandLineLogger.class.getName());
- }
-
- determineLogLevel(options);
-
- TTFReader app = new TTFReader();
-
- log.info("TTF Reader for Apache FOP " + Version.getVersion() + "\n");
-
- if (options.get("-enc") != null) {
- String enc = (String)options.get("-enc");
- if ("ansi".equals(enc)) {
- isCid = false;
- }
- }
-
- if (options.get("-ttcname") != null) {
- ttcName = (String)options.get("-ttcname");
- }
-
- if (options.get("-ef") != null) {
- embFile = (String)options.get("-ef");
- }
-
- if (options.get("-er") != null) {
- embResource = (String)options.get("-er");
- }
-
- if (options.get("-fn") != null) {
- fontName = (String)options.get("-fn");
- }
-
- if (options.get("-cn") != null) {
- className = (String)options.get("-cn");
- }
-
- boolean useKerning = true;
- boolean useAdvanced = true;
- if (options.get("-nocs") != null) {
- useAdvanced = false;
- }
-
- if (arguments.length != 2 || options.get("-h") != null
- || options.get("-help") != null || options.get("--help") != null) {
- displayUsage();
- } else {
- try {
- log.info("Parsing font...");
- TTFFile ttf = app.loadTTF(arguments[0], ttcName, useKerning, useAdvanced);
- if (ttf != null) {
- org.w3c.dom.Document doc = app.constructFontXML(ttf,
- fontName, className, embResource, embFile, isCid,
- ttcName);
-
- if (isCid) {
- log.info("Creating CID encoded metrics...");
- } else {
- log.info("Creating WinAnsi encoded metrics...");
- }
-
- if (doc != null) {
- app.writeFontXML(doc, arguments[1]);
- }
-
- if (ttf.isEmbeddable()) {
- log.info("This font contains no embedding license restrictions.");
- } else {
- log.info("** Note: This font contains license retrictions for\n"
- + " embedding. This font shouldn't be embedded.");
- }
- }
- log.info("");
- log.info("XML font metrics file successfully created.");
- } catch (Exception e) {
- log.error("Error while building XML font metrics file.", e);
- System.exit(-1);
- }
- }
- }
-
- /**
- * Read a TTF file and returns it as an object.
- *
- * @param fileName The filename of the TTF file.
- * @param fontName The name of the font
- * @param useKerning true if should load kerning data
- * @param useAdvanced true if should load advanced typographic table data
- * @return The TTF as an object, null if the font is incompatible.
- * @throws IOException In case of an I/O problem
- */
- public TTFFile loadTTF(String fileName, String fontName, boolean useKerning, boolean useAdvanced) throws IOException {
- TTFFile ttfFile = new TTFFile(useKerning, useAdvanced);
- log.info("Reading " + fileName + "...");
- InputStream stream = new FileInputStream(fileName);
- try {
- FontFileReader reader = new FontFileReader(stream);
- boolean supported = ttfFile.readFont(reader, fontName);
- if (!supported) {
- return null;
- }
- } finally {
- stream.close();
- }
-
- log.info("Font Family: " + ttfFile.getFamilyNames());
- if (ttfFile.isCFF()) {
- throw new UnsupportedOperationException(
- "OpenType fonts with CFF data are not supported, yet");
- }
- return ttfFile;
- }
-
-
- /**
- * Generates the font metrics file from the TTF/TTC file.
- *
- * @param ttf The PFM file to generate the font metrics from.
- * @param fontName Name of the font
- * @param className Class name for the font
- * @param resource path to the font as embedded resource
- * @param file path to the font as file
- * @param isCid True if the font is CID encoded
- * @param ttcName Name of the TrueType Collection
- * @return The DOM document representing the font metrics file.
- */
- public org.w3c.dom.Document constructFontXML(TTFFile ttf,
- String fontName, String className, String resource, String file,
- boolean isCid, String ttcName) {
- log.info("Creating xml font file...");
-
- Document doc;
- try {
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- doc = factory.newDocumentBuilder().newDocument();
- } catch (javax.xml.parsers.ParserConfigurationException e) {
- log.error("Can't create DOM implementation", e);
- return null;
- }
- Element root = doc.createElement("font-metrics");
- doc.appendChild(root);
- root.setAttribute(METRICS_VERSION_ATTR, String.valueOf(METRICS_VERSION));
- if (isCid) {
- root.setAttribute("type", "TYPE0");
- } else {
- root.setAttribute("type", "TRUETYPE");
- }
-
- Element el = doc.createElement("font-name");
- root.appendChild(el);
-
- // Note that the PostScript name usually is something like
- // "Perpetua-Bold", but the TrueType spec says that in the ttf file
- // it should be "Perpetua,Bold".
-
- String s = FontUtil.stripWhiteSpace(ttf.getPostScriptName());
-
- if (fontName != null) {
- el.appendChild(doc.createTextNode(FontUtil.stripWhiteSpace(fontName)));
- } else {
- el.appendChild(doc.createTextNode(s));
- }
- if (ttf.getFullName() != null) {
- el = doc.createElement("full-name");
- root.appendChild(el);
- el.appendChild(doc.createTextNode(ttf.getFullName()));
- }
- Set<String> familyNames = ttf.getFamilyNames();
- if (familyNames.size() > 0) {
- String familyName = familyNames.iterator().next();
- el = doc.createElement("family-name");
- root.appendChild(el);
- el.appendChild(doc.createTextNode(familyName));
- }
-
- el = doc.createElement("embed");
- root.appendChild(el);
- if (file != null && ttf.isEmbeddable()) {
- el.setAttribute("file", file);
- }
- if (resource != null && ttf.isEmbeddable()) {
- el.setAttribute("class", resource);
- }
-
- el = doc.createElement("cap-height");
- root.appendChild(el);
- el.appendChild(doc.createTextNode(String.valueOf(ttf.getCapHeight())));
-
- el = doc.createElement("x-height");
- root.appendChild(el);
- el.appendChild(doc.createTextNode(String.valueOf(ttf.getXHeight())));
-
- el = doc.createElement("ascender");
- root.appendChild(el);
- el.appendChild(doc.createTextNode(String.valueOf(ttf.getLowerCaseAscent())));
-
- el = doc.createElement("descender");
- root.appendChild(el);
- el.appendChild(doc.createTextNode(String.valueOf(ttf.getLowerCaseDescent())));
-
- Element bbox = doc.createElement("bbox");
- root.appendChild(bbox);
- int[] bb = ttf.getFontBBox();
- final String[] names = {"left", "bottom", "right", "top"};
- for (int i = 0; i < names.length; i++) {
- el = doc.createElement(names[i]);
- bbox.appendChild(el);
- el.appendChild(doc.createTextNode(String.valueOf(bb[i])));
- }
-
- el = doc.createElement("flags");
- root.appendChild(el);
- el.appendChild(doc.createTextNode(String.valueOf(ttf.getFlags())));
-
- el = doc.createElement("stemv");
- root.appendChild(el);
- el.appendChild(doc.createTextNode(ttf.getStemV()));
-
- el = doc.createElement("italicangle");
- root.appendChild(el);
- el.appendChild(doc.createTextNode(ttf.getItalicAngle()));
-
- if (ttcName != null) {
- el = doc.createElement("ttc-name");
- root.appendChild(el);
- el.appendChild(doc.createTextNode(ttcName));
- }
-
- el = doc.createElement("subtype");
- root.appendChild(el);
-
- // Fill in extras for CID keyed fonts
- if (isCid) {
- el.appendChild(doc.createTextNode("TYPE0"));
-
- generateDOM4MultiByteExtras(root, ttf, isCid);
- } else {
- // Fill in extras for singlebyte fonts
- el.appendChild(doc.createTextNode("TRUETYPE"));
-
- generateDOM4SingleByteExtras(root, ttf, isCid);
- }
-
- generateDOM4Kerning(root, ttf, isCid);
-
- return doc;
- }
-
- private void generateDOM4MultiByteExtras(Element parent, TTFFile ttf, boolean isCid) {
- Element el;
- Document doc = parent.getOwnerDocument();
-
- Element mel = doc.createElement("multibyte-extras");
- parent.appendChild(mel);
-
- el = doc.createElement("cid-type");
- mel.appendChild(el);
- el.appendChild(doc.createTextNode("CIDFontType2"));
-
- el = doc.createElement("default-width");
- mel.appendChild(el);
- el.appendChild(doc.createTextNode("0"));
-
- el = doc.createElement("bfranges");
- mel.appendChild(el);
- for (CMapSegment ce : ttf.getCMaps()) {
- Element el2 = doc.createElement("bf");
- el.appendChild(el2);
- el2.setAttribute("us", String.valueOf(ce.getUnicodeStart()));
- el2.setAttribute("ue", String.valueOf(ce.getUnicodeEnd()));
- el2.setAttribute("gi", String.valueOf(ce.getGlyphStartIndex()));
- }
-
- el = doc.createElement("cid-widths");
- el.setAttribute("start-index", "0");
- mel.appendChild(el);
-
- int[] wx = ttf.getWidths();
- for (int i = 0; i < wx.length; i++) {
- Element wxel = doc.createElement("wx");
- wxel.setAttribute("w", String.valueOf(wx[i]));
- el.appendChild(wxel);
- }
- }
-
- private void generateDOM4SingleByteExtras(Element parent, TTFFile ttf, boolean isCid) {
- Element el;
- Document doc = parent.getOwnerDocument();
-
- Element sel = doc.createElement("singlebyte-extras");
- parent.appendChild(sel);
-
- el = doc.createElement("encoding");
- sel.appendChild(el);
- el.appendChild(doc.createTextNode(ttf.getCharSetName()));
-
- el = doc.createElement("first-char");
- sel.appendChild(el);
- el.appendChild(doc.createTextNode(String.valueOf(ttf.getFirstChar())));
-
- el = doc.createElement("last-char");
- sel.appendChild(el);
- el.appendChild(doc.createTextNode(String.valueOf(ttf.getLastChar())));
-
- Element widths = doc.createElement("widths");
- sel.appendChild(widths);
-
- for (short i = ttf.getFirstChar(); i <= ttf.getLastChar(); i++) {
- el = doc.createElement("char");
- widths.appendChild(el);
- el.setAttribute("idx", String.valueOf(i));
- el.setAttribute("wdt", String.valueOf(ttf.getCharWidth(i)));
- }
- }
-
- private void generateDOM4Kerning(Element parent, TTFFile ttf, boolean isCid) {
- Element el;
- Document doc = parent.getOwnerDocument();
-
- // Get kerning
- Set<Integer> kerningKeys;
- if (isCid) {
- kerningKeys = ttf.getKerning().keySet();
- } else {
- kerningKeys = ttf.getAnsiKerning().keySet();
- }
-
- for (Integer kpx1 : kerningKeys) {
-
- el = doc.createElement("kerning");
- el.setAttribute("kpx1", kpx1.toString());
- parent.appendChild(el);
- Element el2 = null;
-
- Map<Integer, Integer> h2;
- if (isCid) {
- h2 = ttf.getKerning().get(kpx1);
- } else {
- h2 = ttf.getAnsiKerning().get(kpx1);
- }
-
- for (Integer kpx2 : h2.keySet()) {
- if (isCid || kpx2.intValue() < 256) {
- el2 = doc.createElement("pair");
- el2.setAttribute("kpx2", kpx2.toString());
- Integer val = h2.get(kpx2);
- el2.setAttribute("kern", val.toString());
- el.appendChild(el2);
- }
- }
- }
- }
-
- /**
- * Bugzilla 40739, check that attr has a metrics-version attribute
- * compatible with ours.
- * @param attr attributes read from the root element of a metrics XML file
- * @throws SAXException if incompatible
- */
- public static void checkMetricsVersion(Attributes attr) throws SAXException {
- String err = null;
- final String str = attr.getValue(METRICS_VERSION_ATTR);
- if (str == null) {
- err = "Missing " + METRICS_VERSION_ATTR + " attribute";
- } else {
- int version = 0;
- try {
- version = Integer.parseInt(str);
- if (version < METRICS_VERSION) {
- err = "Incompatible " + METRICS_VERSION_ATTR
- + " value (" + version + ", should be " + METRICS_VERSION
- + ")";
- }
- } catch (NumberFormatException e) {
- err = "Invalid " + METRICS_VERSION_ATTR
- + " attribute value (" + str + ")";
- }
- }
-
- if (err != null) {
- throw new SAXException(
- err
- + " - please regenerate the font metrics file with "
- + "a more recent version of FOP."
- );
- }
- }
-
- }
|