Skip to content
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
12 changes: 9 additions & 3 deletions src/EPPlus.Export.Pdf.Tests/PdfTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,11 @@ Date Author Change
using OfficeOpenXml.Export.PdfExport.Data;
using OfficeOpenXml.Export.PdfExport.Layout;
using OfficeOpenXml.Export.PdfExport.Settings;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Information;
using OfficeOpenXml.Interfaces.Fonts;
using OfficeOpenXml.Style;
using OfficeOpenXml.Table;
using System.Data;
using System.Diagnostics;
using System.Globalization;
using System.Reflection.Metadata;
using System.Text;
using System.Text.RegularExpressions;

Expand Down Expand Up @@ -1105,5 +1102,14 @@ private void InitDataTable()
_dataTable.Rows.Add("Thailand", 69950850, 513120);
_dataTable.Rows.Add("Vietnam", 98168833, 331212);
}

[TestMethod]
public void PngTransparentTest()
{
using var p = OpenTemplatePackage("Allsvenskan2001.xlsx");
var ws = p.Workbook.Worksheets[0];
string path = _pdfPath + "Allsvenskan2001.pdf";
ws.SaveAsPdf(path);
}
}
}
14 changes: 12 additions & 2 deletions src/EPPlus.Export.Pdf/DocumentObjects/PdfImageXObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ public PdfImageXObject(int objectNumber, byte[] imageBytes, int version = 0)
int hival = palette == null || palette.Length < 3 ? 0 : (palette.Length / 3) - 1;
ColorSpace = "[ /Indexed /DeviceRGB " + hival + " <" + PngDecoder.ToHex(palette) + "> ]";
colors = 1;
if (PngDecoder.DecodeIndexedAlpha(imageBytes, width, height, bitDepth, out byte[] indexedAlpha))
{
SoftMaskData = indexedAlpha;
HasSoftMask = true;
}
break;
default:
ColorSpace = "/DeviceRGB";
Expand Down Expand Up @@ -217,8 +222,13 @@ internal static bool ProducesSoftMask(byte[] imageBytes)
{
if (!PngDecoder.ReadPngHeader(imageBytes, out int _, out int _, out int bitDepth, out int colorType, out int interlace))
return false;
if (interlace != 0) return false;
return (colorType == 4 || colorType == 6) && bitDepth == 8;
if (interlace != 0)
return false;
if ((colorType == 4 || colorType == 6) && bitDepth == 8)
return true;
if (colorType == 3)
return PngDecoder.TryReadChunk(imageBytes, "tRNS", out byte[] _);
return false;
}
if (GifDecoder.IsGif(imageBytes))
{
Expand Down
105 changes: 105 additions & 0 deletions src/EPPlus.Export.Pdf/Helpers/PngDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,111 @@ internal static void DecodePngWithAlpha(byte[] pngBytes, int width, int height,
deflatedAlpha = PdfFlate.CompressLeaveOpen(alpha);
}

internal static bool TryReadChunk(byte[] d, string chunkType, out byte[] data)
{
data = null;
if (!IsPng(d)) return false;
int p = _pngSignature.Length;
while (p + 8 <= d.Length)
{
int len = ReadBE32(d, p);
string type = Ascii(d, p + 4, 4);
int dataStart = p + 8;
if (len < 0 || dataStart + len + 4 > d.Length) break;
if (type == chunkType)
{
data = new byte[len];
System.Array.Copy(d, dataStart, data, 0, len);
return true;
}
if (type == "IEND") break;
p = dataStart + len + 4;
}
return false;
}

internal static bool DecodeIndexedAlpha(byte[] pngBytes, int width, int height, int bitDepth, out byte[] deflatedAlpha)
{
deflatedAlpha = null;
if (!TryReadChunk(pngBytes, "tRNS", out byte[] trns) || trns == null || trns.Length == 0)
return false;

byte[] filtered = PdfFlate.Decompress(ReadPngIdat(pngBytes, out byte[] _));
const int bpp = 1;
int stride = (width * bitDepth + 7) / 8;
var prev = new byte[stride];
var cur = new byte[stride];
var alpha = new byte[width * height];
int pos = 0, ai = 0;
for (int y = 0; y < height; y++)
{
int filter = pos < filtered.Length ? filtered[pos++] : 0;
for (int x = 0; x < stride; x++)
{
int raw = pos < filtered.Length ? filtered[pos++] : 0;
int a = x >= bpp ? cur[x - bpp] : 0;
int b = prev[x];
int c = x >= bpp ? prev[x - bpp] : 0;
int val;
switch (filter)
{
case 1: //Sub
val = raw + a;
break;
case 2: //Up
val = raw + b;
break;
case 3: //Average
val = raw + ((a + b) >> 1);
break;
case 4: //Paeth
val = raw + Paeth(a, b, c);
break;
default: //None
val = raw;
break;
}
cur[x] = (byte)(val & 0xFF);
}
for (int x = 0; x < width; x++)
{
int index = GetSample(cur, x, bitDepth);
alpha[ai++] = index < trns.Length ? trns[index] : (byte)255;
}
var swap = prev; prev = cur; cur = swap;
}
deflatedAlpha = PdfFlate.CompressLeaveOpen(alpha);
return true;
}

private static int GetSample(byte[] scanline, int x, int bitDepth)
{
switch (bitDepth)
{
case 8:
return scanline[x];
case 4:
{
int b = scanline[x >> 1];
return (x & 1) == 0 ? (b >> 4) & 0x0F : b & 0x0F;
}
case 2:
{
int b = scanline[x >> 2];
int shift = 6 - 2 * (x & 3);
return (b >> shift) & 0x03;
}
case 1:
{
int b = scanline[x >> 3];
int shift = 7 - (x & 7);
return (b >> shift) & 0x01;
}
default:
return scanline[x];
}
}

internal static int Paeth(int a, int b, int c)
{
int p = a + b - c;
Expand Down
Loading