XSSF (POI) - 更改枢轴上的字体 table

XSSF (POI) - Changing the font on a pivot table

我正在使用 Apache POI 3.12:

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.12</version>
</dependency>

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.12</version>
</dependency>

如何更改枢轴内的字体 table? 在将一个单元格 (I7) 的字体更改为 8pt 大小后检查生成的 .xlsx 显示以下更改:

styles.xml,作为第二个条目的标签内:

<font>
    <sz val="8"/>
    <color indexed="8"/>
    <name val="Calibri"/>
    <family val="2"/>
    <scheme val="minor"/>
</font>

within the <cellXfs> tag as the 5th entry:
<xf numFmtId="0" fontId="1" fillId="0" borderId="0" xfId="0" applyNumberFormat="1" applyFont="1"/>

New tag: dxfs:
<dxfs count="1">
    <dxf>
        <font>
            <sz val="8"/>
        </font>
    </dxf>
</dxfs>

pivottable.xml

<formats count="1">
    <format dxfId="0">
        <pivotArea collapsedLevelsAreSubtotals="1" fieldPosition="0">
        <references count="2">
        <reference field="4294967294" count="1" selected="0">
        <x v="0"/>
        </reference>
        <reference field="0" count="1">
        <x v="0"/>
        </reference>
        </references>
        </pivotArea>
    </format>
</formats>

sheet1.xml

<c r="I7" s="4">

注意:我可能会将此作为自我回答关闭,因为我仍在尝试自己解决它。但是,我已经在这里待了将近一个星期。 POI Pivot table sample

这是一个部分答案,因为它需要使用 excel 来设置而不是纯 poi。

一般步骤:

  1. 在 excel 中创建一个包含枢轴 table 的电子表格,或者使用 sample 作为 poi。
  2. 打开 excel 中的电子表格并保存
  3. 在 poi 中打开电子表格。
  4. 创建 CTDxfs 条目。这是 "font" table 秒。
  5. 使用 CTDXfs id 创建一个 pivotArea 定义。

关于代码:

private static CTFormats getFormats(XSSFPivotTable pivotTable) {
    CTFormats formats = pivotTable.getCTPivotTableDefinition().getFormats();
    if(formats==null)
        formats=pivotTable.getCTPivotTableDefinition().addNewFormats();
    return formats;
}
private static int createDXFs(XSSFWorkbook wb,int font) {
    CTDxfs dxfs=wb.getStylesSource().getCTStylesheet().getDxfs();
    if(dxfs==null)
        dxfs=wb.getStylesSource().getCTStylesheet().addNewDxfs();
    dxfs.setCount(dxfs.getCount()+1);
    CTDxf dxf=dxfs.addNewDxf();
    CTFontSize fontSize=dxf.addNewFont().addNewSz();
    fontSize.setVal(font);
    return (int) dxfs.getCount()-1;
}
public static void setAxisFont(CTFormats pivotTableFormats,int dxfId) {
    CTFormat format=pivotTableFormats.addNewFormat();
    format.setDxfId(dxfId);
    CTPivotArea pivotArea = format.addNewPivotArea();
    pivotArea.setDataOnly(false);
    pivotArea.setLabelOnly(true);
    pivotArea.setOutline(false);
    pivotArea.setFieldPosition(0L);
    pivotArea.setAxis(STAxis.AXIS_ROW);
    pivotArea.setType(STPivotAreaType.BUTTON);
}
public static void setColHeaderFont(CTFormats pivotTableFormats,int dxfId,int colInd) {
    CTFormat format=pivotTableFormats.addNewFormat();
    format.setDxfId(dxfId);
    CTPivotArea pivotArea = format.addNewPivotArea();
    pivotArea.setDataOnly(false);
    pivotArea.setLabelOnly(true);
    pivotArea.setOutline(false);
    CTPivotAreaReferences references = pivotArea.addNewReferences();
    CTPivotAreaReference reference = references.addNewReference();

    reference.setField(new Long(Integer.MAX_VALUE)*2);
    CTIndex x = reference.addNewX();
    x.setV(colInd); //Column
}
public static void setLabelFont(CTFormats pivotTableFormats,int dxfId, int rowInd) {
    CTFormat format=pivotTableFormats.addNewFormat();
    format.setDxfId(dxfId);
    CTPivotArea pivotArea = format.addNewPivotArea();
    pivotArea.setDataOnly(false);
    pivotArea.setLabelOnly(true);
    pivotArea.setFieldPosition(0L);
    CTPivotAreaReferences references = pivotArea.addNewReferences();
    CTPivotAreaReference reference = references.addNewReference();

    reference.setField(0L);
    CTIndex x = reference.addNewX();
    x.setV(rowInd); //Row
}
public static void setDataElementFont(CTFormats pivotTableFormats,int dxfId,int col,int row) {
    CTFormat format=pivotTableFormats.addNewFormat();
    format.setDxfId(dxfId);
    CTPivotArea pivotArea = format.addNewPivotArea();
    //Default values, don't need to explicitly define.
    //pivotArea.setDataOnly(true);
    //pivotArea.setLabelOnly(false);
    CTPivotAreaReferences references = pivotArea.addNewReferences();
    CTPivotAreaReference reference = references.addNewReference();

    reference.setField(new Long(Integer.MAX_VALUE)*2);
    CTIndex x = reference.addNewX();
    x.setV(col); //Column
    reference = references.addNewReference();
    reference.setField(0L);
    x = reference.addNewX();
    x.setV(row); //Row
}

备注:

  • setOutline(false) 需要访问列 headers.
  • setDataOnly(false) 允许更改影响标签和数据。
  • setLabelOnly(true) 限制仅更改标签值。如果你想改变整个column/row,设置为false。
  • unsigned int max 的引用字段值将引用定义为列,另一个有效值为 0,将引用定义为行。
  • 如果 col/row 引用未定义,例如 setColHeaderFont/setLabel 字体,它会影响整个 column/row。这对于格式化特定列可能是可取的。

警告 maven 用于 poi 3.12 的 poi-ooxml-schemas 不包括 CTFormats。这可以通过排除它并包括 1.1 版本来覆盖:

<dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>ooxml-schemas</artifactId>
        <version>1.1</version>
</dependency>

这是一个完整的答案,但比之前使用 excel 的答案复杂得多。

一般步骤:

  1. 在 excel 中创建一个包含枢轴 table 的电子表格,或使用 sample 作为 poi。
  2. 创建数据透视缓存定义。
  3. 创建 CTDxfs 条目。这是 "font" for tables。 (查看其他答案)
  4. 使用 CTDXfs id 创建一个 pivotArea 定义。 (查看其他答案)

步骤 2 本身看起来无害,但却是整个过程的关键。当 Excel 打开 .xlsx 文档时,它会重新验证所有 pivotTable 条目,删除任何不再存在的条目,包括 CTDXfs 条目。然后它生成缓存,然后应用任何格式。但是,如果缓存尚不存在,则在验证过程中将删除所有格式规则!

下面是我用来生成 pivotCache 的代码块。这比普通的 Whosebug 答案要长得多,但不确定这是首选还是 "general guidance" 如何去做。

如果您需要维护或扩展它,请将 .xlsx 重命名为 .zip,解压缩,然后查看 xl\pivotTables\pivotTable1.xml、xl\pivotCache\pivotCacheDefinition1.xml 将其保存在 excel 之前和之后。如果这工作正常,将 pivotCacheDefinition 保存在 excel.

中后应该基本保持不变
public class PivotUtilitiesExample {
    public static void updateCache(XSSFPivotTable pivotTable) {
        updateCache(pivotTable,STAxis.AXIS_ROW);
        updateCache(pivotTable,STAxis.AXIS_COL);
        updateCache(pivotTable,STAxis.AXIS_PAGE);
    }
    /**
     * Updates the pivotCacheDefinition.xml file.  This must be run before any formatting is done.
     * However, it must also be run *AFTER* the pivotTable's source data is created and all label definitions are defined.
     *   the labels are sorted by default.
     * @param pivotTable
     * @param rowLabel if true, updates rowLabels, if false, updates columnLabels.
     */
    private static void updateCache(XSSFPivotTable pivotTable,STAxis.Enum axisType) {
        XSSFSheet sheet=(XSSFSheet) pivotTable.getDataSheet();
        AreaReference pivotArea = new AreaReference(pivotTable.getPivotCacheDefinition().
                getCTPivotCacheDefinition().getCacheSource().getWorksheetSource().getRef());
        CellReference firstCell = pivotArea.getFirstCell();
        CellReference lastCell = pivotArea.getLastCell();
        List<Integer> labelColumns=getLabelColumns(pivotTable,axisType);

        for(int labelCtr=0;labelCtr<labelColumns.size();++labelCtr) {
            CTSharedItems sharedItems=getSharedItems(pivotTable,labelColumns.get(labelCtr));
            //The pivotField entry associated will be the nth axis="axisRow" entry where N is the row label #.
            CTPivotField pivotField=getPivotField(pivotTable,axisType,labelCtr);
            CTItems items=pivotField.getItems();

            ArrayList<String> toCache=new ArrayList<String>(lastCell.getRow()-firstCell.getRow());
            //To make this work, sharedItems and pivotFields must be properly populated.
            //sharedItems should look like:
            //<cacheField name="Names" numFmtId="0"> (Cell A1)
            //<sharedItems count="3">                (Count of unique rows)
            //<s v="Jane"/>                          (Cell B1)
            //<s v="Tarzan"/>                        (Cell C1)
            //<s v="Terk"/>                          (Cell D1)
            //</sharedItems>
            //pivotFields should have an entry like this:
            //<pivotField axis="axisRow" showAll="0">
            //<items count="4">
            //<item x="0"/>
            //<item x="1"/>
            //<item x="2"/>
            //<item t="default"/>
            //</items>
            //Initially, POI will populate with:
            //<pivotField axis="axisRow" showAll="0">
            //<items count="4">
            //<item t="default"/>
            //<item t="default"/>
            //<item t="default"/>
            //<item t="default"/>
            //</items>
            //Start with firstCell.getRow()+1 since the first row is the column name.
            for(int i=firstCell.getRow()+1;i<=lastCell.getRow();++i) {
                String s=sheet.getRow(i).getCell(firstCell.getCol()+labelColumns.get(labelCtr)).getStringCellValue();
                //Only add unique entries.
                if(!toCache.contains(s))
                    toCache.add(s);
            }
            //Blank entries cannot be sorted unless they are specially entered after an M tag.
            //  For most projects this'll be overkill.
            boolean containsBlank=false;
            if(toCache.contains("")) {
                toCache.remove("");
                containsBlank=true;
            }
            //Remove the old cache list.
            for(int i=items.sizeOfItemArray()-1;i>=0;--i)
                items.removeItem(i);
            for(int i=sharedItems.sizeOfBArray()-1;i>=0;--i)
                sharedItems.removeB(i);
            for(int i=sharedItems.sizeOfDArray()-1;i>=0;--i)
                sharedItems.removeD(i);
            for(int i=sharedItems.sizeOfEArray()-1;i>=0;--i)
                sharedItems.removeE(i);
            for(int i=sharedItems.sizeOfMArray()-1;i>=0;--i)
                sharedItems.removeM(i);
            for(int i=sharedItems.sizeOfNArray()-1;i>=0;--i)
                sharedItems.removeN(i);
            for(int i=sharedItems.sizeOfSArray()-1;i>=0;--i)
                sharedItems.removeS(i);
            sharedItems.setCount(sharedItems.getDomNode().getChildNodes().getLength());
            items.setCount(items.sizeOfItemArray());
            for(int i=0;i<toCache.size();++i) {
                CTString string;
                CTItem item;
                string=sharedItems.addNewS();
                sharedItems.setCount(sharedItems.getDomNode().getChildNodes().getLength());
                string.setV(toCache.get(i));

                item=items.addNewItem();
                items.setCount(items.sizeOfItemArray());
                item.setX(i);
            }
            //Create the special blank tag.
            if(containsBlank) {
                int mPosition;
                sharedItems.addNewM();
                mPosition=sharedItems.sizeOfSArray();
                CTString s=sharedItems.addNewS();
                s.setV("");
                s.setU(true);
                sharedItems.setCount(sharedItems.getDomNode().getChildNodes().getLength());
                sharedItems.setContainsBlank(true);
                CTItem item=items.addNewItem();
                item.setM(true);
                item.setX(sharedItems.sizeOfSArray());
                item=items.addNewItem();
                item.setX(mPosition);
                items.setCount(items.sizeOfItemArray());
            }
            //Add the t="default" entry, required for subtotals.
            if(!pivotField.isSetDefaultSubtotal() || pivotField.getDefaultSubtotal()==true) {
                CTItem item;
                item=items.addNewItem();
                items.setCount(items.sizeOfItemArray());
                item.setT(STItemType.DEFAULT);
            }
        }
    }
    //Returns the label columns for all AXIS.  Default POI only has a method for RowLabelColumns.
    private static List<Integer> getLabelColumns(XSSFPivotTable pivotTable,STAxis.Enum axisType) {
        List<Integer> labelColumns;
        if(axisType.equals(STAxis.AXIS_ROW))
            labelColumns=pivotTable.getRowLabelColumns();
        else if(axisType.equals(STAxis.AXIS_COL)) {
            List<CTField> fieldList = pivotTable.getCTPivotTableDefinition().getColFields().getFieldList();
            labelColumns=new ArrayList(fieldList.size());
            for(CTField field:fieldList)
                labelColumns.add(field.getX());
        } else if(axisType.equals(STAxis.AXIS_PAGE)) {
            List<CTPageField> fieldList = pivotTable.getCTPivotTableDefinition().getPageFields().getPageFieldList();
            labelColumns=new ArrayList(fieldList.size());
            for(CTPageField field:fieldList)
                labelColumns.add(field.getFld());
        } else {
            throw new UnsupportedOperationException("Error, STAxis: "+axisType+" is not supported");
        }
        return labelColumns;
    }
    //Returns the sharedItems entry associated with a particular labelColumn.
    private static CTSharedItems getSharedItems(XSSFPivotTable pivotTable,int columnIndex) {
        XSSFSheet sheet=(XSSFSheet) pivotTable.getDataSheet();
        AreaReference pivotArea = new AreaReference(pivotTable.getPivotCacheDefinition().
                getCTPivotCacheDefinition().getCacheSource().getWorksheetSource().getRef());
        CellReference firstCell = pivotArea.getFirstCell();
        String labelName=sheet.getRow(firstCell.getRow()).getCell(firstCell.getCol()+columnIndex).getStringCellValue();
        List<CTCacheField> cacheFieldList = pivotTable.getPivotCacheDefinition().getCTPivotCacheDefinition().getCacheFields().getCacheFieldList();
        CTSharedItems sharedItems=null;
        //The sharedItem entry associated will have name=labelColumn's name.
        for(int i=0;i<cacheFieldList.size();++i)
            if(labelName.equals(cacheFieldList.get(i).getName())) {
                sharedItems=cacheFieldList.get(i).getSharedItems();
                break;
            }
        //Should never be true.
        if(sharedItems==null) {
            throw new RuntimeException("Error, unable to find sharedItems entry in pivotCacheDefinition.xml");
        }
        return sharedItems;
    }
    //Return the nth pivotField entry from the pivotTable definition of a particular Axis.
    private static CTPivotField getPivotField(XSSFPivotTable pivotTable,STAxis.Enum axisType,int index) {
        CTPivotFields pivotFields = pivotTable.getCTPivotTableDefinition().getPivotFields();
        CTPivotField pivotField=null;
        for(int i=0,axisRowNum=-1;i<pivotFields.getPivotFieldList().size();++i) {
            if(axisType.equals(pivotFields.getPivotFieldList().get(i).getAxis()))
                ++axisRowNum;
            if(axisRowNum==index) {
                pivotField=pivotFields.getPivotFieldList().get(i);
                break;
            }
        }
        if(pivotField==null)
            throw new RuntimeException("Error, unable to find pivotField entry in pivotTable.xml");
        return pivotField;
    }
}