أمثلة برمجية
برامج كاملة بخمس لغات. يقرأ كل منها المفتاح من SAHIFA_API_KEY، ويستدعي الواجهة البرمجية الفعلية، ويحفظ النتيجة. لا تستخدم إلا المكتبة القياسية، باستثناء requests في Python. وتُشغَّل جميعها على الواجهة البرمجية قبل كل تغيير في هذه الصفحة. التعليقات داخل الشيفرة باللغة الإنجليزية، كما هو المعتاد.
المتطلبات
| اللغة | الإصدار | التشغيل |
|---|---|---|
| curl | 7.76 فأحدث (لخيار --fail-with-body) | bash pdf.sh |
| JavaScript | Node.js 18 فأحدث (fetch مدمجة) | node pdf.mjs |
| Python | 3.8 فأحدث و pip install requests | python pdf.py |
| .NET | .NET 8 فأحدث | dotnet run pdf.cs في .NET 10، أو الصقه في Program.cs لتطبيق سطر أوامر |
| Java | Java 18 فأحدث لمثال PDF (مصدر بترميز UTF-8)، و 11 فأحدث للبقية | java Pdf.java |
من HTML إلى PDF
فاتورة عربية بتذييل للصفحة، تُحفظ باسم invoice.pdf.
# HTML to PDF with an Arabic footer. Requires: SAHIFA_API_KEY in the environment.
curl https://api.sahifa.dev/v3/convert/pdf \
--user "api:$SAHIFA_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"source": "<html dir=\"rtl\" lang=\"ar\"><body><h1>فاتورة ضريبية</h1><p>الإجمالي: 1,150.00 ر.س</p></body></html>",
"format": "A4",
"margin": "20mm",
"footer": { "source": "<div style=\"width:100%;text-align:center\">صفحة {{page}} من {{total}}</div>" }
}' \
--fail-with-body --output invoice.pdf// HTML to PDF with an Arabic footer. Node.js 18+ (built-in fetch).
// Run: SAHIFA_API_KEY=... node pdf.mjs
import { writeFile } from 'node:fs/promises';
const html = `<html dir="rtl" lang="ar"><body>
<h1>فاتورة ضريبية</h1>
<p>الإجمالي: 1,150.00 ر.س</p>
</body></html>`;
const res = await fetch('https://api.sahifa.dev/v3/convert/pdf', {
method: 'POST',
headers: {
Authorization: 'Basic ' + Buffer.from(`api:${process.env.SAHIFA_API_KEY}`).toString('base64'),
'Content-Type': 'application/json',
},
body: JSON.stringify({
source: html,
format: 'A4',
margin: '20mm',
footer: { source: '<div style="width:100%;text-align:center">صفحة {{page}} من {{total}}</div>' },
}),
});
if (!res.ok) throw new Error(`Sahifa error ${res.status}: ${(await res.json()).error}`);
await writeFile('invoice.pdf', Buffer.from(await res.arrayBuffer()));
console.log(`invoice.pdf created in ${res.headers.get('x-response-duration')} ms`);# HTML to PDF with an Arabic footer. Python 3.8+, requires: pip install requests
# Run: SAHIFA_API_KEY=... python pdf.py
import os
import requests
html = """<html dir="rtl" lang="ar"><body>
<h1>فاتورة ضريبية</h1>
<p>الإجمالي: 1,150.00 ر.س</p>
</body></html>"""
res = requests.post(
"https://api.sahifa.dev/v3/convert/pdf",
auth=("api", os.environ["SAHIFA_API_KEY"]),
json={
"source": html,
"format": "A4",
"margin": "20mm",
"footer": {"source": '<div style="width:100%;text-align:center">صفحة {{page}} من {{total}}</div>'},
},
timeout=60,
)
if not res.ok:
raise RuntimeError(f"Sahifa error {res.status_code}: {res.json()['error']}")
with open("invoice.pdf", "wb") as f:
f.write(res.content)
print(f"invoice.pdf created in {res.headers['X-Response-Duration']} ms")// HTML to PDF with an Arabic footer. .NET 8+.
// .NET 10: SAHIFA_API_KEY=... dotnet run pdf.cs (or paste into Program.cs of a console app)
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json.Nodes;
var key = Environment.GetEnvironmentVariable("SAHIFA_API_KEY");
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(60) };
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"api:{key}")));
var html = """
<html dir="rtl" lang="ar"><body>
<h1>فاتورة ضريبية</h1>
<p>الإجمالي: 1,150.00 ر.س</p>
</body></html>
""";
var body = new JsonObject
{
["source"] = html,
["format"] = "A4",
["margin"] = "20mm",
["footer"] = new JsonObject { ["source"] = "<div style=\"width:100%;text-align:center\">صفحة {{page}} من {{total}}</div>" },
};
var res = await http.PostAsync("https://api.sahifa.dev/v3/convert/pdf",
new StringContent(body.ToJsonString(), Encoding.UTF8, "application/json"));
if (!res.IsSuccessStatusCode)
throw new Exception($"Sahifa error {(int)res.StatusCode}: {await res.Content.ReadAsStringAsync()}");
await File.WriteAllBytesAsync("invoice.pdf", await res.Content.ReadAsByteArrayAsync());
Console.WriteLine("invoice.pdf saved");// HTML to PDF with an Arabic footer. Java 18+ (UTF-8 source by default).
// Run: SAHIFA_API_KEY=... java Pdf.java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;
public class Pdf {
public static void main(String[] args) throws Exception {
String key = System.getenv("SAHIFA_API_KEY");
String auth = Base64.getEncoder().encodeToString(("api:" + key).getBytes(StandardCharsets.UTF_8));
// JSON written by hand to keep the example dependency-free; use Jackson or Gson in real code.
String body = """
{
"source": "<html dir='rtl' lang='ar'><body><h1>فاتورة ضريبية</h1><p>الإجمالي: 1,150.00 ر.س</p></body></html>",
"format": "A4",
"margin": "20mm",
"footer": { "source": "<div style='width:100%;text-align:center'>صفحة {{page}} من {{total}}</div>" }
}
""";
HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.sahifa.dev/v3/convert/pdf"))
.header("Authorization", "Basic " + auth)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body, StandardCharsets.UTF_8))
.build();
HttpResponse<byte[]> res = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofByteArray());
if (res.statusCode() != 200) {
throw new RuntimeException("Sahifa error " + res.statusCode() + ": " + new String(res.body(), StandardCharsets.UTF_8));
}
Files.write(Path.of("invoice.pdf"), res.body());
System.out.println("invoice.pdf saved");
}
}لقطة شاشة لصفحة ويب
الصفحة كاملة بصيغة PNG، مع إخفاء نوافذ الموافقة على ملفات تعريف الارتباط، وتُحفظ باسم page.png.
# Full-page screenshot of a URL, without cookie banners. Requires: SAHIFA_API_KEY.
curl --get https://api.sahifa.dev/take \
--data-urlencode "access_key=$SAHIFA_API_KEY" \
--data-urlencode "url=https://example.com" \
--data "format=png" \
--data "full_page=true" \
--data "block_cookie_banners=true" \
--fail-with-body --output page.png// Full-page screenshot of a URL, without cookie banners. Node.js 18+.
// Run: SAHIFA_API_KEY=... node screenshot.mjs
import { writeFile } from 'node:fs/promises';
const params = new URLSearchParams({
access_key: process.env.SAHIFA_API_KEY,
url: 'https://example.com',
format: 'png',
full_page: 'true',
block_cookie_banners: 'true',
});
const res = await fetch(`https://api.sahifa.dev/take?${params}`);
if (!res.ok) throw new Error(`Sahifa error ${res.status}: ${(await res.json()).error}`);
await writeFile('page.png', Buffer.from(await res.arrayBuffer()));
console.log('page.png saved');# Full-page screenshot of a URL, without cookie banners. Requires: pip install requests
# Run: SAHIFA_API_KEY=... python screenshot.py
import os
import requests
res = requests.get(
"https://api.sahifa.dev/take",
params={
"access_key": os.environ["SAHIFA_API_KEY"],
"url": "https://example.com",
"format": "png",
"full_page": "true",
"block_cookie_banners": "true",
},
timeout=60,
)
if not res.ok:
raise RuntimeError(f"Sahifa error {res.status_code}: {res.json()['error']}")
with open("page.png", "wb") as f:
f.write(res.content)
print("page.png saved")// Full-page screenshot of a URL, without cookie banners. .NET 8+.
// .NET 10: SAHIFA_API_KEY=... dotnet run screenshot.cs
var key = Environment.GetEnvironmentVariable("SAHIFA_API_KEY");
var query = string.Join("&", new Dictionary<string, string>
{
["access_key"] = key!,
["url"] = "https://example.com",
["format"] = "png",
["full_page"] = "true",
["block_cookie_banners"] = "true",
}.Select(p => $"{p.Key}={Uri.EscapeDataString(p.Value)}"));
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(60) };
var res = await http.GetAsync($"https://api.sahifa.dev/take?{query}");
if (!res.IsSuccessStatusCode)
throw new Exception($"Sahifa error {(int)res.StatusCode}: {await res.Content.ReadAsStringAsync()}");
await File.WriteAllBytesAsync("page.png", await res.Content.ReadAsByteArrayAsync());
Console.WriteLine("page.png saved");// Full-page screenshot of a URL, without cookie banners. Java 11+.
// Run: SAHIFA_API_KEY=... java Screenshot.java
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
public class Screenshot {
public static void main(String[] args) throws Exception {
Map<String, String> params = new LinkedHashMap<>();
params.put("access_key", System.getenv("SAHIFA_API_KEY"));
params.put("url", "https://example.com");
params.put("format", "png");
params.put("full_page", "true");
params.put("block_cookie_banners", "true");
String query = params.entrySet().stream()
.map(e -> e.getKey() + "=" + URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8))
.collect(Collectors.joining("&"));
HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.sahifa.dev/take?" + query)).GET().build();
HttpResponse<byte[]> res = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofByteArray());
if (res.statusCode() != 200) {
throw new RuntimeException("Sahifa error " + res.statusCode() + ": " + new String(res.body(), StandardCharsets.UTF_8));
}
Files.write(Path.of("page.png"), res.body());
System.out.println("page.png saved");
}
}إعادة المحاولة
يعيد المحاولة عند 429 و 503 مع انتظار متزايد، ويلتزم بترويسة Retry-After. أما الأخطاء الأخرى فتُرفع فورًا.
# curl retries 408, 429, 500, 502, 503 and 504 responses on its own, honouring Retry-After.
curl https://api.sahifa.dev/v3/convert/pdf \
--user "api:$SAHIFA_API_KEY" \
--header "Content-Type: application/json" \
--data '{ "source": "https://example.com" }' \
--retry 4 --retry-delay 2 --retry-max-time 60 \
--fail-with-body --output example.pdf// Retry on 429 (queue full) and 503 (temporarily unavailable), honouring Retry-After.
// Node.js 18+. Run: SAHIFA_API_KEY=... node retry.mjs
import { writeFile } from 'node:fs/promises';
async function render(body, attempts = 4) {
for (let attempt = 1; ; attempt++) {
const res = await fetch('https://api.sahifa.dev/v3/convert/pdf', {
method: 'POST',
headers: { 'X-API-Key': process.env.SAHIFA_API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
if (res.ok) return Buffer.from(await res.arrayBuffer());
const retryable = res.status === 429 || res.status === 503;
if (!retryable || attempt === attempts) throw new Error(`Sahifa error ${res.status}: ${(await res.json()).error}`);
const wait = Number(res.headers.get('retry-after')) || 2 ** attempt;
await new Promise((r) => setTimeout(r, wait * 1000));
}
}
await writeFile('example.pdf', await render({ source: 'https://example.com' }));
console.log('example.pdf saved');# Retry on 429 (queue full) and 503 (temporarily unavailable), honouring Retry-After.
# Requires: pip install requests. Run: SAHIFA_API_KEY=... python retry.py
import os
import time
import requests
def render(body, attempts=4):
for attempt in range(1, attempts + 1):
res = requests.post(
"https://api.sahifa.dev/v3/convert/pdf",
headers={"X-API-Key": os.environ["SAHIFA_API_KEY"]},
json=body,
timeout=60,
)
if res.ok:
return res.content
if res.status_code not in (429, 503) or attempt == attempts:
raise RuntimeError(f"Sahifa error {res.status_code}: {res.json()['error']}")
time.sleep(int(res.headers.get("Retry-After", 2 ** attempt)))
with open("example.pdf", "wb") as f:
f.write(render({"source": "https://example.com"}))
print("example.pdf saved")// Retry on 429 (queue full) and 503 (temporarily unavailable), honouring Retry-After. .NET 8+.
// .NET 10: SAHIFA_API_KEY=... dotnet run retry.cs
using System.Net;
using System.Text;
using System.Text.Json.Nodes;
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(60) };
http.DefaultRequestHeaders.Add("X-API-Key", Environment.GetEnvironmentVariable("SAHIFA_API_KEY"));
async Task<byte[]> Render(JsonObject body, int attempts = 4)
{
for (var attempt = 1; ; attempt++)
{
var res = await http.PostAsync("https://api.sahifa.dev/v3/convert/pdf",
new StringContent(body.ToJsonString(), Encoding.UTF8, "application/json"));
if (res.IsSuccessStatusCode) return await res.Content.ReadAsByteArrayAsync();
var retryable = res.StatusCode is HttpStatusCode.TooManyRequests or HttpStatusCode.ServiceUnavailable;
if (!retryable || attempt == attempts)
throw new Exception($"Sahifa error {(int)res.StatusCode}: {await res.Content.ReadAsStringAsync()}");
var wait = res.Headers.RetryAfter?.Delta ?? TimeSpan.FromSeconds(Math.Pow(2, attempt));
await Task.Delay(wait);
}
}
await File.WriteAllBytesAsync("example.pdf", await Render(new JsonObject { ["source"] = "https://example.com" }));
Console.WriteLine("example.pdf saved");// Retry on 429 (queue full) and 503 (temporarily unavailable), honouring Retry-After. Java 11+.
// Run: SAHIFA_API_KEY=... java Retry.java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
public class Retry {
static final HttpClient HTTP = HttpClient.newHttpClient();
static byte[] render(String json, int attempts) throws Exception {
for (int attempt = 1; ; attempt++) {
HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.sahifa.dev/v3/convert/pdf"))
.header("X-API-Key", System.getenv("SAHIFA_API_KEY"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json, StandardCharsets.UTF_8))
.build();
HttpResponse<byte[]> res = HTTP.send(request, HttpResponse.BodyHandlers.ofByteArray());
if (res.statusCode() == 200) return res.body();
boolean retryable = res.statusCode() == 429 || res.statusCode() == 503;
if (!retryable || attempt == attempts) {
throw new RuntimeException("Sahifa error " + res.statusCode() + ": " + new String(res.body(), StandardCharsets.UTF_8));
}
long wait = res.headers().firstValue("Retry-After").map(Long::parseLong).orElse((long) Math.pow(2, attempt));
Thread.sleep(wait * 1000);
}
}
public static void main(String[] args) throws Exception {
Files.write(Path.of("example.pdf"), render("{\"source\": \"https://example.com\"}", 4));
System.out.println("example.pdf saved");
}
}المتصفحات وشيفرة الواجهة الأمامية
لا تستدعِ الواجهة البرمجية من المتصفح: سيرى المفتاحَ كلُّ زائر. استدعها من خادمك ثم أرسل الملف إلى المتصفح. مثلًا في Express:
app.get('/invoices/:id.pdf', async (req, res) => {
const html = await renderInvoiceHtml(req.params.id); // your template
const pdf = await fetch('https://api.sahifa.dev/v3/convert/pdf', {
method: 'POST',
headers: { 'X-API-Key': process.env.SAHIFA_API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ source: html, format: 'A4', margin: '15mm' }),
});
if (!pdf.ok) return res.status(502).send('PDF could not be created');
res.type('application/pdf').send(Buffer.from(await pdf.arrayBuffer()));
});
استخدام مكتبة قائمة لـ PDFShift أو ScreenshotOne
إذا كانت شيفرتك تستخدم مكتبة عميل لإحدى الخدمتين، فوجّه عنوانها الأساسي إلى https://api.sahifa.dev واستخدم مفتاح صحيفة. راجع الانتقال.