glideapps / canvas-hypertxt

The fastest way to layout wrapped text on a HTML5 canvas
MIT License
102 stars 10 forks source link

Advanced mode: Blocking Constraints #3

Open Awendel opened 2 years ago

Awendel commented 2 years ago

One example where this library doesn't work well: have a Paragraph with some inline spans that have different styling (e.g. bold, different fontSize etc.) currently the library assumes styling is uniform across paragraph.

The way to solve this is to offer an API function that work per Span and not per Paragraph, but being able to pass on constraints. Such as for a given "line" how much of the available space is blocked already (could encode it as vectors, e.g. if line width is 100, 0-20 blocked, 60-80 blocked etc).

This would also allow Text to "wrap around" objects (e.g. image inside text), which is currently not possible with HTML / CSS.

The result of this advanced function call could be used for Canvas or SVG text rendering.

Essentially for a given Array of Spans with different Style and array of line blocking vector (which one could manually precompute), one wants returned an array of "TextFragments", that each have their own bounding box, X and Y etc.

jassmith commented 2 years ago

Wouldn't having a "first line indent" px be enough? If you want to split styling you need only split your outputs and track your indents. The bigger issue here is the space between the spans. Only you know what it should be and there is no way in canvas to teach the kerning/hinting engine that you are mixing styles.

Awendel commented 2 years ago

true that could work, would just require more work for the developer. But yes with firstLineIndent, technically it should be possible to have multipleSpans with different styles render together.

perhaps we could start there and down the line offer a more advanced API that takes care of producing TextFragment arrays automatically including for content it should wrap around.

jassmith commented 2 years ago

The problem with a convenience API is its likely going to be a less efficient API. The most efficient way to deal with spans would be:

1) split text into spans 2) configure context for span 3) measure span 4) draw span 5) go to 2

since we don't do the drawing for the user and only they know what state they are changing between draw calls, we would have to be very obtuse about state changes and span setup.

Awendel commented 2 years ago

That is correct, except for that you don't have to draw the Span, you can also collect the result in bulk and draw later or do the calculations in a WebWorker for improved latency.

And you are also correct that it would be a less efficient API.

Currently the library emulates the text splitting behaviour of the inbuilt browser so that the logic can be brought into contexts with no native line splitting (canvas, svg).

You could consider if you wanted the library to be more than that: mainly that now that you control the rendering stack, one could offer features that HTML does not support, such as wrapping around / inside objects, which can be done in all professional publishing software (InDesign, ...) but not yet in the browser: Example Image:

maxresdefault

Which would only require to pass on "blocking" segments of a given line etc.

But I understand if its out of scope for this library, it is yours after all!