From f863e25a0550b1205d8270f765064828c60107c0 Mon Sep 17 00:00:00 2001 From: Gerard Gunnewijk Date: Thu, 23 Jul 2026 18:43:26 +0200 Subject: [PATCH 1/2] Added support for the current alpha constant --- .../Content/ExtendedGraphicsState.cs | 35 +++++++++++++++++++ .../Primitives/PdfName.cs | 2 ++ .../Primitives/PdfNames.cs | 4 +++ 3 files changed, 41 insertions(+) diff --git a/src/Synercoding.FileFormats.Pdf/Content/ExtendedGraphicsState.cs b/src/Synercoding.FileFormats.Pdf/Content/ExtendedGraphicsState.cs index 27ff9ef..ad0397d 100644 --- a/src/Synercoding.FileFormats.Pdf/Content/ExtendedGraphicsState.cs +++ b/src/Synercoding.FileFormats.Pdf/Content/ExtendedGraphicsState.cs @@ -22,6 +22,37 @@ public sealed record class ExtendedGraphicsState /// public bool? OverprintNonStroking { get; init; } + /// + /// The current stroking alpha constant, specifying the constant shape or constant opacity value + /// to be used for stroking operations in the transparent imaging model. + /// + public double? CurrentAlphaConstantStroking + { + get; + init + { + if (value < 0 || value > 1) + throw new ArgumentOutOfRangeException(nameof(CurrentAlphaConstantStroking), "Value must be between 0 and 1."); + + field = value; + } + } + + /// + /// The current non-stroking alpha constant, specifying the constant shape or constant opacity value + /// to be used for non-stroking operations in the transparent imaging model. + /// + public double? CurrentAlphaConstantNonStroking + { + get; + init + { + if (value < 0 || value > 1) + throw new ArgumentOutOfRangeException(nameof(CurrentAlphaConstantNonStroking), "Value must be between 0 and 1."); + + field = value; + } + } internal IPdfDictionary ToPdfDictionary() { var dictionary = new PdfDictionary() @@ -33,6 +64,10 @@ internal IPdfDictionary ToPdfDictionary() dictionary[PdfNames.OP] = new PdfBoolean(Overprint.Value); if (OverprintNonStroking.HasValue) dictionary[PdfNames.op] = new PdfBoolean(OverprintNonStroking.Value); + if (CurrentAlphaConstantStroking.HasValue) + dictionary[PdfNames.CA] = new PdfNumber(CurrentAlphaConstantStroking.Value); + if (CurrentAlphaConstantNonStroking.HasValue) + dictionary[PdfNames.ca] = new PdfNumber(CurrentAlphaConstantNonStroking.Value); return dictionary; } diff --git a/src/Synercoding.FileFormats.Pdf/Primitives/PdfName.cs b/src/Synercoding.FileFormats.Pdf/Primitives/PdfName.cs index 8027b6a..73509fb 100644 --- a/src/Synercoding.FileFormats.Pdf/Primitives/PdfName.cs +++ b/src/Synercoding.FileFormats.Pdf/Primitives/PdfName.cs @@ -23,6 +23,8 @@ public sealed class PdfName : IPdfPrimitive, IEquatable { "BitsPerComponent", new PdfName("BitsPerComponent") }, { "Black", new PdfName("Black") }, { "BleedBox", new PdfName("BleedBox") }, + { "CA", new PdfName("CA") }, + { "ca", new PdfName("ca") }, { "CapHeight", new PdfName("CapHeight") }, { "Catalog", new PdfName("Catalog") }, { "CCITTFaxDecode", new PdfName("CCITTFaxDecode") }, diff --git a/src/Synercoding.FileFormats.Pdf/Primitives/PdfNames.cs b/src/Synercoding.FileFormats.Pdf/Primitives/PdfNames.cs index 8905634..1fcf2d6 100644 --- a/src/Synercoding.FileFormats.Pdf/Primitives/PdfNames.cs +++ b/src/Synercoding.FileFormats.Pdf/Primitives/PdfNames.cs @@ -30,6 +30,10 @@ public static PdfName Black => PdfName.Get(nameof(Black)); public static PdfName BleedBox => PdfName.Get(nameof(BleedBox)); + public static PdfName CA + => PdfName.Get(nameof(CA)); + public static PdfName ca + => PdfName.Get(nameof(ca)); public static PdfName CapHeight => PdfName.Get(nameof(CapHeight)); public static PdfName Catalog From fa57b5336a218ebe3e5c4428b28010331a332fca Mon Sep 17 00:00:00 2001 From: Gerard Gunnewijk Date: Thu, 23 Jul 2026 18:43:50 +0200 Subject: [PATCH 2/2] Added support for blend modes --- .../Content/BlendMode.cs | 106 ++++++++++++++++++ .../Content/ExtendedGraphicsState.cs | 10 +- .../Content/Extensions/BlendModeExtensions.cs | 28 +++++ .../Primitives/PdfName.cs | 17 +++ .../Primitives/PdfNames.cs | 34 ++++++ 5 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 src/Synercoding.FileFormats.Pdf/Content/BlendMode.cs create mode 100644 src/Synercoding.FileFormats.Pdf/Content/Extensions/BlendModeExtensions.cs diff --git a/src/Synercoding.FileFormats.Pdf/Content/BlendMode.cs b/src/Synercoding.FileFormats.Pdf/Content/BlendMode.cs new file mode 100644 index 0000000..1faf32d --- /dev/null +++ b/src/Synercoding.FileFormats.Pdf/Content/BlendMode.cs @@ -0,0 +1,106 @@ +namespace Synercoding.FileFormats.Pdf.Content; + +/// +/// Specifies the blend mode to be used in the transparent imaging model. +/// +public enum BlendMode +{ + /// + /// Selects the source color, ignoring the backdrop. + /// + Normal, + /// + /// Multiplies the backdrop and source color values. + /// + /// + /// The result color is always at least as dark as either of the two constituent + /// colors. Multiplying any color with black produces black; multiplying with + /// white leaves the original color unchanged. Painting successive overlapping + /// objects with a color other than black or white produces progressively darker + /// colors. + /// + Multiply, + /// + /// Multiplies the complements of the backdrop and source color values, + /// then complements the result + /// + /// + /// The result color is always at least as light as either of the two constituent + /// colors. Screening any color with white produces white; screening with black + /// leaves the original color unchanged. The effect is similar to projecting + /// multiple photographic slides simultaneously onto a single screen. + /// + Screen, + /// + /// Multiplies or screens the colors, depending on the backdrop color value. + /// Source colors overlay the backdrop while preserving its highlights and + /// shadows. The backdrop color is not replaced but is mixed with the source + /// color to reflect the lightness or darkness of the backdrop. + /// + Overlay, + /// + /// Selects the darker of the backdrop and source colors. + /// + /// + /// The backdrop is replaced with the source where the source is darker; + /// otherwise, it is left unchanged. + /// + Darken, + /// + /// Selects the lighter of the backdrop and source colors. + /// + /// + /// The backdrop is replaced with the source where the source is lighter; + /// otherwise, it is left unchanged. + /// + Lighten, + /// + /// Brightens the backdrop color to reflect the source color. + /// Painting with black produces no changes. + /// + ColorDodge, + /// + /// Darkens the backdrop color to reflect the source color. + /// Painting with white produces no change. + /// + ColorBurn, + /// + /// Multiplies or screens the colors, depending on the source color value. + /// The effect is similar to shining a harsh spotlight on the backdrop. + /// + HardLight, + /// + /// Darkens or lightens the colors, depending on the source color value. + /// The effect is similar to shining a diffused spotlight on the backdrop. + /// + SoftLight, + /// + /// Subtracts the darker of the two constituent colors from the lighter color. + /// + /// + /// Painting with white inverts the backdrop color; + /// painting with black produces no change. + /// + Difference, + /// + /// Produces an effect similar to that of the mode but lower in contrast. + /// Painting with white inverts the backdrop color; painting with black produces no change. + /// + Exclusion, + /// + /// Creates a color with the hue of the source color and the saturation and luminosity of the backdrop color. + /// + Hue, + /// + /// Creates a color with the saturation of the source color and the hue and luminosity of the backdrop color. Painting with this mode in an area of the backdrop that is a pure gray (no saturation) produces no change. + /// + Saturation, + /// + /// Creates a color with the hue and saturation of the source color and the luminosity of the backdrop color. This preserves the gray levels of the backdrop and is useful for coloring monochrome images or tinting color images. + /// + Color, + /// + /// Creates a color with the luminosity of the source color and the hue and saturation of the backdrop color. This produces an inverse effect to that of the mode. + /// + Luminosity +} diff --git a/src/Synercoding.FileFormats.Pdf/Content/ExtendedGraphicsState.cs b/src/Synercoding.FileFormats.Pdf/Content/ExtendedGraphicsState.cs index ad0397d..d9efaad 100644 --- a/src/Synercoding.FileFormats.Pdf/Content/ExtendedGraphicsState.cs +++ b/src/Synercoding.FileFormats.Pdf/Content/ExtendedGraphicsState.cs @@ -1,3 +1,4 @@ +using Synercoding.FileFormats.Pdf.Content.Extensions; using Synercoding.FileFormats.Pdf.Primitives; namespace Synercoding.FileFormats.Pdf.Content; @@ -53,6 +54,12 @@ public double? CurrentAlphaConstantNonStroking field = value; } } + + /// + /// The current blend mode to be used in the transparent imaging model. + /// + public BlendMode? BlendMode { get; init; } + internal IPdfDictionary ToPdfDictionary() { var dictionary = new PdfDictionary() @@ -68,8 +75,9 @@ internal IPdfDictionary ToPdfDictionary() dictionary[PdfNames.CA] = new PdfNumber(CurrentAlphaConstantStroking.Value); if (CurrentAlphaConstantNonStroking.HasValue) dictionary[PdfNames.ca] = new PdfNumber(CurrentAlphaConstantNonStroking.Value); + if (BlendMode.HasValue) + dictionary[PdfNames.BM] = BlendMode.Value.ToPdfName(); return dictionary; } } - diff --git a/src/Synercoding.FileFormats.Pdf/Content/Extensions/BlendModeExtensions.cs b/src/Synercoding.FileFormats.Pdf/Content/Extensions/BlendModeExtensions.cs new file mode 100644 index 0000000..f16692b --- /dev/null +++ b/src/Synercoding.FileFormats.Pdf/Content/Extensions/BlendModeExtensions.cs @@ -0,0 +1,28 @@ +using Synercoding.FileFormats.Pdf.Primitives; + +namespace Synercoding.FileFormats.Pdf.Content.Extensions; + +internal static class BlendModeExtensions +{ + public static PdfName ToPdfName(this BlendMode blendMode) + => blendMode switch + { + BlendMode.Normal => PdfNames.Normal, + BlendMode.Multiply => PdfNames.Multiply, + BlendMode.Screen => PdfNames.Screen, + BlendMode.Overlay => PdfNames.Overlay, + BlendMode.Darken => PdfNames.Darken, + BlendMode.Lighten => PdfNames.Lighten, + BlendMode.ColorDodge => PdfNames.ColorDodge, + BlendMode.ColorBurn => PdfNames.ColorBurn, + BlendMode.HardLight => PdfNames.HardLight, + BlendMode.SoftLight => PdfNames.SoftLight, + BlendMode.Difference => PdfNames.Difference, + BlendMode.Exclusion => PdfNames.Exclusion, + BlendMode.Hue => PdfNames.Hue, + BlendMode.Saturation => PdfNames.Saturation, + BlendMode.Color => PdfNames.Color, + BlendMode.Luminosity => PdfNames.Luminosity, + _ => throw new ArgumentOutOfRangeException(nameof(blendMode), blendMode, null) + }; +} diff --git a/src/Synercoding.FileFormats.Pdf/Primitives/PdfName.cs b/src/Synercoding.FileFormats.Pdf/Primitives/PdfName.cs index 73509fb..4c12755 100644 --- a/src/Synercoding.FileFormats.Pdf/Primitives/PdfName.cs +++ b/src/Synercoding.FileFormats.Pdf/Primitives/PdfName.cs @@ -23,6 +23,7 @@ public sealed class PdfName : IPdfPrimitive, IEquatable { "BitsPerComponent", new PdfName("BitsPerComponent") }, { "Black", new PdfName("Black") }, { "BleedBox", new PdfName("BleedBox") }, + { "BM", new PdfName("BM") }, { "CA", new PdfName("CA") }, { "ca", new PdfName("ca") }, { "CapHeight", new PdfName("CapHeight") }, @@ -33,6 +34,9 @@ public sealed class PdfName : IPdfPrimitive, IEquatable { "CIDFontType2", new PdfName("CIDFontType2") }, { "CIDSystemInfo", new PdfName("CIDSystemInfo") }, { "CIDToGIDMap", new PdfName("CIDToGIDMap") }, + { "Color", new PdfName("Color") }, + { "ColorBurn", new PdfName("ColorBurn") }, + { "ColorDodge", new PdfName("ColorDodge") }, { "ColorSpace", new PdfName("ColorSpace") }, { "Colors", new PdfName("Colors") }, { "Columns", new PdfName("Columns") }, @@ -43,6 +47,7 @@ public sealed class PdfName : IPdfPrimitive, IEquatable { "CropBox", new PdfName("CropBox") }, { "Crypt", new PdfName("Crypt") }, { "Cyan", new PdfName("Cyan") }, + { "Darken", new PdfName("Darken") }, { "DCTDecode", new PdfName("DCTDecode") }, { "Decode", new PdfName("Decode") }, { "DecodeParms", new PdfName("DecodeParms") }, @@ -52,11 +57,13 @@ public sealed class PdfName : IPdfPrimitive, IEquatable { "DeviceCMYK", new PdfName("DeviceCMYK") }, { "DeviceGray", new PdfName("DeviceGray") }, { "DeviceRGB", new PdfName("DeviceRGB") }, + { "Difference", new PdfName("Difference") }, { "EarlyChange", new PdfName("EarlyChange") }, { "Encoding", new PdfName("Encoding") }, { "EFF", new PdfName("EFF") }, { "Encrypt", new PdfName("Encrypt") }, { "EncryptMetadata", new PdfName("EncryptMetadata") }, + { "Exclusion", new PdfName("Exclusion") }, { "ExtGState", new PdfName("ExtGState") }, { "Filter", new PdfName("Filter") }, { "First", new PdfName("First") }, @@ -68,7 +75,9 @@ public sealed class PdfName : IPdfPrimitive, IEquatable { "FlateDecode", new PdfName("FlateDecode") }, { "Font", new PdfName("Font") }, { "FullScreen", new PdfName("FullScreen") }, + { "HardLight", new PdfName("HardLight") }, { "Height", new PdfName("Height") }, + { "Hue", new PdfName("Hue") }, { "ID", new PdfName("ID") }, { "Identity", new PdfName("Identity") }, { "Identity-H", new PdfName("Identity-H") }, @@ -82,13 +91,17 @@ public sealed class PdfName : IPdfPrimitive, IEquatable { "Kids", new PdfName("Kids") }, { "Length", new PdfName("Length") }, { "Length1", new PdfName("Length1") }, + { "Lighten", new PdfName("Lighten") }, + { "Luminosity", new PdfName("Luminosity") }, { "LZWDecode", new PdfName("LZWDecode") }, { "Magenta", new PdfName("Magenta") }, { "MediaBox", new PdfName("MediaBox") }, { "Metadata", new PdfName("Metadata") }, { "ModDate", new PdfName("ModDate") }, + { "Multiply", new PdfName("Multiply") }, { "N", new PdfName("N") }, { "None", new PdfName("None") }, + { "Normal", new PdfName("Normal") }, { "O", new PdfName("O") }, { "ObjStm", new PdfName("ObjStm") }, { "OE", new PdfName("OE") }, @@ -96,6 +109,7 @@ public sealed class PdfName : IPdfPrimitive, IEquatable { "OP", new PdfName("OP") }, { "op", new PdfName("op") }, { "Ordering", new PdfName("Ordering") }, + { "Overlay", new PdfName("Overlay") }, { "P", new PdfName("P") }, { "Page", new PdfName("Page") }, { "PageLayout", new PdfName("PageLayout") }, @@ -115,11 +129,14 @@ public sealed class PdfName : IPdfPrimitive, IEquatable { "Root", new PdfName("Root") }, { "Rotate", new PdfName("Rotate") }, { "RunLengthDecode", new PdfName("RunLengthDecode") }, + { "Saturation", new PdfName("Saturation") }, + { "Screen", new PdfName("Screen") }, { "Separation", new PdfName("Separation") }, { "Shading", new PdfName("Shading") }, { "SinglePage", new PdfName("SinglePage") }, { "Size", new PdfName("Size") }, { "SMask", new PdfName("SMask") }, + { "SoftLight", new PdfName("SoftLight") }, { "Standard", new PdfName("Standard") }, { "StemV", new PdfName("StemV") }, { "StmF", new PdfName("StmF") }, diff --git a/src/Synercoding.FileFormats.Pdf/Primitives/PdfNames.cs b/src/Synercoding.FileFormats.Pdf/Primitives/PdfNames.cs index 1fcf2d6..4b43947 100644 --- a/src/Synercoding.FileFormats.Pdf/Primitives/PdfNames.cs +++ b/src/Synercoding.FileFormats.Pdf/Primitives/PdfNames.cs @@ -30,6 +30,8 @@ public static PdfName Black => PdfName.Get(nameof(Black)); public static PdfName BleedBox => PdfName.Get(nameof(BleedBox)); + public static PdfName BM + => PdfName.Get(nameof(BM)); public static PdfName CA => PdfName.Get(nameof(CA)); public static PdfName ca @@ -50,6 +52,12 @@ public static PdfName CIDSystemInfo => PdfName.Get(nameof(CIDSystemInfo)); public static PdfName CIDToGIDMap => PdfName.Get(nameof(CIDToGIDMap)); + public static PdfName Color + => PdfName.Get(nameof(Color)); + public static PdfName ColorBurn + => PdfName.Get(nameof(ColorBurn)); + public static PdfName ColorDodge + => PdfName.Get(nameof(ColorDodge)); public static PdfName ColorSpace => PdfName.Get(nameof(ColorSpace)); public static PdfName Colors @@ -70,6 +78,8 @@ public static PdfName Crypt => PdfName.Get(nameof(Crypt)); public static PdfName Cyan => PdfName.Get(nameof(Cyan)); + public static PdfName Darken + => PdfName.Get(nameof(Darken)); public static PdfName DCTDecode => PdfName.Get(nameof(DCTDecode)); public static PdfName Decode @@ -86,6 +96,8 @@ public static PdfName DeviceGray => PdfName.Get(nameof(DeviceGray)); public static PdfName DeviceRGB => PdfName.Get(nameof(DeviceRGB)); + public static PdfName Difference + => PdfName.Get(nameof(Difference)); public static PdfName DW => PdfName.Get(nameof(DW)); public static PdfName EarlyChange @@ -98,6 +110,8 @@ public static PdfName Encrypt => PdfName.Get(nameof(Encrypt)); public static PdfName EncryptMetadata => PdfName.Get(nameof(EncryptMetadata)); + public static PdfName Exclusion + => PdfName.Get(nameof(Exclusion)); public static PdfName ExtGState => PdfName.Get(nameof(ExtGState)); public static PdfName Filter @@ -120,8 +134,12 @@ public static PdfName FontName => PdfName.Get(nameof(FontName)); public static PdfName FullScreen => PdfName.Get(nameof(FullScreen)); + public static PdfName HardLight + => PdfName.Get(nameof(HardLight)); public static PdfName Height => PdfName.Get(nameof(Height)); + public static PdfName Hue + => PdfName.Get(nameof(Hue)); public static PdfName ID => PdfName.Get(nameof(ID)); public static PdfName Identity @@ -148,6 +166,10 @@ public static PdfName Length => PdfName.Get(nameof(Length)); public static PdfName Length1 => PdfName.Get(nameof(Length1)); + public static PdfName Lighten + => PdfName.Get(nameof(Lighten)); + public static PdfName Luminosity + => PdfName.Get(nameof(Luminosity)); public static PdfName LZWDecode => PdfName.Get(nameof(LZWDecode)); public static PdfName Magenta @@ -158,10 +180,14 @@ public static PdfName Metadata => PdfName.Get(nameof(Metadata)); public static PdfName ModDate => PdfName.Get(nameof(ModDate)); + public static PdfName Multiply + => PdfName.Get(nameof(Multiply)); public static PdfName N => PdfName.Get(nameof(N)); public static PdfName None => PdfName.Get(nameof(None)); + public static PdfName Normal + => PdfName.Get(nameof(Normal)); public static PdfName O => PdfName.Get(nameof(O)); public static PdfName ObjStm @@ -176,6 +202,8 @@ public static PdfName op => PdfName.Get(nameof(op)); public static PdfName Ordering => PdfName.Get(nameof(Ordering)); + public static PdfName Overlay + => PdfName.Get(nameof(Overlay)); public static PdfName P => PdfName.Get(nameof(P)); public static PdfName Page @@ -214,6 +242,10 @@ public static PdfName Rotate => PdfName.Get(nameof(Rotate)); public static PdfName RunLengthDecode => PdfName.Get(nameof(RunLengthDecode)); + public static PdfName Saturation + => PdfName.Get(nameof(Saturation)); + public static PdfName Screen + => PdfName.Get(nameof(Screen)); public static PdfName Separation => PdfName.Get(nameof(Separation)); public static PdfName Shading @@ -224,6 +256,8 @@ public static PdfName Size => PdfName.Get(nameof(Size)); public static PdfName SMask => PdfName.Get(nameof(SMask)); + public static PdfName SoftLight + => PdfName.Get(nameof(SoftLight)); public static PdfName Standard => PdfName.Get(nameof(Standard)); public static PdfName StemV