克隆新纸时出现问题。
第1张设置“双面打印” ,但是在workbook.cloneSheet(0)
之后,新页丢失了“双面打印” 并属于 “单面打印” 。我无法为新工作表设置“两种尺寸都打印” ,因为找不到设置此方法的方法。
poi-apache printSetup()
是否支持设置“双面打印” 和“双面打印” ?
如果没有该设置的话?
答案 0 :(得分:1)
打印机是否能够进行双面打印取决于打印机硬件。因此,这不是PrintSetup之类的打印设置,而是打印机设置。 Excel能够在其文件中存储打印机设置。这是通过二进制DEVMODEA
结构(Where is the definition of the XLSX printerSettings.bin file format?)完成的。在Office Open XML
中,它存储在/xl/printerSettings/printerSettings[N].bin
中。
Apache POI
到目前为止无法正确克隆该结构。而且我怀疑应该完全克隆它,因为当用户打开的Excel文件与创建者所使用的打印机不完全相同时,它经常会导致出现问题。但是,当然,这只是我的观点,至少对于Office Open XML
是可以做到的。
Workbook.cloneSheet
直到现在都没有克隆PrintSetup
(当前版本apache poi 4.1.2
)。因此,我们首先需要克隆PrintSetup
。以下代码提供了将PrintSetup
从Sheet source
克隆到Sheet clone
的方法。它使用java.beans.*
和java.lang.reflect.Method
使用sourcePrintSetup
中的getter获取所有值,并使用适当的setter将这些值设置为clonePrintSetup
。它仅适用于不带参数的吸气剂和具有精确一个参数的设置器。但这对于PrintSetup
就足够了。
克隆了PrintSetup
后,我们可以将克隆的引用从XSSFSheet
纠正为/xl/printerSettings/printerSettings[N].bin
。 Workbook.cloneSheet
只需将关系复制到旧的/xl/printerSettings/printerSettings[N].bin
包部分。错了/xl/printerSettings/printerSettings[N].bin
软件包的一部分也需要被克隆,并且克隆的表单需要引用该新的/xl/printerSettings/printerSettings[N].bin
软件包的一部分。这是通过以下代码中的方法repairCloningPrinterSettings
完成的。使用当前的apache poi 4.1.2
可以使用。当Workbook.cloneSheet
在更高版本中更改时,必须对其进行更改。
以下代码还提供了一种二进制克隆PackagePart
的方法。它仅适用于以Idx
结尾,后跟点号和扩展名的零件名,例如name1.xml
或name1.png
或printerSettings1.bin
。克隆时它会计算Idx
的数量。
完整示例:
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ooxml.*;
import org.apache.poi.openxml4j.opc.*;
import java.io.*;
import java.beans.*;
import java.lang.reflect.Method;
class ExcelCloneSheet {
//method to transfer InputStream to OutputStream
//to work using Java 8 since InputStream.transferTo needs at least Java 9
static void transferInputStreamToOutputStream(InputStream in, OutputStream out) throws Exception {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
}
//method for binary cloning a PackagePart
//works only for part names which ends with Idx followed by dot followed by extension
//throws Exception when not successful
static PackagePart clonePackagePart(PackagePart sourcePart, String contentType) throws Exception {
OPCPackage oPCPackage = sourcePart.getPackage();
String sourcePartName = sourcePart.getPartName().getName();
String destinationPartName = sourcePartName;
String[] sourcePartNameSplitExtension = sourcePartName.split("\\.");
if (sourcePartNameSplitExtension.length == 2) {
sourcePartName = sourcePartNameSplitExtension[0];
String sourcePartNameExtension = sourcePartNameSplitExtension[1];
int i = sourcePartName.length();
while (i > 0 && Character.isDigit(sourcePartName.charAt(i - 1))) {
i--;
}
int idx = Integer.valueOf(sourcePartName.substring(i));
idx++;
destinationPartName = sourcePartName.substring(0, i) + idx + "." + sourcePartNameExtension;
}
PackagePartName partName = PackagingURIHelper.createPartName(destinationPartName);
PackagePart destinationPart = oPCPackage.createPart(partName, contentType);
InputStream in = sourcePart.getInputStream();
OutputStream out = destinationPart.getOutputStream();
//in.transferTo(out); // at least Java 9 needed
transferInputStreamToOutputStream(in, out);
out.close();
return destinationPart;
}
//method for repairing the relation from sheet to "/xl/printerSettings/printerSettings[N].bin" package part
//clones "/xl/printerSettings/printerSettings[N].bin" package part
//repairs the wrong cloned relation to the old "/xl/printerSettings/printerSettings[N].bin" package part
//works using apache poi 4.1.2
//must be changed when Workbook.cloneSheet changes in later versions
static void repairCloningPrinterSettings(XSSFSheet sheet) throws Exception {
for (POIXMLDocumentPart.RelationPart relationPart : sheet.getRelationParts()) {
String contentType = relationPart.getDocumentPart().getPackagePart().getContentType();
if ("application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings".equals(contentType)) {
System.out.println(relationPart.getRelationship());
//clone the "/xl/printerSettings/printerSettings[N].bin" package part
PackagePart sourcePart = relationPart.getDocumentPart().getPackagePart();
PackagePart destinationPart = clonePackagePart(sourcePart, contentType);
//remove the wrong cloned relation to the old "/xl/printerSettings/printerSettings[N].bin" package part
relationPart.getRelationship().getSource().removeRelationship(relationPart.getRelationship().getId());
//add the relation to the new "/xl/printerSettings/printerSettings[N].bin" package part
PackageRelationship relationship = sheet.getPackagePart().addRelationship(
destinationPart.getPartName(),
TargetMode.INTERNAL,
XSSFRelation.PRINTER_SETTINGS.getRelation());
//set Id of relation to the new "/xl/printerSettings/printerSettings[N].bin" package part
//in sheet's page setup
if (sheet.getCTWorksheet().getPageSetup() == null) sheet.getCTWorksheet().addNewPageSetup();
sheet.getCTWorksheet().getPageSetup().setId(relationship.getId());
}
}
}
//method for cloning the PrintSetup from Sheet source to Sheet clone
//uses java.beans.* and java.lang.reflect.Method to get all values using getters from sourcePrintSetup
//and set those values to clonePrintSetup using the appropriate setters
//works only for getters without parameters and setters having exact one parameter
//throws Exception when not successful
static void clonePrintSetup(Sheet source, Sheet clone) throws Exception {
PrintSetup sourcePrintSetup = source.getPrintSetup();
PrintSetup clonePrintSetup = clone.getPrintSetup();
for(PropertyDescriptor propertyDescriptor : Introspector.getBeanInfo(PrintSetup.class).getPropertyDescriptors()) {
Method getMethod = propertyDescriptor.getReadMethod();
Object value = null;
if (getMethod != null && getMethod.getParameterTypes().length == 0) {
value = getMethod.invoke(sourcePrintSetup);
Method setMethod = propertyDescriptor.getWriteMethod();
if (setMethod != null && setMethod.getParameterTypes().length == 1) {
setMethod.invoke(clonePrintSetup, value);
System.out.println(setMethod + ": " + value);
}
}
}
}
public static void main(String[] args) throws Exception {
Workbook workbook = WorkbookFactory.create(new FileInputStream("Excel.xlsx"));
Sheet sheet = workbook.getSheetAt(0);
Sheet clone = workbook.cloneSheet(0);
//Workbook.cloneSheet does not clone the PrintSetup. So we do it now.
clonePrintSetup(sheet, clone);
if (clone instanceof XSSFSheet) {
XSSFSheet xssfSheet = (XSSFSheet)clone;
//After cloning the cloned sheet has relation to the same
//"/xl/printerSettings/printerSettings[N].bin" package part as the source sheet had.
//This is wrong. So we need to repair.
repairCloningPrinterSettings(xssfSheet);
}
FileOutputStream out = new FileOutputStream("ExcelNew.xlsx");
workbook.write(out);
out.close();
workbook.close();
}
}
在此代码之后,ExcelNew.xlsx
中的克隆工作表应与源工作表具有相同的PrintSetup
以及printerSettings
。