A HotXLS a BIFF8 chart másodlagos tengelycsoportját egy második AxisParent blokkal írja ki, nem úgy, hogy az axisok után hozzáfűz egy második chart groupot. A Classic XLS chart alstreamben minden chart group — ChartFormat, a charttípus rekordja és a CrtLink — a saját axis-group blokkján belül él, és minden sorozat a SerToCrt-en át kapcsolódik az egyikhez. Fordítsd meg ezt a beágyazást, és a sorozatnak nem lesz második chart group, amelyhez kötődhet, akárhány axis rekordot is írsz ki
Miért köt semmihez egy második chart group az axisok után?
A grammatikai szabály az egész válasz, és egyetlen ABNF-sorban áll. Az [MS-XLS] 2.1.7.20.1 CHARTFOMATS szabálya ezt mondja: AxesUsed 1*2AXISPARENT, majd kifejti: AXISPARENT = AxisParent Begin Pos [AXES] 1*4CRT End. Olvasd együtt a két produkciót, és kijön az alak: a chart groupok az axis group gyerekei, nem testvérei. Egy olyan chart-alstream, amelyben két axis-pár és egy utolsó chart group van, nem furcsa elrendezésű dual-axis chart, hanem egy chart egyetlen chart grouppal és árva axis rekordokkal. Ez azért számít, mert a SerToCrt ($1045), amely a SERIESFORMAT blokkon belül ül, nullával kezdődő chart group indexet hordoz, nem axis indexet. Ha crt = 1-et írsz ki, miközben csak egy CRT blokk létezik, a sorozat egy soha ki nem bocsátott chart grouphoz mutat. A félrevezető intuíciót a rekord neve okozza: az AXESUSED ($1046) úgy hangzik, mintha az axisok számát számolná, ezért természetes lenne több axist kiírni. Valójában axis groupokat számol, és minden axis group magával hoz egy teljes plot areát és chart groupot
Sorozat megjelölése a másodlagos axis grouphoz
A HotXLS oldalán ez egyetlen Booleanre szűkül. A TXLSChartSeriesInfo egy SecondaryAxis mezőt hordoz, és ha ezt beállítod a TXLSWorksheets.AddChartSheet-nek átadott tömb bármely sorozatán, az egész builder kétcsoportos módra vált. Nincs külön „enable secondary axis” hívás és nincs axis-count paraméter, mert a szám levezethető: ha bármelyik sorozat másodlagos csoportot akar, a chartnak kettőre van szüksége
var
Wb: TXLSWorkbook;
Series: array [0..1] of TXLSChartSeriesInfo;
begin
Wb := TXLSWorkbook.Create;
try
Wb.Sheets.Add.Name := 'Data';
// ... töltsd ki az A1:C12-t kategóriákkal, bevétellel és margin-nal ...
Series[0] := Default(TXLSChartSeriesInfo); // ezt a rekordot soha ne FillChar-old
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; // az AXESUSED értéke 2 lesz
Wb.Sheets.AddChartSheet('Dual Axis', xlsChartTypeLine,
'Revenue vs Margin', '', '', Series);
Wb.SaveAs('dual-axis.xls', xlExcel97);
finally
Wb.Free;
end;
end;
A Default(TXLSChartSeriesInfo) sor nem dísz. A TXLSChartSeriesInfo managed mezőket (a WideString neveket, a dinamikus trendline- és error-bar tömböket) kever sima Boolean tagokkal, és a Delphi csak azt garantálja, hogy a managed mezők törlődnek helyetted. Ha a SecondaryAxis inicializálatlanul marad, az lesz benne, ami a stacken volt, a gyakorlatban pedig ugyanaz a bináris konzol hostból futtatva egytengelyes chartot, a teszt runner alatt pedig két tengelyeset készít. A kategória- és értéktartományok közben a munkafüzet EXTERNSHEET tábláján keresztül oldódnak fel, még mielőtt a builder látná őket — ugyanazzal az indexgépezettel, amelyet a BIFF SupBook és XTI külső linkek HotXLS-osztályozásáról szóló leírás tárgyal —, ezért egy ismeretlen sheetet megnevező range üres BRAI helyőrzőre romlik, a build viszont nem hibázik
Mit ír ki a HotXLS, amikor egy sorozat másodlagos?
Az emitter alakot vált, nem csak egy értéket. Másodlagos sorozat nélkül az AXESUSED értéke 1 (vagy 0 pie és 3D pie esetén, amelyeknek egyáltalán nincs axis groupjuk), majd egy AxisParent blokk következik. Ha van ilyen sorozat, az AXESUSED értéke 2, a builder pedig kétszer futtatja a blokkot, az iax-et — a 18 bájtos AxisParent ($1041) payload első szavát — előbb 0-ra, aztán 1-re állítva. Mindkét menet kiírja a Pos-t, a kategória- és értéktengelyt (Axis, $101D), a PlotArea jelölőt ($1035), egy alapértelmezett Frame-et, majd a ChartFormat-ot ($1014), a charttípus rekordját, a CrtLink-et ($1022), végül két End jelölőt a chart group és az axis group lezárására. A másodlagos sorozat ezután SerToCrt crt = 1-gyel kötődik, az elsődleges pedig megtartja a crt = 0-t. Egy rekordot szándékosan nem duplikál: a legend csak az első csoportban jelenik meg, mert az Excel egy chartnak egyetlen legendet ad, függetlenül a tengelycsoportok számától. Két további tulajdonságot is világosan kell látni. Az emitter átszervezése, amely a chart groupot az axis-parent blokk belsejébe tette, nem változtatta meg a normál chartok kimenetét — másodlagos sorozat nélkül az alstream bájtról bájtra azonos a korábbi változattal, mert az AddAxisParent paraméterezése iax = 0-val pontosan a régi kódút. A builder továbbra is teljes axis-párt ír ki csoportonként, így a másodlagos group saját kategóriatengellyel érkezik akkor is, ha csak az értékskálája érdekel
Hogyan állítja helyre a chart inspection az axis-group kötést?
Az olvasás két menet a rekordlistán, és ennek így kell lennie, mert az AXESUSED a leírt blokkok előtt érkezik. Az első menet csak a $1046-ot keresi, és az első szavát olvassa axis-group számlálóként. Ez az érték 1-ről indul, és csak nőhet: a HotXLS az aktuális és a deklarált érték maximumát veszi, így egy hibás vagy duplikált AXESUSED nem ronthat vissza egy olyan chartot, amelyről már kiderült, hogy két csoportot deklarál. A második menet követi az aktuális axis groupot, minden AxisParent-nél frissíti, és ezt az indexet bélyegzi minden Axis rekordra, amelyet a következő AxisParent-ig talál
var
Model: TXLSChartModel;
i: Integer;
begin
// A Sheets[1] adat-munkalap, a Sheets[2] a 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;
Két korlátot érdemes megnevezni. A TXLSChartModel.AxisGroupCount azt jelenti, amit a fájl deklarál, nem azt, hogy ténylegesen hány AxisParent blokkot talált; egy fájl, amely 2-t mond, de csak egy blokkot küld, 2-t jelent, és az AxisCount mutatja meg, hol a probléma. A TXLSChartAxis.AxisGroup pedig pozicionális bélyeg: azt rögzíti, melyik blokkban olvastuk az axist, ami az egyetlen dolog, amit a formátum elmond. A sorozatoldalon a SerToCrt dekódolása ahhoz kötött, hogy Series blokkon belül legyünk, mert ugyanaz a rekordazonosító olyan kontextusokban is megjelenik, ahol nem sorozatkötést jelent, egy őrizetlen dekóder pedig gond nélkül felülírná a rossz sorozatot
Másodlagos tengelyek ellenőrzése valódi Excel-fájl nélkül
Ehhez az ellenőrzéshez nem kellett másodlagos tengelyt tartalmazó Excel-fájl, és ez a történet hasznos része. A szerkezeti dekódolás rekord-szekvencia tulajdonsága, ezért egy szintetizált sorozat ugyanolyan pontosan bizonyítja, mint egy rögzített. A regresszió AXESUSED-ot épít 2-es payloaddal, majd két AxisParent blokkot, amelyek mindegyike egy kategória- és egy értéktengelyt foglal magába, és azt várja, hogy a modell AxisGroupCount = 2-t, 0, 0, 1, 1 bélyegű négy axist, valamint a második pár helyes axistípusait adja vissza
// Szerkezeti ellenőrzés Excel-fájl bevonása nélkül
Chart := TXLSCustomChart.Create(nil, $0600);
try
AddWordRecord($1046, [2]); // AXESUSED: két axis group
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;
Ha magad szintetizálsz rekordokat, két gyakorlati megjegyzés fontos. A TXLSCustomChart.AddData(RecID, Len, nil) nem nulla Len esetén dereferálja a payloadot, ezért a Begin ($1033) és End ($1034) jelölőket nulla hosszúsággal kell hozzáadni, nem nil blobbal és elavult hosszértékkel. Egy szintetizált szekvencia a dekódert bizonyítja, az Excel elfogadását viszont nem — az írási oldalt az ABNF alapján építettük fel, majd a GetChartModel-ön keresztüli round-tripben ellenőriztük, tengelyenként 0/0/1/1 bélyeggel és sorozatonként 0/1 chart grouppal, a másodlagos nélküli bájtpontos útvonalat biztonsági hálóként megtartva. Ugyanilyen konzervatív hozzáállásra épül a HotXLS Delphi chart-, kép- és rajztámogatása is: azt dekódold, amit a rekordok mondanak, és ne találgass bináris elrendezést, amelyet nem olvastál a specifikációban. A teljes Delphi suite a write side elkészülte után Win32 és Win64 alatt is 1650-ből 1650 tesztet teljesített
Chart3d jelenetparaméterek és az fAuto csapdája
Két szomszédos részlet okoz gondot, ha az alapértelmezett charton túl mész. Az első a Chart3d ($103A, [MS-XLS] 2.4.46), a chart group belsejében kibocsátott lapos, 14 bájtos payload a 3D változatokhoz: anRot (forgatás, 0–360), anElev (előjeles emelkedés, -90–90), pcDist (perspektívatávolság, 0–100, csak az fPerspective beállításakor számít), pcHeight és pcDepth (a chart szélességének százaléka, 5–500), pcGap (0–500), valamint egy grbit, amelynek bitjei fPerspective $0001, fCluster $0002, fAutoscale $0004, f3DScaling $0010 és f2DWalls $0020. A specifikáció olyan korlátokat is hozzátesz, amelyeket a rekord elrendezése nem kényszerít ki helyetted: transzponált bar charton az anRot és anElev nem lehet 44-nél nagyobb, pie charton pedig az anElev nem lehet negatív
A második az fAuto bit, amely a „miért ignorálta a színeimet?” hibajegyet okozza. A LineFormat ($1007), AreaFormat ($100A) és MarkerFormat ($1009) mind az fAuto-t hordozza a grbit 0. bitjén, és ha ez be van állítva, az Excel automatikus stílust alkalmaz, az explicit RGB-értékeket, vonalstílust, markerformát és markersize-t pedig puszta dísznek tekinti. Minden egyéni sorozatstílust író emitternek törölnie kell a 0. bitet; az alapértelmezett emitterek szándékosan hagyják beállítva, hogy az Excel válassza a palettát. Ha meglévő munkafüzetet szerkesztesz, nem újat építesz, a megőrzési szabályok ismét eltérnek, ezeket az Excel-chartok szerkesztése a megőrzött ChartML elvesztése nélkül tárgyalja
A másodlagos axis groupok, a SerToCrt binding és az itt bemutatott típusos chartmodell a HotXLS Delphi spreadsheet component része Delphi és C++Builder számára, amely Excel telepítése nélkül olvas és ír BIFF8 chartokat; a termékoldal tartalmazza a teljes chartrekord-referenciát és az AddChartSheet overloadjainak listáját