این راهنمای نشان می دهد که چگونه به اضافه کردن تصویب ریاضیات LaTeX در زمان واقعی به یک برنامه ASP.NET با استفاده از Aspose.Tex برای .NET. شما یک API وب کوچک ایجاد می کنید که ورودی LaTEX را می پذیرد، آن را به PNG باز می گرداند، بایت های تصویر را با نوع محتوا صحیح بازگرداند و نتایج را بر روی دیسک ذخیره می کند.
چه چیزی بسازید
یک اپلیکیشن ASP.NET Core با:
Endpoint
POST /api/latex/png
که LaTeX را می پذیرد و یک تصویر PNG را باز می گرداندصفحه HTML ساده که در آن کاربران یک معادله را تایپ و یک پیش نمایش زنده را مشاهده می کنند
کچینگ دیسک با یک هش محتوا و DPI
اعتباربخشی ورودی پایه و دایرکتوری های کار Sandboxed
شما می توانید کد را کپی کنید و آن را همانطور که هست اجرا کنید.
پیش شرط
ویندوز، لینوکس یا macOS با .NET 6 یا بالاتر
Visual Studio 2022 یا VS Code* با افزونه C#
بسته NuGet Aspose.TeX
dotnet add package Aspose.TeX
بایگانی برچسب ها: Tex Exposes TeXOptions
, TeXConfig.ObjectLaTeX
, PngSaveOptions
, ImageDevice
, TeXJob
, InputFileSystemDirectory
, و OutputFileSystemDirectory
شما از این ها برای انتقال LaTeX به PNG استفاده خواهید کرد.
پروژه Layout
یک ASP.NET Core Web API را ایجاد کنید، سپس یک سرویس سبک به علاوه یک صفحه HTML حداقل اضافه کنید.
AsposeTexDemo/
Program.cs
Services/
LatexRenderer.cs
wwwroot/
index.html
سرویس: LaTeX به PNG Render
این سرویس LaTeX را به یک موقت می نویسد. .tex
فایل، کار Aspose.TeX را اجرا می کند و بایت های PNG را باز می گرداند.
// 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();
}
}
بایگانی برچسب ها: ASP.NET Core Endpoint
این تعریف یک قرارداد JSON، ثبت رندر، و نشان دادن یک POST
نقطه نهایی که PNG را باز می گرداند.
// 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();
صفحه تست ساده
این را داخل wwwroot/index.html
برای امتحان کردن API در یک مرورگر بدون هیچ چارچوب جلو.
<!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>
پروژه را اجرا کنید
dotnet run
Open http://localhost:5000
یا http://localhost:5173
بسته به پروفایل راه اندازی شما.یک معادله را تایپ کنید و روی Render کلیک کنید.به روز رسانی های پیش نمایش با خروجی PNG از طرف سرور.
تنظیم و راه اندازی یادداشت ها
- ** مجوز Aspose.TeX*اگر یک فایل مجوز دارید، آن را در هنگام راه اندازی تنظیم کنید تا محدودیت های ارزیابی را حذف کنید.
// in Program.cs, before first use
// new Aspose.TeX.License().SetLicense("Aspose.Total.lic");
- موقعیت ذخیره سازی *رندر فایل های کشی را در زیر می نویسد
tex-cache/
در ریشه محتوا.در لینوکس یا فرآیندهای حاوی شما می توانید این مسیر را بر روی یک حجم پایدار نصب کنید.
- موقعیت ذخیره سازی *رندر فایل های کشی را در زیر می نویسد
**محدودیت اندازه درخواست*نمونه کپس درخواست اندازه 256 کیلوگرم را افزایش می دهد اگر از ورودی های بزرگتر پشتیبانی می کنید.
** دسترسی از طریق منبع*اگر API را از منشأ متفاوتی از سایت ارائه می دهید، به ترتیب CORS را فعال کنید.
لیست چک امنیتی
- این سرویس دستورالعمل های خطرناک را رد می کند.
\input
,\include
, و\write18
.قسمت اجازه را محکم نگه دارید و پیشگام را درBuildStandaloneDocument
. - محدود کردن طول ورودی برای مسدود کردن بار پرداخت پاتولوژیکی.
- وارد یک دایرکتوری کار منحصر به فرد در هر درخواست و پس از موفقیت آن را حذف کنید. نمونه تنها PNG پنهان را حفظ می کند.
- در نظر بگیرید محدودیت نرخ در سطح پروکسی یا API دروازه برای سایت های عمومی.
تیک های عملکرد
- استفاده از کایچ دیسک برای جلوگیری از تکرار معادلات یکسان. نمونه هاش LaTeX plus DPI را برای deduplicate نتایج.
- DPI را بین 150 تا 300 برای اکثر نیازهای UI حفظ کنید. DPI بالاتر باعث افزایش زمان و اندازه تصویر می شود.
- اپلیکیشن را با ارائه یک فرمول رایج در استارتاپ گرم کنید اگر می خواهید اولین درخواست کاربر بلافاصله باشد.
- اگر نیاز به خروجی وکتور برای محتوای zoomable دارید، به
SvgSaveOptions
وSvgDevice
پس از آن، SVG را بپوشانید.بقیه خط لوله یکسان است.
Troubleshooting
- نمایش خفیف یا خطاسوابق سرور را بررسی کنید.اگر LaTeX از بسته ها خارج از پیش فرض ثابت استفاده می کند، آنها را حذف کنید .
\usepackage
واردات کاربر از طریق طراحی - **کلاهبرداری و یا مارجین های بزرگ*در این
standalone
کلاس اسناد به طور معمول مارجین ها را به شدت ارسال می کند.اگر هنوز فضای اضافی را مشاهده می کنید، با\[
و\]
برای نمایش ریاضیات یا حذف آنها برای اندازه inline. - تصویب متنافزایش
PngSaveOptions.Resolution
در 200 تا 300 DPI بیشتر موارد UI به نظر می رسد crisp.
API ارجاع سریع در کد استفاده می شود
TeXOptions.ConsoleAppOptions(TeXConfig.ObjectLaTeX)
: گزینه ها را برای موتور Object LaTeX ایجاد می کندPngSaveOptions
: کنترل خروجی PNG باResolution
وDeviceWritesImages
ImageDevice
: بوفر PNG در حافظه زمانی کهDeviceWritesImages = false
TeXJob(texPath, device, options).Run()
: مجموعه ای از.tex
فایلهای داخل دستگاهInputFileSystemDirectory
وOutputFileSystemDirectory
: تعریف دایرکتوری های کار برای ورودی و خروجی
با استفاده از این بلوک های ساخت و ساز، اپلیکیشن ASP.NET شما می تواند LaTeX را در تقاضا، نتایج ذخیره سازی و معادلات PNG crisp به طور قابل اعتماد ارائه دهد.