Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"docfx": {
"version": "2.78.5",
"version": "2.81.0",
"commands": [
"docfx"
],
Expand Down
11 changes: 8 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@ Post-`1.1.0` improvements accumulate here until the next release is cut.
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 **AngleSharp** to 1.8.2 (resolves GHSA-pgww-w46g-26qg).
- Updated **AngleSharp.Css** from the 1.0.0-beta.144 prerelease to the stable **1.1.2**. Every runtime dependency is now a stable release, so the `NU5104` prerelease-dependency exception is gone.
- Updated bundled native/runtime dependencies to **SkiaSharp** 4.153.1 and **HarfBuzzSharp** 14.2.1.301.
- Updated build and test tooling, including **Microsoft.NET.Test.Sdk** 18.10.1, **xunit.runner.visualstudio** 4.0.0, **coverlet.collector** 10.1.0, **BenchmarkDotNet** 0.15.8, **Microsoft.Playwright** 1.63.0, **PDFtoImage** 5.4.0, **docfx** 2.81.0, and **Microsoft.SourceLink.GitHub** 10.0.401.

### Fixed
- `@layer` blocks now apply, in layer order. Previously their rules were dropped with `CSS-AT-RULE-UNKNOWN-001`.
- An `!important` grid longhand now correctly beats a later normal shorthand in the same rule (for example `grid-row-end: 6 !important; grid-row: 2 / 4` now ends at line 6).
- Updated CI actions to **actions/setup-dotnet** v6 and **github/codeql-action** v4.

## [1.0.2]
Expand Down
9 changes: 4 additions & 5 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,11 @@
<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- CS1591: missing XML doc on internals. -->

<!--
NU5104 (a stable package has a prerelease dependency) is DELIBERATELY demoted from an error to
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.
NU5104 (a stable package has a prerelease dependency) is an ERROR again. It was demoted to a warning
while NetPdf depended on AngleSharp.Css 1.0.0-beta.144; AngleSharp.Css 1.1.2 is stable, so every
runtime dependency is now a stable release and the exception is removed. Do not re-add the demotion
without a written reason here.
-->
<WarningsNotAsErrors>$(WarningsNotAsErrors);NU5104</WarningsNotAsErrors>
</PropertyGroup>

<!-- NuGet package authoring best practices. -->
Expand Down
45 changes: 26 additions & 19 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,40 @@
review before being added here (see docs/clean-room-policy.md). -->
<ItemGroup>
<!--
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.
AngleSharp.Css 1.1.2 — the first STABLE release NetPdf can take. 1.0.0 and 1.0.1 were held back because
they broke background-image painting; 1.1.x fixes that (all background tests and every rendering golden
are unchanged). What 1.1.x changes, and what NetPdf does about it:
* It parses @container and @layer as real grouping rules instead of dropping them. The cascade still
SKIPS @container bodies (container queries are unsupported) and now APPLIES @layer bodies in layer
order — previously those were dropped. Pinned by CascadeCorpusTests.
* It expands a single-value `gap: 20px` / `grid-gap: 20px` into row-gap + an EMPTY column-gap
(a 1.1.x bug; CSS Box Alignment §8.3 says both get the value). CssParserAdapter repairs the empty
column gap from row-gap — see SingleValueGapTwin. Without it every one-value flex/grid gap is lost.
* Its per-rule dedup no longer discards an !important longhand before a later shorthand, so the
grid-row-end known gap closed upstream.
Before bumping it again: run the W3cConformance gates and CssParserAdapterPreprocessTests locally.
-->
<PackageVersion Include="AngleSharp" Version="1.6.0" />
<PackageVersion Include="AngleSharp.Css" Version="1.0.0-beta.144" />
<PackageVersion Include="HarfBuzzSharp" Version="14.2.1.2" />
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Linux" Version="14.2.1.2" />
<PackageVersion Include="HarfBuzzSharp.NativeAssets.macOS" Version="14.2.1.2" />
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Win32" Version="14.2.1.2" />
<PackageVersion Include="SkiaSharp" Version="4.151.1" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="4.151.1" />
<PackageVersion Include="SkiaSharp.NativeAssets.macOS" Version="4.151.1" />
<PackageVersion Include="SkiaSharp.NativeAssets.Win32" Version="4.151.1" />
<PackageVersion Include="AngleSharp" Version="1.8.2" />
<PackageVersion Include="AngleSharp.Css" Version="1.1.2" />
Comment thread
raroche marked this conversation as resolved.
Resolved
<PackageVersion Include="HarfBuzzSharp" Version="14.2.1.301" />
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Linux" Version="14.2.1.301" />
<PackageVersion Include="HarfBuzzSharp.NativeAssets.macOS" Version="14.2.1.301" />
<PackageVersion Include="HarfBuzzSharp.NativeAssets.Win32" Version="14.2.1.301" />
<PackageVersion Include="SkiaSharp" Version="4.153.1" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="4.153.1" />
<PackageVersion Include="SkiaSharp.NativeAssets.macOS" Version="4.153.1" />
<PackageVersion Include="SkiaSharp.NativeAssets.Win32" Version="4.153.1" />
Comment thread
raroche marked this conversation as resolved.
Resolved
</ItemGroup>

<!-- Test-only dependencies. -->
<ItemGroup>
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="4.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
<PackageVersion Include="coverlet.collector" Version="10.0.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.10.1" />
<PackageVersion Include="coverlet.collector" Version="10.1.0" />
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8" />
<PackageVersion Include="SharpFuzz" Version="2.3.0" />
<PackageVersion Include="Microsoft.Playwright" Version="1.62.0" />
<PackageVersion Include="Microsoft.Playwright" Version="1.63.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.
PDFtoImage 5.4.0 declares SkiaSharp 4.150.1, and production pins 4.151.1 just above it, so NuGet
Expand Down Expand Up @@ -67,7 +74,7 @@

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

</Project>
53 changes: 51 additions & 2 deletions src/NetPdf.Css/Parser/CssParserAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1094,7 +1094,10 @@ private static ImmutableArray<CssDeclaration> AdaptDeclarations(ICssStyleDeclara
private static ImmutableArray<CssDeclaration> AdaptProperties(IEnumerable<ICssProperty> properties)
{
var output = ImmutableArray.CreateBuilder<CssDeclaration>();
foreach (var property in properties)
// Materialized once: the single-value-gap repair below needs to look at a SIBLING declaration
// (row-gap) that AngleSharp may emit after the one being repaired (column-gap).
var list = properties as IReadOnlyList<ICssProperty> ?? new List<ICssProperty>(properties);
foreach (var property in list)
{
if (property is null || string.IsNullOrEmpty(property.Name)) continue;
// RC-13 — skip EMPTY-valued declarations, but ONLY for standard properties. AngleSharp expands
Expand All @@ -1113,7 +1116,30 @@ private static ImmutableArray<CssDeclaration> AdaptProperties(IEnumerable<ICssPr
// removing an authored custom-property declaration (whose empty token stream is valid and must
// reach VarResolver) should that upstream behavior change.
var isCustomProperty = property.Name.StartsWith("--", StringComparison.Ordinal);
if (!isCustomProperty && string.IsNullOrWhiteSpace(property.Value)) continue;
if (!isCustomProperty && string.IsNullOrWhiteSpace(property.Value))
{
// AngleSharp.Css 1.1.x expands a SINGLE-value `gap: 20px` (and the legacy
// `grid-gap: 20px`) into `row-gap: 20px` + an EMPTY `column-gap`, instead of repeating
// the value as CSS Box Alignment L3 §8.3 requires ("if <column-gap> is omitted, it is
// set to the same value as <row-gap>"). 1.0.0-beta.144 got this right. Dropping the
// empty longhand (the rule above) would silently lose the column gap — the main-axis
// gap of every row flex container and every grid using the one-value form. Repair it
// from the sibling row gap, carrying that declaration's importance.
//
// Safe because an empty standard longhand is never authored CSS — it only ever comes
// out of shorthand expansion. And a `gap: var(--g)` expansion leaves row-gap empty too,
// so this does not fire there; the var() recovery path handles that case as before.
var twin = SingleValueGapTwin(property.Name, list);
if (twin is not null)
{
output.Add(new CssDeclaration(
Property: property.Name,
Value: new CssValue(twin.Value!),
IsImportant: twin.IsImportant,
Comment thread
raroche marked this conversation as resolved.
Resolved
Location: CssSourceLocation.Unknown));
}
continue;
}
output.Add(new CssDeclaration(
Property: property.Name,
Value: new CssValue(property.Value ?? string.Empty),
Expand All @@ -1125,6 +1151,29 @@ private static ImmutableArray<CssDeclaration> AdaptProperties(IEnumerable<ICssPr
: output.ToImmutable();
}

/// <summary>For an empty <c>column-gap</c> / <c>grid-column-gap</c>, returns the non-empty
/// <c>row-gap</c> / <c>grid-row-gap</c> declaration in the same block (the value a single-value
/// <c>gap</c> shorthand should have given both), or <see langword="null"/> if there is none.</summary>
private static ICssProperty? SingleValueGapTwin(string name, IReadOnlyList<ICssProperty> list)
{
string? rowName =
string.Equals(name, "column-gap", StringComparison.OrdinalIgnoreCase) ? "row-gap"
: string.Equals(name, "grid-column-gap", StringComparison.OrdinalIgnoreCase) ? "grid-row-gap"
: null;
if (rowName is null) return null;
for (var i = 0; i < list.Count; i++)
{
var candidate = list[i];
if (candidate is not null
&& string.Equals(candidate.Name, rowName, StringComparison.OrdinalIgnoreCase)
&& !string.IsNullOrWhiteSpace(candidate.Value))
Comment thread
raroche marked this conversation as resolved.
Resolved
{
return candidate;
}
}
return null;
}

private static string NameFromType(ICssRule rule)
{
var name = rule.Type.ToString();
Expand Down
42 changes: 38 additions & 4 deletions tests/NetPdf.RealDocuments/Css/CascadeCorpusTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,14 +202,48 @@ @layer typography {
var result = CascadeResolver.Resolve(document, sheets,
CssMediaContext.DefaultPrint, sink);

// Outer p { color: red } applies. The @layer body's inner rule does not (v1 gap).
// AngleSharp.Css 1.1.x decomposes the @layer body, so the layered rule now reaches the cascade
Comment thread
raroche marked this conversation as resolved.
Resolved
// (under 1.0.0-beta.144 it arrived as an opaque RawBody and was dropped with
// CSS-AT-RULE-UNKNOWN-001). It must lose to the UNLAYERED rule: per CSS Cascade L5 §6.4,
// normal declarations in any layer rank below unlayered normal declarations — even though the
// layered rule comes later in source order.
var p = document.QuerySelector("p")!;
Assert.NotNull(result.TryGetStylesFor(p)?.GetWinner("color"));
// Diagnostic emitted for the opaque @layer body.
Assert.Contains(sink.Diagnostics,
var winner = result.TryGetStylesFor(p)?.GetWinner("color");
Assert.NotNull(winner);
Assert.Equal("rgba(255, 0, 0, 1)", winner!.Declaration.Value.RawText); // red; AngleSharp normalizes to rgba()
// The body is no longer opaque, so the "preserved but not applied" diagnostic is gone.
Assert.DoesNotContain(sink.Diagnostics,
d => d.Code == CssDiagnosticCodes.CssAtRuleUnknown001);
}

[Fact]
public async Task Container_children_are_skipped_even_when_decomposed()
{
// AngleSharp.Css 1.1.x parses @container as a grouping rule and hands the cascade REAL child
// rules (1.0.0-beta.144 dropped the body entirely). A naive "walk every grouping rule" would
// therefore start applying container-query styles UNCONDITIONALLY. v1 does not evaluate
// container queries, so the children must stay skipped and the diagnostic must still fire.
var html = """
<html><head><style>
p { color: green; }
@container (min-width: 1px) {
p { color: red; }
}
</style></head><body><p>x</p></body></html>
""";
var host = new HtmlParsingHost();
var document = await host.ParseAsync(html, new HtmlPdfOptions());

var sheets = AdaptAllSheetsViaPreprocessor(document);
var sink = new CapturingSink();
var result = CascadeResolver.Resolve(document, sheets, CssMediaContext.DefaultPrint, sink);

var p = document.QuerySelector("p")!;
Assert.Equal("rgba(0, 128, 0, 1)", result.TryGetStylesFor(p)!.GetWinner("color")!.Declaration.Value.RawText); // green; AngleSharp normalizes to rgba()
Assert.Contains(sink.Diagnostics,
d => d.Code == CssDiagnosticCodes.CssContainerQueryUnsupported001);
}

private static string LoadCorpusFile(string relativePath)
{
var path = System.IO.Path.Combine(System.AppContext.BaseDirectory, relativePath);
Expand Down
2 changes: 1 addition & 1 deletion tests/NetPdf.RenderingCorpus/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Pin the Playwright version (which pins a specific Chromium revision) — NEVER use a floating tag — so the
# committed reference PNGs are reproducible and CI (Linux) does not drift against a developer's macOS fonts.
# Bump this tag deliberately as part of a reference-regeneration, never implicitly.
FROM mcr.microsoft.com/playwright/python:v1.62.0-jammy
FROM mcr.microsoft.com/playwright/python:v1.63.0-jammy
Comment thread
raroche marked this conversation as resolved.
Resolved

# pypdfium2 rasterizes the Chrome print-to-PDF at the harness DPI (PDFium — SkiaSharp cannot read PDF).
# Install playwright into the SAME python that runs the generator too: the base image ships Playwright but
Expand Down
Loading
Loading