SVGConvert.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. /**
  2. * @see https://stackoverflow.com/a/28226736
  3. * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas
  4. * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL
  5. *
  6. * @created 25.09.2023
  7. * @author smiley <smiley@chillerlan.net>
  8. * @copyright 2023 smiley
  9. * @license MIT
  10. */
  11. export default class SVGConvert{
  12. /**
  13. * Converts the SVG image from the source element into a raster image with the given size and format
  14. * and inserts the resulting data URI into the src attribute of the given destination image element.
  15. *
  16. * @param {SVGElement|HTMLImageElement} source
  17. * @param {HTMLImageElement} destination
  18. * @param {Number<int>} width
  19. * @param {Number<int>} height
  20. * @param {String} format
  21. * @return {void}
  22. */
  23. static toDataURI(source, destination, width, height, format){
  24. // format is whatever the fuck the specification allows:
  25. // @see https://html.spec.whatwg.org/multipage/canvas.html#dom-canvas-todataurl-dev
  26. format = (format || 'image/png');
  27. if(!destination instanceof HTMLImageElement){
  28. throw new Error('destination is not an instance of HTMLImageElement');
  29. }
  30. if(source instanceof HTMLImageElement){
  31. source = SVGConvert.base64Decode(source);
  32. }
  33. SVGConvert.toCanvas(source, width, height)
  34. .then(canvas => destination.src = canvas.toDataURL(format))
  35. .catch(error => console.log('(╯°□°)╯彡┻━┻ ', error));
  36. }
  37. /**
  38. * Draws the given SVG source on a canvas of the given size and returns the canvas element
  39. *
  40. * @param {SVGElement} source
  41. * @param {Number<int>} width
  42. * @param {Number<int>} height
  43. * @return {Promise<HTMLCanvasElement>}
  44. */
  45. static toCanvas(source, width, height){
  46. return new Promise((resolve, reject) => {
  47. if(!source instanceof SVGElement){
  48. throw new Error('source is not an instance of SVGElement');
  49. }
  50. // the source SVG element needs to be visible
  51. source.style.display = 'inline-block';
  52. source.style.visibility = 'visible';
  53. // add/fix the width/height of the SVG element
  54. // @see https://bugzilla.mozilla.org/show_bug.cgi?id=700533#c39
  55. source.width.baseVal.valueAsString = width;
  56. source.height.baseVal.valueAsString = height;
  57. try{
  58. // stringify the SVG element and create an object URL for it
  59. let svgString = new XMLSerializer().serializeToString(source);
  60. let svgBlob = new Blob([svgString], {type: 'image/svg+xml;charset=utf-8'});
  61. let tempUrl = URL.createObjectURL(svgBlob);
  62. // create a temporary image
  63. let tempImg = new Image(width, height);
  64. // trigger the onLoad event with the temporary blob URL
  65. tempImg.src = tempUrl;
  66. // process the conversion in the onLoad event
  67. tempImg.onload = function(){
  68. // create a canvas element to draw the SVG on
  69. let canvas = document.createElement('canvas');
  70. canvas.width = width;
  71. canvas.height = height;
  72. canvas.getContext('2d').drawImage(tempImg, 0, 0);
  73. // revoke the temporary blob
  74. URL.revokeObjectURL(tempUrl);
  75. // return the PNG image as data URI
  76. resolve(canvas);
  77. };
  78. }
  79. catch(error){
  80. reject(error.message);
  81. }
  82. });
  83. }
  84. /**
  85. * Converts the given SVG base64 data URI into a DOM element
  86. *
  87. * @param {HTMLImageElement} image
  88. * @return {SVGElement}
  89. */
  90. static base64Decode(image){
  91. if(!image instanceof HTMLImageElement){
  92. throw new Error('image is not an instance of HTMLImageElement');
  93. }
  94. if(!image.src || !image.src.includes('base64,')){
  95. throw new Error('invalid image source');
  96. }
  97. return new DOMParser().parseFromString(atob(image.src.split(',')[1]), 'image/svg+xml').firstChild;
  98. }
  99. }