Skip to content

Features/add decode array for separation image #71

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 22 additions & 19 deletions samples/Synercoding.FileFormats.Pdf.ConsoleTester/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -226,33 +226,36 @@ public static void Main(string[] args)
context.Stroke();
});
});
}

using (var pantherPngStream = File.OpenRead("FreePngImage_com/59872-jaguar-panther-royalty-free-cougar-black-cheetah.png"))
using (var pantherImage = SixLabors.ImageSharp.Image.Load<Rgba32>(pantherPngStream))
{
var pantherImg = writer.AddImage(pantherImage);
var transparentPanther = writer.AddSeparationImage(new Separation(LowLevel.PdfName.Get("White"), PredefinedColors.Yellow), pantherImage, GrayScaleMethod.AlphaChannel);

writer.AddPage(page =>
using (var pantherPngStream = File.OpenRead("FreePngImage_com/59872-jaguar-panther-royalty-free-cougar-black-cheetah.png"))
using (var pantherSixImage = SixLabors.ImageSharp.Image.Load<Rgba32>(pantherPngStream))
{
page.MediaBox = mediaBox;
page.TrimBox = trimBox;
var pantherImg = writer.AddImage(pantherSixImage);
var transparentPanther = writer.AddSeparationImage(new Separation(LowLevel.PdfName.Get("White"), PredefinedColors.Yellow), pantherSixImage, GrayScaleMethod.AlphaChannel, [0, 1]);

writer.AddPage(page =>
{
page.MediaBox = mediaBox;
page.TrimBox = trimBox;

var scale = (double)transparentPanther.Width / transparentPanther.Height;
var pantherSize = new Rectangle(0, 0, 216, 216 / scale, Unit.Millimeters);
var scale = (double)blurImage.Width / blurImage.Height;
page.Content.AddImage(reusedImage, new Rectangle(0, 0, scale * 303, 303, Unit.Millimeters));

page.Content.AddImage(pantherImage, pantherSize);
scale = (double)transparentPanther.Width / transparentPanther.Height;
var pantherSize = new Rectangle(0, 0, 216, 216 / scale, Unit.Millimeters);

page.Content.WrapInState(pantherImage, (image, content) =>
{
content.SetExtendedGraphicsState(new ExtendedGraphicsState()
page.Content.AddImage(pantherImg, pantherSize);

page.Content.WrapInState(pantherSixImage, (image, content) =>
{
Overprint = true
content.SetExtendedGraphicsState(new ExtendedGraphicsState()
{
Overprint = true
});
content.AddImage(transparentPanther, pantherSize);
});
content.AddImage(transparentPanther, pantherSize);
});
});
}
}
}
}
Expand Down
10 changes: 9 additions & 1 deletion src/Synercoding.FileFormats.Pdf/GrayScaleMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,13 @@ public enum GrayScaleMethod
/// <summary>
/// Use the average of the Red, Green and Blue channels.
/// </summary>
AverageOfRGBChannels
AverageOfRGBChannels,
/// <summary>
/// The constants defined by ITU-R BT.601 are 0.299 red + 0.587 green + 0.114 blue.
/// </summary>
BT601,
/// <summary>
/// The constants defined by ITU-R BT.709 are 0.2126 red + 0.7152 green + 0.0722 blue.
/// </summary>
BT709,
}
51 changes: 39 additions & 12 deletions src/Synercoding.FileFormats.Pdf/Image.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using Synercoding.FileFormats.Pdf.LowLevel;
using Synercoding.FileFormats.Pdf.LowLevel.Colors.ColorSpaces;
using Synercoding.FileFormats.Pdf.LowLevel.XRef;
using System.IO.Compression;

namespace Synercoding.FileFormats.Pdf;

Expand All @@ -14,7 +13,7 @@ public class Image : IDisposable
{
private protected bool _disposed;

internal Image(PdfReference id, Stream jpgStream, int width, int height, ColorSpace colorSpace, Image? softMask, params StreamFilter[] filters)
internal Image(PdfReference id, Stream jpgStream, int width, int height, ColorSpace colorSpace, Image? softMask, double[]? decodeArray, params StreamFilter[] filters)
{
Reference = id;

Expand All @@ -23,13 +22,16 @@ internal Image(PdfReference id, Stream jpgStream, int width, int height, ColorSp
RawStream = jpgStream;
ColorSpace = colorSpace;
SoftMask = softMask;
DecodeArray = decodeArray;
Filters = filters;
}

internal Image? SoftMask { get; private set; }

internal Stream RawStream { get; private set; }

internal double[]? DecodeArray { get; private set; }

/// <summary>
/// A pdf reference object that can be used to reference to this object
/// </summary>
Expand Down Expand Up @@ -77,23 +79,46 @@ private static Stream _encodeToJpg(SixLabors.ImageSharp.Image image)

internal static Image Get(TableBuilder tableBuilder, Image<Rgba32> image)
{
return new Image(tableBuilder.ReserveId(), _encodeToJpg(image), image.Width, image.Height, DeviceRGB.Instance, GetMask(tableBuilder, image), StreamFilter.DCTDecode);
return new Image(tableBuilder.ReserveId(), _encodeToJpg(image), image.Width, image.Height, DeviceRGB.Instance, GetMask(tableBuilder, image), null, StreamFilter.DCTDecode);
}

internal static Image? GetMask(TableBuilder tableBuilder, Image<Rgba32> image)
{
var hasTrans = image.Metadata.TryGetPngMetadata(out var pngMeta)
&&
(
pngMeta.ColorType == SixLabors.ImageSharp.Formats.Png.PngColorType.RgbWithAlpha
|| pngMeta.ColorType == SixLabors.ImageSharp.Formats.Png.PngColorType.GrayscaleWithAlpha
);

return hasTrans
? new Image(tableBuilder.ReserveId(), AsImageByteStream(image, GrayScaleMethod.AlphaChannel), image.Width, image.Height, DeviceGray.Instance, null, StreamFilter.FlateDecode)
return _hasTransparancy(image)
? new Image(tableBuilder.ReserveId(), AsImageByteStream(image, GrayScaleMethod.AlphaChannel), image.Width, image.Height, DeviceGray.Instance, null, null, StreamFilter.FlateDecode)
: null;
}

private static bool _hasTransparancy(Image<Rgba32> image)
{
if( image.Metadata.TryGetPngMetadata(out var pngMeta))
{
if (pngMeta.ColorType == SixLabors.ImageSharp.Formats.Png.PngColorType.RgbWithAlpha)
return true;
if (pngMeta.ColorType == SixLabors.ImageSharp.Formats.Png.PngColorType.GrayscaleWithAlpha)
return true;
}

bool hasTransparancy = false;
image.ProcessPixelRows(accessor =>
{
for (int y = 0; y < accessor.Height; y++)
{
var row = accessor.GetRowSpan(y);
for (int x = 0; x < row.Length; x++)
{
ref Rgba32 pixel = ref row[x];
if (pixel.A != 0xFF)
{
hasTransparancy = true;
return;
}
}
}
});
return hasTransparancy;
}

internal static Stream AsImageByteStream(Image<Rgba32> image, GrayScaleMethod grayScaleMethod)
{
using (var byteStream = new MemoryStream())
Expand All @@ -118,6 +143,8 @@ internal static Stream AsImageByteStream(Image<Rgba32> image, GrayScaleMethod gr
GrayScaleMethod.GreenChannel => pixel.G,
GrayScaleMethod.BlueChannel => pixel.B,
GrayScaleMethod.AverageOfRGBChannels => (byte)( ( pixel.R + pixel.G + pixel.B ) / 3 ),
GrayScaleMethod.BT601 => (byte)( ( pixel.R * 0.299 ) + ( pixel.G * 0.587 ) + ( pixel.B * 0.114 ) ),
GrayScaleMethod.BT709 => (byte)( ( pixel.R * 0.2126 ) + ( pixel.G * 0.7152 ) + ( pixel.B * 0.0722 ) ),
_ => throw new NotImplementedException()
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public PdfName AddJpgUnsafe(System.IO.Stream jpgStream, int originalWidth, int o
{
var id = _tableBuilder.ReserveId();

var pdfImage = new Image(id, jpgStream, originalWidth, originalHeight, colorSpace, null, StreamFilter.DCTDecode);
var pdfImage = new Image(id, jpgStream, originalWidth, originalHeight, colorSpace, null, null, StreamFilter.DCTDecode);

return AddImage(pdfImage);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Synercoding.FileFormats.Pdf/LowLevel/ObjectStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public ObjectStream Write(Image image)
.Write(PdfName.Get("Width"), image.Width)
.Write(PdfName.Get("Height"), image.Height)
.Write(PdfName.Get("BitsPerComponent"), 8)
.Write(PdfName.Get("Decode"), _decodeArray(image.ColorSpace))
.Write(PdfName.Get("Decode"), image.DecodeArray ?? _decodeArray(image.ColorSpace))
.WriteIfNotNull(PdfName.Get("SMask"), image.SoftMask?.Reference);


Expand Down
13 changes: 10 additions & 3 deletions src/Synercoding.FileFormats.Pdf/PdfWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,18 +167,25 @@ public Image AddImage(Image<Rgba32> image)
/// <param name="separation">The <see cref="Separation"/> to use.</param>
/// <param name="image">The image to use.</param>
/// <param name="grayScaleMethod">The <see cref="GrayScaleMethod"/> to use.</param>
/// <param name="decodeArray">Optional decode array to use, default value is <c>[ 0.0 1.0 ]</c></param>
/// <returns>The SeparationImage reference that can be used in pages</returns>
public Image AddSeparationImage(Separation separation, Image<Rgba32> image, GrayScaleMethod grayScaleMethod)
public Image AddSeparationImage(Separation separation, Image<Rgba32> image, GrayScaleMethod grayScaleMethod, double[]? decodeArray = null)
{
_throwWhenEndingWritten();

decodeArray ??= new double[] { 0, 1 };
if(decodeArray.Length != 2)
throw new ArgumentOutOfRangeException(nameof(decodeArray), "Length of decode array for separation images should be 2.");
if (decodeArray.Any(v => v < 0 || v > 1))
throw new ArgumentOutOfRangeException(nameof(decodeArray), "All values of the decode array should be between 0 and 1.");

var id = _tableBuilder.ReserveId();

var mask = Image.GetMask(_tableBuilder, image);

var imageStream = Image.AsImageByteStream(image, grayScaleMethod);

var pdfImage = new Image(id, imageStream, image.Width, image.Height, separation, mask, StreamFilter.FlateDecode);
var pdfImage = new Image(id, imageStream, image.Width, image.Height, separation, mask, decodeArray, StreamFilter.FlateDecode);

_objectStream.Write(pdfImage);

Expand All @@ -202,7 +209,7 @@ public Image AddJpgUnsafe(Stream jpgStream, int originalWidth, int originalHeigh

var id = _tableBuilder.ReserveId();

var pdfImage = new Image(id, jpgStream, originalWidth, originalHeight, colorSpace, null, StreamFilter.DCTDecode);
var pdfImage = new Image(id, jpgStream, originalWidth, originalHeight, colorSpace, null, null, StreamFilter.DCTDecode);

_objectStream.Write(pdfImage);

Expand Down
Loading