Artykuł techniczny

Twórz dokumenty PDF od podstaw w Delphi za pomocą komponentu PDFium

Generowanie plików PDF w Delphi zwykle oznacza użycie silnika raportowania lub wrappera płótna. Dobrze sprawdzają się one w przypadku faktur, ale gdy musisz złożyć dokument programistycznie - łącząc stos obrazów, stemplując numery Batesa lub budując minimalistyczny zrzut danych - ciężki projektant raportów staje na przeszkodzie. Komponent PDFium udostępnia funkcje tworzenia z silnika PDFium, pozwalając na rozpoczęcie od pustego pliku, dodawanie stron oraz rysowanie tekstu i obrazów bezpośrednio w przestrzeni użytkownika

Potok przetwarzania opiera się na komponencie TPdf. Tworzysz dokument, dodajesz strony, rysujesz na bieżącej stronie i zapisujesz. Nie ma oddzielnego obiektu "płótna"; metody rysowania należą do dokumentu i celują w dowolną stronę, którą właśnie ustawiłeś jako bieżącą

Układ współrzędnych

Zanim cokolwiek narysujesz, musisz wiedzieć, gdzie wyląduje tusz. Przestrzeń użytkownika PDF jest mierzona w punktach, gdzie 72 punkty odpowiadają jednemu calowi. Co ważniejsze, początek układu (0, 0) znajduje się w lewym dolnym rogu strony, a oś Y rośnie w górę. Jeśli jesteś przyzwyczajony do płótna VCL, w którym Y rośnie w dół od górnej krawędzi, przyswojenie tego faktu zajmie ci chwilę. Aby umieścić nagłówek pół cala od góry na stronie o długości 11 cali, nie rysujesz przy Y=36; rysujesz przy Y=756 (co równa się 11 * 72 - 36)

Rozpoczynanie dokumentu i dodawanie stron

CreateDocument alokuje nowy, pusty PDF i pozostawia komponent aktywny. Od tego momentu wywołujesz AddPage z indeksem liczonym od 1 i wymiarami w punktach. Standardowa strona A4 ma rozmiar 595 na 842 punkty; amerykański format Letter to 612 na 792. Dodanie strony nie czyni jej automatycznie celem dla rysowania, więc natychmiast ustawiasz PageNumber tak, aby wskazywał na to, co właśnie utworzyłeś

var
  Pdf: TPdf;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.CreateDocument;

    // Set metadata for the file
    Pdf.Title := 'Monthly Audit Report';
    Pdf.Author := 'Automated System';

    // Add a US Letter page and select it for drawing
    Pdf.AddPage(1, 612, 792);
    Pdf.PageNumber := 1;

    // ... drawing happens here ...

    Pdf.SaveAs('C:\out\report.pdf');
  finally
    Pdf.Free;
  end;
end;

Ustawienie Title (tytułu) i Author (autora) w tym miejscu jest opcjonalne, ale mile widziane. Pola te wypełniają okno dialogowe właściwości dokumentu w programach Acrobat lub Chrome, a wyszukiwarki indeksują je, gdy plik PDF jest hostowany w sieci Web

Rysowanie tekstu za pomocą standardowych czcionek

AddText rysuje pojedynczą linię tekstu. Przekazujesz ciąg znaków, rodzinę czcionek, rozmiar w punktach, współrzędne linii bazowej i TColor

// Draw "Audit Results" in 24pt Helvetica, black, 2 inches from top and left
Pdf.AddText('Audit Results', 'Helvetica', 24, 144, 792 - 144, clBlack);

// Draw a red subheader just below it
Pdf.AddText('Confidential', 'Helvetica-Oblique', 12, 144, 792 - 168, clRed);

Ponieważ budujesz dokument od podstaw, nie musisz osadzać plików czcionek, jeśli trzymasz się 14 standardowych czcionek PDF. Specyfikacja gwarantuje, że każda przeglądarka wie, jak renderować czcionki Helvetica, Times-Roman i Courier (wraz z ich pogrubionymi i pochylonymi wariantami). Prosząc o 'Helvetica', uzyskany plik PDF pozostaje malutki, ponieważ program czcionki nie jest w nim osadzony; urządzenie czytelnika samo dostarcza glify. Współrzędna Y, którą przekazujesz, to typograficzna linia bazowa (baseline), więc tekst rozciąga się powyżej tej współrzędnej o swoją wartość ascent i opada poniżej o wartość descent

Umieszczanie obrazów i skanów

Upuszczanie obrazu na stronę to ten sam pomysł: AddImage przyjmuje ścieżkę pliku, współrzędne X i Y lewego dolnego rogu oraz szerokość i wysokość do narysowania w punktach

// Place a logo at the top right: x=462, y=642 (leaving a 1-inch right margin)
// Draw it 100 points wide and 100 points tall
Pdf.AddImage('C:\assets\logo.png', 462, 642, 100, 100);

Obraz zostaje upuszczony przeskalowany do 100 na 100 punktów, o które prosiłeś, niezależnie od jego oryginalnych wymiarów w pikselach. Jeśli potrzebujesz, aby zachował swój współczynnik proporcji, najpierw odczytujesz z TPicture jego szerokość i wysokość w pikselach, obliczasz jednolity współczynnik skali i przekazujesz do komponentu przeskalowane granice. Szczegółowy przewodnik po tej matematyce znajduje się w artykule towarzyszącym na temat łączenia zeskanowanych obrazów w plik PDF

Specjalnie dla plików JPEG możesz pominąć potok przetwarzania grafiki VCL i osadzić skompresowane bajty bezpośrednio ze strumienia, używając AddJpegImage. Pozwala to uniknąć stratnego cyklu dekodowania/kodowania i umożliwia znacznie szybszy zapis, co czyni ten sposób preferowaną ścieżką podczas pakowania materiałów wyjściowych ze skanera

Zapisywanie wyniku

Dokument istnieje w pamięci do momentu zapisania go na dysku. SaveAs zapisuje gotowy plik PDF i zwraca wartość logiczną wskazującą na powodzenie operacji

if not Pdf.SaveAs('C:\out\report.pdf') then
  ShowMessage('Could not save the PDF. Is the file in use?');

Zawsze sprawdzaj wartość zwracaną. Ciche niepowodzenie w tym miejscu zazwyczaj oznacza, że katalog wyjściowy jest tylko do odczytu, dysk jest pełny lub plik jest aktualnie otwarty w innym programie

Ponieważ TPdf obsługuje również renderowanie i ekstrakcję tekstu, możesz od razu po tym odwrócić się i wywołać RenderPage na nowo zbudowanym dokumencie bez ponownego jego ładowania. Interfejs API do generowania dokumentów jest częścią Komponentu PDFium dla środowiska Delphi, który pokrywa wersje od Delphi 7 aż po nowoczesne wydania