HotXLS, keyfi RGB ve tema renklerini iki katmanda 56 yuvalık BIFF8 renk paletine oturtuyor: NearestIndexedColor OKLab uzayında algısal olarak en yakın mevcut palet girdisini buluyor, BuildBiffPalettePlan ile ApplyBiffPalettePlan ise boş palet yuvalarını yeniden yazarak gerçek renkli bir çalışma kitabının klasik XLS kaydını atlatmasını sağlıyor. Tetikleyici her zaman aynı destek talebi. Biri XLSX'te kurumsal lacivert başlıklar ve yumuşak bir camgöbeği vurgusuyla rapor hazırlıyor, eski bir tüketici için .xls olarak kaydediyor ve başlıklar simsiyah dönerken camgöbeği sırıtan bir turkuaza dönüşüyor. Hiçbir şey çökmüyor, hiçbir uyarı çıkmıyor. Eski formatın renk modeli yeninin tarif ettiğini basitçe tutamıyor ve kütüphane bir şey seçmek zorunda kalmış
Bir XLS dosyası neden yalnızca 56 renk tutabiliyor?
Çünkü BIFF8 hücre formatı hiçbir zaman RGB değeri saklamaz: fontlar, dolgular ve kenarlıklar bir renk indeksi taşır ve çalışma kitabı genelindeki Palette kaydı ($0092, [MS-XLS] §2.4.188) 8 ile 63 arasındaki indeksler için tam olarak 56 opak RGB girdisi sağlar. 0 ile 7 arasındaki indeksler sekiz temel rengin sabit kopyalarıdır ve 63 üzerindeki değerler hiçbir renk değildir; sistem ön planı, sistem arka planı ve grafik metni gibi token'lardır. HotXLS paleti, fiziksel indeksin eksi 7'si olan 1 ile 56 arası bir genel ColorIndex üzerinden açığa çıkarır ve ResolveIndexedColor üç numaralandırma şemasını TXLSIndexedColorSpace ile ayrı tutar: 1..56 API değerleri için xicsPublicColorIndex, ham disk-üzeri indeksler için ve geçirdiğiniz role göre IcvFont, IcvXF ya da IcvChart alt kümesine karşı doğrulanan xicsBiffIcv, 64 ile 65'in sistem ön planı ile arka planı ifade ettiği xicsOoxmlIndexed
var
Res: TXLSIndexedColorResolution;
begin
// $40 bir palet yuvası değil, BIFF icv token'ıdır
Workbook.ResolveIndexedColor($40, xicsBiffIcv, Res);
case Res.Kind of
xickPalette: UseArgb(Res.ARGB); // çözümlendiyse palet yuvası
xickAutomatic,
xickSystem: UseSystemColor(Res.SystemColorRole);
xickInvalid: RejectToken(Res.RawIndex);
end;
end;
Örneğin Res.Kind üzerinde switch yaptığına ve Boolean dönüş değerini yok saydığına dikkat edin. ResolveIndexedColor yalnızca somut bir ARGB elde ettiğinde True döner ve kısa overload hiçbir zaman Windows masaüstünü okumaz, bu yüzden bir automatic ya da system token'ı hâlâ xickSystem olarak sınıflanırken meşru biçimde False dönebilir. HotXLS bunu kendi çalışma kitabı serializer'ında yaşadı: False'ı "renk yok" sayan kod, token'ın Automatic ve System anlamını sessizce çöpe atıyor. Bu token'lar için gerçek RGB değerlerine ihtiyacınız varsa uzun overload'u çağırın ve kendi UI, dışa aktarma ya da headless politikanızı uygulayan bir TXLSTryResolveSystemColor callback sağlayın
HotXLS renkleri RGB yerine neden OKLab'da eşliyor?
Çünkü sRGB kanal değerleri gamma-kodlanmıştır; RGB'deki Öklid uzaklığı insanın gördüğünü takip etmez ve hata, tam da kurumsal paletlerin sevdiği koyu, doygun tonlarda en kötüdür. Koyu mavi $000033'ü alın. RGB'de siyaha uzaklık 51, varsayılan lacivert girdisi $000080'e uzaklık 77'dir, dolayısıyla bir RGB eşleştiricisi başlığınızı kendinden emin biçimde siyaha boyar. OKLab'da karesel uzaklıklar siyaha yaklaşık 0.0312, laciverte 0.0235'tir ve HotXLS laciverti seçer: fiziksel yuva 18'de ColorIndex 11; bu birebir örnek hem Classic hem XLSX motoru için test paketinde sabitlenmiş durumda. ArgbToOklab içindeki dönüşüm her sRGB kanalını doğrusallaştırır, OKLab LMS matrisini uygular, küp kökü alır ve L, a, b üzerine izdüşürür; bundan sonra düz bir karesel Öklid uzaklığı algılanan fark için makul bir vekildir. OKLab CIEDE2000 değildir ve öyle olduğunu iddia da etmez; ama parçalı hue düzeltmeleri yoktur, renk başına bir avuç çarpma tutar ve bir kümeleme döngüsünü sürmeye yeterince kararlıdır — asıl işe yaradığı yer de orasıdır
NearestIndexedColor neyi garanti ediyor?
NearestIndexedColor deterministik, salt-okunur bir cevap garanti eder: bir girdi dönüşümü, önbelleğe alınmış 56 girdi üzerinde sabit bir tarama ve iki girdi eşit yakınlıktayken en düşük genel indeks. Her çalışma kitabı, 56 fiziksel yuvanın tamamının normalize edilmiş ARGB'sini ve OKLab koordinatlarını bir palet üretim sayacıyla birlikte önbelleğe alır. Palet sıfırlaması önbelleği yeniden kurar, tek yuvalık bir değişiklik yalnızca o yuvayı günceller, bayat bir üretime karşı yapılan sorgu ise tahmin yürütmek yerine False döner. Tarama, yuva 8'den başlayan katı küçüktür karşılaştırması kullanır; aynı rengi iki kez içeren bir paletin her zaman daha düşük indeksle cevap vermesinin nedeni budur — iki üretilmiş dosyayı diff'leyip bayt-bayt özdeş çıktı beklediğinizde bu önem kazanır. Girdi alfası dar bir sözleşmeye uyar: sıfır alfa baytı opak sayılır ve kısmen saydam bir değer, palet girdileri alfa taşımadığı için ColorIndex 0 ve PaletteSlot -1 ile reddedilir. Classic motorunun dolgu ve kenarlık yazarları, kaydetme sırasında RGB ve tema renklerini aynı OKLab eşleme rutiniyle indekse çevirir; böylece API ile saklanan dosya, bir rengin hangi yuvaya düştüğü konusunda hemfikir olur
var
Match: TXLSNearestIndexedColorMatch;
begin
if Workbook.NearestIndexedColor($FF000033, Match) then
begin
// Match.ColorIndex = 11, Match.PaletteSlot = 18, Match.ARGB = $FF000080
if not Match.ExactMatch then
LogApproximation(Match.InputARGB, Match.ARGB, Match.DistanceSquared);
end;
end;
BuildBiffPalettePlan gerçek renkleri 56 yuvaya nasıl sığdırıyor?
BuildBiffPalettePlan, çalışma kitabına dokunmadan 56 yuvanın tamamı için eksiksiz bir öneri hesaplar; böylece inceleyebilir, günlüğe yazabilir ya da çöpe atabilirsiniz. Planlayıcı önce ScanIndexedColorUsage çağırır: bir font, dolgu, kenarlık, koşullu biçimlendirme, şekil, yorum ya da çalışma sayfası kılavuz çizgisinin indeksle başvurduğu her yuva kilitlenir, çünkü bir palet girdisini değiştirmek o indeksin tüm tüketicilerini tek seferde yeniden renklendirir. Hedefler, fontlardan, dolgulardan, kenarlıklardan, differential stillerden, veri çubuklarından ve renk ölçeklerinden gelen doğrudan RGB ve çözülmüş tema renkleridir. Her hedef, işlenen başvuru sayısı ile tanım sayısından büyüğüyle ağırlıklandırılır ve koşullu biçimlendirme aralıklarının kapladığı hücreleri sayar; tüm bir sütuna boyanmış bir renk, tek bir notta kullanılandan ağır basar. Yerleştirme sonra sabit bir sırayla ilerler:
- Kilitli yuvalar kaynak renklerini koşulsuz biçimde korur
- Palette zaten var olan bir hedef, eşleşen en düşük yuvasında tutulur ve o yuva sabitlenir
- Kalan benzersiz hedefler boş yuvalara sığıyorsa her biri tam bir yuva alır; atama artan ARGB sırasıyla yapılır
- Aksi hâlde
Quantizedset edilir, her boş yuva en yakın mevcut merkezine uzaklığının ağırlığıyla çarpımı en büyük olan hedefle tohumlanır ve OKLab'da 16 tura kadar frekans-ağırlıklı k-means, atamalar değişmeyi bırakana dek yalnızca boş merkezleri oynatır
Taşma yolunun ne teslim ettiği konusunda kendinize karşı dürüst olun. Kümeleme sınırlı bir yerel optimizasyondur, global optimum değil; boş bir yuva sonunda clamp'lenerek sRGB'ye geri çevrilmiş bir centroid taşır ve bu, hiçbir hücrenin aynen kullanmadığı bir renk olabilir. Asıl elde ettiğiniz tekrarlanabilirliktir: aynı çalışma kitabı her zaman aynı planı üretir ve plan kendi zararını WeightedError, MaxDistanceSquared, ExactTargetWeight ve TotalTargetWeight üzerinden raporlar; böylece bir toplu iş, yaklaşım bir marka kılavuzu için fazla kabalaştığında kaydetmeyi reddedebilir
var
Plan: TXLSBiffPalettePlan;
I: Integer;
begin
Plan := Workbook.BuildBiffPalettePlan; // salt-okunur
if Plan.Quantized and (Plan.MaxDistanceSquared > MaxAcceptedError) then
raise Exception.Create('Too many distinct colors for a BIFF8 palette');
for I := 0 to High(Plan.Slots) do
if Plan.Slots[I].Changed then
LogSlot(Plan.Slots[I].ColorIndex, Plan.Slots[I].SourceARGB,
Plan.Slots[I].TargetARGB);
if not Workbook.ApplyBiffPalettePlan(Plan) then
raise Exception.Create('The palette changed after planning');
end;
ApplyBiffPalettePlan bayat bir planı nasıl reddediyor?
ApplyBiffPalettePlan, tek bir yuvayı yazmadan önce planın tamamını doğrular ve geçerli çalışma kitabıyla herhangi bir şey uyuşmazsa palet hiç dokunulmadan False döner. Plan, SourcePaletteGeneration ile 56 kaynak renk üzerinden 64 bitlik FNV-1a hash'i olan SourcePaletteHash taşır; doğrulama ayrıca her genel ve fiziksel indeksi, her kaynak rengi, kilitli hiçbir yuvanın changed olarak işaretlenmediğini, kilitli ve değişmiş sayılarını ve her hedefin opak olduğunu yeniden kontrol eder. Aradaki herhangi bir etkili palet değişikliği — aynı planın daha önceki başarılı bir uygulaması dahil — planı bayatlatır; planlar fiilen tek kullanımlıktır. Değişmiş yuvası olmayan geçerli bir plan, üretimi ilerletmeden başarılı olur; gerçek bir değişiklik ise üretimi bir kez artırır ve OKLab eşleştiricisini bir kez yeniden kurar — Classic motorunda sabit palet dizisini yeniden yazarak, XLSX motorunda hazırlanmış bir indeksli-renk override listesini takarak
BIFF8 kayıtları ve XLSX'ten XLS'e dönüşüm için açmak
BiffPaletteSavePolicy özelliği varsayılan olarak xbpsPreserve gelir; böylece HotXLS'i yükseltmek hiç kimsenin paletini arkasından yeniden yazmaz. xbpsOptimizeTrueColors yapmak, bir Classic çalışma kitabının SaveAs içinde taze bir plan kurup uygulamasını sağlar; ama yalnızca hedef format xlExcel97 olduğunda. BIFF5, CSV, HTML, PDF, XLSX ve diğer yazarlar bu ayarı yok sayar. Başarılı bir kayıttan sonra optimize palet çalışma kitabı modelinde kalır, sonraki sorgular ve kayıtlar aynı eşlemeyi görür. Kayıt başarısız olursa ya da iptal edilirse orijinal 56 renk ve orijinal üretim geri yüklenir. XLSX kaynakları için lxXlsxExport içindeki SaveXLSXWorkbookAsXLS, yüklenen çalışma kitabından tek bir plan kurar ve herhangi bir stil dönüştürülmeden önce onu hedef paletin içine yazar; çalışma kitabı denetimi ve dönüşüm workbench demosunun kullandığı deterministik köprü budur. Tema renkleri, tint'leri RGB'ye çözüldükten sonra aynı planlayıcıdan geçer; tema renklerini grafik dolgularında canlı tutmayı tercih ederseniz GelFrame tema renkli grafik dolguları makalesi, ikili XLS'in düzleştirilmiş renk yerine şema indeksi saklamayı nasıl hallettiğini anlatıyor
// Classic çalışma kitabı: opt-in, yalnız BIFF8
Workbook.BiffPaletteSavePolicy := xbpsOptimizeTrueColors;
if Workbook.SaveAs('report.xls', xlExcel97) <> 1 then
HandleSaveFailure; // palet çoktan geri yüklendi
// XLSX modelden BIFF8'e, tek deterministik palet planıyla
XWorkbook := TXLSXWorkbook.Create;
try
if XWorkbook.Open('report.xlsx') = 1 then
SaveXLSXWorkbookAsXLS(XWorkbook, 'report.xls');
finally
XWorkbook.Free;
end;
HotXLS palet API'leri IXLSWorkbook ve TXLSXWorkbook üzerinde aynı şekilde çalışır; hem Delphi'den hem C++Builder'dan. Deneme sürümünü indirin ve en renkli elektronik tablonuzu ona gösterin: HotXLS Delphi Excel component sayfası