Dieser Leitfaden zeigt, wie man real-time LaTeX Math Rendering zu einer ASP.NET-App mit Aspose.Tex für .NET hinzufügen kann. Sie bauen eine kleine Web-API, die die Latex-Einträge akzeptiert, sie an PNG rennt, das Bildbytes mit dem richtigen Inhaltstyp zurückgibt und die Ergebnisse auf dem Disk cachet. Der Walkthrough umfasst genaue NuGet-Pakete, minimale Arbeitscodes, Sicherheitsbeauftragte und Performance-Tips.

Was Sie bauen werden

  • Eine ASP.NET Core App mit:

  • Endpoint POST /api/latex/png die LaTeX akzeptiert und eine PNG-Bild zurückgibt

  • Einfache HTML-Seite, in der Benutzer eine Gleichung eingeben und einen Live-Vorblick sehen

  • Disk-Caching mit einem Content-Hash und DPI

  • Basic Input Validation und Sandboxed Work Directory

Sie können den Code kopieren und es so ausführen, wie es ist.

Voraussetzung

  • Windows, Linux oder macOS mit .NET 6 oder höher

  • Visual Studio 2022 oder VS Code* mit C#-Erweiterung

  • NuGet Paket Aspose.TeX

dotnet add package Aspose.TeX

Aspose.TeX Ausstellungen TeXOptions, TeXConfig.ObjectLaTeX, PngSaveOptions, ImageDevice, TeXJob, InputFileSystemDirectory, und OutputFileSystemDirectorySie werden diese verwenden, um LaTeX zu PNG zu übertragen.

Projektlayout

Erstellen Sie ein ASP.NET Core Web API Projekt, fügen Sie dann einen leichten Service plus eine minimale HTML-Seite hinzu.

AsposeTexDemo/
  Program.cs
  Services/
    LatexRenderer.cs
  wwwroot/
    index.html

Service: LaTeX zu PNG Render

Dieser Dienst schreibt den LaTeX zu einem vorübergehenden .tex Datei, läuft die Aspose.TeX Arbeit, und kehrt die PNG-Bytes zurück. Es implementiert auch eine einfache Cache auf dem Festplatte.

// File: Services/LatexRenderer.cs
using System.Security.Cryptography;
using System.Text;
using Aspose.TeX;

namespace AsposeTexDemo.Services;

public sealed class LatexRenderer
{
    private readonly string _cacheRoot;
    private readonly ILogger<LatexRenderer> _log;

    public LatexRenderer(IWebHostEnvironment env, ILogger<LatexRenderer> log)
    {
        _cacheRoot = Path.Combine(env.ContentRootPath, "tex-cache");
        Directory.CreateDirectory(_cacheRoot);
        _log = log;
    }

    // Public entry point. Renders LaTeX to PNG and returns bytes.
    public async Task<byte[]> RenderPngAsync(string latexBody, int dpi = 200, CancellationToken ct = default)
    {
        // Validate and normalize input
        var normalized = NormalizeLatex(latexBody);
        ValidateLatex(normalized);

        // Cache key depends on content and dpi
        var key = Hash($"{normalized}\n{dpi}");
        var cacheDir = Path.Combine(_cacheRoot, key);
        var cachePng = Path.Combine(cacheDir, "out.png");

        if (File.Exists(cachePng))
        {
            _log.LogDebug("Cache hit: {Key}", key);
            return await File.ReadAllBytesAsync(cachePng, ct);
        }

        Directory.CreateDirectory(cacheDir);

        // Prepare a minimal document that wraps the math
        var texDoc = BuildStandaloneDocument(normalized);

        // Write the .tex source into an isolated working folder
        var workDir = Path.Combine(cacheDir, "work");
        Directory.CreateDirectory(workDir);
        var texPath = Path.Combine(workDir, "doc.tex");
        await File.WriteAllTextAsync(texPath, texDoc, Encoding.UTF8, ct);

        // Configure Aspose.TeX conversion options
        var options = TeXOptions.ConsoleAppOptions(TeXConfig.ObjectLaTeX);
        options.InputWorkingDirectory  = new InputFileSystemDirectory(workDir);
        options.OutputWorkingDirectory = new OutputFileSystemDirectory(workDir);

        var png = new PngSaveOptions
        {
            // If you want higher fidelity on HiDPI displays, raise this number
            Resolution = dpi,

            // When false, the ImageDevice buffers PNG bytes in memory so you can capture them without file I/O
            // You can also leave the default (true) and read the file from disk. Both modes are shown below.
            DeviceWritesImages = false
        };
        options.SaveOptions = png;

        // Run the job; capture PNG bytes from the device
        var device = new ImageDevice();
        new TeXJob(texPath, device, options).Run();

        if (device.Result == null || device.Result.Length == 0)
            throw new InvalidOperationException("No PNG output generated by TeX engine.");

        var pngBytes = device.Result[0];

        // Persist into cache for the next request
        await File.WriteAllBytesAsync(cachePng, pngBytes, ct);

        // Clean up working files except cachePng if you want to keep cache slim
        TryDeleteDirectory(workDir);

        return pngBytes;
    }

    private static void TryDeleteDirectory(string dir)
    {
        try { if (Directory.Exists(dir)) Directory.Delete(dir, true); }
        catch { /* swallow to avoid noisy logs in high traffic */ }
    }

    // Minimal, safe preamble for math using Object LaTeX
    private static string BuildStandaloneDocument(string latexBody)
    {
        // With standalone class, the output image is tightly cropped around content
        return
$@"\documentclass{{standalone}}
\usepackage{{amsmath}}
\usepackage{{amssymb}}
\begin{{document}}
{latexBody}
\end{{document}}";
    }

    // Allow plain math snippets like x^2 + y^2 = z^2 and also wrapped forms like \[ ... \]
    private static string NormalizeLatex(string input)
    {
        input = input.Trim();

        // If user did not wrap math, wrap in display math to get proper spacing
        if (!(input.StartsWith(@"\[") && input.EndsWith(@"\]"))
            && !(input.StartsWith(@"$$") && input.EndsWith(@"$$")))
        {
            return $"\\[{input}\\]";
        }
        return input;
    }

    // Very conservative validation to avoid file inclusion or shell escapes
    private static void ValidateLatex(string input)
    {
        // Disallow commands that can touch the filesystem or process environment
        string[] blocked = {
            @"\write18", @"\input", @"\include", @"\openout", @"\write", @"\read",
            @"\usepackage", // preamble is fixed in BuildStandaloneDocument; avoid arbitrary packages
            @"\loop", @"\csname", @"\newread", @"\newwrite"
        };

        foreach (var b in blocked)
        {
            if (input.Contains(b, StringComparison.OrdinalIgnoreCase))
                throw new ArgumentException($"The LaTeX contains a forbidden command: {b}");
        }

        if (input.Length > 4000)
            throw new ArgumentException("Equation too long. Please keep input under 4000 characters.");
    }

    private static string Hash(string s)
    {
        using var sha = SHA256.Create();
        var bytes = sha.ComputeHash(Encoding.UTF8.GetBytes(s));
        return Convert.ToHexString(bytes).ToLowerInvariant();
    }
}

Web API: Minimale ASP.NET Core Endpoint

Dies definiert einen JSON-Vertrag, registriert den Renderer und zeigt eine POST Endpunkte, die PNG zurückgeben.

// File: Program.cs
using System.Text.Json.Serialization;
using AsposeTexDemo.Services;

var builder = WebApplication.CreateBuilder(args);

// Optional: serve a simple static page for testing
builder.Services.AddDirectoryBrowser();

// Add the renderer
builder.Services.AddSingleton<LatexRenderer>();

// Configure Kestrel limits for small payloads
builder.WebHost.ConfigureKestrel(opt =>
{
    opt.Limits.MaxRequestBodySize = 256 * 1024; // 256 KB per request is plenty for math
});

var app = builder.Build();

// Serve wwwroot for quick manual testing
app.UseDefaultFiles();
app.UseStaticFiles();

// DTOs
public record LatexRequest(
    [property: JsonPropertyName("latex")] string Latex,
    [property: JsonPropertyName("dpi")]   int? Dpi
);

app.MapPost("/api/latex/png", async (LatexRequest req, LatexRenderer renderer, HttpContext ctx, CancellationToken ct) =>
{
    if (string.IsNullOrWhiteSpace(req.Latex))
        return Results.BadRequest(new { error = "Missing 'latex'." });

    int dpi = req.Dpi is > 0 and <= 600 ? req.Dpi.Value : 200;

    try
    {
        var bytes = await renderer.RenderPngAsync(req.Latex, dpi, ct);
        ctx.Response.Headers.CacheControl = "public, max-age=31536000, immutable";
        return Results.File(bytes, "image/png");
    }
    catch (ArgumentException ex)
    {
        return Results.BadRequest(new { error = ex.Message });
    }
    catch (Exception ex)
    {
        // Hide engine details from clients; log the exception server-side if needed
        return Results.StatusCode(500);
    }
});

// Health check
app.MapGet("/health", () => Results.Ok(new { ok = true }));

app.Run();

Einfache Testseite

Gießen Sie dies in wwwroot/index.html Versuchen Sie die API in einem Browser ohne Front-End-Framework.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <title>Aspose.TeX LaTeX Demo</title>
  <meta name="viewport" content="width=device-width,initial-scale=1" />
  <style>
    body{font-family:system-ui,Segoe UI,Roboto,Arial,sans-serif;margin:2rem;line-height:1.4}
    textarea{width:100%;height:8rem}
    .row{display:flex;gap:1rem;align-items:flex-start;margin-top:1rem;flex-wrap:wrap}
    .card{border:1px solid #ddd;border-radius:8px;padding:1rem;flex:1 1 320px}
    img{max-width:100%;height:auto;border:1px solid #eee;border-radius:4px;background:#fff}
    label{font-weight:600}
    input[type=number]{width:6rem}
    .muted{color:#666;font-size:.9rem}
    .error{color:#b00020}
  </style>
</head>
<body>
  <h1>Real-time LaTeX to PNG with Aspose.TeX</h1>
  <p class="muted">Type LaTeX math and click Render. The server returns a PNG image rendered by Aspose.TeX.</p>

  <div class="card">
    <label for="latex">LaTeX</label><br />
    <textarea id="latex">x^2 + y^2 = z^2</textarea><br />
    <label for="dpi">DPI</label>
    <input id="dpi" type="number" min="72" max="600" value="200" />
    <button id="btn">Render</button>
    <div id="msg" class="error"></div>
  </div>

  <div class="row">
    <div class="card">
      <h3>Preview</h3>
      <img id="preview" alt="Rendered equation will appear here" />
    </div>
    <div class="card">
      <h3>cURL</h3>
      <pre id="curl" class="muted"></pre>
    </div>
  </div>

  <script>
    const btn = document.getElementById('btn');
    const latex = document.getElementById('latex');
    const dpi = document.getElementById('dpi');
    const img = document.getElementById('preview');
    const msg = document.getElementById('msg');
    const curl = document.getElementById('curl');

    function updateCurl() {
      const payload = JSON.stringify({ latex: latex.value, dpi: Number(dpi.value) }, null, 0);
      curl.textContent =
`curl -s -X POST http://localhost:5000/api/latex/png \
  -H "Content-Type: application/json" \
  -d '${payload}' --output out.png`;
    }

    updateCurl();

    [latex, dpi].forEach(el => el.addEventListener('input', updateCurl));

    btn.addEventListener('click', async () => {
      msg.textContent = '';
      img.src = '';
      try {
        const payload = { latex: latex.value, dpi: Number(dpi.value) };
        const res = await fetch('/api/latex/png', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(payload)
        });
        if (!res.ok) {
          const err = await res.json().catch(() => ({}));
          msg.textContent = err.error || `Error ${res.status}`;
          return;
        }
        const blob = await res.blob();
        img.src = URL.createObjectURL(blob);
      } catch (e) {
        msg.textContent = 'Request failed.';
      }
    });
  </script>
</body>
</html>

Führen Sie das Projekt

dotnet run

Open http://localhost:5000 oder http://localhost:5173 Abhängig von Ihrem Startprofil. Tippen Sie eine Gleichung und klicken Sie auf Render. Die Preview-Updates mit PNG-Ausgang auf der Serverseite.

Konfiguration und Entfernung Notizen

  • **Aspose.TeX Lizenz*Wenn Sie eine Lizenzdatei haben, setzen Sie es während des Startups fest, um die Bewertungsgrenze zu entfernen.
// in Program.cs, before first use
// new Aspose.TeX.License().SetLicense("Aspose.Total.lic");
  • Cache StandortDer Renderer schreibt cache-Dateien unter tex-cache/ auf Linux oder containerized deployments können Sie diesen Pfad auf einem beständigen Volumen montieren.

  • **Request Größe Grenzen*Die Beispielkarte fordert die Größe von 256 KB. Erhöhen Sie, wenn Sie größere Einträge unterstützen.

    • Cross-Origin Zugang*Wenn Sie die API von einer anderen Herkunft als die Website dienen, aktivieren Sie CORS entsprechend.

Sicherheitscheckliste

  • Der Service verweigert potenziell gefährliche Befehle wie \input, \include, und \write18Halten Sie die Zuschlagliste fest und halten Sie den Preamble fest in BuildStandaloneDocument.
  • Einschränken Sie die Eintrittslänge, um pathologische Zahlungen zu blockieren.
  • In einem einzigartigen Arbeitsdatenario auf Anfrage gelangen und das Direktorium nach Erfolg löschen. Die Probe behält nur das cached PNG.
  • Betrachten Sie die Rate-Beschränkung auf der reversen Proxy- oder API-Gateway-Ebene für öffentliche Websites.

Performance Tipp

  • Verwenden Sie Disk Cache, um die gleichen Gleichungen zu recomputieren zu vermeiden. Die Probe Hashes LaTeX plus DPI um Ergebnisse zu deduplieren.
  • Halten Sie DPI zwischen 150 und 300 für die meisten UI-Anforderungen. höhere DPI erhöht die Zeit und Bildgröße.
  • Wärmen Sie die App, indem Sie eine gemeinsame Formel bei Startups erstellen, wenn Sie möchten, dass die erste Benutzeranfrage sofort ist.
  • Wenn Sie ein Vektor-Ausgang für zoombares Inhalt benötigen, wechseln Sie zum SvgSaveOptions und SvgDevice, dann die SVG einfügen. Der Rest der Pipeline ist das gleiche.

Troubleshooting

  • **Blank Ausgang oder Fehler*Überprüfen Sie die Server-Logs. Wenn die LaTeX Pakete außerhalb der Festpreambel verwendet, entfernen Sie sie. \usepackage Nutzerinträge durch Design.
  • Klipping oder große MargenThe standalone Dokumentklasse tritt in der Regel die Margen fest an. Wenn Sie noch zusätzliche Platz sehen, werfen Sie mit \[ und \] für die Mathe anzeigen oder entfernen sie für inline Größe.
    • gehaltener Text*Increase PngSaveOptions.ResolutionBei 200 bis 300 DPI sehen die meisten UI-Fälle crisp aus.

API-Schnellreferenz, die in Code verwendet wird

  • TeXOptions.ConsoleAppOptions(TeXConfig.ObjectLaTeX): Erstellt Optionen für den Object LaTeX-Motor
  • PngSaveOptions: kontrolliert PNG-Ausgabe mit Resolution und DeviceWritesImages
  • ImageDevice: Buffers PNG Ergebnisse in der Erinnerung, wenn DeviceWritesImages = false
  • TeXJob(texPath, device, options).Run()Kompilieren Sie die .tex Datei in das Gerät
  • InputFileSystemDirectory und OutputFileSystemDirectory: definieren Sie die Arbeitsdirektionen für Eingang und Ausgang

Mit diesen Gebäudeblocken kann Ihre ASP.NET-App LaTeX auf Nachfrage, Cache-Ergebnisse und Crisp PNG-Equationen zuverlässig anbieten.

More in this category