1 /* */
2 /* OS/2 Font Driver using the FreeType library */
3 /* */
4 /* Copyright (C) 1997, 1998 Michal Necasek <mike@mendelu.cz> */
5 /* Copyright (C) 1997, 1998 David Turner <turner@email.ENST.Fr> */
6 /* Copyright (C) 1997, 1998 International Business Machines */
7 /* */
8 /* Version: 0.9.90 (Beta) */
9 /* */
10 /* This source is to be compiled with IBM VisualAge C++ 3.0 or possibly */
11 /* Watcom C/C++ 10.0 or higher (Wactom doesn't quite work yet). */
12 /* Other compilers may actually work too but don't forget this is NOT a */
13 /* normal DLL but rather a subsystem DLL. That means it shouldn't use */
14 /* the usual C library as it has to run without runtime environment. */
15 /* VisualAge provides a special subsystem version of the run-time library. */
16 /* All this is of course very compiler-dependent. See makefiles for */
17 /* discussion of switches used. */
18 /* */
19 /* Implemantation Notes: */
20 /* */
21 /* Note #1: As a consequence of this being a subsystem librarary, I had to */
22 /* slightly modify the FreeType source, namely ttmemory.c and ttfile.c. */
23 /* FreeType/2 now allocates several chunks of memory and uses them as a */
24 /* heap. Note that memory allocation should use TTAlloc(), possibly */
25 /* directly SSAllocMem(). malloc() is unusable here and it doesn't work */
26 /* at all (runtime library isn't even initialized). See ttmemory.c for */
27 /* more info. */
28 /* In ttfile.c I had to change all fopen(), fseek()... calls */
29 /* to OS/2 API calls (DosOpen, DosSetFilePtr...) because without proper */
30 /* runtime environment a subsystem DLL cannot use std C library calls. */
31 /* */
32 /* Note #2: On exit of each function reading from font file the API */
33 /* TT_Flush_Stream() must be called. This is because file handles opened */
34 /* by this DLL actually belong to the calling process. As a consequence */
35 /* a) it's easy to run out of file handles, which results in really */
36 /* very nasty behavior and/or crashes. This could be solved by */
37 /* increased file handles limit, but cannot because */
38 /* b) it is impossible to close files open by another process and */
39 /* therefore the fonts cannot be properly uninstalled (you can't */
40 /* delete them while the're open by other process) */
41 /* The only solution I found is very simple - just close the file before */
42 /* exiting a DLL function. This ensures files are not left open across */
43 /* processes and other problems. */
44 /* */
45 /* Note #3: The whole business with linked lists is aimed at lowering */
46 /* memory consumption drastically. If you install 50 TT fonts, OS/2 */
47 /* opens all of them at startup. Even if you never use them, they take */
48 /* up at least over 1 Meg memory. With certain fonts the consumption can */
49 /* easily go over several MB. We limit such waste of memory by only */
50 /* actually keeping open several typefaces most recently used. Their */
51 /* number can be set via entry in OS2.INI. */
52 /* */
53 /* For Intelligent Font Interface (IFI) specification please see IFI32.TXT */
54
55 #ifndef __IBMC__
56 #ifndef __WATCOMC__
57 #error "This source requires IBM VisualAge C++ or Watcom C/C++"
58 #endif
59 #endif
60
61 /* Defining the following uses UCONV.DLL instead of the built-in */
62 /* translation tables. This code should work on any Warp 4 and */
63 /* Warp 3 w/ FixPak 35(?) and above */
64 /* Note: this should be defined before FTIFI.H is #included */
65 #undef USE_UCONV
66
67 #define INCL_WINSHELLDATA /* for accessing OS2.INI */
68 #define INCL_DOSMISC
69 #define INCL_DOSNLS
70 #define INCL_DOSPROCESS
71 #define INCL_GRE_DCS
72 #define INCL_GRE_DEVSUPPORT
73 #define INCL_DDIMISC
74 #define INCL_IFI
75 #include <os2.h>
76 #include <pmddi.h> /* SSAllocmem(), SSFreemem() and more */
77
78 #ifdef USE_UCONV /* uconv.h isn't always available */
79 #include <uconv.h>
80 #endif /* USE_UCONV */
81
82 #include <string.h>
83 #include <stdlib.h> /* min and max macros */
84
85 #define _syscall _System /* the IFI headers don't compile without it */
86
87 #include "32pmifi.h" /* IFI header */
88 #include "freetype.h" /* FreeType header */
89 #include "ftxkern.h" /* kerning extension */
90 #include "ftxwidth.h" /* glyph width extension */
91 #include "ftifi.h" /* xlate table */
92
93
94 /* For the sake of Netscape's text rendering bugs ! */
95 #define NETSCAPE_FIX
96
97 /* Create 'fake' Roman face for Times New Roman (to mimic PMATM's */
98 /* behaviour */
99 #define FAKE_TNR
100
101 /* (indirectly) exported functions */
102 LONG _System ConvertFontFile(PSZ pszSrc, PSZ pszDestDir, PSZ pszNewName);
103 HFF _System LoadFontFile(PSZ pszFileName);
104 LONG _System UnloadFontFile(HFF hff);
105 LONG _System QueryFaces(HFF hff, PIFIMETRICS pifiMetrics, ULONG cMetricLen,
106 ULONG cFountCount, ULONG cStart);
107 HFC _System OpenFontContext(HFF hff, ULONG ulFont);
108 LONG _System SetFontContext(HFC hfc, PCONTEXTINFO pci);
109 LONG _System CloseFontContext(HFC hfc);
110 LONG _System QueryFaceAttr(HFC hfc, ULONG iQuery, PBYTE pBuffer,
111 ULONG cb, PGLYPH pagi, GLYPH giStart);
112 LONG _System QueryCharAttr(HFC hfc, PCHARATTR pCharAttr,
113 PBITMAPMETRICS pbmm);
114 LONG _System QueryFullFaces(HFF hff, PVOID pBuff, PULONG buflen,
115 PULONG cFontCount, ULONG cStart);
116
117 FDDISPATCH fdisp = { /* Font driver dispatch table */
118 LoadFontFile,
119 QueryFaces,
120 UnloadFontFile,
121 OpenFontContext,
122 SetFontContext,
123 CloseFontContext,
124 QueryFaceAttr,
125 QueryCharAttr,
126 NULL, /* this one is no more used, only the spec fails to mention it */
127 ConvertFontFile,
128 QueryFullFaces
129 };
130
131
132
133 /****************************************************************************/
134 /* the single exported entry point; this way is faster than exporting every */
135 /* single function and a bit more flexible */
136 /* */
137 #pragma export (fdhdr, "FONT_DRIVER_DISPATCH_TABLE", 1)
138 FDHEADER fdhdr =
139 { /* Font driver Header */
140 sizeof(FDHEADER),
141 "OS/2 FONT DRIVER", /* do not change */
142 "TrueType (Using FreeType Engine)", /* description up to 40 chars */
143 IFI_VERSION20, /* version */
144 0, /* reserved */
145 &fdisp
146 };
147
148
149 /****************************************************************************/
150 /* some debug macros and functions. the debug version logs system requests */
151 /* to the file C:\FTIFI.LOG */
152 /* */
153 #ifdef DEBUG
154 HFILE LogHandle = NULLHANDLE;
155 ULONG Written = 0;
156 char log[2048] = "";
157 char buf[2048] = "";
158
159
itoa10(int i,char * buffer)160 char* itoa10( int i, char* buffer ) {
161 char* ptr = buffer;
162 char* rptr = buffer;
163 char digit;
164
165 if (i == 0) {
166 buffer[0] = '0';
167 buffer[1] = 0;
168 return buffer;
169 }
170
171 if (i < 0) {
172 *ptr = '-';
173 ptr++; rptr++;
174 i = -i;
175 }
176
177 while (i != 0) {
178 *ptr = (char) (i % 10 + '0');
179 ptr++;
180 i /= 10;
181 }
182
183 *ptr = 0; ptr--;
184
185 while (ptr > rptr) {
186 digit = *ptr;
187 *ptr = *rptr;
188 *rptr = digit;
189 ptr--;
190 rptr++;
191 }
192
193 return buffer;
194 }
195
196 #define COPY(s) strcpy(log, s)
197 #define CAT(s) strcat(log, s)
198 #define CATI(v) strcat(log, itoa10( (int)v, buf ))
199 #define WRITE DosWrite(LogHandle, log, strlen(log), &Written)
200
201 #define ERET1(label) { COPY("Error at "); \
202 CATI(__LINE__); \
203 CAT("\r\n"); \
204 WRITE; \
205 goto label; \
206 }
207
208 #define ERRRET(e) { COPY("Error at "); \
209 CATI(__LINE__); \
210 CAT("\r\n"); \
211 WRITE; \
212 return(e); \
213 }
214
215
216 #else
217
218 #define COPY(s)
219 #define CAT(s)
220 #define CATI(v)
221 #define WRITE
222
223 #define ERET1(label) goto label;
224
225 #define ERRRET(e) return(e);
226
227 #endif /* DEBUG */
228
229
230 /****************************************************************************/
231 /* */
232 /* 'engine' : */
233 /* */
234 /* The FreeType engine instance. Although this is a DLL, it isn't */
235 /* supposed to be shared by apps, as it is only called by the OS/2 GRE. */
236 /* This means that there is no need to bother with reentrancy/thread */
237 /* safety, which aren't supported by FreeType 1.0 anyway. */
238 /* */
239 TT_Engine engine;
240
241 /****************************************************************************/
242 /* */
243 /* TList and TListElement : */
244 /* */
245 /* simple structures used to implement a doubly linked list. Lists are */
246 /* used to implement the HFF object lists, as well as the font size and */
247 /* outline caches. */
248 /* */
249
250 typedef struct _TListElement TListElement, *PListElement;
251
252 struct _TListElement
253 {
254 PListElement next; /* next element in list - NULL if tail */
255 PListElement prev; /* previous element in list - NULL if head */
256 long key; /* value used for searches */
257 void* data; /* pointer to the listed/cached object */
258 };
259
260 typedef struct _TList TList, *PList;
261 struct _TList
262 {
263 PListElement head; /* first element in list - NULL if empty */
264 PListElement tail; /* last element in list - NULL if empty */
265 int count; /* number of elements in list */
266 };
267
268 static PListElement free_elements = 0;
269
270 #if 0
271 /****************************************************************************/
272 /* */
273 /* TGlyph_Image : */
274 /* */
275 /* structure used to store a glyph's attributes, i.e. outlines and metrics */
276 /* Note that we do not cache bitmaps ourselves for the moment. */
277 /* */
278 typedef struct _TGlyph_Image TGlyph_Image, *PGlyph_Image;
279
280 struct _TGlyph_Image
281 {
282 PListElement element; /* list element for this glyph image */
283 TT_Glyph_Metrics metrics;
284 TT_Outline outline;
285 };
286 #endif
287
288
289 /****************************************************************************/
290 /* */
291 /* TFontFace : */
292 /* */
293 /* a structure related to an open font face. It contains data for each of */
294 /* possibly several faces in a .TTC file. */
295
296 typedef struct _TFontFace TFontFace, *PFontFace;
297
298 struct _TFontFace
299 {
300 TT_Face face; /* handle to actual FreeType face object */
301 TT_Glyph glyph; /* handle to FreeType glyph container */
302 TT_CharMap charMap; /* handle to FreeType character map */
303 TT_Kerning directory; /* kerning directory */
304 USHORT *widths; /* glyph width cache for large fonts */
305 USHORT *kernIndices; /* reverse translation cache for kerning */
306 LONG em_size; /* points per em square */
307 ULONG flags; /* various FC_* flags (like FC_FLAG_FIXED)*/
308 #if 0 /* not now */
309 TList sizes; /* list of live child font sizes */
310 #endif
311 LONG charMode; /* character translation mode : */
312 /* 0 = Unicode to UGL */
313 /* 1 = Symbol (no translation) */
314 /* 2 = Unicode w/o translation */
315 };
316
317
318 /****************************************************************************/
319 /* */
320 /* TFontFile : */
321 /* */
322 /* a structure related to an open font file handle. All TFontFiles are */
323 /* kept in a simple linked list. There can be several faces in one font. */
324 /* Face(s) information is stored in a variable-length array of TFontFaces. */
325 /* A single TFontFile structure exactly corresponds to one HFF. */
326
327 typedef struct _TFontFile TFontFile, *PFontFile;
328
329 struct _TFontFile
330 {
331 PListElement element; /* list element for this font face */
332 HFF hff; /* HFF handle used from outside */
333 CHAR filename[260]; /* font file name */
334 LONG ref_count; /* number of times this font file is open */
335 ULONG flags; /* various FL_* flags */
336 ULONG numFaces; /* number of faces in a file (normally 1) */
337 TFontFace *faces; /* array of FontFace structures */
338 };
339
340
341 /* Flag : The font face has a fixed pitch width */
342 #define FC_FLAG_FIXED_WIDTH 1
343
344 /* Flag : Effectively duplicated FL_FLAG_DBCS_FILE. This info is */
345 /* kept twice for simplified access */
346 #define FC_FLAG_DBCS_FACE 2
347
348 /* Flag : This face is an alias */
349 #define FL_FLAG_FAKE_ROMAN 8
350
351 /* Flag : The font file has a live FreeType face object */
352 #define FL_FLAG_LIVE_FACE 16
353
354 /* Flag : A font file's face has a context open - DON'T CLOSE IT! */
355 #define FL_FLAG_CONTEXT_OPEN 32
356
357 /* Flag : This file has been already opened previously*/
358 #define FL_FLAG_ALREADY_USED 64
359
360 /* Flag : This is a font including DBCS characters; this also means */
361 /* the font driver presents to the system a second, vertically */
362 /* rendered, version of this typeface with name prepended by */
363 /* an '@' (used in horizontal-only word processors) */
364 /* Note : For TTCs, the whole collection is either DBCS or not. I've */
365 /* no idea if there are any TTCs with both DBCS and non-DBCS */
366 /* faces. It's possible, but sounds unlikely. */
367 #define FL_FLAG_DBCS_FILE 128
368
369 /* Note, we'll only keep the first max_open_files files with opened */
370 /* FreeType objects/instances.. */
371 int max_open_files = 10;
372
373 /* number of processes using the font driver; used by the init/term */
374 /* routine */
375 ULONG ulProcessCount = 0;
376
377 /* the list of live faces */
378 static TList liveFiles = { NULL, NULL, 0 };
379
380 /* the list of sleeping faces */
381 static TList idleFiles = { NULL, NULL, 0 };
382
383 /****************************************************************************/
384 /* */
385 /* TFontSize : */
386 /* */
387 /* a structure related to a opened font context (a.k.a. instance or */
388 /* transform/pointsize). It exactly corresponds to a HFC. */
389 /* */
390
391 typedef struct _TFontSize TFontSize, *PFontSize;
392
393 struct _TFontSize
394 {
395 PListElement element; /* List element for this font size */
396 HFC hfc; /* HFC handle used from outside */
397 TT_Instance instance; /* handle to FreeType instance */
398 BOOL transformed; /* TRUE = rotation/shearing used (rare) */
399 BOOL vertical; /* TRUE = vertically rendered DBCS face */
400 TT_Matrix matrix; /* transformation matrix */
401 PFontFile file; /* HFF this context belongs to */
402 ULONG faceIndex; /* index of face in a font (for TTCs) */
403 /* TList outlines;*/ /* outlines cache list */
404 };
405
406 /****************************************************************************/
407 /* array of font context handles. Note that there isn't more than one font */
408 /* context open at any time anyway, but we want to be safe.. */
409 /* */
410 #define MAX_CONTEXTS 5
411
412 static TFontSize contexts[MAX_CONTEXTS]; /* this is rather too much */
413
414
415 /****************************************************************************/
416 /* few globals used for NLS */
417 /* */
418 /* Note: most of the internationalization (I18N) code was kindly provided */
419 /* by Ken Borgendale and Marc L Cohen from IBM (big thanks!). I also */
420 /* received help from Tetsuro Nishimura from IBM Japan. */
421 /* I was also unable to test the I18N code on actual Japanese, Chinese... */
422 /* etc. systems. But it might work. */
423 /* */
424
425 static ULONG ScriptTag = -1;
426 static ULONG LangSysTag = -1;
427 static ULONG iLangId = TT_MS_LANGID_ENGLISH_UNITED_STATES; /* language ID */
428 static ULONG uLastGlyph = 255; /* last glyph for language */
429 static PSZ pGlyphlistName = "SYMBOL"; /* PM383, PMJPN, PMKOR.... */
430 static BOOL isGBK = TRUE; /* only used for Chinese */
431 static ULONG ulCp[2] = {1}; /* codepages used */
432 static UCHAR DBCSLead[12]; /* DBCS lead byte table */
433
434 /* rather simple-minded test to decide if given glyph index is a 'halfchar',*/
435 /* i.e. Latin character in a DBCS font which is _not_ to be rotated */
436 #define is_HALFCHAR(_x) ((_x) < 0x0400)
437
438
439 /****************************************************************************/
440 /* */
441 /* interfaceSEId: */
442 /* */
443 /* interfaceSEId (Interface-specific Encoding Id) determines what encoding */
444 /* the font driver should use if a font includes a Unicode encoding. */
445 /* */
446 LONG interfaceSEId(TT_Face face, BOOL UDCflag, LONG encoding);
447
448 /****************************************************************************/
449 /* */
450 /* LookUpName : */
451 /* */
452 /* this function tries to find M$ English name for a face */
453 /* length is limited to FACESIZE (defined by OS/2); returns NULL if */
454 /* unsuccessful. warning: the string gets overwritten on the next */
455 /* invocation */
456 /* */
457 /* TODO: needs enhancing for I18N */
458 static char* LookupName(TT_Face face, int index );
459
460
461 /****************************************************************************/
462 /* */
463 /* GetCharMap : */
464 /* */
465 /* get suitable charmap from font */
466 /* */
467 static ULONG GetCharmap(TT_Face face);
468
469
470 /****************************************************************************/
471 /* */
472 /* GetOutlineLen : */
473 /* */
474 /* get # of bytes needed for glyph outline */
475 /* */
476 static int GetOutlineLen(TT_Outline *ol);
477
478
479 /****************************************************************************/
480 /* */
481 /* GetOutline : */
482 /* */
483 /* get glyph outline in PM format */
484 /* */
485 static int GetOutline(TT_Outline *ol, PBYTE pb);
486
487
488
489 /****************************************************************************/
490 /* */
491 /* IsDBCSChar : */
492 /* */
493 /* Returns TRUE if character is first byte of a DBCS char, FALSE otherwise */
494 /* */
IsDBCSChar(UCHAR c)495 BOOL IsDBCSChar(UCHAR c)
496 {
497 ULONG i;
498
499 for (i = 0; DBCSLead[i] && DBCSLead[i+1]; i += 2)
500 if ((c >= DBCSLead[i]) && (c <= DBCSLead[i+1]))
501 return TRUE;
502 return FALSE;
503 }
504
505
506 /****************************************************************************/
507 /* */
508 /* TT_Alloc & TT_Free : */
509 /* */
510 /* The following two functions are declared here because including */
511 /* the entire ttmemory.h creates more problems than it solves */
512 /* */
513 TT_Error TT_Alloc( long Size, void** P );
514 TT_Error TT_Free( void** P );
515 TT_Error TTMemory_Init(void);
516
517 static TT_Error error;
518
519 #define ALLOC( p, size ) TT_Alloc( (size), (void**)&(p) )
520 #define FREE( p ) TT_Free( (void**)&(p) )
521
522 /****************************************************************************/
523 /* */
524 /* New_Element : */
525 /* */
526 /* return a fresh list element. Either new or recycled. */
527 /* returns NULL if out of memory. */
528 /* */
New_Element(void)529 static PListElement New_Element( void )
530 {
531 PListElement e = free_elements;
532
533 if (e)
534 free_elements = e->next;
535 else
536 {
537 if ( ALLOC( e, sizeof(TListElement) ) )
538 return NULL;
539 }
540 e->next = e->prev = e->data = NULL;
541 e->key = 0;
542
543 return e;
544 }
545
546 /****************************************************************************/
547 /* */
548 /* Done_Element : */
549 /* */
550 /* recycles an old list element */
551 /* */
Done_Element(PListElement element)552 static void Done_Element( PListElement element )
553 {
554 element->next = free_elements;
555 free_elements = element;
556 }
557
558 /****************************************************************************/
559 /* */
560 /* List_Insert : */
561 /* */
562 /* inserts a new object at the head of a given list */
563 /* returns 0 in case of success, -1 otherwise. */
564 /* */
List_Insert(PList list,PListElement element)565 static int List_Insert( PList list, PListElement element )
566 {
567 if (!list || !element)
568 return -1;
569
570 element->next = list->head;
571
572 if (list->head)
573 list->head->prev = element;
574
575 element->prev = NULL;
576 list->head = element;
577
578 if (!list->tail)
579 list->tail = element;
580
581 list->count++;
582 return 0;
583 }
584
585 /****************************************************************************/
586 /* */
587 /* List_Remove : */
588 /* */
589 /* removes an element from its list. Returns 0 in case of success, */
590 /* -1 otherwise. WARNING : this function doesn't check that the */
591 /* element is part of the list. */
592 /* */
List_Remove(PList list,PListElement element)593 static int List_Remove( PList list, PListElement element )
594 {
595 if (!element)
596 return -1;
597
598 if (element->prev)
599 element->prev->next = element->next;
600 else
601 list->head = element->next;
602
603 if (element->next)
604 element->next->prev = element->prev;
605 else
606 list->tail = element->prev;
607
608 element->next = element->prev = NULL;
609 list->count --;
610 return 0;
611 }
612
613 /****************************************************************************/
614 /* */
615 /* List_Find : */
616 /* */
617 /* Look for a given object with a specified key. Returns NULL if the */
618 /* list is empty, or the object wasn't found. */
619 /* */
List_Find(PList list,long key)620 static PListElement List_Find( PList list, long key )
621 {
622 static PListElement cur;
623
624 for ( cur=list->head; cur; cur = cur->next )
625 if ( cur->key == key )
626 return cur;
627
628 /* not found */
629 return NULL;
630 }
631
632 /****************************************************************************/
633 /* */
634 /* Sleep_FontFile : */
635 /* */
636 /* closes a font file's FreeType objects to leave room in memory. */
637 /* */
Sleep_FontFile(PFontFile cur_file)638 static int Sleep_FontFile( PFontFile cur_file )
639 {
640 int i;
641
642 if (!(cur_file->flags & FL_FLAG_LIVE_FACE))
643 ERRRET(-1); /* already asleep */
644
645 /* is this face in use? */
646 if (cur_file->flags & FL_FLAG_CONTEXT_OPEN) {
647 /* move face to top of the list */
648 if (List_Remove( &liveFiles, cur_file->element ))
649 ERRRET(-1);
650 if (List_Insert( &liveFiles, cur_file->element ))
651 ERRRET(-1);
652
653 cur_file = (PFontFile)(liveFiles.tail->data);
654 }
655
656 /* remove the face from the live list */
657 if (List_Remove( &liveFiles, cur_file->element ))
658 ERRRET(-1);
659
660 /* add it to the sleep list */
661 if (List_Insert( &idleFiles, cur_file->element ))
662 ERRRET(-1);
663
664 /* deactivate its objects - we ignore errors there */
665 for (i = 0; i < cur_file->numFaces; i++) {
666 TT_Done_Glyph( cur_file->faces[i].glyph );
667 TT_Close_Face( cur_file->faces[i].face );
668 }
669 cur_file->flags &= ~FL_FLAG_LIVE_FACE;
670
671 return 0;
672 }
673
674 /****************************************************************************/
675 /* */
676 /* Wake_FontFile : */
677 /* */
678 /* awakes a font file, and reloads important data from disk. */
679 /* */
Wake_FontFile(PFontFile cur_file)680 static int Wake_FontFile( PFontFile cur_file )
681 {
682 static TT_Face face;
683 static TT_Glyph glyph;
684 static TT_CharMap cmap;
685 static TT_Face_Properties props;
686 static PFontFace cur_face;
687 ULONG encoding, i;
688
689 if (cur_file->flags & FL_FLAG_LIVE_FACE)
690 ERRRET(-1); /* already awoken !! */
691
692 /* OK, try to activate the FreeType objects */
693 error = TT_Open_Face(engine, cur_file->filename, &face);
694 if (error)
695 {
696 COPY( "Error while opening " ); CAT( cur_file->filename );
697 CAT( ", error code = " ); CATI( error ); CAT( "\r\n" ); WRITE;
698 return -1; /* error, can't open file */
699 /* XXX : should set error condition here! */
700 }
701
702 /* Create a glyph container for it */
703 error = TT_New_Glyph( face, &glyph );
704 if (error)
705 {
706 COPY( "Error while creating container for " ); CAT( cur_file->filename );
707 CAT( ", error code = " ); CATI( error ); CAT( "\r\n" ); WRITE;
708 goto Fail_Face;
709 }
710
711 /* now get suitable charmap for this font */
712 encoding = GetCharmap(face);
713 error = TT_Get_CharMap(face, encoding & 0xFFFF, &cmap);
714 if (error)
715 {
716 COPY( "Error: No char map in " ); CAT( cur_file->filename );
717 CAT( "\r\n" ); WRITE;
718 goto Fail_Glyph;
719 }
720
721 /* Get face properties. Necessary to find out number of fonts for TTCs */
722 TT_Get_Face_Properties(face, &props);
723
724 /* all right, now remove the face from the sleep list */
725 if (List_Remove( &idleFiles, cur_file->element ))
726 ERET1( Fail_Glyph );
727
728 /* add it to the live list */
729 if (List_Insert( &liveFiles, cur_file->element ))
730 ERET1( Fail_Glyph );
731
732 /* If the file is a TTC, the first face is now opened successfully. */
733
734 cur_file->numFaces = props.num_Faces;
735
736 /* Now allocate memory for face data (one struct for each face in TTC). */
737 if (cur_file->faces == NULL) {
738 if (ALLOC(cur_face, sizeof(TFontFace) * cur_file->numFaces))
739 ERET1( Fail_Glyph );
740
741 cur_file->faces = cur_face;
742 }
743 else
744 cur_face = cur_file->faces;
745
746 cur_face->face = face; /* possibly first face in a TTC */
747 cur_face->glyph = glyph;
748 cur_face->charMap = cmap;
749 cur_file->flags |= FL_FLAG_LIVE_FACE;
750
751
752 if (!(cur_file->flags & FL_FLAG_ALREADY_USED)) {
753 cur_face->charMode = encoding >> 16; /* Unicode, Symbol, ... */
754 cur_face->em_size = props.header->Units_Per_EM;
755
756 /* if a face contains over 1024 glyphs, assume it's a DBCS font - */
757 /* VERY probable */
758 TT_Get_Face_Properties(cur_face->face, &props);
759
760 if (props.num_Glyphs > 1024) {
761 cur_file->flags |= FL_FLAG_DBCS_FILE;
762 cur_face->flags |= FC_FLAG_DBCS_FACE;
763 }
764
765 cur_face->widths = NULL;
766 cur_face->kernIndices = NULL;
767 }
768 /* load kerning directory, if any */
769 error = TT_Get_Kerning_Directory(face, &(cur_face->directory));
770 if (error)
771 cur_face->directory.nTables = 0; /* indicates no kerning in font */
772
773 TT_Flush_Face(face); /* this is important ! */
774
775 /* open remaining faces if this font is a TTC */
776 for (i = 1; i < cur_file->numFaces; i++) {
777 error = TT_Open_Collection(engine, cur_file->filename,
778 i, &face);
779 if (error)
780 return -1; /* TODO: handle bad TTCs more tolerantly */
781
782 error = TT_New_Glyph( face, &glyph );
783 if (error)
784 ERET1(Fail_Face);
785
786 encoding = GetCharmap(face);
787 error = TT_Get_CharMap(face, encoding & 0xFFFF, &cmap);
788 if (error)
789 ERET1(Fail_Glyph);
790
791 cur_face = &(cur_file->faces[i]);
792
793 cur_face->face = face;
794 cur_face->glyph = glyph;
795 cur_face->charMap = cmap;
796
797 if (!(cur_file->flags & FL_FLAG_ALREADY_USED)) {
798 cur_face->em_size = props.header->Units_Per_EM;
799 cur_face->charMode = encoding >> 16; /* 0 - Unicode; 1 - Symbol */
800
801 if (cur_file->flags & FL_FLAG_DBCS_FILE)
802 cur_face->flags |= FC_FLAG_DBCS_FACE;
803
804 cur_face->widths = NULL;
805 cur_face->kernIndices = NULL;
806 }
807
808 /* load kerning directory, if any */
809 error = TT_Get_Kerning_Directory(face, &(cur_face->directory));
810 if (error)
811 cur_face->directory.nTables = 0; /* indicates no kerning in font */
812 }
813
814 cur_file->flags |= FL_FLAG_ALREADY_USED; /* indicates some fields need no re-init */
815
816 error = TT_Flush_Face(face); /* this is important ! */
817 if (error) {
818 COPY("Error flushing face\r\n"); WRITE;
819 }
820
821 return 0; /* everything is in order, return 0 == success */
822
823 Fail_Glyph:
824 /* This line isn't really necessary, because the glyph container */
825 /* would be destroyed by the following TT_Close_Face anyway. We */
826 /* however use it for the sake of orthodoxy */
827 TT_Done_Glyph( glyph );
828
829 Fail_Face:
830 TT_Close_Face(face);
831
832 /* note that in case of error (e.g. out of memory), the face stays */
833 /* on the sleeping list */
834 return -1;
835 }
836
837 /****************************************************************************/
838 /* */
839 /* Done_FontFile : */
840 /* */
841 /* destroys a given font file object. This will also destroy all of its */
842 /* live child font sizes (which in turn will destroy the glyph caches). */
843 /* This is done for all faces if the file is a collection. */
844 /* */
845 /* WARNING : The font face must be removed from its list by the caller */
846 /* before this function is called. */
847 /* */
Done_FontFile(PFontFile * file)848 static void Done_FontFile( PFontFile *file )
849 {
850 static PListElement element;
851 static PListElement next;
852 ULONG i;
853
854 #if 0 /* this part isn't really used and maybe it never will */
855 /* destroy its font sizes */
856 element = (*face)->sizes.head;
857 while (element)
858 {
859 next = element->next;
860 /* XXX : right now, we simply free the font size object, */
861 /* because the instance is destroyed automatically */
862 /* by FreeType. */
863
864 FREE( element->data );
865 /* Done_FontSize( (PFontSize)element->data ); - later */
866
867 Done_Element( element );
868 element = next;
869 }
870 #endif
871
872 /* now discard the font face itself */
873 if ((*file)->flags & FL_FLAG_LIVE_FACE)
874 {
875 for (i = 0; i < (*file)->numFaces; i++) {
876 TT_Done_Glyph( (*file)->faces[i].glyph );
877 TT_Close_Face( (*file)->faces[i].face );
878
879 if ((*file)->faces[i].widths)
880 FREE((*file)->faces[i].widths);
881 if ((*file)->faces[i].kernIndices)
882 FREE((*file)->faces[i].kernIndices);
883 }
884 }
885
886 FREE( (*file)->faces );
887 FREE( *file );
888 }
889
890
891 /****************************************************************************/
892 /* */
893 /* New_FontFile : */
894 /* */
895 /* return the address of the TFontFile corresponding to a given */
896 /* HFF. Note that in our implementation, we could simply to a */
897 /* typecast like '(PFontFile)hff'. However, for safety reasons, we */
898 /* look up the handle in the list. */
899 /* */
New_FontFile(char * file_name)900 static PFontFile New_FontFile( char* file_name )
901 {
902 static PListElement element;
903 static PFontFile cur_file;
904 static TT_CharMap cmap;
905
906 /* first, check if it's already open - in the live list */
907 for ( element = liveFiles.head; element; element = element->next )
908 {
909 cur_file = (PFontFile)element->data;
910 if (strcmp( cur_file->filename, file_name ) == 0)
911 goto Exit_Same;
912 }
913
914 /* check in the idle list */
915 for ( element = idleFiles.head; element; element = element->next )
916 {
917 cur_file = (PFontFile)element->data;
918 if (strcmp( cur_file->filename, file_name ) == 0)
919 goto Exit_Same;
920 }
921
922 /* OK, this file isn't opened yet. Create a new font face object */
923 /* then try to wake it up. This will fail if the file can't be found */
924 /* or if we lack memory.. */
925
926 element = New_Element();
927 if (!element)
928 ERRRET(NULL);
929
930 if ( ALLOC( cur_file, sizeof(TFontFile) ) )
931 ERET1( Fail_Element );
932
933 element->data = cur_file;
934 element->key = (long)cur_file; /* use the HFF as cur key */
935
936 cur_file->element = element;
937 cur_file->ref_count = 1;
938 cur_file->hff = (HFF)cur_file;
939 strcpy( cur_file->filename, file_name);
940 cur_file->flags = 0;
941 cur_file->faces = NULL;
942 #if 0 /* not used */
943 cur_face->sizes.head = NULL;
944 cur_face->sizes.tail = NULL;
945 cur_face->sizes.count= 0;
946 #endif
947
948 /* add new font face to sleep list */
949 if (List_Insert( &idleFiles, element ))
950 ERET1( Fail_File );
951
952 /* Make enough room in the live list */
953 if ( liveFiles.count >= max_open_files)
954 {
955 COPY( "rolling...\n" ); WRITE;
956 if (Sleep_FontFile( (PFontFile)(liveFiles.tail->data) ))
957 ERET1( Fail_File );
958 }
959
960 /* wake new font file */
961 if ( Wake_FontFile( cur_file ) )
962 {
963 COPY( "could not open/wake " ); CAT( file_name ); CAT( "\r\n" ); WRITE;
964 if (List_Remove( &idleFiles, element ))
965 ERET1( Fail_File );
966
967 ERET1( Fail_File );
968 }
969
970 return cur_file; /* everything is in order */
971
972 Fail_File:
973 FREE( cur_file );
974
975 Fail_Element:
976 Done_Element( element );
977 return NULL;
978
979 Exit_Same:
980 cur_file->ref_count++; /* increment reference count */
981
982 COPY( " -> (duplicate) hff = " ); CATI( cur_file->hff );
983 CAT( "\r\n" ); WRITE;
984
985 return cur_file; /* no sense going on */
986 }
987
988 /****************************************************************************/
989 /* */
990 /* getFontFile : */
991 /* */
992 /* return the address of the TFontFile corresponding to a given */
993 /* HFF. If asleep, the file and its face object(s) is awoken. */
994 /* */
getFontFile(HFF hff)995 PFontFile getFontFile( HFF hff )
996 {
997 static PListElement element;
998
999 /* look in the live list first */
1000 element = List_Find( &liveFiles, (long)hff );
1001 if (element)
1002 {
1003 /* move it to the front of the live list - if it isn't already */
1004 if ( liveFiles.head != element )
1005 {
1006 if ( List_Remove( &liveFiles, element ) )
1007 ERRRET( NULL );
1008
1009 if ( List_Insert( &liveFiles, element ) )
1010 ERRRET( NULL );
1011 }
1012 return (PFontFile)(element->data);
1013 }
1014
1015 /* the file may be asleep, look in the second list */
1016 element = List_Find( &idleFiles, (long)hff );
1017 if (element)
1018 {
1019 /* we need to awake the font, but before that, we must be sure */
1020 /* that there is enough room in the live list */
1021 if ( liveFiles.count >= max_open_files )
1022 if (Sleep_FontFile( (PFontFile)(liveFiles.tail->data) ))
1023 ERRRET( NULL );
1024
1025 if ( Wake_FontFile( (PFontFile)(element->data) ) )
1026 ERRRET( NULL );
1027
1028 COPY ( "hff " ); CATI( hff ); CAT( " awoken\n" ); WRITE;
1029 return (PFontFile)(element->data);
1030 }
1031
1032 COPY( "Could not find hff " ); CATI( hff ); CAT( " in lists\n" ); WRITE;
1033
1034 #ifdef DEBUG
1035
1036 /* dump files lists */
1037 COPY( "Live files : " ); CATI( liveFiles.count ); CAT( "\r\n" ); WRITE;
1038
1039 for (element = liveFiles.head; element; element = element->next)
1040 {
1041 COPY( ((PFontFile)(element->data))->filename ); CAT("\r\n");WRITE;
1042 }
1043
1044 COPY( "Idle files : " ); CATI( idleFiles.count ); CAT( "\r\n" ); WRITE;
1045 for (element = idleFiles.head; element; element = element->next)
1046 {
1047 COPY( ((PFontFile)(element->data))->filename ); CAT("\r\n");WRITE;
1048 }
1049 #endif
1050
1051 /* could not find the HFF in the list */
1052 return NULL;
1053 }
1054
1055
1056 /****************************************************************************/
1057 /* */
1058 /* getFontSize : */
1059 /* */
1060 /* return pointer to a TFontSize given a HFC handle, NULL if error */
1061 /* */
getFontSize(HFC hfc)1062 static PFontSize getFontSize( HFC hfc )
1063 {
1064 int i;
1065 for ( i = 0; i < MAX_CONTEXTS; i++ )
1066 if ( contexts[i].hfc == hfc ) {
1067 return &contexts[i];
1068 }
1069
1070 return NULL;
1071 }
1072
1073 #ifdef USE_UCONV
1074
1075 /* maximum number of cached UCONV objects */
1076 #define MAX_UCONV_CACHE 10
1077
1078 /* UCONV object used for conversion from UGL to Unicode */
1079 #define UCONV_TYPE_UGL 1
1080
1081 /* UCONV objects used for conversion from local DBCS codepage to Unicode */
1082 #define UCONV_TYPE_BIG5 2
1083 #define UCONV_TYPE_SJIS 4
1084
1085 /* UCONV objects cache entry */
1086 typedef struct _UCACHEENTRY {
1087 UconvObject object; /* actual UCONV object */
1088 PID pid; /* process ID the object is valid for */
1089 ULONG type; /* type of UCONV object (UGL or DBCS) */
1090 } UCACHEENTRY, *PUCACHEENTRY;
1091
1092 /* UCONV globals */
1093 static UCACHEENTRY UconvCache[MAX_UCONV_CACHE]; /* 10 should do it */
1094 static int slotsUsed = 0; /* number of cache slots used */
1095
1096 /****************************************************************************/
1097 /* */
1098 /* getUconvObject : */
1099 /* */
1100 /* a function to cache UCONV objects based on current process. The only */
1101 /* problem is that FT/2 currently doesn't keep track of processes and */
1102 /* consequently the objects aren't freed when a process ends. But UCONV */
1103 /* frees the objects itself anyway. */
getUconvObject(UniChar * name,UconvObject * ConvObj,ULONG UconvType)1104 int getUconvObject(UniChar *name, UconvObject *ConvObj, ULONG UconvType) {
1105 PPIB ppib; /* process/thread info blocks */
1106 PTIB ptib;
1107 PID curPid; /* current process ID */
1108 int i;
1109
1110 /* query current process ID */
1111 if (DosGetInfoBlocks(&ptib, &ppib))
1112 return -1;
1113
1114 curPid = ppib->pib_ulpid;
1115
1116 if (slotsUsed == 0) { /* initialize cache */
1117 if (UniCreateUconvObject(name, ConvObj) != ULS_SUCCESS)
1118 return -1;
1119 UconvCache[0].object = *ConvObj;
1120 UconvCache[0].pid = curPid;
1121 UconvCache[0].type = UconvType;
1122
1123 for (i = 1; i < MAX_UCONV_CACHE; i++) {
1124 UconvCache[i].object = NULL;
1125 UconvCache[i].pid = 0;
1126 }
1127 slotsUsed = 1;
1128 return 0;
1129 }
1130
1131 /* search cache for available conversion object */
1132 i = 0;
1133 while ((UconvCache[i].pid != curPid || UconvCache[i].type != UconvType)
1134 && i < slotsUsed)
1135 i++;
1136
1137 if (i < slotsUsed) { /* entry found in cache */
1138 *ConvObj = UconvCache[i].object;
1139 return 0;
1140 }
1141
1142 /* if cache is full, remove first entry and shift the others 'down' */
1143 if (slotsUsed == MAX_UCONV_CACHE) {
1144 UniFreeUconvObject(UconvCache[0].object);
1145 for (i = 1; i < MAX_UCONV_CACHE; i++) {
1146 UconvCache[i - 1].object = UconvCache[i].object;
1147 UconvCache[i - 1].pid = UconvCache[i].pid;
1148 UconvCache[i - 1].type = UconvCache[i].type;
1149 }
1150 }
1151
1152 if (UniCreateUconvObject(name, ConvObj) != ULS_SUCCESS)
1153 return -1;
1154
1155 if (slotsUsed < MAX_UCONV_CACHE)
1156 slotsUsed++;
1157
1158 UconvCache[slotsUsed - 1].object = *ConvObj;
1159 UconvCache[slotsUsed - 1].pid = curPid;
1160 UconvCache[slotsUsed - 1].type = UconvType;
1161
1162 return 0;
1163 }
1164
1165 /****************************************************************************/
1166 /* */
1167 /* CleanUCONVCache : */
1168 /* */
1169 /* When process is terminated, removes this process' entries in the UCONV */
1170 /* object cache. Errors are disregarded at this point. */
CleanUCONVCache(void)1171 void CleanUCONVCache(void) {
1172 PPIB ppib; /* process/thread info blocks */
1173 PTIB ptib;
1174 PID curPid; /* current process ID */
1175 int i = 0, j;
1176
1177 /* query current process ID */
1178 if (DosGetInfoBlocks(&ptib, &ppib))
1179 return;
1180
1181 curPid = ppib->pib_ulpid;
1182
1183 while (i < slotsUsed) {
1184 /* if PID matches, remove the entry and shift the others 'down' (or up?) */
1185 if (UconvCache[i].pid == curPid) {
1186 UniFreeUconvObject(UconvCache[i].object);
1187 for (j = i + 1; j < slotsUsed; j++) {
1188 UconvCache[j - 1].object = UconvCache[j].object;
1189 UconvCache[j - 1].pid = UconvCache[j].pid;
1190 UconvCache[j - 1].type = UconvCache[j].type;
1191 }
1192 slotsUsed--;
1193 }
1194 i++;
1195 }
1196 }
1197 #endif /* USE_UCONV */
1198
1199 /****************************************************************************/
1200 /* */
1201 /* PM2TT : */
1202 /* */
1203 /* a function to convert PM codepoint to TT glyph index. This is the real */
1204 /* tricky part. */
1205 /* mode = TRANSLATE_UGL - translate UGL to Unicode */
1206 /* mode = TRANSLATE_SYMBOL - no translation - symbol font */
1207 /* mode = TRANSLATE_UNICODE- no translation - Unicode */
PM2TT(TT_CharMap charMap,ULONG mode,int index)1208 static int PM2TT( TT_CharMap charMap,
1209 ULONG mode,
1210 int index)
1211 {
1212 #ifdef USE_UCONV
1213 /* Brand new version that uses UCONV.DLL. This should make FreeType/2 */
1214 /* smaller and at the same time more flexible as it now should use */
1215 /* the Unicode translation tables supplied with base OS/2 Warp 4. */
1216 /* Unfortunately there's a complication (again) since UCONV objects */
1217 /* created in one process can't be used in another. Therefore we */
1218 /* keep a small cache of recently used UCONV objects. */
1219 static UconvObject UGLObj = NULL; /* UGL->Unicode conversion object */
1220 static BOOL UconvSet = FALSE;
1221 char char_data[2], *pin_char_str;
1222 size_t in_bytes_left, uni_chars_left, num_subs;
1223 UniChar *pout_uni_str, uni_buffer[4];
1224 int rc;
1225 static UniChar uglName[10] = L"OS2UGL";
1226 static UniChar uglNameBig5[10] = L"IBM-950";
1227 static UniChar uglNameSJIS[10] = L"IBM-943";
1228
1229 switch (mode) {
1230 case TRANSLATE_UGL:
1231 if (UconvSet == FALSE) {
1232 switch (iLangId) { /* select proper conversion table */
1233 case TT_MS_LANGID_GREEK_GREECE:
1234 strncpy((char*)uglName, (char*)L"OS2UGLG", 16);
1235 break;
1236 case TT_MS_LANGID_HEBREW_ISRAEL:
1237 strncpy((char*)uglName, (char*)L"OS2UGLH", 16);
1238 break;
1239 case TT_MS_LANGID_ARABIC_SAUDI_ARABIA:
1240 strncpy((char*)uglName, (char*)L"OS2UGLA", 16);
1241 break;
1242 }
1243 UconvSet = TRUE;
1244 }
1245
1246 /* get Uconv object - either new or cached */
1247 if (getUconvObject(uglName, &UGLObj, UCONV_TYPE_UGL) != 0)
1248 return 0;
1249
1250 if (index > MAX_GLYPH)
1251 return 0;
1252
1253 char_data[0] = index;
1254 char_data[1] = index >> 8;
1255
1256 pout_uni_str = uni_buffer;
1257 pin_char_str = char_data;
1258 in_bytes_left = 2;
1259 uni_chars_left = 1;
1260
1261 rc = UniUconvToUcs(UGLObj, (void**)&pin_char_str, &in_bytes_left,
1262 &pout_uni_str, &uni_chars_left,
1263 &num_subs);
1264 if (rc != ULS_SUCCESS)
1265 return 0;
1266 else
1267 return TT_Char_Index(charMap, ((unsigned short*)uni_buffer)[0]);
1268
1269 case TRANSLATE_SYMBOL:
1270 case TRANSLATE_UNICODE:
1271 case TRANSLATE_BIG5:
1272 case TRANSLATE_SJIS:
1273 return TT_Char_Index(charMap, index);
1274
1275 case TRANSLATE_UNI_BIG5:
1276 case TRANSLATE_UNI_SJIS:
1277
1278 /* get Uconv object - either new or cached */
1279 switch (mode) {
1280 /* get proper conversion object */
1281 case TRANSLATE_UNI_BIG5:
1282 if (getUconvObject(uglNameBig5, &UGLObj, UCONV_TYPE_BIG5) != 0)
1283 return 0;
1284 break;
1285
1286 case TRANSLATE_UNI_SJIS:
1287 if (getUconvObject(uglNameSJIS, &UGLObj, UCONV_TYPE_SJIS) != 0)
1288 return 0;
1289 break;
1290 }
1291
1292 /* Note the bytes are swapped here for double byte chars! */
1293 if (index & 0xFF00) {
1294 char_data[0] = (index & 0xFF00) >> 8;
1295 char_data[1] = index & 0x00FF;
1296 }
1297 else {
1298 char_data[0] = index;
1299 char_data[1] = 0;
1300 }
1301
1302 pout_uni_str = uni_buffer;
1303 pin_char_str = char_data;
1304 in_bytes_left = 2;
1305 uni_chars_left = 2;
1306
1307 rc = UniUconvToUcs(UGLObj, (void**)&pin_char_str, &in_bytes_left,
1308 &pout_uni_str, &uni_chars_left,
1309 &num_subs);
1310 if (rc != ULS_SUCCESS)
1311 return 0;
1312 else
1313 return TT_Char_Index(charMap, ((unsigned short*)uni_buffer)[0]);
1314
1315 default:
1316 return 0;
1317 }
1318 #else
1319 switch (mode)
1320 {
1321 /* convert from PM383 to Unicode */
1322 case TRANSLATE_UGL:
1323 /* TODO: Hebrew and Arabic UGL */
1324 if (iLangId == TT_MS_LANGID_GREEK_GREECE) /* use Greek UGL */
1325 if ((index >= GREEK_START) && (index < GREEK_START + GREEK_GLYPHS))
1326 return TT_Char_Index(charMap, SubUGLGreek[index - GREEK_START]);
1327
1328 if (index <= MAX_GLYPH)
1329 return TT_Char_Index(charMap, UGL2Uni[index]);
1330 else
1331 ERRRET(0);
1332
1333 case TRANSLATE_SYMBOL :
1334 case TRANSLATE_UNICODE:
1335 case TRANSLATE_BIG5:
1336 case TRANSLATE_SJIS:
1337 return TT_Char_Index(charMap, index);
1338
1339 default:
1340 return 0;
1341 }
1342 #endif
1343 }
1344
1345 /****************************************************************************/
1346 /* */
1347 /* mystricmp : */
1348 /* */
1349 /* A simple function for comparing strings without case sensitivity. Just */
1350 /* returns zero if strings match, one otherwise. I wrote this because */
1351 /* stricmp is not available in the subsystem run-time library (probably */
1352 /* because it uses locales). toupper() is unfortunately unavailable too. */
1353 /* */
1354
1355 #define toupper( c ) ( ((c) >= 'a') && ((c) <= 'z') ? (c) - 'a' + 'A' : (c) )
1356
1357 static
mystricmp(const char * s1,const char * s2)1358 int mystricmp(const char *s1, const char *s2) {
1359 int i = 0;
1360 int match = 0;
1361 int len = strlen(s1);
1362
1363 if (len != strlen(s2))
1364 return 1; /* no match */
1365
1366 while (i < len) {
1367 if (toupper(s1[i]) != toupper(s2[i])) {
1368 match = 1;
1369 break;
1370 }
1371 i++;
1372 }
1373 return match;
1374 }
1375
1376 /* DBCS enabled strrchr (only looks for SBCS chars though) */
1377 static
mystrrchr(char * s,char c)1378 char *mystrrchr(char *s, char c) {
1379 int i = 0;
1380 int lastfound = -1;
1381 int len = strlen(s);
1382
1383 while (i <= len) {
1384 if (IsDBCSChar(s[i])) {
1385 i += 2;
1386 continue;
1387 }
1388 if (s[i] == c)
1389 lastfound = i;
1390 i++;
1391 }
1392 if (lastfound == -1)
1393 return NULL;
1394 else
1395 return s + lastfound;
1396 }
1397
1398 /* -------------------------------------------------------------------------*/
1399 /* here begin the exported functions */
1400 /* -------------------------------------------------------------------------*/
1401
1402 /****************************************************************************/
1403 /* */
1404 /* ConvertFontFile : */
1405 /* */
1406 /* Install/delete font file */
1407 /* */
ConvertFontFile(PSZ source,PSZ dest_dir,PSZ new_name)1408 LONG _System ConvertFontFile( PSZ source,
1409 PSZ dest_dir,
1410 PSZ new_name )
1411 {
1412 PSZ source_name;
1413
1414 COPY("ConvertFontFile: Src = "); CAT(source);
1415 if (dest_dir) {
1416 CAT(", DestDir = "); CAT(dest_dir);
1417 }
1418 CAT("\r\n"); WRITE;
1419
1420 if (dest_dir && new_name)
1421 {
1422 /* install the font file */
1423 source_name = mystrrchr( source, '\\' ); /* find the last backslash */
1424 if (!source_name)
1425 ERRRET(-1);
1426
1427 source_name++;
1428 strcpy( new_name, source_name );
1429
1430 /* check if file is to be copied onto itself */
1431 if (strncmp(source, dest_dir, strlen(dest_dir)) == 0)
1432 return OK; /* do nothing */
1433
1434 if ( DosCopy( source, dest_dir, DCPY_EXISTING) ) /* overwrite file */
1435 ERRRET(-1); /* XXX : we should probably set the error condition */
1436
1437 COPY(" -> Name: "); CAT(new_name); CAT("\r\n"); WRITE;
1438 }
1439 else
1440 {
1441 COPY("Delete file "); CAT(source); CAT("\r\n"); WRITE;
1442 DosDelete(source); /* fail quietly */
1443 }
1444
1445 return OK;
1446 }
1447
1448 /****************************************************************************/
1449 /* */
1450 /* LoadFontFile : */
1451 /* */
1452 /* open a font file and return a handle for it */
1453 /* */
LoadFontFile(PSZ file_name)1454 HFF _System LoadFontFile( PSZ file_name )
1455 {
1456 PSZ extension;
1457 PFontFile cur_file;
1458 PListElement element;
1459
1460 COPY( "LoadFontFile " ); CAT( file_name ); CAT( "\r\n" ); WRITE;
1461
1462 /* first check if the file extension is supported */
1463 extension = mystrrchr( file_name, '.' ); /* find the last dot */
1464 if ( extension == NULL ||
1465 (mystricmp(extension, ".TTF") &&
1466 mystricmp(extension, ".TTC")) )
1467 return ((HFF)-1);
1468
1469 /* now actually open the file */
1470 cur_file = New_FontFile( file_name );
1471 if (cur_file)
1472 return cur_file->hff;
1473 else
1474 return (HFF)-1;
1475 }
1476
1477 /****************************************************************************/
1478 /* */
1479 /* UnloadFontFile : */
1480 /* */
1481 /* destroy resources associated with a given HFF */
1482 /* */
UnloadFontFile(HFF hff)1483 LONG _System UnloadFontFile( HFF hff )
1484 {
1485 PListElement element;
1486
1487 COPY("UnloadFontFile: hff = "); CATI((int) hff); CAT("\r\n"); WRITE;
1488
1489 /* look in the live list first */
1490 for (element = liveFiles.head; element; element = element->next)
1491 {
1492 if (element->key == (long)hff)
1493 {
1494 PFontFile file = (PFontFile)element->data;
1495
1496 if (--file->ref_count > 0) /* don't really close, return OK */
1497 return 0;
1498
1499 List_Remove( &liveFiles, element );
1500 Done_Element( element );
1501 Done_FontFile( &file );
1502 return 0;
1503 }
1504 }
1505
1506 /* now look in sleep list */
1507 for (element = idleFiles.head; element; element = element->next)
1508 {
1509 if (element->key == (long)hff)
1510 {
1511 PFontFile file = (PFontFile)element->data;
1512
1513 if (--file->ref_count > 0) /* don't really close, return OK */
1514 return 0;
1515
1516 List_Remove( &idleFiles, element );
1517 Done_Element( element );
1518 Done_FontFile( &file );
1519 return 0;
1520 }
1521 }
1522
1523 /* didn't find the file */
1524 return -1;
1525 }
1526
1527 /****************************************************************************/
1528 /* */
1529 /* QueryFaces : */
1530 /* */
1531 /* Return font metrics. This routine has to do a lot of not very */
1532 /* hard work. */
1533 /* */
QueryFaces(HFF hff,PIFIMETRICS pifiMetrics,ULONG cMetricLen,ULONG cFontCount,ULONG cStart)1534 LONG _System QueryFaces( HFF hff,
1535 PIFIMETRICS pifiMetrics,
1536 ULONG cMetricLen,
1537 ULONG cFontCount,
1538 ULONG cStart)
1539 {
1540 static TT_Face_Properties properties;
1541 static IFIMETRICS ifi; /* temporary structure */
1542 PFontFace pface;
1543 TT_Header *phead;
1544 TT_Horizontal_Header *phhea;
1545 TT_OS2 *pOS2;
1546 TT_Postscript *ppost;
1547 PIFIMETRICS pifi2;
1548 PFontFile file;
1549 LONG index, faceIndex, ifiCount = 0;
1550 char *name;
1551
1552 COPY( "QueryFaces: hff = " ); CATI( hff );
1553 CAT( ", cFontCount = " ); CATI( cFontCount );
1554 CAT( ", cStart = " ); CATI( cStart );
1555 CAT( ", cMetricLen = " ); CATI( cMetricLen );
1556 CAT( "\r\n");
1557 WRITE;
1558
1559 file = getFontFile(hff);
1560 if (!file)
1561 ERRRET(-1) /* error, invalid handle */
1562
1563 if (cMetricLen == 0) { /* only number of faces is requested */
1564 #ifdef FAKE_TNR
1565 /* create an alias for Times New Roman */
1566 pface = &(file->faces[0]);
1567 name = LookupName(pface->face, TT_NAME_ID_FONT_FAMILY);
1568 if (!strcmp(name, "Times New Roman")) {
1569 file->flags |= FL_FLAG_FAKE_ROMAN;
1570 return 2;
1571 }
1572 #endif
1573 if (file->flags & FL_FLAG_DBCS_FILE)
1574 return file->numFaces * 2;
1575 else
1576 return file->numFaces;
1577 }
1578
1579 for (faceIndex = 0; faceIndex < file->numFaces; faceIndex++) {
1580 /* get pointer to this face's data */
1581 pface = &(file->faces[faceIndex]);
1582
1583 TT_Get_Face_Properties( pface->face, &properties );
1584
1585 pOS2 = properties.os2;
1586 phead = properties.header;
1587 phhea = properties.horizontal;
1588 ppost = properties.postscript;
1589
1590 /* get font name and check it's really found */
1591 name = LookupName(pface->face, TT_NAME_ID_FONT_FAMILY);
1592 if (name == NULL)
1593 ERET1(Fail);
1594
1595 strncpy(ifi.szFamilyname, name, FACESIZE);
1596 ifi.szFamilyname[FACESIZE - 1] = '\0';
1597
1598 name = LookupName(pface->face, TT_NAME_ID_FULL_NAME);
1599 if (name == NULL) {
1600 ERET1(Fail);
1601 }
1602 strncpy(ifi.szFacename, name, FACESIZE);
1603 ifi.szFacename[FACESIZE - 1] = '\0';
1604
1605 /* If Unicode cmap exists in font and it contains more than 1024 glyphs, */
1606 /* then do not translate from UGL to Unicode and use straight Unicode. */
1607 /* But first check if it's a DBCS font and handle it properly */
1608 if ((pface->charMode == TRANSLATE_UGL) && (properties.num_Glyphs > 1024))
1609 {
1610 LONG specEnc;
1611 BOOL UDCflag = FALSE; /* !!!!TODO: UDC support */
1612
1613 specEnc = interfaceSEId(pface->face, UDCflag, PSEID_UNICODE);
1614 switch (specEnc) {
1615 case PSEID_SHIFTJIS:
1616 strcpy( ifi.szGlyphlistName, "PMJPN" );
1617 pface->charMode = TRANSLATE_UNI_SJIS;
1618 break;
1619
1620 case PSEID_BIG5:
1621 strcpy( ifi.szGlyphlistName, "PMCHT" );
1622 pface->charMode = TRANSLATE_UNI_BIG5;
1623 break;
1624
1625 default: /* do use straight Unicode */
1626 strcpy( ifi.szGlyphlistName, "UNICODE" );
1627 pface->charMode = TRANSLATE_UNICODE; /* straight Unicode */
1628 }
1629 #if 0
1630 strcpy( ifi.szGlyphlistName, "PMJPN" );
1631 pface->charMode = TRANSLATE_UNI_SJIS;
1632 #endif
1633 }
1634 else
1635 if (pface->charMode == TRANSLATE_SYMBOL) /* symbol encoding */
1636 strcpy(ifi.szGlyphlistName, "SYMBOL");
1637 else
1638 if (pface->charMode == TRANSLATE_BIG5) /* Big5 encoding */
1639 strcpy(ifi.szGlyphlistName, "PMCHT");
1640 else
1641 if (pface->charMode == TRANSLATE_SJIS)
1642 strcpy(ifi.szGlyphlistName, "PMJPN"); /* ShiftJIS encoding */
1643 else
1644 strcpy(ifi.szGlyphlistName, "PM383");
1645
1646 ifi.idRegistry = 0;
1647 ifi.lCapEmHeight = phead->Units_Per_EM; /* ??? probably correct */
1648 ifi.lXHeight = phead->yMax /2; /* IBM TRUETYPE.DLL does */
1649 ifi.lMaxAscender = pOS2->usWinAscent;
1650
1651 if ((LONG)pOS2->usWinDescent >= 0)
1652 ifi.lMaxDescender = pOS2->usWinDescent;
1653 else
1654 ifi.lMaxDescender = -pOS2->usWinDescent;
1655
1656 ifi.lLowerCaseAscent = phhea->Ascender;
1657 ifi.lLowerCaseDescent = -phhea->Descender;
1658
1659 ifi.lInternalLeading = ifi.lMaxAscender + ifi.lMaxDescender
1660 - ifi.lCapEmHeight;
1661
1662 ifi.lExternalLeading = 0;
1663 ifi.lAveCharWidth = pOS2->xAvgCharWidth;
1664 ifi.lMaxCharInc = phhea->advance_Width_Max;
1665 ifi.lEmInc = phead->Units_Per_EM;
1666 ifi.lMaxBaselineExt = ifi.lMaxAscender + ifi.lMaxDescender;
1667 ifi.fxCharSlope = -ppost->italicAngle; /* is this correct ? */
1668 ifi.fxInlineDir = 0;
1669 ifi.fxCharRot = 0;
1670 ifi.usWeightClass = pOS2->usWeightClass; /* hopefully OK */
1671 ifi.usWidthClass = pOS2->usWidthClass;
1672 ifi.lEmSquareSizeX = phead->Units_Per_EM;
1673 ifi.lEmSquareSizeY = phead->Units_Per_EM; /* probably correct */
1674 ifi.giFirstChar = 0; /* following values should work */
1675 ifi.giLastChar = 503; /* either 383 or 503 */
1676 ifi.giDefaultChar = 0;
1677 ifi.giBreakChar = 32;
1678 ifi.usNominalPointSize = 120; /* these are simply constants */
1679 ifi.usMinimumPointSize = 10;
1680 ifi.usMaximumPointSize = 10000; /* limit to 1000 pt (like the ATM fonts) */
1681 ifi.fsType = pOS2->fsType & IFIMETRICS_LICENSED; /* ??? */
1682 ifi.fsDefn = IFIMETRICS_OUTLINE; /* always with TrueType */
1683 ifi.fsSelection = 0;
1684 ifi.fsCapabilities = 0; /* must be zero according to the IFI spec */
1685 ifi.lSubscriptXSize = pOS2->ySubscriptXSize;
1686 ifi.lSubscriptYSize = pOS2->ySubscriptYSize;
1687 ifi.lSubscriptXOffset = pOS2->ySubscriptXOffset;
1688 ifi.lSubscriptYOffset = pOS2->ySubscriptYOffset;
1689 ifi.lSuperscriptXSize = pOS2->ySuperscriptXSize;
1690 ifi.lSuperscriptYSize = pOS2->ySuperscriptYSize;
1691 ifi.lSuperscriptXOffset = pOS2->ySuperscriptXOffset;
1692 ifi.lSuperscriptYOffset = pOS2->ySuperscriptYOffset;
1693 ifi.lUnderscoreSize = ppost->underlineThickness;
1694 if (ifi.lUnderscoreSize == 150)
1695 ifi.lUnderscoreSize = 100; /* little fix for Arial */
1696 ifi.lUnderscorePosition = -ppost->underlinePosition;
1697 ifi.lStrikeoutSize = pOS2->yStrikeoutSize;
1698 ifi.lStrikeoutPosition = pOS2->yStrikeoutPosition;
1699
1700 #if 1
1701 if (pface->directory.nTables != 0 &&
1702 pface->directory.tables[0].format == 0) { /* we support only format */
1703 ifi.cKerningPairs = (pface->directory.tables[0].length - 8) / 6;
1704 ifi.fsType |= IFIMETRICS_KERNING; /* !!! for testing only! */
1705 }
1706 else
1707 #endif
1708 ifi.cKerningPairs = 0;
1709
1710 /* Note that the following field seems to be the only reliable method of */
1711 /* recognizing a TT font from an app! Not that it should be done. */
1712 ifi.ulFontClass = 0x10D; /* just like TRUETYPE.DLL */
1713
1714 /* the following adjustment are needed because the TT spec defines */
1715 /* usWeightClass and fsType differently */
1716 if (ifi.usWeightClass >= 100)
1717 ifi.usWeightClass /= 100;
1718 if (ifi.usWeightClass == 4)
1719 ifi.usWeightClass = 5; /* does this help? */
1720 if (pOS2->panose[3] == 9) {
1721 ifi.fsType |= IFIMETRICS_FIXED;
1722 pface->flags |= FC_FLAG_FIXED_WIDTH; /* we'll need this later */
1723 }
1724
1725 switch (pface->charMode) { /* adjustments for var. encodings */
1726 case TRANSLATE_UNICODE:
1727 ifi.giLastChar = pOS2->usLastCharIndex;
1728 ifi.fsType |= IFIMETRICS_MBCS | IFIMETRICS_DBCS;
1729 break;
1730
1731 case TRANSLATE_SYMBOL:
1732 ifi.giLastChar = 255;
1733 break;
1734
1735 case TRANSLATE_BIG5:
1736 case TRANSLATE_UNI_BIG5:
1737 ifi.giLastChar = 383;
1738 ifi.fsType |= IFIMETRICS_MBCS | IFIMETRICS_DBCS;
1739 break;
1740
1741 case TRANSLATE_SJIS:
1742 case TRANSLATE_UNI_SJIS:
1743 ifi.giLastChar = 890;
1744 ifi.fsType |= IFIMETRICS_MBCS | IFIMETRICS_DBCS;
1745 break;
1746
1747 }
1748
1749 /* adjust fsSelection (TT defines this differently) */
1750 /* Note: Interestingly, the PMATM font driver seems to use the values
1751 defined in TT spec, at least for italic. Strange. Better leave it. */
1752 if (pOS2->fsSelection & 0x01) {
1753 ifi.fsSelection |= 0x01;
1754 }
1755 if (pOS2->fsSelection & 0x02) {
1756 ifi.fsSelection |= IFIMETRICS_UNDERSCORE;
1757 }
1758 if (pOS2->fsSelection & 0x04) {
1759 ifi.fsSelection |= IFIMETRICS_OVERSTRUCK;
1760 }
1761
1762 /* copy the right amount of data to output buffer, */
1763 /* also handle the 'fake' vertically rendered DBCS fonts */
1764 index = faceIndex * ((file->flags & FL_FLAG_DBCS_FILE) ? 2 : 1);
1765 if ((index >= cStart) && (index < (cStart + cFontCount))) {
1766 memcpy((((PBYTE) pifiMetrics) + ifiCount), &ifi,
1767 sizeof(IFIMETRICS) > cMetricLen ? cMetricLen : sizeof(IFIMETRICS));
1768 ifiCount += cMetricLen;
1769 }
1770 if ((file->flags & FL_FLAG_DBCS_FILE) && (index + 1 >= cStart) &&
1771 (index + 1 < (cStart + cFontCount))) {
1772
1773 pifi2 = (PIFIMETRICS) (((PBYTE) pifiMetrics) + ifiCount);
1774 memcpy(pifi2, &ifi,
1775 sizeof(IFIMETRICS) > cMetricLen ? cMetricLen : sizeof(IFIMETRICS));
1776 strcpy(pifi2->szFamilyname + 1, ifi.szFamilyname);
1777 pifi2->szFamilyname[0] = '@';
1778 strcpy(pifi2->szFacename + 1, ifi.szFacename);
1779 pifi2->szFacename[0] = '@';
1780 ifiCount += cMetricLen;
1781 }
1782 #ifdef FAKE_TNR
1783 if ((file->flags & FL_FLAG_FAKE_ROMAN) && (index + 1 >= cStart) &&
1784 (index + 1 < (cStart + cFontCount))) {
1785 pifi2 = (PIFIMETRICS) (((PBYTE) pifiMetrics) + ifiCount);
1786 memcpy(pifi2, &ifi,
1787 sizeof(IFIMETRICS) > cMetricLen ? cMetricLen : sizeof(IFIMETRICS));
1788 strcpy(pifi2->szFamilyname, "Roman");
1789 switch (strlen(ifi.szFacename)) { /* This looks weird but... works */
1790 case 15: /* Times New Roman */
1791 strcpy(pifi2->szFacename, "Tms Rmn");
1792 break;
1793 case 20: /* Times New Roman Bold*/
1794 strcpy(pifi2->szFacename, "Tms Rmn Bold");
1795 break;
1796 case 22: /* Times New Roman Italic*/
1797 strcpy(pifi2->szFacename, "Tms Rmn Italic");
1798 break;
1799 case 27: /* Times New Roman Bold Italic*/
1800 strcpy(pifi2->szFacename, "Tms Rmn Bold Italic");
1801 break;
1802 }
1803 ifiCount += cMetricLen;
1804 }
1805 #endif
1806 }
1807
1808 Exit:
1809 TT_Flush_Face(pface->face);
1810 return cFontCount;
1811
1812 Fail:
1813 TT_Flush_Face(pface->face);
1814 return -1;
1815 }
1816
1817 /****************************************************************************/
1818 /* */
1819 /* OpenFontContext : */
1820 /* */
1821 /* open new font context */
1822 /* */
OpenFontContext(HFF hff,ULONG ulFont)1823 HFC _System OpenFontContext( HFF hff,
1824 ULONG ulFont)
1825 {
1826 int i = 0;
1827 static TT_Instance instance;
1828 static PFontFile file;
1829 ULONG faceIndex;
1830
1831 COPY("OpenFontContext: hff = "); CATI((int) hff); CAT("\r\n");
1832 COPY(" ulFont = "); CATI((int) ulFont); CAT("\r\n");
1833 WRITE;
1834
1835 file = getFontFile(hff);
1836 if (!file)
1837 ERRRET((HFC)-1) /* error, invalid font handle */
1838
1839 /* calculate real face index in font file */
1840 faceIndex = file->flags & FL_FLAG_DBCS_FILE ? ulFont / 2 : ulFont;
1841
1842 #ifdef FAKE_TNR
1843 if (file->flags & FL_FLAG_FAKE_ROMAN)
1844 /* This font isn't real! */
1845 faceIndex = 0;
1846 #endif
1847
1848 if (faceIndex > file->numFaces)
1849 ERRRET((HFC)-1)
1850
1851 /* OK, create new instance with defaults */
1852 error = TT_New_Instance( file->faces[faceIndex].face, &instance);
1853 if (error)
1854 ERET1( Fail );
1855
1856 /* Instance resolution is set to 72 dpi and is never changed */
1857 error = TT_Set_Instance_Resolutions(instance, 72, 72);
1858 if (error)
1859 ERRRET((HFC)-1)
1860
1861 /* find first unused index */
1862 i = 0;
1863 while ((contexts[i].hfc != 0) && (i < MAX_CONTEXTS))
1864 i++;
1865
1866 if (i == MAX_CONTEXTS)
1867 ERET1( Fail ); /* no free slot in table */
1868
1869 contexts[i].hfc = (HFC)(i + 0x100); /* initialize table entries */
1870 contexts[i].instance = instance;
1871 contexts[i].transformed = FALSE; /* no scaling/rotation assumed */
1872 contexts[i].file = file;
1873 contexts[i].faceIndex = faceIndex;
1874
1875 /* for DBCS fonts/collections, odd indices are vertical versions*/
1876 if ((file->flags & FL_FLAG_DBCS_FILE) && (ulFont & 1))
1877 contexts[i].vertical = TRUE;
1878 else
1879 contexts[i].vertical = FALSE;
1880
1881 file->flags |= FL_FLAG_CONTEXT_OPEN; /* flag as in-use */
1882
1883 COPY("-> hfc "); CATI((int) contexts[i].hfc); CAT("\r\n"); WRITE;
1884
1885 TT_Flush_Face(file->faces[faceIndex].face);
1886 return contexts[i].hfc; /* everything OK */
1887
1888 Fail:
1889 TT_Flush_Face(file->faces[faceIndex].face);
1890 return (HFC)-1;
1891 }
1892
1893 /****************************************************************************/
1894 /* */
1895 /* SetFontContext : */
1896 /* */
1897 /* set font context parameters */
1898 /* */
SetFontContext(HFC hfc,PCONTEXTINFO pci)1899 LONG _System SetFontContext( HFC hfc,
1900 PCONTEXTINFO pci )
1901 {
1902 LONG ptsize, temp, emsize;
1903 PFontSize size;
1904
1905 COPY("SetFontContext: hfc = "); CATI((int) hfc);
1906 CAT(", sizlPPM.cx = "); CATI((int) pci->sizlPPM.cx);
1907 CAT(", sizlPPM.cy = "); CATI((int) pci->sizlPPM.cy);
1908 CAT("\r\n pfxSpot.x = "); CATI((int) pci->pfxSpot.x);
1909 CAT(", pfxSpot.y = "); CATI((int) pci->pfxSpot.y);
1910 CAT("\r\n eM11 = "); CATI((int) pci->matXform.eM11);
1911 CAT(", eM12 = "); CATI((int) pci->matXform.eM12);
1912 CAT(", eM21 = "); CATI((int) pci->matXform.eM21);
1913 CAT(", eM22 = "); CATI((int) pci->matXform.eM22);
1914 CAT("\r\n");
1915 WRITE;
1916
1917 size = getFontSize(hfc);
1918 if (!size)
1919 ERRRET(-1) /* error, invalid context handle */
1920
1921 emsize = size->file->faces[size->faceIndex].em_size;
1922
1923 /* Look at matrix and see if a transform is asked for */
1924 /* Actually when rotating by 90 degrees hinting could be used */
1925
1926 size->transformed =
1927 ( pci->matXform.eM11 != pci->matXform.eM22 ||
1928 (pci->matXform.eM12 | pci->matXform.eM21) != 0 ||
1929 pci->matXform.eM11 <= 0 );
1930
1931 if ( size->transformed )
1932 {
1933 /* check for simple stretch in one direction */
1934 if ((pci->matXform.eM11 > 0 && pci->matXform.eM22 > 0) &&
1935 (pci->matXform.eM12 | pci->matXform.eM21) == 0) {
1936
1937 LONG ptsizex, ptsizey;
1938
1939 size->transformed = FALSE; /* will be handled like nontransformed font */
1940
1941 ptsizex = (emsize * pci->matXform.eM11) >> 10;
1942 ptsizey = (emsize * pci->matXform.eM22) >> 10;
1943
1944 error = TT_Set_Instance_CharSizes(size->instance, ptsizex, ptsizey);
1945 if (error)
1946 ERRRET(-1) /* engine problem */
1947
1948 return 0;
1949 }
1950 /* note that eM21 and eM12 are swapped; I have no idea why, but */
1951 /* it seems to be correct */
1952 size->matrix.xx = pci->matXform.eM11 * 64;
1953 size->matrix.xy = pci->matXform.eM21 * 64;
1954 size->matrix.yx = pci->matXform.eM12 * 64;
1955 size->matrix.yy = pci->matXform.eM22 * 64;
1956
1957 /* set pointsize to Em size; this effectively disables scaling */
1958 /* but enables use of hinting */
1959 error = TT_Set_Instance_CharSize(size->instance, emsize);
1960 if (error)
1961 ERRRET(-1) /* engine problem */
1962
1963 return 0;
1964 }
1965
1966 /* calculate & set point size */
1967 ptsize = (emsize * (pci->matXform.eM11 + pci->matXform.eM21)) >> 10;
1968
1969 if (ptsize <= 0) /* must not allow zero point size ! */
1970 ptsize = 1; /* !!! should be handled better */
1971
1972 error = TT_Set_Instance_CharSize(size->instance, ptsize);
1973 if (error)
1974 ERRRET(-1) /* engine problem */
1975
1976 return 0; /* pretend everything is OK */
1977 }
1978
1979 /****************************************************************************/
1980 /* */
1981 /* CloseFontContext : */
1982 /* */
1983 /* destroy a font context */
1984 /* */
CloseFontContext(HFC hfc)1985 LONG _System CloseFontContext( HFC hfc)
1986 {
1987 PFontSize size;
1988
1989 COPY("CloseFontContext: hfc = "); CATI((int)hfc); CAT("\r\n"); WRITE;
1990
1991 size = getFontSize(hfc);
1992 if (!size)
1993 ERRRET(-1) /* error, invalid context handle */
1994
1995 /* mark table entry as free */
1996 size->hfc = 0;
1997
1998 /* !!!!! set flag in TFontFile structure */
1999 size->file->flags &= ~FL_FLAG_CONTEXT_OPEN; /* reset the in-use flag */
2000
2001 if (size->file->flags & FL_FLAG_LIVE_FACE) {
2002 COPY("Closing instance: "); CATI((int)(size->instance.z)); CAT("\r\n"); WRITE;
2003 error = TT_Done_Instance(size->instance);
2004 if (error)
2005 ERRRET(-1) /* engine error */
2006 }
2007
2008 COPY("CloseFontContext successful\r\n"); WRITE;
2009
2010 return 0; /* success */
2011 }
2012
2013 #define MAX_KERN_INDEX 504
2014
ReverseTranslate(PFontFace face,USHORT index)2015 GLYPH ReverseTranslate(PFontFace face, USHORT index) {
2016 ULONG i;
2017 GLYPH newidx = 0;
2018
2019 /* TODO: enable larger fonts */
2020 for (i = 0; i < MAX_KERN_INDEX; i++) {
2021 newidx = PM2TT(face->charMap,
2022 face->charMode,
2023 i);
2024 if (newidx == index)
2025 break;
2026 }
2027 if (i < MAX_KERN_INDEX)
2028 return i;
2029 else
2030 return 0;
2031 }
2032
2033 /****************************************************************************/
2034 /* */
2035 /* QueryFaceAttr */
2036 /* */
2037 /* Return various info about font face */
2038 /* */
QueryFaceAttr(HFC hfc,ULONG iQuery,PBYTE pBuffer,ULONG cb,PGLYPH pagi,GLYPH giStart)2039 LONG _System QueryFaceAttr( HFC hfc,
2040 ULONG iQuery,
2041 PBYTE pBuffer,
2042 ULONG cb,
2043 PGLYPH pagi,
2044 GLYPH giStart )
2045 {
2046 int count, i = 0;
2047 PFontSize size;
2048 PFontFace face;
2049 static TT_Face_Properties properties;
2050 TT_OS2 *pOS2;
2051 ABC_TRIPLETS* pt;
2052
2053 COPY("QueryFaceAttr: hfc = "); CATI((int) hfc); CAT("\r\n"); WRITE;
2054
2055 size = getFontSize(hfc);
2056 if (!size)
2057 ERRRET(-1) /* error, invalid context handle */
2058
2059 face = &(size->file->faces[size->faceIndex]);
2060
2061 if (iQuery == FD_QUERY_KERNINGPAIRS)
2062 {
2063 TT_Kern_0 kerntab; /* actual kerning table */
2064 ULONG used = 0; /* # bytes used in output buffer */
2065 FD_KERNINGPAIRS *kpair;
2066 USHORT *kernIndices, idx;
2067
2068 count = cb / sizeof(FD_KERNINGPAIRS);
2069
2070 COPY("QUERY_KERNINGPAIRS, "); CATI((int) count);
2071 CAT("\r\n"); WRITE;
2072 #if 1
2073
2074 if (face->directory.tables == NULL)
2075 return 0; /* no kerning info provided */
2076 /* !!!! could use better error checking */
2077 /* Only format 0 is supported (which is what M$ recommends) */
2078 if (face->directory.tables[0].format != 0) /* need only format 0 */
2079 ERRRET(-1);
2080
2081 error = TT_Load_Kerning_Table(face->face, 0);
2082 if (error)
2083 ERET1( Fail );
2084
2085 kerntab = face->directory.tables[0].t.kern0;
2086 kpair = (PVOID)pBuffer;
2087
2088 if (face->kernIndices == NULL) {
2089 TT_Get_Face_Properties( face->face, &properties );
2090 error = ALLOC(face->kernIndices,
2091 properties.num_Glyphs * sizeof (USHORT));
2092 if (error)
2093 ERET1( Fail );
2094
2095 /* fill all entries with -1s */
2096 memset(face->kernIndices, 0xFF,
2097 properties.num_Glyphs * sizeof (USHORT));
2098 }
2099
2100 kernIndices = face->kernIndices;
2101
2102 while ((i < kerntab.nPairs) && (i < count))
2103 {
2104 idx = kerntab.pairs[i].left;
2105 if (kernIndices[idx] == (USHORT)-1)
2106 kernIndices[idx] = ReverseTranslate(face, idx);
2107 kpair->giFirst = kernIndices[idx];
2108 idx = kerntab.pairs[i].right;
2109 if (kernIndices[idx] == (USHORT)-1)
2110 kernIndices[idx] = ReverseTranslate(face, idx);
2111 kpair->giSecond = kernIndices[idx];
2112 kpair->eKerningAmount = kerntab.pairs[i].value;
2113 kpair++;
2114 i++;
2115 }
2116
2117 COPY("Returned kerning pairs: "); CATI(i); CAT("\r\n"); WRITE;
2118 return i; /* # items filled */
2119 #else
2120 return 0; /* no kerning support */
2121
2122 #endif
2123 }
2124
2125 if (iQuery == FD_QUERY_ABC_WIDTHS)
2126 {
2127 count = cb / sizeof(ABC_TRIPLETS);
2128
2129 COPY("QUERY_ABC_WIDTHS, "); CATI((int) count);
2130 CAT(" items, giStart = "); CATI((int) giStart);
2131 if (pBuffer == NULL)
2132 CAT(" NULL buffer");
2133 CAT("\r\n"); WRITE;
2134
2135 /* This call never fails - no error check needed */
2136 TT_Get_Face_Properties( face->face, &properties );
2137
2138 pt = (ABC_TRIPLETS*)pBuffer;
2139 for (i = giStart; i < giStart + count; i++, pt++)
2140 {
2141 int index;
2142 unsigned short wid;
2143 static unsigned short adv_widths [2];
2144 static unsigned short adv_heights[2];
2145
2146 static unsigned short widths[2], heights[2];
2147 static short lefts [2], tops [2];
2148
2149 index = PM2TT( face->charMap,
2150 face->charMode,
2151 i );
2152
2153 /* get advances and bearings */
2154 if (size->vertical && properties.vertical && 0) /* TODO: enable */
2155 error = TT_Get_Face_Metrics( face->face, index, index,
2156 lefts, adv_widths, tops, adv_heights );
2157 else
2158 error = TT_Get_Face_Metrics( face->face, index, index,
2159 lefts, adv_widths, NULL, NULL );
2160
2161 if (error)
2162 goto Broken_Glyph;
2163
2164 /* skip complicated calculations for fixed fonts */
2165 if (face->flags & FC_FLAG_FIXED_WIDTH) {
2166 wid = adv_widths[0] - lefts[0];
2167 }
2168 else { /* proportianal font, it gets trickier */
2169 /* store glyph widths for DBCS fonts
2170 - needed for reasonable performance */
2171 if (face->flags & FC_FLAG_DBCS_FACE) {
2172 if (face->widths == NULL) {
2173 error = ALLOC(face->widths,
2174 properties.num_Glyphs * sizeof (USHORT));
2175 if (error)
2176 goto Broken_Glyph; /* this error really shouldn't happen */
2177
2178 /* tag all entries as unused */
2179 memset(face->widths, 0xFF,
2180 properties.num_Glyphs * sizeof (USHORT));
2181 }
2182 if (face->widths[index] == 0xFFFF) { /* get from file if needed */
2183 error = TT_Get_Face_Widths( face->face, index, index,
2184 widths, heights );
2185 if (error)
2186 goto Broken_Glyph;
2187
2188 /* save for later */
2189 wid = face->widths[index] = widths[0];
2190 }
2191 else
2192 wid = face->widths[index];
2193 }
2194 /* 'small' font, no need to remember widths, OS/2 takes care of it */
2195 else {
2196 /* get width or height - use ftxwidth.c */
2197 error = TT_Get_Face_Widths( face->face, index, index,
2198 widths, heights );
2199 if (error)
2200 goto Broken_Glyph;
2201
2202 wid = widths[0];
2203 }
2204 }
2205
2206 if (size->vertical && !is_HALFCHAR(i))
2207 {
2208 if (properties.vertical && 0) /* TODO: enable */
2209 {
2210 pt->lA = tops[0];
2211 pt->ulB = heights[0];
2212 pt->lC = adv_heights[0] - pt->lA - pt->ulB;
2213 }
2214 else
2215 {
2216 pt->lA = pt->lC = 0;
2217 pt->ulB = properties.os2->usWinAscent +
2218 properties.os2->usWinDescent;
2219 }
2220
2221 }
2222 else
2223 {
2224 pt->lA = lefts[0];
2225 pt->ulB = wid;
2226 pt->lC = adv_widths[0] - pt->lA - pt->ulB;
2227 }
2228
2229 #ifdef NETSCAPE_FIX
2230 if (face->charMode != TRANSLATE_SYMBOL &&
2231 !size->vertical) {
2232 if (face->flags & FC_FLAG_FIXED_WIDTH) {
2233 pt->ulB = pt->ulB + pt->lA + pt->lC;
2234 pt->lA = 0;
2235 pt->lC = 0;
2236 } else if (i == 32) {
2237 /* return nonzero B width for 'space' */
2238 pt->ulB = adv_widths[0] - 2 * lefts[0];
2239 pt->lC = lefts[0];
2240 }
2241 }
2242 #endif
2243 continue;
2244
2245 Broken_Glyph: /* handle broken glyphs gracefully */
2246 pt->lA = pt->lC = 0;
2247
2248 if (size->vertical && !is_HALFCHAR(i))
2249 pt->ulB = properties.os2->usWinAscent +
2250 properties.os2->usWinDescent;
2251 else
2252 pt->ulB = properties.horizontal->xMax_Extent;
2253
2254 }
2255 }
2256
2257 TT_Flush_Face(face->face);
2258 return count; /* number of entries filled in */
2259
2260 Fail:
2261 TT_Flush_Face(face->face);
2262 return -1;
2263 }
2264
2265 /****************************************************************************/
2266 /* */
2267 /* QueryCharAttr : */
2268 /* */
2269 /* Return glyph attributes, basically glyph's bit-map or outline */
2270 /* some variables are declared static to conserve stack space. */
2271 /* */
QueryCharAttr(HFC hfc,PCHARATTR pCharAttr,PBITMAPMETRICS pbmm)2272 LONG _System QueryCharAttr( HFC hfc,
2273 PCHARATTR pCharAttr,
2274 PBITMAPMETRICS pbmm )
2275 {
2276 static TT_Raster_Map bitmap;
2277 static TT_Outline outline;
2278 static TT_BBox bbox;
2279
2280 PFontSize size;
2281 PFontFace face;
2282 LONG temp;
2283 PBYTE pb;
2284 int i, j;
2285 ULONG cb;
2286
2287 size = getFontSize(hfc);
2288 if (!size)
2289 ERRRET(-1) /* error, invalid context handle */
2290
2291 face = &(size->file->faces[size->faceIndex]);
2292
2293 error = TT_Load_Glyph( size->instance,
2294 face->glyph,
2295 PM2TT( face->charMap,
2296 face->charMode,
2297 pCharAttr->gi),
2298 TTLOAD_DEFAULT);
2299
2300 if (error)
2301 {
2302 if (i == 0)
2303 ERET1( Fail ) /* this font's no good, return error */
2304 else
2305 { /* try to recover quietly */
2306 error = TT_Load_Glyph( size->instance,
2307 face->glyph,
2308 0,
2309 TTLOAD_DEFAULT);
2310 if (error) {
2311 COPY("Error code is "); CATI(error); CAT("\r\n"); WRITE;
2312 ERET1( Fail );
2313 }
2314 }
2315 }
2316
2317 TT_Flush_Face( face->face );
2318
2319 error = TT_Get_Glyph_Outline( face->glyph, &outline );
2320 if (error)
2321 ERRRET(-1);
2322
2323 /* --- Vertical fonts handling----------------------------------- */
2324
2325 if (size->vertical && !is_HALFCHAR(pCharAttr->gi)) {
2326 TT_Matrix vertMatrix;
2327
2328 vertMatrix.xx = 0x00000;
2329 vertMatrix.xy = -0x10000;
2330 vertMatrix.yx = 0x10000;
2331 vertMatrix.yy = 0x00000;
2332 TT_Get_Outline_BBox( &outline, &bbox );
2333
2334 /* rotate outline 90 degrees counterclockwise */
2335 TT_Transform_Outline(&outline, &vertMatrix);
2336
2337 /* move outline to the right to adjust for rotation */
2338 TT_Translate_Outline(&outline, bbox.yMax, 0);
2339 /* move outline down a bit */
2340 TT_Translate_Outline(&outline, 0, bbox.yMin);
2341 }
2342
2343 if (size->transformed)
2344 TT_Transform_Outline( &outline, &size->matrix );
2345
2346 /* --- Outline processing --------------------------------------- */
2347
2348 if ( pCharAttr->iQuery & FD_QUERY_OUTLINE )
2349 {
2350 if (pCharAttr->cbLen == 0) /* send required outline size in bytes */
2351 return GetOutlineLen( &outline );
2352
2353 return GetOutline( &outline, pCharAttr->pBuffer );
2354 }
2355
2356 /* --- Bitmap processing ---------------------------------------- */
2357
2358 TT_Get_Outline_BBox( &outline, &bbox );
2359
2360 /* the following seems to be necessary for rotated glyphs */
2361 if (size->transformed) {
2362 bbox.xMax = bbox.xMin = 0;
2363 for (i = 0; i < outline.n_points; i++) {
2364 if (bbox.xMin > outline.points[i].x)
2365 bbox.xMin = outline.points[i].x;
2366 if (bbox.xMax < outline.points[i].x)
2367 bbox.xMax = outline.points[i].x;
2368 }
2369 }
2370 /* grid-fit the bbox */
2371 bbox.xMin &= -64;
2372 bbox.yMin &= -64;
2373
2374 bbox.xMax = (bbox.xMax+63) & -64;
2375 bbox.yMax = (bbox.yMax+63) & -64;
2376
2377 if (pCharAttr->iQuery & FD_QUERY_BITMAPMETRICS)
2378 {
2379 /* fill in bitmap metrics */
2380 /* metrics values are in 26.6 format ! */
2381 pbmm->sizlExtent.cx = (bbox.xMax - bbox.xMin) >> 6;
2382 pbmm->sizlExtent.cy = (bbox.yMax - bbox.yMin) >> 6;
2383 pbmm->cyAscent = 0;
2384 pbmm->pfxOrigin.x = bbox.xMin << 10;
2385 pbmm->pfxOrigin.y = bbox.yMax << 10;
2386
2387 if (!(pCharAttr->iQuery & FD_QUERY_CHARIMAGE))
2388 return sizeof(*pbmm);
2389 }
2390
2391 /* --- actual bitmap processing here --- */
2392 if (pCharAttr->iQuery & FD_QUERY_CHARIMAGE)
2393 {
2394 /* values in 26.6 format ?!? */
2395 bitmap.width = (bbox.xMax - bbox.xMin) >> 6;
2396 bitmap.rows = (bbox.yMax - bbox.yMin) >> 6;
2397 /* width rounded up to nearest multiple of 4 */
2398 bitmap.cols = ((bitmap.width + 31) / 8) & -4;
2399 bitmap.flow = TT_Flow_Down;
2400 bitmap.bitmap = pCharAttr->pBuffer;
2401 bitmap.size = bitmap.rows * bitmap.cols;
2402
2403 if (pCharAttr->cbLen == 0)
2404 return bitmap.size;
2405
2406 if (bitmap.size > pCharAttr->cbLen)
2407 ERRRET(-1) /* otherwise we might overwrite something */
2408
2409 /* clean provided buffer (unfortunately necessary) */
2410 memset(bitmap.bitmap, 0, pCharAttr->cbLen);
2411
2412 error = TT_Get_Glyph_Bitmap( face->glyph,
2413 &bitmap,
2414 -bbox.xMin,
2415 -bbox.yMin );
2416 if (error)
2417 ERRRET(-1); /* engine error */
2418
2419 return bitmap.size; /* return # of bytes */
2420 }
2421 ERRRET(-1) /* error */
2422
2423 Fail:
2424 TT_Flush_Face(face->face);
2425 return -1;
2426 }
2427
2428 /****************************************************************************/
2429 /* */
2430 /* QueryFullFaces : */
2431 /* */
2432 /* Query names of all faces in this file */
2433 /* */
QueryFullFaces(HFF hff,PVOID pBuff,PULONG buflen,PULONG cFontCount,ULONG cStart)2434 LONG _System QueryFullFaces( HFF hff,
2435 PVOID pBuff,
2436 PULONG buflen,
2437 PULONG cFontCount,
2438 ULONG cStart )
2439 {
2440 COPY("!QueryFullFaces: hff = "); CATI((int) hff); CAT("\r\n"); WRITE;
2441 ERRRET(-1) /* error ? */
2442 }
2443
2444 /*---------------------------------------------------------------------------*/
2445 /* end of exported functions */
2446 /*---------------------------------------------------------------------------*/
2447
2448
2449 /****************************************************************************/
2450 /* LimitsInit reads OS2.INI and sets up max_open_files limit, possibly */
2451 /* other variables as well. */
2452 /* */
LimitsInit(void)2453 static void LimitsInit(void) {
2454 char cBuffer[25]; /* ought to be enough */
2455
2456 if (PrfQueryProfileString(HINI_USERPROFILE, "FreeType/2", "OPENFACES",
2457 NULL, cBuffer, sizeof(cBuffer)) > 0) {
2458 max_open_files = atoi(cBuffer);
2459
2460 if (max_open_files < 8) /* ensure limit isn't too low */
2461 max_open_files = 8;
2462 }
2463 else
2464 max_open_files = 12; /* reasonable default */
2465 }
2466
2467
2468 /****************************************************************************/
2469 /* my_itoa is used only in the following function GetUdcInfo. */
2470 /* Works pretty much like expected. */
2471 /* */
my_itoa(int num,char * cp)2472 void my_itoa(int num, char *cp) {
2473 char temp[10];
2474 int i = 0;
2475
2476 do {
2477 temp[i++] = (num % 10) + '0';
2478 num /= 10;
2479 } while (num); /* enddo */
2480
2481 while (i--) {
2482 *cp++ = temp[i];
2483 } /* endwhile */
2484 *cp = '\0';
2485 }
2486
2487 /****************************************************************************/
2488 /* GetUdcInfo determines the UDC ranges used */
2489 /* */
GetUdcInfo(VOID)2490 VOID GetUdcInfo(VOID) {
2491 ULONG ulUdc, ulUdcInfo, i;
2492 PVOID gPtr;
2493 HINI hini;
2494 CHAR szCpStr[10] = "CP";
2495
2496 DosQueryCp(sizeof(ulCp), (ULONG*)&ulCp, &i); /* find out default codepage */
2497 my_itoa((INT) ulCp, szCpStr + 2); /* convert to ASCII */
2498
2499 }
2500
2501 /****************************************************************************/
2502 /* LangInit determines language used at DLL startup, non-zero return value */
2503 /* means error. */
2504 /* This code is crucial, because it determines behaviour of the font driver */
2505 /* with regard to language encodings it will use. */
LangInit(void)2506 static ULONG LangInit(void) {
2507 COUNTRYCODE cc = {0, 0};
2508 COUNTRYINFO ci;
2509 ULONG cilen;
2510
2511 isGBK = FALSE;
2512
2513 GetUdcInfo(); /* get User Defined Character info */
2514
2515 /* get country info; ci.country then contains country code */
2516 if (DosQueryCtryInfo(sizeof(ci), &cc, &ci, &cilen))
2517 return -1;
2518 /* get DBCS lead byte values for later use */
2519 DosQueryDBCSEnv(sizeof(DBCSLead), &cc, DBCSLead);
2520
2521 uLastGlyph = 383;
2522 switch (ci.country) {
2523 case 81: /* Japan */
2524 iLangId = TT_MS_LANGID_JAPANESE_JAPAN;
2525 ScriptTag = *(ULONG *) "kana";
2526 LangSysTag = *(ULONG *) "JAN ";
2527 pGlyphlistName = "PMJPN";
2528 uLastGlyph = 890;
2529 break;
2530
2531 case 88: /* Taiwan */
2532 iLangId = TT_MS_LANGID_CHINESE_TAIWAN;
2533 ScriptTag = *(ULONG *) "kana";
2534 LangSysTag = *(ULONG *) "CHT ";
2535 pGlyphlistName = "PMCHT";
2536 break;
2537
2538 case 86: /* People's Republic of China */
2539 if (ci.codepage == 1386 || ulCp[0] == 1386 || ulCp[1] == 1386) {
2540 isGBK = TRUE;
2541 } /* endif */
2542 iLangId = TT_MS_LANGID_CHINESE_PRC;
2543 ScriptTag = *(ULONG *) "kana";
2544 LangSysTag = *(ULONG *) "CHS ";
2545 pGlyphlistName = "PMPRC";
2546 break;
2547
2548 case 82: /* Korea */
2549 iLangId = TT_MS_LANGID_KOREAN_EXTENDED_WANSUNG_KOREA;
2550 ScriptTag = *(ULONG *) "hang";
2551 LangSysTag = *(ULONG *) "KOR ";
2552 pGlyphlistName = "PMKOR";
2553 uLastGlyph = 949;
2554 break;
2555
2556 case 30: /* Greece - for Alex! */
2557 iLangId = TT_MS_LANGID_GREEK_GREECE;
2558
2559 default: /* none of the above countries */
2560 ScriptTag = *(ULONG *) "";
2561 LangSysTag = *(ULONG *) "";
2562 break;
2563 } /* endswitch */
2564
2565 return 0;
2566 }
2567
2568 /****************************************************************************/
2569 /* */
2570 /* FirstInit : */
2571 /* */
2572 /* Called when font driver is loaded for the first time. Performs the */
2573 /* necessary one-time initialization. */
FirstInit(void)2574 ULONG FirstInit(void) {
2575 LONG lReqCount;
2576 ULONG ulCurMaxFH;
2577
2578 #ifdef DEBUG
2579 ULONG Action;
2580 #endif /* DEBUG */
2581 #ifdef DEBUG
2582 DosOpen("C:\\FTIFI.LOG", &LogHandle, &Action, 0, FILE_NORMAL,
2583 OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_REPLACE_IF_EXISTS,
2584 OPEN_FLAGS_NO_CACHE | OPEN_FLAGS_WRITE_THROUGH |
2585 OPEN_FLAGS_SEQUENTIAL | OPEN_SHARE_DENYWRITE | OPEN_ACCESS_WRITEONLY,
2586 NULL);
2587 COPY("FreeType/2 loaded.\r\n");
2588 WRITE;
2589 #endif /* DEBUG */
2590
2591 /* increase # of file handles by five to be on the safe side */
2592 lReqCount = 5;
2593 DosSetRelMaxFH(&lReqCount, &ulCurMaxFH);
2594 error = TT_Init_FreeType(&engine); /* turn on the FT engine */
2595 if (error)
2596 return 0; /* exit immediately */
2597 error = TT_Init_Kerning_Extension(engine); /* load kerning support */
2598 COPY("FreeType Init called\r\n");
2599 WRITE;
2600
2601 if (LangInit()) /* initialize NLS */
2602 return 0; /* exit on error */
2603 COPY("NLS initialized.\r\n");
2604 WRITE;
2605
2606 LimitsInit(); /* initialize max_open_files */
2607 COPY("Open faces limit set to "); CATI(max_open_files); CAT("\r\n");
2608 WRITE;
2609
2610 if (error)
2611 return 0; /* exit immediately */
2612 COPY("Initialization successful.\r\n");
2613 WRITE;
2614 return 1;
2615 }
2616
2617
2618 /****************************************************************************/
2619 /* */
2620 /* FinalTerm : */
2621 /* */
2622 /* Called when font driver is unloaded for the last time time. Performs */
2623 /* final clean-up, shuts down engine etc. */
FinalTerm(void)2624 ULONG FinalTerm(void) {
2625 PListElement cur;
2626 PListElement tmp;
2627
2628 /* throw away elements from 'free elements' list */
2629 cur = free_elements;
2630 while (cur != NULL) {
2631 tmp = cur;
2632 cur = cur->next;
2633 FREE(tmp);
2634 }
2635
2636 /* turn off engine */
2637 TT_Done_FreeType(engine);
2638
2639 #ifdef DEBUG
2640 COPY("FreeType/2 terminated.\r\n");
2641 WRITE;
2642 DosClose(LogHandle);
2643 #endif
2644 return 1;
2645 }
2646 /****************************************************************************/
2647 /* */
2648 /* _DLL_InitTerm : */
2649 /* */
2650 /* This is the DLL Initialization/termination function. It initializes */
2651 /* the FreeType engine and some internal structures at startup. It cleans */
2652 /* up the UCONV cache at process termination. */
_DLL_InitTerm(ULONG hModule,ULONG ulFlag)2653 ULONG _System _DLL_InitTerm(ULONG hModule, ULONG ulFlag) {
2654 switch (ulFlag) {
2655 case 0: /* initializing */
2656 if (++ulProcessCount == 1)
2657 return FirstInit(); /* loaded for the first time */
2658 else
2659 return 1;
2660
2661 case 1: { /* terminating */
2662 int i;
2663 /* clean UCONV cache */
2664 #ifdef USE_UCONV
2665 CleanUCONVCache();
2666 #endif
2667 if(--ulProcessCount == 0)
2668 return FinalTerm();
2669 else
2670 return 1;
2671 }
2672 }
2673 return 0;
2674 }
2675
2676 /****************************************************************************/
2677 /* */
2678 /* interfaceSEId (Interface-specific Encoding Id) determines what encoding */
2679 /* the font driver should use if a font includes a Unicode encoding. */
2680 /* */
interfaceSEId(TT_Face face,BOOL UDCflag,LONG encoding)2681 LONG interfaceSEId(TT_Face face, BOOL UDCflag, LONG encoding) {
2682 ULONG range1 = 0;
2683 ULONG bits, mask;
2684 TT_OS2 *pOS2;
2685 static TT_Face_Properties props;
2686
2687 TT_Get_Face_Properties(face, &props);
2688 pOS2 = props.os2;
2689
2690 if (encoding == PSEID_UNICODE) {
2691
2692 /* if font is 'small', use PM383; this is done because of DBCS
2693 systems */
2694 if (!UDCflag && props.num_Glyphs < 1024) {
2695 encoding = PSEID_PM383;
2696 } else if (pOS2->version >= 1) {
2697 /*
2698 * * OS/2 table version 1 and later contains codepage *
2699 * bitfield to support multiple codepages.
2700 */
2701 range1 = pOS2->ulCodePageRange1;
2702 bits = 0;
2703
2704 if (range1 & OS2_CP1_ANSI_OEM_JAPANESE_JIS)
2705 bits++;
2706 if (range1 & OS2_CP1_ANSI_OEM_CHINESE_SIMPLIFIED)
2707 bits++;
2708 if (range1 & OS2_CP1_ANSI_OEM_CHINESE_TRADITIONAL)
2709 bits++;
2710 if (range1 & OS2_CP1_ANSI_OEM_KOREAN_WANSUNG)
2711 bits++;
2712 if (range1 & OS2_CP1_ANSI_OEM_KOREAN_JOHAB)
2713 bits++;
2714
2715 /* Note: if font supports more than one of the following codepages,
2716 * encoding is left at PSEID_UNICODE!
2717 */
2718 if (bits == 1) {
2719 switch (range1) {
2720 case OS2_CP1_ANSI_OEM_JAPANESE_JIS:
2721 encoding = PSEID_SHIFTJIS;
2722 break;
2723 case OS2_CP1_ANSI_OEM_CHINESE_SIMPLIFIED:
2724 encoding = PSEID_PRC;
2725 break;
2726 case OS2_CP1_ANSI_OEM_CHINESE_TRADITIONAL:
2727 encoding = PSEID_BIG5;
2728 break;
2729 case OS2_CP1_ANSI_OEM_KOREAN_WANSUNG:
2730 encoding = PSEID_WANSUNG;
2731 break;
2732 case OS2_CP1_ANSI_OEM_KOREAN_JOHAB:
2733 encoding = PSEID_JOHAB;
2734 break;
2735 default:
2736 break;
2737 } /* endswitch */
2738 } /* endif */
2739 } else {
2740 /*
2741 * The codepage range bitfield is not available.
2742 * Codepage must be assumed from the COUNTRY setting.
2743 * This means the user is on his own.
2744 */
2745
2746 switch (iLangId) {
2747 case TT_MS_LANGID_JAPANESE_JAPAN:
2748 encoding = PSEID_SHIFTJIS;
2749 break;
2750 case TT_MS_LANGID_CHINESE_PRC:
2751 case TT_MS_LANGID_CHINESE_SINGAPORE:
2752 encoding = PSEID_PRC;
2753 break;
2754 case TT_MS_LANGID_CHINESE_TAIWAN:
2755 case TT_MS_LANGID_CHINESE_HONG_KONG:
2756 encoding = PSEID_BIG5;
2757 break;
2758 case TT_MS_LANGID_KOREAN_EXTENDED_WANSUNG_KOREA:
2759 encoding = PSEID_WANSUNG;
2760 break;
2761 case TT_MS_LANGID_KOREAN_JOHAB_KOREA:
2762 encoding = PSEID_JOHAB;
2763 break;
2764 }
2765
2766 }
2767 }
2768 return encoding;
2769 }
2770
2771 /****************************************************************************/
2772 /* */
2773 /* LookupName : */
2774 /* */
2775 /* Look for a TrueType name by index, prefer current language */
2776 /* */
LookupName(TT_Face face,int index)2777 static char* LookupName(TT_Face face, int index )
2778 {
2779 static char name_buffer[FACESIZE + 2];
2780 int name_len = 0;
2781 int i, j, n;
2782
2783 USHORT platform, encoding, language, id;
2784 char* string;
2785 USHORT string_len;
2786
2787 int found;
2788
2789 n = TT_Get_Name_Count( face );
2790 if ( n < 0 )
2791 return NULL;
2792
2793 for ( i = 0; i < n; i++ )
2794 {
2795 TT_Get_Name_ID( face, i, &platform, &encoding, &language, &id );
2796 TT_Get_Name_String( face, i, &string, &string_len );
2797
2798 if ( id == index )
2799 {
2800 found = 0;
2801
2802 /* Try to find an appropriate name */
2803 if ( platform == TT_PLATFORM_MICROSOFT )
2804 for ( j = 5; j >= 0; j-- )
2805 if ( encoding == j ) /* Microsoft ? */
2806 switch (language)
2807 {
2808 case TT_MS_LANGID_CHINESE_TAIWAN:
2809 if (encoding == PSEID_PRC)
2810 found = 1;
2811 break;
2812
2813 case TT_MS_LANGID_JAPANESE_JAPAN:
2814 if (encoding == PSEID_SHIFTJIS)
2815 found = 1;
2816 break;
2817
2818 /* these aren't all possibilities; just the most likely ones */
2819 case TT_MS_LANGID_ENGLISH_UNITED_STATES :
2820 case TT_MS_LANGID_ENGLISH_UNITED_KINGDOM :
2821 case TT_MS_LANGID_ENGLISH_AUSTRALIA :
2822 case TT_MS_LANGID_ENGLISH_CANADA :
2823 case TT_MS_LANGID_ENGLISH_NEW_ZEALAND :
2824 case TT_MS_LANGID_ENGLISH_IRELAND :
2825 case TT_MS_LANGID_ENGLISH_SOUTH_AFRICA :
2826 found = 1;
2827 break;
2828 }
2829
2830 if ( !found && platform == 0 && language == 0 )
2831 found = 1;
2832
2833 if (found)
2834 {
2835 if (language == TT_MS_LANGID_CHINESE_TAIWAN ||
2836 language == TT_MS_LANGID_JAPANESE_JAPAN) {
2837 /* it's a DBCS string, copy everything except NULLs */
2838 int i,j;
2839 if (string_len > FACESIZE - 1)
2840 string_len = FACESIZE - 1;
2841
2842 for (i=0, j=0; i<string_len; i++)
2843 if (string[i] != '\0')
2844 name_buffer[j++] = string[i];
2845 name_buffer[j] = '\0';
2846
2847 return name_buffer;
2848 }
2849 else {
2850 /* assume it's an ASCII string in Unicode, just skip the
2851 zeros */
2852 if ( string_len > FACESIZE * 2)
2853 string_len = FACESIZE * 2;
2854
2855 name_len = 0;
2856
2857 for ( i = 1; i < string_len; i += 2 )
2858 name_buffer[name_len++] = string[i];
2859
2860 name_buffer[name_len] = '\0';
2861
2862 return name_buffer;
2863 }
2864 }
2865 }
2866 }
2867
2868 /* Not found */
2869 return NULL;
2870 }
2871
2872 /****************************************************************************/
2873 /* */
2874 /* GetCharMap : */
2875 /* */
2876 /* A function to find a suitable charmap, searching in the following */
2877 /* order of importance : */
2878 /* */
2879 /* 1) Windows Unicode */
2880 /* 2) Apple Unicode */
2881 /* 3) ROC (Taiwan) */
2882 /* 4) ShiftJIS (Japan) */
2883 /* 5) Apple Roman */
2884 /* 6) Windows Symbol - not really supported */
2885 /* */
2886 /* High word of returned ULONG contains type of encoding */
2887 /* */
GetCharmap(TT_Face face)2888 static ULONG GetCharmap(TT_Face face)
2889 {
2890 int n; /* # of encodings (charmaps) available */
2891 USHORT platform, encoding;
2892 int i, best, bestVal, val;
2893
2894 n = TT_Get_CharMap_Count(face);
2895
2896 if (n < 0) /* no encodings at all; don't yet know what the best course of action would be */
2897 ERRRET(-1) /* such font should probably be rejected */
2898
2899 bestVal = 16;
2900 best = -1;
2901
2902 for (i = 0; i < n; i++)
2903 {
2904 TT_Get_CharMap_ID( face, i, &platform, &encoding );
2905
2906 /* Windows Unicode is the highest encoding, return immediately */
2907 /* if we find it.. */
2908 if ( platform == TT_PLATFORM_MICROSOFT && encoding == TT_MS_ID_UNICODE_CS)
2909 return i;
2910
2911 /* otherwise, compare it to the best encoding found */
2912 val = -1;
2913 if (platform == TT_PLATFORM_APPLE_UNICODE)
2914 val = 2;
2915 else if (platform == TT_PLATFORM_MICROSOFT
2916 && encoding == TT_MS_ID_BIG_5)
2917 val = 3;
2918 else if (platform == TT_PLATFORM_MICROSOFT
2919 && encoding == TT_MS_ID_SJIS)
2920 val = 4;
2921 else if (platform == TT_PLATFORM_MACINTOSH
2922 && encoding == TT_MAC_ID_ROMAN)
2923 val = 5;
2924 else if (platform == TT_PLATFORM_MICROSOFT
2925 && encoding == TT_MS_ID_SYMBOL_CS)
2926 val = 6;
2927
2928 if (val > 0 && val <= bestVal)
2929 {
2930 bestVal = val;
2931 best = i;
2932 }
2933 }
2934
2935 if (i < 0)
2936 return 0; /* we didn't find any suitable encoding !! */
2937
2938 if (bestVal == 3) /* Taiwanese font */
2939 best |= ( TRANSLATE_BIG5 << 16 );
2940
2941 if (bestVal == 4) /* Japanese font */
2942 best |= ( TRANSLATE_SJIS << 16 );
2943
2944 if (bestVal == 5) /* for Apple Roman encoding only, this */
2945 best |= ( TRANSLATE_SYMBOL << 16 ); /* means no translation should be performed */
2946
2947 return best;
2948 }
2949
2950 /****************************************************************************/
2951 /* */
2952 /* GetOutlineLen : */
2953 /* */
2954 /* Used to compute the size of an outline once it is converted to */
2955 /* OS/2's specific format. The translation is performed by the later */
2956 /* function called simply "GetOultine". */
2957 /* */
GetOutlineLen(TT_Outline * ol)2958 static int GetOutlineLen(TT_Outline *ol)
2959 {
2960 int index; /* current point's index */
2961 BOOL on_curve; /* current point's state */
2962 int i, start = 0;
2963 int first, last;
2964 ULONG cb = 0;
2965
2966 /* loop thru all contours in a glyph */
2967 for ( i = 0; i < ol->n_contours; i++ ) {
2968
2969 cb += sizeof(POLYGONHEADER);
2970
2971 first = start;
2972 last = ol->contours[i];
2973
2974 on_curve = (ol->flags[first] & 1);
2975 index = first;
2976
2977 /* process each contour point individually */
2978 while ( index < last ) {
2979 index++;
2980
2981 if ( on_curve ) {
2982 /* the previous point was on the curve */
2983 on_curve = ( ol->flags[index] & 1 );
2984 if ( on_curve ) {
2985 /* two successive on points => emit segment */
2986 cb += sizeof(PRIMLINE);
2987 }
2988 }
2989 else {
2990 /* the previous point was off the curve */
2991 on_curve = ( ol->flags[index] & 1 );
2992 if ( on_curve ) {
2993 /* reaching an `on' point */
2994 cb += sizeof(PRIMSPLINE);
2995 }
2996 else {
2997 /* two successive `off' points => create middle point */
2998 cb += sizeof(PRIMSPLINE);
2999 }
3000 }
3001 }
3002
3003 /* end of contour, close curve cleanly */
3004 if ( ol->flags[first] & 1 )
3005 {
3006 if ( on_curve )
3007 cb += sizeof(PRIMLINE);
3008 else
3009 cb += sizeof(PRIMSPLINE);
3010 }
3011 else
3012 if (!on_curve)
3013 cb += sizeof(PRIMSPLINE);
3014
3015 start = ol->contours[i] + 1;
3016
3017 }
3018 return cb; /* return # bytes used */
3019 }
3020
3021 /****************************************************************************/
3022 /* */
3023 /* a few global variables used in the following functions */
3024 /* */
3025 static ULONG cb = 0, polycb;
3026 static LONG lastX, lastY;
3027 static PBYTE pb;
3028 static POINTFX Q, R;
3029 static POLYGONHEADER hdr = {0, FD_POLYGON_TYPE};
3030 static PRIMLINE line = {FD_PRIM_LINE};
3031 static PRIMSPLINE spline = {FD_PRIM_SPLINE};
3032
3033 /****************************************************************************/
3034 /* */
3035 /* LineFrom : */
3036 /* */
3037 /* add a line segment to the PM outline that GetOultine is currently */
3038 /* building. */
3039 /* */
Line_From(LONG x,LONG y)3040 static void Line_From(LONG x, LONG y) {
3041 line.pte.x = x << 10;
3042 line.pte.y = y << 10;
3043 /* store to output buffer */
3044 memcpy(&(pb[cb]), &line, sizeof(line));
3045 cb += sizeof(PRIMLINE);
3046 polycb += sizeof(PRIMLINE);
3047 }
3048
3049
3050 /****************************************************************************/
3051 /* */
3052 /* BezierFrom : */
3053 /* */
3054 /* add a bezier arc to the PM outline that GetOutline is currently */
3055 /* buidling. The second-order Bezier is trivially converted to its */
3056 /* equivalent third-order form. */
3057 /* */
Bezier_From(LONG x0,LONG y0,LONG x2,LONG y2,LONG x1,LONG y1)3058 static void Bezier_From( LONG x0, LONG y0, LONG x2, LONG y2, LONG x1, LONG y1 ) {
3059 spline.pte[0].x = x0 << 10;
3060 spline.pte[0].y = y0 << 10;
3061 /* convert from second-order to cubic Bezier spline */
3062 Q.x = (x0 + 2 * x1) / 3;
3063 Q.y = (y0 + 2 * y1) / 3;
3064 R.x = (x2 + 2 * x1) / 3;
3065 R.y = (y2 + 2 * y1) / 3;
3066 spline.pte[1].x = Q.x << 10;
3067 spline.pte[1].y = Q.y << 10;
3068 spline.pte[2].x = R.x << 10;
3069 spline.pte[2].y = R.y << 10;
3070 /* store to output buffer */
3071 memcpy(&(pb[cb]), &spline, sizeof(spline));
3072 cb += sizeof(PRIMSPLINE);
3073 polycb += sizeof(PRIMSPLINE);
3074 }
3075
3076
3077 /****************************************************************************/
3078 /* */
3079 /* GetOutline : */
3080 /* */
3081 /* Translate a FreeType glyph outline into PM format. The buffer is */
3082 /* expected to be of the size returned by a previous call to the */
3083 /* function GetOutlineLen(). */
3084 /* */
3085 /* This code is taken right from the FreeType ttraster.c source, and */
3086 /* subsequently modified to emit PM segments and arcs. */
3087 /* */
GetOutline(TT_Outline * ol,PBYTE pbuf)3088 static int GetOutline(TT_Outline *ol, PBYTE pbuf) {
3089 LONG x, y; /* current point */
3090 LONG cx, cy; /* current Bezier control point */
3091 LONG mx, my; /* current middle point */
3092 LONG x_first, y_first; /* first point's coordinates */
3093 LONG x_last, y_last; /* last point's coordinates */
3094
3095 int index; /* current point's index */
3096 BOOL on_curve; /* current point's state */
3097 int i, start = 0;
3098 int first, last;
3099 ULONG polystart;
3100
3101 pb = pbuf;
3102 cb = 0;
3103
3104 /* loop thru all contours in a glyph */
3105 for ( i = 0; i < ol->n_contours; i++ ) {
3106
3107 polystart = cb; /* save this polygon's start offset */
3108 polycb = sizeof(POLYGONHEADER); /* size of this polygon */
3109 cb += sizeof(POLYGONHEADER);
3110
3111 first = start;
3112 last = ol->contours[i];
3113
3114 x_first = ol->points[first].x;
3115 y_first = ol->points[first].y;
3116
3117 x_last = ol->points[last].x;
3118 y_last = ol->points[last].y;
3119
3120 lastX = cx = x_first;
3121 lastY = cy = y_first;
3122
3123 on_curve = (ol->flags[first] & 1);
3124 index = first;
3125
3126 /* check first point to determine origin */
3127 if ( !on_curve ) {
3128 /* first point is off the curve. Yes, this happens... */
3129 if ( ol->flags[last] & 1 ) {
3130 lastX = x_last; /* start at last point if it */
3131 lastY = y_last; /* is on the curve */
3132 }
3133 else {
3134 /* if both first and last points are off the curve, */
3135 /* start at their middle and record its position */
3136 /* for closure */
3137 lastX = (lastX + x_last)/2;
3138 lastY = (lastY + y_last)/2;
3139
3140 x_last = lastX;
3141 y_last = lastY;
3142 }
3143 }
3144
3145 /* now process each contour point individually */
3146 while ( index < last ) {
3147 index++;
3148 x = ( ol->points[index].x );
3149 y = ( ol->points[index].y );
3150
3151 if ( on_curve ) {
3152 /* the previous point was on the curve */
3153 on_curve = ( ol->flags[index] & 1 );
3154 if ( on_curve ) {
3155 /* two successive on points => emit segment */
3156 Line_From( lastX, lastY ); /*x, y*/
3157 lastX = x;
3158 lastY = y;
3159 }
3160 else {
3161 /* else, keep current control point for next bezier */
3162 cx = x;
3163 cy = y;
3164 }
3165 }
3166 else {
3167 /* the previous point was off the curve */
3168 on_curve = ( ol->flags[index] & 1 );
3169 if ( on_curve ) {
3170 /* reaching an `on' point */
3171 Bezier_From(lastX, lastY, x, y, cx, cy );
3172 lastX = x;
3173 lastY = y;
3174 }
3175 else {
3176 /* two successive `off' points => create middle point */
3177 mx = (cx + x) / 2;
3178 my = (cy + y)/2;
3179
3180 Bezier_From( lastX, lastY, mx, my, cx, cy );
3181 lastX = mx;
3182 lastY = my;
3183
3184 cx = x;
3185 cy = y;
3186 }
3187 }
3188 }
3189
3190 /* end of contour, close curve cleanly */
3191 if ( ol->flags[first] & 1 ) {
3192 if ( on_curve )
3193 Line_From( lastX, lastY); /* x_first, y_first );*/
3194 else
3195 Bezier_From( lastX, lastY, x_first, y_first, cx, cy );
3196 }
3197 else
3198 if (!on_curve)
3199 Bezier_From( lastX, lastY, x_last, y_last, cx, cy );
3200
3201 start = ol->contours[i] + 1;
3202
3203 hdr.cb = polycb;
3204 memcpy(&(pb[polystart]), &hdr, sizeof(hdr));
3205
3206 }
3207 return cb; /* return # bytes used */
3208 }
3209
3210