Kad stavite logo od 600×400 piksela u zaglavlje generisanog računa, na vašem razvojnom monitoru sa 96 DPI izgleda dobro, a nedelju dana kasnije kupac na laptopu sa visokim DPI prijavljuje da se štampa u veličini poštanske markice. Pikseli se nisu promenili. Promenila se pretpostavka da broj piksela znači fizičku veličinu, a u OOXML-u to ne znači. Slika u tabeli nosi svoje dimenzije u EMU, i dok ne razmišljate u EMU - ili u stvarnim jedinicama koje se na njih čisto mapiraju - vaš raspored zavisi od toga šta god render mašina odluči da pretpostavi o DPI-ju
HotXLS je izvorna VCL spreadsheet komponenta za Delphi i C++Builder koja čita i upisuje XLS i XLSX bez Excela ili bilo kakve COM zavisnosti. Od verzije v2.91.0 objekat XLSX slike više ne tera vas da ručno računate jedinice: uz sirove EMU vrednosti izlaže širinu i visinu u centimetrima, inčima i poentama, plus Scale metodu koja menja veličinu za procenat uz opcionalno zaključavanje odnosa stranica. Ovaj članak objašnjava šta je EMU, zašto ga je DrawingML izabrao i kako da novi sloj geometrije koristite za postavljanje slika po fizičkoj veličini umesto po broju piksela kojem ne možete verovati
Šta je EMU i zašto DrawingML koristi jednu takvu jedinicu
EMU znači English Metric Unit i predstavlja osnovnu dužinsku jedinicu DrawingML-a, sloja za crtanje koji se deli kroz celu Office Open XML porodicu (ECMA-376, deo 1, §20). Jedan EMU je definisan tako da postoji tačno 914400 EMU po inču i 360000 EMU po centimetru. Te dve konstante su ceo razlog zašto jedinica postoji. 914400 je deljiv sa 2, 3, 4, 5, 6, 8, 9, 10, 12 i mnogim drugim brojevima; faktoriše se kao 26 × 32 × 52 × 127. Pošto je 1 inč = 2,54 cm tačno, izbor jedinice deljive i sa 360000 i sa čistim delom od 914400 omogućava formatu da izrazi inče, centimetre i poente kao celobrojne vrednosti bez zaokruživanja na granici jedinice. Tamo gde bi plutajuća vrednost "1,27 cm" vremenom odstupala, EMU čuva 457200 i ostaje tačan
Druga jedinica koja je ovde važna jeste poen. Tipografski poen je 1/72 inča, pa postoji 12700 EMU po poenu (914400 / 72). Poeni su način na koji Excel interno razmišlja o visini redova, veličini fonta i marginama, zbog čega je izlaganje geometrije slike u poentama korisno kada želite da se slika poravna sa metrikom teksta, a ne sa lenjirom na papiru. HotXLS sve četiri veze kodira kao konstantne jedinice 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)
Poslednja rečenica je suština buba sa markicom. Piksel ima fizičku veličinu tek kada fiksirate DPI, a 9525 EMU je veličina piksela pri tačno 96 DPI. Excelov podrazumevani DPI za prikaz je 96, pa slika od 100 piksela završava na 100 × 9525 = 952500 EMU ≈ 2,54 cm u podrazumevanoj postavci - ali ništa u fajlu ne garantuje da će potrošač koristiti 96. Pišite u stvarnim jedinicama i ta dvosmislenost nestaje: 4 cm je 4 cm bez obzira na to da li je ekran na 96 ili 220 DPI
Sloj geometrije TXLSXImage
U HotXLS-u ugrađena slika je TXLSXImage. Njeno kanonsko skladište čine dva celobrojna polja, WidthEMU i HeightEMU, usidrena uz jedan-bazirani Row i Col (ćeliju gore levo od koje slika visi). Svojstva u stvarnim jedinicama su izvedeni prikazi nad tim EMU poljima, a ne posebno stanje - pri čitanju WidthCM deli EMU sa 360000, a pri upisu ga množi i vraća zaokruženo. Zato je svaka dimenzija koju postavite samo drugačiji zapis iste osnovne EMU vrednosti:
WidthInch/HeightInch- EMU ÷ 914400WidthCM/HeightCM- EMU ÷ 360000WidthPt/HeightPt- EMU ÷ 12700WidthEMU/HeightEMU- konačni izvor istine
Sliku dodajete pomoću AddImage(ARow, ACol, AData, AFormat), prosleđujući sirove enkodirane bajtove i TXLSXImageFormat (xlsxImagePng, xlsxImageJpeg, xlsxImageGif, ili xlsxImageBmp); vraća nultobazirani indeks u kolekciji radnog lista Images kolekcije. Postoji i AddImageFromFile(ARow, ACol, AFileName), koja zaključuje format na osnovu ekstenzije fajla. Obratite pažnju na bazu indeksa: AddImage vraća indeks sa nulom kao početkom i Images[] je nultobaziran, što je namerna suprotnost prema Cells[Row, Col] mreži, pa nemojte pretpostaviti da se to dvoje poklapa
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;
Novo napravljena slika podrazumevano ima 100×100 piksela, odnosno kvadrat od 952500 EMU, otprilike kutiju od 2,54 cm pri 96 DPI. Ta podrazumevana vrednost postoji da bi slika bila vidljiva čak i ako zaboravite da joj zadate veličinu, ali za svaki ozbiljan raspored treba da postavite izričitu fizičku veličinu umesto da se oslonite na podrazumevanu vrednost izvedenu iz piksela
Skaliranje i zastavica odnosa stranica
Kada želite da menjate veličinu u odnosu na trenutne dimenzije umesto u odnosu na apsolutni cilj - recimo, da smanjite sliku grafikona na 60% od veličine na kojoj je uvezena - koristite Scale:
procedure Scale(APercent: Double; AKeepAspect: Boolean = True);
APercent je procenat u kome 100 znači bez promene, 150 povećava za polovinu, 50 prepolovljava. Sa AKeepAspect u podrazumevanoj vrednosti True, i širina i visina se množe istim faktorom, pa proporcije ostaju iste i slika od 4×3 cm postaje 6×4,5 cm nakon Scale(150).False i tada se menja samo širina - visina ostaje potpuno ista. Ta asimetrija je namerna: kada želite da nezavisno razvlačite jednu osu, pravi alat su eksplicitni WidthCM/HeightCM setteri širine i visine, a ne-aspektna grana Scale postoji za uži slučaj prilagođavanja samo širine. Lako je pročitati Scale(150, False) kao "razvuci oba slobodno" i iznenaditi se, zato posegnite za setterima kada zaista mislite na dve nezavisne dimenzije
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
Još jedno ponašanje koje vredi znati: Scale(100) prekida tok i vraća se bez dodirivanja ijednog polja, pa ga je bezbedno pozivati bez uslova u petlji u kojoj procenat može biti 100. A pošto je geometrija sačuvana kao celobrojni EMU, svaki setter zaokružuje. Povratno prevođenje kroz razlomljene centimetre zato može da sklizne za delić EMU-a - daleko ispod bilo čega vidljivog, ali vredno znanja ako ikada proveravate tačnu jednakost u testu. Za piksel-po-piksel kontrolu postavite WidthEMU i HeightEMU direktno i preskočite potpuno konverziju jedinica
Čitanje geometrije unazad
Kolekcija slika može da se upituje, što je važno kada učitate postojeću radnu svesku i treba da pregledate ili prilagodite ono što je već tu, a ne samo ono što ste upravo dodali. Images.Count nabraja svaku sliku na radnom listu, Images[i] indeksira ih nultobazirano i FindAt(ARow, ACol) vraća sliku usidrenu na određenu ćeliju - ili nil ako nijedna ne postoji. Postoji i IndexOfCell za indeks umesto za objekat, i 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;
Pošto su svojstva u stvarnim jedinicama živi prikazi, slika uvezena iz drugog alata u nekoj EMU veličini odmah prijavljuje svoje dimenzije u centimetrima - bez ikakvog koraka konverzije sa vaše strane. To se prirodno uklapa u širi model crtanja; ako postavljate i grafikone i oblike, a ne samo rasterske slike, prateći vodič o HotXLS grafikoni, slike i Excel crteži u Delphiju pokriva model sidrenja koji ti objekti dele
Metričke margine za podešavanje stranice
Ista napetost između EMU i stvarnih jedinica pojavljuje se jedan nivo dalje, na stranici. OOXML i Excel čuvaju margine za štampu u inčima, što je nezgodno ako su vam šabloni izveštaja zadati u milimetrima kao u većini sveta izvan SAD. v2.91.0 dodaje omotače u centimetrima preko inč margina: MarginLeftCM, MarginRightCM, MarginTopCM, MarginBottomCM, MarginHeaderCM, i MarginFooterCM. Svaki je tanka pogodnost nad odgovarajućim inčnim svojstvom, uz pretvaranje po tačnom odnosu 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;
Svojstva u inčima (MarginLeft i prateća) ostaju kanonsko skladište, pa možete mešati ta dva - postavite gornju marginu u centimetrima i pročitajte je nazad u inčima, ili obrnuto - a fajl upisan na disk biće identičan u oba slučaja. Pretvaranje je obično množenje sa 2,54, bez zaokruživanja na grubu mrežu, tako da 2 cm ostaje 2 cm sa punom dvostrukom preciznošću. To je ista filozofija metričke pogodnosti kao i geometrija slike: format govori imperijalno ispod haube, a biblioteka vam dopušta da pišete u jedinici u kojoj je vaša specifikacija napisana. Za raspored okolnog izveštaja - naslova, blokova metapodataka i zbirova - pogledajte spajanje ćelija i raspored šablona izveštaja u HotXLS-u, koji koristi ove margine zajedno sa spojenim opsezima i oblašću štampe
Napomena o tome šta geometrija garantuje, a šta ne
Svojstva geometrije kontrolišu deklarisanu veličinu slike u fajlu - veličinu na kojoj će je usklađen potrošač prikazati. Ona ne ponovo uzorkuju bajtove slike; PNG od 50×50 piksela postavljen na 8 cm uvećaće se i izgledati blokasto, baš kao u Excelu. Određivanje veličine je operacija rasporeda, a ne obrade slike, pa obezbedite slici dovoljno izvorne rezolucije za fizičku veličinu koju planirate. Biblioteka takođe ne ponovo enkodira formate: bajtovi koje prosledite u AddImage se skladište i prosleđuju dalje bez izmene, uz TXLSXImageFormat koji navedete. Ako prosledite JPEG bajtove, a označite ih kao xlsxImagePng, dobićete fajl koji Excel ne može da otvori, pa pustite da AddImageFromFile zaključi format iz ekstenzije kad god možete
Ništa od ovoga nije egzotično kada usvojite jednu ideju ispod svega: u OOXML-u fizička veličina je stvarna količina, a pikseli su izvedena senka zavisna od DPI-ja. Pišite slike i margine u centimetrima, inčima ili poentama, pustite da ih HotXLS preslika u tačne EMU vrednosti, i vaši računi i izveštaji štampaće se u istoj veličini na svakoj mašini koja ih otvori
API-je geometrije slike, skaliranja i metričkih margina opisane ovde isporučuju se uz komponentu HotXLS Delphi spreadsheet, koja čita i upisuje XLS i XLSX iz Delphija i C++Buildera bez potrebe za instaliranim Excelom