Teknik Makale

Delphi'de TDataSet ve Raporları PDF'e Aktarma

Delphi'de bir TDataSet veya raporu PDF'e aktarmak için losLab PDF Library iki yol sunar. PDFlibTableExport herhangi bir TDataSet'i — bir FireDAC sorgusunu, bir ClientDataSet'i, bellek içi bir tabloyu — sayfalandırılmış bir PDF tablosuna uyarlar; Addons altındaki üç köprü ise hazırlanmış bir FastReport, QuickReport veya ReportBuilder raporunu aynı PDF yazıcısına iletir. Her ikisi de yazıcı sürücüsü ve görünür bir pencere olmadan gerçek bir PDF oluşturur

İki sorun benzer görünse de öyle değildir. Bir DBGrid veya ham sorgu sonucunun kendine ait bir düzeni yoktur, bu nedenle bunu dışa aktarmak bir düzen tasarlamak anlamına gelir: sütunlar, başlıklar, sayfa sonları. Bir FastReport veya ReportBuilder belgesi zaten tasarlanmış bir düzen taşır, dolayısıyla bunu dışa aktarmak başkasının çizim komutlarını PDF alanında aslına sadık kalarak yeniden oynatmak anlamına gelir. losLab PDF Library, hata modları farklı olduğu için bu konuları ayrı birimlerde tutar; bu makalenin geri kalanı her birini ve nerede güvenilir olmaktan çıktıklarını incelemektedir

Delphi'de bir TDataSet'i PDF'e nasıl aktarırsınız?

PDFlibTableExport tek bir çağrıda veri kümesini bir tabloya dönüştürür. Dışa aktarıcı alan listesini bir kez tarar, ikili blob alanlarını otomatik olarak atlar — ftBlob, ftGraphic, ftBytes ve benzerlerinin kullanışlı bir hücre metni yoktur —, sayısal sütunları sağa hizalar ve stillendirilmiş bir başlık bandı üzerine isteğe bağlı bir zebra şeridi çizer. Arka planda ızgarayı CreateTable ile oluşturur, hücreleri SetTableCellContent ile doldurur ve kendiniz yöneteceğiniz genel Tablo API'si ile aynı olan DrawTableRows ile işler. Kolaylık sarmalayıcısı PDFlibExportDataSet sizin için milimetre ve sol üst başlangıç noktasını yapılandırır, böylece çağıranın yalnızca bir sayfa sağlaması gerekir

uses
  Data.DB, FireDAC.Comp.Client, FireDAC.Stan.StorageBin,
  PDFlibrary, PDFlibTableExport;

var
  MemTable: TFDMemTable;
  PDF: TPDFlib;
  Options: TPDFlibTableExportOptions;
begin
  MemTable := TFDMemTable.Create(nil);
  PDF := TPDFlib.Create;
  try
    MemTable.LoadFromFile('customer.FDS');   // any TDataSet works here

    PDF.SetOrigin(1);
    PDF.SetMeasurementUnits(1);              // millimetres
    PDF.SetPageSize('A4');
    PDF.AddStandardFont(4);

    Options := DefaultTableExportOptions;
    Options.Title := 'Customers';
    Options.ColumnWidth := 32;               // 11 fields fit on A4 at 32 mm
    Options.RepeatHeader := True;            // redraw the header on each page

    PDFlibExportDataSet(PDF, MemTable, 'customers.pdf', Options);
  finally
    PDF.Free;
    MemTable.Free;
  end;
end;

Otomatik blob atlama bir varsayılandır, bir kısıtlama değildir. Alan başına denetime ihtiyaç duyduğunuzda — özel bir sütun etiketi, daha dar bir genişlik veya blob olmayan dahili bir anahtar sütununun çıkarılması gibi — doğrudan bir TPDFlibTableExporter oluşturun ve alan başına bir kez tetiklenen ve size değiştirilebilir bir özellik sunan OnFieldFilter olayına bağlanın. Alanı çıkarmak için Include değerini False yapın veya varsayılanları geçersiz kılmak için ColumnWidth ve DisplayLabel özelliklerini ayarlayın. Bu olay, sayfanın aşırı büyümesini istemediğiniz geniş bir not (memo) sütununu hariç tuttuğunuz yerdir

Sayfalandırma: çizim durumunu sayfalar arasında aktarma

DrawTableRows sayfalandırma durumunu taşır og bunun sözleşmesini anlamak oyunun tamamıdır. İlk satır ve son satır ile çağırırsınız; 1'den küçük bir son satır geçmek, verdiğiniz yükseklikle sınırlı olarak tablonun sonuna kadar çizilmesi anlamına gelir. İşlev fiilen çizdiği yüksekliği döndürür ve GetTableLastDrawnRow sığan son satırı raporlar. Bu çift, bir sayfadan diğerine aktardığınız durumdur: çizilen son satır toplamın gerisinde kaldığında, yeni bir sayfa açar ve ondan sonraki satırdan devam edersiniz. Hiçbir şey yeniden ölçülmez — devam çağrısı bir önceki çağrının kaldığı yerden aynen devam eder

// How the exporter continues a long table across pages
PageHeight := PDF.PageHeight;
Y := PageHeight - Options.Top;
Row := 1;
while Row <= TotalRows do
begin
  DrawHeight := Y - Options.BottomMargin;
  // Last row = 0 means "draw to the end", bounded by DrawHeight
  PDF.DrawTableRows(TableID, Options.Left, Y, DrawHeight, Row, 0);
  LastDrawn := PDF.GetTableLastDrawnRow(TableID);
  if LastDrawn >= TotalRows then
    Break;                       // the whole table fit on this page
  if LastDrawn < Row then
    Break;                       // safety: no forward progress, bail out
  PDF.NewPage;
  Row := LastDrawn + 1;          // continue from the first undrawn row
  Y := PageHeight - Options.Top;
end;

Döngü aynı zamanda yinelenen başlığın bulunduğu yerdir. Her devam sayfasında dışa aktarıcı isteğe bağlı olarak veri satırlarından önce satır 1'i — başlığı — tekrar çizer ve altındaki gövdeyi kaydırmak için ilk DrawTableRows çağrısı tarafından döndürülen yüksekliği kullanır. DrawTableRows'un bir sonraki satır koordinatı yerine bir yükseklik döndürmesinin nedeni budur: dönüş değeri, yinelenen bir başlığı ve devam eden gövdeyi herhangi birinin boyutunu sabit kodlamadan üst üste yığmanıza olanak tanıyan şeydir

Bir FastReport veya ReportBuilder raporunu PDF'e nasıl aktarırsınız?

Rapor köprüleri tam tersi bir yaklaşım sergiler: asla bir düzen oluşturmazlar, mevcut olanı yeniden oynatırlar. Her köprü kendi motoruna motorun yerel dışa aktarma bağlantı noktasından bağlanır. PDFlibFRExport, TfrxCustomExportFilter'ı alt sınıflara ayırır ve FastReport'un not (memo), resim, şekil ve çizgi nesnelerini alarak her birini bir PDFlibPas primitiğine çevirir. PDFlibRBDevice, TppFileDevice'ı alt sınıflara ayırır og ReportBuilder'ın DrawCommand listesini tarar. PDFlibQRExport tamamen üçüncü bir yol izler ve bir sonraki bölümde ele alınmaktadır. Her üçü de arka planda (headless) çalışır; bu da sunucuda bunlara yönelmenin yegane nedenidir

Arka planda (headless) çalışmak zahmetsiz değildir ve FastReport buna ibretlik bir örnektir. TfrxReport.Export, filtrenin ShowDialog özelliğine başvuran önizleme sayfaları üzerinden yönlendirilir ve bu özellik varsayılan olarak True değerini devralır. Etkileşimli masaüstü olmayan bir makinede, modal iletişim kutusu bir iptal sonucu döndürür ve dışa aktarma sessizce başarısız olur. Export'u çağırmadan önce ShowDialog'u False olarak ayarladığınızda rapor sessizce işlenir. ReportBuilder da paralel anahtarlara sahiptir — AllowPrintToFile ve ShowPrintDialog — ve her şeyi Prepare aracılığıyla yürüten QuickReport'un ise iletişim kutusunu engellemeye hiç ihtiyacı yoktur

var
  Exporter: TPDFlibFRExport;
begin
  Report.PrepareReport;                  // build the pages first
  Exporter := TPDFlibFRExport.Create(nil);
  try
    Exporter.FileName := 'invoice.pdf';
    Exporter.ShowDialog := False;        // headless: skip the modal, no cancel
    Report.Export(Exporter);
  finally
    Exporter.Free;
  end;
end;

Üç motor, üç koordinat sistemi

Her köprü kendi koordinat aritmetiğini yapar, çünkü her motor dünyayı farklı ölçer. PDFlibFRExport, FastReport nesne konumlarını 96 PPI'da piksel olarak ele alır ve milimetreye ulaşmak için 96/25.4 ile ölçeklendirir. PDFlibRBDevice, ReportBuilder'ın çizim komutlarını milimetrenin binde biri cinsinden okur ve 1000'e böler; ayrıca kendini ppRegisterDevice aracılığıyla kaydeder, bu nedenle ppReport.DeviceType değerini 'PDFlibPas' olarak ayarlayıp Print'i çağırmak, çıktıyı onun üzerinden yönlendirmek için yeterlidir. PDFlibQRExport hiç koordinat matematiği yapmaz: hazırlanmış bir QuickReport sayfası zaten bir EMF meta dosyasıdır, bu nedenle köprü her sayfayı bir akışa kaydeder ve ImportEMFFromStream'e besler, böylece kitaplığın cihaz bağlamı ve meta dosyası işleme yolu her glifi ve çizgiyi yerleştirir. FastReport ve ReportBuilder köprüleri ise buna karşılık yerel vektör çizim primitifleri ve yerel metin yayar

var
  PDF: TPDFlib;
begin
  PDF := TPDFlib.Create;
  try
    PDF.SetOrigin(1);
    // Prepares the report and appends every page in one call; the bridge
    // turns each page's EMF into PDF through ImportEMFFromStream.
    PDFlibQRExportReport(PDF, QuickRep1, 'ledger.pdf');
  finally
    PDF.Free;
  end;
end;

Doğruluğun bittiği yer

Bir iş akışını bu köprülere teslim etmeden önce sınırları bilin. Veri kümesi dışa aktarıcısı ikili sütunları sessizce atlar, bu nedenle gömülü bir resmi göstermesi gereken bir raporun düz bir tablodan farklı bir yaklaşıma ihtiyacı vardır. QuickReport yolu, EMF üzerinden gittiği için metni vektör gliflerine dönüştürür — sayfa doğru görünür ancak seçilebilir veya aranabilir metin ile erişilebilirlik için etiketli PDF yapısı taşımaz. FastReport ve ReportBuilder gerçek metni korur, ancak yazılan işleyiciler yaygın görünümleri — not (memo), resim, şekil, çizgi — kapsar ve sınırlayıcı bir kutuya geri döner veya sıra dışı olanları atlar; bu nedenle zengin metin, barkodlar veya degrade dolgulara dayanan bir rapor ayrıntı kaybedecektir. Bunların hiçbiri bir kusur değildir; bir çeviri katmanının dürüst sınırıdır

Operasyonel bir uyarı diğerlerinden daha önemlidir. Üç motorun hiçbiri losLab PDF Library ile birlikte gelmez ve köprüler kasıtlı olarak ana derlemeden hariç tutulmuştur — yalnızca arama yolunda zaten FastReport VCL 6.x, QuickReport 8 veya ReportBuilder 20 bulunan bir projeden derlenirler. Hedefledikleri meta dosyası ve DrawCommand yüzeyleri birkaç ana motor sürümü boyunca kararlı kalmıştır, ancak sürüm eşleşmesi size aittir. Bunu doğru yaptığınızda, iki yol geçici bir DBGrid dökümünden tasarlanmış bir faturaya kadar tüm yelpazeyi kapsar. Tablo dışa aktarıcısı ve rapor köprüleri, Delphi ve C++Builder için losLab PDF Library ile birlikte gönderilir