1 51 package org.apache.fop.fonts; 52 53 import org.apache.fop.messaging.MessageHandler; 54 55 import java.io.IOException ; 56 import java.util.Iterator ; 57 import java.util.Map ; 58 import java.util.List ; 59 60 68 public class TTFSubSetFile extends TTFFile { 69 byte[] output = null; 70 int realSize = 0; 71 int currentPos = 0; 72 73 77 int cvtDirOffset = 0; 78 int fpgmDirOffset = 0; 79 int glyfDirOffset = 0; 80 int headDirOffset = 0; 81 int hheaDirOffset = 0; 82 int hmtxDirOffset = 0; 83 int locaDirOffset = 0; 84 int maxpDirOffset = 0; 85 int prepDirOffset = 0; 86 87 int checkSumAdjustmentOffset = 0; 88 int locaOffset = 0; 89 90 93 private void init(int size) { 94 output = new byte[size]; 95 realSize = 0; 96 currentPos = 0; 97 98 } 100 101 104 private void createDirectory() { 105 int numTables = 9; 106 writeByte((byte)0); 108 writeByte((byte)1); 109 writeByte((byte)0); 110 writeByte((byte)0); 111 realSize += 4; 112 113 writeUShort(numTables); 114 realSize += 2; 115 116 int maxPow = maxPow2(numTables); 118 int searchRange = maxPow * 16; 119 writeUShort(searchRange); 120 realSize += 2; 121 122 writeUShort(maxPow); 123 realSize += 2; 124 125 writeUShort((numTables * 16) - searchRange); 126 realSize += 2; 127 128 writeString("cvt "); 130 cvtDirOffset = currentPos; 131 currentPos += 12; 132 realSize += 16; 133 134 if (hasFpgm()) { 135 writeString("fpgm"); 136 fpgmDirOffset = currentPos; 137 currentPos += 12; 138 realSize += 16; 139 } 140 141 writeString("glyf"); 142 glyfDirOffset = currentPos; 143 currentPos += 12; 144 realSize += 16; 145 146 writeString("head"); 147 headDirOffset = currentPos; 148 currentPos += 12; 149 realSize += 16; 150 151 writeString("hhea"); 152 hheaDirOffset = currentPos; 153 currentPos += 12; 154 realSize += 16; 155 156 writeString("hmtx"); 157 hmtxDirOffset = currentPos; 158 currentPos += 12; 159 realSize += 16; 160 161 writeString("loca"); 162 locaDirOffset = currentPos; 163 currentPos += 12; 164 realSize += 16; 165 166 writeString("maxp"); 167 maxpDirOffset = currentPos; 168 currentPos += 12; 169 realSize += 16; 170 171 writeString("prep"); 172 prepDirOffset = currentPos; 173 currentPos += 12; 174 realSize += 16; 175 } 176 177 178 181 private void createCvt(FontFileReader in) throws IOException { 182 TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("cvt "); 183 if (entry != null) { 184 pad4(); 185 seek_tab(in, "cvt ", 0); 186 System.arraycopy(in.getBytes((int)entry.offset, (int)entry.length), 187 0, output, currentPos, (int)entry.length); 188 189 int checksum = getCheckSum(currentPos, (int)entry.length); 190 writeULong(cvtDirOffset, checksum); 191 writeULong(cvtDirOffset + 4, currentPos); 192 writeULong(cvtDirOffset + 8, (int)entry.length); 193 currentPos += (int)entry.length; 194 realSize += (int)entry.length; 195 } else { 196 throw new IOException ("Can't find cvt table"); 197 } 198 } 199 200 201 private boolean hasFpgm() { 202 return (dirTabs.get("fpgm") != null); 203 } 204 205 208 private void createFpgm(FontFileReader in) throws IOException { 209 TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("fpgm"); 210 if (entry != null) { 211 pad4(); 212 seek_tab(in, "fpgm", 0); 213 System.arraycopy(in.getBytes((int)entry.offset, (int)entry.length), 214 0, output, currentPos, (int)entry.length); 215 int checksum = getCheckSum(currentPos, (int)entry.length); 216 writeULong(fpgmDirOffset, checksum); 217 writeULong(fpgmDirOffset + 4, currentPos); 218 writeULong(fpgmDirOffset + 8, (int)entry.length); 219 currentPos += (int)entry.length; 220 realSize += (int)entry.length; 221 } else { 222 } 225 } 226 227 228 229 232 private void createLoca(int size) throws IOException { 233 pad4(); 234 locaOffset = currentPos; 235 writeULong(locaDirOffset + 4, currentPos); 236 writeULong(locaDirOffset + 8, size * 4 + 4); 237 currentPos += size * 4 + 4; 238 realSize += size * 4 + 4; 239 } 240 241 242 246 private void createMaxp(FontFileReader in, int size) throws IOException { 247 TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("maxp"); 248 if (entry != null) { 249 pad4(); 250 seek_tab(in, "maxp", 0); 251 System.arraycopy(in.getBytes((int)entry.offset, (int)entry.length), 252 0, output, currentPos, (int)entry.length); 253 writeUShort(currentPos + 4, size); 254 255 int checksum = getCheckSum(currentPos, (int)entry.length); 256 writeULong(maxpDirOffset, checksum); 257 writeULong(maxpDirOffset + 4, currentPos); 258 writeULong(maxpDirOffset + 8, (int)entry.length); 259 currentPos += (int)entry.length; 260 realSize += (int)entry.length; 261 } else { 262 throw new IOException ("Can't find maxp table"); 263 } 264 } 265 266 267 270 private void createPrep(FontFileReader in) throws IOException { 271 TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("prep"); 272 if (entry != null) { 273 pad4(); 274 seek_tab(in, "prep", 0); 275 System.arraycopy(in.getBytes((int)entry.offset, (int)entry.length), 276 0, output, currentPos, (int)entry.length); 277 278 int checksum = getCheckSum(currentPos, (int)entry.length); 279 writeULong(prepDirOffset, checksum); 280 writeULong(prepDirOffset + 4, currentPos); 281 writeULong(prepDirOffset + 8, (int)entry.length); 282 currentPos += (int)entry.length; 283 realSize += (int)entry.length; 284 } else { 285 throw new IOException ("Can't find prep table"); 286 } 287 } 288 289 290 294 private void createHhea(FontFileReader in, int size) throws IOException { 295 TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("hhea"); 296 if (entry != null) { 297 pad4(); 298 seek_tab(in, "hhea", 0); 299 System.arraycopy(in.getBytes((int)entry.offset, (int)entry.length), 300 0, output, currentPos, (int)entry.length); 301 writeUShort((int)entry.length + currentPos - 2, size); 302 303 int checksum = getCheckSum(currentPos, (int)entry.length); 304 writeULong(hheaDirOffset, checksum); 305 writeULong(hheaDirOffset + 4, currentPos); 306 writeULong(hheaDirOffset + 8, (int)entry.length); 307 currentPos += (int)entry.length; 308 realSize += (int)entry.length; 309 } else { 310 throw new IOException ("Can't find hhea table"); 311 } 312 } 313 314 315 321 private void createHead(FontFileReader in) throws IOException { 322 TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("head"); 323 if (entry != null) { 324 pad4(); 325 seek_tab(in, "head", 0); 326 System.arraycopy(in.getBytes((int)entry.offset, (int)entry.length), 327 0, output, currentPos, (int)entry.length); 328 329 checkSumAdjustmentOffset = currentPos + 8; 330 output[currentPos + 8] = 0; output[currentPos + 9] = 0; 332 output[currentPos + 10] = 0; 333 output[currentPos + 11] = 0; 334 output[currentPos + 50] = 0; output[currentPos + 51] = 1; 337 int checksum = getCheckSum(currentPos, (int)entry.length); 338 writeULong(headDirOffset, checksum); 339 writeULong(headDirOffset + 4, currentPos); 340 writeULong(headDirOffset + 8, (int)entry.length); 341 342 currentPos += (int)entry.length; 343 realSize += (int)entry.length; 344 } else { 345 throw new IOException ("Can't find head table"); 346 } 347 } 348 349 350 353 private void createGlyf(FontFileReader in, 354 Map glyphs) throws IOException { 355 TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf"); 356 int size = 0; 357 int start = 0; 358 int endOffset = 0; if (entry != null) { 360 pad4(); 361 start = currentPos; 362 363 367 int[] origIndexes = new int[glyphs.size()]; 368 369 for (Iterator e = glyphs.keySet().iterator(); e.hasNext(); ) { 370 Integer origIndex = (Integer )e.next(); 371 Integer subsetIndex = (Integer )glyphs.get(origIndex); 372 origIndexes[subsetIndex.intValue()] = origIndex.intValue(); 373 } 374 375 for (int i=0;i<origIndexes.length;i++) { 376 int glyphLength = 0; 377 int nextOffset = 0; 378 int origGlyphIndex = origIndexes[i]; 379 if (origGlyphIndex >= (mtx_tab.length - 1)) { 380 nextOffset = (int)lastLoca; 381 } 382 else { 383 nextOffset = 384 (int)mtx_tab[origGlyphIndex + 1].offset; 385 } 386 glyphLength = nextOffset 387 - (int)mtx_tab[origGlyphIndex].offset; 388 389 System.arraycopy(in.getBytes((int)entry.offset + 391 (int)mtx_tab[origGlyphIndex].offset, 392 glyphLength), 393 0, output, currentPos, glyphLength); 394 395 396 writeULong(locaOffset + i * 4, currentPos - start); 398 if ((currentPos - start + glyphLength) > endOffset) { 399 endOffset = (currentPos - start + glyphLength); 400 } 401 402 currentPos += glyphLength; 403 realSize += glyphLength; 404 405 } 406 407 size = currentPos - start; 408 409 int checksum = getCheckSum(start, size); 410 writeULong(glyfDirOffset, checksum); 411 writeULong(glyfDirOffset + 4, start); 412 writeULong(glyfDirOffset + 8, size); 413 currentPos += 12; 414 realSize += 12; 415 416 writeULong(locaOffset + glyphs.size() * 4, endOffset); 418 419 checksum = getCheckSum(locaOffset, glyphs.size() * 4 + 4); 420 writeULong(locaDirOffset, checksum); 421 } else { 422 throw new IOException ("Can't find glyf table"); 423 } 424 } 425 426 427 433 private void createHmtx(FontFileReader in, 434 Map glyphs) throws IOException { 435 TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("hmtx"); 436 437 int longHorMetricSize = glyphs.size() * 2; 438 int leftSideBearingSize = glyphs.size() * 2; 439 int hmtxSize = longHorMetricSize + leftSideBearingSize; 440 441 if (entry != null) { 442 pad4(); 443 int offset = (int)entry.offset; 444 for (Iterator e = glyphs.keySet().iterator(); e.hasNext(); ) { 445 Integer origIndex = (Integer )e.next(); 446 Integer subsetIndex = (Integer )glyphs.get(origIndex); 447 448 writeUShort(currentPos + subsetIndex.intValue() * 4, 449 mtx_tab[origIndex.intValue()].wx); 450 writeUShort(currentPos + subsetIndex.intValue() * 4 + 2, 451 mtx_tab[origIndex.intValue()].lsb); 452 } 453 454 int checksum = getCheckSum(currentPos, hmtxSize); 455 writeULong(hmtxDirOffset, checksum); 456 writeULong(hmtxDirOffset + 4, currentPos); 457 writeULong(hmtxDirOffset + 8, hmtxSize); 458 currentPos += hmtxSize; 459 realSize += hmtxSize; 460 } else { 461 throw new IOException ("Can't find hmtx table"); 462 } 463 } 464 465 469 private List getIncludedGlyphs(FontFileReader in, int glyphOffset, 470 Integer glyphIdx) throws IOException { 471 List ret = new java.util.ArrayList (); 472 ret.add(glyphIdx); 473 int offset = glyphOffset + (int)mtx_tab[glyphIdx.intValue()].offset 474 + 10; 475 Integer compositeIdx = null; 476 int flags = 0; 477 boolean moreComposites = true; 478 while (moreComposites) { 479 flags = in.readTTFUShort(offset); 480 compositeIdx = new Integer (in.readTTFUShort(offset + 2)); 481 ret.add(compositeIdx); 482 483 offset += 4; 484 if ((flags & 1) > 0) { 485 offset += 4; 487 } else { 488 offset += 2; 489 } 490 491 if ((flags & 8) > 0) { 492 offset += 2; } 494 else if ((flags & 64) > 0) { 495 offset += 4; } 497 else if ((flags & 128) > 0) { 498 offset += 8; } 500 if ((flags & 32) > 0) { 501 moreComposites = true; 502 } 503 else { 504 moreComposites = false; 505 } 506 } 507 508 return ret; 509 } 510 511 512 516 private void remapComposite(FontFileReader in, Map glyphs, 517 int glyphOffset, 518 Integer glyphIdx) throws IOException { 519 int offset = glyphOffset + (int)mtx_tab[glyphIdx.intValue()].offset 520 + 10; 521 522 Integer compositeIdx = null; 523 int flags = 0; 524 boolean moreComposites = true; 525 526 while (moreComposites) { 527 flags = in.readTTFUShort(offset); 528 compositeIdx = new Integer (in.readTTFUShort(offset + 2)); 529 Integer newIdx = (Integer )glyphs.get(compositeIdx); 530 if (newIdx == null) { 531 MessageHandler.error("An embedded font " 534 + "contains bad glyph data. " 535 + "Characters might not display " 536 + "correctly."); 537 moreComposites = false; 538 continue; 539 } 540 541 in.writeTTFUShort(offset + 2, newIdx.intValue()); 542 543 offset += 4; 544 545 if ((flags & 1) > 0) { 546 offset += 4; 548 } else { 549 offset += 2; 550 } 551 552 if ((flags & 8) > 0) { 553 offset += 2; } else if ((flags & 64) > 0) { 555 offset += 4; } else if ((flags & 128) > 0) { 557 offset += 8; } 559 560 if ((flags & 32) > 0) { 561 moreComposites = true; 562 } 563 else { 564 moreComposites = false; 565 } 566 } 567 } 568 569 570 575 private void scanGlyphs(FontFileReader in, 576 Map glyphs) throws IOException { 577 TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf"); 578 Map newComposites = null; 579 Map allComposites = new java.util.HashMap (); 580 581 int newIndex = glyphs.size(); 582 583 if (entry != null) { 584 while (newComposites == null || newComposites.size() > 0) { 585 newComposites = new java.util.HashMap (); 587 588 for (Iterator e = glyphs.keySet().iterator(); e.hasNext(); ) { 589 Integer origIndex = (Integer )e.next(); 590 591 if (in.readTTFShort(entry.offset 592 + mtx_tab[origIndex.intValue()].offset) < 0) { 593 allComposites.put(origIndex, glyphs.get(origIndex)); 595 List composites = 596 getIncludedGlyphs(in, (int)entry.offset, 597 origIndex); 598 599 for (int i = 0; i < composites.size(); i++ ) { 603 Integer cIdx = (Integer )composites.get(i); 604 if (glyphs.get(cIdx) == null 605 && newComposites.get(cIdx) == null) { 606 newComposites.put(cIdx, 607 new Integer (newIndex)); 608 newIndex++; 609 } 610 } 611 } 612 } 613 614 for (Iterator m = newComposites.keySet().iterator(); m.hasNext(); ) { 616 Integer im = (Integer )m.next(); 617 glyphs.put(im, newComposites.get(im)); 618 } 619 } 620 621 623 for (Iterator ce = allComposites.keySet().iterator(); ce.hasNext(); ) { 624 remapComposite(in, glyphs, (int)entry.offset, 625 (Integer )ce.next()); 626 } 627 628 } else { 629 throw new IOException ("Can't find glyph table"); 630 } 631 } 632 633 634 635 639 640 public byte[] readFont(FontFileReader in, String name, 641 Map glyphs) throws IOException { 642 643 647 if (!checkTTC(in, name, false)) { 648 throw new IOException ("Failed to read font"); 649 } 650 651 output = new byte[in.getFileSize()]; 652 653 readDirTabs(in); 654 readFontHeader(in); 655 getNumGlyphs(in); 656 readHorizontalHeader(in); 657 readHorizontalMetrics(in); 658 readIndexToLocation(in); 659 660 scanGlyphs(in, glyphs); 661 662 createDirectory(); 664 createHead(in); 665 createHhea(in, glyphs.size()); createHmtx(in, glyphs); createMaxp(in, glyphs.size()); 669 try { 670 createCvt(in); } catch (IOException ex) { 672 MessageHandler.errorln("TrueType warning: " + ex.getMessage()); 674 } 675 676 try { 677 createFpgm(in); } catch (IOException ex) { 679 MessageHandler.errorln("TrueType warning: " + ex.getMessage()); 681 } 682 683 try { 684 createPrep(in); } catch (IOException ex) { 686 MessageHandler.errorln("TrueType warning: " + ex.getMessage()); 688 } 689 690 try { 691 createLoca(glyphs.size()); } catch (IOException ex) { 693 MessageHandler.errorln("TrueType warning: " + ex.getMessage()); 695 } 696 697 try { 698 createGlyf(in, glyphs); 699 } catch (IOException ex) { 700 MessageHandler.errorln("TrueType warning: " + ex.getMessage()); 702 } 703 704 pad4(); 705 createCheckSumAdjustment(); 706 707 byte[] ret = new byte[realSize]; 708 System.arraycopy(output, 0, ret, 0, realSize); 709 710 return ret; 711 } 712 713 718 private int writeString(String str) { 719 int length = 0; 720 try { 721 byte[] buf = str.getBytes("ISO-8859-1"); 722 System.arraycopy(buf, 0, output, currentPos, buf.length); 723 length = buf.length; 724 currentPos += length; 725 } catch (Exception e) { 726 } 728 729 return length; 730 } 731 732 736 private void writeByte(byte b) { 737 output[currentPos++] = b; 738 } 739 740 744 private void writeUShort(int s) { 745 byte b1 = (byte)((s >> 8) & 0xff); 746 byte b2 = (byte)(s & 0xff); 747 writeByte(b1); 748 writeByte(b2); 749 } 750 751 755 private void writeUShort(int pos, int s) { 756 byte b1 = (byte)((s >> 8) & 0xff); 757 byte b2 = (byte)(s & 0xff); 758 output[pos] = b1; 759 output[pos + 1] = b2; 760 } 761 762 766 private void writeULong(int s) { 767 byte b1 = (byte)((s >> 24) & 0xff); 768 byte b2 = (byte)((s >> 16) & 0xff); 769 byte b3 = (byte)((s >> 8) & 0xff); 770 byte b4 = (byte)(s & 0xff); 771 writeByte(b1); 772 writeByte(b2); 773 writeByte(b3); 774 writeByte(b4); 775 } 776 777 781 private void writeULong(int pos, int s) { 782 byte b1 = (byte)((s >> 24) & 0xff); 783 byte b2 = (byte)((s >> 16) & 0xff); 784 byte b3 = (byte)((s >> 8) & 0xff); 785 byte b4 = (byte)(s & 0xff); 786 output[pos] = b1; 787 output[pos + 1] = b2; 788 output[pos + 2] = b3; 789 output[pos + 3] = b4; 790 } 791 792 795 private short readShort(int pos) { 796 int ret = readUShort(pos); 797 return (short)ret; 798 } 799 800 803 private int readUShort(int pos) { 804 int ret = (int)output[pos]; 805 if (ret < 0) { 806 ret += 256; 807 } 808 ret = ret << 8; 809 if ((int)output[pos + 1] < 0) { 810 ret |= (int)output[pos + 1] + 256; 811 } else 812 ret |= (int)output[pos + 1]; 813 814 return ret; 815 } 816 817 821 private void pad4() { 822 int padSize = currentPos % 4; 823 for (int i = 0; i < padSize; i++) { 824 output[currentPos++] = 0; 825 realSize++; 826 } 827 } 828 829 832 private int maxPow2(int max) { 833 int i = 0; 834 while (Math.pow(2, (double)i) < max) 835 i++; 836 837 return (i - 1); 838 } 839 840 private int log2(int num) { 841 return (int)(Math.log((double)num) / Math.log(2)); 842 } 843 844 845 private int getCheckSum(int start, int size) { 846 return (int)getLongCheckSum(start, size); 847 } 848 849 private long getLongCheckSum(int start, int size) { 850 int remainder = size % 4; 853 if (remainder != 0) { 854 size += remainder; 855 } 856 857 long sum = 0; 858 859 for (int i = 0; i < size; i += 4) { 860 int l = (int)(output[start + i] << 24); 861 l += (int)(output[start + i + 1] << 16); 862 l += (int)(output[start + i + 2] << 16); 863 l += (int)(output[start + i + 3] << 16); 864 sum += l; 865 if (sum > 0xffffffff) { 866 sum = sum - 0xffffffff; 867 } 868 } 869 870 return sum; 871 } 872 873 private void createCheckSumAdjustment() { 874 long sum = getLongCheckSum(0, realSize); 875 int checksum = (int)(0xb1b0afba - sum); 876 writeULong(checkSumAdjustmentOffset, checksum); 877 } 878 879 } 880 881 882 883 | Popular Tags |