Tekninen artikkeli

Excel-chartien teeman värit Delphissä: HotXLS GelFrame

Kirjaimellisella RGB-arvolla täytetty chart-sarja ei seuraa työkirjan teemaa. Vaihda teemaa ja sarja säilyttää vanhan värin. HotXLS käsittelee tämän binääri-XLS:ssä teeman värisinä chart-sarjojen täyttöinä: GelFrame-tietue, 4198 tai $1066, kirjoitetaan heti sarjalohkon AreaFormat-tietueen jälkeen ja se sisältää OfficeArt-skeemaindeksin sekä tint-arvon. Excel renderöi sarjan samalla tavalla kuin itse kirjoittamansa teeman mukaisen täytön

Mistä GelFrame-tietuenumero tulee?

GelFrame-tietuenumero on 4198 ($1066), etkä löydä sitä tietueen omasta spesifikaatiokohdasta. [MS-XLS] 2.4.131 kuvaa mitä GelFrame sisältää mutta toisin kuin useimmat tietueosiot se ei ilmoita rt-arvoa. Chart-alistreamin ABNF ei myöskään auta: se antaa vain tuotannon GELFRAME = 1*2GelFrame *Continue, joka nimeää tietueen mutta ei numeroi sitä. Numero on tietuenumeroiden enumeraatiotaulukossa, useita sivuja payloadia dokumentoivasta osiosta kauempana. Tuotanto on syytä lukea toisen kerran jokaisen lukijaa kirjoittavan silmin: se sallii yhden tai kaksi GelFrame-tietuetta, joita kumpaakin voivat valinnaisesti seurata Continue-tietueet, joten parseri joka olettaa tuotannolle yhden tietueen käsittelee väärin tiedoston jota se ei itse kirjoittanut. HotXLS emittoi täsmälleen yhden GelFramen teeman mukaiselle sarjalle, kuten Excel tekee yksinkertaiselle kiinteälle teematäytölle, ja sen dekooderi käsittelee tietueen itsenäisenä payloadina eikä oleta kiinteää lukumäärää

GelFrame-payloadin sisällä: kaksi OfficeArt-ominaisuustaulukkoa

GelFrame-payload on kaksi peräkkäistä OfficeArt-ominaisuustaulukkoa: OfficeArtFOPT (nimeltään OPT1), jota seuraa OfficeArtTertiaryFOPT (OPT2). Kumpikin taulukko alkaa kaksitavuisella ominaisuuksien määrällä ja jatkuu niin monella kuusitavuisella FOPTE-alkiolla, jossa jokaisessa on kaksitavuinen opid ja nelitavuinen op. opid-arvon bitti 15 on fComplex: kun se on asetettu, op-arvo on tavupituus ja kiinteiden alkioiden jälkeen seuraa muuttuvan pituuden loppuosa. Dekooderi joka ohittaa nämä loput menettää kohdistuksen ja lukee roskana opid-arvoja kaikelle ensimmäisen complex-ominaisuuden jälkeen

Teematäyttö ilmaistaan kolmella ominaisuudella jotka on jaettu molempien taulukoiden yli sekä yhdellä joka ilmoittaa täytön lajin. HotXLS kirjoittaa 28 tavuun neljä ominaisuutta ilman complex-loppuosia:

  • fillType $0180 OPT1:ssä, arvoksi 1 (msofillSolid) asetettuna
  • fillColor $0181 OPT1:ssä, litistetty RGB jonka vanhempi tai teemaa ymmärtämätön kuluttaja piirtää
  • fillColorExt $019E OPT2:ssa, alkuperäinen teeman väri
  • fillColorExtMod $01A0 OPT2:ssa, alkuperäiseen väriin kohdistettu tint tai shade

Jako on formaatin tarkoituksellinen ominaisuus eikä toteutuksen sattuma: [MS-ODRAW] 2.2.2 kuvaa teeman kolmikon litteänä värinä, perusvärinä ja muunnoksena, joten teemat ymmärtävä kuluttaja laskee täytön uudelleen ja muutoin vain järkevästi maalaava kuluttaja saa silti jotakin. Ympäröivät opidit noudattavat samaa mallia ja käyttävät identtistä numerointia vanhoissa ja nykyisissä [MS-ODRAW]-versioissa, mikä helpottaa kahden revision rinnakkaislukemista: fillOpacity $0182, fillBackColor $0183, fillShadeType $019C, fillBackColorExt $01A2 ja fillBackColorExtMod $01A4

Miksi skeemaindeksi on punaisessa tavussa?

Koska OfficeArtCOLORREF määritellään tavun offsetin eikä numeerisen arvon kautta: punainen tavussa 0, vihreä tavussa 1, sininen tavussa 2 ja liput tavussa 3. Lue tämä rakenne little-endian DWORDina, kuten jokainen FOPTE:n op-arvo luetaan, ja punaisesta tulee vähiten merkitsevä tavu. [MS-ODRAW]-dokumentin toimiva lineColor-esimerkki vahvistaa tämän. Siksi fSchemeIndex, joka on lippu E, saa numeerisen arvon $08000000 ja itse skeemaindeksi sijoitetaan punaiseen tavuun vihreän ja sinisen ollessa pakollisesti nollia. Accent1 on siis op-arvo $08000004 eikä $00000004 eikä varsinkaan $04000000

Teemaindeksien järjestys jonka spesifikaatio kieltäytyy määrittelemästä

Spesifikaatio kutsuu skeemaindeksin järjestystä hostin määrittelemäksi eikä anna taulukkoa, mikä tarkoittaa ettei pelkkä tavuasettelu riitä yhteentoimivuuteen Excelin kanssa. HotXLS käyttää spreadsheet-teeman järjestystä, joka tekee kierroksen oikeiden Excel-tiedostojen kanssa:

  • 0 = lt1, 1 = dk1, 2 = lt2, 3 = dk2
  • 4–9 = accent1–accent6
  • 10 = hlink, 11 = folHlink

Tint ja shade: MSOTINTSHADE-payload

fillColorExtMod-op on MSOTINTSHADE-arvo, joka koodaa suunnan ja määrän yhteen DWORD-arvoon signed fractionin sijaan. Arvo $20000000 tarkoittaa muuttamatonta. Vaaleuttava tint on $02F4 shl 16 or amount shl 8 or $10 (MSOTINT); tummentava tint on samanmuotoinen mutta yläosassa on $01F4 (MSOSHADE). amount-tavu kulkee vastakkaiseen suuntaan kuin intuitio: $FF tarkoittaa muuttumatonta ja $00 täyttä muunnosta. HotXLS normalisoi sen yhdeksi DrawingML-tyyliseksi doubleksi jossa positiivinen vaalentaa ja negatiivinen tummentaa, käyttäen plus- tai miinusarvoa (255 - amount) / 255. Kuvaus on täsmällinen niille arvoille joita Excelin käyttöliittymä oikeasti tarjoaa, minkä vuoksi kierros on häviötön eikä likimäärin häviötön: tuttu "Lighter 40%" on amount 153 ja (255 - 153) / 255 on 0,4 ilman pyöristysvirhettä kumpaankaan suuntaan. Shade amount-arvolla 191 palaa arvona -64/255. Tässä encoder, rajattuna lailliseen alueeseen:

if Tint > 0 then                       // MSOTINT - vaaleampi
  TintOp := LongWord($02F4) shl 16 or
    (LongWord(Round(255 * (1 - Tint))) shl 8) or $10
else if Tint < 0 then                  // MSOSHADE - tummempi
  TintOp := LongWord($01F4) shl 16 or
    (LongWord(Round(255 * (1 + Tint))) shl 8) or $10
else
  TintOp := $20000000;                 // MSOCOLORMODUNDEFINED

Teematäytön asettaminen ja lukeminen Delphillä

Kirjoituspuolella teematäyttö on kaksi uutta kenttää sarjakohtaisessa tyylitietueessa. TXLSChartSeriesStyleInfo sai kentät HasFillTheme, FillThemeColor ja FillThemeTint, ja builder emittoi GelFramen vain kun sekä HasStyle että HasFillTheme on asetettu. Jos asetat lisäksi eksplisiittisen FillRgb-arvon, se menee OPT1:n fillColor-kenttään sellaisenaan; jos et aseta sitä, HotXLS litistää värin itse sisäänrakennetun oletus-Office-teemataulukon kautta tint kohdistettuna, joten vain teeman sisältävä sarja saa silti järkevän litteän värin kuluttajille jotka jättävät OPT2:n huomiotta. Huomaa Default()-initialisaatio, koska TXLSChartSeriesInfo sisältää hallinnoituja kenttiä ja sen tavalliset Boolean-jäsenet ovat muuten pinoroskia:

var
  Wb: TXLSWorkbook;
  Series: array [0..1] of TXLSChartSeriesInfo;
begin
  Wb := TXLSWorkbook.Create;
  try
    Wb.Sheets.Add.Name := 'Data';

    Series[0] := Default(TXLSChartSeriesInfo);   // älä koskaan kutsu FillCharia tälle tietueelle
    Series[0].Name := 'Explicit';
    Series[0].Categories := 'Data!$A$1:$A$2';
    Series[0].Values := 'Data!$B$1:$B$2';
    Series[0].HasStyle := True;
    Series[0].Style.HasFill := True;
    Series[0].Style.FillRgb := $C47244;          // accent1, punainen matalassa tavussa
    Series[0].Style.HasFillTheme := True;
    Series[0].Style.FillThemeColor := 4;         // accent1
    Series[0].Style.FillThemeTint := 0.4;        // Lighter 40%

    Series[1] := Default(TXLSChartSeriesInfo);
    Series[1].Name := 'ThemeOnly';
    Series[1].Categories := 'Data!$A$1:$A$2';
    Series[1].Values := 'Data!$C$1:$C$2';
    Series[1].HasStyle := True;
    Series[1].Style.HasFillTheme := True;        // ei eksplisiittistä RGB:tä: litistetään
    Series[1].Style.FillThemeColor := 8;         // accent5

    Wb.Sheets.AddChartSheet('Themed', xlsChartTypeColumn, '', '', '', Series);
    Wb.SaveAs('themed.xls');
  finally
    Wb.Free;
  end;
end;

Takaisin lukeminen kulkee saman chart-mallin kautta jota muu HotXLS-chartien tarkastus käyttää. GetChartModel palauttaa omistetun TXLSChartModel-olion joka vapautetaan, ja jokainen TXLSChartSeries tarjoaa HasFillTheme-, FillThemeColor- ja FillThemeTint-arvot rinnakkain OPT1:n fillColor-arvosta dekoodatun FillRgb-arvon kanssa, joka voittaa kyseisen sarjan AreaFormat-värin. Samat kolme arvoa päätyvät myös kanoniseen semanttiseen snapshot-malliin kenttinä SolidFillThemeSet, SolidFillThemeColor ja SolidFillThemeTint, joten työkirjavertailu näkee teemamuutoksen teemamuutoksena eikä selittämättömänä RGB-ajautumisena. Jos tulet XLSX-puolelta, tämä on binääriformaatin vastine tyylille joka kuvataan HotXLS:n Delphi-oppaassa Excel-charteista, kuvista ja piirroksista:

Wb := TXLSWorkbook.Create;
try
  Wb.Open('themed.xls');
  Model := Wb.Sheets[2]._Chart.GetChartModel;
  try
    Ser := Model.GetSeries(0);
    if Ser.HasFillTheme then
    begin
      WriteLn(Ser.FillThemeColor);            // 4 = accent1
      WriteLn(Ser.FillThemeTint:0:3);         // 0.400
      WriteLn(IntToHex(Ser.FillRgb, 6));      // C47244, OPT1:n fillColor
    end;
  finally
    Model.Free;
  end;
finally
  Wb.Free;
end;

Mitä teeman täyttö binääri-XLS:ssä ei lupaa?

Kolme rehellistä rajaa. Ensinnäkin ja tärkeimpänä jokaiselle joka auditoi tätä koodia: paikallisen aineiston yksikään näytetiedosto ei sisällä GelFrame-tietuetta. Ehdollisen formatoinnin näytteessä olevat tavuparin 66 10 yksitoista esiintymää ovat tietueiden rajojen ulkopuolella ja täyden stream-dumpin tulos on nolla osumaa. Tässä kuvattu bittiasettelu johdettiin spesifikaatiosta ja lukittiin sitten kolmella tavalla: dekoodauksen symmetrialla builderin tulosteessa, käsin rakennetuilla tavutesteillä jotka syöttävät synteettisen $1066-payloadin suoraan dekooderille ja tarkan litistetyn RGB-arvon assertionilla. Tämä on heikompaa näyttöä kuin kaapattu Excel-tiedosto, ja niin kannattaa sanoa sen sijaan että vihjaisi muuta. Toiseksi vain teeman sisältävän täytön litistys käyttää sisäänrakennettua oletus-Office-teemataulukkoa eikä työkirjasta luettua teemaosaa, koska binääri-XLS:ssä ei ole theme partia siinä mielessä kuin paketoidussa XLSX:ssä — jos tarvitset työkirjan oman teeman ohjaamaan litteää väriä, anna FillRgb itse. Kolmanneksi dekooderi hyväksyy GelFramen vain sarjalohkon sisällä; sama tietue voi esiintyä chart-alueella tai axis-framessa, ja sen hyväksyminen siellä kohdistaisi taustatäytön hiljaisesti sarjalle, joten nämä ohitetaan. fillColorExt ilman $08000000-lippua käsitellään samoin tavallisena extended-värinä eikä se koskaan aseta HasFillTheme-arvoa. Työkirjoille joissa chart on laadittu XLSX-maailmassa ja vain kulkee tämän kautta artikkelin Excel-chartien muokkaaminen ChartML:n säilyttäen preservation-polku on turvallisempi, ja näiden tietueiden säiliö käsitellään artikkelissa OLE2-compound-tiedostojen lukeminen Delphissä ilman COM IStoragea

Teeman väriset chart-täytöt, GelFrame-enkooderi ja -dekooderi sekä koko BIFF8-chart-alistreamin builder toimitetaan HotXLS Delphi spreadsheet component -komponentin osana Delphille ja C++Builderille, ja se lukee ja kirjoittaa XLS-, XLSX- ja ODS-tiedostoja ilman asennettua Exceliä