fabricjs / fabric.js

Javascript Canvas Library, SVG-to-Canvas (& canvas-to-SVG) Parser
http://fabricjs.com
Other
29.08k stars 3.52k forks source link

TSPAN SUPPORT #1280

Open Siyfion opened 10 years ago

Siyfion commented 10 years ago

So these issues all relate to the fabric.loadSVGFromString method, when given a string such as:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="-10 -19.297698418170555 201.4199981689453 228.51539500528642"><desc>Created with Snap</desc><defs></defs><g><image xlink:href="http://imgh.us/picnic_12_p1_pic001_uk__v0.svg" preserveAspectRatio="none" x="0" y="0" width="181.42" height="189.92"></image></g><g transform="matrix(1,0,0,1,14.0374,82.2655)"><g><text x="0" y="6.0536095419811105" style="font-family: Jura; font-size: 12px; font-style: normal; font-weight: normal; text-decoration: none; text-anchor: start;"><tspan space="preserve" x="0" dy="1em">&lt;&lt;price&gt;&gt;</tspan></text><rect x="0" y="0" width="90.20648307120202" height="24.26346908396222" fill="#ffffff" stroke="#000000" style="fill-opacity: 0; stroke-width: 0.5px; stroke-dasharray: 5px, 2px;"></rect></g><line x1="45.10324153560101" x2="45.10324153560101" y1="12.13173454198111" y2="-10" fill="none" stroke="#eeeeee" style="stroke-dasharray: 2px, 2px; stroke-width: 0.7px; visibility: hidden;"></line><circle cx="45.10324153560101" cy="12.13173454198111" r="2.5" fill="#000000" stroke="#eeeeee" style="stroke-width: 0.4px; visibility: hidden;"></circle><circle cx="45.10324153560101" cy="-10" r="2.5" fill="#000000" stroke="#eeeeee" style="stroke-width: 0.4px; visibility: hidden;"></circle><rect x="88.20648307120202" y="10.13173454198111" width="4" height="4" fill="#000000" stroke="#eeeeee" style="stroke-width: 0.4px; visibility: hidden;"></rect><rect x="43.10324153560101" y="22.26346908396222" width="4" height="4" fill="#000000" stroke="#eeeeee" style="stroke-width: 0.4px; visibility: hidden;"></rect></g><g transform="matrix(1,0,0,1,14.0374,7.5084)"><g><text x="77.585072682781" y="-3.4223932794852185" style="font-family: 'Mr Dafoe'; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-anchor: middle;"><tspan space="preserve" x="77.585072682781" dy="1em">&lt;&lt;title&gt;&gt;</tspan></text><rect x="0" y="0" width="155.170145365562" height="20.998963441029563" fill="#ffffff" stroke="#000000" style="fill-opacity: 0; stroke-width: 0.5px; stroke-dasharray: 5px, 2px;"></rect></g><line x1="77.585072682781" x2="77.585072682781" y1="10.499481720514781" y2="-10" fill="none" stroke="#eeeeee" style="stroke-dasharray: 2px, 2px; stroke-width: 0.7px; visibility: hidden;"></line><circle cx="77.585072682781" cy="10.499481720514781" r="2.5" fill="#000000" stroke="#eeeeee" style="stroke-width: 0.4px; visibility: hidden;"></circle><circle cx="77.585072682781" cy="-10" r="2.5" fill="#000000" stroke="#eeeeee" style="stroke-width: 0.4px; visibility: hidden;"></circle><rect x="153.170145365562" y="8.499481720514781" width="4" height="4" fill="#000000" stroke="#eeeeee" style="stroke-width: 0.4px; visibility: hidden;"></rect><rect x="75.585072682781" y="18.998963441029563" width="4" height="4" fill="#000000" stroke="#eeeeee" style="stroke-width: 0.4px; visibility: hidden;"></rect></g><g transform="matrix(1,0,0,1,14.0374,31.0128)"><g><text x="0" y="0" style="font-family: 'Shadows Into Light'; font-size: 10px; font-style: normal; font-weight: normal; text-decoration: none; text-anchor: start;"><tspan space="preserve" x="0" dy="1em">&lt;&lt;description&gt;&gt;</tspan></text><rect x="0" y="0" width="154.84369480126873" height="49.72661309883699" fill="#ffffff" stroke="#000000" style="fill-opacity: 0; stroke-width: 0.5px; stroke-dasharray: 5px, 2px;"></rect></g><line x1="77.42184740063436" x2="77.42184740063436" y1="24.863306549418496" y2="-10" fill="none" stroke="#eeeeee" style="stroke-dasharray: 2px, 2px; stroke-width: 0.7px; visibility: hidden;"></line><circle cx="77.42184740063436" cy="24.863306549418496" r="2.5" fill="#000000" stroke="#eeeeee" style="stroke-width: 0.4px; visibility: hidden;"></circle><circle cx="77.42184740063436" cy="-10" r="2.5" fill="#000000" stroke="#eeeeee" style="stroke-width: 0.4px; visibility: hidden;"></circle><rect x="152.84369480126873" y="22.863306549418496" width="4" height="4" fill="#000000" stroke="#eeeeee" style="stroke-width: 0.4px; visibility: hidden;"></rect><rect x="75.42184740063436" y="47.72661309883699" width="4" height="4" fill="#000000" stroke="#eeeeee" style="stroke-width: 0.4px; visibility: hidden;"></rect></g></svg>

Which given the SVG rendered here: screen shot 2014-04-15 at 16 50 04

Produces the following output: screen shot 2014-04-15 at 16 50 18

Which is doing several things wrong:

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

kangax commented 10 years ago

Ok, so I fixed few things.

"visibility: hidden" should now be working. Text is now positioned slightly better, but not exactly correct yet. We need to add support for "text-anchor" which isn't straightforward.

I'll be looking into it more.

christopherney commented 9 years ago

Hi,

I write this code to start to manage tspan position x/y :

  fabric.Text.positionFromTspan = function(element, options) {

        var position = {
            x: 0,
            y: 0
        };

        var childrens = [].slice.call(element.childNodes);

        var tspans = new Array();

        childrens.forEach(function(el, index, array) {

            if (el.nodeName == 'tspan') {
                tspans.push(el);
            }
        });

        if (tspans.length > 0) {

            var tspan = tspans[0];

            var attributes = [].slice.call(tspan.attributes);

            attributes.forEach(function(attr, index, array) {

                if (attr.nodeName == 'x') {
                    position.x = parseFloat(attr.nodeValue);
                } else if (attr.nodeName == 'y') {
                    position.y = parseFloat(attr.nodeValue);
                } 

            });

            return position;

        } else {

            return undefined;
        }
  };

  /**
   * Returns fabric.Text instance from an SVG element (<b>not yet implemented</b>)
   * @static
   * @memberOf fabric.Text
   * @param {SVGElement} element Element to parse
   * @param {Object} [options] Options object
   * @return {fabric.Text} Instance of fabric.Text
   */
  fabric.Text.fromElement = function(element, options) {
    if (!element) {
      return null;
    }

    var parsedAttributes = fabric.parseAttributes(element, fabric.Text.ATTRIBUTE_NAMES);
    options = fabric.util.object.extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes);

    // TSPAN :
    var position = this.positionFromTspan(element, options);

    if (position != undefined) {
        options.top = position.y;
        options.left = position.x;
    } else {
        options.top = options.top || 0;
        options.left = options.left || 0;
    }

    if ('dx' in parsedAttributes) {
      options.left += parsedAttributes.dx;
    }
    if ('dy' in parsedAttributes) {
      options.top += parsedAttributes.dy;
    }

    if (!('fontSize' in options)) {
      options.fontSize = fabric.Text.DEFAULT_SVG_FONT_SIZE;
    }

    if (!options.originX) {
      options.originX = 'left';
    }
    var textContent = element.textContent.replace(/^\s+|\s+$|\n+/g, '').replace(/\s+/g, ' '),
        text = new fabric.Text(textContent, options),
        /*
          Adjust positioning:
            x/y attributes in SVG correspond to the bottom-left corner of text bounding box
            top/left properties in Fabric correspond to center point of text bounding box
        */
        offX = 0;

    if (text.originX === 'left') {
      offX = text.getWidth() / 2;
    }
    if (text.originX === 'right') {
      offX = -text.getWidth() / 2;
    }
    text.set({
      left: text.getLeft() + offX,
      top: text.getTop() - text.getHeight() / 2 + text.fontSize * (0.18 + text._fontSizeFraction) /* 0.3 is the old lineHeight */
    });

    return text;
  };
kangax commented 9 years ago

I closed #820 but we should just not forget to account for rotate attribute on tspans

liatK commented 8 years ago

Hi guys, I'm really straggling with this issue, I'm creating an editor and it's very important to have the ability to save and load back for edit exactly as it was edited, that's proven to be difficult when importing back from svg it re position text elements, is there any clean fix/workaround for this? it's becoming crucial whether or not keep using this approach if no solution will be found.
Thanks!

asturur commented 8 years ago

can t you just use json for exporting and reimporting? svg is not meant for that

DukeCityDigital commented 7 years ago

Wondering about this issue - any possible workarounds related to how the .SVG is initially constructed/saved? Great library thanks

asa9 commented 7 years ago

Hello,

@asturur you are suggesting to use json for exporting and importing, but what if we need to create an export pdf file ? I am using both exporting formats: 1. SVG - for PDF export 2. JSON for later import in canvas. But now I have encountered an issue with updating text in SVG. For text objects I am using texbox as I need text-wrapping. The thing is that after export in SVG each letter is kept in separate tspan with its own styling. But later when I want to update that text (for example some tranbsaltion key ahs been added to canvas and before export to pdf I want to change that key with real translated value) I don't know what styling to apply to tspans. So my question mainly is how fabirc is calculating styling for tspans position, etc. Or is there other way to update text in exported svg without loosing styling and avoiding tspans styling calculation ?

Thanks in advance

asturur commented 7 years ago

there is not. Svg is pretty much an image with POOR text support.

you could make a trick, modify the svg export, add an attribute to text object with a json stringfy of the style object, and make some custom code when reimport it.

not something i want to support.

i want to do proper TSPAN support but i did not start yet

asturur commented 7 years ago

Maybe i understood bad. what styling are you talking about? color, font, size?

asa9 commented 7 years ago

@asturur first of all thanks for your very quick response.

My main problem is that pdf export is done via cron job, when editor is closed. So I can't export svg directly from canvas. I am taking svg from DB and tryng to translate it before export.

So now about stylings mentioned in above comment: I am talking about the style of each tspan element. When we export canvas to svg each letter of canvas text object is kept in separate "" tag that keeps some styling which I guess is responsible for letter position, alignment, font, etc. For example lets consider that in my SVG there is '#' translation key, that should be replaced with 'Hello' string. Please have a look on below tspan element: <tspan x="-100" y="7.87" style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(0,0,0); fill-rule: ; opacity: 1;">#</tspan>

So for translating SVG I need to replace '#' to 'Hello' which means that above tspan(s) list should be changed to new tspan list containing letters for 'Hello' (Means that 1 tspan should be replaced with 5 tspans). Well for 1st tspans can be kept the same x, y, style (but maybe even this can not be kept, as if I have translated 2nd letter bigger/smaller than trasnaltion key letter than might be that x coordinate should be changed as well), but what about other new tspans. The problem is that I don't know what x, y, style to apply for each letter of "Hello". I guess as for styling I can take translation keys last letter style for all other newer elements, but what about x, y position. This is my main problem. So the question can be rephrased so how x, y attribute values are calculated for each tspan based on letter that it contains. Or in general how a text can be changed with other text in SVG without loosing any information about its styling.

Actually I was doing translation of SVG without taking into account tspan at all. Just each text ndoeValue was changed $text->nodeValue = $translatedText (Please refer to below translateTemplateSVG ). BUt then I realised that with this approach text alignments are lost (left, right, center), so I came to an idea that tspans should be kept to have everything working as they supposed to.

static public function translateTemplateSVG($svg) {
        $doc = new DOMDocument();
        $doc->loadXML($svg);
        $texts = $doc->getElementsByTagName('text');

        if(!$texts){
            return $svg;
        }

        foreach ($texts as $text) {
            $currentText = preg_replace('/\s+/', '', $text->nodeValue);
            $traslatedText = self::translateText($currentText);
            if($traslatedText){
                $text->nodeValue = $traslatedText;
            }
        }

        return $doc->saveXML();
}

Looking forward to hear from you soon.

megalithic commented 6 years ago

Hello @asturur; I was curious if there is any future plan for implementing tspansupport for multiline text importing. I see it was removed from the 2.0.0 milestone. :(

asturur commented 6 years ago

clippath is priority and masks. tspan support has started with the vertical offset that is ready in a branch and unmerged.

megalithic commented 6 years ago

Thanks @asturur! Mind sharing the branch that has the changes for handling the vertical offset. Didn't see it in the stale or active branches. Apologies if it is mixed in with other branches and I just didn't look hard enough. Thanks for your work on this project!

asturur commented 6 years ago

is in the branch list, is there. search for superscrit or subscript

On 21 Dec 2017 21:13, "Seth Messer" notifications@github.com wrote:

Thanks @asturur https://github.com/asturur! Mind sharing the branch that has the changes for handling the vertical offset. Didn't see it in the stale or active branches. Apologies if it is mixed in with other branches and I just didn't look hard enough. Thanks for your work on this project!

ā€” You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/kangax/fabric.js/issues/1280#issuecomment-353447070, or mute the thread https://github.com/notifications/unsubscribe-auth/ABI4QKS99LBhcG8XhL4XmE5dr06hLJfWks5tCrwEgaJpZM4Bysv3 .

asturur commented 6 years ago

baseline-shift is the name. but it does not contain much about tspan yet

On 21 Dec 2017 21:20, "Andrea Bogazzi" andreabogazzi79@gmail.com wrote:

is in the branch list, is there. search for superscrit or subscript

On 21 Dec 2017 21:13, "Seth Messer" notifications@github.com wrote:

Thanks @asturur https://github.com/asturur! Mind sharing the branch that has the changes for handling the vertical offset. Didn't see it in the stale or active branches. Apologies if it is mixed in with other branches and I just didn't look hard enough. Thanks for your work on this project!

ā€” You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/kangax/fabric.js/issues/1280#issuecomment-353447070, or mute the thread https://github.com/notifications/unsubscribe-auth/ABI4QKS99LBhcG8XhL4XmE5dr06hLJfWks5tCrwEgaJpZM4Bysv3 .

megalithic commented 6 years ago

Thanks so much @asturur; If y'all have any ideas/details around what you were wanting to see happen with that, I'd be happy to work on it in that baseline-shift branch or in a new branch. It's a pretty important capability for what I'm working on, so I'd for sure be available to work on implementing that feature, at least to some basic degree. Thanks and let me know! Have a great day and Christmas/holiday season if I don't hear back from you.

asturur commented 6 years ago

well if you want to research on it, we still need parsing of text element, preserving the tspan properties for each text chunk and also the dx and dy attribute of tspans can be in written in many different ways and they need to be parsed as well.

tspan that look like different text lines will probably not be different text lines of the same text. will likely be different fabricjs texts.

On 21 Dec 2017 21:28, "Seth Messer" notifications@github.com wrote:

Thanks so much @asturur https://github.com/asturur; If y'all have any ideas/details around what you were wanting to see happen with that, I'd be happy to work on it in that baseline-shift branch or in a new branch. It's a pretty important capability for what I'm working on, so I'd for sure be available to work on implementing that feature, at least to some basic degree. Thanks and let me know! Have a great day and Christmas/holiday season if I don't hear back from you.

ā€” You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/kangax/fabric.js/issues/1280#issuecomment-353450150, or mute the thread https://github.com/notifications/unsubscribe-auth/ABI4QOyVBOpHz6stmMD2TTZPoaAQz1qZks5tCr98gaJpZM4Bysv3 .

Sashkan commented 5 years ago

Isn't it possible to just remove the tspan from the text elements, as a workaround ? Like, in the reviver method ?

asturur commented 5 years ago

Tspans are removed indeed, but positions and different styles are lost.

evious commented 5 years ago

Good afternoon friends I had the same problem to position the tspan with the loadSVGFromString method, I noticed that creating by itext and passing the text configuration parameters the position is more accurate. So far I have managed to position it as in the following example codepen but want to achieve a more exact position. I have tried different methods but as far as Tspan still has no support in the versions of fabricjs

haki9 commented 5 years ago

I change the fabrics.js šŸ‘ https://github.com/haki9/fabric.js/commit/6d049ed0954ac08b7cd927975ca500e6ddd4fd0b

And code javascript:

fabric.loadSVGFromURL(url, function (
            objects,
            options,
            elements,
            allElements
        ) {
            objects.forEach(function (obj, index) {
                if (obj["type"] == "image") {
                    if (obj["id"].toUpperCase() == "BACKGROUND") {
                        fabric.Image.fromURL(obj['xlink:href'], function (img) {
                            canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas));
                        });
                    } else {
                        obj.set({
                            selectable: true,
                            evented: true
                        });
                        canvas.add(obj).renderAll();
                    }
                } else if (obj["type"] == "text") {
                    var element = elements[index];
                    var childrens = [].slice.call(element.childNodes);
                    var value = "";
                    childrens.forEach(function (el, index, array) {
                        if (el.nodeName == "tspan") {
                            value += el.childNodes[0].nodeValue;
                        } else if (el.nodeName == "#text") {
                            value += el.nodeValue;
                        }

                        if (index < childrens.length - 1) {
                            value += "\n";
                        }
                    });

                    value =
                        obj["text-transform"] == "uppercase"
                            ? value.toUpperCase()
                            : value;

                    var text = new fabric.IText(obj.text, obj.toObject());
                    text.set({
                        text: value,
                        type: 'i-text'
                    });

                    var left = 0;
                    var _textAlign = obj.get("textAnchor")
                        ? obj.get("textAnchor")
                        : "left";
                    switch (_textAlign) {
                        case "center":
                            left = obj.left - text.getScaledWidth() / 2;
                            break;
                        case "right":
                            left = obj.left - text.getScaledWidth();
                            break;
                        default:
                            left = obj.left;
                            break;
                    }

                    text.set({
                        left: left,
                        textAlign: _textAlign
                    });
                    canvas.add(text).renderAll();
                } else {
                    canvas.add(obj).renderAll();
                }
            });

            canvas.setWidth(options.width);
            canvas.setHeight(options.height);
            canvas.calcOffset();

And this is SVG file for testing: https://drive.google.com/open?id=1laXzM0SyK1URzHVy9blCFeBDcaWlV78P

Sashkan commented 4 years ago

@haki9 I used your method, it works just fine for styling, but as soon as I put multiple lines in my text, the top positioning of the text gets messed up.

chileap commented 4 years ago

Any updated with this?

asturur commented 4 years ago

No, no one worked on it.

rohitkatlaa commented 4 years ago

Any updates with this??

asturur commented 4 years ago

no zero, zero. Although with dx,dy support we have someone could try to implement it. A new svg parser would make things easeir

reinkepatrick commented 4 years ago

@asturur Something new nowadays or any alternatives?

asturur commented 4 years ago

No, not at all. I ll probably get it done after i made curved text better and after i improved the masks pr

shubhamdogra7042 commented 3 years ago

Hi @asturur, is it fixable. any workaround ?

mr-ehsan-hashmi commented 3 years ago

@asturur any update regarding ### TSpan working in SVG?

asturur commented 3 years ago

no one right now has time to work on this

kevinreuss commented 2 years ago

@asturur is there a solution? I am trying to load an SVG file with tspans and the text gets rendered much too large

bluegaspode commented 2 years ago

I just added a bounty of 200$ on this open bug. Maybe others can join as well, as there seem to be multiple people interested in a fix.

https://app.bountysource.com/issues/1676489-tspan-support

I created the following smaller testcase, with some texts of different sizes and guidelines, which makes it easy to check the results. It was created with Inkscape and optimized for better readability with svgo

text-testcase1

designlook commented 1 year ago

@bluegaspode Maybe something like this? I just did it quickly and didnt really flush out everything.. but just wanted to see.

https://jsfiddle.net/scottyu/0f3jkq7h/18/

bluegaspode commented 1 year ago

For the given example it works already, thats cool!

I have a second example, with just 4 aligned text-blocks, where it doesn't work though :( Again it was created with Inkscape and optimized via svgo and I don't see any strong specialties in the markup.

<svg xmlns="http://www.w3.org/2000/svg" width="300" height="100" id="svg4136" version="1.1">
    <g id="layer1">
        <text xml:space="preserve" style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="30" y="55" id="text4933"><tspan id="tspan4935" x="30" y="55" style="font-size:15px;line-height:1.25">Ā </tspan></text>
        <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="100.129" y="36" id="text4969"><tspan id="tspan4971" x="100.129" y="36" style="font-size:12.5px;line-height:1.25">###LINE2</tspan><tspan x="100.129" y="52" id="tspan4992" style="font-size:12.5px;line-height:1.25">###LINE3</tspan></text>
        <rect style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.59996504;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" id="rect4994" width="200.12" height="279.7" x="0" y="0" ry=".337"/>
        <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="100.129" y="18" id="text4996"><tspan id="tspan4998" x="100.129" y="18" style="font-size:12.5px;line-height:1.25">###LINE1</tspan></text>
        <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="99.717" y="71" id="text4996-5"><tspan id="tspan4998-9" x="99.717" y="71.508" style="font-size:10px;line-height:1.25">###KNX</tspan></text>
        <path style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.6;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" d="M0 60.275h200" id="path4200"/>
        <path style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="M0 75h200" id="path4200-8"/>
    </g>
</svg>

target image should look like this (you can check with a browser or Inkscape)

image

with the given jsfiddle looks like this:

image

Once it works with the callback for fabric it would be great to see it as a pull request to fabric.js library, i.e. extending the loadSVGFromString function to support tspans 'natively'.

somq commented 1 year ago

Any update on this?

Selimology commented 1 year ago

Any update on this?

asturur commented 1 year ago

no we are busy with other tasks sadly and we didn't do any new feature development

ShaMan123 commented 1 year ago

but if anyone wants to PR I can back them

insinfo commented 9 months ago

@gloriousjob @more-strive

Did you find any alternative solution to import a simple svg with correctly aligned text?

<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="637" height="1012" viewBox="305.5 50 637 1012" xml:space="preserve">
<desc>Created with Fabric.js 5.3.0</desc>
<defs>
</defs>
<g transform="matrix(1 0 0 1 624.5 556.5)" id="stage">
<rect style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(255,255,255); fill-rule: nonzero; opacity: 1;" x="-318.5" y="-506" rx="0" ry="0" width="637" height="1012"/>
</g>
<g transform="matrix(6.49 0 0 10.53 628.5 566.91)" id="rect-001pu2rkl">
<rect style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(135,216,203); fill-rule: nonzero; opacity: 1;" x="-49.5" y="-49.5" rx="0" ry="0" width="99" height="99"/>
</g>
<g transform="matrix(6.66 0 0 6.66 486.08 127.53)" style="" id="itext-001k23wx5">
        <text xml:space="preserve" font-family="Calibri" font-size="20" font-style="normal" font-weight="normal" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(254,254,254); fill-rule: nonzero; opacity: 1; white-space: pre;"><tspan x="-21.69" y="6.28">Texto</tspan></text>
</g>
</svg>

svg

cracha_modelo

Fabric.js

image

more-strive commented 9 months ago

Most of my files are in PDF and PSD formats, and now they are parsed directly into JSON as templates
Github: https://github.com/dromara/yft-design demo: https://yft.design Of course, there are still some bugs, but there are no issues with parsing the content

insinfo commented 9 months ago

@more-strive I accessed your demo, and saw that it has PDF and CDR import, how did you manage to implement the conversion from PDF/CDR to the fabic.js json format?

I managed to implement export to PDF, exporting from fabic.js to svg and adding it to the PDF via jsPDF

more-strive commented 9 months ago

PDF is parsed through pymupdf, but CDR has not yet implemented parsing
Github: https://githubfast.com/dromara/yft-design

developersgit commented 4 months ago

Any Update on this? Or any workaround to get the correct left and top values for text elements when importing the svg to canvas?

asturur commented 4 months ago

Hey @developersgit this is a long standing feature i was planning to take up again now that 6.0 is stable. This one, svg masks, single object rendering, text on a path, and canvas rotation i think were the long standing one. I still need to write some basic docs for 6.0 before going on more development work

rryando commented 3 months ago

I found the workaround for text tag shifting , this caused by the object from loadSVGFromString func being flattened, but when the text tag that has tspan child going to flattened, the tspan attributes are not being calculated, especially attributes that control the left and top pos (in this case x,y attributes), so, for the workaround:

loadSVGFromString(reader.result as string).then((output) => {
    const {objects, elements} = output

    objects.forEach((obj, index) => {
      if (obj && obj.type === 'text') {
        const currentElement = elements[index]
        if (currentElement.children.length > 0 && currentElement.children[0].tagName === 'tspan') {
          const tspan = currentElement.children[0]
          // @ts-expect-error; TODO: define tspan types properly
          const { x, y } = tspan.attributes

          // THE FIX: Update x and y position of text object
          obj.left += Number(x.value)
          obj.top += Number(y.value)
        }
        // @ts-expect-error; TODO: define obj types properly
        const text = new Textbox(obj.text, {
          ...obj,
          snapAngle: 45,
          snapThreshold: 1,
          editable: true,
        })
        return canvas?.add(text);
      }
      obj && canvas?.add(obj);
  })
})

hope this helps