From 0cc9b10280c0bb06fe82c4c8a880e5b45ca26130 Mon Sep 17 00:00:00 2001 From: xrmb Date: Sat, 4 Jul 2026 23:40:00 -0400 Subject: [PATCH] Use system temp directory for rendered HTML temp file Previously MDView wrote the intermediate HTML file next to the source .md file, cluttering the markdown directory. Now it uses the system temp directory and injects a tag so relative local images and links still resolve against the markdown directory. Adds test_local.png and a local image test section to test.md to verify local image loading still works with the temp file moved away from the source directory. Updates README.md to describe the new behavior. --- README.md | 2 +- mdview.c | 132 ++++++++++++++++++++++++++++++++++++------------- test.md | 9 ++++ test_local.png | Bin 0 -> 383 bytes 4 files changed, 108 insertions(+), 35 deletions(-) create mode 100644 test_local.png diff --git a/README.md b/README.md index ddf2b7f..2c72d29 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ The `WLXHarness/` directory contains a standalone test harness (contributed by N ## How It Works -MDView is a WLX lister plugin — a DLL that Total Commander loads when you press F3 on a matching file type. It contains a built-in Markdown-to-HTML converter and embeds an MSHTML (IE11) WebBrowser control to render the output. The rendered HTML is written to a temporary file in the same directory as the source `.md` file, allowing local relative image paths to resolve correctly via the Local Machine security zone. The split source view uses a Windows RichEdit control with synchronised ratio-based scrolling. Keyboard input is handled by subclassing the browser's internal window and (when active) the RichEdit control. Settings are persisted via TC's standard INI file mechanism. +MDView is a WLX lister plugin — a DLL that Total Commander loads when you press F3 on a matching file type. It contains a built-in Markdown-to-HTML converter and embeds an MSHTML (IE11) WebBrowser control to render the output. The rendered HTML is written to a temporary file in the system temp directory, and a `` tag is added so that local relative image paths still resolve correctly against the source `.md` directory. The split source view uses a Windows RichEdit control with synchronised ratio-based scrolling. Keyboard input is handled by subclassing the browser's internal window and (when active) the RichEdit control. Settings are persisted via TC's standard INI file mechanism. ## Credits diff --git a/mdview.c b/mdview.c index 23f6a9a..a0781b5 100644 --- a/mdview.c +++ b/mdview.c @@ -1580,44 +1580,108 @@ static HRESULT create_browser(HWND hwnd, IWebBrowser2** ppB, IOleObject** ppO, S *ppB=pB; *ppO=pO; return S_OK; } +/* Build a tag pointing to the source markdown directory so that + relative image/link URLs still resolve after the temp file is moved. */ +static char* dir_to_base_tag(const WCHAR* dir) { + if (!dir || !dir[0]) return NULL; + int len = WideCharToMultiByte(CP_UTF8, 0, dir, -1, NULL, 0, NULL, NULL); + if (len <= 0) return NULL; + char* path = (char*)malloc((size_t)len); + if (!path) return NULL; + WideCharToMultiByte(CP_UTF8, 0, dir, -1, path, len, NULL, NULL); + size_t pl = strlen(path); + int need_slash = (pl == 0 || (path[pl-1] != '\\' && path[pl-1] != '/')); + size_t tag_size = pl + 64 + (need_slash ? 1 : 0); + char* tag = (char*)malloc(tag_size); + if (!tag) { free(path); return NULL; } + for (size_t i = 0; i < pl; i++) { + if (path[i] == '\\') path[i] = '/'; + } + snprintf(tag, tag_size, "", path, need_slash ? "/" : ""); + free(path); + return tag; +} + +/* Insert a string immediately before the first tag. */ +static char* insert_before_head_end(const char* html, const char* insert) { + const char* head_end = strstr(html, ""); + if (!head_end || !insert || !insert[0]) { + size_t len = strlen(html); + char* copy = (char*)malloc(len + 1); + if (copy) memcpy(copy, html, len + 1); + return copy; + } + size_t before = (size_t)(head_end - html); + size_t inslen = strlen(insert); + size_t after = strlen(head_end); + char* out = (char*)malloc(before + inslen + after + 1); + if (!out) { + size_t len = strlen(html); + char* copy = (char*)malloc(len + 1); + if (copy) memcpy(copy, html, len + 1); + return copy; + } + memcpy(out, html, before); + memcpy(out + before, insert, inslen); + memcpy(out + before + inslen, head_end, after + 1); + return out; +} + static void navigate_to_html(IWebBrowser2* pB, const char* html, const WCHAR* dir, WCHAR* outTempPath) { outTempPath[0] = 0; - /* Write HTML to a temp file in the same directory as the .md file. - This makes MSHTML load in the Local Machine zone so file:// images work. */ - WCHAR tempPath[MAX_PATH]; - wcscpy(tempPath, dir); - /* Append a unique temp filename */ - WCHAR tempName[64]; - wsprintfW(tempName, L"_mdview_%08x.html", GetTickCount()); - wcscat(tempPath, tempName); - - /* Write UTF-8 HTML with BOM and Mark of the Web */ - FILE* tf = _wfopen(tempPath, L"wb"); - if (tf) { - /* UTF-8 BOM */ - fputc(0xEF, tf); fputc(0xBB, tf); fputc(0xBF, tf); - /* Mark of the Web — tells MSHTML to allow script execution in local files */ - fprintf(tf, "\r\n"); - fwrite(html, 1, strlen(html), tf); - fclose(tf); - wcscpy(outTempPath, tempPath); - - /* Navigate to the temp file */ - VARIANT ve; VariantInit(&ve); - BSTR url = SysAllocString(tempPath); - IWebBrowser2_Navigate(pB, url, &ve, &ve, &ve, &ve); - SysFreeString(url); - - /* Wait for load */ - READYSTATE rs; int to = 200; - do { MSG msg; while(PeekMessageW(&msg,NULL,0,0,PM_REMOVE)){TranslateMessage(&msg);DispatchMessageW(&msg);} - IWebBrowser2_get_ReadyState(pB,&rs); - if(rs!=READYSTATE_COMPLETE) Sleep(10); - } while(rs!=READYSTATE_COMPLETE && --to>0); - return; + /* Add a tag so relative URLs resolve against the markdown directory. */ + char* base_tag = dir_to_base_tag(dir); + char* final_html = insert_before_head_end(html, base_tag ? base_tag : ""); + if (base_tag) free(base_tag); + + /* Build a unique temp file path in the system temp directory. */ + WCHAR tempPath[MAX_PATH] = {0}; + int have_temp = 0; + DWORD gp = GetTempPathW(MAX_PATH, tempPath); + if (gp && gp <= MAX_PATH) { + size_t tlen = wcslen(tempPath); + if (tlen > 0 && tempPath[tlen-1] != L'\\' && tempPath[tlen-1] != L'/') { + if (tlen < MAX_PATH - 1) { wcscat(tempPath, L"\\"); tlen++; } + } + WCHAR tempName[64]; + wsprintfW(tempName, L"_mdview_%08x_%u.html", GetTickCount(), GetCurrentProcessId()); + if (tlen + wcslen(tempName) < MAX_PATH) { + wcscat(tempPath, tempName); + have_temp = 1; + } } + if (have_temp) { + FILE* tf = _wfopen(tempPath, L"wb"); + if (tf) { + /* UTF-8 BOM */ + fputc(0xEF, tf); fputc(0xBB, tf); fputc(0xBF, tf); + /* Mark of the Web — tells MSHTML to allow script execution in local files */ + fprintf(tf, "\r\n"); + fwrite(final_html, 1, strlen(final_html), tf); + fclose(tf); + free(final_html); + wcscpy(outTempPath, tempPath); + + /* Navigate to the temp file */ + VARIANT ve; VariantInit(&ve); + BSTR url = SysAllocString(tempPath); + IWebBrowser2_Navigate(pB, url, &ve, &ve, &ve, &ve); + SysFreeString(url); + + /* Wait for load */ + READYSTATE rs; int to = 200; + do { MSG msg; while(PeekMessageW(&msg,NULL,0,0,PM_REMOVE)){TranslateMessage(&msg);DispatchMessageW(&msg);} + IWebBrowser2_get_ReadyState(pB,&rs); + if(rs!=READYSTATE_COMPLETE) Sleep(10); + } while(rs!=READYSTATE_COMPLETE && --to>0); + return; + } + } + + free(final_html); + /* Fallback: about:blank + document.write (no local image support) */ VARIANT ve; VariantInit(&ve); BSTR url=SysAllocString(L"about:blank"); @@ -1798,7 +1862,7 @@ __declspec(dllexport) HWND __stdcall ListLoadW(HWND pw, WCHAR* file, int flags) layout_views(data); IWebBrowser2_put_Silent(data->pBrowser, VARIANT_TRUE); - /* Extract directory from file path for temp file placement */ + /* Extract directory from file path for the tag */ WCHAR fileDir[MAX_PATH]; wcsncpy(fileDir, file, MAX_PATH); fileDir[MAX_PATH-1]=0; WCHAR* lastSep = wcsrchr(fileDir, L'\\'); diff --git a/test.md b/test.md index 2e682bf..607c6df 100644 --- a/test.md +++ b/test.md @@ -261,9 +261,18 @@ This is a reference-style image: ![Placeholder][sample-img] +This is a local image loaded from the same directory as this markdown file. It should render correctly even though the temp HTML file is created in the system temp directory, because a `` tag points back at this directory: + +![Local image](test_local.png) + +This is a reference-style local image: + +![Local reference image][local-img] + This is an implicit reference link to [Google]. [md-guide]: https://www.markdownguide.org "Official Markdown Guide" [sample-img]: https://placehold.co/200x100.png "A test placeholder" +[local-img]: test_local.png "Local test image" [Google]: https://www.google.com "Google Search" diff --git a/test_local.png b/test_local.png new file mode 100644 index 0000000000000000000000000000000000000000..c7122f363ebf8bdfb24cbad067ba95064804f64b GIT binary patch literal 383 zcmeAS@N?(olHy`uVBq!ia0vp^CxAGGg9%9bJb4krz`*F{>EaktG3V_$MotC;fg>9} v=T{kj4A5xhIB{>