vectorizer.ai

vectorizer.ai free download

  1. // ==UserScript==
  2. // @name vectorizer.ai
  3. // @version 0.0.6
  4. // @description vectorizer.ai free download
  5. // @icon https://d1j8j7mb8gx2ao.cloudfront.net/p/assets/logos/vectorizer-ai-logo_1c2b7a3ae82c1def79ab4c0e9cc83bcc.svg
  6.  
  7. // @author ml98
  8. // @namespace http://tampermonkey.net/
  9. // @license MIT
  10.  
  11. // @match https://vectorizer.ai/images/*
  12. // @grant none
  13. // @run-at document-start
  14. // ==/UserScript==
  15.  
  16. let state = 0;
  17. let data = { width: 1, height: 1, curves: [], gap_fillers: [], style: {}, errors: 0 };
  18.  
  19. // todo: any other way to inject?
  20. Object.defineProperty(Object.prototype, 'isReady', {
  21. get() {
  22. if(this.vectorInterfaces) {
  23. data.gap_fillers = this.vectorInterfaces
  24. .filter(i => i.color0.a > 0 && i.color1.a > 0)
  25. .map(i => ({ d: i.path.s, stroke: i.css }));
  26. create_download_button();
  27. }
  28. return this.__isReady;
  29. },
  30. set(isReady) {
  31. this.__isReady = isReady;
  32. return isReady;
  33. },
  34. configurable: true,
  35. enumerable: false,
  36. });
  37.  
  38.  
  39. function proxy_functions(object, apply = (f, _this, args) => f.apply(_this, args)) {
  40. const descriptors = Object.getOwnPropertyDescriptors(object);
  41. Object.keys(descriptors).filter(
  42. (name) => descriptors[name].value instanceof Function
  43. ).forEach((f) => {
  44. object[f] = new Proxy(object[f], { apply });
  45. });
  46. }
  47.  
  48. function F(strings, ...values) {
  49. return strings[0] + values.map(
  50. (value, i) => value.toFixed(2).replace(/\.?0+$/,'') + strings[i+1]
  51. ).join('');
  52. }
  53.  
  54. function path2svg(name, args) {
  55. switch (name) {
  56. case "moveTo": {
  57. let [x, y] = args;
  58. return F`M ${x} ${y}`;
  59. }
  60. case "lineTo": {
  61. let [x, y] = args;
  62. return F`L ${x} ${y}`;
  63. }
  64. case "ellipse": {
  65. let [cx, cy, rx, ry, rotation, theta1, theta2, ccw] = args;
  66. let dx = rx * Math.cos(theta2), dy = ry * Math.sin(theta2);
  67. let c = Math.cos(rotation), s = Math.sin(rotation);
  68. let ex= cx + c * dx - s * dy, ey = cy + s * dx + c * dy;
  69. let angle = rotation * (180 / Math.PI);
  70. let large_arc = (theta2 - theta1) % (2 * Math.PI) > Math.PI ? 1 : 0;
  71. let sweep = ccw ? 0 : 1;
  72. return F`A ${rx} ${ry} ${angle} ${large_arc} ${sweep} ${ex} ${ey}`;
  73. }
  74. case "bezierCurveTo": {
  75. let [cp1x, cp1y, cp2x, cp2y, x, y] = args;
  76. return F`C ${cp1x} ${cp1y} ${cp2x} ${cp2y} ${x} ${y}`;
  77. }
  78. case "quadraticCurveTo": {
  79. let [cpx, cpy, x, y] = args;
  80. return F`Q ${cpx} ${cpy} ${x} ${y}`;
  81. }
  82. case "closePath": {
  83. return `Z`;
  84. }
  85. default: {
  86. // throw new Error("unimplemented path", name);
  87. return `ERROR`;
  88. }
  89. }
  90. }
  91.  
  92. proxy_functions(Path2D.prototype, (f, _this, args) => {
  93. _this.s ??= "";
  94. // _this.s += `${f.name}[${args.toString()}] `;
  95. _this.s += path2svg(f.name, args);
  96. return f.apply(_this, args);
  97. });
  98.  
  99. proxy_functions(CanvasRenderingContext2D.prototype, (f, _this, args) => {
  100. if(_this.canvas.id) {
  101. // width, height
  102. }
  103. if(!_this.canvas.id) {
  104. switch(f.name) {
  105. case 'drawImage':
  106. if(state == 0) {
  107. data.width = args[3];
  108. data.height = args[4];
  109. state = 1;
  110. }
  111. break;
  112. case 'clip':
  113. if(state == 1) {
  114. // console.log(_this.lineWidth, _this.lineJoin);
  115. data.curves = [];
  116. data.errors = 0;
  117. data.style = { lineWidth: _this.lineWidth, lineJoin: _this.lineJoin };
  118. state = 2;
  119. }
  120. break;
  121. case 'fill':
  122. if(state == 2) {
  123. // console.log(f.name, _this.fillStyle, args[0].s);
  124. if(!args[0].s) {
  125. data.errors++;
  126. console.log('error: incomplete');
  127. break;
  128. // state = 1;
  129. }
  130. data.curves.push({fill: _this.fillStyle, d: args[0].s});
  131. }
  132. break;
  133. case 'stroke':
  134. if(state == 2) {
  135. // console.log(f.name, _this.strokeStyle, args[0].s);
  136. if(!args[0].s) {
  137. data.errors++;
  138. console.log('error: incomplete');
  139. break;
  140. // state = 1;
  141. }
  142. // data.curves.push({stroke: _this.strokeStyle, d: args[0].s});
  143. }
  144. break;
  145. case 'restore':
  146. if(state == 2) {
  147. state = 1;
  148. console.log(data);
  149. update_download_button();
  150. }
  151. break;
  152. }
  153. }
  154. return f.apply(_this, args);
  155. });
  156.  
  157. function svg_element(tag, attributes) {
  158. const element = document.createElementNS('http://www.w3.org/2000/svg', tag);
  159. for (let attr in attributes) {
  160. element.setAttribute(attr, attributes[attr]);
  161. }
  162. return element;
  163. }
  164.  
  165. function create_svg() {
  166. const { width, height, curves, gap_fillers, style } = data;
  167. const svg = svg_element('svg', {
  168. xmlns: 'http://www.w3.org/2000/svg',
  169. viewBox: `0 0 ${width} ${height}`,
  170. width,
  171. height,
  172. });
  173.  
  174. let g = svg_element('g', {
  175. 'stroke-width': 2,
  176. 'fill': 'none',
  177. 'stroke-linecap': 'butt',
  178. });
  179. svg.appendChild(g);
  180.  
  181. gap_fillers.forEach(({d, stroke}) => {
  182. const element = svg_element('path', {d, stroke, 'vector-effect': 'non-scaling-stroke'});
  183. g.appendChild(element);
  184. });
  185.  
  186. curves.forEach(({d, fill, stroke}) => {
  187. if(fill) {
  188. const element = svg_element('path', {d, fill});
  189. svg.appendChild(element);
  190. }
  191. });
  192.  
  193. return svg;
  194. }
  195.  
  196. let a = null;
  197. function create_download_button() {
  198. if(a) return;
  199.  
  200. const button = document.querySelector('#App-DownloadLink');
  201. a = button.cloneNode(true);
  202. a.style.borderRadius = 0;
  203. a.querySelector('.showPaid').style.display = 'inline';
  204. a.querySelector('.showFree').style.display = 'block';
  205. a.target = '_blank';
  206. a.download = 'result.svg';
  207. button.before(a);
  208.  
  209. a.addEventListener('click', function(event) {
  210. const svg = create_svg();
  211. const content = new XMLSerializer().serializeToString(svg);
  212. const type = 'image/svg+xml';
  213. const url = URL.createObjectURL(new Blob([content], {type}));
  214. if(this.href) {
  215. URL.revokeObjectURL(this.href);
  216. }
  217. this.href = url;
  218. });
  219. }
  220.  
  221. function update_download_button() {
  222. if(!a) return;
  223.  
  224. if(data.errors) {
  225. a.querySelector('.showPaid').textContent = `[missing ${data.errors}]`;
  226. a.style.background = '#4876ff80';
  227. } else {
  228. a.querySelector('.showPaid').textContent = '[ok]';
  229. a.style.background = '#4876ff';
  230. }
  231. }