Home | History | Annotate | Download | only in src
      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
     52 do_indent (FILE *stream, int indent)
     53 {
     54   fprintf (stream, "%*s", indent * 3, "");
     55 }
     56 
     57 static void
     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
     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
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    254 DEF_DUMP (SubstLookupRecord)
    255 {
    256   HB_UNUSED(hb_type);
    257 
    258   DUMP_FUINT (SubstLookupRecord, SequenceIndex);
    259   DUMP_FUINT (SubstLookupRecord, LookupListIndex);
    260 }
    261 
    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 
    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
    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 
    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 
    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
    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 
    345 DEF_DUMP (ContextSubstFormat1)
    346 {
    347   HB_UNUSED(hb_type);
    348   HB_UNUSED(ContextSubstFormat1);
    349 
    350 
    351   DUMP("<!-- Not implemented!!! -->\n");
    352 }
    353 
    354 DEF_DUMP (ContextSubstFormat2)
    355 {
    356   DUMP_FUINT (ContextSubstFormat2, MaxContextLength);
    357   RECURSE (Coverage, Coverage, &ContextSubstFormat2->Coverage);
    358   RECURSE (ClassDefinition, ClassDefinition, &ContextSubstFormat2->ClassDef);
    359 }
    360 
    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
    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 
    391 DEF_DUMP (ChainContextSubstFormat1)
    392 {
    393   HB_UNUSED(hb_type);
    394   HB_UNUSED(ChainContextSubstFormat1);
    395 
    396   DUMP("<!-- Not implemented!!! -->\n");
    397 }
    398 
    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 
    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
    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
    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
    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
    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
    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
    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
    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
    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 
    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 
    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
    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
    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