UNPKG

fabric

Version:

Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.

138 lines (137 loc) 5.56 kB
import { FILL, NONE, STROKE } from "../../constants.mjs"; import { uid } from "../../util/internals/uid.mjs"; import { matrixToSVG } from "../../util/misc/svgExport.mjs"; import { isFiller } from "../../util/typeAssertions.mjs"; import { escapeXml } from "../../util/lang_string.mjs"; import { colorPropToSVG } from "../../util/misc/svgParsing.mjs"; //#region src/shapes/Object/FabricObjectSVGExportMixin.ts var FabricObjectSVGExportMixin = class { /** * Returns styles-string for svg-export * @param {Boolean} skipShadow a boolean to skip shadow filter output * @return {String} */ getSvgStyles(skipShadow) { const fillRule = this.fillRule ? this.fillRule : "nonzero", strokeWidth = this.strokeWidth ? this.strokeWidth : "0", strokeDashArray = this.strokeDashArray ? this.strokeDashArray.join(" ") : NONE, strokeDashOffset = this.strokeDashOffset ? this.strokeDashOffset : "0", strokeLineCap = this.strokeLineCap ? this.strokeLineCap : "butt", strokeLineJoin = this.strokeLineJoin ? this.strokeLineJoin : "miter", strokeMiterLimit = this.strokeMiterLimit ? this.strokeMiterLimit : "4", opacity = typeof this.opacity !== "undefined" ? this.opacity : "1", visibility = this.visible ? "" : " visibility: hidden;", filter = skipShadow ? "" : this.getSvgFilter(), fill = colorPropToSVG(FILL, this.fill); return [ colorPropToSVG(STROKE, this.stroke), "stroke-width: ", strokeWidth, "; ", "stroke-dasharray: ", strokeDashArray, "; ", "stroke-linecap: ", strokeLineCap, "; ", "stroke-dashoffset: ", strokeDashOffset, "; ", "stroke-linejoin: ", strokeLineJoin, "; ", "stroke-miterlimit: ", strokeMiterLimit, "; ", fill, "fill-rule: ", fillRule, "; ", "opacity: ", opacity, ";", filter, visibility ].map((v) => escapeXml(v)).join(""); } /** * Returns filter for svg shadow * @return {String} */ getSvgFilter() { return this.shadow ? `filter: url(#SVGID_${escapeXml(this.shadow.id)});` : ""; } /** * Returns id attribute for svg output * @return {String} */ getSvgCommons() { return [this.id ? `id="${escapeXml(String(this.id))}" ` : "", this.clipPath ? `clip-path="url(#${this.clipPath.clipPathId})" ` : ""].join(""); } /** * Returns transform-string for svg-export * @param {Boolean} use the full transform or the single object one. * @return {String} */ getSvgTransform(full, additionalTransform = "") { return `${`transform="${matrixToSVG(full ? this.calcTransformMatrix() : this.calcOwnMatrix())}`}${additionalTransform}" `; } /** * Returns svg representation of an instance * This function is implemented in each subclass * This is just because typescript otherwise cryies all the time * @return {Array} an array of strings with the specific svg representation * of the instance */ _toSVG(_reviver) { return [""]; } /** * Returns svg representation of an instance * @param {TSVGReviver} [reviver] Method for further parsing of svg representation. * @return {String} svg representation of an instance */ toSVG(reviver) { return this._createBaseSVGMarkup(this._toSVG(reviver), { reviver }); } /** * Returns svg clipPath representation of an instance * @param {TSVGReviver} [reviver] Method for further parsing of svg representation. * @return {String} svg representation of an instance */ toClipPathSVG(reviver) { return " " + this._createBaseClipPathSVGMarkup(this._toSVG(reviver), { reviver }); } /** * @private */ _createBaseClipPathSVGMarkup(objectMarkup, { reviver, additionalTransform = "" } = {}) { const commonPieces = [this.getSvgTransform(true, additionalTransform), this.getSvgCommons()].join(""), index = objectMarkup.indexOf("COMMON_PARTS"); objectMarkup[index] = commonPieces; return reviver ? reviver(objectMarkup.join("")) : objectMarkup.join(""); } /** * @private */ _createBaseSVGMarkup(objectMarkup, { noStyle, reviver, withShadow, additionalTransform } = {}) { const styleInfo = noStyle ? "" : `style="${this.getSvgStyles()}" `, shadowInfo = withShadow ? `style="${this.getSvgFilter()}" ` : "", clipPath = this.clipPath, vectorEffect = this.strokeUniform ? "vector-effect=\"non-scaling-stroke\" " : "", absoluteClipPath = clipPath && clipPath.absolutePositioned, stroke = this.stroke, fill = this.fill, shadow = this.shadow, markup = [], index = objectMarkup.indexOf("COMMON_PARTS"); let clipPathMarkup; if (clipPath) { clipPath.clipPathId = `CLIPPATH_${uid()}`; clipPathMarkup = `<clipPath id="${clipPath.clipPathId}" >\n${clipPath.toClipPathSVG(reviver)}</clipPath>\n`; } if (absoluteClipPath) markup.push("<g ", shadowInfo, this.getSvgCommons(), " >\n"); markup.push("<g ", this.getSvgTransform(false), !absoluteClipPath ? shadowInfo + this.getSvgCommons() : "", " >\n"); objectMarkup[index] = [ styleInfo, vectorEffect, noStyle ? "" : this.addPaintOrder(), " ", additionalTransform ? `transform="${additionalTransform}" ` : "" ].join(""); if (isFiller(fill)) markup.push(fill.toSVG(this)); if (isFiller(stroke)) markup.push(stroke.toSVG(this)); if (shadow) markup.push(shadow.toSVG(this)); if (clipPath) markup.push(clipPathMarkup); markup.push(objectMarkup.join("")); markup.push("</g>\n"); absoluteClipPath && markup.push("</g>\n"); return reviver ? reviver(markup.join("")) : markup.join(""); } addPaintOrder() { return this.paintFirst !== "fill" ? ` paint-order="${escapeXml(this.paintFirst)}" ` : ""; } }; //#endregion export { FabricObjectSVGExportMixin }; //# sourceMappingURL=FabricObjectSVGExportMixin.mjs.map