ASCU_ALL/PrintPDF/PdfSharp/Drawing/XPdfForm.cs

416 lines
14 KiB
C#
Raw Normal View History

2020-09-04 12:49:15 +05:00
#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.IO;
#if GDI
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
#endif
#if WPF
using System.Windows.Media;
#endif
using PdfSharp.Internal;
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;
namespace PdfSharp.Drawing
{
/// <summary>
/// Represents a so called 'PDF form external object', which is typically an imported page of an external
/// PDF document. XPdfForm objects are used like images to draw an existing PDF page of an external
/// document in the current document. XPdfForm objects can only be placed in PDF documents. If you try
/// to draw them using a XGraphics based on an GDI+ context no action is taken if no placeholder image
/// is specified. Otherwise the place holder is drawn.
/// </summary>
public class XPdfForm : XForm
{
/// <summary>
/// Initializes a new instance of the XPdfForm class from the specified path to an external PDF document.
/// Although PDFsharp internally caches XPdfForm objects it is recommended to reuse XPdfForm objects
/// in your code and change the PageNumber property if more than one page is needed form the external
/// document. Furthermore, because XPdfForm can occupy very much memory, it is recommended to
/// dispose XPdfForm objects if not needed anymore.
/// </summary>
internal XPdfForm(string path)
{
int pageNumber;
path = ExtractPageNumber(path, out pageNumber);
#if !NETFX_CORE
path = Path.GetFullPath(path);
if (!File.Exists(path))
throw new FileNotFoundException(PSSR.FileNotFound(path));
#endif
if (PdfReader.TestPdfFile(path) == 0)
throw new ArgumentException("The specified file has no valid PDF file header.", "path");
_path = path;
if (pageNumber != 0)
PageNumber = pageNumber;
}
/// <summary>
/// Initializes a new instance of the <see cref="XPdfForm"/> class from a stream.
/// </summary>
/// <param name="stream">The stream.</param>
internal XPdfForm(Stream stream)
{
// Create a dummy unique path
_path = "*" + Guid.NewGuid().ToString("B");
if (PdfReader.TestPdfFile(stream) == 0)
throw new ArgumentException("The specified stream has no valid PDF file header.", "stream");
_externalDocument = PdfReader.Open(stream);
}
/// <summary>
/// Creates an XPdfForm from a file.
/// </summary>
public static new XPdfForm FromFile(string path)
{
// TODO: Same file should return same object (that's why the function is static).
return new XPdfForm(path);
}
/// <summary>
/// Creates an XPdfForm from a stream.
/// </summary>
public static new XPdfForm FromStream(Stream stream)
{
return new XPdfForm(stream);
}
/*
void Initialize()
{
// ImageFormat has no overridden Equals...
}
*/
/// <summary>
/// Sets the form in the state FormState.Finished.
/// </summary>
internal override void Finish()
{
if (_formState == FormState.NotATemplate || _formState == FormState.Finished)
return;
base.Finish();
//if (Gfx.metafile != null)
// image = Gfx.metafile;
//Debug.Assert(_fromState == FormState.Created || _fromState == FormState.UnderConstruction);
//_fromState = FormState.Finished;
//Gfx.Dispose();
//Gfx = null;
//if (_pdfRenderer != null)
//{
// _pdfForm.Stream = new PdfDictionary.PdfStream(PdfEncoders.RawEncoding.GetBytes(pdfRenderer.GetContent()), this.pdfForm);
// if (_document.Options.CompressContentStreams)
// {
// _pdfForm.Stream.Value = Filtering.FlateDecode.Encode(pdfForm.Stream.Value);
// _pdfForm.Elements["/Filter"] = new PdfName("/FlateDecode");
// }
// int length = _pdfForm.Stream.Length;
// _pdfForm.Elements.SetInteger("/Length", length);
//}
}
/// <summary>
/// Frees the memory occupied by the underlying imported PDF document, even if other XPdfForm objects
/// refer to this document. A reuse of this object doesn't fail, because the underlying PDF document
/// is re-imported if necessary.
/// </summary>
// TODO: NYI: Dispose
protected override void Dispose(bool disposing)
{
if (!_disposed)
{
_disposed = true;
try
{
if (disposing)
{
//...
}
if (_externalDocument != null)
PdfDocument.Tls.DetachDocument(_externalDocument.Handle);
//...
}
finally
{
base.Dispose(disposing);
}
}
}
bool _disposed;
/// <summary>
/// Gets or sets an image that is used for drawing if the current XGraphics object cannot handle
/// PDF forms. A place holder is useful for showing a preview of a page on the display, because
/// PDFsharp cannot render native PDF objects.
/// </summary>
public XImage PlaceHolder
{
get { return _placeHolder; }
set { _placeHolder = value; }
}
XImage _placeHolder;
/// <summary>
/// Gets the underlying PdfPage (if one exists).
/// </summary>
public PdfPage Page
{
get
{
if (IsTemplate)
return null;
PdfPage page = ExternalDocument.Pages[_pageNumber - 1];
return page;
}
}
/// <summary>
/// Gets the number of pages in the PDF form.
/// </summary>
public int PageCount
{
get
{
if (IsTemplate)
return 1;
if (_pageCount == -1)
_pageCount = ExternalDocument.Pages.Count;
return _pageCount;
}
}
int _pageCount = -1;
/// <summary>
/// Gets the width in point of the page identified by the property PageNumber.
/// </summary>
[Obsolete("Use either PixelWidth or PointWidth. Temporarily obsolete because of rearrangements for WPF.")]
public override double Width
{
get
{
PdfPage page = ExternalDocument.Pages[_pageNumber - 1];
return page.Width;
}
}
/// <summary>
/// Gets the height in point of the page identified by the property PageNumber.
/// </summary>
[Obsolete("Use either PixelHeight or PointHeight. Temporarily obsolete because of rearrangements for WPF.")]
public override double Height
{
get
{
PdfPage page = ExternalDocument.Pages[_pageNumber - 1];
return page.Height;
}
}
/// <summary>
/// Gets the width in point of the page identified by the property PageNumber.
/// </summary>
public override double PointWidth
{
get
{
PdfPage page = ExternalDocument.Pages[_pageNumber - 1];
return page.Width;
}
}
/// <summary>
/// Gets the height in point of the page identified by the property PageNumber.
/// </summary>
public override double PointHeight
{
get
{
PdfPage page = ExternalDocument.Pages[_pageNumber - 1];
return page.Height;
}
}
/// <summary>
/// Gets the width in point of the page identified by the property PageNumber.
/// </summary>
public override int PixelWidth
{
get
{
//PdfPage page = ExternalDocument.Pages[_pageNumber - 1];
//return (int)page.Width;
return DoubleUtil.DoubleToInt(PointWidth);
}
}
/// <summary>
/// Gets the height in point of the page identified by the property PageNumber.
/// </summary>
public override int PixelHeight
{
get
{
//PdfPage page = ExternalDocument.Pages[_pageNumber - 1];
//return (int)page.Height;
return DoubleUtil.DoubleToInt(PointHeight);
}
}
/// <summary>
/// Get the size of the page identified by the property PageNumber.
/// </summary>
public override XSize Size
{
get
{
PdfPage page = ExternalDocument.Pages[_pageNumber - 1];
return new XSize(page.Width, page.Height);
}
}
/// <summary>
/// Gets or sets the transformation matrix.
/// </summary>
public override XMatrix Transform
{
get { return _transform; }
set
{
if (_transform != value)
{
// discard PdfFromXObject when Transform changed
_pdfForm = null;
_transform = value;
}
}
}
/// <summary>
/// Gets or sets the page number in the external PDF document this object refers to. The page number
/// is one-based, i.e. it is in the range from 1 to PageCount. The default value is 1.
/// </summary>
public int PageNumber
{
get { return _pageNumber; }
set
{
if (IsTemplate)
throw new InvalidOperationException("The page number of an XPdfForm template cannot be modified.");
if (_pageNumber != value)
{
_pageNumber = value;
// dispose PdfFromXObject when number has changed
_pdfForm = null;
}
}
}
int _pageNumber = 1;
/// <summary>
/// Gets or sets the page index in the external PDF document this object refers to. The page index
/// is zero-based, i.e. it is in the range from 0 to PageCount - 1. The default value is 0.
/// </summary>
public int PageIndex
{
get { return PageNumber - 1; }
set { PageNumber = value + 1; }
}
/// <summary>
/// Gets the underlying document from which pages are imported.
/// </summary>
internal PdfDocument ExternalDocument
{
// The problem is that you can ask an XPdfForm about the number of its pages before it was
// drawn the first time. At this moment the XPdfForm doesn't know the document where it will
// be later draw on one of its pages. To prevent the import of the same document more than
// once, all imported documents of a thread are cached. The cache is local to the current
// thread and not to the appdomain, because I won't get problems in a multi-thread environment
// that I don't understand.
get
{
if (IsTemplate)
throw new InvalidOperationException("This XPdfForm is a template and not an imported PDF page; therefore it has no external document.");
if (_externalDocument == null)
_externalDocument = PdfDocument.Tls.GetDocument(_path);
return _externalDocument;
}
}
internal PdfDocument _externalDocument;
/// <summary>
/// Extracts the page number if the path has the form 'MyFile.pdf#123' and returns
/// the actual path without the number sign and the following digits.
/// </summary>
public static string ExtractPageNumber(string path, out int pageNumber)
{
if (path == null)
throw new ArgumentNullException("path");
pageNumber = 0;
int length = path.Length;
if (length != 0)
{
length--;
if (char.IsDigit(path, length))
{
while (char.IsDigit(path, length) && length >= 0)
length--;
if (length > 0 && path[length] == '#')
{
// Must have at least one dot left of colon to distinguish from e.g. '#123'
if (path.IndexOf('.') != -1)
{
pageNumber = int.Parse(path.Substring(length + 1));
path = path.Substring(0, length);
}
}
}
}
return path;
}
}
}