Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
150 commits
Select commit Hold shift + click to select a range
133e52c
Add spec compliance tracking for CSS 2.1
eXpl0it3r Nov 9, 2026
9954ae5
Verify and document CSS 2.1 box model compliance
eXpl0it3r Nov 9, 2026
82f1562
Add spec compliance tracking for HTML
eXpl0it3r Nov 9, 2026
5689385
Verify and document HTML grouping content and lists compliance
eXpl0it3r Nov 9, 2026
eb0e560
Record the HTML snapshot the manifest was checked against
eXpl0it3r Nov 9, 2026
ea8afa4
Fix CSS 2.1 section titles that start with a pseudo-class
eXpl0it3r Nov 9, 2026
1a0e106
Verify and document CSS 2.1 introduction compliance
eXpl0it3r Nov 9, 2026
a6dbe2f
Verify and document CSS 2.1 conformance compliance
eXpl0it3r Nov 9, 2026
4d7246f
Verify and document CSS 2.1 syntax and data types compliance
eXpl0it3r Nov 9, 2026
d8171eb
Verify and document CSS 2.1 selectors compliance
eXpl0it3r Nov 9, 2026
2eded5d
Verify and document CSS 2.1 cascade and inheritance compliance
eXpl0it3r Nov 9, 2026
62146cc
Verify and document CSS 2.1 media types compliance
eXpl0it3r Nov 9, 2026
59f6492
Verify and document CSS 2.1 visual formatting model compliance
eXpl0it3r Nov 9, 2026
14b1890
Verify and document CSS 2.1 visual formatting details compliance
eXpl0it3r Nov 9, 2026
25f2c26
Verify and document CSS 2.1 visual effects compliance
eXpl0it3r Nov 9, 2026
dc9b4c6
Verify and document CSS 2.1 generated content and lists compliance
eXpl0it3r Nov 9, 2026
29f63a2
Fix a truncated CSS 2.1 section title and gap
eXpl0it3r Nov 9, 2026
187f925
Verify and document CSS 2.1 paged media compliance
eXpl0it3r Nov 9, 2026
4a9eb94
Verify and document CSS 2.1 colors and backgrounds compliance
eXpl0it3r Nov 9, 2026
7fade9e
Verify and document CSS 2.1 fonts compliance
eXpl0it3r Nov 9, 2026
4282379
Verify and document CSS 2.1 text compliance
eXpl0it3r Nov 9, 2026
af8201e
Verify and document CSS 2.1 tables compliance
eXpl0it3r Nov 9, 2026
db0b5ac
Verify and document CSS 2.1 user interface compliance
eXpl0it3r Nov 9, 2026
586b9e0
Verify and document CSS 2.1 aural style sheets compliance
eXpl0it3r Nov 9, 2026
1d068af
Verify and document CSS 2.1 default style sheet compliance
eXpl0it3r Nov 9, 2026
dbc7140
Verify and document CSS 2.1 stacking context compliance
eXpl0it3r Nov 9, 2026
677acac
Verify and document CSS 2.1 grammar compliance
eXpl0it3r Nov 9, 2026
02abcbb
Verify and document HTML common microsyntaxes compliance
eXpl0it3r Nov 9, 2026
f7ba31d
Verify and document HTML elements and global attributes compliance
eXpl0it3r Nov 9, 2026
61ab1c7
Verify and document HTML document metadata compliance
eXpl0it3r Nov 9, 2026
e9f5b5b
Verify and document HTML sections compliance
eXpl0it3r Nov 9, 2026
efd283b
Verify and document HTML text-level semantics compliance
eXpl0it3r Nov 9, 2026
69d5056
Verify and document HTML links compliance
eXpl0it3r Nov 9, 2026
d5b6af0
Verify and document HTML edits compliance
eXpl0it3r Nov 9, 2026
60139fd
Verify and document HTML embedded content and img compliance
eXpl0it3r Nov 9, 2026
54fd3c0
Verify and document HTML images compliance
eXpl0it3r Nov 9, 2026
3fa895c
Verify and document HTML iframe, embed and object compliance
eXpl0it3r Nov 9, 2026
a7db90b
Verify and document HTML media elements compliance
eXpl0it3r Nov 9, 2026
83fde9c
Verify and document HTML image maps compliance
eXpl0it3r Nov 9, 2026
b225b7e
Verify and document HTML MathML, SVG and dimension attributes compliance
eXpl0it3r Nov 9, 2026
c8daf77
Verify and document HTML tables compliance
eXpl0it3r Nov 9, 2026
127b0e0
Verify and document HTML forms compliance
eXpl0it3r Nov 9, 2026
dffd7db
Verify and document HTML interactive elements compliance
eXpl0it3r Nov 9, 2026
66e9339
Verify and document HTML scripting compliance
eXpl0it3r Nov 9, 2026
577d66f
Verify and document HTML custom elements compliance
eXpl0it3r Nov 9, 2026
42737d3
Verify and document HTML common idioms and selector matching compliance
eXpl0it3r Nov 9, 2026
e9cfa50
Verify and document HTML syntax and parsing compliance
eXpl0it3r Nov 9, 2026
f426883
Verify and document HTML rendering compliance
eXpl0it3r Nov 9, 2026
0b0072a
Verify and document HTML obsolete features compliance
eXpl0it3r Nov 9, 2026
6ecae33
Add spec compliance tracking for the CSS modules
eXpl0it3r Nov 10, 2026
0568c16
Verify and document CSS Syntax Level 3 compliance
eXpl0it3r Nov 10, 2026
45dcfd7
Verify and document CSS Namespaces compliance
eXpl0it3r Nov 10, 2026
eaa4f7b
Verify and document CSS Style Attributes compliance
eXpl0it3r Nov 10, 2026
1d8651b
Verify and document CSS Nesting compliance
eXpl0it3r Nov 10, 2026
6a436ad
Verify and document CSS Cascading Level 5 compliance
eXpl0it3r Nov 10, 2026
395958f
Verify and document CSS Custom Properties compliance
eXpl0it3r Nov 10, 2026
cacddd6
Verify and document CSS Conditional Rules Level 3 compliance
eXpl0it3r Nov 10, 2026
fa07821
Verify and document Media Queries Level 4 compliance
eXpl0it3r Nov 10, 2026
71db587
Verify and document Selectors Level 4 compliance
eXpl0it3r Nov 10, 2026
4c178c7
Verify and document CSS Pseudo-Elements Level 4 compliance
eXpl0it3r Nov 10, 2026
b9ff510
Verify and document CSS Values and Units Level 4 compliance
eXpl0it3r Nov 10, 2026
ee10b34
Verify and document CSS Color Level 4 compliance
eXpl0it3r Nov 10, 2026
d613fdf
Verify and document CSS Color Level 5 compliance
eXpl0it3r Nov 10, 2026
bc7f2a1
Verify and document CSS Display Level 3 compliance
eXpl0it3r Nov 10, 2026
0e15f38
Verify and document CSS Box Model Level 3 compliance
eXpl0it3r Nov 10, 2026
08a8246
Verify and document CSS Positioned Layout Level 3 compliance
eXpl0it3r Nov 10, 2026
685dcb0
Verify and document CSS Box Sizing Level 3 compliance
eXpl0it3r Nov 10, 2026
97c7a48
Verify and document CSS Overflow Level 3 compliance
eXpl0it3r Nov 10, 2026
84bd410
Verify and document CSS Fragmentation Level 3 compliance
eXpl0it3r Nov 10, 2026
676a04c
Verify and document CSS Paged Media Level 3 compliance
eXpl0it3r Nov 10, 2026
747c180
Verify and document CSS Lists and Counters Level 3 compliance
eXpl0it3r Nov 10, 2026
80d64c0
Verify and document CSS Generated Content Level 3 compliance
eXpl0it3r Nov 10, 2026
55a1997
Verify and document CSS Backgrounds and Borders Level 3 compliance
eXpl0it3r Nov 10, 2026
257a615
Verify and document CSS Images Level 3 compliance
eXpl0it3r Nov 10, 2026
41bdb2a
Verify and document CSS Basic User Interface Level 3 compliance
eXpl0it3r Nov 10, 2026
0aab1b2
Verify and document CSS Fonts Level 4 compliance
eXpl0it3r Nov 10, 2026
4ed521b
Verify and document CSS Text Level 3 compliance
eXpl0it3r Nov 10, 2026
3a21ade
Verify and document CSS Text Decoration Level 3 compliance
eXpl0it3r Nov 10, 2026
86aea98
Verify and document CSS Table Level 3 compliance
eXpl0it3r Nov 10, 2026
b03eaca
Record static position and ol reversed gaps found in the CSS modules
eXpl0it3r Nov 10, 2026
7e512d8
Quote the legacy color gap so it isn't cut off at the hash
eXpl0it3r Nov 10, 2026
1180f75
Add spec compliance tracking for the remaining CSS modules
eXpl0it3r Nov 10, 2026
eaed9f1
Track the whole HTML standard
eXpl0it3r Nov 10, 2026
643ed72
Verify and document CSS Flexbox compliance
eXpl0it3r Nov 10, 2026
44bf455
Verify and document CSS Box Alignment compliance
eXpl0it3r Nov 10, 2026
2f6c172
Verify and document CSS Grid Layout compliance
eXpl0it3r Nov 10, 2026
6446fcf
Verify and document CSS Transforms Level 1 compliance
eXpl0it3r Nov 10, 2026
f6b9ce4
Verify and document CSS Transforms Level 2 compliance
eXpl0it3r Nov 10, 2026
e3916ea
Verify and document Motion Path compliance
eXpl0it3r Nov 10, 2026
6602cb3
Verify and document CSS Will Change compliance
eXpl0it3r Nov 10, 2026
0da77ce
Verify and document Compositing and Blending compliance
eXpl0it3r Nov 10, 2026
8d3dca1
Verify and document Filter Effects compliance
eXpl0it3r Nov 10, 2026
2281b6c
Verify and document CSS Masking compliance
eXpl0it3r Nov 10, 2026
acc8e3a
Verify and document CSS Animations compliance
eXpl0it3r Nov 10, 2026
41f1491
Verify and document CSS Transitions compliance
eXpl0it3r Nov 10, 2026
f1682b4
Verify and document CSS Easing Functions compliance
eXpl0it3r Nov 10, 2026
5f5d768
Verify and document Web Animations compliance
eXpl0it3r Nov 10, 2026
bcb469a
Verify and document CSS View Transitions compliance
eXpl0it3r Nov 10, 2026
cc6c7b0
Verify and document CSS Writing Modes compliance
eXpl0it3r Nov 10, 2026
cdcb6c2
Verify and document CSS Logical Properties compliance
eXpl0it3r Nov 10, 2026
3a65ec8
Verify and document CSS Inline Layout compliance
eXpl0it3r Nov 10, 2026
5335425
Verify and document CSS Text Level 4 compliance
eXpl0it3r Nov 10, 2026
25ad034
Verify and document CSS Text Decoration Level 4 compliance
eXpl0it3r Nov 10, 2026
0061a28
Verify and document CSS Fonts Level 5 compliance
eXpl0it3r Nov 10, 2026
2c85c3c
Verify and document CSS Font Loading compliance
eXpl0it3r Nov 10, 2026
16f3fb2
Verify and document CSS Speech compliance
eXpl0it3r Nov 10, 2026
45aec34
Verify and document CSS Box Model Level 4 compliance
eXpl0it3r Nov 10, 2026
37d90c3
Verify and document CSS Sizing Level 4 compliance
eXpl0it3r Nov 10, 2026
cec9182
Verify and document CSS Positioned Layout Level 4 compliance
eXpl0it3r Nov 10, 2026
b187d55
Verify and document CSS Display Level 4 compliance
eXpl0it3r Nov 10, 2026
92b1579
Verify and document CSS Containment Level 2 compliance
eXpl0it3r Nov 10, 2026
b92cb15
Verify and document CSS Fragmentation Level 4 compliance
eXpl0it3r Nov 10, 2026
5e951ec
Verify and document CSS Multi-column Layout compliance
eXpl0it3r Nov 10, 2026
9de2406
Verify and document CSS Generated Content for Paged Media compliance
eXpl0it3r Nov 10, 2026
5d67115
Verify and document CSS Backgrounds Level 4 compliance
eXpl0it3r Nov 10, 2026
63ad0a7
Verify and document CSS Borders Level 4 compliance
eXpl0it3r Nov 10, 2026
3f98d9e
Verify and document CSS Images Level 4 compliance
eXpl0it3r Nov 10, 2026
b2db380
Verify and document CSS Color Adjustment compliance
eXpl0it3r Nov 10, 2026
81b3fad
Verify and document CSS Shapes compliance
eXpl0it3r Nov 10, 2026
e640241
Verify and document CSS Basic User Interface Level 4 compliance
eXpl0it3r Nov 10, 2026
1fc82bf
Verify and document CSS Scrollbars compliance
eXpl0it3r Nov 10, 2026
8641dee
Verify and document CSS Scroll Snap compliance
eXpl0it3r Nov 10, 2026
efbe90a
Verify and document CSS Scroll Anchoring compliance
eXpl0it3r Nov 10, 2026
8f05288
Verify and document Resize Observer compliance
eXpl0it3r Nov 10, 2026
509682c
Verify and document CSSOM compliance
eXpl0it3r Nov 10, 2026
4b64cc6
Verify and document CSSOM View compliance
eXpl0it3r Nov 10, 2026
7cde30a
Verify and document Geometry Interfaces compliance
eXpl0it3r Nov 10, 2026
2fe00bf
Verify and document HTML introduction compliance
eXpl0it3r Nov 10, 2026
bf85365
Verify and document HTML common infrastructure compliance
eXpl0it3r Nov 10, 2026
38229a7
Verify and document HTML documents compliance
eXpl0it3r Nov 10, 2026
15c4ddf
Verify and document HTML IANA considerations compliance
eXpl0it3r Nov 10, 2026
27a8d4e
Verify and document HTML microdata compliance
eXpl0it3r Nov 10, 2026
e4254b8
Verify and document HTML user interaction compliance
eXpl0it3r Nov 10, 2026
dbd7cdf
Verify and document HTML XML syntax compliance
eXpl0it3r Nov 10, 2026
212a006
Verify and document HTML loading web pages compliance
eXpl0it3r Nov 10, 2026
74f7312
Verify and document HTML web application APIs compliance
eXpl0it3r Nov 10, 2026
45ef932
Verify and document HTML communication compliance
eXpl0it3r Nov 10, 2026
ff7028f
Verify and document HTML web workers compliance
eXpl0it3r Nov 10, 2026
b94b526
Verify and document HTML worklets compliance
eXpl0it3r Nov 10, 2026
a0711b8
Verify and document HTML web storage compliance
eXpl0it3r Nov 10, 2026
29af3d9
Verify and document CSS Values Level 5 compliance
eXpl0it3r Nov 10, 2026
0bb3d4b
Verify and document CSS Conditional Rules Level 4 compliance
eXpl0it3r Nov 10, 2026
809318f
Verify and document CSS Conditional Rules Level 5 compliance
eXpl0it3r Nov 10, 2026
28068b5
Verify and document Media Queries Level 5 compliance
eXpl0it3r Nov 10, 2026
60c7047
Verify and document CSS Environment Variables compliance
eXpl0it3r Nov 10, 2026
cae2f16
Verify and document CSS Counter Styles compliance
eXpl0it3r Nov 10, 2026
52ca4d4
Record gaps the CSS module and HTML reviews found in other specs
eXpl0it3r Nov 10, 2026
dc488cc
Keep only the spec compliance overview in the job summary
eXpl0it3r Nov 11, 2026
854d220
Link the remaining ignored tests to their gaps and drop port lineage
eXpl0it3r Nov 12, 2026
1128ff0
Run the remaining rendering tests sequentially
eXpl0it3r Nov 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
12 changes: 12 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,18 @@ jobs:
env:
PDF_OUTPUT_DIRECTORY: ${{ github.workspace }}/pdf-artifacts/${{ matrix.platform.name }}-${{ matrix.dotnet.name }}

- name: Spec compliance report
if: ${{ !cancelled() && runner.os == 'Windows' && matrix.dotnet.name == '.NET 8' }}
run: dotnet run --project Source/Tools/HtmlRenderer.SpecCompliance --configuration Release --no-build -- report --output spec-compliance.md --summary $env:GITHUB_STEP_SUMMARY

- name: Upload spec compliance report
if: ${{ !cancelled() && runner.os == 'Windows' && matrix.dotnet.name == '.NET 8' }}
uses: actions/upload-artifact@v7
with:
name: Spec compliance report
path: spec-compliance.md
if-no-files-found: ignore

- name: Create HtmlRenderer.Core NuGet package
run: dotnet pack Source/HtmlRenderer/HtmlRenderer.csproj --configuration Release --include-symbols -p:SymbolPackageFormat=snupkg --no-build --verbosity normal --output ${{ env.NuGetDirectory }}

Expand Down
3 changes: 2 additions & 1 deletion Source/HtmlRenderer.CrossPlatform.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"HtmlRenderer.WPF\\HtmlRenderer.WPF.csproj",
"Test\\HtmlRenderer.IntegrationTest\\HtmlRenderer.IntegrationTest.csproj",
"Test\\HtmlRenderer.PdfSharp.Test\\HtmlRenderer.PdfSharp.Test.csproj",
"Test\\HtmlRenderer.Test\\HtmlRenderer.Test.csproj"
"Test\\HtmlRenderer.Test\\HtmlRenderer.Test.csproj",
"Tools\\HtmlRenderer.SpecCompliance\\HtmlRenderer.SpecCompliance.csproj"
]
}
}
17 changes: 5 additions & 12 deletions Source/HtmlRenderer.PdfSharp/FontResolution/FontFamilyModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,11 @@
namespace TheArtOfDev.HtmlRenderer.PdfSharp.FontResolution
{
/// <summary>
/// One registered face of a <see cref="FontFamilyModel"/>: its CSS Fonts Level 4 matching axes
/// (numeric weight, italic, stretch), the codepoint <see cref="CodepointRange"/>s it is restricted to
/// (from an <c>@font-face</c> <c>unicode-range</c> descriptor) or null when it has none - a face with
/// no explicit ranges is treated as covering everything (matching the same, deliberately simpler,
/// scope as the shared <c>Core.Handlers.FontsHandler</c> face registry - see its own doc comments for
/// why cmap-based coverage extraction isn't ported here: this codebase resolves one font per box, not
/// per glyph, so there is no per-glyph precision to exploit) - and the sniffed description
/// <see cref="FontResolver.ResolveTypeface(string,int,bool,int,int?)"/> hands back once this face is
/// chosen; <see cref="TtfFontDescription.FontNameInvariantCulture"/> doubles as this face's storage
/// key into <see cref="FontResolver.GetFont"/>.
/// One registered face of a <see cref="FontFamilyModel"/>: its CSS Fonts 4 matching axes, its
/// <c>unicode-range</c> or null when it has none (then it covers everything), and its description.
/// <see cref="TtfFontDescription.FontNameInvariantCulture"/> is the face's key into
/// <see cref="FontResolver.GetFont"/>.
/// </summary>
/// <remarks>Ported from PeachPDF's <c>Fonts\FontFamilyModel.cs</c>.</remarks>
internal sealed class FontFaceEntry
{
public FontFaceEntry(int weight, bool italic, int stretch, IReadOnlyList<CodepointRange> explicitRanges, TtfFontDescription description)
Expand All @@ -35,7 +28,7 @@ public FontFaceEntry(int weight, bool italic, int stretch, IReadOnlyList<Codepoi
public TtfFontDescription Description { get; }
}

/// <summary>One CSS font-family: its registered faces. Ported from PeachPDF's <c>Fonts\FontFamilyModel.cs</c>.</summary>
/// <summary>One CSS font-family: its registered faces.</summary>
internal sealed class FontFamilyModel
{
public string Name { get; set; }
Expand Down
30 changes: 6 additions & 24 deletions Source/HtmlRenderer.PdfSharp/FontResolution/FontResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,14 @@
namespace TheArtOfDev.HtmlRenderer.PdfSharp.FontResolution
{
/// <summary>
/// PDFsharp <see cref="IFontResolver"/> implementation backing the PdfSharp backend's font handling -
/// OS font discovery plus CSS Fonts Level 4 §5 nearest-match (slant→stretch→weight) face selection,
/// ported from PeachPDF's <c>Fonts\FontResolver.cs</c>. Unlike WinForms/WPF (which register an
/// <c>@font-face</c> face as an opaque platform font-family handle and let the OS/UI framework do
/// matching), PDFsharp's <see cref="IFontResolver"/> contract needs raw font bytes at PDF-generation
/// time for embedding, so this type does its own matching entirely and is called directly by
/// <c>Adapters.PdfSharpAdapter</c>'s <c>AddFontFace</c>/<c>GetFont</c> overrides, bypassing the shared
/// <c>Core.Handlers.FontsHandler</c> registry other backends use.
/// PDFsharp <see cref="IFontResolver"/> of the PdfSharp backend: system font discovery and
/// CSS Fonts 4 §5 face matching (slant, stretch, weight). PDFsharp needs the font bytes for embedding,
/// so this does its own matching instead of going through <c>Core.Handlers.FontsHandler</c>.
/// </summary>
/// <remarks>
/// Two deliberate scope departures from PeachPDF's own resolver, both already established for this
/// port's shared <c>Core.Handlers.FontsHandler</c> (see its own doc comments):
/// <list type="bullet">
/// <item>No cmap-coverage extraction fallback for <c>unicode-range</c>-less faces - a face with no
/// explicit <c>unicode-range</c> is simply treated as covering everything. This codebase resolves one
/// font per box (not per glyph/run), so there's no per-glyph precision for cmap coverage to buy.</item>
/// <item>No per-<c>FontResolver</c>-instance glyph-typeface/descriptor caching. PeachPDF supports many
/// concurrent <c>PdfGenerator</c>s each with their own <c>FontResolver</c>, so it isolates their custom
/// font caches from each other. This backend's <c>PdfSharpAdapter</c> (and this resolver with it) is a
/// single process-wide singleton via <see cref="Register"/>, matching this project's pre-existing
/// architecture - there's only ever one instance, so nothing to isolate.</item>
/// </list>
/// System font discovery itself is also adapted, not verbatim: no Android/iOS branches (this project's
/// <c>netstandard2.0;net8.0</c> target framework list has no mobile leg to run them on), and Linux
/// discovery shells out to the <c>fc-list</c> CLI rather than P/Invoking <c>libfontconfig.so.1</c>
/// directly (see <see cref="LinuxSystemFontResolver"/>'s own doc comment for why).
/// A face without <c>unicode-range</c> is assumed to cover everything, its glyphs aren't checked,
/// there's one font per box anyway. The resolver is a process-wide singleton (<see cref="Register"/>).
/// On Linux, fonts are found with the <c>fc-list</c> CLI, see <see cref="LinuxSystemFontResolver"/>.
/// </remarks>
public sealed class FontResolver : IFontResolver
{
Expand Down
12 changes: 2 additions & 10 deletions Source/HtmlRenderer.PdfSharp/PdfGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,12 +197,7 @@ public static async Task AddPdfPages(PdfDocument document, string html, PdfGener
container.PerformLayout(measure);
}

// One PDF page per fragmentainer the fragment tree actually materialized - a
// content-empty page slot (CSS Paged Media 3 3.2, e.g. a huge margin that would
// otherwise paginate through blank vertical space - see the margin-truncation
// correction in BlockFragmentation) is simply never in this list, which is what
// gives blank-page skipping for free here instead of the old ceil(height/pageHeight)
// loop's naive page count.
// One PDF page per fragmentainer, pages without content have none.
var tree = container.FragmentTree;
foreach (var fragmentainer in tree?.Fragmentainers ?? (IReadOnlyList<FragmentainerFragment>)Array.Empty<FragmentainerFragment>())
{
Expand Down Expand Up @@ -237,10 +232,7 @@ private static void HandleLinks(PdfDocument document, HtmlContainer container, X
if (tree == null || tree.Fragmentainers.Count == 0)
return;

// Pagination slot -> PDF page index. Not a bare multiply/divide by page height any more:
// a content-empty slot is never materialized as a fragmentainer at all (blank-page
// skipping), so slot indices are not contiguous across tree.Fragmentainers the way a
// fixed-size page grid's would be.
// Pagination slot -> PDF page index, a content-empty slot gets no page (css-page-3 §3.2).
var slotToPage = new Dictionary<int, int>();
for (var pageIndex = 0; pageIndex < tree.Fragmentainers.Count; pageIndex++)
{
Expand Down
41 changes: 7 additions & 34 deletions Source/HtmlRenderer.WinForms/Adapters/WinFormsAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,10 @@ internal sealed class WinFormsAdapter : RAdapter
/// </summary>
private static readonly WinFormsAdapter _instance = new WinFormsAdapter();

// Adapter-level PrivateFontCollection for @font-face-loaded faces - one collection shared for the
// adapter's (process) lifetime, growing by one family per LoadFontFaceFontInt call.
// The @font-face faces, kept for the lifetime of the process.
private readonly PrivateFontCollection _fontFaceCollection = new PrivateFontCollection();

// Backs LoadFontFaceFontInt's temp-file registration (see its own doc comment for why a temp file
// is used instead of PrivateFontCollection.AddMemoryFont) - one directory per process, cleaned up
// by the OS's normal temp-file housekeeping, not by this process.
// The font files of the @font-face faces, see LoadFontFaceFontInt. Left to the OS to clean up.
private static readonly string _fontFaceTempDirectory = CreateFontFaceTempDirectory();

private static string CreateFontFaceTempDirectory()
Expand Down Expand Up @@ -211,37 +208,13 @@ protected override RFont CreateFontInt(RFontFamily family, double size, RFontSty
}

/// <summary>
/// Loads one <c>@font-face</c> face's bytes into the adapter's <see cref="PrivateFontCollection"/>
/// via <see cref="PrivateFontCollection.AddFontFile"/> - through a temp file, matching this
/// adapter's own pre-existing <c>DemoForm.LoadCustomFonts</c> pattern (that path is untouched,
/// this is a new, separate mechanism specific to <c>@font-face</c>).
/// Adds the font to the <see cref="PrivateFontCollection"/> and returns its family, found by the
/// family name in the font file. <see cref="PrivateFontCollection.Families"/> groups the faces of
/// a family and isn't in insertion order.
/// </summary>
/// <remarks>
/// Deliberately NOT <see cref="PrivateFontCollection.AddMemoryFont"/>, despite it needing no temp
/// file: empirically (a throwaway repro project registering a dozen distinct families into one
/// <see cref="PrivateFontCollection"/>), <c>AddMemoryFont</c> is unreliable on this target
/// framework - <see cref="PrivateFontCollection.Families"/> permanently fails to reflect several of
/// the added families (not a timing race: polling for up to 300ms after the call never finds them
/// either), while the identical sequence of files through <c>AddFontFile</c> succeeds 100% of the
/// time across repeated runs. This is a known-flaky area of GDI+'s <c>AddMemoryFont</c> P/Invoke
/// path, not a bug in this port. The temp file is deliberately never deleted: GDI+ keeps it
/// memory-mapped for as long as this process-lifetime singleton's <see cref="PrivateFontCollection"/>
/// references it, and the OS's own temp-directory housekeeping reclaims it afterward - the same
/// "small, bounded, process-lifetime" rationale the removed <c>AddMemoryFont</c>/<c>AllocHGlobal</c>
/// approach relied on.
/// <para>
/// The returned <see cref="FontFamilyAdapter"/> is found by sniffing the font's own internal
/// family name via <see cref="TtfFontDescription"/> and matching it against
/// <see cref="PrivateFontCollection.Families"/> - not by comparing <c>Families.Length</c> before
/// and after, nor by taking the array's last entry. Two bugs made that approach unreliable: (1)
/// <c>Families</c> groups every face by family name (the whole point of
/// <see cref="PrivateFontCollection"/> - it lets <see cref="Font"/> pick the right face via
/// <see cref="FontStyle"/> automatically), so registering a second face of an *already-registered*
/// family (e.g. this face set's own Bold after its Regular) never changes the count at all; and
/// (2) even when the count does change, <c>Families</c> is returned in a GDI-defined (effectively
/// alphabetical) order, not insertion order, so "the last entry" is often a completely unrelated,
/// alphabetically-later family, not the one just added.
/// </para>
/// Goes through a temp file, <see cref="PrivateFontCollection.AddMemoryFont"/> doesn't reliably
/// list the added families. The file can't be deleted while the collection uses it.
/// </remarks>
protected override RFontFamily LoadFontFaceFontInt(byte[] fontBytes, string filePath)
{
Expand Down
17 changes: 17 additions & 0 deletions Source/HtmlRenderer.sln
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.IntegrationTes
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.Test", "Test\HtmlRenderer.Test\HtmlRenderer.Test.csproj", "{7427E12F-1524-4405-AAD1-5AF8ACB3BBBC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{07C2787E-EAC7-C090-1BA3-A61EC2A24D84}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HtmlRenderer.SpecCompliance", "Tools\HtmlRenderer.SpecCompliance\HtmlRenderer.SpecCompliance.csproj", "{8E0BFCAC-F676-4EE1-B639-7D8F2F24656D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -167,6 +171,18 @@ Global
{7427E12F-1524-4405-AAD1-5AF8ACB3BBBC}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{7427E12F-1524-4405-AAD1-5AF8ACB3BBBC}.Release|x86.ActiveCfg = Release|Any CPU
{7427E12F-1524-4405-AAD1-5AF8ACB3BBBC}.Release|x86.Build.0 = Release|Any CPU
{8E0BFCAC-F676-4EE1-B639-7D8F2F24656D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8E0BFCAC-F676-4EE1-B639-7D8F2F24656D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8E0BFCAC-F676-4EE1-B639-7D8F2F24656D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{8E0BFCAC-F676-4EE1-B639-7D8F2F24656D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{8E0BFCAC-F676-4EE1-B639-7D8F2F24656D}.Debug|x86.ActiveCfg = Debug|Any CPU
{8E0BFCAC-F676-4EE1-B639-7D8F2F24656D}.Debug|x86.Build.0 = Debug|Any CPU
{8E0BFCAC-F676-4EE1-B639-7D8F2F24656D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8E0BFCAC-F676-4EE1-B639-7D8F2F24656D}.Release|Any CPU.Build.0 = Release|Any CPU
{8E0BFCAC-F676-4EE1-B639-7D8F2F24656D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{8E0BFCAC-F676-4EE1-B639-7D8F2F24656D}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{8E0BFCAC-F676-4EE1-B639-7D8F2F24656D}.Release|x86.ActiveCfg = Release|Any CPU
{8E0BFCAC-F676-4EE1-B639-7D8F2F24656D}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -179,6 +195,7 @@ Global
{AF39851E-37B9-409E-A34D-CE091A098C86} = {E508FA78-D57E-492F-9BF7-23640001848A}
{A12F4432-337B-4030-B834-5D312A9FB16C} = {E508FA78-D57E-492F-9BF7-23640001848A}
{7427E12F-1524-4405-AAD1-5AF8ACB3BBBC} = {E508FA78-D57E-492F-9BF7-23640001848A}
{8E0BFCAC-F676-4EE1-B639-7D8F2F24656D} = {07C2787E-EAC7-C090-1BA3-A61EC2A24D84}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {902788D6-3165-491D-B860-DF2F74E66C1D}
Expand Down
Loading
Loading