#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, } /// /// 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. /// 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; } //} /// /// Initializes a new instance of the class. /// protected XImage() { } #if GDI || CORE || WPF /// /// Initializes a new instance of the class from an image read by ImageImporter. /// /// The image. /// image XImage(ImportedImage image) { if (image == null) throw new ArgumentNullException("image"); _importedImage = image; Initialize(); } #endif #if GDI /// /// Initializes a new instance of the class from a GDI+ image. /// XImage(Image image) { _gdiImage = image; #if WPF // Is defined in hybrid build. _wpfImage = ImageHelper.CreateBitmapSource(image); #endif Initialize(); } #endif #if WPF /// /// Initializes a new instance of the class from a WPF image. /// 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; } /// /// Creates an BitmapImage from URI. Sets BitmapCacheOption.OnLoad for WPF to prevent image file from being locked. /// 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 /// /// Initializes a new instance of the class from a WinRT image. /// 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 /// /// Implicit conversion from Image to XImage. /// public static implicit operator XImage(Image image) { return new XImage(image); } #endif /// /// Conversion from Image to XImage. /// public static XImage FromGdiPlusImage(Image image) { return new XImage(image); } #endif #if WPF /// /// Conversion from BitmapSource to XImage. /// public static XImage FromBitmapSource(BitmapSource image) { return new XImage(image); } #endif #if NETFX_CORE /// /// Conversion from BitmapSource to XImage. /// public static XImage FromBitmapSource(BitmapSource image) { return new XImage(image); } #endif /// /// Creates an image from the specified file. /// /// The path to a BMP, PNG, GIF, JPEG, TIFF, or PDF file. public static XImage FromFile(string path) { if (PdfReader.TestPdfFile(path) > 0) return new XPdfForm(path); return new XImage(path); } /// /// Creates an image from the specified stream.
/// Silverlight supports PNG and JPEG only. ///
/// The stream containing a BMP, PNG, GIF, JPEG, TIFF, or PDF file. 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 /// /// Creates an image from the specified file. /// /// The path to a BMP, PNG, GIF, JPEG, TIFF, or PDF file. /// Uses an platform-independent implementation if set to true. /// The platform-dependent implementation, if available, will support more image formats. /// The document used to obtain the options. 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; } /// /// Creates an image from the specified stream.
/// Silverlight supports PNG and JPEF only. ///
/// The stream containing a BMP, PNG, GIF, JPEG, TIFF, or PDF file. /// Uses an platform-independent implementation if set to true. /// The platform-dependent implementation, if available, will support more image formats. /// The document used to obtain the options. 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 /// /// Creates an image. /// /// The imported image. [Obsolete("THHO4THHO Internal test code.")] internal static XImage FromImportedImage(ImportedImage image) { if (image == null) throw new ArgumentNullException("image"); return new XImage(image); } #endif #endif /// /// Tests if a file exist. Supports PDF files with page number suffix. /// /// The path to a BMP, PNG, GIF, JPEG, TIFF, or PDF file. 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 /// /// Gets the image filename. /// /// The bitmap source. 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 /// /// Tests if a file is a JPEG. /// /// The filename. internal static bool TestJpeg(string filename) { byte[] imageBits = null; return ReadJpegFile(filename, 16, ref imageBits); } /// /// Tests if a file is a JPEG. /// /// The filename. internal static bool TestJpeg(Stream stream) { byte[] imageBits = null; return ReadJpegFile(stream, 16, ref imageBits) == true; } /// /// Reads the JPEG file. /// /// The filename. /// The maximum count of bytes to be read. /// The bytes read from the file. /// False, if file could not be read or is not a JPEG file. 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; } /// /// Reads the JPEG file. /// /// The stream. /// The maximum count of bytes to be read. /// The bytes read from the file. /// False, if file could not be read or is not a JPEG file. 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 /// /// Under construction /// public void Dispose() { Dispose(true); //GC.SuppressFinalize(this); } /// /// Disposes underlying GDI+ object. /// 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; /// /// Gets the width of the image. /// [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 } } /// /// Gets the height of the image. /// [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 /// /// 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. /// private const decimal FactorDPM72 = 72000 / 25.4m; /// /// The factor for conversion from DPM to PointWidth or PointHeight. /// 1000 mm per meter, 25.4 mm per inch => 1000 / 25.4. /// private const decimal FactorDPM = 1000 / 25.4m; #endif /// /// Gets the width of the image in point. /// 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 } } /// /// Gets the height of the image in point. /// 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 } } /// /// Gets the width of the image in pixels. /// 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 } } /// /// Gets the height of the image in pixels. /// 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 } } /// /// Gets the size in point of the image. /// public virtual XSize Size { get { return new XSize(PointWidth, PointHeight); } } /// /// Gets the horizontal resolution of the image. /// 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 } } /// /// Gets the vertical resolution of the image. /// 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 } } /// /// Gets or sets a flag indicating whether image interpolation is to be performed. /// public virtual bool Interpolate { get { return _interpolate; } set { _interpolate = value; } } bool _interpolate = true; /// /// Gets the format of the image. /// public XImageFormat Format { get { return _format; } } XImageFormat _format; #if WPF /// /// Gets a value indicating whether this image is JPEG. /// 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; /// /// Gets a value indicating whether this image is cmyk. /// 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 /// /// Gets the JPEG memory stream (if IsJpeg returns true). /// public virtual MemoryStream Memory { get { if (!_isCmyk.HasValue) InitializeGdiHelper(); return _memory; } //set { memory = value; } } MemoryStream _memory; /// /// Determines if an image is JPEG w/o creating an Image object. /// private void InitializeJpegQuickTest() { if (_stream != null) _isJpeg = TestJpeg(_stream); else _isJpeg = TestJpeg(GetImageFilename(_wpfImage)); } /// /// 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. /// 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 /// /// If path starts with '*' the image is created from a stream and the path is a GUID. /// internal string _path; /// /// Contains a reference to the original stream if image was created from a stream. /// internal Stream _stream; /// /// Cache PdfImageTable.ImageSelector to speed up finding the right PdfImage /// if this image is used more than once. /// internal PdfImageTable.ImageSelector _selector; } }