text: /* Copyright 2014 Adobe Systems Incorporated (http://www.adobe.com/). All Rights Reserved.
     This software is licensed as OpenSource, under the Apache License, Version 2.0.
     This license is available at: http://opensource.org/licenses/Apache-2.0. */
  
  #include <string.h>
  #include "tto.h"
  #include "sfnt_GDEF.h"
  
  #include "GDEF.h"
  #include "head.h"
  #include "sfnt.h"
  
  static GDEFTbl GDEF;
  static IntX loaded = 0;
  static Card32 GDEFStart;
  
  static void readAttachList(Card32 offset, AttachList* attachList) {
      int i, j;
      Card32 save = TELL();
      SEEK_SURE(offset);
      IN1(attachList->Coverage);
      IN1(attachList->GlyphCount);
  
      attachList->_Coverage = ttoReadCoverage(offset + attachList->Coverage);
  
      attachList->AttachPoint = memNew(sizeof(Offset) * attachList->GlyphCount);
      attachList->_AttachPoint =
          memNew(sizeof(attachList->_AttachPoint[0]) * attachList->GlyphCount);
      for (i = 0; i < attachList->GlyphCount; i++) {
          IN1(attachList->AttachPoint[i]); /* array of offsets */
      }
  
      for (i = 0; i < attachList->GlyphCount; i++) {
          Card32 attachPointeOffset = offset + attachList->AttachPoint[i];
          AttachPoint* attachPoint = &attachList->_AttachPoint[i];
          SEEK_SURE(attachPointeOffset);
          IN1(attachPoint->PointCount);
          attachPoint->PointIndex =
              memNew(sizeof(attachPoint->PointIndex[0]) * attachPoint->PointCount);
          for (j = 0; j < attachPoint->PointCount; j++)
              IN1(attachPoint->PointIndex[j]);
      }
      SEEK_SURE(save);
  }
  
  static void readLigCaretList(Card32 offset, LigCaretList* ligCaretList) {
      int i, j;
      Card32 save = TELL();
      SEEK_SURE(offset);
  
      IN1(ligCaretList->Coverage);
      IN1(ligCaretList->LigGlyphCount);
  
      ligCaretList->_Coverage = ttoReadCoverage(offset + ligCaretList->Coverage);
  
      ligCaretList->LigGlyph = memNew(sizeof(Offset) * ligCaretList->LigGlyphCount);
      ligCaretList->_LigGlyph =
          memNew(sizeof(ligCaretList->_LigGlyph[0]) * ligCaretList->LigGlyphCount);
      for (i = 0; i < ligCaretList->LigGlyphCount; i++) {
          IN1(ligCaretList->LigGlyph[i]);
      }
  
      for (i = 0; i < ligCaretList->LigGlyphCount; i++) {
          Card32 ligGlyphOffset = offset + ligCaretList->LigGlyph[i];
          LigGlyph* ligGlyph = &ligCaretList->_LigGlyph[i];
          SEEK_SURE(ligGlyphOffset);
  
          IN1(ligGlyph->CaretCount);
          ligGlyph->CaretValue = memNew(sizeof(Offset) * ligGlyph->CaretCount);
          ligGlyph->_CaretValue =
              memNew(sizeof(ligGlyph->_CaretValue[0]) * ligGlyph->CaretCount);
  
          for (j = 0; j < ligGlyph->CaretCount; j++) {
              IN1(ligGlyph->CaretValue[j]);
          }
          for (j = 0; j < ligGlyph->CaretCount; j++) {
              Card32 caretValuehOffset = ligGlyphOffset + ligGlyph->CaretValue[j];
              CaretValueFormat3* caretValue = &ligGlyph->_CaretValue[j];
              SEEK_SURE(caretValuehOffset);
  
              IN1(caretValue->CaretValueFormat);
              IN1(caretValue->Coordinate); /* for formats 2 and 4 this has another name, but is still a Card16 */
              if (caretValue->CaretValueFormat == 3) {
                  IN1(caretValue->DeviceTable);
                  ttoReadDeviceTable(caretValuehOffset + caretValue->DeviceTable, &caretValue->_DeviceTable);
              }
          }
      }
  
      SEEK_SURE(save);
  }
  
  static void readMarkGlyphSetsDef(Card32 offset, MarkGlyphSetsDef* markGlyphSetsDef) {
      int i;
      Card32 save = TELL();
      SEEK_SURE(offset);
  
      IN1(markGlyphSetsDef->MarkSetTableFormat);
      IN1(markGlyphSetsDef->MarkSetCount);
  
      markGlyphSetsDef->Coverage = memNew(sizeof(LOffset) * markGlyphSetsDef->MarkSetCount);
      markGlyphSetsDef->_Coverage = memNew(sizeof(void*) * markGlyphSetsDef->MarkSetCount);
      for (i = 0; i < markGlyphSetsDef->MarkSetCount; i++) {
          IN1(markGlyphSetsDef->Coverage[i]);
          markGlyphSetsDef->_Coverage[i] = ttoReadCoverage(offset + markGlyphSetsDef->Coverage[i]);
      }
      SEEK_SURE(save);
  }
  
  void GDEFRead(LongN start, Card32 length) {
      if (loaded)
          return;
      SEEK_ABS(start);
      GDEFStart = start;
      IN1(GDEF.Version);
      IN1(GDEF.GlyphClassDef);
      IN1(GDEF.AttachList);
      IN1(GDEF.LigCaretList);
      IN1(GDEF.MarkAttachClassDef);
      GDEF.hasMarkAttachClassDef = 0;
  
      if (GDEF.Version >= (Fixed)0x00010002)
          IN1(GDEF.MarkGlyphSetsDef);
      else
          GDEF.MarkGlyphSetsDef = 0;
  
      if (GDEF.GlyphClassDef)
          GDEF._GlyphClassDef = ttoReadClass(start + GDEF.GlyphClassDef);
  
      if (GDEF.AttachList)
          readAttachList(start + GDEF.AttachList, &GDEF._AttachList);
  
      if (GDEF.LigCaretList)
          readLigCaretList(start + GDEF.LigCaretList, &GDEF._LigCaretList);
  
      /* Some versions of the GDEF don't have a MarkAttachClassDef entry.
         If they don't, then one of the offsets is at 0x0a */
      if (GDEF.GlyphClassDef != 0x0a && GDEF.AttachList != 0x0a && GDEF.LigCaretList != 0x0a && GDEF.MarkAttachClassDef != 0) {
          GDEF._MarkAttachClassDef = ttoReadClass(start + GDEF.MarkAttachClassDef);
          GDEF.hasMarkAttachClassDef = 1;
      } else
          GDEF.MarkAttachClassDef = 0;
  
      if (GDEF.MarkGlyphSetsDef)
          readMarkGlyphSetsDef(start + GDEF.MarkGlyphSetsDef, &GDEF._MarkGlyphSetsDef);
  
      length = 0; /* suppress compiler complaint */
  }
  
  static void freeAttachList(AttachList* attachList) {
      int i;
  
      if (attachList == NULL)
          return;
  
      ttoFreeCoverage(attachList->_Coverage);
  
      for (i = 0; i < attachList->GlyphCount; i++) {
          AttachPoint* attachPoint = &attachList->_AttachPoint[i];
          memFree(attachPoint->PointIndex);
      }
      memFree(attachList->_AttachPoint);
      memFree(attachList->AttachPoint);
  }
  
  static void freeLigCaretList(LigCaretList* ligCaretList) {
      int i;
  
      if (ligCaretList == NULL)
          return;
  
      ttoFreeCoverage(ligCaretList->_Coverage);
  
      for (i = 0; i < ligCaretList->LigGlyphCount; i++) {
          LigGlyph* ligGlyph = &ligCaretList->_LigGlyph[i];
          memFree(ligGlyph->_CaretValue);
          memFree(ligGlyph->CaretValue);
      }
      memFree(ligCaretList->_LigGlyph);
      memFree(ligCaretList->LigGlyph);
  }
  
  static void freedMarkGlyphSetsDef(MarkGlyphSetsDef* markGlyphSetsDef) {
      int i;
  
      if (markGlyphSetsDef == NULL)
          return;
  
      for (i = 0; i < markGlyphSetsDef->MarkSetCount; i++) {
          ttoFreeCoverage(markGlyphSetsDef->_Coverage[i]);
      }
      memFree(markGlyphSetsDef->_Coverage);
      memFree(markGlyphSetsDef->Coverage);
  }
  
  static void dumpAttachList(Offset attachListOffset, AttachList* attachList, int level) {
      int i, j;
      ttoEnumRec CovList;
      Card32 nitems;
  
      if (attachListOffset == 0)
          return;
  
      DL(2, (OUTPUTBUFF, "--- AttachList (%04hx)\n", attachListOffset));
      DLu(2, "GlyphCount=", attachList->GlyphCount);
  
      ttoDumpCoverage(attachList->Coverage, attachList->_Coverage, level);
      ttoEnumerateCoverage(attachList->Coverage, attachList->_Coverage, &CovList, &nitems);
  
      DL(2, (OUTPUTBUFF, "--- AttachPoint[index]=offset\n"));
      for (i = 0; i < attachList->GlyphCount; i++) {
          DL(2, (OUTPUTBUFF, "[%d]=%04hx ", i, attachList->AttachPoint[i]));
      }
      DL(2, (OUTPUTBUFF, "\n"));
  
      for (i = 0; i < attachList->GlyphCount; i++) {
          char name[MAX_NAME_LEN];
          GlyphId glyphId = *da_INDEX(CovList.glyphidlist, i);
          AttachPoint* attachPoint = &attachList->_AttachPoint[i];
  
          strcpy(name, getGlyphName(glyphId, 0));
          if (level == 7) {
              fprintf(OUTPUTBUFF, "\n\tAttach %s", name);
              for (j = 0; j < attachPoint->PointCount; j++) {
                  fprintf(OUTPUTBUFF, " %d", attachPoint->PointIndex[j]);
              }
              fprintf(OUTPUTBUFF, "%s;", name);
          } else {
              DL(2, (OUTPUTBUFF, "--- AttachPoint index [%d] offset [%04hx]\n", i, attachList->AttachPoint[i]));
              DLu(2, "PointCount=", attachPoint->PointCount);
              DL(2, (OUTPUTBUFF, "Glyph name (%s)\n", name));
              DL(2, (OUTPUTBUFF, "Attachment points= "));
              for (j = 0; j < attachPoint->PointCount; j++) {
                  DL(2, (OUTPUTBUFF, "%hu ", attachPoint->PointIndex[j]));
              }
              DL(2, (OUTPUTBUFF, "\n"));
          }
      }
      if (level == 7)
          fprintf(OUTPUTBUFF, "\n");
      else
          DL(2, (OUTPUTBUFF, "\n"));
  }
  
  const char* kLigCaretNames[] = {"LigatureCaretZeroTypeName", "LigatureCaretByPos", "LigatureCaretByIndex", "LigatureCaretByPos"};
  
  static void dumpLigCaretList(Offset ligCaretListoffset, LigCaretList* ligCaretList, int level) {
      int i, j;
      ttoEnumRec CovList;
      Card32 nitems;
      if (ligCaretListoffset == 0)
          return;
  
      DL(2, (OUTPUTBUFF, "--- LigCaretList (%04hx)\n", ligCaretListoffset));
      DLu(2, "LigGlyphCount=", ligCaretList->LigGlyphCount);
  
      ttoDumpCoverage(ligCaretList->Coverage, ligCaretList->_Coverage, level);
      ttoEnumerateCoverage(ligCaretList->Coverage, ligCaretList->_Coverage, &CovList, &nitems);
  
      DL(2, (OUTPUTBUFF, "--- LigCaretList[index]=offset\n"));
      for (i = 0; i < ligCaretList->LigGlyphCount; i++) {
          DL(2, (OUTPUTBUFF, "[%d]=%04hx ", i, ligCaretList->LigGlyph[i]));
      }
      DL(2, (OUTPUTBUFF, "\n"));
  
      for (i = 0; i < ligCaretList->LigGlyphCount; i++) {
          char name[MAX_NAME_LEN];
          CaretValueFormat3* caretValue;
          GlyphId glyphId = *da_INDEX(CovList.glyphidlist, i);
          LigGlyph* ligGlyph = &ligCaretList->_LigGlyph[i];
          caretValue = &ligGlyph->_CaretValue[0];
          strcpy(name, getGlyphName(glyphId, 0));
  
          if (level == 7) {
              fprintf(OUTPUTBUFF, "\n\t%s %s ", kLigCaretNames[caretValue->CaretValueFormat], name);
          } else {
              DL(2, (OUTPUTBUFF, "--- LigGlyph index [%d] offset (%04hx)\n", i, ligCaretList->LigGlyph[i]));
              DLu(2, "CaretCount=", ligGlyph->CaretCount);
              DL(2, (OUTPUTBUFF, "LigGlyph name (%s)\n", name));
          }
  
          for (j = 0; j < ligGlyph->CaretCount; j++) {
              CaretValueFormat3* caretValue = &ligGlyph->_CaretValue[j];
  
              if (level == 7) {
                  fprintf(OUTPUTBUFF, " %d", caretValue->Coordinate);
                  if (caretValue->CaretValueFormat == 3)
                      ttoDumpDeviceTable(caretValue->DeviceTable, &caretValue->_DeviceTable, level);
              } else {
                  DL(2, (OUTPUTBUFF, "--- CaretValue  Table index [%d] offset [%04hx]\n", j, ligGlyph->CaretValue[j]));
                  DLu(2, "CaretValueFormat   =", caretValue->CaretValueFormat);
                  if ((caretValue->CaretValueFormat == 1) || (caretValue->CaretValueFormat == 3)) {
                      DLu(2, "Coordinate   =", caretValue->Coordinate);
                      if (caretValue->CaretValueFormat == 3)
                          ttoDumpDeviceTable(caretValue->DeviceTable, &caretValue->_DeviceTable, level);
                  } else
                      DLu(2, "IdCaretValue   =", caretValue->Coordinate);
              }
          } /* end caret values for current glyph */
  
          if (level == 7)
              fprintf(OUTPUTBUFF, ";");
          else
              DL(2, (OUTPUTBUFF, "\n"));
      } /* end for all glyphs */
      if (level == 7)
          fprintf(OUTPUTBUFF, "\n");
      else
          DL(2, (OUTPUTBUFF, "\n"));
  }
  
  static void dumpMarkGlyphSetsDef(Offset offset, MarkGlyphSetsDef* markGlyphSetsDef, int level) {
      int i;
      ttoEnumRec CovList;
      Card32 nitems;
  
      if (offset == 0)
          return;
  
      DL(2, (OUTPUTBUFF, "--- markGlyphSets (%04hx)\n", offset));
      DLu(2, "MarkSetTableFormat=", markGlyphSetsDef->MarkSetTableFormat);
      DLu(2, "MarkSetCount=", markGlyphSetsDef->MarkSetCount);
  
      DL(2, (OUTPUTBUFF, "--- Coverage[index]=offset\n"));
      for (i = 0; i < markGlyphSetsDef->MarkSetCount; i++) {
          DL(2, (OUTPUTBUFF, "[%d]=%04x ", i, markGlyphSetsDef->Coverage[i]));
      }
      DL(2, (OUTPUTBUFF, "\n"));
      for (i = 0; i < markGlyphSetsDef->MarkSetCount; i++) {
          Offset localOffset = (Offset)markGlyphSetsDef->Coverage[i];
          if (level == 7) {
              long c;
              ttoEnumerateCoverage(localOffset, markGlyphSetsDef->_Coverage[i], &CovList, &nitems);
              if (CovList.glyphidlist.cnt > 0) {
                  fprintf(OUTPUTBUFF, "\t@GDEF_MarkGlyphSetClass_%d = [", i);
                  for (c = 0; c < CovList.glyphidlist.cnt; c++) {
                      GlyphId glyphId = *da_INDEX(CovList.glyphidlist, c);
                      fprintf(OUTPUTBUFF, " %s", getGlyphName(glyphId, 0));
                  }
                  fprintf(OUTPUTBUFF, "];\n");
              }
          } else
              ttoDumpCoverage(localOffset, markGlyphSetsDef->_Coverage[i], level);
      }
  }
  
  const int kNumGDEFGlyphClasses = 5;
  const int kMaxMarkAttachClasses = 16;
  const char* kGlyphClassNames[] = {
      "Simple Glyphs - Class 0",
      "Base Glyphs - Class 1",
      "Ligature Glyphs - Class 2",
      "Mark Glyphs - Class 3",
      "Component Glyphs - Class 4",
  };
  
  void GDEFDump(IntX level, LongN start) {
      if (!loaded) {
          if (sfntReadTable(GDEF_))
              return;
      }
      initGlyphNames();
  
      if (level == 7)
          fprintf(OUTPUTBUFF, "table GDEF {\n");
  
      DL(1, (OUTPUTBUFF, "### [GDEF] (%08lx)\n", start));
  
      DLV(2, "Version    =", GDEF.Version);
      DLx(2, "GlyphClassDef =", GDEF.GlyphClassDef);
      DLx(2, "AttachList=", GDEF.AttachList);
      DLx(2, "LigCaretList =", GDEF.LigCaretList);
      DLx(2, "MarkAttachClassDef =", GDEF.MarkAttachClassDef);
      if (GDEF.Version >= (Fixed)0x00010002)
          DLx(2, "MarkGlyphSetsDef =", GDEF.MarkGlyphSetsDef);
  
      fprintf(OUTPUTBUFF, "\n\t# Glyph Class Definitions\n");
      if (level == 7) {
          ttoEnumRec* classList;
          Card32 classGlyphCount;
          int classId;
  
          fprintf(OUTPUTBUFF, "\tGlyphClassDef\n");
          classList = memNew(sizeof(ttoEnumRec) * kNumGDEFGlyphClasses);
          ttoEnumerateClass(GDEF.GlyphClassDef, GDEF._GlyphClassDef,
                            kNumGDEFGlyphClasses,
                            classList, &classGlyphCount);
          for (classId = 1; classId < kNumGDEFGlyphClasses; classId++) {
              ttoEnumRec* class;
              long c;
  
              class = &(classList[classId]);
  
              if (class->glyphidlist.cnt == 0) {
                  /* Handle class with no glyphs assigned. */
                  if (classId < (kNumGDEFGlyphClasses - 1))
                      fprintf(OUTPUTBUFF, "\t\t, # %s\n", kGlyphClassNames[classId]);
                  else
                      fprintf(OUTPUTBUFF, "\t\t; # %s\n", kGlyphClassNames[classId]);
              } else {
                  fprintf(OUTPUTBUFF, "\t\t# %s\n\t\t[", kGlyphClassNames[classId]);
                  for (c = 0; c < class->glyphidlist.cnt; c++) {
                      GlyphId glyphId = *da_INDEX(class->glyphidlist, c);
                      fprintf(OUTPUTBUFF, " %s", getGlyphName(glyphId, 0));
                  }
                  if (classId < (kNumGDEFGlyphClasses - 1))
                      fprintf(OUTPUTBUFF, "],\n");
                  else
                      fprintf(OUTPUTBUFF, "];\n");
              }
          }
          fprintf(OUTPUTBUFF, "\t# end GlyphClass definitions\n");
          memFree(classList);
      } else
          ttoDumpClass(GDEF.GlyphClassDef, GDEF._GlyphClassDef, level);
  
      if (GDEF.AttachList) {
          fprintf(OUTPUTBUFF, "\n\t# AttachList Definitions\n");
          dumpAttachList(GDEF.AttachList, &GDEF._AttachList, level);
      }
  
      if (GDEF.LigCaretList) {
          fprintf(OUTPUTBUFF, "\n\t# Ligature Caret List Definitions\n");
          dumpLigCaretList(GDEF.LigCaretList, &GDEF._LigCaretList, level);
      }
  
      if (GDEF.MarkAttachClassDef) {
          fprintf(OUTPUTBUFF, "\n\t# Mark Attach Class Definitions\n");
          if (level == 7) {
              ttoEnumRec* classList;
              Card32 classGlyphCount;
              int classId;
              classList = memNew(sizeof(ttoEnumRec) * kMaxMarkAttachClasses);
              ttoEnumerateClass(GDEF.MarkAttachClassDef, GDEF._MarkAttachClassDef,
                                kMaxMarkAttachClasses,
                                classList, &classGlyphCount);
              for (classId = 0; classId < kMaxMarkAttachClasses; classId++) {
                  ttoEnumRec* class;
                  long c;
                  class = &(classList[classId]);
                  if (class->glyphidlist.cnt > 0) {
                      fprintf(OUTPUTBUFF, "\t@GDEF_MarkAttachClass_%d = [", classId);
                      for (c = 0; c < class->glyphidlist.cnt; c++) {
                          GlyphId glyphId = *da_INDEX(class->glyphidlist, c);
                          fprintf(OUTPUTBUFF, " %s", getGlyphName(glyphId, 0));
                      }
                      fprintf(OUTPUTBUFF, "];\n");
                  }
              }
  
              memFree(classList);
          } else
              ttoDumpClass(GDEF.MarkAttachClassDef, GDEF._MarkAttachClassDef, level);
      }
  
      if (GDEF.MarkGlyphSetsDef) {
          fprintf(OUTPUTBUFF, "\n\t# Mark Glyph Sets Definitions\n");
          dumpMarkGlyphSetsDef(GDEF.MarkGlyphSetsDef, &GDEF._MarkGlyphSetsDef, level);
      }
  
      if (level == 7)
          fprintf(OUTPUTBUFF, "} GDEF;\n");
  }
  
  void GDEFFree(void) {
      if (!loaded)
          return;
  
      ttoFreeClass(GDEF._GlyphClassDef);
  
      freeAttachList(&GDEF._AttachList);
  
      freeLigCaretList(&GDEF._LigCaretList);
  
      freedMarkGlyphSetsDef(&GDEF._MarkGlyphSetsDef);
  }
  
  void GDEFUsage(void) {
      fprintf(OUTPUTBUFF,
              "--- GDEF\n"
              "=7  output the more readable feature file format\n");
  }
  