From 6de55e9dcb7bfdc38c78612705463a42a2439510 Mon Sep 17 00:00:00 2001 From: Gerard Gunnewijk Date: Fri, 20 Jan 2023 18:18:01 +0100 Subject: [PATCH 01/11] Created a object stream to use, and refactored writing objects to stream --- .editorconfig | 49 +- .../DocumentInformation.cs | 86 --- .../Extensions/PdfPageExtensions.cs | 3 +- src/Synercoding.FileFormats.Pdf/Image.cs | 39 +- .../LowLevel/Catalog.cs | 39 - .../LowLevel/ContentStream.cs | 189 ++--- .../Extensions/ContentStreamExtensions.cs | 63 ++ .../Extensions/PdfStreamArrayExtensions.cs | 714 +----------------- .../Extensions/PdfStreamExtensions.cs | 214 ++++-- .../LowLevel/FontSubType.cs | 7 - .../LowLevel/Graphics/GraphicState.cs | 1 + .../LowLevel/Internal/Catalog.cs | 16 + .../LowLevel/Internal/DocumentResources.cs | 10 + .../LowLevel/{ => Internal}/IdGenerator.cs | 2 +- .../LowLevel/{ => Internal}/PageResources.cs | 9 +- .../LowLevel/Internal/PageTree.cs | 26 + .../Internal}/ShapeContext.cs | 4 +- .../LowLevel/ObjectStream.cs | 269 +++++++ .../LowLevel/ObjectType.cs | 11 - .../LowLevel/PageTree.cs | 50 -- .../LowLevel/PdfDictionary.cs | 261 ++----- .../LowLevel/PdfName.cs | 1 - .../LowLevel/PdfReference.cs | 2 +- .../LowLevel/XObjectSubType.cs | 7 - .../LowLevel/XRef/TableBuilder.cs | 9 +- src/Synercoding.FileFormats.Pdf/PdfPage.cs | 107 +-- src/Synercoding.FileFormats.Pdf/PdfWriter.cs | 93 ++- 27 files changed, 797 insertions(+), 1484 deletions(-) delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Catalog.cs create mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/ContentStreamExtensions.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/FontSubType.cs create mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Internal/Catalog.cs create mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Internal/DocumentResources.cs rename src/Synercoding.FileFormats.Pdf/LowLevel/{ => Internal}/IdGenerator.cs (85%) rename src/Synercoding.FileFormats.Pdf/LowLevel/{ => Internal}/PageResources.cs (85%) create mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageTree.cs rename src/Synercoding.FileFormats.Pdf/{Internals => LowLevel/Internal}/ShapeContext.cs (93%) create mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/ObjectStream.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/ObjectType.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/PageTree.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/XObjectSubType.cs diff --git a/.editorconfig b/.editorconfig index d78180e..32b43bd 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,7 +13,8 @@ charset = utf-8 end_of_line = crlf indent_style = space indent_size = 4 -insert_final_newline = false +tab_width = 4 +insert_final_newline = true trim_trailing_whitespace = true ######################### @@ -43,7 +44,6 @@ trim_trailing_whitespace = false # Web Files [*.{htm,html,js,ts,tsx,css,sass,scss,less,svg,vue}] indent_size = 2 -insert_final_newline = true ########################### # C# code style settings @@ -72,6 +72,9 @@ csharp_style_expression_bodied_local_functions = false:silent # https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#pattern-matching csharp_style_pattern_matching_over_is_with_cast_check = true:warning csharp_style_pattern_matching_over_as_with_null_check = true:warning +csharp_style_prefer_pattern_matching = true:silent +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_extended_property_pattern = true:suggestion # Inlined variable declarations # https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#inlined-variable-declarations @@ -104,6 +107,20 @@ csharp_style_prefer_range_operator = true:suggestion # https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#miscellaneous-preferences csharp_style_deconstructed_variable_declaration = true:warning csharp_style_pattern_local_over_anonymous_function = true:none +csharp_prefer_simple_using_statement = false:suggestion +csharp_prefer_static_local_function = true:suggestion +csharp_using_directive_placement = outside_namespace:silent +csharp_style_namespace_declarations = file_scoped:suggestion +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = false:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent +csharp_style_prefer_parameter_null_checking = true:suggestion +csharp_style_prefer_switch_expression = true:suggestion ########################### # .NET code style settings @@ -147,8 +164,8 @@ dotnet_style_prefer_inferred_tuple_names = true:warning dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion dotnet_style_prefer_auto_properties = true:warning dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning -dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion -dotnet_style_prefer_conditional_expression_over_return = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent dotnet_style_prefer_compound_assignment = true:suggestion # Null-checking preferences @@ -156,6 +173,14 @@ dotnet_style_prefer_compound_assignment = true:suggestion dotnet_style_coalesce_expression = true:warning dotnet_style_null_propagation = true:warning +# Miscellaneous preferences +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_allow_multiple_blank_lines_experimental = true:suggestion +dotnet_style_allow_statement_immediately_after_block_experimental = true:silent + ############################# # .NET code quality settings # https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#net-code-quality-settings @@ -249,8 +274,13 @@ dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = static, readonly +# public_static_readonly_fields - Define static and readonly fields +dotnet_naming_symbols.public_static_readonly_fields.applicable_accessibilities = public +dotnet_naming_symbols.public_static_readonly_fields.applicable_kinds = field +dotnet_naming_symbols.public_static_readonly_fields.required_modifiers = static, readonly + # non_private_static_readonly_fields - Define static and readonly fields -dotnet_naming_symbols.non_private_static_readonly_fields.applicable_accessibilities = public, internal, protected +dotnet_naming_symbols.non_private_static_readonly_fields.applicable_accessibilities = internal, protected dotnet_naming_symbols.non_private_static_readonly_fields.applicable_kinds = field dotnet_naming_symbols.non_private_static_readonly_fields.required_modifiers = static, readonly @@ -317,7 +347,12 @@ dotnet_naming_rule.non_private_static_readonly_fields_must_be_pascal_case.severi dotnet_naming_rule.non_private_static_readonly_fields_must_be_pascal_case.symbols = non_private_static_readonly_fields dotnet_naming_rule.non_private_static_readonly_fields_must_be_pascal_case.style = pascal_case -# Public, internal and protected readonly fields must be PascalCase +# Public static readonly fields must be ALL_UPPER_CASE +dotnet_naming_rule.public_static_readonly_fields_must_be_all_upper.severity = warning +dotnet_naming_rule.public_static_readonly_fields_must_be_all_upper.symbols = public_static_readonly_fields +dotnet_naming_rule.public_static_readonly_fields_must_be_all_upper.style = all_upper_with_underscore + +# Internal and protected readonly fields must be PascalCase dotnet_naming_rule.non_private_readonly_fields_must_be_pascal_case.severity = warning dotnet_naming_rule.non_private_readonly_fields_must_be_pascal_case.symbols = non_private_readonly_fields dotnet_naming_rule.non_private_readonly_fields_must_be_pascal_case.style = pascal_case @@ -365,4 +400,4 @@ dotnet_naming_rule.non_interface_types_must_be_pascal_case.style = pascal_case # Interfaces must be PascalCase and start with an 'I' dotnet_naming_rule.interface_types_must_be_prefixed_with_i.severity = warning dotnet_naming_rule.interface_types_must_be_prefixed_with_i.symbols = interface_types -dotnet_naming_rule.interface_types_must_be_prefixed_with_i.style = prefix_interface_interface_with_i \ No newline at end of file +dotnet_naming_rule.interface_types_must_be_prefixed_with_i.style = prefix_interface_interface_with_i diff --git a/src/Synercoding.FileFormats.Pdf/DocumentInformation.cs b/src/Synercoding.FileFormats.Pdf/DocumentInformation.cs index 8e166c4..2f06ae4 100644 --- a/src/Synercoding.FileFormats.Pdf/DocumentInformation.cs +++ b/src/Synercoding.FileFormats.Pdf/DocumentInformation.cs @@ -1,5 +1,4 @@ using Synercoding.FileFormats.Pdf.LowLevel; -using Synercoding.FileFormats.Pdf.LowLevel.Extensions; using System; using System.Collections.Generic; @@ -10,8 +9,6 @@ namespace Synercoding.FileFormats.Pdf /// public class DocumentInformation : IPdfObject { - private bool _isWritten; - internal DocumentInformation(PdfReference id) { Reference = id; @@ -64,88 +61,5 @@ internal DocumentInformation(PdfReference id) /// Extra information that will be added to the PDF meta data /// public IDictionary ExtraInfo { get; } = new Dictionary(); - - internal uint WriteToStream(PdfStream stream) - { - if (_isWritten) - throw new InvalidOperationException("Object is already written to stream."); - - var position = (uint)stream.Position; - - stream.IndirectDictionary(this, static (did, dictionary) => - { - if (!string.IsNullOrWhiteSpace(did.Title)) - dictionary.Write(PdfName.Get("Title"), _toPdfHexadecimalString(did.Title!)); - if (!string.IsNullOrWhiteSpace(did.Author)) - dictionary.Write(PdfName.Get("Author"), _toPdfHexadecimalString(did.Author!)); - if (!string.IsNullOrWhiteSpace(did.Subject)) - dictionary.Write(PdfName.Get("Subject"), _toPdfHexadecimalString(did.Subject!)); - if (!string.IsNullOrWhiteSpace(did.Keywords)) - dictionary.Write(PdfName.Get("Keywords"), _toPdfHexadecimalString(did.Keywords!)); - if (!string.IsNullOrWhiteSpace(did.Creator)) - dictionary.Write(PdfName.Get("Creator"), _toPdfHexadecimalString(did.Creator!)); - if (!string.IsNullOrWhiteSpace(did.Producer)) - dictionary.Write(PdfName.Get("Producer"), _toPdfHexadecimalString(did.Producer!)); - if (did.CreationDate != null) - dictionary.Write(PdfName.Get("CreationDate"), _toPdfDate(did.CreationDate.Value)); - if (did.ModDate != null) - dictionary.Write(PdfName.Get("ModDate"), _toPdfDate(did.ModDate.Value)); - - if(did.ExtraInfo.Count != 0) - foreach(var kv in did.ExtraInfo) - dictionary.Write(PdfName.Get(kv.Key), _toPdfHexadecimalString(kv.Value)); - }); - - _isWritten = true; - - return position; - } - - private static string _toPdfHexadecimalString(string input) - { - var bytes = System.Text.Encoding.ASCII.GetBytes(input); - var builder = new System.Text.StringBuilder((bytes.Length * 2) + 2); - builder.Append('<'); - foreach (var b in bytes) - { - builder.Append(b.ToString("X2")); - } - builder.Append('>'); - return builder.ToString(); - } - - private static string _toPdfDate(DateTimeOffset input) - { - var datePart = input.ToString("yyyyMMddHHmmss"); - - var builder = new System.Text.StringBuilder(22); - builder.Append("(D:"); - builder.Append(datePart); - - var hours = input.Offset.Hours; - var minutes = input.Offset.Minutes; - - if (hours == 0 && minutes == 0) - { - builder.Append("Z00'00"); - } - else - { - if (hours > 0 || (hours == 0 && minutes > 0)) - { - builder.Append('+'); - } - else - { - builder.Append('-'); - } - builder.Append(Math.Abs(hours).ToString().PadLeft(2, '0')); - builder.Append('\''); - builder.Append(minutes.ToString().PadLeft(2, '0')); - } - builder.Append(')'); - - return builder.ToString(); - } } } diff --git a/src/Synercoding.FileFormats.Pdf/Extensions/PdfPageExtensions.cs b/src/Synercoding.FileFormats.Pdf/Extensions/PdfPageExtensions.cs index e24e524..9e1667c 100644 --- a/src/Synercoding.FileFormats.Pdf/Extensions/PdfPageExtensions.cs +++ b/src/Synercoding.FileFormats.Pdf/Extensions/PdfPageExtensions.cs @@ -1,6 +1,7 @@ -using Synercoding.FileFormats.Pdf.Internals; +using Synercoding.FileFormats.Pdf.LowLevel.Extensions; using Synercoding.FileFormats.Pdf.LowLevel.Graphics; using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors; +using Synercoding.FileFormats.Pdf.LowLevel.Internal; using Synercoding.FileFormats.Pdf.LowLevel.Operators.State; using Synercoding.FileFormats.Pdf.LowLevel.Text; using Synercoding.Primitives; diff --git a/src/Synercoding.FileFormats.Pdf/Image.cs b/src/Synercoding.FileFormats.Pdf/Image.cs index a48ca20..71759f1 100644 --- a/src/Synercoding.FileFormats.Pdf/Image.cs +++ b/src/Synercoding.FileFormats.Pdf/Image.cs @@ -11,9 +11,7 @@ namespace Synercoding.FileFormats.Pdf /// public sealed class Image : IPdfObject, IDisposable { - private readonly Stream _imageStream; private bool _disposed; - private bool _isWritten; internal Image(PdfReference id, SixLabors.ImageSharp.Image image) { @@ -28,7 +26,7 @@ internal Image(PdfReference id, SixLabors.ImageSharp.Image image) Width = image.Width; Height = image.Height; ms.Position = 0; - _imageStream = ms; + RawStream = ms; } internal Image(PdfReference id, Stream jpgStream, int width, int height) @@ -37,9 +35,11 @@ internal Image(PdfReference id, Stream jpgStream, int width, int height) Width = width; Height = height; - _imageStream = jpgStream; + RawStream = jpgStream; } + internal Stream RawStream { get; private set; } + /// public PdfReference Reference { get; private set; } @@ -58,38 +58,9 @@ public void Dispose() { if (!_disposed) { - _imageStream.Dispose(); + RawStream.Dispose(); _disposed = true; } } - - internal bool TryWriteToStream(PdfStream stream, out uint position) - { - position = 0; - - if (_isWritten) - return false; - if (_disposed) - throw new ObjectDisposedException(nameof(_imageStream), "Internal image is already disposed"); - - position = (uint)stream.Position; - - stream.IndirectStream(this, _imageStream, this, static (image, dictionary) => - { - dictionary - .Type(ObjectType.XObject) - .SubType(XObjectSubType.Image) - .Write(PdfName.Get("Width"), image.Width) - .Write(PdfName.Get("Height"), image.Height) - .Write(PdfName.Get("ColorSpace"), PdfName.Get("DeviceRGB")) - .Write(PdfName.Get("BitsPerComponent"), 8) - .Write(PdfName.Get("Decode"), 0f, 1f, 0f, 1f, 0f, 1f); - }, - StreamFilter.DCTDecode); - - _isWritten = true; - - return true; - } } } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Catalog.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Catalog.cs deleted file mode 100644 index eb35d05..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Catalog.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Synercoding.FileFormats.Pdf.LowLevel.Extensions; -using System; - -namespace Synercoding.FileFormats.Pdf.LowLevel -{ - internal class Catalog : IPdfObject - { - private bool _isWritten; - - public Catalog(PdfReference id, PageTree pageTree) - { - Reference = id; - PageTree = pageTree; - } - - /// - public PdfReference Reference { get; } - - public PageTree PageTree { get; } - - internal uint WriteToStream(PdfStream stream) - { - if (_isWritten) - throw new InvalidOperationException("Object is already written to stream."); - - var position = (uint)stream.Position; - - stream.IndirectDictionary(this, static (catalog, dictionary) => - { - dictionary - .Type(ObjectType.Catalog) - .Write(PdfName.Get("Pages"), catalog.PageTree.Reference); - }); - _isWritten = true; - - return position; - } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/ContentStream.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/ContentStream.cs index 17f295f..0c1a4c5 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/ContentStream.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/ContentStream.cs @@ -1,6 +1,7 @@ using SixLabors.ImageSharp; using Synercoding.FileFormats.Pdf.LowLevel.Extensions; using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors; +using Synercoding.FileFormats.Pdf.LowLevel.Internal; using Synercoding.FileFormats.Pdf.LowLevel.Operators.Color; using Synercoding.FileFormats.Pdf.LowLevel.Operators.Pathing.Construction; using Synercoding.FileFormats.Pdf.LowLevel.Operators.Pathing.Painting; @@ -9,10 +10,7 @@ using Synercoding.Primitives; using Synercoding.Primitives.Extensions; using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; using Color = Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors.Color; using Point = Synercoding.Primitives.Point; @@ -24,26 +22,26 @@ namespace Synercoding.FileFormats.Pdf.LowLevel public sealed class ContentStream : IPdfObject, IDisposable { private const string UNKNOWN_FILL_RULE = "Unknown fill rule"; - private readonly PdfStream _streamWrapper; internal ContentStream(PdfReference id, PageResources pageResources) { Resources = pageResources; - _streamWrapper = new PdfStream(new MemoryStream()); + InnerStream = new PdfStream(new MemoryStream()); Reference = id; } + + internal PdfStream InnerStream { get; private set; } + internal PageResources Resources { get; } /// public PdfReference Reference { get; } - internal bool IsWritten { get; private set; } - /// public void Dispose() { - _streamWrapper.Dispose(); + InnerStream.Dispose(); } /// @@ -52,7 +50,7 @@ public void Dispose() /// The to support chaining operations. public ContentStream SaveState() { - _streamWrapper.Write('q').NewLine(); + InnerStream.Write('q').NewLine(); return this; } @@ -63,7 +61,7 @@ public ContentStream SaveState() /// The to support chaining operations. public ContentStream RestoreState() { - _streamWrapper.Write('Q').NewLine(); + InnerStream.Write('Q').NewLine(); return this; } @@ -75,7 +73,7 @@ public ContentStream RestoreState() /// The to support chaining operations. public ContentStream CTM(Matrix matrix) { - _streamWrapper + InnerStream .Write(matrix.A) .Space() .Write(matrix.B) @@ -100,7 +98,7 @@ public ContentStream CTM(Matrix matrix) /// The to support chaining operations. public ContentStream BeginText() { - _streamWrapper + InnerStream .Write("BT") .NewLine(); @@ -115,7 +113,7 @@ public ContentStream BeginText() /// The to support chaining operations. public ContentStream SetFontAndSize(PdfName font, float size) { - _streamWrapper + InnerStream .Write(font) .Space() .Write(size) @@ -133,7 +131,7 @@ public ContentStream SetFontAndSize(PdfName font, float size) /// The to support chaining operations. public ContentStream SetTextLeading(float leading) { - _streamWrapper + InnerStream .Write(leading) .Space() .Write("TL") @@ -149,7 +147,7 @@ public ContentStream SetTextLeading(float leading) /// The to support chaining operations. public ContentStream SetCharacterSpacing(float characterSpace) { - _streamWrapper + InnerStream .Write(characterSpace) .Space() .Write("Tc") @@ -165,7 +163,7 @@ public ContentStream SetCharacterSpacing(float characterSpace) /// The to support chaining operations. public ContentStream SetHorizontalScaling(float horizontalScaling) { - _streamWrapper + InnerStream .Write(horizontalScaling) .Space() .Write("Tz") @@ -181,7 +179,7 @@ public ContentStream SetHorizontalScaling(float horizontalScaling) /// The to support chaining operations. public ContentStream SetTextRise(float textRise) { - _streamWrapper + InnerStream .Write(textRise) .Space() .Write("Ts") @@ -197,7 +195,7 @@ public ContentStream SetTextRise(float textRise) /// The to support chaining operations. public ContentStream SetTextRenderMode(TextRenderingMode textRenderingMode) { - _streamWrapper + InnerStream .Write((int)textRenderingMode) .Space() .Write("Tr") @@ -213,7 +211,7 @@ public ContentStream SetTextRenderMode(TextRenderingMode textRenderingMode) /// The to support chaining operations. public ContentStream SetWordSpacing(float wordSpace) { - _streamWrapper + InnerStream .Write(wordSpace) .Space() .Write("Tw") @@ -228,69 +226,33 @@ public ContentStream SetWordSpacing(float wordSpace) /// The to support chaining operations. public ContentStream EndText() { - _streamWrapper + InnerStream .Write("ET") .NewLine(); return this; } - /// - /// Write one or more show text operators to the content stream - /// - /// The text to write - /// The to support chaining operations. - public ContentStream ShowText(string text) + public ContentStream ShowTextTj(string line) { - var lines = _splitOnNewLines(text).ToArray(); + InnerStream + .WriteStringLiteral(line) + .Space() + .Write("Tj") + .NewLine(); - for (int i = 0; i < lines.Length; i++) - { - if (i == 0) - { - _streamWrapper - .WriteStringLiteral(lines[i]) - .Space() - .Write("Tj") - .NewLine(); - } - else - { - _streamWrapper - .WriteStringLiteral(lines[i]) - .Space() - .Write("'") - .NewLine(); - } - } - - return this; - } - - private static IEnumerable _splitOnNewLines(string text) - { - var builder = new StringBuilder(text.Length); - for (int i = 0; i < text.Length; i++) - { - var c = text[i]; - var n = i < text.Length - 1 - ? text[i + 1] - : '0'; - if (( c == '\r' && n == '\n' ) || c == '\r' || c == '\n') - { - yield return builder.ToString(); - builder.Clear(); + return this; + } - if (n == '\n') - i++; // extra skip to also skip the \n - } - else - { - builder.Append(c); - } - } + public ContentStream MoveNextLineShowText(string line) + { + InnerStream + .WriteStringLiteral(line) + .Space() + .Write("'") + .NewLine(); - yield return builder.ToString(); + return this; } /// @@ -300,7 +262,7 @@ private static IEnumerable _splitOnNewLines(string text) /// The to support chaining operations. public ContentStream SetTextPosition(Point position) { - _streamWrapper + InnerStream .Write(position.X.AsRaw(Unit.Points)) .Space() .Write(position.Y.AsRaw(Unit.Points)) @@ -318,7 +280,7 @@ public ContentStream SetTextPosition(Point position) /// The to support chaining operations. public ContentStream Tm(Matrix matrix) { - _streamWrapper + InnerStream .Write(matrix.A) .Space() .Write(matrix.B) @@ -344,7 +306,7 @@ public ContentStream Tm(Matrix matrix) /// The to support chaining operations. public ContentStream Paint(PdfName resource) { - _streamWrapper.Write(resource).Space().Write("Do").NewLine(); + InnerStream.Write(resource).Space().Write("Do").NewLine(); return this; } @@ -356,7 +318,7 @@ public ContentStream Paint(PdfName resource) /// The to support chaining operations. public ContentStream Write(MoveOperator op) { - _streamWrapper + InnerStream .Write(op.X).Space().Write(op.Y).Space() .Write('m').NewLine(); @@ -370,7 +332,7 @@ public ContentStream Write(MoveOperator op) /// The to support chaining operations. public ContentStream Write(LineOperator op) { - _streamWrapper + InnerStream .Write(op.X).Space().Write(op.Y).Space() .Write('l').NewLine(); @@ -384,7 +346,7 @@ public ContentStream Write(LineOperator op) /// The to support chaining operations. public ContentStream Write(CubicBezierCurveDualControlPointsOperator op) { - _streamWrapper + InnerStream .Write(op.X1).Space().Write(op.Y1).Space() .Write(op.X2).Space().Write(op.Y2).Space() .Write(op.X3).Space().Write(op.Y3).Space() @@ -400,7 +362,7 @@ public ContentStream Write(CubicBezierCurveDualControlPointsOperator op) /// The to support chaining operations. public ContentStream Write(CubicBezierCurveInitialControlPointsOperator op) { - _streamWrapper + InnerStream .Write(op.X2).Space().Write(op.Y2).Space() .Write(op.X3).Space().Write(op.Y3).Space() .Write('v').NewLine(); @@ -415,7 +377,7 @@ public ContentStream Write(CubicBezierCurveInitialControlPointsOperator op) /// The to support chaining operations. public ContentStream Write(CubicBezierCurveFinalControlPointsOperator op) { - _streamWrapper + InnerStream .Write(op.X1).Space().Write(op.Y1).Space() .Write(op.X3).Space().Write(op.Y3).Space() .Write('y').NewLine(); @@ -430,7 +392,7 @@ public ContentStream Write(CubicBezierCurveFinalControlPointsOperator op) /// The to support chaining operations. public ContentStream Write(RectangleOperator op) { - _streamWrapper + InnerStream .Write(op.X).Space().Write(op.Y).Space() .Write(op.Width).Space().Write(op.Height).Space() .Write("re").NewLine(); @@ -447,7 +409,7 @@ public ContentStream Write(CloseOperator op) { _ = op; - _streamWrapper + InnerStream .Write('h').NewLine(); return this; @@ -462,7 +424,7 @@ public ContentStream Write(StrokeOperator op) { _ = op; - _streamWrapper + InnerStream .Write('S').NewLine(); return this; @@ -477,7 +439,7 @@ public ContentStream Write(CloseAndStrokeOperator op) { _ = op; - _streamWrapper + InnerStream .Write('s').NewLine(); return this; @@ -492,8 +454,8 @@ public ContentStream Write(FillOperator op) { _ = op.FillRule switch { - FillRule.NonZeroWindingNumber => _streamWrapper.Write('f').NewLine(), - FillRule.EvenOdd => _streamWrapper.Write('f').Write('*').NewLine(), + FillRule.NonZeroWindingNumber => InnerStream.Write('f').NewLine(), + FillRule.EvenOdd => InnerStream.Write('f').Write('*').NewLine(), _ => throw new InvalidOperationException(UNKNOWN_FILL_RULE) }; @@ -509,8 +471,8 @@ public ContentStream Write(FillAndStrokeOperator op) { _ = op.FillRule switch { - FillRule.NonZeroWindingNumber => _streamWrapper.Write('B').NewLine(), - FillRule.EvenOdd => _streamWrapper.Write('B').Write('*').NewLine(), + FillRule.NonZeroWindingNumber => InnerStream.Write('B').NewLine(), + FillRule.EvenOdd => InnerStream.Write('B').Write('*').NewLine(), _ => throw new InvalidOperationException(UNKNOWN_FILL_RULE) }; @@ -526,8 +488,8 @@ public ContentStream Write(CloseFillAndStrokeOperator op) { _ = op.FillRule switch { - FillRule.NonZeroWindingNumber => _streamWrapper.Write('b').NewLine(), - FillRule.EvenOdd => _streamWrapper.Write('b').Write('*').NewLine(), + FillRule.NonZeroWindingNumber => InnerStream.Write('b').NewLine(), + FillRule.EvenOdd => InnerStream.Write('b').Write('*').NewLine(), _ => throw new InvalidOperationException(UNKNOWN_FILL_RULE) }; @@ -543,7 +505,7 @@ public ContentStream Write(EndPathOperator op) { _ = op; - _streamWrapper + InnerStream .Write('n').NewLine(); return this; @@ -592,7 +554,7 @@ public ContentStream SetColorStroke(Color color) /// The to support chaining operations. public ContentStream Write(StrokingColorSpaceOperator op) { - _streamWrapper + InnerStream .Write(op.ColorSpace) .Space() .Write('C').Write('S') @@ -608,7 +570,7 @@ public ContentStream Write(StrokingColorSpaceOperator op) /// The to support chaining operations. public ContentStream Write(NonStrokingColorSpaceOperator op) { - _streamWrapper + InnerStream .Write(op.ColorSpace) .Space() .Write('c').Write('s') @@ -624,7 +586,7 @@ public ContentStream Write(NonStrokingColorSpaceOperator op) /// The to support chaining operations. public ContentStream Write(GrayStrokingColorOperator op) { - _streamWrapper + InnerStream .Write(op.Color.Gray) .Space() .Write('G') @@ -640,7 +602,7 @@ public ContentStream Write(GrayStrokingColorOperator op) /// The to support chaining operations. public ContentStream Write(GrayNonStrokingColorOperator op) { - _streamWrapper + InnerStream .Write(op.Color.Gray) .Space() .Write('g') @@ -656,7 +618,7 @@ public ContentStream Write(GrayNonStrokingColorOperator op) /// The to support chaining operations. public ContentStream Write(RgbStrokingColorOperator op) { - _streamWrapper + InnerStream .Write(op.Color.Red) .Space() .Write(op.Color.Green) @@ -677,7 +639,7 @@ public ContentStream Write(RgbStrokingColorOperator op) /// The to support chaining operations. public ContentStream Write(RgbNonStrokingColorOperator op) { - _streamWrapper + InnerStream .Write(op.Color.Red) .Space() .Write(op.Color.Green) @@ -698,7 +660,7 @@ public ContentStream Write(RgbNonStrokingColorOperator op) /// The to support chaining operations. public ContentStream Write(CmykStrokingColorOperator op) { - _streamWrapper + InnerStream .Write(op.Color.Cyan) .Space() .Write(op.Color.Magenta) @@ -720,7 +682,7 @@ public ContentStream Write(CmykStrokingColorOperator op) /// The to support chaining operations. public ContentStream Write(CmykNonStrokingColorOperator op) { - _streamWrapper + InnerStream .Write(op.Color.Cyan) .Space() .Write(op.Color.Magenta) @@ -744,7 +706,7 @@ public ContentStream Write(SpotStrokingColorOperator op) { var name = Resources.AddSeparation(op.Color.Separation); - _streamWrapper + InnerStream .Write(name) .Space() .Write("CS") @@ -766,7 +728,7 @@ public ContentStream Write(SpotNonStrokingColorOperator op) { var name = Resources.AddSeparation(op.Color.Separation); - _streamWrapper + InnerStream .Write(name) .Space() .Write("cs") @@ -786,7 +748,7 @@ public ContentStream Write(SpotNonStrokingColorOperator op) /// The to support chaining operations. public ContentStream Write(LineWidthOperator op) { - _streamWrapper + InnerStream .Write(op.Width) .Space() .Write('w') @@ -802,7 +764,7 @@ public ContentStream Write(LineWidthOperator op) /// The to support chaining operations. public ContentStream Write(LineCapOperator op) { - _streamWrapper + InnerStream .Write((int)op.Style) .Space() .Write('J') @@ -818,7 +780,7 @@ public ContentStream Write(LineCapOperator op) /// The to support chaining operations. public ContentStream Write(LineJoinOperator op) { - _streamWrapper + InnerStream .Write((int)op.Style) .Space() .Write('j') @@ -834,7 +796,7 @@ public ContentStream Write(LineJoinOperator op) /// The to support chaining operations. public ContentStream Write(MiterLimitOperator op) { - _streamWrapper + InnerStream .Write(op.Limit) .Space() .Write('M') @@ -850,7 +812,7 @@ public ContentStream Write(MiterLimitOperator op) /// The to support chaining operations. public ContentStream Write(DashOperator op) { - _streamWrapper + InnerStream .Write(op.Array) .Space() .Write(op.Phase) @@ -860,20 +822,5 @@ public ContentStream Write(DashOperator op) return this; } - - internal uint WriteToStream(PdfStream stream) - { - if (IsWritten) - { - throw new InvalidOperationException("Object is already written to stream."); - } - var position = (uint)stream.Position; - - _streamWrapper.InnerStream.Position = 0; - stream.IndirectStream(this, _streamWrapper.InnerStream); - IsWritten = true; - - return position; - } } } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/ContentStreamExtensions.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/ContentStreamExtensions.cs new file mode 100644 index 0000000..4394d59 --- /dev/null +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/ContentStreamExtensions.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Synercoding.FileFormats.Pdf.LowLevel.Extensions +{ + /// + /// Class containing extension methods for . + /// + public static class ContentStreamExtensions + { + /// + /// Write a (multi-line) text to the PDF + /// + /// The this extension method is for. + /// The text to write + /// The to support chaining operations. + public static ContentStream ShowText(this ContentStream stream, string text) + { + var lines = _splitOnNewLines(text).ToArray(); + + for (int i = 0; i < lines.Length; i++) + { + if (i == 0) + { + stream.ShowTextTj(lines[i]); + } + else + { + stream.MoveNextLineShowText(lines[i]); + } + } + + return stream; + } + + private static IEnumerable _splitOnNewLines(string text) + { + var builder = new StringBuilder(text.Length); + for (int i = 0; i < text.Length; i++) + { + var c = text[i]; + var n = i < text.Length - 1 + ? text[i + 1] + : '0'; + if (( c == '\r' && n == '\n' ) || c == '\r' || c == '\n') + { + yield return builder.ToString(); + builder.Clear(); + + if (n == '\n') + i++; // extra skip to also skip the \n + } + else + { + builder.Append(c); + } + } + + yield return builder.ToString(); + } + } +} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamArrayExtensions.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamArrayExtensions.cs index 3b926a1..796e205 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamArrayExtensions.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamArrayExtensions.cs @@ -1,5 +1,3 @@ -using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors.ColorSpaces; - namespace Synercoding.FileFormats.Pdf.LowLevel.Extensions { /// @@ -10,48 +8,20 @@ public static class PdfStreamArrayExtensions private const byte BRACKET_OPEN = 0x5B; // [ private const byte BRACKET_CLOSE = 0x5D; // ] - internal static PdfStream Write(this PdfStream stream, Separation separation, PdfReference reference) - { - return stream - .StartObject(reference) - .WriteByte(BRACKET_OPEN) - .Write(PdfName.Get("Separation")) - .Write(separation.Name) - .Write(separation.BasedOnColor.Colorspace.Name) - .Dictionary(separation.BasedOnColor, static (color, dict) => - { - var c0 = new double[color.Colorspace.Components]; - var c1 = color.Components; - var range = new double[color.Colorspace.Components * 2]; - for (int i = 1; i < range.Length; i += 2) - range[i] = 1; - - dict.Write(PdfName.Get("C0"), c0) - .Write(PdfName.Get("C1"), c1) - .Write(PdfName.Get("Domain"), 0, 1) - .Write(PdfName.Get("FunctionType"), 2) - .Write(PdfName.Get("N"), 1.0) - .Write(PdfName.Get("Range"), range); - }) - .WriteByte(BRACKET_CLOSE) - .EndObject() - .NewLine(); - } - /// - /// Write an array of numbers to the pdf stream + /// Write an array of chars to the pdf stream /// /// The pdf stream to write the array to. - /// The array of doubles to write + /// The array of chars to write /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, double[] array) + public static PdfStream Write(this PdfStream stream, params char[] array) { stream .WriteByte(BRACKET_OPEN) .Space(); - foreach (var number in array) - stream.Write(number).Space(); + foreach (var c in array) + stream.Write(c).Space(); stream.WriteByte(BRACKET_CLOSE); @@ -62,70 +32,18 @@ public static PdfStream Write(this PdfStream stream, double[] array) /// Write an array of numbers to the pdf stream /// /// The pdf stream to write the array to. - /// The first number in the array - /// The second number in the array - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, double number1, double number2) - { - stream - .WriteByte(BRACKET_OPEN) - .Space() - .Write(number1) - .Space() - .Write(number2) - .Space() - .WriteByte(BRACKET_CLOSE); - - return stream; - } - - /// - /// Write an array of numbers to the pdf stream - /// - /// The pdf stream to write the array to. - /// The first number in the array - /// The second number in the array - /// The third number in the array + /// The array of doubles to write /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, double number1, double number2, double number3) + public static PdfStream Write(this PdfStream stream, params double[] array) { stream .WriteByte(BRACKET_OPEN) - .Space() - .Write(number1) - .Space() - .Write(number2) - .Space() - .Write(number3) - .Space() - .WriteByte(BRACKET_CLOSE); + .Space(); - return stream; - } + foreach (var number in array) + stream.Write(number).Space(); - /// - /// Write an array of numbers to the pdf stream - /// - /// The pdf stream to write the array to. - /// The first number in the array - /// The second number in the array - /// The third number in the array - /// The fourth number in the array - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, double number1, double number2, double number3, double number4) - { - stream - .WriteByte(BRACKET_OPEN) - .Space() - .Write(number1) - .Space() - .Write(number2) - .Space() - .Write(number3) - .Space() - .Write(number4) - .Space() - .WriteByte(BRACKET_CLOSE); + stream.WriteByte(BRACKET_CLOSE); return stream; } @@ -134,151 +52,38 @@ public static PdfStream Write(this PdfStream stream, double number1, double numb /// Write an array of numbers to the pdf stream /// /// The pdf stream to write the array to. - /// The first number in the array - /// The second number in the array - /// The third number in the array - /// The fourth number in the array - /// The fifth number in the array + /// The array of floats to write /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, double number1, double number2, double number3, double number4, double number5) + public static PdfStream Write(this PdfStream stream, params float[] array) { stream .WriteByte(BRACKET_OPEN) - .Space() - .Write(number1) - .Space() - .Write(number2) - .Space() - .Write(number3) - .Space() - .Write(number4) - .Space() - .Write(number5) - .Space() - .WriteByte(BRACKET_CLOSE); + .Space(); - return stream; - } + foreach (var number in array) + stream.Write(number).Space(); - /// - /// Write an array of numbers to the pdf stream - /// - /// The pdf stream to write the array to. - /// The first number in the array - /// The second number in the array - /// The third number in the array - /// The fourth number in the array - /// The fifth number in the array - /// The sixth number in the array - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, double number1, double number2, double number3, double number4, double number5, double number6) - { - stream - .WriteByte(BRACKET_OPEN) - .Space() - .Write(number1) - .Space() - .Write(number2) - .Space() - .Write(number3) - .Space() - .Write(number4) - .Space() - .Write(number5) - .Space() - .Write(number6) - .Space() - .WriteByte(BRACKET_CLOSE); + stream.WriteByte(BRACKET_CLOSE); return stream; } /// - /// Write an array of numbers to the pdf stream + /// Write an array of to the pdf stream /// /// The pdf stream to write the array to. - /// The first number in the array - /// The second number in the array - /// The third number in the array - /// The fourth number in the array - /// The fifth number in the array - /// The sixth number in the array - /// The seventh number in the array + /// The array of to write /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, - double number1, - double number2, - double number3, - double number4, - double number5, - double number6, - double number7) + public static PdfStream Write(this PdfStream stream, params PdfName[] array) { stream .WriteByte(BRACKET_OPEN) - .Space() - .Write(number1) - .Space() - .Write(number2) - .Space() - .Write(number3) - .Space() - .Write(number4) - .Space() - .Write(number5) - .Space() - .Write(number6) - .Space() - .Write(number7) - .Space() - .WriteByte(BRACKET_CLOSE); + .Space(); - return stream; - } + foreach (var name in array) + stream.Write(name).Space(); - /// - /// Write an array of numbers to the pdf stream - /// - /// The pdf stream to write the array to. - /// The first number in the array - /// The second number in the array - /// The third number in the array - /// The fourth number in the array - /// The fifth number in the array - /// The sixth number in the array - /// The seventh number in the array - /// The eigth number in the array - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, - double number1, - double number2, - double number3, - double number4, - double number5, - double number6, - double number7, - double number8) - { - stream - .WriteByte(BRACKET_OPEN) - .Space() - .Write(number1) - .Space() - .Write(number2) - .Space() - .Write(number3) - .Space() - .Write(number4) - .Space() - .Write(number5) - .Space() - .Write(number6) - .Space() - .Write(number7) - .Space() - .Write(number8) - .Space() - .WriteByte(BRACKET_CLOSE); + stream.WriteByte(BRACKET_CLOSE); return stream; } @@ -287,7 +92,7 @@ public static PdfStream Write(this PdfStream stream, /// Write an array of numbers to the pdf stream /// /// The pdf stream to write the array to. - /// The array of numbers to write + /// The array of integers to write /// The to support chaining operations. public static PdfStream Write(this PdfStream stream, int[] array) { @@ -303,236 +108,11 @@ public static PdfStream Write(this PdfStream stream, int[] array) return stream; } - /// - /// Write an array of numbers to the pdf stream - /// - /// The pdf stream to write the array to. - /// The first number in the array - /// The second number in the array - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, int number1, int number2) - { - stream - .WriteByte(BRACKET_OPEN) - .Space() - .Write(number1) - .Space() - .Write(number2) - .Space() - .WriteByte(BRACKET_CLOSE); - - return stream; - } - - /// - /// Write an array of numbers to the pdf stream - /// - /// The pdf stream to write the array to. - /// The first number in the array - /// The second number in the array - /// The third number in the array - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, int number1, int number2, int number3) - { - stream - .WriteByte(BRACKET_OPEN) - .Space() - .Write(number1) - .Space() - .Write(number2) - .Space() - .Write(number3) - .Space() - .WriteByte(BRACKET_CLOSE); - - return stream; - } - - /// - /// Write an array of numbers to the pdf stream - /// - /// The pdf stream to write the array to. - /// The first number in the array - /// The second number in the array - /// The third number in the array - /// The fourth number in the array - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, int number1, int number2, int number3, int number4) - { - stream - .WriteByte(BRACKET_OPEN) - .Space() - .Write(number1) - .Space() - .Write(number2) - .Space() - .Write(number3) - .Space() - .Write(number4) - .Space() - .WriteByte(BRACKET_CLOSE); - - return stream; - } - - /// - /// Write an array of numbers to the pdf stream - /// - /// The pdf stream to write the array to. - /// The first number in the array - /// The second number in the array - /// The third number in the array - /// The fourth number in the array - /// The fifth number in the array - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, int number1, int number2, int number3, int number4, int number5) - { - stream - .WriteByte(BRACKET_OPEN) - .Space() - .Write(number1) - .Space() - .Write(number2) - .Space() - .Write(number3) - .Space() - .Write(number4) - .Space() - .Write(number5) - .Space() - .WriteByte(BRACKET_CLOSE); - - return stream; - } - - /// - /// Write an array of numbers to the pdf stream - /// - /// The pdf stream to write the array to. - /// The first number in the array - /// The second number in the array - /// The third number in the array - /// The fourth number in the array - /// The fifth number in the array - /// The sixth number in the array - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, int number1, int number2, int number3, int number4, int number5, int number6) - { - stream - .WriteByte(BRACKET_OPEN) - .Space() - .Write(number1) - .Space() - .Write(number2) - .Space() - .Write(number3) - .Space() - .Write(number4) - .Space() - .Write(number5) - .Space() - .Write(number6) - .Space() - .WriteByte(BRACKET_CLOSE); - - return stream; - } - - /// - /// Write an array of numbers to the pdf stream - /// - /// The pdf stream to write the array to. - /// The first number in the array - /// The second number in the array - /// The third number in the array - /// The fourth number in the array - /// The fifth number in the array - /// The sixth number in the array - /// The seventh number in the array - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, - int number1, - int number2, - int number3, - int number4, - int number5, - int number6, - int number7) - { - stream - .WriteByte(BRACKET_OPEN) - .Space() - .Write(number1) - .Space() - .Write(number2) - .Space() - .Write(number3) - .Space() - .Write(number4) - .Space() - .Write(number5) - .Space() - .Write(number6) - .Space() - .Write(number7) - .Space() - .WriteByte(BRACKET_CLOSE); - - return stream; - } - - /// - /// Write an array of numbers to the pdf stream - /// - /// The pdf stream to write the array to. - /// The first number in the array - /// The second number in the array - /// The third number in the array - /// The fourth number in the array - /// The fifth number in the array - /// The sixth number in the array - /// The seventh number in the array - /// The eigth number in the array - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, - int number1, - int number2, - int number3, - int number4, - int number5, - int number6, - int number7, - int number8) - { - stream - .WriteByte(BRACKET_OPEN) - .Space() - .Write(number1) - .Space() - .Write(number2) - .Space() - .Write(number3) - .Space() - .Write(number4) - .Space() - .Write(number5) - .Space() - .Write(number6) - .Space() - .Write(number7) - .Space() - .Write(number8) - .Space() - .WriteByte(BRACKET_CLOSE); - - return stream; - } - /// /// Write an array of pdf references to the pdf stream /// /// The pdf stream to write the array to. - /// The array of pdfreferences to write + /// The array of to write /// The to support chaining operations. public static PdfStream Write(this PdfStream stream, PdfReference[] objectReferences) { @@ -544,247 +124,7 @@ public static PdfStream Write(this PdfStream stream, PdfReference[] objectRefere stream.Space(); } - stream.WriteByte(BRACKET_CLOSE).NewLine(); - - return stream; - } - - /// - /// Write an array of pdf references to the pdf stream - /// - /// The pdf stream to write the array to. - /// The first reference in the array - /// The second reference in the array - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, PdfReference reference1, PdfReference reference2) - { - stream - .WriteByte(BRACKET_OPEN) - .Space() - .Write(reference1) - .Space() - .Write(reference2) - .Space() - .WriteByte(BRACKET_CLOSE); - - return stream; - } - - /// - /// Write an array of pdf references to the pdf stream - /// - /// The pdf stream to write the array to. - /// The first reference in the array - /// The second reference in the array - /// The third reference in the array - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, PdfReference reference1, PdfReference reference2, PdfReference reference3) - { - stream - .WriteByte(BRACKET_OPEN) - .Space() - .Write(reference1) - .Space() - .Write(reference2) - .Space() - .Write(reference3) - .Space() - .WriteByte(BRACKET_CLOSE); - - return stream; - } - - /// - /// Write an array of pdf references to the pdf stream - /// - /// The pdf stream to write the array to. - /// The first reference in the array - /// The second reference in the array - /// The third reference in the array - /// The fourth reference in the array - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, - PdfReference reference1, - PdfReference reference2, - PdfReference reference3, - PdfReference reference4) - { - stream - .WriteByte(BRACKET_OPEN) - .Space() - .Write(reference1) - .Space() - .Write(reference2) - .Space() - .Write(reference3) - .Space() - .Write(reference4) - .Space() - .WriteByte(BRACKET_CLOSE); - - return stream; - } - - /// - /// Write an array of pdf references to the pdf stream - /// - /// The pdf stream to write the array to. - /// The first reference in the array - /// The second reference in the array - /// The third reference in the array - /// The fourth reference in the array - /// The fifth reference in the array - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, - PdfReference reference1, - PdfReference reference2, - PdfReference reference3, - PdfReference reference4, - PdfReference reference5) - { - stream - .WriteByte(BRACKET_OPEN) - .Space() - .Write(reference1) - .Space() - .Write(reference2) - .Space() - .Write(reference3) - .Space() - .Write(reference4) - .Space() - .Write(reference5) - .Space() - .WriteByte(BRACKET_CLOSE); - - return stream; - } - - /// - /// Write an array of pdf references to the pdf stream - /// - /// The pdf stream to write the array to. - /// The first reference in the array - /// The second reference in the array - /// The third reference in the array - /// The fourth reference in the array - /// The fifth reference in the array - /// The sixth reference in the array - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, - PdfReference reference1, - PdfReference reference2, - PdfReference reference3, - PdfReference reference4, - PdfReference reference5, - PdfReference reference6) - { - stream - .WriteByte(BRACKET_OPEN) - .Space() - .Write(reference1) - .Space() - .Write(reference2) - .Space() - .Write(reference3) - .Space() - .Write(reference4) - .Space() - .Write(reference5) - .Space() - .Write(reference6) - .Space() - .WriteByte(BRACKET_CLOSE); - - return stream; - } - - /// - /// Write an array of pdf references to the pdf stream - /// - /// The pdf stream to write the array to. - /// The first reference in the array - /// The second reference in the array - /// The third reference in the array - /// The fourth reference in the array - /// The fifth reference in the array - /// The sixth reference in the array - /// The seventh reference in the array - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, - PdfReference reference1, - PdfReference reference2, - PdfReference reference3, - PdfReference reference4, - PdfReference reference5, - PdfReference reference6, - PdfReference reference7) - { - stream - .WriteByte(BRACKET_OPEN) - .Space() - .Write(reference1) - .Space() - .Write(reference2) - .Space() - .Write(reference3) - .Space() - .Write(reference4) - .Space() - .Write(reference5) - .Space() - .Write(reference6) - .Space() - .Write(reference7) - .Space() - .WriteByte(BRACKET_CLOSE); - - return stream; - } - - /// - /// Write an array of pdf references to the pdf stream - /// - /// The pdf stream to write the array to. - /// The first reference in the array - /// The second reference in the array - /// The third reference in the array - /// The fourth reference in the array - /// The fifth reference in the array - /// The sixth reference in the array - /// The seventh reference in the array - /// The eigth reference in the array - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, - PdfReference reference1, - PdfReference reference2, - PdfReference reference3, - PdfReference reference4, - PdfReference reference5, - PdfReference reference6, - PdfReference reference7, - PdfReference reference8) - { - stream - .WriteByte(BRACKET_OPEN) - .Space() - .Write(reference1) - .Space() - .Write(reference2) - .Space() - .Write(reference3) - .Space() - .Write(reference4) - .Space() - .Write(reference5) - .Space() - .Write(reference6) - .Space() - .Write(reference7) - .Space() - .Write(reference8) - .Space() - .WriteByte(BRACKET_CLOSE); + stream.WriteByte(BRACKET_CLOSE); return stream; } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamExtensions.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamExtensions.cs index 9f56b0b..1276810 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamExtensions.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamExtensions.cs @@ -1,6 +1,8 @@ using Synercoding.Primitives; using System; using System.IO; +using System.Linq; +using System.Reflection; namespace Synercoding.FileFormats.Pdf.LowLevel.Extensions { @@ -130,6 +132,121 @@ public static PdfStream Write(this PdfStream stream, Rectangle rectangle) return stream.Write(rectangle.LLX.Raw, rectangle.LLY.Raw, rectangle.URX.Raw, rectangle.URY.Raw); } + public static PdfStream Write(this PdfStream stream, DateTimeOffset dateTimeOffset) + { + stream + .Write('(') + .Write('D') + .Write(':'); + + stream.Write(dateTimeOffset.Year); + + if (dateTimeOffset.Month < 10) + stream.Write('0'); + stream.Write(dateTimeOffset.Month); + + if (dateTimeOffset.Day < 10) + stream.Write('0'); + stream.Write(dateTimeOffset.Day); + + if (dateTimeOffset.Hour < 10) + stream.Write('0'); + stream.Write(dateTimeOffset.Hour); + + if (dateTimeOffset.Minute < 10) + stream.Write('0'); + stream.Write(dateTimeOffset.Minute); + + if (dateTimeOffset.Second < 10) + stream.Write('0'); + stream.Write(dateTimeOffset.Second); + + var hours = dateTimeOffset.Offset.Hours; + var minutes = dateTimeOffset.Offset.Minutes; + if (hours == 0 && minutes == 0) + { + stream + .Write('Z') + .Write('0') + .Write('0') + .Write('\'') + .Write('0') + .Write('0'); + } + else + { + if (hours > 0 || ( hours == 0 && minutes > 0 )) + stream.Write('+'); + else + stream.Write('-'); + + if (hours < 10) + stream.Write('0'); + stream.Write(hours); + + stream.Write('\''); + + if (minutes < 10) + stream.Write('0'); + stream.Write(minutes); + } + + stream + .Write(')'); + + return stream; + } + + public static PdfStream WriteHexadecimalString(this PdfStream stream, string value) + { + var bytes = System.Text.Encoding.ASCII.GetBytes(value); + + stream.Write('<'); + foreach (var b in bytes) + { + var (c1, c2) = _getByteAsHex(b); + + stream + .Write(c1) + .Write(c2); + } + stream.Write('>'); + + return stream; + + static (char C1, char C2) _getByteAsHex(byte b) + { + var c1 = _getHexCharForNumber(b % 16); + var c2 = _getHexCharForNumber(b / 16 % 16); + + return (c1, c2); + + static char _getHexCharForNumber(int number) + { + return number switch + { + 0 => '0', + 1 => '1', + 2 => '2', + 3 => '3', + 4 => '4', + 5 => '5', + 6 => '6', + 7 => '7', + 8 => '8', + 9 => '9', + 10 => 'A', + 11 => 'B', + 12 => 'C', + 13 => 'D', + 14 => 'E', + 15 => 'F', + _ => throw new InvalidOperationException() + }; + } + } + } + /// /// Write a text to the stream as a string literal /// @@ -170,31 +287,31 @@ public static PdfStream WriteStringLiteral(this PdfStream stream, string value) stream.WriteByte(0x29); // ) return stream; - } - private static int _toOctal(int number) - { - if (number > 511) - throw new ArgumentOutOfRangeException(nameof(number), "Number is higher than octal 777 (dec 511)."); - if (number < 0) - throw new ArgumentOutOfRangeException(nameof(number), "Only positive numbers can be converted."); + static int _toOctal(int number) + { + if (number > 511) + throw new ArgumentOutOfRangeException(nameof(number), "Number is higher than octal 777 (dec 511)."); + if (number < 0) + throw new ArgumentOutOfRangeException(nameof(number), "Only positive numbers can be converted."); - int resultNumber = 0; + int resultNumber = 0; - int quotient = number; - int multiplier = 1; + int quotient = number; + int multiplier = 1; - do - { - int remainder = quotient % 8; - quotient = quotient / 8; + do + { + int remainder = quotient % 8; + quotient = quotient / 8; - resultNumber += multiplier * remainder; - multiplier *= 10; - } - while (quotient != 0); + resultNumber += multiplier * remainder; + multiplier *= 10; + } + while (quotient != 0); - return resultNumber; + return resultNumber; + } } internal static PdfStream StartObject(this PdfStream stream, PdfReference objectReference) @@ -223,66 +340,9 @@ internal static PdfStream EndObject(this PdfStream stream) .NewLine(); } - internal static PdfStream IndirectStream(this PdfStream contentStream, TPdfObject pdfObject, Stream stream, params StreamFilter[] streamFilters) - where TPdfObject : IPdfObject - => contentStream.IndirectStream(pdfObject, stream, _ => { }, streamFilters); - - internal static PdfStream IndirectStream(this PdfStream contentStream, TPdfObject pdfObject, Stream stream, Action dictionaryAction, params StreamFilter[] streamFilters) - where TPdfObject : IPdfObject - => contentStream.IndirectStream(pdfObject, stream, dictionaryAction, static (action, dict) => action(dict), streamFilters); - - internal static PdfStream IndirectStream(this PdfStream contentStream, TPdfObject pdfObject, Stream stream, TData data, Action dictionaryAction, params StreamFilter[] streamFilters) - where TPdfObject : IPdfObject - where TData : notnull - { - contentStream - .StartObject(pdfObject.Reference) - .Dictionary((stream, data, dictionaryAction, streamFilters), static (tuple, dictionary) => - { - dictionary.Write(PdfName.Get("Length"), tuple.stream.Length); - if (tuple.streamFilters != null && tuple.streamFilters.Length > 0) - { - if (tuple.streamFilters.Length == 1) - { - dictionary.Write(PdfName.Get("Filter"), tuple.streamFilters[0].ToPdfName()); - } - else - { - dictionary.Write(PdfName.Get("Filter"), tuple.streamFilters, static (filters, streamPart) => - { - streamPart - .WriteByte(0x5B) // [ - .Space(); - - foreach (var filter in filters) - { - streamPart - .Write(filter.ToPdfName()) - .Space(); - } - - streamPart - .WriteByte(0x5D); // ] - }); - } - } - - tuple.dictionaryAction(tuple.data, dictionary); - }) - .Write("stream") - .NewLine() - .CopyFrom(stream) - .NewLine() - .Write("endstream") - .NewLine() - .EndObject() - .NewLine(); - - return contentStream; - } - internal static PdfStream CopyFrom(this PdfStream stream, Stream data) { + data.Position = 0; data.CopyTo(stream.InnerStream); return stream; } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/FontSubType.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/FontSubType.cs deleted file mode 100644 index 379e54c..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/FontSubType.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel -{ - internal enum FontSubType - { - Type1 - } -} \ No newline at end of file diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/GraphicState.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/GraphicState.cs index 508c077..e1a01b3 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/GraphicState.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/GraphicState.cs @@ -1,4 +1,5 @@ using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors; +using Synercoding.FileFormats.Pdf.LowLevel.Internal; using Synercoding.FileFormats.Pdf.LowLevel.Operators.Pathing.Painting; namespace Synercoding.FileFormats.Pdf.LowLevel.Graphics diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/Catalog.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/Catalog.cs new file mode 100644 index 0000000..f7be7eb --- /dev/null +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/Catalog.cs @@ -0,0 +1,16 @@ +namespace Synercoding.FileFormats.Pdf.LowLevel.Internal +{ + internal class Catalog : IPdfObject + { + public Catalog(PdfReference id, PageTree pageTree) + { + Reference = id; + PageTree = pageTree; + } + + /// + public PdfReference Reference { get; } + + public PageTree PageTree { get; } + } +} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/DocumentResources.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/DocumentResources.cs new file mode 100644 index 0000000..70c386c --- /dev/null +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/DocumentResources.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Synercoding.FileFormats.Pdf.LowLevel.Internal; +internal class DocumentResources +{ +} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/IdGenerator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/IdGenerator.cs similarity index 85% rename from src/Synercoding.FileFormats.Pdf/LowLevel/IdGenerator.cs rename to src/Synercoding.FileFormats.Pdf/LowLevel/Internal/IdGenerator.cs index c8fb862..23d5055 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/IdGenerator.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/IdGenerator.cs @@ -1,4 +1,4 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel +namespace Synercoding.FileFormats.Pdf.LowLevel.Internal { internal sealed class IdGenerator { diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/PageResources.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageResources.cs similarity index 85% rename from src/Synercoding.FileFormats.Pdf/LowLevel/PageResources.cs rename to src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageResources.cs index a37ae08..cf9bcdd 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/PageResources.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageResources.cs @@ -6,10 +6,13 @@ using System.Collections.Generic; using System.Linq; -namespace Synercoding.FileFormats.Pdf.LowLevel +namespace Synercoding.FileFormats.Pdf.LowLevel.Internal { internal sealed class PageResources : IDisposable { + private const string PREFIX_IMAGE = "Im"; + private const string PREFIX_SEPARATION = "Sep"; + private readonly TableBuilder _tableBuilder; private readonly Map _images; private readonly Dictionary _separations; @@ -50,7 +53,7 @@ public PdfName AddImage(Image image) if (_images.Reverse.Contains(image)) return _images.Reverse[image]; - var key = "Im" + System.Threading.Interlocked.Increment(ref _imageCounter).ToString().PadLeft(6, '0'); + var key = PREFIX_IMAGE + System.Threading.Interlocked.Increment(ref _imageCounter).ToString().PadLeft(6, '0'); var pdfName = PdfName.Get(key); @@ -72,7 +75,7 @@ internal PdfName AddSeparation(Separation separation) if (_separations.TryGetValue(separation, out var tuple)) return tuple.Name; - var key = "Sep" + System.Threading.Interlocked.Increment(ref _separationCounter).ToString().PadLeft(6, '0'); + var key = PREFIX_SEPARATION + System.Threading.Interlocked.Increment(ref _separationCounter).ToString().PadLeft(6, '0'); var name = PdfName.Get(key); _separations[separation] = (name, _tableBuilder.ReserveId()); diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageTree.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageTree.cs new file mode 100644 index 0000000..d33cf34 --- /dev/null +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageTree.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; + +namespace Synercoding.FileFormats.Pdf.LowLevel.Internal +{ + internal class PageTree : IPdfObject + { + private readonly List _pages = new List(); + + public PageTree(PdfReference id) + { + Reference = id; + } + + /// + public PdfReference Reference { get; } + + public void AddPage(PdfPage pdfObject) + => _pages.Add(pdfObject); + + public int PageCount + => _pages.Count; + + internal IReadOnlyCollection Pages + => _pages.AsReadOnly(); + } +} diff --git a/src/Synercoding.FileFormats.Pdf/Internals/ShapeContext.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/ShapeContext.cs similarity index 93% rename from src/Synercoding.FileFormats.Pdf/Internals/ShapeContext.cs rename to src/Synercoding.FileFormats.Pdf/LowLevel/Internal/ShapeContext.cs index aabbe92..ced983b 100644 --- a/src/Synercoding.FileFormats.Pdf/Internals/ShapeContext.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/ShapeContext.cs @@ -1,8 +1,8 @@ -using Synercoding.FileFormats.Pdf.LowLevel; +using Synercoding.FileFormats.Pdf.Internals; using Synercoding.FileFormats.Pdf.LowLevel.Graphics; using System; -namespace Synercoding.FileFormats.Pdf.Internals +namespace Synercoding.FileFormats.Pdf.LowLevel.Internal { internal sealed class ShapeContext : IShapeContext, IDisposable { diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/ObjectStream.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/ObjectStream.cs new file mode 100644 index 0000000..f6b5968 --- /dev/null +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/ObjectStream.cs @@ -0,0 +1,269 @@ +using Synercoding.FileFormats.Pdf.LowLevel.Extensions; +using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors.ColorSpaces; +using Synercoding.FileFormats.Pdf.LowLevel.Internal; +using Synercoding.FileFormats.Pdf.LowLevel.Text; +using Synercoding.FileFormats.Pdf.LowLevel.XRef; +using System; +using System.IO; +using System.Linq; + +namespace Synercoding.FileFormats.Pdf.LowLevel; + +internal class ObjectStream +{ + private const byte BRACKET_OPEN = 0x5B; // [ + private const byte BRACKET_CLOSE = 0x5D; // ] + + private readonly TableBuilder _tableBuilder; + + public ObjectStream(PdfStream stream, TableBuilder tableBuilder) + { + InnerStream = stream; + _tableBuilder = tableBuilder; + } + + public PdfStream InnerStream { get; } + + public ObjectStream Write(ContentStream contentStream) + { + if (!_tableBuilder.TrySetPosition(contentStream.Reference, InnerStream.Position)) + return this; + + _indirectStream(contentStream.Reference, contentStream.InnerStream.InnerStream); + + return this; + } + + public ObjectStream Write(Catalog catalog) + { + if (!_tableBuilder.TrySetPosition(catalog.Reference, InnerStream.Position)) + return this; + + _indirectDictionary(catalog.Reference, catalog, static (catalog, dictionary) => + { + dictionary + .Write(PdfName.Get("Type"), PdfName.Get("Catalog")) + .Write(PdfName.Get("Pages"), catalog.PageTree.Reference); + }); + + return this; + } + + public ObjectStream Write(DocumentInformation documentInformation) + { + if (!_tableBuilder.TrySetPosition(documentInformation.Reference, InnerStream.Position)) + return this; + + _indirectDictionary(documentInformation.Reference, documentInformation, static (did, dictionary) => + { + dictionary + .WriteHexadecimalIfNotNullOrWhiteSpace(PdfName.Get("Title"), did.Title) + .WriteHexadecimalIfNotNullOrWhiteSpace(PdfName.Get("Author"), did.Author) + .WriteHexadecimalIfNotNullOrWhiteSpace(PdfName.Get("Subject"), did.Subject) + .WriteHexadecimalIfNotNullOrWhiteSpace(PdfName.Get("Keywords"), did.Keywords) + .WriteHexadecimalIfNotNullOrWhiteSpace(PdfName.Get("Creator"), did.Creator) + .WriteHexadecimalIfNotNullOrWhiteSpace(PdfName.Get("Producer"), did.Producer) + .WriteIfNotNull(PdfName.Get("CreationDate"), did.CreationDate) + .WriteIfNotNull(PdfName.Get("ModDate"), did.ModDate); + + if (did.ExtraInfo.Count != 0) + foreach (var kv in did.ExtraInfo) + dictionary.WriteHexadecimalString(PdfName.Get(kv.Key), kv.Value); + }); + + return this; + } + + public ObjectStream Write(PageTree pageTree) + { + if(!_tableBuilder.TrySetPosition(pageTree.Reference, InnerStream.Position)) + return this; + + _indirectDictionary(pageTree.Reference, pageTree, static (pageTree, dictionary) => + { + dictionary + .Write(PdfName.Get("Type"), PdfName.Get("Pages")) + .Write(PdfName.Get("Kids"), pageTree.Pages.Select(static p => p.Reference).ToArray()) + .Write(PdfName.Get("Count"), pageTree.Pages.Count); + }); + + return this; + } + + public ObjectStream Write(Image image) + { + if (!_tableBuilder.TrySetPosition(image.Reference, InnerStream.Position)) + return this; + + _indirectStream(image.Reference, image.RawStream, image, static (image, dictionary) => + { + dictionary + .Write(PdfName.Get("Type"), PdfName.Get("XObject")) + .Write(PdfName.Get("Subtype"), PdfName.Get("Image")) + .Write(PdfName.Get("Width"), image.Width) + .Write(PdfName.Get("Height"), image.Height) + .Write(PdfName.Get("ColorSpace"), PdfName.Get("DeviceRGB")) + .Write(PdfName.Get("BitsPerComponent"), 8) + .Write(PdfName.Get("Decode"), 0f, 1f, 0f, 1f, 0f, 1f); + }, StreamFilter.DCTDecode); + + return this; + } + + public ObjectStream Write(PdfPage page) + { + if (!_tableBuilder.TrySetPosition(page.Reference, InnerStream.Position)) + return this; + + _indirectDictionary(page.Reference, page, static (page, dictionary) => + { + dictionary + .Write(PdfName.Get("Type"), PdfName.Get("Page")) + .Write(PdfName.Get("Parent"), page.Parent) + .Write(PdfName.Get("MediaBox"), page.MediaBox) + .WriteIfNotNull(PdfName.Get("CropBox"), page.CropBox) + .WriteIfNotNull(PdfName.Get("BleedBox"), page.BleedBox) + .WriteIfNotNull(PdfName.Get("TrimBox"), page.TrimBox) + .WriteIfNotNull(PdfName.Get("Rotate"), page.Rotation); + + // Resources + dictionary.Write(PdfName.Get("Resources"), page.Resources, static (resources, stream) => stream.Dictionary(resources, static (resources, stream) => + { + if (resources.Images.Count != 0) + { + stream.Write(PdfName.Get("XObject"), resources.Images, static (images, stream) => stream.Dictionary(images, static (images, xobject) => + { + foreach (var image in images) + { + xobject.Write(image.Key, image.Value.Reference); + } + })); + } + + if (resources.FontReferences.Count != 0) + { + stream.Write(PdfName.Get("Font"), resources.FontReferences, static (fonts, stream) => stream.Dictionary(fonts, static (fontReferences, fontDictionary) => + { + foreach (var (font, reference) in fontReferences) + { + fontDictionary.Write(font.LookupName, reference); + } + })); + } + + if (resources.SeparationReferences.Count != 0) + { + stream.Write(PdfName.Get("ColorSpace"), resources.SeparationReferences.Values, static (separations, stream) => stream.Dictionary(separations, static (separations, colorspaceDictionary) => + { + foreach (var (name, reference) in separations) + { + colorspaceDictionary.Write(name, reference); + } + })); + } + })); + + // Content stream + dictionary.Write(PdfName.Get("Contents"), page.ContentStream.Reference); + }); + + return this; + } + + public ObjectStream Write(PdfReference reference, Type1StandardFont font) + { + if (!_tableBuilder.TrySetPosition(reference, InnerStream.Position)) + return this; + + _indirectDictionary(reference, font, static (font, dict) => + { + dict + .Write(PdfName.Get("Type"), PdfName.Get("Font")) + .Write(PdfName.Get("Subtype"), PdfName.Get("Type1")) + .Write(PdfName.Get("BaseFont"), font.Name); + }); + + return this; + } + + public ObjectStream Write(PdfReference reference, Separation separation) + { + if (!_tableBuilder.TrySetPosition(reference, InnerStream.Position)) + return this; + + InnerStream + .StartObject(reference) + .WriteByte(BRACKET_OPEN) + .Write(PdfName.Get("Separation")) + .Write(separation.Name) + .Write(separation.BasedOnColor.Colorspace.Name) + .Dictionary(separation.BasedOnColor, static (color, dict) => + { + var c0 = new double[color.Colorspace.Components]; + var c1 = color.Components; + var range = new double[color.Colorspace.Components * 2]; + for (int i = 1; i < range.Length; i += 2) + range[i] = 1; + + dict.Write(PdfName.Get("C0"), c0) + .Write(PdfName.Get("C1"), c1) + .Write(PdfName.Get("Domain"), 0, 1) + .Write(PdfName.Get("FunctionType"), 2) + .Write(PdfName.Get("N"), 1.0) + .Write(PdfName.Get("Range"), range); + }) + .WriteByte(BRACKET_CLOSE) + .EndObject() + .NewLine(); + + return this; + } + + private void _indirectDictionary(PdfReference reference, T data, Action dictionaryAction) + { + InnerStream + .StartObject(reference) + .Dictionary(data, dictionaryAction) + .EndObject() + .NewLine(); + } + + private void _indirectStream(PdfReference reference, Stream stream, params StreamFilter[] streamFilters) + => _indirectStream(reference, stream, _ => { }, streamFilters); + + private void _indirectStream(PdfReference reference, Stream stream, Action dictionaryAction, params StreamFilter[] streamFilters) + => _indirectStream(reference, stream, dictionaryAction, static (action, dict) => action(dict), streamFilters); + + private void _indirectStream(PdfReference reference, Stream stream, TData data, Action dictionaryAction, params StreamFilter[] streamFilters) + where TData : notnull + { + InnerStream + .StartObject(reference) + .Dictionary((stream.Length, data, dictionaryAction, streamFilters), static (tuple, dictionary) => + { + var (length, data, dictionaryAction, streamFilters) = tuple; + dictionary.Write(PdfName.Get("Length"), length); + if (streamFilters != null && tuple.streamFilters.Length > 0) + { + if (streamFilters.Length == 1) + { + dictionary.Write(PdfName.Get("Filter"), streamFilters[0].ToPdfName()); + } + else + { + dictionary.Write(PdfName.Get("Filter"), streamFilters.Select(f => f.ToPdfName()).ToArray()); + } + } + + dictionaryAction(data, dictionary); + }) + .Write("stream") + .NewLine() + .CopyFrom(stream) + .NewLine() + .Write("endstream") + .NewLine() + .EndObject() + .NewLine(); + } +} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/ObjectType.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/ObjectType.cs deleted file mode 100644 index 9cebd93..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/ObjectType.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel -{ - internal enum ObjectType - { - Page, - Pages, - Catalog, - XObject, - Font - } -} \ No newline at end of file diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/PageTree.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/PageTree.cs deleted file mode 100644 index 93e79b6..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/PageTree.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Synercoding.FileFormats.Pdf.LowLevel.Extensions; -using Synercoding.FileFormats.Pdf.LowLevel.XRef; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Synercoding.FileFormats.Pdf.LowLevel -{ - internal class PageTree : IPdfObject - { - private readonly IList _pages = new List(); - private readonly TableBuilder _tableBuilder; - private bool _isWritten; - - public PageTree(PdfReference id, TableBuilder tableBuilder) - { - Reference = id; - _tableBuilder = tableBuilder; - } - - /// - public PdfReference Reference { get; } - - public void AddPage(PdfPage pdfObject) - => _pages.Add(pdfObject); - - public int PageCount - => _pages.Count; - - internal uint WriteToStream(PdfStream stream) - { - if (_isWritten) - { - throw new InvalidOperationException("Object is already written to stream."); - } - - var position = stream.Position; - stream.IndirectDictionary(Reference, this, static (pageTree, dictionary) => - { - dictionary - .Type(ObjectType.Pages) - .Write(PdfName.Get("Kids"), pageTree._pages.Select(static p => p.Reference).ToArray()) - .Write(PdfName.Get("Count"), pageTree._pages.Count); - }); - _isWritten = true; - - return position; - } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/PdfDictionary.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/PdfDictionary.cs index 250a065..5c21726 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/PdfDictionary.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/PdfDictionary.cs @@ -1,7 +1,6 @@ using Synercoding.FileFormats.Pdf.LowLevel.Extensions; using Synercoding.Primitives; using System; -using System.Runtime.Serialization; namespace Synercoding.FileFormats.Pdf.LowLevel { @@ -27,7 +26,7 @@ public PdfDictionary(PdfStream stream) /// The key of the item in the dictionary /// The array to write /// The to support chaining operations. - public PdfDictionary Write(PdfName key, int[] numbers) + public PdfDictionary Write(PdfName key, params int[] numbers) { _stream .Write(key) @@ -43,7 +42,7 @@ public PdfDictionary Write(PdfName key, int[] numbers) /// The key of the item in the dictionary /// The array to write /// The to support chaining operations. - public PdfDictionary Write(PdfName key, double[] numbers) + public PdfDictionary Write(PdfName key, params double[] numbers) { _stream .Write(key) @@ -53,129 +52,66 @@ public PdfDictionary Write(PdfName key, double[] numbers) return this; } - /// - /// Write an array of s to the dictionary - /// - /// The key of the item in the dictionary - /// The array to write - /// The to support chaining operations. - public PdfDictionary Write(PdfName key, PdfReference[] objectReferences) - { - _stream - .Write(key) - .Space() - .Write(objectReferences); - - return this; - } - - /// - /// Write a number to the dictionary - /// - /// The key of the item in the dictionary - /// The number to write - /// The to support chaining operations. - public PdfDictionary Write(PdfName key, double value) - { - _stream - .Write(key) - .Space() - .Write(value); - - return this; - } - - /// - /// Write an array of numbers to the dictionary - /// - /// The key of the item in the dictionary - /// The first number to write in the array - /// The second number to write in the array - /// The to support chaining operations. - public PdfDictionary Write(PdfName key, double value1, double value2) - { - _stream - .Write(key) - .Space() - .Write(value1, value2); - - return this; - } - /// /// Write an array of numbers to the dictionary /// /// The key of the item in the dictionary - /// The first number to write in the array - /// The second number to write in the array - /// The third number to write in the array + /// The array to write /// The to support chaining operations. - public PdfDictionary Write(PdfName key, double value1, double value2, double value3) + public PdfDictionary Write(PdfName key, params float[] numbers) { _stream .Write(key) .Space() - .Write(value1, value2, value3); + .Write(numbers); return this; } /// - /// Write an array of numbers to the dictionary + /// Write an array of to the dictionary /// /// The key of the item in the dictionary - /// The first number to write in the array - /// The second number to write in the array - /// The third number to write in the array - /// The fourth number to write in the array + /// The array to write /// The to support chaining operations. - public PdfDictionary Write(PdfName key, double value1, double value2, double value3, double value4) + public PdfDictionary Write(PdfName key, params PdfName[] names) { _stream .Write(key) .Space() - .Write(value1, value2, value3, value4); + .Write(names); return this; } /// - /// Write an array of numbers to the dictionary + /// Write an array of s to the dictionary /// /// The key of the item in the dictionary - /// The first number to write in the array - /// The second number to write in the array - /// The third number to write in the array - /// The fourth number to write in the array - /// The fifth number to write in the array + /// The array to write /// The to support chaining operations. - public PdfDictionary Write(PdfName key, double value1, double value2, double value3, double value4, double value5) + public PdfDictionary Write(PdfName key, params PdfReference[] objectReferences) { _stream .Write(key) .Space() - .Write(value1, value2, value3, value4, value5); + .Write(objectReferences); return this; } /// - /// Write an array of numbers to the dictionary + /// Write a number to the dictionary /// /// The key of the item in the dictionary - /// The first number to write in the array - /// The second number to write in the array - /// The third number to write in the array - /// The fourth number to write in the array - /// The fifth number to write in the array - /// The sixth number to write in the array + /// The number to write /// The to support chaining operations. - public PdfDictionary Write(PdfName key, double value1, double value2, double value3, double value4, double value5, double value6) + public PdfDictionary Write(PdfName key, double value) { _stream .Write(key) .Space() - .Write(value1, value2, value3, value4, value5, value6); + .Write(value); return this; } @@ -213,107 +149,54 @@ public PdfDictionary Write(PdfName key, int value) } /// - /// Write an array of numbers to the dictionary - /// - /// The key of the item in the dictionary - /// The first number to write in the array - /// The second number to write in the array - /// The to support chaining operations. - public PdfDictionary Write(PdfName key, int value1, int value2) - { - _stream - .Write(key) - .Space() - .Write(value1, value2); - - return this; - } - - /// - /// Write an array of numbers to the dictionary - /// - /// The key of the item in the dictionary - /// The first number to write in the array - /// The second number to write in the array - /// The third number to write in the array - /// The to support chaining operations. - public PdfDictionary Write(PdfName key, int value1, int value2, int value3) - { - _stream - .Write(key) - .Space() - .Write(value1, value2, value3); - - return this; - } - - /// - /// Write an array of numbers to the dictionary + /// Write a text to the dictionary /// /// The key of the item in the dictionary - /// The first number to write in the array - /// The second number to write in the array - /// The third number to write in the array - /// The fourth number to write in the array + /// The text to write /// The to support chaining operations. - public PdfDictionary Write(PdfName key, int value1, int value2, int value3, int value4) + public PdfDictionary Write(PdfName key, string value) { _stream .Write(key) .Space() - .Write(value1, value2, value3, value4); + .Write(value); return this; } /// - /// Write an array of numbers to the dictionary + /// Write a text to the dictionary /// /// The key of the item in the dictionary - /// The first number to write in the array - /// The second number to write in the array - /// The third number to write in the array - /// The fourth number to write in the array - /// The fifth number to write in the array + /// The text to write as a literal string /// The to support chaining operations. - public PdfDictionary Write(PdfName key, int value1, int value2, int value3, int value4, int value5) + public PdfDictionary WriteLiteralString(PdfName key, string value) { _stream .Write(key) .Space() - .Write(value1, value2, value3, value4, value5); + .WriteStringLiteral(value); return this; } /// - /// Write an array of numbers to the dictionary + /// Write a text to the dictionary /// /// The key of the item in the dictionary - /// The first number to write in the array - /// The second number to write in the array - /// The third number to write in the array - /// The fourth number to write in the array - /// The fifth number to write in the array - /// The sixth number to write in the array + /// The text to write as a hexadecimal string /// The to support chaining operations. - public PdfDictionary Write(PdfName key, int value1, int value2, int value3, int value4, int value5, int value6) + public PdfDictionary WriteHexadecimalString(PdfName key, string value) { _stream .Write(key) .Space() - .Write(value1, value2, value3, value4, value5, value6); + .WriteHexadecimalString(value); return this; } - /// - /// Write a text to the dictionary - /// - /// The key of the item in the dictionary - /// The text to write - /// The to support chaining operations. - public PdfDictionary Write(PdfName key, string value) + public PdfDictionary Write(PdfName key, DateTimeOffset value) { _stream .Write(key) @@ -433,56 +316,42 @@ public PdfDictionary WriteIfNotNull(PdfName key, double? value) ? Write(key, value.Value) : this; - internal PdfDictionary Type(ObjectType objectType) - { - var nameValue = objectType switch - { - ObjectType.Catalog => PdfName.Get("Catalog"), - ObjectType.Page => PdfName.Get("Page"), - ObjectType.Pages => PdfName.Get("Pages"), - ObjectType.XObject => PdfName.Get("XObject"), - ObjectType.Font => PdfName.Get("Font"), - _ => throw new NotImplementedException("Unknown objectType: " + objectType) - }; - - _stream - .Write(PdfName.Get("Type")) - .Space() - .Write(nameValue); - - return this; - } - - internal PdfDictionary SubType(XObjectSubType subType) - { - var nameValue = subType switch - { - XObjectSubType.Image => PdfName.Get("Image"), - _ => throw new NotImplementedException("Unknown XObjectSubType: " + subType) - }; - - _stream - .Write(PdfName.Get("Subtype")) - .Space() - .Write(nameValue); - - return this; - } + public PdfDictionary WriteIfNotNull(PdfName key, DateTimeOffset? value) + => value.HasValue + ? Write(key, value.Value) + : this; - internal PdfDictionary SubType(FontSubType subType) - { - var nameValue = subType switch - { - FontSubType.Type1 => PdfName.Get("Type1"), - _ => throw new NotImplementedException("Unknown FontSubType: " + subType) - }; + /// + /// Write a number to the stream if it is not null + /// + /// The key of the item in the dictionary + /// The string to write. + /// The to support chaining operations. + public PdfDictionary WriteIfNotNullOrWhiteSpace(PdfName key, string? value) + => !string.IsNullOrWhiteSpace(value) + ? Write(key, value) + : this; - _stream - .Write(PdfName.Get("Subtype")) - .Space() - .Write(nameValue); + /// + /// Write a number to the stream if it is not null + /// + /// The key of the item in the dictionary + /// The string to write as a pdf literal string. + /// The to support chaining operations. + public PdfDictionary WriteLiteralIfNotNullOrWhiteSpace(PdfName key, string? value) + => !string.IsNullOrWhiteSpace(value) + ? WriteLiteralString(key, value) + : this; - return this; - } + /// + /// Write a number to the stream if it is not null + /// + /// The key of the item in the dictionary + /// The string to write as a pdf hexadecimal string. + /// The to support chaining operations. + public PdfDictionary WriteHexadecimalIfNotNullOrWhiteSpace(PdfName key, string? value) + => !string.IsNullOrWhiteSpace(value) + ? WriteHexadecimalString(key, value) + : this; } -} \ No newline at end of file +} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/PdfName.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/PdfName.cs index a1fa580..35500b7 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/PdfName.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/PdfName.cs @@ -1,4 +1,3 @@ -using Synercoding.FileFormats.Pdf.Internals; using System; using System.Collections.Generic; using System.Linq; diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/PdfReference.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/PdfReference.cs index 202092c..2b17433 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/PdfReference.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/PdfReference.cs @@ -40,4 +40,4 @@ public override string ToString() return $"{ObjectId} {Generation}"; } } -} \ No newline at end of file +} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/XObjectSubType.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/XObjectSubType.cs deleted file mode 100644 index 1362bb0..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/XObjectSubType.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel -{ - internal enum XObjectSubType - { - Image - } -} \ No newline at end of file diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/TableBuilder.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/TableBuilder.cs index fb1245d..9f8b8d6 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/TableBuilder.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/TableBuilder.cs @@ -1,4 +1,5 @@ -using System; +using Synercoding.FileFormats.Pdf.LowLevel.Internal; +using System; using System.Collections.Generic; using System.Linq; @@ -25,12 +26,14 @@ public PdfReference GetId(uint position) return reference; } - public void SetPosition(PdfReference id, uint position) + public bool TrySetPosition(PdfReference id, uint position) { if (_positions[id] != -1) - throw new InvalidOperationException($"Reference {id} already has a position in the xref table."); + return false; _positions[id] = position; + + return true; } public bool Validate() diff --git a/src/Synercoding.FileFormats.Pdf/PdfPage.cs b/src/Synercoding.FileFormats.Pdf/PdfPage.cs index a42f9d2..a84440a 100644 --- a/src/Synercoding.FileFormats.Pdf/PdfPage.cs +++ b/src/Synercoding.FileFormats.Pdf/PdfPage.cs @@ -1,5 +1,5 @@ using Synercoding.FileFormats.Pdf.LowLevel; -using Synercoding.FileFormats.Pdf.LowLevel.Extensions; +using Synercoding.FileFormats.Pdf.LowLevel.Internal; using Synercoding.FileFormats.Pdf.LowLevel.Text; using Synercoding.FileFormats.Pdf.LowLevel.XRef; using Synercoding.Primitives; @@ -17,7 +17,6 @@ public sealed class PdfPage : IPdfObject, IDisposable private readonly PageTree _parent; private int? _rotation; - private bool _isWritten; internal PdfPage(TableBuilder tableBuilder, PageTree parent) { @@ -31,6 +30,9 @@ internal PdfPage(TableBuilder tableBuilder, PageTree parent) ContentStream = new ContentStream(tableBuilder.ReserveId(), Resources); } + internal PdfReference Parent + => _parent.Reference; + internal PageResources Resources { get; } /// @@ -149,106 +151,5 @@ internal void MarkStdFontAsUsed(Type1StandardFont font) { Resources.AddStandardFont(font); } - - internal uint WriteToStream(PdfStream stream) - { - if (_isWritten) - { - throw new InvalidOperationException("Object is already written to stream."); - } - - var position = (uint)stream.Position; - _tableBuilder.SetPosition(Reference, position); - - stream.IndirectDictionary(this, static (page, dictionary) => - { - dictionary - .Type(ObjectType.Page) - .Write(PdfName.Get("Parent"), page._parent.Reference); - - // Boxes - dictionary - .Write(PdfName.Get("MediaBox"), page.MediaBox) - .WriteIfNotNull(PdfName.Get("CropBox"), page.CropBox) - .WriteIfNotNull(PdfName.Get("BleedBox"), page.BleedBox) - .WriteIfNotNull(PdfName.Get("TrimBox"), page.TrimBox) - .WriteIfNotNull(PdfName.Get("Rotate"), page.Rotation); - - // Resources - dictionary.Write(PdfName.Get("Resources"), page.Resources, static (resources, stream) => stream.Dictionary(resources, static (resources, stream) => - { - if (resources.Images.Count != 0) - { - stream.Write(PdfName.Get("XObject"), resources.Images, static (images, stream) => stream.Dictionary(images, static (images, xobject) => - { - foreach (var image in images) - { - xobject.Write(image.Key, image.Value.Reference); - } - })); - } - - if (resources.FontReferences.Count != 0) - { - stream.Write(PdfName.Get("Font"), resources.FontReferences, static (fonts, stream) => stream.Dictionary(fonts, static (fontReferences, fontDictionary) => - { - foreach (var (font, reference) in fontReferences) - { - fontDictionary.Write(font.LookupName, reference); - } - })); - } - - if (resources.SeparationReferences.Count != 0) - { - stream.Write(PdfName.Get("ColorSpace"), resources.SeparationReferences.Values, static (separations, stream) => stream.Dictionary(separations, static (separations, colorspaceDictionary) => - { - foreach (var (name, reference) in separations) - { - colorspaceDictionary.Write(name, reference); - } - })); - } - })); - - // Content stream - dictionary.Write(PdfName.Get("Contents"), page.ContentStream.Reference); - }); - - _isWritten = true; - - foreach (var kv in Resources.Images) - { - if (kv.Value.TryWriteToStream(stream, out uint dependentPosition)) - { - _tableBuilder.SetPosition(kv.Value.Reference, dependentPosition); - } - } - foreach (var (font, refId) in Resources.FontReferences) - { - _tableBuilder.SetPosition(refId, stream.Position); - - stream.IndirectDictionary(refId, font, static (font, dict) => - { - dict - .Type(ObjectType.Font) - .SubType(FontSubType.Type1) - .Write(PdfName.Get("BaseFont"), font.Name); - }); - } - foreach (var (separation, (_, refId)) in Resources.SeparationReferences) - { - _tableBuilder.SetPosition(refId, stream.Position); - - stream.Write(separation, refId); - } - if (!ContentStream.IsWritten) - { - var dependentPosition = ContentStream.WriteToStream(stream); - _tableBuilder.SetPosition(ContentStream.Reference, dependentPosition); - } - - return position; - } } } diff --git a/src/Synercoding.FileFormats.Pdf/PdfWriter.cs b/src/Synercoding.FileFormats.Pdf/PdfWriter.cs index 83167fd..f5a4593 100644 --- a/src/Synercoding.FileFormats.Pdf/PdfWriter.cs +++ b/src/Synercoding.FileFormats.Pdf/PdfWriter.cs @@ -1,5 +1,6 @@ using Synercoding.FileFormats.Pdf.LowLevel; using Synercoding.FileFormats.Pdf.LowLevel.Extensions; +using Synercoding.FileFormats.Pdf.LowLevel.Internal; using Synercoding.FileFormats.Pdf.LowLevel.XRef; using System; using System.IO; @@ -14,7 +15,7 @@ namespace Synercoding.FileFormats.Pdf public sealed class PdfWriter : IDisposable { private readonly bool _ownsStream; - private readonly PdfStream _stream; + private readonly ObjectStream _objectStream; private readonly TableBuilder _tableBuilder = new TableBuilder(); private readonly PageTree _pageTree; @@ -37,10 +38,11 @@ public PdfWriter(Stream stream) /// If the stream is owned, then when this is disposed, the stream is also disposed. public PdfWriter(Stream stream, bool ownsStream) { - _stream = new PdfStream(stream); - _writeHeader(_stream); + var pdfStream = new PdfStream(stream); + _writeHeader(pdfStream); + _objectStream = new ObjectStream(pdfStream, _tableBuilder); - _pageTree = new PageTree(_tableBuilder.ReserveId(), _tableBuilder); + _pageTree = new PageTree(_tableBuilder.ReserveId()); _catalog = new Catalog(_tableBuilder.ReserveId(), _pageTree); DocumentInformation = new DocumentInformation(_tableBuilder.ReserveId()) @@ -69,10 +71,20 @@ public int PageCount /// Action used to set meta data /// Returns this to chain calls public PdfWriter SetDocumentInfo(Action infoAction) + => SetDocumentInfo(infoAction, static (action, did) => action(did)); + + /// + /// Set meta information for this document + /// + /// Type of data to pass to the action + /// Data to be used in the action + /// Action used to set meta data + /// Returns this to chain calls + public PdfWriter SetDocumentInfo(T data, Action infoAction) { _throwWhenEndingWritten(); - infoAction(DocumentInformation); + infoAction(data, DocumentInformation); return this; } @@ -98,8 +110,7 @@ public PdfWriter AddPage(T data, Action pageAction) using (var page = new PdfPage(_tableBuilder, _pageTree)) { pageAction(data, page); - - page.WriteToStream(_stream); + _writePageAndResourcesToObjectStream(page); } return this; @@ -127,7 +138,7 @@ public async Task AddPageAsync(T data, Func page { await pageAction(data, page); - page.WriteToStream(_stream); + _writePageAndResourcesToObjectStream(page); } return this; @@ -146,10 +157,7 @@ public Image AddImage(SixLabors.ImageSharp.Image image) var pdfImage = new Image(id, image); - if (!pdfImage.TryWriteToStream(_stream, out uint position)) - throw new InvalidOperationException("Image was just created but could not be written to stream."); - - _tableBuilder.SetPosition(id, position); + _objectStream.Write(pdfImage); return pdfImage; } @@ -172,10 +180,7 @@ public Image AddJpgImageUnsafe(Stream jpgStream, int originalWidth, int original var pdfImage = new Image(id, jpgStream, originalWidth, originalHeight); - if (!pdfImage.TryWriteToStream(_stream, out uint position)) - throw new InvalidOperationException("Image was just created but could not be written to stream."); - - _tableBuilder.SetPosition(id, position); + _objectStream.Write(pdfImage); return pdfImage; } @@ -191,15 +196,14 @@ public void WriteTrailer() if (_endingWritten) return; - _writePageTree(); - - _writeCatalog(); - - _writeDocumentInformation(); + _objectStream + .Write(_pageTree) + .Write(_catalog) + .Write(DocumentInformation); _writePdfEnding(); - _stream.Flush(); + _objectStream.InnerStream.Flush(); _endingWritten = true; } @@ -209,14 +213,30 @@ public void Dispose() { WriteTrailer(); - _stream.Flush(); + _objectStream.InnerStream.Flush(); if (_ownsStream) { - _stream.Dispose(); + _objectStream.InnerStream.Dispose(); } } + private void _writePageAndResourcesToObjectStream(PdfPage page) + { + _objectStream.Write(page); + + foreach (var kv in page.Resources.Images) + _objectStream.Write(kv.Value); + + foreach (var (font, refId) in page.Resources.FontReferences) + _objectStream.Write(refId, font); + + foreach (var (separation, (_, refId)) in page.Resources.SeparationReferences) + _objectStream.Write(refId, separation); + + _objectStream.Write(page.ContentStream); + } + private void _throwWhenEndingWritten() { if (_endingWritten) throw new InvalidOperationException("Can't change document information when PDF trailer is written to the stream."); @@ -243,36 +263,15 @@ private static void _writeHeader(PdfStream stream) stream.WriteByte(0x0A); // LF } - private void _writeDocumentInformation() - { - _tableBuilder.SetPosition(DocumentInformation.Reference, (uint)_stream.Position); - - DocumentInformation.WriteToStream(_stream); - } - - private void _writePageTree() - { - _tableBuilder.SetPosition(_pageTree.Reference, (uint)_stream.Position); - - _pageTree.WriteToStream(_stream); - } - - private void _writeCatalog() - { - _tableBuilder.SetPosition(_catalog.Reference, (uint)_stream.Position); - - _catalog.WriteToStream(_stream); - } - private void _writePdfEnding() { if (!_tableBuilder.Validate()) throw new InvalidOperationException("XRef table is invalid."); var xRefTable = _tableBuilder.GetXRefTable(); - uint xRefPosition = xRefTable.WriteToStream(_stream); + uint xRefPosition = xRefTable.WriteToStream(_objectStream.InnerStream); - _writeTrailer(_stream, xRefPosition, xRefTable.Section.ObjectCount, _catalog.Reference, DocumentInformation.Reference); + _writeTrailer(_objectStream.InnerStream, xRefPosition, xRefTable.Section.ObjectCount, _catalog.Reference, DocumentInformation.Reference); } private void _writeTrailer(PdfStream stream, uint startXRef, int size, PdfReference root, PdfReference documentInfo) From d1753633fe7253c93715b3729f85c74f281c4ef1 Mon Sep 17 00:00:00 2001 From: Gerard Gunnewijk Date: Mon, 23 Jan 2023 14:03:38 +0100 Subject: [PATCH 02/11] Updated Synercoding.Primitives to rc09 --- Directory.Build.targets | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.targets b/Directory.Build.targets index 2d90efb..32ff1d6 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -8,7 +8,7 @@ - + - \ No newline at end of file + From 835806ce500407d58ffa615157b99ad0f18d67be Mon Sep 17 00:00:00 2001 From: Gerard Gunnewijk Date: Wed, 25 Jan 2023 22:29:17 +0100 Subject: [PATCH 03/11] Combined shapes, images and text into single api --- .../Program.cs | 128 +++--- src/Directory.Build.props | 4 +- .../DocumentInformation.cs | 6 +- .../Extensions/IContentContextExtensions.cs | 14 + .../IPageContentContextExtensions.cs | 126 ++++++ .../Extensions/IShapeContextExtensions.cs | 9 + .../Extensions/PdfPageExtensions.cs | 243 ----------- .../IContentContext.cs | 37 ++ .../IPageContentContext.cs | 15 + .../IShapeContext.cs | 129 ++++-- .../ITextContentContext.cs | 32 ++ src/Synercoding.FileFormats.Pdf/Image.cs | 37 +- .../Internals/PageContentContext.cs | 197 +++++++++ .../Internals/Path.cs | 143 ------- .../Internals/ShapesContext.cs | 241 +++++++++++ .../Internals/StringHelper.cs | 32 ++ .../Internals/TextContentContext.cs | 290 +++++++++++++ .../Internals/WrappedShapesContext.cs | 241 +++++++++++ .../Internals/WrappedTextContentContext.cs | 294 +++++++++++++ .../{Graphics => }/Colors/CmykColor.cs | 4 +- .../LowLevel/{Graphics => }/Colors/Color.cs | 6 +- .../Colors/ColorSpaces/ColorSpace.cs | 2 +- .../Colors/ColorSpaces/DeviceCMYK.cs | 4 +- .../Colors/ColorSpaces/DeviceGray.cs | 2 +- .../Colors/ColorSpaces/DeviceRGB.cs | 4 +- .../Colors/ColorSpaces/Separation.cs | 2 +- .../{Graphics => }/Colors/GrayColor.cs | 4 +- .../{Graphics => }/Colors/PredefinedColors.cs | 2 +- .../{Graphics => }/Colors/RgbColor.cs | 4 +- .../{Graphics => }/Colors/SpotColor.cs | 6 +- .../LowLevel/ContentStream.cs | 401 ++++++++++-------- .../Extensions/ContentStreamExtensions.cs | 63 --- .../Extensions/PdfStreamExtensions.cs | 31 +- .../LowLevel/FillRule.cs | 16 + .../LowLevel/Graphics/Dash.cs | 48 ++- .../LowLevel/Graphics/GraphicState.cs | 77 ---- .../LowLevel/Graphics/LineJoinStyle.cs | 2 +- .../LowLevel/IPdfObject.cs | 13 - .../LowLevel/Internal/Catalog.cs | 6 +- .../LowLevel/Internal/PageResources.cs | 29 +- .../LowLevel/Internal/PageTree.cs | 6 +- .../LowLevel/Internal/ShapeContext.cs | 63 --- .../LowLevel/ObjectStream.cs | 10 +- .../Color/CmykNonStrokingColorOperator.cs | 24 -- .../Color/CmykStrokingColorOperator.cs | 24 -- .../Color/GrayNonStrokingColorOperator.cs | 24 -- .../Color/GrayStrokingColorOperator.cs | 24 -- .../Color/NonStrokingColorSpaceOperator.cs | 22 - .../Color/RgbNonStrokingColorOperator.cs | 24 -- .../Color/RgbStrokingColorOperator.cs | 24 -- .../Color/SpotNonStrokingColorOperator.cs | 24 -- .../Color/SpotStrokingColorOperator.cs | 24 -- .../Color/StrokingColorSpaceOperator.cs | 22 - .../Pathing/Construction/CloseOperator.cs | 7 - ...bicBezierCurveDualControlPointsOperator.cs | 79 ---- ...icBezierCurveFinalControlPointsOperator.cs | 61 --- ...BezierCurveInitialControlPointsOperator.cs | 61 --- .../Pathing/Construction/LineOperator.cs | 43 -- .../Pathing/Construction/MoveOperator.cs | 43 -- .../Pathing/Construction/RectangleOperator.cs | 59 --- .../Painting/CloseAndStrokeOperator.cs | 7 - .../Painting/CloseFillAndStrokeOperator.cs | 22 - .../Pathing/Painting/EndPathOperator.cs | 7 - .../Pathing/Painting/FillAndStrokeOperator.cs | 22 - .../Pathing/Painting/FillOperator.cs | 23 - .../Operators/Pathing/Painting/FillRule.cs | 17 - .../Pathing/Painting/StrokeOperator.cs | 7 - .../LowLevel/Operators/State/DashOperator.cs | 29 -- .../Operators/State/LineCapOperator.cs | 24 -- .../Operators/State/LineJoinOperator.cs | 24 -- .../Operators/State/LineWidthOperator.cs | 22 - .../Operators/State/MiterLimitOperator.cs | 22 - .../LowLevel/Text/Font.cs | 6 +- .../LowLevel/Text/TextState.cs | 2 +- .../LowLevel/Text/Type1StandardFont.cs | 6 +- src/Synercoding.FileFormats.Pdf/Matrix.cs | 12 +- .../PageRotation.cs | 9 + src/Synercoding.FileFormats.Pdf/PdfPage.cs | 85 +--- src/Synercoding.FileFormats.Pdf/PdfWriter.cs | 8 +- .../StandardFonts.cs | 2 - .../Synercoding.FileFormats.Pdf.csproj | 4 +- 81 files changed, 2130 insertions(+), 1842 deletions(-) create mode 100644 src/Synercoding.FileFormats.Pdf/Extensions/IContentContextExtensions.cs create mode 100644 src/Synercoding.FileFormats.Pdf/Extensions/IPageContentContextExtensions.cs create mode 100644 src/Synercoding.FileFormats.Pdf/Extensions/IShapeContextExtensions.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/Extensions/PdfPageExtensions.cs create mode 100644 src/Synercoding.FileFormats.Pdf/IContentContext.cs create mode 100644 src/Synercoding.FileFormats.Pdf/IPageContentContext.cs create mode 100644 src/Synercoding.FileFormats.Pdf/ITextContentContext.cs create mode 100644 src/Synercoding.FileFormats.Pdf/Internals/PageContentContext.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/Internals/Path.cs create mode 100644 src/Synercoding.FileFormats.Pdf/Internals/ShapesContext.cs create mode 100644 src/Synercoding.FileFormats.Pdf/Internals/StringHelper.cs create mode 100644 src/Synercoding.FileFormats.Pdf/Internals/TextContentContext.cs create mode 100644 src/Synercoding.FileFormats.Pdf/Internals/WrappedShapesContext.cs create mode 100644 src/Synercoding.FileFormats.Pdf/Internals/WrappedTextContentContext.cs rename src/Synercoding.FileFormats.Pdf/LowLevel/{Graphics => }/Colors/CmykColor.cs (96%) rename src/Synercoding.FileFormats.Pdf/LowLevel/{Graphics => }/Colors/Color.cs (88%) rename src/Synercoding.FileFormats.Pdf/LowLevel/{Graphics => }/Colors/ColorSpaces/ColorSpace.cs (89%) rename src/Synercoding.FileFormats.Pdf/LowLevel/{Graphics => }/Colors/ColorSpaces/DeviceCMYK.cs (92%) rename src/Synercoding.FileFormats.Pdf/LowLevel/{Graphics => }/Colors/ColorSpaces/DeviceGray.cs (92%) rename src/Synercoding.FileFormats.Pdf/LowLevel/{Graphics => }/Colors/ColorSpaces/DeviceRGB.cs (88%) rename src/Synercoding.FileFormats.Pdf/LowLevel/{Graphics => }/Colors/ColorSpaces/Separation.cs (94%) rename src/Synercoding.FileFormats.Pdf/LowLevel/{Graphics => }/Colors/GrayColor.cs (92%) rename src/Synercoding.FileFormats.Pdf/LowLevel/{Graphics => }/Colors/PredefinedColors.cs (96%) rename src/Synercoding.FileFormats.Pdf/LowLevel/{Graphics => }/Colors/RgbColor.cs (96%) rename src/Synercoding.FileFormats.Pdf/LowLevel/{Graphics => }/Colors/SpotColor.cs (90%) delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/ContentStreamExtensions.cs create mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/FillRule.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/GraphicState.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/IPdfObject.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Internal/ShapeContext.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/CmykNonStrokingColorOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/CmykStrokingColorOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/GrayNonStrokingColorOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/GrayStrokingColorOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/NonStrokingColorSpaceOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/RgbNonStrokingColorOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/RgbStrokingColorOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/SpotNonStrokingColorOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/SpotStrokingColorOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/StrokingColorSpaceOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/CloseOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/CubicBezierCurveDualControlPointsOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/CubicBezierCurveFinalControlPointsOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/CubicBezierCurveInitialControlPointsOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/LineOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/MoveOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/RectangleOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/CloseAndStrokeOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/CloseFillAndStrokeOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/EndPathOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/FillAndStrokeOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/FillOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/FillRule.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/StrokeOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/State/DashOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/State/LineCapOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/State/LineJoinOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/State/LineWidthOperator.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Operators/State/MiterLimitOperator.cs create mode 100644 src/Synercoding.FileFormats.Pdf/PageRotation.cs diff --git a/samples/Synercoding.FileFormats.Pdf.ConsoleTester/Program.cs b/samples/Synercoding.FileFormats.Pdf.ConsoleTester/Program.cs index 85c4835..b1069e3 100644 --- a/samples/Synercoding.FileFormats.Pdf.ConsoleTester/Program.cs +++ b/samples/Synercoding.FileFormats.Pdf.ConsoleTester/Program.cs @@ -1,7 +1,6 @@ using Synercoding.FileFormats.Pdf.Extensions; -using Synercoding.FileFormats.Pdf.LowLevel.Graphics; -using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors; -using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors.ColorSpaces; +using Synercoding.FileFormats.Pdf.LowLevel.Colors; +using Synercoding.FileFormats.Pdf.LowLevel.Colors.ColorSpaces; using Synercoding.FileFormats.Pdf.LowLevel.Text; using Synercoding.Primitives; using Synercoding.Primitives.Extensions; @@ -23,7 +22,7 @@ public static void Main(string[] args) using (var fs = File.OpenWrite(fileName)) using (var writer = new PdfWriter(fs)) { - var bleed = new Spacing(3, Unit.Millimeters); + var bleed = Mm(3); var mediaBox = Sizes.A4.Expand(bleed).AsRectangle(); var trimBox = mediaBox.Contract(bleed); @@ -44,11 +43,42 @@ public static void Main(string[] args) using (var blurStream = File.OpenRead("Pexels_com/4k-wallpaper-blur-bokeh-1484253.jpg")) { - var addedImage = writer.AddJpgImageUnsafe(blurStream, 7000, 4672); + var addedImage = writer.AddJpgUnsafe(blurStream, 7000, 4672, DeviceRGB.Instance); var scale = (double)addedImage.Width / addedImage.Height; - page.AddImage(addedImage, new Rectangle(0, 0, scale * 303, 303, Unit.Millimeters)); + page.Content.AddImage(addedImage, new Rectangle(0, 0, scale * 303, 303, Unit.Millimeters)); } }) + // Add text to page and use it as the clipping path + .AddPage(page => + { + page.MediaBox = mediaBox; + page.TrimBox = trimBox; + + page.Content.WrapInState(content => + { + content.AddText(textOp => + { + textOp.SetTextRenderingMode(TextRenderingMode.AddClippingPath) + .SetFontAndSize(StandardFonts.Helvetica, 160) + .SetTextLeading(500) + .MoveToStartNextLine(Mm(10).AsRaw(Unit.Points), Mm(200).AsRaw(Unit.Points)) + .ShowText("Clipped") + .SetFontAndSize(StandardFonts.HelveticaBold, 650) + .ShowTextOnNextLine("it!"); + }); + + using (var forestStream = File.OpenRead("Pexels_com/android-wallpaper-art-backlit-1114897.jpg")) + using (var forestImage = SixLabors.ImageSharp.Image.Load(forestStream)) + { + var scale = (double)forestImage.Width / forestImage.Height; + + var matrix = Matrix.CreateScaleMatrix(new Value(scale * 303, Unit.Millimeters).AsRaw(Unit.Points), new Value(303, Unit.Millimeters).AsRaw(Unit.Points)) + .Translate(new Value(-100, Unit.Millimeters).AsRaw(Unit.Points), new Value(0, Unit.Millimeters).AsRaw(Unit.Points)); + + page.Content.AddImage(forestImage, matrix); + } + }); + }) // Test placement using rectangle .AddPage(page => { @@ -60,7 +90,7 @@ public static void Main(string[] args) { var scale = (double)barrenImage.Width / barrenImage.Height; - page.AddImage(barrenImage, new Rectangle(0, 0, scale * 303, 303, Unit.Millimeters)); + page.Content.AddImage(barrenImage, new Rectangle(0, 0, scale * 303, 303, Unit.Millimeters)); } using (var eyeStream = File.OpenRead("Pexels_com/adult-blue-blue-eyes-865711.jpg")) @@ -71,7 +101,7 @@ public static void Main(string[] args) var height = 100 * scale; var offSet = 6; - page.AddImage(eyeStream, new Rectangle(offSet, offSet, width + offSet, height + offSet, Unit.Millimeters)); + page.Content.AddImage(eyeStream, new Rectangle(offSet, offSet, width + offSet, height + offSet, Unit.Millimeters)); } }) // Test shape graphics @@ -80,40 +110,39 @@ public static void Main(string[] args) page.MediaBox = mediaBox; page.TrimBox = trimBox; - page.AddShapes(ctx => + page.Content.AddShapes(ctx => { - ctx.DefaultState(g => - { - g.LineWidth = 1; - g.Fill = null; - g.Stroke = null; - g.Dash = new Dash() - { - Array = Array.Empty(), - Phase = 0 - }; - g.MiterLimit = 10; - g.LineCap = LineCapStyle.ButtCap; - g.LineJoin = LineJoinStyle.MiterJoin; - }); + ctx.SetMiterLimit(10) + .SetLineCap(LowLevel.Graphics.LineCapStyle.ButtCap) + .SetLineJoin(LowLevel.Graphics.LineJoinStyle.MiterJoin); - ctx.NewPath(g => { g.Fill = PredefinedColors.Red; g.Stroke = PredefinedColors.Black; g.LineWidth = 5; }) - .Move(100, 100) + ctx.Move(100, 100) .LineTo(200, 100) .LineTo(200, 200) - .LineTo(100, 200); - ctx.NewPath(g => { g.Fill = PredefinedColors.Blue; g.Stroke = null; }) - .Move(50, 50) + .LineTo(100, 200) + .SetLineWidth(5) + .SetStroke(PredefinedColors.Black) + .SetFill(PredefinedColors.Red) + .FillThenStroke(LowLevel.FillRule.NonZeroWindingNumber); + + ctx.Move(50, 50) .LineTo(150, 50) .LineTo(150, 150) .LineTo(50, 150) - .Close(); - ctx.NewPath(g => { g.Fill = null; g.Stroke = PredefinedColors.Yellow; g.LineWidth = 3; g.Dash = new Dash() { Array = new[] { 5d } }; }) - .Move(150, 150) + .SetLineWidth(1) + .SetFill(PredefinedColors.Blue) + .CloseSubPath() + .Fill(LowLevel.FillRule.NonZeroWindingNumber); + + ctx.Move(150, 150) .LineTo(250, 150) .LineTo(250, 250) .LineTo(150, 250) - .Close(); + .SetLineWidth(3) + .SetStroke(PredefinedColors.Yellow) + .SetDashPattern(new LowLevel.Graphics.Dash() { Array = new[] { 5d } }) + .CloseSubPath() + .Stroke(); }); }) // Test pages with text @@ -122,23 +151,26 @@ public static void Main(string[] args) page.MediaBox = mediaBox; page.TrimBox = trimBox; - page.AddText("The quick brown fox jumps over the lazy dog.", new Point(Mm(10), Mm(10)), new TextState(StandardFonts.Helvetica, 12) + page.Content.AddText(ops => { - Fill = PredefinedColors.Blue + ops.MoveToStartNextLine(Mm(10).AsRaw(Unit.Points), Mm(10).AsRaw(Unit.Points)) + .SetFontAndSize(StandardFonts.Helvetica, 12) + .SetFill(PredefinedColors.Blue) + .ShowText("The quick brown fox jumps over the lazy dog."); }); - page.AddText("Text with a newline" + Environment.NewLine + "in it.", new Point(Mm(10), Mm(20))); + + page.Content.AddText("Text with a newline" + Environment.NewLine + "in it.", StandardFonts.Helvetica, 12, new Point(Mm(10), Mm(20))); }) .AddPage(page => { page.MediaBox = mediaBox; page.TrimBox = trimBox; - page.AddText("This page also used Helvetica", new Point(Mm(10), Mm(10)), state => + page.Content.AddText("This page also used Helvetica", StandardFonts.Helvetica, 32, textContext => { - state.FontSize = 32; - state.Font = StandardFonts.Helvetica; - state.RenderingMode = TextRenderingMode.Stroke; - state.Stroke = PredefinedColors.Red; + textContext.MoveToStartNextLine(Mm(10).AsRaw(Unit.Points), Mm(10).AsRaw(Unit.Points)) + .SetTextRenderingMode(TextRenderingMode.Stroke) + .SetStroke(PredefinedColors.Red); }); }) // Test placement using matrix @@ -155,7 +187,7 @@ public static void Main(string[] args) var matrix = Matrix.CreateScaleMatrix(new Value(scale * 303, Unit.Millimeters).AsRaw(Unit.Points), new Value(303, Unit.Millimeters).AsRaw(Unit.Points)) .Translate(new Value(-100, Unit.Millimeters).AsRaw(Unit.Points), new Value(0, Unit.Millimeters).AsRaw(Unit.Points)); - page.AddImage(forestImage, matrix); + page.Content.AddImage(forestImage, matrix); } }); @@ -173,7 +205,7 @@ public static void Main(string[] args) var scale = (double)blurImage.Width / blurImage.Height; - page.AddImage(reusedImage, new Rectangle(0, 0, scale * 303, 303, Unit.Millimeters)); + page.Content.AddImage(reusedImage, new Rectangle(0, 0, scale * 303, 303, Unit.Millimeters)); }); } @@ -185,15 +217,13 @@ public static void Main(string[] args) var scale = (double)blurImage.Width / blurImage.Height; - page.AddImage(reusedImage, new Rectangle(0, 0, scale * 303, 303, Unit.Millimeters)); + page.Content.AddImage(reusedImage, new Rectangle(0, 0, scale * 303, 303, Unit.Millimeters)); - page.AddShapes(trimBox, static (trim, context) => + page.Content.AddShapes(trimBox, static (trim, context) => { - context.DefaultState(state => - { - state.Stroke = new SpotColor(new Separation(LowLevel.PdfName.Get("CutContour"), PredefinedColors.Magenta), 1); - }); - context.NewPath().Rectangle(trim); + context.SetStroke(new SpotColor(new Separation(LowLevel.PdfName.Get("CutContour"), PredefinedColors.Magenta), 1)); + context.Rectangle(trim); + context.Stroke(); }); }); } diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 9b11b21..896375d 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -2,7 +2,7 @@ - net7.0;net6.0;netstandard2.1 + net7.0;net6.0 $(MSBuildAllProjects);$(MSBuildThisFileDirectory)..\Directory.Build.props src @@ -17,4 +17,4 @@ CS1591 - \ No newline at end of file + diff --git a/src/Synercoding.FileFormats.Pdf/DocumentInformation.cs b/src/Synercoding.FileFormats.Pdf/DocumentInformation.cs index 2f06ae4..b07a77c 100644 --- a/src/Synercoding.FileFormats.Pdf/DocumentInformation.cs +++ b/src/Synercoding.FileFormats.Pdf/DocumentInformation.cs @@ -7,7 +7,7 @@ namespace Synercoding.FileFormats.Pdf /// /// This class contains information about the document /// - public class DocumentInformation : IPdfObject + public class DocumentInformation { internal DocumentInformation(PdfReference id) { @@ -54,7 +54,9 @@ internal DocumentInformation(PdfReference id) /// public DateTime? ModDate { get; set; } - /// + /// + /// A pdf reference object that can be used to reference to this object + /// public PdfReference Reference { get; } /// diff --git a/src/Synercoding.FileFormats.Pdf/Extensions/IContentContextExtensions.cs b/src/Synercoding.FileFormats.Pdf/Extensions/IContentContextExtensions.cs new file mode 100644 index 0000000..9592f36 --- /dev/null +++ b/src/Synercoding.FileFormats.Pdf/Extensions/IContentContextExtensions.cs @@ -0,0 +1,14 @@ +using System; +using System.Threading.Tasks; + +namespace Synercoding.FileFormats.Pdf.Extensions; +public static class IContentContextExtensions +{ + public static TContext WrapInState(this TContext context, Action contentOperations) + where TContext : IContentContext + => context.WrapInState(contentOperations, static (operations, context) => operations(context)); + + public static Task WrapInStateAsync(this TContext context, Func contentOperations) + where TContext: IContentContext + => context.WrapInStateAsync(contentOperations, static (operations, context) => operations(context)); +} diff --git a/src/Synercoding.FileFormats.Pdf/Extensions/IPageContentContextExtensions.cs b/src/Synercoding.FileFormats.Pdf/Extensions/IPageContentContextExtensions.cs new file mode 100644 index 0000000..3f06955 --- /dev/null +++ b/src/Synercoding.FileFormats.Pdf/Extensions/IPageContentContextExtensions.cs @@ -0,0 +1,126 @@ +using Synercoding.FileFormats.Pdf.Internals; +using Synercoding.FileFormats.Pdf.LowLevel.Colors.ColorSpaces; +using Synercoding.FileFormats.Pdf.LowLevel.Text; +using Synercoding.Primitives; +using Synercoding.Primitives.Extensions; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace Synercoding.FileFormats.Pdf.Extensions; +public static class IPageContentContextExtensions +{ + public static IPageContentContext AddJpgUnsafe(this IPageContentContext context, System.IO.Stream jpgStream, int originalWidth, int originalHeight, ColorSpace colorSpace) + { + var name = context.RawContentStream.Resources.AddJpgUnsafe(jpgStream, originalWidth, originalHeight, colorSpace); + + context.RawContentStream.Paint(name); + + return context; + } + + public static IPageContentContext AddImage(this IPageContentContext context, System.IO.Stream stream, Matrix matrix) + { + return context.WrapInState((stream, matrix), static (tuple, context) => + { + context.ConcatenateMatrix(tuple.matrix); + context.AddImage(tuple.stream); + }); + } + + public static IPageContentContext AddImage(this IPageContentContext context, System.IO.Stream stream, Rectangle rectangle) + => context.AddImage(stream, rectangle.AsPlacementMatrix()); + + public static IPageContentContext AddImage(this IPageContentContext context, System.IO.Stream stream) + { + using var image = SixLabors.ImageSharp.Image.Load(stream); + + return context.AddImage(image); + } + + public static IPageContentContext AddImage(this IPageContentContext context, SixLabors.ImageSharp.Image image, Matrix matrix) + { + return context.WrapInState((image, matrix), static (tuple, context) => + { + context.ConcatenateMatrix(tuple.matrix); + context.AddImage(tuple.image); + }); + } + + public static IPageContentContext AddImage(this IPageContentContext context, SixLabors.ImageSharp.Image image, Rectangle rectangle) + => context.AddImage(image, rectangle.AsPlacementMatrix()); + + public static IPageContentContext AddImage(this IPageContentContext context, SixLabors.ImageSharp.Image image) + { + var name = context.RawContentStream.Resources.AddImage(image); + + context.RawContentStream.Paint(name); + + return context; + } + + public static IPageContentContext AddImage(this IPageContentContext context, Image image, Rectangle rectangle) + => context.AddImage(image, rectangle.AsPlacementMatrix()); + + public static IPageContentContext AddImage(this IPageContentContext context, Image image, Matrix matrix) + { + return context.WrapInState((image, matrix), static (tuple, context) => + { + context.ConcatenateMatrix(tuple.matrix); + context.AddImage(tuple.image); + }); + } + + public static IPageContentContext AddShapes(this IPageContentContext context, Action shapeOperations) + => context.AddShapes(shapeOperations, static (operations, context) => operations(context)); + + public static Task AddShapesAsync(this IPageContentContext context, Func shapeOperations) + => context.AddShapesAsync(shapeOperations, static (operations, context) => operations(context)); + + public static IPageContentContext AddText(this IPageContentContext context, Action textOperations) + => context.AddText(textOperations, static (operations, context) => operations(context)); + + public static Task AddTextAsync(this IPageContentContext context, Func textOperations) + => context.AddTextAsync(textOperations, static (operations, context) => operations(context)); + + public static IPageContentContext AddText(this IPageContentContext context, string text, Font font, double size) + => context.AddText(text, font, size, ops => { }); + + public static IPageContentContext AddText(this IPageContentContext context, string text, Font font, double size, Point location) + => context.AddText(text, font, size, location, static (location, ops) => + { + ops.MoveToStartNextLine(location.X.AsRaw(Unit.Points), location.Y.AsRaw(Unit.Points)); + }); + + public static IPageContentContext AddText(this IPageContentContext context, string text, Font font, double size, Action extraOperations) + => context.AddText(text, font, size, extraOperations, static (extraOperations, context) => extraOperations(context)); + + public static IPageContentContext AddText(this IPageContentContext context, string text, Font font, double size, T data, Action extraOperations) + { + return context.AddText((text, font, size, data, extraOperations), static (quintuple, context) => + { + var (text, font, size, data, extraOperations) = quintuple; + context.SetFontAndSize(font, size); + + extraOperations(data, context); + + var lines = StringHelper.SplitOnNewLines(text).ToArray(); + + // if no leading parameter is set, and the text spans multiple lines, set textleading to the font size. + if (lines.Length > 1 && context.TextLeading == default) + context.SetTextLeading(size); + + for (int i = 0; i < lines.Length; i++) + { + if (i == 0) + { + context.ShowText(lines[i]); + } + else + { + context.ShowTextOnNextLine(lines[i]); + } + } + }); + } +} diff --git a/src/Synercoding.FileFormats.Pdf/Extensions/IShapeContextExtensions.cs b/src/Synercoding.FileFormats.Pdf/Extensions/IShapeContextExtensions.cs new file mode 100644 index 0000000..acaf827 --- /dev/null +++ b/src/Synercoding.FileFormats.Pdf/Extensions/IShapeContextExtensions.cs @@ -0,0 +1,9 @@ +using Synercoding.Primitives; +using Synercoding.Primitives.Extensions; + +namespace Synercoding.FileFormats.Pdf.Extensions; +public static class IShapeContextExtensions +{ + public static IShapeContext Rectangle(this IShapeContext context, Rectangle rectangle) + => context.Rectangle(rectangle.LLX.AsRaw(Unit.Points), rectangle.LLY.AsRaw(Unit.Points), rectangle.Width.AsRaw(Unit.Points), rectangle.Height.AsRaw(Unit.Points)); +} diff --git a/src/Synercoding.FileFormats.Pdf/Extensions/PdfPageExtensions.cs b/src/Synercoding.FileFormats.Pdf/Extensions/PdfPageExtensions.cs deleted file mode 100644 index 9e1667c..0000000 --- a/src/Synercoding.FileFormats.Pdf/Extensions/PdfPageExtensions.cs +++ /dev/null @@ -1,243 +0,0 @@ -using Synercoding.FileFormats.Pdf.LowLevel.Extensions; -using Synercoding.FileFormats.Pdf.LowLevel.Graphics; -using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors; -using Synercoding.FileFormats.Pdf.LowLevel.Internal; -using Synercoding.FileFormats.Pdf.LowLevel.Operators.State; -using Synercoding.FileFormats.Pdf.LowLevel.Text; -using Synercoding.Primitives; -using System; -using System.IO; - -namespace Synercoding.FileFormats.Pdf.Extensions -{ - /// - /// Extension class for - /// - public static class PdfPageExtensions - { - /// - /// Add text to the page. - /// - /// The page to add the text to. - /// The text to add. - /// The location of the text. - /// The same to chain other calls. - public static PdfPage AddText(this PdfPage page, string text, Point point) - => page.AddText(text, point, new TextState()); - - /// - /// Add text to the page. - /// - /// The page to add the text to. - /// The text to add. - /// The location of the text. - /// Configure the state of the text to place. - /// The same to chain other calls. - public static PdfPage AddText(this PdfPage page, string text, Point point, Action configureState) - { - var state = new TextState(); - configureState(state); - - return page.AddText(text, point, state); - } - - /// - /// Add text to the page. - /// - /// The page to add the text to. - /// The text to add. - /// The location of the text. - /// The state of the text to place. - /// The same to chain other calls. - public static PdfPage AddText(this PdfPage page, string text, Point point, TextState state) - { - page.MarkStdFontAsUsed(state.Font); - - page.ContentStream - .SaveState() - .BeginText() - .SetTextPosition(point) - .SetFontAndSize(state.Font.LookupName, state.FontSize) - .SetTextLeading(state.Leading ?? state.FontSize); - - if (state.Fill is Color fill) - page.ContentStream.SetColorFill(fill); - if (state.Stroke is Color stroke) - page.ContentStream.SetColorStroke(stroke); - if (state.LineWidth is double lineWidth) - page.ContentStream.Write(new LineWidthOperator(lineWidth)); - if (state.LineCap is LineCapStyle lineCapStyle) - page.ContentStream.Write(new LineCapOperator(lineCapStyle)); - if (state.LineJoin is LineJoinStyle lineJoinStyle) - page.ContentStream.Write(new LineJoinOperator(lineJoinStyle)); - if (state.MiterLimit is double miterLimit) - page.ContentStream.Write(new MiterLimitOperator(miterLimit)); - if (state.Dash is Dash dash) - page.ContentStream.Write(new DashOperator(dash.Array, dash.Phase)); - if (state.CharacterSpacing is float charSpace) - page.ContentStream.SetCharacterSpacing(charSpace); - if (state.WordSpacing is float wordSpace) - page.ContentStream.SetWordSpacing(wordSpace); - if (state.HorizontalScaling is float horizontalScaling) - page.ContentStream.SetHorizontalScaling(horizontalScaling); - if (state.TextRise is float textRise) - page.ContentStream.SetTextRise(textRise); - if (state.RenderingMode is TextRenderingMode textRenderingMode) - page.ContentStream.SetTextRenderMode(textRenderingMode); - - page.ContentStream - .ShowText(text) - .EndText() - .RestoreState(); - - return page; - } - - /// - /// Add an image to the pdf page - /// - /// The page to add the image to - /// The image to add - /// The placement matrix - /// The same to chain other calls. - public static PdfPage AddImage(this PdfPage page, Image image, Matrix matrix) - { - page.ContentStream - .SaveState() - .CTM(matrix) - .Paint(page.AddImageToResources(image)) - .RestoreState(); - - return page; - } - - /// - /// Add an image to the pdf page - /// - /// The page to add the image to - /// The image to add - /// The placement rectangle - /// The same to chain other calls. - public static PdfPage AddImage(this PdfPage page, Image image, Rectangle rectangle) - => page.AddImage(image, rectangle.AsPlacementMatrix()); - - /// - /// Add an image to the pdf page - /// - /// The page to add the image to - /// The image to add - /// The placement matrix - /// The same to chain other calls. - public static PdfPage AddImage(this PdfPage page, SixLabors.ImageSharp.Image image, Matrix matrix) - { - page.ContentStream - .SaveState() - .CTM(matrix) - .Paint(page.AddImageToResources(image)) - .RestoreState(); - - return page; - } - - /// - /// Add an image to the pdf page - /// - /// The page to add the image to - /// The image to add - /// The placement rectangle - /// The same to chain other calls. - public static PdfPage AddImage(this PdfPage page, SixLabors.ImageSharp.Image image, Rectangle rectangle) - => page.AddImage(image, rectangle.AsPlacementMatrix()); - - /// - /// Add an image to the pdf page - /// - /// The page to add the image to - /// The image to add - /// The placement matrix - /// The same to chain other calls. - public static PdfPage AddImage(this PdfPage page, Stream imageStream, Matrix matrix) - { - page.ContentStream - .SaveState() - .CTM(matrix) - .Paint(page.AddImageToResources(imageStream)) - .RestoreState(); - - return page; - } - - /// - /// Add an image to the pdf page - /// - /// The page to add the image to - /// The image to add - /// The placement rectangle - /// The same to chain other calls. - public static PdfPage AddImage(this PdfPage page, Stream imageStream, Rectangle rectangle) - => page.AddImage(imageStream, rectangle.AsPlacementMatrix()); - - /// - /// Add an image to the pdf page - /// - /// - /// The is not checked, and is used as is. Make sure only streams that represent a JPG are used. - /// - /// The page to add the image to - /// The image to add - /// The original width of the image - /// The original height of the image - /// The placement matrix - /// The same to chain other calls. - public static PdfPage AddJpgImageUnsafe(this PdfPage page, Stream jpgStream, int originalWidth, int originalHeight, Matrix matrix) - { - page.ContentStream - .SaveState() - .CTM(matrix) - .Paint(page.AddImageToResources(jpgStream, originalWidth, originalHeight)) - .RestoreState(); - - return page; - } - - /// - /// Add an image to the pdf page - /// - /// - /// The is not checked, and is used as is. Make sure only streams that represent a JPG are used. - /// - /// The page to add the image to - /// The image to add - /// The original width of the image - /// The original height of the image - /// The placement rectangle - /// The same to chain other calls. - public static PdfPage AddJpgImageUnsafe(this PdfPage page, Stream jpgStream, int originalWidth, int originalHeight, Rectangle rectangle) - => page.AddJpgImageUnsafe(jpgStream, originalWidth, originalHeight, rectangle.AsPlacementMatrix()); - - /// - /// Add shapes to the pdf page - /// - /// The page to add the shapes to - /// The action painting the shapes - /// The same to chain other calls. - public static PdfPage AddShapes(this PdfPage page, Action paintAction) - => page.AddShapes(paintAction, static (action, context) => action(context)); - - /// - /// Add shapes to the pdf page - /// - /// Type of - /// The page to add the shapes to - /// Data that can be passed to the - /// The action painting the shapes - /// The same to chain other calls. - public static PdfPage AddShapes(this PdfPage page, T data, Action paintAction) - { - using (var context = new ShapeContext(page.ContentStream, page.Resources)) - paintAction(data, context); - - return page; - } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/IContentContext.cs b/src/Synercoding.FileFormats.Pdf/IContentContext.cs new file mode 100644 index 0000000..f857f5c --- /dev/null +++ b/src/Synercoding.FileFormats.Pdf/IContentContext.cs @@ -0,0 +1,37 @@ +using Synercoding.FileFormats.Pdf.LowLevel; +using Synercoding.FileFormats.Pdf.LowLevel.Colors; +using Synercoding.FileFormats.Pdf.LowLevel.Graphics; +using System; +using System.Threading.Tasks; + +namespace Synercoding.FileFormats.Pdf; + +public interface IContentContext + where TSelf : IContentContext +{ + ContentStream RawContentStream { get; } + + TSelf WrapInState(T data, Action contentOperations); + + Task WrapInStateAsync(T data, Func contentOperations); + + Matrix CTM { get; } + Color FillColor { get; } + Color StrokeColor { get; } + double LineWidth { get; } + LineCapStyle LineCap { get; } + LineJoinStyle LineJoin { get; } + double MiterLimit { get; } + Dash DashPattern { get; } + + TSelf ConcatenateMatrix(Matrix matrix); + + TSelf SetStroke(Color stroke); + TSelf SetFill(Color fill); + TSelf SetLineWidth(double lineWidth); + TSelf SetLineCap(LineCapStyle lineCap); + TSelf SetLineJoin(LineJoinStyle lineJoin); + TSelf SetMiterLimit(double miterLimit); + TSelf SetDashPattern(Dash dashPattern); +} + diff --git a/src/Synercoding.FileFormats.Pdf/IPageContentContext.cs b/src/Synercoding.FileFormats.Pdf/IPageContentContext.cs new file mode 100644 index 0000000..db52d63 --- /dev/null +++ b/src/Synercoding.FileFormats.Pdf/IPageContentContext.cs @@ -0,0 +1,15 @@ +using System; +using System.Threading.Tasks; + +namespace Synercoding.FileFormats.Pdf; + +public interface IPageContentContext : IContentContext +{ + IPageContentContext AddImage(Image image); + + IPageContentContext AddText(T data, Action textOperations); + Task AddTextAsync(T data, Func textOperations); + + IPageContentContext AddShapes(T data, Action shapeOperations); + Task AddShapesAsync(T data, Func shapeOperations); +} diff --git a/src/Synercoding.FileFormats.Pdf/IShapeContext.cs b/src/Synercoding.FileFormats.Pdf/IShapeContext.cs index ad72109..96059dd 100644 --- a/src/Synercoding.FileFormats.Pdf/IShapeContext.cs +++ b/src/Synercoding.FileFormats.Pdf/IShapeContext.cs @@ -1,31 +1,106 @@ -using Synercoding.FileFormats.Pdf.LowLevel.Graphics; -using System; +using Synercoding.FileFormats.Pdf.LowLevel; +using Synercoding.Primitives; -namespace Synercoding.FileFormats.Pdf +namespace Synercoding.FileFormats.Pdf; + +public interface IShapeContext : IContentContext { /// - /// Interface representing a context which can be used to draw shapes on a page - /// - public interface IShapeContext - { - /// - /// Change the default graphics state - /// - /// Action used to configure the - /// The calling to support chaining - IShapeContext DefaultState(Action configureState); - - /// - /// Start a new - /// - /// The new object to chain pathing operators - IPath NewPath(); - - /// - /// Start a new with a different graphics state - /// - /// The action used to change the - /// The new object to chain pathing operators - IPath NewPath(Action configureState); - } + /// Begin a new subpath by moving the current point to the coordinates (, ), + /// omitting any connecting line segment. Appends an (m) operator to the content stream + /// + /// The X coordinate of the move + /// The Y coordinate of the move + /// The calling to support chaining operations. + IShapeContext Move(double x, double y); + + + /// + /// Add a line (l) operator to the content stream + /// + /// The X coordinate of the line end point + /// The Y coordinate of the line end point + /// The calling to support chaining operations. + IShapeContext LineTo(double x, double y); + + /// + /// Add a rectangle (re) operator to the content stream + /// + /// The X coordinate of the rectangle + /// The Y coordinate of the rectangle + /// The width of the rectangle + /// The height of the rectangle + /// The calling to support chaining operations. + IShapeContext Rectangle(double x, double y, double width, double height); + + /// + /// Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (, ), + /// using (, ) and (, ) as the Bézier control points. + /// Adds a Cubic Bézier Curve (c) operator to the content stream. + /// + /// The X coordinate of the first control point + /// The Y coordinate of the first control point + /// The X coordinate of the second control point + /// The Y coordinate of the second control point + /// The X coordinate of the endpoint of the curve + /// The Y coordinate of the endpoint of the curve + /// The calling to support chaining operations. + IShapeContext CurveTo(double cpX1, double cpY1, double cpX2, double cpY2, double finalX, double finalY); + + /// + /// Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (, ), + /// using the current point and (, ) as the Bézier control points. + /// Adds a Cubic Bézier Curve (v) operator to the content stream. + /// + /// The X coordinate of the second control point + /// The Y coordinate of the second control point + /// The X coordinate of the endpoint of the curve + /// The Y coordinate of the endpoint of the curve + /// The calling to support chaining operations. + IShapeContext CurveToWithStartAnker(double cpX2, double cpY2, double finalX, double finalY); + + /// + /// Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (, ), + /// using (, ) and (, ) as the Bézier control points. + /// Adds a Cubic Bézier Curve (y) operator to the content stream. + /// + /// The X coordinate of the first control point + /// The Y coordinate of the first control point + /// The X coordinate of the endpoint of the curve + /// The Y coordinate of the endpoint of the curve + /// The calling to support chaining operations. + IShapeContext CurveToWithEndAnker(double cpX1, double cpY1, double finalX, double finalY); + + /// + /// Close the active subpath in this + /// + IShapeContext CloseSubPath(); + + /// + /// + /// + /// + /// + IShapeContext MarkPathForClipping(FillRule fillRule); + + IShapeContext Stroke(); + + /// + /// Close and stroke the path. + /// + /// This operator has the same effect as the sequence () and then (). + /// + IShapeContext CloseSubPathAndStroke(); + + IShapeContext Fill(FillRule fillRule); + + IShapeContext FillThenStroke(FillRule fillRule); + + IShapeContext CloseSubPathFillStroke(FillRule fillRule); + + /// + /// End the path object without filling or stroking it. This operator is a path-painting no-op, used primarily for the side effect of changing the current clipping path. + /// + /// The calling to support chaining operations. + IShapeContext EndPathNoStrokeNoFill(); } diff --git a/src/Synercoding.FileFormats.Pdf/ITextContentContext.cs b/src/Synercoding.FileFormats.Pdf/ITextContentContext.cs new file mode 100644 index 0000000..19d2efb --- /dev/null +++ b/src/Synercoding.FileFormats.Pdf/ITextContentContext.cs @@ -0,0 +1,32 @@ +using Synercoding.FileFormats.Pdf.LowLevel.Text; + +namespace Synercoding.FileFormats.Pdf; + +public interface ITextContentContext : IContentContext +{ + double CharacterSpacing { get; } + double WordSpacing { get; } + double HorizontalScaling { get; } + double TextLeading { get; } + Font? Font { get; } + double? FontSize { get; } + TextRenderingMode TextRenderingMode { get; } + double TextRise { get; } + + ITextContentContext SetCharacterSpacing(double spacing); + ITextContentContext SetWordSpacing(double spacing); + ITextContentContext SetHorizontalScaling(double scaling); + ITextContentContext SetTextLeading(double leading); + ITextContentContext SetFontAndSize(Font font, double size); + ITextContentContext SetTextRenderingMode(TextRenderingMode mode); + ITextContentContext SetTextRise(double textRise); + + ITextContentContext MoveToStartNewLine(); + ITextContentContext MoveToStartNextLine(double offsetX, double offsetY); + ITextContentContext MoveToStartNextLineAndSetLeading(double offsetX, double offsetY); + ITextContentContext ReplaceTextMatrix(Matrix matrix); + + ITextContentContext ShowText(string text); + ITextContentContext ShowTextOnNextLine(string text); + ITextContentContext ShowTextOnNextLine(string text, double wordSpacing, double characterSpacing); +} diff --git a/src/Synercoding.FileFormats.Pdf/Image.cs b/src/Synercoding.FileFormats.Pdf/Image.cs index 71759f1..1b224eb 100644 --- a/src/Synercoding.FileFormats.Pdf/Image.cs +++ b/src/Synercoding.FileFormats.Pdf/Image.cs @@ -1,6 +1,6 @@ using SixLabors.ImageSharp; using Synercoding.FileFormats.Pdf.LowLevel; -using Synercoding.FileFormats.Pdf.LowLevel.Extensions; +using Synercoding.FileFormats.Pdf.LowLevel.Colors.ColorSpaces; using System; using System.IO; @@ -9,7 +9,7 @@ namespace Synercoding.FileFormats.Pdf /// /// Class representing an image inside a pdf /// - public sealed class Image : IPdfObject, IDisposable + public sealed class Image : IDisposable { private bool _disposed; @@ -25,22 +25,47 @@ internal Image(PdfReference id, SixLabors.ImageSharp.Image image) }); Width = image.Width; Height = image.Height; + ColorSpace = DeviceRGB.Instance.Name; + DecodeArray = new double[] { 0, 1, 0, 1, 0, 1 }; ms.Position = 0; RawStream = ms; } - internal Image(PdfReference id, Stream jpgStream, int width, int height) + internal Image(PdfReference id, Stream jpgStream, int width, int height, ColorSpace colorSpace) { Reference = id; Width = width; Height = height; RawStream = jpgStream; + + var (csName, decodeArray) = colorSpace switch + { + DeviceCMYK cmyk => (cmyk.Name, new double[] { 0, 1, 0, 1, 0, 1, 0, 1 }), + DeviceRGB rgb => (rgb.Name, new double[] { 0, 1, 0, 1, 0, 1 }), + _ => throw new ArgumentOutOfRangeException(nameof(colorSpace), $"The provided color space {colorSpace} is currently not supported.") + }; + + ColorSpace = csName; + DecodeArray = decodeArray; + } + + internal Image(PdfReference id, Stream jpgStream, int width, int height, PdfName colorSpace, double[] decodeArray) + { + Reference = id; + + Width = width; + Height = height; + RawStream = jpgStream; + ColorSpace = colorSpace; + DecodeArray = decodeArray; } internal Stream RawStream { get; private set; } - /// + /// + /// A pdf reference object that can be used to reference to this object + /// public PdfReference Reference { get; private set; } /// @@ -53,6 +78,10 @@ internal Image(PdfReference id, Stream jpgStream, int width, int height) /// public int Height { get; } + public PdfName ColorSpace { get; } + + public double[] DecodeArray { get; } + /// public void Dispose() { diff --git a/src/Synercoding.FileFormats.Pdf/Internals/PageContentContext.cs b/src/Synercoding.FileFormats.Pdf/Internals/PageContentContext.cs new file mode 100644 index 0000000..ce51df7 --- /dev/null +++ b/src/Synercoding.FileFormats.Pdf/Internals/PageContentContext.cs @@ -0,0 +1,197 @@ +using Synercoding.FileFormats.Pdf.LowLevel; +using Synercoding.FileFormats.Pdf.LowLevel.Colors; +using Synercoding.FileFormats.Pdf.LowLevel.Graphics; +using Synercoding.Primitives; +using System; +using System.Threading.Tasks; + +namespace Synercoding.FileFormats.Pdf.Internals; + +internal class PageContentContext : IPageContentContext +{ + private readonly PageContentContext? _parent; + + public PageContentContext(ContentStream contentStream) + : this(contentStream, null) + { } + + public PageContentContext(ContentStream contentStream, PageContentContext? parent) + { + RawContentStream = contentStream; + _parent = parent; + } + + public ContentStream RawContentStream { get; } + + private Matrix? _ctm; + public Matrix CTM + => _ctm ?? _parent?.CTM ?? Matrix.Identity; + + private Color? _fill; + public Color FillColor + => _fill ?? _parent?.FillColor ?? PredefinedColors.Black; + + private Color? _stroke; + public Color StrokeColor + => _stroke ?? _parent?.StrokeColor ?? PredefinedColors.Black; + + private double? _lineWidth; + public double LineWidth + => _lineWidth ?? _parent?.LineWidth ?? 1.0; + + private LineCapStyle? _lineCap; + public LineCapStyle LineCap + => _lineCap ?? _parent?.LineCap ?? LineCapStyle.ButtCap; + + private LineJoinStyle? _lineJoin; + public LineJoinStyle LineJoin + => _lineJoin ?? _parent?.LineJoin ?? LineJoinStyle.MiterJoin; + + private double? _miterLimit; + public double MiterLimit + => _miterLimit ?? _parent?.MiterLimit ?? 10.0; + + private Dash? _dashPattern; + public Dash DashPattern + => _dashPattern ?? _parent?.DashPattern ?? new Dash(); + + public IPageContentContext AddImage(Image image) + { + var name = RawContentStream.Resources.AddImage(image); + + RawContentStream.Paint(name); + + return this; + } + + public IPageContentContext AddText(T data, Action textOperations) + { + RawContentStream.BeginText(); + + var state = new TextContentContext(RawContentStream, this); + textOperations(data, state); + + RawContentStream.EndText(); + + return this; + } + + public async Task AddTextAsync(T data, Func textOperations) + { + RawContentStream.BeginText(); + + var state = new TextContentContext(RawContentStream, this); + await textOperations(data, state); + + RawContentStream.EndText(); + + return this; + } + + public IPageContentContext ConcatenateMatrix(Matrix matrix) + { + _ctm = CTM.Multiply(matrix); + RawContentStream.CTM(matrix); + + return this; + } + + public IPageContentContext SetDashPattern(Dash dashPattern) + { + _dashPattern = dashPattern; + + RawContentStream.SetDashPattern(dashPattern); + + return this; + } + + public IPageContentContext SetFill(Color fill) + { + _fill = fill; + + RawContentStream.SetFillColor(fill); + + return this; + } + + public IPageContentContext SetStroke(Color stroke) + { + _stroke = stroke; + + RawContentStream.SetStrokeColor(stroke); + + return this; + } + + public IPageContentContext SetLineCap(LineCapStyle lineCap) + { + _lineCap = lineCap; + + RawContentStream.SetLineCap(lineCap); + + return this; + } + + public IPageContentContext SetLineJoin(LineJoinStyle lineJoin) + { + _lineJoin = lineJoin; + + RawContentStream.SetLineJoin(lineJoin); + + return this; + } + + public IPageContentContext SetLineWidth(double lineWidth) + { + _lineWidth = lineWidth; + + RawContentStream.SetLineWidth(lineWidth); + + return this; + } + + public IPageContentContext SetMiterLimit(double miterLimit) + { + _miterLimit = miterLimit; + + RawContentStream.SetMiterLimit(miterLimit); + + return this; + } + + public IPageContentContext WrapInState(T data, Action contentOperations) + { + RawContentStream.SaveState(); + var state = new PageContentContext(RawContentStream, this); + contentOperations(data, state); + RawContentStream.RestoreState(); + + return this; + } + + public async Task WrapInStateAsync(T data, Func contentOperations) + { + RawContentStream.SaveState(); + var state = new PageContentContext(RawContentStream, this); + await contentOperations(data, state); + RawContentStream.RestoreState(); + + return this; + } + + public IPageContentContext AddShapes(T data, Action shapeOperations) + { + var state = new ShapesContext(RawContentStream, this); + shapeOperations(data, state); + + return this; + } + + public async Task AddShapesAsync(T data, Func shapeOperations) + { + var state = new ShapesContext(RawContentStream, this); + await shapeOperations(data, state); + + return this; + } +} diff --git a/src/Synercoding.FileFormats.Pdf/Internals/Path.cs b/src/Synercoding.FileFormats.Pdf/Internals/Path.cs deleted file mode 100644 index df8ecf6..0000000 --- a/src/Synercoding.FileFormats.Pdf/Internals/Path.cs +++ /dev/null @@ -1,143 +0,0 @@ -using Synercoding.FileFormats.Pdf.LowLevel; -using Synercoding.FileFormats.Pdf.LowLevel.Graphics; -using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors; -using Synercoding.FileFormats.Pdf.LowLevel.Operators.Color; -using Synercoding.FileFormats.Pdf.LowLevel.Operators.Pathing.Construction; -using Synercoding.FileFormats.Pdf.LowLevel.Operators.Pathing.Painting; -using Synercoding.FileFormats.Pdf.LowLevel.Operators.State; -using Synercoding.Primitives; -using System; - -namespace Synercoding.FileFormats.Pdf.Internals -{ - internal class Path : IPath - { - private readonly ContentStream _contentStream; - - public Path(ContentStream contentStream, GraphicsState graphicsState) - { - _contentStream = contentStream; - GraphicsState = graphicsState; - - _startPath(); - } - - internal GraphicsState GraphicsState { get; } - - private void _startPath() - { - _contentStream.SaveState(); - - _contentStream - .Write(new LineWidthOperator(GraphicsState.LineWidth)) - .Write(new LineCapOperator(GraphicsState.LineCap)) - .Write(new LineJoinOperator(GraphicsState.LineJoin)) - .Write(new MiterLimitOperator(GraphicsState.MiterLimit)) - .Write(new DashOperator(GraphicsState.Dash.Array, GraphicsState.Dash.Phase)); - } - - internal void FinishPath() - { - if (GraphicsState.Stroke is not null && GraphicsState.Fill is not null) - { - _contentStream.SetColorFill(GraphicsState.Fill); - _contentStream.SetColorStroke(GraphicsState.Stroke); - - _contentStream.Write(new FillAndStrokeOperator(GraphicsState.FillRule)); - } - else if (GraphicsState.Fill is not null) - { - _contentStream.SetColorFill(GraphicsState.Fill); - _contentStream.Write(new FillOperator(GraphicsState.FillRule)); - } - else if (GraphicsState.Stroke is not null) - { - _contentStream.SetColorStroke(GraphicsState.Stroke); - _contentStream.Write(new StrokeOperator()); - } - else - { - _contentStream.Write(new EndPathOperator()); - } - - _contentStream.RestoreState(); - } - - public void Close() - { - _contentStream.Write(new CloseOperator()); - } - - public IPath CurveTo(double cpX1, double cpY1, double cpX2, double cpY2, double finalX, double finalY) - { - _contentStream.Write(new CubicBezierCurveDualControlPointsOperator(cpX1, cpY1, cpX2, cpY2, finalX, finalY)); - return this; - } - - public IPath CurveTo(Point cp1, Point cp2, Point final) - { - _contentStream.Write(new CubicBezierCurveDualControlPointsOperator(cp1, cp2, final)); - return this; - } - - public IPath CurveToWithEndAnker(double cpX1, double cpY1, double finalX, double finalY) - { - _contentStream.Write(new CubicBezierCurveFinalControlPointsOperator(cpX1, cpY1, finalX, finalY)); - return this; - } - - public IPath CurveToWithEndAnker(Point cp1, Point final) - { - _contentStream.Write(new CubicBezierCurveFinalControlPointsOperator(cp1, final)); - return this; - } - - public IPath CurveToWithStartAnker(double cpX2, double cpY2, double finalX, double finalY) - { - _contentStream.Write(new CubicBezierCurveInitialControlPointsOperator(cpX2, cpY2, finalX, finalY)); - return this; - } - - public IPath CurveToWithStartAnker(Point cp2, Point final) - { - _contentStream.Write(new CubicBezierCurveInitialControlPointsOperator(cp2, final)); - return this; - } - - public IPath LineTo(double x, double y) - { - _contentStream.Write(new LineOperator(x, y)); - return this; - } - - public IPath LineTo(Point point) - { - _contentStream.Write(new LineOperator(point)); - return this; - } - - public IPath Move(double x, double y) - { - _contentStream.Write(new MoveOperator(x, y)); - return this; - } - - public IPath Move(Point point) - { - _contentStream.Write(new MoveOperator(point)); - return this; - } - - public IPath Rectangle(double x, double y, double width, double height) - { - _contentStream.Write(new RectangleOperator(x, y, width, height)); - return this; - } - - public IPath Rectangle(Rectangle rectangle) - { - _contentStream.Write(new RectangleOperator(rectangle)); - return this; - } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/Internals/ShapesContext.cs b/src/Synercoding.FileFormats.Pdf/Internals/ShapesContext.cs new file mode 100644 index 0000000..0444dd3 --- /dev/null +++ b/src/Synercoding.FileFormats.Pdf/Internals/ShapesContext.cs @@ -0,0 +1,241 @@ +using Synercoding.FileFormats.Pdf.LowLevel; +using Synercoding.FileFormats.Pdf.LowLevel.Colors; +using Synercoding.FileFormats.Pdf.LowLevel.Graphics; +using System; +using System.Threading.Tasks; + +namespace Synercoding.FileFormats.Pdf.Internals; + +internal class ShapesContext : IShapeContext +{ + private readonly PageContentContext _parent; + + public ShapesContext(ContentStream contentStream, PageContentContext parent) + { + RawContentStream = contentStream; + _parent = parent; + } + + public ContentStream RawContentStream { get; } + + private Matrix? _ctm; + public Matrix CTM + => _ctm ?? _parent.CTM; + + private Color? _fill; + public Color FillColor + => _fill ?? _parent.FillColor; + + private Color? _stroke; + public Color StrokeColor + => _stroke ?? _parent.StrokeColor; + + private double? _lineWidth; + public double LineWidth + => _lineWidth ?? _parent.LineWidth; + + private LineCapStyle? _lineCap; + public LineCapStyle LineCap + => _lineCap ?? _parent.LineCap; + + private LineJoinStyle? _lineJoin; + public LineJoinStyle LineJoin + => _lineJoin ?? _parent.LineJoin; + + private double? _miterLimit; + public double MiterLimit + => _miterLimit ?? _parent.MiterLimit; + + private Dash? _dashPattern; + public Dash DashPattern + => _dashPattern ?? _parent.DashPattern; + + public IShapeContext ConcatenateMatrix(Matrix matrix) + { + _ctm = CTM.Multiply(matrix); + RawContentStream.CTM(matrix); + + return this; + } + + public IShapeContext SetDashPattern(Dash dashPattern) + { + _dashPattern = dashPattern; + + RawContentStream.SetDashPattern(dashPattern); + + return this; + } + + public IShapeContext SetFill(Color fill) + { + _fill = fill; + + RawContentStream.SetFillColor(fill); + + return this; + } + + public IShapeContext SetStroke(Color stroke) + { + _stroke = stroke; + + RawContentStream.SetStrokeColor(stroke); + + return this; + } + + public IShapeContext SetLineCap(LineCapStyle lineCap) + { + _lineCap = lineCap; + + RawContentStream.SetLineCap(lineCap); + + return this; + } + + public IShapeContext SetLineJoin(LineJoinStyle lineJoin) + { + _lineJoin = lineJoin; + + RawContentStream.SetLineJoin(lineJoin); + + return this; + } + + public IShapeContext SetLineWidth(double lineWidth) + { + _lineWidth = lineWidth; + + RawContentStream.SetLineWidth(lineWidth); + + return this; + } + + public IShapeContext SetMiterLimit(double miterLimit) + { + _miterLimit = miterLimit; + + RawContentStream.SetMiterLimit(miterLimit); + + return this; + } + + public IShapeContext WrapInState(T data, Action contentOperations) + { + RawContentStream.SaveState(); + var state = new WrappedShapesContext(RawContentStream, this); + contentOperations(data, state); + RawContentStream.RestoreState(); + + return this; + } + + public async Task WrapInStateAsync(T data, Func contentOperations) + { + RawContentStream.SaveState(); + var state = new WrappedShapesContext(RawContentStream, this); + await contentOperations(data, state); + RawContentStream.RestoreState(); + + return this; + } + + public IShapeContext Move(double x, double y) + { + RawContentStream.MoveTo(x, y); + + return this; + } + + public IShapeContext LineTo(double x, double y) + { + RawContentStream.LineTo(x, y); + + return this; + } + + public IShapeContext Rectangle(double x, double y, double width, double height) + { + RawContentStream.Rectangle(x, y, width, height); + + return this; + } + + public IShapeContext CurveTo(double cpX1, double cpY1, double cpX2, double cpY2, double finalX, double finalY) + { + RawContentStream.CubicBezierCurve(cpX1, cpY1, cpX2, cpY2, finalX, finalY); + + return this; + } + + public IShapeContext CurveToWithStartAnker(double cpX2, double cpY2, double finalX, double finalY) + { + RawContentStream.CubicBezierCurveV(cpX2, cpY2, finalX, finalY); + + return this; + } + + public IShapeContext CurveToWithEndAnker(double cpX1, double cpY1, double finalX, double finalY) + { + RawContentStream.CubicBezierCurveY(cpX1, cpY1, finalX, finalY); + + return this; + } + + public IShapeContext CloseSubPath() + { + RawContentStream.Close(); + + return this; + } + + public IShapeContext MarkPathForClipping(FillRule fillRule) + { + RawContentStream.Clip(fillRule); + + return this; + } + + public IShapeContext Stroke() + { + RawContentStream.Stroke(); + + return this; + } + + public IShapeContext CloseSubPathAndStroke() + { + RawContentStream.CloseAndStroke(); + + return this; + } + + public IShapeContext Fill(FillRule fillRule) + { + RawContentStream.Fill(fillRule); + + return this; + } + + public IShapeContext FillThenStroke(FillRule fillRule) + { + RawContentStream.FillAndStroke(fillRule); + + return this; + } + + public IShapeContext CloseSubPathFillStroke(FillRule fillRule) + { + RawContentStream.CloseFillAndStroke(fillRule); + + return this; + } + + public IShapeContext EndPathNoStrokeNoFill() + { + RawContentStream.EndPath(); + + return this; + } +} diff --git a/src/Synercoding.FileFormats.Pdf/Internals/StringHelper.cs b/src/Synercoding.FileFormats.Pdf/Internals/StringHelper.cs new file mode 100644 index 0000000..81f2d1d --- /dev/null +++ b/src/Synercoding.FileFormats.Pdf/Internals/StringHelper.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Text; + +namespace Synercoding.FileFormats.Pdf.Internals; +internal static class StringHelper +{ + public static IEnumerable SplitOnNewLines(string text) + { + var builder = new StringBuilder(text.Length); + for (int i = 0; i < text.Length; i++) + { + var c = text[i]; + var n = i < text.Length - 1 + ? text[i + 1] + : '0'; + if (( c == '\r' && n == '\n' ) || c == '\r' || c == '\n') + { + yield return builder.ToString(); + builder.Clear(); + + if (n == '\n') + i++; // extra skip to also skip the \n + } + else + { + builder.Append(c); + } + } + + yield return builder.ToString(); + } +} diff --git a/src/Synercoding.FileFormats.Pdf/Internals/TextContentContext.cs b/src/Synercoding.FileFormats.Pdf/Internals/TextContentContext.cs new file mode 100644 index 0000000..bed7dba --- /dev/null +++ b/src/Synercoding.FileFormats.Pdf/Internals/TextContentContext.cs @@ -0,0 +1,290 @@ +using Synercoding.FileFormats.Pdf.LowLevel; +using Synercoding.FileFormats.Pdf.LowLevel.Colors; +using Synercoding.FileFormats.Pdf.LowLevel.Graphics; +using Synercoding.FileFormats.Pdf.LowLevel.Text; +using System; +using System.Threading.Tasks; + +namespace Synercoding.FileFormats.Pdf.Internals; + +internal class TextContentContext : ITextContentContext +{ + private readonly IPageContentContext _parent; + + public TextContentContext(ContentStream contentStream, IPageContentContext parent) + { + RawContentStream = contentStream; + _parent = parent; + } + + public ContentStream RawContentStream { get; } + + private Matrix? _ctm; + public Matrix CTM + => _ctm ?? _parent?.CTM ?? Matrix.Identity; + + private Color? _fill; + public Color FillColor + => _fill ?? _parent?.FillColor ?? PredefinedColors.Black; + + private Color? _stroke; + public Color StrokeColor + => _stroke ?? _parent?.StrokeColor ?? PredefinedColors.Black; + + private double? _lineWidth; + public double LineWidth + => _lineWidth ?? _parent?.LineWidth ?? 1.0; + + private LineCapStyle? _lineCap; + public LineCapStyle LineCap + => _lineCap ?? _parent?.LineCap ?? LineCapStyle.ButtCap; + + private LineJoinStyle? _lineJoin; + public LineJoinStyle LineJoin + => _lineJoin ?? _parent?.LineJoin ?? LineJoinStyle.MiterJoin; + + private double? _miterLimit; + public double MiterLimit + => _miterLimit ?? _parent?.MiterLimit ?? 10.0; + + private Dash? _dashPattern; + public Dash DashPattern + => _dashPattern ?? _parent?.DashPattern ?? new Dash(); + + private double? _characterSpacing; + public double CharacterSpacing + => _characterSpacing ?? 0.0; + + private double? _wordSpacing; + public double WordSpacing + => _wordSpacing ?? 0.0; + + private double? _horizontalScale; + public double HorizontalScaling + => _horizontalScale ?? 100.0; + + private double? _textLeading; + public double TextLeading + => _textLeading ?? 0.0; + + public Font? Font { get; private set; } + + public double? FontSize { get; private set; } + + private TextRenderingMode? _textRenderingMode; + public TextRenderingMode TextRenderingMode + => _textRenderingMode ?? TextRenderingMode.Fill; + + private double? _textRise; + public double TextRise + => _textRise ?? 0.0; + + public ITextContentContext ConcatenateMatrix(Matrix matrix) + { + _ctm = CTM.Multiply(matrix); + RawContentStream.CTM(matrix); + + return this; + } + + public ITextContentContext SetDashPattern(Dash dashPattern) + { + _dashPattern = dashPattern; + + RawContentStream.SetDashPattern(dashPattern); + + return this; + } + + public ITextContentContext SetFill(Color fill) + { + _fill = fill; + + RawContentStream.SetFillColor(fill); + + return this; + } + + public ITextContentContext SetStroke(Color stroke) + { + _stroke = stroke; + + RawContentStream.SetStrokeColor(stroke); + + return this; + } + + public ITextContentContext SetLineCap(LineCapStyle lineCap) + { + _lineCap = lineCap; + + RawContentStream.SetLineCap(lineCap); + + return this; + } + + public ITextContentContext SetLineJoin(LineJoinStyle lineJoin) + { + _lineJoin = lineJoin; + + RawContentStream.SetLineJoin(lineJoin); + + return this; + } + + public ITextContentContext SetLineWidth(double lineWidth) + { + _lineWidth = lineWidth; + + RawContentStream.SetLineWidth(lineWidth); + + return this; + } + + public ITextContentContext SetMiterLimit(double miterLimit) + { + _miterLimit = miterLimit; + + RawContentStream.SetMiterLimit(miterLimit); + + return this; + } + + public ITextContentContext WrapInState(T data, Action contentOperations) + { + RawContentStream.SaveState(); + var state = new WrappedTextContentContext(RawContentStream, this); + contentOperations(data, state); + RawContentStream.RestoreState(); + + return this; + } + + public async Task WrapInStateAsync(T data, Func contentOperations) + { + RawContentStream.SaveState(); + var state = new WrappedTextContentContext(RawContentStream, this); + await contentOperations(data, state); + RawContentStream.RestoreState(); + + return this; + } + + public ITextContentContext SetCharacterSpacing(double spacing) + { + _characterSpacing = spacing; + + RawContentStream.SetCharacterSpacing(spacing); + + return this; + } + + public ITextContentContext SetWordSpacing(double spacing) + { + _wordSpacing = spacing; + + RawContentStream.SetWordSpacing(spacing); + + return this; + } + + public ITextContentContext SetHorizontalScaling(double scaling) + { + _horizontalScale = scaling; + + RawContentStream.SetHorizontalScaling(scaling); + + return this; + } + + public ITextContentContext SetTextLeading(double leading) + { + _textLeading = leading; + + RawContentStream.SetTextLeading(leading); + + return this; + } + + public ITextContentContext SetFontAndSize(Font font, double size) + { + var fontName = font switch + { + Type1StandardFont stdFont => RawContentStream.Resources.AddStandardFont(stdFont), + _ => throw new NotImplementedException($"Font of type {font?.GetType()} is currently not implemented.") + }; + + Font = font; + FontSize = size; + RawContentStream.SetFontAndSize(fontName, size); + + return this; + } + + public ITextContentContext SetTextRenderingMode(TextRenderingMode mode) + { + _textRenderingMode = mode; + + RawContentStream.SetTextRenderMode(mode); + + return this; + } + + public ITextContentContext SetTextRise(double rise) + { + _textRise = rise; + + RawContentStream.SetTextRise(rise); + + return this; + } + + public ITextContentContext MoveToStartNewLine() + { + RawContentStream.MoveNextLine(); + + return this; + } + + public ITextContentContext MoveToStartNextLine(double offsetX, double offsetY) + { + RawContentStream.MoveNextLineAndOffsetTextPosition(offsetX, offsetY); + + return this; + } + + public ITextContentContext MoveToStartNextLineAndSetLeading(double offsetX, double offsetY) + { + RawContentStream.MoveNextLineSetLeadingAndOffsetTextPosition(offsetX, offsetY); + + return this; + } + + public ITextContentContext ReplaceTextMatrix(Matrix matrix) + { + RawContentStream.Tm(matrix); + + return this; + } + + public ITextContentContext ShowText(string text) + { + RawContentStream.ShowTextTj(text); + + return this; + } + + public ITextContentContext ShowTextOnNextLine(string text) + { + RawContentStream.MoveNextLineShowText(text); + + return this; + } + + public ITextContentContext ShowTextOnNextLine(string text, double wordSpacing, double characterSpacing) + { + RawContentStream.MoveNextLineShowText(text, wordSpacing, characterSpacing); + + return this; + } +} diff --git a/src/Synercoding.FileFormats.Pdf/Internals/WrappedShapesContext.cs b/src/Synercoding.FileFormats.Pdf/Internals/WrappedShapesContext.cs new file mode 100644 index 0000000..b1f2bc5 --- /dev/null +++ b/src/Synercoding.FileFormats.Pdf/Internals/WrappedShapesContext.cs @@ -0,0 +1,241 @@ +using Synercoding.FileFormats.Pdf.LowLevel; +using Synercoding.FileFormats.Pdf.LowLevel.Colors; +using Synercoding.FileFormats.Pdf.LowLevel.Graphics; +using System; +using System.Threading.Tasks; + +namespace Synercoding.FileFormats.Pdf.Internals; + +internal class WrappedShapesContext : IShapeContext +{ + private readonly IShapeContext _parent; + + public WrappedShapesContext(ContentStream contentStream, IShapeContext parent) + { + RawContentStream = contentStream; + _parent = parent; + } + + public ContentStream RawContentStream { get; } + + private Matrix? _ctm; + public Matrix CTM + => _ctm ?? _parent.CTM; + + private Color? _fill; + public Color FillColor + => _fill ?? _parent.FillColor; + + private Color? _stroke; + public Color StrokeColor + => _stroke ?? _parent.StrokeColor; + + private double? _lineWidth; + public double LineWidth + => _lineWidth ?? _parent.LineWidth; + + private LineCapStyle? _lineCap; + public LineCapStyle LineCap + => _lineCap ?? _parent.LineCap; + + private LineJoinStyle? _lineJoin; + public LineJoinStyle LineJoin + => _lineJoin ?? _parent.LineJoin; + + private double? _miterLimit; + public double MiterLimit + => _miterLimit ?? _parent.MiterLimit; + + private Dash? _dashPattern; + public Dash DashPattern + => _dashPattern ?? _parent.DashPattern; + + public IShapeContext ConcatenateMatrix(Matrix matrix) + { + _ctm = CTM.Multiply(matrix); + RawContentStream.CTM(matrix); + + return this; + } + + public IShapeContext SetDashPattern(Dash dashPattern) + { + _dashPattern = dashPattern; + + RawContentStream.SetDashPattern(dashPattern); + + return this; + } + + public IShapeContext SetFill(Color fill) + { + _fill = fill; + + RawContentStream.SetFillColor(fill); + + return this; + } + + public IShapeContext SetStroke(Color stroke) + { + _stroke = stroke; + + RawContentStream.SetStrokeColor(stroke); + + return this; + } + + public IShapeContext SetLineCap(LineCapStyle lineCap) + { + _lineCap = lineCap; + + RawContentStream.SetLineCap(lineCap); + + return this; + } + + public IShapeContext SetLineJoin(LineJoinStyle lineJoin) + { + _lineJoin = lineJoin; + + RawContentStream.SetLineJoin(lineJoin); + + return this; + } + + public IShapeContext SetLineWidth(double lineWidth) + { + _lineWidth = lineWidth; + + RawContentStream.SetLineWidth(lineWidth); + + return this; + } + + public IShapeContext SetMiterLimit(double miterLimit) + { + _miterLimit = miterLimit; + + RawContentStream.SetMiterLimit(miterLimit); + + return this; + } + + public IShapeContext WrapInState(T data, Action contentOperations) + { + RawContentStream.SaveState(); + var state = new WrappedShapesContext(RawContentStream, this); + contentOperations(data, state); + RawContentStream.RestoreState(); + + return this; + } + + public async Task WrapInStateAsync(T data, Func contentOperations) + { + RawContentStream.SaveState(); + var state = new WrappedShapesContext(RawContentStream, this); + await contentOperations(data, state); + RawContentStream.RestoreState(); + + return this; + } + + public IShapeContext Move(double x, double y) + { + RawContentStream.MoveTo(x, y); + + return this; + } + + public IShapeContext LineTo(double x, double y) + { + RawContentStream.LineTo(x, y); + + return this; + } + + public IShapeContext Rectangle(double x, double y, double width, double height) + { + RawContentStream.Rectangle(x, y, width, height); + + return this; + } + + public IShapeContext CurveTo(double cpX1, double cpY1, double cpX2, double cpY2, double finalX, double finalY) + { + RawContentStream.CubicBezierCurve(cpX1, cpY1, cpX2, cpY2, finalX, finalY); + + return this; + } + + public IShapeContext CurveToWithStartAnker(double cpX2, double cpY2, double finalX, double finalY) + { + RawContentStream.CubicBezierCurveV(cpX2, cpY2, finalX, finalY); + + return this; + } + + public IShapeContext CurveToWithEndAnker(double cpX1, double cpY1, double finalX, double finalY) + { + RawContentStream.CubicBezierCurveY(cpX1, cpY1, finalX, finalY); + + return this; + } + + public IShapeContext CloseSubPath() + { + RawContentStream.Close(); + + return this; + } + + public IShapeContext MarkPathForClipping(FillRule fillRule) + { + RawContentStream.Clip(fillRule); + + return this; + } + + public IShapeContext Stroke() + { + RawContentStream.Stroke(); + + return this; + } + + public IShapeContext CloseSubPathAndStroke() + { + RawContentStream.CloseAndStroke(); + + return this; + } + + public IShapeContext Fill(FillRule fillRule) + { + RawContentStream.Fill(fillRule); + + return this; + } + + public IShapeContext FillThenStroke(FillRule fillRule) + { + RawContentStream.FillAndStroke(fillRule); + + return this; + } + + public IShapeContext CloseSubPathFillStroke(FillRule fillRule) + { + RawContentStream.CloseFillAndStroke(fillRule); + + return this; + } + + public IShapeContext EndPathNoStrokeNoFill() + { + RawContentStream.EndPath(); + + return this; + } +} diff --git a/src/Synercoding.FileFormats.Pdf/Internals/WrappedTextContentContext.cs b/src/Synercoding.FileFormats.Pdf/Internals/WrappedTextContentContext.cs new file mode 100644 index 0000000..81b9a7d --- /dev/null +++ b/src/Synercoding.FileFormats.Pdf/Internals/WrappedTextContentContext.cs @@ -0,0 +1,294 @@ +using Synercoding.FileFormats.Pdf.LowLevel; +using Synercoding.FileFormats.Pdf.LowLevel.Colors; +using Synercoding.FileFormats.Pdf.LowLevel.Graphics; +using Synercoding.FileFormats.Pdf.LowLevel.Text; +using System; +using System.Threading.Tasks; + +namespace Synercoding.FileFormats.Pdf.Internals; + +internal class WrappedTextContentContext : ITextContentContext +{ + private readonly ITextContentContext _parent; + + public WrappedTextContentContext(ContentStream contentStream, ITextContentContext parent) + { + RawContentStream = contentStream; + _parent = parent; + } + + public ContentStream RawContentStream { get; } + + private Matrix? _ctm; + public Matrix CTM + => _ctm ?? _parent?.CTM ?? Matrix.Identity; + + private Color? _fill; + public Color FillColor + => _fill ?? _parent?.FillColor ?? PredefinedColors.Black; + + private Color? _stroke; + public Color StrokeColor + => _stroke ?? _parent?.StrokeColor ?? PredefinedColors.Black; + + private double? _lineWidth; + public double LineWidth + => _lineWidth ?? _parent?.LineWidth ?? 1.0; + + private LineCapStyle? _lineCap; + public LineCapStyle LineCap + => _lineCap ?? _parent?.LineCap ?? LineCapStyle.ButtCap; + + private LineJoinStyle? _lineJoin; + public LineJoinStyle LineJoin + => _lineJoin ?? _parent?.LineJoin ?? LineJoinStyle.MiterJoin; + + private double? _miterLimit; + public double MiterLimit + => _miterLimit ?? _parent?.MiterLimit ?? 10.0; + + private Dash? _dashPattern; + public Dash DashPattern + => _dashPattern ?? _parent?.DashPattern ?? new Dash(); + + private double? _characterSpacing; + public double CharacterSpacing + => _characterSpacing ?? _parent.CharacterSpacing; + + private double? _wordSpacing; + public double WordSpacing + => _wordSpacing ?? _parent.WordSpacing; + + private double? _horizontalScale; + public double HorizontalScaling + => _horizontalScale ?? _parent.HorizontalScaling; + + private double? _textLeading; + public double TextLeading + => _textLeading ?? _parent.TextLeading; + + private Font? _font; + public Font? Font + => _font ?? _parent.Font; + + private double? _fontSize; + public double? FontSize + => _fontSize ?? _parent.FontSize; + + private TextRenderingMode? _textRenderingMode; + public TextRenderingMode TextRenderingMode + => _textRenderingMode ?? _parent.TextRenderingMode; + + private double? _textRise; + public double TextRise + => _textRise ?? _parent.TextRise; + + public ITextContentContext ConcatenateMatrix(Matrix matrix) + { + _ctm = CTM.Multiply(matrix); + RawContentStream.CTM(matrix); + + return this; + } + + public ITextContentContext SetDashPattern(Dash dashPattern) + { + _dashPattern = dashPattern; + + RawContentStream.SetDashPattern(dashPattern); + + return this; + } + + public ITextContentContext SetFill(Color fill) + { + _fill = fill; + + RawContentStream.SetFillColor(fill); + + return this; + } + + public ITextContentContext SetStroke(Color stroke) + { + _stroke = stroke; + + RawContentStream.SetStrokeColor(stroke); + + return this; + } + + public ITextContentContext SetLineCap(LineCapStyle lineCap) + { + _lineCap = lineCap; + + RawContentStream.SetLineCap(lineCap); + + return this; + } + + public ITextContentContext SetLineJoin(LineJoinStyle lineJoin) + { + _lineJoin = lineJoin; + + RawContentStream.SetLineJoin(lineJoin); + + return this; + } + + public ITextContentContext SetLineWidth(double lineWidth) + { + _lineWidth = lineWidth; + + RawContentStream.SetLineWidth(lineWidth); + + return this; + } + + public ITextContentContext SetMiterLimit(double miterLimit) + { + _miterLimit = miterLimit; + + RawContentStream.SetMiterLimit(miterLimit); + + return this; + } + + public ITextContentContext WrapInState(T data, Action contentOperations) + { + RawContentStream.SaveState(); + var state = new WrappedTextContentContext(RawContentStream, this); + contentOperations(data, state); + RawContentStream.RestoreState(); + + return this; + } + + public async Task WrapInStateAsync(T data, Func contentOperations) + { + RawContentStream.SaveState(); + var state = new WrappedTextContentContext(RawContentStream, this); + await contentOperations(data, state); + RawContentStream.RestoreState(); + + return this; + } + + public ITextContentContext SetCharacterSpacing(double spacing) + { + _characterSpacing = spacing; + + RawContentStream.SetCharacterSpacing(spacing); + + return this; + } + + public ITextContentContext SetWordSpacing(double spacing) + { + _wordSpacing = spacing; + + RawContentStream.SetWordSpacing(spacing); + + return this; + } + + public ITextContentContext SetHorizontalScaling(double scaling) + { + _horizontalScale = scaling; + + RawContentStream.SetHorizontalScaling(scaling); + + return this; + } + + public ITextContentContext SetTextLeading(double leading) + { + _textLeading = leading; + + RawContentStream.SetTextLeading(leading); + + return this; + } + + public ITextContentContext SetFontAndSize(Font font, double size) + { + var fontName = font switch + { + Type1StandardFont stdFont => RawContentStream.Resources.AddStandardFont(stdFont), + _ => throw new NotImplementedException($"Font of type {font?.GetType()} is currently not implemented.") + }; + + _font = font; + _fontSize = size; + RawContentStream.SetFontAndSize(fontName, size); + + return this; + } + + public ITextContentContext SetTextRenderingMode(TextRenderingMode mode) + { + _textRenderingMode = mode; + + RawContentStream.SetTextRenderMode(mode); + + return this; + } + + public ITextContentContext SetTextRise(double rise) + { + _textRise = rise; + + RawContentStream.SetTextRise(rise); + + return this; + } + + public ITextContentContext MoveToStartNewLine() + { + RawContentStream.MoveNextLine(); + + return this; + } + + public ITextContentContext MoveToStartNextLine(double offsetX, double offsetY) + { + RawContentStream.MoveNextLineAndOffsetTextPosition(offsetX, offsetY); + + return this; + } + + public ITextContentContext MoveToStartNextLineAndSetLeading(double offsetX, double offsetY) + { + RawContentStream.MoveNextLineSetLeadingAndOffsetTextPosition(offsetX, offsetY); + + return this; + } + + public ITextContentContext ReplaceTextMatrix(Matrix matrix) + { + RawContentStream.Tm(matrix); + + return this; + } + + public ITextContentContext ShowText(string text) + { + RawContentStream.ShowTextTj(text); + + return this; + } + + public ITextContentContext ShowTextOnNextLine(string text) + { + RawContentStream.MoveNextLineShowText(text); + + return this; + } + + public ITextContentContext ShowTextOnNextLine(string text, double wordSpacing, double characterSpacing) + { + RawContentStream.MoveNextLineShowText(text, wordSpacing, characterSpacing); + + return this; + } +} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/CmykColor.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/CmykColor.cs similarity index 96% rename from src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/CmykColor.cs rename to src/Synercoding.FileFormats.Pdf/LowLevel/Colors/CmykColor.cs index dd70b99..ccb8d55 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/CmykColor.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/CmykColor.cs @@ -1,7 +1,7 @@ -using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors.ColorSpaces; +using Synercoding.FileFormats.Pdf.LowLevel.Colors.ColorSpaces; using System; -namespace Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors +namespace Synercoding.FileFormats.Pdf.LowLevel.Colors { /// /// Class representing a CMYK color diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/Color.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/Color.cs similarity index 88% rename from src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/Color.cs rename to src/Synercoding.FileFormats.Pdf/LowLevel/Colors/Color.cs index 2d9e053..caff944 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/Color.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/Color.cs @@ -1,7 +1,7 @@ -using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors.ColorSpaces; +using Synercoding.FileFormats.Pdf.LowLevel.Colors.ColorSpaces; using System; -namespace Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors +namespace Synercoding.FileFormats.Pdf.LowLevel.Colors { /// /// Base class of all s. @@ -9,7 +9,7 @@ namespace Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors public abstract class Color : IEquatable { /// - /// The of this . + /// The of this . /// public abstract ColorSpace Colorspace { get; } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/ColorSpaces/ColorSpace.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/ColorSpaces/ColorSpace.cs similarity index 89% rename from src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/ColorSpaces/ColorSpace.cs rename to src/Synercoding.FileFormats.Pdf/LowLevel/Colors/ColorSpaces/ColorSpace.cs index 7f643ff..32c4b46 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/ColorSpaces/ColorSpace.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/ColorSpaces/ColorSpace.cs @@ -1,6 +1,6 @@ using System; -namespace Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors.ColorSpaces; +namespace Synercoding.FileFormats.Pdf.LowLevel.Colors.ColorSpaces; /// /// Class representing a colorspace diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/ColorSpaces/DeviceCMYK.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/ColorSpaces/DeviceCMYK.cs similarity index 92% rename from src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/ColorSpaces/DeviceCMYK.cs rename to src/Synercoding.FileFormats.Pdf/LowLevel/Colors/ColorSpaces/DeviceCMYK.cs index 2030552..ab1a463 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/ColorSpaces/DeviceCMYK.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/ColorSpaces/DeviceCMYK.cs @@ -1,6 +1,6 @@ using System; -namespace Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors.ColorSpaces; +namespace Synercoding.FileFormats.Pdf.LowLevel.Colors.ColorSpaces; /// /// Class representing DeviceCMYK colorspace @@ -37,4 +37,4 @@ public override bool Equals(ColorSpace? other) /// public override int GetHashCode() => HashCode.Combine(Instance, Components); -} \ No newline at end of file +} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/ColorSpaces/DeviceGray.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/ColorSpaces/DeviceGray.cs similarity index 92% rename from src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/ColorSpaces/DeviceGray.cs rename to src/Synercoding.FileFormats.Pdf/LowLevel/Colors/ColorSpaces/DeviceGray.cs index cbba8ed..b8899c6 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/ColorSpaces/DeviceGray.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/ColorSpaces/DeviceGray.cs @@ -1,6 +1,6 @@ using System; -namespace Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors.ColorSpaces; +namespace Synercoding.FileFormats.Pdf.LowLevel.Colors.ColorSpaces; /// /// Class representing DeviceGray colorspace diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/ColorSpaces/DeviceRGB.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/ColorSpaces/DeviceRGB.cs similarity index 88% rename from src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/ColorSpaces/DeviceRGB.cs rename to src/Synercoding.FileFormats.Pdf/LowLevel/Colors/ColorSpaces/DeviceRGB.cs index 2ccc4fe..4395d48 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/ColorSpaces/DeviceRGB.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/ColorSpaces/DeviceRGB.cs @@ -1,6 +1,6 @@ using System; -namespace Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors.ColorSpaces; +namespace Synercoding.FileFormats.Pdf.LowLevel.Colors.ColorSpaces; /// /// Class representing DeviceRGB colorspace @@ -20,7 +20,7 @@ public override int Components /// public override PdfName Name - => PdfName.Get("DeviceRgb"); + => PdfName.Get("DeviceRGB"); /// public bool Equals(DeviceRGB? other) diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/ColorSpaces/Separation.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/ColorSpaces/Separation.cs similarity index 94% rename from src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/ColorSpaces/Separation.cs rename to src/Synercoding.FileFormats.Pdf/LowLevel/Colors/ColorSpaces/Separation.cs index e42f633..fa84040 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/ColorSpaces/Separation.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/ColorSpaces/Separation.cs @@ -1,6 +1,6 @@ using System; -namespace Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors.ColorSpaces; +namespace Synercoding.FileFormats.Pdf.LowLevel.Colors.ColorSpaces; /// /// Class representing a separation colorspace diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/GrayColor.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/GrayColor.cs similarity index 92% rename from src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/GrayColor.cs rename to src/Synercoding.FileFormats.Pdf/LowLevel/Colors/GrayColor.cs index 5764a80..7d64309 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/GrayColor.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/GrayColor.cs @@ -1,7 +1,7 @@ -using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors.ColorSpaces; +using Synercoding.FileFormats.Pdf.LowLevel.Colors.ColorSpaces; using System; -namespace Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors +namespace Synercoding.FileFormats.Pdf.LowLevel.Colors { /// /// Class representing a gray color diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/PredefinedColors.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/PredefinedColors.cs similarity index 96% rename from src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/PredefinedColors.cs rename to src/Synercoding.FileFormats.Pdf/LowLevel/Colors/PredefinedColors.cs index 21a0842..ced3eb2 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/PredefinedColors.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/PredefinedColors.cs @@ -1,4 +1,4 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors +namespace Synercoding.FileFormats.Pdf.LowLevel.Colors { /// /// Class with predefined colors diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/RgbColor.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/RgbColor.cs similarity index 96% rename from src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/RgbColor.cs rename to src/Synercoding.FileFormats.Pdf/LowLevel/Colors/RgbColor.cs index 0a54600..16212f7 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/RgbColor.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/RgbColor.cs @@ -1,7 +1,7 @@ -using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors.ColorSpaces; +using Synercoding.FileFormats.Pdf.LowLevel.Colors.ColorSpaces; using System; -namespace Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors +namespace Synercoding.FileFormats.Pdf.LowLevel.Colors { /// /// Class representing a RGB color diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/SpotColor.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/SpotColor.cs similarity index 90% rename from src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/SpotColor.cs rename to src/Synercoding.FileFormats.Pdf/LowLevel/Colors/SpotColor.cs index 1ebf9c5..0c66abb 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Colors/SpotColor.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/SpotColor.cs @@ -1,7 +1,7 @@ -using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors.ColorSpaces; +using Synercoding.FileFormats.Pdf.LowLevel.Colors.ColorSpaces; using System; -namespace Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors; +namespace Synercoding.FileFormats.Pdf.LowLevel.Colors; /// /// Class representing a spotcolor @@ -16,7 +16,7 @@ public class SpotColor : Color, IEquatable /// Throws if the < 0 or > 1 public SpotColor(Separation separation, double tint) { - if(tint < 0 || tint > 1) + if (tint < 0 || tint > 1) throw new ArgumentOutOfRangeException(nameof(tint), $"Parameter {nameof(tint)} must be between 0.0 and 1.0 (inclusive)."); Separation = separation; Tint = tint; diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/ContentStream.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/ContentStream.cs index 0c1a4c5..9429a6d 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/ContentStream.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/ContentStream.cs @@ -1,17 +1,15 @@ using SixLabors.ImageSharp; +using Synercoding.FileFormats.Pdf.LowLevel.Colors; using Synercoding.FileFormats.Pdf.LowLevel.Extensions; -using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors; +using Synercoding.FileFormats.Pdf.LowLevel.Graphics; using Synercoding.FileFormats.Pdf.LowLevel.Internal; -using Synercoding.FileFormats.Pdf.LowLevel.Operators.Color; -using Synercoding.FileFormats.Pdf.LowLevel.Operators.Pathing.Construction; -using Synercoding.FileFormats.Pdf.LowLevel.Operators.Pathing.Painting; -using Synercoding.FileFormats.Pdf.LowLevel.Operators.State; using Synercoding.FileFormats.Pdf.LowLevel.Text; using Synercoding.Primitives; using Synercoding.Primitives.Extensions; using System; using System.IO; -using Color = Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors.Color; +using System.Linq; +using Color = Synercoding.FileFormats.Pdf.LowLevel.Colors.Color; using Point = Synercoding.Primitives.Point; namespace Synercoding.FileFormats.Pdf.LowLevel @@ -19,7 +17,7 @@ namespace Synercoding.FileFormats.Pdf.LowLevel /// /// Class to represent a content stream /// - public sealed class ContentStream : IPdfObject, IDisposable + public sealed class ContentStream : IDisposable { private const string UNKNOWN_FILL_RULE = "Unknown fill rule"; @@ -35,7 +33,9 @@ internal ContentStream(PdfReference id, PageResources pageResources) internal PageResources Resources { get; } - /// + /// + /// A pdf reference object that can be used to reference to this object + /// public PdfReference Reference { get; } /// @@ -111,7 +111,7 @@ public ContentStream BeginText() /// The font to write to the stream /// The font size to use /// The to support chaining operations. - public ContentStream SetFontAndSize(PdfName font, float size) + public ContentStream SetFontAndSize(PdfName font, double size) { InnerStream .Write(font) @@ -129,7 +129,7 @@ public ContentStream SetFontAndSize(PdfName font, float size) /// /// The leading value to write. /// The to support chaining operations. - public ContentStream SetTextLeading(float leading) + public ContentStream SetTextLeading(double leading) { InnerStream .Write(leading) @@ -145,7 +145,7 @@ public ContentStream SetTextLeading(float leading) /// /// The character spacing value. /// The to support chaining operations. - public ContentStream SetCharacterSpacing(float characterSpace) + public ContentStream SetCharacterSpacing(double characterSpace) { InnerStream .Write(characterSpace) @@ -161,7 +161,7 @@ public ContentStream SetCharacterSpacing(float characterSpace) /// /// The horizontal scaling value. /// The to support chaining operations. - public ContentStream SetHorizontalScaling(float horizontalScaling) + public ContentStream SetHorizontalScaling(double horizontalScaling) { InnerStream .Write(horizontalScaling) @@ -177,7 +177,7 @@ public ContentStream SetHorizontalScaling(float horizontalScaling) /// /// The text rise value. /// The to support chaining operations. - public ContentStream SetTextRise(float textRise) + public ContentStream SetTextRise(double textRise) { InnerStream .Write(textRise) @@ -209,7 +209,7 @@ public ContentStream SetTextRenderMode(TextRenderingMode textRenderingMode) /// /// The word space value. /// The to support chaining operations. - public ContentStream SetWordSpacing(float wordSpace) + public ContentStream SetWordSpacing(double wordSpace) { InnerStream .Write(wordSpace) @@ -255,17 +255,50 @@ public ContentStream MoveNextLineShowText(string line) return this; } + public ContentStream MoveNextLine() + { + InnerStream + .Write("T*") + .NewLine(); + + return this; + } + + public ContentStream MoveNextLineShowText(string line, double wordSpacing, double characterSpacing) + { + InnerStream + .Write(wordSpacing) + .Space() + .Write(characterSpacing) + .Space() + .WriteStringLiteral(line) + .Space() + .Write("\"") + .NewLine(); + + return this; + } + + /// + /// Write the to the content stream with a Td operator + /// + /// The offset to write. + /// The to support chaining operations. + public ContentStream MoveNextLineAndOffsetTextPosition(Point offset) + => MoveNextLineAndOffsetTextPosition(offset.X.AsRaw(Unit.Points), offset.Y.AsRaw(Unit.Points)); + /// - /// Write the to the content stream with a Td operator + /// Write the and offsets to the content stream with a Td operator /// - /// The position to write. + /// The x offset + /// The y offset /// The to support chaining operations. - public ContentStream SetTextPosition(Point position) + public ContentStream MoveNextLineAndOffsetTextPosition(double x, double y) { InnerStream - .Write(position.X.AsRaw(Unit.Points)) + .Write(x) .Space() - .Write(position.Y.AsRaw(Unit.Points)) + .Write(y) .Space() .Write("Td") .NewLine(); @@ -273,6 +306,33 @@ public ContentStream SetTextPosition(Point position) return this; } + /// + /// Write the to the content stream with a TD operator + /// + /// The offset to write. + /// The to support chaining operations. + public ContentStream MoveNextLineSetLeadingAndOffsetTextPosition(Point offset) + => MoveNextLineSetLeadingAndOffsetTextPosition(offset.X.AsRaw(Unit.Points), offset.Y.AsRaw(Unit.Points)); + + /// + /// Write the and offsets to the content stream with a TD operator + /// + /// The x offset + /// The y offset + /// The to support chaining operations. + public ContentStream MoveNextLineSetLeadingAndOffsetTextPosition(double x, double y) + { + InnerStream + .Write(x) + .Space() + .Write(y) + .Space() + .Write("TD") + .NewLine(); + + return this; + } + /// /// Write a text transformation matrix operator (Tm) to the stream /// @@ -314,12 +374,13 @@ public ContentStream Paint(PdfName resource) /// /// Write the operator (m) to the stream /// - /// The operator to write + /// The x coordinate of the target point. + /// The y coordinate of the target point. /// The to support chaining operations. - public ContentStream Write(MoveOperator op) + public ContentStream MoveTo(double x, double y) { InnerStream - .Write(op.X).Space().Write(op.Y).Space() + .Write(x).Space().Write(y).Space() .Write('m').NewLine(); return this; @@ -328,12 +389,13 @@ public ContentStream Write(MoveOperator op) /// /// Write the operator (l) to the stream /// - /// The operator to write + /// The x coordinate of the target point. + /// The y coordinate of the target point. /// The to support chaining operations. - public ContentStream Write(LineOperator op) + public ContentStream LineTo(double x, double y) { InnerStream - .Write(op.X).Space().Write(op.Y).Space() + .Write(x).Space().Write(y).Space() .Write('l').NewLine(); return this; @@ -342,14 +404,19 @@ public ContentStream Write(LineOperator op) /// /// Write the operator (c) to the stream /// - /// The operator to write + /// The x coordinate of the first control point + /// The y coordinate of the first control point + /// The x coordinate of the second control point + /// The y coordinate of the second control point + /// The x coordinate of the target point. + /// The y coordinate of the target point. /// The to support chaining operations. - public ContentStream Write(CubicBezierCurveDualControlPointsOperator op) + public ContentStream CubicBezierCurve(double x1, double y1, double x2, double y2, double x3, double y3) { InnerStream - .Write(op.X1).Space().Write(op.Y1).Space() - .Write(op.X2).Space().Write(op.Y2).Space() - .Write(op.X3).Space().Write(op.Y3).Space() + .Write(x1).Space().Write(y1).Space() + .Write(x2).Space().Write(y2).Space() + .Write(x3).Space().Write(y3).Space() .Write('c').NewLine(); return this; @@ -358,13 +425,16 @@ public ContentStream Write(CubicBezierCurveDualControlPointsOperator op) /// /// Write the operator (v) to the stream /// - /// The operator to write + /// The x coordinate of the second control point + /// The y coordinate of the second control point + /// The x coordinate of the target point. + /// The y coordinate of the target point. /// The to support chaining operations. - public ContentStream Write(CubicBezierCurveInitialControlPointsOperator op) + public ContentStream CubicBezierCurveV(double x2, double y2, double x3, double y3) { InnerStream - .Write(op.X2).Space().Write(op.Y2).Space() - .Write(op.X3).Space().Write(op.Y3).Space() + .Write(x2).Space().Write(y2).Space() + .Write(x3).Space().Write(y3).Space() .Write('v').NewLine(); return this; @@ -373,13 +443,16 @@ public ContentStream Write(CubicBezierCurveInitialControlPointsOperator op) /// /// Write the operator (y) to the stream /// - /// The operator to write + /// The x coordinate of the first control point + /// The y coordinate of the first control point + /// The x coordinate of the target point and second control point. + /// The y coordinate of the target point and second control point. /// The to support chaining operations. - public ContentStream Write(CubicBezierCurveFinalControlPointsOperator op) + public ContentStream CubicBezierCurveY(double x1, double y1, double x3, double y3) { InnerStream - .Write(op.X1).Space().Write(op.Y1).Space() - .Write(op.X3).Space().Write(op.Y3).Space() + .Write(x1).Space().Write(y1).Space() + .Write(x3).Space().Write(y3).Space() .Write('y').NewLine(); return this; @@ -388,27 +461,55 @@ public ContentStream Write(CubicBezierCurveFinalControlPointsOperator op) /// /// Write the operator (re) to the stream /// - /// The operator to write + /// The rectangle to draw. + /// The to support chaining operations. + public ContentStream Rectangle(Primitives.Rectangle rectangle) + { + return Rectangle(rectangle.LLX.AsRaw(Unit.Points), rectangle.LLY.AsRaw(Unit.Points), rectangle.Width.AsRaw(Unit.Points), rectangle.Height.AsRaw(Unit.Points)); + } + + /// + /// Write the operator (re) to the stream + /// + /// The x coordinate of the lower left corner of the rectangle + /// The y coordinate of the lower left corner of the rectangle + /// The width of the rectangle + /// The height of the rectangle /// The to support chaining operations. - public ContentStream Write(RectangleOperator op) + public ContentStream Rectangle(double x, double y, double width, double height) { InnerStream - .Write(op.X).Space().Write(op.Y).Space() - .Write(op.Width).Space().Write(op.Height).Space() + .Write(x).Space().Write(y).Space() + .Write(width).Space().Write(height).Space() .Write("re").NewLine(); return this; } /// - /// Write the operator (h) to the stream + /// Mark the current path for use as a clipping mask /// - /// The operator to write + /// The to use. /// The to support chaining operations. - public ContentStream Write(CloseOperator op) + /// Will throw when a unsupported value for is used. + public ContentStream Clip(FillRule fillRule) { - _ = op; + _ = fillRule switch + { + FillRule.NonZeroWindingNumber => InnerStream.Write('W').NewLine(), + FillRule.EvenOdd => InnerStream.Write('W').Write('*').NewLine(), + _ => throw new InvalidOperationException(UNKNOWN_FILL_RULE) + }; + + return this; + } + /// + /// Write the operator (h) to the stream + /// + /// The to support chaining operations. + public ContentStream Close() + { InnerStream .Write('h').NewLine(); @@ -418,12 +519,9 @@ public ContentStream Write(CloseOperator op) /// /// Write the operator (S) to the stream /// - /// The operator to write /// The to support chaining operations. - public ContentStream Write(StrokeOperator op) + public ContentStream Stroke() { - _ = op; - InnerStream .Write('S').NewLine(); @@ -433,12 +531,9 @@ public ContentStream Write(StrokeOperator op) /// /// Write the operator (s) to the stream /// - /// The operator to write /// The to support chaining operations. - public ContentStream Write(CloseAndStrokeOperator op) + public ContentStream CloseAndStroke() { - _ = op; - InnerStream .Write('s').NewLine(); @@ -448,11 +543,11 @@ public ContentStream Write(CloseAndStrokeOperator op) /// /// Write the operator (f or f*) to the stream /// - /// The operator to write + /// The fill rule to use. /// The to support chaining operations. - public ContentStream Write(FillOperator op) + public ContentStream Fill(FillRule fillRule) { - _ = op.FillRule switch + _ = fillRule switch { FillRule.NonZeroWindingNumber => InnerStream.Write('f').NewLine(), FillRule.EvenOdd => InnerStream.Write('f').Write('*').NewLine(), @@ -465,11 +560,11 @@ public ContentStream Write(FillOperator op) /// /// Write the operator (B or B*) to the stream /// - /// The operator to write + /// The fill rule to use. /// The to support chaining operations. - public ContentStream Write(FillAndStrokeOperator op) + public ContentStream FillAndStroke(FillRule fillRule) { - _ = op.FillRule switch + _ = fillRule switch { FillRule.NonZeroWindingNumber => InnerStream.Write('B').NewLine(), FillRule.EvenOdd => InnerStream.Write('B').Write('*').NewLine(), @@ -482,11 +577,11 @@ public ContentStream Write(FillAndStrokeOperator op) /// /// Write the operator (b or b*) to the stream /// - /// The operator to write + /// The fill rule to use. /// The to support chaining operations. - public ContentStream Write(CloseFillAndStrokeOperator op) + public ContentStream CloseFillAndStroke(FillRule fillRule) { - _ = op.FillRule switch + _ = fillRule switch { FillRule.NonZeroWindingNumber => InnerStream.Write('b').NewLine(), FillRule.EvenOdd => InnerStream.Write('b').Write('*').NewLine(), @@ -499,12 +594,9 @@ public ContentStream Write(CloseFillAndStrokeOperator op) /// /// Write the operator (n) to the stream /// - /// The operator to write /// The to support chaining operations. - public ContentStream Write(EndPathOperator op) + public ContentStream EndPath() { - _ = op; - InnerStream .Write('n').NewLine(); @@ -517,14 +609,14 @@ public ContentStream Write(EndPathOperator op) /// The color to use /// The to support chaining operations. /// Will throw if the implementing color type is not supported. - public ContentStream SetColorFill(Color color) + public ContentStream SetFillColor(Color color) { return color switch { - GrayColor gray => Write(new GrayNonStrokingColorOperator(gray)), - RgbColor rgb => Write(new RgbNonStrokingColorOperator(rgb)), - CmykColor cmyk => Write(new CmykNonStrokingColorOperator(cmyk)), - SpotColor spot => Write(new SpotNonStrokingColorOperator(spot)), + GrayColor gray => SetFillColor(gray), + RgbColor rgb => SetFillColor(rgb), + CmykColor cmyk => SetFillColor(cmyk), + SpotColor spot => SetFillColor(spot), _ => throw new NotImplementedException($"The color type {color.GetType().Name} is not implemented.") }; } @@ -535,59 +627,27 @@ public ContentStream SetColorFill(Color color) /// The color to use /// The to support chaining operations. /// Will throw if the implementing color type is not supported. - public ContentStream SetColorStroke(Color color) + public ContentStream SetStrokeColor(Color color) { return color switch { - GrayColor gray => Write(new GrayStrokingColorOperator(gray)), - RgbColor rgb => Write(new RgbStrokingColorOperator(rgb)), - CmykColor cmyk => Write(new CmykStrokingColorOperator(cmyk)), - SpotColor spot => Write(new SpotStrokingColorOperator(spot)), + GrayColor gray => SetStrokeColor(gray), + RgbColor rgb => SetStrokeColor(rgb), + CmykColor cmyk => SetStrokeColor(cmyk), + SpotColor spot => SetStrokeColor(spot), _ => throw new NotImplementedException($"The color type {color.GetType().Name} is not implemented.") }; } - /// - /// Write the operator (S) to the stream - /// - /// The operator to write - /// The to support chaining operations. - public ContentStream Write(StrokingColorSpaceOperator op) - { - InnerStream - .Write(op.ColorSpace) - .Space() - .Write('C').Write('S') - .NewLine(); - - return this; - } - - /// - /// Write the operator (s) to the stream - /// - /// The operator to write - /// The to support chaining operations. - public ContentStream Write(NonStrokingColorSpaceOperator op) - { - InnerStream - .Write(op.ColorSpace) - .Space() - .Write('c').Write('s') - .NewLine(); - - return this; - } - /// /// Write the operator (G) to the stream /// - /// The operator to write + /// The color to write /// The to support chaining operations. - public ContentStream Write(GrayStrokingColorOperator op) + public ContentStream SetStrokeColor(GrayColor color) { InnerStream - .Write(op.Color.Gray) + .Write(color.Gray) .Space() .Write('G') .NewLine(); @@ -598,12 +658,12 @@ public ContentStream Write(GrayStrokingColorOperator op) /// /// Write the operator (g) to the stream /// - /// The operator to write + /// The color to write /// The to support chaining operations. - public ContentStream Write(GrayNonStrokingColorOperator op) + public ContentStream SetFillColor(GrayColor color) { InnerStream - .Write(op.Color.Gray) + .Write(color.Gray) .Space() .Write('g') .NewLine(); @@ -614,16 +674,16 @@ public ContentStream Write(GrayNonStrokingColorOperator op) /// /// Write the operator (RG) to the stream /// - /// The operator to write + /// The color to write /// The to support chaining operations. - public ContentStream Write(RgbStrokingColorOperator op) + public ContentStream SetStrokeColor(RgbColor color) { InnerStream - .Write(op.Color.Red) + .Write(color.Red) .Space() - .Write(op.Color.Green) + .Write(color.Green) .Space() - .Write(op.Color.Blue) + .Write(color.Blue) .Space() .Write('R') .Write('G') @@ -635,16 +695,16 @@ public ContentStream Write(RgbStrokingColorOperator op) /// /// Write the operator (rg) to the stream /// - /// The operator to write + /// The color to write /// The to support chaining operations. - public ContentStream Write(RgbNonStrokingColorOperator op) + public ContentStream SetFillColor(RgbColor color) { InnerStream - .Write(op.Color.Red) + .Write(color.Red) .Space() - .Write(op.Color.Green) + .Write(color.Green) .Space() - .Write(op.Color.Blue) + .Write(color.Blue) .Space() .Write('r') .Write('g') @@ -656,18 +716,18 @@ public ContentStream Write(RgbNonStrokingColorOperator op) /// /// Write the operator (K) to the stream /// - /// The operator to write + /// The CMYK color to write /// The to support chaining operations. - public ContentStream Write(CmykStrokingColorOperator op) + public ContentStream SetStrokeColor(CmykColor color) { InnerStream - .Write(op.Color.Cyan) + .Write(color.Cyan) .Space() - .Write(op.Color.Magenta) + .Write(color.Magenta) .Space() - .Write(op.Color.Yellow) + .Write(color.Yellow) .Space() - .Write(op.Color.Key) + .Write(color.Key) .Space() .Write('K') .NewLine(); @@ -678,18 +738,18 @@ public ContentStream Write(CmykStrokingColorOperator op) /// /// Write the operator (k) to the stream /// - /// The operator to write + /// The CMYK color to write /// The to support chaining operations. - public ContentStream Write(CmykNonStrokingColorOperator op) + public ContentStream SetFillColor(CmykColor color) { InnerStream - .Write(op.Color.Cyan) + .Write(color.Cyan) .Space() - .Write(op.Color.Magenta) + .Write(color.Magenta) .Space() - .Write(op.Color.Yellow) + .Write(color.Yellow) .Space() - .Write(op.Color.Key) + .Write(color.Key) .Space() .Write('k') .NewLine(); @@ -698,20 +758,20 @@ public ContentStream Write(CmykNonStrokingColorOperator op) } /// - /// Write the operator (SCN) to the stream + /// Write the operator (CS & SCN) to the stream /// - /// The operator to write + /// The spot color to write /// The to support chaining operations. - public ContentStream Write(SpotStrokingColorOperator op) + public ContentStream SetStrokeColor(SpotColor color) { - var name = Resources.AddSeparation(op.Color.Separation); + var name = Resources.AddSeparation(color.Separation); InnerStream .Write(name) .Space() .Write("CS") .Space() - .Write(op.Color.Tint) + .Write(color.Tint) .Space() .Write("SCN") .NewLine(); @@ -720,20 +780,20 @@ public ContentStream Write(SpotStrokingColorOperator op) } /// - /// Write the operator (scn) to the stream + /// Write the operator (cs & scn) to the stream /// - /// The operator to write + /// The spot color to write /// The to support chaining operations. - public ContentStream Write(SpotNonStrokingColorOperator op) + public ContentStream SetFillColor(SpotColor color) { - var name = Resources.AddSeparation(op.Color.Separation); + var name = Resources.AddSeparation(color.Separation); InnerStream .Write(name) .Space() .Write("cs") .Space() - .Write(op.Color.Tint) + .Write(color.Tint) .Space() .Write("scn") .NewLine(); @@ -741,15 +801,10 @@ public ContentStream Write(SpotNonStrokingColorOperator op) return this; } - /// - /// Write the operator (w) to the stream - /// - /// The operator to write - /// The to support chaining operations. - public ContentStream Write(LineWidthOperator op) + public ContentStream SetLineWidth(double width) { InnerStream - .Write(op.Width) + .Write(width) .Space() .Write('w') .NewLine(); @@ -757,15 +812,10 @@ public ContentStream Write(LineWidthOperator op) return this; } - /// - /// Write the operator (J) to the stream - /// - /// The operator to write - /// The to support chaining operations. - public ContentStream Write(LineCapOperator op) + public ContentStream SetLineCap(LineCapStyle lineCap) { InnerStream - .Write((int)op.Style) + .Write((int)lineCap) .Space() .Write('J') .NewLine(); @@ -773,15 +823,10 @@ public ContentStream Write(LineCapOperator op) return this; } - /// - /// Write the operator (j) to the stream - /// - /// The operator to write - /// The to support chaining operations. - public ContentStream Write(LineJoinOperator op) + public ContentStream SetLineJoin(LineJoinStyle lineJoin) { InnerStream - .Write((int)op.Style) + .Write((int)lineJoin) .Space() .Write('j') .NewLine(); @@ -789,15 +834,10 @@ public ContentStream Write(LineJoinOperator op) return this; } - /// - /// Write the operator (M) to the stream - /// - /// The operator to write - /// The to support chaining operations. - public ContentStream Write(MiterLimitOperator op) + public ContentStream SetMiterLimit(double miterLimit) { InnerStream - .Write(op.Limit) + .Write(miterLimit) .Space() .Write('M') .NewLine(); @@ -805,17 +845,12 @@ public ContentStream Write(MiterLimitOperator op) return this; } - /// - /// Write the operator (d) to the stream - /// - /// The operator to write - /// The to support chaining operations. - public ContentStream Write(DashOperator op) + public ContentStream SetDashPattern(Dash dashPattern) { InnerStream - .Write(op.Array) + .Write(dashPattern.Array.ToArray()) .Space() - .Write(op.Phase) + .Write(dashPattern.Phase) .Space() .Write('d') .NewLine(); diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/ContentStreamExtensions.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/ContentStreamExtensions.cs deleted file mode 100644 index 4394d59..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/ContentStreamExtensions.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Synercoding.FileFormats.Pdf.LowLevel.Extensions -{ - /// - /// Class containing extension methods for . - /// - public static class ContentStreamExtensions - { - /// - /// Write a (multi-line) text to the PDF - /// - /// The this extension method is for. - /// The text to write - /// The to support chaining operations. - public static ContentStream ShowText(this ContentStream stream, string text) - { - var lines = _splitOnNewLines(text).ToArray(); - - for (int i = 0; i < lines.Length; i++) - { - if (i == 0) - { - stream.ShowTextTj(lines[i]); - } - else - { - stream.MoveNextLineShowText(lines[i]); - } - } - - return stream; - } - - private static IEnumerable _splitOnNewLines(string text) - { - var builder = new StringBuilder(text.Length); - for (int i = 0; i < text.Length; i++) - { - var c = text[i]; - var n = i < text.Length - 1 - ? text[i + 1] - : '0'; - if (( c == '\r' && n == '\n' ) || c == '\r' || c == '\n') - { - yield return builder.ToString(); - builder.Clear(); - - if (n == '\n') - i++; // extra skip to also skip the \n - } - else - { - builder.Append(c); - } - } - - yield return builder.ToString(); - } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamExtensions.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamExtensions.cs index 1276810..3411733 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamExtensions.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamExtensions.cs @@ -36,33 +36,6 @@ public static PdfStream NewLine(this PdfStream stream) public static PdfStream Write(this PdfStream stream, PdfName name) => stream.Write(name.ToString()); - /// - /// Write a to the stream as a dictionary using - /// - /// The type with data to write to the stream - /// The stream to write the data to - /// The data to write - /// The instructions for writing the type - /// The to support chaining operations. - public static PdfStream IndirectDictionary(this PdfStream stream, TPdfObject pdfObject, Action dictionaryAction) - where TPdfObject : IPdfObject - { - return stream - .StartObject(pdfObject.Reference) - .Dictionary(pdfObject, dictionaryAction) - .EndObject() - .NewLine(); - } - - internal static PdfStream IndirectDictionary(this PdfStream stream, PdfReference reference, T data, Action dictionaryAction) - { - return stream - .StartObject(reference) - .Dictionary(data, dictionaryAction) - .EndObject() - .NewLine(); - } - /// /// Write a dictionary to the /// @@ -216,8 +189,8 @@ public static PdfStream WriteHexadecimalString(this PdfStream stream, string val static (char C1, char C2) _getByteAsHex(byte b) { - var c1 = _getHexCharForNumber(b % 16); - var c2 = _getHexCharForNumber(b / 16 % 16); + var c1 = _getHexCharForNumber(b / 16 % 16); + var c2 = _getHexCharForNumber(b % 16); return (c1, c2); diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/FillRule.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/FillRule.cs new file mode 100644 index 0000000..bc9b79b --- /dev/null +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/FillRule.cs @@ -0,0 +1,16 @@ +namespace Synercoding.FileFormats.Pdf.LowLevel; + +/// +/// Enum representing the ways the area to be filled can be determined +/// +public enum FillRule : byte +{ + /// + /// Represents the non-zero winding rule + /// + NonZeroWindingNumber = 0, + /// + /// Represents the even odd rule + /// + EvenOdd = 1 +} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Dash.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Dash.cs index 1b52347..fa4dfa6 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Dash.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Dash.cs @@ -1,18 +1,42 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel.Graphics +using System.Collections.Generic; + +namespace Synercoding.FileFormats.Pdf.LowLevel.Graphics; + +/// +/// Class representing a dash configuration for a stroking action +/// +public class Dash { + private IReadOnlyList _array = System.Array.Empty(); + /// - /// Class representing a dash configuration for a stroking action + /// Constructor for /// - public class Dash - { - /// - /// Array representing the dash - /// - public double[] Array { get; set; } = System.Array.Empty(); + public Dash() + { } + + /// + /// Constructor for + /// + /// The dash array representing the pattern. + /// The phase of the pattern to start in. + public Dash(double[] array, double phase) + => (Array, Phase) = (array ?? throw new System.ArgumentNullException(nameof(array)), phase); - /// - /// The starting phase of the dash - /// - public double Phase { get; set; } = 0; + /// + /// Array representing the dash + /// + public IReadOnlyList Array + { + get => _array; + init + { + _array = value ?? throw new System.ArgumentNullException(nameof(value)); + } } + + /// + /// The starting phase of the dash + /// + public double Phase { get; init; } = 0; } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/GraphicState.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/GraphicState.cs deleted file mode 100644 index e1a01b3..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/GraphicState.cs +++ /dev/null @@ -1,77 +0,0 @@ -using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors; -using Synercoding.FileFormats.Pdf.LowLevel.Internal; -using Synercoding.FileFormats.Pdf.LowLevel.Operators.Pathing.Painting; - -namespace Synercoding.FileFormats.Pdf.LowLevel.Graphics -{ - /// - /// Class representing the graphic state of a content stream - /// - public sealed class GraphicsState - { - internal GraphicsState(PageResources resources) - { - Resources = resources; - } - - internal PageResources Resources { get; } - - /// - /// The width of the line - /// - public double LineWidth { get; set; } = 1; - - /// - /// The of the fill of the path - /// - /// If null no filling will occur. - public Color? Fill { get; set; } = null; - - /// - /// The of the stroke of the path - /// - /// If null no stroking will occur. - public Color? Stroke { get; set; } = null; - - /// - /// The dash settings of the path - /// - public Dash Dash { get; set; } = new Dash(); - - /// - /// The miter limit - /// - public double MiterLimit { get; set; } = 10; - - /// - /// The of the path - /// - public LineCapStyle LineCap { get; set; } = LineCapStyle.ButtCap; - - /// - /// The of the path - /// - public LineJoinStyle LineJoin { get; set; } = LineJoinStyle.MiterJoin; - - /// - /// The used to determine what areas to fill. - /// - public FillRule FillRule { get; set; } = FillRule.NonZeroWindingNumber; - - internal GraphicsState Clone() - => new GraphicsState(Resources) - { - LineWidth = LineWidth, - Fill = Fill, - Stroke = Stroke, - Dash = new Dash() - { - Array = Dash.Array, - Phase = Dash.Phase - }, - MiterLimit = MiterLimit, - LineCap = LineCap, - LineJoin = LineJoin - }; - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/LineJoinStyle.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/LineJoinStyle.cs index df1ee94..880f54b 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/LineJoinStyle.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/LineJoinStyle.cs @@ -9,7 +9,7 @@ public enum LineJoinStyle /// The outer edges of the strokes for the two segments shall be extended until they meet at an angle. /// /// - /// If the segments meet at too sharp an angle (see ), a bevel join shall be used instead. + /// If the segments meet at too sharp an angle (see ), a bevel join shall be used instead. /// MiterJoin = 0, /// diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/IPdfObject.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/IPdfObject.cs deleted file mode 100644 index 986e721..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/IPdfObject.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel -{ - /// - /// Interface representing a pdf object - /// - public interface IPdfObject - { - /// - /// A pdf reference object that can be used to reference to this object - /// - PdfReference Reference { get; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/Catalog.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/Catalog.cs index f7be7eb..218087b 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/Catalog.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/Catalog.cs @@ -1,6 +1,6 @@ namespace Synercoding.FileFormats.Pdf.LowLevel.Internal { - internal class Catalog : IPdfObject + internal class Catalog { public Catalog(PdfReference id, PageTree pageTree) { @@ -8,7 +8,9 @@ public Catalog(PdfReference id, PageTree pageTree) PageTree = pageTree; } - /// + /// + /// A pdf reference object that can be used to reference to this object + /// public PdfReference Reference { get; } public PageTree PageTree { get; } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageResources.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageResources.cs index cf9bcdd..135639f 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageResources.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageResources.cs @@ -1,5 +1,5 @@ using Synercoding.FileFormats.Pdf.Internals; -using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors.ColorSpaces; +using Synercoding.FileFormats.Pdf.LowLevel.Colors.ColorSpaces; using Synercoding.FileFormats.Pdf.LowLevel.Text; using Synercoding.FileFormats.Pdf.LowLevel.XRef; using System; @@ -48,6 +48,33 @@ public void Dispose() _images.Clear(); } + public PdfName AddJpgUnsafe(System.IO.Stream jpgStream, int originalWidth, int originalHeight, ColorSpace colorSpace) + { + var id = _tableBuilder.ReserveId(); + + var pdfImage = new Image(id, jpgStream, originalWidth, originalHeight, colorSpace); + + return AddImage(pdfImage); + } + + public PdfName AddJpgUnsafe(System.IO.Stream jpgStream, int originalWidth, int originalHeight, PdfName colorSpace, double[] decodeArray) + { + var id = _tableBuilder.ReserveId(); + + var pdfImage = new Image(id, jpgStream, originalWidth, originalHeight, colorSpace, decodeArray); + + return AddImage(pdfImage); + } + + public PdfName AddImage(SixLabors.ImageSharp.Image image) + { + var id = _tableBuilder.ReserveId(); + + var pdfImage = new Image(id, image); + + return AddImage(pdfImage); + } + public PdfName AddImage(Image image) { if (_images.Reverse.Contains(image)) diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageTree.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageTree.cs index d33cf34..087ac15 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageTree.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageTree.cs @@ -2,7 +2,7 @@ namespace Synercoding.FileFormats.Pdf.LowLevel.Internal { - internal class PageTree : IPdfObject + internal class PageTree { private readonly List _pages = new List(); @@ -11,7 +11,9 @@ public PageTree(PdfReference id) Reference = id; } - /// + /// + /// A pdf reference object that can be used to reference to this object + /// public PdfReference Reference { get; } public void AddPage(PdfPage pdfObject) diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/ShapeContext.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/ShapeContext.cs deleted file mode 100644 index ced983b..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/ShapeContext.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Synercoding.FileFormats.Pdf.Internals; -using Synercoding.FileFormats.Pdf.LowLevel.Graphics; -using System; - -namespace Synercoding.FileFormats.Pdf.LowLevel.Internal -{ - internal sealed class ShapeContext : IShapeContext, IDisposable - { - private readonly ContentStream _contentStream; - - internal GraphicsState State { get; } - - internal Path? CurrentPath { get; set; } = null; - - internal ShapeContext(ContentStream contentStream, PageResources resources) - { - _contentStream = contentStream; - - _contentStream.SaveState(); - - State = new GraphicsState(resources); - } - - public void Dispose() - { - _finishPathIfNeeded(); - - _contentStream.RestoreState(); - } - - public IShapeContext DefaultState(Action configureState) - { - configureState(State); - - return this; - } - public IPath NewPath() - => NewPath(_ => { }); - - public IPath NewPath(Action configureState) - { - _finishPathIfNeeded(); - - var state = State.Clone(); - configureState(state); - - var path = new Path(_contentStream, state); - - CurrentPath = path; - - return path; - } - - private void _finishPathIfNeeded() - { - if (CurrentPath != null) - { - CurrentPath.FinishPath(); - CurrentPath = null; - } - } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/ObjectStream.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/ObjectStream.cs index f6b5968..0dd7c98 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/ObjectStream.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/ObjectStream.cs @@ -1,5 +1,5 @@ +using Synercoding.FileFormats.Pdf.LowLevel.Colors.ColorSpaces; using Synercoding.FileFormats.Pdf.LowLevel.Extensions; -using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors.ColorSpaces; using Synercoding.FileFormats.Pdf.LowLevel.Internal; using Synercoding.FileFormats.Pdf.LowLevel.Text; using Synercoding.FileFormats.Pdf.LowLevel.XRef; @@ -102,9 +102,9 @@ public ObjectStream Write(Image image) .Write(PdfName.Get("Subtype"), PdfName.Get("Image")) .Write(PdfName.Get("Width"), image.Width) .Write(PdfName.Get("Height"), image.Height) - .Write(PdfName.Get("ColorSpace"), PdfName.Get("DeviceRGB")) + .Write(PdfName.Get("ColorSpace"), image.ColorSpace) .Write(PdfName.Get("BitsPerComponent"), 8) - .Write(PdfName.Get("Decode"), 0f, 1f, 0f, 1f, 0f, 1f); + .Write(PdfName.Get("Decode"), image.DecodeArray); }, StreamFilter.DCTDecode); return this; @@ -124,7 +124,7 @@ public ObjectStream Write(PdfPage page) .WriteIfNotNull(PdfName.Get("CropBox"), page.CropBox) .WriteIfNotNull(PdfName.Get("BleedBox"), page.BleedBox) .WriteIfNotNull(PdfName.Get("TrimBox"), page.TrimBox) - .WriteIfNotNull(PdfName.Get("Rotate"), page.Rotation); + .WriteIfNotNull(PdfName.Get("Rotate"), (int?)page.Rotation); // Resources dictionary.Write(PdfName.Get("Resources"), page.Resources, static (resources, stream) => stream.Dictionary(resources, static (resources, stream) => @@ -164,7 +164,7 @@ public ObjectStream Write(PdfPage page) })); // Content stream - dictionary.Write(PdfName.Get("Contents"), page.ContentStream.Reference); + dictionary.Write(PdfName.Get("Contents"), page.Content.RawContentStream.Reference); }); return this; diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/CmykNonStrokingColorOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/CmykNonStrokingColorOperator.cs deleted file mode 100644 index 5a5ccfc..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/CmykNonStrokingColorOperator.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors; - -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Color -{ - /// - /// Struct representing a non stroking cmyk operation (k) - /// - public struct CmykNonStrokingColorOperator - { - /// - /// Constructor for . - /// - /// The color to use. - public CmykNonStrokingColorOperator(CmykColor color) - { - Color = color; - } - - /// - /// The color used in the operation - /// - public CmykColor Color { get; init; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/CmykStrokingColorOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/CmykStrokingColorOperator.cs deleted file mode 100644 index 5ca4d6c..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/CmykStrokingColorOperator.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors; - -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Color -{ - /// - /// Struct representing a stroking cmyk operation (K) - /// - public struct CmykStrokingColorOperator - { - /// - /// Constructor for . - /// - /// The color to use. - public CmykStrokingColorOperator(CmykColor color) - { - Color = color; - } - - /// - /// The color used in the operation - /// - public CmykColor Color { get; init; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/GrayNonStrokingColorOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/GrayNonStrokingColorOperator.cs deleted file mode 100644 index ba0812d..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/GrayNonStrokingColorOperator.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors; - -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Color -{ - /// - /// Struct representing a gray non stroking operator (g) - /// - public struct GrayNonStrokingColorOperator - { - /// - /// Constructor for . - /// - /// The color to use. - public GrayNonStrokingColorOperator(GrayColor color) - { - Color = color; - } - - /// - /// The color used in the operation - /// - public GrayColor Color { get; init; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/GrayStrokingColorOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/GrayStrokingColorOperator.cs deleted file mode 100644 index 939de2c..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/GrayStrokingColorOperator.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors; - -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Color -{ - /// - /// Struct representing a gray stroking operator (G) - /// - public struct GrayStrokingColorOperator - { - /// - /// Constructor for . - /// - /// The color to use. - public GrayStrokingColorOperator(GrayColor color) - { - Color = color; - } - - /// - /// The color used in the operation - /// - public GrayColor Color { get; init; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/NonStrokingColorSpaceOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/NonStrokingColorSpaceOperator.cs deleted file mode 100644 index 638419d..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/NonStrokingColorSpaceOperator.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Color -{ - /// - /// Struct representing non stroking color space change operator (cs) - /// - public struct NonStrokingColorSpaceOperator - { - /// - /// Constructor for . - /// - /// The colorspace to use. - public NonStrokingColorSpaceOperator(PdfName colorspace) - { - ColorSpace = colorspace; - } - - /// - /// The of the colorspace that will be set - /// - public PdfName ColorSpace { get; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/RgbNonStrokingColorOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/RgbNonStrokingColorOperator.cs deleted file mode 100644 index 31de12f..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/RgbNonStrokingColorOperator.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors; - -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Color -{ - /// - /// Struct representing a rgb non stroking operator (rg) - /// - public struct RgbNonStrokingColorOperator - { - /// - /// Constructor for . - /// - /// The color to use. - public RgbNonStrokingColorOperator(RgbColor color) - { - Color = color; - } - - /// - /// The color used in the operation - /// - public RgbColor Color { get; init; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/RgbStrokingColorOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/RgbStrokingColorOperator.cs deleted file mode 100644 index f939ccd..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/RgbStrokingColorOperator.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors; - -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Color -{ - /// - /// Struct representing a rgb stroking operator (RG) - /// - public struct RgbStrokingColorOperator - { - /// - /// Constructor for . - /// - /// The color to use. - public RgbStrokingColorOperator(RgbColor color) - { - Color = color; - } - - /// - /// The color used in the operation - /// - public RgbColor Color { get; init; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/SpotNonStrokingColorOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/SpotNonStrokingColorOperator.cs deleted file mode 100644 index 5e3ec02..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/SpotNonStrokingColorOperator.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors; - -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Color -{ - /// - /// Struct representing a spot color non stroking operator (scn) - /// - public struct SpotNonStrokingColorOperator - { - /// - /// Constructor for . - /// - /// The color to use. - public SpotNonStrokingColorOperator(SpotColor color) - { - Color = color; - } - - /// - /// The color used in the operation - /// - public SpotColor Color { get; init; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/SpotStrokingColorOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/SpotStrokingColorOperator.cs deleted file mode 100644 index 0204562..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/SpotStrokingColorOperator.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors; - -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Color -{ - /// - /// Struct representing a spot color stroking operator (SCN) - /// - public struct SpotStrokingColorOperator - { - /// - /// Constructor for . - /// - /// The color to use. - public SpotStrokingColorOperator(SpotColor color) - { - Color = color; - } - - /// - /// The color used in the operation - /// - public SpotColor Color { get; init; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/StrokingColorSpaceOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/StrokingColorSpaceOperator.cs deleted file mode 100644 index 8e5aa65..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Color/StrokingColorSpaceOperator.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Color -{ - /// - /// Struct representing stroking color space change operator (CS) - /// - public struct StrokingColorSpaceOperator - { - /// - /// Constructor for . - /// - /// The colorspace to use. - public StrokingColorSpaceOperator(PdfName colorspace) - { - ColorSpace = colorspace; - } - - /// - /// The of the colorspace that will be set - /// - public PdfName ColorSpace { get; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/CloseOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/CloseOperator.cs deleted file mode 100644 index 08b709d..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/CloseOperator.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Pathing.Construction -{ - /// - /// Struct representing a close operator (h) - /// - public struct CloseOperator { } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/CubicBezierCurveDualControlPointsOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/CubicBezierCurveDualControlPointsOperator.cs deleted file mode 100644 index 1062a64..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/CubicBezierCurveDualControlPointsOperator.cs +++ /dev/null @@ -1,79 +0,0 @@ -using Synercoding.Primitives; - -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Pathing.Construction -{ - /// - /// Struct representing a cubic bézier curve operator (c) - /// - public struct CubicBezierCurveDualControlPointsOperator - { - /// - /// Constructor for - /// - /// The X coordinate of the first control point - /// The Y coordinate of the first control point - /// The X coordinate of the second control point - /// The Y coordinate of the second control point - /// The X coordinate of the end point of the curve - /// The Y coordinate of the end point of the curve - public CubicBezierCurveDualControlPointsOperator(double x1, double y1, double x2, double y2, double x3, double y3) - { - X1 = x1; - Y1 = y1; - X2 = x2; - Y2 = y2; - X3 = x3; - Y3 = y3; - } - - /// - /// Constructor for - /// - /// The first control point - /// The second control point - /// The end point of the curve - public CubicBezierCurveDualControlPointsOperator(Point cp1, Point cp2, Point endpoint) - { - cp1 = cp1.ConvertTo(Unit.Points); - cp2 = cp2.ConvertTo(Unit.Points); - endpoint = endpoint.ConvertTo(Unit.Points); - - X1 = cp1.X.Raw; - Y1 = cp1.Y.Raw; - X2 = cp2.X.Raw; - Y2 = cp2.Y.Raw; - Y3 = endpoint.Y.Raw; - X3 = endpoint.X.Raw; - } - - /// - /// The X coordinate of the first control point - /// - public double X1 { get; init; } - - /// - /// The Y coordinate of the first control point - /// - public double Y1 { get; init; } - - /// - /// The X coordinate of the second control point - /// - public double X2 { get; init; } - - /// - /// The Y coordinate of the second control point - /// - public double Y2 { get; init; } - - /// - /// The X coordinate of the end point of the curve - /// - public double X3 { get; init; } - - /// - /// The Y coordinate of the end point of the curve - /// - public double Y3 { get; init; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/CubicBezierCurveFinalControlPointsOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/CubicBezierCurveFinalControlPointsOperator.cs deleted file mode 100644 index 50184db..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/CubicBezierCurveFinalControlPointsOperator.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Synercoding.Primitives; - -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Pathing.Construction -{ - /// - /// Struct representing a cubic bézier curve operator (y) - /// - public struct CubicBezierCurveFinalControlPointsOperator - { - /// - /// Constructor for - /// - /// The X coordinate of the first control point - /// The Y coordinate of the first control point - /// The X coordinate of the end point of the curve - /// The Y coordinate of the end point of the curve - public CubicBezierCurveFinalControlPointsOperator(double x1, double y1, double x3, double y3) - { - X1 = x1; - Y1 = y1; - X3 = x3; - Y3 = y3; - } - - /// - /// Constructor for - /// - /// The first control point - /// The end point of the curve - public CubicBezierCurveFinalControlPointsOperator(Point cp1, Point endpoint) - { - cp1 = cp1.ConvertTo(Unit.Points); - endpoint = endpoint.ConvertTo(Unit.Points); - - X1 = cp1.X.Raw; - Y1 = cp1.Y.Raw; - X3 = endpoint.X.Raw; - Y3 = endpoint.Y.Raw; - } - - /// - /// The X coordinate of the first control point - /// - public double X1 { get; init; } - - /// - /// The Y coordinate of the first control point - /// - public double Y1 { get; init; } - - /// - /// The X coordinate of the end point of the curve - /// - public double X3 { get; init; } - - /// - /// The Y coordinate of the end point of the curve - /// - public double Y3 { get; init; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/CubicBezierCurveInitialControlPointsOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/CubicBezierCurveInitialControlPointsOperator.cs deleted file mode 100644 index db0dff0..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/CubicBezierCurveInitialControlPointsOperator.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Synercoding.Primitives; - -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Pathing.Construction -{ - /// - /// Struct representing a cubic bézier curve operator (v) - /// - public struct CubicBezierCurveInitialControlPointsOperator - { - /// - /// Constructor for - /// - /// The X coordinate of the second control point - /// The Y coordinate of the second control point - /// The X coordinate of the end point of the curve - /// The Y coordinate of the end point of the curve - public CubicBezierCurveInitialControlPointsOperator(double x2, double y2, double x3, double y3) - { - X2 = x2; - Y2 = y2; - X3 = x3; - Y3 = y3; - } - - /// - /// Constructor for - /// - /// The second control point - /// The end point of the curve - public CubicBezierCurveInitialControlPointsOperator(Point cp2, Point endpoint) - { - cp2 = cp2.ConvertTo(Unit.Points); - endpoint = endpoint.ConvertTo(Unit.Points); - - X2 = cp2.X.Raw; - Y2 = cp2.Y.Raw; - X3 = endpoint.X.Raw; - Y3 = endpoint.Y.Raw; - } - - /// - /// The X coordinate of the second control point - /// - public double X2 { get; init; } - - /// - /// The Y coordinate of the second control point - /// - public double Y2 { get; init; } - - /// - /// The X coordinate of the end point of the curve - /// - public double X3 { get; init; } - - /// - /// The Y coordinate of the end point of the curve - /// - public double Y3 { get; init; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/LineOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/LineOperator.cs deleted file mode 100644 index 692b352..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/LineOperator.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Synercoding.Primitives; - -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Pathing.Construction -{ - /// - /// Struct representing a line operator (l) - /// - public struct LineOperator - { - /// - /// Constructor for a - /// - /// The X coordinate of the line end - /// The Y coordinate of the line end - public LineOperator(double x, double y) - { - X = x; - Y = y; - } - - /// - /// Constructor for a - /// - /// The end point of the line - public LineOperator(Point point) - { - point = point.ConvertTo(Unit.Points); - - X = point.X.Raw; - Y = point.Y.Raw; - } - - /// - /// The X coordinate of the line end - /// - public double X { get; } - - /// - /// The Y coordinate of the line end - /// - public double Y { get; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/MoveOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/MoveOperator.cs deleted file mode 100644 index 92070c6..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/MoveOperator.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Synercoding.Primitives; - -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Pathing.Construction -{ - /// - /// Struct representing a move operator (m) - /// - public struct MoveOperator - { - /// - /// Constructor for a - /// - /// The X coordinate of the move operator - /// The Y coordinate of the move operator - public MoveOperator(double x, double y) - { - X = x; - Y = y; - } - - /// - /// Constructor for a - /// - /// The end point of the line - public MoveOperator(Point point) - { - point = point.ConvertTo(Unit.Points); - - X = point.X.Raw; - Y = point.Y.Raw; - } - - /// - /// The X coordinate of the move operator - /// - public double X { get; } - - /// - /// The Y coordinate of the move operator - /// - public double Y { get; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/RectangleOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/RectangleOperator.cs deleted file mode 100644 index 7c83aca..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Construction/RectangleOperator.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Synercoding.Primitives; - -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Pathing.Construction -{ - /// - /// Struct representing a rectangle operator (re) - /// - public struct RectangleOperator - { - /// - /// Constructor for a - /// - /// The X coordinate of the lower left corner of the rectangle - /// The Y coordinate of the lower left corner of the rectangle - /// The width of the rectangle - /// The height of the rectangle - public RectangleOperator(double x, double y, double width, double height) - { - X = x; - Y = y; - Width = width; - Height = height; - } - - /// - /// Constructor for a - /// - /// - public RectangleOperator(Rectangle rectangle) - { - rectangle = rectangle.ConvertTo(Unit.Points); - - X = rectangle.LLX.Raw; - Y = rectangle.LLY.Raw; - Width = rectangle.Width.Raw; - Height = rectangle.Height.Raw; - } - - /// - /// The X coordinate of the lower left corner of the rectangle - /// - public double X { get; } - - /// - /// The Y coordinate of the lower left corner of the rectangle - /// - public double Y { get; } - - /// - /// The width of the rectangle - /// - public double Width { get; } - - /// - /// The height of the rectangle - /// - public double Height { get; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/CloseAndStrokeOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/CloseAndStrokeOperator.cs deleted file mode 100644 index ea86139..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/CloseAndStrokeOperator.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Pathing.Painting -{ - /// - /// Struct representing a close and stroke operator (s) - /// - public struct CloseAndStrokeOperator { } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/CloseFillAndStrokeOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/CloseFillAndStrokeOperator.cs deleted file mode 100644 index 12b4e45..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/CloseFillAndStrokeOperator.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Pathing.Painting -{ - /// - /// Struct representing a close, fill and stroke operator (b or b*) - /// - public struct CloseFillAndStrokeOperator - { - /// - /// Constructor for - /// - /// The to use to determine what area to fill. - public CloseFillAndStrokeOperator(FillRule fillRule) - { - FillRule = fillRule; - } - - /// - /// The to use during filling. - /// - public FillRule FillRule { get; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/EndPathOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/EndPathOperator.cs deleted file mode 100644 index b3466f2..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/EndPathOperator.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Pathing.Painting -{ - /// - /// Struct representing a end-of-path operator (no fill, no stroke) (n) - /// - public struct EndPathOperator { } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/FillAndStrokeOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/FillAndStrokeOperator.cs deleted file mode 100644 index 5b857da..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/FillAndStrokeOperator.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Pathing.Painting -{ - /// - /// Struct representing a fill and stroke operator (B or B*) - /// - public struct FillAndStrokeOperator - { - /// - /// Constructor for a - /// - /// The to use to determine what area to fill. - public FillAndStrokeOperator(FillRule fillRule) - { - FillRule = fillRule; - } - - /// - /// The to use during filling. - /// - public FillRule FillRule { get; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/FillOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/FillOperator.cs deleted file mode 100644 index c42fd98..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/FillOperator.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Pathing.Painting -{ - /// - /// Struct representing a fill operator (f or f*) - /// - public struct FillOperator - { - /// - /// Constructor for a - /// - /// The to use to determine what area to fill. - public FillOperator(FillRule fillRule) - { - FillRule = fillRule; - } - - - /// - /// The to use during filling. - /// - public FillRule FillRule { get; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/FillRule.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/FillRule.cs deleted file mode 100644 index 58d1cb3..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/FillRule.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Pathing.Painting -{ - /// - /// Enum representing the ways the area to be filled can be determined - /// - public enum FillRule : byte - { - /// - /// Represents the non-zero winding rule - /// - NonZeroWindingNumber = 0, - /// - /// Represents the even odd rule - /// - EvenOdd = 1 - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/StrokeOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/StrokeOperator.cs deleted file mode 100644 index e71fadf..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/Pathing/Painting/StrokeOperator.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.Pathing.Painting -{ - /// - /// Struct representing a stroke operator (S) - /// - public struct StrokeOperator { } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/State/DashOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/State/DashOperator.cs deleted file mode 100644 index b814380..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/State/DashOperator.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.State -{ - /// - /// Struct representing a dash operator (d) - /// - public struct DashOperator - { - /// - /// Constructor for - /// - /// The dash array - /// The dash phase - public DashOperator(double[] array, double phase) - { - Array = array; - Phase = phase; - } - - /// - /// The dash array - /// - public double[] Array { get; } - - /// - /// The dash phase - /// - public double Phase { get; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/State/LineCapOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/State/LineCapOperator.cs deleted file mode 100644 index fb6c399..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/State/LineCapOperator.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Synercoding.FileFormats.Pdf.LowLevel.Graphics; - -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.State -{ - /// - /// Struct representing a line cap operator (J) - /// - public struct LineCapOperator - { - /// - /// Constructor for - /// - /// The style to use - public LineCapOperator(LineCapStyle style) - { - Style = style; - } - - /// - /// The to use - /// - public LineCapStyle Style { get; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/State/LineJoinOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/State/LineJoinOperator.cs deleted file mode 100644 index 346207c..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/State/LineJoinOperator.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Synercoding.FileFormats.Pdf.LowLevel.Graphics; - -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.State -{ - /// - /// Struct representing a line join operator (j) - /// - public struct LineJoinOperator - { - /// - /// Constructor for . - /// - /// The line join style to use. - public LineJoinOperator(LineJoinStyle style) - { - Style = style; - } - - /// - /// The to use. - /// - public LineJoinStyle Style { get; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/State/LineWidthOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/State/LineWidthOperator.cs deleted file mode 100644 index 103d805..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/State/LineWidthOperator.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.State -{ - /// - /// Struct representing a line width operator (w) - /// - public struct LineWidthOperator - { - /// - /// Constructor for a - /// - /// The width of the line - public LineWidthOperator(double width) - { - Width = width; - } - - /// - /// The width of the line - /// - public double Width { get; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/State/MiterLimitOperator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/State/MiterLimitOperator.cs deleted file mode 100644 index 37f5320..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Operators/State/MiterLimitOperator.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel.Operators.State -{ - /// - /// Class representing an miter limit operator for a content stream - /// - public struct MiterLimitOperator - { - /// - /// Constructor for - /// - /// The miter limit - public MiterLimitOperator(double limit) - { - Limit = limit; - } - - /// - /// The limit that this operator represents - /// - public double Limit { get; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Text/Font.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Text/Font.cs index 33ccc2f..7474620 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Text/Font.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Text/Font.cs @@ -1,8 +1,12 @@ +using System; + namespace Synercoding.FileFormats.Pdf.LowLevel.Text; /// /// Base class representing a font /// -public abstract class Font +public abstract class Font : IEquatable { + /// + public abstract bool Equals(Font? other); } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Text/TextState.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Text/TextState.cs index 3da620d..3b491b1 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Text/TextState.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Text/TextState.cs @@ -1,5 +1,5 @@ +using Synercoding.FileFormats.Pdf.LowLevel.Colors; using Synercoding.FileFormats.Pdf.LowLevel.Graphics; -using Synercoding.FileFormats.Pdf.LowLevel.Graphics.Colors; namespace Synercoding.FileFormats.Pdf.LowLevel.Text { diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Text/Type1StandardFont.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Text/Type1StandardFont.cs index af4e9c8..d843635 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Text/Type1StandardFont.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Text/Type1StandardFont.cs @@ -22,7 +22,11 @@ internal Type1StandardFont(PdfName name, PdfName lookupName) /// public override bool Equals(object? obj) - => obj is Type1StandardFont font && Equals(font); + => Equals(obj as Type1StandardFont); + + /// + public override bool Equals(Font? other) + => Equals(other as Type1StandardFont); /// public bool Equals(Type1StandardFont? other) diff --git a/src/Synercoding.FileFormats.Pdf/Matrix.cs b/src/Synercoding.FileFormats.Pdf/Matrix.cs index 797e258..40c1f69 100644 --- a/src/Synercoding.FileFormats.Pdf/Matrix.cs +++ b/src/Synercoding.FileFormats.Pdf/Matrix.cs @@ -65,32 +65,32 @@ public Matrix(double a, double b, double c, double d, double e, double f) /// /// The A value /// - public double A { get; init; } + public double A { get; } /// /// The B value /// - public double B { get; init; } + public double B { get; } /// /// The C value /// - public double C { get; init; } + public double C { get; } /// /// The D value /// - public double D { get; init; } + public double D { get; } /// /// The E value /// - public double E { get; init; } + public double E { get; } /// /// The F value /// - public double F { get; init; } + public double F { get; } /// public override bool Equals(object? obj) diff --git a/src/Synercoding.FileFormats.Pdf/PageRotation.cs b/src/Synercoding.FileFormats.Pdf/PageRotation.cs new file mode 100644 index 0000000..27b1e66 --- /dev/null +++ b/src/Synercoding.FileFormats.Pdf/PageRotation.cs @@ -0,0 +1,9 @@ +namespace Synercoding.FileFormats.Pdf; + +public enum PageRotation +{ + Rotation0 = 0, + Rotation90 = 90, + Rotation180 = 180, + Rotation270 = 270 +} diff --git a/src/Synercoding.FileFormats.Pdf/PdfPage.cs b/src/Synercoding.FileFormats.Pdf/PdfPage.cs index a84440a..3b4d903 100644 --- a/src/Synercoding.FileFormats.Pdf/PdfPage.cs +++ b/src/Synercoding.FileFormats.Pdf/PdfPage.cs @@ -1,6 +1,6 @@ +using Synercoding.FileFormats.Pdf.Internals; using Synercoding.FileFormats.Pdf.LowLevel; using Synercoding.FileFormats.Pdf.LowLevel.Internal; -using Synercoding.FileFormats.Pdf.LowLevel.Text; using Synercoding.FileFormats.Pdf.LowLevel.XRef; using Synercoding.Primitives; using Synercoding.Primitives.Extensions; @@ -11,12 +11,12 @@ namespace Synercoding.FileFormats.Pdf /// /// This class represents a page in a pdf /// - public sealed class PdfPage : IPdfObject, IDisposable + public sealed class PdfPage : IDisposable { private readonly TableBuilder _tableBuilder; private readonly PageTree _parent; - private int? _rotation; + private PageRotation? _rotation; internal PdfPage(TableBuilder tableBuilder, PageTree parent) { @@ -27,7 +27,9 @@ internal PdfPage(TableBuilder tableBuilder, PageTree parent) PageNumber = _parent.PageCount; Reference = tableBuilder.ReserveId(); Resources = new PageResources(_tableBuilder); - ContentStream = new ContentStream(tableBuilder.ReserveId(), Resources); + var contentStream = new ContentStream(tableBuilder.ReserveId(), Resources); + + Content = new PageContentContext(contentStream); } internal PdfReference Parent @@ -40,23 +42,22 @@ internal PdfReference Parent /// public int PageNumber { get; } + public IPageContentContext Content { get; } + /// - /// The content stream of this page + /// A pdf reference object that can be used to reference to this object /// - public ContentStream ContentStream { get; } - - /// public PdfReference Reference { get; } /// /// The rotation of how the page is displayed, must be in increments of 90 /// - public int? Rotation + public PageRotation? Rotation { get => _rotation; set { - if (value is not null && value % 90 != 0) + if (value is not null && !Enum.IsDefined(value.Value)) throw new ArgumentOutOfRangeException(nameof(Rotation), value, "The provided value can only be increments of 90."); _rotation = value; @@ -74,82 +75,26 @@ public int? Rotation public Rectangle? CropBox { get; set; } /// - /// The bleedbox of the + /// The bleed box of the /// public Rectangle? BleedBox { get; set; } /// - /// The trimbox of the + /// The trim box of the /// public Rectangle? TrimBox { get; set; } /// - /// The artbox of the + /// The art box of the /// public Rectangle? Art { get; set; } - /// - /// Add an image to the resources of this page - /// - /// The image to add - /// The that can be used to reference this image in the - public PdfName AddImageToResources(SixLabors.ImageSharp.Image image) - { - var id = _tableBuilder.ReserveId(); - - var pdfImage = new Image(id, image); - - return Resources.AddImage(pdfImage); - } - - /// - /// Add an image to the resources of this page - /// - /// The image to add - /// The width of the image in the - /// The height of the image in the - /// The that can be used to reference this image in the - public PdfName AddImageToResources(System.IO.Stream jpgStream, int width, int height) - { - var id = _tableBuilder.ReserveId(); - - var pdfImage = new Image(id, jpgStream, width, height); - - return Resources.AddImage(pdfImage); - } - - /// - /// Add an image to the resources of this page - /// - /// The image to add - /// The that can be used to reference this image in the - public PdfName AddImageToResources(System.IO.Stream imageStream) - { - using var image = SixLabors.ImageSharp.Image.Load(imageStream); - return AddImageToResources(image); - } - - /// - /// Add an image to the resources of this page - /// - /// The image to add - /// The that can be used to reference this image in the - public PdfName AddImageToResources(Image image) - { - return Resources.AddImage(image); - } - /// public void Dispose() { Resources.Dispose(); - ContentStream.Dispose(); - } - - internal void MarkStdFontAsUsed(Type1StandardFont font) - { - Resources.AddStandardFont(font); + Content.RawContentStream.Dispose(); } } } diff --git a/src/Synercoding.FileFormats.Pdf/PdfWriter.cs b/src/Synercoding.FileFormats.Pdf/PdfWriter.cs index f5a4593..4720e90 100644 --- a/src/Synercoding.FileFormats.Pdf/PdfWriter.cs +++ b/src/Synercoding.FileFormats.Pdf/PdfWriter.cs @@ -1,4 +1,5 @@ using Synercoding.FileFormats.Pdf.LowLevel; +using Synercoding.FileFormats.Pdf.LowLevel.Colors.ColorSpaces; using Synercoding.FileFormats.Pdf.LowLevel.Extensions; using Synercoding.FileFormats.Pdf.LowLevel.Internal; using Synercoding.FileFormats.Pdf.LowLevel.XRef; @@ -171,14 +172,15 @@ public Image AddImage(SixLabors.ImageSharp.Image image) /// The of the jpg image that needs to be added. /// The width of the image in the . /// The height of the image in the . + /// The color space of the jpg image. /// The image reference that can be used in pages - public Image AddJpgImageUnsafe(Stream jpgStream, int originalWidth, int originalHeight) + public Image AddJpgUnsafe(Stream jpgStream, int originalWidth, int originalHeight, ColorSpace colorSpace) { _throwWhenEndingWritten(); var id = _tableBuilder.ReserveId(); - var pdfImage = new Image(id, jpgStream, originalWidth, originalHeight); + var pdfImage = new Image(id, jpgStream, originalWidth, originalHeight, colorSpace); _objectStream.Write(pdfImage); @@ -234,7 +236,7 @@ private void _writePageAndResourcesToObjectStream(PdfPage page) foreach (var (separation, (_, refId)) in page.Resources.SeparationReferences) _objectStream.Write(refId, separation); - _objectStream.Write(page.ContentStream); + _objectStream.Write(page.Content.RawContentStream); } private void _throwWhenEndingWritten() diff --git a/src/Synercoding.FileFormats.Pdf/StandardFonts.cs b/src/Synercoding.FileFormats.Pdf/StandardFonts.cs index 60a5d21..8a7b615 100644 --- a/src/Synercoding.FileFormats.Pdf/StandardFonts.cs +++ b/src/Synercoding.FileFormats.Pdf/StandardFonts.cs @@ -32,7 +32,6 @@ public static class StandardFonts public static Type1StandardFont TimesRomanBoldItalic { get; } = new Type1StandardFont(PdfName.Get("Times-BoldItalic"), PdfName.Get("StdFont-Times-Roman-Bold-Italic")); - /// /// A type 1 standard font for Helvetica /// @@ -57,7 +56,6 @@ public static class StandardFonts public static Type1StandardFont HelveticaBoldOblique { get; } = new Type1StandardFont(PdfName.Get("Helvetica-BoldOblique"), PdfName.Get("StdFont-Helvetica-BoldOblique")); - /// /// A type 1 standard font for Courier /// diff --git a/src/Synercoding.FileFormats.Pdf/Synercoding.FileFormats.Pdf.csproj b/src/Synercoding.FileFormats.Pdf/Synercoding.FileFormats.Pdf.csproj index 461595e..fd772e4 100644 --- a/src/Synercoding.FileFormats.Pdf/Synercoding.FileFormats.Pdf.csproj +++ b/src/Synercoding.FileFormats.Pdf/Synercoding.FileFormats.Pdf.csproj @@ -3,8 +3,8 @@ - - + + From 29f2c406cfbecb0bace0eda095a2029f3d8cb1e1 Mon Sep 17 00:00:00 2001 From: Gerard Gunnewijk Date: Thu, 26 Jan 2023 11:32:18 +0100 Subject: [PATCH 04/11] Some code cleanup --- .../Program.cs | 347 ++-- .../DocumentInformation.cs | 99 +- .../Extensions/IContentContextExtensions.cs | 2 +- .../IPageContentContextExtensions.cs | 9 +- .../Extensions/PrimitiveExtensions.cs | 31 +- src/Synercoding.FileFormats.Pdf/IPath.cs | 133 -- .../IShapeContext.cs | 3 +- src/Synercoding.FileFormats.Pdf/Image.cs | 135 +- .../Internals/ByteSizes.cs | 35 +- .../Internals/Map.cs | 119 +- .../Internals/PageContentContext.cs | 1 - .../Internals/SpanHelper.cs | 57 +- .../Internals/WrappedShapesContext.cs | 2 +- .../LowLevel/Colors/CmykColor.cs | 211 ++- .../LowLevel/Colors/Color.cs | 81 +- .../LowLevel/Colors/GrayColor.cs | 95 +- .../LowLevel/Colors/PredefinedColors.cs | 119 +- .../LowLevel/Colors/RgbColor.cs | 195 +- .../LowLevel/Colors/SpotColor.cs | 2 +- .../LowLevel/ContentStream.cs | 1613 ++++++++--------- .../Extensions/PdfStreamArrayExtensions.cs | 205 ++- .../Extensions/PdfStreamExtensions.cs | 533 +++--- .../Extensions/StreamFilterExtensions.cs | 17 +- .../LowLevel/Graphics/Dash.cs | 5 +- .../LowLevel/Graphics/LineCapStyle.cs | 33 +- .../LowLevel/Graphics/LineJoinStyle.cs | 43 +- .../LowLevel/Internal/Catalog.cs | 25 +- .../LowLevel/Internal/DocumentResources.cs | 10 - .../LowLevel/Internal/IdGenerator.cs | 29 +- .../LowLevel/Internal/PageResources.cs | 147 +- .../LowLevel/Internal/PageTree.cs | 37 +- .../LowLevel/ObjectStream.cs | 2 +- .../LowLevel/PdfDictionary.cs | 693 ++++--- .../LowLevel/PdfName.cs | 211 ++- .../LowLevel/PdfReference.cs | 67 +- .../LowLevel/PdfStream.cs | 277 ++- .../LowLevel/StreamFilter.cs | 19 +- .../LowLevel/Text/TextState.cs | 103 -- .../LowLevel/XRef/Entry.cs | 85 +- .../LowLevel/XRef/Section.cs | 61 +- .../LowLevel/XRef/Table.cs | 87 +- .../LowLevel/XRef/TableBuilder.cs | 82 +- src/Synercoding.FileFormats.Pdf/Matrix.cs | 451 +++-- src/Synercoding.FileFormats.Pdf/PdfPage.cs | 149 +- src/Synercoding.FileFormats.Pdf/PdfWriter.cs | 473 +++-- 45 files changed, 3419 insertions(+), 3714 deletions(-) delete mode 100644 src/Synercoding.FileFormats.Pdf/IPath.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Internal/DocumentResources.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/LowLevel/Text/TextState.cs diff --git a/samples/Synercoding.FileFormats.Pdf.ConsoleTester/Program.cs b/samples/Synercoding.FileFormats.Pdf.ConsoleTester/Program.cs index b1069e3..55514e1 100644 --- a/samples/Synercoding.FileFormats.Pdf.ConsoleTester/Program.cs +++ b/samples/Synercoding.FileFormats.Pdf.ConsoleTester/Program.cs @@ -8,208 +8,196 @@ using System.IO; using static Synercoding.Primitives.ValueCreator; -namespace Synercoding.FileFormats.Pdf.ConsoleTester +namespace Synercoding.FileFormats.Pdf.ConsoleTester; + +public class Program { - public class Program + public static void Main(string[] args) { - public static void Main(string[] args) - { - var fileName = "out.pdf"; + var fileName = "out.pdf"; - if (File.Exists(fileName)) - File.Delete(fileName); + if (File.Exists(fileName)) + File.Delete(fileName); - using (var fs = File.OpenWrite(fileName)) - using (var writer = new PdfWriter(fs)) - { - var bleed = Mm(3); - var mediaBox = Sizes.A4.Expand(bleed).AsRectangle(); - var trimBox = mediaBox.Contract(bleed); + using (var fs = File.OpenWrite(fileName)) + using (var writer = new PdfWriter(fs)) + { + var bleed = Mm(3); + var mediaBox = Sizes.A4.Expand(bleed).AsRectangle(); + var trimBox = mediaBox.Contract(bleed); - writer - // Set document info - .SetDocumentInfo(info => - { - info.Author = "Gerard Gunnewijk"; - info.Title = "Example 1"; - info.Creator = "Synercoding.FileFormats.Pdf"; - info.ExtraInfo.Add("CutContourProgramId", "cloud-shape"); - }) - // Add image to writer directly and then use that image in the page - .AddPage(page => - { - page.MediaBox = mediaBox; - page.TrimBox = trimBox; + writer + // Set document info + .SetDocumentInfo(info => + { + info.Author = "Gerard Gunnewijk"; + info.Title = "Example 1"; + info.Creator = "Synercoding.FileFormats.Pdf"; + info.ExtraInfo.Add("CutContourProgramId", "cloud-shape"); + }) + // Add image to writer directly and then use that image in the page + .AddPage(page => + { + page.MediaBox = mediaBox; + page.TrimBox = trimBox; - using (var blurStream = File.OpenRead("Pexels_com/4k-wallpaper-blur-bokeh-1484253.jpg")) - { - var addedImage = writer.AddJpgUnsafe(blurStream, 7000, 4672, DeviceRGB.Instance); - var scale = (double)addedImage.Width / addedImage.Height; - page.Content.AddImage(addedImage, new Rectangle(0, 0, scale * 303, 303, Unit.Millimeters)); - } - }) - // Add text to page and use it as the clipping path - .AddPage(page => + using (var blurStream = File.OpenRead("Pexels_com/4k-wallpaper-blur-bokeh-1484253.jpg")) { - page.MediaBox = mediaBox; - page.TrimBox = trimBox; + var addedImage = writer.AddJpgUnsafe(blurStream, 7000, 4672, DeviceRGB.Instance); + var scale = (double)addedImage.Width / addedImage.Height; + page.Content.AddImage(addedImage, new Rectangle(0, 0, scale * 303, 303, Unit.Millimeters)); + } + }) + // Add text to page and use it as the clipping path + .AddPage(page => + { + page.MediaBox = mediaBox; + page.TrimBox = trimBox; - page.Content.WrapInState(content => + page.Content.WrapInState(content => + { + content.AddText(textOp => { - content.AddText(textOp => - { - textOp.SetTextRenderingMode(TextRenderingMode.AddClippingPath) - .SetFontAndSize(StandardFonts.Helvetica, 160) - .SetTextLeading(500) - .MoveToStartNextLine(Mm(10).AsRaw(Unit.Points), Mm(200).AsRaw(Unit.Points)) - .ShowText("Clipped") - .SetFontAndSize(StandardFonts.HelveticaBold, 650) - .ShowTextOnNextLine("it!"); - }); - - using (var forestStream = File.OpenRead("Pexels_com/android-wallpaper-art-backlit-1114897.jpg")) - using (var forestImage = SixLabors.ImageSharp.Image.Load(forestStream)) - { - var scale = (double)forestImage.Width / forestImage.Height; - - var matrix = Matrix.CreateScaleMatrix(new Value(scale * 303, Unit.Millimeters).AsRaw(Unit.Points), new Value(303, Unit.Millimeters).AsRaw(Unit.Points)) - .Translate(new Value(-100, Unit.Millimeters).AsRaw(Unit.Points), new Value(0, Unit.Millimeters).AsRaw(Unit.Points)); - - page.Content.AddImage(forestImage, matrix); - } + textOp.SetTextRenderingMode(TextRenderingMode.AddClippingPath) + .SetFontAndSize(StandardFonts.Helvetica, 160) + .SetTextLeading(500) + .MoveToStartNextLine(Mm(10).AsRaw(Unit.Points), Mm(200).AsRaw(Unit.Points)) + .ShowText("Clipped") + .SetFontAndSize(StandardFonts.HelveticaBold, 650) + .ShowTextOnNextLine("it!"); }); - }) - // Test placement using rectangle - .AddPage(page => - { - page.MediaBox = mediaBox; - page.TrimBox = trimBox; - using (var barrenStream = File.OpenRead("Pexels_com/arid-barren-desert-1975514.jpg")) - using (var barrenImage = SixLabors.ImageSharp.Image.Load(barrenStream)) + using (var forestStream = File.OpenRead("Pexels_com/android-wallpaper-art-backlit-1114897.jpg")) + using (var forestImage = SixLabors.ImageSharp.Image.Load(forestStream)) { - var scale = (double)barrenImage.Width / barrenImage.Height; + var scale = (double)forestImage.Width / forestImage.Height; + + var matrix = Matrix.CreateScaleMatrix(new Value(scale * 303, Unit.Millimeters).AsRaw(Unit.Points), new Value(303, Unit.Millimeters).AsRaw(Unit.Points)) + .Translate(new Value(-100, Unit.Millimeters).AsRaw(Unit.Points), new Value(0, Unit.Millimeters).AsRaw(Unit.Points)); - page.Content.AddImage(barrenImage, new Rectangle(0, 0, scale * 303, 303, Unit.Millimeters)); + page.Content.AddImage(forestImage, matrix); } + }); + }) + // Test placement using rectangle + .AddPage(page => + { + page.MediaBox = mediaBox; + page.TrimBox = trimBox; - using (var eyeStream = File.OpenRead("Pexels_com/adult-blue-blue-eyes-865711.jpg")) - { - var scale = 3456d / 5184; + using (var barrenStream = File.OpenRead("Pexels_com/arid-barren-desert-1975514.jpg")) + using (var barrenImage = SixLabors.ImageSharp.Image.Load(barrenStream)) + { + var scale = (double)barrenImage.Width / barrenImage.Height; - var width = 100; - var height = 100 * scale; + page.Content.AddImage(barrenImage, new Rectangle(0, 0, scale * 303, 303, Unit.Millimeters)); + } - var offSet = 6; - page.Content.AddImage(eyeStream, new Rectangle(offSet, offSet, width + offSet, height + offSet, Unit.Millimeters)); - } - }) - // Test shape graphics - .AddPage(page => + using (var eyeStream = File.OpenRead("Pexels_com/adult-blue-blue-eyes-865711.jpg")) { - page.MediaBox = mediaBox; - page.TrimBox = trimBox; + var scale = 3456d / 5184; - page.Content.AddShapes(ctx => - { - ctx.SetMiterLimit(10) - .SetLineCap(LowLevel.Graphics.LineCapStyle.ButtCap) - .SetLineJoin(LowLevel.Graphics.LineJoinStyle.MiterJoin); - - ctx.Move(100, 100) - .LineTo(200, 100) - .LineTo(200, 200) - .LineTo(100, 200) - .SetLineWidth(5) - .SetStroke(PredefinedColors.Black) - .SetFill(PredefinedColors.Red) - .FillThenStroke(LowLevel.FillRule.NonZeroWindingNumber); - - ctx.Move(50, 50) - .LineTo(150, 50) - .LineTo(150, 150) - .LineTo(50, 150) - .SetLineWidth(1) - .SetFill(PredefinedColors.Blue) - .CloseSubPath() - .Fill(LowLevel.FillRule.NonZeroWindingNumber); - - ctx.Move(150, 150) - .LineTo(250, 150) - .LineTo(250, 250) - .LineTo(150, 250) - .SetLineWidth(3) - .SetStroke(PredefinedColors.Yellow) - .SetDashPattern(new LowLevel.Graphics.Dash() { Array = new[] { 5d } }) - .CloseSubPath() - .Stroke(); - }); - }) - // Test pages with text - .AddPage(page => - { - page.MediaBox = mediaBox; - page.TrimBox = trimBox; + var width = 100; + var height = 100 * scale; - page.Content.AddText(ops => - { - ops.MoveToStartNextLine(Mm(10).AsRaw(Unit.Points), Mm(10).AsRaw(Unit.Points)) - .SetFontAndSize(StandardFonts.Helvetica, 12) - .SetFill(PredefinedColors.Blue) - .ShowText("The quick brown fox jumps over the lazy dog."); - }); + var offSet = 6; + page.Content.AddImage(eyeStream, new Rectangle(offSet, offSet, width + offSet, height + offSet, Unit.Millimeters)); + } + }) + // Test shape graphics + .AddPage(page => + { + page.MediaBox = mediaBox; + page.TrimBox = trimBox; - page.Content.AddText("Text with a newline" + Environment.NewLine + "in it.", StandardFonts.Helvetica, 12, new Point(Mm(10), Mm(20))); - }) - .AddPage(page => + page.Content.AddShapes(ctx => { - page.MediaBox = mediaBox; - page.TrimBox = trimBox; + ctx.SetMiterLimit(10) + .SetLineCap(LowLevel.Graphics.LineCapStyle.ButtCap) + .SetLineJoin(LowLevel.Graphics.LineJoinStyle.MiterJoin); + + ctx.Move(100, 100) + .LineTo(200, 100) + .LineTo(200, 200) + .LineTo(100, 200) + .SetLineWidth(5) + .SetStroke(PredefinedColors.Black) + .SetFill(PredefinedColors.Red) + .FillThenStroke(LowLevel.FillRule.NonZeroWindingNumber); + + ctx.Move(50, 50) + .LineTo(150, 50) + .LineTo(150, 150) + .LineTo(50, 150) + .SetLineWidth(1) + .SetFill(PredefinedColors.Blue) + .CloseSubPath() + .Fill(LowLevel.FillRule.NonZeroWindingNumber); + + ctx.Move(150, 150) + .LineTo(250, 150) + .LineTo(250, 250) + .LineTo(150, 250) + .SetLineWidth(3) + .SetStroke(PredefinedColors.Yellow) + .SetDashPattern(new LowLevel.Graphics.Dash() { Array = new[] { 5d } }) + .CloseSubPath() + .Stroke(); + }); + }) + // Test pages with text + .AddPage(page => + { + page.MediaBox = mediaBox; + page.TrimBox = trimBox; - page.Content.AddText("This page also used Helvetica", StandardFonts.Helvetica, 32, textContext => - { - textContext.MoveToStartNextLine(Mm(10).AsRaw(Unit.Points), Mm(10).AsRaw(Unit.Points)) - .SetTextRenderingMode(TextRenderingMode.Stroke) - .SetStroke(PredefinedColors.Red); - }); - }) - // Test placement using matrix - .AddPage(page => + page.Content.AddText(ops => { - page.MediaBox = mediaBox; - page.TrimBox = trimBox; - - using (var forestStream = File.OpenRead("Pexels_com/android-wallpaper-art-backlit-1114897.jpg")) - using (var forestImage = SixLabors.ImageSharp.Image.Load(forestStream)) - { - var scale = (double)forestImage.Width / forestImage.Height; + ops.MoveToStartNextLine(Mm(10).AsRaw(Unit.Points), Mm(10).AsRaw(Unit.Points)) + .SetFontAndSize(StandardFonts.Helvetica, 12) + .SetFill(PredefinedColors.Blue) + .ShowText("The quick brown fox jumps over the lazy dog."); + }); - var matrix = Matrix.CreateScaleMatrix(new Value(scale * 303, Unit.Millimeters).AsRaw(Unit.Points), new Value(303, Unit.Millimeters).AsRaw(Unit.Points)) - .Translate(new Value(-100, Unit.Millimeters).AsRaw(Unit.Points), new Value(0, Unit.Millimeters).AsRaw(Unit.Points)); + page.Content.AddText("Text with a newline" + Environment.NewLine + "in it.", StandardFonts.Helvetica, 12, new Point(Mm(10), Mm(20))); + }) + .AddPage(page => + { + page.MediaBox = mediaBox; + page.TrimBox = trimBox; - page.Content.AddImage(forestImage, matrix); - } + page.Content.AddText("This page also used Helvetica", StandardFonts.Helvetica, 32, textContext => + { + textContext.MoveToStartNextLine(Mm(10).AsRaw(Unit.Points), Mm(10).AsRaw(Unit.Points)) + .SetTextRenderingMode(TextRenderingMode.Stroke) + .SetStroke(PredefinedColors.Red); }); - - using (var blurStream = File.OpenRead("Pexels_com/4k-wallpaper-blur-bokeh-1484253.jpg")) - using (var blurImage = SixLabors.ImageSharp.Image.Load(blurStream)) + }) + // Test placement using matrix + .AddPage(page => { - var reusedImage = writer.AddImage(blurImage); + page.MediaBox = mediaBox; + page.TrimBox = trimBox; - for (int i = 0; i < 4; i++) + using (var forestStream = File.OpenRead("Pexels_com/android-wallpaper-art-backlit-1114897.jpg")) + using (var forestImage = SixLabors.ImageSharp.Image.Load(forestStream)) { - writer.AddPage(page => - { - page.MediaBox = mediaBox; - page.TrimBox = trimBox; + var scale = (double)forestImage.Width / forestImage.Height; - var scale = (double)blurImage.Width / blurImage.Height; + var matrix = Matrix.CreateScaleMatrix(new Value(scale * 303, Unit.Millimeters).AsRaw(Unit.Points), new Value(303, Unit.Millimeters).AsRaw(Unit.Points)) + .Translate(new Value(-100, Unit.Millimeters).AsRaw(Unit.Points), new Value(0, Unit.Millimeters).AsRaw(Unit.Points)); - page.Content.AddImage(reusedImage, new Rectangle(0, 0, scale * 303, 303, Unit.Millimeters)); - }); + page.Content.AddImage(forestImage, matrix); } + }); + using (var blurStream = File.OpenRead("Pexels_com/4k-wallpaper-blur-bokeh-1484253.jpg")) + using (var blurImage = SixLabors.ImageSharp.Image.Load(blurStream)) + { + var reusedImage = writer.AddImage(blurImage); + for (int i = 0; i < 4; i++) + { writer.AddPage(page => { page.MediaBox = mediaBox; @@ -218,15 +206,26 @@ public static void Main(string[] args) var scale = (double)blurImage.Width / blurImage.Height; page.Content.AddImage(reusedImage, new Rectangle(0, 0, scale * 303, 303, Unit.Millimeters)); - - page.Content.AddShapes(trimBox, static (trim, context) => - { - context.SetStroke(new SpotColor(new Separation(LowLevel.PdfName.Get("CutContour"), PredefinedColors.Magenta), 1)); - context.Rectangle(trim); - context.Stroke(); - }); }); } + + + writer.AddPage(page => + { + page.MediaBox = mediaBox; + page.TrimBox = trimBox; + + var scale = (double)blurImage.Width / blurImage.Height; + + page.Content.AddImage(reusedImage, new Rectangle(0, 0, scale * 303, 303, Unit.Millimeters)); + + page.Content.AddShapes(trimBox, static (trim, context) => + { + context.SetStroke(new SpotColor(new Separation(LowLevel.PdfName.Get("CutContour"), PredefinedColors.Magenta), 1)); + context.Rectangle(trim); + context.Stroke(); + }); + }); } } } diff --git a/src/Synercoding.FileFormats.Pdf/DocumentInformation.cs b/src/Synercoding.FileFormats.Pdf/DocumentInformation.cs index b07a77c..6f5f3cb 100644 --- a/src/Synercoding.FileFormats.Pdf/DocumentInformation.cs +++ b/src/Synercoding.FileFormats.Pdf/DocumentInformation.cs @@ -2,66 +2,65 @@ using System; using System.Collections.Generic; -namespace Synercoding.FileFormats.Pdf +namespace Synercoding.FileFormats.Pdf; + +/// +/// This class contains information about the document +/// +public class DocumentInformation { - /// - /// This class contains information about the document - /// - public class DocumentInformation + internal DocumentInformation(PdfReference id) { - internal DocumentInformation(PdfReference id) - { - Reference = id; - } + Reference = id; + } - /// - /// The document's title - /// - public string? Title { get; set; } + /// + /// The document's title + /// + public string? Title { get; set; } - /// - /// The name of the person who created the document - /// - public string? Author { get; set; } + /// + /// The name of the person who created the document + /// + public string? Author { get; set; } - /// - /// The subject of the document - /// - public string? Subject { get; set; } + /// + /// The subject of the document + /// + public string? Subject { get; set; } - /// - /// Keywords associated with the document - /// - public string? Keywords { get; set; } + /// + /// Keywords associated with the document + /// + public string? Keywords { get; set; } - /// - /// If the document was converted to PDF from another format, the name of the conforming product that created the original document from which it was converted. Otherwise the name of the application that created the document. - /// - public string? Creator { get; set; } + /// + /// If the document was converted to PDF from another format, the name of the conforming product that created the original document from which it was converted. Otherwise the name of the application that created the document. + /// + public string? Creator { get; set; } - /// - /// If the document was converted to PDF from another format, the name of the conforming product that converted it to PDF. - /// - public string? Producer { get; set; } + /// + /// If the document was converted to PDF from another format, the name of the conforming product that converted it to PDF. + /// + public string? Producer { get; set; } - /// - /// The date and time the document was created, in human-readable form. - /// - public DateTime? CreationDate { get; set; } = DateTime.Now; + /// + /// The date and time the document was created, in human-readable form. + /// + public DateTime? CreationDate { get; set; } = DateTime.Now; - /// - /// The date and time the document was most recently modified, in human-readable form. - /// - public DateTime? ModDate { get; set; } + /// + /// The date and time the document was most recently modified, in human-readable form. + /// + public DateTime? ModDate { get; set; } - /// - /// A pdf reference object that can be used to reference to this object - /// - public PdfReference Reference { get; } + /// + /// A pdf reference object that can be used to reference to this object + /// + public PdfReference Reference { get; } - /// - /// Extra information that will be added to the PDF meta data - /// - public IDictionary ExtraInfo { get; } = new Dictionary(); - } + /// + /// Extra information that will be added to the PDF meta data + /// + public IDictionary ExtraInfo { get; } = new Dictionary(); } diff --git a/src/Synercoding.FileFormats.Pdf/Extensions/IContentContextExtensions.cs b/src/Synercoding.FileFormats.Pdf/Extensions/IContentContextExtensions.cs index 9592f36..6af2d61 100644 --- a/src/Synercoding.FileFormats.Pdf/Extensions/IContentContextExtensions.cs +++ b/src/Synercoding.FileFormats.Pdf/Extensions/IContentContextExtensions.cs @@ -9,6 +9,6 @@ public static TContext WrapInState(this TContext context, Action context.WrapInState(contentOperations, static (operations, context) => operations(context)); public static Task WrapInStateAsync(this TContext context, Func contentOperations) - where TContext: IContentContext + where TContext : IContentContext => context.WrapInStateAsync(contentOperations, static (operations, context) => operations(context)); } diff --git a/src/Synercoding.FileFormats.Pdf/Extensions/IPageContentContextExtensions.cs b/src/Synercoding.FileFormats.Pdf/Extensions/IPageContentContextExtensions.cs index 3f06955..d84ba60 100644 --- a/src/Synercoding.FileFormats.Pdf/Extensions/IPageContentContextExtensions.cs +++ b/src/Synercoding.FileFormats.Pdf/Extensions/IPageContentContextExtensions.cs @@ -87,10 +87,7 @@ public static IPageContentContext AddText(this IPageContentContext context, stri => context.AddText(text, font, size, ops => { }); public static IPageContentContext AddText(this IPageContentContext context, string text, Font font, double size, Point location) - => context.AddText(text, font, size, location, static (location, ops) => - { - ops.MoveToStartNextLine(location.X.AsRaw(Unit.Points), location.Y.AsRaw(Unit.Points)); - }); + => context.AddText(text, font, size, location, static (location, ops) => ops.MoveToStartNextLine(location.X.AsRaw(Unit.Points), location.Y.AsRaw(Unit.Points))); public static IPageContentContext AddText(this IPageContentContext context, string text, Font font, double size, Action extraOperations) => context.AddText(text, font, size, extraOperations, static (extraOperations, context) => extraOperations(context)); @@ -113,13 +110,9 @@ public static IPageContentContext AddText(this IPageContentContext context, s for (int i = 0; i < lines.Length; i++) { if (i == 0) - { context.ShowText(lines[i]); - } else - { context.ShowTextOnNextLine(lines[i]); - } } }); } diff --git a/src/Synercoding.FileFormats.Pdf/Extensions/PrimitiveExtensions.cs b/src/Synercoding.FileFormats.Pdf/Extensions/PrimitiveExtensions.cs index 33a5ef1..9471c61 100644 --- a/src/Synercoding.FileFormats.Pdf/Extensions/PrimitiveExtensions.cs +++ b/src/Synercoding.FileFormats.Pdf/Extensions/PrimitiveExtensions.cs @@ -1,24 +1,23 @@ using Synercoding.Primitives; -namespace Synercoding.FileFormats.Pdf.Extensions +namespace Synercoding.FileFormats.Pdf.Extensions; + +/// +/// Extension methods for primitives +/// +public static class PrimitiveExtensions { /// - /// Extension methods for primitives + /// Convert a to a transformation matrix /// - public static class PrimitiveExtensions + /// The to use + /// Returns a representing the provided . + public static Matrix AsPlacementMatrix(this Rectangle rectangle) { - /// - /// Convert a to a transformation matrix - /// - /// The to use - /// Returns a representing the provided . - public static Matrix AsPlacementMatrix(this Rectangle rectangle) - { - rectangle = rectangle.ConvertTo(Unit.Points); - return new Matrix( - rectangle.URX.Raw - rectangle.LLX.Raw, 0, - 0, rectangle.URY.Raw - rectangle.LLY.Raw, - rectangle.LLX.Raw, rectangle.LLY.Raw); - } + rectangle = rectangle.ConvertTo(Unit.Points); + return new Matrix( + rectangle.URX.Raw - rectangle.LLX.Raw, 0, + 0, rectangle.URY.Raw - rectangle.LLY.Raw, + rectangle.LLX.Raw, rectangle.LLY.Raw); } } diff --git a/src/Synercoding.FileFormats.Pdf/IPath.cs b/src/Synercoding.FileFormats.Pdf/IPath.cs deleted file mode 100644 index 8dea653..0000000 --- a/src/Synercoding.FileFormats.Pdf/IPath.cs +++ /dev/null @@ -1,133 +0,0 @@ -using Synercoding.Primitives; - -namespace Synercoding.FileFormats.Pdf -{ - /// - /// Interface representing a path in the content stream of a page - /// - public interface IPath - { - /// - /// Begin a new subpath by moving the current point to the coordinates (, ), - /// ommitting any connecting line segment. Appends an (m) operator to the content stream - /// - /// The X coordinate of the move - /// The Y coordinate of the move - /// The calling to support chaining operations. - IPath Move(double x, double y); - - /// - /// Begin a new subpath by moving the current point to the coordinates (), - /// ommitting any connecting line segment. Appends an (m) operator to the content stream - /// - /// The point to where to move - /// The calling to support chaining operations. - IPath Move(Point point); - - /// - /// Add a line (l) operator to the content stream - /// - /// The X coordinate of the line end point - /// The Y coordinate of the line end point - /// The calling to support chaining operations. - IPath LineTo(double x, double y); - - /// - /// Add a line (l) operator to the content stream - /// - /// The point to where to line to - /// The calling to support chaining operations. - IPath LineTo(Point point); - - /// - /// Add a rectangle (re) operator to the content stream - /// - /// The X coordinate of the rectangle - /// The Y coordinate of the rectangle - /// The width of the rectangle - /// The height of the rectangle - /// The calling to support chaining operations. - IPath Rectangle(double x, double y, double width, double height); - - /// - /// Add a rectangle (re) operator to the content stream - /// - /// The rectangle to add to the content stream - /// The calling to support chaining operations. - IPath Rectangle(Rectangle rectangle); - - /// - /// Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (, ), - /// using (, ) and (, ) as the Bézier control points. - /// Adds a Cubic Bézier Curve (c) operator to the content stream. - /// - /// The X coordinate of the first control point - /// The Y coordinate of the first control point - /// The X coordinate of the second control point - /// The Y coordinate of the second control point - /// The X coordinate of the endpoint of the curve - /// The Y coordinate of the endpoint of the curve - /// The calling to support chaining operations. - IPath CurveTo(double cpX1, double cpY1, double cpX2, double cpY2, double finalX, double finalY); - - /// - /// Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (), - /// using () and () as the Bézier control points. - /// Adds a Cubic Bézier Curve (c) operator to the content stream. - /// - /// The first control point - /// The second control point - /// The endpoint of the curve - /// The calling to support chaining operations. - IPath CurveTo(Point cp1, Point cp2, Point final); - - /// - /// Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (, ), - /// using the current point and (, ) as the Bézier control points. - /// Adds a Cubic Bézier Curve (v) operator to the content stream. - /// - /// The X coordinate of the second control point - /// The Y coordinate of the second control point - /// The X coordinate of the endpoint of the curve - /// The Y coordinate of the endpoint of the curve - /// The calling to support chaining operations. - IPath CurveToWithStartAnker(double cpX2, double cpY2, double finalX, double finalY); - - /// - /// Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (), - /// using the current point and () as the Bézier control points. - /// Adds a Cubic Bézier Curve (v) operator to the content stream. - /// - /// The second control point - /// The endpoint of the curve - /// The calling to support chaining operations. - IPath CurveToWithStartAnker(Point cp2, Point final); - - /// - /// Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (, ), - /// using (, ) and (, ) as the Bézier control points. - /// Adds a Cubic Bézier Curve (y) operator to the content stream. - /// - /// The X coordinate of the first control point - /// The Y coordinate of the first control point - /// The X coordinate of the endpoint of the curve - /// The Y coordinate of the endpoint of the curve - /// The calling to support chaining operations. - IPath CurveToWithEndAnker(double cpX1, double cpY1, double finalX, double finalY); - - /// - /// Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (), - /// using () and () as the Bézier control points. - /// Adds a Cubic Bézier Curve (y) operator to the content stream. - /// - /// The first control point - /// The endpoint of the curve - /// The calling to support chaining operations. - IPath CurveToWithEndAnker(Point cp1, Point final); - - /// - /// Close the - /// - void Close(); - } -} diff --git a/src/Synercoding.FileFormats.Pdf/IShapeContext.cs b/src/Synercoding.FileFormats.Pdf/IShapeContext.cs index 96059dd..1ac6853 100644 --- a/src/Synercoding.FileFormats.Pdf/IShapeContext.cs +++ b/src/Synercoding.FileFormats.Pdf/IShapeContext.cs @@ -1,5 +1,4 @@ using Synercoding.FileFormats.Pdf.LowLevel; -using Synercoding.Primitives; namespace Synercoding.FileFormats.Pdf; @@ -11,7 +10,7 @@ public interface IShapeContext : IContentContext /// /// The X coordinate of the move /// The Y coordinate of the move - /// The calling to support chaining operations. + /// The calling to support chaining operations. IShapeContext Move(double x, double y); diff --git a/src/Synercoding.FileFormats.Pdf/Image.cs b/src/Synercoding.FileFormats.Pdf/Image.cs index 1b224eb..e765f4d 100644 --- a/src/Synercoding.FileFormats.Pdf/Image.cs +++ b/src/Synercoding.FileFormats.Pdf/Image.cs @@ -4,92 +4,91 @@ using System; using System.IO; -namespace Synercoding.FileFormats.Pdf +namespace Synercoding.FileFormats.Pdf; + +/// +/// Class representing an image inside a pdf +/// +public sealed class Image : IDisposable { - /// - /// Class representing an image inside a pdf - /// - public sealed class Image : IDisposable + private bool _disposed; + + internal Image(PdfReference id, SixLabors.ImageSharp.Image image) { - private bool _disposed; + Reference = id; - internal Image(PdfReference id, SixLabors.ImageSharp.Image image) + var ms = new MemoryStream(); + image.SaveAsJpeg(ms, new SixLabors.ImageSharp.Formats.Jpeg.JpegEncoder() { - Reference = id; - - var ms = new MemoryStream(); - image.SaveAsJpeg(ms, new SixLabors.ImageSharp.Formats.Jpeg.JpegEncoder() - { - Quality = 100, - ColorType = SixLabors.ImageSharp.Formats.Jpeg.JpegColorType.YCbCrRatio444 - }); - Width = image.Width; - Height = image.Height; - ColorSpace = DeviceRGB.Instance.Name; - DecodeArray = new double[] { 0, 1, 0, 1, 0, 1 }; - ms.Position = 0; - RawStream = ms; - } + Quality = 100, + ColorType = SixLabors.ImageSharp.Formats.Jpeg.JpegColorType.YCbCrRatio444 + }); + Width = image.Width; + Height = image.Height; + ColorSpace = DeviceRGB.Instance.Name; + DecodeArray = new double[] { 0, 1, 0, 1, 0, 1 }; + ms.Position = 0; + RawStream = ms; + } - internal Image(PdfReference id, Stream jpgStream, int width, int height, ColorSpace colorSpace) - { - Reference = id; + internal Image(PdfReference id, Stream jpgStream, int width, int height, ColorSpace colorSpace) + { + Reference = id; - Width = width; - Height = height; - RawStream = jpgStream; + Width = width; + Height = height; + RawStream = jpgStream; - var (csName, decodeArray) = colorSpace switch - { - DeviceCMYK cmyk => (cmyk.Name, new double[] { 0, 1, 0, 1, 0, 1, 0, 1 }), - DeviceRGB rgb => (rgb.Name, new double[] { 0, 1, 0, 1, 0, 1 }), - _ => throw new ArgumentOutOfRangeException(nameof(colorSpace), $"The provided color space {colorSpace} is currently not supported.") - }; + var (csName, decodeArray) = colorSpace switch + { + DeviceCMYK cmyk => (cmyk.Name, new double[] { 0, 1, 0, 1, 0, 1, 0, 1 }), + DeviceRGB rgb => (rgb.Name, new double[] { 0, 1, 0, 1, 0, 1 }), + _ => throw new ArgumentOutOfRangeException(nameof(colorSpace), $"The provided color space {colorSpace} is currently not supported.") + }; - ColorSpace = csName; - DecodeArray = decodeArray; - } + ColorSpace = csName; + DecodeArray = decodeArray; + } - internal Image(PdfReference id, Stream jpgStream, int width, int height, PdfName colorSpace, double[] decodeArray) - { - Reference = id; + internal Image(PdfReference id, Stream jpgStream, int width, int height, PdfName colorSpace, double[] decodeArray) + { + Reference = id; - Width = width; - Height = height; - RawStream = jpgStream; - ColorSpace = colorSpace; - DecodeArray = decodeArray; - } + Width = width; + Height = height; + RawStream = jpgStream; + ColorSpace = colorSpace; + DecodeArray = decodeArray; + } - internal Stream RawStream { get; private set; } + internal Stream RawStream { get; private set; } - /// - /// A pdf reference object that can be used to reference to this object - /// - public PdfReference Reference { get; private set; } + /// + /// A pdf reference object that can be used to reference to this object + /// + public PdfReference Reference { get; private set; } - /// - /// The width of this - /// - public int Width { get; } + /// + /// The width of this + /// + public int Width { get; } - /// - /// The height of this - /// - public int Height { get; } + /// + /// The height of this + /// + public int Height { get; } - public PdfName ColorSpace { get; } + public PdfName ColorSpace { get; } - public double[] DecodeArray { get; } + public double[] DecodeArray { get; } - /// - public void Dispose() + /// + public void Dispose() + { + if (!_disposed) { - if (!_disposed) - { - RawStream.Dispose(); - _disposed = true; - } + RawStream.Dispose(); + _disposed = true; } } } diff --git a/src/Synercoding.FileFormats.Pdf/Internals/ByteSizes.cs b/src/Synercoding.FileFormats.Pdf/Internals/ByteSizes.cs index e59a3d3..07daa5b 100644 --- a/src/Synercoding.FileFormats.Pdf/Internals/ByteSizes.cs +++ b/src/Synercoding.FileFormats.Pdf/Internals/ByteSizes.cs @@ -1,29 +1,28 @@ using System; using System.Globalization; -namespace Synercoding.FileFormats.Pdf.Internals +namespace Synercoding.FileFormats.Pdf.Internals; + +internal static class ByteSizes { - internal static class ByteSizes + public static int Size(double value) { - public static int Size(double value) - { - return value.ToString("G", CultureInfo.InvariantCulture).Length; - } + return value.ToString("G", CultureInfo.InvariantCulture).Length; + } - public static int Size(int integer) - { - if (integer == 0) - return 1; + public static int Size(int integer) + { + if (integer == 0) + return 1; - return (int)Math.Floor(Math.Log10(integer)) + 1; - } + return (int)Math.Floor(Math.Log10(integer)) + 1; + } - public static int Size(uint integer) - { - if (integer == 0) - return 1; + public static int Size(uint integer) + { + if (integer == 0) + return 1; - return (int)Math.Floor(Math.Log10(integer)) + 1; - } + return (int)Math.Floor(Math.Log10(integer)) + 1; } } diff --git a/src/Synercoding.FileFormats.Pdf/Internals/Map.cs b/src/Synercoding.FileFormats.Pdf/Internals/Map.cs index bd6dd0c..1fd6969 100644 --- a/src/Synercoding.FileFormats.Pdf/Internals/Map.cs +++ b/src/Synercoding.FileFormats.Pdf/Internals/Map.cs @@ -2,85 +2,84 @@ using System.Collections; using System.Collections.Generic; -namespace Synercoding.FileFormats.Pdf.Internals +namespace Synercoding.FileFormats.Pdf.Internals; + +internal sealed class Map : IEnumerable> + where T1 : notnull + where T2 : notnull { - internal sealed class Map : IEnumerable> - where T1 : notnull - where T2 : notnull + private readonly IDictionary _forward = new Dictionary(); + private readonly IDictionary _reverse = new Dictionary(); + + public Map() { - private readonly IDictionary _forward = new Dictionary(); - private readonly IDictionary _reverse = new Dictionary(); + Forward = new Indexer(_forward); + Reverse = new Indexer(_reverse); + } - public Map() - { - Forward = new Indexer(_forward); - Reverse = new Indexer(_reverse); - } + public int Count => _forward.Count; - public int Count => _forward.Count; + public void Add(T1 t1, T2 t2) + { + _forward.Add(t1, t2); + _reverse.Add(t2, t1); + } - public void Add(T1 t1, T2 t2) - { - _forward.Add(t1, t2); - _reverse.Add(t2, t1); - } + public void Clear() + { + _forward.Clear(); + _reverse.Clear(); + } - public void Clear() - { - _forward.Clear(); - _reverse.Clear(); - } + public IEnumerator> GetEnumerator() + { + return _forward.GetEnumerator(); + } - public IEnumerator> GetEnumerator() - { - return _forward.GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + public Indexer Forward { get; } + public Indexer Reverse { get; } - public Indexer Forward { get; } - public Indexer Reverse { get; } + public sealed class Indexer : IReadOnlyDictionary + where T3 : notnull + where T4 : notnull + { + private readonly IDictionary _dictionary; - public sealed class Indexer : IReadOnlyDictionary - where T3 : notnull - where T4 : notnull + public Indexer(IDictionary dictionary) { - private readonly IDictionary _dictionary; - - public Indexer(IDictionary dictionary) - { - _dictionary = dictionary; - } + _dictionary = dictionary; + } - public T4 this[T3 index] - { - get => _dictionary[index]; - set => _dictionary[index] = value; - } + public T4 this[T3 index] + { + get => _dictionary[index]; + set => _dictionary[index] = value; + } - public IEnumerable Keys => _dictionary.Keys; + public IEnumerable Keys => _dictionary.Keys; - public IEnumerable Values => _dictionary.Values; + public IEnumerable Values => _dictionary.Values; - public int Count => _dictionary.Count; + public int Count => _dictionary.Count; - public bool Contains(T3 value) - => _dictionary.ContainsKey(value); + public bool Contains(T3 value) + => _dictionary.ContainsKey(value); - public bool ContainsKey(T3 key) - => _dictionary.ContainsKey(key); + public bool ContainsKey(T3 key) + => _dictionary.ContainsKey(key); - public IEnumerator> GetEnumerator() - => _dictionary.GetEnumerator(); + public IEnumerator> GetEnumerator() + => _dictionary.GetEnumerator(); - public bool TryGetValue(T3 key, out T4 value) - => throw new InvalidOperationException("TryGetValue is not supported on types with non-nullable values."); + public bool TryGetValue(T3 key, out T4 value) + => throw new InvalidOperationException("TryGetValue is not supported on types with non-nullable values."); - IEnumerator IEnumerable.GetEnumerator() - => _dictionary.GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() + => _dictionary.GetEnumerator(); } } diff --git a/src/Synercoding.FileFormats.Pdf/Internals/PageContentContext.cs b/src/Synercoding.FileFormats.Pdf/Internals/PageContentContext.cs index ce51df7..55ba135 100644 --- a/src/Synercoding.FileFormats.Pdf/Internals/PageContentContext.cs +++ b/src/Synercoding.FileFormats.Pdf/Internals/PageContentContext.cs @@ -1,7 +1,6 @@ using Synercoding.FileFormats.Pdf.LowLevel; using Synercoding.FileFormats.Pdf.LowLevel.Colors; using Synercoding.FileFormats.Pdf.LowLevel.Graphics; -using Synercoding.Primitives; using System; using System.Threading.Tasks; diff --git a/src/Synercoding.FileFormats.Pdf/Internals/SpanHelper.cs b/src/Synercoding.FileFormats.Pdf/Internals/SpanHelper.cs index 79e4d74..dbdbd45 100644 --- a/src/Synercoding.FileFormats.Pdf/Internals/SpanHelper.cs +++ b/src/Synercoding.FileFormats.Pdf/Internals/SpanHelper.cs @@ -1,46 +1,45 @@ using System; -namespace Synercoding.FileFormats.Pdf.Internals +namespace Synercoding.FileFormats.Pdf.Internals; + +internal static class SpanHelper { - internal static class SpanHelper + public static int FillSpan(Span span, int integer) { - public static int FillSpan(Span span, int integer) + int val = integer; + var intSize = ByteSizes.Size(integer); + for (int i = intSize - 1; i >= 0; i--) { - int val = integer; - var intSize = ByteSizes.Size(integer); - for (int i = intSize - 1; i >= 0; i--) - { - span[i] = (byte)('0' + (val % 10)); - val = val / 10; - } - return intSize; + span[i] = (byte)( '0' + ( val % 10 ) ); + val /= 10; } + return intSize; + } - public static int FillSpan(Span span, uint integer) + public static int FillSpan(Span span, uint integer) + { + uint val = integer; + var intSize = ByteSizes.Size(integer); + for (int i = intSize - 1; i >= 0; i--) { - uint val = integer; - var intSize = ByteSizes.Size(integer); - for (int i = intSize - 1; i >= 0; i--) - { - span[i] = (byte)('0' + (val % 10)); - val = val / 10; - } - return intSize; + span[i] = (byte)( '0' + ( val % 10 ) ); + val /= 10; } + return intSize; + } - public static int WriteIndirectObjectRef(Span bytes, int objectRef, int generation) - { - int position = FillSpan(bytes, objectRef); + public static int WriteIndirectObjectRef(Span bytes, int objectRef, int generation) + { + int position = FillSpan(bytes, objectRef); - bytes[position++] = 0x20; // space + bytes[position++] = 0x20; // space - position += FillSpan(bytes.Slice(position), generation); + position += FillSpan(bytes[position..], generation); - bytes[position++] = 0x20; // space + bytes[position++] = 0x20; // space - bytes[position++] = 0x52; // R + bytes[position++] = 0x52; // R - return position; - } + return position; } } diff --git a/src/Synercoding.FileFormats.Pdf/Internals/WrappedShapesContext.cs b/src/Synercoding.FileFormats.Pdf/Internals/WrappedShapesContext.cs index b1f2bc5..3d68d18 100644 --- a/src/Synercoding.FileFormats.Pdf/Internals/WrappedShapesContext.cs +++ b/src/Synercoding.FileFormats.Pdf/Internals/WrappedShapesContext.cs @@ -1,4 +1,4 @@ -using Synercoding.FileFormats.Pdf.LowLevel; +using Synercoding.FileFormats.Pdf.LowLevel; using Synercoding.FileFormats.Pdf.LowLevel.Colors; using Synercoding.FileFormats.Pdf.LowLevel.Graphics; using System; diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/CmykColor.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/CmykColor.cs index ccb8d55..4dba62c 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/CmykColor.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/CmykColor.cs @@ -1,132 +1,131 @@ using Synercoding.FileFormats.Pdf.LowLevel.Colors.ColorSpaces; using System; -namespace Synercoding.FileFormats.Pdf.LowLevel.Colors +namespace Synercoding.FileFormats.Pdf.LowLevel.Colors; + +/// +/// Class representing a CMYK color +/// +public sealed class CmykColor : Color, IEquatable { + private const string COLOR_COMPONENT_OUT_OF_RANGE = "Color component value must be between 0.0 (zero concentration) and 1.0 (maximum concentration)."; + + private readonly double _cyan = 0; + private readonly double _magenta = 0; + private readonly double _yellow = 0; + private readonly double _key = 0; + /// - /// Class representing a CMYK color + /// Constructor for a /// - public sealed class CmykColor : Color, IEquatable + public CmykColor() + { } + + /// + /// Constructor for a + /// + /// The cyan component of the color + /// The magenta component of the color + /// The yellow component of the color + /// The key component of the color + public CmykColor(double cyan, double magenta, double yellow, double key) { - private const string COLOR_COMPONENT_OUT_OF_RANGE = "Color component value must be between 0.0 (zero concentration) and 1.0 (maximum concentration)."; - - private readonly double _cyan = 0; - private readonly double _magenta = 0; - private readonly double _yellow = 0; - private readonly double _key = 0; - - /// - /// Constructor for a - /// - public CmykColor() - { } - - /// - /// Constructor for a - /// - /// The cyan component of the color - /// The magenta component of the color - /// The yellow component of the color - /// The key component of the color - public CmykColor(double cyan, double magenta, double yellow, double key) - { - Cyan = cyan; - Magenta = magenta; - Yellow = yellow; - Key = key; - } + Cyan = cyan; + Magenta = magenta; + Yellow = yellow; + Key = key; + } - /// - /// The cyan component of this color - /// - /// Value must be between 0.0 (zero concentration) and 1.0 (maximum concentration). - public double Cyan + /// + /// The cyan component of this color + /// + /// Value must be between 0.0 (zero concentration) and 1.0 (maximum concentration). + public double Cyan + { + get => _cyan; + init { - get => _cyan; - init - { - if (value < 0 || value > 1) - throw new ArgumentOutOfRangeException(nameof(Cyan), COLOR_COMPONENT_OUT_OF_RANGE); - - _cyan = value; - } + if (value is < 0 or > 1) + throw new ArgumentOutOfRangeException(nameof(Cyan), COLOR_COMPONENT_OUT_OF_RANGE); + + _cyan = value; } + } - /// - /// The magenta component of this color - /// - /// Value must be between 0.0 (zero concentration) and 1.0 (maximum concentration). - public double Magenta + /// + /// The magenta component of this color + /// + /// Value must be between 0.0 (zero concentration) and 1.0 (maximum concentration). + public double Magenta + { + get => _magenta; + init { - get => _magenta; - init - { - if (value < 0 || value > 1) - throw new ArgumentOutOfRangeException(nameof(Magenta), COLOR_COMPONENT_OUT_OF_RANGE); - - _magenta = value; - } + if (value is < 0 or > 1) + throw new ArgumentOutOfRangeException(nameof(Magenta), COLOR_COMPONENT_OUT_OF_RANGE); + + _magenta = value; } + } - /// - /// The yellow component of this color - /// - /// Value must be between 0.0 (zero concentration) and 1.0 (maximum concentration). - public double Yellow + /// + /// The yellow component of this color + /// + /// Value must be between 0.0 (zero concentration) and 1.0 (maximum concentration). + public double Yellow + { + get => _yellow; + init { - get => _yellow; - init - { - if (value < 0 || value > 1) - throw new ArgumentOutOfRangeException(nameof(Yellow), COLOR_COMPONENT_OUT_OF_RANGE); - - _yellow = value; - } + if (value is < 0 or > 1) + throw new ArgumentOutOfRangeException(nameof(Yellow), COLOR_COMPONENT_OUT_OF_RANGE); + + _yellow = value; } + } - /// - /// The key component of this color - /// - /// Value must be between 0.0 (zero concentration) and 1.0 (maximum concentration). - public double Key + /// + /// The key component of this color + /// + /// Value must be between 0.0 (zero concentration) and 1.0 (maximum concentration). + public double Key + { + get => _key; + init { - get => _key; - init - { - if (value < 0 || value > 1) - throw new ArgumentOutOfRangeException(nameof(Key), COLOR_COMPONENT_OUT_OF_RANGE); - - _key = value; - } + if (value is < 0 or > 1) + throw new ArgumentOutOfRangeException(nameof(Key), COLOR_COMPONENT_OUT_OF_RANGE); + + _key = value; } + } - /// - public override ColorSpace Colorspace - => DeviceCMYK.Instance; + /// + public override ColorSpace Colorspace + => DeviceCMYK.Instance; - /// - public override double[] Components => new double[] { Cyan, Magenta, Yellow, Key }; + /// + public override double[] Components => new double[] { Cyan, Magenta, Yellow, Key }; - /// - public bool Equals(CmykColor? other) - { - return other is not null - && Cyan == other.Cyan - && Magenta == other.Magenta - && Yellow == other.Yellow - && Key == other.Key; - } + /// + public bool Equals(CmykColor? other) + { + return other is not null + && Cyan == other.Cyan + && Magenta == other.Magenta + && Yellow == other.Yellow + && Key == other.Key; + } - /// - public override bool Equals(Color? other) - => Equals(other as CmykColor); + /// + public override bool Equals(Color? other) + => Equals(other as CmykColor); - /// - public override bool Equals(object? obj) - => Equals(obj as CmykColor); + /// + public override bool Equals(object? obj) + => Equals(obj as CmykColor); - /// - public override int GetHashCode() - => HashCode.Combine(Cyan, Magenta, Yellow, Key); - } + /// + public override int GetHashCode() + => HashCode.Combine(Cyan, Magenta, Yellow, Key); } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/Color.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/Color.cs index caff944..6b709ea 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/Color.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/Color.cs @@ -1,48 +1,47 @@ using Synercoding.FileFormats.Pdf.LowLevel.Colors.ColorSpaces; using System; -namespace Synercoding.FileFormats.Pdf.LowLevel.Colors +namespace Synercoding.FileFormats.Pdf.LowLevel.Colors; + +/// +/// Base class of all s. +/// +public abstract class Color : IEquatable { /// - /// Base class of all s. + /// The of this . + /// + public abstract ColorSpace Colorspace { get; } + + /// + /// Retrieve the components that make up this color. + /// + public abstract double[] Components { get; } + + /// + public abstract bool Equals(Color? other); + + /// + public abstract override bool Equals(object? obj); + + /// + public abstract override int GetHashCode(); + + /// + /// Indicates whether the left is equal to the right . + /// + /// The on the left side of the == + /// The on the right side of the == + /// true if the left Color is equal to the right; otherwise, false. + public static bool operator ==(Color left, Color right) + => left.Equals(right); + + /// + /// Indicates whether the left is not equal to the right . /// - public abstract class Color : IEquatable - { - /// - /// The of this . - /// - public abstract ColorSpace Colorspace { get; } - - /// - /// Retrieve the components that make up this color. - /// - public abstract double[] Components { get; } - - /// - public abstract bool Equals(Color? other); - - /// - public abstract override bool Equals(object? obj); - - /// - public abstract override int GetHashCode(); - - /// - /// Indicates whether the left is equal to the right . - /// - /// The on the left side of the == - /// The on the right side of the == - /// true if the left Color is equal to the right; otherwise, false. - public static bool operator ==(Color left, Color right) - => left.Equals(right); - - /// - /// Indicates whether the left is not equal to the right . - /// - /// The on the left side of the != - /// The on the right side of the != - /// true if the left Color is not equal to the right; otherwise, false. - public static bool operator !=(Color left, Color right) - => !( left == right ); - } + /// The on the left side of the != + /// The on the right side of the != + /// true if the left Color is not equal to the right; otherwise, false. + public static bool operator !=(Color left, Color right) + => !( left == right ); } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/GrayColor.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/GrayColor.cs index 7d64309..ec920d1 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/GrayColor.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/GrayColor.cs @@ -1,67 +1,66 @@ using Synercoding.FileFormats.Pdf.LowLevel.Colors.ColorSpaces; using System; -namespace Synercoding.FileFormats.Pdf.LowLevel.Colors +namespace Synercoding.FileFormats.Pdf.LowLevel.Colors; + +/// +/// Class representing a gray color +/// +public sealed class GrayColor : Color, IEquatable { /// - /// Class representing a gray color + /// Construct a new object /// - public sealed class GrayColor : Color, IEquatable + /// + public GrayColor(double gray) { - /// - /// Construct a new object - /// - /// - public GrayColor(double gray) - { - Gray = gray; - } + Gray = gray; + } - private readonly double _gray = 0; + private readonly double _gray = 0; - /// - /// The single component that represents the gray color. - /// - /// - /// 0.0 = Black - /// 1.0 = white - /// - public double Gray + /// + /// The single component that represents the gray color. + /// + /// + /// 0.0 = Black + /// 1.0 = white + /// + public double Gray + { + get => _gray; + init { - get => _gray; - init - { - if (value < 0 || value > 1) - throw new ArgumentOutOfRangeException(nameof(Gray), "Gray value must be between 0.0 (black) and 1.0 (white)."); + if (value is < 0 or > 1) + throw new ArgumentOutOfRangeException(nameof(Gray), "Gray value must be between 0.0 (black) and 1.0 (white)."); - _gray = value; - } + _gray = value; } + } - /// - public override ColorSpace Colorspace - => DeviceGray.Instance; + /// + public override ColorSpace Colorspace + => DeviceGray.Instance; - /// - public override double[] Components => new double[] { Gray }; + /// + public override double[] Components => new double[] { Gray }; - /// - public bool Equals(GrayColor? other) - { - return other is not null - && Gray == other.Gray; - } + /// + public bool Equals(GrayColor? other) + { + return other is not null + && Gray == other.Gray; + } - /// - public override bool Equals(Color? other) - => Equals(other as GrayColor); + /// + public override bool Equals(Color? other) + => Equals(other as GrayColor); - /// - public override bool Equals(object? obj) - => Equals(obj as GrayColor); + /// + public override bool Equals(object? obj) + => Equals(obj as GrayColor); - /// - public override int GetHashCode() - => HashCode.Combine(Gray); - } + /// + public override int GetHashCode() + => HashCode.Combine(Gray); } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/PredefinedColors.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/PredefinedColors.cs index ced3eb2..2e3cdb8 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/PredefinedColors.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/PredefinedColors.cs @@ -1,63 +1,62 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel.Colors +namespace Synercoding.FileFormats.Pdf.LowLevel.Colors; + +/// +/// Class with predefined colors +/// +public static class PredefinedColors { /// - /// Class with predefined colors - /// - public static class PredefinedColors - { - /// - /// Cyan - /// - public static Color Cyan { get; } = new CmykColor(1, 0, 0, 0); - - /// - /// Magenta - /// - public static Color Magenta { get; } = new CmykColor(0, 1, 0, 0); - - /// - /// Yellow - /// - public static Color Yellow { get; } = new CmykColor(0, 0, 1, 0); - - /// - /// Black - /// - public static Color Black { get; } = new GrayColor(0); - - /// - /// Dark gray - /// - public static Color DarkGray { get; } = new GrayColor(0.25); - - /// - /// Gray - /// - public static Color Gray { get; } = new GrayColor(0.5); - - /// - /// Light gray - /// - public static Color LightGray { get; } = new GrayColor(0.75); - - /// - /// White - /// - public static Color White { get; } = new GrayColor(1); - - /// - /// Red - /// - public static Color Red { get; } = new RgbColor(1, 0, 0); - - /// - /// Green - /// - public static Color Green { get; } = new RgbColor(0, 1, 0); - - /// - /// Blue - /// - public static Color Blue { get; } = new RgbColor(0, 0, 1); - } + /// Cyan + /// + public static Color Cyan { get; } = new CmykColor(1, 0, 0, 0); + + /// + /// Magenta + /// + public static Color Magenta { get; } = new CmykColor(0, 1, 0, 0); + + /// + /// Yellow + /// + public static Color Yellow { get; } = new CmykColor(0, 0, 1, 0); + + /// + /// Black + /// + public static Color Black { get; } = new GrayColor(0); + + /// + /// Dark gray + /// + public static Color DarkGray { get; } = new GrayColor(0.25); + + /// + /// Gray + /// + public static Color Gray { get; } = new GrayColor(0.5); + + /// + /// Light gray + /// + public static Color LightGray { get; } = new GrayColor(0.75); + + /// + /// White + /// + public static Color White { get; } = new GrayColor(1); + + /// + /// Red + /// + public static Color Red { get; } = new RgbColor(1, 0, 0); + + /// + /// Green + /// + public static Color Green { get; } = new RgbColor(0, 1, 0); + + /// + /// Blue + /// + public static Color Blue { get; } = new RgbColor(0, 0, 1); } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/RgbColor.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/RgbColor.cs index 16212f7..13d205a 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/RgbColor.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/RgbColor.cs @@ -1,121 +1,120 @@ using Synercoding.FileFormats.Pdf.LowLevel.Colors.ColorSpaces; using System; -namespace Synercoding.FileFormats.Pdf.LowLevel.Colors +namespace Synercoding.FileFormats.Pdf.LowLevel.Colors; + +/// +/// Class representing a RGB color +/// +public sealed class RgbColor : Color, IEquatable { + private const string COLOR_COMPONENT_OUT_OF_RANGE = "Color component value must be between 0.0 (minimum intensity) and 1.0 (maximum intensity)."; + + private readonly double _red = 0; + private readonly double _green = 0; + private readonly double _blue = 0; + + /// + /// Constructor for a + /// + public RgbColor() + { } + /// - /// Class representing a RGB color + /// Constructor for a /// - public sealed class RgbColor : Color, IEquatable + /// The red component of the color + /// The green component of the color + /// The blue component of the color + public RgbColor(double red, double green, double blue) { - private const string COLOR_COMPONENT_OUT_OF_RANGE = "Color component value must be between 0.0 (minimum intensity) and 1.0 (maximum intensity)."; - - private readonly double _red = 0; - private readonly double _green = 0; - private readonly double _blue = 0; - - /// - /// Constructor for a - /// - public RgbColor() - { } - - /// - /// Constructor for a - /// - /// The red component of the color - /// The green component of the color - /// The blue component of the color - public RgbColor(double red, double green, double blue) - { - Red = red; - Green = green; - Blue = blue; - } + Red = red; + Green = green; + Blue = blue; + } + + /// + /// Construct a from bytes values + /// + /// The red component of the color + /// The green component of the color + /// The blue component of the color + public static RgbColor FromBytes(byte red, byte green, byte blue) + => new RgbColor(red / 255d, green / 255d, blue / 255d); - /// - /// Construct a from bytes values - /// - /// The red component of the color - /// The green component of the color - /// The blue component of the color - public static RgbColor FromBytes(byte red, byte green, byte blue) - => new RgbColor(red / 255d, green / 255d, blue / 255d); - - /// - /// The red component of this color - /// - /// Value must be between 0.0 (minimum intensity) and 1.0 (maximum intensity). - public double Red + /// + /// The red component of this color + /// + /// Value must be between 0.0 (minimum intensity) and 1.0 (maximum intensity). + public double Red + { + get => _red; + init { - get => _red; - init - { - if (value < 0 || value > 1) - throw new ArgumentOutOfRangeException(nameof(Red), COLOR_COMPONENT_OUT_OF_RANGE); - - _red = value; - } + if (value is < 0 or > 1) + throw new ArgumentOutOfRangeException(nameof(Red), COLOR_COMPONENT_OUT_OF_RANGE); + + _red = value; } + } - /// - /// The green component of this color - /// - /// Value must be between 0.0 (minimum intensity) and 1.0 (maximum intensity). - public double Green + /// + /// The green component of this color + /// + /// Value must be between 0.0 (minimum intensity) and 1.0 (maximum intensity). + public double Green + { + get => _green; + init { - get => _green; - init - { - if (value < 0 || value > 1) - throw new ArgumentOutOfRangeException(nameof(Green), COLOR_COMPONENT_OUT_OF_RANGE); - - _green = value; - } + if (value is < 0 or > 1) + throw new ArgumentOutOfRangeException(nameof(Green), COLOR_COMPONENT_OUT_OF_RANGE); + + _green = value; } + } - /// - /// The blue component of this color - /// - /// Value must be between 0.0 (minimum intensity) and 1.0 (maximum intensity). - public double Blue + /// + /// The blue component of this color + /// + /// Value must be between 0.0 (minimum intensity) and 1.0 (maximum intensity). + public double Blue + { + get => _blue; + init { - get => _blue; - init - { - if (value < 0 || value > 1) - throw new ArgumentOutOfRangeException(nameof(Blue), COLOR_COMPONENT_OUT_OF_RANGE); - - _blue = value; - } + if (value is < 0 or > 1) + throw new ArgumentOutOfRangeException(nameof(Blue), COLOR_COMPONENT_OUT_OF_RANGE); + + _blue = value; } + } - /// - public override double[] Components => new double[] { Red, Green, Blue }; + /// + public override double[] Components => new double[] { Red, Green, Blue }; - /// - public override ColorSpace Colorspace - => DeviceRGB.Instance; + /// + public override ColorSpace Colorspace + => DeviceRGB.Instance; - /// - public bool Equals(RgbColor? other) - { - return other is not null - && Red == other.Red - && Green == other.Green - && Blue == other.Blue; - } + /// + public bool Equals(RgbColor? other) + { + return other is not null + && Red == other.Red + && Green == other.Green + && Blue == other.Blue; + } - /// - public override bool Equals(Color? other) - => Equals(other as RgbColor); + /// + public override bool Equals(Color? other) + => Equals(other as RgbColor); - /// - public override bool Equals(object? obj) - => Equals(obj as RgbColor); + /// + public override bool Equals(object? obj) + => Equals(obj as RgbColor); - /// - public override int GetHashCode() - => HashCode.Combine(Red, Green, Blue); - } + /// + public override int GetHashCode() + => HashCode.Combine(Red, Green, Blue); } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/SpotColor.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/SpotColor.cs index 0c66abb..9226516 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/SpotColor.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Colors/SpotColor.cs @@ -16,7 +16,7 @@ public class SpotColor : Color, IEquatable /// Throws if the < 0 or > 1 public SpotColor(Separation separation, double tint) { - if (tint < 0 || tint > 1) + if (tint is < 0 or > 1) throw new ArgumentOutOfRangeException(nameof(tint), $"Parameter {nameof(tint)} must be between 0.0 and 1.0 (inclusive)."); Separation = separation; Tint = tint; diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/ContentStream.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/ContentStream.cs index 9429a6d..5f5cd86 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/ContentStream.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/ContentStream.cs @@ -12,850 +12,849 @@ using Color = Synercoding.FileFormats.Pdf.LowLevel.Colors.Color; using Point = Synercoding.Primitives.Point; -namespace Synercoding.FileFormats.Pdf.LowLevel +namespace Synercoding.FileFormats.Pdf.LowLevel; + +/// +/// Class to represent a content stream +/// +public sealed class ContentStream : IDisposable { + private const string UNKNOWN_FILL_RULE = "Unknown fill rule"; + + internal ContentStream(PdfReference id, PageResources pageResources) + { + Resources = pageResources; + InnerStream = new PdfStream(new MemoryStream()); + + Reference = id; + } + + internal PdfStream InnerStream { get; private set; } + + internal PageResources Resources { get; } + /// - /// Class to represent a content stream + /// A pdf reference object that can be used to reference to this object /// - public sealed class ContentStream : IDisposable + public PdfReference Reference { get; } + + /// + public void Dispose() { - private const string UNKNOWN_FILL_RULE = "Unknown fill rule"; + InnerStream.Dispose(); + } - internal ContentStream(PdfReference id, PageResources pageResources) - { - Resources = pageResources; - InnerStream = new PdfStream(new MemoryStream()); + /// + /// Write a save state operator (q) to the stream + /// + /// The to support chaining operations. + public ContentStream SaveState() + { + InnerStream.Write('q').NewLine(); - Reference = id; - } + return this; + } - internal PdfStream InnerStream { get; private set; } + /// + /// Write a restore state operator (Q) to the stream + /// + /// The to support chaining operations. + public ContentStream RestoreState() + { + InnerStream.Write('Q').NewLine(); - internal PageResources Resources { get; } + return this; + } - /// - /// A pdf reference object that can be used to reference to this object - /// - public PdfReference Reference { get; } + /// + /// Write a transformation matrix operator (cm) to the stream + /// + /// The to write. + /// The to support chaining operations. + public ContentStream CTM(Matrix matrix) + { + InnerStream + .Write(matrix.A) + .Space() + .Write(matrix.B) + .Space() + .Write(matrix.C) + .Space() + .Write(matrix.D) + .Space() + .Write(matrix.E) + .Space() + .Write(matrix.F) + .Space() + .Write("cm") + .NewLine(); + + return this; + } - /// - public void Dispose() - { - InnerStream.Dispose(); - } - - /// - /// Write a save state operator (q) to the stream - /// - /// The to support chaining operations. - public ContentStream SaveState() - { - InnerStream.Write('q').NewLine(); + /// + /// Write the BT operator to the stream + /// + /// The to support chaining operations. + public ContentStream BeginText() + { + InnerStream + .Write("BT") + .NewLine(); - return this; - } + return this; + } - /// - /// Write a restore state operator (Q) to the stream - /// - /// The to support chaining operations. - public ContentStream RestoreState() - { - InnerStream.Write('Q').NewLine(); + /// + /// Write the font used to the content stream + /// + /// The font to write to the stream + /// The font size to use + /// The to support chaining operations. + public ContentStream SetFontAndSize(PdfName font, double size) + { + InnerStream + .Write(font) + .Space() + .Write(size) + .Space() + .Write("Tf") + .NewLine(); + + return this; + } - return this; - } + /// + /// Write the text leading to the content stream + /// + /// The leading value to write. + /// The to support chaining operations. + public ContentStream SetTextLeading(double leading) + { + InnerStream + .Write(leading) + .Space() + .Write("TL") + .NewLine(); - /// - /// Write a transformation matrix operator (cm) to the stream - /// - /// The to write. - /// The to support chaining operations. - public ContentStream CTM(Matrix matrix) - { - InnerStream - .Write(matrix.A) - .Space() - .Write(matrix.B) - .Space() - .Write(matrix.C) - .Space() - .Write(matrix.D) - .Space() - .Write(matrix.E) - .Space() - .Write(matrix.F) - .Space() - .Write("cm") - .NewLine(); - - return this; - } - - /// - /// Write the BT operator to the stream - /// - /// The to support chaining operations. - public ContentStream BeginText() - { - InnerStream - .Write("BT") - .NewLine(); - - return this; - } - - /// - /// Write the font used to the content stream - /// - /// The font to write to the stream - /// The font size to use - /// The to support chaining operations. - public ContentStream SetFontAndSize(PdfName font, double size) - { - InnerStream - .Write(font) - .Space() - .Write(size) - .Space() - .Write("Tf") - .NewLine(); - - return this; - } - - /// - /// Write the text leading to the content stream - /// - /// The leading value to write. - /// The to support chaining operations. - public ContentStream SetTextLeading(double leading) - { - InnerStream - .Write(leading) - .Space() - .Write("TL") - .NewLine(); - - return this; - } - - /// - /// Write the character spacing to the content stream - /// - /// The character spacing value. - /// The to support chaining operations. - public ContentStream SetCharacterSpacing(double characterSpace) - { - InnerStream - .Write(characterSpace) - .Space() - .Write("Tc") - .NewLine(); - - return this; - } - - /// - /// Write the horizontal scaling to the content stream - /// - /// The horizontal scaling value. - /// The to support chaining operations. - public ContentStream SetHorizontalScaling(double horizontalScaling) - { - InnerStream - .Write(horizontalScaling) - .Space() - .Write("Tz") - .NewLine(); - - return this; - } - - /// - /// Write the text rise to the content stream - /// - /// The text rise value. - /// The to support chaining operations. - public ContentStream SetTextRise(double textRise) - { - InnerStream - .Write(textRise) - .Space() - .Write("Ts") - .NewLine(); - - return this; - } - - /// - /// Write the text rendering mode to the content stream - /// - /// The text rendering mode value. - /// The to support chaining operations. - public ContentStream SetTextRenderMode(TextRenderingMode textRenderingMode) - { - InnerStream - .Write((int)textRenderingMode) - .Space() - .Write("Tr") - .NewLine(); - - return this; - } - - /// - /// Write the word spacing to the content stream - /// - /// The word space value. - /// The to support chaining operations. - public ContentStream SetWordSpacing(double wordSpace) - { - InnerStream - .Write(wordSpace) - .Space() - .Write("Tw") - .NewLine(); - - return this; - } - - /// - /// Write the end text operator to the content stream - /// - /// The to support chaining operations. - public ContentStream EndText() - { - InnerStream - .Write("ET") - .NewLine(); + return this; + } - return this; - } + /// + /// Write the character spacing to the content stream + /// + /// The character spacing value. + /// The to support chaining operations. + public ContentStream SetCharacterSpacing(double characterSpace) + { + InnerStream + .Write(characterSpace) + .Space() + .Write("Tc") + .NewLine(); - public ContentStream ShowTextTj(string line) - { - InnerStream - .WriteStringLiteral(line) - .Space() - .Write("Tj") - .NewLine(); + return this; + } - return this; - } + /// + /// Write the horizontal scaling to the content stream + /// + /// The horizontal scaling value. + /// The to support chaining operations. + public ContentStream SetHorizontalScaling(double horizontalScaling) + { + InnerStream + .Write(horizontalScaling) + .Space() + .Write("Tz") + .NewLine(); - public ContentStream MoveNextLineShowText(string line) - { - InnerStream - .WriteStringLiteral(line) - .Space() - .Write("'") - .NewLine(); + return this; + } - return this; - } + /// + /// Write the text rise to the content stream + /// + /// The text rise value. + /// The to support chaining operations. + public ContentStream SetTextRise(double textRise) + { + InnerStream + .Write(textRise) + .Space() + .Write("Ts") + .NewLine(); - public ContentStream MoveNextLine() - { - InnerStream - .Write("T*") - .NewLine(); + return this; + } - return this; - } + /// + /// Write the text rendering mode to the content stream + /// + /// The text rendering mode value. + /// The to support chaining operations. + public ContentStream SetTextRenderMode(TextRenderingMode textRenderingMode) + { + InnerStream + .Write((int)textRenderingMode) + .Space() + .Write("Tr") + .NewLine(); - public ContentStream MoveNextLineShowText(string line, double wordSpacing, double characterSpacing) - { - InnerStream - .Write(wordSpacing) - .Space() - .Write(characterSpacing) - .Space() - .WriteStringLiteral(line) - .Space() - .Write("\"") - .NewLine(); - - return this; - } - - /// - /// Write the to the content stream with a Td operator - /// - /// The offset to write. - /// The to support chaining operations. - public ContentStream MoveNextLineAndOffsetTextPosition(Point offset) - => MoveNextLineAndOffsetTextPosition(offset.X.AsRaw(Unit.Points), offset.Y.AsRaw(Unit.Points)); - - /// - /// Write the and offsets to the content stream with a Td operator - /// - /// The x offset - /// The y offset - /// The to support chaining operations. - public ContentStream MoveNextLineAndOffsetTextPosition(double x, double y) - { - InnerStream - .Write(x) - .Space() - .Write(y) - .Space() - .Write("Td") - .NewLine(); - - return this; - } - - /// - /// Write the to the content stream with a TD operator - /// - /// The offset to write. - /// The to support chaining operations. - public ContentStream MoveNextLineSetLeadingAndOffsetTextPosition(Point offset) - => MoveNextLineSetLeadingAndOffsetTextPosition(offset.X.AsRaw(Unit.Points), offset.Y.AsRaw(Unit.Points)); - - /// - /// Write the and offsets to the content stream with a TD operator - /// - /// The x offset - /// The y offset - /// The to support chaining operations. - public ContentStream MoveNextLineSetLeadingAndOffsetTextPosition(double x, double y) - { - InnerStream - .Write(x) - .Space() - .Write(y) - .Space() - .Write("TD") - .NewLine(); - - return this; - } - - /// - /// Write a text transformation matrix operator (Tm) to the stream - /// - /// The to write. - /// The to support chaining operations. - public ContentStream Tm(Matrix matrix) - { - InnerStream - .Write(matrix.A) - .Space() - .Write(matrix.B) - .Space() - .Write(matrix.C) - .Space() - .Write(matrix.D) - .Space() - .Write(matrix.E) - .Space() - .Write(matrix.F) - .Space() - .Write("Tm") - .NewLine(); - - return this; - } - - /// - /// Write a xobject paint operator (Do) to the stream - /// - /// The of the xobject to write. - /// The to support chaining operations. - public ContentStream Paint(PdfName resource) - { - InnerStream.Write(resource).Space().Write("Do").NewLine(); - - return this; - } - - /// - /// Write the operator (m) to the stream - /// - /// The x coordinate of the target point. - /// The y coordinate of the target point. - /// The to support chaining operations. - public ContentStream MoveTo(double x, double y) - { - InnerStream - .Write(x).Space().Write(y).Space() - .Write('m').NewLine(); - - return this; - } - - /// - /// Write the operator (l) to the stream - /// - /// The x coordinate of the target point. - /// The y coordinate of the target point. - /// The to support chaining operations. - public ContentStream LineTo(double x, double y) - { - InnerStream - .Write(x).Space().Write(y).Space() - .Write('l').NewLine(); - - return this; - } - - /// - /// Write the operator (c) to the stream - /// - /// The x coordinate of the first control point - /// The y coordinate of the first control point - /// The x coordinate of the second control point - /// The y coordinate of the second control point - /// The x coordinate of the target point. - /// The y coordinate of the target point. - /// The to support chaining operations. - public ContentStream CubicBezierCurve(double x1, double y1, double x2, double y2, double x3, double y3) - { - InnerStream - .Write(x1).Space().Write(y1).Space() - .Write(x2).Space().Write(y2).Space() - .Write(x3).Space().Write(y3).Space() - .Write('c').NewLine(); - - return this; - } - - /// - /// Write the operator (v) to the stream - /// - /// The x coordinate of the second control point - /// The y coordinate of the second control point - /// The x coordinate of the target point. - /// The y coordinate of the target point. - /// The to support chaining operations. - public ContentStream CubicBezierCurveV(double x2, double y2, double x3, double y3) - { - InnerStream - .Write(x2).Space().Write(y2).Space() - .Write(x3).Space().Write(y3).Space() - .Write('v').NewLine(); - - return this; - } - - /// - /// Write the operator (y) to the stream - /// - /// The x coordinate of the first control point - /// The y coordinate of the first control point - /// The x coordinate of the target point and second control point. - /// The y coordinate of the target point and second control point. - /// The to support chaining operations. - public ContentStream CubicBezierCurveY(double x1, double y1, double x3, double y3) - { - InnerStream - .Write(x1).Space().Write(y1).Space() - .Write(x3).Space().Write(y3).Space() - .Write('y').NewLine(); - - return this; - } - - /// - /// Write the operator (re) to the stream - /// - /// The rectangle to draw. - /// The to support chaining operations. - public ContentStream Rectangle(Primitives.Rectangle rectangle) - { - return Rectangle(rectangle.LLX.AsRaw(Unit.Points), rectangle.LLY.AsRaw(Unit.Points), rectangle.Width.AsRaw(Unit.Points), rectangle.Height.AsRaw(Unit.Points)); - } - - /// - /// Write the operator (re) to the stream - /// - /// The x coordinate of the lower left corner of the rectangle - /// The y coordinate of the lower left corner of the rectangle - /// The width of the rectangle - /// The height of the rectangle - /// The to support chaining operations. - public ContentStream Rectangle(double x, double y, double width, double height) - { - InnerStream - .Write(x).Space().Write(y).Space() - .Write(width).Space().Write(height).Space() - .Write("re").NewLine(); - - return this; - } - - /// - /// Mark the current path for use as a clipping mask - /// - /// The to use. - /// The to support chaining operations. - /// Will throw when a unsupported value for is used. - public ContentStream Clip(FillRule fillRule) - { - _ = fillRule switch - { - FillRule.NonZeroWindingNumber => InnerStream.Write('W').NewLine(), - FillRule.EvenOdd => InnerStream.Write('W').Write('*').NewLine(), - _ => throw new InvalidOperationException(UNKNOWN_FILL_RULE) - }; - - return this; - } - - /// - /// Write the operator (h) to the stream - /// - /// The to support chaining operations. - public ContentStream Close() - { - InnerStream - .Write('h').NewLine(); + return this; + } - return this; - } + /// + /// Write the word spacing to the content stream + /// + /// The word space value. + /// The to support chaining operations. + public ContentStream SetWordSpacing(double wordSpace) + { + InnerStream + .Write(wordSpace) + .Space() + .Write("Tw") + .NewLine(); - /// - /// Write the operator (S) to the stream - /// - /// The to support chaining operations. - public ContentStream Stroke() - { - InnerStream - .Write('S').NewLine(); + return this; + } - return this; - } + /// + /// Write the end text operator to the content stream + /// + /// The to support chaining operations. + public ContentStream EndText() + { + InnerStream + .Write("ET") + .NewLine(); - /// - /// Write the operator (s) to the stream - /// - /// The to support chaining operations. - public ContentStream CloseAndStroke() - { - InnerStream - .Write('s').NewLine(); - - return this; - } - - /// - /// Write the operator (f or f*) to the stream - /// - /// The fill rule to use. - /// The to support chaining operations. - public ContentStream Fill(FillRule fillRule) - { - _ = fillRule switch - { - FillRule.NonZeroWindingNumber => InnerStream.Write('f').NewLine(), - FillRule.EvenOdd => InnerStream.Write('f').Write('*').NewLine(), - _ => throw new InvalidOperationException(UNKNOWN_FILL_RULE) - }; - - return this; - } - - /// - /// Write the operator (B or B*) to the stream - /// - /// The fill rule to use. - /// The to support chaining operations. - public ContentStream FillAndStroke(FillRule fillRule) - { - _ = fillRule switch - { - FillRule.NonZeroWindingNumber => InnerStream.Write('B').NewLine(), - FillRule.EvenOdd => InnerStream.Write('B').Write('*').NewLine(), - _ => throw new InvalidOperationException(UNKNOWN_FILL_RULE) - }; - - return this; - } - - /// - /// Write the operator (b or b*) to the stream - /// - /// The fill rule to use. - /// The to support chaining operations. - public ContentStream CloseFillAndStroke(FillRule fillRule) - { - _ = fillRule switch - { - FillRule.NonZeroWindingNumber => InnerStream.Write('b').NewLine(), - FillRule.EvenOdd => InnerStream.Write('b').Write('*').NewLine(), - _ => throw new InvalidOperationException(UNKNOWN_FILL_RULE) - }; - - return this; - } - - /// - /// Write the operator (n) to the stream - /// - /// The to support chaining operations. - public ContentStream EndPath() - { - InnerStream - .Write('n').NewLine(); - - return this; - } - - /// - /// Set the color used for filling operations - /// - /// The color to use - /// The to support chaining operations. - /// Will throw if the implementing color type is not supported. - public ContentStream SetFillColor(Color color) - { - return color switch - { - GrayColor gray => SetFillColor(gray), - RgbColor rgb => SetFillColor(rgb), - CmykColor cmyk => SetFillColor(cmyk), - SpotColor spot => SetFillColor(spot), - _ => throw new NotImplementedException($"The color type {color.GetType().Name} is not implemented.") - }; - } - - /// - /// Set the color used for stroking operations - /// - /// The color to use - /// The to support chaining operations. - /// Will throw if the implementing color type is not supported. - public ContentStream SetStrokeColor(Color color) - { - return color switch - { - GrayColor gray => SetStrokeColor(gray), - RgbColor rgb => SetStrokeColor(rgb), - CmykColor cmyk => SetStrokeColor(cmyk), - SpotColor spot => SetStrokeColor(spot), - _ => throw new NotImplementedException($"The color type {color.GetType().Name} is not implemented.") - }; - } - - /// - /// Write the operator (G) to the stream - /// - /// The color to write - /// The to support chaining operations. - public ContentStream SetStrokeColor(GrayColor color) - { - InnerStream - .Write(color.Gray) - .Space() - .Write('G') - .NewLine(); - - return this; - } - - /// - /// Write the operator (g) to the stream - /// - /// The color to write - /// The to support chaining operations. - public ContentStream SetFillColor(GrayColor color) - { - InnerStream - .Write(color.Gray) - .Space() - .Write('g') - .NewLine(); - - return this; - } - - /// - /// Write the operator (RG) to the stream - /// - /// The color to write - /// The to support chaining operations. - public ContentStream SetStrokeColor(RgbColor color) - { - InnerStream - .Write(color.Red) - .Space() - .Write(color.Green) - .Space() - .Write(color.Blue) - .Space() - .Write('R') - .Write('G') - .NewLine(); - - return this; - } - - /// - /// Write the operator (rg) to the stream - /// - /// The color to write - /// The to support chaining operations. - public ContentStream SetFillColor(RgbColor color) - { - InnerStream - .Write(color.Red) - .Space() - .Write(color.Green) - .Space() - .Write(color.Blue) - .Space() - .Write('r') - .Write('g') - .NewLine(); - - return this; - } - - /// - /// Write the operator (K) to the stream - /// - /// The CMYK color to write - /// The to support chaining operations. - public ContentStream SetStrokeColor(CmykColor color) - { - InnerStream - .Write(color.Cyan) - .Space() - .Write(color.Magenta) - .Space() - .Write(color.Yellow) - .Space() - .Write(color.Key) - .Space() - .Write('K') - .NewLine(); - - return this; - } - - /// - /// Write the operator (k) to the stream - /// - /// The CMYK color to write - /// The to support chaining operations. - public ContentStream SetFillColor(CmykColor color) - { - InnerStream - .Write(color.Cyan) - .Space() - .Write(color.Magenta) - .Space() - .Write(color.Yellow) - .Space() - .Write(color.Key) - .Space() - .Write('k') - .NewLine(); - - return this; - } - - /// - /// Write the operator (CS & SCN) to the stream - /// - /// The spot color to write - /// The to support chaining operations. - public ContentStream SetStrokeColor(SpotColor color) - { - var name = Resources.AddSeparation(color.Separation); - - InnerStream - .Write(name) - .Space() - .Write("CS") - .Space() - .Write(color.Tint) - .Space() - .Write("SCN") - .NewLine(); - - return this; - } - - /// - /// Write the operator (cs & scn) to the stream - /// - /// The spot color to write - /// The to support chaining operations. - public ContentStream SetFillColor(SpotColor color) - { - var name = Resources.AddSeparation(color.Separation); - - InnerStream - .Write(name) - .Space() - .Write("cs") - .Space() - .Write(color.Tint) - .Space() - .Write("scn") - .NewLine(); - - return this; - } - - public ContentStream SetLineWidth(double width) - { - InnerStream - .Write(width) - .Space() - .Write('w') - .NewLine(); + return this; + } - return this; - } + public ContentStream ShowTextTj(string line) + { + InnerStream + .WriteStringLiteral(line) + .Space() + .Write("Tj") + .NewLine(); + + return this; + } + + public ContentStream MoveNextLineShowText(string line) + { + InnerStream + .WriteStringLiteral(line) + .Space() + .Write("'") + .NewLine(); + + return this; + } - public ContentStream SetLineCap(LineCapStyle lineCap) + public ContentStream MoveNextLine() + { + InnerStream + .Write("T*") + .NewLine(); + + return this; + } + + public ContentStream MoveNextLineShowText(string line, double wordSpacing, double characterSpacing) + { + InnerStream + .Write(wordSpacing) + .Space() + .Write(characterSpacing) + .Space() + .WriteStringLiteral(line) + .Space() + .Write("\"") + .NewLine(); + + return this; + } + + /// + /// Write the to the content stream with a Td operator + /// + /// The offset to write. + /// The to support chaining operations. + public ContentStream MoveNextLineAndOffsetTextPosition(Point offset) + => MoveNextLineAndOffsetTextPosition(offset.X.AsRaw(Unit.Points), offset.Y.AsRaw(Unit.Points)); + + /// + /// Write the and offsets to the content stream with a Td operator + /// + /// The x offset + /// The y offset + /// The to support chaining operations. + public ContentStream MoveNextLineAndOffsetTextPosition(double x, double y) + { + InnerStream + .Write(x) + .Space() + .Write(y) + .Space() + .Write("Td") + .NewLine(); + + return this; + } + + /// + /// Write the to the content stream with a TD operator + /// + /// The offset to write. + /// The to support chaining operations. + public ContentStream MoveNextLineSetLeadingAndOffsetTextPosition(Point offset) + => MoveNextLineSetLeadingAndOffsetTextPosition(offset.X.AsRaw(Unit.Points), offset.Y.AsRaw(Unit.Points)); + + /// + /// Write the and offsets to the content stream with a TD operator + /// + /// The x offset + /// The y offset + /// The to support chaining operations. + public ContentStream MoveNextLineSetLeadingAndOffsetTextPosition(double x, double y) + { + InnerStream + .Write(x) + .Space() + .Write(y) + .Space() + .Write("TD") + .NewLine(); + + return this; + } + + /// + /// Write a text transformation matrix operator (Tm) to the stream + /// + /// The to write. + /// The to support chaining operations. + public ContentStream Tm(Matrix matrix) + { + InnerStream + .Write(matrix.A) + .Space() + .Write(matrix.B) + .Space() + .Write(matrix.C) + .Space() + .Write(matrix.D) + .Space() + .Write(matrix.E) + .Space() + .Write(matrix.F) + .Space() + .Write("Tm") + .NewLine(); + + return this; + } + + /// + /// Write a xobject paint operator (Do) to the stream + /// + /// The of the xobject to write. + /// The to support chaining operations. + public ContentStream Paint(PdfName resource) + { + InnerStream.Write(resource).Space().Write("Do").NewLine(); + + return this; + } + + /// + /// Write the operator (m) to the stream + /// + /// The x coordinate of the target point. + /// The y coordinate of the target point. + /// The to support chaining operations. + public ContentStream MoveTo(double x, double y) + { + InnerStream + .Write(x).Space().Write(y).Space() + .Write('m').NewLine(); + + return this; + } + + /// + /// Write the operator (l) to the stream + /// + /// The x coordinate of the target point. + /// The y coordinate of the target point. + /// The to support chaining operations. + public ContentStream LineTo(double x, double y) + { + InnerStream + .Write(x).Space().Write(y).Space() + .Write('l').NewLine(); + + return this; + } + + /// + /// Write the operator (c) to the stream + /// + /// The x coordinate of the first control point + /// The y coordinate of the first control point + /// The x coordinate of the second control point + /// The y coordinate of the second control point + /// The x coordinate of the target point. + /// The y coordinate of the target point. + /// The to support chaining operations. + public ContentStream CubicBezierCurve(double x1, double y1, double x2, double y2, double x3, double y3) + { + InnerStream + .Write(x1).Space().Write(y1).Space() + .Write(x2).Space().Write(y2).Space() + .Write(x3).Space().Write(y3).Space() + .Write('c').NewLine(); + + return this; + } + + /// + /// Write the operator (v) to the stream + /// + /// The x coordinate of the second control point + /// The y coordinate of the second control point + /// The x coordinate of the target point. + /// The y coordinate of the target point. + /// The to support chaining operations. + public ContentStream CubicBezierCurveV(double x2, double y2, double x3, double y3) + { + InnerStream + .Write(x2).Space().Write(y2).Space() + .Write(x3).Space().Write(y3).Space() + .Write('v').NewLine(); + + return this; + } + + /// + /// Write the operator (y) to the stream + /// + /// The x coordinate of the first control point + /// The y coordinate of the first control point + /// The x coordinate of the target point and second control point. + /// The y coordinate of the target point and second control point. + /// The to support chaining operations. + public ContentStream CubicBezierCurveY(double x1, double y1, double x3, double y3) + { + InnerStream + .Write(x1).Space().Write(y1).Space() + .Write(x3).Space().Write(y3).Space() + .Write('y').NewLine(); + + return this; + } + + /// + /// Write the operator (re) to the stream + /// + /// The rectangle to draw. + /// The to support chaining operations. + public ContentStream Rectangle(Primitives.Rectangle rectangle) + { + return Rectangle(rectangle.LLX.AsRaw(Unit.Points), rectangle.LLY.AsRaw(Unit.Points), rectangle.Width.AsRaw(Unit.Points), rectangle.Height.AsRaw(Unit.Points)); + } + + /// + /// Write the operator (re) to the stream + /// + /// The x coordinate of the lower left corner of the rectangle + /// The y coordinate of the lower left corner of the rectangle + /// The width of the rectangle + /// The height of the rectangle + /// The to support chaining operations. + public ContentStream Rectangle(double x, double y, double width, double height) + { + InnerStream + .Write(x).Space().Write(y).Space() + .Write(width).Space().Write(height).Space() + .Write("re").NewLine(); + + return this; + } + + /// + /// Mark the current path for use as a clipping mask + /// + /// The to use. + /// The to support chaining operations. + /// Will throw when a unsupported value for is used. + public ContentStream Clip(FillRule fillRule) + { + _ = fillRule switch { - InnerStream - .Write((int)lineCap) - .Space() - .Write('J') - .NewLine(); + FillRule.NonZeroWindingNumber => InnerStream.Write('W').NewLine(), + FillRule.EvenOdd => InnerStream.Write('W').Write('*').NewLine(), + _ => throw new InvalidOperationException(UNKNOWN_FILL_RULE) + }; + + return this; + } - return this; - } + /// + /// Write the operator (h) to the stream + /// + /// The to support chaining operations. + public ContentStream Close() + { + InnerStream + .Write('h').NewLine(); + + return this; + } - public ContentStream SetLineJoin(LineJoinStyle lineJoin) + /// + /// Write the operator (S) to the stream + /// + /// The to support chaining operations. + public ContentStream Stroke() + { + InnerStream + .Write('S').NewLine(); + + return this; + } + + /// + /// Write the operator (s) to the stream + /// + /// The to support chaining operations. + public ContentStream CloseAndStroke() + { + InnerStream + .Write('s').NewLine(); + + return this; + } + + /// + /// Write the operator (f or f*) to the stream + /// + /// The fill rule to use. + /// The to support chaining operations. + public ContentStream Fill(FillRule fillRule) + { + _ = fillRule switch { - InnerStream - .Write((int)lineJoin) - .Space() - .Write('j') - .NewLine(); + FillRule.NonZeroWindingNumber => InnerStream.Write('f').NewLine(), + FillRule.EvenOdd => InnerStream.Write('f').Write('*').NewLine(), + _ => throw new InvalidOperationException(UNKNOWN_FILL_RULE) + }; - return this; - } + return this; + } - public ContentStream SetMiterLimit(double miterLimit) + /// + /// Write the operator (B or B*) to the stream + /// + /// The fill rule to use. + /// The to support chaining operations. + public ContentStream FillAndStroke(FillRule fillRule) + { + _ = fillRule switch { - InnerStream - .Write(miterLimit) - .Space() - .Write('M') - .NewLine(); + FillRule.NonZeroWindingNumber => InnerStream.Write('B').NewLine(), + FillRule.EvenOdd => InnerStream.Write('B').Write('*').NewLine(), + _ => throw new InvalidOperationException(UNKNOWN_FILL_RULE) + }; - return this; - } + return this; + } - public ContentStream SetDashPattern(Dash dashPattern) + /// + /// Write the operator (b or b*) to the stream + /// + /// The fill rule to use. + /// The to support chaining operations. + public ContentStream CloseFillAndStroke(FillRule fillRule) + { + _ = fillRule switch { - InnerStream - .Write(dashPattern.Array.ToArray()) - .Space() - .Write(dashPattern.Phase) - .Space() - .Write('d') - .NewLine(); - - return this; - } + FillRule.NonZeroWindingNumber => InnerStream.Write('b').NewLine(), + FillRule.EvenOdd => InnerStream.Write('b').Write('*').NewLine(), + _ => throw new InvalidOperationException(UNKNOWN_FILL_RULE) + }; + + return this; + } + + /// + /// Write the operator (n) to the stream + /// + /// The to support chaining operations. + public ContentStream EndPath() + { + InnerStream + .Write('n').NewLine(); + + return this; + } + + /// + /// Set the color used for filling operations + /// + /// The color to use + /// The to support chaining operations. + /// Will throw if the implementing color type is not supported. + public ContentStream SetFillColor(Color color) + { + return color switch + { + GrayColor gray => SetFillColor(gray), + RgbColor rgb => SetFillColor(rgb), + CmykColor cmyk => SetFillColor(cmyk), + SpotColor spot => SetFillColor(spot), + _ => throw new NotImplementedException($"The color type {color.GetType().Name} is not implemented.") + }; + } + + /// + /// Set the color used for stroking operations + /// + /// The color to use + /// The to support chaining operations. + /// Will throw if the implementing color type is not supported. + public ContentStream SetStrokeColor(Color color) + { + return color switch + { + GrayColor gray => SetStrokeColor(gray), + RgbColor rgb => SetStrokeColor(rgb), + CmykColor cmyk => SetStrokeColor(cmyk), + SpotColor spot => SetStrokeColor(spot), + _ => throw new NotImplementedException($"The color type {color.GetType().Name} is not implemented.") + }; + } + + /// + /// Write the operator (G) to the stream + /// + /// The color to write + /// The to support chaining operations. + public ContentStream SetStrokeColor(GrayColor color) + { + InnerStream + .Write(color.Gray) + .Space() + .Write('G') + .NewLine(); + + return this; + } + + /// + /// Write the operator (g) to the stream + /// + /// The color to write + /// The to support chaining operations. + public ContentStream SetFillColor(GrayColor color) + { + InnerStream + .Write(color.Gray) + .Space() + .Write('g') + .NewLine(); + + return this; + } + + /// + /// Write the operator (RG) to the stream + /// + /// The color to write + /// The to support chaining operations. + public ContentStream SetStrokeColor(RgbColor color) + { + InnerStream + .Write(color.Red) + .Space() + .Write(color.Green) + .Space() + .Write(color.Blue) + .Space() + .Write('R') + .Write('G') + .NewLine(); + + return this; + } + + /// + /// Write the operator (rg) to the stream + /// + /// The color to write + /// The to support chaining operations. + public ContentStream SetFillColor(RgbColor color) + { + InnerStream + .Write(color.Red) + .Space() + .Write(color.Green) + .Space() + .Write(color.Blue) + .Space() + .Write('r') + .Write('g') + .NewLine(); + + return this; + } + + /// + /// Write the operator (K) to the stream + /// + /// The CMYK color to write + /// The to support chaining operations. + public ContentStream SetStrokeColor(CmykColor color) + { + InnerStream + .Write(color.Cyan) + .Space() + .Write(color.Magenta) + .Space() + .Write(color.Yellow) + .Space() + .Write(color.Key) + .Space() + .Write('K') + .NewLine(); + + return this; + } + + /// + /// Write the operator (k) to the stream + /// + /// The CMYK color to write + /// The to support chaining operations. + public ContentStream SetFillColor(CmykColor color) + { + InnerStream + .Write(color.Cyan) + .Space() + .Write(color.Magenta) + .Space() + .Write(color.Yellow) + .Space() + .Write(color.Key) + .Space() + .Write('k') + .NewLine(); + + return this; + } + + /// + /// Write the operator (CS & SCN) to the stream + /// + /// The spot color to write + /// The to support chaining operations. + public ContentStream SetStrokeColor(SpotColor color) + { + var name = Resources.AddSeparation(color.Separation); + + InnerStream + .Write(name) + .Space() + .Write("CS") + .Space() + .Write(color.Tint) + .Space() + .Write("SCN") + .NewLine(); + + return this; + } + + /// + /// Write the operator (cs & scn) to the stream + /// + /// The spot color to write + /// The to support chaining operations. + public ContentStream SetFillColor(SpotColor color) + { + var name = Resources.AddSeparation(color.Separation); + + InnerStream + .Write(name) + .Space() + .Write("cs") + .Space() + .Write(color.Tint) + .Space() + .Write("scn") + .NewLine(); + + return this; + } + + public ContentStream SetLineWidth(double width) + { + InnerStream + .Write(width) + .Space() + .Write('w') + .NewLine(); + + return this; + } + + public ContentStream SetLineCap(LineCapStyle lineCap) + { + InnerStream + .Write((int)lineCap) + .Space() + .Write('J') + .NewLine(); + + return this; + } + + public ContentStream SetLineJoin(LineJoinStyle lineJoin) + { + InnerStream + .Write((int)lineJoin) + .Space() + .Write('j') + .NewLine(); + + return this; + } + + public ContentStream SetMiterLimit(double miterLimit) + { + InnerStream + .Write(miterLimit) + .Space() + .Write('M') + .NewLine(); + + return this; + } + + public ContentStream SetDashPattern(Dash dashPattern) + { + InnerStream + .Write(dashPattern.Array.ToArray()) + .Space() + .Write(dashPattern.Phase) + .Space() + .Write('d') + .NewLine(); + + return this; } } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamArrayExtensions.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamArrayExtensions.cs index 796e205..2c40e4b 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamArrayExtensions.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamArrayExtensions.cs @@ -1,132 +1,131 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel.Extensions +namespace Synercoding.FileFormats.Pdf.LowLevel.Extensions; + +/// +/// Extensions methods for +/// +public static class PdfStreamArrayExtensions { + private const byte BRACKET_OPEN = 0x5B; // [ + private const byte BRACKET_CLOSE = 0x5D; // ] + /// - /// Extensions methods for + /// Write an array of chars to the pdf stream /// - public static class PdfStreamArrayExtensions + /// The pdf stream to write the array to. + /// The array of chars to write + /// The to support chaining operations. + public static PdfStream Write(this PdfStream stream, params char[] array) { - private const byte BRACKET_OPEN = 0x5B; // [ - private const byte BRACKET_CLOSE = 0x5D; // ] - - /// - /// Write an array of chars to the pdf stream - /// - /// The pdf stream to write the array to. - /// The array of chars to write - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, params char[] array) - { - stream - .WriteByte(BRACKET_OPEN) - .Space(); + stream + .WriteByte(BRACKET_OPEN) + .Space(); - foreach (var c in array) - stream.Write(c).Space(); + foreach (var c in array) + stream.Write(c).Space(); - stream.WriteByte(BRACKET_CLOSE); + stream.WriteByte(BRACKET_CLOSE); - return stream; - } + return stream; + } - /// - /// Write an array of numbers to the pdf stream - /// - /// The pdf stream to write the array to. - /// The array of doubles to write - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, params double[] array) - { - stream - .WriteByte(BRACKET_OPEN) - .Space(); + /// + /// Write an array of numbers to the pdf stream + /// + /// The pdf stream to write the array to. + /// The array of doubles to write + /// The to support chaining operations. + public static PdfStream Write(this PdfStream stream, params double[] array) + { + stream + .WriteByte(BRACKET_OPEN) + .Space(); - foreach (var number in array) - stream.Write(number).Space(); + foreach (var number in array) + stream.Write(number).Space(); - stream.WriteByte(BRACKET_CLOSE); + stream.WriteByte(BRACKET_CLOSE); - return stream; - } + return stream; + } - /// - /// Write an array of numbers to the pdf stream - /// - /// The pdf stream to write the array to. - /// The array of floats to write - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, params float[] array) - { - stream - .WriteByte(BRACKET_OPEN) - .Space(); + /// + /// Write an array of numbers to the pdf stream + /// + /// The pdf stream to write the array to. + /// The array of floats to write + /// The to support chaining operations. + public static PdfStream Write(this PdfStream stream, params float[] array) + { + stream + .WriteByte(BRACKET_OPEN) + .Space(); - foreach (var number in array) - stream.Write(number).Space(); + foreach (var number in array) + stream.Write(number).Space(); - stream.WriteByte(BRACKET_CLOSE); + stream.WriteByte(BRACKET_CLOSE); - return stream; - } + return stream; + } - /// - /// Write an array of to the pdf stream - /// - /// The pdf stream to write the array to. - /// The array of to write - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, params PdfName[] array) - { - stream - .WriteByte(BRACKET_OPEN) - .Space(); + /// + /// Write an array of to the pdf stream + /// + /// The pdf stream to write the array to. + /// The array of to write + /// The to support chaining operations. + public static PdfStream Write(this PdfStream stream, params PdfName[] array) + { + stream + .WriteByte(BRACKET_OPEN) + .Space(); - foreach (var name in array) - stream.Write(name).Space(); + foreach (var name in array) + stream.Write(name).Space(); - stream.WriteByte(BRACKET_CLOSE); + stream.WriteByte(BRACKET_CLOSE); - return stream; - } + return stream; + } - /// - /// Write an array of numbers to the pdf stream - /// - /// The pdf stream to write the array to. - /// The array of integers to write - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, int[] array) - { - stream - .WriteByte(BRACKET_OPEN) - .Space(); + /// + /// Write an array of numbers to the pdf stream + /// + /// The pdf stream to write the array to. + /// The array of integers to write + /// The to support chaining operations. + public static PdfStream Write(this PdfStream stream, int[] array) + { + stream + .WriteByte(BRACKET_OPEN) + .Space(); - foreach (var number in array) - stream.Write(number).Space(); + foreach (var number in array) + stream.Write(number).Space(); - stream.WriteByte(BRACKET_CLOSE); + stream.WriteByte(BRACKET_CLOSE); - return stream; - } + return stream; + } - /// - /// Write an array of pdf references to the pdf stream - /// - /// The pdf stream to write the array to. - /// The array of to write - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, PdfReference[] objectReferences) - { - stream.WriteByte(BRACKET_OPEN).Space(); + /// + /// Write an array of pdf references to the pdf stream + /// + /// The pdf stream to write the array to. + /// The array of to write + /// The to support chaining operations. + public static PdfStream Write(this PdfStream stream, PdfReference[] objectReferences) + { + stream.WriteByte(BRACKET_OPEN).Space(); - foreach (var objectReference in objectReferences) - { - stream.Write(objectReference); - stream.Space(); - } + foreach (var objectReference in objectReferences) + { + stream.Write(objectReference); + stream.Space(); + } - stream.WriteByte(BRACKET_CLOSE); + stream.WriteByte(BRACKET_CLOSE); - return stream; - } + return stream; } } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamExtensions.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamExtensions.cs index 3411733..8819fd7 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamExtensions.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamExtensions.cs @@ -1,323 +1,320 @@ using Synercoding.Primitives; using System; using System.IO; -using System.Linq; -using System.Reflection; -namespace Synercoding.FileFormats.Pdf.LowLevel.Extensions +namespace Synercoding.FileFormats.Pdf.LowLevel.Extensions; + +/// +/// Extension method for +/// +public static class PdfStreamExtensions { /// - /// Extension method for + /// Write a space ' ' character to the stream. /// - public static class PdfStreamExtensions - { - /// - /// Write a space ' ' character to the stream. - /// - /// The to write to. - /// The to support chaining operations. - public static PdfStream Space(this PdfStream stream) - => stream.Write(' '); - - /// - /// Write a \r\n newline to the stream. - /// - /// The to write to. - /// The to support chaining operations. - public static PdfStream NewLine(this PdfStream stream) - => stream.Write('\r').Write('\n'); - - /// - /// Write a to the stream - /// - /// The to write to. - /// The pdf name to write. - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, PdfName name) - => stream.Write(name.ToString()); - - /// - /// Write a dictionary to the - /// - /// Type of data to use in the - /// The stream to write to - /// The data to use in the - /// Action to fill the dictionary - /// The to support chaining operations. - public static PdfStream Dictionary(this PdfStream stream, T data, Action streamAction) - { - stream - .Write('<') - .Write('<'); + /// The to write to. + /// The to support chaining operations. + public static PdfStream Space(this PdfStream stream) + => stream.Write(' '); - streamAction(data, new PdfDictionary(stream)); + /// + /// Write a \r\n newline to the stream. + /// + /// The to write to. + /// The to support chaining operations. + public static PdfStream NewLine(this PdfStream stream) + => stream.Write('\r').Write('\n'); - stream - .Write('>') - .Write('>'); + /// + /// Write a to the stream + /// + /// The to write to. + /// The pdf name to write. + /// The to support chaining operations. + public static PdfStream Write(this PdfStream stream, PdfName name) + => stream.Write(name.ToString()); - return stream; - } + /// + /// Write a dictionary to the + /// + /// Type of data to use in the + /// The stream to write to + /// The data to use in the + /// Action to fill the dictionary + /// The to support chaining operations. + public static PdfStream Dictionary(this PdfStream stream, T data, Action streamAction) + { + stream + .Write('<') + .Write('<'); - /// - /// Write a dictionary to the - /// - /// The stream to write to - /// Action to fill the dictionary - /// The to support chaining operations. - public static PdfStream Dictionary(this PdfStream stream, Action streamAction) - => stream.Dictionary(streamAction, static (action, dict) => action(dict)); - - /// - /// Write an empty dictionary to the - /// - /// The stream to write to - /// The to support chaining operations. - public static PdfStream EmptyDictionary(this PdfStream stream) - => stream.Dictionary(_ => { }); - - /// - /// Write an object reference to the stream - /// - /// The stream to write to - /// The to write - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, PdfReference objectReference) - { - return stream - .Write(objectReference.ObjectId) - .Space() - .Write(objectReference.Generation) - .Space() - .Write('R'); - } + streamAction(data, new PdfDictionary(stream)); - /// - /// Write a rectangle to the stream as an array of [ LLX LLY URX URY ] - /// - /// The stream to write to - /// The to write - /// The to support chaining operations. - public static PdfStream Write(this PdfStream stream, Rectangle rectangle) - { - rectangle = rectangle.ConvertTo(Unit.Points); + stream + .Write('>') + .Write('>'); - return stream.Write(rectangle.LLX.Raw, rectangle.LLY.Raw, rectangle.URX.Raw, rectangle.URY.Raw); - } + return stream; + } - public static PdfStream Write(this PdfStream stream, DateTimeOffset dateTimeOffset) - { - stream - .Write('(') - .Write('D') - .Write(':'); + /// + /// Write a dictionary to the + /// + /// The stream to write to + /// Action to fill the dictionary + /// The to support chaining operations. + public static PdfStream Dictionary(this PdfStream stream, Action streamAction) + => stream.Dictionary(streamAction, static (action, dict) => action(dict)); + + /// + /// Write an empty dictionary to the + /// + /// The stream to write to + /// The to support chaining operations. + public static PdfStream EmptyDictionary(this PdfStream stream) + => stream.Dictionary(_ => { }); - stream.Write(dateTimeOffset.Year); + /// + /// Write an object reference to the stream + /// + /// The stream to write to + /// The to write + /// The to support chaining operations. + public static PdfStream Write(this PdfStream stream, PdfReference objectReference) + { + return stream + .Write(objectReference.ObjectId) + .Space() + .Write(objectReference.Generation) + .Space() + .Write('R'); + } - if (dateTimeOffset.Month < 10) - stream.Write('0'); - stream.Write(dateTimeOffset.Month); + /// + /// Write a rectangle to the stream as an array of [ LLX LLY URX URY ] + /// + /// The stream to write to + /// The to write + /// The to support chaining operations. + public static PdfStream Write(this PdfStream stream, Rectangle rectangle) + { + rectangle = rectangle.ConvertTo(Unit.Points); - if (dateTimeOffset.Day < 10) - stream.Write('0'); - stream.Write(dateTimeOffset.Day); + return stream.Write(rectangle.LLX.Raw, rectangle.LLY.Raw, rectangle.URX.Raw, rectangle.URY.Raw); + } - if (dateTimeOffset.Hour < 10) - stream.Write('0'); - stream.Write(dateTimeOffset.Hour); + public static PdfStream Write(this PdfStream stream, DateTimeOffset dateTimeOffset) + { + stream + .Write('(') + .Write('D') + .Write(':'); - if (dateTimeOffset.Minute < 10) - stream.Write('0'); - stream.Write(dateTimeOffset.Minute); + stream.Write(dateTimeOffset.Year); - if (dateTimeOffset.Second < 10) - stream.Write('0'); - stream.Write(dateTimeOffset.Second); + if (dateTimeOffset.Month < 10) + stream.Write('0'); + stream.Write(dateTimeOffset.Month); - var hours = dateTimeOffset.Offset.Hours; - var minutes = dateTimeOffset.Offset.Minutes; - if (hours == 0 && minutes == 0) - { - stream - .Write('Z') - .Write('0') - .Write('0') - .Write('\'') - .Write('0') - .Write('0'); - } - else - { - if (hours > 0 || ( hours == 0 && minutes > 0 )) - stream.Write('+'); - else - stream.Write('-'); + if (dateTimeOffset.Day < 10) + stream.Write('0'); + stream.Write(dateTimeOffset.Day); - if (hours < 10) - stream.Write('0'); - stream.Write(hours); + if (dateTimeOffset.Hour < 10) + stream.Write('0'); + stream.Write(dateTimeOffset.Hour); - stream.Write('\''); + if (dateTimeOffset.Minute < 10) + stream.Write('0'); + stream.Write(dateTimeOffset.Minute); - if (minutes < 10) - stream.Write('0'); - stream.Write(minutes); - } + if (dateTimeOffset.Second < 10) + stream.Write('0'); + stream.Write(dateTimeOffset.Second); + var hours = dateTimeOffset.Offset.Hours; + var minutes = dateTimeOffset.Offset.Minutes; + if (hours == 0 && minutes == 0) + { stream - .Write(')'); - - return stream; + .Write('Z') + .Write('0') + .Write('0') + .Write('\'') + .Write('0') + .Write('0'); } - - public static PdfStream WriteHexadecimalString(this PdfStream stream, string value) + else { - var bytes = System.Text.Encoding.ASCII.GetBytes(value); + if (hours > 0 || ( hours == 0 && minutes > 0 )) + stream.Write('+'); + else + stream.Write('-'); - stream.Write('<'); - foreach (var b in bytes) - { - var (c1, c2) = _getByteAsHex(b); + if (hours < 10) + stream.Write('0'); + stream.Write(hours); - stream - .Write(c1) - .Write(c2); - } - stream.Write('>'); + stream.Write('\''); - return stream; + if (minutes < 10) + stream.Write('0'); + stream.Write(minutes); + } - static (char C1, char C2) _getByteAsHex(byte b) - { - var c1 = _getHexCharForNumber(b / 16 % 16); - var c2 = _getHexCharForNumber(b % 16); + stream + .Write(')'); - return (c1, c2); + return stream; + } - static char _getHexCharForNumber(int number) - { - return number switch - { - 0 => '0', - 1 => '1', - 2 => '2', - 3 => '3', - 4 => '4', - 5 => '5', - 6 => '6', - 7 => '7', - 8 => '8', - 9 => '9', - 10 => 'A', - 11 => 'B', - 12 => 'C', - 13 => 'D', - 14 => 'E', - 15 => 'F', - _ => throw new InvalidOperationException() - }; - } - } + public static PdfStream WriteHexadecimalString(this PdfStream stream, string value) + { + var bytes = System.Text.Encoding.ASCII.GetBytes(value); + + stream.Write('<'); + foreach (var b in bytes) + { + var (c1, c2) = _getByteAsHex(b); + + stream + .Write(c1) + .Write(c2); } + stream.Write('>'); + + return stream; - /// - /// Write a text to the stream as a string literal - /// - /// The stream to write to - /// The string to write - /// The to support chaining operations. - /// Throws when the string contains characters outside of the StandardEncoding range. - public static PdfStream WriteStringLiteral(this PdfStream stream, string value) + static (char C1, char C2) _getByteAsHex(byte b) { - stream.WriteByte(0x28); // ( + var c1 = _getHexCharForNumber(b / 16 % 16); + var c2 = _getHexCharForNumber(b % 16); + + return (c1, c2); - foreach (var c in value) + static char _getHexCharForNumber(int number) { - if (c == '\n') - stream.Write('\\').Write('n'); - else if (c == '\r') - stream.Write('\\').Write('r'); - else if (c == '\t') - stream.Write('\\').Write('t'); - else if (c == '\b') - stream.Write('\\').Write('b'); - else if (c == '\f') - stream.Write('\\').Write('f'); - else if (c == '(') - stream.Write('\\').Write('('); - else if (c == ')') - stream.Write('\\').Write(')'); - else if (c == '\\') - stream.Write('\\').Write('\\'); - else if (c > 0x7F && c < 0x200) - stream.Write('\\').Write(_toOctal(c)); - else if (c <= 0x7F) - stream.Write(c); - else - throw new InvalidOperationException("Character is outside of the StandardEncoding range"); + return number switch + { + 0 => '0', + 1 => '1', + 2 => '2', + 3 => '3', + 4 => '4', + 5 => '5', + 6 => '6', + 7 => '7', + 8 => '8', + 9 => '9', + 10 => 'A', + 11 => 'B', + 12 => 'C', + 13 => 'D', + 14 => 'E', + 15 => 'F', + _ => throw new InvalidOperationException() + }; } + } + } - stream.WriteByte(0x29); // ) + /// + /// Write a text to the stream as a string literal + /// + /// The stream to write to + /// The string to write + /// The to support chaining operations. + /// Throws when the string contains characters outside of the StandardEncoding range. + public static PdfStream WriteStringLiteral(this PdfStream stream, string value) + { + stream.WriteByte(0x28); // ( - return stream; + foreach (var c in value) + { + if (c == '\n') + stream.Write('\\').Write('n'); + else if (c == '\r') + stream.Write('\\').Write('r'); + else if (c == '\t') + stream.Write('\\').Write('t'); + else if (c == '\b') + stream.Write('\\').Write('b'); + else if (c == '\f') + stream.Write('\\').Write('f'); + else if (c == '(') + stream.Write('\\').Write('('); + else if (c == ')') + stream.Write('\\').Write(')'); + else if (c == '\\') + stream.Write('\\').Write('\\'); + else if (c > 0x7F && c < 0x200) + stream.Write('\\').Write(_toOctal(c)); + else if (c <= 0x7F) + stream.Write(c); + else + throw new InvalidOperationException("Character is outside of the StandardEncoding range"); + } - static int _toOctal(int number) - { - if (number > 511) - throw new ArgumentOutOfRangeException(nameof(number), "Number is higher than octal 777 (dec 511)."); - if (number < 0) - throw new ArgumentOutOfRangeException(nameof(number), "Only positive numbers can be converted."); + stream.WriteByte(0x29); // ) - int resultNumber = 0; + return stream; - int quotient = number; - int multiplier = 1; + static int _toOctal(int number) + { + if (number > 511) + throw new ArgumentOutOfRangeException(nameof(number), "Number is higher than octal 777 (dec 511)."); + if (number < 0) + throw new ArgumentOutOfRangeException(nameof(number), "Only positive numbers can be converted."); - do - { - int remainder = quotient % 8; - quotient = quotient / 8; + int resultNumber = 0; + + int quotient = number; + int multiplier = 1; - resultNumber += multiplier * remainder; - multiplier *= 10; - } - while (quotient != 0); + do + { + int remainder = quotient % 8; + quotient /= 8; - return resultNumber; + resultNumber += multiplier * remainder; + multiplier *= 10; } - } + while (quotient != 0); - internal static PdfStream StartObject(this PdfStream stream, PdfReference objectReference) - { - return stream - .Write(objectReference.ObjectId) - .Space() - .Write(objectReference.Generation) - .Space() - .Write('o') - .Write('b') - .Write('j') - .NewLine(); + return resultNumber; } + } - internal static PdfStream EndObject(this PdfStream stream) - { - return stream - .NewLine() - .Write('e') - .Write('n') - .Write('d') - .Write('o') - .Write('b') - .Write('j') - .NewLine(); - } + internal static PdfStream StartObject(this PdfStream stream, PdfReference objectReference) + { + return stream + .Write(objectReference.ObjectId) + .Space() + .Write(objectReference.Generation) + .Space() + .Write('o') + .Write('b') + .Write('j') + .NewLine(); + } - internal static PdfStream CopyFrom(this PdfStream stream, Stream data) - { - data.Position = 0; - data.CopyTo(stream.InnerStream); - return stream; - } + internal static PdfStream EndObject(this PdfStream stream) + { + return stream + .NewLine() + .Write('e') + .Write('n') + .Write('d') + .Write('o') + .Write('b') + .Write('j') + .NewLine(); + } + + internal static PdfStream CopyFrom(this PdfStream stream, Stream data) + { + data.Position = 0; + data.CopyTo(stream.InnerStream); + return stream; } } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/StreamFilterExtensions.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/StreamFilterExtensions.cs index b667e88..c1252da 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/StreamFilterExtensions.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/StreamFilterExtensions.cs @@ -1,16 +1,15 @@ using System; -namespace Synercoding.FileFormats.Pdf.LowLevel.Extensions +namespace Synercoding.FileFormats.Pdf.LowLevel.Extensions; + +internal static class StreamFilterExtensions { - internal static class StreamFilterExtensions + public static PdfName ToPdfName(this StreamFilter streamFilter) { - public static PdfName ToPdfName(this StreamFilter streamFilter) + return streamFilter switch { - return streamFilter switch - { - StreamFilter.DCTDecode => PdfName.Get("DCTDecode"), - _ => throw new NotImplementedException(), - }; - } + StreamFilter.DCTDecode => PdfName.Get("DCTDecode"), + _ => throw new NotImplementedException(), + }; } } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Dash.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Dash.cs index fa4dfa6..e3fe6dc 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Dash.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/Dash.cs @@ -29,10 +29,7 @@ public Dash(double[] array, double phase) public IReadOnlyList Array { get => _array; - init - { - _array = value ?? throw new System.ArgumentNullException(nameof(value)); - } + init => _array = value ?? throw new System.ArgumentNullException(nameof(value)); } /// diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/LineCapStyle.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/LineCapStyle.cs index 21a290d..c555955 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/LineCapStyle.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/LineCapStyle.cs @@ -1,21 +1,20 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel.Graphics +namespace Synercoding.FileFormats.Pdf.LowLevel.Graphics; + +/// +/// Enum representing the shape that shall be used at the ends of open subpaths (and dashes, if any) when they are stroked. +/// +public enum LineCapStyle { /// - /// Enum representing the shape that shall be used at the ends of open subpaths (and dashes, if any) when they are stroked. + /// The stroke shall be squared off at the endpoint of the path. /// - public enum LineCapStyle - { - /// - /// The stroke shall be squared off at the endpoint of the path. - /// - ButtCap = 0, - /// - /// A semicircular arc with a diameter equal to the line width shall be drawn around the endpoint and shall be filled in. - /// - RoundCap = 1, - /// - /// The stroke shall continue beyond the endpoint of the path for a distance equal to half the line width and shall be squared off. - /// - ProjectingSquareCap = 2 - } + ButtCap = 0, + /// + /// A semicircular arc with a diameter equal to the line width shall be drawn around the endpoint and shall be filled in. + /// + RoundCap = 1, + /// + /// The stroke shall continue beyond the endpoint of the path for a distance equal to half the line width and shall be squared off. + /// + ProjectingSquareCap = 2 } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/LineJoinStyle.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/LineJoinStyle.cs index 880f54b..104a503 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/LineJoinStyle.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/LineJoinStyle.cs @@ -1,26 +1,25 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel.Graphics +namespace Synercoding.FileFormats.Pdf.LowLevel.Graphics; + +/// +/// Enum representing the line join style. +/// +public enum LineJoinStyle { /// - /// Enum representing the line join style. + /// The outer edges of the strokes for the two segments shall be extended until they meet at an angle. /// - public enum LineJoinStyle - { - /// - /// The outer edges of the strokes for the two segments shall be extended until they meet at an angle. - /// - /// - /// If the segments meet at too sharp an angle (see ), a bevel join shall be used instead. - /// - MiterJoin = 0, - /// - /// An arc of a circle with a diameter equal to the line width shall be drawn around the point where the two segments meet, - /// connecting the outer edges of the strokes for the two segments. - /// - RoundJoin = 1, - /// - /// The two segments shall be finished with and the resulting notch beyond the ends of the segments shall - /// be filled with a triangle. - /// - BevelJoin = 2 - } + /// + /// If the segments meet at too sharp an angle (see ), a bevel join shall be used instead. + /// + MiterJoin = 0, + /// + /// An arc of a circle with a diameter equal to the line width shall be drawn around the point where the two segments meet, + /// connecting the outer edges of the strokes for the two segments. + /// + RoundJoin = 1, + /// + /// The two segments shall be finished with and the resulting notch beyond the ends of the segments shall + /// be filled with a triangle. + /// + BevelJoin = 2 } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/Catalog.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/Catalog.cs index 218087b..a49286d 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/Catalog.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/Catalog.cs @@ -1,18 +1,17 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel.Internal +namespace Synercoding.FileFormats.Pdf.LowLevel.Internal; + +internal class Catalog { - internal class Catalog + public Catalog(PdfReference id, PageTree pageTree) { - public Catalog(PdfReference id, PageTree pageTree) - { - Reference = id; - PageTree = pageTree; - } + Reference = id; + PageTree = pageTree; + } - /// - /// A pdf reference object that can be used to reference to this object - /// - public PdfReference Reference { get; } + /// + /// A pdf reference object that can be used to reference to this object + /// + public PdfReference Reference { get; } - public PageTree PageTree { get; } - } + public PageTree PageTree { get; } } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/DocumentResources.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/DocumentResources.cs deleted file mode 100644 index 70c386c..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/DocumentResources.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Synercoding.FileFormats.Pdf.LowLevel.Internal; -internal class DocumentResources -{ -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/IdGenerator.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/IdGenerator.cs index 23d5055..ed08d71 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/IdGenerator.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/IdGenerator.cs @@ -1,21 +1,20 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel.Internal +namespace Synercoding.FileFormats.Pdf.LowLevel.Internal; + +internal sealed class IdGenerator { - internal sealed class IdGenerator - { - private int _value; + private int _value; - public IdGenerator() - : this(1) - { } + public IdGenerator() + : this(1) + { } - public IdGenerator(int start) - { - _value = start - 1; - } + public IdGenerator(int start) + { + _value = start - 1; + } - public int GetId() - { - return System.Threading.Interlocked.Increment(ref _value); - } + public int GetId() + { + return System.Threading.Interlocked.Increment(ref _value); } } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageResources.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageResources.cs index 135639f..e2884dd 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageResources.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageResources.cs @@ -6,107 +6,106 @@ using System.Collections.Generic; using System.Linq; -namespace Synercoding.FileFormats.Pdf.LowLevel.Internal +namespace Synercoding.FileFormats.Pdf.LowLevel.Internal; + +internal sealed class PageResources : IDisposable { - internal sealed class PageResources : IDisposable - { - private const string PREFIX_IMAGE = "Im"; - private const string PREFIX_SEPARATION = "Sep"; + private const string PREFIX_IMAGE = "Im"; + private const string PREFIX_SEPARATION = "Sep"; - private readonly TableBuilder _tableBuilder; - private readonly Map _images; - private readonly Dictionary _separations; - private readonly Dictionary _standardFonts; + private readonly TableBuilder _tableBuilder; + private readonly Map _images; + private readonly Dictionary _separations; + private readonly Dictionary _standardFonts; - private int _separationCounter = 0; - private int _imageCounter = 0; + private int _separationCounter = 0; + private int _imageCounter = 0; - internal PageResources(TableBuilder tableBuilder) - { - _tableBuilder = tableBuilder; - _images = new Map(); - _separations = new Dictionary(); - _standardFonts = new Dictionary(); - } + internal PageResources(TableBuilder tableBuilder) + { + _tableBuilder = tableBuilder; + _images = new Map(); + _separations = new Dictionary(); + _standardFonts = new Dictionary(); + } - public IReadOnlyDictionary Images - => _images.Forward; + public IReadOnlyDictionary Images + => _images.Forward; - internal IReadOnlyDictionary SeparationReferences - => _separations; + internal IReadOnlyDictionary SeparationReferences + => _separations; - internal IReadOnlyCollection<(Type1StandardFont Font, PdfReference Reference)> FontReferences - => _standardFonts - .Select(kv => (kv.Key, kv.Value)) - .ToArray(); + internal IReadOnlyCollection<(Type1StandardFont Font, PdfReference Reference)> FontReferences + => _standardFonts + .Select(kv => (kv.Key, kv.Value)) + .ToArray(); - public void Dispose() - { - foreach (var kv in _images) - kv.Value.Dispose(); + public void Dispose() + { + foreach (var kv in _images) + kv.Value.Dispose(); - _images.Clear(); - } + _images.Clear(); + } - public PdfName AddJpgUnsafe(System.IO.Stream jpgStream, int originalWidth, int originalHeight, ColorSpace colorSpace) - { - var id = _tableBuilder.ReserveId(); + public PdfName AddJpgUnsafe(System.IO.Stream jpgStream, int originalWidth, int originalHeight, ColorSpace colorSpace) + { + var id = _tableBuilder.ReserveId(); - var pdfImage = new Image(id, jpgStream, originalWidth, originalHeight, colorSpace); + var pdfImage = new Image(id, jpgStream, originalWidth, originalHeight, colorSpace); - return AddImage(pdfImage); - } + return AddImage(pdfImage); + } - public PdfName AddJpgUnsafe(System.IO.Stream jpgStream, int originalWidth, int originalHeight, PdfName colorSpace, double[] decodeArray) - { - var id = _tableBuilder.ReserveId(); + public PdfName AddJpgUnsafe(System.IO.Stream jpgStream, int originalWidth, int originalHeight, PdfName colorSpace, double[] decodeArray) + { + var id = _tableBuilder.ReserveId(); - var pdfImage = new Image(id, jpgStream, originalWidth, originalHeight, colorSpace, decodeArray); + var pdfImage = new Image(id, jpgStream, originalWidth, originalHeight, colorSpace, decodeArray); - return AddImage(pdfImage); - } + return AddImage(pdfImage); + } - public PdfName AddImage(SixLabors.ImageSharp.Image image) - { - var id = _tableBuilder.ReserveId(); + public PdfName AddImage(SixLabors.ImageSharp.Image image) + { + var id = _tableBuilder.ReserveId(); - var pdfImage = new Image(id, image); + var pdfImage = new Image(id, image); - return AddImage(pdfImage); - } + return AddImage(pdfImage); + } - public PdfName AddImage(Image image) - { - if (_images.Reverse.Contains(image)) - return _images.Reverse[image]; + public PdfName AddImage(Image image) + { + if (_images.Reverse.Contains(image)) + return _images.Reverse[image]; - var key = PREFIX_IMAGE + System.Threading.Interlocked.Increment(ref _imageCounter).ToString().PadLeft(6, '0'); + var key = PREFIX_IMAGE + System.Threading.Interlocked.Increment(ref _imageCounter).ToString().PadLeft(6, '0'); - var pdfName = PdfName.Get(key); + var pdfName = PdfName.Get(key); - _images.Add(pdfName, image); + _images.Add(pdfName, image); - return pdfName; - } + return pdfName; + } - internal PdfName AddStandardFont(Type1StandardFont font) - { - if (!_standardFonts.ContainsKey(font)) - _standardFonts[font] = _tableBuilder.ReserveId(); + internal PdfName AddStandardFont(Type1StandardFont font) + { + if (!_standardFonts.ContainsKey(font)) + _standardFonts[font] = _tableBuilder.ReserveId(); - return font.LookupName; - } + return font.LookupName; + } - internal PdfName AddSeparation(Separation separation) - { - if (_separations.TryGetValue(separation, out var tuple)) - return tuple.Name; + internal PdfName AddSeparation(Separation separation) + { + if (_separations.TryGetValue(separation, out var tuple)) + return tuple.Name; - var key = PREFIX_SEPARATION + System.Threading.Interlocked.Increment(ref _separationCounter).ToString().PadLeft(6, '0'); - var name = PdfName.Get(key); - _separations[separation] = (name, _tableBuilder.ReserveId()); + var key = PREFIX_SEPARATION + System.Threading.Interlocked.Increment(ref _separationCounter).ToString().PadLeft(6, '0'); + var name = PdfName.Get(key); + _separations[separation] = (name, _tableBuilder.ReserveId()); - return name; - } + return name; } } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageTree.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageTree.cs index 087ac15..ac14daf 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageTree.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Internal/PageTree.cs @@ -1,28 +1,27 @@ using System.Collections.Generic; -namespace Synercoding.FileFormats.Pdf.LowLevel.Internal +namespace Synercoding.FileFormats.Pdf.LowLevel.Internal; + +internal class PageTree { - internal class PageTree - { - private readonly List _pages = new List(); + private readonly List _pages = new List(); - public PageTree(PdfReference id) - { - Reference = id; - } + public PageTree(PdfReference id) + { + Reference = id; + } - /// - /// A pdf reference object that can be used to reference to this object - /// - public PdfReference Reference { get; } + /// + /// A pdf reference object that can be used to reference to this object + /// + public PdfReference Reference { get; } - public void AddPage(PdfPage pdfObject) - => _pages.Add(pdfObject); + public void AddPage(PdfPage pdfObject) + => _pages.Add(pdfObject); - public int PageCount - => _pages.Count; + public int PageCount + => _pages.Count; - internal IReadOnlyCollection Pages - => _pages.AsReadOnly(); - } + internal IReadOnlyCollection Pages + => _pages.AsReadOnly(); } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/ObjectStream.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/ObjectStream.cs index 0dd7c98..a79d055 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/ObjectStream.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/ObjectStream.cs @@ -76,7 +76,7 @@ public ObjectStream Write(DocumentInformation documentInformation) public ObjectStream Write(PageTree pageTree) { - if(!_tableBuilder.TrySetPosition(pageTree.Reference, InnerStream.Position)) + if (!_tableBuilder.TrySetPosition(pageTree.Reference, InnerStream.Position)) return this; _indirectDictionary(pageTree.Reference, pageTree, static (pageTree, dictionary) => diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/PdfDictionary.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/PdfDictionary.cs index 5c21726..162ea72 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/PdfDictionary.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/PdfDictionary.cs @@ -2,356 +2,355 @@ using Synercoding.Primitives; using System; -namespace Synercoding.FileFormats.Pdf.LowLevel +namespace Synercoding.FileFormats.Pdf.LowLevel; + +/// +/// Class represents a dictionary in a pdf +/// +public class PdfDictionary { + private readonly PdfStream _stream; + + /// + /// Constructor for + /// + /// The stream to + public PdfDictionary(PdfStream stream) + { + _stream = stream; + } + + /// + /// Write an array of numbers to the dictionary + /// + /// The key of the item in the dictionary + /// The array to write + /// The to support chaining operations. + public PdfDictionary Write(PdfName key, params int[] numbers) + { + _stream + .Write(key) + .Space() + .Write(numbers); + + return this; + } + + /// + /// Write an array of numbers to the dictionary + /// + /// The key of the item in the dictionary + /// The array to write + /// The to support chaining operations. + public PdfDictionary Write(PdfName key, params double[] numbers) + { + _stream + .Write(key) + .Space() + .Write(numbers); + + return this; + } + + /// + /// Write an array of numbers to the dictionary + /// + /// The key of the item in the dictionary + /// The array to write + /// The to support chaining operations. + public PdfDictionary Write(PdfName key, params float[] numbers) + { + _stream + .Write(key) + .Space() + .Write(numbers); + + return this; + } + + /// + /// Write an array of to the dictionary + /// + /// The key of the item in the dictionary + /// The array to write + /// The to support chaining operations. + public PdfDictionary Write(PdfName key, params PdfName[] names) + { + _stream + .Write(key) + .Space() + .Write(names); + + return this; + } + + /// + /// Write an array of s to the dictionary + /// + /// The key of the item in the dictionary + /// The array to write + /// The to support chaining operations. + public PdfDictionary Write(PdfName key, params PdfReference[] objectReferences) + { + _stream + .Write(key) + .Space() + .Write(objectReferences); + + return this; + } + + /// + /// Write a number to the dictionary + /// + /// The key of the item in the dictionary + /// The number to write + /// The to support chaining operations. + public PdfDictionary Write(PdfName key, double value) + { + _stream + .Write(key) + .Space() + .Write(value); + + return this; + } + + /// + /// Write a number to the dictionary + /// + /// The key of the item in the dictionary + /// The number to write + /// The to support chaining operations. + public PdfDictionary Write(PdfName key, long value) + { + _stream + .Write(key) + .Space() + .Write(value); + + return this; + } + /// - /// Class represents a dictionary in a pdf + /// Write a number to the dictionary /// - public class PdfDictionary + /// The key of the item in the dictionary + /// The number to write + /// The to support chaining operations. + public PdfDictionary Write(PdfName key, int value) { - private readonly PdfStream _stream; - - /// - /// Constructor for - /// - /// The stream to - public PdfDictionary(PdfStream stream) - { - _stream = stream; - } - - /// - /// Write an array of numbers to the dictionary - /// - /// The key of the item in the dictionary - /// The array to write - /// The to support chaining operations. - public PdfDictionary Write(PdfName key, params int[] numbers) - { - _stream - .Write(key) - .Space() - .Write(numbers); - - return this; - } - - /// - /// Write an array of numbers to the dictionary - /// - /// The key of the item in the dictionary - /// The array to write - /// The to support chaining operations. - public PdfDictionary Write(PdfName key, params double[] numbers) - { - _stream - .Write(key) - .Space() - .Write(numbers); - - return this; - } - - /// - /// Write an array of numbers to the dictionary - /// - /// The key of the item in the dictionary - /// The array to write - /// The to support chaining operations. - public PdfDictionary Write(PdfName key, params float[] numbers) - { - _stream - .Write(key) - .Space() - .Write(numbers); - - return this; - } - - /// - /// Write an array of to the dictionary - /// - /// The key of the item in the dictionary - /// The array to write - /// The to support chaining operations. - public PdfDictionary Write(PdfName key, params PdfName[] names) - { - _stream - .Write(key) - .Space() - .Write(names); - - return this; - } - - /// - /// Write an array of s to the dictionary - /// - /// The key of the item in the dictionary - /// The array to write - /// The to support chaining operations. - public PdfDictionary Write(PdfName key, params PdfReference[] objectReferences) - { - _stream - .Write(key) - .Space() - .Write(objectReferences); - - return this; - } - - /// - /// Write a number to the dictionary - /// - /// The key of the item in the dictionary - /// The number to write - /// The to support chaining operations. - public PdfDictionary Write(PdfName key, double value) - { - _stream - .Write(key) - .Space() - .Write(value); - - return this; - } - - /// - /// Write a number to the dictionary - /// - /// The key of the item in the dictionary - /// The number to write - /// The to support chaining operations. - public PdfDictionary Write(PdfName key, long value) - { - _stream - .Write(key) - .Space() - .Write(value); - - return this; - } - - /// - /// Write a number to the dictionary - /// - /// The key of the item in the dictionary - /// The number to write - /// The to support chaining operations. - public PdfDictionary Write(PdfName key, int value) - { - _stream - .Write(key) - .Space() - .Write(value); - - return this; - } - - /// - /// Write a text to the dictionary - /// - /// The key of the item in the dictionary - /// The text to write - /// The to support chaining operations. - public PdfDictionary Write(PdfName key, string value) - { - _stream - .Write(key) - .Space() - .Write(value); - - return this; - } - - /// - /// Write a text to the dictionary - /// - /// The key of the item in the dictionary - /// The text to write as a literal string - /// The to support chaining operations. - public PdfDictionary WriteLiteralString(PdfName key, string value) - { - _stream - .Write(key) - .Space() - .WriteStringLiteral(value); - - return this; - } - - /// - /// Write a text to the dictionary - /// - /// The key of the item in the dictionary - /// The text to write as a hexadecimal string - /// The to support chaining operations. - public PdfDictionary WriteHexadecimalString(PdfName key, string value) - { - _stream - .Write(key) - .Space() - .WriteHexadecimalString(value); - - return this; - } - - public PdfDictionary Write(PdfName key, DateTimeOffset value) - { - _stream - .Write(key) - .Space() - .Write(value); - - return this; - } - - /// - /// Write a to the dictionary - /// - /// The key of the item in the dictionary - /// The to write - /// The to support chaining operations. - public PdfDictionary Write(PdfName key, PdfName value) - { - _stream - .Write(key) - .Space() - .Write(value); - - return this; - } - - - /// - /// Write a to the dictionary - /// - /// The key of the item in the dictionary - /// The to write - /// The to support chaining operations. - public PdfDictionary Write(PdfName key, PdfReference objectReference) - { - _stream - .Write(key) - .Space() - .Write(objectReference); - - return this; - } - - /// - /// Use an action to write things to the stream - /// - /// The type of data to pass to - /// The key of the item in the dictionary - /// Data to use in the - /// The action to use to write - /// The to support chaining operations. - public PdfDictionary Write(PdfName key, T data, Action rawActions) - { - _stream - .Write(key) - .Space(); - - rawActions(data, _stream); - - return this; - } - - /// - /// Use an action to write things to the stream - /// - /// The key of the item in the dictionary - /// The action to use to write - /// The to support chaining operations. - public PdfDictionary Write(PdfName key, Action rawActions) - => Write(key, rawActions, static (action, stream) => action(stream)); - - /// - /// Write a to the stream - /// - /// The key of the item in the dictionary - /// The to write. - /// The to support chaining operations. - public PdfDictionary Write(PdfName key, Rectangle rectangle) - { - _stream - .Write(key) - .Space() - .Write(rectangle); - - return this; - } - - /// - /// Write a rectangle to the stream if it is not null - /// - /// The key of the item in the dictionary - /// The to write. - /// The to support chaining operations. - public PdfDictionary WriteIfNotNull(PdfName key, Rectangle? rectangle) - => rectangle.HasValue - ? Write(key, rectangle.Value) - : this; - - /// - /// Write a number to the stream if it is not null - /// - /// The key of the item in the dictionary - /// The number to write. - /// The to support chaining operations. - public PdfDictionary WriteIfNotNull(PdfName key, int? value) - => value.HasValue - ? Write(key, value.Value) - : this; - - /// - /// Write a number to the stream if it is not null - /// - /// The key of the item in the dictionary - /// The number to write. - /// The to support chaining operations. - public PdfDictionary WriteIfNotNull(PdfName key, double? value) - => value.HasValue - ? Write(key, value.Value) - : this; - - public PdfDictionary WriteIfNotNull(PdfName key, DateTimeOffset? value) - => value.HasValue - ? Write(key, value.Value) - : this; - - /// - /// Write a number to the stream if it is not null - /// - /// The key of the item in the dictionary - /// The string to write. - /// The to support chaining operations. - public PdfDictionary WriteIfNotNullOrWhiteSpace(PdfName key, string? value) - => !string.IsNullOrWhiteSpace(value) - ? Write(key, value) - : this; - - /// - /// Write a number to the stream if it is not null - /// - /// The key of the item in the dictionary - /// The string to write as a pdf literal string. - /// The to support chaining operations. - public PdfDictionary WriteLiteralIfNotNullOrWhiteSpace(PdfName key, string? value) - => !string.IsNullOrWhiteSpace(value) - ? WriteLiteralString(key, value) - : this; - - /// - /// Write a number to the stream if it is not null - /// - /// The key of the item in the dictionary - /// The string to write as a pdf hexadecimal string. - /// The to support chaining operations. - public PdfDictionary WriteHexadecimalIfNotNullOrWhiteSpace(PdfName key, string? value) - => !string.IsNullOrWhiteSpace(value) - ? WriteHexadecimalString(key, value) - : this; + _stream + .Write(key) + .Space() + .Write(value); + + return this; } + + /// + /// Write a text to the dictionary + /// + /// The key of the item in the dictionary + /// The text to write + /// The to support chaining operations. + public PdfDictionary Write(PdfName key, string value) + { + _stream + .Write(key) + .Space() + .Write(value); + + return this; + } + + /// + /// Write a text to the dictionary + /// + /// The key of the item in the dictionary + /// The text to write as a literal string + /// The to support chaining operations. + public PdfDictionary WriteLiteralString(PdfName key, string value) + { + _stream + .Write(key) + .Space() + .WriteStringLiteral(value); + + return this; + } + + /// + /// Write a text to the dictionary + /// + /// The key of the item in the dictionary + /// The text to write as a hexadecimal string + /// The to support chaining operations. + public PdfDictionary WriteHexadecimalString(PdfName key, string value) + { + _stream + .Write(key) + .Space() + .WriteHexadecimalString(value); + + return this; + } + + public PdfDictionary Write(PdfName key, DateTimeOffset value) + { + _stream + .Write(key) + .Space() + .Write(value); + + return this; + } + + /// + /// Write a to the dictionary + /// + /// The key of the item in the dictionary + /// The to write + /// The to support chaining operations. + public PdfDictionary Write(PdfName key, PdfName value) + { + _stream + .Write(key) + .Space() + .Write(value); + + return this; + } + + + /// + /// Write a to the dictionary + /// + /// The key of the item in the dictionary + /// The to write + /// The to support chaining operations. + public PdfDictionary Write(PdfName key, PdfReference objectReference) + { + _stream + .Write(key) + .Space() + .Write(objectReference); + + return this; + } + + /// + /// Use an action to write things to the stream + /// + /// The type of data to pass to + /// The key of the item in the dictionary + /// Data to use in the + /// The action to use to write + /// The to support chaining operations. + public PdfDictionary Write(PdfName key, T data, Action rawActions) + { + _stream + .Write(key) + .Space(); + + rawActions(data, _stream); + + return this; + } + + /// + /// Use an action to write things to the stream + /// + /// The key of the item in the dictionary + /// The action to use to write + /// The to support chaining operations. + public PdfDictionary Write(PdfName key, Action rawActions) + => Write(key, rawActions, static (action, stream) => action(stream)); + + /// + /// Write a to the stream + /// + /// The key of the item in the dictionary + /// The to write. + /// The to support chaining operations. + public PdfDictionary Write(PdfName key, Rectangle rectangle) + { + _stream + .Write(key) + .Space() + .Write(rectangle); + + return this; + } + + /// + /// Write a rectangle to the stream if it is not null + /// + /// The key of the item in the dictionary + /// The to write. + /// The to support chaining operations. + public PdfDictionary WriteIfNotNull(PdfName key, Rectangle? rectangle) + => rectangle.HasValue + ? Write(key, rectangle.Value) + : this; + + /// + /// Write a number to the stream if it is not null + /// + /// The key of the item in the dictionary + /// The number to write. + /// The to support chaining operations. + public PdfDictionary WriteIfNotNull(PdfName key, int? value) + => value.HasValue + ? Write(key, value.Value) + : this; + + /// + /// Write a number to the stream if it is not null + /// + /// The key of the item in the dictionary + /// The number to write. + /// The to support chaining operations. + public PdfDictionary WriteIfNotNull(PdfName key, double? value) + => value.HasValue + ? Write(key, value.Value) + : this; + + public PdfDictionary WriteIfNotNull(PdfName key, DateTimeOffset? value) + => value.HasValue + ? Write(key, value.Value) + : this; + + /// + /// Write a number to the stream if it is not null + /// + /// The key of the item in the dictionary + /// The string to write. + /// The to support chaining operations. + public PdfDictionary WriteIfNotNullOrWhiteSpace(PdfName key, string? value) + => !string.IsNullOrWhiteSpace(value) + ? Write(key, value) + : this; + + /// + /// Write a number to the stream if it is not null + /// + /// The key of the item in the dictionary + /// The string to write as a pdf literal string. + /// The to support chaining operations. + public PdfDictionary WriteLiteralIfNotNullOrWhiteSpace(PdfName key, string? value) + => !string.IsNullOrWhiteSpace(value) + ? WriteLiteralString(key, value) + : this; + + /// + /// Write a number to the stream if it is not null + /// + /// The key of the item in the dictionary + /// The string to write as a pdf hexadecimal string. + /// The to support chaining operations. + public PdfDictionary WriteHexadecimalIfNotNullOrWhiteSpace(PdfName key, string? value) + => !string.IsNullOrWhiteSpace(value) + ? WriteHexadecimalString(key, value) + : this; } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/PdfName.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/PdfName.cs index 35500b7..a955b8c 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/PdfName.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/PdfName.cs @@ -3,125 +3,124 @@ using System.Linq; using System.Text; -namespace Synercoding.FileFormats.Pdf.LowLevel +namespace Synercoding.FileFormats.Pdf.LowLevel; + +/// +/// A class representing a name object in a pdf. +/// +public class PdfName { - /// - /// A class representing a name object in a pdf. - /// - public class PdfName + private static readonly IDictionary _reservedNames = new Dictionary() { - private static readonly IDictionary _reservedNames = new Dictionary() - { - { "Author", new PdfName("Author") }, - { "BitsPerComponent", new PdfName("BitsPerComponent") }, - { "BleedBox", new PdfName("BleedBox") }, - { "Catalog", new PdfName("Catalog") }, - { "ColorSpace", new PdfName("ColorSpace") }, - { "Contents", new PdfName("Contents") }, - { "Count", new PdfName("Count") }, - { "CreationDate", new PdfName("CreationDate") }, - { "CropBox", new PdfName("CropBox") }, - { "DCTDecode", new PdfName("DCTDecode") }, - { "Decode", new PdfName("Decode") }, - { "DeviceCMYK", new PdfName("DeviceCMYK") }, - { "DeviceGray", new PdfName("DeviceGray") }, - { "DeviceRGB", new PdfName("DeviceRGB") }, - { "Filter", new PdfName("Filter") }, - { "Height", new PdfName("Height") }, - { "Image", new PdfName("Image") }, - { "Info", new PdfName("Info") }, - { "Kids", new PdfName("Kids") }, - { "Length", new PdfName("Length") }, - { "MediaBox", new PdfName("MediaBox") }, - { "Page", new PdfName("Page") }, - { "Pages", new PdfName("Pages") }, - { "Parent", new PdfName("Parent") }, - { "Producer", new PdfName("Producer") }, - { "Resources", new PdfName("Resources") }, - { "Root", new PdfName("Root") }, - { "Rotate", new PdfName("Rotate") }, - { "Size", new PdfName("Size") }, - { "Subtype", new PdfName("Subtype") }, - { "Title", new PdfName("Title") }, - { "TrimBox", new PdfName("TrimBox") }, - { "Type", new PdfName("Type") }, - { "Width", new PdfName("Width") }, - { "XObject", new PdfName("XObject") }, - }; + { "Author", new PdfName("Author") }, + { "BitsPerComponent", new PdfName("BitsPerComponent") }, + { "BleedBox", new PdfName("BleedBox") }, + { "Catalog", new PdfName("Catalog") }, + { "ColorSpace", new PdfName("ColorSpace") }, + { "Contents", new PdfName("Contents") }, + { "Count", new PdfName("Count") }, + { "CreationDate", new PdfName("CreationDate") }, + { "CropBox", new PdfName("CropBox") }, + { "DCTDecode", new PdfName("DCTDecode") }, + { "Decode", new PdfName("Decode") }, + { "DeviceCMYK", new PdfName("DeviceCMYK") }, + { "DeviceGray", new PdfName("DeviceGray") }, + { "DeviceRGB", new PdfName("DeviceRGB") }, + { "Filter", new PdfName("Filter") }, + { "Height", new PdfName("Height") }, + { "Image", new PdfName("Image") }, + { "Info", new PdfName("Info") }, + { "Kids", new PdfName("Kids") }, + { "Length", new PdfName("Length") }, + { "MediaBox", new PdfName("MediaBox") }, + { "Page", new PdfName("Page") }, + { "Pages", new PdfName("Pages") }, + { "Parent", new PdfName("Parent") }, + { "Producer", new PdfName("Producer") }, + { "Resources", new PdfName("Resources") }, + { "Root", new PdfName("Root") }, + { "Rotate", new PdfName("Rotate") }, + { "Size", new PdfName("Size") }, + { "Subtype", new PdfName("Subtype") }, + { "Title", new PdfName("Title") }, + { "TrimBox", new PdfName("TrimBox") }, + { "Type", new PdfName("Type") }, + { "Width", new PdfName("Width") }, + { "XObject", new PdfName("XObject") }, + }; - private readonly string _raw; - private readonly string _encoded; + private readonly string _raw; + private readonly string _encoded; - private PdfName(string raw) - { - if (raw.Length > 127) - throw new ArgumentOutOfRangeException(nameof(raw), "The name is too long. Max length of 127 is allowed."); - if (raw.Any(c => c > 0xff)) - throw new ArgumentOutOfRangeException(nameof(raw), "The name contains non-ascii characters, and is thus not allowed."); - _raw = raw; - _encoded = _encode(raw); - } + private PdfName(string raw) + { + if (raw.Length > 127) + throw new ArgumentOutOfRangeException(nameof(raw), "The name is too long. Max length of 127 is allowed."); + if (raw.Any(c => c > 0xff)) + throw new ArgumentOutOfRangeException(nameof(raw), "The name contains non-ascii characters, and is thus not allowed."); + _raw = raw; + _encoded = _encode(raw); + } - /// - public override int GetHashCode() - => HashCode.Combine(_encoded, "PdfName"); + /// + public override int GetHashCode() + => HashCode.Combine(_encoded, "PdfName"); - /// - public override string ToString() - => _encoded; + /// + public override string ToString() + => _encoded; - /// - /// Get a from a given - /// - /// The name in raw string form - /// A that is based on the given . - public static PdfName Get(string name) - { - if (_reservedNames.TryGetValue(name, out var value)) - return value; + /// + /// Get a from a given + /// + /// The name in raw string form + /// A that is based on the given . + public static PdfName Get(string name) + { + if (_reservedNames.TryGetValue(name, out var value)) + return value; - System.Diagnostics.Debug.WriteLine($"{{ \"{name}\", new PdfName(\"{name}\") }},"); + System.Diagnostics.Debug.WriteLine($"{{ \"{name}\", new PdfName(\"{name}\") }},"); - return new PdfName(name); - } + return new PdfName(name); + } - private static string _encode(string input) - { - int length = input.Length; + private static string _encode(string input) + { + int length = input.Length; - var pdfName = new StringBuilder(length + 20); - pdfName.Append('/'); - var chars = input.ToCharArray(); + var pdfName = new StringBuilder(length + 20); + pdfName.Append('/'); + var chars = input.ToCharArray(); - foreach (var c in chars) + foreach (var c in chars) + { + // 0xff because only ascii is supported in pdf names + var encoded = c switch { - // 0xff because only ascii is supported in pdf names - var encoded = c switch - { - // Pdf reserved characters - ' ' => "#20", - '#' => "#23", - '%' => "#25", - '(' => "#28", - ')' => "#29", - '/' => "#2f", - '<' => "#3c", - '>' => "#3e", - '[' => "#5b", - ']' => "#5d", - '{' => "#7b", - '}' => "#7d", - // special characters - var cc when cc < 16 => $"#0{Convert.ToString(cc, 16)}", - var cc when cc < 32 => '#' + Convert.ToString(cc, 16), - var cc when cc > 126 => '#' + Convert.ToString(cc, 16), - // "readable characters" - var cc => cc.ToString() - }; - pdfName.Append(encoded); - } - - return pdfName.ToString(); + // Pdf reserved characters + ' ' => "#20", + '#' => "#23", + '%' => "#25", + '(' => "#28", + ')' => "#29", + '/' => "#2f", + '<' => "#3c", + '>' => "#3e", + '[' => "#5b", + ']' => "#5d", + '{' => "#7b", + '}' => "#7d", + // special characters + var cc when cc < 16 => $"#0{Convert.ToString(cc, 16)}", + var cc when cc < 32 => '#' + Convert.ToString(cc, 16), + var cc when cc > 126 => '#' + Convert.ToString(cc, 16), + // "readable characters" + var cc => cc.ToString() + }; + pdfName.Append(encoded); } + + return pdfName.ToString(); } } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/PdfReference.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/PdfReference.cs index 2b17433..f57ea69 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/PdfReference.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/PdfReference.cs @@ -1,43 +1,42 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel +namespace Synercoding.FileFormats.Pdf.LowLevel; + +/// +/// A struct representing a reference +/// +public readonly struct PdfReference { /// - /// A struct representing a reference + /// Constructor for that uses generation 0 /// - public readonly struct PdfReference - { - /// - /// Constructor for that uses generation 0 - /// - /// The id of the reference - internal PdfReference(int objectId) - : this(objectId, 0) - { } + /// The id of the reference + internal PdfReference(int objectId) + : this(objectId, 0) + { } - /// - /// Constructor for - /// - /// The id of the reference - /// The generation of the reference - internal PdfReference(int objectId, int generation) - { - ObjectId = objectId; - Generation = generation; - } + /// + /// Constructor for + /// + /// The id of the reference + /// The generation of the reference + internal PdfReference(int objectId, int generation) + { + ObjectId = objectId; + Generation = generation; + } - /// - /// The object id of this reference - /// - public int ObjectId { get; } + /// + /// The object id of this reference + /// + public int ObjectId { get; } - /// - /// The generation of this reference - /// - public int Generation { get; } + /// + /// The generation of this reference + /// + public int Generation { get; } - /// - public override string ToString() - { - return $"{ObjectId} {Generation}"; - } + /// + public override string ToString() + { + return $"{ObjectId} {Generation}"; } } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/PdfStream.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/PdfStream.cs index 0a220b5..d974a92 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/PdfStream.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/PdfStream.cs @@ -4,172 +4,171 @@ using System.IO; using System.Text; -namespace Synercoding.FileFormats.Pdf.LowLevel +namespace Synercoding.FileFormats.Pdf.LowLevel; + +/// +/// Class that represents a wrapper around a stream to make writing PDF instructions easier. +/// +public class PdfStream : IDisposable { + private const string NUMBER_STRING_FORMAT = "0.0########"; + /// - /// Class that represents a wrapper around a stream to make writing PDF instructions easier. + /// Constructor for /// - public class PdfStream : IDisposable + /// The stream where everything is written to. + public PdfStream(Stream stream) { - private const string NUMBER_STRING_FORMAT = "0.0########"; + InnerStream = stream; + } - /// - /// Constructor for - /// - /// The stream where everything is written to. - public PdfStream(Stream stream) - { - InnerStream = stream; - } + /// + /// Get the position in the stream + /// + public uint Position + => (uint)InnerStream.Position; - /// - /// Get the position in the stream - /// - public uint Position - => (uint)InnerStream.Position; - - /// - /// The stream that is wrapped an being written to - /// - protected internal Stream InnerStream { get; } - - /// - /// Write a to the stream. - /// - /// The byte to write. - /// The calling to support chaining operations. - public PdfStream WriteByte(byte b) - { - InnerStream.WriteByte(b); + /// + /// The stream that is wrapped an being written to + /// + protected internal Stream InnerStream { get; } - return this; - } + /// + /// Write a to the stream. + /// + /// The byte to write. + /// The calling to support chaining operations. + public PdfStream WriteByte(byte b) + { + InnerStream.WriteByte(b); - /// - /// Write a to the stream - /// - /// The char to write. - /// The calling to support chaining operations. - public PdfStream Write(char c) - { - if (c > 0xFF) - throw new InvalidOperationException($"Char {c} cannot be encoded directly because it is outside of the default ASCII range."); + return this; + } - return WriteByte((byte)(c & 0xFF)); - } + /// + /// Write a to the stream + /// + /// The char to write. + /// The calling to support chaining operations. + public PdfStream Write(char c) + { + if (c > 0xFF) + throw new InvalidOperationException($"Char {c} cannot be encoded directly because it is outside of the default ASCII range."); - /// - /// Write a to the stream - /// - /// The integer to write - /// The calling to support chaining operations. - public PdfStream Write(int value) + return WriteByte((byte)( c & 0xFF )); + } + + /// + /// Write a to the stream + /// + /// The integer to write + /// The calling to support chaining operations. + public PdfStream Write(int value) + { + var intSize = ByteSizes.Size(value); + for (int i = intSize - 1; i >= 0; i--) { - var intSize = ByteSizes.Size(value); - for (int i = intSize - 1; i >= 0; i--) - { - var result = (byte)('0' + ((int)(value / Math.Pow(10, i)) % 10)); - InnerStream.WriteByte(result); - } - - return this; + var result = (byte)( '0' + ( (int)( value / Math.Pow(10, i) ) % 10 ) ); + InnerStream.WriteByte(result); } - /// - /// Write a to the stream - /// - /// The long to write - /// The calling to support chaining operations. - public PdfStream Write(long value) + return this; + } + + /// + /// Write a to the stream + /// + /// The long to write + /// The calling to support chaining operations. + public PdfStream Write(long value) + { + var intSize = ByteSizes.Size(value); + for (int i = intSize - 1; i >= 0; i--) { - var intSize = ByteSizes.Size(value); - for (int i = intSize - 1; i >= 0; i--) - { - var result = (byte)('0' + ((int)(value / Math.Pow(10, i)) % 10)); - InnerStream.WriteByte(result); - } - - return this; + var result = (byte)( '0' + ( (int)( value / Math.Pow(10, i) ) % 10 ) ); + InnerStream.WriteByte(result); } - /// - /// Write a to the stream - /// - /// The double to write - /// The calling to support chaining operations. - public PdfStream Write(double value) - { - var stringValue = value.ToString(NUMBER_STRING_FORMAT, CultureInfo.InvariantCulture); - var bytes = Encoding.ASCII.GetBytes(stringValue); - InnerStream.Write(bytes, 0, bytes.Length); + return this; + } - return this; - } + /// + /// Write a to the stream + /// + /// The double to write + /// The calling to support chaining operations. + public PdfStream Write(double value) + { + var stringValue = value.ToString(NUMBER_STRING_FORMAT, CultureInfo.InvariantCulture); + var bytes = Encoding.ASCII.GetBytes(stringValue); + InnerStream.Write(bytes, 0, bytes.Length); - /// - /// Write a to the stream - /// - /// The string to write - /// The calling to support chaining operations. - public PdfStream Write(string text) - { - var bytes = Encoding.ASCII.GetBytes(text); - InnerStream.Write(bytes, 0, bytes.Length); + return this; + } - return this; - } + /// + /// Write a to the stream + /// + /// The string to write + /// The calling to support chaining operations. + public PdfStream Write(string text) + { + var bytes = Encoding.ASCII.GetBytes(text); + InnerStream.Write(bytes, 0, bytes.Length); - /// - /// Copy bytes to the stream from a buffer - /// - /// The byte buffer to write - /// The offset in the array to start copying - /// The amount of bytes to copy - /// The calling to support chaining operations. - public PdfStream Write(byte[] buffer, int offset, int count) - { - InnerStream.Write(buffer, offset, count); + return this; + } - return this; - } + /// + /// Copy bytes to the stream from a buffer + /// + /// The byte buffer to write + /// The offset in the array to start copying + /// The amount of bytes to copy + /// The calling to support chaining operations. + public PdfStream Write(byte[] buffer, int offset, int count) + { + InnerStream.Write(buffer, offset, count); - /// - /// Write a of type to the stream - /// - /// The buffer to write - /// The calling to support chaining operations. - public PdfStream Write(ReadOnlySpan buffer) - { - InnerStream.Write(buffer); + return this; + } - return this; - } + /// + /// Write a of type to the stream + /// + /// The buffer to write + /// The calling to support chaining operations. + public PdfStream Write(ReadOnlySpan buffer) + { + InnerStream.Write(buffer); - /// - /// Flush the inner stream - /// - public void Flush() - { - InnerStream.Flush(); - } + return this; + } - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } + /// + /// Flush the inner stream + /// + public void Flush() + { + InnerStream.Flush(); + } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - /// Parameter to indicate wheter this method was called from dispose or finalizer - protected virtual void Dispose(bool disposing) + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + /// Parameter to indicate wheter this method was called from dispose or finalizer + protected virtual void Dispose(bool disposing) + { + if (disposing) { - if (disposing) - { - InnerStream.Dispose(); - } + InnerStream.Dispose(); } } } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/StreamFilter.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/StreamFilter.cs index cf9bd92..f546b1b 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/StreamFilter.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/StreamFilter.cs @@ -1,14 +1,13 @@ -namespace Synercoding.FileFormats.Pdf.LowLevel +namespace Synercoding.FileFormats.Pdf.LowLevel; + +/// +/// Enum representing different data encoding methods +/// +internal enum StreamFilter { /// - /// Enum representing different data encoding methods + /// Decompress data encoded using a DCT (discrete cosine transform) technique based on the JPEG standard, + /// reproducing image sample data that approximates the original data. /// - internal enum StreamFilter - { - /// - /// Decompress data encoded using a DCT (discrete cosine transform) technique based on the JPEG standard, - /// reproducing image sample data that approximates the original data. - /// - DCTDecode - } + DCTDecode } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Text/TextState.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Text/TextState.cs deleted file mode 100644 index 3b491b1..0000000 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Text/TextState.cs +++ /dev/null @@ -1,103 +0,0 @@ -using Synercoding.FileFormats.Pdf.LowLevel.Colors; -using Synercoding.FileFormats.Pdf.LowLevel.Graphics; - -namespace Synercoding.FileFormats.Pdf.LowLevel.Text -{ - /// - /// Class representing the text state of a content stream - /// - public sealed class TextState - { - internal TextState() - : this(StandardFonts.Helvetica, 12) - { } - - /// - /// Constructor for - /// - /// The font to use - /// The font size - public TextState(Type1StandardFont font, float fontSize) - { - Font = font; - FontSize = fontSize; - } - - /// - /// The of the fill of the text - /// - /// If null no filling will occur. - public Color? Fill { get; set; } = null; - - /// - /// The of the stroke of the text - /// - /// If null no stroking will occur. - public Color? Stroke { get; set; } = null; - - /// - /// The width of the line - /// - public double? LineWidth { get; set; } = 1; - - /// - /// The dash settings of the path - /// - public Dash? Dash { get; set; } = new Dash(); - - /// - /// The miter limit - /// - public double? MiterLimit { get; set; } = 10; - - /// - /// The of the path - /// - public LineCapStyle? LineCap { get; set; } - - /// - /// The of the path - /// - public LineJoinStyle? LineJoin { get; set; } - - /// - /// The type1 standard font - /// - public Type1StandardFont Font { get; set; } - - /// - /// The font size - /// - public float FontSize { get; set; } - - /// - /// Set the text rise - /// - public float? TextRise { get; set; } - - /// - /// The text rendering mode - /// - public TextRenderingMode? RenderingMode { get; set; } - - /// - /// Set the leading for the text - /// - public float? Leading { get; set; } - - /// - /// The horizontal scaling of the text - /// - public float? HorizontalScaling { get; set; } - - /// - /// The spacing between the words - /// - public float? WordSpacing { get; set; } - - /// - /// The spacing between the character glyps - /// - public float? CharacterSpacing { get; set; } - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/Entry.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/Entry.cs index 49a8eb3..70e6bbc 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/Entry.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/Entry.cs @@ -1,61 +1,60 @@ using System; -namespace Synercoding.FileFormats.Pdf.LowLevel.XRef +namespace Synercoding.FileFormats.Pdf.LowLevel.XRef; + +internal readonly struct Entry { - internal struct Entry - { - public Entry(uint data) - : this(data, 0, false) - { } + public Entry(uint data) + : this(data, 0, false) + { } - public Entry(uint data, ushort generation, bool isFree) - { - Data = data; - GenerationNumber = generation; - IsFree = isFree; - } + public Entry(uint data, ushort generation, bool isFree) + { + Data = data; + GenerationNumber = generation; + IsFree = isFree; + } - public uint Data { get; } - public ushort GenerationNumber { get; } + public uint Data { get; } + public ushort GenerationNumber { get; } - public bool IsFree { get; } + public bool IsFree { get; } - public void FillSpan(Span bytes) - { - _fillSpanLeadingZero(bytes.Slice(0, 10), Data); - bytes[10] = 0x20; + public void FillSpan(Span bytes) + { + _fillSpanLeadingZero(bytes[..10], Data); + bytes[10] = 0x20; - _fillSpanLeadingZero(bytes.Slice(11, 5), GenerationNumber); - bytes[16] = 0x20; + _fillSpanLeadingZero(bytes.Slice(11, 5), GenerationNumber); + bytes[16] = 0x20; - bytes[17] = (byte)( IsFree ? 0x66 : 0x6E ); - bytes[18] = 0x0D; - bytes[19] = 0x0A; - } + bytes[17] = (byte)( IsFree ? 0x66 : 0x6E ); + bytes[18] = 0x0D; + bytes[19] = 0x0A; + } - public int ByteSize() - { - return 20; - } + public int ByteSize() + { + return 20; + } - private void _fillSpanLeadingZero(Span span, uint data) + private void _fillSpanLeadingZero(Span span, uint data) + { + uint val = data; + for (int i = span.Length - 1; i >= 0; i--) { - uint val = data; - for (int i = span.Length - 1; i >= 0; i--) - { - span[i] = (byte)( '0' + ( val % 10 ) ); - val /= 10; - } + span[i] = (byte)( '0' + ( val % 10 ) ); + val /= 10; } + } - private void _fillSpanLeadingZero(Span span, ushort data) + private void _fillSpanLeadingZero(Span span, ushort data) + { + int val = data; + for (int i = span.Length - 1; i >= 0; i--) { - int val = data; - for (int i = span.Length - 1; i >= 0; i--) - { - span[i] = (byte)( '0' + ( val % 10 ) ); - val /= 10; - } + span[i] = (byte)( '0' + ( val % 10 ) ); + val /= 10; } } } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/Section.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/Section.cs index 5cbbcc4..9f67447 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/Section.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/Section.cs @@ -1,44 +1,43 @@ using Synercoding.FileFormats.Pdf.Internals; using System; -namespace Synercoding.FileFormats.Pdf.LowLevel.XRef +namespace Synercoding.FileFormats.Pdf.LowLevel.XRef; + +internal class Section { - internal class Section + public Section(int firstObjectNumber, params Entry[] entries) { - public Section(int firstObjectNumber, params Entry[] entries) - { - FirstObjectNumber = firstObjectNumber; - Entries = entries; - } + FirstObjectNumber = firstObjectNumber; + Entries = entries; + } + + public int FirstObjectNumber { get; } + public int ObjectCount => Entries.Length; + public Entry[] Entries { get; } = new Entry[0]; - public int FirstObjectNumber { get; } - public int ObjectCount => Entries.Length; - public Entry[] Entries { get; } = new Entry[0]; + public void FillSpan(Span bytes) + { + int bytesFilled = SpanHelper.FillSpan(bytes[..], FirstObjectNumber); + bytes[bytesFilled] = 0x20; - public void FillSpan(Span bytes) + bytesFilled = bytesFilled + 1 + SpanHelper.FillSpan(bytes[( bytesFilled + 1 )..], ObjectCount); + bytes[bytesFilled] = 0x0D; + bytes[bytesFilled + 1] = 0x0A; + + for (int i = 0; i < Entries.Length; i++) { - int bytesFilled = SpanHelper.FillSpan(bytes.Slice(0), FirstObjectNumber); - bytes[bytesFilled] = 0x20; - - bytesFilled = bytesFilled + 1 + SpanHelper.FillSpan(bytes.Slice(bytesFilled + 1), ObjectCount); - bytes[bytesFilled] = 0x0D; - bytes[bytesFilled + 1] = 0x0A; - - for (int i = 0; i < Entries.Length; i++) - { - var position = bytesFilled + 2 + (i * 20); - Entries[i].FillSpan(bytes.Slice(position, 20)); - } + var position = bytesFilled + 2 + ( i * 20 ); + Entries[i].FillSpan(bytes.Slice(position, 20)); } + } - public int ByteSize() - { - var size = ByteSizes.Size(FirstObjectNumber); - size += ByteSizes.Size(ObjectCount); - size += 3; // 1 space + CR LF - size += ObjectCount * 20; + public int ByteSize() + { + var size = ByteSizes.Size(FirstObjectNumber); + size += ByteSizes.Size(ObjectCount); + size += 3; // 1 space + CR LF + size += ObjectCount * 20; - return size; - } + return size; } } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/Table.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/Table.cs index ae42c82..9820ef1 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/Table.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/Table.cs @@ -1,49 +1,48 @@ -using System; +using System; -namespace Synercoding.FileFormats.Pdf.LowLevel.XRef +namespace Synercoding.FileFormats.Pdf.LowLevel.XRef; + +internal class Table { - internal class Table + public Table(params Entry[] entries) + { + var combined = new Entry[entries.Length + 1]; + combined[0] = new Entry(0, 65535, true); + Array.Copy(entries, 0, combined, 1, entries.Length); + Section = new Section(0, combined); + } + + public Section Section { get; } + + internal uint WriteToStream(PdfStream stream) + { + var position = stream.Position; + + var bytes = new byte[_byteSize()]; + _fillSpan(bytes); + stream.Write(bytes); + + return position; + } + + private void _fillSpan(Span bytes) { - public Table(params Entry[] entries) - { - var combined = new Entry[entries.Length + 1]; - combined[0] = new Entry(0, 65535, true); - Array.Copy(entries, 0, combined, 1, entries.Length); - Section = new Section(0, combined); - } - - public Section Section { get; } - - internal uint WriteToStream(PdfStream stream) - { - var position = (uint)stream.Position; - - var bytes = new byte[_byteSize()]; - _fillSpan(bytes); - stream.Write(bytes); - - return position; - } - - private void _fillSpan(Span bytes) - { - bytes[0] = 0x78; // x - bytes[1] = 0x72; // r - bytes[2] = 0x65; // e - bytes[3] = 0x66; // f - bytes[4] = 0x0D; // CR - bytes[5] = 0x0A; // LF - - var freeSize = Section.ByteSize(); - Section.FillSpan(bytes.Slice(6, freeSize)); - } - - private int _byteSize() - { - int size = 6; // xref + CR LF - size += Section.ByteSize(); - - return size; - } + bytes[0] = 0x78; // x + bytes[1] = 0x72; // r + bytes[2] = 0x65; // e + bytes[3] = 0x66; // f + bytes[4] = 0x0D; // CR + bytes[5] = 0x0A; // LF + + var freeSize = Section.ByteSize(); + Section.FillSpan(bytes.Slice(6, freeSize)); + } + + private int _byteSize() + { + int size = 6; // xref + CR LF + size += Section.ByteSize(); + + return size; } } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/TableBuilder.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/TableBuilder.cs index 9f8b8d6..d5ec713 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/TableBuilder.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/TableBuilder.cs @@ -1,60 +1,58 @@ using Synercoding.FileFormats.Pdf.LowLevel.Internal; -using System; using System.Collections.Generic; using System.Linq; -namespace Synercoding.FileFormats.Pdf.LowLevel.XRef +namespace Synercoding.FileFormats.Pdf.LowLevel.XRef; + +internal class TableBuilder { - internal class TableBuilder - { - private readonly IdGenerator _idGen = new IdGenerator(); - private readonly Dictionary _positions = new Dictionary(); + private readonly IdGenerator _idGen = new IdGenerator(); + private readonly Dictionary _positions = new Dictionary(); - public PdfReference ReserveId() - { - var id = _idGen.GetId(); - var reference = new PdfReference(id); - _positions.Add(reference, -1); - return reference; - } + public PdfReference ReserveId() + { + var id = _idGen.GetId(); + var reference = new PdfReference(id); + _positions.Add(reference, -1); + return reference; + } - public PdfReference GetId(uint position) - { - var id = _idGen.GetId(); - var reference = new PdfReference(id); - _positions.Add(reference, position); - return reference; - } + public PdfReference GetId(uint position) + { + var id = _idGen.GetId(); + var reference = new PdfReference(id); + _positions.Add(reference, position); + return reference; + } - public bool TrySetPosition(PdfReference id, uint position) - { - if (_positions[id] != -1) - return false; + public bool TrySetPosition(PdfReference id, uint position) + { + if (_positions[id] != -1) + return false; - _positions[id] = position; + _positions[id] = position; - return true; - } + return true; + } - public bool Validate() + public bool Validate() + { + foreach (var value in _positions.Values) { - foreach (var value in _positions.Values) + if (value == -1) { - if (value == -1) - { - return false; - } + return false; } - return true; } + return true; + } - public Table GetXRefTable() - { - var entries = _positions - .OrderBy(static x => x.Key.ObjectId) - .Select(static x => new Entry((uint)x.Value)) - .ToArray(); - return new Table(entries); - } + public Table GetXRefTable() + { + var entries = _positions + .OrderBy(static x => x.Key.ObjectId) + .Select(static x => new Entry((uint)x.Value)) + .ToArray(); + return new Table(entries); } } diff --git a/src/Synercoding.FileFormats.Pdf/Matrix.cs b/src/Synercoding.FileFormats.Pdf/Matrix.cs index 40c1f69..b7707ae 100644 --- a/src/Synercoding.FileFormats.Pdf/Matrix.cs +++ b/src/Synercoding.FileFormats.Pdf/Matrix.cs @@ -1,262 +1,261 @@ using System; using System.Runtime.CompilerServices; -namespace Synercoding.FileFormats.Pdf +namespace Synercoding.FileFormats.Pdf; + +/// +/// Struct that represents a +/// +/// +/// Matrix is structured as follows: +/// +/// A B 0 +/// C D 0 +/// E F 1 +/// +/// +public readonly struct Matrix : IEquatable { /// - /// Struct that represents a + /// The identity matrix /// /// - /// Matrix is structured as follows: - /// - /// A B 0 - /// C D 0 - /// E F 1 - /// + /// This matrix can be interpreted as: + /// + /// translation with (0,0) + /// rotation with 0 degrees + /// scaling with (1,1) + /// /// - public readonly struct Matrix : IEquatable - { - /// - /// The identity matrix - /// - /// - /// This matrix can be interpreted as: - /// - /// translation with (0,0) - /// rotation with 0 degrees - /// scaling with (1,1) - /// - /// - public static Matrix Identity { get; } = new Matrix(1, 0, 0, 1, 0, 0); + public static Matrix Identity { get; } = new Matrix(1, 0, 0, 1, 0, 0); - /// - /// Copy constructor for the - /// - /// The matrix to copy - public Matrix(Matrix matrix) - { - A = matrix.A; - B = matrix.B; - C = matrix.C; - D = matrix.D; - E = matrix.E; - F = matrix.F; - } + /// + /// Copy constructor for the + /// + /// The matrix to copy + public Matrix(Matrix matrix) + { + A = matrix.A; + B = matrix.B; + C = matrix.C; + D = matrix.D; + E = matrix.E; + F = matrix.F; + } - /// - /// Constructor for the - /// - /// The value - /// The value - /// The value - /// The value - /// The value - /// The value - public Matrix(double a, double b, double c, double d, double e, double f) - { - A = a; - B = b; - C = c; - D = d; - E = e; - F = f; - } + /// + /// Constructor for the + /// + /// The value + /// The value + /// The value + /// The value + /// The value + /// The value + public Matrix(double a, double b, double c, double d, double e, double f) + { + A = a; + B = b; + C = c; + D = d; + E = e; + F = f; + } - /// - /// The A value - /// - public double A { get; } + /// + /// The A value + /// + public double A { get; } - /// - /// The B value - /// - public double B { get; } + /// + /// The B value + /// + public double B { get; } - /// - /// The C value - /// - public double C { get; } + /// + /// The C value + /// + public double C { get; } - /// - /// The D value - /// - public double D { get; } + /// + /// The D value + /// + public double D { get; } - /// - /// The E value - /// - public double E { get; } + /// + /// The E value + /// + public double E { get; } - /// - /// The F value - /// - public double F { get; } + /// + /// The F value + /// + public double F { get; } - /// - public override bool Equals(object? obj) - => obj is Matrix other && Equals(other); + /// + public override bool Equals(object? obj) + => obj is Matrix other && Equals(other); - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Matrix other) - => A.Equals(other.A) - && B.Equals(other.B) - && C.Equals(other.C) - && D.Equals(other.D) - && E.Equals(other.E) - && F.Equals(other.F); + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Matrix other) + => A.Equals(other.A) + && B.Equals(other.B) + && C.Equals(other.C) + && D.Equals(other.D) + && E.Equals(other.E) + && F.Equals(other.F); - /// - public override int GetHashCode() - => HashCode.Combine(A, B, C, D, E, F); + /// + public override int GetHashCode() + => HashCode.Combine(A, B, C, D, E, F); - /// - public override string ToString() - => $"Matrix [ {A}, {B}, {C}, {D}, {E}, {F} ]"; + /// + public override string ToString() + => $"Matrix [ {A}, {B}, {C}, {D}, {E}, {F} ]"; - /// - /// Apply a rotation operation on the matrix - /// - /// The amount of degrees to rotate - /// The new - public Matrix Rotate(double degrees) - => Multiply(CreateRotationMatrix(degrees)); + /// + /// Apply a rotation operation on the matrix + /// + /// The amount of degrees to rotate + /// The new + public Matrix Rotate(double degrees) + => Multiply(CreateRotationMatrix(degrees)); - /// - /// Apply a horizontal flip operation on the matrix - /// - /// The new - public Matrix FlipHorizontal() - => Multiply(CreateScaleMatrix(-1, 1)); + /// + /// Apply a horizontal flip operation on the matrix + /// + /// The new + public Matrix FlipHorizontal() + => Multiply(CreateScaleMatrix(-1, 1)); - /// - /// Apply a vertical flip operation on the matrix - /// - /// The new - public Matrix FlipVertical() - => Multiply(CreateScaleMatrix(1, -1)); + /// + /// Apply a vertical flip operation on the matrix + /// + /// The new + public Matrix FlipVertical() + => Multiply(CreateScaleMatrix(1, -1)); - /// - /// Apply a scale operation on the matrix - /// - /// The X amount to scale - /// The Y amount to scale - /// The new - public Matrix Scale(double x, double y) - => Multiply(CreateScaleMatrix(x, y)); + /// + /// Apply a scale operation on the matrix + /// + /// The X amount to scale + /// The Y amount to scale + /// The new + public Matrix Scale(double x, double y) + => Multiply(CreateScaleMatrix(x, y)); - /// - /// Apply a skew operation on the matrix - /// - /// The amount of degrees to skew (top left direction) - /// The amount of degrees to skew (bottom right direction) - /// The new - public Matrix Skew(double a, double b) - => Multiply(CreateSkewMatrix(a, b)); + /// + /// Apply a skew operation on the matrix + /// + /// The amount of degrees to skew (top left direction) + /// The amount of degrees to skew (bottom right direction) + /// The new + public Matrix Skew(double a, double b) + => Multiply(CreateSkewMatrix(a, b)); - /// - /// Apply a translation operation on the matrix - /// - /// The X distance to translate - /// The Y distance to translate - /// The new - public Matrix Translate(double x, double y) - => Multiply(CreateTranslationMatrix(x, y)); + /// + /// Apply a translation operation on the matrix + /// + /// The X distance to translate + /// The Y distance to translate + /// The new + public Matrix Translate(double x, double y) + => Multiply(CreateTranslationMatrix(x, y)); - /// - /// Multiply matrix with - /// - /// The left side of the matrix multiplication - /// The right side of the matrix multiplication - /// The new - public static Matrix operator *(Matrix m1, Matrix m2) - => m1.Multiply(m2); + /// + /// Multiply matrix with + /// + /// The left side of the matrix multiplication + /// The right side of the matrix multiplication + /// The new + public static Matrix operator *(Matrix m1, Matrix m2) + => m1.Multiply(m2); - /// - /// Multiply this matrix with - /// - /// The matrix to multiply with - /// The new - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Matrix Multiply(in Matrix other) - => new Matrix( - a: (A * other.A) + (B * other.C), - b: (A * other.B) + (B * other.D), - c: (C * other.A) + (D * other.C), - d: (C * other.B) + (D * other.D), - e: (E * other.A) + (F * other.C) + other.E, - f: (E * other.B) + (F * other.D) + other.F); + /// + /// Multiply this matrix with + /// + /// The matrix to multiply with + /// The new + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Matrix Multiply(in Matrix other) + => new Matrix( + a: ( A * other.A ) + ( B * other.C ), + b: ( A * other.B ) + ( B * other.D ), + c: ( C * other.A ) + ( D * other.C ), + d: ( C * other.B ) + ( D * other.D ), + e: ( E * other.A ) + ( F * other.C ) + other.E, + f: ( E * other.B ) + ( F * other.D ) + other.F); - /// - /// Create a matrix used for rotation - /// - /// The amount of degrees to rotate by - /// A rotated matrix - public static Matrix CreateRotationMatrix(double degree) - { - var rads = _degreeToRad(degree); - return new Matrix( - Math.Cos(rads), Math.Sin(rads) * -1, - Math.Sin(rads), Math.Cos(rads), - 0, 0); - } + /// + /// Create a matrix used for rotation + /// + /// The amount of degrees to rotate by + /// A rotated matrix + public static Matrix CreateRotationMatrix(double degree) + { + var rads = _degreeToRad(degree); + return new Matrix( + Math.Cos(rads), Math.Sin(rads) * -1, + Math.Sin(rads), Math.Cos(rads), + 0, 0); + } - /// - /// Create a matrix used for scaling - /// - /// The X scale - /// The Y scale - /// A scaled matrix - public static Matrix CreateScaleMatrix(double x, double y) - => new Matrix( - x, 0, - 0, y, - 0, 0); + /// + /// Create a matrix used for scaling + /// + /// The X scale + /// The Y scale + /// A scaled matrix + public static Matrix CreateScaleMatrix(double x, double y) + => new Matrix( + x, 0, + 0, y, + 0, 0); - /// - /// Create a matrix used for skewing - /// - /// The amount of degree to skew the top left direction - /// The amount of degree to skew the bottom right direction - /// A skewed matrix - public static Matrix CreateSkewMatrix(double a, double b) - => new Matrix( - 1, Math.Tan(_degreeToRad(a)), - Math.Tan(_degreeToRad(b)), 1, - 0, 0); + /// + /// Create a matrix used for skewing + /// + /// The amount of degree to skew the top left direction + /// The amount of degree to skew the bottom right direction + /// A skewed matrix + public static Matrix CreateSkewMatrix(double a, double b) + => new Matrix( + 1, Math.Tan(_degreeToRad(a)), + Math.Tan(_degreeToRad(b)), 1, + 0, 0); - /// - /// Create a matrix used for translation - /// - /// The amount of X translation - /// The amount of Y translation - /// A matrix representing a translation - public static Matrix CreateTranslationMatrix(double x, double y) - => new Matrix( - 1, 0, - 0, 1, - x, y); + /// + /// Create a matrix used for translation + /// + /// The amount of X translation + /// The amount of Y translation + /// A matrix representing a translation + public static Matrix CreateTranslationMatrix(double x, double y) + => new Matrix( + 1, 0, + 0, 1, + x, y); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static double _degreeToRad(double degree) - => degree * Math.PI / 180; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static double _degreeToRad(double degree) + => degree * Math.PI / 180; - /// - /// Indicates whether the left matrix is equal to the right matrix. - /// - /// The on the left side of the == - /// The on the right side of the == - /// true if the left matrix is equal to the right; otherwise, false. - public static bool operator ==(Matrix left, Matrix right) - => left.Equals(right); + /// + /// Indicates whether the left matrix is equal to the right matrix. + /// + /// The on the left side of the == + /// The on the right side of the == + /// true if the left matrix is equal to the right; otherwise, false. + public static bool operator ==(Matrix left, Matrix right) + => left.Equals(right); - /// - /// Indicates whether the left matrix is not equal to the right matrix. - /// - /// The on the left side of the != - /// The on the right side of the != - /// true if the left matrix is not equal to the right; otherwise, false. - public static bool operator !=(Matrix left, Matrix right) - => !( left == right ); - } + /// + /// Indicates whether the left matrix is not equal to the right matrix. + /// + /// The on the left side of the != + /// The on the right side of the != + /// true if the left matrix is not equal to the right; otherwise, false. + public static bool operator !=(Matrix left, Matrix right) + => !( left == right ); } diff --git a/src/Synercoding.FileFormats.Pdf/PdfPage.cs b/src/Synercoding.FileFormats.Pdf/PdfPage.cs index 3b4d903..bad939d 100644 --- a/src/Synercoding.FileFormats.Pdf/PdfPage.cs +++ b/src/Synercoding.FileFormats.Pdf/PdfPage.cs @@ -6,95 +6,94 @@ using Synercoding.Primitives.Extensions; using System; -namespace Synercoding.FileFormats.Pdf +namespace Synercoding.FileFormats.Pdf; + +/// +/// This class represents a page in a pdf +/// +public sealed class PdfPage : IDisposable { - /// - /// This class represents a page in a pdf - /// - public sealed class PdfPage : IDisposable - { - private readonly TableBuilder _tableBuilder; - private readonly PageTree _parent; + private readonly TableBuilder _tableBuilder; + private readonly PageTree _parent; - private PageRotation? _rotation; + private PageRotation? _rotation; - internal PdfPage(TableBuilder tableBuilder, PageTree parent) - { - _tableBuilder = tableBuilder; - _parent = parent; - _parent.AddPage(this); + internal PdfPage(TableBuilder tableBuilder, PageTree parent) + { + _tableBuilder = tableBuilder; + _parent = parent; + _parent.AddPage(this); - PageNumber = _parent.PageCount; - Reference = tableBuilder.ReserveId(); - Resources = new PageResources(_tableBuilder); - var contentStream = new ContentStream(tableBuilder.ReserveId(), Resources); + PageNumber = _parent.PageCount; + Reference = tableBuilder.ReserveId(); + Resources = new PageResources(_tableBuilder); + var contentStream = new ContentStream(tableBuilder.ReserveId(), Resources); - Content = new PageContentContext(contentStream); - } + Content = new PageContentContext(contentStream); + } - internal PdfReference Parent - => _parent.Reference; + internal PdfReference Parent + => _parent.Reference; - internal PageResources Resources { get; } + internal PageResources Resources { get; } - /// - /// The number of the page - /// - public int PageNumber { get; } + /// + /// The number of the page + /// + public int PageNumber { get; } - public IPageContentContext Content { get; } + public IPageContentContext Content { get; } - /// - /// A pdf reference object that can be used to reference to this object - /// - public PdfReference Reference { get; } + /// + /// A pdf reference object that can be used to reference to this object + /// + public PdfReference Reference { get; } - /// - /// The rotation of how the page is displayed, must be in increments of 90 - /// - public PageRotation? Rotation + /// + /// The rotation of how the page is displayed, must be in increments of 90 + /// + public PageRotation? Rotation + { + get => _rotation; + set { - get => _rotation; - set - { - if (value is not null && !Enum.IsDefined(value.Value)) - throw new ArgumentOutOfRangeException(nameof(Rotation), value, "The provided value can only be increments of 90."); - - _rotation = value; - } + if (value is not null && !Enum.IsDefined(value.Value)) + throw new ArgumentOutOfRangeException(nameof(Rotation), value, "The provided value can only be increments of 90."); + + _rotation = value; } + } - /// - /// The media box of the - /// - public Rectangle MediaBox { get; set; } = Sizes.A4.AsRectangle(); - - /// - /// The cropbox of the , defaults to - /// - public Rectangle? CropBox { get; set; } - - /// - /// The bleed box of the - /// - public Rectangle? BleedBox { get; set; } - - /// - /// The trim box of the - /// - public Rectangle? TrimBox { get; set; } - - /// - /// The art box of the - /// - public Rectangle? Art { get; set; } - - /// - public void Dispose() - { - Resources.Dispose(); + /// + /// The media box of the + /// + public Rectangle MediaBox { get; set; } = Sizes.A4.AsRectangle(); - Content.RawContentStream.Dispose(); - } + /// + /// The cropbox of the , defaults to + /// + public Rectangle? CropBox { get; set; } + + /// + /// The bleed box of the + /// + public Rectangle? BleedBox { get; set; } + + /// + /// The trim box of the + /// + public Rectangle? TrimBox { get; set; } + + /// + /// The art box of the + /// + public Rectangle? Art { get; set; } + + /// + public void Dispose() + { + Resources.Dispose(); + + Content.RawContentStream.Dispose(); } } diff --git a/src/Synercoding.FileFormats.Pdf/PdfWriter.cs b/src/Synercoding.FileFormats.Pdf/PdfWriter.cs index 4720e90..74d2523 100644 --- a/src/Synercoding.FileFormats.Pdf/PdfWriter.cs +++ b/src/Synercoding.FileFormats.Pdf/PdfWriter.cs @@ -8,292 +8,291 @@ using System.Reflection; using System.Threading.Tasks; -namespace Synercoding.FileFormats.Pdf +namespace Synercoding.FileFormats.Pdf; + +/// +/// This class is the start point for creating a pdf file +/// +public sealed class PdfWriter : IDisposable { + private readonly bool _ownsStream; + private readonly ObjectStream _objectStream; + private readonly TableBuilder _tableBuilder = new TableBuilder(); + + private readonly PageTree _pageTree; + private readonly Catalog _catalog; + + private bool _endingWritten = false; + /// - /// This class is the start point for creating a pdf file + /// Constructor for /// - public sealed class PdfWriter : IDisposable + /// The to write the PDF file + public PdfWriter(Stream stream) + : this(stream, true) + { } + + /// + /// Constructor for + /// + /// The to write the PDF file + /// If the stream is owned, then when this is disposed, the stream is also disposed. + public PdfWriter(Stream stream, bool ownsStream) { - private readonly bool _ownsStream; - private readonly ObjectStream _objectStream; - private readonly TableBuilder _tableBuilder = new TableBuilder(); - - private readonly PageTree _pageTree; - private readonly Catalog _catalog; - - private bool _endingWritten = false; - - /// - /// Constructor for - /// - /// The to write the PDF file - public PdfWriter(Stream stream) - : this(stream, true) - { } - - /// - /// Constructor for - /// - /// The to write the PDF file - /// If the stream is owned, then when this is disposed, the stream is also disposed. - public PdfWriter(Stream stream, bool ownsStream) + var pdfStream = new PdfStream(stream); + _writeHeader(pdfStream); + _objectStream = new ObjectStream(pdfStream, _tableBuilder); + + _pageTree = new PageTree(_tableBuilder.ReserveId()); + _catalog = new Catalog(_tableBuilder.ReserveId(), _pageTree); + + DocumentInformation = new DocumentInformation(_tableBuilder.ReserveId()) { - var pdfStream = new PdfStream(stream); - _writeHeader(pdfStream); - _objectStream = new ObjectStream(pdfStream, _tableBuilder); + Producer = $"Synercoding.FileFormats.Pdf {typeof(PdfWriter).GetTypeInfo().Assembly.GetName().Version}", + CreationDate = DateTime.Now + }; - _pageTree = new PageTree(_tableBuilder.ReserveId()); - _catalog = new Catalog(_tableBuilder.ReserveId(), _pageTree); + _ownsStream = ownsStream; + } - DocumentInformation = new DocumentInformation(_tableBuilder.ReserveId()) - { - Producer = $"Synercoding.FileFormats.Pdf {typeof(PdfWriter).GetTypeInfo().Assembly.GetName().Version}", - CreationDate = DateTime.Now - }; + /// + /// Document information, such as the author and title + /// + public DocumentInformation DocumentInformation { get; } - _ownsStream = ownsStream; - } + /// + /// Returns the number of pages already added to the writer + /// + public int PageCount + => _pageTree.PageCount; - /// - /// Document information, such as the author and title - /// - public DocumentInformation DocumentInformation { get; } - - /// - /// Returns the number of pages already added to the writer - /// - public int PageCount - => _pageTree.PageCount; - - /// - /// Set meta information for this document - /// - /// Action used to set meta data - /// Returns this to chain calls - public PdfWriter SetDocumentInfo(Action infoAction) - => SetDocumentInfo(infoAction, static (action, did) => action(did)); - - /// - /// Set meta information for this document - /// - /// Type of data to pass to the action - /// Data to be used in the action - /// Action used to set meta data - /// Returns this to chain calls - public PdfWriter SetDocumentInfo(T data, Action infoAction) - { - _throwWhenEndingWritten(); + /// + /// Set meta information for this document + /// + /// Action used to set meta data + /// Returns this to chain calls + public PdfWriter SetDocumentInfo(Action infoAction) + => SetDocumentInfo(infoAction, static (action, did) => action(did)); + + /// + /// Set meta information for this document + /// + /// Type of data to pass to the action + /// Data to be used in the action + /// Action used to set meta data + /// Returns this to chain calls + public PdfWriter SetDocumentInfo(T data, Action infoAction) + { + _throwWhenEndingWritten(); - infoAction(data, DocumentInformation); + infoAction(data, DocumentInformation); - return this; - } + return this; + } - /// - /// Add a page to the pdf file - /// - /// Action used to setup the page - /// Returns this to chain calls - public PdfWriter AddPage(Action pageAction) - => AddPage(pageAction, static (action, page) => action(page)); - - /// - /// Add a page to the pdf file - /// - /// Data passed into the action - /// Action used to setup the page - /// Returns this to chain calls - public PdfWriter AddPage(T data, Action pageAction) - { - _throwWhenEndingWritten(); + /// + /// Add a page to the pdf file + /// + /// Action used to setup the page + /// Returns this to chain calls + public PdfWriter AddPage(Action pageAction) + => AddPage(pageAction, static (action, page) => action(page)); - using (var page = new PdfPage(_tableBuilder, _pageTree)) - { - pageAction(data, page); - _writePageAndResourcesToObjectStream(page); - } + /// + /// Add a page to the pdf file + /// + /// Data passed into the action + /// Action used to setup the page + /// Returns this to chain calls + public PdfWriter AddPage(T data, Action pageAction) + { + _throwWhenEndingWritten(); - return this; + using (var page = new PdfPage(_tableBuilder, _pageTree)) + { + pageAction(data, page); + _writePageAndResourcesToObjectStream(page); } - /// - /// Add a page to the pdf file - /// - /// Action used to setup the page - /// Returns an awaitable task that resolves into this to chain calls - public async Task AddPageAsync(Func pageAction) - => await AddPageAsync(pageAction, static async (action, page) => await action(page)); - - /// - /// Add a page to the pdf file - /// - /// Data passed into the action - /// Action used to setup the page - /// Returns an awaitable task that resolves into this to chain calls - public async Task AddPageAsync(T data, Func pageAction) - { - _throwWhenEndingWritten(); + return this; + } - using (var page = new PdfPage(_tableBuilder, _pageTree)) - { - await pageAction(data, page); + /// + /// Add a page to the pdf file + /// + /// Action used to setup the page + /// Returns an awaitable task that resolves into this to chain calls + public async Task AddPageAsync(Func pageAction) + => await AddPageAsync(pageAction, static async (action, page) => await action(page)); - _writePageAndResourcesToObjectStream(page); - } + /// + /// Add a page to the pdf file + /// + /// Data passed into the action + /// Action used to setup the page + /// Returns an awaitable task that resolves into this to chain calls + public async Task AddPageAsync(T data, Func pageAction) + { + _throwWhenEndingWritten(); - return this; + using (var page = new PdfPage(_tableBuilder, _pageTree)) + { + await pageAction(data, page); + + _writePageAndResourcesToObjectStream(page); } - /// - /// Add an to the pdf file and get the reference returned - /// - /// The image that needs to be added. - /// The image reference that can be used in pages - public Image AddImage(SixLabors.ImageSharp.Image image) - { - _throwWhenEndingWritten(); + return this; + } - var id = _tableBuilder.ReserveId(); + /// + /// Add an to the pdf file and get the reference returned + /// + /// The image that needs to be added. + /// The image reference that can be used in pages + public Image AddImage(SixLabors.ImageSharp.Image image) + { + _throwWhenEndingWritten(); - var pdfImage = new Image(id, image); + var id = _tableBuilder.ReserveId(); - _objectStream.Write(pdfImage); + var pdfImage = new Image(id, image); - return pdfImage; - } + _objectStream.Write(pdfImage); - /// - /// Add an jpg to the pdf file and get the reference returned - /// - /// - /// The is not checked, and is used as is. Make sure only streams that represent a JPG are used. - /// - /// The of the jpg image that needs to be added. - /// The width of the image in the . - /// The height of the image in the . - /// The color space of the jpg image. - /// The image reference that can be used in pages - public Image AddJpgUnsafe(Stream jpgStream, int originalWidth, int originalHeight, ColorSpace colorSpace) - { - _throwWhenEndingWritten(); + return pdfImage; + } - var id = _tableBuilder.ReserveId(); + /// + /// Add an jpg to the pdf file and get the reference returned + /// + /// + /// The is not checked, and is used as is. Make sure only streams that represent a JPG are used. + /// + /// The of the jpg image that needs to be added. + /// The width of the image in the . + /// The height of the image in the . + /// The color space of the jpg image. + /// The image reference that can be used in pages + public Image AddJpgUnsafe(Stream jpgStream, int originalWidth, int originalHeight, ColorSpace colorSpace) + { + _throwWhenEndingWritten(); - var pdfImage = new Image(id, jpgStream, originalWidth, originalHeight, colorSpace); + var id = _tableBuilder.ReserveId(); - _objectStream.Write(pdfImage); + var pdfImage = new Image(id, jpgStream, originalWidth, originalHeight, colorSpace); - return pdfImage; - } + _objectStream.Write(pdfImage); - /// - /// Write the PDF trailer; indicates that the PDF is done. - /// - /// - /// Other calls to this will throw or have no effect after call this. - /// - public void WriteTrailer() - { - if (_endingWritten) - return; + return pdfImage; + } - _objectStream - .Write(_pageTree) - .Write(_catalog) - .Write(DocumentInformation); + /// + /// Write the PDF trailer; indicates that the PDF is done. + /// + /// + /// Other calls to this will throw or have no effect after call this. + /// + public void WriteTrailer() + { + if (_endingWritten) + return; - _writePdfEnding(); + _objectStream + .Write(_pageTree) + .Write(_catalog) + .Write(DocumentInformation); - _objectStream.InnerStream.Flush(); + _writePdfEnding(); - _endingWritten = true; - } + _objectStream.InnerStream.Flush(); - /// - public void Dispose() - { - WriteTrailer(); + _endingWritten = true; + } - _objectStream.InnerStream.Flush(); + /// + public void Dispose() + { + WriteTrailer(); - if (_ownsStream) - { - _objectStream.InnerStream.Dispose(); - } - } + _objectStream.InnerStream.Flush(); - private void _writePageAndResourcesToObjectStream(PdfPage page) + if (_ownsStream) { - _objectStream.Write(page); + _objectStream.InnerStream.Dispose(); + } + } - foreach (var kv in page.Resources.Images) - _objectStream.Write(kv.Value); + private void _writePageAndResourcesToObjectStream(PdfPage page) + { + _objectStream.Write(page); - foreach (var (font, refId) in page.Resources.FontReferences) - _objectStream.Write(refId, font); + foreach (var kv in page.Resources.Images) + _objectStream.Write(kv.Value); - foreach (var (separation, (_, refId)) in page.Resources.SeparationReferences) - _objectStream.Write(refId, separation); + foreach (var (font, refId) in page.Resources.FontReferences) + _objectStream.Write(refId, font); - _objectStream.Write(page.Content.RawContentStream); - } + foreach (var (separation, (_, refId)) in page.Resources.SeparationReferences) + _objectStream.Write(refId, separation); - private void _throwWhenEndingWritten() - { - if (_endingWritten) throw new InvalidOperationException("Can't change document information when PDF trailer is written to the stream."); - } + _objectStream.Write(page.Content.RawContentStream); + } - private static void _writeHeader(PdfStream stream) - { - stream.WriteByte(0x25); // % - stream.WriteByte(0x50); // P - stream.WriteByte(0x44); // D - stream.WriteByte(0x46); // F - stream.WriteByte(0x2D); // - - stream.WriteByte(0x31); // 1 - stream.WriteByte(0x2E); // . - stream.WriteByte(0x37); // 7 - stream.WriteByte(0x0D); // CR - stream.WriteByte(0x0A); // LF - stream.WriteByte(0x25); // % - stream.WriteByte(0x81); // binary indicator > 128 - stream.WriteByte(0x82); // binary indicator > 128 - stream.WriteByte(0x83); // binary indicator > 128 - stream.WriteByte(0x84); // binary indicator > 128 - stream.WriteByte(0x0D); // CR - stream.WriteByte(0x0A); // LF - } + private void _throwWhenEndingWritten() + { + if (_endingWritten) throw new InvalidOperationException("Can't change document information when PDF trailer is written to the stream."); + } - private void _writePdfEnding() - { - if (!_tableBuilder.Validate()) - throw new InvalidOperationException("XRef table is invalid."); + private static void _writeHeader(PdfStream stream) + { + stream.WriteByte(0x25); // % + stream.WriteByte(0x50); // P + stream.WriteByte(0x44); // D + stream.WriteByte(0x46); // F + stream.WriteByte(0x2D); // - + stream.WriteByte(0x31); // 1 + stream.WriteByte(0x2E); // . + stream.WriteByte(0x37); // 7 + stream.WriteByte(0x0D); // CR + stream.WriteByte(0x0A); // LF + stream.WriteByte(0x25); // % + stream.WriteByte(0x81); // binary indicator > 128 + stream.WriteByte(0x82); // binary indicator > 128 + stream.WriteByte(0x83); // binary indicator > 128 + stream.WriteByte(0x84); // binary indicator > 128 + stream.WriteByte(0x0D); // CR + stream.WriteByte(0x0A); // LF + } - var xRefTable = _tableBuilder.GetXRefTable(); - uint xRefPosition = xRefTable.WriteToStream(_objectStream.InnerStream); + private void _writePdfEnding() + { + if (!_tableBuilder.Validate()) + throw new InvalidOperationException("XRef table is invalid."); - _writeTrailer(_objectStream.InnerStream, xRefPosition, xRefTable.Section.ObjectCount, _catalog.Reference, DocumentInformation.Reference); - } + var xRefTable = _tableBuilder.GetXRefTable(); + uint xRefPosition = xRefTable.WriteToStream(_objectStream.InnerStream); - private void _writeTrailer(PdfStream stream, uint startXRef, int size, PdfReference root, PdfReference documentInfo) - { - stream - .Write("trailer") - .NewLine() - .Dictionary((size, root, documentInfo), static (triple, dictionary) => - { - var (size, root, documentInfo) = triple; - dictionary - .Write(PdfName.Get("Size"), size) - .Write(PdfName.Get("Root"), root) - .Write(PdfName.Get("Info"), documentInfo); - }) - .Write("startxref") - .NewLine() - .Write(startXRef) - .NewLine() - .Write("%%EOF"); - } + _writeTrailer(_objectStream.InnerStream, xRefPosition, xRefTable.Section.ObjectCount, _catalog.Reference, DocumentInformation.Reference); + } + + private void _writeTrailer(PdfStream stream, uint startXRef, int size, PdfReference root, PdfReference documentInfo) + { + stream + .Write("trailer") + .NewLine() + .Dictionary((size, root, documentInfo), static (triple, dictionary) => + { + var (size, root, documentInfo) = triple; + dictionary + .Write(PdfName.Get("Size"), size) + .Write(PdfName.Get("Root"), root) + .Write(PdfName.Get("Info"), documentInfo); + }) + .Write("startxref") + .NewLine() + .Write(startXRef) + .NewLine() + .Write("%%EOF"); } } From 2fe3f2d0dd4d59c47d69a85893e65ec34791ffda Mon Sep 17 00:00:00 2001 From: Gerard Gunnewijk Date: Thu, 26 Jan 2023 11:41:05 +0100 Subject: [PATCH 05/11] Fixed a few info level messages --- src/Synercoding.FileFormats.Pdf/LowLevel/PdfName.cs | 2 -- src/Synercoding.FileFormats.Pdf/LowLevel/XRef/Entry.cs | 9 ++------- src/Synercoding.FileFormats.Pdf/LowLevel/XRef/Section.cs | 2 +- src/Synercoding.FileFormats.Pdf/PdfWriter.cs | 2 +- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/PdfName.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/PdfName.cs index a955b8c..0b9d09d 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/PdfName.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/PdfName.cs @@ -49,7 +49,6 @@ public class PdfName { "XObject", new PdfName("XObject") }, }; - private readonly string _raw; private readonly string _encoded; private PdfName(string raw) @@ -58,7 +57,6 @@ private PdfName(string raw) throw new ArgumentOutOfRangeException(nameof(raw), "The name is too long. Max length of 127 is allowed."); if (raw.Any(c => c > 0xff)) throw new ArgumentOutOfRangeException(nameof(raw), "The name contains non-ascii characters, and is thus not allowed."); - _raw = raw; _encoded = _encode(raw); } diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/Entry.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/Entry.cs index 70e6bbc..4968d2d 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/Entry.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/Entry.cs @@ -33,12 +33,7 @@ public void FillSpan(Span bytes) bytes[19] = 0x0A; } - public int ByteSize() - { - return 20; - } - - private void _fillSpanLeadingZero(Span span, uint data) + private static void _fillSpanLeadingZero(Span span, uint data) { uint val = data; for (int i = span.Length - 1; i >= 0; i--) @@ -48,7 +43,7 @@ private void _fillSpanLeadingZero(Span span, uint data) } } - private void _fillSpanLeadingZero(Span span, ushort data) + private static void _fillSpanLeadingZero(Span span, ushort data) { int val = data; for (int i = span.Length - 1; i >= 0; i--) diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/Section.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/Section.cs index 9f67447..eae01b1 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/Section.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/XRef/Section.cs @@ -13,7 +13,7 @@ public Section(int firstObjectNumber, params Entry[] entries) public int FirstObjectNumber { get; } public int ObjectCount => Entries.Length; - public Entry[] Entries { get; } = new Entry[0]; + public Entry[] Entries { get; } = Array.Empty(); public void FillSpan(Span bytes) { diff --git a/src/Synercoding.FileFormats.Pdf/PdfWriter.cs b/src/Synercoding.FileFormats.Pdf/PdfWriter.cs index 74d2523..1cecde9 100644 --- a/src/Synercoding.FileFormats.Pdf/PdfWriter.cs +++ b/src/Synercoding.FileFormats.Pdf/PdfWriter.cs @@ -276,7 +276,7 @@ private void _writePdfEnding() _writeTrailer(_objectStream.InnerStream, xRefPosition, xRefTable.Section.ObjectCount, _catalog.Reference, DocumentInformation.Reference); } - private void _writeTrailer(PdfStream stream, uint startXRef, int size, PdfReference root, PdfReference documentInfo) + private static void _writeTrailer(PdfStream stream, uint startXRef, int size, PdfReference root, PdfReference documentInfo) { stream .Write("trailer") From 0017f73a7177eaa304438440e6e17c446188c192 Mon Sep 17 00:00:00 2001 From: Gerard Gunnewijk Date: Thu, 26 Jan 2023 12:53:05 +0100 Subject: [PATCH 06/11] Correctly preserved the graphic state & some xml comments --- .../Extensions/IContentContextExtensions.cs | 18 ++ .../IPageContentContextExtensions.cs | 6 +- .../Extensions/IShapeContextExtensions.cs | 2 +- .../GraphicState.cs | 70 +++++ .../IContentContext.cs | 11 +- .../IPageContentContext.cs | 4 +- ...hapeContext.cs => IShapeContentContext.cs} | 77 +++-- .../ITextContentContext.cs | 11 +- src/Synercoding.FileFormats.Pdf/Image.cs | 6 + .../Internals/PageContentContext.cs | 74 ++--- .../Internals/ShapesContentContext.cs | 209 +++++++++++++ .../Internals/ShapesContext.cs | 241 -------------- .../Internals/TextContentContext.cs | 106 ++----- .../Internals/WrappedShapesContext.cs | 241 -------------- .../Internals/WrappedTextContentContext.cs | 294 ------------------ .../Extensions/PdfStreamExtensions.cs | 12 + .../LowLevel/Graphics/LineJoinStyle.cs | 2 +- .../LowLevel/PdfDictionary.cs | 12 + src/Synercoding.FileFormats.Pdf/PdfPage.cs | 5 +- 19 files changed, 433 insertions(+), 968 deletions(-) create mode 100644 src/Synercoding.FileFormats.Pdf/GraphicState.cs rename src/Synercoding.FileFormats.Pdf/{IShapeContext.cs => IShapeContentContext.cs} (53%) create mode 100644 src/Synercoding.FileFormats.Pdf/Internals/ShapesContentContext.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/Internals/ShapesContext.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/Internals/WrappedShapesContext.cs delete mode 100644 src/Synercoding.FileFormats.Pdf/Internals/WrappedTextContentContext.cs diff --git a/src/Synercoding.FileFormats.Pdf/Extensions/IContentContextExtensions.cs b/src/Synercoding.FileFormats.Pdf/Extensions/IContentContextExtensions.cs index 6af2d61..9d33fb4 100644 --- a/src/Synercoding.FileFormats.Pdf/Extensions/IContentContextExtensions.cs +++ b/src/Synercoding.FileFormats.Pdf/Extensions/IContentContextExtensions.cs @@ -2,12 +2,30 @@ using System.Threading.Tasks; namespace Synercoding.FileFormats.Pdf.Extensions; + +/// +/// Extension class for +/// public static class IContentContextExtensions { + /// + /// Wraps the operations in in save state and restore state operators. + /// + /// The context type where this extension applies to. + /// The content context where that will be wrapped. + /// The operations to be wrapped. + /// The same to enable method chaining. public static TContext WrapInState(this TContext context, Action contentOperations) where TContext : IContentContext => context.WrapInState(contentOperations, static (operations, context) => operations(context)); + /// + /// Wraps the operations in in save state and restore state operators. + /// + /// The context type where this extension applies to. + /// The content context where that will be wrapped. + /// The async operations to be wrapped. + /// Task that resolves to the same to enable method chaining. public static Task WrapInStateAsync(this TContext context, Func contentOperations) where TContext : IContentContext => context.WrapInStateAsync(contentOperations, static (operations, context) => operations(context)); diff --git a/src/Synercoding.FileFormats.Pdf/Extensions/IPageContentContextExtensions.cs b/src/Synercoding.FileFormats.Pdf/Extensions/IPageContentContextExtensions.cs index d84ba60..08766ea 100644 --- a/src/Synercoding.FileFormats.Pdf/Extensions/IPageContentContextExtensions.cs +++ b/src/Synercoding.FileFormats.Pdf/Extensions/IPageContentContextExtensions.cs @@ -71,10 +71,10 @@ public static IPageContentContext AddImage(this IPageContentContext context, Ima }); } - public static IPageContentContext AddShapes(this IPageContentContext context, Action shapeOperations) + public static IPageContentContext AddShapes(this IPageContentContext context, Action shapeOperations) => context.AddShapes(shapeOperations, static (operations, context) => operations(context)); - public static Task AddShapesAsync(this IPageContentContext context, Func shapeOperations) + public static Task AddShapesAsync(this IPageContentContext context, Func shapeOperations) => context.AddShapesAsync(shapeOperations, static (operations, context) => operations(context)); public static IPageContentContext AddText(this IPageContentContext context, Action textOperations) @@ -104,7 +104,7 @@ public static IPageContentContext AddText(this IPageContentContext context, s var lines = StringHelper.SplitOnNewLines(text).ToArray(); // if no leading parameter is set, and the text spans multiple lines, set textleading to the font size. - if (lines.Length > 1 && context.TextLeading == default) + if (lines.Length > 1 && context.GraphicState.TextLeading == default) context.SetTextLeading(size); for (int i = 0; i < lines.Length; i++) diff --git a/src/Synercoding.FileFormats.Pdf/Extensions/IShapeContextExtensions.cs b/src/Synercoding.FileFormats.Pdf/Extensions/IShapeContextExtensions.cs index acaf827..3414a93 100644 --- a/src/Synercoding.FileFormats.Pdf/Extensions/IShapeContextExtensions.cs +++ b/src/Synercoding.FileFormats.Pdf/Extensions/IShapeContextExtensions.cs @@ -4,6 +4,6 @@ namespace Synercoding.FileFormats.Pdf.Extensions; public static class IShapeContextExtensions { - public static IShapeContext Rectangle(this IShapeContext context, Rectangle rectangle) + public static IShapeContentContext Rectangle(this IShapeContentContext context, Rectangle rectangle) => context.Rectangle(rectangle.LLX.AsRaw(Unit.Points), rectangle.LLY.AsRaw(Unit.Points), rectangle.Width.AsRaw(Unit.Points), rectangle.Height.AsRaw(Unit.Points)); } diff --git a/src/Synercoding.FileFormats.Pdf/GraphicState.cs b/src/Synercoding.FileFormats.Pdf/GraphicState.cs new file mode 100644 index 0000000..e751e32 --- /dev/null +++ b/src/Synercoding.FileFormats.Pdf/GraphicState.cs @@ -0,0 +1,70 @@ +using Synercoding.FileFormats.Pdf.LowLevel.Colors; +using Synercoding.FileFormats.Pdf.LowLevel.Graphics; +using Synercoding.FileFormats.Pdf.LowLevel.Text; +using System.Linq; + +namespace Synercoding.FileFormats.Pdf; + +public sealed class GraphicState +{ + internal GraphicState() + { + CTM = Matrix.Identity; + FillColor = PredefinedColors.Black; + StrokeColor = PredefinedColors.Black; + LineWidth = 1.0; + LineCap = LineCapStyle.ButtCap; + LineJoin = LineJoinStyle.MiterJoin; + MiterLimit = 10.0; + DashPattern = new Dash(); + CharacterSpacing = 0.0; + WordSpacing = 0.0; + HorizontalScaling = 100.0; + TextLeading = 0.0; + Font = null; + FontSize = null; + TextRenderingMode = TextRenderingMode.Fill; + TextRise = 0.0; + } + + public Matrix CTM { get; internal set; } + public Color FillColor { get; internal set; } + public Color StrokeColor { get; internal set; } + public double LineWidth { get; internal set; } + public LineCapStyle LineCap { get; internal set; } + public LineJoinStyle LineJoin { get; internal set; } + public double MiterLimit { get; internal set; } + public Dash DashPattern { get; internal set; } + public double CharacterSpacing { get; internal set; } + public double WordSpacing { get; internal set; } + public double HorizontalScaling { get; internal set; } + public double TextLeading { get; internal set; } + public Font? Font { get; internal set; } + public double? FontSize { get; internal set; } + public TextRenderingMode TextRenderingMode { get; internal set; } + public double TextRise { get; internal set; } + + internal GraphicState Clone() + { + return new GraphicState() + { + CTM = CTM, + FillColor = FillColor, + StrokeColor = StrokeColor, + LineWidth = LineWidth, + LineCap = LineCap, + LineJoin = LineJoin, + MiterLimit = MiterLimit, + DashPattern = new Dash(DashPattern.Array.ToArray(), DashPattern.Phase), + CharacterSpacing = CharacterSpacing, + WordSpacing = WordSpacing, + HorizontalScaling = HorizontalScaling, + TextLeading = TextLeading, + Font = Font, + FontSize = FontSize, + TextRenderingMode = TextRenderingMode, + TextRise = TextRise + }; + } +} + diff --git a/src/Synercoding.FileFormats.Pdf/IContentContext.cs b/src/Synercoding.FileFormats.Pdf/IContentContext.cs index f857f5c..c411068 100644 --- a/src/Synercoding.FileFormats.Pdf/IContentContext.cs +++ b/src/Synercoding.FileFormats.Pdf/IContentContext.cs @@ -11,19 +11,12 @@ public interface IContentContext { ContentStream RawContentStream { get; } + GraphicState GraphicState { get; } + TSelf WrapInState(T data, Action contentOperations); Task WrapInStateAsync(T data, Func contentOperations); - Matrix CTM { get; } - Color FillColor { get; } - Color StrokeColor { get; } - double LineWidth { get; } - LineCapStyle LineCap { get; } - LineJoinStyle LineJoin { get; } - double MiterLimit { get; } - Dash DashPattern { get; } - TSelf ConcatenateMatrix(Matrix matrix); TSelf SetStroke(Color stroke); diff --git a/src/Synercoding.FileFormats.Pdf/IPageContentContext.cs b/src/Synercoding.FileFormats.Pdf/IPageContentContext.cs index db52d63..65195f4 100644 --- a/src/Synercoding.FileFormats.Pdf/IPageContentContext.cs +++ b/src/Synercoding.FileFormats.Pdf/IPageContentContext.cs @@ -10,6 +10,6 @@ public interface IPageContentContext : IContentContext IPageContentContext AddText(T data, Action textOperations); Task AddTextAsync(T data, Func textOperations); - IPageContentContext AddShapes(T data, Action shapeOperations); - Task AddShapesAsync(T data, Func shapeOperations); + IPageContentContext AddShapes(T data, Action shapeOperations); + Task AddShapesAsync(T data, Func shapeOperations); } diff --git a/src/Synercoding.FileFormats.Pdf/IShapeContext.cs b/src/Synercoding.FileFormats.Pdf/IShapeContentContext.cs similarity index 53% rename from src/Synercoding.FileFormats.Pdf/IShapeContext.cs rename to src/Synercoding.FileFormats.Pdf/IShapeContentContext.cs index 1ac6853..e857ed8 100644 --- a/src/Synercoding.FileFormats.Pdf/IShapeContext.cs +++ b/src/Synercoding.FileFormats.Pdf/IShapeContentContext.cs @@ -2,7 +2,10 @@ namespace Synercoding.FileFormats.Pdf; -public interface IShapeContext : IContentContext +/// +/// Context to enable shape operations on the content +/// +public interface IShapeContentContext : IContentContext { /// /// Begin a new subpath by moving the current point to the coordinates (, ), @@ -10,8 +13,8 @@ public interface IShapeContext : IContentContext /// /// The X coordinate of the move /// The Y coordinate of the move - /// The calling to support chaining operations. - IShapeContext Move(double x, double y); + /// The calling to support chaining operations. + IShapeContentContext Move(double x, double y); /// @@ -19,8 +22,8 @@ public interface IShapeContext : IContentContext /// /// The X coordinate of the line end point /// The Y coordinate of the line end point - /// The calling to support chaining operations. - IShapeContext LineTo(double x, double y); + /// The calling to support chaining operations. + IShapeContentContext LineTo(double x, double y); /// /// Add a rectangle (re) operator to the content stream @@ -29,8 +32,8 @@ public interface IShapeContext : IContentContext /// The Y coordinate of the rectangle /// The width of the rectangle /// The height of the rectangle - /// The calling to support chaining operations. - IShapeContext Rectangle(double x, double y, double width, double height); + /// The calling to support chaining operations. + IShapeContentContext Rectangle(double x, double y, double width, double height); /// /// Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (, ), @@ -43,8 +46,8 @@ public interface IShapeContext : IContentContext /// The Y coordinate of the second control point /// The X coordinate of the endpoint of the curve /// The Y coordinate of the endpoint of the curve - /// The calling to support chaining operations. - IShapeContext CurveTo(double cpX1, double cpY1, double cpX2, double cpY2, double finalX, double finalY); + /// The calling to support chaining operations. + IShapeContentContext CurveTo(double cpX1, double cpY1, double cpX2, double cpY2, double finalX, double finalY); /// /// Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (, ), @@ -55,8 +58,8 @@ public interface IShapeContext : IContentContext /// The Y coordinate of the second control point /// The X coordinate of the endpoint of the curve /// The Y coordinate of the endpoint of the curve - /// The calling to support chaining operations. - IShapeContext CurveToWithStartAnker(double cpX2, double cpY2, double finalX, double finalY); + /// The calling to support chaining operations. + IShapeContentContext CurveToWithStartAnker(double cpX2, double cpY2, double finalX, double finalY); /// /// Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (, ), @@ -67,39 +70,59 @@ public interface IShapeContext : IContentContext /// The Y coordinate of the first control point /// The X coordinate of the endpoint of the curve /// The Y coordinate of the endpoint of the curve - /// The calling to support chaining operations. - IShapeContext CurveToWithEndAnker(double cpX1, double cpY1, double finalX, double finalY); + /// The calling to support chaining operations. + IShapeContentContext CurveToWithEndAnker(double cpX1, double cpY1, double finalX, double finalY); /// - /// Close the active subpath in this + /// Close the active subpath in this /// - IShapeContext CloseSubPath(); + /// The calling to support chaining operations. + IShapeContentContext CloseSubPath(); /// - /// + /// Mark the current path to be used as a clipping mask. /// - /// - /// - IShapeContext MarkPathForClipping(FillRule fillRule); + /// The to use + /// The calling to support chaining operations. + IShapeContentContext MarkPathForClipping(FillRule fillRule); - IShapeContext Stroke(); + /// + /// Stroke the current path + /// + /// The calling to support chaining operations. + IShapeContentContext Stroke(); /// /// Close and stroke the path. /// /// This operator has the same effect as the sequence () and then (). - /// - IShapeContext CloseSubPathAndStroke(); + /// The calling to support chaining operations. + IShapeContentContext CloseSubPathAndStroke(); - IShapeContext Fill(FillRule fillRule); + /// + /// Fill the current path using as the fill rule. + /// + /// The to use + /// The calling to support chaining operations. + IShapeContentContext Fill(FillRule fillRule); - IShapeContext FillThenStroke(FillRule fillRule); + /// + /// Fill the current path using as the fill rule, and then stroke the path. + /// + /// The to use + /// The calling to support chaining operations. + IShapeContentContext FillThenStroke(FillRule fillRule); - IShapeContext CloseSubPathFillStroke(FillRule fillRule); + /// + /// Close the current path, then fill the current path using as the fill rule, and then stroke the path. + /// + /// The to use + /// The calling to support chaining operations. + IShapeContentContext CloseSubPathFillStroke(FillRule fillRule); /// /// End the path object without filling or stroking it. This operator is a path-painting no-op, used primarily for the side effect of changing the current clipping path. /// - /// The calling to support chaining operations. - IShapeContext EndPathNoStrokeNoFill(); + /// The calling to support chaining operations. + IShapeContentContext EndPathNoStrokeNoFill(); } diff --git a/src/Synercoding.FileFormats.Pdf/ITextContentContext.cs b/src/Synercoding.FileFormats.Pdf/ITextContentContext.cs index 19d2efb..56bd7c8 100644 --- a/src/Synercoding.FileFormats.Pdf/ITextContentContext.cs +++ b/src/Synercoding.FileFormats.Pdf/ITextContentContext.cs @@ -1,18 +1,9 @@ -using Synercoding.FileFormats.Pdf.LowLevel.Text; +using Synercoding.FileFormats.Pdf.LowLevel.Text; namespace Synercoding.FileFormats.Pdf; public interface ITextContentContext : IContentContext { - double CharacterSpacing { get; } - double WordSpacing { get; } - double HorizontalScaling { get; } - double TextLeading { get; } - Font? Font { get; } - double? FontSize { get; } - TextRenderingMode TextRenderingMode { get; } - double TextRise { get; } - ITextContentContext SetCharacterSpacing(double spacing); ITextContentContext SetWordSpacing(double spacing); ITextContentContext SetHorizontalScaling(double scaling); diff --git a/src/Synercoding.FileFormats.Pdf/Image.cs b/src/Synercoding.FileFormats.Pdf/Image.cs index e765f4d..df84367 100644 --- a/src/Synercoding.FileFormats.Pdf/Image.cs +++ b/src/Synercoding.FileFormats.Pdf/Image.cs @@ -78,8 +78,14 @@ internal Image(PdfReference id, Stream jpgStream, int width, int height, PdfName /// public int Height { get; } + /// + /// The name of the colorspace used in this + /// public PdfName ColorSpace { get; } + /// + /// The decode array used in this + /// public double[] DecodeArray { get; } /// diff --git a/src/Synercoding.FileFormats.Pdf/Internals/PageContentContext.cs b/src/Synercoding.FileFormats.Pdf/Internals/PageContentContext.cs index 55ba135..e63a299 100644 --- a/src/Synercoding.FileFormats.Pdf/Internals/PageContentContext.cs +++ b/src/Synercoding.FileFormats.Pdf/Internals/PageContentContext.cs @@ -8,51 +8,15 @@ namespace Synercoding.FileFormats.Pdf.Internals; internal class PageContentContext : IPageContentContext { - private readonly PageContentContext? _parent; - - public PageContentContext(ContentStream contentStream) - : this(contentStream, null) - { } - - public PageContentContext(ContentStream contentStream, PageContentContext? parent) + public PageContentContext(ContentStream contentStream, GraphicState graphicState) { RawContentStream = contentStream; - _parent = parent; + GraphicState = graphicState; } public ContentStream RawContentStream { get; } - private Matrix? _ctm; - public Matrix CTM - => _ctm ?? _parent?.CTM ?? Matrix.Identity; - - private Color? _fill; - public Color FillColor - => _fill ?? _parent?.FillColor ?? PredefinedColors.Black; - - private Color? _stroke; - public Color StrokeColor - => _stroke ?? _parent?.StrokeColor ?? PredefinedColors.Black; - - private double? _lineWidth; - public double LineWidth - => _lineWidth ?? _parent?.LineWidth ?? 1.0; - - private LineCapStyle? _lineCap; - public LineCapStyle LineCap - => _lineCap ?? _parent?.LineCap ?? LineCapStyle.ButtCap; - - private LineJoinStyle? _lineJoin; - public LineJoinStyle LineJoin - => _lineJoin ?? _parent?.LineJoin ?? LineJoinStyle.MiterJoin; - - private double? _miterLimit; - public double MiterLimit - => _miterLimit ?? _parent?.MiterLimit ?? 10.0; - - private Dash? _dashPattern; - public Dash DashPattern - => _dashPattern ?? _parent?.DashPattern ?? new Dash(); + public GraphicState GraphicState { get; } public IPageContentContext AddImage(Image image) { @@ -67,7 +31,7 @@ public IPageContentContext AddText(T data, Action tex { RawContentStream.BeginText(); - var state = new TextContentContext(RawContentStream, this); + var state = new TextContentContext(RawContentStream, GraphicState); textOperations(data, state); RawContentStream.EndText(); @@ -79,7 +43,7 @@ public async Task AddTextAsync(T data, Func AddTextAsync(T data, Func(T data, Action contentOperations) { RawContentStream.SaveState(); - var state = new PageContentContext(RawContentStream, this); + var state = new PageContentContext(RawContentStream, GraphicState.Clone()); contentOperations(data, state); RawContentStream.RestoreState(); @@ -171,24 +135,24 @@ public IPageContentContext WrapInState(T data, Action public async Task WrapInStateAsync(T data, Func contentOperations) { RawContentStream.SaveState(); - var state = new PageContentContext(RawContentStream, this); + var state = new PageContentContext(RawContentStream, GraphicState.Clone()); await contentOperations(data, state); RawContentStream.RestoreState(); return this; } - public IPageContentContext AddShapes(T data, Action shapeOperations) + public IPageContentContext AddShapes(T data, Action shapeOperations) { - var state = new ShapesContext(RawContentStream, this); + var state = new ShapesContentContext(RawContentStream, GraphicState); shapeOperations(data, state); return this; } - public async Task AddShapesAsync(T data, Func shapeOperations) + public async Task AddShapesAsync(T data, Func shapeOperations) { - var state = new ShapesContext(RawContentStream, this); + var state = new ShapesContentContext(RawContentStream, GraphicState); await shapeOperations(data, state); return this; diff --git a/src/Synercoding.FileFormats.Pdf/Internals/ShapesContentContext.cs b/src/Synercoding.FileFormats.Pdf/Internals/ShapesContentContext.cs new file mode 100644 index 0000000..ef2b082 --- /dev/null +++ b/src/Synercoding.FileFormats.Pdf/Internals/ShapesContentContext.cs @@ -0,0 +1,209 @@ +using Synercoding.FileFormats.Pdf.LowLevel; +using Synercoding.FileFormats.Pdf.LowLevel.Colors; +using Synercoding.FileFormats.Pdf.LowLevel.Graphics; +using System; +using System.Threading.Tasks; + +namespace Synercoding.FileFormats.Pdf.Internals; + +internal class ShapesContentContext : IShapeContentContext +{ + public ShapesContentContext(ContentStream contentStream, GraphicState graphicState) + { + RawContentStream = contentStream; + GraphicState = graphicState; + } + + public ContentStream RawContentStream { get; } + + public GraphicState GraphicState { get; } + + public IShapeContentContext ConcatenateMatrix(Matrix matrix) + { + GraphicState.CTM = GraphicState.CTM.Multiply(matrix); + RawContentStream.CTM(matrix); + + return this; + } + + public IShapeContentContext SetDashPattern(Dash dashPattern) + { + GraphicState.DashPattern = dashPattern; + + RawContentStream.SetDashPattern(dashPattern); + + return this; + } + + public IShapeContentContext SetFill(Color fill) + { + GraphicState.FillColor = fill; + + RawContentStream.SetFillColor(fill); + + return this; + } + + public IShapeContentContext SetStroke(Color stroke) + { + GraphicState.StrokeColor = stroke; + + RawContentStream.SetStrokeColor(stroke); + + return this; + } + + public IShapeContentContext SetLineCap(LineCapStyle lineCap) + { + GraphicState.LineCap = lineCap; + + RawContentStream.SetLineCap(lineCap); + + return this; + } + + public IShapeContentContext SetLineJoin(LineJoinStyle lineJoin) + { + GraphicState.LineJoin = lineJoin; + + RawContentStream.SetLineJoin(lineJoin); + + return this; + } + + public IShapeContentContext SetLineWidth(double lineWidth) + { + GraphicState.LineWidth = lineWidth; + + RawContentStream.SetLineWidth(lineWidth); + + return this; + } + + public IShapeContentContext SetMiterLimit(double miterLimit) + { + GraphicState.MiterLimit = miterLimit; + + RawContentStream.SetMiterLimit(miterLimit); + + return this; + } + + public IShapeContentContext WrapInState(T data, Action contentOperations) + { + RawContentStream.SaveState(); + var wrappedContext = new ShapesContentContext(RawContentStream, GraphicState.Clone()); + contentOperations(data, wrappedContext); + RawContentStream.RestoreState(); + + return this; + } + + public async Task WrapInStateAsync(T data, Func contentOperations) + { + RawContentStream.SaveState(); + var wrappedContext = new ShapesContentContext(RawContentStream, GraphicState.Clone()); + await contentOperations(data, wrappedContext); + RawContentStream.RestoreState(); + + return this; + } + + public IShapeContentContext Move(double x, double y) + { + RawContentStream.MoveTo(x, y); + + return this; + } + + public IShapeContentContext LineTo(double x, double y) + { + RawContentStream.LineTo(x, y); + + return this; + } + + public IShapeContentContext Rectangle(double x, double y, double width, double height) + { + RawContentStream.Rectangle(x, y, width, height); + + return this; + } + + public IShapeContentContext CurveTo(double cpX1, double cpY1, double cpX2, double cpY2, double finalX, double finalY) + { + RawContentStream.CubicBezierCurve(cpX1, cpY1, cpX2, cpY2, finalX, finalY); + + return this; + } + + public IShapeContentContext CurveToWithStartAnker(double cpX2, double cpY2, double finalX, double finalY) + { + RawContentStream.CubicBezierCurveV(cpX2, cpY2, finalX, finalY); + + return this; + } + + public IShapeContentContext CurveToWithEndAnker(double cpX1, double cpY1, double finalX, double finalY) + { + RawContentStream.CubicBezierCurveY(cpX1, cpY1, finalX, finalY); + + return this; + } + + public IShapeContentContext CloseSubPath() + { + RawContentStream.Close(); + + return this; + } + + public IShapeContentContext MarkPathForClipping(FillRule fillRule) + { + RawContentStream.Clip(fillRule); + + return this; + } + + public IShapeContentContext Stroke() + { + RawContentStream.Stroke(); + + return this; + } + + public IShapeContentContext CloseSubPathAndStroke() + { + RawContentStream.CloseAndStroke(); + + return this; + } + + public IShapeContentContext Fill(FillRule fillRule) + { + RawContentStream.Fill(fillRule); + + return this; + } + + public IShapeContentContext FillThenStroke(FillRule fillRule) + { + RawContentStream.FillAndStroke(fillRule); + + return this; + } + + public IShapeContentContext CloseSubPathFillStroke(FillRule fillRule) + { + RawContentStream.CloseFillAndStroke(fillRule); + + return this; + } + + public IShapeContentContext EndPathNoStrokeNoFill() + { + RawContentStream.EndPath(); + + return this; + } +} diff --git a/src/Synercoding.FileFormats.Pdf/Internals/ShapesContext.cs b/src/Synercoding.FileFormats.Pdf/Internals/ShapesContext.cs deleted file mode 100644 index 0444dd3..0000000 --- a/src/Synercoding.FileFormats.Pdf/Internals/ShapesContext.cs +++ /dev/null @@ -1,241 +0,0 @@ -using Synercoding.FileFormats.Pdf.LowLevel; -using Synercoding.FileFormats.Pdf.LowLevel.Colors; -using Synercoding.FileFormats.Pdf.LowLevel.Graphics; -using System; -using System.Threading.Tasks; - -namespace Synercoding.FileFormats.Pdf.Internals; - -internal class ShapesContext : IShapeContext -{ - private readonly PageContentContext _parent; - - public ShapesContext(ContentStream contentStream, PageContentContext parent) - { - RawContentStream = contentStream; - _parent = parent; - } - - public ContentStream RawContentStream { get; } - - private Matrix? _ctm; - public Matrix CTM - => _ctm ?? _parent.CTM; - - private Color? _fill; - public Color FillColor - => _fill ?? _parent.FillColor; - - private Color? _stroke; - public Color StrokeColor - => _stroke ?? _parent.StrokeColor; - - private double? _lineWidth; - public double LineWidth - => _lineWidth ?? _parent.LineWidth; - - private LineCapStyle? _lineCap; - public LineCapStyle LineCap - => _lineCap ?? _parent.LineCap; - - private LineJoinStyle? _lineJoin; - public LineJoinStyle LineJoin - => _lineJoin ?? _parent.LineJoin; - - private double? _miterLimit; - public double MiterLimit - => _miterLimit ?? _parent.MiterLimit; - - private Dash? _dashPattern; - public Dash DashPattern - => _dashPattern ?? _parent.DashPattern; - - public IShapeContext ConcatenateMatrix(Matrix matrix) - { - _ctm = CTM.Multiply(matrix); - RawContentStream.CTM(matrix); - - return this; - } - - public IShapeContext SetDashPattern(Dash dashPattern) - { - _dashPattern = dashPattern; - - RawContentStream.SetDashPattern(dashPattern); - - return this; - } - - public IShapeContext SetFill(Color fill) - { - _fill = fill; - - RawContentStream.SetFillColor(fill); - - return this; - } - - public IShapeContext SetStroke(Color stroke) - { - _stroke = stroke; - - RawContentStream.SetStrokeColor(stroke); - - return this; - } - - public IShapeContext SetLineCap(LineCapStyle lineCap) - { - _lineCap = lineCap; - - RawContentStream.SetLineCap(lineCap); - - return this; - } - - public IShapeContext SetLineJoin(LineJoinStyle lineJoin) - { - _lineJoin = lineJoin; - - RawContentStream.SetLineJoin(lineJoin); - - return this; - } - - public IShapeContext SetLineWidth(double lineWidth) - { - _lineWidth = lineWidth; - - RawContentStream.SetLineWidth(lineWidth); - - return this; - } - - public IShapeContext SetMiterLimit(double miterLimit) - { - _miterLimit = miterLimit; - - RawContentStream.SetMiterLimit(miterLimit); - - return this; - } - - public IShapeContext WrapInState(T data, Action contentOperations) - { - RawContentStream.SaveState(); - var state = new WrappedShapesContext(RawContentStream, this); - contentOperations(data, state); - RawContentStream.RestoreState(); - - return this; - } - - public async Task WrapInStateAsync(T data, Func contentOperations) - { - RawContentStream.SaveState(); - var state = new WrappedShapesContext(RawContentStream, this); - await contentOperations(data, state); - RawContentStream.RestoreState(); - - return this; - } - - public IShapeContext Move(double x, double y) - { - RawContentStream.MoveTo(x, y); - - return this; - } - - public IShapeContext LineTo(double x, double y) - { - RawContentStream.LineTo(x, y); - - return this; - } - - public IShapeContext Rectangle(double x, double y, double width, double height) - { - RawContentStream.Rectangle(x, y, width, height); - - return this; - } - - public IShapeContext CurveTo(double cpX1, double cpY1, double cpX2, double cpY2, double finalX, double finalY) - { - RawContentStream.CubicBezierCurve(cpX1, cpY1, cpX2, cpY2, finalX, finalY); - - return this; - } - - public IShapeContext CurveToWithStartAnker(double cpX2, double cpY2, double finalX, double finalY) - { - RawContentStream.CubicBezierCurveV(cpX2, cpY2, finalX, finalY); - - return this; - } - - public IShapeContext CurveToWithEndAnker(double cpX1, double cpY1, double finalX, double finalY) - { - RawContentStream.CubicBezierCurveY(cpX1, cpY1, finalX, finalY); - - return this; - } - - public IShapeContext CloseSubPath() - { - RawContentStream.Close(); - - return this; - } - - public IShapeContext MarkPathForClipping(FillRule fillRule) - { - RawContentStream.Clip(fillRule); - - return this; - } - - public IShapeContext Stroke() - { - RawContentStream.Stroke(); - - return this; - } - - public IShapeContext CloseSubPathAndStroke() - { - RawContentStream.CloseAndStroke(); - - return this; - } - - public IShapeContext Fill(FillRule fillRule) - { - RawContentStream.Fill(fillRule); - - return this; - } - - public IShapeContext FillThenStroke(FillRule fillRule) - { - RawContentStream.FillAndStroke(fillRule); - - return this; - } - - public IShapeContext CloseSubPathFillStroke(FillRule fillRule) - { - RawContentStream.CloseFillAndStroke(fillRule); - - return this; - } - - public IShapeContext EndPathNoStrokeNoFill() - { - RawContentStream.EndPath(); - - return this; - } -} diff --git a/src/Synercoding.FileFormats.Pdf/Internals/TextContentContext.cs b/src/Synercoding.FileFormats.Pdf/Internals/TextContentContext.cs index bed7dba..3d50b87 100644 --- a/src/Synercoding.FileFormats.Pdf/Internals/TextContentContext.cs +++ b/src/Synercoding.FileFormats.Pdf/Internals/TextContentContext.cs @@ -9,79 +9,19 @@ namespace Synercoding.FileFormats.Pdf.Internals; internal class TextContentContext : ITextContentContext { - private readonly IPageContentContext _parent; - - public TextContentContext(ContentStream contentStream, IPageContentContext parent) + public TextContentContext(ContentStream contentStream, GraphicState graphicState) { RawContentStream = contentStream; - _parent = parent; + GraphicState = graphicState; } public ContentStream RawContentStream { get; } - private Matrix? _ctm; - public Matrix CTM - => _ctm ?? _parent?.CTM ?? Matrix.Identity; - - private Color? _fill; - public Color FillColor - => _fill ?? _parent?.FillColor ?? PredefinedColors.Black; - - private Color? _stroke; - public Color StrokeColor - => _stroke ?? _parent?.StrokeColor ?? PredefinedColors.Black; - - private double? _lineWidth; - public double LineWidth - => _lineWidth ?? _parent?.LineWidth ?? 1.0; - - private LineCapStyle? _lineCap; - public LineCapStyle LineCap - => _lineCap ?? _parent?.LineCap ?? LineCapStyle.ButtCap; - - private LineJoinStyle? _lineJoin; - public LineJoinStyle LineJoin - => _lineJoin ?? _parent?.LineJoin ?? LineJoinStyle.MiterJoin; - - private double? _miterLimit; - public double MiterLimit - => _miterLimit ?? _parent?.MiterLimit ?? 10.0; - - private Dash? _dashPattern; - public Dash DashPattern - => _dashPattern ?? _parent?.DashPattern ?? new Dash(); - - private double? _characterSpacing; - public double CharacterSpacing - => _characterSpacing ?? 0.0; - - private double? _wordSpacing; - public double WordSpacing - => _wordSpacing ?? 0.0; - - private double? _horizontalScale; - public double HorizontalScaling - => _horizontalScale ?? 100.0; - - private double? _textLeading; - public double TextLeading - => _textLeading ?? 0.0; - - public Font? Font { get; private set; } - - public double? FontSize { get; private set; } - - private TextRenderingMode? _textRenderingMode; - public TextRenderingMode TextRenderingMode - => _textRenderingMode ?? TextRenderingMode.Fill; - - private double? _textRise; - public double TextRise - => _textRise ?? 0.0; + public GraphicState GraphicState { get; } public ITextContentContext ConcatenateMatrix(Matrix matrix) { - _ctm = CTM.Multiply(matrix); + GraphicState.CTM = GraphicState.CTM.Multiply(matrix); RawContentStream.CTM(matrix); return this; @@ -89,7 +29,7 @@ public ITextContentContext ConcatenateMatrix(Matrix matrix) public ITextContentContext SetDashPattern(Dash dashPattern) { - _dashPattern = dashPattern; + GraphicState.DashPattern = dashPattern; RawContentStream.SetDashPattern(dashPattern); @@ -98,7 +38,7 @@ public ITextContentContext SetDashPattern(Dash dashPattern) public ITextContentContext SetFill(Color fill) { - _fill = fill; + GraphicState.FillColor = fill; RawContentStream.SetFillColor(fill); @@ -107,7 +47,7 @@ public ITextContentContext SetFill(Color fill) public ITextContentContext SetStroke(Color stroke) { - _stroke = stroke; + GraphicState.StrokeColor = stroke; RawContentStream.SetStrokeColor(stroke); @@ -116,7 +56,7 @@ public ITextContentContext SetStroke(Color stroke) public ITextContentContext SetLineCap(LineCapStyle lineCap) { - _lineCap = lineCap; + GraphicState.LineCap = lineCap; RawContentStream.SetLineCap(lineCap); @@ -125,7 +65,7 @@ public ITextContentContext SetLineCap(LineCapStyle lineCap) public ITextContentContext SetLineJoin(LineJoinStyle lineJoin) { - _lineJoin = lineJoin; + GraphicState.LineJoin = lineJoin; RawContentStream.SetLineJoin(lineJoin); @@ -134,7 +74,7 @@ public ITextContentContext SetLineJoin(LineJoinStyle lineJoin) public ITextContentContext SetLineWidth(double lineWidth) { - _lineWidth = lineWidth; + GraphicState.LineWidth = lineWidth; RawContentStream.SetLineWidth(lineWidth); @@ -143,7 +83,7 @@ public ITextContentContext SetLineWidth(double lineWidth) public ITextContentContext SetMiterLimit(double miterLimit) { - _miterLimit = miterLimit; + GraphicState.MiterLimit = miterLimit; RawContentStream.SetMiterLimit(miterLimit); @@ -153,8 +93,8 @@ public ITextContentContext SetMiterLimit(double miterLimit) public ITextContentContext WrapInState(T data, Action contentOperations) { RawContentStream.SaveState(); - var state = new WrappedTextContentContext(RawContentStream, this); - contentOperations(data, state); + var wrappedContext = new TextContentContext(RawContentStream, GraphicState.Clone()); + contentOperations(data, wrappedContext); RawContentStream.RestoreState(); return this; @@ -163,8 +103,8 @@ public ITextContentContext WrapInState(T data, Action public async Task WrapInStateAsync(T data, Func contentOperations) { RawContentStream.SaveState(); - var state = new WrappedTextContentContext(RawContentStream, this); - await contentOperations(data, state); + var wrappedContext = new TextContentContext(RawContentStream, GraphicState.Clone()); + await contentOperations(data, wrappedContext); RawContentStream.RestoreState(); return this; @@ -172,7 +112,7 @@ public async Task WrapInStateAsync(T data, Func throw new NotImplementedException($"Font of type {font?.GetType()} is currently not implemented.") }; - Font = font; - FontSize = size; + GraphicState.Font = font; + GraphicState.FontSize = size; RawContentStream.SetFontAndSize(fontName, size); return this; @@ -223,7 +163,7 @@ public ITextContentContext SetFontAndSize(Font font, double size) public ITextContentContext SetTextRenderingMode(TextRenderingMode mode) { - _textRenderingMode = mode; + GraphicState.TextRenderingMode = mode; RawContentStream.SetTextRenderMode(mode); @@ -232,7 +172,7 @@ public ITextContentContext SetTextRenderingMode(TextRenderingMode mode) public ITextContentContext SetTextRise(double rise) { - _textRise = rise; + GraphicState.TextRise = rise; RawContentStream.SetTextRise(rise); diff --git a/src/Synercoding.FileFormats.Pdf/Internals/WrappedShapesContext.cs b/src/Synercoding.FileFormats.Pdf/Internals/WrappedShapesContext.cs deleted file mode 100644 index 3d68d18..0000000 --- a/src/Synercoding.FileFormats.Pdf/Internals/WrappedShapesContext.cs +++ /dev/null @@ -1,241 +0,0 @@ -using Synercoding.FileFormats.Pdf.LowLevel; -using Synercoding.FileFormats.Pdf.LowLevel.Colors; -using Synercoding.FileFormats.Pdf.LowLevel.Graphics; -using System; -using System.Threading.Tasks; - -namespace Synercoding.FileFormats.Pdf.Internals; - -internal class WrappedShapesContext : IShapeContext -{ - private readonly IShapeContext _parent; - - public WrappedShapesContext(ContentStream contentStream, IShapeContext parent) - { - RawContentStream = contentStream; - _parent = parent; - } - - public ContentStream RawContentStream { get; } - - private Matrix? _ctm; - public Matrix CTM - => _ctm ?? _parent.CTM; - - private Color? _fill; - public Color FillColor - => _fill ?? _parent.FillColor; - - private Color? _stroke; - public Color StrokeColor - => _stroke ?? _parent.StrokeColor; - - private double? _lineWidth; - public double LineWidth - => _lineWidth ?? _parent.LineWidth; - - private LineCapStyle? _lineCap; - public LineCapStyle LineCap - => _lineCap ?? _parent.LineCap; - - private LineJoinStyle? _lineJoin; - public LineJoinStyle LineJoin - => _lineJoin ?? _parent.LineJoin; - - private double? _miterLimit; - public double MiterLimit - => _miterLimit ?? _parent.MiterLimit; - - private Dash? _dashPattern; - public Dash DashPattern - => _dashPattern ?? _parent.DashPattern; - - public IShapeContext ConcatenateMatrix(Matrix matrix) - { - _ctm = CTM.Multiply(matrix); - RawContentStream.CTM(matrix); - - return this; - } - - public IShapeContext SetDashPattern(Dash dashPattern) - { - _dashPattern = dashPattern; - - RawContentStream.SetDashPattern(dashPattern); - - return this; - } - - public IShapeContext SetFill(Color fill) - { - _fill = fill; - - RawContentStream.SetFillColor(fill); - - return this; - } - - public IShapeContext SetStroke(Color stroke) - { - _stroke = stroke; - - RawContentStream.SetStrokeColor(stroke); - - return this; - } - - public IShapeContext SetLineCap(LineCapStyle lineCap) - { - _lineCap = lineCap; - - RawContentStream.SetLineCap(lineCap); - - return this; - } - - public IShapeContext SetLineJoin(LineJoinStyle lineJoin) - { - _lineJoin = lineJoin; - - RawContentStream.SetLineJoin(lineJoin); - - return this; - } - - public IShapeContext SetLineWidth(double lineWidth) - { - _lineWidth = lineWidth; - - RawContentStream.SetLineWidth(lineWidth); - - return this; - } - - public IShapeContext SetMiterLimit(double miterLimit) - { - _miterLimit = miterLimit; - - RawContentStream.SetMiterLimit(miterLimit); - - return this; - } - - public IShapeContext WrapInState(T data, Action contentOperations) - { - RawContentStream.SaveState(); - var state = new WrappedShapesContext(RawContentStream, this); - contentOperations(data, state); - RawContentStream.RestoreState(); - - return this; - } - - public async Task WrapInStateAsync(T data, Func contentOperations) - { - RawContentStream.SaveState(); - var state = new WrappedShapesContext(RawContentStream, this); - await contentOperations(data, state); - RawContentStream.RestoreState(); - - return this; - } - - public IShapeContext Move(double x, double y) - { - RawContentStream.MoveTo(x, y); - - return this; - } - - public IShapeContext LineTo(double x, double y) - { - RawContentStream.LineTo(x, y); - - return this; - } - - public IShapeContext Rectangle(double x, double y, double width, double height) - { - RawContentStream.Rectangle(x, y, width, height); - - return this; - } - - public IShapeContext CurveTo(double cpX1, double cpY1, double cpX2, double cpY2, double finalX, double finalY) - { - RawContentStream.CubicBezierCurve(cpX1, cpY1, cpX2, cpY2, finalX, finalY); - - return this; - } - - public IShapeContext CurveToWithStartAnker(double cpX2, double cpY2, double finalX, double finalY) - { - RawContentStream.CubicBezierCurveV(cpX2, cpY2, finalX, finalY); - - return this; - } - - public IShapeContext CurveToWithEndAnker(double cpX1, double cpY1, double finalX, double finalY) - { - RawContentStream.CubicBezierCurveY(cpX1, cpY1, finalX, finalY); - - return this; - } - - public IShapeContext CloseSubPath() - { - RawContentStream.Close(); - - return this; - } - - public IShapeContext MarkPathForClipping(FillRule fillRule) - { - RawContentStream.Clip(fillRule); - - return this; - } - - public IShapeContext Stroke() - { - RawContentStream.Stroke(); - - return this; - } - - public IShapeContext CloseSubPathAndStroke() - { - RawContentStream.CloseAndStroke(); - - return this; - } - - public IShapeContext Fill(FillRule fillRule) - { - RawContentStream.Fill(fillRule); - - return this; - } - - public IShapeContext FillThenStroke(FillRule fillRule) - { - RawContentStream.FillAndStroke(fillRule); - - return this; - } - - public IShapeContext CloseSubPathFillStroke(FillRule fillRule) - { - RawContentStream.CloseFillAndStroke(fillRule); - - return this; - } - - public IShapeContext EndPathNoStrokeNoFill() - { - RawContentStream.EndPath(); - - return this; - } -} diff --git a/src/Synercoding.FileFormats.Pdf/Internals/WrappedTextContentContext.cs b/src/Synercoding.FileFormats.Pdf/Internals/WrappedTextContentContext.cs deleted file mode 100644 index 81b9a7d..0000000 --- a/src/Synercoding.FileFormats.Pdf/Internals/WrappedTextContentContext.cs +++ /dev/null @@ -1,294 +0,0 @@ -using Synercoding.FileFormats.Pdf.LowLevel; -using Synercoding.FileFormats.Pdf.LowLevel.Colors; -using Synercoding.FileFormats.Pdf.LowLevel.Graphics; -using Synercoding.FileFormats.Pdf.LowLevel.Text; -using System; -using System.Threading.Tasks; - -namespace Synercoding.FileFormats.Pdf.Internals; - -internal class WrappedTextContentContext : ITextContentContext -{ - private readonly ITextContentContext _parent; - - public WrappedTextContentContext(ContentStream contentStream, ITextContentContext parent) - { - RawContentStream = contentStream; - _parent = parent; - } - - public ContentStream RawContentStream { get; } - - private Matrix? _ctm; - public Matrix CTM - => _ctm ?? _parent?.CTM ?? Matrix.Identity; - - private Color? _fill; - public Color FillColor - => _fill ?? _parent?.FillColor ?? PredefinedColors.Black; - - private Color? _stroke; - public Color StrokeColor - => _stroke ?? _parent?.StrokeColor ?? PredefinedColors.Black; - - private double? _lineWidth; - public double LineWidth - => _lineWidth ?? _parent?.LineWidth ?? 1.0; - - private LineCapStyle? _lineCap; - public LineCapStyle LineCap - => _lineCap ?? _parent?.LineCap ?? LineCapStyle.ButtCap; - - private LineJoinStyle? _lineJoin; - public LineJoinStyle LineJoin - => _lineJoin ?? _parent?.LineJoin ?? LineJoinStyle.MiterJoin; - - private double? _miterLimit; - public double MiterLimit - => _miterLimit ?? _parent?.MiterLimit ?? 10.0; - - private Dash? _dashPattern; - public Dash DashPattern - => _dashPattern ?? _parent?.DashPattern ?? new Dash(); - - private double? _characterSpacing; - public double CharacterSpacing - => _characterSpacing ?? _parent.CharacterSpacing; - - private double? _wordSpacing; - public double WordSpacing - => _wordSpacing ?? _parent.WordSpacing; - - private double? _horizontalScale; - public double HorizontalScaling - => _horizontalScale ?? _parent.HorizontalScaling; - - private double? _textLeading; - public double TextLeading - => _textLeading ?? _parent.TextLeading; - - private Font? _font; - public Font? Font - => _font ?? _parent.Font; - - private double? _fontSize; - public double? FontSize - => _fontSize ?? _parent.FontSize; - - private TextRenderingMode? _textRenderingMode; - public TextRenderingMode TextRenderingMode - => _textRenderingMode ?? _parent.TextRenderingMode; - - private double? _textRise; - public double TextRise - => _textRise ?? _parent.TextRise; - - public ITextContentContext ConcatenateMatrix(Matrix matrix) - { - _ctm = CTM.Multiply(matrix); - RawContentStream.CTM(matrix); - - return this; - } - - public ITextContentContext SetDashPattern(Dash dashPattern) - { - _dashPattern = dashPattern; - - RawContentStream.SetDashPattern(dashPattern); - - return this; - } - - public ITextContentContext SetFill(Color fill) - { - _fill = fill; - - RawContentStream.SetFillColor(fill); - - return this; - } - - public ITextContentContext SetStroke(Color stroke) - { - _stroke = stroke; - - RawContentStream.SetStrokeColor(stroke); - - return this; - } - - public ITextContentContext SetLineCap(LineCapStyle lineCap) - { - _lineCap = lineCap; - - RawContentStream.SetLineCap(lineCap); - - return this; - } - - public ITextContentContext SetLineJoin(LineJoinStyle lineJoin) - { - _lineJoin = lineJoin; - - RawContentStream.SetLineJoin(lineJoin); - - return this; - } - - public ITextContentContext SetLineWidth(double lineWidth) - { - _lineWidth = lineWidth; - - RawContentStream.SetLineWidth(lineWidth); - - return this; - } - - public ITextContentContext SetMiterLimit(double miterLimit) - { - _miterLimit = miterLimit; - - RawContentStream.SetMiterLimit(miterLimit); - - return this; - } - - public ITextContentContext WrapInState(T data, Action contentOperations) - { - RawContentStream.SaveState(); - var state = new WrappedTextContentContext(RawContentStream, this); - contentOperations(data, state); - RawContentStream.RestoreState(); - - return this; - } - - public async Task WrapInStateAsync(T data, Func contentOperations) - { - RawContentStream.SaveState(); - var state = new WrappedTextContentContext(RawContentStream, this); - await contentOperations(data, state); - RawContentStream.RestoreState(); - - return this; - } - - public ITextContentContext SetCharacterSpacing(double spacing) - { - _characterSpacing = spacing; - - RawContentStream.SetCharacterSpacing(spacing); - - return this; - } - - public ITextContentContext SetWordSpacing(double spacing) - { - _wordSpacing = spacing; - - RawContentStream.SetWordSpacing(spacing); - - return this; - } - - public ITextContentContext SetHorizontalScaling(double scaling) - { - _horizontalScale = scaling; - - RawContentStream.SetHorizontalScaling(scaling); - - return this; - } - - public ITextContentContext SetTextLeading(double leading) - { - _textLeading = leading; - - RawContentStream.SetTextLeading(leading); - - return this; - } - - public ITextContentContext SetFontAndSize(Font font, double size) - { - var fontName = font switch - { - Type1StandardFont stdFont => RawContentStream.Resources.AddStandardFont(stdFont), - _ => throw new NotImplementedException($"Font of type {font?.GetType()} is currently not implemented.") - }; - - _font = font; - _fontSize = size; - RawContentStream.SetFontAndSize(fontName, size); - - return this; - } - - public ITextContentContext SetTextRenderingMode(TextRenderingMode mode) - { - _textRenderingMode = mode; - - RawContentStream.SetTextRenderMode(mode); - - return this; - } - - public ITextContentContext SetTextRise(double rise) - { - _textRise = rise; - - RawContentStream.SetTextRise(rise); - - return this; - } - - public ITextContentContext MoveToStartNewLine() - { - RawContentStream.MoveNextLine(); - - return this; - } - - public ITextContentContext MoveToStartNextLine(double offsetX, double offsetY) - { - RawContentStream.MoveNextLineAndOffsetTextPosition(offsetX, offsetY); - - return this; - } - - public ITextContentContext MoveToStartNextLineAndSetLeading(double offsetX, double offsetY) - { - RawContentStream.MoveNextLineSetLeadingAndOffsetTextPosition(offsetX, offsetY); - - return this; - } - - public ITextContentContext ReplaceTextMatrix(Matrix matrix) - { - RawContentStream.Tm(matrix); - - return this; - } - - public ITextContentContext ShowText(string text) - { - RawContentStream.ShowTextTj(text); - - return this; - } - - public ITextContentContext ShowTextOnNextLine(string text) - { - RawContentStream.MoveNextLineShowText(text); - - return this; - } - - public ITextContentContext ShowTextOnNextLine(string text, double wordSpacing, double characterSpacing) - { - RawContentStream.MoveNextLineShowText(text, wordSpacing, characterSpacing); - - return this; - } -} diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamExtensions.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamExtensions.cs index 8819fd7..f833746 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamExtensions.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Extensions/PdfStreamExtensions.cs @@ -103,6 +103,12 @@ public static PdfStream Write(this PdfStream stream, Rectangle rectangle) return stream.Write(rectangle.LLX.Raw, rectangle.LLY.Raw, rectangle.URX.Raw, rectangle.URY.Raw); } + /// + /// Write a to the + /// + /// The to write to. + /// The to write. + /// The to support chaining operations. public static PdfStream Write(this PdfStream stream, DateTimeOffset dateTimeOffset) { stream @@ -168,6 +174,12 @@ public static PdfStream Write(this PdfStream stream, DateTimeOffset dateTimeOffs return stream; } + /// + /// Write a text to the stream as a hexadecimal string + /// + /// The stream to write to + /// The string to write + /// The to support chaining operations. public static PdfStream WriteHexadecimalString(this PdfStream stream, string value) { var bytes = System.Text.Encoding.ASCII.GetBytes(value); diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/LineJoinStyle.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/LineJoinStyle.cs index 104a503..f2eb82b 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/LineJoinStyle.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/Graphics/LineJoinStyle.cs @@ -9,7 +9,7 @@ public enum LineJoinStyle /// The outer edges of the strokes for the two segments shall be extended until they meet at an angle. /// /// - /// If the segments meet at too sharp an angle (see ), a bevel join shall be used instead. + /// If the segments meet at too sharp an angle (see ), a bevel join shall be used instead. /// MiterJoin = 0, /// diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/PdfDictionary.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/PdfDictionary.cs index 162ea72..04690b6 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/PdfDictionary.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/PdfDictionary.cs @@ -196,6 +196,12 @@ public PdfDictionary WriteHexadecimalString(PdfName key, string value) return this; } + /// + /// Write a date to the dictionary + /// + /// The key of the item in the dictionary + /// The date to write + /// The to support chaining operations. public PdfDictionary Write(PdfName key, DateTimeOffset value) { _stream @@ -316,6 +322,12 @@ public PdfDictionary WriteIfNotNull(PdfName key, double? value) ? Write(key, value.Value) : this; + /// + /// Write a date to the dictionary if it is not null + /// + /// The key of the item in the dictionary + /// The date to write + /// The to support chaining operations. public PdfDictionary WriteIfNotNull(PdfName key, DateTimeOffset? value) => value.HasValue ? Write(key, value.Value) diff --git a/src/Synercoding.FileFormats.Pdf/PdfPage.cs b/src/Synercoding.FileFormats.Pdf/PdfPage.cs index bad939d..5d7d156 100644 --- a/src/Synercoding.FileFormats.Pdf/PdfPage.cs +++ b/src/Synercoding.FileFormats.Pdf/PdfPage.cs @@ -29,7 +29,7 @@ internal PdfPage(TableBuilder tableBuilder, PageTree parent) Resources = new PageResources(_tableBuilder); var contentStream = new ContentStream(tableBuilder.ReserveId(), Resources); - Content = new PageContentContext(contentStream); + Content = new PageContentContext(contentStream, new GraphicState()); } internal PdfReference Parent @@ -42,6 +42,9 @@ internal PdfReference Parent /// public int PageNumber { get; } + /// + /// The to add content to the page. + /// public IPageContentContext Content { get; } /// From 6f5d96cb23f90c7d064f632a86cae39c070be553 Mon Sep 17 00:00:00 2001 From: Gerard Gunnewijk Date: Thu, 26 Jan 2023 14:31:47 +0100 Subject: [PATCH 07/11] More XML comments --- .../IPageContentContextExtensions.cs | 128 ++++++++++++++++++ .../Extensions/IShapeContextExtensions.cs | 10 ++ .../GraphicState.cs | 92 +++++++++++-- .../IContentContext.cs | 70 ++++++++++ .../IPageContentContext.cs | 39 ++++++ .../ITextContentContext.cs | 88 ++++++++++++ .../Internals/PageContentContext.cs | 4 +- .../Internals/ShapesContentContext.cs | 4 +- .../Internals/TextContentContext.cs | 4 +- .../LowLevel/ContentStream.cs | 46 +++++++ 10 files changed, 467 insertions(+), 18 deletions(-) diff --git a/src/Synercoding.FileFormats.Pdf/Extensions/IPageContentContextExtensions.cs b/src/Synercoding.FileFormats.Pdf/Extensions/IPageContentContextExtensions.cs index 08766ea..e697d25 100644 --- a/src/Synercoding.FileFormats.Pdf/Extensions/IPageContentContextExtensions.cs +++ b/src/Synercoding.FileFormats.Pdf/Extensions/IPageContentContextExtensions.cs @@ -8,8 +8,21 @@ using System.Threading.Tasks; namespace Synercoding.FileFormats.Pdf.Extensions; + +/// +/// Extension methods for +/// public static class IPageContentContextExtensions { + /// + /// Add a JPG stream directly to the page + /// + /// The context to add the image to. + /// The stream containing a JPG image + /// The width of the JPG in the . + /// The height of the JPG in the . + /// The colorspace of the JPG in the . + /// The same to enable chaining operations. public static IPageContentContext AddJpgUnsafe(this IPageContentContext context, System.IO.Stream jpgStream, int originalWidth, int originalHeight, ColorSpace colorSpace) { var name = context.RawContentStream.Resources.AddJpgUnsafe(jpgStream, originalWidth, originalHeight, colorSpace); @@ -19,6 +32,13 @@ public static IPageContentContext AddJpgUnsafe(this IPageContentContext context, return context; } + /// + /// Add an image to the page + /// + /// The context to add the image to. + /// The stream containing the image + /// The placement matrix to use + /// The same to enable chaining operations. public static IPageContentContext AddImage(this IPageContentContext context, System.IO.Stream stream, Matrix matrix) { return context.WrapInState((stream, matrix), static (tuple, context) => @@ -28,9 +48,22 @@ public static IPageContentContext AddImage(this IPageContentContext context, Sys }); } + /// + /// Add an image to the page + /// + /// The context to add the image to. + /// The stream containing the image + /// The rectangle of where to place the image. + /// The same to enable chaining operations. public static IPageContentContext AddImage(this IPageContentContext context, System.IO.Stream stream, Rectangle rectangle) => context.AddImage(stream, rectangle.AsPlacementMatrix()); + /// + /// Add an image to the page + /// + /// The context to add the image to. + /// The stream containing the image + /// The same to enable chaining operations. public static IPageContentContext AddImage(this IPageContentContext context, System.IO.Stream stream) { using var image = SixLabors.ImageSharp.Image.Load(stream); @@ -38,6 +71,13 @@ public static IPageContentContext AddImage(this IPageContentContext context, Sys return context.AddImage(image); } + /// + /// Add an image to the page + /// + /// The context to add the image to. + /// The image to place + /// The placement matrix to use + /// The same to enable chaining operations. public static IPageContentContext AddImage(this IPageContentContext context, SixLabors.ImageSharp.Image image, Matrix matrix) { return context.WrapInState((image, matrix), static (tuple, context) => @@ -47,9 +87,22 @@ public static IPageContentContext AddImage(this IPageContentContext context, Six }); } + /// + /// Add an image to the page + /// + /// The context to add the image to. + /// The image to place + /// The rectangle of where to place the image. + /// The same to enable chaining operations. public static IPageContentContext AddImage(this IPageContentContext context, SixLabors.ImageSharp.Image image, Rectangle rectangle) => context.AddImage(image, rectangle.AsPlacementMatrix()); + /// + /// Add an image to the page + /// + /// The context to add the image to. + /// The image to place + /// The same to enable chaining operations. public static IPageContentContext AddImage(this IPageContentContext context, SixLabors.ImageSharp.Image image) { var name = context.RawContentStream.Resources.AddImage(image); @@ -59,9 +112,23 @@ public static IPageContentContext AddImage(this IPageContentContext context, Six return context; } + /// + /// Add an image to the page + /// + /// The context to add the image to. + /// The image to place + /// The rectangle of where to place the image. + /// The same to enable chaining operations. public static IPageContentContext AddImage(this IPageContentContext context, Image image, Rectangle rectangle) => context.AddImage(image, rectangle.AsPlacementMatrix()); + /// + /// Add an image to the page + /// + /// The context to add the image to. + /// The image to place + /// The placement matrix to use + /// The same to enable chaining operations. public static IPageContentContext AddImage(this IPageContentContext context, Image image, Matrix matrix) { return context.WrapInState((image, matrix), static (tuple, context) => @@ -71,27 +138,88 @@ public static IPageContentContext AddImage(this IPageContentContext context, Ima }); } + /// + /// Start a shape operation on the page + /// + /// The context to add the shape to. + /// The shape operations to execute + /// The same to enable chaining operations. public static IPageContentContext AddShapes(this IPageContentContext context, Action shapeOperations) => context.AddShapes(shapeOperations, static (operations, context) => operations(context)); + /// + /// Start a shape operation on the page + /// + /// The context to add the shape to. + /// The shape operations to execute + /// A task that resolves to to enable chaining operations public static Task AddShapesAsync(this IPageContentContext context, Func shapeOperations) => context.AddShapesAsync(shapeOperations, static (operations, context) => operations(context)); + /// + /// Start a text operation on the page + /// + /// The context to add the text to. + /// The text operations to execute + /// The same to enable chaining operations. public static IPageContentContext AddText(this IPageContentContext context, Action textOperations) => context.AddText(textOperations, static (operations, context) => operations(context)); + /// + /// Start a text operation on the page + /// + /// The context to add the text to. + /// The text operations to execute + /// A task that resolves to to enable chaining operations public static Task AddTextAsync(this IPageContentContext context, Func textOperations) => context.AddTextAsync(textOperations, static (operations, context) => operations(context)); + /// + /// Show text on the page using the parameters provided + /// + /// The context to add the text to. + /// The text to show + /// The font to use + /// The font size to use + /// The same to enable chaining operations. public static IPageContentContext AddText(this IPageContentContext context, string text, Font font, double size) => context.AddText(text, font, size, ops => { }); + /// + /// Show text on the page using the parameters provided + /// + /// The context to add the text to. + /// The text to show + /// The font to use + /// The font size to use + /// The location to place the text + /// The same to enable chaining operations. public static IPageContentContext AddText(this IPageContentContext context, string text, Font font, double size, Point location) => context.AddText(text, font, size, location, static (location, ops) => ops.MoveToStartNextLine(location.X.AsRaw(Unit.Points), location.Y.AsRaw(Unit.Points))); + /// + /// Show text on the page using the parameters provided + /// + /// The context to add the text to. + /// The text to show + /// The font to use + /// The font size to use + /// The extra text operations to execute + /// The same to enable chaining operations. public static IPageContentContext AddText(this IPageContentContext context, string text, Font font, double size, Action extraOperations) => context.AddText(text, font, size, extraOperations, static (extraOperations, context) => extraOperations(context)); + /// + /// Show text on the page using the parameters provided + /// + /// The type of the data to provide to the + /// The context to add the text to. + /// The text to show + /// The font to use + /// The font size to use + /// the data to provide to + /// The extra text operations to execute + /// The same to enable chaining operations. public static IPageContentContext AddText(this IPageContentContext context, string text, Font font, double size, T data, Action extraOperations) { return context.AddText((text, font, size, data, extraOperations), static (quintuple, context) => diff --git a/src/Synercoding.FileFormats.Pdf/Extensions/IShapeContextExtensions.cs b/src/Synercoding.FileFormats.Pdf/Extensions/IShapeContextExtensions.cs index 3414a93..e67d85f 100644 --- a/src/Synercoding.FileFormats.Pdf/Extensions/IShapeContextExtensions.cs +++ b/src/Synercoding.FileFormats.Pdf/Extensions/IShapeContextExtensions.cs @@ -2,8 +2,18 @@ using Synercoding.Primitives.Extensions; namespace Synercoding.FileFormats.Pdf.Extensions; + +/// +/// Extensions for +/// public static class IShapeContextExtensions { + /// + /// Add a rectangle to the current path + /// + /// The context to execute this path on. + /// The to add. + /// The same to enable chaining operations. public static IShapeContentContext Rectangle(this IShapeContentContext context, Rectangle rectangle) => context.Rectangle(rectangle.LLX.AsRaw(Unit.Points), rectangle.LLY.AsRaw(Unit.Points), rectangle.Width.AsRaw(Unit.Points), rectangle.Height.AsRaw(Unit.Points)); } diff --git a/src/Synercoding.FileFormats.Pdf/GraphicState.cs b/src/Synercoding.FileFormats.Pdf/GraphicState.cs index e751e32..a633906 100644 --- a/src/Synercoding.FileFormats.Pdf/GraphicState.cs +++ b/src/Synercoding.FileFormats.Pdf/GraphicState.cs @@ -5,13 +5,16 @@ namespace Synercoding.FileFormats.Pdf; +/// +/// Class representing the grahpic state of a PDF at a certain moment in time. +/// public sealed class GraphicState { internal GraphicState() { CTM = Matrix.Identity; - FillColor = PredefinedColors.Black; - StrokeColor = PredefinedColors.Black; + Fill = PredefinedColors.Black; + Stroke = PredefinedColors.Black; LineWidth = 1.0; LineCap = LineCapStyle.ButtCap; LineJoin = LineJoinStyle.MiterJoin; @@ -27,21 +30,86 @@ internal GraphicState() TextRise = 0.0; } - public Matrix CTM { get; internal set; } - public Color FillColor { get; internal set; } - public Color StrokeColor { get; internal set; } - public double LineWidth { get; internal set; } - public LineCapStyle LineCap { get; internal set; } + /// + /// The current transformation matrix, which maps positions from user coordinates to device coordinates. + /// This matrix is modified by each application of the coordinate transformation operator, cm + /// + public Matrix CTM { get; internal set; } + + /// + /// The color used for filling operations + /// + public Color Fill { get; internal set; } + + /// + /// The color used for stroking operations + /// + public Color Stroke { get; internal set; } + + /// + /// The thickness, in user space units, of paths to be stroked. + /// + public double LineWidth { get; internal set; } + + /// + /// A code specifying the shape of the endpoints for any open path that is stroked. + /// + public LineCapStyle LineCap { get; internal set; } + + /// + /// A code specifying the shape of joints between connected segments of a stroked path. + /// public LineJoinStyle LineJoin { get; internal set; } + + /// + /// The maximum length of mitered line joins for stroked paths. + /// This parameter limits the length of “spikes” produced when line segments join at sharp angles. + /// public double MiterLimit { get; internal set; } - public Dash DashPattern { get; internal set; } - public double CharacterSpacing { get; internal set; } - public double WordSpacing { get; internal set; } + + /// + /// A description of the dash pattern to be used when paths are stroked. + /// + public Dash DashPattern { get; internal set; } + + /// + /// The spacing between characters + /// + public double CharacterSpacing { get; internal set; } + + /// + /// The spacing between words + /// + public double WordSpacing { get; internal set; } + + /// + /// The horizontal scaling is a number specifying the percentage of the normal width. + /// public double HorizontalScaling { get; internal set; } + + /// + /// The text leading is a number expressed in unscaled text space units. + /// public double TextLeading { get; internal set; } + + /// + /// The font used when placing text on the page + /// public Font? Font { get; internal set; } + + /// + /// The font size used when placing text on the page + /// public double? FontSize { get; internal set; } + + /// + /// The text rendering mode, determines whether showing text causes glyph outlines to be stroked, filled, used as a clipping boundary, or some combination of the three. + /// public TextRenderingMode TextRenderingMode { get; internal set; } + + /// + /// Text rise, specifies the distance, in unscaled text space units, to move the baseline up or down from its default location. + /// public double TextRise { get; internal set; } internal GraphicState Clone() @@ -49,8 +117,8 @@ internal GraphicState Clone() return new GraphicState() { CTM = CTM, - FillColor = FillColor, - StrokeColor = StrokeColor, + Fill = Fill, + Stroke = Stroke, LineWidth = LineWidth, LineCap = LineCap, LineJoin = LineJoin, diff --git a/src/Synercoding.FileFormats.Pdf/IContentContext.cs b/src/Synercoding.FileFormats.Pdf/IContentContext.cs index c411068..afd6952 100644 --- a/src/Synercoding.FileFormats.Pdf/IContentContext.cs +++ b/src/Synercoding.FileFormats.Pdf/IContentContext.cs @@ -6,25 +6,95 @@ namespace Synercoding.FileFormats.Pdf; +/// +/// Interface to write content to the underlying +/// +/// Type should be the implementing type. public interface IContentContext where TSelf : IContentContext { + /// + /// Provides access to the raw underlying content stream + /// ContentStream RawContentStream { get; } + /// + /// Represents the current graphic state + /// GraphicState GraphicState { get; } + /// + /// Wrap the in save and restore state operators + /// + /// The type of data to pass to + /// Data to pass to + /// The operations to execute in the wrapped state + /// This to enable chaining operations TSelf WrapInState(T data, Action contentOperations); + /// + /// Wrap the in save and restore state operators + /// + /// The type of data to pass to + /// Data to pass to + /// The operations to execute in the wrapped state + /// A task that resolves to this to enable chaining operations Task WrapInStateAsync(T data, Func contentOperations); + /// + /// Concatenate a matrix to + /// + /// The matrix to concat + /// This to enable chaining operations TSelf ConcatenateMatrix(Matrix matrix); + /// + /// The the used for stroking operations. + /// + /// The color to be used for stroking operations. + /// This to enable chaining operations TSelf SetStroke(Color stroke); + + /// + /// The the used for filling operations. + /// + /// The color to be used for filling operations. + /// This to enable chaining operations TSelf SetFill(Color fill); + + /// + /// Set the line width + /// + /// The line width to set + /// This to enable chaining operations TSelf SetLineWidth(double lineWidth); + + /// + /// Set the line cap + /// + /// The line cap style to set + /// This to enable chaining operations TSelf SetLineCap(LineCapStyle lineCap); + + /// + /// Set the line join + /// + /// The line join style to set + /// This to enable chaining operations TSelf SetLineJoin(LineJoinStyle lineJoin); + + /// + /// Set the miter limit + /// + /// The miter limit to set + /// This to enable chaining operations TSelf SetMiterLimit(double miterLimit); + + /// + /// Set the dash pattern + /// + /// The dash pattern to set + /// This to enable chaining operations TSelf SetDashPattern(Dash dashPattern); } diff --git a/src/Synercoding.FileFormats.Pdf/IPageContentContext.cs b/src/Synercoding.FileFormats.Pdf/IPageContentContext.cs index 65195f4..c214368 100644 --- a/src/Synercoding.FileFormats.Pdf/IPageContentContext.cs +++ b/src/Synercoding.FileFormats.Pdf/IPageContentContext.cs @@ -1,15 +1,54 @@ +using Synercoding.FileFormats.Pdf.LowLevel; using System; using System.Threading.Tasks; namespace Synercoding.FileFormats.Pdf; +/// +/// Interface to write content to the underlying +/// public interface IPageContentContext : IContentContext { + /// + /// Add an to the page + /// + /// The image to paint + /// This to enable chaining operations IPageContentContext AddImage(Image image); + /// + /// Add text to the page + /// + /// The type of the data to use in the + /// Data to use in the . + /// The text operations to execute. + /// This to enable chaining operations IPageContentContext AddText(T data, Action textOperations); + + /// + /// Add text to the page + /// + /// The type of the data to use in the + /// Data to use in the . + /// The text operations to execute. + /// A task that resolves to this to enable chaining operations Task AddTextAsync(T data, Func textOperations); + /// + /// Add shapes to the page + /// + /// The type of the data to use in the + /// Data to use in the . + /// The shape operations to execute + /// This to enable chaining operations IPageContentContext AddShapes(T data, Action shapeOperations); + + /// + /// Add shapes to the page + /// + /// The type of the data to use in the + /// Data to use in the . + /// The shape operations to execute + /// A task that resolves to this to enable chaining operations Task AddShapesAsync(T data, Func shapeOperations); } diff --git a/src/Synercoding.FileFormats.Pdf/ITextContentContext.cs b/src/Synercoding.FileFormats.Pdf/ITextContentContext.cs index 56bd7c8..0fde58e 100644 --- a/src/Synercoding.FileFormats.Pdf/ITextContentContext.cs +++ b/src/Synercoding.FileFormats.Pdf/ITextContentContext.cs @@ -2,22 +2,110 @@ namespace Synercoding.FileFormats.Pdf; +/// +/// Context to enable text operations on the content +/// public interface ITextContentContext : IContentContext { + /// + /// Set the character spacing + /// + /// The spacing value + /// This to enable chaining operations ITextContentContext SetCharacterSpacing(double spacing); + + /// + /// Set the word spacing + /// + /// The spacing value + /// This to enable chaining operations ITextContentContext SetWordSpacing(double spacing); + + /// + /// Set the horizontal scaling + /// + /// The scaling value + /// This to enable chaining operations ITextContentContext SetHorizontalScaling(double scaling); + + /// + /// Set the text leading + /// + /// The leading value + /// This to enable chaining operations ITextContentContext SetTextLeading(double leading); + + /// + /// Set the font and size + /// + /// The font to set + /// The size to set + /// This to enable chaining operations ITextContentContext SetFontAndSize(Font font, double size); + + /// + /// Set the text rendering mode + /// + /// The mode to set + /// This to enable chaining operations ITextContentContext SetTextRenderingMode(TextRenderingMode mode); + + /// + /// Set the text rise + /// + /// The text rise to set + /// This to enable chaining operations ITextContentContext SetTextRise(double textRise); + /// + /// Move to the start of a new line + /// + /// This to enable chaining operations ITextContentContext MoveToStartNewLine(); + + /// + /// Move to the start of a new line by way of and + /// + /// The x offset to use + /// The y offset to use + /// This to enable chaining operations ITextContentContext MoveToStartNextLine(double offsetX, double offsetY); + + /// + /// Move to the start of a new line by way of and , and set text leading to + /// + /// The x offset to use + /// The y offset to use + /// This to enable chaining operations ITextContentContext MoveToStartNextLineAndSetLeading(double offsetX, double offsetY); + + /// + /// Replace the current text matrix with + /// + /// The new to use + /// This to enable chaining operations ITextContentContext ReplaceTextMatrix(Matrix matrix); + /// + /// Operation to show text + /// + /// The text to show + /// This to enable chaining operations ITextContentContext ShowText(string text); + + /// + /// Operation to show text on the next line + /// + /// The text to show + /// This to enable chaining operations ITextContentContext ShowTextOnNextLine(string text); + + /// + /// Operation to show text on the next line and setting the and + /// + /// The text to show + /// The word spacing to set + /// The character spacing to set + /// This to enable chaining operations ITextContentContext ShowTextOnNextLine(string text, double wordSpacing, double characterSpacing); } diff --git a/src/Synercoding.FileFormats.Pdf/Internals/PageContentContext.cs b/src/Synercoding.FileFormats.Pdf/Internals/PageContentContext.cs index e63a299..385621d 100644 --- a/src/Synercoding.FileFormats.Pdf/Internals/PageContentContext.cs +++ b/src/Synercoding.FileFormats.Pdf/Internals/PageContentContext.cs @@ -70,7 +70,7 @@ public IPageContentContext SetDashPattern(Dash dashPattern) public IPageContentContext SetFill(Color fill) { - GraphicState.FillColor = fill; + GraphicState.Fill = fill; RawContentStream.SetFillColor(fill); @@ -79,7 +79,7 @@ public IPageContentContext SetFill(Color fill) public IPageContentContext SetStroke(Color stroke) { - GraphicState.StrokeColor = stroke; + GraphicState.Stroke = stroke; RawContentStream.SetStrokeColor(stroke); diff --git a/src/Synercoding.FileFormats.Pdf/Internals/ShapesContentContext.cs b/src/Synercoding.FileFormats.Pdf/Internals/ShapesContentContext.cs index ef2b082..e553b40 100644 --- a/src/Synercoding.FileFormats.Pdf/Internals/ShapesContentContext.cs +++ b/src/Synercoding.FileFormats.Pdf/Internals/ShapesContentContext.cs @@ -37,7 +37,7 @@ public IShapeContentContext SetDashPattern(Dash dashPattern) public IShapeContentContext SetFill(Color fill) { - GraphicState.FillColor = fill; + GraphicState.Fill = fill; RawContentStream.SetFillColor(fill); @@ -46,7 +46,7 @@ public IShapeContentContext SetFill(Color fill) public IShapeContentContext SetStroke(Color stroke) { - GraphicState.StrokeColor = stroke; + GraphicState.Stroke = stroke; RawContentStream.SetStrokeColor(stroke); diff --git a/src/Synercoding.FileFormats.Pdf/Internals/TextContentContext.cs b/src/Synercoding.FileFormats.Pdf/Internals/TextContentContext.cs index 3d50b87..e9fe4c3 100644 --- a/src/Synercoding.FileFormats.Pdf/Internals/TextContentContext.cs +++ b/src/Synercoding.FileFormats.Pdf/Internals/TextContentContext.cs @@ -38,7 +38,7 @@ public ITextContentContext SetDashPattern(Dash dashPattern) public ITextContentContext SetFill(Color fill) { - GraphicState.FillColor = fill; + GraphicState.Fill = fill; RawContentStream.SetFillColor(fill); @@ -47,7 +47,7 @@ public ITextContentContext SetFill(Color fill) public ITextContentContext SetStroke(Color stroke) { - GraphicState.StrokeColor = stroke; + GraphicState.Stroke = stroke; RawContentStream.SetStrokeColor(stroke); diff --git a/src/Synercoding.FileFormats.Pdf/LowLevel/ContentStream.cs b/src/Synercoding.FileFormats.Pdf/LowLevel/ContentStream.cs index 5f5cd86..ec6c0d9 100644 --- a/src/Synercoding.FileFormats.Pdf/LowLevel/ContentStream.cs +++ b/src/Synercoding.FileFormats.Pdf/LowLevel/ContentStream.cs @@ -233,6 +233,11 @@ public ContentStream EndText() return this; } + /// + /// Write text to the content stream using the Tj operator + /// + /// The text to write + /// The to support chaining operations. public ContentStream ShowTextTj(string line) { InnerStream @@ -244,6 +249,11 @@ public ContentStream ShowTextTj(string line) return this; } + /// + /// Write text to the content stream using the ' operator + /// + /// The text to write + /// The to support chaining operations. public ContentStream MoveNextLineShowText(string line) { InnerStream @@ -255,6 +265,10 @@ public ContentStream MoveNextLineShowText(string line) return this; } + /// + /// Move to the next line using the T* operator + /// + /// The to support chaining operations. public ContentStream MoveNextLine() { InnerStream @@ -264,6 +278,13 @@ public ContentStream MoveNextLine() return this; } + /// + /// Write text to the content stream and use certain character and word spacing using the " operator. + /// + /// The text to write + /// The word spacing to set + /// The character spacing to set + /// The to support chaining operations. public ContentStream MoveNextLineShowText(string line, double wordSpacing, double characterSpacing) { InnerStream @@ -801,6 +822,11 @@ public ContentStream SetFillColor(SpotColor color) return this; } + /// + /// Write the line width to the content stream + /// + /// The line width to write + /// The to support chaining operations. public ContentStream SetLineWidth(double width) { InnerStream @@ -812,6 +838,11 @@ public ContentStream SetLineWidth(double width) return this; } + /// + /// Write the line cap style to the content stream + /// + /// The style to write + /// The to support chaining operations. public ContentStream SetLineCap(LineCapStyle lineCap) { InnerStream @@ -823,6 +854,11 @@ public ContentStream SetLineCap(LineCapStyle lineCap) return this; } + /// + /// Write the line join style to the content stream + /// + /// The style to write + /// The to support chaining operations. public ContentStream SetLineJoin(LineJoinStyle lineJoin) { InnerStream @@ -834,6 +870,11 @@ public ContentStream SetLineJoin(LineJoinStyle lineJoin) return this; } + /// + /// Write the miter limit to the content stream + /// + /// The miter limit to write + /// The to support chaining operations. public ContentStream SetMiterLimit(double miterLimit) { InnerStream @@ -845,6 +886,11 @@ public ContentStream SetMiterLimit(double miterLimit) return this; } + /// + /// Write the dash pattern to the content stream + /// + /// The dash pattern to write + /// The to support chaining operations. public ContentStream SetDashPattern(Dash dashPattern) { InnerStream From 76d0ca440248f1a833a63265f687bb4dc21b05f4 Mon Sep 17 00:00:00 2001 From: Gerard Gunnewijk Date: Thu, 26 Jan 2023 15:01:12 +0100 Subject: [PATCH 08/11] Added support back for netstandard 2.1 --- Directory.Build.props | 3 ++- src/Directory.Build.props | 2 +- src/Synercoding.FileFormats.Pdf/PdfPage.cs | 8 +++++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index cc8e849..097bb99 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -50,6 +50,7 @@ $(DefineConstants);SUPPORTS_MEMBERNOTNULL $(DefineConstants);SUPPORTS_INIT + $(DefineConstants);SUPPORTS_TYPED_ENUM_ISDEFINED - \ No newline at end of file + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 896375d..652e21c 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -2,7 +2,7 @@ - net7.0;net6.0 + net7.0;net6.0;netstandard2.1 $(MSBuildAllProjects);$(MSBuildThisFileDirectory)..\Directory.Build.props src diff --git a/src/Synercoding.FileFormats.Pdf/PdfPage.cs b/src/Synercoding.FileFormats.Pdf/PdfPage.cs index 5d7d156..74d1729 100644 --- a/src/Synercoding.FileFormats.Pdf/PdfPage.cs +++ b/src/Synercoding.FileFormats.Pdf/PdfPage.cs @@ -60,8 +60,14 @@ public PageRotation? Rotation get => _rotation; set { + const string ARGUMENT_OUT_OF_RANGE_MESSAGE = "The provided value can only be increments of 90."; +#if SUPPORTS_TYPED_ENUM_ISDEFINED if (value is not null && !Enum.IsDefined(value.Value)) - throw new ArgumentOutOfRangeException(nameof(Rotation), value, "The provided value can only be increments of 90."); + throw new ArgumentOutOfRangeException(nameof(Rotation), value, ARGUMENT_OUT_OF_RANGE_MESSAGE); +#else + if (value is not null && !Enum.IsDefined(typeof(PageRotation), value.Value)) + throw new ArgumentOutOfRangeException(nameof(Rotation), value, ARGUMENT_OUT_OF_RANGE_MESSAGE); +#endif _rotation = value; } From ef144ca9a1cb9c984e8a7fbfe01ebff4aa28ca15 Mon Sep 17 00:00:00 2001 From: Gerard Gunnewijk Date: Thu, 26 Jan 2023 15:01:19 +0100 Subject: [PATCH 09/11] Updated readme --- README.md | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 6c11be8..cde46aa 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [nuget-badge]: https://img.shields.io/nuget/v/Synercoding.FileFormats.Pdf.svg?label=Synercoding.FileFormats.Pdf -This project was created to enable PDF creation on .NETStandard. This because multiple libraries did not suit my purpose of working on .NET Core & .NET Framework the same way. Some alternatives supported settings the different boxes (Media, Crop, Bleed & Trim) but did not fully supported images on all platforms. Others supported images but not the different boxes, and again others did not work at all on .NET Core. +This project was created to enable PDF creation on .NETStandard 2.1, .NET 6 & .NET 7. This because multiple libraries did not suit my purpose of working on .NET Core & .NET Framework the same way. Some alternatives supported settings the different boxes (Media, Crop, Bleed & Trim) but did not fully supported images on all platforms. Others supported images but not the different boxes, and again others did not work at all on .NET Core. Because of those reasons this libary was created. @@ -15,7 +15,20 @@ This project is licensed under MIT license. ## Specifications used This library was created using the specifications lay out in ["PDF 32000-1:2008, Document management – Portable document format – Part 1: PDF 1.7"](https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/PDF32000_2008.pdf). -The full specifications are not implemented. This library currently only supports placements of images, drawing of vector shapes (CMYK, RGB & gray scale), and setting the different boxes. +The full specifications are not implemented. +This library currently only supports: + - Images + - *No transparency support at the moment* + - Text + - *Only the 14 standard fonts are available* + - *See "9.6.2.2 Standard Type 1 Fonts (Standard 14 Fonts)" in the specifications.* + - Shapes + - Setting page boxes (Media, Crop, Bleed, Trim & Art) + - Color model limited to: + - DeviceGray + - DeviceRGB + - DeviceCMYK + - Separations/Spotcolor (with a linearized type 2 method to portay the tint) ## Remarks Unlike most PDF libraries this library does not create the entire PDF model in memory before writing the PDF to a (file)stream. Most libaries support editing capabilities, because this libary only supports creating files, it was not necessary to keep the PDF model in memory. This results in less memory usage. @@ -23,8 +36,9 @@ Unlike most PDF libraries this library does not create the entire PDF model in m To place the images this library makes use of [SixLabors/ImageSharp](https://github.com/SixLabors/ImageSharp). All images that are placed in the PDF will be saved internally as a JPG file. This means that this library currently does **NOT** support transparency. ### Output pdfs -The PDF files created in this library are PDF 1.7 compliant. +The PDF files created in this library are not fully PDF 1.7 compliant, because the standard 14 fonts are partially written to the Font Dictionary, The **FirstChar**, **LastChar**. **Widths** and **FontDescriptor** values are omitted, which is was acceptable prior to PDF 1.5. This special treatment of the 14 standard fonts was deprecated in PDF 1.5. The PDF's that are generated will work in conforming readers because as the specification states: *"For backwards capability, conforming readers shall still provide the special treatment identified for the standard 14 fonts."*. +This shortcoming shall be remedied when broader font support is implemented. ### Sample program images The sample project called *Synercoding.FileFormats.Pdf.ConsoleTester* uses multiple images. Those images were taken from [Pexels.com](https://www.pexels.com/royalty-free-images/) and are licensed under the [Pexels License](https://www.pexels.com/photo-license/). @@ -35,21 +49,18 @@ The sample project called *Synercoding.FileFormats.Pdf.ConsoleTester* uses multi using (var writer = new PdfWriter(fs)) { writer - .AddPage(page => + .AddPage(page => { - var bleed = new Spacing(3, Unit.Millimeters); - page.MediaBox = Sizes.A4Portrait.Expand(bleed).AsRectangle(); + var bleed = Mm(3); + page.MediaBox = Sizes.A4.Expand(bleed).AsRectangle(); page.TrimBox = page.MediaBox.Contract(bleed); - - using (var eyeStream = File.OpenRead("Pexels_com/adult-blue-blue-eyes-865711.jpg")) + + using (var barrenStream = File.OpenRead("Pexels_com/arid-barren-desert-1975514.jpg")) + using (var barrenImage = SixLabors.ImageSharp.Image.Load(barrenStream)) { - var scale = 3456d / 5184; - - var width = 100; - var height = 100 * scale; + var scale = (double)barrenImage.Width / barrenImage.Height; - var offSet = 6; - page.AddImage(eyeStream, new Rectangle(offSet, offSet, width + offSet, height + offSet, Unit.Millimeters)); + page.Content.AddImage(barrenImage, new Rectangle(0, 0, scale * 303, 303, Unit.Millimeters)); } - }); -} \ No newline at end of file + }) +} From ed80c2023cc7837292d7527d2595051f52d54162 Mon Sep 17 00:00:00 2001 From: Gerard Gunnewijk Date: Thu, 26 Jan 2023 15:01:27 +0100 Subject: [PATCH 10/11] Updated release notes --- src/Synercoding.FileFormats.Pdf/PackageDetails.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Synercoding.FileFormats.Pdf/PackageDetails.props b/src/Synercoding.FileFormats.Pdf/PackageDetails.props index 796fc47..cd0ccfe 100644 --- a/src/Synercoding.FileFormats.Pdf/PackageDetails.props +++ b/src/Synercoding.FileFormats.Pdf/PackageDetails.props @@ -10,7 +10,7 @@ Synercoding.FileFormats.Pdf Synercoding.FileFormats.Pdf Contains classes which makes it easy to quickly create a pdf file. - Upgraded ImageSharp and added initial text support. + Rewritten most of the API surface to provide a uniform way to interact with the content. - \ No newline at end of file + From 9c150698d65ec412e4b2619efd314ca7708d0bc0 Mon Sep 17 00:00:00 2001 From: Gerard Gunnewijk Date: Thu, 26 Jan 2023 15:20:03 +0100 Subject: [PATCH 11/11] Added missing xml comments --- .../PageRotation.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/Synercoding.FileFormats.Pdf/PageRotation.cs b/src/Synercoding.FileFormats.Pdf/PageRotation.cs index 27b1e66..15fab8c 100644 --- a/src/Synercoding.FileFormats.Pdf/PageRotation.cs +++ b/src/Synercoding.FileFormats.Pdf/PageRotation.cs @@ -1,9 +1,27 @@ namespace Synercoding.FileFormats.Pdf; +/// +/// Enum representing the rotations a page can have +/// public enum PageRotation { + /// + /// No rotation + /// Rotation0 = 0, + + /// + /// Rotate 90 degrees + /// Rotation90 = 90, + + /// + /// Rotate 180 degrees + /// Rotation180 = 180, + + /// + /// Rotate 270 degrees + /// Rotation270 = 270 }