1
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
59 import com.lowagie.text.Document;
60 import com.lowagie.text.DocumentException;
61 import com.lowagie.text.ExceptionConverter;
62
63
67 class TrueTypeFont extends BaseFont {
68
69
71 static final String codePages[] = {
72 "1252 Latin 1",
73 "1250 Latin 2: Eastern Europe",
74 "1251 Cyrillic",
75 "1253 Greek",
76 "1254 Turkish",
77 "1255 Hebrew",
78 "1256 Arabic",
79 "1257 Windows Baltic",
80 "1258 Vietnamese",
81 null,
82 null,
83 null,
84 null,
85 null,
86 null,
87 null,
88 "874 Thai",
89 "932 JIS/Japan",
90 "936 Chinese: Simplified chars--PRC and Singapore",
91 "949 Korean Wansung",
92 "950 Chinese: Traditional chars--Taiwan and Hong Kong",
93 "1361 Korean Johab",
94 null,
95 null,
96 null,
97 null,
98 null,
99 null,
100 null,
101 "Macintosh Character Set (US Roman)",
102 "OEM Character Set",
103 "Symbol Character Set",
104 null,
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 "869 IBM Greek",
121 "866 MS-DOS Russian",
122 "865 MS-DOS Nordic",
123 "864 Arabic",
124 "863 MS-DOS Canadian French",
125 "862 Hebrew",
126 "861 MS-DOS Icelandic",
127 "860 MS-DOS Portuguese",
128 "857 IBM Turkish",
129 "855 IBM Cyrillic; primarily Russian",
130 "852 Latin 2",
131 "775 MS-DOS Baltic",
132 "737 Greek; former 437 G",
133 "708 Arabic; ASMO 708",
134 "850 WE/Latin 1",
135 "437 US"};
136
137 protected boolean justNames = false;
138
143 protected HashMap tables;
144
146 protected RandomAccessFileOrArray rf;
147
149 protected String fileName;
150
151 protected boolean cff = false;
152
153 protected int cffOffset;
154
155 protected int cffLength;
156
157
160 protected int directoryOffset;
161
164 protected String ttcIndex;
165
166 protected String style = "";
167
169 protected FontHeader head = new FontHeader();
170
172 protected HorizontalHeader hhea = new HorizontalHeader();
173
175 protected WindowsMetrics os_2 = new WindowsMetrics();
176
179 protected int GlyphWidths[];
180
181 protected int bboxes[][];
182
187 protected HashMap cmap10;
188
195 protected HashMap cmap31;
196
197 protected HashMap cmapExt;
198
199
205 protected IntHashtable kerning = new IntHashtable();
206
211 protected String fontName;
212
213
215 protected String fullName[][];
216
217
219 protected String allNameEntries[][];
220
221
223 protected String familyName[][];
224
231 protected double italicAngle;
232
234 protected boolean isFixedPitch = false;
235
236 protected int underlinePosition;
237
238 protected int underlineThickness;
239
240
242 protected static class FontHeader {
243
244 int flags;
245
246 int unitsPerEm;
247
248 short xMin;
249
250 short yMin;
251
252 short xMax;
253
254 short yMax;
255
256 int macStyle;
257 }
258
259
261 protected static class HorizontalHeader {
262
263 short Ascender;
264
265 short Descender;
266
267 short LineGap;
268
269 int advanceWidthMax;
270
271 short minLeftSideBearing;
272
273 short minRightSideBearing;
274
275 short xMaxExtent;
276
277 short caretSlopeRise;
278
279 short caretSlopeRun;
280
281 int numberOfHMetrics;
282 }
283
284
286 protected static class WindowsMetrics {
287
288 short xAvgCharWidth;
289
290 int usWeightClass;
291
292 int usWidthClass;
293
294 short fsType;
295
296 short ySubscriptXSize;
297
298 short ySubscriptYSize;
299
300 short ySubscriptXOffset;
301
302 short ySubscriptYOffset;
303
304 short ySuperscriptXSize;
305
306 short ySuperscriptYSize;
307
308 short ySuperscriptXOffset;
309
310 short ySuperscriptYOffset;
311
312 short yStrikeoutSize;
313
314 short yStrikeoutPosition;
315
316 short sFamilyClass;
317
318 byte panose[] = new byte[10];
319
320 byte achVendID[] = new byte[4];
321
322 int fsSelection;
323
324 int usFirstCharIndex;
325
326 int usLastCharIndex;
327
328 short sTypoAscender;
329
330 short sTypoDescender;
331
332 short sTypoLineGap;
333
334 int usWinAscent;
335
336 int usWinDescent;
337
338 int ulCodePageRange1;
339
340 int ulCodePageRange2;
341
342 int sCapHeight;
343 }
344
345
347 protected TrueTypeFont() {
348 }
349
350
360 TrueTypeFont(String ttFile, String enc, boolean emb, byte ttfAfm[], boolean justNames, boolean forceRead) throws DocumentException, IOException {
361 this.justNames = justNames;
362 String nameBase = getBaseName(ttFile);
363 String ttcName = getTTCName(nameBase);
364 if (nameBase.length() < ttFile.length()) {
365 style = ttFile.substring(nameBase.length());
366 }
367 encoding = enc;
368 embedded = emb;
369 fileName = ttcName;
370 fontType = FONT_TYPE_TT;
371 ttcIndex = "";
372 if (ttcName.length() < nameBase.length())
373 ttcIndex = nameBase.substring(ttcName.length() + 1);
374 if (fileName.toLowerCase().endsWith(".ttf") || fileName.toLowerCase().endsWith(".otf") || fileName.toLowerCase().endsWith(".ttc")) {
375 process(ttfAfm, forceRead);
376 if (!justNames && embedded && os_2.fsType == 2)
377 throw new DocumentException(fileName + style + " cannot be embedded due to licensing restrictions.");
378 }
379 else
380 throw new DocumentException(fileName + style + " is not a TTF, OTF or TTC font file.");
381 if (!encoding.startsWith("#"))
382 PdfEncodings.convertToBytes(" ", enc);
383 createEncoding();
384 }
385
386
392 protected static String getTTCName(String name) {
393 int idx = name.toLowerCase().indexOf(".ttc,");
394 if (idx < 0)
395 return name;
396 else
397 return name.substring(0, idx + 4);
398 }
399
400
401
406 void fillTables() throws DocumentException, IOException {
407 int table_location[];
408 table_location = (int[])tables.get("head");
409 if (table_location == null)
410 throw new DocumentException("Table 'head' does not exist in " + fileName + style);
411 rf.seek(table_location[0] + 16);
412 head.flags = rf.readUnsignedShort();
413 head.unitsPerEm = rf.readUnsignedShort();
414 rf.skipBytes(16);
415 head.xMin = rf.readShort();
416 head.yMin = rf.readShort();
417 head.xMax = rf.readShort();
418 head.yMax = rf.readShort();
419 head.macStyle = rf.readUnsignedShort();
420
421 table_location = (int[])tables.get("hhea");
422 if (table_location == null)
423 throw new DocumentException("Table 'hhea' does not exist " + fileName + style);
424 rf.seek(table_location[0] + 4);
425 hhea.Ascender = rf.readShort();
426 hhea.Descender = rf.readShort();
427 hhea.LineGap = rf.readShort();
428 hhea.advanceWidthMax = rf.readUnsignedShort();
429 hhea.minLeftSideBearing = rf.readShort();
430 hhea.minRightSideBearing = rf.readShort();
431 hhea.xMaxExtent = rf.readShort();
432 hhea.caretSlopeRise = rf.readShort();
433 hhea.caretSlopeRun = rf.readShort();
434 rf.skipBytes(12);
435 hhea.numberOfHMetrics = rf.readUnsignedShort();
436
437 table_location = (int[])tables.get("OS/2");
438 if (table_location == null)
439 throw new DocumentException("Table 'OS/2' does not exist in " + fileName + style);
440 rf.seek(table_location[0]);
441 int version = rf.readUnsignedShort();
442 os_2.xAvgCharWidth = rf.readShort();
443 os_2.usWeightClass = rf.readUnsignedShort();
444 os_2.usWidthClass = rf.readUnsignedShort();
445 os_2.fsType = rf.readShort();
446 os_2.ySubscriptXSize = rf.readShort();
447 os_2.ySubscriptYSize = rf.readShort();
448 os_2.ySubscriptXOffset = rf.readShort();
449 os_2.ySubscriptYOffset = rf.readShort();
450 os_2.ySuperscriptXSize = rf.readShort();
451 os_2.ySuperscriptYSize = rf.readShort();
452 os_2.ySuperscriptXOffset = rf.readShort();
453 os_2.ySuperscriptYOffset = rf.readShort();
454 os_2.yStrikeoutSize = rf.readShort();
455 os_2.yStrikeoutPosition = rf.readShort();
456 os_2.sFamilyClass = rf.readShort();
457 rf.readFully(os_2.panose);
458 rf.skipBytes(16);
459 rf.readFully(os_2.achVendID);
460 os_2.fsSelection = rf.readUnsignedShort();
461 os_2.usFirstCharIndex = rf.readUnsignedShort();
462 os_2.usLastCharIndex = rf.readUnsignedShort();
463 os_2.sTypoAscender = rf.readShort();
464 os_2.sTypoDescender = rf.readShort();
465 if (os_2.sTypoDescender > 0)
466 os_2.sTypoDescender = (short)(-os_2.sTypoDescender);
467 os_2.sTypoLineGap = rf.readShort();
468 os_2.usWinAscent = rf.readUnsignedShort();
469 os_2.usWinDescent = rf.readUnsignedShort();
470 os_2.ulCodePageRange1 = 0;
471 os_2.ulCodePageRange2 = 0;
472 if (version > 0) {
473 os_2.ulCodePageRange1 = rf.readInt();
474 os_2.ulCodePageRange2 = rf.readInt();
475 }
476 if (version > 1) {
477 rf.skipBytes(2);
478 os_2.sCapHeight = rf.readShort();
479 }
480 else
481 os_2.sCapHeight = (int)(0.7 * head.unitsPerEm);
482
483 table_location = (int[])tables.get("post");
484 if (table_location == null) {
485 italicAngle = -Math.atan2(hhea.caretSlopeRun, hhea.caretSlopeRise) * 180 / Math.PI;
486 return;
487 }
488 rf.seek(table_location[0] + 4);
489 short mantissa = rf.readShort();
490 int fraction = rf.readUnsignedShort();
491 italicAngle = mantissa + fraction / 16384.0d;
492 underlinePosition = rf.readShort();
493 underlineThickness = rf.readShort();
494 isFixedPitch = rf.readInt() != 0;
495 }
496
497
503 String getBaseFont() throws DocumentException, IOException {
504 int table_location[];
505 table_location = (int[])tables.get("name");
506 if (table_location == null)
507 throw new DocumentException("Table 'name' does not exist in " + fileName + style);
508 rf.seek(table_location[0] + 2);
509 int numRecords = rf.readUnsignedShort();
510 int startOfStorage = rf.readUnsignedShort();
511 for (int k = 0; k < numRecords; ++k) {
512 int platformID = rf.readUnsignedShort();
513 int platformEncodingID = rf.readUnsignedShort();
514 int languageID = rf.readUnsignedShort();
515 int nameID = rf.readUnsignedShort();
516 int length = rf.readUnsignedShort();
517 int offset = rf.readUnsignedShort();
518 if (nameID == 6) {
519 rf.seek(table_location[0] + startOfStorage + offset);
520 if (platformID == 0 || platformID == 3)
521 return readUnicodeString(length);
522 else
523 return readStandardString(length);
524 }
525 }
526 File file = new File(fileName);
527 return file.getName().replace(' ', '-');
528 }
529
530
535 String[][] getNames(int id) throws DocumentException, IOException {
536 int table_location[];
537 table_location = (int[])tables.get("name");
538 if (table_location == null)
539 throw new DocumentException("Table 'name' does not exist in " + fileName + style);
540 rf.seek(table_location[0] + 2);
541 int numRecords = rf.readUnsignedShort();
542 int startOfStorage = rf.readUnsignedShort();
543 ArrayList names = new ArrayList();
544 for (int k = 0; k < numRecords; ++k) {
545 int platformID = rf.readUnsignedShort();
546 int platformEncodingID = rf.readUnsignedShort();
547 int languageID = rf.readUnsignedShort();
548 int nameID = rf.readUnsignedShort();
549 int length = rf.readUnsignedShort();
550 int offset = rf.readUnsignedShort();
551 if (nameID == id) {
552 int pos = rf.getFilePointer();
553 rf.seek(table_location[0] + startOfStorage + offset);
554 String name;
555 if (platformID == 0 || platformID == 3 || (platformID == 2 && platformEncodingID == 1)){
556 name = readUnicodeString(length);
557 }
558 else {
559 name = readStandardString(length);
560 }
561 names.add(new String[]{String.valueOf(platformID),
562 String.valueOf(platformEncodingID), String.valueOf(languageID), name});
563 rf.seek(pos);
564 }
565 }
566 String thisName[][] = new String[names.size()][];
567 for (int k = 0; k < names.size(); ++k)
568 thisName[k] = (String[])names.get(k);
569 return thisName;
570 }
571
572
576 String[][] getAllNames() throws DocumentException, IOException {
577 int table_location[];
578 table_location = (int[])tables.get("name");
579 if (table_location == null)
580 throw new DocumentException("Table 'name' does not exist in " + fileName + style);
581 rf.seek(table_location[0] + 2);
582 int numRecords = rf.readUnsignedShort();
583 int startOfStorage = rf.readUnsignedShort();
584 ArrayList names = new ArrayList();
585 for (int k = 0; k < numRecords; ++k) {
586 int platformID = rf.readUnsignedShort();
587 int platformEncodingID = rf.readUnsignedShort();
588 int languageID = rf.readUnsignedShort();
589 int nameID = rf.readUnsignedShort();
590 int length = rf.readUnsignedShort();
591 int offset = rf.readUnsignedShort();
592 int pos = rf.getFilePointer();
593 rf.seek(table_location[0] + startOfStorage + offset);
594 String name;
595 if (platformID == 0 || platformID == 3 || (platformID == 2 && platformEncodingID == 1)){
596 name = readUnicodeString(length);
597 }
598 else {
599 name = readStandardString(length);
600 }
601 names.add(new String[]{String.valueOf(nameID), String.valueOf(platformID),
602 String.valueOf(platformEncodingID), String.valueOf(languageID), name});
603 rf.seek(pos);
604 }
605 String thisName[][] = new String[names.size()][];
606 for (int k = 0; k < names.size(); ++k)
607 thisName[k] = (String[])names.get(k);
608 return thisName;
609 }
610
611 void checkCff() {
612 int table_location[];
613 table_location = (int[])tables.get("CFF ");
614 if (table_location != null) {
615 cff = true;
616 cffOffset = table_location[0];
617 cffLength = table_location[1];
618 }
619 }
620
621
627 void process(byte ttfAfm[], boolean preload) throws DocumentException, IOException {
628 tables = new HashMap();
629
630 try {
631 if (ttfAfm == null)
632 rf = new RandomAccessFileOrArray(fileName, preload, Document.plainRandomAccess);
633 else
634 rf = new RandomAccessFileOrArray(ttfAfm);
635 if (ttcIndex.length() > 0) {
636 int dirIdx = Integer.parseInt(ttcIndex);
637 if (dirIdx < 0)
638 throw new DocumentException("The font index for " + fileName + " must be positive.");
639 String mainTag = readStandardString(4);
640 if (!mainTag.equals("ttcf"))
641 throw new DocumentException(fileName + " is not a valid TTC file.");
642 rf.skipBytes(4);
643 int dirCount = rf.readInt();
644 if (dirIdx >= dirCount)
645 throw new DocumentException("The font index for " + fileName + " must be between 0 and " + (dirCount - 1) + ". It was " + dirIdx + ".");
646 rf.skipBytes(dirIdx * 4);
647 directoryOffset = rf.readInt();
648 }
649 rf.seek(directoryOffset);
650 int ttId = rf.readInt();
651 if (ttId != 0x00010000 && ttId != 0x4F54544F)
652 throw new DocumentException(fileName + " is not a valid TTF or OTF file.");
653 int num_tables = rf.readUnsignedShort();
654 rf.skipBytes(6);
655 for (int k = 0; k < num_tables; ++k) {
656 String tag = readStandardString(4);
657 rf.skipBytes(4);
658 int table_location[] = new int[2];
659 table_location[0] = rf.readInt();
660 table_location[1] = rf.readInt();
661 tables.put(tag, table_location);
662 }
663 checkCff();
664 fontName = getBaseFont();
665 fullName = getNames(4);
666 familyName = getNames(1);
667 allNameEntries = getAllNames();
668 if (!justNames) {
669 fillTables();
670 readGlyphWidths();
671 readCMaps();
672 readKerning();
673 readBbox();
674 GlyphWidths = null;
675 }
676 }
677 finally {
678 if (rf != null) {
679 rf.close();
680 if (!embedded)
681 rf = null;
682 }
683 }
684 }
685
686
692 protected String readStandardString(int length) throws IOException {
693 byte buf[] = new byte[length];
694 rf.readFully(buf);
695 try {
696 return new String(buf, WINANSI);
697 }
698 catch (Exception e) {
699 throw new ExceptionConverter(e);
700 }
701 }
702
703
710 protected String readUnicodeString(int length) throws IOException {
711 StringBuffer buf = new StringBuffer();
712 length /= 2;
713 for (int k = 0; k < length; ++k) {
714 buf.append(rf.readChar());
715 }
716 return buf.toString();
717 }
718
719
724 protected void readGlyphWidths() throws DocumentException, IOException {
725 int table_location[];
726 table_location = (int[])tables.get("hmtx");
727 if (table_location == null)
728 throw new DocumentException("Table 'hmtx' does not exist in " + fileName + style);
729 rf.seek(table_location[0]);
730 GlyphWidths = new int[hhea.numberOfHMetrics];
731 for (int k = 0; k < hhea.numberOfHMetrics; ++k) {
732 GlyphWidths[k] = (rf.readUnsignedShort() * 1000) / head.unitsPerEm;
733 rf.readUnsignedShort();
734 }
735 }
736
737
741 protected int getGlyphWidth(int glyph) {
742 if (glyph >= GlyphWidths.length)
743 glyph = GlyphWidths.length - 1;
744 return GlyphWidths[glyph];
745 }
746
747 private void readBbox() throws DocumentException, IOException {
748 int tableLocation[];
749 tableLocation = (int[])tables.get("head");
750 if (tableLocation == null)
751 throw new DocumentException("Table 'head' does not exist in " + fileName + style);
752 rf.seek(tableLocation[0] + TrueTypeFontSubSet.HEAD_LOCA_FORMAT_OFFSET);
753 boolean locaShortTable = (rf.readUnsignedShort() == 0);
754 tableLocation = (int[])tables.get("loca");
755 if (tableLocation == null)
756 return;
757 rf.seek(tableLocation[0]);
758 int locaTable[];
759 if (locaShortTable) {
760 int entries = tableLocation[1] / 2;
761 locaTable = new int[entries];
762 for (int k = 0; k < entries; ++k)
763 locaTable[k] = rf.readUnsignedShort() * 2;
764 }
765 else {
766 int entries = tableLocation[1] / 4;
767 locaTable = new int[entries];
768 for (int k = 0; k < entries; ++k)
769 locaTable[k] = rf.readInt();
770 }
771 tableLocation = (int[])tables.get("glyf");
772 if (tableLocation == null)
773 throw new DocumentException("Table 'glyf' does not exist in " + fileName + style);
774 int tableGlyphOffset = tableLocation[0];
775 bboxes = new int[locaTable.length - 1][];
776 for (int glyph = 0; glyph < locaTable.length - 1; ++glyph) {
777 int start = locaTable[glyph];
778 if (start != locaTable[glyph + 1]) {
779 rf.seek(tableGlyphOffset + start + 2);
780 bboxes[glyph] = new int[]{
781 (rf.readShort() * 1000) / head.unitsPerEm,
782 (rf.readShort() * 1000) / head.unitsPerEm,
783 (rf.readShort() * 1000) / head.unitsPerEm,
784 (rf.readShort() * 1000) / head.unitsPerEm};
785 }
786 }
787 }
788
789
794 void readCMaps() throws DocumentException, IOException {
795 int table_location[];
796 table_location = (int[])tables.get("cmap");
797 if (table_location == null)
798 throw new DocumentException("Table 'cmap' does not exist in " + fileName + style);
799 rf.seek(table_location[0]);
800 rf.skipBytes(2);
801 int num_tables = rf.readUnsignedShort();
802 fontSpecific = false;
803 int map10 = 0;
804 int map31 = 0;
805 int map30 = 0;
806 int mapExt = 0;
807 for (int k = 0; k < num_tables; ++k) {
808 int platId = rf.readUnsignedShort();
809 int platSpecId = rf.readUnsignedShort();
810 int offset = rf.readInt();
811 if (platId == 3 && platSpecId == 0) {
812 fontSpecific = true;
813 map30 = offset;
814 }
815 else if (platId == 3 && platSpecId == 1) {
816 map31 = offset;
817 }
818 else if (platId == 3 && platSpecId == 10) {
819 mapExt = offset;
820 }
821 if (platId == 1 && platSpecId == 0) {
822 map10 = offset;
823 }
824 }
825 if (map10 > 0) {
826 rf.seek(table_location[0] + map10);
827 int format = rf.readUnsignedShort();
828 switch (format) {
829 case 0:
830 cmap10 = readFormat0();
831 break;
832 case 4:
833 cmap10 = readFormat4();
834 break;
835 case 6:
836 cmap10 = readFormat6();
837 break;
838 }
839 }
840 if (map31 > 0) {
841 rf.seek(table_location[0] + map31);
842 int format = rf.readUnsignedShort();
843 if (format == 4) {
844 cmap31 = readFormat4();
845 }
846 }
847 if (map30 > 0) {
848 rf.seek(table_location[0] + map30);
849 int format = rf.readUnsignedShort();
850 if (format == 4) {
851 cmap10 = readFormat4();
852 }
853 }
854 if (mapExt > 0) {
855 rf.seek(table_location[0] + mapExt);
856 int format = rf.readUnsignedShort();
857 switch (format) {
858 case 0:
859 cmapExt = readFormat0();
860 break;
861 case 4:
862 cmapExt = readFormat4();
863 break;
864 case 6:
865 cmapExt = readFormat6();
866 break;
867 case 12:
868 cmapExt = readFormat12();
869 break;
870 }
871 }
872 }
873
874 HashMap readFormat12() throws IOException {
875 HashMap h = new HashMap();
876 rf.skipBytes(2);
877 int table_lenght = rf.readInt();
878 rf.skipBytes(4);
879 int nGroups = rf.readInt();
880 for (int k = 0; k < nGroups; k++) {
881 int startCharCode = rf.readInt();
882 int endCharCode = rf.readInt();
883 int startGlyphID = rf.readInt();
884 for (int i = startCharCode; i <= endCharCode; i++) {
885 int[] r = new int[2];
886 r[0] = startGlyphID;
887 r[1] = getGlyphWidth(r[0]);
888 h.put(new Integer(i), r);
889 startGlyphID++;
890 }
891 }
892 return h;
893 }
894
895
900 HashMap readFormat0() throws IOException {
901 HashMap h = new HashMap();
902 rf.skipBytes(4);
903 for (int k = 0; k < 256; ++k) {
904 int r[] = new int[2];
905 r[0] = rf.readUnsignedByte();
906 r[1] = getGlyphWidth(r[0]);
907 h.put(new Integer(k), r);
908 }
909 return h;
910 }
911
912
917 HashMap readFormat4() throws IOException {
918 HashMap h = new HashMap();
919 int table_lenght = rf.readUnsignedShort();
920 rf.skipBytes(2);
921 int segCount = rf.readUnsignedShort() / 2;
922 rf.skipBytes(6);
923 int endCount[] = new int[segCount];
924 for (int k = 0; k < segCount; ++k) {
925 endCount[k] = rf.readUnsignedShort();
926 }
927 rf.skipBytes(2);
928 int startCount[] = new int[segCount];
929 for (int k = 0; k < segCount; ++k) {
930 startCount[k] = rf.readUnsignedShort();
931 }
932 int idDelta[] = new int[segCount];
933 for (int k = 0; k < segCount; ++k) {
934 idDelta[k] = rf.readUnsignedShort();
935 }
936 int idRO[] = new int[segCount];
937 for (int k = 0; k < segCount; ++k) {
938 idRO[k] = rf.readUnsignedShort();
939 }
940 int glyphId[] = new int[table_lenght / 2 - 8 - segCount * 4];
941 for (int k = 0; k < glyphId.length; ++k) {
942 glyphId[k] = rf.readUnsignedShort();
943 }
944 for (int k = 0; k < segCount; ++k) {
945 int glyph;
946 for (int j = startCount[k]; j <= endCount[k] && j != 0xFFFF; ++j) {
947 if (idRO[k] == 0) {
948 glyph = (j + idDelta[k]) & 0xFFFF;
949 }
950 else {
951 int idx = k + idRO[k] / 2 - segCount + j - startCount[k];
952 if (idx >= glyphId.length)
953 continue;
954 glyph = (glyphId[idx] + idDelta[k]) & 0xFFFF;
955 }
956 int r[] = new int[2];
957 r[0] = glyph;
958 r[1] = getGlyphWidth(r[0]);
959 h.put(new Integer(fontSpecific ? ((j & 0xff00) == 0xf000 ? j & 0xff : j) : j), r);
960 }
961 }
962 return h;
963 }
964
965
971 HashMap readFormat6() throws IOException {
972 HashMap h = new HashMap();
973 rf.skipBytes(4);
974 int start_code = rf.readUnsignedShort();
975 int code_count = rf.readUnsignedShort();
976 for (int k = 0; k < code_count; ++k) {
977 int r[] = new int[2];
978 r[0] = rf.readUnsignedShort();
979 r[1] = getGlyphWidth(r[0]);
980 h.put(new Integer(k + start_code), r);
981 }
982 return h;
983 }
984
985
988 void readKerning() throws IOException {
989 int table_location[];
990 table_location = (int[])tables.get("kern");
991 if (table_location == null)
992 return;
993 rf.seek(table_location[0] + 2);
994 int nTables = rf.readUnsignedShort();
995 int checkpoint = table_location[0] + 4;
996 int length = 0;
997 for (int k = 0; k < nTables; ++k) {
998 checkpoint += length;
999 rf.seek(checkpoint);
1000 rf.skipBytes(2);
1001 length = rf.readUnsignedShort();
1002 int coverage = rf.readUnsignedShort();
1003 if ((coverage & 0xfff7) == 0x0001) {
1004 int nPairs = rf.readUnsignedShort();
1005 rf.skipBytes(6);
1006 for (int j = 0; j < nPairs; ++j) {
1007 int pair = rf.readInt();
1008 int value = rf.readShort() * 1000 / head.unitsPerEm;
1009 kerning.put(pair, value);
1010 }
1011 }
1012 }
1013 }
1014
1015
1020 public int getKerning(int char1, int char2) {
1021 int metrics[] = getMetricsTT(char1);
1022 if (metrics == null)
1023 return 0;
1024 int c1 = metrics[0];
1025 metrics = getMetricsTT(char2);
1026 if (metrics == null)
1027 return 0;
1028 int c2 = metrics[0];
1029 return kerning.get((c1 << 16) + c2);
1030 }
1031
1032
1038 int getRawWidth(int c, String name) {
1039 int[] metric = getMetricsTT(c);
1040 if (metric == null)
1041 return 0;
1042 return metric[1];
1043 }
1044
1045
1050 protected PdfDictionary getFontDescriptor(PdfIndirectReference fontStream, String subsetPrefix, PdfIndirectReference cidset) {
1051 PdfDictionary dic = new PdfDictionary(PdfName.FONTDESCRIPTOR);
1052 dic.put(PdfName.ASCENT, new PdfNumber(os_2.sTypoAscender * 1000 / head.unitsPerEm));
1053 dic.put(PdfName.CAPHEIGHT, new PdfNumber(os_2.sCapHeight * 1000 / head.unitsPerEm));
1054 dic.put(PdfName.DESCENT, new PdfNumber(os_2.sTypoDescender * 1000 / head.unitsPerEm));
1055 dic.put(PdfName.FONTBBOX, new PdfRectangle(
1056 head.xMin * 1000 / head.unitsPerEm,
1057 head.yMin * 1000 / head.unitsPerEm,
1058 head.xMax * 1000 / head.unitsPerEm,
1059 head.yMax * 1000 / head.unitsPerEm));
1060 if (cidset != null)
1061 dic.put(PdfName.CIDSET, cidset);
1062 if (cff) {
1063 if (encoding.startsWith("Identity-"))
1064 dic.put(PdfName.FONTNAME, new PdfName(subsetPrefix + fontName+"-"+encoding));
1065 else
1066 dic.put(PdfName.FONTNAME, new PdfName(subsetPrefix + fontName + style));
1067 }
1068 else
1069 dic.put(PdfName.FONTNAME, new PdfName(subsetPrefix + fontName + style));
1070 dic.put(PdfName.ITALICANGLE, new PdfNumber(italicAngle));
1071 dic.put(PdfName.STEMV, new PdfNumber(80));
1072 if (fontStream != null) {
1073 if (cff)
1074 dic.put(PdfName.FONTFILE3, fontStream);
1075 else
1076 dic.put(PdfName.FONTFILE2, fontStream);
1077 }
1078 int flags = 0;
1079 if (isFixedPitch)
1080 flags |= 1;
1081 flags |= fontSpecific ? 4 : 32;
1082 if ((head.macStyle & 2) != 0)
1083 flags |= 64;
1084 if ((head.macStyle & 1) != 0)
1085 flags |= 262144;
1086 dic.put(PdfName.FLAGS, new PdfNumber(flags));
1087
1088 return dic;
1089 }
1090
1091
1099 protected PdfDictionary getFontBaseType(PdfIndirectReference fontDescriptor, String subsetPrefix, int firstChar, int lastChar, byte shortTag[]) {
1100 PdfDictionary dic = new PdfDictionary(PdfName.FONT);
1101 if (cff) {
1102 dic.put(PdfName.SUBTYPE, PdfName.TYPE1);
1103 dic.put(PdfName.BASEFONT, new PdfName(fontName + style));
1104 }
1105 else {
1106 dic.put(PdfName.SUBTYPE, PdfName.TRUETYPE);
1107 dic.put(PdfName.BASEFONT, new PdfName(subsetPrefix + fontName + style));
1108 }
1109 dic.put(PdfName.BASEFONT, new PdfName(subsetPrefix + fontName + style));
1110 if (!fontSpecific) {
1111 for (int k = firstChar; k <= lastChar; ++k) {
1112 if (!differences[k].equals(notdef)) {
1113 firstChar = k;
1114 break;
1115 }
1116 }
1117 if (encoding.equals("Cp1252") || encoding.equals("MacRoman"))
1118 dic.put(PdfName.ENCODING, encoding.equals("Cp1252") ? PdfName.WIN_ANSI_ENCODING : PdfName.MAC_ROMAN_ENCODING);
1119 else {
1120 PdfDictionary enc = new PdfDictionary(PdfName.ENCODING);
1121 PdfArray dif = new PdfArray();
1122 boolean gap = true;
1123 for (int k = firstChar; k <= lastChar; ++k) {
1124 if (shortTag[k] != 0) {
1125 if (gap) {
1126 dif.add(new PdfNumber(k));
1127 gap = false;
1128 }
1129 dif.add(new PdfName(differences[k]));
1130 }
1131 else
1132 gap = true;
1133 }
1134 enc.put(PdfName.DIFFERENCES, dif);
1135 dic.put(PdfName.ENCODING, enc);
1136 }
1137 }
1138 dic.put(PdfName.FIRSTCHAR, new PdfNumber(firstChar));
1139 dic.put(PdfName.LASTCHAR, new PdfNumber(lastChar));
1140 PdfArray wd = new PdfArray();
1141 for (int k = firstChar; k <= lastChar; ++k) {
1142 if (shortTag[k] == 0)
1143 wd.add(new PdfNumber(0));
1144 else
1145 wd.add(new PdfNumber(widths[k]));
1146 }
1147 dic.put(PdfName.WIDTHS, wd);
1148 if (fontDescriptor != null)
1149 dic.put(PdfName.FONTDESCRIPTOR, fontDescriptor);
1150 return dic;
1151 }
1152
1153 protected byte[] getFullFont() throws IOException {
1154 RandomAccessFileOrArray rf2 = null;
1155 try {
1156 rf2 = new RandomAccessFileOrArray(rf);
1157 rf2.reOpen();
1158 byte b[] = new byte[rf2.length()];
1159 rf2.readFully(b);
1160 return b;
1161 }
1162 finally {
1163 try {if (rf2 != null) {rf2.close();}} catch (Exception e) {}
1164 }
1165 }
1166
1167 protected static int[] compactRanges(ArrayList ranges) {
1168 ArrayList simp = new ArrayList();
1169 for (int k = 0; k < ranges.size(); ++k) {
1170 int[] r = (int[])ranges.get(k);
1171 for (int j = 0; j < r.length; j += 2) {
1172 simp.add(new int[]{Math.max(0, Math.min(r[j], r[j + 1])), Math.min(0xffff, Math.max(r[j], r[j + 1]))});
1173 }
1174 }
1175 for (int k1 = 0; k1 < simp.size() - 1; ++k1) {
1176 for (int k2 = k1 + 1; k2 < simp.size(); ++k2) {
1177 int[] r1 = (int[])simp.get(k1);
1178 int[] r2 = (int[])simp.get(k2);
1179 if ((r1[0] >= r2[0] && r1[0] <= r2[1]) || (r1[1] >= r2[0] && r1[0] <= r2[1])) {
1180 r1[0] = Math.min(r1[0], r2[0]);
1181 r1[1] = Math.max(r1[1], r2[1]);
1182 simp.remove(k2);
1183 --k2;
1184 }
1185 }
1186 }
1187 int[] s = new int[simp.size() * 2];
1188 for (int k = 0; k < simp.size(); ++k) {
1189 int[] r = (int[])simp.get(k);
1190 s[k * 2] = r[0];
1191 s[k * 2 + 1] = r[1];
1192 }
1193 return s;
1194 }
1195
1196 protected void addRangeUni(HashMap longTag, boolean includeMetrics, boolean subsetp) {
1197 if (!subsetp && (subsetRanges != null || directoryOffset > 0)) {
1198 int[] rg = (subsetRanges == null && directoryOffset > 0) ? new int[]{0, 0xffff} : compactRanges(subsetRanges);
1199 HashMap usemap;
1200 if (!fontSpecific && cmap31 != null)
1201 usemap = cmap31;
1202 else if (fontSpecific && cmap10 != null)
1203 usemap = cmap10;
1204 else if (cmap31 != null)
1205 usemap = cmap31;
1206 else
1207 usemap = cmap10;
1208 for (Iterator it = usemap.entrySet().iterator(); it.hasNext();) {
1209 Map.Entry e = (Map.Entry)it.next();
1210 int[] v = (int[])e.getValue();
1211 Integer gi = new Integer(v[0]);
1212 if (longTag.containsKey(gi))
1213 continue;
1214 int c = ((Integer)e.getKey()).intValue();
1215 boolean skip = true;
1216 for (int k = 0; k < rg.length; k += 2) {
1217 if (c >= rg[k] && c <= rg[k + 1]) {
1218 skip = false;
1219 break;
1220 }
1221 }
1222 if (!skip)
1223 longTag.put(gi, includeMetrics ? new int[]{v[0], v[1], c} : null);
1224 }
1225 }
1226 }
1227
1228
1235 void writeFont(PdfWriter writer, PdfIndirectReference ref, Object params[]) throws DocumentException, IOException {
1236 int firstChar = ((Integer)params[0]).intValue();
1237 int lastChar = ((Integer)params[1]).intValue();
1238 byte shortTag[] = (byte[])params[2];
1239 boolean subsetp = ((Boolean)params[3]).booleanValue() && subset;
1240
1241 if (!subsetp) {
1242 firstChar = 0;
1243 lastChar = shortTag.length - 1;
1244 for (int k = 0; k < shortTag.length; ++k)
1245 shortTag[k] = 1;
1246 }
1247 PdfIndirectReference ind_font = null;
1248 PdfObject pobj = null;
1249 PdfIndirectObject obj = null;
1250 String subsetPrefix = "";
1251 if (embedded) {
1252 if (cff) {
1253 pobj = new StreamFont(readCffFont(), "Type1C", compressionLevel);
1254 obj = writer.addToBody(pobj);
1255 ind_font = obj.getIndirectReference();
1256 }
1257 else {
1258 if (subsetp)
1259 subsetPrefix = createSubsetPrefix();
1260 HashMap glyphs = new HashMap();
1261 for (int k = firstChar; k <= lastChar; ++k) {
1262 if (shortTag[k] != 0) {
1263 int[] metrics = null;
1264 if (specialMap != null) {
1265 int[] cd = GlyphList.nameToUnicode(differences[k]);
1266 if (cd != null)
1267 metrics = getMetricsTT(cd[0]);
1268 }
1269 else {
1270 if (fontSpecific)
1271 metrics = getMetricsTT(k);
1272 else
1273 metrics = getMetricsTT(unicodeDifferences[k]);
1274 }
1275 if (metrics != null)
1276 glyphs.put(new Integer(metrics[0]), null);
1277 }
1278 }
1279 addRangeUni(glyphs, false, subsetp);
1280 byte[] b = null;
1281 if (subsetp || directoryOffset != 0 || subsetRanges != null) {
1282 TrueTypeFontSubSet sb = new TrueTypeFontSubSet(fileName, new RandomAccessFileOrArray(rf), glyphs, directoryOffset, true, !subsetp);
1283 b = sb.process();
1284 }
1285 else {
1286 b = getFullFont();
1287 }
1288 int lengths[] = new int[]{b.length};
1289 pobj = new StreamFont(b, lengths, compressionLevel);
1290 obj = writer.addToBody(pobj);
1291 ind_font = obj.getIndirectReference();
1292 }
1293 }
1294 pobj = getFontDescriptor(ind_font, subsetPrefix, null);
1295 if (pobj != null){
1296 obj = writer.addToBody(pobj);
1297 ind_font = obj.getIndirectReference();
1298 }
1299 pobj = getFontBaseType(ind_font, subsetPrefix, firstChar, lastChar, shortTag);
1300 writer.addToBody(pobj, ref);
1301 }
1302
1303
1310 protected byte[] readCffFont() throws IOException {
1311 RandomAccessFileOrArray rf2 = new RandomAccessFileOrArray(rf);
1312 byte b[] = new byte[cffLength];
1313 try {
1314 rf2.reOpen();
1315 rf2.seek(cffOffset);
1316 rf2.readFully(b);
1317 }
1318 finally {
1319 try {
1320 rf2.close();
1321 }
1322 catch (Exception e) {
1323
1324 }
1325 }
1326 return b;
1327 }
1328
1329
1334 public PdfStream getFullFontStream() throws IOException, DocumentException {
1335 if (cff) {
1336 return new StreamFont(readCffFont(), "Type1C", compressionLevel);
1337 }
1338 else {
1339 byte[] b = getFullFont();
1340 int lengths[] = new int[]{b.length};
1341 return new StreamFont(b, lengths, compressionLevel);
1342 }
1343 }
1344
1345
1352 public float getFontDescriptor(int key, float fontSize) {
1353 switch (key) {
1354 case ASCENT:
1355 return os_2.sTypoAscender * fontSize / head.unitsPerEm;
1356 case CAPHEIGHT:
1357 return os_2.sCapHeight * fontSize / head.unitsPerEm;
1358 case DESCENT:
1359 return os_2.sTypoDescender * fontSize / head.unitsPerEm;
1360 case ITALICANGLE:
1361 return (float)italicAngle;
1362 case BBOXLLX:
1363 return fontSize * head.xMin / head.unitsPerEm;
1364 case BBOXLLY:
1365 return fontSize * head.yMin / head.unitsPerEm;
1366 case BBOXURX:
1367 return fontSize * head.xMax / head.unitsPerEm;
1368 case BBOXURY:
1369 return fontSize * head.yMax / head.unitsPerEm;
1370 case AWT_ASCENT:
1371 return fontSize * hhea.Ascender / head.unitsPerEm;
1372 case AWT_DESCENT:
1373 return fontSize * hhea.Descender / head.unitsPerEm;
1374 case AWT_LEADING:
1375 return fontSize * hhea.LineGap / head.unitsPerEm;
1376 case AWT_MAXADVANCE:
1377 return fontSize * hhea.advanceWidthMax / head.unitsPerEm;
1378 case UNDERLINE_POSITION:
1379 return (underlinePosition - underlineThickness / 2) * fontSize / head.unitsPerEm;
1380 case UNDERLINE_THICKNESS:
1381 return underlineThickness * fontSize / head.unitsPerEm;
1382 case STRIKETHROUGH_POSITION:
1383 return os_2.yStrikeoutPosition * fontSize / head.unitsPerEm;
1384 case STRIKETHROUGH_THICKNESS:
1385 return os_2.yStrikeoutSize * fontSize / head.unitsPerEm;
1386 case SUBSCRIPT_SIZE:
1387 return os_2.ySubscriptYSize * fontSize / head.unitsPerEm;
1388 case SUBSCRIPT_OFFSET:
1389 return -os_2.ySubscriptYOffset * fontSize / head.unitsPerEm;
1390 case SUPERSCRIPT_SIZE:
1391 return os_2.ySuperscriptYSize * fontSize / head.unitsPerEm;
1392 case SUPERSCRIPT_OFFSET:
1393 return os_2.ySuperscriptYOffset * fontSize / head.unitsPerEm;
1394 }
1395 return 0;
1396 }
1397
1398
1402 public int[] getMetricsTT(int c) {
1403 if (cmapExt != null)
1404 return (int[])cmapExt.get(new Integer(c));
1405 if (!fontSpecific && cmap31 != null)
1406 return (int[])cmap31.get(new Integer(c));
1407 if (fontSpecific && cmap10 != null)
1408 return (int[])cmap10.get(new Integer(c));
1409 if (cmap31 != null)
1410 return (int[])cmap31.get(new Integer(c));
1411 if (cmap10 != null)
1412 return (int[])cmap10.get(new Integer(c));
1413 return null;
1414 }
1415
1416
1419 public String getPostscriptFontName() {
1420 return fontName;
1421 }
1422
1423
1426 public String[] getCodePagesSupported() {
1427 long cp = (((long)os_2.ulCodePageRange2) << 32) + (os_2.ulCodePageRange1 & 0xffffffffL);
1428 int count = 0;
1429 long bit = 1;
1430 for (int k = 0; k < 64; ++k) {
1431 if ((cp & bit) != 0 && codePages[k] != null)
1432 ++count;
1433 bit <<= 1;
1434 }
1435 String ret[] = new String[count];
1436 count = 0;
1437 bit = 1;
1438 for (int k = 0; k < 64; ++k) {
1439 if ((cp & bit) != 0 && codePages[k] != null)
1440 ret[count++] = codePages[k];
1441 bit <<= 1;
1442 }
1443 return ret;
1444 }
1445
1446
1454 public String[][] getFullFontName() {
1455 return fullName;
1456 }
1457
1458
1466 public String[][] getAllNameEntries() {
1467 return allNameEntries;
1468 }
1469
1470
1478 public String[][] getFamilyFontName() {
1479 return familyName;
1480 }
1481
1482
1485 public boolean hasKernPairs() {
1486 return kerning.size() > 0;
1487 }
1488
1489
1494 public void setPostscriptFontName(String name) {
1495 fontName = name;
1496 }
1497
1498
1505 public boolean setKerning(int char1, int char2, int kern) {
1506 int metrics[] = getMetricsTT(char1);
1507 if (metrics == null)
1508 return false;
1509 int c1 = metrics[0];
1510 metrics = getMetricsTT(char2);
1511 if (metrics == null)
1512 return false;
1513 int c2 = metrics[0];
1514 kerning.put((c1 << 16) + c2, kern);
1515 return true;
1516 }
1517
1518 protected int[] getRawCharBBox(int c, String name) {
1519 HashMap map = null;
1520 if (name == null || cmap31 == null)
1521 map = cmap10;
1522 else
1523 map = cmap31;
1524 if (map == null)
1525 return null;
1526 int metric[] = (int[])map.get(new Integer(c));
1527 if (metric == null || bboxes == null)
1528 return null;
1529 return bboxes[metric[0]];
1530 }
1531 }