mirror of
https://github.com/Hopiu/fabric.js.git
synced 2026-05-18 18:41:08 +00:00
Build dist
This commit is contained in:
parent
70c6d5b030
commit
2534e0db5d
4 changed files with 745 additions and 349 deletions
540
dist/fabric.js
vendored
540
dist/fabric.js
vendored
|
|
@ -15,7 +15,9 @@ else {
|
|||
fabric.document = require("jsdom")
|
||||
.jsdom("<!DOCTYPE html><html><head></head><body></body></html>");
|
||||
|
||||
fabric.window = fabric.document.createWindow();
|
||||
if (fabric.document.createWindow) {
|
||||
fabric.window = fabric.document.createWindow();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -31,7 +33,6 @@ fabric.isTouchSupported = "ontouchstart" in fabric.document.documentElement;
|
|||
fabric.isLikelyNode = typeof Buffer !== 'undefined' &&
|
||||
typeof window === 'undefined';
|
||||
|
||||
|
||||
/**
|
||||
* Attributes parsed from all SVG elements
|
||||
* @type array
|
||||
|
|
@ -50,6 +51,7 @@ fabric.SHARED_ATTRIBUTES = [
|
|||
* Pixel per Inch as a default value set to 96. Can be changed for more realistic conversion.
|
||||
*/
|
||||
fabric.DPI = 96;
|
||||
fabric.reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)';
|
||||
|
||||
|
||||
(function() {
|
||||
|
|
@ -466,10 +468,12 @@ fabric.Collection = {
|
|||
* @param {Number|String} value number to operate on
|
||||
* @return {Number|String}
|
||||
*/
|
||||
parseUnit: function(value) {
|
||||
parseUnit: function(value, fontSize) {
|
||||
var unit = /\D{0,2}$/.exec(value),
|
||||
number = parseFloat(value);
|
||||
|
||||
if (!fontSize) {
|
||||
fontSize = fabric.Text.DEFAULT_SVG_FONT_SIZE;
|
||||
}
|
||||
switch (unit[0]) {
|
||||
case 'mm':
|
||||
return number * fabric.DPI / 25.4;
|
||||
|
|
@ -486,6 +490,9 @@ fabric.Collection = {
|
|||
case 'pc':
|
||||
return number * fabric.DPI / 72 * 12; // or * 16
|
||||
|
||||
case 'em':
|
||||
return number * fontSize;
|
||||
|
||||
default:
|
||||
return number;
|
||||
}
|
||||
|
|
@ -2871,7 +2878,7 @@ if (typeof console !== 'undefined') {
|
|||
return attr;
|
||||
}
|
||||
|
||||
function normalizeValue(attr, value, parentAttributes) {
|
||||
function normalizeValue(attr, value, parentAttributes, fontSize) {
|
||||
var isArray = Object.prototype.toString.call(value) === '[object Array]',
|
||||
parsed;
|
||||
|
||||
|
|
@ -2903,7 +2910,7 @@ if (typeof console !== 'undefined') {
|
|||
value = value === 'start' ? 'left' : value === 'end' ? 'right' : 'center';
|
||||
}
|
||||
else {
|
||||
parsed = isArray ? value.map(parseUnit) : parseUnit(value);
|
||||
parsed = isArray ? value.map(parseUnit) : parseUnit(value, fontSize);
|
||||
}
|
||||
|
||||
return (!isArray && isNaN(parsed) ? value : parsed);
|
||||
|
|
@ -2982,7 +2989,7 @@ if (typeof console !== 'undefined') {
|
|||
],
|
||||
|
||||
// == begin transform regexp
|
||||
number = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)',
|
||||
number = fabric.reNum,
|
||||
|
||||
commaWsp = '(?:\\s+,?\\s*|,\\s*)',
|
||||
|
||||
|
|
@ -3085,40 +3092,6 @@ if (typeof console !== 'undefined') {
|
|||
};
|
||||
})();
|
||||
|
||||
function parseFontDeclaration(value, oStyle) {
|
||||
|
||||
// TODO: support non-px font size
|
||||
var match = value.match(/(normal|italic)?\s*(normal|small-caps)?\s*(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\s*(\d+)px(?:\/(normal|[\d\.]+))?\s+(.*)/);
|
||||
|
||||
if (!match) {
|
||||
return;
|
||||
}
|
||||
|
||||
var fontStyle = match[1],
|
||||
// font variant is not used
|
||||
// fontVariant = match[2],
|
||||
fontWeight = match[3],
|
||||
fontSize = match[4],
|
||||
lineHeight = match[5],
|
||||
fontFamily = match[6];
|
||||
|
||||
if (fontStyle) {
|
||||
oStyle.fontStyle = fontStyle;
|
||||
}
|
||||
if (fontWeight) {
|
||||
oStyle.fontWeight = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight);
|
||||
}
|
||||
if (fontSize) {
|
||||
oStyle.fontSize = parseFloat(fontSize);
|
||||
}
|
||||
if (fontFamily) {
|
||||
oStyle.fontFamily = fontFamily;
|
||||
}
|
||||
if (lineHeight) {
|
||||
oStyle.lineHeight = lineHeight === 'normal' ? 1 : lineHeight;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
|
@ -3130,12 +3103,7 @@ if (typeof console !== 'undefined') {
|
|||
attr = normalizeAttr(pair[0].trim().toLowerCase());
|
||||
value = normalizeValue(attr, pair[1].trim());
|
||||
|
||||
if (attr === 'font') {
|
||||
parseFontDeclaration(value, oStyle);
|
||||
}
|
||||
else {
|
||||
oStyle[attr] = value;
|
||||
}
|
||||
oStyle[attr] = value;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -3152,12 +3120,7 @@ if (typeof console !== 'undefined') {
|
|||
attr = normalizeAttr(prop.toLowerCase());
|
||||
value = normalizeValue(attr, style[prop]);
|
||||
|
||||
if (attr === 'font') {
|
||||
parseFontDeclaration(value, oStyle);
|
||||
}
|
||||
else {
|
||||
oStyle[attr] = value;
|
||||
}
|
||||
oStyle[attr] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3236,7 +3199,7 @@ if (typeof console !== 'undefined') {
|
|||
x = el.getAttribute('x') || 0,
|
||||
y = el.getAttribute('y') || 0,
|
||||
el2 = doc.getElementById(xlink).cloneNode(true),
|
||||
currentTrans = (el.getAttribute('transform') || '') + ' translate(' + x + ', ' + y + ')',
|
||||
currentTrans = (el2.getAttribute('transform') || '') + ' translate(' + x + ', ' + y + ')',
|
||||
parentNode;
|
||||
|
||||
for (var j = 0, attrs = el.attributes, l = attrs.length; j < l; j++) {
|
||||
|
|
@ -3246,7 +3209,7 @@ if (typeof console !== 'undefined') {
|
|||
}
|
||||
|
||||
if (attr.nodeName === 'transform') {
|
||||
currentTrans = currentTrans + ' ' + attr.nodeValue;
|
||||
currentTrans = attr.nodeValue + ' ' + currentTrans;
|
||||
}
|
||||
else {
|
||||
el2.setAttribute(attr.nodeName, attr.nodeValue);
|
||||
|
|
@ -3254,6 +3217,7 @@ if (typeof console !== 'undefined') {
|
|||
}
|
||||
|
||||
el2.setAttribute('transform', currentTrans);
|
||||
el2.setAttribute('instantiated_by_use', '1');
|
||||
el2.removeAttribute('id');
|
||||
parentNode = el.parentNode;
|
||||
parentNode.replaceChild(el2, el);
|
||||
|
|
@ -3261,28 +3225,67 @@ if (typeof console !== 'undefined') {
|
|||
}
|
||||
|
||||
/**
|
||||
* Add a <g> element that envelop all SCG elements and makes the viewbox transformMatrix descend on all elements
|
||||
* Add a <g> element that envelop all child elements and makes the viewbox transformMatrix descend on all elements
|
||||
*/
|
||||
function addSvgTransform(doc, matrix) {
|
||||
matrix[3] = matrix[0] = (matrix[0] > matrix[3] ? matrix[3] : matrix[0]);
|
||||
if (!(matrix[0] !== 1 || matrix[3] !== 1 || matrix[4] !== 0 || matrix[5] !== 0)) {
|
||||
function addVBTransform(element, widthAttr, heightAttr) {
|
||||
|
||||
// http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
|
||||
// matches, e.g.: +14.56e-12, etc.
|
||||
var reViewBoxAttrValue = new RegExp(
|
||||
'^' +
|
||||
'\\s*(' + fabric.reNum + '+)\\s*,?' +
|
||||
'\\s*(' + fabric.reNum + '+)\\s*,?' +
|
||||
'\\s*(' + fabric.reNum + '+)\\s*,?' +
|
||||
'\\s*(' + fabric.reNum + '+)\\s*' +
|
||||
'$'
|
||||
),
|
||||
viewBoxAttr = element.getAttribute('viewBox'),
|
||||
scaleX = 1, scaleY = 1, minX = 0, minY = 0,
|
||||
viewBoxWidth, viewBoxHeight, matrix, el;
|
||||
|
||||
if (viewBoxAttr && (viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue))) {
|
||||
minX = -parseFloat(viewBoxAttr[1]),
|
||||
minY = -parseFloat(viewBoxAttr[2]),
|
||||
viewBoxWidth = parseFloat(viewBoxAttr[3]),
|
||||
viewBoxHeight = parseFloat(viewBoxAttr[4]);
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
if (widthAttr && widthAttr !== viewBoxWidth) {
|
||||
scaleX = widthAttr / viewBoxWidth;
|
||||
}
|
||||
if (heightAttr && heightAttr !== viewBoxHeight) {
|
||||
scaleY = heightAttr / viewBoxHeight;
|
||||
}
|
||||
|
||||
// default is to preserve aspect ratio
|
||||
// preserveAspectRatio attribute to be implemented
|
||||
var el = doc.ownerDocument.createElement('g');
|
||||
while (doc.firstChild != null) {
|
||||
el.appendChild(doc.firstChild);
|
||||
}
|
||||
el.setAttribute('transform',
|
||||
'matrix(' + matrix[0] + ' ' +
|
||||
matrix[1] + ' ' +
|
||||
matrix[2] + ' ' +
|
||||
matrix[3] + ' ' +
|
||||
matrix[4] + ' ' +
|
||||
matrix[5] + ')');
|
||||
scaleY = scaleX = (scaleX > scaleY ? scaleY : scaleX);
|
||||
|
||||
doc.appendChild(el);
|
||||
if (!(scaleX !== 1 || scaleY !== 1 || minX !== 0 || minY !== 0)) {
|
||||
return;
|
||||
}
|
||||
matrix = 'matrix(' + scaleX +
|
||||
' 0' +
|
||||
' 0 ' +
|
||||
scaleY + ' ' +
|
||||
(minX * scaleX) + ' ' +
|
||||
(minY * scaleY) + ')';
|
||||
|
||||
if (element.tagName === 'svg') {
|
||||
el = element.ownerDocument.createElement('g');
|
||||
while (element.firstChild != null) {
|
||||
el.appendChild(element.firstChild);
|
||||
}
|
||||
element.appendChild(el);
|
||||
}
|
||||
else {
|
||||
el = element;
|
||||
matrix += el.getAttribute('transform');
|
||||
}
|
||||
|
||||
el.setAttribute('transform', matrix);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -3297,25 +3300,11 @@ if (typeof console !== 'undefined') {
|
|||
fabric.parseSVGDocument = (function() {
|
||||
|
||||
var reAllowedSVGTagNames = /^(path|circle|polygon|polyline|ellipse|rect|line|image|text)$/,
|
||||
|
||||
// http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
|
||||
// \d doesn't quite cut it (as we need to match an actual float number)
|
||||
|
||||
// matches, e.g.: +14.56e-12, etc.
|
||||
reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)',
|
||||
|
||||
reViewBoxAttrValue = new RegExp(
|
||||
'^' +
|
||||
'\\s*(' + reNum + '+)\\s*,?' +
|
||||
'\\s*(' + reNum + '+)\\s*,?' +
|
||||
'\\s*(' + reNum + '+)\\s*,?' +
|
||||
'\\s*(' + reNum + '+)\\s*' +
|
||||
'$'
|
||||
);
|
||||
reViewBoxTagNames = /^(symbol|image|marker|pattern|view)$/;
|
||||
|
||||
function hasAncestorWithNodeName(element, nodeName) {
|
||||
while (element && (element = element.parentNode)) {
|
||||
if (nodeName.test(element.nodeName)) {
|
||||
if (nodeName.test(element.nodeName) && !element.getAttribute('instantiated_by_use')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -3326,34 +3315,19 @@ if (typeof console !== 'undefined') {
|
|||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
var startTime = new Date(),
|
||||
svgUid = fabric.Object.__uid++;
|
||||
|
||||
parseUseDirectives(doc);
|
||||
|
||||
var startTime = new Date(),
|
||||
svgUid = fabric.Object.__uid++,
|
||||
/* http://www.w3.org/TR/SVG/struct.html#SVGElementWidthAttribute
|
||||
* as per spec, width and height attributes are to be considered
|
||||
* 100% if no value is specified.
|
||||
*/
|
||||
var viewBoxAttr = doc.getAttribute('viewBox'),
|
||||
widthAttr = parseUnit(doc.getAttribute('width') || '100%'),
|
||||
heightAttr = parseUnit(doc.getAttribute('height') || '100%'),
|
||||
viewBoxWidth,
|
||||
viewBoxHeight;
|
||||
heightAttr = parseUnit(doc.getAttribute('height') || '100%');
|
||||
|
||||
if (viewBoxAttr && (viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue))) {
|
||||
var minX = parseFloat(viewBoxAttr[1]),
|
||||
minY = parseFloat(viewBoxAttr[2]),
|
||||
scaleX = 1, scaleY = 1;
|
||||
viewBoxWidth = parseFloat(viewBoxAttr[3]);
|
||||
viewBoxHeight = parseFloat(viewBoxAttr[4]);
|
||||
if (widthAttr && widthAttr !== viewBoxWidth ) {
|
||||
scaleX = widthAttr / viewBoxWidth;
|
||||
}
|
||||
if (heightAttr && heightAttr !== viewBoxHeight) {
|
||||
scaleY = heightAttr / viewBoxHeight;
|
||||
}
|
||||
addSvgTransform(doc, [scaleX, 0, 0, scaleY, scaleX * -minX, scaleY * -minY]);
|
||||
}
|
||||
addVBTransform(doc, widthAttr, heightAttr);
|
||||
|
||||
var descendants = fabric.util.toArray(doc.getElementsByTagName('*'));
|
||||
|
||||
|
|
@ -3369,8 +3343,9 @@ if (typeof console !== 'undefined') {
|
|||
}
|
||||
|
||||
var elements = descendants.filter(function(el) {
|
||||
reViewBoxTagNames.test(el.tagName) && addVBTransform(el, 0, 0);
|
||||
return reAllowedSVGTagNames.test(el.tagName) &&
|
||||
!hasAncestorWithNodeName(el, /^(?:pattern|defs)$/); // http://www.w3.org/TR/SVG/struct.html#DefsElement
|
||||
!hasAncestorWithNodeName(el, /^(?:pattern|defs|symbol)$/); // http://www.w3.org/TR/SVG/struct.html#DefsElement
|
||||
});
|
||||
|
||||
if (!elements || (elements && !elements.length)) {
|
||||
|
|
@ -3379,8 +3354,8 @@ if (typeof console !== 'undefined') {
|
|||
}
|
||||
|
||||
var options = {
|
||||
width: widthAttr ? widthAttr : viewBoxWidth,
|
||||
height: heightAttr ? heightAttr : viewBoxHeight,
|
||||
width: widthAttr,
|
||||
height: heightAttr,
|
||||
widthAttr: widthAttr,
|
||||
heightAttr: heightAttr,
|
||||
svgUid: svgUid
|
||||
|
|
@ -3389,7 +3364,6 @@ if (typeof console !== 'undefined') {
|
|||
fabric.gradientDefs[svgUid] = fabric.getGradientDefs(doc);
|
||||
fabric.cssRules[svgUid] = fabric.getCSSRules(doc);
|
||||
// Precedence of rules: style > class > attribute
|
||||
|
||||
fabric.parseElements(elements, function(instances) {
|
||||
fabric.documentParsingTime = new Date() - startTime;
|
||||
if (callback) {
|
||||
|
|
@ -3457,6 +3431,48 @@ if (typeof console !== 'undefined') {
|
|||
}
|
||||
|
||||
extend(fabric, {
|
||||
/**
|
||||
* Parses a short font declaration, building adding its properties to a style object
|
||||
* @static
|
||||
* @function
|
||||
* @memberOf fabric
|
||||
* @param {String} value font declaration
|
||||
* @param {Object} oStyle definition
|
||||
*/
|
||||
parseFontDeclaration: function(value, oStyle) {
|
||||
var fontDeclaration = '(normal|italic)?\\s*(normal|small-caps)?\\s*'
|
||||
+ '(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\\s*('
|
||||
+ fabric.reNum
|
||||
+ '(?:px|cm|mm|em|pt|pc|in)*)(?:\\/(normal|' + fabric.reNum + '))?\\s+(.*)',
|
||||
match = value.match(fontDeclaration);
|
||||
|
||||
if (!match) {
|
||||
return;
|
||||
}
|
||||
var fontStyle = match[1],
|
||||
// font variant is not used
|
||||
// fontVariant = match[2],
|
||||
fontWeight = match[3],
|
||||
fontSize = match[4],
|
||||
lineHeight = match[5],
|
||||
fontFamily = match[6];
|
||||
|
||||
if (fontStyle) {
|
||||
oStyle.fontStyle = fontStyle;
|
||||
}
|
||||
if (fontWeight) {
|
||||
oStyle.fontWeight = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight);
|
||||
}
|
||||
if (fontSize) {
|
||||
oStyle.fontSize = parseUnit(fontSize);
|
||||
}
|
||||
if (fontFamily) {
|
||||
oStyle.fontFamily = fontFamily;
|
||||
}
|
||||
if (lineHeight) {
|
||||
oStyle.lineHeight = lineHeight === 'normal' ? 1 : lineHeight;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Parses an SVG document, returning all of the gradient declarations found in it
|
||||
|
|
@ -3518,7 +3534,8 @@ if (typeof console !== 'undefined') {
|
|||
}
|
||||
|
||||
var value,
|
||||
parentAttributes = { };
|
||||
parentAttributes = { },
|
||||
fontSize;
|
||||
|
||||
if (typeof svgUid === 'undefined') {
|
||||
svgUid = element.getAttribute('svgUid');
|
||||
|
|
@ -3527,12 +3544,14 @@ if (typeof console !== 'undefined') {
|
|||
if (element.parentNode && /^symbol|[g|a]$/i.test(element.parentNode.nodeName)) {
|
||||
parentAttributes = fabric.parseAttributes(element.parentNode, attributes, svgUid);
|
||||
}
|
||||
fontSize = (parentAttributes && parentAttributes.fontSize ) ||
|
||||
element.getAttribute('font-size') || fabric.Text.DEFAULT_SVG_FONT_SIZE;
|
||||
|
||||
var ownAttributes = attributes.reduce(function(memo, attr) {
|
||||
value = element.getAttribute(attr);
|
||||
if (value) {
|
||||
attr = normalizeAttr(attr);
|
||||
value = normalizeValue(attr, value, parentAttributes);
|
||||
value = normalizeValue(attr, value, parentAttributes, fontSize);
|
||||
|
||||
memo[attr] = value;
|
||||
}
|
||||
|
|
@ -3543,7 +3562,9 @@ if (typeof console !== 'undefined') {
|
|||
// (see: http://www.w3.org/TR/SVG/styling.html#UsingPresentationAttributes)
|
||||
ownAttributes = extend(ownAttributes,
|
||||
extend(getGlobalStylesForElement(element, svgUid), fabric.parseStyleAttribute(element)));
|
||||
|
||||
if (ownAttributes.font) {
|
||||
fabric.parseFontDeclaration(ownAttributes.font, ownAttributes);
|
||||
}
|
||||
return _setStrokeFillOpacity(extend(parentAttributes, ownAttributes));
|
||||
},
|
||||
|
||||
|
|
@ -3660,7 +3681,11 @@ if (typeof console !== 'undefined') {
|
|||
}
|
||||
rule = match[1];
|
||||
rule.split(',').forEach(function(_rule) {
|
||||
allRules[_rule.trim()] = fabric.util.object.clone(ruleObj);
|
||||
_rule = _rule.replace(/^svg/i, '').trim();
|
||||
if (_rule === '') {
|
||||
return;
|
||||
}
|
||||
allRules[_rule] = fabric.util.object.clone(ruleObj);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -5356,8 +5381,15 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
|
|||
var patternSource = typeof this.source === 'function' ? this.source() : this.source,
|
||||
patternWidth = patternSource.width / object.getWidth(),
|
||||
patternHeight = patternSource.height / object.getHeight(),
|
||||
patternOffsetX = this.offsetX / object.getWidth(),
|
||||
patternOffsetY = this.offsetY / object.getHeight(),
|
||||
patternImgSrc = '';
|
||||
|
||||
if (this.repeat === 'repeat-x' || this.repeat === 'no-repeat') {
|
||||
patternHeight = 1;
|
||||
}
|
||||
if (this.repeat === 'repeat-y' || this.repeat === 'no-repeat') {
|
||||
patternWidth = 1;
|
||||
}
|
||||
if (patternSource.src) {
|
||||
patternImgSrc = patternSource.src;
|
||||
}
|
||||
|
|
@ -5366,16 +5398,16 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
|
|||
}
|
||||
|
||||
return '<pattern id="SVGID_' + this.id +
|
||||
'" x="' + this.offsetX +
|
||||
'" y="' + this.offsetY +
|
||||
'" x="' + patternOffsetX +
|
||||
'" y="' + patternOffsetY +
|
||||
'" width="' + patternWidth +
|
||||
'" height="' + patternHeight + '">' +
|
||||
'" height="' + patternHeight + '">\n' +
|
||||
'<image x="0" y="0"' +
|
||||
' width="' + patternSource.width +
|
||||
'" height="' + patternSource.height +
|
||||
'" xlink:href="' + patternImgSrc +
|
||||
'"></image>' +
|
||||
'</pattern>';
|
||||
'"></image>\n' +
|
||||
'</pattern>\n';
|
||||
},
|
||||
/* _TO_SVG_END_ */
|
||||
|
||||
|
|
@ -5520,23 +5552,31 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
|
|||
* @return {String} SVG representation of a shadow
|
||||
*/
|
||||
toSVG: function(object) {
|
||||
var mode = 'SourceAlpha';
|
||||
var mode = 'SourceAlpha', fBoxX = 40, fBoxY = 40;
|
||||
|
||||
if (object && (object.fill === this.color || object.stroke === this.color)) {
|
||||
mode = 'SourceGraphic';
|
||||
}
|
||||
|
||||
if (object.width && object.height) {
|
||||
//http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
|
||||
// we add some extra space to filter box to contain the blur ( 20 )
|
||||
fBoxX = Math.abs(this.offsetX / object.getWidth()) * 100 + 20;
|
||||
fBoxY = Math.abs(this.offsetY / object.getHeight()) * 100 + 20;
|
||||
}
|
||||
|
||||
return (
|
||||
'<filter id="SVGID_' + this.id + '" y="-40%" height="180%">' +
|
||||
'<feGaussianBlur in="' + mode + '" stdDeviation="' +
|
||||
'<filter id="SVGID_' + this.id + '" y="-' + fBoxY + '%" height="' + (100 + 2 * fBoxY) + '%" ' +
|
||||
'x="-' + fBoxX + '%" width="' + (100 + 2 * fBoxX) + '%" ' + '>\n' +
|
||||
'\t<feGaussianBlur in="' + mode + '" stdDeviation="' +
|
||||
(this.blur ? this.blur / 3 : 0) +
|
||||
'"></feGaussianBlur>' +
|
||||
'<feOffset dx="' + this.offsetX + '" dy="' + this.offsetY + '"></feOffset>' +
|
||||
'<feMerge>' +
|
||||
'<feMergeNode></feMergeNode>' +
|
||||
'<feMergeNode in="SourceGraphic"></feMergeNode>' +
|
||||
'</feMerge>' +
|
||||
'</filter>');
|
||||
'"></feGaussianBlur>\n' +
|
||||
'\t<feOffset dx="' + this.offsetX + '" dy="' + this.offsetY + '"></feOffset>\n' +
|
||||
'\t<feMerge>\n' +
|
||||
'\t\t<feMergeNode></feMergeNode>\n' +
|
||||
'\t\t<feMergeNode in="SourceGraphic"></feMergeNode>\n' +
|
||||
'\t</feMerge>\n' +
|
||||
'</filter>\n');
|
||||
},
|
||||
/* _TO_SVG_END_ */
|
||||
|
||||
|
|
@ -5811,6 +5851,16 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
|
|||
* originX: 'left',
|
||||
* originY: 'top'
|
||||
* });
|
||||
* @example <caption>overlayImage loaded from cross-origin</caption>
|
||||
* canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
|
||||
* opacity: 0.5,
|
||||
* angle: 45,
|
||||
* left: 400,
|
||||
* top: 400,
|
||||
* originX: 'left',
|
||||
* originY: 'top',
|
||||
* crossOrigin: 'anonymous'
|
||||
* });
|
||||
*/
|
||||
setOverlayImage: function (image, callback, options) {
|
||||
return this.__setBgOverlayImage('overlayImage', image, callback, options);
|
||||
|
|
@ -5852,6 +5902,16 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
|
|||
* originX: 'left',
|
||||
* originY: 'top'
|
||||
* });
|
||||
* @example <caption>backgroundImage loaded from cross-origin</caption>
|
||||
* canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {
|
||||
* opacity: 0.5,
|
||||
* angle: 45,
|
||||
* left: 400,
|
||||
* top: 400,
|
||||
* originX: 'left',
|
||||
* originY: 'top',
|
||||
* crossOrigin: 'anonymous'
|
||||
* });
|
||||
*/
|
||||
setBackgroundImage: function (image, callback, options) {
|
||||
return this.__setBgOverlayImage('backgroundImage', image, callback, options);
|
||||
|
|
@ -5934,7 +5994,7 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
|
|||
fabric.util.loadImage(image, function(img) {
|
||||
this[property] = new fabric.Image(img, options);
|
||||
callback && callback();
|
||||
}, this);
|
||||
}, this, options && options.crossOrigin);
|
||||
}
|
||||
else {
|
||||
this[property] = image;
|
||||
|
|
@ -8332,10 +8392,11 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
|
|||
* @private
|
||||
*/
|
||||
_setObjectScale: function(localMouse, transform, lockScalingX, lockScalingY, by, lockScalingFlip) {
|
||||
var target = transform.target, forbidScalingX = false, forbidScalingY = false;
|
||||
var target = transform.target, forbidScalingX = false, forbidScalingY = false,
|
||||
strokeWidth = target.stroke ? target.strokeWidth : 0;
|
||||
|
||||
transform.newScaleX = localMouse.x / (target.width + target.strokeWidth);
|
||||
transform.newScaleY = localMouse.y / (target.height + target.strokeWidth);
|
||||
transform.newScaleX = localMouse.x / (target.width + strokeWidth / 2);
|
||||
transform.newScaleY = localMouse.y / (target.height + strokeWidth / 2);
|
||||
|
||||
if (lockScalingFlip && transform.newScaleX <= 0 && transform.newScaleX < target.scaleX) {
|
||||
forbidScalingX = true;
|
||||
|
|
@ -8369,8 +8430,9 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
|
|||
_scaleObjectEqually: function(localMouse, target, transform) {
|
||||
|
||||
var dist = localMouse.y + localMouse.x,
|
||||
lastDist = (target.height + (target.strokeWidth)) * transform.original.scaleY +
|
||||
(target.width + (target.strokeWidth)) * transform.original.scaleX;
|
||||
strokeWidth = target.stroke ? target.strokeWidth : 0,
|
||||
lastDist = (target.height + (strokeWidth / 2)) * transform.original.scaleY +
|
||||
(target.width + (strokeWidth / 2)) * transform.original.scaleX;
|
||||
|
||||
// We use transform.scaleX/Y instead of target.scaleX/Y
|
||||
// because the object may have a min scale and we'll loose the proportions
|
||||
|
|
@ -8481,7 +8543,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
|
|||
angle = 360 + angle;
|
||||
}
|
||||
|
||||
t.target.angle = angle;
|
||||
t.target.angle = angle % 360;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -9811,8 +9873,6 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
|
|||
: [ target, this._activeObject ];
|
||||
|
||||
return new fabric.Group(groupObjects, {
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
canvas: this
|
||||
});
|
||||
},
|
||||
|
|
@ -9831,8 +9891,6 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
|
|||
}
|
||||
else if (group.length > 1) {
|
||||
group = new fabric.Group(group.reverse(), {
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
canvas: this
|
||||
});
|
||||
group.addWithUpdate();
|
||||
|
|
@ -11413,10 +11471,12 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
|
|||
return;
|
||||
}
|
||||
|
||||
var mult = this.canvas._currentMultiplier || 1;
|
||||
|
||||
ctx.shadowColor = this.shadow.color;
|
||||
ctx.shadowBlur = this.shadow.blur;
|
||||
ctx.shadowOffsetX = this.shadow.offsetX;
|
||||
ctx.shadowOffsetY = this.shadow.offsetY;
|
||||
ctx.shadowBlur = this.shadow.blur * mult * (this.scaleX + this.scaleY) / 2;
|
||||
ctx.shadowOffsetX = this.shadow.offsetX * mult * this.scaleX;
|
||||
ctx.shadowOffsetY = this.shadow.offsetY * mult * this.scaleY;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -12455,10 +12515,10 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
|
|||
h = strokeWidth;
|
||||
}
|
||||
if (strokeW) {
|
||||
w += strokeWidth;
|
||||
w += w > 0 ? strokeWidth : -strokeWidth;
|
||||
}
|
||||
if (strokeH) {
|
||||
h += strokeWidth;
|
||||
h += h > 0 ? strokeWidth : -strokeWidth;
|
||||
}
|
||||
this.currentWidth = w * this.scaleX;
|
||||
this.currentHeight = h * this.scaleY;
|
||||
|
|
@ -16412,6 +16472,14 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
this.originalState = { };
|
||||
this.callSuper('initialize');
|
||||
|
||||
if (options.originX) {
|
||||
this.originX = options.originX;
|
||||
}
|
||||
|
||||
if (options.originY) {
|
||||
this.originY = options.originY;
|
||||
}
|
||||
|
||||
this._calcBounds();
|
||||
this._updateObjectsCoords();
|
||||
|
||||
|
|
@ -16435,13 +16503,14 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
*/
|
||||
_updateObjectCoords: function(object) {
|
||||
var objectLeft = object.getLeft(),
|
||||
objectTop = object.getTop();
|
||||
objectTop = object.getTop(),
|
||||
center = this.getCenterPoint();
|
||||
|
||||
object.set({
|
||||
originalLeft: objectLeft,
|
||||
originalTop: objectTop,
|
||||
left: objectLeft - this.left,
|
||||
top: objectTop - this.top
|
||||
left: objectLeft - center.x,
|
||||
top: objectTop - center.y
|
||||
});
|
||||
|
||||
object.setCoords();
|
||||
|
|
@ -16700,14 +16769,13 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
* @private
|
||||
*/
|
||||
_setObjectPosition: function(object) {
|
||||
var groupLeft = this.getLeft(),
|
||||
groupTop = this.getTop(),
|
||||
var center = this.getCenterPoint(),
|
||||
rotated = this._getRotatedLeftTop(object);
|
||||
|
||||
object.set({
|
||||
angle: object.getAngle() + this.getAngle(),
|
||||
left: groupLeft + rotated.left,
|
||||
top: groupTop + rotated.top,
|
||||
left: center.x + rotated.left,
|
||||
top: center.y + rotated.top,
|
||||
scaleX: object.get('scaleX') * this.get('scaleX'),
|
||||
scaleY: object.get('scaleY') * this.get('scaleY')
|
||||
});
|
||||
|
|
@ -16803,8 +16871,20 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
};
|
||||
|
||||
if (!onlyWidthHeight) {
|
||||
obj.left = (minXY.x + maxXY.x) / 2 || 0;
|
||||
obj.top = (minXY.y + maxXY.y) / 2 || 0;
|
||||
obj.left = minXY.x || 0;
|
||||
obj.top = minXY.y || 0;
|
||||
if (this.originX === 'center') {
|
||||
obj.left += obj.width / 2;
|
||||
}
|
||||
if (this.originX === 'right') {
|
||||
obj.left += obj.width;
|
||||
}
|
||||
if (this.originY === 'center') {
|
||||
obj.top += obj.height / 2;
|
||||
}
|
||||
if (this.originY === 'bottom') {
|
||||
obj.top += obj.height;
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
|
@ -16928,6 +17008,33 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
*/
|
||||
crossOrigin: '',
|
||||
|
||||
/**
|
||||
* AlignX value, part of preserveAspectRatio (one of "none", "mid", "min", "max")
|
||||
* @see http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute
|
||||
* This parameter defines how the picture is aligned to its viewport when image element width differs from image width.
|
||||
* @type String
|
||||
* @default
|
||||
*/
|
||||
alignX: 'none',
|
||||
|
||||
/**
|
||||
* AlignY value, part of preserveAspectRatio (one of "none", "mid", "min", "max")
|
||||
* @see http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute
|
||||
* This parameter defines how the picture is aligned to its viewport when image element height differs from image height.
|
||||
* @type String
|
||||
* @default
|
||||
*/
|
||||
alignY: 'none',
|
||||
|
||||
/**
|
||||
* meetOrSlice value, part of preserveAspectRatio (one of "meet", "slice").
|
||||
* if meet the image is always fully visibile, if slice the viewport is always filled with image.
|
||||
* @see http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute
|
||||
* @type String
|
||||
* @default
|
||||
*/
|
||||
meetOrSlice: 'meet',
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {HTMLImageElement | String} element Image element
|
||||
|
|
@ -16964,17 +17071,21 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
* You might need to call `canvas.renderAll` and `object.setCoords` after replacing, to render new image and update controls area.
|
||||
* @param {HTMLImageElement} element
|
||||
* @param {Function} [callback] Callback is invoked when all filters have been applied and new image is generated
|
||||
* @param {Object} [options] Options object
|
||||
* @return {fabric.Image} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
setElement: function(element, callback) {
|
||||
setElement: function(element, callback, options) {
|
||||
this._element = element;
|
||||
this._originalElement = element;
|
||||
this._initConfig();
|
||||
this._initConfig(options);
|
||||
|
||||
if (this.filters.length !== 0) {
|
||||
this.applyFilters(callback);
|
||||
}
|
||||
else if (callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
|
@ -17049,7 +17160,10 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
filters: this.filters.map(function(filterObj) {
|
||||
return filterObj && filterObj.toObject();
|
||||
}),
|
||||
crossOrigin: this.crossOrigin
|
||||
crossOrigin: this.crossOrigin,
|
||||
alignX: this.alignX,
|
||||
alignY: this.alignY,
|
||||
meetOrSlice: this.meetOrSlice
|
||||
});
|
||||
},
|
||||
|
||||
|
|
@ -17060,11 +17174,15 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
* @return {String} svg representation of an instance
|
||||
*/
|
||||
toSVG: function(reviver) {
|
||||
var markup = [], x = -this.width / 2, y = -this.height / 2;
|
||||
var markup = [], x = -this.width / 2, y = -this.height / 2,
|
||||
preserveAspectRatio = 'none';
|
||||
if (this.group && this.group.type === 'path-group') {
|
||||
x = this.left;
|
||||
y = this.top;
|
||||
}
|
||||
if (this.alignX !== 'none' && this.alignY !== 'none') {
|
||||
preserveAspectRatio = 'x' + this.alignX + 'Y' + this.alignY + ' ' + this.meetOrSlice;
|
||||
}
|
||||
markup.push(
|
||||
'<g transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '">\n',
|
||||
'<image xlink:href="', this.getSvgSrc(),
|
||||
|
|
@ -17075,7 +17193,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
// so that object's center aligns with container's left/top
|
||||
'" width="', this.width,
|
||||
'" height="', this.height,
|
||||
'" preserveAspectRatio="none"',
|
||||
'" preserveAspectRatio="', preserveAspectRatio, '"',
|
||||
'></image>\n'
|
||||
);
|
||||
|
||||
|
|
@ -17108,6 +17226,20 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets source of an image
|
||||
* @param {String} src Source string (URL)
|
||||
* @param {Function} [callback] Callback is invoked when image has been loaded (and all filters have been applied)
|
||||
* @param {Object} [options] Options object
|
||||
* @return {fabric.Image} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
setSrc: function(src, callback, options) {
|
||||
fabric.util.loadImage(src, function(img) {
|
||||
return this.setElement(img, callback, options);
|
||||
}, this, options && options.crossOrigin);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns string representation of an instance
|
||||
* @return {String} String representation of an instance
|
||||
|
|
@ -17133,7 +17265,6 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
* @chainable
|
||||
*/
|
||||
applyFilters: function(callback) {
|
||||
|
||||
if (!this._originalElement) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -17187,17 +17318,61 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
*/
|
||||
_render: function(ctx, noTransform) {
|
||||
var x, y, imageMargins = this._findMargins();
|
||||
|
||||
x = (noTransform ? this.left : -this.width / 2);
|
||||
y = (noTransform ? this.top : -this.height / 2);
|
||||
|
||||
if (this.meetOrSlice === 'slice') {
|
||||
ctx.beginPath();
|
||||
ctx.rect(x, y, this.width, this.height);
|
||||
ctx.clip();
|
||||
}
|
||||
|
||||
this._element &&
|
||||
ctx.drawImage(
|
||||
this._element,
|
||||
noTransform ? this.left : -this.width/2,
|
||||
noTransform ? this.top : -this.height/2,
|
||||
this.width,
|
||||
this.height
|
||||
);
|
||||
ctx.drawImage(this._element,
|
||||
x + imageMargins.marginX,
|
||||
y + imageMargins.marginY,
|
||||
imageMargins.width,
|
||||
imageMargins.height
|
||||
);
|
||||
this._renderStroke(ctx);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_findMargins: function() {
|
||||
var width = this.width, height = this.height, scales,
|
||||
scale, marginX = 0, marginY = 0;
|
||||
|
||||
if (this.alignX !== 'none' || this.alignY !== 'none') {
|
||||
scales = [this.width / this._element.width, this.height / this._element.height];
|
||||
scale = this.meetOrSlice === 'meet'
|
||||
? Math.min.apply(null, scales) : Math.max.apply(null, scales);
|
||||
width = this._element.width * scale;
|
||||
height = this._element.height * scale;
|
||||
if (this.alignX === 'Mid') {
|
||||
marginX = (this.width - width) / 2;
|
||||
}
|
||||
if (this.alignX === 'Max') {
|
||||
marginX = this.width - width;
|
||||
}
|
||||
if (this.alignY === 'Mid') {
|
||||
marginY = (this.height - height) / 2;
|
||||
}
|
||||
if (this.alignY === 'Max') {
|
||||
marginY = this.height - height;
|
||||
}
|
||||
}
|
||||
return {
|
||||
width: width,
|
||||
height: height,
|
||||
marginX: marginX,
|
||||
marginY: marginY
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
|
@ -17324,7 +17499,8 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
* @static
|
||||
* @see {@link http://www.w3.org/TR/SVG/struct.html#ImageElement}
|
||||
*/
|
||||
fabric.Image.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('x y width height xlink:href'.split(' '));
|
||||
fabric.Image.ATTRIBUTE_NAMES =
|
||||
fabric.SHARED_ATTRIBUTES.concat('x y width height preserveAspectRatio xlink:href'.split(' '));
|
||||
|
||||
/**
|
||||
* Returns {@link fabric.Image} instance from an SVG element
|
||||
|
|
@ -17335,8 +17511,29 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
* @return {fabric.Image} Instance of fabric.Image
|
||||
*/
|
||||
fabric.Image.fromElement = function(element, callback, options) {
|
||||
var parsedAttributes = fabric.parseAttributes(element, fabric.Image.ATTRIBUTE_NAMES);
|
||||
var parsedAttributes = fabric.parseAttributes(element, fabric.Image.ATTRIBUTE_NAMES),
|
||||
align = 'xMidYMid', meetOrSlice = 'meet', alignX, alignY, aspectRatioAttrs;
|
||||
|
||||
if (parsedAttributes.preserveAspectRatio) {
|
||||
aspectRatioAttrs = parsedAttributes.preserveAspectRatio.split(' ');
|
||||
}
|
||||
|
||||
if (aspectRatioAttrs && aspectRatioAttrs.length) {
|
||||
meetOrSlice = aspectRatioAttrs.pop();
|
||||
if (meetOrSlice !== 'meet' && meetOrSlice !== 'slice') {
|
||||
align = meetOrSlice;
|
||||
meetOrSlice = 'meet';
|
||||
}
|
||||
else if (aspectRatioAttrs.length) {
|
||||
align = aspectRatioAttrs.pop();
|
||||
}
|
||||
}
|
||||
//divide align in alignX and alignY
|
||||
alignX = align !== 'none' ? align.slice(1, 4) : 'none';
|
||||
alignY = align !== 'none' ? align.slice(5, 8) : 'none';
|
||||
parsedAttributes.alignX = alignX;
|
||||
parsedAttributes.alignY = alignY;
|
||||
parsedAttributes.meetOrSlice = meetOrSlice;
|
||||
fabric.Image.fromURL(parsedAttributes['xlink:href'], callback,
|
||||
extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes));
|
||||
};
|
||||
|
|
@ -19233,6 +19430,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
|
|||
*/
|
||||
_renderText: function(ctx, textLines) {
|
||||
ctx.save();
|
||||
this._setOpacity(ctx);
|
||||
this._setShadow(ctx);
|
||||
this._setupCompositeOperation(ctx);
|
||||
this._renderTextFill(ctx, textLines);
|
||||
|
|
|
|||
14
dist/fabric.min.js
vendored
14
dist/fabric.min.js
vendored
File diff suppressed because one or more lines are too long
BIN
dist/fabric.min.js.gz
vendored
BIN
dist/fabric.min.js.gz
vendored
Binary file not shown.
540
dist/fabric.require.js
vendored
540
dist/fabric.require.js
vendored
|
|
@ -15,7 +15,9 @@ else {
|
|||
fabric.document = require("jsdom")
|
||||
.jsdom("<!DOCTYPE html><html><head></head><body></body></html>");
|
||||
|
||||
fabric.window = fabric.document.createWindow();
|
||||
if (fabric.document.createWindow) {
|
||||
fabric.window = fabric.document.createWindow();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -31,7 +33,6 @@ fabric.isTouchSupported = "ontouchstart" in fabric.document.documentElement;
|
|||
fabric.isLikelyNode = typeof Buffer !== 'undefined' &&
|
||||
typeof window === 'undefined';
|
||||
|
||||
|
||||
/**
|
||||
* Attributes parsed from all SVG elements
|
||||
* @type array
|
||||
|
|
@ -50,6 +51,7 @@ fabric.SHARED_ATTRIBUTES = [
|
|||
* Pixel per Inch as a default value set to 96. Can be changed for more realistic conversion.
|
||||
*/
|
||||
fabric.DPI = 96;
|
||||
fabric.reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)';
|
||||
|
||||
|
||||
(function() {
|
||||
|
|
@ -466,10 +468,12 @@ fabric.Collection = {
|
|||
* @param {Number|String} value number to operate on
|
||||
* @return {Number|String}
|
||||
*/
|
||||
parseUnit: function(value) {
|
||||
parseUnit: function(value, fontSize) {
|
||||
var unit = /\D{0,2}$/.exec(value),
|
||||
number = parseFloat(value);
|
||||
|
||||
if (!fontSize) {
|
||||
fontSize = fabric.Text.DEFAULT_SVG_FONT_SIZE;
|
||||
}
|
||||
switch (unit[0]) {
|
||||
case 'mm':
|
||||
return number * fabric.DPI / 25.4;
|
||||
|
|
@ -486,6 +490,9 @@ fabric.Collection = {
|
|||
case 'pc':
|
||||
return number * fabric.DPI / 72 * 12; // or * 16
|
||||
|
||||
case 'em':
|
||||
return number * fontSize;
|
||||
|
||||
default:
|
||||
return number;
|
||||
}
|
||||
|
|
@ -2871,7 +2878,7 @@ if (typeof console !== 'undefined') {
|
|||
return attr;
|
||||
}
|
||||
|
||||
function normalizeValue(attr, value, parentAttributes) {
|
||||
function normalizeValue(attr, value, parentAttributes, fontSize) {
|
||||
var isArray = Object.prototype.toString.call(value) === '[object Array]',
|
||||
parsed;
|
||||
|
||||
|
|
@ -2903,7 +2910,7 @@ if (typeof console !== 'undefined') {
|
|||
value = value === 'start' ? 'left' : value === 'end' ? 'right' : 'center';
|
||||
}
|
||||
else {
|
||||
parsed = isArray ? value.map(parseUnit) : parseUnit(value);
|
||||
parsed = isArray ? value.map(parseUnit) : parseUnit(value, fontSize);
|
||||
}
|
||||
|
||||
return (!isArray && isNaN(parsed) ? value : parsed);
|
||||
|
|
@ -2982,7 +2989,7 @@ if (typeof console !== 'undefined') {
|
|||
],
|
||||
|
||||
// == begin transform regexp
|
||||
number = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)',
|
||||
number = fabric.reNum,
|
||||
|
||||
commaWsp = '(?:\\s+,?\\s*|,\\s*)',
|
||||
|
||||
|
|
@ -3085,40 +3092,6 @@ if (typeof console !== 'undefined') {
|
|||
};
|
||||
})();
|
||||
|
||||
function parseFontDeclaration(value, oStyle) {
|
||||
|
||||
// TODO: support non-px font size
|
||||
var match = value.match(/(normal|italic)?\s*(normal|small-caps)?\s*(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\s*(\d+)px(?:\/(normal|[\d\.]+))?\s+(.*)/);
|
||||
|
||||
if (!match) {
|
||||
return;
|
||||
}
|
||||
|
||||
var fontStyle = match[1],
|
||||
// font variant is not used
|
||||
// fontVariant = match[2],
|
||||
fontWeight = match[3],
|
||||
fontSize = match[4],
|
||||
lineHeight = match[5],
|
||||
fontFamily = match[6];
|
||||
|
||||
if (fontStyle) {
|
||||
oStyle.fontStyle = fontStyle;
|
||||
}
|
||||
if (fontWeight) {
|
||||
oStyle.fontWeight = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight);
|
||||
}
|
||||
if (fontSize) {
|
||||
oStyle.fontSize = parseFloat(fontSize);
|
||||
}
|
||||
if (fontFamily) {
|
||||
oStyle.fontFamily = fontFamily;
|
||||
}
|
||||
if (lineHeight) {
|
||||
oStyle.lineHeight = lineHeight === 'normal' ? 1 : lineHeight;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
|
@ -3130,12 +3103,7 @@ if (typeof console !== 'undefined') {
|
|||
attr = normalizeAttr(pair[0].trim().toLowerCase());
|
||||
value = normalizeValue(attr, pair[1].trim());
|
||||
|
||||
if (attr === 'font') {
|
||||
parseFontDeclaration(value, oStyle);
|
||||
}
|
||||
else {
|
||||
oStyle[attr] = value;
|
||||
}
|
||||
oStyle[attr] = value;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -3152,12 +3120,7 @@ if (typeof console !== 'undefined') {
|
|||
attr = normalizeAttr(prop.toLowerCase());
|
||||
value = normalizeValue(attr, style[prop]);
|
||||
|
||||
if (attr === 'font') {
|
||||
parseFontDeclaration(value, oStyle);
|
||||
}
|
||||
else {
|
||||
oStyle[attr] = value;
|
||||
}
|
||||
oStyle[attr] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3236,7 +3199,7 @@ if (typeof console !== 'undefined') {
|
|||
x = el.getAttribute('x') || 0,
|
||||
y = el.getAttribute('y') || 0,
|
||||
el2 = doc.getElementById(xlink).cloneNode(true),
|
||||
currentTrans = (el.getAttribute('transform') || '') + ' translate(' + x + ', ' + y + ')',
|
||||
currentTrans = (el2.getAttribute('transform') || '') + ' translate(' + x + ', ' + y + ')',
|
||||
parentNode;
|
||||
|
||||
for (var j = 0, attrs = el.attributes, l = attrs.length; j < l; j++) {
|
||||
|
|
@ -3246,7 +3209,7 @@ if (typeof console !== 'undefined') {
|
|||
}
|
||||
|
||||
if (attr.nodeName === 'transform') {
|
||||
currentTrans = currentTrans + ' ' + attr.nodeValue;
|
||||
currentTrans = attr.nodeValue + ' ' + currentTrans;
|
||||
}
|
||||
else {
|
||||
el2.setAttribute(attr.nodeName, attr.nodeValue);
|
||||
|
|
@ -3254,6 +3217,7 @@ if (typeof console !== 'undefined') {
|
|||
}
|
||||
|
||||
el2.setAttribute('transform', currentTrans);
|
||||
el2.setAttribute('instantiated_by_use', '1');
|
||||
el2.removeAttribute('id');
|
||||
parentNode = el.parentNode;
|
||||
parentNode.replaceChild(el2, el);
|
||||
|
|
@ -3261,28 +3225,67 @@ if (typeof console !== 'undefined') {
|
|||
}
|
||||
|
||||
/**
|
||||
* Add a <g> element that envelop all SCG elements and makes the viewbox transformMatrix descend on all elements
|
||||
* Add a <g> element that envelop all child elements and makes the viewbox transformMatrix descend on all elements
|
||||
*/
|
||||
function addSvgTransform(doc, matrix) {
|
||||
matrix[3] = matrix[0] = (matrix[0] > matrix[3] ? matrix[3] : matrix[0]);
|
||||
if (!(matrix[0] !== 1 || matrix[3] !== 1 || matrix[4] !== 0 || matrix[5] !== 0)) {
|
||||
function addVBTransform(element, widthAttr, heightAttr) {
|
||||
|
||||
// http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
|
||||
// matches, e.g.: +14.56e-12, etc.
|
||||
var reViewBoxAttrValue = new RegExp(
|
||||
'^' +
|
||||
'\\s*(' + fabric.reNum + '+)\\s*,?' +
|
||||
'\\s*(' + fabric.reNum + '+)\\s*,?' +
|
||||
'\\s*(' + fabric.reNum + '+)\\s*,?' +
|
||||
'\\s*(' + fabric.reNum + '+)\\s*' +
|
||||
'$'
|
||||
),
|
||||
viewBoxAttr = element.getAttribute('viewBox'),
|
||||
scaleX = 1, scaleY = 1, minX = 0, minY = 0,
|
||||
viewBoxWidth, viewBoxHeight, matrix, el;
|
||||
|
||||
if (viewBoxAttr && (viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue))) {
|
||||
minX = -parseFloat(viewBoxAttr[1]),
|
||||
minY = -parseFloat(viewBoxAttr[2]),
|
||||
viewBoxWidth = parseFloat(viewBoxAttr[3]),
|
||||
viewBoxHeight = parseFloat(viewBoxAttr[4]);
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
if (widthAttr && widthAttr !== viewBoxWidth) {
|
||||
scaleX = widthAttr / viewBoxWidth;
|
||||
}
|
||||
if (heightAttr && heightAttr !== viewBoxHeight) {
|
||||
scaleY = heightAttr / viewBoxHeight;
|
||||
}
|
||||
|
||||
// default is to preserve aspect ratio
|
||||
// preserveAspectRatio attribute to be implemented
|
||||
var el = doc.ownerDocument.createElement('g');
|
||||
while (doc.firstChild != null) {
|
||||
el.appendChild(doc.firstChild);
|
||||
}
|
||||
el.setAttribute('transform',
|
||||
'matrix(' + matrix[0] + ' ' +
|
||||
matrix[1] + ' ' +
|
||||
matrix[2] + ' ' +
|
||||
matrix[3] + ' ' +
|
||||
matrix[4] + ' ' +
|
||||
matrix[5] + ')');
|
||||
scaleY = scaleX = (scaleX > scaleY ? scaleY : scaleX);
|
||||
|
||||
doc.appendChild(el);
|
||||
if (!(scaleX !== 1 || scaleY !== 1 || minX !== 0 || minY !== 0)) {
|
||||
return;
|
||||
}
|
||||
matrix = 'matrix(' + scaleX +
|
||||
' 0' +
|
||||
' 0 ' +
|
||||
scaleY + ' ' +
|
||||
(minX * scaleX) + ' ' +
|
||||
(minY * scaleY) + ')';
|
||||
|
||||
if (element.tagName === 'svg') {
|
||||
el = element.ownerDocument.createElement('g');
|
||||
while (element.firstChild != null) {
|
||||
el.appendChild(element.firstChild);
|
||||
}
|
||||
element.appendChild(el);
|
||||
}
|
||||
else {
|
||||
el = element;
|
||||
matrix += el.getAttribute('transform');
|
||||
}
|
||||
|
||||
el.setAttribute('transform', matrix);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -3297,25 +3300,11 @@ if (typeof console !== 'undefined') {
|
|||
fabric.parseSVGDocument = (function() {
|
||||
|
||||
var reAllowedSVGTagNames = /^(path|circle|polygon|polyline|ellipse|rect|line|image|text)$/,
|
||||
|
||||
// http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
|
||||
// \d doesn't quite cut it (as we need to match an actual float number)
|
||||
|
||||
// matches, e.g.: +14.56e-12, etc.
|
||||
reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)',
|
||||
|
||||
reViewBoxAttrValue = new RegExp(
|
||||
'^' +
|
||||
'\\s*(' + reNum + '+)\\s*,?' +
|
||||
'\\s*(' + reNum + '+)\\s*,?' +
|
||||
'\\s*(' + reNum + '+)\\s*,?' +
|
||||
'\\s*(' + reNum + '+)\\s*' +
|
||||
'$'
|
||||
);
|
||||
reViewBoxTagNames = /^(symbol|image|marker|pattern|view)$/;
|
||||
|
||||
function hasAncestorWithNodeName(element, nodeName) {
|
||||
while (element && (element = element.parentNode)) {
|
||||
if (nodeName.test(element.nodeName)) {
|
||||
if (nodeName.test(element.nodeName) && !element.getAttribute('instantiated_by_use')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -3326,34 +3315,19 @@ if (typeof console !== 'undefined') {
|
|||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
var startTime = new Date(),
|
||||
svgUid = fabric.Object.__uid++;
|
||||
|
||||
parseUseDirectives(doc);
|
||||
|
||||
var startTime = new Date(),
|
||||
svgUid = fabric.Object.__uid++,
|
||||
/* http://www.w3.org/TR/SVG/struct.html#SVGElementWidthAttribute
|
||||
* as per spec, width and height attributes are to be considered
|
||||
* 100% if no value is specified.
|
||||
*/
|
||||
var viewBoxAttr = doc.getAttribute('viewBox'),
|
||||
widthAttr = parseUnit(doc.getAttribute('width') || '100%'),
|
||||
heightAttr = parseUnit(doc.getAttribute('height') || '100%'),
|
||||
viewBoxWidth,
|
||||
viewBoxHeight;
|
||||
heightAttr = parseUnit(doc.getAttribute('height') || '100%');
|
||||
|
||||
if (viewBoxAttr && (viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue))) {
|
||||
var minX = parseFloat(viewBoxAttr[1]),
|
||||
minY = parseFloat(viewBoxAttr[2]),
|
||||
scaleX = 1, scaleY = 1;
|
||||
viewBoxWidth = parseFloat(viewBoxAttr[3]);
|
||||
viewBoxHeight = parseFloat(viewBoxAttr[4]);
|
||||
if (widthAttr && widthAttr !== viewBoxWidth ) {
|
||||
scaleX = widthAttr / viewBoxWidth;
|
||||
}
|
||||
if (heightAttr && heightAttr !== viewBoxHeight) {
|
||||
scaleY = heightAttr / viewBoxHeight;
|
||||
}
|
||||
addSvgTransform(doc, [scaleX, 0, 0, scaleY, scaleX * -minX, scaleY * -minY]);
|
||||
}
|
||||
addVBTransform(doc, widthAttr, heightAttr);
|
||||
|
||||
var descendants = fabric.util.toArray(doc.getElementsByTagName('*'));
|
||||
|
||||
|
|
@ -3369,8 +3343,9 @@ if (typeof console !== 'undefined') {
|
|||
}
|
||||
|
||||
var elements = descendants.filter(function(el) {
|
||||
reViewBoxTagNames.test(el.tagName) && addVBTransform(el, 0, 0);
|
||||
return reAllowedSVGTagNames.test(el.tagName) &&
|
||||
!hasAncestorWithNodeName(el, /^(?:pattern|defs)$/); // http://www.w3.org/TR/SVG/struct.html#DefsElement
|
||||
!hasAncestorWithNodeName(el, /^(?:pattern|defs|symbol)$/); // http://www.w3.org/TR/SVG/struct.html#DefsElement
|
||||
});
|
||||
|
||||
if (!elements || (elements && !elements.length)) {
|
||||
|
|
@ -3379,8 +3354,8 @@ if (typeof console !== 'undefined') {
|
|||
}
|
||||
|
||||
var options = {
|
||||
width: widthAttr ? widthAttr : viewBoxWidth,
|
||||
height: heightAttr ? heightAttr : viewBoxHeight,
|
||||
width: widthAttr,
|
||||
height: heightAttr,
|
||||
widthAttr: widthAttr,
|
||||
heightAttr: heightAttr,
|
||||
svgUid: svgUid
|
||||
|
|
@ -3389,7 +3364,6 @@ if (typeof console !== 'undefined') {
|
|||
fabric.gradientDefs[svgUid] = fabric.getGradientDefs(doc);
|
||||
fabric.cssRules[svgUid] = fabric.getCSSRules(doc);
|
||||
// Precedence of rules: style > class > attribute
|
||||
|
||||
fabric.parseElements(elements, function(instances) {
|
||||
fabric.documentParsingTime = new Date() - startTime;
|
||||
if (callback) {
|
||||
|
|
@ -3457,6 +3431,48 @@ if (typeof console !== 'undefined') {
|
|||
}
|
||||
|
||||
extend(fabric, {
|
||||
/**
|
||||
* Parses a short font declaration, building adding its properties to a style object
|
||||
* @static
|
||||
* @function
|
||||
* @memberOf fabric
|
||||
* @param {String} value font declaration
|
||||
* @param {Object} oStyle definition
|
||||
*/
|
||||
parseFontDeclaration: function(value, oStyle) {
|
||||
var fontDeclaration = '(normal|italic)?\\s*(normal|small-caps)?\\s*'
|
||||
+ '(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\\s*('
|
||||
+ fabric.reNum
|
||||
+ '(?:px|cm|mm|em|pt|pc|in)*)(?:\\/(normal|' + fabric.reNum + '))?\\s+(.*)',
|
||||
match = value.match(fontDeclaration);
|
||||
|
||||
if (!match) {
|
||||
return;
|
||||
}
|
||||
var fontStyle = match[1],
|
||||
// font variant is not used
|
||||
// fontVariant = match[2],
|
||||
fontWeight = match[3],
|
||||
fontSize = match[4],
|
||||
lineHeight = match[5],
|
||||
fontFamily = match[6];
|
||||
|
||||
if (fontStyle) {
|
||||
oStyle.fontStyle = fontStyle;
|
||||
}
|
||||
if (fontWeight) {
|
||||
oStyle.fontWeight = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight);
|
||||
}
|
||||
if (fontSize) {
|
||||
oStyle.fontSize = parseUnit(fontSize);
|
||||
}
|
||||
if (fontFamily) {
|
||||
oStyle.fontFamily = fontFamily;
|
||||
}
|
||||
if (lineHeight) {
|
||||
oStyle.lineHeight = lineHeight === 'normal' ? 1 : lineHeight;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Parses an SVG document, returning all of the gradient declarations found in it
|
||||
|
|
@ -3518,7 +3534,8 @@ if (typeof console !== 'undefined') {
|
|||
}
|
||||
|
||||
var value,
|
||||
parentAttributes = { };
|
||||
parentAttributes = { },
|
||||
fontSize;
|
||||
|
||||
if (typeof svgUid === 'undefined') {
|
||||
svgUid = element.getAttribute('svgUid');
|
||||
|
|
@ -3527,12 +3544,14 @@ if (typeof console !== 'undefined') {
|
|||
if (element.parentNode && /^symbol|[g|a]$/i.test(element.parentNode.nodeName)) {
|
||||
parentAttributes = fabric.parseAttributes(element.parentNode, attributes, svgUid);
|
||||
}
|
||||
fontSize = (parentAttributes && parentAttributes.fontSize ) ||
|
||||
element.getAttribute('font-size') || fabric.Text.DEFAULT_SVG_FONT_SIZE;
|
||||
|
||||
var ownAttributes = attributes.reduce(function(memo, attr) {
|
||||
value = element.getAttribute(attr);
|
||||
if (value) {
|
||||
attr = normalizeAttr(attr);
|
||||
value = normalizeValue(attr, value, parentAttributes);
|
||||
value = normalizeValue(attr, value, parentAttributes, fontSize);
|
||||
|
||||
memo[attr] = value;
|
||||
}
|
||||
|
|
@ -3543,7 +3562,9 @@ if (typeof console !== 'undefined') {
|
|||
// (see: http://www.w3.org/TR/SVG/styling.html#UsingPresentationAttributes)
|
||||
ownAttributes = extend(ownAttributes,
|
||||
extend(getGlobalStylesForElement(element, svgUid), fabric.parseStyleAttribute(element)));
|
||||
|
||||
if (ownAttributes.font) {
|
||||
fabric.parseFontDeclaration(ownAttributes.font, ownAttributes);
|
||||
}
|
||||
return _setStrokeFillOpacity(extend(parentAttributes, ownAttributes));
|
||||
},
|
||||
|
||||
|
|
@ -3660,7 +3681,11 @@ if (typeof console !== 'undefined') {
|
|||
}
|
||||
rule = match[1];
|
||||
rule.split(',').forEach(function(_rule) {
|
||||
allRules[_rule.trim()] = fabric.util.object.clone(ruleObj);
|
||||
_rule = _rule.replace(/^svg/i, '').trim();
|
||||
if (_rule === '') {
|
||||
return;
|
||||
}
|
||||
allRules[_rule] = fabric.util.object.clone(ruleObj);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -5356,8 +5381,15 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
|
|||
var patternSource = typeof this.source === 'function' ? this.source() : this.source,
|
||||
patternWidth = patternSource.width / object.getWidth(),
|
||||
patternHeight = patternSource.height / object.getHeight(),
|
||||
patternOffsetX = this.offsetX / object.getWidth(),
|
||||
patternOffsetY = this.offsetY / object.getHeight(),
|
||||
patternImgSrc = '';
|
||||
|
||||
if (this.repeat === 'repeat-x' || this.repeat === 'no-repeat') {
|
||||
patternHeight = 1;
|
||||
}
|
||||
if (this.repeat === 'repeat-y' || this.repeat === 'no-repeat') {
|
||||
patternWidth = 1;
|
||||
}
|
||||
if (patternSource.src) {
|
||||
patternImgSrc = patternSource.src;
|
||||
}
|
||||
|
|
@ -5366,16 +5398,16 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
|
|||
}
|
||||
|
||||
return '<pattern id="SVGID_' + this.id +
|
||||
'" x="' + this.offsetX +
|
||||
'" y="' + this.offsetY +
|
||||
'" x="' + patternOffsetX +
|
||||
'" y="' + patternOffsetY +
|
||||
'" width="' + patternWidth +
|
||||
'" height="' + patternHeight + '">' +
|
||||
'" height="' + patternHeight + '">\n' +
|
||||
'<image x="0" y="0"' +
|
||||
' width="' + patternSource.width +
|
||||
'" height="' + patternSource.height +
|
||||
'" xlink:href="' + patternImgSrc +
|
||||
'"></image>' +
|
||||
'</pattern>';
|
||||
'"></image>\n' +
|
||||
'</pattern>\n';
|
||||
},
|
||||
/* _TO_SVG_END_ */
|
||||
|
||||
|
|
@ -5520,23 +5552,31 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
|
|||
* @return {String} SVG representation of a shadow
|
||||
*/
|
||||
toSVG: function(object) {
|
||||
var mode = 'SourceAlpha';
|
||||
var mode = 'SourceAlpha', fBoxX = 40, fBoxY = 40;
|
||||
|
||||
if (object && (object.fill === this.color || object.stroke === this.color)) {
|
||||
mode = 'SourceGraphic';
|
||||
}
|
||||
|
||||
if (object.width && object.height) {
|
||||
//http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
|
||||
// we add some extra space to filter box to contain the blur ( 20 )
|
||||
fBoxX = Math.abs(this.offsetX / object.getWidth()) * 100 + 20;
|
||||
fBoxY = Math.abs(this.offsetY / object.getHeight()) * 100 + 20;
|
||||
}
|
||||
|
||||
return (
|
||||
'<filter id="SVGID_' + this.id + '" y="-40%" height="180%">' +
|
||||
'<feGaussianBlur in="' + mode + '" stdDeviation="' +
|
||||
'<filter id="SVGID_' + this.id + '" y="-' + fBoxY + '%" height="' + (100 + 2 * fBoxY) + '%" ' +
|
||||
'x="-' + fBoxX + '%" width="' + (100 + 2 * fBoxX) + '%" ' + '>\n' +
|
||||
'\t<feGaussianBlur in="' + mode + '" stdDeviation="' +
|
||||
(this.blur ? this.blur / 3 : 0) +
|
||||
'"></feGaussianBlur>' +
|
||||
'<feOffset dx="' + this.offsetX + '" dy="' + this.offsetY + '"></feOffset>' +
|
||||
'<feMerge>' +
|
||||
'<feMergeNode></feMergeNode>' +
|
||||
'<feMergeNode in="SourceGraphic"></feMergeNode>' +
|
||||
'</feMerge>' +
|
||||
'</filter>');
|
||||
'"></feGaussianBlur>\n' +
|
||||
'\t<feOffset dx="' + this.offsetX + '" dy="' + this.offsetY + '"></feOffset>\n' +
|
||||
'\t<feMerge>\n' +
|
||||
'\t\t<feMergeNode></feMergeNode>\n' +
|
||||
'\t\t<feMergeNode in="SourceGraphic"></feMergeNode>\n' +
|
||||
'\t</feMerge>\n' +
|
||||
'</filter>\n');
|
||||
},
|
||||
/* _TO_SVG_END_ */
|
||||
|
||||
|
|
@ -5811,6 +5851,16 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
|
|||
* originX: 'left',
|
||||
* originY: 'top'
|
||||
* });
|
||||
* @example <caption>overlayImage loaded from cross-origin</caption>
|
||||
* canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
|
||||
* opacity: 0.5,
|
||||
* angle: 45,
|
||||
* left: 400,
|
||||
* top: 400,
|
||||
* originX: 'left',
|
||||
* originY: 'top',
|
||||
* crossOrigin: 'anonymous'
|
||||
* });
|
||||
*/
|
||||
setOverlayImage: function (image, callback, options) {
|
||||
return this.__setBgOverlayImage('overlayImage', image, callback, options);
|
||||
|
|
@ -5852,6 +5902,16 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
|
|||
* originX: 'left',
|
||||
* originY: 'top'
|
||||
* });
|
||||
* @example <caption>backgroundImage loaded from cross-origin</caption>
|
||||
* canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {
|
||||
* opacity: 0.5,
|
||||
* angle: 45,
|
||||
* left: 400,
|
||||
* top: 400,
|
||||
* originX: 'left',
|
||||
* originY: 'top',
|
||||
* crossOrigin: 'anonymous'
|
||||
* });
|
||||
*/
|
||||
setBackgroundImage: function (image, callback, options) {
|
||||
return this.__setBgOverlayImage('backgroundImage', image, callback, options);
|
||||
|
|
@ -5934,7 +5994,7 @@ fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */
|
|||
fabric.util.loadImage(image, function(img) {
|
||||
this[property] = new fabric.Image(img, options);
|
||||
callback && callback();
|
||||
}, this);
|
||||
}, this, options && options.crossOrigin);
|
||||
}
|
||||
else {
|
||||
this[property] = image;
|
||||
|
|
@ -8332,10 +8392,11 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
|
|||
* @private
|
||||
*/
|
||||
_setObjectScale: function(localMouse, transform, lockScalingX, lockScalingY, by, lockScalingFlip) {
|
||||
var target = transform.target, forbidScalingX = false, forbidScalingY = false;
|
||||
var target = transform.target, forbidScalingX = false, forbidScalingY = false,
|
||||
strokeWidth = target.stroke ? target.strokeWidth : 0;
|
||||
|
||||
transform.newScaleX = localMouse.x / (target.width + target.strokeWidth);
|
||||
transform.newScaleY = localMouse.y / (target.height + target.strokeWidth);
|
||||
transform.newScaleX = localMouse.x / (target.width + strokeWidth / 2);
|
||||
transform.newScaleY = localMouse.y / (target.height + strokeWidth / 2);
|
||||
|
||||
if (lockScalingFlip && transform.newScaleX <= 0 && transform.newScaleX < target.scaleX) {
|
||||
forbidScalingX = true;
|
||||
|
|
@ -8369,8 +8430,9 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
|
|||
_scaleObjectEqually: function(localMouse, target, transform) {
|
||||
|
||||
var dist = localMouse.y + localMouse.x,
|
||||
lastDist = (target.height + (target.strokeWidth)) * transform.original.scaleY +
|
||||
(target.width + (target.strokeWidth)) * transform.original.scaleX;
|
||||
strokeWidth = target.stroke ? target.strokeWidth : 0,
|
||||
lastDist = (target.height + (strokeWidth / 2)) * transform.original.scaleY +
|
||||
(target.width + (strokeWidth / 2)) * transform.original.scaleX;
|
||||
|
||||
// We use transform.scaleX/Y instead of target.scaleX/Y
|
||||
// because the object may have a min scale and we'll loose the proportions
|
||||
|
|
@ -8481,7 +8543,7 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
|
|||
angle = 360 + angle;
|
||||
}
|
||||
|
||||
t.target.angle = angle;
|
||||
t.target.angle = angle % 360;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -9811,8 +9873,6 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
|
|||
: [ target, this._activeObject ];
|
||||
|
||||
return new fabric.Group(groupObjects, {
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
canvas: this
|
||||
});
|
||||
},
|
||||
|
|
@ -9831,8 +9891,6 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
|
|||
}
|
||||
else if (group.length > 1) {
|
||||
group = new fabric.Group(group.reverse(), {
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
canvas: this
|
||||
});
|
||||
group.addWithUpdate();
|
||||
|
|
@ -11413,10 +11471,12 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
|
|||
return;
|
||||
}
|
||||
|
||||
var mult = this.canvas._currentMultiplier || 1;
|
||||
|
||||
ctx.shadowColor = this.shadow.color;
|
||||
ctx.shadowBlur = this.shadow.blur;
|
||||
ctx.shadowOffsetX = this.shadow.offsetX;
|
||||
ctx.shadowOffsetY = this.shadow.offsetY;
|
||||
ctx.shadowBlur = this.shadow.blur * mult * (this.scaleX + this.scaleY) / 2;
|
||||
ctx.shadowOffsetX = this.shadow.offsetX * mult * this.scaleX;
|
||||
ctx.shadowOffsetY = this.shadow.offsetY * mult * this.scaleY;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
@ -12455,10 +12515,10 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
|
|||
h = strokeWidth;
|
||||
}
|
||||
if (strokeW) {
|
||||
w += strokeWidth;
|
||||
w += w > 0 ? strokeWidth : -strokeWidth;
|
||||
}
|
||||
if (strokeH) {
|
||||
h += strokeWidth;
|
||||
h += h > 0 ? strokeWidth : -strokeWidth;
|
||||
}
|
||||
this.currentWidth = w * this.scaleX;
|
||||
this.currentHeight = h * this.scaleY;
|
||||
|
|
@ -16412,6 +16472,14 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
this.originalState = { };
|
||||
this.callSuper('initialize');
|
||||
|
||||
if (options.originX) {
|
||||
this.originX = options.originX;
|
||||
}
|
||||
|
||||
if (options.originY) {
|
||||
this.originY = options.originY;
|
||||
}
|
||||
|
||||
this._calcBounds();
|
||||
this._updateObjectsCoords();
|
||||
|
||||
|
|
@ -16435,13 +16503,14 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
*/
|
||||
_updateObjectCoords: function(object) {
|
||||
var objectLeft = object.getLeft(),
|
||||
objectTop = object.getTop();
|
||||
objectTop = object.getTop(),
|
||||
center = this.getCenterPoint();
|
||||
|
||||
object.set({
|
||||
originalLeft: objectLeft,
|
||||
originalTop: objectTop,
|
||||
left: objectLeft - this.left,
|
||||
top: objectTop - this.top
|
||||
left: objectLeft - center.x,
|
||||
top: objectTop - center.y
|
||||
});
|
||||
|
||||
object.setCoords();
|
||||
|
|
@ -16700,14 +16769,13 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
* @private
|
||||
*/
|
||||
_setObjectPosition: function(object) {
|
||||
var groupLeft = this.getLeft(),
|
||||
groupTop = this.getTop(),
|
||||
var center = this.getCenterPoint(),
|
||||
rotated = this._getRotatedLeftTop(object);
|
||||
|
||||
object.set({
|
||||
angle: object.getAngle() + this.getAngle(),
|
||||
left: groupLeft + rotated.left,
|
||||
top: groupTop + rotated.top,
|
||||
left: center.x + rotated.left,
|
||||
top: center.y + rotated.top,
|
||||
scaleX: object.get('scaleX') * this.get('scaleX'),
|
||||
scaleY: object.get('scaleY') * this.get('scaleY')
|
||||
});
|
||||
|
|
@ -16803,8 +16871,20 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
};
|
||||
|
||||
if (!onlyWidthHeight) {
|
||||
obj.left = (minXY.x + maxXY.x) / 2 || 0;
|
||||
obj.top = (minXY.y + maxXY.y) / 2 || 0;
|
||||
obj.left = minXY.x || 0;
|
||||
obj.top = minXY.y || 0;
|
||||
if (this.originX === 'center') {
|
||||
obj.left += obj.width / 2;
|
||||
}
|
||||
if (this.originX === 'right') {
|
||||
obj.left += obj.width;
|
||||
}
|
||||
if (this.originY === 'center') {
|
||||
obj.top += obj.height / 2;
|
||||
}
|
||||
if (this.originY === 'bottom') {
|
||||
obj.top += obj.height;
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
|
@ -16928,6 +17008,33 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
*/
|
||||
crossOrigin: '',
|
||||
|
||||
/**
|
||||
* AlignX value, part of preserveAspectRatio (one of "none", "mid", "min", "max")
|
||||
* @see http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute
|
||||
* This parameter defines how the picture is aligned to its viewport when image element width differs from image width.
|
||||
* @type String
|
||||
* @default
|
||||
*/
|
||||
alignX: 'none',
|
||||
|
||||
/**
|
||||
* AlignY value, part of preserveAspectRatio (one of "none", "mid", "min", "max")
|
||||
* @see http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute
|
||||
* This parameter defines how the picture is aligned to its viewport when image element height differs from image height.
|
||||
* @type String
|
||||
* @default
|
||||
*/
|
||||
alignY: 'none',
|
||||
|
||||
/**
|
||||
* meetOrSlice value, part of preserveAspectRatio (one of "meet", "slice").
|
||||
* if meet the image is always fully visibile, if slice the viewport is always filled with image.
|
||||
* @see http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute
|
||||
* @type String
|
||||
* @default
|
||||
*/
|
||||
meetOrSlice: 'meet',
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param {HTMLImageElement | String} element Image element
|
||||
|
|
@ -16964,17 +17071,21 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
* You might need to call `canvas.renderAll` and `object.setCoords` after replacing, to render new image and update controls area.
|
||||
* @param {HTMLImageElement} element
|
||||
* @param {Function} [callback] Callback is invoked when all filters have been applied and new image is generated
|
||||
* @param {Object} [options] Options object
|
||||
* @return {fabric.Image} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
setElement: function(element, callback) {
|
||||
setElement: function(element, callback, options) {
|
||||
this._element = element;
|
||||
this._originalElement = element;
|
||||
this._initConfig();
|
||||
this._initConfig(options);
|
||||
|
||||
if (this.filters.length !== 0) {
|
||||
this.applyFilters(callback);
|
||||
}
|
||||
else if (callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
|
@ -17049,7 +17160,10 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
filters: this.filters.map(function(filterObj) {
|
||||
return filterObj && filterObj.toObject();
|
||||
}),
|
||||
crossOrigin: this.crossOrigin
|
||||
crossOrigin: this.crossOrigin,
|
||||
alignX: this.alignX,
|
||||
alignY: this.alignY,
|
||||
meetOrSlice: this.meetOrSlice
|
||||
});
|
||||
},
|
||||
|
||||
|
|
@ -17060,11 +17174,15 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
* @return {String} svg representation of an instance
|
||||
*/
|
||||
toSVG: function(reviver) {
|
||||
var markup = [], x = -this.width / 2, y = -this.height / 2;
|
||||
var markup = [], x = -this.width / 2, y = -this.height / 2,
|
||||
preserveAspectRatio = 'none';
|
||||
if (this.group && this.group.type === 'path-group') {
|
||||
x = this.left;
|
||||
y = this.top;
|
||||
}
|
||||
if (this.alignX !== 'none' && this.alignY !== 'none') {
|
||||
preserveAspectRatio = 'x' + this.alignX + 'Y' + this.alignY + ' ' + this.meetOrSlice;
|
||||
}
|
||||
markup.push(
|
||||
'<g transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '">\n',
|
||||
'<image xlink:href="', this.getSvgSrc(),
|
||||
|
|
@ -17075,7 +17193,7 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
// so that object's center aligns with container's left/top
|
||||
'" width="', this.width,
|
||||
'" height="', this.height,
|
||||
'" preserveAspectRatio="none"',
|
||||
'" preserveAspectRatio="', preserveAspectRatio, '"',
|
||||
'></image>\n'
|
||||
);
|
||||
|
||||
|
|
@ -17108,6 +17226,20 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets source of an image
|
||||
* @param {String} src Source string (URL)
|
||||
* @param {Function} [callback] Callback is invoked when image has been loaded (and all filters have been applied)
|
||||
* @param {Object} [options] Options object
|
||||
* @return {fabric.Image} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
setSrc: function(src, callback, options) {
|
||||
fabric.util.loadImage(src, function(img) {
|
||||
return this.setElement(img, callback, options);
|
||||
}, this, options && options.crossOrigin);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns string representation of an instance
|
||||
* @return {String} String representation of an instance
|
||||
|
|
@ -17133,7 +17265,6 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
* @chainable
|
||||
*/
|
||||
applyFilters: function(callback) {
|
||||
|
||||
if (!this._originalElement) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -17187,17 +17318,61 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
* @param {CanvasRenderingContext2D} ctx Context to render on
|
||||
*/
|
||||
_render: function(ctx, noTransform) {
|
||||
var x, y, imageMargins = this._findMargins();
|
||||
|
||||
x = (noTransform ? this.left : -this.width / 2);
|
||||
y = (noTransform ? this.top : -this.height / 2);
|
||||
|
||||
if (this.meetOrSlice === 'slice') {
|
||||
ctx.beginPath();
|
||||
ctx.rect(x, y, this.width, this.height);
|
||||
ctx.clip();
|
||||
}
|
||||
|
||||
this._element &&
|
||||
ctx.drawImage(
|
||||
this._element,
|
||||
noTransform ? this.left : -this.width/2,
|
||||
noTransform ? this.top : -this.height/2,
|
||||
this.width,
|
||||
this.height
|
||||
);
|
||||
ctx.drawImage(this._element,
|
||||
x + imageMargins.marginX,
|
||||
y + imageMargins.marginY,
|
||||
imageMargins.width,
|
||||
imageMargins.height
|
||||
);
|
||||
this._renderStroke(ctx);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_findMargins: function() {
|
||||
var width = this.width, height = this.height, scales,
|
||||
scale, marginX = 0, marginY = 0;
|
||||
|
||||
if (this.alignX !== 'none' || this.alignY !== 'none') {
|
||||
scales = [this.width / this._element.width, this.height / this._element.height];
|
||||
scale = this.meetOrSlice === 'meet'
|
||||
? Math.min.apply(null, scales) : Math.max.apply(null, scales);
|
||||
width = this._element.width * scale;
|
||||
height = this._element.height * scale;
|
||||
if (this.alignX === 'Mid') {
|
||||
marginX = (this.width - width) / 2;
|
||||
}
|
||||
if (this.alignX === 'Max') {
|
||||
marginX = this.width - width;
|
||||
}
|
||||
if (this.alignY === 'Mid') {
|
||||
marginY = (this.height - height) / 2;
|
||||
}
|
||||
if (this.alignY === 'Max') {
|
||||
marginY = this.height - height;
|
||||
}
|
||||
}
|
||||
return {
|
||||
width: width,
|
||||
height: height,
|
||||
marginX: marginX,
|
||||
marginY: marginY
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
|
@ -17324,7 +17499,8 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
* @static
|
||||
* @see {@link http://www.w3.org/TR/SVG/struct.html#ImageElement}
|
||||
*/
|
||||
fabric.Image.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('x y width height xlink:href'.split(' '));
|
||||
fabric.Image.ATTRIBUTE_NAMES =
|
||||
fabric.SHARED_ATTRIBUTES.concat('x y width height preserveAspectRatio xlink:href'.split(' '));
|
||||
|
||||
/**
|
||||
* Returns {@link fabric.Image} instance from an SVG element
|
||||
|
|
@ -17335,8 +17511,29 @@ fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prot
|
|||
* @return {fabric.Image} Instance of fabric.Image
|
||||
*/
|
||||
fabric.Image.fromElement = function(element, callback, options) {
|
||||
var parsedAttributes = fabric.parseAttributes(element, fabric.Image.ATTRIBUTE_NAMES);
|
||||
var parsedAttributes = fabric.parseAttributes(element, fabric.Image.ATTRIBUTE_NAMES),
|
||||
align = 'xMidYMid', meetOrSlice = 'meet', alignX, alignY, aspectRatioAttrs;
|
||||
|
||||
if (parsedAttributes.preserveAspectRatio) {
|
||||
aspectRatioAttrs = parsedAttributes.preserveAspectRatio.split(' ');
|
||||
}
|
||||
|
||||
if (aspectRatioAttrs && aspectRatioAttrs.length) {
|
||||
meetOrSlice = aspectRatioAttrs.pop();
|
||||
if (meetOrSlice !== 'meet' && meetOrSlice !== 'slice') {
|
||||
align = meetOrSlice;
|
||||
meetOrSlice = 'meet';
|
||||
}
|
||||
else if (aspectRatioAttrs.length) {
|
||||
align = aspectRatioAttrs.pop();
|
||||
}
|
||||
}
|
||||
//divide align in alignX and alignY
|
||||
alignX = align !== 'none' ? align.slice(1, 4) : 'none';
|
||||
alignY = align !== 'none' ? align.slice(5, 8) : 'none';
|
||||
parsedAttributes.alignX = alignX;
|
||||
parsedAttributes.alignY = alignY;
|
||||
parsedAttributes.meetOrSlice = meetOrSlice;
|
||||
fabric.Image.fromURL(parsedAttributes['xlink:href'], callback,
|
||||
extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes));
|
||||
};
|
||||
|
|
@ -19233,6 +19430,7 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Imag
|
|||
*/
|
||||
_renderText: function(ctx, textLines) {
|
||||
ctx.save();
|
||||
this._setOpacity(ctx);
|
||||
this._setShadow(ctx);
|
||||
this._setupCompositeOperation(ctx);
|
||||
this._renderTextFill(ctx, textLines);
|
||||
|
|
|
|||
Loading…
Reference in a new issue