HotXLS u dva sloja preslikava proizvoljne RGB i theme boje na BIFF8 paletu od 56 mjesta: NearestIndexedColor u OKLab prostoru nalazi perceptivno najbliži postojeći unos palete, a BuildBiffPalettePlan s ApplyBiffPalettePlan prepisuju slobodna mjesta palete tako da radna knjiga u pravim bojama preživi spremanje u klasični XLS. Okidač je uvijek isti support ticket. Netko sastavi izvještaj u XLSX-u s korporativnim tamnoplavim naslovima i blagim teal akcentom, spremit ga kao .xls za legacy potrošača, i naslovi se vrate potpuno crni dok teal postane vrišteći tirkiz. Ništa se nije srušilo i nijedno upozorenje nije zasviralo. Model boja starog formata jednostavno ne može nositi ono što je opisao novi, a biblioteka je morala nešto odabrati
Zašto XLS datoteka može držati samo 56 boja?
Zato što BIFF8 format ćelije nikad ne sprema RGB vrijednost: fontovi, ispune i okviri nose indeks boje, a Palette zapis na razini cijele radne knjige ($0092, [MS-XLS] §2.4.188) daje točno 56 neprozirnih RGB unosa za indekse 8 do 63. Indeksi 0 do 7 fiksne su kopije osam osnovnih boja, a vrijednosti iznad 63 uopće nisu boje nego tokeni poput system foreground, system background i chart text. HotXLS paletu izlaže kroz javni ColorIndex od 1 do 56, što je fizički indeks minus 7, a ResolveIndexedColor drži tri sheme numeracije odvojene kroz TXLSIndexedColorSpace: xicsPublicColorIndex za API vrijednosti 1..56, xicsBiffIcv za sirove indekse s diska, koje provjerava protiv podskupa IcvFont, IcvXF ili IcvChart za ulogu koju Vi proslijedite, i xicsOoxmlIndexed, gdje 64 i 65 znače system foreground i background
var
Res: TXLSIndexedColorResolution;
begin
// $40 je BIFF icv token, ne mjesto palete
Workbook.ResolveIndexedColor($40, xicsBiffIcv, Res);
case Res.Kind of
xickPalette: UseArgb(Res.ARGB); // mjesto palete, ako je razriješeno
xickAutomatic,
xickSystem: UseSystemColor(Res.SystemColorRole);
xickInvalid: RejectToken(Res.RawIndex);
end;
end;
Obratite pozornost na to da primjer grana na Res.Kind i ignorira Booleovu povratnu vrijednost. ResolveIndexedColor vraća True samo kada je dobila konkretan ARGB, a kratka overload verzija nikad ne čita Windows desktop, pa automatski ili sistemski token legitimno dolazi natrag kao False dok je i dalje klasificiran kao xickSystem. HotXLS je to pogodio u vlastitom serializatoru radne knjige: kod koji tretira False kao "bez boje" tiho odbacuje Automatic i System značenje tokena. Ako trebate stvarne RGB vrijednosti za te tokene, pozovite dugi overload i dostavite TXLSTryResolveSystemColor callback koji primjenjuje Vašu vlastitu UI, export ili headless politiku
Zašto HotXLS poklapa boje u OKLabu umjesto u RGB-u?
Zato što su sRGB vrijednosti kanala gamma-kodirane, pa euklidska udaljenost u RGB-u ne prati ono što čovjek vidi, a pogreška je najgora upravo u tamnim, zasićenim tonovima koje korporativne palete obožavaju. Uzmite tamnoplavu $000033. U RGB-u je udaljenost do crne 51, a do zadanog navy unosa $000080 77, pa RGB matcher samouvjereno oboji Vaše naslove crno. U OKLabu su kvadrirane udaljenosti oko 0.0312 do crne i 0.0235 do navy, pa HotXLS bira navy, ColorIndex 11 na fizičkom mjestu 18; taj točan slučaj pribijen je u test suite za i Classic i XLSX engine. Konverzija unutar ArgbToOklab linearizira svaki sRGB kanal, primjenjuje OKLab LMS matricu, izvlači kubne korijene i projicira na L, a i b, nakon čega je obična kvadrirana euklidska udaljenost razumna zamjena za percipiranu razliku. OKLab nije CIEDE2000 i ne pretvara se da jest, ali nema piecewise ispravke nijanse, košta par množenja po boji i dovoljno je stabilan da pokreće klaster petlju — i tamo zaista zaslužuje svoje mjesto
Što garantira NearestIndexedColor?
NearestIndexedColor garantira determinističan odgovor samo za čitanje: jedna konverzija ulaza, jedno fiksno skeniranje 56 keširanih unosa i najniži javni indeks kad god su dva unosa jednako blizu. Svaka radna knjiga kešira normalizirani ARGB i OKLab koordinate svih 56 fizičkih mjesta zajedno s brojačem generacije palete. Reset palete gradi keš iznova, promjena jednog mjesta ažurira samo to mjesto, a upit nad zastarjelom generacijom vraća False umjesto da nagađa. Skeniranje koristi strogu manje-od usporedbu počevši od mjesta 8, zato paleta koja istu boju sadrži dvaput uvijek odgovara nižim indeksom; to je bitno kad diffate dvije generirane datoteke i očekujete izlaz identičan do bajta. Ulazna alfa vrijednost slijedi uzak ugovor: alfa bajt nula tretira se kao neproziran, a djelomično prozirna vrijednost odbija se s ColorIndex 0 i PaletteSlot -1, jer unosi palete nemaju alfu. Fill i border pisci Classic enginea konvertiraju RGB i theme boje u indeks istom OKLab rutinom poklapanja u trenutku spremanja, pa se API i spremljena datoteka slažu u koje mjesto boja slijeće
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;
Kako BuildBiffPalettePlan ugura prave boje u 56 mjesta?
BuildBiffPalettePlan računa potpuni prijedlog za svih 56 mjesta ne dirajući radnu knjigu, pa ga možete pregledati, zapisati u log ili odbaciti. Planer najprije zove ScanIndexedColorUsage: svako mjesto koje font, ispuna, okvir, conditional format, oblik, komentar ili gridline radnog lista referencira indeksom zaključano je, jer promjena unosa palete prebojava sve potrošače tog indeksa u jednom mahu. Mete su izravne RGB i razriješene theme boje iz fontova, ispuna, okvira, diferencijalnih stilova, data barova i color scaleova. Svaka meta teži većim od broja referenci u renderu i broja definicija, a conditional format broji ćelije koje njegovi rasponi pokrivaju, pa boja preko cijelog stupca prevagne nad onom u jednoj napomeni. Smještaj zatim ide fiksnim redoslijedom:
- Zaključana mjesta bezuvjetno zadržavaju svoju izvornu boju
- Meta koja već postoji u paleti zadržava se na njenom najnižem mjestu poklapanja, a to mjesto postaje fiksno
- Ako se preostale jedinstvene mete uklapaju u slobodna mjesta, svaka dobiva točno mjesto, dodijeljeno u uzlaznom ARGB redoslijedu
- Inače se postavlja
Quantized, svako slobodno mjesto zasije se metom čija je udaljenost do najbližeg postojećeg centra, pomnožena njenom težinom, najveća, a do 16 krugova frekvencijski-težinskih k-means u OKLabu pomiče samo slobodne centre dok dodjele ne stanu
Budite iskreni prema sebi oko toga što overflow putanja isporučuje. Klasteriranje je ograničena lokalna optimizacija, ne globalni optimum, i slobodno mjesto na kraju drži centroid konvertiran natrag u sRGB uz clamping, što može biti boja koju nijedna ćelija nije koristila doslovno. Ono što dobivate je ponovljivost: ista radna knjiga uvijek daje isti plan, a plan vlastitu štetu javlja kroz WeightedError, MaxDistanceSquared, ExactTargetWeight i TotalTargetWeight, pa batch posao može odbiti spremanje kad aproksimacija postane pregruba za smjernice brenda
var
Plan: TXLSBiffPalettePlan;
I: Integer;
begin
Plan := Workbook.BuildBiffPalettePlan; // samo za čitanje
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;
Kako ApplyBiffPalettePlan odbija zastarjeli plan?
ApplyBiffPalettePlan provjerava cijeli plan prije nego napiše ijedno mjesto i vraća False s netaknutom paletom ako se bilo što ne slaže s trenutnom radnom knjigom. Plan nosi SourcePaletteGeneration i SourcePaletteHash, 64-bitni FNV-1a hash nad 56 izvornih boja; validacija dodatno ponovno provjerava svaki javni i fizički indeks, svaku izvornu boju, da nijedno zaključano mjesto nije označeno kao promijenjeno, brojače zaključanih i promijenjenih, te da je svaka meta neprozirna. Svaka stvarna promjena palete u međuvremenu, uključujući uspješnu raniju primjenu istog plana, čini plan zastarjelim, pa su planovi u praksi jednokratni. Valjan plan bez promijenjenih mjesta uspijeva bez napredovanja generacije, a stvarna promjena podiže generaciju jednom i gradi OKLab matcher iznova jednom — na Classic engineu prepisivanjem fiksnog polja palete, a na XLSX engineu umetanjem pripremljenog indexed-color override popisa
Uključivanje za BIFF8 spremanja i konverziju XLSX u XLS
Svojstvo BiffPaletteSavePolicy zadano je na xbpsPreserve, pa nadogradnja HotXLS-a nikome ne prepisuje paletu iza leđa. Postavljanje na xbpsOptimizeTrueColors natjera Classic radnu knjigu da izgradi i primijeni svjež plan unutar SaveAs, ali samo kad je ciljni format xlExcel97; BIFF5, CSV, HTML, PDF, XLSX i ostali pisci ignoriraju postavku. Nakon uspješnog spremanja optimizirana paleta ostaje u modelu radne knjige, pa kasniji upiti i spremanja vide isto mapiranje. Ako spremanje padne ili se otkaže, vraćaju se izvornih 56 boja i izvorna generacija. Za XLSX izvore, SaveXLSXWorkbookAsXLS u lxXlsxExport gradi jedan plan iz učitane radne knjige i upisuje ga u odredišnu paletu prije konverzije ijednog stila, i to je deterministički most koji vježba demo za reviziju i konverziju radne knjige. Theme boje prolaze kroz isti planer nakon što se njihov tint razriješi u RGB; ako Vi radije želite theme žive u chart ispunama, članak o GelFrame theme bojama za chart ispune pokriva kako binarni XLS sprema indeks sheme umjesto spljoštene boje
// Classic radna knjiga: isključivo uključivanje, samo BIFF8
Workbook.BiffPaletteSavePolicy := xbpsOptimizeTrueColors;
if Workbook.SaveAs('report.xls', xlExcel97) <> 1 then
HandleSaveFailure; // paleta je već vraćena
// XLSX model u BIFF8 s jednim determinističkim planom palete
XWorkbook := TXLSXWorkbook.Create;
try
if XWorkbook.Open('report.xlsx') = 1 then
SaveXLSXWorkbookAsXLS(XWorkbook, 'report.xls');
finally
XWorkbook.Free;
end;
HotXLS API palete rade isto na IXLSWorkbook i TXLSXWorkbook, iz Delphija i C++Buildera jednako. Probnu verziju preuzmite s stranice HotXLS Delphi Excel komponente i usmjerite je na svoju najšareniju proračunsku tablicu