mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-04-05 20:55:01 +08:00
Handle many functions in Shading
This commit is contained in:
parent
0b8252e930
commit
9acfac4fdf
Binary file not shown.
@ -30,5 +30,15 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AxialRadialTensorProductManyFunctions2()
|
||||
{
|
||||
// We just check pages can be parsed correctly for now
|
||||
using (var document = PdfDocument.Open(IntegrationHelpers.GetDocumentPath("iron-ore-q2-q3-2013.pdf")))
|
||||
{
|
||||
var page = document.GetPage(8); // Should not throw
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -71,6 +71,53 @@
|
||||
BBox = bbox;
|
||||
Background = background;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The shading's function(s), if any.
|
||||
/// </summary>
|
||||
public abstract PdfFunction[] Functions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Convert the input values using the functions of the shading.
|
||||
/// </summary>
|
||||
public double[] Eval(params double[] input)
|
||||
{
|
||||
if (Functions == null || Functions.Length == 0)
|
||||
{
|
||||
return input;
|
||||
}
|
||||
else if (Functions.Length == 1)
|
||||
{
|
||||
return Clamp(Functions[0].Eval(input));
|
||||
}
|
||||
|
||||
double[] returnValues = new double[Functions.Length];
|
||||
for (int i = 0; i < Functions.Length; i++)
|
||||
{
|
||||
double[] newValue = Functions[i].Eval(input);
|
||||
returnValues[i] = newValue[0]; // 1-out functions
|
||||
}
|
||||
return Clamp(returnValues);
|
||||
}
|
||||
|
||||
private static double[] Clamp(double[] input)
|
||||
{
|
||||
// From the PDF spec:
|
||||
// "If the value returned by the function for a given colour component
|
||||
// is out of range, it shall be adjusted to the nearest valid value."
|
||||
for (int i = 0; i < input.Length; ++i)
|
||||
{
|
||||
if (input[i] < 0)
|
||||
{
|
||||
input[i] = 0;
|
||||
}
|
||||
else if (input[i] > 1)
|
||||
{
|
||||
input[i] = 1;
|
||||
}
|
||||
}
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -104,19 +151,19 @@
|
||||
/// If the value returned by the function for a given colour component is out of
|
||||
/// range, it shall be adjusted to the nearest valid value.
|
||||
/// </summary>
|
||||
public PdfFunction Function { get; }
|
||||
public override PdfFunction[] Functions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="FunctionBasedShading"/>.
|
||||
/// </summary>
|
||||
public FunctionBasedShading(bool antiAlias, DictionaryToken shadingDictionary,
|
||||
ColorSpaceDetails colorSpace, PdfRectangle? bbox, double[] background, double[] domain,
|
||||
TransformationMatrix matrix, PdfFunction function)
|
||||
TransformationMatrix matrix, PdfFunction[] functions)
|
||||
: base(ShadingType.FunctionBased, antiAlias, shadingDictionary, colorSpace, bbox, background)
|
||||
{
|
||||
Domain = domain;
|
||||
Matrix = matrix;
|
||||
Function = function;
|
||||
Functions = functions;
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,7 +195,7 @@
|
||||
/// returned by the function for a given colour component is out of range, it shall be adjusted
|
||||
/// to the nearest valid value.
|
||||
/// </summary>
|
||||
public PdfFunction Function { get; }
|
||||
public override PdfFunction[] Functions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// (Optional) An array of two boolean values specifying whether to extend the shading beyond the starting
|
||||
@ -161,12 +208,12 @@
|
||||
/// </summary>
|
||||
public AxialShading(bool antiAlias, DictionaryToken shadingDictionary,
|
||||
ColorSpaceDetails colorSpace, PdfRectangle? bbox, double[] background,
|
||||
double[] coords, double[] domain, PdfFunction function, bool[] extend)
|
||||
double[] coords, double[] domain, PdfFunction[] functions, bool[] extend)
|
||||
: base(ShadingType.Axial, antiAlias, shadingDictionary, colorSpace, bbox, background)
|
||||
{
|
||||
Coords = coords;
|
||||
Domain = domain;
|
||||
Function = function;
|
||||
Functions = functions;
|
||||
Extend = extend;
|
||||
}
|
||||
}
|
||||
@ -205,7 +252,7 @@
|
||||
/// shading dictionary. If the value returned by the function for a given colour component
|
||||
/// is out of range, it shall be adjusted to the nearest valid value.
|
||||
/// </summary>
|
||||
public PdfFunction Function { get; }
|
||||
public override PdfFunction[] Functions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// (Optional) An array of two boolean values specifying whether to extend the shading beyond the starting
|
||||
@ -221,12 +268,12 @@
|
||||
/// </summary>
|
||||
public RadialShading(bool antiAlias, DictionaryToken shadingDictionary,
|
||||
ColorSpaceDetails colorSpace, PdfRectangle? bbox, double[] background,
|
||||
double[] coords, double[] domain, PdfFunction function, bool[] extend)
|
||||
double[] coords, double[] domain, PdfFunction[] functions, bool[] extend)
|
||||
: base(ShadingType.Radial, antiAlias, shadingDictionary, colorSpace, bbox, background)
|
||||
{
|
||||
Coords = coords;
|
||||
Domain = domain;
|
||||
Function = function;
|
||||
Functions = functions;
|
||||
Extend = extend;
|
||||
}
|
||||
}
|
||||
@ -280,21 +327,21 @@
|
||||
/// to the nearest valid value.
|
||||
/// This entry shall not be used with an Indexed colour space.
|
||||
/// </summary>
|
||||
public PdfFunction Function { get; }
|
||||
public override PdfFunction[] Functions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="FreeFormGouraudShading"/>.
|
||||
/// </summary>
|
||||
public FreeFormGouraudShading(bool antiAlias, StreamToken shadingStream,
|
||||
ColorSpaceDetails colorSpace, PdfRectangle? bbox, double[] background,
|
||||
int bitsPerCoordinate, int bitsPerComponent, int bitsPerFlag, double[] decode, PdfFunction function)
|
||||
int bitsPerCoordinate, int bitsPerComponent, int bitsPerFlag, double[] decode, PdfFunction[] functions)
|
||||
: base(ShadingType.FreeFormGouraud, antiAlias, shadingStream.StreamDictionary, colorSpace, bbox, background)
|
||||
{
|
||||
BitsPerCoordinate = bitsPerCoordinate;
|
||||
BitsPerComponent = bitsPerComponent;
|
||||
BitsPerFlag = bitsPerFlag;
|
||||
Decode = decode;
|
||||
Function = function;
|
||||
Functions = functions;
|
||||
}
|
||||
}
|
||||
|
||||
@ -343,21 +390,21 @@
|
||||
/// component is out of range, it shall be adjusted to the nearest valid value.
|
||||
/// This entry shall not be used with an Indexed colour space.
|
||||
/// </summary>
|
||||
public PdfFunction Function { get; }
|
||||
public override PdfFunction[] Functions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="LatticeFormGouraudShading"/>.
|
||||
/// </summary>
|
||||
public LatticeFormGouraudShading(bool antiAlias, StreamToken shadingStream,
|
||||
ColorSpaceDetails colorSpace, PdfRectangle? bbox, double[] background,
|
||||
int bitsPerCoordinate, int bitsPerComponent, int verticesPerRow, double[] decode, PdfFunction function)
|
||||
int bitsPerCoordinate, int bitsPerComponent, int verticesPerRow, double[] decode, PdfFunction[] functions)
|
||||
: base(ShadingType.LatticeFormGouraud, antiAlias, shadingStream.StreamDictionary, colorSpace, bbox, background)
|
||||
{
|
||||
BitsPerCoordinate = bitsPerCoordinate;
|
||||
BitsPerComponent = bitsPerComponent;
|
||||
VerticesPerRow = verticesPerRow;
|
||||
Decode = decode;
|
||||
Function = function;
|
||||
Functions = functions;
|
||||
}
|
||||
}
|
||||
|
||||
@ -408,21 +455,21 @@
|
||||
/// shall be adjusted to the nearest valid value.
|
||||
/// This entry shall not be used with an Indexed colour space.
|
||||
/// </summary>
|
||||
public PdfFunction Function { get; }
|
||||
public override PdfFunction[] Functions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="CoonsPatchMeshesShading"/>.
|
||||
/// </summary>
|
||||
public CoonsPatchMeshesShading(bool antiAlias, StreamToken shadingStream,
|
||||
ColorSpaceDetails colorSpace, PdfRectangle? bbox, double[] background,
|
||||
int bitsPerCoordinate, int bitsPerComponent, int bitsPerFlag, double[] decode, PdfFunction function)
|
||||
int bitsPerCoordinate, int bitsPerComponent, int bitsPerFlag, double[] decode, PdfFunction[] functions)
|
||||
: base(ShadingType.CoonsPatch, antiAlias, shadingStream.StreamDictionary, colorSpace, bbox, background)
|
||||
{
|
||||
BitsPerCoordinate = bitsPerCoordinate;
|
||||
BitsPerComponent = bitsPerComponent;
|
||||
BitsPerFlag = bitsPerFlag;
|
||||
Decode = decode;
|
||||
Function = function;
|
||||
Functions = functions;
|
||||
}
|
||||
}
|
||||
|
||||
@ -473,21 +520,21 @@
|
||||
/// shall be adjusted to the nearest valid value.
|
||||
/// This entry shall not be used with an Indexed colour space.
|
||||
/// </summary>
|
||||
public PdfFunction Function { get; }
|
||||
public override PdfFunction[] Functions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="TensorProductPatchMeshesShading"/>.
|
||||
/// </summary>
|
||||
public TensorProductPatchMeshesShading(bool antiAlias, StreamToken shadingStream,
|
||||
ColorSpaceDetails colorSpace, PdfRectangle? bbox, double[] background,
|
||||
int bitsPerCoordinate, int bitsPerComponent, int bitsPerFlag, double[] decode, PdfFunction function)
|
||||
int bitsPerCoordinate, int bitsPerComponent, int bitsPerFlag, double[] decode, PdfFunction[] functions)
|
||||
: base(ShadingType.TensorProductPatch, antiAlias, shadingStream.StreamDictionary, colorSpace, bbox, background)
|
||||
{
|
||||
BitsPerCoordinate = bitsPerCoordinate;
|
||||
BitsPerComponent = bitsPerComponent;
|
||||
BitsPerFlag = bitsPerFlag;
|
||||
Decode = decode;
|
||||
Function = function;
|
||||
Functions = functions;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
using UglyToad.PdfPig.Filters;
|
||||
using UglyToad.PdfPig.Functions;
|
||||
using UglyToad.PdfPig.Graphics.Colors;
|
||||
using UglyToad.PdfPig.Parser.Parts;
|
||||
using UglyToad.PdfPig.Tokenization.Scanner;
|
||||
using UglyToad.PdfPig.Tokens;
|
||||
|
||||
@ -111,6 +112,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
private static PdfFunction[] GetFunctions(IToken functionToken, IPdfTokenScanner scanner, ILookupFilterProvider filterProvider)
|
||||
{
|
||||
if (DirectObjectFinder.TryGet(functionToken, scanner, out ArrayToken fa))
|
||||
{
|
||||
var functionArray = new PdfFunction[fa.Length];
|
||||
for (int i = 0; i < fa.Length; i++)
|
||||
{
|
||||
functionArray[i] = PdfFunctionParser.Create(fa[i], scanner, filterProvider);
|
||||
}
|
||||
return functionArray;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new PdfFunction[] { PdfFunctionParser.Create(functionToken, scanner, filterProvider) };
|
||||
}
|
||||
}
|
||||
|
||||
private static FunctionBasedShading CreateFunctionBasedShading(DictionaryToken shadingDictionary, ColorSpaceDetails colorSpace,
|
||||
double[] background, PdfRectangle? bbox, bool antiAlias, IPdfTokenScanner scanner, ILookupFilterProvider filterProvider)
|
||||
{
|
||||
@ -141,9 +159,9 @@
|
||||
throw new ArgumentNullException($"'{NameToken.Function}' is required for shading type '{ShadingType.FunctionBased}'.");
|
||||
}
|
||||
|
||||
PdfFunction function = PdfFunctionParser.Create(shadingDictionary.Data[NameToken.Function], scanner, filterProvider);
|
||||
PdfFunction[] functions = GetFunctions(shadingDictionary.Data[NameToken.Function], scanner, filterProvider);
|
||||
|
||||
return new FunctionBasedShading(antiAlias, shadingDictionary, colorSpace, bbox, background, domain, matrix, function);
|
||||
return new FunctionBasedShading(antiAlias, shadingDictionary, colorSpace, bbox, background, domain, matrix, functions);
|
||||
}
|
||||
|
||||
private static AxialShading CreateAxialShading(DictionaryToken shadingDictionary, ColorSpaceDetails colorSpace,
|
||||
@ -175,7 +193,7 @@
|
||||
throw new ArgumentNullException($"{NameToken.Function} is required for shading type '{ShadingType.Axial}'.");
|
||||
}
|
||||
|
||||
PdfFunction function = PdfFunctionParser.Create(shadingDictionary.Data[NameToken.Function], scanner, filterProvider);
|
||||
PdfFunction[] functions = GetFunctions(shadingDictionary.Data[NameToken.Function], scanner, filterProvider);
|
||||
|
||||
bool[] extend = new bool[] { false, false }; // Default values
|
||||
if (shadingDictionary.TryGet<ArrayToken>(NameToken.Extend, scanner, out var extendToken))
|
||||
@ -183,7 +201,7 @@
|
||||
extend = extendToken.Data.OfType<BooleanToken>().Select(v => v.Data).ToArray();
|
||||
}
|
||||
|
||||
return new AxialShading(antiAlias, shadingDictionary, colorSpace, bbox, background, coords, domain, function, extend);
|
||||
return new AxialShading(antiAlias, shadingDictionary, colorSpace, bbox, background, coords, domain, functions, extend);
|
||||
}
|
||||
|
||||
private static RadialShading CreateRadialShading(DictionaryToken shadingDictionary, ColorSpaceDetails colorSpace,
|
||||
@ -215,7 +233,7 @@
|
||||
throw new ArgumentNullException($"{NameToken.Function} is required for shading type '{ShadingType.Radial}'.");
|
||||
}
|
||||
|
||||
PdfFunction function = PdfFunctionParser.Create(shadingDictionary.Data[NameToken.Function], scanner, filterProvider);
|
||||
PdfFunction[] functions = GetFunctions(shadingDictionary.Data[NameToken.Function], scanner, filterProvider);
|
||||
|
||||
bool[] extend = new bool[] { false, false }; // Default values
|
||||
if (shadingDictionary.TryGet<ArrayToken>(NameToken.Extend, scanner, out var extendToken))
|
||||
@ -223,7 +241,7 @@
|
||||
extend = extendToken.Data.OfType<BooleanToken>().Select(v => v.Data).ToArray();
|
||||
}
|
||||
|
||||
return new RadialShading(antiAlias, shadingDictionary, colorSpace, bbox, background, coords, domain, function, extend);
|
||||
return new RadialShading(antiAlias, shadingDictionary, colorSpace, bbox, background, coords, domain, functions, extend);
|
||||
}
|
||||
|
||||
private static FreeFormGouraudShading CreateFreeFormGouraudShadedTriangleMeshesShading(StreamToken shadingStream,
|
||||
@ -269,14 +287,14 @@
|
||||
throw new ArgumentNullException($"{NameToken.Decode} is required for shading type '{ShadingType.FreeFormGouraud}'.");
|
||||
}
|
||||
|
||||
PdfFunction function = null; // Optional
|
||||
PdfFunction[] functions = null; // Optional
|
||||
if (shadingStream.StreamDictionary.ContainsKey(NameToken.Function))
|
||||
{
|
||||
function = PdfFunctionParser.Create(shadingStream.StreamDictionary.Data[NameToken.Function], scanner, filterProvider);
|
||||
functions = GetFunctions(shadingStream.StreamDictionary.Data[NameToken.Function], scanner, filterProvider);
|
||||
}
|
||||
|
||||
return new FreeFormGouraudShading(antiAlias, shadingStream, colorSpace, bbox, background,
|
||||
bitsPerCoordinate, bitsPerComponent, bitsPerFlag, decode, function);
|
||||
bitsPerCoordinate, bitsPerComponent, bitsPerFlag, decode, functions);
|
||||
}
|
||||
|
||||
private static LatticeFormGouraudShading CreateLatticeFormGouraudShadedTriangleMeshesShading(StreamToken shadingStream,
|
||||
@ -322,14 +340,14 @@
|
||||
throw new ArgumentNullException($"{NameToken.Decode} is required for shading type '{ShadingType.LatticeFormGouraud}'.");
|
||||
}
|
||||
|
||||
PdfFunction function = null; // Optional
|
||||
PdfFunction[] functions = null; // Optional
|
||||
if (shadingStream.StreamDictionary.ContainsKey(NameToken.Function))
|
||||
{
|
||||
function = PdfFunctionParser.Create(shadingStream.StreamDictionary.Data[NameToken.Function], scanner, filterProvider);
|
||||
functions = GetFunctions(shadingStream.StreamDictionary.Data[NameToken.Function], scanner, filterProvider);
|
||||
}
|
||||
|
||||
return new LatticeFormGouraudShading(antiAlias, shadingStream, colorSpace, bbox, background,
|
||||
bitsPerCoordinate, bitsPerComponent, verticesPerRow, decode, function);
|
||||
bitsPerCoordinate, bitsPerComponent, verticesPerRow, decode, functions);
|
||||
}
|
||||
|
||||
private static CoonsPatchMeshesShading CreateCoonsPatchMeshesShading(StreamToken shadingStream,
|
||||
@ -375,14 +393,14 @@
|
||||
throw new ArgumentNullException($"{NameToken.Decode} is required for shading type '{ShadingType.CoonsPatch}'.");
|
||||
}
|
||||
|
||||
PdfFunction function = null; // Optional
|
||||
PdfFunction[] functions = null; // Optional
|
||||
if (shadingStream.StreamDictionary.ContainsKey(NameToken.Function))
|
||||
{
|
||||
function = PdfFunctionParser.Create(shadingStream.StreamDictionary.Data[NameToken.Function], scanner, filterProvider);
|
||||
functions = GetFunctions(shadingStream.StreamDictionary.Data[NameToken.Function], scanner, filterProvider);
|
||||
}
|
||||
|
||||
return new CoonsPatchMeshesShading(antiAlias, shadingStream, colorSpace, bbox, background,
|
||||
bitsPerCoordinate, bitsPerComponent, bitsPerFlag, decode, function);
|
||||
bitsPerCoordinate, bitsPerComponent, bitsPerFlag, decode, functions);
|
||||
}
|
||||
|
||||
private static TensorProductPatchMeshesShading CreateTensorProductPatchMeshesShading(StreamToken shadingStream,
|
||||
@ -428,14 +446,14 @@
|
||||
throw new ArgumentNullException($"{NameToken.Decode} is required for shading type '{ShadingType.TensorProductPatch}'.");
|
||||
}
|
||||
|
||||
PdfFunction function = null; // Optional
|
||||
PdfFunction[] functions = null; // Optional
|
||||
if (shadingStream.StreamDictionary.ContainsKey(NameToken.Function))
|
||||
{
|
||||
function = PdfFunctionParser.Create(shadingStream.StreamDictionary.Data[NameToken.Function], scanner, filterProvider);
|
||||
functions = GetFunctions(shadingStream.StreamDictionary.Data[NameToken.Function], scanner, filterProvider);
|
||||
}
|
||||
|
||||
return new TensorProductPatchMeshesShading(antiAlias, shadingStream, colorSpace, bbox, background,
|
||||
bitsPerCoordinate, bitsPerComponent, bitsPerFlag, decode, function);
|
||||
bitsPerCoordinate, bitsPerComponent, bitsPerFlag, decode, functions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user