Textbox is a simple library to layout multiline text for display on SVG or Canvas. It can fairly decently line-break and render rich text given some boundaries. It understands simple text, and a subset of HTML and LaTeX syntaxes. The original purpose of this software is to aid labeling charts.
Take a look at Textbox example and demos if you are curious what this library can do.
- Line-break text to fit dimensions.
- Can overflow text into
...
if it doesn't fit designated area. - Understands common text features: bold, italic, links, etc...
- Knows that there is different whitespace like thin, or non-breaking.
- Tries to be smart about line-breaking before or after certain characters (it can occur after
-
but not before). - Supports hyphenation if text is prepared with soft-hyphens.
If you don't want to download and build Textbox yourself, the library is also provided as an NPM package:
$ npm install @borgar/textbox
# the Textbox class
For any use, you will need to start by defining a new Textbox instance. You may pass a configuration object as a parameter:
const box = new Textbox({
font: '12px/14px sans-serif',
width: Infinity,
height: Infinity,
align: 'left',
valign: 'top',
x: 0,
overflow: 'ellipsis',
parser: Textbox.defaultparser,
createElement: Textbox.createElement
});
Shown here are the defaults, but any or all of the above parameters can be provided. The Textbox instance will provide methods by the same name, along with line-breaking and rendering methods:
const box = new Textbox()
.font('12px/14px sans-serif')
.align('left')
.createElement(React.createElement);
# .font( css_font_shorthand )
Define what font to use. This will allow setting both font-size and line-height as well as font-family. Defaults to 12px/14px sans-serif
.
# .width( width_in_px )
Controls the horizontal dimension of the text. This can be a callback function if you want runaround text layout, or to flow the text into irregular space. Defaults to Infinity
(a single line).
A callback provided to this will be called every line with the line number as a parameter.
# .height( height_in_px )
Controls the vertical dimension of the text. Defaults to Infinity
. If the text ends up with more lines than fit into the height, the text is cut and postfixed with an overflow indicator (see overflow).
# .x( indent_in_px )
Sets per-line text horizontal indent. This is most useful for flowing text into irregular shapes. A callback provided to this will be called every line with the line number as a parameter.
# .align( alignment )
Sets text horizontal alignment. Accepts all values you would expect CSS text-align
to understand, as well as SVG text-anchor
equivalents: left, start, center, middle, right, end, and justify.
# .valign( alignment )
Sets text vertical alignment. Accepts all values you would expect CSS vertical-align
to understand: top, start, center, middle, bottom, and end.
# .overflow( indicator )
Sets text overflow mark, similar to CSS text-overflow
. The keyword ellipsis
will set the overflow mark to …
, otherwise the provided string is used as-is.
# .overflowLine( indicator )
Sets text per-line overflow mark, similar to .overflow
. This setting is off by default. The keyword ellipsis
will set the overflow mark to …
, otherwise the provided string is used as-is.
# .overflowWrap( indicator )
Sets whether textbox should line-break within a word to prevent text from overflowing. This setting is normal
by default which allows words to exceed the current line-width (unless line-overflow is set). Alternatively it may be set to break-word
which will force a break mid-words.
# .parser( parser )
Selects which text parser to use. The available parsers are:
Textbox.textparser
(the default, may be selected with"text"
)Textbox.htmlparser
(may be selected with"html"
)Textbox.latexparser
(may be selected with"latext"
)
Textbox will look for a parser on the instance first, then default to Textbox.defaultparser
. So you if you know that you will exclusively be using the HTML parser, you can change the default once:
Textbox.defaultparser = Textbox.htmlparser;
# .createElement( element_factory )
Set the element factory method for creating elements for SVG rendering. This has the same interface as React.createElement
so you may assign that if you are rendering a React application.
If nothing is provided Textbox will default to Textbox.createElement
so you can change the default once globally:
Textbox.createElement = React.createElement;
# .linebreak( text )
Parses text, flows it into the set dimensions and returns a list of the lines. The returned object can then be passed on to .render().
As well as a list of lines of tokens, the lines object has a height property which is useful if you want to set the render destination to the fit the text.
The lines object additionally has a render method so you can pass it on without having the originating Textbox instance.
// No height is set to the box
const box = new Textbox({ width: 150 });
// Text is turned into lines
const lines = box.linebreak( longTextPassage );
// Destination canvas is set to the height of the output
myCanvas.height = lines.height;
// Lines are rendered to the canvas
lines.render( myCanvas );
The lines render method is flexible when it comes to its arguments. It can accept a Canvas
, a CanvasRenderingContext2D
, or nothing in which case it will emit SVG. See .render().
The lines object also includes a .hasOverflow
property which indicates if the text was able to fit a designated height. It will be true if the text did not fit the defined space.
# .render( text )
# .render( text, Canvas )
# .render( text, CanvasRenderingContext2D )
# .render( text, d3-selection )
Render text or a "lines object" (see .linebreak()) to either SVG or Canvas.
The lines render method is flexible when it comes to its arguments and their order. If provided either a Canvas
, a CanvasRenderingContext2D
it will render to the canvas, otherwise it will emit SVG built with the .createElement() interface.
# Textbox.measureText( text, font[, options ])
A utility function to measure the width of a string. Returns the width of the text in a number of pixels. This is the same utility that Textbox uses internally. The font argument is expected to be a valid CSS font shorthand value.
Options is an object of options that work as follows:
{ trim: true }
: Trim the text before measuring. True by default.{ collapse: true }
: Collapse whitespaces to single spaces (same as HTML does). True by default.
Textbox.measureText("Lorem Ipsum", "bold 15px/20px sans-serif", {
trim: false,
collapse: false,
});
-
In SVG links do not automatically get color or pointer cursor. You will need to add your own styles to these.
svg text a { fill: blue; text-decoration: underline; cursor: pointer; }
-
Multi-word links in SVG text are not necessarily a single entity/element like they are in HTML. If the text in a link is line broken, then hovering one segment will not cause segment in the next line over to trigger hover styles.
-
Text justification is fairly broken in SVG. Avoid it for formatted text.
-
Justification does not work consistently in all browsers as they have buggy support for the
word-spacing
property. Google Chrome seems to work fine, Safari works for plain text but incorrectly for formatted text. Firefox is lost in the woods. -
Because of the way text handling is done in SVG, justifying underlined text will create gaps in the underlined text.
-
-
Subscript and superscript are bugged in Firefox because they have never implemented the
baseline-shift
property. The bug report is 15 years old when this is written so not likely to be solved soon.