SmartPDF - lightweight pdf viewer app for rosapps
[reactos.git] / rosapps / smartpdf / fitz / mupdf / pdf_fontfilems.c
1 #include <fitz.h>
2 #include <mupdf.h>
3
4 #include <windows.h>
5
6 #include <ft2build.h>
7 #include FT_FREETYPE_H
8
9 #define SAFE_FZ_READ(file, buf, size)\
10 byteread = fz_read((file), (char*)(buf), (size));\
11 if(byteread<0) err = fz_ioerror(file);\
12 if(byteread != (size)) err = fz_throw("ioerror");\
13 if(err) goto cleanup;
14
15 #define ARRAY_SIZE(a) sizeof(a) / sizeof(a[0])
16
17 #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
18 #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
19
20 #define PLATFORM_UNICODE 0
21 #define PLATFORM_MACINTOSH 1
22 #define PLATFORM_ISO 2
23 #define PLATFORM_MICROSOFT 3
24
25 #define UNI_ENC_UNI_1 0
26 #define UNI_ENC_UNI_1_1 1
27 #define UNI_ENC_ISO 2
28 #define UNI_ENC_UNI_2_BMP 3
29 #define UNI_ENC_UNI_2_FULL_REPERTOIRE 4
30
31 #define MAC_ROMAN 0
32 #define MAC_JAPANESE 1
33 #define MAC_CHINESE_TRADITIONAL 2
34 #define MAC_KOREAN 3
35 #define MAC_CHINESE_SIMPLIFIED 25
36
37 #define MS_ENC_SYMBOL 0
38 #define MS_ENC_UNI_BMP 1
39 #define MS_ENC_SHIFTJIS 2
40 #define MS_ENC_PRC 3
41 #define MS_ENC_BIG5 4
42 #define MS_ENC_WANSUNG 5
43 #define MS_ENC_JOHAB 6
44 #define MS_ENC_UNI_FULL_REPETOIRE 10
45
46 #define TTC_VERSION1 0x00010000
47 #define TTC_VERSION2 0x00020000
48
49 typedef struct pdf_fontmapMS_s pdf_fontmapMS;
50 typedef struct pdf_fontlistMS_s pdf_fontlistMS;
51
52 struct pdf_fontmapMS_s
53 {
54 char fontface[128];
55 char fontpath[MAX_PATH+1];
56 int index;
57 };
58
59 struct pdf_fontlistMS_s
60 {
61 pdf_fontmapMS *fontmap;
62 int len;
63 int cap;
64 };
65
66 typedef struct _tagTT_OFFSET_TABLE
67 {
68 USHORT uMajorVersion;
69 USHORT uMinorVersion;
70 USHORT uNumOfTables;
71 USHORT uSearchRange;
72 USHORT uEntrySelector;
73 USHORT uRangeShift;
74 } TT_OFFSET_TABLE;
75
76 typedef struct _tagTT_TABLE_DIRECTORY
77 {
78 char szTag[4]; //table name
79 ULONG uCheckSum; //Check sum
80 ULONG uOffset; //Offset from beginning of file
81 ULONG uLength; //length of the table in bytes
82 } TT_TABLE_DIRECTORY;
83
84 typedef struct _tagTT_NAME_TABLE_HEADER
85 {
86 USHORT uFSelector; //format selector. Always 0
87 USHORT uNRCount; //Name Records count
88 USHORT uStorageOffset; //Offset for strings storage, from start of the table
89 } TT_NAME_TABLE_HEADER;
90
91 typedef struct _tagTT_NAME_RECORD
92 {
93 USHORT uPlatformID;
94 USHORT uEncodingID;
95 USHORT uLanguageID;
96 USHORT uNameID;
97 USHORT uStringLength;
98 USHORT uStringOffset; //from start of storage area
99 } TT_NAME_RECORD;
100
101 typedef struct _tagFONT_COLLECTION
102 {
103 char Tag[4];
104 ULONG Version;
105 ULONG NumFonts;
106 }FONT_COLLECTION;
107
108 static char *basenames[13] =
109 {
110 "Courier",
111 "Courier-Bold",
112 "Courier-Oblique",
113 "Courier-BoldOblique",
114 "Helvetica",
115 "Helvetica-Bold",
116 "Helvetica-Oblique",
117 "Helvetica-BoldOblique",
118 "Times-Roman",
119 "Times-Bold",
120 "Times-Italic",
121 "Times-BoldItalic",
122 "Symbol",
123 };
124
125 static char *basepatterns[13] =
126 {
127 "CourierNewPSMT",
128 "CourierNewPS-BoldMT",
129 "CourierNewPS-ItalicMT",
130 "CourierNewPS-BoldItalicMT",
131 "ArialMT",
132 "Arial-BoldMT",
133 "Arial-ItalicMT",
134 "Arial-BoldItalicMT",
135 "TimesNewRomanPSMT",
136 "TimesNewRomanPS-BoldMT",
137 "TimesNewRomanPS-ItalicMT",
138 "TimesNewRomanPS-BoldItalicMT",
139 "SymbolMT"
140 };
141
142 static pdf_fontlistMS fontlistMS =
143 {
144 NULL,
145 0,
146 0,
147 };
148
149 static int
150 compare(const void *elem1, const void *elem2)
151 {
152 pdf_fontmapMS *val1 = (pdf_fontmapMS *)elem1;
153 pdf_fontmapMS *val2 = (pdf_fontmapMS *)elem2;
154
155 if(val1->fontface[0] == 0)
156 return 1;
157 if(val2->fontface[0] == 0)
158 return -1;
159
160 return stricmp(val1->fontface, val2->fontface);
161 }
162
163 static void *
164 localbsearch (const void *key, const void *base, size_t num,
165 size_t width, int (*compare)(const void *, const void *))
166 {
167 char *lo = (char *)base;
168 char *hi = (char *)base + (num - 1) * width;
169 char *mid;
170 unsigned int half;
171 int result;
172
173 while (lo <= hi)
174 if (half = num / 2)
175 {
176 mid = lo + (num & 1 ? half : (half - 1)) * width;
177 if (!(result = (*compare)(key,mid)))
178 return(mid);
179 else if (result < 0)
180 {
181 hi = mid - width;
182 num = num & 1 ? half : half-1;
183 }
184 else {
185 lo = mid + width;
186 num = half;
187 }
188 }
189 else if (num)
190 return((*compare)(key,lo) ? 0 : lo);
191 else
192 break;
193
194 return(0);
195 }
196
197 static void
198 removeredundancy(pdf_fontlistMS *fl)
199 {
200 int i;
201 int roffset = 0;
202 int redundancy_count = 0;
203
204 qsort(fl->fontmap,fl->len,sizeof(pdf_fontmapMS),compare);
205 for(i = 0; i < fl->len - 1; ++i)
206 {
207 if(strcmp(fl->fontmap[i].fontface,fl->fontmap[i+1].fontface) == 0)
208 {
209 fl->fontmap[i].fontface[0] = 0;
210 ++redundancy_count;
211 }
212 }
213 qsort(fl->fontmap,fl->len,sizeof(pdf_fontmapMS),compare);
214 fl->len -= redundancy_count;
215 #ifndef NDEBUG
216 for(i = 0; i < fl->len; ++i)
217 fprintf(stdout,"%s , %s , %d\n",fl->fontmap[i].fontface,
218 fl->fontmap[i].fontpath,fl->fontmap[i].index);
219 #endif
220 }
221
222 static fz_error *
223 swapword(char* pbyte, int nLen)
224 {
225 int i;
226 char tmp;
227 int nMax;
228
229 if(nLen%2)
230 return fz_throw("fonterror");
231
232 nMax = nLen / 2;
233 for(i = 0; i < nLen; ++i) {
234 tmp = pbyte[i*2];
235 pbyte[i*2] = pbyte[i*2+1];
236 pbyte[i*2+1] = tmp;
237 }
238 return 0;
239 }
240
241 /* pSouce and PDest can be same */
242 static fz_error *
243 decodeunicodeBMP(char* source, int sourcelen,char* dest, int destlen)
244 {
245 wchar_t tmp[1024*2];
246 int converted;
247 memset(tmp,0,sizeof(tmp));
248 memcpy(tmp,source,sourcelen);
249 swapword((char*)tmp,sourcelen);
250
251 converted = WideCharToMultiByte(CP_ACP, 0, tmp,
252 -1, dest, destlen, NULL, NULL);
253
254 if(converted == 0)
255 return fz_throw("fonterror");
256
257 return 0;
258 }
259
260 static fz_error *
261 decodeunicodeplatform(char* source, int sourcelen,char* dest, int destlen, int enctype)
262 {
263 fz_error *err = nil;
264 switch(enctype)
265 {
266 case UNI_ENC_UNI_1:
267 case UNI_ENC_UNI_2_BMP:
268 err = decodeunicodeBMP(source,sourcelen,dest,destlen);
269 break;
270 case UNI_ENC_UNI_2_FULL_REPERTOIRE:
271 case UNI_ENC_UNI_1_1:
272 case UNI_ENC_ISO:
273 default:
274 err = fz_throw("fonterror : unsupported encoding");
275 break;
276 }
277 return err;
278 }
279
280 static fz_error *
281 decodemacintoshplatform(char* source, int sourcelen,char* dest, int destlen, int enctype)
282 {
283 fz_error *err = nil;
284 switch(enctype)
285 {
286 case MAC_ROMAN:
287 if(sourcelen + 1 > destlen)
288 err = fz_throw("fonterror : short buf lenth");
289 else
290 {
291 memcpy(source,dest,sourcelen);
292 dest[sourcelen] = 0;
293 }
294 break;
295 default:
296 err = fz_throw("fonterror : unsupported encoding");
297 break;
298 }
299 return err;
300 }
301
302 static fz_error *
303 decodemicrosoftplatform(char* source, int sourcelen,char* dest, int destlen, int enctype)
304 {
305 fz_error *err = nil;
306 switch(enctype)
307 {
308 case MS_ENC_SYMBOL:
309 case MS_ENC_UNI_BMP:
310 err = decodeunicodeBMP(source,sourcelen,dest,destlen);
311 break;
312 default:
313 err = fz_throw("fonterror : unsupported encoding");
314 break;
315 }
316 return err;
317 }
318
319 static fz_error *
320 growfontlist(pdf_fontlistMS *fl)
321 {
322 int newcap;
323 pdf_fontmapMS *newitems;
324
325 if(fl->cap == 0)
326 newcap = 32;
327 else
328 newcap = fl->cap * 2;
329
330 newitems = fz_realloc(fl->fontmap, sizeof(pdf_fontmapMS) * newcap);
331 if (!newitems)
332 return fz_outofmem;
333
334 memset(newitems + fl->cap, 0,
335 sizeof(struct fz_keyval_s) * (newcap - fl->cap));
336
337 fl->fontmap = newitems;
338 fl->cap = newcap;
339
340 return nil;
341 }
342
343 static fz_error *
344 insertmapping(pdf_fontlistMS *fl, char *facename, char *path, int index)
345 {
346 fz_error *err;
347
348 if(fl->len == fl->cap) {
349 err = growfontlist(fl);
350 if(err) return err;
351 }
352
353 if(fl->len >= fl->cap)
354 return fz_throw("fonterror : fontlist overflow");
355
356 strlcpy(fl->fontmap[fl->len].fontface, facename,
357 sizeof(fl->fontmap[0].fontface));
358 strlcpy(fl->fontmap[fl->len].fontpath, path,
359 sizeof(fl->fontmap[0].fontpath));
360 fl->fontmap[fl->len].index = index;
361
362 ++fl->len;
363
364 return nil;
365 }
366
367 static fz_error *
368 parseTTF(fz_stream *file, int offset, int index, char *path)
369 {
370 fz_error *err = nil;
371 int byteread;
372
373 TT_OFFSET_TABLE ttOffsetTable;
374 TT_TABLE_DIRECTORY tblDir;
375 TT_NAME_TABLE_HEADER ttNTHeader;
376 TT_NAME_RECORD ttRecord;
377
378 char szTemp[4096];
379 int found;
380 int i;
381
382 fz_seek(file,offset,0);
383 SAFE_FZ_READ(file, &ttOffsetTable, sizeof(TT_OFFSET_TABLE));
384
385 ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
386 ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
387 ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);
388
389 //check is this is a true type font and the version is 1.0
390 if(ttOffsetTable.uMajorVersion != 1 || ttOffsetTable.uMinorVersion != 0)
391 return fz_throw("fonterror : invalid font version");
392
393 found = 0;
394
395 for(i = 0; i< ttOffsetTable.uNumOfTables; i++)
396 {
397 SAFE_FZ_READ(file,&tblDir,sizeof(TT_TABLE_DIRECTORY));
398
399 memcpy(szTemp, tblDir.szTag, 4);
400 szTemp[4] = 0;
401
402 if (stricmp(szTemp, "name") == 0)
403 {
404 found = 1;
405 tblDir.uLength = SWAPLONG(tblDir.uLength);
406 tblDir.uOffset = SWAPLONG(tblDir.uOffset);
407 break;
408 }
409 else if (szTemp[0] == 0)
410 {
411 break;
412 }
413 }
414
415 if (found)
416 {
417 fz_seek(file,tblDir.uOffset,0);
418
419 SAFE_FZ_READ(file,&ttNTHeader,sizeof(TT_NAME_TABLE_HEADER));
420
421 ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount);
422 ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset);
423
424 offset = tblDir.uOffset + sizeof(TT_NAME_TABLE_HEADER);
425
426 for(i = 0; i < ttNTHeader.uNRCount && err == nil; ++i)
427 {
428 fz_seek(file, offset + sizeof(TT_NAME_RECORD)*i, 0);
429 SAFE_FZ_READ(file,&ttRecord,sizeof(TT_NAME_RECORD));
430
431 ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
432 ttRecord.uLanguageID = SWAPWORD(ttRecord.uLanguageID);
433
434 // Full Name
435 if(ttRecord.uNameID == 6)
436 {
437 ttRecord.uPlatformID = SWAPWORD(ttRecord.uPlatformID);
438 ttRecord.uEncodingID = SWAPWORD(ttRecord.uEncodingID);
439 ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
440 ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);
441
442 fz_seek(file, tblDir.uOffset + ttRecord.uStringOffset + ttNTHeader.uStorageOffset, 0);
443 SAFE_FZ_READ(file, szTemp, ttRecord.uStringLength);
444
445 switch(ttRecord.uPlatformID)
446 {
447 case PLATFORM_UNICODE:
448 err = decodeunicodeplatform(szTemp, ttRecord.uStringLength,
449 szTemp, sizeof(szTemp), ttRecord.uEncodingID);
450 break;
451 case PLATFORM_MACINTOSH:
452 err = decodemacintoshplatform(szTemp, ttRecord.uStringLength,
453 szTemp, sizeof(szTemp), ttRecord.uEncodingID);
454 break;
455 case PLATFORM_ISO:
456 err = fz_throw("fonterror : unsupported platform");
457 break;
458 case PLATFORM_MICROSOFT:
459 err = decodemicrosoftplatform(szTemp, ttRecord.uStringLength,
460 szTemp, sizeof(szTemp), ttRecord.uEncodingID);
461 break;
462 }
463
464 if(err == nil)
465 err = insertmapping(&fontlistMS, szTemp, path, index);
466 }
467 }
468 }
469
470 cleanup:
471 return err;
472 }
473
474 static fz_error *
475 parseTTFs(char *path)
476 {
477 fz_error *err = nil;
478 fz_stream *file = nil;
479
480 err = fz_openrfile(&file, path);
481 if(err)
482 goto cleanup;
483
484 err = parseTTF(file,0,0,path);
485 if(err)
486 goto cleanup;
487
488 cleanup:
489 if(file)
490 fz_dropstream(file);
491
492 return err;
493 }
494
495 static fz_error *
496 parseTTCs(char *path)
497 {
498 fz_error *err = nil;
499 int byteread;
500 fz_stream *file = nil;
501 FONT_COLLECTION fontcollectioin;
502 ULONG i;
503
504 err = fz_openrfile(&file, path);
505 if(err)
506 goto cleanup;
507
508 SAFE_FZ_READ(file, &fontcollectioin, sizeof(FONT_COLLECTION));
509 if(memcmp(fontcollectioin.Tag,"ttcf",sizeof(fontcollectioin.Tag)) == 0)
510 {
511 fontcollectioin.Version = SWAPLONG(fontcollectioin.Version);
512 fontcollectioin.NumFonts = SWAPLONG(fontcollectioin.NumFonts);
513 if( fontcollectioin.Version == TTC_VERSION1 ||
514 fontcollectioin.Version == TTC_VERSION2 )
515 {
516 ULONG *offsettable = fz_malloc(sizeof(ULONG)*fontcollectioin.NumFonts);
517 if(offsettable == nil)
518 {
519 err = fz_outofmem;
520 goto cleanup;
521 }
522
523 SAFE_FZ_READ(file, offsettable, sizeof(ULONG)*fontcollectioin.NumFonts);
524 for(i = 0; i < fontcollectioin.NumFonts; ++i)
525 {
526 offsettable[i] = SWAPLONG(offsettable[i]);
527 parseTTF(file,offsettable[i],i,path);
528 }
529 fz_free(offsettable);
530 }
531 else
532 {
533 err = fz_throw("fonterror : invalid version");
534 goto cleanup;
535 }
536 }
537 else
538 {
539 err = fz_throw("fonterror: wrong format");
540 goto cleanup;
541 }
542
543
544 cleanup:
545 if(file)
546 fz_dropstream(file);
547
548 return err;
549 }
550
551 static fz_error*
552 pdf_createfontlistMS()
553 {
554 char szFontDir[MAX_PATH*2];
555 char szSearch[MAX_PATH*2];
556 char szFile[MAX_PATH*2];
557 BOOL fFinished;
558 HANDLE hList;
559 WIN32_FIND_DATA FileData;
560 fz_error *err;
561
562 if (fontlistMS.len != 0)
563 return nil;
564
565 GetWindowsDirectory(szFontDir, sizeof(szFontDir));
566
567 // Get the proper directory path
568 strcat(szFontDir,"\\Fonts\\");
569 sprintf(szSearch,"%s*.tt?",szFontDir);
570 // Get the first file
571 hList = FindFirstFile(szSearch, &FileData);
572 if (hList == INVALID_HANDLE_VALUE)
573 {
574 /* Don't complain about missing directories */
575 if (errno == ENOENT)
576 return fz_throw("fonterror : can't find system fonts dir");
577 return fz_throw("ioerror");
578 }
579 // Traverse through the directory structure
580 fFinished = FALSE;
581 while (!fFinished)
582 {
583 if (!(FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
584 {
585 // Get the full path for sub directory
586 sprintf(szFile,"%s%s", szFontDir, FileData.cFileName);
587 if (szFile[strlen(szFile)-1] == 'c' || szFile[strlen(szFile)-1] == 'C')
588 {
589 err = parseTTCs(szFile);
590 // ignore error parsing a given font file
591 }
592 else if (szFile[strlen(szFile)-1] == 'f'|| szFile[strlen(szFile)-1] == 'F')
593 {
594 err = parseTTFs(szFile);
595 // ignore error parsing a given font file
596 }
597 }
598
599 if (!FindNextFile(hList, &FileData))
600 {
601 if (GetLastError() == ERROR_NO_MORE_FILES)
602 {
603 fFinished = TRUE;
604 }
605 }
606 }
607
608 removeredundancy(&fontlistMS);
609 return nil;
610 }
611
612 void
613 pdf_destoryfontlistMS()
614 {
615 if (fontlistMS.fontmap != nil)
616 fz_free(fontlistMS.fontmap);
617
618 fontlistMS.len = 0;
619 fontlistMS.cap = 0;
620 }
621
622 #if 0 /* This is for testing if localbsearch() fails unexpectedly */
623 pdf_fontmapMS *
624 findlinear(pdf_fontlistMS *list, pdf_fontmapMS *item)
625 {
626 int i, len;
627 pdf_fontmapMS *curritem = list->fontmap;
628
629 len = list->len;
630 for (i = 0; i < len; i++)
631 {
632 if (0 == stricmp(curritem->fontface, item->fontface))
633 return curritem;
634 ++curritem;
635 }
636 return 0;
637 }
638 #endif
639
640 fz_error *
641 pdf_lookupfontMS(char *fontname, char **fontpath, int *index)
642 {
643 pdf_fontmapMS fontmap;
644 pdf_fontmapMS *found = nil;
645 char *pattern;
646 int i;
647
648 if (fontlistMS.len == 0)
649 return fz_throw("fonterror : no fonts in the system");
650
651 pattern = fontname;
652 for (i = 0; i < ARRAY_SIZE(basenames); i++)
653 {
654 if (0 == strcmp(fontname, basenames[i]))
655 {
656 pattern = basepatterns[i];
657 break;
658 }
659 }
660
661 strlcpy(fontmap.fontface,pattern,sizeof(fontmap.fontface));
662 found = localbsearch(&fontmap, fontlistMS.fontmap, fontlistMS.len, sizeof(pdf_fontmapMS),compare);
663
664 #if 0
665 if (!found)
666 found = findlinear(&fontlistMS, &fontmap);
667 #endif
668
669 if (found)
670 {
671 *fontpath = found->fontpath;
672 *index = found->index;
673 }
674 else
675 {
676 *fontpath = fontlistMS.fontmap[0].fontpath;
677 *index = fontlistMS.fontmap[0].index;
678 }
679
680 return nil;
681 }
682
683 static FT_Library ftlib = nil;
684
685 static fz_error *initfontlibs(void)
686 {
687 int fterr;
688 int maj, min, pat;
689 fz_error *err;
690
691 if (ftlib)
692 return nil;
693
694 fterr = FT_Init_FreeType(&ftlib);
695 if (fterr)
696 return fz_throw("freetype failed initialisation: 0x%x", fterr);
697
698 FT_Library_Version(ftlib, &maj, &min, &pat);
699 if (maj == 2 && min == 1 && pat < 7)
700 return fz_throw("freetype version too old: %d.%d.%d", maj, min, pat);
701
702 err = pdf_createfontlistMS();
703 if(err)
704 return err;
705
706 return nil;
707 }
708
709 fz_error *initfontlibs_ms(void)
710 {
711 return initfontlibs();
712 }
713
714 void deinitfontlibs_ms(void)
715 {
716 pdf_destoryfontlistMS();
717 FT_Done_FreeType(ftlib);
718 ftlib = nil;
719 }
720
721 fz_error *
722 pdf_loadbuiltinfont(pdf_font *font, char *basefont)
723 {
724 fz_error *error;
725 int fterr;
726
727 FT_Face face;
728 char *file;
729 int index;
730
731 error = initfontlibs();
732 if (error)
733 return error;
734
735 error = pdf_lookupfontMS(basefont,&file,&index);
736 if(error)
737 return error;
738
739 fterr = FT_New_Face(ftlib, file, index, &face);
740 if (fterr)
741 return fz_throw("freetype could not load font file '%s': 0x%x", file, fterr);
742
743 font->ftface = face;
744
745 return nil;
746 }
747
748 fz_error *
749 pdf_loadsystemfont(pdf_font *font, char *basefont, char *collection)
750 {
751 fz_error *error;
752 int fterr;
753 FT_Face face;
754 char *file;
755 int index;
756
757 error = initfontlibs();
758 if (error)
759 return error;
760
761 error = pdf_lookupfontMS(basefont,&file,&index);
762 if(error)
763 goto cleanup;
764
765 fterr = FT_New_Face(ftlib, file, index, &face);
766 if (fterr) {
767 return fz_throw("freetype could not load font file '%s': 0x%x", file, fterr);
768 }
769
770 font->ftface = face;
771
772 return nil;
773
774 cleanup:
775 return error;
776 }
777
778 fz_error *
779 pdf_loadembeddedfont(pdf_font *font, pdf_xref *xref, fz_obj *stmref)
780 {
781 fz_error *error;
782 int fterr;
783 FT_Face face;
784 fz_buffer *buf;
785
786 error = initfontlibs();
787 if (error)
788 return error;
789
790 error = pdf_loadstream(&buf, xref, fz_tonum(stmref), fz_togen(stmref));
791 if (error)
792 return error;
793
794 fterr = FT_New_Memory_Face(ftlib, buf->rp, buf->wp - buf->rp, 0, &face);
795
796 if (fterr) {
797 fz_free(buf);
798 return fz_throw("freetype could not load embedded font: 0x%x", fterr);
799 }
800
801 font->ftface = face;
802 font->fontdata = buf;
803
804 return nil;
805 }
806