\usepackage{rust}

API Rust pour convertir LaTeX en PDF

Compilez du LaTeX en PDF depuis Rust avec l'API REST FormaTeX. reqwest asynchrone + tokio : aucune installation de TeX Live, aucun binaire à embarquer.

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

\section{Pourquoi Rust}

Pourquoi Rust + FormaTeX

Asynchrone sans surcoût

Le client asynchrone de reqwest s'intègre naturellement au runtime de Tokio. Compilez plusieurs documents en parallèle sans complexité supplémentaire.

Images Docker légères

Votre binaire Rust reste petit : pas de TeX Live, pas d'apt install texlive-full. FormaTeX gère entièrement l'environnement de compilation.

Gestion des erreurs typée

Le type Result de Rust s'associe bien à l'API FormaTeX. Convertissez les erreurs HTTP en erreurs métier au niveau du typage, sans mauvaise surprise à l'exécution.

\section{Dependencies}

Cargo.toml dependencies

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{Démarrage rapide}

Démarrage rapide avec reqwest

L'exemple ci-dessous compile un document LaTeX minimal avec reqwest et écrit le PDF obtenu sur le disque. Lisez votre clé API depuis l'environnement avec 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{Gestionnaire Axum}

Gestionnaire Axum

Enregistrez un AppState partagé auprès de votre routeur Axum et montez compile_handler sur la route de votre choix. Le gestionnaire accepte un corps JSON avec source et un engine facultatif, puis renvoie les octets du PDF directement au client.

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}

Configuration de la variable d'environnement

Stockez votre clé API dans l'environnement, sans jamais l'écrire en dur dans les fichiers source. Lisez-la au démarrage et échouez immédiatement si elle est absente :

// Fail fast at startup let api_key = std::env::var( "FORMATEX_API_KEY" ).expect( "FORMATEX_API_KEY not set" );
  • → Générez des clés API depuis votre tableau de bord après votre inscription.
  • → Utilisez un fichier .env avec la crate dotenvy pendant le développement local.
  • → Toutes les requêtes doivent inclure X-API-Key: <key> .

\section{Reference}

Référence de l'API

ChampTypeRequisDescription
latexstringOuiCode source LaTeX complet sous forme de chaîne
enginestringNonMoteur à utiliser. Par défaut : "pdflatex". Options : pdflatex, xelatex, lualatex, latexmk

POST https://api.formatex.io/api/v1/compile : renvoie les octets bruts du PDF en cas de succès ( 200), ou une erreur JSON en cas d'échec (422/ 401).

\section{Gestion des erreurs}

Gestion des erreurs

Vérifiez response.status().is_success() avant de lire le corps. Convertissez les erreurs HTTP en votre propre type d'erreur pour une propagation propre :

  • → 200 : octets bruts du PDF. À lire avec response.bytes().await? .
  • → 422 : erreur de compilation LaTeX. Le corps JSON contient un champ error avec un extrait du journal TeX.
  • → 401 : clé API invalide ou manquante.

\end{rust}

Commencez à compiler du LaTeX depuis Rust

Obtenez votre clé API et générez des PDF depuis Rust en quelques minutes, sans TeX Live.