Behelyez egy 600×400 képpontos logót egy generált számla fejlécébe, amely a 96 DPI-s fejlesztői monitorán megfelelőnek tűnik, majd egy héttel később egy magas DPI-s laptopot használó ügyfél azt jelzi, hogy a logó egy postai bélyeg méretében nyomtatódik ki. A képpontok (pixels) soha nem változtak. Ami változott, az a feltételezés, hogy a képpontok száma fizikai méretet jelent, és az OOXML-ben ez nem így van. A táblázatban lévő kép a méreteit EMU-ban hordozza, és amíg nem EMU-ban — vagy a rá tisztán leképezhető valós mértékegységekben — gondolkodik, az elrendezése ki van szolgáltatva annak a DPI-nek, amelyet a leképező gép éppen feltételez
A HotXLS egy natív VCL táblázatkezelő komponens Delphihez és C++Builderhez, amely Excel vagy bármilyen COM-függőség nélkül olvassa és írja az XLS és XLSX fájlokat. A v2.91.0 verziótól kezdve az XLSX képobjektum már nem kényszeríti Önt a mértékegységek kézi átszámítására: a nyers EMU mellett centiméterben, hüvelykben és pontokban is elérhetővé teszi a szélességet és magasságot, valamint egy Scale metódust is biztosít, amely százalékos arányban méretezi át a képet opcionális képarány-rögzítéssel. Ez a cikk arról szól, hogy mi is az az EMU valójában, miért választotta a DrawingML, és hogyan használhatja az új geometria felületet a képek fizikai méret szerinti elhelyezésére a nem megbízható képpontszám helyett
Mi az az EMU és miért használja azt a DrawingML
Az EMU az English Metric Unit (angol metrikus egység) rövidítése, és ez a DrawingML alapvető hosszegysége, amely a teljes Office Open XML család (ECMA-376, 1. rész, §20) közös rajzoló rétege. Egy EMU úgy van meghatározva, hogy pontosan 914400 EMU felel meg egy hüvelyknek és 360000 EMU egy centiméternek. Ez a két konstans a teljes oka az egység létezésének. A 914400 osztható 2, 3, 4, 5, 6, 8, 9, 10, 12 és még sok más számmal; tényezői a 26 × 32 × 52 × 127. Mivel 1 hüvelyk pontosan 2,54 cm, egy olyan egység kiválasztása, amely osztható 360000-rel és a 914400 tiszta törtjével is, lehetővé teszi a formátum számára a hüvelyk, centiméter és pont kifejezését egész számokként, kerekítés nélkül az egységhatáron. Ahol a lebegőpontos „1,27 cm” eltolódna, ott az EMU 457200-at tárol és pontos marad
A másik fontos mértékegység itt a pont (point). A tipográfiai pont a hüvelyk 1/72 része, így 12700 EMU felel meg egy pontnak (914400 / 72). A színfalak mögött az Excel maga is pontokban gondolkodik a sorok magasságát, a betűméretet és a margókat illetően, ezért hasznos a képgeometria pontokban történő megadása, ha azt szeretné, hogy a kép a szöveges metrikákhoz igazodjon, nem pedig egy nyomtatott vonalzóhoz. A HotXLS mind a négy kapcsolatot egységkonstansként kódolja a könyvtárban:
const
XlsxEmuPerInch = 914400; // 1 hüvelyk
XlsxEmuPerCm = 360000; // 1 centiméter
XlsxEmuPerPoint = 12700; // 1 pont (1/72 hüvelyk)
XlsxEmuPerPixel = 9525; // 1 képpont 96 DPI mellett (914400 / 96)
Ez az utolsó sor a postai bélyeg hiba kulcsa. Egy képpontnak csak akkor van fizikai mérete, ha rögzítünk egy DPI-t, és a 9525 EMU a képpont mérete kifejezetten 96 DPI mellett. Az Excel alapértelmezett leképezési felbontása 96 DPI, így egy 100 képpontos kép 100 × 9525 = 952500 EMU ≈ 2,54 cm méretet eredményez alapértelmezett beállításoknál — de semmi sem garantálja a fájlban, hogy a megjelenítő is a 96-ot használja. Ha valós egységekben határozza meg a méretet, ez a bizonytalanság eltűnik: a 4 cm az 4 cm marad, függetlenül attól, hogy a képernyő 96 vagy 220 DPI-s
A TXLSXImage geometria felület
A HotXLS-ben a beágyazott kép egy TXLSXImage objektum. Kánonikus tárolása két egész szám típusú mező: WidthEMU és HeightEMU, amelyek egy 1-alapú Row és Col helyhez vannak rögzítve (a bal felső cella, amelytől a kép lóg). A valós egységek tulajdonságai számított nézetek ezen EMU mezők felett, nem pedig külön állapotok — a WidthCM olvasása elosztja az EMU-t 360000-rel, az írása pedig megszorozza és visszakerekíti. Így minden megadott méret ugyanazon alatta lévő EMU érték más-más formájú felírása:
WidthInch/HeightInch— EMU ÷ 914400WidthCM/HeightCM— EMU ÷ 360000WidthPt/HeightPt— EMU ÷ 12700WidthEMU/HeightEMU— az egész szám alapú valóság forrása
A képet az AddImage(ARow, ACol, AData, AFormat) metódussal adhatja hozzá, átadva a nyers kódolt bájtokat és a TXLSXImageFormat-ot (xlsxImagePng, xlsxImageJpeg, xlsxImageGif vagy xlsxImageBmp); ez a munkalap Images gyűjteményének nullaalapú indexét adja vissza. Létezik az AddImageFromFile(ARow, ACol, AFileName) metódus is, amely a kiterjesztésből következtet a formátumra. Vegye figyelembe az index alapját: az AddImage nullaalapú értéket ad vissza és az Images[] tömb is nullaalapú, ami szándékos eltérést mutat az 1-alapú Cells[Row, Col] rácstól, így ne feltételezze, hogy a kettő megegyezik
var
Sheet: TXLSXWorksheet;
Img: TXLSXImage;
Idx: Integer;
begin
Sheet := Workbook.Sheets.Add('Images');
// Horgonyozzon egy PNG-t a 3. sorban, 2. oszlopban; az AddImage 0-alapú indexet ad vissza.
Idx := Sheet.AddImage(3, 2, LogoBytes, xlsxImagePng);
Img := Sheet.Images[Idx];
Img.WidthCM := 4.0; // 4 cm széles -> 1440000 EMU
Img.HeightCM := 3.0; // 3 cm magas -> 1080000 EMU
// Ugyanaz a geometria, más mértékegységekben visszaolvasva.
// Az Img.WidthPt most 113,39 pt, az Img.WidthInch pedig 1,5748 in.
end;
Egy frissen létrehozott kép alapértelmezetten 100×100 képpontos, azaz 952500 EMU négyzet méretű, ami nagyjából egy 2,54 cm-es doboznak felel meg 96 DPI mellett. Ez az alapértelmezés azért létezik, hogy a kép látható legyen még akkor is, ha elfelejti méretezni, de bármilyen valós elrendezéshez explicit fizikai méretet kell beállítania ahelyett, hogy a képpontokból származtatott alapértelmezésre hagyatkozna
Méretezés és a képarány-jelző
Ha a meglévő méretekhez képest szeretne átméretezni az abszolút cél helyett — például egy diagram képét a betöltött méret 60%-ára szeretné csökkenteni —, használja a Scale-t:
procedure Scale(APercent: Double; AKeepAspect: Boolean = True);
A APercent egy százalékos érték, ahol a 100 a változatlant jelenti, a 150 a másfélszeresére növeli, az 50 pedig felezi a méretet. Ha a AKeepAspect az alapértelmezett True értéken marad, a szélesség és a magasság is ugyanazzal a szorzóval szorzódik, így az arányok megmaradnak, és egy 4×3 cm-es képből 6×4,5 cm-es lesz a Scale(150) után. Ha False-t ad meg, csak a szélesség méreteződik — a magasság pontosan úgy marad, ahogy volt. Ez az aszimmetria szándékos: ha az egyik tengelyt függetlenül szeretné nyújtani, a megfelelő eszköz a kifejezett WidthCM/HeightCM beállítók használata, és a Scale képarány nélküli ága a szélesség önálló módosításának szűkebb esetére szolgál. Könnyű a Scale(150, False) hívást „mindkét irányú szabad nyújtásként” értelmezni és meglepődni, ezért használja a beállítókat (setters), ha valóban két független méretet szeretne megadni
Img.WidthCM := 4.0;
Img.HeightCM := 3.0;
Img.Scale(150); // arány rögzítve: most 6,0 x 4,5 cm
Img.Scale(100); // no-op, azonnal visszatér
Img.Scale(50, False); // csak szélesség: 3,0 cm széles, a magasság változatlanul 4,5 cm
Egy apró viselkedés, amit érdemes tudni: a Scale(100) lezárja a futást és visszatér anélkül, hogy bármelyik mezőhöz hozzányúlna, így biztonságosan hívható feltétel nélkül egy olyan ciklusban, ahol a százalékos érték 100 lehet. Mivel a geometriát egész számú EMU-ként tárolja a rendszer, minden beállító kerekít. A tört centimétereken keresztüli oda-vissza átváltás így az EMU egy töredékével eltolódhat — ez jóval a látható szint alatt van, de érdemes tudni róla, ha tesztekben pontos egyezést ellenőriz. A képpont-pontos vezérléshez állítsa be közvetlenül a WidthEMU és HeightEMU értékeket, teljesen kihagyva a mértékegység-átváltást
A geometria visszaolvasása
A képgyűjtemény lekérdezhető, ami fontos, ha betölt egy meglévő munkafüzetet, és meg kell vizsgálnia vagy módosítania kell a már ott lévő képeket ahelyett, hogy újakat adna hozzá. Az Images.Count felsorolja a munkalap összes képét, a Images[i] nullaalapúan indexeli őket, és a FindAt(ARow, ACol) visszaadja az adott cellához rögzített képet — vagy nil értéket ad vissza, ha nincs kép. Létezik továbbá az IndexOfCell az objektum helyett az index lekérésére, valamint a DeleteAt / DeleteInRange az eltávolításhoz
var
i: Integer;
Img: TXLSXImage;
begin
for i := 0 to Sheet.Images.Count - 1 do
begin
Img := Sheet.Images[i];
Writeln(Format('[%d] R%dC%d %.2f x %.2f cm (%d x %d EMU)',
[i, Img.Row, Img.Col, Img.WidthCM, Img.HeightCM,
Img.WidthEMU, Img.HeightEMU]));
end;
Img := Sheet.Images.FindAt(3, 2); // nil-ellenőrzés a használat előtt
if Img <> nil then
Img.Scale(80);
end;
Mivel a valós egységek tulajdonságai élő nézetek, egy más eszközből bizonyos EMU mérettel importált kép azonnal centiméterben jelenti a geometriáját — nincs szükség átváltási lépésre az Ön részéről. Ez természetes módon párosul a szélesebb rajzolási modellel; ha grafikonokat és alakzatokat is elhelyez a raszterképek mellett, a HotXLS diagramokról, képekről és rajzokról Delphiben szóló kísérő útmutató bemutatja azt a horgonyzási modellt, amelyen ezek az objektumok osztoznak
Metrikus oldalbeállítási margók
Ugyanez az EMU kontra valós mértékegységek közötti feszültség jelenik meg eggyel feljebb, a lap szintjén is. Az OOXML és az Excel a nyomtatási margókat hüvelykben tárolja, ami kellemetlen, ha a jelentéssablonjai milliméterben vannak megadva, mint az USA-n kívüli világ nagy részén. A v2.91.0 centiméteres burkolókat ad a hüvelykes margókhoz: MarginLeftCM, MarginRightCM, MarginTopCM, MarginBottomCM, MarginHeaderCM és MarginFooterCM. Mindegyik egy vékony kényelmi réteg a megfelelő hüvelykes tulajdonság felett, átváltva a pontos 1 hüvelyk = 2,54 cm arány szerint
Sheet.MarginLeftCM := 2.0; // 2 cm == 0,7874 hüvelyk
Sheet.MarginRightCM := 2.0;
Sheet.MarginTopCM := 2.5;
Sheet.MarginBottomCM := 2.5;
Sheet.MarginHeaderCM := 1.0;
Sheet.MarginFooterCM := 1.0;
A hüvelyk alapú tulajdonságok (MarginLeft és társai) megmaradnak kánonikus tárolásként, így keverheti a kettőt — beállíthat egy felső margót centiméterben, és visszaolvashatja hüvelykben, vagy fordítva —, és a lemezre írt fájl mindkét esetben azonos lesz. Az átváltás egyszerű szorzás 2,54-gyel, durva rácshoz való kerekítés nélkül, így a 2 cm pontosan 2 cm marad a teljes dupla pontosságig. Ez ugyanaz a metrikus kényelmi filozófia, mint a képgeometria esetében: a formátum birodalmi mértékegységet használ a színfalak mögött, és a könyvtár lehetővé teszi, hogy abban az egységben dolgozzon, amelyben a specifikációja íródott. A környező jelentés elrendezéséhez — címek, metaadat-blokkok, összegek — olvassa el a cellák egyesítése és jelentéssablon elrendezése a HotXLS-ben című cikket, amely ezeket a margókat egyesített tartományokkal és nyomtatási területtel együtt használja
Egy megjegyzés arról, hogy mit garantál és mit nem a geometria
A geometriai tulajdonságok szabályozzák a kép deklarált méretét a fájlban — azt a méretet, amelyen a szabályos feldolgozó leképezi azt. Nem mintázzák újra a kép bájtsorozatát; egy 8 cm-re méretezett 50×50 képpontos PNG felnagyítódik és blokkos (pixeles) lesz, pontosan úgy, mint az Excelben. A méretezés elrendezési művelet, nem pedig képfeldolgozási, ezért biztosítson elegendő forrásfelbontást a tervezett fizikai mérethez. A könyvtár nem kódolja át a formátumokat sem: a AddImage metódusnak átadott bájtok változatlanul kerülnek tárolásra és kiírásra az Ön által deklarált TXLSXImageFormat formátummal. Ha JPEG bájtokat ad át, de xlsxImagePng-ként címkézi meg őket, olyan fájlt hoz létre, amelyet az Excel nem tud megnyitni, ezért lehetőleg hagyja, hogy a AddImageFromFile maga következtessen a formátumra a kiterjesztésből
Mindez egyáltalán nem szokatlan, ha magáévá teszi a mögötte álló egyetlen gondolatot: az OOXML-ben a fizikai méret a valódi mennyiség, a képpontok pedig csupán annak származtatott, DPI-függő árnyékai. Határozza meg a képeket és margókat centiméterben, hüvelykben vagy pontokban, hagyja, hogy a HotXLS leképezze őket pontos EMU-ra, és a számlái, valamint jelentései azonos méretben fognak megjelenni minden gépen, amely megnyitja őket
Az itt leírt képgeometriai, méretezési és metrikus margó API-k a HotXLS Delphi spreadsheet component csomaggal érkeznek, amely Excel telepítése nélkül olvassa és írja az XLS és XLSX fájlokat Delphiből és C++Builderből