////////////////////////////////////////////////////////////////////
        // build new cmap table
        ////////////////////////////////////////////////////////////////////
        private void BuildCharMapTable()
        {
            // create a new cmap sub table
            cmapSubTbl NewSubTbl = new cmapSubTbl(cmapSubTbl.PlatformID, cmapSubTbl.EncodingID, 4);
            NewSubTbl.Language = cmapSubTbl.Language;
            NewSubTbl.SegCount = 2;
            NewSubTbl.SegArray = new cmapSeg[2];
            NewSubTbl.GlyphArray = CharToGlyphArray;

            // test type of font
            if(cmapSubTbl.EncodingID != 0)
            // alphabetic font
            NewSubTbl.SegArray[0] = new cmapSeg(FirstChar, LastChar, 0, 2);
            else
            // symbolic font
            NewSubTbl.SegArray[0] = new cmapSeg(0xf000 + FirstChar, 0xf000 + LastChar, 0, 2);
            NewSubTbl.SegArray[1] = new cmapSeg(0xffff, 0xffff, 1, 0);

            // table size
            Int32 TblSize = 4 + 8 + 16 + 8 * NewSubTbl.SegCount + 2 * CharToGlyphArray.Length;
            Buffer = new Byte[TblSize];
            BufPtr = 0;

            // table version number is 0
            WriteUInt16BigEndian(0);

            // number of tables is 1
            WriteUInt16BigEndian(1);

            // platform id
            WriteUInt16BigEndian(NewSubTbl.PlatformID);

            // encoding id
            WriteUInt16BigEndian(NewSubTbl.EncodingID);

            // offset
            WriteUInt32BigEndian(4 + 8);

            // format
            WriteUInt16BigEndian(NewSubTbl.Format);

            // table length
            WriteInt16BigEndian((16 + 8 * NewSubTbl.SegCount + 2 * CharToGlyphArray.Length));

            // language
            WriteUInt16BigEndian(NewSubTbl.Language);

            // segment count times 2
            WriteInt16BigEndian((NewSubTbl.SegCount * 2));

            // search range
            WriteUInt16BigEndian(NewSubTbl.SearchRange);

            // entry selector
            WriteUInt16BigEndian(NewSubTbl.EntrySelector);

            // range shift
            WriteUInt16BigEndian(NewSubTbl.RangeShift);

            // end character
            for(Int32 Seg = 0; Seg < NewSubTbl.SegCount; Seg++) WriteUInt16BigEndian(NewSubTbl.SegArray[Seg].EndChar);

            // padding
            WriteUInt16BigEndian(0);

            // start character
            for(Int32 Seg = 0; Seg < NewSubTbl.SegCount; Seg++) WriteUInt16BigEndian(NewSubTbl.SegArray[Seg].StartChar);

            // IDDelta
            for(Int32 Seg = 0; Seg < NewSubTbl.SegCount; Seg++) WriteInt16BigEndian(NewSubTbl.SegArray[Seg].IDDelta);

            // IDRangeOffset
            for(Int32 Seg = 0; Seg < NewSubTbl.SegCount; Seg++) WriteUInt16BigEndian((UInt16) (NewSubTbl.SegArray[Seg].IDRangeOffset * 2));

            // char to glyph translation
            for(Int32 Glyph = 0; Glyph < NewSubTbl.GlyphArray.Length; Glyph++) WriteUInt16BigEndian(NewSubTbl.GlyphArray[Glyph]);

            // save
            TableRecordArray[(Int32) Tag.cmap].Data = Buffer;

            // calculate checksum
            TableRecordArray[(Int32) Tag.cmap].Checksum = TableChecksum(Buffer);

            // exit
            return;
        }
        ////////////////////////////////////////////////////////////////////
        // Read "cmap" table
        ////////////////////////////////////////////////////////////////////
        private void GetcmapTable()
        {
            // set buffer for decoding
            Buffer = TableRecordArray[(Int32) Tag.cmap].Data;
            BufPtr = 0;

            // create cmap object
            if(ReadUInt16BigEndian() != 0) throw new ApplicationException("CMAP table version number is not zero");
            Int32 NumberOfTables = ReadUInt16BigEndian();
            cmapSubTbl[] SubTblArray = new cmapSubTbl[NumberOfTables];

            // loop for tables
            for(Int32 Index = 0; Index < NumberOfTables; Index++)
            {
            cmapSubTbl SubTbl = new cmapSubTbl();
            SubTblArray[Index] = SubTbl;
            SubTbl.PlatformID = ReadUInt16BigEndian();
            SubTbl.EncodingID = ReadUInt16BigEndian();
            SubTbl.Offset = ReadUInt32BigEndian();

            // save buffer pointer
            Int32 SaveBufPtr = BufPtr;

            // set offset
            BufPtr = (Int32) SubTbl.Offset;

            // read format code
            SubTbl.Format = ReadUInt16BigEndian();

            // process format 0
            if(SubTbl.Format == 0)
                {
                SubTbl.Length = ReadUInt16BigEndian();
                SubTbl.Language = ReadUInt16BigEndian();
                SubTbl.GlyphArray = new UInt16[256];
                for(Int32 Code = 0; Code < 256; Code++) SubTbl.GlyphArray[Code] = Buffer[BufPtr++];
                }

            // process format 4
            else if(SubTbl.Format == 4)
                {
                SubTbl.Length = ReadUInt16BigEndian();
                SubTbl.Language = ReadUInt16BigEndian();
                SubTbl.SegCount = (UInt16) (ReadUInt16BigEndian() / 2);
                BufPtr += 6;	// skip search range, entry selector and range shift
                SubTbl.SegArray = new cmapSeg[SubTbl.SegCount];
                for(Int32 Seg = 0; Seg < SubTbl.SegCount; Seg++) SubTbl.SegArray[Seg] = new cmapSeg(ReadUInt16BigEndian()); // EndChar
                ReadUInt16BigEndian(); // skip reserved padding
                for(Int32 Seg = 0; Seg < SubTbl.SegCount; Seg++) SubTbl.SegArray[Seg].StartChar = ReadUInt16BigEndian();
                for(Int32 Seg = 0; Seg < SubTbl.SegCount; Seg++) SubTbl.SegArray[Seg].IDDelta = ReadInt16BigEndian();
                for(Int32 Seg = 0; Seg < SubTbl.SegCount; Seg++) SubTbl.SegArray[Seg].IDRangeOffset = (UInt16) (ReadUInt16BigEndian() / 2);
                Int32 GlyphCount = (SubTbl.Length - 16 - 8 * SubTbl.SegCount) / 2;
                SubTbl.GlyphArray = new UInt16[GlyphCount];
                for(Int32 Glyph = 0; Glyph < GlyphCount; Glyph++) SubTbl.GlyphArray[Glyph] = ReadUInt16BigEndian();
                }

            // restore buffer pointer
            BufPtr = SaveBufPtr;
            }

            // sort table
            Array.Sort(SubTblArray);

            // select 'best' sub-table for character code to glyph code translation
            cmapSubTbl = SelectcmapSubTable(SubTblArray);

            // exit
            return;
        }
        ////////////////////////////////////////////////////////////////////
        // Select best sub-table in "cmap" table
        ////////////////////////////////////////////////////////////////////
        private cmapSubTbl SelectcmapSubTable(
			cmapSubTbl[]	SubTblArray
			)
        {
            // search for platform ID = 3 Windows, encoding ID = 0 or 1 Unicode and format 4
            cmapSubTbl SearchSubTbl = new cmapSubTbl(3, (UInt16) (SymbolicFont ? 0 : 1), 4);
            Int32 Index = Array.BinarySearch(SubTblArray, SearchSubTbl);
            if(Index >= 0) return(SubTblArray[Index]);

            // search for platform ID = 3 Windows, encoding ID = 0 or 1 Unicode and format 0
            SearchSubTbl.Format = 0;
            Index = Array.BinarySearch(SubTblArray, SearchSubTbl);
            if(Index >= 0) return(SubTblArray[Index]);

            // not found
            throw new ApplicationException("Required cmap sub-table is missing");
        }