Odborný článok

Paleta 56 farieb BIFF8: OKLab mapovanie farieb v HotXLS

HotXLS mapuje ľubovoľné RGB a theme farby na 56-slotovú BIFF8 paletu vo dvoch vrstvách: NearestIndexedColor nájde v OKLab priestore percepčne najbližší existujúci záznam palety a BuildBiffPalettePlan s ApplyBiffPalettePlan prepíše voľné sloty palety, aby true-color workbook prežil uloženie do klasického XLS. Spúšťačom je vždy rovnaký support ticket. Niekto si postaví report v XLSX s firemnými navy hlavičkami a jemným teal akcentom, uloží ho ako .xls pre legacy konzumenta a hlavičky sa vrátia čisto čierne, zatiaľ čo teal sa zmení na kričiacu tyrkysovú. Nič nespadlo a žiadne varovanie sa neozvalo. Farebný model starého formátu jednoducho neuniesie to, čo popísal ten nový, a knižnica si musela niečo vybrať

Prečo dokáže súbor XLS pojať len 56 farieb?

Pretože BIFF8 formát bunky nikdy neukladá RGB hodnotu: fonty, výplne a orámovania nesú color index a workbook-globálny Palette record ($0092, [MS-XLS] §2.4.188) dodáva presne 56 nepriehľadných RGB záznamov pre indexy 8 až 63. Indexy 0 až 7 sú pevné kópie ôsmich základných farieb a hodnoty nad 63 nie sú farby, ale tokeny ako system foreground, system background a chart text. HotXLS vystavuje paletu cez verejný ColorIndex 1 až 56, čo je fyzický index mínus 7, a ResolveIndexedColor drží tri číslovacie schémy oddelene cez TXLSIndexedColorSpace: xicsPublicColorIndex pre API hodnoty 1..56, xicsBiffIcv pre surové indexy na disku, ktoré sa validujú proti podmnožine IcvFont, IcvXF alebo IcvChart podľa role, ktorú pošlete, a xicsOoxmlIndexed, kde 64 a 65 znamená system foreground a background

HotXLS drží tri indexované farebné schémy oddelene cez TXLSIndexedColorSpace: surové BIFF icv hodnoty, 0 až 7 pevne priradené ôsmim základným farbám, 56 slotov palety 8 až 63 recordu Palette $0092, tokeny nad 63 ako system foreground, verejný ColorIndex 1 až 56 posunutý o mínus 7 a xicsOoxmlIndexed, kde 64 a 65 znamená system foreground a background
Ten istý color index znamená v každej schéme iné čísla, takže HotXLS smeruje každú hodnotu cez ResolveIndexedColor a nenechá surový BIFF token vydávať sa za verejný ColorIndex
var
  Res: TXLSIndexedColorResolution;
begin
  // $40 je BIFF icv token, nie slot palety
  Workbook.ResolveIndexedColor($40, xicsBiffIcv, Res);
  case Res.Kind of
    xickPalette:   UseArgb(Res.ARGB);   // slot palety, ak sa vyriešil
    xickAutomatic,
    xickSystem:    UseSystemColor(Res.SystemColorRole);
    xickInvalid:   RejectToken(Res.RawIndex);
  end;
end;

Všimnite si, že príklad sa vetví podľa Res.Kind a Boolean návratovú hodnotu ignoruje. ResolveIndexedColor vracia True len vtedy, keď získal konkrétny ARGB, a krátky overload nikdy nečíta Windows desktop, takže automatický alebo systémový token oprávnene vráti False a pritom je stále klasifikovaný ako xickSystem. HotXLS na to narazil vo vlastnom workbook serializéri: kód, ktorý berie False ako „žiadnu farbu“, poticho zahodí význam Automatic a System toho tokenu. Ak potrebujete pre tie tokeny reálne RGB hodnoty, zavolajte dlhý overload a dodajte callback TXLSTryResolveSystemColor, ktorý aplikuje vašu vlastnú UI, export alebo headless politiku

Prečo porovnáva HotXLS farby v OKLab namiesto RGB?

Pretože kanály sRGB sú gamma-kódované, euklidovská vzdialenosť v RGB nesleduje to, čo vidí človek, a chyba je najhoršia presne v tých tmavých, saturovaných tónoch, ktoré má firemné palety rady. Vezmite si tmavomodrú $000033. V RGB je vzdialenosť k čiernej 51 a vzdialenosť k predvolenej navy položke $000080 je 77, takže RGB matcher s plnou istotou vyfarví vašu hlavičku na čierno. V OKLab sú kvadratické vzdialenosti zhruba 0.0312 k čiernej a 0.0235 k navy a HotXLS vyberie navy, ColorIndex 11 na fyzickom slotu 18; presne tento prípad je pripnutý v test suite pre Classic aj XLSX engine. Konverzia vnútri ArgbToOklab linearizuje každý sRGB kanál, aplikuje OKLab LMS maticu, ťahá tretie odmocniny a projektuje do L, a a b, po čom je obyčajná kvadratická euklidovská vzdialenosť rozumným proxy pre vnímaný rozdiel. OKLab nie je CIEDE2000 a ani sa za neho nevydáva, nemá však žiadne piecewise korekcie odtieňa, stojí iba pár násobení na farbu a je dostatočne stabilný na to, aby poháňal clustering loop — a práve tam sa naozaj oplatí

Ako HotXLS priraďuje tmavomodrú $000033 do palety: euklidovská vzdialenosť v gamma-kódovanom RGB meria 51 k čiernej a 77 k navy a hlavičku by vyfarvila na čierno, kým kvadratické vzdialenosti ArgbToOklab 0.0312 a 0.0235 nechajú NearestIndexedColor vybrať navy, ColorIndex 11 na fyzickom slotu 18
Gamma-kódované hodnoty kanálov robia z RGB vzdialenosti slabý proxy pre to, čo vidí človek, takže HotXLS konvertuje raz do OKLab a nechá obyčajné kvadratické euklidovské porovnanie riadiť prehľadávanie palety

Čo garantuje NearestIndexedColor?

NearestIndexedColor garantuje deterministickú odpoveď, ktorá nič nezmení: jednu konverziu vstupu, jeden pevný prechod cez 56 zapamätaných položiek a najnižší verejný index vždy, keď sú dve položky rovnako blízko. Každý workbook si pamätá normalizovaný ARGB a OKLab súradnice všetkých 56 fyzických slotov spolu s počítadlom generácie palety. Reset palety prestavia cache, zmena jediného slotu aktualizuje len ten slot a dopyt proti starej generácii vráti False namiesto hádania. Prehľadávanie používa ostré less-than porovnanie začínajúce na slotu 8, a preto paleta obsahujúca tú istú farbu dvakrát vždy odpovie nižším indexom; to má význam, keď diffujete dva vygenerované súbory a očakávate byte-identický výstup. Vstupná alpha nasleduje úzky kontrakt: nulový alpha bajt sa berie ako nepriehľadný a čiastočne priehľadná hodnota sa odmietne s ColorIndex 0 a PaletteSlot -1, pretože položky palety nemajú alpha. Fill a border writery Classic enginu konvertujú RGB a theme farby na index tou istou OKLab matching rutinou pri ukladaní, takže API a uložený súbor sa zhodnú v tom, do ktorého slotu farba padne

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;

Ako vmiestni BuildBiffPalettePlan true colors do 56 slotov?

BuildBiffPalettePlan vypočíta kompletný návrh pre všetkých 56 slotov bez toho, aby siahol na workbook, takže ho môžete prezrieť, logovať alebo zahodiť. Planner najprv zavolá ScanIndexedColorUsage: každý slot, na ktorý sa font, výplň, orámovanie, podmienený formát, tvar, komentár alebo gridline pracovného hárku odkazuje indexom, je zamknutý, pretože zmena položky palety prefarví všetkých konzumentov toho indexu naraz. Cieľmi sú priame RGB a vyriešené theme farby z fontov, výplní, orámovaní, differential styleov, data barov a color scaleov. Každý cieľ má váhu podľa väčšieho z počtu rendered referencií a počtu definícií a podmienený formát počíta bunky, ktoré pokrývajú jeho range, takže farba prekreslená cez celý stĺpec prevaží nad farbou použitej v jedinej poznámke. Umiestňovanie potom prebieha v pevnom poradí:

  • Zamknuté sloty si ponechávajú svoju zdrojovú farbu bez výnimky
  • Cieľ, ktorý už v palete existuje, zostáva na svojom najnižšom zodpovedajúcom slote a ten slot sa stane pevným
  • Ak sa zostávajúce unikátne ciele zmestia do voľných slotov, každý získa exaktný slot, priradený vo vzostupnom poradí ARGB
  • Inak sa nastaví Quantized, každý voľný slot sa zasiahne cieľom, ktorého vzdialenosť k najbližšiemu existujúcemu centru krát jeho váha je najväčšia, a až 16 kôl frequency-weighted k-means v OKLab posúva len voľné centrá, kým priradenia prestanú sa meniť

Buďte k sebe úprimní v tom, čo overflow cesta vlastne dodá. Clustering je ohraničená lokálna optimalizácia, nie globálne optimum, a voľný slot nakoniec drží centroid skonvertovaný späť do sRGB s clampingom, čo môže byť farba, ktorú žiadna bunka nepoužila doslova. To, čo naozaj dostanete, je opakovateľnosť: ten istý workbook vždy dáva ten istý plán a plán hlási svoje škody cez WeightedError, MaxDistanceSquared, ExactTargetWeight a TotalTargetWeight, takže batch job môže uloženie odmietnuť, keď je aproximácia pre brand guideline priveľmi hrubá

Pipeline palety HotXLS pre true-color workbook: ScanIndexedColorUsage zamkne každý slot, na ktorý sa odkazuje font, výplň, orámovanie, podmienený formát, tvar, komentár alebo gridline, BuildBiffPalettePlan umiestni exaktné farby vo vzostupnom poradí ARGB alebo pustí až 16 kôl frequency-weighted k-means v OKLab a ApplyBiffPalettePlan pred zápisom validuje generáciu a FNV-1a hash
Plánovanie je read-only a opakovateľné, plán hlási svoje škody cez WeightedError a MaxDistanceSquared a zastaralý plán sa odmietne s paletou nedotknutou, pretože plány sú v podstate single-use
var
  Plan: TXLSBiffPalettePlan;
  I: Integer;
begin
  Plan := Workbook.BuildBiffPalettePlan;   // iba na čítanie
  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;

Ako odmietne ApplyBiffPalettePlan zastaralý plán?

ApplyBiffPalettePlan validuje celý plán skôr, než zapíše jediný slot, a pri akomkoľvek nesúhlase s aktuálnym workbookom vráti False s paletou nedotknutou. Plán nesie SourcePaletteGeneration a SourcePaletteHash, 64-bitový FNV-1a hash cez 56 zdrojových farieb; validácia znovu skontroluje každý verejný aj fyzický index, každú zdrojovú farbu, to, že žiadny zamknutý slot nie je označený ako zmenený, počty zamknutých a zmenených a to, že každý cieľ je nepriehľadný. Akákoľvek efektívna zmena palety medzitým, vrátane úspešnej skorej aplikácie toho istého plánu, urobí plán zastaralým, takže plány sú v podstate single-use. Validný plán bez zmenených slotov prejde bez posunu generácie a reálna zmena raz zvýši generáciu a raz prestaví OKLab matcher — na Classic engine prepísaním pevného poľa palety a na XLSX engine vymenením pripraveného zoznamu indexed-color override

Zapnutie pre ukladania BIFF8 a konverziu XLSX na XLS

Vlastnosť BiffPaletteSavePolicy má predvolenú hodnotu xbpsPreserve, takže upgrade HotXLS nikomu neprepíše paletu za chrbtom. Nastavenie na xbpsOptimizeTrueColors prinúti Classic workbook postaviť a aplikovať čerstvý plán vnútri SaveAs, ale len vtedy, keď cieľový formát je xlExcel97; BIFF5, CSV, HTML, PDF, XLSX a ostatné writery nastavenie ignorujú. Po úspešnom uložení optimalizovaná paleta zostáva v modeli workbooku, takže neskoršie dopyty a ukladania vidia to isté mapovanie. Ak uloženie zlyhá alebo sa zruší, obnovia sa pôvodných 56 farieb a pôvodná generácia. Pre XLSX zdroje SaveXLSXWorkbookAsXLS v lxXlsxExport postaví jeden plán z načítaného workbooku a zapíše ho do cieľovej palety skôr, než sa konvertuje akýkoľvek štýl — to je deterministický most, ktorý precvičuje demo workbook audit and conversion workbench. Theme farby prechádzajú tým istým plannerom po tom, čo sa ich tint vyrieši na RGB; ak by ste radšej nechali témy živé v chart filloch, článok o theme farbách chart fillov GelFrame popisuje, ako binárny XLS ukladá scheme index namiesto sploštenej farby

// Klasický workbook: opt in, len BIFF8
Workbook.BiffPaletteSavePolicy := xbpsOptimizeTrueColors;
if Workbook.SaveAs('report.xls', xlExcel97) <> 1 then
  HandleSaveFailure;   // paleta už je obnovená

// Model XLSX do BIFF8 s jedným deterministickým plánom palety
XWorkbook := TXLSXWorkbook.Create;
try
  if XWorkbook.Open('report.xlsx') = 1 then
    SaveXLSXWorkbookAsXLS(XWorkbook, 'report.xls');
finally
  XWorkbook.Free;
end;

API palety HotXLS fungujú rovnako na IXLSWorkbook aj TXLSXWorkbook, z Delphi aj C++Builder. Stiahnite si trial a namierte ho na svoj najfarebnejší spreadsheet zo stránky HotXLS Delphi Excel component