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