CMapFormat4(Buffer buffer, int offset) {

      buffer.position(offset);
      buffer.getChar(); // skip, we already know format=4
      int subtableLength = buffer.getChar();
      /* Try to recover from some bad fonts which specify a subtable
       * length that would overflow the byte buffer holding the whole
       * cmap table. If this isn't a recoverable situation an exception
       * may be thrown which is caught higher up the call stack.
       * Whilst this may seem lenient, in practice, unless the "bad"
       * subtable we are using is the last one in the cmap table we
       * would have no way of knowing about this problem anyway.
       */
      if (offset + subtableLength > buffer.capacity()) {
        subtableLength = buffer.capacity() - offset;
      }
      buffer.getChar(); // skip language
      segCount = buffer.getChar() / 2;
      buffer.getChar(); // skip searchRange
      entrySelector = buffer.getChar();
      rangeShift = buffer.getChar() / 2;
      startCount = new char[segCount];
      endCount = new char[segCount];
      idDelta = new short[segCount];
      idRangeOffset = new char[segCount];

      for (int i = 0; i < segCount; i++) {
        endCount[i] = buffer.getChar();
      }
      buffer.getChar(); // 2 bytes for reserved pad
      for (int i = 0; i < segCount; i++) {
        startCount[i] = buffer.getChar();
      }

      for (int i = 0; i < segCount; i++) {
        idDelta[i] = (short) buffer.getChar();
      }

      for (int i = 0; i < segCount; i++) {
        char ctmp = buffer.getChar();
        idRangeOffset[i] = (char) ((ctmp >> 1) & 0xffff);
      }
      /* Can calculate the number of glyph IDs by subtracting
       * "pos" from the length of the cmap
       */
      int pos = (segCount * 8 + 16) / 2;
      buffer.position(pos * 2 + offset); // * 2 for chars
      int numGlyphIds = (subtableLength / 2 - pos);
      glyphIds = new char[numGlyphIds];
      for (int i = 0; i < numGlyphIds; i++) {
        glyphIds[i] = buffer.getChar();
      }
    }
    CMapFormat2(Buffer buffer, int offset) {

      int tableLen = buffer.getChar(offset + 2);
      buffer.position(offset + 6);
      char maxSubHeader = 0;
      for (int i = 0; i < 256; i++) {
        subHeaderKey[i] = buffer.getChar();
        if (subHeaderKey[i] > maxSubHeader) {
          maxSubHeader = subHeaderKey[i];
        }
      }
      /* The value of the subHeaderKey is 8 * the subHeader index,
       * so the number of subHeaders can be obtained by dividing
       * this value bv 8 and adding 1.
       */
      int numSubHeaders = (maxSubHeader >> 3) + 1;
      firstCodeArray = new char[numSubHeaders];
      entryCountArray = new char[numSubHeaders];
      idDeltaArray = new short[numSubHeaders];
      idRangeOffSetArray = new char[numSubHeaders];
      for (int i = 0; i < numSubHeaders; i++) {
        firstCodeArray[i] = buffer.getChar();
        entryCountArray[i] = buffer.getChar();
        idDeltaArray[i] = (short) buffer.getChar();
        idRangeOffSetArray[i] = buffer.getChar();
      }

      int glyphIndexArrSize = (tableLen - 518 - numSubHeaders * 8) / 2;
      glyphIndexArray = new char[glyphIndexArrSize];
      for (int i = 0; i < glyphIndexArrSize; i++) {
        glyphIndexArray[i] = buffer.getChar();
      }
    }
    CMapFormat10(Buffer buffer, int offset) {

      buffer.position(offset + 12);
      startCharCode = buffer.getInt() & INTMASK;
      numChars = buffer.getInt() & INTMASK;
      glyphIdArray = new char[numChars];
      for (int i = 0; i < numChars; i++) {
        glyphIdArray[i] = buffer.getChar();
      }
    }
    CMapFormat6(Buffer buffer, int offset) {

      buffer.position(offset + 6);
      firstCode = buffer.getChar();
      entryCount = buffer.getChar();
      glyphIdArray = new char[entryCount];
      for (int i = 0; i < entryCount; i++) {
        glyphIdArray[i] = buffer.getChar();
      }
    }
    CMapFormat12(Buffer buffer, int offset) {

      numGroups = buffer.getInt(offset + 12);
      startCharCode = new long[numGroups];
      endCharCode = new long[numGroups];
      startGlyphID = new int[numGroups];
      buffer.position(offset + 16);
      // REMIND: why slice ?
      // buffer = buffer.slice();
      for (int i = 0; i < numGroups; i++) {
        startCharCode[i] = buffer.getInt() & INTMASK;
        endCharCode[i] = buffer.getInt() & INTMASK;
        startGlyphID[i] = buffer.getInt() & INTMASK;
      }

      /* Finds the high bit by binary searching through the bits */
      int value = numGroups;

      if (value >= 1 << 16) {
        value >>= 16;
        highBit += 16;
      }

      if (value >= 1 << 8) {
        value >>= 8;
        highBit += 8;
      }

      if (value >= 1 << 4) {
        value >>= 4;
        highBit += 4;
      }

      if (value >= 1 << 2) {
        value >>= 2;
        highBit += 2;
      }

      if (value >= 1 << 1) {
        value >>= 1;
        highBit += 1;
      }

      power = 1 << highBit;
      extra = numGroups - power;
    }
  static CMap initialize(PrismFontFile font) {

    CMap cmap = null;

    int offset, platformID, encodingID = -1;

    int three0 = 0, three1 = 0, three10 = 0, zeroStarOffset = 0;
    boolean zeroStar = false, threeStar = false;

    Buffer cmapBuffer = font.readTable(FontConstants.cmapTag);
    short numberSubTables = cmapBuffer.getShort(2);

    /* Locate the offsets of supported 3,* Microsoft platform encodings,
     * and any 0,* Unicode platform encoding. The latter is used by
     * all current OS X fonts that don't have a Microsoft cmap.
     * We will always prefer the Microsoft cmap, for the fonts that
     * provide both. They ought to perform the same mappings. Although
     * I can imagine that a vendor might provide a different looking
     * glyph for some special characters for OS X vs Windows, I'm not
     * actually aware of any such case.
     */
    for (int i = 0; i < numberSubTables; i++) {
      cmapBuffer.position(i * 8 + 4);
      platformID = cmapBuffer.getShort();

      if (platformID == 0) {
        zeroStar = true;
        encodingID = cmapBuffer.getShort();
        zeroStarOffset = cmapBuffer.getInt();
      } else if (platformID == 3) {
        threeStar = true;
        encodingID = cmapBuffer.getShort();
        offset = cmapBuffer.getInt();
        switch (encodingID) {
          case 0:
            three0 = offset;
            break; // MS Symbol encoding
          case 1:
            three1 = offset;
            break; // MS Unicode cmap
          case 10:
            three10 = offset;
            break; // MS Unicode surrogates
        }
      }
    }

    /* This defines the preference order for cmap subtables */
    if (threeStar) {
      if (three10 != 0) {
        cmap = createCMap(cmapBuffer, three10);
      } else if (three0 != 0) {
        cmap = createCMap(cmapBuffer, three0);
      } else if (three1 != 0) {
        cmap = createCMap(cmapBuffer, three1);
      }
    } else if (zeroStar && zeroStarOffset != 0) {
      cmap = createCMap(cmapBuffer, zeroStarOffset);
    } else {
      /* No 0,* or supported 3,* subtable was found.
       * Use whatever is the first table listed.
       * Since these are supposed to be sorted, there's a good chance
       * it will be Mac Roman (1,0). If its not that then its
       * likely a really old font but not one that's found on either
       * Windows or OS X
       * In fact I didn't even find any OS X font that supported
       * only (1,*).
       * So this seems likely to be an untravelled path which is
       * just as well given that its not likely to work properly.
       */
      cmap = createCMap(cmapBuffer, cmapBuffer.getInt(8));
    }
    return cmap;
  }