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
7 changes: 6 additions & 1 deletion TeXmacs/plugins/image/progs/image/gif.scm
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(texmacs-module (image gif))
(texmacs-module (image gif)
(:use (binary convert)))

(converter gif-file postscript-document
(:function image->psdoc))

(converter gif-file postscript-file
(:require (has-binary-convert?))
(:shell ,(url->system (find-binary-convert)) from to))
7 changes: 6 additions & 1 deletion TeXmacs/plugins/image/progs/image/jpeg.scm
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(texmacs-module (image jpeg))
(texmacs-module (image jpeg)
(:use (binary convert)))

(converter jpeg-file postscript-document
(:function image->psdoc))

(converter jpeg-file postscript-file
(:require (has-binary-convert?))
(:shell ,(url->system (find-binary-convert)) from to))
7 changes: 6 additions & 1 deletion TeXmacs/plugins/image/progs/image/tif.scm
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(texmacs-module (image tif))
(texmacs-module (image tif)
(:use (binary convert)))

(converter tif-file postscript-document
(:function image->psdoc))

(converter tif-file postscript-file
(:require (has-binary-convert?))
(:shell ,(url->system (find-binary-convert)) from to))
60 changes: 51 additions & 9 deletions TeXmacs/plugins/latex/progs/convert/latex/tmtex.scm
Original file line number Diff line number Diff line change
Expand Up @@ -1827,22 +1827,64 @@
(define (tmtex-graphics l)
(tmtex-eps (cons 'graphics l)))

(define (tmtex-guess-format u suffix)
"Detect image format via magic header when suffix is unknown."
(if (and (url-exists? u) (== (format-from-suffix suffix) "generic"))
(with data (string-load u)
(cond ((and (> (string-length data) 8)
(== (char->integer (string-ref data 0)) #xff)
(== (char->integer (string-ref data 1)) #xd8))
"jpeg")
((and (> (string-length data) 8)
(string-starts? data "\x89PNG"))
"png")
((and (> (string-length data) 5)
(string-starts? data "%PDF-"))
"pdf")
(else #f)))
#f))
Comment on lines +1830 to +1845
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tmtex-guess-format uses string-load which loads the entire file into memory just to inspect a few header bytes. This can be very expensive for large images during LaTeX export. Prefer reading only a small prefix (e.g., open the file and read the first ~8–16 bytes) and run the magic-header checks on that prefix.

Copilot uses AI. Check for mistakes.

(define (tmtex-as-eps name)
(let* ((u (url-relative current-save-target (unix->url name)))
(suffix (url-suffix u))
(fm (string-append (format-from-suffix suffix) "-file")))
(if (and (url-exists? u) (in? suffix (list "eps" "pdf" "png" "jpg")))
(detected (tmtex-guess-format u suffix))
(fm (if detected
(string-append detected "-file")
(string-append (format-from-suffix suffix) "-file"))))
Comment on lines 1848 to +1853
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tmtex-guess-format reads the image using u computed relative to current-save-target, but u is only corrected for paths starting with ".." later (inside the conversion branch). This breaks magic-header detection (and the new copy-with-correct-extension path) for images referenced via "../...". Consider normalizing u (apply the existing ".." handling) before calling tmtex-guess-format, and reuse that corrected u in both the copy and convert paths.

Copilot uses AI. Check for mistakes.
(if (and (url-exists? u)
(in? suffix (list "eps" "pdf" "png" "jpg" "jpeg")))
;; Fast path: image already in LaTeX-compatible format
Comment on lines +1854 to +1856
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fast-path suffix check uses suffix as returned by url-suffix, which is case-sensitive, so files like IMAGE.JPG will miss the fast path and be unnecessarily converted/fallback-rendered. Consider using a normalized suffix (e.g., locase-all) for the in? comparison (and similarly for any other suffix comparisons in this function).

Copilot uses AI. Check for mistakes.
(with p (url->string "$TEXMACS_PATH")
(set! name (string-replace name "$TEXMACS_PATH" p))
(set! name (string-replace name "file://" ""))
(list 'includegraphics name))
(receive (name-url name-string) (tmtex-eps-names)
(when (string-starts? name "..")
(set! u (url-relative current-save-source (unix->url name))))
(with nfm (if (== (url-suffix name-url) "pdf") "pdf-file"
"postscript-file")
(convert-to-file u fm nfm name-url))
(list 'includegraphics name-string)))))
(if (and (url-exists? u) detected
(in? detected (list "pdf" "png" "jpeg")))
;; Image has wrong suffix but is actually LaTeX-compatible;
;; copy with correct extension
(receive (name-url name-string) (tmtex-eps-names)
(let* ((ext (if (== detected "jpeg") "jpg" detected))
(dest-url (url-glue (url-unglue name-url 4)
(string-append "." ext)))
(dest-string (string-append
(string-drop-right name-string 4)
"." ext)))
(system-copy u dest-url)
(list 'includegraphics dest-string)))
;; Need format conversion
(receive (name-url name-string) (tmtex-eps-names)
(when (string-starts? name "..")
(set! u (url-relative current-save-source (unix->url name))))
(let* ((nfm (if (== (url-suffix name-url) "pdf") "pdf-file"
"postscript-file"))
(result (convert-to-file u fm nfm name-url)))
(if result
(list 'includegraphics name-string)
;; Conversion failed: fall back to rendering via TeXmacs
(begin
(print-snippet name-url
`(image ,name "0.618par" "" "" "") #t)
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The convert-to-file failure fallback renders an (image ...) snippet with a hard-coded width of 0.618par, which will change the exported image size (and may not match the original document’s image sizing). Consider preserving the original image dimensions/magnification, or at least avoid forcing an arbitrary width in the fallback so the PDF’s natural size is used.

Suggested change
`(image ,name "0.618par" "" "" "") #t)
`(image ,name "" "" "" "") #t)

Copilot uses AI. Check for mistakes.
(list 'includegraphics name-string)))))))))

(define (tmtex-image-length len)
(let* ((s (force-string len))
Expand Down
60 changes: 51 additions & 9 deletions TeXmacs/progs/convert/latex/tmtex.scm
Original file line number Diff line number Diff line change
Expand Up @@ -1825,22 +1825,64 @@
(define (tmtex-graphics l)
(tmtex-eps (cons 'graphics l)))

(define (tmtex-guess-format u suffix)
"Detect image format via magic header when suffix is unknown."
(if (and (url-exists? u) (== (format-from-suffix suffix) "generic"))
(with data (string-load u)
(cond ((and (> (string-length data) 8)
(== (char->integer (string-ref data 0)) #xff)
(== (char->integer (string-ref data 1)) #xd8))
"jpeg")
((and (> (string-length data) 8)
(string-starts? data "\x89PNG"))
"png")
((and (> (string-length data) 5)
(string-starts? data "%PDF-"))
Comment on lines +1831 to +1840
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tmtex-guess-format uses string-load which loads the entire file into memory just to inspect a few header bytes. This can be very expensive for large images during LaTeX export. Prefer reading only a small prefix (e.g., open the file and read the first ~8–16 bytes) and run the magic-header checks on that prefix.

Suggested change
(with data (string-load u)
(cond ((and (> (string-length data) 8)
(== (char->integer (string-ref data 0)) #xff)
(== (char->integer (string-ref data 1)) #xd8))
"jpeg")
((and (> (string-length data) 8)
(string-starts? data "\x89PNG"))
"png")
((and (> (string-length data) 5)
(string-starts? data "%PDF-"))
(let* ((path (url->string u))
(header
(with-input-from-file path
(lambda ()
;; Read at most 16 characters from the file for magic-header checks
(let loop ((i 0) (chars '()))
(if (or (>= i 16) (eof-object? (peek-char)))
(list->string (reverse chars))
(loop (+ i 1) (cons (read-char) chars))))))))
(cond ((and (> (string-length header) 8)
(== (char->integer (string-ref header 0)) #xff)
(== (char->integer (string-ref header 1)) #xd8))
"jpeg")
((and (> (string-length header) 8)
(string-starts? header "\x89PNG"))
"png")
((and (> (string-length header) 5)
(string-starts? header "%PDF-"))

Copilot uses AI. Check for mistakes.
"pdf")
(else #f)))
#f))

(define (tmtex-as-eps name)
(let* ((u (url-relative current-save-target (unix->url name)))
(suffix (url-suffix u))
(fm (string-append (format-from-suffix suffix) "-file")))
(if (and (url-exists? u) (in? suffix (list "eps" "pdf" "png" "jpg")))
(detected (tmtex-guess-format u suffix))
(fm (if detected
(string-append detected "-file")
(string-append (format-from-suffix suffix) "-file"))))
Comment on lines 1846 to +1851
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tmtex-guess-format reads the image using u computed relative to current-save-target, but u is only corrected for paths starting with ".." later (inside the conversion branch). This breaks magic-header detection (and the new copy-with-correct-extension path) for images referenced via "../...". Consider normalizing u (apply the existing ".." handling) before calling tmtex-guess-format, and reuse that corrected u in both the copy and convert paths.

Copilot uses AI. Check for mistakes.
(if (and (url-exists? u)
(in? suffix (list "eps" "pdf" "png" "jpg" "jpeg")))
;; Fast path: image already in LaTeX-compatible format
Comment on lines +1852 to +1854
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fast-path suffix check uses suffix as returned by url-suffix, which is case-sensitive, so files like IMAGE.JPG will miss the fast path and be unnecessarily converted/fallback-rendered. Consider using a normalized suffix (e.g., locase-all) for the in? comparison (and similarly for any other suffix comparisons in this function).

Copilot uses AI. Check for mistakes.
(with p (url->string "$TEXMACS_PATH")
(set! name (string-replace name "$TEXMACS_PATH" p))
(set! name (string-replace name "file://" ""))
(list 'includegraphics name))
(receive (name-url name-string) (tmtex-eps-names)
(when (string-starts? name "..")
(set! u (url-relative current-save-source (unix->url name))))
(with nfm (if (== (url-suffix name-url) "pdf") "pdf-file"
"postscript-file")
(convert-to-file u fm nfm name-url))
(list 'includegraphics name-string)))))
(if (and (url-exists? u) detected
(in? detected (list "pdf" "png" "jpeg")))
;; Image has wrong suffix but is actually LaTeX-compatible;
;; copy with correct extension
(receive (name-url name-string) (tmtex-eps-names)
(let* ((ext (if (== detected "jpeg") "jpg" detected))
(dest-url (url-glue (url-unglue name-url 4)
(string-append "." ext)))
(dest-string (string-append
(string-drop-right name-string 4)
"." ext)))
(system-copy u dest-url)
(list 'includegraphics dest-string)))
;; Need format conversion
(receive (name-url name-string) (tmtex-eps-names)
(when (string-starts? name "..")
(set! u (url-relative current-save-source (unix->url name))))
(let* ((nfm (if (== (url-suffix name-url) "pdf") "pdf-file"
"postscript-file"))
(result (convert-to-file u fm nfm name-url)))
(if result
(list 'includegraphics name-string)
;; Conversion failed: fall back to rendering via TeXmacs
(begin
(print-snippet name-url
`(image ,name "0.618par" "" "" "") #t)
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The convert-to-file failure fallback renders an (image ...) snippet with a hard-coded width of 0.618par, which will change the exported image size (and may not match the original document’s image sizing). Consider preserving the original image dimensions/magnification, or at least avoid forcing an arbitrary width in the fallback so the PDF’s natural size is used.

Suggested change
`(image ,name "0.618par" "" "" "") #t)
`(image ,name "" "" "" "") #t)

Copilot uses AI. Check for mistakes.
(list 'includegraphics name-string)))))))))

(define (tmtex-image-length len)
(let* ((s (force-string len))
Expand Down
2 changes: 1 addition & 1 deletion TeXmacs/progs/generic/embedded-edit.scm
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@
(u (url-relative (current-buffer) f))
(s (url-suffix f)))
(when (url-exists? u)
(let* ((data (string-load u))
(let* ((data (encode-base64 (string-load u)))
(raw `(tuple (raw-data ,data) ,(utf8->cork (url->string (url-tail f))))))
(tree-set t 0 raw))))))

Expand Down
23 changes: 23 additions & 0 deletions devel/203_26.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# 203_26 Export to LaTeX: fix images in inaccessible formats

## 如何测试
1. 打开墨干,插入一张PNG或JPG图片并将其嵌入文档(使用 Edit → Embed image)
2. 导出为LaTeX(File → Export → LaTeX...)
3. 用pdflatex编译导出的.tex文件,确认图片能正常显示
4. 也可测试GIF、WebP、SVG等非LaTeX原生格式,或将一张JPG重命名为.dat后插入

## 2026/03/07 修复LaTeX导出时图片格式不可用的问题
### What
1. `tmtex-as-eps`增加了`jpeg`后缀到快速路径
2. 新增`tmtex-guess-format`函数,通过Magic Header检测未知后缀的图片格式(PDF/PNG/JPEG)
3. 当图片后缀未知但实际格式可被LaTeX直接使用时,复制文件并使用正确的扩展名
4. `convert-to-file`失败时,回退到`print-snippet`通过TeXmacs内部渲染引擎生成PDF
5. 为JPEG、GIF、TIF添加`postscript-file`转换器(通过ImageMagick),补全到PDF的转换链

### Why
导出LaTeX时,图片格式可能不被LaTeX识别(如GIF、WebP、SVG),或者图片后缀与实际格式不符。
原有代码中`tmtex-as-eps`不检查`convert-to-file`的返回值,转换失败时仍然生成指向不存在文件的`\includegraphics`,导致pdflatex编译失败。

### How
- 修改`TeXmacs/progs/convert/latex/tmtex.scm`和`TeXmacs/plugins/latex/progs/convert/latex/tmtex.scm`
- 修改`TeXmacs/plugins/image/progs/image/jpeg.scm`、`gif.scm`、`tif.scm`
14 changes: 13 additions & 1 deletion moebius/Scheme/L1/object_l1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
#include "moebius/data/scheme.hpp"
#include "moebius/tree_label.hpp"

#include <lolly/data/base64.hpp>

using moebius::make_tree_label;
using moebius::RAW_DATA;
using moebius::data::scm_quote;
using moebius::data::scm_unquote;
using moebius::data::tree_to_scheme_tree;
Expand Down Expand Up @@ -122,7 +125,16 @@ tmscm_to_content (tmscm p) {
if (tmscm_is_tree (p)) return tmscm_to_tree (p);
if (tmscm_is_pair (p)) {
if (!tmscm_is_symbol (tmscm_car (p))) return "?";
tree t (make_tree_label (tmscm_to_symbol (tmscm_car (p))));
tree_label tl= make_tree_label (tmscm_to_symbol (tmscm_car (p)));
if (tl == RAW_DATA) {
// RAW_DATA in Scheme S-expression contains base64-encoded data;
// decode back to binary for the C++ tree representation
tree t (RAW_DATA, 1);
t[0]->label=
lolly::data::decode_base64 (tmscm_to_string (tmscm_cadr (p)));
return t;
}
tree t (tl);
p= tmscm_cdr (p);
while (!tmscm_is_null (p)) {
t << tmscm_to_content (tmscm_car (p));
Expand Down
Loading