Een slechtziende lezer kan zwarte tekst op een witte pagina bij standaardcontrast niet ontcijferen, dus vraagt hij om een dark mode. Het naïeve antwoord is elke pixel van de gerenderde pagina inverteren. Het wordt in een week verscheept en breekt de volgende dag: gescande foto's komen terug als filmnegatieven, de gele markeerstiften van de lezer worden een onleesbaar blauw smeuïg, en iemand vraagt waarom de afdruk effen zwart uitkwam. De feature is oprecht het bouwen waard en oprecht makkelijk half goed te krijgen, en de kloof tussen de twee uitkomsten is één idee: elke kleurbeslissing hoort op een specifiek punt in de render-pipeline, en inversie is het verkeerde gereedschap toegepast op het verkeerde stadium. De code hier gebruikt PDFium Component, de op PDFium gebaseerde viewer voor Delphi, C++Builder en Lazarus, wiens render-API die stadia apart blootstelt
Filters zijn presentatiestatus, nooit documentstatus
Eén regel voorkomt de ergste categorie bugs hier: een leesmodus verandert hoe de bitmap geproduceerd of nabewerkt wordt, en verder niets. De PDF-bytes blijven onaangetast, elke modus is omkeerbaar door opnieuw te renderen, en "opslaan" schrijft nooit een gefilterde appearance terug in het bestand. Dit klinkt vanzelfsprekend tot een juridisch reviewer een contract afdrukt onder een actief filter en de geïnverteerde versie indient. Op dat moment verdient de vraag "gebruikt afdrukken de eigen appearance van het document of die van het scherm" een expliciet antwoord in uw specificatie, niet een toeval van het codepad. Houd de filterinstelling in viewer-status, pas die toe op het moment van renderen, en laat elk exportpad verklaren welke appearance het gebruikt
De regel verdient zichzelf tweemaal terug. Omkeerbaarheid is gratis, want wisselen van modus rendert opnieuw uit de ongewijzigde bron: er is geen undo-stack te onderhouden en geen manier waarop een reeks moduswijzigingen de pagina degradeert. Multi-vensterscenario's blijven om dezelfde reden coherent. Twee weergaven van één document kunnen verschillende modi draaien, omdat elke weergave zijn eigen presentatiestatus bezit terwijl het documentobject gedeeld blijft
Render eerst, transformeer daarna
Het ondersteunde patroon is bitmap-nabewerking na renderen: RenderPage produceert de pagina-raster, dan past een transform-pass die aan. De component levert drie transforms als in-place bitmapoperaties, InvertPdfBitmap, DuotonePdfBitmap en GrayscalePdfBitmap, wat de moduswissel een schone tweekoppelfunctie maakt:
function TViewerForm.RenderWithMode(W, H: Integer): TBitmap;
begin
Result := Pdf.RenderPage(0, 0, W, H, ro0, [reAnnotations]);
case FReadingMode of
rmInverted: InvertPdfBitmap(Result);
rmHighContrast: DuotonePdfBitmap(Result, clBlack, $0000C8FF); // donkere achtergrond, amberkleurige tekst
rmGrayscale: GrayscalePdfBitmap(Result);
end;
// rmNormal valt door: het document behoudt zijn eigen kleuren
end;
Uit dit ontwerp volgen twee dingen. Ten eerste is de transform-kosten evenredig met de bitmapgrootte, dus het werk hoort waar uw render-resultaten gecached worden: filter de gecachte bitmap eenmaal, niet bij elke paint. Ten tweede, omdat de transform op de afgewerkte raster draait, raakt hij tekst, vector-kunst, afbeeldingen en appearance van annotaties op dezelfde manier. Die uniformiteit is precies wat gewone inversie fout doet voor foto's. Het is de reden dat de duotone-transform een betere standaard is voor tekstintensieve documenten, omdat die luminantie afbeeldt op een gekozen donker-naar-licht-kleurramp in plaats van tinten te negeren; inversie blijft beschikbaar als expliciete keuze voor lezers die dat willen. Scherpere glyph-randen zijn een aparte hendel. De render-optie reNoSmoothText schakelt tekst-anti-aliasing uit op het moment van renderen en combineert goed met hoogcontrastmodus bij grote zoom
Twee grijstinten die het oneens zijn
De render-opties bevatten reGrayscale, wat eruitziet als een snelkoppeling voorbij de nabewerkingsstap. Het is niet dezelfde bewerking:
// Op engineniveau: grijswaarden toegepast tijdens rasterisatie
GrayA := Pdf.RenderPage(0, 0, W, H, ro0, [reGrayscale]);
// Nabewerking: render in kleur, converteer de afgewerkte bitmap
GrayB := Pdf.RenderPage(0, 0, W, H);
GrayscalePdfBitmap(GrayB);
De optie op engineniveau geldt de raster-uitvoer van afbeeldingsinhoud maar bereikt geen vector-vullingen of tekstkleuren, dus een pagina met gekleurde kopteksten kan terugkomen met grijze foto's en hardnekkig blauwe kopteksten. GrayscalePdfBitmap op de afgewerkte bitmap converteert alles, onvoorwaardelijk. De render-optie verdient nog steeds zijn plaats wanneer u afbeeldingen gede-satureerd wilt terwijl tekstkleur als signaal behouden blijft, wat sommige slechtziende lezers expliciet prefereren. Maar als de eis luidt "grijswaardenpagina", is nabewerking de versie die daaraan voldoet. Welk pad u ook kiest, houd beide RenderPage-overload-stijlen in gedachten. De functievorm retourneert een bitmap die de aanroeper bezit en moet vrijgeven, en dat doet ertoe zodra filters het aantal gerenderde bitmaps in vlucht vermenigvuldigen
Achtergronden, selectiemarkeringen en de PageColor-val
Niet elke comfortaanpassing is een transform. De witte pagina-achtergrond vervangen door een warme tint is voor lichtgevoelige lezers vaak op zichzelf genoeg, en daar is een eigenschap voor. De eigenschap draagt een scope-regel die mensen vangt:
// Beïnvloedt alleen de weergave op het scherm
PdfView.PageColor := $00D9EDF2; // warme papiertint achter de pagina-inhoud
// RenderPage-uitvoer negeert PageColor; geef de kleur expliciet door
Bmp := Pdf.RenderPage(0, 0, W, H, ro0, [], $00D9EDF2);
PageColor verandert wat TPdfView toont, maar bitmaps via RenderPage geproduceerd behouden het standaard wit tenzij de parameter Color anders zegt. Het symptoom is betrouwbaar: het scherm toont de getinte pagina, de gebruiker exporteert of drukt af, en de uitvoer keert terug naar wit. Schrijf dat weg onder dezelfde export-beleidsbeslissing uit de eerste sectie
De overige kleureigenschappen definiëren overlay-markeringen: HighlightColor voor zoektreffers, SelectionColor voor tekstselectie door de gebruiker, ReadingWordColor voor de uitgesproken-woord-cursor. Elk ervan moet opnieuw gecontroleerd worden onder elk filter dat u aanbiedt. Een amber leescursor die op wit werkt verdwijnt na inversie; een bleekblauwe selectie lost op in een hoogcontrast-achtergrond. Onderhoud per-modus overlay-paletten in plaats van één globale set, en test de combinaties met opzet. Filters plus text-to-speech is een normale configuratie voor de lezers die deze feature dient, geen randgeval. De overlay-machinerie zelf wordt behandeld in het artikel over de toegankelijke lezer
Getallen, verificatie en de afdrukvraag
WCAG 2.1 maakt van deze feature iets wat u kunt meten. Succescriterium 1.4.3 vraagt een contrastverhouding van 4,5:1 voor looptekst, en 1.4.6 verhoogt die naar 7:1 voor verhoogd contrast. Spot-check uw hoogcontrastmodus tegen die verhoudingen met een contrastanalyzer op daadwerkelijke gerenderde uitvoer. Tekst over afbeeldingen en tekst in formuliervelden zijn waar de verhoudingen stilletjes falen zelfs wanneer de looptekst slaagt
Afdrukken verdient een eigen beslissing, en de verdedigbare standaard is de eigen appearance van het document, met "afdrukken zoals weergegeven" aangeboden als expliciete gebruikerskeuze. Een afgedrukte pagina is bewijs in meer workflows dan viewerauteurs geneigd zijn te verwachten, en een geïnverteerde afdruk van een contract is een supportincident met juridische bijsmaak. Nog één koppeling doet ertoe voor prestaties: gefilterd renderen verdubbelt het bitmapwerk bij elke moduswissel, dus pas geen transform toe op elk paint-bericht. Cache de gefilterde bitmap en draai de transform opnieuw alleen wanneer de pagina, zoom of modus daadwerkelijk verandert. De caching-strategie die dit goedkoop maakt staat in het artikel over render-cache en zoom-prestaties
Eén ding om in uw UI te beslissen in plaats van in uw code: welke modus de juiste standaard is. Er is niet één antwoord, dus bied de set aan en laat de lezer kiezen. Hoogcontrast past bij de meeste tekstintensieve leeswerk, inversie past bij lezers die expliciet licht-op-donker willen, grijstinten snijdt kleurruis weg, en een achtergrondtint handelt lichtgevoeligheid af. Persisteer de keuze per gebruiker, herstel die bij opstarten, en houd een pad van één toetsaanslag terug naar normaal, want een lezer die in een onleesbare modus landt heeft een snelle uitweg nodig
De render-opties, bitmap-transformen en view-kleureigenschappen die hier gebruikt worden, worden meegeleverd met PDFium Component voor Delphi, C++Builder en Lazarus/FPC, met volledige broncode zodat de transform-implementaties ge-audit of uitgebreid kunnen worden