Tehnički članak

BIFF8 paleta od 56 boja: OKLab mapiranje u HotXLS-u

HotXLS preslikava proizvoljne RGB i tematske boje na BIFF8 paletu od 56 slotova u dva sloja: NearestIndexedColor pronalazi perceptualno najbliži postojeći unos palete u OKLab prostoru, a BuildBiffPalettePlan sa ApplyBiffPalettePlan prepisuje slobodne slotove palete da workbook sa pravim bojama preživi čuvanje u klasični XLS. Okidač je uvek isti support tiket. Neko napravi izveštaj u XLSX-u sa korporativnim tamnoplavim zaglavljima i blagim tirkiznim akcentom, sačuva ga kao .xls za legacy potrošača, i zagavlja se vrate čisto crna dok tirkizna postane preglasna tirkizna. Ništa se nije srušilo i nijedno upozorenje se nije aktiviralo. Model boja starog formata jednostavno ne može da ponese ono što je novi opisao, i biblioteka je morala da izabere nešto

Zašto XLS fajl može da drži samo 56 boja?

Jer BIFF8 format ćelije nikada ne čuva RGB vrednost: fontovi, ispune i ivice nose indeks boje, a Palette record na nivou celog workbook-a ($0092, [MS-XLS] §2.4.188) snabdeva tačno 56 neprozirnih RGB unosa za indekse 8 do 63. Indeksi 0 do 7 su fiksne kopije osam osnovnih boja, a vrednosti iznad 63 uopšte nisu boje nego tokeni poput system foreground, system background i chart text. HotXLS izlaže paletu kroz javni ColorIndex od 1 do 56, što je fizički indeks umanjen za 7, a ResolveIndexedColor drži tri šeme numeracije odvojene kroz TXLSIndexedColorSpace: xicsPublicColorIndex za API vrednosti 1..56, xicsBiffIcv za sirove indekse sa diska, koje proverava prema podskupu IcvFont, IcvXF ili IcvChart za ulogu koju prosledite, i xicsOoxmlIndexed, gde 64 i 65 znače system foreground i background

HotXLS drži tri šeme indeksiranih boja odvojene kroz TXLSIndexedColorSpace: sirove BIFF icv vrednosti, 0 do 7 fiksirano na osam osnovnih boja, 56 slotova palete 8 do 63 iz Palette record $0092, tokeni iznad 63 poput system foreground, javni ColorIndex 1 do 56 pomeren za minus 7, i xicsOoxmlIndexed gde 64 i 65 znače system foreground i background
Isti indeks boje znači različite brojeve u svakoj šemi, pa HotXLS usmerava svaku vrednost kroz ResolveIndexedColor umesto da dozvoli da se sirovi BIFF token pravi kao javni ColorIndex
var
  Res: TXLSIndexedColorResolution;
begin
  // $40 je BIFF icv token, a ne slot palete
  Workbook.ResolveIndexedColor($40, xicsBiffIcv, Res);
  case Res.Kind of
    xickPalette:   UseArgb(Res.ARGB);   // slot palete, ako je razrešen
    xickAutomatic,
    xickSystem:    UseSystemColor(Res.SystemColorRole);
    xickInvalid:   RejectToken(Res.RawIndex);
  end;
end;

Obratite pažnju da primer grana po Res.Kind i ignoriše Boolean povratnu vrednost. ResolveIndexedColor vraća True samo kada je dobila konkretan ARGB, i kratki overload nikada ne čita Windows desktop, pa automatic ili system token legitimno vraća False dok je i dalje klasifikovan kao xickSystem. HotXLS se na to naišao u sopstvenom workbook serializatoru: kod koji tretira False kao „nema boje“ tiho odbacuje Automatic i System značenje tokena. Ako Vam trebaju prave RGB vrednosti za te tokene, pozovite dugi overload i dostavite TXLSTryResolveSystemColor callback koji primenjuje Vašu sopstvenu UI, export ili headless politiku

Zašto HotXLS upoređuje boje u OKLab-u umesto u RGB?

Jer su sRGB kanalske vrednosti gamma-kodirane, pa Euklidsko rastojanje u RGB ne prati ono što ljudsko oko vidi, i greška je najveća upravo u tamnim, zasićenim tonovima koje korporativne palete obožavaju. Uzmite tamnoplavu $000033. U RGB-u je rastojanje do crne 51, a do podrazumevanog navy unosa $000080 je 77, pa RGB matcher poverljivo ofarba Vaše zaglavlje u crno. U OKLab-u su kvadrirana rastojanja oko 0.0312 do crne i 0.0235 do navy, i HotXLS bira navy, ColorIndex 11 na fizičkom slotu 18; taj tačan slučaj je zakačen u test suite-u za oba, Classic i XLSX engine. Konverzija unutar ArgbToOklab linearizuje svaki sRGB kanal, primenjuje OKLab LMS matricu, izvlači kubne korene i projektuje na L, a i b, posle čega je obično kvadrirano Euklidsko rastojanje razumna aproksacija percipirane razlike. OKLab nije CIEDE2000 i ne pretvara se da jeste, ali nema piecewise korekcije nijanse, košta par množenja po boji, i dovoljno je stabilan da pogoni clustering petlju, gde zaista zarađuje svoje mesto

Kako HotXLS uklapa tamnoplavu $000033 u paletu: Euklidsko rastojanje u gamma-kodiranom RGB meri 51 do crne i 77 do navy i ofarbalo bi zaglavlje u crno, dok ArgbToOklab kvadrirana rastojanja 0.0312 i 0.0235 dozvoljavaju NearestIndexedColor da izabere navy, ColorIndex 11 na fizičkom slotu 18
Gamma-kodirane kanalske vrednosti čine RGB rastojanje lošom merom za ono što oko vidi, pa HotXLS jednom konvertuje u OKLab i pusti obično kvadrirano Euklidsko poređenje da vodi skeniranje palete

Šta NearestIndexedColor garantuje?

NearestIndexedColor garantuje deterministički, read-only odgovor: jednu konverziju ulaza, jedno fiksno skeniranje kroz 56 keširanih unosa i najniži javni indeks kad god su dva unosa podjednako blizu. Svaki workbook kešira normalizovani ARGB i OKLab koordinate svih 56 fizičkih slotova zajedno sa brojačem generacije palete. Reset palete gradi keš iznova, promena jednog slota ažurira samo taj slot, a upit nad zastarelom generacijom vraća False umesto da nagađa. Skeniranje koristi strogo manje-poređenje počevši od slota 8, po čemu paleta koja sadrži istu boju dva puta uvek odgovara nižim indeksom; to je bitno kada diff-ujete dva generisana fajla i očekujete bajt-identičan izlaz. Ulazna alpha prati uzan ugovor: nula alpha bajt tretira se kao neproziran, a delimično transparentna vrednost se odbija sa ColorIndex 0 i PaletteSlot -1, pošto unosi palete nemaju alpha. Pisaci ispuna i ivica Classic engine-a konvertuju RGB i tematske boje u indeks istom OKLab rutinom pri čuvanju, pa se API i sačuvani fajl slažu oko toga u koji slot boja pada

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 utera prave boje u 56 slotova?

BuildBiffPalettePlan računa kompletan predlog za svih 56 slotova a da ne dodirne workbook, pa ga možete pregledati, logovati ili odbaciti. Planer prvo poziva ScanIndexedColorUsage: svaki slot koji font, ispuna, ivica, conditional format, shape, komentar ili gridline radnog lista referencira indeksom je zaključan, jer promena unosa palete prebojava svakog potrošača tog indeksa u jednom mahom. Mete su direktne RGB i razrešene tematske boje iz fontova, ispuna, ivica, differential stilova, data bar-ova i color scale-ova. Svaka meta dobija težinu po većem od broja referenci u iscrtanom sadržaju i broja definicija, a conditional format broji ćelije koje njegovi opsezi pokrivaju, pa boja preko cele kolone nadjačava onu iz jednog komentara. Postavljanje zatim ide fiksnim redom:

  • Zaključani slotovi bezuslovno zadržavaju svoju izvornu boju
  • Meta koja već postoji u paleti zadržava se na najnižem odgovarajućem slotu i taj slot postaje fiksiran
  • Ako se preostale jedinstvene mete uklope u slobodne slotove, svaka dobija tačan slot, dodeljen u rastućem ARGB redosledu
  • Inače se postavlja Quantized, svaki slobodan slot se seje metom čije je rastojanje do najbližeg postojećeg centra, pomnoženo njenom težinom, najveće, i do 16 krugova frequency-weighted k-means u OKLab-u pomera samo slobodne centre dok dodele ne stanu

Budite iskreni prema sebi oko toga šta overflow putanja isporučuje. Clustering je ograničena lokalna optimizacija, ne globalni optimum, i slobodan slot na kraju drži centroid konvertovan nazad u sRGB sa klampingom, što može biti boja koju nijedna ćelija nije koristila doslovno. Ono što dobijate je ponovljivost: isti workbook uvek daje isti plan, a plan prijavljuje sopstvenu štetu kroz WeightedError, MaxDistanceSquared, ExactTargetWeight i TotalTargetWeight, pa batch posao može da odbije čuvanje kada aproksimacija postane previše gruba za brand smernice

HotXLS pipeline palete za workbook sa pravim bojama: ScanIndexedColorUsage zaključava svaki slot koji font, ispuna, ivica, conditional format, shape, komentar ili gridline referencira, BuildBiffPalettePlan postavlja tačne boje u rastućem ARGB redosledu ili pokreće do 16 krugova frequency-weighted k-means u OKLab-u, a ApplyBiffPalettePlan proverava generaciju i FNV-1a hash pre upisa
Planiranje je read-only i ponovljivo, plan prijavljuje sopstvenu štetu kroz WeightedError i MaxDistanceSquared, a zastareli plan se odbija uz paletu netaknutom jer su planovi efektivno jednokratni
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 zastareli plan?

ApplyBiffPalettePlan validira ceo plan pre nego što upiše ijedan slot, i vraća False sa paletom netaknutom ako bilo šta ne odgovara trenutnom workbook-u. Plan nosi SourcePaletteGeneration i SourcePaletteHash, 64-bitni FNV-1a hash preko 56 izvornih boja; validacija takođe ponovo proverava svaki javni i fizički indeks, svaku izvornu boju, da nijedan zaključani slot nije označen kao promenjen, brojače zaključanih i promenjenih, i da je svaka meta neprozirna. Bilo koja efektivna promena palete u međuvremenu, uključujući uspešnu raniju primenu istog plana, čini plan zastarelim, pa su planovi efektivno jednokratni. Ispravan plan bez promenjenih slotova uspeva bez pomeranja generacije, a prava promena podiže generaciju jednom i gradi OKLab matcher iznova jednom, na Classic engine-u prepisivanjem fiksnog niza palete, a na XLSX engine-u uklapanjem pripremljene indexed-color override liste

Uključivanje za BIFF8 čuvanja i konverziju XLSX-a u XLS

Svojstvo BiffPaletteSavePolicy podrazumevano je xbpsPreserve, pa nadogradnja HotXLS-a nikome ne prepisuje paletu iza leđa. Postavljanje na xbpsOptimizeTrueColors tera Classic workbook da izgradi i primeni svež plan unutar SaveAs, ali samo kada je ciljni format xlExcel97; BIFF5, CSV, HTML, PDF, XLSX i ostali pisci ignorišu podešavanje. Posle uspešnog čuvanja optimizovana paleta ostaje u workbook modelu, pa kasniji upiti i čuvanja vide isto mapiranje. Ako čuvanje padne ili se otkaže, originalnih 56 boja i originalna generacija se vraćaju. Za XLSX izvore, SaveXLSXWorkbookAsXLS u lxXlsxExport gradi jedan plan iz učitanog workbook-a i upisuje ga u odredišnu paletu pre nego što bilo koji stil bude konvertovan, što je deterministički most koji demo za audit i konverziju workbook-a izvodi. Tematske boje prolaze kroz isti planer posle što im se tint razreši u RGB; ako biste radije držali teme živim u chart ispunama, članak o GelFrame tematskim bojama chart ispuna pokriva kako binarni XLS čuva scheme indeks umesto spljoštene boje

// Classic workbook: uključuje se eksplicitno, samo BIFF8
Workbook.BiffPaletteSavePolicy := xbpsOptimizeTrueColors;
if Workbook.SaveAs('report.xls', xlExcel97) <> 1 then
  HandleSaveFailure;   // paleta je već vraćena

// XLSX model u BIFF8 sa 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-ji palete rade isto na IXLSWorkbook i TXLSXWorkbook, iz Delphi-ja i C++Builder-a podjednako. Preuzmite trial i usmerite ga na svoj najšareniji spreadsheet sa stranice HotXLS Delphi Excel komponente