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
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,74 +2,137 @@
|
|||||||
{
|
{
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
void DefaultColorSpace(ColorSpace? colorSpaceActual = null)
|
||||||
|
{
|
||||||
|
if (colorSpaceActual.HasValue)
|
||||||
|
{
|
||||||
|
switch (colorSpaceActual)
|
||||||
|
{
|
||||||
|
case ColorSpace.DeviceGray:
|
||||||
|
currentStateFunc().CurrentStrokingColor = GrayColor.Black;
|
||||||
|
break;
|
||||||
|
case ColorSpace.DeviceRGB:
|
||||||
|
currentStateFunc().CurrentStrokingColor = RGBColor.Black;
|
||||||
|
break;
|
||||||
|
case ColorSpace.DeviceCMYK:
|
||||||
|
currentStateFunc().CurrentStrokingColor = CMYKColor.Black;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
currentStateFunc().CurrentStrokingColor = GrayColor.Black;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CurrentStrokingColorSpace = ColorSpace.DeviceGray;
|
||||||
|
currentStateFunc().CurrentStrokingColor = GrayColor.Black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AdvancedStrokingColorSpace = null;
|
||||||
|
|
||||||
if (colorspace.TryMapToColorSpace(out var colorspaceActual))
|
if (colorspace.TryMapToColorSpace(out var colorspaceActual))
|
||||||
{
|
{
|
||||||
CurrentStrokingColorSpace = 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))
|
||||||
{
|
{
|
||||||
case ColorSpace.DeviceGray:
|
AdvancedStrokingColorSpace = namedColorSpace.Name;
|
||||||
currentStateFunc().CurrentStrokingColor = GrayColor.Black;
|
CurrentStrokingColorSpace = colorspaceActual;
|
||||||
break;
|
}
|
||||||
case ColorSpace.DeviceRGB:
|
else
|
||||||
currentStateFunc().CurrentStrokingColor = RGBColor.Black;
|
{
|
||||||
break;
|
DefaultColorSpace();
|
||||||
case ColorSpace.DeviceCMYK:
|
|
||||||
currentStateFunc().CurrentStrokingColor = CMYKColor.Black;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
currentStateFunc().CurrentStrokingColor = GrayColor.Black;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CurrentStrokingColorSpace = ColorSpace.DeviceGray;
|
DefaultColorSpace();
|
||||||
currentStateFunc().CurrentStrokingColor = GrayColor.Black;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetNonStrokingColorspace(NameToken colorspace)
|
public void SetNonStrokingColorspace(NameToken colorspace)
|
||||||
{
|
{
|
||||||
|
void DefaultColorSpace(ColorSpace? colorSpaceActual = null)
|
||||||
|
{
|
||||||
|
if (colorSpaceActual.HasValue)
|
||||||
|
{
|
||||||
|
switch (colorSpaceActual)
|
||||||
|
{
|
||||||
|
case ColorSpace.DeviceGray:
|
||||||
|
currentStateFunc().CurrentNonStrokingColor = GrayColor.Black;
|
||||||
|
break;
|
||||||
|
case ColorSpace.DeviceRGB:
|
||||||
|
currentStateFunc().CurrentNonStrokingColor = RGBColor.Black;
|
||||||
|
break;
|
||||||
|
case ColorSpace.DeviceCMYK:
|
||||||
|
currentStateFunc().CurrentNonStrokingColor = CMYKColor.Black;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
currentStateFunc().CurrentNonStrokingColor = GrayColor.Black;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CurrentNonStrokingColorSpace = ColorSpace.DeviceGray;
|
||||||
|
currentStateFunc().CurrentNonStrokingColor = GrayColor.Black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AdvancedNonStrokingColorSpace = null;
|
||||||
|
|
||||||
if (colorspace.TryMapToColorSpace(out var colorspaceActual))
|
if (colorspace.TryMapToColorSpace(out var colorspaceActual))
|
||||||
{
|
{
|
||||||
CurrentNonStrokingColorSpace = colorspaceActual;
|
CurrentNonStrokingColorSpace = 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 var colorSpaceActual))
|
||||||
{
|
{
|
||||||
case ColorSpace.DeviceGray:
|
AdvancedNonStrokingColorSpace = namedColorSpace.Name;
|
||||||
currentStateFunc().CurrentNonStrokingColor = GrayColor.Black;
|
CurrentNonStrokingColorSpace = colorSpaceActual;
|
||||||
break;
|
}
|
||||||
case ColorSpace.DeviceRGB:
|
else
|
||||||
currentStateFunc().CurrentNonStrokingColor = RGBColor.Black;
|
{
|
||||||
break;
|
DefaultColorSpace();
|
||||||
case ColorSpace.DeviceCMYK:
|
|
||||||
currentStateFunc().CurrentNonStrokingColor = CMYKColor.Black;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
currentStateFunc().CurrentNonStrokingColor = GrayColor.Black;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CurrentNonStrokingColorSpace = ColorSpace.DeviceGray;
|
DefaultColorSpace();
|
||||||
currentStateFunc().CurrentNonStrokingColor = GrayColor.Black;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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.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)
|
||||||
|
@@ -17,6 +17,16 @@
|
|||||||
/// The <see cref="ColorSpace"/> used for non-stroking operations.
|
/// The <see cref="ColorSpace"/> used for non-stroking operations.
|
||||||
/// </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.
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
@@ -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,
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user