001/* 002 * $Id: TrueTypeFont.java 4784 2011-03-15 08:33:00Z blowagie $ 003 * 004 * This file is part of the iText (R) project. 005 * Copyright (c) 1998-2011 1T3XT BVBA 006 * Authors: Bruno Lowagie, Paulo Soares, et al. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU Affero General Public License version 3 010 * as published by the Free Software Foundation with the addition of the 011 * following permission added to Section 15 as permitted in Section 7(a): 012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT, 013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. 014 * 015 * This program is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 017 * or FITNESS FOR A PARTICULAR PURPOSE. 018 * See the GNU Affero General Public License for more details. 019 * You should have received a copy of the GNU Affero General Public License 020 * along with this program; if not, see http://www.gnu.org/licenses or write to 021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 022 * Boston, MA, 02110-1301 USA, or download the license from the following URL: 023 * http://itextpdf.com/terms-of-use/ 024 * 025 * The interactive user interfaces in modified source and object code versions 026 * of this program must display Appropriate Legal Notices, as required under 027 * Section 5 of the GNU Affero General Public License. 028 * 029 * In accordance with Section 7(b) of the GNU Affero General Public License, 030 * a covered work must retain the producer line in every PDF that is created 031 * or manipulated using iText. 032 * 033 * You can be released from the requirements of the license by purchasing 034 * a commercial license. Buying such a license is mandatory as soon as you 035 * develop commercial activities involving the iText software without 036 * disclosing the source code of your own applications. 037 * These activities include: offering paid services to customers as an ASP, 038 * serving PDFs on the fly in a web application, shipping iText with a closed 039 * source product. 040 * 041 * For more information, please contact iText Software Corp. at this 042 * address: sales@itextpdf.com 043 */ 044package com.itextpdf.text.pdf; 045 046import java.io.File; 047import java.io.IOException; 048import java.util.ArrayList; 049import java.util.HashMap; 050import java.util.HashSet; 051import java.util.Map; 052 053import com.itextpdf.text.Document; 054import com.itextpdf.text.DocumentException; 055import com.itextpdf.text.ExceptionConverter; 056import com.itextpdf.text.error_messages.MessageLocalization; 057 058/** Reads a Truetype font 059 * 060 * @author Paulo Soares 061 */ 062class TrueTypeFont extends BaseFont { 063 064 /** The code pages possible for a True Type font. 065 */ 066 static final String codePages[] = { 067 "1252 Latin 1", 068 "1250 Latin 2: Eastern Europe", 069 "1251 Cyrillic", 070 "1253 Greek", 071 "1254 Turkish", 072 "1255 Hebrew", 073 "1256 Arabic", 074 "1257 Windows Baltic", 075 "1258 Vietnamese", 076 null, 077 null, 078 null, 079 null, 080 null, 081 null, 082 null, 083 "874 Thai", 084 "932 JIS/Japan", 085 "936 Chinese: Simplified chars--PRC and Singapore", 086 "949 Korean Wansung", 087 "950 Chinese: Traditional chars--Taiwan and Hong Kong", 088 "1361 Korean Johab", 089 null, 090 null, 091 null, 092 null, 093 null, 094 null, 095 null, 096 "Macintosh Character Set (US Roman)", 097 "OEM Character Set", 098 "Symbol Character Set", 099 null, 100 null, 101 null, 102 null, 103 null, 104 null, 105 null, 106 null, 107 null, 108 null, 109 null, 110 null, 111 null, 112 null, 113 null, 114 null, 115 "869 IBM Greek", 116 "866 MS-DOS Russian", 117 "865 MS-DOS Nordic", 118 "864 Arabic", 119 "863 MS-DOS Canadian French", 120 "862 Hebrew", 121 "861 MS-DOS Icelandic", 122 "860 MS-DOS Portuguese", 123 "857 IBM Turkish", 124 "855 IBM Cyrillic; primarily Russian", 125 "852 Latin 2", 126 "775 MS-DOS Baltic", 127 "737 Greek; former 437 G", 128 "708 Arabic; ASMO 708", 129 "850 WE/Latin 1", 130 "437 US"}; 131 132 protected boolean justNames = false; 133 /** Contains the location of the several tables. The key is the name of 134 * the table and the value is an <CODE>int[2]</CODE> where position 0 135 * is the offset from the start of the file and position 1 is the length 136 * of the table. 137 */ 138 protected HashMap<String, int[]> tables; 139 /** The file in use. 140 */ 141 protected RandomAccessFileOrArray rf; 142 /** The file name. 143 */ 144 protected String fileName; 145 146 protected boolean cff = false; 147 148 protected int cffOffset; 149 150 protected int cffLength; 151 152 /** The offset from the start of the file to the table directory. 153 * It is 0 for TTF and may vary for TTC depending on the chosen font. 154 */ 155 protected int directoryOffset; 156 /** The index for the TTC font. It is an empty <CODE>String</CODE> for a 157 * TTF file. 158 */ 159 protected String ttcIndex; 160 /** The style modifier */ 161 protected String style = ""; 162 /** The content of table 'head'. 163 */ 164 protected FontHeader head = new FontHeader(); 165 /** The content of table 'hhea'. 166 */ 167 protected HorizontalHeader hhea = new HorizontalHeader(); 168 /** The content of table 'OS/2'. 169 */ 170 protected WindowsMetrics os_2 = new WindowsMetrics(); 171 /** The width of the glyphs. This is essentially the content of table 172 * 'hmtx' normalized to 1000 units. 173 */ 174 protected int GlyphWidths[]; 175 176 protected int bboxes[][]; 177 /** The map containing the code information for the table 'cmap', encoding 1.0. 178 * The key is the code and the value is an <CODE>int[2]</CODE> where position 0 179 * is the glyph number and position 1 is the glyph width normalized to 1000 180 * units. 181 */ 182 protected HashMap<Integer, int[]> cmap10; 183 /** The map containing the code information for the table 'cmap', encoding 3.1 184 * in Unicode. 185 * <P> 186 * The key is the code and the value is an <CODE>int</CODE>[2] where position 0 187 * is the glyph number and position 1 is the glyph width normalized to 1000 188 * units. 189 */ 190 protected HashMap<Integer, int[]> cmap31; 191 192 protected HashMap<Integer, int[]> cmapExt; 193 194 /** The map containing the kerning information. It represents the content of 195 * table 'kern'. The key is an <CODE>Integer</CODE> where the top 16 bits 196 * are the glyph number for the first character and the lower 16 bits are the 197 * glyph number for the second character. The value is the amount of kerning in 198 * normalized 1000 units as an <CODE>Integer</CODE>. This value is usually negative. 199 */ 200 protected IntHashtable kerning = new IntHashtable(); 201 /** 202 * The font name. 203 * This name is usually extracted from the table 'name' with 204 * the 'Name ID' 6. 205 */ 206 protected String fontName; 207 208 /** The full name of the font 209 */ 210 protected String fullName[][]; 211 212 /** All the names of the Names-Table 213 */ 214 protected String allNameEntries[][]; 215 216 /** The family name of the font 217 */ 218 protected String familyName[][]; 219 /** The italic angle. It is usually extracted from the 'post' table or in it's 220 * absence with the code: 221 * <P> 222 * <PRE> 223 * -Math.atan2(hhea.caretSlopeRun, hhea.caretSlopeRise) * 180 / Math.PI 224 * </PRE> 225 */ 226 protected double italicAngle; 227 /** <CODE>true</CODE> if all the glyphs have the same width. 228 */ 229 protected boolean isFixedPitch = false; 230 231 protected int underlinePosition; 232 233 protected int underlineThickness; 234 235 /** The components of table 'head'. 236 */ 237 protected static class FontHeader { 238 /** A variable. */ 239 int flags; 240 /** A variable. */ 241 int unitsPerEm; 242 /** A variable. */ 243 short xMin; 244 /** A variable. */ 245 short yMin; 246 /** A variable. */ 247 short xMax; 248 /** A variable. */ 249 short yMax; 250 /** A variable. */ 251 int macStyle; 252 } 253 254 /** The components of table 'hhea'. 255 */ 256 protected static class HorizontalHeader { 257 /** A variable. */ 258 short Ascender; 259 /** A variable. */ 260 short Descender; 261 /** A variable. */ 262 short LineGap; 263 /** A variable. */ 264 int advanceWidthMax; 265 /** A variable. */ 266 short minLeftSideBearing; 267 /** A variable. */ 268 short minRightSideBearing; 269 /** A variable. */ 270 short xMaxExtent; 271 /** A variable. */ 272 short caretSlopeRise; 273 /** A variable. */ 274 short caretSlopeRun; 275 /** A variable. */ 276 int numberOfHMetrics; 277 } 278 279 /** The components of table 'OS/2'. 280 */ 281 protected static class WindowsMetrics { 282 /** A variable. */ 283 short xAvgCharWidth; 284 /** A variable. */ 285 int usWeightClass; 286 /** A variable. */ 287 int usWidthClass; 288 /** A variable. */ 289 short fsType; 290 /** A variable. */ 291 short ySubscriptXSize; 292 /** A variable. */ 293 short ySubscriptYSize; 294 /** A variable. */ 295 short ySubscriptXOffset; 296 /** A variable. */ 297 short ySubscriptYOffset; 298 /** A variable. */ 299 short ySuperscriptXSize; 300 /** A variable. */ 301 short ySuperscriptYSize; 302 /** A variable. */ 303 short ySuperscriptXOffset; 304 /** A variable. */ 305 short ySuperscriptYOffset; 306 /** A variable. */ 307 short yStrikeoutSize; 308 /** A variable. */ 309 short yStrikeoutPosition; 310 /** A variable. */ 311 short sFamilyClass; 312 /** A variable. */ 313 byte panose[] = new byte[10]; 314 /** A variable. */ 315 byte achVendID[] = new byte[4]; 316 /** A variable. */ 317 int fsSelection; 318 /** A variable. */ 319 int usFirstCharIndex; 320 /** A variable. */ 321 int usLastCharIndex; 322 /** A variable. */ 323 short sTypoAscender; 324 /** A variable. */ 325 short sTypoDescender; 326 /** A variable. */ 327 short sTypoLineGap; 328 /** A variable. */ 329 int usWinAscent; 330 /** A variable. */ 331 int usWinDescent; 332 /** A variable. */ 333 int ulCodePageRange1; 334 /** A variable. */ 335 int ulCodePageRange2; 336 /** A variable. */ 337 int sCapHeight; 338 } 339 340 /** This constructor is present to allow extending the class. 341 */ 342 protected TrueTypeFont() { 343 } 344 345 /** Creates a new TrueType font. 346 * @param ttFile the location of the font on file. The file must end in '.ttf' or 347 * '.ttc' but can have modifiers after the name 348 * @param enc the encoding to be applied to this font 349 * @param emb true if the font is to be embedded in the PDF 350 * @param ttfAfm the font as a <CODE>byte</CODE> array 351 * @throws DocumentException the font is invalid 352 * @throws IOException the font file could not be read 353 * @since 2.1.5 354 */ 355 TrueTypeFont(String ttFile, String enc, boolean emb, byte ttfAfm[], boolean justNames, boolean forceRead) throws DocumentException, IOException { 356 this.justNames = justNames; 357 String nameBase = getBaseName(ttFile); 358 String ttcName = getTTCName(nameBase); 359 if (nameBase.length() < ttFile.length()) { 360 style = ttFile.substring(nameBase.length()); 361 } 362 encoding = enc; 363 embedded = emb; 364 fileName = ttcName; 365 fontType = FONT_TYPE_TT; 366 ttcIndex = ""; 367 if (ttcName.length() < nameBase.length()) 368 ttcIndex = nameBase.substring(ttcName.length() + 1); 369 if (fileName.toLowerCase().endsWith(".ttf") || fileName.toLowerCase().endsWith(".otf") || fileName.toLowerCase().endsWith(".ttc")) { 370 process(ttfAfm, forceRead); 371 if (!justNames && embedded && os_2.fsType == 2) 372 throw new DocumentException(MessageLocalization.getComposedMessage("1.cannot.be.embedded.due.to.licensing.restrictions", fileName + style)); 373 } 374 else 375 throw new DocumentException(MessageLocalization.getComposedMessage("1.is.not.a.ttf.otf.or.ttc.font.file", fileName + style)); 376 if (!encoding.startsWith("#")) 377 PdfEncodings.convertToBytes(" ", enc); // check if the encoding exists 378 createEncoding(); 379 } 380 381 /** Gets the name from a composed TTC file name. 382 * If I have for input "myfont.ttc,2" the return will 383 * be "myfont.ttc". 384 * @param name the full name 385 * @return the simple file name 386 */ 387 protected static String getTTCName(String name) { 388 int idx = name.toLowerCase().indexOf(".ttc,"); 389 if (idx < 0) 390 return name; 391 else 392 return name.substring(0, idx + 4); 393 } 394 395 396 /** 397 * Reads the tables 'head', 'hhea', 'OS/2' and 'post' filling several variables. 398 * @throws DocumentException the font is invalid 399 * @throws IOException the font file could not be read 400 */ 401 void fillTables() throws DocumentException, IOException { 402 int table_location[]; 403 table_location = tables.get("head"); 404 if (table_location == null) 405 throw new DocumentException(MessageLocalization.getComposedMessage("table.1.does.not.exist.in.2", "head", fileName + style)); 406 rf.seek(table_location[0] + 16); 407 head.flags = rf.readUnsignedShort(); 408 head.unitsPerEm = rf.readUnsignedShort(); 409 rf.skipBytes(16); 410 head.xMin = rf.readShort(); 411 head.yMin = rf.readShort(); 412 head.xMax = rf.readShort(); 413 head.yMax = rf.readShort(); 414 head.macStyle = rf.readUnsignedShort(); 415 416 table_location = tables.get("hhea"); 417 if (table_location == null) 418 throw new DocumentException(MessageLocalization.getComposedMessage("table.1.does.not.exist.in.2", "hhea", fileName + style)); 419 rf.seek(table_location[0] + 4); 420 hhea.Ascender = rf.readShort(); 421 hhea.Descender = rf.readShort(); 422 hhea.LineGap = rf.readShort(); 423 hhea.advanceWidthMax = rf.readUnsignedShort(); 424 hhea.minLeftSideBearing = rf.readShort(); 425 hhea.minRightSideBearing = rf.readShort(); 426 hhea.xMaxExtent = rf.readShort(); 427 hhea.caretSlopeRise = rf.readShort(); 428 hhea.caretSlopeRun = rf.readShort(); 429 rf.skipBytes(12); 430 hhea.numberOfHMetrics = rf.readUnsignedShort(); 431 432 table_location = tables.get("OS/2"); 433 if (table_location == null) 434 throw new DocumentException(MessageLocalization.getComposedMessage("table.1.does.not.exist.in.2", "OS/2", fileName + style)); 435 rf.seek(table_location[0]); 436 int version = rf.readUnsignedShort(); 437 os_2.xAvgCharWidth = rf.readShort(); 438 os_2.usWeightClass = rf.readUnsignedShort(); 439 os_2.usWidthClass = rf.readUnsignedShort(); 440 os_2.fsType = rf.readShort(); 441 os_2.ySubscriptXSize = rf.readShort(); 442 os_2.ySubscriptYSize = rf.readShort(); 443 os_2.ySubscriptXOffset = rf.readShort(); 444 os_2.ySubscriptYOffset = rf.readShort(); 445 os_2.ySuperscriptXSize = rf.readShort(); 446 os_2.ySuperscriptYSize = rf.readShort(); 447 os_2.ySuperscriptXOffset = rf.readShort(); 448 os_2.ySuperscriptYOffset = rf.readShort(); 449 os_2.yStrikeoutSize = rf.readShort(); 450 os_2.yStrikeoutPosition = rf.readShort(); 451 os_2.sFamilyClass = rf.readShort(); 452 rf.readFully(os_2.panose); 453 rf.skipBytes(16); 454 rf.readFully(os_2.achVendID); 455 os_2.fsSelection = rf.readUnsignedShort(); 456 os_2.usFirstCharIndex = rf.readUnsignedShort(); 457 os_2.usLastCharIndex = rf.readUnsignedShort(); 458 os_2.sTypoAscender = rf.readShort(); 459 os_2.sTypoDescender = rf.readShort(); 460 if (os_2.sTypoDescender > 0) 461 os_2.sTypoDescender = (short)-os_2.sTypoDescender; 462 os_2.sTypoLineGap = rf.readShort(); 463 os_2.usWinAscent = rf.readUnsignedShort(); 464 os_2.usWinDescent = rf.readUnsignedShort(); 465 os_2.ulCodePageRange1 = 0; 466 os_2.ulCodePageRange2 = 0; 467 if (version > 0) { 468 os_2.ulCodePageRange1 = rf.readInt(); 469 os_2.ulCodePageRange2 = rf.readInt(); 470 } 471 if (version > 1) { 472 rf.skipBytes(2); 473 os_2.sCapHeight = rf.readShort(); 474 } 475 else 476 os_2.sCapHeight = (int)(0.7 * head.unitsPerEm); 477 478 table_location = tables.get("post"); 479 if (table_location == null) { 480 italicAngle = -Math.atan2(hhea.caretSlopeRun, hhea.caretSlopeRise) * 180 / Math.PI; 481 return; 482 } 483 rf.seek(table_location[0] + 4); 484 short mantissa = rf.readShort(); 485 int fraction = rf.readUnsignedShort(); 486 italicAngle = mantissa + fraction / 16384.0d; 487 underlinePosition = rf.readShort(); 488 underlineThickness = rf.readShort(); 489 isFixedPitch = rf.readInt() != 0; 490 } 491 492 /** 493 * Gets the Postscript font name. 494 * @throws DocumentException the font is invalid 495 * @throws IOException the font file could not be read 496 * @return the Postscript font name 497 */ 498 String getBaseFont() throws DocumentException, IOException { 499 int table_location[]; 500 table_location = tables.get("name"); 501 if (table_location == null) 502 throw new DocumentException(MessageLocalization.getComposedMessage("table.1.does.not.exist.in.2", "name", fileName + style)); 503 rf.seek(table_location[0] + 2); 504 int numRecords = rf.readUnsignedShort(); 505 int startOfStorage = rf.readUnsignedShort(); 506 for (int k = 0; k < numRecords; ++k) { 507 int platformID = rf.readUnsignedShort(); 508 int platformEncodingID = rf.readUnsignedShort(); 509 int languageID = rf.readUnsignedShort(); 510 int nameID = rf.readUnsignedShort(); 511 int length = rf.readUnsignedShort(); 512 int offset = rf.readUnsignedShort(); 513 if (nameID == 6) { 514 rf.seek(table_location[0] + startOfStorage + offset); 515 if (platformID == 0 || platformID == 3) 516 return readUnicodeString(length); 517 else 518 return readStandardString(length); 519 } 520 } 521 File file = new File(fileName); 522 return file.getName().replace(' ', '-'); 523 } 524 525 /** Extracts the names of the font in all the languages available. 526 * @param id the name id to retrieve 527 * @throws DocumentException on error 528 * @throws IOException on error 529 */ 530 String[][] getNames(int id) throws DocumentException, IOException { 531 int table_location[]; 532 table_location = tables.get("name"); 533 if (table_location == null) 534 throw new DocumentException(MessageLocalization.getComposedMessage("table.1.does.not.exist.in.2", "name", fileName + style)); 535 rf.seek(table_location[0] + 2); 536 int numRecords = rf.readUnsignedShort(); 537 int startOfStorage = rf.readUnsignedShort(); 538 ArrayList<String[]> names = new ArrayList<String[]>(); 539 for (int k = 0; k < numRecords; ++k) { 540 int platformID = rf.readUnsignedShort(); 541 int platformEncodingID = rf.readUnsignedShort(); 542 int languageID = rf.readUnsignedShort(); 543 int nameID = rf.readUnsignedShort(); 544 int length = rf.readUnsignedShort(); 545 int offset = rf.readUnsignedShort(); 546 if (nameID == id) { 547 int pos = rf.getFilePointer(); 548 rf.seek(table_location[0] + startOfStorage + offset); 549 String name; 550 if (platformID == 0 || platformID == 3 || platformID == 2 && platformEncodingID == 1){ 551 name = readUnicodeString(length); 552 } 553 else { 554 name = readStandardString(length); 555 } 556 names.add(new String[]{String.valueOf(platformID), 557 String.valueOf(platformEncodingID), String.valueOf(languageID), name}); 558 rf.seek(pos); 559 } 560 } 561 String thisName[][] = new String[names.size()][]; 562 for (int k = 0; k < names.size(); ++k) 563 thisName[k] = names.get(k); 564 return thisName; 565 } 566 567 /** Extracts all the names of the names-Table 568 * @throws DocumentException on error 569 * @throws IOException on error 570 */ 571 String[][] getAllNames() throws DocumentException, IOException { 572 int table_location[]; 573 table_location = tables.get("name"); 574 if (table_location == null) 575 throw new DocumentException(MessageLocalization.getComposedMessage("table.1.does.not.exist.in.2", "name", fileName + style)); 576 rf.seek(table_location[0] + 2); 577 int numRecords = rf.readUnsignedShort(); 578 int startOfStorage = rf.readUnsignedShort(); 579 ArrayList<String[]> names = new ArrayList<String[]>(); 580 for (int k = 0; k < numRecords; ++k) { 581 int platformID = rf.readUnsignedShort(); 582 int platformEncodingID = rf.readUnsignedShort(); 583 int languageID = rf.readUnsignedShort(); 584 int nameID = rf.readUnsignedShort(); 585 int length = rf.readUnsignedShort(); 586 int offset = rf.readUnsignedShort(); 587 int pos = rf.getFilePointer(); 588 rf.seek(table_location[0] + startOfStorage + offset); 589 String name; 590 if (platformID == 0 || platformID == 3 || platformID == 2 && platformEncodingID == 1){ 591 name = readUnicodeString(length); 592 } 593 else { 594 name = readStandardString(length); 595 } 596 names.add(new String[]{String.valueOf(nameID), String.valueOf(platformID), 597 String.valueOf(platformEncodingID), String.valueOf(languageID), name}); 598 rf.seek(pos); 599 } 600 String thisName[][] = new String[names.size()][]; 601 for (int k = 0; k < names.size(); ++k) 602 thisName[k] = names.get(k); 603 return thisName; 604 } 605 606 void checkCff() { 607 int table_location[]; 608 table_location = tables.get("CFF "); 609 if (table_location != null) { 610 cff = true; 611 cffOffset = table_location[0]; 612 cffLength = table_location[1]; 613 } 614 } 615 616 /** Reads the font data. 617 * @param ttfAfm the font as a <CODE>byte</CODE> array, possibly <CODE>null</CODE> 618 * @throws DocumentException the font is invalid 619 * @throws IOException the font file could not be read 620 * @since 2.1.5 621 */ 622 void process(byte ttfAfm[], boolean preload) throws DocumentException, IOException { 623 tables = new HashMap<String, int[]>(); 624 625 try { 626 if (ttfAfm == null) 627 rf = new RandomAccessFileOrArray(fileName, preload, Document.plainRandomAccess); 628 else 629 rf = new RandomAccessFileOrArray(ttfAfm); 630 if (ttcIndex.length() > 0) { 631 int dirIdx = Integer.parseInt(ttcIndex); 632 if (dirIdx < 0) 633 throw new DocumentException(MessageLocalization.getComposedMessage("the.font.index.for.1.must.be.positive", fileName)); 634 String mainTag = readStandardString(4); 635 if (!mainTag.equals("ttcf")) 636 throw new DocumentException(MessageLocalization.getComposedMessage("1.is.not.a.valid.ttc.file", fileName)); 637 rf.skipBytes(4); 638 int dirCount = rf.readInt(); 639 if (dirIdx >= dirCount) 640 throw new DocumentException(MessageLocalization.getComposedMessage("the.font.index.for.1.must.be.between.0.and.2.it.was.3", fileName, String.valueOf(dirCount - 1), String.valueOf(dirIdx))); 641 rf.skipBytes(dirIdx * 4); 642 directoryOffset = rf.readInt(); 643 } 644 rf.seek(directoryOffset); 645 int ttId = rf.readInt(); 646 if (ttId != 0x00010000 && ttId != 0x4F54544F) 647 throw new DocumentException(MessageLocalization.getComposedMessage("1.is.not.a.valid.ttf.or.otf.file", fileName)); 648 int num_tables = rf.readUnsignedShort(); 649 rf.skipBytes(6); 650 for (int k = 0; k < num_tables; ++k) { 651 String tag = readStandardString(4); 652 rf.skipBytes(4); 653 int table_location[] = new int[2]; 654 table_location[0] = rf.readInt(); 655 table_location[1] = rf.readInt(); 656 tables.put(tag, table_location); 657 } 658 checkCff(); 659 fontName = getBaseFont(); 660 fullName = getNames(4); //full name 661 familyName = getNames(1); //family name 662 allNameEntries = getAllNames(); 663 if (!justNames) { 664 fillTables(); 665 readGlyphWidths(); 666 readCMaps(); 667 readKerning(); 668 readBbox(); 669 GlyphWidths = null; 670 } 671 } 672 finally { 673 if (rf != null) { 674 rf.close(); 675 if (!embedded) 676 rf = null; 677 } 678 } 679 } 680 681 /** Reads a <CODE>String</CODE> from the font file as bytes using the Cp1252 682 * encoding. 683 * @param length the length of bytes to read 684 * @return the <CODE>String</CODE> read 685 * @throws IOException the font file could not be read 686 */ 687 protected String readStandardString(int length) throws IOException { 688 byte buf[] = new byte[length]; 689 rf.readFully(buf); 690 try { 691 return new String(buf, WINANSI); 692 } 693 catch (Exception e) { 694 throw new ExceptionConverter(e); 695 } 696 } 697 698 /** Reads a Unicode <CODE>String</CODE> from the font file. Each character is 699 * represented by two bytes. 700 * @param length the length of bytes to read. The <CODE>String</CODE> will have <CODE>length</CODE>/2 701 * characters 702 * @return the <CODE>String</CODE> read 703 * @throws IOException the font file could not be read 704 */ 705 protected String readUnicodeString(int length) throws IOException { 706 StringBuffer buf = new StringBuffer(); 707 length /= 2; 708 for (int k = 0; k < length; ++k) { 709 buf.append(rf.readChar()); 710 } 711 return buf.toString(); 712 } 713 714 /** Reads the glyphs widths. The widths are extracted from the table 'hmtx'. 715 * The glyphs are normalized to 1000 units. 716 * @throws DocumentException the font is invalid 717 * @throws IOException the font file could not be read 718 */ 719 protected void readGlyphWidths() throws DocumentException, IOException { 720 int table_location[]; 721 table_location = tables.get("hmtx"); 722 if (table_location == null) 723 throw new DocumentException(MessageLocalization.getComposedMessage("table.1.does.not.exist.in.2", "hmtx", fileName + style)); 724 rf.seek(table_location[0]); 725 GlyphWidths = new int[hhea.numberOfHMetrics]; 726 for (int k = 0; k < hhea.numberOfHMetrics; ++k) { 727 GlyphWidths[k] = rf.readUnsignedShort() * 1000 / head.unitsPerEm; 728 rf.readUnsignedShort(); 729 } 730 } 731 732 /** Gets a glyph width. 733 * @param glyph the glyph to get the width of 734 * @return the width of the glyph in normalized 1000 units 735 */ 736 protected int getGlyphWidth(int glyph) { 737 if (glyph >= GlyphWidths.length) 738 glyph = GlyphWidths.length - 1; 739 return GlyphWidths[glyph]; 740 } 741 742 private void readBbox() throws DocumentException, IOException { 743 int tableLocation[]; 744 tableLocation = tables.get("head"); 745 if (tableLocation == null) 746 throw new DocumentException(MessageLocalization.getComposedMessage("table.1.does.not.exist.in.2", "head", fileName + style)); 747 rf.seek(tableLocation[0] + TrueTypeFontSubSet.HEAD_LOCA_FORMAT_OFFSET); 748 boolean locaShortTable = rf.readUnsignedShort() == 0; 749 tableLocation = tables.get("loca"); 750 if (tableLocation == null) 751 return; 752 rf.seek(tableLocation[0]); 753 int locaTable[]; 754 if (locaShortTable) { 755 int entries = tableLocation[1] / 2; 756 locaTable = new int[entries]; 757 for (int k = 0; k < entries; ++k) 758 locaTable[k] = rf.readUnsignedShort() * 2; 759 } 760 else { 761 int entries = tableLocation[1] / 4; 762 locaTable = new int[entries]; 763 for (int k = 0; k < entries; ++k) 764 locaTable[k] = rf.readInt(); 765 } 766 tableLocation = tables.get("glyf"); 767 if (tableLocation == null) 768 throw new DocumentException(MessageLocalization.getComposedMessage("table.1.does.not.exist.in.2", "glyf", fileName + style)); 769 int tableGlyphOffset = tableLocation[0]; 770 bboxes = new int[locaTable.length - 1][]; 771 for (int glyph = 0; glyph < locaTable.length - 1; ++glyph) { 772 int start = locaTable[glyph]; 773 if (start != locaTable[glyph + 1]) { 774 rf.seek(tableGlyphOffset + start + 2); 775 bboxes[glyph] = new int[]{ 776 rf.readShort() * 1000 / head.unitsPerEm, 777 rf.readShort() * 1000 / head.unitsPerEm, 778 rf.readShort() * 1000 / head.unitsPerEm, 779 rf.readShort() * 1000 / head.unitsPerEm}; 780 } 781 } 782 } 783 784 /** Reads the several maps from the table 'cmap'. The maps of interest are 1.0 for symbolic 785 * fonts and 3.1 for all others. A symbolic font is defined as having the map 3.0. 786 * @throws DocumentException the font is invalid 787 * @throws IOException the font file could not be read 788 */ 789 void readCMaps() throws DocumentException, IOException { 790 int table_location[]; 791 table_location = tables.get("cmap"); 792 if (table_location == null) 793 throw new DocumentException(MessageLocalization.getComposedMessage("table.1.does.not.exist.in.2", "cmap", fileName + style)); 794 rf.seek(table_location[0]); 795 rf.skipBytes(2); 796 int num_tables = rf.readUnsignedShort(); 797 fontSpecific = false; 798 int map10 = 0; 799 int map31 = 0; 800 int map30 = 0; 801 int mapExt = 0; 802 for (int k = 0; k < num_tables; ++k) { 803 int platId = rf.readUnsignedShort(); 804 int platSpecId = rf.readUnsignedShort(); 805 int offset = rf.readInt(); 806 if (platId == 3 && platSpecId == 0) { 807 fontSpecific = true; 808 map30 = offset; 809 } 810 else if (platId == 3 && platSpecId == 1) { 811 map31 = offset; 812 } 813 else if (platId == 3 && platSpecId == 10) { 814 mapExt = offset; 815 } 816 if (platId == 1 && platSpecId == 0) { 817 map10 = offset; 818 } 819 } 820 if (map10 > 0) { 821 rf.seek(table_location[0] + map10); 822 int format = rf.readUnsignedShort(); 823 switch (format) { 824 case 0: 825 cmap10 = readFormat0(); 826 break; 827 case 4: 828 cmap10 = readFormat4(); 829 break; 830 case 6: 831 cmap10 = readFormat6(); 832 break; 833 } 834 } 835 if (map31 > 0) { 836 rf.seek(table_location[0] + map31); 837 int format = rf.readUnsignedShort(); 838 if (format == 4) { 839 cmap31 = readFormat4(); 840 } 841 } 842 if (map30 > 0) { 843 rf.seek(table_location[0] + map30); 844 int format = rf.readUnsignedShort(); 845 if (format == 4) { 846 cmap10 = readFormat4(); 847 } 848 } 849 if (mapExt > 0) { 850 rf.seek(table_location[0] + mapExt); 851 int format = rf.readUnsignedShort(); 852 switch (format) { 853 case 0: 854 cmapExt = readFormat0(); 855 break; 856 case 4: 857 cmapExt = readFormat4(); 858 break; 859 case 6: 860 cmapExt = readFormat6(); 861 break; 862 case 12: 863 cmapExt = readFormat12(); 864 break; 865 } 866 } 867 } 868 869 HashMap<Integer, int[]> readFormat12() throws IOException { 870 HashMap<Integer, int[]> h = new HashMap<Integer, int[]>(); 871 rf.skipBytes(2); 872 int table_lenght = rf.readInt(); 873 rf.skipBytes(4); 874 int nGroups = rf.readInt(); 875 for (int k = 0; k < nGroups; k++) { 876 int startCharCode = rf.readInt(); 877 int endCharCode = rf.readInt(); 878 int startGlyphID = rf.readInt(); 879 for (int i = startCharCode; i <= endCharCode; i++) { 880 int[] r = new int[2]; 881 r[0] = startGlyphID; 882 r[1] = getGlyphWidth(r[0]); 883 h.put(Integer.valueOf(i), r); 884 startGlyphID++; 885 } 886 } 887 return h; 888 } 889 890 /** The information in the maps of the table 'cmap' is coded in several formats. 891 * Format 0 is the Apple standard character to glyph index mapping table. 892 * @return a <CODE>HashMap</CODE> representing this map 893 * @throws IOException the font file could not be read 894 */ 895 HashMap<Integer, int[]> readFormat0() throws IOException { 896 HashMap<Integer, int[]> h = new HashMap<Integer, int[]>(); 897 rf.skipBytes(4); 898 for (int k = 0; k < 256; ++k) { 899 int r[] = new int[2]; 900 r[0] = rf.readUnsignedByte(); 901 r[1] = getGlyphWidth(r[0]); 902 h.put(Integer.valueOf(k), r); 903 } 904 return h; 905 } 906 907 /** The information in the maps of the table 'cmap' is coded in several formats. 908 * Format 4 is the Microsoft standard character to glyph index mapping table. 909 * @return a <CODE>HashMap</CODE> representing this map 910 * @throws IOException the font file could not be read 911 */ 912 HashMap<Integer, int[]> readFormat4() throws IOException { 913 HashMap<Integer, int[]> h = new HashMap<Integer, int[]>(); 914 int table_lenght = rf.readUnsignedShort(); 915 rf.skipBytes(2); 916 int segCount = rf.readUnsignedShort() / 2; 917 rf.skipBytes(6); 918 int endCount[] = new int[segCount]; 919 for (int k = 0; k < segCount; ++k) { 920 endCount[k] = rf.readUnsignedShort(); 921 } 922 rf.skipBytes(2); 923 int startCount[] = new int[segCount]; 924 for (int k = 0; k < segCount; ++k) { 925 startCount[k] = rf.readUnsignedShort(); 926 } 927 int idDelta[] = new int[segCount]; 928 for (int k = 0; k < segCount; ++k) { 929 idDelta[k] = rf.readUnsignedShort(); 930 } 931 int idRO[] = new int[segCount]; 932 for (int k = 0; k < segCount; ++k) { 933 idRO[k] = rf.readUnsignedShort(); 934 } 935 int glyphId[] = new int[table_lenght / 2 - 8 - segCount * 4]; 936 for (int k = 0; k < glyphId.length; ++k) { 937 glyphId[k] = rf.readUnsignedShort(); 938 } 939 for (int k = 0; k < segCount; ++k) { 940 int glyph; 941 for (int j = startCount[k]; j <= endCount[k] && j != 0xFFFF; ++j) { 942 if (idRO[k] == 0) { 943 glyph = j + idDelta[k] & 0xFFFF; 944 } 945 else { 946 int idx = k + idRO[k] / 2 - segCount + j - startCount[k]; 947 if (idx >= glyphId.length) 948 continue; 949 glyph = glyphId[idx] + idDelta[k] & 0xFFFF; 950 } 951 int r[] = new int[2]; 952 r[0] = glyph; 953 r[1] = getGlyphWidth(r[0]); 954 h.put(Integer.valueOf(fontSpecific ? ((j & 0xff00) == 0xf000 ? j & 0xff : j) : j), r); 955 } 956 } 957 return h; 958 } 959 960 /** The information in the maps of the table 'cmap' is coded in several formats. 961 * Format 6 is a trimmed table mapping. It is similar to format 0 but can have 962 * less than 256 entries. 963 * @return a <CODE>HashMap</CODE> representing this map 964 * @throws IOException the font file could not be read 965 */ 966 HashMap<Integer, int[]> readFormat6() throws IOException { 967 HashMap<Integer, int[]> h = new HashMap<Integer, int[]>(); 968 rf.skipBytes(4); 969 int start_code = rf.readUnsignedShort(); 970 int code_count = rf.readUnsignedShort(); 971 for (int k = 0; k < code_count; ++k) { 972 int r[] = new int[2]; 973 r[0] = rf.readUnsignedShort(); 974 r[1] = getGlyphWidth(r[0]); 975 h.put(Integer.valueOf(k + start_code), r); 976 } 977 return h; 978 } 979 980 /** Reads the kerning information from the 'kern' table. 981 * @throws IOException the font file could not be read 982 */ 983 void readKerning() throws IOException { 984 int table_location[]; 985 table_location = tables.get("kern"); 986 if (table_location == null) 987 return; 988 rf.seek(table_location[0] + 2); 989 int nTables = rf.readUnsignedShort(); 990 int checkpoint = table_location[0] + 4; 991 int length = 0; 992 for (int k = 0; k < nTables; ++k) { 993 checkpoint += length; 994 rf.seek(checkpoint); 995 rf.skipBytes(2); 996 length = rf.readUnsignedShort(); 997 int coverage = rf.readUnsignedShort(); 998 if ((coverage & 0xfff7) == 0x0001) { 999 int nPairs = rf.readUnsignedShort(); 1000 rf.skipBytes(6); 1001 for (int j = 0; j < nPairs; ++j) { 1002 int pair = rf.readInt(); 1003 int value = rf.readShort() * 1000 / head.unitsPerEm; 1004 kerning.put(pair, value); 1005 } 1006 } 1007 } 1008 } 1009 1010 /** Gets the kerning between two Unicode chars. 1011 * @param char1 the first char 1012 * @param char2 the second char 1013 * @return the kerning to be applied 1014 */ 1015 @Override 1016 public int getKerning(int char1, int char2) { 1017 int metrics[] = getMetricsTT(char1); 1018 if (metrics == null) 1019 return 0; 1020 int c1 = metrics[0]; 1021 metrics = getMetricsTT(char2); 1022 if (metrics == null) 1023 return 0; 1024 int c2 = metrics[0]; 1025 return kerning.get((c1 << 16) + c2); 1026 } 1027 1028 /** Gets the width from the font according to the unicode char <CODE>c</CODE>. 1029 * If the <CODE>name</CODE> is null it's a symbolic font. 1030 * @param c the unicode char 1031 * @param name the glyph name 1032 * @return the width of the char 1033 */ 1034 @Override 1035 int getRawWidth(int c, String name) { 1036 int[] metric = getMetricsTT(c); 1037 if (metric == null) 1038 return 0; 1039 return metric[1]; 1040 } 1041 1042 /** Generates the font descriptor for this font. 1043 * @return the PdfDictionary containing the font descriptor or <CODE>null</CODE> 1044 * @param subsetPrefix the subset prefix 1045 * @param fontStream the indirect reference to a PdfStream containing the font or <CODE>null</CODE> 1046 */ 1047 protected PdfDictionary getFontDescriptor(PdfIndirectReference fontStream, String subsetPrefix, PdfIndirectReference cidset) { 1048 PdfDictionary dic = new PdfDictionary(PdfName.FONTDESCRIPTOR); 1049 dic.put(PdfName.ASCENT, new PdfNumber(os_2.sTypoAscender * 1000 / head.unitsPerEm)); 1050 dic.put(PdfName.CAPHEIGHT, new PdfNumber(os_2.sCapHeight * 1000 / head.unitsPerEm)); 1051 dic.put(PdfName.DESCENT, new PdfNumber(os_2.sTypoDescender * 1000 / head.unitsPerEm)); 1052 dic.put(PdfName.FONTBBOX, new PdfRectangle( 1053 head.xMin * 1000 / head.unitsPerEm, 1054 head.yMin * 1000 / head.unitsPerEm, 1055 head.xMax * 1000 / head.unitsPerEm, 1056 head.yMax * 1000 / head.unitsPerEm)); 1057 if (cidset != null) 1058 dic.put(PdfName.CIDSET, cidset); 1059 if (cff) { 1060 if (encoding.startsWith("Identity-")) 1061 dic.put(PdfName.FONTNAME, new PdfName(subsetPrefix + fontName+"-"+encoding)); 1062 else 1063 dic.put(PdfName.FONTNAME, new PdfName(subsetPrefix + fontName + style)); 1064 } 1065 else 1066 dic.put(PdfName.FONTNAME, new PdfName(subsetPrefix + fontName + style)); 1067 dic.put(PdfName.ITALICANGLE, new PdfNumber(italicAngle)); 1068 dic.put(PdfName.STEMV, new PdfNumber(80)); 1069 if (fontStream != null) { 1070 if (cff) 1071 dic.put(PdfName.FONTFILE3, fontStream); 1072 else 1073 dic.put(PdfName.FONTFILE2, fontStream); 1074 } 1075 int flags = 0; 1076 if (isFixedPitch) 1077 flags |= 1; 1078 flags |= fontSpecific ? 4 : 32; 1079 if ((head.macStyle & 2) != 0) 1080 flags |= 64; 1081 if ((head.macStyle & 1) != 0) 1082 flags |= 262144; 1083 dic.put(PdfName.FLAGS, new PdfNumber(flags)); 1084 1085 return dic; 1086 } 1087 1088 /** Generates the font dictionary for this font. 1089 * @return the PdfDictionary containing the font dictionary 1090 * @param subsetPrefix the subset prefix 1091 * @param firstChar the first valid character 1092 * @param lastChar the last valid character 1093 * @param shortTag a 256 bytes long <CODE>byte</CODE> array where each unused byte is represented by 0 1094 * @param fontDescriptor the indirect reference to a PdfDictionary containing the font descriptor or <CODE>null</CODE> 1095 */ 1096 protected PdfDictionary getFontBaseType(PdfIndirectReference fontDescriptor, String subsetPrefix, int firstChar, int lastChar, byte shortTag[]) { 1097 PdfDictionary dic = new PdfDictionary(PdfName.FONT); 1098 if (cff) { 1099 dic.put(PdfName.SUBTYPE, PdfName.TYPE1); 1100 dic.put(PdfName.BASEFONT, new PdfName(fontName + style)); 1101 } 1102 else { 1103 dic.put(PdfName.SUBTYPE, PdfName.TRUETYPE); 1104 dic.put(PdfName.BASEFONT, new PdfName(subsetPrefix + fontName + style)); 1105 } 1106 dic.put(PdfName.BASEFONT, new PdfName(subsetPrefix + fontName + style)); 1107 if (!fontSpecific) { 1108 for (int k = firstChar; k <= lastChar; ++k) { 1109 if (!differences[k].equals(notdef)) { 1110 firstChar = k; 1111 break; 1112 } 1113 } 1114 if (encoding.equals("Cp1252") || encoding.equals("MacRoman")) 1115 dic.put(PdfName.ENCODING, encoding.equals("Cp1252") ? PdfName.WIN_ANSI_ENCODING : PdfName.MAC_ROMAN_ENCODING); 1116 else { 1117 PdfDictionary enc = new PdfDictionary(PdfName.ENCODING); 1118 PdfArray dif = new PdfArray(); 1119 boolean gap = true; 1120 for (int k = firstChar; k <= lastChar; ++k) { 1121 if (shortTag[k] != 0) { 1122 if (gap) { 1123 dif.add(new PdfNumber(k)); 1124 gap = false; 1125 } 1126 dif.add(new PdfName(differences[k])); 1127 } 1128 else 1129 gap = true; 1130 } 1131 enc.put(PdfName.DIFFERENCES, dif); 1132 dic.put(PdfName.ENCODING, enc); 1133 } 1134 } 1135 dic.put(PdfName.FIRSTCHAR, new PdfNumber(firstChar)); 1136 dic.put(PdfName.LASTCHAR, new PdfNumber(lastChar)); 1137 PdfArray wd = new PdfArray(); 1138 for (int k = firstChar; k <= lastChar; ++k) { 1139 if (shortTag[k] == 0) 1140 wd.add(new PdfNumber(0)); 1141 else 1142 wd.add(new PdfNumber(widths[k])); 1143 } 1144 dic.put(PdfName.WIDTHS, wd); 1145 if (fontDescriptor != null) 1146 dic.put(PdfName.FONTDESCRIPTOR, fontDescriptor); 1147 return dic; 1148 } 1149 1150 protected byte[] getFullFont() throws IOException { 1151 RandomAccessFileOrArray rf2 = null; 1152 try { 1153 rf2 = new RandomAccessFileOrArray(rf); 1154 rf2.reOpen(); 1155 byte b[] = new byte[rf2.length()]; 1156 rf2.readFully(b); 1157 return b; 1158 } 1159 finally { 1160 try {if (rf2 != null) {rf2.close();}} catch (Exception e) {} 1161 } 1162 } 1163 1164 protected static int[] compactRanges(ArrayList<int[]> ranges) { 1165 ArrayList<int[]> simp = new ArrayList<int[]>(); 1166 for (int k = 0; k < ranges.size(); ++k) { 1167 int[] r = ranges.get(k); 1168 for (int j = 0; j < r.length; j += 2) { 1169 simp.add(new int[]{Math.max(0, Math.min(r[j], r[j + 1])), Math.min(0xffff, Math.max(r[j], r[j + 1]))}); 1170 } 1171 } 1172 for (int k1 = 0; k1 < simp.size() - 1; ++k1) { 1173 for (int k2 = k1 + 1; k2 < simp.size(); ++k2) { 1174 int[] r1 = simp.get(k1); 1175 int[] r2 = simp.get(k2); 1176 if (r1[0] >= r2[0] && r1[0] <= r2[1] || r1[1] >= r2[0] && r1[0] <= r2[1]) { 1177 r1[0] = Math.min(r1[0], r2[0]); 1178 r1[1] = Math.max(r1[1], r2[1]); 1179 simp.remove(k2); 1180 --k2; 1181 } 1182 } 1183 } 1184 int[] s = new int[simp.size() * 2]; 1185 for (int k = 0; k < simp.size(); ++k) { 1186 int[] r = simp.get(k); 1187 s[k * 2] = r[0]; 1188 s[k * 2 + 1] = r[1]; 1189 } 1190 return s; 1191 } 1192 1193 protected void addRangeUni(HashMap<Integer, int[]> longTag, boolean includeMetrics, boolean subsetp) { 1194 if (!subsetp && (subsetRanges != null || directoryOffset > 0)) { 1195 int[] rg = subsetRanges == null && directoryOffset > 0 ? new int[]{0, 0xffff} : compactRanges(subsetRanges); 1196 HashMap<Integer, int[]> usemap; 1197 if (!fontSpecific && cmap31 != null) 1198 usemap = cmap31; 1199 else if (fontSpecific && cmap10 != null) 1200 usemap = cmap10; 1201 else if (cmap31 != null) 1202 usemap = cmap31; 1203 else 1204 usemap = cmap10; 1205 for (Map.Entry<Integer, int[]> e: usemap.entrySet()) { 1206 int[] v = e.getValue(); 1207 Integer gi = Integer.valueOf(v[0]); 1208 if (longTag.containsKey(gi)) 1209 continue; 1210 int c = e.getKey().intValue(); 1211 boolean skip = true; 1212 for (int k = 0; k < rg.length; k += 2) { 1213 if (c >= rg[k] && c <= rg[k + 1]) { 1214 skip = false; 1215 break; 1216 } 1217 } 1218 if (!skip) 1219 longTag.put(gi, includeMetrics ? new int[]{v[0], v[1], c} : null); 1220 } 1221 } 1222 } 1223 1224 protected void addRangeUni(HashSet<Integer> longTag, boolean subsetp) { 1225 if (!subsetp && (subsetRanges != null || directoryOffset > 0)) { 1226 int[] rg = subsetRanges == null && directoryOffset > 0 ? new int[]{0, 0xffff} : compactRanges(subsetRanges); 1227 HashMap<Integer, int[]> usemap; 1228 if (!fontSpecific && cmap31 != null) 1229 usemap = cmap31; 1230 else if (fontSpecific && cmap10 != null) 1231 usemap = cmap10; 1232 else if (cmap31 != null) 1233 usemap = cmap31; 1234 else 1235 usemap = cmap10; 1236 for (Map.Entry<Integer, int[]> e: usemap.entrySet()) { 1237 int[] v = e.getValue(); 1238 Integer gi = Integer.valueOf(v[0]); 1239 if (longTag.contains(gi)) 1240 continue; 1241 int c = e.getKey().intValue(); 1242 boolean skip = true; 1243 for (int k = 0; k < rg.length; k += 2) { 1244 if (c >= rg[k] && c <= rg[k + 1]) { 1245 skip = false; 1246 break; 1247 } 1248 } 1249 if (!skip) 1250 longTag.add(gi); 1251 } 1252 } 1253 } 1254 1255 /** Outputs to the writer the font dictionaries and streams. 1256 * @param writer the writer for this document 1257 * @param ref the font indirect reference 1258 * @param params several parameters that depend on the font type 1259 * @throws IOException on error 1260 * @throws DocumentException error in generating the object 1261 */ 1262 @Override 1263 void writeFont(PdfWriter writer, PdfIndirectReference ref, Object params[]) throws DocumentException, IOException { 1264 int firstChar = ((Integer)params[0]).intValue(); 1265 int lastChar = ((Integer)params[1]).intValue(); 1266 byte shortTag[] = (byte[])params[2]; 1267 boolean subsetp = ((Boolean)params[3]).booleanValue() && subset; 1268 1269 if (!subsetp) { 1270 firstChar = 0; 1271 lastChar = shortTag.length - 1; 1272 for (int k = 0; k < shortTag.length; ++k) 1273 shortTag[k] = 1; 1274 } 1275 PdfIndirectReference ind_font = null; 1276 PdfObject pobj = null; 1277 PdfIndirectObject obj = null; 1278 String subsetPrefix = ""; 1279 if (embedded) { 1280 if (cff) { 1281 pobj = new StreamFont(readCffFont(), "Type1C", compressionLevel); 1282 obj = writer.addToBody(pobj); 1283 ind_font = obj.getIndirectReference(); 1284 } 1285 else { 1286 if (subsetp) 1287 subsetPrefix = createSubsetPrefix(); 1288 HashSet<Integer> glyphs = new HashSet<Integer>(); 1289 for (int k = firstChar; k <= lastChar; ++k) { 1290 if (shortTag[k] != 0) { 1291 int[] metrics = null; 1292 if (specialMap != null) { 1293 int[] cd = GlyphList.nameToUnicode(differences[k]); 1294 if (cd != null) 1295 metrics = getMetricsTT(cd[0]); 1296 } 1297 else { 1298 if (fontSpecific) 1299 metrics = getMetricsTT(k); 1300 else 1301 metrics = getMetricsTT(unicodeDifferences[k]); 1302 } 1303 if (metrics != null) 1304 glyphs.add(Integer.valueOf(metrics[0])); 1305 } 1306 } 1307 addRangeUni(glyphs, subsetp); 1308 byte[] b = null; 1309 if (subsetp || directoryOffset != 0 || subsetRanges != null) { 1310 TrueTypeFontSubSet sb = new TrueTypeFontSubSet(fileName, new RandomAccessFileOrArray(rf), glyphs, directoryOffset, true, !subsetp); 1311 b = sb.process(); 1312 } 1313 else { 1314 b = getFullFont(); 1315 } 1316 int lengths[] = new int[]{b.length}; 1317 pobj = new StreamFont(b, lengths, compressionLevel); 1318 obj = writer.addToBody(pobj); 1319 ind_font = obj.getIndirectReference(); 1320 } 1321 } 1322 pobj = getFontDescriptor(ind_font, subsetPrefix, null); 1323 if (pobj != null){ 1324 obj = writer.addToBody(pobj); 1325 ind_font = obj.getIndirectReference(); 1326 } 1327 pobj = getFontBaseType(ind_font, subsetPrefix, firstChar, lastChar, shortTag); 1328 writer.addToBody(pobj, ref); 1329 } 1330 1331 /** 1332 * If this font file is using the Compact Font File Format, then this method 1333 * will return the raw bytes needed for the font stream. If this method is 1334 * ever made public: make sure to add a test if (cff == true). 1335 * @return a byte array 1336 * @since 2.1.3 1337 */ 1338 protected byte[] readCffFont() throws IOException { 1339 RandomAccessFileOrArray rf2 = new RandomAccessFileOrArray(rf); 1340 byte b[] = new byte[cffLength]; 1341 try { 1342 rf2.reOpen(); 1343 rf2.seek(cffOffset); 1344 rf2.readFully(b); 1345 } 1346 finally { 1347 try { 1348 rf2.close(); 1349 } 1350 catch (Exception e) { 1351 // empty on purpose 1352 } 1353 } 1354 return b; 1355 } 1356 1357 /** 1358 * Returns a PdfStream object with the full font program. 1359 * @return a PdfStream with the font program 1360 * @since 2.1.3 1361 */ 1362 @Override 1363 public PdfStream getFullFontStream() throws IOException, DocumentException { 1364 if (cff) { 1365 return new StreamFont(readCffFont(), "Type1C", compressionLevel); 1366 } 1367 else { 1368 byte[] b = getFullFont(); 1369 int lengths[] = new int[]{b.length}; 1370 return new StreamFont(b, lengths, compressionLevel); 1371 } 1372 } 1373 1374 /** Gets the font parameter identified by <CODE>key</CODE>. Valid values 1375 * for <CODE>key</CODE> are <CODE>ASCENT</CODE>, <CODE>CAPHEIGHT</CODE>, <CODE>DESCENT</CODE> 1376 * and <CODE>ITALICANGLE</CODE>. 1377 * @param key the parameter to be extracted 1378 * @param fontSize the font size in points 1379 * @return the parameter in points 1380 */ 1381 @Override 1382 public float getFontDescriptor(int key, float fontSize) { 1383 switch (key) { 1384 case ASCENT: 1385 return os_2.sTypoAscender * fontSize / head.unitsPerEm; 1386 case CAPHEIGHT: 1387 return os_2.sCapHeight * fontSize / head.unitsPerEm; 1388 case DESCENT: 1389 return os_2.sTypoDescender * fontSize / head.unitsPerEm; 1390 case ITALICANGLE: 1391 return (float)italicAngle; 1392 case BBOXLLX: 1393 return fontSize * head.xMin / head.unitsPerEm; 1394 case BBOXLLY: 1395 return fontSize * head.yMin / head.unitsPerEm; 1396 case BBOXURX: 1397 return fontSize * head.xMax / head.unitsPerEm; 1398 case BBOXURY: 1399 return fontSize * head.yMax / head.unitsPerEm; 1400 case AWT_ASCENT: 1401 return fontSize * hhea.Ascender / head.unitsPerEm; 1402 case AWT_DESCENT: 1403 return fontSize * hhea.Descender / head.unitsPerEm; 1404 case AWT_LEADING: 1405 return fontSize * hhea.LineGap / head.unitsPerEm; 1406 case AWT_MAXADVANCE: 1407 return fontSize * hhea.advanceWidthMax / head.unitsPerEm; 1408 case UNDERLINE_POSITION: 1409 return (underlinePosition - underlineThickness / 2) * fontSize / head.unitsPerEm; 1410 case UNDERLINE_THICKNESS: 1411 return underlineThickness * fontSize / head.unitsPerEm; 1412 case STRIKETHROUGH_POSITION: 1413 return os_2.yStrikeoutPosition * fontSize / head.unitsPerEm; 1414 case STRIKETHROUGH_THICKNESS: 1415 return os_2.yStrikeoutSize * fontSize / head.unitsPerEm; 1416 case SUBSCRIPT_SIZE: 1417 return os_2.ySubscriptYSize * fontSize / head.unitsPerEm; 1418 case SUBSCRIPT_OFFSET: 1419 return -os_2.ySubscriptYOffset * fontSize / head.unitsPerEm; 1420 case SUPERSCRIPT_SIZE: 1421 return os_2.ySuperscriptYSize * fontSize / head.unitsPerEm; 1422 case SUPERSCRIPT_OFFSET: 1423 return os_2.ySuperscriptYOffset * fontSize / head.unitsPerEm; 1424 case WEIGHT_CLASS: 1425 return os_2.usWeightClass; 1426 case WIDTH_CLASS: 1427 return os_2.usWidthClass; 1428 } 1429 return 0; 1430 } 1431 1432 /** Gets the glyph index and metrics for a character. 1433 * @param c the character 1434 * @return an <CODE>int</CODE> array with {glyph index, width} 1435 */ 1436 public int[] getMetricsTT(int c) { 1437 if (cmapExt != null) 1438 return cmapExt.get(Integer.valueOf(c)); 1439 if (!fontSpecific && cmap31 != null) 1440 return cmap31.get(Integer.valueOf(c)); 1441 if (fontSpecific && cmap10 != null) 1442 return cmap10.get(Integer.valueOf(c)); 1443 if (cmap31 != null) 1444 return cmap31.get(Integer.valueOf(c)); 1445 if (cmap10 != null) 1446 return cmap10.get(Integer.valueOf(c)); 1447 return null; 1448 } 1449 1450 /** Gets the postscript font name. 1451 * @return the postscript font name 1452 */ 1453 @Override 1454 public String getPostscriptFontName() { 1455 return fontName; 1456 } 1457 1458 /** Gets the code pages supported by the font. 1459 * @return the code pages supported by the font 1460 */ 1461 @Override 1462 public String[] getCodePagesSupported() { 1463 long cp = ((long)os_2.ulCodePageRange2 << 32) + (os_2.ulCodePageRange1 & 0xffffffffL); 1464 int count = 0; 1465 long bit = 1; 1466 for (int k = 0; k < 64; ++k) { 1467 if ((cp & bit) != 0 && codePages[k] != null) 1468 ++count; 1469 bit <<= 1; 1470 } 1471 String ret[] = new String[count]; 1472 count = 0; 1473 bit = 1; 1474 for (int k = 0; k < 64; ++k) { 1475 if ((cp & bit) != 0 && codePages[k] != null) 1476 ret[count++] = codePages[k]; 1477 bit <<= 1; 1478 } 1479 return ret; 1480 } 1481 1482 /** Gets the full name of the font. If it is a True Type font 1483 * each array element will have {Platform ID, Platform Encoding ID, 1484 * Language ID, font name}. The interpretation of this values can be 1485 * found in the Open Type specification, chapter 2, in the 'name' table.<br> 1486 * For the other fonts the array has a single element with {"", "", "", 1487 * font name}. 1488 * @return the full name of the font 1489 */ 1490 @Override 1491 public String[][] getFullFontName() { 1492 return fullName; 1493 } 1494 1495 /** Gets all the entries of the Names-Table. If it is a True Type font 1496 * each array element will have {Name ID, Platform ID, Platform Encoding ID, 1497 * Language ID, font name}. The interpretation of this values can be 1498 * found in the Open Type specification, chapter 2, in the 'name' table.<br> 1499 * For the other fonts the array has a single element with {"", "", "", 1500 * font name}. 1501 * @return the full name of the font 1502 */ 1503 @Override 1504 public String[][] getAllNameEntries() { 1505 return allNameEntries; 1506 } 1507 1508 /** Gets the family name of the font. If it is a True Type font 1509 * each array element will have {Platform ID, Platform Encoding ID, 1510 * Language ID, font name}. The interpretation of this values can be 1511 * found in the Open Type specification, chapter 2, in the 'name' table.<br> 1512 * For the other fonts the array has a single element with {"", "", "", 1513 * font name}. 1514 * @return the family name of the font 1515 */ 1516 @Override 1517 public String[][] getFamilyFontName() { 1518 return familyName; 1519 } 1520 1521 /** Checks if the font has any kerning pairs. 1522 * @return <CODE>true</CODE> if the font has any kerning pairs 1523 */ 1524 @Override 1525 public boolean hasKernPairs() { 1526 return kerning.size() > 0; 1527 } 1528 1529 /** 1530 * Sets the font name that will appear in the pdf font dictionary. 1531 * Use with care as it can easily make a font unreadable if not embedded. 1532 * @param name the new font name 1533 */ 1534 @Override 1535 public void setPostscriptFontName(String name) { 1536 fontName = name; 1537 } 1538 1539 /** 1540 * Sets the kerning between two Unicode chars. 1541 * @param char1 the first char 1542 * @param char2 the second char 1543 * @param kern the kerning to apply in normalized 1000 units 1544 * @return <code>true</code> if the kerning was applied, <code>false</code> otherwise 1545 */ 1546 @Override 1547 public boolean setKerning(int char1, int char2, int kern) { 1548 int metrics[] = getMetricsTT(char1); 1549 if (metrics == null) 1550 return false; 1551 int c1 = metrics[0]; 1552 metrics = getMetricsTT(char2); 1553 if (metrics == null) 1554 return false; 1555 int c2 = metrics[0]; 1556 kerning.put((c1 << 16) + c2, kern); 1557 return true; 1558 } 1559 1560 @Override 1561 protected int[] getRawCharBBox(int c, String name) { 1562 HashMap<Integer, int[]> map = null; 1563 if (name == null || cmap31 == null) 1564 map = cmap10; 1565 else 1566 map = cmap31; 1567 if (map == null) 1568 return null; 1569 int metric[] = map.get(Integer.valueOf(c)); 1570 if (metric == null || bboxes == null) 1571 return null; 1572 return bboxes[metric[0]]; 1573 } 1574}