Teknik Makale

Delphi'de HotXLS Görüntü Geometrisi: EMU, cm ve Ölçekleme

Oluşturulan bir faturanın başlığına 600×400 piksellik bir logo yerleştirirsiniz, 96 DPI geliştirme monitörünüzde doğru görünür ve bir hafta sonra yüksek DPI dizüstü kullanan bir müşteri bunun posta pulu kadar küçük yazdırıldığını bildirir. Pikseller hiç değişmedi. Değişen şey, piksel sayısının fiziksel boyut anlamına geldiği varsayımıdır; OOXML'de bu doğru değildir. Bir spreadsheet görseli boyutlarını EMU cinsinden taşır ve siz EMU ile ya da ona temiz biçimde eşlenen gerçek dünya birimleriyle düşünmeye başlayana kadar düzeniniz, işleme yapan makinenin varsaydığı DPI değerinin insafına kalır

HotXLS, Excel veya herhangi bir COM bağımlılığı olmadan XLS ve XLSX okuyup yazan, Delphi ve C++Builder için yerel bir VCL spreadsheet component'tir. v2.91.0 itibarıyla XLSX görüntü nesnesi artık birim aritmetiğini elle yapmanızı istemiyor: ham EMU değerinin yanında santimetre, inç ve punto cinsinden genişlik ile yükseklik ve ayrıca Scale adlı, isteğe bağlı en-boy oranı kilidiyle yüzdeye göre yeniden boyutlandıran bir yöntem sunuyor. Bu makale, EMU'nun gerçekte ne olduğunu, DrawingML'in neden bunu seçtiğini ve yeni geometri yüzeyini, güvenemeyeceğiniz bir piksel sayısıyla değil fiziksel boyutla resim yerleştirmek için nasıl kullanacağınızı anlatır

EMU nedir ve DrawingML bunu neden kullanır

EMU, English Metric Unit anlamına gelir ve Office Open XML ailesinin tamamında paylaşılan çizim katmanı olan DrawingML'in temel uzunluk birimidir (ECMA-376, Part 1, §20). Bir EMU, tam olarak inç başına 914400 EMU ve santimetre başına 360000 EMU olacak şekilde tanımlanır. Bu iki sabit, birimin var olmasının bütün nedenidir. 914400 sayısı 2, 3, 4, 5, 6, 8, 9, 10, 12 ve daha fazlasına bölünebilir; çarpanlarına ayrıldığında 26 × 32 × 52 × 127 eder. 1 inç = 2.54 cm tam eşit olduğu için, hem 360000'e hem de 914400'ün temiz bir kesrine bölünebilen bir birim seçmek, biçimin inçleri, santimetreleri ve puntoları birim sınırında yuvarlama olmadan tamsayılar olarak ifade etmesini sağlar. Kayan noktalı "1.27 cm" değeri kayma yaratırken, EMU 457200 olarak saklar ve tam kalır

Burada önemli olan diğer birim puntodur. Tipografik bir punto 1/72 inçtir, dolayısıyla punto başına 12700 EMU vardır (914400 / 72). Punto, Excel'in satır yükseklikleri, yazı tipi boyutları ve kenar boşlukları için arka planda düşündüğü birimdir; bu yüzden görüntü geometrisini punto cinsinden göstermek, resmi basılı cetvelle değil metin ölçüleriyle hizalamak istediğinizde yararlıdır. HotXLS, bu dört ilişkinin tamamını kütüphane içinde birim sabitleri olarak kodlar:

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)

Son satır, posta pulu boyutu hatasının özüdür. Piksel, ancak bir DPI belirlediğinizde fiziksel boyuta sahip olur ve 9525 EMU, özellikle 96 DPI'ta bir pikselin boyutudur. Excel'in varsayılan işleme DPI değeri 96'dır; bu yüzden 100 piksellik bir görsel, varsayılan kurulumda 100 × 9525 = 952500 EMU ≈ 2.54 cm olur, ama dosyada tüketicinin 96 kullanacağını garanti eden hiçbir şey yoktur. Gerçek birimlerle yazarsanız bu belirsizlik kaybolur: ekran 96 ya da 220 DPI olsa da 4 cm yine 4 cm'dir

TXLSXImage geometri yüzeyi

HotXLS içindeki gömülü bir resim bir TXLSXImage nesnesidir. Kanonik saklama biçimi, WidthEMU ve HeightEMU adlı iki tamsayı alanıdır; bunlar 1 tabanlı Row ve Col değerine, yani resmin bağlandığı sol üst hücreye sabitlenir. Gerçek birim özellikleri bu EMU alanları üzerindeki hesaplanmış görünümlerdir, ayrı bir durum değildir; WidthCM okunduğunda EMU değeri 360000'e bölünür, yazıldığında ise çarpılıp yeniden yuvarlanır. Yani ayarladığınız her boyut, alttaki aynı EMU değerinin başka bir yazımıdır:

  • WidthInch / HeightInch — EMU ÷ 914400
  • WidthCM / HeightCM — EMU ÷ 360000
  • WidthPt / HeightPt — EMU ÷ 12700
  • WidthEMU / HeightEMU — doğruluğun tamsayı kaynağı

Bir görseli AddImage(ARow, ACol, AData, AFormat) ile eklersiniz; ham kodlanmış baytları ve bir TXLSXImageFormat (xlsxImagePng, xlsxImageJpeg, xlsxImageGif ya da xlsxImageBmp) geçirirsiniz ve yöntem, çalışma sayfasının sıfır tabanlı koleksiyonundaki Images dizini döndürür. Ayrıca dosya uzantısından biçimi çıkaran AddImageFromFile(ARow, ACol, AFileName) da vardır. Dizin tabanına dikkat edin: AddImage sıfır tabanlı döner ve Images[] de sıfır tabanlıdır; bu, 1 tabanlı Cells[Row, Col] ızgarasıyla bilinçli bir karşıtlıktır, dolayısıyla ikisinin aynı olduğunu varsaymayın

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;

Yeni oluşturulan bir görsel varsayılan olarak 100×100 piksel, yani 952500 EMU kare boyutuyla başlar; bu da 96 DPI'ta yaklaşık 2.54 cm'lik bir kutuya denk gelir. Bu varsayılan, siz boyut vermeyi unutursanız görselin görünür kalması için vardır; ama gerçek bir düzen için piksel türevli varsayılanı kullanmak yerine açık bir fiziksel boyut ayarlamalısınız

Ölçekleme ve en-boy oranı bayrağı

Mutlak bir hedefe değil de mevcut boyutlara göre yeniden boyutlandırmak istiyorsanız — örneğin içe aktarıldığı boyutun yüzde 60'ına küçültmek gibi — Scale kullanın:

procedure Scale(APercent: Double; AKeepAspect: Boolean = True);

APercent, yüzdelik değerdir; 100 değişiklik yok demektir, 150 yarı oranında büyütür, 50 yarıya indirir. AKeepAspect varsayılan True değerindeyken hem genişlik hem yükseklik aynı katsayıyla çarpılır; böylece oranlar korunur ve 4×3 cm'lik görsel, Scale(150) işleminden sonra 6×4.5 cm olur. False geçirirseniz yalnızca genişlik ölçeklenir; yükseklik olduğu gibi bırakılır. Bu asimetri bilinçlidir: tek ekseni bağımsız esnetmek istediğinizde doğru araç açık WidthCM/HeightCM ayarlayıcılarıdır ve Scale yönteminin en-boy oranı dışı dalı, yalnızca genişliği ayarlamanın daha dar kullanım durumu içindir. Scale(150, False) ifadesini "her ikisini de serbestçe esnet" diye okumak kolaydır ve sürpriz yaşatır; gerçekten iki bağımsız boyut istediğinizde ayarlayıcıları kullanın

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

Bilmeniz gereken küçük bir davranış: Scale(100), hiçbir alana dokunmadan erkenden döner; bu yüzden yüzde değeri 100 çıkabilecek bir döngü içinde bunu koşulsuz çağırmak güvenlidir. Geometri tamsayı EMU olarak saklandığı için her ayarlayıcı yuvarlama yapar. Bu nedenle kesirli santimetreler üzerinden round-trip yapmak, EMU'nun küçük bir kesri kadar kayma yaratabilir; görünür bir şey olmaktan çok uzaktır, ama testte tam eşitlik bekliyorsanız bilinmeye değerdir. Piksel düzeyinde tam denetim istiyorsanız WidthEMU ile HeightEMU değerlerini doğrudan ayarlayın ve birim dönüşümünü tamamen atlayın

Geometriyi geri okuma

Görüntü koleksiyonu sorgulanabilir yapıdadır; bu, mevcut bir çalışma kitabını yüklediğinizde yeni eklediğiniz şeyi değil, zaten orada olanı incelemeniz ya da ayarlamanız gerektiğinde önemlidir. Images.Count, sayfadaki her resmi numaralandırır; Images[i] bunları sıfır tabanlı dizinler ve FindAt(ARow, ACol) belirli bir hücreye sabitlenmiş görseli ya da yoksa nil döndürür. Nesnenin kendisi yerine dizin için IndexOfCell ve kaldırma için DeleteAt / DeleteInRange da vardır

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;

Gerçek birim özellikleri canlı görünümler olduğu için, başka bir araçtan belirli bir EMU boyutuyla içe aktarılan resim santimetre cinsinden geometrisini anında bildirir; sizin ayrıca dönüştürme yapmanız gerekmez. Bu, daha geniş çizim modeliyle doğal biçimde eşleşir; grafikler, şekiller ve raster görselleri birlikte yerleştiriyorsanız, Delphi'de HotXLS chart, image ve Excel drawing işlemleri hakkındaki eşlikçi kılavuz, bu nesnelerin paylaştığı sabitleme modelini anlatır

Metrik sayfa ayarı kenar boşlukları

EMU ile gerçek birimler arasındaki aynı gerilim, bir seviye dışarıda, sayfanın kendisinde de görülür. OOXML ve Excel, yazdırma kenar boşluklarını inç olarak saklar; bu da ABD dışındaki dünyanın çoğunda olduğu gibi rapor şablonları milimetre ile tanımlanıyorsa elverişsizdir. v2.91.0, inç kenar boşluklarının üzerine santimetre sarmalayıcıları ekler: MarginLeftCM, MarginRightCM, MarginTopCM, MarginBottomCM, MarginHeaderCM ve MarginFooterCM. Her biri karşılık gelen inç özelliğinin ince bir kolaylık katmanıdır ve tam 1 inç = 2.54 cm oranıyla dönüşüm yapar

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;

İnç özellikleri (MarginLeft ve diğerleri) kanonik saklama olarak kalır; bu yüzden ikisini karıştırabilirsiniz — üst kenar boşluğunu santimetre olarak ayarlayıp inç olarak geri okuyabilir ya da tersini yapabilirsiniz — ve diske yazılan dosya her iki durumda da aynıdır. Dönüşüm sadece 2.54 ile düz çarpmadır, kaba bir ızgaraya yuvarlama yapılmaz; bu yüzden 2 cm, tam double hassasiyetiyle 2 cm kalır. Bu, görüntü geometrisindekiyle aynı metrik kolaylık yaklaşımıdır: biçim arka planda emperyal birimlerle konuşur, kütüphane ise tanımınız hangi birimde yazılmışsa onunla yazmanıza izin verir. Çevredeki raporu — başlıklar, meta veri blokları, toplamlar — yerleştirmek için HotXLS içinde birleştirilmiş hücreler ve rapor şablonu düzeni kılavuzuna bakın; bu kılavuz, bu kenar boşluklarını birleştirilmiş aralıklar ve yazdırma alanıyla birlikte kullanır

Geometrinin neyi garanti ettiği ve neyi etmediğine dair bir not

Geometri özellikleri, dosyadaki görselin bildirilmiş boyutunu denetler; yani uyumlu bir tüketicinin görseli hangi boyutta çizeceğini. Görsel baytlarını yeniden örneklemez; 8 cm'ye ayarlanmış 50×50 piksellik PNG ölçeklenir ve Excel'de olacağı gibi bloklu görünür. Boyutlandırma bir görüntü işleme değil düzen işlemidir; bu yüzden hedeflediğiniz fiziksel boyut için resme yeterli kaynak çözünürlüğü sağlayın. Kütüphane biçimleri yeniden kodlamaz da: AddImage içine verdiğiniz baytlar, ilan ettiğiniz TXLSXImageFormat ile aynen saklanır ve yazılır. JPEG baytları verip bunları xlsxImagePng olarak etiketlerseniz, Excel'in açamayacağı bir dosya üretirsiniz; bu yüzden mümkün olduğunda AddImageFromFile yönteminin uzantıdan biçimi çıkarmasına izin verin

Altındaki tek fikri içselleştirdiğinizde bunların hiçbiri egzotik değildir: OOXML'de gerçek büyüklük fiziksel boyuttur ve pikseller bunun DPI'ye bağlı türetilmiş gölgesidir. Görselleri ve kenar boşluklarını santimetre, inç veya punto ile tanımlayın, HotXLS'in bunları tam EMU değerlerine dönüştürmesine izin verin ve faturalarınız ile raporlarınız her makinede aynı boyutta yazdırılsın

Burada açıklanan görüntü geometrisi, ölçekleme ve metrik kenar boşluğu API'leri, HotXLS Delphi spreadsheet component ile birlikte sunulur; bu bileşen, Excel kurulumu gerektirmeden Delphi ve C++Builder'dan XLS ve XLSX okuyup yazar