ASCU_ALL/PdfSharp/Drawing/XImage.cs
2020-09-04 12:49:15 +05:00

1561 lines
52 KiB
C#

#region PDFsharp - A .NET library for processing PDF
//
// Authors:
// Stefan Lange
//
// 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;
#if CORE
using System.Drawing;
#endif
#if GDI
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
#endif
#if WPF
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
#endif
#if NETFX_CORE || UWP
using Windows.UI.Xaml.Media.Imaging;
#endif
using PdfSharp.Drawing.Internal;
using PdfSharp.Internal;
using PdfSharp.Pdf.IO;
using PdfSharp.Pdf.Advanced;
// WPFHACK
#pragma warning disable 0169
#pragma warning disable 0649
namespace PdfSharp.Drawing
{
[Flags]
internal enum XImageState
{
UsedInDrawingContext = 0x00000001,
StateMask = 0x0000FFFF,
}
/// <summary>
/// Defines an object used to draw image files (bmp, png, jpeg, gif) and PDF forms.
/// An abstract base class that provides functionality for the Bitmap and Metafile descended classes.
/// </summary>
public class XImage : IDisposable
{
// The hierarchy is adapted to WPF/Silverlight/WinRT
//
// XImage <-- ImageSource
// XForm
// PdfForm
// XBitmapSource <-- BitmapSource
// XBitmapImage <-- BitmapImage
// ???
//public bool Disposed
//{
// get { return _disposed; }
// set { _disposed = value; }
//}
/// <summary>
/// Initializes a new instance of the <see cref="XImage"/> class.
/// </summary>
protected XImage()
{ }
#if GDI || CORE || WPF
/// <summary>
/// Initializes a new instance of the <see cref="XImage"/> class from an image read by ImageImporter.
/// </summary>
/// <param name="image">The image.</param>
/// <exception cref="System.ArgumentNullException">image</exception>
XImage(ImportedImage image)
{
if (image == null)
throw new ArgumentNullException("image");
_importedImage = image;
Initialize();
}
#endif
#if GDI
/// <summary>
/// Initializes a new instance of the <see cref="XImage"/> class from a GDI+ image.
/// </summary>
XImage(Image image)
{
_gdiImage = image;
#if WPF // Is defined in hybrid build.
_wpfImage = ImageHelper.CreateBitmapSource(image);
#endif
Initialize();
}
#endif
#if WPF
/// <summary>
/// Initializes a new instance of the <see cref="XImage"/> class from a WPF image.
/// </summary>
XImage(BitmapSource image)
{
_wpfImage = image;
Initialize();
}
#endif
#if WPF
XImage(Uri uri)
{
//var uriSource = new Uri(@"/WpfApplication1;component/Untitled.png", UriKind.Relative); foo.Source = new BitmapImage(uriSource);
_wpfImage = BitmapFromUri(uri);
//throw new NotImplementedException("XImage from Uri.");
// WPF
//Image finalImage = new Image();
//finalImage.Width = 80;
//...BitmapImage logo = new BitmapImage()
//logo.BeginInit();logo.UriSource = new Uri("pack://application:,,,/ApplicationName;component/Resources/logo.png");
//logo.EndInit();
//...finalImage.Source = logo;
}
/// <summary>
/// Creates an BitmapImage from URI. Sets BitmapCacheOption.OnLoad for WPF to prevent image file from being locked.
/// </summary>
public static BitmapImage BitmapFromUri(Uri uri)
{
#if !SILVERLIGHT
// Using new BitmapImage(uri) will leave a lock on the file, leading to problems with temporary image files in server environments.
// We use BitmapCacheOption.OnLoad to prevent this lock.
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.UriSource = uri;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.EndInit();
return bitmap;
#else
return new BitmapImage(uri);
#endif
}
#endif
#if NETFX_CORE
/// <summary>
/// Initializes a new instance of the <see cref="XImage"/> class from a WinRT image.
/// </summary>
XImage(BitmapSource image)
{
_wrtImage = image;
Initialize();
}
#endif
// Useful stuff here: http://stackoverflow.com/questions/350027/setting-wpf-image-source-in-code
XImage(string path)
{
#if !NETFX_CORE && !UWP
path = Path.GetFullPath(path);
if (!File.Exists(path))
throw new FileNotFoundException(PSSR.FileNotFound(path));
//throw new FileNotFoundException(PSSR.FileNotFound(path), path);
#endif
_path = path;
//FileStream file = new FileStream(filename, FileMode.Open);
//BitsLength = (int)file.Length;
//Bits = new byte[BitsLength];
//file.Read(Bits, 0, BitsLength);
//file.Close();
#if CORE_WITH_GDI || GDI
try
{
Lock.EnterGdiPlus();
_gdiImage = Image.FromFile(path);
}
finally { Lock.ExitGdiPlus(); }
#endif
#if WPF && !SILVERLIGHT
//BitmapSource.Create()
// BUG: BitmapImage locks the file
//_wpfImage = new BitmapImage(new Uri(path)); // AGHACK
// Suggested change from forum to prevent locking.
_wpfImage = BitmapFromUri(new Uri(path));
#endif
#if WPF && SILVERLIGHT
//BitmapSource.Create()
// BUG: BitmapImage locks the file
//_wpfImage = new BitmapImage(new Uri(path)); // AGHACK
//Debug-Break.Break();
#endif
#if true_
float vres = image.VerticalResolution;
float hres = image.HorizontalResolution;
SizeF size = image.PhysicalDimension;
int flags = image.Flags;
Size sz = image.Size;
GraphicsUnit units = GraphicsUnit.Millimeter;
RectangleF rect = image.GetBounds(ref units);
int width = image.Width;
#endif
Initialize();
}
XImage(Stream stream)
{
// Create a dummy unique path.
_path = "*" + Guid.NewGuid().ToString("B");
// TODO: Create a fingerprint of the bytes in the stream to identify identical images.
// TODO: Merge code for CORE_WITH_GDI and GDI.
#if CORE_WITH_GDI
// Create a GDI+ image.
try
{
Lock.EnterGdiPlus();
_gdiImage = Image.FromStream(stream);
}
finally { Lock.ExitGdiPlus(); }
#endif
#if GDI
// Create a GDI+ image.
try
{
Lock.EnterGdiPlus();
_gdiImage = Image.FromStream(stream);
}
finally { Lock.ExitGdiPlus(); }
#endif
#if WPF && !SILVERLIGHT
// Create a WPF BitmapImage.
BitmapImage bmi = new BitmapImage();
bmi.BeginInit();
bmi.StreamSource = stream;
bmi.EndInit();
_wpfImage = bmi;
#endif
#if SILVERLIGHT
int length = (int)stream.Length;
stream.Seek(0, SeekOrigin.Begin);
//_bytes = new byte[length];
//stream.Read(_bytes, 0, length);
//stream.Seek(0, SeekOrigin.Begin);
// Create a Silverlight BitmapImage.
_wpfImage = new BitmapImage();
_wpfImage.SetSource(stream);
#endif
#if true_
float vres = image.VerticalResolution;
float hres = image.HorizontalResolution;
SizeF size = image.PhysicalDimension;
int flags = image.Flags;
Size sz = image.Size;
GraphicsUnit units = GraphicsUnit.Millimeter;
RectangleF rect = image.GetBounds(ref units);
int width = image.Width;
#endif
// Must assign _stream before Initialize().
_stream = stream;
Initialize();
}
#if GDI //|| CORE
#if UseGdiObjects
/// <summary>
/// Implicit conversion from Image to XImage.
/// </summary>
public static implicit operator XImage(Image image)
{
return new XImage(image);
}
#endif
/// <summary>
/// Conversion from Image to XImage.
/// </summary>
public static XImage FromGdiPlusImage(Image image)
{
return new XImage(image);
}
#endif
#if WPF
/// <summary>
/// Conversion from BitmapSource to XImage.
/// </summary>
public static XImage FromBitmapSource(BitmapSource image)
{
return new XImage(image);
}
#endif
#if NETFX_CORE
/// <summary>
/// Conversion from BitmapSource to XImage.
/// </summary>
public static XImage FromBitmapSource(BitmapSource image)
{
return new XImage(image);
}
#endif
/// <summary>
/// Creates an image from the specified file.
/// </summary>
/// <param name="path">The path to a BMP, PNG, GIF, JPEG, TIFF, or PDF file.</param>
public static XImage FromFile(string path)
{
if (PdfReader.TestPdfFile(path) > 0)
return new XPdfForm(path);
return new XImage(path);
}
/// <summary>
/// Creates an image from the specified stream.<br/>
/// Silverlight supports PNG and JPEG only.
/// </summary>
/// <param name="stream">The stream containing a BMP, PNG, GIF, JPEG, TIFF, or PDF file.</param>
public static XImage FromStream(Stream stream)
{
if (stream == null)
throw new ArgumentNullException("stream");
if (PdfReader.TestPdfFile(stream) > 0)
return new XPdfForm(stream);
return new XImage(stream);
}
#if DEBUG
#if CORE || GDI || WPF
/// <summary>
/// Creates an image from the specified file.
/// </summary>
/// <param name="path">The path to a BMP, PNG, GIF, JPEG, TIFF, or PDF file.</param>
/// <param name="platformIndependent">Uses an platform-independent implementation if set to true.
/// The platform-dependent implementation, if available, will support more image formats.</param>
/// <param name="document">The document used to obtain the options.</param>
internal static XImage FromFile(string path, bool platformIndependent, PdfDocument document)
{
if (!platformIndependent)
return FromFile(path);
// TODO: Check PDF file.
ImageImporter ii = ImageImporter.GetImageImporter();
ImportedImage i = ii.ImportImage(path, document);
if (i == null)
throw new InvalidOperationException("Unsupported image format.");
XImage image = new XImage(i);
image._path = path;
return image;
}
/// <summary>
/// Creates an image from the specified stream.<br/>
/// Silverlight supports PNG and JPEF only.
/// </summary>
/// <param name="stream">The stream containing a BMP, PNG, GIF, JPEG, TIFF, or PDF file.</param>
/// <param name="platformIndependent">Uses an platform-independent implementation if set to true.
/// The platform-dependent implementation, if available, will support more image formats.</param>
/// <param name="document">The document used to obtain the options.</param>
internal static XImage FromStream(Stream stream, bool platformIndependent, PdfDocument document)
{
if (!platformIndependent)
return FromStream(stream);
// TODO: Check PDF file.
ImageImporter ii = ImageImporter.GetImageImporter();
ImportedImage i = ii.ImportImage(stream, document);
if (i == null)
throw new InvalidOperationException("Unsupported image format.");
XImage image = new XImage(i);
image._stream = stream;
return image;
}
#endif
#endif
#if DEBUG
#if CORE || GDI || WPF
/// <summary>
/// Creates an image.
/// </summary>
/// <param name="image">The imported image.</param>
[Obsolete("THHO4THHO Internal test code.")]
internal static XImage FromImportedImage(ImportedImage image)
{
if (image == null)
throw new ArgumentNullException("image");
return new XImage(image);
}
#endif
#endif
/// <summary>
/// Tests if a file exist. Supports PDF files with page number suffix.
/// </summary>
/// <param name="path">The path to a BMP, PNG, GIF, JPEG, TIFF, or PDF file.</param>
public static bool ExistsFile(string path)
{
// Support for "base64:" pseudo protocol is a MigraDoc feature, currently completely implemented in MigraDoc files. TODO: Does support for "base64:" make sense for PDFsharp? Probably not as PDFsharp can handle images from streams.
//if (path.StartsWith("base64:")) // The Image is stored in the string here, so the file exists.
// return true;
if (PdfReader.TestPdfFile(path) > 0)
return true;
#if !NETFX_CORE && !UWP
return File.Exists(path);
#else
return false;
#endif
}
internal XImageState XImageState
{
get { return _xImageState; }
set { _xImageState = value; }
}
XImageState _xImageState;
internal void Initialize()
{
#if CORE || GDI || WPF
if (_importedImage != null)
{
ImportedImageJpeg iiJpeg = _importedImage as ImportedImageJpeg;
// In PDF there are two formats: JPEG and PDF bitmap.
if (iiJpeg != null)
_format = XImageFormat.Jpeg;
else
_format = XImageFormat.Png;
return;
}
#endif
#if CORE_WITH_GDI
if (_gdiImage != null)
{
// ImageFormat has no overridden Equals function.
string guid;
try
{
Lock.EnterGdiPlus();
guid = _gdiImage.RawFormat.Guid.ToString("B").ToUpper();
}
finally
{
Lock.ExitGdiPlus();
}
switch (guid)
{
case "{B96B3CAA-0728-11D3-9D7B-0000F81EF32E}": // memoryBMP
case "{B96B3CAB-0728-11D3-9D7B-0000F81EF32E}": // bmp
case "{B96B3CAF-0728-11D3-9D7B-0000F81EF32E}": // png
_format = XImageFormat.Png;
break;
case "{B96B3CAE-0728-11D3-9D7B-0000F81EF32E}": // jpeg
_format = XImageFormat.Jpeg;
break;
case "{B96B3CB0-0728-11D3-9D7B-0000F81EF32E}": // gif
_format = XImageFormat.Gif;
break;
case "{B96B3CB1-0728-11D3-9D7B-0000F81EF32E}": // tiff
_format = XImageFormat.Tiff;
break;
case "{B96B3CB5-0728-11D3-9D7B-0000F81EF32E}": // icon
_format = XImageFormat.Icon;
break;
case "{B96B3CAC-0728-11D3-9D7B-0000F81EF32E}": // emf
case "{B96B3CAD-0728-11D3-9D7B-0000F81EF32E}": // wmf
case "{B96B3CB2-0728-11D3-9D7B-0000F81EF32E}": // exif
case "{B96B3CB3-0728-11D3-9D7B-0000F81EF32E}": // photoCD
case "{B96B3CB4-0728-11D3-9D7B-0000F81EF32E}": // flashPIX
default:
throw new InvalidOperationException("Unsupported image format.");
}
return;
}
#endif
#if GDI
if (_gdiImage != null)
{
// ImageFormat has no overridden Equals function.
string guid;
try
{
Lock.EnterGdiPlus();
guid = _gdiImage.RawFormat.Guid.ToString("B").ToUpper();
}
finally { Lock.ExitGdiPlus(); }
switch (guid)
{
case "{B96B3CAA-0728-11D3-9D7B-0000F81EF32E}": // memoryBMP
case "{B96B3CAB-0728-11D3-9D7B-0000F81EF32E}": // bmp
case "{B96B3CAF-0728-11D3-9D7B-0000F81EF32E}": // png
_format = XImageFormat.Png;
break;
case "{B96B3CAE-0728-11D3-9D7B-0000F81EF32E}": // jpeg
_format = XImageFormat.Jpeg;
break;
case "{B96B3CB0-0728-11D3-9D7B-0000F81EF32E}": // gif
_format = XImageFormat.Gif;
break;
case "{B96B3CB1-0728-11D3-9D7B-0000F81EF32E}": // tiff
_format = XImageFormat.Tiff;
break;
case "{B96B3CB5-0728-11D3-9D7B-0000F81EF32E}": // icon
_format = XImageFormat.Icon;
break;
case "{B96B3CAC-0728-11D3-9D7B-0000F81EF32E}": // emf
case "{B96B3CAD-0728-11D3-9D7B-0000F81EF32E}": // wmf
case "{B96B3CB2-0728-11D3-9D7B-0000F81EF32E}": // exif
case "{B96B3CB3-0728-11D3-9D7B-0000F81EF32E}": // photoCD
case "{B96B3CB4-0728-11D3-9D7B-0000F81EF32E}": // flashPIX
default:
throw new InvalidOperationException("Unsupported image format.");
}
return;
}
#endif
#if WPF
#if !SILVERLIGHT
if (_wpfImage != null)
{
//string filename = GetImageFilename(_wpfImage);
// WPF treats all images as images.
// We give JPEG images a special treatment.
// Test if it's a JPEG.
bool isJpeg = IsJpeg; // TestJpeg(filename);
if (isJpeg)
{
_format = XImageFormat.Jpeg;
return;
}
string pixelFormat = _wpfImage.Format.ToString();
switch (pixelFormat)
{
case "Bgr32":
case "Bgra32":
case "Pbgra32":
_format = XImageFormat.Png;
break;
//case "{B96B3CAE-0728-11D3-9D7B-0000F81EF32E}": // jpeg
// format = XImageFormat.Jpeg;
// break;
//case "{B96B3CB0-0728-11D3-9D7B-0000F81EF32E}": // gif
case "BlackWhite":
case "Indexed1":
case "Indexed4":
case "Indexed8":
case "Gray8":
_format = XImageFormat.Gif;
break;
//case "{B96B3CB1-0728-11D3-9D7B-0000F81EF32E}": // tiff
// format = XImageFormat.Tiff;
// break;
//case "{B96B3CB5-0728-11D3-9D7B-0000F81EF32E}": // icon
// format = XImageFormat.Icon;
// break;
//case "{B96B3CAC-0728-11D3-9D7B-0000F81EF32E}": // emf
//case "{B96B3CAD-0728-11D3-9D7B-0000F81EF32E}": // wmf
//case "{B96B3CB2-0728-11D3-9D7B-0000F81EF32E}": // exif
//case "{B96B3CB3-0728-11D3-9D7B-0000F81EF32E}": // photoCD
//case "{B96B3CB4-0728-11D3-9D7B-0000F81EF32E}": // flashPIX
default:
Debug.Assert(false, "Unknown pixel format: " + pixelFormat);
_format = XImageFormat.Gif;
break;// throw new InvalidOperationException("Unsupported image format.");
}
}
#else
if (_wpfImage != null)
{
// TODO improve implementation for Silverlight.
//string pixelFormat = "jpg"; //_wpfImage...Format.ToString();
//string filename = GetImageFilename(_wpfImage);
// WPF treats all images as images.
// We give JPEG images a special treatment.
// Test if it's a JPEG:
bool isJpeg = true; // IsJpeg; // TestJpeg(filename);
if (isJpeg)
{
_format = XImageFormat.Jpeg;
return;
}
/*
switch (pixelFormat)
{
case "Bgr32":
case "Bgra32":
case "Pbgra32":
_format = XImageFormat.Png;
break;
//case "{B96B3CAE-0728-11D3-9D7B-0000F81EF32E}": // jpeg
// format = XImageFormat.Jpeg;
// break;
//case "{B96B3CB0-0728-11D3-9D7B-0000F81EF32E}": // gif
case "BlackWhite":
case "Indexed1":
case "Indexed4":
case "Indexed8":
case "Gray8":
_format = XImageFormat.Gif;
break;
//case "{B96B3CB1-0728-11D3-9D7B-0000F81EF32E}": // tiff
// format = XImageFormat.Tiff;
// break;
//case "{B96B3CB5-0728-11D3-9D7B-0000F81EF32E}": // icon
// format = XImageFormat.Icon;
// break;
//case "{B96B3CAC-0728-11D3-9D7B-0000F81EF32E}": // emf
//case "{B96B3CAD-0728-11D3-9D7B-0000F81EF32E}": // wmf
//case "{B96B3CB2-0728-11D3-9D7B-0000F81EF32E}": // exif
//case "{B96B3CB3-0728-11D3-9D7B-0000F81EF32E}": // photoCD
//case "{B96B3CB4-0728-11D3-9D7B-0000F81EF32E}": // flashPIX
default:
Debug.Assert(false, "Unknown pixel format: " + pixelFormat);
_format = XImageFormat.Gif;
break;// throw new InvalidOperationException("Unsupported image format.");
}
*/
}
#endif
#endif
}
#if WPF
/// <summary>
/// Gets the image filename.
/// </summary>
/// <param name="bitmapSource">The bitmap source.</param>
internal static string GetImageFilename(BitmapSource bitmapSource)
{
string filename = bitmapSource.ToString();
filename = UrlDecodeStringFromStringInternal(filename);
if (filename.StartsWith("file:///"))
filename = filename.Substring(8); // Remove all 3 slashes!
else if (filename.StartsWith("file://"))
filename = filename.Substring(5); // Keep 2 slashes (UNC path)
return filename;
}
private static string UrlDecodeStringFromStringInternal(string s/*, Encoding e*/)
{
int length = s.Length;
string result = "";
for (int i = 0; i < length; i++)
{
char ch = s[i];
if (ch == '+')
{
ch = ' ';
}
else if ((ch == '%') && (i < (length - 2)))
{
if ((s[i + 1] == 'u') && (i < (length - 5)))
{
int num3 = HexToInt(s[i + 2]);
int num4 = HexToInt(s[i + 3]);
int num5 = HexToInt(s[i + 4]);
int num6 = HexToInt(s[i + 5]);
if (((num3 < 0) || (num4 < 0)) || ((num5 < 0) || (num6 < 0)))
{
goto AddByte;
}
ch = (char)((((num3 << 12) | (num4 << 8)) | (num5 << 4)) | num6);
i += 5;
result += ch;
continue;
}
int num7 = HexToInt(s[i + 1]);
int num8 = HexToInt(s[i + 2]);
if ((num7 >= 0) && (num8 >= 0))
{
byte b = (byte)((num7 << 4) | num8);
i += 2;
result += (char)b;
continue;
}
}
AddByte:
if ((ch & 0xff80) == 0)
{
result += ch;
}
else
{
result += ch;
}
}
return result;
}
private static int HexToInt(char h)
{
if (h >= '0' && h <= '9')
return (h - '0');
if (h >= 'a' && h <= 'f')
return ((h - 'a') + 10);
if (h >= 'A' && h <= 'F')
return (h - 'A') + 10;
return -1;
}
#endif
#if WPF
/// <summary>
/// Tests if a file is a JPEG.
/// </summary>
/// <param name="filename">The filename.</param>
internal static bool TestJpeg(string filename)
{
byte[] imageBits = null;
return ReadJpegFile(filename, 16, ref imageBits);
}
/// <summary>
/// Tests if a file is a JPEG.
/// </summary>
/// <param name="stream">The filename.</param>
internal static bool TestJpeg(Stream stream)
{
byte[] imageBits = null;
return ReadJpegFile(stream, 16, ref imageBits) == true;
}
/// <summary>
/// Reads the JPEG file.
/// </summary>
/// <param name="filename">The filename.</param>
/// <param name="maxRead">The maximum count of bytes to be read.</param>
/// <param name="imageBits">The bytes read from the file.</param>
/// <returns>False, if file could not be read or is not a JPEG file.</returns>
internal static bool ReadJpegFile(string filename, int maxRead, ref byte[] imageBits)
{
if (File.Exists(filename))
{
FileStream fs = null;
try
{
fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
}
catch
{
return false;
}
bool? test = ReadJpegFile(fs, maxRead, ref imageBits);
// Treat test result as definite.
if (test == false || test == true)
{
fs.Close();
return test.Value;
}
// Test result is maybe.
// Hack: store the file in PDF if extension matches ...
string str = filename.ToLower();
if (str.EndsWith(".jpg") || str.EndsWith(".jpeg"))
return true;
}
return false;
}
/// <summary>
/// Reads the JPEG file.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="maxRead">The maximum count of bytes to be read.</param>
/// <param name="imageBits">The bytes read from the file.</param>
/// <returns>False, if file could not be read or is not a JPEG file.</returns>
internal static bool? ReadJpegFile(Stream stream, int maxRead, ref byte[] imageBits)
{
if (!stream.CanSeek)
return false;
stream.Seek(0, SeekOrigin.Begin);
if (stream.Length < 16)
{
return false;
}
int len = maxRead == -1 ? (int)stream.Length : maxRead;
imageBits = new byte[len];
stream.Read(imageBits, 0, len);
if (imageBits[0] == 0xff &&
imageBits[1] == 0xd8 &&
imageBits[2] == 0xff &&
imageBits[3] == 0xe0 &&
imageBits[6] == 0x4a &&
imageBits[7] == 0x46 &&
imageBits[8] == 0x49 &&
imageBits[9] == 0x46 &&
imageBits[10] == 0x0)
{
return true;
}
// TODO: Exif: find JFIF header
if (imageBits[0] == 0xff &&
imageBits[1] == 0xd8 &&
imageBits[2] == 0xff &&
imageBits[3] == 0xe1 /*&&
imageBits[6] == 0x4a &&
imageBits[7] == 0x46 &&
imageBits[8] == 0x49 &&
imageBits[9] == 0x46 &&
imageBits[10] == 0x0*/)
{
// Hack: store the file in PDF if extension matches ...
return null;
}
return false;
}
#endif
/// <summary>
/// Under construction
/// </summary>
public void Dispose()
{
Dispose(true);
//GC.SuppressFinalize(this);
}
/// <summary>
/// Disposes underlying GDI+ object.
/// </summary>
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
_disposed = true;
#if CORE || GDI || WPF
//if (_importedImage != null)
{
_importedImage = null;
}
#endif
#if CORE_WITH_GDI || GDI
if (_gdiImage != null)
{
try
{
Lock.EnterGdiPlus();
_gdiImage.Dispose();
_gdiImage = null;
}
finally { Lock.ExitGdiPlus(); }
}
#endif
#if WPF
_wpfImage = null;
#endif
}
bool _disposed;
/// <summary>
/// Gets the width of the image.
/// </summary>
[Obsolete("Use either PixelWidth or PointWidth. Temporarily obsolete because of rearrangements for WPF. Currently same as PixelWidth, but will become PointWidth in future releases of PDFsharp.")]
public virtual double Width
{
get
{
#if CORE || GDI || WPF
if (_importedImage != null)
{
return _importedImage.Information.Width;
}
#endif
#if (CORE_WITH_GDI || GDI) && !WPF
try
{
Lock.EnterGdiPlus();
return _gdiImage.Width;
}
finally { Lock.ExitGdiPlus(); }
#endif
#if GDI && WPF
double gdiWidth = _gdiImage.Width;
double wpfWidth = _wpfImage.PixelWidth;
Debug.Assert(gdiWidth == wpfWidth);
return wpfWidth;
#endif
//#if GDI && !WPF
// return _gdiImage.Width;
//#endif
#if WPF && !GDI
return _wpfImage.PixelWidth;
#endif
#if NETFX_CORE || UWP
return 100;
#endif
}
}
/// <summary>
/// Gets the height of the image.
/// </summary>
[Obsolete("Use either PixelHeight or PointHeight. Temporarily obsolete because of rearrangements for WPF. Currently same as PixelHeight, but will become PointHeight in future releases of PDFsharp.")]
public virtual double Height
{
get
{
#if CORE_WITH_GDI || GDI || WPF
if (_importedImage != null)
{
return _importedImage.Information.Height;
}
#endif
#if (CORE_WITH_GDI || GDI) && !WPF && !WPF
try
{
Lock.EnterGdiPlus();
return _gdiImage.Height;
}
finally { Lock.ExitGdiPlus(); }
#endif
#if GDI && WPF
double gdiHeight = _gdiImage.Height;
double wpfHeight = _wpfImage.PixelHeight;
Debug.Assert(gdiHeight == wpfHeight);
return wpfHeight;
#endif
//#if GDI && !WPF
// return _gdiImage.Height;
//#endif
#if WPF && !GDI
return _wpfImage.PixelHeight;
#endif
#if NETFX_CORE || UWP
return _wrtImage.PixelHeight;
#endif
}
}
#if CORE || GDI || WPF
/// <summary>
/// The factor for conversion from DPM to PointWidth or PointHeight.
/// 72 points per inch, 1000 mm per meter, 25.4 mm per inch => 72 * 1000 / 25.4.
/// </summary>
private const decimal FactorDPM72 = 72000 / 25.4m;
/// <summary>
/// The factor for conversion from DPM to PointWidth or PointHeight.
/// 1000 mm per meter, 25.4 mm per inch => 1000 / 25.4.
/// </summary>
private const decimal FactorDPM = 1000 / 25.4m;
#endif
/// <summary>
/// Gets the width of the image in point.
/// </summary>
public virtual double PointWidth
{
get
{
#if CORE || GDI || WPF
if (_importedImage != null)
{
if (_importedImage.Information.HorizontalDPM > 0)
return (double)(_importedImage.Information.Width * FactorDPM72 / _importedImage.Information.HorizontalDPM);
if (_importedImage.Information.HorizontalDPI > 0)
return (double)(_importedImage.Information.Width * 72 / _importedImage.Information.HorizontalDPI);
// Assume 72 DPI if information not available.
return _importedImage.Information.Width;
}
#endif
#if (CORE_WITH_GDI || GDI) && !WPF
try
{
Lock.EnterGdiPlus();
return _gdiImage.Width * 72 / _gdiImage.HorizontalResolution;
}
finally { Lock.ExitGdiPlus(); }
#endif
#if GDI && WPF
double gdiWidth = _gdiImage.Width * 72 / _gdiImage.HorizontalResolution;
double wpfWidth = _wpfImage.Width * 72.0 / 96.0;
//Debug.Assert(gdiWidth == wpfWidth);
Debug.Assert(DoubleUtil.AreRoughlyEqual(gdiWidth, wpfWidth, 5));
return wpfWidth;
#endif
//#if GDI && !WPF
// return _gdiImage.Width * 72 / _gdiImage.HorizontalResolution;
//#endif
#if WPF && !GDI
#if !SILVERLIGHT
Debug.Assert(Math.Abs(_wpfImage.PixelWidth * 72 / _wpfImage.DpiX - _wpfImage.Width * 72.0 / 96.0) < 0.001);
return _wpfImage.Width * 72.0 / 96.0;
#else
// AGHACK
return _wpfImage.PixelWidth * 72 / 96.0;
#endif
#endif
#if NETFX_CORE || UWP
//var wb = new WriteableBitmap();
//GetImagePropertiesAsync
return 100;
#endif
}
}
/// <summary>
/// Gets the height of the image in point.
/// </summary>
public virtual double PointHeight
{
get
{
#if CORE || GDI || WPF
if (_importedImage != null)
{
if (_importedImage.Information.VerticalDPM > 0)
return (double)(_importedImage.Information.Height * FactorDPM72 / _importedImage.Information.VerticalDPM);
if (_importedImage.Information.VerticalDPI > 0)
return (double)(_importedImage.Information.Height * 72 / _importedImage.Information.VerticalDPI);
// Assume 72 DPI if information not available.
return _importedImage.Information.Width;
}
#endif
#if (CORE_WITH_GDI || GDI) && !WPF
try
{
Lock.EnterGdiPlus();
return _gdiImage.Height * 72 / _gdiImage.HorizontalResolution;
}
finally { Lock.ExitGdiPlus(); }
#endif
#if GDI && WPF
double gdiHeight = _gdiImage.Height * 72 / _gdiImage.HorizontalResolution;
double wpfHeight = _wpfImage.Height * 72.0 / 96.0;
Debug.Assert(DoubleUtil.AreRoughlyEqual(gdiHeight, wpfHeight, 5));
return wpfHeight;
#endif
//#if GDI && !WPF
// return _gdiImage.Height * 72 / _gdiImage.HorizontalResolution;
//#endif
#if WPF && !GDI
#if !SILVERLIGHT
Debug.Assert(Math.Abs(_wpfImage.PixelHeight * 72 / _wpfImage.DpiY - _wpfImage.Height * 72.0 / 96.0) < 0.001);
return _wpfImage.Height * 72.0 / 96.0;
#else
// AGHACK
return _wpfImage.PixelHeight * 72 / 96.0;
#endif
#endif
#if NETFX_CORE || UWP
return _wrtImage.PixelHeight; //_gdi Image.Width * 72 / _gdiImage.HorizontalResolution;
#endif
}
}
/// <summary>
/// Gets the width of the image in pixels.
/// </summary>
public virtual int PixelWidth
{
get
{
#if CORE || GDI || WPF
if (_importedImage != null)
return (int)_importedImage.Information.Width;
#endif
#if CORE_WITH_GDI
try
{
Lock.EnterGdiPlus();
return _gdiImage.Width;
}
finally { Lock.ExitGdiPlus(); }
#endif
#if GDI && !WPF
try
{
Lock.EnterGdiPlus();
return _gdiImage.Width;
}
finally { Lock.ExitGdiPlus(); }
#endif
#if GDI && WPF
int gdiWidth = _gdiImage.Width;
int wpfWidth = _wpfImage.PixelWidth;
Debug.Assert(gdiWidth == wpfWidth);
return wpfWidth;
#endif
//#if GDI && !WPF
// return _gdiImage.Width;
//#endif
#if WPF && !GDI
return _wpfImage.PixelWidth;
#endif
#if NETFX_CORE || UWP
return _wrtImage.PixelWidth;
#endif
}
}
/// <summary>
/// Gets the height of the image in pixels.
/// </summary>
public virtual int PixelHeight
{
get
{
#if CORE || GDI || WPF
if (_importedImage != null)
return (int)_importedImage.Information.Height;
#endif
#if CORE_WITH_GDI
try
{
Lock.EnterGdiPlus();
return _gdiImage.Height;
}
finally { Lock.ExitGdiPlus(); }
#endif
#if GDI && !WPF
try
{
Lock.EnterGdiPlus();
return _gdiImage.Height;
}
finally { Lock.ExitGdiPlus(); }
#endif
#if GDI && WPF
int gdiHeight = _gdiImage.Height;
int wpfHeight = _wpfImage.PixelHeight;
Debug.Assert(gdiHeight == wpfHeight);
return wpfHeight;
#endif
//#if GDI && !WPF
// return _gdiImage.Height;
//#endif
#if WPF && !GDI
return _wpfImage.PixelHeight;
#endif
#if NETFX_CORE || UWP
return _wrtImage.PixelHeight;
#endif
}
}
/// <summary>
/// Gets the size in point of the image.
/// </summary>
public virtual XSize Size
{
get { return new XSize(PointWidth, PointHeight); }
}
/// <summary>
/// Gets the horizontal resolution of the image.
/// </summary>
public virtual double HorizontalResolution
{
get
{
#if CORE || GDI || WPF
if (_importedImage != null)
{
if (_importedImage.Information.HorizontalDPI > 0)
return (double)_importedImage.Information.HorizontalDPI;
if (_importedImage.Information.HorizontalDPM > 0)
return (double)(_importedImage.Information.HorizontalDPM / FactorDPM);
return 72;
}
#endif
#if (CORE_WITH_GDI || GDI) && !WPF
try
{
Lock.EnterGdiPlus();
return _gdiImage.HorizontalResolution;
}
finally { Lock.ExitGdiPlus(); }
#endif
#if GDI && WPF
double gdiResolution = _gdiImage.HorizontalResolution;
double wpfResolution = _wpfImage.PixelWidth * 96.0 / _wpfImage.Width;
Debug.Assert(gdiResolution == wpfResolution);
return wpfResolution;
#endif
//#if GDI && !WPF
// return _gdiImage.HorizontalResolution;
//#endif
#if WPF && !GDI
#if !SILVERLIGHT
return _wpfImage.DpiX; //.PixelWidth * 96.0 / _wpfImage.Width;
#else
// AGHACK
return 96;
#endif
#endif
#if NETFX_CORE || UWP
return 96;
#endif
}
}
/// <summary>
/// Gets the vertical resolution of the image.
/// </summary>
public virtual double VerticalResolution
{
get
{
#if CORE || GDI || WPF
if (_importedImage != null)
{
if (_importedImage.Information.VerticalDPI > 0)
return (double)_importedImage.Information.VerticalDPI;
if (_importedImage.Information.VerticalDPM > 0)
return (double)(_importedImage.Information.VerticalDPM / FactorDPM);
return 72;
}
#endif
#if (CORE_WITH_GDI || GDI) && !WPF
try
{
Lock.EnterGdiPlus();
return _gdiImage.VerticalResolution;
}
finally { Lock.ExitGdiPlus(); }
#endif
#if GDI && WPF
double gdiResolution = _gdiImage.VerticalResolution;
double wpfResolution = _wpfImage.PixelHeight * 96.0 / _wpfImage.Height;
Debug.Assert(gdiResolution == wpfResolution);
return wpfResolution;
#endif
//#if GDI && !WPF
// return _gdiImage.VerticalResolution;
//#endif
#if WPF && !GDI
#if !SILVERLIGHT
return _wpfImage.DpiY; //.PixelHeight * 96.0 / _wpfImage.Height;
#else
// AGHACK
return 96;
#endif
#endif
#if NETFX_CORE || UWP
return 96;
#endif
}
}
/// <summary>
/// Gets or sets a flag indicating whether image interpolation is to be performed.
/// </summary>
public virtual bool Interpolate
{
get { return _interpolate; }
set { _interpolate = value; }
}
bool _interpolate = true;
/// <summary>
/// Gets the format of the image.
/// </summary>
public XImageFormat Format
{
get { return _format; }
}
XImageFormat _format;
#if WPF
/// <summary>
/// Gets a value indicating whether this image is JPEG.
/// </summary>
internal virtual bool IsJpeg
{
#if !SILVERLIGHT
//get { if (!isJpeg.HasValue) InitializeGdiHelper(); return isJpeg.HasValue ? isJpeg.Value : false; }
get
{
if (!_isJpeg.HasValue)
InitializeJpegQuickTest();
return _isJpeg.HasValue ? _isJpeg.Value : false;
}
//set { isJpeg = value; }
#else
// AGHACK
get { return true; }
#endif
}
private bool? _isJpeg;
/// <summary>
/// Gets a value indicating whether this image is cmyk.
/// </summary>
internal virtual bool IsCmyk
{
#if !SILVERLIGHT
get { if (!_isCmyk.HasValue) InitializeGdiHelper(); return _isCmyk.HasValue ? _isCmyk.Value : false; }
//set { isCmyk = value; }
#else
get { return false; } // AGHACK
#endif
}
private bool? _isCmyk;
#if !SILVERLIGHT
/// <summary>
/// Gets the JPEG memory stream (if IsJpeg returns true).
/// </summary>
public virtual MemoryStream Memory
{
get
{
if (!_isCmyk.HasValue) InitializeGdiHelper();
return _memory;
}
//set { memory = value; }
}
MemoryStream _memory;
/// <summary>
/// Determines if an image is JPEG w/o creating an Image object.
/// </summary>
private void InitializeJpegQuickTest()
{
if (_stream != null)
_isJpeg = TestJpeg(_stream);
else
_isJpeg = TestJpeg(GetImageFilename(_wpfImage));
}
/// <summary>
/// Initializes the GDI helper.
/// We use GDI+ to detect if image is JPEG.
/// If so, we also determine if it's CMYK and we read the image bytes.
/// </summary>
private void InitializeGdiHelper()
{
if (!_isCmyk.HasValue)
{
try
{
string imageFilename = GetImageFilename(_wpfImage);
// To reduce exceptions, check if file exists.
if (!string.IsNullOrEmpty(imageFilename) && File.Exists(imageFilename))
{
MemoryStream memory = new MemoryStream();
using (FileStream file = new FileStream(imageFilename, FileMode.Open, FileAccess.Read, FileShare.Read))
{
byte[] bytes = new byte[file.Length];
file.Read(bytes, 0, (int)file.Length);
memory.Write(bytes, 0, (int)file.Length);
memory.Seek(0, SeekOrigin.Begin);
}
InitializeJpegHelper(memory);
}
else if (_stream != null)
{
MemoryStream memory = new MemoryStream();
// If we have a stream, copy data from the stream.
if (_stream != null && _stream.CanSeek)
{
_stream.Seek(0, SeekOrigin.Begin);
byte[] buffer = new byte[32 * 1024]; // 32K buffer.
int bytesRead;
while ((bytesRead = _stream.Read(buffer, 0, buffer.Length)) > 0)
{
memory.Write(buffer, 0, bytesRead);
}
InitializeJpegHelper(memory);
}
}
}
catch { }
}
}
private void InitializeJpegHelper(MemoryStream memory)
{
using (System.Drawing.Image image = new System.Drawing.Bitmap(memory))
{
string guid = image.RawFormat.Guid.ToString("B").ToUpper();
_isJpeg = guid == "{B96B3CAE-0728-11D3-9D7B-0000F81EF32E}";
_isCmyk = (image.Flags &
((int)System.Drawing.Imaging.ImageFlags.ColorSpaceCmyk | (int)System.Drawing.Imaging.ImageFlags.ColorSpaceYcck)) != 0;
if (_isJpeg.Value)
{
//_memory = new MemoryStream();
//image.Save(_memory, System.Drawing.Imaging.ImageFormat.Jpeg);
if ((int)memory.Length != 0)
{
_memory = memory;
}
else
{
_memory = null;
}
}
}
}
#endif
#endif
#if DEBUG_
// TEST
internal void CreateAllImages(string name)
{
if (image != null)
{
image.Save(name + ".bmp", ImageFormat.Bmp);
image.Save(name + ".emf", ImageFormat.Emf);
image.Save(name + ".exif", ImageFormat.Exif);
image.Save(name + ".gif", ImageFormat.Gif);
image.Save(name + ".ico", ImageFormat.Icon);
image.Save(name + ".jpg", ImageFormat.Jpeg);
image.Save(name + ".png", ImageFormat.Png);
image.Save(name + ".tif", ImageFormat.Tiff);
image.Save(name + ".wmf", ImageFormat.Wmf);
image.Save(name + "2.bmp", ImageFormat.MemoryBmp);
}
}
#endif
internal void AssociateWithGraphics(XGraphics gfx)
{
if (_associatedGraphics != null)
throw new InvalidOperationException("XImage already associated with XGraphics.");
_associatedGraphics = null;
}
internal void DisassociateWithGraphics()
{
if (_associatedGraphics == null)
throw new InvalidOperationException("XImage not associated with XGraphics.");
_associatedGraphics.DisassociateImage();
Debug.Assert(_associatedGraphics == null);
}
internal void DisassociateWithGraphics(XGraphics gfx)
{
if (_associatedGraphics != gfx)
throw new InvalidOperationException("XImage not associated with XGraphics.");
_associatedGraphics = null;
}
internal XGraphics AssociatedGraphics
{
get { return _associatedGraphics; }
set { _associatedGraphics = value; }
}
XGraphics _associatedGraphics;
#if CORE || GDI || WPF
internal ImportedImage _importedImage;
#endif
#if CORE_WITH_GDI || GDI
internal Image _gdiImage;
#endif
#if WPF
internal BitmapSource _wpfImage;
#if SILVERLIGHT
//internal byte[] _bytes;
#endif
#endif
#if NETFX_CORE || UWP
internal BitmapSource _wrtImage;
#endif
/// <summary>
/// If path starts with '*' the image is created from a stream and the path is a GUID.
/// </summary>
internal string _path;
/// <summary>
/// Contains a reference to the original stream if image was created from a stream.
/// </summary>
internal Stream _stream;
/// <summary>
/// Cache PdfImageTable.ImageSelector to speed up finding the right PdfImage
/// if this image is used more than once.
/// </summary>
internal PdfImageTable.ImageSelector _selector;
}
}