├── .gitignore ├── .travis.yml ├── CHANGELOG ├── LICENSE ├── README.md ├── demo ├── test.dxf ├── test.json ├── test.svg └── usage.js ├── index.js ├── libs ├── matrix │ ├── extends.js │ ├── matrix.js │ └── utils.js ├── objects │ ├── circle.js │ ├── group.js │ ├── image.js │ ├── index.js │ ├── path.js │ ├── polygon.js │ ├── rect.js │ ├── svgobject.js │ ├── text.js │ └── tspan.js ├── parser.js └── svg.js ├── package.json └── test ├── 00_rect.js ├── 01_polygon.js ├── 02_text.js ├── 04_image.js ├── 10_manipulate_svg.js ├── 11_contains.js └── svg ├── contains.svg ├── notcontains.svg └── test.svg /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules 16 | 17 | .idea/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | [0.11.0] 2 | - convert elements into Path (object path created but not used at this time on import and manipulation) 3 | - add common class on all elements 4 | - add class on specific element 5 | - add possibility to set stylesheets to svg. 6 | [0.10.0] 7 | - Add function removeByType 8 | [0.9.3] 9 | - Fix bug on InnerBox calculation 10 | [0.9.2] 11 | - Optimize code 12 | - add function removeAllByType on svg and group classes 13 | [0.9.1] 14 | - add svg and group function findByIdWithoutType 15 | [0.9.0] 16 | - Fix bugs 17 | - Add circle element 18 | [0.8.6] 19 | - Fix bugs 20 | [0.8.5] 21 | - Fix bugs 22 | [0.8.4] 23 | - Fix bugs 24 | [0.8.3] 25 | - Fix bugs 26 | [0.8.2] 27 | - Add function to calculate all innerBoxes and set innerbox in data object 28 | [0.8.1] 29 | - Fix bugs 30 | [0.8.0] 31 | - Get Rect and Polygon InnerBox 32 | [0.7.2] 33 | - Clean dependencies 34 | [0.7.1] 35 | - Clean Polygon identical points in sequence. 36 | [0.7.0] 37 | - Convert DXF File to SVG 38 | [0.6.4] 39 | - update dependancies 40 | - add function findByIdAndType in SVG and Group 41 | [0.6.3] 42 | - get SVG BBox 43 | [0.6.2] 44 | - fix bugs 45 | [0.6.1] 46 | - add image xlink:href support 47 | [0.6.0] 48 | - use correct practice to inherit svgobject 49 | - start rewrite all code with odnb code style 50 | - add image arsing for xml and json 51 | [0.5.4] 52 | - remove the 't_' prefix on text ID 53 | - set svg width and height 54 | - get svg width and height 55 | - get group bbox 56 | - fix png conversion size error on OSX 57 | [0.5.3] 58 | - Add Image Setters 59 | - fix bug on href attribute (replaced by xlink:href) 60 | [0.5.2] 61 | - Add Image to Elements 62 | [0.5.1] 63 | - Add fromJSON function 64 | [0.5.0] 65 | - Add element 66 | - save svg into .svg file 67 | - save svg into .png file 68 | [0.4.4] 69 | - fix bug on setId for text with his value 70 | [0.4.3] 71 | - set data.id and data.name automatically with setId and setName. It's update to communicate with snapsvg and protect setted id 72 | [0.4.1] 73 | - add findById in Svg and Group classes 74 | [0.4.0] 75 | - optimize boundingbox calcul. 76 | - Manual calculation of BoundingBox 77 | - Remove PhantomJS constraint 78 | [0.3.2] 79 | - optimize boundingbox calcul but, with phantomJS, no support large size. Wait svg implementation in jsdom 80 | [0.3.0] 81 | - add contains function 82 | [0.2.4] 83 | - fix major bug if use many svg as same time 84 | [0.2.2] 85 | - fix bug on applyMatrix 86 | [0.2.1] 87 | - add matrix apply for all svg 88 | [0.2.0] 89 | - use data-* attributes 90 | - use Phantom.JS to calculate boundingBox 91 | [0.1.0] 92 | - Create Project 93 | - Add element 94 | - Add 95 | - Add Element 96 | - Add Element 97 | - Add Element 98 | - Add Element 99 | - Use Snap.Svg Matrix class -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | svgutils 2 | ======== 3 | 4 | Svg Utils for pasing SVGFile and manipulate Matrix object like Snap.svg 5 | 6 | [![npm status](http://img.shields.io/npm/v/svgutils.svg?style=flat-square)](https://www.npmjs.org/package/svgutils) 7 | [![build status](https://secure.travis-ci.org/throrin19/svgutils.svg?style=flat-square)](http://travis-ci.org/throrin19/svgutils) 8 | [![dependency status](https://david-dm.org/throrin19/svgutils.svg?style=flat)](https://david-dm.org/throrin19/svgutils) 9 | [![Code Climate](https://codeclimate.com/github/throrin19/svgutils/badges/gpa.svg?style=flat-square)](https://codeclimate.com/github/throrin19/svgutils) 10 | [![experimental](http://img.shields.io/badge/stability-experimental-DD5F0A.svg?style=flat-square)](http://nodejs.org/api/documentation.html#documentation_stability_index) 11 | [![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=throrin19&url=https://github.com/throrin19/svgutils/&title=SvgUtils&language=Javascript&tags=github&category=software) 12 | 13 | ## Install 14 | 15 | ``` 16 | npm install svgutils 17 | ``` 18 | 19 | ## Requirements 20 | 21 | ### NodeJS 22 | 23 | Required version : 0.10.22 24 | 25 | ## Usage 26 | 27 | With SVGUtils you can : 28 | 29 | + Convert SVG Document to JSON 30 | + Convert JSON to SVG Document 31 | + Manipulate Svg 32 | + Use Matrix to SVG or elements 33 | + ... 34 | 35 | #### Warning 36 | 37 | For Text and Group, the boundingbox result is not true but it's enough for basic manipulations 38 | 39 | ### Loading SVG file 40 | 41 | ```javascript 42 | var Svg = require('svgutils').Svg; 43 | 44 | 45 | Svg.fromSvgDocument(__dirname + '/test2.svg', function(err, svg){ 46 | // ... 47 | }); 48 | ``` 49 | 50 | ### Convert SVG file to JSON 51 | 52 | ```javascript 53 | var Svg = require('svgutils').Svg; 54 | 55 | Svg.fromSvgDocument(__dirname + '/test2.svg', function(err, svg){ 56 | if(err){ 57 | throw new Error('SVG file not found or invalid'); 58 | } 59 | 60 | var json = svg.toJSON(); 61 | }); 62 | ``` 63 | 64 | ### Apply Matrix and get transformed svg 65 | 66 | #### Currents Matrix only 67 | 68 | ```javascript 69 | var Svg = require('svgutils').Svg; 70 | 71 | Svg.fromSvgDocument(__dirname + '/test2.svg', function(err, svg){ 72 | if(err){ 73 | throw new Error('SVG file not found or invalid'); 74 | } 75 | 76 | svg.applyMatrix(null, function(newSvg){ 77 | console.log(newSvg.toString()); 78 | }); 79 | }); 80 | ``` 81 | 82 | #### Externals and currents Matrix 83 | 84 | ```javascript 85 | var Svg = require('svgutils').Svg, 86 | Matrix = require('svgutils';.Matrix; 87 | 88 | Svg.fromSvgDocument(__dirname + '/test2.svg', function(err, svg){ 89 | if(err){ 90 | throw new Error('SVG file not found or invalid'); 91 | } 92 | 93 | // Ex : apply translate(10, 20) to all svg 94 | svg.applyMatrix(new Matrix(1, 0, 0, 1, 10, 20), function(newSvg){ 95 | console.log(newSvg.toString()); 96 | }); 97 | }); 98 | ``` 99 | 100 | ### Save generated SVG or Convert to PNG 101 | 102 | #### Save SVG 103 | 104 | ```javascript 105 | var Svg = require('svgutils').Svg, 106 | Matrix = require('svgutils';.Matrix; 107 | 108 | Svg.fromSvgDocument(__dirname + '/test2.svg', function(err, svg){ 109 | if(err){ 110 | throw new Error('SVG file not found or invalid'); 111 | } 112 | 113 | svg.save({ output : '/home/user/svg.svg' }, function(err, filename){ 114 | if(err){ 115 | throw err; 116 | } 117 | }); 118 | }); 119 | ``` 120 | 121 | #### Save PNG 122 | 123 | ```javascript 124 | var Svg = require('svgutils').Svg, 125 | Matrix = require('svgutils';.Matrix; 126 | 127 | Svg.fromSvgDocument(__dirname + '/test2.svg', function(err, svg){ 128 | if(err){ 129 | throw new Error('SVG file not found or invalid'); 130 | } 131 | 132 | svg.savePng({ output : '/home/user/svg.png' }, function(err, filename){ 133 | if(err){ 134 | throw err; 135 | } 136 | }); 137 | }); 138 | ``` 139 | 140 | ### Convert others formats to SVG 141 | 142 | #### DXF to SVG (thanks to Thomas Desmoulin and his [DXF-parsing module](https://github.com/thomasdesmoulin/dxf-parsing)) 143 | 144 | You can create SVG from DXF file. You can, as you want, get specifics DXF layers. 145 | 146 | ```javascript 147 | Svg.fromDxfFile({ 148 | path : __dirname + '/test.dxf' 149 | }, function (err, svg) { 150 | if(err){ 151 | throw new Error('SVG file not found or invalid'); 152 | } 153 | 154 | // your converted svg 155 | }); 156 | ``` 157 | 158 | 159 | ## Contrbute 160 | 161 | + Fork the repo 162 | + create a branch git checkout -b my_branch 163 | + Add your changes 164 | + Commit your changes: git commit -am "Added some awesome stuff" 165 | + Push your branch: git push origin my_branch 166 | + Make a pull request to development branch 167 | 168 | -------------------------------------------------------------------------------- /demo/test.dxf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/throrin19/svgutils/8c826dbf3a9806fbe40ef0c5151117404fadceae/demo/test.dxf -------------------------------------------------------------------------------- /demo/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "elements" : [ 3 | { 4 | "points" : [ 5 | { 6 | "y" : 480.027314528534, 7 | "x" : 755.3130820602219 8 | }, 9 | { 10 | "y" : 480.0346138910102, 11 | "x" : 814.5756889094729 12 | }, 13 | { 14 | "y" : 418.8106042140523, 15 | "x" : 814.584238184381 16 | }, 17 | { 18 | "y" : 418.8033048515965, 19 | "x" : 755.3216313351406 20 | } 21 | ], 22 | "data" : { 23 | "name" : "T67", 24 | "id" : "__T67" 25 | }, 26 | "fill" : "#dddddd", 27 | "stroke" : "#aaaaaa", 28 | "name" : "__T67", 29 | "id" : "__T67", 30 | "type" : "polygon" 31 | }, 32 | { 33 | "points" : [ 34 | { 35 | "y" : 990.73293304003, 36 | "x" : 587.38555067879 37 | }, 38 | { 39 | "y" : 990.7514350256, 40 | "x" : 695.94289365716 41 | }, 42 | { 43 | "y" : 940.0806638816, 44 | "x" : 695.94289365716 45 | }, 46 | { 47 | "y" : 940.0806638816, 48 | "x" : 634.71172347229 49 | }, 50 | { 51 | "y" : 952.00210991633, 52 | "x" : 634.71172347229 53 | }, 54 | { 55 | "y" : 952.00210991633, 56 | "x" : 587.38555067879 57 | } 58 | ], 59 | "data" : { 60 | "name" : "1F20", 61 | "id" : "1__1F20" 62 | }, 63 | "fill" : "#dddddd", 64 | "stroke" : "#aaaaaa", 65 | "name" : "1__1F20", 66 | "id" : "1__1F20", 67 | "type" : "polygon" 68 | } 69 | ] 70 | } -------------------------------------------------------------------------------- /demo/test.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Texte en dehors 8 | Several lines: 9 | First line 10 | Second line 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /demo/usage.js: -------------------------------------------------------------------------------- 1 | var Svg = require('../index').Svg; 2 | 3 | 4 | Svg.fromJsonFile(__dirname + '/test.json', function (err, svg) { 5 | var element = svg.elements[0]; 6 | element.getInnerBox(function (innerBox) { 7 | //console.log(innerBox); 8 | }); 9 | //var element = svg.elements[1]; 10 | //element.getInnerBox(function (innerBox) { 11 | // console.log(innerBox); 12 | //}); 13 | }); -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Matrix = require(__dirname +'/libs/matrix/extends'), 4 | Parser = require(__dirname + '/libs/parser'), 5 | Svg = require(__dirname + '/libs/svg'), 6 | Elements = require(__dirname + '/libs/objects/index'), 7 | utilsMatrix = require(__dirname + '/libs/matrix/utils'); 8 | 9 | module.exports = { 10 | Matrix : Matrix, 11 | Parser : Parser, 12 | Svg : Svg, 13 | Elements : Elements, 14 | utilsMatrix : utilsMatrix 15 | }; -------------------------------------------------------------------------------- /libs/matrix/extends.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Matrix = require(__dirname + '/matrix'), 4 | utils = require(__dirname + '/utils'); 5 | 6 | module.exports = Matrix; 7 | 8 | /** 9 | * Create Matrix from element informations 10 | * 11 | * @param {object} bbox Bounding box 12 | * @param {SvgObject} element Svg Element 13 | * @return {Matrix} Applied Matrix object 14 | */ 15 | module.exports.fromElement = function(bbox, element){ 16 | if(typeof element.transform != 'undefined'){ 17 | var tstr = element.transform; 18 | 19 | var res = []; 20 | tstr = tstr.replace(/(?:^|\s)(\w+)\(([^)]+)\)/g, function (all, name, params) { 21 | params = params.split(/\s*,\s*/); 22 | if (name == "rotate" && params.length == 1) { 23 | params.push(0, 0); 24 | } 25 | if (name == "scale") { 26 | if (params.length == 2) { 27 | params.push(0, 0); 28 | } 29 | if (params.length == 1) { 30 | params.push(params[0], 0, 0); 31 | } 32 | } 33 | if (name == "skewX") { 34 | res.push(["m", 1, 0, math.tan(rad(params[0])), 1, 0, 0]); 35 | } else if (name == "skewY") { 36 | res.push(["m", 1, math.tan(rad(params[0])), 0, 1, 0, 0]); 37 | } else { 38 | res.push([name.charAt(0)].concat(params)); 39 | } 40 | return all; 41 | }); 42 | return utils.transform2matrix(res, bbox); 43 | } 44 | return new Matrix(); 45 | }; -------------------------------------------------------------------------------- /libs/matrix/matrix.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Snap.svg Matrix subclass 3 | */ 4 | 5 | var PI = Math.PI, 6 | math = Math; 7 | 8 | function rad(deg) { 9 | return deg % 360 * PI / 180; 10 | } 11 | function deg(rad) { 12 | return rad * 180 / PI % 360; 13 | } 14 | 15 | 16 | function Matrix(a, b, c, d, e, f) { 17 | if (a != null && typeof a == 'object'){ 18 | this.a = +a.a; 19 | this.b = +a.b; 20 | this.c = +a.c; 21 | this.d = +a.d; 22 | this.e = +a.e; 23 | this.f = +a.f; 24 | } else 25 | if (a != null) { 26 | this.a = +a; 27 | this.b = +b; 28 | this.c = +c; 29 | this.d = +d; 30 | this.e = +e; 31 | this.f = +f; 32 | } else { 33 | this.a = 1; 34 | this.b = 0; 35 | this.c = 0; 36 | this.d = 1; 37 | this.e = 0; 38 | this.f = 0; 39 | } 40 | } 41 | (function (matrixproto) { 42 | /*\ 43 | * Matrix.add 44 | [ method ] 45 | ** 46 | * Adds the given matrix to existing one 47 | - a (number) 48 | - b (number) 49 | - c (number) 50 | - d (number) 51 | - e (number) 52 | - f (number) 53 | * or 54 | - matrix (object) @Matrix 55 | \*/ 56 | matrixproto.add = function (a, b, c, d, e, f) { 57 | var out = [[], [], []], 58 | m = [[this.a, this.c, this.e], [this.b, this.d, this.f], [0, 0, 1]], 59 | matrix = [[a, c, e], [b, d, f], [0, 0, 1]], 60 | x, y, z, res; 61 | 62 | if (a && a instanceof Matrix) { 63 | matrix = [[a.a, a.c, a.e], [a.b, a.d, a.f], [0, 0, 1]]; 64 | } 65 | 66 | for (x = 0; x < 3; x++) { 67 | for (y = 0; y < 3; y++) { 68 | res = 0; 69 | for (z = 0; z < 3; z++) { 70 | res += m[x][z] * matrix[z][y]; 71 | } 72 | out[x][y] = res; 73 | } 74 | } 75 | this.a = out[0][0]; 76 | this.b = out[1][0]; 77 | this.c = out[0][1]; 78 | this.d = out[1][1]; 79 | this.e = out[0][2]; 80 | this.f = out[1][2]; 81 | return this; 82 | }; 83 | /*\ 84 | * Matrix.invert 85 | [ method ] 86 | ** 87 | * Returns an inverted version of the matrix 88 | = (object) @Matrix 89 | \*/ 90 | matrixproto.invert = function () { 91 | var me = this, 92 | x = me.a * me.d - me.b * me.c; 93 | return new Matrix(me.d / x, -me.b / x, -me.c / x, me.a / x, (me.c * me.f - me.d * me.e) / x, (me.b * me.e - me.a * me.f) / x); 94 | }; 95 | /*\ 96 | * Matrix.clone 97 | [ method ] 98 | ** 99 | * Returns a copy of the matrix 100 | = (object) @Matrix 101 | \*/ 102 | matrixproto.clone = function () { 103 | return new Matrix(this.a, this.b, this.c, this.d, this.e, this.f); 104 | }; 105 | /*\ 106 | * Matrix.translate 107 | [ method ] 108 | ** 109 | * Translate the matrix 110 | - x (number) horizontal offset distance 111 | - y (number) vertical offset distance 112 | \*/ 113 | matrixproto.translate = function (x, y) { 114 | return this.add(1, 0, 0, 1, x, y); 115 | }; 116 | /*\ 117 | * Matrix.scale 118 | [ method ] 119 | ** 120 | * Scales the matrix 121 | - x (number) amount to be scaled, with `1` resulting in no change 122 | - y (number) #optional amount to scale along the vertical axis. (Otherwise `x` applies to both axes.) 123 | - cx (number) #optional horizontal origin point from which to scale 124 | - cy (number) #optional vertical origin point from which to scale 125 | * Default cx, cy is the middle point of the element. 126 | \*/ 127 | matrixproto.scale = function (x, y, cx, cy) { 128 | y == null && (y = x); 129 | (cx || cy) && this.add(1, 0, 0, 1, cx, cy); 130 | this.add(x, 0, 0, y, 0, 0); 131 | (cx || cy) && this.add(1, 0, 0, 1, -cx, -cy); 132 | return this; 133 | }; 134 | /*\ 135 | * Matrix.rotate 136 | [ method ] 137 | ** 138 | * Rotates the matrix 139 | - a (number) angle of rotation, in degrees 140 | - x (number) horizontal origin point from which to rotate 141 | - y (number) vertical origin point from which to rotate 142 | \*/ 143 | matrixproto.rotate = function (a, x, y) { 144 | a = rad(a); 145 | x = x || 0; 146 | y = y || 0; 147 | var cos = +math.cos(a).toFixed(9), 148 | sin = +math.sin(a).toFixed(9); 149 | this.add(cos, sin, -sin, cos, x, y); 150 | return this.add(1, 0, 0, 1, -x, -y); 151 | }; 152 | /*\ 153 | * Matrix.x 154 | [ method ] 155 | ** 156 | * Returns x coordinate for given point after transformation described by the matrix. See also @Matrix.y 157 | - x (number) 158 | - y (number) 159 | = (number) x 160 | \*/ 161 | matrixproto.x = function (x, y) { 162 | return x * this.a + y * this.c + this.e; 163 | }; 164 | /*\ 165 | * Matrix.y 166 | [ method ] 167 | ** 168 | * Returns y coordinate for given point after transformation described by the matrix. See also @Matrix.x 169 | - x (number) 170 | - y (number) 171 | = (number) y 172 | \*/ 173 | matrixproto.y = function (x, y) { 174 | return x * this.b + y * this.d + this.f; 175 | }; 176 | matrixproto.get = function (i) { 177 | return +this[Str.fromCharCode(97 + i)].toFixed(4); 178 | }; 179 | matrixproto.toString = function () { 180 | return "matrix(" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)].join() + ")"; 181 | }; 182 | matrixproto.offset = function () { 183 | return [this.e.toFixed(4), this.f.toFixed(4)]; 184 | }; 185 | function norm(a) { 186 | return a[0] * a[0] + a[1] * a[1]; 187 | } 188 | function normalize(a) { 189 | var mag = math.sqrt(norm(a)); 190 | a[0] && (a[0] /= mag); 191 | a[1] && (a[1] /= mag); 192 | } 193 | // SIERRA Matrix.split(): HTML formatting for the return value is scrambled. It should appear _Returns: {OBJECT} in format:..._ 194 | // SIERRA Matrix.split(): the _shear_ parameter needs to be detailed. Is it an angle? What does it affect? 195 | // SIERRA Matrix.split(): The idea of _simple_ transforms needs to be detailed and contrasted with any alternatives. 196 | /*\ 197 | * Matrix.split 198 | [ method ] 199 | ** 200 | * Splits matrix into primitive transformations 201 | = (object) in format: 202 | o dx (number) translation by x 203 | o dy (number) translation by y 204 | o scalex (number) scale by x 205 | o scaley (number) scale by y 206 | o shear (number) shear 207 | o rotate (number) rotation in deg 208 | o isSimple (boolean) could it be represented via simple transformations 209 | \*/ 210 | matrixproto.split = function () { 211 | var out = {}; 212 | // translation 213 | out.dx = this.e; 214 | out.dy = this.f; 215 | 216 | // scale and shear 217 | var row = [[this.a, this.c], [this.b, this.d]]; 218 | out.scalex = math.sqrt(norm(row[0])); 219 | normalize(row[0]); 220 | 221 | out.shear = row[0][0] * row[1][0] + row[0][1] * row[1][1]; 222 | row[1] = [row[1][0] - row[0][0] * out.shear, row[1][1] - row[0][1] * out.shear]; 223 | 224 | out.scaley = math.sqrt(norm(row[1])); 225 | normalize(row[1]); 226 | out.shear /= out.scaley; 227 | 228 | // rotation 229 | var sin = -row[0][1], 230 | cos = row[1][1]; 231 | if (cos < 0) { 232 | out.rotate = deg(math.acos(cos)); 233 | if (sin < 0) { 234 | out.rotate = 360 - out.rotate; 235 | } 236 | } else { 237 | out.rotate = deg(math.asin(sin)); 238 | } 239 | 240 | out.isSimple = !+out.shear.toFixed(9) && (out.scalex.toFixed(9) == out.scaley.toFixed(9) || !out.rotate); 241 | out.isSuperSimple = !+out.shear.toFixed(9) && out.scalex.toFixed(9) == out.scaley.toFixed(9) && !out.rotate; 242 | out.noRotation = !+out.shear.toFixed(9) && !out.rotate; 243 | return out; 244 | }; 245 | // SIERRA Matrix.toTransformString(): The format of the string needs to be detailed. 246 | /*\ 247 | * Matrix.toTransformString 248 | [ method ] 249 | ** 250 | * Returns transform string that represents given matrix 251 | = (string) transform string 252 | \*/ 253 | matrixproto.toTransformString = function (shorter) { 254 | var s = shorter || this.split(); 255 | if (s.isSimple) { 256 | s.scalex = +s.scalex.toFixed(4); 257 | s.scaley = +s.scaley.toFixed(4); 258 | s.rotate = +s.rotate.toFixed(4); 259 | return (s.dx || s.dy ? "t" + [+s.dx.toFixed(4), +s.dy.toFixed(4)] : E) + 260 | (s.scalex != 1 || s.scaley != 1 ? "s" + [s.scalex, s.scaley, 0, 0] : E) + 261 | (s.rotate ? "r" + [+s.rotate.toFixed(4), 0, 0] : E); 262 | } else { 263 | return "m" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)]; 264 | } 265 | }; 266 | })(Matrix.prototype); 267 | 268 | module.exports = Matrix; -------------------------------------------------------------------------------- /libs/matrix/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Matrix = require(__dirname + '/matrix'), 4 | spaces = "\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029", 5 | Str = String, 6 | has = "hasOwnProperty"; 7 | 8 | 9 | 10 | module.exports = { 11 | clone : function(obj) { 12 | if (typeof obj == "function" || Object(obj) !== obj) { 13 | return obj; 14 | } 15 | var res = new obj.constructor; 16 | for (var key in obj) if (obj[has](key)) { 17 | res[key] = this.clone(obj[key]); 18 | } 19 | return res; 20 | }, 21 | is : function(o, type) { 22 | type = Str.prototype.toLowerCase.call(type); 23 | if (type == "finite") { 24 | return isFinite(o); 25 | } 26 | if (type == "array" && 27 | (o instanceof Array || Array.isArray && Array.isArray(o))) { 28 | return true; 29 | } 30 | return (type == "null" && o === null) || 31 | (type == typeof o && o !== null) || 32 | (type == "object" && o === Object(o)) || 33 | objectToString.call(o).slice(8, -1).toLowerCase() == type; 34 | }, 35 | parseTransformString : function(TString){ 36 | if (!TString) { 37 | return null; 38 | } 39 | var paramCounts = {r: 3, s: 4, t: 2, m: 6}, 40 | data = []; 41 | if (this.is(TString, "array") && this.is(TString[0], "array")) { // rough assumption 42 | data = this.clone(TString); 43 | } 44 | if (!data.length) { 45 | Str(TString).replace(tCommand, function (a, b, c) { 46 | var params = [], 47 | name = b.toLowerCase(); 48 | c.replace(pathValues, function (a, b) { 49 | b && params.push(+b); 50 | }); 51 | data.push([b].concat(params)); 52 | }); 53 | } 54 | return data; 55 | }, 56 | rgTransform : new RegExp("^[a-z][" + spaces + "]*-?\\.?\\d"), 57 | transform2matrix : function(tstr, bbox){ 58 | var tdata = this.parseTransformString(tstr), 59 | m = new Matrix; 60 | if (tdata) { 61 | for (var i = 0, ii = tdata.length; i < ii; i++) { 62 | var t = tdata[i], 63 | tlen = t.length, 64 | command = Str(t[0]).toLowerCase(), 65 | absolute = t[0] != command, 66 | inver = absolute ? m.invert() : 0, 67 | x1, 68 | y1, 69 | x2, 70 | y2, 71 | bb; 72 | if (command == "t" && tlen == 2){ 73 | m.translate(t[1], 0); 74 | } else if (command == "t" && tlen == 3) { 75 | if (absolute) { 76 | x1 = inver.x(0, 0); 77 | y1 = inver.y(0, 0); 78 | x2 = inver.x(t[1], t[2]); 79 | y2 = inver.y(t[1], t[2]); 80 | m.translate(x2 - x1, y2 - y1); 81 | } else { 82 | m.translate(t[1], t[2]); 83 | } 84 | } else if (command == "r") { 85 | if (tlen == 2) { 86 | bb = bb || bbox; 87 | m.rotate(t[1], bb.x + bb.width / 2, bb.y + bb.height / 2); 88 | } else if (tlen == 4) { 89 | if (absolute) { 90 | x2 = inver.x(t[2], t[3]); 91 | y2 = inver.y(t[2], t[3]); 92 | m.rotate(t[1], x2, y2); 93 | } else { 94 | m.rotate(t[1], t[2], t[3]); 95 | } 96 | } 97 | } else if (command == "s") { 98 | if (tlen == 2 || tlen == 3) { 99 | bb = bb || bbox; 100 | m.scale(t[1], t[tlen - 1], bb.x + bb.width / 2, bb.y + bb.height / 2); 101 | } else if (tlen == 4) { 102 | if (absolute) { 103 | x2 = inver.x(t[2], t[3]); 104 | y2 = inver.y(t[2], t[3]); 105 | m.scale(t[1], t[1], x2, y2); 106 | } else { 107 | m.scale(t[1], t[1], t[2], t[3]); 108 | } 109 | } else if (tlen == 5) { 110 | if (absolute) { 111 | x2 = inver.x(t[3], t[4]); 112 | y2 = inver.y(t[3], t[4]); 113 | m.scale(t[1], t[2], x2, y2); 114 | } else { 115 | m.scale(t[1], t[2], t[3], t[4]); 116 | } 117 | } 118 | } else if (command == "m" && tlen == 7) { 119 | m.add(t[1], t[2], t[3], t[4], t[5], t[6]); 120 | } 121 | } 122 | } 123 | return m; 124 | }, 125 | rectPath : function(x, y, w, h, r) { 126 | if (r) { 127 | return [ 128 | ["M", x + r, y], 129 | ["l", w - r * 2, 0], 130 | ["a", r, r, 0, 0, 1, r, r], 131 | ["l", 0, h - r * 2], 132 | ["a", r, r, 0, 0, 1, -r, r], 133 | ["l", r * 2 - w, 0], 134 | ["a", r, r, 0, 0, 1, -r, -r], 135 | ["l", 0, r * 2 - h], 136 | ["a", r, r, 0, 0, 1, r, -r], 137 | ["z"] 138 | ]; 139 | } 140 | var res = [["M", x, y], ["l", w, 0], ["l", 0, h], ["l", -w, 0], ["z"]]; 141 | //res.toString = toString; 142 | return res; 143 | }, 144 | bbox : function(x, y, width, height) { 145 | if (x == null) { 146 | x = y = width = height = 0; 147 | } 148 | if (y == null) { 149 | y = x.y; 150 | width = x.width; 151 | height = x.height; 152 | x = x.x; 153 | } 154 | return { 155 | x: x, 156 | y: y, 157 | width: width, 158 | w: width, 159 | height: height, 160 | h: height, 161 | x2: x + width, 162 | y2: y + height, 163 | cx: x + width / 2, 164 | cy: y + height / 2, 165 | r1: Math.min(width, height) / 2, 166 | r2: Math.max(width, height) / 2, 167 | r0: Math.sqrt(width * width + height * height) / 2, 168 | path: this.rectPath(x, y, width, height), 169 | vb: [x, y, width, height].join(" ") 170 | }; 171 | } 172 | }; -------------------------------------------------------------------------------- /libs/objects/circle.js: -------------------------------------------------------------------------------- 1 | var SvgObject = require(__dirname + '/svgobject'), 2 | Matrix = require(__dirname + '/../matrix/extends'), 3 | _ = require('underscore'), 4 | utils = require(__dirname + '/../matrix/utils'), 5 | async = require('async'), 6 | nUtil = require('util'); 7 | 8 | var Circle = function() { 9 | if (!(this instanceof Circle)) 10 | throw 'this function in a constructor. Use new to call it'; 11 | 12 | SvgObject.call(this); 13 | this.type = 'circle'; 14 | this.cx = 0; 15 | this.cy = 0; 16 | this.r = 0; 17 | }; 18 | 19 | nUtil.inherits(Circle, SvgObject); 20 | 21 | /** 22 | * Get Center x value 23 | * @returns {number} 24 | */ 25 | Circle.prototype.getCx = function getCx() { 26 | return this.cx; 27 | }; 28 | 29 | /** 30 | * Get Center y value 31 | * @returns {number} 32 | */ 33 | Circle.prototype.getCy = function getCy() { 34 | return this.cy; 35 | }; 36 | 37 | /** 38 | * Get circle's radius 39 | * @return {number} 40 | */ 41 | Circle.prototype.getRadius = function getRadius() { 42 | return this.r; 43 | }; 44 | 45 | /** 46 | * Set Cx coordinate 47 | * @param {number} cx cx coordinate 48 | */ 49 | Circle.prototype.setCx = function setCx(cx) { 50 | this.cx = cx; 51 | }; 52 | 53 | /** 54 | * Set Cy coordinate 55 | * @param {number} cy cy coordinate 56 | */ 57 | Circle.prototype.setCy = function setCy(cy) { 58 | this.cy = cy; 59 | }; 60 | 61 | /** 62 | * Set Radius 63 | * @param {number} r radius 64 | */ 65 | Circle.prototype.setRadius = function setRadius(r) { 66 | this.r = r; 67 | }; 68 | 69 | /** 70 | * Return JSON from object 71 | * @param {boolean} [matrix] return transform attribute if false. 72 | * @returns {object} JSON Object 73 | */ 74 | Circle.prototype.toJSON = function(matrix){ 75 | var parentJSON = SvgObject.prototype.toJSON.call(this, matrix); 76 | 77 | parentJSON.cx = this.cx; 78 | parentJSON.cy = this.cy; 79 | parentJSON.r = this.r; 80 | 81 | return parentJSON; 82 | }; 83 | 84 | /** 85 | * Return XML from object 86 | * @param {boolean} [matrix] return transform attribute if false. 87 | * @returns {xmlBuilder} XML Object 88 | */ 89 | Circle.prototype.toXml = function toXml(matrix) { 90 | var xml = SvgObject.prototype.toXml.call(this, matrix); 91 | 92 | xml.att('cx', this.cx); 93 | xml.att('cy', this.cy); 94 | xml.att('r', this.r); 95 | 96 | return xml; 97 | }; 98 | 99 | /** 100 | * Get the element Bounding Box 101 | * @param {function} callback Callback Function 102 | */ 103 | Circle.prototype.getBBox = function getBBox(callback) { 104 | this.bbox = utils.bbox(this.cx - this.r, this.cy - this.r, this.r*2, this.r*2); 105 | callback(this.bbox); 106 | }; 107 | 108 | /** 109 | * Get the element innerBox 110 | * @param {function} callback Callback function 111 | */ 112 | Circle.prototype.getInnerBox = function getInnerBox(callback) { 113 | // @todo verify calcul 114 | var diff = this.r*2 - Math.sqrt(2)*this.r; 115 | 116 | callback({ 117 | x : this.cx - diff, 118 | y : this.cy - diff, 119 | width : this.r - diff, 120 | height : this.r - diff 121 | }); 122 | }; 123 | 124 | module.exports = Circle; 125 | 126 | /** 127 | * Create Circle from SVG image node 128 | * 129 | * @param {object} node xml2js node from SVG file 130 | * @returns {Circle} the Circle object 131 | */ 132 | module.exports.fromNode = function fromNode(node) { 133 | var circle = new Circle(); 134 | 135 | if (typeof node !== 'undefined' && typeof node.$ !== 'undefined') { 136 | SvgObject.fromNode(circle, node); 137 | 138 | if (typeof node.$.cx !== 'undefined') { 139 | circle.cx = parseFloat(node.$.cx); 140 | } 141 | if (typeof node.$.cy !== 'undefined') { 142 | circle.cy = parseFloat(node.$.cy); 143 | } 144 | if (typeof node.$.r !== 'undefined') { 145 | circle.r = parseFloat(node.$.r); 146 | } 147 | } 148 | return circle; 149 | }; 150 | 151 | /** 152 | * Create Circle from JSON element 153 | * 154 | * @param {object} json json element 155 | * @returns {Circle} the Circle object 156 | */ 157 | module.exports.fromJson = function fromJson(json){ 158 | var circle = new Circle(); 159 | 160 | if (typeof json !== 'undefined') { 161 | SvgObject.fromJson(circle, json); 162 | 163 | if (typeof json.cx !== 'undefined') { 164 | circle.cx = json.cx; 165 | } 166 | if (typeof json.cy !== 'undefined') { 167 | circle.cy = json.cy; 168 | } 169 | if (typeof json.r !== 'undefined') { 170 | circle.r = json.r; 171 | } 172 | } 173 | return circle; 174 | }; -------------------------------------------------------------------------------- /libs/objects/group.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var SvgObject = require(__dirname + '/svgobject'), 4 | Matrix = require(__dirname + '/../matrix/extends'), 5 | _ = require('underscore'), 6 | utils = require(__dirname + '/../matrix/utils'), 7 | async = require('async'), 8 | nUtil = require('util'); 9 | 10 | var Group = function(){ 11 | if (!(this instanceof Group)) 12 | throw 'this function in a constructor. Use new to call it'; 13 | 14 | SvgObject.call(this); 15 | this.type = "g"; 16 | this.childs = []; 17 | }; 18 | 19 | nUtil.inherits(Group, SvgObject); 20 | 21 | Group.prototype.toJSON = function(matrix){ 22 | var parentJSON = SvgObject.prototype.toJSON.call(this, matrix); 23 | 24 | parentJSON.type = this.type; 25 | parentJSON.childs = []; 26 | 27 | _.each(this.childs, function(child){ 28 | parentJSON.childs.push(child.toJSON(matrix)); 29 | }); 30 | 31 | return parentJSON; 32 | }; 33 | 34 | Group.prototype.toXml = function(matrix){ 35 | var xml = SvgObject.prototype.toXml.call(this, matrix); 36 | 37 | _.each(this.childs, function(child){ 38 | xml.importXMLBuilder(child.toXml(matrix)); 39 | }); 40 | 41 | return xml; 42 | }; 43 | 44 | /** 45 | * Find elements in Group and return new Group object with all elements by selected type 46 | * 47 | * @param {string} type Selected type (rect|polygon|g|...) 48 | * @param {boolean} all true : Return all elements in same level and children groups 49 | * @returns {Group} new Group object with selected types elements 50 | */ 51 | Group.prototype.findByType = function(type, all){ 52 | var group = new Group(), 53 | elems = []; 54 | 55 | _.each(this.childs, function(elem){ 56 | if(elem.type == type) 57 | elems.push(elem); 58 | 59 | if(all && elem.type == 'g'){ 60 | var subgroup = elem.findByType(type, all); 61 | elems = _.union(elems, subgroup.childs); 62 | } 63 | }); 64 | 65 | group.childs = _.union(group.childs, elems); 66 | 67 | return group; 68 | }; 69 | 70 | /** 71 | * Find elements in Group and return selected SvgObject 72 | * 73 | * @param {string} id Item id 74 | * @returns {SvgObject} SvgObject element 75 | */ 76 | Group.prototype.findById = function findById(id) { 77 | var returnElem = null; 78 | 79 | _.each(this.childs, function (elem) { 80 | if (elem.id == id) { 81 | returnElem = elem; 82 | }else if (elem.type == 'g') { 83 | returnElem = elem.findById(id); 84 | } 85 | }); 86 | 87 | return returnElem; 88 | }; 89 | 90 | /** 91 | * Find elements in Group and return selected SvgObject 92 | * 93 | * @param {string} id Item id 94 | * @param {string} type Item type (rect, path, ...) 95 | * @returns {SvgObject} SvgObject element 96 | */ 97 | Group.prototype.findByIdAndType = function findByIdAndType(id, type) { 98 | var returnElem = null; 99 | 100 | _.each(this.childs, function (elem) { 101 | if (elem.id == id && elem.type == type) { 102 | returnElem = elem; 103 | } else if (elem.type == 'g') { 104 | returnElem = elem.findByIdAndType(id, type); 105 | } 106 | }); 107 | 108 | return returnElem; 109 | }; 110 | 111 | /** 112 | * Find elements in Group and return selected SvgObject 113 | * 114 | * @param {string} id Item id 115 | * @param {string} type Item type (rect, path, ...) 116 | * @returns {SvgObject} SvgObject element 117 | */ 118 | Group.prototype.findByIdWithoutType = function findByIdWithoutType(id, type) { 119 | var returnElem = null; 120 | 121 | _.each(this.childs, function (elem) { 122 | if (elem.id == id && elem.type != type) { 123 | returnElem = elem; 124 | } else if (elem.type == 'g') { 125 | returnElem = elem.findByIdWithoutType(id, type); 126 | } 127 | }); 128 | 129 | return returnElem; 130 | }; 131 | 132 | /** 133 | * Remove All elements by type. If type is 'g', all childs elements are moved on svg root node. 134 | * @param {string} type Type to remove 135 | * @return {Array} List of elements of we moved outside the group 136 | */ 137 | Group.prototype.removeAllByType = function removeAllByType(type) { 138 | var elements = []; 139 | _.each(this.childs, function (elem) { 140 | if (elem.type === 'g') { 141 | var elms = elem.removeAllByType(type); 142 | if (type === 'g') { 143 | elements = _.union(elements, elms); 144 | } 145 | } 146 | if (elem.type !== type) { 147 | elements.push(elem); 148 | } 149 | }); 150 | 151 | this.childs = elements; 152 | return elements; 153 | }; 154 | 155 | Group.prototype.applyMatrix = function(matrix, callback){ 156 | var group = new Group(); 157 | group.style = this.style; 158 | group.classes = this.classes; 159 | group.id = this.id; 160 | group.name = this.name; 161 | group.stroke = this.stroke; 162 | group.fill = this.fill; 163 | group.type = this.type; 164 | group.data = this.data; 165 | 166 | async.each(this.childs, function(child, c){ 167 | child.getBBox(function(bbox){ 168 | var matrix2 = matrix.clone(); 169 | var childMatrix = Matrix.fromElement(bbox, child); 170 | matrix2.add(childMatrix); 171 | child.applyMatrix(matrix2, function(elem){ 172 | group.childs.push(elem); 173 | c(); 174 | }); 175 | }); 176 | }, function(){ 177 | callback(group); 178 | }); 179 | }; 180 | 181 | Group.prototype.getBBox = function(callback){ 182 | var minX = +Infinity, 183 | maxX = -Infinity, 184 | minY = +Infinity, 185 | maxY = -Infinity, 186 | self = this; 187 | 188 | async.each(this.childs, function(child, done){ 189 | child.getBBox(function(bbox){ 190 | minX = Math.min(minX, bbox.x); 191 | minY = Math.min(minY, bbox.y); 192 | maxX = Math.max(maxX, bbox.x2); 193 | maxY = Math.max(maxY, bbox.y2); 194 | done(); 195 | }); 196 | }, function(){ 197 | self.bbox = utils.bbox(minX,minY,maxX-minX,maxY-minY); 198 | callback(self.bbox); 199 | }); 200 | }; 201 | 202 | /** 203 | * Remove specifics types of objetcs in group 204 | * @param {object} params Function params 205 | * @param {string|Array} params.type target type(s) (g, rect, ...) 206 | */ 207 | Group.prototype.removeByType = function removeByType(params) { 208 | this.childs = _.filter(this.childs, function (element) { 209 | if (!_.isArray(params.type)) { 210 | var t = params.type; 211 | params.type = []; 212 | params.type.push(t); 213 | } 214 | 215 | if (element.type === 'g') { 216 | // remove elements for group 217 | element.removeByType(params); 218 | // if remove group type, add all elements in svg 219 | if (_.contains(params.type, 'g')) { 220 | _.union(this.childs, element.childs); 221 | } 222 | } 223 | 224 | return !_.contains(params.type, element.type); 225 | }, this); 226 | }; 227 | 228 | /** 229 | * Convert Svg elements into Path. 230 | * Works only on rect, polygon and polyline 231 | */ 232 | Group.prototype.convertElementsToPath = function convertElementsToPath() { 233 | var elements = []; 234 | 235 | _.each(this.childs, function (element) { 236 | switch(element.type) { 237 | case 'rect' : 238 | case 'polygon' : 239 | case 'polyline' : 240 | elements.push(element.toPath()); 241 | break; 242 | case 'g' : 243 | element.convertElementsToPath(); 244 | elements.push(element); 245 | break; 246 | default : 247 | elements.push(element); 248 | } 249 | }, this); 250 | 251 | this.childs = elements; 252 | }; 253 | 254 | /** 255 | * Calculate all innerboxes in Group. Return copy of current group with elements with data attribute innerbox. 256 | * @param {function} callback Callback Function 257 | */ 258 | Group.prototype.calculateAllInnerBoxes = function calculateAllInnerBoxes(callback) { 259 | var group = new Group(); 260 | group.style = this.style; 261 | group.classes = this.classes; 262 | group.id = this.id; 263 | group.name = this.name; 264 | group.stroke = this.stroke; 265 | group.fill = this.fill; 266 | group.type = this.type; 267 | group.data = this.data; 268 | async.each(this.childs, function (child, done) { 269 | switch (child.type) { 270 | case 'rect' : 271 | case 'polygon' : 272 | case 'polyline' : 273 | case 'circle' : 274 | child.getInnerBox(function (innerBox) { 275 | child.data.innerbox = innerBox; 276 | group.childs.push(child); 277 | done(); 278 | }); 279 | break; 280 | case 'g' : 281 | child.calculateAllInnerBoxes( function (group) { 282 | group.childs.push(child); 283 | done(); 284 | }); 285 | break; 286 | default : 287 | group.childs.push(child); 288 | done(); 289 | } 290 | }, function () { 291 | callback(group); 292 | }); 293 | }; 294 | 295 | module.exports = Group; 296 | 297 | module.exports.fromNode = function(node){ 298 | var group = new Group(); 299 | 300 | if(typeof node != 'undefined'){ 301 | SvgObject.fromNode(group, node); 302 | 303 | var Parser = require(__dirname + '/../parser'); 304 | 305 | group.childs = Parser.parseXmlNode(node); 306 | } 307 | return group; 308 | }; 309 | 310 | module.exports.fromJson = function(json){ 311 | var group = new Group(); 312 | 313 | if(typeof json != 'undefined'){ 314 | SvgObject.fromJson(group, json); 315 | 316 | var Parser = require(__dirname + '/../parser'); 317 | 318 | group.childs = Parser.parseJson(json.childs); 319 | } 320 | return group; 321 | }; -------------------------------------------------------------------------------- /libs/objects/image.js: -------------------------------------------------------------------------------- 1 | var SvgObject = require(__dirname + '/svgobject'), 2 | Matrix = require(__dirname + '/../matrix/extends'), 3 | _ = require('underscore'), 4 | utils = require(__dirname + '/../matrix/utils'), 5 | async = require('async'), 6 | nUtil = require('util'); 7 | 8 | var Img = function(){ 9 | if (!(this instanceof Img)) 10 | throw 'this function in a constructor. Use new to call it'; 11 | 12 | SvgObject.call(this); 13 | this.type = 'image'; 14 | this.href = ''; 15 | this.x = 0; 16 | this.y = 0; 17 | this.preserveAspectRatio = 'none'; 18 | this.width = 0; 19 | this.height = 0; 20 | }; 21 | 22 | nUtil.inherits(Img, SvgObject); 23 | 24 | Img.prototype.setX = function setX(x) { 25 | this.x = x; 26 | }; 27 | Img.prototype.setY = function setY(y) { 28 | this.y = y; 29 | }; 30 | Img.prototype.setWidth = function setWidth(w) { 31 | this.width = w; 32 | }; 33 | Img.prototype.setHeight = function setHeight(h) { 34 | this.height = h; 35 | }; 36 | Img.prototype.setHref = function setHref(href) { 37 | this.href = href; 38 | }; 39 | Img.prototype.setPreserveAspectRatio = function setPreserveAspectRatio(p) { 40 | this.preserveAspectRatio = p; 41 | }; 42 | 43 | /** 44 | * Return JSON from object 45 | * @param {boolean} [matrix] return transform attribute if false. 46 | * @returns {object} JSON Object 47 | */ 48 | Img.prototype.toJSON = function toJSON(matrix) { 49 | var parentJSON = SvgObject.prototype.toJSON.call(this, matrix); 50 | 51 | parentJSON.x = this.x; 52 | parentJSON.y = this.y; 53 | parentJSON.href = this.href; 54 | parentJSON.preserveAspectRatio = this.preserveAspectRatio; 55 | parentJSON.width = this.width; 56 | parentJSON.height = this.height; 57 | 58 | return parentJSON; 59 | }; 60 | 61 | /** 62 | * Return XML from object 63 | * @param {boolean} [matrix] return transform attribute if false. 64 | * @returns {xmlBuilder} XML Object 65 | */ 66 | Img.prototype.toXml = function toXml(matrix) { 67 | var xml = SvgObject.prototype.toXml.call(this, matrix); 68 | 69 | xml.att('x', this.x); 70 | xml.att('y', this.y); 71 | xml.att('xlink:href', this.href); 72 | xml.att('preserveAspectRatio', this.preserveAspectRatio); 73 | xml.att('width', this.width); 74 | xml.att('height', this.height); 75 | 76 | return xml; 77 | }; 78 | 79 | /** 80 | * Get the element Bounding Box 81 | * @param {function} callback Callback Function 82 | */ 83 | Img.prototype.getBBox = function getBBox(callback) { 84 | this.bbox = utils.bbox(this.x, this.y, this.width, this.height); 85 | callback(this.bbox); 86 | }; 87 | 88 | module.exports = Img; 89 | 90 | /** 91 | * Create Img from SVG image node 92 | * 93 | * @param {object} node xml2js node from SVG file 94 | * @returns {Img} the Img object 95 | */ 96 | module.exports.fromNode = function fromNode(node) { 97 | var image = new Img(); 98 | 99 | if (typeof node !== 'undefined' && typeof node.$ !== 'undefined') { 100 | SvgObject.fromNode(image, node); 101 | 102 | if (typeof node.$.x !== 'undefined') { 103 | image.x = parseFloat(node.$.x); 104 | } 105 | if (typeof node.$.y !== 'undefined') { 106 | image.y = parseFloat(node.$.y); 107 | } 108 | if (typeof node.$.width !== 'undefined') { 109 | image.width = parseFloat(node.$.width); 110 | } 111 | if (typeof node.$.height !== 'undefined') { 112 | image.height = parseFloat(node.$.height); 113 | } 114 | if (typeof node.$.href !== 'undefined') { 115 | image.href = node.$.href; 116 | } 117 | if (typeof node.$['xlink:href'] !== 'undefined') { 118 | image.href = node.$['xlink:href']; 119 | } 120 | if (typeof node.$.preserveAspectRatio !== 'undefined') { 121 | image.preserveAspectRatio = node.$.preserveAspectRatio; 122 | } 123 | } 124 | return image; 125 | }; 126 | 127 | /** 128 | * Create Img from JSON element 129 | * 130 | * @param {object} json json element 131 | * @returns {Img} the image object 132 | */ 133 | module.exports.fromJson = function fromJson(json){ 134 | var image = new Img(); 135 | 136 | if (typeof json != 'undefined') { 137 | SvgObject.fromJson(image, json); 138 | 139 | if (typeof json.x !== 'undefined') { 140 | image.x = json.x; 141 | } 142 | if (typeof json.y !== 'undefined') { 143 | image.y = json.y; 144 | } 145 | if (typeof json.width !== 'undefined') { 146 | image.width = json.width; 147 | } 148 | if (typeof json.height !== 'undefined') { 149 | image.height = json.height; 150 | } 151 | if (typeof json.href !== 'undefined') { 152 | image.href = json.href; 153 | } 154 | if (typeof json.preserveAspectRatio !== 'undefined') { 155 | image.preserveAspectRatio = json.preserveAspectRatio; 156 | } 157 | } 158 | return image; 159 | }; -------------------------------------------------------------------------------- /libs/objects/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | Group : require(__dirname + '/group'), 5 | Polygon : require(__dirname + '/polygon'), 6 | Rect : require(__dirname + '/rect'), 7 | SvgObject : require(__dirname + '/svgobject'), 8 | Text : require(__dirname + '/text'), 9 | Tspan : require(__dirname + '/tspan'), 10 | Image : require(__dirname + '/image'), 11 | Circle : require(__dirname + '/circle'), 12 | Path : require(__dirname + '/path') 13 | }; -------------------------------------------------------------------------------- /libs/objects/path.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var SvgObject = require(__dirname + "/svgobject"), 4 | utils = require(__dirname + '/../matrix/utils'), 5 | nUtil = require('util'); 6 | 7 | var Path = function(){ 8 | if (!(this instanceof Path)) 9 | throw 'this function in a constructor. Use new to call it'; 10 | 11 | SvgObject.call(this); 12 | this.type = 'path'; 13 | this.d = ""; 14 | }; 15 | 16 | nUtil.inherits(Path, SvgObject); 17 | 18 | /** 19 | * Set the path draw command (attribute d) 20 | * @param {string} d The draw command 21 | */ 22 | Path.prototype.setD = function setD(d) { 23 | this.d = d; 24 | }; 25 | 26 | /** 27 | * Convert Path element to JSON element 28 | * @param {boolean} [matrix] return transform attribute if false. 29 | * @returns {object} JSON representation of Path element 30 | */ 31 | Path.prototype.toJSON = function toJSON(matrix){ 32 | var parentJSON = SvgObject.prototype.toJSON.call(this, matrix); 33 | 34 | parentJSON.d = this.d; 35 | 36 | return parentJSON; 37 | }; 38 | 39 | /** 40 | * Convert Path Element to xml object 41 | * @param {boolean} [matrix] return transform attribute if false. 42 | * @returns {xmlBuilder} XML Object 43 | */ 44 | Path.prototype.toXml = function toXml(matrix) { 45 | var xml = SvgObject.prototype.toXml.call(this, matrix); 46 | 47 | xml.att('d', this.d); 48 | 49 | return xml; 50 | }; 51 | 52 | module.exports = Path; -------------------------------------------------------------------------------- /libs/objects/polygon.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Matrix = require(__dirname + '/../matrix/extends'), 4 | SvgObject = require(__dirname + '/svgobject'), 5 | utils = require(__dirname + '/../matrix/utils'), 6 | _ = require('underscore'), 7 | nUtil = require('util'); 8 | 9 | var EPSILON = 0.1; 10 | 11 | var Polygon = function() { 12 | if (!(this instanceof Polygon)) { 13 | throw 'this function in a constructor. Use new to call it'; 14 | } 15 | 16 | SvgObject.call(this); 17 | this.type = "polygon"; 18 | this.points = []; 19 | }; 20 | 21 | nUtil.inherits(Polygon, SvgObject); 22 | 23 | /** 24 | * Get Polygon points in Array to simply manipulation 25 | * 26 | * @param {string} points Polygon|Polyline points attribute value 27 | */ 28 | Polygon.prototype.setPointsFromString = function setPointsFromString(points) { 29 | var coords = [], 30 | point = {}, 31 | previousPoint = {}; 32 | 33 | points = points.replace(/ +(?= )/g, ''); 34 | _.each(points.split(/[, ]/), function (xy, index) { 35 | if (index%2 == 0) { 36 | point.x = xy; 37 | } else { 38 | point.y = xy; 39 | } 40 | 41 | if (index%2 == 1 && index > 0 && (point.x !== previousPoint.x || point.y !== previousPoint.y)) { 42 | coords.push(point); 43 | previousPoint = point; 44 | point = {}; 45 | } 46 | }); 47 | 48 | this.points = coords; 49 | this.bbox = undefined; 50 | }; 51 | 52 | Polygon.prototype.addPoint = function addPoint(x, y) { 53 | var different = true; 54 | if (this.points.length > 0) { 55 | var lastPoint = this.points[this.points.length-1]; 56 | different = lastPoint.x !== x || lastPoint.y !== y; 57 | } 58 | if (different) { 59 | this.points.push({ x : x, y : y }); 60 | } 61 | this.bbox = undefined; 62 | }; 63 | 64 | /** 65 | * Return JSON from object 66 | * @param {boolean} [matrix] return transform attribute if false. 67 | * @returns {object} JSON Object 68 | */ 69 | Polygon.prototype.toJSON = function toJSON(matrix) { 70 | var parentJSON = SvgObject.prototype.toJSON.call(this, matrix); 71 | 72 | parentJSON.type = this.type; 73 | parentJSON.points = this.points; 74 | 75 | return parentJSON; 76 | }; 77 | 78 | /** 79 | * Return XML from object 80 | * @param {boolean} [matrix] return transform attribute if false. 81 | * @returns {xmlBuilder} XML Object 82 | */ 83 | Polygon.prototype.toXml = function toXml(matrix) { 84 | 85 | var xml = SvgObject.prototype.toXml.call(this, matrix); 86 | 87 | var points = ""; 88 | _.each(this.points, function (point) { 89 | points += point.x + "," + point.y + " "; 90 | }); 91 | 92 | xml.att('points', points.substr(0, points.length-1)); 93 | 94 | return xml; 95 | }; 96 | 97 | /** 98 | * Return element converted into Path. 99 | * @return {Path} Path Object 100 | */ 101 | Polygon.prototype.toPath = function toPath() { 102 | var path = SvgObject.prototype.toPath.call(this); 103 | 104 | path.d = ""; 105 | this.points.forEach(function(point, index) { 106 | if(index == 0){ 107 | path.d += "M " + point.x + " " + point.y; 108 | } else { 109 | path.d += " L" + point.x + " " + point.y 110 | } 111 | }); 112 | path.d += ' Z'; 113 | 114 | return path; 115 | }; 116 | 117 | Polygon.prototype.applyMatrix = function applyMatrix(matrix, callback) { 118 | var polygon = new Polygon(); 119 | polygon.style = this.style; 120 | polygon.classes = this.classes; 121 | polygon.id = this.id; 122 | polygon.name = this.name; 123 | polygon.stroke = this.stroke; 124 | polygon.fill = this.fill; 125 | polygon.type = this.type; 126 | polygon.data = this.data; 127 | 128 | _.each(this.points, function (point) { 129 | polygon.addPoint( 130 | matrix.x(point.x, point.y), 131 | matrix.y(point.x, point.y) 132 | ); 133 | }); 134 | 135 | callback(polygon); 136 | }; 137 | 138 | /** 139 | * Get the element Bounding Box 140 | * @param {function} callback Callback Function 141 | */ 142 | Polygon.prototype.getBBox = function getBBox(callback) { 143 | var minX = +Infinity, 144 | maxX = -Infinity, 145 | minY = +Infinity, 146 | maxY = -Infinity; 147 | 148 | _.each(this.points, function (point) { 149 | minX = Math.min(point.x, minX); 150 | maxX = Math.max(point.x, maxX); 151 | minY = Math.min(point.y, minY); 152 | maxY = Math.max(point.y, maxY); 153 | }); 154 | 155 | 156 | this.bbox = utils.bbox(minX, minY, Math.abs(Math.abs(maxX) - Math.abs(minX)), Math.abs(Math.abs(maxY) - Math.abs(minY))); 157 | callback(this.bbox); 158 | }; 159 | 160 | /** 161 | * Get the element innerBox 162 | * @param {function} callback Callback function 163 | */ 164 | Polygon.prototype.getInnerBox = function getInnerBox(callback) { 165 | var verticesY = [], 166 | pointsCount = this.points.length, 167 | segments = [], 168 | prevY = Infinity, 169 | innerRect = { 170 | x : 0, 171 | y : 0, 172 | width : 0, 173 | height : 0 174 | }, 175 | segment; 176 | 177 | if (pointsCount === 0) { 178 | callback(innerRect); 179 | return; 180 | } 181 | 182 | _.each(this.points, function (point) { 183 | verticesY.push(point.y); 184 | }, this); 185 | verticesY = _.sortBy(verticesY, function (y) { 186 | return y; 187 | }); 188 | _.each(verticesY, function (y, i) { 189 | if (Math.abs(y - prevY) < EPSILON) { 190 | return; 191 | } 192 | if (i > 0) { 193 | segment = this._widestSegmentAtY(y-0.1); 194 | if (segment.width > 0) { 195 | segments.push(segment); 196 | } 197 | } 198 | if (i < pointsCount-1) { 199 | segment = this._widestSegmentAtY(y+0.1); 200 | if (segment.width > 0) { 201 | segments.push(segment); 202 | } 203 | } 204 | 205 | prevY = y; 206 | }, this); 207 | 208 | if (segments.length > 1) { 209 | var iSeg0 = 0, 210 | iSeg1 = 0, 211 | curRect = innerRect; 212 | 213 | for (iSeg0 = 0, iSeg1 = 1; iSeg1 < segments.length; iSeg0 += 2, iSeg1 += 2) { 214 | var segment0 = segments[iSeg0], 215 | segment1 = segments[iSeg1]; 216 | 217 | if (Math.abs(segment0.width - segment1.width) < EPSILON) { 218 | var x0 = Math.max(segment0.x, segment1.x), 219 | x1 = Math.min(segment0.x + segment0.width, segment1.x + segment1.width), 220 | width = x1 - x0; 221 | curRect = { 222 | x : x0, 223 | y : segment0.y, 224 | width : width, 225 | height : segment1.y - segment0.y 226 | }; 227 | } else { 228 | var point0, point1; 229 | 230 | if (segment1.width > segment0.width) { 231 | point0 = { 232 | x : 0.5 * (segment0.x + segment1.x), 233 | y : 0.5 * (segment0.y + segment1.y) 234 | }; 235 | point1 = { 236 | x : 0.5 * (segment0.x + segment0.width + segment1.x + segment1.width), 237 | y : 0.5 * (segment0.y + segment0.width + segment1.y + segment1.height) 238 | }; 239 | curRect = { 240 | x : point0.x, 241 | y : point0.y, 242 | width : point1.x - point0.x, 243 | height : segment1.y - point0.y 244 | } 245 | } else { 246 | point0 = { 247 | x : 0.5 * (segment0.x + segment1.x), 248 | y : 0.5 * (segment0.y + segment1.y) 249 | }; 250 | point1 = { 251 | x : 0.5 * (segment0.x + segment0.width + segment1.x + segment1.width), 252 | y : 0.5 * (segment0.y + segment0.width + segment1.y + segment1.height) 253 | }; 254 | curRect = { 255 | x : point0.x, 256 | y : segment0.y, 257 | width : point1.x - point0.x, 258 | height : point0.y - segment0.y 259 | } 260 | } 261 | } 262 | 263 | if ( 264 | curRect.width > innerRect.width || 265 | (curRect.width === innerRect.width && curRect.height > innerRect.height) 266 | ) { 267 | innerRect = curRect; 268 | } 269 | } 270 | } 271 | 272 | callback(innerRect); 273 | }; 274 | 275 | /** 276 | * Get segment at specific Y coordinate 277 | * @param {number} y Y coordinate 278 | * @returns {{x: number, y: number, width: number}} 279 | * @private 280 | */ 281 | Polygon.prototype._widestSegmentAtY = function _widestSegmentAtY(y) { 282 | var segment = { 283 | x : 0, 284 | y : y, 285 | width : 0 286 | }, 287 | pointsCount = this.points.length, 288 | xArray = [], 289 | i, j; 290 | 291 | if (pointsCount < 3) { 292 | return segment; 293 | } 294 | 295 | // compute all the intersections (x coordinates) 296 | for (i = 0, j = pointsCount-1; i < pointsCount; j = i++) { 297 | var point1 = this.points[i], 298 | point2 = this.points[j]; 299 | if ((point1.y > y) != (point2.y > y)) { 300 | if (Math.abs(point2.x - point1.x) < EPSILON) { 301 | xArray.push(point1.x); 302 | } else { 303 | // y = a x + b 304 | var a = (point2.y - point1.y)/(point2.x - point1.x), 305 | b = point2.y - a * point2.x, 306 | x = (y - b)/a; 307 | if (x >= Math.min(point2.x, point1.x) && x <= Math.max(point2.x, point1.x)) { 308 | xArray.push(x); 309 | } 310 | } 311 | } 312 | } 313 | 314 | xArray = _.sortBy(xArray, function (x) { 315 | return x; 316 | }); 317 | 318 | for (i = 0, j = 1; j < xArray.length; i+=2, j+=2) { 319 | var width = xArray[j] - xArray[i]; 320 | if (width > segment.width) { 321 | segment.x = xArray[i]; 322 | segment.width = width; 323 | } 324 | } 325 | 326 | return segment; 327 | }; 328 | 329 | module.exports = Polygon; 330 | 331 | /** 332 | * Create Polygon from SVG polygon|polyline node 333 | * 334 | * @param {object} node xml2js node from SVG file 335 | * @param {boolean} [line] true : polyline, false : polygon. False as default 336 | * @returns {Polygon} the polygon object 337 | */ 338 | module.exports.fromNode = function fromNode(node, line) { 339 | var polygon = new Polygon(); 340 | 341 | if (line == true) { 342 | polygon.type = 'polyline'; 343 | } 344 | if (typeof node != 'undefined' && typeof node.$ != 'undefined') { 345 | SvgObject.fromNode(polygon, node); 346 | 347 | if (typeof node.$.points != 'undefined') { 348 | polygon.setPointsFromString(node.$.points); 349 | } 350 | } 351 | 352 | return polygon; 353 | }; 354 | 355 | /** 356 | * Create Polygon From JSON object 357 | * @param {object} json JSON polygon Object 358 | * @param {boolean} line True : polyline, false : polygon 359 | * @returns {Polygon} 360 | */ 361 | module.exports.fromJson = function fromJson(json, line) { 362 | 363 | var polygon = new Polygon(); 364 | 365 | if (line == true) { 366 | polygon.type = 'polyline'; 367 | } 368 | if (typeof json != 'undefined') { 369 | SvgObject.fromJson(polygon, json); 370 | 371 | if (typeof json.points != 'undefined') { 372 | polygon.points = json.points; 373 | } 374 | } 375 | 376 | return polygon; 377 | }; -------------------------------------------------------------------------------- /libs/objects/rect.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var SvgObject = require(__dirname + "/svgobject"), 4 | utils = require(__dirname + '/../matrix/utils'), 5 | Polygon = require(__dirname + '/polygon'), 6 | nUtil = require('util'); 7 | 8 | var Rect = function(){ 9 | if (!(this instanceof Rect)) 10 | throw 'this function in a constructor. Use new to call it'; 11 | 12 | SvgObject.call(this); 13 | this.type = 'rect'; 14 | this.x = 0; 15 | this.y = 0; 16 | this.width = 0; 17 | this.height = 0; 18 | this.rx = 0; 19 | this.ry = 0; 20 | }; 21 | 22 | nUtil.inherits(Rect, SvgObject); 23 | 24 | /** 25 | * Set X origin 26 | * @param {number} x X origin 27 | */ 28 | Rect.prototype.setX = function setX(x){ 29 | this.x = x; 30 | }; 31 | 32 | /** 33 | * Set Y origin 34 | * @param {number} y y origin 35 | */ 36 | Rect.prototype.setY = function setY(y){ 37 | this.y = y; 38 | }; 39 | 40 | /** 41 | * Set Width 42 | * @param {number} width Rect width 43 | */ 44 | Rect.prototype.setWidth = function setWidth(width){ 45 | this.width = width; 46 | }; 47 | 48 | /** 49 | * Set Height 50 | * @param {number} height Rect height 51 | */ 52 | Rect.prototype.setHeight = function setHeight(height){ 53 | this.height = height; 54 | }; 55 | 56 | /** 57 | * Set rounded x corner 58 | * @param {number} rx rounded x corner 59 | */ 60 | Rect.prototype.setRx = function setRx(rx){ 61 | this.rx = rx; 62 | }; 63 | 64 | /** 65 | * Set rounded y corner 66 | * @param {number} ry rounded y corner 67 | */ 68 | Rect.prototype.setRy = function setRy(ry){ 69 | this.ry = ry; 70 | }; 71 | 72 | Rect.prototype.toJSON = function toJSON(matrix){ 73 | var parentJSON = SvgObject.prototype.toJSON.call(this, matrix); 74 | 75 | parentJSON.x = this.x; 76 | parentJSON.y = this.y; 77 | parentJSON.rx = this.rx; 78 | parentJSON.ry = this.ry; 79 | parentJSON.width = this.width; 80 | parentJSON.height = this.height; 81 | 82 | return parentJSON; 83 | }; 84 | 85 | Rect.prototype.toXml = function toXml(matrix){ 86 | var xml = SvgObject.prototype.toXml.call(this, matrix); 87 | 88 | xml.att('x', this.x); 89 | xml.att('y', this.y); 90 | xml.att('width', this.width); 91 | xml.att('height', this.height); 92 | xml.att('rx', this.rx); 93 | xml.att('ry', this.ry); 94 | 95 | return xml; 96 | }; 97 | 98 | /** 99 | * Return element converted into Path. 100 | * @return {Path} Path Object 101 | */ 102 | Rect.prototype.toPath = function toPath() { 103 | var path = SvgObject.prototype.toPath.call(this); 104 | 105 | path.d = 'M' + this.x + ' ' + this.y + 106 | ' L' + this.x + ' ' + (this.y+this.height) + 107 | ' L' + (this.x+this.width) + ' ' + (this.y+this.height) + 108 | ' L' + (this.x+this.width) + ' ' + this.y + 109 | ' Z'; 110 | 111 | return path; 112 | }; 113 | 114 | Rect.prototype.applyMatrix = function applyMatrix(matrix, callback){ 115 | var polygon = new Polygon(); 116 | polygon.style = this.style; 117 | polygon.classes = this.classes; 118 | polygon.id = this.id; 119 | polygon.name = this.name; 120 | polygon.stroke = this.stroke; 121 | polygon.fill = this.fill; 122 | polygon.data = this.data; 123 | 124 | polygon.addPoint( 125 | matrix.x(this.x, this.y), 126 | matrix.y(this.x, this.y) 127 | ); 128 | polygon.addPoint( 129 | matrix.x(this.x+this.width, this.y), 130 | matrix.y(this.x+this.width, this.y) 131 | ); 132 | polygon.addPoint( 133 | matrix.x(this.x+this.width, this.y+this.height), 134 | matrix.y(this.x+this.width, this.y+this.height) 135 | ); 136 | polygon.addPoint( 137 | matrix.x(this.x, this.y+this.height), 138 | matrix.y(this.x, this.y+this.height) 139 | ); 140 | 141 | callback(polygon); 142 | }; 143 | 144 | /** 145 | * Get the element Bounding Box 146 | * @param {function} callback Callback Function 147 | */ 148 | Rect.prototype.getBBox = function getBBox(callback){ 149 | this.bbox = utils.bbox(this.x, this.y, this.width, this.height); 150 | callback(this.bbox); 151 | }; 152 | 153 | /** 154 | * Get the element innerBox 155 | * @param {function} callback Callback function 156 | */ 157 | Rect.prototype.getInnerBox = function getInnerBox(callback) { 158 | this.getBBox(callback); 159 | }; 160 | 161 | module.exports = Rect; 162 | 163 | /** 164 | * Create Rect from SVG rect node 165 | * @param {object} node xml2js node from SVG file 166 | * @returns {Rect} the rect object 167 | */ 168 | module.exports.fromNode = function fromNode(node){ 169 | var rect = new Rect(); 170 | 171 | if(typeof node != 'undefined' && typeof node.$ != 'undefined'){ 172 | SvgObject.fromNode(rect, node); 173 | 174 | if(typeof node.$.x != 'undefined'){ 175 | rect.x = parseFloat(node.$.x); 176 | } 177 | if(typeof node.$.y != 'undefined'){ 178 | rect.y = parseFloat(node.$.y); 179 | } 180 | if(typeof node.$.rx != 'undefined'){ 181 | rect.rx = parseFloat(node.$.rx); 182 | } 183 | if(typeof node.$.ry != 'undefined'){ 184 | rect.ry = parseFloat(node.$.ry); 185 | } 186 | if(typeof node.$.width != 'undefined'){ 187 | rect.width = parseFloat(node.$.width); 188 | } 189 | if(typeof node.$.height != 'undefined'){ 190 | rect.height = parseFloat(node.$.height); 191 | } 192 | } 193 | 194 | return rect; 195 | }; 196 | 197 | /** 198 | * Create Rect from JSON element 199 | * @param {object} json json element 200 | * @returns {Rect} the rect object 201 | */ 202 | module.exports.fromJson = function fromJson(json){ 203 | var rect = new Rect(); 204 | 205 | if(typeof json != 'undefined'){ 206 | SvgObject.fromJson(rect, json); 207 | 208 | if(typeof json.x != 'undefined'){ 209 | rect.x = json.x; 210 | } 211 | if(typeof json.y != 'undefined'){ 212 | rect.y = json.y; 213 | } 214 | if(typeof json.rx != 'undefined'){ 215 | rect.rx = json.rx; 216 | } 217 | if(typeof json.ry != 'undefined'){ 218 | rect.ry = json.ry; 219 | } 220 | if(typeof json.width != 'undefined'){ 221 | rect.width = json.width; 222 | } 223 | if(typeof json.height != 'undefined'){ 224 | rect.height = json.height; 225 | } 226 | } 227 | 228 | return rect; 229 | }; 230 | 231 | -------------------------------------------------------------------------------- /libs/objects/svgobject.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _ = require('underscore'), 4 | builder = require('xmlbuilder'), 5 | async = require('async'), 6 | Matrix = require(__dirname + '/../matrix/extends'); 7 | 8 | /** 9 | * 10 | * @constructor 11 | */ 12 | var SvgObject = function(){ 13 | if (!(this instanceof SvgObject)) 14 | throw 'this function in a constructor. Use new to call it'; 15 | 16 | this.classes = []; 17 | this.id = ""; 18 | this.name = ""; 19 | this.stroke = ""; 20 | this.fill = ""; 21 | this.style = {}; 22 | this.transform = undefined; 23 | this.data = {}; 24 | this.bbox = undefined; 25 | }; 26 | 27 | 28 | /** 29 | * Set classes 30 | * 31 | * @param {string} classes Classes separate by space 32 | */ 33 | SvgObject.prototype.setClassesFromString = function setClassesFromString(classes){ 34 | this.classes = classes.split(" "); 35 | }; 36 | 37 | /** 38 | * Add specific class to svgObject 39 | * @param {string} className Class Name 40 | */ 41 | SvgObject.prototype.addClass = function addClass(className) { 42 | this.classes.push(className); 43 | }; 44 | 45 | /** 46 | * Set Stroke Color 47 | * 48 | * @param {string} stroke Stroke Color (default : black) 49 | */ 50 | SvgObject.prototype.setStroke = function setStroke(stroke){ 51 | this.stroke = stroke; 52 | }; 53 | 54 | /** 55 | * Set Fill color 56 | * 57 | * @param {string} fill Fill Color (default : black) 58 | */ 59 | SvgObject.prototype.setFill = function setFill(fill){ 60 | this.fill = fill; 61 | }; 62 | 63 | /** 64 | * Set ID 65 | * @param {string} id ID for element 66 | */ 67 | SvgObject.prototype.setId = function setId(id){ 68 | this.id = id; 69 | this.data.id = id; 70 | }; 71 | 72 | /** 73 | * Set Name 74 | * @param {string} name Name 75 | */ 76 | SvgObject.prototype.setName = function setName(name){ 77 | this.name = name; 78 | this.data.name = name; 79 | }; 80 | 81 | /** 82 | * Set Transformation String 83 | * @param {string} transform Transformation in SVG format 84 | */ 85 | SvgObject.prototype.setTransform = function setTransform(transform){ 86 | this.transform = transform; 87 | }; 88 | 89 | /** 90 | * Set all data attrbutes for element 91 | * @param {object} data Data Object 92 | */ 93 | SvgObject.prototype.setData = function setData(data){ 94 | this.data = data; 95 | }; 96 | 97 | /** 98 | * Set Style from css string (color:#aaaaaa; background-color:#ffffff;) 99 | * @param {string} styleStr Element style like the style html attribute value. Each styles are separate by ';' 100 | */ 101 | SvgObject.prototype.setStyleFromString = function setStyleFromString(styleStr){ 102 | styleStr.split(';').forEach(function(line){ 103 | if(line.length > 0){ 104 | var style = line.split(':'); 105 | this.style[style[0].replace(/^\s+|\s+$/g, '')] = style[1].replace(/^\s+|\s+$/g, ''); 106 | } 107 | }, this); 108 | }; 109 | 110 | /** 111 | * Set Style 112 | * @param {object} style Element style object 113 | */ 114 | SvgObject.prototype.setStyle = function setStyle(style){ 115 | this.style = style; 116 | }; 117 | 118 | /** 119 | * get Element classes 120 | * @returns {Array} 121 | */ 122 | SvgObject.prototype.getClasses = function getClasses(){ 123 | return this.classes; 124 | }; 125 | 126 | /** 127 | * get Element id 128 | * @returns {string} 129 | */ 130 | SvgObject.prototype.getId = function getId(){ 131 | return this.id; 132 | }; 133 | 134 | /** 135 | * get Element Name 136 | * @returns {string} 137 | */ 138 | SvgObject.prototype.getName = function getName(){ 139 | return this.name; 140 | }; 141 | 142 | /** 143 | * get Element Stroke 144 | * @returns {string} 145 | */ 146 | SvgObject.prototype.getStroke = function getStroke(){ 147 | return this.stroke; 148 | }; 149 | 150 | /** 151 | * Get Element fill 152 | * @returns {string} 153 | */ 154 | SvgObject.prototype.getFill = function getFill(){ 155 | return this.fill; 156 | }; 157 | 158 | /** 159 | * get Element Style 160 | * @returns {object} 161 | */ 162 | SvgObject.prototype.getStyle = function getStyle(){ 163 | return this.style; 164 | }; 165 | 166 | /** 167 | * get transformation matrix 168 | * @returns {undefined|object} 169 | */ 170 | SvgObject.prototype.getTransform = function getTransform(){ 171 | return this.transform; 172 | }; 173 | 174 | /** 175 | * get Element data 176 | * @returns {object} 177 | */ 178 | SvgObject.prototype.getData = function getData(){ 179 | return this.data; 180 | }; 181 | 182 | /** 183 | * Return JSON from object 184 | * @param {boolean} [matrix] return transform attribute if false. 185 | * @returns {object} JSON Object 186 | */ 187 | SvgObject.prototype.toJSON = function toJSON(matrix){ 188 | 189 | var json = { 190 | type : this.type 191 | }; 192 | 193 | if(this.classes.length > 0) 194 | json.classes = this.classes; 195 | if(this.id != null && this.id != '') 196 | json.id = this.id; 197 | if(this.name != null && this.name != '') 198 | json.name = this.name; 199 | if(this.stroke != null && this.stroke != '') 200 | json.stroke = this.stroke; 201 | if(this.fill != null && this.fill != '') 202 | json.fill = this.fill; 203 | if(!_.isEmpty(this.style)) 204 | json.style = this.style; 205 | if(!_.isEmpty(this.data)) 206 | json.data = this.data; 207 | if (this.bbox) { 208 | json.bbox = this.bbox; 209 | } 210 | if(typeof this.transform != 'undefined' && matrix != true) 211 | json.transform = this.transform; 212 | 213 | return json; 214 | }; 215 | 216 | /** 217 | * Return XML from object 218 | * @param {boolean} [matrix] return transform attribute if false. 219 | * @returns {xmlBuilder} XML Object 220 | */ 221 | SvgObject.prototype.toXml = function toXml(matrix){ 222 | var xml = builder.create(this.type); 223 | 224 | var style = ""; 225 | _.each(this.style, function(value, index){ 226 | style += index + ":" + value + ";" 227 | }); 228 | 229 | if(this.classes.length > 0) 230 | xml.att("class", this.classes.join(" ")); 231 | if(typeof this.transform != 'undefined' && matrix != true) 232 | xml.att("transform", this.transform); 233 | if(this.id.length > 0) 234 | xml.att("id", this.id); 235 | if(this.name.length > 0) 236 | xml.att("name", this.name); 237 | if(this.stroke.length > 0) 238 | xml.att("stroke", this.stroke); 239 | if(this.fill.length > 0) 240 | xml.att("fill", this.fill); 241 | if (this.bbox) { 242 | xml.att("data-bbox", JSON.stringify(this.bbox)); 243 | } 244 | 245 | _.each(this.data, function(value, key){ 246 | if(key && value){ 247 | xml.att("data-" + key, typeof value == "string" ? value : JSON.stringify(value)); 248 | } 249 | }); 250 | 251 | xml.att("style", style); 252 | 253 | return xml; 254 | }; 255 | 256 | /** 257 | * Return element with XML String representation 258 | * @returns {string} Element convert to String 259 | */ 260 | SvgObject.prototype.toString = function toString(){ 261 | return this.toXml().toString(); 262 | }; 263 | 264 | /** 265 | * Return element converted into Path. 266 | * For moment, works only with rect, polygon and polyline 267 | * Others return empty path 268 | * @return {Path} Path Object 269 | */ 270 | SvgObject.prototype.toPath = function toPath() { 271 | var Path = require(__dirname + "/path"); 272 | var path = new Path(); 273 | path.classes = this.classes; 274 | path.id = this.id; 275 | path.name = this.name; 276 | path.stroke = this.stroke; 277 | path.fill = this.fill; 278 | path.style = this.style; 279 | path.transform = this.transform; 280 | path.bbox = this.bbox; 281 | path.data = this.data; 282 | 283 | return path; 284 | }; 285 | 286 | /** 287 | * Get the element Bounding Box 288 | * @param {function} callback Callback Function 289 | */ 290 | SvgObject.prototype.getBBox = function getBBox(callback){ 291 | callback(this.bbox); 292 | }; 293 | 294 | /** 295 | * Return Matrix representation of current transform attribute. 296 | * @param {function} callback Callback function 297 | */ 298 | SvgObject.prototype.getCurrentMatrix = function getCurrentMatrix(callback){ 299 | var self = this; 300 | this.getBBox(function(bbox){ 301 | callback(Matrix.fromElement(bbox, self)); 302 | }); 303 | }; 304 | 305 | /** 306 | * Get the element innerBox 307 | * @param {function} callback 308 | */ 309 | SvgObject.prototype.getInnerBox = function getInnerBox(callback) { 310 | callback({ 311 | x : 0, 312 | y : 0, 313 | width : 0, 314 | height : 0 315 | }); 316 | }; 317 | 318 | /** 319 | * Indicates whether an other svgObject is contained in this svgObject 320 | * @param {SvgObject} svgObject SvgObject 321 | * @param {function} callback Callback function 322 | */ 323 | SvgObject.prototype.contains = function contains(svgObject, callback){ 324 | var self = this; 325 | async.parallel({ 326 | ownBbox : function(c){ 327 | self.getBBox(function(bbox){ 328 | c(null, bbox); 329 | }); 330 | }, 331 | objBBox : function(c){ 332 | svgObject.getBBox(function(bbox){ 333 | c(null, bbox); 334 | }); 335 | } 336 | }, function(err, result){ 337 | var ownPoints = [ 338 | { x : result.ownBbox.x, y : result.ownBbox.y }, 339 | { x : result.ownBbox.x2, y : result.ownBbox.y }, 340 | { x : result.ownBbox.x2, y : result.ownBbox.y2 }, 341 | { x : result.ownBbox.x, y : result.ownBbox.y2 } 342 | ], 343 | objPoints = [ 344 | { x : result.objBBox.x, y : result.objBBox.y }, 345 | { x : result.objBBox.x2, y : result.objBBox.y }, 346 | { x : result.objBBox.x2, y : result.objBBox.y2 }, 347 | { x : result.objBBox.x, y : result.objBBox.y2 } 348 | ]; 349 | 350 | var c = [ false, false, false, false ]; 351 | 352 | for(var i = 0; i < objPoints.length; ++i){ 353 | for(var j = 0, k = ownPoints.length -1; j < ownPoints.length; k = j++){ 354 | if( 355 | ((ownPoints[j].y > objPoints[i].y) != (ownPoints[k].y > objPoints[i].y)) && 356 | (objPoints[i].x < (ownPoints[k].x - ownPoints[j].x) * (objPoints[i].y - ownPoints[j].y) / (ownPoints[k].y - ownPoints[j].y) + ownPoints[j].x) 357 | ){ 358 | c[i] = !c[i]; 359 | } 360 | } 361 | } 362 | 363 | if(c[0] == true || c[1] == true || c[2] == true || c[3] == true){ 364 | callback(true); 365 | return; 366 | } 367 | 368 | callback(false); 369 | }); 370 | }; 371 | 372 | module.exports = SvgObject; 373 | 374 | /** 375 | * Generate SVGElement from SVG node 376 | * 377 | * @param {SvgObject} object SvgObject element (polygon|group|... 378 | * @param {object} node xml2js element 379 | */ 380 | module.exports.fromNode = function fromNode(object, node){ 381 | if(typeof node != 'undefined' && typeof node.$ != 'undefined'){ 382 | if(typeof node.$['class'] != 'undefined'){ 383 | object.setClassesFromString(node.$['class']); 384 | } 385 | if(typeof node.$.id != 'undefined'){ 386 | object.setId(node.$.id); 387 | object.setName(node.$.id); 388 | } 389 | if(typeof node.$.stroke != 'undefined'){ 390 | object.setStroke(node.$.stroke); 391 | } 392 | if(typeof node.$.fill != 'undefined'){ 393 | object.setFill(node.$.fill); 394 | } 395 | if(typeof node.$.style != 'undefined'){ 396 | object.setStyleFromString(node.$.style); 397 | } 398 | if(typeof node.$.transform != 'undefined'){ 399 | object.setTransform(node.$.transform); 400 | } 401 | _.each(node.$, function(value, index){ 402 | var match = index.match(/(?:^|\s)data-(.*?)(?:$|\s)\b/); 403 | if(match){ 404 | try{ 405 | object.data[match[1]] = JSON.parse(value); 406 | }catch(e){ 407 | object.data[match[1]] = value; 408 | } 409 | } 410 | }); 411 | } 412 | }; 413 | 414 | /** 415 | * Generate SVGElement from JSON element 416 | * 417 | * @param {SvgObject} object SvgObject element (polygon|group|... 418 | * @param {object} json json element 419 | */ 420 | module.exports.fromJson = function fromJson(object, json){ 421 | if(typeof json != 'undefined'){ 422 | if(typeof json.classes != 'undefined'){ 423 | object.classes = json.classes; 424 | } 425 | if(typeof json.id != 'undefined'){ 426 | object.setId(json.id); 427 | object.setName(json.id); 428 | } 429 | if(typeof json.name != 'undefined'){ 430 | object.setName(json.id); 431 | } 432 | if(typeof json.stroke != 'undefined'){ 433 | object.stroke = json.stroke; 434 | } 435 | if(typeof json.fill != 'undefined'){ 436 | object.fill = json.fill; 437 | } 438 | if(typeof json.style != 'undefined'){ 439 | object.style = json.style; 440 | } 441 | if(typeof json.transform != 'undefined'){ 442 | object.transform = json.transform; 443 | } 444 | if(typeof json.data != 'undefined'){ 445 | object.data = json.data; 446 | } 447 | } 448 | }; -------------------------------------------------------------------------------- /libs/objects/text.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var SvgObject = require(__dirname + '/svgobject'), 4 | _ = require('underscore'), 5 | Tspan = require(__dirname + '/tspan'), 6 | utils = require(__dirname + '/../matrix/utils'), 7 | async = require('async'), 8 | nUtil = require('util'); 9 | 10 | var Text = function(){ 11 | if (!(this instanceof Text)) 12 | throw 'this function in a constructor. Use new to call it'; 13 | 14 | SvgObject.call(this); 15 | this.type = 'text'; 16 | this.value = ""; 17 | this.x = 0; 18 | this.y = 0; 19 | this.childs = []; 20 | }; 21 | 22 | nUtil.inherits(Text, SvgObject); 23 | 24 | /** 25 | * Set X origin 26 | * @param {number} x X origin 27 | */ 28 | Text.prototype.setX = function setX(x){ 29 | this.x = x; 30 | }; 31 | 32 | /** 33 | * Set Y origin 34 | * @param {number} y y origin 35 | */ 36 | Text.prototype.setY = function setY(y){ 37 | this.y = y; 38 | }; 39 | 40 | /** 41 | * Set Text Value 42 | * @param {string} string Text value 43 | */ 44 | Text.prototype.setValue = function setValue(string){ 45 | this.value = string; 46 | }; 47 | 48 | /** 49 | * Set Text children 50 | * @param {Array} childs Tspan Array 51 | */ 52 | Text.prototype.setChildren = function setChildren(childs){ 53 | this.childs = childs; 54 | }; 55 | 56 | /** 57 | * Get X origin 58 | * @returns {number} 59 | */ 60 | Text.prototype.getX = function getX(){ 61 | return this.x; 62 | }; 63 | 64 | /** 65 | * Get Y origin 66 | * @returns {number} 67 | */ 68 | Text.prototype.getY = function getY(){ 69 | return this.y; 70 | }; 71 | 72 | /** 73 | * Get Tspan Value 74 | * @returns {string} 75 | */ 76 | Text.prototype.getValue = function getValue(){ 77 | return this.value; 78 | }; 79 | 80 | /** 81 | * Get Text children 82 | * @returns {Array} 83 | */ 84 | Text.prototype.getChildren = function getChildren(){ 85 | return this.childs; 86 | } 87 | 88 | /** 89 | * Add Tspan child 90 | * @param {Tspan} child Tspan child 91 | */ 92 | Text.prototype.addChild = function addChild(child){ 93 | this.childs.push(child); 94 | }; 95 | 96 | /** 97 | * Return JSON from object 98 | * @param {boolean} [matrix] return transform attribute if false. 99 | * @returns {object} JSON Object 100 | */ 101 | Text.prototype.toJSON = function toJSON(matrix){ 102 | var parentJSON = SvgObject.prototype.toJSON.call(this, matrix); 103 | 104 | parentJSON.value = this.value; 105 | parentJSON.x = this.x; 106 | parentJSON.y = this.y; 107 | 108 | parentJSON.childs = []; 109 | 110 | _.each(this.childs, function(child){ 111 | parentJSON.childs.push(child.toJSON()); 112 | }); 113 | 114 | return parentJSON; 115 | }; 116 | 117 | /** 118 | * Return XML from object 119 | * @param {boolean} [matrix] return transform attribute if false. 120 | * @returns {xmlBuilder} XML Object 121 | */ 122 | Text.prototype.toXml = function toXml(matrix){ 123 | var xml = SvgObject.prototype.toXml.call(this, matrix); 124 | 125 | xml.att('x', this.x + 'px'); 126 | xml.att('y', this.y + 'px'); 127 | xml.txt(this.value); 128 | 129 | _.each(this.childs, function(child){ 130 | xml.importXMLBuilder(child.toXml()); 131 | }); 132 | 133 | return xml; 134 | }; 135 | 136 | /** 137 | * Apply param Matrix and callback new SvgObject with this matrix 138 | * @param {object} matrix Matrix to apply 139 | * @param {function} callback Callback Function 140 | */ 141 | Text.prototype.applyMatrix = function applyMatrix(matrix, callback){ 142 | var text = new Text(); 143 | text.style = this.style; 144 | text.classes = this.classes; 145 | text.id = this.id; 146 | text.name = this.name; 147 | text.stroke = this.stroke; 148 | text.fill = this.fill; 149 | text.type = this.type; 150 | text.value = this.value; 151 | text.x = matrix.x(this.x, this.y); 152 | text.y = matrix.y(this.x, this.y); 153 | text.data = this.data; 154 | 155 | async.each(this.childs, function(child, c){ 156 | child.applyMatrix(matrix, function(tspan){ 157 | text.childs.push(tspan); 158 | c(); 159 | }); 160 | }, function(){ 161 | callback(text); 162 | }); 163 | }; 164 | 165 | /** 166 | * Get the element Bounding Box 167 | * @param {function} callback Callback Function 168 | */ 169 | Text.prototype.getBBox = function getBBox(callback){ 170 | this.bbox = utils.bbox(this.x, this.y, 9, 30); // ok, it's not true but, it's ok for basic short text with basic police 171 | callback(this.bbox); 172 | }; 173 | 174 | module.exports = Text; 175 | 176 | /** 177 | * Generate SVGElement from SVG node 178 | * @param {object} node xml2js element 179 | */ 180 | module.exports.fromNode = function fromNode(node){ 181 | var text = new Text(); 182 | 183 | SvgObject.fromNode(text, node); 184 | 185 | if(typeof node != 'undefined'){ 186 | if(typeof node.$ != 'undefined'){ 187 | if(typeof node.$.x != 'undefined'){ 188 | text.x = parseFloat(node.$.x); 189 | } 190 | if(typeof node.$.y != 'undefined'){ 191 | text.y = parseFloat(node.$.y); 192 | } 193 | } 194 | 195 | if(typeof node._ != 'undefined'){ 196 | text.value = node._; 197 | 198 | if(_.isEmpty(text.id)){ 199 | text.setId(node._.replace(/[`~!@#$%^&*()|+\=?;:'",<>\{\}\[\]\\\/ ]/img,'')); 200 | } 201 | } 202 | 203 | if(typeof node.tspan != 'undefined'){ 204 | // we are children 205 | _.each(node.tspan, function(tspan){ 206 | text.childs.push(Tspan.fromNode(tspan)); 207 | }); 208 | } 209 | } 210 | 211 | return text; 212 | }; 213 | 214 | /** 215 | * Generate SVGElement from JSON element 216 | * @param {object} json json element 217 | */ 218 | module.exports.fromJson = function fromJson(json){ 219 | var text = new Text(); 220 | 221 | if(typeof json != 'undefined'){ 222 | SvgObject.fromJson(text, json); 223 | 224 | if(typeof json.value != 'undefined'){ 225 | text.value = json.value; 226 | 227 | if(_.isEmpty(text.id)){ 228 | text.setId(json.value.replace(/[`~!@#$%^&*()|+\=?;:'",.<>\{\}\[\]\\\/ ]/img,'')); 229 | } 230 | } 231 | if(typeof json.x != 'undefined'){ 232 | text.x = json.x; 233 | } 234 | if(typeof json.y != 'undefined'){ 235 | text.y = json.y; 236 | } 237 | if(typeof json.childs != 'undefined'){ 238 | _.each(json.childs, function(tspan){ 239 | text.childs.push(Tspan.fromJson(tspan)); 240 | }); 241 | } 242 | } 243 | 244 | return text; 245 | }; -------------------------------------------------------------------------------- /libs/objects/tspan.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var SvgObject = require(__dirname + '/svgobject'), 4 | nUtil = require('util'); 5 | 6 | var Tspan = function(){ 7 | if (!(this instanceof Tspan)) 8 | throw 'this function in a constructor. Use new to call it'; 9 | 10 | SvgObject.call(this); 11 | this.type = 'tspan'; 12 | this.value = ""; 13 | this.x = 0; 14 | this.y = 0; 15 | }; 16 | 17 | nUtil.inherits(Tspan, SvgObject); 18 | 19 | /** 20 | * Set X origin 21 | * @param {number} x X origin 22 | */ 23 | Tspan.prototype.setX = function setX(x){ 24 | this.x = x; 25 | }; 26 | 27 | /** 28 | * Set Y origin 29 | * @param {number} y y origin 30 | */ 31 | Tspan.prototype.setY = function setY(y){ 32 | this.y = y; 33 | }; 34 | 35 | /** 36 | * Set Text Value 37 | * @param {string} string Text value 38 | */ 39 | Tspan.prototype.setValue = function setValue(string){ 40 | this.value = string; 41 | }; 42 | 43 | /** 44 | * Get X origin 45 | * @returns {number} 46 | */ 47 | Tspan.prototype.getX = function getX(){ 48 | return this.x; 49 | }; 50 | 51 | /** 52 | * Get Y origin 53 | * @returns {number} 54 | */ 55 | Tspan.prototype.getY = function getY(){ 56 | return this.y; 57 | }; 58 | 59 | /** 60 | * Get Tspan Value 61 | * @returns {string} 62 | */ 63 | Tspan.prototype.getValue = function getValue(){ 64 | return this.value; 65 | }; 66 | 67 | /** 68 | * Return JSON from object 69 | * @param {boolean} [matrix] return transform attribute if false. 70 | * @returns {object} JSON Object 71 | */ 72 | Tspan.prototype.toJSON = function toJSON(matrix){ 73 | var parentJSON = SvgObject.prototype.toJSON.call(this, matrix); 74 | 75 | parentJSON.value = this.value; 76 | parentJSON.x = this.x; 77 | parentJSON.y = this.y; 78 | 79 | return parentJSON; 80 | }; 81 | 82 | /** 83 | * Return XML from object 84 | * @param {boolean} [matrix] return transform attribute if false. 85 | * @returns {xmlBuilder} XML Object 86 | */ 87 | Tspan.prototype.toXml = function toXml(matrix){ 88 | var xml = SvgObject.prototype.toXml.call(this, matrix); 89 | 90 | xml.att('x', this.x); 91 | xml.att('y', this.y); 92 | xml.txt(this.value); 93 | 94 | return xml; 95 | }; 96 | 97 | /** 98 | * Apply param Matrix and callback new SvgObject with this matrix 99 | * @param {object} matrix Matrix to apply 100 | * @param {function} callback Callback Function 101 | */ 102 | Tspan.prototype.applyMatrix = function applyMatrix(matrix, callback){ 103 | var tspan = new Tspan(); 104 | tspan.style = this.style; 105 | tspan.classes = this.classes; 106 | tspan.id = this.id; 107 | tspan.name = this.name; 108 | tspan.stroke = this.stroke; 109 | tspan.fill = this.fill; 110 | tspan.type = this.type; 111 | tspan.value = this.value; 112 | tspan.x = matrix.x(this.x, this.y); 113 | tspan.y = matrix.y(this.x, this.y); 114 | tspan.data = this.data; 115 | 116 | callback(tspan); 117 | }; 118 | 119 | module.exports = Tspan; 120 | 121 | /** 122 | * Generate SVGElement from SVG node 123 | * @param {object} node xml2js element 124 | */ 125 | module.exports.fromNode = function fromNode(node){ 126 | var text = new Tspan(); 127 | 128 | if(typeof node != 'undefined'){ 129 | 130 | SvgObject.fromNode(text, node); 131 | 132 | if(typeof node.$ != 'undefined'){ 133 | if(typeof node.$.x != 'undefined'){ 134 | text.x = parseFloat(node.$.x); 135 | } 136 | if(typeof node.$.y != 'undefined'){ 137 | text.y = parseFloat(node.$.y); 138 | } 139 | } 140 | 141 | if(typeof node._ != 'undefined'){ 142 | text.value = node._; 143 | } 144 | } 145 | 146 | return text; 147 | }; 148 | 149 | /** 150 | * Generate SVGElement from JSON element 151 | * @param {object} json json element 152 | */ 153 | module.exports.fromJson = function fromJSON(json){ 154 | var text = new Tspan(); 155 | 156 | if(typeof json != 'undefined'){ 157 | SvgObject.fromJson(text, json); 158 | 159 | if(typeof json.value != 'undefined'){ 160 | text.value = json.value; 161 | } 162 | if(typeof json.x != 'undefined'){ 163 | text.x = json.x; 164 | } 165 | if(typeof json.y != 'undefined'){ 166 | text.y = json.y; 167 | } 168 | } 169 | 170 | return text; 171 | }; -------------------------------------------------------------------------------- /libs/parser.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'), 4 | _ = require('underscore'), 5 | elements = require(__dirname + '/objects/index'), 6 | dxfParser = require('dxf-parsing').Parser; 7 | 8 | var Parser = function Parser() {}; 9 | 10 | /** 11 | * Convert Svg file into Svg object 12 | * @param {object} svg xml2js object 13 | * @param {function} callback Callback function 14 | */ 15 | Parser.convertXml = function convertXml(svg, callback) { 16 | if(svg == null || typeof svg.svg == 'undefined' || svg.svg == null){ 17 | callback(new Error("Your SVG is empty or invalid")); 18 | return; 19 | } 20 | 21 | var elements = Parser.parseXmlNode(svg.svg); 22 | callback(null, elements); 23 | }; 24 | 25 | /** 26 | * Convert JSON file into Svg object 27 | * @param {object} json json object 28 | * @param {function} callback Callback function 29 | */ 30 | Parser.convertJson = function convertJson(json, callback) { 31 | if(typeof json.elements == 'undefined' || json.elements.length == 0){ 32 | callback(new Error("Your SVG is empty or invalid")); 33 | return; 34 | } 35 | 36 | var elements = Parser.parseJson(json.elements); 37 | callback(null, elements); 38 | }; 39 | 40 | /** 41 | * Parse svg possibles nodes : , , ... 42 | * @param {object} node xml2js node 43 | * @returns {Array} node elements convert to SvgObject type 44 | */ 45 | Parser.parseXmlNode = function parseXmlNode(node) { 46 | var nodes = []; 47 | 48 | _.each(node, function (content, index) { 49 | switch (index) { 50 | case 'g' : 51 | nodes = _.union(nodes, Parser.parseXmlGroup(content)); 52 | break; 53 | case 'polygon' : 54 | nodes = _.union(nodes, Parser.parseXmlPolygon(content)); 55 | break; 56 | case 'polyline' : 57 | nodes = _.union(nodes, Parser.parseXmlPolygon(content, true)); 58 | break; 59 | case 'rect' : 60 | nodes = _.union(nodes, Parser.parseXmlRect(content)); 61 | break; 62 | case 'text' : 63 | nodes = _.union(nodes, Parser.parseXmlText(content)); 64 | break; 65 | case 'image' : 66 | nodes = _.union(nodes, Parser.parseXmlImage(content)); 67 | break; 68 | case 'circle' : 69 | nodes = _.union(nodes, Parser.parseXmlCircle(content)); 70 | break; 71 | } 72 | }); 73 | 74 | return nodes; 75 | }; 76 | 77 | /** 78 | * Parse json elements 79 | * @param {Array} elements xml2js node 80 | * @returns {Array} node elements convert to SvgObject type 81 | */ 82 | Parser.parseJson = function parseJson(elements) { 83 | var nodes = []; 84 | 85 | _.each(elements, function (element) { 86 | switch (element.type) { 87 | case 'g' : 88 | nodes.push(Parser.parseJsonGroup(element)); 89 | break; 90 | case 'polygon' : 91 | nodes.push(Parser.parseJsonPolygon(element)); 92 | break; 93 | case 'polyline' : 94 | nodes.push(Parser.parseJsonPolygon(element, true)); 95 | break; 96 | case 'rect' : 97 | nodes.push(Parser.parseJsonRect(element)); 98 | break; 99 | case 'text' : 100 | nodes.push(Parser.parseJsonText(element)); 101 | break; 102 | case 'image' : 103 | nodes.push(Parser.parseJsonImage(element)); 104 | break; 105 | case 'circle' : 106 | nodes.push(Parser.parseJsonCircle(element)); 107 | break; 108 | } 109 | }); 110 | 111 | return nodes; 112 | }; 113 | 114 | /** 115 | * Parse Group Elements Array 116 | * @param {Array} array xml2js elements array 117 | * @returns {Array} Groups array 118 | */ 119 | Parser.parseXmlGroup = function parseXmlGroup(array) { 120 | var groups = []; 121 | 122 | _.each(array, function (item) { 123 | groups.push(elements.Group.fromNode(item)); 124 | }); 125 | 126 | return groups; 127 | }; 128 | 129 | /** 130 | * Parse Group json element 131 | * @param {object} elem Group element in JSON format 132 | * @returns {object} Group object 133 | */ 134 | Parser.parseJsonGroup = function parseJsonGroup(elem) { 135 | return elements.Group.fromJson(elem); 136 | }; 137 | 138 | /** 139 | * Parse Group Elements Array 140 | * @param {Array} array xml2js elements array 141 | * @param {boolean} [isPolyline] true : polyline. false : polygon. (default : false) 142 | * @returns {Array} Polygons array 143 | */ 144 | Parser.parseXmlPolygon = function parseXmlPolygon(array, isPolyline) { 145 | var polygons = []; 146 | 147 | _.each(array, function (item) { 148 | polygons.push(elements.Polygon.fromNode(item, isPolyline)); 149 | }); 150 | 151 | return polygons; 152 | }; 153 | 154 | /** 155 | * Parse Polygon json element 156 | * @param {object} elem Polygon element in JSON format 157 | * @returns {Polygon} Polygon object 158 | */ 159 | Parser.parseJsonPolygon = function parseJsonPolygon(elem) { 160 | return elements.Polygon.fromJson(elem); 161 | }; 162 | 163 | /** 164 | * Parse Rect Elements Array 165 | * @param {Array} array xml2js elements array 166 | * @returns {Array} Rects array 167 | */ 168 | Parser.parseXmlRect = function parseXmlRect(array) { 169 | var rects = []; 170 | 171 | _.each(array, function (item) { 172 | rects.push(elements.Rect.fromNode(item)); 173 | }); 174 | 175 | return rects; 176 | }; 177 | 178 | /** 179 | * Parse Rect json element 180 | * @param {object} elem Rect element in JSON format 181 | * @returns {object} Rect object 182 | */ 183 | Parser.parseJsonRect = function parseJsonRect(elem) { 184 | return elements.Rect.fromJson(elem); 185 | }; 186 | 187 | /** 188 | * Parse Text Elements Array 189 | * @param {Array} array xml2js elements array 190 | * @returns {Array} Texts array 191 | */ 192 | Parser.parseXmlText = function parseXmlText(array) { 193 | var texts = []; 194 | 195 | _.each(array, function (item) { 196 | texts.push(elements.Text.fromNode(item)); 197 | }); 198 | 199 | return texts; 200 | }; 201 | 202 | /** 203 | * Parse Text json element 204 | * @param {object} elem Text element in JSON format 205 | * @returns {Text} Text object 206 | */ 207 | Parser.parseJsonText = function (elem) { 208 | return elements.Text.fromJson(elem); 209 | }; 210 | 211 | /** 212 | * Parse Image Elements Array 213 | * @param {Array} array xml2js elements array 214 | * @returns {Array} Image array 215 | */ 216 | Parser.parseXmlImage = function parseXmlImage(array) { 217 | var images = []; 218 | 219 | _.each(array, function (item) { 220 | images.push(elements.Image.fromNode(item)); 221 | }); 222 | 223 | return images; 224 | }; 225 | 226 | /** 227 | * Parse Image json element 228 | * @param {object} elem Image element in JSON format 229 | * @returns {object} Image object 230 | */ 231 | Parser.parseJsonImage = function parseJsonImage(elem) { 232 | return elements.Image.fromJson(elem); 233 | }; 234 | 235 | /** 236 | * Parse Circle Elements Array 237 | * @param {Array} array xml2js elements array 238 | * @returns {Array} Circle array 239 | */ 240 | Parser.parseXmlCircle = function parseXmlCircle(array) { 241 | var circles = []; 242 | 243 | _.each(array, function (item) { 244 | circles.push(elements.Circle.fromNode(item)); 245 | }); 246 | 247 | return circles; 248 | }; 249 | 250 | /** 251 | * Parse Circle json element 252 | * @param {object} elem Circle element in JSON format 253 | * @returns {object} Circle object 254 | */ 255 | Parser.parseJsonCircle = function parseJsonCircle(elem) { 256 | return elements.Circle.fromJson(elem); 257 | }; 258 | 259 | /** 260 | * Convert DXF File to SVG 261 | * @param {object} params Function params 262 | * @param {string} params.path DXF File path 263 | * @param {function} callback Callback Function 264 | */ 265 | Parser.convertDxf = function convertDxf(params, callback) { 266 | dxfParser.toArray(params.path, function (err, tab) { 267 | if (err) { 268 | callback(err); 269 | return; 270 | } 271 | 272 | var polygons = dxfParser.getPolygons(tab), 273 | texts = dxfParser.getTexts(tab, true); 274 | 275 | callback(null, { 276 | polygons : polygons, 277 | texts : texts, 278 | circles : dxfParser.getCircles(tab, true), 279 | mapping : dxfParser.makeMappings(polygons, texts), 280 | layers : dxfParser.getLayersByEntities(tab, ['text', 'polygon', 'circle']), 281 | parameters : dxfParser.getParameters(tab), 282 | dimensions : dxfParser.getDimensions(polygons) 283 | }); 284 | }.bind(this)); 285 | }; 286 | 287 | module.exports = Parser; -------------------------------------------------------------------------------- /libs/svg.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var fs = require('fs'), 4 | xml2js = require('xml2js'), 5 | _ = require('underscore'), 6 | builder = require('xmlbuilder'), 7 | async = require('async'), 8 | SvgParser = require(__dirname + '/parser'), 9 | Matrix = require(__dirname + '/matrix/extends'), 10 | gm = require('gm').subClass({ imageMagick: true }), 11 | utils = require(__dirname + '/matrix/utils'); 12 | 13 | var Svg = function Svg() { 14 | if (!(this instanceof Svg)) 15 | throw 'this function in a constructor. Use new to call it'; 16 | 17 | this.elements = []; 18 | this.size = { width: 100, height : 100 }; 19 | this.stylesheets = []; 20 | }; 21 | 22 | /** 23 | * Set Svg Elements 24 | * @param {Array} elements SvgObject Array (rect|polygon|polyline|...) 25 | */ 26 | Svg.prototype.setElements = function setElements(elements) { 27 | this.elements = elements; 28 | }; 29 | 30 | /** 31 | * Add SvgObject element to current SVG 32 | * @param {SvgObject} element SvgObject Element 33 | */ 34 | Svg.prototype.addElement = function toJSON(element) { 35 | this.elements.push(element); 36 | }; 37 | 38 | /** 39 | * Add Specific stylesheet in SVG 40 | * @param {string} cssFilePath Css File Path to add 41 | */ 42 | Svg.prototype.addStyleSheet = function addStyleSheet(cssFilePath) { 43 | this.stylesheets.push(cssFilePath); 44 | }; 45 | 46 | /** 47 | * Convert Svg to Json format 48 | * @param {boolean} matrix String representation without transform attribute 49 | * @returns {object} Svg Json Object representation 50 | */ 51 | Svg.prototype.toJSON = function toXml(matrix) { 52 | if(typeof matrix == 'undefined') matrix = false; 53 | 54 | var json = { 55 | elements : [], 56 | stylesheets : this.stylesheets, 57 | size : this.size 58 | }; 59 | 60 | _.each(this.elements, function (element) { 61 | json.elements.push(element.toJSON(matrix)); 62 | }); 63 | 64 | return json; 65 | }; 66 | 67 | /** 68 | * Convert Svg to Xml format 69 | * @param {boolean} matrix String representation without transform attribute 70 | * @returns {object} XMLBuilder Svg representation 71 | */ 72 | Svg.prototype.toXml = function toXml(matrix) { 73 | if(typeof matrix == 'undefined') matrix = false; 74 | 75 | var xml = builder.create('svg'); 76 | xml.att('version', '1.1'); 77 | xml.att('xmlns', 'http://www.w3.org/2000/svg'); 78 | xml.att('width', this.size.width+'px'); 79 | xml.att('height', this.size.height+'px'); 80 | 81 | _.each(this.stylesheets, function (styleSheet) { 82 | xml.ins('xml-stylesheet', 'type="text/xsl" href="'+ styleSheet +'"'); 83 | }); 84 | 85 | _.each(this.elements, function (element) { 86 | xml.importXMLBuilder(element.toXml(matrix)); 87 | }); 88 | 89 | return xml; 90 | }; 91 | 92 | /** 93 | * Convert SVG to String : 94 | * '...' 95 | * @param {boolean} content false : return only svg content, true : return all svg in tag 96 | * @param {boolean} [matrix] String representation without transform attribute 97 | * @returns {string} Svg String representation 98 | */ 99 | Svg.prototype.toString = function toString(content, matrix) { 100 | if(typeof matrix == 'undefined') matrix = false; 101 | 102 | if (content == true) { 103 | return this.toXml(matrix).toString(); 104 | } else { 105 | var string = ''; 106 | _.each(this.elements, function(element){ 107 | string += element.toXml(matrix).toString(); 108 | }); 109 | return string; 110 | } 111 | }; 112 | 113 | /** 114 | * Find elements in SVG and return new Svg object with all elements by selected type 115 | * 116 | * @param {string} type Selected type (rect|polygon|g|...) 117 | * @param {boolean} all true : find all type in groups and root, false : find only in root 118 | * @returns {Svg} new Svg object with selected types elements 119 | */ 120 | Svg.prototype.findByType = function findByType(type, all) { 121 | var svg = new Svg(); 122 | 123 | _.each(this.elements, function (elem) { 124 | if (elem.type == type) { 125 | svg.addElement(elem); 126 | } 127 | 128 | if (all && elem.type == 'g') { 129 | var group = elem.findByType(type, all); 130 | _.each(group.childs, function (child) { 131 | svg.addElement(child); 132 | }); 133 | } 134 | }); 135 | 136 | return svg; 137 | }; 138 | 139 | /** 140 | * Find one element by id in SVG 141 | * 142 | * @param {string} id Item id 143 | * @returns {SvgObject} SvgObject element 144 | */ 145 | Svg.prototype.findById = function (id) { 146 | var returnElem = null; 147 | 148 | _.each(this.elements, function (elem) { 149 | if (elem.id == id) { 150 | returnElem = elem; 151 | }else if (elem.type == 'g') { 152 | returnElem = elem.findById(id); 153 | } 154 | }); 155 | 156 | return returnElem; 157 | }; 158 | 159 | /** 160 | * Find one element by id and type in SVG 161 | * 162 | * @param {string} id Item id 163 | * @param {string} type Item type (rect, path, ...) 164 | * @returns {SvgObject} SvgObject element 165 | */ 166 | Svg.prototype.findByIdAndType = function findByIdAndType(id, type) { 167 | var returnElem = null; 168 | 169 | _.each(this.elements, function (elem) { 170 | if (elem.id == id && elem.type == type) { 171 | returnElem = elem; 172 | }else if (elem.type == 'g') { 173 | returnElem = elem.findByIdAndType(id, type); 174 | } 175 | }); 176 | 177 | return returnElem; 178 | }; 179 | 180 | /** 181 | * Find one element by id without param type in SVG 182 | * 183 | * @param {string} id Item id 184 | * @param {string} type Item type (rect, path, ...) 185 | * @returns {SvgObject} SvgObject element 186 | */ 187 | Svg.prototype.findByIdWithoutType = function findByIdWithoutType(id, type) { 188 | var returnElem = null; 189 | 190 | _.each(this.elements, function (elem) { 191 | if (elem.id === id && elem.type !== type) { 192 | returnElem = elem; 193 | }else if (elem.type === 'g') { 194 | returnElem = elem.findByIdWithoutType(id, type); 195 | } 196 | }); 197 | 198 | return returnElem; 199 | }; 200 | 201 | /** 202 | * Remove All elements by type. If type is 'g', all childs elements are moved on svg root node. 203 | * @param {string} type Type to remove 204 | */ 205 | Svg.prototype.removeAllByType = function removeAllByType(type) { 206 | var elements = []; 207 | _.each(this.elements, function (elem) { 208 | if (elem.type === 'g') { 209 | var elms = elem.removeAllByType(type); 210 | if (type === 'g') { 211 | elements = _.union(elements, elms); 212 | } 213 | } 214 | if (elem.type !== type) { 215 | elements.push(elem); 216 | } 217 | }); 218 | 219 | this.elements = elements; 220 | }; 221 | 222 | /** 223 | * Generate new Svg element with all applied matrix to all elements. 224 | * Convert rect into polygon 225 | * @param {array|Matrix} matrix Matrix to be applied in addition to those elements. 226 | * @param {function} callback Callback Function 227 | */ 228 | Svg.prototype.applyMatrix = function applyMatrix(matrix, callback) { 229 | var svg = new Svg(); 230 | 231 | var applyMatrix = new Matrix(); 232 | if (matrix != null) { 233 | if(matrix instanceof Array){ 234 | _.each(matrix, function(mat){ 235 | applyMatrix.add(mat); 236 | }); 237 | }else{ 238 | applyMatrix = matrix; 239 | } 240 | } 241 | 242 | async.each(this.elements, function (elem, c) { 243 | elem.getBBox( function (bbox) { 244 | var applyCloneMatrix = applyMatrix.clone(); 245 | var matrix = applyCloneMatrix.add(Matrix.fromElement(bbox, elem)); 246 | elem.applyMatrix(matrix, function(e){ 247 | svg.addElement(e); 248 | c(); 249 | }); 250 | }); 251 | }, function () { 252 | callback(svg); 253 | }); 254 | }; 255 | 256 | /** 257 | * Save Svg file 258 | * @param {object} params Functon Params 259 | * @param {string} [params.output] Output file 260 | * @param {function} callback Callback Function 261 | */ 262 | Svg.prototype.save = function save(params, callback) { 263 | var defOpts = { 264 | output : '/tmp/export_' + new Date().getTime() + '.svg' 265 | }; 266 | 267 | params = _.extend({}, defOpts, params); 268 | 269 | fs.writeFile(params.output, this.toString(true), function (err) { 270 | if (err) { 271 | callback(err); 272 | return; 273 | } 274 | callback(null, params.output); 275 | }); 276 | }; 277 | 278 | /** 279 | * Save Svg file in Png Format 280 | * @param {object} params Functon Params 281 | * @param {string} [params.output] Output file 282 | * @param {function} callback Callback Function 283 | */ 284 | Svg.prototype.savePng = function savePng(params, callback) { 285 | var defOpts = { 286 | output : '/tmp/export_' + new Date().getTime() + '.png' 287 | }; 288 | 289 | params = _.extend({}, defOpts, params); 290 | 291 | this.save({}, function (err, file) { 292 | if (err) { 293 | callback(err); 294 | return; 295 | } 296 | 297 | gm(file).write(params.output, function (err) { 298 | if (err) { 299 | callback(err); 300 | return; 301 | } 302 | 303 | callback(null, params.output); 304 | }); 305 | }); 306 | }; 307 | 308 | /** 309 | * Refresh SVG size 310 | * @param {function} callback Callback Function 311 | */ 312 | Svg.prototype.getSize = function getSize(callback) { 313 | var minX = +Infinity, 314 | maxX = -Infinity, 315 | minY = +Infinity, 316 | maxY = -Infinity, 317 | self = this; 318 | 319 | async.each(this.elements, function (child, done) { 320 | child.getBBox(function (bbox) { 321 | minX = Math.min(minX, bbox.x); 322 | minY = Math.min(minY, bbox.y); 323 | maxX = Math.max(maxX, bbox.x2); 324 | maxY = Math.max(maxY, bbox.y2); 325 | done(); 326 | }); 327 | }, function () { 328 | var bbox = utils.bbox(minX,minY,maxX,maxY); 329 | self.size = { width : bbox.w, height : bbox.h }; 330 | 331 | callback(self.size); 332 | }); 333 | }; 334 | 335 | /** 336 | * Get SVG bbox 337 | * @param {function} callback Callback Function 338 | */ 339 | Svg.prototype.getBBox = function getBBox(callback) { 340 | var minX = +Infinity, 341 | maxX = -Infinity, 342 | minY = +Infinity, 343 | maxY = -Infinity; 344 | 345 | async.each(this.elements, function (child, done) { 346 | child.getBBox(function (bbox) { 347 | minX = Math.min(minX, bbox.x); 348 | minY = Math.min(minY, bbox.y); 349 | maxX = Math.max(maxX, bbox.x2); 350 | maxY = Math.max(maxY, bbox.y2); 351 | done(); 352 | }); 353 | }, function () { 354 | callback(utils.bbox(minX,minY,maxX,maxY)); 355 | }); 356 | }; 357 | 358 | /** 359 | * Calculate all innerboxes in SVG. Return copy of current svg with elements with data attribute innerbox. 360 | * @param {function} callback Callback Function 361 | */ 362 | Svg.prototype.calculateAllInnerBoxes = function calculateAllInnerBoxes(callback) { 363 | var svg = new Svg(); 364 | async.each(this.elements, function (child, done) { 365 | switch (child.type) { 366 | case 'rect' : 367 | case 'polygon' : 368 | case 'polyline' : 369 | case 'circle' : 370 | child.getInnerBox(function (innerBox) { 371 | child.data.innerbox = innerBox; 372 | svg.addElement(child); 373 | done(); 374 | }); 375 | break; 376 | case 'g' : 377 | child.calculateAllInnerBoxes( function (group) { 378 | svg.addElement(child); 379 | done(); 380 | }); 381 | break; 382 | default : 383 | svg.addElement(child); 384 | done(); 385 | } 386 | }, function () { 387 | svg.getSize(function () { 388 | callback(svg); 389 | }); 390 | }); 391 | }; 392 | 393 | /** 394 | * Remove specifics types of objetcs in svg 395 | * @param {object} params Function params 396 | * @param {string|Array} params.type target type(s) (g, rect, ...) 397 | */ 398 | Svg.prototype.removeByType = function removeByType(params) { 399 | this.elements = _.filter(this.elements, function (element) { 400 | if (!_.isArray(params.type)) { 401 | var t = params.type; 402 | params.type = []; 403 | params.type.push(t); 404 | } 405 | 406 | if (element.type === 'g') { 407 | // remove elements for group 408 | element.removeByType(params); 409 | // if remove group type, add all elements in svg 410 | if (_.contains(params.type, 'g')) { 411 | _.union(this.elements, element.childs); 412 | } 413 | } 414 | 415 | return !_.contains(params.type, element.type); 416 | }, this); 417 | }; 418 | 419 | /** 420 | * Convert Svg elements into Path. 421 | * Works only on rect, polygon and polyline 422 | */ 423 | Svg.prototype.convertElementsToPath = function convertElementsToPath() { 424 | var elements = []; 425 | 426 | _.each(this.elements, function (element) { 427 | switch(element.type) { 428 | case 'rect' : 429 | case 'polygon' : 430 | case 'polyline' : 431 | elements.push(element.toPath()); 432 | break; 433 | case 'g' : 434 | element.convertElementsToPath(); 435 | elements.push(element); 436 | break; 437 | default : 438 | elements.push(element); 439 | } 440 | }, this); 441 | this.elements = elements; 442 | }; 443 | 444 | module.exports = Svg; 445 | 446 | /** 447 | * Create Svg from Svg Document 448 | * @param {string} path Uri of source file 449 | * @param {function} callback Callback Function 450 | */ 451 | module.exports.fromSvgDocument = function fromSvgDocument(path, callback) { 452 | fs.readFile(path, function (error, data) { 453 | if (error) { 454 | callback(error); 455 | return; 456 | } 457 | Svg.fromXmlString(data.toString(), callback); 458 | }); 459 | }; 460 | 461 | /** 462 | * Create Svg from Xml String representation 463 | * @param {string} string Svg string representation 464 | * @param {function} callback Callback Function 465 | */ 466 | module.exports.fromXmlString = function fromXmlString(string, callback) { 467 | var parser = new xml2js.Parser(); 468 | 469 | parser.addListener('end', function (result) { 470 | SvgParser.convertXml(result, function (err, elements) { 471 | if (err) { 472 | callback(err); 473 | return; 474 | } 475 | 476 | var svg = new Svg(); 477 | svg.setElements(elements); 478 | svg.getSize(function () { 479 | callback(null, svg); 480 | }); 481 | }); 482 | }); 483 | 484 | parser.parseString(string); 485 | }; 486 | 487 | 488 | module.exports.fromJsonFile = function fromJsonFile(path, callback) { 489 | fs.readFile(path, function (error, data) { 490 | if (error) { 491 | callback(error); 492 | return; 493 | } 494 | Svg.fromJsonString(data.toString(), callback); 495 | }); 496 | }; 497 | 498 | module.exports.fromJsonString = function fromJsonString(string, callback) { 499 | var json = JSON.parse(string); 500 | 501 | Svg.fromJson(json, callback); 502 | }; 503 | 504 | module.exports.fromJson = function fromJson(json, callback) { 505 | SvgParser.convertJson(json, function (err, elements) { 506 | if (err) { 507 | callback(err); 508 | return; 509 | } 510 | 511 | var svg = new Svg(); 512 | svg.setElements(elements); 513 | 514 | svg.getSize(function () { 515 | callback(null, svg); 516 | }); 517 | }); 518 | }; 519 | 520 | /** 521 | * Create SVG from dxf file 522 | * @param {object} params Function params 523 | * @param {string} params.path DXF File path 524 | * @param {Array} params.layers Import element for passed layers names 525 | * @param {function} callback Callback Function 526 | */ 527 | module.exports.fromDxfFile = function fromDxfFile(params, callback) { 528 | SvgParser.convertDxf(params, function (err, result) { 529 | if (err) { 530 | callback (err); 531 | return; 532 | } 533 | 534 | var svg = new Svg(); 535 | _.each(result.polygons, function (polygon) { 536 | // test if params.layers is set and if polygon is in specified layers 537 | if (!params.layers || params.layers.length == 0 || _.contains(params.layers, polygon.layer)) { 538 | svg.addElement(SvgParser.parseJsonPolygon({ 539 | points : polygon.points, 540 | fill : 'white', 541 | stroke : 'black' 542 | })); 543 | } 544 | }, this); 545 | _.each(result.circles, function (circle) { 546 | if (!params.layers || params.layers.length == 0 || _.contains(params.layers, circle.layer)) { 547 | svg.addElement(SvgParser.parseJsonPolygon({ 548 | points : circle.points, 549 | fill : 'white', 550 | stroke : 'black' 551 | })); 552 | } 553 | }); 554 | _.each(result.texts, function (text) { 555 | if (!params.layers || params.layers.length == 0 || _.contains(params.layers, text.layer)) { 556 | svg.addElement(SvgParser.parseJsonText({ 557 | fill : 'black', 558 | value : text.contents, 559 | x : text.point.x, 560 | y : text.point.y 561 | })); 562 | } 563 | }); 564 | svg.getBBox(function (bbox) { 565 | var matrix = new Matrix(1, 0, 0, -1, Math.abs(Math.min(bbox.x, bbox.x2))+10, Math.abs(Math.min(bbox.y, bbox.y2))+10); 566 | svg.applyMatrix(matrix, function (svg) { 567 | svg.getSize(function () { 568 | callback(null, svg); 569 | }); 570 | }); 571 | }); 572 | }); 573 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svgutils", 3 | "version": "0.12.5", 4 | "description": "Svg Utils for pasing SVGFile and manipulate Matrix object like Snap.svg", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha --reporter spec -t 5000" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/throrin19/svgutils.git" 12 | }, 13 | "keywords": [ 14 | "svg", 15 | "matrix", 16 | "parsing", 17 | "json", 18 | "xml" 19 | ], 20 | "author": "Benjamin Besse ", 21 | "license": "Apache", 22 | "readmeFilename": "README.md", 23 | "gitHead": "5c71469ee5f51b6e08bc8f5e3cd2b0fadf0ed89a", 24 | "dependencies": { 25 | "async": "~0.9.0", 26 | "dxf-parsing": "^0.3.7", 27 | "gm": "~1.17.0", 28 | "underscore": "~1.7.0", 29 | "xml2js": "~0.4.4", 30 | "xmlbuilder": "^2.4.3" 31 | }, 32 | "devDependencies": { 33 | "mocha": "" 34 | }, 35 | "engines": { 36 | "node": "~0.10.22" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/00_rect.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var svgutils = require(__dirname + '/../index'), 4 | xml2js = require('xml2js'), 5 | rectJSON = { 6 | type : 'rect', 7 | x : 10, 8 | y : 10, 9 | width : 100, 10 | height: 100 11 | }; 12 | 13 | describe("manipulate Rect class", function(){ 14 | it("create from XML", function(done){ 15 | var xml = ''; 16 | 17 | var parser = new xml2js.Parser(); 18 | parser.addListener('end', function(result) { 19 | var rect = svgutils.Elements.Rect.fromNode(result); 20 | 21 | if((rect instanceof svgutils.Elements.Rect) == false){ 22 | done(new Error("Creation failed")); 23 | return; 24 | } 25 | done(); 26 | }); 27 | parser.parseString(xml); 28 | }); 29 | it("create from JSON", function(done){ 30 | var rect = svgutils.Elements.Rect.fromJson(rectJSON); 31 | if((rect instanceof svgutils.Elements.Rect) == false){ 32 | done(new Error("Creation failed")); 33 | return; 34 | } 35 | done(); 36 | }); 37 | it("apply translate Matrix", function(done){ 38 | // translate(10, 50) 39 | var matrix = new svgutils.Matrix(1, 0, 0, 1, 10, 50); 40 | var rect = svgutils.Elements.Rect.fromJson(rectJSON); 41 | 42 | rect.applyMatrix(matrix, function(polygon){ 43 | if((polygon instanceof svgutils.Elements.Polygon) == false){ 44 | done(new Error("Apply matrix failed")); 45 | return; 46 | } 47 | polygon.getBBox(function(bbox){ 48 | if(bbox.x != 20 || bbox.y != 60){ 49 | done(new Error("Apply matrix failed")); 50 | return; 51 | } 52 | done(); 53 | }); 54 | }); 55 | }); 56 | it("get current Matrix", function(done){ 57 | var rect = svgutils.Elements.Rect.fromJson(rectJSON); 58 | rect.getCurrentMatrix(function(matrix){ 59 | if( 60 | matrix.a != 1 || 61 | matrix.b != 0 || 62 | matrix.c != 0 || 63 | matrix.d != 1 || 64 | matrix.e != 0 || 65 | matrix.f != 0 66 | ){ 67 | done(new Error("Incorrect Matrix")); 68 | return; 69 | } 70 | done(); 71 | }); 72 | }); 73 | }); -------------------------------------------------------------------------------- /test/01_polygon.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var svgutils = require(__dirname + '/../index'), 4 | xml2js = require('xml2js'), 5 | _ = require('underscore'), 6 | polygonJSON = { 7 | type : 'polygon', 8 | points : [ 9 | { x : 10, y : 10 }, 10 | { x : 30, y : 10 }, 11 | { x : 30, y : 30 }, 12 | { x : 10, y : 30 } 13 | ] 14 | }; 15 | 16 | describe("manipulate Polygon class", function(){ 17 | it("create from XML", function(done){ 18 | var xml = ''; 19 | 20 | var parser = new xml2js.Parser(); 21 | parser.addListener('end', function(result) { 22 | var polygon = svgutils.Elements.Polygon.fromNode(result); 23 | 24 | if((polygon instanceof svgutils.Elements.Polygon) == false){ 25 | done(new Error("Creation failed")); 26 | return; 27 | } 28 | done(); 29 | }); 30 | parser.parseString(xml); 31 | }); 32 | it("create from JSON", function(done){ 33 | var polygon = svgutils.Elements.Polygon.fromJson(polygonJSON); 34 | if((polygon instanceof svgutils.Elements.Polygon) == false){ 35 | done(new Error("Creation failed")); 36 | return; 37 | } 38 | done(); 39 | }); 40 | it("apply translate Matrix", function(done){ 41 | // translate(10, 50) 42 | var matrix = new svgutils.Matrix(1, 0, 0, 1, 10, 50); 43 | var polygon = svgutils.Elements.Polygon.fromJson(polygonJSON); 44 | 45 | polygon.applyMatrix(matrix, function(polygon2){ 46 | if((polygon2 instanceof svgutils.Elements.Polygon) == false){ 47 | done(new Error("Matrix failed")); 48 | return; 49 | } 50 | var success = true; 51 | _.each(polygon2.points, function(point, index){ 52 | if(point.x != polygon.points[index].x + 10 || point.y != polygon.points[index].y + 50){ 53 | success = false; 54 | } 55 | }); 56 | if(!success){ 57 | done(new Error("Matrix failed")); 58 | return; 59 | } 60 | done(); 61 | }); 62 | }); 63 | it("get current Matrix", function(done){ 64 | var polygon = svgutils.Elements.Rect.fromJson(polygonJSON); 65 | polygon.getCurrentMatrix(function(matrix){ 66 | if( 67 | matrix.a != 1 || 68 | matrix.b != 0 || 69 | matrix.c != 0 || 70 | matrix.d != 1 || 71 | matrix.e != 0 || 72 | matrix.f != 0 73 | ){ 74 | done(new Error("Incorrect Matrix")); 75 | return; 76 | } 77 | done(); 78 | }); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /test/02_text.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var svgutils = require(__dirname + '/../index'), 4 | xml2js = require('xml2js'), 5 | json = { 6 | type : 'text', 7 | x : 10, 8 | y : 10, 9 | value : "Lorem Ipsum" 10 | }; 11 | 12 | describe("manipulate Text class", function(){ 13 | it("create from XML", function(done){ 14 | var xml = 'Lorem Ipsum'; 15 | 16 | var parser = new xml2js.Parser(); 17 | parser.addListener('end', function(result) { 18 | var text = svgutils.Elements.Text.fromNode(result); 19 | 20 | if((text instanceof svgutils.Elements.Text) == false){ 21 | done(new Error("Creation failed")); 22 | return; 23 | } 24 | done(); 25 | }); 26 | parser.parseString(xml); 27 | }); 28 | it("create from JSON", function(done){ 29 | var text = svgutils.Elements.Text.fromJson(json); 30 | if((text instanceof svgutils.Elements.Text) == false){ 31 | done(new Error("Creation failed")); 32 | return; 33 | } 34 | done(); 35 | }); 36 | it("apply translate Matrix", function(done){ 37 | // translate(10, 50) 38 | var matrix = new svgutils.Matrix(1, 0, 0, 1, 10, 50); 39 | var text = svgutils.Elements.Text.fromJson(json); 40 | 41 | text.applyMatrix(matrix, function(newText){ 42 | if((newText instanceof svgutils.Elements.Text) == false){ 43 | done(new Error("Apply matrix failed")); 44 | return; 45 | } 46 | if(newText.x != text.x + 10 || newText.y != text.y + 50){ 47 | done(new Error("Apply matrix failed")); 48 | return; 49 | } 50 | done(); 51 | }); 52 | }); 53 | it("get current Matrix", function(done){ 54 | var text = svgutils.Elements.Text.fromJson(json); 55 | text.getCurrentMatrix(function(matrix){ 56 | if( 57 | matrix.a != 1 || 58 | matrix.b != 0 || 59 | matrix.c != 0 || 60 | matrix.d != 1 || 61 | matrix.e != 0 || 62 | matrix.f != 0 63 | ){ 64 | done(new Error("Incorrect Matrix")); 65 | return; 66 | } 67 | done(); 68 | }); 69 | }); 70 | }); -------------------------------------------------------------------------------- /test/04_image.js: -------------------------------------------------------------------------------- 1 | var svgutils = require(__dirname + '/../index'), 2 | xml2js = require('xml2js'), 3 | imgJSON = { 4 | type : 'image', 5 | x : 0, 6 | y : 0, 7 | width : 2000, 8 | height: 2000, 9 | href : 'https://devgoomeodata.blob.core.windows.net/images/i_cf8ba75c36411fd689725c9f5ae3e4cf.png', 10 | preserveAspectRatio : 'none' 11 | }; 12 | 13 | describe("manipulate Image class", function(){ 14 | it("create from XML", function(done){ 15 | var xml = ''; 16 | 17 | var parser = new xml2js.Parser(); 18 | parser.addListener('end', function(result) { 19 | var rect = svgutils.Elements.Rect.fromNode(result); 20 | 21 | if((rect instanceof svgutils.Elements.Rect) == false){ 22 | done(new Error("Creation failed")); 23 | return; 24 | } 25 | done(); 26 | }); 27 | parser.parseString(xml); 28 | }); 29 | it("create from JSON", function(done){ 30 | var rect = svgutils.Elements.Rect.fromJson(imgJSON); 31 | if((rect instanceof svgutils.Elements.Rect) == false){ 32 | done(new Error("Creation failed")); 33 | return; 34 | } 35 | done(); 36 | }); 37 | }); -------------------------------------------------------------------------------- /test/10_manipulate_svg.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Unit Test for manipulating existing Svg file 3 | */ 4 | 5 | 6 | var svg = null, 7 | svgPath = __dirname + '/svg/test.svg', 8 | svgutils = require(__dirname + '/../index'); 9 | 10 | describe('Manipulating existing File', function(){ 11 | it("load Svg", function(done){ 12 | svgutils.Svg.fromSvgDocument(svgPath, function(error, svgObject){ 13 | if(error){ 14 | done(error); 15 | return; 16 | } 17 | 18 | if(typeof svgObject == 'undefined'){ 19 | done(new Error("No Svg created")); 20 | return; 21 | } 22 | 23 | if((svgObject instanceof svgutils.Svg) == false){ 24 | done(new Error("no correct object returned")); 25 | return; 26 | } 27 | svg = svgObject; 28 | done(); 29 | }); 30 | }); 31 | it("check root elements", function(done){ 32 | if(svg == null){ 33 | done(new Error("Svg is null")); 34 | return; 35 | } 36 | if(svg.elements.length != 2){ 37 | done(new Error("No correct number of element are count")); 38 | return; 39 | } 40 | if(svg.elements[0].type != 'image'){ 41 | done(new Error("Element is not a group")); 42 | return; 43 | } 44 | done(); 45 | }); 46 | it("find all polygons in SVG", function(done){ 47 | if(svg == null){ 48 | done(new Error("Svg is null")); 49 | return; 50 | } 51 | var findSvg = svg.findByType('polygon', true); 52 | if(findSvg.elements.length != 1){ 53 | done(new Error("No correct number of polygons were founds")); 54 | return; 55 | } 56 | done(); 57 | }); 58 | it("Save Svg", function(done){ 59 | if(svg == null){ 60 | done(new Error("Svg is null")); 61 | return; 62 | } 63 | svg.save({}, function(err, file){ 64 | if(err){ 65 | done(err); 66 | return; 67 | } 68 | 69 | done(); 70 | }); 71 | }); 72 | it("Save as Png", function(done){ 73 | if(svg == null){ 74 | done(new Error("Svg is null")); 75 | return; 76 | } 77 | svg.savePng({}, function(err, file){ 78 | if(err){ 79 | done(err); 80 | return; 81 | } 82 | 83 | done(); 84 | }); 85 | }) 86 | }); -------------------------------------------------------------------------------- /test/11_contains.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var containPath = __dirname + '/svg/contains.svg', 4 | noContainPath = __dirname + '/svg/notcontains.svg', 5 | svgutils = require(__dirname + '/../index'); 6 | 7 | describe("Contains or not contains", function(){ 8 | it('polygon contains text', function(done){ 9 | svgutils.Svg.fromSvgDocument(containPath, function(err, svg){ 10 | if(err){ 11 | done(err); 12 | return; 13 | } 14 | 15 | // polygon contains text 16 | svg.elements[0].contains(svg.elements[1], function(result){ 17 | if(result == false){ 18 | done(new Error('Polygon must be contains text')); 19 | return; 20 | } 21 | done(); 22 | }); 23 | }); 24 | }); 25 | it('polygon not contains text', function(done){ 26 | svgutils.Svg.fromSvgDocument(noContainPath, function(err, svg){ 27 | if(err){ 28 | done(err); 29 | return; 30 | } 31 | 32 | // polygon contains text 33 | svg.elements[0].contains(svg.elements[1], function(result){ 34 | if(result == true){ 35 | done(new Error('Polygon must be no contains text')); 36 | return; 37 | } 38 | done(); 39 | }); 40 | }); 41 | }); 42 | }); -------------------------------------------------------------------------------- /test/svg/contains.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Several lines 4 | -------------------------------------------------------------------------------- /test/svg/notcontains.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Several lines 4 | -------------------------------------------------------------------------------- /test/svg/test.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Texte en dehors 9 | Several lines: 10 | First line 11 | Second line 12 | 13 | 14 | --------------------------------------------------------------------------------