MarkDown (MD) Viewer

Started by Nicola, Yesterday at 08:41:31 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Nicola

Hi, 
I'm trying to program a Markdown viewer. I would like one that's light and simple. I thought about using an RTF conversion to use its viewer. It's not bad. However, I think I will try using an HTML viewer with HTML conversions of the MD file... 

Best regards.


' MD VIEWER by Nicola Piano (12-2025)
'====================================
$ filename "md_viewer.exe"
uses WinUtil
uses FileDialogs
uses ParseUtil

' --- CARICAMENTO LIBRERIE ---
sys hRichLib = LoadLibrary("msftedit.dll")
if hRichLib = 0 then hRichLib = LoadLibrary("riched20.dll")

% ID_RICHEDIT = 101
% ID_MENU_OPEN = 201
sys hRichEdit, hFont

type SETTEXTEX
    uint flags
    uint codepage
end type
% EM_SETTEXTEX = 0x0461
% ST_RTF       = 2
% CP_ACP       = 0


function UTF8ToAnsi(string s) as string
    sys L = len(s)
    if L = 0 then return ""
   
    ' 1. Calcoliamo quanti caratteri Wide servono
    sys wLen = MultiByteToWideChar(65001, 0, strptr(s), L, 0, 0)
   
    ' Allocazione buffer Wide (2 byte per carattere)
    string ws = space(wLen * 2)
    MultiByteToWideChar(65001, 0, strptr(s), L, strptr(ws), wLen)
   
    ' 2. Calcoliamo quanto spazio ANSI serve
    sys aLen = WideCharToMultiByte(0, 0, strptr(ws), wLen, 0, 0, 0, 0)
    string res = space(aLen)
    WideCharToMultiByte(0, 0, strptr(ws), wLen, strptr(res), aLen, 0, 0)
   
    return res
end function

' --- Funzione Parser Markdown ---
function MD_to_RTF(string md) as string
    ' 0:nero, 1:blu, 4:verde, 5:bordeaux
    string rtf = "{\rtf1\ansi\deff0{\fonttbl{\f0 Segoe UI;}{\f1 Courier New;}}"
    rtf += "{\colortbl;\red0\green0\blue255;\red255\green0\blue0;\red100\green100\blue100;\red0\green128\blue0;\red128\green0\blue64;}\f0\fs24 "
   
    string ToC = "\b\fs28 INDICE\b0\fs24\line\line "
    string contentRTF = ""
    string curLine
    sys pos = 1, nextPos = 0
    bool inCode = false

    do
        nextPos = instr(pos, md, chr(10))
        if nextPos > 0 then
            curLine = mid(md, pos, nextPos - pos)
            pos = nextPos + 1
        else
            curLine = mid(md, pos)
            pos = 0
        end if
       
        if instr(curLine, chr(13)) > 0 then replace(curLine, chr(13), "")
       
        ' 1. Protezione RTF
        replace(curLine, "\", "\\")
        replace(curLine, "{", "\{")
        replace(curLine, "}", "\}")

        ' 2. Pulizia Link [testo](#anchor) -> testo
        ' Fondamentale per file come Variables.md per ripristinare la leggibilità
        do while instr(curLine, "[") > 0 and instr(curLine, "](#") > 0
            sys p1 = instr(curLine, "[")
            sys p2 = instr(p1, curLine, "]")
            sys p3 = instr(p2, curLine, ")")
            if p2 > p1 and p3 > p2 then
                string linkTxt = mid(curLine, p1 + 1, p2 - p1 - 1)
                curLine = left(curLine, p1 - 1) + linkTxt + mid(curLine, p3 + 1)
            else
                exit do
            end if
        loop

        ' 3. Gestione Titoli (Flessibile: cerca il cancelletto anche dopo simboli speciali)
        string tLine = ltrim(curLine)
        ' Cerchiamo la posizione del primo #
        sys hashPos = instr(tLine, "#")
        if hashPos > 0 and hashPos < 5 then ' Se il cancelletto è all'inizio (max 4 spazi/simboli)
            if instr(tLine, "# ") > 0 then
                ToC += "\cf1\b\fs32 " + tLine + "\b0\fs24\cf0\line "
                curLine = "\b\cf1\fs32 " + curLine + "\fs24\cf0\b0"
            elseif instr(tLine, "## ") > 0 then
                ToC += "\cf1\b\fs28 " + tLine + "\b0\fs24\cf0\line "
                curLine = "\b\cf1\fs28 " + curLine + "\fs24\cf0\b0"
            elseif instr(tLine, "### ") > 0 then
                ToC += "\cf4\b\fs24 " + tLine + "\b0\cf0\line "
                curLine = "\b\cf4 " + curLine + "\cf0\b0"
            end if
        end if

        ' 4. Blocchi Codice
        if left(ltrim(curLine), 3) = "```" then
            inCode = not inCode
            if inCode then contentRTF += "\line\f1\fs20 " else contentRTF += "\f0\fs24\line "
            continue do
        end if

        if inCode = false then
            ' 5. Grassetto (Logica a prova di simboli speciali)
            sys b1 = 1
            do
                b1 = instr(b1, curLine, "**")
                if b1 > 0 then
                    sys b2 = instr(b1 + 2, curLine, "**")
                    if b2 > b1 then
                        string boldTxt = mid(curLine, b1 + 2, b2 - b1 - 2)
                        string rep = "\b " + boldTxt + "\b0 "
                        curLine = left(curLine, b1 - 1) + rep + mid(curLine, b2 + 2)
                        b1 += len(rep)
                    else
                        exit do
                    end if
                else
                    exit do
                end if
            loop

            ' 6. Tabelle (Pipe |)
            if instr(curLine, "|") > 0 and instr(curLine, "---") = 0 then
                replace(curLine, "|", "\tab ")
                curLine = "\li400 " + curLine
            end if

            contentRTF += curLine + "\line "
        else
            contentRTF += "   " + curLine + "\line "
        end if
       
        if pos = 0 then exit do
    loop
   
    return rtf + ToC + "\line\emdash\line\line " + contentRTF + "}"
end function

' --- Procedura Caricamento ---
sub LoadMD(sys hEdit, string f)
    if f = "" then exit sub
   
    ' --- 1. SVUOTIAMO LA GUI ---
    SendMessage(hEdit, WM_SETTEXT, 0, strptr(""))
   
    string raw = getfile(f)
    if len(raw) = 0 then exit sub
   
    ' --- 2. CORREZIONE CARATTERI (UTF-8 a ANSI) ---
    ' Applichiamo la conversione prima di passarlo al parser
    'raw = UTF8ToAnsi(raw)
   
    static string sRTF
    sRTF = MD_to_RTF(raw)
   
    dim st as SETTEXTEX
    st.flags = ST_RTF
    st.codepage = 0
   
    SendMessage(hEdit, EM_SETTEXTEX, @st, strptr(sRTF))
    SetWindowText(GetParent(hEdit), "O2 Viewer - " + f)
end sub

static string sRTF

' --- WndProc ---
function WndProc(sys hwnd, uMsg, wParam, lParam) as long callback
    select umsg
        case WM_CREATE
            ' Menu
            sys hMenu = CreateMenu()
            sys hSub = CreatePopupMenu()
            AppendMenu(hSub, MF_STRING, ID_MENU_OPEN, "&Apri...")
            AppendMenu(hMenu, MF_POPUP, hSub, "&File")
            SetMenu(hwnd, hMenu)

            ' Creazione RichEdit
            hRichEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "RichEdit50W", "", _
                        WS_CHILD | WS_VISIBLE | ES_MULTILINE | WS_VSCROLL | 0x0800, _
                        0, 0, 0, 0, hwnd, ID_RICHEDIT, hinst, null)
           
            hFont = CreateFont(20, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 0, "Segoe UI")
            SendMessage(hRichEdit, WM_SETFONT, hFont, 1)

  if hRichEdit <> 0 then
                string testo = "# Ciao Charles" + chr(10) + "O2 is GREAT"
                static string sRTF
                sRTF = MD_to_RTF(testo)
               
                 'usa EM_SETTEXTEX:
                 dim st as SETTEXTEX
                 st.flags = ST_RTF
                 st.codepage = 0
                 SendMessage(hRichEdit, EM_SETTEXTEX, @st, strptr(sRTF))
               
                SetWindowText(hwnd, "O2 Viewer - Pronto")
            end if

        case WM_COMMAND
            if loword(wParam) = ID_MENU_OPEN then
                ' Filtro corretto per FileDialogs.inc [cite: 9]
                string filt = "Markdown" + chr(0) + "*.md" + chr(0) + "All" + chr(0) + "*.*" + chr(0)
                string f = GetFileName("", 0, filt)
'mbox f
                if f <> "" then LoadMD(hRichEdit, f)
            end if

        case WM_SIZE
            if hRichEdit then
                RECT rc : GetClientRect(hwnd, &rc)
                MoveWindow(hRichEdit, 5, 5, rc.right-10, rc.bottom-10, TRUE)
            end if

        case WM_DESTROY
            if hRichLib then FreeLibrary(hRichLib)
            PostQuitMessage 0

        case else
            return DefWindowProc(hwnd, uMsg, wParam, lParam)
    end select
end function

MainWindow 800, 600, WS_OVERLAPPEDWINDOW

Charles Pegge