HotXLS, BIFF8 chart'larının ikincil eksen gruplarını eksenlerden sonra ikinci bir chart group ekleyerek değil, ikinci bir AxisParent bloğu yayımlayarak yazar. Classic XLS chart substream'inde her chart group — ChartFormat, chart type record ve CrtLink — kendi axis-group bloğunun içinde yaşar ve her series birine SerToCrt üzerinden bağlanır. Bu iç içe yapıyı ters kurarsanız kaç axis record yayımladığınızdan bağımsız olarak bir series'in bağlanabileceği ikinci bir chart group yoktur
Eksenlerden sonra ikinci bir chart group neden hiçbir şeyi bağlamaz?
Grammar bütün cevaptır ve tek satırlık ABNF'dir. [MS-XLS] 2.1.7.20.1 içindeki CHARTFOMATS kuralı AxesUsed 1*2AXISPARENT der ve ardından AXISPARENT = AxisParent Begin Pos [AXES] 1*4CRT End açılımını verir. Bu iki production birlikte okunduğunda şekil ortaya çıkar: chart group'lar axis group'un çocuklarıdır, kardeşleri değil. İki axis çifti ve sondan gelen bir chart group taşıyan chart substream, layout tuhaflığı olan dual-axis chart değil; bir chart group ile bir grup orphan axis record taşıyan bir chart'tır. Bu önemlidir, çünkü SERIESFORMAT bloğunun içinde bulunan SerToCrt ($1045), axis index'i değil 0 tabanlı chart group index'ini taşır. Yalnızca bir CRT bloğu yayımlanmışken crt = 1 yazmak series'i hiç yayımlanmamış bir chart group'a yönlendirir. İnsanları şaşırtan sezgi record adından gelir: AXESUSED ($1046) eksenleri sayıyor gibi görünür ve doğal sonraki adım daha çok eksen yayımlamaktır. O, axis group'ları sayar ve her axis group yanında tam bir plot area ile chart group getirir
Bir series'i ikincil axis group için işaretlemek
HotXLS tarafında bu tek bir Boolean'a iner. TXLSChartSeriesInfo, TXLSWorksheets.AddChartSheet'e verdiğiniz array içindeki herhangi bir series üzerinde ayarlandığında bütün builder'ı two-group mode'a geçiren bir SecondaryAxis alanı taşır. Ayrı bir "enable secondary axis" çağrısı ve axis-count parameter'ı yoktur, çünkü sayı türetilebilir: herhangi bir series secondary group istiyorsa chart'ın iki gruba ihtiyacı vardır
var
Wb: TXLSWorkbook;
Series: array [0..1] of TXLSChartSeriesInfo;
begin
Wb := TXLSWorkbook.Create;
try
Wb.Sheets.Add.Name := 'Data';
// ... A1:C12 içine category, revenue ve margin doldur ...
Series[0] := Default(TXLSChartSeriesInfo); // bu record'u asla FillChar'lama
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 artık 2 olur
Wb.Sheets.AddChartSheet('Dual Axis', xlsChartTypeLine,
'Revenue vs Margin', '', '', Series);
Wb.SaveAs('dual-axis.xls', xlExcel97);
finally
Wb.Free;
end;
end;
Default(TXLSChartSeriesInfo) satırı dekorasyon değildir. TXLSChartSeriesInfo, managed field'ları (WideString adları, dynamic trendline ve error-bar array'leri) plain Boolean üyeleriyle karıştırır; Delphi yalnızca managed field'ların sizin için temizlenmesini garanti eder. SecondaryAxis'i uninitialized bırakırsanız stack'te ne varsa o olur; pratikte aynı binary console host'tan çalışırken single-axis, test runner altında dual-axis chart üretir. Bu arada category ve value range'leri builder onları görmeden önce workbook'un EXTERNSHEET table'ı üzerinden çözülür — HotXLS'in BIFF SupBook ve XTI external link'lerini sınıflandırması yazısında ele alınan aynı index machinery — bu nedenle bilinmeyen bir sheet'i adlandıran range build'i başarısız kılmak yerine boş bir BRAI placeholder'a dönüşür
Bir series secondary olduğunda HotXLS ne yayımlar?
Emitter yalnızca bir değeri değil, şeklini değiştirir. Secondary series yoksa AXESUSED 1 taşır (axis group'ı olmayan pie ve 3D pie için 0) ve ardından tek bir AxisParent bloğu gelir. Bir tane varsa AXESUSED 2 taşır ve builder bloğu iki kez çalıştırır; 18 baytlık AxisParent ($1041) payload'ının ilk word'ü olan iax önce 0, sonra 1 olur. Her geçiş Pos'u, category axis'i ve value axis'i (Axis, $101D), PlotArea işaretini ($1035), varsayılan bir Frame'i, ardından ChartFormat ($1014), chart type record'ı, CrtLink'i ($1022) ve chart group ile axis group'u kapatmak için iki End marker'ını yayımlar. Secondary series sonra SerToCrt crt = 1 ile bağlanır, primary olan crt = 0'ı korur. Bilerek çoğaltılmayan bir record vardır: legend yalnızca ilk group'ta yayımlanır, çünkü Excel kaç axis group taşıdığına bakmadan bir chart'a tek legend verir. İki özellik daha açıkça söylenmelidir. Emitter'ı chart group'u axis-parent bloğunun içine koyacak şekilde yeniden yapılandırmak ordinary chart'ların çıktısını değiştirmedi; secondary series yokken substream önceki sürümle byte özdeştir, çünkü AddAxisParent'ı iax = 0 ile parameterize etmek eski code path'in aynısıdır. Builder ayrıca her group için tam bir axis çifti yayımlar; bu nedenle yalnızca value scale'i önemseseniz bile secondary group kendi category axis'iyle gelir
Chart inspection axis-group binding'i nasıl geri kazanır?
Record listesi üzerinde iki geçiş gerekir, çünkü AXESUSED onun açıkladığı bloklardan önce gelir. İlk geçiş yalnızca $1046'yı arar ve ilk word'ünü axis-group count olarak okur. Bu değer 1'den başlar ve yalnızca yükselir, asla düşmez: HotXLS mevcut count ile bildirilenin maksimumunu alır; bu nedenle bozuk veya yinelenmiş bir AXESUSED, iki group bildirdiği zaten görülmüş bir chart'ı geriye çekemez. İkinci geçiş mevcut axis group'u izler, her AxisParent'ta günceller ve bir sonraki AxisParent görünene kadar karşılaştığı her Axis record'ına bu index'i damgalar
var
Model: TXLSChartModel;
i: Integer;
begin
// Sheets[1] data worksheet, Sheets[2] chart sheet'tir
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;
İki sınırın adı konmalı. TXLSChartModel.AxisGroupCount, gerçekten kaç AxisParent bloğu bulunduğunu değil dosyanın bildirdiğini raporlar; 2 deyip tek blok gönderen bir dosya 2 raporlar, AxisCount ise bunu fark ettiğiniz yerdir. TXLSChartAxis.AxisGroup ise konumsal bir damgadır: bir axis'in hangi blok içinde okunduğunu kaydeder ve formatın size söylediği tek şey budur. Series tarafında SerToCrt decode edilmesi bir Series block içinde olma şartına bağlıdır; aynı record id series binding olmayan context'lerde de görünür ve guard'sız bir decoder yanlış series'i neşeyle overwrite eder
İkincil eksenleri gerçek bir Excel dosyası olmadan doğrulamak
Buradaki doğrulama için secondary axis'li gerçek bir Excel dosyası gerekmedi; hikâyenin yararlı kısmı budur. Structural decoding record sequence'ın bir özelliğidir; bu yüzden sentezlenmiş bir sequence de yakalanmış olan kadar kesin kanıt verir. Regresyon, payload'ı 2 olan AXESUSED ve her biri bir category axis ile bir value axis saran iki AxisParent bloğu kurar; ardından modelin AxisGroupCount = 2, 0, 0, 1, 1 olarak damgalanmış dört axis ve ikinci çiftte beklenen axis type'ları döndürdüğünü doğrular
// Excel dosyası hiç kullanılmadan yapısal doğrulama
Chart := TXLSCustomChart.Create(nil, $0600);
try
AddWordRecord($1046, [2]); // AXESUSED: iki 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;
Record'ları kendiniz sentezliyorsanız iki pratik not var. TXLSCustomChart.AddData(RecID, Len, nil), Len sıfır değilse payload'a dereference yapar; bu nedenle Begin ($1033) ve End ($1034) marker'ları nil blob ve eski length ile değil, sıfır length ile eklenmelidir. Ayrıca sentetik bir sequence yalnızca decoder'ı kanıtlar, Excel'in çıktınızı kabul etmesini değil. Write side ABNF'den kuruldu, sonra GetChartModel üzerinden round-trip ile kontrol edildi; axis başına 0/0/1/1 damgaları ve series başına 0/1 chart group'ları doğrulandı, secondary olmayan byte-özdeş yol da safety net olarak kaldı. Bu, Delphi için HotXLS chart, image ve drawing desteğinin geri kalanının kurulduğu aynı muhafazakâr tavırdır: record'ların söylediğini decode edin ve specification'da okumadığınız bir binary layout'u tahmin etmeyi reddedin. Write side geldikten sonra tam Delphi paketi Win32 ve Win64'te 1650 testin 1650'sini de geçti
Chart3d scene parametreleri ve fAuto tuzağı
Varsayılan chart'ın ötesine geçenleri iki komşu ayrıntı yakalar. Birincisi, 3D varyantları için chart group içinde yayımlanan düz 14 baytlık payload Chart3d ($103A, [MS-XLS] 2.4.46)'dir: anRot (rotation, 0 ile 360), anElev (signed elevation, -90 ile 90), pcDist (perspective distance, 0 ile 100; fPerspective ayarlanmadıkça yok sayılır), pcHeight ve pcDepth (chart width yüzdesi, 5 ile 500), pcGap (0 ile 500) ve bitleri fPerspective $0001, fCluster $0002, fAutoscale $0004, f3DScaling $0010 ve f2DWalls $0020 olan bir grbit. Specification, record layout'ın sizin için uygulamayacağı kısıtlar da ekler: transposed bar chart'ta anRot ve anElev 44'ü aşmamalı, pie chart'ta anElev negatif olmamalıdır
İkincisi fAuto bitidir ve "renklerim neden yok sayıldı" bug report'unu üreten budur. LineFormat ($1007), AreaFormat ($100A) ve MarkerFormat ($1009) kendi grbit değerlerinin 0. bitinde fAuto taşır; bu bit ayarlı olduğunda Excel otomatik stilini uygular ve hemen yanındaki açık RGB değerlerini, line style'ı, marker shape'i ve marker size'ı dekorasyon sayar. Custom series style yazan her emitter bit 0'ı temizlemelidir; varsayılan emitter'lar Excel'in palette'i seçmesi için onu ayarlı tutar. Yeni bir workbook kurmak yerine mevcut olanı düzenliyorsanız preservation kuralları yine değişir; bunlar korunan ChartML'i kaybetmeden Excel chart'larını düzenleme yazısında ele alınır
İkincil axis group'ları, SerToCrt binding'i ve burada gösterilen typed chart model, Delphi ve C++Builder için HotXLS Delphi spreadsheet component içinde sunulur; Excel kurulu olmadan BIFF8 chart'larını okur ve yazar, ürün sayfasında tam chart record referansı ile AddChartSheet overload listesi bulunur