Excel-työkirja voi kantaa EMF- ja WMF-kuvia, ja perinteinen tapa piirtää sellainen on antaa tavuvirta käyttöjärjestelmän metafile-soittimelle. Se on päätös, joka kannattaa katsoa suoraan silmiin: metafile on sarjallistettu komentovirta grafiikka-API:lle, ja sen toistaminen tarkoittaa sähköpostitse saapuneen tiedoston päästämistä ajamaan grafiikka-ajuria. HotXLS valitsee toisen reitin. XLSDecodeVectorScene jäsentää metafilen itse, varmistaa otsikon, jokaisen tietueen koon, ilmoitetun tietuekokonaismäärän ja tiedostonlopputietueen tarkan sijainnin, hylkää escape-tietueet täysin ja palauttaa kohteen TXLSVectorScene, joka sisältää primitiivisiä piirtokomentoja, jotka Canvas- ja SVG-taustat toistavat omalla koodillaan. Ajuritoistoa ei ole mukana missään vaiheessa
Kauppa on kattavuus sulkua vastaan. Suorakulmiopohjainen komentojen sallittujen luettelo ei toista jokaista metafilea, jonka suunnittelija voi luoda, joten skenaario raportoi, monta piirtotietuetta se ei voinut esittää, ja kutsuja päättää, mitä siitä seuraa. Palveluprosessille, joka renderöi dokumentteja, joita se ei luonut, tuo kauppa on oikein päin
Miksi metafile-toisto on huono sopivuus luotamattomalle syötteelle?
Koska muoto ei ole kuva, se on ohjelma. EMF-tietuevirta käsittelee laitekontekstin tilapinoa, varaa ja valitsee objekteja kahvataulukosta, ja se voi kantaa escape-tietueita, joiden payload välitetään laitteenajurille. Sen toistaminen harjoittaa polkuja alustan grafiikkapinossa, jotka kirjoitettiin oletuksella, että metafile tuli yhteistyöhön sopivasta sovelluksesta samalla koneella. Kun syöte on laskentataulukon liite, tuo oletus on poissa, eikä mikään määrä huolellisuutta laskentataulukkokirjaston sisällä auta, koska kirjasto ei ole komponentti, joka tekee jäsennyksen
Tämä on sama päättely, joka hallitsee säiliökerrosta. Työkirja on ZIP-arkisto, ja HotXLS varmistaa sen keskihakemiston sen sijaan että luottaisi ilmoitettuihin siirtymiin, mistä kerrotaan artikkelissa ZIP:n end-of-central-directory -validointi. Metafile-payloadit ovat saman ongelman seuraava kerros
Mitä dekooderi tarkistaa ennen kuin piirtää mitään
Validointi on rakenteellista ja se tapahtuu etukäteen, koska jäsentin, joka alkaa piirtää ja varmistaa matkan varrella, on jo toiminut datalla, jota se ei ole varmistanut. Otsikon on täsmättävä tiukasti, ei uskottavasti. Jokaisen tietueen on ilmoitettava koko, joka mahtuu jäljellä olevan puskurin sisään ja on riittävän suuri sen omille kiinteille kentille. Otsikon ilmoittaman tietueiden lukumäärän on täsmättävä todellisuudessa läsnä oleviin tietueisiin. Tiedostonlopputietueen on istuttava täsmälleen siellä, missä virta päättyy, eikä ainoastaan jossakin lähellä sitä, mikä sulkee perässä roikkuvan roskan temppu, joka piilottaa toisen payloadin pätevän kuvan taakse
Rakenteen ulkopuolella dekooderi on fail-closed semantiikan suhteen. Escape-tietueet hylätään, ei ohiteta. Tilaa muuttava tietue, jota dekooderi ei mallinna, saa purun epäonnistumaan sen ohittamisen sijaan, koska tilamuutoksen ohittaminen tarkoittaa, että jokainen sitä seuraava piirtokomento suoritetaan tilassa, jota tiedosto ei pyytänyt, ja tulos on kuva, joka on väärin tavalla, jota kukaan ei voi ennustaa. Tuettujen komentojen joukon ulkopuoliset piirtotietueet ovat toinen asia: ne lasketaan ja ohitetaan, koska puuttuva muoto on näkyvä, raportoitava aukko eikä hiljainen korruptio
Budjetit ovat osa muotosopimusta
Vektorimuodoilla on oma versionsa pakkauksen pommi. Muutama kilotavu tietueita voi ilmoittaa polylinoja, joissa on satoja miljoonia pisteitä, tai kuvan, jonka ilmoitetut ulottuvuudet kertoutuvat teratavuiksi. Rajat on siksi oltava eksplisiittisiä vakioita, eivät sitä, minkä kone sattuu selviävän
// lxVectorScene-yksiköstä: purkubudjetti, ilmoitettuna eikä epäsuorana
XL_VECTOR_MAX_RECORDS = 1000000;
XL_VECTOR_MAX_HANDLES = 4096;
XL_VECTOR_MAX_DC_DEPTH = 32;
XL_VECTOR_MAX_COMMANDS = 100000;
XL_VECTOR_MAX_POINTS_PER_RECORD = 100000;
XL_VECTOR_MAX_TOTAL_POINTS = 2000000;
XL_VECTOR_MAX_TEXT_CHARS = 4096;
XL_VECTOR_MAX_TOTAL_TEXT_CHARS = 1000000;
XL_VECTOR_MAX_IMAGE_SIDE = 8192;
XL_VECTOR_MAX_IMAGE_PIXELS = 32 * 1024 * 1024;
XL_VECTOR_MAX_IMAGE_BYTES = 64 * 1024 * 1024;
XL_VECTOR_MAX_COORD = 1000000000;
Kaksi näistä ansaitsee huomion. Laitekontekstin syvyyskatto 32 on olemassa, koska SaveDC- ja RestoreDC-tietueet sijoittuvat sisäkkäin, ja epätasapainoinen virta voi puskea ikuisesti; 32 on antelias todellisille metafileille ja halpa valvoa. Koordinaattikatto on olemassa, koska koordinaatit ruokkivat transformaatiota, ja arvo kokonaislukualueen rajojen lähellä tuottaa muunnetun tuloksen, joka on joko ääretön tai kiertää, minkä jälkeen jokainen rajauslaatikon laskenta alavirrassa on merkityksetöntä. Koordinaattien puristaminen jäsennyshetkellä on paljon helpompi päätellä kuin jokaisen geometrian kuluttajan puolustaminen
Skenaarion käyttäminen
Dekooderi luovuttaa takaisin objektin, jonka omistat, komentojen lukumäärän, nimelliskoon ja lukumäärän piirtotietueista, joita se päätti olla esittämättä
uses
lxVectorScene;
var
Scene: TXLSVectorScene;
Error: WideString;
I: Integer;
begin
// Data pitää sisällään raakan kuvapayloadin, joka otettiin työkirjasta
if not XLSDecodeVectorScene(Data, xlsvfEmf, Scene, Error) then
begin
// Hylätty: otsikko, rajat, kokonaismäärät, EOF-sijainti tai budjetti
LogReject('metafile rejected: ' + Error);
Exit;
end;
try
if Scene.SkippedDrawRecords > 0 then
LogWarning(Format('%d drawing records outside the safe subset',
[Scene.SkippedDrawRecords]));
for I := 0 to Scene.Count - 1 do
case Scene.Commands[I].Kind of
xlsvcRectangle: DrawRect(Scene.Commands[I]);
xlsvcEllipse: DrawEllipse(Scene.Commands[I]);
xlsvcPolyline,
xlsvcPolygon,
xlsvcBezier: DrawPath(Scene.Commands[I]);
xlsvcText: DrawText(Scene.Commands[I]);
xlsvcImage: DrawImage(Scene.Commands[I]);
end;
finally
Scene.Free;
end;
end;
Komentotietue kantaa kaiken, mitä tausta tarvitsee, eikä mitään, mikä vaatii laitetta: kynän läsnäolon, värin, leveyden ja tyylin; siveltimen läsnäolon ja värin; geometrian; sekä tekstille merkkijonon, fontin nimen, koon, tyylit ja tasauksen. Se tekee samasta skenaariosta käytettävän sekä näytöllä toimivalle canvas-renderöijälle että SVG-kirjoittajalle, ja siksi vektoripolku ei eroa esikatselun ja viennin välillä. Laskentataulukon sisällön näytörenderöinti yleisesti käsitellään artikkelissa mukautetun VCL-ruudukon renderöinnistä
Kuvan hylkääminen ei vahingoita työkirjaa
Tärkeä ominaisuus tässä suunnittelussa on, että hylätty purku vaikuttaa vain renderöintiin. Alkuperäinen payload pysyy mallissa, joten työkirja, joka avataan ja tallennetaan uudelleen, kantaa metafile-kuvansa ulos tavu tavulta, riippumatta siitä, pystyikö turvallinen dekooderi piirtää ne. Olemassa oleva rajattu rasteripolku pysyy myös käytettävissä varatienä. Toisin sanoen tiukka jäsentin portittaa sen, mitä suoritetaan, ei sitä, mitä säilytetään, ja tuo ero mahdollistaa tietoturvamotivoidun muutoksen toimittamisen ilman että se muuttuu datamenetyksen muutokseksi
Piirtoobjektien käsittely yleisesti, mukaan lukien objektimallin osat, jotka selviävät kierroksista koskemattomina, käsitellään artikkelissa kaaviot, kuvat ja piirrokset
Mihin tämä jättää palvelinkäyttöönoton
Jos renderöit käyttäjien lataamia työkirjoja palvelussa, käytännön asema on nyt puolustettavissa: metafile-kuvat jäsentää koodi, jota voit auditoida, rajoittaa vakiot, jotka voit lukea, eikä mitään koskaan anneta grafiikka-ajurille. Rehellinen varaus on kattavuus. Piirustustyökalujen tuottamat monimutkaiset metafilet osuvat ohitettujen tietueiden laskuriin, ja vastaus siihen on tuoda laskuri esiin sen sijaan että laajentaisi sallittujen luetteloa hiljaa. Kuva, joka renderöityy osittain ja sanoo sen, on tukikeskustelu; kuva, joka renderöityy väärin eikä sano mitään, on virheraportti asiakkaalta
HotXLS käsittelee muodot XLS, XLSX, ODS ja CSV natiivisti Delphissä ja C++Builderissa ilman asennettua Exceliä, ja sama rajatun jäsennyksen filosofia kulkee sen säiliö-, kaava- ja piirroskerrosten läpi. Muoto- ja tietoturvayksityiskohdat on lueteltu HotXLS Delphi spreadsheet -komponentin tuotesivulla