├── LICENSE
├── README.md
├── img
├── source-image.jpg
├── wm-1.png
├── wm-2.png
└── wm-3.png
├── index.html
├── lib
└── watermark.js
├── procedural-watermark-sample.js
├── ready.js
└── result-screen-shot.png
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Carlos Cabo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # watermark-js
2 | Small JS library to watermarks pictures using JS and HTML5 Canvas element.
3 |
4 |
5 |
6 | **This library is primary intended for back-end usage, allowing the backend users to add watermarks / badges to the images in a non-removable way (the watermarks are _rendered_ over the image itself).** In front-end there are several ways of **simulate a _watermark_** over an image without the need of using this library.
7 |
8 | ## Requirements
9 |
10 | ````
11 | JQuery 1.5+
12 | Web browser with HTML5 Canvas support (IE9+)
13 | ````
14 |
15 | ## Basic usage
16 |
17 | ````javascript
18 | my_watermarked = new Watermark(); // Create new object instance
19 |
20 | my_watermarked
21 | .setPicture('img/source-image.jpg') // Base picture, url or data-url
22 | .addWatermark('img/wm-1.png') // Url or data-url
23 | .render( function(){
24 | // Do something when watermarking ends
25 | });
26 | ````
27 |
28 | ## IMPORTANT(1): CORS
29 | If you get a message in the brower's console about **«A "tainted" canvas»**, you are trying to use images with the _CORS enabled_ [read a brief explanation at MDN](https://developer.mozilla.org/es/docs/Web/HTML/Imagen_con_CORS_habilitado).
30 |
31 | If you are going to work with uploaded **images stored in a different server domain, you will need to enable CORS configuration in the third-party storage**, in order to let the library work with images.
32 | For example, if you are using Amazon S3 storage for your images, this is the [CORS documentation](http://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html#how-do-i-enable-cors).
33 |
34 | ## IMPORTANT(2): The .render() callback
35 | The **watermarking proccess is asyncronous**, so if you want to access / use the resulting watermarked images you must do it inside the `.render()` method, passing a _callback_ function that will be executed **once the watermarking is finished**.
36 |
37 | ## Creating several watermarked thumbnails
38 | You can pass an optional array of `widths` to the `.setPicture()` method to create several sizes from the original base image, this eases the task of creating several sizes of the watermarked image automatically.
39 |
40 | ````javascript
41 | my_watermarked
42 | .setPicture('img/source-image.jpg', [640, 320, 240]) // Array of thumbs widths
43 | //...
44 | ````
45 |
46 | ## Watermark images options
47 | The watermark images can have several optional settings
48 |
49 | ````javascript
50 | .addWatermark('img/wm-3.png', // Image url or data-url
51 | {
52 | position: [1,1], // Default is [0.5, 0.5]
53 | scale: 2.0, // Default is 1.0
54 | opacity: 0.5 // Default is 1.0
55 | }
56 | )
57 | ````
58 |
59 | `position` indicates the position of the watermark **relative to the base image**, first component of the array is the horizontal position, and second one the vertical position, both in a range of [0-1]:
60 |
61 | ````javascript
62 | // Positions
63 | // left top -> [0, 0]
64 | // left center -> [0, 0.5]
65 | // left bottom -> [0, 1]
66 | // right top -> [1, 0]
67 | // right center -> [1, 0.5]
68 | // right bottom -> [1, 1]
69 | // center top -> [0.5, 0]
70 | // center center -> [0.5, 0.5]
71 | // center bottom -> [0.5, 1]
72 | ````
73 |
74 | `scale` sets an enlargement / reduction to the watermark. So if you want the watermark be **half the size the original image you must set this value `0.5`**. Remember that enlarging images can produce _blurry_ results.
75 |
76 | ## Accessing the resulting watermarked images
77 |
78 | You have two methods to retrieve the resulting watermarked images, both return **an array of elements**:
79 |
80 | ````javascript
81 | my_watermarked.getImgs( format, quality ); // Returns array of
s
82 | my_watermarked.getDataUrls( format, quality ); // Returns array of data-urls
83 | // format: 'image/png' (default) / 'image/jpeg'
84 | // quality: float in range [ 0-1 ] ( 1 is best quality )
85 | ````
86 |
87 | Sample usage:
88 |
89 | ````javascript
90 | // Gets all the resulting images in PNG format
91 | var resulting_imgs = my_watermarked.getImgs( 'image/png' );
92 | // Add all of them to the
element
93 | $.each( resulting_imgs, function(idx, item) {
94 | $('body').append( $(item) );
95 | });
96 |
97 | // Get all the resulting data urls in Jpeg 90% quality
98 | var resulting_data_urls = my_watermarked.getDataUrls( 'image/jpeg', 0.9 );
99 | console.log(resulting_data_urls);
100 | ````
101 |
102 | ## Clear watermarks
103 |
104 | Clear watermark configurations and results in case you want to make a new fresh watermark.
105 |
106 | ````javascript
107 | .clearWatermarks();
108 | ````
109 |
110 | ## Advanced usage
111 | As both `.setPicture()` and `.addWatermark()` accept a data-url image as parameter you can build complex / dynamic watermarks **passing a functión that return data-url as result**. Take a look to the `procedural-watermark-sample.js` included in the repo to see the sample function that creates the price badge .
112 |
113 | Following the demo code, with all the avaliable options.
114 |
115 | ````javascript
116 | my_watermarked = new Watermark();
117 |
118 | my_watermarked
119 | .setPicture('img/source-image.jpg', [400, 250])
120 | .addWatermark('img/wm-1.png',
121 | {
122 | position: [0,0]
123 | }
124 | )
125 | .addWatermark('img/wm-2.png')
126 | .addWatermark('img/wm-3.png',
127 | {
128 | position: [1,1],
129 | scale: 2.0,
130 | opacity: 0.5
131 | }
132 | )
133 | .addWatermark(
134 | proceduralWatermark( 'DESDE', '99', ',99€'),
135 | {
136 | position: [1,0]
137 | }
138 | )
139 | .render( function(){
140 |
141 | // var resulting_canvas = wm.getCanvases();
142 | // $.each( resulting_canvas, function(idx, item) {
143 | // $('body').append( $(item) );
144 | // });
145 |
146 | var resulting_imgs = my_watermarked.getImgs( 'image/png' );
147 | $.each( resulting_imgs, function(idx, item) {
148 | $('body').append( $(item) );
149 | });
150 |
151 | var resulting_data_urls = my_watermarked.getDataUrls( 'image/jpeg', 0.9 );
152 | console.log(resulting_data_urls);
153 |
154 | }); // render callback
155 | ````
156 |
157 | ## Utils
158 | ````javascript
159 | // Return the data-URL of image in selector
160 | my_watermarked.getDataUrlFromImg( $('selector') );
161 | ````
162 |
163 | ## Changelog
164 | * 0.2 Avoid web browser cache issues when requesting CORS images
165 | * 0.1 Initial release
166 |
167 | ## TO-DO (or not ;)
168 | - Add vertical constraints to the thumbnails widths
169 | - Add a `fitWidth` to the watermark options...
170 |
--------------------------------------------------------------------------------
/img/source-image.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carloscabo/watermark-js/6a960d51c4a1ae728bb02383dd25d1d4fb6b31a1/img/source-image.jpg
--------------------------------------------------------------------------------
/img/wm-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carloscabo/watermark-js/6a960d51c4a1ae728bb02383dd25d1d4fb6b31a1/img/wm-1.png
--------------------------------------------------------------------------------
/img/wm-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carloscabo/watermark-js/6a960d51c4a1ae728bb02383dd25d1d4fb6b31a1/img/wm-2.png
--------------------------------------------------------------------------------
/img/wm-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carloscabo/watermark-js/6a960d51c4a1ae728bb02383dd25d1d4fb6b31a1/img/wm-3.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Watermark JS
6 |
7 |
8 |
9 |
10 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/lib/watermark.js:
--------------------------------------------------------------------------------
1 | /**
2 | * watermark-js V0.2 by Carlos Cabo 2016
3 | * https://github.com/carloscabo/watermark-js
4 | */
5 |
6 | // data URI - MDN https://developer.mozilla.org/en-US/docs/data_URIs
7 | // The "data" URL scheme: http://tools.ietf.org/html/rfc2397
8 | // Valid URL Characters: http://tools.ietf.org/html/rfc2396#section2
9 |
10 | // Positions
11 | // left top -> 0, 0
12 | // left center -> 0, 0.5
13 | // left bottom -> 0, 1
14 | // right top -> 1, 0
15 | // right center -> 1, 0.5
16 | // right bottom -> 1, 1
17 | // center top -> 0.5, 0
18 | // center center -> 0.5, 0.5
19 | // center bottom -> 0.5, 1
20 |
21 | var Watermark = (function() {
22 |
23 | function Constructor( user_settings ) {
24 | this.version = 0.1;
25 |
26 | // Internal data
27 | this.data = {
28 | picture: {
29 | sizes: null
30 | }, // Source picture
31 | results: [], // Resulting watermarker images
32 | watermarks: [], // Watermarks to be applied
33 | pending_watermarks: 0,
34 | callback: null
35 | };
36 |
37 | // Default settings
38 | this.settings = {};
39 | $.extend(true, this.settings, user_settings);
40 |
41 | }
42 |
43 | Constructor.prototype = {
44 |
45 | // Sets base pìcture to work with
46 | setPicture: function( url_or_data, sizes ) {
47 | var
48 | _t = this.data;
49 | _t.picture.url = this.addAntiCacheParam( url_or_data );
50 | if (typeof sizes !== 'undefined') _t.picture.sizes = sizes;
51 | return this; // Chainning
52 | }, // setPicture
53 |
54 | // Adds a watermark element that will be rendered ove the base picture
55 | // when the .render() methos is called
56 | addWatermark: function( url_or_data, user_options ) {
57 | var
58 | _t = this,
59 | wm = {},
60 | default_options = {
61 | position: [0.5, 0.5],
62 | scale: 1.0,
63 | opacity: 1.0
64 | };
65 |
66 | _t.data.pending_watermarks++;
67 |
68 | wm.url = _t.addAntiCacheParam( url_or_data );
69 | wm.options = $.extend(default_options, user_options);
70 |
71 | _t.data.watermarks.push( wm );
72 |
73 | return this;
74 | },
75 |
76 | // Clear watermark configurations and results
77 | // in case you want to make a fresh watermark
78 | clearWatermarks: function() {
79 | var _t = this;
80 | _t.data.pending_watermarks = 0;
81 | _t.data.watermarks.length = 0; // faster than = []
82 | _t.data.results.length = 0; // faster than = []
83 | },
84 |
85 | // Creates a canvas an return an object with
86 | // .canvas and .ctx (context)
87 | createCanvas: function( img, sx, sy, sw, sh, dx, dy, dw, dh ) {
88 | var
89 | objs = {};
90 | objs.canvas = document.createElement('canvas');
91 | objs.canvas.width = dw;
92 | objs.canvas.height = dh;
93 | objs.ctx = objs.canvas.getContext('2d');
94 | objs.ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);
95 | return objs;
96 | }, // createCanvas
97 |
98 | // Starts the process of creating the base picture canvas and thumbs
99 | // Once done it renders the watermarks over the pictures
100 | // Finally launches the callback function
101 | render: function( callback ) {
102 | var
103 | _t = this,
104 | $img = $('
');
105 |
106 | _t.data.callback = callback;
107 |
108 | // The crossOrigin attribute is a CORS settings attribute.
109 | // Its purpose is to allow images from third-party sites that allow
110 | // cross-origin access to be used with canvas.
111 | // Remember enabled cross-origin access in the third-party site,
112 | // for example if you are using amazon S3 for storage:
113 | // http://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html#how-do-i-enable-cors
114 | $img[0].crossOrigin = "Anonymous";
115 | $img.on('load', function() {
116 | console.log('Source picture loaded');
117 | var
118 | img = this,
119 | // Exact copy of picture
120 | picture = _t.createCanvas( img, 0, 0, img.width, img.height, 0, 0, img.width, img.height );
121 | // picture.pos = _t.calculatePositions( img.width, img.height );
122 | _t.data.results.push( picture );
123 |
124 | // $('body').append( picture.canvas );
125 |
126 | // Create thumbs
127 | if ( _t.data.picture.sizes !== null) {
128 | for (var i = 0; i < _t.data.picture.sizes.length; i++) {
129 | var
130 | w = _t.data.picture.sizes[i],
131 | h = parseInt( (img.height / img.width) * w, 10 );
132 | picture = _t.createCanvas( img, 0, 0, img.width, img.height, 0, 0, w, h );
133 | _t.data.results.push( picture );
134 | // $('body').append( picture.canvas );
135 | // console.log(_t.data.watermarks);
136 | // console.log(w, h);
137 | }
138 | }
139 | _t.renderWatermarks();
140 | }).attr('src', _t.data.picture.url);
141 | },
142 |
143 | renderWatermarks: function () {
144 | var
145 | _t = this;
146 |
147 | for (var i = 0; i < _t.data.watermarks.length; i++) {
148 | var
149 | wm = _t.data.watermarks[i],
150 | $img = $('
');
151 |
152 | // The crossOrigin attribute is a CORS settings attribute.
153 | // Its purpose is to allow images from third-party sites that allow
154 | // cross-origin access to be used with canvas.
155 | // Remember enabled cross-origin access in the third-party site,
156 | // for example if you are using amazon S3 for storage:
157 | // http://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html#how-do-i-enable-cors
158 | $img[0].crossOrigin = "Anonymous";
159 | $img.on('load', function() {
160 |
161 | var
162 | wm_img = this,
163 | wm_obj = _t.createCanvas( wm_img, 0, 0, wm_img.width, wm_img.height, 0, 0, wm_img.width, wm_img.height ),
164 | options= $(this).data('options'),
165 | scale = options.scale,
166 | position = options.position,
167 | w = wm_img.width * scale,
168 | h = wm_img.height * scale;
169 |
170 | // $('body').append( wm_obj.canvas );
171 | for (var j = 0; j < _t.data.results.length; j++) {
172 | // _t.data.results[j];
173 | _t.data.results[j].ctx.globalAlpha = options.opacity;
174 | _t.data.results[j].ctx.drawImage(
175 | wm_obj.canvas,
176 | ( _t.data.results[j].canvas.width - w ) * position[0],
177 | ( _t.data.results[j].canvas.height - h ) * position[1],
178 | w,
179 | h
180 | );
181 | // $('body').append( _t.data.results[j].canvas );
182 | }
183 | _t.data.pending_watermarks--;
184 | if (_t.data.pending_watermarks === 0) {
185 | _t.data.callback();
186 | }
187 | }).data( 'options', wm.options ).attr('src', wm.url);
188 | }
189 | },
190 |
191 | // Returns array of