当前位置: 首页 > news >正文

Word XML 批注范围克隆处理器

该类用于处理 Word 文档(XML 结构)中被批注标记的文本范围,
实现指定内容的深度克隆,并将其插入到目标节点之后。
适用于在生成或修改 .docx 文件时复制批注内容块。

/*** Word XML 批注范围克隆处理器* * 该类用于处理 Word 文档(XML 结构)中被批注标记的文本范围,* 实现指定内容的深度克隆,并将其插入到目标节点之后。* 适用于在生成或修改 .docx 文件时复制批注内容块。*/
class WordCommentRangeCloner
{/*** 存储文档节点的线性列表,用于通过索引快速访问* @var array*/private $domList = [];/*** 记录每个节点索引关联的命名信息,用于模板或块引用* 格式: [index] => [['type', 'name'], ...]* @var array*/private $domIdxToName = [];/*** 存储按名称组织的块引用,支持多实例* 格式: ['blockName#1'] => ['type' => [index1, index2, ...]]* @var array*/private $blocks = [];/*** 扩展索引映射,用于记录节点间的附加关联关系* 格式: [index] => [relatedIndex1, relatedIndex2, ...]* @var array*/private $idxExtendIdxs = [];/*** 将克隆的节点插入到目标节点之后** 如果目标节点有下一个兄弟节点,则插入到其前;* 否则作为父节点的最后一个子节点追加。** @param \DOMNode $copy 要插入的已克隆节点* @param \DOMNode $targetNode 目标位置的参考节点* @return void*/public function insertAfter($copy, $targetNode){if ($nextSibling = $targetNode->nextSibling) {// 如果存在下一个兄弟节点,则插入到它前面if ($parentNode = $nextSibling->parentNode) {$parentNode->insertBefore($copy, $nextSibling);}} else {// 否则作为父节点的最后一个子节点添加if ($parentNode = $targetNode->parentNode) {$parentNode->appendChild($copy);}}}/*** 克隆指定索引的节点及其子树,并插入到指定位置后** 此方法用于复制 Word 文档中被批注包裹的内容块。* 支持维护索引、命名、关联等元数据一致性。** @param int $nodeIdx 要克隆的原始节点在 domList 中的索引* @param int $endNodeIdx 插入位置的参考节点索引(插入到该节点之后)* @param string $name 原始块名称(用于命名新实例)* @param int $idx 克隆实例编号(用于区分多个副本,如 #1, #2)* @param int $reIdx 递归层级编号(嵌套克隆时使用)* @return int 返回克隆子树在 domList 中的起始索引*/private function cloneNode($nodeIdx, $endNodeIdx, $name, $idx, $reIdx = 0){$originalNode = $this->domList[$nodeIdx];$clonedNode = clone $originalNode;// 为克隆节点重建树索引(使用引用传递维护连续索引)$startIndex = count($this->domList);$this->treeToList($clonedNode, $startIndex);// 构建原始索引到新索引的映射表$oldToNewMap = $this->buildIndexMap($originalNode, $clonedNode);// 更新命名映射关系(domIdxToName 和 blocks)$this->updateNameMappings($originalNode, $oldToNewMap, $idx);// 更新扩展索引关联关系$this->updateExtendIdxs($originalNode, $oldToNewMap);// 将克隆的节点插入到目标节点之后$this->insertAfter($clonedNode, $this->domList[$endNodeIdx]);return $startIndex;}/*** 遍历节点树并生成线性节点列表,同时设置每个节点的起止索引** 用于重建克隆节点或新插入节点的索引结构。** @param \DOMNode $node 当前处理的节点* @param int|null $index 引用传递的当前索引值* @return void*/protected function treeToList($node, &$index = null){if ($index === null) {$index = count($this->domList);}if (is_null($node)) {return;}$node->idxBegin = $index;$node->tagList = [];$this->domList[$index++] = $node;if ($node->hasChildNodes()) {foreach ($node->childNodes as $childNode) {if ($childNode->nodeType !== XML_TEXT_NODE) { // 非文本节点$tagName = $childNode->tagName;$node->tagList[$tagName][] = $childNode;$this->treeToList($childNode, $index);// 合并后代标签列表foreach ($childNode->tagList as $tag => $nodes) {foreach ($nodes as $n) {$node->tagList[$tag][] = $n;}}}}}$node->idxEnd = $index - 1;}/*** 构建原始节点索引到克隆节点索引的映射表** @param \DOMNode $orig 原始节点* @param \DOMNode $clone 克隆节点* @return array 映射表 [oldIndex => newIndex]*/private function buildIndexMap($orig, $clone){$map = [];$this->walkNodeAndMap($orig, $clone, $map);return $map;}/*** 递归遍历原始与克隆节点,建立索引映射** @param \DOMNode $orig* @param \DOMNode $clone* @param array &$map 引用传递的映射表* @return void*/private function walkNodeAndMap($orig, $clone, &$map){$map[$orig->idxBegin] = $clone->idxBegin;if ($orig->hasChildNodes()) {$origChildren = iterator_to_array($orig->childNodes);$cloneChildren = iterator_to_array($clone->childNodes);$j = 0;foreach ($origChildren as $child) {if ($child->nodeType !== XML_TEXT_NODE) {$map = $j < count($cloneChildren) ? $cloneChildren[$j] : null;if ($map) {$this->walkNodeAndMap($child, $map, $map);$j++;}}}}}/*** 更新命名映射表(domIdxToName 和 blocks)** 为克隆节点分配新名称(如 name#1),并注册到 blocks 中** @param \DOMNode $originalNode 原始节点* @param array $oldToNewMap 索引映射表* @param int $cloneIdx 克隆编号* @return void*/private function updateNameMappings($originalNode, $oldToNewMap, $cloneIdx){$begin = $originalNode->idxBegin;$end = $originalNode->idxEnd;for ($i = $begin; $i <= $end; $i++) {if (isset($this->domIdxToName[$i])) {$newIdx = $oldToNewMap[$i];$nameTemps = $this->domIdxToName[$i];foreach ($nameTemps as $key => $nameTemp) {$newName = $nameTemp[1] . '#' . $cloneIdx;$nameTemps[$key] = [$nameTemp[0], $newName];$this->blocks[$newName][$nameTemp[0]][] = $newIdx;}$this->domIdxToName[$newIdx] = $nameTemps;}}}/*** 更新扩展索引关联关系** 将原始节点的 idxExtendIdxs 映射到克隆节点** @param \DOMNode $originalNode 原始节点* @param array $oldToNewMap 索引映射表* @return void*/private function updateExtendIdxs($originalNode, $oldToNewMap){$begin = $originalNode->idxBegin;$end = $originalNode->idxEnd;for ($i = $begin; $i <= $end; $i++) {if (isset($this->idxExtendIdxs[$i])) {$newIdx = $oldToNewMap[$i];$this->idxExtendIdxs[$newIdx] = [];foreach ($this->idxExtendIdxs[$i] as $oldRefId) {if (isset($oldToNewMap[$oldRefId])) {$this->idxExtendIdxs[$newIdx][] = $oldToNewMap[$oldRefId];}}}}}
}

http://www.lryc.cn/news/616048.html

相关文章:

  • React:useEffect 与副作用
  • MyBatis的xml中字符串类型判空与非字符串类型判空处理方式
  • 秋招春招实习百度笔试百度管培生笔试题库百度非技术岗笔试|笔试解析和攻略|题库分享
  • wordpress语言包制作工具
  • python正则表达式里面有特殊符号如何处理
  • 亚麻云之静态资源管家——S3存储服务实战
  • Day41--动态规划--121. 买卖股票的最佳时机,122. 买卖股票的最佳时机 II,123. 买卖股票的最佳时机 III
  • LeetCode 组合总数
  • AI质检数据准备利器:基于Qt/QML 5.14的图像批量裁剪工具开发实战
  • Python 2025:最新技术趋势与展望
  • Text2SQL 自助式数据报表开发(Chat BI)
  • 解决 .NET Core 6.0 + PostgreSQL 网站首次连接缓慢问题
  • 嵌入式软件分层架构的设计原理与实践验证(有限状态机理解及结构体封装理解)
  • spring-ai整合PGVector实现RAG
  • WinForm之TreeView控件
  • Baumer高防护相机如何通过YoloV8深度学习模型实现道路坑洼的检测识别(C#代码UI界面版)
  • [激光原理与应用-223]:机械 - 机加厂加工机械需要2D还是3D图?
  • jvm有哪些垃圾回收器,实际中如何选择?
  • 本地WSL部署接入 whisper + ollama qwen3:14b 总结字幕校对增强版
  • Code Exercising Day 10 of “Code Ideas Record“:StackQueue part02
  • 低版本 IntelliJ IDEA 使用高版本 JDK 语言特性的问题
  • IDEA 如何导入系统设置
  • 基于ECharts的智慧社区数据可视化
  • IDEA 快捷编辑指南
  • IntelliJ IDEA 2025.2 重磅发布
  • OneCode 3.0 可视化功能全面分析:从开发者到用户的全场景解析
  • [激光原理与应用-214]:设计 - 皮秒紫外激光器 - 电控设计,高精度、高可靠性与智能化的全链路方案
  • 【渲染流水线】[几何阶段]-[归一化NDC]以UnityURP为例
  • SpringMVC的知识点总结
  • JDBC的连接过程(超详细)