\usepackage{rust}

واجهة API لتحويل LaTeX إلى PDF بلغة Rust

جمّع LaTeX إلى PDF من Rust باستخدام FormaTeX REST API. مكتبتا reqwest وtokio غير المتزامنتان، دون تثبيت TeX Live ودون ملف تنفيذي لتضمينه.

Also available in:Python·Node.js·Go·PHP·Ruby·Rust·cURL|Full API reference

\section{لماذا Rust}

لماذا Rust مع FormaTeX

تزامن بلا كلفة إضافية

ينسجم عميل reqwest غير المتزامن بشكل طبيعي مع بيئة تشغيل Tokio. جمّع عدة مستندات بالتزامن دون تعقيد إضافي.

صور Docker خفيفة

يبقى ملف Rust التنفيذي لديك صغيرًا، دون TeX Live ودون apt install texlive-full. يتولى FormaTeX بيئة التجميع بالكامل.

معالجة أخطاء آمنة الأنواع

يتناسب النوع Result في Rust جيدًا مع FormaTeX API. حوّل أخطاء HTTP إلى أخطاء المجال على مستوى النوع، دون مفاجآت وقت التشغيل.

\section{Dependencies}

تبعيات Cargo.toml

Cargo.toml
[dependencies] reqwest = { version = "0.12", features = ["json"] } tokio = { version = "1", features = ["full"] } serde_json = "1" serde = { version = "1", features = ["derive"] } # For Axum integration axum = "0.7" bytes = "1"

\section{بداية سريعة}

بداية سريعة مع reqwest

يجمّع المثال أدناه مستند LaTeX بسيطًا باستخدام reqwest ويكتب ملف PDF الناتج على القرص. اقرأ مفتاح API من البيئة باستخدام std::env::var("FORMATEX_API_KEY") .

src/main.rs
use reqwest::Client;
use serde_json::json;
use std::fs;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api_key = std::env::var("FORMATEX_API_KEY")?;

    let latex = r#"\documentclass{article}
\usepackage{amsmath}
\begin{document}
Hello from Rust! $E = mc^2$
\end{document}"#;

    let client = Client::new();
    let response = client
        .post("https://api.formatex.io/api/v1/compile")
        .header("X-API-Key", &api_key)
        .json(&json!({ "latex": latex, "engine": "pdflatex" }))
        .send()
        .await?;

    if !response.status().is_success() {
        let body = response.text().await?;
        return Err(format!("API error: {body}").into());
    }

    let pdf = response.bytes().await?;
    fs::write("output.pdf", &pdf)?;
    println!("PDF saved to output.pdf");
    Ok(())
}

\section{معالج Axum}

معالج Axum

سجّل AppState مشتركًا في موجّه Axum لديك وركّب compile_handler على أي مسار. يقبل المعالج جسم JSON يحتوي على source وengine الاختياري، ثم يبثّ بايتات PDF مباشرة إلى العميل.

src/handlers/compile.rs
use axum::{
    body::Bytes,
    extract::State,
    http::{HeaderMap, StatusCode},
    response::{IntoResponse, Response},
    Json,
};
use reqwest::Client;
use serde::Deserialize;

#[derive(Clone)]
pub struct AppState {
    pub http:    Client,
    pub api_key: String,
}

#[derive(Deserialize)]
pub struct CompileRequest {
    pub source: String,
    #[serde(default = "default_engine")]
    pub engine: String,
}

fn default_engine() -> String {
    "pdflatex".to_string()
}

pub async fn compile_handler(
    State(state): State<AppState>,
    Json(body):   Json<CompileRequest>,
) -> Result<Response, (StatusCode, String)> {
    let res = state
        .http
        .post("https://api.formatex.io/api/v1/compile")
        .header("X-API-Key", &state.api_key)
        .json(&serde_json::json!({
            "latex": body.source,
            "engine": body.engine,
        }))
        .send()
        .await
        .map_err(|e| (StatusCode::BAD_GATEWAY, e.to_string()))?;

    if !res.status().is_success() {
        let msg = res.text().await.unwrap_or_default();
        return Err((StatusCode::UNPROCESSABLE_ENTITY, msg));
    }

    let pdf: Bytes = res
        .bytes()
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;

    let mut headers = HeaderMap::new();
    headers.insert("Content-Type", "application/pdf".parse().unwrap());
    headers.insert(
        "Content-Disposition",
        "attachment; filename=output.pdf".parse().unwrap(),
    );

    Ok((headers, pdf).into_response())
}

\section{Authentication}

إعداد متغير البيئة

احتفظ بمفتاح API في البيئة، ولا تكتبه أبدًا داخل ملفات المصدر. اقرأه عند بدء التشغيل وأوقف التنفيذ فورًا إذا كان مفقودًا:

// Fail fast at startup let api_key = std::env::var( "FORMATEX_API_KEY" ).expect( "FORMATEX_API_KEY not set" );
  • → أنشئ مفاتيح API من لوحة التحكم بعد التسجيل.
  • → استخدم ملف .env مع الحزمة dotenvy أثناء التطوير المحلي.
  • → يجب أن تتضمن جميع الطلبات X-API-Key: <key> .

\section{Reference}

مرجع API

الحقلالنوعمطلوبالوصف
latexstringنعمكود LaTeX المصدري الكامل كسلسلة نصية
enginestringلاالمحرك المراد استخدامه. الافتراضي: "pdflatex". الخيارات: pdflatex وxelatex وlualatex وlatexmk

POST https://api.formatex.io/api/v1/compile — يعيد بايتات PDF الخام عند النجاح ( 200)، أو خطأ بصيغة JSON عند الفشل (422/ 401).

\section{معالجة الأخطاء}

معالجة الأخطاء

افحص response.status().is_success() قبل قراءة الجسم. حوّل أخطاء HTTP إلى نوع الخطأ الخاص بك لنشرها بشكل نظيف:

  • → 200 — بايتات PDF الخام. اقرأها باستخدام response.bytes().await? .
  • → 422 — خطأ في تجميع LaTeX. يحتوي جسم JSON على حقل error مع مقتطف من سجل TeX.
  • → 401 — مفتاح API غير صالح أو مفقود.

\end{rust}

ابدأ تجميع LaTeX من Rust

احصل على مفتاح API واحصل على ملف PDF من Rust خلال دقائق، دون الحاجة إلى TeX Live.