Source Code Cross Referenced for TTFFile.java in  » Graphic-Library » fop » org » apache » fop » fonts » truetype » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Graphic Library » fop » org.apache.fop.fonts.truetype 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Licensed to the Apache Software Foundation (ASF) under one or more
0003:         * contributor license agreements.  See the NOTICE file distributed with
0004:         * this work for additional information regarding copyright ownership.
0005:         * The ASF licenses this file to You under the Apache License, Version 2.0
0006:         * (the "License"); you may not use this file except in compliance with
0007:         * the License.  You may obtain a copy of the License at
0008:         * 
0009:         *      http://www.apache.org/licenses/LICENSE-2.0
0010:         * 
0011:         * Unless required by applicable law or agreed to in writing, software
0012:         * distributed under the License is distributed on an "AS IS" BASIS,
0013:         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014:         * See the License for the specific language governing permissions and
0015:         * limitations under the License.
0016:         */
0017:
0018:        /* $Id: TTFFile.java 563951 2007-08-08 17:37:33Z vhennebert $ */
0019:
0020:        package org.apache.fop.fonts.truetype;
0021:
0022:        import java.io.IOException;
0023:        import java.util.Iterator;
0024:        import java.util.Map;
0025:        import java.util.List;
0026:
0027:        import org.apache.commons.logging.Log;
0028:        import org.apache.commons.logging.LogFactory;
0029:        import org.apache.fop.fonts.Glyphs;
0030:
0031:        /**
0032:         * Reads a TrueType file or a TrueType Collection.
0033:         * The TrueType spec can be found at the Microsoft.
0034:         * Typography site: http://www.microsoft.com/truetype/
0035:         */
0036:        public class TTFFile {
0037:
0038:            static final byte NTABS = 24;
0039:            static final int NMACGLYPHS = 258;
0040:            static final int MAX_CHAR_CODE = 255;
0041:            static final int ENC_BUF_SIZE = 1024;
0042:
0043:            /** Set to true to get even more debug output than with level DEBUG */
0044:            public static final boolean TRACE_ENABLED = false;
0045:
0046:            private String encoding = "WinAnsiEncoding"; // Default encoding
0047:
0048:            private short firstChar = 0;
0049:            private boolean isEmbeddable = true;
0050:            private boolean hasSerifs = true;
0051:            /**
0052:             * Table directory
0053:             */
0054:            protected Map dirTabs;
0055:            private Map kerningTab; // for CIDs
0056:            private Map ansiKerningTab; // For winAnsiEncoding
0057:            private List cmaps;
0058:            private List unicodeMapping;
0059:
0060:            private int upem; // unitsPerEm from "head" table
0061:            private int nhmtx; // Number of horizontal metrics
0062:            private int postFormat;
0063:            private int locaFormat;
0064:            /**
0065:             * Offset to last loca
0066:             */
0067:            protected long lastLoca = 0;
0068:            private int numberOfGlyphs; // Number of glyphs in font (read from "maxp" table)
0069:            private int nmGlyphs; // Used in fixWidths - remove?
0070:
0071:            /**
0072:             * Contains glyph data
0073:             */
0074:            protected TTFMtxEntry[] mtxTab; // Contains glyph data
0075:            private int[] mtxEncoded = null;
0076:
0077:            private String fontName = "";
0078:            private String fullName = "";
0079:            private String notice = "";
0080:            private String familyName = "";
0081:            private String subFamilyName = "";
0082:
0083:            private long italicAngle = 0;
0084:            private long isFixedPitch = 0;
0085:            private int fontBBox1 = 0;
0086:            private int fontBBox2 = 0;
0087:            private int fontBBox3 = 0;
0088:            private int fontBBox4 = 0;
0089:            private int capHeight = 0;
0090:            private int os2CapHeight = 0;
0091:            private int underlinePosition = 0;
0092:            private int underlineThickness = 0;
0093:            private int xHeight = 0;
0094:            private int os2xHeight = 0;
0095:            //Effective ascender/descender
0096:            private int ascender = 0;
0097:            private int descender = 0;
0098:            //Ascender/descender from hhea table
0099:            private int hheaAscender = 0;
0100:            private int hheaDescender = 0;
0101:            //Ascender/descender from OS/2 table
0102:            private int os2Ascender = 0;
0103:            private int os2Descender = 0;
0104:
0105:            private short lastChar = 0;
0106:
0107:            private int[] ansiWidth;
0108:            private Map ansiIndex;
0109:
0110:            // internal mapping of glyph indexes to unicode indexes
0111:            // used for quick mappings in this class
0112:            private Map glyphToUnicodeMap = new java.util.HashMap();
0113:            private Map unicodeToGlyphMap = new java.util.HashMap();
0114:
0115:            private TTFDirTabEntry currentDirTab;
0116:
0117:            private boolean isCFF;
0118:
0119:            /**
0120:             * logging instance
0121:             */
0122:            protected Log log = LogFactory.getLog(TTFFile.class);
0123:
0124:            /**
0125:             * Key-value helper class
0126:             */
0127:            class UnicodeMapping {
0128:
0129:                private int unicodeIndex;
0130:                private int glyphIndex;
0131:
0132:                UnicodeMapping(int glyphIndex, int unicodeIndex) {
0133:                    this .unicodeIndex = unicodeIndex;
0134:                    this .glyphIndex = glyphIndex;
0135:                    glyphToUnicodeMap.put(new Integer(glyphIndex), new Integer(
0136:                            unicodeIndex));
0137:                    unicodeToGlyphMap.put(new Integer(unicodeIndex),
0138:                            new Integer(glyphIndex));
0139:                }
0140:
0141:                /**
0142:                 * Returns the glyphIndex.
0143:                 * @return the glyph index
0144:                 */
0145:                public int getGlyphIndex() {
0146:                    return glyphIndex;
0147:                }
0148:
0149:                /**
0150:                 * Returns the unicodeIndex.
0151:                 * @return the Unicode index
0152:                 */
0153:                public int getUnicodeIndex() {
0154:                    return unicodeIndex;
0155:                }
0156:            }
0157:
0158:            /**
0159:             * Position inputstream to position indicated
0160:             * in the dirtab offset + offset
0161:             */
0162:            boolean seekTab(FontFileReader in, String name, long offset)
0163:                    throws IOException {
0164:                TTFDirTabEntry dt = (TTFDirTabEntry) dirTabs.get(name);
0165:                if (dt == null) {
0166:                    log.error("Dirtab " + name + " not found.");
0167:                    return false;
0168:                } else {
0169:                    in.seekSet(dt.getOffset() + offset);
0170:                    this .currentDirTab = dt;
0171:                }
0172:                return true;
0173:            }
0174:
0175:            /**
0176:             * Convert from truetype unit to pdf unit based on the
0177:             * unitsPerEm field in the "head" table
0178:             * @param n truetype unit
0179:             * @return pdf unit
0180:             */
0181:            public int convertTTFUnit2PDFUnit(int n) {
0182:                int ret;
0183:                if (n < 0) {
0184:                    long rest1 = n % upem;
0185:                    long storrest = 1000 * rest1;
0186:                    long ledd2 = (storrest != 0 ? rest1 / storrest : 0);
0187:                    ret = -((-1000 * n) / upem - (int) ledd2);
0188:                } else {
0189:                    ret = (n / upem) * 1000 + ((n % upem) * 1000) / upem;
0190:                }
0191:
0192:                return ret;
0193:            }
0194:
0195:            /**
0196:             * Read the cmap table,
0197:             * return false if the table is not present or only unsupported
0198:             * tables are present. Currently only unicode cmaps are supported.
0199:             * Set the unicodeIndex in the TTFMtxEntries and fills in the
0200:             * cmaps vector.
0201:             */
0202:            private boolean readCMAP(FontFileReader in) throws IOException {
0203:
0204:                unicodeMapping = new java.util.ArrayList();
0205:
0206:                //Read CMAP table and correct mtxTab.index
0207:                int mtxPtr = 0;
0208:
0209:                seekTab(in, "cmap", 2);
0210:                int numCMap = in.readTTFUShort(); // Number of cmap subtables
0211:                long cmapUniOffset = 0;
0212:
0213:                if (log.isDebugEnabled()) {
0214:                    log.debug(numCMap + " cmap tables");
0215:                }
0216:
0217:                //Read offset for all tables. We are only interested in the unicode table
0218:                for (int i = 0; i < numCMap; i++) {
0219:                    int cmapPID = in.readTTFUShort();
0220:                    int cmapEID = in.readTTFUShort();
0221:                    long cmapOffset = in.readTTFULong();
0222:
0223:                    log.debug("Platform ID: " + cmapPID + " Encoding: "
0224:                            + cmapEID);
0225:
0226:                    if (cmapPID == 3 && cmapEID == 1) {
0227:                        cmapUniOffset = cmapOffset;
0228:                    }
0229:                }
0230:
0231:                if (cmapUniOffset <= 0) {
0232:                    log.fatal("Unicode cmap table not present");
0233:                    log.fatal("Unsupported format: Aborting");
0234:                    return false;
0235:                }
0236:
0237:                // Read unicode cmap
0238:                seekTab(in, "cmap", cmapUniOffset);
0239:                int cmapFormat = in.readTTFUShort();
0240:                /*int cmap_length =*/in.readTTFUShort(); //skip cmap length
0241:
0242:                if (log.isDebugEnabled()) {
0243:                    log.debug("CMAP format: " + cmapFormat);
0244:                }
0245:
0246:                if (cmapFormat == 4) {
0247:                    in.skip(2); // Skip version number
0248:                    int cmapSegCountX2 = in.readTTFUShort();
0249:                    int cmapSearchRange = in.readTTFUShort();
0250:                    int cmapEntrySelector = in.readTTFUShort();
0251:                    int cmapRangeShift = in.readTTFUShort();
0252:
0253:                    if (log.isDebugEnabled()) {
0254:                        log.debug("segCountX2   : " + cmapSegCountX2);
0255:                        log.debug("searchRange  : " + cmapSearchRange);
0256:                        log.debug("entrySelector: " + cmapEntrySelector);
0257:                        log.debug("rangeShift   : " + cmapRangeShift);
0258:                    }
0259:
0260:                    int[] cmapEndCounts = new int[cmapSegCountX2 / 2];
0261:                    int[] cmapStartCounts = new int[cmapSegCountX2 / 2];
0262:                    int[] cmapDeltas = new int[cmapSegCountX2 / 2];
0263:                    int[] cmapRangeOffsets = new int[cmapSegCountX2 / 2];
0264:
0265:                    for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
0266:                        cmapEndCounts[i] = in.readTTFUShort();
0267:                    }
0268:
0269:                    in.skip(2); // Skip reservedPad
0270:
0271:                    for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
0272:                        cmapStartCounts[i] = in.readTTFUShort();
0273:                    }
0274:
0275:                    for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
0276:                        cmapDeltas[i] = in.readTTFShort();
0277:                    }
0278:
0279:                    //int startRangeOffset = in.getCurrentPos();
0280:
0281:                    for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
0282:                        cmapRangeOffsets[i] = in.readTTFUShort();
0283:                    }
0284:
0285:                    int glyphIdArrayOffset = in.getCurrentPos();
0286:
0287:                    // Insert the unicode id for the glyphs in mtxTab
0288:                    // and fill in the cmaps ArrayList
0289:
0290:                    for (int i = 0; i < cmapStartCounts.length; i++) {
0291:
0292:                        if (log.isTraceEnabled()) {
0293:                            log.trace(i + ": " + cmapStartCounts[i] + " - "
0294:                                    + cmapEndCounts[i]);
0295:                        }
0296:
0297:                        for (int j = cmapStartCounts[i]; j <= cmapEndCounts[i]; j++) {
0298:
0299:                            // Update lastChar
0300:                            if (j < 256 && j > lastChar) {
0301:                                lastChar = (short) j;
0302:                            }
0303:
0304:                            if (mtxPtr < mtxTab.length) {
0305:                                int glyphIdx;
0306:                                // the last character 65535 = .notdef
0307:                                // may have a range offset
0308:                                if (cmapRangeOffsets[i] != 0 && j != 65535) {
0309:                                    int glyphOffset = glyphIdArrayOffset
0310:                                            + ((cmapRangeOffsets[i] / 2)
0311:                                                    + (j - cmapStartCounts[i])
0312:                                                    + (i) - cmapSegCountX2 / 2)
0313:                                            * 2;
0314:                                    in.seekSet(glyphOffset);
0315:                                    glyphIdx = (in.readTTFUShort() + cmapDeltas[i]) & 0xffff;
0316:
0317:                                    unicodeMapping.add(new UnicodeMapping(
0318:                                            glyphIdx, j));
0319:                                    mtxTab[glyphIdx].getUnicodeIndex().add(
0320:                                            new Integer(j));
0321:
0322:                                    // Also add winAnsiWidth
0323:                                    List v = (List) ansiIndex
0324:                                            .get(new Integer(j));
0325:                                    if (v != null) {
0326:                                        Iterator e = v.listIterator();
0327:                                        while (e.hasNext()) {
0328:                                            Integer aIdx = (Integer) e.next();
0329:                                            ansiWidth[aIdx.intValue()] = mtxTab[glyphIdx]
0330:                                                    .getWx();
0331:
0332:                                            if (log.isTraceEnabled()) {
0333:                                                log.trace("Added width "
0334:                                                        + mtxTab[glyphIdx]
0335:                                                                .getWx()
0336:                                                        + " uni: " + j
0337:                                                        + " ansi: "
0338:                                                        + aIdx.intValue());
0339:                                            }
0340:                                        }
0341:                                    }
0342:
0343:                                    if (log.isTraceEnabled()) {
0344:                                        log.trace("Idx: " + glyphIdx
0345:                                                + " Delta: " + cmapDeltas[i]
0346:                                                + " Unicode: " + j + " name: "
0347:                                                + mtxTab[glyphIdx].getName());
0348:                                    }
0349:                                } else {
0350:                                    glyphIdx = (j + cmapDeltas[i]) & 0xffff;
0351:
0352:                                    if (glyphIdx < mtxTab.length) {
0353:                                        mtxTab[glyphIdx].getUnicodeIndex().add(
0354:                                                new Integer(j));
0355:                                    } else {
0356:                                        log.debug("Glyph " + glyphIdx
0357:                                                + " out of range: "
0358:                                                + mtxTab.length);
0359:                                    }
0360:
0361:                                    unicodeMapping.add(new UnicodeMapping(
0362:                                            glyphIdx, j));
0363:                                    if (glyphIdx < mtxTab.length) {
0364:                                        mtxTab[glyphIdx].getUnicodeIndex().add(
0365:                                                new Integer(j));
0366:                                    } else {
0367:                                        log.debug("Glyph " + glyphIdx
0368:                                                + " out of range: "
0369:                                                + mtxTab.length);
0370:                                    }
0371:
0372:                                    // Also add winAnsiWidth
0373:                                    List v = (List) ansiIndex
0374:                                            .get(new Integer(j));
0375:                                    if (v != null) {
0376:                                        Iterator e = v.listIterator();
0377:                                        while (e.hasNext()) {
0378:                                            Integer aIdx = (Integer) e.next();
0379:                                            ansiWidth[aIdx.intValue()] = mtxTab[glyphIdx]
0380:                                                    .getWx();
0381:                                        }
0382:                                    }
0383:
0384:                                    //getLogger().debug("IIdx: " +
0385:                                    //    mtxPtr +
0386:                                    //    " Delta: " + cmap_deltas[i] +
0387:                                    //    " Unicode: " + j +
0388:                                    //    " name: " +
0389:                                    //    mtxTab[(j+cmap_deltas[i]) & 0xffff].name);
0390:
0391:                                }
0392:                                if (glyphIdx < mtxTab.length) {
0393:                                    if (mtxTab[glyphIdx].getUnicodeIndex()
0394:                                            .size() < 2) {
0395:                                        mtxPtr++;
0396:                                    }
0397:                                }
0398:                            }
0399:                        }
0400:                    }
0401:                }
0402:                return true;
0403:            }
0404:
0405:            /**
0406:             * Print first char/last char
0407:             */
0408:            private void printMaxMin() {
0409:                int min = 255;
0410:                int max = 0;
0411:                for (int i = 0; i < mtxTab.length; i++) {
0412:                    if (mtxTab[i].getIndex() < min) {
0413:                        min = mtxTab[i].getIndex();
0414:                    }
0415:                    if (mtxTab[i].getIndex() > max) {
0416:                        max = mtxTab[i].getIndex();
0417:                    }
0418:                }
0419:                log.info("Min: " + min);
0420:                log.info("Max: " + max);
0421:            }
0422:
0423:            /**
0424:             * Reads the font using a FontFileReader.
0425:             *
0426:             * @param in The FontFileReader to use
0427:             * @throws IOException In case of an I/O problem
0428:             */
0429:            public void readFont(FontFileReader in) throws IOException {
0430:                readFont(in, (String) null);
0431:            }
0432:
0433:            /**
0434:             * initialize the ansiWidths array (for winAnsiEncoding)
0435:             * and fill with the missingwidth
0436:             */
0437:            private void initAnsiWidths() {
0438:                ansiWidth = new int[256];
0439:                for (int i = 0; i < 256; i++) {
0440:                    ansiWidth[i] = mtxTab[0].getWx();
0441:                }
0442:
0443:                // Create an index hash to the ansiWidth
0444:                // Can't just index the winAnsiEncoding when inserting widths
0445:                // same char (eg bullet) is repeated more than one place
0446:                ansiIndex = new java.util.HashMap();
0447:                for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) {
0448:                    Integer ansi = new Integer(i);
0449:                    Integer uni = new Integer((int) Glyphs.WINANSI_ENCODING[i]);
0450:
0451:                    List v = (List) ansiIndex.get(uni);
0452:                    if (v == null) {
0453:                        v = new java.util.ArrayList();
0454:                        ansiIndex.put(uni, v);
0455:                    }
0456:                    v.add(ansi);
0457:                }
0458:            }
0459:
0460:            /**
0461:             * Read the font data.
0462:             * If the fontfile is a TrueType Collection (.ttc file)
0463:             * the name of the font to read data for must be supplied,
0464:             * else the name is ignored.
0465:             *
0466:             * @param in The FontFileReader to use
0467:             * @param name The name of the font
0468:             * @return boolean Returns true if the font is valid
0469:             * @throws IOException In case of an I/O problem
0470:             */
0471:            public boolean readFont(FontFileReader in, String name)
0472:                    throws IOException {
0473:
0474:                /*
0475:                 * Check if TrueType collection, and that the name
0476:                 * exists in the collection
0477:                 */
0478:                if (!checkTTC(in, name)) {
0479:                    if (name == null) {
0480:                        throw new IllegalArgumentException(
0481:                                "For TrueType collection you must specify which font "
0482:                                        + "to select (-ttcname)");
0483:                    } else {
0484:                        throw new IOException(
0485:                                "Name does not exist in the TrueType collection: "
0486:                                        + name);
0487:                    }
0488:                }
0489:
0490:                readDirTabs(in);
0491:                readFontHeader(in);
0492:                getNumGlyphs(in);
0493:                if (log.isDebugEnabled()) {
0494:                    log.debug("Number of glyphs in font: " + numberOfGlyphs);
0495:                }
0496:                readHorizontalHeader(in);
0497:                readHorizontalMetrics(in);
0498:                initAnsiWidths();
0499:                readPostScript(in);
0500:                readOS2(in);
0501:                determineAscDesc();
0502:                if (!isCFF) {
0503:                    readIndexToLocation(in);
0504:                    readGlyf(in);
0505:                }
0506:                readName(in);
0507:                boolean pcltFound = readPCLT(in);
0508:                // Read cmap table and fill in ansiwidths
0509:                boolean valid = readCMAP(in);
0510:                if (!valid) {
0511:                    return false;
0512:                }
0513:                // Create cmaps for bfentries
0514:                createCMaps();
0515:                // print_max_min();
0516:
0517:                readKerning(in);
0518:                guessVerticalMetricsFromGlyphBBox();
0519:                return true;
0520:            }
0521:
0522:            private void createCMaps() {
0523:                cmaps = new java.util.ArrayList();
0524:                TTFCmapEntry tce = new TTFCmapEntry();
0525:
0526:                Iterator e = unicodeMapping.listIterator();
0527:                UnicodeMapping um = (UnicodeMapping) e.next();
0528:                UnicodeMapping lastMapping = um;
0529:
0530:                tce.setUnicodeStart(um.getUnicodeIndex());
0531:                tce.setGlyphStartIndex(um.getGlyphIndex());
0532:
0533:                while (e.hasNext()) {
0534:                    um = (UnicodeMapping) e.next();
0535:                    if (((lastMapping.getUnicodeIndex() + 1) != um
0536:                            .getUnicodeIndex())
0537:                            || ((lastMapping.getGlyphIndex() + 1) != um
0538:                                    .getGlyphIndex())) {
0539:                        tce.setUnicodeEnd(lastMapping.getUnicodeIndex());
0540:                        cmaps.add(tce);
0541:
0542:                        tce = new TTFCmapEntry();
0543:                        tce.setUnicodeStart(um.getUnicodeIndex());
0544:                        tce.setGlyphStartIndex(um.getGlyphIndex());
0545:                    }
0546:                    lastMapping = um;
0547:                }
0548:
0549:                tce.setUnicodeEnd(um.getUnicodeIndex());
0550:                cmaps.add(tce);
0551:            }
0552:
0553:            /**
0554:             * Returns the Windows name of the font.
0555:             * @return String The Windows name
0556:             */
0557:            public String getWindowsName() {
0558:                return familyName + "," + subFamilyName;
0559:            }
0560:
0561:            /**
0562:             * Returns the PostScript name of the font.
0563:             * @return String The PostScript name
0564:             */
0565:            public String getPostScriptName() {
0566:                if ("Regular".equals(subFamilyName)
0567:                        || "Roman".equals(subFamilyName)) {
0568:                    return familyName;
0569:                } else {
0570:                    return familyName + "," + subFamilyName;
0571:                }
0572:            }
0573:
0574:            /**
0575:             * Returns the font family name of the font.
0576:             * @return String The family name
0577:             */
0578:            public String getFamilyName() {
0579:                return familyName;
0580:            }
0581:
0582:            /**
0583:             * Returns the font sub family name of the font.
0584:             * @return String The sub family name
0585:             */
0586:            public String getSubFamilyName() {
0587:                return subFamilyName;
0588:            }
0589:
0590:            /**
0591:             * Returns the name of the character set used.
0592:             * @return String The caracter set
0593:             */
0594:            public String getCharSetName() {
0595:                return encoding;
0596:            }
0597:
0598:            /**
0599:             * Returns the CapHeight attribute of the font.
0600:             * @return int The CapHeight
0601:             */
0602:            public int getCapHeight() {
0603:                return (int) convertTTFUnit2PDFUnit(capHeight);
0604:            }
0605:
0606:            /**
0607:             * Returns the XHeight attribute of the font.
0608:             * @return int The XHeight
0609:             */
0610:            public int getXHeight() {
0611:                return (int) convertTTFUnit2PDFUnit(xHeight);
0612:            }
0613:
0614:            /**
0615:             * Returns the Flags attribute of the font.
0616:             * @return int The Flags
0617:             */
0618:            public int getFlags() {
0619:                int flags = 32; // Use Adobe Standard charset
0620:                if (italicAngle != 0) {
0621:                    flags = flags | 64;
0622:                }
0623:                if (isFixedPitch != 0) {
0624:                    flags = flags | 2;
0625:                }
0626:                if (hasSerifs) {
0627:                    flags = flags | 1;
0628:                }
0629:                return flags;
0630:            }
0631:
0632:            /**
0633:             * Returns the StemV attribute of the font.
0634:             * @return String The StemV
0635:             */
0636:            public String getStemV() {
0637:                return "0";
0638:            }
0639:
0640:            /**
0641:             * Returns the ItalicAngle attribute of the font.
0642:             * @return String The ItalicAngle
0643:             */
0644:            public String getItalicAngle() {
0645:                String ia = Short.toString((short) (italicAngle / 0x10000));
0646:
0647:                // This is the correct italic angle, however only int italic
0648:                // angles are supported at the moment so this is commented out.
0649:                /*
0650:                 * if ((italicAngle % 0x10000) > 0 )
0651:                 * ia=ia+(comma+Short.toString((short)((short)((italicAngle % 0x10000)*1000)/0x10000)));
0652:                 */
0653:                return ia;
0654:            }
0655:
0656:            /**
0657:             * Returns the font bounding box.
0658:             * @return int[] The font bbox
0659:             */
0660:            public int[] getFontBBox() {
0661:                final int[] fbb = new int[4];
0662:                fbb[0] = (int) convertTTFUnit2PDFUnit(fontBBox1);
0663:                fbb[1] = (int) convertTTFUnit2PDFUnit(fontBBox2);
0664:                fbb[2] = (int) convertTTFUnit2PDFUnit(fontBBox3);
0665:                fbb[3] = (int) convertTTFUnit2PDFUnit(fontBBox4);
0666:
0667:                return fbb;
0668:            }
0669:
0670:            /**
0671:             * Returns the LowerCaseAscent attribute of the font.
0672:             * @return int The LowerCaseAscent
0673:             */
0674:            public int getLowerCaseAscent() {
0675:                return (int) convertTTFUnit2PDFUnit(ascender);
0676:            }
0677:
0678:            /**
0679:             * Returns the LowerCaseDescent attribute of the font.
0680:             * @return int The LowerCaseDescent
0681:             */
0682:            public int getLowerCaseDescent() {
0683:                return (int) convertTTFUnit2PDFUnit(descender);
0684:            }
0685:
0686:            /**
0687:             * Returns the index of the last character, but this is for WinAnsiEncoding
0688:             * only, so the last char is < 256.
0689:             * @return short Index of the last character (<256)
0690:             */
0691:            public short getLastChar() {
0692:                return lastChar;
0693:            }
0694:
0695:            /**
0696:             * Returns the index of the first character.
0697:             * @return short Index of the first character
0698:             */
0699:            public short getFirstChar() {
0700:                return firstChar;
0701:            }
0702:
0703:            /**
0704:             * Returns an array of character widths.
0705:             * @return int[] The character widths
0706:             */
0707:            public int[] getWidths() {
0708:                int[] wx = new int[mtxTab.length];
0709:                for (int i = 0; i < wx.length; i++) {
0710:                    wx[i] = (int) convertTTFUnit2PDFUnit(mtxTab[i].getWx());
0711:                }
0712:
0713:                return wx;
0714:            }
0715:
0716:            /**
0717:             * Returns the width of a given character.
0718:             * @param idx Index of the character
0719:             * @return int Standard width
0720:             */
0721:            public int getCharWidth(int idx) {
0722:                return (int) convertTTFUnit2PDFUnit(ansiWidth[idx]);
0723:            }
0724:
0725:            /**
0726:             * Returns the kerning table.
0727:             * @return Map The kerning table
0728:             */
0729:            public Map getKerning() {
0730:                return kerningTab;
0731:            }
0732:
0733:            /**
0734:             * Returns the ANSI kerning table.
0735:             * @return Map The ANSI kerning table
0736:             */
0737:            public Map getAnsiKerning() {
0738:                return ansiKerningTab;
0739:            }
0740:
0741:            /**
0742:             * Indicates if the font may be embedded.
0743:             * @return boolean True if it may be embedded
0744:             */
0745:            public boolean isEmbeddable() {
0746:                return isEmbeddable;
0747:            }
0748:
0749:            /**
0750:             * Indicates whether or not the font is an OpenType
0751:             * CFF font (rather than a TrueType font).
0752:             * @return true if the font is in OpenType CFF format.
0753:             */
0754:            public boolean isCFF() {
0755:                return this .isCFF;
0756:            }
0757:
0758:            /**
0759:             * Read Table Directory from the current position in the
0760:             * FontFileReader and fill the global HashMap dirTabs
0761:             * with the table name (String) as key and a TTFDirTabEntry
0762:             * as value.
0763:             * @param in FontFileReader to read the table directory from
0764:             * @throws IOException in case of an I/O problem
0765:             */
0766:            protected void readDirTabs(FontFileReader in) throws IOException {
0767:                int sfntVersion = in.readTTFLong(); // TTF_FIXED_SIZE (4 bytes)
0768:                switch (sfntVersion) {
0769:                case 0x10000:
0770:                    log.debug("sfnt version: OpenType 1.0");
0771:                    break;
0772:                case 0x4F54544F: //"OTTO"
0773:                    this .isCFF = true;
0774:                    log.debug("sfnt version: OpenType with CFF data");
0775:                    break;
0776:                case 0x74727565: //"true"
0777:                    log.debug("sfnt version: Apple TrueType");
0778:                    break;
0779:                case 0x74797031: //"typ1"
0780:                    log
0781:                            .debug("sfnt version: Apple Type 1 housed in sfnt wrapper");
0782:                    break;
0783:                default:
0784:                    log.debug("Unknown sfnt version: "
0785:                            + Integer.toHexString(sfntVersion));
0786:                    break;
0787:                }
0788:                int ntabs = in.readTTFUShort();
0789:                in.skip(6); // 3xTTF_USHORT_SIZE
0790:
0791:                dirTabs = new java.util.HashMap();
0792:                TTFDirTabEntry[] pd = new TTFDirTabEntry[ntabs];
0793:                log.debug("Reading " + ntabs + " dir tables");
0794:                for (int i = 0; i < ntabs; i++) {
0795:                    pd[i] = new TTFDirTabEntry();
0796:                    dirTabs.put(pd[i].read(in), pd[i]);
0797:                }
0798:                log.debug("dir tables: " + dirTabs.keySet());
0799:            }
0800:
0801:            /**
0802:             * Read the "head" table, this reads the bounding box and
0803:             * sets the upem (unitsPerEM) variable
0804:             * @param in FontFileReader to read the header from
0805:             * @throws IOException in case of an I/O problem
0806:             */
0807:            protected void readFontHeader(FontFileReader in) throws IOException {
0808:                seekTab(in, "head", 2 * 4 + 2 * 4 + 2);
0809:                upem = in.readTTFUShort();
0810:                log.debug("unit per em: " + upem);
0811:
0812:                in.skip(16);
0813:
0814:                fontBBox1 = in.readTTFShort();
0815:                fontBBox2 = in.readTTFShort();
0816:                fontBBox3 = in.readTTFShort();
0817:                fontBBox4 = in.readTTFShort();
0818:
0819:                in.skip(2 + 2 + 2);
0820:
0821:                locaFormat = in.readTTFShort();
0822:            }
0823:
0824:            /**
0825:             * Read the number of glyphs from the "maxp" table
0826:             * @param in FontFileReader to read the number of glyphs from
0827:             * @throws IOException in case of an I/O problem
0828:             */
0829:            protected void getNumGlyphs(FontFileReader in) throws IOException {
0830:                seekTab(in, "maxp", 4);
0831:                numberOfGlyphs = in.readTTFUShort();
0832:            }
0833:
0834:            /**
0835:             * Read the "hhea" table to find the ascender and descender and
0836:             * size of "hmtx" table, as a fixed size font might have only
0837:             * one width.
0838:             * @param in FontFileReader to read the hhea table from
0839:             * @throws IOException in case of an I/O problem
0840:             */
0841:            protected void readHorizontalHeader(FontFileReader in)
0842:                    throws IOException {
0843:                seekTab(in, "hhea", 4);
0844:                hheaAscender = in.readTTFShort();
0845:                log.debug("hhea.Ascender: " + hheaAscender + " "
0846:                        + convertTTFUnit2PDFUnit(hheaAscender));
0847:                hheaDescender = in.readTTFShort();
0848:                log.debug("hhea.Descender: " + hheaDescender + " "
0849:                        + convertTTFUnit2PDFUnit(hheaDescender));
0850:
0851:                in.skip(2 + 2 + 3 * 2 + 8 * 2);
0852:                nhmtx = in.readTTFUShort();
0853:                log.debug("Number of horizontal metrics: " + nhmtx);
0854:
0855:            }
0856:
0857:            /**
0858:             * Read "hmtx" table and put the horizontal metrics
0859:             * in the mtxTab array. If the number of metrics is less
0860:             * than the number of glyphs (eg fixed size fonts), extend
0861:             * the mtxTab array and fill in the missing widths
0862:             * @param in FontFileReader to read the hmtx table from
0863:             * @throws IOException in case of an I/O problem
0864:             */
0865:            protected void readHorizontalMetrics(FontFileReader in)
0866:                    throws IOException {
0867:                seekTab(in, "hmtx", 0);
0868:
0869:                int mtxSize = Math.max(numberOfGlyphs, nhmtx);
0870:                mtxTab = new TTFMtxEntry[mtxSize];
0871:
0872:                if (TRACE_ENABLED) {
0873:                    log.debug("*** Widths array: \n");
0874:                }
0875:                for (int i = 0; i < mtxSize; i++) {
0876:                    mtxTab[i] = new TTFMtxEntry();
0877:                }
0878:                for (int i = 0; i < nhmtx; i++) {
0879:                    mtxTab[i].setWx(in.readTTFUShort());
0880:                    mtxTab[i].setLsb(in.readTTFUShort());
0881:
0882:                    if (TRACE_ENABLED) {
0883:                        if (log.isDebugEnabled()) {
0884:                            log.debug("   width[" + i + "] = "
0885:                                    + convertTTFUnit2PDFUnit(mtxTab[i].getWx())
0886:                                    + ";");
0887:                        }
0888:                    }
0889:                }
0890:
0891:                if (nhmtx < mtxSize) {
0892:                    // Fill in the missing widths
0893:                    int lastWidth = mtxTab[nhmtx - 1].getWx();
0894:                    for (int i = nhmtx; i < mtxSize; i++) {
0895:                        mtxTab[i].setWx(lastWidth);
0896:                        mtxTab[i].setLsb(in.readTTFUShort());
0897:                    }
0898:                }
0899:            }
0900:
0901:            /**
0902:             * Read the "post" table
0903:             * containing the PostScript names of the glyphs.
0904:             */
0905:            private final void readPostScript(FontFileReader in)
0906:                    throws IOException {
0907:                seekTab(in, "post", 0);
0908:                postFormat = in.readTTFLong();
0909:                italicAngle = in.readTTFULong();
0910:                underlinePosition = in.readTTFShort();
0911:                underlineThickness = in.readTTFShort();
0912:                isFixedPitch = in.readTTFULong();
0913:
0914:                //Skip memory usage values
0915:                in.skip(4 * 4);
0916:
0917:                log.debug("PostScript format: 0x"
0918:                        + Integer.toHexString(postFormat));
0919:                switch (postFormat) {
0920:                case 0x00010000:
0921:                    log.debug("PostScript format 1");
0922:                    for (int i = 0; i < Glyphs.MAC_GLYPH_NAMES.length; i++) {
0923:                        mtxTab[i].setName(Glyphs.MAC_GLYPH_NAMES[i]);
0924:                    }
0925:                    break;
0926:                case 0x00020000:
0927:                    log.debug("PostScript format 2");
0928:                    int numGlyphStrings = 0;
0929:
0930:                    // Read Number of Glyphs
0931:                    int l = in.readTTFUShort();
0932:
0933:                    // Read indexes
0934:                    for (int i = 0; i < l; i++) {
0935:                        mtxTab[i].setIndex(in.readTTFUShort());
0936:
0937:                        if (mtxTab[i].getIndex() > 257) {
0938:                            //Index is not in the Macintosh standard set
0939:                            numGlyphStrings++;
0940:                        }
0941:
0942:                        if (log.isTraceEnabled()) {
0943:                            log.trace("PostScript index: "
0944:                                    + mtxTab[i].getIndexAsString());
0945:                        }
0946:                    }
0947:
0948:                    // firstChar=minIndex;
0949:                    String[] psGlyphsBuffer = new String[numGlyphStrings];
0950:                    if (log.isDebugEnabled()) {
0951:                        log
0952:                                .debug("Reading "
0953:                                        + numGlyphStrings
0954:                                        + " glyphnames, that are not in the standard Macintosh"
0955:                                        + " set. Total number of glyphs=" + l);
0956:                    }
0957:                    for (int i = 0; i < psGlyphsBuffer.length; i++) {
0958:                        psGlyphsBuffer[i] = in.readTTFString(in.readTTFUByte());
0959:                    }
0960:
0961:                    //Set glyph names
0962:                    for (int i = 0; i < l; i++) {
0963:                        if (mtxTab[i].getIndex() < NMACGLYPHS) {
0964:                            mtxTab[i].setName(Glyphs.MAC_GLYPH_NAMES[mtxTab[i]
0965:                                    .getIndex()]);
0966:                        } else {
0967:                            if (!mtxTab[i].isIndexReserved()) {
0968:                                int k = mtxTab[i].getIndex() - NMACGLYPHS;
0969:
0970:                                if (log.isTraceEnabled()) {
0971:                                    log.trace(k + " i=" + i + " mtx="
0972:                                            + mtxTab.length + " ps="
0973:                                            + psGlyphsBuffer.length);
0974:                                }
0975:
0976:                                mtxTab[i].setName(psGlyphsBuffer[k]);
0977:                            }
0978:                        }
0979:                    }
0980:
0981:                    break;
0982:                case 0x00030000:
0983:                    // PostScript format 3 contains no glyph names
0984:                    log.debug("PostScript format 3");
0985:                    break;
0986:                default:
0987:                    log.error("Unknown PostScript format: " + postFormat);
0988:                }
0989:            }
0990:
0991:            /**
0992:             * Read the "OS/2" table
0993:             */
0994:            private final void readOS2(FontFileReader in) throws IOException {
0995:                // Check if font is embeddable
0996:                if (dirTabs.get("OS/2") != null) {
0997:                    seekTab(in, "OS/2", 2 * 4);
0998:                    int fsType = in.readTTFUShort();
0999:                    if (fsType == 2) {
1000:                        isEmbeddable = false;
1001:                    } else {
1002:                        isEmbeddable = true;
1003:                    }
1004:                    in.skip(11 * 2);
1005:                    in.skip(10); //panose array
1006:                    in.skip(4 * 4); //unicode ranges
1007:                    in.skip(4);
1008:                    in.skip(3 * 2);
1009:                    int v;
1010:                    os2Ascender = in.readTTFShort(); //sTypoAscender
1011:                    log.debug("sTypoAscender: " + os2Ascender + " "
1012:                            + convertTTFUnit2PDFUnit(os2Ascender));
1013:                    os2Descender = in.readTTFShort(); //sTypoDescender
1014:                    log.debug("sTypoDescender: " + os2Descender + " "
1015:                            + convertTTFUnit2PDFUnit(os2Descender));
1016:                    v = in.readTTFShort(); //sTypoLineGap
1017:                    log.debug("sTypoLineGap: " + v);
1018:                    v = in.readTTFUShort(); //usWinAscent
1019:                    log.debug("usWinAscent: " + v + " "
1020:                            + convertTTFUnit2PDFUnit(v));
1021:                    v = in.readTTFUShort(); //usWinDescent
1022:                    log.debug("usWinDescent: " + v + " "
1023:                            + convertTTFUnit2PDFUnit(v));
1024:                    in.skip(2 * 4);
1025:                    this .os2xHeight = in.readTTFShort(); //sxHeight
1026:                    log.debug("sxHeight: " + this .os2xHeight);
1027:                    this .os2CapHeight = in.readTTFShort(); //sCapHeight
1028:                    log.debug("sCapHeight: " + this .os2CapHeight);
1029:
1030:                } else {
1031:                    isEmbeddable = true;
1032:                }
1033:            }
1034:
1035:            /**
1036:             * Read the "loca" table.
1037:             * @param in FontFileReader to read from
1038:             * @throws IOException In case of a I/O problem
1039:             */
1040:            protected final void readIndexToLocation(FontFileReader in)
1041:                    throws IOException {
1042:                if (!seekTab(in, "loca", 0)) {
1043:                    throw new IOException(
1044:                            "'loca' table not found, happens when the font file doesn't"
1045:                                    + " contain TrueType outlines (trying to read an OpenType CFF font maybe?)");
1046:                }
1047:                for (int i = 0; i < numberOfGlyphs; i++) {
1048:                    mtxTab[i].setOffset(locaFormat == 1 ? in.readTTFULong()
1049:                            : (in.readTTFUShort() << 1));
1050:                }
1051:                lastLoca = (locaFormat == 1 ? in.readTTFULong() : (in
1052:                        .readTTFUShort() << 1));
1053:            }
1054:
1055:            /**
1056:             * Read the "glyf" table to find the bounding boxes.
1057:             * @param in FontFileReader to read from
1058:             * @throws IOException In case of a I/O problem
1059:             */
1060:            private final void readGlyf(FontFileReader in) throws IOException {
1061:                TTFDirTabEntry dirTab = (TTFDirTabEntry) dirTabs.get("glyf");
1062:                if (dirTab == null) {
1063:                    throw new IOException(
1064:                            "glyf table not found, cannot continue");
1065:                }
1066:                for (int i = 0; i < (numberOfGlyphs - 1); i++) {
1067:                    if (mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) {
1068:                        in.seekSet(dirTab.getOffset() + mtxTab[i].getOffset());
1069:                        in.skip(2);
1070:                        final int[] bbox = { in.readTTFShort(),
1071:                                in.readTTFShort(), in.readTTFShort(),
1072:                                in.readTTFShort() };
1073:                        mtxTab[i].setBoundingBox(bbox);
1074:                    } else {
1075:                        mtxTab[i].setBoundingBox(mtxTab[0].getBoundingBox());
1076:                    }
1077:                }
1078:
1079:                long n = ((TTFDirTabEntry) dirTabs.get("glyf")).getOffset();
1080:                for (int i = 0; i < numberOfGlyphs; i++) {
1081:                    if ((i + 1) >= mtxTab.length
1082:                            || mtxTab[i].getOffset() != mtxTab[i + 1]
1083:                                    .getOffset()) {
1084:                        in.seekSet(n + mtxTab[i].getOffset());
1085:                        in.skip(2);
1086:                        final int[] bbox = { in.readTTFShort(),
1087:                                in.readTTFShort(), in.readTTFShort(),
1088:                                in.readTTFShort() };
1089:                        mtxTab[i].setBoundingBox(bbox);
1090:                    } else {
1091:                        /**@todo Verify that this is correct, looks like a copy/paste bug (jm)*/
1092:                        final int bbox0 = mtxTab[0].getBoundingBox()[0];
1093:                        final int[] bbox = { bbox0, bbox0, bbox0, bbox0 };
1094:                        mtxTab[i].setBoundingBox(bbox);
1095:                        /* Original code
1096:                        mtxTab[i].bbox[0] = mtxTab[0].bbox[0];
1097:                        mtxTab[i].bbox[1] = mtxTab[0].bbox[0];
1098:                        mtxTab[i].bbox[2] = mtxTab[0].bbox[0];
1099:                        mtxTab[i].bbox[3] = mtxTab[0].bbox[0]; */
1100:                    }
1101:                    if (log.isTraceEnabled()) {
1102:                        log.trace(mtxTab[i].toString(this ));
1103:                    }
1104:                }
1105:            }
1106:
1107:            /**
1108:             * Read the "name" table.
1109:             * @param in FontFileReader to read from
1110:             * @throws IOException In case of a I/O problem
1111:             */
1112:            private final void readName(FontFileReader in) throws IOException {
1113:                seekTab(in, "name", 2);
1114:                int i = in.getCurrentPos();
1115:                int n = in.readTTFUShort();
1116:                int j = in.readTTFUShort() + i - 2;
1117:                i += 2 * 2;
1118:
1119:                while (n-- > 0) {
1120:                    // getLogger().debug("Iteration: " + n);
1121:                    in.seekSet(i);
1122:                    final int platformID = in.readTTFUShort();
1123:                    final int encodingID = in.readTTFUShort();
1124:                    final int languageID = in.readTTFUShort();
1125:
1126:                    int k = in.readTTFUShort();
1127:                    int l = in.readTTFUShort();
1128:
1129:                    if (((platformID == 1 || platformID == 3) && (encodingID == 0 || encodingID == 1))
1130:                            && (k == 1 || k == 2 || k == 0 || k == 4 || k == 6)) {
1131:                        in.seekSet(j + in.readTTFUShort());
1132:                        String txt = in.readTTFString(l);
1133:
1134:                        log.debug(platformID + " " + encodingID + " "
1135:                                + languageID + " " + k + " " + txt);
1136:                        switch (k) {
1137:                        case 0:
1138:                            notice = txt;
1139:                            break;
1140:                        case 1:
1141:                            familyName = txt;
1142:                            break;
1143:                        case 2:
1144:                            subFamilyName = txt;
1145:                            break;
1146:                        case 4:
1147:                            fullName = txt;
1148:                            break;
1149:                        case 6:
1150:                            fontName = txt;
1151:                            break;
1152:                        }
1153:                        if (!notice.equals("") && !fullName.equals("")
1154:                                && !fontName.equals("")
1155:                                && !familyName.equals("")
1156:                                && !subFamilyName.equals("")) {
1157:                            break;
1158:                        }
1159:                    }
1160:                    i += 6 * 2;
1161:                }
1162:            }
1163:
1164:            /**
1165:             * Read the "PCLT" table to find xHeight and capHeight.
1166:             * @param in FontFileReader to read from
1167:             * @throws IOException In case of a I/O problem
1168:             */
1169:            private final boolean readPCLT(FontFileReader in)
1170:                    throws IOException {
1171:                TTFDirTabEntry dirTab = (TTFDirTabEntry) dirTabs.get("PCLT");
1172:                if (dirTab != null) {
1173:                    in.seekSet(dirTab.getOffset() + 4 + 4 + 2);
1174:                    xHeight = in.readTTFUShort();
1175:                    log.debug("xHeight from PCLT: " + xHeight + " "
1176:                            + convertTTFUnit2PDFUnit(xHeight));
1177:                    in.skip(2 * 2);
1178:                    capHeight = in.readTTFUShort();
1179:                    log.debug("capHeight from PCLT: " + capHeight + " "
1180:                            + convertTTFUnit2PDFUnit(capHeight));
1181:                    in.skip(2 + 16 + 8 + 6 + 1 + 1);
1182:
1183:                    int serifStyle = in.readTTFUByte();
1184:                    serifStyle = serifStyle >> 6;
1185:                    serifStyle = serifStyle & 3;
1186:                    if (serifStyle == 1) {
1187:                        hasSerifs = false;
1188:                    } else {
1189:                        hasSerifs = true;
1190:                    }
1191:                    return true;
1192:                } else {
1193:                    return false;
1194:                }
1195:            }
1196:
1197:            /**
1198:             * Determines the right source for the ascender and descender values. The problem here is
1199:             * that the interpretation of these values is not the same for every font. There doesn't seem
1200:             * to be a uniform definition of an ascender and a descender. In some fonts
1201:             * the hhea values are defined after the Apple interpretation, but not in every font. The
1202:             * same problem is in the OS/2 table. FOP needs the ascender and descender to determine the
1203:             * baseline so we need values which add up more or less to the "em box". However, due to
1204:             * accent modifiers a character can grow beyond the em box. 
1205:             */
1206:            private void determineAscDesc() {
1207:                int hheaBoxHeight = hheaAscender - hheaDescender;
1208:                int os2BoxHeight = os2Ascender - os2Descender;
1209:                if (os2Ascender > 0 && os2BoxHeight <= upem) {
1210:                    ascender = os2Ascender;
1211:                    descender = os2Descender;
1212:                } else if (hheaAscender > 0 && hheaBoxHeight <= upem) {
1213:                    ascender = hheaAscender;
1214:                    descender = hheaDescender;
1215:                } else {
1216:                    if (os2Ascender > 0) {
1217:                        //Fall back to info from OS/2 if possible
1218:                        ascender = os2Ascender;
1219:                        descender = os2Descender;
1220:                    } else {
1221:                        ascender = hheaAscender;
1222:                        descender = hheaDescender;
1223:                    }
1224:                }
1225:
1226:                log.debug("Font box height: " + (ascender - descender));
1227:                if (ascender - descender > upem) {
1228:                    log
1229:                            .warn("Ascender and descender together are larger than the em box."
1230:                                    + " This could lead to a wrong baseline placement in Apache FOP.");
1231:                }
1232:            }
1233:
1234:            private void guessVerticalMetricsFromGlyphBBox() {
1235:                // Approximate capHeight from height of "H"
1236:                // It's most unlikely that a font misses the PCLT table
1237:                // This also assumes that postscriptnames exists ("H")
1238:                // Should look it up int the cmap (that wouldn't help
1239:                // for charsets without H anyway...)
1240:                // Same for xHeight with the letter "x"
1241:                int localCapHeight = 0;
1242:                int localXHeight = 0;
1243:                int localAscender = 0;
1244:                int localDescender = 0;
1245:                for (int i = 0; i < mtxTab.length; i++) {
1246:                    if ("H".equals(mtxTab[i].getName())) {
1247:                        localCapHeight = mtxTab[i].getBoundingBox()[3];
1248:                    } else if ("x".equals(mtxTab[i].getName())) {
1249:                        localXHeight = mtxTab[i].getBoundingBox()[3];
1250:                    } else if ("d".equals(mtxTab[i].getName())) {
1251:                        localAscender = mtxTab[i].getBoundingBox()[3];
1252:                    } else if ("p".equals(mtxTab[i].getName())) {
1253:                        localDescender = mtxTab[i].getBoundingBox()[1];
1254:                    } else {
1255:                        // OpenType Fonts with a version 3.0 "post" table don't have glyph names.
1256:                        // Use Unicode indices instead.
1257:                        List unicodeIndex = mtxTab[i].getUnicodeIndex();
1258:                        if (unicodeIndex.size() > 0) {
1259:                            //Only the first index is used
1260:                            char ch = (char) ((Integer) unicodeIndex.get(0))
1261:                                    .intValue();
1262:                            if (ch == 'H') {
1263:                                localCapHeight = mtxTab[i].getBoundingBox()[3];
1264:                            } else if (ch == 'x') {
1265:                                localXHeight = mtxTab[i].getBoundingBox()[3];
1266:                            } else if (ch == 'd') {
1267:                                localAscender = mtxTab[i].getBoundingBox()[3];
1268:                            } else if (ch == 'p') {
1269:                                localDescender = mtxTab[i].getBoundingBox()[1];
1270:                            }
1271:                        }
1272:                    }
1273:                }
1274:                log.debug("Ascender from glyph 'd': " + localAscender + " "
1275:                        + convertTTFUnit2PDFUnit(localAscender));
1276:                log.debug("Descender from glyph 'p': " + localDescender + " "
1277:                        + convertTTFUnit2PDFUnit(localDescender));
1278:                if (ascender - descender > upem) {
1279:                    log
1280:                            .debug("Replacing specified ascender/descender with derived values to get values"
1281:                                    + " which fit in the em box.");
1282:                    ascender = localAscender;
1283:                    descender = localDescender;
1284:                }
1285:
1286:                log.debug("xHeight from glyph 'x': " + localXHeight + " "
1287:                        + convertTTFUnit2PDFUnit(localXHeight));
1288:                log.debug("CapHeight from glyph 'H': " + localCapHeight + " "
1289:                        + convertTTFUnit2PDFUnit(localCapHeight));
1290:                if (capHeight == 0) {
1291:                    capHeight = localCapHeight;
1292:                    if (capHeight == 0) {
1293:                        capHeight = os2CapHeight;
1294:                    }
1295:                    if (capHeight == 0) {
1296:                        log.warn("capHeight value could not be determined."
1297:                                + " The font may not work as expected.");
1298:                    }
1299:                }
1300:                if (xHeight == 0) {
1301:                    xHeight = localXHeight;
1302:                    if (xHeight == 0) {
1303:                        xHeight = os2xHeight;
1304:                    }
1305:                    if (xHeight == 0) {
1306:                        log.warn("xHeight value could not be determined."
1307:                                + " The font may not work as expected.");
1308:                    }
1309:                }
1310:            }
1311:
1312:            /**
1313:             * Read the kerning table, create a table for both CIDs and
1314:             * winAnsiEncoding.
1315:             * @param in FontFileReader to read from
1316:             * @throws IOException In case of a I/O problem
1317:             */
1318:            private final void readKerning(FontFileReader in)
1319:                    throws IOException {
1320:                // Read kerning
1321:                kerningTab = new java.util.HashMap();
1322:                ansiKerningTab = new java.util.HashMap();
1323:                TTFDirTabEntry dirTab = (TTFDirTabEntry) dirTabs.get("kern");
1324:                if (dirTab != null) {
1325:                    seekTab(in, "kern", 2);
1326:                    for (int n = in.readTTFUShort(); n > 0; n--) {
1327:                        in.skip(2 * 2);
1328:                        int k = in.readTTFUShort();
1329:                        if (!((k & 1) != 0) || (k & 2) != 0 || (k & 4) != 0) {
1330:                            return;
1331:                        }
1332:                        if ((k >> 8) != 0) {
1333:                            continue;
1334:                        }
1335:
1336:                        k = in.readTTFUShort();
1337:                        in.skip(3 * 2);
1338:                        while (k-- > 0) {
1339:                            int i = in.readTTFUShort();
1340:                            int j = in.readTTFUShort();
1341:                            int kpx = in.readTTFShort();
1342:                            if (kpx != 0) {
1343:                                // CID kerning table entry, using unicode indexes
1344:                                final Integer iObj = glyphToUnicode(i);
1345:                                final Integer u2 = glyphToUnicode(j);
1346:                                if (iObj == null) {
1347:                                    // happens for many fonts (Ubuntu font set),
1348:                                    // stray entries in the kerning table?? 
1349:                                    log
1350:                                            .warn("Unicode index (1) not found for glyph "
1351:                                                    + i);
1352:                                } else if (u2 == null) {
1353:                                    log
1354:                                            .warn("Unicode index (2) not found for glyph "
1355:                                                    + i);
1356:                                } else {
1357:                                    Map adjTab = (Map) kerningTab.get(iObj);
1358:                                    if (adjTab == null) {
1359:                                        adjTab = new java.util.HashMap();
1360:                                    }
1361:                                    adjTab.put(u2, new Integer(
1362:                                            (int) convertTTFUnit2PDFUnit(kpx)));
1363:                                    kerningTab.put(iObj, adjTab);
1364:                                }
1365:                            }
1366:                        }
1367:                    }
1368:
1369:                    // Create winAnsiEncoded kerning table from kerningTab
1370:                    // (could probably be simplified, for now we remap back to CID indexes and 
1371:                    // then to winAnsi)
1372:                    Iterator ae = kerningTab.keySet().iterator();
1373:                    while (ae.hasNext()) {
1374:                        Integer unicodeKey1 = (Integer) ae.next();
1375:                        Integer cidKey1 = unicodeToGlyph(unicodeKey1.intValue());
1376:                        Map akpx = new java.util.HashMap();
1377:                        Map ckpx = (Map) kerningTab.get(unicodeKey1);
1378:
1379:                        Iterator aee = ckpx.keySet().iterator();
1380:                        while (aee.hasNext()) {
1381:                            Integer unicodeKey2 = (Integer) aee.next();
1382:                            Integer cidKey2 = unicodeToGlyph(unicodeKey2
1383:                                    .intValue());
1384:                            Integer kern = (Integer) ckpx.get(unicodeKey2);
1385:
1386:                            Iterator uniMap = mtxTab[cidKey2.intValue()]
1387:                                    .getUnicodeIndex().listIterator();
1388:                            while (uniMap.hasNext()) {
1389:                                Integer unicodeKey = (Integer) uniMap.next();
1390:                                Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey
1391:                                        .intValue());
1392:                                for (int u = 0; u < ansiKeys.length; u++) {
1393:                                    akpx.put(ansiKeys[u], kern);
1394:                                }
1395:                            }
1396:                        }
1397:
1398:                        if (akpx.size() > 0) {
1399:                            Iterator uniMap = mtxTab[cidKey1.intValue()]
1400:                                    .getUnicodeIndex().listIterator();
1401:                            while (uniMap.hasNext()) {
1402:                                Integer unicodeKey = (Integer) uniMap.next();
1403:                                Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey
1404:                                        .intValue());
1405:                                for (int u = 0; u < ansiKeys.length; u++) {
1406:                                    ansiKerningTab.put(ansiKeys[u], akpx);
1407:                                }
1408:                            }
1409:                        }
1410:                    }
1411:                }
1412:            }
1413:
1414:            /**
1415:             * Return a List with TTFCmapEntry.
1416:             * @return A list of TTFCmapEntry objects
1417:             */
1418:            public List getCMaps() {
1419:                return cmaps;
1420:            }
1421:
1422:            /**
1423:             * Check if this is a TrueType collection and that the given
1424:             * name exists in the collection.
1425:             * If it does, set offset in fontfile to the beginning of
1426:             * the Table Directory for that font.
1427:             * @param in FontFileReader to read from
1428:             * @param name The name to check
1429:             * @return True if not collection or font name present, false otherwise
1430:             * @throws IOException In case of an I/O problem
1431:             */
1432:            protected final boolean checkTTC(FontFileReader in, String name)
1433:                    throws IOException {
1434:                String tag = in.readTTFString(4);
1435:
1436:                if ("ttcf".equals(tag)) {
1437:                    // This is a TrueType Collection
1438:                    in.skip(4);
1439:
1440:                    // Read directory offsets
1441:                    int numDirectories = (int) in.readTTFULong();
1442:                    // int numDirectories=in.readTTFUShort();
1443:                    long[] dirOffsets = new long[numDirectories];
1444:                    for (int i = 0; i < numDirectories; i++) {
1445:                        dirOffsets[i] = in.readTTFULong();
1446:                    }
1447:
1448:                    log.info("This is a TrueType collection file with "
1449:                            + numDirectories + " fonts");
1450:                    log.info("Containing the following fonts: ");
1451:                    // Read all the directories and name tables to check
1452:                    // If the font exists - this is a bit ugly, but...
1453:                    boolean found = false;
1454:
1455:                    // Iterate through all name tables even if font
1456:                    // Is found, just to show all the names
1457:                    long dirTabOffset = 0;
1458:                    for (int i = 0; (i < numDirectories); i++) {
1459:                        in.seekSet(dirOffsets[i]);
1460:                        readDirTabs(in);
1461:
1462:                        readName(in);
1463:
1464:                        if (fullName.equals(name)) {
1465:                            found = true;
1466:                            dirTabOffset = dirOffsets[i];
1467:                            log.info(fullName + " <-- selected");
1468:                        } else {
1469:                            log.info(fullName);
1470:                        }
1471:
1472:                        // Reset names
1473:                        notice = "";
1474:                        fullName = "";
1475:                        familyName = "";
1476:                        fontName = "";
1477:                        subFamilyName = "";
1478:                    }
1479:
1480:                    in.seekSet(dirTabOffset);
1481:                    return found;
1482:                } else {
1483:                    in.seekSet(0);
1484:                    return true;
1485:                }
1486:            }
1487:
1488:            /*
1489:             * Helper classes, they are not very efficient, but that really
1490:             * doesn't matter...
1491:             */
1492:            private Integer[] unicodeToWinAnsi(int unicode) {
1493:                List ret = new java.util.ArrayList();
1494:                for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) {
1495:                    if (unicode == Glyphs.WINANSI_ENCODING[i]) {
1496:                        ret.add(new Integer(i));
1497:                    }
1498:                }
1499:                return (Integer[]) ret.toArray(new Integer[0]);
1500:            }
1501:
1502:            /**
1503:             * Dumps a few informational values to System.out.
1504:             */
1505:            public void printStuff() {
1506:                System.out.println("Font name:   " + fontName);
1507:                System.out.println("Full name:   " + fullName);
1508:                System.out.println("Family name: " + familyName);
1509:                System.out.println("Subfamily name: " + subFamilyName);
1510:                System.out.println("Notice:      " + notice);
1511:                System.out.println("xHeight:     "
1512:                        + (int) convertTTFUnit2PDFUnit(xHeight));
1513:                System.out.println("capheight:   "
1514:                        + (int) convertTTFUnit2PDFUnit(capHeight));
1515:
1516:                int italic = (int) (italicAngle >> 16);
1517:                System.out.println("Italic:      " + italic);
1518:                System.out.print("ItalicAngle: "
1519:                        + (short) (italicAngle / 0x10000));
1520:                if ((italicAngle % 0x10000) > 0) {
1521:                    System.out.print("."
1522:                            + (short) ((italicAngle % 0x10000) * 1000)
1523:                            / 0x10000);
1524:                }
1525:                System.out.println();
1526:                System.out.println("Ascender:    "
1527:                        + convertTTFUnit2PDFUnit(ascender));
1528:                System.out.println("Descender:   "
1529:                        + convertTTFUnit2PDFUnit(descender));
1530:                System.out.println("FontBBox:    ["
1531:                        + (int) convertTTFUnit2PDFUnit(fontBBox1) + " "
1532:                        + (int) convertTTFUnit2PDFUnit(fontBBox2) + " "
1533:                        + (int) convertTTFUnit2PDFUnit(fontBBox3) + " "
1534:                        + (int) convertTTFUnit2PDFUnit(fontBBox4) + "]");
1535:            }
1536:
1537:            /**
1538:             * Map a glyph index to the corresponding unicode code point
1539:             * 
1540:             * @param glyphIndex
1541:             * @return unicode code point
1542:             * @throws IOException if glyphIndex not found
1543:             */
1544:            private Integer glyphToUnicode(int glyphIndex) throws IOException {
1545:                return (Integer) glyphToUnicodeMap.get(new Integer(glyphIndex));
1546:            }
1547:
1548:            /**
1549:             * Map a unicode code point to the corresponding glyph index 
1550:             * 
1551:             * @param unicodeIndex unicode code point
1552:             * @return glyph index
1553:             * @throws IOException if unicodeIndex not found
1554:             */
1555:            private Integer unicodeToGlyph(int unicodeIndex) throws IOException {
1556:                final Integer result = (Integer) unicodeToGlyphMap
1557:                        .get(new Integer(unicodeIndex));
1558:                if (result == null) {
1559:                    throw new IOException(
1560:                            "Glyph index not found for unicode value "
1561:                                    + unicodeIndex);
1562:                }
1563:                return result;
1564:            }
1565:
1566:            /**
1567:             * Static main method to get info about a TrueType font.
1568:             * @param args The command line arguments
1569:             */
1570:            public static void main(String[] args) {
1571:                try {
1572:                    TTFFile ttfFile = new TTFFile();
1573:
1574:                    FontFileReader reader = new FontFileReader(args[0]);
1575:
1576:                    String name = null;
1577:                    if (args.length >= 2) {
1578:                        name = args[1];
1579:                    }
1580:
1581:                    ttfFile.readFont(reader, name);
1582:                    ttfFile.printStuff();
1583:
1584:                } catch (IOException ioe) {
1585:                    System.err.println("Problem reading font: "
1586:                            + ioe.toString());
1587:                    ioe.printStackTrace(System.err);
1588:                }
1589:            }
1590:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.