HotXLS sovittaa mielivaltaiset RGB- ja teemavärit 56-paikkaiseen BIFF8-väripalettiin kahdessa kerroksessa: NearestIndexedColor etsii aistillisesti lähimmän olemassa olevan palettimerkinnän OKLab-avaruudessa, ja BuildBiffPalettePlan yhdessä ApplyBiffPalettePlan-metodin kanssa kirjoittaa vapaat palettipaikat uudelleen, jotta true color -työkirja selviää tallennuksesta klassiseen XLS-muotoon. Laukaisija on aina sama tukipyyntö. Joku rakentaa XLSX-raportin yrityksen tummansinisillä otsikoilla ja pehmeällä teal-korostuksella, tallentaa sen .xls-muodossa vanhan kuluttajakoodin vuoksi, ja otsikot palaavat puhtaana mustana kun taas teal muuttuu kirkkaaksi turkoosiksi. Mikään ei kaatunut eikä yhtään varoitusta lauennut. Vanhan formaatin värimalli ei yksinkertaisesti pysty taltioimaan sitä mitä uusi kuvasi, ja kirjaston piti valita jotakin
Miksi XLS-tiedosto voi taltioida vain 56 väriä?
Siksi että BIFF8-solun muotoilu ei koskaan tallenna RGB-arvoa: fontit, täytöt ja reunukset kantavat väri-indeksiä, ja koko työkirjan kattava Palette-tietue ($0092, [MS-XLS] §2.4.188) tarjoaa täsmälleen 56 läpinäkymätöntä RGB-merkintää indekseille 8–63. Indeksit 0–7 ovat kahdeksan perusvärin kiinteät kopiot, ja 63:aa suuremmat arvot eivät ole värejä lainkaan vaan tokeneita kuten system foreground, system background ja chart text. HotXLS avaa paletin julkisena ColorIndex-arvona 1–56, joka on fyysinen indeksi miinus 7, ja ResolveIndexedColor pitää kolme numerointijärjestelmää erillään TXLSIndexedColorSpace-tyypin kautta: xicsPublicColorIndex 1–56:n API-arvoille, xicsBiffIcv raaoille levillä matkustaville indekseille, jotka validoidaan antamaasi roolia vasten IcvFont-, IcvXF- tai IcvChart-joukossa, sekä xicsOoxmlIndexed, jossa 64 ja 65 tarkoittavat system foregroundia ja system backgroundia
var
Res: TXLSIndexedColorResolution;
begin
// $40 on BIFF-icv-token, ei palettipaikka
Workbook.ResolveIndexedColor($40, xicsBiffIcv, Res);
case Res.Kind of
xickPalette: UseArgb(Res.ARGB); // palettipaikka, jos ratkesi
xickAutomatic,
xickSystem: UseSystemColor(Res.SystemColorRole);
xickInvalid: RejectToken(Res.RawIndex);
end;
end;
Huomaa, että esimerkki haarautuu Res.Kind-arvon mukaan ja sivuuttaa Boolean-paluarvon. ResolveIndexedColor palauttaa arvon True vain silloin kun se sai konkreettisen ARGB:n, ja lyhyt overload ei koskaan lue Windowsin työpöytää, joten automatic- tai system-token palautuu laillisesti arvoon False luokiteltuna silti xickSystem-luokkaan. HotXLS kompastui tähän omassa työkirjaserialisoijassaan: koodi joka tulkitsee arvon False ”ei väriä” -merkitykseen heittää hiljaisena pois tokenin Automatic- ja System-merkitykset. Jos tarvitset kyseisille tokeneille oikeita RGB-arvoja, kutsu pitkää overloadia ja toimita TXLSTryResolveSystemColor-callback, joka soveltaa omaa UI-, export- tai headless-käytäntöäsi
Miksi HotXLS sovittaa värit OKLabissa eikä RGB:ssä?
Siksi että sRGB-kanava-arvot ovat gamma-koodattuja, joten euklidinen etäisyys RGB:ssä ei seuraa sitä mitä ihminen näkee, ja virhe on pahimmillaan juuri niissä tummissa, tyydyttyneissä sävyissä joita yrityspaletit rakastavat. Ota tummansininen $000033. RGB:ssä etäisyys mustaan on 51 ja etäisyys oletusarvoiseen navy-merkintään $000080 on 77, joten RGB-vertailija maalaa otsikkosi mustaksi täysin varmasti. OKLabissa neliöidyt etäisyydet ovat noin 0,0312 mustaan ja 0,0235 navyyn, ja HotXLS valitsee navyn, ColorIndex 11 fyysisellä paikalla 18; juuri tämä tapaus on lukittu testipakettiin sekä Classic- että XLSX-koneelle. ArgbToOklab-konversio linearisoi jokaisen sRGB-kanavan, soveltaa OKLabin LMS-matriisia, ottaa kuutiojuuret ja projisoi L-, a- ja b-akseleille, minkä jälkeen pelkkä neliöity euklidinen etäisyys on kohtuullinen likiarvo havaitulle erotukselle. OKLab ei ole CIEDE2000 eikä esitä olevaankaan, mutta sillä ei ole paloittaisia sävykorjauksia, se maksaa kourallisen kertolaskuja väriä kohti, ja se on tarpeeksi vakaa ohjaamaan klusterointisilmukkaa, ja siinä se oikeasti ansaitsee paikkansa
Mitä NearestIndexedColor takaa?
NearestIndexedColor takaa deterministisen, vain lukevan vastauksen: yhden syötteen konversion, yhden kiinteän skannauksen 56 välimuistissa pidettävän merkinnän yli ja pienimmän julkisen indeksin aina kun kaksi merkintää ovat yhtä lähellä. Jokainen työkirja välimuistittaa normalisoidun ARGB:n ja kaikkien 56 fyysisen paikan OKLab-koordinaatit yhdessä paletin generatiolaskurin kanssa. Paletin nollaus rakentaa välimuistin uudelleen, yksittäisen paikan muutos päivittää vain kyseisen paikan, ja kysely vanhentunutta generatiota vastaan palauttaa arvon False sen sijaan että arvaisi. Skannaus käyttää tiukkaa pienempi kuin -vertailua aloittaen paikasta 8, minkä vuoksi paletti joka sisältää saman värin kahdesti vastaa aina pienemmällä indeksillä; se merkitsee silloin kun vertailet kahta generoitua tiedostoa ja odotat tavutarkkaa tulosta. Syötteen alfa noudattaa kapeaa sopimusta: nollan alfatavu tulkitaan peittäväksi, ja osittain läpinäkyvä arvo hylätään palauttamalla ColorIndex 0 ja PaletteSlot -1, koska palettimerkinnöillä ei ole alfaa. Classic-koneen täyttö- ja reunuskirjoittajat konvertoivat RGB- ja teemavärit indeksiksi samalla OKLab-vertailurutiinilla tallennushetkellä, joten API ja talletettu tiedosto ovat samaa mieltä siitä, mille paikalle väri laskeutuu
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;
Miten BuildBiffPalettePlan mahduttaa true color -värit 56 paikkaan?
BuildBiffPalettePlan laskee valmiin ehdotuksen kaikille 56 paikalle koskematta työkirjaan, joten voit tutkia, lokittaa tai hylätä sen. Suunnittelija kutsuu ensin ScanIndexedColorUsage-metodia: jokainen paikka, johon fontti, täyttö, reunus, ehdollinen muotoilu, kuvio, kommentti tai worksheetin ruudukko viittaa indeksin kautta lukitaan, koska palettimerkinnän muuttaminen värää jokaisen kyseisen indeksin kuluttajan kerralla uudelleen. Kohteet ovat suorat RGB- ja ratkaistut teemavärit fonteista, täytöistä, reunuksista, differentiaalisista tyyleistä, datapalkkeista ja väriasteikoista. Jokainen kohde painotetaan suuremmalla sen renderöityvien viittausten määrästä ja sen määritelmien määrästä, ja ehdollinen muotoilu laskee alueittensa kattamat solut, joten koko sarakkeen yli maalattu väri voittaa yhdessä kommentissa käytetyn. Sijoitus etenee sitten kiinteässä järjestyksessä:
- Lukitut paikat säilyttävät lähdevärinsä ehdottomasti
- Kohde joka on jo paletissa säilytetään pienimmällä vastaavalla paikallaan ja kyseinen paikka muuttuu kiinteäksi
- Jos jäljellä olevat uniikit kohteet mahtuvat vapaisiin paikkoihin, jokainen saa tarkan paikan, osoitettuna nousevassa ARGB-järjestyksessä
- Muuten asetetaan
Quantized, jokainen vapaa paikka alustetaan sillä kohteella jonka etäisyys lähimpään olemassa olevaan keskukseen kerrottuna painollaan on suurin, ja enintään 16 kierrosta frekvenssipainotettua k-means-klusterointia OKLabissa siirtää vain vapaita keskuksia kunnes sijoitukset lakkaavat muuttumasta
Ole rehellinen itsellesi siitä, mitä ylivuotopolku tuottaa. Klusterointi on rajattu paikallinen optimointi, ei globaali optimi, ja vapaa paikka päätyy pitämään keskuspunktia joka on konvertoitu takaisin sRGB:hen katkaisulla, ja se voi olla väri jota mikään solu ei käyttänyt sellaisenaan. Sen sijaan saat toistettavuuden: sama työkirja tuottaa aina saman suunnitelman, ja suunnitelma raportoi oman vahingonsa WeightedError-, MaxDistanceSquared-, ExactTargetWeight- ja TotalTargetWeight-arvojen kautta, joten eräajo voi kieltäytyä tallentamasta kun likiarvo muuttuu liian karkeaksi brändiohjeistukselle
var
Plan: TXLSBiffPalettePlan;
I: Integer;
begin
Plan := Workbook.BuildBiffPalettePlan; // vain luku
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;
Miten ApplyBiffPalettePlan hylkää vanhentuneen suunnitelman?
ApplyBiffPalettePlan validoi koko suunnitelman ennen kuin se kirjoittaa yhtään paikkaa, ja palauttaa arvon False palettia koskematta jos mikä tahansa on ristiriidassa nykyisen työkirjan kanssa. Suunnitelma kantaa mukanaan SourcePaletteGeneration- ja SourcePaletteHash-kentät, 64-bittisen FNV-1a-tiivisteen 56 lähdeväristä; validointi tarkistaa myös jokaisen julkisen ja fyysisen indeksin, jokaisen lähdevärin, ettei yhtään lukittua paikkaa ole merkitty muuttuneeksi, lukittujen ja muuttuneiden määrät sekä että jokainen kohde on peittävä. Mikä tahansa toteutunut palettimuutos väliin jäävältä ajalta, myös saman suunnitelman aiempi onnistunut soveltaminen, tekee suunnitelmasta vanhentuneen, joten suunnitelmat ovat käytännössä kertakäyttöisiä. Kelvollinen suunnitelma ilman muuttuneita paikkoja onnistuu siirtämättä generatiota, ja todellinen muutos nostaa generatiota kerran ja rakentaa OKLab-vertailijan kerran uudelleen, Classic-koneella ylikirjoittamalla kiinteän palettitaulukon ja XLSX-koneella vaihtamalla paikalleen valmistellun indeksoitujen värien ohituslistan
Käyttöönotto BIFF8-tallennuksissa ja XLSX–XLS-muunnoksissa
BiffPaletteSavePolicy-ominaisuuden oletusarvo on xbpsPreserve, joten HotXLS:n päivittäminen ei koskaan kirjoita kenenkään palettia selän takana. Asetus arvoon xbpsOptimizeTrueColors saa Classic-työkirjan rakentamaan ja soveltamaan tuoreen suunnitelman SaveAs-kutsun sisällä, mutta vain kun kohdemuoto on xlExcel97; BIFF5, CSV, HTML, PDF, XLSX ja muut kirjoittajat ohittavat asetuksen. Onnistuneen tallennuksen jälkeen optimoitu paletti jää työkirjamalliin, joten myöhemmät kyselyt ja tallennukset näkevät saman kuvauksen. Jos tallennus epäonnistuu tai perutaan, alkuperäiset 56 väriä ja alkuperäinen generatio palautetaan. XLSX-lähteille SaveXLSXWorkbookAsXLS lxXlsxExportissa rakentaa yhden suunnitelman ladatusta työkirjasta ja kirjoittaa sen kohdepalettiin ennen minkään tyylin konversiota, mikä on deterministinen silta jota työkirjan auditointi- ja muunnostyökalun demo harjoittelee. Teemavärit kulkevat saman suunnittelijan läpi sen jälkeen kun niiden tint on ratkaistu RGB:ksi; jos haluat pitää teemat elävinä chart-täytöissä, GelFrame-teemavärien chart-täyttöartikkeli kertoo miten binääri-XLS tallentaa skeemaindeksin litistyneen värin sijaan
// Classic-työkirja: valitse käyttöön, vain BIFF8
Workbook.BiffPaletteSavePolicy := xbpsOptimizeTrueColors;
if Workbook.SaveAs('report.xls', xlExcel97) <> 1 then
HandleSaveFailure; // paletti on jo palautettu
// XLSX-malli BIFF8:ksi yhdellä deterministisellä palettiluonnoksella
XWorkbook := TXLSXWorkbook.Create;
try
if XWorkbook.Open('report.xlsx') = 1 then
SaveXLSXWorkbookAsXLS(XWorkbook, 'report.xls');
finally
XWorkbook.Free;
end;
HotXLS:n paletti-API:t toimivat samalla tavalla IXLSWorkbook- ja TXLSXWorkbook-rajapinnoilla Delphista ja C++Builderista käsin. Lataa kokeiluversio ja kokeile sitä värikkäimmällä taulukkolaskentatiedostollasi; saat sen HotXLS Delphi Excel -komponentin sivulta