feat: add Go, PHP, and Laravel SDKs
Some checks failed
Build & Deploy to Staging / Build & Deploy to Staging (push) Has been cancelled
Some checks failed
Build & Deploy to Staging / Build & Deploy to Staging (push) Has been cancelled
- Go SDK: zero deps, functional options pattern, full endpoint coverage - PHP SDK: PHP 8.1+, curl-based, PdfOptions class, PSR-4 autoloading - Laravel package: ServiceProvider, Facade, config publishing - All SDKs document complete PDF options including new v0.4.5 params
This commit is contained in:
parent
1545df9a7b
commit
bc67c52d3a
13 changed files with 1133 additions and 0 deletions
151
sdk/go/README.md
Normal file
151
sdk/go/README.md
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
# DocFast Go SDK
|
||||
|
||||
Official Go client for the [DocFast](https://docfast.dev) HTML/Markdown to PDF API.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
go get github.com/docfast/docfast-go
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
docfast "github.com/docfast/docfast-go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
client := docfast.New("df_pro_your_api_key")
|
||||
|
||||
// HTML to PDF
|
||||
pdf, err := client.HTML("<h1>Hello World</h1><p>Generated with DocFast</p>", nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
os.WriteFile("output.pdf", pdf, 0644)
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### HTML to PDF
|
||||
|
||||
```go
|
||||
pdf, err := client.HTML("<h1>Hello</h1>", &docfast.PDFOptions{
|
||||
Format: "A4",
|
||||
Landscape: true,
|
||||
Margin: &docfast.Margin{Top: "20mm", Bottom: "20mm"},
|
||||
})
|
||||
```
|
||||
|
||||
### HTML with custom CSS
|
||||
|
||||
```go
|
||||
pdf, err := client.HTMLWithCSS(
|
||||
"<h1>Styled</h1>",
|
||||
"h1 { color: navy; font-family: Georgia; }",
|
||||
nil,
|
||||
)
|
||||
```
|
||||
|
||||
### Markdown to PDF
|
||||
|
||||
```go
|
||||
pdf, err := client.Markdown("# Report\n\nGenerated **automatically**.", nil)
|
||||
```
|
||||
|
||||
### URL to PDF
|
||||
|
||||
```go
|
||||
pdf, err := client.URL("https://example.com", &docfast.PDFOptions{
|
||||
Format: "Letter",
|
||||
PrintBackground: docfast.Bool(true),
|
||||
})
|
||||
```
|
||||
|
||||
### Headers and Footers
|
||||
|
||||
```go
|
||||
pdf, err := client.HTML(html, &docfast.PDFOptions{
|
||||
DisplayHeaderFooter: true,
|
||||
HeaderTemplate: `<div style="font-size:10px;text-align:center;width:100%">My Document</div>`,
|
||||
FooterTemplate: `<div style="font-size:10px;text-align:center;width:100%">Page <span class="pageNumber"></span> of <span class="totalPages"></span></div>`,
|
||||
Margin: &docfast.Margin{Top: "40mm", Bottom: "20mm"},
|
||||
})
|
||||
```
|
||||
|
||||
### Custom Page Size
|
||||
|
||||
```go
|
||||
pdf, err := client.HTML(html, &docfast.PDFOptions{
|
||||
Width: "8.5in",
|
||||
Height: "11in",
|
||||
Scale: 0.8,
|
||||
})
|
||||
```
|
||||
|
||||
### Templates
|
||||
|
||||
```go
|
||||
// List available templates
|
||||
templates, err := client.Templates()
|
||||
|
||||
// Render a template
|
||||
pdf, err := client.RenderTemplate("invoice", map[string]interface{}{
|
||||
"company": "Acme Corp",
|
||||
"items": []map[string]interface{}{{"name": "Widget", "price": 9.99}},
|
||||
"_format": "A4",
|
||||
})
|
||||
```
|
||||
|
||||
## PDF Options
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `Format` | string | `"A4"` | Page size: A4, Letter, Legal, A3, A5, Tabloid |
|
||||
| `Landscape` | bool | `false` | Landscape orientation |
|
||||
| `Margin` | *Margin | `nil` | Page margins (CSS units) |
|
||||
| `PrintBackground` | *bool | `true` | Print background graphics |
|
||||
| `Filename` | string | `""` | Suggested filename |
|
||||
| `HeaderTemplate` | string | `""` | HTML header template |
|
||||
| `FooterTemplate` | string | `""` | HTML footer template |
|
||||
| `DisplayHeaderFooter` | bool | `false` | Show header/footer |
|
||||
| `Scale` | float64 | `1` | Rendering scale (0.1–2.0) |
|
||||
| `PageRanges` | string | `""` | Pages to print (e.g. "1-3,5") |
|
||||
| `PreferCSSPageSize` | bool | `false` | Prefer CSS @page size |
|
||||
| `Width` | string | `""` | Custom paper width |
|
||||
| `Height` | string | `""` | Custom paper height |
|
||||
|
||||
## Error Handling
|
||||
|
||||
```go
|
||||
pdf, err := client.HTML("<h1>Test</h1>", nil)
|
||||
if err != nil {
|
||||
var apiErr *docfast.Error
|
||||
if errors.As(err, &apiErr) {
|
||||
fmt.Printf("API error: %s (status %d)\n", apiErr.Message, apiErr.StatusCode)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
```go
|
||||
// Custom base URL (e.g. for self-hosted or staging)
|
||||
client := docfast.New("key", docfast.WithBaseURL("https://staging.docfast.dev"))
|
||||
|
||||
// Custom HTTP client
|
||||
client := docfast.New("key", docfast.WithHTTPClient(&http.Client{
|
||||
Timeout: 120 * time.Second,
|
||||
}))
|
||||
```
|
||||
|
||||
## Links
|
||||
|
||||
- [Documentation](https://docfast.dev/docs)
|
||||
- [API Reference](https://docfast.dev/openapi.json)
|
||||
- [Get an API Key](https://docfast.dev)
|
||||
293
sdk/go/docfast.go
Normal file
293
sdk/go/docfast.go
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
// Package docfast provides a Go client for the DocFast HTML/Markdown to PDF API.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// client := docfast.New("df_pro_your_key")
|
||||
// pdf, err := client.HTML("<h1>Hello</h1>", nil)
|
||||
package docfast
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const defaultBaseURL = "https://docfast.dev"
|
||||
|
||||
// Margin defines PDF page margins using CSS units (e.g. "20mm", "1in").
|
||||
type Margin struct {
|
||||
Top string `json:"top,omitempty"`
|
||||
Bottom string `json:"bottom,omitempty"`
|
||||
Left string `json:"left,omitempty"`
|
||||
Right string `json:"right,omitempty"`
|
||||
}
|
||||
|
||||
// PDFOptions configures PDF generation. All fields are optional.
|
||||
type PDFOptions struct {
|
||||
// Page size: A4, Letter, Legal, A3, A5, Tabloid. Ignored if Width/Height set.
|
||||
Format string `json:"format,omitempty"`
|
||||
// Landscape orientation.
|
||||
Landscape bool `json:"landscape,omitempty"`
|
||||
// Page margins.
|
||||
Margin *Margin `json:"margin,omitempty"`
|
||||
// Print background graphics and colors. Default: true.
|
||||
PrintBackground *bool `json:"printBackground,omitempty"`
|
||||
// Suggested filename for the PDF download.
|
||||
Filename string `json:"filename,omitempty"`
|
||||
// HTML template for page header. Requires DisplayHeaderFooter: true.
|
||||
// Supports CSS classes: date, title, url, pageNumber, totalPages.
|
||||
HeaderTemplate string `json:"headerTemplate,omitempty"`
|
||||
// HTML template for page footer. Same classes as HeaderTemplate.
|
||||
FooterTemplate string `json:"footerTemplate,omitempty"`
|
||||
// Show header and footer templates.
|
||||
DisplayHeaderFooter bool `json:"displayHeaderFooter,omitempty"`
|
||||
// Scale of webpage rendering (0.1 to 2.0). Default: 1.
|
||||
Scale float64 `json:"scale,omitempty"`
|
||||
// Paper ranges to print, e.g. "1-3,5".
|
||||
PageRanges string `json:"pageRanges,omitempty"`
|
||||
// Give CSS @page size priority over Format.
|
||||
PreferCSSPageSize bool `json:"preferCSSPageSize,omitempty"`
|
||||
// Paper width with units (e.g. "8.5in"). Overrides Format.
|
||||
Width string `json:"width,omitempty"`
|
||||
// Paper height with units (e.g. "11in"). Overrides Format.
|
||||
Height string `json:"height,omitempty"`
|
||||
}
|
||||
|
||||
// Template describes an available PDF template.
|
||||
type Template struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
// Error is returned when the API responds with an error.
|
||||
type Error struct {
|
||||
StatusCode int
|
||||
Message string
|
||||
Code string
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
if e.Code != "" {
|
||||
return fmt.Sprintf("docfast: %s (code=%s, status=%d)", e.Message, e.Code, e.StatusCode)
|
||||
}
|
||||
return fmt.Sprintf("docfast: %s (status=%d)", e.Message, e.StatusCode)
|
||||
}
|
||||
|
||||
// ClientOption configures the Client.
|
||||
type ClientOption func(*Client)
|
||||
|
||||
// WithBaseURL sets a custom API base URL.
|
||||
func WithBaseURL(url string) ClientOption {
|
||||
return func(c *Client) { c.baseURL = url }
|
||||
}
|
||||
|
||||
// WithHTTPClient sets a custom http.Client.
|
||||
func WithHTTPClient(hc *http.Client) ClientOption {
|
||||
return func(c *Client) { c.httpClient = hc }
|
||||
}
|
||||
|
||||
// Client is the DocFast API client.
|
||||
type Client struct {
|
||||
apiKey string
|
||||
baseURL string
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
// New creates a new DocFast client.
|
||||
func New(apiKey string, opts ...ClientOption) *Client {
|
||||
c := &Client{
|
||||
apiKey: apiKey,
|
||||
baseURL: defaultBaseURL,
|
||||
httpClient: &http.Client{
|
||||
Timeout: 60 * time.Second,
|
||||
},
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(c)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Client) post(path string, body map[string]interface{}) ([]byte, error) {
|
||||
data, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("docfast: marshal error: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", c.baseURL+path, bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("docfast: request error: %w", err)
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+c.apiKey)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("docfast: request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("docfast: read error: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
apiErr := &Error{StatusCode: resp.StatusCode, Message: fmt.Sprintf("HTTP %d", resp.StatusCode)}
|
||||
var errResp struct {
|
||||
Error string `json:"error"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
if json.Unmarshal(respBody, &errResp) == nil {
|
||||
if errResp.Error != "" {
|
||||
apiErr.Message = errResp.Error
|
||||
}
|
||||
apiErr.Code = errResp.Code
|
||||
}
|
||||
return nil, apiErr
|
||||
}
|
||||
|
||||
return respBody, nil
|
||||
}
|
||||
|
||||
func (c *Client) get(path string) ([]byte, error) {
|
||||
req, err := http.NewRequest("GET", c.baseURL+path, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("docfast: request error: %w", err)
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+c.apiKey)
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("docfast: request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("docfast: read error: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
apiErr := &Error{StatusCode: resp.StatusCode, Message: fmt.Sprintf("HTTP %d", resp.StatusCode)}
|
||||
var errResp struct {
|
||||
Error string `json:"error"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
if json.Unmarshal(respBody, &errResp) == nil && errResp.Error != "" {
|
||||
apiErr.Message = errResp.Error
|
||||
apiErr.Code = errResp.Code
|
||||
}
|
||||
return nil, apiErr
|
||||
}
|
||||
|
||||
return respBody, nil
|
||||
}
|
||||
|
||||
func mergeOptions(body map[string]interface{}, opts *PDFOptions) {
|
||||
if opts == nil {
|
||||
return
|
||||
}
|
||||
if opts.Format != "" {
|
||||
body["format"] = opts.Format
|
||||
}
|
||||
if opts.Landscape {
|
||||
body["landscape"] = true
|
||||
}
|
||||
if opts.Margin != nil {
|
||||
body["margin"] = opts.Margin
|
||||
}
|
||||
if opts.PrintBackground != nil {
|
||||
body["printBackground"] = *opts.PrintBackground
|
||||
}
|
||||
if opts.Filename != "" {
|
||||
body["filename"] = opts.Filename
|
||||
}
|
||||
if opts.HeaderTemplate != "" {
|
||||
body["headerTemplate"] = opts.HeaderTemplate
|
||||
}
|
||||
if opts.FooterTemplate != "" {
|
||||
body["footerTemplate"] = opts.FooterTemplate
|
||||
}
|
||||
if opts.DisplayHeaderFooter {
|
||||
body["displayHeaderFooter"] = true
|
||||
}
|
||||
if opts.Scale != 0 {
|
||||
body["scale"] = opts.Scale
|
||||
}
|
||||
if opts.PageRanges != "" {
|
||||
body["pageRanges"] = opts.PageRanges
|
||||
}
|
||||
if opts.PreferCSSPageSize {
|
||||
body["preferCSSPageSize"] = true
|
||||
}
|
||||
if opts.Width != "" {
|
||||
body["width"] = opts.Width
|
||||
}
|
||||
if opts.Height != "" {
|
||||
body["height"] = opts.Height
|
||||
}
|
||||
}
|
||||
|
||||
// HTML converts HTML content to PDF. Returns the raw PDF bytes.
|
||||
func (c *Client) HTML(html string, opts *PDFOptions) ([]byte, error) {
|
||||
body := map[string]interface{}{"html": html}
|
||||
mergeOptions(body, opts)
|
||||
return c.post("/v1/convert/html", body)
|
||||
}
|
||||
|
||||
// HTMLWithCSS converts an HTML fragment with optional CSS to PDF.
|
||||
func (c *Client) HTMLWithCSS(html, css string, opts *PDFOptions) ([]byte, error) {
|
||||
body := map[string]interface{}{"html": html, "css": css}
|
||||
mergeOptions(body, opts)
|
||||
return c.post("/v1/convert/html", body)
|
||||
}
|
||||
|
||||
// Markdown converts Markdown content to PDF.
|
||||
func (c *Client) Markdown(markdown string, opts *PDFOptions) ([]byte, error) {
|
||||
body := map[string]interface{}{"markdown": markdown}
|
||||
mergeOptions(body, opts)
|
||||
return c.post("/v1/convert/markdown", body)
|
||||
}
|
||||
|
||||
// MarkdownWithCSS converts Markdown with optional CSS to PDF.
|
||||
func (c *Client) MarkdownWithCSS(markdown, css string, opts *PDFOptions) ([]byte, error) {
|
||||
body := map[string]interface{}{"markdown": markdown, "css": css}
|
||||
mergeOptions(body, opts)
|
||||
return c.post("/v1/convert/markdown", body)
|
||||
}
|
||||
|
||||
// URL converts a web page at the given URL to PDF.
|
||||
func (c *Client) URL(url string, opts *PDFOptions) ([]byte, error) {
|
||||
body := map[string]interface{}{"url": url}
|
||||
mergeOptions(body, opts)
|
||||
return c.post("/v1/convert/url", body)
|
||||
}
|
||||
|
||||
// Templates returns the list of available PDF templates.
|
||||
func (c *Client) Templates() ([]Template, error) {
|
||||
data, err := c.get("/v1/templates")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var result struct {
|
||||
Templates []Template `json:"templates"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &result); err != nil {
|
||||
return nil, fmt.Errorf("docfast: decode error: %w", err)
|
||||
}
|
||||
return result.Templates, nil
|
||||
}
|
||||
|
||||
// RenderTemplate renders a template with the given data and returns PDF bytes.
|
||||
func (c *Client) RenderTemplate(templateID string, data map[string]interface{}) ([]byte, error) {
|
||||
body := map[string]interface{}{"data": data}
|
||||
return c.post("/v1/templates/"+templateID, body)
|
||||
}
|
||||
|
||||
// Bool is a helper to create a *bool for PDFOptions.PrintBackground.
|
||||
func Bool(v bool) *bool { return &v }
|
||||
3
sdk/go/go.mod
Normal file
3
sdk/go/go.mod
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
module github.com/docfast/docfast-go
|
||||
|
||||
go 1.21
|
||||
Loading…
Add table
Add a link
Reference in a new issue