You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin中基于Razor模板自动将HTML转PDF的替代方案求助

Auto-convert WebView HTML to PDF (No Manual Printing) on Android

Got it, let's figure out how to automate converting your WebView-loaded HTML to a PDF without relying on manual print dialogs. Your current code uses the system print flow, but we can tweak that to write directly to a file instead. Here are two solid approaches tailored for your Xamarin.Android setup:


Approach 1: Customize PrintDocumentAdapter to Write to PDF File

Instead of passing the PrintDocumentAdapter to the system PrintManager (which triggers the print dialog), we can wrap it to intercept the output stream and save it directly to a PDF file. This leverages the system's built-in HTML-to-PDF rendering and pagination logic, making it ideal for multi-page content.

Step-by-Step Implementation:

  1. Create a wrapper class for PrintDocumentAdapter that redirects the output to your target file:
public class FilePrintAdapter : PrintDocumentAdapter
{
    private readonly PrintDocumentAdapter _originalAdapter;
    private readonly string _outputFilePath;

    public FilePrintAdapter(PrintDocumentAdapter originalAdapter, string outputFilePath)
    {
        _originalAdapter = originalAdapter;
        _outputFilePath = outputFilePath;
    }

    public override void OnLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, CancellationSignal cancellationSignal, LayoutResultCallback callback, Bundle extras)
    {
        _originalAdapter.OnLayout(oldAttributes, newAttributes, cancellationSignal, callback, extras);
    }

    public override void OnWrite(PageRange[] pages, ParcelFileDescriptor destination, CancellationSignal cancellationSignal, WriteResultCallback callback)
    {
        // Write the print output directly to our target PDF file
        using var targetStream = new FileStream(_outputFilePath, FileMode.Create);
        using var sourceStream = new FileStream(destination.FileDescriptor, FileAccess.ReadWrite);
        
        // Copy the rendered content to our file
        sourceStream.CopyTo(targetStream);
        
        // Notify the system we're done writing
        callback.OnWriteFinished(pages);
    }
}
  1. Modify your existing code to use this wrapper, and ensure you wait for the WebView to fully load:
// Load your HTML into the WebView as before
Control.LoadDataWithBaseURL("file:///android_asset/", new razorModel { Model = Model.MyModel.Instance }.GenerateString(), "text/html", "UTF-8", null);

// Wait for the WebView to finish rendering content (critical!)
Control.SetWebViewClient(new WebViewClient
{
    public override void OnPageFinished(WebView view, string url)
    {
        base.OnPageFinished(view, url);
        
        // Define your output path (use app-specific storage for Android 10+ to avoid permission issues)
        string fileName = $"MyPrint_{Guid.NewGuid()}.pdf";
        string outputPath = Path.Combine(Application.Context.GetExternalFilesDir(null).AbsolutePath, fileName);

        // Set up print attributes (adjust size, resolution, margins as needed)
        var printAttributes = new PrintAttributes.Builder()
            .SetMediaSize(PrintAttributes.MediaSize.IsoA4)
            .SetResolution(new PrintAttributes.Resolution("pdf_res", Android.Print.PrintAttributes.ResolutionQualityHigh, 600, 600))
            .SetMinMargins(PrintAttributes.Margins.NoMargins)
            .Build();

        // Create the custom adapter and trigger the conversion
        var originalAdapter = view.CreatePrintDocumentAdapter(fileName);
        var fileAdapter = new FilePrintAdapter(originalAdapter, outputPath);
        
        // Trigger layout and write flow
        fileAdapter.OnLayout(null, printAttributes, null, new LayoutCallback(fileAdapter), null);
    }
});

// Helper callback to start writing after layout completes
private class LayoutCallback : PrintDocumentAdapter.LayoutResultCallback
{
    private readonly FilePrintAdapter _adapter;

    public LayoutCallback(FilePrintAdapter adapter) => _adapter = adapter;

    public override void OnLayoutFinished(PrintDocumentInfo info, bool changed)
    {
        base.OnLayoutFinished(info, changed);
        
        // Use a dummy descriptor to trigger the write process
        using var dummyDescriptor = ParcelFileDescriptor.Open(new File(Path.GetTempFileName()), ParcelFileMode.ReadWrite);
        _adapter.OnWrite(new[] { PageRange.AllPages }, dummyDescriptor, null, new WriteCallback());
    }
}

// Helper callback to handle post-write actions
private class WriteCallback : PrintDocumentAdapter.WriteResultCallback
{
    public override void OnWriteFinished(PageRange[] pages)
    {
        base.OnWriteFinished(pages);
        // Add your post-processing logic here (e.g., notify user, upload file)
        Console.WriteLine("PDF saved successfully!");
    }
}

Key Notes:

  • Wait for WebView load: Always trigger PDF generation in OnPageFinished to ensure all HTML, CSS, and images are fully rendered.
  • Storage permissions: For Android 10+, use app-specific storage (as shown) to avoid needing WRITE_EXTERNAL_STORAGE permission.

Approach 2: Manual Rendering with PdfDocument

If you need fine-grained control over the PDF output, you can use Android's PdfDocument to draw the WebView content directly onto PDF pages. This works best for single-page content, as multi-page pagination requires custom logic.

Example Code:

Control.SetWebViewClient(new WebViewClient
{
    public override void OnPageFinished(WebView view, string url)
    {
        base.OnPageFinished(view, url);
        
        // Initialize PdfDocument with A4 page size (595x842 points)
        var document = new PdfDocument();
        var pageInfo = new PdfDocument.PageInfo.Builder(595, 842, 1).Create();
        var page = document.StartPage(pageInfo);
        
        // Draw the WebView content onto the PDF page
        var canvas = page.Canvas;
        float scaleFactor = Math.Min(595f / view.Width, 842f / view.Height);
        canvas.Scale(scaleFactor, scaleFactor);
        view.Draw(canvas);
        
        // Finish the page and save the document
        document.FinishPage(page);
        string outputPath = Path.Combine(Application.Context.GetExternalFilesDir(null).AbsolutePath, $"MyPrint_{Guid.NewGuid()}.pdf");
        
        using var stream = new FileStream(outputPath, FileMode.Create);
        document.WriteTo(stream);
        document.Close();
        
        Console.WriteLine("PDF saved via manual rendering!");
    }
});

Caveat:

  • For long documents, you'll need to calculate content height, split it into multiple pages, and adjust the canvas position for each page—this can get complex compared to the first approach.

Both methods eliminate manual print dialogs and automate the PDF generation process. The first approach is recommended for most cases, as it handles pagination and rendering reliably using the system's built-in tools.

内容的提问来源于stack exchange,提问作者micael cunha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:01:06