add colors to letters based on current font and graphics state

This commit is contained in:
Eliot Jones
2019-08-05 19:26:10 +01:00
parent 0df35b8488
commit 4dde4ca0c1
12 changed files with 171 additions and 101 deletions

View File

@@ -1,15 +1,21 @@
namespace UglyToad.PdfPig.Graphics
{
using System;
using Colors;
using Tokens;
internal class ColorSpaceContext : IColorSpaceContext
{
public ColorSpace CurrentStrokingColorSpace { get; private set; } = ColorSpace.DeviceGray;
public ColorSpace CurrentNonStrokingColorSpace { get; private set; } = ColorSpace.DeviceGray;
private readonly Func<CurrentGraphicsState> currentStateFunc;
public IColor CurrentStrokingColor { get; private set; } = GrayColor.Black;
public IColor CurrentNonStrokingColor { get; private set; } = GrayColor.Black;
public ColorSpace CurrentStrokingColorSpace { get; private set; } = ColorSpace.DeviceGray;
public ColorSpace CurrentNonStrokingColorSpace { get; private set; } = ColorSpace.DeviceGray;
public ColorSpaceContext(Func<CurrentGraphicsState> currentStateFunc)
{
this.currentStateFunc = currentStateFunc ?? throw new ArgumentNullException(nameof(currentStateFunc));
}
public void SetStrokingColorspace(NameToken colorspace)
{
@@ -19,23 +25,23 @@
switch (colorspaceActual)
{
case ColorSpace.DeviceGray:
CurrentStrokingColor = GrayColor.Black;
currentStateFunc().CurrentStrokingColor = GrayColor.Black;
break;
case ColorSpace.DeviceRGB:
CurrentStrokingColor = RGBColor.Black;
currentStateFunc().CurrentStrokingColor = RGBColor.Black;
break;
case ColorSpace.DeviceCMYK:
CurrentStrokingColor = CMYKColor.Black;
currentStateFunc().CurrentStrokingColor = CMYKColor.Black;
break;
default:
CurrentStrokingColor = GrayColor.Black;
currentStateFunc().CurrentStrokingColor = GrayColor.Black;
break;
}
}
else
{
CurrentStrokingColorSpace = ColorSpace.DeviceGray;
CurrentStrokingColor = GrayColor.Black;
currentStateFunc().CurrentStrokingColor = GrayColor.Black;
}
}
@@ -47,60 +53,60 @@
switch (colorspaceActual)
{
case ColorSpace.DeviceGray:
CurrentNonStrokingColor = GrayColor.Black;
currentStateFunc().CurrentNonStrokingColor = GrayColor.Black;
break;
case ColorSpace.DeviceRGB:
CurrentNonStrokingColor = RGBColor.Black;
currentStateFunc().CurrentNonStrokingColor = RGBColor.Black;
break;
case ColorSpace.DeviceCMYK:
CurrentNonStrokingColor = CMYKColor.Black;
currentStateFunc().CurrentNonStrokingColor = CMYKColor.Black;
break;
default:
CurrentNonStrokingColor = GrayColor.Black;
currentStateFunc().CurrentNonStrokingColor = GrayColor.Black;
break;
}
}
else
{
CurrentNonStrokingColorSpace = ColorSpace.DeviceGray;
CurrentNonStrokingColor = GrayColor.Black;
currentStateFunc().CurrentNonStrokingColor = GrayColor.Black;
}
}
public void SetStrokingColorGray(decimal gray)
{
CurrentStrokingColorSpace = ColorSpace.DeviceGray;
CurrentStrokingColor = new GrayColor(gray);
currentStateFunc().CurrentStrokingColor = new GrayColor(gray);
}
public void SetStrokingColorRgb(decimal r, decimal g, decimal b)
{
CurrentStrokingColorSpace = ColorSpace.DeviceRGB;
CurrentStrokingColor = new RGBColor(r, g, b);
currentStateFunc().CurrentStrokingColor = new RGBColor(r, g, b);
}
public void SetStrokingColorCmyk(decimal c, decimal m, decimal y, decimal k)
{
CurrentStrokingColorSpace = ColorSpace.DeviceCMYK;
CurrentStrokingColor = new CMYKColor(c, m, y, k);
currentStateFunc().CurrentStrokingColor = new CMYKColor(c, m, y, k);
}
public void SetNonStrokingColorGray(decimal gray)
{
CurrentNonStrokingColorSpace = ColorSpace.DeviceGray;
CurrentNonStrokingColor = new GrayColor(gray);
currentStateFunc().CurrentNonStrokingColor = new GrayColor(gray);
}
public void SetNonStrokingColorRgb(decimal r, decimal g, decimal b)
{
CurrentNonStrokingColorSpace = ColorSpace.DeviceRGB;
CurrentNonStrokingColor = new RGBColor(r, g, b);
currentStateFunc().CurrentNonStrokingColor = new RGBColor(r, g, b);
}
public void SetNonStrokingColorCmyk(decimal c, decimal m, decimal y, decimal k)
{
CurrentNonStrokingColorSpace = ColorSpace.DeviceCMYK;
CurrentNonStrokingColor = new CMYKColor(c, m, y, k);
currentStateFunc().CurrentNonStrokingColor = new CMYKColor(c, m, y, k);
}
}
}

View File

@@ -45,9 +45,15 @@
/// <inheritdoc/>
public (decimal r, decimal g, decimal b) ToRGBValues()
{
return ((255 * (1 - C) * (1 - K)) / 255m,
(255 * (1 - M) * (1 - K)) / 255m,
(255 * (1 - Y) * (1 - K)) / 255m);
return ((1 - C) * (1 - K),
(1 - M) * (1 - K),
(1 - Y) * (1 - K));
}
/// <inheritdoc />
public override string ToString()
{
return $"CMYK: ({C}, {M}, {Y}, {K})";
}
}
}

View File

@@ -29,5 +29,11 @@
{
return (Gray, Gray, Gray);
}
/// <inheritdoc />
public override string ToString()
{
return $"Gray: {Gray}";
}
}
}

View File

@@ -41,5 +41,11 @@
{
return (R, G, B);
}
/// <inheritdoc />
public override string ToString()
{
return $"RGB: ({R}, {G}, {B})";
}
}
}

View File

@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Colors;
using Content;
using Core;
using Fonts;
@@ -39,7 +40,7 @@
public PdfPath CurrentPath { get; private set; }
public IColorSpaceContext ColorSpaceContext { get; } = new ColorSpaceContext();
public IColorSpaceContext ColorSpaceContext { get; }
public PdfPoint CurrentPosition { get; set; }
@@ -66,6 +67,7 @@
this.xObjectFactory = xObjectFactory;
this.log = log;
graphicsStack.Push(new CurrentGraphicsState());
ColorSpaceContext = new ColorSpaceContext(GetCurrentState);
}
public PageContent Process(IReadOnlyList<IGraphicsStateOperation> operations)
@@ -165,11 +167,27 @@
.Transform(TextMatrices.TextMatrix
.Transform(renderingMatrix
.Transform(boundingBox.GlyphBounds)));
var transformedPdfBounds = rotation.Rotate(transformationMatrix)
.Transform(TextMatrices.TextMatrix
.Transform(renderingMatrix.Transform(new PdfRectangle(0, 0, boundingBox.Width, 0))));
ShowGlyph(font, transformedGlyphBounds, transformedPdfBounds.BottomLeft, transformedPdfBounds.BottomRight, transformedPdfBounds.Width, unicode, fontSize, pointSize);
// If the text rendering mode calls for filling, the current nonstroking color in the graphics state is used;
// if it calls for stroking, the current stroking color is used.
// In modes that perform both filling and stroking, the effect is as if each glyph outline were filled and then stroked in separate operations.
// TODO: expose color as something more advanced
var color = currentState.FontState.TextRenderingMode != TextRenderingMode.Stroke
? currentState.CurrentNonStrokingColor
: currentState.CurrentStrokingColor;
ShowGlyph(font, transformedGlyphBounds,
transformedPdfBounds.BottomLeft,
transformedPdfBounds.BottomRight,
transformedPdfBounds.Width,
unicode,
fontSize,
color,
pointSize);
decimal tx, ty;
if (font.IsVertical)
@@ -336,9 +354,23 @@
TextMatrices.TextMatrix = newMatrix;
}
private void ShowGlyph(IFont font, PdfRectangle glyphRectangle, PdfPoint startBaseLine, PdfPoint endBaseLine, decimal width, string unicode, decimal fontSize, decimal pointSize)
private void ShowGlyph(IFont font, PdfRectangle glyphRectangle,
PdfPoint startBaseLine,
PdfPoint endBaseLine,
decimal width,
string unicode,
decimal fontSize,
IColor color,
decimal pointSize)
{
var letter = new Letter(unicode, glyphRectangle, startBaseLine, endBaseLine, width, fontSize, font.Name.Data, pointSize);
var letter = new Letter(unicode, glyphRectangle,
startBaseLine,
endBaseLine,
width,
fontSize,
font.Name.Data,
color,
pointSize);
Letters.Add(letter);
}

View File

@@ -1,6 +1,7 @@
// ReSharper disable RedundantDefaultMemberInitializer
namespace UglyToad.PdfPig.Graphics
{
using Colors;
using Core;
using PdfPig.Core;
@@ -96,6 +97,16 @@ namespace UglyToad.PdfPig.Graphics
/// </summary>
public decimal Smoothness { get; set; } = 0;
/// <summary>
/// The current active stroking color for paths.
/// </summary>
public IColor CurrentStrokingColor { get; set; }
/// <summary>
/// The current active non-stroking color for text and fill.
/// </summary>
public IColor CurrentNonStrokingColor { get; set; }
#endregion
/// <inheritdoc />
@@ -118,7 +129,9 @@ namespace UglyToad.PdfPig.Graphics
NonStrokingOverprint = NonStrokingOverprint,
OverprintMode = OverprintMode,
Smoothness = Smoothness,
StrokeAdjustment = StrokeAdjustment
StrokeAdjustment = StrokeAdjustment,
CurrentStrokingColor = CurrentStrokingColor,
CurrentNonStrokingColor = CurrentNonStrokingColor
};
}
}

View File

@@ -17,17 +17,7 @@
/// The <see cref="ColorSpace"/> used for non-stroking operations.
/// </summary>
ColorSpace CurrentNonStrokingColorSpace { get; }
/// <summary>
/// The <see cref="IColor"/> used for stroking operations.
/// </summary>
IColor CurrentStrokingColor { get; }
/// <summary>
/// The <see cref="IColor"/> used for non-stroking operations.
/// </summary>
IColor CurrentNonStrokingColor { get; }
/// <summary>
/// Set the current color space to use for stroking operations.
/// </summary>

View File

@@ -126,11 +126,11 @@ namespace UglyToad.PdfPig.Graphics
case SetNonStrokeColorAdvanced.Symbol:
if (operands[operands.Count - 1] is NameToken scnLowerPatternName)
{
return new SetStrokeColorAdvanced(operands.Take(operands.Count - 1).Select(x => ((NumericToken)x).Data).ToList(), scnLowerPatternName);
return new SetNonStrokeColorAdvanced(operands.Take(operands.Count - 1).Select(x => ((NumericToken)x).Data).ToList(), scnLowerPatternName);
}
else if (operands.All(x => x is NumericToken))
{
return new SetStrokeColorAdvanced(operands.Select(x => ((NumericToken)x).Data).ToList());
return new SetNonStrokeColorAdvanced(operands.Select(x => ((NumericToken)x).Data).ToList());
}
var errorMessageScnLower = string.Join(", ", operands.Select(x => x.ToString()));