(mflt_enable_new_feature): Document it.
[m17n/m17n-lib.git] / src / m17n-flt.c
index 6e77058..76f0f99 100644 (file)
@@ -1,5 +1,5 @@
 /* m17n-flt.c -- Font Layout Table sub-module.
-   Copyright (C) 2003, 2004, 2007, 2008, 2009
+   Copyright (C) 2003, 2004, 2007, 2008, 2009, 2010
      National Institute of Advanced Industrial Science and Technology (AIST)
      Registration Number H15PRO112
 
@@ -257,9 +257,9 @@ enum GlyphInfoMask
 {
   CategoryCodeMask = 0x7F,
   CombiningCodeMask = 0xFFFFFF,
-  CombinedMask = 1 << 27,
-  LeftPaddingMask = 1 << 28,
-  RightPaddingMask = 1 << 29
+  CombinedMask = 1 << 28,
+  LeftPaddingMask = 1 << 29,
+  RightPaddingMask = 1 << 30
 };
 
 #define SET_GLYPH_INFO(g, mask, ctx, info)                     \
@@ -543,7 +543,14 @@ load_category_table (MPlist *plist, MFLTFont *font)
       elt = MPLIST_PLIST (p);
       if (MPLIST_SYMBOL_P (elt))
        {
-         MPlist *next = MPLIST_NEXT (elt);
+         MPlist *next;
+
+         if (! mflt_enable_new_feature)
+           {
+             M17N_OBJECT_UNREF (table);
+             return NULL;
+           }
+         next = MPLIST_NEXT (elt);
          if (! MPLIST_INTEGER_P (next))
            MERROR_GOTO (MERROR_FLT, end);
          if (! feature_table_head)
@@ -566,6 +573,11 @@ load_category_table (MPlist *plist, MFLTFont *font)
        }
       else if (MPLIST_SYMBOL_P (elt))
        {
+         if (! mflt_enable_new_feature)
+           {
+             M17N_OBJECT_UNREF (table);
+             return NULL;
+           }
          if (font)
            {
              MFLTOtfSpec spec;
@@ -730,81 +742,93 @@ otf_store_features (char *p, char *end, unsigned *buf)
   buf[i] = 0;
 }
 
+/* SYMBOL's name       features[0]             for checking    for applying
+                              features[1]      GSUB     GPOS   GSUB    GPOS
+   -------------       ------------------      -------------   ------------
+   SCRIPT              [-1,0]      [-1,0]         any | any       all  all
+   SCRIPT=             NULL        [-1,0]        none & 1        none  all
+   SCRIPT+             [-1,0]        NULL           1 & none      all  none
+   SCRIPT=+            NULL          NULL        none & none     none  none
+   SCRIPT=F1           [F1,0]      [-1,0]          F1 & 1          F1  all
+   SCRIPT+F1           [-1][0]     [F1,0]           1 & F1       none  F1
+   SCRIPT=F1+          [F1,0]        NULL          F1 & none       F1  none
+   SCRIPT=~F2          [-1,F2,0]   [-1,0]         ~F2 & 1      all~F2  all
+   SCRIPT=F1,~F2       [F1,-1,F2,0][-1,0]      F1&~F2 & 1      F1 (*1) all
+
+   (*1) Invalid specification
+ */
+
 static int
 parse_otf_command (MSymbol symbol, MFLTOtfSpec *spec)
 {
   char *str = MSYMBOL_NAME (symbol);
   char *end = str + MSYMBOL_NAMELEN (symbol);
   unsigned int script, langsys;
-  char *gsub, *gpos;
-  int gsub_count = 0, gpos_count = 0;
+  char *features[3];
+  int feature_count[2];                /* [0]:GSUB, [1]:GPOS */
+  int i;
   char *p;
 
   memset (spec, 0, sizeof (MFLTOtfSpec));
 
   spec->sym = symbol;
   str += 5;                    /* skip the heading ":otf=" or ":otf?" */
-  if (str[-1] == '?' && ! *str)
-    /* This is a spec to reset category codes.  */
-    return 0;
-  script = gen_otf_tag (str, 8);
+  if (str[-1] == '?')
+    {
+      if (! mflt_enable_new_feature)
+       /* The client can't use this command.  */
+       return -1;
+      if (! *str)
+       /* This is a spec to reset category codes.  */
+       return 0;
+    }
+  spec->script = gen_otf_tag (str, 8);
   str += 4;
   if (*str == '/')
     {
-      langsys = gen_otf_tag (str, 8);
+      spec->langsys = gen_otf_tag (str, 8);
       str += 4;
     }
   else
-    langsys = 0;
-  gsub = str;
+    spec->langsys = 0;
+  features[0] = str;
   if (*str != '=')
     /* Apply all GSUB features.  */
-      gsub_count = 1;
+      feature_count[0] = -1;
   else
     {
       p = str + 1;
-      str = otf_count_features (p, end, '+', &gsub_count);
+      str = otf_count_features (p, end, '+', feature_count);
       if (! str)
        MERROR (MERROR_FLT, -1);
     }
-  gpos = str;
+  features[1] = str;
   if (*str != '+')
     /* Apply all GPOS features.  */
-    gpos_count = 1;
+    feature_count[1] = -1;
   else
     {
       p = str + 1;
-      str = otf_count_features (p, end, '\0', &gpos_count);
+      str = otf_count_features (p, end, '\0', feature_count + 1);
       if (! str)
        MERROR (MERROR_FLT, -1);
     }
-
-  spec->script = script;
-  spec->langsys = langsys;
-  if (gsub_count > 0)
-    {
-      spec->features[0] = malloc (sizeof (int) * (gsub_count + 1));
-      if (! spec->features[0])
-       return -2;
-      if (*gsub == '=')
-       otf_store_features (gsub + 1, gpos, spec->features[0]);
-      else
-       spec->features[0][0] = 0xFFFFFFFF, spec->features[0][1] = 0;
-    }
-  if (gpos_count > 0)
-    {
-      spec->features[1] = malloc (sizeof (int) * (gpos_count + 1));
-      if (! spec->features[1])
-       {
-         if (spec->features[0])
-           free (spec->features[0]);
+  features[2] = str;
+  for (i = 0; i < 2; i++)
+    if (feature_count[i])
+      {
+       spec->features[i] = malloc (sizeof (int)
+                                   * (feature_count[i] < 0 ? 2
+                                      : feature_count[i] + 1));
+       if (! spec->features[i])
          return -2;
-       }
-      if (*gpos == '+')
-       otf_store_features (gpos + 1, str, spec->features[1]);
-      else
-       spec->features[1][0] = 0xFFFFFFFF, spec->features[1][1] = 0;
-    }
+       if (feature_count[i] > 0)
+         otf_store_features (features[i] + 1, features[i + 1],
+                             spec->features[i]);
+       else
+         spec->features[i][0] = 0xFFFFFFFF, spec->features[i][1] = 0;
+      }
+
   return 0;
 }
 
@@ -821,7 +845,7 @@ load_otf_command (FontLayoutCmd *cmd, MSymbol sym)
   char *name = MSYMBOL_NAME (sym);
   int result;
 
-  if (name[0] != ':' && name[0] != '?')
+  if (name[0] != ':')
     {
       /* This is old format of "otf:...".  Change it to ":otf=...".  */
       char *str = alloca (MSYMBOL_NAMELEN (sym) + 2);
@@ -1397,6 +1421,8 @@ load_flt (MFLT *flt, MPlist *key_list)
              continue;
            }
          category = load_category_table (pl, NULL);
+         if (! category)
+           goto err;
          if (! flt->coverage)
            {
              flt->coverage = category;
@@ -1423,7 +1449,7 @@ load_flt (MFLT *flt, MPlist *key_list)
     }
   if (category)
     unref_category_table (category);
-
+ err:
   if (! MPLIST_TAIL_P (plist))
     {
       M17N_OBJECT_UNREF (top);
@@ -1572,7 +1598,7 @@ typedef struct
 
 static int run_command (int, int, int, int, FontLayoutContext *);
 static int run_otf (int, MFLTOtfSpec *, int, int, FontLayoutContext *);
-static int run_otf_category (int, MFLTOtfSpec *, int, int, FontLayoutContext *);
+static int try_otf (int, MFLTOtfSpec *, int, int, FontLayoutContext *);
 
 #define NMATCH 20
 
@@ -1706,12 +1732,12 @@ run_rule (int depth,
            {
              if (MPLIST_INTEGER_P (p))
                {
-                 GREF (&gstring, i)->code = MPLIST_INTEGER (p);
+                 GREF (&gstring, i)->c = MPLIST_INTEGER (p);
                  GREF (&gstring, i)->encoded = 0;
                }
              else
                {
-                 GREF (&gstring, i)->code = GREF (ctx->in, idx)->code;
+                 GREF (&gstring, i)->c = GREF (ctx->in, idx)->code;
                  GREF (&gstring, i)->encoded = GREF (ctx->in, idx)->encoded;
                  idx++;
                }
@@ -1855,18 +1881,21 @@ decode_packed_otf_tag (FontLayoutContext *ctx, MFLTGlyphString *gstring,
       unsigned int tag = g->internal & 0xFFFFFFF;
       char enc;
 
+      if (GET_COMBINED (g))
+       continue;
       if (! category)
        {
          SET_CATEGORY_CODE (g, 0);
          continue;
        }
+      enc = '\0';
       if (tag & 0xFFFFF80)
        {
          int i;
 
          /* Clear the feature tag code.  */
          g->internal &= ~0xFFFFFFF;
-         for (i = 0, enc = '\0'; i < category->feature_table.size; i++)
+         for (i = 0; i < category->feature_table.size; i++)
            if (category->feature_table.tag[i] == tag)
              {
                enc = category->feature_table.code[i];
@@ -1875,10 +1904,9 @@ decode_packed_otf_tag (FontLayoutContext *ctx, MFLTGlyphString *gstring,
                break;
              }
        }
-      else
-       enc = '\0';
       if (! enc)
-       enc = g->c > 0 ? (int) mchartable_lookup (category->table, g->c) : 1;
+       enc = (g->c > 0 ? (int) mchartable_lookup (category->table, g->c)
+              : g->c == 0 ? 1 : ' ');
       SET_CATEGORY_CODE (g, enc);
     }
 }
@@ -1992,11 +2020,13 @@ run_otf (int depth,
 }
 
 static int
-run_otf_category (int depth, MFLTOtfSpec *otf_spec, int from, int to,
-                 FontLayoutContext *ctx)
+try_otf (int depth, MFLTOtfSpec *otf_spec, int from, int to,
+        FontLayoutContext *ctx)
 {
   MFLTFont *font = ctx->font;
-  int from_idx = ctx->out->used;
+
+  if (MDEBUG_FLAG () > 2)
+    MDEBUG_PRINT3 ("\n [FLT] %*s%s", depth, "", MSYMBOL_NAME (otf_spec->sym));
 
   if (! otf_spec->features[0] && ! otf_spec->features[1])
     {
@@ -2017,6 +2047,7 @@ run_otf_category (int depth, MFLTOtfSpec *otf_spec, int from, int to,
                          ? (int) mchartable_lookup (table, g->code)
                          : ' ');
              SET_CATEGORY_CODE (g, enc);
+             ctx->encoded[i - ctx->encoded_offset] = enc;
            }
        }
       return from;
@@ -2025,16 +2056,10 @@ run_otf_category (int depth, MFLTOtfSpec *otf_spec, int from, int to,
   if (ctx->stage->category->feature_table.size == 0)
     return from;
 
-  if (MDEBUG_FLAG () > 2)
-    MDEBUG_PRINT3 ("\n [FLT] %*s%s", depth, "", MSYMBOL_NAME (otf_spec->sym));
-
   font->get_glyph_id (font, ctx->in, from, to);
-  if (font->drive_otf)
+  if (mflt_try_otf)
     {
-      int out_len;
-      int i;
-
-      to = font->drive_otf (font, otf_spec, ctx->in, from, to, NULL, NULL);
+      to = mflt_try_otf (font, otf_spec, ctx->in, from, to);
       if (to < 0)
        return from;
       decode_packed_otf_tag (ctx, ctx->in, from, to, ctx->stage->category);
@@ -2143,7 +2168,7 @@ run_command (int depth, int id, int from, int to, FontLayoutContext *ctx)
       else if (cmd->type == FontLayoutCmdTypeOTF)
        to = run_otf (depth, &cmd->body.otf, from, to, ctx);
       else if (cmd->type == FontLayoutCmdTypeOTFCategory)
-       to = run_otf_category (depth, &cmd->body.otf, from, to, ctx);
+       to = try_otf (depth, &cmd->body.otf, from, to, ctx);
       return to;
     }
 
@@ -2214,12 +2239,14 @@ run_command (int depth, int id, int from, int to, FontLayoutContext *ctx)
       {
        int i;
 
+       if (MDEBUG_FLAG () > 2)
+         MDEBUG_PRINT2 ("\n [FLT] %*s|", depth, "");
        i = from < to ? from : from - 1;
        GDUP (ctx, i);
        g = GREF (ctx->out, ctx->out->used - 1);
        g->c = -1, g->code = 0;
        g->xadv = g->yadv = 0;
-       SET_ENCODED (g, 0);
+       SET_ENCODED (g, 1);
        SET_MEASURED (g, 0);
        SET_CATEGORY_CODE (g, ' ');
        return from;
@@ -2290,11 +2317,15 @@ run_stages (MFLTGlyphString *gstring, int from, int to,
 
          if (GET_COMBINED (g)
              || (prev_category && prev_category != ctx->stage->category))
-           enc = (GET_ENCODED (g)
-                  ? (g->c > 0 ? (int) mchartable_lookup (table, g->c) : 1)
-                  : g->code
-                  ? (int) mchartable_lookup (table, g->code)
-                  : ' ');
+           {
+             enc = (GET_ENCODED (g)
+                    ? (g->c > 0 ? (int) mchartable_lookup (table, g->c) : 1)
+                    : g->code
+                    ? (int) mchartable_lookup (table, g->code)
+                    : ' ');
+             if (! GET_COMBINED (g))
+               SET_CATEGORY_CODE (g, enc);
+           }
          else
            enc = GET_CATEGORY_CODE (g);
          ctx->encoded[i - from] = enc;
@@ -2644,10 +2675,12 @@ m17n_init_flt (void)
   Mgenerator = msymbol ("generator");
   Mend = msymbol ("end");
 
+  mflt_enable_new_feature = 0;
   mflt_iterate_otf_feature = NULL;
   mflt_font_id = NULL;
+  mflt_try_otf = NULL;
 
-  MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize the flt modules."));
+  MDEBUG_PRINT_TIME ("INIT", (mdebug__output, " to initialize the flt modules."));
   MDEBUG_POP_TIME ();
 }
 
@@ -2662,7 +2695,7 @@ m17n_fini_flt (void)
 
   MDEBUG_PUSH_TIME ();
   free_flt_list ();
-  MDEBUG_PRINT_TIME ("FINI", (stderr, " to finalize the flt modules."));
+  MDEBUG_PRINT_TIME ("FINI", (mdebug__output, " to finalize the flt modules."));
   MDEBUG_POP_TIME ();
   m17n_fini_core ();
 }
@@ -3028,14 +3061,24 @@ mflt_run (MFLTGlyphString *gstring, int from, int to,
        {
          MDEBUG_PRINT ("\n [FLT]   (RESULT");
          if (MDEBUG_FLAG () > 1)
-           for (i = 0; this_from < this_to; this_from++, i++)
-             {
-               if (i > 0 && i % 4 == 0)
-                 MDEBUG_PRINT ("\n [FLT]          ");
-               g = GREF (gstring, this_from);
-               MDEBUG_PRINT4 (" (%04X %d %d %d)",
-                              g->code, g->xadv, g->xoff, g->yoff);
-             }
+           {
+             int idx = -1;
+             for (i = 0; this_from < this_to; i++, this_from++)
+               {
+                 g = GREF (gstring, this_from);
+                 if (g->from != idx)
+                   {
+                     if (i > 0)
+                       MDEBUG_PRINT2 ("\n [FLT]           %02d-%02d",
+                                      g->from, g->to);
+                     else
+                       MDEBUG_PRINT2 (" %02d-%02d", g->from, g->to);
+                     idx = g->from;
+                   }
+                 MDEBUG_PRINT4 (" (%04X %d %d %d)",
+                                g->code, g->xadv, g->xoff, g->yoff);
+               }
+           }
          else
            for (; this_from < this_to; this_from++)
              MDEBUG_PRINT1 (" %04X", GREF (gstring, this_from)->code);
@@ -3064,6 +3107,15 @@ mflt_run (MFLTGlyphString *gstring, int from, int to,
   return to;
 }
 
+/***en
+    @brief Flag to control several new OTF handling commands.
+
+    If the variable mflt_enable_new_feature is nonzero, the function
+    #mflt_run () can drive a Font Layout Table that contains the new
+    OTF-related commands ":otf?" and/or OTF feature specification in a
+    category table.  */
+int mflt_enable_new_feature;
+
 int (*mflt_iterate_otf_feature) (struct _MFLTFont *font,
                                 MFLTOtfSpec *spec,
                                 int from, int to,
@@ -3071,6 +3123,9 @@ int (*mflt_iterate_otf_feature) (struct _MFLTFont *font,
 
 MSymbol (*mflt_font_id) (struct _MFLTFont *font);
 
+int (*mflt_try_otf) (struct _MFLTFont *font, MFLTOtfSpec *spec,
+                    MFLTGlyphString *gstring, int from, int to);
+
 \f
 /* for debugging... */
 
@@ -3083,7 +3138,7 @@ dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
   prefix[indent] = 0;
 
   if (id >= 0)
-    fprintf (stderr, "0x%02X", id);
+    fprintf (mdebug__output, "0x%02X", id);
   else if (id <= CMD_ID_OFFSET_INDEX)
     {
       int idx = CMD_ID_TO_INDEX (id);
@@ -3094,57 +3149,58 @@ dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
          FontLayoutCmdRule *rule = &cmd->body.rule;
          int i;
 
-         fprintf (stderr, "(rule ");
+         fprintf (mdebug__output, "(rule ");
          if (rule->src_type == SRC_REGEX)
-           fprintf (stderr, "\"%s\"", rule->src.re.pattern);
+           fprintf (mdebug__output, "\"%s\"", rule->src.re.pattern);
          else if (rule->src_type == SRC_INDEX)
-           fprintf (stderr, "%d", rule->src.match_idx);
+           fprintf (mdebug__output, "%d", rule->src.match_idx);
          else if (rule->src_type == SRC_SEQ)
-           fprintf (stderr, "(seq)");
+           fprintf (mdebug__output, "(seq)");
          else if (rule->src_type == SRC_RANGE)
-           fprintf (stderr, "(range)");
+           fprintf (mdebug__output, "(range)");
          else
-           fprintf (stderr, "(invalid src)");
+           fprintf (mdebug__output, "(invalid src)");
 
          for (i = 0; i < rule->n_cmds; i++)
            {
-             fprintf (stderr, "\n%s  ", prefix);
+             fprintf (mdebug__output, "\n%s  ", prefix);
              dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
            }
-         fprintf (stderr, ")");
+         fprintf (mdebug__output, ")");
        }
       else if (cmd->type == FontLayoutCmdTypeCond)
        {
          FontLayoutCmdCond *cond = &cmd->body.cond;
          int i;
 
-         fprintf (stderr, "(cond");
+         fprintf (mdebug__output, "(cond");
          for (i = 0; i < cond->n_cmds; i++)
            {
-             fprintf (stderr, "\n%s  ", prefix);
+             fprintf (mdebug__output, "\n%s  ", prefix);
              dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
            }
-         fprintf (stderr, ")");
+         fprintf (mdebug__output, ")");
        }
       else if (cmd->type == FontLayoutCmdTypeOTF)
        {
-         fprintf (stderr, "(otf)");
+         fprintf (mdebug__output, "(otf)");
        }
       else
-       fprintf (stderr, "(error-command)");
+       fprintf (mdebug__output, "(error-command)");
     }
   else if (id <= CMD_ID_OFFSET_COMBINING)
-    fprintf (stderr, "cominging-code");
+    fprintf (mdebug__output, "cominging-code");
   else
-    fprintf (stderr, "(predefiend %d)", id);
+    fprintf (mdebug__output, "(predefiend %d)", id);
 }
 
 /***en
     @brief Dump a Font Layout Table.
 
     The mdebug_dump_flt () function prints the Font Layout Table $FLT
-    in a human readable way to the stderr.  $INDENT specifies how many
-    columns to indent the lines but the first one.
+    in a human readable way to the stderr or to what specified by the
+    environment variable MDEBUG_OUTPUT_FILE.  $INDENT specifies how
+    many columns to indent the lines but the first one.
 
     @return
     This function returns $FLT.  */
@@ -3158,38 +3214,45 @@ mdebug_dump_flt (MFLT *flt, int indent)
 
   memset (prefix, 32, indent);
   prefix[indent] = 0;
-  fprintf (stderr, "(flt");
+  fprintf (mdebug__output, "(flt");
   MPLIST_DO (plist, flt->stages)
     {
       FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
       int i;
 
-      fprintf (stderr, "\n%s  (stage %d", prefix, stage_idx);
+      fprintf (mdebug__output, "\n%s  (stage %d", prefix, stage_idx);
       for (i = 0; i < stage->used; i++)
        {
-         fprintf (stderr, "\n%s    ", prefix);
+         fprintf (mdebug__output, "\n%s    ", prefix);
          dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
        }
-      fprintf (stderr, ")");
+      fprintf (mdebug__output, ")");
       stage_idx++;
     }
-  fprintf (stderr, ")");
+  fprintf (mdebug__output, ")");
   return flt;
 }
 
+/***en
+    @brief Dump an MFLTGlyphString.
+
+    The mflt_dump_gstring () function prints the glyph sequence
+    $GSTRING in a human readable way to the stderr or to what
+    specified by the environment variable MDEBUG_OUTPUT_FILE.  */
+
 void
 mflt_dump_gstring (MFLTGlyphString *gstring)
 {
   int i;
 
-  fprintf (stderr, "(flt-gstring");
+  fprintf (mdebug__output, "(flt-gstring");
   for (i = 0; i < gstring->used; i++)
     {
       MFLTGlyph *g = GREF (gstring, i);
-      fprintf (stderr, "\n  (%02d pos:%d-%d c:%04X code:%04X cat:%c)",
+      fprintf (mdebug__output, "\n  (%02d pos:%d-%d c:%04X code:%04X cat:%c)",
               i, g->from, g->to, g->c, g->code, GET_CATEGORY_CODE (g));
     }
-  fprintf (stderr, ")\n");
+  fprintf (mdebug__output, ")\n");
 }
 
 /*** @} */