#region MigraDoc - Creating Documents on the Fly // // Authors: // Klaus Potzesny // // Copyright (c) 2001-2017 empira Software GmbH, Cologne Area (Germany) // // http://www.pdfsharp.com // http://www.migradoc.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.IO; using System.Diagnostics; using PdfSharp.Drawing; using MigraDoc.DocumentObjectModel.Shapes; using MigraDoc.Rendering.Resources; namespace MigraDoc.Rendering { /// /// Renders images. /// public class ImageRenderer : ShapeRenderer { public ImageRenderer(XGraphics gfx, Image image, FieldInfos fieldInfos) : base(gfx, image, fieldInfos) { _image = image; ImageRenderInfo renderInfo = new ImageRenderInfo(); renderInfo.DocumentObject = _shape; _renderInfo = renderInfo; } public ImageRenderer(XGraphics gfx, RenderInfo renderInfo, FieldInfos fieldInfos) : base(gfx, renderInfo, fieldInfos) { _image = (Image)renderInfo.DocumentObject; } public override void Format(Area area, FormatInfo previousFormatInfo) { _imageFilePath = _image.GetFilePath(_documentRenderer.WorkingDirectory); // The Image is stored in the string if path starts with "base64:", otherwise we check whether the file exists. if (!_imageFilePath.StartsWith("base64:") && !XImage.ExistsFile(_imageFilePath)) { _failure = ImageFailure.FileNotFound; Debug.WriteLine(Messages2.ImageNotFound(_image.Name), "warning"); } ImageFormatInfo formatInfo = (ImageFormatInfo)_renderInfo.FormatInfo; formatInfo.Failure = _failure; formatInfo.ImagePath = _imageFilePath; CalculateImageDimensions(); base.Format(area, previousFormatInfo); } protected override XUnit ShapeHeight { get { ImageFormatInfo formatInfo = (ImageFormatInfo)_renderInfo.FormatInfo; return formatInfo.Height + _lineFormatRenderer.GetWidth(); } } protected override XUnit ShapeWidth { get { ImageFormatInfo formatInfo = (ImageFormatInfo)_renderInfo.FormatInfo; return formatInfo.Width + _lineFormatRenderer.GetWidth(); } } public override void Render() { RenderFilling(); ImageFormatInfo formatInfo = (ImageFormatInfo)_renderInfo.FormatInfo; Area contentArea = _renderInfo.LayoutInfo.ContentArea; XRect destRect = new XRect(contentArea.X, contentArea.Y, formatInfo.Width, formatInfo.Height); if (formatInfo.Failure == ImageFailure.None) { XImage xImage = null; try { XRect srcRect = new XRect(formatInfo.CropX, formatInfo.CropY, formatInfo.CropWidth, formatInfo.CropHeight); //xImage = XImage.FromFile(formatInfo.ImagePath); xImage = CreateXImage(formatInfo.ImagePath); _gfx.DrawImage(xImage, destRect, srcRect, XGraphicsUnit.Point); //Pixel. } catch (Exception) { RenderFailureImage(destRect); } finally { if (xImage != null) xImage.Dispose(); } } else RenderFailureImage(destRect); RenderLine(); } void RenderFailureImage(XRect destRect) { _gfx.DrawRectangle(XBrushes.LightGray, destRect); string failureString; ImageFormatInfo formatInfo = (ImageFormatInfo)RenderInfo.FormatInfo; switch (formatInfo.Failure) { case ImageFailure.EmptySize: failureString = Messages2.DisplayEmptyImageSize; break; case ImageFailure.FileNotFound: failureString = Messages2.DisplayImageFileNotFound; break; case ImageFailure.InvalidType: failureString = Messages2.DisplayInvalidImageType; break; case ImageFailure.NotRead: default: failureString = Messages2.DisplayImageNotRead; break; } // Create stub font XFont font = new XFont("Courier New", 8); _gfx.DrawString(failureString, font, XBrushes.Red, destRect, XStringFormats.Center); } private void CalculateImageDimensions() { ImageFormatInfo formatInfo = (ImageFormatInfo)_renderInfo.FormatInfo; if (formatInfo.Failure == ImageFailure.None) { XImage xImage = null; try { //xImage = XImage.FromFile(_imageFilePath); xImage = CreateXImage(_imageFilePath); } catch (InvalidOperationException ex) { Debug.WriteLine(Messages2.InvalidImageType(ex.Message)); formatInfo.Failure = ImageFailure.InvalidType; } if (formatInfo.Failure == ImageFailure.None) { try { XUnit usrWidth = _image.Width.Point; XUnit usrHeight = _image.Height.Point; bool usrWidthSet = !_image._width.IsNull; bool usrHeightSet = !_image._height.IsNull; XUnit resultWidth = usrWidth; XUnit resultHeight = usrHeight; Debug.Assert(xImage != null); double xPixels = xImage.PixelWidth; bool usrResolutionSet = !_image._resolution.IsNull; double horzRes = usrResolutionSet ? _image.Resolution : xImage.HorizontalResolution; double vertRes = usrResolutionSet ? _image.Resolution : xImage.VerticalResolution; // ReSharper disable CompareOfFloatsByEqualityOperator if (horzRes == 0 && vertRes == 0) { horzRes = 72; vertRes = 72; } else if (horzRes == 0) { Debug.Assert(false, "How can this be?"); horzRes = 72; } else if (vertRes == 0) { Debug.Assert(false, "How can this be?"); vertRes = 72; } // ReSharper restore CompareOfFloatsByEqualityOperator XUnit inherentWidth = XUnit.FromInch(xPixels / horzRes); double yPixels = xImage.PixelHeight; XUnit inherentHeight = XUnit.FromInch(yPixels / vertRes); //bool lockRatio = _image.IsNull("LockAspectRatio") ? true : _image.LockAspectRatio; bool lockRatio = _image._lockAspectRatio.IsNull || _image.LockAspectRatio; double scaleHeight = _image.ScaleHeight; double scaleWidth = _image.ScaleWidth; //bool scaleHeightSet = !_image.IsNull("ScaleHeight"); //bool scaleWidthSet = !_image.IsNull("ScaleWidth"); bool scaleHeightSet = !_image._scaleHeight.IsNull; bool scaleWidthSet = !_image._scaleWidth.IsNull; if (lockRatio && !(scaleHeightSet && scaleWidthSet)) { if (usrWidthSet && !usrHeightSet) { resultHeight = inherentHeight / inherentWidth * usrWidth; } else if (usrHeightSet && !usrWidthSet) { resultWidth = inherentWidth / inherentHeight * usrHeight; } // ReSharper disable once ConditionIsAlwaysTrueOrFalse else if (!usrHeightSet && !usrWidthSet) { resultHeight = inherentHeight; resultWidth = inherentWidth; } if (scaleHeightSet) { resultHeight = resultHeight * scaleHeight; resultWidth = resultWidth * scaleHeight; } if (scaleWidthSet) { resultHeight = resultHeight * scaleWidth; resultWidth = resultWidth * scaleWidth; } } else { if (!usrHeightSet) resultHeight = inherentHeight; if (!usrWidthSet) resultWidth = inherentWidth; if (scaleHeightSet) resultHeight = resultHeight * scaleHeight; if (scaleWidthSet) resultWidth = resultWidth * scaleWidth; } formatInfo.CropWidth = (int)xPixels; formatInfo.CropHeight = (int)yPixels; if (_image._pictureFormat != null && !_image._pictureFormat.IsNull()) { PictureFormat picFormat = _image.PictureFormat; //Cropping in pixels. XUnit cropLeft = picFormat.CropLeft.Point; XUnit cropRight = picFormat.CropRight.Point; XUnit cropTop = picFormat.CropTop.Point; XUnit cropBottom = picFormat.CropBottom.Point; formatInfo.CropX = (int)(horzRes * cropLeft.Inch); formatInfo.CropY = (int)(vertRes * cropTop.Inch); formatInfo.CropWidth -= (int)(horzRes * ((XUnit)(cropLeft + cropRight)).Inch); formatInfo.CropHeight -= (int)(vertRes * ((XUnit)(cropTop + cropBottom)).Inch); //Scaled cropping of the height and width. double xScale = resultWidth / inherentWidth; double yScale = resultHeight / inherentHeight; cropLeft = xScale * cropLeft; cropRight = xScale * cropRight; cropTop = yScale * cropTop; cropBottom = yScale * cropBottom; resultHeight = resultHeight - cropTop - cropBottom; resultWidth = resultWidth - cropLeft - cropRight; } if (resultHeight <= 0 || resultWidth <= 0) { formatInfo.Width = XUnit.FromCentimeter(2.5); formatInfo.Height = XUnit.FromCentimeter(2.5); Debug.WriteLine(Messages2.EmptyImageSize); _failure = ImageFailure.EmptySize; } else { formatInfo.Width = resultWidth; formatInfo.Height = resultHeight; } } catch (Exception ex) { Debug.WriteLine(Messages2.ImageNotReadable(_image.Name, ex.Message)); formatInfo.Failure = ImageFailure.NotRead; } finally { if (xImage != null) xImage.Dispose(); } } } if (formatInfo.Failure != ImageFailure.None) { if (!_image._width.IsNull) formatInfo.Width = _image.Width.Point; else formatInfo.Width = XUnit.FromCentimeter(2.5); if (!_image._height.IsNull) formatInfo.Height = _image.Height.Point; else formatInfo.Height = XUnit.FromCentimeter(2.5); } } XImage CreateXImage(string uri) { if (uri.StartsWith("base64:")) { string base64 = uri.Substring("base64:".Length); byte[] bytes = Convert.FromBase64String(base64); #if WPF || CORE_WITH_GDI || GDI // WPF stores a reference to the stream publicly. We must not destroy the stream here, otherwise rendering the PDF will fail. // Same for GDI. CORE currently uses the GDI implementation. // We have to rely on the garbage collector to properly dispose the MemoryStream. { Stream stream = new MemoryStream(bytes); XImage image = XImage.FromStream(stream); return image; } #else using (Stream stream = new MemoryStream(bytes)) { XImage image = XImage.FromStream(stream); return image; } #endif } return XImage.FromFile(uri); } readonly Image _image; string _imageFilePath; ImageFailure _failure; } }