c# - 使用 ITextSharp 将谷歌可视化区域图 svg 添加到 pdf

标签 c# javascript svg google-visualization itext

我正在创建一个适合打印的 pdf 版本的网页,并且在尝试将 Google 面积图添加到 pdf 时遇到了一些困难。

面积图是使用 JavaScript 生成的,并在页面上呈现为 svg,我尝试了多种不同的方法来添加图表,包括导入包含图表的 div 的 html(到目前为止,我只使用这种方法是图表的标题,而不是实际的图表),我已经考虑将 svg 转换为图像格式(png、jpeg、gif 等),然后保存图像并添加到 pdf 作为图像。

有人对我如何实现这个有任何想法吗?

如有任何建议,我们将不胜感激。

最佳答案

使用 jQuery 和 https://github.com/vvvv/SVG我假设您正在使用 MVC

Javascript:

$("#makePdf").click(function (e) {
    $.ajax({
        type: 'POST',
        url: '/GeneratePdf',
        dataType: 'json',
        data: JSON.stringify({ svgXml: $("<div />").append($("#chart svg").clone()).html() }),
        contentType: 'application/json;charset=utf-8',
        success: function (data) {
            window.open('/GetPdf?pdfId=' + data.pdfId, '_blank');
        }
    });
    e.preventDefault();
});

$("<div />").append($("#chart svg").clone()).html()之所以需要,是因为无法获取当前元素的 xml 源,这是我能找到的最优雅的方法,将其附加到新的 div 中。并获取其内容的 html。

C#:

using System;
using System.IO;
using System.Drawing.Imaging;
using iTextSharp.text;
using iTextSharp.text.pdf;
using Svg;

// ...

public static MemoryStream PngFromSvg(string svgXml)
{
    MemoryStream pngStream = new MemoryStream();
    if (!string.IsNullOrWhiteSpace(svgXml))
    {
        byte[] byteArray = Encoding.ASCII.GetBytes(svgXml);
        using (MemoryStream stream = new MemoryStream(byteArray))
        {
            SvgDocument svgDocument = SvgDocument.Open(stream);
            System.Drawing.Bitmap bitmap = svgDocument.Draw();
            bitmap.Save(pngStream, System.Drawing.Imaging.ImageFormat.Png);
        }
    }
}

public static MemoryStream GeneratePdf(MemoryStream pngStream)
{
    MemoryStream pdfStream = new MemoryStream();

    using (Document document = new Document(PageSize.A4, 20, 20, 20, 20))
    {
        PdfWriter pdfWriter = PdfWriter.GetInstance(document, pdfStream);

        // ...
        Image chartImage = Image.GetInstance(System.Drawing.Image.FromStream(pngStream), ImageFormat.Png);
        PdfPCell chartCell = new PdfPCell(chartImage, true);
        chartCell.Border = Rectangle.NO_BORDER;
        //...

        document.Close();
        pdfWriter.Close();
    }

    return pdfStream;
}

public class SvgData
{
    public string svgXml { get; set; }
}
public class PdfData
{
    public string pdfId { get; set; }
}

public JsonResult GeneratePdf(SvgData data)
{
    byte[] pdfBytes = GeneratePdf(PngFromSvg(data.svgXml)).ToArray();

    // ...
    // Either:
    //  :: Store PDF in database as blob and return unique ID
    //  :: Store as file and return path to file

    return Json(new PdfData{pdfId = pdf.id}, JsonRequestBehavior.AllowGet);
}

public ActionResult GetPdf(Int64 pdfId)
{   
    // retrieve pdf from database
    byte[] pdfBytes = pdf.raw;

    return File(pdfBytes, "application/pdf");
}

这段代码很糟糕,但应该是不言自明的。在过去的两周里,这让我很头疼,所以我知道您在试图找到“有效”的解决方案时所经历的痛苦。在我看来,发送 svg 数据进行解析是很糟糕的!但除了模拟网络浏览器的渲染引擎之外,我想不出其他方法!

注意:当在 style 属性中设置样式时,字体可能会正确呈现,您需要解析这些字体并使用 font- 设置字体。属性例如

$chartSvg.find("text").each(function () {
    var $this = $(this);
    $this.attr("font-size", $this.css("font-size"));
    $this.css("font-size", "");

    $this.attr("font-style", $this.css("font-style"));
    $this.css("font-style", "");

    $this.attr("font-variant", $this.css("font-variant"));
    $this.css("font-variant", "");

    $this.attr("font-weight", $this.css("font-weight"));
    $this.css("font-weight", "");

    $this.attr("font-family", $this.css("font-family"));
    $this.css("font-family", "");
});

png 的质量可能很差,所以我所做的是将 svg 缩放到两倍大小,然后将其发送到服务器:

var $chartSvg = $("#chart").find("svg");
var $chartSvgClone = $chartSvg.clone().attr("height", $chartSvg.height() * 2).attr("width", $chartSvg.width() * 2).css("transform", "scale(2)");
var $svgXml = $("<div />").append($chartSvgClone).html();
// ...

另一个警告,svg 库似乎没有正确设置字体系列,因此在您的 C# 代码中传递您的 SvgDocument:

public void SetFonts(SvgElement parent)
{
    try
    {
        SvgText svgText = (SvgText)parent;
        svgText.Font = new System.Drawing.Font("Arial", svgText.FontSize.Value);
    }
    catch
    {
    }

    if(parent.HasChildren())
    {
        foreach(SvgElement child in parent.Children)
        {
            SetFonts(child);
        }
    }
}

关于c# - 使用 ITextSharp 将谷歌可视化区域图 svg 添加到 pdf,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25507598/

相关文章:

javascript - jQuery - 如何使水平拆分器显示在中间,即使屏幕已重新调整大小时

javascript - jQuery 获取 HTML 内容 INSIDE div

html - 你能为 svg "background-image"制作动画吗?

css - SVG 绘图不适用于 chrome

svg - 我如何将一个 SVG 置于另一个 SVG 中

c# - 如何模拟带有 out 参数的方法?

c# - DTO 到 TypeScript 生成器

c# - DateTime.ToString 问题

c# - 动态添加新的身份验证方案

javascript - 如何在拖放 d3 v4 中获取目标元素?