Delphi + Cloud2Pdf

Integração completa para geração de PDFs em aplicações Delphi/Object Pascal

🔥 TNetHTTPClient 💪 Native REST ⚡ Async Ready

🚀 Quick Start

1

Adicionar Units

uses System.Net.HttpClient, System.Net.URLClient, System.JSON, System.SysUtils;
2

Criar Classe Cloud2PdfService

Copie a classe da seção abaixo

3

Usar no Form

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;

📦 Classe TCloud2PdfService

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.

📝 Exemplo Completo no Form

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.

⚡ Geração Assíncrona com Thread

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;

💡 Dicas

Use TNetHTTPClient

Disponível nativamente no Delphi (System.Net.HttpClient)

Threads para Não Bloquear UI

Use TThread para processamento assíncrono

Libere Objetos JSON

Sempre use .Free nos TJSONObject retornados

Compatível com Delphi 10.2+

TNetHTTPClient disponível desde Delphi 10.2 Tokyo