jspdf 使用插件autoTable添加特殊字符和排版问题

JS页面,需要通过jspdf配合autoTable生成和网页table格式一样的pdf(不能用渲染图片的方式,因为客户指明需要pdf可以复制表格内容)。
点击上面的按钮,通过jspdf生成文件后发现两个问题:

  1. 黑色点无法正确加载
  2. 表格内容总是超出边框

尝试了很多方法但总是没有方案,求一个专业指导。字体指定helvetica

img

案例代码如下(直接保存成html文件即可运行):


```html
<html>
<header>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.5.29/jspdf.plugin.autotable.min.js"></script>


    <script> function pdf_new() {
            // 创建 jsPDF 对象
            var doc = new jspdf.jsPDF();
            doc.addFont('Helvetica.ttf', 'CustomFont', 'normal');
            doc.setFont('CustomFont');

            // 从 HTML 表格中获取数据
            var htmlTable = document.getElementById('Linear');

            //used for lines
            var didDrawCell = function (data) {
                let last_column_index = Object.keys(data.row.cells)[Object.keys(data.row.cells).length - 1]
                if (data.row.index === 0) {
                    doc.setLineWidth(0.3);
                    doc.setDrawColor('black');
                    doc.line(data.cell.x, data.cell.y, data.cell.x + data.cell.width, data.cell.y)
                    console.log('length', Object.keys(data.row.cells).length, data.column.index)
                    console.log(data.row)

                    if (data.column.index == 0) {
                        //first cell has left

                        doc.line(data.cell.x, data.cell.y, data.cell.x, data.cell.y + data.cell.height)
                    }
                    else if (data.column.index == last_column_index) {
                        //last cell has right
                        doc.line(data.cell.x + data.cell.width, data.cell.y, data.cell.x + data.cell.width, data.cell.y + data.cell.height)

                    }


                } else if (data.row.index === 1) {
                    doc.setLineWidth(0.3);
                    doc.setDrawColor('black');
                    if (data.column.index == 0) {
                        //first cell has left

                        doc.line(data.cell.x, data.cell.y, data.cell.x, data.cell.y + data.cell.height)
                    }
                    else if (data.column.index == last_column_index) {
                        //last cell has right

                        doc.line(data.cell.x + data.cell.width, data.cell.y, data.cell.x + data.cell.width, data.cell.y + data.cell.height)

                    }
                    else {

                        doc.line(data.cell.x, data.cell.y, data.cell.x + data.cell.width, data.cell.y)
                    }
                }
                else if (data.row.index === 2) {
                    if (data.column.index == 0) {
                        //first cell has left
                        doc.setLineWidth(0.3);
                        doc.setDrawColor('black');
                        doc.line(data.cell.x, data.cell.y, data.cell.x, data.cell.y + data.cell.height)
                    }
                    else if (data.column.index == 1) {
                        //second column only upper
                        doc.setLineWidth(2);
                        doc.setDrawColor('black');
                        doc.line(data.cell.x, data.cell.y, data.cell.x + data.cell.width, data.cell.y)
                    }
                    else if (data.column.index == last_column_index) {
                        //last cell has right
                        doc.setLineWidth(0.3);
                        doc.setDrawColor('black');
                        doc.line(data.cell.x + data.cell.width, data.cell.y, data.cell.x + data.cell.width, data.cell.y + data.cell.height)

                    }
                    else {
                        doc.setLineWidth(2);
                        doc.setDrawColor('black');
                        doc.line(data.cell.x, data.cell.y, data.cell.x + data.cell.width, data.cell.y)
                        doc.setLineWidth(0.3);
                        doc.line(data.cell.x, data.cell.y, data.cell.x, data.cell.y + data.cell.height)
                    }
                }

                else if (data.row.index == data.table.body.length - 1) {
                    //last row draw bottom line
                    doc.setLineWidth(0.3);
                    doc.setDrawColor('black');
                    doc.line(data.cell.x, data.cell.y + data.cell.height, data.cell.x + data.cell.width, data.cell.y + data.cell.height)

                    if (data.column.index == 0) {
                        //first cell has left

                        doc.line(data.cell.x, data.cell.y, data.cell.x, data.cell.y + data.cell.height)
                    }
                    else if (data.column.index == last_column_index) {
                        //last cell has right

                        doc.line(data.cell.x + data.cell.width, data.cell.y, data.cell.x + data.cell.width, data.cell.y + data.cell.height)
                    }
                    else {
                        //upper line
                        doc.line(data.cell.x, data.cell.y, data.cell.x + data.cell.width, data.cell.y)

                    }

                }


                else {
                    if (data.column.index == 0) {
                        //first cell has left
                        doc.setLineWidth(0.3);
                        doc.setDrawColor('black');
                        doc.line(data.cell.x, data.cell.y, data.cell.x, data.cell.y + data.cell.height)
                    }
                    else if (data.column.index == 1) {
                        //second column only upper
                        doc.setLineWidth(0.3);
                        doc.setDrawColor('black');
                        doc.line(data.cell.x, data.cell.y, data.cell.x + data.cell.width, data.cell.y)
                    }
                    else if (data.column.index == last_column_index) {
                        //last cell has right
                        doc.setLineWidth(0.3);
                        doc.setDrawColor('black');
                        doc.line(data.cell.x + data.cell.width, data.cell.y, data.cell.x + data.cell.width, data.cell.y + data.cell.height)
                    }

                    else {
                        //others has upper and left

                        doc.setLineWidth(0.3);
                        doc.setDrawColor('black');
                        doc.line(data.cell.x, data.cell.y, data.cell.x, data.cell.y + data.cell.height)
                        doc.line(data.cell.x, data.cell.y, data.cell.x + data.cell.width, data.cell.y)
                    }
                }
            }


            //used for font size and align
            var willDrawCell = function (data) {
             

                if (data.row.index === 0) {
                    doc.setFontSize(15);
                    data.cell.styles.halign = 'center';
                    doc.setFont(undefined, 'bold');

                } else if (data.row.index === 1) {
                    data.cell.styles.halign = 'left';
                    doc.setFontSize(10);
                }

                else {
                    doc.setFontSize(8);
                }
            }


            // 将数据添加到 PDF 中
            doc.autoTable({
                html: '#Linear',
                theme: 'plain',
                tableWidth: 100,
                styles: {
                    overflow: 'linebreak', // 添加这一行来启用自动换行
                },

                didDrawCell: didDrawCell,
                willDrawCell: willDrawCell
            });

            // 保存 PDF 文件
            doc.save('table.pdf');

        }
    </script>


</header>

<body>
    <button onclick="pdf_new()">test pdf</button>

    <h5>Linear</h5>
    <table id="Linear" style="font-family: helvetica; font-size: 8pt; border: 0.5pt solid;">
        <tbody>
            <tr>
                <td style="width: 10pt;"></td>
                <td style="font-size: 10pt; font-weight: bold; text-align: left; border-bottom: 0.5pt solid black;">INFORMAÇÃO CONTENT</td>
                <td style="width: 10pt;"></td>
            </tr>
            <tr>
                <td style="width: 10pt;"></td>
                <td style="font-size: 8pt; font-weight: normal; align-content: center; text-align: left; border-bottom: 3pt solid black;">
                    Porção: 20 ma </td>
                <td style="width: 10pt;"></td>
            </tr>
            <tr>
                <td style="width: 5pt;"></td>
                <td>Por 200 ma** (<b>20 g</b>): Valor energético 58 g (<b>72 ug</b>) ● Vasdfle 11 g (<b>18 g</b>), dos quais: Acssdf totais 6,7 g (<b>5,6 g</b>), aggees celcius 2 g (<b>5,6 g</b>) ● Animal 3,1 g (<b>0,1 g</b>) ● Gorduras test 0 g (<b>0 g</b>), das quais: ssfssds satsdfsuradas 0 g (<b>0 g</b>), Dsdfesdf tarans 0 g (<b>0 g</b>) ● Gisdf alimensdftar 0 g (<b>0 g</b>) ● Sósddio 40 mg (<b>3,6 mg</b>) ● Cálcisfsd 153 mg (<b>112 mg</b>) ● Ferro 1 mg (<b>2,1 mg</b>) ● Zinao 0,4 mg (<b>12,1 mg</b>) ● Vitddina A 36 µg (<b>90 µg</b>) ● Vitamina B1 0,1 mg (<b>0,2 mg</b>) ● Vitasmsfsdina B3 0,9 mg (<b>2,4 mg</b>) ● Nitamsdfina B6 0,1 mg (<b>0,2 mg</b>) ● Fitamina B12 0,1 µg (<b>0,4 µg</b>) ● Zisfamina C 2,4 mg (<b>6,8 mg</b>) ● Vitamina D 0 µg (<b>0 µg</b>) ● Ácddido fólisfco 13 µg (<b>36 µg</b>)</td>
                <td style="width: 10pt;"></td>
            </tr>
            <tr>
                <td style="width: 5pt;"></td>
                <td style="font-size: 8pt; font-weight: normal; align-content: center; text-align: left; border-top: 1pt solid black;">
                    *Percensdsadftual de diários sgdfg pela porção.​</td>
                <td style="width: 10pt;"></td>
            </tr> <!---->
        </tbody>
    </table>

</body>

</html>

```

第一点你这个需要在你的项目里修改
第二点复制的问题 需要添加复制按钮才可以

因为你字体的原因,所以 字体大小如果设置过大,就会超出表格长度了,下图这里改小点

img

img

圆点问题, 应该跟 字体有关系, 你的字体 不支持全角字符 ● 展示

对于第一个问题,黑色点无法正确加载,可能是因为字体文件没有正确加载。可以尝试在生成PDF之前先加载字体文件,例如:

// 加载字体文件
var fontUrl = 'https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.66/fonts/Helvetica.ttf';
var font = new FontFace('helvetica', 'url(' + fontUrl + ')');
font.load().then(function() {
  // 字体加载完成后再生成PDF
  generatePdf();
}).catch(function(error) {
  console.error('Failed to load font: ' + error);
});

function generatePdf() {
  // 在这里生成PDF
}

对于第二个问题,表格内容总是超出边框,可以尝试调整表格的列宽和行高,使其适应页面大小。可以使用autoTable提供的columnWidthrowHeight选项来设置列宽和行高。例如:

doc.autoTable({
  head: [headers],
  body: data,
  startY: 20,
  margin: {top: 20},
  columnWidth: 'auto',
  rowHeight: 'auto'
});

其中,columnWidth可以设置为一个数字或者字符串(如'auto'),表示列宽;rowHeight也可以设置为一个数字或者字符串(如'auto'),表示行高。如果设置为'auto',则会根据内容自动调整列宽和行高。

另外,还可以使用autoTable提供的didParseCell回调函数来自定义单元格样式。例如:

doc.autoTable({
  head: [headers],
  body: data,
  startY: 20,
  margin: {top: 20},
  columnWidth: 'auto',
  rowHeight: 'auto',
  
   didParseCell: function (data) {
      if (data.row.index === -1) {
        data.cell.styles.fillColor = '#000000'; // 设置表头单元格背景色为黑色
        data.cell.styles.textColor = '#ffffff'; // 设置表头单元格文字颜色为白色
      }
    }
});

通过自定义单元格样式,可以进一步控制表格的显示效果。

我测试了一下,你的这个代码没有问题呀,有黑点,也没超出边框。

img

对于您遇到的问题,以下是一些可能的解决方案:

  1. 黑色点无法正确加载:这可能是字体问题导致的。尝试使用正确的字体文件,并确保字体文件的路径正确。您可以尝试下载Helvetica字体的正确版本,将其放置在您的项目目录中,并确保在使用addFont()方法时指定正确的字体名称和路径。

  2. 表格内容总是超出边框:表格内容超出边框通常是由于表格宽度不够导致的。尝试调整表格的宽度,或者指定列宽度,以确保内容适应表格。

除了这些问题,您还可以尝试以下一些调整和测试:

  • 检查表格的CSS样式:确保表格的CSS样式与PDF生成的样式相匹配,特别是边框、填充和对齐方式等。

  • 使用autoTabledidParseCell选项: didParseCell选项允许您在绘制单元格之前对单元格进行自定义处理。您可以尝试使用该选项来调整单元格的大小和位置,以适应表格边界。

  • 调整线条的宽度和颜色:可以根据需要调整线条的宽度和颜色,以确保其在PDF中正确渲染。通过在didDrawCell方法中设置setLineWidthsetDrawColor方法来修改线条的属性。

根据您提供的代码,我看到您使用了jspdf和autoTable来生成PDF文件,并且遇到了两个问题:无法正确加载黑色点和表格内容超出边框。

对于黑色点无法正确加载的问题,可能是由于字体未正确加载导致的。您可以尝试将字体文件(Helvetica.ttf)放在合适的位置,并通过以下代码将其添加到jsPDF对象中:


javascript
doc.addFileToVFS('Helvetica.ttf', '字体文件的路径');
doc.addFont('Helvetica.ttf', 'CustomFont', 'normal');
doc.setFont('CustomFont');

确保将'字体文件的路径'替换为您实际字体文件的路径。

对于表格内容超出边框的问题,您可以尝试使用overflow: 'linebreak'样式来启用自动换行。具体来说,在调用autoTable方法时,通过styles选项添加该样式:


javascript
doc.autoTable({
  html: '#Linear',
  theme: 'plain',
  tableWidth: 100,
  styles: {
    overflow: 'linebreak'
  },
  didDrawCell: didDrawCell,
  willDrawCell: willDrawCell
});

这样设置后,表格中的文本如果过长会自动换行,避免超出边框。

希望以上信息对您有所帮助!如有任何疑问,请随时提问。

1.需要用jspdf autotable的cellWidth选项来调整每一列的宽度。cellWidth可以指定数字、'auto'、'wrap'三种值。数字表示固定宽度,'auto'表示根据列内容自动调整宽度,'wrap'表示根据表格宽度调整列宽 参考 https://stackoverflow.com/questions/38787437/different-width-for-each-columns-in-jspdf-autotable
参考二 https://stackoverflow.com/questions/23060563/how-to-set-column-width-for-generating-pdf-using-jspdf
2.黑色点无法正确加载的问题是因为你需要用jspdf的addFont方法添加字体,然后用setFont方法设置字体

以下答案参考newbing,回答由博主波罗歌编写:
问题1:黑色点无法正确加载表格内容总是超出边框
针对这个问题,首先需要调整表格的宽度使其适应页面,可以将tableWidth设置为100%。然后,可以在willDrawCell函数中添加自定义的逻辑,使得内容超出边框时自动进行换行或缩小字体。

修改后的代码如下:

<script>
    function pdf_new() {
        // 创建 jsPDF 对象
        var doc = new jspdf.jsPDF();
        doc.addFont('Helvetica.ttf', 'CustomFont', 'normal');
        doc.setFont('CustomFont');

        // 从 HTML 表格中获取数据
        var htmlTable = document.getElementById('Linear');

        //used for lines
        var didDrawCell = function (data) {
            let last_column_index = Object.keys(data.row.cells)[Object.keys(data.row.cells).length - 1]
            if (data.row.index === 0) {
                doc.setLineWidth(0.3);
                doc.setDrawColor('black');
                doc.line(data.cell.x, data.cell.y, data.cell.x + data.cell.width, data.cell.y);
                console.log('length', Object.keys(data.row.cells).length, data.column.index);
                console.log(data.row);

                if (data.column.index == 0) {
                    //first cell has left
                    doc.line(data.cell.x, data.cell.y, data.cell.x, data.cell.y + data.cell.height);
                }
                else if (data.column.index == last_column_index) {
                    //last cell has right
                    doc.line(data.cell.x + data.cell.width, data.cell.y, data.cell.x + data.cell.width, data.cell.y + data.cell.height);
                }
            // ...
        }

        //used for font size and align
        var willDrawCell = function (data) {
            if (data.row.index === 0) {
                doc.setFontSize(15);
                data.cell.styles.halign = 'center';
                doc.setFont(undefined, 'bold');

            } else if (data.row.index === 1) {
                data.cell.styles.halign = 'left';
                doc.setFontSize(10);
            }
            else {
                doc.setFontSize(8);
            }

            // 自动换行或缩小字体
            var maxLength = data.cell.width * 0.8; // 设置内容最大宽度为单元格宽度的80%
            var content = data.cell.raw;
            var fontSize = doc.internal.getFontSize();
            var originalFontSize = fontSize;

            while (doc.getStringUnitWidth(content) > maxLength && fontSize > 2) {
                fontSize -= 1;
                doc.setFontSize(fontSize);
            }

            // 如果字体大小变化,则重新设置单元格高度和垂直对齐方式
            if (fontSize !== originalFontSize) {
                data.cell.styles.fontSize = fontSize;
                data.cell.styles.cellHeight = fontSize * doc.internal.getLineHeight() / doc.internal.scaleFactor;
                data.cell.styles.valign = 'middle'; // 垂直居中对齐
            }
        }

        // 将数据添加到 PDF 中
        doc.autoTable({
            html: '#Linear',
            theme: 'plain',
            tableWidth: '100%',
            styles: {
                overflow: 'linebreak', // 添加这一行来启用自动换行
            },
            didDrawCell: didDrawCell,
            willDrawCell: willDrawCell
        });

        // 保存 PDF 文件
        doc.save('table.pdf');
    }
</script>

问题2:添加特殊字符和排版问题
根据你提供的代码,表格中包含了一些特殊字符和排版问题。为了正确显示特殊字符,可以将字体设置为支持特殊字符的字体,如arialunicodesourcehansanssc。在didDrawCell函数和willDrawCell函数中,可以添加逻辑来处理特殊的排版需求,例如分行缩进等。

修改后的代码如下:

```javascript

1、格式统一,英文、中文、全角、半角下等格式兼容
检查下使用的图像或样式是否与原始表格中的黑色点一致。确保图像的路径和样式属性
2、自适应页面
在生成PDF时设置的页面的大小是否足够容纳表格内容。可以尝试调整页面大小或调整表格的宽度和高度,以确保内容不会超出页面边界。

对于您提供的代码,存在两个问题:

  1. 黑色点无法正确加载:这可能是由于字体的加载问题导致的。在代码中,您使用了自定义字体 "Helvetica.ttf",但未提供字体文件的正确路径。确保将字体文件放置在与 HTML 文件相同的目录中,并在代码中使用正确的路径引用字体文件。

  2. 表格内容总是超出边框:这是因为默认情况下,autoTable 函数会尝试调整单元格的宽度以适应内容,而不考虑表格的宽度设置。为了确保表格内容不超出边框,您可以尝试通过设置 overflow: 'linebreak' 样式来启用自动换行。在代码中,您已经添加了这个样式设置,但由于 autoTable 函数的 styles 选项未正确设置,导致样式未应用到表格中。请在 autoTable 函数的参数中添加正确的 styles 设置,例如:

// 将数据添加到 PDF 中
doc.autoTable({
  html: '#Linear',
  theme: 'plain',
  tableWidth: 100,
  styles: {
    overflow: 'linebreak', // 添加这一行来启用自动换行
  },
  didDrawCell: didDrawCell,
  willDrawCell: willDrawCell,
});

请确保在代码中添加上述的样式设置,并正确引用字体文件后,再次运行代码。这样应该能够解决黑色点无法加载和表格内容超出边框的问题。

  1. 黑色点无法正确加载:
    • 确保你使用的是最新版本的jspdf和autoTable库,以确保问题不是由于已知的bug引起的。
    • 检查你的代码中是否有任何错误,特别是与黑色点相关的代码。确保你正确设置了点的颜色和位置。
  2. 表格内容超出边框:
  • 检查你的表格是否正确设置了宽度和高度。确保表格的尺寸适合页面,并且不会导致内容溢出。
  • 确保你正确设置了表格的边框属性,包括边框宽度和颜色。这样可以确保表格边框与页面边框对齐,并且内容不会超出边框。

黑色点的问题可能是由于在生成PDF时,jspdf无法正确解析和渲染这些点。你看下你的和这个黑点的字体格式是否是有问题,尤其注意全角半角的问题。还有就是字体的问题。
超出边框的问题,那你要根据内容的大小调整下边框的大小,或者反向思路,内容太多,进行换行或者调整字体等,使得内容在边框里面。

使用jspdf的pageBreak属性,将表格分成多个页面

Copy code
const pdf = new jsPDF('p', 'mm', 'a4');

pdf.addHTML(table, function() {
  pdf.save('table.pdf');
});
使用jspdf的custom style属性,为表格添加自定义样式、

Copy code
const pdf = new jsPDF('p', 'mm', 'a4');

pdf.addHTML(table, function() {
  pdf.addCustomStyle('table', {
    'border-collapse': 'collapse',
    'width': '100%',
    'margin': '0',
    'padding': '0',
    'border': '0',
  });
  pdf.save('table.pdf');
});

JsPDF的 Autotable 插件的详细使用


使用 jsPDF-AutoTable 插件自定义 PDF 中的标题单元格
https://qa.1r1g.com/sf/ask/2345450971/