paperjs / paper.js

The Swiss Army Knife of Vector Graphics Scripting – Scriptographer ported to JavaScript and the browser, using HTML5 Canvas. Created by @lehni & @puckey
http://paperjs.org
Other
14.5k stars 1.23k forks source link

TextItem line breaks get ignored when exporting to SVG #988

Open aschmi opened 8 years ago

aschmi commented 8 years ago

There is a problem with the SVG export of TextItems. If it contains multi-line text, it gets exported as a single text-tag which does not provide out-of-the-box support for word-wrapping.

https://github.com/paperjs/paper.js/blob/6975690824b8bed20177bb2d6a32feb8ba801420/src/svg/SvgExport.js#L252

Howerever, there seem to be solutions to this problem:

http://bl.ocks.org/vicapow/cd2046770ceea172d4db

https://www.w3.org/TR/SVG/text.html#TextElement Each ‘text’ element causes a single string of text to be rendered. SVG performs no automatic line breaking or word wrapping. To achieve the effect of multiple lines of text, use one of the following methods:

  • The author or authoring package needs to pre-compute the line breaks and use multiple ‘text’ elements (one for each line of text).
  • The author or authoring package needs to pre-compute the line breaks and use a single ‘text’ element with one or more ‘tspan’ child elements with appropriate values for attributes ‘x’, ‘y’, ‘dx’ and ‘dy’ to set new start positions for those characters which start new lines. (This approach allows user text selection across multiple lines of text -- see Text selection and clipboard operations.)
  • Express the text to be rendered in another XML namespace such as XHTML [XHTML] embedded inline within a ‘foreignObject’ element. (Note: the exact semantics of this approach are not completely defined at this time.)

https://www.safaribooksonline.com/library/view/svg-text-layout/9781491933817/ch04.html

kaelumania commented 8 years ago

@lehni a simple fix might be to split the text by newline and add multiple <tspan> within a text element when creating the SVG nodes (https://github.com/paperjs/paper.js/blob/6975690824b8bed20177bb2d6a32feb8ba801420/src/svg/SvgExport.js#L252)

samtripp commented 8 years ago

Temporary Fix. Replace exportText() with this.

`function exportText(item) { var node = SvgElement.create('text', getTransform(item._matrix, true), formatter);

    for(var i=0; i<item._lines.length; i++) {
        var tspan = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
        tspan.textContent = item._lines[i];
        var dy = item.leading;
        if(i==0) dy = 0;
        tspan.setAttributeNS(null, "x", node.getAttribute('x'));
        tspan.setAttributeNS(null, "dy", dy);
        node.appendChild(tspan);
    }

    return node;
}`
jo-soft commented 7 years ago

@samtripp why is your solution only a temporary fix? where is the "natural" point to attack this issue?

samtripp commented 7 years ago

@jo-soft in my tests this fixed the issue. I say temporary as I assume this bug will be fixed in a future release and you can use this solution in the meantime.

jo-soft commented 6 years ago

it still seems not to be fixed in the current version (0.11.5). any reason why it's not include? or is there any work which can be done to include it in the upcoming version?

kkeith-adg commented 5 years ago

Would love to see this fixed in the library. Currently have a project which would benefit from it greatly.

I was able to work around it for now by using the code mentioned above, but without modifying the core library (since I couldn't find a clean way to do it without forking).

let svg = paper.project.exportSVG({
    onExport: (item, node) => {
        if (item._class === 'PointText') {
            node.textContent = null;
            for (let i = 0; i < item._lines.length; i++) {
                let tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
                tspan.textContent = item._lines[i];
                let dy = item.leading;
                if (i === 0) {
                    dy = 0;
                }
                tspan.setAttributeNS(null, 'x', node.getAttribute('x'));
                tspan.setAttributeNS(null, 'dy', dy);
                node.appendChild(tspan);
            }
        }
        return node;
    }
});

Also worth noting, you can clear out any custom data you attached to the item here as well which can cause the SVG export to fail.

francoly commented 5 years ago

Thanks , your method is very useful

SkiBum326 commented 2 years ago

I just wanted to ping this thread since my project is also affected by this issue. I'm going to try out the code mentioned by @kkeith-adg, but it would be great for this fix to be handled inside paper JS!

Edit: The fix works, but I made a small tweak. The export function was ignoring empty newlines, so I'm appending an extra zero-width space to each line:

paper.project.exportSVG({
            onExport: (item, node) => {
                if (item._class === 'PointText') {
                    node.textContent = null;
                    for (let i = 0; i < item._lines.length; i++) {
                        let tspan = document.createElementNS(
                            'http://www.w3.org/2000/svg',
                            'tspan'
                        );
                        tspan.textContent = `\u200b${item._lines[i]}`;
                        let dy = item.leading;
                        if (i === 0) {
                            dy = 0;
                        }
                        tspan.setAttributeNS(null, 'x', node.getAttribute('x'));
                        tspan.setAttributeNS(null, 'dy', dy);
                        node.appendChild(tspan);
                    }
                }
                return node;
            },
        });
Warvan1 commented 1 year ago

Just Commenting to state that in 2023 this Bug that was pointed out in 2016 is still not fixed :( as it is an issue in a project I am working on as well.