You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ExcelToFoConverter.java 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.hssf.converter;
  16. import java.io.File;
  17. import java.util.ArrayList;
  18. import java.util.List;
  19. import javax.xml.transform.OutputKeys;
  20. import javax.xml.transform.Transformer;
  21. import javax.xml.transform.TransformerFactory;
  22. import javax.xml.transform.dom.DOMSource;
  23. import javax.xml.transform.stream.StreamResult;
  24. import org.apache.poi.hpsf.SummaryInformation;
  25. import org.apache.poi.hssf.usermodel.HSSFCell;
  26. import org.apache.poi.hssf.usermodel.HSSFCellStyle;
  27. import org.apache.poi.hssf.usermodel.HSSFFont;
  28. import org.apache.poi.hssf.usermodel.HSSFRichTextString;
  29. import org.apache.poi.hssf.usermodel.HSSFRow;
  30. import org.apache.poi.hssf.usermodel.HSSFSheet;
  31. import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  32. import org.apache.poi.hssf.util.HSSFColor;
  33. import org.apache.poi.hwpf.converter.FoDocumentFacade;
  34. import org.apache.poi.hwpf.converter.FontReplacer.Triplet;
  35. import org.apache.poi.ss.formula.eval.ErrorEval;
  36. import org.apache.poi.ss.usermodel.BorderStyle;
  37. import org.apache.poi.ss.usermodel.CellStyle;
  38. import org.apache.poi.ss.util.CellRangeAddress;
  39. import org.apache.poi.util.Beta;
  40. import org.apache.poi.util.POILogFactory;
  41. import org.apache.poi.util.POILogger;
  42. import org.apache.poi.util.XMLHelper;
  43. import org.w3c.dom.Document;
  44. import org.w3c.dom.Element;
  45. import org.w3c.dom.Text;
  46. /**
  47. * Converts xls files (97-2007) to XSL FO.
  48. *
  49. * @author Sergey Vladimirov (vlsergey {at} gmail {dot} com)
  50. */
  51. @Beta
  52. public class ExcelToFoConverter extends AbstractExcelConverter
  53. {
  54. private static final float CM_PER_INCH = 2.54f;
  55. private static final float DPI = 72;
  56. private static final POILogger logger = POILogFactory
  57. .getLogger( ExcelToFoConverter.class );
  58. private static final float PAPER_A4_HEIGHT_INCHES = 29.4f / CM_PER_INCH;
  59. private static final float PAPER_A4_WIDTH_INCHES = 21.0f / CM_PER_INCH;
  60. /**
  61. * Java main() interface to interact with {@link ExcelToFoConverter}
  62. *
  63. * <p>
  64. * Usage: ExcelToHtmlConverter infile outfile
  65. * </p>
  66. * Where infile is an input .xls file ( Word 97-2007) which will be rendered
  67. * as XSL FO into outfile
  68. */
  69. public static void main( String[] args ) throws Exception {
  70. if ( args.length < 2 ) {
  71. System.err.println( "Usage: ExcelToFoConverter <inputFile.xls> <saveTo.xml>" );
  72. return;
  73. }
  74. System.out.println( "Converting " + args[0] );
  75. System.out.println( "Saving output to " + args[1] );
  76. Document doc = ExcelToHtmlConverter.process( new File( args[0] ) );
  77. DOMSource domSource = new DOMSource( doc );
  78. StreamResult streamResult = new StreamResult( new File(args[1]) );
  79. TransformerFactory tf = TransformerFactory.newInstance();
  80. Transformer serializer = tf.newTransformer();
  81. // TODO set encoding from a command argument
  82. serializer.setOutputProperty( OutputKeys.ENCODING, "UTF-8" );
  83. serializer.setOutputProperty( OutputKeys.INDENT, "no" );
  84. serializer.setOutputProperty( OutputKeys.METHOD, "xml" );
  85. serializer.transform( domSource, streamResult );
  86. }
  87. /**
  88. * Converts Excel file (97-2007) into XSL FO file.
  89. *
  90. * @param xlsFile
  91. * file to process
  92. * @return DOM representation of result XSL FO
  93. */
  94. public static Document process( File xlsFile ) throws Exception
  95. {
  96. final HSSFWorkbook workbook = ExcelToFoUtils.loadXls( xlsFile );
  97. ExcelToFoConverter excelToHtmlConverter = new ExcelToFoConverter(
  98. XMLHelper.getDocumentBuilderFactory().newDocumentBuilder()
  99. .newDocument() );
  100. excelToHtmlConverter.processWorkbook( workbook );
  101. Document doc = excelToHtmlConverter.getDocument();
  102. workbook.close();
  103. return doc;
  104. }
  105. private final FoDocumentFacade foDocumentFacade;
  106. private float pageMarginInches = 0.4f;
  107. public ExcelToFoConverter( Document document )
  108. {
  109. this.foDocumentFacade = new FoDocumentFacade( document );
  110. }
  111. public ExcelToFoConverter( FoDocumentFacade foDocumentFacade )
  112. {
  113. this.foDocumentFacade = foDocumentFacade;
  114. }
  115. protected String createPageMaster( float tableWidthIn, String pageMasterName )
  116. {
  117. final float paperHeightIn;
  118. final float paperWidthIn;
  119. {
  120. float requiredWidthIn = tableWidthIn + 2 * getPageMarginInches();
  121. if ( requiredWidthIn < PAPER_A4_WIDTH_INCHES )
  122. {
  123. // portrait orientation
  124. paperWidthIn = PAPER_A4_WIDTH_INCHES;
  125. paperHeightIn = PAPER_A4_HEIGHT_INCHES;
  126. }
  127. else
  128. {
  129. // landscape orientation
  130. paperWidthIn = requiredWidthIn;
  131. paperHeightIn = paperWidthIn
  132. * ( PAPER_A4_WIDTH_INCHES / PAPER_A4_HEIGHT_INCHES );
  133. }
  134. }
  135. final float leftMargin = getPageMarginInches();
  136. final float rightMargin = getPageMarginInches();
  137. final float topMargin = getPageMarginInches();
  138. final float bottomMargin = getPageMarginInches();
  139. Element pageMaster = foDocumentFacade
  140. .addSimplePageMaster( pageMasterName );
  141. pageMaster.setAttribute( "page-height", paperHeightIn + "in" );
  142. pageMaster.setAttribute( "page-width", paperWidthIn + "in" );
  143. Element regionBody = foDocumentFacade.addRegionBody( pageMaster );
  144. regionBody.setAttribute( "margin", topMargin + "in " + rightMargin
  145. + "in " + bottomMargin + "in " + leftMargin + "in" );
  146. return pageMasterName;
  147. }
  148. @Override
  149. protected Document getDocument()
  150. {
  151. return foDocumentFacade.getDocument();
  152. }
  153. public float getPageMarginInches()
  154. {
  155. return pageMarginInches;
  156. }
  157. /**
  158. * Returns <tt>false</tt> if cell style by itself (without text, i.e.
  159. * borders, fill, etc.) worth a mention, <tt>true</tt> otherwise
  160. *
  161. * @return <tt>false</tt> if cell style by itself (without text, i.e.
  162. * borders, fill, etc.) worth a mention, <tt>true</tt> otherwise
  163. */
  164. protected boolean isEmptyStyle( CellStyle cellStyle ) {
  165. return cellStyle == null || (
  166. cellStyle.getFillPattern() == 0
  167. && cellStyle.getBorderTopEnum() == BorderStyle.NONE
  168. && cellStyle.getBorderRightEnum() == BorderStyle.NONE
  169. && cellStyle.getBorderBottomEnum() == BorderStyle.NONE
  170. && cellStyle.getBorderLeftEnum() == BorderStyle.NONE
  171. );
  172. }
  173. protected boolean processCell( HSSFWorkbook workbook, HSSFCell cell,
  174. Element tableCellElement, int normalWidthPx, int maxSpannedWidthPx,
  175. float normalHeightPt )
  176. {
  177. final HSSFCellStyle cellStyle = cell.getCellStyle();
  178. String value;
  179. switch ( cell.getCellTypeEnum() )
  180. {
  181. case STRING:
  182. // XXX: enrich
  183. value = cell.getRichStringCellValue().getString();
  184. break;
  185. case FORMULA:
  186. switch ( cell.getCachedFormulaResultTypeEnum() )
  187. {
  188. case STRING:
  189. HSSFRichTextString str = cell.getRichStringCellValue();
  190. if ( str != null && str.length() > 0 )
  191. {
  192. value = ( str.toString() );
  193. }
  194. else
  195. {
  196. value = ExcelToHtmlUtils.EMPTY;
  197. }
  198. break;
  199. case NUMERIC:
  200. double nValue = cell.getNumericCellValue();
  201. short df = cellStyle.getDataFormat();
  202. String dfs = cellStyle.getDataFormatString();
  203. value = _formatter.formatRawCellContents(nValue, df, dfs );
  204. break;
  205. case BOOLEAN:
  206. value = Boolean.toString( cell.getBooleanCellValue() );
  207. break;
  208. case ERROR:
  209. value = ErrorEval.getText( cell.getErrorCellValue() );
  210. break;
  211. default:
  212. logger.log(
  213. POILogger.WARN,
  214. "Unexpected cell cachedFormulaResultType ("
  215. + cell.getCachedFormulaResultTypeEnum() + ")" );
  216. value = ExcelToHtmlUtils.EMPTY;
  217. break;
  218. }
  219. break;
  220. case BLANK:
  221. value = ExcelToHtmlUtils.EMPTY;
  222. break;
  223. case NUMERIC:
  224. value = _formatter.formatCellValue( cell );
  225. break;
  226. case BOOLEAN:
  227. value = Boolean.toString( cell.getBooleanCellValue() );
  228. break;
  229. case ERROR:
  230. value = ErrorEval.getText( cell.getErrorCellValue() );
  231. break;
  232. default:
  233. logger.log( POILogger.WARN,
  234. "Unexpected cell type (" + cell.getCellTypeEnum() + ")" );
  235. return true;
  236. }
  237. final boolean noText = ExcelToHtmlUtils.isEmpty( value );
  238. final boolean wrapInDivs = !noText && !cellStyle.getWrapText();
  239. final boolean emptyStyle = isEmptyStyle( cellStyle );
  240. if ( !emptyStyle && noText ) {
  241. /*
  242. * if cell style is defined (like borders, etc.) but cell text
  243. * is empty, add "&nbsp;" to output, so browser won't collapse
  244. * and ignore cell
  245. */
  246. value = "\u00A0";
  247. }
  248. if ( isOutputLeadingSpacesAsNonBreaking() && value.startsWith( " " ) )
  249. {
  250. StringBuilder builder = new StringBuilder();
  251. for ( int c = 0; c < value.length(); c++ )
  252. {
  253. if ( value.charAt( c ) != ' ' ) {
  254. break;
  255. }
  256. builder.append( '\u00a0' );
  257. }
  258. if ( value.length() != builder.length() ) {
  259. builder.append( value.substring( builder.length() ) );
  260. }
  261. value = builder.toString();
  262. }
  263. Text text = foDocumentFacade.createText( value );
  264. Element block = foDocumentFacade.createBlock();
  265. if ( wrapInDivs )
  266. {
  267. block.setAttribute( "absolute-position", "fixed" );
  268. block.setAttribute( "left", "0px" );
  269. block.setAttribute( "top", "0px" );
  270. block.setAttribute( "bottom", "0px" );
  271. block.setAttribute( "min-width", normalWidthPx + "px" );
  272. if ( maxSpannedWidthPx != Integer.MAX_VALUE )
  273. {
  274. block.setAttribute( "max-width", maxSpannedWidthPx + "px" );
  275. }
  276. block.setAttribute( "overflow", "hidden" );
  277. block.setAttribute( "height", normalHeightPt + "pt" );
  278. block.setAttribute( "keep-together.within-line", "always" );
  279. block.setAttribute( "wrap-option", "no-wrap" );
  280. }
  281. processCellStyle( workbook, cell.getCellStyle(), tableCellElement,
  282. block );
  283. block.appendChild( text );
  284. tableCellElement.appendChild( block );
  285. return ExcelToHtmlUtils.isEmpty( value ) && emptyStyle;
  286. }
  287. protected void processCellStyle( HSSFWorkbook workbook,
  288. HSSFCellStyle cellStyle, Element cellTarget, Element blockTarget )
  289. {
  290. blockTarget.setAttribute( "white-space-collapse", "false" );
  291. {
  292. String textAlign = ExcelToFoUtils.getAlign( cellStyle
  293. .getAlignment() );
  294. if ( ExcelToFoUtils.isNotEmpty( textAlign ) )
  295. blockTarget.setAttribute( "text-align", textAlign );
  296. }
  297. if ( cellStyle.getFillPattern() == 0 )
  298. {
  299. // no fill
  300. }
  301. else if ( cellStyle.getFillPattern() == 1 )
  302. {
  303. final HSSFColor foregroundColor = cellStyle
  304. .getFillForegroundColorColor();
  305. if ( foregroundColor != null )
  306. cellTarget.setAttribute( "background-color",
  307. ExcelToFoUtils.getColor( foregroundColor ) );
  308. }
  309. else
  310. {
  311. final HSSFColor backgroundColor = cellStyle
  312. .getFillBackgroundColorColor();
  313. if ( backgroundColor != null )
  314. cellTarget.setAttribute( "background-color",
  315. ExcelToHtmlUtils.getColor( backgroundColor ) );
  316. }
  317. processCellStyleBorder( workbook, cellTarget, "top",
  318. cellStyle.getBorderTopEnum(), cellStyle.getTopBorderColor() );
  319. processCellStyleBorder( workbook, cellTarget, "right",
  320. cellStyle.getBorderRightEnum(), cellStyle.getRightBorderColor() );
  321. processCellStyleBorder( workbook, cellTarget, "bottom",
  322. cellStyle.getBorderBottomEnum(), cellStyle.getBottomBorderColor() );
  323. processCellStyleBorder( workbook, cellTarget, "left",
  324. cellStyle.getBorderLeftEnum(), cellStyle.getLeftBorderColor() );
  325. HSSFFont font = cellStyle.getFont( workbook );
  326. processCellStyleFont( workbook, blockTarget, font );
  327. }
  328. protected void processCellStyleBorder( HSSFWorkbook workbook,
  329. Element cellTarget, String type, BorderStyle xlsBorder, short borderColor )
  330. {
  331. if ( xlsBorder == BorderStyle.NONE )
  332. return;
  333. StringBuilder borderStyle = new StringBuilder();
  334. borderStyle.append( ExcelToHtmlUtils.getBorderWidth( xlsBorder ) );
  335. final HSSFColor color = workbook.getCustomPalette().getColor(
  336. borderColor );
  337. if ( color != null )
  338. {
  339. borderStyle.append( ' ' );
  340. borderStyle.append( ExcelToHtmlUtils.getColor( color ) );
  341. borderStyle.append( ' ' );
  342. borderStyle.append( ExcelToHtmlUtils.getBorderStyle( xlsBorder ) );
  343. }
  344. cellTarget.setAttribute( "border-" + type, borderStyle.toString() );
  345. }
  346. protected void processCellStyleFont( HSSFWorkbook workbook,
  347. Element blockTarget, HSSFFont font )
  348. {
  349. Triplet triplet = new Triplet();
  350. triplet.fontName = font.getFontName();
  351. switch ( font.getBoldweight() )
  352. {
  353. case HSSFFont.BOLDWEIGHT_BOLD:
  354. triplet.bold = true;
  355. break;
  356. case HSSFFont.BOLDWEIGHT_NORMAL:
  357. default:
  358. triplet.bold = false;
  359. break;
  360. }
  361. if ( font.getItalic() )
  362. {
  363. triplet.italic = true;
  364. }
  365. getFontReplacer().update( triplet );
  366. setBlockProperties( blockTarget, triplet );
  367. final HSSFColor fontColor = workbook.getCustomPalette().getColor(
  368. font.getColor() );
  369. if ( fontColor != null )
  370. blockTarget.setAttribute( "color",
  371. ExcelToHtmlUtils.getColor( fontColor ) );
  372. if ( font.getFontHeightInPoints() != 0 )
  373. blockTarget.setAttribute( "font-size", font.getFontHeightInPoints()
  374. + "pt" );
  375. }
  376. protected void processColumnHeaders( HSSFSheet sheet, int maxSheetColumns,
  377. Element table )
  378. {
  379. Element tableHeader = foDocumentFacade.createTableHeader();
  380. Element row = foDocumentFacade.createTableRow();
  381. if ( isOutputRowNumbers() )
  382. {
  383. // empty cell at left-top corner
  384. final Element tableCellElement = foDocumentFacade.createTableCell();
  385. tableCellElement.appendChild( foDocumentFacade.createBlock() );
  386. row.appendChild( tableCellElement );
  387. }
  388. for ( int c = 0; c < maxSheetColumns; c++ )
  389. {
  390. if ( !isOutputHiddenColumns() && sheet.isColumnHidden( c ) )
  391. continue;
  392. Element cell = foDocumentFacade.createTableCell();
  393. Element block = foDocumentFacade.createBlock();
  394. block.setAttribute( "text-align", "center" );
  395. block.setAttribute( "font-weight", "bold" );
  396. String text = getColumnName( c );
  397. block.appendChild( foDocumentFacade.createText( text ) );
  398. cell.appendChild( block );
  399. row.appendChild( cell );
  400. }
  401. tableHeader.appendChild( row );
  402. table.appendChild( tableHeader );
  403. }
  404. /**
  405. * Creates COLGROUP element with width specified for all columns. (Except
  406. * first if <tt>{@link #isOutputRowNumbers()}==true</tt>)
  407. *
  408. * @return table width in inches
  409. */
  410. protected float processColumnWidths( HSSFSheet sheet, int maxSheetColumns,
  411. Element table )
  412. {
  413. float tableWidth = 0;
  414. if ( isOutputRowNumbers() )
  415. {
  416. final float columnWidthIn = getDefaultColumnWidth( sheet ) / DPI;
  417. final Element rowNumberColumn = foDocumentFacade
  418. .createTableColumn();
  419. rowNumberColumn.setAttribute( "column-width", columnWidthIn + "in" );
  420. table.appendChild( rowNumberColumn );
  421. tableWidth += columnWidthIn;
  422. }
  423. for ( int c = 0; c < maxSheetColumns; c++ )
  424. {
  425. if ( !isOutputHiddenColumns() && sheet.isColumnHidden( c ) )
  426. continue;
  427. final float columnWidthIn = getColumnWidth( sheet, c ) / DPI;
  428. Element col = foDocumentFacade.createTableColumn();
  429. col.setAttribute( "column-width", columnWidthIn + "in" );
  430. table.appendChild( col );
  431. tableWidth += columnWidthIn;
  432. }
  433. table.setAttribute( "width", tableWidth + "in" );
  434. return tableWidth;
  435. }
  436. protected void processDocumentInformation(
  437. SummaryInformation summaryInformation )
  438. {
  439. if ( ExcelToFoUtils.isNotEmpty( summaryInformation.getTitle() ) )
  440. foDocumentFacade.setTitle( summaryInformation.getTitle() );
  441. if ( ExcelToFoUtils.isNotEmpty( summaryInformation.getAuthor() ) )
  442. foDocumentFacade.setCreator( summaryInformation.getAuthor() );
  443. if ( ExcelToFoUtils.isNotEmpty( summaryInformation.getKeywords() ) )
  444. foDocumentFacade.setKeywords( summaryInformation.getKeywords() );
  445. if ( ExcelToFoUtils.isNotEmpty( summaryInformation.getComments() ) )
  446. foDocumentFacade.setDescription( summaryInformation.getComments() );
  447. }
  448. /**
  449. * @return maximum 1-base index of column that were rendered, zero if none
  450. */
  451. protected int processRow( HSSFWorkbook workbook,
  452. CellRangeAddress[][] mergedRanges, HSSFRow row,
  453. Element tableRowElement )
  454. {
  455. final HSSFSheet sheet = row.getSheet();
  456. final short maxColIx = row.getLastCellNum();
  457. if ( maxColIx <= 0 )
  458. {
  459. return 0;
  460. }
  461. final List<Element> emptyCells = new ArrayList<Element>( maxColIx );
  462. if ( isOutputRowNumbers() )
  463. {
  464. Element tableRowNumberCellElement = processRowNumber( row );
  465. emptyCells.add( tableRowNumberCellElement );
  466. }
  467. int maxRenderedColumn = 0;
  468. for ( int colIx = 0; colIx < maxColIx; colIx++ )
  469. {
  470. if ( !isOutputHiddenColumns() && sheet.isColumnHidden( colIx ) )
  471. continue;
  472. CellRangeAddress range = ExcelToHtmlUtils.getMergedRange(
  473. mergedRanges, row.getRowNum(), colIx );
  474. if ( range != null
  475. && ( range.getFirstColumn() != colIx || range.getFirstRow() != row
  476. .getRowNum() ) )
  477. continue;
  478. HSSFCell cell = row.getCell( colIx );
  479. // spanning using overlapping blocks
  480. int divWidthPx = 0;
  481. {
  482. divWidthPx = getColumnWidth( sheet, colIx );
  483. boolean hasBreaks = false;
  484. for ( int nextColumnIndex = colIx + 1; nextColumnIndex < maxColIx; nextColumnIndex++ )
  485. {
  486. if ( !isOutputHiddenColumns()
  487. && sheet.isColumnHidden( nextColumnIndex ) )
  488. continue;
  489. if ( row.getCell( nextColumnIndex ) != null
  490. && !isTextEmpty( row.getCell( nextColumnIndex ) ) )
  491. {
  492. hasBreaks = true;
  493. break;
  494. }
  495. divWidthPx += getColumnWidth( sheet, nextColumnIndex );
  496. }
  497. if ( !hasBreaks )
  498. divWidthPx = Integer.MAX_VALUE;
  499. }
  500. Element tableCellElement = foDocumentFacade.createTableCell();
  501. if ( range != null )
  502. {
  503. if ( range.getFirstColumn() != range.getLastColumn() )
  504. tableCellElement.setAttribute(
  505. "number-columns-spanned",
  506. String.valueOf( range.getLastColumn()
  507. - range.getFirstColumn() + 1 ) );
  508. if ( range.getFirstRow() != range.getLastRow() )
  509. tableCellElement.setAttribute(
  510. "number-rows-spanned",
  511. String.valueOf( range.getLastRow()
  512. - range.getFirstRow() + 1 ) );
  513. }
  514. boolean emptyCell;
  515. if ( cell != null )
  516. {
  517. emptyCell = processCell( workbook, cell, tableCellElement,
  518. getColumnWidth( sheet, colIx ), divWidthPx,
  519. row.getHeight() / 20f );
  520. }
  521. else
  522. {
  523. tableCellElement.appendChild( foDocumentFacade.createBlock() );
  524. emptyCell = true;
  525. }
  526. if ( emptyCell )
  527. {
  528. emptyCells.add( tableCellElement );
  529. }
  530. else
  531. {
  532. for ( Element emptyCellElement : emptyCells )
  533. {
  534. tableRowElement.appendChild( emptyCellElement );
  535. }
  536. emptyCells.clear();
  537. tableRowElement.appendChild( tableCellElement );
  538. maxRenderedColumn = colIx;
  539. }
  540. }
  541. return maxRenderedColumn + 1;
  542. }
  543. protected Element processRowNumber( HSSFRow row )
  544. {
  545. Element tableRowNumberCellElement = foDocumentFacade.createTableCell();
  546. Element block = foDocumentFacade.createBlock();
  547. block.setAttribute( "text-align", "right" );
  548. block.setAttribute( "font-weight", "bold" );
  549. Text text = foDocumentFacade.createText( getRowName( row ) );
  550. block.appendChild( text );
  551. tableRowNumberCellElement.appendChild( block );
  552. return tableRowNumberCellElement;
  553. }
  554. protected float processSheet( HSSFWorkbook workbook, HSSFSheet sheet,
  555. Element flow )
  556. {
  557. final int physicalNumberOfRows = sheet.getPhysicalNumberOfRows();
  558. if ( physicalNumberOfRows <= 0 )
  559. return 0;
  560. processSheetName( sheet, flow );
  561. Element table = foDocumentFacade.createTable();
  562. table.setAttribute( "table-layout", "fixed" );
  563. Element tableBody = foDocumentFacade.createTableBody();
  564. final CellRangeAddress[][] mergedRanges = ExcelToHtmlUtils
  565. .buildMergedRangesMap( sheet );
  566. final List<Element> emptyRowElements = new ArrayList<Element>(
  567. physicalNumberOfRows );
  568. int maxSheetColumns = 1;
  569. for ( int r = sheet.getFirstRowNum(); r <= sheet.getLastRowNum(); r++ )
  570. {
  571. HSSFRow row = sheet.getRow( r );
  572. if ( row == null )
  573. continue;
  574. if ( !isOutputHiddenRows() && row.getZeroHeight() )
  575. continue;
  576. Element tableRowElement = foDocumentFacade.createTableRow();
  577. tableRowElement.setAttribute( "height", row.getHeight() / 20f
  578. + "pt" );
  579. int maxRowColumnNumber = processRow( workbook, mergedRanges, row,
  580. tableRowElement );
  581. if ( tableRowElement.getChildNodes().getLength() == 0 )
  582. {
  583. Element emptyCellElement = foDocumentFacade.createTableCell();
  584. emptyCellElement.appendChild( foDocumentFacade.createBlock() );
  585. tableRowElement.appendChild( emptyCellElement );
  586. }
  587. if ( maxRowColumnNumber == 0 )
  588. {
  589. emptyRowElements.add( tableRowElement );
  590. }
  591. else
  592. {
  593. if ( !emptyRowElements.isEmpty() )
  594. {
  595. for ( Element emptyRowElement : emptyRowElements )
  596. {
  597. tableBody.appendChild( emptyRowElement );
  598. }
  599. emptyRowElements.clear();
  600. }
  601. tableBody.appendChild( tableRowElement );
  602. }
  603. maxSheetColumns = Math.max( maxSheetColumns, maxRowColumnNumber );
  604. }
  605. float tableWidthIn = processColumnWidths( sheet, maxSheetColumns, table );
  606. if ( isOutputColumnHeaders() )
  607. {
  608. processColumnHeaders( sheet, maxSheetColumns, table );
  609. }
  610. table.appendChild( tableBody );
  611. flow.appendChild( table );
  612. return tableWidthIn;
  613. }
  614. /**
  615. * Process single sheet (as specified by 0-based sheet index)
  616. *
  617. * @return <tt>true</tt> if result were added to FO document, <tt>false</tt>
  618. * otherwise
  619. */
  620. protected boolean processSheet( HSSFWorkbook workbook, int sheetIndex )
  621. {
  622. String pageMasterName = "sheet-" + sheetIndex;
  623. Element pageSequence = foDocumentFacade
  624. .createPageSequence( pageMasterName );
  625. Element flow = foDocumentFacade.addFlowToPageSequence( pageSequence,
  626. "xsl-region-body" );
  627. HSSFSheet sheet = workbook.getSheetAt( sheetIndex );
  628. float tableWidthIn = processSheet( workbook, sheet, flow );
  629. if ( tableWidthIn == 0 )
  630. return false;
  631. createPageMaster( tableWidthIn, pageMasterName );
  632. foDocumentFacade.addPageSequence( pageSequence );
  633. return true;
  634. }
  635. protected void processSheetName( HSSFSheet sheet, Element flow )
  636. {
  637. Element titleBlock = foDocumentFacade.createBlock();
  638. Triplet triplet = new Triplet();
  639. triplet.bold = true;
  640. triplet.italic = false;
  641. triplet.fontName = "Arial";
  642. getFontReplacer().update( triplet );
  643. setBlockProperties( titleBlock, triplet );
  644. titleBlock.setAttribute( "font-size", "200%" );
  645. Element titleInline = foDocumentFacade.createInline();
  646. titleInline.appendChild( foDocumentFacade.createText( sheet
  647. .getSheetName() ) );
  648. titleBlock.appendChild( titleInline );
  649. flow.appendChild( titleBlock );
  650. Element titleBlock2 = foDocumentFacade.createBlock();
  651. Element titleInline2 = foDocumentFacade.createInline();
  652. titleBlock2.appendChild( titleInline2 );
  653. flow.appendChild( titleBlock2 );
  654. }
  655. public void processWorkbook( HSSFWorkbook workbook )
  656. {
  657. final SummaryInformation summaryInformation = workbook
  658. .getSummaryInformation();
  659. if ( summaryInformation != null )
  660. {
  661. processDocumentInformation( summaryInformation );
  662. }
  663. for ( int s = 0; s < workbook.getNumberOfSheets(); s++ )
  664. {
  665. processSheet( workbook, s );
  666. }
  667. }
  668. private void setBlockProperties( Element textBlock, Triplet triplet )
  669. {
  670. if ( triplet.bold )
  671. textBlock.setAttribute( "font-weight", "bold" );
  672. if ( triplet.italic )
  673. textBlock.setAttribute( "font-style", "italic" );
  674. if ( ExcelToFoUtils.isNotEmpty( triplet.fontName ) )
  675. textBlock.setAttribute( "font-family", triplet.fontName );
  676. }
  677. public void setPageMarginInches( float pageMarginInches )
  678. {
  679. this.pageMarginInches = pageMarginInches;
  680. }
  681. }