]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.document.linking.ui/src/org/simantics/document/linking/report/pdf/PDFTable.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.document.linking.ui / src / org / simantics / document / linking / report / pdf / PDFTable.java
index 8f9dafadf3c8587a3fdc1f7f897134d518ce919d..902d27159d219ac79c098cbe868b9605d4821b8a 100644 (file)
-package org.simantics.document.linking.report.pdf;\r
-\r
-import java.awt.Color;\r
-import java.awt.Font;\r
-import java.awt.Graphics2D;\r
-import java.awt.Shape;\r
-import java.awt.font.LineBreakMeasurer;\r
-import java.awt.font.TextAttribute;\r
-import java.awt.font.TextLayout;\r
-import java.net.URL;\r
-import java.text.AttributedCharacterIterator;\r
-import java.text.AttributedString;\r
-import java.util.ArrayList;\r
-import java.util.Collections;\r
-import java.util.Hashtable;\r
-import java.util.List;\r
-\r
-import org.simantics.document.linking.report.Document.TextSize;\r
-import org.simantics.document.linking.report.Table;\r
-import org.simantics.document.linking.report.TableColumn;\r
-import org.simantics.document.linking.report.TableColumn.Alignment;\r
-import org.simantics.document.linking.report.TableRow;\r
-import org.simantics.document.linking.report.TextItem;\r
-import org.simantics.document.linking.report.URLItem;\r
-\r
-import com.lowagie.text.pdf.PdfAction;\r
-\r
-\r
-public class PDFTable implements Table, PDFElement {\r
-       PDFDocument writer;\r
-       PDFPageStream stream;\r
-       PDFPage startPage;\r
-       \r
-       int currentLine = 0;\r
-       \r
-       List<TableColumn> columns = new ArrayList<TableColumn>();\r
-       List<String> columnNames = new ArrayList<String>();\r
-       List<Integer> columnSizes = new ArrayList<Integer>();\r
-       List<Integer> columnPositions = new ArrayList<Integer>();\r
-       \r
-       TextItem title = null;\r
-       \r
-       boolean headerVisible = true;\r
-       private boolean linesVisible = true;\r
-       private boolean linesPrevVisible = true;\r
-       boolean clipText = false;\r
-       \r
-       int textOffsetX = 2;\r
-       int textOffsetY = 2;\r
-       \r
-       public PDFTable(PDFDocument writer, PDFPageStream stream) {\r
-               this.writer = writer;\r
-               this.stream = stream;\r
-               this.startPage = stream.getCurrentPage();\r
-               \r
-       }\r
-       \r
-       public PDFTable(PDFTable table) {\r
-               this.writer = table.writer;\r
-               this.stream = table.stream;\r
-               this.startPage = stream.getCurrentPage();\r
-               this.columns.addAll(table.columns);\r
-               this.columnNames.addAll(table.columnNames);\r
-               \r
-               updateColumnPositions();\r
-       }\r
-       \r
-       \r
-       @Override\r
-       public PDFPage getPage() {\r
-               return startPage;\r
-       }\r
-       \r
-       /* (non-Javadoc)\r
-        * @see org.simantics.document.linking.report.Table#addColumn(java.lang.String, double)\r
-        */\r
-       @Override\r
-       public TableColumn addColumn(String name, double width) {\r
-               TableColumn tc = new TableColumn(name, width);\r
-               columns.add(tc);\r
-               columnNames.add(name);\r
-               \r
-               updateColumnPositions();\r
-               return tc;\r
-       }\r
-       \r
-       private void updateColumnPositions() {\r
-               int pos = 0;\r
-               columnSizes.clear();\r
-               columnPositions.clear();\r
-               for (TableColumn c : columns) {\r
-                       int size = (int)(c.getWidth()*stream.getContentWidth());\r
-                       columnSizes.add(size);\r
-                       columnPositions.add(pos);\r
-                       pos+=size;\r
-               }\r
-       }\r
-       \r
-       @Override\r
-       public List<TableColumn> getColumns() {\r
-               return columns;\r
-       }\r
-       \r
-       @Override\r
-       public boolean isLinesVisible() {\r
-               return linesVisible;\r
-       }\r
-       \r
-       @Override\r
-       public void setLinesVisible(boolean b) {\r
-               if (this.linesVisible == b)\r
-                       return;\r
-               this.linesPrevVisible = linesVisible;\r
-               this.linesVisible = b;\r
-       }\r
-       \r
-       @Override\r
-       public boolean isHeaderVisible() {\r
-               return headerVisible;\r
-       }\r
-       \r
-       @Override\r
-       public void setHeaderVisible(boolean b) {\r
-               this.headerVisible = b;\r
-       }\r
-       \r
-       private boolean isFirstLine() {\r
-               return currentLine == 0 || stream.getCurrentPage().currentLine == 1;\r
-       }\r
-       \r
-       @Override\r
-       public void setTitle(String title) {\r
-               try {\r
-                       this.title = writer.newItem(TextItem.class);\r
-                       this.title.setText(title);\r
-               } catch (Exception e) {\r
-                       \r
-               }\r
-       }\r
-       \r
-       @Override\r
-       public void setTitle(TextItem title){\r
-               this.title = title;\r
-       }\r
-       \r
-       /* (non-Javadoc)\r
-        * @see org.simantics.document.linking.report.Table#writeRow(java.lang.String[])\r
-        */\r
-       @Override\r
-       public TableRow writeRow(String... line) throws Exception{\r
-               List<String> list = new ArrayList<String>(line.length);\r
-               for (String s : line)\r
-                       list.add(s);\r
-               return writeRow(list);\r
-       }\r
-       \r
-       /* (non-Javadoc)\r
-        * @see org.simantics.document.linking.report.Table#writeRow(java.util.List)\r
-        */\r
-       @Override\r
-       public TableRow writeRow(List<String> line) throws Exception{\r
-               if (isFirstLine())\r
-                       writeHeader();\r
-               return _writeRow(line);\r
-       }\r
-       \r
-       @Override\r
-       public TableRow writeRowItem(TextItem... line) throws Exception {\r
-               List<TextItem> list = new ArrayList<TextItem>(line.length);\r
-               for (TextItem s : line)\r
-                       list.add(s);\r
-               return writeRowItem(list);\r
-       }\r
-       \r
-       @Override\r
-       public TableRow writeRowItem(List<TextItem> line) throws Exception {\r
-               if (isFirstLine())\r
-                       writeHeader();\r
-               return _writeRow2(line);\r
-       }\r
-       \r
-       private TableRow _writeRow(List<String> line) throws Exception {\r
-               int h = getTextHeight();\r
-               int ht = getTopHeight();\r
-               int hb = getBottomHeight();\r
-               PDFPage page = getCurrentPage();\r
-               Graphics2D g2d = page.g2d;\r
-               Shape clip = g2d.getClip();\r
-               \r
-               if (clipText) {\r
-                       for (int i = 0; i < line.size(); i++) {\r
-                               if (line.get(i) == null)\r
-                                       continue;\r
-                               g2d.setClip(columnPositions.get(i),ht-1,columnSizes.get(i),hb-ht+2);\r
-                               g2d.drawString(line.get(i), columnPositions.get(i)+textOffsetX, h);\r
-                       }\r
-                       g2d.setClip(clip);\r
-                       if (linesVisible) {\r
-                               for (int i = 0; i < line.size(); i++) {\r
-                                       g2d.drawLine(columnPositions.get(i), ht, columnPositions.get(i), hb);\r
-                               }\r
-                               if (isFirstLine() || !linesPrevVisible) {\r
-                                       g2d.drawLine(0, ht, stream.contentWidth, ht);\r
-                                       linesPrevVisible = true;\r
-                               }\r
-                               g2d.drawLine(stream.contentWidth, ht, stream.contentWidth, hb);\r
-                               g2d.drawLine(0, hb, stream.contentWidth, hb);\r
-                       }\r
-                       currentLine++;\r
-                       page.currentLine++;\r
-                       page.availableLines--;\r
-                       page.currentPixel += getLineHeight();\r
-               } else {\r
-                       PositionedRow row = _getRow(line);\r
-                       if (stream.contentHeight-page.currentPixel < row.reservedSpace) {\r
-                               stream.nextPage();\r
-                               page = getCurrentPage();\r
-                               g2d = page.g2d;\r
-                               writeHeader();\r
-                               row = _getRow(line);\r
-                       }\r
-                       row.render(g2d);\r
-                       currentLine+= row.realLines;\r
-                       page.currentLine+= row.realLines;\r
-                       page.currentPixel += row.reservedSpace;\r
-                       page.estimateAvailableLines();\r
-               }\r
-               \r
-               stream.checkNextPage();\r
-               return new PDFTableRow();\r
-       }\r
-       \r
-       private TableRow _writeRow2(List<TextItem> line) throws Exception {\r
-               int h = getTextHeight();\r
-               int ht = getTopHeight();\r
-               int hb = getBottomHeight();\r
-               PDFPage page = getCurrentPage();\r
-               Graphics2D g2d = page.g2d;\r
-               Shape clip = g2d.getClip();\r
-               \r
-               if (clipText) {\r
-                       for (int i = 0; i < line.size(); i++) {\r
-                               TextItem text = line.get(i);\r
-                               if (text == null)\r
-                                       continue;\r
-                               g2d.setClip(columnPositions.get(i),ht,columnSizes.get(i),hb-ht);\r
-                               g2d.drawString(text.getText(), columnPositions.get(i)+textOffsetX, h);\r
-                               if (text instanceof URLItem) {\r
-                                       URL url = ((URLItem)text).getURL();\r
-                                       if (url != null) {\r
-                                               addLink(url, columnPositions.get(i),ht,columnSizes.get(i),hb-ht);\r
-                                       }\r
-                               }\r
-                       }\r
-                       g2d.setClip(clip);\r
-                       if (linesVisible) {\r
-                               for (int i = 0; i < line.size(); i++) {\r
-                                       g2d.drawLine(columnPositions.get(i), ht, columnPositions.get(i), hb);\r
-                               }\r
-                               if (isFirstLine() || !linesPrevVisible) {\r
-                                       g2d.drawLine(0, ht, stream.contentWidth, ht);\r
-                                       linesPrevVisible = true;\r
-                               }\r
-                               g2d.drawLine(stream.contentWidth, ht, stream.contentWidth, hb);\r
-                               g2d.drawLine(0, hb, stream.contentWidth, hb);\r
-                       }\r
-                       currentLine++;\r
-                       page.currentLine++;\r
-                       page.availableLines--;\r
-                       page.currentPixel += getLineHeight();\r
-               } else {\r
-                       PositionedRow row = _getRow2(line);\r
-                       if (stream.contentHeight-page.currentPixel < row.reservedSpace) {\r
-                               stream.nextPage();\r
-                               page = getCurrentPage();\r
-                               g2d = page.g2d;\r
-                               writeHeader();\r
-                               row = _getRow2(line);\r
-                       }\r
-                       row.render(g2d);\r
-                       currentLine+= row.realLines;\r
-                       page.currentLine+= row.realLines;\r
-                       page.currentPixel += row.reservedSpace;\r
-                       page.estimateAvailableLines();\r
-               }\r
-               \r
-               stream.checkNextPage();\r
-               return new PDFTableRow();\r
-       }\r
-       \r
-       void writeLine(String line) throws Exception{\r
-               writeLine(line, 0);\r
-       }\r
-       \r
-       void writeLine(TextItem line) throws Exception{\r
-               writeLine(line, 0);\r
-       }\r
-       \r
-       private void writeHeader() throws Exception{\r
-               if (headerVisible) {\r
-                       TextSize s = currentTextSize;\r
-                       setTextSize(TextSize.MEDIUM);\r
-                       if (title != null) {\r
-                               boolean b = linesVisible;\r
-                               setLinesVisible(false);\r
-                               writeLine(title);\r
-                               setLinesVisible(b);\r
-                       }\r
-                       _writeRow(columnNames);\r
-                       setTextSize(s);\r
-               }\r
-       }\r
-       \r
-       void writeLine(String line, int x) throws Exception{\r
-               int h = getTextHeight();\r
-               int ht = getTopHeight();\r
-               int hb = getBottomHeight();\r
-               PDFPage page = getCurrentPage();\r
-               Graphics2D g2d = page.g2d;\r
-               g2d.drawString(line, x+textOffsetX, h);\r
-               if (linesVisible) {\r
-                       if (isFirstLine() || !linesPrevVisible) {\r
-                               g2d.drawLine(0, ht, stream.contentWidth, ht);\r
-                               linesPrevVisible = true;\r
-                       }\r
-                       g2d.drawLine(0, ht, 0, hb);\r
-                       g2d.drawLine(stream.contentWidth, ht, stream.contentWidth, hb);\r
-                       g2d.drawLine(0, hb, stream.contentWidth, hb);\r
-               }\r
-               currentLine++;\r
-               page.currentLine++;\r
-               page.availableLines--;\r
-               page.currentPixel += getLineHeight();\r
-               stream.checkNextPage();\r
-       }\r
-       \r
-       void writeLine(TextItem line, int x) throws Exception{\r
-               int h = getTextHeight();\r
-               int ht = getTopHeight();\r
-               int hb = getBottomHeight();\r
-               PDFPage page = getCurrentPage();\r
-               Graphics2D g2d = page.g2d;\r
-               g2d.drawString(line.getText(), x+textOffsetX, h);\r
-               if (linesVisible) {\r
-                       if (isFirstLine() || !linesPrevVisible) {\r
-                               g2d.drawLine(0, ht, stream.contentWidth, ht);\r
-                               linesPrevVisible = true;\r
-                       }\r
-                       g2d.drawLine(0, ht, 0, hb);\r
-                       g2d.drawLine(stream.contentWidth, ht, stream.contentWidth, hb);\r
-                       g2d.drawLine(0, hb, stream.contentWidth, hb);\r
-               }\r
-               if (line instanceof URLItem) {\r
-                       URL url = ((URLItem)line).getURL();\r
-                       if (url != null) {\r
-                               addLink(url, 0,ht,stream.contentWidth,hb-ht);\r
-                       }\r
-               }\r
-               currentLine++;\r
-               page.currentLine++;\r
-               page.availableLines--;\r
-               page.currentPixel += getLineHeight();\r
-               stream.checkNextPage();\r
-       }\r
-       \r
-       int getTopHeight() {\r
-               return getTopHeight(currentLine);\r
-       }\r
-       \r
-       int getTopHeight(int line) {\r
-               return (line-currentLine)*getLineHeight()+getCurrentPage().currentPixel;\r
-       }\r
-       \r
-       int getTextHeight() {\r
-               PDFPage page = getCurrentPage();\r
-               return page.currentPixel+getLineHeight()-page.fm.getDescent()-textOffsetY;\r
-       }\r
-       \r
-       PDFPage getCurrentPage() {\r
-               return stream.getCurrentPage();\r
-       }\r
-       \r
-       int getBottomHeight() {\r
-               return getBottomHeight(currentLine);\r
-       }\r
-       \r
-       protected int getLineHeight() {\r
-               return getCurrentPage().fm.getHeight()+textOffsetY;\r
-       }\r
-       \r
-       private int getBottomHeight(int line) {\r
-               return (line-currentLine+1)*getLineHeight()+getCurrentPage().currentPixel;\r
-       }\r
-       \r
-       public int getAvailableLines() {\r
-               PDFPage page = getCurrentPage();\r
-               int contentHeight = stream.contentHeight;\r
-               int pixelY = page.currentPixel;\r
-               return (int)Math.floor((contentHeight-pixelY)/getLineHeight());\r
-       }\r
-       \r
-       private TextSize currentTextSize = TextSize.SMALL;\r
-       \r
-       @Override\r
-       public void setTextSize(TextSize size) {\r
-               stream.getCurrentPage().setFont(writer.fonts.get(size));\r
-               currentTextSize = size;\r
-       }\r
-       \r
-       @Override\r
-       public TextSize getTextSize() {\r
-               return currentTextSize;\r
-       }\r
-       \r
-       private PositionedRow _getRow(List<String> line) {\r
-               PositionedRow row = new PositionedRow();\r
-               int h = getTextHeight();\r
-               int realLines = 1;\r
-               int reservedSpace = 0;\r
-               List<List<PositionedText>> cells = new ArrayList<List<PositionedText>>(line.size());\r
-               if (line.size() > 1) {\r
-                       for (int i = 0; i < line.size(); i++) {\r
-                               String text = line.get(i);\r
-                               int availableSize = columnSizes.get(i)-textOffsetX;\r
-                               if (text != null && text.length() > 0) {\r
-                                       List<PositionedText> pt = getText(text,  columnPositions.get(i)+textOffsetX, h,availableSize, columns.get(i).getAlignment());\r
-                                       cells.add(pt);\r
-                                       reservedSpace = Math.max(reservedSpace, getResevedSpace(pt)+getLineHeight());\r
-                                       realLines = Math.max(realLines, getLineSpace(pt));\r
-                               } else {\r
-                                       cells.add(Collections.<PositionedText> emptyList());\r
-                                       reservedSpace = Math.max(reservedSpace, getLineHeight());\r
-                               }\r
-                       }\r
-               } else {\r
-                       String text = line.get(0);\r
-                       int availableSize = stream.contentWidth;\r
-                       if (text != null && text.length() > 0) {\r
-                               List<PositionedText> pt = getText(text,  textOffsetX, h,availableSize, columns.get(0).getAlignment());\r
-                               cells.add(pt);\r
-                               reservedSpace = Math.max(reservedSpace, getResevedSpace(pt)+getLineHeight());\r
-                               realLines = Math.max(realLines, getLineSpace(pt));\r
-                       } else {\r
-                               cells.add(Collections.<PositionedText> emptyList());\r
-                               reservedSpace = Math.max(reservedSpace, getLineHeight());\r
-                       }\r
-               }\r
-               row.reservedSpace = reservedSpace;\r
-               row.startLine = currentLine;\r
-               row.realLines = realLines;\r
-               row.cells = cells;\r
-               return row;\r
-       }\r
-       \r
-       private PositionedRow _getRow2(List<TextItem> line) {\r
-               PositionedRow row = new PositionedRow();\r
-               int h = getTextHeight();\r
-               int realLines = 1;\r
-               int reservedSpace = 0;\r
-               row.cells = new ArrayList<List<PositionedText>>(line.size());\r
-               row.urls = new ArrayList<URL>();\r
-               if (line.size() > 1) {\r
-                       for (int i = 0; i < line.size(); i++) {\r
-                               TextItem item =line.get(i); \r
-                               \r
-                               int availableSize = columnSizes.get(i)-textOffsetX;\r
-                               if (item != null && item.getText().length() > 0) {\r
-                                       String text = item.getText();\r
-                                       List<PositionedText> pt = getText(text,  columnPositions.get(i)+textOffsetX, h,availableSize, columns.get(i).getAlignment());\r
-                                       row.cells.add(pt);\r
-                                       reservedSpace = Math.max(reservedSpace, getResevedSpace(pt)+getLineHeight());\r
-                                       realLines = Math.max(realLines, getLineSpace(pt));\r
-                               } else {\r
-                                       row.cells.add(Collections.<PositionedText> emptyList());\r
-                                       reservedSpace = Math.max(reservedSpace, getLineHeight());\r
-                               }\r
-                               if (item instanceof URLItem) {\r
-                                       row.urls.add(((URLItem)item).getURL());\r
-                               } else {\r
-                                       row.urls.add(null);\r
-                               }\r
-                       }\r
-               \r
-               } else {\r
-                       String text = line.get(0).getText();\r
-                       int availableSize = stream.contentWidth;\r
-                       if (text != null && text.length() > 0) {\r
-                               List<PositionedText> pt = getText(text,  textOffsetX, h,availableSize, columns.get(0).getAlignment());\r
-                               row.cells.add(pt);\r
-                               reservedSpace = Math.max(reservedSpace, getResevedSpace(pt)+getLineHeight());\r
-                               realLines = Math.max(realLines, getLineSpace(pt));\r
-                       } else {\r
-                               row.cells.add(Collections.<PositionedText> emptyList());\r
-                               reservedSpace = Math.max(reservedSpace, getLineHeight());\r
-                       }\r
-               }\r
-               row.reservedSpace = reservedSpace;\r
-               row.startLine = currentLine;\r
-               row.realLines = realLines;\r
-               return row;\r
-       }\r
-       \r
-       private int getResevedSpace(List<PositionedText> pt) {\r
-               float sy = pt.get(0).drawPosY;\r
-               float ey = pt.get(pt.size()-1).drawPosY;\r
-               return (int)Math.ceil(ey-sy);\r
-       }\r
-       /**\r
-        * Usually lines of multi-line cells consume less space than the maximum line height (FontMetrics). This method calculates the exact amount of lines required by a cell. \r
-        * @param pt\r
-        * @return\r
-        */\r
-       private int getLineSpace(List<PositionedText> pt) {\r
-               if (pt.size() < 2)\r
-                       return 1;\r
-               \r
-               return (int)(getResevedSpace(pt)/getLineHeight())+1;\r
-       }\r
-       \r
-       private class PositionedRow {\r
-               int startLine;\r
-               int realLines;\r
-               int reservedSpace;\r
-               List<List<PositionedText>> cells;\r
-               List<URL> urls;\r
-               \r
-               void render(Graphics2D g) {\r
-                       int ht = getTopHeight(startLine);\r
-                       int hb = ht +reservedSpace;\r
-                       if (cells.size() > 0) {\r
-                               for (int i = 0; i < cells.size(); i++) {\r
-                                       List<PositionedText> ptl = cells.get(i);\r
-                                       for (PositionedText pt : ptl)\r
-                                               pt.render(g);\r
-                                       if (urls != null) {\r
-                                               URL url = urls.get(i);\r
-                                               if (url != null) {\r
-                                                       addLink(url, columnPositions.get(i),ht,columnSizes.get(i),hb-ht);\r
-                                               }\r
-                                       }\r
-                               }\r
-                       } \r
-                       if (linesVisible) {\r
-                               if (cells.size() > 0) {\r
-                                       for (int i = 0; i < cells.size(); i++) {\r
-                                               g.drawLine(columnPositions.get(i), ht, columnPositions.get(i), hb);\r
-                                       }\r
-                               } else {\r
-                                       g.drawLine(columnPositions.get(0), ht, columnPositions.get(0), hb);\r
-                               }\r
-                               if (isFirstLine() || !linesPrevVisible) {\r
-                                       g.drawLine(0, ht, stream.contentWidth, ht);\r
-                                       linesPrevVisible = true;\r
-                               }\r
-                               g.drawLine(stream.contentWidth, ht, stream.contentWidth, hb);\r
-                               g.drawLine(0, hb, stream.contentWidth, hb);\r
-                               \r
-                       }\r
-               }\r
-       }\r
-       \r
-       private void addLink(URL url, int x, int y, int w, int h) {\r
-               PDFPage page = getCurrentPage();\r
-               float fx = +page.stream.marginLeft + x;\r
-               float fy = -page.stream.marginTop + page.template.getHeight() - y;\r
-               page.template.setAction(new PdfAction(url), fx, fy-h, fx+w, fy);\r
-       }\r
-       \r
-       private List<PositionedText> getText(String text, int x, int y, int cellWidth, Alignment alignment) {\r
-               List<PositionedText> result = new ArrayList<PositionedText>();\r
-               Hashtable<TextAttribute, Object> map = new Hashtable<TextAttribute, Object>();\r
-               PDFPage page = getCurrentPage();\r
-               Font font = page.getFont();\r
-               for (TextAttribute a : font.getAttributes().keySet()) {\r
-                       Object v = font.getAttributes().get(a);\r
-                       if (v != null)\r
-                               map.put(a, v);\r
-               }\r
-//             map.putAll(font.getAttributes());\r
-               map.put(TextAttribute.FOREGROUND, Color.black);\r
-\r
-               AttributedString attributedText = new AttributedString( text, map);\r
-\r
-               AttributedCharacterIterator paragraph = attributedText.getIterator();\r
-               int paragraphStart = paragraph.getBeginIndex();\r
-               int paragraphEnd = paragraph.getEndIndex();\r
-               LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(paragraph, page.frc);\r
-               lineMeasurer.setPosition(paragraphStart);\r
-               \r
-               // Get lines until the entire paragraph has been displayed.\r
-        int next, limit, charat, position = 0;\r
-        int drawPosY = y;\r
-        while ((position = lineMeasurer.getPosition()) < paragraphEnd) {\r
-\r
-            // Find possible line break and set it as a limit to the next layout\r
-            next = lineMeasurer.nextOffset(cellWidth);\r
-            limit = next;\r
-            charat = text.indexOf(System.getProperty("line.separator"),position+1);\r
-            if(charat < next && charat != -1){\r
-               limit = charat;\r
-            }\r
-\r
-            // Retrieve next layout. A cleverer program would also cache\r
-            // these layouts until the component is re-sized.\r
-            TextLayout layout = lineMeasurer.nextLayout(cellWidth, limit, false);\r
-\r
-               // Compute pen x position. If the paragraph is right-to-left we\r
-               // will align the TextLayouts to the right edge of the panel.\r
-               // Note: this won't occur for the English text in this sample.\r
-               // Note: drawPosX is always where the LEFT of the text is placed.\r
-               float drawPosX = 0;\r
-               switch (alignment) {\r
-                       case LEFT: \r
-                               drawPosX = layout.isLeftToRight() ? 0 : cellWidth - layout.getAdvance();\r
-                               break;\r
-                       case CENTER:\r
-                               drawPosX = (cellWidth - layout.getAdvance()) / 2;\r
-                               break;\r
-                       case RIGHT:\r
-                               drawPosX = layout.isLeftToRight() ? cellWidth - layout.getAdvance() : 0;\r
-                               break;\r
-               }\r
-               \r
-               drawPosX += x;\r
-               \r
-               // If text has been forced to vertical, align it to center\r
-//             if(breakWidth < textAreaWidth) {\r
-//                     float centerCorrection = layout.isLeftToRight() ? \r
-//                                     (float) (layout.getAdvance() / 2) : \r
-//                                             -1 * (float) (layout.getAdvance() / 2);\r
-//                     drawPosX = textAreaWidth / 2 - centerCorrection;\r
-//             }\r
-\r
-               // Stop drawing if the text won't fit\r
-//             if (breakHeight < drawPosY + layout.getDescent() + layout.getLeading()) {\r
-//                 break;\r
-//             }\r
-\r
-//             drawPosY += layout.getAscent();\r
-\r
-               // Add TextLayout at (drawPosX, drawPosY).\r
-               result.add(new PositionedText(drawPosX, drawPosY, layout));\r
-\r
-               // Move y-coordinate in preparation for next layout.\r
-               //drawPosY += layout.getDescent() + layout.getLeading();\r
-               drawPosY += layout.getDescent() + layout.getLeading() + layout.getAscent();\r
-//             drawPosY += getLineHeight();\r
-           }\r
-        return result;\r
-       }\r
-       \r
-       class PositionedText {\r
-        float drawPosX;\r
-        float drawPosY;\r
-        TextLayout layout;      \r
-        \r
-        public PositionedText(float drawPosX, float drawPosY, TextLayout layout) {\r
-            this.drawPosX = drawPosX;\r
-            this.drawPosY = drawPosY;\r
-            this.layout = layout;\r
-        }\r
-        \r
-        public void render(Graphics2D g) {\r
-            layout.draw(g, drawPosX, drawPosY);\r
-        }\r
-    }\r
-}\r
+package org.simantics.document.linking.report.pdf;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.font.LineBreakMeasurer;
+import java.awt.font.TextAttribute;
+import java.awt.font.TextLayout;
+import java.net.URL;
+import java.text.AttributedCharacterIterator;
+import java.text.AttributedString;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.List;
+
+import org.simantics.document.linking.report.Document.TextSize;
+import org.simantics.document.linking.report.Table;
+import org.simantics.document.linking.report.TableColumn;
+import org.simantics.document.linking.report.TableColumn.Alignment;
+import org.simantics.document.linking.report.TableRow;
+import org.simantics.document.linking.report.TextItem;
+import org.simantics.document.linking.report.URLItem;
+
+import com.lowagie.text.pdf.PdfAction;
+
+
+public class PDFTable implements Table, PDFElement {
+       PDFDocument writer;
+       PDFPageStream stream;
+       PDFPage startPage;
+       
+       int currentLine = 0;
+       
+       List<TableColumn> columns = new ArrayList<TableColumn>();
+       List<String> columnNames = new ArrayList<String>();
+       List<Integer> columnSizes = new ArrayList<Integer>();
+       List<Integer> columnPositions = new ArrayList<Integer>();
+       
+       TextItem title = null;
+       
+       boolean headerVisible = true;
+       private boolean linesVisible = true;
+       private boolean linesPrevVisible = true;
+       boolean clipText = false;
+       
+       int textOffsetX = 2;
+       int textOffsetY = 2;
+       
+       public PDFTable(PDFDocument writer, PDFPageStream stream) {
+               this.writer = writer;
+               this.stream = stream;
+               this.startPage = stream.getCurrentPage();
+               
+       }
+       
+       public PDFTable(PDFTable table) {
+               this.writer = table.writer;
+               this.stream = table.stream;
+               this.startPage = stream.getCurrentPage();
+               this.columns.addAll(table.columns);
+               this.columnNames.addAll(table.columnNames);
+               
+               updateColumnPositions();
+       }
+       
+       
+       @Override
+       public PDFPage getPage() {
+               return startPage;
+       }
+       
+       /* (non-Javadoc)
+        * @see org.simantics.document.linking.report.Table#addColumn(java.lang.String, double)
+        */
+       @Override
+       public TableColumn addColumn(String name, double width) {
+               TableColumn tc = new TableColumn(name, width);
+               columns.add(tc);
+               columnNames.add(name);
+               
+               updateColumnPositions();
+               return tc;
+       }
+       
+       private void updateColumnPositions() {
+               int pos = 0;
+               columnSizes.clear();
+               columnPositions.clear();
+               for (TableColumn c : columns) {
+                       int size = (int)(c.getWidth()*stream.getContentWidth());
+                       columnSizes.add(size);
+                       columnPositions.add(pos);
+                       pos+=size;
+               }
+       }
+       
+       @Override
+       public List<TableColumn> getColumns() {
+               return columns;
+       }
+       
+       @Override
+       public boolean isLinesVisible() {
+               return linesVisible;
+       }
+       
+       @Override
+       public void setLinesVisible(boolean b) {
+               if (this.linesVisible == b)
+                       return;
+               this.linesPrevVisible = linesVisible;
+               this.linesVisible = b;
+       }
+       
+       @Override
+       public boolean isHeaderVisible() {
+               return headerVisible;
+       }
+       
+       @Override
+       public void setHeaderVisible(boolean b) {
+               this.headerVisible = b;
+       }
+       
+       private boolean isFirstLine() {
+               return currentLine == 0 || stream.getCurrentPage().currentLine == 1;
+       }
+       
+       @Override
+       public void setTitle(String title) {
+               try {
+                       this.title = writer.newItem(TextItem.class);
+                       this.title.setText(title);
+               } catch (Exception e) {
+                       
+               }
+       }
+       
+       @Override
+       public void setTitle(TextItem title){
+               this.title = title;
+       }
+       
+       /* (non-Javadoc)
+        * @see org.simantics.document.linking.report.Table#writeRow(java.lang.String[])
+        */
+       @Override
+       public TableRow writeRow(String... line) throws Exception{
+               List<String> list = new ArrayList<String>(line.length);
+               for (String s : line)
+                       list.add(s);
+               return writeRow(list);
+       }
+       
+       /* (non-Javadoc)
+        * @see org.simantics.document.linking.report.Table#writeRow(java.util.List)
+        */
+       @Override
+       public TableRow writeRow(List<String> line) throws Exception{
+               if (isFirstLine())
+                       writeHeader();
+               return _writeRow(line);
+       }
+       
+       @Override
+       public TableRow writeRowItem(TextItem... line) throws Exception {
+               List<TextItem> list = new ArrayList<TextItem>(line.length);
+               for (TextItem s : line)
+                       list.add(s);
+               return writeRowItem(list);
+       }
+       
+       @Override
+       public TableRow writeRowItem(List<TextItem> line) throws Exception {
+               if (isFirstLine())
+                       writeHeader();
+               return _writeRow2(line);
+       }
+       
+       private TableRow _writeRow(List<String> line) throws Exception {
+               int h = getTextHeight();
+               int ht = getTopHeight();
+               int hb = getBottomHeight();
+               PDFPage page = getCurrentPage();
+               Graphics2D g2d = page.g2d;
+               Shape clip = g2d.getClip();
+               
+               if (clipText) {
+                       for (int i = 0; i < line.size(); i++) {
+                               if (line.get(i) == null)
+                                       continue;
+                               g2d.setClip(columnPositions.get(i),ht-1,columnSizes.get(i),hb-ht+2);
+                               g2d.drawString(line.get(i), columnPositions.get(i)+textOffsetX, h);
+                       }
+                       g2d.setClip(clip);
+                       if (linesVisible) {
+                               for (int i = 0; i < line.size(); i++) {
+                                       g2d.drawLine(columnPositions.get(i), ht, columnPositions.get(i), hb);
+                               }
+                               if (isFirstLine() || !linesPrevVisible) {
+                                       g2d.drawLine(0, ht, stream.contentWidth, ht);
+                                       linesPrevVisible = true;
+                               }
+                               g2d.drawLine(stream.contentWidth, ht, stream.contentWidth, hb);
+                               g2d.drawLine(0, hb, stream.contentWidth, hb);
+                       }
+                       currentLine++;
+                       page.currentLine++;
+                       page.availableLines--;
+                       page.currentPixel += getLineHeight();
+               } else {
+                       PositionedRow row = _getRow(line);
+                       if (stream.contentHeight-page.currentPixel < row.reservedSpace) {
+                               stream.nextPage();
+                               page = getCurrentPage();
+                               g2d = page.g2d;
+                               writeHeader();
+                               row = _getRow(line);
+                       }
+                       row.render(g2d);
+                       currentLine+= row.realLines;
+                       page.currentLine+= row.realLines;
+                       page.currentPixel += row.reservedSpace;
+                       page.estimateAvailableLines();
+               }
+               
+               stream.checkNextPage();
+               return new PDFTableRow();
+       }
+       
+       private TableRow _writeRow2(List<TextItem> line) throws Exception {
+               int h = getTextHeight();
+               int ht = getTopHeight();
+               int hb = getBottomHeight();
+               PDFPage page = getCurrentPage();
+               Graphics2D g2d = page.g2d;
+               Shape clip = g2d.getClip();
+               
+               if (clipText) {
+                       for (int i = 0; i < line.size(); i++) {
+                               TextItem text = line.get(i);
+                               if (text == null)
+                                       continue;
+                               g2d.setClip(columnPositions.get(i),ht,columnSizes.get(i),hb-ht);
+                               g2d.drawString(text.getText(), columnPositions.get(i)+textOffsetX, h);
+                               if (text instanceof URLItem) {
+                                       URL url = ((URLItem)text).getURL();
+                                       if (url != null) {
+                                               addLink(url, columnPositions.get(i),ht,columnSizes.get(i),hb-ht);
+                                       }
+                               }
+                       }
+                       g2d.setClip(clip);
+                       if (linesVisible) {
+                               for (int i = 0; i < line.size(); i++) {
+                                       g2d.drawLine(columnPositions.get(i), ht, columnPositions.get(i), hb);
+                               }
+                               if (isFirstLine() || !linesPrevVisible) {
+                                       g2d.drawLine(0, ht, stream.contentWidth, ht);
+                                       linesPrevVisible = true;
+                               }
+                               g2d.drawLine(stream.contentWidth, ht, stream.contentWidth, hb);
+                               g2d.drawLine(0, hb, stream.contentWidth, hb);
+                       }
+                       currentLine++;
+                       page.currentLine++;
+                       page.availableLines--;
+                       page.currentPixel += getLineHeight();
+               } else {
+                       PositionedRow row = _getRow2(line);
+                       if (stream.contentHeight-page.currentPixel < row.reservedSpace) {
+                               stream.nextPage();
+                               page = getCurrentPage();
+                               g2d = page.g2d;
+                               writeHeader();
+                               row = _getRow2(line);
+                       }
+                       row.render(g2d);
+                       currentLine+= row.realLines;
+                       page.currentLine+= row.realLines;
+                       page.currentPixel += row.reservedSpace;
+                       page.estimateAvailableLines();
+               }
+               
+               stream.checkNextPage();
+               return new PDFTableRow();
+       }
+       
+       void writeLine(String line) throws Exception{
+               writeLine(line, 0);
+       }
+       
+       void writeLine(TextItem line) throws Exception{
+               writeLine(line, 0);
+       }
+       
+       private void writeHeader() throws Exception{
+               if (headerVisible) {
+                       TextSize s = currentTextSize;
+                       setTextSize(TextSize.MEDIUM);
+                       if (title != null) {
+                               boolean b = linesVisible;
+                               setLinesVisible(false);
+                               writeLine(title);
+                               setLinesVisible(b);
+                       }
+                       _writeRow(columnNames);
+                       setTextSize(s);
+               }
+       }
+       
+       void writeLine(String line, int x) throws Exception{
+               int h = getTextHeight();
+               int ht = getTopHeight();
+               int hb = getBottomHeight();
+               PDFPage page = getCurrentPage();
+               Graphics2D g2d = page.g2d;
+               g2d.drawString(line, x+textOffsetX, h);
+               if (linesVisible) {
+                       if (isFirstLine() || !linesPrevVisible) {
+                               g2d.drawLine(0, ht, stream.contentWidth, ht);
+                               linesPrevVisible = true;
+                       }
+                       g2d.drawLine(0, ht, 0, hb);
+                       g2d.drawLine(stream.contentWidth, ht, stream.contentWidth, hb);
+                       g2d.drawLine(0, hb, stream.contentWidth, hb);
+               }
+               currentLine++;
+               page.currentLine++;
+               page.availableLines--;
+               page.currentPixel += getLineHeight();
+               stream.checkNextPage();
+       }
+       
+       void writeLine(TextItem line, int x) throws Exception{
+               int h = getTextHeight();
+               int ht = getTopHeight();
+               int hb = getBottomHeight();
+               PDFPage page = getCurrentPage();
+               Graphics2D g2d = page.g2d;
+               g2d.drawString(line.getText(), x+textOffsetX, h);
+               if (linesVisible) {
+                       if (isFirstLine() || !linesPrevVisible) {
+                               g2d.drawLine(0, ht, stream.contentWidth, ht);
+                               linesPrevVisible = true;
+                       }
+                       g2d.drawLine(0, ht, 0, hb);
+                       g2d.drawLine(stream.contentWidth, ht, stream.contentWidth, hb);
+                       g2d.drawLine(0, hb, stream.contentWidth, hb);
+               }
+               if (line instanceof URLItem) {
+                       URL url = ((URLItem)line).getURL();
+                       if (url != null) {
+                               addLink(url, 0,ht,stream.contentWidth,hb-ht);
+                       }
+               }
+               currentLine++;
+               page.currentLine++;
+               page.availableLines--;
+               page.currentPixel += getLineHeight();
+               stream.checkNextPage();
+       }
+       
+       int getTopHeight() {
+               return getTopHeight(currentLine);
+       }
+       
+       int getTopHeight(int line) {
+               return (line-currentLine)*getLineHeight()+getCurrentPage().currentPixel;
+       }
+       
+       int getTextHeight() {
+               PDFPage page = getCurrentPage();
+               return page.currentPixel+getLineHeight()-page.fm.getDescent()-textOffsetY;
+       }
+       
+       PDFPage getCurrentPage() {
+               return stream.getCurrentPage();
+       }
+       
+       int getBottomHeight() {
+               return getBottomHeight(currentLine);
+       }
+       
+       protected int getLineHeight() {
+               return getCurrentPage().fm.getHeight()+textOffsetY;
+       }
+       
+       private int getBottomHeight(int line) {
+               return (line-currentLine+1)*getLineHeight()+getCurrentPage().currentPixel;
+       }
+       
+       public int getAvailableLines() {
+               PDFPage page = getCurrentPage();
+               int contentHeight = stream.contentHeight;
+               int pixelY = page.currentPixel;
+               return (int)Math.floor((contentHeight-pixelY)/getLineHeight());
+       }
+       
+       private TextSize currentTextSize = TextSize.SMALL;
+       
+       @Override
+       public void setTextSize(TextSize size) {
+               stream.getCurrentPage().setFont(writer.fonts.get(size));
+               currentTextSize = size;
+       }
+       
+       @Override
+       public TextSize getTextSize() {
+               return currentTextSize;
+       }
+       
+       private PositionedRow _getRow(List<String> line) {
+               PositionedRow row = new PositionedRow();
+               int h = getTextHeight();
+               int realLines = 1;
+               int reservedSpace = 0;
+               List<List<PositionedText>> cells = new ArrayList<List<PositionedText>>(line.size());
+               if (line.size() > 1) {
+                       for (int i = 0; i < line.size(); i++) {
+                               String text = line.get(i);
+                               int availableSize = columnSizes.get(i)-textOffsetX;
+                               if (text != null && text.length() > 0) {
+                                       List<PositionedText> pt = getText(text,  columnPositions.get(i)+textOffsetX, h,availableSize, columns.get(i).getAlignment());
+                                       cells.add(pt);
+                                       reservedSpace = Math.max(reservedSpace, getResevedSpace(pt)+getLineHeight());
+                                       realLines = Math.max(realLines, getLineSpace(pt));
+                               } else {
+                                       cells.add(Collections.<PositionedText> emptyList());
+                                       reservedSpace = Math.max(reservedSpace, getLineHeight());
+                               }
+                       }
+               } else {
+                       String text = line.get(0);
+                       int availableSize = stream.contentWidth;
+                       if (text != null && text.length() > 0) {
+                               List<PositionedText> pt = getText(text,  textOffsetX, h,availableSize, columns.get(0).getAlignment());
+                               cells.add(pt);
+                               reservedSpace = Math.max(reservedSpace, getResevedSpace(pt)+getLineHeight());
+                               realLines = Math.max(realLines, getLineSpace(pt));
+                       } else {
+                               cells.add(Collections.<PositionedText> emptyList());
+                               reservedSpace = Math.max(reservedSpace, getLineHeight());
+                       }
+               }
+               row.reservedSpace = reservedSpace;
+               row.startLine = currentLine;
+               row.realLines = realLines;
+               row.cells = cells;
+               return row;
+       }
+       
+       private PositionedRow _getRow2(List<TextItem> line) {
+               PositionedRow row = new PositionedRow();
+               int h = getTextHeight();
+               int realLines = 1;
+               int reservedSpace = 0;
+               row.cells = new ArrayList<List<PositionedText>>(line.size());
+               row.urls = new ArrayList<URL>();
+               if (line.size() > 1) {
+                       for (int i = 0; i < line.size(); i++) {
+                               TextItem item =line.get(i); 
+                               
+                               int availableSize = columnSizes.get(i)-textOffsetX;
+                               if (item != null && item.getText().length() > 0) {
+                                       String text = item.getText();
+                                       List<PositionedText> pt = getText(text,  columnPositions.get(i)+textOffsetX, h,availableSize, columns.get(i).getAlignment());
+                                       row.cells.add(pt);
+                                       reservedSpace = Math.max(reservedSpace, getResevedSpace(pt)+getLineHeight());
+                                       realLines = Math.max(realLines, getLineSpace(pt));
+                               } else {
+                                       row.cells.add(Collections.<PositionedText> emptyList());
+                                       reservedSpace = Math.max(reservedSpace, getLineHeight());
+                               }
+                               if (item instanceof URLItem) {
+                                       row.urls.add(((URLItem)item).getURL());
+                               } else {
+                                       row.urls.add(null);
+                               }
+                       }
+               
+               } else {
+                       String text = line.get(0).getText();
+                       int availableSize = stream.contentWidth;
+                       if (text != null && text.length() > 0) {
+                               List<PositionedText> pt = getText(text,  textOffsetX, h,availableSize, columns.get(0).getAlignment());
+                               row.cells.add(pt);
+                               reservedSpace = Math.max(reservedSpace, getResevedSpace(pt)+getLineHeight());
+                               realLines = Math.max(realLines, getLineSpace(pt));
+                       } else {
+                               row.cells.add(Collections.<PositionedText> emptyList());
+                               reservedSpace = Math.max(reservedSpace, getLineHeight());
+                       }
+               }
+               row.reservedSpace = reservedSpace;
+               row.startLine = currentLine;
+               row.realLines = realLines;
+               return row;
+       }
+       
+       private int getResevedSpace(List<PositionedText> pt) {
+               float sy = pt.get(0).drawPosY;
+               float ey = pt.get(pt.size()-1).drawPosY;
+               return (int)Math.ceil(ey-sy);
+       }
+       /**
+        * Usually lines of multi-line cells consume less space than the maximum line height (FontMetrics). This method calculates the exact amount of lines required by a cell. 
+        * @param pt
+        * @return
+        */
+       private int getLineSpace(List<PositionedText> pt) {
+               if (pt.size() < 2)
+                       return 1;
+               
+               return (int)(getResevedSpace(pt)/getLineHeight())+1;
+       }
+       
+       private class PositionedRow {
+               int startLine;
+               int realLines;
+               int reservedSpace;
+               List<List<PositionedText>> cells;
+               List<URL> urls;
+               
+               void render(Graphics2D g) {
+                       int ht = getTopHeight(startLine);
+                       int hb = ht +reservedSpace;
+                       if (cells.size() > 0) {
+                               for (int i = 0; i < cells.size(); i++) {
+                                       List<PositionedText> ptl = cells.get(i);
+                                       for (PositionedText pt : ptl)
+                                               pt.render(g);
+                                       if (urls != null) {
+                                               URL url = urls.get(i);
+                                               if (url != null) {
+                                                       addLink(url, columnPositions.get(i),ht,columnSizes.get(i),hb-ht);
+                                               }
+                                       }
+                               }
+                       } 
+                       if (linesVisible) {
+                               if (cells.size() > 0) {
+                                       for (int i = 0; i < cells.size(); i++) {
+                                               g.drawLine(columnPositions.get(i), ht, columnPositions.get(i), hb);
+                                       }
+                               } else {
+                                       g.drawLine(columnPositions.get(0), ht, columnPositions.get(0), hb);
+                               }
+                               if (isFirstLine() || !linesPrevVisible) {
+                                       g.drawLine(0, ht, stream.contentWidth, ht);
+                                       linesPrevVisible = true;
+                               }
+                               g.drawLine(stream.contentWidth, ht, stream.contentWidth, hb);
+                               g.drawLine(0, hb, stream.contentWidth, hb);
+                               
+                       }
+               }
+       }
+       
+       private void addLink(URL url, int x, int y, int w, int h) {
+               PDFPage page = getCurrentPage();
+               float fx = +page.stream.marginLeft + x;
+               float fy = -page.stream.marginTop + page.template.getHeight() - y;
+               page.template.setAction(new PdfAction(url), fx, fy-h, fx+w, fy);
+       }
+       
+       private List<PositionedText> getText(String text, int x, int y, int cellWidth, Alignment alignment) {
+               List<PositionedText> result = new ArrayList<PositionedText>();
+               Hashtable<TextAttribute, Object> map = new Hashtable<TextAttribute, Object>();
+               PDFPage page = getCurrentPage();
+               Font font = page.getFont();
+               for (TextAttribute a : font.getAttributes().keySet()) {
+                       Object v = font.getAttributes().get(a);
+                       if (v != null)
+                               map.put(a, v);
+               }
+//             map.putAll(font.getAttributes());
+               map.put(TextAttribute.FOREGROUND, Color.black);
+
+               AttributedString attributedText = new AttributedString( text, map);
+
+               AttributedCharacterIterator paragraph = attributedText.getIterator();
+               int paragraphStart = paragraph.getBeginIndex();
+               int paragraphEnd = paragraph.getEndIndex();
+               LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(paragraph, page.frc);
+               lineMeasurer.setPosition(paragraphStart);
+               
+               // Get lines until the entire paragraph has been displayed.
+        int next, limit, charat, position = 0;
+        int drawPosY = y;
+        while ((position = lineMeasurer.getPosition()) < paragraphEnd) {
+
+            // Find possible line break and set it as a limit to the next layout
+            next = lineMeasurer.nextOffset(cellWidth);
+            limit = next;
+            charat = text.indexOf(System.getProperty("line.separator"),position+1);
+            if(charat < next && charat != -1){
+               limit = charat;
+            }
+
+            // Retrieve next layout. A cleverer program would also cache
+            // these layouts until the component is re-sized.
+            TextLayout layout = lineMeasurer.nextLayout(cellWidth, limit, false);
+
+               // Compute pen x position. If the paragraph is right-to-left we
+               // will align the TextLayouts to the right edge of the panel.
+               // Note: this won't occur for the English text in this sample.
+               // Note: drawPosX is always where the LEFT of the text is placed.
+               float drawPosX = 0;
+               switch (alignment) {
+                       case LEFT: 
+                               drawPosX = layout.isLeftToRight() ? 0 : cellWidth - layout.getAdvance();
+                               break;
+                       case CENTER:
+                               drawPosX = (cellWidth - layout.getAdvance()) / 2;
+                               break;
+                       case RIGHT:
+                               drawPosX = layout.isLeftToRight() ? cellWidth - layout.getAdvance() : 0;
+                               break;
+               }
+               
+               drawPosX += x;
+               
+               // If text has been forced to vertical, align it to center
+//             if(breakWidth < textAreaWidth) {
+//                     float centerCorrection = layout.isLeftToRight() ? 
+//                                     (float) (layout.getAdvance() / 2) : 
+//                                             -1 * (float) (layout.getAdvance() / 2);
+//                     drawPosX = textAreaWidth / 2 - centerCorrection;
+//             }
+
+               // Stop drawing if the text won't fit
+//             if (breakHeight < drawPosY + layout.getDescent() + layout.getLeading()) {
+//                 break;
+//             }
+
+//             drawPosY += layout.getAscent();
+
+               // Add TextLayout at (drawPosX, drawPosY).
+               result.add(new PositionedText(drawPosX, drawPosY, layout));
+
+               // Move y-coordinate in preparation for next layout.
+               //drawPosY += layout.getDescent() + layout.getLeading();
+               drawPosY += layout.getDescent() + layout.getLeading() + layout.getAscent();
+//             drawPosY += getLineHeight();
+           }
+        return result;
+       }
+       
+       class PositionedText {
+        float drawPosX;
+        float drawPosY;
+        TextLayout layout;      
+        
+        public PositionedText(float drawPosX, float drawPosY, TextLayout layout) {
+            this.drawPosX = drawPosX;
+            this.drawPosY = drawPosY;
+            this.layout = layout;
+        }
+        
+        public void render(Graphics2D g) {
+            layout.draw(g, drawPosX, drawPosY);
+        }
+    }
+}