1 //========================================================================
5 // Copyright 1996 Derek B. Noonburg
7 //========================================================================
10 #pragma implementation
32 #include "Japan12CMapInfo.h"
34 #if CHINESE_GB_SUPPORT
35 #include "GB12CMapInfo.h"
37 #if CHINESE_CNS_SUPPORT
38 #include "CNS13CMapInfo.h"
41 //------------------------------------------------------------------------
43 static int CDECL cmpWidthExcep(const void *w1, const void *w2);
44 static int CDECL cmpWidthExcepV(const void *w1, const void *w2);
46 //------------------------------------------------------------------------
48 static Gushort *defCharWidths[12] = {
52 courierBoldObliqueWidths,
54 helveticaObliqueWidths,
56 helveticaBoldObliqueWidths,
63 //------------------------------------------------------------------------
65 //------------------------------------------------------------------------
67 GfxFont::GfxFont(char *tag1, Ref id1, Dict *fontDict) {
68 BuiltinFont *builtinFont;
69 Object obj1, obj2, obj3, obj4;
74 // get font tag and ID
75 tag = new GString(tag1);
79 type = fontUnknownType;
80 fontDict->lookup("Subtype", &obj1);
81 if (obj1.isName("Type1"))
83 else if (obj1.isName("Type1C"))
85 else if (obj1.isName("Type3"))
87 else if (obj1.isName("TrueType"))
89 else if (obj1.isName("Type0"))
96 fontDict->lookup("BaseFont", &obj1);
98 name = new GString(obj1.getName());
101 // Newer Adobe tools are using Base14-compatible TrueType fonts
102 // without embedding them, so munge the names into the equivalent
103 // PostScript names. This is a kludge -- it would be nice if Adobe
104 // followed their own spec.
105 if (type == fontTrueType) {
106 p = name->getCString();
108 if (!strncmp(p, "Arial", 5)) {
109 if (!strcmp(p+5, ",Bold")) {
110 name2 = "Helvetica-Bold";
111 } else if (!strcmp(p+5, ",Italic")) {
112 name2 = "Helvetica-Oblique";
113 } else if (!strcmp(p+5, ",BoldItalic")) {
114 name2 = "Helvetica-BoldOblique";
118 } else if (!strncmp(p, "TimesNewRoman", 13)) {
119 if (!strcmp(p+13, ",Bold")) {
120 name2 = "Times-Bold";
121 } else if (!strcmp(p+13, ",Italic")) {
122 name2 = "Times-Italic";
123 } else if (!strcmp(p+13, ",BoldItalic")) {
124 name2 = "Times-BoldItalic";
126 name2 = "Times-Roman";
128 } else if (!strncmp(p, "CourierNew", 10)) {
129 if (!strcmp(p+10, ",Bold")) {
130 name2 = "Courier-Bold";
131 } else if (!strcmp(p+10, ",Italic")) {
132 name2 = "Courier-Oblique";
133 } else if (!strcmp(p+10, ",BoldItalic")) {
134 name2 = "Courier-BoldOblique";
141 name = new GString(name2);
145 // is it a built-in font?
148 for (i = 0; i < numBuiltinFonts; ++i) {
149 if (!strcmp(builtinFonts[i].name, name->getCString())) {
150 builtinFont = &builtinFonts[i];
156 // assume Times-Roman by default (for substitution purposes)
159 // get info from font descriptor
164 fontDict->lookup("FontDescriptor", &obj1);
168 obj1.dictLookup("Flags", &obj2);
170 flags = obj2.getInt();
174 obj1.dictLookup("FontName", &obj2);
176 embFontName = new GString(obj2.getName());
179 // look for embedded font file
180 if (type == fontType1) {
181 obj1.dictLookupNF("FontFile", &obj2);
183 embFontID = obj2.getRef();
186 if (embFontID.num == -1 && type == fontTrueType) {
187 obj1.dictLookupNF("FontFile2", &obj2);
189 embFontID = obj2.getRef();
192 if (embFontID.num == -1) {
193 obj1.dictLookupNF("FontFile3", &obj2);
195 embFontID = obj2.getRef();
197 if (obj3.isStream()) {
198 obj3.streamGetDict()->lookup("Subtype", &obj4);
199 if (obj4.isName("Type1"))
201 else if (obj4.isName("Type1C"))
203 else if (obj4.isName("Type3"))
205 else if (obj4.isName("TrueType"))
207 else if (obj4.isName("Type0"))
216 // look for MissingWidth
217 obj1.dictLookup("MissingWidth", &obj2);
219 missingWidth = obj2.getInt();
225 // get Type3 font definition
226 if (type == fontType3) {
227 fontDict->lookup("CharProcs", &charProcs);
228 if (!charProcs.isDict()) {
229 error(-1, "Missing or invalid CharProcs dictionary in Type 3 font");
234 // look for an external font file
236 if (type == fontType1 && name)
240 fontMat[0] = fontMat[3] = 1;
241 fontMat[1] = fontMat[2] = fontMat[4] = fontMat[5] = 0;
242 if (fontDict->lookup("FontMatrix", &obj1)->isArray()) {
243 for (i = 0; i < 6 && i < obj1.arrayGetLength(); ++i) {
244 if (obj1.arrayGet(i, &obj2)->isNum())
245 fontMat[i] = obj2.getNum();
251 // get encoding and character widths
252 if (type == fontType0) {
253 getType0EncAndWidths(fontDict);
255 getEncAndWidths(fontDict, builtinFont, missingWidth);
259 GfxFont::~GfxFont() {
264 if (!is16 && encoding) {
273 if (charProcs.isDict()) {
277 gfree(widths16.exceps);
278 gfree(widths16.excepsV);
282 double GfxFont::getWidth(GString *s) {
287 for (i = 0; i < s->getLength(); ++i)
288 w += widths[s->getChar(i) & 0xff];
292 double GfxFont::getWidth16(int c) {
296 w = widths16.defWidth;
298 b = widths16.numExceps;
299 // invariant: widths16.exceps[a].last < c < widths16.exceps[b].first
302 if (widths16.exceps[m].last < c) {
304 } else if (c < widths16.exceps[m].first) {
307 w = widths16.exceps[m].width;
314 double GfxFont::getHeight16(int c) {
318 h = widths16.defHeight;
320 b = widths16.numExcepsV;
321 // invariant: widths16.excepsV[a].last < c < widths16.excepsV[b].first
324 if (widths16.excepsV[m].last < c) {
326 } else if (c < widths16.excepsV[m].first) {
329 h = widths16.excepsV[m].height;
336 double GfxFont::getOriginX16(int c) {
340 vx = widths16.defWidth / 2;
342 b = widths16.numExcepsV;
343 // invariant: widths16.excepsV[a].last < c < widths16.excepsV[b].first
346 if (widths16.excepsV[m].last < c) {
348 } else if (c < widths16.excepsV[m].first) {
351 vx = widths16.excepsV[m].vx;
358 double GfxFont::getOriginY16(int c) {
364 b = widths16.numExcepsV;
365 // invariant: widths16.excepsV[a].last < c < widths16.excepsV[b].first
368 if (widths16.excepsV[m].last < c) {
370 } else if (c < widths16.excepsV[m].first) {
373 vy = widths16.excepsV[m].vy;
380 Object *GfxFont::getCharProc(int code, Object *proc) {
381 if (charProcs.isDict()) {
382 charProcs.dictLookup(encoding->getCharName(code), proc);
389 void GfxFont::getEncAndWidths(Dict *fontDict, BuiltinFont *builtinFont,
391 Object obj1, obj2, obj3;
397 // Encodings start with a base encoding, which can come from
398 // (in order of priority):
399 // 1. FontDict.Encoding or FontDict.Encoding.BaseEncoding
400 // - MacRoman / WinAnsi / Standard
401 // 2. embedded font file
403 // - builtin --> builtin encoding
404 // - TrueType --> MacRomanEncoding
405 // - others --> StandardEncoding
406 // and then add a list of differences from
407 // FontDict.Encoding.Differences.
409 // check FontDict for base encoding
411 fontDict->lookup("Encoding", &obj1);
413 obj1.dictLookup("BaseEncoding", &obj2);
414 if (obj2.isName("MacRomanEncoding")) {
415 encoding = macRomanEncoding.copy();
416 } else if (obj2.isName("WinAnsiEncoding")) {
417 encoding = winAnsiEncoding.copy();
418 } else if (obj2.isName("StandardEncoding")) {
419 encoding = standardEncoding.copy();
422 } else if (obj1.isName("MacRomanEncoding")) {
423 encoding = macRomanEncoding.copy();
424 } else if (obj1.isName("WinAnsiEncoding")) {
425 encoding = winAnsiEncoding.copy();
426 } else if (obj1.isName("StandardEncoding")) {
427 encoding = standardEncoding.copy();
431 // check embedded or external font file for base encoding
432 if ((type == fontType1 || type == fontType1C) &&
433 (extFontFile || embFontID.num >= 0)) {
435 buf = readExtFontFile(&len);
437 buf = readEmbFontFile(&len);
439 if (type == fontType1)
440 fontFile = new Type1FontFile(buf, len);
442 fontFile = new Type1CFontFile(buf, len);
443 if (fontFile->getName()) {
446 embFontName = new GString(fontFile->getName());
449 encoding = fontFile->getEncoding(gTrue);
455 // get default base encoding
458 encoding = builtinFont->encoding->copy();
459 else if (type == fontTrueType)
460 encoding = macRomanEncoding.copy();
462 encoding = standardEncoding.copy();
465 // merge differences into encoding
466 fontDict->lookup("Encoding", &obj1);
468 obj1.dictLookup("Differences", &obj2);
469 if (obj2.isArray()) {
471 for (i = 0; i < obj2.arrayGetLength(); ++i) {
472 obj2.arrayGet(i, &obj3);
474 code = obj3.getInt();
475 } else if (obj3.isName()) {
477 encoding->addChar(code, copyString(obj3.getName()));
480 error(-1, "Wrong type in font encoding resource differences (%s)",
490 // get character widths
492 makeWidths(fontDict, builtinFont->encoding, builtinFont->widths,
495 makeWidths(fontDict, NULL, NULL, missingWidth);
498 void GfxFont::findExtFontFile() {
502 for (path = fontPath; *path; ++path) {
503 extFontFile = appendToPath(new GString(*path), name->getCString());
504 f = fopen(extFontFile->getCString(), "rb");
506 extFontFile->append(".pfb");
507 f = fopen(extFontFile->getCString(), "rb");
510 extFontFile->del(extFontFile->getLength() - 4, 4);
511 extFontFile->append(".pfa");
512 f = fopen(extFontFile->getCString(), "rb");
523 char *GfxFont::readExtFontFile(int *len) {
527 if (!(f = fopen(extFontFile->getCString(), "rb"))) {
528 error(-1, "Internal: external font file '%s' vanished", extFontFile);
531 fseek(f, 0, SEEK_END);
532 *len = (int)ftell(f);
533 fseek(f, 0, SEEK_SET);
534 buf = (char *)gmalloc(*len);
535 if ((int)fread(buf, 1, *len, f) != *len)
536 error(-1, "Error reading external font file '%s'", extFontFile);
541 char *GfxFont::readEmbFontFile(int *len) {
548 obj1.initRef(embFontID.num, embFontID.gen);
550 if (!obj2.isStream()) {
551 error(-1, "Embedded font file is not a stream");
557 str = obj2.getStream();
562 while ((c = str->getChar()) != EOF) {
565 buf = (char *)grealloc(buf, size);
578 void GfxFont::makeWidths(Dict *fontDict, FontEncoding *builtinEncoding,
579 Gushort *builtinWidths, int missingWidth) {
581 int firstChar, lastChar;
588 // initialize all widths
589 for (code = 0; code < 256; ++code) {
590 widths[code] = missingWidth * 0.001;
593 // use widths from built-in font
594 if (builtinEncoding) {
595 code2 = 0; // to make gcc happy
596 for (code = 0; code < 256; ++code) {
597 if ((charName = encoding->getCharName(code)) &&
598 (code2 = builtinEncoding->getCharCode(charName)) >= 0)
599 widths[code] = builtinWidths[code2] * 0.001;
602 // get widths from font dict
604 fontDict->lookup("FirstChar", &obj1);
605 firstChar = obj1.isInt() ? obj1.getInt() : 0;
607 fontDict->lookup("LastChar", &obj1);
608 lastChar = obj1.isInt() ? obj1.getInt() : 255;
610 if (type == fontType3)
614 fontDict->lookup("Widths", &obj1);
615 if (obj1.isArray()) {
616 for (code = firstChar; code <= lastChar; ++code) {
617 obj1.arrayGet(code - firstChar, &obj2);
619 widths[code] = obj2.getNum() * mult;
624 // couldn't find widths -- use defaults
626 //~ certain PDF generators apparently don't include widths
627 //~ for Arial and TimesNewRoman -- and this error message
629 error(-1, "No character widths resource for non-builtin font");
641 defWidths = defCharWidths[index];
642 code2 = 0; // to make gcc happy
643 for (code = 0; code < 256; ++code) {
644 if ((charName = encoding->getCharName(code)) &&
645 (code2 = standardEncoding.getCharCode(charName)) >= 0)
646 widths[code] = defWidths[code2] * 0.001;
653 void GfxFont::getType0EncAndWidths(Dict *fontDict) {
654 Object obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8;
658 widths16.exceps = NULL;
659 widths16.excepsV = NULL;
662 fontDict->lookup("DescendantFonts", &obj1);
663 if (!obj1.isArray() || obj1.arrayGetLength() != 1) {
664 error(-1, "Bad DescendantFonts entry for Type 0 font");
667 obj1.arrayGet(0, &obj2);
668 if (!obj2.isDict()) {
669 error(-1, "Bad descendant font of Type 0 font");
674 obj2.dictLookup("CIDSystemInfo", &obj3);
675 if (!obj3.isDict()) {
676 error(-1, "Bad CIDSystemInfo in Type 0 font descendant");
679 obj3.dictLookup("Registry", &obj4);
680 obj3.dictLookup("Ordering", &obj5);
681 if (obj4.isString() && obj5.isString()) {
682 if (obj4.getString()->cmp("Adobe") == 0 &&
683 obj5.getString()->cmp("Japan1") == 0) {
686 enc16.charSet = font16AdobeJapan12;
688 error(-1, "Xpdf was compiled without Japanese font support");
691 } else if (obj4.getString()->cmp("Adobe") == 0 &&
692 obj5.getString()->cmp("GB1") == 0) {
693 #if CHINESE_GB_SUPPORT
695 enc16.charSet = font16AdobeGB12;
697 error(-1, "Xpdf was compiled without Chinese GB font support");
700 } else if (obj4.getString()->cmp("Adobe") == 0 &&
701 obj5.getString()->cmp("CNS1") == 0) {
702 #if CHINESE_CNS_SUPPORT
704 enc16.charSet = font16AdobeCNS13;
706 error(-1, "Xpdf was compiled without Chinese CNS font support");
710 error(-1, "Uknown Type 0 character set: %s-%s",
711 obj4.getString()->getCString(), obj5.getString()->getCString());
715 error(-1, "Unknown Type 0 character set");
722 // get default char width
723 obj2.dictLookup("DW", &obj3);
725 widths16.defWidth = obj3.getInt() * 0.001;
727 widths16.defWidth = 1.0;
730 // get default char metrics for vertical font
731 obj2.dictLookup("DW2", &obj3);
732 widths16.defVY = 0.880;
733 widths16.defHeight = -1;
734 if (obj3.isArray() && obj3.arrayGetLength() == 2) {
735 obj3.arrayGet(0, &obj4);
737 widths16.defVY = obj4.getInt() * 0.001;
740 obj3.arrayGet(1, &obj4);
742 widths16.defHeight = obj4.getInt() * 0.001;
748 // get char width exceptions
749 widths16.exceps = NULL;
750 widths16.numExceps = 0;
751 obj2.dictLookup("W", &obj3);
752 if (obj3.isArray()) {
756 while (i+1 < obj3.arrayGetLength()) {
757 obj3.arrayGet(i, &obj4);
758 obj3.arrayGet(i+1, &obj5);
759 if (obj4.isInt() && obj5.isInt()) {
760 obj3.arrayGet(i+2, &obj6);
762 error(-1, "Bad widths array in Type 0 font");
768 if (k == excepsSize) {
770 widths16.exceps = (GfxFontWidthExcep *)
771 grealloc(widths16.exceps,
772 excepsSize * sizeof(GfxFontWidthExcep));
774 widths16.exceps[k].first = obj4.getInt();
775 widths16.exceps[k].last = obj5.getInt();
776 widths16.exceps[k].width = obj6.getNum() * 0.001;
780 } else if (obj4.isInt() && obj5.isArray()) {
781 if (k + obj5.arrayGetLength() >= excepsSize) {
782 excepsSize = (k + obj5.arrayGetLength() + 15) & ~15;
783 widths16.exceps = (GfxFontWidthExcep *)
784 grealloc(widths16.exceps,
785 excepsSize * sizeof(GfxFontWidthExcep));
788 for (j = 0; j < obj5.arrayGetLength(); ++j) {
789 obj5.arrayGet(j, &obj6);
791 error(-1, "Bad widths array in Type 0 font");
795 widths16.exceps[k].first = widths16.exceps[k].last = n++;
796 widths16.exceps[k].width = obj6.getNum() * 0.001;
802 error(-1, "Bad widths array in Type 0 font");
811 widths16.numExceps = k;
813 qsort(widths16.exceps, k, sizeof(GfxFontWidthExcep), &cmpWidthExcep);
817 // get char metric exceptions for vertical font
818 widths16.excepsV = NULL;
819 widths16.numExcepsV = 0;
820 obj2.dictLookup("W2", &obj3);
821 if (obj3.isArray()) {
825 while (i+1 < obj3.arrayGetLength()) {
826 obj3.arrayGet(i, &obj4);
827 obj3.arrayGet(i+1, &obj5);
828 if (obj4.isInt() && obj5.isInt()) {
829 obj3.arrayGet(i+2, &obj6);
830 obj3.arrayGet(i+3, &obj7);
831 obj3.arrayGet(i+4, &obj8);
832 if (!obj6.isNum() || !obj7.isNum() || !obj8.isNum()) {
833 error(-1, "Bad widths (W2) array in Type 0 font");
841 if (k == excepsSize) {
843 widths16.excepsV = (GfxFontWidthExcepV *)
844 grealloc(widths16.excepsV,
845 excepsSize * sizeof(GfxFontWidthExcepV));
847 widths16.excepsV[k].first = obj4.getInt();
848 widths16.excepsV[k].last = obj5.getInt();
849 widths16.excepsV[k].height = obj6.getNum() * 0.001;
850 widths16.excepsV[k].vx = obj7.getNum() * 0.001;
851 widths16.excepsV[k].vy = obj8.getNum() * 0.001;
857 } else if (obj4.isInt() && obj5.isArray()) {
858 if (k + obj5.arrayGetLength() / 3 >= excepsSize) {
859 excepsSize = (k + obj5.arrayGetLength() / 3 + 15) & ~15;
860 widths16.excepsV = (GfxFontWidthExcepV *)
861 grealloc(widths16.excepsV,
862 excepsSize * sizeof(GfxFontWidthExcepV));
865 for (j = 0; j < obj5.arrayGetLength(); j += 3) {
866 obj5.arrayGet(j, &obj6);
867 obj5.arrayGet(j+1, &obj7);
868 obj5.arrayGet(j+1, &obj8);
869 if (!obj6.isNum() || !obj7.isNum() || !obj8.isNum()) {
870 error(-1, "Bad widths (W2) array in Type 0 font");
874 widths16.excepsV[k].first = widths16.exceps[k].last = n++;
875 widths16.excepsV[k].height = obj6.getNum() * 0.001;
876 widths16.excepsV[k].vx = obj7.getNum() * 0.001;
877 widths16.excepsV[k].vy = obj8.getNum() * 0.001;
885 error(-1, "Bad widths array in Type 0 font");
893 widths16.numExcepsV = k;
895 qsort(widths16.excepsV, k, sizeof(GfxFontWidthExcepV), &cmpWidthExcepV);
903 // get encoding (CMap)
904 fontDict->lookup("Encoding", &obj1);
905 if (!obj1.isName()) {
906 error(-1, "Bad encoding for Type 0 font");
910 if (enc16.charSet == font16AdobeJapan12) {
911 for (i = 0; gfxJapan12Tab[i].name; ++i) {
912 if (!strcmp(obj1.getName(), gfxJapan12Tab[i].name))
915 if (!gfxJapan12Tab[i].name) {
916 error(-1, "Unknown encoding '%s' for Adobe-Japan1-2 font",
920 enc16.enc = gfxJapan12Tab[i].enc;
923 #if CHINESE_GB_SUPPORT
924 if (enc16.charSet == font16AdobeGB12) {
925 for (i = 0; gfxGB12Tab[i].name; ++i) {
926 if (!strcmp(obj1.getName(), gfxGB12Tab[i].name))
929 if (!gfxGB12Tab[i].name) {
930 error(-1, "Unknown encoding '%s' for Adobe-GB1-2 font",
934 enc16.enc = gfxGB12Tab[i].enc;
937 #if CHINESE_CNS_SUPPORT
938 if (enc16.charSet == font16AdobeCNS13) {
939 for (i = 0; gfxCNS13Tab[i].name; ++i) {
940 if (!strcmp(obj1.getName(), gfxCNS13Tab[i].name))
943 if (!gfxCNS13Tab[i].name) {
944 error(-1, "Unknown encoding '%s' for Adobe-CNS1-3 font",
948 enc16.enc = gfxCNS13Tab[i].enc;
964 //~ fix this --> add 16-bit font support to FontFile
965 encoding = new FontEncoding();
966 makeWidths(fontDict, NULL, NULL, 0);
969 static int CDECL cmpWidthExcep(const void *w1, const void *w2) {
970 return ((GfxFontWidthExcep *)w1)->first - ((GfxFontWidthExcep *)w2)->first;
973 static int CDECL cmpWidthExcepV(const void *w1, const void *w2) {
974 return ((GfxFontWidthExcepV *)w1)->first - ((GfxFontWidthExcepV *)w2)->first;
977 //------------------------------------------------------------------------
979 //------------------------------------------------------------------------
981 GfxFontDict::GfxFontDict(Dict *fontDict) {
985 numFonts = fontDict->getLength();
986 fonts = (GfxFont **)gmalloc(numFonts * sizeof(GfxFont *));
987 for (i = 0; i < numFonts; ++i) {
988 fontDict->getValNF(i, &obj1);
990 if (obj1.isRef() && obj2.isDict()) {
991 fonts[i] = new GfxFont(fontDict->getKey(i), obj1.getRef(),
994 error(-1, "font resource is not a dictionary");
1002 GfxFontDict::~GfxFontDict() {
1005 for (i = 0; i < numFonts; ++i)
1010 GfxFont *GfxFontDict::lookup(char *tag) {
1013 for (i = 0; i < numFonts; ++i) {
1014 if (fonts[i]->matches(tag))