1 /*
   2  * Copyright © 2018  Ebrahim Byagowi
   3  *
   4  *  This is part of HarfBuzz, a text shaping 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 
  25 #ifndef HB_AAT_LAYOUT_JUST_TABLE_HH
  26 #define HB_AAT_LAYOUT_JUST_TABLE_HH
  27 
  28 #include "hb-aat-layout-common.hh"
  29 #include "hb-ot-layout.hh"
  30 #include "hb-open-type.hh"
  31 
  32 #include "hb-aat-layout-morx-table.hh"
  33 
  34 /*
  35  * just -- Justification
  36  * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6just.html
  37  */
  38 #define HB_AAT_TAG_just HB_TAG('j','u','s','t')
  39 
  40 
  41 namespace AAT {
  42 
  43 using namespace OT;
  44 
  45 
  46 struct ActionSubrecordHeader
  47 {
  48   bool sanitize (hb_sanitize_context_t *c) const
  49   {
  50     TRACE_SANITIZE (this);
  51     return_trace (likely (c->check_struct (this)));
  52   }
  53 
  54   HBUINT16      actionClass;    /* The JustClass value associated with this
  55                                  * ActionSubrecord. */
  56   HBUINT16      actionType;     /* The type of postcompensation action. */
  57   HBUINT16      actionLength;   /* Length of this ActionSubrecord record, which
  58                                  * must be a multiple of 4. */
  59   public:
  60   DEFINE_SIZE_STATIC (6);
  61 };
  62 
  63 struct DecompositionAction
  64 {
  65   bool sanitize (hb_sanitize_context_t *c) const
  66   {
  67     TRACE_SANITIZE (this);
  68     return_trace (likely (c->check_struct (this)));
  69   }
  70 
  71   ActionSubrecordHeader
  72                 header;
  73   Fixed         lowerLimit;     /* If the distance factor is less than this value,
  74                                  * then the ligature is decomposed. */
  75   Fixed         upperLimit;     /* If the distance factor is greater than this value,
  76                                  * then the ligature is decomposed. */
  77   HBUINT16      order;          /* Numerical order in which this ligature will
  78                                  * be decomposed; you may want infrequent ligatures
  79                                  * to decompose before more frequent ones. The ligatures
  80                                  * on the line of text will decompose in increasing
  81                                  * value of this field. */
  82   ArrayOf<HBUINT16>
  83                 decomposedglyphs;
  84                                 /* Number of 16-bit glyph indexes that follow;
  85                                  * the ligature will be decomposed into these glyphs.
  86                                  *
  87                                  * Array of decomposed glyphs. */
  88   public:
  89   DEFINE_SIZE_ARRAY (18, decomposedglyphs);
  90 };
  91 
  92 struct UnconditionalAddGlyphAction
  93 {
  94   bool sanitize (hb_sanitize_context_t *c) const
  95   {
  96     TRACE_SANITIZE (this);
  97     return_trace (c->check_struct (this));
  98   }
  99 
 100   protected:
 101   ActionSubrecordHeader
 102                 header;
 103   GlyphID       addGlyph;       /* Glyph that should be added if the distance factor
 104                                  * is growing. */
 105 
 106   public:
 107   DEFINE_SIZE_STATIC (8);
 108 };
 109 
 110 struct ConditionalAddGlyphAction
 111 {
 112   bool sanitize (hb_sanitize_context_t *c) const
 113   {
 114     TRACE_SANITIZE (this);
 115     return_trace (likely (c->check_struct (this)));
 116   }
 117 
 118   protected:
 119   ActionSubrecordHeader
 120                 header;
 121   Fixed         substThreshold; /* Distance growth factor (in ems) at which
 122                                  * this glyph is replaced and the growth factor
 123                                  * recalculated. */
 124   GlyphID       addGlyph;       /* Glyph to be added as kashida. If this value is
 125                                  * 0xFFFF, no extra glyph will be added. Note that
 126                                  * generally when a glyph is added, justification
 127                                  * will need to be redone. */
 128   GlyphID       substGlyph;     /* Glyph to be substituted for this glyph if the
 129                                  * growth factor equals or exceeds the value of
 130                                  * substThreshold. */
 131   public:
 132   DEFINE_SIZE_STATIC (14);
 133 };
 134 
 135 struct DuctileGlyphAction
 136 {
 137   bool sanitize (hb_sanitize_context_t *c) const
 138   {
 139     TRACE_SANITIZE (this);
 140     return_trace (likely (c->check_struct (this)));
 141   }
 142 
 143   protected:
 144   ActionSubrecordHeader
 145                 header;
 146   HBUINT32      variationAxis;  /* The 4-byte tag identifying the ductile axis.
 147                                  * This would normally be 0x64756374 ('duct'),
 148                                  * but you may use any axis the font contains. */
 149   Fixed         minimumLimit;   /* The lowest value for the ductility axis tha
 150                                  * still yields an acceptable appearance. Normally
 151                                  * this will be 1.0. */
 152   Fixed         noStretchValue; /* This is the default value that corresponds to
 153                                  * no change in appearance. Normally, this will
 154                                  * be 1.0. */
 155   Fixed         maximumLimit;   /* The highest value for the ductility axis that
 156                                  * still yields an acceptable appearance. */
 157   public:
 158   DEFINE_SIZE_STATIC (22);
 159 };
 160 
 161 struct RepeatedAddGlyphAction
 162 {
 163   bool sanitize (hb_sanitize_context_t *c) const
 164   {
 165     TRACE_SANITIZE (this);
 166     return_trace (likely (c->check_struct (this)));
 167   }
 168 
 169   protected:
 170   ActionSubrecordHeader
 171                 header;
 172   HBUINT16      flags;          /* Currently unused; set to 0. */
 173   GlyphID       glyph;          /* Glyph that should be added if the distance factor
 174                                  * is growing. */
 175   public:
 176   DEFINE_SIZE_STATIC (10);
 177 };
 178 
 179 struct ActionSubrecord
 180 {
 181   unsigned int get_length () const { return u.header.actionLength; }
 182 
 183   bool sanitize (hb_sanitize_context_t *c) const
 184   {
 185     TRACE_SANITIZE (this);
 186     if (unlikely (!c->check_struct (this)))
 187       return_trace (false);
 188 
 189     switch (u.header.actionType)
 190     {
 191     case 0:  return_trace (u.decompositionAction.sanitize (c));
 192     case 1:  return_trace (u.unconditionalAddGlyphAction.sanitize (c));
 193     case 2:  return_trace (u.conditionalAddGlyphAction.sanitize (c));
 194     // case 3: return_trace (u.stretchGlyphAction.sanitize (c));
 195     case 4:  return_trace (u.decompositionAction.sanitize (c));
 196     case 5:  return_trace (u.decompositionAction.sanitize (c));
 197     default: return_trace (true);
 198     }
 199   }
 200 
 201   protected:
 202   union {
 203   ActionSubrecordHeader         header;
 204   DecompositionAction           decompositionAction;
 205   UnconditionalAddGlyphAction   unconditionalAddGlyphAction;
 206   ConditionalAddGlyphAction     conditionalAddGlyphAction;
 207   /* StretchGlyphAction stretchGlyphAction; -- Not supported by CoreText */
 208   DuctileGlyphAction            ductileGlyphAction;
 209   RepeatedAddGlyphAction        repeatedAddGlyphAction;
 210   } u;                          /* Data. The format of this data depends on
 211                                  * the value of the actionType field. */
 212   public:
 213   DEFINE_SIZE_UNION (6, header);
 214 };
 215 
 216 struct PostcompensationActionChain
 217 {
 218   bool sanitize (hb_sanitize_context_t *c) const
 219   {
 220     TRACE_SANITIZE (this);
 221     if (unlikely (!c->check_struct (this)))
 222       return_trace (false);
 223 
 224     unsigned int offset = min_size;
 225     for (unsigned int i = 0; i < count; i++)
 226     {
 227       const ActionSubrecord& subrecord = StructAtOffset<ActionSubrecord> (this, offset);
 228       if (unlikely (!subrecord.sanitize (c))) return_trace (false);
 229       offset += subrecord.get_length ();
 230     }
 231 
 232     return_trace (true);
 233   }
 234 
 235   protected:
 236   HBUINT32      count;
 237 
 238   public:
 239   DEFINE_SIZE_STATIC (4);
 240 };
 241 
 242 struct JustWidthDeltaEntry
 243 {
 244   enum Flags
 245   {
 246     Reserved1           =0xE000,/* Reserved. You should set these bits to zero. */
 247     UnlimiteGap         =0x1000,/* The glyph can take unlimited gap. When this
 248                                  * glyph participates in the justification process,
 249                                  * it and any other glyphs on the line having this
 250                                  * bit set absorb all the remaining gap. */
 251     Reserved2           =0x0FF0,/* Reserved. You should set these bits to zero. */
 252     Priority            =0x000F /* The justification priority of the glyph. */
 253   };
 254 
 255   enum Priority
 256   {
 257     Kashida             = 0,    /* Kashida priority. This is the highest priority
 258                                  * during justification. */
 259     Whitespace          = 1,    /* Whitespace priority. Any whitespace glyphs (as
 260                                  * identified in the glyph properties table) will
 261                                  * get this priority. */
 262     InterCharacter      = 2,    /* Inter-character priority. Give this to any
 263                                  * remaining glyphs. */
 264     NullPriority        = 3     /* Null priority. You should set this priority for
 265                                  * glyphs that only participate in justification
 266                                  * after the above priorities. Normally all glyphs
 267                                  * have one of the previous three values. If you
 268                                  * don't want a glyph to participate in justification,
 269                                  * and you don't want to set its factors to zero,
 270                                  * you may instead assign it to the null priority. */
 271   };
 272 
 273   protected:
 274   Fixed         beforeGrowLimit;/* The ratio by which the advance width of the
 275                                  * glyph is permitted to grow on the left or top side. */
 276   Fixed         beforeShrinkLimit;
 277                                 /* The ratio by which the advance width of the
 278                                  * glyph is permitted to shrink on the left or top side. */
 279   Fixed         afterGrowLimit; /* The ratio by which the advance width of the glyph
 280                                  * is permitted to shrink on the left or top side. */
 281   Fixed         afterShrinkLimit;
 282                                 /* The ratio by which the advance width of the glyph
 283                                  * is at most permitted to shrink on the right or
 284                                  * bottom side. */
 285   HBUINT16      growFlags;      /* Flags controlling the grow case. */
 286   HBUINT16      shrinkFlags;    /* Flags controlling the shrink case. */
 287 
 288   public:
 289   DEFINE_SIZE_STATIC (20);
 290 };
 291 
 292 struct WidthDeltaPair
 293 {
 294   bool sanitize (hb_sanitize_context_t *c) const
 295   {
 296     TRACE_SANITIZE (this);
 297     return_trace (likely (c->check_struct (this)));
 298   }
 299 
 300   protected:
 301   HBUINT32      justClass;      /* The justification category associated
 302                                  * with the wdRecord field. Only 7 bits of
 303                                  * this field are used. (The other bits are
 304                                  * used as padding to guarantee longword
 305                                  * alignment of the following record). */
 306   JustWidthDeltaEntry
 307                 wdRecord;       /* The actual width delta record. */
 308 
 309   public:
 310   DEFINE_SIZE_STATIC (24);
 311 };
 312 
 313 typedef OT::LArrayOf<WidthDeltaPair> WidthDeltaCluster;
 314 
 315 struct JustificationCategory
 316 {
 317   typedef void EntryData;
 318 
 319   enum Flags
 320   {
 321     SetMark             =0x8000,/* If set, make the current glyph the marked
 322                                  * glyph. */
 323     DontAdvance         =0x4000,/* If set, don't advance to the next glyph before
 324                                  * going to the new state. */
 325     MarkCategory        =0x3F80,/* The justification category for the marked
 326                                  * glyph if nonzero. */
 327     CurrentCategory     =0x007F /* The justification category for the current
 328                                  * glyph if nonzero. */
 329   };
 330 
 331   bool sanitize (hb_sanitize_context_t *c, const void *base) const
 332   {
 333     TRACE_SANITIZE (this);
 334     return_trace (likely (c->check_struct (this) &&
 335                           morphHeader.sanitize (c) &&
 336                           stHeader.sanitize (c)));
 337   }
 338 
 339   protected:
 340   ChainSubtable<ObsoleteTypes>
 341                 morphHeader;    /* Metamorphosis-style subtable header. */
 342   StateTable<ObsoleteTypes, EntryData>
 343                 stHeader;       /* The justification insertion state table header */
 344   public:
 345   DEFINE_SIZE_STATIC (30);
 346 };
 347 
 348 struct JustificationHeader
 349 {
 350   bool sanitize (hb_sanitize_context_t *c, const void *base) const
 351   {
 352     TRACE_SANITIZE (this);
 353     return_trace (likely (c->check_struct (this) &&
 354                           justClassTable.sanitize (c, base, base) &&
 355                           wdcTable.sanitize (c, base) &&
 356                           pcTable.sanitize (c, base) &&
 357                           lookupTable.sanitize (c, base)));
 358   }
 359 
 360   protected:
 361   OffsetTo<JustificationCategory>
 362                 justClassTable; /* Offset to the justification category state table. */
 363   OffsetTo<WidthDeltaCluster>
 364                 wdcTable;       /* Offset from start of justification table to start
 365                                  * of the subtable containing the width delta factors
 366                                  * for the glyphs in your font.
 367                                  *
 368                                  * The width delta clusters table. */
 369   OffsetTo<PostcompensationActionChain>
 370                 pcTable;        /* Offset from start of justification table to start
 371                                  * of postcompensation subtable (set to zero if none).
 372                                  *
 373                                  * The postcompensation subtable, if present in the font. */
 374   Lookup<OffsetTo<WidthDeltaCluster> >
 375                 lookupTable;    /* Lookup table associating glyphs with width delta
 376                                  * clusters. See the description of Width Delta Clusters
 377                                  * table for details on how to interpret the lookup values. */
 378 
 379   public:
 380   DEFINE_SIZE_MIN (8);
 381 };
 382 
 383 struct just
 384 {
 385   static constexpr hb_tag_t tableTag = HB_AAT_TAG_just;
 386 
 387   bool sanitize (hb_sanitize_context_t *c) const
 388   {
 389     TRACE_SANITIZE (this);
 390 
 391     return_trace (likely (c->check_struct (this) &&
 392                           version.major == 1 &&
 393                           horizData.sanitize (c, this, this) &&
 394                           vertData.sanitize (c, this, this)));
 395   }
 396 
 397   protected:
 398   FixedVersion<>version;        /* Version of the justification table
 399                                  * (0x00010000u for version 1.0). */
 400   HBUINT16      format;         /* Format of the justification table (set to 0). */
 401   OffsetTo<JustificationHeader>
 402                 horizData;      /* Byte offset from the start of the justification table
 403                                  * to the header for tables that contain justification
 404                                  * information for horizontal text.
 405                                  * If you are not including this information,
 406                                  * store 0. */
 407   OffsetTo<JustificationHeader>
 408                 vertData;       /* ditto, vertical */
 409 
 410   public:
 411   DEFINE_SIZE_STATIC (10);
 412 };
 413 
 414 } /* namespace AAT */
 415 
 416 
 417 #endif /* HB_AAT_LAYOUT_JUST_TABLE_HH */