Fachartikel

Aufteilen von PDF-Dokumenten in mehrere Dateien mit PDFium Delphi

· PDF-Programmierung

Das Aufteilen einer großen PDF-Datei in kleinere Dateien ist unerlässlich für die Dokumentverteilung, Archivierung und Verarbeitung. Split PDF Die Demo zeigt, wie PDF-Dokumente mit PDFium VCL in Delphi unter Verwendung verschiedener Methoden aufgeteilt werden können.

Überblick

Diese umfassende Demo bietet drei Aufteilungsmodi: einzelne Seiten, Seitenbereiche und nach Lesezeichen. Sie umfasst Fortschrittsverfolgung, detaillierte Protokollierung und anpassbare Ausgabedateibenennung.

Aufteilungsmodi

  • Einzelne Seiten – Erstellen Sie eine separate PDF-Datei für jede Seite.
  • Seitenbereiche – Aufteilen nach benutzerdefinierten Seitenbereichen (z. B. 1-5, 6-10).
  • Durch Lesezeichen. – Aufteilen an Lesezeichen, um logische Abschnitte zu erstellen.

PDFium-DLL-Anforderungen

Stellen Sie vor dem Ausführen einer PDFium VCL-Anwendung sicher, dass die PDFium DLL-Dateien installiert sind:

  • pdfium32.dll / pdfium64.dll – Standardversionen (~5-6 MB).
  • pdfium32v8.dll / pdfium64v8.dll – Mit V8 JavaScript-Engine (~23-27 MB).

Installation: Ausführen PDFiumVCL\DLLs\CopyDlls.bat als Administrator, um die DLLs automatisch in die Windows-Systemverzeichnisse zu kopieren.

Aufteilen in einzelne Seiten.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
procedure TFormMain.ProcessIndividualPages;
var
  I: Integer;
  OutputFile: string;
  OutputDir: string;
  PdfNew: TPdf;
begin
  OutputDir := GetOutputDirectory;
  UpdateProgress('Splitting into individual pages...', 0, Pdf.PageCount);
  
  PdfNew := TPdf.Create(nil);
  try
    for I := 1 to Pdf.PageCount do
    begin
      if FCancelled then
        Break;
        
      // Create new document for this page
      PdfNew.CreateDocument;
      
      // Import single page
      PdfNew.ImportPages(Pdf, IntToStr(I), 1);
      
      // Generate output filename
      OutputFile := GenerateOutputFileName(
        edtFilePattern.Text, Pdf.FileName, I);
      OutputFile := OutputDir + '\' + OutputFile;
      
      // Save the single-page PDF
      if PdfNew.SaveAs(OutputFile) then
      begin
        LogMessage(Format('Created: %s', [ExtractFileName(OutputFile)]));
        Inc(FSplitCount);
      end
      else
        LogMessage(Format('Failed to create: %s', [OutputFile]), LOG_ERROR);
        
      PdfNew.Active := False;
      
      UpdateProgress('Processing...', I, Pdf.PageCount);
    end;
  finally
    PdfNew.Free;
  end;
end;

Aufteilen nach Seitenbereichen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
procedure TFormMain.ProcessPageRanges;
var
  Ranges: TPageRanges;
  I: Integer;
  OutputFile: string;
  OutputDir: string;
  PageList: string;
  PdfNew: TPdf;
begin
  Ranges := ParsePageRanges(edtPageRanges.Text);
  if Length(Ranges) = 0 then
  begin
    LogMessage('No valid page ranges specified', LOG_ERROR);
    Exit;
  end;
  
  OutputDir := GetOutputDirectory;
  UpdateProgress('Splitting by page ranges...', 0, Length(Ranges));
  
  PdfNew := TPdf.Create(nil);
  try
    for I := 0 to High(Ranges) do
    begin
      if FCancelled then
        Break;
        
      PdfNew.CreateDocument;
      
      // Build page range string
      PageList := Format('%d-%d', [Ranges[I].StartPage, Ranges[I].EndPage]);
      
      // Import the range
      PdfNew.ImportPages(Pdf, PageList, 1);
      
      // Generate output filename
      OutputFile := Format('%s\%s_pages_%d-%d.pdf', [
        OutputDir,
        ChangeFileExt(ExtractFileName(Pdf.FileName), ''),
        Ranges[I].StartPage,
        Ranges[I].EndPage
      ]);
      
      if PdfNew.SaveAs(OutputFile) then
      begin
        LogMessage(Format('Created: %s (pages %s)',
          [ExtractFileName(OutputFile), PageList]));
        Inc(FSplitCount);
      end;
      
      PdfNew.Active := False;
      
      UpdateProgress('Processing...', I + 1, Length(Ranges));
    end;
  finally
    PdfNew.Free;
  end;
end;

Seitenbereiche analysieren

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
function TFormMain.ParsePageRanges(const RangeStr: string): TPageRanges;
var
  Parts: TStringList;
  I: Integer;
  Part: string;
  DashPos: Integer;
  StartPage, EndPage: Integer;
  Range: TPageRange;
begin
  SetLength(Result, 0);
  
  if Trim(RangeStr) = '' then
    Exit;
    
  Parts := TStringList.Create;
  try
    Parts.Delimiter := ',';
    Parts.DelimitedText := RangeStr;
    
    for I := 0 to Parts.Count - 1 do
    begin
      Part := Trim(Parts[I]);
      if Part = '' then
        Continue;
        
      DashPos := Pos('-', Part);
      if DashPos > 0 then
      begin
        // Range: "1-5"
        StartPage := StrToIntDef(Trim(Copy(Part, 1, DashPos - 1)), 0);
        EndPage := StrToIntDef(Trim(Copy(Part, DashPos + 1, Length(Part))), 0);
      end
      else
      begin
        // Single page: "3"
        StartPage := StrToIntDef(Part, 0);
        EndPage := StartPage;
      end;
      
      if (StartPage > 0) and (EndPage >= StartPage) and
         (EndPage <= Pdf.PageCount) then
      begin
        Range.StartPage := StartPage;
        Range.EndPage := EndPage;
        SetLength(Result, Length(Result) + 1);
        Result[High(Result)] := Range;
      end;
    end;
  finally
    Parts.Free;
  end;
end;

Aufteilen nach Lesezeichen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
procedure TFormMain.ProcessBookmarks;
var
  Bookmarks: TBookmarks;
  I: Integer;
  StartPage, EndPage: Integer;
  OutputFile: string;
  OutputDir: string;
  BookmarkTitle: string;
  PdfNew: TPdf;
begin
  Bookmarks := Pdf.Bookmarks;
  
  if Length(Bookmarks) = 0 then
  begin
    LogMessage('No bookmarks found in document', LOG_WARNING);
    Exit;
  end;
  
  OutputDir := GetOutputDirectory;
  UpdateProgress('Splitting by bookmarks...', 0, Length(Bookmarks));
  
  PdfNew := TPdf.Create(nil);
  try
    for I := 0 to High(Bookmarks) do
    begin
      if FCancelled then
        Break;
        
      StartPage := Bookmarks[I].PageNumber;
      
      // End page is start of next bookmark or end of document
      if I < High(Bookmarks) then
        EndPage := Bookmarks[I + 1].PageNumber - 1
      else
        EndPage := Pdf.PageCount;
        
      if (StartPage > 0) and (EndPage >= StartPage) then
      begin
        PdfNew.CreateDocument;
        
        PdfNew.ImportPages(Pdf,
          Format('%d-%d', [StartPage, EndPage]), 1);
          
        // Clean bookmark title for filename
        BookmarkTitle := Bookmarks[I].Title;
        BookmarkTitle := StringReplace(BookmarkTitle, '/', '_', [rfReplaceAll]);
        BookmarkTitle := StringReplace(BookmarkTitle, '\', '_', [rfReplaceAll]);
        BookmarkTitle := StringReplace(BookmarkTitle, ':', '_', [rfReplaceAll]);
        
        OutputFile := Format('%s\%02d_%s.pdf', [
          OutputDir, I + 1, BookmarkTitle]);
          
        if PdfNew.SaveAs(OutputFile) then
        begin
          LogMessage(Format('Created: %s (pages %d-%d)',
            [ExtractFileName(OutputFile), StartPage, EndPage]));
          Inc(FSplitCount);
        end;
        
        PdfNew.Active := False;
      end;
      
      UpdateProgress('Processing...', I + 1, Length(Bookmarks));
    end;
  finally
    PdfNew.Free;
  end;
end;

Anpassbare Ausgabedateinamen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function TFormMain.GenerateOutputFileName(
  const Pattern, SourceFile: string;
  PageNum: Integer): string;
var
  BaseName, Ext: string;
begin
  BaseName := ChangeFileExt(ExtractFileName(SourceFile), '');
  Ext := ExtractFileExt(SourceFile);
  
  Result := StringReplace(Pattern, '{filename}', BaseName,
    [rfReplaceAll, rfIgnoreCase]);
  Result := StringReplace(Result, '{page}', IntToStr(PageNum),
    [rfReplaceAll, rfIgnoreCase]);
  Result := StringReplace(Result, '{page:000}', Format('%.3d', [PageNum]),
    [rfReplaceAll, rfIgnoreCase]);
    
  // Ensure .pdf extension
  if not EndsText('.pdf', Result) then
    Result := Result + '.pdf';
end;

Fortschritts- und Statusmeldungen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
procedure TFormMain.UpdateProgress(const Status: string;
  Current, Total: Integer);
begin
  lblStatus.Caption := Status;
  
  if Total > 0 then
  begin
    prgProgress.Max := Total;
    prgProgress.Position := Current;
    lblProgress.Caption := Format('%d of %d pages', [Current, Total]);
  end
  else
  begin
    prgProgress.Position := 0;
    lblProgress.Caption := 'Initializing...';
  end;
  
  Application.ProcessMessages;
end;
 
procedure TFormMain.LogMessage(const Msg: string;
  const Level: string = 'INFO');
var
  TimeStamp, LogLine: string;
begin
  TimeStamp := FormatDateTime('hh:nn:ss', Now);
  LogLine := Format('[%s] %s: %s', [TimeStamp, Level, Msg]);
  
  mmoLog.Lines.Add(LogLine);
  mmoLog.Perform(WM_VSCROLL, SB_BOTTOM, 0);
  Application.ProcessMessages;
end;

Stornierungsunterstützung

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
procedure TFormMain.btnCancelClick(Sender: TObject);
begin
  FCancelled := True;
  LogMessage('Cancellation requested...', LOG_WARNING);
end;
 
procedure TFormMain.SetProcessingState(Processing: Boolean);
begin
  FProcessing := Processing;
  
  btnBrowse.Enabled := not Processing;
  edtPdfFile.Enabled := not Processing;
  grpOptions.Enabled := not Processing;
  btnSplit.Enabled := not Processing;
  btnCancel.Enabled := Processing;
end;

Zusammenfassung der Ergebnisse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
procedure TFormMain.ShowCompletionSummary;
var
  ElapsedTime: TDateTime;
  ElapsedStr: string;
begin
  ElapsedTime := Now - FStartTime;
  ElapsedStr := FormatDateTime('nn:ss', ElapsedTime);
  
  if FCancelled then
    LogMessage(Format('Operation cancelled. Created %d files in %s',
      [FSplitCount, ElapsedStr]), LOG_WARNING)
  else
    LogMessage(Format('Split completed. Created %d files in %s',
      [FSplitCount, ElapsedStr]), LOG_SUCCESS);
end;

Anwendungsfälle

  • Kapitel extrahieren – Bücher oder Handbücher durch Kapitel aufteilen, wobei Lesezeichen verwendet werden
  • Seiten verteilen – Unterschiedlichen Teammitgliedern unterschiedliche Seitenbereiche zuweisen
  • Archivierung nach Abschnitt – Große Dokumente in kleinere, übersichtlichere Dateien aufteilen
  • Seitenauszug Extrahieren Sie bestimmte Seiten zum Teilen oder zur Überprüfung.

Abschluss

Die Split PDF-Demo zeigt die Flexibilität von PDFium VCL für die Dokumentmanipulation. Egal ob das Aufteilen nach Seiten, Bereichen oder logischen Abschnitten, die durch Lesezeichen definiert sind, der Prozess ist einfach und effizient.

In Kombination mit anpassbaren Ausgabennamen und Fortschrittsverfolgung können Sie professionelle Dokumententeilungstools für jeden Workflow erstellen.

Download PDFium VCL-Komponente von loslab.com und übernehmen Sie die Kontrolle über Ihre PDF-Dokumente.