diff --git a/examples/AdvancedTextExtraction.cs b/examples/AdvancedTextExtraction.cs
index ff605fb5..036ed1aa 100644
--- a/examples/AdvancedTextExtraction.cs
+++ b/examples/AdvancedTextExtraction.cs
@@ -10,7 +10,8 @@
public static class AdvancedTextExtraction
{
public static void Run(string filePath)
- {
+ {
+#if YET_TO_BE_DONE
var sb = new StringBuilder();
using (var document = PdfDocument.Open(filePath))
@@ -86,6 +87,7 @@
}
Console.WriteLine(sb.ToString());
+#endif
}
}
}
diff --git a/examples/Program.cs b/examples/Program.cs
index 5297a657..108128d6 100644
--- a/examples/Program.cs
+++ b/examples/Program.cs
@@ -45,9 +45,14 @@
},
{7,
("Advance text extraction using layout analysis algorithms",
- () => AdvancedTextExtraction.Run(Path.Combine(filesDirectory, "ICML03-081.pdf")))
- }
- };
+ () => AdvancedTextExtraction.Run(Path.Combine(filesDirectory, "ICML03-081.pdf")))
+ },
+ {
+ 8,
+ ("Extract Words with newline detection (example with algorithm). Issue 512",
+ () => OpenDocumentAndExtractWords.Run(Path.Combine(filesDirectory, "OPEN.RABBIT.ENGLISH.LOP.pdf")))
+ }
+ };
var choices = string.Join(Environment.NewLine, examples.Select(x => $"{x.Key}: {x.Value.name}"));
diff --git a/src/UglyToad.PdfPig.Fonts/CorruptCompressedDataException.cs b/src/UglyToad.PdfPig.Fonts/CorruptCompressedDataException.cs
new file mode 100644
index 00000000..9cb4b394
--- /dev/null
+++ b/src/UglyToad.PdfPig.Fonts/CorruptCompressedDataException.cs
@@ -0,0 +1,34 @@
+namespace UglyToad.PdfPig.Fonts
+{
+ using System;
+ using System.Runtime.Serialization;
+
+ ///
+ /// Thrown when a PDF contains an invalid compressed data stream.
+ ///
+ [Serializable]
+ public class CorruptCompressedDataException : Exception
+ {
+ ///
+ public CorruptCompressedDataException()
+ {
+ }
+
+ ///
+ public CorruptCompressedDataException(string message) : base(message)
+ {
+ }
+
+ ///
+ public CorruptCompressedDataException(string message, Exception inner) : base(message, inner)
+ {
+ }
+
+ ///
+ protected CorruptCompressedDataException(
+ SerializationInfo info,
+ StreamingContext context) : base(info, context)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/FICTIF_TABLE_INDEX.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/FICTIF_TABLE_INDEX.pdf
new file mode 100644
index 00000000..db5f2305
Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/FICTIF_TABLE_INDEX.pdf differ
diff --git a/src/UglyToad.PdfPig.Tests/Integration/Documents/OPEN.RABBIT.ENGLISH.LOP.pdf b/src/UglyToad.PdfPig.Tests/Integration/Documents/OPEN.RABBIT.ENGLISH.LOP.pdf
new file mode 100644
index 00000000..0ca6c9a6
Binary files /dev/null and b/src/UglyToad.PdfPig.Tests/Integration/Documents/OPEN.RABBIT.ENGLISH.LOP.pdf differ
diff --git a/src/UglyToad.PdfPig.Tests/Integration/IndexedPageSummaryFileTests.cs b/src/UglyToad.PdfPig.Tests/Integration/IndexedPageSummaryFileTests.cs
new file mode 100644
index 00000000..beeea4b8
--- /dev/null
+++ b/src/UglyToad.PdfPig.Tests/Integration/IndexedPageSummaryFileTests.cs
@@ -0,0 +1,54 @@
+namespace UglyToad.PdfPig.Tests.Integration;
+
+using System.Linq;
+using Xunit;
+
+public class IndexedPageSummaryFileTests
+{
+ private static string GetFilename()
+ {
+ return IntegrationHelpers.GetDocumentPath("FICTIF_TABLE_INDEX.pdf");
+ }
+
+ [Fact]
+ public void HasCorrectNumberOfPages()
+ {
+ using (var document = PdfDocument.Open(GetFilename()))
+ {
+ Assert.Equal(14, document.NumberOfPages);
+ }
+ }
+
+ [Fact]
+ public void GetPagesWorks()
+ {
+ using (var document = PdfDocument.Open(GetFilename()))
+ {
+ var pageCount = document.GetPages().Count();
+
+ Assert.Equal(14, pageCount);
+ }
+ }
+
+ [Theory]
+ [InlineData("M. HERNANDEZ DANIEL", 1)]
+ [InlineData("M. HERNANDEZ DANIEL", 2)]
+ [InlineData("Mme ALIBERT CHLOE AA", 3)]
+ [InlineData("Mme ALIBERT CHLOE AA", 4)]
+ [InlineData("M. SIMPSON BART AAA", 5)]
+ [InlineData("M. SIMPSON BART AAA", 6)]
+ [InlineData("M. BOND JAMES A", 7)]
+ [InlineData("M. BOND JAMES A", 8)]
+ [InlineData("M. DE BALZAC HONORE", 9)]
+ [InlineData("M. DE BALZAC HONORE", 10)]
+ [InlineData("M. STALLONE SILVESTER", 11)]
+ [InlineData("M. STALLONE SILVESTER", 12)]
+ [InlineData("M. SCOTT MICHAEL", 13)]
+ [InlineData("M. SCOTT MICHAEL", 14)]
+ public void CheckSpecificNamesPresence_InIndexedPageNumbersFile(string searchedName, int pageNumber)
+ {
+ using var document = PdfDocument.Open(GetFilename());
+ var page = document.GetPage(pageNumber);
+ Assert.Contains(searchedName, page.Text);
+ }
+}
\ No newline at end of file
diff --git a/src/UglyToad.PdfPig.Tests/Parser/Parts/FileHeaderParserTests.cs b/src/UglyToad.PdfPig.Tests/Parser/Parts/FileHeaderParserTests.cs
index 374b8396..6a50d0c6 100644
--- a/src/UglyToad.PdfPig.Tests/Parser/Parts/FileHeaderParserTests.cs
+++ b/src/UglyToad.PdfPig.Tests/Parser/Parts/FileHeaderParserTests.cs
@@ -51,7 +51,7 @@
var result = FileHeaderParser.Parse(scanner.scanner, scanner.bytes, false, log);
Assert.Equal(1.2m, result.Version);
- Assert.Equal(TestEnvironment.IsUnixPlatform ? 7 : 9, result.OffsetInFile);
+ Assert.Equal(TestEnvironment.IsSingleByteNewLine(input) ? 7 : 9, result.OffsetInFile);
}
[Fact]
@@ -66,38 +66,42 @@
[Fact]
public void HeaderPrecededByJunkNonLenientDoesNotThrow()
- {
- var scanner = StringBytesTestConverter.Scanner(@"one
- %PDF-1.2");
+ {
+ var input = @"one
+ %PDF-1.2";
+ var scanner = StringBytesTestConverter.Scanner(input);
var result = FileHeaderParser.Parse(scanner.scanner, scanner.bytes, false, log);
Assert.Equal(1.2m, result.Version);
- Assert.Equal(TestEnvironment.IsUnixPlatform ? 12 : 13, result.OffsetInFile);
+ Assert.Equal(TestEnvironment.IsSingleByteNewLine(input) ? 12 : 13, result.OffsetInFile);
}
[Fact]
public void HeaderPrecededByJunkLenientReads()
- {
- var scanner = StringBytesTestConverter.Scanner(@"one
- %PDF-1.7");
+ {
+ var input = @"one
+ %PDF-1.7";
+ var scanner = StringBytesTestConverter.Scanner(input);
var result = FileHeaderParser.Parse(scanner.scanner, scanner.bytes, true, log);
Assert.Equal(1.7m, result.Version);
- Assert.Equal(TestEnvironment.IsUnixPlatform ? 12 : 13, result.OffsetInFile);
+ Assert.Equal(TestEnvironment.IsSingleByteNewLine(input) ? 12 : 13, result.OffsetInFile);
}
[Fact]
public void HeaderPrecededByJunkDoesNotThrow()
- {
- var scanner = StringBytesTestConverter.Scanner(@"one two
-three %PDF-1.6");
+ {
+ var s = @"one two
+three %PDF-1.6";
+
+ var scanner = StringBytesTestConverter.Scanner(s);
var result = FileHeaderParser.Parse(scanner.scanner, scanner.bytes, true, log);
Assert.Equal(1.6m, result.Version);
- Assert.Equal(TestEnvironment.IsUnixPlatform ? 14 : 15, result.OffsetInFile);
+ Assert.Equal(TestEnvironment.IsSingleByteNewLine(s) ? 14 : 15, result.OffsetInFile);
}
[Fact]
diff --git a/src/UglyToad.PdfPig.Tests/TestEnvironment.cs b/src/UglyToad.PdfPig.Tests/TestEnvironment.cs
index abc44914..17545a27 100644
--- a/src/UglyToad.PdfPig.Tests/TestEnvironment.cs
+++ b/src/UglyToad.PdfPig.Tests/TestEnvironment.cs
@@ -4,6 +4,7 @@
public static class TestEnvironment
{
- public static readonly bool IsUnixPlatform = Environment.NewLine.Length == 1;
+ public static bool IsSingleByteNewLine(string s) => s.IndexOf('\r') < 0;
+
}
}
diff --git a/src/UglyToad.PdfPig/Content/Catalog.cs b/src/UglyToad.PdfPig/Content/Catalog.cs
index 61906602..9bbdd71f 100644
--- a/src/UglyToad.PdfPig/Content/Catalog.cs
+++ b/src/UglyToad.PdfPig/Content/Catalog.cs
@@ -29,7 +29,12 @@
///
/// The page tree for this document containing all pages, page numbers and their dictionaries.
///
- public PageTreeNode PageTree { get; }
+ public PageTreeNode PageTree { get; }
+
+ ///
+ /// Number of discovered pages.
+ ///
+ public int? NumberOfDiscoveredPages => pagesByNumber?.Count;
///
/// Create a new .
diff --git a/src/UglyToad.PdfPig/Content/IResourceStore.cs b/src/UglyToad.PdfPig/Content/IResourceStore.cs
index 26984a6c..5cbfa8fc 100644
--- a/src/UglyToad.PdfPig/Content/IResourceStore.cs
+++ b/src/UglyToad.PdfPig/Content/IResourceStore.cs
@@ -6,7 +6,7 @@
internal interface IResourceStore
{
- void LoadResourceDictionary(DictionaryToken resourceDictionary);
+ void LoadResourceDictionary(DictionaryToken resourceDictionary, InternalParsingOptions parsingOptions);
///
/// Remove any named resources and associated state for the last resource dictionary loaded.
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/Content/Pages.cs b/src/UglyToad.PdfPig/Content/Pages.cs
index 93f60603..3bd55c09 100644
--- a/src/UglyToad.PdfPig/Content/Pages.cs
+++ b/src/UglyToad.PdfPig/Content/Pages.cs
@@ -21,6 +21,13 @@
this.pdfScanner = pdfScanner ?? throw new ArgumentNullException(nameof(pdfScanner));
Count = catalog.PagesDictionary.GetIntOrDefault(NameToken.Count);
+ var CountOfPagesByPagesTree = catalog.PageTree.Children.Count;
+ var numberOfDiscoveredPages = catalog.NumberOfDiscoveredPages;
+ if (numberOfDiscoveredPages is null == false && Count != numberOfDiscoveredPages)
+ {
+ //log.Warning($"Dictionary Page Count {Count} different to discovered pages {numberOfDiscoveredPages}. Using {numberOfDiscoveredPages}.");
+ Count = numberOfDiscoveredPages.Value;
+ }
}
public Page GetPage(int pageNumber, InternalParsingOptions parsingOptions)
diff --git a/src/UglyToad.PdfPig/Content/ResourceStore.cs b/src/UglyToad.PdfPig/Content/ResourceStore.cs
index de743aee..fa6e146e 100644
--- a/src/UglyToad.PdfPig/Content/ResourceStore.cs
+++ b/src/UglyToad.PdfPig/Content/ResourceStore.cs
@@ -33,7 +33,7 @@
this.fontFactory = fontFactory;
}
- public void LoadResourceDictionary(DictionaryToken resourceDictionary)
+ public void LoadResourceDictionary(DictionaryToken resourceDictionary, InternalParsingOptions parsingOptions)
{
lastLoadedFont = (null, null);
@@ -43,7 +43,7 @@
{
var fontDictionary = DirectObjectFinder.Get(fontBase, scanner);
- LoadFontDictionary(fontDictionary);
+ LoadFontDictionary(fontDictionary, parsingOptions);
}
if (resourceDictionary.TryGet(NameToken.Xobject, out var xobjectBase))
@@ -132,7 +132,7 @@
currentResourceState.Pop();
}
- private void LoadFontDictionary(DictionaryToken fontDictionary)
+ private void LoadFontDictionary(DictionaryToken fontDictionary, InternalParsingOptions parsingOptions)
{
lastLoadedFont = (null, null);
@@ -157,7 +157,18 @@
continue;
}
- loadedFonts[reference] = fontFactory.Get(fontObject);
+ try
+ {
+ loadedFonts[reference] = fontFactory.Get(fontObject);
+ }
+ catch
+ {
+ if (!parsingOptions.SkipMissingFonts)
+ {
+ throw;
+ }
+ }
+
}
else if (pair.Value is DictionaryToken fd)
{
diff --git a/src/UglyToad.PdfPig/Filters/FlateFilter.cs b/src/UglyToad.PdfPig/Filters/FlateFilter.cs
index b67b2010..511080b3 100644
--- a/src/UglyToad.PdfPig/Filters/FlateFilter.cs
+++ b/src/UglyToad.PdfPig/Filters/FlateFilter.cs
@@ -1,5 +1,6 @@
namespace UglyToad.PdfPig.Filters
{
+ using Fonts;
using System;
using System.Collections.Generic;
using System.IO;
@@ -79,10 +80,17 @@
memoryStream.ReadByte();
memoryStream.ReadByte();
- using (var deflate = new DeflateStream(memoryStream, CompressionMode.Decompress))
+ try
{
- deflate.CopyTo(output);
- return output.ToArray();
+ using (var deflate = new DeflateStream(memoryStream, CompressionMode.Decompress))
+ {
+ deflate.CopyTo(output);
+ return output.ToArray();
+ }
+ }
+ catch (InvalidDataException ex)
+ {
+ throw new CorruptCompressedDataException("Invalid Flate compressed stream encountered", ex);
}
}
}
diff --git a/src/UglyToad.PdfPig/Graphics/ContentStreamProcessor.cs b/src/UglyToad.PdfPig/Graphics/ContentStreamProcessor.cs
index 8f6d5440..1e5efffd 100644
--- a/src/UglyToad.PdfPig/Graphics/ContentStreamProcessor.cs
+++ b/src/UglyToad.PdfPig/Graphics/ContentStreamProcessor.cs
@@ -479,7 +479,7 @@
var hasResources = formStream.StreamDictionary.TryGet(NameToken.Resources, pdfScanner, out var formResources);
if (hasResources)
{
- resourceStore.LoadResourceDictionary(formResources);
+ resourceStore.LoadResourceDictionary(formResources, parsingOptions);
}
// 1. Save current state.
diff --git a/src/UglyToad.PdfPig/Parser/CatalogFactory.cs b/src/UglyToad.PdfPig/Parser/CatalogFactory.cs
index da2bb2cc..26095d8a 100644
--- a/src/UglyToad.PdfPig/Parser/CatalogFactory.cs
+++ b/src/UglyToad.PdfPig/Parser/CatalogFactory.cs
@@ -81,11 +81,13 @@
pageNumber.Increment();
return new PageTreeNode(nodeDictionaryInput, referenceInput, true, pageNumber.PageCount).WithChildren(EmptyArray.Instance);
- }
-
-
-
- //If we got here, we have to iterate till we manage to exit
+ }
+
+
+
+ //If we got here, we have to iterate till we manage to exit
+
+ HashSet visitedTokens = new HashSet(); // As we visit each token add to this list (the hashcode of the indirect reference)
var toProcess =
new Queue<(PageTreeNode thisPage, IndirectReference reference, DictionaryToken nodeDictionary, IndirectReference parentReference,
@@ -102,8 +104,16 @@
do
{
- var current = toProcess.Dequeue();
-
+ var current = toProcess.Dequeue();
+ var currentReferenceHash = current.reference.GetHashCode();
+ if (visitedTokens.Contains(currentReferenceHash))
+ {
+ continue; // don't revisit token already processed. break infinite loop. Issue #512
+ }
+ else
+ {
+ visitedTokens.Add(currentReferenceHash);
+ }
if (!current.nodeDictionary.TryGet(NameToken.Kids, pdfTokenScanner, out ArrayToken kids))
{
if (!isLenientParsing)
diff --git a/src/UglyToad.PdfPig/Parser/PageFactory.cs b/src/UglyToad.PdfPig/Parser/PageFactory.cs
index 61e54289..591500b2 100644
--- a/src/UglyToad.PdfPig/Parser/PageFactory.cs
+++ b/src/UglyToad.PdfPig/Parser/PageFactory.cs
@@ -63,13 +63,13 @@
{
var resource = pageTreeMembers.ParentResources.Dequeue();
- resourceStore.LoadResourceDictionary(resource);
+ resourceStore.LoadResourceDictionary(resource, parsingOptions);
stackDepth++;
}
if (dictionary.TryGet(NameToken.Resources, pdfScanner, out DictionaryToken resources))
{
- resourceStore.LoadResourceDictionary(resources);
+ resourceStore.LoadResourceDictionary(resources, parsingOptions);
stackDepth++;
}
diff --git a/src/UglyToad.PdfPig/Parser/Parts/BruteForceSearcher.cs b/src/UglyToad.PdfPig/Parser/Parts/BruteForceSearcher.cs
index 2dda4ecc..ae91268c 100644
--- a/src/UglyToad.PdfPig/Parser/Parts/BruteForceSearcher.cs
+++ b/src/UglyToad.PdfPig/Parser/Parts/BruteForceSearcher.cs
@@ -176,7 +176,7 @@
const string searchTerm = "%%EOF";
- var minimumEndOffset = bytes.Length - searchTerm.Length;
+ var minimumEndOffset = bytes.Length - searchTerm.Length + 1; // Issue #512 - Unable to open PDF - BruteForceScan starts from earlier of two EOF marker due to min end offset off by 1
bytes.Seek(minimumEndOffset);
diff --git a/src/UglyToad.PdfPig/Writer/PdfDocumentBuilder.cs b/src/UglyToad.PdfPig/Writer/PdfDocumentBuilder.cs
index 56b102a3..a13ea14d 100644
--- a/src/UglyToad.PdfPig/Writer/PdfDocumentBuilder.cs
+++ b/src/UglyToad.PdfPig/Writer/PdfDocumentBuilder.cs
@@ -559,6 +559,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)