/******************************************************************************
* Product: Adempiere ERP & CRM Smart Business Solution *
* Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved. *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
* For the text or an alternative of this public license, you may reach us *
* ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA *
* or via info@compiere.org or http://www.compiere.org/license.html *
*****************************************************************************/
package org.compiere.print.layout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.io.Serializable;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.regex.Pattern;
import org.compiere.model.MQuery;
import org.compiere.print.MPrintFormatItem;
import org.compiere.print.MPrintTableFormat;
import org.compiere.print.PrintData;
import org.compiere.print.util.SerializableMatrix;
import org.compiere.print.util.SerializableMatrixImpl;
import org.compiere.report.MReportLine;
import org.compiere.util.Evaluator;
import org.compiere.util.KeyNamePair;
import org.compiere.util.NamePair;
import org.compiere.util.Util;
import org.compiere.util.ValueNamePair;
/**
* Table Print Element.
* Maintains a logical cross page table, which is "broken up" when printing.
*
* The table is 3 pages wide, 2 pages high * +-----+-----+-----+ * | 1.1 | 1.2 | 1.3 | * +-----+-----+-----+ * | 2.1 | 2.2 | 2.3 | * +-----+-----+-----+ * Printed * +-----+-----+-----+ * | 1 | 2 | 3 | * +-----+-----+-----+ * | 4 | 5 | 6 | * +-----+-----+-----+ ** @author Jorg Janke * @version $Id: TableElement.java,v 1.2 2006/07/30 00:53:02 jjanke Exp $ * * @author Teo Sarca, SC ARHIPAC SERVICE SRL *
* The rowCol.. maps are organized as follows - Point (row,col)
* row - data if 0..m - if -1 for the entire column
* column - data if 0..n - if -1 for the entire row
* i.e. Point (-1, -1) is the default for the table
*
* @param columnHeader array with column headers (Key=ColumnName)
* @param columnMaxWidth array with column max width - 0=no restrictions - negative=suppress if null
* @param columnMaxHeight array with row max height for a column - 0=no restrictions; -1=one row only
* @param columnJustification field justification for column
*
* @param fixedWidth array with column fixed width
* @param functionRows list of function rows
* @param multiLineHeader if true, the header is not truncated at maxWidth
*
* @param data 2D array with data to be printed [row][col]
* @param pk array of primary keys
* @param pkColumnName primary key name
*
* @param pageNoStart page number of starting page
* @param firstPage bounds on first page
* @param nextPages bounds on following pages
* @param repeatedColumns repeat first x columns on - X Axis follow pages
* @param additionalLines map of old column to below printed column
*
* @param rowColFont HashMap with Point as key with Font overwrite
* @param rowColColor HashMap with Point as key with foreground Color overwrite
* @param rowColBackground HashMap with Point as key with background Color overwrite
* @param tFormat table format
* @param pageBreak Arraylist of rows with page break
*
* @param colSuppressRepeats
* @param rowColReportLine
* @param finReportSumRows
*/
public TableElement (ValueNamePair[] columnHeader,
int[] columnMaxWidth, int[] columnMaxHeight, String[] columnJustification,
boolean[] fixedWidth, ArrayList
* Examples:
* From general to specific:
* (-1,-1) => for entire table
* (-1, c) => for entire column c
* (r, -1) => for entire row r (overwrites column)
* (r, c) => for specific cell (highest priority)
* Header is row -2
* (-2,-1) => for all header columns
* (-2, c) => for header column c
*
* @param row row
* @param col column
* @return Font for row/col
*/
private Font getFont (int row, int col)
{
// First specific position
Font font = (Font)m_rowColFont.get(new Point(row, col));
if (font != null)
return font;
// Row Next
font = (Font)m_rowColFont.get(new Point (row, ALL));
if (font != null)
return font;
// Column then
font = (Font)m_rowColFont.get(new Point (ALL, col));
if (font != null)
return font;
// default
return m_baseFont;
} // getFont
/**
* Get Foreground Color.
* @param row row
* @param col column
* @return Foreground Color for row/col
*/
private Color getColor (int row, int col)
{
// First specific position
Color color = (Color)m_rowColColor.get(new Point(row, col));
if (color != null)
return color;
// Row Next
color = (Color)m_rowColColor.get(new Point (row, ALL));
if (color != null)
return color;
// Column then
color = (Color)m_rowColColor.get(new Point (ALL, col));
if (color != null)
return color;
// default
return m_baseColor;
} // getFont
/**
* Get Background Color.
* @param row row
* @param col column
* @return Background Color for row/col
*/
private Color getBackground (int row, int col)
{
// First specific position
Color color = (Color)m_rowColBackground.get(new Point(row, col));
if (color != null)
return color;
// Row Next
color = (Color)m_rowColBackground.get(new Point (row, ALL));
if (color != null)
return color;
// Column then
color = (Color)m_rowColBackground.get(new Point (ALL, col));
if (color != null)
return color;
// default
return m_baseBackground;
} // getFont
/**
* Get Calculated Height on page
* @param pageNo page number
* @return Height
*/
public float getHeight (int pageNo)
{
int pageIndex = getPageIndex(pageNo);
int pageYindex = getPageYIndex(pageIndex);
if (log.isLoggable(Level.FINE)) log.fine("Page=" + pageNo + " - PageIndex=" + pageIndex
+ ", PageYindex=" + pageYindex);
float pageHeight = ((Float)m_pageHeight.get(pageYindex)).floatValue();
float pageHeightPrevious = 0f;
if (pageYindex > 0)
pageHeightPrevious = ((Float)m_pageHeight.get(pageYindex-1)).floatValue();
float retValue = pageHeight - pageHeightPrevious;
if (log.isLoggable(Level.FINE)) log.fine("Page=" + pageNo + " - PageIndex=" + pageIndex + ", PageYindex=" + pageYindex + ", Height=" + String.valueOf(retValue));
return retValue;
} // getHeight
/**
* Get Calculated Width on page
* @param pageNo page number
* @return Width
*/
public float getWidth (int pageNo)
{
int pageIndex = getPageIndex(pageNo);
if (pageIndex == 0)
return m_firstPage.width;
return m_nextPages.width;
} // getHeight
/**
* Get number of "real" pages.
* @return page count
*/
public int getPageCount()
{
return m_firstRowOnPage.size() * m_firstColumnOnPage.size();
} // getPageCount
/**
* Get zero based Page Index within Layout
* @param pageNo real page no
* @return page index
*/
protected int getPageIndex (int pageNo)
{
int index = pageNo - m_pageNoStart;
if (index < 0)
log.log(Level.SEVERE, "index=" + index, new Exception());
return index;
} // getPageIndex
/**
* Get X - Page Index.
* The table is 3 pages wide, 2 pages high - index
* +-----+-----+-----+
* | 0.0 | 0.1 | 0.2 |
* +-----+-----+-----+
* | 1.0 | 1.1 | 1.2 |
* +-----+-----+-----+
* Page Index
* +-----+-----+-----+
* | 0 | 1 | 2 |
* +-----+-----+-----+
* | 3 | 4 | 5 |
* +-----+-----+-----+
*
* @param pageIndex zero based page index
* @return page index on X axis
*/
protected int getPageXIndex (int pageIndex)
{
int noXpages = m_firstColumnOnPage.size();
int x = pageIndex % noXpages;
return x;
} // getPageXIndex
/**
* Get X - Page Count
* @return X page count
*/
protected int getPageXCount ()
{
return m_firstColumnOnPage.size();
} // getPageXCount
/**
* Get Y | Page Index.
* The table is 3 pages wide, 2 pages high - index
* +-----+-----+-----+
* | 0.0 | 0.1 | 0.2 |
* +-----+-----+-----+
* | 1.0 | 1.1 | 1.2 |
* +-----+-----+-----+
* Page Index
* +-----+-----+-----+
* | 0 | 1 | 2 |
* +-----+-----+-----+
* | 3 | 4 | 5 |
* +-----+-----+-----+
*
* @param pageIndex zero based page index
* @return page index on Y axis
*/
protected int getPageYIndex (int pageIndex)
{
int noXpages = m_firstColumnOnPage.size();
int y = (pageIndex - (pageIndex % noXpages)) / noXpages;
return y;
} // getPageYIndex
/**
* Get Y | Page Count
* @return Y page count
*/
protected int getPageYCount ()
{
return m_firstRowOnPage.size();
} // getPageYCount
/**
* Get Drill Down Query
* @param relativePoint point to find print element
* @param pageNo page number
* @return drill down query of print element or null
*/
@Override
public MQuery getDrillDown (Point relativePoint, int pageNo)
{
if (m_rowColDrillDown.size() == 0)
return null;
if (!getBounds(pageNo).contains(relativePoint))
return null;
int row = getRow (relativePoint.y, pageNo);
if (row == -1)
return null;
int col = getCol (relativePoint.x, pageNo);
if (col == -1)
return null;
if (log.isLoggable(Level.FINE)) log.fine("Row=" + row + ", Col=" + col + ", PageNo=" + pageNo);
//
NamePair pp = (NamePair)m_rowColDrillDown.get(new Point(row,col));
if (pp == null)
return null;
String columnName = MQuery.getZoomColumnName(m_columnHeader[col].getID());
String tableName = MQuery.getZoomTableName(columnName);
Object code = pp.getID();
if (pp instanceof KeyNamePair)
code = Integer.valueOf(((KeyNamePair)pp).getKey());
//
MQuery query = new MQuery(tableName);
query.addRestriction(columnName, MQuery.EQUAL, code, null, pp.toString());
return query;
} // getDrillDown
/**
* Get Drill Across Query
* @param relativePoint point to find print element
* @param pageNo page number
* @return drill across query of print element or null
*/
@Override
public MQuery getDrillAcross (Point relativePoint, int pageNo)
{
if (!getBounds(pageNo).contains(relativePoint))
return null;
int row = getRow (relativePoint.y, pageNo);
if (row == -1)
return null;
if (log.isLoggable(Level.FINE)) log.fine("Row=" + row + ", PageNo=" + pageNo);
//
if (m_pk[row] == null) // FunctionRows
return null;
return MQuery.getEqualQuery(m_pkColumnName, m_pk[row].getKey());
} // getDrillAcross
/**
* Get relative Bounds of Element.
* (entire page, not just used portion)
* @param pageNo pageNo
* @return bounds
*/
public Rectangle getBounds(int pageNo)
{
int pageIndex = getPageIndex(pageNo);
int pageYindex = getPageYIndex(pageIndex);
if (pageYindex == 0)
return m_firstPage;
else
return m_nextPages;
} // getBounds
/**
* Get Row for yPos
* @param yPos y position (page relative)
* @param pageNo page number
* @return row index or -1
*/
private int getRow (int yPos, int pageNo)
{
int pageIndex = getPageIndex(pageNo);
int pageYindex = getPageYIndex(pageIndex);
//
int curY = (pageYindex == 0 ? m_firstPage.y : m_nextPages.y) + m_headerHeight;
if (yPos < curY)
return -1; // above
//
int firstRow = ((Integer)m_firstRowOnPage.get(pageYindex)).intValue();
int nextPageRow = m_data.getRowCount(); // no of rows
if (pageYindex+1 < m_firstRowOnPage.size())
nextPageRow = ((Integer)m_firstRowOnPage.get(pageYindex+1)).intValue();
//
for (int row = firstRow; row < nextPageRow; row++)
{
int rowHeight = ((Float)m_rowHeights.get(row)).intValue(); // includes 2*Gaps+Line
if (yPos >= curY && yPos < (curY + rowHeight))
return row;
curY += rowHeight;
}
// below
return -1;
} // getRow
/**
* Get Column for xPos
* @param xPos x position (page relative)
* @param pageNo page number
* @return column index or -1
*/
private int getCol (int xPos, int pageNo)
{
int pageIndex = getPageIndex(pageNo);
int pageXindex = getPageXIndex(pageIndex);
//
int curX = pageXindex == 0 ? m_firstPage.x : m_nextPages.x;
if (xPos < curX)
return -1; // too left
int firstColumn = ((Integer)m_firstColumnOnPage.get(pageXindex)).intValue();
int nextPageColumn = m_columnHeader.length; // no of cols
if (pageXindex+1 < m_firstColumnOnPage.size())
nextPageColumn = ((Integer)m_firstColumnOnPage.get(pageXindex+1)).intValue();
// fixed volumns
int regularColumnStart = firstColumn;
for (int col = 0; col < m_repeatedColumns; col++)
{
int colWidth = ((Float)m_columnWidths.get(col)).intValue(); // includes 2*Gaps+Line
if (xPos >= curX && xPos < (curX + colWidth))
return col;
curX += colWidth;
if (regularColumnStart == col)
regularColumnStart++;
}
// regular columns
for (int col = regularColumnStart; col < nextPageColumn; col++)
{
int colWidth = ((Float)m_columnWidths.get(col)).intValue(); // includes 2*Gaps+Line
if (xPos >= curX && xPos < (curX + colWidth))
return col;
curX += colWidth;
} // for all columns
// too right
return -1;
} // getCol
/**
* Paint/Print.
*
* @param g2D Graphics
* @param pageNo page number for multi page support (0 = header/footer)
* @param pageStart top left Location of page
* @param ctx context
* @param isView true if online view (IDs are links)
*/
@Override
public void paint (Graphics2D g2D, int pageNo, Point2D pageStart, Properties ctx, boolean isView)
{
int pageIndex = getPageIndex(pageNo);
int pageXindex = getPageXIndex(pageIndex);
int pageYindex = getPageYIndex(pageIndex);
if (DEBUG_PRINT)
if (log.isLoggable(Level.CONFIG)) log.config("Page=" + pageNo + " [x=" + pageXindex + ", y=" + pageYindex + "]");
//
int firstColumn = ((Integer)m_firstColumnOnPage.get(pageXindex)).intValue();
int nextPageColumn = m_columnHeader.length; // no of cols
if (pageXindex+1 < m_firstColumnOnPage.size())
nextPageColumn = ((Integer)m_firstColumnOnPage.get(pageXindex+1)).intValue();
//
if (pageYindex >= m_firstRowOnPage.size()) {
pageYindex = m_firstRowOnPage.size() - 1;
}
if (pageYindex<0)
return;
int firstRow = ((Integer)m_firstRowOnPage.get(pageYindex)).intValue();
int nextPageRow = m_data.getRowCount(); // no of rows
if (pageYindex+1 < m_firstRowOnPage.size())
nextPageRow = ((Integer)m_firstRowOnPage.get(pageYindex+1)).intValue();
if (DEBUG_PRINT)
if (log.isLoggable(Level.FINEST)) log.finest("Col=" + firstColumn + "-" + (nextPageColumn-1)
+ ", Row=" + firstRow + "-" + (nextPageRow-1));
// Top Left
int startX = (int)pageStart.getX();
int startY = (int)pageStart.getY();
// Table Start
startX += pageIndex == 0 ? m_firstPage.x : m_nextPages.x;
startY += pageIndex == 0 ? m_firstPage.y : m_nextPages.y;
if (DEBUG_PRINT)
if (log.isLoggable(Level.FINEST)) log.finest("PageStart=" + pageStart + ", StartTable x=" + startX + ", y=" + startY);
// paint first fixed volumns
boolean firstColumnPrint = true;
int regularColumnStart = firstColumn;
for (int col = 0; col < m_repeatedColumns && col < m_columnWidths.size(); col++)
{
int colWidth = ((Float)m_columnWidths.get(col)).intValue(); // includes 2*Gaps+Line
if (colWidth != 0)
{
printColumn (g2D, col, startX, startY, firstColumnPrint, firstRow, nextPageRow, isView);
startX += colWidth;
firstColumnPrint = false;
}
if (regularColumnStart == col)
regularColumnStart++;
}
// paint columns
for (int col = regularColumnStart; col < nextPageColumn; col++)
{
int colWidth = ((Float)m_columnWidths.get(col)).intValue(); // includes 2*Gaps+Line
if (colWidth != 0)
{
printColumn (g2D, col, startX, startY, firstColumnPrint, firstRow, nextPageRow, isView);
startX += colWidth;
firstColumnPrint = false;
}
} // for all columns
} // paint
/**
* Print non zero width Column
* @param g2D graphics
* @param col column index
* @param origX start X
* @param origY start Y
* @param leftVline if true print left vertical line (for first column)
* @param firstRow first row index
* @param nextPageRow row index of next page
* @param isView true if online view (IDs are links)
*/
private void printColumn (Graphics2D g2D, int col,
final int origX, final int origY, boolean leftVline,
final int firstRow, final int nextPageRow, boolean isView)
{
int curX = origX;
int curY = origY; // start from top
//
float colWidth = ((Float)m_columnWidths.get(col)).floatValue(); // includes 2*Gaps+Line
float netWidth = colWidth - (2*H_GAP) - m_tFormat.getVLineStroke().floatValue();
if (leftVline)
netWidth -= m_tFormat.getVLineStroke().floatValue();
float rowHeight = m_headerHeight;
float netHeight = rowHeight - (4*m_tFormat.getLineStroke().floatValue()) + (2*V_GAP);
if (DEBUG_PRINT)
if (log.isLoggable(Level.FINER)) log.finer("#" + col + " - x=" + curX + ", y=" + curY
+ ", width=" + colWidth + "/" + netWidth + ", HeaderHeight=" + rowHeight + "/" + netHeight);
String alignment = m_columnJustification[col];
// paint header ***************************************************
if (leftVline) // draw left | line
{
g2D.setPaint(m_tFormat.getVLine_Color());
g2D.setStroke(m_tFormat.getVLine_Stroke());
if (m_tFormat.isPaintBoundaryLines()) // -> | (left)
g2D.drawLine(origX, (int)(origY+m_tFormat.getLineStroke().floatValue()),
origX, (int)(origY+rowHeight-(4*m_tFormat.getLineStroke().floatValue())));
curX += m_tFormat.getVLineStroke().floatValue();
}
// X - start line
if (m_tFormat.isPaintHeaderLines())
{
g2D.setPaint(m_tFormat.getHeaderLine_Color());
g2D.setStroke(m_tFormat.getHeader_Stroke());
g2D.drawLine(origX, origY, // -> - (top)
(int)(origX+colWidth-m_tFormat.getVLineStroke().floatValue()), origY);
}
curY += (2 * m_tFormat.getLineStroke().floatValue()); // thick
// Background
Color bg = getBackground(HEADER_ROW, col);
if (!bg.equals(Color.white))
{
g2D.setPaint(bg);
g2D.fillRect(curX,
(int)(curY-m_tFormat.getLineStroke().floatValue()),
(int)(colWidth-m_tFormat.getVLineStroke().floatValue()),
(int)(rowHeight-(4*m_tFormat.getLineStroke().floatValue())));
}
int tempCurY = curY;
curX += H_GAP; // upper left gap
curY += V_GAP;
// Header
AttributedString aString = null;
AttributedCharacterIterator iter = null;
LineBreakMeasurer measurer = null;
float usedHeight = 0;
// Calculate column header height - teo_sarca [ 1673429 ]
String headerString = m_columnHeader[col].toString();
if (headerString.length() == 0)
headerString = " ";
//if (m_columnHeader[col].toString().length() > 0)
{
aString = new AttributedString(headerString);
aString.addAttribute(TextAttribute.FONT, getFont(HEADER_ROW, col));
aString.addAttribute(TextAttribute.FOREGROUND, getColor(HEADER_ROW, col));
//
boolean fastDraw = LayoutEngine.s_FASTDRAW;
if (fastDraw && !isView && !Util.is8Bit(headerString))
fastDraw = false;
iter = aString.getIterator();
measurer = new LineBreakMeasurer(iter, g2D.getFontRenderContext());
while (measurer.getPosition() < iter.getEndIndex()) // print header
{
TextLayout layout = measurer.nextLayout(netWidth + 2);
if (iter.getEndIndex() != measurer.getPosition())
fastDraw = false;
if (alignment.equals(MPrintFormatItem.FIELDALIGNMENTTYPE_Block))
{
layout = layout.getJustifiedLayout(netWidth + 2);
fastDraw = false;
}
curY += layout.getAscent();
float penX = curX;
if (alignment.equals(MPrintFormatItem.FIELDALIGNMENTTYPE_Center))
penX += (netWidth-layout.getAdvance())/2;
else if ((alignment.equals(MPrintFormatItem.FIELDALIGNMENTTYPE_TrailingRight) && layout.isLeftToRight())
|| (alignment.equals(MPrintFormatItem.FIELDALIGNMENTTYPE_LeadingLeft) && !layout.isLeftToRight()))
penX += netWidth-layout.getAdvance();
//
if (fastDraw)
{ // Bug - set Font/Color explicitly
g2D.setFont(getFont(HEADER_ROW, col));
g2D.setColor(getColor(HEADER_ROW, col));
g2D.drawString(iter, penX, curY);
}
else
layout.draw(g2D, penX, curY); // -> text
curY += layout.getDescent() + layout.getLeading();
usedHeight += layout.getAscent() + layout.getDescent();
if ( !m_multiLineHeader ) // one line only
break;
}
}
curX += netWidth + H_GAP;
curY = tempCurY + (int)(rowHeight-(4*m_tFormat.getLineStroke().floatValue()));
// Y end line
g2D.setPaint(m_tFormat.getVLine_Color());
g2D.setStroke(m_tFormat.getVLine_Stroke());
if (m_tFormat.isPaintVLines()) // -> | (right)
g2D.drawLine(curX, (int)(origY+m_tFormat.getLineStroke().floatValue()),
curX, (int)(origY+rowHeight-(4*m_tFormat.getLineStroke().floatValue())));
curX += m_tFormat.getVLineStroke().floatValue();
// X end line
if (m_tFormat.isPaintHeaderLines())
{
g2D.setPaint(m_tFormat.getHeaderLine_Color());
g2D.setStroke(m_tFormat.getHeader_Stroke());
g2D.drawLine(origX, curY, // -> - (button)
(int)(origX+colWidth-m_tFormat.getVLineStroke().floatValue()), curY);
}
curY += (2 * m_tFormat.getLineStroke().floatValue()); // thick
// paint Data ***************************************************
for (int row = firstRow; row < nextPageRow; row++)
{
rowHeight = m_rowHeights.get(row); // includes 2*Gaps+Line
netHeight = rowHeight - (2*V_GAP) - m_tFormat.getLineStroke().floatValue();
int rowYstart = curY;
curX = origX;
if (leftVline) // draw left | line
{
g2D.setPaint(m_tFormat.getVLine_Color());
g2D.setStroke(m_tFormat.getVLine_Stroke());
if (m_tFormat.isPaintBoundaryLines())
g2D.drawLine(curX, rowYstart, // -> | (left)
curX, (int)(rowYstart+rowHeight-m_tFormat.getLineStroke().floatValue()));
curX += m_tFormat.getVLineStroke().floatValue();
}
// Background
bg = getBackground(row, col);
if (!bg.equals(Color.white))
{
g2D.setPaint(bg);
g2D.fillRect(curX, curY,
(int)(colWidth-m_tFormat.getVLineStroke().floatValue()),
(int)(rowHeight-m_tFormat.getLineStroke().floatValue()) );
}
// Over Line
MReportLine rLine = getReportLine(row, col);
if (rLine != null)
{
if (rLine.getOverline() > 1)
{
curY -= V_GAP + m_tFormat.getVLineStroke().floatValue();
g2D.setPaint(m_tFormat.getHeaderLine_Color());
g2D.setStroke(rLine.getOverlineStroke(m_tFormat.getVLineStroke()));
g2D.drawLine(curX, (int) (curY + m_tFormat.getVLineStroke().floatValue()),
(int) (curX + colWidth - m_tFormat.getVLineStroke().floatValue()), (int) (curY + m_tFormat.getVLineStroke().floatValue()));
curY += V_GAP + m_tFormat.getVLineStroke().floatValue();
}
if (rLine.getOverline() > 0)
{
g2D.setPaint(m_tFormat.getHeaderLine_Color());
g2D.setStroke(rLine.getOverlineStroke(m_tFormat.getVLineStroke()));
g2D.drawLine(curX, curY, (int) (curX + colWidth - m_tFormat.getVLineStroke().floatValue()), curY);
curY += m_tFormat.getVLineStroke().floatValue();
}
}
curX += H_GAP; // upper left gap
curY += V_GAP;
// actual data
Object[] printItems = getPrintItems(row,col);
float penY = curY;
// suppress repeated values
boolean suppress = false;
if (m_colSuppressRepeats[col] && row > 0 && row != firstRow)
{
Object[] lastItems = {};
lastItems = getPrintItems(row-1, col);
if (Arrays.equals(lastItems,printItems) )
suppress = true;
}
if (!suppress)
{
if (m_tablePrintData != null && m_pageLogics != null && col < m_pageLogics.size())
{
String pageLogic = m_pageLogics.get(col);
if (!Util.isEmpty(pageLogic, true))
{
m_tablePrintData.setRowIndex(row);
PrintDataEvaluatee evaluatee = new PrintDataEvaluatee(getCurrentPage(), m_tablePrintData);
boolean display = Evaluator.evaluateLogic(evaluatee, pageLogic);
if (!display)
suppress = true;
}
}
}
if ( !suppress )
{
for (int index = 0; index < printItems.length; index++)
{
if (printItems[index] == null )
;
else if (printItems[index] instanceof ImageElement)
{
Image imageToDraw = ((ImageElement)printItems[index]).getImage();
if (imageToDraw != null) // teo_sarca [ 1674706 ]
{
// Draw image using the scale factor - teo_sarca, [ 1673548 ] Image is not scaled in a report table cell
double scale = ((ImageElement)printItems[index]).getScaleFactor();
if (scale != 1.0) {
AffineTransform transform = new AffineTransform();
transform.translate(curX, penY);
transform.scale(scale, scale);
g2D.drawImage(imageToDraw, transform, this);
}
else {
g2D.drawImage(imageToDraw, curX, (int)penY, this);
}
}
}
else if (printItems[index] instanceof BarcodeElement)
{
BarcodeElement barcodeElement = (BarcodeElement)printItems[index];
barcodeElement.paint(g2D, curX, (int)penY);
}
else if (printItems[index] instanceof Boolean)
{
int penX = curX + (int)((netWidth-LayoutEngine.IMAGE_SIZE.width)/2); // center
if (((Boolean)printItems[index]).booleanValue())
g2D.drawImage(LayoutEngine.IMAGE_TRUE, penX, (int)penY, this);
else
g2D.drawImage(LayoutEngine.IMAGE_FALSE, penX, (int)penY, this);
penY += LayoutEngine.IMAGE_SIZE.height;
}
else if (printItems[index] instanceof HTMLRenderer)
{
HTMLRenderer renderer = (HTMLRenderer)printItems[index];
Rectangle allocation = new Rectangle((int)colWidth, (int)netHeight);
// log.finest( "printColumn HTML - " + allocation);
g2D.translate(curX, penY);
renderer.paint(g2D, allocation);
g2D.translate(-curX, -penY);
penY += allocation.getHeight();
}
else
{
String str = printItems[index].toString();
if (DEBUG_PRINT)
if (log.isLoggable(Level.FINE)) log.fine("row=" + row + ",col=" + col + " - " + str + " 8Bit=" + Util.is8Bit(str));
if (str.length() > 0)
{
usedHeight = 0;
String[] lines = Pattern.compile("\n", Pattern.MULTILINE).split(str);
for (int lineNo = 0; lineNo < lines.length; lineNo++)
{
String thisLine = lines[lineNo];
if (thisLine.length() == 0)
thisLine = " ";
aString = new AttributedString(thisLine);
aString.addAttribute(TextAttribute.FONT, getFont(row, col));
if (isView && printItems[index] instanceof NamePair) // ID
{
aString.addAttribute(TextAttribute.FOREGROUND, LINK_COLOR);
aString.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_LOW_ONE_PIXEL, 0, str.length());
}
else
aString.addAttribute(TextAttribute.FOREGROUND, getColor(row, col));
//
iter = aString.getIterator();
boolean fastDraw = LayoutEngine.s_FASTDRAW;
if (fastDraw && !isView && !Util.is8Bit(thisLine))
fastDraw = false;
measurer = new LineBreakMeasurer(iter, g2D.getFontRenderContext());
while (measurer.getPosition() < iter.getEndIndex()) // print element
{
TextLayout layout = measurer.nextLayout(netWidth + 2);
if (iter.getEndIndex() != measurer.getPosition())
fastDraw = false;
float lineHeight = layout.getAscent() + layout.getDescent() + layout.getLeading();
if ((m_columnMaxHeight[col] <= 0
|| (usedHeight + lineHeight) <= m_columnMaxHeight[col])
&& (usedHeight + lineHeight) <= netHeight)
{
if (alignment.equals(MPrintFormatItem.FIELDALIGNMENTTYPE_Block) && measurer.getPosition() < iter.getEndIndex())
{
layout = layout.getJustifiedLayout(netWidth + 2);
fastDraw = false;
}
penY += layout.getAscent();
float penX = curX;
if (alignment.equals(MPrintFormatItem.FIELDALIGNMENTTYPE_Center))
penX += (netWidth-layout.getAdvance())/2;
else if ((alignment.equals(MPrintFormatItem.FIELDALIGNMENTTYPE_TrailingRight) && layout.isLeftToRight())
|| (alignment.equals(MPrintFormatItem.FIELDALIGNMENTTYPE_LeadingLeft) && !layout.isLeftToRight()))
penX += netWidth-layout.getAdvance();
//
if (fastDraw)
{ // Bug - set Font/Color explicitly
g2D.setFont(getFont(row, col));
if (isView && printItems[index] instanceof NamePair) // ID
{
g2D.setColor(LINK_COLOR);
// TextAttribute.UNDERLINE
}
else
g2D.setColor(getColor(row, col));
g2D.drawString(iter, penX, penY);
}
else
layout.draw(g2D, penX, penY); // -> text
if (DEBUG_PRINT)
if (log.isLoggable(Level.FINE)) log.fine("row=" + row + ",col=" + col + " - " + str + " - x=" + penX + ",y=" + penY);
penY += layout.getDescent() + layout.getLeading();
usedHeight += lineHeight;
//
if (m_columnMaxHeight[col] == -1) // FirstLineOny
break;
}
} // print element
} // for all lines
} // length > 0
} // non boolean
} // for all print items
} // not suppressed
curY += netHeight + V_GAP;
curX += netWidth + H_GAP;
// Y end line
g2D.setPaint(m_tFormat.getVLine_Color());
g2D.setStroke(m_tFormat.getVLine_Stroke());
if (m_tFormat.isPaintVLines())
g2D.drawLine(curX, rowYstart, // -> | (right)
curX, (int)(rowYstart+rowHeight-m_tFormat.getLineStroke().floatValue()));
curX += m_tFormat.getVLineStroke().floatValue();
// Under Line
if (rLine != null && rLine.getUnderline() > 0)
{
if (rLine.getUnderline() > 1)
{
curY -= V_GAP + m_tFormat.getVLineStroke().floatValue();
g2D.setPaint(m_tFormat.getHeaderLine_Color());
g2D.setStroke(rLine.getUnderlineStroke(m_tFormat.getVLineStroke()));
g2D.drawLine(origX, curY, (int) (origX + colWidth - m_tFormat.getVLineStroke().floatValue()), curY);
curY += V_GAP + m_tFormat.getVLineStroke().floatValue();
}
if (rLine.getUnderline() > 0)
{
g2D.setPaint(m_tFormat.getHeaderLine_Color());
g2D.setStroke(rLine.getUnderlineStroke(m_tFormat.getVLineStroke()));
g2D.drawLine(origX, curY, (int) (origX + colWidth - m_tFormat.getVLineStroke().floatValue()), curY);
}
}
// Maintain financial report detail and column section Y position
if ((int) (rowYstart + rowHeight) > curY)
{
curY = (int) (rowYstart + rowHeight);
}
// X end line
if (row == m_data.getRowCount()-1) // last Line
{
// left some space between underline and last line
curY += 2 * V_GAP;
/**
* Bug fix - Bottom line was always displayed whether or not header lines was set to be visible
* @author ashley
*/
if (m_tFormat.isPaintHeaderLines())
{
g2D.setPaint(m_tFormat.getHeaderLine_Color());
g2D.setStroke(m_tFormat.getHeader_Stroke());
g2D.drawLine(origX, curY, // -> - (last line)
(int)(origX+colWidth-m_tFormat.getVLineStroke().floatValue()), curY);
curY += (2 * m_tFormat.getLineStroke().floatValue()); // thick
}
else
{
curY += m_tFormat.getLineStroke().floatValue();
}
}
else
{
// next line is a function column -> underline this
boolean nextIsFunction = m_functionRows.contains(Integer.valueOf(row+1));
if (nextIsFunction && m_functionRows.contains(Integer.valueOf(row)))
nextIsFunction = false; // this is a function line too
MReportLine nextLine = getReportLine(row + 1, col);
if (nextIsFunction || (m_finReportSumRows.contains(Integer.valueOf(row + 1)) && nextLine != null
&& nextLine.getOverline() == 0))
{
g2D.setPaint(m_tFormat.getFunctFG_Color());
g2D.setStroke(m_tFormat.getHLine_Stroke());
g2D.drawLine(origX, curY, // -> - (bottom)
(int) (origX + colWidth - m_tFormat.getVLineStroke().floatValue()), curY);
}
else if (m_tFormat.isPaintHLines())
{
g2D.setPaint(m_tFormat.getHLine_Color());
g2D.setStroke(m_tFormat.getHLine_Stroke());
g2D.drawLine(origX, curY, // -> - (bottom)
(int)(origX+colWidth-m_tFormat.getVLineStroke().floatValue()), curY);
}
curY += m_tFormat.getLineStroke().floatValue();
}
} // for all rows
} // printColumn
/**
* Add Additional Lines to row/col
* @param row row
* @param col col
* @param data data
*/
private void addPrintLines (int row, int col, Serializable data)
{
while (m_printRows.getRowCount() <= row)
m_printRows.addRow(null);
m_printRows.setRowIndex(row);
List