R7.9: Explorer's New makes docx, xlsx, pptx and md on Windows

Each app installs a blank file and points ShellNew at it. Explorer
lists a type only through its default class, so a type no program
handles (no default, or one naming no class) gets the app's class;
an installed handler is never replaced. Uninstall removes only what
this install wrote.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-17 00:32:48 -04:00
1 parent 2f971c646f
commit 632a66eb46
11 files changed
+80 -5

No files matched your search

+2
View File
@@ -144,6 +144,7 @@ tests/scripts/*.sh *.py 脚本测试(缺工具时报 SKIP)
packaging/macos/bundle.sh ely-doc debug 出 dist/ElyDoc.app(带图标、文档类型与该应用的引擎)
packaging/macos/release.sh [批次] 该批各应用的 release 包与 dmg,另有装全部的 ElyOffice dmg;ELY_SIGN_IDENTITY 加 ELY_NOTARY_PROFILE 即公证
packaging/windows/bundle.sh 由 dist 里各应用的安装程序出合集 ElyOffice 安装程序(先跑 installer.sh)
packaging/windows/templates.sh 以本构建的内核重写 packaging/windows/new 的空白 DOCX、XLSX、PPTX(右键“新建”用)
packaging/icons/masters.py 写出 assets/icons 的全部 SVG 母版(图标设计只改这里)
packaging/icons/build.sh 由 assets/icons 的母版渲染 icns、png、ico(只在 macOS)
tests/scripts/icons.sh 八个图标齐全、未过期,macOS 为 squircle,其余无底形
@@ -315,4 +316,5 @@ cargo run -p ely-doc 启动一个应用(ElyDoc 先 cargo build -p ely
- 2026-10-16(Gitea #4):装了宋体仍报缺少(R7.3)。内核按名找字体时比较 `GetEnglishSearchFontName`(小写 ASCII、去空格、中日韩本地名译为英文名,`宋体` 即 `simsun`),所以文档的 `宋体` 找到并用上 SimSun;缺字检查却比较原名与所用字体名,报为缺失。Linux 上内核以界面语言(英文)为 fontconfig 字体取名,文档里的本地名一律报缺。补丁 `0048-lok-fonts-missing-by-search-name.patch`:缺字检查同样比较检索名。实测(2026-10-16):Arch 容器装 ElyDoc 0.1.5 的 Arch 包与 simsun.ttc,40 行宋体五号、文档网格的 docx 报缺宋体,正文实为 SimSun(无此字体时为方框),与 Windows 同为一页;换上打过 0048 的内核后同一容器不再报缺、仍为一页。用户所说一页变两页未能复现,需其文件:十种宋体版式(44 与 45 行的文档网格、无网格、折行两端对齐、字符网格、粗体、小四、固定行距、1.5 倍行距)在 Windows 与 Linux 内核上页数、断行、分页逐行相同(`.work/r7/pages`),网格下每页 44 行、每行约 39 字,同 Word 的 A4 默认;差别若在,是该文件里 LibreOffice 与 Word 的排版不同,与平台无关。缺字检查测试:`navigate/tests.rs` 以随包的 DejaVu Sans 写作 `DEJAVU SANS`,只报真缺的字体。另见:Linux 上属性栏的字体框为空(字体列表只有 SimSun,文档写宋体),未改。
- 2026-10-16(Gitea #5):每个 PDF 打开即报已损坏(R7.4)。C11.8 把画图块与导出时 MuPDF 的任何警告都算作损坏;读页面时载入未嵌入的字体也发警告(中文文件里写名而不嵌入的宋体等:`non-embedded font using identity encoding`),合法的文件因此转为修复副本、保存一律另存。现在警告监视(`keep_warning`,打开时的扫描、画图块、导出与 Markdown 的 PDF 共用)只略去这一句与紧随其后的 MuPDF 重复计数(`... repeated N times...`);其余照算损坏,含内容的语法错误、无字体写字、坏的嵌入字体与图片流。读文字与搜索也受监视(坏图片读过即被缓存,之后画图块、导出与检查都不再报警告):其间遇到的损坏记入报告与该文档,下一次画图块或检查图片报出,报出之前导出图片与文字一律拒绝。监视开始时,MuPDF 尚未报出的重复计数(来自其余未受监视的读取)按它所重复的那句归类(`ctx->warn.message`):字体提示的重复略去,其余照算。`tests/output.rs` 核对:两个同名未嵌入字体的文件先读文字再画图块、导出图片与文字都不算损坏;无字体写字与 `BT ] ET` 的图块为损坏;`[/FlateDecode /ASCIIHexDecode]` 下校验和错的图片使两种导出都被拒、图块为损坏。先读文字或搜索后,这样的两张图片、颜色键掩码无效的一张图片、以及它在两个未嵌入字体之前或之后画出,画图块、导出与检查图片仍报损坏。
- 2026-10-16(Gitea #3):Windows 与 Linux 打印(R7.6)。`print_pdf` 改为异步:对话框在应用线程上,读页与出作业在自己的线程上(`print::off`),应用不卡。Windows:系统打印对话框(`PrintDlgEx`,Windows 11 显示为新式对话框),打开前把默认打印机的设置改成文档的纸张与方向(按打印机自己的纸型匹配(纸型须装得下页面,至多大 2 毫米,Linux 的 PWG 名同此);没有即自定义尺寸),设置须连同打印机名(`DEVNAMES`)一起交给对话框,否则对话框改用打印机自己的偏好(实测:Letter 文档印成 A4);所选页码范围照办,份数交驱动;勾“打印到文件”时文档输出为 `FILE:`,由打印子系统问文件名。驱动不支持所设纸张时会换成别的(实测“Microsoft Print to PDF”没有自定义尺寸,1000×600 点的页面换成 A4),所以出作业之前(`StartDoc` 之前)按打印机实际的纸张核对每一页:超出纸张半点以上(纸张尺寸至多舍入到整点,`print::ROUNDING`)、转过来也超出即报错,什么也不印(实测:600×842 点的页面原在 A4 上印出、右缘被裁)。页面由 Windows 自带的 PDF 渲染(`Windows.Data.Pdf`)按打印机分辨率分条画出,按原大从纸张的角上放(扣去不可打印的偏移),只在转过来才放得下时左转 90 度;渲染器的尺寸按 DIP 算、乘显示缩放,每次作业先试画一次量出倍数。`ELY_PRINT_TO` 时不弹对话框,经“Microsoft Print to PDF”写成文件(测试用)。`GlobalFree` 成功时返回空,windows-rs 读成错误码 0 的错误,须当作成功(否则打印报“操作成功完成”)。Linux:经 xdg 桌面门户的 `org.freedesktop.portal.Print`(`PreparePrint` 后带其令牌 `Print`,交出 PDF 的文件描述符),页面设置为文档纸张(标准纸型给 PWG 名,否则自定义毫米尺寸),边距为零,只许输出 PDF(`supported_output_file_formats`,否则对话框可选 PostScript,写出的却是 PDF);门户照原样交出所给的字节,对话框的选择由应用排定(`print/linux/sheets.rs`):GTK 设置里的页码范围(从零起)、奇偶页、倒序、份数与逐份、缩放、每张页数与排列,排到对话框所选纸张与方向上,每页为一个表单对象(带可打印批注的外观;带 NoRotate 的批注在转过的页面上绕其左上角转回、保持正立,同 MuPDF;外观的资源名避开页面已有的;可打印却没有外观的批注即报错,链接与弹出说明本无外观),单页按原大乘缩放从纸张左上角放,超出半点以上而转过来放得下即左转,否则报错;方向照 GTK 的写法(`reverse-portrait`,门户文档的页面设置同此);奇偶页只在 GTK 的文件与 lpr 后端时由 Ely 排(设置里有 `output-uri` 或 `lpr-commandline`),CUPS 后端自己把它交给 CUPS(`cups-page-set`,GTK 3.24.52 实测),Ely 再排一次即丢页;份数、每张页数等 GTK 留给应用,Ely 照排;裁剪框超出媒体框的部分不印(取两者的交);页码范围的开放终点 GTK 写作 -1(`1--1`),倒着的范围即报错;多页按格居中,再交内存文件(`memfd`);直接用 zbus:ashpd 0.13.13 的 `PageSetup`、`Settings` 缺任一字段即解析失败,真门户的回复常不带 `PPDName`。页面尺寸由 lopdf 读(CropBox,否则 MediaBox,继承、`/Rotate` 与页面自己的 `/UserUnit`)。打印不再按平台禁用(原 `print::BUILT` 与“此平台尚未构建”删去)。替身门户的测试核对选页、缩放、倒序与份数后交出的页面与位置,与只许 PDF。ElyPDF 的打印副本(`ely_print_copy`)按打印的规则画:可打印(不隐藏)的批注与表单控件,外观画进页面内容(位置照 MuPDF 自己的 `pdf_annot_transform`,与屏上一致;无外观的先由 MuPDF 生成,MuPDF 原只在显示时放在本地表里、不存入文件),其余批注与表单去掉,只留链接;系统的渲染器按屏幕规则画(不打印的照画,“打印但不显示”的不画,实测 Windows),画进内容后各渲染器都只见要打印的;仍画不出的即报错;`tests/output.rs` 渲染副本核对。真窗口(Windows):ElyDoc 的 Ctrl+P、Ely 的打印对话框、系统对话框、“Microsoft Print to PDF”的另存为,印出的 PDF 为文档的 Letter;系统对话框取消时不打印、不报错(`.work/r76/print.ps1`)。未验证:Linux 真桌面上的门户(只对替身门户核对);实体打印机。
- 2026-10-16(用户定):Windows 资源管理器的右键“新建”可建 DOCX、XLSX、PPTX 与 Markdown 文件(R7.9)。空白文件在 `packaging/windows/new`(`templates.sh` 以本构建的内核把空文件转存而得,无作者与日期;Markdown 为空文件),随所属的应用装在安装目录(列在 `.ely-installed` 里),`HKCU\…\.<扩展名>\ShellNew` 的 `FileName` 指向它。资源管理器只在扩展名有默认 ProgID 时列出新建项(实测:只有 `ShellNew`、或只在 `OpenWithProgids` 下时都不列);所以只在该类型没有任何程序处理时(HKCR 下无默认值,或默认值所指的类不存在:本机 Windows 11 的 `.md` 指向不存在的 `Markdown Document`)把默认值设为本应用的 ProgID(写在 HKCU,用户自己的 `UserChoice` 不动),已有的从不改动(装着 Office、WPS 时新建菜单用它们自己的项);没有处理程序的这四种类型从此由 Ely 打开。卸载只删仍是本安装写下的 `ShellNew` 与默认值。ProgID 的名称为“<应用> Document”(新建菜单与类型列显示它)。`tests/scripts/types.sh` 核对四种都有新建项,默认值只在判空之后写。
- 2026-10-16(用户定):ElyMark 打开的块保持原样(R7.5)。组件库 bff7ebc 起,可视模式里按下的块以原文显示在它合上时的框里(同样的边距与透明边框),字号、行高与颜色同合上时:段落、列表、引用等为正文,标题按其级数的字号、1.3 倍行高、加粗(六级为次要色),照原文写出的块(链接定义等)为等宽次要色;不再加深色底板与另一套边距,按下前后不挪位置。代码块、块公式与 front matter 仍有自己的底板(代码与 front matter 为等宽小号,front matter 为次要色,同合上时)。输入框的着色(源码模式同用)给标题与粗体同一字重(原为半粗,压过所设的加粗),六级标题里的字照为次要色(行内代码照为正文色,2e65b05),front matter 照 front matter 读(原读成分隔线加标题,键名变亮变粗)(1ff993d、962964e)。表格单元格照旧。打字机模式的居中另按两端余量判定(原只按块高与视图高,打开前后同高时首块不再居中)。同批带入组件库 cdc5ce8(标题栏拖动区里的控件按下归控件,Windows)。Windows 真窗口核对(`.work/r75`):段落、三级标题、代码块打开前后位置相同。
+1
View File
@@ -1,6 +1,7 @@
; Per-user installer; pass each define the checks below name.
Unicode true
!include "MUI2.nsh"
!include "LogicLib.nsh"
!ifndef APP_NAME
!error "pass -DAPP_NAME"
+4
View File
@@ -18,6 +18,10 @@ makensis=${MAKENSIS:-"/c/Program Files (x86)/NSIS/makensis.exe"}
}
stage=$root/target/stage/$slug
"$root/packaging/stage.sh" "$slug" "$stage"
# Explorer's New copies these; the install list owns them.
for ext in $(awk -F'\t' -v slug="$slug" '$1 == slug { print $2 }' "$root/packaging/types.tsv"); do
[ ! -e "$root/packaging/windows/new/new.$ext" ] || cp "$root/packaging/windows/new/new.$ext" "$stage/"
done
types=$root/target/stage/$slug-types.nsh
"$root/packaging/windows/types.sh" "$slug" >"$types"
list=$root/target/stage/$slug-files.txt
Binary file not shown.
View File
Whitespace-only changes.
Binary file not shown.
Binary file not shown.
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env bash
# Writes packaging/windows/new: the blank files Explorer's New makes.
set -euo pipefail
root=$(cd "$(dirname "$0")/../.." && pwd)
soffice=$root/engines/lo-src/instdir/program/soffice.com
[ -x "$soffice" ] || {
echo "templates.sh: no kernel at $soffice; run scripts/build-libreoffice.sh" >&2
exit 1
}
out=$root/packaging/windows/new
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
mkdir "$work/in"
# The kernel reads an empty file as a new document.
for ext in docx xlsx pptx; do
: >"$work/in/new.$ext"
"$soffice" --headless --norestore "-env:UserInstallation=file:///$(cygpath -m "$work/profile")" \
--convert-to "$ext" --outdir "$(cygpath -w "$out")" "$(cygpath -w "$work/in/new.$ext")"
[ -s "$out/new.$ext" ] || {
echo "templates.sh: the kernel wrote no new.$ext" >&2
exit 1
}
done
: >"$out/new.md"
+31 -1
View File
@@ -10,11 +10,16 @@ progid="ElyOffice.$name"
classes='Software\Classes'
exts=$(awk -F'\t' -v slug="$slug" '$1 == slug && $2 != "/" { print $2 }' "$root/packaging/types.tsv")
folder=$(awk -F'\t' -v slug="$slug" '$1 == slug && $2 == "/" { print "yes" }' "$root/packaging/types.tsv")
# Types Explorer's New menu makes, from packaging/windows/new.
fresh=""
for ext in $exts; do
[ ! -e "$root/packaging/windows/new/new.$ext" ] || fresh="$fresh $ext"
done
# Open With lists the app; the user's default stays theirs.
echo '!macro ElyTypesInstall'
if [ -n "$exts" ]; then
echo " WriteRegStr HKCU \"$classes\\$progid\" \"\" \"$name\""
echo " WriteRegStr HKCU \"$classes\\$progid\" \"\" \"$name Document\""
echo " WriteRegStr HKCU \"$classes\\$progid\\DefaultIcon\" \"\" \"\$INSTDIR\\$slug.ico\""
echo " WriteRegStr HKCU \"$classes\\$progid\\shell\\open\\command\" \"\" '\"\$INSTDIR\\$slug.exe\" \"%1\"'"
echo " WriteRegStr HKCU \"$classes\\Applications\\$slug.exe\" \"FriendlyAppName\" \"$name\""
@@ -23,6 +28,20 @@ if [ -n "$exts" ]; then
echo " WriteRegStr HKCU \"$classes\\Applications\\$slug.exe\\SupportedTypes\" \".$ext\" \"\""
done
fi
for ext in $fresh; do
echo " WriteRegStr HKCU \"$classes\\.$ext\\ShellNew\" \"FileName\" \"\$INSTDIR\\new.$ext\""
# New needs a class; take only a handlerless type.
echo " ReadRegStr \$0 HKCR \".$ext\" \"\""
echo " ClearErrors"
# A default naming no class handles nothing either.
echo " \${If} \$0 != \"\""
echo " EnumRegKey \$1 HKCR \"\$0\" 0"
echo " \${EndIf}"
echo " \${If} \$0 == \"\""
echo " \${OrIf} \${Errors}"
echo " WriteRegStr HKCU \"$classes\\.$ext\" \"\" \"$progid\""
echo " \${EndIf}"
done
if [ -n "$folder" ]; then
echo " WriteRegStr HKCU \"$classes\\Directory\\shell\\$progid\" \"\" \"Open in $name\""
echo " WriteRegStr HKCU \"$classes\\Directory\\shell\\$progid\" \"Icon\" \"\$INSTDIR\\$slug.ico\""
@@ -39,6 +58,17 @@ if [ -n "$exts" ]; then
echo " DeleteRegValue HKCU \"$classes\\.$ext\\OpenWithProgids\" \"$progid\""
done
fi
# Only what this install wrote.
for ext in $fresh; do
echo " ReadRegStr \$0 HKCU \"$classes\\.$ext\\ShellNew\" \"FileName\""
echo " \${If} \$0 == \"\$INSTDIR\\new.$ext\""
echo " DeleteRegKey HKCU \"$classes\\.$ext\\ShellNew\""
echo " \${EndIf}"
echo " ReadRegStr \$0 HKCU \"$classes\\.$ext\" \"\""
echo " \${If} \$0 == \"$progid\""
echo " DeleteRegValue HKCU \"$classes\\.$ext\" \"\""
echo " \${EndIf}"
done
if [ -n "$folder" ]; then
echo " DeleteRegKey HKCU \"$classes\\Directory\\shell\\$progid\""
fi
+1
View File
@@ -284,3 +284,4 @@
- [x] R7.6 Windows 与 Linux 不能打印(#3,§19.3):`print_pdf` 只有 macOS;Windows 用系统打印对话框,Linux 经 xdg 桌面门户的打印接口
- [x] R7.7 发布 0.1.6:版本号,三端建包、签名公证、上传,合集包一并上传,本机装上(v0.1.6 共 46 个文件,出自 a1b8c18:六个公证 dmg 与 ElyOffice dmg、六个安装程序与合集 setup.exe、24 个 Linux 包、一对源码包;`crates.py` 改问 cargo 本机链进的 crate,补 BSD-3-Clause;源码包只由 Linux 上传,重跑遇同名即失败、什么也不传(`ci-upload.sh`);本机六个应用静默装上 0.1.6、冒烟通过 — 第 1 轮:macOS 与 Linux 并行会重复上传源码;第 2 轮:重跑跳过同名会使更新清单与 dmg 散列不符、注释超长;第 3 轮:部分已传的重跑未测、注释超长,修后不再审查)
- [x] R7.8 答复 #3 至 #6:每条写已修于 0.1.6;#6 的工作区建议另开 [FEATURE REQUEST] 并链过去(#3、#4、#5、#6 各一条答复,#7 为 [FEATURE REQUEST] 工作区与常用文件夹;#3、#5、#6 关闭,#4 开着等报告者的文件 — 第 1 轮:#5 的答复说只有读不出的内容才算损坏,超出 0.1.6 所修,改为只说未嵌入字体的提示不再算损坏;第 2 轮 PASS)
- [x] R7.9 Windows 右键“新建”(用户 2026-10-16 定,发 0.1.7,只发 Windows):资源管理器的新建菜单可建 DOCX、XLSX、PPTX、Markdown 文件,各由所属的应用装上与卸下(`types.sh` 测试另以两个变体核对:去掉判空、去掉 md 的模板各自失败;本机在 0.1.6 上静默装 0.1.7 的 ElyDoc、ElyTable、ElyShow、ElyMark:新建菜单列出四项,无 `.ely-old`,四个模板按新建的方式复制后在所属应用里打开,日志无错误与警告;卸载 ElyMark 后 `.md` 的新建项与默认值都删去、`UserChoice` 未动,重装复原;`windows-installer.sh`、`windows-bundle.sh` 通过。未送审:用户要求从简)
+16 -4
View File
@@ -28,7 +28,19 @@ for slug in $(cut -f1 "$types" | sort -u); do
mimes=$(cut -f3 <<<"$rows" | sort -u | wc -l | tr -d ' ')
[ "$utis" -eq "$mimes" ] || fail "$slug: macOS declares $utis types, the table $mimes"
done
# Never the default: macOS ranks Alternate; Windows only lists.
! grep -q 'Classes\\\.[a-z]*" ""' <(for s in $(cut -f1 "$types" | sort -u); do "$root/packaging/windows/types.sh" "$s"; done) ||
fail "Windows sets a default handler"
echo "ok: types ($count file types on three systems)"
# Never the default: Windows fills only a handlerless type.
for s in $(cut -f1 "$types" | sort -u); do
"$root/packaging/windows/types.sh" "$s" | awk -v slug="$s" '
/WriteRegStr HKCU "Software\\Classes\\\.[a-z]*" ""/ && (prev !~ /\$\{OrIf\} \$\{Errors\}$/ || prev2 !~ /\$\{If\} \$0 == ""$/) { print slug ": " $0; bad = 1 }
{ prev2 = prev; prev = $0 } END { exit bad }' || fail "Windows sets a default handler"
done
# Explorer's New makes docx, xlsx, pptx and md.
fresh=0
for ext in docx xlsx pptx md; do
slug=$(awk -F'\t' -v ext="$ext" '$2 == ext { print $1 }' "$types")
windows=$("$root/packaging/windows/types.sh" "$slug")
grep -qF "\\.$ext\\ShellNew\" \"FileName\" \"\$INSTDIR\\new.$ext\"" <<<"$windows" ||
fail "$slug: New misses .$ext"
fresh=$((fresh + 1))
done
echo "ok: types ($count file types on three systems, $fresh in New)"