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