#region PDFsharp - A .NET library for processing PDF
//
// Authors:
//   Stefan Lange (mailto:Stefan.Lange@pdfsharp.com)
//
// Copyright (c) 2005-2007 empira Software GmbH, Cologne (Germany)
//
// http://www.pdfsharp.com
// http://sourceforge.net/projects/pdfsharp
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
// DEALINGS IN THE SOFTWARE.
#endregion

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using PdfSharp.Internal;

using Fixed = System.Int32;
using fword = System.Int16;
using ufword = System.UInt16;

namespace PdfSharp.Fonts.TrueType
{
  class FontImage
  {
    byte[] bytes;
    //int size;
    int pos;

    //    public FontImage(byte[] bytes)
    //    {
    //      this.bytes = bytes;
    //    }

#if Gdip
    public FontImage(System.Drawing.Font font)
    {
#if DEBUG_
      NativeMethods.LOGFONT logFont = new NativeMethods.LOGFONT();
      font.ToLogFont(logFont);
#endif
      IntPtr hfont = font.ToHfont();
      IntPtr hdc = NativeMethods.GetDC(IntPtr.Zero);
      IntPtr oldFont = NativeMethods.SelectObject(hdc, hfont);
      // size is exactly the size of the font file.
      int size = NativeMethods.GetFontData(hdc, 0, 0, null, 0);
      int error = Marshal.GetLastWin32Error();
      error.GetType();
      this.bytes = new byte[size];
      int xx = NativeMethods.GetFontData(hdc, 0, 0, this.bytes, this.bytes.Length);
      NativeMethods.SelectObject(hdc, oldFont);
      NativeMethods.ReleaseDC(IntPtr.Zero, hdc);
      Read();

      // TODO After we read the tables we don't need the image anymore. Save a LOGFONT as a
      // way to resurect it if needed (e.g. for embedding).
    }
#endif

    public byte[] Bytes
    {
      get { return this.bytes; }
    }

    uint version;
    int tableCount;
    DirectoryEntry[] directoryEntries;

    // required tables:
    internal CMapTable cmap;
    internal MaximumProfileTable maxp;
    internal FontHeaderTable head;
    internal HorizontalHeaderTable hhea;
    internal HorizontalMetricsTable hmtx;
    internal OS2Table os2;
    internal PostScriptTable post;
    // TODO Do we need the name table?

    /// <summary>
    /// Reads all required table from the font prgram.
    /// </summary>
    internal void Read()
    {
      try
      {
        this.version = ReadULong();
        this.tableCount = ReadUShort();
        this.directoryEntries = new DirectoryEntry[this.tableCount];

        // Move to table dictionary at position 12.
        Seek(12);
        for (int idx = 0; idx < this.tableCount; idx++)
        {
          //Seek(12 + (idx << 4));
          DirectoryEntry entry = new DirectoryEntry();
          entry.Read(this);
          this.directoryEntries[idx] = entry;
        }

        ////// must not be a bitmap-only font
        ////if (Directory.LookUp("bhed"))
        ////{
        ////  ASSERT_THROW("TrueType Bitmap Fonts are not supported");
        ////}

        // Read tables
        if (Seek(CMapTable.Tag) != -1)
          this.cmap = new CMapTable(this);

        if (Seek(MaximumProfileTable.Tag) != -1)
          this.maxp = new MaximumProfileTable(this);

        if (Seek(FontHeaderTable.Tag) != -1)
          this.head = new FontHeaderTable(this);

        if (Seek(HorizontalHeaderTable.Tag) != -1)
          this.hhea = new HorizontalHeaderTable(this);

        if (Seek(HorizontalMetricsTable.Tag) != -1)
          this.hmtx = new HorizontalMetricsTable(this);

        if (Seek(OS2Table.Tag) != -1)
          this.os2 = new OS2Table(this);

        if (Seek(PostScriptTable.Tag) != -1)
          this.post = new PostScriptTable(this);
      }
      catch (Exception ex)
      {
        throw ex;
      }
    }

    public int Seek(int position)
    {
      this.pos = position;
      return this.pos;
    }

    public int SeekOffset(int offset)
    {
      this.pos += offset;
      return this.pos;
    }

    public int Position
    {
      get { return this.pos; }
    }

    public int Seek(string tag)
    {
      for (int idx = 0; idx < this.directoryEntries.Length; idx++)
      {
        if (this.directoryEntries[idx].tag == tag)
          return Seek(this.directoryEntries[idx].offset);
      }
      return -1;
    }

    /// <summary>
    /// Reads a System.Byte.
    /// </summary>
    public byte ReadByte()
    {
      return this.bytes[this.pos++];
    }

    /// <summary>
    /// Reads a System.Int16.
    /// </summary>
    public short ReadShort()
    {
      int pos = this.pos;
      this.pos += 2;
      return (short)((this.bytes[pos] << 8) | (this.bytes[pos + 1]));
    }

    /// <summary>
    /// Reads a System.UInt16.
    /// </summary>
    public ushort ReadUShort()
    {
      int pos = this.pos;
      this.pos += 2;
      return (ushort)((this.bytes[pos] << 8) | (this.bytes[pos + 1]));
    }

    /// <summary>
    /// Reads a System.Int32.
    /// </summary>
    public int ReadLong()
    {
      int pos = this.pos;
      this.pos += 4;
      return (int)((this.bytes[pos] << 24) | (this.bytes[pos + 1] << 16) | (this.bytes[pos + 2] << 8) | (this.bytes[pos + 3]));
    }

    /// <summary>
    /// Reads a System.UInt32.
    /// </summary>
    public uint ReadULong()
    {
      int pos = this.pos;
      this.pos += 4;
      return (uint)((this.bytes[pos] << 24) | (this.bytes[pos + 1] << 16) | (this.bytes[pos + 2] << 8) | (this.bytes[pos + 3]));
    }

    /// <summary>
    /// Reads a System.Int32.
    /// </summary>
    public Fixed ReadFixed()
    {
      int pos = this.pos;
      this.pos += 4;
      return (int)((this.bytes[pos] << 24) | (this.bytes[pos + 1] << 16) | (this.bytes[pos + 2] << 8) | (this.bytes[pos + 3]));
    }

    /// <summary>
    /// Reads a System.Int16.
    /// </summary>
    public short ReadFWord()
    {
      int pos = this.pos;
      this.pos += 2;
      return (short)((this.bytes[pos] << 8) | (this.bytes[pos + 1]));
    }

    /// <summary>
    /// Reads a System.UInt16.
    /// </summary>
    public ushort ReadUFWord()
    {
      int pos = this.pos;
      this.pos += 2;
      return (ushort)((this.bytes[pos] << 8) | (this.bytes[pos + 1]));
    }

    /// <summary>
    /// Reads a System.Int64.
    /// </summary>
    public long ReadLongDate()
    {
      int pos = this.pos;
      this.pos += 8;
      return (int)((this.bytes[pos] << 56) | (this.bytes[pos + 1] << 48) | (this.bytes[pos + 2] << 40) | (this.bytes[pos + 32]) |
                   (this.bytes[pos + 4] << 24) | (this.bytes[pos + 5] << 16) | (this.bytes[pos + 5] << 8) | (this.bytes[pos + 7]));
    }

    /// <summary>
    /// Reads a System.String with the specified size.
    /// </summary>
    public string ReadString(int size)
    {
      char[] chars = new char[size];
      for (int idx = 0; idx < size; idx++)
        chars[idx] = (char)this.bytes[this.pos++];
      return new string(chars);
    }

    /// <summary>
    /// Reads a System.Byte[] with the specified size.
    /// </summary>
    public byte[] ReadBytes(int size)
    {
      byte[] bytes = new byte[size];
      for (int idx = 0; idx < size; idx++)
        bytes[idx] = this.bytes[this.pos++];
      return bytes;
    }

    /// <summary>
    /// Reads a System.Char[4] as System.String.
    /// </summary>
    public string ReadTag()
    {
      return ReadString(4);
      //char[] tag = new char[4];
      //tag[0] = (char)this.bytes[this.pos++];
      //tag[1] = (char)this.bytes[this.pos++];
      //tag[2] = (char)this.bytes[this.pos++];
      //tag[3] = (char)this.bytes[this.pos++];
      //return new string(tag);
    }
  }

  internal class DirectoryEntry
  {
    public string tag;
    public uint checkSum;
    public int offset;
    public uint length;

    public void Read(FontImage image)
    {
      this.tag = image.ReadTag();
      this.checkSum = image.ReadULong();
      this.offset = image.ReadLong();
      this.length = image.ReadULong();
    }
  }

  internal enum PlatformId
  {
    Apple, Mac, Iso, Win
  }

  internal enum WinEncodingId
  {
    Symbol, Unicode
  }

  /// <summary>
  /// CMap format 4: Segment mapping to delta values.
  /// The Windows standard format.
  /// </summary>
  internal class CMap4
  {
    public WinEncodingId encodingId; // Windows encoding ID.
    public ushort format;           // Format number is set to 4.
    public ushort length;           // This is the length in bytes of the subtable. 
    public ushort language;         // This field must be set to zero for all cmap subtables whose platform IDs are other than Macintosh (platform ID 1). 
    public ushort segCountX2;       // 2 x segCount.
    public ushort searchRange;      // 2 x (2**floor(log2(segCount)))
    public ushort entrySelector;    // log2(searchRange/2)
    public ushort rangeShift;
    public ushort[] endCount;        // [segCount] / End characterCode for each segment, last=0xFFFF.
    public ushort[] startCount;      // [segCount] / Start character code for each segment.
    public short[] idDelta;         // [segCount] / Delta for all character codes in segment.
    public ushort[] idRangeOffs;     // [segCount] / Offsets into glyphIdArray or 0
    public int glyphCount;      // = (length - (16 + 4 * 2 * segCount)) / 2;
    public ushort[] glyphIdArray;    // Glyph index array (arbitrary length)

    public CMap4(FontImage image, WinEncodingId encodingId)
    {
      this.encodingId = encodingId;
      Read(image);
    }

    public CMap4(WinEncodingId encodingId)
    {
      this.encodingId = encodingId;
    }

    internal void Read(FontImage image)
    {
      try
      {
        // m_EncodingID     = encID;
        this.format = image.ReadUShort();
        Debug.Assert(this.format == 4, "Only format 4 expected.");
        this.length = image.ReadUShort();
        this.language = image.ReadUShort();  // Always null in Windows
        this.segCountX2 = image.ReadUShort();
        this.searchRange = image.ReadUShort();
        this.entrySelector = image.ReadUShort();
        this.rangeShift = image.ReadUShort();

        int segCount = this.segCountX2 / 2;
        this.glyphCount = (this.length - (16 + 8 * segCount)) / 2;

        //ASSERT_CONDITION(0 <= m_NumGlyphIds && m_NumGlyphIds < m_Length, "Invalid Index");

        this.endCount = new ushort[segCount];
        this.startCount = new ushort[segCount];
        this.idDelta = new short[segCount];
        this.idRangeOffs = new ushort[segCount];

        this.glyphIdArray = new ushort[this.glyphCount];

        for (int idx = 0; idx < segCount; idx++)
          this.endCount[idx] = image.ReadUShort();

        //ASSERT_CONDITION(m_EndCount[segs - 1] == 0xFFFF, "Out of Index");

        // Read reserved pad.
        image.ReadUShort();

        for (int idx = 0; idx < segCount; idx++)
          this.startCount[idx] = image.ReadUShort();

        for (int idx = 0; idx < segCount; idx++)
          this.idDelta[idx] = image.ReadShort();

        for (int idx = 0; idx < segCount; idx++)
          this.idRangeOffs[idx] = image.ReadUShort();

        for (int idx = 0; idx < this.glyphCount; idx++)
          this.glyphIdArray[idx] = image.ReadUShort();
      }
      catch (Exception ex)
      {
        throw ex;
      }
    }
  }

  internal class CMapTable
  {
    public const string Tag = "cmap";

    public ushort version;
    public ushort numTables;

    /// <summary>
    /// Is true for symbol font encoding.
    /// </summary>
    public bool symbol;

    public CMap4 cmap4;

    public CMapTable(FontImage image)
    {
      Read(image);
    }

    internal void Read(FontImage image)
    {
      try
      {
        int tableOffset = image.Position;

        this.version = image.ReadUShort();
        this.numTables = image.ReadUShort();

        bool success = false;
        for (int idx = 0; idx < this.numTables; idx++)
        {
          PlatformId platformId = (PlatformId)image.ReadUShort();
          WinEncodingId encodingId = (WinEncodingId)image.ReadUShort();
          int offset = image.ReadLong();

          int currentPosition = image.Position;

          // Just read Windows stuff
          if (platformId == PlatformId.Win && (encodingId == WinEncodingId.Symbol || encodingId == WinEncodingId.Unicode))
          {
            this.symbol = encodingId == WinEncodingId.Symbol;

            image.Seek(tableOffset + offset);
            this.cmap4 = new CMap4(image, encodingId);
            image.Seek(currentPosition);
            // We have found what we are looking for, so break.
            success = true;
            break;
          }
        }
        if (!success)
          throw new InvalidOperationException("Font has no usable platform or encoding ID.");
      }
      catch (Exception ex)
      {
        throw ex;
      }
    }
  }

  internal class FontHeaderTable
  {
    public const string Tag = "head";

    public Fixed version;               // 0x00010000 for version 1.0.
    public Fixed fontRevision;
    public uint checkSumAdjustment;
    public uint magicNumber;            // Set to 0x5F0F3CF5
    public ushort flags;
    public ushort unitsPerEm;           // Valid range is from 16 to 16384. This value should be a power of 2 for fonts that have TrueType outlines.
    public long created;
    public long modified;
    public short xMin, yMin;            // For all glyph bounding boxes.
    public short xMax, yMax;            // For all glyph bounding boxes.
    public ushort macStyle;
    public ushort lowestRecPPEM;
    public short fontDirectionHint;
    public short indexToLocFormat;
    public short glyphDataFormat;

    public FontHeaderTable(FontImage image)
    {
      Read(image);
    }

    public void Read(FontImage image)
    {
      try
      {
        this.version = image.ReadFixed();
        this.fontRevision = image.ReadFixed();
        this.checkSumAdjustment = image.ReadULong();
        this.magicNumber = image.ReadULong();
        this.flags = image.ReadUShort();
        this.unitsPerEm = image.ReadUShort();
        this.created = image.ReadLongDate();
        this.modified = image.ReadLongDate();
        this.xMin = image.ReadShort();
        this.yMin = image.ReadShort();
        this.xMax = image.ReadShort();
        this.yMax = image.ReadShort();
        this.macStyle = image.ReadUShort();
        this.lowestRecPPEM = image.ReadUShort();
        this.fontDirectionHint = image.ReadShort();
        this.indexToLocFormat = image.ReadShort();
        this.glyphDataFormat = image.ReadShort();
      }
      catch (Exception ex)
      {
        throw ex;
      }
    }
  }


  internal class HorizontalHeaderTable
  {
    public const string Tag = "hhea";

    public Fixed version;                 // 0x00010000 for version 1.0.
    public fword ascender;                // Typographic ascent. (Distance from baseline of highest ascender) 
    public fword descender;               // Typographic descent. (Distance from baseline of lowest descender) 
    public fword lineGap;                 // Typographic line gap. Negative LineGap values are treated as zero in Windows 3.1, System 6, and System 7.
    public ufword advanceWidthMax;
    public fword minLeftSideBearing;
    public fword minRightSideBearing;
    public fword xMaxExtent;
    public short caretSlopeRise;
    public short caretSlopeRun;
    public short reserved1;
    public short reserved2;
    public short reserved3;
    public short reserved4;
    public short reserved5;
    public short metricDataFormat;
    public ushort numberOfHMetrics;

    public HorizontalHeaderTable(FontImage image)
    {
      Read(image);
    }

    public void Read(FontImage image)
    {
      try
      {
        this.version = image.ReadFixed();
        this.ascender = image.ReadFWord();
        this.descender = image.ReadFWord();
        this.lineGap = image.ReadFWord();
        this.advanceWidthMax = image.ReadUFWord();
        this.minLeftSideBearing = image.ReadFWord();
        this.minRightSideBearing = image.ReadFWord();
        this.xMaxExtent = image.ReadFWord();
        this.caretSlopeRise = image.ReadShort();
        this.caretSlopeRun = image.ReadShort();
        this.reserved1 = image.ReadShort();
        this.reserved2 = image.ReadShort();
        this.reserved3 = image.ReadShort();
        this.reserved4 = image.ReadShort();
        this.reserved5 = image.ReadShort();
        this.metricDataFormat = image.ReadShort();
        this.numberOfHMetrics = image.ReadUShort();
      }
      catch (Exception ex)
      {
        throw ex;
      }
    }
  }

  internal class HorizontalMetrics
  {
    public ushort advanceWidth;
    public short lsb;

    public HorizontalMetrics(FontImage image)
    {
      Read(image);
    }

    public void Read(FontImage image)
    {
      try
      {
        this.advanceWidth = image.ReadUFWord();
        this.lsb = image.ReadFWord();
      }
      catch (Exception ex)
      {
        throw ex;
      }
    }
  }

  internal class HorizontalMetricsTable
  {
    public const string Tag = "hmtx";

    public HorizontalMetrics[] metrics;
    public fword[] leftSideBearing;

    public HorizontalMetricsTable(FontImage image)
    {
      Read(image);
    }

    public void Read(FontImage image)
    {
      try
      {
        HorizontalHeaderTable hhea = image.hhea;
        MaximumProfileTable maxp = image.maxp;
        if (hhea != null && maxp != null)
        {
          int numMetrics = hhea.numberOfHMetrics; //->NumberOfHMetrics();
          int numLsbs = maxp.numGlyphs - numMetrics;

          Debug.Assert(numMetrics != 0);
          Debug.Assert(numLsbs >= 0);

          this.metrics = new HorizontalMetrics[numMetrics];
          for (int idx = 0; idx < numMetrics; idx++)
            this.metrics[idx] = new HorizontalMetrics(image);

          if (numLsbs > 0)
          {
            this.leftSideBearing = new fword[numLsbs];
            for (int idx = 0; idx < numLsbs; idx++)
              this.leftSideBearing[idx] = image.ReadFWord();
          }
        }
      }
      catch (Exception ex)
      {
        throw ex;
      }
    }
  }

  /// <summary>
  /// This table establishes the memory requirements for this font.
  /// Fonts with CFF data must use Version 0.5 of this table, specifying only the numGlyphs field.
  /// Fonts with TrueType outlines must use Version 1.0 of this table, where all data is required.
  /// Both formats of OpenType require a 'maxp' table because a number of applications call the 
  /// Windows GetFontData() API on the 'maxp' table to determine the number of glyphs in the font.
  /// </summary>
  internal class MaximumProfileTable
  {
    public const string Tag = "maxp";

    public Fixed version;
    public ushort numGlyphs;
    public ushort maxPoints;
    public ushort maxContours;
    public ushort maxCompositePoints;
    public ushort maxCompositeContours;
    public ushort maxZones;
    public ushort maxTwilightPoints;
    public ushort maxStorage;
    public ushort maxFunctionDefs;
    public ushort maxInstructionDefs;
    public ushort maxStackElements;
    public ushort maxSizeOfInstructions;
    public ushort maxComponentElements;
    public ushort maxComponentDepth;

    public MaximumProfileTable(FontImage image)
    {
      Read(image);
    }

    public void Read(FontImage image)
    {
      try
      {
        this.version = image.ReadFixed();
        this.numGlyphs = image.ReadUShort();
        this.maxPoints = image.ReadUShort();
        this.maxContours = image.ReadUShort();
        this.maxCompositePoints = image.ReadUShort();
        this.maxCompositeContours = image.ReadUShort();
        this.maxZones = image.ReadUShort();
        this.maxTwilightPoints = image.ReadUShort();
        this.maxStorage = image.ReadUShort();
        this.maxFunctionDefs = image.ReadUShort();
        this.maxInstructionDefs = image.ReadUShort();
        this.maxStackElements = image.ReadUShort();
        this.maxSizeOfInstructions = image.ReadUShort();
        this.maxComponentElements = image.ReadUShort();
        this.maxComponentDepth = image.ReadUShort();
      }
      catch (Exception ex)
      {
        throw ex;
      }
    }
  }

  /// <summary>
  /// The OS/2 table consists of a set of metrics that are required in OpenType fonts. 
  /// </summary>
  internal class OS2Table
  {
    public const string Tag = "OS/2";

    public ushort version;
    public short xAvgCharWidth;
    public ushort usWeightClass;
    public ushort usWidthClass;
    public ushort fsType;
    public short ySubscriptXSize;
    public short ySubscriptYSize;
    public short ySubscriptXOffset;
    public short ySubscriptYOffset;
    public short ySuperscriptXSize;
    public short ySuperscriptYSize;
    public short ySuperscriptXOffset;
    public short ySuperscriptYOffset;
    public short yStrikeoutSize;
    public short yStrikeoutPosition;
    public short sFamilyClass;
    public byte[] panose; // = new byte[10];
    public uint ulUnicodeRange1;         // Bits 0-31
    public uint ulUnicodeRange2;         // Bits 32-63
    public uint ulUnicodeRange3;         // Bits 64-95
    public uint ulUnicodeRange4;         // Bits 96-127
    public string achVendID; // = "";
    public ushort fsSelection;
    public ushort usFirstCharIndex;
    public ushort usLastCharIndex;
    public short sTypoAscender;
    public short sTypoDescender;
    public short sTypoLineGap;
    public ushort usWinAscent;
    public ushort usWinDescent;
    // version >= 1
    public uint ulCodePageRange1;        // Bits 0-31
    public uint ulCodePageRange2;        // Bits 32-63
    // version >= 2
    public short sxHeight;
    public short sCapHeight;
    public ushort usDefaultChar;
    public ushort usBreakChar;
    public ushort usMaxContext;

    public OS2Table(FontImage image)
    {
      Read(image);
    }

    public void Read(FontImage image)
    {
      try
      {
        this.version = image.ReadUShort();
        this.xAvgCharWidth = image.ReadShort();
        this.usWeightClass = image.ReadUShort();
        this.usWidthClass = image.ReadUShort();
        this.fsType = image.ReadUShort();
        this.ySubscriptXSize = image.ReadShort();
        this.ySubscriptYSize = image.ReadShort();
        this.ySubscriptXOffset = image.ReadShort();
        this.ySubscriptYOffset = image.ReadShort();
        this.ySuperscriptXSize = image.ReadShort();
        this.ySuperscriptYSize = image.ReadShort();
        this.ySuperscriptXOffset = image.ReadShort();
        this.ySuperscriptYOffset = image.ReadShort();
        this.yStrikeoutSize = image.ReadShort();
        this.yStrikeoutPosition = image.ReadShort();
        this.sFamilyClass = image.ReadShort();
        this.panose = image.ReadBytes(10);
        this.ulUnicodeRange1 = image.ReadULong();
        this.ulUnicodeRange2 = image.ReadULong();
        this.ulUnicodeRange3 = image.ReadULong();
        this.ulUnicodeRange4 = image.ReadULong();
        this.achVendID = image.ReadString(4);
        this.fsSelection = image.ReadUShort();
        this.usFirstCharIndex = image.ReadUShort();
        this.usLastCharIndex = image.ReadUShort();
        this.sTypoAscender = image.ReadShort();
        this.sTypoDescender = image.ReadShort();
        this.sTypoLineGap = image.ReadShort();
        this.usWinAscent = image.ReadUShort();
        this.usWinDescent = image.ReadUShort();

        if (this.version >= 1)
        {
          this.ulCodePageRange1 = image.ReadULong();
          this.ulCodePageRange2 = image.ReadULong();
        }

        if (this.version >= 2)
        {
          this.sxHeight = image.ReadShort();
          this.sCapHeight = image.ReadShort();
          this.usDefaultChar = image.ReadUShort();
          this.usBreakChar = image.ReadUShort();
          this.usMaxContext = image.ReadUShort();
        }
      }
      catch (Exception ex)
      {
        throw ex;
      }
    }
  }

  /// <summary>
  /// This table contains additional information needed to use TrueType or OpenTypeTM fonts
  /// on PostScript printers. 
  /// </summary>
  internal class PostScriptTable
  {
    public const string Tag = "post";

    public Fixed formatType;
    public float italicAngle;
    public fword underlinePosition;
    public fword underlineThickness;
    public ulong isFixedPitch;
    public ulong minMemType42;
    public ulong maxMemType42;
    public ulong minMemType1;
    public ulong maxMemType1;

    public PostScriptTable(FontImage image)
    {
      Read(image);
    }

    public void Read(FontImage image)
    {
      try
      {
        this.formatType = image.ReadFixed();
        this.italicAngle = image.ReadFixed() / 65536f;
        this.underlinePosition = image.ReadFWord();
        this.underlineThickness = image.ReadFWord();
        this.isFixedPitch = image.ReadULong();
        this.minMemType42 = image.ReadULong();
        this.maxMemType42 = image.ReadULong();
        this.minMemType1 = image.ReadULong();
        this.maxMemType1 = image.ReadULong();
      }
      catch (Exception ex)
      {
        throw ex;
      }
    }
  }

  //  /// <summary>
  //  /// Windows code page IDs.
  //  /// </summary>
  //  internal enum WindowsCodePage
  //  {
  //    CP1252 =  0,  // Latin 1
  //    CP1250 =  1,  // Latin 2: Eastern Europe
  //    CP1251 =  2,  // Cyrillic
  //    CP1253 =  3,  // Greek
  //    CP1254 =  4,  // Turkish
  //    CP1255 =  5,  // Hebrew
  //    CP1256 =  6,  // Arabic
  //    CP1257 =  7,  // Windows Baltic
  //    CP1258 =  8,  // Vietnamese
  //    CP874  = 16,  // Thai
  //    CP932  = 17,  // JIS/Japan
  //    CP936  = 18,  // Chinese: Simplified chars--PRC and Singapore
  //    CP949  = 19,  // Korean Wansung
  //    CP950  = 20,  // Chinese: Traditional chars--Taiwan and Hong Kong
  //    CP1361 = 21,  // Korean Johab
  //    CP869  = 48,  // IBM Greek
  //    CP866  = 49,  // MS-DOS Russian
  //    CP865  = 50,  // MS-DOS Nordic
  //    CP864  = 51,  // Arabic
  //    CP863  = 52,  // MS-DOS Canadian French
  //    CP862  = 53,  // Hebrew
  //    CP861  = 54,  // MS-DOS Icelandic
  //    CP860  = 55,  // MS-DOS Portuguese
  //    CP857  = 56,  // IBM Turkish
  //    CP855  = 57,  // IBM Cyrillic; primarily Russian
  //    CP852  = 58,  // Latin 2
  //    CP775  = 59,  // MS-DOS Baltic
  //    CP737  = 60,  // Greek; former 437 G
  //    CP708  = 61,  // Arabic; ASMO 708
  //    CP850  = 62,  // WE/Latin 1
  //    CP437  = 63,  // US
  //  }
}
