Artikel Teknis

Membuka dan Menyimpan Spreadsheet ODS di Delphi dengan HotXLS

Sebuah backend pelaporan Delphi yang sudah bertahun-tahun memancarkan .xlsx mendapat sebuah persyaratan baru: aturan pengadaan seorang pelanggan sektor publik mewajibkan output OpenDocument Spreadsheet, dan para analis di akun itu mengirim kembali edit mereka sebagai file .ods yang disimpan dari LibreOffice. Jadi sekarang kode yang sama harus menulis ODS dan membacanya. HotXLS, library spreadsheet Object Pascal native milik losLab untuk Delphi dan C++Builder, menangani kedua arah tersebut tanpa Excel atau LibreOffice terpasang di mana pun. Yang tidak dilakukannya adalah membuat kedua arah itu simetris. Ekspor membawa jauh lebih banyak daripada yang dipulihkan import, dan sebuah tim yang mengira sebaliknya akan menyaksikan formula dan formatting menguap di suatu tempat antara revisi pelanggan dan laporan berikutnya, tanpa sebuah error untuk ditelusuri penyebabnya

Dukungan ODS berada pada facade XLSX, bukan facade XLS

HotXLS mengirimkan dua hierarki class yang independen dalam satu paket: TXLSWorkbook di unit lxHandle untuk file .xls BIFF8 biner, dan TXLSXWorkbook di unit lxHandleX untuk paket .xlsx OOXML. Setiap titik masuk OpenDocument - OpenODS, SaveAsODS, GetODSSheetNames - menggantung pada TXLSXWorkbook. Penempatan ini bukan sembarangan. Sebuah paket ODS, sebagaimana dispesifikasikan dalam OASIS ODF 1.3, adalah sebuah arsip zip yang membawa sebuah anggota mimetype, sebuah manifest, dan sebuah badan content.xml, yang menjadikannya sepupu struktural dari zip OOXML; BIFF8 adalah sebuah stream rekaman biner era 1990-an yang tidak punya kesamaan apa pun dengannya

Penempatan itu punya sisi praktis: sebuah workbook .xls lawas tidak bisa langsung menjadi .ods dalam satu pemanggilan. Anda menjembatani konten BIFF ke dalam model XLSX terlebih dahulu, dengan SaveXLSWorkbookAsXLSX dari unit lxXlsxExport, membuka ulang hasilnya lewat TXLSXWorkbook, lalu mengekspor dari sana. Jembatan itu tidak lossless, dan layak mengetahui celahnya sebelum Anda membangun sesuatu di atasnya. Ia menyalin nilai, formula, format angka, font, fill, dan lebar kolom. Ia membuang border, range gabungan, comment, chart, dan conditional formatting. Sebuah sumber .xls dengan formatting berat akan sampai di ODS terlihat lebih polos daripada saat ia berangkat, dan itu adalah sifat dari jembatan itu, bukan dari ODS writer-nya

Deteksi pada sisi import bersifat otomatis. Method Open biasa mengenali sebuah paket ODS lewat anggota mimetype-nya, kembali ke sebuah pemeriksaan content.xml level-atas ketika anggota itu tidak ada, sehingga sebuah jalur kode generik "buka apa pun yang diunggah pengguna" tidak membutuhkan pengendusan ekstensinya sendiri. Setelah dibuka, properti SourceFormat melaporkan cabang mana yang terpicu

Diagram tata letak kelas HotXLS di Delphi di mana setiap titik masuk ODS hidup di TXLSXWorkbook dan jembatan SaveXLSWorkbookAsXLSX membawa konten .xls BIFF8 menyeberang
Setiap entry point OpenDocument bergantung pada TXLSXWorkbook, dan .xls legacy mencap ODS hanya melalui bridge BIFF ke XLSX yang lossy

Mengekspor ke ODS dengan TODSExportOptions

Pemanggilan ekspornya sendiri hanya satu baris; objek options di sekelilingnya membawa keputusan-keputusan yang belakangan akan ditanyakan seorang reviewer:

var
  Book: TXLSXWorkbook;
  Opts: TODSExportOptions;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('quarterly-report.xlsx');
    Opts := TODSExportOptions.Create;        // caller memiliki dan membebaskan objek ini
    try
      Opts.Generator := 'ReportService 4.2'; // override meta:generator
      Opts.IncludeCharts := True;
      Opts.IncludeImages := True;
      Book.SaveAsODS('quarterly-report.ods', Opts);
    finally
      Opts.Free;
    end;
  finally
    Book.Free;
  end;
end;

Objek options ini dimiliki oleh caller. HotXLS tidak akan membebaskannya, itulah sebabnya try..finally bagian dalam itu ada di sana dan bukan sekadar opsional. Dua properti yang mengubah output, bukan sekadar melabelinya, layak dilihat lebih dekat. Menyetel IncludeCharts := False melakukan lebih dari sekadar menyembunyikan chart: ia melucuti sub-dokumen chart dan entri manifest-nya dari paket, yang justru menjadi hal yang Anda inginkan ketika konsumennya adalah sebuah data pipeline yang akan tersandung olehnya. Generator meng-override string meta:generator ODF, yang jika tidak akan berbunyi HotXLS/<version>; override ini ketika tooling downstream mengambil sidik jari produsen file untuk mengarahkan dukungan. Jika tidak ada dari itu yang berlaku, lewati objek options sepenuhnya. Memanggil SaveAs(FileName, xlsxOpenDocumentSpreadsheet) sama dengan SaveAsODS dengan default, dan overload stream pada keduanya memungkinkan Anda menulis paket itu langsung ke dalam sebuah response HTTP tanpa file sementara

Apa yang dibaca jalur import - dan apa yang sengaja dilewatinya

Baca bagian ini dengan cermat sebelum Anda menjanjikan siapa pun fidelitas round-trip. Import ODS dalam HotXLS memang secara sengaja adalah sebuah jalur ringan. Ia mempertahankan nilai cell skalar dan hasil cache yang dibawa setiap formula saat penyimpanan, dan ia mengembangkan baris dan kolom yang berulang ke dalam grid. Ia tidak membawa serta style, ekspresi formula ODS, atau drawing

Pilihan soal formula adalah yang paling mungkin menggigit, dan itu dibuat dengan sengaja. Sebuah cell ODF menyimpan dua hal berdampingan: ekspresi formulanya, ditulis dalam dialek OpenFormula yang didefinisikan dalam ODF 1.3 Part 4, dan nilai terakhir yang dihitung aplikasi yang memproduksinya. Menerjemahkan OpenFormula ke dalam sintaks formula Excel adalah masalah konversi-dialeknya sendiri, dengan kasus tepi sungguhan di seputar kosakata fungsi, sintaks referensi, dan model error. Membaca nilai cache-nya sebagai gantinya menghindari seluruh kelas kesalahan-terjemahan diam-diam itu, sehingga angka yang Anda import persis sama dengan angka yang terakhir dilihat pengirimnya. Biayanya adalah angka itu tiba sebagai angka, bukan sebagai formula hidup yang menghasilkannya

Mode kegagalan yang perlu dirancang di sekitarnya mengikuti secara langsung: sebuah spreadsheet yang totalnya benar saat terakhir disimpan LibreOffice akan ter-import dengan angka yang benar, tetapi angka-angka itu sekarang menjadi konstanta. Edit sebuah cell input, hitung ulang, dan tidak ada yang bergerak - formulanya sudah hilang, hanya hasil akhirnya yang tersisa. Jika workflow membutuhkan formula hidup setelah import, bangun ulang secara programatik dari aturan bisnis Anda sendiri lewat Cell.Formula, yang pada facade XLSX menerima ekspresi tanpa tanda sama-dengan di depan

Merancang di seputar round-trip yang asimetris

Ekspor merender dari model workbook in-memory yang lengkap: nilai, style, dan, jika Anda memintanya, chart dan gambar. Import hanya mengembalikan nilai. Jadi lintasan .xlsx ke .ods berfidelitas tinggi, dan lintasan .ods ke .xlsx membawa kembali nilai dan hasil cache tetapi tanpa styling dan tanpa formula hidup. Rangkaikan keduanya, dan asimetrinya berlipat ganda. Sebuah siklus penuh .xlsx ke .ods ke .xlsx menulis segalanya dengan setia saat berangkat dan kehilangan style serta formula saat kembali, meski tidak ada yang salah pada langkah mana pun

Diagram round trip ODS HotXLS yang asimetris dari Delphi: ekspor kesetiaan-penuh dari model workbook in-memory dan impor hanya-nilai yang menyisakan formula sebagai konstanta
Ekspor merender model penuh di memori sementara impor mengembalikan nilai dan hasil cache, sehingga siklus penuh .xlsx ke .ods ke .xlsx diam-diam menjatuhkan style dan formula hidup
Book := TXLSXWorkbook.Create;
try
  Book.Open('vendor-revision.ods');          // format terdeteksi otomatis
  if Book.SourceFormat = xlsxOpenDocumentSpreadsheet then
  begin
    // Nilai dan hasil cache formula ada setelah sebuah import ODS;
    // style dan formula hidup tidak. Bangun ulang apa pun yang
    // dibutuhkan pipeline downstream sebelum menyimpan.
    Book.Sheets[0].Cells[2, 5].Formula := 'SUM(B2:D2)';
    Book.SaveAs('vendor-revision.xlsx');
  end;
finally
  Book.Free;
end;

Pola arsitektural yang muncul dari ini: perlakukan file .ods yang masuk sebagai umpan data, bukan sebagai dokumen untuk diedit di tempat. Pertahankan workbook kanonik dalam .xlsx, baca nilai dari revisi pelanggan, dan pancarkan ODS segar sesuai permintaan dari salinan kanoniknya. Verifikasi berlaku di kedua kubu - buka file yang diekspor di LibreOffice Calc, konsumen ODF acuan, dan di Excel, yang sudah membaca ODS selama bertahun-tahun tetapi tidak sepakat dengan LibreOffice di batas-batas dukungan chart dan style. Jumlah sheet, segelintir cell kunci, dan kehadiran chart membentuk sebuah smoke check yang cukup per profil ekspor

Melakukan triase pada sebuah file ODS sebelum berkomitmen pada sebuah import

Ketika sebuah endpoint menerima unggahan, mendaftar nama sheet jauh lebih murah daripada sebuah parse penuh dan menangkap kejutan struktural sejak dini:

Diagram gerbang triase unggahan HotXLS di Delphi di mana GetODSSheetNames menolak paket ODS yang tak terbaca dan sheet yang hilang sebelum impor penuh berjalan
Probe GetODSSheetNames berbiaya jauh lebih murah daripada parse penuh dan menangkap kegagalan sheet ter-rename selagi error masih bisa menyebut nama file
Names := TStringList.Create;
Book := TXLSXWorkbook.Create;
try
  if Book.GetODSSheetNames('incoming.ods', Names) <= 0 then
    raise Exception.Create('not a readable ODS package');
  if Names.IndexOf('Data') < 0 then
    raise Exception.Create('revision is missing the Data sheet');
finally
  Book.Free;
  Names.Free;
end;

Konvensi nilai baliknya menyandung orang: pemanggilan HotXLS umumnya mengembalikan sebuah jumlah positif atau 1 saat berhasil dan -1 saat gagal, mengosongkan daftarnya ketika gagal, sehingga uji dengan <= 0, bukan membandingkan terhadap satu nilai positif tertentu. GetODSSheetNames tidak mereset maupun mengisi instance workbook-nya, sehingga satu objek probe tunggal bisa memeriksa seluruh direktori file yang masuk. Pemeriksaan struktural seperti ini menangkap kegagalan dunia-nyata yang paling umum - seorang analis mengganti nama atau menghapus sebuah sheet sebelum mengirim kembali revisinya - di gerbangnya, tempat pesan error masih bisa menyebutkan nama file dan sheet yang hilang, alih-alih muncul sebagai sebuah referensi nil tiga lapis lebih dalam

Jika Anda membangun sebuah pipeline konversi yang lebih luas di sekitar ini, pola workbench audit dan konversi workbook menunjukkan cara menginventarisasi fitur sebuah file sebelum memilih format target, dan panduan performa workbook besar menjaga ekspor batch tetap berada dalam batas memori yang masuk akal

HotXLS adalah sebuah library spreadsheet native untuk Delphi dan C++Builder dengan kode sumber lengkap; daftar fitur lengkap dan detail lisensinya ada pada halaman produk HotXLS Delphi Component