1 /* Copyright (C) 2000-2008 by George Williams */
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are met:
6 * Redistributions of source code must retain the above copyright notice, this
7 * list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
13 * The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "fontforgevw.h"
32 #include <sys/types.h>
43 # include </Developer/Headers/FlatCarbon/Files.h>
50 const int mac_dpi
= 72;
51 /* I had always assumed that the mac still believed in 72dpi screens, but I */
52 /* see that in geneva under OS/9, the pointsize does not match the pixel */
53 /* size of the font. But the dpi is not constant (and the differences */
54 /* excede those supplied by rounding errors) varying between 96 and 84dpi */
56 /* A Mac Resource fork */
57 /* http://developer.apple.com/techpubs/mac/MoreToolbox/MoreToolbox-9.html */
58 /* begins with a 16 byte header containing: */
59 /* resource start offset */
60 /* map start offset */
63 /* then 256-16 bytes of zeros */
64 /* the resource section consists of (many) */
65 /* 4 byte length count */
67 /* the map section contains */
68 /* A copy of the 16 byte header */
69 /* a 4 byte mac internal value (I hope) */
70 /* another 4 bytes of mac internal values (I hope) */
71 /* a 2 byte offset from the start of the map section to the list of resource types */
72 /* a 2 byte offset from the start of the map section to the list of resource names */
73 /* The resource type list consists of */
74 /* a 2 byte count of the number of resource types (-1) */
75 /* (many copies of) */
76 /* a 4 byte resource type ('FOND' for example) */
77 /* a 2 byte count of the number of resources of this type (-1) */
78 /* a 2 byte offset from the type list start to the resource table */
79 /* a resource table looks like */
80 /* a 2 byte offset from the resource name table to a pascal */
81 /* string containing this resource's name (or 0xffff for none) */
82 /* 1 byte of resource flags */
83 /* 3 bytes of offset from the resource section to the length & */
84 /* data of this instance of the resource type */
86 /* The resource name section consists of */
87 /* a bunch of pascal strings (ie. preceded by a length byte) */
89 /* The POST resource isn't noticeably documented, it's pretty much a */
90 /* straight copy of the pfb file cut up into 0x800 byte chunks. */
91 /* (each section of the pfb file has it's own set of chunks, the last may be smaller than 0x800) */
92 /* The NFNT resource http://developer.apple.com/techpubs/mac/Text/Text-250.html */
93 /* The FOND resource http://developer.apple.com/techpubs/mac/Text/Text-269.html */
94 /* The sfnt resource is basically a copy of the ttf file */
96 /* A MacBinary file */
97 /* http://www.lazerware.com/formats/macbinary.html */
98 /* begins with a 128 byte header */
99 /* (which specifies lengths for data/resource forks) */
100 /* (and contains mac type/creator data) */
101 /* (and other stuff) */
102 /* (and finally a crc checksum) */
103 /* is followed by the data section (padded to a mult of 128 bytes) */
104 /* is followed by the resource section (padded to a mult of 128 bytes) */
106 /* ******************************** Creation ******************************** */
118 struct resourcetype
{
120 struct resource
*res
;
124 struct macbinaryheader
{
126 char *binfilename
; /* if macfilename is null and this is set we will figure out macfilename by removing .bin */
132 enum psstyle_flags
{ psf_bold
= 1, psf_italic
= 2, psf_outline
= 4,
133 psf_shadow
= 0x8, psf_condense
= 0x10, psf_extend
= 0x20
136 uint16
_MacStyleCode(char *styles
, SplineFont
* sf
, uint16
* psstylecode
)
138 unsigned short stylecode
= 0, psstyle
= 0;
140 if (strstrmatch(styles
, "Bold") || strstrmatch(styles
, "Demi") ||
141 strstrmatch(styles
, "Heav") || strstrmatch(styles
, "Blac") ||
142 /* A few fonts have German/French styles in their names */
143 strstrmatch(styles
, "Fett") || strstrmatch(styles
, "Gras")) {
146 } else if (sf
!= NULL
&& sf
->weight
!= NULL
&&
147 (strstrmatch(sf
->weight
, "Bold")
148 || strstrmatch(sf
->weight
, "Demi")
149 || strstrmatch(sf
->weight
, "Heav")
150 || strstrmatch(sf
->weight
, "Blac")
151 || strstrmatch(sf
->weight
, "Fett")
152 || strstrmatch(sf
->weight
, "Gras"))) {
156 /* URW uses four leter abbreviations of Italic and Oblique */
157 /* Somebody else uses two letter abbrevs */
158 if ((sf
!= NULL
&& sf
->italicangle
!= 0) ||
159 strstrmatch(styles
, "Ital") ||
160 strstrmatch(styles
, "Obli") ||
161 strstrmatch(styles
, "Slanted") ||
162 strstrmatch(styles
, "Kurs") || strstr(styles
, "It")) {
163 stylecode
|= sf_italic
;
164 psstyle
|= psf_italic
;
166 if (strstrmatch(styles
, "Underline")) {
167 stylecode
|= sf_underline
;
169 if (strstrmatch(styles
, "Outl")) {
170 stylecode
|= sf_outline
;
171 psstyle
|= psf_outline
;
173 if (strstr(styles
, "Shadow") != NULL
) {
174 stylecode
|= sf_shadow
;
175 psstyle
|= psf_shadow
;
177 if (strstrmatch(styles
, "Cond") || strstr(styles
, "Cn") ||
178 strstrmatch(styles
, "Narrow")) {
179 stylecode
|= sf_condense
;
180 psstyle
|= psf_condense
;
182 if (strstrmatch(styles
, "Exte") || strstr(styles
, "Ex")) {
183 stylecode
|= sf_extend
;
184 psstyle
|= psf_extend
;
186 if ((psstyle
& psf_extend
) && (psstyle
& psf_condense
)) {
189 ("Warning: %s(%s) is both extended and condensed. That's impossible.\n"),
190 sf
->fontname
, sf
->origname
);
193 ("Warning: Both extended and condensed. That's impossible.\n"));
194 psstyle
&= ~psf_extend
;
195 stylecode
&= ~sf_extend
;
197 if (psstylecode
!= NULL
)
198 *psstylecode
= psstyle
;
204 /* ******************************** Reading ********************************* */
206 static SplineFont
*SearchPostscriptResources(FILE * f
, long rlistpos
,
207 int subcnt
, long rdata_pos
,
208 long name_list
, int flags
)
210 long here
= ftell(f
);
211 long *offsets
, lenpos
;
214 int len
, type
, i
, j
, rlen
;
215 unsigned short id
, *rsrcids
;
216 /* I don't pretend to understand the rational behind the format of a */
217 /* postscript font. It appears to be split up into chunks where the */
218 /* maximum chunk size is 0x800, each section (ascii, binary, ascii, eof) */
219 /* has its own set of chunks (ie chunks don't cross sections) */
226 fseek(f
, rlistpos
, SEEK_SET
);
227 rsrcids
= gcalloc(subcnt
, sizeof(short));
228 offsets
= gcalloc(subcnt
, sizeof(long));
229 for (i
= 0; i
< subcnt
; ++i
) {
230 rsrcids
[i
] = getushort(f
);
231 tmp
= (short) getushort(f
);
234 /* flags = */ getc(f
);
237 offsets
[i
] = rdata_pos
+ ((ch1
<< 16) | (ch2
<< 8) | getc(f
));
238 /* mbz = */ getlong(f
);
243 LogError(_("Can't open temporary file for postscript output\n"));
244 fseek(f
, here
, SEEK_SET
);
259 for (i
= 0; i
< subcnt
; ++i
) {
260 for (j
= 0; j
< subcnt
; ++j
)
261 if (rsrcids
[j
] == id
)
264 LogError(_("Missing POST resource %u\n"), id
);
268 fseek(f
, offsets
[j
], SEEK_SET
);
272 rlen
-= 2; /* those two bytes don't count as real data */
276 long hold
= ftell(pfb
);
277 fseek(pfb
, lenpos
, SEEK_SET
);
278 putc(len
>> 24, pfb
);
279 putc((len
>> 16) & 0xff, pfb
);
280 putc((len
>> 8) & 0xff, pfb
);
281 putc(len
& 0xff, pfb
);
282 fseek(pfb
, hold
, SEEK_SET
);
283 if (ch1
== 5) /* end of font mark */
300 buffer
= galloc(max
);
301 if (buffer
== NULL
) {
302 LogError(_("Out of memory\n"));
306 if(fread(buffer
, 1, rlen
, f
) != ((size_t)rlen
) ) {
307 LogError(_("Unable to read %u bytes for buffer\n"),rlen
);
310 fwrite(buffer
, 1, rlen
, pfb
);
317 fseek(pfb
, lenpos
, SEEK_SET
);
318 putc(len
>> 24, pfb
);
319 putc((len
>> 16) & 0xff, pfb
);
320 putc((len
>> 8) & 0xff, pfb
);
321 putc(len
& 0xff, pfb
);
322 fseek(f
, here
, SEEK_SET
);
324 if (flags
& ttf_onlynames
)
325 return ((SplineFont
*) _NamesReadPostscript(pfb
)); /* This closes the font for us */
327 fd
= _ReadPSFont(pfb
);
330 sf
= SplineFontFromPSFont(fd
);
332 /* There is no FOND in a postscript file, so we can't read any kerning */
338 static SplineFont
*SearchTtfResources(FILE * f
, long rlistpos
, int subcnt
,
339 long rdata_pos
, long name_list
,
340 char *filename
, int flags
,
341 enum openflags openflags
)
343 long here
, start
= ftell(f
);
347 int len
, i
, rlen
, ilen
;
348 /* The sfnt resource is just a copy of the ttf file */
355 char *pt
, *lparen
, *rparen
;
356 char *chosenname
= NULL
;
358 fseek(f
, rlistpos
, SEEK_SET
);
359 if (subcnt
> 1 || (flags
& ttf_onlynames
)) {
360 names
= gcalloc(subcnt
+ 1, sizeof(char *));
361 for (i
= 0; i
< subcnt
; ++i
) {
362 /* resource id = */ getushort(f
);
363 /* rname = (short) */ getushort(f
);
364 /* flags = */ getc(f
);
367 roff
= rdata_pos
+ ((ch1
<< 16) | (ch2
<< 8) | getc(f
));
368 /* mbz = */ getlong(f
);
370 names
[i
] = TTFGetFontName(f
, roff
+ 4, roff
+ 4);
371 if (names
[i
] == NULL
) {
373 sprintf(buffer
, "Nameless%d", i
);
374 names
[i
] = copy(buffer
);
376 fseek(f
, here
, SEEK_SET
);
378 if (flags
& ttf_onlynames
) {
379 return ((SplineFont
*) names
);
381 if ((pt
= strrchr(filename
, '/')) == NULL
)
383 /* Someone gave me a font "Nafees Nastaleeq(Updated).ttf" and complained */
384 /* that ff wouldn't open it */
385 /* Now someone will complain about "Nafees(Updated).ttc(fo(ob)ar)" */
386 if ((lparen
= strrchr(pt
, '(')) != NULL
&&
387 (rparen
= strrchr(lparen
, ')')) != NULL
&& rparen
[1] == '\0') {
388 char *find
= copy(lparen
+ 1);
389 pt
= strchr(find
, ')');
392 for (which
= subcnt
- 1; which
>= 0; --which
)
393 if (strcmp(names
[which
], find
) == 0)
397 which
= strtol(find
, &end
, 10);
402 char *fn
= copy(filename
);
403 fn
[lparen
- filename
] = '\0';
404 ff_post_error(_("Not in Collection"), _("%s is not in %.100s"),
411 if (lparen
== NULL
&& which
!= -1)
412 chosenname
= copy(names
[which
]);
413 for (i
= 0; i
< subcnt
; ++i
)
416 fseek(f
, rlistpos
, SEEK_SET
);
419 for (i
= 0; i
< subcnt
; ++i
) {
420 /* resource id = */ getushort(f
);
421 rname
= (short) getushort(f
);
422 /* flags = */ getc(f
);
425 roff
= rdata_pos
+ ((ch1
<< 16) | (ch2
<< 8) | getc(f
));
426 /* mbz = */ getlong(f
);
433 LogError(_("Can't open temporary file for truetype output.\n"));
437 fseek(f
, roff
, SEEK_SET
);
438 ilen
= rlen
= getlong(f
);
439 if (rlen
> 16 * 1024)
446 buffer
= malloc(max
);
448 for (len
= 0; len
< rlen
;) {
450 if (rlen
- len
< ilen
)
452 temp
= fread(buffer
, 1, temp
, f
);
455 fwrite(buffer
, 1, temp
, ttf
);
459 sf
= _SFReadTTF(ttf
, flags
, openflags
, NULL
, NULL
);
463 fseek(f
, start
, SEEK_SET
);
464 if (sf
->chosenname
== NULL
)
465 sf
->chosenname
= chosenname
;
468 fseek(f
, here
, SEEK_SET
);
472 fseek(f
, start
, SEEK_SET
);
476 typedef struct fond
{
481 short size
, style
, id
;
483 /* size==0 => scalable */
484 /* style>>8 is the bit depth (0=>1, 1=>2, 2=>4, 3=>8) */
485 /* search order for ID is sfnt, NFNT, FONT */
489 short *widthtab
; /* 4.12 fixed number with the width specified as a fraction of an em */
496 unsigned char ch1
, ch2
;
497 short offset
; /* 4.12 */
509 short kernMax
; /* bb learing */
510 short Descent
; /* maximum negative distance below baseline */
511 short fRectWidth
; /* bounding box width */
512 short fRectHeight
; /* bounding box height */
513 unsigned short *offsetWidths
; /* offset to start of offset/width table */
514 /* 0xffff => undefined, else high byte is offset in locTable, */
515 /* low byte is width */
519 short rowWords
; /* shorts per row */
520 unsigned short *fontImage
; /* rowWords*fRectHeight */
521 /* Images for all characters plus one extra for undefined */
522 unsigned short *locs
; /* lastchar-firstchar+3 words */
523 /* Horizontal offset to start of n'th character. Note: applies */
524 /* to each row. Missing characters have same loc as following */
527 static void FondListFree(FOND
* list
)
532 while (list
!= NULL
) {
535 for (i
= 0; i
< list
->stylewidthcnt
; ++i
)
536 free(list
->stylewidths
[i
].widthtab
);
537 free(list
->stylewidths
);
538 for (i
= 0; i
< list
->stylekerncnt
; ++i
)
539 free(list
->stylekerns
[i
].kerns
);
540 free(list
->stylekerns
);
541 for (i
= 0; i
< 48; ++i
)
542 free(list
->psnames
[i
]);
548 /* There's probably only one fond in the file, but there could be more so be */
550 /* I want the fond: */
551 /* to get the fractional widths for the SWIDTH entry on bdf */
552 /* to get the font name */
553 /* to get the font association tables */
554 /* to get the style flags */
555 /* http://developer.apple.com/techpubs/mac/Text/Text-269.html */
556 static FOND
*BuildFondList(FILE * f
, long rlistpos
, int subcnt
, long rdata_pos
,
557 long name_list
, int flags
)
559 long here
, start
= ftell(f
);
564 int i
, j
, k
, cnt
, isfixed
;
565 FOND
*head
= NULL
, *cur
;
566 long widoff
, kernoff
, styleoff
;
568 fseek(f
, rlistpos
, SEEK_SET
);
569 for (i
= 0; i
< subcnt
; ++i
) {
570 /* resource id = */ getushort(f
);
571 rname
= (short) getushort(f
);
572 /* flags = */ getc(f
);
575 offset
= rdata_pos
+ ((ch1
<< 16) | (ch2
<< 8) | getc(f
));
576 /* mbz = */ getlong(f
);
579 cur
= gcalloc(1, sizeof(FOND
));
584 fseek(f
, name_list
+ rname
, SEEK_SET
);
586 if( fread(name
, 1, ch1
, f
) != ((size_t)ch1
)) {
587 LogError(_("Unable to read %u bytes for name, but going on.\n"),ch1
);
590 cur
->fondname
= copy(name
);
594 fseek(f
, offset
, SEEK_SET
);
595 isfixed
= getushort(f
) & 0x8000 ? 1 : 0;
596 /* family id = */ getushort(f
);
597 cur
->first
= getushort(f
);
598 cur
->last
= getushort(f
);
599 /* on a 1 point font... */
600 /* ascent = */ getushort(f
);
601 /* descent = (short) */ getushort(f
);
602 /* leading = */ getushort(f
);
603 /* widmax = */ getushort(f
);
604 if ((widoff
= getlong(f
)) != 0)
606 if ((kernoff
= getlong(f
)) != 0)
608 if ((styleoff
= getlong(f
)) != 0)
610 for (j
= 0; j
< 9; ++j
)
612 /* internal & undefined, for international scripts = */ getlong(f
);
613 /* version = */ getushort(f
);
614 cur
->assoc_cnt
= getushort(f
) + 1;
615 cur
->assoc
= gcalloc(cur
->assoc_cnt
, sizeof(struct assoc
));
616 for (j
= 0; j
< cur
->assoc_cnt
; ++j
) {
617 cur
->assoc
[j
].size
= getushort(f
);
618 cur
->assoc
[j
].style
= getushort(f
);
619 cur
->assoc
[j
].id
= getushort(f
);
622 fseek(f
, widoff
, SEEK_SET
);
623 cnt
= getushort(f
) + 1;
624 cur
->stylewidthcnt
= cnt
;
625 cur
->stylewidths
= gcalloc(cnt
, sizeof(struct stylewidths
));
626 for (j
= 0; j
< cnt
; ++j
) {
627 cur
->stylewidths
[j
].style
= getushort(f
);
628 cur
->stylewidths
[j
].widthtab
=
629 galloc((cur
->last
- cur
->first
+ 3) * sizeof(short));
630 for (k
= cur
->first
; k
<= cur
->last
+ 2; ++k
)
631 cur
->stylewidths
[j
].widthtab
[k
] = getushort(f
);
634 if (kernoff
!= 0 && (flags
& ttf_onlykerns
)) {
635 fseek(f
, kernoff
, SEEK_SET
);
636 cnt
= getushort(f
) + 1;
637 cur
->stylekerncnt
= cnt
;
638 cur
->stylekerns
= gcalloc(cnt
, sizeof(struct stylekerns
));
639 for (j
= 0; j
< cnt
; ++j
) {
640 cur
->stylekerns
[j
].style
= getushort(f
);
641 cur
->stylekerns
[j
].kernpairs
= getushort(f
);
642 cur
->stylekerns
[j
].kerns
=
643 galloc(cur
->stylekerns
[j
].kernpairs
* sizeof(struct kerns
));
644 for (k
= 0; k
< cur
->stylekerns
[j
].kernpairs
; ++k
) {
645 cur
->stylekerns
[j
].kerns
[k
].ch1
= getc(f
);
646 cur
->stylekerns
[j
].kerns
[k
].ch2
= getc(f
);
647 cur
->stylekerns
[j
].kerns
[k
].offset
= getushort(f
);
652 uint8 stringoffsets
[48];
653 int strcnt
, stringlen
, format
;
655 fseek(f
, styleoff
, SEEK_SET
);
656 /* class = */ getushort(f
);
657 /* glyph encoding offset = */ getlong(f
);
658 /* reserved = */ getlong(f
);
659 for (j
= 0; j
< 48; ++j
)
660 stringoffsets
[j
] = getc(f
);
661 strcnt
= getushort(f
);
662 strings
= galloc(strcnt
* sizeof(char *));
663 for (j
= 0; j
< strcnt
; ++j
) {
665 strings
[j
] = galloc(stringlen
+ 2);
666 strings
[j
][0] = stringlen
;
667 strings
[j
][stringlen
+ 1] = '\0';
668 for (k
= 0; k
< stringlen
; ++k
)
669 strings
[j
][k
+ 1] = getc(f
);
671 for (j
= 0; j
< 48; ++j
) {
672 for (k
= j
- 1; k
>= 0; --k
)
673 if (stringoffsets
[j
] == stringoffsets
[k
])
676 continue; /* this style doesn't exist */
677 format
= stringoffsets
[j
] - 1;
678 stringlen
= strings
[0][0];
680 for (k
= 0; k
< strings
[format
][0]; ++k
)
681 stringlen
+= strings
[strings
[format
][k
+ 1] - 1][0];
682 pt
= cur
->psnames
[j
] = galloc(stringlen
+ 1);
683 strcpy(pt
, strings
[0] + 1);
686 for (k
= 0; k
< strings
[format
][0]; ++k
) {
687 strcpy(pt
, strings
[strings
[format
][k
+ 1] - 1] + 1);
688 pt
+= strings
[strings
[format
][k
+ 1] - 1][0];
692 for (j
= 0; j
< strcnt
; ++j
)
696 fseek(f
, here
, SEEK_SET
);
698 fseek(f
, start
, SEEK_SET
);
702 static char *BuildName(char *family
, int style
)
704 char buffer
[350] = "";
706 strncpy(buffer
, family
, 200);
710 strcat(buffer
, "Bold");
711 if (style
& sf_italic
)
712 strcat(buffer
, "Italic");
713 if (style
& sf_underline
)
714 strcat(buffer
, "Underline");
715 if (style
& sf_outline
)
716 strcat(buffer
, "Outline");
717 if (style
& sf_shadow
)
718 strcat(buffer
, "Shadow");
719 if (style
& sf_condense
)
720 strcat(buffer
, "Condensed");
721 if (style
& sf_extend
)
722 strcat(buffer
, "Extended");
723 return (copy(buffer
));
726 static int GuessStyle(char *fontname
, int *styles
, int style_cnt
)
729 char *stylenames
= _GetModifiers(fontname
, NULL
, NULL
);
731 style
= _MacStyleCode(stylenames
, NULL
, NULL
);
732 for (which
= style_cnt
; which
>= 0; --which
)
733 if (styles
[which
] == style
)
739 static FOND
*PickFOND(FOND
* fondlist
, char *filename
, char **name
, int *style
)
743 uint8 stylesused
[96];
745 FOND
**fonds
= NULL
, *fond
= NULL
;
751 if ((pt
= strrchr(filename
, '/')) != NULL
)
753 if ((lparen
= strchr(filename
, '(')) != NULL
&& strchr(lparen
, ')') != NULL
) {
754 find
= copy(lparen
+ 1);
755 pt
= strchr(find
, ')');
758 for (test
= fondlist
; test
!= NULL
; test
= test
->next
) {
759 for (i
= 0; i
< 48; ++i
)
760 if (test
->psnames
[i
] != NULL
761 && strcmp(find
, test
->psnames
[i
]) == 0) {
762 *style
= (i
& 3) | ((i
& ~3) << 1); /* PS styles skip underline bit */
763 *name
= copy(test
->psnames
[i
]);
769 /* The file may contain multiple families, and each family may contain */
770 /* multiple styles (and each style may contain multiple sizes, but that's */
771 /* not an issue for us here) */
773 for (i
= 0; i
< 2; ++i
) {
775 for (test
= fondlist
; test
!= NULL
; test
= test
->next
)
776 if (test
->fondname
!= NULL
) {
777 memset(stylesused
, 0, sizeof(stylesused
));
778 for (j
= 0; j
< test
->assoc_cnt
; ++j
) {
779 if (test
->assoc
[j
].size
!= 0
780 && !stylesused
[test
->assoc
[j
].style
]) {
781 stylesused
[test
->assoc
[j
].style
] = true;
784 BuildName(test
->fondname
, test
->assoc
[j
].style
);
785 styles
[cnt
] = test
->assoc
[j
].style
;
793 names
= gcalloc(cnt
+ 1, sizeof(char *));
794 fonds
= galloc(cnt
* sizeof(FOND
*));
795 styles
= galloc(cnt
* sizeof(int));
800 for (which
= cnt
- 1; which
>= 0; --which
)
801 if (strcmp(names
[which
], find
) == 0)
803 if (which
== -1 && strstrmatch(find
, test
->fondname
) != NULL
)
804 which
= GuessStyle(find
, styles
, cnt
);
806 char *fn
= copy(filename
);
807 fn
[lparen
- filename
] = '\0';
808 ff_post_error(_("Not in Collection"), _("%s is not in %.100s"),
818 *name
= copy(names
[which
]);
819 *style
= styles
[which
];
821 for (i
= 0; i
< cnt
; ++i
)
833 /* Look for kerning info and merge it into the currently existing font "into" */
834 static SplineFont
*FindFamilyStyleKerns(SplineFont
* into
, EncMap
* map
,
835 FOND
* fondlist
, char *filename
)
841 int ch1
, ch2
, offset
;
843 SplineChar
*sc1
, *sc2
;
845 fond
= PickFOND(fondlist
, filename
, &name
, &style
);
846 if (fond
== NULL
|| into
== NULL
)
848 for (i
= 0; i
< fond
->stylekerncnt
; ++i
)
849 if (fond
->stylekerns
[i
].style
== style
)
851 if (i
== fond
->stylekerncnt
) {
852 LogError(_("No kerning table for %s\n"), name
);
856 for (j
= 0; j
< fond
->stylekerns
[i
].kernpairs
; ++j
) {
857 ch1
= fond
->stylekerns
[i
].kerns
[j
].ch1
;
858 ch2
= fond
->stylekerns
[i
].kerns
[j
].ch2
;
860 (fond
->stylekerns
[i
].kerns
[j
].offset
*
861 (into
->ascent
+ into
->descent
) + (1 << 11)) >> 12;
862 sc1
= SFMakeChar(into
, map
, ch1
);
863 sc2
= SFMakeChar(into
, map
, ch2
);
864 for (kp
= sc1
->kerns
; kp
!= NULL
; kp
= kp
->next
)
869 kp
= chunkalloc(sizeof(KernPair
));
871 kp
->next
= sc1
->kerns
;
873 script
= SCScriptFromUnicode(sc1
);
874 if (script
== DEFAULT_SCRIPT
)
875 script
= SCScriptFromUnicode(sc2
);
877 SFSubTableFindOrMake(sc1
->parent
, CHR('k', 'e', 'r', 'n'),
885 /* Look for a bare truetype font in a binhex/macbinary wrapper */
886 static SplineFont
*MightBeTrueType(FILE * binary
, int32 pos
, int32 dlen
,
887 int flags
, enum openflags openflags
)
894 if (flags
& ttf_onlynames
) {
896 char *temp
= TTFGetFontName(binary
, pos
, pos
);
899 ret
= galloc(2 * sizeof(char *));
902 return ((SplineFont
*) ret
);
905 buffer
= galloc(8192);
908 fseek(binary
, pos
, SEEK_SET
);
910 len
= dlen
> 8192 ? 8192 : dlen
;
911 len
= fread(buffer
, 1, dlen
> 8192 ? 8192 : dlen
, binary
);
914 fwrite(buffer
, 1, len
, temp
);
918 sf
= _SFReadTTF(temp
, flags
, openflags
, NULL
, NULL
);
924 static SplineFont
*IsResourceFork(FILE * f
, long offset
, char *filename
,
925 int flags
, enum openflags openflags
,
926 SplineFont
* into
, EncMap
* map
)
928 /* If it is a good resource fork then the first 16 bytes are repeated */
929 /* at the location specified in bytes 4-7 */
930 /* We include an offset because if we are looking at a mac binary file */
931 /* the resource fork will actually start somewhere in the middle of the */
932 /* file, not at the beginning */
933 unsigned char buffer
[16], buffer2
[16];
934 long rdata_pos
, map_pos
, type_list
, name_list
, rpos
;
935 int32 rdata_len
, map_len
;
936 uint32 nfnt_pos
, font_pos
, fond_pos
;
938 int i
, cnt
, subcnt
, nfnt_subcnt
= 0, font_subcnt
= 0, fond_subcnt
= 0;
940 FOND
*fondlist
= NULL
;
942 fseek(f
, offset
, SEEK_SET
);
943 if (fread(buffer
, 1, 16, f
) != 16)
947 ((buffer
[0] << 24) | (buffer
[1] << 16) | (buffer
[2] << 8) | buffer
[3]);
950 ((buffer
[4] << 24) | (buffer
[5] << 16) | (buffer
[6] << 8) | buffer
[7]);
952 ((buffer
[8] << 24) | (buffer
[9] << 16) | (buffer
[10] << 8) |
955 ((buffer
[12] << 24) | (buffer
[13] << 16) | (buffer
[14] << 8) |
957 if (rdata_pos
+ rdata_len
!= map_pos
|| rdata_len
== 0)
959 fseek(f
, map_pos
, SEEK_SET
);
960 buffer2
[15] = buffer
[15] + 1; /* make it be different */
961 if (fread(buffer2
, 1, 16, f
) != 16)
964 /* Apple's data fork resources appear to have a bunch of zeroes here instead */
965 /* of a copy of the first 16 bytes */
966 for (i
= 0; i
< 16; ++i
)
970 for (i
= 0; i
< 16; ++i
)
971 if (buffer
[i
] != buffer2
[i
])
974 getlong(f
); /* skip the handle to the next resource map */
975 getushort(f
); /* skip the file resource number */
976 getushort(f
); /* skip the attributes */
977 type_list
= map_pos
+ getushort(f
);
978 name_list
= map_pos
+ getushort(f
);
980 fseek(f
, type_list
, SEEK_SET
);
981 cnt
= getushort(f
) + 1;
982 for (i
= 0; i
< cnt
; ++i
) {
984 /* printf( "%c%c%c%c\n", tag>>24, (tag>>16)&0xff, (tag>>8)&0xff, tag&0xff ); */
985 subcnt
= getushort(f
) + 1;
986 rpos
= type_list
+ getushort(f
);
988 if (tag
== CHR('P', 'O', 'S', 'T') && !(flags
& (ttf_onlystrikes
| ttf_onlykerns
))) /* No FOND */
989 sf
= SearchPostscriptResources(f
, rpos
, subcnt
, rdata_pos
,
991 else if (tag
== CHR('s', 'f', 'n', 't') && !(flags
& ttf_onlykerns
))
992 sf
= SearchTtfResources(f
, rpos
, subcnt
, rdata_pos
, name_list
,
993 filename
, flags
, openflags
);
994 else if (tag
== CHR('N', 'F', 'N', 'T')) {
996 nfnt_subcnt
= subcnt
;
997 } else if (tag
== CHR('F', 'O', 'N', 'T')) {
999 font_subcnt
= subcnt
;
1000 } else if (tag
== CHR('F', 'O', 'N', 'D')) {
1002 fond_subcnt
= subcnt
;
1007 if (flags
& ttf_onlynames
) /* Not interested in bitmap resources here */
1010 if (flags
& ttf_onlykerns
) { /* For kerns */
1011 if (fond_subcnt
!= 0)
1013 BuildFondList(f
, fond_pos
, fond_subcnt
, rdata_pos
, name_list
,
1015 into
= FindFamilyStyleKerns(into
, map
, fondlist
, filename
);
1016 FondListFree(fondlist
);
1019 /* Ok. If no outline font, try for a bitmap */
1020 if (nfnt_subcnt
== 0) {
1021 nfnt_pos
= font_pos
;
1022 nfnt_subcnt
= font_subcnt
;
1024 return ((SplineFont
*) - 1); /* It's a valid resource file, but just has no fonts */
1028 static SplineFont
*IsResourceInBinary(FILE * f
, char *filename
, int flags
,
1029 enum openflags openflags
,
1030 SplineFont
* into
, EncMap
* map
)
1032 unsigned char header
[128];
1033 unsigned long offset
, dlen
, rlen
;
1035 if (fread(header
, 1, 128, f
) != 128)
1037 if (header
[0] != 0 || header
[74] != 0 || header
[82] != 0 || header
[1] <= 0
1038 || header
[1] > 33 || header
[63] != 0 || header
[2 + header
[1]] != 0)
1041 ((header
[0x53] << 24) | (header
[0x54] << 16) | (header
[0x55] << 8) |
1044 ((header
[0x57] << 24) | (header
[0x58] << 16) | (header
[0x59] << 8) |
1046 /* 128 bytes for header, then the dlen is padded to a 128 byte boundary */
1047 offset
= 128 + ((dlen
+ 127) & ~127);
1048 /* Look for a bare truetype font in a binhex/macbinary wrapper */
1049 if (dlen
!= 0 && rlen
<= dlen
) {
1051 if (fread(header
, 1, 4, f
) != ((size_t)4)) {
1052 LogError(_("Unable to read 4 bytes for header, but going on.\n"));
1055 if (strcmp((char *) header
, "OTTO") == 0
1056 || strcmp((char *) header
, "true") == 0
1057 || strcmp((char *) header
, "ttcf") == 0 || (header
[0] == 0
1061 return (MightBeTrueType(f
, pos
, dlen
, flags
, openflags
));
1063 return (IsResourceFork(f
, offset
, filename
, flags
, openflags
, into
, map
));
1066 static int lastch
= 0, repeat
= 0;
1067 static void outchr(FILE * binary
, int ch
)
1073 /* no repeat, output a literal 0x90 (the repeat flag) */
1075 putc(lastch
, binary
);
1077 for (i
= 1; i
< ch
; ++i
)
1078 putc(lastch
, binary
);
1081 } else if (ch
== 0x90) {
1089 static SplineFont
*IsResourceInHex(FILE * f
, char *filename
, int flags
,
1090 enum openflags openflags
, SplineFont
* into
,
1093 /* convert file from 6bit to 8bit */
1094 /* interesting data is enclosed between two colons */
1095 FILE *binary
= tmpfile();
1097 "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
1098 int ch
, val
, cnt
, i
, dlen
, rlen
;
1099 unsigned char header
[20];
1103 if (binary
== NULL
) {
1104 LogError(_("can't create temporary file\n"));
1108 lastch
= repeat
= 0;
1109 while ((ch
= getc(f
)) != ':'); /* There may be comments before file start */
1111 while ((ch
= getc(f
)) != ':') {
1114 for (pt
= sixbit
; *pt
!= ch
&& *pt
!= '\0'; ++pt
);
1119 val
= (val
<< 6) | (pt
- sixbit
);
1121 outchr(binary
, (val
>> 16) & 0xff);
1122 outchr(binary
, (val
>> 8) & 0xff);
1123 outchr(binary
, val
& 0xff);
1129 outchr(binary
, val
<< 2);
1130 else if (cnt
== 2) {
1132 outchr(binary
, (val
>> 8) & 0xff);
1133 outchr(binary
, val
& 0xff);
1134 } else if (cnt
== 3) {
1136 outchr(binary
, (val
>> 16) & 0xff);
1137 outchr(binary
, (val
>> 8) & 0xff);
1138 outchr(binary
, val
& 0xff);
1143 ch
= getc(binary
); /* Name length */
1145 for (i
= 0; i
< ch
; ++i
)
1147 if (getc(binary
) != '\0') {
1151 if ( fread(header
, 1, 20, binary
) != 20 ) {
1152 LogError(_("Can't read 20 bytes for header\n"));
1156 (header
[10] << 24) | (header
[11] << 16) | (header
[12] << 8) |
1159 (header
[14] << 24) | (header
[15] << 16) | (header
[16] << 8) |
1161 /* Look for a bare truetype font in a binhex/macbinary wrapper */
1162 if (dlen
!= 0 && rlen
< dlen
) {
1163 int pos
= ftell(binary
);
1164 if (fread(header
, 1, 4, binary
) != 4) {
1165 LogError(_("Can't read 4 bytes for header\n"));
1169 if (strcmp((char *) header
, "OTTO") == 0
1170 || strcmp((char *) header
, "true") == 0
1171 || strcmp((char *) header
, "ttcf") == 0 || (header
[0] == 0
1174 && header
[3] == 0)) {
1175 ret
= MightBeTrueType(binary
, pos
, dlen
, flags
, openflags
);
1186 IsResourceFork(binary
, ftell(binary
) + dlen
+ 2, filename
, flags
,
1187 openflags
, into
, map
);
1193 static SplineFont
*IsResourceInFile(char *filename
, int flags
,
1194 enum openflags openflags
, SplineFont
* into
,
1200 char *temp
= filename
, *lparen
;
1202 if ((pt
= strrchr(filename
, '/')) == NULL
)
1204 if ((lparen
= strchr(pt
, '(')) != NULL
&& strchr(lparen
, ')') != NULL
) {
1205 temp
= copy(filename
);
1206 temp
[lparen
- filename
] = '\0';
1208 f
= fopen(temp
, "rb");
1209 if (temp
!= filename
)
1213 spt
= strrchr(filename
, '/');
1216 pt
= strrchr(spt
, '.');
1217 if (pt
!= NULL
&& (pt
[1] == 'b' || pt
[1] == 'B')
1218 && (pt
[2] == 'i' || pt
[2] == 'I') && (pt
[3] == 'n' || pt
[3] == 'N')
1219 && (pt
[4] == '\0' || pt
[4] == '(')) {
1220 if ((sf
= IsResourceInBinary(f
, filename
, flags
, openflags
, into
, map
))) {
1224 } else if (pt
!= NULL
&& (pt
[1] == 'h' || pt
[1] == 'H')
1225 && (pt
[2] == 'q' || pt
[2] == 'Q') && (pt
[3] == 'x'
1227 && (pt
[4] == '\0' || pt
[4] == '(')) {
1228 if ((sf
= IsResourceInHex(f
, filename
, flags
, openflags
, into
, map
))) {
1234 sf
= IsResourceFork(f
, 0, filename
, flags
, openflags
, into
, map
);
1238 sf
= HasResourceFork(filename
, flags
, openflags
, into
, map
);
1243 static SplineFont
*FindResourceFile(char *filename
, int flags
,
1244 enum openflags openflags
, SplineFont
* into
,
1247 char *spt
, *pt
, *dpt
;
1251 if ((sf
= IsResourceInFile(filename
, flags
, openflags
, into
, map
)))
1254 /* Well, look in the resource fork directory (if it exists), the resource */
1255 /* fork is placed there in a seperate file on (some) non-Mac disks */
1256 strcpy(buffer
, filename
);
1257 spt
= strrchr(buffer
, '/');
1263 pt
= filename
+ (spt
- buffer
);
1265 strcpy(spt
, "resource.frk/");
1267 if ((sf
= IsResourceInFile(buffer
, flags
, openflags
, into
, map
)))
1270 /* however the resource fork does not appear to do long names properly */
1271 /* names are always lower case 8.3, do some simple things to check */
1272 spt
= strrchr(buffer
, '/') + 1;
1273 for (pt
= spt
; *pt
; ++pt
)
1276 dpt
= strchr(spt
, '.');
1278 dpt
= spt
+ strlen(spt
);
1279 if (dpt
- spt
> 8 || strlen(dpt
) > 4) {
1281 strncpy(exten
, dpt
, 7);
1282 exten
[4] = '\0'; /* it includes the dot */
1289 return (IsResourceInFile(buffer
, flags
, openflags
, into
, map
));
1293 static char *createtmpfile(char *filename
)
1296 p
= strrchr(filename
,'/');
1300 assert(strlen(filename
)>=5);
1301 tempname
= malloc(strlen(filename
)+2);
1302 if (tempname
== NULL
) {
1303 LogError(_("Out of memory\n"));
1306 strcpy(tempname
,filename
);
1307 strcpy(tempname
+strlen(tempname
)-5,"XXXXXX"); /* dfont -> XXXXXX */
1311 int i
= mkstemp(tempname
);
1322 static char *SearchTtfResourcesFile(FILE * f
, long rlistpos
, int subcnt
,
1323 long rdata_pos
, long name_list
,
1324 char *filename
, char *fontname
)
1330 int len
, i
, rlen
, ilen
;
1331 /* The sfnt resource is just a copy of the ttf file */
1332 char *buffer
= NULL
;
1339 fseek(f
, rlistpos
, SEEK_SET
);
1341 names
= gcalloc(subcnt
+ 1, sizeof(char *));
1342 for (i
= 0; i
< subcnt
; ++i
) {
1343 /* resource id = */ getushort(f
);
1344 /* rname = (short) */ getushort(f
);
1345 /* flags = */ getc(f
);
1348 roff
= rdata_pos
+ ((ch1
<< 16) | (ch2
<< 8) | getc(f
));
1349 /* mbz = */ getlong(f
);
1351 names
[i
] = TTFGetPSFontName(f
, roff
+ 4, roff
+ 4);
1352 if (names
[i
] == NULL
) {
1354 sprintf(buffer
, "Nameless%d", i
);
1355 names
[i
] = copy(buffer
);
1357 fseek(f
, here
, SEEK_SET
);
1360 char *find
= fontname
;
1361 for (which
= subcnt
- 1; which
>= 0; --which
)
1362 if (strcmp(names
[which
], find
) == 0)
1366 which
= strtol(find
, &end
, 10);
1371 ff_post_error(_("Not in Collection"), _("%s is not in %.100s"),
1377 for (i
= 0; i
< subcnt
; ++i
)
1380 fseek(f
, rlistpos
, SEEK_SET
);
1383 for (i
= 0; i
< subcnt
; ++i
) {
1384 /* resource id = */ getushort(f
);
1385 rname
= (short) getushort(f
);
1386 /* flags = */ getc(f
);
1389 roff
= rdata_pos
+ ((ch1
<< 16) | (ch2
<< 8) | getc(f
));
1390 /* mbz = */ getlong(f
);
1395 sf
= createtmpfile(filename
);
1396 ttf
= fopen(sf
, "wb");
1398 LogError(_("Can't open temporary file for truetype output.\n"));
1402 fseek(f
, roff
, SEEK_SET
);
1403 ilen
= rlen
= getlong(f
);
1404 if (rlen
> 16 * 1024)
1411 buffer
= malloc(max
);
1413 for (len
= 0; len
< rlen
;) {
1415 if (rlen
- len
< ilen
)
1417 temp
= fread(buffer
, 1, temp
, f
);
1420 fwrite(buffer
, 1, temp
, ttf
);
1429 static char *IsResourceForkFile(FILE * f
, char *filename
, char *fontname
)
1431 /* If it is a good resource fork then the first 16 bytes are repeated */
1432 /* at the location specified in bytes 4-7 */
1433 /* We include an offset because if we are looking at a mac binary file */
1434 /* the resource fork will actually start somewhere in the middle of the */
1435 /* file, not at the beginning */
1436 unsigned char buffer
[16], buffer2
[16];
1437 long rdata_pos
, map_pos
, type_list
, name_list
, rpos
;
1438 int32 rdata_len
, map_len
;
1444 fseek(f
, 0, SEEK_SET
);
1445 if (fread(buffer
, 1, 16, f
) != 16)
1448 ((buffer
[0] << 24) | (buffer
[1] << 16) | (buffer
[2] << 8) | buffer
[3]);
1450 ((buffer
[4] << 24) | (buffer
[5] << 16) | (buffer
[6] << 8) | buffer
[7]);
1452 ((buffer
[8] << 24) | (buffer
[9] << 16) | (buffer
[10] << 8) |
1455 ((buffer
[12] << 24) | (buffer
[13] << 16) | (buffer
[14] << 8) |
1457 if (rdata_pos
+ rdata_len
!= map_pos
|| rdata_len
== 0)
1459 fseek(f
, map_pos
, SEEK_SET
);
1460 buffer2
[15] = buffer
[15] + 1; /* make it be different */
1461 if (fread(buffer2
, 1, 16, f
) != 16)
1463 for (i
= 0; i
< 16; ++i
)
1464 if (buffer2
[i
] != 0)
1467 for (i
= 0; i
< 16; ++i
)
1468 if (buffer
[i
] != buffer2
[i
])
1471 getlong(f
); /* skip the handle to the next resource map */
1472 getushort(f
); /* skip the file resource number */
1473 getushort(f
); /* skip the attributes */
1474 type_list
= map_pos
+ getushort(f
);
1475 name_list
= map_pos
+ getushort(f
);
1476 fseek(f
, type_list
, SEEK_SET
);
1477 cnt
= getushort(f
) + 1;
1478 for (i
= 0; i
< cnt
; ++i
) {
1480 subcnt
= getushort(f
) + 1;
1481 rpos
= type_list
+ getushort(f
);
1483 if (tag
== CHR('s', 'f', 'n', 't')) {
1484 sf
= SearchTtfResourcesFile(f
, rpos
, subcnt
, rdata_pos
, name_list
,
1485 filename
, fontname
);
1494 /* filename "/opt/tex/texmf-fonts/fonts/data/LucidaGrande.dfont",
1495 fontname "Lucida Grande Bold"
1497 char *FindResourceTtfFont(char *filename
, char *fontname
)
1500 FILE *f
= fopen(filename
, "rb");
1503 sf
= IsResourceForkFile(f
, filename
, fontname
);
1508 SplineFont
*SFReadMacBinary(char *filename
, int flags
, enum openflags openflags
)
1510 SplineFont
*sf
= FindResourceFile(filename
, flags
, openflags
, NULL
, NULL
);
1513 LogError(_("Couldn't find a font file named %s\n"), filename
);
1514 else if (sf
== (SplineFont
*) (-1)) {
1516 ("%s is a mac resource file but contains no postscript or truetype fonts\n"),
1523 char **NamesReadMacBinary(char *filename
)
1525 return ((char **) FindResourceFile(filename
, ttf_onlynames
, 0, NULL
, NULL
));
1528 /* should try to optimize this */
1529 SplineFont
*SFReadMacBinaryInfo(char *filename
, int flags
,
1530 enum openflags openflags
)
1532 SplineFont
*sf
= FindResourceFile(filename
, flags
, openflags
, NULL
, NULL
);
1535 LogError(_("Couldn't find a font file named %s\n"), filename
);
1536 else if (sf
== (SplineFont
*) (-1)) {
1538 ("%s is a mac resource file but contains no postscript or truetype fonts\n"),