diff options
author | fotis <fotis@unknown> | 2001-02-27 12:30:36 +0000 |
---|---|---|
committer | fotis <fotis@unknown> | 2001-02-27 12:30:36 +0000 |
commit | 375b678502231f5577d5b18550c35678d11e11d8 (patch) | |
tree | e4a11a39dc47debb1465ec80f686c553caa5a4a6 /src | |
parent | 440ec1d3a5601f93e8798acd8a6325c39a19ddd9 (diff) | |
download | xmlgraphics-fop-375b678502231f5577d5b18550c35678d11e11d8.tar.gz xmlgraphics-fop-375b678502231f5577d5b18550c35678d11e11d8.zip |
enabling CID keyed truetype fonts; this gives support
for other encodings than WinAnsiEncoding (eg japanese, chinese, arabic,
iso-whatever, etc). Also makes font inclusion easier Tore Engvig
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@194095 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src')
33 files changed, 3262 insertions, 1107 deletions
diff --git a/src/org/apache/fop/configuration/Configuration.java b/src/org/apache/fop/configuration/Configuration.java index 844fede9e..088982841 100644 --- a/src/org/apache/fop/configuration/Configuration.java +++ b/src/org/apache/fop/configuration/Configuration.java @@ -220,6 +220,17 @@ public class Configuration { /** + * method to access fonts values in the standard configuration + * + * @param key a string containing the key value for the configuration value + * @return Hashtable a Hashtable containing the values + * null if the key is not defined. + */ + public static Vector getFonts() { + return (Vector) Configuration.getValue("fonts", Configuration.STANDARD); + } + + /** * initializes this configuration * @param config contains the configuration information */ diff --git a/src/org/apache/fop/configuration/ConfigurationParser.java b/src/org/apache/fop/configuration/ConfigurationParser.java index c5c3d620b..1dab0e2b7 100644 --- a/src/org/apache/fop/configuration/ConfigurationParser.java +++ b/src/org/apache/fop/configuration/ConfigurationParser.java @@ -7,14 +7,19 @@ package org.apache.fop.configuration; +//sax import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.Attributes; import org.xml.sax.Locator; + +//java import java.util.Hashtable; import java.util.Vector; +//fop import org.apache.fop.messaging.MessageHandler; + /** * SAX2 Handler which retrieves the configuration information and stores them in Configuration. * Normally this class doesn't need to be accessed directly. @@ -28,6 +33,8 @@ public class ConfigurationParser extends DefaultHandler { private final int IN_LIST = 8; private final int IN_SUBENTRY = 16; private final int IN_SUBKEY = 32; + private final int IN_FONTS = 64; + private final int IN_FONT = 128; private final int STRING = 0; private final int LIST = 1; @@ -62,6 +69,23 @@ public class ConfigurationParser extends DefaultHandler { /** determines role / target of configuration information, default is standard */ private String role = "standard"; + //stores fonts + private Vector fontList = null; + + //stores information on one font + private FontInfo fontInfo = null; + + //stores information on a font triplet + private FontTriplet fontTriplet = null; + + //information on a font + private String fontName, metricsFile, embedFile, kerningAsString; + private boolean kerning; + private Vector fontTriplets; + + //information on a font triplet + private String fontTripletName, weight, style; + public void startDocument() { configuration = Configuration.getConfiguration(); } @@ -93,6 +117,25 @@ public class ConfigurationParser extends DefaultHandler { role = attributes.getValue("role"); } } else if (localName.equals("configuration") ) { + } else if (localName.equals("fonts") ) { //list of fonts starts + fontList = new Vector (10); + } else if (localName.equals("font") ) { + kerningAsString = attributes.getValue("kerning"); + if (kerningAsString.equalsIgnoreCase("yes")) { + kerning = true; + } else { + kerning = false; + } + metricsFile = attributes.getValue("metrics-file"); + embedFile = attributes.getValue("embed-file"); + fontName = attributes.getValue("name"); + fontTriplets = new Vector(5); + } else if (localName.equals("font-triplet") ) { + fontTripletName = attributes.getValue("name"); + weight = attributes.getValue("weight"); + style = attributes.getValue("style"); + fontTriplet = new FontTriplet(fontTripletName,weight,style); + fontTriplets.addElement(fontTriplet); } else { //to make sure that user knows about false tag MessageHandler.errorln( @@ -117,20 +160,31 @@ public class ConfigurationParser extends DefaultHandler { } status = OUT; role = "standard"; - key = ""; - value = ""; - } else if (localName.equals("subentry")) { - map.put(subkey, value); - status -= IN_SUBENTRY; - key = ""; - value = ""; - } else if (localName.equals("key")) { - status -= IN_KEY; - } else if (localName.equals("list")) { - status -= IN_LIST; - value = ""; - } else if (localName.equals("value")) { - status -= IN_VALUE; + key = ""; + value = ""; + } else if (localName.equals("subentry")) { + map.put(subkey, value); + status -= IN_SUBENTRY; + key = ""; + value = ""; + } else if (localName.equals("key")) { + status -= IN_KEY; + } else if (localName.equals("list")) { + status -= IN_LIST; + value = ""; + } else if (localName.equals("value")) { + status -= IN_VALUE; + } else if (localName.equals("fonts") ) { + this.store("standard", "fonts", fontList); + } else if (localName.equals("font") ) { + fontInfo = new FontInfo(fontName,metricsFile,kerning,fontTriplets,embedFile); + fontList.addElement(fontInfo); + fontTriplets = null; + metricsFile = null; + embedFile = null; + fontName = null; + kerningAsString = ""; + } else if (localName.equals("font-triplet") ) { } } diff --git a/src/org/apache/fop/configuration/ConfigurationReader.java b/src/org/apache/fop/configuration/ConfigurationReader.java index 92d7c338a..edde15624 100644 --- a/src/org/apache/fop/configuration/ConfigurationReader.java +++ b/src/org/apache/fop/configuration/ConfigurationReader.java @@ -1,9 +1,53 @@ -/* - * Copyright (C) 2001 The Apache Software Foundation. All rights reserved. - * For details on use and redistribution please refer to the - * LICENSE file included with these sources." - */ +/* + + ============================================================================ + The Apache Software License, Version 1.1 + ============================================================================ + + Copyright (C) 1999 The Apache Software Foundation. All rights reserved. + + Redistribution and use in source and binary forms, with or without modifica- + tion, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. The end-user documentation included with the redistribution, if any, must + include the following acknowledgment: "This product includes software + developed by the Apache Software Foundation (http://www.apache.org/)." + Alternately, this acknowledgment may appear in the software itself, if + and wherever such third-party acknowledgments normally appear. + + 4. The names "Fop" and "Apache Software Foundation" must not be used to + endorse or promote products derived from this software without prior + written permission. For written permission, please contact + apache@apache.org. + + 5. Products derived from this software may not be called "Apache", nor may + "Apache" appear in their name, without prior written permission of the + Apache Software Foundation. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- + DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + This software consists of voluntary contributions made by many individuals + on behalf of the Apache Software Foundation and was originally created by + James Tauber <jtauber@jtauber.com>. For more information on the Apache + Software Foundation, please see <http://www.apache.org/>. + */ package org.apache.fop.configuration; @@ -31,7 +75,7 @@ import org.apache.fop.configuration.Configuration; * } * </code> * Once the configuration has been setup, the information can be accessed with - * the methods of Configuration. + * the methods of StandardConfiguration. */ public class ConfigurationReader { @@ -103,7 +147,7 @@ public class ConfigurationReader { * * @return the created SAX parser */ - static XMLReader createParser() { + public static XMLReader createParser() { String parserClassName = System.getProperty("org.xml.sax.parser"); if (parserClassName == null) { parserClassName = "org.apache.xerces.parsers.SAXParser"; diff --git a/src/org/apache/fop/configuration/FontInfo.java b/src/org/apache/fop/configuration/FontInfo.java new file mode 100644 index 000000000..07af6b12b --- /dev/null +++ b/src/org/apache/fop/configuration/FontInfo.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2001 The Apache Software Foundation. All rights reserved. + * For details on use and redistribution please refer to the + * LICENSE file included with these sources." + */ + + +package org.apache.fop.configuration; + +import java.util.Vector; + +/** + * FontInfo contains meta information on fonts (where is the metrics file etc.) + */ + +public class FontInfo { + private String metricsFile, embedFile, name; + private boolean kerning; + private Vector fontTriplets; + + public FontInfo (String name, String metricsFile, boolean kerning, + Vector fontTriplets, String embedFile) { + this.name = name; + this.metricsFile = metricsFile; + this.embedFile = embedFile; + this.kerning = kerning; + this.fontTriplets = fontTriplets; + } + + public String getMetricsFile() { + return metricsFile; + } + public String getEmbedFile() { + return embedFile; + } + public boolean getKerning() { + return kerning; + } + public Vector getFontTriplets() { + return fontTriplets; + } + +} + diff --git a/src/org/apache/fop/configuration/FontTriplet.java b/src/org/apache/fop/configuration/FontTriplet.java new file mode 100644 index 000000000..d5e927d6f --- /dev/null +++ b/src/org/apache/fop/configuration/FontTriplet.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2001 The Apache Software Foundation. All rights reserved. + * For details on use and redistribution please refer to the + * LICENSE file included with these sources." + */ + + +package org.apache.fop.configuration; + +/** + * FontTriplet contains information on name, weight, style of one font + */ + + +public class FontTriplet { + private String name, weight, style; + public FontTriplet(String name, String weight, String style) { + this.name = name; + this.weight = weight; + this.style = style; + } + + public String getName() { + return name; + } + + public String getWeight() { + return weight; + } + + public String getStyle() { + return style; + } + +} + + + + diff --git a/src/org/apache/fop/fonts/Glyphs.java b/src/org/apache/fop/fonts/Glyphs.java index cba61bcdb..99fc076e3 100644 --- a/src/org/apache/fop/fonts/Glyphs.java +++ b/src/org/apache/fop/fonts/Glyphs.java @@ -604,7 +604,254 @@ public class Glyphs { "yacute", "thorn", "ydieresis"}; - + + /** The characters in WinAnsiEncoding */ + public static char[] winAnsiEncoding = { + // not used until char 32 + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + // 0x20 + ' ', + '\u0021', + '\"', + '\u0023', + '$', + '%', + '&', + '\'', + '(', + ')', + '*', + '+', + ',', + '\u002d', + '\u002e', + '/', + // 0x30 + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + ':', + ';', + '<', + '=', + '>', + '?', + '@', + // 0x40 + 'A', + 'B', + 'C', + 'D', + 'E', + 'F', + 'G', + 'H', + 'I', + 'J', + 'K', + 'L', + 'M', + 'N', + 'O', + // 0x50 + 'P', + 'Q', + 'R', + 'S', + 'T', + 'U', + 'V', + 'W', + 'X', + 'Y', + 'Z', + '\u005b', + '\\', + '\u005d', + '\u005e', + '_', + // 0x60 + '\u2018', + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i', + 'j', + 'k', + 'l', + 'm', + 'n', + 'o', + // 0x70 + 'p', + 'q', + 'r', + 's', + 't', + 'u', + 'v', + 'w', + 'x', + 'y', + 'z', + '\u007b', + '\u007c', + '\u007d', + '\u007e', + '\u2022', + // 0x80 + '\u20ac', + '\u2022', + '\u201a', + '\u0192', + '\u201e', + '\u2026', + '\u2020', + '\u2021', + '\u02c6', + '\u2030', + '\u0160', + '\u2039', + '\u0152', + '\u2022', + '\u017d', + '\u2022', + // 0x90 + '\u2022', + '\u2018', // quoteleft + '\u2019', // quoteright + '\u201c', // quotedblleft + '\u201d', // quotedblright + '\u2022', // bullet + '\u2013', // endash + '\u2014', // emdash + '~', + '\u2022', // bullet + '\u0161', + '\u203a', + '\u0153', + '\u2022', + '\u017e', + '\u0178', + // 0xA0 + ' ', + '\u00a1', + '\u00a2', + '£', + '\u00a4', + '\u00a5', + '\u00a6', + '\u00a7', + '\u00a8', + '\u00a9', + '\u00aa', + '\u00ab', + '\u00ac', + '\u00ad', + '\u00ae', + '\u00af', + // 0xb0 + '\u00b0', + '\u00b1', + '\u00b2', + '\u00b3', + '\u00b4', + '\u00b5', // This is hand-coded, the rest is assumption + '\u00b6', // and *might* not be correct... + '\u00b7', + '\u00b8', + '\u00b9', + '\u00ba', + '\u00bb', + '\u00bc', + '\u00bd', + '\u00be', + '\u00bf', + // 0xc0 + '\u00c0', + '\u00c1', + '\u00c2', + '\u00c3', + '\u00c4', + '\u00c5', // Aring + '\u00c6', // AE + '\u00c7', + '\u00c8', + '\u00c9', + '\u00ca', + '\u00cb', + '\u00cc', + '\u00cd', + '\u00ce', + '\u00cf', + // 0xd0 + '\u00d0', + '\u00d1', + '\u00d2', + '\u00d3', + '\u00d4', + '\u00d5', + '\u00d6', + '\u00d7', + '\u00d8', // Oslash + '\u00d9', + '\u00da', + '\u00db', + '\u00dc', + '\u00dd', + '\u00de', + '\u00df', + // 0xe0 + '\u00e0', + '\u00e1', + '\u00e2', + '\u00e3', + '\u00e4', + '\u00e5', // aring + '\u00e6', // ae + '\u00e7', + '\u00e8', + '\u00e9', + '\u00ea', + '\u00eb', + '\u00ec', + '\u00ed', + '\u00ee', + '\u00ef', + // 0xf0 + '\u00f0', + '\u00f1', + '\u00f2', + '\u00f3', + '\u00f4', + '\u00f5', + '\u00f6', + '\u00f7', + '\u00f8', + '\u00f9', + '\u00fa', + '\u00fb', + '\u00fc', + '\u00fd', + '\u00fe', + '\u00ff' + }; + static String[] unicode_glyphs={ "\u0041", "A", "\u00C6", "AE", diff --git a/src/org/apache/fop/fonts/PFMFile.java b/src/org/apache/fop/fonts/PFMFile.java index 5ebb011cc..f1cae42d3 100644 --- a/src/org/apache/fop/fonts/PFMFile.java +++ b/src/org/apache/fop/fonts/PFMFile.java @@ -203,11 +203,11 @@ public class PFMFile { String glyph1=Glyphs.tex8r[g1]; String glyph2=Glyphs.tex8r[g2]; - Hashtable adjTab=(Hashtable)kerningTab.get(glyph1); + Hashtable adjTab=(Hashtable)kerningTab.get(new Integer(g1)); if (adjTab==null) adjTab=new Hashtable(); - adjTab.put(glyph2, new Integer(adj)); - kerningTab.put(glyph1, adjTab); + adjTab.put(new Integer(g2), new Integer(adj)); + kerningTab.put(new Integer(g1), adjTab); } } diff --git a/src/org/apache/fop/fonts/TTFCmapEntry.java b/src/org/apache/fop/fonts/TTFCmapEntry.java index af1eb5c00..3e8dd746a 100644 --- a/src/org/apache/fop/fonts/TTFCmapEntry.java +++ b/src/org/apache/fop/fonts/TTFCmapEntry.java @@ -50,11 +50,38 @@ */ package org.apache.fop.fonts; -class TTFCmapEntry { - int platform_id; - int encoding_id; - long offset; - - TTFCmapEntry() { - } +/** + * The CMap entry contains information of a Unicode range and the + * the glyph indexes related to the range + */ +public class TTFCmapEntry { + public int unicodeStart; + public int unicodeEnd; + public int glyphStartIndex; + + TTFCmapEntry() { + unicodeStart = 0; + unicodeEnd = 0; + glyphStartIndex = 0; + } + + TTFCmapEntry (int unicodeStart, + int unicodeEnd, + int glyphStartIndex) { + this.unicodeStart = unicodeStart; + this.unicodeEnd = unicodeEnd; + this.glyphStartIndex = glyphStartIndex; + } + + public boolean equals (Object o) { + if (o instanceof TTFCmapEntry) { + TTFCmapEntry ce = (TTFCmapEntry)o; + if (ce.unicodeStart == this.unicodeStart && + ce.unicodeEnd == this.unicodeEnd && + ce.glyphStartIndex == this.glyphStartIndex) + return true; + } + return false; + } + } diff --git a/src/org/apache/fop/fonts/TTFDirTabEntry.java b/src/org/apache/fop/fonts/TTFDirTabEntry.java index 42fed9f3a..30f0c4cfe 100644 --- a/src/org/apache/fop/fonts/TTFDirTabEntry.java +++ b/src/org/apache/fop/fonts/TTFDirTabEntry.java @@ -52,34 +52,36 @@ package org.apache.fop.fonts; import java.io.*; class TTFDirTabEntry { - byte[] tag; - int checksum; - long offset; - long length; - - TTFDirTabEntry() { - tag = new byte[4]; - } - - /** Read Dir Tab, return tag name */ - public String read(FontFileReader in) throws IOException { - tag[0]=in.readTTFByte(); - tag[1]=in.readTTFByte(); - tag[2]=in.readTTFByte(); - tag[3]=in.readTTFByte(); - - in.skip(4); // Skip checksum - - offset=in.readTTFULong(); - length=in.readTTFULong(); - - System.out.println ("Read dir tab [" + tag[0]+ - " "+tag[1] + - " "+tag[2] + - " "+tag[3] + - "] offset: " + offset + - " length: " + length + - " name: " + new String(tag)); - return new String(tag, "ISO-8859-1"); - } + byte[] tag; + int checksum; + long offset; + long length; + + TTFDirTabEntry() { + tag = new byte[4]; + } + + /** Read Dir Tab, return tag name */ + public String read(FontFileReader in) throws IOException { + tag[0]=in.readTTFByte(); + tag[1]=in.readTTFByte(); + tag[2]=in.readTTFByte(); + tag[3]=in.readTTFByte(); + + in.skip(4); // Skip checksum + + offset=in.readTTFULong(); + length=in.readTTFULong(); + + /* + System.out.println ("Read dir tab [" + tag[0]+ + " "+tag[1] + + " "+tag[2] + + " "+tag[3] + + "] offset: " + offset + + " length: " + length + + " name: " + new String(tag)); + */ + return new String(tag, "ISO-8859-1"); + } } diff --git a/src/org/apache/fop/fonts/TTFFile.java b/src/org/apache/fop/fonts/TTFFile.java index 06987e77a..02c49bfe1 100644 --- a/src/org/apache/fop/fonts/TTFFile.java +++ b/src/org/apache/fop/fonts/TTFFile.java @@ -50,274 +50,823 @@ */ package org.apache.fop.fonts; import java.io.*; +import java.util.Enumeration; import java.util.Hashtable; +import java.util.Vector; +/** + * Reads a TrueType file or a TrueType Collection. + * The TrueType spec can be found at the Microsoft + * Typography site: http://www.microsoft.com/truetype/ + */ public class TTFFile { - static final byte NTABS = 24; - static final int NMACGLYPHS = 258; - static final int MAX_CHAR_CODE = 255; - static final int ENC_BUF_SIZE = 1024; - - boolean is_embeddable=true; - boolean hasSerifs=true; - Hashtable dirTabs; - Hashtable kerningTab; - - /** Position inputstream to position indicated - in the dirtab offset + offset */ - void seek_tab(FontFileReader in, String name, long offset) - throws IOException { - TTFDirTabEntry dt=(TTFDirTabEntry)dirTabs.get(name); - if (dt==null) { - System.out.println("Dirtab " + name + " not found."); - return; - } - - in.seek_set(dt.offset+offset); - } - - int get_ttf_funit(int n) { - int ret; - if (n < 0) { - long rest1=n % upem; - long storrest=1000*rest1; - long ledd2=rest1/storrest; - ret = -((-1000*n)/upem - (int)ledd2); - } else { - ret = (n/upem)*1000 + ((n % upem)*1000)/upem; - } - - return ret; - } - - int upem; - int ntabs; - int nhmtx; - int post_format; - int loca_format; - int nglyphs; - int nmglyphs; - int names_count; - - - - TTFDirTabEntry dir_tab; - TTFMtxEntry mtx_tab[]; - int[] mtx_encoded=null; - boolean reencoded=false; - String enc_names; - - String fontName=""; - String fullName=""; - String notice=""; - String familyName=""; - String subFamilyName=""; - - long italicAngle = 0; - long isFixedPitch = 0; - int fontBBox1 = 0; - int fontBBox2 = 0; - int fontBBox3 = 0; - int fontBBox4 = 0; - int capHeight = 0; - int underlinePosition = 0; - int underlineThickness = 0; - int xHeight = 0; - int ascender = 0; - int descender = 0; - - public void readFont(FontFileReader in) throws IOException { - int i, j, k, l, platform_id, encoding_id, language_id; - long n; - TTFDirTabEntry[] pd; - TTFMtxEntry[] pm; - String[] ps_glyphs_buf; - - in.skip(4); // TTF_FIXED_SIZE - ntabs=in.readTTFUShort(); - in.skip(6); // 3xTTF_USHORT_SIZE; - - // Read Dir tabs - dirTabs=new Hashtable(); - pd=new TTFDirTabEntry[ntabs]; - //System.out.println("Reading " + ntabs + " dir tables"); - for (i=0; i < ntabs; i++) { - pd[i]=new TTFDirTabEntry(); - dirTabs.put(pd[i].read(in), - pd[i]); - } - - seek_tab(in, "head", 2*4 + 2*4 + 2); - upem=in.readTTFUShort(); - - in.skip(16); - - fontBBox1=in.readTTFShort(); - fontBBox2=in.readTTFShort(); - fontBBox3=in.readTTFShort(); - fontBBox4=in.readTTFShort(); - - in.skip(2+2+2); - - loca_format=in.readTTFShort(); - - seek_tab(in, "maxp", 4); - nglyphs=in.readTTFUShort(); - //System.out.println("nglyphs= " + nglyphs); - mtx_tab=new TTFMtxEntry[nglyphs]; - - for (i=0; i < nglyphs; i++) - mtx_tab[i]=new TTFMtxEntry(); - - seek_tab(in, "hhea", 4); - ascender=in.readTTFShort(); // Use sTypoAscender in "OS/2" table? - descender=in.readTTFShort(); // Use sTypoDescender in "OS/2" table? - - in.skip(2+2+3*2+8*2); - nhmtx=in.readTTFUShort(); - //System.out.println("nhmtx: " + nhmtx); - seek_tab(in, "hmtx", 0); - for (i=0; i < nhmtx; i++) { - mtx_tab[i].wx=in.readTTFUShort(); - in.skip(2); - } - // NB: Her skal det settes mer wx. - - seek_tab(in, "post", 0); - post_format=in.readTTFLong(); - italicAngle=in.readTTFULong(); - //System.out.println("Italic angle: " + italicAngle); - underlinePosition=in.readTTFShort(); - underlineThickness=in.readTTFShort(); - isFixedPitch=in.readTTFULong(); - - in.skip(4*4); - - switch (post_format) { - case 0x00010000: - //System.out.println("Postscript format 1"); - for (i=0; i<Glyphs.mac_glyph_names.length; i++) { - mtx_tab[i].name=Glyphs.mac_glyph_names[i]; - } - break; - case 0x00020000: - //System.out.println("Postscript format 2"); - l = in.readTTFUShort(); - for (i=0; i < l ; i++) { - mtx_tab[i].index=in.readTTFUShort(); - } - - TTFDirTabEntry dirTab= - (TTFDirTabEntry)dirTabs.get("post"); - if (dirTab==null) - System.out.println("Can't find table 'post'"); - - n=dirTab.length - (in.getCurrentPos() - dirTab.offset); - ps_glyphs_buf=new String[(int)n]; - int nn=(ps_glyphs_buf.length < nglyphs) ? - ps_glyphs_buf.length : nglyphs; - //System.out.println("Reading " + n + " glyphnames"); - for (i=0; i < nn; i++) { - ps_glyphs_buf[i]=in.readTTFString(in.readTTFUByte()); - } - - for (i=0; i < l; i++) { - if (mtx_tab[i].index < NMACGLYPHS) { - mtx_tab[i].name = Glyphs.mac_glyph_names[mtx_tab[i].index]; - } else { - k = mtx_tab[i].index - NMACGLYPHS ; - mtx_tab[i].name=ps_glyphs_buf[k]; + static final byte NTABS = 24; + static final int NMACGLYPHS = 258; + static final int MAX_CHAR_CODE = 255; + static final int ENC_BUF_SIZE = 1024; + + static String encoding="WinAnsiEncoding"; // Deafult encoding + short firstChar=0; + boolean is_embeddable=true; + boolean hasSerifs=true; + Hashtable dirTabs; // Table directory + Hashtable kerningTab; // for CIDs + Hashtable ansiKerningTab; // For winAnsiEncoding + Vector cmaps; + Vector unicodeMapping; // + + int upem; // unitsPerEm from "head" table + int nhmtx; // Number of horizontal metrics + int post_format; + int loca_format; + int nglyphs; // Number of glyphs in font (read from "maxp" table) + int nmglyphs; // Used in fixWidths - remove? + + TTFMtxEntry mtx_tab[]; // Contains glyph data + int[] mtx_encoded=null; + + String fontName=""; + String fullName=""; + String notice=""; + String familyName=""; + String subFamilyName=""; + + long italicAngle = 0; + long isFixedPitch = 0; + int fontBBox1 = 0; + int fontBBox2 = 0; + int fontBBox3 = 0; + int fontBBox4 = 0; + int capHeight = 0; + int underlinePosition = 0; + int underlineThickness = 0; + int xHeight = 0; + int ascender = 0; + int descender = 0; + + short lastChar = 0; + + int ansiWidth[]; + Hashtable ansiIndex; + + /** Position inputstream to position indicated + in the dirtab offset + offset */ + void seek_tab(FontFileReader in, String name, long offset) + throws IOException { + TTFDirTabEntry dt=(TTFDirTabEntry)dirTabs.get(name); + if (dt==null) { + System.out.println("Dirtab " + name + " not found."); + return; + } + + in.seek_set(dt.offset+offset); + } + + /** Convert from truetype unit to pdf unit based on the + * unitsPerEm field in the "head" table + @param n truetype unit + @return pdf unit + */ + int get_ttf_funit(int n) { + int ret; + if (n < 0) { + long rest1=n % upem; + long storrest=1000*rest1; + long ledd2=rest1/storrest; + ret = -((-1000*n)/upem - (int)ledd2); + } else { + ret = (n/upem)*1000 + ((n % upem)*1000)/upem; + } + + return ret; + } + + /** Read the cmap table, + * return false if the table is not present or only unsupported + * tables are present. Currently only unicode cmaps are supported. + * Set the unicodeIndex in the TTFMtxEntries and fills in the + * cmaps vector. + */ + private boolean readCMAP(FontFileReader in) + throws IOException { + + unicodeMapping = new Vector(); + + /** Read CMAP table and correct mtx_tab.index*/ + int mtxPtr=0; + + seek_tab(in, "cmap", 2); + int num_cmap=in.readTTFUShort(); // Number of cmap subtables + long cmap_unioffset=0; + + //System.out.println(num_cmap+" cmap tables"); + + /* Read offset for all tables + We are only interested in the unicode table + */ + for (int i=0; i< num_cmap; i++) { + int cmap_pid=in.readTTFUShort(); + int cmap_eid=in.readTTFUShort(); + long cmap_offset=in.readTTFULong(); + + //System.out.println("Platform ID: "+cmap_pid+ + // " Encoding: "+cmap_eid); + + if (cmap_pid==3 && cmap_eid==1) + cmap_unioffset=cmap_offset; + } + + if (cmap_unioffset <= 0) { + System.out.println("Unicode cmap table not present"); + return false; + } + + // Read unicode cmap + seek_tab(in, "cmap", cmap_unioffset); + int cmap_format=in.readTTFUShort(); + int cmap_length=in.readTTFUShort(); + + //System.out.println("CMAP format: "+cmap_format); + if (cmap_format==4) { + in.skip(2); // Skip version number + int cmap_segCountX2=in.readTTFUShort(); + int cmap_searchRange=in.readTTFUShort(); + int cmap_entrySelector=in.readTTFUShort(); + int cmap_rangeShift=in.readTTFUShort(); + + /* + System.out.println("segCountX2 : "+cmap_segCountX2); + System.out.println("searchRange : "+cmap_searchRange); + System.out.println("entrySelector: "+cmap_entrySelector); + System.out.println("rangeShift : "+cmap_rangeShift); + */ + + int cmap_endCounts[]=new int[cmap_segCountX2/2]; + int cmap_startCounts[]=new int[cmap_segCountX2/2]; + int cmap_deltas[]=new int[cmap_segCountX2/2]; + int cmap_rangeOffsets[]=new int[cmap_segCountX2/2]; + + for (int i=0; i < (cmap_segCountX2/2); i++) { + cmap_endCounts[i]=in.readTTFUShort(); + } + + in.skip(2); // Skip reservedPad + + for (int i=0; i < (cmap_segCountX2/2); i++) { + cmap_startCounts[i]=in.readTTFUShort(); + } + + for (int i=0; i < (cmap_segCountX2/2); i++) { + cmap_deltas[i]=in.readTTFShort(); + } + + int startRangeOffset=in.getCurrentPos(); + + for (int i=0; i < (cmap_segCountX2/2); i++) { + cmap_rangeOffsets[i]=in.readTTFShort(); + } + + int glyphIdArrayOffset=in.getCurrentPos(); + + // Insert the unicode id for the glyphs in mtx_tab + // and fill in the cmaps Vector + + for (int i = 0; i < cmap_startCounts.length; i++) { + /* + System.out.println(i+ ": "+cmap_startCounts[i]+ + " - "+cmap_endCounts[i]); + */ + for (int j=cmap_startCounts[i]; + j <= cmap_endCounts[i]; j++) { + + // Update lastChar + if (j < 256 && j > lastChar) + lastChar = (short)j; + + if (mtxPtr < mtx_tab.length) { + if (cmap_rangeOffsets[i] != 0) { + int glyphOffset = glyphIdArrayOffset+ + ((cmap_rangeOffsets[i]/2)+ + (j-cmap_startCounts[i])+ + (i)- + cmap_segCountX2/2)*2; + in.seek_set(glyphOffset); + int glyphIdx=(in.readTTFUShort()+ + cmap_deltas[i]) & 0xffff; + + unicodeMapping.addElement( + new UnicodeMapping(glyphIdx, j)); + mtx_tab[glyphIdx].unicodeIndex.addElement(new Integer(j)); + + + // Also add winAnsiWidth + if (false) { + int d = j; + if (j > 127) + d = (int) + org.apache.fop.render.pdf.CodePointMapping.map[j]; + if (d < ansiWidth.length) + ansiWidth[d] = mtx_tab[glyphIdx].wx; + } else { + Vector v= + (Vector)ansiIndex.get(new Integer(j)); + if (v != null) { + for (Enumeration e=v.elements(); + e.hasMoreElements();) { + Integer aIdx = (Integer)e.nextElement(); + ansiWidth[aIdx.intValue()]= + mtx_tab[glyphIdx].wx; + /* + System.out.println("Added width "+ + mtx_tab[glyphIdx].wx + + " uni: " + j + + " ansi: " + aIdx.intValue()); + */ + } + } + } + /* + System.out.println("Idx: "+ + glyphIdx + + " Delta: " + cmap_deltas[i]+ + " Unicode: " + j + + " name: " + + mtx_tab[glyphIdx].name); + */ + + } else { + int glyphIdx=(j+cmap_deltas[i]) & 0xffff; + + mtx_tab[glyphIdx].unicodeIndex.addElement(new Integer(j)); + + unicodeMapping.addElement( + new UnicodeMapping(glyphIdx, + j)); + + // Also add winAnsiWidth + if (false) { + int d = j; + if (j > 127) + d = (int) + org.apache.fop.render.pdf.CodePointMapping.map[j]; + + if (d < ansiWidth.length) + ansiWidth[d] = mtx_tab[glyphIdx].wx; + } else { + Vector v= + (Vector)ansiIndex.get(new Integer(j)); + if (v != null) { + for (Enumeration e=v.elements(); + e.hasMoreElements();) { + Integer aIdx = (Integer)e.nextElement(); + ansiWidth[aIdx.intValue()]= + mtx_tab[glyphIdx].wx; + } + } + } + + /* + System.out.println("IIdx: "+ + mtxPtr + + " Delta: " + cmap_deltas[i]+ + " Unicode: " + j + + " name: " + + mtx_tab[(j+cmap_deltas[i]) & 0xffff].name); + */ + } + mtxPtr++; + } } - } - - break; - case 0x00030000: - //System.out.println("Postscript format 3 - index"); - break; - default: - //System.out.println("Unknown format : " + post_format); - } - - // Check if font is embeddable - if (dirTabs.get("OS/2") != null) { - seek_tab(in, "OS/2", 2*4); - int fsType=in.readTTFUShort(); - if ((fsType & 2) == 0) - is_embeddable=false; - else + } + } + return true; + } + + + + /** + * Print first char/last char + */ + private void print_max_min() { + int min=255; + int max=0; + for (int i=0; i < mtx_tab.length; i++) { + if (mtx_tab[i].index < min) + min = mtx_tab[i].index; + if (mtx_tab[i].index > max) + max = mtx_tab[i].index; + } + System.out.println("Min: "+min); + System.out.println("Max: "+max); + } + + public void readFont(FontFileReader in) throws IOException { + readFont(in, (String)null); + } + + /** initialize the ansiWidths array (for winAnsiEncoding) + * and fill with the missingwidth + */ + private void initAnsiWidths() { + ansiWidth=new int[256]; + for (int i = 0; i < 256; i++) + ansiWidth[i] = mtx_tab[0].wx; + + // Create an index hash to the ansiWidth + // Can't just index the winAnsiEncoding when inserting widths + // same char (eg bullet) is repeated more than one place + ansiIndex=new Hashtable(); + for (int i=32; i < Glyphs.winAnsiEncoding.length; i++) { + Integer ansi = new Integer(i); + Integer uni = new Integer((int)Glyphs.winAnsiEncoding[i]); + + Vector v = (Vector)ansiIndex.get(uni); + if (v == null) { + v = new Vector(); + ansiIndex.put(uni, v); + } + v.addElement(ansi); + } + } + + + /** + * Read the font data + * If the fontfile is a TrueType Collection (.ttf file) + * The name of the font to read data for must be supplied, + * else the name is ignored + */ + public void readFont(FontFileReader in, String name) throws IOException { + + /* Check if TrueType collection, and that the name + exists in the collection + */ + if (!checkTTC(in, name)) + throw new IOException("Failed to read font"); + + readDirTabs(in); + readFontHeader(in); + readHorizontalHeader(in); + readHorizontalMetrics(in); + initAnsiWidths(); + readPostscript(in); + readOS2(in); + readIndexToLocation(in); + readGlyf(in); + readName(in); + readPCLT(in); + readCMAP(in); // Read cmap table and fill in ansiwidths + createCMaps(); // Create cmaps for bfentries + //print_max_min(); + + readKerning(in); + } + + private void createCMaps() { + cmaps = new Vector(); + TTFCmapEntry tce = new TTFCmapEntry(); + + Enumeration e = unicodeMapping.elements(); + UnicodeMapping um = (UnicodeMapping)e.nextElement(); + UnicodeMapping lastMapping = um; + + tce.unicodeStart = um.uIdx; + tce.glyphStartIndex = um.gIdx; + + while (e.hasMoreElements()) { + um = (UnicodeMapping)e.nextElement(); + if (((lastMapping.uIdx+1) != um.uIdx) || + ((lastMapping.gIdx+1) != um.gIdx)) { + tce.unicodeEnd = lastMapping.uIdx; + cmaps.addElement(tce); + + tce = new TTFCmapEntry(); + tce.unicodeStart = um.uIdx; + tce.glyphStartIndex = um.gIdx; + } + lastMapping = um; + } + + tce.unicodeEnd = um.uIdx; + cmaps.addElement(tce); + } + + public void printStuff() { + System.out.println("Font name: " + fontName); + System.out.println("Full name: " + fullName); + System.out.println("Family name: " + familyName); + System.out.println("Subfamily name: " + subFamilyName); + System.out.println("Notice: " + notice); + System.out.println("xHeight: " + (int)get_ttf_funit(xHeight)); + System.out.println("capheight: " + (int)get_ttf_funit(capHeight)); + + int italic=(int)(italicAngle>>16); + System.out.println("Italic: " + italic); + System.out.print("ItalicAngle: " + (short)(italicAngle/0x10000)); + if ((italicAngle % 0x10000) > 0 ) + System.out.print("."+(short)((italicAngle % 0x10000)*1000)/0x10000); + System.out.println(); + System.out.println("Ascender: " + get_ttf_funit(ascender)); + System.out.println("Descender: " + get_ttf_funit(descender)); + System.out.println("FontBBox: [" + (int)get_ttf_funit(fontBBox1) + + " " + (int)get_ttf_funit(fontBBox2) + + " " + (int)get_ttf_funit(fontBBox3) + + " " + (int)get_ttf_funit(fontBBox4)+"]"); + } + + public static void main(String[] args) { + try { + TTFFile ttfFile=new TTFFile(); + FontFileReader reader= + new FontFileReader(args[0]); + + String name=null; + if (args.length >= 2) + name=args[1]; + + ttfFile.readFont(reader, name); + ttfFile.printStuff(); + + } catch (IOException ioe) { + System.out.println(ioe.toString()); + } + } + + public String getWindowsName() { + return new String(familyName+","+subFamilyName); + } + + public String getPostscriptName() { + if ("Regular".equals(subFamilyName) || + "Roman".equals(subFamilyName)) + return familyName; + else + return familyName+","+subFamilyName; + } + public String getFamilyName() { + return familyName; + } + public String getCharSetName() { + return encoding; + } + public int getCapHeight() { + return (int)get_ttf_funit(capHeight); + } + public int getXHeight() { + return (int)get_ttf_funit(xHeight); + } + + public int getFlags() { + int flags=32; // Use Adobe Standard charset + if (italicAngle != 0) + flags = flags | 64; + if (isFixedPitch != 0) + flags = flags | 2; + if (hasSerifs) + flags = flags | 1; + return flags; + } + + + public String getStemV() { + return "0"; + } + + public String getItalicAngle() { + String ia=Short.toString((short)(italicAngle/0x10000)); + if ((italicAngle % 0x10000) > 0 ) + ia=ia+("."+Short.toString((short)((short)((italicAngle % 0x10000)*1000)/0x10000))); + + return ia; + } + + public int[] getFontBBox() { + int[] fbb=new int[4]; + fbb[0]=(int)get_ttf_funit(fontBBox1); + fbb[1]=(int)get_ttf_funit(fontBBox2); + fbb[2]=(int)get_ttf_funit(fontBBox3); + fbb[3]=(int)get_ttf_funit(fontBBox4); + + return fbb; + } + + public int getLowerCaseAscent() { + return (int)get_ttf_funit(ascender); + } + + public int getLowerCaseDescent() { + return (int)get_ttf_funit(descender); + } + + // This is only for WinAnsiEncoding, so the last char is + // the last char < 256 + public short getLastChar() { + return lastChar; + } + + public short getFirstChar() { + return firstChar; + } + + public int[] getWidths() { + int[] wx = new int[mtx_tab.length]; + for (int i = 0; i < wx.length; i++) + wx[i] = (int)get_ttf_funit(mtx_tab[i].wx); + + return wx; + } + + public int getCharWidth(int idx) { + return (int)get_ttf_funit(ansiWidth[idx]); + } + + public Hashtable getKerning() { + return kerningTab; + } + + public Hashtable getAnsiKerning() { + return ansiKerningTab; + } + + public boolean isEmbeddable() { + return is_embeddable; + } + + + /** + * Read Table Directory from the current position in the + * FontFileReader and fill the global Hashtable dirTabs + * with the table name (String) as key and a TTFDirTabEntry + * as value. + */ + private void readDirTabs(FontFileReader in) throws IOException { + in.skip(4); // TTF_FIXED_SIZE + int ntabs=in.readTTFUShort(); + in.skip(6); // 3xTTF_USHORT_SIZE + + dirTabs=new Hashtable(); + TTFDirTabEntry[] pd=new TTFDirTabEntry[ntabs]; + //System.out.println("Reading " + ntabs + " dir tables"); + for (int i=0; i < ntabs; i++) { + pd[i]=new TTFDirTabEntry(); + dirTabs.put(pd[i].read(in), + pd[i]); + } + } + + /** + * Read the "head" table, this reads the bounding box and + * sets the upem (unitsPerEM) variable + */ + private void readFontHeader(FontFileReader in) throws IOException { + seek_tab(in, "head", 2*4 + 2*4 + 2); + upem=in.readTTFUShort(); + + in.skip(16); + + fontBBox1=in.readTTFShort(); + fontBBox2=in.readTTFShort(); + fontBBox3=in.readTTFShort(); + fontBBox4=in.readTTFShort(); + + in.skip(2+2+2); + + loca_format=in.readTTFShort(); + } + + /** + * Read the number of glyphs from the "maxp" table + */ + private void getNumGlyphs(FontFileReader in) throws IOException { + seek_tab(in, "maxp", 4); + nglyphs=in.readTTFUShort(); + System.out.println("Number of glyphs in font: " + nglyphs); + } + + + /** Read the "hhea" table to find the ascender and descender and + * size of "hmtx" table, i.e. a fixed size font might have only + * one width + */ + private void readHorizontalHeader(FontFileReader in) throws IOException { + seek_tab(in, "hhea", 4); + ascender=in.readTTFShort(); // Use sTypoAscender in "OS/2" table? + descender=in.readTTFShort(); // Use sTypoDescender in "OS/2" table? + + in.skip(2+2+3*2+8*2); + nhmtx=in.readTTFUShort(); + //System.out.println("Number of horizontal metrics: " + nhmtx); + } + + /** + * Read "hmtx" table and put the horizontal metrics + * in the mtx_tab array. If the number of metrics is less + * than the number of glyphs (eg fixed size fonts), extend + * the mtx_tab array and fill in the missing widths + */ + private void readHorizontalMetrics(FontFileReader in) throws IOException { + seek_tab(in, "hmtx", 0); + + int mtx_size=(nglyphs > nhmtx) ? nglyphs : nhmtx; + mtx_tab=new TTFMtxEntry[mtx_size]; + + //System.out.println("*** Widths array: \n"); + for (int i=0; i < mtx_size; i++) + mtx_tab[i]=new TTFMtxEntry(); + for (int i=0; i < nhmtx; i++) { + mtx_tab[i].wx=in.readTTFUShort(); + /* + System.out.println(" width["+i+"] = "+ + get_ttf_funit(mtx_tab[i].wx)+";"); + */ + in.skip(2); // Skip left side bearing + } + + if (nhmtx < mtx_size) { + // Fill in the missing widths + int lastWidth=mtx_tab[nhmtx-1].wx; + for (int i=nhmtx; i < mtx_size; i++) { + mtx_tab[i].wx=lastWidth; + } + } + } + + + /** + * Read the "post" table + * containing the postscript names of the glyphs. + */ + private final void readPostscript(FontFileReader in) throws IOException { + String[] ps_glyphs_buf; + int i, k, l; + + seek_tab(in, "post", 0); + post_format=in.readTTFLong(); + italicAngle=in.readTTFULong(); + underlinePosition=in.readTTFShort(); + underlineThickness=in.readTTFShort(); + isFixedPitch=in.readTTFULong(); + + in.skip(4*4); + + //System.out.println("Post format: "+post_format); + switch (post_format) { + case 0x00010000: + //System.out.println("Postscript format 1"); + for (i=0; i < Glyphs.mac_glyph_names.length; i++) { + mtx_tab[i].name=Glyphs.mac_glyph_names[i]; + } + break; + case 0x00020000: + //System.out.println("Postscript format 2"); + int numGlyphStrings=0; + l = in.readTTFUShort(); // Num Glyphs + //short minIndex=256; + for (i=0; i < l ; i++) { // Read indexes + mtx_tab[i].index=in.readTTFUShort(); + //if (minIndex > mtx_tab[i].index) + //minIndex=(short)mtx_tab[i].index; + + if (mtx_tab[i].index > 257) + numGlyphStrings++; + + //System.out.println("Post index: "+mtx_tab[i].index); + } + //firstChar=minIndex; + ps_glyphs_buf=new String[numGlyphStrings]; + //System.out.println("Reading " + numGlyphStrings + + // " glyphnames"+ + // ", was n num glyphs="+l); + for (i=0; i < ps_glyphs_buf.length; i++) { + ps_glyphs_buf[i]=in.readTTFString(in.readTTFUByte()); + } + + for (i=0; i < l; i++) { + if (mtx_tab[i].index < NMACGLYPHS) { + mtx_tab[i].name = Glyphs.mac_glyph_names[mtx_tab[i].index]; + } else { + k = mtx_tab[i].index - NMACGLYPHS ; + /* + System.out.println(k+" i="+i+" mtx="+mtx_tab.length+ + " ps="+ps_glyphs_buf.length); + */ + mtx_tab[i].name=ps_glyphs_buf[k]; + } + } + + break; + case 0x00030000: + // Postscript format 3 contains no glyph names + System.out.println("Postscript format 3"); + break; + default: + System.out.println("Unknown Postscript format : " + + post_format); + } + } + + + /** + * Read the "OS/2" table + */ + private final void readOS2(FontFileReader in) throws IOException { + // Check if font is embeddable + if (dirTabs.get("OS/2") != null) { + seek_tab(in, "OS/2", 2*4); + int fsType=in.readTTFUShort(); + if ((fsType & 2) == fsType) + is_embeddable=false; + else + is_embeddable=true; + } else is_embeddable=true; - } else - is_embeddable=true; - - - - seek_tab(in, "loca", 0); - for (i=0; i < nglyphs ; i++) { - mtx_tab[i].offset = (loca_format == 1 ? in.readTTFULong() : - (in.readTTFUShort() << 1)); - } - - TTFDirTabEntry dirTab = - (TTFDirTabEntry)dirTabs.get("glyf"); - for (i=0; i < (nglyphs-1); i++) { - if (mtx_tab[i].offset != mtx_tab[i+1].offset) { - in.seek_set(dirTab.offset + mtx_tab[i].offset); - in.skip(2); - mtx_tab[i].bbox[0]=in.readTTFShort(); - mtx_tab[i].bbox[1]=in.readTTFShort(); - mtx_tab[i].bbox[2]=in.readTTFShort(); - mtx_tab[i].bbox[3]=in.readTTFShort(); - } else { - mtx_tab[i].bbox[0]=mtx_tab[0].bbox[0]; - mtx_tab[i].bbox[1]=mtx_tab[0].bbox[1]; - mtx_tab[i].bbox[2]=mtx_tab[0].bbox[2]; - mtx_tab[i].bbox[3]=mtx_tab[0].bbox[3]; - } - } - - //System.out.println("nglyf="+nglyphs+" mtx="+mtx_tab.length); + } + + /** + * Read the "loca" table + */ + private final void readIndexToLocation(FontFileReader in) + throws IOException { + seek_tab(in, "loca", 0); + for (int i=0; i < nglyphs ; i++) { + mtx_tab[i].offset = (loca_format == 1 ? in.readTTFULong() : + (in.readTTFUShort() << 1)); + } + } + + /** + * Read the "glyf" table to find the bounding boxes + */ + private final void readGlyf(FontFileReader in) throws IOException { + TTFDirTabEntry dirTab = + (TTFDirTabEntry)dirTabs.get("glyf"); + for (int i=0; i < (nglyphs-1); i++) { + if (mtx_tab[i].offset != mtx_tab[i+1].offset) { + in.seek_set(dirTab.offset + mtx_tab[i].offset); + in.skip(2); + mtx_tab[i].bbox[0]=in.readTTFShort(); + mtx_tab[i].bbox[1]=in.readTTFShort(); + mtx_tab[i].bbox[2]=in.readTTFShort(); + mtx_tab[i].bbox[3]=in.readTTFShort(); + } else { + mtx_tab[i].bbox[0]=mtx_tab[0].bbox[0]; + mtx_tab[i].bbox[1]=mtx_tab[0].bbox[1]; + mtx_tab[i].bbox[2]=mtx_tab[0].bbox[2]; + mtx_tab[i].bbox[3]=mtx_tab[0].bbox[3]; + } + } - - n=((TTFDirTabEntry)dirTabs.get("glyf")).offset; - for (i=0; i < nglyphs; i++) { - if ((i+1) >= mtx_tab.length || - mtx_tab[i].offset != mtx_tab[i+1].offset) { - in.seek_set(n+mtx_tab[i].offset); - in.skip(2); - mtx_tab[i].bbox[0]=in.readTTFShort(); - mtx_tab[i].bbox[1]=in.readTTFShort(); - mtx_tab[i].bbox[2]=in.readTTFShort(); - mtx_tab[i].bbox[3]=in.readTTFShort(); - } else { - mtx_tab[i].bbox[0]=mtx_tab[0].bbox[0]; - mtx_tab[i].bbox[1]=mtx_tab[0].bbox[0]; - mtx_tab[i].bbox[2]=mtx_tab[0].bbox[0]; - mtx_tab[i].bbox[3]=mtx_tab[0].bbox[0]; - } - //System.out.println(mtx_tab[i].toString(this)); - } - - seek_tab(in, "name", 2); - i = in.getCurrentPos(); - n = in.readTTFUShort(); - j = in.readTTFUShort() + i - 2; - i += 2*2; - - while (n-- > 0) { - in.seek_set(i); - platform_id=in.readTTFUShort(); - encoding_id=in.readTTFUShort(); - language_id=in.readTTFUShort(); - //System.out.println("Platform id: " + language_id); - //System.out.println("Encoding id: " + language_id); - //System.out.println("Language id: " + language_id); - k=in.readTTFUShort(); - l=in.readTTFUShort(); - - if ((platform_id==1 && encoding_id==0) && - (k==1 || k==2 || k==0 || k==4 || k==6)) { - in.seek_set(j+in.readTTFUShort()); - String txt = in.readTTFString(l); + + long n=((TTFDirTabEntry)dirTabs.get("glyf")).offset; + for (int i=0; i < nglyphs; i++) { + if ((i+1) >= mtx_tab.length || + mtx_tab[i].offset != mtx_tab[i+1].offset) { + in.seek_set(n+mtx_tab[i].offset); + in.skip(2); + mtx_tab[i].bbox[0]=in.readTTFShort(); + mtx_tab[i].bbox[1]=in.readTTFShort(); + mtx_tab[i].bbox[2]=in.readTTFShort(); + mtx_tab[i].bbox[3]=in.readTTFShort(); + } else { + mtx_tab[i].bbox[0]=mtx_tab[0].bbox[0]; + mtx_tab[i].bbox[1]=mtx_tab[0].bbox[0]; + mtx_tab[i].bbox[2]=mtx_tab[0].bbox[0]; + mtx_tab[i].bbox[3]=mtx_tab[0].bbox[0]; + } + //System.out.println(mtx_tab[i].toString(this)); + } + } + + /** + * Read the "name" table + */ + private final void readName(FontFileReader in) throws IOException { + int platform_id, encoding_id, language_id; + + seek_tab(in, "name", 2); + int i = in.getCurrentPos(); + int n = in.readTTFUShort(); + int j = in.readTTFUShort() + i - 2; + i += 2*2; + + while (n-- > 0) { + //System.out.println("Iteration: "+n); + in.seek_set(i); + platform_id=in.readTTFUShort(); + encoding_id=in.readTTFUShort(); + language_id=in.readTTFUShort(); + + int k=in.readTTFUShort(); + int l=in.readTTFUShort(); + + if (((platform_id==1 || platform_id==3) && + (encoding_id==0 || encoding_id==1)) && + (k==1 || k==2 || k==0 || k==4 || k==6)) { +// if (k==1 || k==2 || k==0 || k==4 || k==6) { + in.seek_set(j+in.readTTFUShort()); + String txt = in.readTTFString(l); + //System.out.println(platform_id+" "+encoding_id+ + //" "+k+" "+txt); switch (k) { case 0: notice=txt; break; case 1: familyName=txt; break; @@ -327,202 +876,215 @@ public class TTFFile { } if (!notice.equals("") && !fullName.equals("") && !fontName.equals("") && !familyName.equals("") && - !subFamilyName.equals("")) - break; - } - i+=6*2; - } - - dirTab= - (TTFDirTabEntry)dirTabs.get("PCLT"); - if (dirTab!=null) { - in.seek_set(dirTab.offset + 4 + 4 + 2); - xHeight=in.readTTFUShort(); - in.skip(2*2); - capHeight=in.readTTFUShort(); - in.skip(2+16+8+6+1+1); - - int serifStyle=in.readTTFUByte(); - serifStyle=serifStyle >> 6; - serifStyle=serifStyle & 3; - if (serifStyle == 1) - hasSerifs=false; - else - hasSerifs=true; - - } else { - // Approximate capHeight from height of "H" - for (i=0; i < mtx_tab.length; i++) { - if ("H".equals(mtx_tab[i].name)) - capHeight=mtx_tab[i].bbox[3]- - mtx_tab[i].bbox[1]; - } - } - - // Read kerning - kerningTab=new Hashtable(); - dirTab= - (TTFDirTabEntry)dirTabs.get("kern"); - if (dirTab!=null) { - seek_tab(in, "kern", 2); - for (n=in.readTTFUShort(); n>0 ; n--) { + !subFamilyName.equals("")) { + break; + } + } + i+=6*2; + } + } + + /** + * Read the "PCLT" table to find xHeight and capHeight + */ + private final void readPCLT(FontFileReader in) throws IOException { + TTFDirTabEntry dirTab= + (TTFDirTabEntry)dirTabs.get("PCLT"); + if (dirTab!=null) { + in.seek_set(dirTab.offset + 4 + 4 + 2); + xHeight=in.readTTFUShort(); in.skip(2*2); - k=in.readTTFUShort(); - if (!((k & 1)!=0) || (k & 2)!=0 || (k & 4)!=0) - return; - if ((k >> 8) !=0) - continue; - - k=in.readTTFUShort(); - in.skip(3 * 2); - while (k-- > 0) { - i=in.readTTFUShort(); - j=in.readTTFUShort(); - int kpx=in.readTTFShort(); - if (kpx != 0) { - Hashtable adjTab=(Hashtable)kerningTab.get(mtx_tab[i].name); - if (adjTab==null) - adjTab=new java.util.Hashtable(); - adjTab.put(mtx_tab[j].name, new Integer((int)get_ttf_funit(kpx))); - kerningTab.put(mtx_tab[i].name, adjTab); - } + capHeight=in.readTTFUShort(); + in.skip(2+16+8+6+1+1); + + int serifStyle=in.readTTFUByte(); + serifStyle=serifStyle >> 6; + serifStyle=serifStyle & 3; + if (serifStyle == 1) + hasSerifs=false; + else + hasSerifs=true; + + } else { + // Approximate capHeight from height of "H" + // It's most unlikly that a font misses the PCLT table + // This also assumes that psocriptnames exists ("H") + // Should look it up int the cmap (that wouldn't help + // for charsets without H anyway...) + for (int i=0; i < mtx_tab.length; i++) { + if ("H".equals(mtx_tab[i].name)) + capHeight=mtx_tab[i].bbox[3]- + mtx_tab[i].bbox[1]; } - } - //System.out.println(kerningTab.toString()); - } - } - - - public void printStuff() { - System.out.println("Font name: " + fontName); - System.out.println("Full name: " + fullName); - System.out.println("Family name: " + familyName); - System.out.println("Subfamily name: " + subFamilyName); - System.out.println("Notice: " + notice); - System.out.println("xHeight: " + (int)get_ttf_funit(xHeight)); - System.out.println("capheight: " + (int)get_ttf_funit(capHeight)); - - int italic=(int)(italicAngle>>16); - System.out.println("Italic: " + italic); - System.out.print("ItalicAngle: " + (short)(italicAngle/0x10000)); - if ((italicAngle % 0x10000) > 0 ) - System.out.print("."+(short)((italicAngle % 0x10000)*1000)/0x10000); - System.out.println(); - System.out.println("Ascender: " + get_ttf_funit(ascender)); - System.out.println("Descender: " + get_ttf_funit(descender)); - System.out.println("FontBBox: [" + (int)get_ttf_funit(fontBBox1) + - " " + (int)get_ttf_funit(fontBBox2) + - " " + (int)get_ttf_funit(fontBBox3) + - " " + (int)get_ttf_funit(fontBBox4)+"]"); - } - - public static void main(String[] args) { - try { - TTFFile ttfFile=new TTFFile(); - FontFileReader reader= - new FontFileReader(args[0]); - - ttfFile.readFont(reader); - ttfFile.printStuff(); - - } catch (IOException ioe) { - System.out.println(ioe.toString()); - } - } - - public String getWindowsName() { - return new String(familyName+","+subFamilyName); - } - public String getPostscriptName() { - return fontName; - } - public String getCharSetName() { - return "WinAnsi"; - } - public int getCapHeight() { - return (int)get_ttf_funit(capHeight); - } - public int getXHeight() { - return (int)get_ttf_funit(xHeight); - } - public int getFlags() { - int flags=32; // Use Adobe Standard charset - if (italicAngle != 0) - flags = flags | 64; - if (isFixedPitch != 0) - flags = flags | 2; - if (hasSerifs) - flags = flags | 1; - return flags; - } - public String getStemV() { - return "0"; - } - public String getItalicAngle() { - String ia=Short.toString((short)(italicAngle/0x10000)); - if ((italicAngle % 0x10000) > 0 ) - ia=ia+("."+Short.toString((short)((short)((italicAngle % 0x10000)*1000)/0x10000))); - - return ia; - } - public int[] getFontBBox() { - int[] fbb=new int[4]; - fbb[0]=(int)get_ttf_funit(fontBBox1); - fbb[1]=(int)get_ttf_funit(fontBBox2); - fbb[2]=(int)get_ttf_funit(fontBBox3); - fbb[3]=(int)get_ttf_funit(fontBBox4); - - return fbb; - } - public int getLowerCaseAscent() { - return (int)get_ttf_funit(ascender); - } - public int getLowerCaseDescent() { - return (int)get_ttf_funit(descender); - } - public short getLastChar() { - fixWidth(); - return (short)(nmglyphs-1); - } - public short getFirstChar() { - return 0; - } - - public int getCharWidth(int idx) { - fixWidth(); - - return (int)get_ttf_funit(mtx_encoded[idx]); - } - - public Hashtable getKerning() { - return kerningTab; - } - - public boolean isEmbeddable() { - return is_embeddable; - } - private void fixWidth() { - if (reencoded) - return; - reencoded=true; - //System.out.println("Reencoding widths"); - nmglyphs=0; - mtx_encoded=new int[Glyphs.tex8r.length]; - - Hashtable existingGlyphs=new java.util.Hashtable(); - - for (int i=0; i < mtx_tab.length; i++) - existingGlyphs.put(mtx_tab[i].name, new Integer(mtx_tab[i].wx)); + } + } - - for (int i=0; i < Glyphs.tex8r.length; i++) { - nmglyphs++; - Integer wx=(Integer)existingGlyphs.get(Glyphs.tex8r[i]); - if (wx==null) - mtx_encoded[i]=0; - else - mtx_encoded[i]=wx.intValue(); - } - } + /** + * Read the kerning table, create a table for both CIDs and + * winAnsiEncoding + */ + private final void readKerning(FontFileReader in) throws IOException { + // Read kerning + kerningTab=new Hashtable(); + TTFDirTabEntry dirTab= + (TTFDirTabEntry)dirTabs.get("kern"); + if (dirTab!=null) { + seek_tab(in, "kern", 2); + for (int n=in.readTTFUShort(); n > 0 ; n--) { + in.skip(2*2); + int k=in.readTTFUShort(); + if (!((k & 1)!=0) || (k & 2)!=0 || (k & 4)!=0) + return; + if ((k >> 8) !=0) + continue; + + k=in.readTTFUShort(); + in.skip(3 * 2); + while (k-- > 0) { + int i=in.readTTFUShort(); + int j=in.readTTFUShort(); + int kpx=in.readTTFShort(); + if (kpx != 0) { + // CID table + Integer iObj=new Integer(i); + Hashtable adjTab= + (Hashtable)kerningTab.get(iObj); + if (adjTab==null) + adjTab=new java.util.Hashtable(); + adjTab.put(new Integer(j), + new Integer((int)get_ttf_funit(kpx))); + kerningTab.put(iObj, adjTab); + } + } + } + //System.out.println(kerningTab.toString()); + + // Create winAnsiEncoded kerning table + + ansiKerningTab = new Hashtable(); + for (Enumeration ae = kerningTab.keys(); + ae.hasMoreElements();) { + Integer cidKey = (Integer)ae.nextElement(); + Hashtable akpx = new Hashtable(); + Hashtable ckpx = (Hashtable)kerningTab.get(cidKey); + + for (Enumeration aee = ckpx.keys(); + aee.hasMoreElements();) { + Integer cidKey2 = (Integer)aee.nextElement(); + Integer kern = (Integer)ckpx.get(cidKey2); + + for (Enumeration uniMap = mtx_tab[cidKey2.intValue()].unicodeIndex.elements(); + uniMap.hasMoreElements();) { + Integer unicodeKey = (Integer)uniMap.nextElement(); + Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue()); + for (int u = 0; u < ansiKeys.length; u++) { + akpx.put(ansiKeys[u], kern); + } + } + } + + if (akpx.size() > 0) + for (Enumeration uniMap = mtx_tab[cidKey.intValue()].unicodeIndex.elements(); + uniMap.hasMoreElements();) { + Integer unicodeKey = (Integer)uniMap.nextElement(); + Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue()); + for (int u = 0; u < ansiKeys.length; u++) { + ansiKerningTab.put(ansiKeys[u], akpx); + } + } + } + } + } + + /** Return a vector with TTFCmapEntry + */ + public Vector getCMaps() { + return cmaps; + } + + /** + * Check if this is a TrueType collection and that the given + * name exists in the collection. + * If it does, set offset in fontfile to the beginning of + * the Table Directory for that font + @ return true if not collection or font name present, false + otherwise + */ + private final boolean checkTTC(FontFileReader in, String name) + throws IOException { + String tag=in.readTTFString(4); + + if ("ttcf".equals(tag)) { + // This is a TrueType Collection + in.skip(4); + + // Read directory offsets + int numDirectories=(int)in.readTTFULong(); + //int numDirectories=in.readTTFUShort(); + long[] dirOffsets=new long[numDirectories]; + for (int i=0; i < numDirectories; i++) { + dirOffsets[i]=in.readTTFULong(); + } + + System.out.println("This is a TrueType collection file with"+ + numDirectories + " fonts"); + System.out.println("Containing the following fonts: "); + // Read all the directories and name tables to check + // If the font exists - this is a bit ugly, but... + boolean found=false; + + // Iterate through all name tables even if font + // Is found, just to show all the names + for (int i=0; (i < numDirectories); i++) { + in.seek_set(dirOffsets[i]); + readDirTabs(in); + readName(in); + + if (fullName.equals(name)) { + found=true; + in.seek_set(dirOffsets[i]); + System.out.println("* " + fullName); + } else { + System.out.println(fullName); + } + + // Reset names + notice=""; + fullName=""; + familyName=""; + fontName=""; + subFamilyName=""; + } + + return found; + } else { + in.seek_set(0); + return true; + } + } + + /* Helper classes, they are not very efficient, but that really + doesn't matter... */ + private Integer[] unicodeToWinAnsi(int unicode) { + Vector ret=new Vector(); + for (int i=32; i < Glyphs.winAnsiEncoding.length; i++) + if (unicode == Glyphs.winAnsiEncoding[i]) + ret.addElement(new Integer(i)); + Integer[] itg = new Integer[ret.size()]; + ret.copyInto(itg); + return itg; + } } + +/** + * Key-value helper class + */ +class UnicodeMapping { + int uIdx; + int gIdx; + UnicodeMapping(int gIdx, int uIdx) { + this.uIdx = uIdx; + this.gIdx = gIdx; + } +} diff --git a/src/org/apache/fop/fonts/TTFMtxEntry.java b/src/org/apache/fop/fonts/TTFMtxEntry.java index c44c2c965..e1333bd24 100644 --- a/src/org/apache/fop/fonts/TTFMtxEntry.java +++ b/src/org/apache/fop/fonts/TTFMtxEntry.java @@ -51,27 +51,30 @@ package org.apache.fop.fonts; import java.io.*; +import java.util.Vector; class TTFMtxEntry { - int wx; - String name; - int index; - int[] bbox; - long offset; - byte found; - - TTFMtxEntry() { - name=""; - found=0; - bbox=new int[4]; - } - - public String toString(TTFFile t) { - return new String("Glyph "+name+ " index: " + index + - " bbox [ "+t.get_ttf_funit(bbox[0])+ - " " + t.get_ttf_funit(bbox[1]) + - " " + t.get_ttf_funit(bbox[2]) + - " " + t.get_ttf_funit(bbox[3]) + "]" + - "wx: "+t.get_ttf_funit(wx)); - } + int wx; + String name; + int index; + Vector unicodeIndex; + int[] bbox; + long offset; + byte found; + + TTFMtxEntry() { + name=""; + found=0; + unicodeIndex = new Vector(); + bbox=new int[4]; + } + + public String toString(TTFFile t) { + return new String("Glyph "+name+ " index: " + index + + " bbox [ "+t.get_ttf_funit(bbox[0])+ + " " + t.get_ttf_funit(bbox[1]) + + " " + t.get_ttf_funit(bbox[2]) + + " " + t.get_ttf_funit(bbox[3]) + "]" + + "wx: "+t.get_ttf_funit(wx)); + } } diff --git a/src/org/apache/fop/fonts/apps/PFMReader.java b/src/org/apache/fop/fonts/apps/PFMReader.java index c7c1410f2..581ba20db 100644 --- a/src/org/apache/fop/fonts/apps/PFMReader.java +++ b/src/org/apache/fop/fonts/apps/PFMReader.java @@ -67,10 +67,6 @@ import java.util.Enumeration; * @author jeremias.maerki@outline.ch */ public class PFMReader { - - static private final String XSL_POSTPROCESS = "FontPostProcess.xsl"; - static private final String XSL_SORT = "FontPostProcessSort.xsl"; - private boolean invokedStandalone = false; public PFMReader() { @@ -88,13 +84,14 @@ public class PFMReader { Vector arguments=new Vector(); for (int i=0; i < args.length; i++) { if (args[i].startsWith("-")) { - i++; - if (i < args.length) - options.put(args[i-1], args[i]); - else - options.put(args[i-1], ""); + if ((i+1) < args.length && !args[i+1].startsWith("-")) { + options.put(args[i], args[i+1]); + i++; + } else { + options.put(args[i], ""); + } } else { - arguments.addElement(args[i]); + arguments.addElement(args[i]); } } @@ -107,18 +104,10 @@ public class PFMReader { System.out.println(" java org.apache.fop.fonts.apps.PFMReader [options] metricfile.pfm xmlfile.xml\n"); System.out.println(" where options can be:\n"); System.out.println(" -fn <fontname>\n"); - System.out.println(" default is to use the fontname in the .pfm file, but you can override\n"); - System.out.println(" that name to make sure that the embedded font is used instead of installed\n"); - System.out.println(" fonts when viewing documents with Acrobat Reader.\n"); - System.out.println(" -cn <classname>\n"); - System.out.println(" default is to use the fontname\n"); - System.out.println(" -ef <path to the Type1 .pfb fontfile>\n"); - System.out.println(" will add the possibility to embed the font. When running fop, fop will look\n"); - System.out.println(" for this file to embed it\n"); - System.out.println(" -er <path to Type1 fontfile relative to org/apache/fop/render/pdf/fonts>\n"); - System.out.println(" you can also include the fontfile in the fop.jar file when building fop.\n"); - System.out.println(" You can use both -ef and -er. The file specified in -ef will be searched first,\n"); - System.out.println(" then the -er file.\n"); + System.out.println(" default is to use the fontname in the .ttf file, but\n"+ + " you can override that name to make sure that the\n"); + System.out.println(" embedded font is used (if you're embedding fonts)\n"); + System.out.println(" instead of installed fonts when viewing documents with Acrobat Reader.\n"); } @@ -142,56 +131,54 @@ public class PFMReader { * then the -er file. */ public static void main(String[] args) { - String embFile=null; - String embResource=null; - String className=null; - String fontName=null; - - Hashtable options=new Hashtable(); - String[] arguments=parseArguments(options, args); - - PFMReader app = new PFMReader(); - app.invokedStandalone = true; - - System.out.println("PFM Reader v1.1"); - System.out.println(); - - 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"); - - if (arguments.length != 2 || - options.get("-h") != null || - options.get("-help") != null || - options.get("--help") != null) - displayUsage(); - else { - PFMFile pfm = app.loadPFM(arguments[0]); - if (pfm != null) { - app.preview(pfm); - - org.w3c.dom.Document doc = app.constructFontXML(pfm, - fontName, - className, - embResource, - embFile); - - doc = app.postProcessXML(doc); - if (doc != null) { + String embFile=null; + String embResource=null; + String className=null; + String fontName=null; + + Hashtable options=new Hashtable(); + String[] arguments=parseArguments(options, args); + + PFMReader app = new PFMReader(); + app.invokedStandalone = true; + + System.out.println("PFM Reader v1.1"); + System.out.println(); + + 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"); + + if (arguments.length != 2 || + options.get("-h") != null || + options.get("-help") != null || + options.get("--help") != null) + displayUsage(); + else { + PFMFile pfm = app.loadPFM(arguments[0]); + if (pfm != null) { + app.preview(pfm); + + org.w3c.dom.Document doc = app.constructFontXML(pfm, + fontName, + className, + embResource, + embFile); + app.writeFontXML(doc, arguments[1]); - } - } - } + } + } } + /** * Read a PFM file and returns it as an object. * @@ -286,7 +273,8 @@ public class PFMReader { Document doc = new DocumentImpl(); Element root = doc.createElement("font-metrics"); doc.appendChild(root); - + root.setAttribute("type", "TYPE1"); + Element el = doc.createElement("font-name"); root.appendChild(el); el.appendChild(doc.createTextNode(pfm.getPostscriptName())); @@ -300,30 +288,12 @@ public class PFMReader { s = new String(sb); } - el = doc.createElement("class-name"); + el = doc.createElement("embed"); root.appendChild(el); - if (className != null) - el.appendChild(doc.createTextNode(className)); - else - el.appendChild(doc.createTextNode(s)); - - el = doc.createElement("embedFile"); - root.appendChild(el); - if (file==null) - el.appendChild(doc.createTextNode("null")); - else - el.appendChild(doc.createTextNode("\""+escapeString(file)+"\"")); - - el = doc.createElement("embedResource"); - root.appendChild(el); - if (resource==null) - el.appendChild(doc.createTextNode("null")); - else - el.appendChild(doc.createTextNode("\""+escapeString(resource)+"\"")); - - el = doc.createElement("subtype"); - root.appendChild(el); - el.appendChild(doc.createTextNode("Type1")); + if (file!=null) + el.setAttribute("file", file); + if (resource!=null) + el.setAttribute("class", resource); el = doc.createElement("encoding"); root.appendChild(el); @@ -391,8 +361,8 @@ public class PFMReader { for (short i = pfm.getFirstChar(); i < pfm.getLastChar(); i++) { el = doc.createElement("char"); widths.appendChild(el); - el.setAttribute("ansichar", "0x00" + Integer.toHexString(i).toUpperCase()); - el.setAttribute("width", new Integer(pfm.getCharWidth(i)).toString()); + el.setAttribute("idx", Integer.toString(i)); + el.setAttribute("wdt", new Integer(pfm.getCharWidth(i)).toString()); } @@ -403,72 +373,20 @@ public class PFMReader { el.setAttribute("kpx1", kpx1); root.appendChild(el); Element el2=null; - + Hashtable h2=(Hashtable)pfm.getKerning().get(kpx1); for (Enumeration enum2=h2.keys(); enum2.hasMoreElements(); ) { - String kpx2=(String)enum2.nextElement(); - el2=doc.createElement("pair"); - el2.setAttribute("kpx2", kpx2); - Integer val=(Integer)h2.get(kpx2); - el2.setAttribute("kern", val.toString()); - el.appendChild(el2); + Integer kpx2=(Integer)enum2.nextElement(); + el2=doc.createElement("pair"); + el2.setAttribute("kpx2", kpx2.toString()); + Integer val=(Integer)h2.get(kpx2); + el2.setAttribute("kern", val.toString()); + el.appendChild(el2); } } return doc; } - /** - * Modifies the generated font metrics file. First, it processes the - * character mmappings, then it sorts them. - * - * @param doc The DOM document representing the font metrics file. - * @return A DOM document representing the processed font metrics file. - */ - public org.w3c.dom.Document postProcessXML(org.w3c.dom.Document doc) { - try { - OutputFormat format = new OutputFormat(doc); //Serialize DOM - XMLSerializer serial = new XMLSerializer(System.out, format); - serial.asDOMSerializer(); // As a DOM Serializer - serial.serialize(doc.getDocumentElement()); - - System.out.println("Postprocessing..."); - System.out.println(); - - - - InputStream xsl = this.getClass().getResourceAsStream(XSL_POSTPROCESS); - if (xsl == null) { - throw new Exception("Resource " + XSL_POSTPROCESS + " not found"); - } - - Document targetDoc = new DocumentImpl(); - org.apache.fop.tools.xslt.XSLTransform.transform(doc, xsl, targetDoc); - - - System.out.println("Sorting..."); - System.out.println(); - - // Sort the whole thing - - - xsl = this.getClass().getResourceAsStream(XSL_SORT); - if (xsl == null) { - throw new Exception("Resource " + XSL_SORT + " not found"); - } - - - org.w3c.dom.Document targetDocSorted = new DocumentImpl(); - - org.apache.fop.tools.xslt.XSLTransform.transform(targetDoc, xsl, targetDocSorted); - - return targetDocSorted; - - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - private String escapeString(String str) { StringBuffer esc=new StringBuffer(); @@ -483,3 +401,7 @@ public class PFMReader { return esc.toString(); } } + + + + diff --git a/src/org/apache/fop/fonts/apps/TTFReader.java b/src/org/apache/fop/fonts/apps/TTFReader.java index f7dbe0bfd..e4ff7774b 100644 --- a/src/org/apache/fop/fonts/apps/TTFReader.java +++ b/src/org/apache/fop/fonts/apps/TTFReader.java @@ -66,9 +66,6 @@ import java.util.Enumeration; */ public class TTFReader { - static private final String XSL_POSTPROCESS = "TTFPostProcess.xsl"; - static private final String XSL_SORT = "TTFPostProcessSort.xsl"; - private boolean invokedStandalone = false; public TTFReader() { @@ -86,13 +83,14 @@ public class TTFReader { Vector arguments=new Vector(); for (int i=0; i < args.length; i++) { if (args[i].startsWith("-")) { - i++; - if (i < args.length) - options.put(args[i-1], args[i]); - else - options.put(args[i-1], ""); + if ((i+1) < args.length && !args[i+1].startsWith("-")) { + options.put(args[i], args[i+1]); + i++; + } else { + options.put(args[i], ""); + } } else { - arguments.addElement(args[i]); + arguments.addElement(args[i]); } } @@ -105,19 +103,22 @@ public class TTFReader { private final static void displayUsage() { System.out.println(" java org.apache.fop.fonts.apps.TTFReader [options] fontfile.ttf xmlfile.xml\n"); System.out.println(" where options can be:\n"); + System.out.println("-enc cid"); + System.out.println(" With this option you create a CID keyed font."); + System.out.println(" If you're going to use characters outside the"); + System.out.println(" pdfencoding range (almost the same as iso-8889-1)"); + System.out.println(" you must 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>\n"); - System.out.println(" default is to use the fontname in the .ttf file, but you can override\n"); - System.out.println(" that name to make sure that the embedded font is used instead of installed\n"); - System.out.println(" fonts when viewing documents with Acrobat Reader.\n"); - System.out.println(" -cn <classname>\n"); - System.out.println(" default is to use the fontname\n"); - System.out.println(" -ef <path to the truetype fontfile>\n"); - System.out.println(" will add the possibility to embed the font. When running fop, fop will look\n"); - System.out.println(" for this file to embed it\n"); - System.out.println(" -er <path to truetype fontfile relative to org/apache/fop/render/pdf/fonts>\n"); - System.out.println(" you can also include the fontfile in the fop.jar file when building fop.\n"); - System.out.println(" You can use both -ef and -er. The file specified in -ef will be searched first,\n"); - System.out.println(" then the -er file.\n"); + System.out.println(" default is to use the fontname in the .ttf file, but\n"+ + " you can override that name to make sure that the\n"); + System.out.println(" embedded font is used (if you're embedding fonts)\n"); + System.out.println(" instead of installed fonts when viewing documents with Acrobat Reader.\n"); } @@ -145,6 +146,8 @@ public class TTFReader { String embResource=null; String className=null; String fontName=null; + String ttcName=null; + boolean isCid=false; Hashtable options=new Hashtable(); String[] arguments=parseArguments(options, args); @@ -152,9 +155,18 @@ public class TTFReader { TTFReader app = new TTFReader(); app.invokedStandalone = true; - System.out.println("TTF Reader v1.0"); + System.out.println("TTF Reader v1.1"); System.out.println(); + if (options.get("-enc") != null) { + String enc = (String)options.get("-enc"); + if ("cid".equals(enc)) + isCid=true; + } + + if (options.get("-ttcname") != null) + ttcName=(String)options.get("-ttcname"); + if (options.get("-ef") != null) embFile=(String)options.get("-ef"); @@ -173,20 +185,26 @@ public class TTFReader { options.get("--help") != null) displayUsage(); else { - TTFFile ttf = app.loadTTF(arguments[0]); + TTFFile ttf = app.loadTTF(arguments[0], ttcName); if (ttf != null) { - app.preview(ttf); - org.w3c.dom.Document doc = app.constructFontXML(ttf, fontName, className, embResource, - embFile); + embFile, + isCid, + ttcName); - doc = app.postProcessXML(doc); if (doc != null) { app.writeFontXML(doc, arguments[1]); } + + if (ttf.isEmbeddable()) + System.out.println("This font contains no embedding license restrictions"); + else + System.out.println("** Note: This font contains license retrictions for\n"+ + " embedding. This font can't be embedded."); + } } } @@ -195,16 +213,16 @@ public class TTFReader { * Read a TTF file and returns it as an object. * * @param filename The filename of the PFM file. - * @return The PFM as an object. + * @return The TTF as an object. */ - public TTFFile loadTTF(String filename) { + public TTFFile loadTTF(String fileName, String fontName) { TTFFile ttfFile=new TTFFile(); try { - System.out.println("Reading " + filename + "..."); + System.out.println("Reading " + fileName + "..."); System.out.println(); - FontFileReader reader = new FontFileReader(filename); - ttfFile.readFont(reader); + FontFileReader reader = new FontFileReader(fileName); + ttfFile.readFont(reader, fontName); } catch (Exception e) { e.printStackTrace(); return null; @@ -212,45 +230,6 @@ public class TTFReader { return ttfFile; } - /** - * Displays a preview of the TTF file on the console. - * - * @param ttf The TTF file to preview. - */ - public void preview(TTFFile ttf) { - PrintStream out = System.out; - - out.print("Font: "); - out.println(ttf.getWindowsName()); - out.print("Name: "); - out.println(ttf.getPostscriptName()); - out.print("CharSet: "); - out.println(ttf.getCharSetName()); - out.print("CapHeight: "); - out.println(ttf.getCapHeight()); - out.print("XHeight: "); - out.println(ttf.getXHeight()); - out.print("LowerCaseAscent: "); - out.println(ttf.getLowerCaseAscent()); - out.print("LowerCaseDescent: "); - out.println(ttf.getLowerCaseDescent()); - out.print("Having widths for "); - out.print(ttf.getLastChar()-ttf.getFirstChar()); - out.print(" characters ("); - out.print(ttf.getFirstChar()); - out.print("-"); - out.print(ttf.getLastChar()); - out.println(")."); - out.print("for example: Char "); - out.print(ttf.getFirstChar()); - out.print(" has a width of "); - out.println(ttf.getCharWidth(ttf.getFirstChar())); - out.println(); - if (ttf.isEmbeddable()) - out.println("This font might be embedded"); - else - out.println("This font might not be embedded"); - } /** * Writes the generated DOM Document to a file. @@ -276,21 +255,26 @@ public class TTFReader { } /** - * Generates the font metrics file from the PFM file. + * Generates the font metrics file from the TTF/TTC file. * - * @param pfm The PFM file to generate the font metrics from. + * @param ttf The PFM file to generate the font metrics from. * @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) { + String file, boolean isCid, + String ttcName) { System.out.println("Creating xml font file..."); System.out.println(); Document doc = new DocumentImpl(); Element root = doc.createElement("font-metrics"); doc.appendChild(root); - + if (isCid) + root.setAttribute("type", "TYPE0"); + else + root.setAttribute("type", "TRUETYPE"); + Element el = doc.createElement("font-name"); root.appendChild(el); @@ -298,51 +282,19 @@ public class TTFReader { // "Perpetua-Bold", but the TrueType spec says that in the ttf file // it should be "Perpetua,Bold". - String s = ttf.getPostscriptName(); + String s = stripWhiteSpace(ttf.getPostscriptName()); if (fontName != null) - el.appendChild(doc.createTextNode(fontName)); - else - el.appendChild(doc.createTextNode(s.replace('-', ','))); - - int pos = s.indexOf("-"); - if (pos >= 0) { - char sb[] = new char[s.length() - 1]; - s.getChars(0, pos, sb, 0); - s.getChars(pos + 1, s.length(), sb, pos); - s = new String(sb); - } - - el = doc.createElement("class-name"); - root.appendChild(el); - if (className != null) - el.appendChild(doc.createTextNode(className)); + el.appendChild(doc.createTextNode(stripWhiteSpace(fontName))); else - el.appendChild(doc.createTextNode(s)); + el.appendChild(doc.createTextNode(s)); - el = doc.createElement("embedFile"); - root.appendChild(el); - //if (file==null || !ttf.isEmbeddable()) - if (file==null) - el.appendChild(doc.createTextNode("null")); - else - el.appendChild(doc.createTextNode("\""+escapeString(file)+"\"")); - - el = doc.createElement("embedResource"); + el = doc.createElement("embed"); root.appendChild(el); - //if (resource==null || !ttf.isEmbeddable()) - if (resource==null) - el.appendChild(doc.createTextNode("null")); - else - el.appendChild(doc.createTextNode("\""+escapeString(resource)+"\"")); - - el = doc.createElement("subtype"); - root.appendChild(el); - el.appendChild(doc.createTextNode("TRUETYPE")); - - el = doc.createElement("encoding"); - root.appendChild(el); - el.appendChild(doc.createTextNode(ttf.getCharSetName()+"Encoding")); + 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); @@ -353,12 +305,12 @@ public class TTFReader { root.appendChild(el); value = new Integer(ttf.getXHeight()); el.appendChild(doc.createTextNode(value.toString())); - + el = doc.createElement("ascender"); root.appendChild(el); value = new Integer(ttf.getLowerCaseAscent()); el.appendChild(doc.createTextNode(value.toString())); - + el = doc.createElement("descender"); root.appendChild(el); value = new Integer(ttf.getLowerCaseDescent()); @@ -379,115 +331,143 @@ public class TTFReader { root.appendChild(el); value = new Integer(ttf.getFlags()); el.appendChild(doc.createTextNode(value.toString())); - + el = doc.createElement("stemv"); root.appendChild(el); value = new Integer(ttf.getStemV()); el.appendChild(doc.createTextNode(value.toString())); - + el = doc.createElement("italicangle"); root.appendChild(el); value = new Integer(ttf.getItalicAngle()); el.appendChild(doc.createTextNode(value.toString())); - el = doc.createElement("first-char"); - root.appendChild(el); - value = new Integer(ttf.getFirstChar()); - el.appendChild(doc.createTextNode(value.toString())); - - el = doc.createElement("last-char"); - root.appendChild(el); - value = new Integer(ttf.getLastChar()); - el.appendChild(doc.createTextNode(value.toString())); - - Element widths = doc.createElement("widths"); - root.appendChild(widths); - - for (short i = ttf.getFirstChar(); i < ttf.getLastChar(); i++) { - el = doc.createElement("char"); - widths.appendChild(el); - //el.setAttribute("ansichar", "0x00" + Integer.toHexString(i).toUpperCase()); - el.setAttribute("name", "0x00" + - Integer.toHexString(i).toUpperCase()); - el.setAttribute("width", - new Integer(ttf.getCharWidth(i)).toString()); - } - - // Get kerning - for (Enumeration enum=ttf.getKerning().keys(); enum.hasMoreElements();) { - String kpx1=(String)enum.nextElement(); - el=doc.createElement("kerning"); - el.setAttribute("kpx1", kpx1); - root.appendChild(el); - Element el2=null; - - Hashtable h2=(Hashtable)ttf.getKerning().get(kpx1); - for (Enumeration enum2=h2.keys(); enum2.hasMoreElements(); ) { - String kpx2=(String)enum2.nextElement(); - el2=doc.createElement("pair"); - el2.setAttribute("kpx2", kpx2); - Integer val=(Integer)h2.get(kpx2); - el2.setAttribute("kern", val.toString()); - el.appendChild(el2); - } + if (ttcName != null) { + el = doc.createElement("ttc-name"); + root.appendChild(el); + el.appendChild(doc.createTextNode(ttcName)); } - return doc; - } + + el = doc.createElement("subtype"); + root.appendChild(el); + + // Fill in extras for CID keyed fonts + if (isCid) { + el.appendChild(doc.createTextNode("TYPE0")); - /** - * Modifies the generated font metrics file. First, it processes the - * character mmappings, then it sorts them. - * - * @param doc The DOM document representing the font metrics file. - * @return A DOM document representing the processed font metrics file. - */ - public org.w3c.dom.Document postProcessXML(org.w3c.dom.Document doc) { - if (true) - return doc; - try { - OutputFormat format = new OutputFormat(doc); //Serialize DOM - XMLSerializer serial = new XMLSerializer(System.out, format); - serial.asDOMSerializer(); // As a DOM Serializer - serial.serialize(doc.getDocumentElement()); + Element mel = doc.createElement("multibyte-extras"); + root.appendChild(mel); - System.out.println("Postprocessing..."); - System.out.println(); - - - - InputStream xsl = this.getClass().getResourceAsStream(XSL_POSTPROCESS); - if (xsl == null) { - throw new Exception("Resource " + XSL_POSTPROCESS + " not found"); + 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 (Enumeration e=ttf.getCMaps().elements(); e.hasMoreElements();) { + TTFCmapEntry ce = (TTFCmapEntry)e.nextElement(); + Element el2=doc.createElement("bf"); + el.appendChild(el2); + el2.setAttribute("us", Integer.toString(ce.unicodeStart)); + el2.setAttribute("ue", Integer.toString(ce.unicodeEnd)); + el2.setAttribute("gi", Integer.toString(ce.glyphStartIndex)); } - - Document targetDoc = new DocumentImpl(); - org.apache.fop.tools.xslt.XSLTransform.transform(doc, xsl, targetDoc); - - - System.out.println("Sorting..."); - System.out.println(); - - // Sort the whole thing - - xsl = this.getClass().getResourceAsStream(XSL_SORT); - if (xsl == null) { - throw new Exception("Resource " + XSL_SORT + " not found"); + 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", Integer.toString(wx[i])); + el.appendChild(wxel); } - - - org.w3c.dom.Document targetDocSorted = new DocumentImpl(); + } else { + // Fill in extras for singlebyte fonts + el.appendChild(doc.createTextNode("TRUETYPE")); - org.apache.fop.tools.xslt.XSLTransform.transform(targetDoc, xsl, targetDocSorted); + Element sel=doc.createElement("singlebyte-extras"); + root.appendChild(sel); + + el = doc.createElement("encoding"); + sel.appendChild(el); + el.appendChild(doc.createTextNode(ttf.getCharSetName())); - return targetDocSorted; + el = doc.createElement("first-char"); + sel.appendChild(el); + value = new Integer(ttf.getFirstChar()); + el.appendChild(doc.createTextNode(value.toString())); + + el = doc.createElement("last-char"); + sel.appendChild(el); + value = new Integer(ttf.getLastChar()); + el.appendChild(doc.createTextNode(value.toString())); + + 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", Integer.toString(i)); + el.setAttribute("wdt", Integer.toString(ttf.getCharWidth(i))); + } + } + + // Get kerning + Enumeration enum; + if (isCid) + enum=ttf.getKerning().keys(); + else + enum=ttf.getAnsiKerning().keys(); + + while (enum.hasMoreElements()) { + Integer kpx1=(Integer)enum.nextElement(); - } catch (Exception e) { - e.printStackTrace(); - return null; + el=doc.createElement("kerning"); + el.setAttribute("kpx1", kpx1.toString()); + root.appendChild(el); + Element el2=null; + + Hashtable h2; + if (isCid) + h2 = (Hashtable)ttf.getKerning().get(kpx1); + else + h2 = (Hashtable)ttf.getAnsiKerning().get(kpx1); + + for (Enumeration enum2=h2.keys(); enum2.hasMoreElements(); ) { + Integer kpx2=(Integer)enum2.nextElement(); + if (isCid || kpx2.intValue() < 256) { + el2=doc.createElement("pair"); + el2.setAttribute("kpx2", kpx2.toString()); + Integer val=(Integer)h2.get(kpx2); + el2.setAttribute("kern", val.toString()); + el.appendChild(el2); + } + } } + + return doc; } - + + + private String stripWhiteSpace(String s) { + char[] ch = new char[s.length()]; + s.getChars(0, s.length(), ch, 0); + StringBuffer stb = new StringBuffer(); + for (int i = 0; i < ch.length; i++) + if (ch[i] != ' ' && ch[i] != '\r' && + ch[i] != '\n' && ch[i] != '\t') + stb.append(ch[i]); + + return stb.toString(); + } + private String escapeString(String str) { StringBuffer esc=new StringBuffer(); diff --git a/src/org/apache/fop/layout/FontInfo.java b/src/org/apache/fop/layout/FontInfo.java index 869a54e98..84a2a4979 100644 --- a/src/org/apache/fop/layout/FontInfo.java +++ b/src/org/apache/fop/layout/FontInfo.java @@ -57,13 +57,14 @@ import java.util.Enumeration; import org.apache.fop.apps.FOPException; public class FontInfo { - + Hashtable usedFonts; Hashtable triplets; // look up a font-triplet to find a font-name Hashtable fonts; // look up a font-name to get a font (that implements FontMetric at least) public FontInfo() { this.triplets = new Hashtable(); - this.fonts = new Hashtable(); + this.fonts = new Hashtable(); + this.usedFonts = new Hashtable(); } public void addFontProperties(String name, String family, String style, String weight) { @@ -109,6 +110,8 @@ public class FontInfo { } MessageHandler.errorln("WARNING: unknown font "+family+" so defaulted font to any"); } + + usedFonts.put(f, fonts.get(f)); return f; } @@ -116,13 +119,17 @@ public class FontInfo { return this.fonts; } + public Hashtable getUsedFonts() { + return this.usedFonts; + } + public FontMetric getMetricsFor(String fontName) throws FOPException { - return (FontMetric)fonts.get(fontName); + usedFonts.put(fontName, fonts.get(fontName)); + return (FontMetric)fonts.get(fontName); } public FontMetric getMetricsFor(String family, String style, String weight) throws FOPException { // given a family, style and weight, return the metric - - return (FontMetric)fonts.get(fontLookup(family,style,weight)); + return (FontMetric)fonts.get(fontLookup(family,style,weight)); } } diff --git a/src/org/apache/fop/layout/FontState.java b/src/org/apache/fop/layout/FontState.java index f022ab54a..e000b5e77 100644 --- a/src/org/apache/fop/layout/FontState.java +++ b/src/org/apache/fop/layout/FontState.java @@ -54,17 +54,17 @@ import org.apache.fop.apps.FOPException; import org.apache.fop.fo.properties.FontVariant; public class FontState { - - protected FontInfo fontInfo; - private String fontName; - private int fontSize; - private String fontFamily; - private String fontStyle; - private String fontWeight; - private FontMetric metric; - private int fontVariant; - - public FontState(FontInfo fontInfo, String fontFamily, String fontStyle, String fontWeight, int fontSize, int fontVariant) throws FOPException { + + protected FontInfo fontInfo; + private String fontName; + private int fontSize; + private String fontFamily; + private String fontStyle; + private String fontWeight; + private FontMetric metric; + private int fontVariant; + + public FontState(FontInfo fontInfo, String fontFamily, String fontStyle, String fontWeight, int fontSize, int fontVariant) throws FOPException { this.fontInfo = fontInfo; this.fontFamily = fontFamily; this.fontStyle = fontStyle; @@ -73,65 +73,99 @@ public class FontState { this.fontName = fontInfo.fontLookup(fontFamily,fontStyle,fontWeight); this.metric = fontInfo.getMetricsFor(fontName); this.fontVariant = fontVariant; - } - - public int getAscender() { + } + + public int getAscender() { return metric.getAscender(fontSize) / 1000; - } - - public int getCapHeight() { + } + + public int getCapHeight() { return metric.getCapHeight(fontSize) / 1000; - } - - public int getDescender() { + } + + public int getDescender() { return metric.getDescender(fontSize) / 1000; - } - - public String getFontName() { + } + + public String getFontName() { return this.fontName; - } - - public int getFontSize() { + } + + public int getFontSize() { return this.fontSize; - } - - public String getFontWeight() { + } + + public String getFontWeight() { return this.fontWeight; - } - - public String getFontFamily() { + } + + public String getFontFamily() { return this.fontFamily; - } - - public String getFontStyle() { + } + + public String getFontStyle() { return this.fontStyle; - } - - public int getFontVariant() { + } + + public int getFontVariant() { return this.fontVariant; - } - - public FontInfo getFontInfo() { + } + + public FontInfo getFontInfo() { return this.fontInfo; - } - - public int getXHeight() { + } + + public int getXHeight() { return metric.getXHeight(fontSize) / 1000; - } - public java.util.Hashtable getKerning() { - java.util.Hashtable ret=new java.util.Hashtable(); - try { - FontMetric fm=fontInfo.getMetricsFor(fontFamily, fontStyle, fontWeight); - if (fm instanceof org.apache.fop.layout.FontDescriptor) { - org.apache.fop.layout.FontDescriptor fdes=(org.apache.fop.layout.FontDescriptor)fm; - ret=fdes.getKerningInfo(); - } - } catch (Exception e) {} - return ret; - } - - public int width(int charnum) { - // returns width of given character number in millipoints + } + + public java.util.Hashtable getKerning() { + java.util.Hashtable ret=new java.util.Hashtable(); + try { + FontMetric fm=fontInfo.getMetricsFor(fontFamily, fontStyle, + fontWeight); + if (fm instanceof org.apache.fop.layout.FontDescriptor) { + org.apache.fop.layout.FontDescriptor fdes= + (org.apache.fop.layout.FontDescriptor)fm; + ret=fdes.getKerningInfo(); + } + } catch (Exception e) {} + return ret; + } + + public int width(int charnum) { + // returns width of given character number in millipoints return (metric.width(charnum, fontSize) / 1000); - } + } + + /** + * Map a java character (unicode) to a font character + * Default uses CodePointMapping + */ + public char mapChar(char c) { + try { + FontMetric fm=fontInfo.getMetricsFor(fontFamily, fontStyle, + fontWeight); + if (fm instanceof org.apache.fop.render.pdf.Font) { + org.apache.fop.render.pdf.Font f= + (org.apache.fop.render.pdf.Font)fm; + return f.mapChar(c); + } + } catch (Exception e) {} + + // Use default CodePointMapping + if (c > 127) { + char d = org.apache.fop.render.pdf.CodePointMapping.map[c]; + if (d != 0) { + c = d; + } else { + c = '#'; + } + } + + return c; + } } + + + diff --git a/src/org/apache/fop/layout/LineArea.java b/src/org/apache/fop/layout/LineArea.java index 01052ef9d..5f31fed46 100644 --- a/src/org/apache/fop/layout/LineArea.java +++ b/src/org/apache/fop/layout/LineArea.java @@ -184,7 +184,9 @@ public class LineArea extends Area { */ //Space must be alloted to the page number, so currently we give it 3 spaces - int width = currentFontState.width(32) * 3; + + int width = currentFontState.width(currentFontState.mapChar(' ')); + PageNumberInlineArea pia = new PageNumberInlineArea(currentFontState, this.red, @@ -214,6 +216,10 @@ public class LineArea extends Area { int wordStart = start; int wordLength = 0; int wordWidth = 0; + // With CID fonts, space isn't neccecary currentFontState.width(32) + int whitespaceWidth = + currentFontState.width(currentFontState.mapChar(' ')); + char[] data = new char[odata.length]; for (int count = 0; count < odata.length; count++) { data[count] = odata[count]; @@ -224,18 +230,10 @@ public class LineArea extends Area { int charWidth; /* get the character */ char c = data[i]; - - if (c > 127) { - /* this class shouldn't be hard coded */ - char d = org.apache.fop.render.pdf.CodePointMapping.map[c]; - if (d != 0) { - c = data[i] = d; - } else { - MessageHandler.error("ch" + (int) c + "?"); - c = data[i] = '#'; - } - } - + if (!((c == ' ') || (c == '\n') || (c == '\r') || + (c == '\t'))) + c = data[i] = currentFontState.mapChar(c); + charWidth = currentFontState.width(c); if ((c == ' ') || (c == '\n') || (c == '\r') || @@ -248,12 +246,12 @@ public class LineArea extends Area { if (this.whiteSpaceCollapse == WhiteSpaceCollapse.FALSE) { if (c == ' ') { - spaceWidth += currentFontState.width(32); + spaceWidth += whitespaceWidth; } else if (c == '\n') { // force line break return i; } else if (c == '\t') { - spaceWidth += 8 * currentFontState.width(32); + spaceWidth += 8 * whitespaceWidth; } } // else ignore it @@ -339,7 +337,7 @@ public class LineArea extends Area { embeddedLinkStart = 0; //reset embeddedLinkStart since a space was encountered - spaceWidth = currentFontState.width(32); + spaceWidth = whitespaceWidth; /* here is the place for space-treatment value 'ignore': @@ -359,7 +357,7 @@ public class LineArea extends Area { // force a line break return i; } else if (c == '\t') { - spaceWidth = currentFontState.width(32); + spaceWidth = whitespaceWidth; } } @@ -370,7 +368,7 @@ public class LineArea extends Area { if (this.whiteSpaceCollapse == WhiteSpaceCollapse.FALSE) { prev = WHITESPACE; - spaceWidth = currentFontState.width(32); + spaceWidth = whitespaceWidth; } else { // skip over it start++; diff --git a/src/org/apache/fop/pdf/PDFCIDFont.java b/src/org/apache/fop/pdf/PDFCIDFont.java index f13141da0..65588c6a6 100644 --- a/src/org/apache/fop/pdf/PDFCIDFont.java +++ b/src/org/apache/fop/pdf/PDFCIDFont.java @@ -69,6 +69,7 @@ public class PDFCIDFont extends PDFObject { protected PDFWArray w2; protected PDFCIDSystemInfo systemInfo; protected PDFCIDFontDescriptor descriptor; + protected PDFCMap cmap; /** * /CIDToGIDMap (only for CIDFontType2, see p 212) * can be either "Identity" (default) or a PDFStream @@ -79,14 +80,15 @@ public class PDFCIDFont extends PDFObject { /** * create the /Font object */ - public PDFCIDFont(int number, String basefont, String cidtype, - int dw, int[] w, String registry, String ordering, int supplement, - PDFCIDFontDescriptor descriptor) { + public PDFCIDFont(int number, String basefont, byte cidtype, + int dw, int[] w, + String registry, String ordering, int supplement, + PDFCIDFontDescriptor descriptor) { super(number); this.basefont = basefont; - this.cidtype = cidtype; + this.cidtype = TYPE_NAMES[(int)cidtype]; this.dw = new Integer(dw); this.w = new PDFWArray(); this.w.addEntry(0, w); @@ -95,19 +97,20 @@ public class PDFCIDFont extends PDFObject { this.systemInfo = new PDFCIDSystemInfo(registry, ordering, supplement); this.descriptor = descriptor; this.cidMap = null; + this.cmap = null; } /** * create the /Font object */ - public PDFCIDFont(int number, String basefont, String cidtype, + public PDFCIDFont(int number, String basefont, byte cidtype, int dw, PDFWArray w, PDFCIDSystemInfo systemInfo, PDFCIDFontDescriptor descriptor) { super(number); this.basefont = basefont; - this.cidtype = cidtype; + this.cidtype = TYPE_NAMES[(int)cidtype]; this.dw = new Integer(dw); this.w = w; this.dw2 = null; @@ -115,6 +118,7 @@ public class PDFCIDFont extends PDFObject { this.systemInfo = systemInfo; this.descriptor = descriptor; this.cidMap = null; + this.cmap = null; } /** set the /DW attribute */ @@ -137,6 +141,13 @@ public class PDFCIDFont extends PDFObject { this.dw2 = new int[] {posY, displacementY}; } + /** + * Set the CMap used as /ToUnicode cmap + */ + public void setCMAP(PDFCMap cmap) { + this.cmap = cmap; + } + /** set the /W2 array */ public void setW2(PDFWArray w2) { this.w2 = w2; @@ -172,16 +183,20 @@ public class PDFCIDFont extends PDFObject { p.append(cidMap.referencePDF()); } p.append(" \n/Subtype /"); p.append(this.cidtype); - p.append("\n"); p.append(systemInfo.toPDF()); + p.append("\n"); p.append(systemInfo.toPDFString()); p.append("\n/FontDescriptor "); p.append(this.descriptor.referencePDF()); + + if (cmap != null) { + p.append("\n/ToUnicode "); + p.append(cmap.referencePDF()); + } if (dw != null) { p.append("\n/DW "); p.append(this.dw); } if (w != null) { p.append("\n/W "); - p.append(w.toPDF()); - p.append(" \n>>\nendobj\n"); + p.append(w.toPDFString()); } if (dw2 != null) { p.append("\n/DW2 ["); // always two values, see p 211 @@ -191,9 +206,11 @@ public class PDFCIDFont extends PDFObject { } if (w2 != null) { p.append("\n/W2 "); - p.append(w2.toPDF()); + p.append(w2.toPDFString()); p.append(" \n>>\nendobj\n"); } + p.append(" \n>>\nendobj\n"); return p.toString(); } } + diff --git a/src/org/apache/fop/pdf/PDFCIDFontDescriptor.java b/src/org/apache/fop/pdf/PDFCIDFontDescriptor.java index 9e8cddec5..eabc818e6 100644 --- a/src/org/apache/fop/pdf/PDFCIDFontDescriptor.java +++ b/src/org/apache/fop/pdf/PDFCIDFontDescriptor.java @@ -92,6 +92,7 @@ public class PDFCIDFontDescriptor extends PDFFontDescriptor { } protected void fillInPDF(StringBuffer p) { + p.append("\n/MissingWidth 500\n"); if (lang != null) { p.append("\n/Lang /"); p.append(lang); } diff --git a/src/org/apache/fop/pdf/PDFCMap.java b/src/org/apache/fop/pdf/PDFCMap.java index 1a086a9af..de0292fb0 100644 --- a/src/org/apache/fop/pdf/PDFCMap.java +++ b/src/org/apache/fop/pdf/PDFCMap.java @@ -150,6 +150,12 @@ public class PDFCMap extends PDFStream { this.wMode = mode; } + public void addContents() { + StringBuffer p=new StringBuffer(); + fillInPDF(p); + add(p.toString()); + } + /** * set the base CMap * @@ -169,16 +175,59 @@ public class PDFCMap extends PDFStream { } public void fillInPDF(StringBuffer p) { - p.append(" /Type /CMap\n/CMapName /" + name); - p.append("\n"); p.append(sysInfo.toPDF()); - p.append("\n/WMode "); p.append(wMode); - if (base != null) { - p.append("\n/UseCMap "); - if (base instanceof String) { - p.append("/"+base); - } else { // base instanceof PDFStream - p.append(((PDFStream)base).referencePDF()); - } - } + //p.append("/Type /CMap\n"); + //p.append(sysInfo.toPDFString()); + //p.append("/CMapName /" + name); + //p.append("\n"); + p.append("%!PS-Adobe-3.0 Resource-CMap\n"); + p.append("%%DocumentNeededResources: ProcSet (CIDInit)\n"); + p.append("%%IncludeResource: ProcSet (CIDInit)\n"); + p.append("%%BeginResource: CMap (" + name + ")\n"); + p.append("%%EndComments\n"); + + p.append("/CIDInit /ProcSet findresource begin\n"); + p.append("12 dict begin\n"); + p.append("begincmap\n"); + + p.append("/CIDSystemInfo 3 dict dup begin\n"); + p.append(" /Registry (Adobe) def\n"); + p.append(" /Ordering (Identity) def\n"); + p.append(" /Supplement 0 def\n"); + p.append("end def\n"); + + p.append("/CMapVersion 1 def\n"); + p.append("/CMapType 1 def\n"); + p.append("/CMapName /" + name + " def\n"); + + p.append("1 begincodespacerange\n"); + p.append("<0000> <FFFF>\n"); + p.append("endcodespacerange\n"); + p.append("1 begincidrange\n"); + p.append("<0000> <FFFF> 0\n"); + p.append("endcidrange\n"); + + //p.append("1 beginbfrange\n"); + //p.append("<0020> <0100> <0000>\n"); + //p.append("endbfrange\n"); + + p.append("endcmap\n"); + p.append("CMapName currentdict /CMap defineresource pop\n"); + p.append("end\n"); + p.append("end\n"); + p.append("%%EndResource\n"); + p.append("%%EOF\n"); + /* + p.append(" /Type /CMap\n/CMapName /" + name); + p.append("\n"); + p.append("\n/WMode "); p.append(wMode); + if (base != null) { + p.append("\n/UseCMap "); + if (base instanceof String) { + p.append("/"+base); + } else { // base instanceof PDFStream + p.append(((PDFStream)base).referencePDF()); + } + } + */ } } diff --git a/src/org/apache/fop/pdf/PDFDocument.java b/src/org/apache/fop/pdf/PDFDocument.java index 7f55fda80..7d2c7ec2e 100644 --- a/src/org/apache/fop/pdf/PDFDocument.java +++ b/src/org/apache/fop/pdf/PDFDocument.java @@ -61,6 +61,8 @@ import org.apache.fop.image.FopImage; import org.apache.fop.layout.LinkSet; import org.apache.fop.datatypes.ColorSpace; +import org.apache.fop.render.pdf.CIDFont; + import org.apache.fop.datatypes.IDReferences; import org.apache.fop.layout.Page; import org.apache.fop.layout.FontMetric; @@ -751,59 +753,119 @@ public class PDFDocument { /* create a PDFFont with the next object number and add to the list of objects */ if (descriptor == null) { - PDFFont font = new PDFFont(++this.objectcount, fontname, PDFFont.TYPE1, - basefont, encoding); + PDFFont font = new PDFFont(++this.objectcount, fontname, + PDFFont.TYPE1, + basefont, encoding); this.objects.addElement(font); return font; } else { - byte subtype=PDFFont.TYPE1; - if (metrics instanceof org.apache.fop.render.pdf.Font) - subtype=((org.apache.fop.render.pdf.Font)metrics).getSubType(); - - PDFFontNonBase14 font = (PDFFontNonBase14)PDFFont.createFont( + byte subtype=PDFFont.TYPE1; + if (metrics instanceof org.apache.fop.render.pdf.Font) + subtype=((org.apache.fop.render.pdf.Font)metrics).getSubType(); + + PDFFontDescriptor pdfdesc = makeFontDescriptor(descriptor, + subtype); + + PDFFontNonBase14 font = null; + if (subtype == PDFFont.TYPE0) { + PDFCMap cmap = new PDFCMap(++this.objectcount, + "fop-ucs-H", + new PDFCIDSystemInfo("Adobe", + "Identity", + 0)); + cmap.addContents(); + this.objects.addElement(cmap); + + font = (PDFFontNonBase14)PDFFont.createFont( + ++this.objectcount, fontname, + subtype, basefont, cmap); + } else { + + font = (PDFFontNonBase14)PDFFont.createFont( ++this.objectcount, fontname, subtype, basefont, encoding); + } this.objects.addElement(font); - PDFFontDescriptor pdfdesc = makeFontDescriptor(descriptor); + font.setDescriptor(pdfdesc); - font.setWidthMetrics(metrics.getFirstChar(), metrics.getLastChar(), - makeArray(metrics.getWidths(1))); + + if (subtype == PDFFont.TYPE0) { + CIDFont cidMetrics = (CIDFont)metrics; + PDFCIDSystemInfo sysInfo = + new PDFCIDSystemInfo(cidMetrics.getRegistry(), + cidMetrics.getOrdering(), + cidMetrics.getSupplement()); + PDFCIDFont cidFont = + new PDFCIDFont(++this.objectcount, basefont, + cidMetrics.getCidType(), + cidMetrics.getDefaultWidth(), + cidMetrics.getWidths(), + sysInfo, (PDFCIDFontDescriptor)pdfdesc); + this.objects.addElement(cidFont); + + //((PDFFontType0)font).setCMAP(cmap); + + ((PDFFontType0)font).setDescendantFonts(cidFont); + } else { + font.setWidthMetrics(metrics.getFirstChar(), + metrics.getLastChar(), + makeArray(metrics.getWidths(1))); + } + return font; } } /** - * make a /FontDescriptor object for a CID font + * make a /FontDescriptor object */ - public PDFFontDescriptor makeFontDescriptor(FontDescriptor desc) { - - /* create a PDFFontDescriptor with the next object number and add to - the list of objects */ - PDFFontDescriptor font = - new PDFFontDescriptor(++this.objectcount, - desc.fontName(), desc.getAscender(), desc.getDescender(), - desc.getCapHeight(), desc.getFlags(), - new PDFRectangle(desc.getFontBBox()), desc.getStemV(), - desc.getItalicAngle()); - - // Check if embedding of this font is enabled in the configurationfile - // and that the font is embeddable - java.util.Vector cfgv= - org.apache.fop.configuration.Configuration.getListValue("embed-fonts", 0); - if (cfgv!=null && cfgv.contains(desc.fontName()) && desc.isEmbeddable()) { - PDFStream stream=desc.getFontFile(this.objectcount+1); - if (stream!=null) { - this.objectcount++; - font.setFontFile(desc.getSubType(), stream); - this.objects.addElement(font); - this.objects.addElement(stream); - } - } else { - this.objects.addElement(font); - } + public PDFFontDescriptor makeFontDescriptor(FontDescriptor desc, + byte subtype) { + PDFFontDescriptor font = null; + + if (subtype == PDFFont.TYPE0) { + // CID Font + font = + new PDFCIDFontDescriptor(++this.objectcount, + desc.fontName(), + desc.getFontBBox(), + //desc.getAscender(), + //desc.getDescender(), + desc.getCapHeight(), + desc.getFlags(), + //new PDFRectangle(desc.getFontBBox()), + desc.getItalicAngle(), + desc.getStemV(), + null); //desc.getLang(), + //null);//desc.getPanose()); + } else { + // Create normal FontDescriptor + font = + new PDFFontDescriptor(++this.objectcount, + desc.fontName(), + desc.getAscender(), + desc.getDescender(), + desc.getCapHeight(), + desc.getFlags(), + new PDFRectangle(desc.getFontBBox()), + desc.getStemV(), + desc.getItalicAngle()); + } + // Check if the font is embeddable + if (desc.isEmbeddable()) { + PDFStream stream=desc.getFontFile(this.objectcount+1); + if (stream!=null) { + this.objectcount++; + font.setFontFile(desc.getSubType(), stream); + this.objects.addElement(font); + this.objects.addElement(stream); + } + } else { + this.objects.addElement(font); + } return font; - } + } /** diff --git a/src/org/apache/fop/pdf/PDFFont.java b/src/org/apache/fop/pdf/PDFFont.java index fefc42547..ed43a8017 100644 --- a/src/org/apache/fop/pdf/PDFFont.java +++ b/src/org/apache/fop/pdf/PDFFont.java @@ -136,10 +136,9 @@ public class PDFFont extends PDFObject { public static PDFFont createFont(int number, String fontname, byte subtype, String basefont, Object encoding) { switch (subtype) { - /* case TYPE0 : - return new PDFFontType0(number, fontname, subtype, basefont, encoding); - */ + return new PDFFontType0(number, fontname, subtype, + basefont, encoding); case TYPE1 : case MMTYPE1 : return new PDFFontType1(number, fontname, subtype, basefont, encoding); @@ -175,7 +174,10 @@ public class PDFFont extends PDFObject { PDFFontNonBase14 font; switch (subtype) { case TYPE0 : - return null; // should not happend + font = new PDFFontType0(number, fontname, subtype, + basefont, encoding); + font.setDescriptor(descriptor); + return font; case TYPE1 : case MMTYPE1 : font = new PDFFontType1(number, fontname, subtype, basefont, encoding); diff --git a/src/org/apache/fop/pdf/PDFFontDescriptor.java b/src/org/apache/fop/pdf/PDFFontDescriptor.java index 97fd60e72..b13030d24 100644 --- a/src/org/apache/fop/pdf/PDFFontDescriptor.java +++ b/src/org/apache/fop/pdf/PDFFontDescriptor.java @@ -180,10 +180,13 @@ public class PDFFontDescriptor extends PDFObject { p.append("\n/FontFile "); break; case PDFFont.TRUETYPE: + p.append("\n/FontFile2 "); + break; + case PDFFont.TYPE0: p.append("\n/FontFile2 "); break; default: - p.append("\n/FontFile3 "); + p.append("\n/FontFile2 "); } p.append(fontfile.referencePDF()); } diff --git a/src/org/apache/fop/pdf/PDFFontType0.java b/src/org/apache/fop/pdf/PDFFontType0.java index 7cc0003ca..7ca15647c 100644 --- a/src/org/apache/fop/pdf/PDFFontType0.java +++ b/src/org/apache/fop/pdf/PDFFontType0.java @@ -49,17 +49,18 @@ */ package org.apache.fop.pdf; - /** * class representing a Type0 font. * * Type0 fonts are specified on page 208 and onwards of the PDF 1.3 spec. */ -public class PDFFontType0 extends PDFFont { +public class PDFFontType0 extends PDFFontNonBase14 { /** this should be an array of CIDFont but only the first one is used */ protected PDFCIDFont descendantFonts; + protected PDFCMap cmap; + /** * create the /Font object * @@ -78,6 +79,7 @@ public class PDFFontType0 extends PDFFont { /* set fields using paramaters */ this.descendantFonts = null; + cmap=null; } /** @@ -111,13 +113,19 @@ public class PDFFontType0 extends PDFFont { this.descendantFonts = descendantFonts; } + public void setCMAP(PDFCMap cmap) { + this.cmap=cmap; + } /** * fill in the specifics for the font's subtype */ - protected void fillInPDF(StringBuffer p) { - if (descendantFonts != null) { - p.append("\n/DescendantFonts [ "+ this.descendantFonts.referencePDF() + " ] "); - } - - } + protected void fillInPDF(StringBuffer p) { + if (descendantFonts != null) { + p.append("\n/DescendantFonts [ " + + this.descendantFonts.referencePDF() + " ] "); + } + if (cmap != null) { + p.append("\n/ToUnicode "+cmap.referencePDF()); + } + } } diff --git a/src/org/apache/fop/pdf/PDFWArray.java b/src/org/apache/fop/pdf/PDFWArray.java index acbdd07c5..be1ed8b8e 100644 --- a/src/org/apache/fop/pdf/PDFWArray.java +++ b/src/org/apache/fop/pdf/PDFWArray.java @@ -132,7 +132,7 @@ public class PDFWArray { metrics = m; } public void fillInPDF(StringBuffer p) { - p.setLength(0); + //p.setLength(0); p.append(start); p.append(" ["); for (int i = 0; i < metrics.length; i++) { p.append(this.metrics[i]); diff --git a/src/org/apache/fop/render/pdf/CIDFont.java b/src/org/apache/fop/render/pdf/CIDFont.java new file mode 100644 index 000000000..a8eff598b --- /dev/null +++ b/src/org/apache/fop/render/pdf/CIDFont.java @@ -0,0 +1,29 @@ +package org.apache.fop.render.pdf; + +import org.apache.fop.pdf.PDFWArray; + +public abstract class CIDFont extends Font { + + // Required + public abstract String getCidBaseFont(); + public abstract byte getCidType(); + public abstract String getCharEncoding(); + public abstract String getRegistry(); + public abstract String getOrdering(); + public abstract int getSupplement(); + // Optional + public int getDefaultWidth() { return 0; } + public PDFWArray getWidths() { return null; } + // public int getWinCharSet() { return 0; } + + // Need For FOP + + /** + *Returns CMap Object . + *<p> + *If this method does not return null , the mapping from character codes + *to a font number is performed in FOP . When the getCidType() method + *returns CIDFontType2 , this method must not return null . + */ + public CMap getCMap() { return null; } +} diff --git a/src/org/apache/fop/render/pdf/CMap.java b/src/org/apache/fop/render/pdf/CMap.java new file mode 100644 index 000000000..8b40e3ec3 --- /dev/null +++ b/src/org/apache/fop/render/pdf/CMap.java @@ -0,0 +1,55 @@ +/*-- $Id$ -- + + ============================================================================ + The Apache Software License, Version 1.1 + ============================================================================ + + Copyright (C) 1999 The Apache Software Foundation. All rights reserved. + + Redistribution and use in source and binary forms, with or without modifica- + tion, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. The end-user documentation included with the redistribution, if any, must + include the following acknowledgment: "This product includes software + developed by the Apache Software Foundation (http://www.apache.org/)." + Alternately, this acknowledgment may appear in the software itself, if + and wherever such third-party acknowledgments normally appear. + + 4. The names "Fop" and "Apache Software Foundation" must not be used to + endorse or promote products derived from this software without prior + written permission. For written permission, please contact + apache@apache.org. + + 5. Products derived from this software may not be called "Apache", nor may + "Apache" appear in their name, without prior written permission of the + Apache Software Foundation. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- + DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + This software consists of voluntary contributions made by many individuals + on behalf of the Apache Software Foundation and was originally created by + James Tauber <jtauber@jtauber.com>. For more information on the Apache + Software Foundation, please see <http://www.apache.org/>. + + */ +package org.apache.fop.render.pdf; + +public interface CMap { + public abstract char mapping(char ch); +} diff --git a/src/org/apache/fop/render/pdf/Font.java b/src/org/apache/fop/render/pdf/Font.java index 2801d558a..3bb151a04 100644 --- a/src/org/apache/fop/render/pdf/Font.java +++ b/src/org/apache/fop/render/pdf/Font.java @@ -58,22 +58,38 @@ import org.apache.fop.layout.FontMetric; */ public abstract class Font implements FontMetric { - /** - * get the encoding of the font - */ + /** + * get the encoding of the font + */ public abstract String encoding(); - /** - * get the base font name - */ + /** + * get the base font name + */ public abstract String fontName(); + + /** + * get the subtype of the font, default is TYPE1 + */ + public byte getSubType() { + return org.apache.fop.pdf.PDFFont.TYPE1; + } - /** - * get the subtype of the font, default is TYPE1 - */ - public byte getSubType() { - return org.apache.fop.pdf.PDFFont.TYPE1; - } + /** + * Provide a default mapping + */ + public char mapChar(char c) { + // Use default CodePointMapping + if (c > 127) { + char d = org.apache.fop.render.pdf.CodePointMapping.map[c]; + if (d != 0) { + c = d; + } else { + c = '#'; + } + } + return c; + } } diff --git a/src/org/apache/fop/render/pdf/FontReader.java b/src/org/apache/fop/render/pdf/FontReader.java new file mode 100644 index 000000000..153ab4a36 --- /dev/null +++ b/src/org/apache/fop/render/pdf/FontReader.java @@ -0,0 +1,334 @@ +/*-- $Id$ -- + + ============================================================================ + The Apache Software License, Version 1.1 + ============================================================================ + + Copyright (C) 1999 The Apache Software Foundation. All rights reserved. + + Redistribution and use in source and binary forms, with or without modifica- + tion, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. The end-user documentation included with the redistribution, if any, must + include the following acknowledgment: "This product includes software + developed by the Apache Software Foundation (http://www.apache.org/)." + Alternately, this acknowledgment may appear in the software itself, if + and wherever such third-party acknowledgments normally appear. + + 4. The names "Fop" and "Apache Software Foundation" must not be used to + endorse or promote products derived from this software without prior + written permission. For written permission, please contact + apache@apache.org. + + 5. Products derived from this software may not be called "Apache", nor may + "Apache" appear in their name, without prior written permission of the + Apache Software Foundation. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- + DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + This software consists of voluntary contributions made by many individuals + on behalf of the Apache Software Foundation and was originally created by + James Tauber <jtauber@jtauber.com>. For more information on the Apache + Software Foundation, please see <http://www.apache.org/>. + + */ + +package org.apache.fop.render.pdf; +import org.apache.fop.render.pdf.fonts.*; +import org.xml.sax.helpers.DefaultHandler; +import org.xml.sax.XMLReader; +import org.xml.sax.SAXException; +import org.xml.sax.InputSource; +import org.xml.sax.Locator; +import org.xml.sax.Attributes; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Vector; +import java.util.Hashtable; +import org.apache.fop.pdf.PDFWArray; +import org.apache.fop.pdf.PDFCIDFont; +import org.apache.fop.configuration.ConfigurationReader; + +/** + * Class for reading a metric.xml file and creating a font object. + * Typical usage: + <pre> + FontReader reader = new FontReader(<path til metrics.xml>); + reader.setFontEmbedPath(<path to a .ttf or .pfb file or null to diable embedding>); + reader.useKerning(true); + Font f = reader.getFont(); + </pre> +*/ +public class FontReader extends DefaultHandler { + private Locator locator = null; + private boolean isCID = false; + private MultiByteFont multiFont = null; + private SingleByteFont singleFont = null; + private Font returnFont = null; + //private SingleByteFont singleFont = null; + private String text = null; + + private Vector cidWidths = null; + private int cidWidthIndex = 0; + + private Hashtable currentKerning = null; + + private Vector bfranges = null; + + private void createFont(String path) throws IOException { + XMLReader parser = ConfigurationReader.createParser(); + if (parser == null) + throw new IOException("Unable to create SAX parser"); + + try { + parser.setFeature("http://xml.org/sax/features/namespace-prefixes", + false); + } catch (SAXException e) { + throw new IOException ("You need a SAX parser which supports " + + "SAX version 2"); + } + + parser.setContentHandler(this); + + try { + parser.parse(path); + } catch (SAXException e) { + throw new IOException(e.getMessage()); + } + } + + /** + * Sets the path to embed a font. a null value disables font embedding + */ + public void setFontEmbedPath(String path) { + if (isCID) + multiFont.embedFileName=path; + else + singleFont.embedFileName=path; + } + + /** + * Enable/disable use of kerning for the font + */ + public void useKerning(boolean kern) { + if (isCID) + multiFont.useKerning = true; + else + singleFont.useKerning = true; + } + + + /** + * Get the generated font object + */ + public Font getFont() { + return returnFont; + } + + /** + * Construct a FontReader object from a path to a metric.xml file + * and read metric data + */ + public FontReader(String path) throws IOException { + createFont(path); + } + + public void startDocument() { + } + + public void setDocumentLocator(Locator locator) { + this.locator = locator; + } + + public void startElement(String uri, String localName, + String qName, Attributes attributes) { + if (localName.equals("font-metrics")) { + if ("TYPE0".equals(attributes.getValue("type"))) { + multiFont = new MultiByteFont(); + returnFont = multiFont; + isCID = true; + } else if ("TRUETYPE".equals(attributes.getValue("type"))) { + singleFont = new SingleByteFont(); + singleFont.subType = org.apache.fop.pdf.PDFFont.TRUETYPE; + returnFont = singleFont; + isCID = false; + } else { + singleFont = new SingleByteFont(); + singleFont.subType = org.apache.fop.pdf.PDFFont.TYPE1; + returnFont = singleFont; + isCID = false; + } + } else if ("embed".equals(localName)) { + if (isCID) { + // This *is* annoying... should create a common + // interface for sing/multibytefonts... + multiFont.embedFileName = attributes.getValue("file"); + multiFont.embedResourceName = attributes.getValue("class"); + } else { + singleFont.embedFileName = attributes.getValue("file"); + singleFont.embedResourceName = attributes.getValue("class"); + } + } else if ("cid-widths".equals(localName)) { + cidWidthIndex = getInt(attributes.getValue("start-index")); + cidWidths = new Vector(); + } else if ("kerning".equals(localName)) { + currentKerning = new Hashtable(); + if (isCID) + multiFont.kerning.put(new Integer(attributes.getValue("kpx1")), + currentKerning); + else + singleFont.kerning.put(new Integer(attributes.getValue("kpx1")), + currentKerning); + } else if ("bfranges".equals(localName)) { + bfranges = new Vector(); + } else if ("bf".equals(localName)) { + BFEntry entry = new BFEntry(); + entry.unicodeStart = getInt(attributes.getValue("us")); + entry.unicodeEnd = getInt(attributes.getValue("ue")); + entry.glyphStartIndex = getInt(attributes.getValue("gi")); + bfranges.addElement(entry); + } else if ("wx".equals(localName)) { + cidWidths.addElement(new Integer(attributes.getValue("w"))); + } else if ("widths".equals(localName)) { + singleFont.width = new int[256]; + } else if ("char".equals(localName)) { + try { + singleFont.width[Integer.parseInt(attributes.getValue("idx"))] = + Integer.parseInt(attributes.getValue("wdt")); + } catch (NumberFormatException ne) { + System.out.println("Malformed width in metric file: " + + ne.getMessage()); + } + } else if ("pair".equals(localName)) { + currentKerning.put(new Integer(attributes.getValue("kpx2")), + new Integer(attributes.getValue("kern"))); + } + } + + private int getInt(String str) { + int ret = 0; + try { + ret = Integer.parseInt(str); + } catch (Exception e) {} + return ret; + } + + public void endElement(String uri, String localName, String qName) { + if ("font-name".equals(localName)) + if (isCID) + multiFont.fontName = text; + else + singleFont.fontName = text; + else if ("cap-height".equals(localName)) + if (isCID) + multiFont.capHeight = getInt(text); + else + singleFont.capHeight = getInt(text); + else if ("x-height".equals(localName)) + if (isCID) + multiFont.xHeight = getInt(text); + else + singleFont.xHeight = getInt(text); + else if ("ascender".equals(localName)) + if (isCID) + multiFont.ascender = getInt(text); + else + singleFont.ascender = getInt(text); + else if ("descender".equals(localName)) + if (isCID) + multiFont.descender = getInt(text); + else + singleFont.descender = getInt(text); + else if ("left".equals(localName)) + if (isCID) + multiFont.fontBBox[0] = getInt(text); + else + singleFont.fontBBox[0] = getInt(text); + else if ("bottom".equals(localName)) + if (isCID) + multiFont.fontBBox[1] = getInt(text); + else + singleFont.fontBBox[1] = getInt(text); + else if ("right".equals(localName)) + if (isCID) + multiFont.fontBBox[2] = getInt(text); + else + singleFont.fontBBox[2] = getInt(text); + else if ("first-char".equals(localName)) + singleFont.firstChar = getInt(text); + else if ("last-char".equals(localName)) + singleFont.lastChar = getInt(text); + else if ("top".equals(localName)) + if (isCID) + multiFont.fontBBox[3] = getInt(text); + else + singleFont.fontBBox[3] = getInt(text); + else if ("flags".equals(localName)) + if (isCID) + multiFont.flags = getInt(text); + else + singleFont.flags = getInt(text); + else if ("stemv".equals(localName)) + if (isCID) + multiFont.stemV = getInt(text); + else + singleFont.stemV = getInt(text); + else if ("italic-angle".equals(localName)) + if (isCID) + multiFont.italicAngle = getInt(text); + else + singleFont.italicAngle = getInt(text); + else if ("missing-width".equals(localName)) + if (isCID) + multiFont.missingWidth = getInt(text); + else + singleFont.missingWidth = getInt(text); + else if ("cid-type".equals(localName)) { + if ("CIDFontType2".equals(text)) + multiFont.cidType = PDFCIDFont.CID_TYPE2; + } else if ("default-width".equals(localName)) { + multiFont.defaultWidth = getInt(text); + } else if ("cid-widths".equals(localName)) { + int[] wds = new int[cidWidths.size()]; + int j = 0; + for (Enumeration e = cidWidths.elements(); e.hasMoreElements();) { + Integer i = (Integer)e.nextElement(); + wds[j++] = i.intValue(); + } + + multiFont.warray.addEntry(cidWidthIndex, wds); + multiFont.width = wds; + + } else if ("bfranges".equals(localName)) { + BFEntry[] entries = new BFEntry[bfranges.size()]; + bfranges.copyInto(entries); + multiFont.bfentries = entries; + } + + } + + public void characters(char[] ch, int start, int length) { + char c[] = new char[length]; + System.arraycopy(ch, start, c, 0, length); + text = new String(c); + } + +} + + diff --git a/src/org/apache/fop/render/pdf/FontSetup.java b/src/org/apache/fop/render/pdf/FontSetup.java index c2209a921..396d9cb46 100644 --- a/src/org/apache/fop/render/pdf/FontSetup.java +++ b/src/org/apache/fop/render/pdf/FontSetup.java @@ -57,10 +57,13 @@ import org.apache.fop.layout.FontInfo; import org.apache.fop.layout.FontDescriptor; import org.apache.fop.pdf.PDFDocument; import org.apache.fop.pdf.PDFResources; +import org.apache.fop.configuration.Configuration; +import org.apache.fop.configuration.FontTriplet; // Java import java.util.Enumeration; import java.util.Hashtable; +import java.util.Vector; /** * sets up the PDF fonts. @@ -95,7 +98,7 @@ public class FontSetup { fontInfo.addMetrics("F12", new CourierBoldOblique()); fontInfo.addMetrics("F13", new Symbol()); fontInfo.addMetrics("F14", new ZapfDingbats()); - + //Custom type 1 fonts step 1/2 // fontInfo.addMetrics("F15", new OMEP()); // fontInfo.addMetrics("F16", new GaramondLightCondensed()); @@ -209,8 +212,53 @@ public class FontSetup { "bold"); fontInfo.addFontProperties("F9", "Computer-Modern-Typewriter", "normal", "normal"); + + /* Add configured fonts */ + addConfiguredFonts(fontInfo, 15); } + /** + * Add fonts from configuration file starting with + * internalnames F<num> + */ + public static void addConfiguredFonts(FontInfo fontInfo, int num) { + + String internalName=null; + FontReader reader = null; + + Vector fontInfos = Configuration.getFonts(); + for (Enumeration e = fontInfos.elements(); e.hasMoreElements();) { + org.apache.fop.configuration.FontInfo configFontInfo = + (org.apache.fop.configuration.FontInfo)e.nextElement(); + + try { + String metricsFile = configFontInfo.getMetricsFile(); + if (metricsFile != null) { + internalName = "F"+num; + num++; + reader = new FontReader(metricsFile); + reader.useKerning(configFontInfo.getKerning()); + reader.setFontEmbedPath(configFontInfo.getEmbedFile()); + fontInfo.addMetrics(internalName, reader.getFont()); + + Vector triplets = configFontInfo.getFontTriplets(); + for (Enumeration t = triplets.elements(); t.hasMoreElements();) { + FontTriplet triplet = (FontTriplet)t.nextElement(); + + fontInfo.addFontProperties(internalName, + triplet.getName(), + triplet.getStyle(), + triplet.getWeight()); + } + } + } catch (Exception ex) { + MessageHandler.error("Failed to read font metrics file " + + configFontInfo.getMetricsFile() + + " : " + ex.getMessage()); + } + } + } + /** * add the fonts in the font info to the PDF document * @@ -218,7 +266,7 @@ public class FontSetup { * @param fontInfo font info object to get font information from */ public static void addToResources(PDFDocument doc, FontInfo fontInfo) { - Hashtable fonts = fontInfo.getFonts(); + Hashtable fonts = fontInfo.getUsedFonts(); Enumeration e = fonts.keys(); PDFResources resources = doc.getResources(); while (e.hasMoreElements()) { diff --git a/src/org/apache/fop/render/pdf/PDFRenderer.java b/src/org/apache/fop/render/pdf/PDFRenderer.java index 0954856d5..ae7c0d1c6 100644 --- a/src/org/apache/fop/render/pdf/PDFRenderer.java +++ b/src/org/apache/fop/render/pdf/PDFRenderer.java @@ -147,6 +147,8 @@ public class PDFRenderer implements Renderer { /** the current colour for use in svg */ private PDFColor currentColour = new PDFColor(0, 0, 0); + private FontInfo fontInfo; + // previous values used for text-decoration drawing int prevUnderlineXEndPos; int prevUnderlineYEndPos; @@ -175,8 +177,6 @@ public class PDFRenderer implements Renderer { /** The width of the previous word. Used to calculate space between */ int prevWordWidth = 0; - boolean useKerning; - private PDFOutline rootOutline; /** @@ -184,10 +184,6 @@ public class PDFRenderer implements Renderer { */ public PDFRenderer() { this.pdfDoc = new PDFDocument(); - - String cfgKern = Configuration.getStringValue("use-kerning"); - useKerning = ("yes".equals(cfgKern) || "true".equals(cfgKern)); - } /** @@ -224,6 +220,8 @@ public class PDFRenderer implements Renderer { } renderRootExtensions(areaTree); + FontSetup.addToResources(this.pdfDoc, fontInfo); + MessageHandler.logln("writing out PDF"); this.pdfDoc.output(stream); } @@ -654,16 +652,23 @@ public class PDFRenderer implements Renderer { Hashtable kerning = null; boolean kerningAvailable = false; - if (useKerning) { - kerning = area.getFontState().getKerning(); - if (kerning != null && !kerning.isEmpty()) { - kerningAvailable = true; - } - } - + kerning = area.getFontState().getKerning(); + if (kerning != null && !kerning.isEmpty()) { + kerningAvailable = true; + } + String name = area.getFontState().getFontName(); int size = area.getFontState().getFontSize(); + // This assumes that *all* CIDFonts use a /ToUnicode mapping + boolean useMultiByte = false; + Font f = (Font)area.getFontState().getFontInfo().getFonts().get(name); + if (f instanceof CIDFont) + useMultiByte=true; + //String startText = useMultiByte ? "<FEFF" : "("; + String startText = useMultiByte ? "<" : "("; + String endText = useMultiByte ? ">" : ")"; + PDFColor theAreaColor = new PDFColor((double) area.getRed(), (double) area.getGreen(), (double) area.getBlue()); @@ -726,7 +731,7 @@ public class PDFRenderer implements Renderer { closeText(); pdf.append("1 0 0 1 " +(rx / 1000f) + " " + - (bl / 1000f) + " Tm [("); + (bl / 1000f) + " Tm [" + startText); prevWordY = bl; textOpen = true; } @@ -734,7 +739,7 @@ public class PDFRenderer implements Renderer { // express the space between words in thousandths of an em int space = prevWordX - rx + prevWordWidth; float emDiff = (float)space / (float)currentFontSize * 1000f; - pdf.append(emDiff + " ("); + pdf.append(emDiff + " " + startText); } prevWordWidth = area.getContentWidth(); prevWordX = rx; @@ -745,10 +750,10 @@ public class PDFRenderer implements Renderer { // for every word. pdf.append("1 0 0 1 " +(rx / 1000f) + " " + (bl / 1000f) + " Tm "); if (kerningAvailable) { - pdf.append(" [("); + pdf.append(" [" + startText); } else { - pdf.append(" ("); + pdf.append(" " + startText); } } @@ -768,27 +773,33 @@ public class PDFRenderer implements Renderer { char ch = s.charAt(i); String prepend = ""; - if (ch > 127) { - pdf.append("\\"); - pdf.append(Integer.toOctalString((int) ch)); - } else { - switch (ch) { - case '(': - case ')': - case '\\': - prepend = "\\"; - break; + if (!useMultiByte) { + if(ch > 127) { + pdf.append("\\"); + pdf.append(Integer.toOctalString((int) ch)); + } else { + switch (ch) { + case '(': + case ')': + case '\\': + prepend = "\\"; + break; + } + pdf.append(getUnicodeString(prepend+ch, useMultiByte)); } - pdf.append(prepend+ch); - } + } else { + pdf.append(getUnicodeString(prepend+ch, useMultiByte)); + } + if (kerningAvailable && (i+1) < l) { - pdf.append(addKerning((new Character(ch)).toString(), - (new Character(s.charAt(i+1))).toString(), - kerning)); - } - + pdf.append(addKerning((new Integer((int)ch)), + (new Integer((int)s.charAt(i+1))), + kerning, + startText, endText)); + } + } - pdf.append(") "); + pdf.append(endText + " "); if (!OPTIMIZE_TEXT) { if (kerningAvailable) { pdf.append("] TJ\n"); @@ -807,6 +818,45 @@ public class PDFRenderer implements Renderer { } + + /** + * Convert a string to a unicode hex representation + */ + private String getUnicodeString(StringBuffer str, boolean useMultiByte) { + return getUnicodeString(str.toString(), useMultiByte); + } + + /** + * Convert a string to a multibyte hex representation + */ + private String getUnicodeString(String str, boolean useMultiByte) { + if (!useMultiByte) { + return str; + } else { + StringBuffer buf = new StringBuffer(str.length()*4); + byte[] uniBytes = null; + try { + uniBytes = str.getBytes("UnicodeBigUnmarked"); + } catch (Exception e) { + // This should never fail + } + + for (int i = 0; i < uniBytes.length; i++) { + int b = (uniBytes[i] < 0) ? (int)(256+uniBytes[i]) + : (int)uniBytes[i]; + + String hexString=Integer.toHexString(b); + if (hexString.length()==1) + buf=buf.append("0"+hexString); + else + buf=buf.append(hexString); + } + + return buf.toString(); + } + } + + /** Checks to see if we have some text rendering commands open * still and writes out the TJ command to the stream if we do */ @@ -886,7 +936,9 @@ public class PDFRenderer implements Renderer { } - private StringBuffer addKerning(String ch1, String ch2, Hashtable kerning) { + private StringBuffer addKerning(Integer ch1, Integer ch2, + Hashtable kerning, String startText, + String endText) { Hashtable h2=(Hashtable)kerning.get(ch1); int pwdt=0; StringBuffer buf=new StringBuffer(""); @@ -895,7 +947,7 @@ public class PDFRenderer implements Renderer { Integer wdt=(Integer)h2.get(ch2); if (wdt!=null) { pwdt=-wdt.intValue(); - buf=buf.append(") " + pwdt + " ("); + buf=buf.append(endText + " " + pwdt + " " + startText); } } return buf; @@ -1021,8 +1073,8 @@ public class PDFRenderer implements Renderer { * @param fontInfo font info to set up */ public void setupFontInfo(FontInfo fontInfo) { + this.fontInfo = fontInfo; FontSetup.setup(fontInfo); - FontSetup.addToResources(this.pdfDoc, fontInfo); } /** diff --git a/src/org/apache/fop/render/pdf/fonts/BFEntry.java b/src/org/apache/fop/render/pdf/fonts/BFEntry.java new file mode 100644 index 000000000..a0f8f198c --- /dev/null +++ b/src/org/apache/fop/render/pdf/fonts/BFEntry.java @@ -0,0 +1,73 @@ +/*-- $Id$ -- + + ============================================================================ + The Apache Software License, Version 1.1 + ============================================================================ + + Copyright (C) 1999 The Apache Software Foundation. All rights reserved. + + Redistribution and use in source and binary forms, with or without modifica- + tion, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. The end-user documentation included with the redistribution, if any, must + include the following acknowledgment: "This product includes software + developed by the Apache Software Foundation (http://www.apache.org/)." + Alternately, this acknowledgment may appear in the software itself, if + and wherever such third-party acknowledgments normally appear. + + 4. The names "Fop" and "Apache Software Foundation" must not be used to + endorse or promote products derived from this software without prior + written permission. For written permission, please contact + apache@apache.org. + + 5. Products derived from this software may not be called "Apache", nor may + "Apache" appear in their name, without prior written permission of the + Apache Software Foundation. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- + DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + This software consists of voluntary contributions made by many individuals + on behalf of the Apache Software Foundation and was originally created by + James Tauber <jtauber@jtauber.com>. For more information on the Apache + Software Foundation, please see <http://www.apache.org/>. + + */ + +package org.apache.fop.render.pdf.fonts; + +/** + * This is just a holder class for bfentries - not get/put methods provided + */ +public class BFEntry { + public int unicodeStart; + public int unicodeEnd; + public int glyphStartIndex; + + public BFEntry() { + } + + public BFEntry (int unicodeStart, + int unicodeEnd, + int glyphStartIndex) { + this.unicodeStart = unicodeStart; + this.unicodeEnd = unicodeEnd; + this.glyphStartIndex = glyphStartIndex; + } + +} diff --git a/src/org/apache/fop/render/pdf/fonts/MultiByteFont.java b/src/org/apache/fop/render/pdf/fonts/MultiByteFont.java new file mode 100644 index 000000000..7b55e843d --- /dev/null +++ b/src/org/apache/fop/render/pdf/fonts/MultiByteFont.java @@ -0,0 +1,236 @@ + + +package org.apache.fop.render.pdf.fonts; + +import org.apache.fop.render.pdf.Font; +import org.apache.fop.layout.FontDescriptor; +import org.apache.fop.fonts.Glyphs; +import org.apache.fop.pdf.PDFStream; +import org.apache.fop.pdf.PDFWArray; +import org.apache.fop.pdf.PDFCIDFont; +import org.apache.fop.render.pdf.CIDFont; +import org.apache.fop.render.pdf.CMap; +import org.apache.fop.pdf.PDFTTFStream; +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.File; +import java.io.BufferedInputStream; +import java.util.Hashtable; + +/** + * Generic MultiByte (CID) font + */ +public class MultiByteFont extends CIDFont implements FontDescriptor { + public String fontName = null; + public String encoding = "Identity-H"; + + public int capHeight = 0; + public int xHeight = 0; + public int ascender = 0; + public int descender = 0; + public int[] fontBBox = {0, 0, 0, 0}; + + public String embedFileName = null; + public String embedResourceName = null; + public PDFTTFStream embeddedFont=null; + + public int flags = 4; + public int stemV = 0; + public int italicAngle = 0; + public int missingWidth = 0; + public int defaultWidth = 0; + public byte cidType = PDFCIDFont.CID_TYPE2; + + public Hashtable kerning=new Hashtable(); + public boolean useKerning = true; + + public PDFWArray warray=new PDFWArray(); + public int width[] = null; + + public BFEntry[] bfentries = null; + + + public MultiByteFont() {} + + public final boolean hasKerningInfo() { + return (useKerning & kerning.isEmpty()); + } + public final java.util.Hashtable getKerningInfo() {return kerning;} + + public byte getSubType() { + return org.apache.fop.pdf.PDFFont.TYPE0; + } + + public String getLang() {return null;} + public String getPanose() {return null;} + public int getAvgWidth() {return -1;} + public int getMinWidth() {return -1;} + public int getMaxWidth() {return -1;} + public int getleading() {return -1;} + public int getStemH() {return 0;} + public int getMissingWidth() {return missingWidth;} + public int getDefaultWidth() {return defaultWidth;} + public String getRegistry() {return "Adobe";} + public String getOrdering() {return "UCS";} + public int getSupplement() {return 0;} + public byte getCidType() {return cidType;} + public String getCidBaseFont() {return fontName;} + public String getCharEncoding() {return "Identity-H";} + + public PDFWArray getWidths() { + return warray; + } + + public boolean isEmbeddable() { + return (embedFileName==null && embedResourceName==null) ? false : true; + } + + + public PDFStream getFontFile(int i) { + InputStream instream=null; + + int iniSize = 256000; + int incSize = 128000; + // Get file first + if (embedFileName!=null) + try { + File ef = new File(embedFileName); + iniSize = (int)ef.length()+1; + incSize = (int)ef.length()/10; + instream=new FileInputStream(embedFileName); + } catch (Exception e) { + System.out.println("Failed to embed fontfile: "+embedFileName); + } + + // Get resource + if (instream==null && embedResourceName!=null) + try { + instream=new BufferedInputStream(this.getClass().getResourceAsStream(embedResourceName)); + } catch (Exception e) { + System.out.println("Failed to embed fontresource: "+embedResourceName); + } + + if (instream==null) + return (PDFStream)null; + + // Read fontdata + byte[] file = new byte[iniSize]; + int fsize = 0; + + try { + int l = instream.read(file, 0, iniSize); + fsize += l; + + if (l==iniSize) { + // More to read - needs to extend + byte[] tmpbuf; + + while (l > 0) { + tmpbuf = new byte[file.length + incSize]; + System.arraycopy(file, 0, tmpbuf, 0, file.length); + l=instream.read(tmpbuf, file.length, incSize); + fsize += l; + file = tmpbuf; + + if (l < incSize) // whole file read. No need to loop again + l=0; + } + } + + // Only TrueType CID fonts are supported now + embeddedFont=new PDFTTFStream(i, fsize); + embeddedFont.addFilter("flate"); + embeddedFont.addFilter("ascii-85"); + embeddedFont.setData(file, fsize); + instream.close(); + } catch (Exception e) {} + + return (PDFStream) embeddedFont; + } + + public String encoding() { + return encoding; + } + + public String fontName() { + return fontName; + } + + public int getAscender() {return ascender;} + public int getDescender() {return descender;} + public int getCapHeight() {return capHeight;} + + public int getAscender(int size) { + return size * ascender; + } + + public int getCapHeight(int size) { + return size * capHeight; + } + + public int getDescender(int size) { + return size * descender; + } + + public int getXHeight(int size) { + return size * xHeight; + } + + public int getFlags() { + return flags; + } + + public int[] getFontBBox() { + return fontBBox; + } + + public int getItalicAngle() { + return italicAngle; + } + + public int getStemV() { + return stemV; + } + + public int getFirstChar() { + return 0; + } + + public int getLastChar() { + return 255; + } + + public int width(int i, int size) { + return size * width[i]; + } + + public int[] getWidths(int size) { + int[] arr = new int[width.length]; + System.arraycopy(width, 0, arr, 0, width.length-1); + for( int i = 0; i < arr.length; i++) arr[i] *= size; + return arr; + } + + public char mapChar(char c) { + int idx = (int)c; + int retIdx = 0; + + for (int i = 0; (i < bfentries.length) && retIdx == 0; i++) { + + /* + System.out.println("us: "+bfentries[i].unicodeStart + + " ue: "+bfentries[i].unicodeEnd+ + " gi: "+bfentries[i].glyphStartIndex); + */ + if (bfentries[i].unicodeStart <= idx && + bfentries[i].unicodeEnd >= idx) { + retIdx=bfentries[i].glyphStartIndex + idx - + bfentries[i].unicodeStart; + } + } + + //System.out.println("Map: "+ c + " (" + idx + ") = " + retIdx); + return (char)retIdx; + } +} + diff --git a/src/org/apache/fop/render/pdf/fonts/SingleByteFont.java b/src/org/apache/fop/render/pdf/fonts/SingleByteFont.java new file mode 100644 index 000000000..413d2e7d3 --- /dev/null +++ b/src/org/apache/fop/render/pdf/fonts/SingleByteFont.java @@ -0,0 +1,196 @@ + + +package org.apache.fop.render.pdf.fonts; + +import org.apache.fop.render.pdf.Font; +import org.apache.fop.layout.FontDescriptor; +import org.apache.fop.fonts.Glyphs; +import org.apache.fop.pdf.PDFStream; +import org.apache.fop.pdf.PDFTTFStream; +import org.apache.fop.pdf.PDFT1Stream; +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.BufferedInputStream; +import java.util.Hashtable; + +/** + * Generic SingleByte font + */ +public class SingleByteFont extends Font implements FontDescriptor { + public String fontName = null; + public String encoding = "WinAnsiEncoding"; + + public int capHeight = 0; + public int xHeight = 0; + public int ascender = 0; + public int descender = 0; + public int[] fontBBox = {0, 0, 0, 0}; + + public String embedFileName = null; + public String embedResourceName = null; + public PDFStream embeddedFont=null; + + public int firstChar = 0; + public int lastChar = 255; + public int flags = 4; + public int stemV = 0; + public int italicAngle = 0; + public int missingWidth = 0; + + public Hashtable kerning=new Hashtable(); + public boolean useKerning = true; + + public int width[] = null; + public byte subType = 0; + + public final boolean hasKerningInfo() { + return (useKerning & kerning.isEmpty()); + } + + public final java.util.Hashtable getKerningInfo() {return kerning;} + + public byte getSubType() { + return subType; + } + + public int getAvgWidth() {return -1;} + public int getMinWidth() {return -1;} + public int getMaxWidth() {return -1;} + public int getleading() {return -1;} + public int getStemH() {return 0;} + public int getMissingWidth() {return missingWidth;} + + public String getCharEncoding() {return encoding;} + + public boolean isEmbeddable() { + return (embedFileName==null && embedResourceName==null) ? false : true; + } + + + public PDFStream getFontFile(int i) { + InputStream instream=null; + + // Get file first + if (embedFileName!=null) + try { + instream=new FileInputStream(embedFileName); + } catch (Exception e) { + System.out.println("Failed to embed fontfile: "+embedFileName); + } + + // Get resource + if (instream==null && embedResourceName!=null) + try { + instream=new BufferedInputStream(this.getClass().getResourceAsStream(embedResourceName)); + } catch (Exception e) { + System.out.println("Failed to embed fontresource: "+embedResourceName); + } + + if (instream==null) + return (PDFStream)null; + + // Read fontdata + byte[] file = new byte[128000]; + int fsize = 0; + + try { + int l = instream.read(file, 0, 128000); + fsize += l; + + if (l==128000) { + // More to read - needs to extend + byte[] tmpbuf; + + while (l > 0) { + tmpbuf = new byte[file.length + 64000]; + System.arraycopy(file, 0, tmpbuf, 0, file.length); + l=instream.read(tmpbuf, file.length, 64000); + fsize += l; + file = tmpbuf; + + if (l < 64000) // whole file read. No need to loop again + l=0; + } + } + + if (subType == org.apache.fop.pdf.PDFFont.TYPE1) { + embeddedFont=new PDFT1Stream(i, fsize); + ((PDFT1Stream)embeddedFont).setData(file, fsize); + } else { + embeddedFont=new PDFTTFStream(i, fsize); + ((PDFTTFStream)embeddedFont).setData(file, fsize); + } + + embeddedFont.addFilter("flate"); + embeddedFont.addFilter("ascii-85"); + instream.close(); + } catch (Exception e) {} + + return (PDFStream) embeddedFont; + } + + public String encoding() { + return encoding; + } + + public String fontName() { + return fontName; + } + + public int getAscender() {return ascender;} + public int getDescender() {return descender;} + public int getCapHeight() {return capHeight;} + + public int getAscender(int size) { + return size * ascender; + } + + public int getCapHeight(int size) { + return size * capHeight; + } + + public int getDescender(int size) { + return size * descender; + } + + public int getXHeight(int size) { + return size * xHeight; + } + + public int getFlags() { + return flags; + } + + public int[] getFontBBox() { + return fontBBox; + } + + public int getItalicAngle() { + return italicAngle; + } + + public int getStemV() { + return stemV; + } + + public int getFirstChar() { + return 0; + //return firstChar; + } + + public int getLastChar() { + return lastChar; + } + + public int width(int i, int size) { + return size * width[i]; + } + + public int[] getWidths(int size) { + int[] arr = new int[width.length]; + System.arraycopy(width, 0, arr, 0, width.length-1); + for( int i = 0; i < arr.length; i++) arr[i] *= size; + return arr; + } +} + |