Tehnički članak

Izvoz Excel radnih listova u PDF u Delphi-ju bez Office-a

Da biste izvezli Excel radni list u PDF iz Delphi-ju ili C++Builder-a bez instaliranog Office-a, pozovite SaveAsPDF na HotXLS radnom listu ili radnoj svesci (workbook). HotXLS je izvorna VCL Excel komponenta, a njen izvoz radnog lista u PDF renderuje popunjeni opseg (used range) lista u samostalnu PDF 1.4 datoteku, preslikavajući širine kolona u Excelu i prikazani tekst ćelija na A4 stranice sa automatskim prelomom na više stranica (pagination). U renderovanju ne učestvuje kopija Excela, drajver štampača niti bilo koji spoljni DLL

HotXLS tretira PDF kao format za prezentaciju podataka, srodan njenim putanjama za izvoz u CSV, TSV i HTML, a ne kao motor za vizuelno renderovanje sa preciznošću u piksel. Izvoz reprodukuje tabelarni sadržaj i raspored: vrednosti koje vidite u mreži, proporcije kolona i linije mreže (gridlines) oko ćelija. Taj okvir je važan jer unapred postavlja iskrena očekivanja, a ostatak ovog članka govori o tome gde se tačno nalazi ta granica

Kako konvertovati Excel u PDF u Delphi-ju bez Office-a?

Najkraći put je jedan poziv. Klasa TXLSWorksheet izlaže SaveAsPDF(FileName) i SaveAsPDF(Stream), a TXLSWorkbook izlaže iste dve preopterećene metode (overloads) koje prosleđuju poziv na aktivni list. Svaka od njih vraća Integer, 1 u slučaju uspeha i -1 u slučaju neuspeha, tako da se možete granati na osnovu rezultata bez potrebe da poziv obmotavate sopstvenim rukovanjem izuzecima

uses
  lxHandle;

var
  Book: TXLSWorkbook;
begin
  Book := TXLSWorkbook.Create;
  try
    Book.Open('sales-june.xlsx');
    // Render the active sheet's used range straight to report.pdf.
    if Book.SaveAsPDF('report.pdf') = 1 then
      Writeln('PDF written');
  finally
    Book.Free;
  end;
end;

Kada pozovete SaveAsPDF na radnom listu, HotXLS izvozi UsedRange tog lista, što je granični okvir svake popunjene ćelije. Preopterećena metoda radne sveske je pogodnost koja delegira poziv na aktivni list, tako da izveštaj sa više listova ili prolazi kroz listove koji su mu potrebni u petlji, ili bira aktivni list pre poziva. Ne postoji režim za celu radnu svesku (sve u jednom prolazu) koji spaja svaki list: svaki poziv renderuje jedan list ili jedan opseg

Kako HotXLS pretvara popunjeni opseg u PDF stranice?

HotXLS prolazi kroz popunjeni opseg ćeliju po ćeliju i raspoređuje ga kao numerisanu mrežu. Geometrija kolona dolazi direktno iz Excela: svaka širina kolone se konvertuje iz Excelove jedinice širine u tipografske tačke (points), sa minimalnim pragom tako da se nijedna kolona ne skupi na nulu. Kada su kolone zajedno šire od upotrebljive stranice, HotXLS ih ravnomerno smanjuje kako bi stale; kada već staju, ostavlja ih na njihovoj prirodnoj širini umesto da ih rasteže preko celog lista

Redovi zatim teku niz stranicu sve dok sledeći ne bi prešao donju marginu, u kom trenutku HotXLS otvara novu stranicu i nastavlja, tako da se list od hiljadu redova sam prelama na stranice bez ikakvog vašeg vođenja evidencije o prelomima stranica. Svaka ćelija štampa svoj FormattedText, odnosno prikazani string proizveden nakon primene formata broja, tako da se datum prikazuje onako kako ga list prikazuje, a ćelija valute zadržava svoje separatore hiljada i simbol. Tekst koji prekorači svoju kolonu se skraćuje na širinu ćelije umesto da se preliva u susednu

Kontrolisanje orijentacije, margina i veličine fonta

Za sve što prevazilazi podrazumevani raspored, direktno upravljajte izvoznikom. Klasa TXLSPDFExport se nalazi iza pomoćnih funkcija u jednoj liniji, i objavljuje tri osobine: Orientation (xlsPdfPortrait ili xlsPdfLandscape, portret podrazumevano), Margin (tačke, podrazumevano 40) i FontSize (tačke, podrazumevano 9). Podesite ih pre nego što pozovete SaveAsPDF i ceo dokument će ih primeniti

uses
  lxHandle, lxPDF;

var
  Book: TXLSWorkbook;
  Exporter: TXLSPDFExport;
begin
  Book := TXLSWorkbook.Create;
  try
    Book.Open('wide-ledger.xlsx');
    Exporter := TXLSPDFExport.Create;
    try
      Exporter.Orientation := xlsPdfLandscape;  // fit wide sheets on the page
      Exporter.Margin := 28;                     // tighter page margin, in points
      Exporter.FontSize := 8;                    // denser rows
      Exporter.SaveAsPDF(Book.ActiveSheet, 'ledger.pdf');
    finally
      Exporter.Free;
    end;
  finally
    Book.Free;
  end;
end;

Klasa TXLSPDFExport takođe prihvata opseg umesto celog lista kroz metodu SaveAsPDF(Range: IXLSRange; ...), što je način na koji izvozite zbirni blok iz lista koji takođe sadrži proračune na brzinu. Ta preopterećena metoda sa opsegom je ista ona na koju se pomoćnik radnog lista interno oslanja, tako da se raspored, skaliranje i prelom stranica ponašaju identično bez obzira na to da li joj date pun popunjeni opseg ili ručno izabranu selekciju

var
  Book: TXLSWorkbook;
  Exporter: TXLSPDFExport;
  Summary: IXLSRange;
begin
  Book := TXLSWorkbook.Create;
  try
    Book.Open('quarter.xlsx');
    // Export only the summary block, not the scratch columns beside it.
    Summary := Book.Sheets[1].Range['A1', 'F20'];
    Exporter := TXLSPDFExport.Create;
    try
      Exporter.SaveAsPDF(Summary, 'summary.pdf');
    finally
      Exporter.Free;
    end;
  finally
    Book.Free;
  end;
end;

Zašto HotXLS ne ugrađuje fontove: kompromis između Helvetica i WinAnsi

HotXLS piše tekst koristeći ugrađeni Helvetica font u PDF-u i WinAnsiEncoding, što je nameran izbor sa nula zavisnosti, ali i sa jednim stvarnim ograničenjem. Pošto je Helvetica jedan od standardnih 14 fontova koje svaki usaglašeni PDF čitač već poseduje, izvoznik uopšte ne ugrađuje program fonta: datoteka ostaje mala i otvara se bilo gde, bez potrebe za licenciranjem ili isporukom bilo čega uz nju. Cena tog izbora je repertoar karaktera koje stranica može prikazati

Kodni raspored WinAnsiEncoding pokriva Windows-1252 skup, tako da se latinični tekst, uobičajena interpunkcija i uobičajeni zapadnoevropski akcenti renderuju ispravno, a HotXLS smešta šačicu tipografskih karaktera, zakrivljene navodnike, en i em crtice i znak evra u njihove CP1252 pozicije. Karakteri izvan tog opsega, kao što su CJK ideogrami, ćirilica, grčki i arapski jezik, na stranici se pretvaraju u znak pitanja. Ako vaši listovi sadrže nelatinični tekst, ovaj izvoznik je pogrešan alat, i za taj posao je pogodan puni PDF renderer sa podrškom za stilove

Gde se zaustavlja izvoz radnog lista u PDF

HotXLS renderuje sadržaj i strukturu, a ne puni vizuelni stil lista. Popunjavanje ćelija (cell fills), stilovi ivica po pojedinačnim ivicama, fontovi i boje po ćelijama, grafikoni i ugrađene slike su izvan onoga što izvoznik crta; ono što dobijate je mreža teksta sa ujednačenim linijama mreže. To je isti ugovor koji poštuju izvoznici u HTML i RTF, i to je pravo rešenje za izveštaj ili predaju podataka gde brojevi nose značenje, a vizuelna tema ne

Kada je cilj stilizovan dokument u skladu sa brendom, uobičajeni šablon je da se brojevi najpre sklapanju u šablonu tabele, što detaljno pokriva radni tok generisanja izveštaja vođen šablonima. A pošto ništa od ovoga ne dodiruje COM niti pokrenutu instancu Excela, ovaj proces se uklapa u isti model automatizacije bez Office-a koji HotXLS koristi svuda: čist Pascal, spreman za primenu na serveru bez interfejsa (headless), bez potrebe za licenciranjem ili nadgledanjem interoperabilnosti

Samostalan PDF, bajt po bajt

HotXLS ručno gradi strukturu PDF datoteke i izračunava njenu tabelu unakrsnih referenci (cross-reference table) na osnovu tačnih pomaka bajtova (byte offsets), što je razlog zašto se izlaz otvara u svakom čitaču. Dok serijalizuje svaki objekat, HotXLS beleži početni pomak tog objekta pre nego što doda njegove bajtove, a zatim pomera tekuću poziciju za dužinu koju je upravo upisala, tako da tabela xref sleće precizno na svaki objekat, a startxref označava gde ta tabela počinje. Čitač koji pronađe ove pomake pomerene makar za jedan bajt odbija celu datoteku, što samostalno izračunavanje pomaka čini nosećim ugovorom izvoznika

Taj ugovor nosi jednu strogu posledicu koju vredi znati. Čvor Pages se piše prvi kao objekat 1, ali njegova lista pod-stranica nije poznata sve dok se ne emituje svaka stranica, pa ga HotXLS rezerviše kao čuvara mesta i ubacuje konačne stranice i broj pre nego što se pokrene prolaz pomaka, nikada posle. Jednom kada se izmere bajtovi objekta za xref, oni se zamrzavaju: promena jednog bajta nakon toga bi pomerila svaki sledeći pomak i pokvarila tabelu. To je mala disciplina koja obezbeđuje standardno usaglašen PDF 1.4 bez ikakve biblioteke u pozadini

Metode SaveAsPDF i klasa TXLSPDFExport prikazane ovde se isporučuju sa komponente HotXLS Delphi Excel Component, zajedno sa izvoznicima u CSV, HTML i RTF i punim API-jem radnog lista za Delphi i C++Builder