-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add rich text and color support #1
Merged
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
__WARNING:__ Still a WIP. See `// DEBUG` comments throughout. Instead of dealing with text as a string, introduce the concept of Words where a Word is either a visible, written word, or whitespace, and have the option to infer whitespace when an array of Words is given instead of a string. A Word then has its own formatting options aside from the "base" formatting set in the `CanvasTextConfig` object. If a Word doesn't have `format` options, it will simply use the "default" format in the 2D context, which may have been modified based on the `CanvasTextConfig` object provided to `drawText()`. To preserve the existing `drawText()` and `splitText()` APIs, `splitText()` splits the text into an array of Words and then calls the new `splitWords()` function that does all the work. For now, providing an array of Words to `drawText()` throws an error since this isn't supported yet. But everything else workds as before when providing a string: All supported alignments, including justification. Other fixes/changes: - Breaks cannot occur within a word anymore, even when the box width gets too narrow to fit anything. At this point, a minimum of one word is rendered per line, on top of any hard line breaks specified with newline characters. - The context's state is saved and restored in `drawText()`, meaning that after the call, the context's font, etc, styles/settings are back to what they were prior to calling it instead of being set to what was specified in the `CanvasTextConfig` object.
__WARNING:__ See remaining `// DEBUG` comments. In order to make it possible to use a library other than canvas-txt to actually draw the text, yet still leverage this library's text wrapping and rich text features, `splitWords()` now does all the positioning that `drawText()` use to do, which means that `splitText()`, which calls `splitWords()` internally, must now be given many more parameters than it used to. Since `splitText()` was already exported as a utility from this library, this must be a breaking change (major version update). So now, `splitWords()` returns the positioned words along with the required `textAlign` and `textBaseline` to set on the 2D context in order to render the text what is now a very simple loop. This makes it possible to call `splitWords()` directly and then use the output to render each word using another library such as Konva where each word could then be dragged or hit-tested, or do it without a library altogether. Plain text (single text format) works at this point, but rich text still does not render properly due to misalignment issues.
__WARNING:__ See `// DEBUG` comments for what still needs doing/fixing. Works quite well now, especially when NOT using different font sizes for different words. If all words use the same size, just alter their style, weight, or variant, it renders nicely. Updated the demo app to always set "ipsum" to italic, and "consectetur" to bold, if either is found in the sample text to render.
__WARNING:__ There are still remaining `// DEBUG` items to address - Performance: Allow consumer to provide pre-computed `TextMetrics` objects in the `Words[]`, which would typically come from a previous call to `splitWords()` since the function now sets a new `Word.metrics` property to the measured metrics for the `Word.format` (if any). This will dramatically speed up the process when most Words have already been measured from a previous pass (assuming formatting hasn't changed). - Performance: Change `WordMap` key to a string hash of the Word's `text` and `format` in order to reuse associated metrics between identical Words. Also impacts memory use. - Memory: Re-use metrics between identical Words instead of re-measuring them unnecessarily. This is particularly significant with whitespace characters which show up in __huge__ numbers in any given set of Words and are nearly all identical.
Native Canvas `TextMetrics` objects fail to serialize (cause an exception) when given as data to the `postMessage()` of a Web Worker thread (or from the main thread). Therefore, work with objects having a similar interface but not being actual `TextMetrics` instances. Also provide two JSON stringify helpers: `specToJson()` and `wordsToJson()` This should help consumers to deal with native `TextMetrics` objects.
…ype, remove self as contributor from package
Node-based `canvas` rendering does not currently support the newer `fontBoundingBox*` TextMetrics properties, so use a `textBaseline='bottom'` fallback trick in that case.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
For posterity, this was original proposed as geongeorge/Canvas-Txt#95 but was too big of a change to take in. So it will now be published as a new package.
I tried my very best to keep the existing interface to
splitText()
but couldn't, so this is a breaking change from the originalcanvas-txt
library's API, though not a huge one. I also changed the output file names since the.mjs
was causing issues with our Webpack build and.js
was the path of least resistance.Adds rich text and color support by introducing the concept of "Words". Giving a simple string to
drawText()
will now, internally, convert it to aWord[]
array. EachWord
can have aformat
and can also prime thesplitWords()
calculations with previously-calculatedmetrics
to improve performance (meaningcontext.measureText()
will be skipped for thatWord
).Moves the bulk of the code out of
drawText()
and intosplitWords()
(which is now called by the existingsplitText()
) so thatsplitWords()
returns a "render spec" with aPositionedWord[]
array and alldrawText()
needs to do now is loop over the words and draw them on the canvas at the pre-calculated positions.As an easy bonus, while color has no effect on word splitting,
drawText()
now supports it. EachWord
can have its own color.I enhanced the demo to always render two words in a different color and font style.
The only thing that remains a "TODO" is handling different font sizes. It will render, but not nicely, because the code still assumes the text baseline is always the same while it probably needs to be different, and the words need to be aligned to it in some way. But sticking with a single font size and a single font family, the output is nice with different colors, bolded/italic text, etc.
Text still wraps and aligns as before.
Web demo
Node demo output