将 XWPFRun 拆分为多个运行

2024-02-13

我正在尝试修改existingWord 文档自动将其中的某些关键字加粗。举个例子:

敏捷的棕色狐狸跳过了懒狗。 (1)

会成为:

快速棕色fox跳过懒惰者dog. (2)

我的问题是 (1) 是一次运行,(2) 变成 5 次运行(5 作为狗后面的句点不是粗体,但它是一个细节)。我得到了多次运行。完全没问题。

问题#1:

有没有一种方法可以轻松地将同一段落中的一个运行拆分为多个运行?我没能做到。

问题2:

由于我无法分割运行,因此我尝试创建一个新段落,但它确实不理想并将运行添加到其中。我已经成功地完全复制了一个段落并修改了重复段落中的运行,我保留了样式(这是预期的),但我丢失了重复段落中的注释。

理想情况下,我想就地分割运行(在段落内),但如果不可能有一个更好的克隆器:

  public static void cloneRun(XWPFRun source, XWPFRun clone) {
    CTRPr rPr = clone.getCTR().isSetRPr()
        ? clone.getCTR().getRPr()
        : clone.getCTR().addNewRPr();
    rPr.set(source.getCTR().getRPr());
    clone.setText(source.getText(0));
  }

In 如何使用 apache poi 更改特定 Word 文档的颜色? https://stackoverflow.com/questions/40318507/how-do-i-change-color-of-a-particular-word-document-using-apache-poi/40327308#40327308我已经展示了一种分割算法XWPFRuns出于格式原因。这仅用于格式化一个字符,并且不会克隆运行属性。但基本的已经显示出来了。我们必须查看整个段落,因为只有插入运行的方法。我们需要按字符循环运行文本,因为所有拆分为单词的方法都会导致标点符号出现问题,然后将单词重新组装成段落。

缺少的是一种将运行属性从原始运行克隆到新添加的运行属性的方法。这可以通过克隆底层来完成w:rPr元素。

然后整个方法就是遍历段落中的所有运行。如果我们有一个包含关键字的运行,则将运行文本拆分为字符。然后遍历该运行中的所有字符并缓冲它们。如果缓冲的字符流以关键字结尾,则将当前缓冲的除关键字之外的所有字符设置为实际运行的文本。然后为格式化关键字插入新的运行,并从原始运行克隆运行属性。将关键字设置到运行中并进行附加格式化。然后为下一个字符插入一个新的运行,并从原始运行中克隆运行属性。对于该段落中的每次运行,依此类推。

完整示例:

import java.io.*;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlCursor;

import java.util.*;
import java.awt.Desktop;

public class WordFormatWords {

 static void cloneRunProperties(XWPFRun source, XWPFRun dest) { // clones the underlying w:rPr element
  CTR tRSource = source.getCTR();
  CTRPr rPrSource = tRSource.getRPr();
  if (rPrSource != null) {
   CTRPr rPrDest = (CTRPr)rPrSource.copy();
   CTR tRDest = dest.getCTR();
   tRDest.setRPr(rPrDest);
  }
 }

 static void formatWord(XWPFParagraph paragraph, String keyword, Map<String, String> formats) {
  int runNumber = 0;
  while (runNumber < paragraph.getRuns().size()) { //go through all runs, we cannot use for each since we will possibly insert new runs
   XWPFRun run = paragraph.getRuns().get(runNumber);
   XWPFRun run2 = run;
   String runText = run.getText(0);
   if (runText != null && runText.contains(keyword)) { //if we have a run with keyword in it, then

    // This code part is to manage comment ranges.
    // Do we have commentRangeEnd immediately after the run?
    // If so then remember that in a cursor.
    XmlCursor commentRangeEndCursor = null; 
    XmlCursor cursor = run.getCTR().newCursor();
    cursor.toEndToken();
    if (cursor.hasNextToken()) {
     cursor.toNextToken();
     XmlObject commentRangeEnd = cursor.getObject();
     if (commentRangeEnd != null && commentRangeEnd instanceof CTMarkupRange) {
      commentRangeEndCursor = cursor;
     }
    }

    char[] runChars = runText.toCharArray(); //split run text into characters
    StringBuffer sb = new StringBuffer();
    for (int charNumber = 0; charNumber < runChars.length; charNumber++) { //go through all characters in that run
     sb.append(runChars[charNumber]); //buffer all characters
     runText = sb.toString();
     if (runText.endsWith(keyword)) { //if the bufferend character stream ends with the keyword  
      //set all chars, which are current buffered, except the keyword, as the text of the actual run
      run.setText(runText.substring(0, runText.length() - keyword.length()), 0); 
      run2 = paragraph.insertNewRun(++runNumber); //insert new run for the formatted keyword
      cloneRunProperties(run, run2); // clone the run properties from original run
      run2.setText(keyword, 0); // set the keyword in run
      for (String toSet : formats.keySet()) { // do the additional formatting
       if ("color".equals(toSet)) {
        run2.setColor(formats.get(toSet));
       } else if ("bold".equals(toSet)) {
        run2.setBold(Boolean.valueOf(formats.get(toSet)));
       }
      }
      run2 = paragraph.insertNewRun(++runNumber); //insert a new run for the next characters
      cloneRunProperties(run, run2); // clone the run properties from original run
      run = run2;
      sb = new StringBuffer(); //empty the buffer
     } 
    }
    run.setText(sb.toString(), 0); //set all characters, which are currently buffered, as the text of the actual run

    // This code part is to manage comment ranges.
    // If we had remembered commentRangeEnd, then move this to here now.
    if(commentRangeEndCursor != null) {
     cursor = run.getCTR().newCursor();
     cursor.toEndToken();
     if (cursor.hasNextToken()) {
      cursor.toNextToken();
      commentRangeEndCursor.moveXml(cursor);
     }
     cursor.dispose();
     commentRangeEndCursor.dispose();
    }

   }
   runNumber++;
  }
 }


 public static void main(String[] args) throws Exception {

  XWPFDocument doc = new XWPFDocument(new FileInputStream("source.docx"));

  String[] keywords = new String[]{"fox", "dog"};
  Map<String, String> formats = new HashMap<String, String>();
  formats.put("bold", "true");
  formats.put("color", "DC143C");

  for (XWPFParagraph paragraph : doc.getParagraphs()) { //go through all paragraphs
   for (String keyword : keywords) {
    formatWord(paragraph, keyword, formats);
   }
  }

  FileOutputStream out = new FileOutputStream("result.docx");
  doc.write(out);
  out.close();
  doc.close();

  System.out.println("Done");
  Desktop.getDesktop().open(new File("result.docx"));

 }
}

这段代码还关心XML标记范围元素,例如commentRangeEnd紧接着跑步之后r元素。此类标记范围元素用于标记其他元素组的开始和结束。例如,应用注释的一组文本运行元素位于commentRangeStart and commentRangeEnd具有相同的id.

如果在需要分割的运行之后立即跟随commentRangeEnd,然后我们在光标中记住它。然后在分割运行后我们移动这个commentRangeEnd紧接着最后一个新插入的运行。所以评论应该保持正确。

当然,即使这样也会有一些缺点,因为方法很笨拙。Microsoft Word有时将文本存储在文本运行中。对于这个问题,没有唯一的通用解决方案Microsoft Word是源头。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将 XWPFRun 拆分为多个运行 的相关文章

随机推荐

  • ISO 8601 中的时间戳 - 最后 6 位数字 yyyy-MM-dd'T'HH:mm:ss。

    我的时间戳看起来像这样 2015 03 21T11 08 14 859831 2015 03 21T11 07 22 956087 我读了一篇关于 ISO 8601 的 Wiki 文章 https en wikipedia org wiki
  • pandas - 计算另一列中每个唯一值的 DataFrame 中值的出现次数

    假设我有一个数据帧 term score 0 this 0 1 that 1 2 the other 3 3 something 2 4 anything 1 5 the other 2 6 that 2 7 this 0 8 someth
  • MySQL按日期分组的累积和

    我知道有一些与此相关的帖子 但我的情况有点不同 我想在这方面获得一些帮助 我需要从数据库中提取一些数据 这些数据是每天交互的累积计数 目前这就是我所拥有的 SELECT e Date AS e date count e ID AS num
  • 如何将 dll 导入到在网络服务器上运行的 ASP.NET Web 应用程序中

    我在 VS 2010 中创建了一个 dll 我将其放入网络服务器上的 app code 文件夹中 然后我通过尝试使用来访问它imports GetWebPageData 如果这意味着什么的话 dll 位于 bin debug 下的 vs 项
  • 如何在Java中的不同类中使用相同的对象

    假设我有 3 个 java 类 A B 和 C 我需要创建一个在 A 和 B 中都使用的 C 类对象 但单独创建该对象的问题是类 c 的构造函数被调用了 2 次 但我希望构造函数只被调用一次 所以我想将A类中创建的对象使用到B类中 因此 创
  • 如何从 Web api 控制器返回文件?

    我正在使用 MVC 5 Web Api 控制器 我想返回一个文件 Route public HttpResponseMessage GetFile var statusCode HttpStatusCode OK FileStream fi
  • jQuery 倒计时有问题吗?函数serverSync:服务器时间

    serverSync serverTime函数从服务器返回值 但我检查了服务器和客户端时间是否相同 当我调用服务器与服务器同步时 它不会显示倒计时 帮我 function var shortly new Date var newTime n
  • SQL更新记录,每次从1开始递增值

    我使用单个插入语句将批量记录添加到表中 我希望为每个新批次分配递增的数字 但每次都从 1 开始 所以 如果我有 Batch Name IncementingValue 1 Joe 1 1 Pete 2 1 Andy 3 2 Sue 1 2
  • Scrapy好像没有做DFO

    我有一个网站 我的爬虫需要遵循一定的顺序 例如 在开始进行 a2 之前 它需要先进行 a1 b1 c1 等操作 a b 和 c 中的每一个都由不同的解析函数处理 并且相应的 url 在 Request 对象中创建并生成 下面粗略地说明了我正
  • 不允许的参数嵌套属性 -rails

    我正在尝试向 2 个表提交表单 但不知何故我收到了此语法错误unexpected n 在这条线上joins sources landslide id and found unpermitted parameter sources在滑坡参数中
  • 无法从 Sequel gem 连接 mysql

    当我尝试从 Sequel 连接到 MySQL 时 我收到这些错误 require rubygems require sequel DB Sequel connect adapter gt mysql user gt root host gt
  • DependencyProperty 未在 NotifyCollectionChanged 事件上通知 UI

    我正在使用一个自定义控件 该控件具有选定的项目依赖属性 我已将其连接到集合更改事件 但未通知 UI 并且 PropertyChanged 事件始终为空 通常我会说这是一个数据上下文问题 但我无法更改控件上的数据上下文 因为不会显示任何数据
  • 如何使用Matlab的bsxfun求解累加和

    我有以下 慢 代码 A is n by m matrix B is n by m by d matrix C is n by m by d matrix R is 1 by d vector A zeros n m for i 1 d A
  • 将 XML 实体保留在输出中 (jSoup)

    我正在使用 jsoup 进行一些 xml 处理 问题是 它正在替换 xml 实体 即 187 与 html 实体 raquo 我如何保留原始 xml 实体 常规脚本 import org jsoup Jsoup import org jso
  • Vue 警告客户端渲染的虚拟 DOM 树与服务器渲染的内容不匹配

    我正在尝试使用 Vue js 和 Nuxt 在表中循环 tr 但是当我加载页面时出现以下错误 vue runtime esm js 2b0e 619 Vue warn 客户端渲染 虚拟 DOM 树与服务器渲染的内容不匹配 这是 可能是由不正
  • Eclipse 停止编译 RenderScript 文件

    我的 Android 项目中有一个 RenderScript rs 文件 SomeScript rs 在我开发它的过程中 它运行得很好 但后来我在 Eclipse 中清理了工作区 现在它似乎没有将该文件识别为 RenderScript 文件
  • Symfony:错误:尝试调用函数“ctype_digit”

    我尝试在服务器上安装 Symfony 3 3 项目时遇到错误 我正在使用 Apache HTTP 服务器和 PHP 7 0 当我访问 app dev php 时 它显示该错误 UndefinedFunctionException Attem
  • 如何删除旧的不需要的设备?

    所以这与我之前发布的问题有关 如何删除4 3模拟器 https stackoverflow com q 10834817 96716 我愚蠢地安装了 iOS 5 0 模拟器 现在我上一篇文章中提供的技术不起作用了 当您删除 5 0sdk 然
  • 自定义语言的 Gradle 插件

    我有一种自定义语言 假设它是 MyLang 但它可以是任何语言 我想为其制作一个插件 该插件需要 能够识别 DSL 给定语言的源集 能够使用可执行文件 编译器 来编译它们 我能够创建一个带有编译任务 还为空 的插件 并使用 Language
  • 将 XWPFRun 拆分为多个运行

    我正在尝试修改existingWord 文档自动将其中的某些关键字加粗 举个例子 敏捷的棕色狐狸跳过了懒狗 1 会成为 快速棕色fox跳过懒惰者dog 2 我的问题是 1 是一次运行 2 变成 5 次运行 5 作为狗后面的句点不是粗体 但它