html匯出pdf的四種方式
阿新 • • 發佈:2018-11-01
將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檔案: