src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-cmap-table.hh
author erikj
Tue, 12 Sep 2017 19:03:39 +0200
changeset 47216 71c04702a3d5
parent 40435 jdk/src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-cmap-table.hh@553eb1a50733
child 48274 51772bf1fb0c
permissions -rw-r--r--
8187443: Forest Consolidation: Move files to unified layout Reviewed-by: darcy, ihse

/*
 * Copyright © 2014  Google, Inc.
 *
 *  This is part of HarfBuzz, a text shaping library.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Google Author(s): Behdad Esfahbod
 */

#ifndef HB_OT_CMAP_TABLE_HH
#define HB_OT_CMAP_TABLE_HH

#include "hb-open-type-private.hh"


namespace OT {


/*
 * cmap -- Character To Glyph Index Mapping Table
 */

#define HB_OT_TAG_cmap HB_TAG('c','m','a','p')


struct CmapSubtableFormat0
{
  inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
  {
    hb_codepoint_t gid = codepoint < 256 ? glyphIdArray[codepoint] : 0;
    if (!gid)
      return false;
    *glyph = gid;
    return true;
  }

  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this));
  }

  protected:
  USHORT        format;         /* Format number is set to 0. */
  USHORT        lengthZ;        /* Byte length of this subtable. */
  USHORT        languageZ;      /* Ignore. */
  BYTE          glyphIdArray[256];/* An array that maps character
                                 * code to glyph index values. */
  public:
  DEFINE_SIZE_STATIC (6 + 256);
};

struct CmapSubtableFormat4
{
  struct accelerator_t
  {
    inline void init (const CmapSubtableFormat4 *subtable)
    {
      segCount = subtable->segCountX2 / 2;
      endCount = subtable->values;
      startCount = endCount + segCount + 1;
      idDelta = startCount + segCount;
      idRangeOffset = idDelta + segCount;
      glyphIdArray = idRangeOffset + segCount;
      glyphIdArrayLength = (subtable->length - 16 - 8 * segCount) / 2;
    }

    static inline bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph)
    {
      const accelerator_t *thiz = (const accelerator_t *) obj;

      /* Custom two-array bsearch. */
      int min = 0, max = (int) thiz->segCount - 1;
      const USHORT *startCount = thiz->startCount;
      const USHORT *endCount = thiz->endCount;
      unsigned int i;
      while (min <= max)
      {
        int mid = (min + max) / 2;
        if (codepoint < startCount[mid])
          max = mid - 1;
        else if (codepoint > endCount[mid])
          min = mid + 1;
        else
        {
          i = mid;
          goto found;
        }
      }
      return false;

    found:
      hb_codepoint_t gid;
      unsigned int rangeOffset = thiz->idRangeOffset[i];
      if (rangeOffset == 0)
        gid = codepoint + thiz->idDelta[i];
      else
      {
        /* Somebody has been smoking... */
        unsigned int index = rangeOffset / 2 + (codepoint - thiz->startCount[i]) + i - thiz->segCount;
        if (unlikely (index >= thiz->glyphIdArrayLength))
          return false;
        gid = thiz->glyphIdArray[index];
        if (unlikely (!gid))
          return false;
        gid += thiz->idDelta[i];
      }

      *glyph = gid & 0xFFFFu;
      return true;
    }

    const USHORT *endCount;
    const USHORT *startCount;
    const USHORT *idDelta;
    const USHORT *idRangeOffset;
    const USHORT *glyphIdArray;
    unsigned int segCount;
    unsigned int glyphIdArrayLength;
  };

  inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
  {
    accelerator_t accel;
    accel.init (this);
    return accel.get_glyph_func (&accel, codepoint, glyph);
  }

  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    if (unlikely (!c->check_struct (this)))
      return_trace (false);

    if (unlikely (!c->check_range (this, length)))
    {
      /* Some broken fonts have too long of a "length" value.
       * If that is the case, just change the value to truncate
       * the subtable at the end of the blob. */
      uint16_t new_length = (uint16_t) MIN ((uintptr_t) 65535,
                                            (uintptr_t) (c->end -
                                                         (char *) this));
      if (!c->try_set (&length, new_length))
        return_trace (false);
    }

    return_trace (16 + 4 * (unsigned int) segCountX2 <= length);
  }

  protected:
  USHORT        format;         /* Format number is set to 4. */
  USHORT        length;         /* This is the length in bytes of the
                                 * subtable. */
  USHORT        languageZ;      /* Ignore. */
  USHORT        segCountX2;     /* 2 x segCount. */
  USHORT        searchRangeZ;   /* 2 * (2**floor(log2(segCount))) */
  USHORT        entrySelectorZ; /* log2(searchRange/2) */
  USHORT        rangeShiftZ;    /* 2 x segCount - searchRange */

  USHORT        values[VAR];
#if 0
  USHORT        endCount[segCount];     /* End characterCode for each segment,
                                         * last=0xFFFFu. */
  USHORT        reservedPad;            /* Set to 0. */
  USHORT        startCount[segCount];   /* Start character code for each segment. */
  SHORT         idDelta[segCount];      /* Delta for all character codes in segment. */
  USHORT        idRangeOffset[segCount];/* Offsets into glyphIdArray or 0 */
  USHORT        glyphIdArray[VAR];      /* Glyph index array (arbitrary length) */
#endif

  public:
  DEFINE_SIZE_ARRAY (14, values);
};

struct CmapSubtableLongGroup
{
  friend struct CmapSubtableFormat12;
  friend struct CmapSubtableFormat13;

  int cmp (hb_codepoint_t codepoint) const
  {
    if (codepoint < startCharCode) return -1;
    if (codepoint > endCharCode)   return +1;
    return 0;
  }

  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this));
  }

  private:
  ULONG         startCharCode;  /* First character code in this group. */
  ULONG         endCharCode;    /* Last character code in this group. */
  ULONG         glyphID;        /* Glyph index; interpretation depends on
                                 * subtable format. */
  public:
  DEFINE_SIZE_STATIC (12);
};

template <typename UINT>
struct CmapSubtableTrimmed
{
  inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
  {
    /* Rely on our implicit array bound-checking. */
    hb_codepoint_t gid = glyphIdArray[codepoint - startCharCode];
    if (!gid)
      return false;
    *glyph = gid;
    return true;
  }

  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) && glyphIdArray.sanitize (c));
  }

  protected:
  UINT          formatReserved; /* Subtable format and (maybe) padding. */
  UINT          lengthZ;        /* Byte length of this subtable. */
  UINT          languageZ;      /* Ignore. */
  UINT          startCharCode;  /* First character code covered. */
  ArrayOf<GlyphID, UINT>
                glyphIdArray;   /* Array of glyph index values for character
                                 * codes in the range. */
  public:
  DEFINE_SIZE_ARRAY (5 * sizeof (UINT), glyphIdArray);
};

struct CmapSubtableFormat6  : CmapSubtableTrimmed<USHORT> {};
struct CmapSubtableFormat10 : CmapSubtableTrimmed<ULONG > {};

template <typename T>
struct CmapSubtableLongSegmented
{
  inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
  {
    int i = groups.bsearch (codepoint);
    if (i == -1)
      return false;
    *glyph = T::group_get_glyph (groups[i], codepoint);
    return true;
  }

  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) && groups.sanitize (c));
  }

  protected:
  USHORT        format;         /* Subtable format; set to 12. */
  USHORT        reservedZ;      /* Reserved; set to 0. */
  ULONG         lengthZ;        /* Byte length of this subtable. */
  ULONG         languageZ;      /* Ignore. */
  SortedArrayOf<CmapSubtableLongGroup, ULONG>
                groups;         /* Groupings. */
  public:
  DEFINE_SIZE_ARRAY (16, groups);
};

struct CmapSubtableFormat12 : CmapSubtableLongSegmented<CmapSubtableFormat12>
{
  static inline hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group,
                                                hb_codepoint_t u)
  { return group.glyphID + (u - group.startCharCode); }
};

struct CmapSubtableFormat13 : CmapSubtableLongSegmented<CmapSubtableFormat13>
{
  static inline hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group,
                                                hb_codepoint_t u HB_UNUSED)
  { return group.glyphID; }
};

typedef enum
{
  GLYPH_VARIANT_NOT_FOUND = 0,
  GLYPH_VARIANT_FOUND = 1,
  GLYPH_VARIANT_USE_DEFAULT = 2
} glyph_variant_t;

struct UnicodeValueRange
{
  inline int cmp (const hb_codepoint_t &codepoint) const
  {
    if (codepoint < startUnicodeValue) return -1;
    if (codepoint > startUnicodeValue + additionalCount) return +1;
    return 0;
  }

  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this));
  }

  UINT24        startUnicodeValue;      /* First value in this range. */
  BYTE          additionalCount;        /* Number of additional values in this
                                         * range. */
  public:
  DEFINE_SIZE_STATIC (4);
};

typedef SortedArrayOf<UnicodeValueRange, ULONG> DefaultUVS;

struct UVSMapping
{
  inline int cmp (const hb_codepoint_t &codepoint) const
  {
    return unicodeValue.cmp (codepoint);
  }

  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this));
  }

  UINT24        unicodeValue;   /* Base Unicode value of the UVS */
  GlyphID       glyphID;        /* Glyph ID of the UVS */
  public:
  DEFINE_SIZE_STATIC (5);
};

typedef SortedArrayOf<UVSMapping, ULONG> NonDefaultUVS;

struct VariationSelectorRecord
{
  inline glyph_variant_t get_glyph (hb_codepoint_t codepoint,
                                    hb_codepoint_t *glyph,
                                    const void *base) const
  {
    int i;
    const DefaultUVS &defaults = base+defaultUVS;
    i = defaults.bsearch (codepoint);
    if (i != -1)
      return GLYPH_VARIANT_USE_DEFAULT;
    const NonDefaultUVS &nonDefaults = base+nonDefaultUVS;
    i = nonDefaults.bsearch (codepoint);
    if (i != -1)
    {
      *glyph = nonDefaults[i].glyphID;
       return GLYPH_VARIANT_FOUND;
    }
    return GLYPH_VARIANT_NOT_FOUND;
  }

  inline int cmp (const hb_codepoint_t &variation_selector) const
  {
    return varSelector.cmp (variation_selector);
  }

  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) &&
                  defaultUVS.sanitize (c, base) &&
                  nonDefaultUVS.sanitize (c, base));
  }

  UINT24        varSelector;    /* Variation selector. */
  OffsetTo<DefaultUVS, ULONG>
                defaultUVS;     /* Offset to Default UVS Table. May be 0. */
  OffsetTo<NonDefaultUVS, ULONG>
                nonDefaultUVS;  /* Offset to Non-Default UVS Table. May be 0. */
  public:
  DEFINE_SIZE_STATIC (11);
};

struct CmapSubtableFormat14
{
  inline glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint,
                                            hb_codepoint_t variation_selector,
                                            hb_codepoint_t *glyph) const
  {
    return record[record.bsearch(variation_selector)].get_glyph (codepoint, glyph, this);
  }

  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) &&
                  record.sanitize (c, this));
  }

  protected:
  USHORT        format;         /* Format number is set to 14. */
  ULONG         lengthZ;        /* Byte length of this subtable. */
  SortedArrayOf<VariationSelectorRecord, ULONG>
                record;         /* Variation selector records; sorted
                                 * in increasing order of `varSelector'. */
  public:
  DEFINE_SIZE_ARRAY (10, record);
};

struct CmapSubtable
{
  /* Note: We intentionally do NOT implement subtable formats 2 and 8. */

  inline bool get_glyph (hb_codepoint_t codepoint,
                         hb_codepoint_t *glyph) const
  {
    switch (u.format) {
    case  0: return u.format0 .get_glyph(codepoint, glyph);
    case  4: return u.format4 .get_glyph(codepoint, glyph);
    case  6: return u.format6 .get_glyph(codepoint, glyph);
    case 10: return u.format10.get_glyph(codepoint, glyph);
    case 12: return u.format12.get_glyph(codepoint, glyph);
    case 13: return u.format13.get_glyph(codepoint, glyph);
    case 14:
    default: return false;
    }
  }

  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    if (!u.format.sanitize (c)) return_trace (false);
    switch (u.format) {
    case  0: return_trace (u.format0 .sanitize (c));
    case  4: return_trace (u.format4 .sanitize (c));
    case  6: return_trace (u.format6 .sanitize (c));
    case 10: return_trace (u.format10.sanitize (c));
    case 12: return_trace (u.format12.sanitize (c));
    case 13: return_trace (u.format13.sanitize (c));
    case 14: return_trace (u.format14.sanitize (c));
    default:return_trace (true);
    }
  }

  public:
  union {
  USHORT                format;         /* Format identifier */
  CmapSubtableFormat0   format0;
  CmapSubtableFormat4   format4;
  CmapSubtableFormat6   format6;
  CmapSubtableFormat10  format10;
  CmapSubtableFormat12  format12;
  CmapSubtableFormat13  format13;
  CmapSubtableFormat14  format14;
  } u;
  public:
  DEFINE_SIZE_UNION (2, format);
};


struct EncodingRecord
{
  inline int cmp (const EncodingRecord &other) const
  {
    int ret;
    ret = platformID.cmp (other.platformID);
    if (ret) return ret;
    ret = encodingID.cmp (other.encodingID);
    if (ret) return ret;
    return 0;
  }

  inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) &&
                  subtable.sanitize (c, base));
  }

  USHORT        platformID;     /* Platform ID. */
  USHORT        encodingID;     /* Platform-specific encoding ID. */
  OffsetTo<CmapSubtable, ULONG>
                subtable;       /* Byte offset from beginning of table to the subtable for this encoding. */
  public:
  DEFINE_SIZE_STATIC (8);
};

struct cmap
{
  static const hb_tag_t tableTag        = HB_OT_TAG_cmap;

  inline const CmapSubtable *find_subtable (unsigned int platform_id,
                                            unsigned int encoding_id) const
  {
    EncodingRecord key;
    key.platformID.set (platform_id);
    key.encodingID.set (encoding_id);

    /* Note: We can use bsearch, but since it has no performance
     * implications, we use lsearch and as such accept fonts with
     * unsorted subtable list. */
    int result = encodingRecord./*bsearch*/lsearch (key);
    if (result == -1 || !encodingRecord[result].subtable)
      return NULL;

    return &(this+encodingRecord[result].subtable);
  }

  inline bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this) &&
                  likely (version == 0) &&
                  encodingRecord.sanitize (c, this));
  }

  USHORT                version;        /* Table version number (0). */
  SortedArrayOf<EncodingRecord>
                        encodingRecord; /* Encoding tables. */
  public:
  DEFINE_SIZE_ARRAY (4, encodingRecord);
};


} /* namespace OT */


#endif /* HB_OT_CMAP_TABLE_HH */