Return-Path: Delivered-To: apmail-pdfbox-commits-archive@www.apache.org Received: (qmail 16284 invoked from network); 7 Feb 2010 18:10:01 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 7 Feb 2010 18:10:01 -0000 Received: (qmail 13964 invoked by uid 500); 7 Feb 2010 18:10:01 -0000 Delivered-To: apmail-pdfbox-commits-archive@pdfbox.apache.org Received: (qmail 13939 invoked by uid 500); 7 Feb 2010 18:10:01 -0000 Mailing-List: contact commits-help@pdfbox.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@pdfbox.apache.org Delivered-To: mailing list commits@pdfbox.apache.org Received: (qmail 13930 invoked by uid 99); 7 Feb 2010 18:10:01 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 07 Feb 2010 18:10:01 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 07 Feb 2010 18:09:44 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 1973023889DA; Sun, 7 Feb 2010 18:09:22 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r907461 [1/3] - in /pdfbox/fontbox/trunk/src: main/java/org/apache/fontbox/cff/ main/java/org/apache/fontbox/cff/charset/ main/java/org/apache/fontbox/cff/encoding/ test/java/org/apache/fontbox/cff/ Date: Sun, 07 Feb 2010 18:09:21 -0000 To: commits@pdfbox.apache.org From: lehmi@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20100207180922.1973023889DA@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: lehmi Date: Sun Feb 7 18:09:19 2010 New Revision: 907461 URL: http://svn.apache.org/viewvc?rev=907461&view=rev Log: PDFBOX-542: added support for Adobe CFF/Type2 fonts to FontBox. Patch by Villu Ruusmann (villu dot ruusmann at gmail dot com) Added: pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/ pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/AFMFormatter.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFDataInput.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFFont.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFOperator.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFParser.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFStandardString.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CharStringCommand.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CharStringConverter.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CharStringHandler.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CharStringRenderer.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/DataInput.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/DataOutput.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/Type1CharStringFormatter.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/Type1CharStringParser.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/Type1FontFormatter.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/Type1FontUtil.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/Type2CharStringParser.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/charset/ pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/charset/CFFCharset.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/charset/CFFExpertCharset.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/charset/CFFExpertSubsetCharset.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/charset/CFFISOAdobeCharset.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/charset/package.html pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/encoding/ pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/encoding/CFFEncoding.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/encoding/CFFExpertEncoding.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/encoding/CFFStandardEncoding.java pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/encoding/package.html pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/package.html pdfbox/fontbox/trunk/src/test/java/org/apache/fontbox/cff/ pdfbox/fontbox/trunk/src/test/java/org/apache/fontbox/cff/Type1CharStringTest.java pdfbox/fontbox/trunk/src/test/java/org/apache/fontbox/cff/Type1FontUtilTest.java Added: pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/AFMFormatter.java URL: http://svn.apache.org/viewvc/pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/AFMFormatter.java?rev=907461&view=auto ============================================================================== --- pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/AFMFormatter.java (added) +++ pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/AFMFormatter.java Sun Feb 7 18:09:19 2010 @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +import java.awt.geom.Rectangle2D; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.apache.fontbox.cff.encoding.CFFEncoding; + +/** + * This class creates all needed AFM font metric data from a CFFFont ready to be read from a AFMPaser. + * + * @author Villu Russmann + * @version $Revision$ + */ +public class AFMFormatter +{ + + private AFMFormatter() + { + } + + /** + * Create font metric data for the given CFFFont. + * @param font the CFFFont + * @return the created font metric data + * @throws IOException if an error occurs during reading + */ + public static byte[] format(CFFFont font) throws IOException + { + DataOutput output = new DataOutput(); + printFont(font, output); + return output.getBytes(); + } + + private static void printFont(CFFFont font, DataOutput output) + throws IOException + { + printFontMetrics(font, output); + } + + @SuppressWarnings(value = { "unchecked" }) + private static void printFontMetrics(CFFFont font, DataOutput output) + throws IOException + { + output.println("StartFontMetrics 2.0"); + output.println("FontName " + font.getName()); + output.println("FullName " + font.getProperty("FullName")); + output.println("FamilyName " + font.getProperty("FamilyName")); + output.println("Weight " + font.getProperty("Weight")); + CFFEncoding encoding = font.getEncoding(); + if (encoding.isFontSpecific()) + { + output.println("EncodingScheme FontSpecific"); + } + List fontBBox = (List) font.getProperty("FontBBox"); + output.println("FontBBox " + fontBBox.get(0) + " " + fontBBox.get(1) + + " " + fontBBox.get(2) + " " + fontBBox.get(3)); + printDirectionMetrics(font, output); + printCharMetrics(font, output); + output.println("EndFontMetrics"); + } + + private static void printDirectionMetrics(CFFFont font, DataOutput output) + throws IOException + { + output.println("UnderlinePosition " + + font.getProperty("UnderlinePosition")); + output.println("UnderlineThickness " + + font.getProperty("UnderlineThickness")); + output.println("ItalicAngle " + font.getProperty("ItalicAngle")); + output.println("IsFixedPitch " + font.getProperty("isFixedPitch")); + } + + private static void printCharMetrics(CFFFont font, DataOutput output) + throws IOException + { + List metrics; + try + { + metrics = renderFont(font); + } + catch (IOException ioe) + { + return; + } + output.println("StartCharMetrics " + metrics.size()); + Collections.sort(metrics); + for (CharMetric metric : metrics) + { + output.print("C " + metric.code + " ;"); + output.print(" "); + output.print("WX " + metric.width + " ;"); + output.print(" "); + output.print("N " + metric.name + " ;"); + output.print(" "); + output.print("B " + (int) metric.bounds.getX() + " " + + (int) metric.bounds.getY() + " " + + (int) metric.bounds.getMaxX() + " " + + (int) metric.bounds.getMaxY() + " ;"); + output.println(); + } + output.println("EndCharMetrics"); + } + + private static List renderFont(CFFFont font) throws IOException + { + List metrics = new ArrayList(); + CharStringRenderer renderer = font.createRenderer(); + Collection mappings = font.getMappings(); + for (CFFFont.Mapping mapping : mappings) + { + CharMetric metric = new CharMetric(); + metric.code = mapping.getCode(); + metric.name = mapping.getName(); + renderer.render(mapping.toType1Sequence()); + metric.width = renderer.getWidth(); + metric.bounds = renderer.getBounds(); + metrics.add(metric); + } + return metrics; + } + + /** + * This class represents the metric of one single character. + * + */ + private static class CharMetric implements Comparable + { + private int code; + private String name; + private int width; + private Rectangle2D bounds; + + public int compareTo(CharMetric that) + { + return code - that.code; + } + } +} \ No newline at end of file Added: pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFDataInput.java URL: http://svn.apache.org/viewvc/pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFDataInput.java?rev=907461&view=auto ============================================================================== --- pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFDataInput.java (added) +++ pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFDataInput.java Sun Feb 7 18:09:19 2010 @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +import java.io.IOException; + +/** + * This is specialized DataInput. It's used to parse a CFFFont. + * + * @author Villu Russmann + * @version $Revision$ + */ +public class CFFDataInput extends DataInput +{ + + /** + * Constructor. + * @param buffer the buffer to be read + */ + public CFFDataInput(byte[] buffer) + { + super(buffer); + } + + /** + * Read one single Card8 value from the buffer. + * @return the card8 value + * @throws IOException if an error occurs during reading + */ + public int readCard8() throws IOException + { + return readUnsignedByte(); + } + + /** + * Read one single Card16 value from the buffer. + * @return the card16 value + * @throws IOException if an error occurs during reading + */ + public int readCard16() throws IOException + { + return readUnsignedShort(); + } + + /** + * Read the offset from the buffer. + * @param offSize the given offsize + * @return the offset + * @throws IOException if an error occurs during reading + */ + public int readOffset(int offSize) throws IOException + { + int value = 0; + for (int i = 0; i < offSize; i++) + { + value = value << 8 | readUnsignedByte(); + } + return value; + } + + /** + * Read the offsize from the buffer. + * @return the offsize + * @throws IOException if an error occurs during reading + */ + public int readOffSize() throws IOException + { + return readUnsignedByte(); + } + + /** + * Read a SID from the buffer. + * @return the SID + * @throws IOException if an error occurs during reading + */ + public int readSID() throws IOException + { + return readUnsignedShort(); + } +} \ No newline at end of file Added: pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFFont.java URL: http://svn.apache.org/viewvc/pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFFont.java?rev=907461&view=auto ============================================================================== --- pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFFont.java (added) +++ pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFFont.java Sun Feb 7 18:09:19 2010 @@ -0,0 +1,353 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.fontbox.cff.charset.CFFCharset; +import org.apache.fontbox.cff.encoding.CFFEncoding; + +/** + * This class represents a CFF/Type2 Font. + * + * @author Villu Russmann + * @version $Revision$ + */ +public class CFFFont +{ + + private String fontname = null; + private Map topDict = new LinkedHashMap(); + private Map privateDict = new LinkedHashMap(); + private CFFEncoding fontEncoding = null; + private CFFCharset fontCharset = null; + private Map charStringsDict = new LinkedHashMap(); + + /** + * The name of the font. + * @return the name of the font + */ + public String getName() + { + return fontname; + } + + /** + * Sets the name of the font. + * @param name the name of the font + */ + public void setName(String name) + { + fontname = name; + } + + /** + * Returns the value for the given name from the dictionary. + * @param name the name of the value + * @return the value of the name if available + */ + public Object getProperty(String name) + { + Object topDictValue = topDict.get(name); + if (topDictValue != null) + { + return topDictValue; + } + Object privateDictValue = privateDict.get(name); + if (privateDictValue != null) + { + return privateDictValue; + } + return null; + } + + /** + * Adds the given key/value pair to the top dictionary. + * @param name the given key + * @param value the given value + */ + public void addValueToTopDict(String name, Object value) + { + if (value != null) + { + topDict.put(name, value); + } + } + /** + * Returns the top dictionary. + * @return the dictionary + */ + public Map getTopDict() + { + return topDict; + } + + /** + * Adds the given key/value pair to the private dictionary. + * @param name the given key + * @param value the given value + */ + public void addValueToPrivateDict(String name, Object value) + { + if (value != null) + { + privateDict.put(name, value); + } + } + /** + * Returns the private dictionary. + * @return the dictionary + */ + public Map getPrivateDict() + { + return privateDict; + } + + /** + * Get the mapping (code/SID/charname/bytes) for this font. + * @return mappings for codes < 256 and for codes > = 256 + */ + public Collection getMappings() + { + List mappings = new ArrayList(); + Set mappedNames = new HashSet(); + Iterator codes = fontEncoding.getCodes(); + while (codes.hasNext()) + { + int code = codes.next(); + int sid = fontEncoding.getSID(code); + String charName = fontCharset.getName(sid); + // Predefined encoding + if (charName == null) + { + continue; + } + byte[] bytes = charStringsDict.get(charName); + if (bytes == null) + { + continue; + } + Mapping mapping = new Mapping(); + mapping.setCode(code); + mapping.setSID(sid); + mapping.setName(charName); + mapping.setBytes(bytes); + mappings.add(mapping); + mappedNames.add(charName); + } + // XXX + int code = 256; + for (CFFCharset.Entry entry : fontCharset.getEntries()) + { + String name = entry.getName(); + if (mappedNames.contains(name)) + { + continue; + } + byte[] bytes = this.charStringsDict.get(name); + if (bytes == null) + { + continue; + } + Mapping mapping = new Mapping(); + mapping.setCode(code++); + mapping.setSID(entry.getSID()); + mapping.setName(name); + mapping.setBytes(bytes); + + mappings.add(mapping); + + mappedNames.add(name); + } + return mappings; + } + + /** + * Returns the CFFEncoding of the font. + * @return the encoding + */ + public CFFEncoding getEncoding() + { + return fontEncoding; + } + + /** + * Sets the CFFEncoding of the font. + * @param encoding the given CFFEncoding + */ + public void setEncoding(CFFEncoding encoding) + { + fontEncoding = encoding; + } + + /** + * Returns the CFFCharset of the font. + * @return the charset + */ + public CFFCharset getCharset() + { + return fontCharset; + } + + /** + * Sets the CFFCharset of the font. + * @param charset the given CFFCharset + */ + public void setCharset(CFFCharset charset) + { + fontCharset = charset; + } + + /** + * Returns the character strings dictionary. + * @return the dictionary + */ + public Map getCharStringsDict() + { + return charStringsDict; + } + + /** + * Creates a CharStringConverter for this font. + * @return the new CharStringConverter + */ + public CharStringConverter createConverter() + { + Number defaultWidthX = (Number) getProperty("defaultWidthX"); + Number nominalWidthX = (Number) getProperty("nominalWidthX"); + return new CharStringConverter(defaultWidthX.intValue(), nominalWidthX + .intValue()); + } + + /** + * Creates a CharStringRenderer for this font. + * @return the new CharStringRenderer + */ + public CharStringRenderer createRenderer() + { + return new CharStringRenderer(); + } + + /** + * {@inheritDoc} + */ + public String toString() + { + return getClass().getName() + "[name=" + fontname + ", topDict=" + topDict + + ", privateDict=" + privateDict + ", encoding=" + fontEncoding + + ", charset=" + fontCharset + ", charStringsDict=" + + charStringsDict + "]"; + } + + /** + * This class is used for the font mapping. + * + */ + public class Mapping + { + private int mappedCode; + private int mappedSID; + private String mappedName; + private byte[] mappedBytes; + + /** + * Converts the mapping into a Type1-sequence. + * @return the Type1-sequence + * @throws IOException if an error occurs during reading + */ + public List toType1Sequence() throws IOException + { + CharStringConverter converter = createConverter(); + return converter.convert(toType2Sequence()); + } + + /** + * Converts the mapping into a Type2-sequence. + * @return the Type2-sequence + * @throws IOException if an error occurs during reading + */ + public List toType2Sequence() throws IOException + { + Type2CharStringParser parser = new Type2CharStringParser(); + return parser.parse(getBytes()); + } + + /** + * Gets the value for the code. + * @return the code + */ + public int getCode() + { + return mappedCode; + } + + private void setCode(int code) + { + mappedCode = code; + } + + /** + * Gets the value for the SID. + * @return the SID + */ + public int getSID() + { + return mappedSID; + } + + private void setSID(int sid) + { + this.mappedSID = sid; + } + + /** + * Gets the value for the name. + * @return the name + */ + public String getName() + { + return mappedName; + } + + private void setName(String name) + { + this.mappedName = name; + } + + /** + * Gets the value for the bytes. + * @return the bytes + */ + public byte[] getBytes() + { + return mappedBytes; + } + + private void setBytes(byte[] bytes) + { + this.mappedBytes = bytes; + } + } +} \ No newline at end of file Added: pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFOperator.java URL: http://svn.apache.org/viewvc/pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFOperator.java?rev=907461&view=auto ============================================================================== --- pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFOperator.java (added) +++ pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFOperator.java Sun Feb 7 18:09:19 2010 @@ -0,0 +1,261 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * This class represents a CFF operator. + * @author Villu Russmann + * @version $Revision: 1.0 $ + */ +public class CFFOperator +{ + + private Key operatorKey = null; + private String operatorName = null; + + private CFFOperator(Key key, String name) + { + setKey(key); + setName(name); + } + + /** + * The key of the operator. + * @return the key + */ + public Key getKey() + { + return operatorKey; + } + + private void setKey(Key key) + { + operatorKey = key; + } + + /** + * The name of the operator. + * @return the name + */ + public String getName() + { + return operatorName; + } + + private void setName(String name) + { + operatorName = name; + } + + /** + * {@inheritDoc} + */ + public String toString() + { + return getName(); + } + + /** + * {@inheritDoc} + */ + public int hashCode() + { + return getKey().hashCode(); + } + + /** + * {@inheritDoc} + */ + public boolean equals(Object object) + { + if (object instanceof CFFOperator) + { + CFFOperator that = (CFFOperator) object; + return getKey().equals(that.getKey()); + } + return false; + } + + private static void register(Key key, String name) + { + CFFOperator operator = new CFFOperator(key, name); + keyMap.put(key, operator); + nameMap.put(name, operator); + } + + /** + * Returns the operator corresponding to the given key. + * @param key the given key + * @return the corresponding operator + */ + public static CFFOperator getOperator(Key key) + { + return keyMap.get(key); + } + + /** + * Returns the operator corresponding to the given name. + * @param key the given name + * @return the corresponding operator + */ + public static CFFOperator getOperator(String name) + { + return nameMap.get(name); + } + + /** + * This class is a holder for a key value. It consists of one or two bytes. + * @author Villu Russmann + */ + public static class Key + { + private int[] value = null; + + /** + * Constructor. + * @param b0 the one byte value + */ + public Key(int b0) + { + this(new int[] { b0 }); + } + + /** + * Constructor. + * @param b0 the first byte of a two byte value + * @param b1 the second byte of a two byte value + */ + public Key(int b0, int b1) + { + this(new int[] { b0, b1 }); + } + + private Key(int[] value) + { + setValue(value); + } + + /** + * Returns the value of the key. + * @return the value + */ + public int[] getValue() + { + return value; + } + + private void setValue(int[] value) + { + this.value = value; + } + + /** + * {@inheritDoc} + */ + public String toString() + { + return Arrays.toString(getValue()); + } + + /** + * {@inheritDoc} + */ + public int hashCode() + { + return Arrays.hashCode(getValue()); + } + + /** + * {@inheritDoc} + */ + public boolean equals(Object object) + { + if (object instanceof Key) + { + Key that = (Key) object; + return Arrays.equals(getValue(), that.getValue()); + } + return false; + } + } + + private static Map keyMap = new LinkedHashMap(); + private static Map nameMap = new LinkedHashMap(); + + static + { + // Top DICT + register(new Key(0), "version"); + register(new Key(1), "Notice"); + register(new Key(12, 0), "Copyright"); + register(new Key(2), "FullName"); + register(new Key(3), "FamilyName"); + register(new Key(4), "Weight"); + register(new Key(12, 1), "isFixedPitch"); + register(new Key(12, 2), "ItalicAngle"); + register(new Key(12, 3), "UnderlinePosition"); + register(new Key(12, 4), "UnderlineThickness"); + register(new Key(12, 5), "PaintType"); + register(new Key(12, 6), "CharstringType"); + register(new Key(12, 7), "FontMatrix"); + register(new Key(13), "UniqueID"); + register(new Key(5), "FontBBox"); + register(new Key(12, 8), "StrokeWidth"); + register(new Key(14), "XUID"); + register(new Key(15), "charset"); + register(new Key(16), "Encoding"); + register(new Key(17), "CharStrings"); + register(new Key(18), "Private"); + register(new Key(12, 20), "SyntheticBase"); + register(new Key(12, 21), "PostScript"); + register(new Key(12, 22), "BaseFontName"); + register(new Key(12, 23), "BaseFontBlend"); + register(new Key(12, 30), "ROS"); + register(new Key(12, 31), "CIDFontVersion"); + register(new Key(12, 32), "CIDFontRevision"); + register(new Key(12, 33), "CIDFontType"); + register(new Key(12, 34), "CIDCount"); + register(new Key(12, 35), "UIDBase"); + register(new Key(12, 36), "FDArray"); + register(new Key(12, 37), "FDSelect"); + register(new Key(12, 38), "FontName"); + + // Private DICT + register(new Key(6), "BlueValues"); + register(new Key(7), "OtherBlues"); + register(new Key(8), "FamilyBlues"); + register(new Key(9), "FamilyOtherBlues"); + register(new Key(12, 9), "BlueScale"); + register(new Key(12, 10), "BlueShift"); + register(new Key(12, 11), "BlueFuzz"); + register(new Key(10), "StdHW"); + register(new Key(11), "StdVW"); + register(new Key(12, 12), "StemSnapH"); + register(new Key(12, 13), "StemSnapV"); + register(new Key(12, 14), "ForceBold"); + register(new Key(12, 15), "LanguageGroup"); + register(new Key(12, 16), "ExpansionFactor"); + register(new Key(12, 17), "initialRandomSeed"); + register(new Key(19), "Subrs"); + register(new Key(20), "defaultWidthX"); + register(new Key(21), "nominalWidthX"); + } +} \ No newline at end of file Added: pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFParser.java URL: http://svn.apache.org/viewvc/pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFParser.java?rev=907461&view=auto ============================================================================== --- pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFParser.java (added) +++ pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFParser.java Sun Feb 7 18:09:19 2010 @@ -0,0 +1,847 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.fontbox.cff.charset.CFFCharset; +import org.apache.fontbox.cff.charset.CFFExpertCharset; +import org.apache.fontbox.cff.charset.CFFExpertSubsetCharset; +import org.apache.fontbox.cff.charset.CFFISOAdobeCharset; +import org.apache.fontbox.cff.encoding.CFFEncoding; +import org.apache.fontbox.cff.encoding.CFFExpertEncoding; +import org.apache.fontbox.cff.encoding.CFFStandardEncoding; + +/** + * This class represents a parser for a CFF font. + * @author Villu Russmann + * @version $Revision: 1.0 $ + */ +public class CFFParser +{ + + private CFFDataInput input = null; + private Header header = null; + private IndexData nameIndex = null; + private IndexData topDictIndex = null; + private IndexData stringIndex = null; + private IndexData globalSubrIndex = null; + + /** + * Parsing CFF Font using a byte array as input. + * @param bytes the given byte array + * @return the parsed CFF fonts + * @throws IOException If there is an error reading from the stream + */ + public List parse(byte[] bytes) throws IOException + { + input = new CFFDataInput(bytes); + header = readHeader(input); + nameIndex = readIndexData(input); + topDictIndex = readIndexData(input); + stringIndex = readIndexData(input); + globalSubrIndex = readIndexData(input); + + List fonts = new ArrayList(); + for (int i = 0; i < nameIndex.count; i++) + { + CFFFont font = parseFont(i); + fonts.add(font); + } + return fonts; + } + + private static Header readHeader(CFFDataInput input) throws IOException + { + Header header = new Header(); + header.major = input.readCard8(); + header.minor = input.readCard8(); + header.hdrSize = input.readCard8(); + header.offSize = input.readOffSize(); + return header; + } + + private static IndexData readIndexData(CFFDataInput input) + throws IOException + { + IndexData index = new IndexData(); + index.count = input.readCard16(); + if (index.count == 0) + { + return index; + } + index.offSize = input.readOffSize(); + index.offset = new int[index.count + 1]; + for (int i = 0; i < index.offset.length; i++) + { + index.offset[i] = input.readOffset(index.offSize); + } + index.data = new int[index.offset[index.offset.length - 1] + - index.offset[0]]; + for (int i = 0; i < index.data.length; i++) + { + index.data[i] = input.readCard8(); + } + return index; + } + + private static DictData readDictData(CFFDataInput input) throws IOException + { + DictData dict = new DictData(); + dict.entries = new ArrayList(); + while (input.hasRemaining()) + { + DictData.Entry entry = readEntry(input); + dict.entries.add(entry); + } + return dict; + } + + private static DictData.Entry readEntry(CFFDataInput input) + throws IOException + { + DictData.Entry entry = new DictData.Entry(); + while (true) + { + int b0 = input.readUnsignedByte(); + + if (b0 >= 0 && b0 <= 21) + { + entry.operator = readOperator(input, b0); + break; + } + else if (b0 == 28 || b0 == 29) + { + entry.operands.add(readIntegerNumber(input, b0)); + } + else if (b0 == 30) + { + entry.operands.add(readRealNumber(input, b0)); + } + else if (b0 >= 32 && b0 <= 254) + { + entry.operands.add(readIntegerNumber(input, b0)); + } + else + { + throw new IllegalArgumentException(); + } + } + return entry; + } + + private static CFFOperator readOperator(CFFDataInput input, int b0) + throws IOException + { + CFFOperator.Key key = readOperatorKey(input, b0); + return CFFOperator.getOperator(key); + } + + private static CFFOperator.Key readOperatorKey(CFFDataInput input, int b0) + throws IOException + { + if (b0 == 12) + { + int b1 = input.readUnsignedByte(); + return new CFFOperator.Key(b0, b1); + } + return new CFFOperator.Key(b0); + } + + private static Integer readIntegerNumber(CFFDataInput input, int b0) + throws IOException + { + if (b0 == 28) + { + int b1 = input.readUnsignedByte(); + int b2 = input.readUnsignedByte(); + return Integer.valueOf((short) (b1 << 8 | b2)); + } + else if (b0 == 29) + { + int b1 = input.readUnsignedByte(); + int b2 = input.readUnsignedByte(); + int b3 = input.readUnsignedByte(); + int b4 = input.readUnsignedByte(); + return Integer.valueOf(b1 << 24 | b2 << 16 | b3 << 8 | b4); + } + else if (b0 >= 32 && b0 <= 246) + { + return Integer.valueOf(b0 - 139); + } + else if (b0 >= 247 && b0 <= 250) + { + int b1 = input.readUnsignedByte(); + return Integer.valueOf((b0 - 247) * 256 + b1 + 108); + } + else if (b0 >= 251 && b0 <= 254) + { + int b1 = input.readUnsignedByte(); + return Integer.valueOf(-(b0 - 251) * 256 - b1 - 108); + } + else + { + throw new IllegalArgumentException(); + } + } + + private static Double readRealNumber(CFFDataInput input, int b0) + throws IOException + { + StringBuffer sb = new StringBuffer(); + boolean done = false; + while (!done) + { + int b = input.readUnsignedByte(); + int[] nibbles = { b / 16, b % 16 }; + for (int nibble : nibbles) + { + switch (nibble) + { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + case 0x4: + case 0x5: + case 0x6: + case 0x7: + case 0x8: + case 0x9: + sb.append(nibble); + break; + case 0xa: + sb.append("."); + break; + case 0xb: + sb.append("E"); + break; + case 0xc: + sb.append("E-"); + break; + case 0xd: + break; + case 0xe: + sb.append("-"); + break; + case 0xf: + done = true; + break; + default: + throw new IllegalArgumentException(); + } + } + } + return Double.valueOf(sb.toString()); + } + + private CFFFont parseFont(int index) throws IOException + { + CFFFont font = new CFFFont(); + DataInput nameInput = new DataInput(nameIndex.getBytes(index)); + String name = nameInput.getString(); + font.setName(name); + CFFDataInput topDictInput = new CFFDataInput(topDictIndex + .getBytes(index)); + DictData topDict = readDictData(topDictInput); + DictData.Entry syntheticBaseEntry = topDict.getEntry("SyntheticBase"); + if (syntheticBaseEntry != null) + { + throw new IOException("Synthetic Fonts are not supported"); + } + DictData.Entry rosEntry = topDict.getEntry("ROS"); + if (rosEntry != null) + { + throw new IOException("CID-keyed Fonts are not supported"); + } + font.addValueToTopDict("version", getString(topDict,"version")); + font.addValueToTopDict("Notice", getString(topDict,"Notice")); + font.addValueToTopDict("Copyright", getString(topDict,"Copyright")); + font.addValueToTopDict("FullName", getString(topDict,"FullName")); + font.addValueToTopDict("FamilyName", getString(topDict,"FamilyName")); + font.addValueToTopDict("Weight", getString(topDict,"Weight")); + font.addValueToTopDict("isFixedPitch", getBoolean(topDict, "isFixedPitch", false)); + font.addValueToTopDict("ItalicAngle", getNumber(topDict, "ItalicAngle", 0)); + font.addValueToTopDict("UnderlinePosition", getNumber(topDict, "UnderlinePosition", -100)); + font.addValueToTopDict("UnderlineThickness", getNumber(topDict, "UnderlineThickness", 50)); + font.addValueToTopDict("PaintType", getNumber(topDict, "PaintType", 0)); + font.addValueToTopDict("CharstringType", getNumber(topDict, "CharstringType", 2)); + font.addValueToTopDict("FontMatrix", getArray(topDict, "FontMatrix", Arrays + . asList(Double.valueOf(0.001), Double.valueOf(0), + Double.valueOf(0), Double.valueOf(0.001), Double + .valueOf(0), Double.valueOf(0)))); + font.addValueToTopDict("UniqueID", getNumber(topDict, "UniqueID", null)); + font.addValueToTopDict("FontBBox", getArray(topDict, "FontBBox", Arrays + . asList(Integer.valueOf(0), Integer.valueOf(0), + Integer.valueOf(0), Integer.valueOf(0)))); + font.addValueToTopDict("StrokeWidth", getNumber(topDict, "StrokeWidth", 0)); + font.addValueToTopDict("XUID", getArray(topDict, "XUID", null)); + DictData.Entry charStringsEntry = topDict.getEntry("CharStrings"); + int charStringsOffset = charStringsEntry.getNumber(0).intValue(); + input.setPosition(charStringsOffset); + IndexData charStringsIndex = readIndexData(input); + DictData.Entry charsetEntry = topDict.getEntry("charset"); + CFFCharset charset; + int charsetId = charsetEntry != null ? charsetEntry.getNumber(0) + .intValue() : 0; + if (charsetId == 0) + { + charset = CFFISOAdobeCharset.getInstance(); + } + else if (charsetId == 1) + { + charset = CFFExpertCharset.getInstance(); + } + else if (charsetId == 2) + { + charset = CFFExpertSubsetCharset.getInstance(); + } + else + { + input.setPosition(charsetId); + charset = readCharset(input, charStringsIndex.count); + } + font.setCharset(charset); + font.getCharStringsDict().put(".notdef", charStringsIndex.getBytes(0)); + int[] gids = new int[charStringsIndex.count]; + List glyphEntries = charset.getEntries(); + for (int i = 0; i < glyphEntries.size(); i++) + { + CFFCharset.Entry glyphEntry = glyphEntries.get(i); + gids[i] = glyphEntry.getSID(); + font.getCharStringsDict().put(glyphEntry.getName(), charStringsIndex.getBytes(i + 1)); + } + DictData.Entry encodingEntry = topDict.getEntry("Encoding"); + CFFEncoding encoding; + int encodingId = encodingEntry != null ? encodingEntry.getNumber(0) + .intValue() : 0; + if (encodingId == 0) + { + encoding = CFFStandardEncoding.getInstance(); + } + else if (encodingId == 1) + { + encoding = CFFExpertEncoding.getInstance(); + } + else + { + input.setPosition(encodingId); + encoding = readEncoding(input, gids); + } + font.setEncoding(encoding); + DictData.Entry privateEntry = topDict.getEntry("Private"); + int privateOffset = privateEntry.getNumber(1).intValue(); + input.setPosition(privateOffset); + int privateSize = privateEntry.getNumber(0).intValue(); + CFFDataInput privateDictData = new CFFDataInput(input.readBytes(privateSize)); + DictData privateDict = readDictData(privateDictData); + font.addValueToPrivateDict("BlueValues", getDelta(privateDict, "BlueValues", null)); + font.addValueToPrivateDict("OtherBlues", getDelta(privateDict, "OtherBlues", null)); + font.addValueToPrivateDict("FamilyBlues", getDelta(privateDict, "FamilyBlues", null)); + font.addValueToPrivateDict("FamilyOtherBlues", getDelta(privateDict, "FamilyOtherBlues", null)); + font.addValueToPrivateDict("BlueScale", getNumber(privateDict, "BlueScale", Double.valueOf(0.039625))); + font.addValueToPrivateDict("BlueShift", getNumber(privateDict, "BlueShift", Integer.valueOf(7))); + font.addValueToPrivateDict("BlueFuzz", getNumber(privateDict, "BlueFuzz", Integer.valueOf(1))); + font.addValueToPrivateDict("StdHW", getNumber(privateDict, "StdHW", null)); + font.addValueToPrivateDict("StdVW", getNumber(privateDict, "StdVW", null)); + font.addValueToPrivateDict("StemSnapH", getDelta(privateDict, "StemSnapH", null)); + font.addValueToPrivateDict("StemSnapV", getDelta(privateDict, "StemSnapV", null)); + font.addValueToPrivateDict("ForceBold", getBoolean(privateDict, "ForceBold", false)); + font.addValueToPrivateDict("LanguageGroup", getNumber(privateDict, "LanguageGroup", Integer.valueOf(0))); + font.addValueToPrivateDict("ExpansionFactor", getNumber(privateDict, "ExpansionFactor", Double.valueOf(0.06))); + font.addValueToPrivateDict("initialRandomSeed", getNumber(privateDict, "initialRandomSeed", Integer.valueOf(0))); + font.addValueToPrivateDict("defaultWidthX", getNumber(privateDict, "defaultWidthX", Integer.valueOf(0))); + font.addValueToPrivateDict("nominalWidthX", getNumber(privateDict, "nominalWidthX", Integer.valueOf(0))); + return font; + } + + private String readString(int index) throws IOException + { + if (index >= 0 && index <= 390) + { + return CFFStandardString.getName(index); + } + DataInput dataInput = new DataInput(stringIndex.getBytes(index - 391)); + return dataInput.getString(); + } + + private String getString(DictData dict, String name) throws IOException + { + DictData.Entry entry = dict.getEntry(name); + return (entry != null ? readString(entry.getNumber(0).intValue()) : null); + } + + private Boolean getBoolean(DictData dict, String name, boolean defaultValue) throws IOException + { + DictData.Entry entry = dict.getEntry(name); + return entry != null ? entry.getBoolean(0) : defaultValue; + } + + private Number getNumber(DictData dict, String name, Number defaultValue) throws IOException + { + DictData.Entry entry = dict.getEntry(name); + return entry != null ? entry.getNumber(0) : defaultValue; + } + + // TODO Where is the difference to getDelta?? + private List getArray(DictData dict, String name, List defaultValue) throws IOException + { + DictData.Entry entry = dict.getEntry(name); + return entry != null ? entry.getArray() : defaultValue; + } + + // TODO Where is the difference to getArray?? + private List getDelta(DictData dict, String name, List defaultValue) throws IOException + { + DictData.Entry entry = dict.getEntry(name); + return entry != null ? entry.getArray() : defaultValue; + } + + private CFFEncoding readEncoding(CFFDataInput dataInput, int[] gids) + throws IOException + { + int format = dataInput.readCard8(); + int baseFormat = format & 0x7f; + + if (baseFormat == 0) + { + return readFormat0Encoding(dataInput, format, gids); + } + else if (baseFormat == 1) + { + return readFormat1Encoding(dataInput, format, gids); + } + else + { + throw new IllegalArgumentException(); + } + } + + private Format0Encoding readFormat0Encoding(CFFDataInput dataInput, int format, + int[] gids) throws IOException + { + Format0Encoding encoding = new Format0Encoding(); + encoding.format = format; + encoding.nCodes = dataInput.readCard8(); + encoding.code = new int[encoding.nCodes]; + for (int i = 0; i < encoding.code.length; i++) + { + encoding.code[i] = dataInput.readCard8(); + encoding.register(encoding.code[i], gids[i]); + } + if ((format & 0x80) != 0) + { + readSupplement(dataInput, encoding); + } + return encoding; + } + + private Format1Encoding readFormat1Encoding(CFFDataInput dataInput, int format, + int[] gids) throws IOException + { + Format1Encoding encoding = new Format1Encoding(); + encoding.format = format; + encoding.nRanges = dataInput.readCard8(); + int count = 0; + encoding.range = new Format1Encoding.Range1[encoding.nRanges]; + for (int i = 0; i < encoding.range.length; i++) + { + Format1Encoding.Range1 range = new Format1Encoding.Range1(); + range.first = dataInput.readCard8(); + range.nLeft = dataInput.readCard8(); + encoding.range[i] = range; + for (int j = 0; j < 1 + range.nLeft; j++) + { + encoding.register(range.first + j, gids[count + j]); + } + count += 1 + range.nLeft; + } + if ((format & 0x80) != 0) + { + readSupplement(dataInput, encoding); + } + return encoding; + } + + private void readSupplement(CFFDataInput dataInput, EmbeddedEncoding encoding) + throws IOException + { + encoding.nSups = dataInput.readCard8(); + encoding.supplement = new EmbeddedEncoding.Supplement[encoding.nSups]; + for (int i = 0; i < encoding.supplement.length; i++) + { + EmbeddedEncoding.Supplement supplement = new EmbeddedEncoding.Supplement(); + supplement.code = dataInput.readCard8(); + supplement.glyph = dataInput.readSID(); + encoding.supplement[i] = supplement; + } + } + + private CFFCharset readCharset(CFFDataInput dataInput, int nGlyphs) + throws IOException + { + int format = dataInput.readCard8(); + if (format == 0) + { + return readFormat0Charset(dataInput, format, nGlyphs); + } + else if (format == 1) + { + return readFormat1Charset(dataInput, format, nGlyphs); + } + else + { + throw new IllegalArgumentException(); + } + } + + private Format0Charset readFormat0Charset(CFFDataInput dataInput, int format, + int nGlyphs) throws IOException + { + Format0Charset charset = new Format0Charset(); + charset.format = format; + charset.glyph = new int[nGlyphs - 1]; + for (int i = 0; i < charset.glyph.length; i++) + { + charset.glyph[i] = dataInput.readSID(); + charset.register(charset.glyph[i], readString(charset.glyph[i])); + } + return charset; + } + + private Format1Charset readFormat1Charset(CFFDataInput dataInput, int format, + int nGlyphs) throws IOException + { + Format1Charset charset = new Format1Charset(); + charset.format = format; + charset.range = new Format1Charset.Range1[0]; + for (int i = 0; i < nGlyphs - 1;) + { + Format1Charset.Range1[] newRange = new Format1Charset.Range1[charset.range.length + 1]; + System.arraycopy(charset.range, 0, newRange, 0, + charset.range.length); + charset.range = newRange; + Format1Charset.Range1 range = new Format1Charset.Range1(); + range.first = dataInput.readSID(); + range.nLeft = dataInput.readCard8(); + charset.range[charset.range.length - 1] = range; + for (int j = 0; j < 1 + range.nLeft; j++) + { + charset.register(range.first + j, readString(range.first + j)); + } + i += 1 + range.nLeft; + } + return charset; + } + + /** + * Inner class holding the header of a CFF font. + */ + private static class Header + { + private int major; + private int minor; + private int hdrSize; + private int offSize; + + @Override + public String toString() + { + return getClass().getName() + "[major=" + major + ", minor=" + + minor + ", hdrSize=" + hdrSize + ", offSize=" + offSize + + "]"; + } + } + + /** + * Inner class holding the IndexData of a CFF font. + */ + private static class IndexData + { + private int count; + private int offSize; + private int[] offset; + private int[] data; + + public byte[] getBytes(int index) + { + int length = offset[index + 1] - offset[index]; + byte[] bytes = new byte[length]; + for (int i = 0; i < length; i++) + { + bytes[i] = (byte) data[offset[index] - 1 + i]; + } + return bytes; + } + + @Override + public String toString() + { + return getClass().getName() + "[count=" + count + ", offSize=" + + offSize + ", offset=" + Arrays.toString(offset) + + ", data=" + Arrays.toString(data) + "]"; + } + } + + /** + * Inner class holding the DictData of a CFF font. + */ + private static class DictData + { + + private List entries = null; + + public Entry getEntry(CFFOperator.Key key) + { + return getEntry(CFFOperator.getOperator(key)); + } + + public Entry getEntry(String name) + { + return getEntry(CFFOperator.getOperator(name)); + } + + private Entry getEntry(CFFOperator operator) + { + for (Entry entry : entries) + { + if (entry.operator.equals(operator)) + { + return entry; + } + } + return null; + } + + /** + * {@inheritDoc} + */ + public String toString() + { + return getClass().getName() + "[entries=" + entries + "]"; + } + + /** + * Inner class holding an operand of a CFF font. + */ + private static class Entry + { + private List operands = new ArrayList(); + private CFFOperator operator = null; + + public Number getNumber(int index) + { + return operands.get(index); + } + + public Boolean getBoolean(int index) + { + Number operand = operands.get(index); + if (operand instanceof Integer) + { + switch (operand.intValue()) + { + case 0: + return Boolean.FALSE; + case 1: + return Boolean.TRUE; + default: + break; + } + } + throw new IllegalArgumentException(); + } + + // TODO unused?? + public Integer getSID(int index) + { + Number operand = operands.get(index); + if (operand instanceof Integer) + { + return (Integer) operand; + } + throw new IllegalArgumentException(); + } + + // TODO Where is the difference to getDelta?? + public List getArray() + { + return operands; + } + + // TODO Where is the difference to getArray?? + public List getDelta() + { + return operands; + } + + @Override + public String toString() + { + return getClass().getName() + "[operands=" + operands + + ", operator=" + operator + "]"; + } + } + } + + /** + * Inner class representing an embedded CFF encoding. + */ + private abstract static class EmbeddedEncoding extends CFFEncoding + { + + private int nSups; + private Supplement[] supplement; + + @Override + public boolean isFontSpecific() + { + return true; + } + + /** + * Inner class representing a supplement for an encoding. + */ + private static class Supplement + { + private int code; + private int glyph; + + @Override + public String toString() + { + return getClass().getName() + "[code=" + code + ", glyph=" + + glyph + "]"; + } + } + } + + /** + * Inner class representing a Format0 encoding. + */ + private static class Format0Encoding extends EmbeddedEncoding + { + private int format; + private int nCodes; + private int[] code; + + @Override + public String toString() + { + return getClass().getName() + "[format=" + format + ", nCodes=" + + nCodes + ", code=" + Arrays.toString(code) + + ", supplement=" + Arrays.toString(super.supplement) + "]"; + } + } + + /** + * Inner class representing a Format1 encoding. + */ + private static class Format1Encoding extends EmbeddedEncoding + { + private int format; + private int nRanges; + private Range1[] range; + + @Override + public String toString() + { + return getClass().getName() + "[format=" + format + ", nRanges=" + + nRanges + ", range=" + Arrays.toString(range) + + ", supplement=" + Arrays.toString(super.supplement) + "]"; + } + + /** + * Inner class representing a range of an encoding. + */ + private static class Range1 + { + private int first; + private int nLeft; + + @Override + public String toString() + { + return getClass().getName() + "[first=" + first + ", nLeft=" + + nLeft + "]"; + } + } + } + + /** + * Inner class representing an embedded CFF charset. + */ + private abstract static class EmbeddedCharset extends CFFCharset + { + @Override + public boolean isFontSpecific() + { + return true; + } + } + + /** + * Inner class representing a Format0 charset. + */ + private static class Format0Charset extends EmbeddedCharset + { + private int format; + private int[] glyph; + + @Override + public String toString() + { + return getClass().getName() + "[format=" + format + ", glyph=" + + Arrays.toString(glyph) + "]"; + } + } + + /** + * Inner class representing a Format1 charset. + */ + private static class Format1Charset extends EmbeddedCharset + { + private int format; + private Range1[] range; + + @Override + public String toString() + { + return getClass().getName() + "[format=" + format + ", range=" + + Arrays.toString(range) + "]"; + } + + /** + * Inner class representing a range of a charset. + */ + private static class Range1 + { + private int first; + private int nLeft; + + @Override + public String toString() + { + return getClass().getName() + "[first=" + first + ", nLeft=" + + nLeft + "]"; + } + } + } +} \ No newline at end of file Added: pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFStandardString.java URL: http://svn.apache.org/viewvc/pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFStandardString.java?rev=907461&view=auto ============================================================================== --- pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFStandardString.java (added) +++ pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CFFStandardString.java Sun Feb 7 18:09:19 2010 @@ -0,0 +1,435 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +/** + * This class represents a standard SID to String mapping. + * @author Villu Russmann + * @version $Revision: 1.0 $ + */ +public class CFFStandardString +{ + + private CFFStandardString() + { + } + + /** + * This will return the string mapped to the given SID. + * @param sid the given SID + * @return the mapped string + */ + public static String getName(int sid) + { + return SID2STR[sid]; + } + + private static final String[] SID2STR = + { + ".notdef", + "space", + "exclam", + "quotedbl", + "numbersign", + "dollar", + "percent", + "ampersand", + "quoteright", + "parenleft", + "parenright", + "asterisk", + "plus", + "comma", + "hyphen", + "period", + "slash", + "zero", + "one", + "two", + "three", + "four", + "five", + "six", + "seven", + "eight", + "nine", + "colon", + "semicolon", + "less", + "equal", + "greater", + "question", + "at", + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "bracketleft", + "backslash", + "bracketright", + "asciicircum", + "underscore", + "quoteleft", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "braceleft", + "bar", + "braceright", + "asciitilde", + "exclamdown", + "cent", + "sterling", + "fraction", + "yen", + "florin", + "section", + "currency", + "quotesingle", + "quotedblleft", + "guillemotleft", + "guilsinglleft", + "guilsinglright", + "fi", + "fl", + "endash", + "dagger", + "daggerdbl", + "periodcentered", + "paragraph", + "bullet", + "quotesinglbase", + "quotedblbase", + "quotedblright", + "guillemotright", + "ellipsis", + "perthousand", + "questiondown", + "grave", + "acute", + "circumflex", + "tilde", + "macron", + "breve", + "dotaccent", + "dieresis", + "ring", + "cedilla", + "hungarumlaut", + "ogonek", + "caron", + "emdash", + "AE", + "ordfeminine", + "Lslash", + "Oslash", + "OE", + "ordmasculine", + "ae", + "dotlessi", + "lslash", + "oslash", + "oe", + "germandbls", + "onesuperior", + "logicalnot", + "mu", + "trademark", + "Eth", + "onehalf", + "plusminus", + "Thorn", + "onequarter", + "divide", + "brokenbar", + "degree", + "thorn", + "threequarters", + "twosuperior", + "registered", + "minus", + "eth", + "multiply", + "threesuperior", + "copyright", + "Aacute", + "Acircumflex", + "Adieresis", + "Agrave", + "Aring", + "Atilde", + "Ccedilla", + "Eacute", + "Ecircumflex", + "Edieresis", + "Egrave", + "Iacute", + "Icircumflex", + "Idieresis", + "Igrave", + "Ntilde", + "Oacute", + "Ocircumflex", + "Odieresis", + "Ograve", + "Otilde", + "Scaron", + "Uacute", + "Ucircumflex", + "Udieresis", + "Ugrave", + "Yacute", + "Ydieresis", + "Zcaron", + "aacute", + "acircumflex", + "adieresis", + "agrave", + "aring", + "atilde", + "ccedilla", + "eacute", + "ecircumflex", + "edieresis", + "egrave", + "iacute", + "icircumflex", + "idieresis", + "igrave", + "ntilde", + "oacute", + "ocircumflex", + "odieresis", + "ograve", + "otilde", + "scaron", + "uacute", + "ucircumflex", + "udieresis", + "ugrave", + "yacute", + "ydieresis", + "zcaron", + "exclamsmall", + "Hungarumlautsmall", + "dollaroldstyle", + "dollarsuperior", + "ampersandsmall", + "Acutesmall", + "parenleftsuperior", + "parenrightsuperior", + "twodotenleader", + "onedotenleader", + "zerooldstyle", + "oneoldstyle", + "twooldstyle", + "threeoldstyle", + "fouroldstyle", + "fiveoldstyle", + "sixoldstyle", + "sevenoldstyle", + "eightoldstyle", + "nineoldstyle", + "commasuperior", + "threequartersemdash", + "periodsuperior", + "questionsmall", + "asuperior", + "bsuperior", + "centsuperior", + "dsuperior", + "esuperior", + "isuperior", + "lsuperior", + "msuperior", + "nsuperior", + "osuperior", + "rsuperior", + "ssuperior", + "tsuperior", + "ff", + "ffi", + "ffl", + "parenleftinferior", + "parenrightinferior", + "Circumflexsmall", + "hyphensuperior", + "Gravesmall", + "Asmall", + "Bsmall", + "Csmall", + "Dsmall", + "Esmall", + "Fsmall", + "Gsmall", + "Hsmall", + "Ismall", + "Jsmall", + "Ksmall", + "Lsmall", + "Msmall", + "Nsmall", + "Osmall", + "Psmall", + "Qsmall", + "Rsmall", + "Ssmall", + "Tsmall", + "Usmall", + "Vsmall", + "Wsmall", + "Xsmall", + "Ysmall", + "Zsmall", + "colonmonetary", + "onefitted", + "rupiah", + "Tildesmall", + "exclamdownsmall", + "centoldstyle", + "Lslashsmall", + "Scaronsmall", + "Zcaronsmall", + "Dieresissmall", + "Brevesmall", + "Caronsmall", + "Dotaccentsmall", + "Macronsmall", + "figuredash", + "hypheninferior", + "Ogoneksmall", + "Ringsmall", + "Cedillasmall", + "questiondownsmall", + "oneeighth", + "threeeighths", + "fiveeighths", + "seveneighths", + "onethird", + "twothirds", + "zerosuperior", + "foursuperior", + "fivesuperior", + "sixsuperior", + "sevensuperior", + "eightsuperior", + "ninesuperior", + "zeroinferior", + "oneinferior", + "twoinferior", + "threeinferior", + "fourinferior", + "fiveinferior", + "sixinferior", + "seveninferior", + "eightinferior", + "nineinferior", + "centinferior", + "dollarinferior", + "periodinferior", + "commainferior", + "Agravesmall", + "Aacutesmall", + "Acircumflexsmall", + "Atildesmall", + "Adieresissmall", + "Aringsmall", + "AEsmall", + "Ccedillasmall", + "Egravesmall", + "Eacutesmall", + "Ecircumflexsmall", + "Edieresissmall", + "Igravesmall", + "Iacutesmall", + "Icircumflexsmall", + "Idieresissmall", + "Ethsmall", + "Ntildesmall", + "Ogravesmall", + "Oacutesmall", + "Ocircumflexsmall", + "Otildesmall", + "Odieresissmall", + "OEsmall", + "Oslashsmall", + "Ugravesmall", + "Uacutesmall", + "Ucircumflexsmall", + "Udieresissmall", + "Yacutesmall", + "Thornsmall", + "Ydieresissmall", + "001.000", + "001.001", + "001.002", + "001.003", + "Black", + "Bold", + "Book", + "Light", + "Medium", + "Regular", + "Roman", + "Semibold", + }; +} \ No newline at end of file Added: pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CharStringCommand.java URL: http://svn.apache.org/viewvc/pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CharStringCommand.java?rev=907461&view=auto ============================================================================== --- pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CharStringCommand.java (added) +++ pdfbox/fontbox/trunk/src/main/java/org/apache/fontbox/cff/CharStringCommand.java Sun Feb 7 18:09:19 2010 @@ -0,0 +1,307 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.fontbox.cff; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * This class represents a CharStringCommand. + * + * @author Villu Russmann + * @version $Revision$ + */ +public class CharStringCommand +{ + + private Key commandKey = null; + + /** + * Constructor with one value. + * + * @param b0 value + */ + public CharStringCommand(int b0) + { + setKey(new Key(b0)); + } + + /** + * Constructor with two values. + * + * @param b0 value1 + * @param b1 value2 + */ + public CharStringCommand(int b0, int b1) + { + setKey(new Key(b0, b1)); + } + + /** + * Constructor with an array as values. + * + * @param values array of values + */ + public CharStringCommand(int[] values) + { + setKey(new Key(values)); + } + + /** + * The key of the CharStringCommand. + * @return the key + */ + public Key getKey() + { + return commandKey; + } + + private void setKey(Key key) + { + commandKey = key; + } + + /** + * {@inheritDoc} + */ + public String toString() + { + return getKey().toString(); + } + + /** + * {@inheritDoc} + */ + public int hashCode() + { + return getKey().hashCode(); + } + + /** + * {@inheritDoc} + */ + public boolean equals(Object object) + { + if (object instanceof CharStringCommand) + { + CharStringCommand that = (CharStringCommand) object; + return getKey().equals(that.getKey()); + } + return false; + } + + /** + * A static class to hold one or more int values as key. + */ + public static class Key + { + + private int[] keyValues = null; + + /** + * Constructor with one value. + * + * @param b0 value + */ + public Key(int b0) + { + setValue(new int[] { b0 }); + } + + /** + * Constructor with two values. + * + * @param b0 value1 + * @param b1 value2 + */ + public Key(int b0, int b1) + { + setValue(new int[] { b0, b1 }); + } + + /** + * Constructor with an array as values. + * + * @param values array of values + */ + public Key(int[] values) + { + setValue(values); + } + + /** + * Array the with the values. + * + * @return array with the values + */ + public int[] getValue() + { + return keyValues; + } + + private void setValue(int[] value) + { + keyValues = value; + } + + /** + * {@inheritDoc} + */ + public String toString() + { + return Arrays.toString(getValue()); + } + + /** + * {@inheritDoc} + */ + public int hashCode() + { + if (keyValues[0] == 12) + { + if (keyValues.length > 1) + { + return keyValues[0] ^ keyValues[1]; + } + } + return keyValues[0]; + } + + /** + * {@inheritDoc} + */ + public boolean equals(Object object) + { + if (object instanceof Key) + { + Key that = (Key) object; + if (keyValues[0] == 12 && that.keyValues[0] == 12) + { + if (keyValues.length > 1 && that.keyValues.length > 1) + { + return keyValues[1] == that.keyValues[1]; + } + + return keyValues.length == that.keyValues.length; + } + return keyValues[0] == that.keyValues[0]; + } + return false; + } + } + + /** + * A map with the Type1 vocabulary. + */ + public static final Map TYPE1_VOCABULARY; + + static + { + Map map = new LinkedHashMap(); + map.put(new Key(1), "hstem"); + map.put(new Key(3), "vstem"); + map.put(new Key(4), "vmoveto"); + map.put(new Key(5), "rlineto"); + map.put(new Key(6), "hlineto"); + map.put(new Key(7), "vlineto"); + map.put(new Key(8), "rrcurveto"); + map.put(new Key(9), "closepath"); + map.put(new Key(10), "callsubr"); + map.put(new Key(11), "return"); + map.put(new Key(12), "escape"); + map.put(new Key(12, 0), "dotsection"); + map.put(new Key(12, 1), "vstem3"); + map.put(new Key(12, 2), "hstem3"); + map.put(new Key(12, 6), "seac"); + map.put(new Key(12, 7), "sbw"); + map.put(new Key(12, 12), "div"); + map.put(new Key(12, 16), "callothersubr"); + map.put(new Key(12, 17), "pop"); + map.put(new Key(12, 33), "setcurrentpoint"); + map.put(new Key(13), "hsbw"); + map.put(new Key(14), "endchar"); + map.put(new Key(21), "rmoveto"); + map.put(new Key(22), "hmoveto"); + map.put(new Key(30), "vhcurveto"); + map.put(new Key(31), "hvcurveto"); + + TYPE1_VOCABULARY = Collections.unmodifiableMap(map); + } + + /** + * A map with the Type2 vocabulary. + */ + public static final Map TYPE2_VOCABULARY; + + static + { + Map map = new LinkedHashMap(); + map.put(new Key(1), "hstem"); + map.put(new Key(3), "vstem"); + map.put(new Key(4), "vmoveto"); + map.put(new Key(5), "rlineto"); + map.put(new Key(6), "hlineto"); + map.put(new Key(7), "vlineto"); + map.put(new Key(8), "rrcurveto"); + map.put(new Key(10), "callsubr"); + map.put(new Key(11), "return"); + map.put(new Key(12), "escape"); + map.put(new Key(12, 3), "and"); + map.put(new Key(12, 4), "or"); + map.put(new Key(12, 5), "not"); + map.put(new Key(12, 9), "abs"); + map.put(new Key(12, 10), "add"); + map.put(new Key(12, 11), "sub"); + map.put(new Key(12, 12), "div"); + map.put(new Key(12, 14), "neg"); + map.put(new Key(12, 15), "eq"); + map.put(new Key(12, 18), "drop"); + map.put(new Key(12, 20), "put"); + map.put(new Key(12, 21), "get"); + map.put(new Key(12, 22), "ifelse"); + map.put(new Key(12, 23), "random"); + map.put(new Key(12, 24), "mul"); + map.put(new Key(12, 26), "sqrt"); + map.put(new Key(12, 27), "dup"); + map.put(new Key(12, 28), "exch"); + map.put(new Key(12, 29), "index"); + map.put(new Key(12, 30), "roll"); + map.put(new Key(12, 34), "hflex"); + map.put(new Key(12, 35), "flex"); + map.put(new Key(12, 36), "hflex1"); + map.put(new Key(12, 37), "flex1"); + map.put(new Key(14), "endchar"); + map.put(new Key(18), "hstemhm"); + map.put(new Key(19), "hintmask"); + map.put(new Key(20), "cntrmask"); + map.put(new Key(21), "rmoveto"); + map.put(new Key(22), "hmoveto"); + map.put(new Key(23), "vstemhm"); + map.put(new Key(24), "rcurveline"); + map.put(new Key(25), "rlinecurve"); + map.put(new Key(26), "vvcurveto"); + map.put(new Key(27), "hhcurveto"); + map.put(new Key(28), "shortint"); + map.put(new Key(29), "callgsubr"); + map.put(new Key(30), "vhcurveto"); + map.put(new Key(31), "hvcurveto"); + + TYPE2_VOCABULARY = Collections.unmodifiableMap(map); + } +} \ No newline at end of file