Integração completa para geração de PDFs em aplicações Delphi/Object Pascal
uses
System.Net.HttpClient, System.Net.URLClient,
System.JSON, System.SysUtils;
Copie a classe da seção abaixo
var
PdfService: TCloud2PdfService;
Result: TJSONObject;
begin
PdfService := TCloud2PdfService.Create('seu_token_aqui');
try
Result := PdfService.GenerateAsync('<h1>Hello!</h1>', 'ref-123');
ShowMessage('PDF ID: ' + Result.GetValue('pdf_id').Value);
finally
PdfService.Free;
end;
end;
unit Cloud2PdfService;
interface
uses
System.SysUtils, System.Classes, System.JSON,
System.Net.HttpClient, System.Net.URLClient;
type
TCloud2PdfService = class
private
FApiUrl: string;
FApiToken: string;
FHttpClient: TNetHTTPClient;
function CreateAuthHeader: TNetHeaders;
public
constructor Create(const AApiToken: string; const AApiUrl: string = 'https://api.cloud2pdf.com/v1');
destructor Destroy; override;
function GenerateAsync(const AHtml: string; const AReference: string = '';
const AFormat: string = 'A4'; const ALandscape: Boolean = False): TJSONObject;
function CheckStatus(const APdfId: Integer): TJSONObject;
function WaitForCompletion(const APdfId: Integer; const AMaxAttempts: Integer = 60;
const ADelaySeconds: Integer = 5): TJSONObject;
function Download(const APdfId: Integer): TBytes;
function DownloadAndSave(const APdfId: Integer; const AFileName: string): Boolean;
end;
implementation
constructor TCloud2PdfService.Create(const AApiToken: string; const AApiUrl: string);
begin
inherited Create;
FApiUrl := AApiUrl;
FApiToken := AApiToken;
FHttpClient := TNetHTTPClient.Create(nil);
FHttpClient.ConnectionTimeout := 30000;
FHttpClient.ResponseTimeout := 30000;
end;
destructor TCloud2PdfService.Destroy;
begin
FHttpClient.Free;
inherited;
end;
function TCloud2PdfService.CreateAuthHeader: TNetHeaders;
begin
SetLength(Result, 3);
Result[0] := TNameValuePair.Create('Authorization', 'Bearer ' + FApiToken);
Result[1] := TNameValuePair.Create('Content-Type', 'application/json');
Result[2] := TNameValuePair.Create('Accept', 'application/json');
end;
function TCloud2PdfService.GenerateAsync(const AHtml: string; const AReference: string;
const AFormat: string; const ALandscape: Boolean): TJSONObject;
var
RequestBody: TJSONObject;
Options: TJSONObject;
Response: IHTTPResponse;
ResponseStr: string;
begin
Result := nil;
// Montar JSON
RequestBody := TJSONObject.Create;
Options := TJSONObject.Create;
try
Options.AddPair('format', AFormat);
Options.AddPair('landscape', TJSONBool.Create(ALandscape));
RequestBody.AddPair('html', AHtml);
RequestBody.AddPair('is_disk', TJSONTrue.Create);
RequestBody.AddPair('reference', AReference);
RequestBody.AddPair('options', Options);
// Fazer requisição
Response := FHttpClient.Post(
FApiUrl + '/pdf/generate-async',
TStringStream.Create(RequestBody.ToString, TEncoding.UTF8),
nil,
CreateAuthHeader
);
if Response.StatusCode = 202 then
begin
ResponseStr := Response.ContentAsString;
Result := TJSONObject.ParseJSONValue(ResponseStr) as TJSONObject;
end
else
raise Exception.CreateFmt('API Error: %d - %s', [Response.StatusCode, Response.StatusText]);
finally
RequestBody.Free;
end;
end;
function TCloud2PdfService.CheckStatus(const APdfId: Integer): TJSONObject;
var
Response: IHTTPResponse;
ResponseStr: string;
begin
Result := nil;
Response := FHttpClient.Get(
FApiUrl + '/pdf/status/' + IntToStr(APdfId),
nil,
CreateAuthHeader
);
if Response.StatusCode = 200 then
begin
ResponseStr := Response.ContentAsString;
Result := TJSONObject.ParseJSONValue(ResponseStr) as TJSONObject;
end;
end;
function TCloud2PdfService.WaitForCompletion(const APdfId: Integer;
const AMaxAttempts: Integer; const ADelaySeconds: Integer): TJSONObject;
var
I: Integer;
Status: TJSONObject;
StatusValue: string;
begin
Result := nil;
for I := 1 to AMaxAttempts do
begin
Status := CheckStatus(APdfId);
if Assigned(Status) then
begin
StatusValue := Status.GetValue<string>('status');
if StatusValue = 'completed' then
Exit(Status);
if StatusValue = 'failed' then
begin
Status.Free;
raise Exception.Create('PDF generation failed');
end;
Status.Free;
end;
Sleep(ADelaySeconds * 1000);
end;
raise Exception.Create('Timeout waiting for PDF completion');
end;
function TCloud2PdfService.Download(const APdfId: Integer): TBytes;
var
Response: IHTTPResponse;
Stream: TMemoryStream;
begin
Stream := TMemoryStream.Create;
try
Response := FHttpClient.Get(
FApiUrl + '/pdf/download/' + IntToStr(APdfId),
Stream,
CreateAuthHeader
);
if Response.StatusCode = 200 then
begin
Stream.Position := 0;
SetLength(Result, Stream.Size);
Stream.ReadBuffer(Result[0], Stream.Size);
end
else
raise Exception.CreateFmt('Download failed: %d', [Response.StatusCode]);
finally
Stream.Free;
end;
end;
function TCloud2PdfService.DownloadAndSave(const APdfId: Integer; const AFileName: string): Boolean;
var
PdfContent: TBytes;
FileStream: TFileStream;
begin
Result := False;
try
PdfContent := Download(APdfId);
FileStream := TFileStream.Create(AFileName, fmCreate);
try
FileStream.WriteBuffer(PdfContent[0], Length(PdfContent));
Result := True;
finally
FileStream.Free;
end;
except
on E: Exception do
raise Exception.CreateFmt('Error saving PDF: %s', [E.Message]);
end;
end;
end.
unit MainForm;
interface
uses
System.SysUtils, System.Classes, Vcl.Controls, Vcl.Forms, Vcl.StdCtrls,
Vcl.Dialogs, Cloud2PdfService;
type
TFormMain = class(TForm)
MemoHtml: TMemo;
BtnGenerate: TButton;
BtnCheckStatus: TButton;
BtnDownload: TButton;
EditPdfId: TEdit;
EditReference: TEdit;
Label1: TLabel;
Label2: TLabel;
procedure BtnGenerateClick(Sender: TObject);
procedure BtnCheckStatusClick(Sender: TObject);
procedure BtnDownloadClick(Sender: TObject);
private
FPdfService: TCloud2PdfService;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
var
FormMain: TFormMain;
implementation
{$R *.dfm}
constructor TFormMain.Create(AOwner: TComponent);
begin
inherited;
FPdfService := TCloud2PdfService.Create('seu_token_aqui');
end;
destructor TFormMain.Destroy;
begin
FPdfService.Free;
inherited;
end;
procedure TFormMain.BtnGenerateClick(Sender: TObject);
var
Result: TJSONObject;
PdfId: string;
begin
try
Result := FPdfService.GenerateAsync(
MemoHtml.Text,
EditReference.Text,
'A4',
False
);
if Assigned(Result) then
begin
PdfId := Result.GetValue<string>('pdf_id');
EditPdfId.Text := PdfId;
ShowMessage('PDF generation started!' + sLineBreak + 'PDF ID: ' + PdfId);
Result.Free;
end;
except
on E: Exception do
ShowMessage('Error: ' + E.Message);
end;
end;
procedure TFormMain.BtnCheckStatusClick(Sender: TObject);
var
Status: TJSONObject;
StatusValue: string;
begin
if EditPdfId.Text = '' then
begin
ShowMessage('Enter PDF ID first');
Exit;
end;
try
Status := FPdfService.CheckStatus(StrToInt(EditPdfId.Text));
if Assigned(Status) then
begin
StatusValue := Status.GetValue<string>('status');
ShowMessage('Status: ' + StatusValue);
Status.Free;
end;
except
on E: Exception do
ShowMessage('Error: ' + E.Message);
end;
end;
procedure TFormMain.BtnDownloadClick(Sender: TObject);
var
SaveDialog: TSaveDialog;
begin
if EditPdfId.Text = '' then
begin
ShowMessage('Enter PDF ID first');
Exit;
end;
SaveDialog := TSaveDialog.Create(nil);
try
SaveDialog.Filter := 'PDF Files (*.pdf)|*.pdf';
SaveDialog.DefaultExt := 'pdf';
SaveDialog.FileName := 'document.pdf';
if SaveDialog.Execute then
begin
try
if FPdfService.DownloadAndSave(StrToInt(EditPdfId.Text), SaveDialog.FileName) then
ShowMessage('PDF saved successfully!')
else
ShowMessage('Error saving PDF');
except
on E: Exception do
ShowMessage('Error: ' + E.Message);
end;
end;
finally
SaveDialog.Free;
end;
end;
end.
Para não bloquear a interface durante o polling
type
TPdfGenerationThread = class(TThread)
private
FPdfService: TCloud2PdfService;
FHtml: string;
FReference: string;
FPdfId: Integer;
FOnComplete: TProc<Integer, string>;
FOnError: TProc<string>;
protected
procedure Execute; override;
public
constructor Create(APdfService: TCloud2PdfService; const AHtml, AReference: string;
AOnComplete: TProc<Integer, string>; AOnError: TProc<string>);
end;
constructor TPdfGenerationThread.Create(APdfService: TCloud2PdfService;
const AHtml, AReference: string; AOnComplete: TProc<Integer, string>;
AOnError: TProc<string>);
begin
inherited Create(True);
FPdfService := APdfService;
FHtml := AHtml;
FReference := AReference;
FOnComplete := AOnComplete;
FOnError := AOnError;
FreeOnTerminate := True;
end;
procedure TPdfGenerationThread.Execute;
var
Result: TJSONObject;
Status: TJSONObject;
PublicUrl: string;
begin
try
// Iniciar geração
Result := FPdfService.GenerateAsync(FHtml, FReference);
if Assigned(Result) then
begin
FPdfId := Result.GetValue<Integer>('pdf_id');
Result.Free;
// Aguardar conclusão
Status := FPdfService.WaitForCompletion(FPdfId);
if Assigned(Status) then
begin
PublicUrl := Status.GetValue<TJSONObject>('public_link').GetValue<string>('url');
Synchronize(
procedure
begin
FOnComplete(FPdfId, PublicUrl);
end
);
Status.Free;
end;
end;
except
on E: Exception do
begin
Synchronize(
procedure
begin
FOnError(E.Message);
end
);
end;
end;
end;
// Uso:
procedure TFormMain.BtnGenerateAsyncClick(Sender: TObject);
begin
TPdfGenerationThread.Create(
FPdfService,
MemoHtml.Text,
EditReference.Text,
procedure(APdfId: Integer; AUrl: string)
begin
ShowMessage('PDF Ready!' + sLineBreak + 'ID: ' + IntToStr(APdfId) + sLineBreak + 'URL: ' + AUrl);
end,
procedure(AError: string)
begin
ShowMessage('Error: ' + AError);
end
).Start;
ShowMessage('PDF generation started in background...');
end;
Disponível nativamente no Delphi (System.Net.HttpClient)
Use TThread para processamento assíncrono
Sempre use .Free nos TJSONObject retornados
TNetHTTPClient disponível desde Delphi 10.2 Tokyo