Strona PDF to lista wyświetlania obiektów, a podczas wyodrębniania obrazów iterujesz po tej liście, aby znaleźć obiekty namalowane jako mapy bitowe. Komponent PDFium udostępnia tę listę poprzez klasę TPdf, pozwalając na zapytanie, ile obiektów znajduje się na stronie, odfiltrowanie obrazów, odczytanie ich oryginalnych wymiarów w pikselach i wyciągnięcie ich danych. Możesz poprosić o obraz jako gotową do użycia mapę bitową Delphi TBitmap lub jako surowe bajty dokładnie w takiej postaci, w jakiej znajdują się w pliku PDF
Iterowanie po obiektach strony
Wszystko zaczyna się od przejścia przez stronę. Ustaw Pdf.PageNumber na stronę, którą chcesz zbadać, a następnie zrób pętlę od 0 do Pdf.PageObjectCount - 1. Każdy obiekt na stronie - ciągi tekstu, ścieżki wektorowe, cieniowanie i obrazy - ma indeks na tej liście. Właściwość Pdf.PageObjectType[I] informuje o rodzaju obiektu, który znajduje się pod tym indeksem
var
I: Integer;
begin
Pdf.PageNumber := 1;
for I := 0 to Pdf.PageObjectCount - 1 do
begin
if Pdf.PageObjectType[I] = otImage then
begin
// Extract the image at index I
end;
end;
end;
Szukasz typu otImage. Gdy go znajdziesz, komponent daje ci dwa sposoby na jego wyodrębnienie. To, który z nich wybierzesz, zależy od tego, co zamierzasz zrobić z obrazem i czy zależy ci na formacie kompresji użytym wewnątrz pliku PDF
Wyodrębnianie jako mapa bitowa Delphi
Najprostszą ścieżką jest metoda Pdf.GetImageBitmap. Przekazuje ona obiekt obrazu do silnika renderującego PDFium, prosi o zdekodowanie dowolnego formatu używanego przez PDF (JPEG, Flate, JPX, JBIG2) i zwraca standardowy obiekt TBitmap z Delphi. Mapa bitowa ma wymiary odpowiadające rzeczywistej wielkości obrazu w pikselach, a nie rozmiarowi, do jakiego został on rozciągnięty na stronie pliku PDF
var
Bitmap: TBitmap;
begin
Bitmap := Pdf.GetImageBitmap(I);
if Assigned(Bitmap) then
try
// Draw it, process it, or encode it to another format
Image1.Picture.Assign(Bitmap);
finally
Bitmap.Free;
end;
end;
Jest to właściwy wybór, gdy zamierzasz narysować obraz na płótnie, pokazać go w kontrolce TImage lub przetwarzać jego piksele w pamięci. Ponieważ otrzymujesz nieskompresowany obraz DIB, możesz go natychmiast przekazać do dowolnej procedury rysowania z biblioteki VCL. Jeśli chcesz zapisać go na dysku, możesz zakodować go do formatu PNG za pomocą klasy TPngImage z Delphi
Jeśli potrzebujesz wymiarów w pikselach przed podjęciem decyzji o wyodrębnieniu obrazu, metody Pdf.ImageWidth i Pdf.ImageHeight zwracają je bez uprzedniego dekodowania danych pikseli
Wyodrębnianie surowych bajtów
Czasami pobranie mapy bitowej jest złą odpowiedzią. Jeśli PDF zawiera obraz JPEG, pobranie go jako mapy bitowej zmusza do ponownego zakodowania go jako pliku JPEG w celu zapisania, co powoduje utratę jakości w tym procesie. Jeśli wyodrębniasz setki obrazów, aby zapisać je w archiwum, potrzebujesz surowych bajtów dokładnie w takiej postaci, w jakiej zostały skompresowane wewnątrz pliku PDF, całkowicie omijając cykl dekodowania/kodowania
Metoda Pdf.GetImageData zwraca dokładnie to, o czym mowa. Przyjmuje indeks obiektu i zwraca tablicę TBytes zawierającą użyteczne dane. Drugi parametr, RawBytes, kontroluje, czy PDFium stosuje filtry obrazu przed zwróceniem danych. Niemal zawsze potrzebujesz tutaj wartości True, aby uzyskać podstawowy format pliku (np. strumień JPEG), a nie nieskompresowane piksele
Aby dowiedzieć się, jakiego rozszerzenia użyć podczas zapisywania tych bajtów, wywołaj Pdf.GetImageFilters. Zwraca on oddzielony przecinkami ciąg znaków oznaczający filtry PDF zastosowane do obrazu. DCTDecode oznacza, że danymi wynikowymi jest format JPEG. FlateDecode zazwyczaj oznacza surowe piksele skompresowane za pomocą biblioteki zlib (często zapisywane w praktyce jako plik PNG, chociaż surowe bajty są tylko strumieniem zlib, a nie sformatowanym plikiem w formacie PNG). JPXDecode to inaczej format JPEG2000
Ścieżka z surowymi bajtami jest szybka i bezstratna dla formatów takich jak JPEG, ale przenosi ona na ciebie ciężar identyfikacji i poprawnego "opakowania" tego formatu. Jeśli filtr zwraca wartość FlateDecode, bajty w tablicy to tylko wartości dla przestrzeni barw RGB lub CMYK skompresowane za pomocą strumienia zlib. Nie możesz po prostu zapisać ich do pliku o nazwie z rozszerzeniem .png i oczekiwać, że przeglądarka go otworzy, ponieważ bajtom brakuje nagłówka specyficznego dla pliku w formacie PNG i odpowiedniej struktury podziału. W przypadku tych formatów innych niż JPEG często pragmatycznym wyborem jest powrót do metody GetImageBitmap i pozwolenie klasie TPngImage z Delphi na prawidłowe zapisanie pliku
Wyodrębnianie wszystkiego ze strony
Typowa procedura wyodrębniająca przechodzi przez obiekty, szuka typu otImage, sprawdza filtry i decyduje, jak pobrać dane. Ten przykład wyodrębnia obrazy JPEG jako surowe bajty, aby uniknąć ponownej kompresji, a wszystkie inne pobiera jako mapę bitową, by mogły zostać zapisane jako plik PNG
uses
System.IOUtils, Vcl.Graphics, Vcl.Imaging.pngimage;
procedure ExtractImages(Pdf: TPdf; const OutDir: string);
var
I, ImgCount: Integer;
Filters, FilePath: string;
Data: TBytes;
Bitmap: TBitmap;
Png: TPngImage;
begin
ImgCount := 0;
for I := 0 to Pdf.PageObjectCount - 1 do
begin
if Pdf.PageObjectType[I] <> otImage then
Continue;
Inc(ImgCount);
Filters := Pdf.GetImageFilters(I);
if Pos('DCTDecode', Filters) > 0 then
begin
// It's a JPEG. Pull the raw bytes and save as .jpg.
Data := Pdf.GetImageData(I, True);
if Length(Data) > 0 then
begin
FilePath := TPath.Combine(OutDir, Format('image_%d.jpg', [ImgCount]));
TFile.WriteAllBytes(FilePath, Data);
end;
end
else
begin
// Not a JPEG. Pull as a bitmap and let Delphi encode it to PNG.
Bitmap := Pdf.GetImageBitmap(I);
if Assigned(Bitmap) then
try
Png := TPngImage.Create;
try
Png.Assign(Bitmap);
FilePath := TPath.Combine(OutDir, Format('image_%d.png', [ImgCount]));
Png.SaveToFile(FilePath);
finally
Png.Free;
end;
finally
Bitmap.Free;
end;
end;
end;
end;
Ponieważ GetImageFilters potrafi zwrócić po kilka filtrów naraz (jak na przykład ASCII85Decode,DCTDecode), najbezpieczniejszą metodą na sprawdzenie wystąpienia określonego parametru będzie funkcja Pos
Ekstrakcja obiektów znajduje tylko te obrazy, które istnieją jako oddzielne obiekty typu XObject w pliku PDF. Nie wyodrębni ona rysunków wektorowych (takich jak logo składające się z linii i krzywych) ani tekstu. Jeśli potrzebujesz obrazu określonego regionu strony bez względu na to, jak został narysowany, jest to zadanie dla renderowania, a nie ekstrakcji. Aby dowiedzieć się, jak wyrenderować stronę do mapy bitowej, zapoznaj się z artykułem towarzyszącym poświęconym konwersji stron PDF na obrazy JPEG
Metody służące do enumeracji oraz ekstrakcji obiektów pokazane w tym dokumencie są częścią Komponentu PDFium dla środowisk Delphi i C++Builder