index.html 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. <!DOCTYPE html>
  2. <meta http-equiv="content-type" content="text/html; charset=UTF8">
  3. <title>saveSvgAsPng</title>
  4. <link href='https://fonts.googleapis.com/css?family=Open+Sans:400italic,400,300,600' rel='stylesheet' type='text/css' />
  5. <link rel=stylesheet href=bootstrap.min.css />
  6. <style>
  7. @font-face {
  8. font-family: 'Stalemate';
  9. font-style: normal;
  10. font-weight: 400;
  11. src: url(stalemate.ttf) format('truetype');
  12. }
  13. input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button {
  14. padding: 15px;
  15. }
  16. h2, h3 {
  17. margin-top: 0;
  18. }
  19. h3 .btn {
  20. margin-top: -8px;
  21. }
  22. ul {
  23. list-style-type: none;
  24. padding: 0;
  25. }
  26. ul li {
  27. padding: 30px 20px;
  28. border-bottom: 1px dashed gray;
  29. }
  30. svg, img {
  31. border: 1px solid lightgray;
  32. }
  33. textarea {
  34. width: 100%;
  35. height: 100px;
  36. }
  37. .error {
  38. border: 1px solid red;
  39. border-radius: 10px;
  40. color: red;
  41. padding: 8px 10px;
  42. }
  43. #sized-with-css svg {
  44. width: 200px;
  45. height: 200px;
  46. }
  47. #selectors-prefixed svg rect {
  48. fill: blue;
  49. }
  50. rect.css-styled {
  51. fill: green !important;
  52. }
  53. #selectors-prefixed rect.css-styled {
  54. fill: green !important;
  55. }
  56. /* Invalid selectors */
  57. [ng\:cloak] {
  58. display: block;
  59. }
  60. ng\:form {
  61. display: block;
  62. }
  63. </style>
  64. <script type=text/template id=inline-template>
  65. <div class=row>
  66. <div class=col-md-6>
  67. <h2></h2>
  68. </div>
  69. <div class=col-md-6>
  70. <h3>Preview <button class="save btn">Save as PNG</button></h3>
  71. </div>
  72. </div>
  73. <div class=row>
  74. <div class="canvas col-md-6">
  75. </div>
  76. <div class=col-md-6>
  77. <div class=preview></div>
  78. </div>
  79. </div>
  80. </script>
  81. <div class=container>
  82. <!-- Needed to trigger correct custom font rasterization in Chrome -->
  83. <span style='font-family: "Stalemate";color:white'>A</span>
  84. <h1>saveSvgAsPng</h1>
  85. <p>This page tests various features of saveSvgAsPng.</p>
  86. <p>You can test your own SVG code in the Sandbox. If something doesn't work as expected, you can <a href="https://github.com/exupero/saveSvgAsPng/issues">file an issue on GitHub</a>.</p>
  87. <ul>
  88. <li id=sandbox>
  89. <h2>Sandbox</h2>
  90. <p>Paste you SVG below to see how it renders.</p>
  91. <textarea><svg></svg></textarea>
  92. <br/>
  93. <button class="render btn">Preview</button>
  94. <div class=load-target style="margin-top:20px;"></div>
  95. <br/>
  96. <h3>Preview <button class="save btn">Save as PNG</button></h3>
  97. <span class=error style="display:none;"></span>
  98. <div class=preview></div>
  99. </li>
  100. <li id=filereader>
  101. <div class=row>
  102. <div class=col-md-6>
  103. <h2>Load from your hard drive</h2>
  104. </div>
  105. <div class=col-md-6>
  106. <h3>Preview <button class="save btn">Save as PNG</button></h3>
  107. </div>
  108. </div>
  109. <div class=row>
  110. <div class=col-md-6>
  111. <input type=file id=file name="files[]" />
  112. <div class=load-target></div>
  113. </div>
  114. <div class=col-md-6>
  115. <div class=preview>No file selected.</div>
  116. </div>
  117. </div>
  118. </li>
  119. <li id=inline>
  120. <svg width=200 height=200>
  121. <rect x=50 y=50 width=100 height=100></rect>
  122. </svg>
  123. </li>
  124. <li id=embedded-png>
  125. <svg width=200 height=200>
  126. <image xlink:href=image.png x=50 y=50 width=100 height=100></image>
  127. </svg>
  128. </li>
  129. <li id=embedded-svg>
  130. <svg width=200 height=200>
  131. <image xlink:href=test.svg x=50 y=50 width=100 height=100></image>
  132. </svg>
  133. </li>
  134. <li id=sized-with-pixels>
  135. <svg width="200px" height="200px">
  136. <rect x=50 y=50 width=100 height=100></rect>
  137. </svg>
  138. </li>
  139. <li id=sized-with-style>
  140. <svg style="width:200px;height:200px;">
  141. <rect x=50 y=50 width=100 height=100></rect>
  142. </svg>
  143. </li>
  144. <li id=sized-with-css>
  145. <svg>
  146. <rect x=50 y=50 width=100 height=100></rect>
  147. </svg>
  148. </li>
  149. <li id=scaling>
  150. <svg width=200 height=200>
  151. <rect width=100 height=100></rect>
  152. <image xlink:href=image.png x=50 y=50 width=100 height=100></image>
  153. </svg>
  154. </li>
  155. <li id=selectors-prefixed>
  156. <svg width=200 height=200>
  157. <rect x=0 y=50 width=100 height=100></rect>
  158. <rect class=css-styled x=100 y=50 width=100 height=100></rect>
  159. </svg>
  160. </li>
  161. <li id=modified-style>
  162. <svg width=200 height=200>
  163. <rect x=0 y=50 width=100 height=100></rect>
  164. <rect class=css-styled x=100 y=50 width=100 height=100></rect>
  165. </svg>
  166. </li>
  167. <li id=modified-css>
  168. <svg width=200 height=200>
  169. <rect x=0 y=50 width=100 height=100></rect>
  170. <rect class=css-styled x=100 y=50 width=100 height=100></rect>
  171. </svg>
  172. </li>
  173. <li id=group>
  174. <svg width=200 height=200>
  175. <g id=sub-group transform="translate(40,40)">
  176. <rect x=10 y=10 width=100 height=100></rect>
  177. </g>
  178. </svg>
  179. </li>
  180. <li id=percentage-size>
  181. <svg width="100%" height="100%">
  182. <rect x=25 y=25 width=100 height=100></rect>
  183. </svg>
  184. </li>
  185. <li id=background-color>
  186. <svg width=200 height=200>
  187. <g id=sub-group transform="translate(40,40)">
  188. <rect x=10 y=10 width=100 height=100></rect>
  189. </g>
  190. </svg>
  191. </li>
  192. <li id=pan-and-zoom>
  193. <svg width=200 height=200>
  194. <g transform="scale(2)">
  195. <rect x=10 y=10 width=100 height=100></rect>
  196. </g>
  197. </svg>
  198. </li>
  199. <li id=unicode>
  200. <svg width=200 height=200>
  201. <text x=100 y=100 text-anchor=middle dy=14>ö,i,ç,ğ</text>
  202. </svg>
  203. </li>
  204. <li id=gradient>
  205. <svg width=200 height=200>
  206. <defs>
  207. <linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
  208. <stop offset="0%" style="stop-color:rgb(255,0,255);stop-opacity:1" />
  209. <stop offset="100%" style="stop-color:rgb(0,255,255);stop-opacity:1" />
  210. </linearGradient>
  211. </defs>
  212. <line x2="200" y2="200" stroke="url(#grad1)" stroke-width="5px" />
  213. </svg>
  214. </li>
  215. <li id=foreign-object>
  216. <svg width=200 height=200>
  217. <foreignobject x=50 y=50 width=50 height=100>
  218. <div>Foreign Object</div>
  219. </foreignobject>
  220. </svg>
  221. </li>
  222. <li id=xmlns-override>
  223. <svg width=200 height=200>
  224. <foreignobject x=50 y=50 width=50 height=100>
  225. <div xml:xmlns="http://www.w3.org/2000/xmlns/">Foreign Object</div>
  226. </foreignobject>
  227. </svg>
  228. </li>
  229. <li id=opacity>
  230. <svg width=200 height=200>
  231. <rect x=50 y=50 width=100 height=100 fill="green"></rect>
  232. <rect x=60 y=60 width=100 height=100 fill="blue" opacity="0.5"></rect>
  233. </svg>
  234. </li>
  235. <li id=entities>
  236. <svg width=200 height=200>
  237. <text x=50 y=100>&quot;&amp;&cent;&nbsp;&pound;&raquo;&frac14;&Ccedil;</text>
  238. </svg>
  239. </li>
  240. <li id=transformed-text>
  241. <svg width=200 height=200>
  242. <text transform="translate(100,100)rotate(45)">Hello</text>
  243. </svg>
  244. </li>
  245. <li id=custom-font>
  246. <svg width=200 height=200>
  247. <text x=100 y=100 text-anchor=middle dy=14 style="font-family:'Stalemate';font-size:36pt;">Custom Fonts</text>
  248. </svg>
  249. <div style="color:red;">
  250. <p>
  251. Custom fonts are supported but in a very rudimentary way. Note: if you don't see the demo working,
  252. click "Save as PNG" - it should work.
  253. </p>
  254. <p>Make sure that the custom font is applied to a non-svg element first. This will help browser to rasterize SVG correctly onto canvas.</p>
  255. <p>@font-face declaration has to be inside document stylesheets (not in the external `link` tag)</p>
  256. <p>Only first `url()` is inlined into svg (don't have multiple urls in the font-face).</p>
  257. </div>
  258. </li>
  259. </ul>
  260. </div>
  261. <script src=https://code.jquery.com/jquery-latest.js></script>
  262. <script src=saveSvgAsPng.js></script>
  263. <script>
  264. function handleFileSelect(evt) {
  265. var $el = $('#filereader');
  266. var files = evt.target.files;
  267. for (var i = 0, f; f = files[i]; i++) {
  268. var reader = new FileReader();
  269. reader.onload = (function(file) {
  270. return function(e) {
  271. $el.find('.load-target').html(e.target.result);
  272. svgAsPngUri($el.find('.load-target svg')[0], null, function(uri) {
  273. $el.find('input').hide()
  274. $el.find('.preview').html('<img src="' + uri + '" />');
  275. });
  276. $el.find('.save').click(function() {
  277. saveSvgAsPng($el.find('.load-target svg')[0], 'test.png');
  278. });
  279. }
  280. })(f);
  281. reader.readAsText(f);
  282. }
  283. }
  284. if (window.File && window.FileReader && window.FileList && window.Blob) {
  285. document.getElementById('file').addEventListener('change', handleFileSelect, false);
  286. }
  287. function inlineTest(title, $el, saveOptions, testOptions) {
  288. var svg = $el.html();
  289. var template = $('#inline-template').html();
  290. var row = $el.html(template);
  291. row.find('h2').text(title);
  292. row.find('.canvas').html(svg);
  293. var canvas = row.find(testOptions && testOptions.selector || 'svg')[0];
  294. svgAsPngUri(canvas, saveOptions, function(uri) {
  295. row.find('.preview').html('<img src="' + uri + '" />');
  296. });
  297. row.find('.save').click(function() {
  298. saveSvgAsPng(canvas, 'test.png', saveOptions);
  299. });
  300. }
  301. inlineTest('Directly in the HTML', $('#inline'));
  302. inlineTest('With linked PNG image', $('#embedded-png'));
  303. inlineTest('With linked SVG image', $('#embedded-svg'));
  304. inlineTest('Sized with pixels', $('#sized-with-pixels'));
  305. inlineTest('Sized with style', $('#sized-with-style'));
  306. inlineTest('Sized with CSS', $('#sized-with-css'));
  307. inlineTest('At a higher resolution', $('#scaling'), {scale: 2});
  308. inlineTest('When CSS styling selectors are prefixed', $('#selectors-prefixed'), {
  309. selectorRemap: function(s) {return s.replace('#selectors-prefixed ', '')}
  310. });
  311. inlineTest('Modifying the style', $('#modified-style'), {
  312. modifyStyle: function(s) {return s.replace('green', 'red')}
  313. });
  314. inlineTest('Modifying the whole CSS rule', $('#modified-css'), {
  315. modifyCss: function(selector, properties) {
  316. selector = selector.replace('#selectors-prefixed ', '');
  317. properties = properties.replace('green', 'blue');
  318. return selector + '{' + properties + '}';
  319. }
  320. });
  321. inlineTest('Exporting a group within an SVG', $('#group'), null, {
  322. selector: '#sub-group'
  323. });
  324. inlineTest('Percentage Height and Width', $('#percentage-size'));
  325. inlineTest('Background color', $('#background-color'), {backgroundColor: 'lightblue'});
  326. inlineTest('Pan and Zoom', $('#pan-and-zoom'), {
  327. left: -50,
  328. top: -50,
  329. width: 300,
  330. height: 300
  331. });
  332. inlineTest('With Unicode characters', $('#unicode'));
  333. inlineTest('With gradients', $('#gradient'));
  334. inlineTest('With foreign objects', $('#foreign-object'));
  335. inlineTest('With opacity', $('#opacity'));
  336. inlineTest('When setting xmlns on foreign object children', $('#xmlns-override'));
  337. inlineTest('When using HTML entites', $('#entities'));
  338. inlineTest('Transformed text', $('#transformed-text'));
  339. inlineTest('With custom fonts', $('#custom-font'));
  340. var $sandbox = $('#sandbox');
  341. $sandbox.find('.render').click(function() {
  342. $sandbox.find('.error').hide().text('');
  343. $sandbox.find('.load-target').html($('#sandbox textarea').val());
  344. var canvas = $sandbox.find('.load-target svg')[0];
  345. try {
  346. svgAsPngUri(canvas, null, function(uri) {
  347. $sandbox.find('.preview').html('<img src="' + uri + '" />');
  348. });
  349. $sandbox.find('.save').unbind('click').click(function() {
  350. saveSvgAsPng(canvas, 'test.png');
  351. });
  352. } catch(err) {
  353. $sandbox.find('.error').show().text(err.message);
  354. $sandbox.find('.preview').html('');
  355. }
  356. });
  357. </script>