text: /*
   * << Haru Free PDF Library >> -- hpdf_font_cid.c
   *
   * URL: http://libharu.org
   *
   * Copyright (c) 1999-2006 Takeshi Kanno <takeshi_kanno@est.hi-ho.ne.jp>
   * Copyright (c) 2007-2009 Antony Dovgal <tony@daylessday.org>
   *
   * Permission to use, copy, modify, distribute and sell this software
   * and its documentation for any purpose is hereby granted without fee,
   * provided that the above copyright notice appear in all copies and
   * that both that copyright notice and this permission notice appear
   * in supporting documentation.
   * It is provided "as is" without express or implied warranty.
   *
   */
  
  #include "hpdf_conf.h"
  #include "hpdf_utils.h"
  #include "hpdf_font.h"
  
  static HPDF_Font
  CIDFontType0_New (HPDF_Font parent,
                    HPDF_Xref xref);
  
  
  static HPDF_Font
  CIDFontType2_New (HPDF_Font parent,
                    HPDF_Xref xref);
  
  
  static HPDF_TextWidth
  TextWidth  (HPDF_Font         font,
              const HPDF_BYTE  *text,
              HPDF_UINT         len);
  
  
  static HPDF_UINT
  MeasureText  (HPDF_Font         font,
                const HPDF_BYTE  *text,
                HPDF_UINT         len,
                HPDF_REAL         width,
                HPDF_REAL         font_size,
                HPDF_REAL         char_space,
                HPDF_REAL         word_space,
                HPDF_BOOL         wordwrap,
                HPDF_REAL        *real_width);
  
  
  static char*
  UINT16ToHex  (char        *s,
                HPDF_UINT16  val,
                char        *eptr,
                HPDF_BYTE    width);
  
  static char *
  CidRangeToHex (char        *s,
  	       HPDF_UINT16  from,
  	       HPDF_UINT16  to,
  	       char        *eptr);
  
  static HPDF_Dict
  CreateCMap  (HPDF_Encoder   encoder,
               HPDF_Xref      xref);
  
  
  static void
  OnFree_Func  (HPDF_Dict  obj);
  
  
  static HPDF_STATUS
  CIDFontType2_BeforeWrite_Func  (HPDF_Dict   obj);
  
  
  /*--------------------------------------------------------------------------*/
  
  HPDF_Font
  HPDF_Type0Font_New  (HPDF_MMgr        mmgr,
                       HPDF_FontDef     fontdef,
                       HPDF_Encoder     encoder,
                       HPDF_Xref        xref)
  {
      HPDF_Dict font;
      HPDF_FontAttr attr;
      HPDF_CMapEncoderAttr encoder_attr;
      HPDF_STATUS ret = 0;
      HPDF_Array descendant_fonts;
  
      HPDF_PTRACE ((" HPDF_Type0Font_New\n"));
  
      font = HPDF_Dict_New (mmgr);
      if (!font)
          return NULL;
  
      font->header.obj_class |= HPDF_OSUBCLASS_FONT;
  
      /* check whether the fontdef object and the encoder object is valid. */
      if (encoder->type != HPDF_ENCODER_TYPE_DOUBLE_BYTE) {
          HPDF_SetError(font->error, HPDF_INVALID_ENCODER_TYPE, 0);
          return NULL;
      }
  
      if (fontdef->type != HPDF_FONTDEF_TYPE_CID &&
          fontdef->type != HPDF_FONTDEF_TYPE_TRUETYPE) {
          HPDF_SetError(font->error, HPDF_INVALID_FONTDEF_TYPE, 0);
          return NULL;
      }
  
      attr = HPDF_GetMem (mmgr, sizeof(HPDF_FontAttr_Rec));
      if (!attr) {
          HPDF_Dict_Free (font);
          return NULL;
      }
  
      font->header.obj_class |= HPDF_OSUBCLASS_FONT;
      font->write_fn = NULL;
      font->free_fn = OnFree_Func;
      font->attr = attr;
  
      encoder_attr = (HPDF_CMapEncoderAttr)encoder->attr;
  
      HPDF_MemSet (attr, 0, sizeof(HPDF_FontAttr_Rec));
  
      attr->writing_mode = encoder_attr->writing_mode;
      attr->text_width_fn = TextWidth;
      attr->measure_text_fn = MeasureText;
      attr->fontdef = fontdef;
      attr->encoder = encoder;
      attr->xref = xref;
  
      if (HPDF_Xref_Add (xref, font) != HPDF_OK)
          return NULL;
  
      ret += HPDF_Dict_AddName (font, "Type", "Font");
      ret += HPDF_Dict_AddName (font, "BaseFont", fontdef->base_font);
      ret += HPDF_Dict_AddName (font, "Subtype", "Type0");
  
      if (fontdef->type == HPDF_FONTDEF_TYPE_CID) {
          ret += HPDF_Dict_AddName (font, "Encoding", encoder->name);
      } else {
          /*
  	 * Handle the Unicode encoding, see hpdf_encoding_utf.c For some
  	 * reason, xpdf-based readers cannot deal with our cmap but work
  	 * fine when using the predefined "Identity-H"
  	 * encoding. However, text selection does not work, unless we
  	 * add a ToUnicode cmap. This CMap should also be "Identity",
  	 * but that does not work -- specifying our cmap as a stream however
  	 * does work. Who can understand that ?
  	 */
          if (HPDF_StrCmp(encoder_attr->ordering, "Identity-H") == 0) {
  	    ret += HPDF_Dict_AddName (font, "Encoding", "Identity-H");
  	    attr->cmap_stream = CreateCMap (encoder, xref);
  
  	    if (attr->cmap_stream) {
  	        ret += HPDF_Dict_Add (font, "ToUnicode", attr->cmap_stream);
  	    } else
  	        return NULL;
  	} else {
              attr->cmap_stream = CreateCMap (encoder, xref);
  
  	    if (attr->cmap_stream) {
  	        ret += HPDF_Dict_Add (font, "Encoding", attr->cmap_stream);
  	    } else
  	      return NULL;
  	}
      }
  
      if (ret != HPDF_OK)
          return NULL;
  
      descendant_fonts = HPDF_Array_New (mmgr);
      if (!descendant_fonts)
          return NULL;
  
      if (HPDF_Dict_Add (font, "DescendantFonts", descendant_fonts) != HPDF_OK)
          return NULL;
  
      if (fontdef->type == HPDF_FONTDEF_TYPE_CID) {
          attr->descendant_font = CIDFontType0_New (font, xref);
          attr->type = HPDF_FONT_TYPE0_CID;
      } else {
          attr->descendant_font = CIDFontType2_New (font, xref);
          attr->type = HPDF_FONT_TYPE0_TT;
      }
  
      if (!attr->descendant_font)
          return NULL;
      else
          if (HPDF_Array_Add (descendant_fonts, attr->descendant_font) !=
                  HPDF_OK)
              return NULL;
  
      return font;
  }
  
  static void
  OnFree_Func  (HPDF_Dict  obj)
  {
      HPDF_FontAttr attr = (HPDF_FontAttr)obj->attr;
  
      HPDF_PTRACE ((" HPDF_Type0Font_OnFree\n"));
  
      if (attr)
          HPDF_FreeMem (obj->mmgr, attr);
  }
  
  static HPDF_Font
  CIDFontType0_New (HPDF_Font parent, HPDF_Xref xref)
  {
      HPDF_STATUS ret = HPDF_OK;
      HPDF_FontAttr attr = (HPDF_FontAttr)parent->attr;
      HPDF_FontDef fontdef = attr->fontdef;
      HPDF_CIDFontDefAttr fontdef_attr = (HPDF_CIDFontDefAttr)fontdef->attr;
      HPDF_Encoder encoder = attr->encoder;
      HPDF_CMapEncoderAttr encoder_attr =
                  (HPDF_CMapEncoderAttr)encoder->attr;
  
      HPDF_UINT16 save_cid = 0;
      HPDF_Font font;
      HPDF_Array array;
      HPDF_Array sub_array = NULL;
      HPDF_UINT i;
  
      HPDF_Dict descriptor;
      HPDF_Dict cid_system_info;
  
      HPDF_PTRACE ((" HPDF_CIDFontType0_New\n"));
  
      font = HPDF_Dict_New (parent->mmgr);
      if (!font)
          return NULL;
  
      if (HPDF_Xref_Add (xref, font) != HPDF_OK)
          return NULL;
  
      ret += HPDF_Dict_AddName (font, "Type", "Font");
      ret += HPDF_Dict_AddName (font, "Subtype", "CIDFontType0");
      ret += HPDF_Dict_AddNumber (font, "DW", fontdef_attr->DW);
      ret += HPDF_Dict_AddName (font, "BaseFont", fontdef->base_font);
      if (ret != HPDF_OK)
          return NULL;
  
      /* add 'DW2' element */
      array = HPDF_Array_New (parent->mmgr);
      if (!array)
          return NULL;
  
      if (HPDF_Dict_Add (font, "DW2", array) != HPDF_OK)
          return NULL;
  
      ret += HPDF_Array_AddNumber (array, fontdef_attr->DW2[0]);
      ret += HPDF_Array_AddNumber (array, fontdef_attr->DW2[1]);
  
      if (ret != HPDF_OK)
          return NULL;
  
      /* add 'W' element */
      array = HPDF_Array_New (parent->mmgr);
      if (!array)
          return NULL;
  
      if (HPDF_Dict_Add (font, "W", array) != HPDF_OK)
          return NULL;
  
      /* Create W array. */
      for (i = 0; i< fontdef_attr->widths->count; i++) {
          HPDF_CID_Width *w =
                  (HPDF_CID_Width *)HPDF_List_ItemAt (fontdef_attr->widths, i);
  
          if (w->cid != save_cid + 1 || !sub_array) {
              sub_array = HPDF_Array_New (parent->mmgr);
              if (!sub_array)
                  return NULL;
  
              ret += HPDF_Array_AddNumber (array, w->cid);
              ret += HPDF_Array_Add (array, sub_array);
          }
  
          ret += HPDF_Array_AddNumber (sub_array, w->width);
          save_cid = w->cid;
  
          if (ret != HPDF_OK)
              return NULL;
      }
  
      /* create descriptor */
      descriptor = HPDF_Dict_New (parent->mmgr);
      if (!descriptor)
          return NULL;
  
      if (HPDF_Xref_Add (xref, descriptor) != HPDF_OK)
          return NULL;
  
      if (HPDF_Dict_Add (font, "FontDescriptor", descriptor) != HPDF_OK)
          return NULL;
  
      ret += HPDF_Dict_AddName (descriptor, "Type", "FontDescriptor");
      ret += HPDF_Dict_AddName (descriptor, "FontName", fontdef->base_font);
      ret += HPDF_Dict_AddNumber (descriptor, "Ascent", fontdef->ascent);
      ret += HPDF_Dict_AddNumber (descriptor, "Descent", fontdef->descent);
      ret += HPDF_Dict_AddNumber (descriptor, "CapHeight",
                  fontdef->cap_height);
      ret += HPDF_Dict_AddNumber (descriptor, "MissingWidth",
                  fontdef->missing_width);
      ret += HPDF_Dict_AddNumber (descriptor, "Flags", fontdef->flags);
  
      if (ret != HPDF_OK)
          return NULL;
  
      array = HPDF_Box_Array_New (parent->mmgr, fontdef->font_bbox);
      if (!array)
          return NULL;
  
      ret += HPDF_Dict_Add (descriptor, "FontBBox", array);
      ret += HPDF_Dict_AddNumber (descriptor, "ItalicAngle",
              fontdef->italic_angle);
      ret += HPDF_Dict_AddNumber (descriptor, "StemV", fontdef->stemv);
  
      if (ret != HPDF_OK)
          return NULL;
  
      /* create CIDSystemInfo dictionary */
      cid_system_info = HPDF_Dict_New (parent->mmgr);
      if (!cid_system_info)
          return NULL;
  
      if (HPDF_Dict_Add (font, "CIDSystemInfo", cid_system_info) != HPDF_OK)
          return NULL;
  
      ret += HPDF_Dict_Add (cid_system_info, "Registry",
              HPDF_String_New (parent->mmgr, encoder_attr->registry, NULL));
      ret += HPDF_Dict_Add (cid_system_info, "Ordering",
              HPDF_String_New (parent->mmgr, encoder_attr->ordering, NULL));
      ret += HPDF_Dict_AddNumber (cid_system_info, "Supplement",
              encoder_attr->suppliment);
  
      if (ret != HPDF_OK)
          return NULL;
  
      return font;
  }
  
  static HPDF_Font
  CIDFontType2_New (HPDF_Font parent, HPDF_Xref xref)
  {
      HPDF_STATUS ret = HPDF_OK;
      HPDF_FontAttr attr = (HPDF_FontAttr)parent->attr;
      HPDF_FontDef fontdef = attr->fontdef;
      HPDF_TTFontDefAttr fontdef_attr = (HPDF_TTFontDefAttr)fontdef->attr;
      HPDF_Encoder encoder = attr->encoder;
      HPDF_CMapEncoderAttr encoder_attr =
                  (HPDF_CMapEncoderAttr)encoder->attr;
  
      HPDF_Font font;
      HPDF_Array array;
      HPDF_UINT i;
      HPDF_UNICODE tmp_map[65536];
      HPDF_Dict cid_system_info;
  
      HPDF_UINT16 max = 0;
  
      HPDF_PTRACE ((" HPDF_CIDFontType2_New\n"));
  
      font = HPDF_Dict_New (parent->mmgr);
      if (!font)
          return NULL;
  
      if (HPDF_Xref_Add (xref, font) != HPDF_OK)
          return NULL;
  
      parent->before_write_fn = CIDFontType2_BeforeWrite_Func;
  
      ret += HPDF_Dict_AddName (font, "Type", "Font");
      ret += HPDF_Dict_AddName (font, "Subtype", "CIDFontType2");
      ret += HPDF_Dict_AddNumber (font, "DW", fontdef->missing_width);
      if (ret != HPDF_OK)
          return NULL;
  
      /* add 'DW2' element */
      array = HPDF_Array_New (font->mmgr);
      if (!array)
          return NULL;
  
      if (HPDF_Dict_Add (font, "DW2", array) != HPDF_OK)
          return NULL;
  
      ret += HPDF_Array_AddNumber (array, (HPDF_INT32)(fontdef->font_bbox.bottom));
      ret += HPDF_Array_AddNumber (array, (HPDF_INT32)(fontdef->font_bbox.bottom -
                  fontdef->font_bbox.top));
  
      HPDF_MemSet (tmp_map, 0, sizeof(HPDF_UNICODE) * 65536);
  
      if (ret != HPDF_OK)
          return NULL;
  
      for (i = 0; i < 256; i++) {
          HPDF_UINT j;
  
          for (j = 0; j < 256; j++) {
  	    if (encoder->to_unicode_fn == HPDF_CMapEncoder_ToUnicode) {
  		HPDF_UINT16 cid = encoder_attr->cid_map[i][j];
  		if (cid != 0) {
  		    HPDF_UNICODE unicode = encoder_attr->unicode_map[i][j];
  		    HPDF_UINT16 gid = HPDF_TTFontDef_GetGlyphid (fontdef,
  								 unicode);
  		    tmp_map[cid] = gid;
  		    if (max < cid)
  			max = cid;
  		}
  	    } else {
  		HPDF_UNICODE unicode = (i << 8) | j;
  		HPDF_UINT16 gid = HPDF_TTFontDef_GetGlyphid (fontdef,
  							     unicode);
  		tmp_map[unicode] = gid;
  		if (max < unicode)
  		    max = unicode;
  	    }
  	}
      }
  
      if (max > 0) {
          HPDF_INT16 dw = fontdef->missing_width;
          HPDF_UNICODE *ptmp_map = tmp_map;
          HPDF_Array tmp_array = NULL;
  
          /* add 'W' element */
          array = HPDF_Array_New (font->mmgr);
          if (!array)
              return NULL;
  
          if (HPDF_Dict_Add (font, "W", array) != HPDF_OK)
              return NULL;
  
          for (i = 0; i < max; i++, ptmp_map++) {
              HPDF_INT w = HPDF_TTFontDef_GetGidWidth (fontdef, *ptmp_map);
  
              if (w != dw) {
                  if (!tmp_array) {
                      if (HPDF_Array_AddNumber (array, i) != HPDF_OK)
                          return NULL;
  
                      tmp_array = HPDF_Array_New (font->mmgr);
                      if (!tmp_array)
                          return NULL;
  
                      if (HPDF_Array_Add (array, tmp_array) != HPDF_OK)
                          return NULL;
                  }
  
                  if ((ret = HPDF_Array_AddNumber (tmp_array, w)) != HPDF_OK)
                      return NULL;
              } else
                    tmp_array = NULL;
          }
  
          /* create "CIDToGIDMap" data */
          if (fontdef_attr->embedding) {
              attr->map_stream = HPDF_DictStream_New (font->mmgr, xref);
              if (!attr->map_stream)
                  return NULL;
  
              if (HPDF_Dict_Add (font, "CIDToGIDMap", attr->map_stream) != HPDF_OK)
                  return NULL;
  
              for (i = 0; i < max; i++) {
                  HPDF_BYTE u[2];
                  HPDF_UINT16 gid = tmp_map[i];
  
                  u[0] = (HPDF_BYTE)(gid >> 8);
                  u[1] = (HPDF_BYTE)gid;
  
                  HPDF_MemCpy ((HPDF_BYTE *)(tmp_map + i), u, 2);
              }
  
              if ((ret = HPDF_Stream_Write (attr->map_stream->stream,
                              (HPDF_BYTE *)tmp_map, max * 2)) != HPDF_OK)
                  return NULL;
          }
      } else {
          HPDF_SetError (font->error, HPDF_INVALID_FONTDEF_DATA, 0);
          return 0;
      }
  
      /* create CIDSystemInfo dictionary */
      cid_system_info = HPDF_Dict_New (parent->mmgr);
      if (!cid_system_info)
          return NULL;
  
      if (HPDF_Dict_Add (font, "CIDSystemInfo", cid_system_info) != HPDF_OK)
          return NULL;
  
      ret += HPDF_Dict_Add (cid_system_info, "Registry",
              HPDF_String_New (parent->mmgr, encoder_attr->registry, NULL));
      ret += HPDF_Dict_Add (cid_system_info, "Ordering",
              HPDF_String_New (parent->mmgr, encoder_attr->ordering, NULL));
      ret += HPDF_Dict_AddNumber (cid_system_info, "Supplement",
              encoder_attr->suppliment);
  
      if (ret != HPDF_OK)
          return NULL;
  
      return font;
  }
  
  
  static HPDF_STATUS
  CIDFontType2_BeforeWrite_Func  (HPDF_Dict obj)
  {
      HPDF_FontAttr font_attr = (HPDF_FontAttr)obj->attr;
      HPDF_FontDef def = font_attr->fontdef;
      HPDF_TTFontDefAttr def_attr = (HPDF_TTFontDefAttr)def->attr;
      HPDF_STATUS ret = 0;
  
      HPDF_PTRACE ((" CIDFontType2_BeforeWrite_Func\n"));
  
      if (font_attr->map_stream)
          font_attr->map_stream->filter = obj->filter;
  
      if (font_attr->cmap_stream)
          font_attr->cmap_stream->filter = obj->filter;
  
      if (!font_attr->fontdef->descriptor) {
          HPDF_Dict descriptor = HPDF_Dict_New (obj->mmgr);
          HPDF_Array array;
  
          if (!descriptor)
              return HPDF_Error_GetCode (obj->error);
  
          if (def_attr->embedding) {
              HPDF_Dict font_data = HPDF_DictStream_New (obj->mmgr,
                      font_attr->xref);
  
              if (!font_data)
                  return HPDF_Error_GetCode (obj->error);
  
              if (HPDF_TTFontDef_SaveFontData (font_attr->fontdef,
                  font_data->stream) != HPDF_OK)
                  return HPDF_Error_GetCode (obj->error);
  
              ret += HPDF_Dict_Add (descriptor, "FontFile2", font_data);
              ret += HPDF_Dict_AddNumber (font_data, "Length1",
                      def_attr->length1);
              ret += HPDF_Dict_AddNumber (font_data, "Length2", 0);
              ret += HPDF_Dict_AddNumber (font_data, "Length3", 0);
  
              font_data->filter = obj->filter;
  
              if (ret != HPDF_OK)
                  return HPDF_Error_GetCode (obj->error);
          }
  
          ret += HPDF_Xref_Add (font_attr->xref, descriptor);
          ret += HPDF_Dict_AddName (descriptor, "Type", "FontDescriptor");
          ret += HPDF_Dict_AddNumber (descriptor, "Ascent", def->ascent);
          ret += HPDF_Dict_AddNumber (descriptor, "Descent", def->descent);
          ret += HPDF_Dict_AddNumber (descriptor, "Flags", def->flags);
  
          array = HPDF_Box_Array_New (obj->mmgr, def->font_bbox);
          ret += HPDF_Dict_Add (descriptor, "FontBBox", array);
  
          ret += HPDF_Dict_AddName (descriptor, "FontName", def_attr->base_font);
          ret += HPDF_Dict_AddNumber (descriptor, "ItalicAngle",
                  def->italic_angle);
          ret += HPDF_Dict_AddNumber (descriptor, "StemV", def->stemv);
          ret += HPDF_Dict_AddNumber (descriptor, "XHeight", def->x_height);
  
          if (ret != HPDF_OK)
              return HPDF_Error_GetCode (obj->error);
  
          font_attr->fontdef->descriptor = descriptor;
      }
  
      if ((ret = HPDF_Dict_AddName (obj, "BaseFont",
                  def_attr->base_font)) != HPDF_OK)
          return ret;
  
      if ((ret = HPDF_Dict_AddName (font_attr->descendant_font, "BaseFont",
                  def_attr->base_font)) != HPDF_OK)
          return ret;
  
      return HPDF_Dict_Add (font_attr->descendant_font, "FontDescriptor",
                  font_attr->fontdef->descriptor);
  }
  
  
  static HPDF_TextWidth
  TextWidth  (HPDF_Font         font,
              const HPDF_BYTE  *text,
              HPDF_UINT         len)
  {
      HPDF_TextWidth tw = {0, 0, 0, 0};
      HPDF_FontAttr attr = (HPDF_FontAttr)font->attr;
      HPDF_ParseText_Rec  parse_state;
      HPDF_Encoder encoder = attr->encoder;
      HPDF_UINT i = 0;
      HPDF_INT dw2;
      HPDF_BYTE b = 0;
  
      HPDF_PTRACE ((" HPDF_Type0Font_TextWidth\n"));
  
      if (attr->fontdef->type == HPDF_FONTDEF_TYPE_CID) {
          HPDF_CIDFontDefAttr cid_fontdef_attr =
                      (HPDF_CIDFontDefAttr)attr->fontdef->attr;
          dw2 = cid_fontdef_attr->DW2[1];
      } else {
          dw2 = (HPDF_INT)(attr->fontdef->font_bbox.bottom -
                      attr->fontdef->font_bbox.top);
      }
  
      HPDF_Encoder_SetParseText (encoder, &parse_state, text, len);
  
      while (i < len) {
          HPDF_ByteType btype = (encoder->byte_type_fn)(encoder, &parse_state);
          HPDF_UINT16 cid;
          HPDF_UNICODE unicode;
          HPDF_UINT16 code;
          HPDF_UINT w = 0;
  
          b = *text++;
          code = b;
  
          if (btype == HPDF_BYTE_TYPE_LEAD) {
              code <<= 8;
              code = (HPDF_UINT16)(code + *text);
          }
  
          if (btype != HPDF_BYTE_TYPE_TRIAL) {
              if (attr->writing_mode == HPDF_WMODE_HORIZONTAL) {
                  if (attr->fontdef->type == HPDF_FONTDEF_TYPE_CID) {
                      /* cid-based font */
                      cid = HPDF_CMapEncoder_ToCID (encoder, code);
                      w = HPDF_CIDFontDef_GetCIDWidth (attr->fontdef, cid);
                  } else {
                      /* unicode-based font */
                      unicode = (encoder->to_unicode_fn)(encoder, code);
                      w = HPDF_TTFontDef_GetCharWidth (attr->fontdef, unicode);
                  }
              } else {
                  w = -dw2;
              }
  
              tw.numchars++;
          }
  
          if (HPDF_IS_WHITE_SPACE(code)) {
              tw.numwords++;
              tw.numspace++;
          }
  
          tw.width += w;
          i++;
      }
  
      /* 2006.08.19 add. */
      if (HPDF_IS_WHITE_SPACE(b))
          ; /* do nothing. */
      else
          tw.numwords++;
  
      return tw;
  }
  
  
  static HPDF_UINT
  MeasureText  (HPDF_Font          font,
                const HPDF_BYTE   *text,
                HPDF_UINT          len,
                HPDF_REAL          width,
                HPDF_REAL          font_size,
                HPDF_REAL          char_space,
                HPDF_REAL          word_space,
                HPDF_BOOL          wordwrap,
                HPDF_REAL         *real_width)
  {
      HPDF_REAL w = 0;
      HPDF_UINT tmp_len = 0;
      HPDF_UINT i;
      HPDF_FontAttr attr = (HPDF_FontAttr)font->attr;
      HPDF_ByteType last_btype = HPDF_BYTE_TYPE_TRIAL;
      HPDF_Encoder encoder = attr->encoder;
      HPDF_ParseText_Rec  parse_state;
      HPDF_INT dw2;
  
      HPDF_PTRACE ((" HPDF_Type0Font_MeasureText\n"));
  
      if (attr->fontdef->type == HPDF_FONTDEF_TYPE_CID) {
          HPDF_CIDFontDefAttr cid_fontdef_attr =
                  (HPDF_CIDFontDefAttr)attr->fontdef->attr;
          dw2 = cid_fontdef_attr->DW2[1];
      } else {
          dw2 = (HPDF_INT)(attr->fontdef->font_bbox.bottom -
                      attr->fontdef->font_bbox.top);
      }
  
      HPDF_Encoder_SetParseText (encoder, &parse_state, text, len);
  
      for (i = 0; i < len; i++) {
          HPDF_BYTE b = *text++;
          HPDF_BYTE b2 = *text;  /* next byte */
          HPDF_ByteType btype = HPDF_Encoder_ByteType (encoder, &parse_state);
          HPDF_UNICODE unicode;
          HPDF_UINT16 code = b;
          HPDF_UINT16 tmp_w = 0;
  
          if (btype == HPDF_BYTE_TYPE_LEAD) {
              code <<= 8;
              code = (HPDF_UINT16)(code + b2);
          }
  
          if (!wordwrap) {
              if (HPDF_IS_WHITE_SPACE(b)) {
                  tmp_len = i + 1;
                  if (real_width)
                      *real_width = w;
              } else if (btype == HPDF_BYTE_TYPE_SINGLE ||
                          btype == HPDF_BYTE_TYPE_LEAD) {
                  tmp_len = i;
                  if (real_width)
                      *real_width = w;
              }
          } else {
              if (HPDF_IS_WHITE_SPACE(b)) {
                  tmp_len = i + 1;
                  if (real_width)
                      *real_width = w;
              } /* else
  			//Commenting this out fixes problem with HPDF_Text_Rect() splitting the words
              if (last_btype == HPDF_BYTE_TYPE_TRIAL ||
                      (btype == HPDF_BYTE_TYPE_LEAD &&
                      last_btype == HPDF_BYTE_TYPE_SINGLE)) {
                  if (!HPDF_Encoder_CheckJWWLineHead(encoder, code)) {
                      tmp_len = i;
                      if (real_width)
                          *real_width = w;
                  }
              }*/
          }
  
          if (HPDF_IS_WHITE_SPACE(b)) {
              w += word_space;
          }
  
          if (btype != HPDF_BYTE_TYPE_TRIAL) {
              if (attr->writing_mode == HPDF_WMODE_HORIZONTAL) {
                  if (attr->fontdef->type == HPDF_FONTDEF_TYPE_CID) {
                      /* cid-based font */
                      HPDF_UINT16 cid = HPDF_CMapEncoder_ToCID (encoder, code);
                      tmp_w = HPDF_CIDFontDef_GetCIDWidth (attr->fontdef, cid);
                  } else {
                      /* unicode-based font */
                      unicode = (encoder->to_unicode_fn)(encoder, code);
                      tmp_w = HPDF_TTFontDef_GetCharWidth (attr->fontdef,
                              unicode);
                  }
              } else {
                  tmp_w = (HPDF_UINT16)(-dw2);
              }
  
              if (i > 0)
                  w += char_space;
          }
  
          w += (HPDF_REAL)((HPDF_DOUBLE)tmp_w * font_size / 1000);
  
          /* 2006.08.04 break when it encountered  line feed */
          if (w > width || b == 0x0A)
              return tmp_len;
  
          if (HPDF_IS_WHITE_SPACE(b))
              last_btype = HPDF_BYTE_TYPE_TRIAL;
          else
              last_btype = btype;
      }
  
      /* all of text can be put in the specified width */
      if (real_width)
          *real_width = w;
  
      return len;
  }
  
  
  
  static char*
  UINT16ToHex  (char        *s,
                HPDF_UINT16  val,
                char        *eptr,
                HPDF_BYTE    width)
  {
      HPDF_BYTE b[2];
      HPDF_UINT16 val2;
      char c;
  
      if (eptr - s < 7)
          return s;
  
      /* align byte-order */
      HPDF_MemCpy (b, (HPDF_BYTE *)&val, 2);
      val2 = (HPDF_UINT16)((HPDF_UINT16)b[0] << 8 | (HPDF_UINT16)b[1]);
  
      HPDF_MemCpy (b, (HPDF_BYTE *)&val2, 2);
  
      *s++ = '<';
  
      /*
       * In principle a range of <00> - <1F> can now not be
       * distinguished from <0000> - <001F>..., this seems something
       * that is wrong with CID ranges. For the UCS-2 encoding we need
       * to add <0000> - <FFFF> and this cannot be <00> - <FFFF> (or at
       * least, that crashes Mac OSX Preview).
       */
      if (width == 2) {
          c = b[0] >> 4;
          if (c <= 9)
              c += 0x30;
          else
              c += 0x41 - 10;
          *s++ = c;
  
          c = (char)(b[0] & 0x0f);
          if (c <= 9)
              c += 0x30;
          else
              c += 0x41 - 10;
          *s++ = c;
      }
  
      c = (char)(b[1] >> 4);
      if (c <= 9)
          c += 0x30;
      else
          c += 0x41 - 10;
      *s++ = c;
  
      c = (char)(b[1] & 0x0f);
      if (c <= 9)
          c += 0x30;
      else
          c += 0x41 - 10;
      *s++ = c;
  
      *s++ = '>';
      *s = 0;
  
      return s;
  }
  
  static char*
  CidRangeToHex  (char        *s,
                  HPDF_UINT16  from,
                  HPDF_UINT16  to,
                  char        *eptr)
  {
      HPDF_BYTE width = (to > 255) ? 2 : 1;
      char *pbuf;
  
      pbuf = UINT16ToHex (s, from, eptr, width);
      *pbuf++ = ' ';
      pbuf = UINT16ToHex (pbuf, to, eptr, width);
  
      return pbuf;
  }
  
  static HPDF_Dict
  CreateCMap  (HPDF_Encoder   encoder,
               HPDF_Xref      xref)
  {
      HPDF_STATUS ret = HPDF_OK;
      HPDF_Dict cmap = HPDF_DictStream_New (encoder->mmgr, xref);
      HPDF_CMapEncoderAttr attr = (HPDF_CMapEncoderAttr)encoder->attr;
      char buf[HPDF_TMP_BUF_SIZ];
      char *pbuf;
      char *eptr = buf + HPDF_TMP_BUF_SIZ - 1;
      HPDF_UINT i;
      HPDF_UINT phase, odd;
      HPDF_Dict sysinfo;
  
      if (!cmap)
          return NULL;
  
      ret += HPDF_Dict_AddName (cmap, "Type", "CMap");
      ret += HPDF_Dict_AddName (cmap, "CMapName", encoder->name);
  
      sysinfo = HPDF_Dict_New (encoder->mmgr);
      if (!sysinfo)
          return NULL;
  
      if (HPDF_Dict_Add (cmap, "CIDSystemInfo", sysinfo) != HPDF_OK)
          return NULL;
  
      ret += HPDF_Dict_Add (sysinfo, "Registry", HPDF_String_New (encoder->mmgr,
                      attr->registry, NULL));
      ret += HPDF_Dict_Add (sysinfo, "Ordering", HPDF_String_New (encoder->mmgr,
                      attr->ordering, NULL));
      ret += HPDF_Dict_AddNumber (sysinfo, "Supplement", attr->suppliment);
      ret += HPDF_Dict_AddNumber (cmap, "WMode",
                      (HPDF_UINT32)attr->writing_mode);
  
      /* create cmap data from encoding data */
      ret += HPDF_Stream_WriteStr (cmap->stream,
                  "%!PS-Adobe-3.0 Resource-CMap\r\n");
      ret += HPDF_Stream_WriteStr (cmap->stream,
                  "%%DocumentNeededResources: ProcSet (CIDInit)\r\n");
      ret += HPDF_Stream_WriteStr (cmap->stream,
                  "%%IncludeResource: ProcSet (CIDInit)\r\n");
  
      pbuf = (char *)HPDF_StrCpy (buf, "%%BeginResource: CMap (", eptr);
      pbuf = (char *)HPDF_StrCpy (pbuf, encoder->name, eptr);
      HPDF_StrCpy (pbuf, ")\r\n", eptr);
      ret += HPDF_Stream_WriteStr (cmap->stream, buf);
  
      pbuf = (char *)HPDF_StrCpy (buf, "%%Title: (", eptr);
      pbuf = (char *)HPDF_StrCpy (pbuf, encoder->name, eptr);
      *pbuf++ = ' ';
      pbuf = (char *)HPDF_StrCpy (pbuf, attr->registry, eptr);
      *pbuf++ = ' ';
      pbuf = (char *)HPDF_StrCpy (pbuf, attr->ordering, eptr);
      *pbuf++ = ' ';
      pbuf = HPDF_IToA (pbuf, attr->suppliment, eptr);
      HPDF_StrCpy (pbuf, ")\r\n", eptr);
      ret += HPDF_Stream_WriteStr (cmap->stream, buf);
  
      ret += HPDF_Stream_WriteStr (cmap->stream, "%%Version: 1.0\r\n");
      ret += HPDF_Stream_WriteStr (cmap->stream, "%%EndComments\r\n");
  
      ret += HPDF_Stream_WriteStr (cmap->stream,
                  "/CIDInit /ProcSet findresource begin\r\n\r\n");
  
      /* Adobe CMap and CIDFont Files Specification recommends to allocate
       * five more elements to this dictionary than existing elements.
       */
      ret += HPDF_Stream_WriteStr (cmap->stream, "12 dict begin\r\n\r\n");
  
      ret += HPDF_Stream_WriteStr (cmap->stream, "begincmap\r\n\r\n");
      ret += HPDF_Stream_WriteStr (cmap->stream,
                  "/CIDSystemInfo 3 dict dup begin\r\n");
  
      pbuf = (char *)HPDF_StrCpy (buf, "  /Registry (", eptr);
      pbuf = (char *)HPDF_StrCpy (pbuf, attr->registry, eptr);
      HPDF_StrCpy (pbuf, ") def\r\n", eptr);
      ret += HPDF_Stream_WriteStr (cmap->stream, buf);
  
      pbuf = (char *)HPDF_StrCpy (buf, "  /Ordering (", eptr);
      pbuf = (char *)HPDF_StrCpy (pbuf, attr->ordering, eptr);
      HPDF_StrCpy (pbuf, ") def\r\n", eptr);
      ret += HPDF_Stream_WriteStr (cmap->stream, buf);
  
      pbuf = (char *)HPDF_StrCpy (buf, "  /Supplement ", eptr);
      pbuf = HPDF_IToA (pbuf, attr->suppliment, eptr);
      pbuf = (char *)HPDF_StrCpy (pbuf, " def\r\n", eptr);
      HPDF_StrCpy (pbuf, "end def\r\n\r\n", eptr);
      ret += HPDF_Stream_WriteStr (cmap->stream, buf);
  
      pbuf = (char *)HPDF_StrCpy (buf, "/CMapName /", eptr);
      pbuf = (char *)HPDF_StrCpy (pbuf, encoder->name, eptr);
      HPDF_StrCpy (pbuf, " def\r\n", eptr);
      ret += HPDF_Stream_WriteStr (cmap->stream, buf);
  
      ret += HPDF_Stream_WriteStr (cmap->stream, "/CMapVersion 1.0 def\r\n");
      ret += HPDF_Stream_WriteStr (cmap->stream, "/CMapType 1 def\r\n\r\n");
  
      if (attr->uid_offset >= 0) {
          pbuf = (char *)HPDF_StrCpy (buf, "/UIDOffset ", eptr);
          pbuf = HPDF_IToA (pbuf, attr->uid_offset, eptr);
          HPDF_StrCpy (pbuf, " def\r\n\r\n", eptr);
          ret += HPDF_Stream_WriteStr (cmap->stream, buf);
      }
  
      pbuf = (char *)HPDF_StrCpy (buf, "/XUID [", eptr);
      pbuf = HPDF_IToA (pbuf, attr->xuid[0], eptr);
      *pbuf++ = ' ';
      pbuf = HPDF_IToA (pbuf, attr->xuid[1], eptr);
      *pbuf++ = ' ';
      pbuf = HPDF_IToA (pbuf, attr->xuid[2], eptr);
      HPDF_StrCpy (pbuf, "] def\r\n\r\n", eptr);
      ret += HPDF_Stream_WriteStr (cmap->stream, buf);
  
      pbuf = (char *)HPDF_StrCpy (buf, "/WMode ", eptr);
      pbuf = HPDF_IToA (pbuf, (HPDF_UINT32)attr->writing_mode, eptr);
      HPDF_StrCpy (pbuf, " def\r\n\r\n", eptr);
      ret += HPDF_Stream_WriteStr (cmap->stream, buf);
  
      /* add code-space-range */
      pbuf = HPDF_IToA (buf, attr->code_space_range->count, eptr);
      HPDF_StrCpy (pbuf, " begincodespacerange\r\n", eptr);
      ret += HPDF_Stream_WriteStr (cmap->stream, buf);
  
      for (i = 0; i < attr->code_space_range->count; i++) {
          HPDF_CidRange_Rec *range = HPDF_List_ItemAt (attr->code_space_range,
                          i);
  
          pbuf = CidRangeToHex(buf, range->from, range->to, eptr);
  
          HPDF_StrCpy (pbuf, "\r\n", eptr);
  
          ret += HPDF_Stream_WriteStr (cmap->stream, buf);
  
          if (ret != HPDF_OK)
              return NULL;
      }
  
      HPDF_StrCpy (buf, "endcodespacerange\r\n\r\n", eptr);
      ret += HPDF_Stream_WriteStr (cmap->stream, buf);
      if (ret != HPDF_OK)
          return NULL;
  
      /* add not-def-range */
      pbuf = HPDF_IToA (buf, attr->notdef_range->count, eptr);
      HPDF_StrCpy (pbuf, " beginnotdefrange\r\n", eptr);
      ret += HPDF_Stream_WriteStr (cmap->stream, buf);
  
      for (i = 0; i < attr->notdef_range->count; i++) {
          HPDF_CidRange_Rec *range = HPDF_List_ItemAt (attr->notdef_range, i);
  
          pbuf = CidRangeToHex(buf, range->from, range->to, eptr);
          *pbuf++ = ' ';
          pbuf = HPDF_IToA (pbuf, range->cid, eptr);
          HPDF_StrCpy (pbuf, "\r\n", eptr);
  
          ret += HPDF_Stream_WriteStr (cmap->stream, buf);
  
          if (ret != HPDF_OK)
              return NULL;
      }
  
      HPDF_StrCpy (buf, "endnotdefrange\r\n\r\n", eptr);
      ret += HPDF_Stream_WriteStr (cmap->stream, buf);
      if (ret != HPDF_OK)
          return NULL;
  
      /* add cid-range */
      phase = attr->cmap_range->count / 100;
      odd = attr->cmap_range->count % 100;
      if (phase > 0)
          pbuf = HPDF_IToA (buf, 100, eptr);
      else
          pbuf = HPDF_IToA (buf, odd, eptr);
      HPDF_StrCpy (pbuf, " begincidrange\r\n", eptr);
      ret += HPDF_Stream_WriteStr (cmap->stream, buf);
  
      for (i = 0; i < attr->cmap_range->count; i++) {
          HPDF_CidRange_Rec *range = HPDF_List_ItemAt (attr->cmap_range, i);
  
          pbuf = CidRangeToHex(buf, range->from, range->to, eptr);
          *pbuf++ = ' ';
          pbuf = HPDF_IToA (pbuf, range->cid, eptr);
          HPDF_StrCpy (pbuf, "\r\n", eptr);
  
          ret += HPDF_Stream_WriteStr (cmap->stream, buf);
  
          if ((i + 1) %100 == 0) {
              phase--;
              pbuf = (char *)HPDF_StrCpy (buf, "endcidrange\r\n\r\n", eptr);
  
              if (phase > 0)
                  pbuf = HPDF_IToA (pbuf, 100, eptr);
              else
                  pbuf = HPDF_IToA (pbuf, odd, eptr);
  
              HPDF_StrCpy (pbuf, " begincidrange\r\n", eptr);
  
              ret += HPDF_Stream_WriteStr (cmap->stream, buf);
          }
  
          if (ret != HPDF_OK)
              return NULL;
      }
  
      if (odd > 0)
          pbuf = (char *)HPDF_StrCpy (buf, "endcidrange\r\n", eptr);
  
      pbuf = (char *)HPDF_StrCpy (pbuf, "endcmap\r\n", eptr);
      pbuf = (char *)HPDF_StrCpy (pbuf, "CMapName currentdict /CMap "
              "defineresource pop\r\n", eptr);
      pbuf = (char *)HPDF_StrCpy (pbuf, "end\r\n", eptr);
      pbuf = (char *)HPDF_StrCpy (pbuf, "end\r\n\r\n", eptr);
      pbuf = (char *)HPDF_StrCpy (pbuf, "%%EndResource\r\n", eptr);
      HPDF_StrCpy (pbuf, "%%EOF\r\n", eptr);
      ret += HPDF_Stream_WriteStr (cmap->stream, buf);
  
      if (ret != HPDF_OK)
          return NULL;
  
      return cmap;
  }
  
  