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