1 /*
2  * Copyright (C) 2000, 2007  Red Hat, Inc.
3  *
4  * This is part of HarfBuzz, an OpenType Layout engine library.
5  *
6  * Permission is hereby granted, without written agreement and without
7  * license or royalty fees, to use, copy, modify, and distribute this
8  * software and its documentation for any purpose, provided that the
9  * above copyright notice and the following two paragraphs appear in
10  * all copies of this software.
11  *
12  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16  * DAMAGE.
17  *
18  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23  *
24  * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
25  */
26 
27 #include "harfbuzz-impl.h"
28 #include "harfbuzz-dump.h"
29 #include "harfbuzz-gdef-private.h"
30 #include "harfbuzz-gsub-private.h"
31 #include "harfbuzz-gpos-private.h"
32 #include "harfbuzz-open-private.h"
33 #include <stdarg.h>
34 
35 #define DUMP(format) dump (stream, indent, format)
36 #define DUMP1(format, arg1) dump (stream, indent, format, arg1)
37 #define DUMP2(format, arg1, arg2) dump (stream, indent, format, arg1, arg2)
38 #define DUMP3(format, arg1, arg2, arg3) dump (stream, indent, format, arg1, arg2, arg3)
39 
40 #define DUMP_FINT(strct,fld) dump (stream, indent, "<" #fld ">%d</" #fld ">\n", (strct)->fld)
41 #define DUMP_FUINT(strct,fld) dump (stream, indent, "<" #fld ">%u</" #fld ">\n", (strct)->fld)
42 #define DUMP_FGLYPH(strct,fld) dump (stream, indent, "<" #fld ">%#06x</" #fld ">\n", (strct)->fld)
43 #define DUMP_FGLYPH(strct,fld) dump (stream, indent, "<" #fld ">%#06x</" #fld ">\n", (strct)->fld)
44 #define DUMP_USHORT_ARRAY(strct,fld,cnt) Dump_UShort_Array ((strct)->fld, cnt, #fld, stream, indent);
45 
46 #define DEF_DUMP(type) static void Dump_ ## type (HB_ ## type *type, FILE *stream, int indent, HB_Type hb_type)
47 #define RECURSE(name, type, val) do {  DUMP ("<" #name ">\n"); Dump_ ## type (val, stream, indent + 1, hb_type); DUMP ("</" #name ">\n"); } while (0)
48 #define RECURSE_NUM(name, i, type, val) do {  DUMP1 ("<" #name "> <!-- %d -->\n", i); Dump_ ## type (val, stream, indent + 1, hb_type); DUMP ("</" #name ">\n"); } while (0)
49 #define DUMP_VALUE_RECORD(val, frmt) do {  DUMP ("<ValueRecord>\n"); Dump_ValueRecord (val, stream, indent + 1, hb_type, frmt); DUMP ("</ValueRecord>\n"); } while (0)
50 
51 static void
do_indent(FILE * stream,int indent)52 do_indent (FILE *stream, int indent)
53 {
54   fprintf (stream, "%*s", indent * 3, "");
55 }
56 
57 static void
dump(FILE * stream,int indent,const char * format,...)58 dump (FILE *stream, int indent, const char *format, ...)
59 {
60   va_list list;
61 
62   do_indent (stream, indent);
63 
64   va_start (list, format);
65   vfprintf (stream, format, list);
66   va_end (list);
67 }
68 
69 static void
Dump_UShort_Array(HB_UShort * array,int count,const char * name,FILE * stream,int indent)70 Dump_UShort_Array (HB_UShort *array, int count, const char *name, FILE *stream, int indent)
71 {
72   int i;
73 
74   do_indent (stream, indent);
75 
76   fprintf (stream, "<%s>", name);
77   for (i = 0; i < count; i++)
78     fprintf (stream, "%d%s", array[i], i == 0 ? "" : " ");
79   fprintf (stream, "</%s>\n", name);
80 }
81 
82 static void
Print_Tag(HB_UInt tag,FILE * stream)83 Print_Tag (HB_UInt tag, FILE *stream)
84 {
85   fprintf (stream, "%c%c%c%c",
86 	   (unsigned char)(tag >> 24),
87 	   (unsigned char)((tag >> 16) & 0xff),
88 	   (unsigned char)((tag >> 8) & 0xff),
89 	   (unsigned char)(tag & 0xff));
90 }
91 
DEF_DUMP(LangSys)92 DEF_DUMP (LangSys)
93 {
94   int i;
95 
96   HB_UNUSED(hb_type);
97 
98   DUMP_FUINT (LangSys, LookupOrderOffset);
99   DUMP_FUINT (LangSys, ReqFeatureIndex);
100   DUMP_FUINT (LangSys, FeatureCount);
101 
102   for (i=0; i < LangSys->FeatureCount; i++)
103     DUMP1("<FeatureIndex>%d</FeatureIndex>\n", LangSys->FeatureIndex[i]);
104 }
105 
DEF_DUMP(ScriptTable)106 DEF_DUMP (ScriptTable)
107 {
108   int i;
109 
110   RECURSE (DefaultLangSys, LangSys, &ScriptTable->DefaultLangSys);
111 
112   DUMP_FUINT (ScriptTable, LangSysCount);
113 
114   for (i=0; i < ScriptTable->LangSysCount; i++)
115     {
116       do_indent (stream, indent);
117       fprintf (stream, "<LangSysTag>");
118       Print_Tag (ScriptTable->LangSysRecord[i].LangSysTag, stream);
119       fprintf (stream, "</LangSysTag>\n");
120       RECURSE_NUM (LangSys, i, LangSys, &ScriptTable->LangSysRecord[i].LangSys);
121     }
122 }
123 
DEF_DUMP(ScriptList)124 DEF_DUMP (ScriptList)
125 {
126   int i;
127 
128   DUMP_FUINT (ScriptList, ScriptCount);
129 
130   for (i=0; i < ScriptList->ScriptCount; i++)
131     {
132       do_indent (stream, indent);
133       fprintf (stream, "<ScriptTag>");
134       Print_Tag (ScriptList->ScriptRecord[i].ScriptTag, stream);
135       fprintf (stream, "</ScriptTag>\n");
136       RECURSE_NUM (Script, i, ScriptTable, &ScriptList->ScriptRecord[i].Script);
137     }
138 }
139 
DEF_DUMP(Feature)140 DEF_DUMP (Feature)
141 {
142   int i;
143 
144   HB_UNUSED(hb_type);
145 
146   DUMP_FUINT (Feature, FeatureParams);
147   DUMP_FUINT (Feature, LookupListCount);
148 
149   for (i=0; i < Feature->LookupListCount; i++)
150     DUMP1("<LookupIndex>%d</LookupIndex>\n", Feature->LookupListIndex[i]);
151 }
152 
DEF_DUMP(MarkRecord)153 DEF_DUMP (MarkRecord)
154 {
155   HB_UNUSED(hb_type);
156 
157   DUMP_FUINT (MarkRecord, Class);
158   DUMP1("<Anchor>%d</Anchor>\n", MarkRecord->MarkAnchor.PosFormat );
159 }
160 
DEF_DUMP(MarkArray)161 DEF_DUMP (MarkArray)
162 {
163   int i;
164 
165   DUMP_FUINT (MarkArray, MarkCount);
166 
167   for (i=0; i < MarkArray->MarkCount; i++)
168     RECURSE_NUM (MarkRecord, i, MarkRecord, &MarkArray->MarkRecord[i]);
169 }
170 
DEF_DUMP(FeatureList)171 DEF_DUMP (FeatureList)
172 {
173   int i;
174 
175   DUMP_FUINT (FeatureList, FeatureCount);
176 
177   for (i=0; i < FeatureList->FeatureCount; i++)
178     {
179       do_indent (stream, indent);
180       fprintf (stream, "<FeatureTag>");
181       Print_Tag (FeatureList->FeatureRecord[i].FeatureTag, stream);
182       fprintf (stream, "</FeatureTag> <!-- %d -->\n", i);
183       RECURSE_NUM (Feature, i, Feature, &FeatureList->FeatureRecord[i].Feature);
184     }
185 }
186 
DEF_DUMP(Coverage)187 DEF_DUMP (Coverage)
188 {
189   HB_UNUSED(hb_type);
190 
191   DUMP_FUINT (Coverage, CoverageFormat);
192 
193   if (Coverage->CoverageFormat == 1)
194     {
195       int i;
196       DUMP_FUINT (&Coverage->cf.cf1, GlyphCount);
197 
198       for (i = 0; i < Coverage->cf.cf1.GlyphCount; i++)
199 	DUMP2("<Glyph>%#06x</Glyph> <!-- %d -->\n",
200 	      Coverage->cf.cf1.GlyphArray[i], i);
201     }
202   else
203     {
204       int i;
205       DUMP_FUINT (&Coverage->cf.cf2, RangeCount);
206 
207       for ( i = 0; i < Coverage->cf.cf2.RangeCount; i++ )
208 	  DUMP3("<Glyph>%#06x - %#06x</Glyph> <!-- %d -->\n",
209 	        Coverage->cf.cf2.RangeRecord[i].Start,
210 	        Coverage->cf.cf2.RangeRecord[i].End, i);
211     }
212 }
213 
DEF_DUMP(ClassRangeRecord)214 DEF_DUMP (ClassRangeRecord)
215 {
216   HB_UNUSED(hb_type);
217 
218   DUMP_FGLYPH (ClassRangeRecord, Start);
219   DUMP_FGLYPH (ClassRangeRecord, End);
220   DUMP_FUINT (ClassRangeRecord, Class);
221 }
222 
DEF_DUMP(ClassDefinition)223 DEF_DUMP (ClassDefinition)
224 {
225   HB_UNUSED(hb_type);
226 
227   DUMP_FUINT( ClassDefinition, ClassFormat);
228   DUMP_FUINT( ClassDefinition, loaded);
229 
230   if (ClassDefinition->ClassFormat == 1)
231     {
232       int i;
233       HB_ClassDefFormat1 *ClassDefFormat1 = &ClassDefinition->cd.cd1;
234       DUMP("<ClassDefinition>\n");
235       DUMP_FUINT (ClassDefFormat1, StartGlyph );
236       DUMP_FUINT (ClassDefFormat1, GlyphCount );
237       for (i = 0; i < ClassDefFormat1->GlyphCount; i++)
238 	DUMP2(" <Class>%d</Class> <!-- %#06x -->", ClassDefFormat1->ClassValueArray[i],
239 	      ClassDefFormat1->StartGlyph+i );
240     }
241   else if (ClassDefinition->ClassFormat == 2)
242     {
243       int i;
244       HB_ClassDefFormat2 *ClassDefFormat2 = &ClassDefinition->cd.cd2;
245       DUMP_FUINT (ClassDefFormat2, ClassRangeCount);
246 
247       for (i = 0; i < ClassDefFormat2->ClassRangeCount; i++)
248 	RECURSE_NUM (ClassRangeRecord, i, ClassRangeRecord, &ClassDefFormat2->ClassRangeRecord[i]);
249     }
250   else
251     fprintf(stderr, "invalid class def table!!!\n");
252 }
253 
DEF_DUMP(SubstLookupRecord)254 DEF_DUMP (SubstLookupRecord)
255 {
256   HB_UNUSED(hb_type);
257 
258   DUMP_FUINT (SubstLookupRecord, SequenceIndex);
259   DUMP_FUINT (SubstLookupRecord, LookupListIndex);
260 }
261 
DEF_DUMP(ChainSubClassRule)262 DEF_DUMP (ChainSubClassRule)
263 {
264   int i;
265 
266   DUMP_USHORT_ARRAY (ChainSubClassRule, Backtrack, ChainSubClassRule->BacktrackGlyphCount);
267   DUMP_USHORT_ARRAY (ChainSubClassRule, Input, ChainSubClassRule->InputGlyphCount - 1);
268   DUMP_USHORT_ARRAY (ChainSubClassRule, Lookahead, ChainSubClassRule->LookaheadGlyphCount);
269 
270   for (i = 0; i < ChainSubClassRule->SubstCount; i++)
271     RECURSE_NUM (SubstLookupRecord, i, SubstLookupRecord, &ChainSubClassRule->SubstLookupRecord[i]);
272 
273   indent--;
274 }
275 
DEF_DUMP(ChainSubClassSet)276 DEF_DUMP (ChainSubClassSet)
277 {
278   int i;
279 
280   DUMP_FUINT( ChainSubClassSet, ChainSubClassRuleCount );
281   for (i = 0; i < ChainSubClassSet->ChainSubClassRuleCount; i++)
282     RECURSE_NUM (ChainSubClassRule, i, ChainSubClassRule, &ChainSubClassSet->ChainSubClassRule[i]);
283 }
284 
285 static void
Dump_GSUB_Lookup_Single(HB_SubTable * subtable,FILE * stream,int indent,HB_Type hb_type)286 Dump_GSUB_Lookup_Single (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type)
287 {
288   HB_SingleSubst *SingleSubst = &subtable->st.gsub.single;
289 
290   DUMP_FUINT (SingleSubst, SubstFormat);
291   RECURSE (Coverage, Coverage, &SingleSubst->Coverage);
292 
293   if (SingleSubst->SubstFormat == 1)
294     {
295       DUMP_FINT (&SingleSubst->ssf.ssf1, DeltaGlyphID);
296     }
297   else
298     {
299       int i;
300 
301       DUMP_FINT (&SingleSubst->ssf.ssf2, GlyphCount);
302       for (i=0; i < SingleSubst->ssf.ssf2.GlyphCount; i++)
303 	DUMP2("<Substitute>%#06x</Substitute> <!-- %d -->\n", SingleSubst->ssf.ssf2.Substitute[i], i);
304     }
305 }
306 
DEF_DUMP(Ligature)307 DEF_DUMP (Ligature)
308 {
309   int i;
310 
311   HB_UNUSED(hb_type);
312 
313   DUMP_FGLYPH (Ligature, LigGlyph);
314   DUMP_FUINT (Ligature, ComponentCount);
315 
316   for (i=0; i < Ligature->ComponentCount - 1; i++)
317     DUMP1("<Component>%#06x</Component>\n", Ligature->Component[i]);
318 }
319 
DEF_DUMP(LigatureSet)320 DEF_DUMP (LigatureSet)
321 {
322   int i;
323 
324   DUMP_FUINT (LigatureSet, LigatureCount);
325 
326   for (i=0; i < LigatureSet->LigatureCount; i++)
327     RECURSE_NUM (Ligature, i, Ligature, &LigatureSet->Ligature[i]);
328 }
329 
330 static void
Dump_GSUB_Lookup_Ligature(HB_SubTable * subtable,FILE * stream,int indent,HB_Type hb_type)331 Dump_GSUB_Lookup_Ligature (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type)
332 {
333   int i;
334   HB_LigatureSubst *LigatureSubst = &subtable->st.gsub.ligature;
335 
336   DUMP_FUINT (LigatureSubst, SubstFormat);
337   RECURSE (Coverage, Coverage, &LigatureSubst->Coverage);
338 
339   DUMP_FUINT (LigatureSubst, LigatureSetCount);
340 
341   for (i=0; i < LigatureSubst->LigatureSetCount; i++)
342     RECURSE_NUM (LigatureSet, i, LigatureSet, &LigatureSubst->LigatureSet[i]);
343 }
344 
DEF_DUMP(ContextSubstFormat1)345 DEF_DUMP (ContextSubstFormat1)
346 {
347   HB_UNUSED(hb_type);
348   HB_UNUSED(ContextSubstFormat1);
349 
350 
351   DUMP("<!-- Not implemented!!! -->\n");
352 }
353 
DEF_DUMP(ContextSubstFormat2)354 DEF_DUMP (ContextSubstFormat2)
355 {
356   DUMP_FUINT (ContextSubstFormat2, MaxContextLength);
357   RECURSE (Coverage, Coverage, &ContextSubstFormat2->Coverage);
358   RECURSE (ClassDefinition, ClassDefinition, &ContextSubstFormat2->ClassDef);
359 }
360 
DEF_DUMP(ContextSubstFormat3)361 DEF_DUMP (ContextSubstFormat3)
362 {
363   HB_UNUSED(hb_type);
364   HB_UNUSED(ContextSubstFormat3);
365 
366   DUMP("<!-- Not implemented!!! -->\n");
367 }
368 
369 static void
Dump_GSUB_Lookup_Context(HB_SubTable * subtable,FILE * stream,int indent,HB_Type hb_type)370 Dump_GSUB_Lookup_Context (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type)
371 {
372   HB_ContextSubst *ContextSubst = &subtable->st.gsub.context;
373 
374   DUMP_FUINT (ContextSubst, SubstFormat);
375   switch( ContextSubst->SubstFormat )
376     {
377     case 1:
378       Dump_ContextSubstFormat1 (&ContextSubst->csf.csf1, stream, indent+2, hb_type);
379       break;
380     case 2:
381       Dump_ContextSubstFormat2 (&ContextSubst->csf.csf2, stream, indent+2, hb_type);
382       break;
383     case 3:
384       Dump_ContextSubstFormat3 (&ContextSubst->csf.csf3, stream, indent+2, hb_type);
385       break;
386     default:
387       fprintf(stderr, "invalid subformat!!!!!\n");
388     }
389 }
390 
DEF_DUMP(ChainContextSubstFormat1)391 DEF_DUMP (ChainContextSubstFormat1)
392 {
393   HB_UNUSED(hb_type);
394   HB_UNUSED(ChainContextSubstFormat1);
395 
396   DUMP("<!-- Not implemented!!! -->\n");
397 }
398 
DEF_DUMP(ChainContextSubstFormat2)399 DEF_DUMP (ChainContextSubstFormat2)
400 {
401   int i;
402 
403   RECURSE (Coverage, Coverage, &ChainContextSubstFormat2->Coverage);
404   DUMP_FUINT (ChainContextSubstFormat2, MaxBacktrackLength);
405   RECURSE (ClassDefinition, ClassDefinition, &ChainContextSubstFormat2->BacktrackClassDef);
406   DUMP_FUINT (ChainContextSubstFormat2, MaxInputLength);
407   RECURSE (ClassDefinition, ClassDefinition, &ChainContextSubstFormat2->InputClassDef);
408   DUMP_FUINT (ChainContextSubstFormat2, MaxLookaheadLength);
409   RECURSE (ClassDefinition, ClassDefinition, &ChainContextSubstFormat2->LookaheadClassDef);
410 
411   DUMP_FUINT (ChainContextSubstFormat2, ChainSubClassSetCount);
412   for (i = 0; i < ChainContextSubstFormat2->ChainSubClassSetCount; i++)
413     RECURSE (ChainSubClassSet, ChainSubClassSet, &ChainContextSubstFormat2->ChainSubClassSet[i]);
414 }
415 
DEF_DUMP(ChainContextSubstFormat3)416 DEF_DUMP (ChainContextSubstFormat3)
417 {
418   int i;
419 
420   DUMP_FUINT (ChainContextSubstFormat3, BacktrackGlyphCount);
421   for (i = 0; i < ChainContextSubstFormat3->BacktrackGlyphCount; i++)
422     RECURSE (BacktrackCoverage, Coverage, &ChainContextSubstFormat3->BacktrackCoverage[i]);
423   DUMP_FUINT (ChainContextSubstFormat3, InputGlyphCount);
424   for (i = 0; i < ChainContextSubstFormat3->InputGlyphCount; i++)
425     RECURSE (InputCoverage, Coverage, &ChainContextSubstFormat3->InputCoverage[i]);
426   DUMP_FUINT (ChainContextSubstFormat3, LookaheadGlyphCount);
427   for (i = 0; i < ChainContextSubstFormat3->LookaheadGlyphCount; i++)
428     RECURSE (LookaheadCoverage, Coverage, &ChainContextSubstFormat3->LookaheadCoverage[i]);
429 
430   for (i = 0; i < ChainContextSubstFormat3->SubstCount; i++)
431     RECURSE_NUM (SubstLookupRecord, i, SubstLookupRecord, &ChainContextSubstFormat3->SubstLookupRecord[i]);
432 
433 }
434 
435 static void
Dump_GSUB_Lookup_Chain(HB_SubTable * subtable,FILE * stream,int indent,HB_Type hb_type)436 Dump_GSUB_Lookup_Chain (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type)
437 {
438   HB_ChainContextSubst *chain = &subtable->st.gsub.chain;
439 
440   DUMP_FUINT (chain, SubstFormat);
441   switch (chain->SubstFormat)
442     {
443     case 1:
444       Dump_ChainContextSubstFormat1 (&chain->ccsf.ccsf1, stream, indent+2, hb_type);
445       break;
446     case 2:
447       Dump_ChainContextSubstFormat2 (&chain->ccsf.ccsf2, stream, indent+2, hb_type);
448       break;
449     case 3:
450       Dump_ChainContextSubstFormat3 (&chain->ccsf.ccsf3, stream, indent+2, hb_type);
451       break;
452     default:
453       fprintf(stderr, "invalid subformat!!!!!\n");
454     }
455 }
456 
457 static void
Dump_Device(HB_Device * Device,FILE * stream,int indent,HB_Type hb_type)458 Dump_Device (HB_Device *Device, FILE *stream, int indent, HB_Type hb_type)
459 {
460   int i;
461   int bits;
462   int n_per;
463   unsigned int mask;
464 
465   HB_UNUSED(hb_type);
466 
467   DUMP_FUINT (Device, StartSize);
468   DUMP_FUINT (Device, EndSize);
469   DUMP_FUINT (Device, DeltaFormat);
470   switch (Device->DeltaFormat)
471     {
472     case 1:
473       bits = 2;
474       break;
475     case 2:
476       bits = 4;
477       break;
478     case 3:
479       bits = 8;
480       break;
481     default:
482       bits = 0;
483       break;
484     }
485 
486   DUMP ("<DeltaValue>");
487   if (!bits)
488     {
489 
490       fprintf(stderr, "invalid DeltaFormat!!!!!\n");
491     }
492   else
493     {
494       n_per = 16 / bits;
495       mask = (1 << bits) - 1;
496       mask = mask << (16 - bits);
497 
498       for (i = Device->StartSize; i <= Device->EndSize ; i++)
499 	{
500 	  HB_UShort val = Device->DeltaValue[i / n_per];
501 	  HB_Short signed_val = ((val << ((i % n_per) * bits)) & mask);
502 	  dump (stream, indent, "%d", signed_val >> (16 - bits));
503 	  if (i != Device->EndSize)
504 	    DUMP (", ");
505 	}
506     }
507   DUMP ("</DeltaValue>\n");
508 }
509 
510 static void
Dump_ValueRecord(HB_ValueRecord * ValueRecord,FILE * stream,int indent,HB_Type hb_type,HB_UShort value_format)511 Dump_ValueRecord (HB_ValueRecord *ValueRecord, FILE *stream, int indent, HB_Type hb_type, HB_UShort value_format)
512 {
513   if (value_format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT)
514     DUMP_FINT (ValueRecord, XPlacement);
515   if (value_format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT)
516     DUMP_FINT (ValueRecord, YPlacement);
517   if (value_format & HB_GPOS_FORMAT_HAVE_X_ADVANCE)
518     DUMP_FINT (ValueRecord, XAdvance);
519   if (value_format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE)
520     DUMP_FINT (ValueRecord, XAdvance);
521   if (value_format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT_DEVICE)
522     RECURSE (Device, Device, &*ValueRecord->DeviceTables[VR_X_PLACEMENT_DEVICE]);
523   if (value_format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT_DEVICE)
524     RECURSE (Device, Device, &*ValueRecord->DeviceTables[VR_Y_PLACEMENT_DEVICE]);
525   if (value_format & HB_GPOS_FORMAT_HAVE_X_ADVANCE_DEVICE)
526     RECURSE (Device, Device, &*ValueRecord->DeviceTables[VR_X_ADVANCE_DEVICE]);
527   if (value_format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE_DEVICE)
528     RECURSE (Device, Device, &*ValueRecord->DeviceTables[VR_Y_ADVANCE_DEVICE]);
529 #ifdef HB_SUPPORT_MULTIPLE_MASTER
530   if (value_format & HB_GPOS_FORMAT_HAVE_X_ID_PLACEMENT)
531     DUMP_FUINT (ValueRecord, XIdPlacement);
532   if (value_format & HB_GPOS_FORMAT_HAVE_Y_ID_PLACEMENT)
533     DUMP_FUINT (ValueRecord, YIdPlacement);
534   if (value_format & HB_GPOS_FORMAT_HAVE_X_ID_ADVANCE)
535     DUMP_FUINT (ValueRecord, XIdAdvance);
536   if (value_format & HB_GPOS_FORMAT_HAVE_Y_ID_ADVANCE)
537     DUMP_FUINT (ValueRecord, XIdAdvance);
538 #endif
539 }
540 
541 static void
Dump_GPOS_Lookup_Single(HB_SubTable * subtable,FILE * stream,int indent,HB_Type hb_type)542 Dump_GPOS_Lookup_Single (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type)
543 {
544   HB_SinglePos *SinglePos = &subtable->st.gpos.single;
545 
546   DUMP_FUINT (SinglePos, PosFormat);
547   RECURSE (Coverage, Coverage, &SinglePos->Coverage);
548 
549   DUMP_FUINT (SinglePos, ValueFormat);
550 
551   if (SinglePos->PosFormat == 1)
552     {
553       DUMP_VALUE_RECORD (&SinglePos->spf.spf1.Value, SinglePos->ValueFormat);
554     }
555   else
556     {
557       int i;
558 
559       DUMP_FUINT (&SinglePos->spf.spf2, ValueCount);
560       for (i = 0; i < SinglePos->spf.spf2.ValueCount; i++)
561 	DUMP_VALUE_RECORD (&SinglePos->spf.spf2.Value[i], SinglePos->ValueFormat);
562     }
563 }
564 
565 static void
Dump_PairValueRecord(HB_PairValueRecord * PairValueRecord,FILE * stream,int indent,HB_Type hb_type,HB_UShort ValueFormat1,HB_UShort ValueFormat2)566 Dump_PairValueRecord (HB_PairValueRecord *PairValueRecord, FILE *stream, int indent, HB_Type hb_type, HB_UShort ValueFormat1, HB_UShort ValueFormat2)
567 {
568   DUMP_FUINT (PairValueRecord, SecondGlyph);
569   DUMP_VALUE_RECORD (&PairValueRecord->Value1, ValueFormat1);
570   DUMP_VALUE_RECORD (&PairValueRecord->Value2, ValueFormat2);
571 }
572 
573 static void
Dump_PairSet(HB_PairSet * PairSet,FILE * stream,int indent,HB_Type hb_type,HB_UShort ValueFormat1,HB_UShort ValueFormat2)574 Dump_PairSet (HB_PairSet *PairSet, FILE *stream, int indent, HB_Type hb_type, HB_UShort ValueFormat1, HB_UShort ValueFormat2)
575 {
576   int i;
577   DUMP_FUINT (PairSet, PairValueCount);
578 
579   for (i = 0; i < PairSet->PairValueCount; i++)
580     {
581       DUMP ("<PairValueRecord>\n");
582       Dump_PairValueRecord (&PairSet->PairValueRecord[i], stream, indent + 1, hb_type, ValueFormat1, ValueFormat2);
583       DUMP ("</PairValueRecord>\n");
584     }
585 }
586 
587 static void
Dump_GPOS_Lookup_Pair(HB_SubTable * subtable,FILE * stream,int indent,HB_Type hb_type)588 Dump_GPOS_Lookup_Pair (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type)
589 {
590   HB_PairPos *PairPos = &subtable->st.gpos.pair;
591 
592   DUMP_FUINT (PairPos, PosFormat);
593   RECURSE (Coverage, Coverage, &PairPos->Coverage);
594 
595   DUMP_FUINT (PairPos, ValueFormat1);
596   DUMP_FUINT (PairPos, ValueFormat2);
597 
598   if (PairPos->PosFormat == 1)
599     {
600       int i;
601 
602       DUMP_FUINT (&PairPos->ppf.ppf1, PairSetCount);
603       for (i = 0; i < PairPos->ppf.ppf1.PairSetCount; i++)
604 	{
605 	  DUMP ("<PairSet>\n");
606 	  Dump_PairSet (&PairPos->ppf.ppf1.PairSet[i], stream, indent + 1, hb_type, PairPos->ValueFormat1, PairPos->ValueFormat2);
607 	  DUMP ("</PairSet>\n");
608 	}
609     }
610   else
611     {
612     }
613 }
614 
615 static void
Dump_GPOS_Lookup_Markbase(HB_SubTable * subtable,FILE * stream,int indent,HB_Type hb_type)616 Dump_GPOS_Lookup_Markbase (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type)
617 {
618   int i;
619   HB_MarkBasePos *markbase = &subtable->st.gpos.markbase;
620 
621   DUMP_FUINT (markbase, PosFormat);
622   RECURSE (Coverage, Coverage, &markbase->MarkCoverage);
623   RECURSE (Coverage, Coverage, &markbase->BaseCoverage);
624   DUMP_FUINT (markbase, ClassCount);
625   RECURSE (MarkArray, MarkArray, &markbase->MarkArray);
626 
627   DUMP ("<BaseArray>\n");
628   indent++;
629 
630   DUMP_FUINT (&markbase->BaseArray, BaseCount);
631   for (i = 0; i < markbase->BaseArray.BaseCount; i++)
632     {
633       int j;
634       HB_BaseRecord *r = &markbase->BaseArray.BaseRecord[i];
635       DUMP1 ("<BaseRecord> <!-- %d -->\n",  i);
636       for (j = 0; j < markbase->ClassCount; j++)
637 	DUMP1 ("  <Anchor>%d</Anchor>\n", r->BaseAnchor->PosFormat);
638       DUMP ("<BaseRecord>\n");
639     }
640 
641   indent--;
642   DUMP ("</BaseArray>\n");
643 }
644 
DEF_DUMP(Lookup)645 DEF_DUMP (Lookup)
646 {
647   int i;
648   const char *lookup_name;
649   void (*lookup_func) (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type) = NULL;
650 
651   if (hb_type == HB_Type_GSUB)
652     {
653       switch (Lookup->LookupType)
654 	{
655 	case  HB_GSUB_LOOKUP_SINGLE:
656 	  lookup_name = "SINGLE";
657 	  lookup_func = Dump_GSUB_Lookup_Single;
658 	  break;
659 	case  HB_GSUB_LOOKUP_MULTIPLE:
660 	  lookup_name = "MULTIPLE";
661 	  break;
662 	case  HB_GSUB_LOOKUP_ALTERNATE:
663 	  lookup_name = "ALTERNATE";
664 	  break;
665 	case  HB_GSUB_LOOKUP_LIGATURE:
666 	  lookup_name = "LIGATURE";
667 	  lookup_func = Dump_GSUB_Lookup_Ligature;
668 	  break;
669 	case  HB_GSUB_LOOKUP_CONTEXT:
670 	  lookup_name = "CONTEXT";
671 	  lookup_func = Dump_GSUB_Lookup_Context;
672 	  break;
673 	case  HB_GSUB_LOOKUP_CHAIN:
674 	  lookup_name = "CHAIN";
675 	  lookup_func = Dump_GSUB_Lookup_Chain;
676 	  break;
677 	default:
678 	  lookup_name = "(unknown)";
679 	  lookup_func = NULL;
680 	  break;
681 	}
682     }
683   else
684     {
685       switch (Lookup->LookupType)
686 	{
687 	case HB_GPOS_LOOKUP_SINGLE:
688 	  lookup_name = "SINGLE";
689 	  lookup_func = Dump_GPOS_Lookup_Single;
690 	  break;
691 	case HB_GPOS_LOOKUP_PAIR:
692 	  lookup_name = "PAIR";
693 	  lookup_func = Dump_GPOS_Lookup_Pair;
694 	  break;
695 	case HB_GPOS_LOOKUP_CURSIVE:
696 	  lookup_name = "CURSIVE";
697 	  break;
698 	case HB_GPOS_LOOKUP_MARKBASE:
699 	  lookup_name = "MARKBASE";
700 	  lookup_func = Dump_GPOS_Lookup_Markbase;
701 	  break;
702 	case HB_GPOS_LOOKUP_MARKLIG:
703 	  lookup_name = "MARKLIG";
704 	  break;
705 	case HB_GPOS_LOOKUP_MARKMARK:
706 	  lookup_name = "MARKMARK";
707 	  break;
708 	case HB_GPOS_LOOKUP_CONTEXT:
709 	  lookup_name = "CONTEXT";
710 	  break;
711 	case HB_GPOS_LOOKUP_CHAIN:
712 	  lookup_name = "CHAIN";
713 	  break;
714 	default:
715 	  lookup_name = "(unknown)";
716 	  lookup_func = NULL;
717 	  break;
718 	}
719     }
720 
721   DUMP2("<LookupType>%s</LookupType> <!-- %d -->\n", lookup_name, Lookup->LookupType);
722   DUMP1("<LookupFlag>%#06x</LookupFlag>\n", Lookup->LookupFlag);
723 
724   for (i=0; i < Lookup->SubTableCount; i++)
725     {
726       DUMP ("<Subtable>\n");
727       if (lookup_func)
728 	(*lookup_func) (&Lookup->SubTable[i], stream, indent + 1, hb_type);
729       DUMP ("</Subtable>\n");
730     }
731 }
732 
DEF_DUMP(LookupList)733 DEF_DUMP (LookupList)
734 {
735   int i;
736 
737   DUMP_FUINT (LookupList, LookupCount);
738 
739   for (i=0; i < LookupList->LookupCount; i++)
740     RECURSE_NUM (Lookup, i, Lookup, &LookupList->Lookup[i]);
741 }
742 
743 void
HB_Dump_GSUB_Table(HB_GSUB gsub,FILE * stream)744 HB_Dump_GSUB_Table (HB_GSUB gsub, FILE *stream)
745 {
746   int indent = 1;
747   HB_Type hb_type = HB_Type_GSUB;
748 
749   do_indent (stream, indent);
750   fprintf(stream, "<!-- GSUB -->\n");
751   RECURSE (ScriptList, ScriptList, &gsub->ScriptList);
752   RECURSE (FeatureList, FeatureList, &gsub->FeatureList);
753   RECURSE (LookupList, LookupList, &gsub->LookupList);
754 }
755 
756 void
HB_Dump_GPOS_Table(HB_GPOS gpos,FILE * stream)757 HB_Dump_GPOS_Table (HB_GPOS gpos, FILE *stream)
758 {
759   int indent = 1;
760   HB_Type hb_type = HB_Type_GPOS;
761 
762   do_indent (stream, indent);
763   fprintf(stream, "<!-- GPOS -->\n");
764   RECURSE (ScriptList, ScriptList, &gpos->ScriptList);
765   RECURSE (FeatureList, FeatureList, &gpos->FeatureList);
766   RECURSE (LookupList, LookupList, &gpos->LookupList);
767 }
768