fabricjs / fabric.js

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

Add support for vertical text #511

Open raichu opened 11 years ago

raichu commented 11 years ago

Vertical text is very commonly used with CJK text, so please please :)

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

raichu commented 11 years ago

A related feature request would be text along an arc.

raichu commented 11 years ago

It also seems impossible to achieve this using SVG (#512)

raichu commented 11 years ago

Wouldn't rotating glyphs (separately) by 90 degrees (counter-clockwise) suffice to have basic vertical text support? Justification, underline and strike-through wouldn't work of course, but it beats having no CJK/vertical text support.

I believe this would be a very simple change, but I'm not sure where to start.

kangax commented 11 years ago

Well, since we're talking about text rendering, it's in text.class.js. All "classes" use "render" (public, often inherited from fabric.Object) and "_render" (private, custom rendering logic) methods. Then there's 2 types of rendering for text — via Cufon (old, deprecated, using cufon.js) and native (using canvas native text rendering capabilties). So you'd probably need to look into _renderViaNative (https://github.com/kangax/fabric.js/blob/master/src/text.class.js#L272-L305). Since we support multiline rendering (unlike native canvas text rendering), we have that logic on top. 2 relevant methods are _renderTextFill and _renderTextStroke — they render actual text (the rest take care of background, decoration, etc.). From there, you'll probably find _drawTextLine which has some logic to determine character positioning depending on alignment of text, etc. but essentially boils down to calling ctx[method] (where method is "fillText" or "strokeText"). That's the part you would need to break down into per-character rendering, together with custom transformation.

raichu commented 11 years ago

I see; native eventually calls canvas' strokeText and fillText, and as far as I found, it's not possible to apply transformation per-glyph (as the HTML5 canvas standard stands today). This means I either need to implement something like strokeText and fillText myself, or modify Cufon, which is deprecated :(

zhangzhzh commented 11 years ago

I think it is important.

raichu commented 11 years ago

Here's a crazy idea :) Since canvas doesn't support it, how about calling fillText() for each CJK glyph, with a fixed space in between? I don't think one needs to worry about kerning etc in vertical text. This requires different renderer (and toSVG function) for vertical text mode though, so maybe it's better to consider a whole new VerticalText object instead of modifying Text?

RottenFinger commented 8 years ago

@raichu did you make it?

limichange commented 6 years ago

😅 what's the result?

spiritree commented 6 years ago

any solution? :sweat:

bkstorm commented 6 years ago

I think this feature is necessary. Actually, I'm trying to implement vertical text, it's very common with CJK text.

asturur commented 6 years ago

Please before actually implementing it with the idea that the feature is outside the of your personal project, open an issue on the api-discussion repository and let s discuss there: what languages use vertical text and what it looks like. how vertical text static should work. how interaction should work. what code is necessary.

i ll be very happy to be available in prototyping and help, so that we can actually do something that can be merged and mantained.

OPY-bbt commented 5 years ago

Is there any progress on this?

TsuchiyaMasahiro commented 5 years ago

In Japanese, vertical writing starts at the top right and goes down, and when there is a line break, it moves one line to the left. I found someone's program as follows.

  var vertical_text = function(context, text, x, y) {
        var textList = text.split('\n');
        var lineHeight = context.measureText("あ").width;

       textList.forEach(function(elm, i) {
            Array.prototype.forEach.call(elm, function(ch, j) {
                context.fillText(ch, x-lineHeight*i, y+lineHeight*j);
            });
       });
  };

I hope that fabric.js will support vertical text, too.

asturur commented 4 years ago

i would very much support it.

How the cursor is supposed to work? is it vertical near the last character or is horizontal between chars?

Also do lineheight make sense, and any kind of alignment does?

ghost commented 4 years ago

我会非常支持。

游标应该如何工作?是在最后一个字符附近垂直还是在字符之间水平?

lineheight是否也有意义,并且任何对齐方式都可以吗?

It should be at the inter-character level at least in Chinese and Japanese. I think it's really time to add this feature because you can't just rely on /n And happy Christmas Eve!

asturur commented 4 years ago

So the character should be HORIZONTAL between chars. Do you have a screenshot of any vertical text writing app to see it in action?

How does MS word or google docs behave with vertical text?

What do people use for writing vertically that can we look at?

TsuchiyaMasahiro commented 4 years ago

MS word (Japanese) japanese2

TsuchiyaMasahiro commented 4 years ago

MS word textbox version japanese1

TsuchiyaMasahiro commented 4 years ago

japanese_position

asturur commented 4 years ago

Why some character are smaller? Is there some rule or because are used as grapheme clusters? I would not add specific language information that requires knowledge of that specific language.

TsuchiyaMasahiro commented 4 years ago

Some Japanese characters are smaller. For example, UnicodeU+3041, 3043, 3045, etc. are smaller than others. It is Japanese language rule.

OK, if you would support the cursor orientation and movement for vertical writing, maybe I can customize it by myself.

asturur commented 4 years ago

does actual standard canvas text make those character smaller? or is an ignored rule so far? I m talking by canvas in general not fabric in particular.

I would love to start this. I have some info now.

TsuchiyaMasahiro commented 4 years ago

Yes, those characters are made smaller in an actual standard canvas.

For example below, a character [ゆ] is smaller than others. https://jsfiddle.net/uemon/wpL3m4au/

In English, lowercase letters are smaller than uppercase letters. In the same way, some Japanese letters are smaller than other.

Those letters are set in an upper right position in vertical text, as I mentioned yesterday.

asturur commented 4 years ago

ok an in vertical alignment it goes top right. That make sense.

We could say that vertical text is like if we rotate a normal textbox clockwise of 90 degree but then we rotate each character counter clockwise 90 degree

TsuchiyaMasahiro commented 4 years ago

Some Japanese symbol characters (such as '[', '<','-') rotate clockwise of 90 degree, and it is not rotated counter clockwise 90 degree in vertical text.

When you rotate each character coutner clockwise 90 degree, could you add exception as below ?


fabric.Object.prototype.exceptionVerticalRoratin = ['[, ']', '<','>']; <-★each user can define it accroding to their own language.

if ( each character of vertical text is in the exceptionVerticalRoratin array ) { don't rotate counter clockwise 90 degree } else { rotate counter clockwise 90 degree }

asturur commented 4 years ago

so the brackets are laid down horizontally, following the text flow, and so taking less space? do they break the caracteristic block sizing of japanese writing? or do they get extra space?

TsuchiyaMasahiro commented 4 years ago

I think they take the same space and don't break the character block size of Japanese writing.

It may depend on the kind of the font. In a case of fixed-width font, those symbol characters take the same space. And in a case of proportional font, those symbol characters take less space.

compare

canhnht commented 4 years ago

Hi everyone. Thanks for raising the feature vertical writing. I would like to contribute to implement this feature. This is very popular in CJK text. Is there any progress on this feature?

canhnht commented 4 years ago

I'm exploring text.class.js in fabricjs's source code to see how to add vertical writing. I'm thinking of implementing inputting vertical text in textbox. In canvas's Web API, I don't find any API that supports typing text in a textbox. But fabricjs can support that. How can fabricjs support typing text in a textbox inside canvas?

asturur commented 4 years ago

Hi @canhnht fabricJS can type on a canvas because it use an hidden textarea which content then gets processed by the code and painted on the canvas.

It would be good to have vertical text and also vertical text knowledge, because the main factor is that i have no idea how vertical text work and what users would expect from it ( is it RTL or LTR, how do line break works and so on ).

You could start looking at how text rendering works and how you would reposition text each grapheme cluster one under the other. The real writing would still happen on a classic horizontal textarea, that is invisible.

canhnht commented 4 years ago

Oh, I got it. Thanks for your clarification. So I think the mechanism is quite similar to examples in this gist https://gist.github.com/aya-eiya/1721182 I will try implementing vertical writing based on that. Is the main code for text rendering and repositioning grapheme in the file itext.class.js? Would you mind to explain a little bit about working flow of rendering text? Could you point me to the code that renders cursor and text? Thanks in advance.

asturur commented 4 years ago

yes i can explain everything and support you in delivering the feature if you have the will and time to do so. You find most of the code in text.class.js, on the base of my experience my suggestion for possible steps are:

1) forgetting the interactive part and the cursor, just take a normal text object, write something on it, and work to have it measured and displayed vertically. ... once that work and is merged ( because we will test and merge code in small portions )

2) look at iText class, look at cursor rendering and position work ( how it gets moved with keyboard and so on, text selection for text ranges ) and adapt the cursor and selection to vertical text. ... once that work and is merged ....

3) we can look at Textbox and see if the flow adjustment make sense with vertical text too.

Does this make sense to you?

Small incremental code changes is the key to get things done, because fabricjs is large and is hard to understand and keep everything under control with large changes.

canhnht commented 4 years ago

Thanks a lot for your suggestion. 👍 Fabricjs is really huge. 😅 I will try to add incremental code changes and merge small code changes as soon as possible.

I have explored fabric's source code and understood more about components in Text, IText and Textbox classes. I made some changes in fabric's code and built a demo https://github.com/canhnht/FabricJS-Demo. In this demo, it can show that these things are feasible:

From my understanding, to implement vertical writing, I think I will need to change these things in fabric's code:

From your suggestion, I will start from step 1. It seems that this step is related to calculation of width and height of character, boundary box and many other things. Currently I'm also stuck in this part. Could you help to point out the code for calculating these things when a text object is initialized? Thanks in advance.

asturur commented 4 years ago

As soon as i am back to a computer i ll link relevant pieces of code.

Fabricjs does nor measure height, it assumes is in relation with fontSize, while this can be wrong for vertical text, i m fine starting keeping that assumption.

A minimal piece of work could totally be also just add a 'vertical' true/false property to text and just let the code that calculate text width/height return the correct size.

Following with a proper rendering PR.

For the special characters i would not do anything, who uses vertical text knows how to handle them i presume, and how to input them too.

i ll write asap.

asturur commented 4 years ago

You have to give a look at initDimensions and go deep to the calls up to:

https://github.com/fabricjs/fabric.js/blob/master/src/shapes/text.class.js#L712

_measureLine is the building block of measureLine that is of getLineWidth up to getTextWidth.

It run over the characters one by one and with getGraphemeBox https://github.com/fabricjs/fabric.js/blob/master/src/shapes/text.class.js#L744 will measure every char taking in account kerning.

Now getGraphemeBox could be as good as it is., with the difference that if the text is vertical oriente:

textHeight is the lenght of the longest line measured as if each char was typed one under the other textWidth is the sum of the largest char width for each column of text.

There are some concept like lineHeigh that would become the space between each column and also charSpacing that would still be the distance between each char inside the same column.

canhnht commented 4 years ago

Thanks a lot for explaining relevant code and all the concepts. <3 I also added a vertical true/false property to IText and added another version for each function that is affected by vertical writing. I think I should change vertical to Text, since it's the base class of text object and will be inherited by IText and Textbox.

For _measureLine and _getGraphemeBox, I will add another version for vertical writing, for example _measureLineInVerticalMode or _getGraphemeBoxInVerticalMode. Is that fine? About kerned width, it seems to be the space between characters in a text. In vertical writing, will its calculation need to be changed?

asturur commented 4 years ago

i think we do not have the tools for kernedwidth vertically. Unless canvas support it natively.

for now vertical text will have all the chars high the same, no kerning, and that's it.

For me is important to have vertical text ( like also right to left ) because is not about an extra feature but a feature that is culturally important. So the first draft won't be perfect but will be there, and see it working will make possible for more people to improve it.

canhnht commented 4 years ago

Hi @asturur, I'm quite confused about mechanism of positioning and resizing of boundary box in text object. In normal writing (i.e. horizontal text), when text is changed:

The resizing and positioning of boundary box in vertical writing is quite different from normal writing. I don't know where to change that mechanism. Could you help to point out part of the code for calculating boundary box? Thanks in advance.

asturur commented 4 years ago

You shouldn't be worrying of those things already :) Anyway that is not a fabricJS coded behaviour related to text. That is a side effect of the default object origin being top/left based.

If you set originX = 'center' and originY = 'center' for your object you will see that for normal text, the bounding box expands on both sizes , horizontally and vertically.

I know that vertical text are generally moving from the right to the left, but please ignore that and let's look into it when a single vertical text line works.

If you want to open a PR you should be better contributing small incremental changes toward the final goal or i won't be able to review and merge them.

canhnht commented 4 years ago

Thanks a lot for your explanation. I'm trying to understand how other functions and classes of text object interact with each other and the calculations of some properties in text class. I will try to get a vertical text line work first.

asturur commented 4 years ago

Ideally Text does everything, and IText handles interactivity. Still ideally Textbox will disappear and wrapping will be an option in the Text basic class, as well as rtl and vertical.

manhthepixta commented 3 years ago

Hi any update on this?

asturur commented 3 years ago

no. I have 5-7 small fixes i want to get out and then work on rtl text and then vertical text

saberjsd commented 3 years ago

Hi again! I have met an advanced demand too.(〃'▽'〃) emmmmm..........

saberjsd commented 3 years ago

like css writing-mode: vertical-rl;

manhthepixta commented 3 years ago

@asturur This gist might help. From a Japanese person. https://gist.github.com/aya-eiya/1721182 Japanese character displays very well.

zhuziqingdemao commented 3 years ago

no. I have 5-7 small fixes i want to get out and then work on rtl text and then vertical text

Hi @asturur ! Has the problem been solved? I need the ability to arrange multiple lines of text vertically 😭 So thanks

asturur commented 3 years ago

No @zhuziqingdemao no one worked on it yet.

thuytv-scuti commented 2 years ago

@asturur I've wrote a sample, it's a mess but it worked for me, hope it help. it will be great if you guys help me optimize it. https://gist.github.com/thuytv-scuti/2848ebd6214a670e6d87ddfce388448b