U zaglavlje generiranog računa ubacite logotip od 600×400 piksela, na vašem 96-DPI razvojnom monitoru izgleda ispravno, a tjedan dana kasnije kupac na prijenosniku visoke gustoće prijavi da se tiska veličine poštanske marke. Pikseli se nisu promijenili. Promijenila se pretpostavka da broj piksela znači fizičku veličinu, a u OOXML-u to nije slučaj. Slika u proračunskoj tablici nosi svoje dimenzije u EMU-u, i dok god razmišljate u EMU-u - ili u stvarnim jedinicama koje se na njega čisto mapiraju - raspored vam ovisi o bilo kojem DPI-u koji prikazni stroj slučajno pretpostavi
HotXLS je izvorna VCL spreadsheet komponenta za Delphi i C++Builder koja čita i piše XLS i XLSX bez Excela i bez bilo kakve COM ovisnosti. Od v2.91.0 XLSX objekt slike više vas ne tjera da ručno računate jedinice: uz sirovi EMU izlaže širinu i visinu u centimetrima, inčima i točkama, plus Scale metodu koja mijenja veličinu prema postotku uz opcionalno zaključavanje omjera stranica. Ovaj članak govori o tome što je EMU zapravo, zašto ga je DrawingML odabrao i kako koristiti novo geometrijsko sučelje da slike postavljate prema fizičkoj veličini, a ne prema broju piksela kojem ne možete vjerovati
Što je EMU i zašto ga DrawingML koristi
EMU znači English Metric Unit i osnovna je dužinska jedinica DrawingML-a, sloja za crtanje koji je zajednički cijeloj obitelji Office Open XML (ECMA-376, Part 1, §20). Jedan EMU definiran je tako da ih ima točno 914400 EMU po inču i 360000 EMU po centimetru. Te dvije konstante cijeli su razlog zašto jedinica postoji. 914400 je djeljiv s 2, 3, 4, 5, 6, 8, 9, 10, 12 i mnogim drugim brojevima; faktorizira se kao 26 × 32 × 52 × 127. Budući da je 1 inč = 2.54 cm točno, odabir jedinice djeljive i s 360000 i s čistim razlomkom od 914400 omogućuje formatu da izrazi inče, centimetre i točke kao cijele brojeve bez zaokruživanja na granici jedinice. Tamo gdje bi plutajuća vrijednost "1.27 cm" polako driftala, EMU pohrani 457200 i ostaje točan
Druga jedinica koja je ovdje važna jest točka. Tipografska točka iznosi 1/72 inča, pa ima 12700 EMU po točki (914400 / 72). Točke su način na koji Excel iznutra razmišlja o visini redaka, veličini fonta i marginama, zbog čega je izlaganje geometrije slike u točkama korisno kada želite da se slika poravna s metričkim tekstom, a ne s ispisanim ravnalom. HotXLS te četiri veze kodira kao konstante jedinica u biblioteci:
const
XlsxEmuPerInch = 914400; // 1 inch
XlsxEmuPerCm = 360000; // 1 centimetre
XlsxEmuPerPoint = 12700; // 1 point (1/72 inch)
XlsxEmuPerPixel = 9525; // 1 pixel at 96 DPI (914400 / 96)
Ta posljednja linija srž je buga s poštanskom markom. Piksel ima fizičku veličinu tek kada fiksirate DPI, a 9525 EMU je veličina piksela pri točno 96 DPI. Excelov zadani DPI za renderiranje iznosi 96, pa slika od 100 piksela završi na 100 × 9525 = 952500 EMU ≈ 2.54 cm u zadanom postavu - ali ništa u datoteci ne jamči da će potrošač koristiti 96. Autorirajte u stvarnim jedinicama i ta dvoznačnost nestaje: 4 cm je 4 cm bez obzira je li zaslon 96 ili 220 DPI
Surface geometrije TXLSXImage
Ugrađena slika u HotXLS-u je TXLSXImage. Njezina kanonska pohrana su dva cjelobrojna polja, WidthEMU i HeightEMU, usidrena na jedan-baziranom Row i Col (ćelija u gornjem lijevom kutu iz koje slika visi). Svojstva za stvarne jedinice izračunati su pogledi nad tim EMU poljima, a ne zasebno stanje - čitanje WidthCM dijeli EMU s 360000, a zapisivanje ga množi i vraća natrag zaokruživanjem. Dakle, svaku dimenziju koju postavite samo je drukčiji zapis iste temeljne EMU vrijednosti:
WidthInch/HeightInch— EMU ÷ 914400WidthCM/HeightCM— EMU ÷ 360000WidthPt/HeightPt— EMU ÷ 12700WidthEMU/HeightEMU— izvor istine kao cijeli broj
Sliku dodajete s AddImage(ARow, ACol, AData, AFormat), prosljeđujući sirove kodirane bajtove i TXLSXImageFormat (xlsxImagePng, xlsxImageJpeg, xlsxImageGif, ili xlsxImageBmp); vraća zero-based indeks u kolekciji Images. Postoji i AddImageFromFile(ARow, ACol, AFileName), koja iz nastavka datoteke zaključuje format. Primijetite bazu indeksa: AddImage vraća zero-based, a Images[] je zero-based, što je namjerni kontrast prema jednom-baziranom Cells[Row, Col] gridu, pa nemojte pretpostaviti da su ta dva ista
var
Sheet: TXLSXWorksheet;
Img: TXLSXImage;
Idx: Integer;
begin
Sheet := Workbook.Sheets.Add('Images');
// Anchor a PNG at row 3, column 2; AddImage returns a 0-based index.
Idx := Sheet.AddImage(3, 2, LogoBytes, xlsxImagePng);
Img := Sheet.Images[Idx];
Img.WidthCM := 4.0; // 4 cm wide -> 1440000 EMU
Img.HeightCM := 3.0; // 3 cm tall -> 1080000 EMU
// Same geometry, read back in other units.
// Img.WidthPt is now 113.39 pt, Img.WidthInch is 1.5748 in.
end;
Novostvorena slika zadano je 100×100 piksela, tj. 952500 EMU kvadrat, otprilike kutija od 2.54 cm pri 96 DPI. Ta zadana vrijednost postoji kako bi slika bila vidljiva i ako joj zaboravite postaviti veličinu, ali za svaki stvarni raspored trebate eksplicitnu fizičku veličinu umjesto da se oslanjate na zadanu vrijednost izvedenu iz piksela
Skaliranje i zastavica omjera stranica
Kad želite mijenjati veličinu relativno prema trenutačnim dimenzijama umjesto prema apsolutnom cilju - recimo, smanjiti sliku grafikona na 60% od veličine u kojoj je uvezena - koristite Scale:
procedure Scale(APercent: Double; AKeepAspect: Boolean = True);
APercentAPercentAKeepAspectAKeepAspectTrueTrueScale(150)Scale(150)FalseFalseWidthCMWidthCMHeightCM/ScaleScaleScale(150, False)Scale(150, False)
Img.WidthCM := 4.0;
Img.HeightCM := 3.0;
Img.Scale(150); // aspect locked: now 6.0 x 4.5 cm
Img.Scale(100); // no-op, returns immediately
Img.Scale(50, False); // width only: 3.0 cm wide, height unchanged at 4.5 cm
Scale(100)Scale(100)Scale(100)integerintegerWidthEMUWidthEMUHeightEMUHeightEMU
Čitanje geometrije natrag
Kolekcija slika može se ispitivati, što je važno kada učitate postojeću radnu knjigu i trebate pregledati ili prilagoditi ono što je već ondje umjesto onoga što ste tek dodali. Images.CountImages.CountImages[i] indeksira ih zero-based, a FindAt(ARow, ACol) vraća sliku usidrenu za određenu ćeliju - ili nil ako je nema. Postoji i IndexOfCell za indeks umjesto objekta, te DeleteAt / DeleteInRange za uklanjanje
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-check before use
if Img <> nil then
Img.Scale(80);
end;
Budući da su svojstva stvarnih jedinica živi prikazi, slika uvezena s nekom EMU veličinom iz drugog alata odmah prijavljuje svoju geometriju u centimetrima - bez ikakvog koraka pretvorbe s vaše strane. To se prirodno uklapa u širi model crtanja; ako postavljate i grafikone i oblike uz rasterske slike, prateći vodič o HotXLS grafikonima, slikama i Excel crtežima u Delphiju pokriva model usidrenja koji ti objekti dijele
Metričke margine stranice
Ista napetost između EMU-a i stvarnih jedinica pojavljuje se jednu razinu dalje, na stranici. OOXML i Excel pohranjuju margine ispisa u inčima, što je nezgodno ako su vaši predlošci izvještaja zadani u milimetrima kao u većini svijeta izvan SAD-a. v2.91.0 dodaje centimetarske omotače oko inčnih margina: MarginLeftCM, MarginRightCM, MarginTopCM, MarginBottomCM, MarginHeaderCM, i MarginFooterCM. Svaki je tanka pogodnost nad odgovarajućim inčnim svojstvom, uz pretvorbu točno po omjeru 1 inč = 2.54 cm
Sheet.MarginLeftCM := 2.0; // 2 cm == 0.7874 inch
Sheet.MarginRightCM := 2.0;
Sheet.MarginTopCM := 2.5;
Sheet.MarginBottomCM := 2.5;
Sheet.MarginHeaderCM := 1.0;
Sheet.MarginFooterCM := 1.0;
Inčna svojstva (MarginLeft i ostala) ostaju kanonska pohrana, pa možete miješati oba - postaviti gornju marginu u centimetrima i vratiti je u inčima, ili obrnuto - a datoteka zapisana na disk identična je u oba slučaja. Pretvorba je običan množitelj s 2.54, bez zaokruživanja na grubu mrežu, pa 2 cm ostaje 2 cm do pune double preciznosti. To je ista filozofija metričke pogodnosti kao i geometrija slike: format iznutra govori imperijalno, a biblioteka vam dopušta da pišete u jedinici u kojoj je napisana vaša specifikacija. Za raspoređivanje okolnog izvještaja - naslovi, blokovi metapodataka, zbrojevi - pogledajte spojene ćelije i raspored predloška izvještaja u HotXLS-u, koji ove margine koristi zajedno sa spojenim rasponima i područjem ispisa
Napomena o tome što geometrija jamči, a što ne
Geometrijska svojstva kontroliraju deklariranu veličinu slike u datoteci - veličinu na kojoj će je usklađeni potrošač renderirati. Ona ne resamplira bajtove slike; PNG od 50×50 piksela postavljen na 8 cm povećat će se i izgledati blokasto, baš kao u Excelu. Veličina je operacija rasporeda, a ne obrade slike, pa sliku hranite dovoljno visokom izvorne rezolucijom za fizičku veličinu koju želite. Biblioteka također ne ponovno kodira formate: bajtovi koje proslijedite u AddImage se pohranjuju i zapisuju takvi kakvi jesu, uz TXLSXImageFormat koji ste deklarirali. Proslijedite JPEG bajtove, ali ih označite kao xlsxImagePng i dobit ćete datoteku koju Excel ne može otvoriti, pa pustite AddImageFromFile da format zaključi iz nastavka kad god možete
Ništa od ovoga nije egzotično kad usvojite jednu ideju ispod svega: u OOXML-u je fizička veličina stvarna veličina, a pikseli su izvedena, DPI-ovisna sjena te veličine. Pišite slike i margine u centimetrima, inčima ili točkama, pustite HotXLS da ih preslika u točan EMU, i vaši će računi i izvještaji ispisivati istu veličinu na svakom računalu koje ih otvori
API za geometriju slika, skaliranje i metričke margine opisan ovdje dolazi s HotXLS Delphi spreadsheet component, koja čita i piše XLS i XLSX iz Delphija i C++Buildera bez potrebe za instaliranim Excelom