0

I have created svg from fabric js. While creating card i have applied text-align to object using below code:

activeObj.set({
    textAlign : 'center',
});
canvas.renderAll();

So my svg is look like below:

https://codepen.io/dhavalsisodiya/pen/BrRbRG

Now when i changed my {company_address} address variable with original value, that text should be align center but it is not.

https://codepen.io/dhavalsisodiya/pen/VXbRzy

Then i have modified SVG manually, I have added text-anchor="middle" in tspan tag of SVG (that is the placing of my varaible)

https://codepen.io/dhavalsisodiya/pen/ZxKPab

After adding that my text is appearing center in SVG.

So is there a way to do that using fabric js, so it contain text-anchor="middle" when i apply text alignment?

Update

I have applied the textAnchor also, but it is not converting to svg.

Codepen

https://codepen.io/dhavalsisodiya/pen/gvQooL

DS9
  • 2,995
  • 4
  • 52
  • 102

2 Answers2

1

There is no text-anchor support in fabric.js for svgs.

You can make it quite easily maybe...

Take the text class from fabric.js, take the _getSVGLeftTopOffsets method for svg export. This methods return -this.width/2 always. I suppose it could return 0 if textAnchor is middle, and this.width/2 if textAnchor is end, becoming:

/**
 * @private
 */
_getSVGLeftTopOffsets: function() {
  return {
    textLeft: this.textAnchor === 'middle' ? 0 : this.textAnchor === 'start' -this.width / 2 : this.width / 2,
    textTop: -this.height / 2,
    lineTop: this.getHeightOfLine(0)
  };
},

Now take the _wrapSVGTextAndBg method and add the text-anchor property to the svg code:

/**
 * @private
 */
_wrapSVGTextAndBg: function(markup, textAndBg) {
  var noShadow = true, filter = this.getSvgFilter(),
      style = filter === '' ? '' : ' style="' + filter + '"',
      textDecoration = this.getSvgTextDecoration(this);
  markup.push(
    '\t<g ', this.getSvgId(), 'transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '"',
    style, '>\n',
    textAndBg.textBgRects.join(''),
    '\t\t<text xml:space="preserve" ',
    'text-anchor="' + this.textAnchor + '" ',
    (this.fontFamily ? 'font-family="' + this.fontFamily.replace(/"/g, '\'') + '" ' : ''),
    (this.fontSize ? 'font-size="' + this.fontSize + '" ' : ''),
    (this.fontStyle ? 'font-style="' + this.fontStyle + '" ' : ''),
    (this.fontWeight ? 'font-weight="' + this.fontWeight + '" ' : ''),
    (textDecoration ? 'text-decoration="' + textDecoration + '" ' : ''),
    'style="', this.getSvgStyles(noShadow), '"', this.addPaintOrder(), ' >',
    textAndBg.textSpans.join(''),
    '</text>\n',
    '\t</g>\n'
  );
},

at this point add to the text class the default for textAnchor value that should be start

AndreaBogazzi
  • 14,323
  • 3
  • 38
  • 63
  • I am able to active that using your answer. But now the issue is i am not able to apply that change on json means (JSON.stringify( canvas1.toJSON(['id']) )) , is there a way to do that? So user don't have to apply text-align each time they edit the card template. – DS9 Mar 23 '18 at 06:25
  • I want to store that property in json which i am using to render canvas on edit time. I am storing json when user first create the card template and then storing that json on database. – DS9 Mar 23 '18 at 06:27
  • 1
    @DS9 check [tutorial toObject](http://fabricjs.com/fabric-intro-part-3), you can extend toObject with `textAnchor` property. – Durga Mar 23 '18 at 06:53
0

Thanks to @AndreaBogazzi, i was able to resolve it using below method:

I have modified fabric js file with below:

Added 'text-anchor="' + this.textAnchor + '" ', in _wrapSVGTextAndBg: function(markup, textAndBg) { as @AndreaBogazzi suggested.

/**
 * @private
 */
_wrapSVGTextAndBg: function(markup, textAndBg) {
  var noShadow = true, filter = this.getSvgFilter(),
      style = filter === '' ? '' : ' style="' + filter + '"',
      textDecoration = this.getSvgTextDecoration(this);
  markup.push(
    '\t<g ', this.getSvgId(), 'transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '"',
    style, '>\n',
    textAndBg.textBgRects.join(''),
    '\t\t<text xml:space="preserve" ',
    'text-anchor="' + this.textAnchor + '" ',
    (this.fontFamily ? 'font-family="' + this.fontFamily.replace(/"/g, '\'') + '" ' : ''),
    (this.fontSize ? 'font-size="' + this.fontSize + '" ' : ''),
    (this.fontStyle ? 'font-style="' + this.fontStyle + '" ' : ''),
    (this.fontWeight ? 'font-weight="' + this.fontWeight + '" ' : ''),
    (textDecoration ? 'text-decoration="' + textDecoration + '" ' : ''),
    'style="', this.getSvgStyles(noShadow), '"', this.addPaintOrder(), ' >',
    textAndBg.textSpans.join(''),
    '</text>\n',
    '\t</g>\n'
  );
},

Then i have modified below:

_createTextCharSpan: function(_char, styleDecl, left, top) {
  var styleProps = this.getSvgSpanStyles(styleDecl, _char !== _char.trim()),
      fillStyles = styleProps ? 'style="' + styleProps + '"' : '';

    //Addded to support text-anhor middle
    if(this.textAnchor==='middle')
    {
        return [
            '\t\t\t<tspan  ',
            fillStyles, '>',
            fabric.util.string.escapeXml(_char),
            '</tspan>\n'
        ].join('');
    }
    else
    {
        return [
            '\t\t\t<tspan x="', toFixed(left, NUM_FRACTION_DIGITS), '" y="',
            toFixed(top, NUM_FRACTION_DIGITS), '" ',
            fillStyles, '>',
            fabric.util.string.escapeXml(_char),
            '</tspan>\n'
          ].join('');
    }
},

Extend the toObjectTo to add textAnchor in json, see here: extend toObject :

$('.text_alignment').click(function(){
   var cur_value = $(this).attr('data-action');
    var activeObj = canvas.getActiveObject();
    if (cur_value != '' && activeObj) {
        activeObj.set({
            textAlign : cur_value,
            textAnchor : 'middle'
        });
        //To modify Object
        activeObj.toObject = (function(toObject) {
          return function() {
            return fabric.util.object.extend(toObject.call(this), {
              textAnchor: 'middle'
            });
          };
        })(activeObj.toObject);

        //activeObj.textAnchor  = cur_value;
        canvas.renderAll();
    }
    else
    {
        alert('Please select text');
        return false;
    }
});

By making above changes, now my variable stays middle even if i modified text.

DS9
  • 2,995
  • 4
  • 52
  • 102