/// <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])); } }
/// <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); }
/// <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)); // } //} }
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)); // } //} }
/// <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); }
/* 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; } }
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; } }
/* 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; } }
/// <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; } } }