HotXLS, bir resmin gerçek piksel geometrisini, herhangi bir çizim geometrisi yazılmadan önce görüntü dosyasının kendi başlığından okur. lxImage.pas içindeki GetImageSize, dosya uzantısına göre GetJpegSize, GetGifSize, GetBmpSize, GetPngSize ya da GetWmfEmfSize öğesine dağıtır, pikselleri 0.75 çarpanıyla noktaya çevirir ve gerçekten söyleyemediğinde -1 döndürür
Son madde, doğru hale gelmesi en uzun süren kısımdı. Başarısız olamayan bir boyut okuyucusu yalan söyleyen bir boyut okuyucusudur ve görüntü geometrisi hakkındaki bir yalan ekleme zamanında yüzeye çıkmaz. Haftalar sonra yüzeye çıkar; biri çalışma kitabını açtığında ve şirket logosu ezilmiş bir dikdörtgen olduğunda
Bir resmin çizime girmeden önce gerçek geometrisine neden ihtiyacı var?
Çünkü elektronik tablo formatı kapsamı saklar, görüntüyü değil. AddPicture ya da Images.AddFromFile bir resmi yerleştirdiğinde, çizim parçası şekil için açık bir genişlik ve yükseklik kaydeder — DrawingML'de bu, ECMA-376 Bölüm 1 §20.4'e göre İngiliz Metrik Birimleri cinsinden a:ext elemanının cx ve cy öznitelikleridir. O kayıtta "görüntünün doğal boyutunu kullan" diye bir şey yoktur, o yüzden doğal boyut bir yerden gelmek zorundadır ve var olduğu tek yer görüntü baytlarıdır. Üç kat yanlış al ve çalışma kitabını açan her uygulamada, sonsuza dek resim üç kat esner. O piksel boyutlarından EMU'ya birim dönüşümü ayrı bir endişedir ve EMU birimleri ve ölçekleme makalesinde ele alınır; bu yazı ondan önceki adım hakkında, piksel sayılarının hiç elde edildiği adım. Bir okuyucu başarısız olduğunda fallback 100'e 100'dür ve bu sayı bilinçli olarak, apaçık biçimde yanlıştır. Makul bir boyut tahmini değil, "bu dosyayı kimse okuyamadı" anlamına gelen bir işarettir ve makul görünen yanlış bir sayıdan çok daha iyidir, çünkü kullanıcı onu hemen rapor eder
GIF: hiç ayrıştırılmamış tanımlayıcı
GetGifSize yenidir ve o var olmadan önce bir çalışma sayfasına eklenen her GIF tam olarak 100'e 100 çıkıyordu — hiç GIF dalı yoktu, o yüzden uzantı GetImageSize öğesinin varsayılan koluna düşüyor ve -1 döndürüyordu. Bu, setteki en ucuz ayrıştırılacak başlıktır; ihmalin biraz utandırıcı ve düzeltmesinin çok kolay olmasının nedeni bu. GIF89a şartnamesi Logical Screen Descriptor'ı altı baytlık imzadan hemen sonraya koyar ve §18 ilk iki alanını mantıksal ekran genişliği ve yüksekliği olarak tanımlar; her biri küçük endian saklanan işaretsiz 16 bitlik bir değerdir. Dosya terimleriyle bu, genişlik için bayt 6 ve 7, yükseklik için bayt 8 ve 9'dur; o yüzden tek bir on baytlık okuma imza kontrolünü ve iki boyutu da kapsar
function GetGifSize(const AFileName: WideString; var AWidth, AHeight: Double): Integer;
var
f: TWFileStream;
Header: array [0.. 9] of Byte;
wWidth, wHeight: Word;
begin
Result:= - 1;
AWidth:= 100;
AHeight:= 100;
f:= TWFileStream.Create(AFileName, fmOpenRead);
try
// "GIF8" imzası, ardından genişlik ve yüksekliği 6 ve 8 ofsetlerinde
// küçük endian sözcükler olarak tutan Logical Screen Descriptor
if (f.Read(Header[0], SizeOf(Header))= SizeOf(Header))and
(Header[0]= Ord('G'))and (Header[1]= Ord('I'))and
(Header[2]= Ord('F'))and (Header[3]= Ord('8')) then
begin
wWidth:= Header[6]or (Header[7] shl 8);
wHeight:= Header[8]or (Header[9] shl 8);
if (wWidth> 0)and (wHeight> 0) then
begin
AWidth:= wWidth* 0.75;
AHeight:= wHeight* 0.75;
Result:= 1;
end;
end;
finally
f.Free;
end;
end;
Şeklini not et: hata değerleri önce atanır ve başarı yolu onları üzerine yazan tek şeydir. lxImage.pas içindeki her okuyucu bu sıralamayı izler, çünkü "bunu okuyamadım"ı her erken çıkışta hatırlanması gereken bir şey olmaktansa varsayılan yapar. Tam GIF87a ya da GIF89a dizesi yerine yalnızca GIF8 kontrolü bilinçlidir — tanımlayıcı düzeni iki revizyonda da aynıdır ve geometrisi okunabilir bir dosyayı reddetmenin hiçbir faydası yoktur
12'lik bir BMP başlık boyutu aslında neyi değiştirir?
GetBmpSize, 14 ofsetindeki dört baytı DIB başlık boyutu olarak okur ve değere göre dallanır, çünkü başlık boyutu 12 olan bir BMP, boyutlarını diğer her BMP'den tamamen farklı bir alan genişliğinde saklar. [MS-WMF] §2.2.2 aileyi ortaya koyar: BITMAPCOREHEADER 12 bayttır ve 16 bitlik genişlik ile yükseklik taşır; BITMAPINFOHEADER, BITMAPV4HEADER ve BITMAPV5HEADER ise 32 bitlik işaretli biWidth ve biHeight ile başlar. Dosyada iki bayt varken dört bayt okumak yüksek sesle başarısız olmaz; düzlem sayısını ve bit derinliğini bir sayının yüksek yarısı olarak çeker ve birkaç yüz bin piksel genişliğinde bir bitmap raporlar. Aynı başlıktaki ikinci tuzak biHeight işaretidir: negatif bir değer negatif bir yükseklik anlamına gelmez, DIB satırlarının aşağıdan yukarı yerine yukarıdan aşağı saklandığı anlamına gelir; geometriyle hiçbir ilgisi olmayan bir piksel sırası ifadesidir
f.Seek(14, soFromBeginning);
if f.Read(HeaderSize, 4)= 4 then
begin
if HeaderSize= 12 then
begin
// BITMAPCOREHEADER 16 bitlik boyutlar saklar
if (f.Read(wWidth, 2)= 2)and (f.Read(wHeight, 2)= 2) then
begin
lWidth:= wWidth;
lHeight:= wHeight;
Result:= 1;
end;
end
else if (f.Read(lWidth, 4)= 4)and (f.Read(lHeight, 4)= 4) then
Result:= 1;
end;
// ...
// Negatif bir biHeight yalnızca yukarıdan aşağı bir DIB'i işaretler; büyüklük boyuttur
if (Result= 1)and (lWidth> 0)and (lHeight<> 0) then
begin
AWidth:= lWidth* 0.75;
AHeight:= Abs(lHeight)* 0.75;
end
else
Result:= - 1;
Son guard yakından okunmaya değer. Yükseklik > 0 yerine <> 0 değerine karşı test edilir, çünkü negatif yasal, sıfır yasal değildir ve Abs yalnızca dönüşüm noktasında uygulanır. Genişliğin eşdeğer bir durumu yoktur — negatif bir biWidth format tarafından tanımlanmaz, o yüzden bozuk bir dosya gibi ele alınır ve -1e düşürülür. Bunun yerine ham negatif bir değeri yukarı iletmek çizim kaydında negatif bir kapsam üretir; o da çalışma kitabını hangi uygulamanın açtığına bağlı olarak kenetlenir, yok sayılır ya da ters çevrilmiş bir şekil olarak render edilir
JPEG: hangi işaretçiler gerçekten çerçeve başlığıdır?
GetJpegSize, [$C0..$C3, $C5..$C7, $C9..$CB, $CD..$CF] işaretçi kümesini kabul eder ve o aralıktaki üç boşluk tam da meselenin kendisidir. ITU-T T.81 §B.2.2 çerçeve başlığını SOFn olarak tanımlar ve satır sayısı ile satır başına örnek, yalnızca yaygın olanlarda değil her çerçeve türünde çerçeve başlığında yaşar. Aynı sayısal aralığa düşen ama çerçeve başlığı olmayan işaretçiler $C4 (DHT, Huffman tablolarını tanımla), $C8 (JPG, ayrılmış) ve $CC (DAC, aritmetik kodlama koşullandırmasını tanımla) biçimindedir. Yalnızca baseline, genişletilmiş sıralı ve progressive — $C0, $C1 ve $C2 — tanıyan bir okuyucu, bir kameranın ya da ekran görüntüsü aracının ürettiği neredeyse her JPEG üzerinde çalışır ve sonra belge iş akışları için önemli olanlarda sessizce başarısız olur: kayıpsız JPEG ($C3), aritmetik kodlu varyantlar ($C9 ile $CB arası) ve hiyerarşik çerçeve türleri ($CD ile $CF arası) çerçeve başlığının ötesine tarar, dosyanın sonundan çıkar ve fallback'e düşer. Tıbbi görüntüleme ve taranmış belge hatları tam olarak bu dosyaları üretir
// Her SOFn işaretçisi çerçeve boyutlarını taşır; $C4 (DHT), $C8 (JPG)
// ve $CC (DAC) aralığı paylaşır ama çerçeve başlığı değildir
if Seg in [$C0.. $C3, $C5.. $C7, $C9.. $CB, $CD.. $CF] then
begin
ReadLen:= f.Read(Dummy[0], 3);
ReadLen:= f.Read(wHeight, 2);
wHeight:= swapByte(wHeight);
ReadLen:= f.Read(wWidth, 2);
wWidth:= swapByte(wWidth);
found:= True;
Break;
end
O yedi satırda iki ayrıntı. Atlanan üç bayt, segment uzunluk sözcüğü artı örnek hassasiyeti baytıdır; o yüzden okuma satır sayısına iner; JPEG yüksekliği genişlikten önce saklar — çoğu formatın tersi ve yapılması kolay bir yer değiştirme. Ve değerler swapByte üzerinden geçer, çünkü JPEG baştan sona büyük endiandır; GetPngSize öğesinin 32 bitlik IHDR değerlerini lswapByte üzerinden geçirmesinin nedeni de aynı. Segment yürüyüşünün kendi guard'ı vardır: bir işaretçi parametresiz olmadığında okuyucu iki baytlık uzunluğu alır, bayt takas eder ve değer 2'nin altındaysa seek yapmayı reddeder — kendi alanından kısa bir uzunluk, geriye doğru bir seek'e çevrilir ve bir tarama döngüsündeki geriye doğru seek, başkasının kontrol ettiği bir dosyada sonsuz döngüdür
Eksi bir döndürmek makul bir sayı döndürmekten iyidir
Beş okuyucunun ortak ipliği, GetImageSize öğesinin iki değil üç sonucu ayırmasıdır. 1, geometrinin dosyadan okunduğu anlamına gelir. -1, okunmadığı ve çağıranın varsayılanını kullanması gerektiği anlamına gelir. 7, GetWmfEmfSize tarafından döndürülür ve bambaşka bir şey ifade eder: AddPicture öğesine boyutu metadosya BSE kaydından almasını söyler, çünkü bir WMF ya da EMF kapsamını dosya başlığındaki sabit bir ofset yerine çalışma kitabının kendi resim kaydında taşır. Genel olarak metadosya işleme sınırlı EMF ve WMF decoder makalesinde ele alınır. GetJpegSize, bu ayrımın bir çıkarım yerine açık bir bayrağa neden ihtiyaç duyduğunu gösterir: tarama döngüsü üç yoldan sonlanabilir — bir çerçeve başlığı buldu, dosyanın sonuna ulaştı ya da bozuk bir segment uzunluğundan vazgeçti — ve yalnızca ilki wWidth ile wHeight değerlerini atar. found Boolean'ı olmadan diğer iki yol, o yerellerin o anda ne içerdiyse onu çevirir ve sonucu bir başarı olarak yukarı uzatırdı. Başlatılmamış yereller sık sık sıfırdır ve sıfır burada mümkün olan en kötü cevaptır: aşağı akıştaki her geçerlilik kontrolünden sağ çıkar ve kimsenin seçemediği ya da silemediği görünmez bir resim üretir
var
W, H: Double;
begin
case GetImageSize(FileName, W, H) of
1: Sheet.AddPicture(FileName, Row, Col, W, H);
7: Sheet.AddPicture(FileName, Row, Col); // metadosya: boyut BSE kaydından
else
// W ve H 100 x 100'dür; bir yer tutucuyu kabul etmek yerine bir politika belirle
LogWarning('unreadable image geometry: ' + FileName);
end;
end;
Dürüst sınırların olduğu yer
Bu okuyucular bilinçli olarak bir başlığı ayrıştırır ve durur. Pikselleri çözmezler, o yüzden saklanan boyutları ve başka hiçbir şeyi raporlarlar ve saklanan boyutların hikâyenin tamamı olmadığı durumlar vardır. Çerçeveleri mantıksal ekrandan küçük olan animasyonlu bir GIF yine de mantıksal ekranı raporlar; yerleşim için doğrudur ve bir görüntüleyicinin gösterdiğiyle eşleşmeyebilir. 90 derece döndürme belirten bir EXIF yönelim etiketi taşıyan bir JPEG, döndürülmemiş çerçeve boyutlarını raporlar; o yüzden telefonda çekilen dikey bir fotoğraf yatay olarak geri gelebilir. Kare olmayan piksel en-boy oranına sahip bir DIB ham piksel sayılarını raporlar ve 0.75 çarpanı her yerde 96 DPI varsayar. Bunların hiçbiri başlık okumadaki hatalar değildir ve hiçbiri görüntüyü çözmeden düzeltilemez; bu katmanın işi de bu değildir — belgelenmiş sınırlardır, o yüzden hattın telefon fotoğraflarıyla uğraşıyorsa yönelim etiketini kendin oku ve AddPicture çağırmadan önce değerleri takas et. Resim yerleştirme, çapalama ve çizim nesne modeli hakkındaki her şey grafikler, görseller ve çizimler makalesinde ele alınır
HotXLS, XLS, XLSX, ODS ve CSV'yi Delphi ve C++Builder'da yerel olarak, hiç Excel kurulumu gerektirmeden okur ve yazar; resim ekleme bunların her birinde bu başlık yolundan geçer. Format desteği ve lisans ayrıntıları HotXLS Delphi elektronik tablo bileşeni ürün sayfasındadır