Tekninen artikkeli

BIFF8:n chartin toissijaiset akseliryhmät HotXLS:llä

HotXLS kirjoittaa BIFF8-chartin toissijaiset akseliryhmät emittoimalla toisen AxisParent-lohkon eikä liittämällä toista chart groupia akselien jälkeen. Klassisen XLS-chart-alistreamin jokainen chart group — ChartFormat, chart-tyyppitietue ja CrtLink — elää oman akseliryhmälohkonsa sisällä, ja jokainen sarja sitoutuu yhteen SerToCrt-tietueen kautta. Käännä sisennys väärin eikä sarjalla ole toista chart groupia johon sitoutua, riippumatta siitä kuinka monta akselitietuetta emittoit

Miksi akselien jälkeen oleva toinen chart group ei sido mitään?

Grammatiikka on koko vastaus, ja se on yksi ABNF-rivi. [MS-XLS] 2.1.7.20.1:n CHARTFOMATS-sääntö sanoo AxesUsed 1*2AXISPARENT ja avaa sen jälkeen muodon AXISPARENT = AxisParent Begin Pos [AXES] 1*4CRT End. Lue nämä kaksi tuotantoa yhdessä ja rakenne seuraa itsestään: chart groupit ovat akseliryhmän lapsia eivät sisaruksia. Chart-alistream jossa on kaksi akseliparia ja yksi perässä tuleva chart group ei ole kahden akselin chart layout-poikkeamalla, vaan chart jossa on yksi chart group ja joukko orpoja akselitietueita. Tämä on tärkeää, koska SerToCrt ($1045), joka sijaitsee SERIESFORMAT-lohkossa, sisältää nollapohjaisen chart group -indeksin eikä akseli-indeksiä. Kun kirjoitat crt = 1 vaikka vain yksi CRT-lohko on olemassa, sarja osoittaa chart groupiin jota ei koskaan emittoitu. Ihmisiä hämää tietueen nimi: AXESUSED ($1046) kuulostaa siltä että se laskee akselit, joten luonnollinen seuraava liike on emittoida lisää akseleita. Se laskee akseliryhmiä ja jokainen akseliryhmä vetää mukanaan kokonaisen piirtoalueen ja chart groupin

Sarjan merkitseminen toissijaiselle akseliryhmälle

HotXLS-puolella tämä tiivistyy yhteen Boolean-kenttään. TXLSChartSeriesInfo sisältää SecondaryAxis-kentän, ja sen asettaminen mille tahansa sarjalle jonka annat TXLSWorksheets.AddChartSheet-metodille vaihtaa koko rakennusprosessin kahden ryhmän tilaan. Erillistä "enable secondary axis" -kutsua tai akselimääräparametria ei ole, koska määrä voidaan johtaa: jos yksikin sarja haluaa toissijaisen ryhmän, chart tarvitsee kaksi ryhmää

var
  Wb: TXLSWorkbook;
  Series: array [0..1] of TXLSChartSeriesInfo;
begin
  Wb := TXLSWorkbook.Create;
  try
    Wb.Sheets.Add.Name := 'Data';
    // ... täytä A1:C12 kategorioilla, tuloilla ja marginaalilla ...

    Series[0] := Default(TXLSChartSeriesInfo);   // älä koskaan kutsu FillCharia tälle tietueelle
    Series[0].Name := 'Revenue';
    Series[0].Categories := 'Data!$A$1:$A$12';
    Series[0].Values := 'Data!$B$1:$B$12';

    Series[1] := Default(TXLSChartSeriesInfo);
    Series[1].Name := 'Margin';
    Series[1].Categories := 'Data!$A$1:$A$12';
    Series[1].Values := 'Data!$C$1:$C$12';
    Series[1].SecondaryAxis := True;             // AXESUSED muuttuu arvoksi 2

    Wb.Sheets.AddChartSheet('Dual Axis', xlsChartTypeLine,
      'Revenue vs Margin', '', '', Series);
    Wb.SaveAs('dual-axis.xls', xlExcel97);
  finally
    Wb.Free;
  end;
end;

Default(TXLSChartSeriesInfo)-rivi ei ole koristetta. TXLSChartSeriesInfo sekoittaa hallinnoituja kenttiä ( WideString-nimet, dynaamisen trendline- ja error-bar-taulukon) tavallisiin Boolean-jäseniin, ja Delphi takaa että se tyhjentää puolestasi vain hallinnoidut kentät. Jätä SecondaryAxis alustamatta ja se on mitä pinossa sattui olemaan, mikä käytännössä tarkoittaa että sama binääri tuottaa yhden akselin chartin konsoli-isännässä mutta kahden akselin chartin test runnerissa. Kategoria- ja arvoalueet puolestaan ratkaistaan työkirjan EXTERNSHEET-taulukon kautta ennen kuin builder näkee niitä — sama indeksimekanismi käsitellään artikkelissa HotXLS:n BIFF SupBook- ja XTI-ulkoisten linkkien luokittelu — joten tuntemattomaan sheettiin osoittava alue heikkenee tyhjäksi BRAI-paikanvaraajaksi sen sijaan että build epäonnistuisi

Mitä HotXLS emittoi kun sarja on toissijainen?

Emitteri muuttaa rakennetta eikä vain arvoa. Ilman toissijaista sarjaa AXESUSED sisältää arvon 1 (tai 0 pie- ja 3D pie -chartilla joilla ei ole akseliryhmiä lainkaan) ja sitä seuraa yksi AxisParent-lohko. Yhden kanssa AXESUSED sisältää arvon 2 ja builder ajaa lohkon kahdesti, jolloin 18-tavuisen AxisParent ($1041) -payloadin ensimmäinen sana iax saa vuorotellen arvot 0 ja 1. Jokainen kierros emittoi Pos-tietueen, kategoria-akselin ja arvoakselin (Axis, $101D), PlotArea-merkin ($1035), oletus-Frame-tietueen, sitten ChartFormat-tietueen ($1014), chart-tyyppitietueen, CrtLink-tietueen ($1022) ja kaksi End-merkkiä chart groupin ja akseliryhmän sulkemiseksi. Toissijainen sarja sitoutuu sitten muodossa SerToCrt crt = 1 ja ensisijainen säilyttää arvon crt = 0. Yhtä tietuetta ei tarkoituksella monisteta: selite emittoidaan vain ensimmäiseen ryhmään, koska Excel antaa chartille yhden selitteen riippumatta sen akseliryhmien määrästä. Kaksi muutakin ominaisuutta on syytä sanoa suoraan. Emitterin uudelleenjärjestely niin että chart group asetetaan axis-parent-lohkon sisään ei muuttanut tavallisten chartien tulostetta — ilman toissijaista sarjaa alistream on tavutasolla identtinen aiemman version kanssa, koska AddAxisParent-metodin parametrin iax = 0 käyttäminen on täsmälleen vanha koodipolku. Builder emittoi myös edelleen täyden akseliparin jokaiselle ryhmälle, joten toissijainen ryhmä sisältää aina oman kategoria-akselinsa vaikka välittäisit vain sen arvoskaalasta

Miten chartin tarkastus palauttaa akseliryhmäsidoksen?

Lukeminen tapahtuu kahtena kierroksena tietuelistan yli, ja niin täytyykin, koska AXESUSED tulee ennen lohkoja jotka se kuvaa. Ensimmäinen kierros etsii vain arvoa $1046 ja lukee sen ensimmäisen sanan akseliryhmien määräksi. Arvo alkaa 1:stä ja sitä vain kasvatetaan, ei koskaan pienennetä: HotXLS ottaa nykyisen määrän ja ilmoitetun määrän maksimin, joten väärin muodostunut tai kahdentunut AXESUSED ei voi palauttaa jo kahden ryhmän ilmoittanutta chartia yhteen. Toinen kierros seuraa nykyistä akseliryhmää, päivittää sitä jokaisessa AxisParent-tietueessa ja leimaa tuon indeksin jokaiselle kohtaamalleen Axis-tietueelle seuraavaan AxisParent-tietueeseen asti

var
  Model: TXLSChartModel;
  i: Integer;
begin
  // Sheets[1] on data-worksheet ja Sheets[2] chart sheet
  Model := Wb.Sheets[2]._Chart.GetChartModel;
  try
    if Model.AxisGroupCount = 2 then
      Writeln('AXESUSED declares a secondary axis group');
    for i := 0 to Model.AxisCount - 1 do
      Writeln('axis ', i, ' group ', Model.GetAxis(i).AxisGroup);
    for i := 0 to Model.SeriesCount - 1 do
      Writeln('series ', i, ' chart group ', Model.GetSeries(i).ChartGroup);
  finally
    Model.Free;
  end;
end;

Kaksi rajaa kannattaa nimetä. TXLSChartModel.AxisGroupCount ilmoittaa mitä tiedosto väittää, ei sitä kuinka monta AxisParent-lohkoa oikeasti löytyi; tiedosto joka sanoo 2 mutta toimittaa yhden lohkon ilmoittaa arvoksi 2 ja AxisCount on kohta jossa huomaat eron. TXLSChartAxis.AxisGroup on positionaalinen leima: se kirjaa minkä lohkon sisällä akseli luettiin, ja se on ainoa tieto jonka formaatti antaa. Sarjapuolella SerToCrt-dekoodaus on sidottu siihen että ollaan Series-lohkon sisällä, koska sama tietue-id esiintyy asiayhteyksissä joissa se ei ole sarjasidos ja ilman suojausta dekooderi ylikirjoittaisi väärän sarjan

Toissijaisten akselien varmistaminen ilman oikeaa Excel-tiedostoa

Tässä varmistuksessa ei tarvittu toissijaisella akselilla varustettua Excel-tiedostoa, ja juuri se on tarinan hyödyllinen osa. Rakenteellinen dekoodaus on tietuesekvenssin ominaisuus, joten syntetisoitu sekvenssi todistaa sen yhtä hyvin kuin talteen otettu. Regressio rakentaa AXESUSED-tietueen payloadilla 2, sitten kaksi AxisParent-lohkoa joista kumpikin käärii kategoria-akselin ja arvoakselin, ja varmistaa että malli palaa muodossa AxisGroupCount = 2, neljä akselia leimattuna 0, 0, 1, 1 ja toisen parin odotetut akselityypit

// Rakenteellinen varmistus ilman minkäänlaista Excel-tiedostoa
Chart := TXLSCustomChart.Create(nil, $0600);
try
  AddWordRecord($1046, [2]);   // AXESUSED: kaksi akseliryhmää
  AddAxisParentGroup(0);       // AxisParent iax=0 + Begin + 2 Axis + End
  AddAxisParentGroup(1);       // AxisParent iax=1 + Begin + 2 Axis + End

  Model := Chart.GetChartModel;
  Assert.AreEqual(2, Model.AxisGroupCount);
  Assert.AreEqual(4, Model.AxisCount);
  Assert.AreEqual(0, Model.GetAxis(1).AxisGroup);
  Assert.AreEqual(1, Model.GetAxis(2).AxisGroup);
finally
  Model.Free;
  Chart.Free;
end;

Jos syntetisoit tietueita itse, kaksi käytännön huomiota ovat tärkeitä. TXLSCustomChart.AddData(RecID, Len, nil) dereferoi payloadin kun Len on nollaa suurempi, joten Begin ($1033)- ja End ($1034)-merkit täytyy lisätä nollapituudella eikä nil-blobilla ja vanhalla pituudella. Syntetisoitu sekvenssi todistaa dekooderin, ei koskaan Excelin hyväksyntää tulosteellesi — kirjoituspuoli rakennettiin ABNF:n perusteella ja tarkistettiin sitten kierroksella GetChartModel-mallin kautta, jossa varmistettiin akselileimat 0/0/1/1 ja sarjakohtaiset chart groupit 0/1 sekä tavutasolla identtinen ei-toissijainen polku turvaverkkona. Se on sama konservatiivinen asenne jonka varaan muu HotXLS:n chart-, kuva- ja piirrostuki Delphille on rakennettu: dekoodaa se mitä tietueet sanovat ja kieltäydy arvaamasta binäärirakennetta jota et ole spesifikaatiosta lukenut. Täysi Delphi-testipaketti ajoi kirjoituspuolen jälkeen Win32:ssa ja Win64:ssa molemmissa 1650 testiä 1650:stä

Chart3d:n näkymäparametrit ja fAuto-ansa

Kaksi vierekkäistä yksityiskohtaa puree ihmisiä jotka menevät oletuschartia pidemmälle. Ensimmäinen on Chart3d ($103A, [MS-XLS] 2.4.46), litteä 14-tavuinen payload joka emittoidaan 3D-varianttien chart groupin sisälle: anRot (kierto, 0–360), anElev (korkeus, signed, -90–90), pcDist (perspektiivietäisyys, 0–100, ohitetaan ellei fPerspective ole asetettu), pcHeight ja pcDepth (prosenttia chartin leveydestä, 5–500), pcGap (0–500) ja grbit, jonka bitit ovat fPerspective $0001, fCluster $0002, fAutoscale $0004, f3DScaling $0010 ja f2DWalls $0020. Spesifikaatio lisää rajoitteita joita tietueen rakenne ei pakota puolestasi: transponoidussa bar chartissa anRot ja anElev eivät saa ylittää arvoa 44, ja pie chartissa anElev ei saa olla negatiivinen

Toinen on fAuto-bitti ja juuri se tuottaa ilmoituksen "my colors were ignored". LineFormat ($1007), AreaFormat ($100A) ja MarkerFormat ($1009) sisältävät kaikki fAuto-bitin grbit-arvon bitissä 0, ja kun bitti on asetettu Excel käyttää automaattista tyyliä ja käsittelee sen vieressä olevia eksplisiittisiä RGB-arvoja, viivatyyliä, markerin muotoa ja kokoa koristeena. Oma sarjatyyliä kirjoittavan emitterin täytyy tyhjentää bit 0; oletusemitterit pitävät sen asetettuna juuri siksi että Excel valitsee paletin. Jos muokkaat olemassa olevaa työkirjaa etkä rakenna uutta, säilytyssäännöt eroavat jälleen ja ne käsitellään artikkelissa Excel-chartien muokkaaminen säilyttäen ChartML

Toissijaiset akseliryhmät, SerToCrt-sidonta ja tässä näytetty tyypitetty chart-malli toimitetaan HotXLS Delphi spreadsheet component -komponentissa Delphille ja C++Builderille, joka lukee ja kirjoittaa BIFF8-charteja ilman asennettua Exceliä; tuotesivu sisältää täydellisen chart-tietueviitteen ja AddChartSheet-overloadien luettelon