Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
83ec240
started work in scaling pdf with percentage
AdrianEPPlus Sep 22, 2026
76ec058
Merge branch 'develop9' into feature/pdf_scaling
AdrianEPPlus Sep 22, 2026
246d6c6
Progress
AdrianEPPlus Sep 22, 2026
b1593ac
Scaling header and footer
AdrianEPPlus Sep 23, 2026
4e1303d
Added fit to page test.
AdrianEPPlus Sep 24, 2026
b65aa60
scaling progress
AdrianEPPlus Oct 1, 2026
40117fe
Added more tests for scaling
AdrianEPPlus Oct 5, 2026
9036253
Merge branch 'develop9' into feature/pdf_scaling
AdrianEPPlus Oct 5, 2026
9fdbd4d
Fixed layout order and renamed PdfCatalog to EPPlusToPdfWriter
AdrianEPPlus Oct 7, 2026
e02c710
Merge branch 'develop9' into fix/_pdf_minior_issues
AdrianEPPlus Oct 7, 2026
a9e363d
removed debug comments
AdrianEPPlus Oct 7, 2026
912d439
removed more comments from pdf.
AdrianEPPlus Oct 7, 2026
5dd4d79
Added additional info to info object.
AdrianEPPlus Oct 8, 2026
a930c16
Merge branch 'develop9' into feature/pdf_info_object_update
swmal Oct 8, 2026
7a7a324
Merge branch 'develop9' into feature/pdf_info_object_update
swmal Oct 8, 2026
d7cac71
fixed null error when reading File from package.
AdrianEPPlus Oct 8, 2026
cc62230
header footer scaling.
AdrianEPPlus Oct 12, 2026
2ed20f9
align with page margins progress
AdrianEPPlus Oct 12, 2026
34386a3
Align with page margins done
AdrianEPPlus Oct 12, 2026
84e91a5
Merge branch 'develop9' into fix/pdf_hf_fixes
AdrianEPPlus Oct 13, 2026
fa91931
removed commented code.
AdrianEPPlus Oct 13, 2026
0244d3c
Added shrink to fit & better check clipping for spilling text.
AdrianEPPlus Oct 13, 2026
b00cdbb
Fixed fill selection for total row in tables.
AdrianEPPlus Oct 13, 2026
7b41847
Added draft feature to pdf.
AdrianEPPlus Oct 14, 2026
30eeb35
Added feature for black and white PDF export.
AdrianEPPlus Oct 14, 2026
4f659ce
Added black and white support for jpegs
AdrianEPPlus Oct 16, 2026
cb472d2
removed comments
AdrianEPPlus Oct 19, 2026
bdcbd2a
Merge branch 'develop9' into feature/pdf_draft
AdrianEPPlus Oct 20, 2026
cc68ece
Merge branch 'feature/pdf_draft' into feature/pdf_blackwhite
AdrianEPPlus Oct 20, 2026
346bbcf
Merge branch 'develop9' into feature/pdf_blackwhite
AdrianEPPlus Oct 20, 2026
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
28 changes: 28 additions & 0 deletions src/EPPlus.Export.Pdf.Tests/PdfTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1336,5 +1336,33 @@ public void PdfIsDraftTest1()
string path = _pdfPath + "DraftTest1.pdf";
ws.SaveAsPdf(path);
}

[TestMethod]
public void PdfIsBlackAndWhiteTest1()
{
using var p = OpenTemplatePackage("PDFTest.xlsx");
var ws = p.Workbook.Worksheets[0];
ws.PrinterSettings.BlackAndWhite = true;
string path = _pdfPath + "BlackAndWhiteTest1.pdf";
ws.SaveAsPdf(path);
}
[TestMethod]
public void PdfIsBlackAndWhiteTest2()
{
using var p = OpenTemplatePackage("CMYK.xlsx");
var ws = p.Workbook.Worksheets[0];
ws.PrinterSettings.BlackAndWhite = true;
string path = _pdfPath + "BlackAndWhiteTest2.pdf";
ws.SaveAsPdf(path);
}
[TestMethod]
public void PdfIsBlackAndWhiteTest3()
{
using var p = OpenTemplatePackage("ProgressiveJPEG.xlsx");
var ws = p.Workbook.Worksheets[0];
ws.PrinterSettings.BlackAndWhite = true;
string path = _pdfPath + "BlackAndWhiteTest3.pdf";
ws.SaveAsPdf(path);
}
}
}
2 changes: 1 addition & 1 deletion src/EPPlus.Export.Pdf/DocumentObjects/PdfContentStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ public void AddText(PdfCellContentLayout cell, Vector2 position, double textRota
commands.Add($"S");
}
commands.Add("BT");
commands.Add(color.ToFillCommand());
commands.Add((pageSettings.BlackAndWhite ? Color.Black : color).ToFillCommand());
commands.Add($"{textMatrix.A.ToPdfStringF4()} {textMatrix.B.ToPdfStringF4()} {textMatrix.C.ToPdfStringF4()} {textMatrix.D.ToPdfStringF4()} {textMatrix.E.ToPdfStringF4()} {textMatrix.F.ToPdfStringF4()} Tm");
byte currentFontId = shapedText.ShapedText.Glyphs.Length > 0 ? shapedText.ShapedText.Glyphs[0].FontId : (byte)0;
string currentFontLabel = shapedText.FontIdMap.ContainsKey(currentFontId)
Expand Down
58 changes: 46 additions & 12 deletions src/EPPlus.Export.Pdf/DocumentObjects/PdfImageXObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ internal class PdfImageXObject : PdfObject
internal byte[] SoftMaskData { get; }
internal int SoftMaskObjectNumber { get; set; }

public PdfImageXObject(int objectNumber, byte[] imageBytes, int version = 0)
public PdfImageXObject(int objectNumber, byte[] imageBytes, bool blackAndWhite, int version = 0)
: base(objectNumber, version)
{
byte[] icoDib = null;
Expand All @@ -46,6 +46,7 @@ public PdfImageXObject(int objectNumber, byte[] imageBytes, int version = 0)
BitsPerComponent = 8;
ColorSpace = "/DeviceRGB";
Filter = "FlateDecode";
if (blackAndWhite) GrayscaleRgb(icoRgb);
_bytes = PdfFlate.CompressLeaveOpen(icoRgb);
if (icoAlpha != null)
{
Expand All @@ -55,20 +56,32 @@ public PdfImageXObject(int objectNumber, byte[] imageBytes, int version = 0)
}
else if (JpegDecoder.IsJpeg(imageBytes))
{
_bytes = imageBytes;
Filter = "DCTDecode";
BitsPerComponent = 8;
JpegDecoder.ReadJpegInfo(imageBytes, out int width, out int height, out int components, out bool adobe);
Width = width;
Height = height;
if (components == 4)
if (blackAndWhite && JpegCmykHelper.TryDecode(imageBytes, out int gw, out int gh, out byte[] grayPixels))
{
ColorSpace = "/DeviceCMYK";
if (adobe) Decode = "[ 1 0 1 0 1 0 1 0 ]";
Width = gw;
Height = gh;
BitsPerComponent = 8;
ColorSpace = "/DeviceGray";
Filter = "FlateDecode";
_bytes = PdfFlate.CompressLeaveOpen(grayPixels);
}
else
{
ColorSpace = components == 1 ? "/DeviceGray" : "/DeviceRGB";
JpegDecoder.ReadJpegInfo(imageBytes, out int width, out int height, out int components, out bool adobe);
_bytes = blackAndWhite ? JpegBlackWhiteHelper.MakeGrayscale(imageBytes) : imageBytes;
Filter = "DCTDecode";
BitsPerComponent = 8;
Width = width;
Height = height;
if (components == 4)
{
ColorSpace = "/DeviceCMYK";
if (adobe) Decode = "[ 1 0 1 0 1 0 1 0 ]";
}
else
{
ColorSpace = components == 1 ? "/DeviceGray" : "/DeviceRGB";
}
}
}
else if (PngDecoder.IsPng(imageBytes))
Expand All @@ -81,11 +94,18 @@ public PdfImageXObject(int objectNumber, byte[] imageBytes, int version = 0)
{
BitsPerComponent = 8;
ColorSpace = colorType == 6 ? "/DeviceRGB" : "/DeviceGray";
PngDecoder.DecodePngWithAlpha(imageBytes, width, height, colorType, out byte[] color, out byte[] alpha);
PngDecoder.DecodePngWithAlpha(imageBytes, width, height, blackAndWhite, colorType, out byte[] color, out byte[] alpha);
_bytes = color;
SoftMaskData = alpha;
HasSoftMask = true;
}
else if (colorType == 2 && blackAndWhite)
{
BitsPerComponent = 8;
ColorSpace = "/DeviceRGB";
PngDecoder.DecodePngTruecolor(imageBytes, width, height, blackAndWhite, out byte[] grayRgb);
_bytes = grayRgb;
}
else
{
BitsPerComponent = bitDepth;
Expand All @@ -98,6 +118,8 @@ public PdfImageXObject(int objectNumber, byte[] imageBytes, int version = 0)
colors = 1;
break;
case 3:
if (blackAndWhite && palette != null)
GrayscaleRgb(palette);
int hival = palette == null || palette.Length < 3 ? 0 : (palette.Length / 3) - 1;
ColorSpace = "[ /Indexed /DeviceRGB " + hival + " <" + PngDecoder.ToHex(palette) + "> ]";
colors = 1;
Expand All @@ -124,6 +146,7 @@ public PdfImageXObject(int objectNumber, byte[] imageBytes, int version = 0)
BitsPerComponent = 8;
ColorSpace = "/DeviceRGB";
Filter = "FlateDecode";
if (blackAndWhite) GrayscaleRgb(bmpRgb);
_bytes = PdfFlate.CompressLeaveOpen(bmpRgb);
}
else if (GifDecoder.TryDecode(imageBytes, out int gifW, out int gifH, out byte[] gifRgb, out byte[] gifAlpha))
Expand All @@ -132,6 +155,7 @@ public PdfImageXObject(int objectNumber, byte[] imageBytes, int version = 0)
BitsPerComponent = 8;
ColorSpace = "/DeviceRGB";
Filter = "FlateDecode";
if (blackAndWhite) GrayscaleRgb(gifRgb);
_bytes = PdfFlate.CompressLeaveOpen(gifRgb);
if (gifAlpha != null) { SoftMaskData = PdfFlate.CompressLeaveOpen(gifAlpha); HasSoftMask = true; }
}
Expand All @@ -142,6 +166,7 @@ public PdfImageXObject(int objectNumber, byte[] imageBytes, int version = 0)
BitsPerComponent = 8;
ColorSpace = "/DeviceRGB";
Filter = "FlateDecode";
if (blackAndWhite) GrayscaleRgb(tifRgb);
_bytes = PdfFlate.CompressLeaveOpen(tifRgb);
if (tifAlpha != null)
{
Expand Down Expand Up @@ -170,6 +195,15 @@ private PdfImageXObject(int objectNumber, int version, byte[] deflatedGray, int
Filter = "FlateDecode";
}

internal static void GrayscaleRgb(byte[] rgb)
{
for (int i = 0; i + 2 < rgb.Length; i += 3)
{
byte y = (byte)(0.2126 * rgb[i] + 0.7152 * rgb[i + 1] + 0.0722 * rgb[i + 2] + 0.5);
rgb[i] = rgb[i + 1] = rgb[i + 2] = y;
}
}

internal static PdfImageXObject CreateSoftMask(int objectNumber, byte[] deflatedGray, int width, int height) => new PdfImageXObject(objectNumber, 0, deflatedGray, width, height);

internal static bool CanEmbed(byte[] imageBytes)
Expand Down
2 changes: 1 addition & 1 deletion src/EPPlus.Export.Pdf/ExcelPdf.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ private void AddImageData()
{
foreach (var image in _dictionaries.Images)
{
var img = image.Value.GetImageObject(_document.Count + 1);
var img = image.Value.GetImageObject(_document.Count + 1, _documentSettings.BlackAndWhite);
if (img.HasSoftMask)
{
var mask = PdfImageXObject.CreateSoftMask(_document.Count + 1, img.SoftMaskData, img.Width, img.Height);
Expand Down
Loading
Loading