1- 'use strict' ;
1+ const fs = require ( 'fs' )
2+ const get = require ( 'simple-get' )
3+ const webp = require ( '@cwasm/webp' )
24
3- /*!
4- * Canvas - Image
5- * Copyright (c) 2010 LearnBoost <tj@learnboost.com>
6- * MIT Licensed
5+ const bindings = require ( './bindings' )
6+
7+ /** @typedef {Object } Image */
8+ const Image = module . exports = bindings . Image
9+
10+ const proto = Image . prototype
11+ const _getSource = proto . getSource
12+ const _setSource = proto . setSource
13+
14+ delete proto . getSource
15+ delete proto . setSource
16+
17+ /**
18+ * @param {Image } image
19+ * @param {Error } err
720 */
21+ function signalError ( image , err ) {
22+ if ( typeof image . onerror === 'function' ) return image . onerror ( err )
23+
24+ throw err
25+ }
826
927/**
10- * Module dependencies.
28+ * @param {Image } image
29+ * @param {string } value
1130 */
31+ function loadDataUrl ( image , value ) {
32+ const firstComma = value . indexOf ( ',' )
33+ const isBase64 = value . lastIndexOf ( 'base64' , firstComma ) !== - 1
34+ const source = value . slice ( firstComma + 1 )
1235
13- const bindings = require ( './bindings' )
14- const Image = module . exports = bindings . Image
15- const http = require ( "http" )
16- const https = require ( "https" )
36+ let data
37+ try {
38+ data = Buffer . from ( source , isBase64 ? 'base64' : 'utf8' )
39+ } catch ( err ) {
40+ return signalError ( image , err )
41+ }
1742
18- const proto = Image . prototype ;
19- const _getSource = proto . getSource ;
20- const _setSource = proto . setSource ;
43+ return setSource ( image , data , value )
44+ }
45+
46+ /**
47+ * @param {Image } image
48+ * @param {string } value
49+ */
50+ function loadHttpUrl ( image , value ) {
51+ return get . concat ( value , ( err , res , data ) => {
52+ if ( err ) return signalError ( image , err )
2153
22- delete proto . getSource ;
23- delete proto . setSource ;
54+ if ( res . statusCode < 200 || res . statusCode >= 300 ) {
55+ return signalError ( image , new Error ( `Server responded with ${ res . statusCode } ` ) )
56+ }
57+
58+ return setSource ( image , data , value )
59+ } )
60+ }
61+
62+ /**
63+ * @param {Image } image
64+ * @param {string } value
65+ */
66+ function loadFileUrl ( image , value ) {
67+ fs . readFile ( value . replace ( 'file://' , '' ) , ( err , data ) => {
68+ if ( err ) return signalError ( image , err )
69+
70+ setSource ( image , data , value )
71+ } )
72+ }
73+
74+ /**
75+ * @param {Image } image
76+ * @param {string } value
77+ */
78+ function loadLocalFile ( image , value ) {
79+ fs . readFile ( value , ( err , data ) => {
80+ if ( err ) return signalError ( image , err )
81+
82+ setSource ( image , data , value )
83+ } )
84+ }
2485
2586Object . defineProperty ( Image . prototype , 'src' , {
2687 /**
@@ -33,49 +94,44 @@ Object.defineProperty(Image.prototype, 'src', {
3394 * @param {String|Buffer } val filename, buffer, data URI, URL
3495 * @api public
3596 */
36- set ( val ) {
37- if ( typeof val === 'string' ) {
38- if ( / ^ \s * d a t a : / . test ( val ) ) { // data: URI
39- const commaI = val . indexOf ( ',' )
40- // 'base64' must come before the comma
41- const isBase64 = val . lastIndexOf ( 'base64' , commaI ) !== - 1
42- const content = val . slice ( commaI + 1 )
43- setSource ( this , Buffer . from ( content , isBase64 ? 'base64' : 'utf8' ) , val ) ;
44- } else if ( / ^ \s * h t t p s ? : \/ \/ / . test ( val ) ) { // remote URL
45- const onerror = err => {
46- if ( typeof this . onerror === 'function' ) {
47- this . onerror ( err )
48- } else {
49- throw err
50- }
51- }
52-
53- const type = / ^ \s * h t t p s : \/ \/ / . test ( val ) ? https : http
54- type . get ( val , res => {
55- if ( res . statusCode !== 200 ) {
56- return onerror ( new Error ( `Server responded with ${ res . statusCode } ` ) )
57- }
58- const buffers = [ ]
59- res . on ( 'data' , buffer => buffers . push ( buffer ) )
60- res . on ( 'end' , ( ) => {
61- setSource ( this , Buffer . concat ( buffers ) ) ;
62- } )
63- } ) . on ( 'error' , onerror )
64- } else { // local file path assumed
65- setSource ( this , val ) ;
66- }
67- } else if ( Buffer . isBuffer ( val ) ) {
68- setSource ( this , val ) ;
97+ set ( val ) {
98+ if ( Buffer . isBuffer ( val ) ) {
99+ return setSource ( this , val , val )
100+ }
101+
102+ if ( typeof val !== 'string' ) {
103+ return // ignore invalid values
104+ }
105+
106+ // Strip leading & trailing whitespace
107+ val = val . trim ( )
108+
109+ // Data URL
110+ if ( / ^ d a t a : / . test ( val ) ) {
111+ return loadDataUrl ( this , val )
69112 }
113+
114+ // HTTP(S) URL
115+ if ( / ^ h t t p s ? : \/ \/ / . test ( val ) ) {
116+ return loadHttpUrl ( this , val )
117+ }
118+
119+ // File URL
120+ if ( / ^ f i l e : \/ \/ / . test ( val ) ) {
121+ return loadFileUrl ( this , val )
122+ }
123+
124+ // Assume local file path
125+ loadLocalFile ( this , val )
70126 } ,
71127
72- get ( ) {
128+ get ( ) {
73129 // TODO https://github.com/Automattic/node-canvas/issues/118
74- return getSource ( this ) ;
130+ return this . _originalSource || _getSource . call ( this )
75131 } ,
76132
77133 configurable : true
78- } ) ;
134+ } )
79135
80136/**
81137 * Inspect image.
@@ -86,19 +142,27 @@ Object.defineProperty(Image.prototype, 'src', {
86142 * @api public
87143 */
88144
89- Image . prototype . inspect = function ( ) {
90- return '[Image'
91- + ( this . complete ? ':' + this . width + 'x' + this . height : '' )
92- + ( this . src ? ' ' + this . src : '' )
93- + ( this . complete ? ' complete' : '' )
94- + ']' ;
95- } ;
145+ Image . prototype . inspect = function ( ) {
146+ return '[Image' +
147+ ( this . complete ? ':' + this . width + 'x' + this . height : '' ) +
148+ ( this . src ? ' ' + this . src : '' ) +
149+ ( this . complete ? ' complete' : '' ) +
150+ ']'
151+ }
96152
97- function getSource ( img ) {
98- return img . _originalSource || _getSource . call ( img ) ;
153+ /**
154+ * @param {Buffer } source
155+ */
156+ function isWebP ( source ) {
157+ return ( source . toString ( 'ascii' , 0 , 4 ) === 'RIFF' && source . toString ( 'ascii' , 8 , 12 ) === 'WEBP' )
99158}
100159
101- function setSource ( img , src , origSrc ) {
102- _setSource . call ( img , src ) ;
103- img . _originalSource = origSrc ;
160+ /**
161+ * @param {Image } image
162+ * @param {Buffer } source
163+ * @param {Buffer|string } originalSource
164+ */
165+ function setSource ( image , source , originalSource ) {
166+ _setSource . call ( image , isWebP ( source ) ? webp . decode ( source ) : source )
167+ image . _originalSource = originalSource
104168}
0 commit comments