Teknik Makale

HotXLS Delphi Component: ODS open, save, and round-trip in Delphi

Yıllardır .xlsx üreten bir Delphi raporlama arka ucu yeni bir gereksinim alır: kamu sektöründen bir müşterinin satın alma kuralları OpenDocument Spreadsheet çıktısını zorunlu kılar ve o hesaptaki analistler düzenlemelerini LibreOffice'ten kaydedilmiş .ods dosyaları olarak geri gönderir. Şimdi aynı kodun ODS'yi hem yazması hem okuması gerekiyor. losLab'ın Delphi ve C++Builder için yerel Object Pascal elektronik tablo kütüphanesi olan HotXLS, hiçbir yerde Excel ya da LibreOffice kurulu olmadan her iki yönü de ele alır. Yapmadığı şey ise iki yönü simetrik yapmaktır. Dışa aktarma, içe aktarmanın geri kazandığından çok daha fazlasını taşır ve aksini varsayan bir ekip, formüllerin ve biçimlendirmenin müşterinin revizyonu ile bir sonraki rapor arasında bir yerde, işaret edecek hiçbir hata olmadan buharlaştığını izler

ODS desteği XLS cephesinde değil, XLSX cephesinde yaşar

HotXLS tek bir paket içinde iki bağımsız sınıf hiyerarşisi gönderir: ikili BIFF8 .xls dosyaları için lxHandle biriminde TXLSWorkbook ve OOXML .xlsx paketleri için lxHandleX biriminde TXLSXWorkbook. Her OpenDocument giriş noktası - OpenODS, SaveAsODS, GetODSSheetNames - TXLSXWorkbook'a asılıdır. Bu yerleşim keyfi değildir. OASIS ODF 1.3'te belirtildiği gibi bir ODS paketi, bir mimetype üyesi, bir manifest ve bir content.xml gövdesi taşıyan bir zip arşividir, ki bu onu OOXML zip'inin yapısal bir kuzeni yapar; BIFF8 ise ortak hiçbir yanı olmayan 1990'lardan kalma ikili bir kayıt akışıdır

Bu yerleşimin pratik bir keskin kenarı var: eski bir .xls çalışma kitabı tek bir çağrıda .ods olamaz. BIFF içeriğini önce lxXlsxExport biriminden SaveXLSWorkbookAsXLSX ile XLSX modeline köprülersiniz, sonucu TXLSXWorkbook aracılığıyla yeniden açarsınız, ardından oradan dışa aktarırsınız. Köprü kayıpsız değildir ve üzerine inşa etmeden önce boşlukları bilmeye değer. Değerleri, formülleri, sayı biçimlerini, fontları, dolguları ve sütun genişliklerini kopyalar. Kenarlıkları, birleştirilmiş aralıkları, yorumları, grafikleri ve koşullu biçimlendirmeyi düşürür. Ağır biçimlendirmeli bir .xls kaynağı, ODS'ye ayrıldığından daha sade görünerek ulaşır ve bu, ODS yazıcısının değil, köprünün bir özelliğidir

İçe aktarma tarafında tespit otomatiktir. Düz Open metodu bir ODS paketini mimetype üyesinden tanır, bu üye yoksa üst düzey bir content.xml kontrolüne geri döner, bu yüzden genel bir "kullanıcının ne yüklediğini aç" kod yolunun kendi uzantı koklamasına ihtiyacı yoktur. Açıldıktan sonra, SourceFormat özelliği hangi dalın tetiklendiğini bildirir

Delphi'de HotXLS sınıf düzeninin şeması: her ODS giriş noktası TXLSXWorkbook üzerinde yaşar ve bir SaveXLSWorkbookAsXLSX köprüsü BIFF8 .xls içeriğini karşıya taşır
Her OpenDocument giriş noktası TXLSXWorkbook'a asılır ve eski bir .xls, ODS'ye yalnızca kayıplı BIFF'den-XLSX'e köprüsü üzerinden ulaşır

TODSExportOptions ile ODS'ye dışa aktarma

Dışa aktarma çağrısının kendisi tek bir satırdır; onun etrafındaki seçenekler nesnesi, bir incelemecinin daha sonra soracağı kararları taşır:

var
  Book: TXLSXWorkbook;
  Opts: TODSExportOptions;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('quarterly-report.xlsx');
    Opts := TODSExportOptions.Create;        // çağıran buna sahiptir ve serbest bırakır
    try
      Opts.Generator := 'ReportService 4.2'; // meta:generator geçersiz kılma
      Opts.IncludeCharts := True;
      Opts.IncludeImages := True;
      Book.SaveAsODS('quarterly-report.ods', Opts);
    finally
      Opts.Free;
    end;
  finally
    Book.Free;
  end;
end;

Seçenekler nesnesine çağıran sahiptir. HotXLS onu serbest bırakmaz, bu yüzden içteki try..finally oradadır ve isteğe bağlı değildir. Çıktıyı yalnızca etiketlemek yerine değiştiren iki özelliğe daha yakından bakmaya değer. IncludeCharts := False ayarlamak grafikleri gizlemekten fazlasını yapar: grafik alt belgelerini ve manifest girdilerini paketten çıkarır, ki bu tam olarak tüketicinin onlara takılacak bir veri hattı olduğu durumda istediğiniz şeydir. Generator, aksi halde HotXLS/<version> okuyan ODF meta:generator dizesini geçersiz kılar; aşağı akış araçları desteği yönlendirmek için dosya üreticilerini parmak izi olarak tanıdığında bunu geçersiz kılın. Bunların hiçbiri geçerli değilse, seçenekler nesnesini tamamen atlayın. SaveAs(FileName, xlsxOpenDocumentSpreadsheet)'i çağırmak, varsayılanlarla SaveAsODS ile aynıdır ve ikisindeki akış aşırı yüklemeleri, paketi hiçbir geçici dosya olmadan doğrudan bir HTTP yanıtına yazmanıza izin verir

İçe aktarma yolunun okuduğu - ve kasıtlı olarak atladığı

Herhangi birine gidiş-dönüş sadakati vaat etmeden önce bu kısmı dikkatle okuyun. HotXLS'te ODS içe aktarma kasıtlı olarak hafif bir yoldur. Skaler hücre değerlerini ve her formülün kayıt zamanında taşıdığı önbelleğe alınmış sonucu korur ve tekrarlanan satırları ve sütunları ızgaraya genişletir. Stilleri, ODS formül ifadelerini ya da çizimleri taşımaz

Formül seçimi en çok ısırma olasılığı olandır ve kasıtlı olarak yapılmıştır. Bir ODF hücresi yan yana iki şey saklar: ODF 1.3 Bölüm 4'te tanımlanan OpenFormula lehçesinde yazılmış formül ifadesi ve üreten uygulamanın onun için hesapladığı son değer. OpenFormula'yı Excel formül sözdizimine çevirmek, fonksiyon kelime dağarcıkları, referans sözdizimi ve hata modelleri etrafında gerçek uç durumlara sahip kendi lehçe dönüştürme problemidir. Bunun yerine önbelleğe alınmış değeri okumak, o sessiz yanlış çeviri sınıfının tamamını atlar, bu yüzden içe aktardığınız sayılar tam olarak göndericinin son gördüğü sayılardır. Maliyeti, onların canlı, kendilerini üreten formüller olarak değil sayılar olarak gelmesidir

Etrafında tasarım yapılması gereken hata modu doğrudan buradan çıkar: LibreOffice onu son kaydettiğinde toplamları doğru olan bir elektronik tablo, doğru sayılarla içe aktarılır, ama bu sayılar artık sabittir. Bir girdi hücresini düzenleyin, yeniden hesaplayın, hiçbir şey değişmez - formül gitmiştir, yalnızca son sonucu kalır. İş akışının içe aktarmadan sonra canlı formüllere ihtiyacı varsa, bunları kendi iş kurallarınızdan, XLSX cephesinde başında eşittir işareti olmadan ifadeyi alan Cell.Formula aracılığıyla programatik olarak yeniden kurun

Asimetrik gidiş-dönüş etrafında tasarım yapmak

Dışa aktarma, tam bellek içi çalışma kitabı modelinden render eder: değerler, stiller ve isterseniz grafikler ve görüntüler. İçe aktarma yalnızca değerleri döndürür. Bu yüzden .xlsx'ten .ods'ye ayak yüksek sadakatlidir ve .ods'ten .xlsx'e ayak değerleri ve önbelleğe alınmış sonuçları geri getirir ama biçimlendirme ya da canlı formül getirmez. İkisini zincirleyin, asimetri katlanır. Tam bir .xlsx'ten .ods'ye .xlsx döngüsü, giderken her şeyi sadakatle yazar ve geri dönerken, her iki adımda da hiçbir şey yanlış gitmemiş olsa bile, stilleri ve formülleri kaybeder

Delphi'den asimetrik HotXLS ODS gidiş dönüşünün şeması: bellek içi çalışma kitabı modelinden tam-sadakatli dışa aktarma ve formülleri sabit olarak bırakan yalnızca-değer içe aktarma
Export bellek içi modelin tamamını işlerken import değerleri ile önbelleklenmiş sonuçları döndürür; tam bir .xlsx - .ods - .xlsx döngüsü bu yüzden stilleri ve canlı formülleri sessizce düşürür
Book := TXLSXWorkbook.Create;
try
  Book.Open('vendor-revision.ods');          // biçim otomatik tespit edilir
  if Book.SourceFormat = xlsxOpenDocumentSpreadsheet then
  begin
    // Bir ODS içe aktarmadan sonra değerler ve önbelleğe alınmış formül
    // sonuçları mevcuttur; stiller ve canlı formüller değil. Kaydetmeden
    // önce aşağı akış hattının neye bağlı olduğunu yeniden kurun.
    Book.Sheets[0].Cells[2, 5].Formula := 'SUM(B2:D2)';
    Book.SaveAs('vendor-revision.xlsx');
  end;
finally
  Book.Free;
end;

Bundan çıkan mimari kalıp: gelen .ods dosyalarını, yerinde düzenlenecek belgeler olarak değil, veri beslemeleri olarak ele alın. Kanonik çalışma kitabını .xlsx'te tutun, müşteri revizyonlarından değerleri okuyun ve kanonik kopyadan talep üzerine taze ODS üretin. Doğrulama her iki kampa da aittir - dışa aktarılan dosyaları, referans ODF tüketicisi olan LibreOffice Calc'ta ve yıllardır ODS okuyan ama grafik ve stil desteğinin kenarlarında LibreOffice ile anlaşmayan Excel'de açın. Sayfa sayısı, bir avuç anahtar hücre ve grafik varlığı, dışa aktarma profili başına yeterli bir duman kontrolü oluşturur

Bir içe aktarmaya bağlanmadan önce bir ODS dosyasını triyaj etmek

Bir uç nokta yüklemeleri kabul ettiğinde, sayfa adlarını listelemek tam bir ayrıştırmadan çok daha ucuzdur ve yapısal sürprizleri erkenden yakalar:

Delphi'de HotXLS yükleme triyaj kapısının şeması: GetODSSheetNames, tam içe aktarma çalışmadan önce okunamayan ODS paketlerini ve eksik sayfaları reddeder
Bir GetODSSheetNames sondası tam ayrıştırmadan çok daha az maliyetlidir ve hatanın dosyayı hâlâ adlandırabildiği sırada, yeniden adlandırılmış sayfa başarısızlığını yakalar
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;

Dönüş kuralı insanları takılmasına neden olur: HotXLS çağrıları genellikle başarıda pozitif bir sayı veya 1 ve başarısızlıkta -1 döndürür, başarısız olduklarında listeyi temizler, bu yüzden belirli bir pozitif değerle karşılaştırmak yerine <= 0 test edin. GetODSSheetNames, çalışma kitabı örneğini ne sıfırlar ne de doldurur, bu yüzden tek bir yoklama nesnesi, gelen dosyaların tüm bir dizinini denetleyebilir. Bunun gibi yapısal kontroller, en yaygın gerçek dünya hatasını - bir analistin revizyonu geri göndermeden önce bir sayfayı yeniden adlandırması ya da silmesi - hata mesajının hâlâ dosyayı ve eksik sayfayı adlandırabildiği kapıda yakalar, üç katman derinde nil bir referans olarak yüzeye çıkmak yerine

Bunun etrafında daha geniş bir dönüştürme hattı inşa ediyorsanız, çalışma kitabı denetimi ve dönüştürme çalışma tezgahı kalıbı, bir hedef biçim seçmeden önce bir dosyanın özelliklerini nasıl envanterleyeceğinizi gösterir ve büyük çalışma kitabı performansı rehberi, toplu dışa aktarmaları makul bellek sınırları içinde tutar

HotXLS, tam kaynak koduna sahip yerel bir Delphi ve C++Builder elektronik tablo kütüphanesidir; tam özellik listesi ve lisanslama ayrıntıları HotXLS Delphi Component ürün sayfasındadır