En Excel-arbeidsbok kan bære EMF- og WMF-bilder, og den konvensjonelle måten å tegne én på, er å gi bytestrømmen til operativsystemets metafilspiller. Det er en avgjørelse verdt å se rett på: en metafil er en serialisert kommandostrøm for et grafikk-API, og å spille én av, betyr å la en fil som ankom over e-post drive grafikkdriveren. HotXLS tar den andre ruten. XLSDecodeVectorScene parser metafilen selv, validerer headeren, hver poststørrelse, det deklarerte posttotalen og den nøyaktige plasseringen av slutt-på-fil-posten, avviser escape-poster blankt, og returnerer en TXLSVectorScene av primitive tegnekommandoer som Canvas- og SVG-backendene spiller av gjennom sin egen kode. Ingen driveravspilling er involvert på noe tidspunkt
Byttet er dekning for innkapsling. En rektangelorientert kommandohviteliste vil ikke reprodusere hver metafil en designer kan skape, så scenen rapporterer hvor mange tegneposter den ikke kunne representere, og kalleren avgjør hva den vil gjøre med det. For en servertjeneste som renderer dokumenter den ikke skapte, er det byttet riktig vei rundt
Hvorfor er metafilavspilling en dårlig passform for utiltrudt input?
Fordi formatet ikke er et bilde, det er et program. En EMF-poststrøm manipulerer en device-context-tilstandsstakk, allokerer og velger objekter fra en håndtakstabell, og kan bære escape-poster hvis payload sendes videre til en enhetsdriver. Å spille den av, trener veier i plattformens grafikkstakk som ble skrevet under antakelsen om at metafilen kom fra et samarbeidende program på samme maskin. Når inputen er et regnearkvedlegg, er den antakelsen borte, og ingen mengde omsorg inne i regnearkbiblioteket hjelper, fordi biblioteket ikke er komponenten som gjør parsingen
Dette er den samme resonneringen som styrer containerlaget. En arbeidsbok er et ZIP-arkiv, og HotXLS validerer dets sentralkatalog snarere enn å stole på deklarerte forskyvninger, som beskrevet i artikkelen om ZIP end-of-central-directory-validering. Metafilpayloads er neste lag av det samme problemet
Hva dekoderen sjekker før den tegner noe
Valideringen er strukturell og skjer på forhånd, fordi en parser som begynner å tegne og validerer underveis, allerede har handlet på data den ikke har verifisert. Headeren må matche strengt snarere enn plausibelt. Hver post må erklære en størrelse som passer innenfor den gjenværende bufferen og er stor nok for sine egne faste felt. Postantallet headeren erklærer, må matche postene faktisk til stede. Slutt-på-fil-posten må sitte nøyaktig der strømmen slutter, ikke bare et sted i nærheten, noe som lukker søppel-på-slutten-trikset som gjemmer en andre payload bak et gyldig bilde
Utover struktur er dekoderen fail-closed på semantikk. Escape-poster avslås, ikke hoppes over. En tilstandsendrende post dekoderen ikke modellerer, får dekodingen til å feile snarere enn å bli ignorert, for å ignorere en tilstandsendring betyr at hver etterfølgende tegnekommando kjøres i en tilstand filen ikke ba om, og resultatet er et bilde som er feil på en måte ingen kan forutsi. Tegneposter utenfor det støttede kommandosettet er en annen sak: de telles og hoppes over, fordi en manglende form er et synlig, rapporterbart gap snarere enn en lydløs korruptering
Budsjetter er en del av formatkontrakten
Vektorformater har sin egen versjon av dekomprimeringsbomben. Noen få kilobyte med poster kan erklære polylinjer med hundrevis av millioner punkter, eller et bilde hvis deklarerte dimensjoner multipliserer inn i terabyte. Grenser må derfor være eksplisitte konstanter snarere enn det maskinen tilfeldigvis overlever
// Fra lxVectorScene: dekodebudsjettet, slått fast snarere enn underforstått
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;
To av disse fortjener en merknad. Device-context-dybdetaket på 32 finnes fordi SaveDC- og RestoreDC-poster nester, og en ubalansert strøm kan pushe for alltid; 32 er generøst for ekte metafiler og billig å håndheve. Koordinattaket finnes fordi koordinater mater en transform, og en verdi nær grensene av heltallsområdet produserer et transformert resultat som enten er uendelig eller wrapper, hvoretter enhver bbox-beregning nedstrøms er nonsens. Å klemme koordinater ved parsetid er mye enklere å resonnere om enn å forsvare hver konsument av geometrien
Å bruke scenen
Dekoderen gir tilbake et objekt du eier, et kommandoantall, en nominell størrelse, og et antall tegneposter den valgte å ikke representere
uses
lxVectorScene;
var
Scene: TXLSVectorScene;
Error: WideString;
I: Integer;
begin
// Data holder rått bildepayload tatt fra arbeidsboken
if not XLSDecodeVectorScene(Data, xlsvfEmf, Scene, Error) then
begin
// Avslått: header, grenser, totaler, EOF-plassering eller et budsjett
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;
Kommandoposten bærer alt en backend trenger og ingenting som krever en enhet: pen til stede, farge, bredde og stil; børste til stede og farge; geometrien; og for tekst strengen, fontnavnet, størrelsen, stilene og justeringen. Det er det som gjør den samme scenen brukbar av både skjerm-canvas-rendereren og SVG-skriveren, og det er hvorfor vektorveien ikke divergerer mellom forhåndsvisning og eksport. Skjermrendering av regnearkinnhold generelt er dekket i artikkelen om tilpasset VCL grid-rendering
Å avvise et bilde skader ikke arbeidsboken
En viktig egenskap ved dette designet er at en avslått dekoding påvirker bare rendering. Den opprinnelige payloaden blir i modellen, så en arbeidsbok som åpnes og lagres igjen, bærer sine metafilbilder ut byte for byte, enten den trygge dekoderen kunne tegne dem eller ikke. Den eksisterende avgrensede rasterveien forblir også tilgjengelig som fallback. Med andre ord, den strenge parseren gater hva som blir kjørt, ikke hva som blir bevart, noe som er skillet som lar en sikkerhetsmotivert endring sendes uten å bli en datatapsendring
Håndtering av tegneobjekter generelt, inkludert delene av objektmodellen som overlever rundturer urørt, er dekket i artikkelen om diagrammer, bilder og tegninger
Hvor dette etterlater en serverutrulling
Hvis du renderer brukeropplastede arbeidsbøker i en tjeneste, er den praktiske posisjonen nå forsvarlig: metafilbilder parses av kode du kan revidere, avgrenset av konstanter du kan lese, og aldri gitt til en grafikkdriver. Det ærlige forbeholdet er dekning. Komplekse metafiler produsert av tegneverktøy vil treffe den hoppede-post-telleren, og svaret på det er å synliggjøre telleren snarere enn å utvide hvitelisten lydløst. Et bilde som renderer delvis og sier det, er en supportsamtale; et bilde som renderer feil og sier ingenting, er en feilrapport fra en kunde
HotXLS håndterer XLS, XLSX, ODS og CSV nativt i Delphi og C++Builder uten Excel installert, og den samme avgrenset-parse-filosofien går gjennom dets container-, formel- og tegnelag. Format- og sikkerhetsdetaljer er oppført på produktsiden for HotXLS Delphi spreadsheet component