1 17 18 19 20 package org.apache.fop.fonts.truetype; 21 22 import java.io.IOException ; 23 import java.util.Iterator ; 24 import java.util.Map ; 25 import java.util.List ; 26 27 28 36 public class TTFSubSetFile extends TTFFile { 37 38 private byte[] output = null; 39 private int realSize = 0; 40 private int currentPos = 0; 41 42 46 private int cvtDirOffset = 0; 47 private int fpgmDirOffset = 0; 48 private int glyfDirOffset = 0; 49 private int headDirOffset = 0; 50 private int hheaDirOffset = 0; 51 private int hmtxDirOffset = 0; 52 private int locaDirOffset = 0; 53 private int maxpDirOffset = 0; 54 private int prepDirOffset = 0; 55 56 private int checkSumAdjustmentOffset = 0; 57 private int locaOffset = 0; 58 59 62 private void init(int size) { 63 output = new byte[size]; 64 realSize = 0; 65 currentPos = 0; 66 67 } 69 70 73 private void createDirectory() { 74 int numTables = 9; 75 writeByte((byte)0); 77 writeByte((byte)1); 78 writeByte((byte)0); 79 writeByte((byte)0); 80 realSize += 4; 81 82 writeUShort(numTables); 83 realSize += 2; 84 85 int maxPow = maxPow2(numTables); 87 int searchRange = maxPow * 16; 88 writeUShort(searchRange); 89 realSize += 2; 90 91 writeUShort(maxPow); 92 realSize += 2; 93 94 writeUShort((numTables * 16) - searchRange); 95 realSize += 2; 96 97 writeString("cvt "); 99 cvtDirOffset = currentPos; 100 currentPos += 12; 101 realSize += 16; 102 103 if (hasFpgm()) { 104 writeString("fpgm"); 105 fpgmDirOffset = currentPos; 106 currentPos += 12; 107 realSize += 16; 108 } 109 110 writeString("glyf"); 111 glyfDirOffset = currentPos; 112 currentPos += 12; 113 realSize += 16; 114 115 writeString("head"); 116 headDirOffset = currentPos; 117 currentPos += 12; 118 realSize += 16; 119 120 writeString("hhea"); 121 hheaDirOffset = currentPos; 122 currentPos += 12; 123 realSize += 16; 124 125 writeString("hmtx"); 126 hmtxDirOffset = currentPos; 127 currentPos += 12; 128 realSize += 16; 129 130 writeString("loca"); 131 locaDirOffset = currentPos; 132 currentPos += 12; 133 realSize += 16; 134 135 writeString("maxp"); 136 maxpDirOffset = currentPos; 137 currentPos += 12; 138 realSize += 16; 139 140 writeString("prep"); 141 prepDirOffset = currentPos; 142 currentPos += 12; 143 realSize += 16; 144 } 145 146 147 150 private void createCvt(FontFileReader in) throws IOException { 151 TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("cvt "); 152 if (entry != null) { 153 pad4(); 154 seekTab(in, "cvt ", 0); 155 System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()), 156 0, output, currentPos, (int)entry.getLength()); 157 158 int checksum = getCheckSum(currentPos, (int)entry.getLength()); 159 writeULong(cvtDirOffset, checksum); 160 writeULong(cvtDirOffset + 4, currentPos); 161 writeULong(cvtDirOffset + 8, (int)entry.getLength()); 162 currentPos += (int)entry.getLength(); 163 realSize += (int)entry.getLength(); 164 } else { 165 throw new IOException ("Can't find cvt table"); 166 } 167 } 168 169 170 private boolean hasFpgm() { 171 return (dirTabs.get("fpgm") != null); 172 } 173 174 175 178 private void createFpgm(FontFileReader in) throws IOException { 179 TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("fpgm"); 180 if (entry != null) { 181 pad4(); 182 seekTab(in, "fpgm", 0); 183 System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()), 184 0, output, currentPos, (int)entry.getLength()); 185 int checksum = getCheckSum(currentPos, (int)entry.getLength()); 186 writeULong(fpgmDirOffset, checksum); 187 writeULong(fpgmDirOffset + 4, currentPos); 188 writeULong(fpgmDirOffset + 8, (int)entry.getLength()); 189 currentPos += (int)entry.getLength(); 190 realSize += (int)entry.getLength(); 191 } else { 192 } 195 } 196 197 198 199 202 private void createLoca(int size) throws IOException { 203 pad4(); 204 locaOffset = currentPos; 205 writeULong(locaDirOffset + 4, currentPos); 206 writeULong(locaDirOffset + 8, size * 4 + 4); 207 currentPos += size * 4 + 4; 208 realSize += size * 4 + 4; 209 } 210 211 212 216 private void createMaxp(FontFileReader in, int size) throws IOException { 217 TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("maxp"); 218 if (entry != null) { 219 pad4(); 220 seekTab(in, "maxp", 0); 221 System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()), 222 0, output, currentPos, (int)entry.getLength()); 223 writeUShort(currentPos + 4, size); 224 225 int checksum = getCheckSum(currentPos, (int)entry.getLength()); 226 writeULong(maxpDirOffset, checksum); 227 writeULong(maxpDirOffset + 4, currentPos); 228 writeULong(maxpDirOffset + 8, (int)entry.getLength()); 229 currentPos += (int)entry.getLength(); 230 realSize += (int)entry.getLength(); 231 } else { 232 throw new IOException ("Can't find maxp table"); 233 } 234 } 235 236 237 240 private void createPrep(FontFileReader in) throws IOException { 241 TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("prep"); 242 if (entry != null) { 243 pad4(); 244 seekTab(in, "prep", 0); 245 System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()), 246 0, output, currentPos, (int)entry.getLength()); 247 248 int checksum = getCheckSum(currentPos, (int)entry.getLength()); 249 writeULong(prepDirOffset, checksum); 250 writeULong(prepDirOffset + 4, currentPos); 251 writeULong(prepDirOffset + 8, (int)entry.getLength()); 252 currentPos += (int)entry.getLength(); 253 realSize += (int)entry.getLength(); 254 } else { 255 throw new IOException ("Can't find prep table"); 256 } 257 } 258 259 260 264 private void createHhea(FontFileReader in, int size) throws IOException { 265 TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("hhea"); 266 if (entry != null) { 267 pad4(); 268 seekTab(in, "hhea", 0); 269 System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()), 270 0, output, currentPos, (int)entry.getLength()); 271 writeUShort((int)entry.getLength() + currentPos - 2, size); 272 273 int checksum = getCheckSum(currentPos, (int)entry.getLength()); 274 writeULong(hheaDirOffset, checksum); 275 writeULong(hheaDirOffset + 4, currentPos); 276 writeULong(hheaDirOffset + 8, (int)entry.getLength()); 277 currentPos += (int)entry.getLength(); 278 realSize += (int)entry.getLength(); 279 } else { 280 throw new IOException ("Can't find hhea table"); 281 } 282 } 283 284 285 291 private void createHead(FontFileReader in) throws IOException { 292 TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("head"); 293 if (entry != null) { 294 pad4(); 295 seekTab(in, "head", 0); 296 System.arraycopy(in.getBytes((int)entry.getOffset(), (int)entry.getLength()), 297 0, output, currentPos, (int)entry.getLength()); 298 299 checkSumAdjustmentOffset = currentPos + 8; 300 output[currentPos + 8] = 0; output[currentPos + 9] = 0; 302 output[currentPos + 10] = 0; 303 output[currentPos + 11] = 0; 304 output[currentPos + 50] = 0; output[currentPos + 51] = 1; 307 int checksum = getCheckSum(currentPos, (int)entry.getLength()); 308 writeULong(headDirOffset, checksum); 309 writeULong(headDirOffset + 4, currentPos); 310 writeULong(headDirOffset + 8, (int)entry.getLength()); 311 312 currentPos += (int)entry.getLength(); 313 realSize += (int)entry.getLength(); 314 } else { 315 throw new IOException ("Can't find head table"); 316 } 317 } 318 319 320 323 private void createGlyf(FontFileReader in, 324 Map glyphs) throws IOException { 325 TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf"); 326 int size = 0; 327 int start = 0; 328 int endOffset = 0; if (entry != null) { 330 pad4(); 331 start = currentPos; 332 333 337 int[] origIndexes = new int[glyphs.size()]; 338 339 Iterator e = glyphs.keySet().iterator(); 340 while (e.hasNext()) { 341 Integer origIndex = (Integer )e.next(); 342 Integer subsetIndex = (Integer )glyphs.get(origIndex); 343 origIndexes[subsetIndex.intValue()] = origIndex.intValue(); 344 } 345 346 for (int i = 0; i < origIndexes.length; i++) { 347 int glyphLength = 0; 348 int nextOffset = 0; 349 int origGlyphIndex = origIndexes[i]; 350 if (origGlyphIndex >= (mtxTab.length - 1)) { 351 nextOffset = (int)lastLoca; 352 } else { 353 nextOffset = (int)mtxTab[origGlyphIndex + 1].getOffset(); 354 } 355 glyphLength = nextOffset - (int)mtxTab[origGlyphIndex].getOffset(); 356 357 System.arraycopy( 359 in.getBytes((int)entry.getOffset() + (int)mtxTab[origGlyphIndex].getOffset(), 360 glyphLength), 0, 361 output, currentPos, 362 glyphLength); 363 364 365 writeULong(locaOffset + i * 4, currentPos - start); 367 if ((currentPos - start + glyphLength) > endOffset) { 368 endOffset = (currentPos - start + glyphLength); 369 } 370 371 currentPos += glyphLength; 372 realSize += glyphLength; 373 374 } 375 376 size = currentPos - start; 377 378 int checksum = getCheckSum(start, size); 379 writeULong(glyfDirOffset, checksum); 380 writeULong(glyfDirOffset + 4, start); 381 writeULong(glyfDirOffset + 8, size); 382 currentPos += 12; 383 realSize += 12; 384 385 writeULong(locaOffset + glyphs.size() * 4, endOffset); 387 388 checksum = getCheckSum(locaOffset, glyphs.size() * 4 + 4); 389 writeULong(locaDirOffset, checksum); 390 } else { 391 throw new IOException ("Can't find glyf table"); 392 } 393 } 394 395 396 402 private void createHmtx(FontFileReader in, 403 Map glyphs) throws IOException { 404 TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("hmtx"); 405 406 int longHorMetricSize = glyphs.size() * 2; 407 int leftSideBearingSize = glyphs.size() * 2; 408 int hmtxSize = longHorMetricSize + leftSideBearingSize; 409 410 if (entry != null) { 411 pad4(); 412 Iterator e = glyphs.keySet().iterator(); 414 while (e.hasNext()) { 415 Integer origIndex = (Integer )e.next(); 416 Integer subsetIndex = (Integer )glyphs.get(origIndex); 417 418 writeUShort(currentPos + subsetIndex.intValue() * 4, 419 mtxTab[origIndex.intValue()].getWx()); 420 writeUShort(currentPos + subsetIndex.intValue() * 4 + 2, 421 mtxTab[origIndex.intValue()].getLsb()); 422 } 423 424 int checksum = getCheckSum(currentPos, hmtxSize); 425 writeULong(hmtxDirOffset, checksum); 426 writeULong(hmtxDirOffset + 4, currentPos); 427 writeULong(hmtxDirOffset + 8, hmtxSize); 428 currentPos += hmtxSize; 429 realSize += hmtxSize; 430 } else { 431 throw new IOException ("Can't find hmtx table"); 432 } 433 } 434 435 439 private List getIncludedGlyphs(FontFileReader in, int glyphOffset, 440 Integer glyphIdx) throws IOException { 441 List ret = new java.util.ArrayList (); 442 ret.add(glyphIdx); 443 int offset = glyphOffset + (int)mtxTab[glyphIdx.intValue()].getOffset() + 10; 444 Integer compositeIdx = null; 445 int flags = 0; 446 boolean moreComposites = true; 447 while (moreComposites) { 448 flags = in.readTTFUShort(offset); 449 compositeIdx = new Integer (in.readTTFUShort(offset + 2)); 450 ret.add(compositeIdx); 451 452 offset += 4; 453 if ((flags & 1) > 0) { 454 offset += 4; 456 } else { 457 offset += 2; 458 } 459 460 if ((flags & 8) > 0) { 461 offset += 2; } else if ((flags & 64) > 0) { 463 offset += 4; } else if ((flags & 128) > 0) { 465 offset += 8; } 467 468 if ((flags & 32) > 0) { 469 moreComposites = true; 470 } else { 471 moreComposites = false; 472 } 473 } 474 475 return ret; 476 } 477 478 479 483 private void remapComposite(FontFileReader in, Map glyphs, 484 int glyphOffset, 485 Integer glyphIdx) throws IOException { 486 int offset = glyphOffset + (int)mtxTab[glyphIdx.intValue()].getOffset() 487 + 10; 488 489 Integer compositeIdx = null; 490 int flags = 0; 491 boolean moreComposites = true; 492 493 while (moreComposites) { 494 flags = in.readTTFUShort(offset); 495 compositeIdx = new Integer (in.readTTFUShort(offset + 2)); 496 Integer newIdx = (Integer )glyphs.get(compositeIdx); 497 if (newIdx == null) { 498 moreComposites = false; 505 continue; 506 } 507 508 in.writeTTFUShort(offset + 2, newIdx.intValue()); 509 510 offset += 4; 511 512 if ((flags & 1) > 0) { 513 offset += 4; 515 } else { 516 offset += 2; 517 } 518 519 if ((flags & 8) > 0) { 520 offset += 2; } else if ((flags & 64) > 0) { 522 offset += 4; } else if ((flags & 128) > 0) { 524 offset += 8; } 526 527 if ((flags & 32) > 0) { 528 moreComposites = true; 529 } else { 530 moreComposites = false; 531 } 532 } 533 } 534 535 536 541 private void scanGlyphs(FontFileReader in, 542 Map glyphs) throws IOException { 543 TTFDirTabEntry entry = (TTFDirTabEntry)dirTabs.get("glyf"); 544 Map newComposites = null; 545 Map allComposites = new java.util.HashMap (); 546 547 int newIndex = glyphs.size(); 548 549 if (entry != null) { 550 while (newComposites == null || newComposites.size() > 0) { 551 newComposites = new java.util.HashMap (); 553 554 Iterator e = glyphs.keySet().iterator(); 555 while (e.hasNext()) { 556 Integer origIndex = (Integer )e.next(); 557 558 if (in.readTTFShort(entry.getOffset() 559 + mtxTab[origIndex.intValue()].getOffset()) < 0) { 560 allComposites.put(origIndex, glyphs.get(origIndex)); 562 List composites = 563 getIncludedGlyphs(in, (int)entry.getOffset(), 564 origIndex); 565 566 Iterator cps = composites.iterator(); 570 while (cps.hasNext()) { 571 Integer cIdx = (Integer )cps.next(); 572 if (glyphs.get(cIdx) == null 573 && newComposites.get(cIdx) == null) { 574 newComposites.put(cIdx, 575 new Integer (newIndex)); 576 newIndex++; 577 } 578 } 579 } 580 } 581 582 Iterator m = newComposites.keySet().iterator(); 584 while (m.hasNext()) { 585 Integer im = (Integer )m.next(); 586 glyphs.put(im, newComposites.get(im)); 587 } 588 } 589 590 Iterator ce = allComposites.keySet().iterator(); 592 while (ce.hasNext()) { 593 remapComposite(in, glyphs, (int)entry.getOffset(), 594 (Integer )ce.next()); 595 } 596 597 } else { 598 throw new IOException ("Can't find glyf table"); 599 } 600 } 601 602 603 604 614 public byte[] readFont(FontFileReader in, String name, 615 Map glyphs) throws IOException { 616 617 if (!checkTTC(in, name)) { 619 throw new IOException ("Failed to read font"); 620 } 621 622 output = new byte[in.getFileSize()]; 623 624 readDirTabs(in); 625 readFontHeader(in); 626 getNumGlyphs(in); 627 readHorizontalHeader(in); 628 readHorizontalMetrics(in); 629 readIndexToLocation(in); 630 631 scanGlyphs(in, glyphs); 632 633 createDirectory(); 635 createHead(in); 636 createHhea(in, glyphs.size()); createHmtx(in, glyphs); createMaxp(in, glyphs.size()); 640 try { 641 createCvt(in); } catch (IOException ex) { 643 } 646 647 try { 648 createFpgm(in); } catch (IOException ex) { 650 } 653 654 try { 655 createPrep(in); } catch (IOException ex) { 657 } 660 661 try { 662 createLoca(glyphs.size()); } catch (IOException ex) { 664 } 667 668 try { 669 createGlyf(in, glyphs); 670 } catch (IOException ex) { 671 } 674 675 pad4(); 676 createCheckSumAdjustment(); 677 678 byte[] ret = new byte[realSize]; 679 System.arraycopy(output, 0, ret, 0, realSize); 680 681 return ret; 682 } 683 684 689 private int writeString(String str) { 690 int length = 0; 691 try { 692 byte[] buf = str.getBytes("ISO-8859-1"); 693 System.arraycopy(buf, 0, output, currentPos, buf.length); 694 length = buf.length; 695 currentPos += length; 696 } catch (java.io.UnsupportedEncodingException e) { 697 } 699 700 return length; 701 } 702 703 707 private void writeByte(byte b) { 708 output[currentPos++] = b; 709 } 710 711 715 private void writeUShort(int s) { 716 byte b1 = (byte)((s >> 8) & 0xff); 717 byte b2 = (byte)(s & 0xff); 718 writeByte(b1); 719 writeByte(b2); 720 } 721 722 726 private void writeUShort(int pos, int s) { 727 byte b1 = (byte)((s >> 8) & 0xff); 728 byte b2 = (byte)(s & 0xff); 729 output[pos] = b1; 730 output[pos + 1] = b2; 731 } 732 733 737 private void writeULong(int s) { 738 byte b1 = (byte)((s >> 24) & 0xff); 739 byte b2 = (byte)((s >> 16) & 0xff); 740 byte b3 = (byte)((s >> 8) & 0xff); 741 byte b4 = (byte)(s & 0xff); 742 writeByte(b1); 743 writeByte(b2); 744 writeByte(b3); 745 writeByte(b4); 746 } 747 748 752 private void writeULong(int pos, int s) { 753 byte b1 = (byte)((s >> 24) & 0xff); 754 byte b2 = (byte)((s >> 16) & 0xff); 755 byte b3 = (byte)((s >> 8) & 0xff); 756 byte b4 = (byte)(s & 0xff); 757 output[pos] = b1; 758 output[pos + 1] = b2; 759 output[pos + 2] = b3; 760 output[pos + 3] = b4; 761 } 762 763 766 private short readShort(int pos) { 767 int ret = readUShort(pos); 768 return (short)ret; 769 } 770 771 774 private int readUShort(int pos) { 775 int ret = (int)output[pos]; 776 if (ret < 0) { 777 ret += 256; 778 } 779 ret = ret << 8; 780 if ((int)output[pos + 1] < 0) { 781 ret |= (int)output[pos + 1] + 256; 782 } else { 783 ret |= (int)output[pos + 1]; 784 } 785 786 return ret; 787 } 788 789 793 private void pad4() { 794 int padSize = currentPos % 4; 795 for (int i = 0; i < padSize; i++) { 796 output[currentPos++] = 0; 797 realSize++; 798 } 799 } 800 801 804 private int maxPow2(int max) { 805 int i = 0; 806 while (Math.pow(2, (double)i) < max) { 807 i++; 808 } 809 810 return (i - 1); 811 } 812 813 private int log2(int num) { 814 return (int)(Math.log((double)num) / Math.log(2)); 815 } 816 817 818 private int getCheckSum(int start, int size) { 819 return (int)getLongCheckSum(start, size); 820 } 821 822 private long getLongCheckSum(int start, int size) { 823 int remainder = size % 4; 826 if (remainder != 0) { 827 size += remainder; 828 } 829 830 long sum = 0; 831 832 for (int i = 0; i < size; i += 4) { 833 int l = (int)(output[start + i] << 24); 834 l += (int)(output[start + i + 1] << 16); 835 l += (int)(output[start + i + 2] << 16); 836 l += (int)(output[start + i + 3] << 16); 837 sum += l; 838 if (sum > 0xffffffff) { 839 sum = sum - 0xffffffff; 840 } 841 } 842 843 return sum; 844 } 845 846 private void createCheckSumAdjustment() { 847 long sum = getLongCheckSum(0, realSize); 848 int checksum = (int)(0xb1b0afba - sum); 849 writeULong(checkSumAdjustmentOffset, checksum); 850 } 851 852 } 853 854 855 856 | Popular Tags |