使用 Apache POI 更改 XLSX 表单控件位置

Changing XLSX form control location with Apache POI

我有一些包含表单控件的 xlsm 文件。我想以编程方式将每个 sheet 上的特定按钮向下移动几行。我的第一个希望是做这样的事情:

FileInputStream inputStream = new FileInputStream(new File("t.xlsm"));
XSSFWorkbook wb = new XSSFWorkbook(inputStream);
XSSFSheet xs = (XSSFSheet)wb.getSheetAt(1);
RelationPart rp = xs.getRelationParts().get(0);
XSSFDrawing drawing = (XSSFDrawing)rp.getDocumentPart();

for(XSSFShape sh : drawing.getShapes()){
    XSSFClientAnchor a = (XSSFClientAnchor)sh.getAnchor();
    if (sh.getShapeName().equals("Button 2")) {
        a.setRow1(a.getRow1()+10);
        a.setRow2(a.getRow2()+10);
    }
}

但是,XSSFDrawing.getShapes() 给出的形状对象是副本,在 wb.write() 之后对它们的任何更改都不会反映在文档中。

我尝试了其他几种方法,例如获取 CTShape 并在其中解析 XML,但事情很快就变得棘手了。

是否有推荐的方法来通过 POI 管理这样的表单控件?

我最终直接摆弄了 XML:

wb = new XSSFWorkbook(new File(xlsmFile));
XSSFSheet s = wb.getSheet("TWO");
XmlObject[] subobj = s.getCTWorksheet().selectPath(declares+
            " .//mc:AlternateContent/mc:Choice/main:controls/mc:AlternateContent/mc:Choice/main:control");

String targetButton = "Button 2";
int rowsDown = 10;

for (XmlObject obj : subobj) {
    XmlCursor cursor = obj.newCursor();
    cursor.push();
    String attrName = cursor.getAttributeText(new QName("name"));
    if (attrName.equals(targetButton)) {
        cursor.selectPath(declares+" .//main:from/xdr:row");
        if (!cursor.toNextSelection()) {
            throw new Exception();
        }
        int newRow = Integer.parseInt(cursor.getTextValue()) + rowsDown;
        cursor.setTextValue(Integer.toString(newRow));

        cursor.pop();
        cursor.selectPath(declares+" .//main:to/xdr:row");
        if (!cursor.toNextSelection()) {
            throw new Exception();
        }
        newRow = Integer.parseInt(cursor.getTextValue()) + rowsDown;
        cursor.setTextValue(Integer.toString(newRow));
    }
    cursor.dispose();
}

这会将命名按钮向下移动 10 行。我必须找到按钮名称(这可能不容易通过 Excel 找到,我直接检查了文件)。我猜这对正在使用的 Excel 版本非常敏感。