Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions TeXmacs/plugins/goldfish/src/goldfish.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2473,6 +2473,28 @@ static s7_pointer
f_os_temp_dir (s7_scheme* sc, s7_pointer args) {
tb_char_t path[GOLDFISH_PATH_MAXN];
tb_directory_temporary (path, GOLDFISH_PATH_MAXN);
#ifdef TB_CONFIG_OS_WINDOWS
tb_wchar_t path_w[GOLDFISH_PATH_MAXN] = {0};
tb_wchar_t path_long_w[GOLDFISH_PATH_MAXN]= {0};
if (tb_atow (path_w, path, GOLDFISH_PATH_MAXN) != -1) {
tb_size_t path_len= tb_wcslen (path_w);
tb_bool_t had_sep = path_len > 0
&& (path_w[path_len - 1] == L'\\' || path_w[path_len - 1] == L'/');
if (had_sep) path_w[path_len - 1]= L'\0';

DWORD long_len= GetLongPathNameW (path_w, path_long_w, GOLDFISH_PATH_MAXN);
if (long_len > 0 && long_len < GOLDFISH_PATH_MAXN) {
if (had_sep) {
path_long_w[long_len++] = L'\\';
path_long_w[long_len] = L'\0';
}

tb_char_t path_long[GOLDFISH_PATH_MAXN];
tb_size_t size= tb_wtoa (path_long, path_long_w, GOLDFISH_PATH_MAXN);
if (size != -1 && size > 0) return s7_make_string (sc, path_long);
}
}
#endif
return s7_make_string (sc, path);
}

Expand Down
30 changes: 19 additions & 11 deletions TeXmacs/plugins/tikz/goldfish/tm-tikz.scm
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
(define (goldfish-quote s)
(string-append "\"" (escape-string s) "\""))

(define (windows-quote s)
(string-append "\"" s "\""))

(define (tikz-welcome)
(flush-prompt "tikz] ")
(flush-verbatim "Liii STEM interface to TikZ"))
Expand All @@ -35,10 +38,10 @@
(read-code ""))

(define (gen-temp-path)
(let ((tikz-tmpdir (string-append (os-temp-dir) "/tikz")))
(let ((tikz-tmpdir (string-append (os-temp-dir) (string (os-sep)) "tikz")))
(when (not (file-exists? tikz-tmpdir))
(mkdir tikz-tmpdir))
(string-append tikz-tmpdir "/" (uuid4))))
(string-append tikz-tmpdir (string (os-sep)) (uuid4))))

(define (wrap-tikz-code code)
(let ((trimmed (string-trim-left code)))
Expand Down Expand Up @@ -109,19 +112,24 @@
(lambda () (display code))))

(define (tikz-temp-dir)
(string-append (os-temp-dir) "/tikz"))
(string-append (os-temp-dir) (string (os-sep)) "tikz"))

(define (run-pdflatex tex-path pdflatex-bin)
(let* ((inner-cmd (string-append (goldfish-quote pdflatex-bin)
(let* ((redirect (if (os-windows?) " > NUL 2>&1" ""))
(quote-fn (if (os-windows?) windows-quote goldfish-quote))
(inner-cmd (string-append (quote-fn pdflatex-bin)
" --interaction=errorstopmode -halt-on-error "
(goldfish-quote tex-path)
" > /dev/null 2>&1"))
(cmd (string-append "sh -c " (goldfish-quote inner-cmd)))
(quote-fn tex-path)
redirect))
(cmd (if (os-windows?)
(string-append "cmd.exe /c call " inner-cmd)
(string-append "sh -c " (goldfish-quote inner-cmd))))
(orig-dir (getcwd)))
(unsetenv "DYLD_LIBRARY_PATH")
(unsetenv "DYLD_FRAMEWORK_PATH")
(unsetenv "DYLD_FALLBACK_LIBRARY_PATH")
(unsetenv "DYLD_FALLBACK_FRAMEWORK_PATH")
(when (os-macos?)
(unsetenv "DYLD_LIBRARY_PATH")
(unsetenv "DYLD_FRAMEWORK_PATH")
(unsetenv "DYLD_FALLBACK_LIBRARY_PATH")
(unsetenv "DYLD_FALLBACK_FRAMEWORK_PATH"))
(chdir (tikz-temp-dir))
(let ((result (os-call cmd)))
(chdir orig-dir)
Expand Down
23 changes: 16 additions & 7 deletions TeXmacs/plugins/tikz/tests/tm-tikz-test.scm
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
(liii string)
(liii list)
(liii path)
(liii os)
)

; Simulate the wrap-tikz-code logic from tm-tikz.scm
Expand Down Expand Up @@ -210,6 +211,14 @@
(check (parse-magic-line "% -width 0.8par -height") => (list "0.8par" "0px"))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(check
(if (and (os-windows?) (string-contains? (getenv "TEMP" "") "~"))
(not (string-contains? (os-temp-dir) "~"))
#t)
=>
#t)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; image-valid? helper
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Expand All @@ -220,13 +229,13 @@
;; Tests for image-valid?
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define test-empty-path "/tmp/tikz-test-empty.txt")
(define test-empty-path (string-append (os-temp-dir) (string (os-sep)) "tikz-test-empty.txt"))

(with-output-to-file test-empty-path
(lambda ()
(display "")))

(check (image-valid? "/tmp/nonexistent-file-12345.txt") => #f)
(check (image-valid? (string-append (os-temp-dir) (string (os-sep)) "nonexistent-file-12345.txt")) => #f)
(check (image-valid? test-empty-path) => #f)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Expand Down Expand Up @@ -265,11 +274,11 @@
;; Tests for pdf-page-empty?
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define test-log-empty-path "/tmp/tikz-test-log-empty.log")
(define test-log-valid-path "/tmp/tikz-test-log-valid.log")
(define test-log-vertical-path "/tmp/tikz-test-log-vertical.log")
(define test-log-horizontal-path "/tmp/tikz-test-log-horizontal.log")
(define test-log-point-path "/tmp/tikz-test-log-point.log")
(define test-log-empty-path (string-append (os-temp-dir) (string (os-sep)) "tikz-test-log-empty.log"))
(define test-log-valid-path (string-append (os-temp-dir) (string (os-sep)) "tikz-test-log-valid.log"))
(define test-log-vertical-path (string-append (os-temp-dir) (string (os-sep)) "tikz-test-log-vertical.log"))
(define test-log-horizontal-path (string-append (os-temp-dir) (string (os-sep)) "tikz-test-log-horizontal.log"))
(define test-log-point-path (string-append (os-temp-dir) (string (os-sep)) "tikz-test-log-point.log"))

(with-output-to-file test-log-empty-path
(lambda ()
Expand Down
110 changes: 110 additions & 0 deletions devel/0309.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# [0309] 修复 Windows 下 TikZ 图片无法回传到文档

## 1 相关文档
- [dddd.md](dddd.md) - 任务文档模板

## 2 任务相关的代码文件
- `TeXmacs/plugins/goldfish/src/goldfish.hpp`
- `TeXmacs/plugins/tikz/goldfish/tm-tikz.scm`
- `TeXmacs/plugins/tikz/tests/tm-tikz-test.scm`
- `tests/System/Files/input_file_flush_test.cpp`

## 3 如何测试

### 3.1 确定性测试(单元测试)
```bash
xmake b goldfish
xmake b input_file_flush_test
xmake r input_file_flush_test
TeXmacs/plugins/goldfish/bin/goldfish.exe TeXmacs/plugins/tikz/tests/tm-tikz-test.scm
```

### 3.2 非确定性测试(文档验证)
```bash
xmake b stem
xmake r stem
```

在 Windows + MiKTeX 环境中验证:

1. 打开 TikZ 插件,输入一段不含中文的 TikZ 代码,确认图片能正常插入当前文档。
2. 若系统 `TEMP`/`TMP` 环境变量为 `C:\Users\NAME~1\AppData\Local\Temp` 这类 8.3 短路径,确认插图流程仍然成功。
3. 执行以下命令,确认 `os-temp-dir` 返回值不再包含 `~`:

```bash
TeXmacs/plugins/goldfish/bin/goldfish.exe eval "(begin (import (liii os)) (display (os-temp-dir)) (newline))"
```

## 4 如何提交

提交前执行以下最少步骤:

```bash
xmake b goldfish
xmake b input_file_flush_test
xmake r input_file_flush_test
TeXmacs/plugins/goldfish/bin/goldfish.exe TeXmacs/plugins/tikz/tests/tm-tikz-test.scm
```

## 5 What

修复 Windows 下 TikZ 插件生成 PDF 后无法把图片返回到文档的问题,根因是临时目录路径使用了 8.3 短路径表示,导致 `pdflatex` 无法正确打开输入文件。

1. 在 Goldfish 层规范化 Windows 临时目录,避免返回带 `~` 的 8.3 短路径。
2. 调整 TikZ 插件在 Windows 下的路径拼接和 `pdflatex` 调用方式。
3. 新增 `file:` 协议回归测试,确认 host 侧可以把 PDF 文件转换成内部 `IMAGE` 节点。
4. 补充 TikZ Scheme 测试,覆盖 Windows 下 `TEMP` 含 `~` 的场景。

## 6 Why

TikZ 插件的返回链路是:

1. Goldfish 生成临时 `.tex`
2. 调用外部 `pdflatex` 生成 `.pdf`
3. 通过 `flush-file` 发送 `file:<path>?width=...&height=...`
4. host 侧 `file_flush` 读取 PDF 字节并构造内部 `IMAGE`

实际排查发现,host 侧的 `file:` 协议解析在 Windows 上对正常绝对路径是可用的,问题出在更前面:

- Windows 上 `os-temp-dir` 可能返回 `C:\Users\LUOYIN~1\AppData\Local\Temp` 这种 8.3 短路径。
- TikZ 插件把该路径拼进 `pdflatex` 命令行后,MiKTeX 会把 `~` 前的路径片段错误识别为文件名,导致 `.pdf` 根本没有生成。
- 既然没有 PDF 产物,后续 `flush-file -> file_flush -> IMAGE` 这条回传链路自然也不会发生。

因此修复重点不是 `file_flush`,而是保证 TikZ 插件传给 `pdflatex` 的临时路径在 Windows 上是稳定可读的长路径。

## 7 How

### 7.1 Goldfish 层修复 temp 路径

在 `g_os-temp-dir` 中保留原有“取系统 temp 目录”的语义,但在 Windows 分支额外做一次规范化:

- 先调用 `tb_directory_temporary()` 取 temp 路径。
- 将结果转成 `WCHAR`。
- 使用 `GetLongPathNameW()` 把 `C:\Users\NAME~1\...` 展开为长路径。
- 若转换成功,再转回 UTF-8 字符串返回给 Scheme;失败则回退到原值。

这样不会改变 temp 目录的选择逻辑,只是修正其字符串表示。

### 7.2 TikZ 插件适配 Windows 命令行

在 `tm-tikz.scm` 中同步做了两类整理:

- 临时路径拼接从硬编码 `/` 改为 `os-sep`,确保 Windows 下生成 `...\tikz\UUID.tex`。
- `run-pdflatex` 在 Windows 下改为:
- 使用简单的双引号包装路径和可执行文件
- 使用 `cmd.exe /c call ... > NUL 2>&1`

Linux/macOS 仍保留原来的 `sh -c` 分支。

### 7.3 测试策略

本任务分成两层验证:

- `input_file_flush_test.cpp`:直接向 `texmacs_input` 注入 `file:` 协议,确认 Windows host 侧对正常 PDF 路径能构造出 `IMAGE`,从而排除 `file_flush` 本身的问题。
- `tm-tikz-test.scm`:在 Scheme 层检查 `os-temp-dir` 在 Windows + `TEMP` 含 `~` 时不再返回短路径,并把原先写死 `/tmp` 的辅助测试改成跨平台 temp 路径。

### 7.4 当前范围

本任务修复的是 Windows 下 temp 目录 8.3 短路径导致的 TikZ 失败问题。

它不覆盖另一个更大的 Windows Unicode 路径问题:当前 `g_os-call` 仍通过 `std::system` 执行命令,因此“中文用户名/中文路径是否完全可用”不在本次修复范围内。
65 changes: 65 additions & 0 deletions tests/System/Files/input_file_flush_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/******************************************************************************
* MODULE : input_file_flush_test.cpp
* COPYRIGHT : (C) 2026 OpenAI
*******************************************************************************
* This software falls under the GNU general public license version 3 or later.
* It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
* in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
******************************************************************************/

#include "Generic/input.hpp"
#include "analyze.hpp"
#include "base.hpp"
#include "file.hpp"
#include "tm_link.hpp"
#include "tm_url.hpp"
#include "tree.hpp"
#include "tree_helper.hpp"
#include "url.hpp"
#include <QtTest/QtTest>

class TestInputFileFlush : public QObject {
Q_OBJECT

private slots:
void init () { init_lolly (); }
void test_file_protocol_loads_pdf_as_image ();
};

void
TestInputFileFlush::test_file_protocol_loads_pdf_as_image () {
url pdf_url=
resolve (url_system ("$TEXMACS_PATH/tests/PDF/pdf_1_4_sample.pdf"), "r");
QVERIFY (exists (pdf_url));

string pdf_path = concretize (pdf_url);
string pdf_bytes= string_load (pdf_url);
url reparsed = url_system (pdf_path);
QVERIFY (exists (reparsed));
qcompare (suffix (reparsed), "pdf");

string protocol_s;
protocol_s << DATA_BEGIN << "file:" << pdf_path << "?width=0.3par&height=0px"
<< DATA_END;

texmacs_input in ("output");
for (int i= 0; i < N (protocol_s); ++i)
(void) in->put (protocol_s[i]);

tree doc= in->get ("output");
QVERIFY (is_document (doc));
QCOMPARE (N (doc), 1);

tree image= doc[0];
QVERIFY (is_func (image, moebius::IMAGE, 5));
QVERIFY (is_func (image[0], moebius::TUPLE, 2));
QVERIFY (is_func (image[0][0], moebius::RAW_DATA, 1));

qcompare (as_string (image[0][1]), "pdf");
qcompare (as_string (image[1]), "0.3par");
qcompare (as_string (image[2]), "");
QCOMPARE (N (as_string (image[0][0][0])), N (pdf_bytes));
}

QTEST_MAIN (TestInputFileFlush)
#include "input_file_flush_test.moc"
Loading