使用jsPDF创建的pdf中出现重复的内容

10
我正在使用jsPDF将html转换为pdf。在某些情况下,如果html中包含svg图表,则生成的pdf中会出现部分数据重复。
例如,如果图表具有图例,则会出现重复。请参见下面的屏幕截图。城市名称和百分比重复显示。

enter image description here

以下是创建 PDF 的代码。
pdf.addHTML($("#page1"), options, function(){
            pdf.addPage();
            pdf.addHTML($("#page2"), options, function(){
                pdf.addPage();
                pdf.output('dataurlnewwindow');
            });
        });

编辑 1:

这是我目前已经弄清楚的内容。

<div id="outerDiv">
    <div id="pieChart"></div>
</div>

当我执行以下代码时:pdf.addHTML($("#pieChart"),没有问题。
但是,当我执行以下代码时:pdf.addHTML($("#outerDiv"),标签会重复出现。
这是我生成c3js图表的方式。
var pieChart  = c3.generate({
            bindto: '#pieChart',

编辑 2:-

以下是我的全部代码。

<!DOCTYPE html>
<html>

<head>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.10/c3.min.css">
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.10/c3.min.js"></script>
    <script type="text/javascript" src="https://code.jquery.com/jquery-2.2.0.min.js"></script>
    <script type="text/javascript" src="http://gabelerner.github.io/canvg/rgbcolor.js"></script>
    <script type="text/javascript" src="http://gabelerner.github.io/canvg/StackBlur.js"></script>
    <script type="text/javascript" src="http://gabelerner.github.io/canvg/canvg.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.2.61/jspdf.min.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.5.0-alpha1/html2canvas.js"></script>

    <script type='text/javascript'>
    function replaceAllSVGsWithTempCanvas(elemSelector) {
        var svgElements = $(elemSelector).find('svg');

        //replace all svgs with a temp canvas
        svgElements.each(function() {
            var canvas, xml;

            // canvg doesn't cope very well with em font sizes so find the calculated size in pixels and replace it in the element.
            $.each($(this).find('[style*=em]'), function(index, el) {
                $(this).css('font-size', getStyle(el, 'font-size'));
            });

            canvas = document.createElement("canvas");
            canvas.className = "screenShotTempCanvas";

            //convert SVG into a XML string
            xml = (new XMLSerializer()).serializeToString(this);

            // Removing the name space as IE throws an error
            xml = xml.replace(/xmlns=\"http:\/\/www\.w3\.org\/2000\/svg\"/, '');

            //draw the SVG onto a canvas
            canvg(canvas, xml);
            $(canvas).insertAfter(this);

            //hide the SVG element
            $(this).attr('class', 'tempHide');
            $(this).hide();
        });
    }

    jQuery(document).ready(function($) {
        genChart();
    });

    function genPDF() {
        var options = {
            background: '#fff'
        };
        var pdf = new jsPDF('p', 'pt', 'a4');

        replaceAllSVGsWithTempCanvas(".content");

        pdf.addHTML($("#chartOuter"), options, function() {
            pdf.output('dataurlnewwindow');

            $(".content").find('.screenShotTempCanvas').remove();
            $(".content").find('.tempHide').show().removeClass('tempHide');

        });
    }

    function genChart() {
        var chart = c3.generate({
            data: {
                columns: [
                    ['data1', 30],
                    ['data2', 120],
                ],
                type: 'pie'
            }
        });
    }
    </script>
</head>

<body class="content">
    <table width="100%">
        <tr>
            <td width="50%">
                <div id="chartOuter">
                    <div id="chart"></div>
                </div>
            </td>
        </tr>
        <tr>
            <td colspan="2" align="left">
                <input type="button" onclick="genPDF();" value="Generate PDF" />
            </td>
        </tr>
    </table>
</body>

</html>

编辑 3:

我尝试使用 html2canvas 将 html 转换为画布。但它也出现了相同的问题。

编辑 4:

我现在可以解决重复问题。但是生成的 PDF 中的图表和文本有点模糊。基本上,我添加了 replaceAllSVGsWithTempCanvas 函数,然后在写入 PDF 时使用它。但似乎这个函数对 HTML 做了一些使得内容变得模糊的事情。实际上,饼图等不再是圆形,而是呈椭圆形。

使用修改后的 JS 编辑了问题。


请问您能否将您的HTML、JS代码上传到Fiddle上? - Raki
@Raki,我已经编辑了问题并添加了所有的HTML和JS代码。 - ajm
只需在pdf.addHTML(...)之前添加$(".c3 svg").css({"font-size":"0px"});,如我的答案所解释的那样。 ;) - Louys Patrice Bessette
4个回答

3

看起来这是html2canvas中的一个bug。您应该在html2canvas加载后添加下面的代码,以修复它(感谢这个人):

NodeParser.prototype.getChildren = function(parentContainer) {
    return flatten([].filter.call(parentContainer.node.childNodes, renderableNode).map(function(node) {
        var container = [node.nodeType === Node.TEXT_NODE && node.parentElement.tagName !== "text" ? new TextContainer(node, parentContainer) : new NodeContainer(node, parentContainer)].filter(nonIgnoredElement);
        return node.nodeType === Node.ELEMENT_NODE && container.length && node.tagName !== "TEXTAREA" ? (container[0].isElementVisible() ? container.concat(this.getChildren(container[0])) : []) : container;
    }, this));
};

由于html2canvas作为模块加载,因此您应该直接在源代码中找到NodeParser.prototype.getChildren并将其编辑为与上述匹配。这意味着您不能从CDN加载它。

试一下


我应该在哪个文件中进行更改?addhtml.js?还是jspdf.debug.js? - ajm
@anything 嗯,html2canvas.js。 - Gokhan Kurt
它解决了饼图的问题,但是条形图和折线图仍然存在问题。x轴和y轴上的标签会重复出现。有什么想法吗? - ajm
注意到您没有使用最新版本的html2canvas。还有,您没有注意到它在Firefox上不能运行,在IE上也表现不佳,这些问题在发布问题之前就应该注意到。实际上这个库一开始就没有起作用。 - Gokhan Kurt
还有其他选择吗? - ajm
显示剩余2条评论

2

我认为可以通过两个步骤来正确解决问题。
首先, 我注释掉了“addhtml插件”,因为我的控制台出现了这个错误:

Refused to execute script from 'https://raw.githubusercontent.com/MrRio/jsPDF/master/plugins/addhtml.js' because its MIME type ('text/plain') is not executable, and strict MIME type checking is enabled.

其次, 我将PDF源代码从#chartOuter 改为 #chart

    pdf.addHTML($("#chart"), options, function () {
    var string = pdf.output('datauristring');
    $('.preview-pane').attr('src', string)
});

没有更多的重复。

-----
编辑
(由于我一开始误解了问题)

需要使用外部div... 可能还要使用整个页面。

好吧... 老实说,我不知道为什么会出现这个文本问题。
但我注意到,即使指定为“sans-serif”,重复的文本也带有衬线字体。
所以我就专注于这个问题。
我试图改变字体大小... 它影响了重复的文本,但文字并没有遵循css规则。好吧。

然后我尝试只删除pdf创建部分之前的文本... 然后魔术发生了!
;)

$(".c3 svg").css({"font-size":"0px"});

以下是完整的脚本,我没有改动您原来的代码。

jQuery(document).ready(function ($) {
        genChart();
    });

    function genPDF() {
        var options = { background: '#fff'};
        var pdf = new jsPDF('p', 'pt', 'a4');

        $(".c3 svg").css({"font-size":"0px"});    // <-- This does the trick !

        pdf.addHTML($("#chartOuter"), options, function () {
            var string = pdf.output('datauristring');
            $('.preview-pane').attr('src', string)
        });
    }

    function genChart() {
        var chart = c3.generate({
            data: {
                columns: [
                    ['data1', 30],
                    ['data2', 120],
                ],
                type : 'pie'
            }
        });
    }

请查看我在第一次编辑中的注释。当我使用 #chart 时它可以工作,但现在当我使用 #chartOuter 时无法工作。 - ajm
好的...但是你为什么需要使用外部div呢? - Louys Patrice Bessette
看到HTML中还有很多其他的内容需要写入PDF。我可以选择图表div并将其写入PDF,但是所有其他的CSS和HTML都必须手动完成。 - ajm

1
你可以尝试使用ChartJS而不是C3。
我已经修改了你的代码并成功地尝试了它。
<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.10/c3.min.css">

    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.10/c3.min.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.3/Chart.min.js"></script>

    <script type="text/javascript" src="https://code.jquery.com/jquery-2.2.0.min.js"></script>

    <!-- <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.2.61/jspdf.debug.js"></script> -->
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.2.61/jspdf.min.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.5.0-alpha1/html2canvas.js"></script>
    <script type="text/javascript" src="https://raw.githubusercontent.com/MrRio/jsPDF/master/plugins/addhtml.js"></script>

    <script type='text/javascript'>

        jQuery(document).ready(function ($) {
            genChart();
        });

        function genPDF() {
            var options = { background: '#fff'};
            var pdf = new jsPDF('p', 'pt', 'a4');

            pdf.addHTML($("#chartOuter"), options, function () {
                var string = pdf.output('datauristring');
                $('.preview-pane').attr('src', string)
            });
        }

        function genChart () {
          var ctx = $("#chart");
          var options = {};

          var myPieChart = new Chart(ctx, {
              type: 'pie',
              data: {
                    labels: [
                        "data1",
                        "data2"
                    ],
                    datasets: [{
                            data: [30, 120],
                            backgroundColor: [
                                "#FF6384",
                                "#36A2EB"
                            ],
                            hoverBackgroundColor: [
                                "#FF6384",
                                "#36A2EB"
                            ]
                        }]
                },
              options: options
          });
        }
    </script>
</head>
<body>

    <table width="100%">
        <tr>
            <td width="50%">
                <div id="chartOuter">
                    <!-- <div id="chart"></div> -->
                    <canvas id="chart" width="400" height="400"></canvas>
                </div>
            </td>
            <td>
                <iframe height="550px" class="preview-pane" type="application/pdf" width="100%" frameborder="0" style="position:relative;z-index:999"></iframe>
            </td>
        </tr>
        <tr>
            <td colspan="2" align="left">
                <input type="button" onclick="genPDF();" value="Generate PDF"/>
            </td>
        </tr>
    </table>

</body>
</html>

我们现在无法更改图表库,因为我们已经创建了很多图表,而且我们将不得不重新编写所有内容。这在目前阶段是不可行的。 - ajm

0

动态加载的PDF文件模糊,而官方演示的同一文件却非常清晰。我无法确定问题所在?

使用Chrome浏览器。版本号为73.0.3683.86(官方版本)(64位) 以下是我的代码:

  async process(buffer, index) {
      
      // Processing the decrypted data stream
      let uint8Array = new Uint8Array(buffer);
      var word = await this.Uint8ToBase64(uint8Array);
      var decryptedData = CryptoJS.AES.decrypt(word, this.authorKey, {
        mode: CryptoJS.mode.ECB,
        padding: CryptoJS.pad.Pkcs7
      });
      
      // Then turn wordArray to uint8Array
      let getUint8Array = await this.wordArrayToU8(decryptedData);
      
      // decryption ends
      this.loadingPdf(getUint8Array, index);
    },
    async loadingPdf(getUint8Array, index) {
      
      // render canvas
      let pdf = await pdfjsLib.getDocument({ data: getUint8Array, cMapUrl: cMapUrl, cMapPacked: cMapPacked  });
      let page = await pdf.getPage(1).then(page => {
        return page;
      });
      let canvas = document.getElementById("the-canvas" + index);
      const CSS_UNITS = 96.0 / 72.0;
      const DEFAULT_SCALE = 1.7;
      const UNKNOWN_SCALE = 0;
      let viewport = page.getViewport( DEFAULT_SCALE * CSS_UNITS);
      if (canvas.dataset.runed) return;

      canvas.width = viewport.width*CSS_UNITS;
      canvas.height = viewport.height*CSS_UNITS;
      this.canvasW =
        this.canvasW > (1000 / viewport.height) * viewport.width
          ? this.canvasW
          : (1000 / viewport.height) * viewport.width;
      canvas.style.width = (1000 / viewport.height) * viewport.width;
      canvas.dataset.runed = true;
      var context = canvas.getContext('2d');
      
       // [Important] Turn off anti-aliasing
      context.mozImageSmoothingEnabled = false;
      context.webkitImageSmoothingEnabled = false;
      context.msImageSmoothingEnabled = false;
      context.imageSmoothingEnabled = false;
      await page.render({
        //enableWebGL: true,
        // canvasContext: context,
        transform: [CSS_UNITS, 0, 0, CSS_UNITS, 0, 0],
      //  transform: [1, 0, 0, 1, 0, 0],
        canvasContext: canvas.getContext("2d"),
        viewport: viewport
      });
      this.loadedPages.push(index)
    },


请将中文注释转换为英文注释。 - xskxzr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接