mirror of
https://github.com/UglyToad/PdfPig.git
synced 2026-03-10 00:23:29 +08:00
588 lines
29 KiB
C#
588 lines
29 KiB
C#
namespace UglyToad.PdfPig.Util
|
|
{
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Linq;
|
|
using Content;
|
|
using Filters;
|
|
using Graphics.Colors;
|
|
using Parser.Parts;
|
|
using Tokenization.Scanner;
|
|
using Tokens;
|
|
using UglyToad.PdfPig.Functions;
|
|
|
|
internal static class ColorSpaceMapper
|
|
{
|
|
public static bool TryMap(NameToken name, IResourceStore resourceStore, out ColorSpace colorSpaceResult)
|
|
{
|
|
if (name.TryMapToColorSpace(out colorSpaceResult))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (!resourceStore.TryGetNamedColorSpace(name, out var colorSpaceNamedToken))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (colorSpaceNamedToken.Name.TryMapToColorSpace(out colorSpaceResult))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal static class ColorSpaceDetailsParser
|
|
{
|
|
public static ColorSpaceDetails GetColorSpaceDetails(ColorSpace? colorSpace,
|
|
DictionaryToken imageDictionary,
|
|
IPdfTokenScanner scanner,
|
|
IResourceStore resourceStore,
|
|
ILookupFilterProvider filterProvider,
|
|
bool cannotRecurse = false)
|
|
{
|
|
if ((imageDictionary.TryGet(NameToken.ImageMask, scanner, out BooleanToken isImageMask) && isImageMask.Data) ||
|
|
(imageDictionary.TryGet(NameToken.Im, scanner, out BooleanToken isImMask) && isImMask.Data) ||
|
|
filterProvider.GetFilters(imageDictionary, scanner).OfType<CcittFaxDecodeFilter>().Any())
|
|
{
|
|
if (cannotRecurse)
|
|
{
|
|
// Not sure if always Gray, it's just a single color.
|
|
return DeviceGrayColorSpaceDetails.Instance;
|
|
}
|
|
|
|
var colorSpaceDetails = GetColorSpaceDetails(colorSpace, imageDictionary.Without(NameToken.Filter).Without(NameToken.F), scanner, resourceStore, filterProvider, true);
|
|
return IndexedColorSpaceDetails.Stencil(colorSpaceDetails);
|
|
}
|
|
|
|
if (!colorSpace.HasValue)
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
switch (colorSpace.Value)
|
|
{
|
|
case ColorSpace.DeviceGray:
|
|
return DeviceGrayColorSpaceDetails.Instance;
|
|
case ColorSpace.DeviceRGB:
|
|
return DeviceRgbColorSpaceDetails.Instance;
|
|
case ColorSpace.DeviceCMYK:
|
|
return DeviceCmykColorSpaceDetails.Instance;
|
|
case ColorSpace.CalGray:
|
|
{
|
|
if (!TryGetColorSpaceArray(imageDictionary, resourceStore, scanner, out var colorSpaceArray)
|
|
|| colorSpaceArray.Length != 2)
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
var first = colorSpaceArray[0] as NameToken;
|
|
|
|
if (first is null || !ColorSpaceMapper.TryMap(first, resourceStore, out var innerColorSpace)
|
|
|| innerColorSpace != ColorSpace.CalGray)
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
var second = colorSpaceArray[1];
|
|
|
|
// WhitePoint is required
|
|
if (!DirectObjectFinder.TryGet(second, scanner, out DictionaryToken? dictionaryToken) ||
|
|
!dictionaryToken.TryGet(NameToken.WhitePoint, scanner, out ArrayToken? whitePointToken))
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
var whitePoint = whitePointToken.Data.OfType<NumericToken>().Select(x => x.Double).ToArray();
|
|
|
|
// BlackPoint is optional
|
|
double[]? blackPoint = null;
|
|
if (dictionaryToken.TryGet(NameToken.BlackPoint, scanner, out ArrayToken? blackPointToken))
|
|
{
|
|
blackPoint = blackPointToken.Data.OfType<NumericToken>().Select(x => x.Double).ToArray();
|
|
}
|
|
|
|
// Gamma is optional
|
|
double? gamma = null;
|
|
if (dictionaryToken.TryGet(NameToken.Gamma, scanner, out NumericToken? gammaToken))
|
|
{
|
|
gamma = gammaToken.Double;
|
|
}
|
|
|
|
return new CalGrayColorSpaceDetails(whitePoint, blackPoint, gamma);
|
|
}
|
|
case ColorSpace.CalRGB:
|
|
{
|
|
if (!TryGetColorSpaceArray(imageDictionary, resourceStore, scanner, out var colorSpaceArray)
|
|
|| colorSpaceArray.Length != 2)
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
var first = colorSpaceArray[0] as NameToken;
|
|
|
|
if (first is null || !ColorSpaceMapper.TryMap(first, resourceStore, out var innerColorSpace)
|
|
|| innerColorSpace != ColorSpace.CalRGB)
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
var second = colorSpaceArray[1];
|
|
|
|
// WhitePoint is required
|
|
if (!DirectObjectFinder.TryGet(second, scanner, out DictionaryToken? dictionaryToken) ||
|
|
!dictionaryToken.TryGet(NameToken.WhitePoint, scanner, out ArrayToken? whitePointToken))
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
var whitePoint = whitePointToken.Data.OfType<NumericToken>().Select(x => x.Double).ToArray();
|
|
|
|
// BlackPoint is optional
|
|
double[]? blackPoint = null;
|
|
if (dictionaryToken.TryGet(NameToken.BlackPoint, scanner, out ArrayToken? blackPointToken))
|
|
{
|
|
blackPoint = blackPointToken.Data.OfType<NumericToken>().Select(static x => x.Double).ToArray();
|
|
}
|
|
|
|
// Gamma is optional
|
|
double[]? gamma = null;
|
|
if (dictionaryToken.TryGet(NameToken.Gamma, scanner, out ArrayToken? gammaToken))
|
|
{
|
|
gamma = gammaToken.Data.OfType<NumericToken>().Select(static x => x.Double).ToArray();
|
|
}
|
|
|
|
// Matrix is optional
|
|
double[]? matrix = null;
|
|
if (dictionaryToken.TryGet(NameToken.Matrix, scanner, out ArrayToken? matrixToken))
|
|
{
|
|
matrix = matrixToken.Data.OfType<NumericToken>().Select(static x => x.Double).ToArray();
|
|
}
|
|
|
|
return new CalRGBColorSpaceDetails(whitePoint, blackPoint, gamma, matrix);
|
|
}
|
|
case ColorSpace.Lab:
|
|
{
|
|
if (!TryGetColorSpaceArray(imageDictionary, resourceStore, scanner, out var colorSpaceArray)
|
|
|| colorSpaceArray.Length != 2)
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
var first = colorSpaceArray[0] as NameToken;
|
|
|
|
if (first is null || !ColorSpaceMapper.TryMap(first, resourceStore, out var innerColorSpace)
|
|
|| innerColorSpace != ColorSpace.Lab)
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
var second = colorSpaceArray[1];
|
|
|
|
// WhitePoint is required
|
|
if (!DirectObjectFinder.TryGet(second, scanner, out DictionaryToken? dictionaryToken) ||
|
|
!dictionaryToken.TryGet(NameToken.WhitePoint, scanner, out ArrayToken? whitePointToken))
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
var whitePoint = whitePointToken.Data.OfType<NumericToken>().Select(x => x.Double).ToArray();
|
|
|
|
// BlackPoint is optional
|
|
double[]? blackPoint = null;
|
|
if (dictionaryToken.TryGet(NameToken.BlackPoint, scanner, out ArrayToken? blackPointToken))
|
|
{
|
|
blackPoint = blackPointToken.Data.OfType<NumericToken>().Select(x => x.Double).ToArray();
|
|
}
|
|
|
|
// Matrix is optional
|
|
double[]? matrix = null;
|
|
if (dictionaryToken.TryGet(NameToken.Matrix, scanner, out ArrayToken? matrixToken))
|
|
{
|
|
matrix = matrixToken.Data.OfType<NumericToken>().Select(x => x.Double).ToArray();
|
|
}
|
|
|
|
return new LabColorSpaceDetails(whitePoint, blackPoint, matrix);
|
|
}
|
|
case ColorSpace.ICCBased:
|
|
{
|
|
if (!TryGetColorSpaceArray(imageDictionary, resourceStore, scanner, out var colorSpaceArray)
|
|
|| colorSpaceArray.Length != 2)
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
var first = colorSpaceArray[0] as NameToken;
|
|
|
|
if (first is null || !ColorSpaceMapper.TryMap(first, resourceStore, out var innerColorSpace)
|
|
|| innerColorSpace != ColorSpace.ICCBased)
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
var second = colorSpaceArray[1];
|
|
|
|
// N is required
|
|
if (!DirectObjectFinder.TryGet(second, scanner, out StreamToken? streamToken) ||
|
|
!streamToken.StreamDictionary.TryGet(NameToken.N, scanner, out NumericToken? numeric))
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
// Alternate is optional
|
|
ColorSpaceDetails? alternateColorSpaceDetails = null;
|
|
if (streamToken.StreamDictionary.TryGet(NameToken.Alternate, out NameToken alternateColorSpaceNameToken) &&
|
|
ColorSpaceMapper.TryMap(alternateColorSpaceNameToken, resourceStore, out var alternateColorSpace))
|
|
{
|
|
alternateColorSpaceDetails =
|
|
GetColorSpaceDetails(alternateColorSpace, imageDictionary, scanner, resourceStore, filterProvider, true);
|
|
}
|
|
|
|
// Range is optional
|
|
double[]? range = null;
|
|
if (streamToken.StreamDictionary.TryGet(NameToken.Range, scanner, out ArrayToken? arrayToken))
|
|
{
|
|
range = arrayToken.Data.OfType<NumericToken>().Select(x => x.Double).ToArray();
|
|
}
|
|
|
|
// Metadata is optional
|
|
XmpMetadata? metadata = null;
|
|
if (streamToken.StreamDictionary.TryGet(NameToken.Metadata, scanner, out StreamToken? metadataStream))
|
|
{
|
|
metadata = new XmpMetadata(metadataStream, filterProvider, scanner);
|
|
}
|
|
|
|
return new ICCBasedColorSpaceDetails(numeric.Int, alternateColorSpaceDetails, range, metadata);
|
|
}
|
|
case ColorSpace.Indexed:
|
|
{
|
|
if (cannotRecurse)
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
if (!TryGetColorSpaceArray(imageDictionary, resourceStore, scanner, out var colorSpaceArray)
|
|
|| colorSpaceArray.Length != 4)
|
|
{
|
|
// Error instead?
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
var first = colorSpaceArray[0] as NameToken;
|
|
|
|
if (first is null || !ColorSpaceMapper.TryMap(first, resourceStore, out var innerColorSpace)
|
|
|| innerColorSpace != ColorSpace.Indexed)
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
var second = colorSpaceArray[1];
|
|
|
|
ColorSpaceDetails baseDetails;
|
|
|
|
if (DirectObjectFinder.TryGet(second, scanner, out NameToken? baseColorSpaceNameToken)
|
|
&& ColorSpaceMapper.TryMap(baseColorSpaceNameToken, resourceStore, out var baseColorSpaceName))
|
|
{
|
|
baseDetails = GetColorSpaceDetails(
|
|
baseColorSpaceName,
|
|
imageDictionary,
|
|
scanner,
|
|
resourceStore,
|
|
filterProvider,
|
|
true);
|
|
}
|
|
else if (DirectObjectFinder.TryGet(second, scanner, out ArrayToken? baseColorSpaceArrayToken)
|
|
&& baseColorSpaceArrayToken.Length > 0 && baseColorSpaceArrayToken[0] is NameToken baseColorSpaceArrayNameToken
|
|
&& ColorSpaceMapper.TryMap(baseColorSpaceArrayNameToken, resourceStore, out var baseColorSpaceArrayColorSpace))
|
|
{
|
|
var pseudoImageDictionary = new DictionaryToken(
|
|
new Dictionary<NameToken, IToken>
|
|
{
|
|
{ NameToken.ColorSpace, baseColorSpaceArrayToken }
|
|
});
|
|
|
|
baseDetails = GetColorSpaceDetails(
|
|
baseColorSpaceArrayColorSpace,
|
|
pseudoImageDictionary,
|
|
scanner,
|
|
resourceStore,
|
|
filterProvider,
|
|
true);
|
|
}
|
|
else
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
if (baseDetails is UnsupportedColorSpaceDetails)
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
var third = colorSpaceArray[2];
|
|
|
|
if (!DirectObjectFinder.TryGet(third, scanner, out NumericToken? hiValNumericToken))
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
var hival = hiValNumericToken.Int;
|
|
|
|
var fourth = colorSpaceArray[3];
|
|
|
|
byte[] tableBytes;
|
|
|
|
if (DirectObjectFinder.TryGet(fourth, scanner, out HexToken? tableHexToken))
|
|
{
|
|
tableBytes = [.. tableHexToken.Bytes];
|
|
}
|
|
else if (DirectObjectFinder.TryGet(fourth, scanner, out StreamToken? tableStreamToken))
|
|
{
|
|
tableBytes = tableStreamToken.Decode(filterProvider, scanner).Span.ToArray();
|
|
}
|
|
else if (DirectObjectFinder.TryGet(fourth, scanner, out StringToken? stringToken))
|
|
{
|
|
tableBytes = stringToken.GetBytes();
|
|
}
|
|
else
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
return new IndexedColorSpaceDetails(baseDetails, (byte)hival, tableBytes);
|
|
}
|
|
case ColorSpace.Pattern:
|
|
{
|
|
if (cannotRecurse)
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
ColorSpaceDetails underlyingColourSpace = UnsupportedColorSpaceDetails.Instance;
|
|
if (imageDictionary.Data.Count > 0)
|
|
{
|
|
// Pattern color spaces do not always have dictionary token
|
|
if (!TryGetColorSpaceArray(imageDictionary, resourceStore, scanner, out var colorSpaceArray)
|
|
|| (colorSpaceArray.Length != 1 && colorSpaceArray.Length != 2))
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
if (!DirectObjectFinder.TryGet(colorSpaceArray[0], scanner, out NameToken? patternColorSpaceNameToken)
|
|
|| !patternColorSpaceNameToken.Equals(NameToken.Pattern))
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
// Uncoloured Tiling Patterns
|
|
if (colorSpaceArray.Length > 1 && DirectObjectFinder.TryGet(colorSpaceArray[1], scanner, out NameToken? underlyingCsNameToken)
|
|
&& ColorSpaceMapper.TryMap(underlyingCsNameToken, resourceStore, out var underlyingColorSpaceName))
|
|
{
|
|
underlyingColourSpace = GetColorSpaceDetails(
|
|
underlyingColorSpaceName,
|
|
imageDictionary,
|
|
scanner,
|
|
resourceStore,
|
|
filterProvider,
|
|
true);
|
|
}
|
|
}
|
|
return new PatternColorSpaceDetails(resourceStore.GetPatterns(), underlyingColourSpace);
|
|
}
|
|
case ColorSpace.Separation:
|
|
{
|
|
if (!TryGetColorSpaceArray(imageDictionary, resourceStore, scanner, out var colorSpaceArray)
|
|
|| colorSpaceArray.Length != 4)
|
|
{
|
|
// Error instead?
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
if (!DirectObjectFinder.TryGet(colorSpaceArray[0], scanner, out NameToken? separationColorSpaceNameToken)
|
|
|| !separationColorSpaceNameToken.Equals(NameToken.Separation))
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
if (!DirectObjectFinder.TryGet(colorSpaceArray[1], scanner, out NameToken? separationNameToken))
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
ColorSpaceDetails alternateColorSpaceDetails;
|
|
if (DirectObjectFinder.TryGet(colorSpaceArray[2], scanner, out NameToken? alternateNameToken)
|
|
&& ColorSpaceMapper.TryMap(alternateNameToken, resourceStore, out var baseColorSpaceName))
|
|
{
|
|
alternateColorSpaceDetails = GetColorSpaceDetails(
|
|
baseColorSpaceName,
|
|
imageDictionary,
|
|
scanner,
|
|
resourceStore,
|
|
filterProvider,
|
|
true);
|
|
}
|
|
else if (DirectObjectFinder.TryGet(colorSpaceArray[2], scanner, out ArrayToken? alternateArrayToken)
|
|
&& alternateArrayToken.Length > 0
|
|
&& alternateArrayToken[0] is NameToken alternateColorSpaceNameToken
|
|
&& ColorSpaceMapper.TryMap(alternateColorSpaceNameToken, resourceStore, out var alternateArrayColorSpace))
|
|
{
|
|
var pseudoImageDictionary = new DictionaryToken(
|
|
new Dictionary<NameToken, IToken>
|
|
{
|
|
{ NameToken.ColorSpace, alternateArrayToken }
|
|
});
|
|
|
|
alternateColorSpaceDetails = GetColorSpaceDetails(
|
|
alternateArrayColorSpace,
|
|
pseudoImageDictionary,
|
|
scanner,
|
|
resourceStore,
|
|
filterProvider,
|
|
true);
|
|
}
|
|
else
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
PdfFunction function;
|
|
var func = colorSpaceArray[3];
|
|
|
|
if (DirectObjectFinder.TryGet(func, scanner, out DictionaryToken? functionDictionary))
|
|
{
|
|
function = PdfFunctionParser.Create(functionDictionary, scanner, filterProvider);
|
|
}
|
|
else if (DirectObjectFinder.TryGet(func, scanner, out StreamToken? functionStream))
|
|
{
|
|
function = PdfFunctionParser.Create(functionStream, scanner, filterProvider);
|
|
}
|
|
else
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
return new SeparationColorSpaceDetails(separationNameToken, alternateColorSpaceDetails, function);
|
|
}
|
|
case ColorSpace.DeviceN:
|
|
{
|
|
if (!TryGetColorSpaceArray(imageDictionary, resourceStore, scanner, out var colorSpaceArray)
|
|
|| (colorSpaceArray.Length != 4 && colorSpaceArray.Length != 5))
|
|
{
|
|
// Error instead?
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
if (!DirectObjectFinder.TryGet(colorSpaceArray[0], scanner, out NameToken? deviceNColorSpaceNameToken)
|
|
|| !deviceNColorSpaceNameToken.Equals(NameToken.Devicen))
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
if (!DirectObjectFinder.TryGet(colorSpaceArray[1], scanner, out ArrayToken? deviceNNamesToken))
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
ColorSpaceDetails alternateColorSpaceDetails;
|
|
if (DirectObjectFinder.TryGet(colorSpaceArray[2], scanner, out NameToken? alternateNameToken)
|
|
&& ColorSpaceMapper.TryMap(alternateNameToken, resourceStore, out var baseColorSpaceName))
|
|
{
|
|
alternateColorSpaceDetails = GetColorSpaceDetails(
|
|
baseColorSpaceName,
|
|
imageDictionary,
|
|
scanner,
|
|
resourceStore,
|
|
filterProvider,
|
|
true);
|
|
}
|
|
else if (DirectObjectFinder.TryGet(colorSpaceArray[2], scanner, out ArrayToken? alternateArrayToken)
|
|
&& alternateArrayToken.Length > 0
|
|
&& alternateArrayToken[0] is NameToken alternateColorSpaceNameToken
|
|
&& ColorSpaceMapper.TryMap(alternateColorSpaceNameToken, resourceStore, out var alternateArrayColorSpace))
|
|
{
|
|
var pseudoImageDictionary = new DictionaryToken(
|
|
new Dictionary<NameToken, IToken>
|
|
{
|
|
{ NameToken.ColorSpace, alternateArrayToken }
|
|
});
|
|
|
|
alternateColorSpaceDetails = GetColorSpaceDetails(
|
|
alternateArrayColorSpace,
|
|
pseudoImageDictionary,
|
|
scanner,
|
|
resourceStore,
|
|
filterProvider,
|
|
true);
|
|
}
|
|
else
|
|
{
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
|
|
var func = colorSpaceArray[3];
|
|
PdfFunction tintFunc = PdfFunctionParser.Create(func, scanner, filterProvider);
|
|
|
|
if (colorSpaceArray.Length > 4 && DirectObjectFinder.TryGet(colorSpaceArray[4], scanner, out DictionaryToken? deviceNAttributesToken))
|
|
{
|
|
// Optionnal
|
|
|
|
// Subtype - NameToken - Optional - Default value: DeviceN.
|
|
NameToken subtype = NameToken.Devicen;
|
|
if (deviceNAttributesToken.ContainsKey(NameToken.Subtype))
|
|
{
|
|
subtype = deviceNAttributesToken.Get<NameToken>(NameToken.Subtype, scanner);
|
|
}
|
|
|
|
// Colorants - dictionary - Required if Subtype is NChannel and the colour space includes spot colorants; otherwise optional
|
|
DictionaryToken? colorants = null;
|
|
if (deviceNAttributesToken.ContainsKey(NameToken.Colorants))
|
|
{
|
|
colorants = deviceNAttributesToken.Get<DictionaryToken>(NameToken.Colorants, scanner);
|
|
}
|
|
|
|
// Process - dictionary - Required if Subtype is NChannel and the colour space includes components of a process colour space, otherwise optional; PDF 1.6
|
|
DictionaryToken? process = null;
|
|
if (deviceNAttributesToken.ContainsKey(NameToken.Process))
|
|
{
|
|
process = deviceNAttributesToken.Get<DictionaryToken>(NameToken.Process, scanner);
|
|
}
|
|
|
|
// MixingHints - dictionary - Optional
|
|
DictionaryToken? mixingHints = null;
|
|
if (deviceNAttributesToken.ContainsKey(NameToken.MixingHints))
|
|
{
|
|
mixingHints = deviceNAttributesToken.Get<DictionaryToken>(NameToken.MixingHints, scanner);
|
|
}
|
|
|
|
var attributes = new DeviceNColorSpaceDetails.DeviceNColorSpaceAttributes(subtype, colorants, process, mixingHints);
|
|
|
|
return new DeviceNColorSpaceDetails(deviceNNamesToken.Data.OfType<NameToken>().ToArray(), alternateColorSpaceDetails, tintFunc, attributes);
|
|
}
|
|
|
|
return new DeviceNColorSpaceDetails(deviceNNamesToken.Data.OfType<NameToken>().ToArray(), alternateColorSpaceDetails, tintFunc);
|
|
}
|
|
default:
|
|
return UnsupportedColorSpaceDetails.Instance;
|
|
}
|
|
}
|
|
|
|
private static bool TryGetColorSpaceArray(DictionaryToken imageDictionary, IResourceStore resourceStore,
|
|
IPdfTokenScanner scanner,
|
|
[NotNullWhen(true)] out ArrayToken? colorSpaceArray)
|
|
{
|
|
var colorSpace = imageDictionary.GetObjectOrDefault(NameToken.ColorSpace, NameToken.Cs);
|
|
|
|
if (!DirectObjectFinder.TryGet(colorSpace, scanner, out colorSpaceArray)
|
|
&& DirectObjectFinder.TryGet(colorSpace, scanner, out NameToken? colorSpaceName) &&
|
|
resourceStore.TryGetNamedColorSpace(colorSpaceName, out var colorSpaceNamedToken))
|
|
{
|
|
colorSpaceArray = colorSpaceNamedToken.Data as ArrayToken;
|
|
}
|
|
|
|
return colorSpaceArray != null;
|
|
}
|
|
}
|
|
}
|