将大型PDF文件拆分为较小的文件对于文档分发、归档和处理至关重要。 Split PDF 演示程序展示了如何使用PDFium VCL在Delphi中拆分PDF文档。
概述
这个全面的演示提供了三种拆分模式:单个页面、页面范围和按书签。它包括进度跟踪、详细日志记录以及可自定义的输出文件名。
拆分模式
- 单个页面 – 为每个页面创建单独的PDF文件
- 页面范围 – 根据自定义页码范围拆分(例如:1-5,6-10)。
- 通过书签。 – 在书签边界处拆分,以形成逻辑章节。
PDFium DLL 的要求
在运行任何 PDFium VCL 应用程序之前,请确保已安装 PDFium DLL 文件:
pdfium32.dll/pdfium64.dll– 标准版本(约 5-6 MB)pdfium32v8.dll/pdfium64v8.dll– 包含 V8 JavaScript 引擎的版本(约 23-27 MB)
安装: 运行 PDFiumVCL\DLLs\CopyDlls.bat 以管理员身份,自动将 DLL 文件复制到 Windows 系统目录。
拆分为单个页面。
|
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; |
按页码范围拆分。
|
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; |
解析页面范围。
|
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; |
按书签拆分。
|
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; |
自定义输出文件名。
|
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; |
进度和状态更新。
|
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; |
取消支持
|
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; |
完成摘要
|
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; |
用例
- 提取章节 – 使用书签将书籍或手册按章节拆分
- 分配页面 – 将不同的页面范围分配给不同的团队成员
- 按章节归档 – 将大型文档组织成更小、更易于管理的文件
- 页面提取 提取特定页面以供共享或审查。
结论。
Split PDF 演示展示了 PDFium VCL 在文档处理方面的灵活性。 无论是按页面、范围,还是由书签定义的逻辑部分进行分割,该过程都简单高效。
结合可自定义的输出命名和进度跟踪,您可以构建适用于任何工作流程的专业文档分割工具。
下载 PDFium VCL 组件 来自 loslab.com,掌控您的 PDF 文档。