B4A Library [B4X] BCTextEngine / BBCodeView - Text engine + BBCode parser + Rich Text View

Status
Not open for further replies.
Be open minded.

i_view64_p9fVifsimf.png


This is a cross platform library with several features:

- Text drawing engine.
- Text layout engine.
- BBCode parser.
- Two custom views that connect everything:
BBCodeView - multiline, scrollable with support for embedded views.
BBLabel - lightweight label.
- NEW: BBScrollingLabel: https://www.b4x.com/android/forum/threads/b4x-bbscrollinglabel-rich-text-scrolling-label.114310/

F0DYcnZwgV.gif



The library and the code inside can be used for all kinds of things related to text. You can for example use it as an alternative to CSBuilder where you have more control over the text drawing.

I will explain the main use case of BBCodeView.
Our layout is usually made of boxes. The boxes positions and sizes can change, however no matter what, the content is split into boxes.

With BBCodeView, text is the king. The text layout engine builds the layout based on the text. This is similar to html code. However, unlike html where you need to run a monstrous engine (WebView) inside your app, here there is no additional engine. BBCodeView is made of a ScrollView with an ImageView and additional views that you can add.
This means that you don't need to anything special to interact with the internal views and you can add any view you like inside the text.

Lets start with a simple example:
1. Create a layout with BBCodeView.
2. Initialize a BCTextEngine object. This should be called after the BBCodeViews and BBLabels were added.
B4X:
TextEngine.Initialize (Activity)
BBCodeView1.Text = $"Hello world!"$

You can use all kinds of BBCode tags to format the text and add non-text elements inside.
B4X:
BBCodeView1.Text = $"[b]Hello[/b] [url]world![/url]"$

java_Cv9hBtIpbV.png


The supported tags are:

[b] - Bold.
[u] - Underline.
[Url] - Clickable link. The LinkClicked event is raised when the content is clicked. Examples:
[url]world![/url]
[url="https://www.b4x.com"]hello[/url]
[Plain] - prevents the internal text from being parsed.
[Color] - Changes the text color. Example:
[color=#ff00ff]Hello[/color]
[Img] - Adds an image. Can be local or remote. Examples:

[img dir="${File.DirInternal}" FileName="logo.png" width=50/] 'width and height are optional for local images. Omit the dir parameter for assets files.
[img url="https://...s/l/42/42649.jpg?1432374732" width=60 height=60/] 'width and height are required for remote images.
[Vertical] - Changes the vertical offset. Example:
[Vertical=30]aaa[/Vertical]
[TextSize] - Changes the text size. Example: [TextSize=25]asdasd[/TextSize]
[Alignment] - Changes the horizontal alignment. One of the following values: Left, Right or Center.
Example: [Alignment=Center]Title[/Alignment]
[View] - Adds a custom view. Example:
[View=Btn1/]

[Span] - Creates an unbreakable section. Supports the following keys: MinWidth and Alignment. You can use it to create columns (see the example).

[INDENT]- Indention level. Example:
[INDENT=2]
[LIST]
[*]- Creates an ordered or unordered list. See the attached examples.
[FontAwesome] and [MaterialIcons] - Inserts a FontAwesome or Material Icons icon. Example:
[FontAwesome=0xF034/]
Supports the following keys: Size (text size) and Vertical (vertical offset).
[E] - Adds a sequence of characters that will be treated as a single complex character. Useful for complex emojis such as flags. Example
[E=??/]
[Font] - Sets a custom font that was previously added to TextEngine.CustomFonts. See this post for more information: https://www.b4x.com/android/forum/threads/b4x-bctextengine-bbcodeview-text-engine-bbcode-parser-rich-text-view.106207/post-702304
[a] - Adds an anchor. Used with BBCodeView.ScrollToAnchor. See example: https://www.b4x.com/android/forum/threads/b4x-bctextengine-bbcodeview-text-engine-bbcode-parser-rich-text-view.106207/post-938548

View, Img, FontAwesome and MaterialIcons tags should end with /].
You can also use vertical as a key inside View and Img tags.

Adding views is done in two steps:
1. Create a view and add it to BBCodeView.Views map.
2. Add it with the View tag.

B4X:
Dim btn As Button
btn.Initialize("btn")
btn.Text = "Click!"
btn.SetLayoutAnimated(0, 0, 0, 100dip, 40dip)
BBCodeView1.Views.Put("btn", btn)
BBCodeView1.Text = $"Lets add a button here [View=btn Vertical=10/]. Do you see it?"$

java_ROTR5mX3ls.png


The cross platform b4xlib is attached. Note that it depends on jBitmapCreator v4.71+ which is available in B4J v7.50 and B4i v5.80.
B4A library: https://www.b4x.com/android/forum/attachments/bitmapcreator-zip.81067/ (copy to the internal folder).
It is recommended to use Java 9+ on B4J. You will get better results on high DPI screens.

(The example adds a CustomListView to the layout. This can cause an issue in B4A if the layout becomes scrollable.)



Updates

- v1.95 - New anchors feature: https://www.b4x.com/android/forum/t...code-parser-rich-text-view.106207/post-938548
- v1.93 - Fix issues with Arabic scripts.
- v1.92 - MinGapBetweenLines field is now public. It can be used to make the lines more dense.
- v1.91 - Fixes several issues with Arabic languages.
- v1.90 - Fixes an issue where auto detect url stops working after the view is resized.
- v1.89 - Adds support for Arabic. Make sure to set BBCodeView/BBLabel.RTL = True. This version requires B4A 11.0+, B4i 7.5+ or B4J 9.1+
- v1.88 - Fixes an issue with MaterialIcons tag.
- v1.87 - BBCodeView - Links are underlined automatically when the user moves the cursor or presses on the link. Note that it is only enabled when lazy loading is enabled.
Example was updated and it is now based on B4XPages. B4i - There is an important inline OBJC code in example that you should add to prevent unwanted URL clicks when the user scrolls the BBCodeView.
- v1.86 - Initial support for right to left languages: https://www.b4x.com/android/forum/t...code-parser-rich-text-view.106207/post-779309
- v1.84 - Fixes bugs related to handling of complex characters.
- v1.81 - Adds an option for asynchronous drawing. This is an advanced option. You can see an usage example in the Pleroma client.
- v1.80 - Fixes several bugs. Allows disabling word wrap in BBLabel.
- v1.77 - Fixes issue with bold fonts in iOS 13 and exposes BBCodeView_BaseResize, it s is needed in B4A when we want to resize BBCodeView.
- v1.76 - New VowelsCodePoints set. Usage example: https://www.b4x.com/android/forum/threads/b4x-bctextengine-unicode-problem.117893/post-737764
Fix for wrapping issue with indented lines.
- v1.75 - B4i only - fixes an issue where wrong colors can appear.
- v1.74 - Fixes an issue with non-breaking white space.
- v1.73 - Adds support for kerning - dynamic spacing between characters based on the specific characters. Example here: https://www.b4x.com/android/forum/t...code-parser-rich-text-view.106207/post-711296
Kerning is enabled by default. It has some overhead. In most cases it shouldn't be significant. You can disable kerning with TextEngine.KerningEnabled = False.
- v1.72 - BCTextRun.Text field was added back. Fixed issue where setting BBLabel.Text to an empty string didn't remove the text.
- v1.71 - New Font tag: https://www.b4x.com/android/forum/t...code-parser-rich-text-view.106207/post-702304
- v1.70 - Adds support for complex characters such as emojis. Most emojis can be added directly and the text engine will recognize the sequences automatically. In some cases such as with flags you should use the new 'E' tag to add the emoji. See the examples.
- v1.65 - Fixes an issue where the designer text color was ignored. The set text color is used as the default color.
New BBLabel.DisableResizeEvent field. This is used by BCToast library which removes the internal ImageView from BBLabel after it is drawn.

- v1.64 - New property: WordBoundariesThatCanConnectToPrevWord - sets the split characters that will be connected to the previous word. Default value is ".,:".
- BBCodeDesigner v1.00 - B4J utility that helps with writing and testing BBCode strings:

java_olBorAQt9N.png


1.63 - Views tags support the width and height properties.
1.62 - Adds support for Justify alignment.
1.61 - New BBCodeView.ExternalRuns property. This allows setting the styled text directly. Usage example: https://www.b4x.com/android/forum/threads/b4x-bctextengine-parser-b4x-code-highlighter.109308/

1.60 - Important update. Adds support for "lazy loading". In this mode the text is only drawn when it becomes visible and is removed when it becomes invisible.
With this change it is possible to use BBCodeView to show text made of thousands of lines.
Lazy loading is enabled by default. It is set in the designer. Uncheck this option if you want to get a single bitmap with all the text.

1.58 - Removes the background BitmapCreator that is not used by BBCode parser.
1.57 - BCTextEngine.WordBoundaries is now a public variable. Its default value is: "&*+-/.<>=\' ,:{}" & TAB & CRLF & Chr(13)
You can modify it and change the list of characters that are treated as separators.
1.56 - The '(' character is no longer considered a separator character.
1.55 - Underline tag accepts Color and Thickness parameters:

java_4JJvdNeT0E.png


- Named colors are supported. Same names as available with XUI.
1.52 - Fixes a bug with images in BBLabels.
1.51 - Changes the way the BBLabel ImageView is set to make it possible to measure it.
1.50 - Large update. New BBLabel custom view. This is the lightweight version of BBCodeView. It is not scrollable and doesn't handle events.
- TextEngine.Initialize expects the parent view. It then searches the views tree for BB views and sets the engine. It is no longer needed to set the TextEngine for each view.
- New MaterialIcons and FontAwesome tags.
- The text can be set with the designer.
The examples were updated.

1.06 - New Indent and List tags.

firefox_VF1FJGFOeE.png


See the examples.

1.05 - New Span tag. The Span tag creates an unbreakable content. It supports the following keys: MinWidth and Alignment.
You can use it to create tables:

firefox_595n6GLQ5W.png


Width dimensions can be set with %x units. The percentage is relative to BBCodeView width.

1.03 - Fixes a drawing issue.
1.02 - New BBCodeView.Padding field. Can be used to change the padding. Default value is:
B4X:
'left, top, right, bottom
If xui.IsB4J Then
   Padding.Initialize(5dip, 5dip, 20dip, 5dip) 'leaves space for the scroll bar.
Else
   Padding.Initialize(5dip, 5dip, 5dip, 5dip)
End If

1.01 - Fixes a compilation issue in release mode.
The Img.Dir parameter is not needed when loading files from the assets folder.

Another example with a bit more logic is available here: https://www.b4x.com/android/forum/t...rser-rich-text-view.106207/page-2#post-665369

BCTextEngine is an internal library.
 

Attachments

  • BBCodeDesigner.zip
    18.2 KB · Views: 2,708
  • BCTextExample.zip
    232.8 KB · Views: 2,101
  • BCTextEngine.b4xlib
    22.2 KB · Views: 571
Last edited:

peacemaker

Expert
Licensed User
Longtime User
Thanks, indeed, there is difference, but, seems, not each is able to detect. I remember that this "eye feature" may get negative result, if an app development customer needs such design nuances and is able to see, but me, as developer, - not :confused:
 

js486dog

Active Member
Licensed User
Longtime User
v1.73 released with support for kerning.

Without kerning:

java_YqhMxj8rZc.png


With kerning:

java_ah0SKXxJS9.png
Erel please I have a little problem with 1.73 version.

Please look at the pictures.
1.73
BCTextEngine_173.jpg

1.72
BCTextEngine_172.jpg

I load UTF8 text file in the BCTextEngine.
Here is my code:
B4X:
TView.TextEngine.WordBoundaries = "&*+-/<>=\' :{}" & TAB & CRLF & Chr(13)
If pis_nt.Text ="N" Then TView.Text = $" [Alignment=${alig}][TextSize=${txtsize}][color=${texcolor}][PLAIN]${te1}[/PLAIN][/color][/TextSize][/alignment] "$ _
Else TView.Text = $" [Alignment=${alig}][TextSize=${txtsize}][color=${texcolor}][b][PLAIN]${te1}[/PLAIN][/b][/color][/TextSize][/alignment] "$
 

Attachments

  • BCTextEngine_173.jpg
    BCTextEngine_173.jpg
    193.8 KB · Views: 397
  • BCTextEngine_172.jpg
    BCTextEngine_172.jpg
    192.2 KB · Views: 410
Last edited:

Toky Olivier

Active Member
Licensed User
Longtime User
Erel please I have a little problem with 1.73 version.

Please look at the pictures.
1.73
View attachment 88581
1.72
View attachment 88582
I load UTF8 text file in the BCTextEngine.
Here is my code:
B4X:
TView.TextEngine.WordBoundaries = "&*+-/<>=\' :{}" & TAB & CRLF & Chr(13)
If pis_nt.Text ="N" Then TView.Text = $" [Alignment=${alig}][TextSize=${txtsize}][color=${texcolor}][PLAIN]${te1}[/PLAIN][/color][/TextSize][/alignment] "$ _
Else TView.Text = $" [Alignment=${alig}][TextSize=${txtsize}][color=${texcolor}][b][PLAIN]${te1}[/PLAIN][/b][/color][/TextSize][/alignment] "$
It seems that the problem comes from some punctuations only: ! . "
 

William Lancee

Well-Known Member
Licensed User
Longtime User
@js486dog

Those word boundaries work fine in my app. So it must have something to do with your content "te1".
I added tabs and crlfs to my content and it still works fine. Are there any unprintable characters between "." and "Beda"?
 

William Lancee

Well-Known Member
Licensed User
Longtime User
I note that your boundary list is almost the same as the default (you have removed the . and the ,)
Does it work with the default (i.e. comment out: 'TView.TextEngine.WordBoundaries = "&*+-/<>=\' :{}" & TAB & CRLF & Chr(13)
Does your content have Chr(13) in it, as part of end of line characters?
Just thinking...

@Toky Olivier notes that the "!" is not in the word boundaries (either in yours or the default, neither is "?")
 
Last edited:

Toky Olivier

Active Member
Licensed User
Longtime User
I note that your boundary list is almost the same as the default (you have removed the . and the ,)
Does it work with the default (i.e. comment out: 'TView.TextEngine.WordBoundaries = "&*+-/<>=\' :{}" & TAB & CRLF & Chr(13)
Does your content have Chr(13) in it, as part of end of line characters?
Just thinking...

@Toky Olivier notes that the "!" is not in the word boundaries (either in yours or the default, neither is "?")
I think that he needs to add also the Double Quote character in the WordBoundaries by adding Chr(34) or $"""$
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
I'm curious, can you comment on how difficult this was to add?
Finding the kerning (?) space is not too difficult as it is based on the same implementation that used to extract the glyphs with the canvas:
B4X:
Private Sub GetKernSpaceBetweenChars (fm As BCFontMetrics, PrevChar As String, ThisChar As String, PrevGlyph As BCGlyph, ThisGlyph As BCGlyph) As Int
    Dim together As String = PrevChar & ThisChar
    Dim Space As Int = fm.KerningTable.GetDefault(together, -1000)
    If Space > -1000 Then Return Space
    Dim w As Int = CreateGlyph(together, fm, True).Width
    Dim res As Int = w - PrevGlyph.Width - ThisGlyph.Width
    fm.KerningTable.Put(together, res)
    Return res
End Sub

BCTextEngine is quite complex so any new feature and especially a low level feature such as this one, do require some work.
 

Sandman

Expert
Licensed User
Longtime User
BCTextEngine is quite complex so any new feature and especially a low level feature such as this one, do require some work.
I can imagine. I really appreciate it, thanks.

Thanks for the explanation.
 

dobro

Member
Licensed User
hello

I wanted to put an HTML text in the BBLabel
or BBCodeView, but it's causing an error.

while the display of a single line of text works

e.g. this works: sortie.Text = "[ b]BB code written in Bold [ /b]"

but not sortie.Text =HTML_code <--- variable filled with an HTML text

(Look my ZIP ) Thanks

3
Error (position - 1555): Invalid beacon: 127987,65039,8205,9895,65039
Error occurred online: 184 (BBCodeParser)
java.lang.RuntimeException: The object must first be initialized (List).
at anywheresoftware.b4a.AbsObjectWrapper.getObject(AbsObjectWrapper.java:50)
at anywheresoftware.b4a.objects.collections.List.getSize(List.java:129)
to b4a.example.bbcodeparser._createruns(bbcodeparser.java:166)

on com.android.internal.os.ZygoteInit.main(ZygoteInit.java:679)

ect ....



Translated with www.DeepL.com/Translator (free version)
 

Attachments

  • lire_html_url.zip
    10.1 KB · Views: 445
Last edited:

dobro

Member
Licensed User
This library is not for HTML texts but for BB Codes.
You can use another library like: https://www.b4x.com/android/forum/threads/html-textview-v1-0.50696/#content

I understand that, yes.
but I thought I'd use the View to simply display the text of the HTML code, with no desire to interpret it ...

I'm looking for a view to simply display text
if in addition I can add tags to change the look ....

But thank you for your answer, I'll look at the library you recommend. :)

REEDIT :
Ps: the library that you indicate, seems to interpret the HTML code, it's not what I'm looking for.
I'm only looking for a "Gadget_Text" (rtf for example )
 

William Lancee

Well-Known Member
Licensed User
Longtime User
@dobro

If you don't want content to be parsed, use Plain tag. You have a "[" at index 916.

I don't know what the limit is, but 85108 for this string is too big on my system.

B4X:
Dim lengthLimit As Int = 2000
BBLabel1.Text = $"[Plain]${HTML_text.SubString2(0, lengthLimit)}[/Plain]"$
 
Last edited:

William Lancee

Well-Known Member
Licensed User
Longtime User
Oddly enough the Plain tag also works on this Forum! It exists between "use" and "tag" without quotes. The code tags are ignored!

If you don't want content to be parsed, use tag. You have a "[" at index 916.

I don't know what the limit is, but 85108 for this string is too big on my system.

[CODE=b4x]Dim lengthLimit As Int = 2000
BBLabel1.Text = $"[Plain]${HTML_text.SubString2(0, lengthLimit)}"$[/CODE]
 

dobro

Member
Licensed User
I answer to myself

That's what EditText is for !:rolleyes:

@William Lancee

Thanks, I didn't know [plain ] :)
with the EditText, I can do what I want :)

it takes the size without any problem (on my tablet)
I also learned that ".initialize" was only useful
when the View (Gadget) is not already created by the designer

in any case thank you for everything :)
 
Last edited:

William Lancee

Well-Known Member
Licensed User
Longtime User
@js486dog

Your text sometimes has chr(160) instead of chr(32)
chr(160) Unicode is "NO-BREAK SPACE"
Replacing it with chr(32) fixes the problem.

B4X:
MyText = MyText.Replace (Chr(160), Chr(32))
 
Status
Not open for further replies.
Top