ASCU_ALL/MigraDoc.Rendering/Rendering/ImageRenderer.cs
2020-09-04 12:49:15 +05:00

368 lines
15 KiB
C#

#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
{
/// <summary>
/// Renders images.
/// </summary>
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;
}
}