add support for alternative colorspace in separation colorspaces #89

This commit is contained in:
Eliot Jones
2019-12-06 17:23:15 +00:00
parent c57d8446e4
commit e38da0a403
13 changed files with 162 additions and 51 deletions

View File

@@ -1,11 +1,14 @@
namespace UglyToad.PdfPig.Tests.Graphics namespace UglyToad.PdfPig.Tests.Graphics
{ {
using System.Collections.Generic; using System.Collections.Generic;
using Content;
using PdfPig.Geometry; using PdfPig.Geometry;
using PdfPig.Graphics; using PdfPig.Graphics;
using PdfPig.IO; using PdfPig.IO;
using PdfPig.Tokens; using PdfPig.Tokens;
using PdfPig.Core; using PdfPig.Core;
using PdfPig.Fonts;
using Tokens;
internal class TestOperationContext : IOperationContext internal class TestOperationContext : IOperationContext
{ {
@@ -29,7 +32,7 @@
{ {
StateStack.Push(new CurrentGraphicsState()); StateStack.Push(new CurrentGraphicsState());
CurrentPath = new PdfPath(); CurrentPath = new PdfPath();
ColorSpaceContext = new ColorSpaceContext(GetCurrentState); ColorSpaceContext = new ColorSpaceContext(GetCurrentState, new ResourceStore(new TestPdfTokenScanner(), new TestFontFactory()));
} }
public CurrentGraphicsState GetCurrentState() public CurrentGraphicsState GetCurrentState()
@@ -89,5 +92,13 @@
public void EndInlineImage(IReadOnlyList<byte> bytes) public void EndInlineImage(IReadOnlyList<byte> bytes)
{ {
} }
private class TestFontFactory : IFontFactory
{
public IFont Get(DictionaryToken dictionary, bool isLenientParsing)
{
return null;
}
}
} }
} }

View File

@@ -1,6 +1,7 @@
namespace UglyToad.PdfPig.Content namespace UglyToad.PdfPig.Content
{ {
using Fonts; using Fonts;
using Graphics.Colors;
using Tokens; using Tokens;
internal interface IResourceStore internal interface IResourceStore
@@ -21,6 +22,6 @@
IFont GetFontDirectly(IndirectReferenceToken fontReferenceToken, bool isLenientParsing); IFont GetFontDirectly(IndirectReferenceToken fontReferenceToken, bool isLenientParsing);
bool TryGetNamedColorSpace(NameToken name, out IToken namedColorSpace); bool TryGetNamedColorSpace(NameToken name, out ResourceColorSpace namedColorSpace);
} }
} }

View File

@@ -4,6 +4,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using Exceptions; using Exceptions;
using Fonts; using Fonts;
using Graphics.Colors;
using Parser.Parts; using Parser.Parts;
using Tokenization.Scanner; using Tokenization.Scanner;
using Tokens; using Tokens;
@@ -19,7 +20,7 @@
private readonly Dictionary<NameToken, DictionaryToken> extendedGraphicsStates = new Dictionary<NameToken, DictionaryToken>(); private readonly Dictionary<NameToken, DictionaryToken> extendedGraphicsStates = new Dictionary<NameToken, DictionaryToken>();
private readonly Dictionary<NameToken, NameToken> colorSpaceNames = new Dictionary<NameToken, NameToken>(); private readonly Dictionary<NameToken, ResourceColorSpace> namedColorSpaces = new Dictionary<NameToken, ResourceColorSpace>();
public ResourceStore(IPdfTokenScanner scanner, IFontFactory fontFactory) public ResourceStore(IPdfTokenScanner scanner, IFontFactory fontFactory)
{ {
@@ -72,7 +73,7 @@
if (DirectObjectFinder.TryGet(nameColorSpacePair.Value, scanner, out NameToken colorSpaceName)) if (DirectObjectFinder.TryGet(nameColorSpacePair.Value, scanner, out NameToken colorSpaceName))
{ {
colorSpaceNames[name] = colorSpaceName; namedColorSpaces[name] = new ResourceColorSpace(colorSpaceName);
} }
else if (DirectObjectFinder.TryGet(nameColorSpacePair.Value, scanner, out ArrayToken colorSpaceArray)) else if (DirectObjectFinder.TryGet(nameColorSpacePair.Value, scanner, out ArrayToken colorSpaceArray))
{ {
@@ -88,7 +89,7 @@
throw new PdfDocumentFormatException($"Invalid ColorSpace array encountered in page resource dictionary: {colorSpaceArray}."); throw new PdfDocumentFormatException($"Invalid ColorSpace array encountered in page resource dictionary: {colorSpaceArray}.");
} }
colorSpaceNames[name] = arrayNamedColorSpace; namedColorSpaces[name] = new ResourceColorSpace(arrayNamedColorSpace, colorSpaceArray);
} }
else else
{ {
@@ -158,16 +159,16 @@
return font; return font;
} }
public bool TryGetNamedColorSpace(NameToken name, out IToken namedToken) public bool TryGetNamedColorSpace(NameToken name, out ResourceColorSpace namedToken)
{ {
namedToken = null; namedToken = default(ResourceColorSpace);
if (name == null) if (name == null)
{ {
throw new ArgumentNullException(nameof(name)); throw new ArgumentNullException(nameof(name));
} }
if (!colorSpaceNames.TryGetValue(name, out var colorSpaceName)) if (!namedColorSpaces.TryGetValue(name, out var colorSpaceName))
{ {
return false; return false;
} }

View File

@@ -8,10 +8,10 @@ namespace UglyToad.PdfPig.Export
public interface ITextExporter public interface ITextExporter
{ {
/// <summary> /// <summary>
/// Get the text representation. /// Get the text representation of a page in a desired format.
/// </summary> /// </summary>
/// <param name="page"></param> /// <param name="page">The page to convert to the format.</param>
/// <returns></returns> /// <returns>The <see langword="string"/> containing the page contents represented in a compatible format.</returns>
string Get(Page page); string Get(Page page);
} }
} }

View File

@@ -2,27 +2,35 @@
{ {
using System; using System;
using Colors; using Colors;
using Content;
using Tokens; using Tokens;
internal class ColorSpaceContext : IColorSpaceContext internal class ColorSpaceContext : IColorSpaceContext
{ {
private readonly Func<CurrentGraphicsState> currentStateFunc; private readonly Func<CurrentGraphicsState> currentStateFunc;
private readonly IResourceStore resourceStore;
public ColorSpace CurrentStrokingColorSpace { get; private set; } = ColorSpace.DeviceGray; public ColorSpace CurrentStrokingColorSpace { get; private set; } = ColorSpace.DeviceGray;
public ColorSpace CurrentNonStrokingColorSpace { get; private set; } = ColorSpace.DeviceGray; public ColorSpace CurrentNonStrokingColorSpace { get; private set; } = ColorSpace.DeviceGray;
public ColorSpaceContext(Func<CurrentGraphicsState> currentStateFunc) public NameToken AdvancedStrokingColorSpace { get; private set; }
public NameToken AdvancedNonStrokingColorSpace { get; private set; }
public ColorSpaceContext(Func<CurrentGraphicsState> currentStateFunc, IResourceStore resourceStore)
{ {
this.currentStateFunc = currentStateFunc ?? throw new ArgumentNullException(nameof(currentStateFunc)); this.currentStateFunc = currentStateFunc ?? throw new ArgumentNullException(nameof(currentStateFunc));
this.resourceStore = resourceStore ?? throw new ArgumentNullException(nameof(resourceStore));
} }
public void SetStrokingColorspace(NameToken colorspace) public void SetStrokingColorspace(NameToken colorspace)
{ {
if (colorspace.TryMapToColorSpace(out var colorspaceActual)) void DefaultColorSpace(ColorSpace? colorSpaceActual = null)
{ {
CurrentStrokingColorSpace = colorspaceActual; if (colorSpaceActual.HasValue)
switch (colorspaceActual) {
switch (colorSpaceActual)
{ {
case ColorSpace.DeviceGray: case ColorSpace.DeviceGray:
currentStateFunc().CurrentStrokingColor = GrayColor.Black; currentStateFunc().CurrentStrokingColor = GrayColor.Black;
@@ -45,12 +53,40 @@
} }
} }
public void SetNonStrokingColorspace(NameToken colorspace) AdvancedStrokingColorSpace = null;
{
if (colorspace.TryMapToColorSpace(out var colorspaceActual)) if (colorspace.TryMapToColorSpace(out var colorspaceActual))
{ {
CurrentNonStrokingColorSpace = colorspaceActual; CurrentStrokingColorSpace = colorspaceActual;
switch (colorspaceActual) }
else if (resourceStore.TryGetNamedColorSpace(colorspace, out var namedColorSpace))
{
if (namedColorSpace.Name == NameToken.Separation && namedColorSpace.Data is ArrayToken separationArray
&& separationArray.Length == 4
&& separationArray[2] is NameToken alternativeColorSpaceName
&& alternativeColorSpaceName.TryMapToColorSpace(out colorspaceActual))
{
AdvancedStrokingColorSpace = namedColorSpace.Name;
CurrentStrokingColorSpace = colorspaceActual;
}
else
{
DefaultColorSpace();
}
}
else
{
DefaultColorSpace();
}
}
public void SetNonStrokingColorspace(NameToken colorspace)
{
void DefaultColorSpace(ColorSpace? colorSpaceActual = null)
{
if (colorSpaceActual.HasValue)
{
switch (colorSpaceActual)
{ {
case ColorSpace.DeviceGray: case ColorSpace.DeviceGray:
currentStateFunc().CurrentNonStrokingColor = GrayColor.Black; currentStateFunc().CurrentNonStrokingColor = GrayColor.Black;
@@ -73,6 +109,33 @@
} }
} }
AdvancedNonStrokingColorSpace = null;
if (colorspace.TryMapToColorSpace(out var colorspaceActual))
{
CurrentNonStrokingColorSpace = colorspaceActual;
}
else if (resourceStore.TryGetNamedColorSpace(colorspace, out var namedColorSpace))
{
if (namedColorSpace.Name == NameToken.Separation && namedColorSpace.Data is ArrayToken separationArray
&& separationArray.Length == 4
&& separationArray[2] is NameToken alternativeColorSpaceName
&& alternativeColorSpaceName.TryMapToColorSpace(out var colorSpaceActual))
{
AdvancedNonStrokingColorSpace = namedColorSpace.Name;
CurrentNonStrokingColorSpace = colorSpaceActual;
}
else
{
DefaultColorSpace();
}
}
else
{
DefaultColorSpace();
}
}
public void SetStrokingColorGray(decimal gray) public void SetStrokingColorGray(decimal gray)
{ {
CurrentStrokingColorSpace = ColorSpace.DeviceGray; CurrentStrokingColorSpace = ColorSpace.DeviceGray;

View File

@@ -0,0 +1,22 @@
namespace UglyToad.PdfPig.Graphics.Colors
{
using Tokens;
/// <summary>
/// A color space definition from a resource dictionary.
/// </summary>
internal struct ResourceColorSpace
{
public NameToken Name { get; }
public IToken Data { get; }
public ResourceColorSpace(NameToken name, IToken data)
{
Name = name;
Data = data;
}
public ResourceColorSpace(NameToken name) : this(name, null) { }
}
}

View File

@@ -92,7 +92,7 @@
this.filterProvider = filterProvider ?? throw new ArgumentNullException(nameof(filterProvider)); this.filterProvider = filterProvider ?? throw new ArgumentNullException(nameof(filterProvider));
this.log = log; this.log = log;
graphicsStack.Push(new CurrentGraphicsState()); graphicsStack.Push(new CurrentGraphicsState());
ColorSpaceContext = new ColorSpaceContext(GetCurrentState); ColorSpaceContext = new ColorSpaceContext(GetCurrentState, resourceStore);
} }
public PageContent Process(IReadOnlyList<IGraphicsStateOperation> operations) public PageContent Process(IReadOnlyList<IGraphicsStateOperation> operations)

View File

@@ -18,6 +18,16 @@
/// </summary> /// </summary>
ColorSpace CurrentNonStrokingColorSpace { get; } ColorSpace CurrentNonStrokingColorSpace { get; }
/// <summary>
/// The name of the advanced ColorSpace active for stroking operations, if any.
/// </summary>
NameToken AdvancedStrokingColorSpace { get; }
/// <summary>
/// The name of the advanced ColorSpace active for non-stroking operations, if any.
/// </summary>
NameToken AdvancedNonStrokingColorSpace { get; }
/// <summary> /// <summary>
/// Set the current color space to use for stroking operations. /// Set the current color space to use for stroking operations.
/// </summary> /// </summary>

View File

@@ -42,17 +42,17 @@
return true; return true;
} }
if (!resourceStore.TryGetNamedColorSpace(name, out var colorSpaceNamedToken) || !(colorSpaceNamedToken is NameToken newName)) if (!resourceStore.TryGetNamedColorSpace(name, out var colorSpaceNamedToken))
{ {
return false; return false;
} }
if (newName.TryMapToColorSpace(out colorSpaceResult)) if (colorSpaceNamedToken.Name.TryMapToColorSpace(out colorSpaceResult))
{ {
return true; return true;
} }
if (TryExtendedColorSpaceNameMapping(newName, out colorSpaceResult)) if (TryExtendedColorSpaceNameMapping(colorSpaceNamedToken.Name, out colorSpaceResult))
{ {
return true; return true;
} }

View File

@@ -54,7 +54,8 @@
/// <inheritdoc /> /// <inheritdoc />
public void Run(IOperationContext operationContext) public void Run(IOperationContext operationContext)
{ {
if (operationContext.ColorSpaceContext.CurrentNonStrokingColorSpace.GetFamily() != ColorSpaceFamily.Device) if (operationContext.ColorSpaceContext.CurrentNonStrokingColorSpace.GetFamily() != ColorSpaceFamily.Device
|| operationContext.ColorSpaceContext.AdvancedNonStrokingColorSpace != null)
{ {
return; return;
} }

View File

@@ -54,7 +54,8 @@
/// <inheritdoc /> /// <inheritdoc />
public void Run(IOperationContext operationContext) public void Run(IOperationContext operationContext)
{ {
if (operationContext.ColorSpaceContext.CurrentStrokingColorSpace.GetFamily() != ColorSpaceFamily.Device) if (operationContext.ColorSpaceContext.CurrentStrokingColorSpace.GetFamily() != ColorSpaceFamily.Device
|| operationContext.ColorSpaceContext.AdvancedStrokingColorSpace != null)
{ {
return; return;
} }

View File

@@ -138,6 +138,7 @@
private PageContent GetContent(IReadOnlyList<byte> contentBytes, CropBox cropBox, UserSpaceUnit userSpaceUnit, PageRotationDegrees rotation, bool isLenientParsing) private PageContent GetContent(IReadOnlyList<byte> contentBytes, CropBox cropBox, UserSpaceUnit userSpaceUnit, PageRotationDegrees rotation, bool isLenientParsing)
{ {
var txt = OtherEncodings.BytesAsLatin1String(contentBytes);
var operations = pageContentParser.Parse(new ByteArrayInputBytes(contentBytes)); var operations = pageContentParser.Parse(new ByteArrayInputBytes(contentBytes));
var context = new ContentStreamProcessor(cropBox.Bounds, resourceStore, userSpaceUnit, rotation, isLenientParsing, pdfScanner, var context = new ContentStreamProcessor(cropBox.Bounds, resourceStore, userSpaceUnit, rotation, isLenientParsing, pdfScanner,

View File

@@ -122,12 +122,12 @@
return true; return true;
} }
if (!resourceStore.TryGetNamedColorSpace(name, out var colorSpaceNamedToken) || !(colorSpaceNamedToken is NameToken newName)) if (!resourceStore.TryGetNamedColorSpace(name, out var colorSpaceNamedToken))
{ {
return false; return false;
} }
return newName.TryMapToColorSpace(out colorSpaceResult); return colorSpaceNamedToken.Name.TryMapToColorSpace(out colorSpaceResult);
} }
} }
} }