Chart series yang di-fill dengan literal RGB tidak mengikuti workbook theme. Ubah theme dan series tetap menggunakan warna lama. HotXLS menangani hal ini dalam binary XLS dengan theme-colored chart series fill: record GelFrame, 4198 atau $1066, ditulis tepat setelah AreaFormat di dalam series block dan membawa OfficeArt scheme index serta tint. Excel kemudian merender series seperti saat merender themed fill yang ditulisnya sendiri
Dari mana nomor record GelFrame berasal?
Nomor record GelFrame adalah 4198 ($1066), dan Anda tidak akan menemukannya di section spesifikasi milik record itu sendiri. [MS-XLS] 2.4.131 menjelaskan isi GelFrame, tetapi tidak seperti sebagian besar section record, bagian tersebut tidak menyatakan value rt. Chart substream ABNF juga tidak membantu: ia hanya memberi production GELFRAME = 1*2GelFrame *Continue, yang menamai record tanpa memberi nomor. Nomornya berada di record-number enumeration table, beberapa halaman jauhnya dari section yang mendokumentasikan payload. Production tersebut layak diperiksa ulang oleh siapa pun yang menulis reader: production itu mengizinkan satu atau dua record GelFrame, masing-masing dapat diikuti record Continue, sehingga parser yang mengasumsikan satu record per production akan salah menangani file yang tidak ditulisnya. HotXLS menerbitkan tepat satu GelFrame per themed series, seperti yang dihasilkan Excel untuk simple solid theme fill, dan decoder-nya memperlakukan record sebagai payload mandiri, bukan mengasumsikan count tetap
Di dalam payload GelFrame: dua property table OfficeArt
Payload GelFrame adalah dua OfficeArt property table yang berurutan: OfficeArtFOPT (disebut OPT1) diikuti OfficeArtTertiaryFOPT (OPT2). Setiap table terdiri dari property count 2 byte diikuti sejumlah entry FOPTE 6 byte, dan setiap entry adalah opid 2 byte plus op 4 byte. Bit 15 opid adalah fComplex: ketika diset, value op adalah byte length dan variable tail mengikuti fixed entry. Decoder yang mengabaikan tail akan kehilangan sinkronisasi lalu membaca opid sampah untuk semua property setelah complex property pertama
Theme fill diekspresikan oleh tiga property yang tersebar di kedua table, ditambah satu property yang menyatakan fill kind. HotXLS menulis empat property dalam 28 byte tanpa complex tail:
fillType$0180 di OPT1, diset ke 1 (msofillSolid)fillColor$0181 di OPT1, flattened RGB yang akan digambar consumer lama atau consumer yang tidak memahami themefillColorExt$019E di OPT2, base theme colorfillColorExtMod$01A0 di OPT2, tint atau shade yang diterapkan ke base
Pemisahan tersebut disengaja dalam format, bukan kecelakaan implementasi: [MS-ODRAW] 2.2.2 menjelaskan theme triple sebagai flat color ditambah base color dan modification, sehingga consumer yang memahami theme menghitung ulang fill, sementara consumer yang tidak memahami theme tetap menggambar sesuatu yang masuk akal. opid di sekitarnya mengikuti pola yang sama dan membawa numbering identik dalam edisi [MS-ODRAW] lama maupun saat ini, yang memudahkan cross-reading dua revision: fillOpacity $0182, fillBackColor $0183, fillShadeType $019C, fillBackColorExt $01A2, dan fillBackColorExtMod $01A4
Mengapa scheme index berada di red byte?
Karena OfficeArtCOLORREF ditentukan berdasarkan byte offset, bukan numeric value: red pada byte 0, green pada byte 1, blue pada byte 2, flags pada byte 3. Baca structure itu sebagai little-endian DWORD, seperti yang dilakukan setiap FOPTE op, dan red menjadi least significant byte. Contoh lineColor yang dikerjakan dalam [MS-ODRAW] menegaskannya. Jadi fSchemeIndex, yaitu flags bit E, memiliki nilai numerik $08000000, dan scheme index itu sendiri diletakkan di red byte dengan green serta blue wajib nol. Karena itu Accent1 memiliki op value $08000004, bukan $00000004 dan jelas bukan $04000000
Urutan theme index yang menolak didefinisikan spesifikasi
Spesifikasi menyebut urutan scheme index sebagai host-defined dan tidak memberikan table, sehingga byte layout saja tidak cukup untuk interoperabilitas dengan Excel. HotXLS menggunakan urutan spreadsheet theme, yang dapat round-trip dengan file Excel nyata:
- 0 = lt1, 1 = dk1, 2 = lt2, 3 = dk2
- 4 sampai 9 = accent1 sampai accent6
- 10 = hlink, 11 = folHlink
Tint dan shade: payload MSOTINTSHADE
Op fillColorExtMod adalah value MSOTINTSHADE, dan mengkodekan arah serta amount dalam satu DWORD, bukan sebagai signed fraction. Value $20000000 berarti unmodified. Lightening tint adalah $02F4 shl 16 or amount shl 8 or $10 (MSOTINT); darkening tint memiliki bentuk yang sama dengan $01F4 pada high word (MSOSHADE). Byte amount bergerak berlawanan dengan intuisi: $FF berarti unchanged dan $00 berarti full modification. HotXLS menormalisasikannya menjadi satu double bergaya DrawingML, dengan nilai positif mencerahkan dan negatif menggelapkan, memakai plus atau minus (255 - amount) / 255. Pemetaan ini tepat untuk value yang benar-benar ditawarkan Excel di UI, sehingga round-trip bersifat lossless, bukan kira-kira lossless: "Lighter 40%" yang dikenal itu memiliki amount 153, dan (255 - 153) / 255 adalah 0.4 tanpa rounding error di kedua arah. Shade dengan amount 191 kembali sebagai -64/255. Berikut encoder yang dibatasi ke rentang legal:
if Tint > 0 then // MSOTINT - lebih terang
TintOp := LongWord($02F4) shl 16 or
(LongWord(Round(255 * (1 - Tint))) shl 8) or $10
else if Tint < 0 then // MSOSHADE - lebih gelap
TintOp := LongWord($01F4) shl 16 or
(LongWord(Round(255 * (1 + Tint))) shl 8) or $10
else
TintOp := $20000000; // MSOCOLORMODUNDEFINED
Menetapkan dan membaca theme fill dari Delphi
Di sisi write, theme fill adalah dua field tambahan pada per-series style record. TXLSChartSeriesStyleInfo mendapatkan HasFillTheme, FillThemeColor, dan FillThemeTint, dan builder menerbitkan GelFrame hanya ketika HasStyle dan HasFillTheme sama-sama diset. Jika Anda juga menetapkan FillRgb eksplisit, value itu masuk ke OPT1 fillColor secara verbatim; jika tidak, HotXLS melakukan flatten pada color melalui built-in default Office theme table dengan tint diterapkan, sehingga series yang hanya memiliki theme tetap memiliki flat color yang masuk akal bagi consumer yang mengabaikan OPT2. Perhatikan inisialisasi Default(), yang penting karena TXLSChartSeriesInfo memiliki managed field dan member Boolean biasa yang sebaliknya menjadi sampah stack:
var
Wb: TXLSWorkbook;
Series: array [0..1] of TXLSChartSeriesInfo;
begin
Wb := TXLSWorkbook.Create;
try
Wb.Sheets.Add.Name := 'Data';
Series[0] := Default(TXLSChartSeriesInfo); // jangan pernah FillChar record ini
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 pada low byte
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; // tanpa RGB eksplisit: di-flatten
Series[1].Style.FillThemeColor := 8; // accent5
Wb.Sheets.AddChartSheet('Themed', xlsChartTypeColumn, '', '', '', Series);
Wb.SaveAs('themed.xls');
finally
Wb.Free;
end;
end;
Membacanya kembali melewati chart model yang sama dengan yang digunakan HotXLS untuk chart inspection lainnya. GetChartModel mengembalikan TXLSChartModel milik caller yang harus Anda free, dan setiap TXLSChartSeries mengekspos HasFillTheme, FillThemeColor, serta FillThemeTint di samping FillRgb yang didekode dari OPT1 fillColor, yang memiliki precedence atas AreaFormat color untuk series tersebut. Tiga value yang sama juga masuk ke canonical semantic snapshot sebagai SolidFillThemeSet, SolidFillThemeColor, dan SolidFillThemeTint, sehingga workbook diff melihat perubahan theme sebagai perubahan theme, bukan RGB drift yang tidak dapat dijelaskan. Jika Anda datang dari sisi XLSX, ini adalah counterpart binary-format dari styling yang dijelaskan dalam panduan HotXLS untuk Excel chart, image, dan drawing di Delphi:
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;
Apa yang tidak dijanjikan theme fill pada binary XLS?
Ada tiga batasan yang perlu dinyatakan jujur. Pertama, dan paling penting bagi siapa pun yang mengaudit code ini: tidak ada sample file dalam corpus lokal yang mengandung record GelFrame sama sekali. Sebelas kemunculan pasangan byte 66 10 dalam conditional-formatting sample berada di non-record boundary, dan full-stream record dump menemukan nol hit. Bit layout yang dijelaskan di sini diturunkan dari spesifikasi lalu dikunci dengan tiga cara, melalui decode symmetry pada builder output, byte test buatan tangan yang memasukkan payload $1066 sintetis langsung ke decoder, serta assertion atas flattened RGB. Bukti ini lebih lemah daripada Excel file hasil capture, dan layak dinyatakan demikian, bukan memberi kesan sebaliknya. Kedua, flattening untuk theme-only fill memakai built-in default Office theme table, bukan theme part yang dibaca dari workbook, karena binary XLS tidak memiliki theme part dalam pengertian XLSX packaged — jika flat color harus digerakkan oleh theme milik workbook, berikan FillRgb sendiri. Ketiga, decoder hanya menerima GelFrame di dalam series block; record yang sama dapat muncul pada chart area atau axis frame, dan menerimanya di sana akan diam-diam mengatribusikan background fill kepada series, sehingga record tersebut diabaikan. fillColorExt tanpa flag $08000000 juga diperlakukan sebagai extended color biasa dan tidak pernah mengeset HasFillTheme. Untuk workbook yang chart-nya dibuat di dunia XLSX lalu hanya melewati jalur ini, preservation path dalam mengedit Excel chart tanpa kehilangan ChartML lebih aman, dan container yang menampung record tersebut dibahas dalam membaca OLE2 compound file di Delphi tanpa COM IStorage
Theme-colored chart fill, GelFrame encoder dan decoder, serta full BIFF8 chart substream builder tersedia dalam HotXLS Delphi spreadsheet component untuk Delphi dan C++Builder, yang membaca dan menulis XLS, XLSX, dan ODS tanpa Excel terpasang