/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ro.nextreports.engine.exporter;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import java.util.List;
import java.util.Set;
import java.util.Date;
import ro.nextreports.engine.ReleaseInfoAdapter;
import ro.nextreports.engine.Report;
import ro.nextreports.engine.ReportLayout;
import ro.nextreports.engine.band.Band;
import ro.nextreports.engine.band.BandElement;
import ro.nextreports.engine.band.Hyperlink;
import ro.nextreports.engine.band.HyperlinkBandElement;
import ro.nextreports.engine.band.ImageBandElement;
import ro.nextreports.engine.band.ImageColumnBandElement;
import ro.nextreports.engine.band.ReportBandElement;
import ro.nextreports.engine.exporter.util.StyleFormatConstants;
import ro.nextreports.engine.queryexec.QueryException;
import ro.nextreports.engine.util.HtmlUtil;
import ro.nextreports.engine.util.StringUtil;
/**
* Created by IntelliJ IDEA.
* User: mihai.panaitescu
* Date: Nov 10, 2008
* Time: 4:23:32 PM
*/
public class HtmlExporter extends ResultExporter {
private PrintStream stream;
public HtmlExporter(ExporterBean bean) {
super(bean);
}
protected void initExport() throws QueryException {
stream = createPrintStream();
if (!bean.isSubreport()) {
String style = buildHtmlStyle(getReportLayout());
stream.print("<html><head>\n");
stream.print("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
stream.print(getMetaData());
stream.print("<title>");
stream.print(getDocumentTitle());
stream.print("</title>\n" + style + " \n</head> ");
if (bean.getReportLayout().getBackgroundImage() != null) {
stream.print("<body style=\"background-image:url(");
stream.print(bean.getReportLayout().getBackgroundImage());
stream.print(")\">\n");
} else {
stream.print("<body>\n");
}
}
if (bean.getReportLayout().isUseSize()) {
stream.print("<table>");
} else {
stream.print("<table style='width:100%'>");
}
}
private String getMetaData() {
StringBuilder sb = new StringBuilder();
sb.append("<meta name=\"author\" content=\"").
append(ReleaseInfoAdapter.getCompany()).append("\">\n");
sb.append("<meta name=\"creator\" content=\"").
append("NextReports ").append(ReleaseInfoAdapter.getVersionNumber()).append("\">\n");
sb.append("<meta name=\"subject\" content=\"").
append("Created by NextReports Designer ").append(ReleaseInfoAdapter.getVersionNumber()).append("\">\n");
sb.append("<meta name=\"date\" content=\"").
append(new Date()).append("\">\n");
sb.append("<meta name=\"keywords\" content=\"").
append(ReleaseInfoAdapter.getHome()).append("\">\n");
return sb.toString();
}
protected void finishExport() {
stream.print("</table>\n");
if (!bean.isSubreport()) {
stream.print("</body></html>");
}
stream.flush();
if (!bean.isSubreport()) {
stream.close();
}
}
private String getSubreportTable() {
try {
return subreportStream.toString("UTF-8");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return "";
} finally {
stream.close();
}
}
protected void close() {
if (!bean.isSubreport()) {
stream.close();
}
}
protected void flush() {
if (resultSetRow % FLUSH_ROWS == 0) {
flushNow();
}
}
protected void flushNow() {
stream.flush();
}
protected Set<CellElement> getIgnoredCells(Band band) {
return getIgnoredCellElements(band);
}
protected void exportCell(String bandName, BandElement bandElement, Object value, int gridRow, int row, int column,
int cols, int rowSpan, int colSpan, boolean isImage) {
if (newRow) {
stream.print("<tr>");
}
stream.print(getTd(bandName, gridRow, row, column, value, rowSpan, colSpan, bandElement, isImage));
if (column + colSpan == cols) {
stream.print("</tr>\n");
}
if (newRow) {
// special case when we have a rowSpan>1 and this cell is also extended on all columns:
// because there are no other cells on the span rows (they are null and ingnored)
// we must add a set of n-1 <tr></tr>
if (column + colSpan == cols) {
for (int i = 0; i < newRowCount - 1; i++) {
stream.print("<tr></tr>\n");
}
}
}
}
protected void afterRowExport() {
}
protected String getNullElement() {
return " ";
}
private String getTd(String bandName, int gridRow, int i, int j, Object value, int rowSpan, int colSpan,
BandElement bandElement, boolean image) {
StringBuilder sb = new StringBuilder();
String val;
if (image) {
val = (String) value;
} else {
val = getStringValue(value, getPattern(bandElement));
}
sb.append("<td ");
// if render conditions we cannot use the class anymore, but an inline style
if (hasRenderConditions(bandElement, value) || hasRowRenderConditions(bandElement, gridRow, value)) {
sb.append("style=\"");
sb.append(renderCssCode(bandElement, value, gridRow, j, colSpan, true));
sb.append("\"");
} else {
sb.append("class='");
if (bean.isSubreport()) {
sb.append(StringUtil.replaceSpaces(bean.getFileName(), "_")).append("_");
}
sb.append(bandName).append(i).append("_").append(j).append("'");
}
sb.append(" rowspan=").append(rowSpan).
append(" colspan=").append(colSpan);
if (bean.getReportLayout().isUseSize()) {
sb.append(" width=").append("\"");
sb.append(getColumnWidth(j, colSpan));
sb.append("\"");
}
if (bandElement != null) {
String headers = bandElement.getHtmlAccHeaders();
if (headers != null) {
sb.append(" headers=\"").append(headers).append("\"");
}
String id = bandElement.getHtmlAccId();
if (id != null) {
sb.append(" id=\"").append(id).append("\"");
}
String scope = bandElement.getHtmlAccScope();
if (scope != null) {
sb.append(" scope=\"").append(scope).append("\"");
}
}
sb.append(">");
if (image) {
ImageBandElement ibe = (ImageBandElement) bandElement;
sb.append("<img src=\"").append(ibe.getImage()).append("\"");
if (ibe.isScaled()) {
sb.append(" width=\"").append(ibe.getWidth()).append("\"");
sb.append(" height=\"").append(ibe.getHeight()).append("\"");
}
sb.append(" alt=\"").append(IMAGE_NOT_LOADED).append("\"></img>");
} else if (bandElement instanceof HyperlinkBandElement) {
Hyperlink hyperlink = ((HyperlinkBandElement) bandElement).getHyperlink();
sb.append("<a href=\"").append(hyperlink.getUrl()).append("\" target=\"_blank\">").
append(hyperlink.getText()).append("</a>");
} else if (bandElement instanceof ReportBandElement) {
Report report = ((ReportBandElement)bandElement).getReport();
ExporterBean eb = null;
try {
eb = getSubreportExporterBean(report);
HtmlExporter subExporter = new HtmlExporter(eb);
subExporter.export();
sb.append(subExporter.getSubreportTable());
} catch (Exception e) {
e.printStackTrace();
} finally {
if ((eb != null) && (eb.getResult() != null)) {
eb.getResult().close();
}
}
} else if (bandElement instanceof ImageColumnBandElement){
String v = StringUtil.getValueAsString(value, null);
if(StringUtil.BLOB.equals(v)) {
sb.append(StringUtil.BLOB);
} else {
ImageColumnBandElement icbe = (ImageColumnBandElement) bandElement;
byte[] imageBytes = StringUtil.decodeImage(v);
sb.append("<img src=\"data:image/jpg;base64,").append(v).append("\"");
if (icbe.isScaled()) {
sb.append(" width=\"").append(icbe.getWidth()).append("\"");
sb.append(" height=\"").append(icbe.getHeight()).append("\"");
}
sb.append(" alt=\"").append(IMAGE_NOT_LOADED).append("\"></img>");
}
} else {
sb.append(val);
}
sb.append("</td>");
return sb.toString();
}
private String renderCssCode(BandElement be, int gridRow, int gridColumn, int colSpan) {
// this method is run in init code and we cannot overwrite cell render conditions here
// the overwrite is done in getTD method
return renderCssCode(be, null, gridRow, gridColumn, colSpan, false);
}
private String renderCssCode(BandElement be, Object value, int gridRow, int gridColumn, int colSpan, boolean overwriteCellRenderCond) {
Map<String, Object> style = null;
style = buildCellStyleMap(be, value, gridRow, gridColumn, colSpan, overwriteCellRenderCond);
// to see a background image all cells must not have any background!
if (bean.getReportLayout().getBackgroundImage() != null) {
style.remove(StyleFormatConstants.BACKGROUND_COLOR);
}
return HtmlUtil.getCssCode(be, style);
}
private String buildHtmlStyle(ReportLayout reportLayout) {
StringBuilder retval = new StringBuilder();
retval.append("<style type=\"text/css\" > \n table{ border-collapse:collapse; } \n ");
List<Band> bands = reportLayout.getDocumentBands();
for (Band band : bands) {
int rows = band.getRowCount();
int cols = band.getColumnCount();
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
BandElement be = band.getElementAt(i, j);
int colSpan = (be == null) ? 1 : be.getColSpan();
retval.append(" .").append(band.getName()).append(i).append("_").append(j).append(" \n{").
append(renderCssCode(be, i, j, colSpan)).append(" } \n");
if (be instanceof ReportBandElement) {
retval.append(buildSubreportHtmlStyle((ReportBandElement)be));
}
}
}
}
retval.append("</style> \n");
return retval.toString();
}
private String buildSubreportHtmlStyle(ReportBandElement rbe) {
StringBuilder retval = new StringBuilder();
ReportLayout layout = rbe.getReport().getLayout();
List<Band> bands = layout.getDocumentBands();
for (Band band : bands) {
int rows = band.getRowCount();
int cols = band.getColumnCount();
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
BandElement be = band.getElementAt(i, j);
int colSpan = (be == null) ? 1 : be.getColSpan();
retval.append(" .").append(StringUtil.replaceSpaces(rbe.getReport().getBaseName(), "_")).append("_");
retval.append(band.getName()).append(i).append("_").append(j).append(" \n{").
append(renderCssCode(be, i, j, colSpan)).append(" } \n");
// add recursive the style for subreports in other subreports
if (be instanceof ReportBandElement) {
retval.append(buildSubreportHtmlStyle((ReportBandElement)be));
}
}
}
}
return retval.toString();
}
private String getStringValue(Object val, String pattern) {
String v = StringUtil.getValueAsString(val, pattern, getReportLanguage());
if (v == null) {
return getNullElement();
}
return v;
}
// even if HTML is not a paged document we need to take care for print page breaks "page-break-before: always"
// tables need to be broken in order to force a page break.
protected void newPage() {
if (!bean.isSubreport()) {
try {
stream.print("</table>\n");
stream.println("<p style=\"page-break-before: always\"></p>\n");
if (bean.getReportLayout().isUseSize()) {
stream.print("<table>");
} else {
stream.print("<table style='width:100%'>");
}
if (bean.getReportLayout().isHeaderOnEveryPage()) {
printHeaderBand();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
// private String getRotationStyle(short angle) {
// StringBuilder sb = new StringBuilder();
// if (angle == -90) {
// sb.append("-webkit-transform: rotate(90deg);\n"); // safari
// sb.append("-moz-transform: rotate(90deg);\n"); // firefox
// sb.append("-o-transform: rotate(90deg);\n"); // opera
// sb.append("-ms-transform: rotate(90deg);\n"); // IE 9+
// sb.append("filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);\n"); // Internet Explorer till 9
// } else if (angle == 90) {
// sb.append("-webkit-transform: rotate(-90deg);\n"); // safari
// sb.append("-moz-transform: rotate(-90deg);\n"); // firefox
// sb.append("-o-transform: rotate(-90deg);\n"); // opera
// sb.append("-ms-transform: rotate(-90deg);\n"); // IE 9+
// sb.append("filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);\n"); // Internet Explorer till 9
// }
// return sb.toString();
// }
}