/*********************************************************************** * This file is part of iDempiere ERP Open Source * * http://www.idempiere.org * * * * Copyright (C) Contributors * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301, USA. * **********************************************************************/ package org.idempiere.zk.datatable; import java.awt.Font; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.Writer; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import org.adempiere.exceptions.AdempiereException; import org.apache.ecs.XhtmlDocument; import org.apache.ecs.xhtml.a; import org.apache.ecs.xhtml.input; import org.apache.ecs.xhtml.link; import org.apache.ecs.xhtml.script; import org.apache.ecs.xhtml.span; import org.apache.ecs.xhtml.style; import org.apache.ecs.xhtml.table; import org.apache.ecs.xhtml.tbody; import org.apache.ecs.xhtml.td; import org.apache.ecs.xhtml.th; import org.apache.ecs.xhtml.thead; import org.apache.ecs.xhtml.tr; import org.compiere.model.MColumn; import org.compiere.model.MElement; import org.compiere.model.MQuery; import org.compiere.model.MRole; import org.compiere.model.MStyle; import org.compiere.model.MSysConfig; import org.compiere.model.MTable; import org.compiere.print.IHTMLExtension; import org.compiere.print.MPrintFont; import org.compiere.print.MPrintFormat; import org.compiere.print.MPrintFormatItem; import org.compiere.print.MPrintTableFormat; import org.compiere.print.PrintData; import org.compiere.print.PrintDataElement; import org.compiere.print.ReportEngine; import org.compiere.print.layout.InstanceAttributeColumn; import org.compiere.print.layout.InstanceAttributeData; import org.compiere.print.layout.LayoutEngine; import org.compiere.util.CLogger; import org.compiere.util.DisplayType; import org.compiere.util.Env; import org.compiere.util.Ini; import org.compiere.util.Language; import org.compiere.util.Msg; import org.compiere.util.Util; import org.idempiere.print.renderer.HTMLReportRenderer; import org.idempiere.print.renderer.IReportRenderer; import org.osgi.service.component.annotations.Component; import org.zkoss.zk.ui.Executions; import com.google.common.net.MediaType; @Component(service = IReportRenderer.class, immediate = true) public class DatatableReportRenderer implements IReportRenderer { private static final CLogger log = CLogger.getCLogger(DatatableReportRenderer.class); private static final String JS_DATA_IDENTIFIER = "JS_DataTable"; protected enum FunctionTypes {SUM, COUNT, MIN, MAX, AVG, DEVIATION, VARIANCE, GROUP_BY, ORDER_BY}; public DatatableReportRenderer() { } @Override public String getId() { return DatatableReportRendererConfiguration.ID; } @Override public String getName() { return getId(); } @Override public String getContentType() { return MediaType.HTML_UTF_8.toString(); } @Override public String getFileExtension() { return DatatableReportRendererConfiguration.FILE_EXTENSION; } @Override public void renderReport(ReportEngine reportEngine, DatatableReportRendererConfiguration configuration) { try { Language lang = configuration.getLanguage(); if (lang == null) lang = Language.getLoginLanguage(); Writer fw = null; if (configuration.getOutputFile() != null) fw = new OutputStreamWriter(new FileOutputStream(configuration.getOutputFile(), false), Ini.getCharset()); else fw = configuration.getOutputWriter(); createHTML (reportEngine, new BufferedWriter(fw), configuration.isOnlyTable(), lang, configuration.getExtension(), configuration.isExport()); } catch (FileNotFoundException fnfe) { log.log(Level.SEVERE, "(f) - " + fnfe.toString()); } catch (Exception e) { log.log(Level.SEVERE, "(f)", e); throw new AdempiereException(e); } } @Override public boolean isBinary() { return false; } @Override public Class getConfigurationType() { return DatatableReportRendererConfiguration.class; } /** * Write HTML to writer * @param reportEngine * @param writer writer * @param onlyTable if false create complete HTML document * @param language optional language - if null numbers/dates are not formatted * @param extension optional extension for html output * @param isExport when isExport = true will don't embed resource dependent zk framework */ private void createHTML (ReportEngine reportEngine, Writer writer, boolean onlyTable, Language language, IHTMLExtension extension, boolean isExport) { MPrintFormat printFormat = reportEngine.getPrintFormat(); PrintData printData = reportEngine.getPrintData(); MQuery reportEngineQuery = reportEngine.getQuery(); LayoutEngine layout = reportEngine.getLayout(); Map> mapCssInfo = new HashMap<>(); try { DataTableOptions dataTableOptions = new DataTableOptions(language.getLocale().toLanguageTag()); //collect column to print List columns = new ArrayList<>(); List asiElements = new ArrayList<>(); int columnCount = 0; for (int col = 0; col < printFormat.getItemCount(); col++) { MPrintFormatItem item = printFormat.getItem(col); if (item.isPrinted()) { if (item.isTypeField() && item.isPrintInstanceAttributes()) { InstanceAttributeData asiElement = new InstanceAttributeData(item, columnCount); asiElement.readAttributesData(printData); asiElements.add(asiElement); continue; } else { columns.add(item); columnCount++; } } } if (asiElements.size() > 0) { int columnCreated = 0; for(InstanceAttributeData data : asiElements) { List instanceColumns = data.getColumns(); int index = data.getColumnIndex() + columnCreated; for(InstanceAttributeColumn c : instanceColumns) { columns.add(index, c); index++; columnCreated++; } } } String cssPrefix = extension != null ? extension.getClassPrefix() : null; if (cssPrefix != null && cssPrefix.trim().length() == 0) cssPrefix = null; table parameterTable = null; if (!printFormat.isForm()) { if (reportEngineQuery != null && reportEngineQuery.isActive()) { int rows = reportEngineQuery.getReportProcessQuery() != null ? reportEngineQuery.getReportProcessQuery().getRestrictionCount() : reportEngineQuery.getRestrictionCount(); if (rows > 0) { parameterTable = new table(); if (cssPrefix != null) parameterTable.setClass(cssPrefix + "-parameter-table"); else parameterTable.setClass("parameter-table"); parameterTable.setNeedClosingTag(false); } } } table table = new table(); if (cssPrefix != null) { table.setClass("cell-border compact hover " + cssPrefix + "-table"); } table.setID(JS_DATA_IDENTIFIER); // table.setNeedClosingTag(false); PrintWriter w = new PrintWriter(writer); XhtmlDocument doc = null; boolean minify = MSysConfig.getBooleanValue(MSysConfig.HTML_REPORT_MINIFY, true, Env.getAD_Client_ID(Env.getCtx())); if (onlyTable) { w.print(HTMLReportRenderer.compress(table.toString(), minify)); } else { doc = new XhtmlDocument(); doc.getHtml().setNeedClosingTag(false); doc.getBody().setNeedClosingTag(false); doc.appendHead(""); if (extension != null && !Util.isEmpty(extension.getWebFontLinks(), true)) { doc.appendHead(extension.getWebFontLinks()); } if (extension != null && extension.getStyleURL() != null) { // maybe cache style content with key is path String pathStyleFile = extension.getFullPathStyle(); // creates a temp file - delete below Path path = Paths.get(pathStyleFile); List styleLines = Files.readAllLines(path, Ini.getCharset()); Files.delete(path); // delete temp file StringBuilder styleBuild = new StringBuilder(); for (String styleLine : styleLines){ styleBuild.append(styleLine); //.append("\n"); } HTMLReportRenderer.appendInlineCss (doc, styleBuild); } if (extension != null && extension.getScriptURL() != null && !isExport) { script jslink = new script(); jslink.setLanguage("javascript"); jslink.setSrc(extension.getScriptURL()); doc.appendHead(jslink); } appendStyles(doc, isExport, dataTableOptions.geti18nURL()); appendScripts (doc, isExport); if (extension != null && !isExport){ extension.setWebAttribute(doc.getBody()); } } if (doc != null) { MPrintFormatItem item = null; int printColIndex = -1; for(int col = 0; col < columns.size(); col++) { Object colobj = columns.get(col); if (colobj instanceof MPrintFormatItem) item = (MPrintFormatItem) colobj; else if (colobj instanceof InstanceAttributeColumn) item = ((InstanceAttributeColumn) colobj).getPrintFormatItem(); if(item != null) { printColIndex++; HTMLReportRenderer.addCssInfo(printFormat, item, printColIndex, mapCssInfo); } } HTMLReportRenderer.appendInlineCss(doc, mapCssInfo); StringBuilder styleBuild = new StringBuilder(); MPrintTableFormat tf = printFormat.getTableFormat(); HTMLReportRenderer.CSSInfo cssInfo = new HTMLReportRenderer.CSSInfo(tf.getPageHeader_Font(), tf.getPageHeaderFG_Color()); if (cssPrefix != null) { if (parameterTable != null) styleBuild.append("."+cssPrefix + "-parameter-table th").append(cssInfo.getCssRule()); styleBuild.append("."+cssPrefix + "-table th").append(cssInfo.getCssRule()); } else { if (parameterTable != null) styleBuild.append("parameter-table th").append(cssInfo.getCssRule()); styleBuild.append("table th").append(cssInfo.getCssRule()); } cssInfo = new HTMLReportRenderer.CSSInfo(tf.getParameter_Font(), tf.getParameter_Color()); styleBuild.append(".tr-parameter td").append(cssInfo.getCssRule()); cssInfo = new HTMLReportRenderer.CSSInfo(tf.getFunct_Font(), tf.getFunctFG_Color()); styleBuild.append(".tr-function td").append(cssInfo.getCssRule()); MPrintFont printFont = MPrintFont.get(printFormat.getAD_PrintFont_ID()); Font base = printFont.getFont(); Font newFont = new Font(base.getName(), Font.PLAIN, base.getSize()-1); cssInfo = new HTMLReportRenderer.CSSInfo(newFont, null); styleBuild.append(".tr-level-1 td").append(cssInfo.getCssRule()); newFont = new Font(base.getName(), Font.PLAIN, base.getSize()-2); cssInfo = new HTMLReportRenderer.CSSInfo(newFont, null); styleBuild.append(".tr-level-2 td").append(cssInfo.getCssRule()); styleBuild = new StringBuilder(styleBuild.toString().replaceAll(";", "!important;")); HTMLReportRenderer.appendInlineCss (doc, styleBuild); w.print(doc.toString()); w.print("
"); String paraWrapId = null; if (parameterTable != null) { paraWrapId = cssPrefix + "-para-table-wrap"; w.print("
"); w.print(""+Msg.getMsg(Env.getCtx(), "Parameter")+""); w.print(HTMLReportRenderer.compress(parameterTable.toString(), minify)); tr tr = new tr(); tr.setClass("tr-parameter"); MQuery query = reportEngineQuery; if (reportEngineQuery.getReportProcessQuery() != null) query = reportEngineQuery.getReportProcessQuery(); for (int r = 0; r < query.getRestrictionCount(); r++) { if (r > 0) { tr = new tr(); tr.setClass("tr-parameter"); } td td = new td(); tr.addElement(td); td.addElement(query.getInfoName(r)); td = new td(); tr.addElement(td); td.addElement(query.getInfoOperator(r)); td = new td(); tr.addElement(td); td.addElement(query.getInfoDisplayAll(r)); w.print(HTMLReportRenderer.compress(tr.toString(), minify)); } w.print(""); w.print("
"); } StringBuilder tableWrapDiv = new StringBuilder(); tableWrapDiv.append("
"); w.print(HTMLReportRenderer.compress(tableWrapDiv.toString(), minify)); w.print(HTMLReportRenderer.compress(table.toString(), minify)); } thead thead = new thead(); thead.setClass("sticky"); tbody tbody = new tbody(); tbody.setNeedClosingTag(false); Boolean [] colSuppressRepeats = layout == null || layout.colSuppressRepeats == null? LayoutEngine.getColSuppressRepeats(printFormat):layout.colSuppressRepeats; Object [] preValues = null; if (colSuppressRepeats != null){ preValues = new Object [colSuppressRepeats.length]; } int printColIndex = -1; HashMap suppressMap = new HashMap<>(); //search input at header { tr tr = new tr(); for (int col = 0; col < printFormat.getItemCount(); col++) { MPrintFormatItem item = printFormat.getItem(col); if (item.isPrinted()) { var printName = item.getPrintName(language); if (!Util.isEmpty(printName)) { th th = new th(); th.addAttribute("data-dt-order", "disable"); tr.addElement(th); input searchInput = new input(); searchInput.addAttribute("placeholder", "Search "+printName); th.addElement(searchInput); } } } thead.addElement(tr); } // for all rows (-1 = header row) for (int row = -1; row < printData.getRowCount(); row++) { tr tr = new tr(); if (row != -1) { printData.setRowIndex(row); if(printData.isFunctionRow()) continue; if (extension != null && !isExport) { extension.extendRowElement(tr, printData); } if (printData.isFunctionRow()) { tr.setClass(cssPrefix + "-functionrow"); } else if ( row < printData.getRowCount() && printData.isFunctionRow(row+1)) { tr.setClass(cssPrefix + "-lastgrouprow"); } // add row to table body //tbody.addElement(tr); } else { // add row to table header thead.addElement(tr); } printColIndex = -1; // for all columns for (int col = 0; col < columns.size(); col++) { Object colObj = columns.get(col); MPrintFormatItem item = null; InstanceAttributeColumn instanceAttributeColumn = null; if (colObj instanceof MPrintFormatItem) { item = (MPrintFormatItem) colObj; } else if (colObj instanceof InstanceAttributeColumn) { instanceAttributeColumn = (InstanceAttributeColumn) colObj; item = instanceAttributeColumn.getPrintFormatItem(); } if (item != null) { printColIndex++; // header row if (row == -1) { th th = new th(); tr.addElement(th); String columnHeader = instanceAttributeColumn != null ? instanceAttributeColumn.getName() : item.getPrintName(language); th.addElement(Util.maskHTML(columnHeader)); if (cssPrefix != null && instanceAttributeColumn == null) { MColumn column = MColumn.get(Env.getCtx(), item.getAD_Column_ID()); if (column != null && column.getAD_Column_ID() > 0) { if (DisplayType.isNumeric(column.getAD_Reference_ID())) { th.setClass(cssPrefix + "-number"); } } } if (item.isSuppressNull()) { suppressMap.put(printColIndex, th); th.setID("report-th-"+printColIndex); } // Calculate DateTables Options if(isDataTableFunctionColumn(item)) { if(item.isOrderBy()) dataTableOptions.addPrintColumnIndex(FunctionTypes.ORDER_BY, item.isDesc() ? dataTableOptions.DESC_OFFSET + printColIndex : printColIndex); if(item.isGroupBy()) dataTableOptions.addPrintColumnIndex(FunctionTypes.GROUP_BY, printColIndex); if(item.isSummarized()) dataTableOptions.addPrintColumnIndex(FunctionTypes.SUM, printColIndex); if(item.isCounted()) dataTableOptions.addPrintColumnIndex(FunctionTypes.COUNT, printColIndex); if(item.isMinCalc()) dataTableOptions.addPrintColumnIndex(FunctionTypes.MIN, printColIndex); if(item.isMaxCalc()) dataTableOptions.addPrintColumnIndex(FunctionTypes.MAX, printColIndex); if(item.isAveraged()) dataTableOptions.addPrintColumnIndex(FunctionTypes.AVG, printColIndex); if(item.isDeviationCalc()) dataTableOptions.addPrintColumnIndex(FunctionTypes.DEVIATION, printColIndex); if(item.isVarianceCalc()) dataTableOptions.addPrintColumnIndex(FunctionTypes.VARIANCE, printColIndex); } if(item.getAD_Column_ID() > 0) { MColumn column = MColumn.get(item.getAD_Column_ID()); String description = column.get_Translation(MElement.COLUMNNAME_Description,language.getAD_Language()); if(!Util.isEmpty(description)) { th.addAttribute("title", description); } } } else { td td = new td(); tr.addElement(td); MStyle style = item.getAD_FieldStyle_ID() > 0 ? MStyle.get(Env.getCtx(), item.getAD_FieldStyle_ID()) : null; Object obj = instanceAttributeColumn != null ? instanceAttributeColumn.getPrintDataElement(row) : printData.getNodeByPrintFormatItemId(item.getAD_PrintFormatItem_ID()); if (obj == null || !ReportEngine.isDisplayPFItem(printData, item)){ td.addElement(" "); if (colSuppressRepeats != null && colSuppressRepeats[printColIndex]){ preValues[printColIndex] = null; } if (item.isSuppressNull() && obj != null && suppressMap.containsKey(printColIndex)) suppressMap.remove(printColIndex); } else if (obj instanceof PrintDataElement) { PrintDataElement pde = (PrintDataElement) obj; String value = pde.getValueDisplay(language); // formatted if (colSuppressRepeats != null && colSuppressRepeats[printColIndex]){ if (value.equals(preValues[printColIndex])){ td.addElement(" "); continue; }else{ preValues[printColIndex] = value; } } if (item.isSuppressNull() && obj != null && suppressMap.containsKey(printColIndex)) suppressMap.remove(printColIndex); if (pde.getColumnName().endsWith("_ID") && extension != null && !isExport) { boolean isZoom = false; if (item.getColumnName().equals("Record_ID")) { Object tablePDE = printData.getNode("AD_Table_ID"); if (tablePDE != null && tablePDE instanceof PrintDataElement) { int tableID = -1; try { tableID = Integer.parseInt(((PrintDataElement)tablePDE).getValueAsString()); } catch (Exception e) { tableID = -1; } if (tableID > 0) { MTable mTable = MTable.get(Env.getCtx(), tableID); String tableName = mTable.getTableName(); value = reportEngine.getIdentifier(mTable, tableName, Integer.parseInt(value)); String foreignColumnName = tableName + "_ID"; pde.setForeignColumnName(foreignColumnName); isZoom = true; } } } else { isZoom = true; } if (isZoom) { // check permission on the zoomed window MTable mTable = MTable.get(Env.getCtx(), pde.getForeignColumnName().substring(0, pde.getForeignColumnName().length()-3)); int Record_ID = -1; try { Record_ID = Integer.parseInt(pde.getValueAsString()); } catch (Exception e) { Record_ID = -1; } Boolean canAccess = null; if (Record_ID >= 0 && mTable != null) { int AD_Window_ID = Env.getZoomWindowID(mTable.get_ID(), Record_ID); canAccess = MRole.getDefault().getWindowAccess(AD_Window_ID); } if (canAccess == null) { isZoom = false; } } if (isZoom) { //link for column a href = new a("javascript:void(0)"); href.setID(pde.getColumnName() + "_" + row + "_a"); td.addElement(href); href.addElement(Util.maskHTML(value)); if (cssPrefix != null) href.setClass(cssPrefix + "-href"); // Set Style if(style != null && style.isWrapWithSpan()) HTMLReportRenderer.setStyle(printData, href, style); else HTMLReportRenderer.setStyle(printData, td, style); extension.extendIDColumn(row, td, href, pde); } else { // Set Style if(style != null && style.isWrapWithSpan()) { span span = new span(); HTMLReportRenderer.setStyle(printData, span, style); span.addElement(Util.maskHTML(value)); td.addElement(span); } else { HTMLReportRenderer.setStyle(printData, td, style); td.addElement(Util.maskHTML(value)); } } } else { // Set Style if(style != null && style.isWrapWithSpan()) { span span = new span(); HTMLReportRenderer.setStyle(printData, span, style); span.addElement(Util.maskHTML(value)); td.addElement(span); } else { HTMLReportRenderer.setStyle(printData, td, style); td.addElement(Util.maskHTML(value)); } } if (cssPrefix != null) { if (DisplayType.isNumeric(pde.getDisplayType())) td.setClass(cssPrefix + "-number"); else if (DisplayType.isDate(pde.getDisplayType())) td.setClass(cssPrefix + "-date"); else td.setClass(cssPrefix + "-text"); } //just run with on record if (row == 0) HTMLReportRenderer.addCssInfo(printFormat, item, printColIndex, mapCssInfo); } else if (obj instanceof PrintData) { // ignore contained Data } else log.log(Level.SEVERE, "Element not PrintData(Element) " + obj.getClass()); } } // printed } // for all columns dataTableOptions.setPrintColLastIndex(printColIndex); /* output table header */ if (row == -1){ w.print(HTMLReportRenderer.compress(thead.toString(), minify)); // output open of tbody w.print(HTMLReportRenderer.compress(tbody.toString(), minify)); }else{ // output row by row w.print(HTMLReportRenderer.compress(tr.toString(), minify)); } } // for all rows w.print(""); w.print(""); if (suppressMap.size() > 0) { StringBuilder st = new StringBuilder(); for(th t : suppressMap.values()) { if (st.length() > 0) st.append(","); st.append("#").append(t.getAttribute("id")); } st.append(" {\n\t\tdisplay:none;\n\t}"); style styleTag = new style(); styleTag.addElement(st.toString()); w.print(HTMLReportRenderer.compress(styleTag.toString(), minify)); } if (!onlyTable) { w.print("
"); w.print("
"); String dataTableOptionString = dataTableOptions.getDataTableOptions(); if( dataTableOptionString != null ) { w.print(""); } w.print(""); w.print(""); } w.flush(); w.close(); } catch (Exception e) { log.log(Level.SEVERE, e.getMessage(), e); throw new AdempiereException(e); } } // createHTML /** * Is PrintFormat Item Function for DataTables * @param item * @return */ private boolean isDataTableFunctionColumn(MPrintFormatItem item) { if(item.isOrderBy() || item.isGroupBy() || item.isSummarized() || item.isCounted() || item.isMinCalc() || item.isAveraged() || item.isDeviationCalc() || item.isMaxCalc() || item.isVarianceCalc()) return true; return false; } /** * If isExport, embed script content, otherwise embed script url * @param doc * @param isExport * @throws IOException * @throws URISyntaxException */ private void appendScripts (XhtmlDocument doc, boolean isExport) throws IOException, URISyntaxException{ List urls = Arrays.asList("~./js/datatables/datatables.min.js"); if (isExport){ // embed script by content for (String extraScriptPath : urls){ URL url = getClass().getResource(extraScriptPath.replace("~./", "/web/")); appendInlineScriptContent (doc, readResource(url)); } } else { // embed script by link for (String extraScriptUrl : urls){ embedScriptLink (doc, Executions.encodeURL(extraScriptUrl)); } } } /** * If isExport, embed css content, otherwise embed css url * @param doc * @param isExport * @param i18nURL * @throws IOException * @throws URISyntaxException */ private void appendStyles (XhtmlDocument doc, boolean isExport, String i18nURL) throws IOException, URISyntaxException{ List urls = Arrays.asList("~./js/datatables/datatables.min.css", "~./js/datatables/customization.css"); if (isExport){ // embed css by content for (String extraStylePath : urls){ URL url = getClass().getResource(extraStylePath.replace("~./", "/web/")); HTMLReportRenderer.appendInlineCss(doc, readResource(url)); } } else { // embed css by link for (String extraStyleUrl : urls){ embedStyleLink (doc, Executions.encodeURL(extraStyleUrl)); if (i18nURL != null) { link link = new link(); link.setHref(i18nURL); link.setRel("prefetch"); link.setType("application/json"); doc.appendHead(link); } } } } /** * Read contents from URL * @param url * @return contents from URL * @throws IOException */ private StringBuilder readResource(URL url) throws IOException { StringBuilder builder = new StringBuilder(); InputStream is = url.openStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); String line; while ((line = reader.readLine()) != null) { builder.append(line); } reader.close(); return builder; } /** * Embed script url into head tag * @param doc * @param scriptUrl */ protected void embedScriptLink (XhtmlDocument doc, String scriptUrl){ script jslink = new script(); jslink.setLanguage("javascript"); jslink.setSrc(scriptUrl); doc.appendHead(jslink); } /** * Embed css url into head tag * @param doc * @param scriptUrl */ protected void embedStyleLink (XhtmlDocument doc, String styleUrl){ link csslink = new link(); csslink.setType("text/css"); csslink.setRel("stylesheet"); csslink.setHref(styleUrl); doc.appendHead(csslink); } /** * Embed script content into head tag * @param doc * @param buildScriptContent */ private void appendInlineScriptContent (XhtmlDocument doc, StringBuilder buildScriptContent){ if (buildScriptContent.length() > 0){ buildScriptContent.insert(0, ""); doc.appendHead(buildScriptContent.toString()); } } }