/// <summary>
        /// Prepares the object to get saved.
        /// </summary>
        internal override void PrepareForSave()
        {
            base.PrepareForSave();

            // Fonts are always embedded.
            OpenTypeFontface subSet = FontDescriptor._descriptor.FontFace.CreateFontSubSet(_cmapInfo.GlyphIndices, false);

            byte[] fontData = subSet.FontSource.Bytes;

            PdfDictionary fontStream = new PdfDictionary(Owner);

            Owner.Internals.AddObject(fontStream);
            FontDescriptor.Elements[PdfFontDescriptor.Keys.FontFile2] = fontStream.Reference;

            fontStream.Elements["/Length1"] = new PdfInteger(fontData.Length);
            if (!Owner.Options.NoCompression)
            {
                fontData = Filtering.FlateDecode.Encode(fontData, _document.Options.FlateEncodeMode);
                fontStream.Elements["/Filter"] = new PdfName("/FlateDecode");
            }
            fontStream.Elements["/Length"] = new PdfInteger(fontData.Length);
            fontStream.CreateStream(fontData);

            FirstChar = 0;
            LastChar  = 255;
            PdfArray width = Widths;

            //width.Elements.Clear();
            for (int idx = 0; idx < 256; idx++)
            {
                width.Elements.Add(new PdfInteger(FontDescriptor._descriptor.Widths[idx]));
            }
        }
Exemple #2
0
        /// <summary>
        /// Prepares the object to get saved.
        /// </summary>
        internal override void PrepareForSave()
        {
            base.PrepareForSave();

#if DEBUG_
            if (FontDescriptor._descriptor.FontFace.loca == null)
            {
                GetType();
            }
#endif
            // CID fonts must be always embedded. PDFsharp embedds automatically a subset.
            OpenTypeFontface subSet = null;
            if (FontDescriptor._descriptor.FontFace.loca == null)
            {
                subSet = FontDescriptor._descriptor.FontFace;
            }
            else
            {
                subSet = FontDescriptor._descriptor.FontFace.CreateFontSubSet(_cmapInfo.GlyphIndices, true);
            }
            byte[]        fontData   = subSet.FontSource.Bytes;
            PdfDictionary fontStream = new PdfDictionary(Owner);
            Owner.Internals.AddObject(fontStream);
            FontDescriptor.Elements[PdfFontDescriptor.Keys.FontFile2] = fontStream.Reference;

            fontStream.Elements["/Length1"] = new PdfInteger(fontData.Length);
            if (!Owner.Options.NoCompression)
            {
                fontData = Filtering.FlateDecode.Encode(fontData, _document.Options.FlateEncodeMode);
                fontStream.Elements["/Filter"] = new PdfName("/FlateDecode");
            }
            fontStream.Elements["/Length"] = new PdfInteger(fontData.Length);
            fontStream.CreateStream(fontData);
        }
Exemple #3
0
        /// <summary>
        /// Prepares the object to get saved.
        /// </summary>
        internal override void PrepareForSave()
        {
            base.PrepareForSave();

            if (FontEmbedding == PdfFontEmbedding.Always || FontEmbedding == PdfFontEmbedding.Automatic)
            {
                FontData subSet   = this.fontDescriptor.descriptor.fontData.CreateFontSubSet(this.cmapInfo.GlyphIndices, false);
                byte[]   fontData = subSet.Data;

#if DEBUG_
                TrueTypeFontSubSet fss        = new TrueTypeFontSubSet("", this.cmapInfo.descriptor.fontData, this.cmapInfo.GlyphIndices, 0, true, false);
                byte[]             fontSubSet = fss.Process();
                fss.CompareBytes(fontSubSet, fontProgram);
#endif
                PdfDictionary fontStream = new PdfDictionary(this.Owner);
                this.Owner.Internals.AddObject(fontStream);
                this.fontDescriptor.Elements[PdfFontDescriptor.Keys.FontFile2] = fontStream.Reference;

                fontStream.Elements["/Length1"] = new PdfInteger(fontData.Length);
                if (!this.Owner.Options.NoCompression)
                {
                    fontData = Filtering.FlateDecode.Encode(fontData);
                    fontStream.Elements["/Filter"] = new PdfName("/FlateDecode");
                }
                fontStream.Elements["/Length"] = new PdfInteger(fontData.Length);
                fontStream.CreateStream(fontData);
            }

            //if (this.cmapInfo == null)
            //{
            FirstChar = 0;
            LastChar  = 255;
            PdfArray width = Widths;
            //width.Elements.Clear();
            for (int idx = 0; idx < 256; idx++)
            {
                width.Elements.Add(new PdfInteger(this.fontDescriptor.descriptor.widths[idx]));
            }
            //}
            //else
            //{
            //  FirstChar = (char)Math.Min(this.cmapInfo.MinChar, 255u);
            //  LastChar = (char)Math.Min(this.cmapInfo.MaxChar, 255u);

            //  PdfArray width = Widths;
            //  Debug.Assert(width.Elements.Count == 0);
            //  //width.Elements.Clear();
            //  for (int idx = FirstChar; idx <= LastChar; idx++)
            //  {
            //    int charWidth = 0;
            //    if (this.cmapInfo.Contains((char)idx))
            //      charWidth = this.fontDescriptor.descriptor.widths[idx];
            //    width.Elements.Add(new PdfInteger(charWidth));
            //  }
            //}
        }
Exemple #4
0
        public static void CreateXmpMetadata(PdfDocument document, PdfCompat compat)
        {
            var metadataDict = new PdfDictionary(document);

            metadataDict.Elements["/Type"]    = new PdfName("/Metadata");
            metadataDict.Elements["/Subtype"] = new PdfName("/XML");
            metadataDict.CreateStream(CreateRawXmpMetadata(document.Info, GetConformance(compat)));
            document.Internals.AddObject(metadataDict);
            document.Internals.Catalog.Elements["/Metadata"] = metadataDict.Reference;
        }
        private static PdfDictionary ProcessFilters(PdfDictionary dictionary)
        {
            PdfDictionary result;

            // Create a dictionary mapping (i.e. switch statement) to process the expected filters.
            var map = new Dictionary <string, Func <byte[], byte[]> >()
            {
                { "/FlateDecode", (d) => {
                      var decoder = new FlateDecode();
                      return(decoder.Decode(d));
                  } }
            };

            // Get all of the filters.
            var filters = ((PdfArray)dictionary.Elements["/Filter"])
                          .Elements.Where(e => e.IsName())
                          .Select(e => ((PdfName)e).Value)
                          .ToList();

            // If only one filter in array. Just rewrite the /Filter
            if (filters.Count == 1)
            {
                result = dictionary.Clone();
                result.Elements["/Filter"] = new PdfName(filters[0]);
                return(result);
            }

            // Process each filter in order. The last filter should be the actual encoded image.
            byte[] data = dictionary.Stream.Value;
            for (int index = 0; index < (filters.Count - 1); index++)
            {
                if (!map.ContainsKey(filters[index]))
                {
                    throw new NotSupportedException(String.Format("Encountered embedded image with multiple filters: \"{0}\". Unable to process the filter: \"{1}\".",
                                                                  String.Join(",", filters), filters[index]));
                }
                data = map[filters[index]].Invoke(data);
            }

            result = new PdfDictionary();
            result.Elements.Add("/Filter", new PdfName(filters.Last()));
            foreach (var element in dictionary.Elements.Where(e => !String.Equals(e.Key, "/Filter", StringComparison.OrdinalIgnoreCase)))
            {
                result.Elements.Add(element.Key, element.Value);
            }
            result.CreateStream(data);

            return(result);
        }
        /// <summary>
        /// Prepares the object to get saved.
        /// </summary>
        internal override void PrepareForSave()
        {
            base.PrepareForSave();

#if DEBUG
            if (this.fontDescriptor.descriptor.fontData.loca == null)
            {
                GetType();
            }
#endif
            // CID fonts must be always embedded. PDFsharp embedds automatically a subset.
            FontData subSet = null;
            if (this.fontDescriptor.descriptor.fontData.loca == null)
            {
                subSet = this.fontDescriptor.descriptor.fontData;
            }
            else
            {
                subSet = this.fontDescriptor.descriptor.fontData.CreateFontSubSet(this.cmapInfo.GlyphIndices, true);
            }
            byte[] fontData = subSet.Data;
#if DEBUG_
            TrueTypeFontSubSet fss        = new TrueTypeFontSubSet("", this.cmapInfo.descriptor.fontData, this.cmapInfo.GlyphIndices, 0, false, false);
            byte[]             fontSubSet = fss.Process();
            fss.CompareBytes(fontSubSet, fontProgram);
#endif
            PdfDictionary fontStream = new PdfDictionary(this.Owner);
            this.Owner.Internals.AddObject(fontStream);
            this.fontDescriptor.Elements[PdfFontDescriptor.Keys.FontFile2] = fontStream.Reference;

            fontStream.Elements["/Length1"] = new PdfInteger(fontData.Length);
            if (!this.Owner.Options.NoCompression)
            {
                fontData = Filtering.FlateDecode.Encode(fontData);
                fontStream.Elements["/Filter"] = new PdfName("/FlateDecode");
            }
            fontStream.Elements["/Length"] = new PdfInteger(fontData.Length);
            fontStream.CreateStream(fontData);
        }
    /// <summary>
    /// Prepares the object to get saved.
    /// </summary>
    internal override void PrepareForSave()
    {
      base.PrepareForSave();

      if (FontEmbedding == PdfFontEmbedding.Always || FontEmbedding == PdfFontEmbedding.Automatic)
      {
        FontData subSet = this.fontDescriptor.descriptor.fontData.CreateFontSubSet(this.cmapInfo.GlyphIndices, false);
        byte[] fontData = subSet.Data;

#if DEBUG_
        TrueTypeFontSubSet fss = new TrueTypeFontSubSet("", this.cmapInfo.descriptor.fontData, this.cmapInfo.GlyphIndices, 0, true, false);
        byte[] fontSubSet = fss.Process();
        fss.CompareBytes(fontSubSet, fontProgram);
#endif
        PdfDictionary fontStream = new PdfDictionary(this.Owner);
        this.Owner.Internals.AddObject(fontStream);
        this.fontDescriptor.Elements[PdfFontDescriptor.Keys.FontFile2] = fontStream.Reference;

        fontStream.Elements["/Length1"] = new PdfInteger(fontData.Length);
        if (!this.Owner.Options.NoCompression)
        {
          fontData = Filtering.FlateDecode.Encode(fontData);
          fontStream.Elements["/Filter"] = new PdfName("/FlateDecode");
        }
        fontStream.Elements["/Length"] = new PdfInteger(fontData.Length);
        fontStream.CreateStream(fontData);
      }

      //if (this.cmapInfo == null)
      //{
        FirstChar = 0;
        LastChar = 255;
        PdfArray width = Widths;
        //width.Elements.Clear();
        for (int idx = 0; idx < 256; idx++)
          width.Elements.Add(new PdfInteger(this.fontDescriptor.descriptor.widths[idx]));
      //}
      //else
      //{
      //  FirstChar = (char)Math.Min(this.cmapInfo.MinChar, 255u);
      //  LastChar = (char)Math.Min(this.cmapInfo.MaxChar, 255u);

      //  PdfArray width = Widths;
      //  Debug.Assert(width.Elements.Count == 0);
      //  //width.Elements.Clear();
      //  for (int idx = FirstChar; idx <= LastChar; idx++)
      //  {
      //    int charWidth = 0;
      //    if (this.cmapInfo.Contains((char)idx))
      //      charWidth = this.fontDescriptor.descriptor.widths[idx];
      //    width.Elements.Add(new PdfInteger(charWidth));
      //  }
      //}
    }
Exemple #8
0
        /// <summary>
        /// Prepares the object to get saved.
        /// </summary>
        internal override void PrepareForSave()
        {
            base.PrepareForSave();

#if DEBUG_
            if (FontDescriptor._descriptor.FontFace.loca == null)
            {
                GetType();
            }
#endif
            // CID fonts must be always embedded. PDFsharp embedds automatically a subset.
            OpenTypeFontface subSet = null;
            if (FontDescriptor._descriptor.FontFace.loca == null)
                subSet = FontDescriptor._descriptor.FontFace;
            else
                subSet = FontDescriptor._descriptor.FontFace.CreateFontSubSet(_cmapInfo.GlyphIndices, true);
            byte[] fontData = subSet.FontSource.Bytes;
            PdfDictionary fontStream = new PdfDictionary(Owner);
            Owner.Internals.AddObject(fontStream);
            FontDescriptor.Elements[PdfFontDescriptor.Keys.FontFile2] = fontStream.Reference;

            fontStream.Elements["/Length1"] = new PdfInteger(fontData.Length);
            if (!Owner.Options.NoCompression)
            {
                fontData = Filtering.FlateDecode.Encode(fontData, _document.Options.FlateEncodeMode);
                fontStream.Elements["/Filter"] = new PdfName("/FlateDecode");
            }
            fontStream.Elements["/Length"] = new PdfInteger(fontData.Length);
            fontStream.CreateStream(fontData);
        }
Exemple #9
0
    /* BITMAPINFOHEADER struct and byte offsets:
        typedef struct tagBITMAPINFOHEADER{
          DWORD  biSize;           // 14
          LONG   biWidth;          // 18
          LONG   biHeight;         // 22
          WORD   biPlanes;         // 26
          WORD   biBitCount;       // 28
          DWORD  biCompression;    // 30
          DWORD  biSizeImage;      // 34
          LONG   biXPelsPerMeter;  // 38
          LONG   biYPelsPerMeter;  // 42
          DWORD  biClrUsed;        // 46
          DWORD  biClrImportant;   // 50
        } BITMAPINFOHEADER, *PBITMAPINFOHEADER; 
    */

    private void ReadIndexedMemoryBitmap(int bits/*, ref bool hasAlpha*/)
    {
#if DEBUG_
      image.image.Save("$$$.bmp", ImageFormat.Bmp);
#endif
      int pdfVersion = Owner.Version;
      int firstMaskColor = -1, lastMaskColor = -1;
      bool segmentedColorMask = false;

      MemoryStream memory = new MemoryStream();
#if GDI
      image.gdiImage.Save(memory, ImageFormat.Bmp);
#endif
#if WPF
#if !SILVERLIGHT
      // WPFTHHO: StL: keine Ahnung ob das so stimmt.
      BmpBitmapEncoder encoder = new BmpBitmapEncoder();
      encoder.Frames.Add(BitmapFrame.Create(image.wpfImage));
      encoder.Save(memory);
#else
      // AGHACK
#endif
#endif
      int streamLength = (int)memory.Length;
      Debug.Assert(streamLength > 0, "Bitmap image encoding failed.");
      if (streamLength > 0)
      {
        byte[] imageBits = new byte[streamLength];
        memory.Seek(0, SeekOrigin.Begin);
        memory.Read(imageBits, 0, streamLength);
        memory.Close();

        int height = image.PixelHeight;
        int width = image.PixelWidth;

        if (ReadWord(imageBits, 0) != 0x4d42 || // "BM"
          ReadDWord(imageBits, 2) != streamLength ||
          ReadDWord(imageBits, 14) != 40 || // sizeof BITMAPINFOHEADER
#if WPF
          // TODOWPF: bug with height and width??? With which files???
          ReadDWord(imageBits, 18) != width ||
          ReadDWord(imageBits, 22) != height)
#else
          ReadDWord(imageBits, 18) != width ||
          ReadDWord(imageBits, 22) != height)
#endif
        {
          throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format");
        }
#if WPF
        // TODOWPF: bug with height and width
        width = ReadDWord(imageBits, 18);
        height = ReadDWord(imageBits, 22);
#endif
        int fileBits = ReadWord(imageBits, 28);
        if (fileBits != bits)
        {
          if (fileBits == 1 || fileBits == 4 || fileBits == 8)
            bits = fileBits;
        }

        if (ReadWord(imageBits, 26) != 1 ||
            ReadWord(imageBits, 28) != bits ||
            ReadDWord(imageBits, 30) != 0)
        {
          throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format #2");
        }

        int bytesFileOffset = ReadDWord(imageBits, 10);
        int bytesColorPaletteOffset = 0x36; // GDI+ always returns Windows bitmaps: sizeof BITMAPFILEHEADER + sizeof BITMAPINFOHEADER
        int paletteColors = ReadDWord(imageBits, 46);
        if ((bytesFileOffset - bytesColorPaletteOffset) / 4 != paletteColors)
        {
          throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format #3");
        }

        MonochromeMask mask = new MonochromeMask(width, height);

        bool isGray = bits == 8 && (paletteColors == 256 || paletteColors == 0);
        int isBitonal = 0; // 0: false; >0: true; <0: true (inverted)
        byte[] paletteData = new byte[3 * paletteColors];
        for (int color = 0; color < paletteColors; ++color)
        {
          paletteData[3 * color] = imageBits[bytesColorPaletteOffset + 4 * color + 2];
          paletteData[3 * color + 1] = imageBits[bytesColorPaletteOffset + 4 * color + 1];
          paletteData[3 * color + 2] = imageBits[bytesColorPaletteOffset + 4 * color + 0];
          if (isGray)
            isGray = paletteData[3 * color] == paletteData[3 * color + 1] &&
              paletteData[3 * color] == paletteData[3 * color + 2];

          if (imageBits[bytesColorPaletteOffset + 4 * color + 3] < 128)
          {
            // We treat this as transparency:
            if (firstMaskColor == -1)
              firstMaskColor = color;
            if (lastMaskColor == -1 || lastMaskColor == color - 1)
              lastMaskColor = color;
            if (lastMaskColor != color)
              segmentedColorMask = true;
          }
          //else
          //{
          //  // We treat this as opacity:
          //}
        }

        if (bits == 1)
        {
          if (paletteColors == 0)
            isBitonal = 1;
          if (paletteColors == 2)
          {
            if (paletteData[0] == 0 &&
              paletteData[1] == 0 &&
              paletteData[2] == 0 &&
              paletteData[3] == 255 &&
              paletteData[4] == 255 &&
              paletteData[5] == 255)
              isBitonal = 1; // Black on white
            if (paletteData[5] == 0 &&
              paletteData[4] == 0 &&
              paletteData[3] == 0 &&
              paletteData[2] == 255 &&
              paletteData[1] == 255 &&
              paletteData[0] == 255)
              isBitonal = -1; // White on black
          }
        }

        // NYI: (no sample found where this was required) 
        // if (segmentedColorMask = true)
        // { ... }

        FlateDecode fd = new FlateDecode();
        PdfDictionary colorPalette = null;
        if (isBitonal == 0 && !isGray)
        {
          colorPalette = new PdfDictionary(this.document);
          byte[] packedPaletteData = paletteData.Length >= 48 ? fd.Encode(paletteData) : null; // don't compress small palettes
          if (packedPaletteData != null && packedPaletteData.Length + 20 < paletteData.Length) // +20: compensate for the overhead (estimated value)
          {
            // Create compressed color palette:
            colorPalette.CreateStream(packedPaletteData);
            colorPalette.Elements[Keys.Length] = new PdfInteger(packedPaletteData.Length);
            colorPalette.Elements[Keys.Filter] = new PdfName("/FlateDecode");
          }
          else
          {
            // Create uncompressed color palette:
            colorPalette.CreateStream(paletteData);
            colorPalette.Elements[Keys.Length] = new PdfInteger(paletteData.Length);
          }
          Owner.irefTable.Add(colorPalette);
        }

        bool isFaxEncoding = false;
        byte[] imageData = new byte[((width * bits + 7) / 8) * height];
        byte[] imageDataFax = null;
        int k = 0;


        if (bits == 1)
        {
          // TODO: flag/option?
          // We try Group 3 1D and Group 4 (2D) encoding here and keep the smaller byte array.
          //byte[] temp = new byte[imageData.Length];
          //int ccittSize = DoFaxEncoding(ref temp, imageBits, (uint)bytesFileOffset, (uint)width, (uint)height);

          // It seems that Group 3 2D encoding never beats both other encodings, therefore we don't call it here.
          //byte[] temp2D = new byte[imageData.Length];
          //uint dpiY = (uint)image.VerticalResolution;
          //uint kTmp = 0;
          //int ccittSize2D = DoFaxEncoding2D((uint)bytesFileOffset, ref temp2D, imageBits, (uint)width, (uint)height, dpiY, out kTmp);
          //k = (int) kTmp;

          byte[] tempG4 = new byte[imageData.Length];
          int ccittSizeG4 = DoFaxEncodingGroup4(ref tempG4, imageBits, (uint)bytesFileOffset, (uint)width, (uint)height);

          isFaxEncoding = /*ccittSize > 0 ||*/ ccittSizeG4 > 0;
          if (isFaxEncoding)
          {
            //if (ccittSize == 0)
            //  ccittSize = 0x7fffffff;
            if (ccittSizeG4 == 0)
              ccittSizeG4 = 0x7fffffff;
            //if (ccittSize <= ccittSizeG4)
            //{
            //  Array.Resize(ref temp, ccittSize);
            //  imageDataFax = temp;
            //  k = 0;
            //}
            //else
            {
              Array.Resize(ref tempG4, ccittSizeG4);
              imageDataFax = tempG4;
              k = -1;
            }
          }
        }

        //if (!isFaxEncoding)
        {
          int bytesOffsetRead = 0;
          if (bits == 8 || bits == 4 || bits == 1)
          {
            int bytesPerLine = (width * bits + 7) / 8;
            for (int y = 0; y < height; ++y)
            {
              mask.StartLine(y);
              int bytesOffsetWrite = (height - 1 - y) * ((width * bits + 7) / 8);
              for (int x = 0; x < bytesPerLine; ++x)
              {
                if (isGray)
                {
                  // Lookup the gray value from the palette:
                  imageData[bytesOffsetWrite] = paletteData[3 * imageBits[bytesFileOffset + bytesOffsetRead]];
                }
                else
                {
                  // Store the palette index:
                  imageData[bytesOffsetWrite] = imageBits[bytesFileOffset + bytesOffsetRead];
                }
                if (firstMaskColor != -1)
                {
                  int n = imageBits[bytesFileOffset + bytesOffsetRead];
                  if (bits == 8)
                  {
                    // TODO???: segmentedColorMask == true => bad mask NYI
                    mask.AddPel((n >= firstMaskColor) && (n <= lastMaskColor));
                  }
                  else if (bits == 4)
                  {
                    // TODO???: segmentedColorMask == true => bad mask NYI
                    int n1 = (n & 0xf0) / 16;
                    int n2 = (n & 0x0f);
                    mask.AddPel((n1 >= firstMaskColor) && (n1 <= lastMaskColor));
                    mask.AddPel((n2 >= firstMaskColor) && (n2 <= lastMaskColor));
                  }
                  else if (bits == 1)
                  {
                    // TODO???: segmentedColorMask == true => bad mask NYI
                    for (int bit = 1; bit <= 8; ++bit)
                    {
                      int n1 = (n & 0x80) / 128;
                      mask.AddPel((n1 >= firstMaskColor) && (n1 <= lastMaskColor));
                      n *= 2;
                    }
                  }
                }
                bytesOffsetRead += 1;
                bytesOffsetWrite += 1;
              }
              bytesOffsetRead = 4 * ((bytesOffsetRead + 3) / 4); // Align to 32 bit boundary
            }
          }
          else
          {
            throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format #3");
          }
        }

        if (firstMaskColor != -1 &&
          lastMaskColor != -1)
        {
          // Color mask requires Reader 4.0 or higher:
          if (!segmentedColorMask && pdfVersion >= 13)
          {
            PdfArray array = new PdfArray(document);
            array.Elements.Add(new PdfInteger(firstMaskColor));
            array.Elements.Add(new PdfInteger(lastMaskColor));
            Elements[Keys.Mask] = array;
          }
          else
          {
            // Monochrome mask
            byte[] maskDataCompressed = fd.Encode(mask.MaskData);
            PdfDictionary pdfMask = new PdfDictionary(document);
            pdfMask.Elements.SetName(Keys.Type, "/XObject");
            pdfMask.Elements.SetName(Keys.Subtype, "/Image");

            this.Owner.irefTable.Add(pdfMask);
            pdfMask.Stream = new PdfStream(maskDataCompressed, pdfMask);
            pdfMask.Elements[Keys.Length] = new PdfInteger(maskDataCompressed.Length);
            pdfMask.Elements[Keys.Filter] = new PdfName("/FlateDecode");
            pdfMask.Elements[Keys.Width] = new PdfInteger(width);
            pdfMask.Elements[Keys.Height] = new PdfInteger(height);
            pdfMask.Elements[Keys.BitsPerComponent] = new PdfInteger(1);
            pdfMask.Elements[Keys.ImageMask] = new PdfBoolean(true);
            Elements[Keys.Mask] = pdfMask.Reference;
          }
        }

        byte[] imageDataCompressed = fd.Encode(imageData);
        byte[] imageDataFaxCompressed = isFaxEncoding ? fd.Encode(imageDataFax) : null;

        bool usesCcittEncoding = false;
        if (isFaxEncoding &&
          (imageDataFax.Length < imageDataCompressed.Length ||
          imageDataFaxCompressed.Length < imageDataCompressed.Length))
        {
          // /CCITTFaxDecode creates the smaller file (with or without /FlateDecode):
          usesCcittEncoding = true;

          if (imageDataFax.Length < imageDataCompressed.Length)
          {
            Stream = new PdfStream(imageDataFax, this);
            Elements[Keys.Length] = new PdfInteger(imageDataFax.Length);
            Elements[Keys.Filter] = new PdfName("/CCITTFaxDecode");
            //PdfArray array2 = new PdfArray(this.document);
            PdfDictionary dictionary = new PdfDictionary();
            if (k != 0)
              dictionary.Elements.Add("/K", new PdfInteger(k));
            if (isBitonal < 0)
              dictionary.Elements.Add("/BlackIs1", new PdfBoolean(true));
            dictionary.Elements.Add("/EndOfBlock", new PdfBoolean(false));
            dictionary.Elements.Add("/Columns", new PdfInteger(width));
            dictionary.Elements.Add("/Rows", new PdfInteger(height));
            //array2.Elements.Add(dictionary);
            Elements[Keys.DecodeParms] = dictionary; // array2;
          }
          else
          {
            Stream = new PdfStream(imageDataFaxCompressed, this);
            Elements[Keys.Length] = new PdfInteger(imageDataFaxCompressed.Length);
            PdfArray arrayFilters = new PdfArray(document);
            arrayFilters.Elements.Add(new PdfName("/FlateDecode"));
            arrayFilters.Elements.Add(new PdfName("/CCITTFaxDecode"));
            Elements[Keys.Filter] = arrayFilters;
            PdfArray arrayDecodeParms = new PdfArray(document);

            PdfDictionary dictFlateDecodeParms = new PdfDictionary();
            //dictFlateDecodeParms.Elements.Add("/Columns", new PdfInteger(1));

            PdfDictionary dictCcittFaxDecodeParms = new PdfDictionary();
            if (k != 0)
              dictCcittFaxDecodeParms.Elements.Add("/K", new PdfInteger(k));
            if (isBitonal < 0)
              dictCcittFaxDecodeParms.Elements.Add("/BlackIs1", new PdfBoolean(true));
            dictCcittFaxDecodeParms.Elements.Add("/EndOfBlock", new PdfBoolean(false));
            dictCcittFaxDecodeParms.Elements.Add("/Columns", new PdfInteger(width));
            dictCcittFaxDecodeParms.Elements.Add("/Rows", new PdfInteger(height));

            arrayDecodeParms.Elements.Add(dictFlateDecodeParms); // How to add the "null object"?
            arrayDecodeParms.Elements.Add(dictCcittFaxDecodeParms);
            Elements[Keys.DecodeParms] = arrayDecodeParms;
          }
        }
        else
        {
          // /FlateDecode creates the smaller file (or no monochrome bitmap):
          Stream = new PdfStream(imageDataCompressed, this);
          Elements[Keys.Length] = new PdfInteger(imageDataCompressed.Length);
          Elements[Keys.Filter] = new PdfName("/FlateDecode");
        }

        Elements[Keys.Width] = new PdfInteger(width);
        Elements[Keys.Height] = new PdfInteger(height);
        Elements[Keys.BitsPerComponent] = new PdfInteger(bits);
        // TODO: CMYK

        // CCITT encoding: we need color palette for isBitonal == 0
        // FlateDecode: we need color palette for isBitonal <= 0 unless we have grayscales
        if ((usesCcittEncoding && isBitonal == 0) ||
          (!usesCcittEncoding && isBitonal <= 0  && !isGray))
        {
          PdfArray arrayColorSpace = new PdfArray(document);
          arrayColorSpace.Elements.Add(new PdfName("/Indexed"));
          arrayColorSpace.Elements.Add(new PdfName("/DeviceRGB"));
          arrayColorSpace.Elements.Add(new PdfInteger(paletteColors - 1));
          // ReSharper disable PossibleNullReferenceException
          arrayColorSpace.Elements.Add(colorPalette.Reference);
          // ReSharper restore PossibleNullReferenceException
          Elements[Keys.ColorSpace] = arrayColorSpace;
        }
        else
        {
          Elements[Keys.ColorSpace] = new PdfName("/DeviceGray");
        }
        if (image.Interpolate)
          Elements[Keys.Interpolate] = PdfBoolean.True;
      }
    }
Exemple #10
0
        private void CreateIndexedMemoryBitmap(int bits)
        {
            ImageDataBitmap idb = (ImageDataBitmap)_image._importedImage.ImageData;
            ImageInformation ii = _image._importedImage.Information;

            int pdfVersion = Owner.Version;
            int firstMaskColor = -1, lastMaskColor = -1;
            bool segmentedColorMask = idb.SegmentedColorMask;

            {

                FlateDecode fd = new FlateDecode();
                if (firstMaskColor != -1 &&
                  lastMaskColor != -1)
                {
                    // Color mask requires Reader 4.0 or higher:
                    //if (!segmentedColorMask && pdfVersion >= 13)
                    if (!segmentedColorMask && pdfVersion >= 13 && !idb.IsGray)
                    {
                        PdfArray array = new PdfArray(_document);
                        array.Elements.Add(new PdfInteger(firstMaskColor));
                        array.Elements.Add(new PdfInteger(lastMaskColor));
                        Elements[Keys.Mask] = array;
                    }
                    else
                    {
                        // Monochrome mask
                        byte[] maskDataCompressed = fd.Encode(idb.BitmapMask, _document.Options.FlateEncodeMode);
                        PdfDictionary pdfMask = new PdfDictionary(_document);
                        pdfMask.Elements.SetName(Keys.Type, "/XObject");
                        pdfMask.Elements.SetName(Keys.Subtype, "/Image");

                        Owner._irefTable.Add(pdfMask);
                        pdfMask.Stream = new PdfStream(maskDataCompressed, pdfMask);
                        pdfMask.Elements[PdfStream.Keys.Length] = new PdfInteger(maskDataCompressed.Length);
                        pdfMask.Elements[PdfStream.Keys.Filter] = new PdfName("/FlateDecode");
                        pdfMask.Elements[Keys.Width] = new PdfInteger((int)ii.Width);
                        pdfMask.Elements[Keys.Height] = new PdfInteger((int)ii.Height);
                        pdfMask.Elements[Keys.BitsPerComponent] = new PdfInteger(1);
                        pdfMask.Elements[Keys.ImageMask] = new PdfBoolean(true);
                        Elements[Keys.Mask] = pdfMask.Reference;
                    }
                }

                byte[] imageDataCompressed = fd.Encode(idb.Data, _document.Options.FlateEncodeMode);
                byte[] imageDataFaxCompressed = idb.DataFax != null ? fd.Encode(idb.DataFax, _document.Options.FlateEncodeMode) : null;

                bool usesCcittEncoding = false;
                if (idb.DataFax != null &&
                  (idb.LengthFax < imageDataCompressed.Length ||
                  imageDataFaxCompressed.Length < imageDataCompressed.Length))
                {
                    // /CCITTFaxDecode creates the smaller file (with or without /FlateDecode):
                    usesCcittEncoding = true;

                    if (idb.LengthFax < imageDataCompressed.Length)
                    {
                        Stream = new PdfStream(idb.DataFax, this);
                        Elements[PdfStream.Keys.Length] = new PdfInteger(idb.LengthFax);
                        Elements[PdfStream.Keys.Filter] = new PdfName("/CCITTFaxDecode");
                        //PdfArray array2 = new PdfArray(_document);
                        PdfDictionary dictionary = new PdfDictionary();
                        if (idb.K != 0)
                            dictionary.Elements.Add("/K", new PdfInteger(idb.K));
                        if (idb.IsBitonal < 0)
                            dictionary.Elements.Add("/BlackIs1", new PdfBoolean(true));
                        dictionary.Elements.Add("/EndOfBlock", new PdfBoolean(false));
                        dictionary.Elements.Add("/Columns", new PdfInteger((int)ii.Width));
                        dictionary.Elements.Add("/Rows", new PdfInteger((int)ii.Height));
                        //array2.Elements.Add(dictionary);
                        Elements[PdfStream.Keys.DecodeParms] = dictionary; // array2;
                    }
                    else
                    {
                        Stream = new PdfStream(imageDataFaxCompressed, this);
                        Elements[PdfStream.Keys.Length] = new PdfInteger(imageDataFaxCompressed.Length);
                        PdfArray arrayFilters = new PdfArray(_document);
                        arrayFilters.Elements.Add(new PdfName("/FlateDecode"));
                        arrayFilters.Elements.Add(new PdfName("/CCITTFaxDecode"));
                        Elements[PdfStream.Keys.Filter] = arrayFilters;
                        PdfArray arrayDecodeParms = new PdfArray(_document);

                        PdfDictionary dictFlateDecodeParms = new PdfDictionary();
                        //dictFlateDecodeParms.Elements.Add("/Columns", new PdfInteger(1));

                        PdfDictionary dictCcittFaxDecodeParms = new PdfDictionary();
                        if (idb.K != 0)
                            dictCcittFaxDecodeParms.Elements.Add("/K", new PdfInteger(idb.K));
                        if (idb.IsBitonal < 0)
                            dictCcittFaxDecodeParms.Elements.Add("/BlackIs1", new PdfBoolean(true));
                        dictCcittFaxDecodeParms.Elements.Add("/EndOfBlock", new PdfBoolean(false));
                        dictCcittFaxDecodeParms.Elements.Add("/Columns", new PdfInteger((int)ii.Width));
                        dictCcittFaxDecodeParms.Elements.Add("/Rows", new PdfInteger((int)ii.Height));

                        arrayDecodeParms.Elements.Add(dictFlateDecodeParms); // How to add the "null object"?
                        arrayDecodeParms.Elements.Add(dictCcittFaxDecodeParms);
                        Elements[PdfStream.Keys.DecodeParms] = arrayDecodeParms;
                    }
                }
                else
                {
                    // /FlateDecode creates the smaller file (or no monochrome bitmap):
                    Stream = new PdfStream(imageDataCompressed, this);
                    Elements[PdfStream.Keys.Length] = new PdfInteger(imageDataCompressed.Length);
                    Elements[PdfStream.Keys.Filter] = new PdfName("/FlateDecode");
                }

                Elements[Keys.Width] = new PdfInteger((int)ii.Width);
                Elements[Keys.Height] = new PdfInteger((int)ii.Height);
                Elements[Keys.BitsPerComponent] = new PdfInteger(bits);
                // TODO: CMYK

                // CCITT encoding: we need color palette for isBitonal == 0
                // FlateDecode: we need color palette for isBitonal <= 0 unless we have grayscales
                if ((usesCcittEncoding && idb.IsBitonal == 0) ||
                  (!usesCcittEncoding && idb.IsBitonal <= 0 && !idb.IsGray))
                {
                    PdfDictionary colorPalette = null;
                    colorPalette = new PdfDictionary(_document);
                    byte[] packedPaletteData = idb.PaletteDataLength >= 48 ? fd.Encode(idb.PaletteData, _document.Options.FlateEncodeMode) : null; // don't compress small palettes
                    if (packedPaletteData != null && packedPaletteData.Length + 20 < idb.PaletteDataLength) // +20: compensate for the overhead (estimated value)
                    {
                        // Create compressed color palette:
                        colorPalette.CreateStream(packedPaletteData);
                        colorPalette.Elements[PdfStream.Keys.Length] = new PdfInteger(packedPaletteData.Length);
                        colorPalette.Elements[PdfStream.Keys.Filter] = new PdfName("/FlateDecode");
                    }
                    else
                    {
                        // Create uncompressed color palette:
                        colorPalette.CreateStream(idb.PaletteData);
                        colorPalette.Elements[PdfStream.Keys.Length] = new PdfInteger(idb.PaletteDataLength);
                    }
                    Owner._irefTable.Add(colorPalette);

                    PdfArray arrayColorSpace = new PdfArray(_document);
                    arrayColorSpace.Elements.Add(new PdfName("/Indexed"));
                    arrayColorSpace.Elements.Add(new PdfName("/DeviceRGB"));
                    arrayColorSpace.Elements.Add(new PdfInteger((int)ii.ColorsUsed - 1));
                    arrayColorSpace.Elements.Add(colorPalette.Reference);
                    Elements[Keys.ColorSpace] = arrayColorSpace;
                }
                else
                {
                    Elements[Keys.ColorSpace] = new PdfName("/DeviceGray");
                }
                if (_image.Interpolate)
                    Elements[Keys.Interpolate] = PdfBoolean.True;
            }
        }
Exemple #11
0
    /* BITMAPINFOHEADER struct and byte offsets:
        typedef struct tagBITMAPINFOHEADER{
          DWORD  biSize;           // 14
          LONG   biWidth;          // 18
          LONG   biHeight;         // 22
          WORD   biPlanes;         // 26
          WORD   biBitCount;       // 28
          DWORD  biCompression;    // 30
          DWORD  biSizeImage;      // 34
          LONG   biXPelsPerMeter;  // 38
          LONG   biYPelsPerMeter;  // 42
          DWORD  biClrUsed;        // 46
          DWORD  biClrImportant;   // 50
        } BITMAPINFOHEADER, *PBITMAPINFOHEADER; 
    */

    private void ReadIndexedMemoryBitmap(int bits, ref bool hasAlpha)
    {
#if DEBUG_
      image.image.Save("$$$.bmp", ImageFormat.Bmp);
#endif
      int pdfVersion = this.Owner.Version;
      int firstMaskColor = -1, lastMaskColor = -1;
      bool segmentedColorMask = false;

      MemoryStream memory = new MemoryStream();
#if GDI
      image.gdiImage.Save(memory, ImageFormat.Bmp);
#endif
#if WPF
      // WPFTHHO: StL: keine Ahnung ob das so stimmt.
      BmpBitmapEncoder encoder = new BmpBitmapEncoder();
      encoder.Frames.Add(BitmapFrame.Create(this.image.wpfImage));
      encoder.Save(memory);
#endif
      int streamLength = (int)memory.Length;
      Debug.Assert(streamLength > 0, "Bitmap image encoding failed.");
      if (streamLength > 0)
      {
        byte[] imageBits = new byte[streamLength];
        memory.Seek(0, SeekOrigin.Begin);
        memory.Read(imageBits, 0, streamLength);
        memory.Close();

        int height = this.image.PixelHeight;
        int width = this.image.PixelWidth;

        if (ReadWord(imageBits, 0) != 0x4d42 || // "BM"
          ReadDWord(imageBits, 2) != streamLength ||
          ReadDWord(imageBits, 14) != 40 || // sizeof BITMAPINFOHEADER
#if WPF
          // TODOWPF: bug with height and width
          false)
#else
          ReadDWord(imageBits, 18) != width ||
          ReadDWord(imageBits, 22) != height)
#endif
        {
          throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format");
        }
#if WPF
        // TODOWPF: bug with height and width
        width = ReadDWord(imageBits, 18);
        height = ReadDWord(imageBits, 22);
#endif
        if (ReadWord(imageBits, 26) != 1 ||
            ReadWord(imageBits, 28) != bits ||
            ReadDWord(imageBits, 30) != 0)
        {
          throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format #2");
        }

        int bytesFileOffset = ReadDWord(imageBits, 10);
        int bytesColorPaletteOffset = 0x36; // GDI+ always returns Windows bitmaps: sizeof BITMAPFILEHEADER + sizeof BITMAPINFOHEADER
        int paletteColors = ReadDWord(imageBits, 46);
        if ((bytesFileOffset - bytesColorPaletteOffset) / 4 != paletteColors)
        {
          throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format #3");
        }

        MonochromeMask mask = new MonochromeMask(width, height);

        byte[] paletteData = new byte[3 * paletteColors];
        for (int color = 0; color < paletteColors; ++color)
        {
          paletteData[3 * color] = imageBits[bytesColorPaletteOffset + 4 * color + 2];
          paletteData[3 * color + 1] = imageBits[bytesColorPaletteOffset + 4 * color + 1];
          paletteData[3 * color + 2] = imageBits[bytesColorPaletteOffset + 4 * color + 0];
          if (imageBits[bytesColorPaletteOffset + 4 * color + 3] < 128)
          {
            // We treat this as transparency:
            if (firstMaskColor == -1)
              firstMaskColor = color;
            if (lastMaskColor == -1 || lastMaskColor == color - 1)
              lastMaskColor = color;
            if (lastMaskColor != color)
              segmentedColorMask = true;
          }
          else
          {
            // We treat this as opacity:
          }
        }

        // NYI: (no sample found where this was required) 
        // if (segmentedColorMask = true)
        // { ... }

        FlateDecode fd = new FlateDecode();
        PdfDictionary colorPalette = new PdfDictionary(this.document);
        // TODO: decide at run-time if compression makes sense
#if false
        // Create uncompressed color palette:
        colorPalette.CreateStream(paletteData);
        colorPalette.Elements[Keys.Length] = new PdfInteger(paletteData.Length);
#else
        // Create compressed color palette:
        byte[] packedPaletteData = fd.Encode(paletteData);
        colorPalette.CreateStream(packedPaletteData);
        colorPalette.Elements[Keys.Length] = new PdfInteger(packedPaletteData.Length);
        colorPalette.Elements[Keys.Filter] = new PdfName("/FlateDecode");
#endif
        this.Owner.irefTable.Add(colorPalette);

        byte[] imageData = new byte[1 * width * height];

        int bytesOffsetRead = 0;
        if (bits == 8 || bits == 4 || bits == 1)
        {
          int bytesPerLine = (width * bits + 7) / 8;
          for (int y = 0; y < height; ++y)
          {
            mask.StartLine(y);
            int bytesOffsetWrite = (height - 1 - y) * ((width * bits + 7) / 8);
            for (int x = 0; x < bytesPerLine; ++x)
            {
              imageData[bytesOffsetWrite] = imageBits[bytesFileOffset + bytesOffsetRead];
              if (firstMaskColor != -1)
              {
                int n = imageBits[bytesFileOffset + bytesOffsetRead];
                if (bits == 8)
                {
                  // TODO???: segmentedColorMask == true => falsche Maske NYI
                  mask.AddPel((n >= firstMaskColor) && (n <= lastMaskColor));
                }
                else if (bits == 4)
                {
                  // TODO???: segmentedColorMask == true => falsche Maske NYI
                  int n1 = (n & 0xf0) / 16;
                  int n2 = (n & 0x0f);
                  mask.AddPel((n1 >= firstMaskColor) && (n1 <= lastMaskColor));
                  mask.AddPel((n2 >= firstMaskColor) && (n2 <= lastMaskColor));
                }
                else if (bits == 1)
                {
                  // TODO???: segmentedColorMask == true => bad mask NYI
                  for (int bit = 1; bit <= 8; ++bit)
                  {
                    int n1 = (n & 0x80) / 128;
                    mask.AddPel((n1 >= firstMaskColor) && (n1 <= lastMaskColor));
                    n *= 2;
                  }
                }
              }
              bytesOffsetRead += 1;
              bytesOffsetWrite += 1;
            }
            bytesOffsetRead = 4 * ((bytesOffsetRead + 3) / 4); // Align to 32 bit boundary
          }
        }
        else
        {
          throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format #3");
        }

        if (firstMaskColor != -1 &&
          lastMaskColor != -1)
        {
          // Color mask requires Reader 4.0 or higher:
          if (!segmentedColorMask && pdfVersion >= 13)
          {
            PdfArray array = new PdfArray(this.document);
            array.Elements.Add(new PdfInteger(firstMaskColor));
            array.Elements.Add(new PdfInteger(lastMaskColor));
            Elements[Keys.Mask] = array;
          }
          else
          {
            // Monochrome mask
            byte[] maskDataCompressed = fd.Encode(mask.MaskData);
            PdfDictionary pdfMask = new PdfDictionary(this.document);
            pdfMask.Elements.SetName(Keys.Type, "/XObject");
            pdfMask.Elements.SetName(Keys.Subtype, "/Image");

            this.Owner.irefTable.Add(pdfMask);
            pdfMask.Stream = new PdfStream(maskDataCompressed, pdfMask);
            pdfMask.Elements[Keys.Length] = new PdfInteger(maskDataCompressed.Length);
            pdfMask.Elements[Keys.Filter] = new PdfName("/FlateDecode");
            pdfMask.Elements[Keys.Width] = new PdfInteger(width);
            pdfMask.Elements[Keys.Height] = new PdfInteger(height);
            pdfMask.Elements[Keys.BitsPerComponent] = new PdfInteger(1);
            pdfMask.Elements[Keys.ImageMask] = new PdfBoolean(true);
            Elements[Keys.Mask] = pdfMask.Reference;
          }
        }

        byte[] imageDataCompressed = fd.Encode(imageData);

        Stream = new PdfStream(imageDataCompressed, this);
        Elements[Keys.Length] = new PdfInteger(imageDataCompressed.Length);
        Elements[Keys.Filter] = new PdfName("/FlateDecode");
        Elements[Keys.Width] = new PdfInteger(width);
        Elements[Keys.Height] = new PdfInteger(height);
        Elements[Keys.BitsPerComponent] = new PdfInteger(bits);
        PdfArray array2 = new PdfArray(this.document);
        array2.Elements.Add(new PdfName("/Indexed"));
        // TODO: CMYK
        array2.Elements.Add(new PdfName("/DeviceRGB"));
        array2.Elements.Add(new PdfInteger(paletteColors - 1));
        array2.Elements.Add(colorPalette.Reference);
        Elements[Keys.ColorSpace] = array2;
      }
    }
Exemple #12
0
        /// <summary>
        /// Prepares the object to get saved.
        /// </summary>
        internal override void PrepareForSave()
        {
            base.PrepareForSave();

            // Fonts are always embedded.
            OpenTypeFontface subSet = FontDescriptor._descriptor.FontFace.CreateFontSubSet(_cmapInfo.GlyphIndices, false);
            byte[] fontData = subSet.FontSource.Bytes;

            PdfDictionary fontStream = new PdfDictionary(Owner);
            Owner.Internals.AddObject(fontStream);
            FontDescriptor.Elements[PdfFontDescriptor.Keys.FontFile2] = fontStream.Reference;

            fontStream.Elements["/Length1"] = new PdfInteger(fontData.Length);
            if (!Owner.Options.NoCompression)
            {
                fontData = Filtering.FlateDecode.Encode(fontData, _document.Options.FlateEncodeMode);
                fontStream.Elements["/Filter"] = new PdfName("/FlateDecode");
            }
            fontStream.Elements["/Length"] = new PdfInteger(fontData.Length);
            fontStream.CreateStream(fontData);

            FirstChar = 0;
            LastChar = 255;
            PdfArray width = Widths;
            //width.Elements.Clear();
            for (int idx = 0; idx < 256; idx++)
                width.Elements.Add(new PdfInteger(FontDescriptor._descriptor.Widths[idx]));
        }
        /* BITMAPINFOHEADER struct and byte offsets:
         *  typedef struct tagBITMAPINFOHEADER{
         *    DWORD  biSize;           // 14
         *    LONG   biWidth;          // 18
         *    LONG   biHeight;         // 22
         *    WORD   biPlanes;         // 26
         *    WORD   biBitCount;       // 28
         *    DWORD  biCompression;    // 30
         *    DWORD  biSizeImage;      // 34
         *    LONG   biXPelsPerMeter;  // 38
         *    LONG   biYPelsPerMeter;  // 42
         *    DWORD  biClrUsed;        // 46
         *    DWORD  biClrImportant;   // 50
         *  } BITMAPINFOHEADER, *PBITMAPINFOHEADER;
         */

        private void ReadIndexedMemoryBitmap(int bits /*, ref bool hasAlpha*/)
        {
            int  pdfVersion = Owner.Version;
            int  firstMaskColor = -1, lastMaskColor = -1;
            bool segmentedColorMask = false;

            MemoryStream memory = new MemoryStream();
            // THHO4THHO Use ImageImporterBMP here to avoid redundant code.

            int streamLength = (int)memory.Length;

            Debug.Assert(streamLength > 0, "Bitmap image encoding failed.");
            if (streamLength > 0)
            {
                byte[] imageBits = new byte[streamLength];
                memory.Seek(0, SeekOrigin.Begin);
                memory.Read(imageBits, 0, streamLength);
                memory.Dispose();

                int height = _image.PixelHeight;
                int width  = _image.PixelWidth;

                if (ReadWord(imageBits, 0) != 0x4d42 || // "BM"
                    ReadDWord(imageBits, 2) != streamLength ||
                    ReadDWord(imageBits, 14) != 40 ||   // sizeof BITMAPINFOHEADER
                    ReadDWord(imageBits, 18) != width ||
                    ReadDWord(imageBits, 22) != height)
                {
                    throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format");
                }
                int fileBits = ReadWord(imageBits, 28);
                if (fileBits != bits)
                {
                    if (fileBits == 1 || fileBits == 4 || fileBits == 8)
                    {
                        bits = fileBits;
                    }
                }

                if (ReadWord(imageBits, 26) != 1 ||
                    ReadWord(imageBits, 28) != bits ||
                    ReadDWord(imageBits, 30) != 0)
                {
                    throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format #2");
                }

                int       bytesFileOffset         = ReadDWord(imageBits, 10);
                const int bytesColorPaletteOffset = 0x36; // GDI+ always returns Windows bitmaps: sizeof BITMAPFILEHEADER + sizeof BITMAPINFOHEADER
                int       paletteColors           = ReadDWord(imageBits, 46);
                if ((bytesFileOffset - bytesColorPaletteOffset) / 4 != paletteColors)
                {
                    throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format #3");
                }

                MonochromeMask mask = new MonochromeMask(width, height);

                bool   isGray      = bits == 8 && (paletteColors == 256 || paletteColors == 0);
                int    isBitonal   = 0; // 0: false; >0: true; <0: true (inverted)
                byte[] paletteData = new byte[3 * paletteColors];
                for (int color = 0; color < paletteColors; ++color)
                {
                    paletteData[3 * color]     = imageBits[bytesColorPaletteOffset + 4 * color + 2];
                    paletteData[3 * color + 1] = imageBits[bytesColorPaletteOffset + 4 * color + 1];
                    paletteData[3 * color + 2] = imageBits[bytesColorPaletteOffset + 4 * color + 0];
                    if (isGray)
                    {
                        isGray = paletteData[3 * color] == paletteData[3 * color + 1] &&
                                 paletteData[3 * color] == paletteData[3 * color + 2];
                    }

                    if (imageBits[bytesColorPaletteOffset + 4 * color + 3] < 128)
                    {
                        // We treat this as transparency:
                        if (firstMaskColor == -1)
                        {
                            firstMaskColor = color;
                        }
                        if (lastMaskColor == -1 || lastMaskColor == color - 1)
                        {
                            lastMaskColor = color;
                        }
                        if (lastMaskColor != color)
                        {
                            segmentedColorMask = true;
                        }
                    }
                    //else
                    //{
                    //  // We treat this as opacity:
                    //}
                }

                if (bits == 1)
                {
                    if (paletteColors == 0)
                    {
                        isBitonal = 1;
                    }
                    if (paletteColors == 2)
                    {
                        if (paletteData[0] == 0 &&
                            paletteData[1] == 0 &&
                            paletteData[2] == 0 &&
                            paletteData[3] == 255 &&
                            paletteData[4] == 255 &&
                            paletteData[5] == 255)
                        {
                            isBitonal = 1; // Black on white
                        }
                        if (paletteData[5] == 0 &&
                            paletteData[4] == 0 &&
                            paletteData[3] == 0 &&
                            paletteData[2] == 255 &&
                            paletteData[1] == 255 &&
                            paletteData[0] == 255)
                        {
                            isBitonal = -1; // White on black
                        }
                    }
                }

                // NYI: (no sample found where this was required)
                // if (segmentedColorMask = true)
                // { ... }

                bool   isFaxEncoding = false;
                byte[] imageData     = new byte[((width * bits + 7) / 8) * height];
                byte[] imageDataFax  = null;
                int    k             = 0;

                if (bits == 1)
                {
                    // TODO: flag/option?
                    // We try Group 3 1D and Group 4 (2D) encoding here and keep the smaller byte array.
                    //byte[] temp = new byte[imageData.Length];
                    //int ccittSize = DoFaxEncoding(ref temp, imageBits, (uint)bytesFileOffset, (uint)width, (uint)height);

                    // It seems that Group 3 2D encoding never beats both other encodings, therefore we don't call it here.
                    //byte[] temp2D = new byte[imageData.Length];
                    //uint dpiY = (uint)image.VerticalResolution;
                    //uint kTmp = 0;
                    //int ccittSize2D = DoFaxEncoding2D((uint)bytesFileOffset, ref temp2D, imageBits, (uint)width, (uint)height, dpiY, out kTmp);
                    //k = (int) kTmp;

                    byte[] tempG4      = new byte[imageData.Length];
                    int    ccittSizeG4 = DoFaxEncodingGroup4(ref tempG4, imageBits, (uint)bytesFileOffset, (uint)width, (uint)height);

                    isFaxEncoding = /*ccittSize > 0 ||*/ ccittSizeG4 > 0;
                    if (isFaxEncoding)
                    {
                        //if (ccittSize == 0)
                        //  ccittSize = 0x7fffffff;
                        if (ccittSizeG4 == 0)
                        {
                            ccittSizeG4 = 0x7fffffff;
                        }
                        //if (ccittSize <= ccittSizeG4)
                        //{
                        //  Array.Resize(ref temp, ccittSize);
                        //  imageDataFax = temp;
                        //  k = 0;
                        //}
                        //else
                        {
                            Array.Resize(ref tempG4, ccittSizeG4);
                            imageDataFax = tempG4;
                            k            = -1;
                        }
                    }
                }

                //if (!isFaxEncoding)
                {
                    int bytesOffsetRead = 0;
                    if (bits == 8 || bits == 4 || bits == 1)
                    {
                        int bytesPerLine = (width * bits + 7) / 8;
                        for (int y = 0; y < height; ++y)
                        {
                            mask.StartLine(y);
                            int bytesOffsetWrite = (height - 1 - y) * ((width * bits + 7) / 8);
                            for (int x = 0; x < bytesPerLine; ++x)
                            {
                                if (isGray)
                                {
                                    // Lookup the gray value from the palette:
                                    imageData[bytesOffsetWrite] = paletteData[3 * imageBits[bytesFileOffset + bytesOffsetRead]];
                                }
                                else
                                {
                                    // Store the palette index.
                                    imageData[bytesOffsetWrite] = imageBits[bytesFileOffset + bytesOffsetRead];
                                }
                                if (firstMaskColor != -1)
                                {
                                    int n = imageBits[bytesFileOffset + bytesOffsetRead];
                                    if (bits == 8)
                                    {
                                        // TODO???: segmentedColorMask == true => bad mask NYI
                                        mask.AddPel((n >= firstMaskColor) && (n <= lastMaskColor));
                                    }
                                    else if (bits == 4)
                                    {
                                        // TODO???: segmentedColorMask == true => bad mask NYI
                                        int n1 = (n & 0xf0) / 16;
                                        int n2 = (n & 0x0f);
                                        mask.AddPel((n1 >= firstMaskColor) && (n1 <= lastMaskColor));
                                        mask.AddPel((n2 >= firstMaskColor) && (n2 <= lastMaskColor));
                                    }
                                    else if (bits == 1)
                                    {
                                        // TODO???: segmentedColorMask == true => bad mask NYI
                                        for (int bit = 1; bit <= 8; ++bit)
                                        {
                                            int n1 = (n & 0x80) / 128;
                                            mask.AddPel((n1 >= firstMaskColor) && (n1 <= lastMaskColor));
                                            n *= 2;
                                        }
                                    }
                                }
                                bytesOffsetRead  += 1;
                                bytesOffsetWrite += 1;
                            }
                            bytesOffsetRead = 4 * ((bytesOffsetRead + 3) / 4); // Align to 32 bit boundary
                        }
                    }
                    else
                    {
                        throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format #3");
                    }
                }

                FlateDecode fd = new FlateDecode();
                if (firstMaskColor != -1 &&
                    lastMaskColor != -1)
                {
                    // Color mask requires Reader 4.0 or higher:
                    //if (!segmentedColorMask && pdfVersion >= 13)
                    if (!segmentedColorMask && pdfVersion >= 13 && !isGray)
                    {
                        PdfArray array = new PdfArray(_document);
                        array.Elements.Add(new PdfInteger(firstMaskColor));
                        array.Elements.Add(new PdfInteger(lastMaskColor));
                        Elements[Keys.Mask] = array;
                    }
                    else
                    {
                        // Monochrome mask
                        byte[]        maskDataCompressed = fd.Encode(mask.MaskData, _document.Options.FlateEncodeMode);
                        PdfDictionary pdfMask            = new PdfDictionary(_document);
                        pdfMask.Elements.SetName(Keys.Type, "/XObject");
                        pdfMask.Elements.SetName(Keys.Subtype, "/Image");

                        Owner._irefTable.Add(pdfMask);
                        pdfMask.Stream = new PdfStream(maskDataCompressed, pdfMask);
                        pdfMask.Elements[PdfStream.Keys.Length] = new PdfInteger(maskDataCompressed.Length);
                        pdfMask.Elements[PdfStream.Keys.Filter] = new PdfName("/FlateDecode");
                        pdfMask.Elements[Keys.Width]            = new PdfInteger(width);
                        pdfMask.Elements[Keys.Height]           = new PdfInteger(height);
                        pdfMask.Elements[Keys.BitsPerComponent] = new PdfInteger(1);
                        pdfMask.Elements[Keys.ImageMask]        = new PdfBoolean(true);
                        Elements[Keys.Mask] = pdfMask.Reference;
                    }
                }

                byte[] imageDataCompressed    = fd.Encode(imageData, _document.Options.FlateEncodeMode);
                byte[] imageDataFaxCompressed = isFaxEncoding ? fd.Encode(imageDataFax, _document.Options.FlateEncodeMode) : null;

                bool usesCcittEncoding = false;
                if (isFaxEncoding &&
                    (imageDataFax.Length < imageDataCompressed.Length ||
                     imageDataFaxCompressed.Length < imageDataCompressed.Length))
                {
                    // /CCITTFaxDecode creates the smaller file (with or without /FlateDecode):
                    usesCcittEncoding = true;

                    if (imageDataFax.Length < imageDataCompressed.Length)
                    {
                        Stream = new PdfStream(imageDataFax, this);
                        Elements[PdfStream.Keys.Length] = new PdfInteger(imageDataFax.Length);
                        Elements[PdfStream.Keys.Filter] = new PdfName("/CCITTFaxDecode");
                        //PdfArray array2 = new PdfArray(_document);
                        PdfDictionary dictionary = new PdfDictionary();
                        if (k != 0)
                        {
                            dictionary.Elements.Add("/K", new PdfInteger(k));
                        }
                        if (isBitonal < 0)
                        {
                            dictionary.Elements.Add("/BlackIs1", new PdfBoolean(true));
                        }
                        dictionary.Elements.Add("/EndOfBlock", new PdfBoolean(false));
                        dictionary.Elements.Add("/Columns", new PdfInteger(width));
                        dictionary.Elements.Add("/Rows", new PdfInteger(height));
                        //array2.Elements.Add(dictionary);
                        Elements[PdfStream.Keys.DecodeParms] = dictionary; // array2;
                    }
                    else
                    {
                        Stream = new PdfStream(imageDataFaxCompressed, this);
                        Elements[PdfStream.Keys.Length] = new PdfInteger(imageDataFaxCompressed.Length);
                        PdfArray arrayFilters = new PdfArray(_document);
                        arrayFilters.Elements.Add(new PdfName("/FlateDecode"));
                        arrayFilters.Elements.Add(new PdfName("/CCITTFaxDecode"));
                        Elements[PdfStream.Keys.Filter] = arrayFilters;
                        PdfArray arrayDecodeParms = new PdfArray(_document);

                        PdfDictionary dictFlateDecodeParms = new PdfDictionary();
                        //dictFlateDecodeParms.Elements.Add("/Columns", new PdfInteger(1));

                        PdfDictionary dictCcittFaxDecodeParms = new PdfDictionary();
                        if (k != 0)
                        {
                            dictCcittFaxDecodeParms.Elements.Add("/K", new PdfInteger(k));
                        }
                        if (isBitonal < 0)
                        {
                            dictCcittFaxDecodeParms.Elements.Add("/BlackIs1", new PdfBoolean(true));
                        }
                        dictCcittFaxDecodeParms.Elements.Add("/EndOfBlock", new PdfBoolean(false));
                        dictCcittFaxDecodeParms.Elements.Add("/Columns", new PdfInteger(width));
                        dictCcittFaxDecodeParms.Elements.Add("/Rows", new PdfInteger(height));

                        arrayDecodeParms.Elements.Add(dictFlateDecodeParms); // How to add the "null object"?
                        arrayDecodeParms.Elements.Add(dictCcittFaxDecodeParms);
                        Elements[PdfStream.Keys.DecodeParms] = arrayDecodeParms;
                    }
                }
                else
                {
                    // /FlateDecode creates the smaller file (or no monochrome bitmap):
                    Stream = new PdfStream(imageDataCompressed, this);
                    Elements[PdfStream.Keys.Length] = new PdfInteger(imageDataCompressed.Length);
                    Elements[PdfStream.Keys.Filter] = new PdfName("/FlateDecode");
                }

                Elements[Keys.Width]            = new PdfInteger(width);
                Elements[Keys.Height]           = new PdfInteger(height);
                Elements[Keys.BitsPerComponent] = new PdfInteger(bits);
                // TODO: CMYK

                // CCITT encoding: we need color palette for isBitonal == 0
                // FlateDecode: we need color palette for isBitonal <= 0 unless we have grayscales
                if ((usesCcittEncoding && isBitonal == 0) ||
                    (!usesCcittEncoding && isBitonal <= 0 && !isGray))
                {
                    PdfDictionary colorPalette = null;
                    colorPalette = new PdfDictionary(_document);
                    byte[] packedPaletteData = paletteData.Length >= 48 ? fd.Encode(paletteData, _document.Options.FlateEncodeMode) : null; // don't compress small palettes
                    if (packedPaletteData != null && packedPaletteData.Length + 20 < paletteData.Length)                                    // +20: compensate for the overhead (estimated value)
                    {
                        // Create compressed color palette:
                        colorPalette.CreateStream(packedPaletteData);
                        colorPalette.Elements[PdfStream.Keys.Length] = new PdfInteger(packedPaletteData.Length);
                        colorPalette.Elements[PdfStream.Keys.Filter] = new PdfName("/FlateDecode");
                    }
                    else
                    {
                        // Create uncompressed color palette:
                        colorPalette.CreateStream(paletteData);
                        colorPalette.Elements[PdfStream.Keys.Length] = new PdfInteger(paletteData.Length);
                    }
                    Owner._irefTable.Add(colorPalette);

                    PdfArray arrayColorSpace = new PdfArray(_document);
                    arrayColorSpace.Elements.Add(new PdfName("/Indexed"));
                    arrayColorSpace.Elements.Add(new PdfName("/DeviceRGB"));
                    arrayColorSpace.Elements.Add(new PdfInteger(paletteColors - 1));
                    arrayColorSpace.Elements.Add(colorPalette.Reference);
                    Elements[Keys.ColorSpace] = arrayColorSpace;
                }
                else
                {
                    Elements[Keys.ColorSpace] = new PdfName("/DeviceGray");
                }
                if (_image.Interpolate)
                {
                    Elements[Keys.Interpolate] = PdfBoolean.True;
                }
            }
        }