PDF-export breekt af op een gedachtestreepje in een grafiek- of diagramlabel #1942

Closed
opened 2026-09-03 09:20:32 +00:00 by brenno · 2 comments
Owner

Wat er misgaat

Een ingesloten SVG in de PDF-export (grafiek, mermaid-diagram, formule) laat de
hele export stuklopen zodra er ergens in de tekening één teken boven U+00FF
staat. Niet dat ene diagram: het hele document.

De lezer van package:pdf kiest voor <text> in een SVG hardgecodeerd een
standaardfont (src/svg/painter.dart:117 — Helvetica, Times of Courier naar
font-family), zonder terugvallijst. Die standaardfonts zijn Latin-1, en
PdfFont.stringMetrics werpt op alles daarboven.

Nagemeten met een kale pw.SvgImage:

in de SVG-tekst uitkomst
Start OK
(em-streepje) ArgumentError
(krul-apostrof) ArgumentError
ArgumentError
Cyrillisch / Grieks / ł ArgumentError

Dit is dus geen randgeval voor exotische schriften: een gedachtestreepje of een
typografische apostrof in een grafiektitel is genoeg. Dat zijn precies de tekens
die een tekstverwerker en onze eigen documentmodus vanzelf produceren.

Waarom de bestaande try het niet vangt

DocumentPdfWidgets._graphic (lib/services/pdf/document_pdf_widgets.dart)
zet pw.SvgImage in een try, maar die dekt alleen de constructor. De worp
komt uit SvgImage.paint, dus tijdens document.save()
(lib/services/pdf/document_pdf_renderer.dart:257) — ruim buiten die try.

Stack:

PdfFont.stringMetrics (package:pdf/src/pdf/obj/font.dart:311)
new SvgText.fromXml   (package:pdf/src/svg/text.dart:81)
SvgGroup.paintShape   (package:pdf/src/svg/group.dart:65)
SvgPainter.paint      (package:pdf/src/svg/painter.dart:51)
SvgImage.paint        (package:pdf/src/widgets/svg.dart:133)

Reproductie

  1. Maak een document met een grafiekblok waarvan de titel een em-streepje bevat
    (Omzet — per kwartaal), of een mermaid-diagram met een label met .
  2. Exporteer naar PDF.

Verwacht: een PDF. Werkelijk: de export faalt.

Reikwijdte

Raakt alle drie de SVG-routes van de documentexport, want ze lopen alle drie
door _resolveGraphics naar pw.SvgImage:

  • grafieken (_chartSvg zet de titel, reeksnamen en aslabels van de gebruiker
    in <text>)
  • mermaid-diagrammen
  • formules (MathJax zet zijn glyphs als <path>, dus die is minder gevoelig,
    maar \text{…} niet)

Voorgestelde oplossing

pw.SvgImage neemt een customFontLookup. Daarmee kunnen we per tekening het
gebundelde Unicode-font (Roboto, dat de export al als fontFallback meekrijgt)
opgeven zodra de tekst in de SVG niet volledig Latin-1 is, en anders bij de
standaardfonts blijven — die dragen een echte vette en cursieve snede, en dat is
precies waarom document_pdf_fonts.dart ze kiest.

Regressietest

Een test die een document met een grafiektitel mét em-streepje door de
PDF-export haalt en bytes terugverwacht. Die staat nu rood.

Meegenomen: een tweede, cosmetische melding

In dezelfde run:

flutter: unhandled element <defs/>; Picture key: Svg loader

Die komt uit vector_graphics_compiler en is onze eigen opschoning:
sanitizeMermaidSvg haalt <marker> en <style> uit <defs> weg, de
XML-serializer schrijft het lege element als <defs/>, en de parser handelt
defs alleen af als het níét zelfsluitend is (parser.dart:921). Er gaat niets
verloren — de defs was al leeg — maar het is ruis in elke debug-run, en de
opruiming is één regel in sanitize_svg.dart.

## Wat er misgaat Een ingesloten SVG in de PDF-export (grafiek, mermaid-diagram, formule) laat de **hele export** stuklopen zodra er ergens in de tekening één teken boven U+00FF staat. Niet dat ene diagram: het hele document. De lezer van `package:pdf` kiest voor `<text>` in een SVG hardgecodeerd een standaardfont (`src/svg/painter.dart:117` — Helvetica, Times of Courier naar `font-family`), zonder terugvallijst. Die standaardfonts zijn Latin-1, en `PdfFont.stringMetrics` werpt op alles daarboven. Nagemeten met een kale `pw.SvgImage`: | in de SVG-tekst | uitkomst | |---|---| | `Start` | OK | | `—` (em-streepje) | `ArgumentError` | | `’` (krul-apostrof) | `ArgumentError` | | `…` `→` | `ArgumentError` | | Cyrillisch / Grieks / `ł` | `ArgumentError` | Dit is dus geen randgeval voor exotische schriften: een gedachtestreepje of een typografische apostrof in een grafiektitel is genoeg. Dat zijn precies de tekens die een tekstverwerker en onze eigen documentmodus vanzelf produceren. ## Waarom de bestaande `try` het niet vangt `DocumentPdfWidgets._graphic` (`lib/services/pdf/document_pdf_widgets.dart`) zet `pw.SvgImage` in een `try`, maar die dekt alleen de constructor. De worp komt uit `SvgImage.paint`, dus tijdens `document.save()` (`lib/services/pdf/document_pdf_renderer.dart:257`) — ruim buiten die `try`. Stack: ``` PdfFont.stringMetrics (package:pdf/src/pdf/obj/font.dart:311) new SvgText.fromXml (package:pdf/src/svg/text.dart:81) SvgGroup.paintShape (package:pdf/src/svg/group.dart:65) SvgPainter.paint (package:pdf/src/svg/painter.dart:51) SvgImage.paint (package:pdf/src/widgets/svg.dart:133) ``` ## Reproductie 1. Maak een document met een grafiekblok waarvan de titel een em-streepje bevat (`Omzet — per kwartaal`), of een mermaid-diagram met een label met `’`. 2. Exporteer naar PDF. Verwacht: een PDF. Werkelijk: de export faalt. ## Reikwijdte Raakt alle drie de SVG-routes van de documentexport, want ze lopen alle drie door `_resolveGraphics` naar `pw.SvgImage`: - grafieken (`_chartSvg` zet de titel, reeksnamen en aslabels van de gebruiker in `<text>`) - mermaid-diagrammen - formules (MathJax zet zijn glyphs als `<path>`, dus die is minder gevoelig, maar `\text{…}` niet) ## Voorgestelde oplossing `pw.SvgImage` neemt een `customFontLookup`. Daarmee kunnen we per tekening het gebundelde Unicode-font (Roboto, dat de export al als `fontFallback` meekrijgt) opgeven zodra de tekst in de SVG niet volledig Latin-1 is, en anders bij de standaardfonts blijven — die dragen een echte vette en cursieve snede, en dat is precies waarom `document_pdf_fonts.dart` ze kiest. ## Regressietest Een test die een document met een grafiektitel mét em-streepje door de PDF-export haalt en bytes terugverwacht. Die staat nu rood. ## Meegenomen: een tweede, cosmetische melding In dezelfde run: ``` flutter: unhandled element <defs/>; Picture key: Svg loader ``` Die komt uit `vector_graphics_compiler` en is onze eigen opschoning: `sanitizeMermaidSvg` haalt `<marker>` en `<style>` uit `<defs>` weg, de XML-serializer schrijft het lege element als `<defs/>`, en de parser handelt `defs` alleen af als het níét zelfsluitend is (`parser.dart:921`). Er gaat niets verloren — de `defs` was al leeg — maar het is ruis in elke debug-run, en de opruiming is één regel in `sanitize_svg.dart`.
Author
Owner

Opgepakt. Tak: fix/pdf-svg-unicode-1942. Verwachte reikwijdte: lib/services/pdf/document_pdf_fonts.dart, document_pdf_widgets.dart, document_pdf_export.dart, lib/utils/sanitize_svg.dart, plus tests.

Opgepakt. Tak: fix/pdf-svg-unicode-1942. Verwachte reikwijdte: lib/services/pdf/document_pdf_fonts.dart, document_pdf_widgets.dart, document_pdf_export.dart, lib/utils/sanitize_svg.dart, plus tests.
Author
Owner

Gerepareerd en op main (merge 8c7280d82, PR #1966).

Wat erin zit:

  • DocumentPdfFonts.svgTypesetting zegt per tekening of de standaardsneden volstaan, welk Unicode-font het anders moet doen, of dat de tekening niet te zetten is. Het gebundelde Roboto gaat via customFontLookup mee.
  • Zonder zulke tekens blijft een tekening op de standaardsneden — die dragen een echte vette en cursieve snede, en dat was de reden om ze te kiezen.
  • Zonder Unicode-font valt de tekening terug op haar bron in plaats van de export af te breken.
  • De melding over onzetbare tekens hield op bij de rand van elke tekening. Dat is meegenomen: svgTextContent leest nu ook de <text>- en <tspan>-knopen. Zonder dat zou deze reparatie een luide fout in stil verlies veranderen — een pijl () staat namelijk níét in het gebundelde Roboto en werd een leeg blokje.
  • De lege <defs/> uit de opschoning is weg.

Acht regressietests, waarvan vier rood stonden tegen de onherstelde code. Handmatig gecontroleerd op een echte export: kop, alinea, grafiektitel met gedachtestreepje en krul-apostrofs in de aslabels staan er allemaal.

Wat er níét in zit: de pijl zelf blijft een leeg blokje, want het gebundelde Roboto-Variable heeft U+2192 niet in zijn cmap. De export meldt hem nu, maar zetten kan hij hem niet — een ander of extra terugvalfont is een aparte afweging (bestandsgrootte tegenover dekking).

Gerepareerd en op `main` (merge 8c7280d82, PR #1966). Wat erin zit: - `DocumentPdfFonts.svgTypesetting` zegt per tekening of de standaardsneden volstaan, welk Unicode-font het anders moet doen, of dat de tekening niet te zetten is. Het gebundelde Roboto gaat via `customFontLookup` mee. - Zonder zulke tekens blijft een tekening op de standaardsneden — die dragen een echte vette en cursieve snede, en dat was de reden om ze te kiezen. - Zonder Unicode-font valt de tekening terug op haar bron in plaats van de export af te breken. - De melding over onzetbare tekens hield op bij de rand van elke tekening. Dat is meegenomen: `svgTextContent` leest nu ook de `<text>`- en `<tspan>`-knopen. Zonder dat zou deze reparatie een luide fout in stil verlies veranderen — een pijl (`→`) staat namelijk níét in het gebundelde Roboto en werd een leeg blokje. - De lege `<defs/>` uit de opschoning is weg. Acht regressietests, waarvan vier rood stonden tegen de onherstelde code. Handmatig gecontroleerd op een echte export: kop, alinea, grafiektitel met gedachtestreepje en krul-apostrofs in de aslabels staan er allemaal. Wat er níét in zit: de pijl zelf blijft een leeg blokje, want het gebundelde Roboto-Variable heeft U+2192 niet in zijn `cmap`. De export meldt hem nu, maar zetten kan hij hem niet — een ander of extra terugvalfont is een aparte afweging (bestandsgrootte tegenover dekking).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
LibreKAT/Ocideck#1942
No description provided.