您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

TTFFile.java 69KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.fonts.truetype;
  19. import java.io.IOException;
  20. import java.util.ArrayList;
  21. import java.util.BitSet;
  22. import java.util.Comparator;
  23. import java.util.HashMap;
  24. import java.util.HashSet;
  25. import java.util.Iterator;
  26. import java.util.List;
  27. import java.util.Map;
  28. import java.util.Map.Entry;
  29. import java.util.Set;
  30. import java.util.SortedSet;
  31. import java.util.TreeSet;
  32. import org.apache.commons.logging.Log;
  33. import org.apache.commons.logging.LogFactory;
  34. import org.apache.fop.fonts.BFEntry;
  35. import org.apache.fop.fonts.FontUtil;
  36. import org.apache.xmlgraphics.fonts.Glyphs;
  37. /**
  38. * Reads a TrueType file or a TrueType Collection.
  39. * The TrueType spec can be found at the Microsoft.
  40. * Typography site: http://www.microsoft.com/truetype/
  41. */
  42. public class TTFFile {
  43. static final byte NTABS = 24;
  44. static final int MAX_CHAR_CODE = 255;
  45. static final int ENC_BUF_SIZE = 1024;
  46. private static final String[] MAC_GLYPH_ORDERING = {
  47. /* 0x000 */
  48. ".notdef", ".null", "nonmarkingreturn", "space",
  49. "exclam", "quotedbl", "numbersign", "dollar",
  50. "percent", "ampersand", "quotesingle", "parenleft",
  51. "parenright", "asterisk", "plus", "comma",
  52. /* 0x010 */
  53. "hyphen", "period", "slash", "zero",
  54. "one", "two", "three", "four",
  55. "five", "six", "seven", "eight",
  56. "nine", "colon", "semicolon", "less",
  57. /* 0x020 */
  58. "equal", "greater", "question", "at",
  59. "A", "B", "C", "D",
  60. "E", "F", "G", "H",
  61. "I", "J", "K", "L",
  62. /* 0x030 */
  63. "M", "N", "O", "P",
  64. "Q", "R", "S", "T",
  65. "U", "V", "W", "X",
  66. "Y", "Z", "bracketleft", "backslash",
  67. /* 0x040 */
  68. "bracketright", "asciicircum", "underscore", "grave",
  69. "a", "b", "c", "d",
  70. "e", "f", "g", "h",
  71. "i", "j", "k", "l",
  72. /* 0x050 */
  73. "m", "n", "o", "p",
  74. "q", "r", "s", "t",
  75. "u", "v", "w", "x",
  76. "y", "z", "braceleft", "bar",
  77. /* 0x060 */
  78. "braceright", "asciitilde", "Adieresis", "Aring",
  79. "Ccedilla", "Eacute", "Ntilde", "Odieresis",
  80. "Udieresis", "aacute", "agrave", "acircumflex",
  81. "adieresis", "atilde", "aring", "ccedilla",
  82. /* 0x070 */
  83. "eacute", "egrave", "ecircumflex", "edieresis",
  84. "iacute", "igrave", "icircumflex", "idieresis",
  85. "ntilde", "oacute", "ograve", "ocircumflex",
  86. "odieresis", "otilde", "uacute", "ugrave",
  87. /* 0x080 */
  88. "ucircumflex", "udieresis", "dagger", "degree",
  89. "cent", "sterling", "section", "bullet",
  90. "paragraph", "germandbls", "registered", "copyright",
  91. "trademark", "acute", "dieresis", "notequal",
  92. /* 0x090 */
  93. "AE", "Oslash", "infinity", "plusminus",
  94. "lessequal", "greaterequal", "yen", "mu",
  95. "partialdiff", "summation", "product", "pi",
  96. "integral", "ordfeminine", "ordmasculine", "Omega",
  97. /* 0x0A0 */
  98. "ae", "oslash", "questiondown", "exclamdown",
  99. "logicalnot", "radical", "florin", "approxequal",
  100. "Delta", "guillemotleft", "guillemotright", "ellipsis",
  101. "nonbreakingspace", "Agrave", "Atilde", "Otilde",
  102. /* 0x0B0 */
  103. "OE", "oe", "endash", "emdash",
  104. "quotedblleft", "quotedblright", "quoteleft", "quoteright",
  105. "divide", "lozenge", "ydieresis", "Ydieresis",
  106. "fraction", "currency", "guilsinglleft", "guilsinglright",
  107. /* 0x0C0 */
  108. "fi", "fl", "daggerdbl", "periodcentered",
  109. "quotesinglbase", "quotedblbase", "perthousand", "Acircumflex",
  110. "Ecircumflex", "Aacute", "Edieresis", "Egrave",
  111. "Iacute", "Icircumflex", "Idieresis", "Igrave",
  112. /* 0x0D0 */
  113. "Oacute", "Ocircumflex", "apple", "Ograve",
  114. "Uacute", "Ucircumflex", "Ugrave", "dotlessi",
  115. "circumflex", "tilde", "macron", "breve",
  116. "dotaccent", "ring", "cedilla", "hungarumlaut",
  117. /* 0x0E0 */
  118. "ogonek", "caron", "Lslash", "lslash",
  119. "Scaron", "scaron", "Zcaron", "zcaron",
  120. "brokenbar", "Eth", "eth", "Yacute",
  121. "yacute", "Thorn", "thorn", "minus",
  122. /* 0x0F0 */
  123. "multiply", "onesuperior", "twosuperior", "threesuperior",
  124. "onehalf", "onequarter", "threequarters", "franc",
  125. "Gbreve", "gbreve", "Idotaccent", "Scedilla",
  126. "scedilla", "Cacute", "cacute", "Ccaron",
  127. /* 0x100 */
  128. "ccaron", "dcroat"
  129. };
  130. /** The FontFileReader used to read this truetype font */
  131. protected FontFileReader fontFile;
  132. /** Set to true to get even more debug output than with level DEBUG */
  133. public static final boolean TRACE_ENABLED = false;
  134. private final String encoding = "WinAnsiEncoding"; // Default encoding
  135. private final short firstChar = 0;
  136. private boolean isEmbeddable = true;
  137. private boolean hasSerifs = true;
  138. /**
  139. * Table directory
  140. */
  141. protected Map<TTFTableName, TTFDirTabEntry> dirTabs;
  142. private Map<Integer, Map<Integer, Integer>> kerningTab; // for CIDs
  143. private Map<Integer, Map<Integer, Integer>> ansiKerningTab; // For winAnsiEncoding
  144. private List<BFEntry> cmaps;
  145. private List<UnicodeMapping> unicodeMapping;
  146. private int upem; // unitsPerEm from "head" table
  147. private int nhmtx; // Number of horizontal metrics
  148. private PostScriptVersion postScriptVersion;
  149. private int locaFormat;
  150. /**
  151. * Offset to last loca
  152. */
  153. protected long lastLoca = 0;
  154. private int numberOfGlyphs; // Number of glyphs in font (read from "maxp" table)
  155. /**
  156. * Contains glyph data
  157. */
  158. protected TTFMtxEntry[] mtxTab; // Contains glyph data
  159. private String postScriptName = "";
  160. private String fullName = "";
  161. private String notice = "";
  162. private Set<String> familyNames = new HashSet<String>();
  163. private String subFamilyName = "";
  164. private long italicAngle = 0;
  165. private long isFixedPitch = 0;
  166. private int fontBBox1 = 0;
  167. private int fontBBox2 = 0;
  168. private int fontBBox3 = 0;
  169. private int fontBBox4 = 0;
  170. private int capHeight = 0;
  171. private int os2CapHeight = 0;
  172. private int underlinePosition = 0;
  173. private int underlineThickness = 0;
  174. private int xHeight = 0;
  175. private int os2xHeight = 0;
  176. //Effective ascender/descender
  177. private int ascender = 0;
  178. private int descender = 0;
  179. //Ascender/descender from hhea table
  180. private int hheaAscender = 0;
  181. private int hheaDescender = 0;
  182. //Ascender/descender from OS/2 table
  183. private int os2Ascender = 0;
  184. private int os2Descender = 0;
  185. private int usWeightClass = 0;
  186. private short lastChar = 0;
  187. private int[] ansiWidth;
  188. private Map<Integer, List<Integer>> ansiIndex;
  189. // internal mapping of glyph indexes to unicode indexes
  190. // used for quick mappings in this class
  191. private Map<Integer, Integer> glyphToUnicodeMap = new HashMap<Integer, Integer> ();
  192. private Map<Integer, Integer> unicodeToGlyphMap = new HashMap<Integer, Integer> ();
  193. private TTFDirTabEntry currentDirTab;
  194. private boolean isCFF;
  195. /**
  196. * logging instance
  197. */
  198. protected Log log = LogFactory.getLog(TTFFile.class);
  199. /**
  200. * Key-value helper class (immutable)
  201. */
  202. final class UnicodeMapping {
  203. private final int unicodeIndex;
  204. private final int glyphIndex;
  205. UnicodeMapping(int glyphIndex, int unicodeIndex) {
  206. this.unicodeIndex = unicodeIndex;
  207. this.glyphIndex = glyphIndex;
  208. glyphToUnicodeMap.put(new Integer(glyphIndex), new Integer(unicodeIndex));
  209. unicodeToGlyphMap.put(new Integer(unicodeIndex), new Integer(glyphIndex));
  210. }
  211. /**
  212. * Returns the glyphIndex.
  213. * @return the glyph index
  214. */
  215. public int getGlyphIndex() {
  216. return glyphIndex;
  217. }
  218. /**
  219. * Returns the unicodeIndex.
  220. * @return the Unicode index
  221. */
  222. public int getUnicodeIndex() {
  223. return unicodeIndex;
  224. }
  225. }
  226. /**
  227. * Version of the PostScript table (<q>post</q>) contained in this font.
  228. */
  229. public static enum PostScriptVersion {
  230. /** PostScript table version 1.0. */
  231. V1,
  232. /** PostScript table version 2.0. */
  233. V2,
  234. /** PostScript table version 3.0. */
  235. V3,
  236. /** Unknown version of the PostScript table. */
  237. UNKNOWN;
  238. }
  239. /**
  240. * Position inputstream to position indicated
  241. * in the dirtab offset + offset
  242. */
  243. boolean seekTab(FontFileReader in, TTFTableName tableName,
  244. long offset) throws IOException {
  245. TTFDirTabEntry dt = dirTabs.get(tableName);
  246. if (dt == null) {
  247. log.error("Dirtab " + tableName.getName() + " not found.");
  248. return false;
  249. } else {
  250. in.seekSet(dt.getOffset() + offset);
  251. this.currentDirTab = dt;
  252. }
  253. return true;
  254. }
  255. /**
  256. * Convert from truetype unit to pdf unit based on the
  257. * unitsPerEm field in the "head" table
  258. * @param n truetype unit
  259. * @return pdf unit
  260. */
  261. public int convertTTFUnit2PDFUnit(int n) {
  262. int ret;
  263. if (n < 0) {
  264. long rest1 = n % upem;
  265. long storrest = 1000 * rest1;
  266. long ledd2 = (storrest != 0 ? rest1 / storrest : 0);
  267. ret = -((-1000 * n) / upem - (int)ledd2);
  268. } else {
  269. ret = (n / upem) * 1000 + ((n % upem) * 1000) / upem;
  270. }
  271. return ret;
  272. }
  273. /**
  274. * Read the cmap table,
  275. * return false if the table is not present or only unsupported
  276. * tables are present. Currently only unicode cmaps are supported.
  277. * Set the unicodeIndex in the TTFMtxEntries and fills in the
  278. * cmaps vector.
  279. */
  280. private boolean readCMAP() throws IOException {
  281. unicodeMapping = new ArrayList<UnicodeMapping>();
  282. seekTab(fontFile, TTFTableName.CMAP, 2);
  283. int numCMap = fontFile.readTTFUShort(); // Number of cmap subtables
  284. long cmapUniOffset = 0;
  285. long symbolMapOffset = 0;
  286. if (log.isDebugEnabled()) {
  287. log.debug(numCMap + " cmap tables");
  288. }
  289. //Read offset for all tables. We are only interested in the unicode table
  290. for (int i = 0; i < numCMap; i++) {
  291. int cmapPID = fontFile.readTTFUShort();
  292. int cmapEID = fontFile.readTTFUShort();
  293. long cmapOffset = fontFile.readTTFLong();
  294. if (log.isDebugEnabled()) {
  295. log.debug("Platform ID: " + cmapPID + " Encoding: " + cmapEID);
  296. }
  297. if (cmapPID == 3 && cmapEID == 1) {
  298. cmapUniOffset = cmapOffset;
  299. }
  300. if (cmapPID == 3 && cmapEID == 0) {
  301. symbolMapOffset = cmapOffset;
  302. }
  303. }
  304. if (cmapUniOffset > 0) {
  305. return readUnicodeCmap(cmapUniOffset, 1);
  306. } else if (symbolMapOffset > 0) {
  307. return readUnicodeCmap(symbolMapOffset, 0);
  308. } else {
  309. log.fatal("Unsupported TrueType font: No Unicode or Symbol cmap table"
  310. + " not present. Aborting");
  311. return false;
  312. }
  313. }
  314. private boolean readUnicodeCmap // CSOK: MethodLength
  315. (long cmapUniOffset, int encodingID)
  316. throws IOException {
  317. //Read CMAP table and correct mtxTab.index
  318. int mtxPtr = 0;
  319. // Read unicode cmap
  320. seekTab(fontFile, TTFTableName.CMAP, cmapUniOffset);
  321. int cmapFormat = fontFile.readTTFUShort();
  322. /*int cmap_length =*/ fontFile.readTTFUShort(); //skip cmap length
  323. if (log.isDebugEnabled()) {
  324. log.debug("CMAP format: " + cmapFormat);
  325. }
  326. if (cmapFormat == 4) {
  327. fontFile.skip(2); // Skip version number
  328. int cmapSegCountX2 = fontFile.readTTFUShort();
  329. int cmapSearchRange = fontFile.readTTFUShort();
  330. int cmapEntrySelector = fontFile.readTTFUShort();
  331. int cmapRangeShift = fontFile.readTTFUShort();
  332. if (log.isDebugEnabled()) {
  333. log.debug("segCountX2 : " + cmapSegCountX2);
  334. log.debug("searchRange : " + cmapSearchRange);
  335. log.debug("entrySelector: " + cmapEntrySelector);
  336. log.debug("rangeShift : " + cmapRangeShift);
  337. }
  338. int[] cmapEndCounts = new int[cmapSegCountX2 / 2];
  339. int[] cmapStartCounts = new int[cmapSegCountX2 / 2];
  340. int[] cmapDeltas = new int[cmapSegCountX2 / 2];
  341. int[] cmapRangeOffsets = new int[cmapSegCountX2 / 2];
  342. for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
  343. cmapEndCounts[i] = fontFile.readTTFUShort();
  344. }
  345. fontFile.skip(2); // Skip reservedPad
  346. for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
  347. cmapStartCounts[i] = fontFile.readTTFUShort();
  348. }
  349. for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
  350. cmapDeltas[i] = fontFile.readTTFShort();
  351. }
  352. //int startRangeOffset = in.getCurrentPos();
  353. for (int i = 0; i < (cmapSegCountX2 / 2); i++) {
  354. cmapRangeOffsets[i] = fontFile.readTTFUShort();
  355. }
  356. int glyphIdArrayOffset = fontFile.getCurrentPos();
  357. BitSet eightBitGlyphs = new BitSet(256);
  358. // Insert the unicode id for the glyphs in mtxTab
  359. // and fill in the cmaps ArrayList
  360. for (int i = 0; i < cmapStartCounts.length; i++) {
  361. if (log.isTraceEnabled()) {
  362. log.trace(i + ": " + cmapStartCounts[i]
  363. + " - " + cmapEndCounts[i]);
  364. }
  365. if (log.isDebugEnabled()) {
  366. if (isInPrivateUseArea(cmapStartCounts[i], cmapEndCounts[i])) {
  367. log.debug("Font contains glyphs in the Unicode private use area: "
  368. + Integer.toHexString(cmapStartCounts[i]) + " - "
  369. + Integer.toHexString(cmapEndCounts[i]));
  370. }
  371. }
  372. for (int j = cmapStartCounts[i]; j <= cmapEndCounts[i]; j++) {
  373. // Update lastChar
  374. if (j < 256 && j > lastChar) {
  375. lastChar = (short)j;
  376. }
  377. if (j < 256) {
  378. eightBitGlyphs.set(j);
  379. }
  380. if (mtxPtr < mtxTab.length) {
  381. int glyphIdx;
  382. // the last character 65535 = .notdef
  383. // may have a range offset
  384. if (cmapRangeOffsets[i] != 0 && j != 65535) {
  385. int glyphOffset = glyphIdArrayOffset
  386. + ((cmapRangeOffsets[i] / 2)
  387. + (j - cmapStartCounts[i])
  388. + (i)
  389. - cmapSegCountX2 / 2) * 2;
  390. fontFile.seekSet(glyphOffset);
  391. glyphIdx = (fontFile.readTTFUShort() + cmapDeltas[i])
  392. & 0xffff;
  393. unicodeMapping.add(new UnicodeMapping(glyphIdx, j));
  394. mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
  395. if (encodingID == 0 && j >= 0xF020 && j <= 0xF0FF) {
  396. //Experimental: Mapping 0xF020-0xF0FF to 0x0020-0x00FF
  397. //Tested with Wingdings and Symbol TTF fonts which map their
  398. //glyphs in the region 0xF020-0xF0FF.
  399. int mapped = j - 0xF000;
  400. if (!eightBitGlyphs.get(mapped)) {
  401. //Only map if Unicode code point hasn't been mapped before
  402. unicodeMapping.add(new UnicodeMapping(glyphIdx, mapped));
  403. mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(mapped));
  404. }
  405. }
  406. // Also add winAnsiWidth
  407. List<Integer> v = ansiIndex.get(new Integer(j));
  408. if (v != null) {
  409. for (Integer aIdx : v) {
  410. ansiWidth[aIdx.intValue()]
  411. = mtxTab[glyphIdx].getWx();
  412. if (log.isTraceEnabled()) {
  413. log.trace("Added width "
  414. + mtxTab[glyphIdx].getWx()
  415. + " uni: " + j
  416. + " ansi: " + aIdx.intValue());
  417. }
  418. }
  419. }
  420. if (log.isTraceEnabled()) {
  421. log.trace("Idx: "
  422. + glyphIdx
  423. + " Delta: " + cmapDeltas[i]
  424. + " Unicode: " + j
  425. + " name: " + mtxTab[glyphIdx].getName());
  426. }
  427. } else {
  428. glyphIdx = (j + cmapDeltas[i]) & 0xffff;
  429. if (glyphIdx < mtxTab.length) {
  430. mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
  431. } else {
  432. log.debug("Glyph " + glyphIdx
  433. + " out of range: "
  434. + mtxTab.length);
  435. }
  436. unicodeMapping.add(new UnicodeMapping(glyphIdx, j));
  437. if (glyphIdx < mtxTab.length) {
  438. mtxTab[glyphIdx].getUnicodeIndex().add(new Integer(j));
  439. } else {
  440. log.debug("Glyph " + glyphIdx
  441. + " out of range: "
  442. + mtxTab.length);
  443. }
  444. // Also add winAnsiWidth
  445. List<Integer> v = ansiIndex.get(new Integer(j));
  446. if (v != null) {
  447. for (Integer aIdx : v) {
  448. ansiWidth[aIdx.intValue()] = mtxTab[glyphIdx].getWx();
  449. }
  450. }
  451. //getLogger().debug("IIdx: " +
  452. // mtxPtr +
  453. // " Delta: " + cmap_deltas[i] +
  454. // " Unicode: " + j +
  455. // " name: " +
  456. // mtxTab[(j+cmap_deltas[i]) & 0xffff].name);
  457. }
  458. if (glyphIdx < mtxTab.length) {
  459. if (mtxTab[glyphIdx].getUnicodeIndex().size() < 2) {
  460. mtxPtr++;
  461. }
  462. }
  463. }
  464. }
  465. }
  466. } else {
  467. log.error("Cmap format not supported: " + cmapFormat);
  468. return false;
  469. }
  470. return true;
  471. }
  472. private boolean isInPrivateUseArea(int start, int end) {
  473. return (isInPrivateUseArea(start) || isInPrivateUseArea(end));
  474. }
  475. private boolean isInPrivateUseArea(int unicode) {
  476. return (unicode >= 0xE000 && unicode <= 0xF8FF);
  477. }
  478. /**
  479. * Print first char/last char
  480. */
  481. private void printMaxMin() {
  482. int min = 255;
  483. int max = 0;
  484. for (int i = 0; i < mtxTab.length; i++) {
  485. if (mtxTab[i].getIndex() < min) {
  486. min = mtxTab[i].getIndex();
  487. }
  488. if (mtxTab[i].getIndex() > max) {
  489. max = mtxTab[i].getIndex();
  490. }
  491. }
  492. log.info("Min: " + min);
  493. log.info("Max: " + max);
  494. }
  495. /**
  496. * Reads the font using a FontFileReader.
  497. *
  498. * @param in The FontFileReader to use
  499. * @throws IOException In case of an I/O problem
  500. */
  501. public void readFont(FontFileReader in) throws IOException {
  502. readFont(in, (String)null);
  503. }
  504. /**
  505. * initialize the ansiWidths array (for winAnsiEncoding)
  506. * and fill with the missingwidth
  507. */
  508. private void initAnsiWidths() {
  509. ansiWidth = new int[256];
  510. for (int i = 0; i < 256; i++) {
  511. ansiWidth[i] = mtxTab[0].getWx();
  512. }
  513. // Create an index hash to the ansiWidth
  514. // Can't just index the winAnsiEncoding when inserting widths
  515. // same char (eg bullet) is repeated more than one place
  516. ansiIndex = new HashMap<Integer, List<Integer>>();
  517. for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) {
  518. Integer ansi = new Integer(i);
  519. Integer uni = new Integer(Glyphs.WINANSI_ENCODING[i]);
  520. List<Integer> v = ansiIndex.get(uni);
  521. if (v == null) {
  522. v = new ArrayList<Integer>();
  523. ansiIndex.put(uni, v);
  524. }
  525. v.add(ansi);
  526. }
  527. }
  528. /**
  529. * Read the font data.
  530. * If the fontfile is a TrueType Collection (.ttc file)
  531. * the name of the font to read data for must be supplied,
  532. * else the name is ignored.
  533. *
  534. * @param in The FontFileReader to use
  535. * @param name The name of the font
  536. * @return boolean Returns true if the font is valid
  537. * @throws IOException In case of an I/O problem
  538. */
  539. public boolean readFont(FontFileReader in, String name) throws IOException {
  540. fontFile = in;
  541. /*
  542. * Check if TrueType collection, and that the name
  543. * exists in the collection
  544. */
  545. if (!checkTTC(name)) {
  546. if (name == null) {
  547. throw new IllegalArgumentException(
  548. "For TrueType collection you must specify which font "
  549. + "to select (-ttcname)");
  550. } else {
  551. throw new IOException(
  552. "Name does not exist in the TrueType collection: " + name);
  553. }
  554. }
  555. readDirTabs();
  556. readFontHeader();
  557. getNumGlyphs();
  558. if (log.isDebugEnabled()) {
  559. log.debug("Number of glyphs in font: " + numberOfGlyphs);
  560. }
  561. readHorizontalHeader();
  562. readHorizontalMetrics();
  563. initAnsiWidths();
  564. readPostScript();
  565. readOS2();
  566. determineAscDesc();
  567. if (!isCFF) {
  568. readIndexToLocation();
  569. readGlyf();
  570. }
  571. readName();
  572. boolean pcltFound = readPCLT();
  573. // Read cmap table and fill in ansiwidths
  574. boolean valid = readCMAP();
  575. if (!valid) {
  576. return false;
  577. }
  578. // Create cmaps for bfentries
  579. createCMaps();
  580. // print_max_min();
  581. readKerning();
  582. guessVerticalMetricsFromGlyphBBox();
  583. return true;
  584. }
  585. /**
  586. * Reads a font.
  587. *
  588. * @param in FontFileReader to read from
  589. * @param name Name to be checked for in the font file
  590. * @param glyphs Map of glyphs (glyphs has old index as (Integer) key and
  591. * new index as (Integer) value)
  592. * @throws IOException in case of an I/O problem
  593. */
  594. public void readFont(FontFileReader in, String name,
  595. Map<Integer, Integer> glyphs) throws IOException {
  596. readFont(in, name);
  597. }
  598. private void createCMaps() {
  599. cmaps = new ArrayList<BFEntry>();
  600. int unicodeStart;
  601. int glyphStart;
  602. int unicodeEnd;
  603. Iterator<UnicodeMapping> e = unicodeMapping.listIterator();
  604. UnicodeMapping um = e.next();
  605. UnicodeMapping lastMapping = um;
  606. unicodeStart = um.getUnicodeIndex();
  607. glyphStart = um.getGlyphIndex();
  608. while (e.hasNext()) {
  609. um = e.next();
  610. if (((lastMapping.getUnicodeIndex() + 1) != um.getUnicodeIndex())
  611. || ((lastMapping.getGlyphIndex() + 1) != um.getGlyphIndex())) {
  612. unicodeEnd = lastMapping.getUnicodeIndex();
  613. cmaps.add(new BFEntry(unicodeStart, unicodeEnd, glyphStart));
  614. unicodeStart = um.getUnicodeIndex();
  615. glyphStart = um.getGlyphIndex();
  616. }
  617. lastMapping = um;
  618. }
  619. unicodeEnd = lastMapping.getUnicodeIndex();
  620. cmaps.add(new BFEntry(unicodeStart, unicodeEnd, glyphStart));
  621. }
  622. /**
  623. * Returns the PostScript name of the font.
  624. * @return String The PostScript name
  625. */
  626. public String getPostScriptName() {
  627. if (postScriptName.length() == 0) {
  628. return FontUtil.stripWhiteSpace(getFullName());
  629. } else {
  630. return postScriptName;
  631. }
  632. }
  633. PostScriptVersion getPostScriptVersion() {
  634. return postScriptVersion;
  635. }
  636. /**
  637. * Returns the font family names of the font.
  638. * @return Set The family names (a Set of Strings)
  639. */
  640. public Set<String> getFamilyNames() {
  641. return familyNames;
  642. }
  643. /**
  644. * Returns the font sub family name of the font.
  645. * @return String The sub family name
  646. */
  647. public String getSubFamilyName() {
  648. return subFamilyName;
  649. }
  650. /**
  651. * Returns the full name of the font.
  652. * @return String The full name
  653. */
  654. public String getFullName() {
  655. return fullName;
  656. }
  657. /**
  658. * Returns the name of the character set used.
  659. * @return String The caracter set
  660. */
  661. public String getCharSetName() {
  662. return encoding;
  663. }
  664. /**
  665. * Returns the CapHeight attribute of the font.
  666. * @return int The CapHeight
  667. */
  668. public int getCapHeight() {
  669. return convertTTFUnit2PDFUnit(capHeight);
  670. }
  671. /**
  672. * Returns the XHeight attribute of the font.
  673. * @return int The XHeight
  674. */
  675. public int getXHeight() {
  676. return convertTTFUnit2PDFUnit(xHeight);
  677. }
  678. /**
  679. * Returns the number of bytes necessary to pad the currentPosition so that a table begins
  680. * on a 4-byte boundary.
  681. * @param currentPosition the position to pad.
  682. * @return int the number of bytes to pad.
  683. */
  684. protected int getPadSize(int currentPosition) {
  685. int padSize = 4 - (currentPosition % 4);
  686. return padSize < 4 ? padSize : 0;
  687. }
  688. /**
  689. * Returns the Flags attribute of the font.
  690. * @return int The Flags
  691. */
  692. public int getFlags() {
  693. int flags = 32; // Use Adobe Standard charset
  694. if (italicAngle != 0) {
  695. flags |= 64;
  696. }
  697. if (isFixedPitch != 0) {
  698. flags |= 2;
  699. }
  700. if (hasSerifs) {
  701. flags |= 1;
  702. }
  703. return flags;
  704. }
  705. /**
  706. * Returns the weight class of this font. Valid values are 100, 200....,800, 900.
  707. * @return the weight class value (or 0 if there was no OS/2 table in the font)
  708. */
  709. public int getWeightClass() {
  710. return this.usWeightClass;
  711. }
  712. /**
  713. * Returns the StemV attribute of the font.
  714. * @return String The StemV
  715. */
  716. public String getStemV() {
  717. return "0";
  718. }
  719. /**
  720. * Returns the ItalicAngle attribute of the font.
  721. * @return String The ItalicAngle
  722. */
  723. public String getItalicAngle() {
  724. String ia = Short.toString((short)(italicAngle / 0x10000));
  725. // This is the correct italic angle, however only int italic
  726. // angles are supported at the moment so this is commented out.
  727. /*
  728. * if ((italicAngle % 0x10000) > 0 )
  729. * ia=ia+(comma+Short.toString((short)((short)((italicAngle % 0x10000)*1000)/0x10000)));
  730. */
  731. return ia;
  732. }
  733. /**
  734. * @return int[] The font bbox
  735. */
  736. public int[] getFontBBox() {
  737. final int[] fbb = new int[4];
  738. fbb[0] = convertTTFUnit2PDFUnit(fontBBox1);
  739. fbb[1] = convertTTFUnit2PDFUnit(fontBBox2);
  740. fbb[2] = convertTTFUnit2PDFUnit(fontBBox3);
  741. fbb[3] = convertTTFUnit2PDFUnit(fontBBox4);
  742. return fbb;
  743. }
  744. /**
  745. * Returns the LowerCaseAscent attribute of the font.
  746. * @return int The LowerCaseAscent
  747. */
  748. public int getLowerCaseAscent() {
  749. return convertTTFUnit2PDFUnit(ascender);
  750. }
  751. /**
  752. * Returns the LowerCaseDescent attribute of the font.
  753. * @return int The LowerCaseDescent
  754. */
  755. public int getLowerCaseDescent() {
  756. return convertTTFUnit2PDFUnit(descender);
  757. }
  758. /**
  759. * Returns the index of the last character, but this is for WinAnsiEncoding
  760. * only, so the last char is < 256.
  761. * @return short Index of the last character (<256)
  762. */
  763. public short getLastChar() {
  764. return lastChar;
  765. }
  766. /**
  767. * Returns the index of the first character.
  768. * @return short Index of the first character
  769. */
  770. public short getFirstChar() {
  771. return firstChar;
  772. }
  773. /**
  774. * Returns an array of character widths.
  775. * @return int[] The character widths
  776. */
  777. public int[] getWidths() {
  778. int[] wx = new int[mtxTab.length];
  779. for (int i = 0; i < wx.length; i++) {
  780. wx[i] = convertTTFUnit2PDFUnit(mtxTab[i].getWx());
  781. }
  782. return wx;
  783. }
  784. /**
  785. * Returns the width of a given character.
  786. * @param idx Index of the character
  787. * @return int Standard width
  788. */
  789. public int getCharWidth(int idx) {
  790. return convertTTFUnit2PDFUnit(ansiWidth[idx]);
  791. }
  792. /**
  793. * Returns the kerning table.
  794. * @return Map The kerning table
  795. */
  796. public Map<Integer, Map<Integer, Integer>> getKerning() {
  797. return kerningTab;
  798. }
  799. /**
  800. * Returns the ANSI kerning table.
  801. * @return Map The ANSI kerning table
  802. */
  803. public Map<Integer, Map<Integer, Integer>> getAnsiKerning() {
  804. return ansiKerningTab;
  805. }
  806. /**
  807. * Indicates if the font may be embedded.
  808. * @return boolean True if it may be embedded
  809. */
  810. public boolean isEmbeddable() {
  811. return isEmbeddable;
  812. }
  813. /**
  814. * Indicates whether or not the font is an OpenType
  815. * CFF font (rather than a TrueType font).
  816. * @return true if the font is in OpenType CFF format.
  817. */
  818. public boolean isCFF() {
  819. return this.isCFF;
  820. }
  821. /**
  822. * Read Table Directory from the current position in the
  823. * FontFileReader and fill the global HashMap dirTabs
  824. * with the table name (String) as key and a TTFDirTabEntry
  825. * as value.
  826. * @throws IOException in case of an I/O problem
  827. */
  828. protected void readDirTabs() throws IOException {
  829. int sfntVersion = fontFile.readTTFLong(); // TTF_FIXED_SIZE (4 bytes)
  830. switch (sfntVersion) {
  831. case 0x10000:
  832. log.debug("sfnt version: OpenType 1.0");
  833. break;
  834. case 0x4F54544F: //"OTTO"
  835. this.isCFF = true;
  836. log.debug("sfnt version: OpenType with CFF data");
  837. break;
  838. case 0x74727565: //"true"
  839. log.debug("sfnt version: Apple TrueType");
  840. break;
  841. case 0x74797031: //"typ1"
  842. log.debug("sfnt version: Apple Type 1 housed in sfnt wrapper");
  843. break;
  844. default:
  845. log.debug("Unknown sfnt version: " + Integer.toHexString(sfntVersion));
  846. break;
  847. }
  848. int ntabs = fontFile.readTTFUShort();
  849. fontFile.skip(6); // 3xTTF_USHORT_SIZE
  850. dirTabs = new HashMap<TTFTableName, TTFDirTabEntry>();
  851. TTFDirTabEntry[] pd = new TTFDirTabEntry[ntabs];
  852. log.debug("Reading " + ntabs + " dir tables");
  853. for (int i = 0; i < ntabs; i++) {
  854. pd[i] = new TTFDirTabEntry();
  855. String tableName = pd[i].read(fontFile);
  856. dirTabs.put(TTFTableName.getValue(tableName), pd[i]);
  857. }
  858. dirTabs.put(TTFTableName.DIRECTORY_TABLE,
  859. new TTFDirTabEntry(0L, fontFile.getCurrentPos()));
  860. log.debug("dir tables: " + dirTabs.keySet());
  861. }
  862. /**
  863. * Read the "head" table, this reads the bounding box and
  864. * sets the upem (unitsPerEM) variable
  865. * @throws IOException in case of an I/O problem
  866. */
  867. protected void readFontHeader() throws IOException {
  868. seekTab(fontFile, TTFTableName.HEAD, 2 * 4 + 2 * 4);
  869. int flags = fontFile.readTTFUShort();
  870. if (log.isDebugEnabled()) {
  871. log.debug("flags: " + flags + " - " + Integer.toString(flags, 2));
  872. }
  873. upem = fontFile.readTTFUShort();
  874. if (log.isDebugEnabled()) {
  875. log.debug("unit per em: " + upem);
  876. }
  877. fontFile.skip(16);
  878. fontBBox1 = fontFile.readTTFShort();
  879. fontBBox2 = fontFile.readTTFShort();
  880. fontBBox3 = fontFile.readTTFShort();
  881. fontBBox4 = fontFile.readTTFShort();
  882. if (log.isDebugEnabled()) {
  883. log.debug("font bbox: xMin=" + fontBBox1
  884. + " yMin=" + fontBBox2
  885. + " xMax=" + fontBBox3
  886. + " yMax=" + fontBBox4);
  887. }
  888. fontFile.skip(2 + 2 + 2);
  889. locaFormat = fontFile.readTTFShort();
  890. }
  891. /**
  892. * Read the number of glyphs from the "maxp" table
  893. * @throws IOException in case of an I/O problem
  894. */
  895. protected void getNumGlyphs() throws IOException {
  896. seekTab(fontFile, TTFTableName.MAXP, 4);
  897. numberOfGlyphs = fontFile.readTTFUShort();
  898. }
  899. /**
  900. * Read the "hhea" table to find the ascender and descender and
  901. * size of "hmtx" table, as a fixed size font might have only
  902. * one width.
  903. * @throws IOException in case of an I/O problem
  904. */
  905. protected void readHorizontalHeader()
  906. throws IOException {
  907. seekTab(fontFile, TTFTableName.HHEA, 4);
  908. hheaAscender = fontFile.readTTFShort();
  909. hheaDescender = fontFile.readTTFShort();
  910. fontFile.skip(2 + 2 + 3 * 2 + 8 * 2);
  911. nhmtx = fontFile.readTTFUShort();
  912. if (log.isDebugEnabled()) {
  913. log.debug("hhea.Ascender: " + formatUnitsForDebug(hheaAscender));
  914. log.debug("hhea.Descender: " + formatUnitsForDebug(hheaDescender));
  915. log.debug("Number of horizontal metrics: " + nhmtx);
  916. }
  917. }
  918. /**
  919. * Read "hmtx" table and put the horizontal metrics
  920. * in the mtxTab array. If the number of metrics is less
  921. * than the number of glyphs (eg fixed size fonts), extend
  922. * the mtxTab array and fill in the missing widths
  923. * @throws IOException in case of an I/O problem
  924. */
  925. protected void readHorizontalMetrics()
  926. throws IOException {
  927. seekTab(fontFile, TTFTableName.HMTX, 0);
  928. int mtxSize = Math.max(numberOfGlyphs, nhmtx);
  929. mtxTab = new TTFMtxEntry[mtxSize];
  930. if (TRACE_ENABLED) {
  931. log.debug("*** Widths array: \n");
  932. }
  933. for (int i = 0; i < mtxSize; i++) {
  934. mtxTab[i] = new TTFMtxEntry();
  935. }
  936. for (int i = 0; i < nhmtx; i++) {
  937. mtxTab[i].setWx(fontFile.readTTFUShort());
  938. mtxTab[i].setLsb(fontFile.readTTFUShort());
  939. if (TRACE_ENABLED) {
  940. if (log.isDebugEnabled()) {
  941. log.debug(" width[" + i + "] = "
  942. + convertTTFUnit2PDFUnit(mtxTab[i].getWx()) + ";");
  943. }
  944. }
  945. }
  946. if (nhmtx < mtxSize) {
  947. // Fill in the missing widths
  948. int lastWidth = mtxTab[nhmtx - 1].getWx();
  949. for (int i = nhmtx; i < mtxSize; i++) {
  950. mtxTab[i].setWx(lastWidth);
  951. mtxTab[i].setLsb(fontFile.readTTFUShort());
  952. }
  953. }
  954. }
  955. /**
  956. * Read the "post" table
  957. * containing the PostScript names of the glyphs.
  958. */
  959. private void readPostScript() throws IOException {
  960. seekTab(fontFile, TTFTableName.POST, 0);
  961. int postFormat = fontFile.readTTFLong();
  962. italicAngle = fontFile.readTTFULong();
  963. underlinePosition = fontFile.readTTFShort();
  964. underlineThickness = fontFile.readTTFShort();
  965. isFixedPitch = fontFile.readTTFULong();
  966. //Skip memory usage values
  967. fontFile.skip(4 * 4);
  968. log.debug("PostScript format: 0x" + Integer.toHexString(postFormat));
  969. switch (postFormat) {
  970. case 0x00010000:
  971. log.debug("PostScript format 1");
  972. postScriptVersion = PostScriptVersion.V1;
  973. for (int i = 0; i < MAC_GLYPH_ORDERING.length; i++) {
  974. mtxTab[i].setName(MAC_GLYPH_ORDERING[i]);
  975. }
  976. break;
  977. case 0x00020000:
  978. log.debug("PostScript format 2");
  979. postScriptVersion = PostScriptVersion.V2;
  980. int numGlyphStrings = 0;
  981. // Read Number of Glyphs
  982. int l = fontFile.readTTFUShort();
  983. // Read indexes
  984. for (int i = 0; i < l; i++) {
  985. mtxTab[i].setIndex(fontFile.readTTFUShort());
  986. if (mtxTab[i].getIndex() > 257) {
  987. //Index is not in the Macintosh standard set
  988. numGlyphStrings++;
  989. }
  990. if (log.isTraceEnabled()) {
  991. log.trace("PostScript index: " + mtxTab[i].getIndexAsString());
  992. }
  993. }
  994. // firstChar=minIndex;
  995. String[] psGlyphsBuffer = new String[numGlyphStrings];
  996. if (log.isDebugEnabled()) {
  997. log.debug("Reading " + numGlyphStrings
  998. + " glyphnames, that are not in the standard Macintosh"
  999. + " set. Total number of glyphs=" + l);
  1000. }
  1001. for (int i = 0; i < psGlyphsBuffer.length; i++) {
  1002. psGlyphsBuffer[i] = fontFile.readTTFString(fontFile.readTTFUByte());
  1003. }
  1004. //Set glyph names
  1005. for (int i = 0; i < l; i++) {
  1006. if (mtxTab[i].getIndex() < MAC_GLYPH_ORDERING.length) {
  1007. mtxTab[i].setName(MAC_GLYPH_ORDERING[mtxTab[i].getIndex()]);
  1008. } else {
  1009. if (!mtxTab[i].isIndexReserved()) {
  1010. int k = mtxTab[i].getIndex() - MAC_GLYPH_ORDERING.length;
  1011. if (log.isTraceEnabled()) {
  1012. log.trace(k + " i=" + i + " mtx=" + mtxTab.length
  1013. + " ps=" + psGlyphsBuffer.length);
  1014. }
  1015. mtxTab[i].setName(psGlyphsBuffer[k]);
  1016. }
  1017. }
  1018. }
  1019. break;
  1020. case 0x00030000:
  1021. // PostScript format 3 contains no glyph names
  1022. log.debug("PostScript format 3");
  1023. postScriptVersion = PostScriptVersion.V3;
  1024. break;
  1025. default:
  1026. log.error("Unknown PostScript format: " + postFormat);
  1027. postScriptVersion = PostScriptVersion.UNKNOWN;
  1028. }
  1029. }
  1030. /**
  1031. * Read the "OS/2" table
  1032. */
  1033. private void readOS2() throws IOException {
  1034. // Check if font is embeddable
  1035. TTFDirTabEntry os2Entry = dirTabs.get(TTFTableName.OS2);
  1036. if (os2Entry != null) {
  1037. seekTab(fontFile, TTFTableName.OS2, 0);
  1038. int version = fontFile.readTTFUShort();
  1039. if (log.isDebugEnabled()) {
  1040. log.debug("OS/2 table: version=" + version
  1041. + ", offset=" + os2Entry.getOffset() + ", len=" + os2Entry.getLength());
  1042. }
  1043. fontFile.skip(2); //xAvgCharWidth
  1044. this.usWeightClass = fontFile.readTTFUShort();
  1045. // usWidthClass
  1046. fontFile.skip(2);
  1047. int fsType = fontFile.readTTFUShort();
  1048. if (fsType == 2) {
  1049. isEmbeddable = false;
  1050. } else {
  1051. isEmbeddable = true;
  1052. }
  1053. fontFile.skip(11 * 2);
  1054. fontFile.skip(10); //panose array
  1055. fontFile.skip(4 * 4); //unicode ranges
  1056. fontFile.skip(4);
  1057. fontFile.skip(3 * 2);
  1058. int v;
  1059. os2Ascender = fontFile.readTTFShort(); //sTypoAscender
  1060. os2Descender = fontFile.readTTFShort(); //sTypoDescender
  1061. if (log.isDebugEnabled()) {
  1062. log.debug("sTypoAscender: " + os2Ascender
  1063. + " -> internal " + convertTTFUnit2PDFUnit(os2Ascender));
  1064. log.debug("sTypoDescender: " + os2Descender
  1065. + " -> internal " + convertTTFUnit2PDFUnit(os2Descender));
  1066. }
  1067. v = fontFile.readTTFShort(); //sTypoLineGap
  1068. if (log.isDebugEnabled()) {
  1069. log.debug("sTypoLineGap: " + v);
  1070. }
  1071. v = fontFile.readTTFUShort(); //usWinAscent
  1072. if (log.isDebugEnabled()) {
  1073. log.debug("usWinAscent: " + formatUnitsForDebug(v));
  1074. }
  1075. v = fontFile.readTTFUShort(); //usWinDescent
  1076. if (log.isDebugEnabled()) {
  1077. log.debug("usWinDescent: " + formatUnitsForDebug(v));
  1078. }
  1079. //version 1 OS/2 table might end here
  1080. if (os2Entry.getLength() >= 78 + (2 * 4) + (2 * 2)) {
  1081. fontFile.skip(2 * 4);
  1082. this.os2xHeight = fontFile.readTTFShort(); //sxHeight
  1083. this.os2CapHeight = fontFile.readTTFShort(); //sCapHeight
  1084. if (log.isDebugEnabled()) {
  1085. log.debug("sxHeight: " + this.os2xHeight);
  1086. log.debug("sCapHeight: " + this.os2CapHeight);
  1087. }
  1088. }
  1089. } else {
  1090. isEmbeddable = true;
  1091. }
  1092. }
  1093. /**
  1094. * Read the "loca" table.
  1095. * @throws IOException In case of a I/O problem
  1096. */
  1097. protected final void readIndexToLocation()
  1098. throws IOException {
  1099. if (!seekTab(fontFile, TTFTableName.LOCA, 0)) {
  1100. throw new IOException("'loca' table not found, happens when the font file doesn't"
  1101. + " contain TrueType outlines (trying to read an OpenType CFF font maybe?)");
  1102. }
  1103. for (int i = 0; i < numberOfGlyphs; i++) {
  1104. mtxTab[i].setOffset(locaFormat == 1 ? fontFile.readTTFULong()
  1105. : (fontFile.readTTFUShort() << 1));
  1106. }
  1107. lastLoca = (locaFormat == 1 ? fontFile.readTTFULong()
  1108. : (fontFile.readTTFUShort() << 1));
  1109. }
  1110. /**
  1111. * Read the "glyf" table to find the bounding boxes.
  1112. * @throws IOException In case of a I/O problem
  1113. */
  1114. private void readGlyf() throws IOException {
  1115. TTFDirTabEntry dirTab = dirTabs.get(TTFTableName.GLYF);
  1116. if (dirTab == null) {
  1117. throw new IOException("glyf table not found, cannot continue");
  1118. }
  1119. for (int i = 0; i < (numberOfGlyphs - 1); i++) {
  1120. if (mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) {
  1121. fontFile.seekSet(dirTab.getOffset() + mtxTab[i].getOffset());
  1122. fontFile.skip(2);
  1123. final int[] bbox = {
  1124. fontFile.readTTFShort(),
  1125. fontFile.readTTFShort(),
  1126. fontFile.readTTFShort(),
  1127. fontFile.readTTFShort()};
  1128. mtxTab[i].setBoundingBox(bbox);
  1129. } else {
  1130. mtxTab[i].setBoundingBox(mtxTab[0].getBoundingBox());
  1131. }
  1132. }
  1133. long n = (dirTabs.get(TTFTableName.GLYF)).getOffset();
  1134. for (int i = 0; i < numberOfGlyphs; i++) {
  1135. if ((i + 1) >= mtxTab.length
  1136. || mtxTab[i].getOffset() != mtxTab[i + 1].getOffset()) {
  1137. fontFile.seekSet(n + mtxTab[i].getOffset());
  1138. fontFile.skip(2);
  1139. final int[] bbox = {
  1140. fontFile.readTTFShort(),
  1141. fontFile.readTTFShort(),
  1142. fontFile.readTTFShort(),
  1143. fontFile.readTTFShort()};
  1144. mtxTab[i].setBoundingBox(bbox);
  1145. } else {
  1146. /**@todo Verify that this is correct, looks like a copy/paste bug (jm)*/
  1147. final int bbox0 = mtxTab[0].getBoundingBox()[0];
  1148. final int[] bbox = {bbox0, bbox0, bbox0, bbox0};
  1149. mtxTab[i].setBoundingBox(bbox);
  1150. /* Original code
  1151. mtxTab[i].bbox[0] = mtxTab[0].bbox[0];
  1152. mtxTab[i].bbox[1] = mtxTab[0].bbox[0];
  1153. mtxTab[i].bbox[2] = mtxTab[0].bbox[0];
  1154. mtxTab[i].bbox[3] = mtxTab[0].bbox[0]; */
  1155. }
  1156. if (log.isTraceEnabled()) {
  1157. log.trace(mtxTab[i].toString(this));
  1158. }
  1159. }
  1160. }
  1161. /**
  1162. * Read the "name" table.
  1163. * @throws IOException In case of a I/O problem
  1164. */
  1165. private void readName() throws IOException {
  1166. seekTab(fontFile, TTFTableName.NAME, 2);
  1167. int i = fontFile.getCurrentPos();
  1168. int n = fontFile.readTTFUShort();
  1169. int j = fontFile.readTTFUShort() + i - 2;
  1170. i += 2 * 2;
  1171. while (n-- > 0) {
  1172. // getLogger().debug("Iteration: " + n);
  1173. fontFile.seekSet(i);
  1174. final int platformID = fontFile.readTTFUShort();
  1175. final int encodingID = fontFile.readTTFUShort();
  1176. final int languageID = fontFile.readTTFUShort();
  1177. int k = fontFile.readTTFUShort();
  1178. int l = fontFile.readTTFUShort();
  1179. if (((platformID == 1 || platformID == 3)
  1180. && (encodingID == 0 || encodingID == 1))) {
  1181. fontFile.seekSet(j + fontFile.readTTFUShort());
  1182. String txt;
  1183. if (platformID == 3) {
  1184. txt = fontFile.readTTFString(l, encodingID);
  1185. } else {
  1186. txt = fontFile.readTTFString(l);
  1187. }
  1188. if (log.isDebugEnabled()) {
  1189. log.debug(platformID + " "
  1190. + encodingID + " "
  1191. + languageID + " "
  1192. + k + " " + txt);
  1193. }
  1194. switch (k) {
  1195. case 0:
  1196. if (notice.length() == 0) {
  1197. notice = txt;
  1198. }
  1199. break;
  1200. case 1: //Font Family Name
  1201. case 16: //Preferred Family
  1202. familyNames.add(txt);
  1203. break;
  1204. case 2:
  1205. if (subFamilyName.length() == 0) {
  1206. subFamilyName = txt;
  1207. }
  1208. break;
  1209. case 4:
  1210. if (fullName.length() == 0 || (platformID == 3 && languageID == 1033)) {
  1211. fullName = txt;
  1212. }
  1213. break;
  1214. case 6:
  1215. if (postScriptName.length() == 0) {
  1216. postScriptName = txt;
  1217. }
  1218. break;
  1219. default:
  1220. break;
  1221. }
  1222. }
  1223. i += 6 * 2;
  1224. }
  1225. }
  1226. /**
  1227. * Read the "PCLT" table to find xHeight and capHeight.
  1228. * @throws IOException In case of a I/O problem
  1229. */
  1230. private boolean readPCLT() throws IOException {
  1231. TTFDirTabEntry dirTab = dirTabs.get(TTFTableName.PCLT);
  1232. if (dirTab != null) {
  1233. fontFile.seekSet(dirTab.getOffset() + 4 + 4 + 2);
  1234. xHeight = fontFile.readTTFUShort();
  1235. log.debug("xHeight from PCLT: " + formatUnitsForDebug(xHeight));
  1236. fontFile.skip(2 * 2);
  1237. capHeight = fontFile.readTTFUShort();
  1238. log.debug("capHeight from PCLT: " + formatUnitsForDebug(capHeight));
  1239. fontFile.skip(2 + 16 + 8 + 6 + 1 + 1);
  1240. int serifStyle = fontFile.readTTFUByte();
  1241. serifStyle = serifStyle >> 6;
  1242. serifStyle = serifStyle & 3;
  1243. if (serifStyle == 1) {
  1244. hasSerifs = false;
  1245. } else {
  1246. hasSerifs = true;
  1247. }
  1248. return true;
  1249. } else {
  1250. return false;
  1251. }
  1252. }
  1253. /**
  1254. * Determines the right source for the ascender and descender values. The problem here is
  1255. * that the interpretation of these values is not the same for every font. There doesn't seem
  1256. * to be a uniform definition of an ascender and a descender. In some fonts
  1257. * the hhea values are defined after the Apple interpretation, but not in every font. The
  1258. * same problem is in the OS/2 table. FOP needs the ascender and descender to determine the
  1259. * baseline so we need values which add up more or less to the "em box". However, due to
  1260. * accent modifiers a character can grow beyond the em box.
  1261. */
  1262. private void determineAscDesc() {
  1263. int hheaBoxHeight = hheaAscender - hheaDescender;
  1264. int os2BoxHeight = os2Ascender - os2Descender;
  1265. if (os2Ascender > 0 && os2BoxHeight <= upem) {
  1266. ascender = os2Ascender;
  1267. descender = os2Descender;
  1268. } else if (hheaAscender > 0 && hheaBoxHeight <= upem) {
  1269. ascender = hheaAscender;
  1270. descender = hheaDescender;
  1271. } else {
  1272. if (os2Ascender > 0) {
  1273. //Fall back to info from OS/2 if possible
  1274. ascender = os2Ascender;
  1275. descender = os2Descender;
  1276. } else {
  1277. ascender = hheaAscender;
  1278. descender = hheaDescender;
  1279. }
  1280. }
  1281. if (log.isDebugEnabled()) {
  1282. log.debug("Font box height: " + (ascender - descender));
  1283. if (ascender - descender > upem) {
  1284. log.debug("Ascender and descender together are larger than the em box.");
  1285. }
  1286. }
  1287. }
  1288. private void guessVerticalMetricsFromGlyphBBox() {
  1289. // Approximate capHeight from height of "H"
  1290. // It's most unlikely that a font misses the PCLT table
  1291. // This also assumes that postscriptnames exists ("H")
  1292. // Should look it up in the cmap (that wouldn't help
  1293. // for charsets without H anyway...)
  1294. // Same for xHeight with the letter "x"
  1295. int localCapHeight = 0;
  1296. int localXHeight = 0;
  1297. int localAscender = 0;
  1298. int localDescender = 0;
  1299. for (int i = 0; i < mtxTab.length; i++) {
  1300. if ("H".equals(mtxTab[i].getName())) {
  1301. localCapHeight = mtxTab[i].getBoundingBox()[3];
  1302. } else if ("x".equals(mtxTab[i].getName())) {
  1303. localXHeight = mtxTab[i].getBoundingBox()[3];
  1304. } else if ("d".equals(mtxTab[i].getName())) {
  1305. localAscender = mtxTab[i].getBoundingBox()[3];
  1306. } else if ("p".equals(mtxTab[i].getName())) {
  1307. localDescender = mtxTab[i].getBoundingBox()[1];
  1308. } else {
  1309. // OpenType Fonts with a version 3.0 "post" table don't have glyph names.
  1310. // Use Unicode indices instead.
  1311. List unicodeIndex = mtxTab[i].getUnicodeIndex();
  1312. if (unicodeIndex.size() > 0) {
  1313. //Only the first index is used
  1314. char ch = (char)((Integer)unicodeIndex.get(0)).intValue();
  1315. if (ch == 'H') {
  1316. localCapHeight = mtxTab[i].getBoundingBox()[3];
  1317. } else if (ch == 'x') {
  1318. localXHeight = mtxTab[i].getBoundingBox()[3];
  1319. } else if (ch == 'd') {
  1320. localAscender = mtxTab[i].getBoundingBox()[3];
  1321. } else if (ch == 'p') {
  1322. localDescender = mtxTab[i].getBoundingBox()[1];
  1323. }
  1324. }
  1325. }
  1326. }
  1327. if (log.isDebugEnabled()) {
  1328. log.debug("Ascender from glyph 'd': " + formatUnitsForDebug(localAscender));
  1329. log.debug("Descender from glyph 'p': " + formatUnitsForDebug(localDescender));
  1330. }
  1331. if (ascender - descender > upem) {
  1332. log.debug("Replacing specified ascender/descender with derived values to get values"
  1333. + " which fit in the em box.");
  1334. ascender = localAscender;
  1335. descender = localDescender;
  1336. }
  1337. if (log.isDebugEnabled()) {
  1338. log.debug("xHeight from glyph 'x': " + formatUnitsForDebug(localXHeight));
  1339. log.debug("CapHeight from glyph 'H': " + formatUnitsForDebug(localCapHeight));
  1340. }
  1341. if (capHeight == 0) {
  1342. capHeight = localCapHeight;
  1343. if (capHeight == 0) {
  1344. capHeight = os2CapHeight;
  1345. }
  1346. if (capHeight == 0) {
  1347. log.warn("capHeight value could not be determined."
  1348. + " The font may not work as expected.");
  1349. }
  1350. }
  1351. if (xHeight == 0) {
  1352. xHeight = localXHeight;
  1353. if (xHeight == 0) {
  1354. xHeight = os2xHeight;
  1355. }
  1356. if (xHeight == 0) {
  1357. log.warn("xHeight value could not be determined."
  1358. + " The font may not work as expected.");
  1359. }
  1360. }
  1361. }
  1362. /**
  1363. * Read the kerning table, create a table for both CIDs and
  1364. * winAnsiEncoding.
  1365. * @throws IOException In case of a I/O problem
  1366. */
  1367. private void readKerning() throws IOException {
  1368. // Read kerning
  1369. kerningTab = new HashMap<Integer, Map<Integer, Integer>>();
  1370. ansiKerningTab = new HashMap<Integer, Map<Integer, Integer>>();
  1371. TTFDirTabEntry dirTab = dirTabs.get(TTFTableName.KERN);
  1372. if (dirTab != null) {
  1373. seekTab(fontFile, TTFTableName.KERN, 2);
  1374. for (int n = fontFile.readTTFUShort(); n > 0; n--) {
  1375. fontFile.skip(2 * 2);
  1376. int k = fontFile.readTTFUShort();
  1377. if (!((k & 1) != 0) || (k & 2) != 0 || (k & 4) != 0) {
  1378. return;
  1379. }
  1380. if ((k >> 8) != 0) {
  1381. continue;
  1382. }
  1383. k = fontFile.readTTFUShort();
  1384. fontFile.skip(3 * 2);
  1385. while (k-- > 0) {
  1386. int i = fontFile.readTTFUShort();
  1387. int j = fontFile.readTTFUShort();
  1388. int kpx = fontFile.readTTFShort();
  1389. if (kpx != 0) {
  1390. // CID kerning table entry, using unicode indexes
  1391. final Integer iObj = glyphToUnicode(i);
  1392. final Integer u2 = glyphToUnicode(j);
  1393. if (iObj == null) {
  1394. // happens for many fonts (Ubuntu font set),
  1395. // stray entries in the kerning table??
  1396. log.debug("Ignoring kerning pair because no Unicode index was"
  1397. + " found for the first glyph " + i);
  1398. } else if (u2 == null) {
  1399. log.debug("Ignoring kerning pair because Unicode index was"
  1400. + " found for the second glyph " + i);
  1401. } else {
  1402. Map<Integer, Integer> adjTab = kerningTab.get(iObj);
  1403. if (adjTab == null) {
  1404. adjTab = new HashMap<Integer, Integer>();
  1405. }
  1406. adjTab.put(u2, new Integer(convertTTFUnit2PDFUnit(kpx)));
  1407. kerningTab.put(iObj, adjTab);
  1408. }
  1409. }
  1410. }
  1411. }
  1412. // Create winAnsiEncoded kerning table from kerningTab
  1413. // (could probably be simplified, for now we remap back to CID indexes and
  1414. // then to winAnsi)
  1415. for (Integer unicodeKey1 : kerningTab.keySet()) {
  1416. Integer cidKey1 = unicodeToGlyph(unicodeKey1.intValue());
  1417. Map<Integer, Integer> akpx = new HashMap<Integer, Integer>();
  1418. Map<Integer, Integer> ckpx = kerningTab.get(unicodeKey1);
  1419. for (Integer unicodeKey2 : ckpx.keySet()) {
  1420. Integer cidKey2 = unicodeToGlyph(unicodeKey2.intValue());
  1421. Integer kern = (Integer)ckpx.get(unicodeKey2);
  1422. Iterator uniMap = mtxTab[cidKey2.intValue()].getUnicodeIndex().listIterator();
  1423. while (uniMap.hasNext()) {
  1424. Integer unicodeKey = (Integer)uniMap.next();
  1425. Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue());
  1426. for (int u = 0; u < ansiKeys.length; u++) {
  1427. akpx.put(ansiKeys[u], kern);
  1428. }
  1429. }
  1430. }
  1431. if (akpx.size() > 0) {
  1432. Iterator uniMap = mtxTab[cidKey1.intValue()].getUnicodeIndex().listIterator();
  1433. while (uniMap.hasNext()) {
  1434. Integer unicodeKey = (Integer)uniMap.next();
  1435. Integer[] ansiKeys = unicodeToWinAnsi(unicodeKey.intValue());
  1436. for (int u = 0; u < ansiKeys.length; u++) {
  1437. ansiKerningTab.put(ansiKeys[u], akpx);
  1438. }
  1439. }
  1440. }
  1441. }
  1442. }
  1443. }
  1444. /**
  1445. * Streams a font.
  1446. * @param ttfOut The interface for streaming True Type tables.
  1447. * @exception IOException file write error
  1448. */
  1449. public void stream(TTFOutputStream ttfOut) throws IOException {
  1450. SortedSet<Map.Entry<TTFTableName, TTFDirTabEntry>> sortedDirTabs = sortDirTabMap(dirTabs);
  1451. byte[] file = fontFile.getAllBytes();
  1452. TTFTableOutputStream tableOut = ttfOut.getTableOutputStream();
  1453. TTFGlyphOutputStream glyphOut = ttfOut.getGlyphOutputStream();
  1454. ttfOut.startFontStream();
  1455. for (Map.Entry<TTFTableName, TTFDirTabEntry> entry : sortedDirTabs) {
  1456. int offset = (int) entry.getValue().getOffset();
  1457. int paddedLength = (int) entry.getValue().getLength();
  1458. paddedLength += getPadSize(offset + paddedLength);
  1459. if (entry.getKey().equals(TTFTableName.GLYF)) {
  1460. streamGlyf(glyphOut, file, offset, paddedLength);
  1461. } else {
  1462. tableOut.streamTable(file, offset, paddedLength);
  1463. }
  1464. }
  1465. ttfOut.endFontStream();
  1466. }
  1467. private void streamGlyf(TTFGlyphOutputStream glyphOut, byte[] fontFile, int tableOffset,
  1468. int tableLength) throws IOException {
  1469. //Stream all but the last glyph
  1470. int glyphStart = 0;
  1471. int glyphEnd = 0;
  1472. glyphOut.startGlyphStream();
  1473. for (int i = 0; i < mtxTab.length - 1; i++) {
  1474. glyphStart = (int) mtxTab[i].getOffset() + tableOffset;
  1475. glyphEnd = (int) mtxTab[i + 1].getOffset() + tableOffset;
  1476. glyphOut.streamGlyph(fontFile, glyphStart, glyphEnd - glyphStart);
  1477. }
  1478. glyphOut.streamGlyph(fontFile, glyphEnd, (tableOffset + tableLength) - glyphEnd);
  1479. glyphOut.endGlyphStream();
  1480. }
  1481. /**
  1482. * This returns the order in which the tables in a truetype font should be written to file.
  1483. * @param directoryTabs the map that is to be sorted.
  1484. * @return TTFTablesNames[] an array of table names sorted in the order they should appear in
  1485. * the TTF file.
  1486. */
  1487. SortedSet<Map.Entry<TTFTableName, TTFDirTabEntry>>
  1488. sortDirTabMap(Map<TTFTableName, TTFDirTabEntry> directoryTabs) {
  1489. SortedSet<Map.Entry<TTFTableName, TTFDirTabEntry>> sortedSet
  1490. = new TreeSet<Map.Entry<TTFTableName, TTFDirTabEntry>>(
  1491. new Comparator<Map.Entry<TTFTableName, TTFDirTabEntry>>() {
  1492. public int compare(Entry<TTFTableName, TTFDirTabEntry> o1,
  1493. Entry<TTFTableName, TTFDirTabEntry> o2) {
  1494. return (int) (o1.getValue().getOffset() - o2.getValue().getOffset());
  1495. }
  1496. });
  1497. sortedSet.addAll(directoryTabs.entrySet());
  1498. return sortedSet;
  1499. }
  1500. /**
  1501. * Return a List with TTFCmapEntry.
  1502. * @return A list of TTFCmapEntry objects
  1503. */
  1504. public List<BFEntry> getCMaps() {
  1505. return cmaps;
  1506. }
  1507. /**
  1508. * Check if this is a TrueType collection and that the given
  1509. * name exists in the collection.
  1510. * If it does, set offset in fontfile to the beginning of
  1511. * the Table Directory for that font.
  1512. * @param name The name to check
  1513. * @return True if not collection or font name present, false otherwise
  1514. * @throws IOException In case of an I/O problem
  1515. */
  1516. protected final boolean checkTTC(String name) throws IOException {
  1517. String tag = fontFile.readTTFString(4);
  1518. if ("ttcf".equals(tag)) {
  1519. // This is a TrueType Collection
  1520. fontFile.skip(4);
  1521. // Read directory offsets
  1522. int numDirectories = (int)fontFile.readTTFULong();
  1523. // int numDirectories=in.readTTFUShort();
  1524. long[] dirOffsets = new long[numDirectories];
  1525. for (int i = 0; i < numDirectories; i++) {
  1526. dirOffsets[i] = fontFile.readTTFULong();
  1527. }
  1528. log.info("This is a TrueType collection file with "
  1529. + numDirectories + " fonts");
  1530. log.info("Containing the following fonts: ");
  1531. // Read all the directories and name tables to check
  1532. // If the font exists - this is a bit ugly, but...
  1533. boolean found = false;
  1534. // Iterate through all name tables even if font
  1535. // Is found, just to show all the names
  1536. long dirTabOffset = 0;
  1537. for (int i = 0; (i < numDirectories); i++) {
  1538. fontFile.seekSet(dirOffsets[i]);
  1539. readDirTabs();
  1540. readName();
  1541. if (fullName.equals(name)) {
  1542. found = true;
  1543. dirTabOffset = dirOffsets[i];
  1544. log.info(fullName + " <-- selected");
  1545. } else {
  1546. log.info(fullName);
  1547. }
  1548. // Reset names
  1549. notice = "";
  1550. fullName = "";
  1551. familyNames.clear();
  1552. postScriptName = "";
  1553. subFamilyName = "";
  1554. }
  1555. fontFile.seekSet(dirTabOffset);
  1556. return found;
  1557. } else {
  1558. fontFile.seekSet(0);
  1559. return true;
  1560. }
  1561. }
  1562. /**
  1563. * Return TTC font names
  1564. * @param in FontFileReader to read from
  1565. * @return True if not collection or font name present, false otherwise
  1566. * @throws IOException In case of an I/O problem
  1567. */
  1568. public final List<String> getTTCnames(FontFileReader in) throws IOException {
  1569. List<String> fontNames = new ArrayList<String>();
  1570. String tag = in.readTTFString(4);
  1571. if ("ttcf".equals(tag)) {
  1572. // This is a TrueType Collection
  1573. in.skip(4);
  1574. // Read directory offsets
  1575. int numDirectories = (int)in.readTTFULong();
  1576. long[] dirOffsets = new long[numDirectories];
  1577. for (int i = 0; i < numDirectories; i++) {
  1578. dirOffsets[i] = in.readTTFULong();
  1579. }
  1580. if (log.isDebugEnabled()) {
  1581. log.debug("This is a TrueType collection file with "
  1582. + numDirectories + " fonts");
  1583. log.debug("Containing the following fonts: ");
  1584. }
  1585. for (int i = 0; (i < numDirectories); i++) {
  1586. in.seekSet(dirOffsets[i]);
  1587. readDirTabs();
  1588. readName();
  1589. log.debug(fullName);
  1590. fontNames.add(fullName);
  1591. // Reset names
  1592. notice = "";
  1593. fullName = "";
  1594. familyNames.clear();
  1595. postScriptName = "";
  1596. subFamilyName = "";
  1597. }
  1598. in.seekSet(0);
  1599. return fontNames;
  1600. } else {
  1601. log.error("Not a TTC!");
  1602. return null;
  1603. }
  1604. }
  1605. /*
  1606. * Helper classes, they are not very efficient, but that really
  1607. * doesn't matter...
  1608. */
  1609. private Integer[] unicodeToWinAnsi(int unicode) {
  1610. List<Integer> ret = new ArrayList<Integer>();
  1611. for (int i = 32; i < Glyphs.WINANSI_ENCODING.length; i++) {
  1612. if (unicode == Glyphs.WINANSI_ENCODING[i]) {
  1613. ret.add(new Integer(i));
  1614. }
  1615. }
  1616. return ret.toArray(new Integer[0]);
  1617. }
  1618. /**
  1619. * Dumps a few informational values to System.out.
  1620. */
  1621. public void printStuff() {
  1622. System.out.println("Font name: " + postScriptName);
  1623. System.out.println("Full name: " + fullName);
  1624. System.out.println("Family name: " + familyNames);
  1625. System.out.println("Subfamily name: " + subFamilyName);
  1626. System.out.println("Notice: " + notice);
  1627. System.out.println("xHeight: " + convertTTFUnit2PDFUnit(xHeight));
  1628. System.out.println("capheight: " + convertTTFUnit2PDFUnit(capHeight));
  1629. int italic = (int)(italicAngle >> 16);
  1630. System.out.println("Italic: " + italic);
  1631. System.out.print("ItalicAngle: " + (short)(italicAngle / 0x10000));
  1632. if ((italicAngle % 0x10000) > 0) {
  1633. System.out.print("."
  1634. + (short)((italicAngle % 0x10000) * 1000)
  1635. / 0x10000);
  1636. }
  1637. System.out.println();
  1638. System.out.println("Ascender: " + convertTTFUnit2PDFUnit(ascender));
  1639. System.out.println("Descender: " + convertTTFUnit2PDFUnit(descender));
  1640. System.out.println("FontBBox: [" + convertTTFUnit2PDFUnit(fontBBox1)
  1641. + " " + convertTTFUnit2PDFUnit(fontBBox2) + " "
  1642. + convertTTFUnit2PDFUnit(fontBBox3) + " "
  1643. + convertTTFUnit2PDFUnit(fontBBox4) + "]");
  1644. }
  1645. private String formatUnitsForDebug(int units) {
  1646. return units + " -> " + convertTTFUnit2PDFUnit(units) + " internal units";
  1647. }
  1648. /**
  1649. * Map a glyph index to the corresponding unicode code point
  1650. *
  1651. * @param glyphIndex
  1652. * @return unicode code point
  1653. * @throws IOException if glyphIndex not found
  1654. */
  1655. private Integer glyphToUnicode(int glyphIndex) throws IOException {
  1656. return glyphToUnicodeMap.get(new Integer(glyphIndex));
  1657. }
  1658. /**
  1659. * Map a unicode code point to the corresponding glyph index
  1660. *
  1661. * @param unicodeIndex unicode code point
  1662. * @return glyph index
  1663. * @throws IOException if unicodeIndex not found
  1664. */
  1665. private Integer unicodeToGlyph(int unicodeIndex) throws IOException {
  1666. final Integer result
  1667. = unicodeToGlyphMap.get(new Integer(unicodeIndex));
  1668. if (result == null) {
  1669. throw new IOException(
  1670. "Glyph index not found for unicode value " + unicodeIndex);
  1671. }
  1672. return result;
  1673. }
  1674. String getGlyphName(int glyphIndex) {
  1675. return mtxTab[glyphIndex].getName();
  1676. }
  1677. /**
  1678. * Static main method to get info about a TrueType font.
  1679. * @param args The command line arguments
  1680. */
  1681. public static void main(String[] args) {
  1682. try {
  1683. TTFFile ttfFile = new TTFFile();
  1684. FontFileReader reader = new FontFileReader(args[0]);
  1685. String name = null;
  1686. if (args.length >= 2) {
  1687. name = args[1];
  1688. }
  1689. ttfFile.readFont(reader, name);
  1690. ttfFile.printStuff();
  1691. } catch (IOException ioe) {
  1692. System.err.println("Problem reading font: " + ioe.toString());
  1693. ioe.printStackTrace(System.err);
  1694. }
  1695. }
  1696. }