Skip to content

Commit c54ddf2

Browse files
committed
Add corsImg option
Merged @eachnawzw's PR](tsayen#329) by hand. Bumped versions of Chai, ESLint, Prettier, and SemVer. Updated control image to match new Chrome version.
1 parent 4d5fab9 commit c54ddf2

File tree

7 files changed

+252
-172
lines changed

7 files changed

+252
-172
lines changed

README.md

+7-4
Original file line numberDiff line numberDiff line change
@@ -195,17 +195,20 @@ Are you facing a [CORS policy](https://developer.mozilla.org/en-US/docs/Web/HTTP
195195
issue in your app? Don't worry, there are alternative solutions to this problem that you
196196
can explore. Here are some options to consider:
197197

198-
1. **Use third-party services like [allOrigins](https://allorigins.win/).** With this
198+
1. **Use the option.corsImg support by passing images** With this option, you can setup
199+
a proxy service that will process the requests in a safe CORS context.
200+
201+
2. **Use third-party services like [allOrigins](https://allorigins.win/).** With this
199202
service, you can fetch the source code or an image in base64 format from any website.
200203
However, this method can be a bit slow.
201204

202-
1. **Set up your own API service.** Compared to third-party services like
205+
3. **Set up your own API service.** Compared to third-party services like
203206
[allOrigins](https://allorigins.win/), this method can be faster, but you'll need to
204207
convert the image URL to base64 format. You can use the
205208
"[image-to-base64](https://github.com/renanbastos93/image-to-base64)" package for this
206209
purpose.
207210

208-
1. **Utilize
211+
4. **Utilize
209212
[server-side functions](https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props)
210213
features of frameworks like [Next.js](https://nextjs.org/).** This is the easiest and
211214
most convenient method, where you can directly fetch a URL source within
@@ -327,7 +330,7 @@ Klimas (fixes), Edgardo Di Gesto (fixes), 樊冬 Fan Dong (fixes), Shrijan Tripa
327330
SNDST00M (optimize), Joseph White (performance CSS), Phani Rithvij (test), David
328331
DOLCIMASCOLO (packaging), Zee (ZM) @zm-cttae (many major updates), Joshua Walsh
329332
@JoshuaWalsh (Firefox issues), Emre Coban @emrecoban (documentation), Nate Stuyvesant
330-
@nstuyvesant (fixes)
333+
@nstuyvesant (fixes), King Wang @eachmawzw (CORS image proxy)
331334

332335
## License
333336

dist/dom-to-image-more.min.js

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/dom-to-image-more.min.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package-lock.json

+179-155
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
{
22
"name": "dom-to-image-more",
3-
"version": "3.1.6",
3+
"version": "3.2.0",
44
"description": "Generates an image from a DOM node using HTML5 canvas and SVG",
55
"main": "dist/dom-to-image-more.min.js",
66
"devDependencies": {
7-
"chai": "^4.3.7",
8-
"eslint": "^8.39.0",
7+
"chai": "^4.3.10",
8+
"eslint": "^8.50.0",
99
"grunt": "^1.6.1",
1010
"grunt-cli": "^1.4.3",
1111
"grunt-contrib-jshint": "^3.2.0",
@@ -20,8 +20,8 @@
2020
"karma-mocha": "^2.0.1",
2121
"karma-mocha-reporter": "^2.2.5",
2222
"mocha": "^10.2.0",
23-
"prettier": "^2.8.8",
24-
"semver": "^7.5.0"
23+
"prettier": "^3.0.3",
24+
"semver": "^7.5.4"
2525
},
2626
"scripts": {
2727
"format": "eslint src --fix && prettier --write .",
@@ -61,7 +61,8 @@
6161
"Andoni Zubimendi @AndoniZubimendi",
6262
"Joshua Walsh @JoshuaWalsh",
6363
"Emre Coban @emrecoban",
64-
"Nate Stuyvesant @nstuyvesant"
64+
"Nate Stuyvesant @nstuyvesant",
65+
"King Wang @eachmawzw"
6566
],
6667
"license": "MIT",
6768
"bugs": {

spec/resources/defaultStyles/control-image

+1-1
Large diffs are not rendered by default.

src/dom-to-image-more.js

+55-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
httpTimeout: 30000,
2121
// Style computation cache tag rules (options are strict, relaxed)
2222
styleCaching: 'strict',
23+
// Default cors config is to request the image address directly
24+
corsImg: undefined,
2325
};
2426

2527
const domtoimage = {
@@ -76,6 +78,11 @@
7678
* @param {Boolean} options.cacheBust - set to true to cache bust by appending the time to the request url
7779
* @param {String} options.styleCaching - set to 'strict', 'relaxed' to select style caching rules
7880
* @param {Boolean} options.copyDefaultStyles - set to false to disable use of default styles of elements
81+
* @param {Object} options.corsImg - When the image is restricted by the server from cross-domain requests, the proxy address is passed in to get the image
82+
* - @param {String} url - eg: https://cors-anywhere.herokuapp.com/
83+
* - @param {Enumerator} method - get, post
84+
* - @param {Object} headers - eg: { "Content-Type", "application/json;charset=UTF-8" }
85+
* - @param {Object} data - post payload
7986
* @return {Promise} - A promise that is fulfilled with a SVG image data URL
8087
* */
8188
function toSvg(node, options) {
@@ -259,6 +266,12 @@
259266
domtoimage.impl.options.cacheBust = options.cacheBust;
260267
}
261268

269+
if (typeof(options.corsImg) === 'undefined') {
270+
domtoimage.impl.options.corsImg = defaultOptions.corsImg;
271+
} else {
272+
domtoimage.impl.options.corsImg = options.corsImg;
273+
}
274+
262275
if (typeof options.useCredentials === 'undefined') {
263276
domtoimage.impl.options.useCredentials = defaultOptions.useCredentials;
264277
} else {
@@ -790,8 +803,38 @@
790803
if (domtoimage.impl.options.useCredentials) {
791804
request.withCredentials = true;
792805
}
793-
request.open('GET', url, true);
794-
request.send();
806+
807+
if (domtoimage.impl.options.corsImg
808+
&& url.indexOf('http') === 0
809+
&& url.indexOf(window.location.origin) === -1) {
810+
const method = (domtoimage.impl.options.corsImg.method || 'GET').toUpperCase() === 'POST'
811+
? 'POST'
812+
: 'GET';
813+
814+
request.open(method, (domtoimage.impl.options.corsImg.url || '').replace('#{cors}', url), true);
815+
816+
let isJson = false;
817+
const headers = domtoimage.impl.options.corsImg.headers || {};
818+
Object.keys(headers).forEach(function (key) {
819+
if (headers[key].indexOf('application/json') !== -1) {
820+
isJson = true;
821+
}
822+
request.setRequestHeader(key, headers[key]);
823+
});
824+
825+
const corsData = handleJson(domtoimage.impl.options.corsImg.data || '');
826+
827+
Object.keys(corsData).forEach(function (key) {
828+
if (typeof(corsData[key]) === 'string') {
829+
corsData[key] = corsData[key].replace('#{cors}', url);
830+
}
831+
});
832+
833+
request.send(isJson ? JSON.stringify(corsData) : corsData);
834+
} else {
835+
request.open('GET', url, true);
836+
request.send();
837+
}
795838

796839
let placeholder;
797840
if (domtoimage.impl.options.imagePlaceholder) {
@@ -806,7 +849,7 @@
806849
return;
807850
}
808851

809-
if (request.status !== 200) {
852+
if (request.status >= 300) {
810853
if (placeholder) {
811854
resolve(placeholder);
812855
} else {
@@ -835,6 +878,15 @@
835878
}
836879
}
837880

881+
function handleJson(data) {
882+
try {
883+
return JSON.parse(JSON.stringify(data));
884+
} catch (e) {
885+
fail('corsImg.data is missing or invalid');
886+
return;
887+
}
888+
}
889+
838890
function fail(message) {
839891
console.error(message);
840892
resolve('');

0 commit comments

Comments
 (0)