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
2 changes: 1 addition & 1 deletion .github/workflows/capture-benchmark-baseline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
steps:
- uses: actions/checkout@v7
- name: Set up .NET 10
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v6
with:
dotnet-version: "10.0.x"
- name: Platform key (must resolve to linux-x64)
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
steps:
- uses: actions/checkout@v7
- name: Set up .NET 10
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v6
with:
dotnet-version: "10.0.x"

Expand Down Expand Up @@ -128,7 +128,7 @@ jobs:
steps:
- uses: actions/checkout@v7
- name: Set up .NET 10
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v6
with:
dotnet-version: "10.0.x"
- name: Run the visual-regression gate (PDFium diff vs committed Chrome references)
Expand All @@ -147,7 +147,7 @@ jobs:
steps:
- uses: actions/checkout@v7
- name: Set up .NET 10
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v6
with:
dotnet-version: "10.0.x"
- name: Run benchmark gate (vs pinned baseline, +25% tolerance)
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ jobs:
- uses: actions/checkout@v7

- name: Set up .NET 10
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v6
with:
dotnet-version: "10.0.x"

- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v4
with:
languages: csharp
build-mode: manual
Expand All @@ -41,6 +41,6 @@ jobs:
run: dotnet build NetPdf.slnx -c Release

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v4
with:
category: "/language:csharp"
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
steps:
- uses: actions/checkout@v7
- name: Set up .NET 10
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v6
with:
dotnet-version: "10.0.x"
- name: Restore local tools (pinned DocFX)
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/fuzz-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
- uses: actions/checkout@v7

- name: Set up .NET 10
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v6
with:
dotnet-version: "10.0.x"

Expand All @@ -58,7 +58,7 @@ jobs:
steps:
- uses: actions/checkout@v7
- name: Set up .NET 10
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v6
with:
dotnet-version: "10.0.x"
- name: Restore
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
ref: ${{ github.event.inputs.tag || github.ref }}

- name: Set up .NET 10
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v6
with:
dotnet-version: "10.0.x"

Expand Down Expand Up @@ -123,7 +123,7 @@ jobs:
environment: nuget-release
steps:
- name: Set up .NET 10
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v6
with:
dotnet-version: "10.0.x"

Expand Down
15 changes: 13 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,17 @@ All notable changes to NetPdf are documented here. The format follows [Keep a Ch

## [Unreleased]

Post-`1.0.2` improvements accumulate here until the next release is cut.
Post-`1.1.0` improvements accumulate here until the next release is cut.

## [1.1.0]

A minor dependency refresh release that resolves the current AngleSharp vulnerability warning, consolidates the compatible open Dependabot runtime/test-tooling/CI action updates, and migrates code away from SkiaSharp APIs newly obsolete in SkiaSharp 4.x.

### Changed
- Updated **AngleSharp** to 1.6.0 to resolve GHSA-pgww-w46g-26qg; **AngleSharp.Css** remains pinned to 1.0.0-beta.144 because stable 1.0.0 regresses background-image parsing/painting.
- Updated bundled native/runtime dependencies to **SkiaSharp** 4.150.1 and **HarfBuzzSharp** 14.2.1.1.
- Updated build and test tooling, including **Microsoft.NET.Test.Sdk** 18.8.1, **xunit.runner.visualstudio** 3.1.5, **coverlet.collector** 10.0.1, **BenchmarkDotNet** 0.15.8, and **Microsoft.SourceLink.GitHub** 10.0.301.
- Updated CI actions to **actions/setup-dotnet** v6 and **github/codeql-action** v4.

## [1.0.2]

Expand Down Expand Up @@ -72,7 +82,8 @@ The first stable release. `HtmlPdf.Convert(html)` runs the full HTML → CSS →
- Single `NetPdf` NuGet package bundling the whole engine; optional `NetPdf.Languages.*` hyphenation add-ons.
- Source Link + symbol packages for source-stepping.

[Unreleased]: https://github.com/raroche/NetPdf/compare/v1.0.2...HEAD
[Unreleased]: https://github.com/raroche/NetPdf/compare/v1.1.0...HEAD
[1.1.0]: https://github.com/raroche/NetPdf/compare/v1.0.2...v1.1.0
[1.0.2]: https://github.com/raroche/NetPdf/compare/v1.0.1...v1.0.2
[1.0.1]: https://github.com/raroche/NetPdf/compare/v1.0.0...v1.0.1
[1.0.0]: https://github.com/raroche/NetPdf/compare/0.9.0-rc1...v1.0.0
23 changes: 11 additions & 12 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,9 @@

<!--
NU5104 (a stable package has a prerelease dependency) is DELIBERATELY demoted from an error to
a VISIBLE warning rather than suppressed. NetPdf 1.0.0 depends on AngleSharp.Css, which has NO
stable 1.x release — only 1.0.0-beta.* (the last stable, 0.17.0, targets the AngleSharp 0.x API
and is incompatible with the AngleSharp 1.1.2 we require). So NetPdf's stable line carries a
prerelease AngleSharp.Css as a documented, maintainer-approved v1.0 exception (the parser is
mature; the -beta suffix reflects AngleSharp.Css's own release cadence). We do NOT hide it: pack
emits NU5104 so the exception is auditable at release time. Revisit if/when AngleSharp.Css ships
a stable 1.x. See the CHANGELOG 1.0.0 note.
a VISIBLE warning rather than suppressed. NetPdf still depends on AngleSharp.Css 1.0.0-beta.144
because AngleSharp.Css 1.0.0 regresses background-image parsing/painting in the current test suite.
We do NOT hide it: pack emits NU5104 so the exception is auditable at release time.
-->
<WarningsNotAsErrors>$(WarningsNotAsErrors);NU5104</WarningsNotAsErrors>
</PropertyGroup>
Expand Down Expand Up @@ -64,10 +60,13 @@
<!--
PackageReleaseNotes is plain text on nuget.org (not a rendered link), so we
point readers at the machine-readable CHANGELOG rather than duplicating it.
The notes intentionally carry no version string, so there is nothing here to
keep in sync with VersionPrefix — the CHANGELOG link is the source of truth.
The notes intentionally carry no version string AND no version-specific claim
(per PR #355 review [P2]: "First stable release." shipped unchanged in the
1.1.0 package), so there is nothing here to keep in sync with VersionPrefix —
the CHANGELOG link is the source of truth. `ReleaseVersionParityTests`
guards this: keep the text release-agnostic.
-->
<PackageReleaseNotes>First stable release. Full changelog: https://github.com/raroche/NetPdf/blob/main/CHANGELOG.md</PackageReleaseNotes>
<PackageReleaseNotes>Release notes for this and every prior version: https://github.com/raroche/NetPdf/blob/main/CHANGELOG.md</PackageReleaseNotes>

<!--
Versioning. Directory.Build.props is the MSBuild source-of-truth — every
Expand All @@ -76,7 +75,7 @@
doesn't parse XML; if they ever disagree, this file wins for what NuGet
actually packs. Bump protocol: update BOTH files, commit, then `git tag`.
-->
<VersionPrefix>1.0.2</VersionPrefix>
<VersionPrefix>1.1.0</VersionPrefix>
<VersionSuffix></VersionSuffix>

<!-- Symbols & Source Link: enable source-stepping for downstream consumers. -->
Expand All @@ -101,7 +100,7 @@
<EnablePackageValidation>true</EnablePackageValidation>
<!-- Baseline = the previous published release (must exist on nuget.org): fail the build on any
accidental public-API break vs. that release. Bump this to <prev> on each release. -->
<PackageValidationBaselineVersion>1.0.1</PackageValidationBaselineVersion>
<PackageValidationBaselineVersion>1.0.2</PackageValidationBaselineVersion>

<!-- NuGet client requirement. -->
<MinClientVersion>5.0.0</MinClientVersion>
Expand Down
48 changes: 26 additions & 22 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,41 @@
review before being added here (see docs/clean-room-policy.md). -->
<ItemGroup>
<!--
HELD at 1.1.2 / beta.144. The Dependabot bump (AngleSharp 1.5.2 / AngleSharp.Css beta.216) REGRESSES
background-image rendering: `background-image: url(...)` stops painting entirely (29 background-paint
tests drop to zero image output, plus one cascade edge case). Verified by isolation — reverting only
these two, with SkiaSharp/HarfBuzz kept at their new versions, makes the suite green. Do not bump until
the background-image parsing regression is diagnosed against a newer AngleSharp.Css.
AngleSharp.Css is HELD at beta.144. The stable 1.0.0 release regresses background-image rendering:
`background-image: url(...)` stops painting (the background-paint and multi-layer image tests drop to
zero image output) and several pre-pass recovery tests no longer observe the dropped opaque-rule slots.
AngleSharp itself is bumped for GHSA-pgww-w46g-26qg; keep AngleSharp.Css pinned until the parser
regression is diagnosed.
-->
<PackageVersion Include="AngleSharp" Version="1.1.2" />
<PackageVersion Include="AngleSharp" Version="1.6.0" />
<PackageVersion Include="AngleSharp.Css" Version="1.0.0-beta.144" />
<PackageVersion Include="HarfBuzzSharp" Version="8.3.1.5" />
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Linux" Version="8.3.1.5" />
<PackageVersion Include="HarfBuzzSharp.NativeAssets.macOS" Version="8.3.1.5" />
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Win32" Version="8.3.1.5" />
<PackageVersion Include="SkiaSharp" Version="3.119.4" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="3.119.4" />
<PackageVersion Include="SkiaSharp.NativeAssets.macOS" Version="3.119.4" />
<PackageVersion Include="SkiaSharp.NativeAssets.Win32" Version="3.119.4" />
<PackageVersion Include="HarfBuzzSharp" Version="14.2.1.1" />
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Linux" Version="14.2.1.1" />
<PackageVersion Include="HarfBuzzSharp.NativeAssets.macOS" Version="14.2.1.1" />
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Win32" Version="14.2.1.1" />
<PackageVersion Include="SkiaSharp" Version="4.150.1" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="4.150.1" />
<PackageVersion Include="SkiaSharp.NativeAssets.macOS" Version="4.150.1" />
<PackageVersion Include="SkiaSharp.NativeAssets.Win32" Version="4.150.1" />
</ItemGroup>

<!-- Test-only dependencies. -->
<ItemGroup>
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
<PackageVersion Include="coverlet.collector" Version="10.0.1" />
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8" />
<PackageVersion Include="SharpFuzz" Version="2.3.0" />
<PackageVersion Include="Microsoft.Playwright" Version="1.61.0" />
<!-- Visual-regression harness (PR 8): PDFium (via PDFtoImage, ships bblanchon.PDFium native for
macOS/Linux/Win) rasterizes the NetPdf PDF -> RGBA; SkiaSharp only WRITES PDF. Its SkiaSharp floor is
3.119.2 — the repo-wide SkiaSharp pin above matches it, so the harness measures the same renderer
dependency set as production. -->
macOS/Linux/Win) rasterizes the NetPdf PDF -> RGBA; SkiaSharp only WRITES PDF. PDFtoImage 5.2.1
declares a SkiaSharp FLOOR of 3.119.2; production intentionally pins 4.150.1 above (PR #355), and
NuGet unifies the graph to that single higher version — so the harness still rasterizes against the
SAME SkiaSharp the shipped package uses. The tradeoff of the gap: PDFtoImage runs against a SkiaSharp
newer than it compiled against, so a removed 3.x API would surface as a runtime MissingMethodException
rather than a build break. The RenderingCorpus suite is what proves that hasn't happened — if it ever
fails after a SkiaSharp major bump, suspect this gap first. -->
<PackageVersion Include="PDFtoImage" Version="5.2.1" />
</ItemGroup>

Expand All @@ -62,7 +66,7 @@

<!-- Build infrastructure. -->
<ItemGroup>
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="10.0.300" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="10.0.301" />
</ItemGroup>

</Project>
6 changes: 3 additions & 3 deletions build/version.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"version": "1.0.2",
"version": "1.1.0",
"phase": 5,
"phaseDescription": "Packaging & release (v1.0 launch)",
"phaseDescription": "Packaging & release",
"publicApiFrozen": true,
"lastUpdated": "2026-07-10",
"lastUpdated": "2026-07-27",
"_note": "Informational only. The MSBuild source-of-truth is Directory.Build.props (VersionPrefix + VersionSuffix). When bumping versions, update BOTH files together, then create the git tag. ReleaseVersionParityTests guards that this file, Directory.Build.props, and the CHANGELOG's latest version heading agree. Phase 5 will introduce a small MSBuild task that reads this file directly."
}
2 changes: 1 addition & 1 deletion src/NetPdf.Pdf/Images/ImageFilterApplier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ internal static class ImageFilterApplier
using (chain)
using (var paint = new SKPaint { IsAntialias = true, ImageFilter = chain })
{
canvas.DrawBitmap(src, padL, padT, paint);
canvas.DrawBitmap(src, padL, padT, new SKSamplingOptions(SKFilterMode.Linear, SKMipmapMode.None), paint);
}

using var image = surface.Snapshot();
Expand Down
5 changes: 3 additions & 2 deletions src/NetPdf.Pdf/Images/ImageMaskApplier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,11 @@ internal static class ImageMaskApplier

// Draw the base, then multiply its alpha by the mask's alpha (DstIn) — the mask scaled to fill
// the base's pixel box. Result alpha = baseAlpha × maskAlpha; result RGB = base RGB.
canvas.DrawBitmap(baseDecoded, 0, 0);
var sampling = new SKSamplingOptions(SKFilterMode.Linear, SKMipmapMode.None);
canvas.DrawBitmap(baseDecoded, 0, 0, sampling);
using (var paint = new SKPaint { IsAntialias = true, BlendMode = SKBlendMode.DstIn })
{
canvas.DrawBitmap(maskDecoded, new SKRect(0, 0, w, h), paint);
canvas.DrawBitmap(maskDecoded, new SKRect(0, 0, w, h), sampling, paint);
}

using var image = surface.Snapshot();
Expand Down
7 changes: 3 additions & 4 deletions src/NetPdf.Pdf/Images/TextShadowRasterizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,17 +112,16 @@ internal static class TextShadowRasterizer
{
destOffsetXPx = destOffsetYPx = destWidthPx = destHeightPx = 0;

using var fullPath = new SKPath();
using var fullPathBuilder = new SKPathBuilder();
for (var i = 0; i < glyphArray.Length; i++)
{
using var glyphPath = font.GetGlyphPath(glyphArray[i]);
if (glyphPath is not null && !glyphPath.IsEmpty)
{
using var placed = new SKPath();
glyphPath.Transform(SKMatrix.CreateTranslation(penXDevice[i], 0), placed);
fullPath.AddPath(placed);
fullPathBuilder.AddPath(glyphPath, penXDevice[i], 0, SKPathAddMode.Append);
}
}
using var fullPath = fullPathBuilder.Detach();
if (fullPath.IsEmpty) return null; // an all-whitespace run paints no shadow.

var ink = fullPath.TightBounds;
Expand Down
9 changes: 5 additions & 4 deletions src/NetPdf.Svg/SvgClipMask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ public static void ApplyClipPath(SKCanvas canvas, XElement el, SvgStyle style, S
/// when no child contributes geometry.</summary>
private static SKPath? BuildClipPathGeometry(XElement clip, SvgStyle style, SvgRenderState state)
{
SKPath? acc = null;
using var acc = new SKPathBuilder();
var hasPath = false;
foreach (var child in clip.Elements())
{
var target = child;
Expand All @@ -71,10 +72,10 @@ public static void ApplyClipPath(SKCanvas canvas, XElement el, SvgStyle style, S
if (SvgTransform.Parse(SvgRasterizer.Attr(target, "transform")) is { } tm) sp.Transform(tm);
if (useOffset is { } uo) sp.Transform(uo);
if (useTransform is { } ut2) sp.Transform(ut2);
if (acc is null) acc = new SKPath(sp);
else acc.AddPath(sp);
acc.AddPath(sp, SKPathAddMode.Append);
hasPath = true;
}
return acc;
return hasPath ? acc.Detach() : null;
}

/// <summary>The geometry bounding box of an element in its OWN coordinate space (its own
Expand Down
Loading
Loading