├── .travis.yml
├── demo
├── test.dxf
├── usage.js
├── test.svg
└── test.json
├── .gitignore
├── test
├── svg
│ ├── contains.svg
│ ├── notcontains.svg
│ └── test.svg
├── 04_image.js
├── 11_contains.js
├── 02_text.js
├── 00_rect.js
├── 10_manipulate_svg.js
└── 01_polygon.js
├── index.js
├── libs
├── objects
│ ├── index.js
│ ├── path.js
│ ├── tspan.js
│ ├── circle.js
│ ├── image.js
│ ├── rect.js
│ ├── text.js
│ ├── group.js
│ ├── svgobject.js
│ └── polygon.js
├── matrix
│ ├── extends.js
│ ├── utils.js
│ └── matrix.js
├── parser.js
└── svg.js
├── package.json
├── CHANGELOG
├── README.md
└── LICENSE
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "0.10"
--------------------------------------------------------------------------------
/demo/test.dxf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Goomeo/svgutils/HEAD/demo/test.dxf
--------------------------------------------------------------------------------
/.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/
--------------------------------------------------------------------------------
/test/svg/contains.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/svg/notcontains.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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/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 | };
--------------------------------------------------------------------------------
/demo/test.svg:
--------------------------------------------------------------------------------
1 |
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "svgutils",
3 | "version": "0.13.6",
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/Goomeo/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": "~1.4.2",
26 | "dxf-parsing": "^0.3.7",
27 | "gm": "~1.20.0",
28 | "underscore": "~1.7.0",
29 | "xml2js": "~0.4.4",
30 | "xmlbuilder": "^3.1.0"
31 | },
32 | "devDependencies": {
33 | "mocha": ""
34 | },
35 | "engines": {
36 | "node": "~0.10.22"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/test/svg/test.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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/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 | });
--------------------------------------------------------------------------------
/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/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 | };
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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/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/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 | });
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | };
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | svgutils
2 | ========
3 |
4 | Svg Utils for pasing SVGFile and manipulate Matrix object like Snap.svg
5 |
6 | [](https://www.npmjs.org/package/svgutils)
7 | [](http://travis-ci.org/throrin19/svgutils)
8 | [](https://david-dm.org/throrin19/svgutils)
9 | [](https://codeclimate.com/github/throrin19/svgutils)
10 | [](http://nodejs.org/api/documentation.html#documentation_stability_index)
11 | [](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 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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/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/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' && returnElem == null) {
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' && returnElem == null) {
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' && returnElem == null) {
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 | };
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 | function calculAires( matrix, xKeys, yKeys ) {
178 | var nbInside = 0;
179 | var nbOutside = 0;
180 | // parcours les lignes
181 | for(var i in xKeys) {
182 | for(var j in yKeys) {
183 | if( matrix[ xKeys[i] ] ) {
184 | if( matrix[ xKeys[i] ][ yKeys[j] ] == 1 ) nbInside++;
185 | else if( matrix[ xKeys[i] ][ yKeys[j] ] == 0 ) nbOutside++
186 | }
187 | }
188 | }
189 | return { inside : nbInside, outside : nbOutside };
190 | }
191 |
192 | function pointIsInside( point, polyPoints ) {
193 | var c = false;
194 | var i = 0, j=0;
195 | for (i = 0, j = polyPoints.length - 1; i < polyPoints.length; j = i++) {
196 | if ((( arrondi(polyPoints[i].y) > arrondi(point.y) ) != ( arrondi(polyPoints[j].y) > arrondi(point.y))) &&
197 | ( arrondi(point.x) <
198 | ( arrondi(polyPoints[j].x) - arrondi(polyPoints[i].x) ) * ( arrondi(point.y) - arrondi(polyPoints[i].y)) / (arrondi(polyPoints[j].y) - arrondi(polyPoints[i].y)) +
199 | arrondi(polyPoints[i].x))) {
200 | c = !c;
201 | }
202 | }
203 | return c;
204 | }
205 |
206 | function makeMatrix( points, box ) {
207 | var matrix = [];
208 | var yKeys = [];
209 | var xKeys = [];
210 | var first = true;
211 | for(var i=arrondi(box.x); i< box.x + (box.width + offsetX); i = arrondi(i+offsetX) ) {
212 | for(var j=arrondi(box.y); j< box.y + (box.height + offsetY); j= arrondi(j+offsetY) ){
213 | if( !_.isArray(matrix[i]) ) matrix[i] = [];
214 | matrix[i][j] = pointIsInside( {x:i, y:j}, points ) == true ? 1 : 0;
215 | if( first == true ) yKeys.push(j);
216 | }
217 | first = false;
218 | xKeys.push(i);
219 | }
220 | return {matrix:matrix, xKeys:xKeys, yKeys:yKeys};
221 | }
222 |
223 | function matrixToRect (matrix) {
224 | var x = 99999;
225 | var y = 99999;
226 | var maxX = 0;
227 | var maxY = 0;
228 | var width = 0;
229 | var height = 0;
230 | for(var i in xKeys) {
231 | for(var j in yKeys) {
232 | if(matrix[ xKeys[i] ]) {
233 | if( matrix[ xKeys[i] ][ yKeys[j] ] ) {
234 | y = Math.min(y, yKeys[j] );
235 | maxY = Math.max(maxY, yKeys[j] );
236 | }
237 | x = Math.min(x, xKeys[i] );
238 | maxX = Math.max(maxX, xKeys[i] );
239 | }
240 | }
241 | }
242 | return {x: x,
243 | y: y,
244 | width: arrondi(maxX - x),
245 | height: arrondi(maxY - y) };
246 | }
247 |
248 | function deleteRow( matrix, xKeys, yKeys, x, y, from ) {
249 | var newMatrix = matrix;
250 | var find = false;
251 | _.each(xKeys, function( i ){
252 | _.each(yKeys, function( j ){
253 | if( x == 0 && j == y) {
254 | if(newMatrix[i]) newMatrix[i][j]= null;
255 | find = true;
256 | }
257 | });
258 | if( y == 0 && i == x) {
259 | newMatrix[i] = null;
260 | find = true;
261 | }
262 | });
263 | if( find == true && x == 0 && from == 'top') {
264 | ryKeys = _.without(ryKeys, y);
265 | yKeys = _.without(yKeys, y);
266 | }
267 | if( find == true && y == 0 && from == 'right') {
268 | rxKeys = _.without(rxKeys, x);
269 | xKeys = _.without(xKeys, x);
270 | }
271 | if( find == true && x == 0 && from == 'bottom') {
272 | yKeys = _.without(yKeys, y);
273 | ryKeys = _.without(ryKeys, y);
274 | }
275 | if( find == true && y == 0 && from == 'left') {
276 | rxKeys = _.without(rxKeys, x);
277 | xKeys = _.without(xKeys, x);
278 | }
279 | return;
280 | }
281 |
282 | function differenceAire( matrix, xKeys, yKeys, i, direction) {
283 | var beforeAire = calculAires( matrix, xKeys, yKeys );
284 | if( direction=='top' || direction=='bottom') deleteRow( matrix, xKeys, yKeys, 0, i, direction );
285 | else deleteRow( matrix, xKeys, yKeys, i, 0, direction );
286 | var newAire = calculAires( matrix, xKeys, yKeys );
287 | if( ((beforeAire.inside - newAire.inside) > (precision*(beforeAire.outside - newAire.outside))) ) {
288 | return true;
289 | } else return false;
290 | }
291 |
292 | function arrondi( item) {
293 | return Math.round( item *1000) / 1000;
294 | }
295 |
296 | var points = this.points;
297 | var minX = +Infinity,
298 | maxX = -Infinity,
299 | minY = +Infinity,
300 | maxY = -Infinity;
301 |
302 | _.each(this.points, function (point) {
303 | minX = Math.min(point.x, minX);
304 | maxX = Math.max(point.x, maxX);
305 | minY = Math.min(point.y, minY);
306 | maxY = Math.max(point.y, maxY);
307 | });
308 | var box = utils.bbox(minX, minY, Math.abs(Math.abs(maxX) - Math.abs(minX)), Math.abs(Math.abs(maxY) - Math.abs(minY)));
309 |
310 | if (box.width <= 0.1 || box.height <= 0.1 ) {
311 | callback({
312 | x: 0,
313 | y: 0,
314 | width: 0,
315 | height: 0
316 | });
317 | return;
318 | }
319 |
320 | var offsetX = arrondi(box.width / 40);
321 | var offsetY = arrondi(box.height / 40);
322 | var getMatrixParams = makeMatrix( points, box );
323 |
324 | var matrix = getMatrixParams.matrix;
325 | var xKeys = getMatrixParams.xKeys;
326 | var yKeys = getMatrixParams.yKeys;
327 | var rxKeys = _.sortBy(xKeys, function (name) {return name}).reverse();
328 | var ryKeys = _.sortBy(yKeys, function (name) {return name}).reverse();
329 |
330 | var originalrxKeys = JSON.parse(JSON.stringify(rxKeys));
331 | var originalryKeys = JSON.parse(JSON.stringify(ryKeys));
332 |
333 | var precision = 0.7;
334 |
335 | // on elimine les lignes par le haut
336 |
337 | for(var i in originalryKeys) {
338 | if( differenceAire(matrix, xKeys, yKeys, originalryKeys[i], 'top') ) break;
339 | }
340 |
341 | // on elimine les lignes par la droite
342 | for( var i in originalrxKeys ) {
343 | if( differenceAire(matrix, xKeys, yKeys, originalrxKeys[i], 'right') ) break;
344 | }
345 |
346 | // on elimine les lignes par le bas
347 | for( var i in yKeys ) {
348 | if( differenceAire(matrix, xKeys, yKeys, yKeys[i], 'bottom') ) break;
349 | }
350 | // on elimine les lignes par la gauche
351 | for( var i in xKeys ) {
352 | if( differenceAire(matrix, xKeys, yKeys, xKeys[i], 'left') ) break;
353 | }
354 |
355 | innerRect = matrixToRect(matrix);
356 |
357 | // cas spécifique non gérer par l'algo
358 | // on enleve un quart de l'outerbox
359 | if( innerRect.x == 99999 &&
360 | innerRect.y == 99999 &&
361 | innerRect.width == -99999 &&
362 | innerRect.height == -99999) {
363 |
364 | var deltaQuarterX = box.width / 4;
365 | var deltaQuarterY = box.height / 4;
366 |
367 | innerRect.x = box.x + deltaQuarterX;
368 | innerRect.y = box.y + deltaQuarterY;
369 | innerRect.width = box.width - deltaQuarterX;
370 | innerRect.height = box.height - deltaQuarterY;
371 | }
372 |
373 | callback(innerRect);
374 | };
375 |
376 |
377 | /**
378 | * Get segment at specific Y coordinate
379 | * @param {number} y Y coordinate
380 | * @returns {{x: number, y: number, width: number}}
381 | * @private
382 | */
383 | Polygon.prototype._widestSegmentAtY = function _widestSegmentAtY(y) {
384 | var segment = {
385 | x : 0,
386 | y : y,
387 | width : 0
388 | },
389 | pointsCount = this.points.length,
390 | xArray = [],
391 | i, j;
392 |
393 | if (pointsCount < 3) {
394 | return segment;
395 | }
396 |
397 | // compute all the intersections (x coordinates)
398 | for (i = 0, j = pointsCount-1; i < pointsCount; j = i++) {
399 | var point1 = this.points[i],
400 | point2 = this.points[j];
401 | if ((point1.y > y) != (point2.y > y)) {
402 | if (Math.abs(point2.x - point1.x) < EPSILON) {
403 | xArray.push(point1.x);
404 | } else {
405 | // y = a x + b
406 | var a = (point2.y - point1.y)/(point2.x - point1.x),
407 | b = point2.y - a * point2.x,
408 | x = (y - b)/a;
409 | if (x >= Math.min(point2.x, point1.x) && x <= Math.max(point2.x, point1.x)) {
410 | xArray.push(x);
411 | }
412 | }
413 | }
414 | }
415 |
416 | xArray = _.sortBy(xArray, function (x) {
417 | return x;
418 | });
419 |
420 | for (i = 0, j = 1; j < xArray.length; i+=2, j+=2) {
421 | var width = xArray[j] - xArray[i];
422 | if (width > segment.width) {
423 | segment.x = xArray[i];
424 | segment.width = width;
425 | }
426 | }
427 |
428 | return segment;
429 | };
430 |
431 | module.exports = Polygon;
432 |
433 | /**
434 | * Create Polygon from SVG polygon|polyline node
435 | *
436 | * @param {object} node xml2js node from SVG file
437 | * @param {boolean} [line] true : polyline, false : polygon. False as default
438 | * @returns {Polygon} the polygon object
439 | */
440 | module.exports.fromNode = function fromNode(node, line) {
441 | var polygon = new Polygon();
442 |
443 | if (line == true) {
444 | polygon.type = 'polyline';
445 | }
446 | if (typeof node != 'undefined' && typeof node.$ != 'undefined') {
447 | SvgObject.fromNode(polygon, node);
448 |
449 | if (typeof node.$.points != 'undefined') {
450 | polygon.setPointsFromString(node.$.points);
451 | }
452 | }
453 |
454 | return polygon;
455 | };
456 |
457 | /**
458 | * Create Polygon From JSON object
459 | * @param {object} json JSON polygon Object
460 | * @param {boolean} line True : polyline, false : polygon
461 | * @returns {Polygon}
462 | */
463 | module.exports.fromJson = function fromJson(json, line) {
464 |
465 | var polygon = new Polygon();
466 |
467 | if (line == true) {
468 | polygon.type = 'polyline';
469 | }
470 | if (typeof json != 'undefined') {
471 | SvgObject.fromJson(polygon, json);
472 |
473 | if (typeof json.points != 'undefined') {
474 | polygon.points = json.points;
475 | }
476 | }
477 |
478 | return polygon;
479 | };
--------------------------------------------------------------------------------
/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