mirror of
https://github.com/iOfficeAI/OfficeCLI
synced 2026-04-21 13:37:23 +00:00
fix(word-html): render tracked changes, rotated cell text, and cell noWrap
Render-comparison testing found several cell/revision rendering gaps: - Tracked insertions (<w:ins>) previously rendered as plain text, losing the author annotation. Now wrap in a .track-ins span with underline + green color, with the author name in a tooltip. - Tracked deletions (<w:del>) were dropped entirely, leaving the reviewer unable to see what was removed. Now render the deleted text inside a .track-del span with strikethrough + red color. - Cell <w:textDirection> btLr/tbRl was ignored — text stayed horizontal where Word rotates 90°. Emit CSS writing-mode:vertical-rl; btLr adds a 180° rotation to flip the reading direction. - Cell <w:noWrap/> was dropped — now emits white-space:nowrap so cell content doesn't wrap.
This commit is contained in:
parent
ee0d067e1e
commit
af7fca565e
2 changed files with 34 additions and 2 deletions
|
|
@ -1165,6 +1165,25 @@ public partial class WordHandler
|
|||
parts.Add($"width:{w / 50.0:0.#}%");
|
||||
}
|
||||
|
||||
// Cell text direction (tcDir): rotate text 90° or 270° via CSS writing-mode + transform
|
||||
// Common values: btLr (bottom→top, left→right = 90° CCW), tbRl (top→bottom, right→left = 90° CW)
|
||||
var tcDir = tcPr.GetFirstChild<TextDirection>()?.Val?.InnerText;
|
||||
if (tcDir != null)
|
||||
{
|
||||
var wm = tcDir switch
|
||||
{
|
||||
"btLr" => "vertical-rl;transform:rotate(180deg)", // read bottom-up
|
||||
"tbRl" => "vertical-rl", // read top-down
|
||||
"lrTb" or null => null, // default horizontal
|
||||
_ => null,
|
||||
};
|
||||
if (wm != null) parts.Add($"writing-mode:{wm}");
|
||||
}
|
||||
|
||||
// Cell noWrap — prevents content wrapping within the cell
|
||||
if (tcPr.NoWrap != null)
|
||||
parts.Add("white-space:nowrap");
|
||||
|
||||
// Padding — add vertical compensation for CSS line-height:1 clipping glyph ascenders
|
||||
const double CellPadVComp = 3.0; // pt
|
||||
var margins = tcPr?.TableCellMargin;
|
||||
|
|
|
|||
|
|
@ -93,13 +93,26 @@ public partial class WordHandler
|
|||
}
|
||||
else if (child.LocalName is "ins" or "moveTo")
|
||||
{
|
||||
// Tracked insertions — render their child runs
|
||||
// Tracked insertions — underline to match Word's default revision mark style
|
||||
var author = child.GetAttributes().FirstOrDefault(a => a.LocalName == "author").Value;
|
||||
var authorAttr = string.IsNullOrEmpty(author) ? "" : $" title=\"Inserted by {HtmlEncodeAttr(author)}\"";
|
||||
sb.Append($"<span class=\"track-ins\" style=\"text-decoration:underline;color:#2E7D32\"{authorAttr}>");
|
||||
foreach (var insRun in child.Elements<Run>())
|
||||
RenderRunHtml(sb, insRun, para);
|
||||
sb.Append("</span>");
|
||||
}
|
||||
else if (child.LocalName is "del" or "moveFrom")
|
||||
{
|
||||
// Tracked deletions — skip (deleted content should not be displayed)
|
||||
// Tracked deletions — strikethrough with color, preserving the deleted text
|
||||
// The delText inside del runs carries the actual deleted content; we render it so
|
||||
// a reader of the preview can see what was removed.
|
||||
var author = child.GetAttributes().FirstOrDefault(a => a.LocalName == "author").Value;
|
||||
var authorAttr = string.IsNullOrEmpty(author) ? "" : $" title=\"Deleted by {HtmlEncodeAttr(author)}\"";
|
||||
var delText = string.Concat(child.Descendants()
|
||||
.Where(e => e.LocalName == "delText" || e.LocalName == "t")
|
||||
.Select(e => e.InnerText));
|
||||
if (!string.IsNullOrEmpty(delText))
|
||||
sb.Append($"<span class=\"track-del\" style=\"text-decoration:line-through;color:#C62828\"{authorAttr}>{HtmlEncode(delText)}</span>");
|
||||
}
|
||||
else if (child is Hyperlink hyperlink)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue