1 //========================================================================
5 // Copyright 1996 Derek B. Noonburg
7 //========================================================================
10 #pragma implementation
35 //------------------------------------------------------------------------
37 static int CDECL cmpWidthExcep(const void *w1, const void *w2);
38 static int CDECL cmpWidthExcepV(const void *w1, const void *w2);
40 //------------------------------------------------------------------------
42 static Gushort *defCharWidths[12] = {
46 courierBoldObliqueWidths,
48 helveticaObliqueWidths,
50 helveticaBoldObliqueWidths,
57 //------------------------------------------------------------------------
59 //------------------------------------------------------------------------
61 GfxFont::GfxFont(char *tag1, Ref id1, Dict *fontDict) {
62 BuiltinFont *builtinFont;
63 Object obj1, obj2, obj3, obj4;
66 // get font tag and ID
67 tag = new GString(tag1);
72 fontDict->lookup("BaseFont", &obj1);
74 name = new GString(obj1.getName());
77 // is it a built-in font?
80 for (i = 0; i < numBuiltinFonts; ++i) {
81 if (!strcmp(builtinFonts[i].name, name->getCString())) {
82 builtinFont = &builtinFonts[i];
89 type = fontUnknownType;
90 fontDict->lookup("Subtype", &obj1);
91 if (obj1.isName("Type1"))
93 else if (obj1.isName("Type1C"))
95 else if (obj1.isName("Type3"))
97 else if (obj1.isName("TrueType"))
99 else if (obj1.isName("Type0"))
104 // assume Times-Roman (or TimesNewRoman), but explicitly check for
105 // Arial and CourierNew -- certain PDF generators apparently don't
106 // include FontDescriptors for Arial, TimesNewRoman, and CourierNew
107 flags = fontSerif; // assume Times-Roman by default
108 if (type == fontTrueType && !name->cmp("Arial"))
110 else if (type == fontTrueType && !name->cmp("CourierNew"))
111 flags = fontFixedWidth;
113 // get info from font descriptor
117 fontDict->lookup("FontDescriptor", &obj1);
121 obj1.dictLookup("Flags", &obj2);
123 flags = obj2.getInt();
127 obj1.dictLookup("FontName", &obj2);
129 embFontName = new GString(obj2.getName());
132 // look for embedded font file
133 if (type == fontType1) {
134 obj1.dictLookupNF("FontFile", &obj2);
136 embFontID = obj2.getRef();
139 if (embFontID.num == -1 && type == fontTrueType) {
140 obj1.dictLookupNF("FontFile2", &obj2);
142 embFontID = obj2.getRef();
145 if (embFontID.num == -1) {
146 obj1.dictLookupNF("FontFile3", &obj2);
148 embFontID = obj2.getRef();
150 if (obj3.isStream()) {
151 obj3.streamGetDict()->lookup("Subtype", &obj4);
152 if (obj4.isName("Type1"))
154 else if (obj4.isName("Type1C"))
156 else if (obj4.isName("Type3"))
158 else if (obj4.isName("TrueType"))
160 else if (obj4.isName("Type0"))
171 // look for an external font file
173 if (type == fontType1 && name)
177 fontMat[0] = fontMat[3] = 1;
178 fontMat[1] = fontMat[2] = fontMat[4] = fontMat[5] = 0;
179 if (fontDict->lookup("FontMatrix", &obj1)->isArray()) {
180 for (i = 0; i < 6 && i < obj1.arrayGetLength(); ++i) {
181 if (obj1.arrayGet(i, &obj2)->isNum())
182 fontMat[i] = obj2.getNum();
188 // get encoding and character widths
189 if (type == fontType0)
190 getType0EncAndWidths(fontDict);
192 getEncAndWidths(fontDict, builtinFont);
195 GfxFont::~GfxFont() {
199 if (!is16 && encoding)
206 gfree(widths16.exceps);
207 gfree(widths16.excepsV);
211 double GfxFont::getWidth(GString *s) {
216 for (i = 0; i < s->getLength(); ++i)
217 w += widths[s->getChar(i) & 0xff];
221 double GfxFont::getWidth16(int c) {
225 w = widths16.defWidth;
227 b = widths16.numExceps;
228 // invariant: widths16.exceps[a].last < c < widths16.exceps[b].first
231 if (widths16.exceps[m].last < c) {
233 } else if (c < widths16.exceps[m].first) {
236 w = widths16.exceps[m].width;
243 double GfxFont::getHeight16(int c) {
247 h = widths16.defHeight;
249 b = widths16.numExcepsV;
250 // invariant: widths16.excepsV[a].last < c < widths16.excepsV[b].first
253 if (widths16.excepsV[m].last < c) {
255 } else if (c < widths16.excepsV[m].first) {
258 h = widths16.excepsV[m].height;
265 double GfxFont::getOriginX16(int c) {
269 vx = widths16.defWidth / 2;
271 b = widths16.numExcepsV;
272 // invariant: widths16.excepsV[a].last < c < widths16.excepsV[b].first
275 if (widths16.excepsV[m].last < c) {
277 } else if (c < widths16.excepsV[m].first) {
280 vx = widths16.excepsV[m].vx;
287 double GfxFont::getOriginY16(int c) {
293 b = widths16.numExcepsV;
294 // invariant: widths16.excepsV[a].last < c < widths16.excepsV[b].first
297 if (widths16.excepsV[m].last < c) {
299 } else if (c < widths16.excepsV[m].first) {
302 vy = widths16.excepsV[m].vy;
309 void GfxFont::getEncAndWidths(Dict *fontDict, BuiltinFont *builtinFont) {
310 Object obj1, obj2, obj3;
316 // Encodings start with a base encoding, which can come from
317 // (in order of priority):
318 // 1. FontDict.Encoding or FontDict.Encoding.BaseEncoding
319 // - MacRoman / WinAnsi / Standard
320 // 2. embedded font file
322 // - builtin --> builtin encoding
323 // - TrueType --> MacRomanEncoding
324 // - others --> StandardEncoding
325 // and then add a list of differences from
326 // FontDict.Encoding.Differences.
328 // check FontDict for base encoding
330 fontDict->lookup("Encoding", &obj1);
332 obj1.dictLookup("BaseEncoding", &obj2);
333 if (obj2.isName("MacRomanEncoding")) {
334 encoding = macRomanEncoding.copy();
335 } else if (obj2.isName("WinAnsiEncoding")) {
336 encoding = winAnsiEncoding.copy();
337 } else if (obj2.isName("StandardEncoding")) {
338 encoding = standardEncoding.copy();
341 } else if (obj1.isName("MacRomanEncoding")) {
342 encoding = macRomanEncoding.copy();
343 } else if (obj1.isName("WinAnsiEncoding")) {
344 encoding = winAnsiEncoding.copy();
345 } else if (obj1.isName("StandardEncoding")) {
346 encoding = standardEncoding.copy();
350 // check embedded or external font file for base encoding
351 if ((type == fontType1 || type == fontType1C) &&
352 (extFontFile || embFontID.num >= 0)) {
354 buf = readExtFontFile(&len);
356 buf = readEmbFontFile(&len);
358 if (type == fontType1)
359 fontFile = new Type1FontFile(buf, len);
361 fontFile = new Type1CFontFile(buf, len);
362 if (fontFile->getName()) {
365 embFontName = new GString(fontFile->getName());
368 encoding = fontFile->getEncoding(gTrue);
374 // get default base encoding
377 encoding = builtinFont->encoding->copy();
378 else if (type == fontTrueType)
379 encoding = macRomanEncoding.copy();
381 encoding = standardEncoding.copy();
384 // merge differences into encoding
385 fontDict->lookup("Encoding", &obj1);
387 obj1.dictLookup("Differences", &obj2);
388 if (obj2.isArray()) {
390 for (i = 0; i < obj2.arrayGetLength(); ++i) {
391 obj2.arrayGet(i, &obj3);
393 code = obj3.getInt();
394 } else if (obj3.isName()) {
396 encoding->addChar(code, copyString(obj3.getName()));
399 error(-1, "Wrong type in font encoding resource differences (%s)",
409 // get character widths
411 makeWidths(fontDict, builtinFont->encoding, builtinFont->widths);
413 makeWidths(fontDict, NULL, NULL);
416 void GfxFont::findExtFontFile() {
420 for (path = fontPath; *path; ++path) {
421 extFontFile = appendToPath(new GString(*path), name->getCString());
422 f = fopen(extFontFile->getCString(), "rb");
424 extFontFile->append(".pfb");
425 f = fopen(extFontFile->getCString(), "rb");
428 extFontFile->del(extFontFile->getLength() - 4, 4);
429 extFontFile->append(".pfa");
430 f = fopen(extFontFile->getCString(), "rb");
441 char *GfxFont::readExtFontFile(int *len) {
445 if (!(f = fopen(extFontFile->getCString(), "rb"))) {
446 error(-1, "Internal: external font file '%s' vanished", extFontFile);
449 fseek(f, 0, SEEK_END);
450 *len = (int)ftell(f);
451 fseek(f, 0, SEEK_SET);
452 buf = (char *)gmalloc(*len);
453 if ((int)fread(buf, 1, *len, f) != *len)
454 error(-1, "Error reading external font file '%s'", extFontFile);
459 char *GfxFont::readEmbFontFile(int *len) {
466 obj1.initRef(embFontID.num, embFontID.gen);
468 if (!obj2.isStream()) {
469 error(-1, "Embedded font file is not a stream");
474 str = obj2.getStream();
479 while ((c = str->getChar()) != EOF) {
482 buf = (char *)grealloc(buf, size);
494 void GfxFont::makeWidths(Dict *fontDict, FontEncoding *builtinEncoding,
495 Gushort *builtinWidths) {
497 int firstChar, lastChar;
504 // initialize all widths to zero
505 for (code = 0; code < 256; ++code)
508 // use widths from built-in font
509 if (builtinEncoding) {
510 code2 = 0; // to make gcc happy
511 for (code = 0; code < 256; ++code) {
512 if ((charName = encoding->getCharName(code)) &&
513 (code2 = builtinEncoding->getCharCode(charName)) >= 0)
514 widths[code] = builtinWidths[code2] * 0.001;
517 // get widths from font dict
519 fontDict->lookup("FirstChar", &obj1);
520 firstChar = obj1.isInt() ? obj1.getInt() : 0;
522 fontDict->lookup("LastChar", &obj1);
523 lastChar = obj1.isInt() ? obj1.getInt() : 255;
525 if (type == fontType3)
529 fontDict->lookup("Widths", &obj1);
530 if (obj1.isArray()) {
531 for (code = firstChar; code <= lastChar; ++code) {
532 obj1.arrayGet(code - firstChar, &obj2);
534 widths[code] = obj2.getNum() * mult;
539 // couldn't find widths -- use defaults
541 //~ certain PDF generators apparently don't include widths
542 //~ for Arial and TimesNewRoman -- and this error message
544 error(-1, "No character widths resource for non-builtin font");
556 defWidths = defCharWidths[index];
557 code2 = 0; // to make gcc happy
558 for (code = 0; code < 256; ++code) {
559 if ((charName = encoding->getCharName(code)) &&
560 (code2 = standardEncoding.getCharCode(charName)) >= 0)
561 widths[code] = defWidths[code2] * 0.001;
568 void GfxFont::getType0EncAndWidths(Dict *fontDict) {
569 Object obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8;
573 widths16.exceps = NULL;
574 widths16.excepsV = NULL;
577 fontDict->lookup("DescendantFonts", &obj1);
578 if (!obj1.isArray() || obj1.arrayGetLength() != 1) {
579 error(-1, "Bad DescendantFonts entry for Type 0 font");
582 obj1.arrayGet(0, &obj2);
583 if (!obj2.isDict("Font")) {
584 error(-1, "Bad descendant font of Type 0 font");
589 obj2.dictLookup("CIDSystemInfo", &obj3);
590 if (!obj3.isDict()) {
591 error(-1, "Bad CIDSystemInfo in Type 0 font descendant");
594 obj3.dictLookup("Registry", &obj4);
595 obj3.dictLookup("Ordering", &obj5);
596 if (obj4.isString() && obj5.isString()) {
597 if (obj4.getString()->cmp("Adobe") == 0 &&
598 obj5.getString()->cmp("Japan1") == 0) {
601 enc16.charSet = font16AdobeJapan12;
603 error(-1, "Xpdf was compiled without Japanese font support");
607 error(-1, "Uknown Type 0 character set: %s-%s",
608 obj4.getString()->getCString(), obj5.getString()->getCString());
612 error(-1, "Unknown Type 0 character set");
619 // get default char width
620 obj2.dictLookup("DW", &obj3);
622 widths16.defWidth = obj3.getInt() * 0.001;
624 widths16.defWidth = 1.0;
627 // get default char metrics for vertical font
628 obj2.dictLookup("DW2", &obj3);
629 widths16.defVY = 0.880;
630 widths16.defHeight = -1;
631 if (obj3.isArray() && obj3.arrayGetLength() == 2) {
632 obj3.arrayGet(0, &obj4);
634 widths16.defVY = obj4.getInt() * 0.001;
637 obj3.arrayGet(1, &obj4);
639 widths16.defHeight = obj4.getInt() * 0.001;
645 // get char width exceptions
646 widths16.exceps = NULL;
647 widths16.numExceps = 0;
648 obj2.dictLookup("W", &obj3);
649 if (obj3.isArray()) {
653 while (i+1 < obj3.arrayGetLength()) {
654 obj3.arrayGet(i, &obj4);
655 obj3.arrayGet(i+1, &obj5);
656 if (obj4.isInt() && obj5.isInt()) {
657 obj3.arrayGet(i+2, &obj6);
659 error(-1, "Bad widths array in Type 0 font");
665 if (k == excepsSize) {
667 widths16.exceps = (GfxFontWidthExcep *)
668 grealloc(widths16.exceps,
669 excepsSize * sizeof(GfxFontWidthExcep));
671 widths16.exceps[k].first = obj4.getInt();
672 widths16.exceps[k].last = obj5.getInt();
673 widths16.exceps[k].width = obj6.getNum() * 0.001;
677 } else if (obj4.isInt() && obj5.isArray()) {
678 if (k + obj5.arrayGetLength() >= excepsSize) {
679 excepsSize = (k + obj5.arrayGetLength() + 15) & ~15;
680 widths16.exceps = (GfxFontWidthExcep *)
681 grealloc(widths16.exceps,
682 excepsSize * sizeof(GfxFontWidthExcep));
685 for (j = 0; j < obj5.arrayGetLength(); ++j) {
686 obj5.arrayGet(j, &obj6);
688 error(-1, "Bad widths array in Type 0 font");
692 widths16.exceps[k].first = widths16.exceps[k].last = n++;
693 widths16.exceps[k].width = obj6.getNum() * 0.001;
699 error(-1, "Bad widths array in Type 0 font");
708 widths16.numExceps = k;
710 qsort(widths16.exceps, k, sizeof(GfxFontWidthExcep), &cmpWidthExcep);
714 // get char metric exceptions for vertical font
715 widths16.excepsV = NULL;
716 widths16.numExcepsV = 0;
717 obj2.dictLookup("W2", &obj3);
718 if (obj3.isArray()) {
722 while (i+1 < obj3.arrayGetLength()) {
723 obj3.arrayGet(i, &obj4);
724 obj3.arrayGet(i+1, &obj5);
725 if (obj4.isInt() && obj5.isInt()) {
726 obj3.arrayGet(i+2, &obj6);
727 obj3.arrayGet(i+3, &obj7);
728 obj3.arrayGet(i+4, &obj8);
729 if (!obj6.isNum() || !obj7.isNum() || !obj8.isNum()) {
730 error(-1, "Bad widths (W2) array in Type 0 font");
738 if (k == excepsSize) {
740 widths16.excepsV = (GfxFontWidthExcepV *)
741 grealloc(widths16.excepsV,
742 excepsSize * sizeof(GfxFontWidthExcepV));
744 widths16.excepsV[k].first = obj4.getInt();
745 widths16.excepsV[k].last = obj5.getInt();
746 widths16.excepsV[k].height = obj6.getNum() * 0.001;
747 widths16.excepsV[k].vx = obj7.getNum() * 0.001;
748 widths16.excepsV[k].vy = obj8.getNum() * 0.001;
754 } else if (obj4.isInt() && obj5.isArray()) {
755 if (k + obj5.arrayGetLength() / 3 >= excepsSize) {
756 excepsSize = (k + obj5.arrayGetLength() / 3 + 15) & ~15;
757 widths16.excepsV = (GfxFontWidthExcepV *)
758 grealloc(widths16.excepsV,
759 excepsSize * sizeof(GfxFontWidthExcepV));
762 for (j = 0; j < obj5.arrayGetLength(); j += 3) {
763 obj5.arrayGet(j, &obj6);
764 obj5.arrayGet(j+1, &obj7);
765 obj5.arrayGet(j+1, &obj8);
766 if (!obj6.isNum() || !obj7.isNum() || !obj8.isNum()) {
767 error(-1, "Bad widths (W2) array in Type 0 font");
771 widths16.excepsV[k].first = widths16.exceps[k].last = n++;
772 widths16.excepsV[k].height = obj6.getNum() * 0.001;
773 widths16.excepsV[k].vx = obj7.getNum() * 0.001;
774 widths16.excepsV[k].vy = obj8.getNum() * 0.001;
782 error(-1, "Bad widths array in Type 0 font");
790 widths16.numExcepsV = k;
792 qsort(widths16.excepsV, k, sizeof(GfxFontWidthExcepV), &cmpWidthExcepV);
800 // get encoding (CMap)
801 fontDict->lookup("Encoding", &obj1);
802 if (!obj1.isName()) {
803 error(-1, "Bad encoding for Type 0 font");
807 if (enc16.charSet == font16AdobeJapan12) {
808 for (i = 0; gfxFontEnc16Tab[i].name; ++i) {
809 if (!strcmp(obj1.getName(), gfxFontEnc16Tab[i].name))
812 if (!gfxFontEnc16Tab[i].name) {
813 error(-1, "Unknown encoding '%s' for Adobe-Japan1-2 font",
817 enc16.enc = gfxFontEnc16Tab[i].enc;
833 //~ fix this --> add 16-bit font support to FontFile
834 encoding = new FontEncoding();
835 makeWidths(fontDict, NULL, NULL);
838 static int CDECL cmpWidthExcep(const void *w1, const void *w2) {
839 return ((GfxFontWidthExcep *)w1)->first - ((GfxFontWidthExcep *)w2)->first;
842 static int CDECL cmpWidthExcepV(const void *w1, const void *w2) {
843 return ((GfxFontWidthExcepV *)w1)->first - ((GfxFontWidthExcepV *)w2)->first;
846 //------------------------------------------------------------------------
848 //------------------------------------------------------------------------
850 GfxFontDict::GfxFontDict(Dict *fontDict) {
854 numFonts = fontDict->getLength();
855 fonts = (GfxFont **)gmalloc(numFonts * sizeof(GfxFont *));
856 for (i = 0; i < numFonts; ++i) {
857 fontDict->getValNF(i, &obj1);
859 if (obj1.isRef() && obj2.isDict("Font")) {
860 fonts[i] = new GfxFont(fontDict->getKey(i), obj1.getRef(),
863 error(-1, "font resource is not a dictionary");
871 GfxFontDict::~GfxFontDict() {
874 for (i = 0; i < numFonts; ++i)
879 GfxFont *GfxFontDict::lookup(char *tag) {
882 for (i = 0; i < numFonts; ++i) {
883 if (fonts[i]->matches(tag))