926 lines
27 KiB
ObjectPascal
926 lines
27 KiB
ObjectPascal
{******************************************}
|
|
{ }
|
|
{ FastReport VCL }
|
|
{ PDFView support }
|
|
{ }
|
|
{ Copyright (c) 1998-2021 }
|
|
{ by Fast Reports Inc. }
|
|
{ }
|
|
{******************************************}
|
|
|
|
unit frxPDFium;
|
|
|
|
interface
|
|
|
|
uses
|
|
{$IFNDEF Linux}
|
|
Windows,
|
|
{$ELSE}
|
|
LCLType, LCLIntf, LCLProc,
|
|
{$ENDIF}
|
|
SysUtils, Classes, Graphics, Contnrs, SyncObjs;
|
|
|
|
type
|
|
{$IFDEF DELPHI16}
|
|
frxInteger = NativeInt;
|
|
{$ELSE}
|
|
frxInteger = Integer;
|
|
{$ENDIF}
|
|
|
|
|
|
TPdfDocument = class;
|
|
|
|
TPdfDocumentSaveOption = (
|
|
dsoIncremental = 1,
|
|
dsoNoIncremental = 2,
|
|
dsoRemoveSecurity = 3
|
|
);
|
|
|
|
TPdfDrawOption = (
|
|
pdoAnnotations,
|
|
pdoLCDOptimized,
|
|
pdoNoNativeText,
|
|
pdoGrayScale,
|
|
pdoNoCatch,
|
|
pdoLimitedImageCacheSize,
|
|
pdoForceHalftone,
|
|
pdoPrinting,
|
|
pdoUseMetafile,
|
|
pdoPrintTextWithGDI
|
|
);
|
|
TPdfDrawOptions = set of TPdfDrawOption;
|
|
|
|
PFPDF_FILEWRITE = ^FPDF_FILEWRITE;
|
|
FPDF_FILEWRITE = record
|
|
version: Integer;
|
|
WriteBlock: function(pThis: PFPDF_FILEWRITE; pData: Pointer; size: LongWord): Integer; cdecl;
|
|
end;
|
|
PFPdfFileWrite = ^TFPdfFileWrite;
|
|
TFPdfFileWrite = FPDF_FILEWRITE;
|
|
|
|
PFPDFFileWriteEx = ^TFPDFFileWriteEx;
|
|
TFPDFFileWriteEx = record
|
|
Inner: TFPDFFileWrite;
|
|
Stream: TStream;
|
|
end;
|
|
|
|
FPDF_BOOL = Integer;
|
|
FPDF_DWORD = LongWord;
|
|
FPDF_BYTESTRING = PAnsiChar;
|
|
__FPDF_PTRREC = record end;
|
|
__PFPDF_PTRREC = ^__FPDF_PTRREC;
|
|
FPDF_DOCUMENT = type __PFPDF_PTRREC;
|
|
FPDF_FORMHANDLE = type __PFPDF_PTRREC;
|
|
FPDF_PAGE = type __PFPDF_PTRREC;
|
|
FPDF_BITMAP = type __PFPDF_PTRREC;
|
|
|
|
TPdfPage = class
|
|
private
|
|
FDocument: TPdfDocument;
|
|
FPage: FPDF_PAGE;
|
|
FWidth: Single;
|
|
FHeight: Single;
|
|
function GetDrawFlags(const Options: TPdfDrawOptions): Integer;
|
|
procedure AfterOpen;
|
|
public
|
|
constructor Create(ADocument: TPdfDocument; APage: FPDF_PAGE);
|
|
procedure Draw(vCanvas: TCanvas; X, Y, Width, Height, Rotation: Integer; const Options: TPdfDrawOptions);
|
|
function IsLoaded: Boolean;
|
|
procedure Open;
|
|
procedure Close;
|
|
property Width: Single read FWidth;
|
|
property Height: Single read FHeight;
|
|
end;
|
|
|
|
TPdfDocument = class
|
|
private
|
|
FDocument: FPDF_DOCUMENT;
|
|
FPages: TObjectList;
|
|
FBuffer: PByte;
|
|
FPageIndex: Integer;
|
|
FErrorText: String;
|
|
StrongError: Boolean;
|
|
|
|
procedure InitPDFium;
|
|
procedure InternLoadFromMem(Buffer: PByte; Size: frxInteger; const Password: String);
|
|
|
|
function GetActive: Boolean;
|
|
function GetPageCount: Integer;
|
|
function IsPageValid: Boolean;
|
|
function GetCurrentPage: TPdfPage;
|
|
function GetPage(Index: Integer): TPdfPage;
|
|
procedure SetPageIndex(Value: Integer);
|
|
procedure SetErrorText(v: String);
|
|
procedure PageContentChanged(Closing: Boolean);
|
|
function IsPageLoaded(PageIndex: Integer): Boolean;
|
|
procedure CheckActive;
|
|
public
|
|
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Close;
|
|
procedure Clear;
|
|
procedure LoadFromStream(AStream: TStream; const Password: String = '');
|
|
procedure Paint(Canvas: Tcanvas; Rect: TRect; Rotation: Integer; DrawOptions: TPdfDrawOptions);
|
|
function ReloadPage(APage: TPdfPage): FPDF_PAGE;
|
|
|
|
function NewDocument: Boolean;
|
|
function ImportPages(Source: TPdfDocument; const Range: string = ''; Index: Integer = -1): Boolean;
|
|
procedure SaveToStream(Stream: TStream; Option: TPdfDocumentSaveOption = dsoRemoveSecurity; FileVersion: Integer = -1);
|
|
|
|
property Active: Boolean read GetActive;
|
|
property PageIndex: Integer read FPageIndex write SetPageIndex;
|
|
property PageCount: Integer read GetPageCount;
|
|
property CurrentPage: TPdfPage read GetCurrentPage;
|
|
property Pages[Index: Integer]: TPdfPage read GetPage;
|
|
property ErrorText: String read FErrorText write SetErrorText;
|
|
end;
|
|
|
|
procedure RaiseLastPdfError;
|
|
|
|
const
|
|
{$IFDEF Linux}
|
|
{$IFDEF CPU64}
|
|
pdfium_dll = 'frx_pdfium_64.so';
|
|
{$ELSE}
|
|
pdfium_dll = 'frx_pdfium.so';
|
|
{$ENDIF}
|
|
{$ELSE}
|
|
{$IFDEF WIN64}
|
|
pdfium_dll = 'frx_pdfium_64.dll';
|
|
{$ELSE}
|
|
pdfium_dll = 'frx_pdfium.dll';
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
EMF_PPI = 600;
|
|
|
|
type
|
|
TPDF_InitLibrary = procedure(); {$IFDEF DLLEXPORT}stdcall{$ELSE}cdecl{$ENDIF};
|
|
TPDF_CloseDocument = procedure(document: FPDF_DOCUMENT); {$IFDEF DLLEXPORT}stdcall{$ELSE}cdecl{$ENDIF};
|
|
TPDF_LoadMemDocument64 = function(data_buf: Pointer; size: frxInteger; password: FPDF_BYTESTRING): FPDF_DOCUMENT; {$IFDEF DLLEXPORT}stdcall{$ELSE}cdecl{$ENDIF};
|
|
TPDF_LoadPage = function(document: FPDF_DOCUMENT; page_index: Integer): FPDF_PAGE; {$IFDEF DLLEXPORT}stdcall{$ELSE}cdecl{$ENDIF};
|
|
TPDF_GetPageCount = function(document: FPDF_DOCUMENT): Integer; {$IFDEF DLLEXPORT}stdcall{$ELSE}cdecl{$ENDIF};
|
|
{$IFNDEF Linux}
|
|
TPDF_RenderPage = procedure(DC: HDC; page: FPDF_PAGE; start_x, start_y, size_x, size_y: Integer;
|
|
rotate: Integer; flags: Integer); {$IFDEF DLLEXPORT}stdcall{$ELSE}cdecl{$ENDIF};
|
|
{$ELSE}
|
|
TPDFBitmap_Create = function(width, height: Integer; alpha: Integer): FPDF_BITMAP; {$IFDEF DLLEXPORT}stdcall{$ELSE}cdecl{$ENDIF};
|
|
TPDFBitmap_FillRect = procedure(bitmap: FPDF_BITMAP; left, top, width, height: Integer; color: FPDF_DWORD); {$IFDEF DLLEXPORT}stdcall{$ELSE}cdecl{$ENDIF};
|
|
TPDF_RenderPageBitmap = procedure(bitmap: FPDF_BITMAP; page: FPDF_PAGE; start_x, start_y, size_x, size_y: Integer; rotate: Integer; flags: Integer); {$IFDEF DLLEXPORT}stdcall{$ELSE}cdecl{$ENDIF};
|
|
TPDFBitmap_GetBuffer = function(bitmap: FPDF_BITMAP): Pointer; {$IFDEF DLLEXPORT}stdcall{$ELSE}cdecl{$ENDIF};
|
|
TPDFBitmap_Destroy = procedure(bitmap: FPDF_BITMAP); {$IFDEF DLLEXPORT}stdcall{$ELSE}cdecl{$ENDIF};
|
|
{$ENDIF}
|
|
TPDF_GetPageWidthF = function(page: FPDF_PAGE): Single; {$IFDEF DLLEXPORT}stdcall{$ELSE}cdecl{$ENDIF};
|
|
TPDF_GetPageHeightF = function(page: FPDF_PAGE): Single; {$IFDEF DLLEXPORT}stdcall{$ELSE}cdecl{$ENDIF};
|
|
TPDFPage_HasTransparency = function(page: FPDF_PAGE): FPDF_BOOL; {$IFDEF DLLEXPORT}stdcall{$ELSE}cdecl{$ENDIF};
|
|
TPDFPage_GetRotation = function(page: FPDF_PAGE): Integer; {$IFDEF DLLEXPORT}stdcall{$ELSE}cdecl{$ENDIF};
|
|
TPDF_ClosePage = procedure(page: FPDF_PAGE); {$IFDEF DLLEXPORT}stdcall{$ELSE}cdecl{$ENDIF};
|
|
TPDF_ImportPages = function(dest_doc, src_doc: FPDF_DOCUMENT; pagerange: FPDF_BYTESTRING; index: Integer): FPDF_BOOL; {$IFDEF DLLEXPORT}stdcall{$ELSE}cdecl{$ENDIF};
|
|
TPDF_CreateNewDocument = function: FPDF_DOCUMENT; {$IFDEF DLLEXPORT}stdcall{$ELSE}cdecl{$ENDIF};
|
|
TPDF_SaveAsCopy = function(document: FPDF_DOCUMENT; pFileWrite: PFPDF_FILEWRITE; flags: FPDF_DWORD): FPDF_BOOL; {$IFDEF DLLEXPORT}stdcall{$ELSE}cdecl{$ENDIF};
|
|
TPDF_SaveWithVersion = function(document: FPDF_DOCUMENT; pFileWrite: PFPDF_FILEWRITE;
|
|
flags: FPDF_DWORD; fileVersion: Integer): FPDF_BOOL; {$IFDEF DLLEXPORT}stdcall{$ELSE}cdecl{$ENDIF};
|
|
TPDF_GetLastError = function(): LongWord; {$IFDEF DLLEXPORT}stdcall{$ELSE}cdecl{$ENDIF};
|
|
TPDF_SetPrintTextWithGDI = procedure(use_gdi: LongBool); {$IFDEF DLLEXPORT}stdcall{$ELSE}cdecl{$ENDIF};
|
|
|
|
var
|
|
PdfiumModule: HMODULE;
|
|
PDFiumInitCritSect: TCriticalSection;
|
|
//
|
|
FPDF_InitLibrary: TPDF_InitLibrary;
|
|
FPDF_CloseDocument: TPDF_CloseDocument;
|
|
FPDF_LoadMemDocument64: TPDF_LoadMemDocument64;
|
|
FPDF_LoadPage: TPDF_LoadPage;
|
|
FPDF_GetPageCount: TPDF_GetPageCount;
|
|
{$IFNDEF Linux}
|
|
FPDF_RenderPage: TPDF_RenderPage;
|
|
{$ELSE}
|
|
FPDFBitmap_Create: TPDFBitmap_Create;
|
|
FPDFBitmap_FillRect: TPDFBitmap_FillRect;
|
|
FPDF_RenderPageBitmap: TPDF_RenderPageBitmap;
|
|
FPDFBitmap_GetBuffer: TPDFBitmap_GetBuffer;
|
|
FPDFBitmap_Destroy: TPDFBitmap_Destroy;
|
|
{$ENDIF}
|
|
FPDF_GetPageWidthF: TPDF_GetPageWidthF;
|
|
FPDF_GetPageHeightF: TPDF_GetPageHeightF;
|
|
FPDFPage_HasTransparency: TPDFPage_HasTransparency;
|
|
FPDFPage_GetRotation: TPDFPage_GetRotation;
|
|
FPDF_ClosePage: TPDF_ClosePage;
|
|
FPDF_ImportPages: TPDF_ImportPages;
|
|
FPDF_CreateNewDocument: TPDF_CreateNewDocument;
|
|
FPDF_SaveAsCopy: TPDF_SaveAsCopy;
|
|
FPDF_SaveWithVersion: TPDF_SaveWithVersion;
|
|
FPDF_GetLastError: TPDF_GetLastError;
|
|
FPDF_SetPrintTextWithGDI: TPDF_SetPrintTextWithGDI;
|
|
|
|
PDFiumDLLPath: String;
|
|
|
|
procedure Lock;
|
|
procedure UnLock;
|
|
|
|
procedure frxPDF_InitLibrary();
|
|
procedure frxPDF_CloseDocument(document: FPDF_DOCUMENT);
|
|
function frxPDF_LoadMemDocument64(data_buf: Pointer; size: frxInteger; password: FPDF_BYTESTRING): FPDF_DOCUMENT;
|
|
function frxPDF_LoadPage(document: FPDF_DOCUMENT; page_index: Integer): FPDF_PAGE;
|
|
function frxPDF_GetPageCount(document: FPDF_DOCUMENT): Integer;
|
|
{$IFNDEF Linux}
|
|
procedure frxPDF_RenderPage(DC: HDC; page: FPDF_PAGE; start_x, start_y, size_x, size_y: Integer;
|
|
rotate: Integer; flags: Integer);
|
|
{$ELSE}
|
|
function frxPDFBitmap_Create(width, height: Integer; alpha: Integer): FPDF_BITMAP;
|
|
procedure frxPDFBitmap_FillRect(bitmap: FPDF_BITMAP; left, top, width, height: Integer; color: FPDF_DWORD);
|
|
procedure frxPDF_RenderPageBitmap(bitmap: FPDF_BITMAP; page: FPDF_PAGE; start_x, start_y, size_x, size_y: Integer; rotate: Integer; flags: Integer);
|
|
function frxPDFBitmap_GetBuffer(bitmap: FPDF_BITMAP): Pointer;
|
|
procedure frxPDFBitmap_Destroy(bitmap: FPDF_BITMAP);
|
|
{$ENDIF}
|
|
function frxPDF_GetPageWidthF(page: FPDF_PAGE): Single;
|
|
function frxPDF_GetPageHeightF(page: FPDF_PAGE): Single;
|
|
function frxPDFPage_HasTransparency(page: FPDF_PAGE): FPDF_BOOL;
|
|
function frxPDFPage_GetRotation(page: FPDF_PAGE): Integer;
|
|
procedure frxPDF_ClosePage(page: FPDF_PAGE);
|
|
function frxPDF_ImportPages(dest_doc, src_doc: FPDF_DOCUMENT; pagerange: FPDF_BYTESTRING; index: Integer): FPDF_BOOL;
|
|
function frxPDF_CreateNewDocument: FPDF_DOCUMENT;
|
|
function frxPDF_SaveAsCopy(document: FPDF_DOCUMENT; pFileWrite: PFPDF_FILEWRITE; flags: FPDF_DWORD): FPDF_BOOL;
|
|
function frxPDF_SaveWithVersion(document: FPDF_DOCUMENT; pFileWrite: PFPDF_FILEWRITE;
|
|
flags: FPDF_DWORD; fileVersion: Integer): FPDF_BOOL;
|
|
function frxPDF_GetLastError(): LongWord;
|
|
procedure frxPDF_SetPrintTextWithGDI(use_gdi: LongBool);
|
|
|
|
procedure frxRenderPage(vCanvas: TCanvas; page: FPDF_PAGE; start_x, start_y, size_x, size_y: Integer;
|
|
rotate: Integer; flags: Integer);
|
|
|
|
const
|
|
FPDF_ERR_SUCCESS = 0;
|
|
FPDF_ERR_UNKNOWN = 1;
|
|
FPDF_ERR_FILE = 2;
|
|
FPDF_ERR_FORMAT = 3;
|
|
FPDF_ERR_PASSWORD = 4;
|
|
FPDF_ERR_SECURITY = 5;
|
|
FPDF_ERR_PAGE = 6;
|
|
RsPdfErrorSuccess = 'No error.';
|
|
RsPdfErrorUnknown = 'Unknown error.';
|
|
RsPdfErrorFile = 'File not found or can''t be opened.';
|
|
RsPdfErrorFormat = 'File is not a PDF document or is corrupted.';
|
|
RsPdfErrorPassword = 'Password required or invalid password.';
|
|
RsPdfErrorSecurity = 'Security schema is not supports.';
|
|
RsPdfErrorPage = 'Page does not exist or data error.';
|
|
|
|
RsPdfInvalidPage = 'Invalid page';
|
|
RsPdfDLLNotFound = pdfium_dll + ' was not found.';
|
|
RsPdfCheckActive = 'attempt to render before loading the document.';
|
|
RsPdfEmptyDocument = 'Empty document.';
|
|
|
|
implementation
|
|
|
|
uses frxPrinter;
|
|
{TPdfPage}
|
|
|
|
function TPdfPage.GetDrawFlags(const Options: TPdfDrawOptions): Integer;
|
|
begin
|
|
Result := 0;
|
|
if (pdoAnnotations in Options) then
|
|
Result := Result or 1;
|
|
if (pdoLCDOptimized in Options) then
|
|
Result := Result or 2;
|
|
if (pdoNoNativeText in Options) then
|
|
Result := Result or 4;
|
|
if (pdoGrayScale in Options) then
|
|
Result := Result or 8;
|
|
if (pdoNoCatch in Options) then
|
|
Result := Result or 256;
|
|
if (pdoLimitedImageCacheSize in Options) then
|
|
Result := Result or 512;
|
|
if (pdoForceHalftone in Options) then
|
|
Result := Result or 1024;
|
|
if (pdoPrinting in Options) then
|
|
Result := Result or 2048;
|
|
end;
|
|
|
|
procedure TPdfPage.AfterOpen;
|
|
begin
|
|
FWidth := frxPDF_GetPageWidthF(FPage);
|
|
FHeight := frxPDF_GetPageHeightF(FPage);
|
|
end;
|
|
|
|
constructor TPdfPage.Create(ADocument: TPdfDocument; APage: FPDF_PAGE);
|
|
begin
|
|
FDocument := ADocument;
|
|
FPage := APage;
|
|
AfterOpen;
|
|
end;
|
|
|
|
procedure TPdfPage.Draw(vCanvas: TCanvas; X, Y, Width, Height, Rotation: Integer; const Options: TPdfDrawOptions);
|
|
begin
|
|
Lock;
|
|
if Assigned(FPDF_SetPrintTextWithGDI) then
|
|
FPDF_SetPrintTextWithGDI(LongBool(pdoPrintTextWithGDI in Options));
|
|
UnLock;
|
|
frxRenderPage(vCanvas, FPage, X, Y, Width, Height, Rotation, GetDrawFlags(Options));
|
|
end;
|
|
|
|
function TPdfPage.IsLoaded: Boolean;
|
|
begin
|
|
Result := FPage <> nil;
|
|
end;
|
|
|
|
procedure TPdfPage.Open;
|
|
begin
|
|
if FPage = nil then
|
|
begin
|
|
FPage := FDocument.ReloadPage(Self);
|
|
AfterOpen;
|
|
end;
|
|
end;
|
|
|
|
procedure TPdfPage.Close;
|
|
begin
|
|
if FPage <> nil then
|
|
begin
|
|
frxPDF_ClosePage(FPage);
|
|
FPage := nil;
|
|
end;
|
|
end;
|
|
|
|
{TPdfDocument}
|
|
|
|
procedure TPdfDocument.InitPDFium;
|
|
var
|
|
DLL_FPath: String;
|
|
begin
|
|
try
|
|
PDFiumInitCritSect.Enter;
|
|
if (PdfiumModule <> 0) then
|
|
Exit;
|
|
|
|
DLL_FPath := PDFiumDLLPath + pdfium_dll;
|
|
PdfiumModule := LoadLibrary(PChar(DLL_FPath));
|
|
|
|
{$IFDEF LCLGTK2}
|
|
if (PdfiumModule = 0) then
|
|
begin
|
|
DLL_FPath := ExtractFilePath(ParamStr(0)) + pdfium_dll;
|
|
PdfiumModule := LoadLibrary(PChar(DLL_FPath));
|
|
end;
|
|
{$ENDIF}
|
|
|
|
if (PdfiumModule = 0) then
|
|
begin
|
|
ErrorText := RsPdfDLLNotFound;
|
|
StrongError := True;
|
|
Exit;
|
|
end;
|
|
|
|
// Init Func
|
|
FPDF_InitLibrary := TPDF_InitLibrary(GetProcAddress(PdfiumModule, 'FPDF_InitLibrary'));
|
|
FPDF_CloseDocument := TPDF_CloseDocument(GetProcAddress(PdfiumModule, 'FPDF_CloseDocument'));
|
|
FPDF_LoadMemDocument64 := TPDF_LoadMemDocument64(GetProcAddress(PdfiumModule, 'FPDF_LoadMemDocument64'));
|
|
FPDF_LoadPage := TPDF_LoadPage(GetProcAddress(PdfiumModule, 'FPDF_LoadPage'));
|
|
FPDF_GetPageCount := TPDF_GetPageCount(GetProcAddress(PdfiumModule, 'FPDF_GetPageCount'));
|
|
{$IFNDEF Linux}
|
|
FPDF_RenderPage := TPDF_RenderPage(GetProcAddress(PdfiumModule, 'FPDF_RenderPage'));
|
|
{$ELSE}
|
|
FPDFBitmap_Create := TPDFBitmap_Create(GetProcAddress(PdfiumModule, 'FPDFBitmap_Create'));
|
|
FPDFBitmap_FillRect := TPDFBitmap_FillRect(GetProcAddress(PdfiumModule, 'FPDFBitmap_FillRect'));
|
|
FPDF_RenderPageBitmap := TPDF_RenderPageBitmap(GetProcAddress(PdfiumModule, 'FPDF_RenderPageBitmap'));
|
|
FPDFBitmap_GetBuffer := TPDFBitmap_GetBuffer(GetProcAddress(PdfiumModule, 'FPDFBitmap_GetBuffer'));
|
|
FPDFBitmap_Destroy := TPDFBitmap_Destroy(GetProcAddress(PdfiumModule, 'FPDFBitmap_Destroy'));
|
|
{$ENDIF}
|
|
FPDF_GetPageWidthF := TPDF_GetPageWidthF(GetProcAddress(PdfiumModule, 'FPDF_GetPageWidthF'));
|
|
FPDF_GetPageHeightF := TPDF_GetPageHeightF(GetProcAddress(PdfiumModule, 'FPDF_GetPageHeightF'));
|
|
FPDFPage_HasTransparency := TPDFPage_HasTransparency(GetProcAddress(PdfiumModule, 'FPDFPage_HasTransparency'));
|
|
FPDFPage_GetRotation := TPDFPage_GetRotation(GetProcAddress(PdfiumModule, 'FPDFPage_GetRotation'));
|
|
FPDF_ClosePage := TPDF_ClosePage(GetProcAddress(PdfiumModule, 'FPDF_ClosePage'));
|
|
FPDF_ImportPages := TPDF_ImportPages(GetProcAddress(PdfiumModule, 'FPDF_ImportPages'));
|
|
FPDF_CreateNewDocument := TPDF_CreateNewDocument(GetProcAddress(PdfiumModule, 'FPDF_CreateNewDocument'));
|
|
FPDF_SaveAsCopy := TPDF_SaveAsCopy(GetProcAddress(PdfiumModule, 'FPDF_SaveAsCopy'));
|
|
FPDF_SaveWithVersion := TPDF_SaveWithVersion(GetProcAddress(PdfiumModule, 'FPDF_SaveWithVersion'));
|
|
FPDF_GetLastError := TPDF_GetLastError(GetProcAddress(PdfiumModule, 'FPDF_GetLastError'));
|
|
FPDF_SetPrintTextWithGDI := TPDF_SetPrintTextWithGDI(GetProcAddress(PdfiumModule, 'FPDF_SetPrintTextWithGDI'));
|
|
FPDF_InitLibrary;
|
|
finally
|
|
PDFiumInitCritSect.Leave;
|
|
end;
|
|
end;
|
|
|
|
procedure TPdfDocument.InternLoadFromMem(Buffer: PByte; Size: frxInteger; const Password: String);
|
|
var
|
|
APassword: AnsiString;
|
|
begin
|
|
if Size > 0 then
|
|
begin
|
|
APassword := AnsiString(Password);
|
|
if Password = '' then
|
|
FDocument := frxPDF_LoadMemDocument64(Buffer, Size, nil)
|
|
else
|
|
FDocument := frxPDF_LoadMemDocument64(Buffer, Size, PAnsiChar(@APassword[1]));
|
|
|
|
if FDocument = nil then
|
|
RaiseLastPdfError;
|
|
FPages.Count := frxPDF_GetPageCount(FDocument);
|
|
FPageIndex := 0;
|
|
PageContentChanged(False);
|
|
end;
|
|
end;
|
|
|
|
function TPdfDocument.GetActive: Boolean;
|
|
begin
|
|
Result := FDocument <> nil;
|
|
end;
|
|
|
|
function TPdfDocument.GetPageCount: Integer;
|
|
begin
|
|
Result := FPages.Count;
|
|
end;
|
|
|
|
function TPdfDocument.IsPageValid: Boolean;
|
|
begin
|
|
Result := Active and (PageIndex < PageCount);
|
|
end;
|
|
|
|
function TPdfDocument.GetCurrentPage: TPdfPage;
|
|
begin
|
|
if IsPageValid then
|
|
Result := Pages[PageIndex]
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
function TPdfDocument.GetPage(Index: Integer): TPdfPage;
|
|
var
|
|
LPage: FPDF_PAGE;
|
|
begin
|
|
Result := TPdfPage(FPages[Index]);
|
|
if Result = nil then
|
|
begin
|
|
LPage := frxPDF_LoadPage(FDocument, Index);
|
|
if LPage = nil then
|
|
RaiseLastPdfError;
|
|
Result := TPdfPage.Create(Self, LPage);
|
|
FPages[Index] := Result;
|
|
end
|
|
end;
|
|
|
|
procedure TPdfDocument.SetPageIndex(Value: Integer);
|
|
begin
|
|
if Value >= PageCount then
|
|
Value := PageCount - 1;
|
|
if Value < 0 then
|
|
Value := 0;
|
|
|
|
if Value <> FPageIndex then
|
|
begin
|
|
if (FPageIndex >= 0) and (FPageIndex < PageCount) and IsPageLoaded(FPageIndex) then
|
|
Pages[FPageIndex].Close;
|
|
FPageIndex := Value;
|
|
PageContentChanged(False);
|
|
end;
|
|
end;
|
|
|
|
procedure TPdfDocument.SetErrorText(v: String);
|
|
begin
|
|
if not StrongError then
|
|
FErrorText := v;
|
|
end;
|
|
|
|
procedure TPdfDocument.PageContentChanged(Closing: Boolean);
|
|
begin
|
|
CurrentPage.Open;
|
|
end;
|
|
|
|
function TPdfDocument.IsPageLoaded(PageIndex: Integer): Boolean;
|
|
begin
|
|
Result := False;
|
|
if (CurrentPage <> nil) then
|
|
Result := CurrentPage.IsLoaded;
|
|
end;
|
|
|
|
procedure TPdfDocument.CheckActive;
|
|
begin
|
|
if not Active then
|
|
raise Exception.Create(RsPdfCheckActive);
|
|
end;
|
|
|
|
constructor TPdfDocument.Create;
|
|
begin
|
|
FPages := TObjectList.Create;
|
|
StrongError := False;
|
|
try
|
|
ErrorText := '';
|
|
InitPDFium;
|
|
except
|
|
on E : Exception do
|
|
ErrorText := E.Message;
|
|
end;
|
|
end;
|
|
|
|
destructor TPdfDocument.Destroy;
|
|
begin
|
|
Close;
|
|
FPages.Free;
|
|
end;
|
|
|
|
procedure TPdfDocument.Close;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to FPages.Count - 1 do
|
|
if (FPages[i] <> nil) then
|
|
TPdfPage(FPages[i]).Close;
|
|
|
|
FPages.Clear;
|
|
FPageIndex := 0;
|
|
|
|
if FDocument <> nil then
|
|
begin
|
|
frxPDF_CloseDocument(FDocument);
|
|
FDocument := nil;
|
|
end;
|
|
|
|
if FBuffer <> nil then
|
|
begin
|
|
FreeMem(FBuffer);
|
|
FBuffer := nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TPdfDocument.Clear;
|
|
begin
|
|
Close;
|
|
end;
|
|
|
|
procedure RaiseLastPdfError;
|
|
begin
|
|
case (frxPDF_GetLastError()) of
|
|
FPDF_ERR_SUCCESS:
|
|
raise Exception.Create(RsPdfErrorSuccess);
|
|
FPDF_ERR_FILE:
|
|
raise Exception.Create(RsPdfErrorFile);
|
|
FPDF_ERR_FORMAT:
|
|
raise Exception.Create(RsPdfErrorFormat);
|
|
FPDF_ERR_PASSWORD:
|
|
raise Exception.Create(RsPdfErrorPassword);
|
|
FPDF_ERR_SECURITY:
|
|
raise Exception.Create(RsPdfErrorSecurity);
|
|
FPDF_ERR_PAGE:
|
|
raise Exception.Create(RsPdfErrorPage);
|
|
else
|
|
raise Exception.Create(RsPdfErrorUnknown);
|
|
end;
|
|
end;
|
|
|
|
procedure TPdfDocument.LoadFromStream(AStream: TStream; const Password: String = '');
|
|
var
|
|
Size: NativeInt;
|
|
begin
|
|
if StrongError then
|
|
Exit;
|
|
ErrorText := '';
|
|
Close;
|
|
Size := AStream.Size;
|
|
if Size > 0 then
|
|
begin
|
|
GetMem(FBuffer, Size);
|
|
try
|
|
AStream.ReadBuffer(FBuffer^, Size);
|
|
InternLoadFromMem(FBuffer, Size, Password);
|
|
except
|
|
on E : Exception do
|
|
begin
|
|
Close;
|
|
ErrorText := E.Message;
|
|
end;
|
|
end;
|
|
end
|
|
else ErrorText := RsPdfEmptyDocument;
|
|
end;
|
|
|
|
procedure TPdfDocument.Paint(Canvas: Tcanvas; Rect: TRect; Rotation: Integer; DrawOptions: TPdfDrawOptions);
|
|
procedure DrawOnMetafile;
|
|
{$IFNDEF FPC}
|
|
var
|
|
emf: TMetafile;
|
|
c: TMetafileCanvas;
|
|
xForm: TXForm;
|
|
begin
|
|
emf := TMetafile.Create;
|
|
emf.Width := Round(CurrentPage.Width);
|
|
emf.Height := Round(CurrentPage.Height);
|
|
try
|
|
c := TMetafileCanvas.Create(emf, 0);
|
|
SetGraphicsMode(C.Handle, GM_ADVANCED);
|
|
xForm.eM11 := 1 / (EMF_PPI / 72);
|
|
xForm.eM12 := 0.0;
|
|
xForm.eM21 := 0.0;
|
|
xForm.eM22 := 1 / (EMF_PPI / 72);
|
|
xForm.eDx := 0.0;
|
|
xForm.eDy := 0.0;
|
|
SetWorldTransform(C.Handle, xForm);
|
|
try
|
|
CurrentPage.Draw(C, 0, 0, Round(CurrentPage.Width * EMF_PPI / 72),Round(CurrentPage.Height * EMF_PPI / 72), Rotation, DrawOptions + [pdoPrinting]);
|
|
finally
|
|
c.Free;
|
|
end;
|
|
Canvas.StretchDraw(Rect, emf);
|
|
finally
|
|
emf.Free;
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
CurrentPage.Draw(Canvas, Rect.Left, Rect.Top, Rect.Right - Rect.Left, Rect.Bottom - Rect.Top, Rotation, DrawOptions)
|
|
{$ENDIF}
|
|
end;
|
|
begin
|
|
if IsPageValid then
|
|
begin
|
|
if pdoUseMetafile in DrawOptions then
|
|
DrawOnMetafile
|
|
else
|
|
CurrentPage.Draw(Canvas, Rect.Left, Rect.Top, Rect.Right - Rect.Left, Rect.Bottom - Rect.Top, Rotation, DrawOptions)
|
|
end
|
|
else
|
|
begin
|
|
with Canvas do
|
|
begin
|
|
Font.Name := 'Arial';
|
|
Font.Size := Round(10);
|
|
Font.Color := clBlue;
|
|
Rect.Left := Rect.Left + 2;
|
|
Rect.Top := Rect.Top + 2;
|
|
DrawText(Handle, PChar(RsPdfInvalidPage), Length(RsPdfInvalidPage), Rect,
|
|
DT_WORDBREAK);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TPdfDocument.ReloadPage(APage: TPdfPage): FPDF_PAGE;
|
|
var
|
|
Index: Integer;
|
|
begin
|
|
CheckActive;
|
|
Index := FPages.IndexOf(APage);
|
|
Result := frxPDF_LoadPage(FDocument, Index);
|
|
if Result = nil then
|
|
RaiseLastPdfError;
|
|
end;
|
|
|
|
function TPdfDocument.NewDocument: Boolean;
|
|
begin
|
|
Close;
|
|
FDocument := frxPDF_CreateNewDocument();
|
|
Result := FDocument <> nil;
|
|
end;
|
|
|
|
function TPdfDocument.ImportPages(Source: TPdfDocument; const Range: string = ''; Index: Integer = -1): Boolean;
|
|
var
|
|
A: AnsiString;
|
|
I, NewCount, OldCount, InsertCount: Integer;
|
|
begin
|
|
CheckActive;
|
|
Source.CheckActive;
|
|
|
|
OldCount := frxPDF_GetPageCount(FDocument);
|
|
if Index < 0 then
|
|
Index := OldCount;
|
|
A := AnsiString(Range);
|
|
Result := frxPDF_ImportPages(FDocument, Source.FDocument, PAnsiChar(Pointer(A)), Index) <> 0;
|
|
NewCount := frxPDF_GetPageCount(FDocument);
|
|
InsertCount := NewCount - OldCount;
|
|
if InsertCount > 0 then
|
|
begin
|
|
FPages.Count := NewCount;
|
|
if Index < OldCount then
|
|
begin
|
|
Move(FPages.List[Index], FPages.List[Index + InsertCount], (OldCount - Index) * SizeOf(TObject));
|
|
for I := Index to Index + InsertCount - 1 do
|
|
FPages[Index] := nil;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function WriteBlockToStream(pThis: PFPDF_FILEWRITE; pData: Pointer; size: LongWord): Integer; cdecl;
|
|
begin
|
|
Result := Ord(LongWord(PFPDFFileWriteEx(pThis)^.Stream.Write(pData^, size)) = size);
|
|
end;
|
|
|
|
procedure TPdfDocument.SaveToStream(Stream: TStream; Option: TPdfDocumentSaveOption = dsoRemoveSecurity; FileVersion: Integer = -1);
|
|
var
|
|
FileWriteInfo: TFPDFFileWriteEx;
|
|
begin
|
|
CheckActive;
|
|
|
|
FileWriteInfo.Inner.version := 1;
|
|
FileWriteInfo.Inner.WriteBlock := @WriteBlockToStream;
|
|
FileWriteInfo.Stream := Stream;
|
|
|
|
if FileVersion <> -1 then
|
|
frxPDF_SaveWithVersion(FDocument, @FileWriteInfo, Ord(Option), FileVersion)
|
|
else
|
|
frxPDF_SaveAsCopy(FDocument, @FileWriteInfo, Ord(Option));
|
|
end;
|
|
|
|
{frxPDF_...}
|
|
|
|
procedure Lock;
|
|
begin
|
|
PDFiumInitCritSect.Enter;
|
|
end;
|
|
|
|
procedure UnLock;
|
|
begin
|
|
PDFiumInitCritSect.Leave;
|
|
end;
|
|
|
|
procedure frxPDF_InitLibrary();
|
|
begin
|
|
Lock;
|
|
FPDF_InitLibrary;
|
|
UnLock;
|
|
end;
|
|
|
|
procedure frxPDF_CloseDocument(document: FPDF_DOCUMENT);
|
|
begin
|
|
Lock;
|
|
FPDF_CloseDocument(document);
|
|
UnLock;
|
|
end;
|
|
|
|
function frxPDF_LoadMemDocument64(data_buf: Pointer; size: frxInteger; password: FPDF_BYTESTRING): FPDF_DOCUMENT;
|
|
begin
|
|
Lock;
|
|
Result := FPDF_LoadMemDocument64(data_buf, size, password);
|
|
UnLock;
|
|
end;
|
|
|
|
function frxPDF_LoadPage(document: FPDF_DOCUMENT; page_index: Integer): FPDF_PAGE;
|
|
begin
|
|
Lock;
|
|
Result := FPDF_LoadPage(document, page_index);
|
|
UnLock;
|
|
end;
|
|
|
|
function frxPDF_GetPageCount(document: FPDF_DOCUMENT): Integer;
|
|
begin
|
|
Lock;
|
|
Result := FPDF_GetPageCount(document);
|
|
UnLock;
|
|
end;
|
|
|
|
{$IFNDEF Linux}
|
|
procedure frxPDF_RenderPage(DC: HDC; page: FPDF_PAGE; start_x, start_y, size_x, size_y: Integer;
|
|
rotate: Integer; flags: Integer);
|
|
begin
|
|
Lock;
|
|
FPDF_RenderPage(DC, page, start_x, start_y, size_x, size_y, rotate, flags);
|
|
UnLock;
|
|
end;
|
|
{$ELSE}
|
|
function frxPDFBitmap_Create(width, height: Integer; alpha: Integer): FPDF_BITMAP;
|
|
begin
|
|
Lock;
|
|
Result := FPDFBitmap_Create(width, height, alpha);
|
|
UnLock;
|
|
end;
|
|
|
|
procedure frxPDFBitmap_FillRect(bitmap: FPDF_BITMAP; left, top, width, height: Integer; color: FPDF_DWORD);
|
|
begin
|
|
Lock;
|
|
FPDFBitmap_FillRect(bitmap, left, top, width, height, color);
|
|
UnLock;
|
|
end;
|
|
|
|
procedure frxPDF_RenderPageBitmap(bitmap: FPDF_BITMAP; page: FPDF_PAGE; start_x, start_y, size_x, size_y: Integer; rotate: Integer; flags: Integer);
|
|
begin
|
|
Lock;
|
|
FPDF_RenderPageBitmap(bitmap, page, start_x, start_y, size_x, size_y, rotate, flags);
|
|
UnLock;
|
|
end;
|
|
|
|
function frxPDFBitmap_GetBuffer(bitmap: FPDF_BITMAP): Pointer;
|
|
begin
|
|
Lock;
|
|
Result := FPDFBitmap_GetBuffer(bitmap);
|
|
UnLock;
|
|
end;
|
|
|
|
procedure frxPDFBitmap_Destroy(bitmap: FPDF_BITMAP);
|
|
begin
|
|
Lock;
|
|
FPDFBitmap_Destroy(bitmap);
|
|
UnLock;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function frxPDF_GetPageWidthF(page: FPDF_PAGE): Single;
|
|
begin
|
|
Lock;
|
|
Result := FPDF_GetPageWidthF(page);
|
|
UnLock;
|
|
end;
|
|
|
|
function frxPDF_GetPageHeightF(page: FPDF_PAGE): Single;
|
|
begin
|
|
Lock;
|
|
Result := FPDF_GetPageHeightF(page);
|
|
UnLock;
|
|
end;
|
|
|
|
function frxPDFPage_HasTransparency(page: FPDF_PAGE): FPDF_BOOL;
|
|
begin
|
|
Lock;
|
|
Result := FPDFPage_HasTransparency(page);
|
|
UnLock;
|
|
end;
|
|
|
|
function frxPDFPage_GetRotation(page: FPDF_PAGE): Integer;
|
|
begin
|
|
Lock;
|
|
Result := FPDFPage_GetRotation(page);
|
|
UnLock;
|
|
end;
|
|
|
|
procedure frxPDF_ClosePage(page: FPDF_PAGE);
|
|
begin
|
|
Lock;
|
|
FPDF_ClosePage(page);
|
|
UnLock;
|
|
end;
|
|
|
|
function frxPDF_ImportPages(dest_doc, src_doc: FPDF_DOCUMENT; pagerange: FPDF_BYTESTRING; index: Integer): FPDF_BOOL;
|
|
begin
|
|
Lock;
|
|
Result := FPDF_ImportPages(dest_doc, src_doc, pagerange, index);
|
|
UnLock;
|
|
end;
|
|
|
|
function frxPDF_CreateNewDocument: FPDF_DOCUMENT;
|
|
begin
|
|
Lock;
|
|
Result := FPDF_CreateNewDocument();
|
|
UnLock;
|
|
end;
|
|
|
|
function frxPDF_SaveAsCopy(document: FPDF_DOCUMENT; pFileWrite: PFPDF_FILEWRITE; flags: FPDF_DWORD): FPDF_BOOL;
|
|
begin
|
|
Lock;
|
|
Result := FPDF_SaveAsCopy(document, pFileWrite, flags);
|
|
UnLock;
|
|
end;
|
|
|
|
function frxPDF_SaveWithVersion(document: FPDF_DOCUMENT; pFileWrite: PFPDF_FILEWRITE;
|
|
flags: FPDF_DWORD; fileVersion: Integer): FPDF_BOOL;
|
|
begin
|
|
Lock;
|
|
Result := FPDF_SaveWithVersion(document, pFileWrite, flags, fileVersion);
|
|
UnLock;
|
|
end;
|
|
|
|
function frxPDF_GetLastError(): LongWord;
|
|
begin
|
|
Lock;
|
|
Result := FPDF_GetLastError();
|
|
UnLock;
|
|
end;
|
|
|
|
procedure frxPDF_SetPrintTextWithGDI(use_gdi: LongBool);
|
|
begin
|
|
Lock;
|
|
FPDF_SetPrintTextWithGDI(use_gdi);
|
|
UnLock;
|
|
end;
|
|
|
|
procedure frxRenderPage(vCanvas: TCanvas; page: FPDF_PAGE; start_x, start_y, size_x, size_y: Integer;
|
|
rotate: Integer; flags: Integer);
|
|
{$IFDEF Linux}
|
|
var
|
|
PDF_Bitmap: FPDF_BITMAP;
|
|
bmp: TBitmap;
|
|
PBuf: PInteger;
|
|
{$ENDIF}
|
|
begin
|
|
{$IFNDEF Linux}
|
|
frxPDF_RenderPage(vCanvas.Handle, page, start_x, start_y, size_x, size_y, rotate, flags);
|
|
{$ELSE}
|
|
PDF_Bitmap := frxPDFBitmap_Create(size_x, size_y, 0);
|
|
frxPDFBitmap_FillRect(PDF_Bitmap, 0, 0, size_x, size_y, $00FFFFFF);
|
|
frxPDF_RenderPageBitmap(PDF_Bitmap, page, 0, 0, size_x, size_y, rotate, flags);
|
|
|
|
PBuf := frxPDFBitmap_GetBuffer(PDF_Bitmap);
|
|
bmp := TBitmap.Create;
|
|
bmp.Handle := CreateBitmap(size_x, size_y, 1, 32, PBuf);
|
|
stretchblt(vCanvas.Handle, start_x, start_y + size_y, size_x, -size_y, bmp.Canvas.Handle, 0, 0, bmp.Width, bmp.Height, srccopy);
|
|
bmp.Free;
|
|
|
|
frxPDFBitmap_Destroy(PDF_Bitmap);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
initialization
|
|
PDFiumInitCritSect := TCriticalSection.Create;
|
|
PdfiumModule := 0;
|
|
PDFiumDLLPath := '';
|
|
|
|
finalization
|
|
PDFiumInitCritSect.Free;
|
|
|
|
end.
|