*** empty log message ***
[m17n/libotf.git] / src / otfdrive.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 #include "otf.h"
6 #include "otferror.h"
7
8 #define GSTRING_DELETE(gstring, from, len)                              \
9   do {                                                                  \
10     memmove (gstring->glyphs + from, gstring->glyphs + from + len,      \
11              sizeof (OTF_Glyph) * (gstring->used - from - len));        \
12     gstring->used -= len;                                               \
13   } while (0)
14
15
16 #define GSTRING_INSERT(gstring, pos, len)                               \
17   do {                                                                  \
18     if (gstring->used + len > gstring->size)                            \
19       {                                                                 \
20         char *errfmt = "GSTRING%s";                                     \
21                                                                         \
22         gstring->size = gstring->used + len;                            \
23         gstring->glyphs = (OTF_Glyph *) realloc (gstring->glyphs,       \
24                                                  gstring->size);        \
25         if (! gstring->glyphs)                                          \
26           OTF_ERROR (OTF_ERROR_MEMORY, "");                             \
27       }                                                                 \
28     memmove (gstring->glyphs + pos + len, gstring->glyphs + pos,        \
29              sizeof (OTF_Glyph) * (gstring->used - pos));               \
30     gstring->used += len;                                               \
31   } while (0)
32
33
34 static int
35 gstring_subst (OTF_GlyphString *gstring, int from, int to,
36                OTF_GlyphID *ids, int num)
37 {
38   int errret = -1;
39   int len = to - from;
40   int i;
41
42   if (len < num)
43     GSTRING_INSERT (gstring, from, (num - len));
44   else if (len > num)
45     GSTRING_DELETE (gstring, from, (len - num));
46   for (i = 0; i < num; i++)
47     gstring->glyphs[from + i].glyph_id = ids[i];
48   return 0;
49 }
50
51 \f
52 static int
53 get_coverage_index (OTF_Coverage *coverage, OTF_GlyphID id)
54 {
55   int i;
56
57   if (coverage->CoverageFormat == 1)
58     {
59       for (i = 0; i < coverage->Count; i++)
60         if (coverage->table.GlyphArray[i] == id)
61           return i;
62     }
63   else
64     {
65       for (i = 0; i < coverage->Count; i++)
66         if (coverage->table.RangeRecord[i].Start <= id
67             && coverage->table.RangeRecord[i].End >= id)
68           return (coverage->table.RangeRecord[i].StartCoverageIndex
69                   + (id - coverage->table.RangeRecord[i].Start));
70     }
71   return -1;
72 }
73
74 static unsigned
75 get_class_def (OTF_ClassDef *class_def, OTF_GlyphID glyph_id)
76 {
77   if (class_def->ClassFormat == 1)
78     {
79       int idx = (int) glyph_id - (int) class_def->f.f1.StartGlyph;
80
81       if (idx >= 0 && idx < class_def->f.f1.GlyphCount)
82         return class_def->f.f1.ClassValueArray[idx];
83     }
84   else
85     {
86       int i;
87
88       for (i = 0; i < class_def->f.f2.ClassRangeCount; i++)
89         if (glyph_id >= class_def->f.f2.ClassRangeRecord[i].Start
90             && glyph_id >= class_def->f.f2.ClassRangeRecord[i].End)
91           return class_def->f.f2.ClassRangeRecord[i].Class;
92     }
93   return 0;
94 }
95
96 static OTF_LangSys *
97 get_langsys (OTF_ScriptList *script_list, char *script, char *language)
98 {
99
100   OTF_Tag script_tag = otf_tag (script);
101   OTF_Tag langsys_tag = otf_tag (language);
102   int i, j;
103
104   for (i = 0; i < script_list->ScriptCount; i++)
105     if (script_list->Script[i].ScriptTag == script_tag)
106       {
107         OTF_Script *script = script_list->Script + i;
108
109         if (! langsys_tag)
110           return &script->DefaultLangSys;
111         for (j = 0; j < script->LangSysCount; j++)
112           if (script->LangSysRecord[j].LangSysTag == langsys_tag)
113             return script->LangSys + j;
114         return &script->DefaultLangSys; 
115       }
116
117   return NULL;
118 }
119
120 static int
121 get_feature_index (OTF_LangSys *LangSys, OTF_FeatureList *FeatureList,
122                    char *features, int *feature_index)
123 {
124   int nfeatures = 0;
125
126   if (features)
127     {
128       char *p0, *p1;
129       int len = strlen (features) + 1;
130
131       p0 = alloca (len);
132       for (p1 = p0; *p1; p1++)
133         if (*p1 == ':')
134           *p1 = '\0';
135       
136       while (len > 0)
137         {
138           int this_len = strlen (p0) + 1;
139           OTF_Tag tag = otf_tag (p0);
140
141           if (tag)
142             {
143               int i;
144
145               for (i = 0; i < FeatureList->FeatureCount; i++)
146                 if (tag == FeatureList->Feature[i].FeatureTag)
147                   {
148                     feature_index[nfeatures++] = i;
149                     if (nfeatures == FeatureList->FeatureCount)
150                       break;
151                   }
152             }
153           p0 += this_len;
154           len -= this_len;
155         }
156     }
157   else
158     {
159       for (; nfeatures < LangSys->FeatureCount; nfeatures++)
160         feature_index[nfeatures] = LangSys->FeatureIndex[nfeatures];
161     }
162
163   return nfeatures;
164 }
165
166 static int
167 lookup_gsub (OTF_LookupList *lookup_list, unsigned lookup_list_index,
168              OTF_GlyphString *gstring, int gidx)
169 {
170   char *errfmt = "GSUB Looking up%s";
171   int errret = -1;
172   OTF_Lookup *lookup = lookup_list->Lookup + lookup_list_index;
173   unsigned int flag = lookup->LookupFlag;
174   int orig_gidx = gidx;
175   OTF_Glyph *g = gstring->glyphs + gidx;
176   int i;
177
178   if (! g->glyph_id
179       || (g->GlyphClass
180           && (flag & (1 << g->GlyphClass))))
181     return (gidx + 1);
182
183   /* Try all subtables until one of them handles the current glyph.  */
184   for (i = 0; i < lookup->SubTableCount && gidx == orig_gidx; i++)
185     {
186       OTF_LookupSubTableGSUB *subtable = lookup->SubTable.gsub + i;
187       int coverage_idx;
188
189       if (subtable->Coverage.offset)
190         {
191           coverage_idx = get_coverage_index (&subtable->Coverage,
192                                              g->glyph_id);
193           if (coverage_idx < 0)
194             continue;
195         }
196
197       switch (lookup->LookupType)
198         {
199         case 1:
200           if (subtable->Format == 1)
201             g->glyph_id += subtable->u.single1.DeltaGlyphID;
202           else
203             g->glyph_id = subtable->u.single2.Substitute[coverage_idx];
204           gidx++;
205           break;
206
207         case 2:
208           {
209             OTF_GSUB_Multiple1 *multiple1 = &subtable->u.multiple1;
210             OTF_Sequence *seq = multiple1->Sequence + coverage_idx;
211
212             gstring_subst (gstring, gidx, gidx + 1,
213                            seq->Substitute, seq->GlyphCount);
214             gidx += seq->GlyphCount;
215           }
216           break;
217
218         case 3:
219           OTF_ERROR (OTF_ERROR_GSUB_DRIVE, " (LookupType not yet supported)");
220
221         case 4:
222           if (subtable->Format == 1)
223             {
224               OTF_GSUB_Ligature1 *lig1 = &subtable->u.ligature1;
225               OTF_LigatureSet *ligset = lig1->LigatureSet + coverage_idx;
226               int j;
227
228               for (j = 0; j < ligset->LigatureCount; j++)
229                 {
230                   OTF_Ligature *lig = ligset->Ligature + j;
231                   int k;
232
233                   if (gstring->used - gidx < lig->CompCount)
234                     continue;
235                   for (k = 1; k < lig->CompCount; k++)
236                     if (gstring->glyphs[gidx + k].glyph_id
237                         != lig->Component[k - 1])
238                       break;
239                   if (k < lig->CompCount)
240                     continue;
241                   gstring_subst (gstring, gidx, gidx + lig->CompCount,
242                                  &lig->LigGlyph, 1);
243                   gidx++;
244                   break;
245                 }
246             }
247           else
248             OTF_ERROR (OTF_ERROR_GSUB_DRIVE, " (invalid SubFormat)");
249           break;
250               
251         case 6:
252           if (subtable->Format == 1)
253             OTF_ERROR (OTF_ERROR_GSUB_DRIVE, " (not yet supported)");
254           else if (subtable->Format == 2)
255             OTF_ERROR (OTF_ERROR_GSUB_DRIVE, " (not yet supported)");
256           else
257             {
258               OTF_GSUB_ChainContext3 *context3
259                 = &subtable->u.chain_context3;
260               int back_gidx = gidx - context3->BacktrackGlyphCount;
261               int fore_gidx = gidx + context3->InputGlyphCount;
262               int orig_used;
263               int j;
264
265               if (back_gidx < 0
266                   || fore_gidx + context3->LookaheadGlyphCount > gstring->used)
267                 break;
268
269               for (j = 0; j < context3->BacktrackGlyphCount; j++)
270                 if (get_coverage_index (context3->Backtrack + j,
271                                         gstring->glyphs[back_gidx + j].glyph_id)
272                     < 0)
273                   break;
274               /* Start from the secoding coverage_idx because the
275                  first one is the same as subtable->Coverage and thus
276                  already tested */
277               for (j = 1; j < context3->InputGlyphCount; j++)
278                 if (get_coverage_index (context3->Input + j - 1,
279                                         gstring->glyphs[gidx + j].glyph_id)
280                     < 0)
281                   break;
282               for (j = 0; j < context3->LookaheadGlyphCount; j++)
283                 if (get_coverage_index (context3->LookAhead + j,
284                                         gstring->glyphs[fore_gidx + j].glyph_id)
285                     < 0)
286                   break;
287
288               orig_used = gstring->used;
289               for (j = 0; j < context3->SubstCount; j++)
290                 lookup_gsub (lookup_list,
291                              context3->SubstLookupRecord[j].LookupListIndex,
292                              gstring,
293                              gidx + context3->SubstLookupRecord[j].SequenceIndex);
294               gidx += context3->InputGlyphCount + (gstring->used - orig_used);
295             }
296           break;
297
298         default:
299           continue;
300         }
301     }
302   if (gidx == orig_gidx)
303     {
304       //printf ("not applied\n");
305       gidx++;
306     }
307   else
308     {
309       // printf ("done\n");
310     }
311   return gidx;
312 }
313
314 \f
315
316 /* GPOS */
317 unsigned
318 get_anchor (OTF_Anchor *anchor, OTF_ValueRecord *rec)
319 {
320   unsigned value_format = OTF_XPlacement | OTF_YPlacement;
321
322   rec->XPlacement = anchor->XCoordinate;
323   rec->YPlacement = anchor->YCoordinate;
324   if (anchor->AnchorFormat == 1)
325     /* Nothing to do */
326     ;
327   else if (anchor->AnchorFormat == 2)
328     /* Not yet implemented */
329     ;
330   else if (anchor->AnchorFormat == 3)
331     /* Not yet implemented */
332     ;
333   return value_format;
334 }
335
336
337 static int
338 lookup_gpos (OTF_LookupList *lookup_list, unsigned lookup_list_index,
339              OTF_GlyphString *gstring, int gidx)
340 {
341   char *errfmt = "GPOS Looking up%s";
342   int errret = -1;
343   OTF_Lookup *lookup = lookup_list->Lookup + lookup_list_index;
344   unsigned int flag = lookup->LookupFlag;
345   int orig_gidx = gidx;
346   OTF_Glyph *g = gstring->glyphs + gidx;
347   int i;
348
349   if (! g->glyph_id
350       || (g->GlyphClass
351           && (flag & (1 << g->GlyphClass))))
352     return (gidx + 1);
353
354   /* Try all subtables until one of them handles the current glyph.  */
355   for (i = 0; i < lookup->SubTableCount && gidx == orig_gidx; i++)
356     {
357       OTF_LookupSubTableGPOS *subtable = lookup->SubTable.gpos + i;
358       int coverage_idx;
359
360       // printf ("subtype:%d ", subtable->Format);
361       if (subtable->Coverage.offset)
362         {
363           coverage_idx = get_coverage_index (&subtable->Coverage,
364                                              g->glyph_id);
365           if (coverage_idx < 0)
366             {
367               // printf ("not covererd ");
368               continue;
369             }
370         }
371
372       switch (lookup->LookupType)
373         {
374         case 1:
375           OTF_ERROR (OTF_ERROR_GPOS_DRIVE, " (not yet supported)");
376
377         case 2:
378           if (gidx + 1 >= gstring->used)
379             continue;
380           if (subtable->Format == 1)
381             OTF_ERROR (OTF_ERROR_GPOS_DRIVE, " (not yet supported)");
382           else if (subtable->Format == 2)
383             {
384               OTF_GPOS_Pair2 *pair2 = &subtable->u.pair2;
385               unsigned class1, class2;
386
387               printf ("GPOS 2-2: c:0x%x g:0x%x\n", g->c, g->glyph_id);
388               gidx++;
389               class1 = get_class_def (&pair2->ClassDef1, g->glyph_id);
390               class2 = get_class_def (&pair2->ClassDef2, g[1].glyph_id);
391               g->positioning_type = lookup->LookupType;
392               g->f.f2.format = pair2->ValueFormat1;
393               g->f.f2.value
394                 = &pair2->Class1Record[class1].Class2Record[class2].Value1;
395               if (pair2->ValueFormat2)
396                 {
397                   g++, gidx++;
398                   g->positioning_type = lookup->LookupType;
399                   g->f.f2.format = pair2->ValueFormat2;
400                   g->f.f2.value
401                     = &pair2->Class1Record[class1].Class2Record[class2].Value2;
402                 }
403             }
404           break;
405
406         case 3:
407           OTF_ERROR (OTF_ERROR_GPOS_DRIVE, " (not yet supported)");
408
409         case 4:
410           if (gidx < 1)
411             continue;
412           if (subtable->Format == 1)
413             {
414               OTF_GPOS_MarkBase1 *mark_base1 = &subtable->u.mark_base1;
415               OTF_MarkRecord *mark_record;
416               OTF_BaseRecord *base_record;
417               OTF_Anchor *anchor1, *anchor2;
418               int coverage_idx_base
419                 = get_coverage_index (&mark_base1->BaseCoverage,
420                                       g[-1].glyph_id);
421
422               if (coverage_idx_base < 0)
423                 continue;
424               printf ("GPOS 4-1: c:0x%x g:0x%x\n", g->c, g->glyph_id);
425               mark_record = mark_base1->MarkArray.MarkRecord + coverage_idx;
426               base_record
427                 = mark_base1->BaseArray.BaseRecord + coverage_idx_base;
428               anchor1 = &mark_record->MarkAnchor;
429               anchor2 = &base_record->BaseAnchor[mark_record->Class];
430               g->positioning_type = lookup->LookupType;
431               g->f.f4.mark_anchor = anchor1;
432               g->f.f4.base_anchor = anchor2;
433               break;
434             }
435           else
436             OTF_ERROR (OTF_ERROR_GPOS_DRIVE, " (not yet supported)");
437           break;
438               
439         case 6:
440           OTF_ERROR (OTF_ERROR_GPOS_DRIVE, " (not yet supported)");
441           break;
442
443         default:
444           continue;
445         }
446     }
447   if (gidx == orig_gidx)
448     {
449       // printf ("not applied\n");
450       gidx++;
451     }
452   else
453     {
454       // printf ("done\n");
455     }
456   return gidx;
457 }
458
459 static int
460 lookup_cmap (OTF_cmap *cmap, int c)
461 {
462   int i;
463
464   if (! cmap || ! cmap->Unicode)
465     return 0;
466
467   switch (cmap->Unicode->subtable.format)
468     {
469     case 0:
470       break;
471
472     case 4:
473       {
474         OTF_EncodingSubtable4 *sub4 = cmap->Unicode->subtable.f.f4;
475         int segCount = sub4->segCountX2 / 2;
476
477         for (i = 0; i < segCount; i++)
478           if (c <= sub4->segments[i].endCount)
479             break;
480         if (i == segCount || c < sub4->segments[i].startCount)
481           return 0;
482         if (sub4->segments[i].idRangeOffset == 0xFFFF)
483           return c + sub4->segments[i].idDelta;
484         return sub4->glyphIdArray[sub4->segments[i].idRangeOffset
485                                   + (c - sub4->segments[i].startCount)];
486       }
487       break;
488     }
489   return 0;
490 }
491
492 \f
493
494 /* APIs */
495
496 int
497 otf_drive_cmap (OTF *otf, OTF_GlyphString *gstring)
498 {
499   OTF_cmap *cmap;
500   int i;
501
502   if (! otf->cmap
503       && otf_get_table (otf, "cmap") < 0)
504     return -1;
505
506   cmap = otf->cmap;
507   for (i = 0; i < gstring->used; i++)
508       gstring->glyphs[i].glyph_id = lookup_cmap (cmap, gstring->glyphs[i].c);
509
510   return 0;
511 }
512
513
514 int
515 otf_drive_gdef (OTF *otf, OTF_GlyphString *gstring)
516 {
517   OTF_GDEF *gdef;
518   int i;
519
520   if (! otf->gdef
521       && otf_get_table (otf, "GDEF") < 0)
522     return -1;
523   gdef = otf->gdef;
524
525   if (gdef->glyph_class_def.offset)
526     for (i = 0; i < gstring->used; i++)
527       gstring->glyphs[i].GlyphClass
528         = get_class_def (&gdef->glyph_class_def,
529                          gstring->glyphs[i].glyph_id);
530
531   if (gdef->mark_attach_class_def.offset)
532     for (i = 0; i < gstring->used; i++)
533       gstring->glyphs[i].MarkAttachClass
534         = get_class_def (&gdef->mark_attach_class_def,
535                          gstring->glyphs[i].glyph_id);
536
537   return 0;
538 }
539
540
541 int
542 otf_drive_gsub (OTF *otf, OTF_GlyphString *gstring,
543                 char *script, char *language, char *features)
544 {
545   char *errfmt = "GSUB driving%s";
546   int errret = -1;
547   OTF_GSUB *gsub;
548   OTF_LangSys *LangSys;
549   int nfeatures;
550   int *feature_index;
551   int i, j;
552
553   if (! otf->gsub
554       && otf_get_table (otf, "GSUB") < 0)
555     return -1;
556   gsub = otf->gsub;
557
558   LangSys = get_langsys (&gsub->ScriptList, script, language);
559   if (! LangSys)
560     return -1;
561
562   feature_index = alloca (sizeof (int) * gsub->FeatureList.FeatureCount);
563   if (! feature_index)
564     OTF_ERROR (OTF_ERROR_MEMORY, " feature list");
565
566   nfeatures = get_feature_index (LangSys, &gsub->FeatureList,
567                                  features, feature_index);
568   if (nfeatures == 0)
569     OTF_ERROR (OTF_ERROR_GSUB_DRIVE, " no feature");
570
571   for (i = 0; i < nfeatures; i++)
572     {
573       OTF_Feature *feature = gsub->FeatureList.Feature + feature_index[i];
574
575       for (j = 0; j < feature->LookupCount; j++)
576         {
577           int gidx = 0;
578
579           while (gidx < gstring->used)
580             gidx = lookup_gsub (&gsub->LookupList, feature->LookupListIndex[j],
581                                 gstring, gidx);
582         }
583     }
584
585   return 0;
586 }
587
588 int
589 otf_drive_gpos (OTF *otf, OTF_GlyphString *gstring,
590                 char *script, char *language, char *features)
591 {
592   char *errfmt = "GPOS driving%s";
593   int errret = -1;
594   OTF_GPOS *gpos;
595   OTF_LangSys *LangSys;
596   int nfeatures;
597   int *feature_index;
598   int i, j;
599
600   if (! otf->gpos
601       && otf_get_table (otf, "GPOS") < 0)
602     return -1;
603   gpos = otf->gpos;
604
605   LangSys = get_langsys (&gpos->ScriptList, script, language);
606   if (! LangSys)
607     return -1;
608
609   feature_index = alloca (sizeof (int) * gpos->FeatureList.FeatureCount);
610   if (! feature_index)
611     OTF_ERROR (OTF_ERROR_MEMORY, " feature list");
612
613   nfeatures = get_feature_index (LangSys, &gpos->FeatureList,
614                                  features, feature_index);
615   if (nfeatures == 0)
616     OTF_ERROR (OTF_ERROR_GSUB_DRIVE, " no feature");
617
618   for (i = 0; i < nfeatures; i++)
619     {
620       OTF_Feature *feature = gpos->FeatureList.Feature + feature_index[i];
621
622       for (j = 0; j < feature->LookupCount; j++)
623         {
624           int gidx = 0;
625
626           while (gidx < gstring->used)
627             gidx = lookup_gpos (&gpos->LookupList, feature->LookupListIndex[j],
628                                 gstring, gidx);
629         }
630     }
631
632   return 0;
633 }
634
635 int
636 otf_drive_tables (OTF *otf, OTF_GlyphString *gstring,
637                   char *script, char *language)
638 {
639   if (otf_drive_cmap (otf, gstring) < 0)
640     return -1;
641   if (otf_drive_gdef (otf, gstring) < 0)
642     return -1;
643   if (otf_drive_gsub (otf, gstring, script, language, NULL) < 0)
644     return -1;
645   if (otf_drive_gpos (otf, gstring, script, language, NULL) < 0)
646     return -1;
647   return 0;
648 }