shithub: freetype+ttf2subf

ref: dbe4872bf0e21a88303cad237c931ea224633007
dir: /src/sfnt/ttcmap.c/

View raw version
/***************************************************************************/
/*                                                                         */
/*  ttcmap.c                                                               */
/*                                                                         */
/*    TrueType character mapping table (cmap) support (body).              */
/*                                                                         */
/*  Copyright 1996-1999 by                                                 */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used        */
/*  modified and distributed under the terms of the FreeType project       */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#include <ftdebug.h>

#include <ttload.h>
#include <ttcmap.h>
#include <tterrors.h>

/* required by the tracing mode */
#undef  FT_COMPONENT
#define FT_COMPONENT      trace_ttcmap


  static TT_UInt  code_to_index0( TT_CMapTable*  charmap, TT_ULong  char_code );
  static TT_UInt  code_to_index2( TT_CMapTable*  charmap, TT_ULong  char_code );
  static TT_UInt  code_to_index4( TT_CMapTable*  charmap, TT_ULong  char_code );
  static TT_UInt  code_to_index6( TT_CMapTable*  charmap, TT_ULong  char_code );      

  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    TT_CharMap_Load                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Loads a given TrueType character map into memory.                  */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face   :: A handle to the parent face object.                      */
  /*    stream :: A handle to the current stream object.                   */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    table  :: A pointer to a cmap object.                              */
  /*                                                                       */
  /* <Return>                                                              */
  /*    Error code.  0 means success.                                      */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The function assumes that the stream is already in use (i.e.,      */
  /*    opened).  In case of error, all partially allocated tables are     */
  /*    released.                                                          */
  /*                                                                       */
  LOCAL_FUNC
  TT_Error  TT_CharMap_Load( TT_Face        face,
                             TT_CMapTable*  cmap,
                             FT_Stream      stream )
  {
    TT_Error   error;
    FT_Memory  memory;
    TT_UShort  num_SH, num_Seg, i;

    TT_UShort  u, l;

    TT_CMap0*  cmap0;
    TT_CMap2*  cmap2;
    TT_CMap4*  cmap4;
    TT_CMap6*  cmap6;

    TT_CMap2SubHeader* cmap2sub;
    TT_CMap4Segment*   segments;


    if ( cmap->loaded )
      return TT_Err_Ok;

    memory = stream->memory;

    if ( FILE_Seek( cmap->offset ) )
      return error;

    switch ( cmap->format )
    {
    case 0:
      cmap0 = &cmap->c.cmap0;

      if ( ALLOC( cmap0->glyphIdArray, 256L )            ||
           FILE_Read( (void*)cmap0->glyphIdArray, 256L ) )
         goto Fail;

      cmap->get_index = code_to_index0;
      break;

    case 2:
      num_SH = 0;
      cmap2  = &cmap->c.cmap2;

      /* allocate subheader keys */

      if ( ALLOC_ARRAY( cmap2->subHeaderKeys, 256, TT_UShort ) ||
           ACCESS_Frame( 512L )                                )
        goto Fail;

      for ( i = 0; i < 256; i++ )
      {
        u = GET_UShort() / 8;
        cmap2->subHeaderKeys[i] = u;

        if ( num_SH < u )
          num_SH = u;
      }

      FORGET_Frame();

      /* load subheaders */

      cmap2->numGlyphId = l =
        ( ( cmap->length - 2L*(256+3) - num_SH*8L ) & 0xffff ) / 2;

      if ( ALLOC_ARRAY( cmap2->subHeaders,
                        num_SH + 1,
                        TT_CMap2SubHeader )    ||
           ACCESS_Frame( ( num_SH + 1 ) * 8L ) )
        goto Fail;

      cmap2sub = cmap2->subHeaders;

      for ( i = 0; i <= num_SH; i++ )
      {
        cmap2sub->firstCode     = GET_UShort();
        cmap2sub->entryCount    = GET_UShort();
        cmap2sub->idDelta       = GET_Short();
        /* we apply the location offset immediately */
        cmap2sub->idRangeOffset = GET_UShort() - ( num_SH - i ) * 8 - 2;

        cmap2sub++;
      }

      FORGET_Frame();

      /* load glyph IDs */

      if ( ALLOC_ARRAY( cmap2->glyphIdArray, l, TT_UShort ) ||
           ACCESS_Frame( l * 2L )                           )
        goto Fail;

      for ( i = 0; i < l; i++ )
        cmap2->glyphIdArray[i] = GET_UShort();

      FORGET_Frame();
      
      cmap->get_index = code_to_index2;
      break;

    case 4:
      cmap4 = &cmap->c.cmap4;

      /* load header */

      if ( ACCESS_Frame( 8L ) )
        goto Fail;

      cmap4->segCountX2    = GET_UShort();
      cmap4->searchRange   = GET_UShort();
      cmap4->entrySelector = GET_UShort();
      cmap4->rangeShift    = GET_UShort();

      num_Seg = cmap4->segCountX2 / 2;

      FORGET_Frame();

      /* load segments */

      if ( ALLOC_ARRAY( cmap4->segments,
                        num_Seg,
                        TT_CMap4Segment )         ||
           ACCESS_Frame( (num_Seg * 4 + 1) * 2L ) )
        goto Fail;

      segments = cmap4->segments;

      for ( i = 0; i < num_Seg; i++ )
        segments[i].endCount      = GET_UShort();

      (void)GET_UShort();

      for ( i = 0; i < num_Seg; i++ )
        segments[i].startCount    = GET_UShort();

      for ( i = 0; i < num_Seg; i++ )
        segments[i].idDelta       = GET_Short();

      for ( i = 0; i < num_Seg; i++ )
        segments[i].idRangeOffset = GET_UShort();

      FORGET_Frame();

      cmap4->numGlyphId = l =
        ( ( cmap->length - ( 16L + 8L * num_Seg ) ) & 0xFFFF ) /2;

      /* load IDs */

      if ( ALLOC_ARRAY( cmap4->glyphIdArray, l, TT_UShort ) ||
           ACCESS_Frame( l*2L )                             )
        goto Fail;

      for ( i = 0; i < l; i++ )
        cmap4->glyphIdArray[i] = GET_UShort();

      FORGET_Frame();
      
      cmap->get_index = code_to_index4;
      break;

    case 6:
      cmap6 = &cmap->c.cmap6;

      if ( ACCESS_Frame( 4L ) )
        goto Fail;

      cmap6->firstCode  = GET_UShort();
      cmap6->entryCount = GET_UShort();

      FORGET_Frame();

      l = cmap6->entryCount;

      if ( ALLOC_ARRAY( cmap6->glyphIdArray,
                        cmap6->entryCount,
                        TT_Short )           ||
           ACCESS_Frame( l * 2L )            )
        goto Fail;

      for ( i = 0; i < l; i++ )
        cmap6->glyphIdArray[i] = GET_UShort();

      FORGET_Frame();
      cmap->get_index = code_to_index6;
      break;

    default:   /* corrupt character mapping table */
      return TT_Err_Invalid_CharMap_Format;

    }
    return TT_Err_Ok;

  Fail:
    TT_CharMap_Free( face, cmap );
    return error;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    TT_CharMap_Free                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Destroys a character mapping table.                                */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face :: A handle to the parent face object.                        */
  /*    cmap :: A handle to a cmap object.                                 */
  /*                                                                       */
  /* <Return>                                                              */
  /*    Error code.  0 means success.                                      */
  /*                                                                       */
  LOCAL_FUNC
  TT_Error  TT_CharMap_Free( TT_Face        face,
                             TT_CMapTable*  cmap )
  {
    FT_Memory  memory;


    if ( !cmap )
      return TT_Err_Ok;

    memory = face->root.driver->memory;

    switch ( cmap->format )
    {
    case 0:
      FREE( cmap->c.cmap0.glyphIdArray );
      break;

    case 2:
      FREE( cmap->c.cmap2.subHeaderKeys );
      FREE( cmap->c.cmap2.subHeaders );
      FREE( cmap->c.cmap2.glyphIdArray );
      break;

    case 4:
      FREE( cmap->c.cmap4.segments );
      FREE( cmap->c.cmap4.glyphIdArray );
      cmap->c.cmap4.segCountX2 = 0;
      break;

    case 6:
      FREE( cmap->c.cmap6.glyphIdArray );
      cmap->c.cmap6.entryCount = 0;
      break;

    default:
      /* invalid table format, do nothing */
      ;
    }

    cmap->loaded = FALSE;
    return TT_Err_Ok;
  }



  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    code_to_index0                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Converts the character code into a glyph index.  Uses format 0.    */
  /*    `charCode' must be in the range 0x00-0xFF (otherwise 0 is          */
  /*    returned).                                                         */
  /*                                                                       */
  /* <Input>                                                               */
  /*    charCode :: The wanted character code.                             */
  /*    cmap0    :: A pointer to a cmap table in format 0.                 */
  /*                                                                       */
  /* <Return>                                                              */
  /*    Glyph index into the glyphs array.  0 if the glyph does not        */
  /*    exist.                                                             */
  /*                                                                       */
  static
  TT_UInt code_to_index0( TT_CMapTable*  cmap,
                          TT_ULong       charCode )
  {
    TT_CMap0*  cmap0 = &cmap->c.cmap0;
    
    return ( charCode <= 0xFF ? cmap0->glyphIdArray[charCode] : 0 );
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    code_to_index2                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Converts the character code into a glyph index.  Uses format 2.    */
  /*                                                                       */
  /* <Input>                                                               */
  /*    charCode :: The wanted character code.                             */
  /*    cmap2    :: A pointer to a cmap table in format 2.                 */
  /*                                                                       */
  /* <Return>                                                              */
  /*    Glyph index into the glyphs array.  0 if the glyph does not        */
  /*    exist.                                                             */
  /*                                                                       */
  static
  TT_UInt  code_to_index2( TT_CMapTable*  cmap,
                           TT_ULong       charCode )
  {
    TT_UInt            result, index1, offset;
    TT_UInt            char_lo;
    TT_ULong           char_hi;
    TT_CMap2SubHeader* sh2;
    TT_CMap2*          cmap2;

    cmap2   = &cmap->c.cmap2;
    result  = 0;
    char_lo = (TT_UInt)(charCode & 0xFF);
    char_hi = charCode >> 8;

    if ( char_hi == 0 )
    {
      /* an 8-bit character code - we use the subHeader 0 in this case */
      /* to test wether the character code is in the charmap           */
      if ( cmap2->subHeaderKeys[char_lo] == 0 )
      {
        result = cmap2->glyphIdArray[char_lo];
      }
    }
    else
    {
      /* a 16-bit character code */
      index1 = cmap2->subHeaderKeys[ char_hi & 0xFF ];
      if (index1)
      {
        sh2      = cmap2->subHeaders + index1;
        char_lo -= sh2->firstCode;

        if (char_lo < sh2->entryCount)
        {
          offset = sh2->idRangeOffset/2 + char_lo;
          if (offset < cmap2->numGlyphId)
          {
            result = cmap2->glyphIdArray[offset];
            if (result)
              result = (result + sh2->idDelta) & 0xFFFF;
          }
        }
      }
    }
    return result;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    code_to_index4                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Converts the character code into a glyph index.  Uses format 4.    */
  /*                                                                       */
  /* <Input>                                                               */
  /*    charCode :: The wanted character code.                             */
  /*    cmap4    :: A pointer to a cmap table in format 4.                 */
  /*                                                                       */
  /* <Return>                                                              */
  /*    Glyph index into the glyphs array.  0 if the glyph does not        */
  /*    exist.                                                             */
  /*                                                                       */
  static
  TT_UInt  code_to_index4( TT_CMapTable*  cmap,
                           TT_ULong       charCode )
  {
    TT_UInt          result, index1, segCount;
    TT_CMap4*        cmap4;
    TT_CMap4Segment  *seg4, *limit;

    cmap4    = &cmap->c.cmap4;
    result   = 0;
    segCount = cmap4->segCountX2 / 2;
    seg4     = cmap4->segments;
    limit    = seg4 + segCount;

    for ( ; seg4 < limit; seg4++ )
    {
      if ( charCode <= seg4->endCount )
      {
        /* the ranges are sorted in increasing order, if we're out of  */
        /* the range here, the char code isn't in the charmap, so exit */
        if ( charCode < seg4->startCount )
          break;

        /* when the idRangeOffset is 0, we can compute the glyph index */
        /* directly..                                                  */
        if ( seg4->idRangeOffset == 0 )
          result = (charCode + seg4->idDelta) & 0xFFFF;
        else
        /* otherwise, we must use the glyphIdArray to do it            */
        {
          index1 = seg4->idRangeOffset/2 + (charCode - seg4->startCount)
                   - (segCount-1);

          if ( index1 < cmap4->numGlyphId       &&
               cmap4->glyphIdArray[index1] != 0 )
          {
            result = (cmap4->glyphIdArray[index1] + seg4->idDelta) & 0xFFFF;
          }
        }
        break;
      }
    }

    return result;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    code_to_index6                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Converts the character code into a glyph index.  Uses format 6.    */
  /*                                                                       */
  /* <Input>                                                               */
  /*    charCode :: The wanted character code.                             */
  /*    cmap6    :: A pointer to a cmap table in format 6.                 */
  /*                                                                       */
  /* <Return>                                                              */
  /*    Glyph index into the glyphs array.  0 if the glyph does not        */
  /*    exist.                                                             */
  /*                                                                       */
  static
  TT_UInt  code_to_index6( TT_CMapTable*  cmap,
                           TT_ULong   charCode )
  {
    TT_CMap6*  cmap6;
    TT_UInt    result = 0;

    cmap6     = &cmap->c.cmap6;
    result    = 0;
    charCode -= cmap6->firstCode;

    if ( charCode < cmap6->entryCount )
      result =  cmap6->glyphIdArray[charCode];

    return result;
  }


/* END */