Markdown to html with a twist

Erel

B4X founder
Staff member
Licensed User
Longtime User
1737961919193.png


Code that downloads a markdown string from github and then converts it to html and shows it with WebView:
Code: Copy
Private Sub Start
    Dim Py As PyBridge
    Py.Initialize(Me, "Bridge")
    Wait For Bridge_Connected
    Dim j As HttpJob
    j.Initialize("", Me)
    j.Download("https://gist.githubusercontent.com/rt2zz/e0a1d6ab2682d2c47746950b84c0b6ee/raw/83b8b4814c3417111b9b9bef86a552608506603e/markdown-sample.md")
    Wait For (j) JobDone (j As HttpJob)
    If j.Success Then
        Dim markdown2 As PyWrapper = Py.ImportLib.ImportModule("markdown2")
        Dim md As String = j.GetString
        Wait For (markdown2.Run("markdown", Array(md)).Fetch) Complete (Html As PyWrapper)
        WebView1.LoadHtml(Html.Value)
    End If
End Sub

And for the twist:
markdown2 is a python library.
 
View attachment 161177

Code that downloads a markdown string from github and then converts it to html and shows it with WebView:
Code: Copy
Private Sub Start
    Dim Py As PyBridge
    Py.Initialize(Me, "Bridge")
    Wait For Bridge_Connected
    Dim j As HttpJob
    j.Initialize("", Me)
    j.Download("https://gist.githubusercontent.com/rt2zz/e0a1d6ab2682d2c47746950b84c0b6ee/raw/83b8b4814c3417111b9b9bef86a552608506603e/markdown-sample.md")
    Wait For (j) JobDone (j As HttpJob)
    If j.Success Then
        Dim markdown2 As PyWrapper = Py.ImportLib.ImportModule("markdown2")
        Dim md As String = j.GetString
        Wait For (markdown2.Run("markdown", Array(md)).Fetch) Complete (Html As PyWrapper)
        WebView1.LoadHtml(Html.Value)
    End If
End Sub

And for the twist:
markdown2 is a python library.

That means will have new version of B4J? or a new library...

🎉
 
That means will have new version of B4J? or a new library...

🎉
I see Erel has added more and more support for Python recently.
Maybe PyWrapper and PyBridge will be releasing soon. This thread looks like a teaser intro for the upcoming libraries.
I think this will enable us to use some Python libraries such as field in Data Science and AI.

https://www.b4x.com/android/forum/threads/python-b4xserializator-implementation-in-python.165109/
https://www.b4x.com/android/forum/threads/python-jrdc2-python-client.165111/
https://www.b4x.com/android/forum/pages/results/?query=python
 
Text recognition with EasyOCR: https://www.jaided.ai/easyocr/tutorial/
Processed offline and with GPU, if available.

java_LfJKxE11Uh.gif

(image source: https://www.lumina.com.ph/news-and-...-signs-in-the-philippines-and-their-meanings/)

The relevant code:
Code: Copy
Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("MainPage")
    xui.SetDataFolder("EasyOCR")
    Chooser.Initialize(Me, "Chooser")
    SetWorkingState(True)
    Py.Initialize(Me, "py")
    Py.Start(Py.CreateOptions("D:\python\python\python.exe"))
    Label1.Text = "Waiting for Python process to connect"
    cvs.Initialize(pnlCanvas)
    Wait For Py_Connected
    Label1.Text = "Loading OCR reader"
    ocr.Initialize(Py, Array("en"))
    Wait For (Py.Flush) Complete (Unused As Boolean)
    Label1.Text = "Ready"
    SetWorkingState(False)
End Sub

Private Sub Button1_Click
    Wait For (Chooser.ChooseImage(Button1)) Complete (Result As MediaChooserResult)
    If Result.Success Then
        Dim bmp As B4XBitmap = xui.LoadBitmap(Result.MediaDir, Result.MediaFile)
        Dim OrigWidth As Int = bmp.Width
        bmp = bmp.Resize(ImageView1.Width, ImageView1.Height, True)
        Dim Scale As Float = OrigWidth / bmp.Width
        pnlCanvas.SetLayoutAnimated(0, ImageView1.Left, ImageView1.Top, bmp.Width, bmp.Height)
        cvs.ClearRect(cvs.TargetRect)
        cvs.Resize(pnlCanvas.Width, pnlCanvas.Height)
        cvs.Invalidate
        ImageView1.SetBitmap(bmp)
        SetWorkingState(True)
        Label1.Text = "Reading text from image"
        Wait For (ocr.ReadText(File.Combine(Result.MediaDir, Result.MediaFile))) Complete (OcrResults As List)
        SetWorkingState(False)
        Label1.Text = ""
        cvs.ClearRect(cvs.TargetRect)
        For Each res As OcrResult In OcrResults
            Dim MinX = 100000, MaxX = 0, MinY = 100000, MaxY = 0 As Int
            For Each pnt As OcrPoint In res.Points
                MinX = Min(pnt.X, MinX)
                MinY = Min(pnt.Y, MinY)
                MaxX = Max(pnt.X, MaxX)
                MaxY = Max(pnt.Y, MaxY)
            Next
            Dim r As B4XRect
            r.Initialize(MinX / Scale, MinY / Scale, MaxX / Scale, MaxY / Scale)
            cvs.DrawRect(r, xui.Color_Blue, False, 2dip)
            If Label1.Text = "" Then
                Label1.Text = res.Text
            Else
                Label1.Text = Label1.Text & ", " & res.Text
            End If
            cvs.Invalidate
        Next
    End If
End Sub

The Python related code is implemented in a separate class. It is quite trivial, maybe except of a definition of two dataclasses that map the results to B4X types:
Code: Copy
Type OcrPoint (X As Int, Y As Int)
Type OcrResult (Points As List, Text As String, Score As Double)

A draft version of the library is attached for the courageous developers who want to see how it works.
This example depends on Python with EasyOCR dependencies. I like this portable Python for Windows: https://winpython.github.io/
Making it work with GPU requires some steps which will be explained in the future.
 

Attachments

Whaoo! Thank you so much Erel! I have not tried this, I will soon. However, please, if you find any solution that can read human written handwriting on conventional paper - camera or bitmap-chooser based( not digital text extraction as you have already provided more than enough on that).
 
V0.2 of PyBridge is attached. The API is still not final and will be changed before the first release.

Note that this method should be added to the main module:
Code: Copy
Public Sub PyLogHelper (Text As String, Clr As Int)
    LogColor(Text, Clr)
End Sub

I've added two code snippets (write py and you will see them). One for the above snippet and a more important one for easy implementation of RunCode.

B4J_dgyz30BS7l.gif
 

Attachments

Last edited:
Note that this code also works:
Code: Copy
Dim r As PyWrapper = ExampleOfAdding(10, 20)
r = ExampleOfAdding(r, r)
r.Print 'prints 60

You can combine local and remote (PyWrapper) arguments. I will explain all of this.
And to get the value back to B4X:
Code: Copy
Wait For (r.Fetch) Complete (r As PyWrapper)
Dim x As Int = r.Value
Log(x) '60
 
Is there a way to re-run code or do we have to pass the python source each time?
 
@Erel ...As I understand the code is running async... so for that you are using "wait for" ....

But if for example... the code has while 1==1:
Will loop for ever...

- So may be need a quit option ctrl-c... to terminal pipe..
- also may be need get somehow results of current output or current values of variables...
 
Is there a way to re-run code or do we have to pass the python source each time?
The code is "registered" once and is then mapped to the function name. It is recommended to use a single sub as in my examples. The code variable is just a pointer to a string. It has no effect on performance.

.As I understand the code is running async... so for that you are using "wait for" ....
It is a bit more complicated. I will explain it soon.

If you make an endless loop then the wait for will never end. The program will not freeze. This is exactly like waiting for an event that is never raised.
 
This example demonstrates how Python can be distributed together with your desktop app.

It uses a library named fpdf2 to create a PDF file from a html text.

1738673890044.png



1738673920355.png



The interesting code is:
Code: Copy
Public Sub HtmlToPDF (Html As Object) As PyWrapper
    Dim Code As String = $"
import fpdf

def HtmlToPDF (Html):
    pdf = fpdf.FPDF()
    pdf.add_page()
    pdf.write_html(Html)
    return pdf.output()
"$
    Return Py.Utils.RunCode("HtmlToPDF", Array(Html), Code)
End Sub

And usage:
Code: Copy
Wait For (HtmlToPDF(TextArea1.Text).Fetch) Complete (Result As PyWrapper) 'notice the call to Fetch.
It returns the PDF document as an array of bytes.

Now for the distribution.
First, you can try it yourself: https://www.b4x.com/b4j/files/PyPDF_Example.exe

The installer size is 47MB and it is a standalone package - it includes both the Java runtime and the Python runtime. Overall not too bad.

Steps to build it:

1. Download the "dot" package: https://github.com/winpython/winpython/releases/tag/11.2.20241228final
2. Unzip it in the project folder in a folder.

1738674392682.png


Install the dependencies needed. Use WinPython Command Prompt.exe to run pip.

The bridge is started with:
Code: Copy
Py.Start(Py.CreateOptions(File.Combine(File.DirApp, "..\Python\Python\python.exe")))

And this custom build action will copy Python to the standalone package (it also excludes a few folders):
Code: Copy
#CustomBuildAction: after packager, %WINDIR%\System32\robocopy.exe, ..\Python temp\build\python /E /XD __pycache__ Doc tcl pip setuptools tests

The project is attached.
PyBridge v0.3 is also attached. Note that it depends on two updated internal libraries: https://www.b4x.com/android/forum/threads/updates-to-internal-libaries.48274/post-1014241
There were many internal improvements. Note that the Connected event now includes a "Success" parameter:
Code: Copy
Wait For Py_Connected (Success As Boolean)
 

Attachments

Last edited:
This example demonstrates how Python can be distributed together with your desktop app.

It uses a library named fpdf2 to create a PDF file from a html text.

View attachment 161377


View attachment 161378


The interesting code is:
Code: Copy
Public Sub HtmlToPDF (Html As Object) As PyWrapper
    Dim Code As String = $"
import fpdf

def HtmlToPDF (Html):
    pdf = fpdf.FPDF()
    pdf.add_page()
    pdf.write_html(Html)
    return pdf.output()
"$
    Return Py.Utils.RunCode("HtmlToPDF", Array(Html), Code)
End Sub

And usage:
Code: Copy
Wait For (HtmlToPDF(TextArea1.Text).Fetch) Complete (Result As PyWrapper) 'notice the call to Fetch.
It returns the PDF document as an array of bytes.

Now for the distribution.
First, you can try it yourself: https://www.b4x.com/b4j/files/PyPDF_Example.exe

The installer size is 47MB and it is a standalone package - it includes both the Java runtime and the Python runtime. Overall not too bad.

Steps to build it:

1. Download the "dot" package: https://github.com/winpython/winpython/releases/tag/11.2.20241228final
2. Unzip it in the project folder in a folder.

View attachment 161379

Install the dependencies needed. Use WinPython Command Prompt.exe to run pip.

The bridge is started with:
Code: Copy
Py.Start(Py.CreateOptions(File.Combine(File.DirApp, "..\Python\Python\python.exe")))

And this custom build action will copy Python to the standalone package (it also excludes a few folders):
Code: Copy
#CustomBuildAction: after packager, %WINDIR%\System32\robocopy.exe, ..\Python temp\build\python /E /XD __pycache__ Doc tcl pip setuptools

The project is attached.
PyBridge v0.3 is also attached. Note that it depends on two updated internal libraries: https://www.b4x.com/android/forum/threads/updates-to-internal-libaries.48274/post-1014241
There were many internal improvements. Note that the Connected event now includes a "Success" parameter:
Code: Copy
Wait For Py_Connected (Success As Boolean)
Wow... if it includes into it the java runtime + python runtime.... the size is very good...

I thought will had a size of 2gb (winpython+our runtime)

I think that...this is a new ERA of B4X !

BACKEND scripts without need to recompile..
 
There are some very large libraries such as torch + cuda, that will increase the size significantly. For such libraries, and especially with cuda, it makes sense to make the python path configurable and rely upon an existing installation. For many other libraries, the solution described above will be good enough.
 
PyComm line 18:Caused by: java.lang.RuntimeException: Field: correctedClasses not found in: anywheresoftware.b4a.randomaccessfile.RandomAccessFile
 
Back
Top