B4A Class Draw HTML text (also some HTML parsing)

I am working on a game and I wanted dialog to be more than just plain text. Some games starting with the N64 and GBA era have had switches of text color and size. I figured the easiest way to control all that would be to use HTML. It also has the standard typewriter effect, and a scrolling effect when done. But more effects can be added later (ie: slow down/speed up the typewriter effect down for some parts)

Tags it supports:

<B>: Sets the current block to bold
<U>: Sets the current block to underline
<I>: Sets the current block to italic
<FONT>
Supported parameters:
COLOR="#FF00FF": Color format in #RRGGBB, or #AARRGGBB
COLOR="red": supports all the predefined HTML colors
FACE="fontname"
use .AddFont or .AddFontFile to add a font beforehand. But if fontname is a font file in the asset directory it'll be added automatically.
Android's built in fonts have the fontnames as: default, default bold, monospace, sans serif, serif
SIZE:
"number": Set the fontsize of this block to number
"+number", "-number": Set the fontsize of this block to number+/- the current fontsize
"xx-small": default fontsize - 3
"x-small": default fontsize - 2
"small": default fontsize - 1
"large": default fontsize + 1
"x-large": default fontsize + 2
"xx-large": default fontsize + 3
"xx-smaller": current fontsize - 3
"x-smaller": current fontsize - 2
"smaller": current fontsize - 1
"larger": current fontsize + 1
"x-larger": current fontsize + 2
"xx-larger": current fontsize + 3
<IMG SRC="name">: Draws an image scaled to the size of the line (useful for button icons)
<BR>/<P>: Forces a carriage return

All HTML entities are supported except for "shy", "zwnj", "zwj", "lrm", "rlm".
This includes specifying the number in decimal (&#nnnn; where nnnn = decimal number) and hexadecimal (&#xhhhh; where hhhh = hexadecimal number)
They're all converted to their proper character in the parsing stage, except for &NBSP; which is handled a special way for the word wrapping.

It now supports basic CSS (see post below)

HTML parsing:
SplitHTML: Splits HTML up into parts of plain text, and HTML tags
IsValidHTML: Checks split HTML if the HTML tags are in a valid order. ie: each tag that requires and ending tag has one in the proper spot
ParseTag: Parses an HTML tag into into a map containing it's parameters=values pairs (all lower-cased), tag_name, tag_closing ("open" = <TAG>, "start" = </TAG>, "end" = <TAG />, "singleton" = tags that don't require an ending tag ("area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr"))

Unlike a web browser, carriage returns and repeated spaces are treated as they are, instead of collapsing them into a single space. I may eliminate repeated spaces later on.

The code is optimized to run fast, so when you pass it HTML, it is split apart and parsed into segments that can be handled by the draw system much faster than parsing HTML each frame. You can set up the default font, size, color, bold/italic/underline, alpha as they're just public variables.

If you leave the drawing up to this class, you can set the background color (so it's not just drawing text over text from a previous frame) or tell it to sample the canvas in case you have an image you want to preserve as the background using .SampleBackground and .ClearBackground

This code is actually more advanced than what many games use. It's more advanced than code I used to use (which was just simple word-wrapping using a single font and size), and that alone was even more advanced than many games use. I can't tell you how many games I've played that used a single, hard-coded font size, or have caught improper/manual word-wrapping. I know gamers have complained about those sort of things (ie: The start of the HD era of console gaming had complaints of small font sizes, which even with my older code could have been easily solved by letting users choose their font size).

If there is demand for more functionality added, I'll endeavor to do so. I can even add keyboard handling (ie: pass it a keycode, and it'll move the caret/cursor around, handle selection highlighting, editing the text)

Example:
B4X:
Sub Globals
   'These global variables will be redeclared each time the activity is created.
   'These variables can only be accessed from this module.
   Dim BG As Canvas
   Dim HTMLtextObject As HTMLtext
End Sub

Sub Activity_Create(FirstTime As Boolean)
   BG.Initialize(Activity)
End Sub

Sub Activity_Resume
   BG.Initialize(Activity)
End Sub

Sub Activity_Touch (Action As Int, X As Float, Y As Float)
   If Action = 0 Then
       Dim HTML As String = "this is a test <FONT COLOR='red'>of&NBSP;the</FONT> &ograve; &ogrAve; html splitter&exclamation;"
       HTMLtextObject.Initialize(BG)
 
       HTMLtextObject.setDestination(100,100,200,200)
       HTMLtextObject.InitializeEvent(Me, "HTML")
       HTMLtextObject.InitializeTimer(50)
  
       HTMLtextObject.LoadCSS(File.DirAssets, "test.css")
       HTMLtextObject.HTML = HTML
       HTMLtextObject.StartTypewiter
   End If
End Sub

Sub HTML_Tick
   Activity.Invalidate
End Sub
Sub HTML_TypewriterDone
   HTMLtextObject.ScrollSpeed = 5
End Sub
Sub HTML_ScrollDone
   Log("Scroll done")
End Sub

version 2:
-Adds basic CSS support (uses tag names, IDs, and classes)

version 3:
-Fixed the typewriter typo noted below
-closing tags are handled better
-underline is properly supported
 

Attachments

  • HTMLtext.zip
    17.3 KB · Views: 171
Last edited:

NeoTechni

Well-Known Member
Licensed User
Longtime User
There is no size tag. It's <FONT SIZE="10">text</FONT>

My example code screwed up the order since it's setting the font size after loading the HTML, which already has the text size set... I updated the code.

I'm also considering some basic CSS support. Like just classnames and tagnames in particular
 

NeoTechni

Well-Known Member
Licensed User
Longtime User
I added basic CSS support.

Upon loading a CSS file using .LoadCSS, it'll check for any CSS block (or whatever you call it) that matches the "body, html" selector, and use those blocks to set the defaults.

Upon loading HTML, it'll check for CSS blocks that matches the Class or ID paramters, or the tagname
meaning <DEMO ID="test", CLASS="classtest classing"> will match against either "demo", "#test", ".classtest", or ".classing"
I am trying to stick to the standards as much as I can
It won't check any of the advanced CSS selectors, like checking if it's the child of a selector, or ":not(something)"

I figure the use case of this is if you want say, certain settings to always be used for a specific character in a game, so you can use
<DIV CLASS="msg-jimbob">Hi y'all</DIV>
And have jimbob use a specific font all the time (ie: comic sans)

Now currently the CSS supports only specific key:value; key names
"background-color": sets the background color of the entire window for now
"font-size"
"font-style": can be "italic", "bold", "underline", "regular" or "normal" (italic, bold and underline are all set to false), "default" (set to the defaults)
"content": sets the text of the node
"font-family": sets the font of the node
"line-height" sets the line height of the entirety of the HTML

I can add support for more of course.
I intend to add text alignment, and the ability to set the width of nodes (so you can have columns, ie: for end credits), but those are more complex than you'd think
 

NeoTechni

Well-Known Member
Licensed User
Longtime User
I had a very busy few weeks... Anywho, I added underline support. Before closing tags had to end themselves manually, </b> would remove bold, even if it was inside another bold tag. I made it actually load the settings from before the start of the tag so recursion is handled properly. I fixed the above typo. I broke out the drawing code so it'll be easier to add more features later (ie: stroke, shadow, blink)
 
Top