mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-09-21 04:17:57 +08:00
add support for alternative colorspace in separation colorspaces #89
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
namespace UglyToad.PdfPig.Tests.Graphics
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Content;
|
||||
using PdfPig.Geometry;
|
||||
using PdfPig.Graphics;
|
||||
using PdfPig.IO;
|
||||
using PdfPig.Tokens;
|
||||
using PdfPig.Core;
|
||||
using PdfPig.Fonts;
|
||||
using Tokens;
|
||||
|
||||
internal class TestOperationContext : IOperationContext
|
||||
{
|
||||
@@ -29,7 +32,7 @@
|
||||
{
|
||||
StateStack.Push(new CurrentGraphicsState());
|
||||
CurrentPath = new PdfPath();
|
||||
ColorSpaceContext = new ColorSpaceContext(GetCurrentState);
|
||||
ColorSpaceContext = new ColorSpaceContext(GetCurrentState, new ResourceStore(new TestPdfTokenScanner(), new TestFontFactory()));
|
||||
}
|
||||
|
||||
public CurrentGraphicsState GetCurrentState()
|
||||
@@ -89,5 +92,13 @@
|
||||
public void EndInlineImage(IReadOnlyList<byte> bytes)
|
||||
{
|
||||
}
|
||||
|
||||
private class TestFontFactory : IFontFactory
|
||||
{
|
||||
public IFont Get(DictionaryToken dictionary, bool isLenientParsing)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
namespace UglyToad.PdfPig.Content
|
||||
{
|
||||
using Fonts;
|
||||
using Graphics.Colors;
|
||||
using Tokens;
|
||||
|
||||
internal interface IResourceStore
|
||||
@@ -21,6 +22,6 @@
|
||||
|
||||
IFont GetFontDirectly(IndirectReferenceToken fontReferenceToken, bool isLenientParsing);
|
||||
|
||||
bool TryGetNamedColorSpace(NameToken name, out IToken namedColorSpace);
|
||||
bool TryGetNamedColorSpace(NameToken name, out ResourceColorSpace namedColorSpace);
|
||||
}
|
||||
}
|
@@ -4,6 +4,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Exceptions;
|
||||
using Fonts;
|
||||
using Graphics.Colors;
|
||||
using Parser.Parts;
|
||||
using Tokenization.Scanner;
|
||||
using Tokens;
|
||||
@@ -19,7 +20,7 @@
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -72,7 +73,7 @@
|
||||
|
||||
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))
|
||||
{
|
||||
@@ -88,7 +89,7 @@
|
||||
throw new PdfDocumentFormatException($"Invalid ColorSpace array encountered in page resource dictionary: {colorSpaceArray}.");
|
||||
}
|
||||
|
||||
colorSpaceNames[name] = arrayNamedColorSpace;
|
||||
namedColorSpaces[name] = new ResourceColorSpace(arrayNamedColorSpace, colorSpaceArray);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -158,16 +159,16 @@
|
||||
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)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
if (!colorSpaceNames.TryGetValue(name, out var colorSpaceName))
|
||||
if (!namedColorSpaces.TryGetValue(name, out var colorSpaceName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@@ -8,10 +8,10 @@ namespace UglyToad.PdfPig.Export
|
||||
public interface ITextExporter
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the text representation.
|
||||
/// Get the text representation of a page in a desired format.
|
||||
/// </summary>
|
||||
/// <param name="page"></param>
|
||||
/// <returns></returns>
|
||||
/// <param name="page">The page to convert to the format.</param>
|
||||
/// <returns>The <see langword="string"/> containing the page contents represented in a compatible format.</returns>
|
||||
string Get(Page page);
|
||||
}
|
||||
}
|
||||
|
@@ -2,27 +2,35 @@
|
||||
{
|
||||
using System;
|
||||
using Colors;
|
||||
using Content;
|
||||
using Tokens;
|
||||
|
||||
internal class ColorSpaceContext : IColorSpaceContext
|
||||
{
|
||||
private readonly Func<CurrentGraphicsState> currentStateFunc;
|
||||
private readonly IResourceStore resourceStore;
|
||||
|
||||
public ColorSpace CurrentStrokingColorSpace { 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.resourceStore = resourceStore ?? throw new ArgumentNullException(nameof(resourceStore));
|
||||
}
|
||||
|
||||
public void SetStrokingColorspace(NameToken colorspace)
|
||||
{
|
||||
if (colorspace.TryMapToColorSpace(out var colorspaceActual))
|
||||
void DefaultColorSpace(ColorSpace? colorSpaceActual = null)
|
||||
{
|
||||
CurrentStrokingColorSpace = colorspaceActual;
|
||||
switch (colorspaceActual)
|
||||
if (colorSpaceActual.HasValue)
|
||||
{
|
||||
switch (colorSpaceActual)
|
||||
{
|
||||
case ColorSpace.DeviceGray:
|
||||
currentStateFunc().CurrentStrokingColor = GrayColor.Black;
|
||||
@@ -45,12 +53,40 @@
|
||||
}
|
||||
}
|
||||
|
||||
public void SetNonStrokingColorspace(NameToken colorspace)
|
||||
{
|
||||
AdvancedStrokingColorSpace = null;
|
||||
|
||||
if (colorspace.TryMapToColorSpace(out var colorspaceActual))
|
||||
{
|
||||
CurrentNonStrokingColorSpace = colorspaceActual;
|
||||
switch (colorspaceActual)
|
||||
CurrentStrokingColorSpace = 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:
|
||||
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)
|
||||
{
|
||||
CurrentStrokingColorSpace = ColorSpace.DeviceGray;
|
||||
|
@@ -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) { }
|
||||
}
|
||||
}
|
@@ -92,7 +92,7 @@
|
||||
this.filterProvider = filterProvider ?? throw new ArgumentNullException(nameof(filterProvider));
|
||||
this.log = log;
|
||||
graphicsStack.Push(new CurrentGraphicsState());
|
||||
ColorSpaceContext = new ColorSpaceContext(GetCurrentState);
|
||||
ColorSpaceContext = new ColorSpaceContext(GetCurrentState, resourceStore);
|
||||
}
|
||||
|
||||
public PageContent Process(IReadOnlyList<IGraphicsStateOperation> operations)
|
||||
|
@@ -18,6 +18,16 @@
|
||||
/// </summary>
|
||||
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>
|
||||
/// Set the current color space to use for stroking operations.
|
||||
/// </summary>
|
||||
|
@@ -42,17 +42,17 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!resourceStore.TryGetNamedColorSpace(name, out var colorSpaceNamedToken) || !(colorSpaceNamedToken is NameToken newName))
|
||||
if (!resourceStore.TryGetNamedColorSpace(name, out var colorSpaceNamedToken))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (newName.TryMapToColorSpace(out colorSpaceResult))
|
||||
if (colorSpaceNamedToken.Name.TryMapToColorSpace(out colorSpaceResult))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TryExtendedColorSpaceNameMapping(newName, out colorSpaceResult))
|
||||
if (TryExtendedColorSpaceNameMapping(colorSpaceNamedToken.Name, out colorSpaceResult))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@@ -54,7 +54,8 @@
|
||||
/// <inheritdoc />
|
||||
public void Run(IOperationContext operationContext)
|
||||
{
|
||||
if (operationContext.ColorSpaceContext.CurrentNonStrokingColorSpace.GetFamily() != ColorSpaceFamily.Device)
|
||||
if (operationContext.ColorSpaceContext.CurrentNonStrokingColorSpace.GetFamily() != ColorSpaceFamily.Device
|
||||
|| operationContext.ColorSpaceContext.AdvancedNonStrokingColorSpace != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@@ -54,7 +54,8 @@
|
||||
/// <inheritdoc />
|
||||
public void Run(IOperationContext operationContext)
|
||||
{
|
||||
if (operationContext.ColorSpaceContext.CurrentStrokingColorSpace.GetFamily() != ColorSpaceFamily.Device)
|
||||
if (operationContext.ColorSpaceContext.CurrentStrokingColorSpace.GetFamily() != ColorSpaceFamily.Device
|
||||
|| operationContext.ColorSpaceContext.AdvancedStrokingColorSpace != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@@ -138,6 +138,7 @@
|
||||
|
||||
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 context = new ContentStreamProcessor(cropBox.Bounds, resourceStore, userSpaceUnit, rotation, isLenientParsing, pdfScanner,
|
||||
|
@@ -122,12 +122,12 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!resourceStore.TryGetNamedColorSpace(name, out var colorSpaceNamedToken) || !(colorSpaceNamedToken is NameToken newName))
|
||||
if (!resourceStore.TryGetNamedColorSpace(name, out var colorSpaceNamedToken))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return newName.TryMapToColorSpace(out colorSpaceResult);
|
||||
return colorSpaceNamedToken.Name.TryMapToColorSpace(out colorSpaceResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user