Articolo tecnico

Divisione di documenti PDF in più file con PDFium Delphi

· Programmazione PDF

Dividere un file PDF di grandi dimensioni in file più piccoli è essenziale per la distribuzione, l'archiviazione e l'elaborazione dei documenti. Demo di "Split PDF". La demo mostra come dividere documenti PDF utilizzando vari metodi con PDFium VCL in Delphi.

Panoramica

Questa demo completa offre tre modalità di divisione: pagine singole, intervalli di pagine e tramite segnalibri. Include il monitoraggio dei progressi, la registrazione dettagliata e la denominazione personalizzabile dei file di output.

Modalità di divisione.

  • Pagine singole. – Crea un file PDF separato per ogni pagina.
  • Intervalli di pagine. – Dividi in base a intervalli di pagine personalizzati (ad esempio, 1-5, 6-10).
  • Tramite segnalibri. – Dividi ai confini dei segnalibri per sezioni logiche.

Requisiti delle librerie DLL di PDFium

Prima di eseguire qualsiasi applicazione VCL di PDFium, assicurarsi che i file DLL di PDFium siano installati:

  • pdfium32.dll / pdfium64.dll – Versioni standard (~5-6 MB)
  • pdfium32v8.dll / pdfium64v8.dll – Con il motore JavaScript V8 (~23-27 MB)

Installazione: Esegui PDFiumVCL\DLLs\CopyDlls.bat come amministratore per copiare automaticamente i file DLL nelle directory di sistema di Windows.

Divisione in pagine individuali.

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;

Divisione in base a intervalli di pagine.

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;

Analisi degli intervalli di pagina

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;

Divisione tramite segnalibri.

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;

Nomi file di output personalizzabili.

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;

Aggiornamenti di avanzamento e stato.

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;

Supporto per l'annullamento.

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;

Riepilogo di completamento.

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;

Casi d'uso.

  • Estrai capitoli. – Dividi libri o manuali per capitolo utilizzando i segnalibri.
  • Distribuisci pagine. – Assegna a diversi membri del team intervalli di pagine diversi.
  • Archivia per sezione. – Organizza documenti di grandi dimensioni in file più piccoli e gestibili.
  • Estrazione di pagine. Estrai pagine specifiche per la condivisione o la revisione.

Conclusione.

La demo di Split PDF dimostra la flessibilità di PDFium VCL per la manipolazione dei documenti. Che si tratti di dividere per pagine, intervalli o sezioni logiche definite dai segnalibri, il processo è semplice ed efficiente.

Combinato con la denominazione dell'output personalizzabile e il monitoraggio dei progressi, puoi creare strumenti professionali per la divisione di documenti per qualsiasi flusso di lavoro.

Download. Componente PDFium VCL da loslab.com e prendi il controllo dei tuoi documenti PDF.