通过DocumentVisitor读取锁定区域,当存在锁定的表格时,会出现读到未锁定区域的问题

public class NoEditableRangePrinter extends DocumentVisitor {
    public NoEditableRangePrinter() {
        mBuilder = new StringBuilder();
    }



    public String toText() {
        String result = mBuilder.toString();
        // 页脚图片会被识别为 字符串PAGE 过滤掉 问题范本 FBOT20230123
        return StringUtils.replace(result, "PAGE", "");
    }

    public void reset() {
        mBuilder.setLength(0);
        mInsideEditableRange = true;
    }

    // <summary>
    // Called when an EditableRangeStart node is encountered in the document.
    // </summary>

    @Override
    public int visitDocumentStart(Document doc) throws Exception {
        mInsideEditableRange = true;
        return VisitorAction.CONTINUE;
    }

    /**
     * VisitorAction
     * @param editableRangeStart
     * @return
     */
    @Override
    public int visitEditableRangeStart(EditableRangeStart editableRangeStart) {

        mInsideEditableRange = true;

        return VisitorAction.CONTINUE;
    }

    // <summary>
    // Called when an EditableRangeEnd node is encountered in the document.
    // </summary>
    /**
     * VisitorAction
     * @param editableRangeEnd
     * @return
     */
    @Override
    public int visitEditableRangeEnd(final EditableRangeEnd editableRangeEnd) {
//            mBuilder.append(" -- End of editable range -- " + "\r\n");

        mInsideEditableRange = false;

        return VisitorAction.CONTINUE;
    }

    // <summary>
    // Called when a Run node is encountered in the document. This visitor only records runs that are inside editable ranges.
    // </summary>

    /**
     * doing Visitor
     * @param run
     * @return
     */
    @Override
    public int visitRun(final Run run) {
        if (!mInsideEditableRange) {
            mBuilder.append(StringUtils.trim(run.getText()));
        }

        return VisitorAction.CONTINUE;
    }

    private boolean mInsideEditableRange;
    private final StringBuilder mBuilder;
}

示例文件
a.docx (13.7 KB)

执行

getNoEditableString("a.docx")

期望得到空字符串
结果得到 “123456”

使用的是

<dependency>
    <groupId>com.aspose</groupId>
    <artifactId>aspose-words</artifactId>
    <version>23.6</version>
</dependency>

@chazz 出现此问题的原因是 EditableRangeEnd 不强制位于 EditableRangeStart 之后,可能存在嵌套,但您的代码没有考虑到这一点:

请尝试像这样修改您的代码:

public class NoEditableRangePrinter extends DocumentVisitor {
    public NoEditableRangePrinter() {
        mBuilder = new StringBuilder();
    }
        
    public String toText() {
        String result = mBuilder.toString();
        // 页脚图片会被识别为 字符串PAGE 过滤掉 问题范本 FBOT20230123
        return result.replace("PAGE", "");
    }
        
    public void reset() {
        mBuilder.setLength(0);
        mEditableRangeNestingList = new ArrayList<EditableRangeEnd>();
    }
        
    // <summary>
    // Called when an EditableRangeStart node is encountered in the document.
    // </summary>
        
    @Override
    public int visitDocumentStart(Document doc) throws Exception {
        return VisitorAction.CONTINUE;
    }
        
    /**
     * VisitorAction
     * @param editableRangeStart
     * @return
     */
    @Override
    public int visitEditableRangeStart(EditableRangeStart editableRangeStart) throws Exception {
    
        mEditableRangeNestingList.add(editableRangeStart.getEditableRange().getEditableRangeEnd());
        System.out.println(editableRangeStart.getId() + "          " + mEditableRangeNestingList.size());
        return VisitorAction.CONTINUE;
    }
        
    // <summary>
    // Called when an EditableRangeEnd node is encountered in the document.
    // </summary>
    /**
     * VisitorAction
     * @param editableRangeEnd
     * @return
     */
    @Override
    public int visitEditableRangeEnd(final EditableRangeEnd editableRangeEnd) {
    
        mEditableRangeNestingList.remove(editableRangeEnd);
        System.out.println(editableRangeEnd.getId() + "          " + mEditableRangeNestingList.size());
        return VisitorAction.CONTINUE;
    }
        
    // <summary>
    // Called when a Run node is encountered in the document. This visitor only records runs that are inside editable ranges.
    // </summary>
        
    /**
     * doing Visitor
     * @param run
     * @return
     */
    @Override
    public int visitRun(final Run run) {
        if (!isInsideEditableRange()) {
            mBuilder.append(run.getText().trim());
        }
            
        return VisitorAction.CONTINUE;
    }
        
    private boolean isInsideEditableRange()
    {
        return mEditableRangeNestingList.size()>0;
    }
        
    private ArrayList<EditableRangeEnd> mEditableRangeNestingList = new ArrayList<EditableRangeEnd>();
    private final StringBuilder mBuilder;
}
1 Like

a.docx (11.5 KB)

@alexey.noskov 请看一下这个文件呢
按照您提供给我的逻辑 可以取到表格外的锁定内容
但是表格内的锁定区域 99999 无法被提取到
是否有其他的解决办法呢

@chazz 实际上并不清楚为什么 MS Word 将“999999”文本显示为受保护,因为它在可编辑范围内。 即使检查内部文档 XML 也可以清楚地看到它在可编辑范围内:

<w:tr>
	<w:tblPrEx>
		<w:tblW w:w="0" w:type="auto" />
		<w:tblInd w:w="0" w:type="dxa" />
		<w:tblCellMar>
			<w:top w:w="0" w:type="dxa" />
			<w:left w:w="108" w:type="dxa" />
			<w:bottom w:w="0" w:type="dxa" />
			<w:right w:w="108" w:type="dxa" />
		</w:tblCellMar>
	</w:tblPrEx>
	<w:tc>
		<w:tcPr>
			<w:tcW w:w="1420" w:type="dxa" />
		</w:tcPr>
		<w:p>
			<w:permStart w:id="4" w:colFirst="0" w:colLast="0" w:edGrp="everyone" />
			<w:permStart w:id="5" w:colFirst="1" w:colLast="1" w:edGrp="everyone" />
			<w:permStart w:id="6" w:colFirst="3" w:colLast="3" w:edGrp="everyone" />
			<w:permStart w:id="7" w:colFirst="4" w:colLast="4" w:edGrp="everyone" />
			<w:permStart w:id="8" w:colFirst="5" w:colLast="5" w:edGrp="everyone" />
			<w:r>
				<w:rPr>
					<w:rFonts w:hint="eastAsia" />
				</w:rPr>
				<w:t>2</w:t>
			</w:r>
		</w:p>
	</w:tc>
	<w:tc>
		<w:tcPr>
			<w:tcW w:w="1420" w:type="dxa" />
		</w:tcPr>
		<w:p />
	</w:tc>
	<w:tc>
		<w:tcPr>
			<w:tcW w:w="1420" w:type="dxa" />
		</w:tcPr>
		<w:p>
			<w:pPr>
				<w:rPr>
					<w:rFonts w:hint="eastAsia" />
				</w:rPr>
			</w:pPr>
			<w:r>
				<w:rPr>
					<w:rFonts w:hint="eastAsia" />
				</w:rPr>
				<w:t>9</w:t>
			</w:r>
			<w:r>
				<w:t>99999</w:t>
			</w:r>
		</w:p>
	</w:tc>
	<w:tc>
		<w:tcPr>
			<w:tcW w:w="1420" w:type="dxa" />
		</w:tcPr>
		<w:p />
	</w:tc>
	<w:tc>
		<w:tcPr>
			<w:tcW w:w="1421" w:type="dxa" />
		</w:tcPr>
		<w:p />
	</w:tc>
	<w:tc>
		<w:tcPr>
			<w:tcW w:w="1421" w:type="dxa" />
		</w:tcPr>
		<w:p />
	</w:tc>
</w:tr>
<w:permEnd w:id="4" />
<w:permEnd w:id="5" />
<w:permEnd w:id="6" />
<w:permEnd w:id="7" />
<w:permEnd w:id="8" />

正如您在第 4、5、6、7、8 行第一个单元格的第一段中看到的,可编辑范围开始,在行结束后结束。

@alexey.noskov 操作中 在设置该文档时是分开设置的 将表格的前半部分设置为可编辑 然后避开9999的区域 将表格的后半部分设置为可编辑,最后添加文档保护

@chazz 感谢您提供更多信息。 是的,我了解该文档是如何创建的。 但我在文档模型的这一行中看不到受保护单元格和可编辑单元格之间有任何区别。 因此,目前尚不清楚 MS Word 如何确定该特定单元格受到保护。 Aspose.Words对象文档模型完全对应内部文档结构。

1 Like

1.docx (10.0 KB)

@alexey.noskov 请帮忙查看这个case 按照我们的代码逻辑 应该读不到内容的

@chazz 您能否更详细地说明问题。 正如我所看到的,所附文档包含 4 个带有 RUN 节点的段落。

我们期望读取到整个文档中所有未锁定区域的内容,这个文档应该是没有锁定区域的,但是依然可以读取出来

@chazz 提供的文档没有任何受保护区域,因此我在这里没有看到任何问题。

是的 正是因为没有任何受保护区域 所以按照您上面提供的代码 不应该读取到内容,但目前会读到所有的Text内容

@chazz 您的文档不受保护:

Document document = new Document("C:\\Temp\\in.docx");
System.out.println(ProtectionType.toString(document.getProtectionType()));

上面的代码打印除可编辑范围内的内容之外的文档内容。 因此,预计代码会打印文档的所有内容,因为没有可编辑范围,即没有可排除的内容。

可能我的需求没有描述清楚
我希望输出各类word中所有锁定区域文本内容
1、没有任何锁定区域的文档希望可以输出为空
1.docx (10.0 KB)
输出 “”
2、有锁定区域的输出锁定区域的文本内容
a.docx (11.5 KB)
输出 “888 999999”
a.docx (13.7 KB)
输出为 “”
其中场景二 按照您提供的代码 工作良好,但是场景一目前是错误的,有什么好的解决办法吗

1 Like

@chazz 您应该如上所述检查该文档是否受到保护。 在第一种情况下,文档不受保护,因此没有受限制的内容。

1 Like