From 5826de5193022e15b3b72637ec84ab725543f38b Mon Sep 17 00:00:00 2001 From: Andrew Bennett Date: Tue, 14 Apr 2020 11:26:02 -0500 Subject: [PATCH] Add support for PDF output. --- README.md | 13 +++++++++--- package.json | 4 ++-- render.js | 39 ++++++++++++++++++++++++++++++------ test/.gitignore | 1 + test/expected/32-64-pad.pdf | Bin 0 -> 1273 bytes test/expected/64-32-pad.pdf | Bin 0 -> 1273 bytes test/expected/offset.pdf | Bin 0 -> 1256 bytes test/expected/simple.pdf | Bin 0 -> 1256 bytes test/test.js | 8 ++++++-- test/test.json | 4 ++++ 10 files changed, 56 insertions(+), 13 deletions(-) create mode 100644 test/expected/32-64-pad.pdf create mode 100644 test/expected/64-32-pad.pdf create mode 100644 test/expected/offset.pdf create mode 100644 test/expected/simple.pdf diff --git a/README.md b/README.md index 5447ca9..e423db1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # svgexport -svgexport is a Node.js module and command-line tool for exporting SVG files to PNG and JPEG, it uses Puppeteer for rendering SVG files. +svgexport is a Node.js module and command-line tool for exporting SVG files to PNG, JPEG, and PDF. + +It uses Puppeteer for rendering SVG files. ### Command Line @@ -16,7 +18,7 @@ svgexport [] [] [] [] [] [] - png|jpeg|jpg + png|jpeg|jpg|pdf If not specified, it will be inferred from output file extension or defaults to "png". 1%-100% @@ -71,6 +73,11 @@ Use a CSS to style input SVG: svgexport input.svg output.jpg "svg{background:silver;}" ``` +Export a PDF: +``` +svgexport input.svg output.pdf +``` + By default, Puppeteer has a page load timeout of 30 seconds. This might not be enough for large SVG files. If you want to change the page timeout, set the `SVGEXPORT_TIMEOUT` environment variable to the desired number of seconds. @@ -104,4 +111,4 @@ svgexport was migrated from PhantomJS to Puppeteer by [Michael Heerklotz](https: Copyright (c) 2016 Ali Shakiba Available under the MIT license -*Keywords: svg, export, rasterize, converter, png, jpeg, jpg, cli, command-line, inkscape, illustrator, coreldraw* +*Keywords: svg, export, rasterize, converter, png, jpeg, jpg, pdf, cli, command-line, inkscape, illustrator, coreldraw* diff --git a/package.json b/package.json index 66a5665..4a09b75 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,8 @@ "url": "https://github.com/shakiba/svgexport.git" }, "dependencies": { - "async": "^3.1.0", - "puppeteer": "^2.0.0" + "async": "^3.2.0", + "puppeteer": "^2.1.1" }, "main": "index.js", "bin": { diff --git a/render.js b/render.js index 7e9e6c2..2e06afd 100644 --- a/render.js +++ b/render.js @@ -114,6 +114,14 @@ async function renderSvg(commands, done, stdout) { svg.style.setProperty('top', 0, 'important'); } + if (clip.x > 0 && clip.y > 0) { + svg.style.setProperty('transform', `translateX(${-clip.x}px) translateY(${-clip.y}px)`, 'important'); + } else if (clip.x > 0) { + svg.style.setProperty('transform', `translateX(${-clip.x}px)`, 'important'); + } else if (clip.y > 0) { + svg.style.setProperty('transform', `translateY(${-clip.y}px)`, 'important'); + } + svg.style.setProperty('width', (input.width * output.scale) + 'px', 'important'); svg.style.setProperty('height', (input.height * output.scale) + 'px', 'important'); @@ -121,9 +129,6 @@ async function renderSvg(commands, done, stdout) { var svgContent = await page.content(); - clip.x = Math.max(clip.x, 0); - clip.y = Math.max(clip.y, 0); - var renderContent = ` @@ -142,8 +147,8 @@ async function renderSvg(commands, done, stdout) { border: 0 !important; padding: 0 !important; position: fixed !important; - left: ${clip.x}px !important; - top: ${clip.y}px !important; + left: 0 !important; + top: 0 !important; width: ${output.width}px !important; height: ${output.height}px !important; " @@ -167,7 +172,18 @@ async function renderSvg(commands, done, stdout) { } var outputEl = await page.$('#svgExportOutput-fa5ce2b6d16510'); - await outputEl.screenshot(renderSettings); + if (output.format === 'pdf') { + await page.emulateMediaType('screen'); + await page.pdf({ + path: imgfile, + displayHeaderFooter: false, + width: `${output.width}px`, + height: `${output.height}px`, + printBackground: false + }); + } else { + await outputEl.screenshot(renderSettings); + } stdout(svgfile + ' ' + imgfile + ' ' + output.toString() + '\n'); @@ -214,6 +230,17 @@ function Command(input, params, outputfile) { } }); + params.first(/^(pdf)$/i, function(match) { + output.format = match[1]; + }, function() { + if (outputfile) { + var ext = /.(pdf)$/.exec(outputfile); + if (ext && ext[1]) { + output.format = ext[1]; + } + } + }); + output.format = output.format.toLowerCase().replace('jpg', 'jpeg'); // output diff --git a/test/.gitignore b/test/.gitignore index 770f078..8c8a8d5 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -1,2 +1,3 @@ +/exported/*.pdf /exported/*.png /exported/*.jpg diff --git a/test/expected/32-64-pad.pdf b/test/expected/32-64-pad.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8f2f73ef6479d6c54e81cabdee1d2b58096267b0 GIT binary patch literal 1273 zcmaJ>O=uKJ6keEsYa@E{peynYhQt-q)jutFjlZio-jWfZ^pjh@)W?HAaLU&CZ zm;EymLC~N^y(l6%i3b(;pak_G2#W41galy)*PmU;dJw^j?jpWwduAYtMNw7#`g^b5 zd*9dHJdn<;)!Iy@dHUttQ$+&;eAjNJqeD%3j0)~SYpUdN$F4Z56?MSlg^I<{nA>Ah z6@9^p6MPYXZSJP2V9=WWPA57creI1c(dk!r(KxP9pxNn$&IzWYBo{On|JOox9FQ&q?tB}{F2Z6Q0Zp5}LYOX{Jwp#=& zIa6^hfmO(Jia@^2Fbt?Kh)ro|)MM>XU(lH54z*p#6VULf7Nd4?KWLb)+Fcb`7vzOk zVIUV7jdQQyW5g)nByzzigM>K+F$@VTb1yecEi5q2Mr?~^d%BW1YQ2))A#EAEj12%a zQ${M%%;E*6s9Ts@6eZ9}G?@fnAhV8AJ;RRnEg2H5?0+422n6T?R9f(HlB zwe&W<|E0Vxc~Y-lz46o3%<&B`Bje9Y?bC0Xe*g2w!{u}S#*TNhkC!}rec|e+&WUZe zN16`h7jAu{PMlb|<3P5f^OW-3zPMs^@v$T8MuuAcS~WU3@vk=SJn7u3URieh+p(!@ zcX(;aiQa!Pv-`Zc{^ZxmrHA(a`s`-pn-$&nCTGUx{`lwF-;+Bdd&eSn+W~*s;jj9i zEI)Dm=SOdUyR|y;;_~4Cyy!;mJe+%cS3P@aM_e`{tSgj{-e^fp8Aa?wmifHmSH}iYs1b!BsM|EmTn=#Swst-7 ziA{@QFXrL21pZ>?+04)25=Q(JTq1!julWj(+GXa+KUL2DfnmmKmU98JUTkWwJH$c$ W%_KvaS(}+ow=H~A2GampJv4nvD literal 0 HcmV?d00001 diff --git a/test/expected/64-32-pad.pdf b/test/expected/64-32-pad.pdf new file mode 100644 index 0000000000000000000000000000000000000000..182c55dba05f1252a9741e0a2e66e12c3bdb98d7 GIT binary patch literal 1273 zcmaJ>O=uHA6t4et(Ta!%q3A;_{�Ln=}y#+N7ybt0Ad~1jXrQnr=;Y>Sm&;dJzBr z5X9euir_`99`vAxD)=v=tp`2%a}dQMq9Ps?Dd?NHNuULXVP^K*_ujnszHhfK7LF{? z>J6pt#ph4&6b%UQwoYZyA~obND!2o)Lmdv!TKVi8MU6Q;l{XoJiH#Oj(HF9Qf-j23 z7PrGxFqj=)s1uzKLo+m==GT4m2$_Ww?OJ&1|0cnp!t9hx!Ba`aq@XEkV+*J&xs#=t zijh$s2g(cDhBO3qUjvjE1j9T_EgO;qG<+(>DBZjgG|X3RDhMnLlETR|kc*7Q zxs%FaL@(eZa=|HsxH%PLc%)&5JBco8VufM0*)mzYxiN^N)GX=k@)of&*a1)@8RR0& zOrBzjx`Nqh(E&O!f+OfP!$4K{gsa_W&JSFtMtJGgaE-|5ylFCVTxa_!Sk z`}C6@`(o#-6IEXi?p{o`yuUNH`RMgFMt_HL-v?%F7`bZiDC^qYhhr~Po%&XN`BwJX z;M1;XP2kFr$D2O-cFezBe{68%@n7q8*$1~QS3Y{BBPC^|u@P~W<9WwqIY}|f*-S72 z$)b|gR#p(8Mzb`{8r?nQ?os3f8Ej4nDR6Xc_J9;wtr*b&DKLcPix!r$Xd_42 zNzEDle|igX8>Jv}AW4fO*Qf?l#HdpQiHs+Y;B(`<1<6O3;z~g|sf|1ub`J7yCK<{J R>WE@x8Zn4cSJ$*E@)uRmf&c&j literal 0 HcmV?d00001 diff --git a/test/expected/offset.pdf b/test/expected/offset.pdf new file mode 100644 index 0000000000000000000000000000000000000000..0453cfd163872cf59e0819543cb407afe4de5f6f GIT binary patch literal 1256 zcmaJ>UuYCZ7)SJJNfpJCC*^zatR=*q{d1Q~2q)aF7lY=!+@$iuidxvbvp#JSme z5rwJ`f<#M;FO{Z`C51pkqcjr#AbC(krCKbEj#89Usy3{1`2I zehH9tI`CbIJk&Zxps?ApENBcEn=%kIV_nb~5OjEjdOj2g82Gf5W9jG&7+A2rwI-1n z6r>0ks6l1pTokLAF$&m;8nCM*VNLZMmI^F$ksqfnQgqn2p38E5$pp5g+futj;nH3i zuK@IP8O3mz%Zp6YcQC&s$H0ik5(%hE)V8A4eh#t=E02=4doREIrDOG@6~}w5*>BFY zO?5aw9Upq|?)Kco@Sdmf_ANIvGppK1{)Hdzj~q(Ax_fYP#hrl#e*GV3-TBLZ>^wV` z+E!X~uH8O-F}iQ{^tw}#+Ti&4>tDS1{`7^Z*K3_ML(AXJA3W*a+J9=eW9HcWj_GT^ zi?-m;=T}RE&;Gmm>&?Yu4|h$z>D_lEW3SqnUwFLi^mnt)MCPw+H{j)!qd!iXJ!Ji* z4U2QdC&8N&`fq>dwkg&jiO?-(lmC(tN=Q4$syq-bt13a!y>6aKDAbLs53!mAy}v?B zEE&$JaPCJ1P_MqcRDQ;-eHJJ`=9G}3v+5bqK_6iCVt^Grn&jv*WifP@Y1cLOQFvJe zB~cEQMeSXgrJ@cJl}{}p7UmBLsmD;zCZVcwLV&^eIc)coa}@8~NYe*ht=m#m(lRfp zV@h}pEvM838`TKJHPUDhi$<*|j6wr5uyWPFZ(v;p9^8T%QM`**tQ-F*Etnm}B3iI+ z^V7KQu8-oRpkA2?^<&C=2N+hY=Qx+3_~NDZ`=cDx&r2y(l~l+&GlQ6x7Kv=#nSKw) CpnU@X literal 0 HcmV?d00001 diff --git a/test/expected/simple.pdf b/test/expected/simple.pdf new file mode 100644 index 0000000000000000000000000000000000000000..433a83cd80c02133304231ace42cb6e2503bce49 GIT binary patch literal 1256 zcmaJ>TZ{}r6!m_zXNV6W+=$(^gs$$M*UB0(GiGhrHJRaqu_!&YvrTu`ZcmMjR|FqI zL_!b|BQt5@- zqL9+?;`68XiUtJumaR%_tD5u}72Jb)$sUh8R@rG*)QraqWs{*Xx6Ps|#)1)@~RdZAi?gS0XRhL%;;)1;J$_(TrvvKYf zN|-SWIEb8Z$Rt5cehx!A+T6?aQWF8D*mlcg*|tO+pK7mUbU<6$vatc6rfsAm#Y|pc zin@xqMbQIVEEbPLNg%V1QaO7vb9>@_GXBliY3GlvFmLs5`SNVSw8iA&RdHd8^J~+$ z{`e4W^2feIPm2%EKfgF}jJ@m5z?@}YdsjCdy;ACVTOPgS&gEInTOS@ieFU~Q-@E&4 z;L@u88Ec16(${_X^2FXdw&|sF=5_zU3mZ2ZjcZ4h`;MFT_t^uP@Xf}d8>YVc`0(e@ z-Qx=vo>| z@oiY^zAi3t2QUJ)NLqsqRE1DP;8|AUWzS?KNhzw=%rOCZU%2WTRuQ1KJG96W!I})# zcH{sVYs(3#XAEsKKO~18Nm=9; z**8UodcKj!d~yqsAb$WzzWxQMB(+dRXZ#&jT2ea_ceZO#3DNqnBo)-=MR`mS+fa8( z#bezHftW&S4@AOYBMjY8vuRkle88>QqVoT#vuR=MMZK*BZ