Ääneenlukutoiminnolla on äänen lisäksi yksi näkyvä tehtävä: jokaisen puhutun sanan kohdalla kyseinen sana on korostettava sivulla ja pidettävä näkyvissä Tähän tarvitaan jokaisen sanan rajaava suorakulmio indeksoituna samaan merkkivirtaan, josta puhemoottori lukee Jos rajaavat suorakulmiot saadaan mutta indeksointi epäonnistuu, korostus jää sana tai kaksi jälkeen äänestä; jos indeksointi onnistuu mutta sivutilan käsittely mokataan, korostus osuu kokonaan väärälle sivulle Tämän puhepuoli, itse syntetisaattori, on osa, joka harvoin hajoaa SAPI raportoi sanarajat merkin tarkkuudella Se, mikä hajoaa, on ohut kartoituskerros puhepuskurin merkkisiirtymän ja renderöidyllä sivulla olevan suorakulmion välillä
PDFium Component toimittaa tämän kartoituksen Delphille, C++Builderille ja Lazarukselle; sanalaatikot ovat olleet saatavilla versiosta v1.53 lähtien ja seurantakursori versiosta v1.56 Pinta-ala on tarkoituksella kapea: kutsu, joka palauttaa sivun sanalaatikot, seuraaja, joka muuttaa merkkisiirtymän maalatuksi korostukseksi, sekä pari ominaisuutta väriä ja automaattista vieritystä varten Kapeudestaan huolimatta järjestys, jossa kutsut tehdään, ratkaisee toimiiko ominaisuus, ja useimmat alla kuvatuista virheistä johtuvat oikeiden funktioiden kutsumisesta väärässä järjestyksessä
Merkit eivät ole sanoja, ja puhesynteesimoottorit puhuvat merkkeinä
Puhemoottori kuluttaa litteän merkkijonon ja raportoi edistymisen merkkiasemina kyseisessä merkkijonossa PDF-sivulla on glyyfit sijoitettuna sivutilaan, jossa "sana" on heuristinen glyyfijonojen klusteri Nämä kaksi koordinaatistoa eivät jaa mitään, ellei syntetisaattorille annettu teksti ole tavusta tavuun sama teksti, josta sanalaatikot on laskettu Tämä on sääntö numero yksi, eikä se anna anteeksi Jos tyhjätila normalisoidaan, pehmeät tavuviivat poistetaan tai poimittua tekstiä muutoin "siistitään" ennen sen puhumista, jokainen myöhempi siirtymä menee hiljaisesti väärin Puhu tarkalleen se, mitä poimit, tai pidä yllä eksplisiittistä siirtymien uudelleenkartoitustaulukkoa Kolmatta vaihtoehtoa, joka selviytyisi todellisista asiakirjoista, ei ole
Uudelleenkartoitustaulukko ei ole hypoteettinen reunatapaus Heti kun käyttöliittymäsi lisää puhutun sivuilmoituksen ("sivu viisi") tai laajentaa lyhenteen syntetisaattoria varten, puhuttu merkkijono eroaa poimitusta Tallenna jokaisen lisäyksen sijainti ja pituus, ja vähennä sitten kertynyt korjaus ennen jokaista seurantakutsua Kirjanpitoa on ehkä kaksikymmentä riviä, ja se on ero korostuksen välillä, joka selviää seuraavasta ominaisuuspyynnöstä, ja sellaisen välillä, joka hajoaa heti kun joku pyytää puhuttuja otsikoita
Mitä sanalaatikko antaa
Jokainen TPdfWordBox-tietue kantaa sanan tekstin, sen StartIndex-arvon ja merkkimäärän Count sivutekstin sisällä, sivutilassa olevan Rect-suorakulmion sekä ykkösestä alkavan Page-numeron StartIndex-kenttä on silta kahden koordinaatiston välillä: se on sama siirtymä, jonka SAPI antaa takaisin lukiessaan PageWordBoxes palauttaa koko taulukon aktiiviselle sivulle
procedure TReaderForm.PreparePage(PageNo: Integer);
begin
PdfView.PageNumber := PageNo; // näkymän sanalaatikot seuraavat sen näyttämää sivua
FWords := PdfView.PageWordBoxes;
FPageText := BuildSpeechText(FWords); // yhdistä Word.Text-arvot järjestyksessä
if Length(FWords) = 0 then
HandleImageOnlyPage(PageNo); // skannaus ilman tekstikerrosta
end;
Järjestystä koskeva kommentti on ratkaisevan tärkeä Katseluohjelman PageWordBoxes tokenisoi sen sivun tekstikerroksen, jota näkymä parhaillaan näyttää, joten navigoi näkymä ensin ja poimi vasta sitten; renderöintiä ei tarvita, vain avattu asiakirja (Asiakirjakomponentti, TPdf, tarjoaa oman PageWordBoxes-toiminnon, joka on sidottu Pdf.PageNumber-arvoon päätöntä käyttöä varten Nämä kaksi sivunumeroa ovat toisistaan riippumattomia, mikä on oma ansansa) Tyhjä tulos sivulla, joka näyttää sisältävän sisältöä, tarkoittaa vain kuvana olevaa skannausta Ohjaa se OCR:ään tai ilmoita siitä ainakin ääneen ("sivu 4 ei sisällä luettavaa tekstiä") sen sijaan, että ääni vaikenisi ilman selitystä
SAPI-sanarajojen kytkeminen seuraajaan
TrackReadingWordAt, näkymässä, on koko ominaisuuden sarana Anna sille sivunumero ja merkki-indeksi; se löytää kyseisen merkin sisältävän sanalaatikon, maalaa lukukursorin sen päälle ja palauttaa sanaindeksin, tai −1, kun indeksi osuu sanojen väliin SAPI:n sanarajailmoitus toimittaa juuri sen merkkiaseman, jota se tarvitsee
procedure TReaderForm.OnSpeechWordBoundary(StreamPos: Integer);
var
WordIdx: Integer;
begin
// Kartoittaa siirtymän sanalaatikkoon ja siirtää korostuksen yhdellä kutsulla
WordIdx := PdfView.TrackReadingWordAt(FPageNo, StreamPos);
if WordIdx < 0 then
Exit; // raja osui minkään sanan ulkopuolelle: pidä edellinen korostus
end;
Kaksi varovaisuutta osoittavaa yksityiskohtaa ansaitsee huomion tässä Ensinnäkin TrackReadingWordAt pitää omaa sanalaatikkovälimuistiaan seuratulle sivulle, joka rakennetaan uudelleen automaattisesti sivun vaihtuessa, joten kustannus per raja pysyy tasaisena riippumatta siitä, kuinka nopeasti rajat saapuvat Toiseksi se ei tarkista rajoja anteliaasti Indeksi, joka on sivun merkkimäärän tasalla tai sen yli, palauttaa −1 sen sijaan, että se rajattaisiin viimeiseen sanaan Kohtele −1-arvoa "pidä edellinen korostus" -tilana, ei koskaan virheenä, koska välimerkkijaksot ja sanojen välinen tyhjätila tuottavat oikeutetusti rajoja, jotka eivät kuulu millekään sanalle Jokaisen −1:n lokittaminen hautaa sinut Laske ne sivukohtaisesti sen sijaan, ja tarkastele tarkkaan jokaista sivua, jolla suhde piikittyy, sillä se yleensä tarkoittaa tekstin normalisoinnin ristiriitaa takaisin sääntöön numero yksi
Itse kursori: väri, seuranta ja siivous
SetReadingWord maalaa korostuksen suoraan, kun pidät sanalaatikkoa itse hallussasi, ReadingWordColor tyylittelee sen, ja ReadingWordFollow := True vierittää näkymää juuri sen verran, että puhuttu sana pysyy näkyvissä Tuo viimeinen ominaisuus ansaitsee paikkansa Käsin rakennettu "keskitä nykyinen sana" -vieritys saa sivun nykimään jokaisella rivinvaihdolla, ja liikeherkät lukijat sammuttavat koko ominaisuuden minuutin sisällä Korostus renderöityy vain sivulle, joka näytetään aktiivisessa TPdfView-komponentissa parhaillaan, joten monisivuisen lukemisen on edettävä PageNumber-arvon kanssa puheen tahdissa ja sitten ajettava valmisteluvaihe uudelleen uudelle sivulle ennen kuin sen ensimmäinen rajatapahtuma saapuu Jätä se tekemättä, ja jokaisen sivun ensimmäiset korostukset osoittavat vanhentuneisiin koordinaatteihin
procedure TReaderForm.StopReading;
begin
FVoice.Stop; // pysäytä SAPI-toisto ensin
PdfView.ClearReadingWord; // poista sitten korostus; vanhentunut kursori näyttää bugilta
end;
Symmetria sammutuksessa on se, mikä pitää korostuksen rehellisenä Jokaisen tauon, pysäytyksen ja sivunvaihdon polun on päädyttävä ClearReadingWord-kutsuun Jätä se pois, ja keltainen suorakulmio jää lojumaan pysäytetylle sivulle näyttäen täsmälleen vialta, minkä jokainen testaaja raportoi, vaikka mikään ei oikeasti ole rikki
Puhenopeus kuormittaa tätä putkea enemmän kuin asiakirjan koko 300 sanan minuuttinopeudella rajatapahtumat saapuvat 200 ms:n välein, ja nopeimmilla SAPI-nopeuksilla ne tulevat nopeammin kuin silmä mukavasti seuraa Oikea vastaus on yhdistäminen, ei jonottaminen Jos uusi raja saapuu, kun korostuksen päivitys on vielä kesken, hylkää vanhentunut ja maalaa uusin Kursori, joka käy läpi jokaisen sanan järjestyksessä mutta laahaa puoli sekuntia jäljessä, tuntuu rikkinäiseltä; sellainen, joka silloin tällöin hyppää sanan yli pysyen synkronissa äänen kanssa, ei tunnu
Reunatapaukset, jotka erottavat demot tuotteista
Muutamat asiakirjaluokat paljastavat saumat Yhdistelmämerkit ovat hienovaraisimpia: Unicode-sekvenssit, kuten peruskirjain plus yhdistelmädiakriitti, voivat viedä enemmän merkki-indeksejä kuin visuaalinen sana antaa ymmärtää, joten mikä tahansa siirtymäaritmetiikka, joka olettaa yhden indeksin per glyyfi, ajautuu hitaasti vinoon Tämä on vahvin argumentti sen puolesta, että TrackReadingWordAt saa omistaa kartoituksen sen sijaan, että sananumerot laskettaisiin käsin Tavutus on arkisempaa mutta yleisempää: rivinvaihdon yli katkennut sana muuttuu kahdeksi laatikoksi, ja jos puhut sen yhtenä yksikkönä, sen jälkiosan rajatapahtuma ratkeaa ensimmäiseen laatikkoon Tämä on yleensä ihan ok, mutta se on päätös, joten tee se tarkoituksella sen sijaan, että löytäisit sen sattumalta Tagitus muuttaa itse lukujärjestystä Kun asiakirjalla on kunnolliset rakennetagit (ISO 14289:n, PDF/UA:n, alue), sanajärjestys seuraa loogista rakennetta; ilman niitä se turvautuu asettelun heuristiikkaan, ja kaksipalstainen tagittamaton sivu voi lukeutua suoraan molempien palstojen yli Kierretyt sivut ovat viimeinen yleinen tapaus: jokaisen sanan Rect rajaa sen edelleen oikein sivutilassa, mutta vaakavirtaukselle viritetty näkymän seurantakäytäntö vierittää nykivästi, kun teksti kulkee pystysuunnassa, joten pidä ainakin yksi kierretty asiakirja regressiojoukossa Lukujärjestyksen käsittelystä, lausetason yksiköistä ReadingUnits-toiminnon kautta sekä laajemmasta avustavasta pinosta, katso esteettömän PDF-lukijan rakentaminen Delphissä
Yksi alustarajoite muovaa käyttöönottoa SAPI toimii vain Windowsissa Sanalaatikko- ja seurantarajapinta on tavusta tavuun identtinen Lazaruksen ja FPC:n alla, mutta Linux- ja macOS-käännökset tarvitsevat eri syntetisaattorin kytkettynä samojen rajatapahtumien taakse; tuo asennus käydään läpi artikkelissa katseluohjelman ajaminen Lazaruksella ja FPC:llä Korostuksen kustannus on myös vuorovaikutuksessa sivuvälimuistisi kanssa puhenopeuksien kasvaessa, ja budjettiaritmetiikka artikkelissa renderöintivälimuisti ja zoomauksen suorituskyky pätee tässäkin muuttumattomana
Kun yksittäisen sanan korostus on väärä tarkkuustaso
Sanatason karaoke ei aina ole se, mitä lukija haluaa Korkeilla puhenopeuksilla sana sanalta vilkkuva kursori muuttuu omaksi visuaaliseksi häiriökseen, ja osa kuuntelijoista seuraa lausetta mukavammin kuin yksittäisten sanojen strobovaloa Tätä tapausta varten komponentti tarjoaa karkeamman yksikön ReadingUnits palauttaa lause- ja lohkotason yksiköitä, joilla kullakin on omat korostussuorakulmionsa, ja ne maalataan SetReadingHighlight-funktiolla SetReadingWord-funktion sijaan Kytkentä on samanmuotoinen: rajasiirtymä ohjaa edelleen, mikä yksikkö syttyy, mutta korostettava yksikkö kattaa lauseenosan tai rivin yksittäisen sanan sijaan Sekä hitaammat lukijat että nopea toisto suosivat yleensä tätä, eikä mikään estä tarjoamasta molempia tiloja asetuksen takana
Versiorajat kannattaa selvittää ennen tämän varaan rakentamista: sanalaatikot vaativat PDFium Component -version v1.53 tai uudemman, ja seurantakursori vaatii version v1.56 Koko lukurajapinta, lausetason yksiköt ja toimiva ääneenlukudemo löytyvät PDFium Component -tuotesivulta