diff --git a/src/UglyToad.PdfPig.Tests/Graphics/TestOperationContext.cs b/src/UglyToad.PdfPig.Tests/Graphics/TestOperationContext.cs
index f092e6f4..30ba1f59 100644
--- a/src/UglyToad.PdfPig.Tests/Graphics/TestOperationContext.cs
+++ b/src/UglyToad.PdfPig.Tests/Graphics/TestOperationContext.cs
@@ -1,8 +1,6 @@
namespace UglyToad.PdfPig.Tests.Graphics
{
using System.Collections.Generic;
- using Content;
- using PdfPig.Fonts;
using PdfPig.Geometry;
using PdfPig.Graphics;
using PdfPig.IO;
@@ -66,22 +64,9 @@
public void ClosePath()
{
}
- }
- internal class TestResourceStore : IResourceStore
- {
- public void LoadResourceDictionary(DictionaryToken dictionary, bool isLenientParsing)
+ public void SetNamedGraphicsState(NameToken stateName)
{
}
-
- public IFont GetFont(NameToken name)
- {
- return null;
- }
-
- public StreamToken GetXObject(NameToken name)
- {
- return null;
- }
}
}
diff --git a/src/UglyToad.PdfPig.Tests/Graphics/TestResourceStore.cs b/src/UglyToad.PdfPig.Tests/Graphics/TestResourceStore.cs
new file mode 100644
index 00000000..66778454
--- /dev/null
+++ b/src/UglyToad.PdfPig.Tests/Graphics/TestResourceStore.cs
@@ -0,0 +1,33 @@
+namespace UglyToad.PdfPig.Tests.Graphics
+{
+ using Content;
+ using PdfPig.Fonts;
+ using PdfPig.Tokens;
+
+ internal class TestResourceStore : IResourceStore
+ {
+ public void LoadResourceDictionary(DictionaryToken dictionary, bool isLenientParsing)
+ {
+ }
+
+ public IFont GetFont(NameToken name)
+ {
+ return null;
+ }
+
+ public StreamToken GetXObject(NameToken name)
+ {
+ return null;
+ }
+
+ public DictionaryToken GetExtendedGraphicsStateDictionary(NameToken name)
+ {
+ return null;
+ }
+
+ public IFont GetFontDirectly(IndirectReferenceToken fontReferenceToken, bool isLenientParsing)
+ {
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/UglyToad.PdfPig.Tests/PublicApiScannerTests.cs b/src/UglyToad.PdfPig.Tests/PublicApiScannerTests.cs
index 0e38466d..ec17d69b 100644
--- a/src/UglyToad.PdfPig.Tests/PublicApiScannerTests.cs
+++ b/src/UglyToad.PdfPig.Tests/PublicApiScannerTests.cs
@@ -43,6 +43,7 @@
"UglyToad.PdfPig.Content.DocumentInformation",
"UglyToad.PdfPig.Content.Letter",
"UglyToad.PdfPig.Content.Page",
+ "UglyToad.PdfPig.Content.PageRotationDegrees",
"UglyToad.PdfPig.Content.PageSize",
"UglyToad.PdfPig.Content.Word",
"UglyToad.PdfPig.Content.TextLine",
diff --git a/src/UglyToad.PdfPig/Content/IResourceStore.cs b/src/UglyToad.PdfPig/Content/IResourceStore.cs
index 47200798..112f6624 100644
--- a/src/UglyToad.PdfPig/Content/IResourceStore.cs
+++ b/src/UglyToad.PdfPig/Content/IResourceStore.cs
@@ -10,5 +10,9 @@
IFont GetFont(NameToken name);
StreamToken GetXObject(NameToken name);
+
+ DictionaryToken GetExtendedGraphicsStateDictionary(NameToken name);
+
+ IFont GetFontDirectly(IndirectReferenceToken fontReferenceToken, bool isLenientParsing);
}
}
\ No newline at end of file
diff --git a/src/UglyToad.PdfPig/Content/Page.cs b/src/UglyToad.PdfPig/Content/Page.cs
index 4929ea8e..bb08f029 100644
--- a/src/UglyToad.PdfPig/Content/Page.cs
+++ b/src/UglyToad.PdfPig/Content/Page.cs
@@ -32,7 +32,7 @@
///
/// The rotation of the page in degrees (clockwise). Valid values are 0, 90, 180 and 270.
///
- public int Rotation { get; }
+ public PageRotationDegrees Rotation { get; }
internal PageContent Content { get; }
@@ -72,7 +72,7 @@
[NotNull]
public Experimental ExperimentalAccess { get; }
- internal Page(int number, DictionaryToken dictionary, MediaBox mediaBox, CropBox cropBox, int rotation, PageContent content,
+ internal Page(int number, DictionaryToken dictionary, MediaBox mediaBox, CropBox cropBox, PageRotationDegrees rotation, PageContent content,
AnnotationProvider annotationProvider)
{
if (number <= 0)
@@ -80,11 +80,6 @@
throw new ArgumentOutOfRangeException(nameof(number), "Page number cannot be 0 or negative.");
}
- if (rotation != 0 && rotation != 90 && rotation != 180 && rotation != 270)
- {
- throw new ArgumentOutOfRangeException(nameof(rotation), $"Rotation must be 0, 90, 180 or 270. Got: {rotation}.");
- }
-
Dictionary = dictionary ?? throw new ArgumentNullException(nameof(dictionary));
Number = number;
diff --git a/src/UglyToad.PdfPig/Content/PageRotationDegrees.cs b/src/UglyToad.PdfPig/Content/PageRotationDegrees.cs
new file mode 100644
index 00000000..08fc3942
--- /dev/null
+++ b/src/UglyToad.PdfPig/Content/PageRotationDegrees.cs
@@ -0,0 +1,116 @@
+namespace UglyToad.PdfPig.Content
+{
+ using System;
+ using System.Diagnostics.Contracts;
+ using Core;
+
+ ///
+ /// Represents the rotation of a page in a PDF document defined by the page dictionary in degrees clockwise.
+ ///
+ public struct PageRotationDegrees : IEquatable
+ {
+ ///
+ /// The rotation of the page in degrees clockwise.
+ ///
+ public int Value { get; }
+
+ ///
+ /// Get the rotation expressed in radians (anti-clockwise).
+ ///
+ public decimal Radians
+ {
+ get
+ {
+ switch (Value)
+ {
+ case 0:
+ return 0;
+ case 90:
+ return -(decimal)(0.5 * Math.PI);
+ case 180:
+ return -(decimal) Math.PI;
+ case 270:
+ return -(decimal) (1.5 * Math.PI);
+ default:
+ throw new InvalidOperationException($"Invalid value for rotation: {Value}.");
+ }
+ }
+ }
+
+ ///
+ /// Create a .
+ ///
+ /// Rotation in degrees clockwise.
+ public PageRotationDegrees(int rotation)
+ {
+ if (rotation != 0 && rotation != 90 && rotation != 180 && rotation != 270)
+ {
+ throw new ArgumentOutOfRangeException(nameof(rotation), $"Rotation must be 0, 90, 180 or 270. Got: {rotation}.");
+ }
+
+ Value = rotation;
+ }
+
+ [Pure]
+ internal TransformationMatrix Rotate(TransformationMatrix matrix)
+ {
+ TransformationMatrix thisMatrix;
+ switch (Value)
+ {
+ case 0:
+ thisMatrix = TransformationMatrix.FromArray(new[]{ 1m, 0, 0, 1 });
+ break;
+ case 90:
+ thisMatrix = TransformationMatrix.FromArray(new[] {0m, -1, 1, 0});
+ break;
+ case 180:
+ thisMatrix = TransformationMatrix.FromArray(new[] {-1m, 0, 0, -1});
+ break;
+ case 270:
+ thisMatrix = TransformationMatrix.FromArray(new[] {0m, 1, -1, 0});
+ break;
+ default:
+ throw new InvalidOperationException($"Invalid value for rotation: {Value}.");
+ }
+
+ return thisMatrix.Multiply(matrix);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return Value.GetHashCode();
+ }
+
+ ///
+ public override string ToString()
+ {
+ return Value.ToString();
+ }
+
+ ///
+ public override bool Equals(object obj)
+ {
+ return obj is PageRotationDegrees degrees && Equals(degrees);
+ }
+
+ ///
+ public bool Equals(PageRotationDegrees other)
+ {
+ return Value == other.Value;
+ }
+
+ ///
+ /// Equal.
+ ///
+ public static bool operator ==(PageRotationDegrees degrees1, PageRotationDegrees degrees2)
+ {
+ return degrees1.Equals(degrees2);
+ }
+
+ ///
+ /// Not equal.
+ ///
+ public static bool operator !=(PageRotationDegrees degrees1, PageRotationDegrees degrees2) => !(degrees1 == degrees2);
+ }
+}
diff --git a/src/UglyToad.PdfPig/Content/ResourceContainer.cs b/src/UglyToad.PdfPig/Content/ResourceContainer.cs
index 39991f25..15969430 100644
--- a/src/UglyToad.PdfPig/Content/ResourceContainer.cs
+++ b/src/UglyToad.PdfPig/Content/ResourceContainer.cs
@@ -2,6 +2,7 @@
{
using System;
using System.Collections.Generic;
+ using Exceptions;
using Fonts;
using Parser.Parts;
using Tokenization.Scanner;
@@ -15,6 +16,8 @@
private readonly Dictionary loadedFonts = new Dictionary();
private readonly Dictionary currentResourceState = new Dictionary();
+ private readonly Dictionary extendedGraphicsStates = new Dictionary();
+
public ResourceContainer(IPdfTokenScanner scanner, IFontFactory fontFactory)
{
this.scanner = scanner;
@@ -26,7 +29,7 @@
if (resourceDictionary.TryGet(NameToken.Font, out var fontBase))
{
var fontDictionary = DirectObjectFinder.Get(fontBase, scanner);
-
+
LoadFontDictionary(fontDictionary, isLenientParsing);
}
@@ -44,6 +47,17 @@
currentResourceState[NameToken.Create(pair.Key)] = reference.Data;
}
}
+
+ if (resourceDictionary.TryGet(NameToken.ExtGState, scanner, out DictionaryToken extGStateDictionaryToken))
+ {
+ foreach (var pair in extGStateDictionaryToken.Data)
+ {
+ var name = NameToken.Create(pair.Key);
+ var state = DirectObjectFinder.Get(pair.Value, scanner);
+
+ extendedGraphicsStates[name] = state;
+ }
+ }
}
private void LoadFontDictionary(DictionaryToken fontDictionary, bool isLenientParsing)
@@ -68,7 +82,7 @@
{
continue;
}
-
+
var fontObject = DirectObjectFinder.Get(objectKey, scanner);
if (fontObject == null)
@@ -79,7 +93,7 @@
loadedFonts[reference] = fontFactory.Get(fontObject, isLenientParsing);
}
}
-
+
public IFont GetFont(NameToken name)
{
var reference = currentResourceState[name];
@@ -89,6 +103,18 @@
return font;
}
+ public IFont GetFontDirectly(IndirectReferenceToken fontReferenceToken, bool isLenientParsing)
+ {
+ if (!DirectObjectFinder.TryGet(fontReferenceToken, scanner, out DictionaryToken fontDictionaryToken))
+ {
+ throw new PdfDocumentFormatException($"The requested font reference token {fontReferenceToken} wasn't a font.");
+ }
+
+ var font = fontFactory.Get(fontDictionaryToken, isLenientParsing);
+
+ return font;
+ }
+
public StreamToken GetXObject(NameToken name)
{
var reference = currentResourceState[name];
@@ -97,6 +123,10 @@
return stream;
}
+
+ public DictionaryToken GetExtendedGraphicsStateDictionary(NameToken name)
+ {
+ return extendedGraphicsStates[name];
+ }
}
}
-
diff --git a/src/UglyToad.PdfPig/Fonts/CharacterBoundingBox.cs b/src/UglyToad.PdfPig/Fonts/CharacterBoundingBox.cs
new file mode 100644
index 00000000..77444735
--- /dev/null
+++ b/src/UglyToad.PdfPig/Fonts/CharacterBoundingBox.cs
@@ -0,0 +1,17 @@
+namespace UglyToad.PdfPig.Fonts
+{
+ using Geometry;
+
+ internal class CharacterBoundingBox
+ {
+ public PdfRectangle GlyphBounds { get; }
+
+ public decimal Width { get; }
+
+ public CharacterBoundingBox(PdfRectangle glyphBounds, decimal width)
+ {
+ GlyphBounds = glyphBounds;
+ Width = width;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/UglyToad.PdfPig/Fonts/IFont.cs b/src/UglyToad.PdfPig/Fonts/IFont.cs
index 331aa8db..e46ac516 100644
--- a/src/UglyToad.PdfPig/Fonts/IFont.cs
+++ b/src/UglyToad.PdfPig/Fonts/IFont.cs
@@ -1,7 +1,6 @@
namespace UglyToad.PdfPig.Fonts
{
using Core;
- using Geometry;
using IO;
using Tokens;
@@ -19,17 +18,4 @@
TransformationMatrix GetFontMatrix();
}
-
- internal class CharacterBoundingBox
- {
- public PdfRectangle GlyphBounds { get; }
-
- public decimal Width { get; }
-
- public CharacterBoundingBox(PdfRectangle glyphBounds, decimal width)
- {
- GlyphBounds = glyphBounds;
- Width = width;
- }
- }
}
diff --git a/src/UglyToad.PdfPig/Fonts/Simple/TrueTypeSimpleFont.cs b/src/UglyToad.PdfPig/Fonts/Simple/TrueTypeSimpleFont.cs
index 3953859b..1d8e0e85 100644
--- a/src/UglyToad.PdfPig/Fonts/Simple/TrueTypeSimpleFont.cs
+++ b/src/UglyToad.PdfPig/Fonts/Simple/TrueTypeSimpleFont.cs
@@ -11,89 +11,6 @@
using TrueType;
using Util.JetBrains.Annotations;
- ///
- /// Some TrueType fonts use both the Standard 14 descriptor and the TrueType font from disk.
- ///
- internal class TrueTypeStandard14FallbackSimpleFont : IFont
- {
- private static readonly TransformationMatrix DefaultTransformation =
- TransformationMatrix.FromValues(1m / 1000m, 0, 0, 1m / 1000m, 0, 0);
-
- private readonly FontMetrics fontMetrics;
- private readonly Encoding encoding;
- private readonly TrueTypeFontProgram font;
-
- public NameToken Name { get; }
-
- public bool IsVertical { get; } = false;
-
- public TrueTypeStandard14FallbackSimpleFont(NameToken name, FontMetrics fontMetrics, Encoding encoding, TrueTypeFontProgram font)
- {
- this.fontMetrics = fontMetrics;
- this.encoding = encoding ?? throw new ArgumentNullException(nameof(encoding));
- this.font = font;
- Name = name;
- }
-
- public int ReadCharacterCode(IInputBytes bytes, out int codeLength)
- {
- codeLength = 1;
- return bytes.CurrentByte;
- }
-
- public bool TryGetUnicode(int characterCode, out string value)
- {
- value = null;
-
- // If the font is a simple font that uses one of the predefined encodings MacRomanEncoding, MacExpertEncoding, or WinAnsiEncoding...
-
- // Map the character code to a character name.
- var encodedCharacterName = encoding.GetName(characterCode);
-
- // Look up the character name in the Adobe Glyph List.
- try
- {
- value = GlyphList.AdobeGlyphList.NameToUnicode(encodedCharacterName);
- }
- catch
- {
- return false;
- }
-
- return true;
- }
-
- public CharacterBoundingBox GetBoundingBox(int characterCode)
- {
- var fontMatrix = GetFontMatrix();
- if (font != null && font.TryGetBoundingBox(characterCode, out var bounds))
- {
- bounds = fontMatrix.Transform(bounds);
- return new CharacterBoundingBox(bounds, bounds.Width);
- }
-
- var name = encoding.GetName(characterCode);
- var metrics = fontMetrics.CharacterMetrics[name];
-
- bounds = fontMatrix.Transform(metrics.BoundingBox);
- var width = fontMatrix.TransformX(metrics.WidthX);
-
- return new CharacterBoundingBox(bounds, width);
- }
-
- public TransformationMatrix GetFontMatrix()
- {
- if (font?.TableRegister.HeaderTable != null)
- {
- var scale = (decimal)font.GetFontMatrixMultiplier();
-
- return TransformationMatrix.FromValues(1 / scale, 0, 0, 1 / scale, 0, 0);
- }
-
- return DefaultTransformation;
- }
- }
-
internal class TrueTypeSimpleFont : IFont
{
private static readonly TransformationMatrix DefaultTransformation =
@@ -194,14 +111,14 @@
{
boundingBox = DefaultTransformation.Transform(boundingBox);
}
-
+
decimal width;
var index = characterCode - firstCharacter;
if (widths != null && index >= 0 && index < widths.Length)
{
fromFont = false;
- width = widths[index];
+ width = widths[index];
}
else if (fontProgram != null)
{
@@ -214,7 +131,7 @@
{
throw new InvalidOperationException($"Could not retrieve width for character code: {characterCode} in font {Name}.");
}
-
+
if (fromFont)
{
width = fontMatrix.Transform(new PdfVector(width, 0)).X;
@@ -227,10 +144,22 @@
return new CharacterBoundingBox(boundingBox, width);
}
+ public TransformationMatrix GetFontMatrix()
+ {
+ var scale = 1000m;
+
+ if (fontProgram?.TableRegister.HeaderTable != null)
+ {
+ scale = fontProgram.GetFontMatrixMultiplier();
+ }
+
+ return TransformationMatrix.FromValues(1m / scale, 0, 0, 1m / scale, 0, 0);
+ }
+
private PdfRectangle GetBoundingBoxInGlyphSpace(int characterCode, out bool fromFont)
{
fromFont = true;
-
+
if (fontProgram == null)
{
return descriptor.BoundingBox;
@@ -262,18 +191,6 @@
return widths[index];
}
-
- public TransformationMatrix GetFontMatrix()
- {
- var scale = 1000m;
-
- if (fontProgram?.TableRegister.HeaderTable != null)
- {
- scale = fontProgram.GetFontMatrixMultiplier();
- }
-
- return TransformationMatrix.FromValues(1m / scale, 0, 0, 1m / scale, 0, 0);
- }
}
}
diff --git a/src/UglyToad.PdfPig/Fonts/Simple/TrueTypeStandard14FallbackSimpleFont.cs b/src/UglyToad.PdfPig/Fonts/Simple/TrueTypeStandard14FallbackSimpleFont.cs
new file mode 100644
index 00000000..5442bb3a
--- /dev/null
+++ b/src/UglyToad.PdfPig/Fonts/Simple/TrueTypeStandard14FallbackSimpleFont.cs
@@ -0,0 +1,92 @@
+namespace UglyToad.PdfPig.Fonts.Simple
+{
+ using System;
+ using Core;
+ using Encodings;
+ using IO;
+ using Tokens;
+ using TrueType;
+
+ ///
+ /// Some TrueType fonts use both the Standard 14 descriptor and the TrueType font from disk.
+ ///
+ internal class TrueTypeStandard14FallbackSimpleFont : IFont
+ {
+ private static readonly TransformationMatrix DefaultTransformation =
+ TransformationMatrix.FromValues(1m / 1000m, 0, 0, 1m / 1000m, 0, 0);
+
+ private readonly FontMetrics fontMetrics;
+ private readonly Encoding encoding;
+ private readonly TrueTypeFontProgram font;
+
+ public NameToken Name { get; }
+
+ public bool IsVertical { get; } = false;
+
+ public TrueTypeStandard14FallbackSimpleFont(NameToken name, FontMetrics fontMetrics, Encoding encoding, TrueTypeFontProgram font)
+ {
+ this.fontMetrics = fontMetrics;
+ this.encoding = encoding ?? throw new ArgumentNullException(nameof(encoding));
+ this.font = font;
+ Name = name;
+ }
+
+ public int ReadCharacterCode(IInputBytes bytes, out int codeLength)
+ {
+ codeLength = 1;
+ return bytes.CurrentByte;
+ }
+
+ public bool TryGetUnicode(int characterCode, out string value)
+ {
+ value = null;
+
+ // If the font is a simple font that uses one of the predefined encodings MacRomanEncoding, MacExpertEncoding, or WinAnsiEncoding...
+
+ // Map the character code to a character name.
+ var encodedCharacterName = encoding.GetName(characterCode);
+
+ // Look up the character name in the Adobe Glyph List.
+ try
+ {
+ value = GlyphList.AdobeGlyphList.NameToUnicode(encodedCharacterName);
+ }
+ catch
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ public CharacterBoundingBox GetBoundingBox(int characterCode)
+ {
+ var fontMatrix = GetFontMatrix();
+ if (font != null && font.TryGetBoundingBox(characterCode, out var bounds))
+ {
+ bounds = fontMatrix.Transform(bounds);
+ return new CharacterBoundingBox(bounds, bounds.Width);
+ }
+
+ var name = encoding.GetName(characterCode);
+ var metrics = fontMetrics.CharacterMetrics[name];
+
+ bounds = fontMatrix.Transform(metrics.BoundingBox);
+ var width = fontMatrix.TransformX(metrics.WidthX);
+
+ return new CharacterBoundingBox(bounds, width);
+ }
+
+ public TransformationMatrix GetFontMatrix()
+ {
+ if (font?.TableRegister.HeaderTable != null)
+ {
+ var scale = (decimal)font.GetFontMatrixMultiplier();
+
+ return TransformationMatrix.FromValues(1 / scale, 0, 0, 1 / scale, 0, 0);
+ }
+
+ return DefaultTransformation;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/UglyToad.PdfPig/Fonts/SystemFonts/SystemFontFinder.cs b/src/UglyToad.PdfPig/Fonts/SystemFonts/SystemFontFinder.cs
index ca531bb4..ee1447f4 100644
--- a/src/UglyToad.PdfPig/Fonts/SystemFonts/SystemFontFinder.cs
+++ b/src/UglyToad.PdfPig/Fonts/SystemFonts/SystemFontFinder.cs
@@ -240,6 +240,7 @@
}
}
+ data.Seek(0);
font = trueTypeFontParser.Parse(data);
var psName = font.TableRegister.NameTable?.GetPostscriptName() ?? font.Name;
if (!cache.ContainsKey(psName))
diff --git a/src/UglyToad.PdfPig/Graphics/ContentStreamProcessor.cs b/src/UglyToad.PdfPig/Graphics/ContentStreamProcessor.cs
index ce348686..9839c316 100644
--- a/src/UglyToad.PdfPig/Graphics/ContentStreamProcessor.cs
+++ b/src/UglyToad.PdfPig/Graphics/ContentStreamProcessor.cs
@@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using Content;
+ using Core;
using Fonts;
using Geometry;
using IO;
@@ -20,12 +21,14 @@
private readonly List paths = new List();
private readonly IResourceStore resourceStore;
private readonly UserSpaceUnit userSpaceUnit;
+ private readonly PageRotationDegrees rotation;
private readonly bool isLenientParsing;
private readonly IPdfTokenScanner pdfScanner;
private readonly XObjectFactory xObjectFactory;
private readonly ILog log;
private Stack graphicsStack = new Stack();
+ private IFont activeExtendedGraphicsStateFont = null;
public TextMatrices TextMatrices { get; } = new TextMatrices();
@@ -43,13 +46,14 @@
public List Letters = new List();
- public ContentStreamProcessor(PdfRectangle cropBox, IResourceStore resourceStore, UserSpaceUnit userSpaceUnit, bool isLenientParsing,
+ public ContentStreamProcessor(PdfRectangle cropBox, IResourceStore resourceStore, UserSpaceUnit userSpaceUnit, PageRotationDegrees rotation, bool isLenientParsing,
IPdfTokenScanner pdfScanner,
XObjectFactory xObjectFactory,
ILog log)
{
this.resourceStore = resourceStore;
this.userSpaceUnit = userSpaceUnit;
+ this.rotation = rotation;
this.isLenientParsing = isLenientParsing;
this.pdfScanner = pdfScanner;
this.xObjectFactory = xObjectFactory;
@@ -91,6 +95,7 @@
public void PopState()
{
graphicsStack.Pop();
+ activeExtendedGraphicsStateFont = null;
}
public void PushState()
@@ -102,7 +107,7 @@
{
var currentState = GetCurrentState();
- var font = resourceStore.GetFont(currentState.FontState.FontName);
+ var font = currentState.FontState.FromExtendedGraphicsState ? activeExtendedGraphicsStateFont : resourceStore.GetFont(currentState.FontState.FontName);
if (font == null)
{
@@ -147,11 +152,11 @@
var boundingBox = font.GetBoundingBox(code);
- var transformedGlyphBounds = transformationMatrix
+ var transformedGlyphBounds = rotation.Rotate(transformationMatrix)
.Transform(TextMatrices.TextMatrix
.Transform(renderingMatrix
.Transform(boundingBox.GlyphBounds)));
- var transformedPdfBounds = transformationMatrix
+ var transformedPdfBounds = rotation.Rotate(transformationMatrix)
.Transform(TextMatrices.TextMatrix
.Transform(renderingMatrix.Transform(new PdfRectangle(0, 0, boundingBox.Width, 0))));
@@ -274,6 +279,36 @@
CurrentPath = null;
}
+ public void SetNamedGraphicsState(NameToken stateName)
+ {
+ var currentGraphicsState = GetCurrentState();
+
+ var state = resourceStore.GetExtendedGraphicsStateDictionary(stateName);
+
+ if (state.TryGet(NameToken.Lw, pdfScanner, out NumericToken lwToken))
+ {
+ currentGraphicsState.LineWidth = lwToken.Data;
+ }
+
+ if (state.TryGet(NameToken.Lc, pdfScanner, out NumericToken lcToken))
+ {
+ currentGraphicsState.CapStyle = (LineCapStyle) lcToken.Int;
+ }
+
+ if (state.TryGet(NameToken.Lj, pdfScanner, out NumericToken ljToken))
+ {
+ currentGraphicsState.JoinStyle = (LineJoinStyle) ljToken.Int;
+ }
+
+ if (state.TryGet(NameToken.Font, pdfScanner, out ArrayToken fontArray) && fontArray.Length == 2
+ && fontArray.Data[0] is IndirectReferenceToken fontReference && fontArray.Data[1] is NumericToken sizeToken)
+ {
+ currentGraphicsState.FontState.FromExtendedGraphicsState = true;
+ currentGraphicsState.FontState.FontSize = sizeToken.Data;
+ activeExtendedGraphicsStateFont = resourceStore.GetFontDirectly(fontReference, isLenientParsing);
+ }
+ }
+
private void AdjustTextMatrix(decimal tx, decimal ty)
{
var matrix = TransformationMatrix.GetTranslationMatrix(tx, ty);
diff --git a/src/UglyToad.PdfPig/Graphics/CurrentFontState.cs b/src/UglyToad.PdfPig/Graphics/CurrentFontState.cs
index dbccbd41..71056993 100644
--- a/src/UglyToad.PdfPig/Graphics/CurrentFontState.cs
+++ b/src/UglyToad.PdfPig/Graphics/CurrentFontState.cs
@@ -2,6 +2,7 @@
namespace UglyToad.PdfPig.Graphics
{
using Core;
+ using Operations.SpecialGraphicsState;
using PdfPig.Core;
using Tokens;
@@ -10,6 +11,11 @@ namespace UglyToad.PdfPig.Graphics
///
public class CurrentFontState : IDeepCloneable
{
+ ///
+ /// Whether the font comes from the extended graphics state via the operator.
+ ///
+ public bool FromExtendedGraphicsState { get; set; } = false;
+
///
/// A value in unscaled text space units which is added to the horizontal (or vertical if in vertical writing mode)
/// glyph displacement.
diff --git a/src/UglyToad.PdfPig/Graphics/IOperationContext.cs b/src/UglyToad.PdfPig/Graphics/IOperationContext.cs
index 924b2ec2..ad5e033b 100644
--- a/src/UglyToad.PdfPig/Graphics/IOperationContext.cs
+++ b/src/UglyToad.PdfPig/Graphics/IOperationContext.cs
@@ -81,5 +81,11 @@
/// Close the current path.
///
void ClosePath();
+
+ ///
+ /// Update the graphics state to apply the state from the named ExtGState dictionary.
+ ///
+ /// The name of the state to apply.
+ void SetNamedGraphicsState(NameToken stateName);
}
}
\ No newline at end of file
diff --git a/src/UglyToad.PdfPig/Graphics/Operations/SpecialGraphicsState/SetGraphicsStateOperatorsFromDictionary.cs b/src/UglyToad.PdfPig/Graphics/Operations/SpecialGraphicsState/SetGraphicsStateOperatorsFromDictionary.cs
index 40aaf41c..88987392 100644
--- a/src/UglyToad.PdfPig/Graphics/Operations/SpecialGraphicsState/SetGraphicsStateOperatorsFromDictionary.cs
+++ b/src/UglyToad.PdfPig/Graphics/Operations/SpecialGraphicsState/SetGraphicsStateOperatorsFromDictionary.cs
@@ -34,6 +34,7 @@
///
public void Run(IOperationContext operationContext)
{
+ operationContext.SetNamedGraphicsState(Name);
}
///
diff --git a/src/UglyToad.PdfPig/Parser/PageFactory.cs b/src/UglyToad.PdfPig/Parser/PageFactory.cs
index 1a8667d0..05c8e6e1 100644
--- a/src/UglyToad.PdfPig/Parser/PageFactory.cs
+++ b/src/UglyToad.PdfPig/Parser/PageFactory.cs
@@ -53,10 +53,10 @@
throw new InvalidOperationException($"Page {number} had its type specified as {type} rather than 'Page'.");
}
- var rotation = pageTreeMembers.Rotation;
+ var rotation = new PageRotationDegrees(pageTreeMembers.Rotation);
if (dictionary.TryGet(NameToken.Rotate, pdfScanner, out NumericToken rotateToken))
{
- rotation = rotateToken.Int;
+ rotation = new PageRotationDegrees(rotateToken.Int);
}
MediaBox mediaBox = GetMediaBox(number, dictionary, pageTreeMembers, isLenientParsing);
@@ -100,7 +100,7 @@
}
}
- content = GetContent(bytes, cropBox, userSpaceUnit, isLenientParsing);
+ content = GetContent(bytes, cropBox, userSpaceUnit, rotation, isLenientParsing);
}
else
{
@@ -113,7 +113,7 @@
var bytes = contentStream.Decode(filterProvider);
- content = GetContent(bytes, cropBox, userSpaceUnit, isLenientParsing);
+ content = GetContent(bytes, cropBox, userSpaceUnit, rotation, isLenientParsing);
}
var page = new Page(number, dictionary, mediaBox, cropBox, rotation, content, new AnnotationProvider(pdfScanner, dictionary, isLenientParsing));
@@ -121,11 +121,11 @@
return page;
}
- private PageContent GetContent(IReadOnlyList contentBytes, CropBox cropBox, UserSpaceUnit userSpaceUnit, bool isLenientParsing)
+ private PageContent GetContent(IReadOnlyList contentBytes, CropBox cropBox, UserSpaceUnit userSpaceUnit, PageRotationDegrees rotation, bool isLenientParsing)
{
var operations = pageContentParser.Parse(new ByteArrayInputBytes(contentBytes));
- var context = new ContentStreamProcessor(cropBox.Bounds, resourceStore, userSpaceUnit, isLenientParsing, pdfScanner, xObjectFactory, log);
+ var context = new ContentStreamProcessor(cropBox.Bounds, resourceStore, userSpaceUnit, rotation, isLenientParsing, pdfScanner, xObjectFactory, log);
return context.Process(operations);
}