Techninis straipsnis

Excel diagramų temos spalvos Delphi: HotXLS GelFrame

Diagramos serija, užpildyta tiesiogine RGB reikšme, neseka darbaknygės temos. Pakeiskite temą ir serija išlaikys seną spalvą. HotXLS dvejetainiame XLS tai tvarko temos spalvomis nuspalvintais diagramų serijų užpildais: GelFrame įrašu, 4198 arba $1066, įrašomu iškart po AreaFormat serijos bloke ir turinčiu OfficeArt schemos indeksą bei atspalvį. Tada Excel seriją atvaizduoja taip, kaip pats atvaizduoja temos užpildą

Iš kur atsiranda GelFrame įrašo numeris?

GelFrame įrašo numeris yra 4198 ($1066), ir jo nerasite paties įrašo specifikacijos skyriuje. [MS-XLS] 2.4.131 aprašo, ką turi GelFrame, bet, kitaip nei dauguma įrašų skyrių, nenurodo rt reikšmės. Diagramos posrauto ABNF irgi nepadeda: jis pateikia tik produkciją GELFRAME = 1*2GelFrame *Continue, įvardijančią įrašą, bet ne jo numerį. Numeris slypi įrašų numerių išvardijimo lentelėje, už kelių puslapių nuo skyriaus, dokumentuojančio naudingąją apkrovą. Šią produkciją verta dar kartą perskaityti kiekvienam, rašančiam skaitytuvą: ji leidžia vieną arba du GelFrame įrašus, po kiekvieno pasirinktinai galinčius eiti Continue įrašai, todėl parseris, darantis prielaidą apie vieną įrašą produkcijai, neteisingai tvarkys failą, kurio pats nerašė. HotXLS kiekvienai temos serijai išveda lygiai vieną GelFrame, kaip tai daro Excel paprastam vientisos temos užpildui, o jo dekoderis įrašą traktuoja kaip savarankišką naudingąją apkrovą, o ne remiasi fiksuotu skaičiumi

GelFrame naudingosios apkrovos viduje: dvi OfficeArt savybių lentelės

GelFrame naudingoji apkrova yra dvi viena po kitos einančios OfficeArt savybių lentelės: OfficeArtFOPT (vadinama OPT1), po jos OfficeArtTertiaryFOPT (OPT2). Kiekviena lentelė prasideda dviejų baitų savybių skaičiumi, po kurio eina tiek šešių baitų FOPTE įrašų, o kiekviename įraše yra dviejų baitų opid ir keturių baitų op. opid 15 bitas yra fComplex: jam esant op reikšmė tampa baitų ilgiu, o po fiksuotų įrašų eina kintama uodega. Dekoderis, ignoruojantis šias uodegas, praranda sinchronizaciją ir viskam po pirmos sudėtinės savybės skaito šiukšlių opid

Temos užpildas išreiškiamas trimis savybėmis, paskirstytomis abiejose lentelėse, ir dar viena, deklaruojančia užpildo rūšį. HotXLS rašo keturias savybes per 28 baitus, be sudėtinių uodegų:

  • fillType $0180 OPT1, nustatyta į 1 (msofillSolid)
  • fillColor $0181 OPT1, suplotas RGB, kurį nupieš senesnis arba temos nesuprantantis vartotojas
  • fillColorExt $019E OPT2, pagrindinė temos spalva
  • fillColorExtMod $01A0 OPT2, šiam pagrindui pritaikytas atspalvis arba šešėlis

Šis atskyrimas yra sąmoningas formato sprendimas, o ne įgyvendinimo atsitiktinumas: [MS-ODRAW] 2.2.2 temos trejetą aprašo kaip plokščią spalvą, pagrindinę spalvą ir modifikaciją, todėl temas suprantantis vartotojas perskaičiuoja užpildą, o jų nesuprantantis vis tiek nupieš kažką protingo. Aplink esantys opid laikosi to paties šablono ir turi tą patį numeravimą senajame bei dabartiniame [MS-ODRAW] leidime, todėl patogu skaityti dvi redakcijas kartu: fillOpacity $0182, fillBackColor $0183, fillShadeType $019C, fillBackColorExt $01A2 ir fillBackColorExtMod $01A4

Kodėl schemos indeksas sėdi raudoname baite?

Nes OfficeArtCOLORREF apibrėžiamas pagal baito poslinkį, o ne skaitinę reikšmę: raudona 0 baite, žalia 1, mėlyna 2, vėliavos 3. Perskaitykite šią struktūrą kaip little-endian DWORD, o tai yra kiekvienas FOPTE op, ir raudona tampa mažiausiai reikšmingu baitu. [MS-ODRAW] pateiktas lineColor pavyzdys tai patvirtina. Todėl fSchemeIndex, kuris yra vėliavų bitas E, turi skaitinę reikšmę $08000000, o pats schemos indeksas dedamas į raudoną baitą, žalią ir mėlyną paliekant nulius. Vadinasi, Accent1 op reikšmė yra $08000004, o ne $00000004 ir tikrai ne $04000000

Temos indeksų tvarka, kurios specifikacija neapibrėžia

Specifikacija schemos indeksų tvarką vadina apibrėžiama pagrindine sistema ir nepateikia lentelės, todėl vien baitų išdėstymo nepakanka sąveikai su Excel. HotXLS naudoja skaičiuoklės temos tvarką, kuri persikelia pirmyn ir atgal su tikrais Excel failais:

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

Atspalvis ir šešėlis: MSOTINTSHADE naudingoji apkrova

fillColorExtMod op yra MSOTINTSHADE reikšmė, viename DWORD koduojanti kryptį ir dydį, o ne ženklo trupmeną. Reikšmė $20000000 reiškia nepakeistą spalvą. Šviesinantis atspalvis yra $02F4 shl 16 or amount shl 8 or $10 (MSOTINT), o tamsinantis turi tokią pačią formą su $01F4 aukštame žodyje (MSOSHADE). amount baitas intuityviai veikia atvirkščiai: $FF reiškia nepakitę, o $00 – pilną modifikaciją. HotXLS tai normalizuoja į vieną DrawingML stiliaus double, kur teigiamas skaičius šviesina, o neigiamas tamsina, naudodama plius arba minus (255 - amount) / 255. Atvaizdavimas tikslus toms reikšmėms, kurias Excel iš tikrųjų siūlo savo UI, todėl kelionė pirmyn ir atgal yra be nuostolių, o ne apytikslė: pažįstamas „Lighter 40%“ yra amount 153, o (255 - 153) / 255 yra 0.4 be apvalinimo klaidos į abi puses. Šešėlis su amount 191 grįžta kaip -64/255. Štai legalų diapazoną apribojantis koduotuvas:

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

Temos užpildo nustatymas ir skaitymas Delphi aplinkoje

Rašymo pusėje temos užpildas yra du papildomi laukai kiekvienos serijos stiliaus įraše. TXLSChartSeriesStyleInfo gavo HasFillTheme, FillThemeColor ir FillThemeTint, o generatorius GelFrame išveda tik tada, kai nustatyti ir HasStyle, ir HasFillTheme. Jei taip pat nustatysite aiškų FillRgb, ši reikšmė į OPT1 fillColor patenka pažodžiui; jei ne, HotXLS pats suploja spalvą per integruotą numatytąją Office temos lentelę su pritaikytu atspalviu, todėl vien temos turinti serija vis dar turi protingą plokščią spalvą vartotojams, ignoruojantiems OPT2. Atkreipkite dėmesį į Default() inicializavimą, kuris svarbus, nes TXLSChartSeriesInfo turi valdomų laukų, o paprasti Boolean nariai kitu atveju būtų šiukšlės iš steko:

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

    Series[0] := Default(TXLSChartSeriesInfo);   // niekada nenaudoti FillChar šiam įrašui
    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, raudona žemiausiame baite
    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;        // be aiškaus RGB: suplotas
    Series[1].Style.FillThemeColor := 8;         // accent5

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

Skaitymas atgal eina per tą patį diagramos modelį, kurį naudoja likęs HotXLS diagramos tikrinimas. GetChartModel grąžina jums priklausantį TXLSChartModel, kurį turite atlaisvinti, o kiekvienas TXLSChartSeries kartu su iš OPT1 fillColor iškoduotu FillRgb pateikia HasFillTheme, FillThemeColor ir FillThemeTint; šis RGB turi pirmumą prieš tos serijos AreaFormat spalvą. Tos pačios trys reikšmės pasiekia ir kanoninę semantinę momentinę kopiją kaip SolidFillThemeSet, SolidFillThemeColor ir SolidFillThemeTint, todėl darbaknygės diff temos pakeitimą mato kaip temos pakeitimą, o ne nepaaiškinamą RGB poslinkį. Jei ateinate iš XLSX pusės, tai yra dvejetainio formato atitikmuo stiliui, aprašytam HotXLS vadove apie Excel diagramas, vaizdus ir piešinius Delphi aplinkoje:

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 fillColor
    end;
  finally
    Model.Free;
  end;
finally
  Wb.Free;
end;

Ko nežada temos užpildas dvejetainiame XLS?

Trys sąžiningi apribojimai. Pirma, ir svarbiausia kiekvienam, audituojančiam šį kodą: joks vietinio korpuso pavyzdinis failas neturi GelFrame įrašo. Vienuolika baitų poros 66 10 atvejų sąlyginio formatavimo pavyzdyje yra ne įrašų ribose, o viso srauto įrašų išklotinė randa nulį atvejų. Čia aprašytas bitų išdėstymas išvestas iš specifikacijos ir tada prisegtas trimis būdais: dekodavimo simetrija generatoriaus išvestyje, rankomis sukurti baitų testai, tiesiai į dekoderį paduodantys sintetinę $1066 naudingąją apkrovą, ir tikslaus suplotų RGB tvirtinimas. Tai silpnesnė įrodymų forma nei užfiksuotas Excel failas, todėl verta taip ir pasakyti, o ne sudaryti kitą įspūdį. Antra, vien temos turinčio užpildo suplotam variantui naudojama integruota numatytoji Office temos lentelė, o ne iš darbaknygės perskaityta temos dalis, nes dvejetainis XLS neturi temos dalies ta prasme, kurią turi supakuotas XLSX – jei reikia, kad plokščią spalvą lemtų pačios darbaknygės tema, pateikite FillRgb patys. Trečia, dekoderis priima GelFrame tik serijos bloke; tas pats įrašas gali pasirodyti diagramos srityje arba ašies rėmelyje, o priėmus jį ten būtų tyliai priskirtas fono užpildas serijai, todėl tokie atvejai ignoruojami. fillColorExt be $08000000 vėliavos taip pat laikomas paprasta išplėsta spalva ir niekada nenustato HasFillTheme. Darbaknygėms, kurių diagrama sukurta XLSX pasaulyje ir tik praeina per šį kelią, saugesnis yra Excel diagramų redagavimo neprarandant ChartML išsaugojimo kelias, o konteineris, kuriame yra šie įrašai, aprašytas skaitant OLE2 sudėtinius failus Delphi be COM IStorage

Temos spalvų diagramų užpildai, GelFrame koduotuvas ir dekoderis bei visas BIFF8 diagramos posrauto generatorius pateikiami HotXLS Delphi skaičiuoklių komponente, skirtame Delphi ir C++Builder, kuris skaito ir rašo XLS, XLSX bei ODS be įdiegto Excel