570 lines
20 KiB
C#
570 lines
20 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;
|
||
|
#if GDI
|
||
|
using System.Drawing;
|
||
|
using System.Drawing.Drawing2D;
|
||
|
using System.Drawing.Imaging;
|
||
|
#endif
|
||
|
#if WPF
|
||
|
using System.Windows.Media;
|
||
|
#endif
|
||
|
using PdfSharp.Drawing.Pdf;
|
||
|
using PdfSharp.Pdf;
|
||
|
using PdfSharp.Pdf.Advanced;
|
||
|
using PdfSharp.Pdf.Filters;
|
||
|
|
||
|
namespace PdfSharp.Drawing
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Represents a graphical object that can be used to render retained graphics on it.
|
||
|
/// In GDI+ it is represented by a Metafile, in WPF by a DrawingVisual, and in PDF by a Form XObjects.
|
||
|
/// </summary>
|
||
|
public class XForm : XImage, IContentStream
|
||
|
{
|
||
|
internal enum FormState
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// The form is an imported PDF page.
|
||
|
/// </summary>
|
||
|
NotATemplate,
|
||
|
|
||
|
/// <summary>
|
||
|
/// The template is just created.
|
||
|
/// </summary>
|
||
|
Created,
|
||
|
|
||
|
/// <summary>
|
||
|
/// XGraphics.FromForm() was called.
|
||
|
/// </summary>
|
||
|
UnderConstruction,
|
||
|
|
||
|
/// <summary>
|
||
|
/// The form was drawn at least once and is 'frozen' now.
|
||
|
/// </summary>
|
||
|
Finished,
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Initializes a new instance of the <see cref="XForm"/> class.
|
||
|
/// </summary>
|
||
|
protected XForm()
|
||
|
{ }
|
||
|
|
||
|
#if GDI
|
||
|
/// <summary>
|
||
|
/// Initializes a new instance of the XForm class such that it can be drawn on the specified graphics
|
||
|
/// object.
|
||
|
/// </summary>
|
||
|
/// <param name="gfx">The graphics object that later is used to draw this form.</param>
|
||
|
/// <param name="size">The size in points of the form.</param>
|
||
|
public XForm(XGraphics gfx, XSize size)
|
||
|
{
|
||
|
if (gfx == null)
|
||
|
throw new ArgumentNullException("gfx");
|
||
|
if (size.Width < 1 || size.Height < 1)
|
||
|
throw new ArgumentNullException("size", "The size of the XPdfForm is to small.");
|
||
|
|
||
|
_formState = FormState.Created;
|
||
|
//templateSize = size;
|
||
|
_viewBox.Width = size.Width;
|
||
|
_viewBox.Height = size.Height;
|
||
|
|
||
|
// If gfx belongs to a PdfPage also create the PdfFormXObject
|
||
|
if (gfx.PdfPage != null)
|
||
|
{
|
||
|
_document = gfx.PdfPage.Owner;
|
||
|
_pdfForm = new PdfFormXObject(_document, this);
|
||
|
PdfRectangle rect = new PdfRectangle(new XPoint(), size);
|
||
|
_pdfForm.Elements.SetRectangle(PdfFormXObject.Keys.BBox, rect);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#if GDI
|
||
|
/// <summary>
|
||
|
/// Initializes a new instance of the XForm class such that it can be drawn on the specified graphics
|
||
|
/// object.
|
||
|
/// </summary>
|
||
|
/// <param name="gfx">The graphics object that later is used to draw this form.</param>
|
||
|
/// <param name="width">The width of the form.</param>
|
||
|
/// <param name="height">The height of the form.</param>
|
||
|
public XForm(XGraphics gfx, XUnit width, XUnit height)
|
||
|
: this(gfx, new XSize(width, height))
|
||
|
{ }
|
||
|
#endif
|
||
|
|
||
|
/// <summary>
|
||
|
/// Initializes a new instance of the <see cref="XForm"/> class that represents a page of a PDF document.
|
||
|
/// </summary>
|
||
|
/// <param name="document">The PDF document.</param>
|
||
|
/// <param name="viewBox">The view box of the page.</param>
|
||
|
public XForm(PdfDocument document, XRect viewBox)
|
||
|
{
|
||
|
if (viewBox.Width < 1 || viewBox.Height < 1)
|
||
|
throw new ArgumentNullException("viewBox", "The size of the XPdfForm is to small.");
|
||
|
// I must tie the XPdfForm to a document immediately, because otherwise I would have no place where
|
||
|
// to store the resources.
|
||
|
if (document == null)
|
||
|
throw new ArgumentNullException("document", "An XPdfForm template must be associated with a document at creation time.");
|
||
|
|
||
|
_formState = FormState.Created;
|
||
|
_document = document;
|
||
|
_pdfForm = new PdfFormXObject(document, this);
|
||
|
//_templateSize = size;
|
||
|
_viewBox = viewBox;
|
||
|
PdfRectangle rect = new PdfRectangle(viewBox);
|
||
|
_pdfForm.Elements.SetRectangle(PdfFormXObject.Keys.BBox, rect);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Initializes a new instance of the <see cref="XForm"/> class that represents a page of a PDF document.
|
||
|
/// </summary>
|
||
|
/// <param name="document">The PDF document.</param>
|
||
|
/// <param name="size">The size of the page.</param>
|
||
|
public XForm(PdfDocument document, XSize size)
|
||
|
: this(document, new XRect(0, 0, size.Width, size.Height))
|
||
|
{
|
||
|
////if (size.width < 1 || size.height < 1)
|
||
|
//// throw new ArgumentNullException("size", "The size of the XPdfForm is to small.");
|
||
|
////// I must tie the XPdfForm to a document immediately, because otherwise I would have no place where
|
||
|
////// to store the resources.
|
||
|
////if (document == null)
|
||
|
//// throw new ArgumentNullException("document", "An XPdfForm template must be associated with a document.");
|
||
|
|
||
|
////_formState = FormState.Created;
|
||
|
////_document = document;
|
||
|
////pdfForm = new PdfFormXObject(document, this);
|
||
|
////templateSize = size;
|
||
|
////PdfRectangle rect = new PdfRectangle(new XPoint(), size);
|
||
|
////pdfForm.Elements.SetRectangle(PdfFormXObject.Keys.BBox, rect);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Initializes a new instance of the <see cref="XForm"/> class that represents a page of a PDF document.
|
||
|
/// </summary>
|
||
|
/// <param name="document">The PDF document.</param>
|
||
|
/// <param name="width">The width of the page.</param>
|
||
|
/// <param name="height">The height of the page</param>
|
||
|
public XForm(PdfDocument document, XUnit width, XUnit height)
|
||
|
: this(document, new XRect(0, 0, width, height))
|
||
|
{ }
|
||
|
|
||
|
/// <summary>
|
||
|
/// This function should be called when drawing the content of this form is finished.
|
||
|
/// The XGraphics object used for drawing the content is disposed by this function and
|
||
|
/// cannot be used for any further drawing operations.
|
||
|
/// PDFsharp automatically calls this function when this form was used the first time
|
||
|
/// in a DrawImage function.
|
||
|
/// </summary>
|
||
|
public void DrawingFinished()
|
||
|
{
|
||
|
if (_formState == FormState.Finished)
|
||
|
return;
|
||
|
|
||
|
if (_formState == FormState.NotATemplate)
|
||
|
throw new InvalidOperationException("This object is an imported PDF page and you cannot finish drawing on it because you must not draw on it at all.");
|
||
|
|
||
|
Finish();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Called from XGraphics constructor that creates an instance that work on this form.
|
||
|
/// </summary>
|
||
|
internal void AssociateGraphics(XGraphics gfx)
|
||
|
{
|
||
|
if (_formState == FormState.NotATemplate)
|
||
|
throw new NotImplementedException("The current version of PDFsharp cannot draw on an imported page.");
|
||
|
|
||
|
if (_formState == FormState.UnderConstruction)
|
||
|
throw new InvalidOperationException("An XGraphics object already exists for this form.");
|
||
|
|
||
|
if (_formState == FormState.Finished)
|
||
|
throw new InvalidOperationException("After drawing a form it cannot be modified anymore.");
|
||
|
|
||
|
Debug.Assert(_formState == FormState.Created);
|
||
|
_formState = FormState.UnderConstruction;
|
||
|
Gfx = gfx;
|
||
|
}
|
||
|
internal XGraphics Gfx;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Disposes this instance.
|
||
|
/// </summary>
|
||
|
protected override void Dispose(bool disposing)
|
||
|
{
|
||
|
base.Dispose(disposing);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Sets the form in the state FormState.Finished.
|
||
|
/// </summary>
|
||
|
internal virtual void Finish()
|
||
|
{
|
||
|
#if GDI
|
||
|
if (_formState == FormState.NotATemplate || _formState == FormState.Finished)
|
||
|
return;
|
||
|
|
||
|
if (Gfx.Metafile != null)
|
||
|
_gdiImage = Gfx.Metafile;
|
||
|
|
||
|
Debug.Assert(_formState == FormState.Created || _formState == FormState.UnderConstruction);
|
||
|
_formState = FormState.Finished;
|
||
|
Gfx.Dispose();
|
||
|
Gfx = null;
|
||
|
|
||
|
if (PdfRenderer != null)
|
||
|
{
|
||
|
//pdfForm.CreateStream(PdfEncoders.RawEncoding.GetBytes(PdfRenderer.GetContent()));
|
||
|
PdfRenderer.Close();
|
||
|
Debug.Assert(PdfRenderer == null);
|
||
|
|
||
|
if (_document.Options.CompressContentStreams)
|
||
|
{
|
||
|
_pdfForm.Stream.Value = Filtering.FlateDecode.Encode(_pdfForm.Stream.Value, _document.Options.FlateEncodeMode);
|
||
|
_pdfForm.Elements["/Filter"] = new PdfName("/FlateDecode");
|
||
|
}
|
||
|
int length = _pdfForm.Stream.Length;
|
||
|
_pdfForm.Elements.SetInteger("/Length", length);
|
||
|
}
|
||
|
#endif
|
||
|
#if WPF
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the owning document.
|
||
|
/// </summary>
|
||
|
internal PdfDocument Owner
|
||
|
{
|
||
|
get { return _document; }
|
||
|
}
|
||
|
PdfDocument _document;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the color model used in the underlying PDF document.
|
||
|
/// </summary>
|
||
|
internal PdfColorMode ColorMode
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (_document == null)
|
||
|
return PdfColorMode.Undefined;
|
||
|
return _document.Options.ColorMode;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets a value indicating whether this instance is a template.
|
||
|
/// </summary>
|
||
|
internal bool IsTemplate
|
||
|
{
|
||
|
get { return _formState != FormState.NotATemplate; }
|
||
|
}
|
||
|
internal FormState _formState;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Get the width of the page identified by the property PageNumber.
|
||
|
/// </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 override double Width
|
||
|
{
|
||
|
//get { return templateSize.width; }
|
||
|
get { return _viewBox.Width; }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Get the width of the page identified by the property PageNumber.
|
||
|
/// </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 override double Height
|
||
|
{
|
||
|
//get { return templateSize.height; }
|
||
|
get { return _viewBox.Height; }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Get the width in point of this image.
|
||
|
/// </summary>
|
||
|
public override double PointWidth
|
||
|
{
|
||
|
//get { return templateSize.width; }
|
||
|
get { return _viewBox.Width; }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Get the height in point of this image.
|
||
|
/// </summary>
|
||
|
public override double PointHeight
|
||
|
{
|
||
|
//get { return templateSize.height; }
|
||
|
get { return _viewBox.Height; }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Get the width of the page identified by the property PageNumber.
|
||
|
/// </summary>
|
||
|
public override int PixelWidth
|
||
|
{
|
||
|
//get { return (int)templateSize.width; }
|
||
|
get { return (int)_viewBox.Width; }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Get the height of the page identified by the property PageNumber.
|
||
|
/// </summary>
|
||
|
public override int PixelHeight
|
||
|
{
|
||
|
//get { return (int)templateSize.height; }
|
||
|
get { return (int)_viewBox.Height; }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Get the size of the page identified by the property PageNumber.
|
||
|
/// </summary>
|
||
|
public override XSize Size
|
||
|
{
|
||
|
//get { return templateSize; }
|
||
|
get { return _viewBox.Size; }
|
||
|
}
|
||
|
//XSize templateSize;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the view box of the form.
|
||
|
/// </summary>
|
||
|
public XRect ViewBox
|
||
|
{
|
||
|
get { return _viewBox; }
|
||
|
}
|
||
|
XRect _viewBox;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets 72, the horizontal resolution by design of a form object.
|
||
|
/// </summary>
|
||
|
public override double HorizontalResolution
|
||
|
{
|
||
|
get { return 72; }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets 72 always, the vertical resolution by design of a form object.
|
||
|
/// </summary>
|
||
|
public override double VerticalResolution
|
||
|
{
|
||
|
get { return 72; }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets or sets the bounding box.
|
||
|
/// </summary>
|
||
|
public XRect BoundingBox
|
||
|
{
|
||
|
get { return _boundingBox; }
|
||
|
set { _boundingBox = value; } // TODO: pdfForm = null
|
||
|
}
|
||
|
XRect _boundingBox;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets or sets the transformation matrix.
|
||
|
/// </summary>
|
||
|
public virtual XMatrix Transform
|
||
|
{
|
||
|
get { return _transform; }
|
||
|
set
|
||
|
{
|
||
|
if (_formState == FormState.Finished)
|
||
|
throw new InvalidOperationException("After a XPdfForm was once drawn it must not be modified.");
|
||
|
_transform = value;
|
||
|
}
|
||
|
}
|
||
|
internal XMatrix _transform;
|
||
|
|
||
|
internal PdfResources Resources
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
Debug.Assert(IsTemplate, "This function is for form templates only.");
|
||
|
return PdfForm.Resources;
|
||
|
//if (resources == null)
|
||
|
// resources = (PdfResources)pdfForm.Elements.GetValue(PdfFormXObject.Keys.Resources, VCF.Create); // VCF.CreateIndirect
|
||
|
//return resources;
|
||
|
}
|
||
|
}
|
||
|
//PdfResources resources;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Implements the interface because the primary function is internal.
|
||
|
/// </summary>
|
||
|
PdfResources IContentStream.Resources
|
||
|
{
|
||
|
get { return Resources; }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the resource name of the specified font within this form.
|
||
|
/// </summary>
|
||
|
internal string GetFontName(XFont font, out PdfFont pdfFont)
|
||
|
{
|
||
|
Debug.Assert(IsTemplate, "This function is for form templates only.");
|
||
|
pdfFont = _document.FontTable.GetFont(font);
|
||
|
Debug.Assert(pdfFont != null);
|
||
|
string name = Resources.AddFont(pdfFont);
|
||
|
return name;
|
||
|
}
|
||
|
|
||
|
string IContentStream.GetFontName(XFont font, out PdfFont pdfFont)
|
||
|
{
|
||
|
return GetFontName(font, out pdfFont);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Tries to get the resource name of the specified font data within this form.
|
||
|
/// Returns null if no such font exists.
|
||
|
/// </summary>
|
||
|
internal string TryGetFontName(string idName, out PdfFont pdfFont)
|
||
|
{
|
||
|
Debug.Assert(IsTemplate, "This function is for form templates only.");
|
||
|
pdfFont = _document.FontTable.TryGetFont(idName);
|
||
|
string name = null;
|
||
|
if (pdfFont != null)
|
||
|
name = Resources.AddFont(pdfFont);
|
||
|
return name;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the resource name of the specified font data within this form.
|
||
|
/// </summary>
|
||
|
internal string GetFontName(string idName, byte[] fontData, out PdfFont pdfFont)
|
||
|
{
|
||
|
Debug.Assert(IsTemplate, "This function is for form templates only.");
|
||
|
pdfFont = _document.FontTable.GetFont(idName, fontData);
|
||
|
//pdfFont = new PdfType0Font(Owner, idName, fontData);
|
||
|
//pdfFont.Document = _document;
|
||
|
Debug.Assert(pdfFont != null);
|
||
|
string name = Resources.AddFont(pdfFont);
|
||
|
return name;
|
||
|
}
|
||
|
|
||
|
string IContentStream.GetFontName(string idName, byte[] fontData, out PdfFont pdfFont)
|
||
|
{
|
||
|
return GetFontName(idName, fontData, out pdfFont);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the resource name of the specified image within this form.
|
||
|
/// </summary>
|
||
|
internal string GetImageName(XImage image)
|
||
|
{
|
||
|
Debug.Assert(IsTemplate, "This function is for form templates only.");
|
||
|
PdfImage pdfImage = _document.ImageTable.GetImage(image);
|
||
|
Debug.Assert(pdfImage != null);
|
||
|
string name = Resources.AddImage(pdfImage);
|
||
|
return name;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Implements the interface because the primary function is internal.
|
||
|
/// </summary>
|
||
|
string IContentStream.GetImageName(XImage image)
|
||
|
{
|
||
|
return GetImageName(image);
|
||
|
}
|
||
|
|
||
|
internal PdfFormXObject PdfForm
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
Debug.Assert(IsTemplate, "This function is for form templates only.");
|
||
|
if (_pdfForm.Reference == null)
|
||
|
_document._irefTable.Add(_pdfForm);
|
||
|
return _pdfForm;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the resource name of the specified form within this form.
|
||
|
/// </summary>
|
||
|
internal string GetFormName(XForm form)
|
||
|
{
|
||
|
Debug.Assert(IsTemplate, "This function is for form templates only.");
|
||
|
PdfFormXObject pdfForm = _document.FormTable.GetForm(form);
|
||
|
Debug.Assert(pdfForm != null);
|
||
|
string name = Resources.AddForm(pdfForm);
|
||
|
return name;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Implements the interface because the primary function is internal.
|
||
|
/// </summary>
|
||
|
string IContentStream.GetFormName(XForm form)
|
||
|
{
|
||
|
return GetFormName(form);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// The PdfFormXObject gets invalid when PageNumber or transform changed. This is because a modification
|
||
|
/// of an XPdfForm must not change objects that are already been drawn.
|
||
|
/// </summary>
|
||
|
internal PdfFormXObject _pdfForm; // TODO: make private
|
||
|
|
||
|
internal XGraphicsPdfRenderer PdfRenderer;
|
||
|
|
||
|
#if WPF && !SILVERLIGHT
|
||
|
/// <summary>
|
||
|
/// Gets a value indicating whether this image is cmyk.
|
||
|
/// </summary>
|
||
|
/// <value><c>true</c> if this image is cmyk; otherwise, <c>false</c>.</value>
|
||
|
internal override bool IsCmyk
|
||
|
{
|
||
|
get { return false; } // not supported and not relevant
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets a value indicating whether this image is JPEG.
|
||
|
/// </summary>
|
||
|
/// <value><c>true</c> if this image is JPEG; otherwise, <c>false</c>.</value>
|
||
|
internal override bool IsJpeg
|
||
|
{
|
||
|
get { return base.IsJpeg; }// not supported and not relevant
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the JPEG memory stream (if IsJpeg returns true).
|
||
|
/// </summary>
|
||
|
/// <value>The memory.</value>
|
||
|
public override MemoryStream Memory
|
||
|
{
|
||
|
get { throw new NotImplementedException(); }
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
}
|