diff --git a/agents.md b/agents.md index 6d3fd21..c6de9e0 100644 --- a/agents.md +++ b/agents.md @@ -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`):段落、三级标题、代码块打开前后位置相同。 diff --git a/packaging/windows/installer.nsi b/packaging/windows/installer.nsi index 9389caf..7bbeb49 100644 --- a/packaging/windows/installer.nsi +++ b/packaging/windows/installer.nsi @@ -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" diff --git a/packaging/windows/installer.sh b/packaging/windows/installer.sh index 55058ee..f89a95d 100755 --- a/packaging/windows/installer.sh +++ b/packaging/windows/installer.sh @@ -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 diff --git a/packaging/windows/new/new.docx b/packaging/windows/new/new.docx new file mode 100644 index 0000000..9503e81 Binary files /dev/null and b/packaging/windows/new/new.docx differ diff --git a/packaging/windows/new/new.md b/packaging/windows/new/new.md new file mode 100644 index 0000000..e69de29 diff --git a/packaging/windows/new/new.pptx b/packaging/windows/new/new.pptx new file mode 100644 index 0000000..e1a66d4 Binary files /dev/null and b/packaging/windows/new/new.pptx differ diff --git a/packaging/windows/new/new.xlsx b/packaging/windows/new/new.xlsx new file mode 100644 index 0000000..99976f8 Binary files /dev/null and b/packaging/windows/new/new.xlsx differ diff --git a/packaging/windows/templates.sh b/packaging/windows/templates.sh new file mode 100644 index 0000000..2448f56 --- /dev/null +++ b/packaging/windows/templates.sh @@ -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" diff --git a/packaging/windows/types.sh b/packaging/windows/types.sh index 9d384ce..1e484c9 100755 --- a/packaging/windows/types.sh +++ b/packaging/windows/types.sh @@ -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 diff --git a/tasks/TASKS.md b/tasks/TASKS.md index ebe4094..c6af501 100644 --- a/tasks/TASKS.md +++ b/tasks/TASKS.md @@ -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` 通过。未送审:用户要求从简) diff --git a/tests/scripts/types.sh b/tests/scripts/types.sh index 26d9b72..5757ec3 100755 --- a/tests/scripts/types.sh +++ b/tests/scripts/types.sh @@ -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)"