mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-10-14 10:55:04 +08:00
#21 support writing lines, curves and rectangles. add documentinformation to output. rename characterpath
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
[Fact]
|
||||
public void BezierCurveGeneratesCorrectBoundingBox()
|
||||
{
|
||||
var curve = new CharacterPath.BezierCurve(new PdfPoint(60, 105),
|
||||
var curve = new PdfPath.BezierCurve(new PdfPoint(60, 105),
|
||||
new PdfPoint(75, 30),
|
||||
new PdfPoint(215, 115),
|
||||
new PdfPoint(140, 160));
|
||||
@@ -28,7 +28,7 @@
|
||||
[Fact]
|
||||
public void LoopBezierCurveGeneratesCorrectBoundingBox()
|
||||
{
|
||||
var curve = new CharacterPath.BezierCurve(new PdfPoint(166, 142),
|
||||
var curve = new PdfPath.BezierCurve(new PdfPoint(166, 142),
|
||||
new PdfPoint(75, 30),
|
||||
new PdfPoint(215, 115),
|
||||
new PdfPoint(140, 160));
|
||||
@@ -47,7 +47,7 @@
|
||||
[Fact]
|
||||
public void BezierCurveAddsCorrectSvgCommand()
|
||||
{
|
||||
var curve = new CharacterPath.BezierCurve(new PdfPoint(60, 105),
|
||||
var curve = new PdfPath.BezierCurve(new PdfPoint(60, 105),
|
||||
new PdfPoint(75, 30),
|
||||
new PdfPoint(215, 115),
|
||||
new PdfPoint(140, 160));
|
||||
|
@@ -3,6 +3,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Content;
|
||||
using PdfPig.Fonts;
|
||||
using PdfPig.Geometry;
|
||||
using PdfPig.Graphics;
|
||||
using PdfPig.IO;
|
||||
using PdfPig.Tokens;
|
||||
@@ -17,9 +18,14 @@
|
||||
public TextMatrices TextMatrices { get; set; }
|
||||
= new TextMatrices();
|
||||
|
||||
public PdfPath CurrentPath { get; set; }
|
||||
|
||||
public PdfPoint CurrentPosition { get; set; }
|
||||
|
||||
public TestOperationContext()
|
||||
{
|
||||
StateStack.Push(new CurrentGraphicsState());
|
||||
CurrentPath = new PdfPath();
|
||||
}
|
||||
|
||||
public CurrentGraphicsState GetCurrentState()
|
||||
@@ -48,6 +54,18 @@
|
||||
public void ApplyXObject(StreamToken xObjectStream)
|
||||
{
|
||||
}
|
||||
|
||||
public void BeginSubpath()
|
||||
{
|
||||
}
|
||||
|
||||
public void StrokePath(bool close)
|
||||
{
|
||||
}
|
||||
|
||||
public void ClosePath()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal class TestResourceStore : IResourceStore
|
||||
|
@@ -93,6 +93,14 @@
|
||||
|
||||
var page = builder.AddPage(PageSize.A4);
|
||||
|
||||
page.DrawLine(new PdfPoint(30, 520), new PdfPoint(360, 520));
|
||||
page.DrawLine(new PdfPoint(360, 520), new PdfPoint(360, 250));
|
||||
|
||||
page.DrawLine(new PdfPoint(25, 70), new PdfPoint(100, 70), 3);
|
||||
|
||||
page.DrawRectangle(new PdfPoint(30, 100), 250, 100, 0.5m);
|
||||
page.DrawRectangle(new PdfPoint(30, 200), 250, 100, 0.5m);
|
||||
|
||||
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Fonts", "TrueType");
|
||||
var file = Path.Combine(path, "Andada-Regular.ttf");
|
||||
|
||||
@@ -139,6 +147,8 @@
|
||||
{
|
||||
var builder = new PdfDocumentBuilder();
|
||||
|
||||
builder.DocumentInformation.Title = "Hello Windows!";
|
||||
|
||||
var page = builder.AddPage(PageSize.A4);
|
||||
|
||||
var file = @"C:\Windows\Fonts\BASKVILL.TTF";
|
||||
|
@@ -18,7 +18,7 @@
|
||||
/// <summary>
|
||||
/// The current path.
|
||||
/// </summary>
|
||||
public CharacterPath Path { get; } = new CharacterPath();
|
||||
public PdfPath Path { get; } = new PdfPath();
|
||||
|
||||
/// <summary>
|
||||
/// The current location of the active point.
|
||||
|
@@ -46,7 +46,7 @@
|
||||
/// Evaluate the CharString for the character with a given name returning the path constructed for the glyph.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the character to retrieve the CharString for.</param>
|
||||
/// <returns>A <see cref="CharacterPath"/> for the glyph.</returns>
|
||||
/// <returns>A <see cref="PdfPath"/> for the glyph.</returns>
|
||||
public Type2Glyph Generate(string name)
|
||||
{
|
||||
Type2Glyph glyph;
|
||||
@@ -171,7 +171,7 @@
|
||||
/// The path of the glyph.
|
||||
/// </summary>
|
||||
[NotNull]
|
||||
public CharacterPath Path { get; }
|
||||
public PdfPath Path { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The width of the glyph as a difference from the nominal width X for the font. Optional.
|
||||
@@ -181,7 +181,7 @@
|
||||
/// <summary>
|
||||
/// Create a new <see cref="Type2Glyph"/>.
|
||||
/// </summary>
|
||||
public Type2Glyph(CharacterPath path, decimal? widthDifferenceFromNominal)
|
||||
public Type2Glyph(PdfPath path, decimal? widthDifferenceFromNominal)
|
||||
{
|
||||
Path = path ?? throw new ArgumentNullException(nameof(path));
|
||||
WidthDifferenceFromNominal = widthDifferenceFromNominal;
|
||||
|
@@ -7,7 +7,7 @@ namespace UglyToad.PdfPig.Fonts
|
||||
using System.Text;
|
||||
using Geometry;
|
||||
|
||||
internal class CharacterPath
|
||||
internal class PdfPath
|
||||
{
|
||||
private readonly List<IPathCommand> commands = new List<IPathCommand>();
|
||||
private PdfPoint? currentPosition;
|
||||
@@ -239,8 +239,8 @@ namespace UglyToad.PdfPig.Fonts
|
||||
double maxX;
|
||||
if (StartPoint.X <= EndPoint.X)
|
||||
{
|
||||
minX = (double) StartPoint.X;
|
||||
maxX = (double) EndPoint.X;
|
||||
minX = (double)StartPoint.X;
|
||||
maxX = (double)EndPoint.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -254,9 +254,9 @@ namespace UglyToad.PdfPig.Fonts
|
||||
{
|
||||
minY = (double)StartPoint.Y;
|
||||
maxY = (double)EndPoint.Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
minY = (double)EndPoint.Y;
|
||||
maxY = (double)StartPoint.Y;
|
||||
}
|
||||
@@ -294,7 +294,7 @@ namespace UglyToad.PdfPig.Fonts
|
||||
// P' = 3da(1-t)^2 + 6db(1-t)t + 3dct^2
|
||||
// P' = 3da - 3dat - 3dat + 3dat^2 + 6dbt - 6dbt^2 + 3dct^2
|
||||
// P' = (3da - 6db + 3dc)t^2 + (6db - 3da - 3da)t + 3da
|
||||
var p1 = (double)( isX ? StartPoint.X : StartPoint.Y);
|
||||
var p1 = (double)(isX ? StartPoint.X : StartPoint.Y);
|
||||
var p2 = (double)(isX ? FirstControlPoint.X : FirstControlPoint.Y);
|
||||
var p3 = (double)(isX ? SecondControlPoint.X : SecondControlPoint.Y);
|
||||
var p4 = (double)(isX ? EndPoint.X : EndPoint.Y);
|
||||
@@ -361,8 +361,8 @@ namespace UglyToad.PdfPig.Fonts
|
||||
// P = (1−t)^3*P_1 + 3(1−t)^2*t*P_2 + 3(1−t)*t^2*P_3 + t^3*P_4
|
||||
var oneMinusT = 1 - t;
|
||||
var p = ((oneMinusT * oneMinusT * oneMinusT) * p1)
|
||||
+ (3 * (oneMinusT * oneMinusT) * t * p2)
|
||||
+ (3 * oneMinusT * (t * t) * p3)
|
||||
+ (3 * (oneMinusT * oneMinusT) * t * p2)
|
||||
+ (3 * oneMinusT * (t * t) * p3)
|
||||
+ ((t * t * t) * p4);
|
||||
|
||||
return p;
|
||||
@@ -374,5 +374,9 @@ namespace UglyToad.PdfPig.Fonts
|
||||
EndPoint.X, EndPoint.Y);
|
||||
}
|
||||
}
|
||||
|
||||
public void Rectangle(decimal x, decimal y, decimal width, decimal height)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@@ -7,7 +7,7 @@
|
||||
|
||||
internal class Type1BuildCharContext
|
||||
{
|
||||
private readonly Func<int, CharacterPath> characterByIndexFactory;
|
||||
private readonly Func<int, PdfPath> characterByIndexFactory;
|
||||
public IReadOnlyDictionary<int, Type1CharStrings.CommandSequence> Subroutines { get; }
|
||||
|
||||
public decimal WidthX { get; set; }
|
||||
@@ -21,7 +21,7 @@
|
||||
public bool IsFlexing { get; set; }
|
||||
|
||||
[NotNull]
|
||||
public CharacterPath Path { get; private set; } = new CharacterPath();
|
||||
public PdfPath Path { get; private set; } = new PdfPath();
|
||||
|
||||
public PdfPoint CurrentPosition { get; set; }
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
public IReadOnlyList<PdfPoint> FlexPoints { get; }
|
||||
|
||||
public Type1BuildCharContext(IReadOnlyDictionary<int, Type1CharStrings.CommandSequence> subroutines,
|
||||
Func<int, CharacterPath> characterByIndexFactory)
|
||||
Func<int, PdfPath> characterByIndexFactory)
|
||||
{
|
||||
this.characterByIndexFactory = characterByIndexFactory ?? throw new ArgumentNullException(nameof(characterByIndexFactory));
|
||||
Subroutines = subroutines ?? throw new ArgumentNullException(nameof(subroutines));
|
||||
@@ -43,12 +43,12 @@
|
||||
|
||||
}
|
||||
|
||||
public CharacterPath GetCharacter(int characterCode)
|
||||
public PdfPath GetCharacter(int characterCode)
|
||||
{
|
||||
return characterByIndexFactory(characterCode);
|
||||
}
|
||||
|
||||
public void SetPath(CharacterPath path)
|
||||
public void SetPath(PdfPath path)
|
||||
{
|
||||
Path = path ?? throw new ArgumentNullException(nameof(path));
|
||||
}
|
||||
|
@@ -10,7 +10,7 @@
|
||||
{
|
||||
private readonly IReadOnlyDictionary<int, string> charStringIndexToName;
|
||||
private readonly object locker = new object();
|
||||
private readonly Dictionary<string, CharacterPath> glyphs = new Dictionary<string, CharacterPath>();
|
||||
private readonly Dictionary<string, PdfPath> glyphs = new Dictionary<string, PdfPath>();
|
||||
|
||||
public IReadOnlyDictionary<string, CommandSequence> CharStrings { get; }
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
Subroutines = subroutines ?? throw new ArgumentNullException(nameof(subroutines));
|
||||
}
|
||||
|
||||
public CharacterPath Generate(string name)
|
||||
public PdfPath Generate(string name)
|
||||
{
|
||||
CharacterPath glyph;
|
||||
PdfPath glyph;
|
||||
lock (locker)
|
||||
{
|
||||
if (glyphs.TryGetValue(name, out var result))
|
||||
@@ -47,7 +47,7 @@
|
||||
return glyph;
|
||||
}
|
||||
|
||||
private CharacterPath Run(CommandSequence sequence)
|
||||
private PdfPath Run(CommandSequence sequence)
|
||||
{
|
||||
var context = new Type1BuildCharContext(Subroutines, i =>
|
||||
{
|
||||
|
@@ -16,6 +16,7 @@
|
||||
|
||||
internal class ContentStreamProcessor : IOperationContext
|
||||
{
|
||||
private readonly List<PdfPath> paths = new List<PdfPath>();
|
||||
private readonly IResourceStore resourceStore;
|
||||
private readonly UserSpaceUnit userSpaceUnit;
|
||||
private readonly bool isLenientParsing;
|
||||
@@ -26,6 +27,9 @@
|
||||
|
||||
public TextMatrices TextMatrices { get; } = new TextMatrices();
|
||||
|
||||
public PdfPath CurrentPath { get; private set; }
|
||||
public PdfPoint CurrentPosition { get; set; }
|
||||
|
||||
public int StackSize => graphicsStack.Count;
|
||||
|
||||
private readonly Dictionary<XObjectType, List<XObjectContentRecord>> xObjects = new Dictionary<XObjectType, List<XObjectContentRecord>>
|
||||
@@ -73,7 +77,7 @@
|
||||
graphicsStack.Push(saved.Peek().DeepClone());
|
||||
return saved;
|
||||
}
|
||||
|
||||
|
||||
[DebuggerStepThrough]
|
||||
public CurrentGraphicsState GetCurrentState()
|
||||
{
|
||||
@@ -245,6 +249,26 @@
|
||||
}
|
||||
}
|
||||
|
||||
public void BeginSubpath()
|
||||
{
|
||||
CurrentPath = new PdfPath();
|
||||
}
|
||||
|
||||
public void StrokePath(bool close)
|
||||
{
|
||||
if (close)
|
||||
{
|
||||
ClosePath();
|
||||
}
|
||||
}
|
||||
|
||||
public void ClosePath()
|
||||
{
|
||||
CurrentPath.ClosePath();
|
||||
paths.Add(CurrentPath);
|
||||
CurrentPath = null;
|
||||
}
|
||||
|
||||
private void AdjustTextMatrix(decimal tx, decimal ty)
|
||||
{
|
||||
var matrix = TransformationMatrix.GetTranslationMatrix(tx, ty);
|
||||
|
@@ -1,11 +1,19 @@
|
||||
namespace UglyToad.PdfPig.Graphics
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Fonts;
|
||||
using Geometry;
|
||||
using IO;
|
||||
using Tokens;
|
||||
using Util.JetBrains.Annotations;
|
||||
|
||||
internal interface IOperationContext
|
||||
{
|
||||
[CanBeNull]
|
||||
PdfPath CurrentPath { get; }
|
||||
|
||||
PdfPoint CurrentPosition { get; set; }
|
||||
|
||||
CurrentGraphicsState GetCurrentState();
|
||||
|
||||
TextMatrices TextMatrices { get; }
|
||||
@@ -21,5 +29,11 @@
|
||||
void ShowPositionedText(IReadOnlyList<IToken> tokens);
|
||||
|
||||
void ApplyXObject(StreamToken xObjectStream);
|
||||
|
||||
void BeginSubpath();
|
||||
|
||||
void StrokePath(bool close);
|
||||
|
||||
void ClosePath();
|
||||
}
|
||||
}
|
@@ -17,11 +17,13 @@
|
||||
|
||||
public void Run(IOperationContext operationContext, IResourceStore resourceStore)
|
||||
{
|
||||
operationContext.StrokePath(true);
|
||||
}
|
||||
|
||||
public void Write(Stream stream)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
stream.WriteText(Symbol);
|
||||
stream.WriteNewLine();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
@@ -25,7 +25,10 @@
|
||||
|
||||
public void Write(Stream stream)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
stream.WriteDecimal(Width);
|
||||
stream.WriteWhiteSpace();
|
||||
stream.WriteText(Symbol);
|
||||
stream.WriteNewLine();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
@@ -25,11 +25,28 @@
|
||||
|
||||
public void Run(IOperationContext operationContext, IResourceStore resourceStore)
|
||||
{
|
||||
operationContext.CurrentPath.BezierCurveTo(ControlPoint1.X, ControlPoint1.Y,
|
||||
ControlPoint2.X, ControlPoint2.Y,
|
||||
End.X, End.Y);
|
||||
operationContext.CurrentPosition = End;
|
||||
}
|
||||
|
||||
public void Write(Stream stream)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
stream.WriteDecimal(ControlPoint1.X);
|
||||
stream.WriteWhiteSpace();
|
||||
stream.WriteDecimal(ControlPoint1.Y);
|
||||
stream.WriteWhiteSpace();
|
||||
stream.WriteDecimal(ControlPoint2.X);
|
||||
stream.WriteWhiteSpace();
|
||||
stream.WriteDecimal(ControlPoint2.Y);
|
||||
stream.WriteWhiteSpace();
|
||||
stream.WriteDecimal(End.X);
|
||||
stream.WriteWhiteSpace();
|
||||
stream.WriteDecimal(End.Y);
|
||||
stream.WriteWhiteSpace();
|
||||
stream.WriteText(Symbol);
|
||||
stream.WriteNewLine();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
@@ -22,11 +22,26 @@
|
||||
|
||||
public void Run(IOperationContext operationContext, IResourceStore resourceStore)
|
||||
{
|
||||
operationContext.CurrentPath.BezierCurveTo(ControlPoint1.X, ControlPoint1.Y,
|
||||
End.X,
|
||||
End.Y,
|
||||
End.X,
|
||||
End.Y);
|
||||
operationContext.CurrentPosition = End;
|
||||
}
|
||||
|
||||
public void Write(Stream stream)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
stream.WriteDecimal(ControlPoint1.X);
|
||||
stream.WriteWhiteSpace();
|
||||
stream.WriteDecimal(ControlPoint1.Y);
|
||||
stream.WriteWhiteSpace();
|
||||
stream.WriteDecimal(End.X);
|
||||
stream.WriteWhiteSpace();
|
||||
stream.WriteDecimal(End.Y);
|
||||
stream.WriteWhiteSpace();
|
||||
stream.WriteText(Symbol);
|
||||
stream.WriteNewLine();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
@@ -26,11 +26,23 @@
|
||||
|
||||
public void Run(IOperationContext operationContext, IResourceStore resourceStore)
|
||||
{
|
||||
operationContext.BeginSubpath();
|
||||
operationContext.CurrentPath.Rectangle(LowerLeft.X, LowerLeft.Y, Width, Height);
|
||||
operationContext.CurrentPath.ClosePath();
|
||||
}
|
||||
|
||||
public void Write(Stream stream)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
stream.WriteDecimal(LowerLeft.X);
|
||||
stream.WriteWhiteSpace();
|
||||
stream.WriteDecimal(LowerLeft.Y);
|
||||
stream.WriteWhiteSpace();
|
||||
stream.WriteDecimal(Width);
|
||||
stream.WriteWhiteSpace();
|
||||
stream.WriteDecimal(Height);
|
||||
stream.WriteWhiteSpace();
|
||||
stream.WriteText(Symbol);
|
||||
stream.WriteNewLine();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
@@ -22,11 +22,27 @@
|
||||
|
||||
public void Run(IOperationContext operationContext, IResourceStore resourceStore)
|
||||
{
|
||||
operationContext.CurrentPath.BezierCurveTo(operationContext.CurrentPosition.X,
|
||||
operationContext.CurrentPosition.Y,
|
||||
ControlPoint2.X,
|
||||
ControlPoint2.Y,
|
||||
End.X,
|
||||
End.Y);
|
||||
operationContext.CurrentPosition = End;
|
||||
}
|
||||
|
||||
public void Write(Stream stream)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
stream.WriteDecimal(ControlPoint2.X);
|
||||
stream.WriteWhiteSpace();
|
||||
stream.WriteDecimal(ControlPoint2.Y);
|
||||
stream.WriteWhiteSpace();
|
||||
stream.WriteDecimal(End.X);
|
||||
stream.WriteWhiteSpace();
|
||||
stream.WriteDecimal(End.Y);
|
||||
stream.WriteWhiteSpace();
|
||||
stream.WriteText(Symbol);
|
||||
stream.WriteNewLine();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
@@ -19,11 +19,18 @@
|
||||
|
||||
public void Run(IOperationContext operationContext, IResourceStore resourceStore)
|
||||
{
|
||||
operationContext.CurrentPath.LineTo(End.X, End.Y);
|
||||
operationContext.CurrentPosition = End;
|
||||
}
|
||||
|
||||
public void Write(Stream stream)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
stream.WriteDecimal(End.X);
|
||||
stream.WriteWhiteSpace();
|
||||
stream.WriteDecimal(End.Y);
|
||||
stream.WriteWhiteSpace();
|
||||
stream.WriteText(Symbol);
|
||||
stream.WriteWhiteSpace();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
@@ -19,11 +19,18 @@
|
||||
|
||||
public void Run(IOperationContext operationContext, IResourceStore resourceStore)
|
||||
{
|
||||
operationContext.BeginSubpath();
|
||||
operationContext.CurrentPosition = Point;
|
||||
}
|
||||
|
||||
public void Write(Stream stream)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
stream.WriteDecimal(Point.X);
|
||||
stream.WriteWhiteSpace();
|
||||
stream.WriteDecimal(Point.Y);
|
||||
stream.WriteWhiteSpace();
|
||||
stream.WriteText(Symbol);
|
||||
stream.WriteNewLine();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
@@ -17,11 +17,13 @@
|
||||
|
||||
public void Run(IOperationContext operationContext, IResourceStore resourceStore)
|
||||
{
|
||||
operationContext.CurrentPath.ClosePath();
|
||||
}
|
||||
|
||||
public void Write(Stream stream)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
stream.WriteText(Symbol);
|
||||
stream.WriteNewLine();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
@@ -17,11 +17,13 @@
|
||||
|
||||
public void Run(IOperationContext operationContext, IResourceStore resourceStore)
|
||||
{
|
||||
operationContext.StrokePath(false);
|
||||
}
|
||||
|
||||
public void Write(Stream stream)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
stream.WriteText(Symbol);
|
||||
stream.WriteNewLine();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
@@ -17,12 +17,15 @@
|
||||
|
||||
internal class PdfDocumentBuilder
|
||||
{
|
||||
private const byte Break = (byte) '\n';
|
||||
private const byte Break = (byte)'\n';
|
||||
private static readonly TrueTypeFontParser Parser = new TrueTypeFontParser();
|
||||
|
||||
private readonly Dictionary<int, PdfPageBuilder> pages = new Dictionary<int, PdfPageBuilder>();
|
||||
private readonly Dictionary<Guid, FontStored> fonts = new Dictionary<Guid, FontStored>();
|
||||
|
||||
public bool IncludeDocumentInformation { get; set; } = true;
|
||||
public DocumentInformationBuilder DocumentInformation { get; } = new DocumentInformationBuilder();
|
||||
|
||||
public IReadOnlyDictionary<int, PdfPageBuilder> Pages => pages;
|
||||
public IReadOnlyDictionary<Guid, IWritingFont> Fonts => fonts.ToDictionary(x => x.Key, x => x.Value.FontProgram);
|
||||
|
||||
@@ -219,7 +222,18 @@
|
||||
|
||||
var catalogRef = context.WriteObject(memory, catalog);
|
||||
|
||||
TokenWriter.WriteCrossReferenceTable(context.ObjectOffsets, catalogRef, memory);
|
||||
var informationReference = default(IndirectReference?);
|
||||
if (IncludeDocumentInformation)
|
||||
{
|
||||
var informationDictionary = DocumentInformation.ToDictionary();
|
||||
if (informationDictionary.Count > 0)
|
||||
{
|
||||
var dictionary = new DictionaryToken(informationDictionary);
|
||||
informationReference = context.WriteObject(memory, dictionary).Number;
|
||||
}
|
||||
}
|
||||
|
||||
TokenWriter.WriteCrossReferenceTable(context.ObjectOffsets, catalogRef, memory, informationReference);
|
||||
|
||||
return memory.ToArray();
|
||||
}
|
||||
@@ -257,7 +271,7 @@
|
||||
new NumericToken(rectangle.TopRight.Y)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private static void WriteString(string text, MemoryStream stream, bool appendBreak = true)
|
||||
{
|
||||
var bytes = OtherEncodings.StringAsLatin1Bytes(text);
|
||||
@@ -295,5 +309,52 @@
|
||||
Name = name ?? throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
}
|
||||
|
||||
internal class DocumentInformationBuilder
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string Author { get; set; }
|
||||
public string Subject { get; set; }
|
||||
public string Keywords { get; set; }
|
||||
public string Creator { get; set; }
|
||||
public string Producer { get; set; } = "PdfPig";
|
||||
|
||||
internal Dictionary<NameToken, IToken> ToDictionary()
|
||||
{
|
||||
var result = new Dictionary<NameToken, IToken>();
|
||||
|
||||
if (Title != null)
|
||||
{
|
||||
result[NameToken.Title] = new StringToken(Title);
|
||||
}
|
||||
|
||||
if (Author != null)
|
||||
{
|
||||
result[NameToken.Author] = new StringToken(Author);
|
||||
}
|
||||
|
||||
if (Subject != null)
|
||||
{
|
||||
result[NameToken.Subject] = new StringToken(Subject);
|
||||
}
|
||||
|
||||
if (Keywords != null)
|
||||
{
|
||||
result[NameToken.Keywords] = new StringToken(Keywords);
|
||||
}
|
||||
|
||||
if (Creator != null)
|
||||
{
|
||||
result[NameToken.Creator] = new StringToken(Creator);
|
||||
}
|
||||
|
||||
if (Producer != null)
|
||||
{
|
||||
result[NameToken.Producer] = new StringToken(Producer);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -6,6 +6,8 @@
|
||||
using Core;
|
||||
using Geometry;
|
||||
using Graphics.Operations;
|
||||
using Graphics.Operations.General;
|
||||
using Graphics.Operations.PathConstruction;
|
||||
using Graphics.Operations.TextObjects;
|
||||
using Graphics.Operations.TextPositioning;
|
||||
using Graphics.Operations.TextShowing;
|
||||
@@ -28,6 +30,39 @@
|
||||
PageNumber = number;
|
||||
}
|
||||
|
||||
public void DrawLine(PdfPoint from, PdfPoint to, decimal lineWidth = 1)
|
||||
{
|
||||
if (lineWidth != 1)
|
||||
{
|
||||
operations.Add(new SetLineWidth(lineWidth));
|
||||
}
|
||||
|
||||
operations.Add(new BeginNewSubpath(from.X, from.Y));
|
||||
operations.Add(new AppendStraightLineSegment(to.X, to.Y));
|
||||
operations.Add(StrokePath.Value);
|
||||
|
||||
if (lineWidth != 1)
|
||||
{
|
||||
operations.Add(new SetLineWidth(1));
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawRectangle(PdfPoint position, decimal width, decimal height, decimal lineWidth = 1)
|
||||
{
|
||||
if (lineWidth != 1)
|
||||
{
|
||||
operations.Add(new SetLineWidth(lineWidth));
|
||||
}
|
||||
|
||||
operations.Add(new AppendRectangle(position.X, position.Y, width, height));
|
||||
operations.Add(StrokePath.Value);
|
||||
|
||||
if (lineWidth != 1)
|
||||
{
|
||||
operations.Add(new SetLineWidth(lineWidth));
|
||||
}
|
||||
}
|
||||
|
||||
public List<Letter> AddText(string text, decimal fontSize, PdfPoint position, PdfDocumentBuilder.AddedFont font)
|
||||
{
|
||||
if (font == null)
|
||||
|
@@ -2,6 +2,7 @@
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Tokens;
|
||||
@@ -95,7 +96,8 @@
|
||||
|
||||
public static void WriteCrossReferenceTable(IReadOnlyDictionary<IndirectReference, long> objectOffsets,
|
||||
ObjectToken catalogToken,
|
||||
Stream outputStream)
|
||||
Stream outputStream,
|
||||
IndirectReference? documentInformationReference)
|
||||
{
|
||||
if (objectOffsets.Count == 0)
|
||||
{
|
||||
@@ -150,11 +152,18 @@
|
||||
outputStream.Write(Trailer, 0, Trailer.Length);
|
||||
WriteLineBreak(outputStream);
|
||||
|
||||
var trailerDictionary = new DictionaryToken(new Dictionary<NameToken, IToken>
|
||||
var trailerDictionaryData = new Dictionary<NameToken, IToken>
|
||||
{
|
||||
{NameToken.Size, new NumericToken(objectOffsets.Count) },
|
||||
{NameToken.Root, new IndirectReferenceToken(catalogToken.Number) }
|
||||
});
|
||||
{NameToken.Size, new NumericToken(objectOffsets.Count)},
|
||||
{NameToken.Root, new IndirectReferenceToken(catalogToken.Number)}
|
||||
};
|
||||
|
||||
if (documentInformationReference.HasValue)
|
||||
{
|
||||
trailerDictionaryData[NameToken.Info] = new IndirectReferenceToken(documentInformationReference.Value);
|
||||
}
|
||||
|
||||
var trailerDictionary = new DictionaryToken(trailerDictionaryData);
|
||||
|
||||
WriteDictionary(trailerDictionary, outputStream);
|
||||
WriteLineBreak(outputStream);
|
||||
|
Reference in New Issue
Block a user