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:
+
+
+
+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 0000000..c7122f3
Binary files /dev/null and b/test_local.png differ