X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;ds=sidebyside;f=bundles%2Forg.simantics.document.linking.ui%2Fsrc%2Forg%2Fsimantics%2Fdocument%2Flinking%2Freport%2Fpdf%2FPDFTable.java;h=902d27159d219ac79c098cbe868b9605d4821b8a;hb=refs%2Fchanges%2F55%2F2055%2F1;hp=8f9dafadf3c8587a3fdc1f7f897134d518ce919d;hpb=969bd23cab98a79ca9101af33334000879fb60c5;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.document.linking.ui/src/org/simantics/document/linking/report/pdf/PDFTable.java b/bundles/org.simantics.document.linking.ui/src/org/simantics/document/linking/report/pdf/PDFTable.java index 8f9dafadf..902d27159 100644 --- a/bundles/org.simantics.document.linking.ui/src/org/simantics/document/linking/report/pdf/PDFTable.java +++ b/bundles/org.simantics.document.linking.ui/src/org/simantics/document/linking/report/pdf/PDFTable.java @@ -1,667 +1,667 @@ -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 columns = new ArrayList(); - List columnNames = new ArrayList(); - List columnSizes = new ArrayList(); - List columnPositions = new ArrayList(); - - 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 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 list = new ArrayList(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 line) throws Exception{ - if (isFirstLine()) - writeHeader(); - return _writeRow(line); - } - - @Override - public TableRow writeRowItem(TextItem... line) throws Exception { - List list = new ArrayList(line.length); - for (TextItem s : line) - list.add(s); - return writeRowItem(list); - } - - @Override - public TableRow writeRowItem(List line) throws Exception { - if (isFirstLine()) - writeHeader(); - return _writeRow2(line); - } - - private TableRow _writeRow(List 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 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 line) { - PositionedRow row = new PositionedRow(); - int h = getTextHeight(); - int realLines = 1; - int reservedSpace = 0; - List> cells = new ArrayList>(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 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. emptyList()); - reservedSpace = Math.max(reservedSpace, getLineHeight()); - } - } - } else { - String text = line.get(0); - int availableSize = stream.contentWidth; - if (text != null && text.length() > 0) { - List 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. emptyList()); - reservedSpace = Math.max(reservedSpace, getLineHeight()); - } - } - row.reservedSpace = reservedSpace; - row.startLine = currentLine; - row.realLines = realLines; - row.cells = cells; - return row; - } - - private PositionedRow _getRow2(List line) { - PositionedRow row = new PositionedRow(); - int h = getTextHeight(); - int realLines = 1; - int reservedSpace = 0; - row.cells = new ArrayList>(line.size()); - row.urls = new ArrayList(); - 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 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. 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 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. emptyList()); - reservedSpace = Math.max(reservedSpace, getLineHeight()); - } - } - row.reservedSpace = reservedSpace; - row.startLine = currentLine; - row.realLines = realLines; - return row; - } - - private int getResevedSpace(List 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 pt) { - if (pt.size() < 2) - return 1; - - return (int)(getResevedSpace(pt)/getLineHeight())+1; - } - - private class PositionedRow { - int startLine; - int realLines; - int reservedSpace; - List> cells; - List 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 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 getText(String text, int x, int y, int cellWidth, Alignment alignment) { - List result = new ArrayList(); - Hashtable map = new Hashtable(); - 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); - } - } -} +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 columns = new ArrayList(); + List columnNames = new ArrayList(); + List columnSizes = new ArrayList(); + List columnPositions = new ArrayList(); + + 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 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 list = new ArrayList(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 line) throws Exception{ + if (isFirstLine()) + writeHeader(); + return _writeRow(line); + } + + @Override + public TableRow writeRowItem(TextItem... line) throws Exception { + List list = new ArrayList(line.length); + for (TextItem s : line) + list.add(s); + return writeRowItem(list); + } + + @Override + public TableRow writeRowItem(List line) throws Exception { + if (isFirstLine()) + writeHeader(); + return _writeRow2(line); + } + + private TableRow _writeRow(List 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 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 line) { + PositionedRow row = new PositionedRow(); + int h = getTextHeight(); + int realLines = 1; + int reservedSpace = 0; + List> cells = new ArrayList>(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 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. emptyList()); + reservedSpace = Math.max(reservedSpace, getLineHeight()); + } + } + } else { + String text = line.get(0); + int availableSize = stream.contentWidth; + if (text != null && text.length() > 0) { + List 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. emptyList()); + reservedSpace = Math.max(reservedSpace, getLineHeight()); + } + } + row.reservedSpace = reservedSpace; + row.startLine = currentLine; + row.realLines = realLines; + row.cells = cells; + return row; + } + + private PositionedRow _getRow2(List line) { + PositionedRow row = new PositionedRow(); + int h = getTextHeight(); + int realLines = 1; + int reservedSpace = 0; + row.cells = new ArrayList>(line.size()); + row.urls = new ArrayList(); + 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 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. 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 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. emptyList()); + reservedSpace = Math.max(reservedSpace, getLineHeight()); + } + } + row.reservedSpace = reservedSpace; + row.startLine = currentLine; + row.realLines = realLines; + return row; + } + + private int getResevedSpace(List 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 pt) { + if (pt.size() < 2) + return 1; + + return (int)(getResevedSpace(pt)/getLineHeight())+1; + } + + private class PositionedRow { + int startLine; + int realLines; + int reservedSpace; + List> cells; + List 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 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 getText(String text, int x, int y, int cellWidth, Alignment alignment) { + List result = new ArrayList(); + Hashtable map = new Hashtable(); + 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); + } + } +}