public IndirectObject <PdfInteger> SetData(string data) { DefilteredLength = data.Length; Value = FlateDecode.DeflateString(data); Length = new IndirectObject <PdfInteger>(new PdfInteger(Buffer.Length), 0, 0); return(Length); }
private static Bitmap PdfImageToBitmap(PdfDictionary image) { string filter = image.Elements.GetName(PdfDictionary.PdfStream.Keys.Filter); switch (filter) { case "/DCTDecode": byte[] stream = image.Stream.Value; return(new Bitmap(new MemoryStream(stream))); //Thanks to VacentViscera - https://forum.pdfsharp.net/viewtopic.php?f=8&t=3801 case "/FlateDecode": int width = image.Elements.GetInteger(PdfImage.Keys.Width); int height = image.Elements.GetInteger(PdfImage.Keys.Height); int bitsPerComponent = image.Elements.GetInteger(PdfImage.Keys.BitsPerComponent); FlateDecode flate = new FlateDecode(); byte[] imageData = flate.Decode(image.Stream.Value); PixelFormat pixelFormat; switch (bitsPerComponent) { case 1: pixelFormat = PixelFormat.Format1bppIndexed; break; case 8: pixelFormat = PixelFormat.Format8bppIndexed; break; case 24: pixelFormat = PixelFormat.Format24bppRgb; break; default: throw new Exception("Unknown pixel format: " + bitsPerComponent); } Bitmap bitmap = new Bitmap(width, height, pixelFormat); var bmpData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, pixelFormat); int length = (int)Math.Ceiling(width * bitsPerComponent / 8.0); for (int i = 0; i < height; i++) { Marshal.Copy(imageData, i * length, new IntPtr(bmpData.Scan0.ToInt32() + i * bmpData.Stride), length); } bitmap.UnlockBits(bmpData); return(bitmap); default: return(null); } }
private void SetColorPallete(Bitmap bmp, PdfArray cSpace) { var globals2 = cSpace.Elements.GetReference(3).Value as PdfDictionary; var palData = new FlateDecode().Decode(globals2.Stream.Value); ColorPalette pal = bmp.Palette; for (int i = 0; i < palData.Length; i += 3) { pal.Entries[i / 3] = Color.FromArgb(255, palData[i], palData[i + 1], palData[i + 2]); } bmp.Palette = pal; }
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); }
static string ExportVectors(PdfDocument document, PdfDictionary image, ref int count) { // get the stream bytes byte[] imgData = image.Stream.Value; // sometimes an image can be dual encoded, if so decode the first layer var filters = image.Elements.GetArray("/Filter"); string filter; if (filters != null && filters.Elements.GetName(0) == "/FlateDecode") { // FlateDecode imgData = new FlateDecode().Decode(image.Stream.Value); filter = filters.Elements.GetName(1); } else if (filters != null && filters.Elements.Count == 1) { filter = filters.Elements.GetName(0); } else { filter = image.Elements.GetName("/Filter"); } if (filter == "/FlateDecode") { imgData = new FlateDecode().Decode(image.Stream.Value); } string data = new StreamReader(new MemoryStream(imgData)).ReadToEnd(); File.WriteAllText("content_" + cnt++ + ".txt", data); // for some reason the jpeg image is rotated. Not sure how to detect this so for now just rotate them 90deg CC //if (filter == "/DCTDecode" || filter == "/JBIG2Decode") // bmp.RotateFlip(rotateFlipType: System.Drawing.RotateFlipType.Rotate270FlipNone); return(data); }
/// <summary> /// Creates the keys for a JPEG image. /// </summary> void InitializeJpeg() { byte[] imageBits = null; using (MemoryStream memory = _image.AsJpeg()) { imageBits = memory.ToArray(); } bool tryFlateDecode = _document.Options.UseFlateDecoderForJpegImages == PdfUseFlateDecoderForJpegImages.Automatic; bool useFlateDecode = _document.Options.UseFlateDecoderForJpegImages == PdfUseFlateDecoderForJpegImages.Always; FlateDecode fd = new FlateDecode(); byte[] imageDataCompressed = (useFlateDecode || tryFlateDecode) ? fd.Encode(imageBits, _document.Options.FlateEncodeMode) : null; if (useFlateDecode || tryFlateDecode && imageDataCompressed.Length < imageBits.Length) { Stream = new PdfStream(imageDataCompressed, this); Elements[PdfStream.Keys.Length] = new PdfInteger(imageDataCompressed.Length); PdfArray arrayFilters = new PdfArray(_document); arrayFilters.Elements.Add(new PdfName("/FlateDecode")); arrayFilters.Elements.Add(new PdfName("/DCTDecode")); Elements[PdfStream.Keys.Filter] = arrayFilters; } else { Stream = new PdfStream(imageBits, this); Elements[PdfStream.Keys.Length] = new PdfInteger(imageBits.Length); Elements[PdfStream.Keys.Filter] = new PdfName("/DCTDecode"); } if (_image.Interpolate) { Elements[Keys.Interpolate] = PdfBoolean.True; } Elements[Keys.Width] = new PdfInteger(_image.PixelWidth); Elements[Keys.Height] = new PdfInteger(_image.PixelHeight); Elements[Keys.BitsPerComponent] = new PdfInteger(8); Elements[Keys.ColorSpace] = new PdfName("/DeviceRGB"); }
public override Bitmap GetImage() { // get the stream bytes byte[] imgData = image.Stream.Value; // sometimes an image can be dual encoded, if so decode the first layer var filters = image.Elements.GetArray("/Filter"); string filter; if (filters != null && filters.Elements.GetName(0) == "/FlateDecode") { // FlateDecode imgData = new FlateDecode().Decode(image.Stream.Value); filter = filters.Elements.GetName(1); } else if (filters != null && filters.Elements.Count == 1) { filter = filters.Elements.GetName(0); } else { filter = image.Elements.GetName("/Filter"); } switch (filter) { case "/FlateDecode": // ? imgData = new FlateDecode().Decode(image.Stream.Value); break; case "/DCTDecode": // JPEG format // nativly supported by PDF so nothing to do here break; case "/CCITTFaxDecode": // TIFF format MemoryStream m = new MemoryStream(); Tiff tiff = Tiff.ClientOpen("custom", "w", m, new TiffStream()); tiff.SetField(TiffTag.IMAGEWIDTH, image.Elements.GetInteger("/Width")); tiff.SetField(TiffTag.IMAGELENGTH, image.Elements.GetInteger("/Height")); tiff.SetField(TiffTag.COMPRESSION, Compression.CCITTFAX4); tiff.SetField(TiffTag.BITSPERSAMPLE, image.Elements.GetInteger("/BitsPerComponent")); tiff.SetField(TiffTag.SAMPLESPERPIXEL, 1); tiff.WriteRawStrip(0, imgData, imgData.Length); tiff.Close(); imgData = m.ToArray(); break; case "/JBIG2Decode": var d = new JBig2Decoder.JBIG2StreamDecoder(); var decodeParams = image.Elements.GetDictionary("/DecodeParms"); if (decodeParams != null) { var globalRef = decodeParams.Elements.GetObject("/JBIG2Globals"); if (globalRef != null) { var globals = document.Internals.GetObject(globalRef.Reference.ObjectID) as PdfDictionary; d.setGlobalData(globals.Stream.Value); } } imgData = d.decodeJBIG2(imgData); break; default: throw new Exception("Dont know how to decode PDF image type of " + filter); } return(ImageHelper.ConvertTiffToBmps(new MemoryStream(imgData)).First()); }
/* 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; } } }
/// <summary> /// Reads images that are returned from GDI+ without color palette. /// </summary> /// <param name="components">4 (32bpp RGB), 3 (24bpp RGB, 32bpp ARGB)</param> /// <param name="bits">8</param> /// <param name="hasAlpha">true (ARGB), false (RGB)</param> private void ReadTrueColorMemoryBitmap(int components, int bits, bool hasAlpha) { int pdfVersion = Owner.Version; MemoryStream memory = new MemoryStream(); memory = _image.AsBitmap(); // 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; // TODO: we could define structures for // BITMAPFILEHEADER // { BITMAPINFO } // BITMAPINFOHEADER // to avoid ReadWord and ReadDWord ... (but w/o pointers this doesn't help much) bool bigHeader = false; if (ReadWord(imageBits, 0) != 0x4d42 || // "BM" ReadDWord(imageBits, 2) != streamLength || ReadDWord(imageBits, 18) != width || ReadDWord(imageBits, 22) != height) { throw new NotImplementedException("ReadTrueColorMemoryBitmap: unsupported format"); } int infoHeaderSize = ReadDWord(imageBits, 14); // sizeof BITMAPINFOHEADER if (infoHeaderSize != 40 && infoHeaderSize != 108) { throw new NotImplementedException("ReadTrueColorMemoryBitmap: unsupported format #2"); } bigHeader = infoHeaderSize == 108; if (ReadWord(imageBits, 26) != 1 || (!hasAlpha && ReadWord(imageBits, bigHeader?30:28) != components * bits || hasAlpha && ReadWord(imageBits, bigHeader?30:28) != (components + 1) * bits) || bigHeader ? ReadWord(imageBits, 32) != 0 : ReadDWord(imageBits, 30) != 0) { throw new NotImplementedException("ReadTrueColorMemoryBitmap: unsupported format #3"); } int nFileOffset = ReadDWord(imageBits, 10); int logicalComponents = components; if (components == 4) { logicalComponents = 3; } byte[] imageData = new byte[components * width * height]; bool hasMask = false; bool hasAlphaMask = false; byte[] alphaMask = hasAlpha ? new byte[width * height] : null; MonochromeMask mask = hasAlpha ? new MonochromeMask(width, height) : null; int nOffsetRead = 0; if (logicalComponents == 3) { for (int y = 0; y < height; ++y) { int nOffsetWrite = 3 * (height - 1 - y) * width; int nOffsetWriteAlpha = 0; if (hasAlpha) { mask.StartLine(y); nOffsetWriteAlpha = (height - 1 - y) * width; } for (int x = 0; x < width; ++x) { imageData[nOffsetWrite] = imageBits[nFileOffset + nOffsetRead + 2]; imageData[nOffsetWrite + 1] = imageBits[nFileOffset + nOffsetRead + 1]; imageData[nOffsetWrite + 2] = imageBits[nFileOffset + nOffsetRead]; if (hasAlpha) { mask.AddPel(imageBits[nFileOffset + nOffsetRead + 3]); alphaMask[nOffsetWriteAlpha] = imageBits[nFileOffset + nOffsetRead + 3]; if (!hasMask || !hasAlphaMask) { if (imageBits[nFileOffset + nOffsetRead + 3] != 255) { hasMask = true; if (imageBits[nFileOffset + nOffsetRead + 3] != 0) { hasAlphaMask = true; } } } ++nOffsetWriteAlpha; } nOffsetRead += hasAlpha ? 4 : components; nOffsetWrite += 3; } nOffsetRead = 4 * ((nOffsetRead + 3) / 4); // Align to 32 bit boundary } } else if (components == 1) { // Grayscale throw new NotImplementedException("Image format not supported (grayscales)."); } FlateDecode fd = new FlateDecode(); if (hasMask) { // monochrome mask is either sufficient or // provided for compatibility with older reader versions 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; } if (hasMask && hasAlphaMask && pdfVersion >= 14) { // The image provides an alpha mask (requires Arcrobat 5.0 or higher) byte[] alphaMaskCompressed = fd.Encode(alphaMask, _document.Options.FlateEncodeMode); PdfDictionary smask = new PdfDictionary(_document); smask.Elements.SetName(Keys.Type, "/XObject"); smask.Elements.SetName(Keys.Subtype, "/Image"); Owner._irefTable.Add(smask); smask.Stream = new PdfStream(alphaMaskCompressed, smask); smask.Elements[PdfStream.Keys.Length] = new PdfInteger(alphaMaskCompressed.Length); smask.Elements[PdfStream.Keys.Filter] = new PdfName("/FlateDecode"); smask.Elements[Keys.Width] = new PdfInteger(width); smask.Elements[Keys.Height] = new PdfInteger(height); smask.Elements[Keys.BitsPerComponent] = new PdfInteger(8); smask.Elements[Keys.ColorSpace] = new PdfName("/DeviceGray"); Elements[Keys.SMask] = smask.Reference; } byte[] imageDataCompressed = fd.Encode(imageData, _document.Options.FlateEncodeMode); 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(8); // TODO: CMYK Elements[Keys.ColorSpace] = new PdfName("/DeviceRGB"); if (_image.Interpolate) { Elements[Keys.Interpolate] = PdfBoolean.True; } } }