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:
B4X:
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.
 

Magma

Expert
Licensed User
Longtime User
View attachment 161177

Code that downloads a markdown string from github and then converts it to html and shows it with WebView:
B4X:
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...

🎉
 

aeric

Expert
Licensed User
Longtime User
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
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
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:
B4X:
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:
B4X:
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

  • EasyOCR.zip
    10.5 KB · Views: 75
  • PyBridge.b4xlib
    13.7 KB · Views: 69

omo

Active Member
Licensed User
Longtime User
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).
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
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:
B4X:
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

  • PyBridge.b4xlib
    16.4 KB · Views: 45
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User
Note that this code also works:
B4X:
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:
B4X:
Wait For (r.Fetch) Complete (r As PyWrapper)
Dim x As Int = r.Value
Log(x) '60
 

Daestrum

Expert
Licensed User
Longtime User
Is there a way to re-run code or do we have to pass the python source each time?
 

Magma

Expert
Licensed User
Longtime User
@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...
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
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.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
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:
B4X:
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:
B4X:
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:
B4X:
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):
B4X:
#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:
B4X:
Wait For Py_Connected (Success As Boolean)
 

Attachments

  • PyFPDF.zip
    16.7 KB · Views: 57
  • PyBridge.b4xlib
    16.5 KB · Views: 47
Last edited:

Magma

Expert
Licensed User
Longtime User
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:
B4X:
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:
B4X:
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:
B4X:
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):
B4X:
#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:
B4X:
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..
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
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.
 

jkhazraji

Active Member
Licensed User
Longtime User
PyComm line 18:Caused by: java.lang.RuntimeException: Field: correctedClasses not found in: anywheresoftware.b4a.randomaccessfile.RandomAccessFile
 

teddybear

Well-Known Member
Licensed User

jkhazraji

Active Member
Licensed User
Longtime User
You missed this post#15
I downloaded the two libraries , added them refreshed the libraries but I had the same error. Am I missing something else?
1738701037971.png
 
Top