First commit

Send all results
This commit is contained in:
2020-09-04 12:49:15 +05:00
commit 330a2ccfda
2819 changed files with 226201 additions and 0 deletions

View File

@@ -0,0 +1,328 @@
#region PDFsharp - A .NET library for processing PDF
//
// Authors:
// Thomas Hövel
//
// Copyright (c) 2005-2017 empira Software GmbH, Cologne Area (Germany)
//
// http://www.pdfsharp.com
// http://sourceforge.net/projects/pdfsharp
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#endregion
using System;
using System.Diagnostics;
using System.IO;
using PdfSharp.Pdf;
namespace PdfSharp.Drawing
{
/// <summary>
/// This interface will be implemented by specialized classes, one for JPEG, one for BMP, one for PNG, one for GIF. Maybe more.
/// </summary>
internal interface IImageImporter
{
/// <summary>
/// Imports the image. Returns null if the image importer does not support the format.
/// </summary>
ImportedImage ImportImage(StreamReaderHelper stream, PdfDocument document);
/// <summary>
/// Prepares the image data needed for the PDF file.
/// </summary>
ImageData PrepareImage(ImagePrivateData data);
}
// $THHO Add IDispose?.
/// <summary>
/// Helper for dealing with Stream data.
/// </summary>
internal class StreamReaderHelper
{
internal StreamReaderHelper(Stream stream)
{
#if GDI || WPF
_stream = stream;
MemoryStream ms = stream as MemoryStream;
if (ms == null)
{
// THHO4STLA byte[] or MemoryStream?
_ownedMemoryStream = ms = new MemoryStream();
CopyStream(stream, ms);
// For .NET 4: stream.CopyTo(ms);
}
_data = ms.GetBuffer();
_length = (int)ms.Length;
#else
// For WinRT there is no GetBuffer() => alternative implementation for WinRT.
// TODO: Are there advantages of GetBuffer()? It should reduce LOH fragmentation.
_stream = stream;
_stream.Position = 0;
if (_stream.Length > int.MaxValue)
throw new ArgumentException("Stream is too large.", "stream");
_length = (int)_stream.Length;
_data = new byte[_length];
_stream.Read(_data, 0, _length);
#endif
}
internal byte GetByte(int offset)
{
if (_currentOffset + offset >= _length)
{
Debug.Assert(false);
return 0;
}
return _data[_currentOffset + offset];
}
internal ushort GetWord(int offset, bool bigEndian)
{
return (ushort)(bigEndian ?
GetByte(offset) * 256 + GetByte(offset + 1) :
GetByte(offset) + GetByte(offset + 1) * 256);
}
internal uint GetDWord(int offset, bool bigEndian)
{
return (uint)(bigEndian ?
GetWord(offset, true) * 65536 + GetWord(offset + 2, true) :
GetWord(offset, false) + GetWord(offset + 2, false) * 65536);
}
private static void CopyStream(Stream input, Stream output)
{
byte[] buffer = new byte[65536];
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, read);
}
}
/// <summary>
/// Resets this instance.
/// </summary>
public void Reset()
{
_currentOffset = 0;
}
/// <summary>
/// Gets the original stream.
/// </summary>
public Stream OriginalStream
{
get { return _stream; }
}
private readonly Stream _stream;
internal int CurrentOffset
{
get { return _currentOffset; }
set { _currentOffset = value; }
}
private int _currentOffset;
/// <summary>
/// Gets the data as byte[].
/// </summary>
public byte[] Data
{
get { return _data; }
}
private readonly byte[] _data;
/// <summary>
/// Gets the length of Data.
/// </summary>
public int Length
{
get { return _length; }
}
private readonly int _length;
#if GDI || WPF
/// <summary>
/// Gets the owned memory stream. Can be null if no MemoryStream was created.
/// </summary>
public MemoryStream OwnedMemoryStream
{
get { return _ownedMemoryStream; }
}
private readonly MemoryStream _ownedMemoryStream;
#endif
}
/// <summary>
/// The imported image.
/// </summary>
internal abstract class ImportedImage
{
/// <summary>
/// Initializes a new instance of the <see cref="ImportedImage"/> class.
/// </summary>
protected ImportedImage(IImageImporter importer, ImagePrivateData data, PdfDocument document)
{
Data = data;
_document = document;
data.Image = this;
_importer = importer;
}
/// <summary>
/// Gets information about the image.
/// </summary>
public ImageInformation Information
{
get { return _information; }
private set { _information = value; }
}
private ImageInformation _information = new ImageInformation();
/// <summary>
/// Gets a value indicating whether image data for the PDF file was already prepared.
/// </summary>
public bool HasImageData
{
get { return _imageData != null; }
}
/// <summary>
/// Gets the image data needed for the PDF file.
/// </summary>
public ImageData ImageData
{
get { if(!HasImageData) _imageData = PrepareImageData(); return _imageData; }
private set { _imageData = value; }
}
private ImageData _imageData;
internal virtual ImageData PrepareImageData()
{
throw new NotImplementedException();
}
private IImageImporter _importer;
internal ImagePrivateData Data;
internal readonly PdfDocument _document;
}
/// <summary>
/// Public information about the image, filled immediately.
/// Note: The stream will be read and decoded on the first call to PrepareImageData().
/// ImageInformation can be filled for corrupted images that will throw an expection on PrepareImageData().
/// </summary>
internal class ImageInformation
{
internal enum ImageFormats
{
/// <summary>
/// Standard JPEG format (RGB).
/// </summary>
JPEG,
/// <summary>
/// Grayscale JPEG format.
/// </summary>
JPEGGRAY,
/// <summary>
/// JPEG file with inverted CMYK, thus RGBW.
/// </summary>
JPEGRGBW,
/// <summary>
/// JPEG file with CMYK.
/// </summary>
JPEGCMYK,
Palette1,
Palette4,
Palette8,
RGB24,
ARGB32
}
internal ImageFormats ImageFormat;
internal uint Width;
internal uint Height;
/// <summary>
/// The horizontal DPI (dots per inch). Can be 0 if not supported by the image format.
/// Note: JFIF (JPEG) files may contain either DPI or DPM or just the aspect ratio. Windows BMP files will contain DPM. Other formats may support any combination, including none at all.
/// </summary>
internal decimal HorizontalDPI;
/// <summary>
/// The vertical DPI (dots per inch). Can be 0 if not supported by the image format.
/// </summary>
internal decimal VerticalDPI;
/// <summary>
/// The horizontal DPM (dots per meter). Can be 0 if not supported by the image format.
/// </summary>
internal decimal HorizontalDPM;
/// <summary>
/// The vertical DPM (dots per meter). Can be 0 if not supported by the image format.
/// </summary>
internal decimal VerticalDPM;
/// <summary>
/// The horizontal component of the aspect ratio. Can be 0 if not supported by the image format.
/// Note: Aspect ratio will be set if either DPI or DPM was set, but may also be available in the absence of both DPI and DPM.
/// </summary>
internal decimal HorizontalAspectRatio;
/// <summary>
/// The vertical component of the aspect ratio. Can be 0 if not supported by the image format.
/// </summary>
internal decimal VerticalAspectRatio;
/// <summary>
/// The colors used. Only valid for images with palettes, will be 0 otherwise.
/// </summary>
internal uint ColorsUsed;
}
/// <summary>
/// Contains internal data. This includes a reference to the Stream if data for PDF was not yet prepared.
/// </summary>
internal abstract class ImagePrivateData
{
internal ImagePrivateData()
{
}
/// <summary>
/// Gets the image.
/// </summary>
public ImportedImage Image
{
get { return _image; }
internal set { _image = value; }
}
private ImportedImage _image;
}
/// <summary>
/// Contains data needed for PDF. Will be prepared when needed.
/// </summary>
internal abstract class ImageData
{
}
}

View File

@@ -0,0 +1,92 @@
#region PDFsharp - A .NET library for processing PDF
//
// Authors:
// Thomas Hövel
//
// Copyright (c) 2005-2017 empira Software GmbH, Cologne Area (Germany)
//
// http://www.pdfsharp.com
// http://sourceforge.net/projects/pdfsharp
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#endregion
using System.Collections.Generic;
using System.IO;
using PdfSharp.Pdf;
namespace PdfSharp.Drawing.Internal
{
/// <summary>
/// The class that imports images of various formats.
/// </summary>
internal class ImageImporter
{
// TODO Make a singleton!
/// <summary>
/// Gets the image importer.
/// </summary>
public static ImageImporter GetImageImporter()
{
return new ImageImporter();
}
private ImageImporter()
{
_importers.Add(new ImageImporterJpeg());
_importers.Add(new ImageImporterBmp());
// TODO: Special importer for PDF? Or dealt with at a higher level?
}
/// <summary>
/// Imports the image.
/// </summary>
public ImportedImage ImportImage(Stream stream, PdfDocument document)
{
StreamReaderHelper helper = new StreamReaderHelper(stream);
// Try all registered importers to see if any of them can handle the image.
foreach (IImageImporter importer in _importers)
{
helper.Reset();
ImportedImage image = importer.ImportImage(helper, document);
if (image != null)
return image;
}
return null;
}
#if GDI || WPF || CORE
/// <summary>
/// Imports the image.
/// </summary>
public ImportedImage ImportImage(string filename, PdfDocument document)
{
ImportedImage ii;
using (Stream fs = File.OpenRead(filename))
{
ii = ImportImage(fs, document);
}
return ii;
}
#endif
private readonly List<IImageImporter> _importers = new List<IImageImporter>();
}
}

View File

@@ -0,0 +1,681 @@
#region PDFsharp - A .NET library for processing PDF
//
// Authors:
// Thomas Hövel
//
// Copyright (c) 2005-2017 empira Software GmbH, Cologne Area (Germany)
//
// http://www.pdfsharp.com
// http://sourceforge.net/projects/pdfsharp
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#endregion
using System;
using PdfSharp.Pdf;
using PdfSharp.Pdf.Advanced;
namespace PdfSharp.Drawing.Internal
{
// $THHO THHO4THHO add support for PdfDocument.Options.
internal class ImageImporterBmp : ImageImporterRoot, IImageImporter
{
public ImportedImage ImportImage(StreamReaderHelper stream, PdfDocument document)
{
try
{
stream.CurrentOffset = 0;
int offsetImageData;
if (TestBitmapFileHeader(stream, out offsetImageData))
{
// Magic: TestBitmapFileHeader updates stream.CurrentOffset on success.
ImagePrivateDataBitmap ipd = new ImagePrivateDataBitmap(stream.Data, stream.Length);
ImportedImage ii = new ImportedImageBitmap(this, ipd, document);
if (TestBitmapInfoHeader(stream, ii, offsetImageData))
{
//stream.CurrentOffset = offsetImageData;
return ii;
}
}
}
// ReSharper disable once EmptyGeneralCatchClause
catch (Exception)
{
}
return null;
}
private bool TestBitmapFileHeader(StreamReaderHelper stream, out int offset)
{
offset = 0;
// File must start with "BM".
if (stream.GetWord(0, true) == 0x424d)
{
int filesize = (int)stream.GetDWord(2, false);
// Integrity check: filesize set in BM header should match size of the stream.
// We test "<" instead of "!=" to allow extra bytes at the end of the stream.
if (filesize < stream.Length)
return false;
offset = (int)stream.GetDWord(10, false);
stream.CurrentOffset += 14;
return true;
}
return false;
}
private bool TestBitmapInfoHeader(StreamReaderHelper stream, ImportedImage ii, int offset)
{
int size = (int)stream.GetDWord(0, false);
if (size == 40 || size == 108 || size == 124) // sizeof BITMAPINFOHEADER == 40, sizeof BITMAPV4HEADER == 108, sizeof BITMAPV5HEADER == 124
{
uint width = stream.GetDWord(4, false);
int height = (int)stream.GetDWord(8, false);
int planes = stream.GetWord(12, false);
int bitcount = stream.GetWord(14, false);
int compression = (int)stream.GetDWord(16, false);
int sizeImage = (int)stream.GetDWord(20, false);
int xPelsPerMeter = (int)stream.GetDWord(24, false);
int yPelsPerMeter = (int)stream.GetDWord(28, false);
uint colorsUsed = stream.GetDWord(32, false);
uint colorsImportant = stream.GetDWord(36, false);
// TODO Integrity and plausibility checks.
if (sizeImage != 0 && sizeImage + offset > stream.Length)
return false;
ImagePrivateDataBitmap privateData = (ImagePrivateDataBitmap)ii.Data;
// Return true only for supported formats.
if (compression == 0 || compression == 3) // BI_RGB == 0, BI_BITFIELDS == 3
{
((ImagePrivateDataBitmap)ii.Data).Offset = offset;
((ImagePrivateDataBitmap)ii.Data).ColorPaletteOffset = stream.CurrentOffset + size;
ii.Information.Width = width;
ii.Information.Height = (uint)Math.Abs(height);
ii.Information.HorizontalDPM = xPelsPerMeter;
ii.Information.VerticalDPM = yPelsPerMeter;
privateData.FlippedImage = height < 0;
if (planes == 1 && bitcount == 24)
{
// RGB24
ii.Information.ImageFormat = ImageInformation.ImageFormats.RGB24;
// TODO: Verify Mask if size >= 108 && compression == 3.
return true;
}
if (planes == 1 && bitcount == 32)
{
// ARGB32
//ii.Information.ImageFormat = ImageInformation.ImageFormats.ARGB32;
ii.Information.ImageFormat = compression == 0 ?
ImageInformation.ImageFormats.RGB24 :
ImageInformation.ImageFormats.ARGB32;
// TODO: tell RGB from ARGB. Idea: assume RGB if alpha is always 0.
// TODO: Verify Mask if size >= 108 && compression == 3.
return true;
}
if (planes == 1 && bitcount == 8)
{
// Palette8
ii.Information.ImageFormat = ImageInformation.ImageFormats.Palette8;
ii.Information.ColorsUsed = colorsUsed;
return true;
}
if (planes == 1 && bitcount == 4)
{
// Palette8
ii.Information.ImageFormat = ImageInformation.ImageFormats.Palette4;
ii.Information.ColorsUsed = colorsUsed;
return true;
}
if (planes == 1 && bitcount == 1)
{
// Palette8
ii.Information.ImageFormat = ImageInformation.ImageFormats.Palette1;
ii.Information.ColorsUsed = colorsUsed;
return true;
}
// TODO Implement more formats!
}
}
return false;
}
public ImageData PrepareImage(ImagePrivateData data)
{
throw new NotImplementedException();
}
}
/// <summary>
/// Bitmap refers to the format used in PDF. Will be used for BMP, PNG, TIFF, GIF and others.
/// </summary>
internal class ImportedImageBitmap : ImportedImage
{
/// <summary>
/// Initializes a new instance of the <see cref="ImportedImageBitmap"/> class.
/// </summary>
public ImportedImageBitmap(IImageImporter importer, ImagePrivateDataBitmap data, PdfDocument document)
: base(importer, data, document)
{ }
internal override ImageData PrepareImageData()
{
ImagePrivateDataBitmap data = (ImagePrivateDataBitmap)Data;
ImageDataBitmap imageData = new ImageDataBitmap(_document);
//imageData.Data = data.Data;
//imageData.Length = data.Length;
data.CopyBitmap(imageData);
return imageData;
}
}
// THHO4THHO Maybe there will be derived classes for direct bitmaps vs. palettized bitmaps or so. Time will tell.
/// <summary>
/// Contains data needed for PDF. Will be prepared when needed.
/// Bitmap refers to the format used in PDF. Will be used for BMP, PNG, TIFF, GIF and others.
/// </summary>
internal class ImageDataBitmap : ImageData
{
private ImageDataBitmap()
{
}
internal ImageDataBitmap(PdfDocument document)
{
_document = document;
}
/// <summary>
/// Gets the data.
/// </summary>
public byte[] Data
{
get { return _data; }
internal set { _data = value; }
}
private byte[] _data;
/// <summary>
/// Gets the length.
/// </summary>
public int Length
{
get { return _length; }
internal set { _length = value; }
}
private int _length;
/// <summary>
/// Gets the data.
/// </summary>
public byte[] DataFax
{
get { return _dataFax; }
internal set { _dataFax = value; }
}
private byte[] _dataFax;
/// <summary>
/// Gets the length.
/// </summary>
public int LengthFax
{
get { return _lengthFax; }
internal set { _lengthFax = value; }
}
private int _lengthFax;
public byte[] AlphaMask
{
get { return _alphaMask; }
internal set { _alphaMask = value; }
}
private byte[] _alphaMask;
public int AlphaMaskLength
{
get { return _alphaMaskLength; }
internal set { _alphaMaskLength = value; }
}
private int _alphaMaskLength;
public byte[] BitmapMask
{
get { return _bitmapMask; }
internal set { _bitmapMask = value; }
}
private byte[] _bitmapMask;
public int BitmapMaskLength
{
get { return _bitmapMaskLength; }
internal set { _bitmapMaskLength = value; }
}
private int _bitmapMaskLength;
public byte[] PaletteData
{
get { return _paletteData; }
set { _paletteData = value; }
}
private byte[] _paletteData;
public int PaletteDataLength
{
get { return _paletteDataLength; }
set { _paletteDataLength = value; }
}
private int _paletteDataLength;
public bool SegmentedColorMask;
public int IsBitonal;
public int K;
public bool IsGray;
internal readonly PdfDocument _document;
}
/// <summary>
/// Image data needed for PDF bitmap images.
/// </summary>
internal class ImagePrivateDataBitmap : ImagePrivateData
{
/// <summary>
/// Initializes a new instance of the <see cref="ImagePrivateDataBitmap"/> class.
/// </summary>
public ImagePrivateDataBitmap(byte[] data, int length)
{
_data = data;
_length = length;
}
/// <summary>
/// Gets the data.
/// </summary>
public byte[] Data
{
get { return _data; }
//internal set { _data = value; }
}
private readonly byte[] _data;
/// <summary>
/// Gets the length.
/// </summary>
public int Length
{
get { return _length; }
//internal set { _length = value; }
}
private readonly int _length;
/// <summary>
/// True if first line is the top line, false if first line is the bottom line of the image. When needed, lines will be reversed while converting data into PDF format.
/// </summary>
internal bool FlippedImage;
/// <summary>
/// The offset of the image data in Data.
/// </summary>
internal int Offset;
/// <summary>
/// The offset of the color palette in Data.
/// </summary>
internal int ColorPaletteOffset;
internal void CopyBitmap(ImageDataBitmap dest)
{
switch (Image.Information.ImageFormat)
{
case ImageInformation.ImageFormats.ARGB32:
CopyTrueColorMemoryBitmap(3, 8, true, dest);
break;
case ImageInformation.ImageFormats.RGB24:
CopyTrueColorMemoryBitmap(4, 8, false, dest);
break;
case ImageInformation.ImageFormats.Palette8:
CopyIndexedMemoryBitmap(8, dest);
break;
case ImageInformation.ImageFormats.Palette4:
CopyIndexedMemoryBitmap(4, dest);
break;
case ImageInformation.ImageFormats.Palette1:
CopyIndexedMemoryBitmap(1, dest);
break;
default:
throw new NotImplementedException();
}
}
/// <summary>
/// Copies images 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>
/// <param name="dest">Destination </param>
private void CopyTrueColorMemoryBitmap(int components, int bits, bool hasAlpha, ImageDataBitmap dest)
{
int width = (int)Image.Information.Width;
int height = (int)Image.Information.Height;
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 nFileOffset = Offset;
int nOffsetRead = 0;
if (logicalComponents == 3)
{
for (int y = 0; y < height; ++y)
{
// TODO Handle Flipped.
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] = Data[nFileOffset + nOffsetRead + 2];
imageData[nOffsetWrite + 1] = Data[nFileOffset + nOffsetRead + 1];
imageData[nOffsetWrite + 2] = Data[nFileOffset + nOffsetRead];
if (hasAlpha)
{
mask.AddPel(Data[nFileOffset + nOffsetRead + 3]);
alphaMask[nOffsetWriteAlpha] = Data[nFileOffset + nOffsetRead + 3];
if (!hasMask || !hasAlphaMask)
{
if (Data[nFileOffset + nOffsetRead + 3] != 255)
{
hasMask = true;
if (Data[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).");
}
dest.Data = imageData;
dest.Length = imageData.Length;
if (alphaMask != null)
{
dest.AlphaMask = alphaMask;
dest.AlphaMaskLength = alphaMask.Length;
}
if (mask != null)
{
dest.BitmapMask = mask.MaskData;
dest.BitmapMaskLength = mask.MaskData.Length;
}
}
private void CopyIndexedMemoryBitmap(int bits/*, ref bool hasAlpha*/, ImageDataBitmap dest)
{
int firstMaskColor = -1, lastMaskColor = -1;
bool segmentedColorMask = false;
int bytesColorPaletteOffset = ((ImagePrivateDataBitmap)Image.Data).ColorPaletteOffset; // GDI+ always returns Windows bitmaps: sizeof BITMAPFILEHEADER + sizeof BITMAPINFOHEADER
int bytesFileOffset = ((ImagePrivateDataBitmap)Image.Data).Offset;
uint paletteColors = Image.Information.ColorsUsed;
int width = (int)Image.Information.Width;
int height = (int)Image.Information.Height;
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] = Data[bytesColorPaletteOffset + 4 * color + 2];
paletteData[3 * color + 1] = Data[bytesColorPaletteOffset + 4 * color + 1];
paletteData[3 * color + 2] = Data[bytesColorPaletteOffset + 4 * color + 0];
if (isGray)
isGray = paletteData[3 * color] == paletteData[3 * color + 1] &&
paletteData[3 * color] == paletteData[3 * color + 2];
if (Data[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 && dest._document.Options.EnableCcittCompressionForBilevelImages)
{
// 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 = PdfImage.DoFaxEncodingGroup4(ref tempG4, Data, (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 * Data[bytesFileOffset + bytesOffsetRead]];
}
else
{
// Store the palette index.
imageData[bytesOffsetWrite] = Data[bytesFileOffset + bytesOffsetRead];
}
if (firstMaskColor != -1)
{
int n = Data[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");
}
}
dest.Data = imageData;
dest.Length = imageData.Length;
if (imageDataFax != null)
{
dest.DataFax = imageDataFax;
dest.LengthFax = imageDataFax.Length;
}
dest.IsGray = isGray;
dest.K = k;
dest.IsBitonal = isBitonal;
dest.PaletteData = paletteData;
dest.PaletteDataLength = paletteData.Length;
dest.SegmentedColorMask = segmentedColorMask;
//if (alphaMask != null)
//{
// dest.AlphaMask = alphaMask;
// dest.AlphaMaskLength = alphaMask.Length;
//}
if (mask != null && firstMaskColor != -1)
{
dest.BitmapMask = mask.MaskData;
dest.BitmapMaskLength = mask.MaskData.Length;
}
}
}
}

View File

@@ -0,0 +1,363 @@
#region PDFsharp - A .NET library for processing PDF
//
// Authors:
// Thomas Hövel
//
// Copyright (c) 2005-2017 empira Software GmbH, Cologne Area (Germany)
//
// http://www.pdfsharp.com
// http://sourceforge.net/projects/pdfsharp
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#endregion
using System;
using PdfSharp.Pdf;
namespace PdfSharp.Drawing.Internal
{
// ReSharper disable once InconsistentNaming
internal class ImageImporterJpeg : ImageImporterRoot, IImageImporter
{
// TODO Find information about JPEG2000.
// Notes: JFIF is big-endian.
public ImportedImage ImportImage(StreamReaderHelper stream, PdfDocument document)
{
try
{
stream.CurrentOffset = 0;
// Test 2 magic bytes.
if (TestFileHeader(stream))
{
// Skip over 2 magic bytes.
stream.CurrentOffset += 2;
ImagePrivateDataDct ipd = new ImagePrivateDataDct(stream.Data, stream.Length);
ImportedImage ii = new ImportedImageJpeg(this, ipd, document);
if (TestJfifHeader(stream, ii))
{
bool colorHeader = false, infoHeader = false;
while (MoveToNextHeader(stream))
{
if (TestColorFormatHeader(stream, ii))
{
colorHeader = true;
}
else if (TestInfoHeader(stream, ii))
{
infoHeader = true;
}
}
if (colorHeader && infoHeader)
return ii;
}
}
}
// ReSharper disable once EmptyGeneralCatchClause
catch (Exception)
{
}
return null;
}
private bool TestFileHeader(StreamReaderHelper stream)
{
// File must start with 0xffd8.
return stream.GetWord(0, true) == 0xffd8;
}
private bool TestJfifHeader(StreamReaderHelper stream, ImportedImage ii)
{
// The App0 header should be the first header in every JFIF file.
if (stream.GetWord(0, true) == 0xffe0)
{
// Now check for text "JFIF".
if (stream.GetDWord(4, true) == 0x4a464946)
{
int blockLength = stream.GetWord(2, true);
if (blockLength >= 16)
{
int version = stream.GetWord(9, true);
int units = stream.GetByte(11);
int densityX = stream.GetWord(12, true);
int densityY = stream.GetWord(14, true);
switch (units)
{
case 0: // Aspect ratio only.
ii.Information.HorizontalAspectRatio = densityX;
ii.Information.VerticalAspectRatio = densityY;
break;
case 1: // DPI.
ii.Information.HorizontalDPI = densityX;
ii.Information.VerticalDPI = densityY;
break;
case 2: // DPCM.
ii.Information.HorizontalDPM = densityX * 100;
ii.Information.VerticalDPM = densityY * 100;
break;
}
// More information here? More tests?
return true;
}
}
}
return false;
}
private bool TestColorFormatHeader(StreamReaderHelper stream, ImportedImage ii)
{
// The SOS header (start of scan).
if (stream.GetWord(0, true) == 0xffda)
{
int components = stream.GetByte(4);
if (components < 1 || components > 4 || components == 2)
return false;
// 1 for grayscale, 3 for RGB, 4 for CMYK.
int blockLength = stream.GetWord(2, true);
// Integrity check: correct size?
if (blockLength != 6 + 2 * components)
return false;
// Eventually do more tests here.
// Magic: we assume that all JPEG files with 4 components are RGBW (inverted CMYK) and not CMYK.
// We add a test to tell CMYK from RGBW when we encounter a test file in CMYK format.
ii.Information.ImageFormat = components == 3 ? ImageInformation.ImageFormats.JPEG :
(components == 1 ? ImageInformation.ImageFormats.JPEGGRAY : ImageInformation.ImageFormats.JPEGRGBW);
return true;
}
return false;
}
private bool TestInfoHeader(StreamReaderHelper stream, ImportedImage ii)
{
// The SOF header (start of frame).
int header = stream.GetWord(0, true);
if (header >= 0xffc0 && header <= 0xffc3 ||
header >= 0xffc9 && header <= 0xffcb)
{
// Lines in image.
int sizeY = stream.GetWord(5, true);
// Samples per line.
int sizeX = stream.GetWord(7, true);
// $THHO TODO: Check if we always get useful information here.
ii.Information.Width = (uint)sizeX;
ii.Information.Height = (uint)sizeY;
return true;
}
return false;
}
private bool MoveToNextHeader(StreamReaderHelper stream)
{
int blockLength = stream.GetWord(2, true);
int headerMagic = stream.GetByte(0);
int headerType = stream.GetByte(1);
if (headerMagic == 0xff)
{
// EOI: last header.
if (headerType == 0xd9)
return false;
// Check for standalone markers.
if (headerType == 0x01 || headerType >= 0xd0 && headerType <= 0xd7)
{
stream.CurrentOffset += 2;
return true;
}
// Now assume header with block size.
stream.CurrentOffset += 2 + blockLength;
return true;
}
return false;
}
public ImageData PrepareImage(ImagePrivateData data)
{
throw new NotImplementedException();
}
//int GetJpgSizeTestCode(byte[] pData, uint FileSizeLow, out int pWidth, out int pHeight)
//{
// pWidth = -1;
// pHeight = -1;
// int i = 0;
// if ((pData[i] == 0xFF) && (pData[i + 1] == 0xD8) && (pData[i + 2] == 0xFF) && (pData[i + 3] == 0xE0))
// {
// i += 4;
// // Check for valid JPEG header (null terminated JFIF)
// if ((pData[i + 2] == 'J') && (pData[i + 3] == 'F') && (pData[i + 4] == 'I') && (pData[i + 5] == 'F')
// && (pData[i + 6] == 0x00))
// {
// //Retrieve the block length of the first block since the first block will not contain the size of file
// int block_length = pData[i] * 256 + pData[i + 1];
// while (i < FileSizeLow)
// {
// //Increase the file index to get to the next block
// i += block_length;
// if (i >= FileSizeLow)
// {
// //Check to protect against segmentation faults
// return -1;
// }
// if (pData[i] != 0xFF)
// {
// return -2;
// }
// if (pData[i + 1] == 0xC0)
// {
// //0xFFC0 is the "Start of frame" marker which contains the file size
// //The structure of the 0xFFC0 block is quite simple [0xFFC0][ushort length][uchar precision][ushort x][ushort y]
// pHeight = pData[i + 5] * 256 + pData[i + 6];
// pWidth = pData[i + 7] * 256 + pData[i + 8];
// return 0;
// }
// else
// {
// i += 2; //Skip the block marker
// //Go to the next block
// block_length = pData[i] * 256 + pData[i + 1];
// }
// }
// //If this point is reached then no size was found
// return -3;
// }
// else
// {
// return -4;
// } //Not a valid JFIF string
// }
// else
// {
// return -5;
// } //Not a valid SOI header
// //return -6;
//} // GetJpgSize
}
/// <summary>
/// Imported JPEG image.
/// </summary>
internal class ImportedImageJpeg : ImportedImage
{
/// <summary>
/// Initializes a new instance of the <see cref="ImportedImageJpeg"/> class.
/// </summary>
public ImportedImageJpeg(IImageImporter importer, ImagePrivateDataDct data, PdfDocument document)
: base(importer, data, document)
{ }
internal override ImageData PrepareImageData()
{
ImagePrivateDataDct data = (ImagePrivateDataDct)Data;
ImageDataDct imageData = new ImageDataDct();
imageData.Data = data.Data;
imageData.Length = data.Length;
return imageData;
}
}
/// <summary>
/// Contains data needed for PDF. Will be prepared when needed.
/// </summary>
internal class ImageDataDct : ImageData
{
/// <summary>
/// Gets the data.
/// </summary>
public byte[] Data
{
get { return _data; }
internal set { _data = value; }
}
private byte[] _data;
/// <summary>
/// Gets the length.
/// </summary>
public int Length
{
get { return _length; }
internal set { _length = value; }
}
private int _length;
}
/*internal*/
/// <summary>
/// Private data for JPEG images.
/// </summary>
internal class ImagePrivateDataDct : ImagePrivateData
{
/// <summary>
/// Initializes a new instance of the <see cref="ImagePrivateDataDct"/> class.
/// </summary>
public ImagePrivateDataDct(byte[] data, int length)
{
_data = data;
_length = length;
}
/// <summary>
/// Gets the data.
/// </summary>
public byte[] Data
{
get { return _data; }
//internal set { _data = value; }
}
private readonly byte[] _data;
/// <summary>
/// Gets the length.
/// </summary>
public int Length
{
get { return _length; }
//internal set { _length = value; }
}
private readonly int _length;
}
}

View File

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