html导出pdf的四种方式

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

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

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

方式 优点 缺点 分页 图片 表格 链接 中文 特殊字符、样式 导出样例 备注
jsPDF 1、整个过程在客户端执行(不需要服务器参与),调用简单 1、生成的pdf为图片形式,且内容失真 支持 支持 支持 不支持 支持 支持
iText 1、功能基本可以实现,比较灵活2、生成pdf质量较高 1、对html标签严;格,少一个结束标签就会报错;2、后端实现复杂,服务器需要安装字体;3、图片渲染比较复杂(暂时还没解决) 支持 支持 支持 支持 支持 支持
wkhtmltopdf 1、调用方式简单(只需执行一行脚本);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

时间: 2024-10-06 05:25:59

html导出pdf的四种方式的相关文章

vue项目中导出PDF的两种方式

参考大家导出的方式,基本上是如下两种: 1.使用 html2Canvas + jsPDF 导出PDF, 这种方式什么都好,就是下载的pdf太模糊了.对要求好的pdf这种方式真是不行啊! 2.调用浏览器自身的方法.window.print() 来打印(打印时可选下载),这种方式打印出来很清楚,但纯在浏览器兼容问题. 谷歌浏览器比较好用点. 两种导出pdf清晰度对比: --------------左边 html2canvas + jspdf:-----------------------------

Java报表工具FineReport导出EXCEL的四种方式

在实际的应用中会经常需要将数据导出成excel,导出的方式除原样导出还有分页导出.分页分sheet导出和大数据量导出.对于excel2003版,限制了每个sheet的最大行数和列数,大数据量导出时会默认时分多个sheet,而excel2007不会出现这样的问题.这些导出方式在JAVA程序中分别有不同的接口来实现: 1.  原样导出 原样导出就是不预览直接导出excel 其程序接口代码如下: outputStream = new FileOutputStream(new File("E:\\Exc

Skype for business/Lync之证书解析(四)证书申请的四种方式

有四种方式进行证书申请,这四种方式不仅适合skype/lync,也适合任何证书申请场景(除了第一种方式): 第一种方式:在SFB/LYNC安装界面中用证书向导自动生成与分配证书,操作最简单,但生成的证书导出时不能导出私钥. 第二种方式:通过MMC,参见本系列之http://huoxian.blog.51cto.com/9437529/1680132 第三种方式:通过web 通过web申请需要使用ssl加密连接,即采取https://ca/certsrv方式,默认没有https,请增加https连

程序员初学机器学习的四种方式

http://blog.jobbole.com/67621/ 本文由 伯乐在线 - XiaoxiaoLi 翻译.未经许可,禁止转载!英文出处:Jason Brownlee.欢迎加入翻译组. 学习机器学习有很多方法,大多数人选择从理论开始. 如果你是个程序员,那么你已经掌握了把问题拆分成相应组成部分及设计小项目原型的能力,这些能力能帮助你学习新的技术.类库和方法.这些对任何一个职业程序员来说都是重要的能力,现在它们也能用在初学机器学习上. 要想有效地学习机器学习你必须学习相关理论,但是你可以利用你

.NET导出Excel的四种方法及评测

.NET导出Excel的四种方法及评测 导出Excel是.NET的常见需求,开源社区.市场上,都提供了不少各式各样的Excel操作相关包.本文,我将使用NPOI.EPPlus.OpenXML.Aspose.Cells四个市面上常见的库,各完成一个导出Excel示例.然后对其代码风格和性能做一个横向比较.最后我将说出我自己的感想. 文中所有的示例代码可以在这里下载: https://github.com/sdcb/blog-data/tree/master/2019/20190824-dotnet

Android——数据存储(四种方式之一)SharedPrefereces

Android--数据存储(四种方式) 1.SharedPrefereces   轻量级.XML  存储文件名,数据保存在data/data/basepackage/shared_prefs/myopt.xml中   实例-收藏-记住密码自动登录 //一种轻量级的数据存储方式//通过KEY 存入数据--putxxxx(key,value) 取出数据--getxxxx(key  default)   2.读写SD卡  SD的根目录  适用于数据流读写 3.SQLite  轻量级.dp文件多用于手机

JAVA中集合输出的四种方式

在JAVA中Collection输出有四种方式,分别如下: 一) Iterator输出. 该方式适用于Collection的所有子类. public class Hello { public static void main(String[] args) throws Exception { Set<Person> javaProgramers = new HashSet<Person>(); javaProgramers.add(new Person("aaron&qu

struts2访问国际化消息的四种方式

Struts2的国际化是建立在java国际化的基础之上的,因此具有强大的国际互能力.Struts2运行时自动检测当前的 Location,然后使用RsourceBundle加载对应的Locale资源文件.因为Struts2对java的国际化进行了封装,因此国际化起来更简单,用户一般提供不同国家的消息资源即可.在Struts2的国际化包含三个部分:前台的国际化,Action中的国际化,验证配置文件的国际化. 在Struts2中加载全局资源文件 国际化的前提是如何让Struts2能够加载到国际化消息

Android中多线程的使用四种方式最全总结

当我们启动一个App的时候,Android系统会启动一个Linux Process,该Process包含一个Thread,称为UI Thread或Main Thread.通常一个应用的所有组件都运行在这一个Process中,当然,你可以通过修改四大组件在Manifest.xml中的代码块(<activity><service><provider><receiver>)中的android:process属性指定其运行在不同的process中.当一个组件在启动的