1 //========================================================================
2 //
3 // WebFont.cc
4 //
5 // Copyright 2019 Glyph & Cog, LLC
6 //
7 //========================================================================
8 
9 #include <aconf.h>
10 
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
14 
15 #include "gmem.h"
16 #include "gmempp.h"
17 #include "GHash.h"
18 #include "FoFiTrueType.h"
19 #include "FoFiType1C.h"
20 #include "CharCodeToUnicode.h"
21 #include "WebFont.h"
22 
WebFont(GfxFont * gfxFontA,XRef * xref)23 WebFont::WebFont(GfxFont *gfxFontA, XRef *xref) {
24   GfxFontType type;
25   Ref id;
26 
27   gfxFont = gfxFontA;
28   fontBuf = NULL;
29   ffTrueType = NULL;
30   ffType1C = NULL;
31   isOpenType = gFalse;
32 
33   if (gfxFont->getEmbeddedFontID(&id)) {
34     type = gfxFont->getType();
35     if (type == fontTrueType ||
36 	type == fontTrueTypeOT ||
37 	type == fontCIDType2 ||
38 	type == fontCIDType2OT) {
39       if ((fontBuf = gfxFont->readEmbFontFile(xref, &fontLength))) {
40 	ffTrueType = FoFiTrueType::make(fontBuf, fontLength, 0);
41       }
42     } else if (type == fontType1C ||
43 	       type == fontCIDType0C) {
44       if ((fontBuf = gfxFont->readEmbFontFile(xref, &fontLength))) {
45 	ffType1C = FoFiType1C::make(fontBuf, fontLength);
46       }
47     } else if (type == fontType1COT ||
48 	       type == fontCIDType0COT) {
49       if ((fontBuf = gfxFont->readEmbFontFile(xref, &fontLength))) {
50 	isOpenType = gTrue;
51       }
52     }
53   }
54 }
55 
~WebFont()56 WebFont::~WebFont() {
57   delete ffTrueType;
58   delete ffType1C;
59   gfree(fontBuf);
60 }
61 
canWriteTTF()62 GBool WebFont::canWriteTTF() {
63   return ffTrueType != NULL;
64 }
65 
canWriteOTF()66 GBool WebFont::canWriteOTF() {
67   return ffType1C || isOpenType;
68 }
69 
writeToFile(void * stream,const char * data,int len)70 static void writeToFile(void *stream, const char *data, int len) {
71   fwrite(data, 1, len, (FILE *)stream);
72 }
73 
writeTTF(const char * fontFilePath)74 GBool WebFont::writeTTF(const char *fontFilePath) {
75   FILE *out;
76   int *codeToGID;
77   Guchar *cmapTable;
78   GBool freeCodeToGID;
79   int nCodes, cmapTableLength;
80 
81   if (!ffTrueType) {
82     return gFalse;
83   }
84   if (gfxFont->isCIDFont()) {
85     codeToGID = ((GfxCIDFont *)gfxFont)->getCIDToGID();
86     nCodes = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
87     if (!codeToGID) {
88       nCodes = ffTrueType->getNumGlyphs();
89     }
90     freeCodeToGID = gFalse;
91   } else {
92     codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ffTrueType);
93     nCodes = 256;
94     freeCodeToGID = gTrue;
95   }
96   cmapTable = makeUnicodeCmapTable(codeToGID, nCodes, &cmapTableLength);
97   if (freeCodeToGID) {
98     gfree(codeToGID);
99   }
100   if (!cmapTable) {
101     return gFalse;
102   }
103   if (!(out = fopen(fontFilePath, "wb"))) {
104     gfree(cmapTable);
105     return gFalse;
106   }
107   ffTrueType->writeTTF(writeToFile, out, NULL, NULL,
108 		       cmapTable, cmapTableLength);
109   fclose(out);
110   gfree(cmapTable);
111   return gTrue;
112 }
113 
writeOTF(const char * fontFilePath)114 GBool WebFont::writeOTF(const char *fontFilePath) {
115   int *codeToGID;
116   Gushort *widths;
117   Guchar *cmapTable;
118   FILE *out;
119   int nCodes, nWidths, cmapTableLength;
120 
121   if (ffType1C) {
122     if (gfxFont->getType() == fontType1C) {
123       codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ffType1C);
124       if (!(cmapTable = makeUnicodeCmapTable(codeToGID, 256,
125 					     &cmapTableLength))) {
126 	gfree(codeToGID);
127 	return gFalse;
128       }
129       widths = makeType1CWidths(codeToGID, 256, &nWidths);
130       gfree(codeToGID);
131     } else { // fontCIDType0C
132       codeToGID = ffType1C->getCIDToGIDMap(&nCodes);
133       if (!(cmapTable = makeUnicodeCmapTable(codeToGID, nCodes,
134 					     &cmapTableLength))) {
135 	gfree(codeToGID);
136 	return gFalse;
137       }
138       widths = makeCIDType0CWidths(codeToGID, nCodes, &nWidths);
139       gfree(codeToGID);
140     }
141     if (!(out = fopen(fontFilePath, "wb"))) {
142       gfree(cmapTable);
143       gfree(widths);
144       return gFalse;
145     }
146     ffType1C->convertToOpenType(writeToFile, out,
147 				nWidths, widths,
148 				cmapTable, cmapTableLength);
149     fclose(out);
150     gfree(cmapTable);
151     gfree(widths);
152 
153   } else if (isOpenType) {
154     if (!(out = fopen(fontFilePath, "wb"))) {
155       return gFalse;
156     }
157     if (fwrite(fontBuf, 1, fontLength, out) != (Guint)fontLength) {
158       fclose(out);
159       return gFalse;
160     }
161     fclose(out);
162 
163   } else {
164     return gFalse;
165   }
166 
167   return gTrue;
168 }
169 
makeType1CWidths(int * codeToGID,int nCodes,int * nWidths)170 Gushort *WebFont::makeType1CWidths(int *codeToGID, int nCodes,
171 				   int *nWidths) {
172   Gushort *widths;
173   Gushort width;
174   int widthsLen, gid, i;
175 
176   widthsLen = ffType1C->getNumGlyphs();
177   widths = (Gushort *)gmallocn(widthsLen, sizeof(Gushort));
178   for (i = 0; i < widthsLen; ++i) {
179     widths[i] = 0;
180   }
181   for (i = 0; i < nCodes; ++i) {
182     gid = codeToGID[i];
183     if (gid < 0 || gid >= widthsLen) {
184       continue;
185     }
186     width = (Gushort)(((Gfx8BitFont *)gfxFont)->getWidth((Guchar)i)
187 		      * 1000 + 0.5);
188     if (width == 0) {
189       continue;
190     }
191     widths[gid] = width;
192   }
193   *nWidths = widthsLen;
194   return widths;
195 }
196 
makeCIDType0CWidths(int * codeToGID,int nCodes,int * nWidths)197 Gushort *WebFont::makeCIDType0CWidths(int *codeToGID, int nCodes,
198 				      int *nWidths) {
199   Gushort *widths;
200   Gushort width;
201   int widthsLen, gid, i;
202 
203   widthsLen = ffType1C->getNumGlyphs();
204   widths = (Gushort *)gmallocn(widthsLen, sizeof(Gushort));
205   for (i = 0 ; i < widthsLen; ++i) {
206     widths[i] = 0;
207   }
208   for (i = 0; i < nCodes; ++i) {
209     gid = codeToGID[i];
210     if (gid < 0 || gid >= widthsLen) {
211       continue;
212     }
213     width = (Gushort)(((GfxCIDFont *)gfxFont)->getWidth((CID)i)
214 		      * 1000 + 0.5);
215     if (width == 0) {
216       continue;
217     }
218     widths[gid] = width;
219   }
220   *nWidths = widthsLen;
221   return widths;
222 }
223 
makeUnicodeCmapTable(int * codeToGID,int nCodes,int * unicodeCmapLength)224 Guchar *WebFont::makeUnicodeCmapTable(int *codeToGID, int nCodes,
225 				      int *unicodeCmapLength) {
226   int *unicodeToGID;
227   Guchar *cmapTable;
228   int unicodeToGIDLength, nMappings, len;
229   int nSegs, searchRange, entrySelector, rangeShift;
230   int glyphIdOffset, idRangeOffset;
231   int start, end, c, i;
232 
233   if (!(unicodeToGID = makeUnicodeToGID(codeToGID, nCodes,
234 					&unicodeToGIDLength))) {
235     return NULL;
236   }
237 
238   // count the valid code-to-glyph mappings, and the sequences of
239   // consecutive valid mappings
240   // (note: char code 65535 is used to mark the end of table)
241   nMappings = 0;
242   nSegs = 1; // count the last segment, mapping 65535
243   for (c = 0; c < unicodeToGIDLength && c <= 65534; ++c) {
244     if (unicodeToGID[c]) {
245       ++nMappings;
246       if (c == 0 || !unicodeToGID[c-1]) {
247 	++nSegs;
248       }
249     }
250   }
251 
252   i = 1;
253   entrySelector = 0;
254   while (2 * i <= nSegs) {
255     i *= 2;
256     ++entrySelector;
257   }
258   searchRange = 1 << (entrySelector + 1);
259   rangeShift = 2 * nSegs - searchRange;
260 
261   len = 28 + nSegs * 8 + nMappings * 2;
262   cmapTable = (Guchar *)gmalloc(len);
263 
264   // header
265   cmapTable[ 0] = 0x00;	// table version
266   cmapTable[ 1] = 0x00;
267   cmapTable[ 2] = 0x00;	// number of cmaps
268   cmapTable[ 3] = 0x01;
269   cmapTable[ 4] = 0x00;	// platform[0]
270   cmapTable[ 5] = 0x03;
271   cmapTable[ 6] = 0x00;	// encoding[0]
272   cmapTable[ 7] = 0x01;
273   cmapTable[ 8] = 0x00;	// offset[0]
274   cmapTable[ 9] = 0x00;
275   cmapTable[10] = 0x00;
276   cmapTable[11] = 0x0c;
277 
278   // table info
279   cmapTable[12] = 0x00;					// cmap format
280   cmapTable[13] = 0x04;
281   cmapTable[14] = (Guchar)((len - 12) >> 8);		// cmap length
282   cmapTable[15] = (Guchar)(len - 12);
283   cmapTable[16] = 0x00;					// cmap version
284   cmapTable[17] = 0x00;
285   cmapTable[18] = (Guchar)(nSegs >> 7);			// segCountX2
286   cmapTable[19] = (Guchar)(nSegs << 1);
287   cmapTable[20] = (Guchar)(searchRange >> 8);		// searchRange
288   cmapTable[21] = (Guchar)searchRange;
289   cmapTable[22] = (Guchar)(entrySelector >> 8);		// entrySelector
290   cmapTable[23] = (Guchar)entrySelector;
291   cmapTable[24] = (Guchar)(rangeShift >> 8);		// rangeShift
292   cmapTable[25] = (Guchar)rangeShift;
293   cmapTable[26 + nSegs*2    ] = 0;			// reservedPad
294   cmapTable[26 + nSegs*2 + 1] = 0;
295 
296   i = 0;
297   glyphIdOffset = 28 + nSegs*8;
298   for (c = 0; c < unicodeToGIDLength && c <= 65534; ++c) {
299     if (unicodeToGID[c]) {
300       if (c == 0 || !unicodeToGID[c-1]) {
301 	start = c;
302 	cmapTable[28 + nSegs*2 + i*2    ] = (Guchar)(start >> 8);
303 	cmapTable[28 + nSegs*2 + i*2 + 1] = (Guchar)start;
304 	cmapTable[28 + nSegs*4 + i*2    ] = (Guchar)0;	// idDelta
305 	cmapTable[28 + nSegs*4 + i*2 + 1] = (Guchar)0;
306 	idRangeOffset = glyphIdOffset - (28 + nSegs*6 + i*2);
307 	cmapTable[28 + nSegs*6 + i*2    ] = (Guchar)(idRangeOffset >> 8);
308 	cmapTable[28 + nSegs*6 + i*2 + 1] = (Guchar)idRangeOffset;
309       }
310       if (c == 65534 || !unicodeToGID[c+1]) {
311 	end = c;
312 	cmapTable[26 + i*2    ] = (Guchar)(end >> 8);
313 	cmapTable[26 + i*2 + 1] = (Guchar)end;
314 	++i;
315       }
316       cmapTable[glyphIdOffset++] = (Guchar)(unicodeToGID[c] >> 8);
317       cmapTable[glyphIdOffset++] = (Guchar)unicodeToGID[c];
318     }
319   }
320 
321   // last segment maps code 65535 to GID 0
322   cmapTable[26 + i*2    ] = (Guchar)0xff;		// end
323   cmapTable[26 + i*2 + 1] = (Guchar)0xff;
324   cmapTable[28 + nSegs*2 + i*2    ] = (Guchar)0xff;	// start
325   cmapTable[28 + nSegs*2 + i*2 + 1] = (Guchar)0xff;
326   cmapTable[28 + nSegs*4 + i*2    ] = (Guchar)0;	// idDelta
327   cmapTable[28 + nSegs*4 + i*2 + 1] = (Guchar)1;
328   cmapTable[28 + nSegs*6 + i*2    ] = (Guchar)0;	// idRangeOffset
329   cmapTable[28 + nSegs*6 + i*2 + 1] = (Guchar)0;
330 
331   gfree(unicodeToGID);
332 
333   *unicodeCmapLength = len;
334   return cmapTable;
335 }
336 
makeUnicodeToGID(int * codeToGID,int nCodes,int * unicodeToGIDLength)337 int *WebFont::makeUnicodeToGID(int *codeToGID, int nCodes,
338 			       int *unicodeToGIDLength) {
339   int *unicodeToGID;
340   CharCodeToUnicode *ctu;
341   Unicode u[2];
342   int len, size, newSize, uLen, c, gid;
343 
344   if (gfxFont->isCIDFont()) {
345     if (!(ctu = ((GfxCIDFont *)gfxFont)->getToUnicode())) {
346       return NULL;
347     }
348   } else {
349     ctu = ((Gfx8BitFont *)gfxFont)->getToUnicode();
350   }
351 
352   len = 0;
353   size = 256;
354   unicodeToGID = (int *)gmallocn(size, sizeof(int));
355   memset(unicodeToGID, 0, size * sizeof(int));
356   for (c = 0; c < nCodes; ++c) {
357     gid = codeToGID ? codeToGID[c] : c;
358     if (gid < 0 || gid >= 65536) {
359       continue;
360     }
361     uLen = ctu->mapToUnicode(c, u, 2);
362     if (uLen != 1) {
363       continue;
364     }
365     if (u[0] >= 65536) {    // sanity check
366       continue;
367     }
368     if ((int)u[0] >= size) {
369       newSize = 2 * size;
370       while ((int)u[0] >= newSize) {
371 	newSize *= 2;
372       }
373       unicodeToGID = (int *)greallocn(unicodeToGID, newSize, sizeof(int));
374       memset(unicodeToGID + size, 0, (newSize - size) * sizeof(int));
375       size = newSize;
376     }
377     unicodeToGID[u[0]] = gid;
378     if ((int)u[0] >= len) {
379       len = u[0] + 1;
380     }
381   }
382 
383   ctu->decRefCnt();
384 
385   *unicodeToGIDLength = len;
386   return unicodeToGID;
387 }
388