From 95df15996b87c28c55ea70acbef7e3b1ad188fa1 Mon Sep 17 00:00:00 2001 From: Eliot Jones Date: Fri, 9 Dec 2022 08:44:56 -0500 Subject: [PATCH] add ability to rotate page by number of degrees, make builder fluent --- .../Content/PageRotationDegrees.cs | 2 +- .../Writer/PdfDocumentBuilder.cs | 6 +- src/UglyToad.PdfPig/Writer/PdfPageBuilder.cs | 57 +++++++++++++++---- 3 files changed, 51 insertions(+), 14 deletions(-) diff --git a/src/UglyToad.PdfPig/Content/PageRotationDegrees.cs b/src/UglyToad.PdfPig/Content/PageRotationDegrees.cs index 261e9b8f..38bd219e 100644 --- a/src/UglyToad.PdfPig/Content/PageRotationDegrees.cs +++ b/src/UglyToad.PdfPig/Content/PageRotationDegrees.cs @@ -44,7 +44,7 @@ /// /// Create a . /// - /// Rotation in degrees clockwise. + /// Rotation in degrees clockwise, must be a multiple of 90. public PageRotationDegrees(int rotation) { if (rotation < 0) diff --git a/src/UglyToad.PdfPig/Writer/PdfDocumentBuilder.cs b/src/UglyToad.PdfPig/Writer/PdfDocumentBuilder.cs index 039223bf..26c110e3 100644 --- a/src/UglyToad.PdfPig/Writer/PdfDocumentBuilder.cs +++ b/src/UglyToad.PdfPig/Writer/PdfDocumentBuilder.cs @@ -3,7 +3,6 @@ namespace UglyToad.PdfPig.Writer { using System; using System.Collections.Generic; - using System.Diagnostics; using System.IO; using System.Linq; using Content; @@ -559,6 +558,11 @@ namespace UglyToad.PdfPig.Writer pageDictionary[NameToken.MediaBox] = RectangleToArray(page.Value.PageSize); } + if (page.Value.rotation.HasValue) + { + pageDictionary[NameToken.Rotate] = new NumericToken(page.Value.rotation.Value); + } + // Adobe Acrobat errors if content streams ref'd by multiple pages, turn off // dedup if on to avoid issues var prev = context.AttemptDeduplication; diff --git a/src/UglyToad.PdfPig/Writer/PdfPageBuilder.cs b/src/UglyToad.PdfPig/Writer/PdfPageBuilder.cs index 872ccb4a..7c6f4cd3 100644 --- a/src/UglyToad.PdfPig/Writer/PdfPageBuilder.cs +++ b/src/UglyToad.PdfPig/Writer/PdfPageBuilder.cs @@ -46,6 +46,8 @@ private int imageKey = 1; + internal int? rotation; + internal IReadOnlyDictionary Resources => pageDictionary.GetOrCreateDict(NameToken.Resources); /// @@ -131,7 +133,7 @@ /// The first point on the line. /// The last point on the line. /// The width of the line in user space units. - public void DrawLine(PdfPoint from, PdfPoint to, decimal lineWidth = 1) + public PdfPageBuilder DrawLine(PdfPoint from, PdfPoint to, decimal lineWidth = 1) { if (lineWidth != 1) { @@ -146,6 +148,8 @@ { currentStream.Add(new SetLineWidth(1)); } + + return this; } /// @@ -156,7 +160,7 @@ /// The height of the rectangle. /// The width of the line border of the rectangle. /// Whether to fill with the color set by . - public void DrawRectangle(PdfPoint position, decimal width, decimal height, decimal lineWidth = 1, bool fill = false) + public PdfPageBuilder DrawRectangle(PdfPoint position, decimal width, decimal height, decimal lineWidth = 1, bool fill = false) { if (lineWidth != 1) { @@ -178,6 +182,17 @@ { currentStream.Add(new SetLineWidth(lineWidth)); } + + return this; + } + + /// + /// Set the number of degrees by which the page is rotated clockwise when displayed or printed. + /// + public PdfPageBuilder SetRotation(PageRotationDegrees degrees) + { + rotation = degrees.Value; + return this; } /// @@ -188,7 +203,7 @@ /// Position of the third corner of the triangle. /// The width of the line border of the triangle. /// Whether to fill with the color set by . - public void DrawTriangle(PdfPoint point1, PdfPoint point2, PdfPoint point3, decimal lineWidth = 1, bool fill = false) + public PdfPageBuilder DrawTriangle(PdfPoint point1, PdfPoint point2, PdfPoint point3, decimal lineWidth = 1, bool fill = false) { if (lineWidth != 1) { @@ -213,6 +228,8 @@ { currentStream.Add(new SetLineWidth(lineWidth)); } + + return this; } /// @@ -222,9 +239,11 @@ /// The diameter of the circle. /// The width of the line border of the circle. /// Whether to fill with the color set by . - public void DrawCircle(PdfPoint center, decimal diameter, decimal lineWidth = 1, bool fill = false) + public PdfPageBuilder DrawCircle(PdfPoint center, decimal diameter, decimal lineWidth = 1, bool fill = false) { DrawEllipsis(center, diameter, diameter, lineWidth, fill); + + return this; } /// @@ -235,7 +254,7 @@ /// The height of the ellipsis. /// The width of the line border of the ellipsis. /// Whether to fill with the color set by . - public void DrawEllipsis(PdfPoint center, decimal width, decimal height, decimal lineWidth = 1, bool fill = false) + public PdfPageBuilder DrawEllipsis(PdfPoint center, decimal width, decimal height, decimal lineWidth = 1, bool fill = false) { width /= 2; height /= 2; @@ -283,6 +302,8 @@ { currentStream.Add(new SetLineWidth(lineWidth)); } + + return this; } /// @@ -291,10 +312,12 @@ /// Red - 0 to 255 /// Green - 0 to 255 /// Blue - 0 to 255 - public void SetStrokeColor(byte r, byte g, byte b) + public PdfPageBuilder SetStrokeColor(byte r, byte g, byte b) { currentStream.Add(Push.Value); currentStream.Add(new SetStrokeColorDeviceRgb(RgbToDecimal(r), RgbToDecimal(g), RgbToDecimal(b))); + + return this; } /// @@ -303,11 +326,13 @@ /// Red - 0 to 1 /// Green - 0 to 1 /// Blue - 0 to 1 - internal void SetStrokeColorExact(decimal r, decimal g, decimal b) + internal PdfPageBuilder SetStrokeColorExact(decimal r, decimal g, decimal b) { currentStream.Add(Push.Value); currentStream.Add(new SetStrokeColorDeviceRgb(CheckRgbDecimal(r, nameof(r)), CheckRgbDecimal(g, nameof(g)), CheckRgbDecimal(b, nameof(b)))); + + return this; } /// @@ -316,18 +341,22 @@ /// Red - 0 to 255 /// Green - 0 to 255 /// Blue - 0 to 255 - public void SetTextAndFillColor(byte r, byte g, byte b) + public PdfPageBuilder SetTextAndFillColor(byte r, byte g, byte b) { currentStream.Add(Push.Value); currentStream.Add(new SetNonStrokeColorDeviceRgb(RgbToDecimal(r), RgbToDecimal(g), RgbToDecimal(b))); + + return this; } /// /// Restores the stroke, text and fill color to default (black). /// - public void ResetColor() + public PdfPageBuilder ResetColor() { currentStream.Add(Pop.Value); + + return this; } /// @@ -451,9 +480,11 @@ /// To insert invisible text, for example output of OCR, use TextRenderingMode.Neither. /// /// Text rendering mode to set. - public void SetTextRenderingMode(TextRenderingMode mode) + public PdfPageBuilder SetTextRenderingMode(TextRenderingMode mode) { currentStream.Add(new SetTextRenderingMode(mode)); + + return this; } private NameToken GetAddedFont(PdfDocumentBuilder.AddedFont font) @@ -690,7 +721,7 @@ /// Copy a page from unknown source to this page /// /// Page to be copied - public void CopyFrom(Page srcPage) + public PdfPageBuilder CopyFrom(Page srcPage) { if (currentStream.Operations.Count > 0) { @@ -704,7 +735,7 @@ // If the page doesn't have resources, then we copy the entire content stream, since not operation would collide // with the ones already written destinationStream.Operations.AddRange(srcPage.Operations); - return; + return this; } // TODO: How should we handle any other token in the page dictionary (Eg. LastModified, MediaBox, CropBox, BleedBox, TrimBox, ArtBox, @@ -828,6 +859,8 @@ } destinationStream.Operations.AddRange(operations); + + return this; } private List DrawLetters(NameToken name, string text, IWritingFont font, TransformationMatrix fontMatrix, decimal fontSize, TransformationMatrix textMatrix)