HotXLS mapper vilkårlige RGB- og temafarger inn på BIFF8-palettens 56 plasser i to lag: NearestIndexedColor finner den perseptuelt nærmeste eksisterende palettoppføringen i OKLab-rommet, og BuildBiffPalettePlan med ApplyBiffPalettePlan skriver om de ledige palettplassene slik at en arbeidsbok med truecolor-farger overlever en lagring til klassisk XLS. Utløseren er alltid den samme supportsaken. Noen bygger en rapport i XLSX med marineblå overskrifter og en dempet teal-aksent, lagrer den som .xls til en eldre forbruker, og overskriftene kommer tilbake helt svarte mens teal-en blir til en skrikende turkis. Ingenting krasjet, og ingen advarsel ble gitt. Fargemodellen i det gamle formatet klarer rett og slett ikke å holde på det det nye beskriver, og biblioteket måtte bare velge noe
Hvorfor kan en XLS-fil bare romme 56 farger?
Fordi et BIFF8-celleformat aldri lagrer en RGB-verdi: fonter, fyll og kantlinjer bærer en fargeindeks, og den arbeidsbokomfattende Palette-recorden ($0092, [MS-XLS] §2.4.188) leverer nøyaktig 56 opaque RGB-oppføringer for indeksene 8 til 63. Indeksene 0 til 7 er faste kopier av de åtte grunnfargene, og verdiene over 63 er ikke farger i det hele tatt, men tokens som systemforgrunn, systembakgrunn og chart-tekst. HotXLS eksponerer paletten gjennom en offentlig ColorIndex fra 1 til 56, som er den fysiske indeksen minus 7, og ResolveIndexedColor holder de tre nummereringsordningene fra hverandre gjennom TXLSIndexedColorSpace: xicsPublicColorIndex for API-verdiene 1..56, xicsBiffIcv for rå indekser på disk, som valideres mot IcvFont-, IcvXF- eller IcvChart-delmengden for rollen du oppgir, og xicsOoxmlIndexed, der 64 og 65 betyr systemforgrunn og systembakgrunn
var
Res: TXLSIndexedColorResolution;
begin
// $40 er et BIFF icv-token, ikke en palettplass
Workbook.ResolveIndexedColor($40, xicsBiffIcv, Res);
case Res.Kind of
xickPalette: UseArgb(Res.ARGB); // palettplass, hvis den løses opp
xickAutomatic,
xickSystem: UseSystemColor(Res.SystemColorRole);
xickInvalid: RejectToken(Res.RawIndex);
end;
end;
Merk at eksemplet svitsjer på Res.Kind og ignorerer den boolske returverdien. ResolveIndexedColor returnerer True bare når den har fått tak i en konkret ARGB, og den korte overloaden leser aldri Windows-skrivebordet, så et automatisk- eller system-token kan fullt lovlig komme tilbake som False og likevel være klassifisert som xickSystem. HotXLS traff på dette i sin egen arbeidsbokserialiserer: kode som behandler False som «ingen farge», kaster stille bort tokens Automatic- og System-betydning. Trenger du ekte RGB-verdier for de tokenene, kaller du den lange overloaden og oppgir en TXLSTryResolveSystemColor-callback som anvender din egen UI-, eksport- eller headless-policy
Hvorfor matcher HotXLS farger i OKLab i stedet for RGB?
Fordi sRGB-kanalverdier er gamma-kodet, så euklidisk avstand i RGB følger ikke det et menneske ser, og feilen er verst i akkurat de mørke, mettede tonene bedriftspaletter elsker. Ta den mørkeblå $000033. I RGB er avstanden til svart 51, og avstanden til standard marineblå-oppføringen $000080 er 77, så en RGB-matcher maler overskriften din trygt svart. I OKLab er de kvadrerte avstandene omkring 0,0312 til svart og 0,0235 til marineblå, og HotXLS velger marineblå, ColorIndex 11 på fysisk plass 18; akkurat det tilfellet er festet i testpakken for både Classic- og XLSX-motoren. Konverteringen inne i ArgbToOklab lineariserer hver sRGB-kanal, anvender OKLab LMS-matrisen, tar kubikkroter og projiserer på L, a og b, og etter det er en enkel kvadrert euklidisk avstand en rimelig proxy for opplevd forskjell. OKLab er ikke CIEDE2000 og later som det ikke, men den har ingen stykkevise kulørkorreksjoner, koster en neve multiplikasjoner per farge, og er stabil nok til å drive en clustering-løkke, og det er der den virkelig tjener plassen sin
Hva garanterer NearestIndexedColor?
NearestIndexedColor garanterer et deterministisk, skrivebeskyttet svar: én inngangskonvertering, én fast skanning over de 56 oppføringene i cachen, og lavest offentlig indeks når to oppføringer er like nære. Hver arbeidsbok cacher den normaliserte ARGB-en og OKLab-koordinatene for alle 56 fysiske plasser sammen med en palettgenerasjonsteller. En palettnullstilling bygger cachen på nytt, en endring av én plass oppdaterer bare den plassen, og en spørring mot en foreldet generasjon returnerer False i stedet for å gjette. Skanningen bruker en streng mindre-enn-sammenligning som starter på plass 8, og det er derfor en palett som inneholder samme farge to ganger, alltid svarer med den lavere indeksen; det teller når du diff-er to genererte filer og forventer byte-identisk output. Inngangs-alpha følger en snever kontrakt: en null alpha-byte behandles som opaque, og en delvis gjennomsiktig verdi avvises med ColorIndex 0 og PaletteSlot -1, siden palettoppføringer ikke har alpha. Fyll- og kantlinjeskriverne i Classic-motoren konverterer RGB- og temafarger til en indeks med samme OKLab-matcherutinene ved lagring, så API-en og den lagrede filen er enige om hvilken plass en farge lander på
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;
Hvordan får BuildBiffPalettePlan truecolor-farger inn på 56 plasser?
BuildBiffPalettePlan beregner et komplett forslag til alle 56 plassene uten å røre arbeidsboken, så du kan inspisere, logge eller forkaste det. Planleggeren kaller først ScanIndexedColorUsage: enhver plass som en font, et fyll, en kantlinje, en betinget formatering, en shape, en kommentar eller rutenettet i regnearket refererer til via indeks, låses, fordi en endring av en palettoppføring farger om alle forbrukerne av den indeksen på én gang. Målene er de direkte RGB- og de løste temafargene fra fonter, fyll, kantlinjer, differentialstiler, databar og fargeskalaer. Hvert mål vektes etter det største av antall rendererte referanser og antall definisjoner, og en betinget formatering teller cellene områdene sine dekker, så en farge malt på tvers av en hel kolonne veier tyngre enn én brukt i en enkelt kommentar. Plasseringen går så videre i fast rekkefølge:
- Låste plasser beholder kildefargen sin betingelsesløst
- Et mål som allerede finnes i paletten, beholdes på sin laveste matchende plass, og den plassen blir fastlåst
- Passer de gjenværende unike målene inn i de ledige plassene, får hver av dem en eksakt plass, tildelt i stigende ARGB-rekkefølge
- Ellers settes
Quantized, hver ledig plass seres med målet hvis avstand til nærmeste eksisterende senter, ganget med vekten sin, er størst, og opptil 16 runder med frekvensvektet k-means i OKLab flytter bare de ledige sentrene til fordelingene slutter å endre seg
Vær ærlig med deg selv på hva overflow-stien leverer. Clusteringen er en avgrenset lokal optimalisering, ikke et globalt optimum, og en ledig plass ender opp med å holde en centroid konvertert tilbake til sRGB med clamping, som kan være en farge ingen celle brukte ordrett. Det du får, er repeterbarhet: samme arbeidsbok gir alltid samme plan, og planen rapporterer sin egen skade gjennom WeightedError, MaxDistanceSquared, ExactTargetWeight og TotalTargetWeight, så en batchjobb kan nekte å lagre når approksimasjonen blir for grov for merkevarelinjene
var
Plan: TXLSBiffPalettePlan;
I: Integer;
begin
Plan := Workbook.BuildBiffPalettePlan; // skrivebeskyttet
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;
Hvordan avviser ApplyBiffPalettePlan en foreldet plan?
ApplyBiffPalettePlan validerer hele planen før den skriver en eneste plass, og returnerer False med paletten urørt hvis noe ikke stemmer med den gjeldende arbeidsboken. Planen bærer SourcePaletteGeneration og SourcePaletteHash, en 64-bit FNV-1a-hash over de 56 kildefargene; valideringen sjekker på nytt hver offentlig og fysisk indeks, hver kildefarge, at ingen låst plass er merket som endret, antall låste og endrede, og at hvert mål er opaque. Enhver effektiv palettendring i mellomtiden, inkludert en vellykket tidligere anvendelse av samme plan, gjør planen foreldet, så planer er i praksis engangs. En gyldig plan uten endrede plasser lykkes uten å rykke generasjonen fremover, og en ekte endring rykker generasjonen én gang og bygger OKLab-matcheren én gang på nytt — på Classic-motoren ved å skrive det faste palettarrayet om, og på XLSX-motoren ved å bytte inn en ferdig forberedt indexed-color-override-liste
Slå det på for BIFF8-lagring og XLSX-til-XLS-konvertering
Egenskapen BiffPaletteSavePolicy står som standard til xbpsPreserve, så en HotXLS-oppgradering skriver aldri om noens palett bak ryggen deres. Setter du den til xbpsOptimizeTrueColors, bygger og anvender en Classic-arbeidsbok en fersk plan inne i SaveAs, men bare når målformatet er xlExcel97; BIFF5, CSV, HTML, PDF, XLSX og de andre skriverne ignorerer innstillingen. Etter en vellykket lagring blir den optimaliserte paletten værende i arbeidsbokmodellen, så senere spørringer og lagringer ser den samme mappen. Feiler lagringen eller avbrytes den, gjenopprettes de opprinnelige 56 fargene og den opprinnelige generasjonen. For XLSX-kilder bygger SaveXLSXWorkbookAsXLS i lxXlsxExport én plan fra den innleste arbeidsboken og skriver den inn i målpaletten før noen stil konverteres, som er den deterministiske broen workbench-demonstrasjonen for arbeidsbok-audit og konvertering trener på. Temafarger går gjennom samme planlegger etter at tinten deres er løst til RGB; vil du heller holde temaene live i chart-fyll, dekker artikkelen om GelFrame-temafarge-chart-fyll hvordan binær XLS lagrer en skjemaindeks i stedet for en utflatet farge
// Klassisk arbeidsbok: opt in, kun BIFF8
Workbook.BiffPaletteSavePolicy := xbpsOptimizeTrueColors;
if Workbook.SaveAs('report.xls', xlExcel97) <> 1 then
HandleSaveFailure; // paletten er allerede gjenopprettet
// XLSX-modell til BIFF8 med én deterministisk palettplan
XWorkbook := TXLSXWorkbook.Create;
try
if XWorkbook.Open('report.xlsx') = 1 then
SaveXLSXWorkbookAsXLS(XWorkbook, 'report.xls');
finally
XWorkbook.Free;
end;
HotXLS-palett-API-ene jobber på samme måte på IXLSWorkbook og TXLSXWorkbook, både fra Delphi og C++Builder. Last ned prøveversjonen og pek den på det mest fargerike regnearket ditt fra HotXLS Delphi Excel-komponentsiden