Code Monkey home page Code Monkey logo

defold-richtext's Introduction

Defold-RichText

Defold-RichText is a system to create styled text based on an HTML inspired markup language.

Installation

You can use RichText in your own project by adding this project as a Defold library dependency. Open your game.project file and in the dependencies field under project add:

https://github.com/britzl/defold-richtext/archive/master.zip

Or point to the ZIP file of a specific release.

Markup format

The markup format is HTML inspired, but not intended to be fully compatible with standard HTML. Just like in HTML the idea is that sections of text can be enclosed in matching start and end tags:

This is a <b>bold</b> statement!

This is a bold statement!

Nested elements

Nested elements are supported. Use this to give a section of text a combination of styles:

This is a <b>bold <i>italic</i></b> statement!

This is a bold italic statement!

Supported tags

The following tags are supported:

Tag Description Example
a Create a "hyperlink" that generates a message <a=message_id>Foobar</a>
when clicked (see richtext.on_click)
b The text should be bold <b>Foobar</b>
br Insert a line break (see notes on linebreak) <br/>
color Change text color <color=red>Foobar</color>
<color=1.0,0,0,1.0>Foobar</color>
<color=#ff0000>Foobar</color>
<color=#ff0000ff>Foobar</color>
shadow Change text shadow <shadow=red>Foobar</shadow>
<shadow=1.0,0,0,1.0>Foobar</shadow>
<shadow=#ff0000>Foobar</shadow>
<shadow=#ff0000ff>Foobar</shadow>
outline Change text shadow <outline=red>Foobar</outline>
<outline=1.0,0,0,1.0>Foobar</outline>
<outline=#ff0000>Foobar</outline>
<outline=#ff0000ff>Foobar</outline>
font Change font <font=MyCoolFont>Foobar</font>
i The text should be italic <i>Foobar</i>
img Display image <img=texture:image/>
Display image in fixed square <img=texture:image,size/>
Display image in fixed rectangle <img=texture:image,width,height/>
nobr Prevent the text from breaking Words <nobr>inside tag</nobr> won't break
size Change text size, relative to default size <size=2>Twice as large</size>
spine Display spine model <spine=scene:anim/>
p Adds extra spacing below the line where this <p>A paragraph</p>\nSome other text
tag ends. Adds a newline before its opening <p=2.5>This has 2.5 lines of spacing</p>
tag if it doesn't already exist.
repeat Repeat the text enclosed by the tag <repeat=5>Echo </repeat> five times

Line breaks

Note that there is no need for the HTML <br/> tag since line breaks (i.e. \n) are parsed and presented by the system. Note that a single <br> (ie without a closing or empty tag) isn't supported (even though most browsers accept it).

Named colors

The following named colors are supported:

Name Hex value Swatch
aqua #00ffffff
black #000000ff
blue #0000ffff
brown #a52a2aff
cyan #00ffffff
darkblue #0000a0ff
fuchsia #ff00ffff
green #008000ff
grey #808080ff
lightblue #add8e6ff
lime #00ff00ff
magenta #ff00ffff
maroon #800000ff
navy #000080ff
olive #808000ff
orange #ffa500ff
purple #800080ff
red #ff0000ff
silver #c0c0c0ff
teal #008080ff
white #ffffffff
yellow #ffff00ff

You can add your own named colors as well:

local color = require "richtext.color"

color.add("blood", "#8A0303")
color.add("asparagus", "#87a96b")
color.add("denim", "#1560bd")

HTML entities

The RichText library has limited support for HTML entities (reserved characters):

  • &gt; converts into >
  • &lt; converts into <
  • &nbsp; converts into a non-breaking space between two words, ie 100&nbsp;km becomes 100 km with 100 and km acting as a single word with a space between.
  • &zwsp; converts into a zero-width space character (\226\128\139). This is not an official HTML entity but it is provided as a convenient way to insert one into text.

Usage

The RichText library will create gui text nodes representing the markup in the text passed to the library. It will search for tags and split the entire text into words, where each word contains additional meta-data that is used to create and configure text nodes. This means that the library will create as many text nodes as there are words in the text.

Basic example

A simple example with some color and linebreaks:

richtext.create("Single line text with a dash of <color=red>color</color>\nBy default left aligned.", "Roboto-Regular")

Advanced example

A more complex example with different fonts, colors, inline images and automatic linebreaks:

local settings = {
	fonts = {
		Roboto = {
			regular = hash("Roboto-Regular"),
			italic = hash("Roboto-Italic"),
			bold = hash("Roboto-Bold"),
			bold_italic = hash("Roboto-BoldItalic"),
		},
		Nanum = {
			regular = hash("Nanum-Regular"),
		},
	},
	width = 400,
	parent = gui.get_node("bg"),
	color = vmath.vector4(0.95, 0.95, 1.0, 1.0),
	shadow = vmath.vector4(0.0, 0.0, 0.0, 1.0),
}

local text = "<size=3><outline=green>RichText</outline></size>Lorem <color=0,0.5,0,1>ipsum </color><img=smileys:zombie/> dolor <color=red>sit </color><color=#ff00ffff>amet, </color><size=1.15><font=Nanum>consectetur </font></size>adipiscing elit. <b>Nunc </b>tincidunt <b><i>mattis</i> libero</b> <i>non viverra</i>.\n\nNullam ornare <img=smileys:hungry/>accumsan rhoncus.\n\nNunc placerat nibh a purus auctor, id scelerisque massa <size=2>rutrum.</size>"

richtext.create(text, "Roboto", settings)

Custom tags

Custom tags can be accessed and used in two ways:

  • Use richtext.tagged(words, tag) to get all words with a certain tag, even a custom one
  • Use tags.register(tag, fn) to register a custom tag handler:
	tags.register("boldred", function(params, settings)
		tags.apply("color", "red", settings)
		tags.apply("b", nil, settings)
	end)
	richtext.create("I am <boldred>bold and red</boldred>!", "Roboto", settings)

API

richtext.create(text, font, settings)

Creates rich text gui nodes from a text containing markup.

PARAMETERS

  • text (string) - The text to create rich text from
  • font (string) - Name of default font. Must match the name of a font in the gui scene or a key in the fonts table in settings (see below).
  • settings (table) - Optional table containing settings

The settings table can contain the following values:

  • width (number) - Maximum width of a line of text. Omit this value to present the entire text on a single line
  • position (vector3) - The position that the text aligns to (refer to the align setting for details). Defaults to 0,0 if not specified.
  • size (number) - Default size of any created node. Same as using a <size=2> tag in the text. Defaults to 1 if not specified.
  • parent (node) - GUI nodes will be attached to this node if specified.
  • fonts (table) - Table with fonts, keyed on font name. See separate section below. If no fonts table is provided the font used will be the one passed to richtext.create().
  • layers (table) - Table with font, texture and spine scene mappings to layer names. See separate section below.
  • color (vector4) - The default color of text. Will be white if not specified.
  • shadow (vector4) - The default shadow color of text. Will be transparent if not specified.
  • outline (vector4) - The default outline color of text. Will be transparent if not specified.
  • align (hash) - One of richtext.ALIGN_LEFT, richtext.ALIGN_CENTER, richtext.ALIGN_RIGHT and richtext.ALIGN_JUSTIFY. Defaults to richtext.ALIGN_LEFT. Defines how the words of a line of text are positioned in relation the provided position. Width must be specified for richtext.ALIGN_JUSTIFY.
  • valign (hash) - One of richtext.VALIGN_TOP, richtext.VALIGN_MIDDLE and richtext.VALIGN_BOTTOM. Defaults to richtext.VALIGN_TOP. Defines how the words of a line of text are positioned vertically on the line.
  • line_spacing (number) - Value to multiply line height with. Set to a value lower than 1.0 to reduce space between lines and a value higher than 1.0 to increase space between lines. Defaults to 1.0.
  • paragraph_spacing (number) - Space to leave after lines with where <p> tags end. Relative to the line height. Defaults to 0.5 lines.
  • image_pixel_grid_snap (boolean) - Set to true to position image on full pixels (positions rounded to nearest integer) to avoid effects of anti-aliasing. Defaults to false.
  • combine_words (boolean) - Set to true to combine words with the same style on a line into a single node. This is useful for very long texts where the maximum number of nodes would exceed the limit set in the GUI. The disadvantage is that any per word operations will not work since the words have been combined.
  • dryrun (boolean) - Set to true to perform a complete layout of the words without creating gui nodes.

The fonts table should have the following format:

	name (string) = {
		regular (string) = font (hash),
		italic (string) = font (hash),
		bold (string) = font (hash),
		bold_italic (string) = font (hash),
	},
	name (string) = {
		...
	},

Where name is the name specified in a <font> tag and the font for each of regular, italic, bold and bold_italic should correspond to the name of a font added to a .gui scene.

The layers table should map fonts, textures and spine scenes to layer names. It should have the following format:

	fonts = {
		font (hash) = layer (hash),
		...
		font (hash) = layer (hash),
	},
	images = {
		texture (hash) = layer (hash),
		...
		texture (hash) = layer (hash),
	},
	spinescenes = {
		spinescene (hash) = layer (hash),
		...
		spinescene (hash) = layer (hash),
	}

Where layer is the name of a layer in the .gui scene, font is the value returned from a call to gui.get_font(node), texture is the value returned from a call to gui.get_texture(node) and finally spinescene is the value returned from a call to gui.get_spine_scene(node).

RETURNS

  • words (table) - A table with all the words that the text has been broken up into. Each word is represented by a table with keys such as node, tags, text etc
  • metrics (table) - A table with text metrics.

The metrics table contains the following values:

  • width (number) - Width of the text
  • height (number) - Height of the text
  • char_count (number) - Number of characters in the text including whitespace
  • img_count (number) - Number of images in the text
  • spine_count (number) - Number of spine models in the text

A word in the words table contains the following values:

  • size (number) - Size of the word
  • color (vector4) - Color of the word
  • shadow (vector4) - Shadow color of the word
  • outline (vector4) - Outline color of the word
  • node (node) - The GUI node representing the word
  • metrics (table) - Word metrics (width, height, total_width and additionally for text nodes: max_descent, max_ascent)
  • font (string) - Font name
  • text (string) - Word text (empty for non text nodes)

richtext.tagged(words, tag)

Get all words with a specific tag.

PARAMETERS

  • words (table) - The words to search, as received by a call to richtext.create().
  • tag (string) - Name of the tag to search for. Pass nil to get all words without tags.

RETURNS

  • words (table) - A table with all the words that matches the tag.

richtext.truncate(words, length, [options])

Truncate a text down to a specific length. This function has two modes of operation: 1) It can truncate the text on a per word basis or 2) on a per character/image basis. The function will disable nodes that shouldn't be visible and in the case of truncating on a per character basis also update the text in nodes that should be partially visible. The text metrics of a truncated word will be updated.

PARAMETERS

  • words (table) - The words to truncate, as received by a call to richtext.create().
  • length (number) - Maximum number of words or characters and images to show.
  • options (table) - Optional table with options when truncating.

Available options in the option table are:

  • words (boolean) - True if the function should truncate down to the nearest full word instead of truncating to a partial word.

RETURNS

  • word (table) - The last visible word.

richtext.length(text)

Get the length of a text ignoring any tags except image and spine tags which are treated as having a length of 1.

PARAMETERS

  • text (string|table) - The text to measure. This can either be a string or a list of words, as received by a call to richtext.create().

RETURNS

  • length (number) - The length of the provided text.

richtext.characters(word)

Split a word into it's characters, including the creation of the gui nodes. Each of the characters will be given the same attributes as the word, and they will be positioned correctly within the word.

PARAMETERS

  • word (table) - The word to split, as received from a call to richtext.create() or richtext.tagged().

RETURNS

  • characters (table) - The individual characters of the word.

richtext.on_click(words, action)

Call this function when a click/touch has been detected and your text contains words with an a tag. These words act as "hyperlinks" and will generate a message when clicked. The generated message will contain the following values:

  • node_id (hash) - Id of the node which was clicked
  • text (string) - Text of the clicked word
  • screen_x (number) - Horizontal position of the click
  • screen_y (number) - Vertical position of the click
  • tags (table) - All tags on the clicked word

PARAMETERS

  • words (table) - A list of words, as received from richtext.create() or richtext.tagged().
  • action (table) - The action table from the on_input lifecycle function.

RETURNS

  • consumed (boolean) - True if any word was clicked.

richtext.remove(words)

Removes all gui text nodes created by richtext.create().

PARAMETERS

  • words (table) - Table of words, as received from a call to richtext.create().

richtext.plaintext(words)

Returns the words created by richtext.create() as a plain text string without any formatting or tags. Linebreaks are included in the returned string.

PARAMETERS

  • words (table) - Table of words, as received from a call to richtext.create().

RETURNS

  • plaintext (string) - Plain text version of the words.

richtext.ALIGN_LEFT

Left-align text. The words of a line starts at the specified position (see richtext.create settings above).

richtext.ALIGN_CENTER

Center text. The words of a line are centered on the specified position (see richtext.create settings above).

richtext.ALIGN_RIGHT

Right-align text. The words of a line ends at the specified position (see richtext.create settings above).

richtext.ALIGN_JUSTIFY

Justify text. The words of a line start at the specified position and are spaced such that the last character of the last word ends at the right edge of the line bounds (see richtext.create settings above).

richtext.VALIGN_TOP

Vertically align the words on a line so that the top of the words on the line align.

richtext.VALIGN_MIDDLE

Vertically align the words on a line so that the middle of the words on the line align.

richtext.VALIGN_BOTTOM

Vertically align the words on a line so that the bottom of the words on the line align.

Credits

defold-richtext's People

Contributors

8bitskull avatar abadonna avatar britzl avatar dapetcu21 avatar dblumin45 avatar rawktron avatar subsoap avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

defold-richtext's Issues

Defold-richtext does not set text node outline color

Defold-richtext does not set text node outline color. For runtime created nodes default outline color is black (for nodes created in editor this color is white). Because of this, text created by defold-richtext looks in some cases thinner then text in other text nodes (at least for bitmap fonts).

Truncate to x many chars while ignoring tags

So <size=1>Lara</size><br/><size=0.7>Well, hello there!</size> could be truncated to

<size=1>Lara</size><br/><size=0.7>Well, h</size> for whatever function helper name could be.

This is so RPG style per char text showing can be easily done.

Custom tags or classes

Add the ability to apply custom tags and then provide a function to return all nodes with a specific tag/class

Line padding setting tag?

Allow adjustment to padding between words? So negative y margin/padding could move lines closer together with less of a gap.

Anchor tag not correct work, if font is big size

Hello.
If font is a big size, then message "click text" work, when I click in up left corner. Only up left corner of word, but not whole word. It reproduce on font Roboto-Regular with 72 pt in example project.

How to reuse already created nodes?

for expample

rnode = richtext.create('<color=red>my text</color>')
.... somewhere deep in code ....
rnode.text += '<color=green>new text</color>'

wait tag

I added this tag to a custom version of RichText for my needs in a visual novel style project. It would probably be useful for others but I'm not sure if it should be a part of the main project. These are the main changes.

	elseif tag == "wait" then
		settings.wait = tonumber(params)
		if is_empty then
			-- empty tag, ie tag without content
			-- example <br/> and <img=texture:image/>
			local tag_settings = parse_tag(name, params)
			merge_tags(word_settings, tag_settings)
			local add_this_word = ""
			if tag_settings.wait ~= nil and tag_settings.wait > 0 then
				local wait_word = "\226\128\139"
				for i=1, tag_settings.wait do
					add_this_word = add_this_word .. wait_word
				end
			end
			add_word(add_this_word, word_settings, all_words)
		elseif not is_endtag then

Then the tag is used like <wait=30> which inserts 30 zero width characters. This is useful in visual novel projects where you have text appearing once character at a time and you want to add small delays between actions.

Bold and linebreak compatibility

A linebreak seem to disappear when using the b tag.

Passing in this to richtext.create() gives correct results (there is one empty line between the lines):
Line 1\n\nLine 2

but this generates two lines without the empty line in between:
<b>Line 1</b>\n\nLine 2

Adding extra waiting time for create_truncate_example()

It would be useful if it were somehow possible to add tags like <wait=1/> to be able to skip timer.delay repeats based on those kinds of tags. Maybe being able to check the next word/tag see if it's a wait tag and live edit it so it's at or below 0?

This would be useful to add a little time between line breaks / add suspense in text.

I need a way to prevent a line break with whatever's in front of the current token.

I have the following scenario:

I have a text bubble with some arbitrary text, which needs to have a small caret image prepended. Something like this:

Screenshot 2019-03-29 at 18 27 05

The caret must never be orphaned alone on a line. I tried adding <nobr> before the last word, but I'm left with situations like this, which are invalid:

Jennifer has <i>logged <nobr>in</i> <img=interludes:advance/></nobr>

Combine words causes wrong metrics

The spacing between words that rich-text applies is not always consistent with the one that Defold applies. This causes lines of text to be more compact than the reported metrics and stuff like this happens, where a lot of extra space is being unused and the line is not well center-aligned:

Screenshot 2019-08-23 at 15 27 42

I think the correct approach for combine_words would be to attempt to combine the words during layout (preferably between getting word metrics and actually creating the node) and measure the whole combined string, not just words individually.

I can try to write a PR for that, as I would really want to have more accurate word spacing in Interrogation, both for improving readability and improving text density.

Multiline strings break, linebreaks of multiline strings should be ignored?

	local text = [[
	<size=1>Lara</size><br/>
	<size=0.7>Well, hello there!</size><br/>
	<size=0.7>What do we have here?</size><img=icons:waiting/>]]

Becomes

2018-03-01 21_56_28-vn

It should be

2018-03-01 21_57_28-vn

Which requires

local text = [[<size=1>Lara</size><br/><size=0.7>Well, hello there!</size><br/><size=0.7>What do we have here?</size><img=icons:waiting/>]]

It would be nice to be able to format in scripts with multiline strings so it's a little easier to manage.

Vertical alignment setting

A vertical alignment setting so that components of different heights can be arranged by top/bottom/middle.

Current behaviour looks like this:
image

Desired behaviour would be a setting that could achieve all of these setups:
image

How to deal with exceeding the node limit?

I don't think that RichText should to handle this case explicitly, I'm just wondering if you have any ideas how to solve this for my project.

I'm dealing with quite a bit of text, generally filling the screen. I'm using the combine_words option, and most of the time, most of the text is plain, so there is no issue at all. But sometimes I will have text that definitely exceeds the 1024 max node limit per gui. I'm making a MUD client (an interface for online text-adventure games), so I'm not in control of the text that I get, only what I do with it. I know I need to split things between multiple GUI components, I'm just not sure what the best way to split it up is.

Some ways I've thought about:

  1. Always split my text into a preset number of large chunks (3-5?).
    • Simple, but not guaranteed to succeed (i.e would crash) in extreme cases.
  2. Always split the text into 1024-character pieces.
    • Still simple, should always work, but seems very inefficient for not being needed 90% of the time.
  3. Count tags and split it up dynamically?
    • Should maybe always work, but not so simple.
    • I don't think I could get an accurate node count without fully parsing the text an extra time, which seems dumb.
  4. Fork & modify RichText to split the "pipeline" into multiple function calls.
    • Should always work, sounds "clean", but requires probably non-trivial changes to RichText.
    • Something like:
      1. Parse.
      2. Combine words. (+ calculate line breaks, etc.?)
      3. Divide word list if there are more than 1024 nodes.
      4. Create nodes in however many GUIs are needed.

Rescale images inline

Maybe add a <scale> tag? Or use the <size> tag? (this would be a breaking change)

Justified text

Not really an issue, but a feature request. While it´s cool to have text left/center/right aligned, I miss being able to justify it or have a function to manage space between words.

I know, a simple autojustify may create the "space river" effect in the text but... Is there any chance the feature will be included in this cool addon of defold?

Double newlines in conjunction with <size> create extra newline

Here's a minimal reproductible example:

local text = "<size=0.5>The brown fox jumps over the lazy dog. The brown fox jumps over the lazy dog.\n\nThe brown fox jumps over the lazy dog. The brown fox jumps over the lazy dog.</size>"
local words, metrics = richtext.create(text, "Roboto", settings)

And here's how it looks (notice the extra newline in the middle of the second paragraph):
Screenshot 2019-04-12 at 12 52 32

Add support for overlapping/out of order tags.

Allow RichText to handle text like this:

<color=red><b>some text</color></b>

...where the tags are not closed in the same order that they are opened.

Or they could actually overlap, like this:
"<i>this is italic <b>bold italic : </i>bold regular</b> back to normal."

this is italic bold italic : bold regular back to normal.

I guess the parser would have to store the current styling state with the order of opened tags, so it could handle any tag that came next, rather than only searching for consecutive open-close pairs.


Feel free to close as "won't add".

Per character effects

A way to break words within special tags into individually characters and then be able to iterate over them to produce different effects.

Maybe something like
Taste the <char class="rainbow">RAINBOW!</char> or <char class="doom">don't</char>

Would return two char numeric tables with a node for each character within the char tag allowing the char tables to be stored and their nodes iterated over to produce different effects.

Sprites being scaled when they shouldn't be?

What's happening here? There are two icons. One is displayed like how it should be, the second looks incorrect. I tried to reset the size but it did not seem to help.

local text = [[<img=icons:document/><size=1>Kalipi</size><br/><size=0.7>Well, hello there! He he he... ho ho ho...</size><br/><size=0.7>What do we have here?</size><size=1.0></size><img=icons:document/>]]		

2018-03-06 22_48_37-vn

Adding some size 1 text doesn't help either

2018-03-06 22_51_31-vn

Putting second on its own line also doesn't help

2018-03-06 22_52_36-vn

Weird, hard to repro bug

I don't know if RichText is at fault, but we've been having this bug happen once or twice a day on one of our expo machines at Gamescom. RichText is the main shared codepath between all the occurence points of this bug, so I said I'd share.

We haven't had it ever happen on any other PC.

The machine is a MacBookPro10,1, 8GB RAM, Mojave 10.14.5, NVIDIA GT650M, connected to an external display.

Nothing in the console.

IMG_20190822_144805282
IMG_20190822_144910659
IMG_20190822_144751540

Make it easy to inject color lists

I want to be able to insert a list of colors by name and then be able to easily use them in text. Color names like "abyssal_green" or "lotus_red" with associated color codes.

What I am doing currently is

local richtext_color = require "richtext.color"

local function richtext_insert_colors()
	richtext_color.COLORS["abyssal_green"] = richtext_color.parse_hex("#05f0bc") -- #14ffb9
	richtext_color.COLORS["lotus_red"] = richtext_color.parse_hex("#ff0946")
end

Maybe make it a richtext main function to add a color to the list by name.

richtext.add_color("name", "code") 

Where code is auto detected to be RGB/RGBA/hex/hexa, and if the color already existed it would silently replace it.

The new 5.8.0 tag parser generates empty text words that can break layout

Stuff like this happens:

Screenshot 2019-09-08 at 13 44 22

Printing out the generated words table, I notice there's some empty text nodes, including one at the very end, which creates the extra newline (see words[7] and words[8] in the output below):

The text and options fed to richtext.create():

DEBUG:SCRIPT: "We'll be bringing in a subject. They're a trained actor, but should be fully in character for the duration. I'm to be considered invisible and inaudible by the actor, but I'll be guiding you along.<nobr> <img=t:advance/></nobr>",
dialogue,
{ --[[0x136f546e0]]
  combine_words = true,
  align = hash: [ALIGN_CENTER],
  fonts = { --[[0x12070b0e0]]
    timer = { --[[0x12070bd80]]
      regular = hash: [timer]
    },
    title = { --[[0x12070bbd0]]
      bold_italic = hash: [title_bolditalic],
      regular = hash: [title],
      italic = hash: [title_italic],
      bold = hash: [title_bold]
    },
    dialogue_bold = { --[[0x12070b6e0]]
      bold_italic = hash: [dialogue_italic],
      regular = hash: [dialogue_bold],
      italic = hash: [dialogue_bolditalic],
      bold = hash: [dialogue]
    },
    dialogue = { --[[0x12070b130]]
      bold_italic = hash: [dialogue_bolditalic],
      regular = hash: [dialogue],
      italic = hash: [dialogue_italic],
      bold = hash: [dialogue_bold]
    },
    document_serif = { --[[0x12070b670]]
      bold_italic = hash: [document_serif_bolditalic],
      regular = hash: [document_serif],
      italic = hash: [document_serif_italic],
      bold = hash: [document_serif_bold]
    },
    document = { --[[0x12070b7a0]]
      regular = hash: [document],
      italic = hash: [document_italic],
      bold = hash: [document_bold]
    },
    dialogue_small = { --[[0x12070b370]]
      bold_italic = hash: [dialogue_bolditalic_small],
      regular = hash: [dialogue_small],
      italic = hash: [dialogue_italic_small],
      bold = hash: [dialogue_bold_small]
    },
    dialogue_italic = { --[[0x12070b5b0]]
      bold_italic = hash: [dialogue_bold],
      regular = hash: [dialogue_italic],
      italic = hash: [dialogue],
      bold = hash: [dialogue_bolditalic]
    }
  },
  layers = { --[[0x1207049c0]]
    fonts = { --[[0x12066cd40]]
      hash: [timer] = hash: [tutor_timer],
      hash: [title_bolditalic] = hash: [tutor_title_bolditalic],
      hash: [document_serif_italic] = hash: [tutor_document_serif_italic],
      hash: [title_bold] = hash: [tutor_title_bold],
      hash: [title_italic] = hash: [tutor_title_italic],
      hash: [dialogue_bold_small] = hash: [tutor_dialogue_bold_small],
      hash: [dialogue_bolditalic_small] = hash: [tutor_dialogue_bolditalic_small],
      hash: [dialogue_italic_small] = hash: [tutor_dialogue_italic_small],
      hash: [dialogue_small] = hash: [tutor_dialogue_small],
      hash: [dialogue_italic] = hash: [tutor_dialogue_italic],
      hash: [dialogue] = hash: [tutor_dialogue],
      hash: [dialogue_bolditalic] = hash: [tutor_dialogue_bolditalic],
      hash: [dialogue_bold] = hash: [tutor_dialogue_bold],
      hash: [document] = hash: [tutor_document],
      hash: [document_italic] = hash: [tutor_document_italic],
      hash: [document_bold] = hash: [tutor_document_bold],
      hash: [document_serif] = hash: [tutor_document_serif],
      hash: [document_serif_bold] = hash: [tutor_document_serif_bold],
      hash: [title] = hash: [tutor_title],
      hash: [document_serif_bolditalic] = hash: [tutor_document_serif_bolditalic]
    },
    spinescenes = { } --[[0x136d608f0]],
    images = { } --[[0x136d608a0]]
  },
  parent = @(398.80001831055, 137.89999389648, 0),
  width = 1255
}

And the words table:

DEBUG:SCRIPT: 
{ --[[0x136f54830]]
  1 = { --[[0x136f51100]]
    outline = vmath.vector4(0, 0, 0, 0),
    size = 1,
    color = vmath.vector4(1, 1, 1, 1),
    metrics = { --[[0x136e547b0]]
      width = 1156,
      max_descent = 13,
      max_ascent = 48,
      height = 61,
      total_width = 1167
    },
    text = "We'll be bringing in a subject. They're a trained ",
    tags = { } --[[0x136f555f0]],
    node = We'll be bringing in a subject. They're a trained @(-583.5, 0, 0),
    font = "dialogue",
    shadow = vmath.vector4(0, 0, 0, 0)
  },
  2 = { --[[0x136f38750]]
    outline = vmath.vector4(0, 0, 0, 0),
    size = 1,
    color = vmath.vector4(1, 1, 1, 1),
    metrics = { --[[0x136de9360]]
      width = 1093,
      max_descent = 13,
      max_ascent = 48,
      height = 61,
      total_width = 1104
    },
    text = "actor, but should be fully in character for the ",
    tags = { ... } --[[0x136f555f0]],
    node = actor, but should be fully in character for the @(-552, -61, 0),
    font = "dialogue",
    shadow = vmath.vector4(0, 0, 0, 0)
  },
  3 = { --[[0x136f39d60]]
    outline = vmath.vector4(0, 0, 0, 0),
    size = 1,
    color = vmath.vector4(1, 1, 1, 1),
    metrics = { --[[0x136da5680]]
      width = 1057,
      max_descent = 13,
      max_ascent = 48,
      height = 61,
      total_width = 1068
    },
    text = "duration. I'm to be considered invisible and ",
    tags = { ... } --[[0x136f555f0]],
    node = duration. I'm to be considered invisible and @(-534, -122, 0),
    font = "dialogue",
    shadow = vmath.vector4(0, 0, 0, 0)
  },
  4 = { --[[0x136f016b0]]
    outline = vmath.vector4(0, 0, 0, 0),
    size = 1,
    color = vmath.vector4(1, 1, 1, 1),
    metrics = { --[[0x136d28390]]
      width = 1248,
      max_descent = 13,
      max_ascent = 48,
      height = 61,
      total_width = 1248
    },
    text = "inaudible by the actor, but I'll be guiding you along.",
    tags = { ... } --[[0x136f555f0]],
    node = inaudible by the actor, but I'll be guiding you along.@(-654.5, -183, 0),
    font = "dialogue",
    shadow = vmath.vector4(0, 0, 0, 0)
  },
  5 = { --[[0x136ed7d60]]
    outline = vmath.vector4(0, 0, 0, 0),
    size = 1,
    color = vmath.vector4(1, 1, 1, 1),
    font = "dialogue",
    text = " ",
    nobr = true,
    node =  @(593.5, -183, 0),
    metrics = { --[[0x136d1c5c0]]
      width = 0,
      max_descent = 13,
      max_ascent = 48,
      height = 0,
      total_width = 11
    },
    tags = { --[[0x136ee3fa0]]
      nobr = true,
      img = "t:advance"
    },
    linebreak = false,
    tag = "nobr",
    shadow = vmath.vector4(0, 0, 0, 0)
  },
  6 = { --[[0x136ece3a0]]
    outline = vmath.vector4(0, 0, 0, 0),
    size = 1,
    color = vmath.vector4(1, 1, 1, 1),
    font = "dialogue",
    text = "",
    nobr = true,
    image = { --[[0x136eda4d0]]
      anim = "advance",
      texture = "t"
    },
    node = box@(604.5, -183, 0),
    tags = { --[[0x136ee3fa0]]
      nobr = true,
      img = "t:advance"
    },
    metrics = { --[[0x136d01700]]
      width = 50,
      height = 50,
      total_width = 50
    },
    tag = "img",
    shadow = vmath.vector4(0, 0, 0, 0)
  },
  7 = { --[[0x136ec67c0]]
    outline = vmath.vector4(0, 0, 0, 0),
    size = 1,
    color = vmath.vector4(1, 1, 1, 1),
    font = "dialogue",
    text = "",
    nobr = true,
    node = @(654.5, -183, 0),
    metrics = { --[[0x136d03b70]]
      width = 0,
      max_descent = 13,
      max_ascent = 48,
      height = 61,
      total_width = 0
    },
    tags = { --[[0x136ec5860]]
      nobr = true
    },
    linebreak = false,
    tag = "nobr",
    shadow = vmath.vector4(0, 0, 0, 0)
  },
  8 = { --[[0x136ebeab0]]
    outline = vmath.vector4(0, 0, 0, 0),
    size = 1,
    color = vmath.vector4(1, 1, 1, 1),
    font = "dialogue",
    text = "",
    node = @(0, -244, 0),
    tags = { } --[[0x136ec8680]],
    metrics = { --[[0x136d05070]]
      width = 0,
      max_descent = 13,
      max_as...
[Output truncated]

is_empty tags improperly being added to preceding words since 5.8.0

If I do

local last_word = richtext.truncate(words, length, {words = false})

then

pprint(last_word.tags)

for the text

text...,,,,,,,,,,,,,,,,,,,,,,,,,,,,<br/>

I will get

DEBUG:SCRIPT: 
{ --[[0000019E1A974C90]]
  br = true
}
DEBUG:SCRIPT: 
{ --[[0000019E1A974C90]]
  br = true
}
DEBUG:SCRIPT: 
{ --[[0000019E1A974C90]]
  br = true
}
etc.

Same is true for other is_empty like

Add callback support for handling unknown tags

Allow the user to setup a callback function to run if the user wants to use tags which are not defined in the normal extension.

Example for a possible action tag would be

<action=particles:text_emitter>Make this pretty</action>

and

<action=sound:ding/>

The main thing the callback needs to be used for is defining how the data of the tag is handled so in the sound case the "sound:ding" would need to be handled when doing parse_tag.

local parse = require("richtext.parse")
parse.callback = function(tag, params, settings)
	if tag == "action" then
		local action_type, action_option = params:match("(.-):(.*)")
		settings.action = {
			type = action_type,
			option = action_option
		}
	end
end


.... in parse.lua below other tag definitions

	elseif M.callback then
		M.callback(tag, params, settings)
	end

metrics should include img count

Reasoning is that if you use an img within a truncate based on the metrics.char_count you will likely cut off the final char as the actual truncated chars will be one short by the end of it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.