diff --git a/src/scratchpad/src/org/apache/poi/hssf/converter/ExcelToHtmlConverter.java b/src/scratchpad/src/org/apache/poi/hssf/converter/ExcelToHtmlConverter.java index 7377ba3bb..99cf70c93 100644 --- a/src/scratchpad/src/org/apache/poi/hssf/converter/ExcelToHtmlConverter.java +++ b/src/scratchpad/src/org/apache/poi/hssf/converter/ExcelToHtmlConverter.java @@ -123,6 +123,10 @@ public class ExcelToHtmlConverter private final HtmlDocumentFacade htmlDocumentFacade; + private boolean outputColumnHeaders = true; + + private boolean outputRowNumbers = true; + private final Element styles; private final Set usedStyles = new LinkedHashSet(); @@ -249,11 +253,42 @@ public class ExcelToHtmlConverter } } + /** + * Generates name for output as column header in case + * {@link #isOutputColumnHeaders()} == true + * + * @param columnIndex + * 0-based column index + */ + protected String getColumnName( int columnIndex ) + { + return String.valueOf( columnIndex + 1 ); + } + public Document getDocument() { return htmlDocumentFacade.getDocument(); } + /** + * Generates name for output as row number in case + * {@link #isOutputRowNumbers()} == true + */ + private String getRowName( HSSFRow row ) + { + return String.valueOf( row.getRowNum() + 1 ); + } + + public boolean isOutputColumnHeaders() + { + return outputColumnHeaders; + } + + public boolean isOutputRowNumbers() + { + return outputRowNumbers; + } + protected boolean processCell( HSSFCell cell, Element tableCellElement ) { final HSSFCellStyle cellStyle = cell.getCellStyle(); @@ -348,6 +383,52 @@ public class ExcelToHtmlConverter return ExcelToHtmlUtils.isEmpty( value ) && cellStyleIndex == 0; } + protected void processColumnHeaders( int maxSheetColumns, Element table ) + { + Element tableHeader = htmlDocumentFacade.createTableHeader(); + table.appendChild( tableHeader ); + + Element tr = htmlDocumentFacade.createTableRow(); + + if ( isOutputRowNumbers() ) + { + // empty row at left-top corner + tr.appendChild( htmlDocumentFacade.createTableHeaderCell() ); + } + + for ( int c = 0; c < maxSheetColumns; c++ ) + { + Element th = htmlDocumentFacade.createTableHeaderCell(); + String text = getColumnName( c ); + th.appendChild( htmlDocumentFacade.createText( text ) ); + tr.appendChild( th ); + } + tableHeader.appendChild( tr ); + } + + /** + * Creates COLGROUP element with width specified for all columns. (Except + * first if {@link #isOutputRowNumbers()}==true) + */ + protected void processColumnWidths( HSSFSheet sheet, int maxSheetColumns, + Element table ) + { + // draw COLS after we know max column number + Element columnGroup = htmlDocumentFacade.createTableColumnGroup(); + if ( isOutputRowNumbers() ) + { + columnGroup.appendChild( htmlDocumentFacade.createTableColumn() ); + } + for ( int c = 0; c < maxSheetColumns; c++ ) + { + Element col = htmlDocumentFacade.createTableColumn(); + col.setAttribute( "width", String.valueOf( ExcelToHtmlUtils + .getColumnWidthInPx( sheet.getColumnWidth( c ) ) ) ); + columnGroup.appendChild( col ); + } + table.appendChild( columnGroup ); + } + protected void processDocumentInformation( SummaryInformation summaryInformation ) { @@ -365,16 +446,26 @@ public class ExcelToHtmlConverter .addDescription( summaryInformation.getComments() ); } - protected boolean processRow( HSSFRow row, Element tableRowElement ) + /** + * @return maximum 1-base index of column that were rendered, zero if none + */ + protected int processRow( HSSFRow row, Element tableRowElement ) { - boolean emptyRow = true; - final short maxColIx = row.getLastCellNum(); if ( maxColIx <= 0 ) - return true; + return 0; final List emptyCells = new ArrayList( maxColIx ); + if ( isOutputRowNumbers() ) + { + Element tableRowNumberCellElement = htmlDocumentFacade + .createTableHeaderCell(); + processRowNumber( row, tableRowNumberCellElement ); + emptyCells.add( tableRowNumberCellElement ); + } + + int maxRenderedColumn = 0; for ( int colIx = 0; colIx < maxColIx; colIx++ ) { HSSFCell cell = row.getCell( colIx ); @@ -404,18 +495,24 @@ public class ExcelToHtmlConverter emptyCells.clear(); tableRowElement.appendChild( tableCellElement ); - emptyRow = false; + maxRenderedColumn = colIx; } } - return emptyRow; + return maxRenderedColumn + 1; + } + + protected void processRowNumber( HSSFRow row, + Element tableRowNumberCellElement ) + { + tableRowNumberCellElement.setAttribute( "class", "rownumber" ); + Text text = htmlDocumentFacade.createText( getRowName( row ) ); + tableRowNumberCellElement.appendChild( text ); } protected void processSheet( HSSFSheet sheet ) { - Element h1 = htmlDocumentFacade.createHeader1(); - h1.appendChild( htmlDocumentFacade.createText( sheet.getSheetName() ) ); - htmlDocumentFacade.getBody().appendChild( h1 ); + processSheetHeader( htmlDocumentFacade.getBody(), sheet ); final int physicalNumberOfRows = sheet.getPhysicalNumberOfRows(); if ( physicalNumberOfRows <= 0 ) @@ -426,24 +523,24 @@ public class ExcelToHtmlConverter final List emptyRowElements = new ArrayList( physicalNumberOfRows ); - + int maxSheetColumns = 1; for ( int r = 0; r < physicalNumberOfRows; r++ ) { HSSFRow row = sheet.getRow( r ); Element tableRowElement = htmlDocumentFacade.createTableRow(); - boolean emptyRow; + int maxRowColumnNumber; if ( row != null ) { - emptyRow = processRow( row, tableRowElement ); + maxRowColumnNumber = processRow( row, tableRowElement ); } else { - emptyRow = true; + maxRowColumnNumber = 0; } - if ( emptyRow ) + if ( maxRowColumnNumber == 0 ) { emptyRowElements.add( tableRowElement ); } @@ -451,22 +548,37 @@ public class ExcelToHtmlConverter { if ( !emptyRowElements.isEmpty() ) { - for ( Element emptyCellElement : emptyRowElements ) + for ( Element emptyRowElement : emptyRowElements ) { - tableBody.appendChild( emptyCellElement ); + tableBody.appendChild( emptyRowElement ); } emptyRowElements.clear(); } tableBody.appendChild( tableRowElement ); - emptyRow = false; } + maxSheetColumns = Math.max( maxSheetColumns, maxRowColumnNumber ); + } + + processColumnWidths( sheet, maxSheetColumns, table ); + + if ( isOutputColumnHeaders() ) + { + processColumnHeaders( maxSheetColumns, table ); } table.appendChild( tableBody ); + htmlDocumentFacade.getBody().appendChild( table ); } + protected void processSheetHeader( Element htmlBody, HSSFSheet sheet ) + { + Element h2 = htmlDocumentFacade.createHeader2(); + h2.appendChild( htmlDocumentFacade.createText( sheet.getSheetName() ) ); + htmlBody.appendChild( h2 ); + } + public void processWorkbook( HSSFWorkbook workbook ) { final SummaryInformation summaryInformation = workbook @@ -476,6 +588,12 @@ public class ExcelToHtmlConverter processDocumentInformation( summaryInformation ); } + for ( int s = 0; s < workbook.getNumberOfSheets(); s++ ) + { + HSSFSheet sheet = workbook.getSheetAt( s ); + processSheet( sheet ); + } + for ( short i = 0; i < workbook.getNumCellStyles(); i++ ) { HSSFCellStyle cellStyle = workbook.getCellStyleAt( i ); @@ -488,11 +606,15 @@ public class ExcelToHtmlConverter .createText( "td.cellstyle_" + i + "{" + buildStyle( workbook, cellStyle ) + "}\n" ) ); } + } - for ( int s = 0; s < workbook.getNumberOfSheets(); s++ ) - { - HSSFSheet sheet = workbook.getSheetAt( s ); - processSheet( sheet ); - } + public void setOutputColumnHeaders( boolean outputColumnHeaders ) + { + this.outputColumnHeaders = outputColumnHeaders; + } + + public void setOutputRowNumbers( boolean outputRowNumbers ) + { + this.outputRowNumbers = outputRowNumbers; } } diff --git a/src/scratchpad/src/org/apache/poi/hssf/converter/ExcelToHtmlUtils.java b/src/scratchpad/src/org/apache/poi/hssf/converter/ExcelToHtmlUtils.java index 3e35a716f..0131b6f30 100644 --- a/src/scratchpad/src/org/apache/poi/hssf/converter/ExcelToHtmlUtils.java +++ b/src/scratchpad/src/org/apache/poi/hssf/converter/ExcelToHtmlUtils.java @@ -29,6 +29,9 @@ public class ExcelToHtmlUtils { static final String EMPTY = ""; + private static final short EXCEL_COLUMN_WIDTH_FACTOR = 256; + private static final int UNIT_OFFSET_LENGTH = 7; + public static String getBorderStyle( short xlsBorder ) { final String borderStyle; @@ -93,6 +96,23 @@ public class ExcelToHtmlUtils return stringBuilder.toString(); } + /** + * See here for Xio explanation and details + */ + public static int getColumnWidthInPx( int widthUnits ) + { + int pixels = ( widthUnits / EXCEL_COLUMN_WIDTH_FACTOR ) + * UNIT_OFFSET_LENGTH; + + int offsetWidthUnits = widthUnits % EXCEL_COLUMN_WIDTH_FACTOR; + pixels += Math.round( offsetWidthUnits + / ( (float) EXCEL_COLUMN_WIDTH_FACTOR / UNIT_OFFSET_LENGTH ) ); + + return pixels; + } + static boolean isEmpty( String str ) { return str == null || str.length() == 0; diff --git a/src/scratchpad/src/org/apache/poi/hwpf/converter/HtmlDocumentFacade.java b/src/scratchpad/src/org/apache/poi/hwpf/converter/HtmlDocumentFacade.java index d43fd4494..a1d951aaa 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/converter/HtmlDocumentFacade.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/converter/HtmlDocumentFacade.java @@ -75,6 +75,11 @@ public class HtmlDocumentFacade return document.createElement( "h1" ); } + public Element createHeader2() + { + return document.createElement( "h2" ); + } + public Element createHyperlink( String internalDestination ) { final Element basicLink = document.createElement( "a" ); @@ -112,6 +117,16 @@ public class HtmlDocumentFacade return document.createElement( "td" ); } + public Element createTableColumn() + { + return document.createElement( "col" ); + } + + public Element createTableColumnGroup() + { + return document.createElement( "colgroup" ); + } + public Element createTableHeader() { return document.createElement( "thead" );