精华内容
下载资源
问答
  • 里面包含html导出pdf的html2canvas.js和jspdf.debug.js两个文件
  • 3、动态html拼接转pdf public static void htmlCodeComeString(String linkcss,String htmlCode, String outputFile,String title) throws Exception { OutputStream os = new FileOutputStream(outputFile); ...
  • html2canvas,jspdf.debug俩个js文件,还有个小例子,例子有瑕疵,大概功能能实现,在优化吧,
  • html页面导出pdf

    2019-01-04 15:12:37
    将html页面导出pdf,将html页面导出pdf,利用 html2canvas + jsPDF 在线将HTML导出成pdf
  • html导出pdf 中文字体

    2021-09-30 09:23:27
    中文字体,导出中文转换
  • itext html导出pdf

    2018-07-09 16:31:38
    itext html导出pdf---------------------------------------------------------------------------------
  • HTML导出PDF代码.html

    2020-06-19 14:31:16
    一份可以将HTML页面导出成PDF的代码,亲测可以整肠生使用,在编辑好的HTML页面将本文件中的部分复制过去,即可进行将HTML页面导出PDF,在里面可以设置背景颜色,是否分页等属性。
  • .net core 纯html文本 通过wkhtmltopdf 导出pdf分享,多页
  • Html解决pdf导出出现的中文数字等不换行等问题, 已重新修改了Jar包, 重新打包好了,可直接使用,亲测好用.
  • 前人的基础上稍加修改优化了一下,解决了html2Canvas只能截取可见html部分问题以及截取全部目标内容后样式失效问题。 具体问题需要具体分析。 exportPDF(){//点击事件 //imageDom为pdf目标,注意imageDom的样式需要...

    前人的基础上稍加修改优化了一下,解决了html2Canvas只能截取可见html部分问题以及截取全部目标内容后样式失效问题。
    具体问题需要具体分析。

    exportPDF(){//点击事件
      //imageDom为pdf目标,注意imageDom的样式需要和body平级,注意不要污染全局
      var width = this.$refs.imageDom.style.width
      var cloneDom = this.$refs.imageDom.cloneNode(true)
      cloneDom.style.width = width
      document.body.appendChild(cloneDom)
      html2Canvas(cloneDom,{
    	allowTain:true
      }).then(function(canvas){
    	let contentWidth = canvas.width;
    	let contentHeight = canvas.height;
    	let pageHeight = contentWidth / 592.28 * 841.89;
    	let leftHeight = contentHeight;
    	let position = 0;
    	let imgWidth = 592.28;
    	let imgHeight = 592.28 / contentWidth * contentHeight;
        let pageData = canvas.toDataURL('image/jpej',1.0);
        let PDF = new jsPDF('','pt','a4');
    	if(leftHeight < pageHeight){
    	  PDF.addImage(pageData,'JPEG',0,0,imgWidth,imgHeight)
    	}else{
    	  while(leftHeight > 0){
    	    PDF.addImage(pageData,'JPEG',0,position,imgWidth,imgHeight);
    	    leftHeight -= pageHeight;
    		position -= 841.89;
    		if(leftHeight > 0){
    	      PDF.addPage();
    	    }
    	  }
        }
    	PDF.save('可以设置成变量.pdf')
      })
      document.body.removeChild(cloneDom)
    }
    

    但是还有别的问题:html连续的内容被粗暴截断,后续继续更新,先把基本功能实现


    展开全文
  • 使用C# NET Core技术,通过预设的html模板,导出Word文件,Html文件,Pdf,使用说明参考https://blog.csdn.net/qq_27337291/article/details/120676751?spm=1001.2014.3001.5501
  • 基础效果图: demo源码 使用html2canvas自动分页效果图: demo源码 一、基础: 1.先引入几个文件 ...script src="https://cdn.bootcdn.net/ajax/libs/html2canvas/0.5.0-beta4/html2canvas.min.js"&g

    欢迎点击: 个人官网博客

    基础效果图:
    demo源码
    在这里插入图片描述

    使用html2canvas自动分页效果图:
    demo源码
    在这里插入图片描述

    一、基础:

    1.先引入几个文件

    <script src="http://www.jq22.com/jquery/1.8.3/jquery.min.js"></script>
        <script src="https://cdn.bootcdn.net/ajax/libs/html2canvas/0.5.0-beta4/html2canvas.min.js"></script>
        <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/jspdf/1.5.2/jspdf.min.js"></script>
    

    2.平常我们都是后端传链接打开新窗口预览pdf,那么在纯前端html中可以使用iframe标签预览pdf,jspdf或者配合html2canvas创建pdf

    <iframe id="pdf" width="500" height="400" seamless scrolling="yes" src="" frameborder="0"></iframe>
    
    //看不懂这一句,请往下看
     const doc = new jsPDF('p', 'pt', 'a4')//a4纸大小
    $('#pdf').attr('src', doc.output('datauristring')) //预览放在iframe标签
     
    

    直接下载pdf:doc.save(“a4.pdf”);
    导出pdf文件数据:doc.output(‘datauristring’)
    添加一页: doc.addPage();

    3.文字,图片,多边形,的运用

    <script>
            const doc = new jsPDF('p', 'pt', 'a4')//a4纸大小
    
    
            //文字
            // // 打印所有可用的字体
            // console.log(doc.getFontList());
            // // 设置字体(内置的几种字体都是不支持中文,中文字体需要自己添加)
            // doc.setFont(doc.getFontList()[3]);
            doc.setFontSize(10)
    
            doc.text(20, 20, 'This is the default font.');
    
            doc.setFont("courier");
            doc.setFontType("normal");
            doc.text(20, 30, 'This is courier normal.');
    
            doc.setFont("times");
            doc.setFontType("italic");
            doc.text(20, 40, 'This is times italic.');
    
            doc.setFont("helvetica");
            doc.setFontType("bold");
            doc.text(20, 50, 'This is helvetica bold.');
    
            doc.setTextColor(255, 0, 0);
            doc.setFont("courier");
            doc.setFontType("bolditalic");
            doc.text(20, 60, 'This is courier bbolditalic');
    
    
    
            //多边形
            doc.setLineWidth(1);
            doc.setDrawColor(255, 0, 0);
            doc.setFillColor(0, 0, 255);
            doc.triangle(100, 100, 110, 100, 120, 130, 'FD'); //分别三点x,y坐标
    
    
    
            //图片
            var myImage = new Image();
            myImage.src = './s1.png';
            myImage.onload = function () {
                //x,y,w,h
                doc.addImage(myImage, 'png', 50, 100, myImage.width, myImage.height);
                console.log(doc.output('datauristring')) //所生成的pdf文件
                doc.addPage(); //如果后面还有内容,添加一个空页
                doc.addImage(myImage, 'png', 50, 100, myImage.width, myImage.height); //相对于新添加一个空页
                $('#pdf').attr('src', doc.output('datauristring')) //预览放在iframe标签
                  // doc.save("a4.pdf");//保存为pdf
            };
    
    
    
    
          
        </script>
    

    使用htmlcanvas

    如果不了解htmlcanvas,点击这里

    function create() {
                var dom = $('.poster-container')//为需要生成pdf最外层的div盒子
                html2canvas(dom[0], {
                    useCORS: true, //跨域
                    allowTaint: false,
                    logging: false,
                    letterRendering: true,
                    taintTest: true, //在渲染前测试图片
                    dpi: window.devicePixelRatio, // window.devicePixelRatio是设备像素比
                    background: "#fff"
                }).then(function (canvas) {
                    var imgUrl = canvas.toDataURL('image/png');
                    doc.addImage(imgUrl, 'png', 0, 100, $('.poster-container').width(), $('.poster-container')
                        .height());
                    $('#pdf').attr('src', doc.output('datauristring')) //预览放在iframe标签
                }
    

    二、自动分页pdf

    巧妙的运用了html2canvas的截图效果,然后把图片转化为pdf文件

     html2canvas(document.querySelector('#boardPdf'), {
                    scale: 2,
                    dpi: window.devicePixelRatio * 2,
                    useCORS: true, // 如果截图的内容里有图片,可能会有跨域的情况,加上这个参数,解决文件跨域问题
                    onrendered: function (canvas) {
                        var contentWidth = canvas.width
                        var contentHeight = canvas.height
                        // 一页pdf显示html页面生成的canvas高度;
                        var pageHeight = contentWidth / 592.28 * 841.89
                        // 未生成pdf的html页面高度
                        var leftHeight = contentHeight
                        // pdf页面偏移
                        var position = 0
                        // a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
                        var imgWidth = 595.28
                        var imgHeight = 592.28 / contentWidth * contentHeight
                        var pageData = canvas.toDataURL('image/jpeg', 1.0)
                        // eslint-disable-next-line
                        var pdf = new jsPDF('', 'pt', 'a4')
                        // 有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
                        // 当内容未超过pdf一页显示的范围,无需分页
                        if (leftHeight < pageHeight) {
                            pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
                        } else {
                            while (leftHeight > 0) {
                                pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
                                leftHeight -= pageHeight
                                position -= 841.89
                                // 避免添加空白页
                                if (leftHeight > 0) {
                                    pdf.addPage()
                                }
                            }
                        }
                        // pdf.save('demo.pdf')
                        $('#pdf').attr('src', pdf.output('datauristring'))
                    },
                    background: '#fff'//背景色
                })
    
    展开全文
  • html页面的内容导出pdf,带有demo的实例,通俗易懂,直接调用。
  • C# asp.net html 导出 pdf 打印 引用 wkhtmltopdf.exe,简单明了,不用编译那么多代码,直接调用wkhtmltopdf.exe把html格式的导出pdf ,经过本人测试时ok的!
  • html导出pdf的四种方式

    千次阅读 2018-11-01 17:20:00
    html页面导出pdf文件并打印,可以直接在windows下使用Ctrl + P,苹果下⌘ + P。 如果需要用代码实现,可以考虑jsPDF、iText、wkhtmltopdf等方式。 以下是三种方式代码对比: 方式优点缺点分页图片表格...

    将html页面导出为pdf文件并打印,可以直接在windows下使用Ctrl + P,苹果下⌘ + P。

    如果需要用代码实现,可以考虑jsPDF、iText、wkhtmltopdf等方式。

    以下是三种方式代码对比:

    方式优点缺点分页图片表格链接中文特殊字符、样式导出样例备注
    jsPDF1、整个过程在客户端执行(不需要服务器参与),调用简单1、生成的pdf为图片形式,且内容失真支持支持支持不支持支持支持  
    iText1、功能基本可以实现,比较灵活2、生成pdf质量较高1、对html标签严;格,少一个结束标签就会报错;2、后端实现复杂,服务器需要安装字体;3、图片渲染比较复杂(暂时还没解决)支持支持支持支持支持支持  
    wkhtmltopdf1、调用方式简单(只需执行一行脚本);2、生成pdf质量较高1、服务器需要安装wkhtmltopdf环境;2、根据网址生成pdf,对于有权限控制的页面需要在拦截器进行处理支持支持支持支持支持支持  


    从实用和质量综合考虑,个人推荐使用iText。生成各种票据等文件质量好,代码也并不复杂。


    以下是我使用三种方式测试的例子,IDE使用IDEA,Spring Boot结合Freemarker。

    1.iText

    https://itextpdf.com/

    iText是一个第三方报表java插件,可以在后端利用java随意生成、转化pdf文件,提供了很多api,比较灵活。

            <!--PDF-->
            <dependency>
                <groupId>org.eclipse.birt.runtime.3_7_1</groupId>
                <artifactId>com.lowagie.text</artifactId>
                <version>2.1.7</version>
            </dependency>
            <dependency>
                <groupId>org.xhtmlrenderer</groupId>
                <artifactId>flying-saucer-pdf</artifactId>
                <version>9.0.8</version>
            </dependency>
            <dependency>
                <groupId>com.itextpdf</groupId>
                <artifactId>itextpdf</artifactId>
                <version>5.4.2</version>
            </dependency>
            <!--PDF end-->

    使用iText需要下载字体文件

      /**
         * iText生成PDF 需要字体支持
         *
         * @param args
         * @throws IOException
         * @throws DocumentException
         */
        public static void main(String[] args) throws IOException, DocumentException {
            ITextRenderer renderer = new ITextRenderer();
            ITextFontResolver fontResolver = renderer.getFontResolver();
            fontResolver.addFont("E:/下载/simsunttc/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            OutputStream os = new FileOutputStream("E:/create/" + UUID.randomUUID() + ".pdf");
            String htmlstr = "<!DOCTYPE html>\n" +
                    "<html lang=\"en\">\n" +
                    "<head>\n" +
                    "    <title>Title</title>\n" +
                    "</head>\n" +
                    "<body>\n" +
                    "<table border=\"1\">\n" +
                    "    <tr>\n" +
                    "        <td>row 1, cell 1</td>\n" +
                    "        <td>row 1, cell 2</td>\n" +
                    "    </tr>\n" +
                    "    <tr>\n" +
                    "        <td>row 2, cell 1</td>\n" +
                    "        <td>row 2, cell 2</td>\n" +
                    "    </tr>\n" +
                    "</table>\n" +
                    "</body>\n" +
                    "</html>";
            renderer.setDocumentFromString(htmlstr);
            renderer.layout();
            renderer.createPDF(os);
        }

    使用Itext可以方便的根据写好的html模板来填充内容,设置标题页眉添加背景图片等操作。

    2.jsPDF

    生成效果并不是很好,但是无需后台服务器支持,操作简单

    <!DOCTYPE>
    <html>
      <head>
        <title>
          html2canvas example
        </title>
          <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <style type="text/css">
          body {
            margin: 0;
            padding: 0;
            background-color: white;
          }
          header, section {
              overflow: hidden;
          }
          ul {
            margin: 0;
            border: 0;
            padding: 0;
          }
          li {
            display: block; /* i.e., suppress marker */
            color: black;
            height: 4em;
            width: 25%;
            margin: 0;
            float: left;
            background-color: green;
            text-align: center;
            line-height: 4em;
          }
          
          aside {
            width: 20%;
            float: left;
            text-align: center;
          }
    
          aside a {
            display: block;
            height: 4em;
            color: blue;
          }
    
          article {
            padding: 2em 0;
            width: 80%;
            float: left;
          }
        </style>
      </head>
      <body>
        <header>
          <nav>
            <ul>
              <li>one</li>
              <li>two</li>
              <li>three</li>
              <li>four</li>
            </ul>
          </nav>
        </header>
        <section>
          <aside>
            <h3>it is a title</h3>
            <a href="">Stone Giant</a>
            <a href="">link2</a>
            <a href="">link3</a>
            <a href="">link4</a>
            <a href="">link5</a>
            <a href="">link6</a>
          </aside>
            <article>
                <img src="./img/Stone.png">
                <button id="renderPdf">DOWNLOAD PDF</button>
                <h2>Stone Giant</h2>
                <p>
                    Coming to life as a chunk of stone, Tiny's origins are a mystery on which he continually speculates. He is a Stone Giant now, but what did he used to be? A splinter broken from a Golem's heel? A shard swept from a gargoyle-sculptor's workshop? A fragment of the Oracular Visage of Garthos? A deep curiosity drives him, and he travels the world tirelessly seeking his origins, his parentage, his people. As he roams, he gathers weight and size; the forces that weather lesser rocks, instead cause Tiny to grow and ever grow.
                </p>
                <p>
                    以一团石头的形式出现的生命体,小小不断思索他的起源,但这始终是个谜。现在的他是个石巨人,但过去是什么呢?从土傀儡的脚后跟掉落的碎片?从制造石像鬼的工房被打扫出来的碎屑?神圣预言石的表层之砂?受到强烈的好奇心驱使,他不知疲倦的环游世界,寻找着他的起源,他的出身,和他的种族。在旅途中,他变得越来越庞大,不过路上的风雨吹打掉了他身上的石头,所以他不停的吸收新的岩石,永远在长大。
                </p>
                <img src="./img/Spectre.jpg">
                <h2>Spectre</h2>
                <p>
                    Just as higher states of energy seek a lower level, the Spectre known as Mercurial is a being of intense and violent energy who finds herself irresistibly drawn to scenes of strife as they unfold in the physical world. While her normal spectral state transcends sensory limitations, each time she takes on a physical manifestation, she is stricken by a loss of self--though not of purpose. In the clash of combat, her identity shatters and reconfigures, and she begins to regain awareness. She grasps that she is Mercurial the Spectre--and that all of her Haunts are but shadows of the one true Spectre. Focus comes in the struggle for survival; her true mind reasserts itself; until in the final moments of victory or defeat, she transcends matter and is restored once more to her eternal form.
                </p>
                <p>
                    和所有强大的能量都喜欢欺凌弱小一样,被称为墨丘利的幽鬼也是一个拥有着强横能量的存在,同样的,她对现实世界中的冲突和纷争无比着迷。然而她平时的幽鬼形态超越了常人的感知范围,因此每当她以实体形态出现时,她不得不损失一部分自我能量——尽管她也不愿意。在战斗中,她的自我意识逐渐散落并重新聚合,她也开始有了意识。她意识到了自己是幽鬼墨丘利——其他所有的鬼影都只是她自我的阴影。出于重新凝聚的打算,她开始专注,她的心智也在不断的成熟。只有等到她取得胜利或者彻底失败时,她那超物质的形态才会得以重聚。
                </p>
                <img src="./img/Ancient%20Apparition.jpg">
                <h2>Ancient Apparition</h2>
                <p>
                    Kaldr, the Ancient Apparition, is an image projected from outside time. He springs from the cold, infinite void that both predates the universe and awaits its end. Kaldr is, Kaldr was, Kaldr shall be...and what we perceive, powerful as it appears to us, is but the faintest faded echo of the true, eternal Kaldr. Some believe that as the cosmos ages and approaches its final moments, the brightness and power of Kaldr will also intensify--that the Ancient Apparition will grow younger and stronger as eternity's end draws nigh. His grip of ice will bring all matter to a stop, his image will cast a light too terrible to behold. An Apparition no longer!
                </p>
                <p>
                    卡德尔,极寒幽魂,是时光之外的冰冷投影。他来自寒冷的无尽虚空,目睹宇宙诞生,见证宇宙终结。卡德尔是夕在,今在,永在的无上力量...我们的所有认知,所有自认为正确的强大的事物,在永恒的卡德尔看来,不过是最细微最无力的附和。有人相信,随着宇宙的老化并走向衰亡,卡德尔的力量和光芒也将变得更强——极寒幽魂将更加年轻,更加强大。他对冰霜的控制能够冻结一切事物,他的投影放出的光芒异常夺目。他将不再是幽魂,而是神!
                </p>
                <img src="./img/weaver.jpg">
                <h2>Weaver</h2>
                <p>
                    The fabric of creation needs constant care, lest it grow tattered; for when it unravels, whole worlds come undone. It is the work of the Weavers to keep the fabric tight, to repair worn spots in the mesh of reality. They also defend from the things that gnaw and lay their eggs in frayed regions, whose young can quickly devour an entire universe if the Weavers let their attention lapse. Skitskurr was a master Weaver, charged with keep one small patch of creation tightly woven and unfaded. But the job was not enough to satisfy. It nagged him that the original work of creation all lay in the past; the Loom had done its work and travelled on. He wanted to create rather than merely maintain--to weave worlds of his own devising. He began making small changes to his domain, but the thrill of creation proved addictive, and his strokes became bolder, pulling against the pattern that the Loom had woven. The guardians came, with their scissors, and Weaver's world was pared off, snipped from the cosmic tapestry, which they rewove without him in it. Skitskurr found himself alone, apart from his kind, a state that would have been torment for any other Weaver. But Skitskurr rejoiced, for now he was free. Free to create for himself, to begin anew. The raw materials he needed to weave a new reality were all around him. All he had to do was tear apart this old world at the seams.
                </p>
                <p>
                    创世之纱需要长期细心的照料,以防止其变得残破;因为一旦它散开了,整个世界就将毁于一旦。编织者的工作就是保持创世之纱的紧密,用现实之网修补它的破损。他们同样要防止那些在创世之纱的缺口上产卵或者侵蚀创世之纱的虫子,只要编织者稍微分心,这些家伙的幼虫就能吞噬掉整个宇宙。斯吉茨格尔是一名大师级的编织者,负责维护一块小补丁的紧密。然而这项任务并不能满足他,他经常唠叨过去那些原始的创造工作,对干完活就走人的世界纺织者也是颇有微词。他想创造,不想只是维护——他想按自己的设计编织出自己的世界。他开始在他负责的区域上做手脚,逐渐不能自拔,他的胆子也愈发的大,甚至私自改动了世界纺织者编织的图案。最后,守卫者来了,毁掉了编织者所作的一切,直接从创世之纱上去除了这一块,然后重新编织,却不让他参与其中。斯吉茨格尔现在孤身一人,被种群所弃,换做任何其他编织者,都会备受折磨。然而斯吉茨格尔却无比愉悦,因为他终于自由了,能够自由的创造,重头开始。他创造新世界所需的所有材料都触手可及。他只需要从缺口处将现在的世界撕裂。
                </p>
                <img src="./img/Doom%20Bringer.jpg">
                <h2>Doom Bringer</h2>
                <p>
                    A towering being of unimaginable evil, Lucifer the Doom Bringer marches the farthest reaches of the world in search of new and exciting ways to satisfy his taste for unrest and greed. Once a feared leader in the army of the Purgers of the Realm, Doom left his position as a comrade of fellow demonic warriors as he simply could not bear the thought of sharing the glory of pillaging and feats of destruction with other lowly demons. Despite no longer leading an army, Doom is a fearful foe in combat, possessing mastery of hellish magic and physical attacks - eventually, the world will belong to Doom.
                </p>
                <p>
                    一个邪恶程度超乎想象的存在——末日使者路西法在世界各地不停寻找着新的方法来满足他的贪婪和对骚乱的热衷。他曾经是其所在国度中备受畏惧的灭劫军团统帅,然而末日使者后来却离开了他的将军职位,丢下了一帮恶魔战士,原因很简单,他无法与一帮低级恶魔分享掠夺和毁灭带来的所谓荣耀。尽管他不再是军队的统帅了,末日使者在战斗中仍然是个令人恐惧的对手,他拥有极高的肉搏技巧,还掌握了邪恶的地狱魔法——最终,整个世界将为他所有。
                </p>
                <img src="./img/Dragon%20Knight.jpg">
                <h2>Dragon Knight</h2>
                <p>
                    After years on the trail of a legendary Eldwurm, the Knight Davion found himself facing a disappointing foe: the dreaded Slyrak had grown ancient and frail, its wings tattered, its few remaining scales stricken with scale-rot, its fangs ground to nubs, and its fire-gouts no more threatening than a pack of wet matchsticks. Seeing no honor to be gained in dragon-murder, Knight Davion prepared to turn away and leave his old foe to die in peace. But a voice crept into his thoughts, and Slyrak gave a whispered plea that Davion might honor him with death in combat. Davion agreed, and found himself rewarded beyond expectation for his act of mercy: As he sank his blade in Slyrak's breast, the dragon pierced Davion's throat with a talon. As their blood mingled, Slyrak sent his power out along the Blood Route, sending all its strength and centuries of wisdom to the knight. The dragon's death sealed their bond and Dragon Knight was born. The ancient power slumbers in the Dragon Knight Davion, waking when he calls it. Or perhaps it is the Dragon that calls the Knight...
                </p>
                <p>
                    在传说中的龙冢——厄尔多姆试炼多年以后,骑士戴维安发现自己的对手愈发不能令他满意了:过去那个让人闻风丧胆的神龙斯莱瑞克已经变得苍老而脆弱,它的双翼已经残破,它所剩不多的龙鳞也开始腐烂,它的爪子变得肿大老化,它曾经引以为傲的火焰吐息现在威力和进水了的火柴差不多。戴维安觉得这样的屠龙行径已经不能给他带来任何荣誉,转身就要离开,让他的老对手安静的死去。但是他的脑海里传来了一个声音,斯莱瑞克低声的乞求着,让戴维安允许它光荣的战死。戴维安同意了,随即发现他的怜悯给他带来了意外的收获:当他将手中的锋刃刺入斯莱瑞克的胸膛时,龙使出最后的力量用龙爪刺穿了他的喉咙,随着他们血液的融合,斯莱瑞克将它所有的力量随着血液赐予了戴维安,也赐予了他龙族千万年来的智慧。龙的死去将他们的命运完全的绑定在了一起,龙骑士横空出世。古老的力量在龙骑士戴维安的身体里沉睡着,当他需要力量时则完全复苏。而龙族之力,也唤醒了骑士的所有力量...
                </p>
                <img src="./img/Venomancer.jpg">
                <h2>Venomancer</h2>
                <p>
                    In the Acid Jungles of Jidi Isle, poison runs in the veins and bubbles in the guts of every creature that scuttles, climbs or swoops between fluorescent vines dripping with caustic sap. Yet even in this toxic menagerie, Venomancer is acknowledged as the most venomous. Ages ago, an Herbalist named Lesale crossed the Bay of Fradj by coracle, searching for potent essences that might be extracted from bark and root, and found instead a nightmare transformation. Two leagues into Jidi's jungle, Lesale encountered a reptile camouflaged as an epiphyte, which stung him as he mistakenly plucked it. In desperation, he used his partial knowledge of the jungle's herbal bounty, mixing the venom of the (swiftly throttled) reptile with the nectar of an armored orchid, to compound an antidote. In the moments before a black paralysis claimed him completely, he injected himself by orchid-thorn, and instantly fell into a coma. Seventeen years later, something stirred in the spot where he had fallen, throwing off the years' accumulation of humus: Venomancer. Lesale the Herbalist no longer--but Lesale the Deathbringer. His mind was all but erased, and his flesh had been consumed and replaced by a new type of matter--one fusing the venom of the reptile with the poisonous integument of the orchid. Jidi's Acid Jungles knew a new master, one before whom even the most vicious predators soon learned to bow or burrow for their lives. The lurid isle proved too confining, and some human hunger deep in the heart of the Venomancer drove Lesale out in search of new poisons--and new deaths to bring.
                </p>
                <p>
                    在基迪岛上的浓酸密林中,在所有生物的体内,包括植物的根茎,动物的内脏中,都流淌着致命的腐蚀剧毒。然而,就算在这种毒巢里,剧毒术士也是公认的万毒之王。多年以前,一个叫做里瑟尔的植物学家乘坐小舟跨越弗拉基海湾,想要从植物的根须中提取出一种强力药剂,结果他却遭遇了噩梦一般的变故。在深入到基迪岛密林中数英里时,里瑟尔遇到了一种伪装成寄生植物的毒性爬虫,当他想把爬虫扯下来的时候,被爬虫狠狠的蛰了。绝望之际,他用他对丛林植物仅有的认知,飞快地掐住这只爬虫后,将它的毒液和一种带甲兰花的花蜜混合,合成了解毒剂。他用兰花的尖刺为自己注射了解毒剂,然后立即陷入昏迷,并且逐渐陷入了全身完全麻木的状态。十七年后,在他倒下的地方,从多年积累的腐土中钻出某个东西:剧毒术士。草药学家里瑟尔已经不复存在,现在他是死亡使者里瑟尔。他的记忆几乎都没有了,他原来的肉体已经毁灭,现在被一种新的物质所替代--融合了那只爬虫的毒液和兰花的毒性外皮。基迪岛的浓酸丛林现在有了新的主人,过去最剧毒的捕食者在他面前都只能逃走或臣服求饶。这个可怕的岛屿毕竟太有限了,里瑟尔受到内心深处残留的人类的饥渴驱使,离开了岛屿,去寻找新的毒物,以及带来新的死亡。
                </p>
                <img src="./img/Beast%20Master.jpg">
                <h2>Beast Master</h2>
                <p>
                    Karroch was born a child of the stocks. His mother died in childbirth; his father, a farrier for the Mad King of Slom, was trampled to death when he was five. Afterward Karroch was indentured to the king’s menagerie, where he grew up among all the beasts of the royal court: lions, apes, fell-deer, and things less known, things barely believed in. When the lad was seven, an explorer brought in a beast like none before seen. Dragged before the King in chains, the beast spoke, though its mouth moved not. Its words: a plea for freedom. The King only laughed and ordered the beast perform for his amusement; and when it refused, struck it with the Mad Scepter and ordered it dragged to the stocks. Over the coming months, the boy Karroch sneaked food and medicinal draughts to the wounded creature, but only managed to slow its deterioration. Wordlessly, the beast spoke to the boy, and over time their bond strengthened until the boy found he could hold up his end of a conversation--could in fact speak now to all the creatures of the King's menagerie. On the night the beast died, a rage came over the boy. He incited the animals of the court to rebel and threw open their cages to set them amok on the palace grounds. The Mad King was mauled in the mayhem. In the chaos, one regal stag bowed to the boy who had freed him; and with Beastmaster astride him, leapt the high walls of the estate, and escaped. Now a man, Karroch the Beastmaster has not lost his ability to converse with wild creatures. He has grown into a warrior at one with nature’s savagery.
                </p>
                <p>
                    卡洛克自出生伊始就被当做兽婴。他的母亲在他出生时就死去;他的父亲是狂王斯洛姆的马蹄铁匠,在他五岁时被马群践踏致死。后来,卡洛克将自己卖到国王的动物园干活,在那里,他和宫廷里面饲养的狮子,猩猩,野鹿以及其他一些很少见的甚至传说中的野兽一起长大。在他七岁那年,一个冒险者带着一只没人见过的野兽来觐见国王。当这只野兽被国王的链条锁住的时候,它说话了,乞求自由,然而它的嘴并没有张开。国王大笑,命令野兽表演助兴,遭到拒绝以后,国王用他的疯狂权杖狠狠的抽打了野兽,并把它关在了兽栏里面。接下来的几个月里,卡洛克每天都给这个受伤的野兽偷偷的带去食物和药物,然而这一切只能减缓野兽的死亡。这只野兽和卡洛克开始了交流,无言的交流,他们之间的情感纽带也随着时间的推移而加深,最后卡洛克发现他竟然能够和宫廷动物园里面的所有动物交流。在那只野兽死去的晚上,卡洛克狂怒无比,他煽动了所有的动物一起反叛,并且将它们的笼子打开,在宫廷广场上大开杀戒。狂王在动乱中受伤。在混乱之中,一只皇家雄鹿在这个救了它的男孩面前屈膝,让他以兽王的身份骑上它,带他跃过了堡垒的高墙,逃出生天。现在,兽王卡洛克已经成长为一个男子汉,并且仍然能够自由的和野生动物交谈。他已经成为了拥有自然狂猛野性的战士。
                </p>
                <img src="./img/Dark%20Seer.jpg">
                <h2>Dark Seer</h2>
                <p>
                    Fast when he needs to be, and a cunning strategist, Ish'Kafel the Dark Seer requires no edged weapons to vanquish his enemies, relying instead on the strength of his powerful mind. His talent lies in his ability to maneuver the fight to his advantage. Hailing from a place he calls 'The Land behind the wall,' Dark Seer remains an outsider here—a warrior from a realm beyond the veil of this reality. Once a great general among his people, and a valiant defender of the god-king Damathryx, Dark Seer’s army was wiped out by a much larger force in the final days of the Great Boundaries War. Facing certain defeat, he made one last desperate act: he led the enemy forces into the maze between the walls. At the last moment, just before capture, he crossed over—then sealed the walls forever in an explosive release of dark energy. When the dust settled, he saw that he had saved his people but found himself blinking at the sun of a different world, with no way to return. Now he is committed to proving his worth as a military strategist, and vows to show that he’s the greatest tactician this strange new world has ever seen.
                </p>
                <p>
                    迅捷如风,足智多谋,黑暗贤者依什卡菲尔并不需要多么锋利的武器来搏斗,他总是运用强大的心灵之力来征服敌人。他有着颠覆战局使之对己方有利的天才。迎着欢呼和敬意,他从一个叫做“幻墙之末”的世界走了出来,并不热衷于这个世界的纷争——他是一个来自现实世界之外的勇者。曾经,黑暗贤者是备受人民尊敬的将军,是神王达玛瑞克斯麾下英勇的保卫者,然而他的军队在边境大战的最后几天,被一股更为强大的力量悉数歼灭。面临如此惨败,他绝望的做出了最后一个决定:引诱着敌军进入了幻墙迷宫。在他即将被捕的前一刻,他穿过幻墙,释放出强大的黑暗能量,将幻墙永远的封印起来。当飞扬的尘土归于平静以后,他发现他成功的拯救了自己的人民,而自己却沐浴在另一个世界的阳光下,亦真亦幻,无法回到现实世界。现在,他决心以一名战略家的身份来证明自己的价值,并且立誓要让这个新的世界见识他那伟大的谋略。
                </p>
            </article>
        </section>
        <footer>write by linwalker @2017</footer>
        <script type="text/javascript" src="./js/html2canvas.js"></script>
        <script type="text/javascript" src="./js/jsPdf.debug.js"></script>
        <script type="text/javascript">
    
          var downPdf = document.getElementById("renderPdf");
    
          downPdf.onclick = function() {
              html2canvas(document.body, {
                  onrendered:function(canvas) {
    
                      var contentWidth = canvas.width;
                      var contentHeight = canvas.height;
    
                      //一页pdf显示html页面生成的canvas高度;
                      var pageHeight = contentWidth / 595.28 * 841.89;
                      //未生成pdf的html页面高度
                      var leftHeight = contentHeight;
                      //pdf页面偏移
                      var position = 0;
                      //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
                      var imgWidth = 555.28;
                      var imgHeight = 555.28/contentWidth * contentHeight;
    
                      var pageData = canvas.toDataURL('image/jpeg', 1.0);
    
                      var pdf = new jsPDF('', 'pt', 'a4');
                      //有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
                      //当内容未超过pdf一页显示的范围,无需分页
                      if (leftHeight < pageHeight) {
                          pdf.addImage(pageData, 'JPEG', 20, 0, imgWidth, imgHeight );
                      } else {
                          while(leftHeight > 0) {
                              pdf.addImage(pageData, 'JPEG', 20, position, imgWidth, imgHeight)
                              leftHeight -= pageHeight;
                              position -= 841.89;
                              //避免添加空白页
                              if(leftHeight > 0) {
                                  pdf.addPage();
                              }
                          }
                      }
    
                      pdf.save('content.pdf');
                  }
              })
          }
        </script>
      </body>
    </html>

    3.wkhtmltopdf

    wkhtmltopdf是一个可以把html转为pdf的插件,有windows、linux等平台的版本,最大的特点就是使用简单,语言无关性。
    1、下载:官网下载 https://wkhtmltopdf.org/downloads.html
    2、执行:该插件是“绿色版”,无需编译安装,下载解压后,在bin目录下有wkhtmltoimage和wkhtmltopdf两个文件,生成pdf可以直接运行wkhtmltopdf(也可以把bin目录配置到环境变量),执行wkhtmltopdf -V查看是否可以执行。 

    wkhtmltopdf --disable-smart-shrinking https://www.cnblogs.com/jiangwz myBlog.pdf

    生成的PDF文件:

     

    转载于:https://www.cnblogs.com/jiangwz/p/9890472.html

    展开全文
  • Java HTML直接导出PDF需要的包有:core-renderer-1.0.jar core-renderer-R8pre1.jar core-renderer.jar iText-2.0.8.jar jtidy-4aug2000r7-dev.jar Tidy.jar iTextAsian.jar
  • SpringBoot + Freemarker +itext 渲染 html 导出 PDF

    千次阅读 热门讨论 2019-01-10 17:15:28
    文章目录1 摘要2 Maven 依赖3 核心代码3.1 资源文件处理工具类3.2 PDF 生成工具类3.3 PDF 导出所需资源文件3.4 配置资源文件路径4 导出流程4.1 Service 层4.2 Controller 层4.3 导出效果5 注意事项6 Github 源码 ...

    1 摘要

    PDF 导出在需要将信息纸质化存档时会使用到。这里将介绍在 spring boot 框架下使用 Freemarker + iText 将 ftl 模板渲染成 html,然后导出 PDF 文件

    Freemarker 官方文档: https://freemarker.apache.org/docs/index.html

    2 Maven 依赖

    核心 Maven 依赖

    pom.xml 文件

                <!-- freemarker,生成 html -->
                <dependency>
                    <groupId>org.freemarker</groupId>
                    <artifactId>freemarker</artifactId>
                    <version>${freemarker.version}</version>
                </dependency>
                <!-- 渲染 css 样式 -->
                <dependency>
                    <groupId>org.xhtmlrenderer</groupId>
                    <artifactId>flying-saucer-pdf</artifactId>
                    <version>${flying-saucer-pdf.version}</version>
                </dependency>
                <!-- iText,export pdf -->
                <dependency>
                    <groupId>com.itextpdf</groupId>
                    <artifactId>itextpdf</artifactId>
                    <version>${itextpdf.version}</version>
                </dependency>
    

    其中对应的版本为:

    <freemarker.version>2.3.28</freemarker.version>
    <flying-saucer-pdf.version>9.1.16</flying-saucer-pdf.version>
    <itextpdf.version>5.5.13</itextpdf.version>
    

    3 核心代码

    3.1 资源文件处理工具类

    com.ljq.demo.common.util.ResourceFileUtil

    package com.ljq.demo.common.util;
    
    import org.springframework.util.ResourceUtils;
    
    import java.io.File;
    import java.io.FileNotFoundException;
    
    /**
     * @Description: 项目静态资源文件工具类
     *     仅可用于包含在web项目中的资源文件路径,资源文件必须放置于 web 模块下
     * @Author: junqiang.lu
     * @Date: 2019/1/4
     */
    public class ResourceFileUtil {
    
    
        /**
         * 获取资源文件
         *
         * @param relativePath 资源文件相对路径(相对于 resources路径,路径 + 文件名)
         *                     eg: "templates/pdf_export_demo.ftl"
         * @return
         * @throws FileNotFoundException
         */
        public static File getFile(String relativePath) throws FileNotFoundException {
            if (relativePath == null || relativePath.length() == 0) {
                return null;
            }
            if (relativePath.startsWith("/")) {
                relativePath = relativePath.substring(1);
            }
            File file = ResourceUtils.getFile(ResourceUtils.CLASSPATH_URL_PREFIX
                    + relativePath);
    
            return file;
        }
    
        /**
         * 获取资源绝对路径
         *
         * @param relativePath 资源文件相对路径(相对于 resources路径,路径 + 文件名)
         *                     eg: "templates/pdf_export_demo.ftl"
         * @return
         * @throws FileNotFoundException
         */
        public static String getAbsolutePath(String relativePath) throws FileNotFoundException {
            return getFile(relativePath).getAbsolutePath();
        }
    
        /**
         * 获取资源父级目录
         *
         * @param relativePath 资源文件相对路径(相对于 resources路径,路径 + 文件名)
         *                     eg: "templates/pdf_export_demo.ftl"
         * @return
         * @throws FileNotFoundException
         */
        public static String getParent(String relativePath) throws FileNotFoundException {
            return getFile(relativePath).getParent();
        }
    
        /**
         * 获取资源文件名
         *
         * @param relativePath 资源文件相对路径(相对于 resources路径,路径 + 文件名)
         *                     eg: "templates/pdf_export_demo.ftl"
         * @return
         * @throws FileNotFoundException
         */
        public static String getFileName(String relativePath) throws FileNotFoundException {
            return getFile(relativePath).getName();
        }
    
    }
    

    3.2 PDF 生成工具类

    com.ljq.demo.common.util.PDFUtil

    package com.ljq.demo.common.util;
    
    import com.lowagie.text.DocumentException;
    import com.lowagie.text.pdf.BaseFont;
    import freemarker.template.Configuration;
    import freemarker.template.Template;
    import freemarker.template.TemplateException;
    import freemarker.template.TemplateExceptionHandler;
    import org.xhtmlrenderer.pdf.ITextFontResolver;
    import org.xhtmlrenderer.pdf.ITextRenderer;
    
    import java.io.*;
    import java.util.Map;
    
    /**
     * @Description: pdf 导出工具类
     * @Author: junqiang.lu
     * @Date: 2018/12/25
     */
    public class PDFUtil {
    
        private PDFUtil(){}
    
        private volatile static Configuration configuration;
    
        static {
            if (configuration == null) {
                synchronized (PDFUtil.class) {
                    if (configuration == null) {
                        configuration = new Configuration(Configuration.VERSION_2_3_28);
                    }
                }
            }
        }
    
        /**
         * freemarker 引擎渲染 html
         *
         * @param dataMap 传入 html 模板的 Map 数据
         * @param ftlFilePath html 模板文件相对路径(相对于 resources路径,路径 + 文件名)
         *                    eg: "templates/pdf_export_demo.ftl"
         * @return
         */
        public static String freemarkerRender(Map<String, Object> dataMap, String ftlFilePath) {
            Writer out = new StringWriter();
            configuration.setDefaultEncoding("UTF-8");
            configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
            try {
                configuration.setDirectoryForTemplateLoading(new File(ResourceFileUtil.getParent(ftlFilePath)));
                configuration.setLogTemplateExceptions(false);
                configuration.setWrapUncheckedExceptions(true);
                Template template = configuration.getTemplate(ResourceFileUtil.getFileName(ftlFilePath));
                template.process(dataMap, out);
                out.flush();
                return out.toString();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (TemplateException e) {
                e.printStackTrace();
            } finally {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
    
        /**
         * 使用 iText 生成 PDF 文档
         *
         * @param htmlTmpStr html 模板文件字符串
         * @param fontFile 所需字体文件(相对路径+文件名)
         * */
        public static byte[] createPDF(String htmlTmpStr, String fontFile) {
            ByteArrayOutputStream outputStream = null;
            byte[] result = null;
            try {
                outputStream = new ByteArrayOutputStream();
                ITextRenderer renderer = new ITextRenderer();
                renderer.setDocumentFromString(htmlTmpStr);
                ITextFontResolver fontResolver = renderer.getFontResolver();
                // 解决中文支持问题,需要所需字体(ttc)文件
                fontResolver.addFont(ResourceFileUtil.getAbsolutePath(fontFile),BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
                renderer.layout();
                renderer.createPDF(outputStream);
                result=outputStream.toByteArray();
                if(outputStream != null) {
                    outputStream.flush();
                    outputStream.close();
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (DocumentException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return result;
        }
    
    }
    

    3.3 PDF 导出所需资源文件

    PDF 导出需要 freemarker 的渲染模板 ftl 文件

    关于 ftl 模板的一些信息: ftl 模板使用和 html 一致的标签,和 html 不同的是 ftl 模板文件可以添加独有的逻辑元算标签, 如 if-else, for 等,逻辑运算标签具体可参考 freemarker 官方文档:

    https://freemarker.apache.org/docs/ref_directives.html

    本项目示例模板:

    templates/pdf_export_employee_kpi.ftl

    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <title>Title</title>
        <style>
            @page {
                size: 279mm 216mm;
            }
            table{
                margin: auto;
                border: 1px solid #333;
                border-bottom: none;
                border-left: none;
                font-family: SimSun;
            }
            td{
                height: 30px;
                border: 1px solid #333;
                border-top: none;
                text-align: center;
                position: relative;
            }
            tr.title{
                font-weight: bold;
            }
            td.title{
                height: 50px;
                font-weight: bold;
            }
            td.value{
                color: blue;
            }
            td.content{
                font-size: 12px;
                text-align: left;
            }
            td.sign{
                text-align: left;
                height: 40px;
            }
        </style>
    
    </head>
    <body>
    
    <table class="table" cellspacing="0">
    
        <tr >
            <td class="title" colspan="10">
                项目成员绩效考核表
            </td>
        </tr>
    
        <tr >
            <td style="width: 10%;">
                被考核者
            </td>
            <td class="value" style="width: 10%;">
               <#if employee??>
                <#if employee.name??>
                    ${employee.name}
                </#if>
               </#if>
            </td>
            <td style="width: 10%;">
                部门
            </td>
            <td colspan="2" class="value" style="width: 20%;">
                 <#if employeeDepart??>
                    <#if employeeDepart.name??>
                        ${employeeDepart.name}
                    </#if>
                 </#if>
            </td>
            <td style="width: 10%;">
                考核者
            </td>
            <td class="value" style="width: 10%;">
                 <#if acceptUser??>
                    <#if acceptUser.name??>
                        ${acceptUser.name}
                    </#if>
                 </#if>
            </td>
            <td style="width: 10%;">
                考核时间
            </td>
            <td colspan="2" class="value" style="width: 20%;">
                 <#if statisticalTime??>
                     ${statisticalTime}
                 </#if>
            </td>
        </tr>
    
        <tr >
            <td colspan="10">
                第一部分工作目标(权重80%)
            </td>
        </tr>
    
        <tr class="title">
            <td colspan="2">
                指标名称
            </td>
            <td >
                权重
            </td>
            <td colspan="3">
                指标定义与评分标准
            </td>
            <td >
                完成值
            </td>
            <td >
                数据提供部门/人
            </td>
            <td >
                自评得分
            </td>
            <td >
                上级评分
            </td>
        </tr>
    
        <tr >
            <td colspan="2">
                工作计划完成率
            </td>
            <td >
                30%
            </td>
            <td colspan="3" class="content">
                实际完成量/计划完成量*100%<br/>
                1.完成比≥100%,本项为满分;<br/>
                2.完成比在90%(含)-100%(不含),扣10分;<br/>
                3.完成比在80%(含)-90%(不含),扣20分;<br/>
                4.完成比在80%(不含)以下的,本项为0分
            </td>
            <td class="value">
                <#if jobCompletionRate??>
                    ${jobCompletionRate*100}%
                </#if>
            </td>
            <td >
                项目经理
            </td>
            <td class="value">
                <#if jobCompletionRateScore??>
                    ${jobCompletionRateScore}
                </#if>
            </td>
            <td class="value">
                 <#if jobCompletionRateSuperiorScore??>
                     ${jobCompletionRateSuperiorScore}
                 </#if>
            </td>
        </tr>
    
        <tr >
            <td colspan="2">
                工作计划完成及时率
            </td>
            <td >
                25%
            </td>
            <td colspan="3" class="content">
                实际完成天数/计划完成天数*100%<br/>
                1.完成比≦100%,本项为满分;<br/>
                2.完成比在100%-110%(不含),扣5分;<br/>
                3.完成比在110%(含)-130%(不含)扣10分;<br/>
                4.完成比在130%(含)以上的,本项为0分
            </td>
            <td class="value">
                <#if finishRate??>
                    ${finishRate*100}%
                </#if>
            </td>
            <td >
                项目经理
            </td>
            <td class="value">
                <#if finishRateScore??>
                    ${finishRateScore}
                </#if>
            </td>
            <td class="value">
                 <#if finishRateSuperiorScore??>
                     ${finishRateSuperiorScore}
                 </#if>
            </td>
        </tr>
    
        <tr >
            <td colspan="2">
                返工率
            </td>
            <td >
                20%
            </td>
            <td colspan="3" class="content">
                实际返工次数/计划返工次数*100%<br/>
                1.完成比≤100%,本项为满分;<br/>
                2.完成比在100%(不含)-110%(不含),扣10分;<br/>
                3.完成比在110%(不含)-120%(含),扣15分;<br/>
                4.完成比在120%(不含)以上的,本项为0分
            </td>
            <td class="value">
                <#if returnRate??>
                    ${returnRate*100}%
                </#if>
            </td>
            <td >
                项目经理<br/>
                外型供应商
            </td>
            <td class="value">
                <#if returnRateScore??>
                    ${returnRateScore}
                </#if>
            </td>
            <td class="value">
                 <#if returnRateSuperiorScore??>
                     ${returnRateSuperiorScore}
                 </#if>
            </td>
        </tr>
    
        <tr >
            <td colspan="2">
                技术文档资料保存完整性
            </td>
            <td >
                15%
            </td>
            <td colspan="3" class="content">
                实际上传资料数/要求上传资料数*100%<br/>
                1.完成比100%,本项得满分;<br/>
                2.完成比<100%,本项为0分
            </td>
            <td class="value">
                <#if uploadRate??>
                    ${uploadRate*100}%
                </#if>
            </td>
            <td >
                项目经理
            </td>
            <td class="value">
                <#if uploadRateScore??>
                    ${uploadRateScore}
                </#if>
            </td>
            <td class="value">
                 <#if uploadRateSuperiorScore??>
                     ${uploadRateSuperiorScore}
                 </#if>
            </td>
        </tr>
    
        <tr >
            <td colspan="2">
                满意度
            </td>
            <td >
                10%
            </td>
            <td colspan="3" class="content">
                及时参加项目例会、积极汇报工作<br/>
                总分10分<br/>
                每遗漏一次,扣4分;扣完为止
            </td>
            <td >
                --
            </td>
            <td >
                项目经理
            </td>
            <td class="value">
                <#if satisfactionScore??>
                    ${satisfactionScore}
                </#if>
            </td>
            <td class="value">
                 <#if satisfactionSuperiorScore??>
                     ${satisfactionSuperiorScore}
                 </#if>
            </td>
        </tr>
    
        <tr >
            <td colspan="10">
                第二部分工作态度(权重20%)
            </td>
        </tr>
    
        <tr class="title">
            <td colspan="2">
                工作态度指标
            </td>
            <td>
                衡量方法
            </td>
            <td colspan="4">
                衡量标准
            </td>
            <td >
                权重
            </td>
            <td >
                自评得分
            </td>
            <td >
                上级评分
            </td>
        </tr>
    
        <tr >
            <td colspan="2">
                责任心
            </td>
            <td>
                上级评价
            </td>
            <td colspan="4" class="content">
                1.仅仅能按上级要求完成本职工作(5分); <br/>
                2.能够严格按照工作标准完成工作目标对本职工作负责到底,<br/>
                工作中不推卸责任、不上交矛盾,失误较少(10分); <br/>
                3.对待工作不怕繁琐、有耐心,考虑问题与做事细致、周到(15分); <br/>
                4.对待工作精益求精,力求一次性做到完美(20分); <br/>
                5.对团队成员拥有强烈的责任感,努力帮助团队成员提升工作质量(25分)
            </td>
            <td >
                25%
            </td>
            <td class="value">
        		<#if responsibilityScore??>
                    ${responsibilityScore}
                </#if>
            </td>
            <td class="value">
        		<#if responsibilitySuperiorScore??>
                    ${responsibilitySuperiorScore}
                </#if>
            </td>
        </tr>
    
        <tr >
            <td colspan="2">
                主动性
            </td>
            <td>
                上级评价
            </td>
            <td colspan="4" class="content">
                1.按上级安排/ 指示做事,安排什么做什么(5分)<br/>
                2.按自己的职位职责做事,工作任务大多能完成;<br/>
                同时对工作中出现的问题,也能被动反应,予以处理(10分)<br/>
                3.对自己的工作大致有思考,上级安排的任务能有效配合确定工作计划,<br/>
                并按计划完成工作任务;同时能积极处理工作中出现的各种问题,<br/>
                需要请示或上级支持时也能按程序办理(15分)<br/>
                4.提前思考、主动安排自己的工作计划,并将之主动与上级沟通、协商、确定,<br/>
                按计划推进、完成工作任务;对工作问题提前预防,并妥善处理各类问题;<br/>
                能积极主动地协助同事完成职责范围外的其他工作(20分)<br/>
                5.上级只给出一个方向或任务,既能独立地制定计划、组织资源、推进实施、保证完成,<br/>
                支持、鼓励团队成员与周围同事积极主动开展工作,<br/>
                能营造积极、主动的文化氛围(25)
            </td>
            <td >
                25%
            </td>
            <td class="value">
        		<#if initiativeScore??>
                    ${initiativeScore}
                </#if>
            </td>
            <td class="value">
        		<#if initiativeSuperiorScore??>
                    ${initiativeSuperiorScore}
                </#if>
            </td>
        </tr>
    
        <tr >
            <td colspan="2">
                团队合作
            </td>
            <td>
                上级评价
            </td>
            <td colspan="4" class="content">
                1.积极融入团队并乐于接受同事帮助,配合团队完成工作(5分)<br/>
                2.主动给予同事必要的帮助;碰到困难时,善于利用团队的力量解决问题(10分)<br/>
                3.决策前积极发表个人意见,充分参与团队讨论;决策后,个人无论是否有异议,<br/>
                必须从行动上完全予以支持(15分);<br/>
                4.能够客观认识同事的优缺点,并在工作中充分体现“对事不对人”的原则(20分)<br/>
                5.能够以积极正面的心态去影响团队,并改善团队表现和氛围(25分)<br/>
            </td>
            <td >
                25%
            </td>
            <td class="value">
        		<#if teamCooperationScore??>
                    ${teamCooperationScore}
                </#if>
            </td>
            <td class="value">
        		<#if teamCooperationSuperiorScore??>
                    ${teamCooperationSuperiorScore}
                </#if>
            </td>
        </tr>
        <tr >
            <td colspan="2">
                保密意识
            </td>
            <td>
                上级评价
            </td>
            <td colspan="4" class="content">
                1.对岗位的保密责任有一定的认识(5分);<br/>
                2.熟悉公司保密协议,明确职责范围内的保密事项,并采取相应的维护措施(10分)<br/>
                3.以身作则,自觉、严格遵守保密协议,对保密协议未明确界定的问题能够很好的处理(15分)<br/>
                4.影响身边的同事,宣传保密意识,随时提醒同事;发现保密协议的缺陷和漏洞<br/>
                能及时向有关部门报告,并提出完善建议(20分);<br/>
                5.获悉他人违反和破坏保密协议时,积极抵制,能够及时向公司有关部门报告,<br/>
                并分情况采取积极措施以最大限度减少恶性后果,处理得当(25分)
            </td>
            <td >
                25%
            </td>
            <td class="value">
        		<#if secrecyScore??>
                    ${secrecyScore}
                </#if>
            </td>
            <td class="value">
        		<#if secrecySuperiorScore??>
                    ${secrecySuperiorScore}
                </#if>
            </td>
        </tr>
    
        <tr >
            <td colspan="8">
                合计
            </td>
            <td class="value">
        		<#if totalScore??>
                    ${totalScore}
                </#if>
            </td>
            <td class="value">
        		<#if totalSuperiorScore??>
                    ${totalSuperiorScore}
                </#if>
            </td>
        </tr>
    
        <tr >
            <td colspan="2">
                等级评定规则
            </td>
            <td class="content" colspan="5">
                A:优秀(100分以上)<br/>
                B:良好(90-100分)<br/>
                C:合格(80-90分)<br/>
                D:基本合格(70-80分)<br/>
                E:需改进(60-70分)<br/>
                F:不合格(60分以下)
            </td>
            <td >
                等级评定
            </td>
            <td class="value" colspan="2">
                <#if grade??>
                    ${grade}
                </#if>
            </td>
        </tr>
    
        <tr style="height: 100px;" >
            <td colspan="2">
                自我总结
            </td>
            <td colspan="8" class="content value">
        		<#if selfSummary??>
                    ${selfSummary}
                </#if>
            </td>
        </tr>
    
        <tr >
            <td rowspan="2" colspan="2">
                考核结果确认
            </td>
            <td class="sign" colspan="4">
                考核者签名:
            </td>
            <td class="sign" colspan="4">
                日期:
            </td>
        </tr>
        <tr>
            <td class="sign" colspan="4">
                被考核者签名:
            </td>
            <td class="sign" colspan="4">
                日期:
            </td>
        </tr>
    
    </table>
    
    </body>
    </html>
    

    字体文件

    freemarker 如需使用字体,则需要在项目中添加,在包含中文的 pdf 导出中,必须添加字体,否则会出现乱码,本项目示例中添加的为 宋体

    static/font/simsun.ttc

    3.4 配置资源文件路径

    资源文件路径建议使用可配置方式,不推荐在程序中直接写死

    application.yml

    ## pdf export config
    pdfExport:
      fontSimsun: static/font/simsun.ttc
      employeeKpiFtl: templates/pdf_export_employee_kpi.ftl
    

    配置文件类:

    com.ljq.demo.common.config.PDFExportConfig

    package com.ljq.demo.common.config;
    
    import lombok.Data;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * @Description: PDF 文件导出配置文件
     * @Author: junqiang.lu
     * @Date: 2019/1/8
     */
    @Configuration
    @Data
    public class PDFExportConfig {
    
        /**
         * 宋体字体文件相对路径
         */
        @Value("${pdfExport.fontSimsun}")
        private String fontSimsun;
    
        /**
         * 员工绩效考核导出模板文件相对路径
         */
        @Value("${pdfExport.employeeKpiFtl}")
        private String employeeKpiFtl;
    
    }
    

    4 导出流程

    4.1 Service 层

    接口: com.ljq.demo.springboot.service.CommonService

    package com.ljq.demo.springboot.service;
    
    import com.ljq.demo.springboot.vo.DownloadBean;
    import org.springframework.http.ResponseEntity;
    
    /**
     * @Description: 公共业务
     * @Author: junqiang.lu
     * @Date: 2018/12/24
     */
    public interface CommonService {
    
        /**
         * PDF 文件导出
         * @return
         */
        ResponseEntity<?> export();
    
    }
    

    实现类: com.ljq.demo.springboot.service.impl.CommonServiceImpl

    package com.ljq.demo.springboot.service.impl;
    
    import com.ljq.demo.common.config.PDFExportConfig;
    import com.ljq.demo.common.util.FileUtil;
    import com.ljq.demo.common.util.PDFUtil;
    import com.ljq.demo.springboot.service.CommonService;
    import com.ljq.demo.springboot.vo.DownloadBean;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.MediaType;
    import org.springframework.http.ResponseEntity;
    import org.springframework.stereotype.Service;
    
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * @Description: 公共业务具体实现类
     * @Author: junqiang.lu
     * @Date: 2018/12/24
     */
    @Service("commonService")
    public class CommonServiceImpl implements CommonService {
    
        @Autowired
        private PDFExportConfig pdfExportConfig;
    
        /**
         * PDF 文件导出
         *
         * @return
         */
        @Override
        public ResponseEntity<?> export() {
            HttpHeaders headers = new HttpHeaders();
    
            /**
             * 数据导出(PDF 格式)
             */
            Map<String, Object> dataMap = new HashMap<>(16);
            dataMap.put("statisticalTime",new Date().toString());
    
            String htmlStr = PDFUtil.freemarkerRender(dataMap, pdfExportConfig.getEmployeeKpiFtl());
            byte[] pdfBytes = PDFUtil.createPDF(htmlStr, pdfExportConfig.getFontSimsun());
            if (pdfBytes != null && pdfBytes.length > 0) {
                String fileName = System.currentTimeMillis() + (int) (Math.random() * 90000 + 10000) + ".pdf";
                headers.setContentDispositionFormData("attachment", fileName);
                headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
                return new ResponseEntity<byte[]>(pdfBytes, headers, HttpStatus.OK);
            }
    
            headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
            return new ResponseEntity<String>("{ \"code\" : \"404\", \"message\" : \"not found\" }",
                    headers, HttpStatus.NOT_FOUND);
        }
    }
    

    4.2 Controller 层

    com.ljq.demo.springboot.web.controller.CommonController

    package com.ljq.demo.springboot.web.controller;
    
    import com.ljq.demo.common.api.ApiResult;
    import com.ljq.demo.springboot.service.CommonService;
    import com.ljq.demo.springboot.vo.DownloadBean;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.MediaType;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.multipart.MultipartFile;
    
    /**
     * @Description: 公共模块控制中心
     * @Author: junqiang.lu
     * @Date: 2018/12/24
     */
    @RestController
    @RequestMapping("api/demo/common")
    public class CommonController {
    
        private static final Logger logger = LoggerFactory.getLogger(CommonController.class);
    
        @Autowired
        private CommonService commonService;
    
        /**
         * PDF 文件导出
         * 使用 a 链接即可下载;如果为 ajax/vue,则需要转换为 form 表单格式
         * eg: http://${apiAddress}/api/demo/common/export?key1=${key}&key2=${key2}
         */
        @RequestMapping(value = "/export", method = {RequestMethod.POST, RequestMethod.GET},
                produces = MediaType.MULTIPART_FORM_DATA_VALUE)
        public ResponseEntity<?> export(){
            try {
                ResponseEntity<?> responseEntity = commonService.export();
                return responseEntity;
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
            return new ResponseEntity<String>("{ \"code\" : \"404\", \"message\" : \"not found\" }",
                    headers, HttpStatus.NOT_FOUND);
        }
    
    }
    

    4.3 导出效果

    启动项目,访问导出地址: http://127.0.0.1:8088/api/demo/common/export

    本项目中导出的 PDF 文件: resource/1547086678821.pdf

    导出文件参考图:

    pdf-1

    pdf-2

    导出失败返回效果:

    导出失败

    5 注意事项

    PDF 导出看似简单,但是在实际项目应用中仍然存在不少的坑,这里列出一些作者在开发时遇到的问题,仅供参考

    • 5.1 html 的标签必须书写规范,标签必须闭合,如 <br/>,<mate />

    • 5.2 导出的 PDF 文件包含表格,则建议使用百分比来设置宽度,否则可能出现表格右侧内容显示不完整

    • 设置行内元素自动换行,如果不设置,则会导致右侧超出,内容显示不完整;换行示例:

      <tr style="height: 100px;">
            <td class="left" >部门意见</td>
            <td colspan="3">
              <div style="width: 100%;word-wrap:break-word;text-overflow:ellipsis;overflow:hidden;">
                啊啊啊啊啊啊啊啊反反复复反反复复
            </td>
          </tr>
      

      换行属性必须用这一种模式,其他换行规则仍然会导致右侧超出 如使用 style="word-break:break-all;" ,当一段文字没有标点,长度超过页面最大宽度时,仍然会导致表格右侧内容缺失

    • 5.3 样式 style 必须在 <head> </head> 标签中书写,否则不生效

    • 5.4对于空值的校验,Freemarker 在渲染模板时不允许取值为空,如果某一项值可能为空,则必须添加校验,推荐取值方式:

      <#if demoValue??>
          ${demoValue}
      </#if>
      

      Freemarker 逻辑运算可参考官方文档: https://freemarker.apache.org/docs/ref_directives.html

    • 5.5 不能直接判断二级属性是否为空。在实际开发过程中,可能一个属性是一个对象,该对象中又包含其他属性,对象包含的属性为二级属性,在校验非空时,不能直接校验二级属性,应该先校验一级属性,再校验二级属性,具体示例:

      <#if employee??>  
          <#if employee.name??>
              ${employee.name}
          </#if>
      </#if>
      
    • 5.6 导出PDF页面大小的设置。windows 与 linux 默认大小有区别

      可参考: freemarker生成pdf,linux环境下A4纸的大小为216x279(美国标准?) ,Windows下为210X290

    • 5.7 PDF 导出所需的模板以及字体文件不能放在 jar 内,否则会找不到文件;可以采用的两种解决方案: (1)将资源文件放在需要部署的 web 模块内,和 spring boot 的 application.yml 放一起; (2) 放在服务器中的固定位置,在资源路径配置中使用绝对路径

    6 Github 源码

    github 仓库源码: https://github.com/Flying9001/springBootDemo

    个人公众号:404Code,分享半个互联网人的技术与思考,感兴趣的可以关注.
    404Code

    展开全文
  • npm install html2canvas jspdf --save 在项目工具方法存放文件夹utils中创建htmlToPdf.js...// 导出页面为PDF格式 import html2Canvas from 'html2canvas'; import JsPDF from 'jspdf'; export default { ...
  • jsPdf文件,压缩包中有jspdf.debug.js和html2canvas.js文件,如果感觉麻烦,请到官网下载:https://parall.ax/products/jspdf,填写相关信息即可完成下载
  • JAVA使用wkhtmltopdf 将Html 导出PDF

    千次阅读 2018-08-03 16:15:17
    1.安装wkhtmltopdf  ... 根据自己的系统来选择对应的安装包 ...命令:wkhtmltopdf 需要转化为pdfhtml地址 导出位置   4.下面附上JAVA代码 主要工具类: public class HtmlToPdf { //...
  • 每页一个DIV,加上样式page-break-inside:....pdfpage{page-break-inside:avoid;} <div class="pdfpage">1</div> <div class="pdfpage">2</div> <div class="pdfpage">3</d...
  • 纯js实现页面html导出pdf(一)

    千次阅读 2018-04-27 15:54:44
    页面导出pdf,因为我的页面是动态生成且样式是不固定的,故采用这种方式将html页面导出pdf格式,可以实现分页 function $exportReportTemplet() {  // 将 id 为preview_container 的 div 渲染成 canvas ...
  • PD4ML 实测Java将HTML导出PDF 成功解决中文乱码问题
  • swagger导出pdfhtml

    2018-11-14 16:23:33
    swagger导出pdfhtml
  • 博文链接:https://294676001.iteye.com/blog/1622311
  • 您可以将子页面导出HTML,然后使用此工具将其转换为 PDF。 下载 从发布页面获取适用于 Windows 和 Mac 的最新下载: 用法 下载工具并解压。 在,将您希望导出的所有页面放在一个页面中,然后单击“导出”,...
  • npm installhtml2canvas --save npm install jsPDF --save main.js文件 import html2canvas from 'html2canvas' import jsPDFfrom 'jsPDF' Vue.prototype.html2canvas = html2canvas Vue.prototype....
  • var targetDom = $('#qyjxpjzdContent'); //获取要导出的dom /*如果你的页面中有svg,请开启以下功能 因为html2canvas 不能完全识别svg或者不识别svg中部分元素的属性,如:entirety.html页面中的filt...
  • 重点注意:导出HTML须要在导出的HTML中对中文文本直接指定font-faimly,否者HTML导出pdf仍会中文乱码 html转pdf代码 若是以上测试代码经过了,就能够编写正式的导出代码。 html方法定义: html(HTMLelement|...
  • itext导出HTMLPDF的换行和解决中文包(配套三个core-renderer.jar,itext-2.0.8.jar,iTextAsian.jar)

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 34,788
精华内容 13,915
关键字:

html导出pdf