Tekninen artikkeli

Heikkonäköisen PDF-värifilterit Delphissä PDFiumilla

Heikkonäköinen lukija ei erota mustaa tekstiä valkoisella sivulla oletuskontrastilla, joten hän pyytää tummaa tilaa. Naiivi vastaus on kääntää jokainen renderöidyn sivun pikseli. Se toimitetaan viikossa ja rikkoo seuraavana päivänä: skannatut valokuvat palaavat näyttäen filminegatiiveilta, lukijan keltaiset korostusmerkit muuttuvat lukukelvottomaksi siniseksi sivelyksi, ja joku kysyy miksi tuloste tuli täysin mustana. Ominaisuus on aidosti rakentamisen arvoinen ja aidosti helppo saada puolioikein, ja kuilu kahden lopputuloksen välillä on yksi idea: kukin värivalinta kuuluu tiettyyn pisteeseen renderöintiputkessa, ja invertointi on väärä työkalu sovellettuna väärässä vaiheessa. Koodi tässä käyttää PDFium Componentia, PDFium-pohjaista katselinta Delphille, C++Builderille ja Lazarukselle, jonka renderöintirajapinta paljastaa nuo vaiheet erikseen

Filterit ovat esitystila, eivät koskaan dokumenttitila

Yksi sääntö estää pahimman viraluokan täällä: lukutila muuttaa sitä miten bitmap tuotetaan tai jälkikäsitellään, eikä mitään muuta. PDF-tavut pysyvät koskemattomina, jokainen tila on peruutettavissa uudelleenrenderöimällä, ja "tallenna" ei koskaan kirjoita filteröityä ulkonäköä takaisin tiedostoon. Tämä kuulostaa ilmeiseltä kunnes lakitarkastaja tulostaa sopimuksen aktiivisen filterin alla ja arkistoi invertoidun version. Silloin kysymys "käyttääkö tulostus dokumentin omaa ulkonäköä vai ruudun" osoittautuu ansaitsevan eksplisiittisen vastauksen specissäsi, ei koodipolun onnettomuutta. Pidä filter-asetus katselimen tilassa, sovella se renderöintiaikana, ja tee jokainen vientipolu ilmoittaa minkä ulkonäön se käyttää

Sääntö maksaa itsensä takaisin kahdesti. Peruutettavuus tulee ilmaiseksi, koska tilojen vaihto renderöi uudelleen muuttumattomasta lähteestä: ei ole peruutuspinon ylläpitoa eikä mitään tapaa jolla peräkkäiset tilanvaihdot heikentäisivät sivua. Moni-ikkunaskenaariot pysyvät yhtenäisinä samasta syystä. Kahden saman dokumentin näkymää voi ajaa eri tiloja, koska kumpikin näkymä omistaa esitystilansa kun dokumenttiobjekti pysyy jaetuna

Renderöi ensin, muunna toiseksi

Tuettu malli on renderöinnin jälkeinen bitmap-käsittely: RenderPage tuottaa sivun rasterin, sitten muunnosläpikäynti säätää sitä. Komponentti toimittaa kolme muunnosta paikanvarsinaisina bitmap-operaatioina, InvertPdfBitmap, DuotonePdfBitmap ja GrayscalePdfBitmap, mikä tekee tilanvaihdosta puhdas kaksivaiheinen funktio:

Kaavio Delphi-PDF-katselimen lukutilaputkesta, jossa yksi PDFium RenderPage -kutsu ruokkii neljää lukutilaa, kukin paikallaan toimiva bittikarttamuunnos kuten InvertPdfBitmap tai DuotonePdfBitmap
RenderPage tuottaa rasterin kerran, ja aktiivinen lukumoodi valitsee yhden paikallaan tehtävän bittikarttamuunnoksen
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);  // tumma tausta, meripihkanvärinen teksti
    rmGrayscale:    GrayscalePdfBitmap(Result);
  end;
  // rmNormal putoaa läpi: asiakirja säilyttää omat värinsä
end;

Kaksi asiaa seuraa tästä suunnittelusta. Ensinnä, muunnoskustannus on suhteessa bitmapin kokoon, joten työ kuuluu sinne missä renderöintituloksesi on välimuistissa: filteröi välimuistitettu bitmap kerran, ei jokaisella maalauksella. Toiseksi, koska muunnos ajetaan valmiilla rasterilla, se osuu tekstiin, vektorigrafiikkaan, kuviin ja annotaatioulkonäköön samalla tavalla. Tuo yhtenäisyys on juuri se minkä pelkkä invertointi saa väärin valokuvien kohdalla. Se on syy miksi duotone-muunnos tekee paremman oletuksen tekstipitoisille dokumenteille, koska se kuvaa luminanssin valitulle tumma-vaalea värirammille sävyjen kieltämisen sijaan; invertointi pysyy saatavilla eksplisiittisenä valintana lukijoille jotka haluavat sen. Terävät glyfin reunat ovat erillinen vipu. reNoSmoothText-renderöintivalinta sammuttaa tekstin antialiasoinnin renderöintiaikana ja parittuu hyvin korkean kontrastin tilan kanssa suuressa zoomissa

Kaksi harmaasävyä jotka ovat eri mieltä

Renderöintivalintoihin kuuluu reGrayscale, joka näyttää oikotieltä jälkikäsittelyvaiheen ohi. Se ei ole sama operaatio:

Kaavio vertailee PDFium reGrayscale -renderointiasetusta, joka poistaa kuvien värikylläisyyden mutta jättää värikkäät otsikot, ja Delphi GrayscalePdfBitmap -jälkikäsittelyä, joka muuntaa koko sivun
Moottorivaihtoehto poistaa kuvasisällön värisävyn, kun taas jälkikäsittely muuntaa jokaisen valmiin bittikartan pikselin
// Moottoritaso: harmaasävy käytetty rasteroinnin aikana
GrayA := Pdf.RenderPage(0, 0, W, H, ro0, [reGrayscale]);

// Jälkikäsittely: renderöi värillisenä, muunna valmis bittikartta
GrayB := Pdf.RenderPage(0, 0, W, H);
GrayscalePdfBitmap(GrayB);

Enginitason valinta koskee kuvasisällön rasteritulostetta mutta ei yllä vektoritäyttöihin tai tekstiväreihin, joten sivu jossa on värit otsikot voi palata harmailla valokuvilla ja itsepäisesti sinisillä otsikoilla. GrayscalePdfBitmap valmiissa bitmapissa muuntaa kaiken, ehdottomasti. Renderöintivalinta ansaitsee silti paikkansa kun haluat kuvien olevan desaturoituja samalla kun pidät tekstin värin signaalina, mitä jotkut heikkonäköiset lukijat erityisesti suosivat. Mutta jos vaatimus lukee "harmaasävyinen sivu", jälkikäsittely on versio joka täyttää sen. Kumman polun valitsetkin, pidä molemmat RenderPage-ylikuormatyyliä mielessä. Funktiomuoto palauttaa bitmapin jonka kutsuja omistaa ja joka pitää vapauttaa, ja se merittaa heti kun filterit kertovat lensessä olevien renderöityjen bitmapien määrän

Taustat, valintamerkit ja PageColor-ansa

Ei jokainen mukavuussäätö ole muunnos. Valkoisen sivutaustan korvaaminen lämpimällä sävyllä on usein yksinään riittävä häikäistysherkillle lukijoille, ja sille on oma ominaisuus. Ominaisuus kantaa skooppiasääntöä joka nappaa ihmiset:

Kaavio PageColor-laajuusansasta Delphi-PDF-katselimessa: sävy näkyy näytöllä, kun taas RenderPage-tuloste pysyy valkoisena, ellei väriä anneta eksplisiittisesti
PageColor sävyttää vain näytöllisen näkymän, ja RenderPage pitää sivun valkoisena, ellei väriä anneta eksplisiittisesti
// Vaikuttaa vain näytöllä olevaan näkymään
PdfView.PageColor := $00D9EDF2;  // lämmin paperisävy sivusisällön takana

// RenderPage-tuloste ei ota PageColoria huomioon; välitä väri nimenomaisesti
Bmp := Pdf.RenderPage(0, 0, W, H, ro0, [], $00D9EDF2);

PageColor muuttaa mitä TPdfView näyttää, mutta RenderPage:n kautta tuotetut bitmapit pitävät oletusvalkoisen ellei Color-parametri sano muuta. Oire on luotettava: ruutu näyttää sävytetyn sivun, käyttäjä vie tai tulostaa, ja tuloste palautuu valkoiseksi. Arkistoi se saman vientipolitiikkapäätöksen alle ensimmäisestä luvusta

Loput väriominaisuudet määrittelevät overlay-merkit: HighlightColor hakutreffeille, SelectionColor käyttäjän tekstivalinnalle, ReadingWordColor puhutun-sanan kursorille. Jokainen niistä on tarkistettava uudelleen jokaisen filterin alla jota tarjoat. Amberinen lukukursori joka toimii valkoisella katoaa invertoinnin jälkeen; vaaleansininen valinta katoaa korkean kontrastin taustaan. Ylläpidä per-tila-overlay-paletteja ennemmin kuin yhtä globaalia joukkoa, ja testaa yhdistelmät tahallaan. Filterit plus teksti-puheeksi on normaali konfiguraatio lukijoille joita tämä ominaisuus palvelee, ei reunatapaus. Overlay-koneisto itse on käsitelty saavutettavan lukijan artikkelissa

Numerot, verifiointi ja tulostuskysymys

WCAG 2.1 muuttaa tämän ominaisuuden joksikin mitä voit mitata. Onnistumiskriteeri 1.4.3 pyytää 4.5:1 kontrastisuhdetta leipätekstille, ja 1.4.6 nostaa sen 7:1:een tehostettua kontrastia varten. Tarkista korkean kontrastin tilasi nuo suhteet vasten kontrastianalysaattorilla ajettuna todellisessa renderöidyssä tulosteessa. Teksti kuvien päällä ja teksti lomakekentissä on siellä missä suhteet hiljaa epäonnistuvat silloinkin kun leipäteksti läpäisee

Tulostus ansaitsee oman päätöksensä, ja puolustettava oletus on dokumentin oma ulkonäkö, "tulosta näytetynä" tarjottuna eksplisiittisenä käyttäjävalintana. Tulostettu sivu on todiste useammissa vuoksissa kuin katselintekijät yleensä odottavat, ja invertitu sopimuksen tuloste on tukitapaus lakimaisella maustella. Vielä yksi paritus merittaa suorituskyvyn kannalta: filteröity renderöinti kaksinkertaistaa bitmaptyön jokaisella tilanvaihdolla, joten älä sovella muunnosta jokaisessa paint-viestissä. Välimuistita filteröity bitmap ja aja muunnos uudelleen vain kun sivu, zoom tai tila oikeasti muuttuu. Välimuististrategia joka tekee tästä halvan elää renderöintivälimuistin ja zoomin suorituskykyartikkelissa

Yksi asia ratkaista käyttöliittymässäsi ennemmin kuin koodissasi: mikä tila on oikea oletus. Ei ole yksittäistä vastausta, joten tarjoa joukko ja anna lukijan valita. Korkea kontrasti sopii useimmalle tekstipitoiselle lukemiselle, invertointi sopii lukijoille jotka erityisesti haluavat vaaleaa-tumman-varisella, harmaasävy leikkaa värikohinan, ja taustasävy käsittelee häikäistysherkkyyttä. Pysäytä valinta per käyttäjä, palauta se käynnistyksessä, ja pidä yhden näppäimen polku takaisin normaaliin, koska lukija joka laskeutuu tilaan jota hän ei voi lukea tarvitsee nopean ulospääsyn

Tässä käytetyt renderöintivalinnat, bitmap-muunnokset ja näkymän väriominaisuudet toimitetaan PDFium Component:n mukana Delphille, C++Builderille ja Lazarus/FPC:lle, täydellisen lähdekoodin kanssa jotta muunnostoteutukset voidaan auditoida tai laajentaa