Literal RGB ile doldurulmuş bir chart series workbook theme'i izlemez. Theme'i değiştirin, series eski rengi korur. HotXLS bunu binary XLS'te theme renkli chart series fill'leriyle çözer: GelFrame record'ı, 4198 veya $1066, series block içindeki AreaFormat'ın hemen arkasına yazılır ve bir OfficeArt scheme index ile tint taşır. Excel bundan sonra series'i kendi yazdığı themed fill'i render ettiği gibi render eder
GelFrame record number nereden gelir?
GelFrame record number 4198'dir ($1066) ve bunu record'un kendi specification bölümünde bulamazsınız. [MS-XLS] 2.4.131 bir GelFrame'in içeriğini tanımlar, ama çoğu record section'ın aksine rt değerini söylemez. Chart substream ABNF'si de yardımcı olmaz; yalnızca record'ı numaralandırmadan adlandıran GELFRAME = 1*2GelFrame *Continue production'ını verir. Numara, payload'ı belgeleyen bölümden birkaç sayfa uzaktaki record-number enumeration table'da yaşar. Bu production, bir veya iki GelFrame record'ına ve her birinin isteğe bağlı Continue record'larıyla izlenmesine izin verdiği için reader yazan herkesin yeniden bakmasını hak eder; production başına tek record varsayan parser, yazmadığı bir dosyayı yanlış ele alır. HotXLS basit bir solid theme fill için Excel'in ürettiği biçimde themed series başına tam bir GelFrame yayımlar ve decoder, sabit bir count varsaymak yerine record'ı kendi başına yeten bir payload olarak ele alır
GelFrame payload'ının içinde: iki OfficeArt property table
GelFrame payload'ı arka arkaya gelen iki OfficeArt property table'dır: bir OfficeArtFOPT (OPT1 olarak adlandırılır) ve ardından bir OfficeArtTertiaryFOPT (OPT2). Her table, o kadar altı baytlık FOPTE entry izleyen iki baytlık property count taşır; her entry iki baytlık opid ile dört baytlık op'tan oluşur. opid'in 15. biti fComplex'tir: ayarlıysa op değeri bir byte length olur ve fixed entry'lerden sonra değişken bir tail gelir. Bu tail'leri yok sayan bir decoder senkronizasyondan çıkar ve ilk complex property'den sonraki her şey için garbage opid okur
Theme fill, iki table'e dağılmış üç property ve fill kind'ı bildiren bir property ile ifade edilir. HotXLS tail olmadan dört property'yi 28 bayta yazar:
- OPT1'de
fillType$0180, 1 (msofillSolid) olarak ayarlı - OPT1'de
fillColor$0181, eski veya theme'den habersiz bir consumer'ın çizeceği flattened RGB - OPT2'de
fillColorExt$019E, base theme color - OPT2'de
fillColorExtMod$01A0, bu base'e uygulanan tint veya shade
Bu bölme implementation kazası değil, formatın bilinçli tercihidir: [MS-ODRAW] 2.2.2 theme triple'ını flat color artı base color artı modification olarak açıklar; theme anlayan bir consumer fill'i yeniden hesaplar, anlamayan da makul bir şey çizer. Etrafındaki opid'ler de aynı deseni izler ve eski ile güncel [MS-ODRAW] sürümlerinde aynı numaraları taşır; iki revision'ı yan yana okurken bu kullanışlıdır: fillOpacity $0182, fillBackColor $0183, fillShadeType $019C, fillBackColorExt $01A2 ve fillBackColorExtMod $01A4
Scheme index neden red byte içinde durur?
Çünkü bir OfficeArtCOLORREF numeric value ile değil byte offset ile tanımlanır: red byte 0'da, green byte 1'de, blue byte 2'de, flags byte 3'tedir. Her FOPTE op'unun yaptığı gibi bu yapıyı little-endian DWORD olarak okuyun; red least significant byte olur. [MS-ODRAW]'daki worked lineColor örneği bunu doğrular. Dolayısıyla flags bit E olan fSchemeIndex numeric olarak $08000000 değerini alır ve scheme index red byte'a, green ile blue sıfır olmak üzere konur. Accent1 bu yüzden $08000004 op value'sudur; $00000004 değil ve kesinlikle $04000000 değil
Specification'ın tanımlamayı reddettiği theme index sırası
Specification scheme index sırasını host-defined çağırır ve hiçbir table vermez; bu da byte layout'un Excel ile interoperability için tek başına yeterli olmadığı anlamına gelir. HotXLS, gerçek Excel dosyalarıyla round-trip yapan spreadsheet theme order'ını kullanır:
- 0 = lt1, 1 = dk1, 2 = lt2, 3 = dk2
- 4 ile 9 arası = accent1 ile accent6 arası
- 10 = hlink, 11 = folHlink
Tint ve shade: MSOTINTSHADE payload'ı
fillColorExtMod op'u bir MSOTINTSHADE değeridir ve yön ile miktarı signed fraction olarak değil tek bir DWORD içinde encode eder. $20000000 değeri değişiklik yapılmadığını gösterir. Lightening tint, $02F4 shl 16 or amount shl 8 or $10 (MSOTINT); darkening tint ise high word'de $01F4 olan aynı biçimdedir (MSOSHADE). amount byte'ı sezginin tersine çalışır: $FF değişmemiş, $00 tam modification anlamına gelir. HotXLS bunu DrawingML-style tek bir double'a normalize eder; pozitif açıklaştırır, negatif koyulaştırır ve artı veya eksi (255 - amount) / 255 kullanır. Excel'in UI'da gerçekten sunduğu değerlerde mapping tamdır; bu yüzden round-trip yaklaşık değil kayıpsızdır: tanıdık "Lighter 40%" amount 153'tür ve (255 - 153) / 255 her iki yönde yuvarlama hatası olmadan 0.4'tür. amount 191 olan bir shade -64/255 olarak geri gelir. İşte legal range'e sıkıştırılmış encoder:
if Tint > 0 then // MSOTINT - lighter
TintOp := LongWord($02F4) shl 16 or
(LongWord(Round(255 * (1 - Tint))) shl 8) or $10
else if Tint < 0 then // MSOSHADE - darker
TintOp := LongWord($01F4) shl 16 or
(LongWord(Round(255 * (1 + Tint))) shl 8) or $10
else
TintOp := $20000000; // MSOCOLORMODUNDEFINED
Delphi'den theme fill ayarlamak ve okumak
Write side'da theme fill, per-series style record'a eklenen iki alanla gelir. TXLSChartSeriesStyleInfo artık HasFillTheme, FillThemeColor ve FillThemeTint taşıyor; builder GelFrame'i yalnızca HasStyle ve HasFillTheme ikisi de ayarlı olduğunda yayımlar. Açık bir FillRgb de ayarlarsanız bu değer OPT1 fillColor'a olduğu gibi girer; ayarlamazsanız HotXLS rengi tint uygulanmış built-in default Office theme table üzerinden kendi flatten eder; bu sayede yalnızca theme taşıyan series, OPT2'yi yok sayan consumer'lar için de makul bir flat color taşır. Default() initialization'ı önemlidir; çünkü TXLSChartSeriesInfo managed field'lar içerir ve plain Boolean üyeleri aksi halde stack garbage olur:
var
Wb: TXLSWorkbook;
Series: array [0..1] of TXLSChartSeriesInfo;
begin
Wb := TXLSWorkbook.Create;
try
Wb.Sheets.Add.Name := 'Data';
Series[0] := Default(TXLSChartSeriesInfo); // bu record'u asla FillChar'lama
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, red low byte'tadır
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; // açık RGB yok: flatten edilir
Series[1].Style.FillThemeColor := 8; // accent5
Wb.Sheets.AddChartSheet('Themed', xlsChartTypeColumn, '', '', '', Series);
Wb.SaveAs('themed.xls');
finally
Wb.Free;
end;
end;
Geri okuma, HotXLS chart inspection'ının geri kalanının kullandığı aynı chart model üzerinden geçer. GetChartModel, sizin serbest bıraktığınız owned bir TXLSChartModel döndürür; her TXLSChartSeries, HasFillTheme, FillThemeColor ve FillThemeTint sunar; ayrıca FillRgb, OPT1 fillColor'dan decode edilir ve bu renk o series için AreaFormat rengine göre önceliklidir. Aynı üç değer canonical semantic snapshot'a da SolidFillThemeSet, SolidFillThemeColor ve SolidFillThemeTint olarak ulaşır; böylece bir workbook diff theme değişimini açıklanamayan RGB drift'i değil theme değişimi olarak görür. XLSX tarafından geliyorsanız bu, HotXLS'in Delphi'de Excel chart, image ve drawing kılavuzunda anlatılan style'ların binary-format karşılığıdır:
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;
Binary XLS'teki theme fill neyi vaat etmez?
Dürüstçe üç sınır vardır. Birincisi ve bu kodu denetleyen herkes için en önemlisi: yerel corpus'taki hiçbir sample file GelFrame record'ı taşımıyor. Conditional-formatting sample'daki byte pair 66 10'un on bir tekrarı record boundary olmayan yerlerde duruyor ve full-stream record dump sıfır hit buluyor. Burada açıklanan bit layout specification'dan türetildi, ardından üç şekilde sabitlendi: builder output üzerinde decode symmetry, synthetic $1066 payload'ını doğrudan decoder'a veren hand-built byte test'leri ve exact flattened RGB assertion. Bu, yakalanmış bir Excel file'dan daha zayıf bir kanıt biçimidir ve aksini ima etmek yerine bunu söylemek gerekir. İkincisi, theme-only fill için flattening workbook'tan okunan bir theme part değil built-in default Office theme table kullanır; binary XLS, packaged XLSX'in taşıdığı anlamda bir theme part taşımadığı için workbook'un kendi theme'i flat color'ı sürsün istiyorsanız FillRgb'yi kendiniz sağlayın. Üçüncüsü, decoder yalnızca bir series block içindeki GelFrame'i kabul eder; aynı record chart area veya axis frame üzerinde görünebilir ve orada kabul edilirse background fill sessizce series'e atanır, bu nedenle onlar yok sayılır. $08000000 flag'i olmayan bir fillColorExt de plain extended color sayılır ve asla HasFillTheme'i ayarlamaz. Chart'ın XLSX dünyasında yazıldığı ve yalnızca aradan geçtiği workbook'larda ChartML'i kaybetmeden Excel chart'larını düzenleme preservation path'i daha güvenli yoldur; bu record'ların oturduğu container ise Delphi'de OLE2 compound file'larını COM IStorage olmadan okuma yazısında kapsanır
Theme renkli chart fill'leri, GelFrame encoder ve decoder'ı ve full BIFF8 chart substream builder'ı, Delphi ve C++Builder için HotXLS Delphi spreadsheet component'in parçasıdır; Excel kurulu olmadan XLS, XLSX ve ODS okur ve yazar