Skip to content

Commit

Permalink
Add option to post-process excel formulas for xlsx, html and pdf outp…
Browse files Browse the repository at this point in the history
…uts #135
  • Loading branch information
Gavrilov-Ivan committed Dec 13, 2022
1 parent 19e4109 commit 8441e25
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 28 deletions.
1 change: 1 addition & 0 deletions core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ configure(core) {
exclude(group: 'bouncycastle', module: 'bctsp-jdk14')
}
compile(group: 'org.apache.poi', name: 'poi', version: '4.1.1')
compile(group: 'org.apache.poi', name: 'poi-ooxml', version: '4.1.1')
compile(group: 'org.apache.poi', name: 'poi-scratchpad', version: '4.1.1') {
exclude(group: 'org.apache.poi', module: 'poi')
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public interface PropertiesLoader {
String CUBA_REPORTING_PUT_EMPTY_ROW_IF_NO_DATA_SELECTED = "cuba.reporting.dataextractor.putEmptyRowIfNoDataSelected";
String CUBA_REPORTING_FONTS_DIRECTORY = "cuba.reporting.fontsDirectory";
String CUBA_REPORTING_OPEN_HTML_FOR_PDF_CONVERSION = "cuba.reporting.openHtmlForPdfConversion";
String CUBA_REPORTING_FORMULAS_POST_PROCESSING_EVALUATION_ENABLED = "cuba.reporting.formulasPostProcessingEvaluationEnabled";

Properties load() throws IOException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ public Reporting createReportingEngine(PropertiesLoader propertiesLoader) throws
}
}

String formulasEvaluationEnabled = properties.getProperty(PropertiesLoader.CUBA_REPORTING_FORMULAS_POST_PROCESSING_EVALUATION_ENABLED, "true");
formatterFactory.setFormulasPostProcessingEvaluationEnabled(Boolean.parseBoolean(formulasEvaluationEnabled));

reporting.setFormatterFactory(formatterFactory);
SqlDataLoader sqlDataLoader = new PropertiesSqlLoaderFactory(propertiesLoader).create();
GroovyDataLoader groovyDataLoader = new GroovyDataLoader(new DefaultScriptingImpl());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,12 @@ public class DefaultFormatterFactory implements ReportFormatterFactory {

protected ReportInlinersProvider inlinersProvider;

protected boolean formulasPostProcessingEvaluationEnabled;

public DefaultFormatterFactory() {
htmlImportProcessor = new HtmlImportProcessorImpl();
htmlToPdfConverterFactory = new HtmlToPdfConverterFactory();
formulasPostProcessingEvaluationEnabled = true;
formattersMap.put("xls", factoryInput -> {
XLSFormatter xlsFormatter = new XLSFormatter(factoryInput);
xlsFormatter.setDocumentConverter(documentConverter);
Expand Down Expand Up @@ -91,6 +94,7 @@ public DefaultFormatterFactory() {
xlsxFormatter.setDefaultFormatProvider(defaultFormatProvider);
xlsxFormatter.setDocumentConverter(documentConverter);
xlsxFormatter.setScripting(scripting);
xlsxFormatter.setFormulasPostProcessingEvaluationEnabled(formulasPostProcessingEvaluationEnabled);
return xlsxFormatter;
};
formattersMap.put("xlsx", xlsxCreator);
Expand Down Expand Up @@ -147,6 +151,14 @@ public void setScripting(Scripting scripting) {
this.scripting = scripting;
}

public boolean isFormulasPostProcessingEvaluationEnabled() {
return formulasPostProcessingEvaluationEnabled;
}

public void setFormulasPostProcessingEvaluationEnabled(boolean formulasPostProcessingEvaluationEnabled) {
this.formulasPostProcessingEvaluationEnabled = formulasPostProcessingEvaluationEnabled;
}

public ReportFormatter createFormatter(FormatterFactoryInput factoryInput) {
String templateExtension = factoryInput.templateExtension;
BandData rootBand = factoryInput.rootBand;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.docx4j.XmlUtils;
import org.docx4j.dml.chart.CTAxDataSource;
import org.docx4j.dml.chart.CTChart;
Expand All @@ -53,10 +56,7 @@

import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.*;
import java.util.*;
import java.util.regex.Matcher;

Expand All @@ -82,6 +82,8 @@ public class XlsxFormatter extends AbstractFormatter {
protected BandData previousRangeBandData;
protected int previousRangesRightOffset;

protected boolean formulasPostProcessingEvaluationEnabled = true;

protected Unmarshaller unmarshaller;
protected Marshaller marshaller;

Expand All @@ -96,6 +98,10 @@ public void setDocumentConverter(DocumentConverter documentConverter) {
this.documentConverter = documentConverter;
}

public void setFormulasPostProcessingEvaluationEnabled(boolean formulasPostProcessingEvaluationEnabled) {
this.formulasPostProcessingEvaluationEnabled = formulasPostProcessingEvaluationEnabled;
}

@Override
public void renderDocument() {
init();
Expand Down Expand Up @@ -135,36 +141,40 @@ protected void validateTemplateContainsNamedRange() {
protected void saveAndClose() {
try {
checkThreadInterrupted();
if (ReportOutputType.xlsx.equals(outputType)) {
writeToOutputStream(result.getPackage(), outputStream);
outputStream.flush();
} else if (ReportOutputType.csv.equals(outputType)) {
if (ReportOutputType.csv.equals(outputType)) {
saveXlsxAsCsv(result, outputStream);
outputStream.flush();
} else if (ReportOutputType.pdf.equals(outputType)) {
if (documentConverter != null) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
writeToOutputStream(result.getPackage(), bos);
documentConverter.convertToPdf(DocumentConverter.FileType.SPREADSHEET, bos.toByteArray(), outputStream);
outputStream.flush();
} else {
throw new UnsupportedOperationException(
"XlsxFormatter could not convert result to pdf without Libre/Open office connected. " +
"Please setup Libre/Open office connection details.");
} else {
ByteArrayOutputStream intermediateBos = new ByteArrayOutputStream();
writeToOutputStream(result.getPackage(), intermediateBos);
if (formulasPostProcessingEvaluationEnabled) {
intermediateBos = evaluateFormulas(intermediateBos.toByteArray());
}
} else if (ReportOutputType.html.equals(outputType)) {
if (documentConverter != null) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
writeToOutputStream(result.getPackage(), bos);
documentConverter.convertToHtml(DocumentConverter.FileType.SPREADSHEET, bos.toByteArray(), outputStream);

if (ReportOutputType.xlsx.equals(outputType)) {
outputStream.write(intermediateBos.toByteArray());
outputStream.flush();
} else if (ReportOutputType.pdf.equals(outputType)) {
if (documentConverter != null) {
documentConverter.convertToPdf(DocumentConverter.FileType.SPREADSHEET, intermediateBos.toByteArray(), outputStream);
outputStream.flush();
} else {
throw new UnsupportedOperationException(
"XlsxFormatter could not convert result to pdf without Libre/Open office connected. " +
"Please setup Libre/Open office connection details.");
}
} else if (ReportOutputType.html.equals(outputType)) {
if (documentConverter != null) {
documentConverter.convertToHtml(DocumentConverter.FileType.SPREADSHEET, intermediateBos.toByteArray(), outputStream);
outputStream.flush();
} else {
throw new UnsupportedOperationException(
"XlsxFormatter could not convert result to html without Libre/Open office connected. " +
"Please setup Libre/Open office connection details.");
}
} else {
throw new UnsupportedOperationException(
"XlsxFormatter could not convert result to html without Libre/Open office connected. " +
"Please setup Libre/Open office connection details.");
throw new UnsupportedOperationException(String.format("XlsxFormatter could not output file with type [%s]", outputType));
}
} else {
throw new UnsupportedOperationException(String.format("XlsxFormatter could not output file with type [%s]", outputType));
}
} catch (Docx4JException e) {
throw wrapWithReportingException("An error occurred while saving result report", e);
Expand All @@ -175,6 +185,28 @@ protected void saveAndClose() {
}
}

protected ByteArrayOutputStream evaluateFormulas(byte[] content) {
try {
ByteArrayInputStream bis = new ByteArrayInputStream(content);
org.apache.poi.ss.usermodel.Workbook workbook = new XSSFWorkbook(bis);
FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
for (org.apache.poi.ss.usermodel.Sheet sheet : workbook) {
for (org.apache.poi.ss.usermodel.Row row : sheet) {
for (org.apache.poi.ss.usermodel.Cell cell : row) {
if (cell.getCellType() == CellType.FORMULA) {
evaluator.evaluateFormulaCell(cell);
}
}
}
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
workbook.write(bos);
return bos;
} catch (IOException e) {
throw new ReportingException(e);
}
}

protected void init() {
try {
template = Document.create(SpreadsheetMLPackage.load(reportTemplate.getDocumentContent()));
Expand Down

0 comments on commit 8441e25

Please sign in to comment.