Skip to content
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ office_plugin/**/*.user.props
office_plugin/**/*.pfx
office_plugin/installer/vsto-signing.cer
office_plugin/installer/devcert.cer
office_plugin/tests/**/artifacts/
!LaTeXSnipper-linux.spec
!LaTeXSnipper-macos.spec
!LaTeXSnipper.spec
Expand Down
49 changes: 44 additions & 5 deletions docs/office_plugin_formula_workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,10 @@ PowerPoint 公式以 shape 为单位保存元数据。短字段写入 shape tags

| 设置项 | 影响链路 | 不影响链路 |
| --- | --- | --- |
| 插入后端:OLE / Word OMML | 新插入公式、更新公式、转换目标渲染引擎 | 加载所选、删除所选、引用字段 |
| 公式默认颜色 | 新插入公式、格式化所选 | 加载所选、更新公式、打开编辑器预览、格式化全文、重编号、引用 |
| 公式默认字体 | 新插入公式、格式化所选 | 加载所选、更新公式、打开编辑器预览、格式化全文、重编号、引用 |
| 公式缩放 | 新插入公式、格式化所选、自然尺寸记录 | 加载所选、重编号、引用 |
| 插入后端:OLE / Word OMML | 新插入公式、公式解析、更新公式、转换目标渲染引擎 | 加载所选、删除所选、引用字段 |
| 公式默认颜色 | 新插入公式、公式解析、格式化所选 | 加载所选、更新公式、打开编辑器预览、格式化全文、重编号、引用 |
| 公式默认字体 | 新插入公式、公式解析、格式化所选 | 加载所选、更新公式、打开编辑器预览、格式化全文、重编号、引用 |
| 公式缩放 | 新插入公式、公式解析、格式化所选、自然尺寸记录 | 加载所选、重编号、引用 |
| 编号位置:左 / 右 | 新插入编号公式、给已有公式添加编号、更新编号公式时重建布局 | 已存在编号公式不会仅因保存设置或执行重编号自动左右移动 |
| 编号外框 | 新插入自动编号、给已有公式添加编号、重编号、手动编号显示 | 引用字段自身不独立生成外框,只引用目标编号书签 |
| 包含章编号 | 新插入自动编号、给已有公式添加编号、重编号 | 手动编号文本 |
Expand Down Expand Up @@ -294,6 +294,41 @@ REF LaTeXSnipperEq_{equationId} \h

`OMML 转 OLE` 命令会额外识别当前选区内的 Word 原生 OMML 公式,并按上面的 MathML 源码链路转换为 LaTeXSnipper OLE。该能力只属于显式转换入口。

## Word 公式解析链路

“解析所选”和“解析全文”把带明确 LaTeX 定界符的普通文本转换为现有 LaTeXSnipper 托管公式。解析结果不使用新的元数据格式,后续可继续加载、编辑、转换、格式化、编号和引用。

### 扫描范围与定界符

- 识别 `$...$`、`\(...\)`、`$$...$$` 和 `\[...\]`,其中 `$$...$$` 优先于 `$...$`。
- `$...$` 与 `\(...\)` 生成无编号行内公式;`$$...$$` 与 `\[...\]` 生成行间公式。
- 只处理完整闭合且内容非空的公式;行内公式不能跨段落。未闭合、空内容或转换失败的源码原样保留。
- 定界符是否转义按其前方连续反斜杠数量判断;奇数表示转义,偶数表示有效定界符。`\$` 因此是普通美元符号。
- `\begin{...}...\end{...}` 只可作为定界符内部内容,不作为外层公式边界。
- 正文与每个表格单元格独立扫描,定界符不能跨越单元格或不安全区域闭合。
- “解析所选”要求完整起止定界符都位于非空选区中;部分选择和折叠光标不扩展扫描范围。
- “解析全文”只扫描主正文 story 及其中的表格,不扫描页眉、页脚、脚注、尾注、批注或文本框。

扫描时会把已有 LaTeXSnipper 公式、Word 原生公式、字段、超链接、content control、公式引用、编号边界及受保护内容作为硬边界。解析不会进入这些对象,也不会利用定界符外的 `(1)`、`(1)` 或 `[1]` 推断编号。

### 设置、编号与预处理

解析开始时只读取一次当前设置和状态窗格编号选项,整批公式使用同一快照:

1. 插入后端决定生成 Word OMML 还是 OLE。
2. 默认字体和颜色写入完整 LaTeX 源码,缩放倍率写入既有 `FontScale` 字段。
3. 行内公式始终无编号。
4. 行间公式存在唯一、顶层、非注释且非转义的 `\tag{...}` 时,预处理先移除该命令,并把非空 tag 文本写入既有手动编号字段。这一优先级高于状态窗格编号选项,MathJax 不负责排版编号。
5. 不含有效 `\tag` 的行间公式遵循状态窗格当前的自动编号或自定义编号选项;解析本身不执行全文重编号。

空 `\tag{}`、重复顶层 `\tag`、未闭合 tag、`\tag*`、嵌套分组中的 `\tag`,以及行内公式中的顶层 `\tag` 均视为当前公式解析失败。注释或转义的 `\tag` 保留在公式源码中但不作为插件编号;`\label`、`\ref` 和 `\eqref` 不参与插件编号或引用推断。

### 批处理、状态与重试

候选公式按文档位置倒序、每 5 个一批处理,每批使用短 Word undo record 并更新一次进度。单个公式准备或替换失败时保留原文并继续:无失败时最终只显示成功解析数量,有失败时显示处理总数、成功数和失败数;未发现候选时单独提示“未找到可解析的公式”。

命令取消或超时后,已经完成的批次仍是正常托管公式,未处理的定界文本保持原样。再次执行解析即可继续;扫描器会跳过已生成的托管公式,因此不会重复转换。

## Word 格式化链路

### 格式化所选
Expand Down Expand Up @@ -349,6 +384,7 @@ PowerPoint 编辑器固定白底黑字,因为 PowerPoint 编辑画布背景板
| Word 重编号 | 中 | 一次扫描公式、章/节边界和字段;异常公式跳过,引用字段只更新 LaTeXSnipper REF |
| Word 引用 | 中 | 占位符、目标公式选择、书签和 REF 字段组合;稳定入口是公式或公式所在段落 |
| 批量转换所选 | 高 | 稳定快照、倒序分批、每批短 undo;主要耗时仍是 MathJax 渲染和 Office COM 替换 |
| Word 公式解析 | 高 | 安全范围扫描、tag 预处理、MathJax/OMML 或 OLE 渲染、倒序分批替换及失败续跑 |
| 格式化所选 | 中 | 仅适合 LaTeX 源码公式;会重写顶层字体和颜色宏并重新渲染;多选时分批处理 |
| 格式化全文 | 低 | 只恢复自然字号或自然尺寸,不批量改写源码 |
| PowerPoint OLE/PNG | 中 | shape 元数据、自然尺寸、渲染引擎切换和位置缩放维护;多选转换和格式化按批处理 |
Expand All @@ -360,6 +396,7 @@ PowerPoint 编辑器固定白底黑字,因为 PowerPoint 编辑画布背景板
- 添加编号/插入编号公式:编号状态计算优先按当前位置前的对象构建时间线,减少不必要的元数据读取。
- 格式化所选行内基线:只扫描当前段落对象,避免大文档中选一个公式也遍历全文。
- 批量转换和批量格式化所选:只收集稳定快照;不跨批持有 live COM object;按 5 个公式一批处理;转换和涉及替换的格式化按倒序执行;每批更新一次状态窗格;Word 每批使用短 undo record 和短屏幕刷新暂停区间。
- 公式解析:先收集仅包含位置、原文、LaTeX 和显示模式的稳定候选;按文档位置倒序、每 5 个一批准备和替换;不跨批持有 live COM object;失败项保留原文并允许下次续跑。

## OLE payload

Expand Down Expand Up @@ -387,7 +424,7 @@ MathJax 3.2.2 的 SVG 对部分数学字母会输出 `<text data-variant="...">`

## Undo 与外部传递

Word 插入、更新、删除、编号、转换和格式化都在必要位置使用 Word undo record。用户撤销后,Word 会同时回撤对象、字段和文档变量的变化。
Word 插入、更新、删除、编号、解析、转换和格式化都在必要位置使用 Word undo record。用户撤销后,Word 会同时回撤对象、字段和文档变量的变化。

文档发给其他用户再返回时,插件能否加载取决于 Office 是否保留:

Expand All @@ -404,3 +441,5 @@ Word 插入、更新、删除、编号、转换和格式化都在必要位置使
- 不把默认字体/颜色作为编辑器打开时的临时样式。
- 不在加载所选时改写侧边栏用户草稿以外的持久源码。
- 不在格式化全文时批量重写所有公式源码。
- 不把解析与全文重编号耦合;解析后的全局序号校正仍由用户显式执行“重编号”。
- 不扫描非主正文 story,也不跨已有公式、字段、受保护对象或表格单元格边界配对定界符。
3 changes: 3 additions & 0 deletions office_plugin/LaTeXSnipper.OfficePlugin.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@
<Project Path="hosts/PowerPointAddIn/LaTeXSnipper.OfficePlugin.PowerPointAddIn.csproj" />
<Project Path="hosts/WordAddIn/LaTeXSnipper.OfficePlugin.WordAddIn.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/LaTeXSnipper.OfficePlugin.WordParsingE2E/LaTeXSnipper.OfficePlugin.WordParsingE2E.csproj" />
</Folder>
</Solution>
14 changes: 13 additions & 1 deletion office_plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Office 2016 is not officially supported (requires manual .NET 4.8 and WebView2 i
- Load, update, and delete managed formulas
- Chapter/section-aware automatic numbering, references, boundaries, and Renumber All
- In-place conversion between managed OLE and OMML formulas, plus explicit conversion of selected native Word OMML formulas to LaTeXSnipper OLE
- Parsing of `$...$`, `\(...\)`, `$$...$$`, and `\[...\]` LaTeX in the selected range or main document body, including table cells
- Selected formula style reset and document-wide natural-size restoration
- Screenshot OCR via desktop Bridge

Expand Down Expand Up @@ -54,8 +55,9 @@ Office 2016 is not officially supported (requires manual .NET 4.8 and WebView2 i
| `hosts/WordVstoAddIn` | Thin VSTO shell loaded by Word |
| `hosts/PowerPointAddIn` | PowerPoint workflows: Ribbon, OLE/PNG insertion, metadata, controller |
| `hosts/PowerPointVstoAddIn` | Thin VSTO shell loaded by PowerPoint |
| `tests/LaTeXSnipper.OfficePlugin.WordParsingE2E` | Real-Word OMML/OLE parsing regression test |
| `installer/` | Inno Setup installer and release build entry point |
| `tools/` | Release-only VSTO build and installer cleanup support |
| `tools/` | Build, installer, metadata, and Word parsing test entry points |
| `hosts/OleFormulaObjectNative/` | Native C++ COM/OLE in-proc handler DLL registered as the Office formula object for 32-bit and 64-bit Office |

Shared libraries target `net48;net9.0`. Office hosts target .NET Framework 4.8. The native OLE handler is built for x64 and Win32 Office.
Expand All @@ -71,3 +73,13 @@ office_plugin\installer\build.bat 2.4.0 Release
Output: `office_plugin\release\OfficePluginSetup-2.4.0.exe`

Run the installer as administrator. Close Word and PowerPoint before installation, upgrade, or removal.

## Word Parsing E2E Test

Close every Word window and run:

```powershell
office_plugin\tools\Test-WordFormulaParsingE2E.ps1
```

The test exercises both OMML and OLE backends through the production controller. Disposable DOCX/PDF evidence is written to the test project's ignored `artifacts` directory.
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ namespace LaTeXSnipper.OfficePlugin.WordAddIn;

public sealed partial class DynamicWordApplicationAdapter
{
public Task<int> ResetCustomFormulaSizesAsync(CancellationToken cancellationToken)
public Task<WordFormattingResetResult> ResetCustomFormulaSizesAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
int formulaCount = 0;
int resetCount = 0;
ExecuteWithScreenUpdatingSuspended(() =>
{
Expand All @@ -31,6 +32,7 @@ public Task<int> ResetCustomFormulaSizesAsync(CancellationToken cancellationToke
foreach (object candidate in equationControls)
{
cancellationToken.ThrowIfCancellationRequested();
formulaCount++;
dynamic control = candidate;
string equationId = GetEquationId(control);
if (!WordFormulaMetadataStore.TryLoadOmmlNaturalFontSize(
Expand Down Expand Up @@ -64,6 +66,8 @@ public Task<int> ResetCustomFormulaSizesAsync(CancellationToken cancellationToke
continue;
}

formulaCount++;

if (!WordFormulaMetadataStore.TryLoadOleNaturalSize(
CurrentDocument,
Convert.ToString(inlineShape.AlternativeText) ?? string.Empty,
Expand All @@ -87,7 +91,7 @@ public Task<int> ResetCustomFormulaSizesAsync(CancellationToken cancellationToke
}
});

return Task.FromResult(resetCount);
return Task.FromResult(new WordFormattingResetResult(formulaCount, resetCount));
}

public System.Threading.Tasks.Task ResetManagedEquationFormattingAsync(
Expand Down
Loading