私は、PDF上でテーブルセルを1対1でマップする必要があるアプリケーションに取り組んでいます。この目的のために
テーブルのセル幅を見つけました
0:61 1:61 2:272 3:55 4:59
しかし、PDFではそれは異なって見えます。以下は、問題を示すためのサンプルコードです。-
SimpleTableDemo.java
package com.swing.data;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.TableColumn;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
public class SimpleTableDemo extends JPanel implements ActionListener {
private boolean DEBUG = false;
private static JTable table = null;
private TableColumnAdjuster app = null;
String[] columnNames = {"First Name",
"Last Name",
"Sport",
"# of Years",
"Vegetarian"};
Object[][] data = {
{"Kathy", "Smith",
"SnowboardingXXXXXXXXXXXXXXXXXXXXXXXXXXX", new Integer(5), new Boolean(false)},
{"John", "Doe",
"Rowing", new Integer(3), new Boolean(true)},
{"Sue", "Black",
"Knitting", new Integer(2), new Boolean(false)},
{"Jane", "White",
"Speed reading", new Integer(20), new Boolean(true)},
{"Joe", "Brown",
"Pool", new Integer(10), new Boolean(false)}
};
CutomTableCellRenderer renderer = null;
ArrayList<Integer> listColWidths = new ArrayList<Integer>();
public SimpleTableDemo() {
super(new GridLayout(1,0));
table = new JTable(data, columnNames);
table.setPreferredScrollableViewportSize(new Dimension(500, 70));
table.setFillsViewportHeight(true);
renderer = new CutomTableCellRenderer(table.getDefaultRenderer(Object.class));
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.setDefaultRenderer(Object.class, renderer);
//table.setSelectionMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
app = new TableColumnAdjuster(table);
app.adjustColumns();
JScrollPane scrollPane = new JScrollPane(table);
for(int i=0;i<table.getColumnCount();i++)
{
TableColumn column = table.getColumnModel().getColumn(i);
listColWidths.add(column.getWidth());
}
//Add the scroll pane to this panel.
add(scrollPane);
JButton button = new JButton("Print");
add(button);
button.addActionListener(this);
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("SimpleTableDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create and set up the content pane.
SimpleTableDemo newContentPane = new SimpleTableDemo();
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
@Override
public void actionPerformed(ActionEvent e) {
printPdf();
}
public void printPdf()
{
Document document = new Document();
try {
PdfWriter.getInstance(document,new FileOutputStream("F://SimplePDFTable.pdf"));
document.open();
PdfPTable pdfTable = new PdfPTable(5);
int tableWidths[] = new int[columnNames.length];
for(int i= 0; i<listColWidths.size();i++)
{
tableWidths[i] = listColWidths.get(i);
}
pdfTable.setWidths(tableWidths);
PdfPCell cell = new PdfPCell();
Phrase phrase = null;
cell.setBackgroundColor(BaseColor.LIGHT_GRAY);
cell.setBorderWidthLeft(1);
for(int i=0;i<table.getColumnCount();i++)
{
phrase = new Phrase(columnNames[i]);
cell = new PdfPCell(phrase);
pdfTable.addCell(cell);
}
for(int i=0;i<5;++i)
{
for(int j=0;j<5;++j)
{
phrase = new Phrase(String.valueOf(data[i][j]));
cell = new PdfPCell(phrase);
pdfTable.addCell(cell);
}
}
document.add(pdfTable);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
document.close();
}
}
TableColumnAdjuster.java
package com.swing.data;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
/*
* Class to manage the widths of colunmns in a table.
*
* Various properties control how the width of the column is calculated.
* Another property controls whether column width calculation should be dynamic.
* Finally, various Actions will be added to the table to allow the user
* to customize the functionality.
*
* This class was designed to be used with tables that use an auto resize mode
* of AUTO_RESIZE_OFF. With all other modes you are constrained as the width
* of the columns must fit inside the table. So if you increase one column, one
* or more of the other columns must decrease. Because of this the resize mode
* of RESIZE_ALL_COLUMNS will work the best.
*/
public class TableColumnAdjuster implements PropertyChangeListener, TableModelListener
{
private JTable table;
private int spacing;
private boolean isColumnHeaderIncluded;
private boolean isColumnDataIncluded;
private boolean isOnlyAdjustLarger;
private boolean isDynamicAdjustment;
private Map<TableColumn, Integer> columnSizes = new HashMap<TableColumn, Integer>();
/*
* Specify the table and use default spacing
*/
public TableColumnAdjuster(JTable table)
{
this(table, 6);
}
/*
* Specify the table and spacing
*/
public TableColumnAdjuster(JTable table, int spacing)
{
this.table = table;
this.spacing = spacing;
setColumnHeaderIncluded( true );
setColumnDataIncluded( true );
setOnlyAdjustLarger( true );
setDynamicAdjustment( false );
installActions();
}
/*
* Adjust the widths of all the columns in the table
*/
public void adjustColumns()
{
TableColumnModel tcm = table.getColumnModel();
for (int i = 0; i < tcm.getColumnCount(); i++)
{
adjustColumn(i);
}
}
/*
* Adjust the width of the specified column in the table
*/
public void adjustColumn(final int column)
{
TableColumn tableColumn = table.getColumnModel().getColumn(column);
if (! tableColumn.getResizable()) return;
int columnHeaderWidth = getColumnHeaderWidth( column );
int columnDataWidth = getColumnDataWidth( column );
int preferredWidth = Math.max(columnHeaderWidth, columnDataWidth);
System.out.println(column + ":" + preferredWidth);
updateTableColumn(column, preferredWidth);
}
/*
* Calculated the width based on the column name
*/
private int getColumnHeaderWidth(int column)
{
if (! isColumnHeaderIncluded) return 0;
TableColumn tableColumn = table.getColumnModel().getColumn(column);
Object value = tableColumn.getHeaderValue();
TableCellRenderer renderer = tableColumn.getHeaderRenderer();
if (renderer == null)
{
renderer = table.getTableHeader().getDefaultRenderer();
}
Component c = renderer.getTableCellRendererComponent(table, value, false, false, -1, column);
return c.getPreferredSize().width;
}
/*
* Calculate the width based on the widest cell renderer for the
* given column.
*/
private int getColumnDataWidth(int column)
{
if (! isColumnDataIncluded) return 0;
int preferredWidth = 0;
int maxWidth = table.getColumnModel().getColumn(column).getMaxWidth();
for (int row = 0; row < table.getRowCount(); row++)
{
preferredWidth = Math.max(preferredWidth, getCellDataWidth(row, column));
// We've exceeded the maximum width, no need to check other rows
if (preferredWidth >= maxWidth)
break;
}
return preferredWidth;
}
/*
* Get the preferred width for the specified cell
*/
private int getCellDataWidth(int row, int column)
{
// Inovke the renderer for the cell to calculate the preferred width
TableCellRenderer cellRenderer = table.getCellRenderer(row, column);
Component c = table.prepareRenderer(cellRenderer, row, column);
int width = c.getPreferredSize().width + table.getIntercellSpacing().width;
return width;
}
/*
* Update the TableColumn with the newly calculated width
*/
private void updateTableColumn(int column, int width)
{
final TableColumn tableColumn = table.getColumnModel().getColumn(column);
if (! tableColumn.getResizable()) return;
width += spacing;
// Don't shrink the column width
if (isOnlyAdjustLarger)
{
width = Math.max(width, tableColumn.getPreferredWidth());
}
columnSizes.put(tableColumn, new Integer(tableColumn.getWidth()));
table.getTableHeader().setResizingColumn(tableColumn);
tableColumn.setWidth(width);
}
/*
* Restore the widths of the columns in the table to its previous width
*/
public void restoreColumns()
{
TableColumnModel tcm = table.getColumnModel();
for (int i = 0; i < tcm.getColumnCount(); i++)
{
restoreColumn(i);
}
}
/*
* Restore the width of the specified column to its previous width
*/
private void restoreColumn(int column)
{
TableColumn tableColumn = table.getColumnModel().getColumn(column);
Integer width = columnSizes.get(tableColumn);
if (width != null)
{
table.getTableHeader().setResizingColumn(tableColumn);
tableColumn.setWidth( width.intValue() );
}
}
/*
* Indicates whether to include the header in the width calculation
*/
public void setColumnHeaderIncluded(boolean isColumnHeaderIncluded)
{
this.isColumnHeaderIncluded = isColumnHeaderIncluded;
}
/*
* Indicates whether to include the model data in the width calculation
*/
public void setColumnDataIncluded(boolean isColumnDataIncluded)
{
this.isColumnDataIncluded = isColumnDataIncluded;
}
/*
* Indicates whether columns can only be increased in size
*/
public void setOnlyAdjustLarger(boolean isOnlyAdjustLarger)
{
this.isOnlyAdjustLarger = isOnlyAdjustLarger;
}
/*
* Indicate whether changes to the model should cause the width to be
* dynamically recalculated.
*/
public void setDynamicAdjustment(boolean isDynamicAdjustment)
{
// May need to add or remove the TableModelListener when changed
if (this.isDynamicAdjustment != isDynamicAdjustment)
{
if (isDynamicAdjustment)
{
table.addPropertyChangeListener( this );
table.getModel().addTableModelListener( this );
}
else
{
table.removePropertyChangeListener( this );
table.getModel().removeTableModelListener( this );
}
}
this.isDynamicAdjustment = isDynamicAdjustment;
}
//
// Implement the PropertyChangeListener
//
public void propertyChange(PropertyChangeEvent e)
{
// When the TableModel changes we need to update the listeners
// and column widths
if ("model".equals(e.getPropertyName()))
{
TableModel model = (TableModel)e.getOldValue();
model.removeTableModelListener( this );
model = (TableModel)e.getNewValue();
model.addTableModelListener( this );
adjustColumns();
}
}
//
// Implement the TableModelListener
//
public void tableChanged(TableModelEvent e)
{
if (! isColumnDataIncluded) return;
// A cell has been updated
if (e.getType() == TableModelEvent.UPDATE)
{
int column = table.convertColumnIndexToView(e.getColumn());
// Only need to worry about an increase in width for this cell
if (isOnlyAdjustLarger)
{
int row = e.getFirstRow();
TableColumn tableColumn = table.getColumnModel().getColumn(column);
if (tableColumn.getResizable())
{
int width = getCellDataWidth(row, column);
updateTableColumn(column, width);
}
}
// Could be an increase of decrease so check all rows
else
{
adjustColumn( column );
}
}
// The update affected more than one column so adjust all columns
else
{
adjustColumns();
}
}
/*
* Install Actions to give user control of certain functionality.
*/
private void installActions()
{
installColumnAction(true, true, "adjustColumn", "control ADD");
installColumnAction(false, true, "adjustColumns", "control shift ADD");
installColumnAction(true, false, "restoreColumn", "control SUBTRACT");
installColumnAction(false, false, "restoreColumns", "control shift SUBTRACT");
installToggleAction(true, false, "toggleDynamic", "control MULTIPLY");
installToggleAction(false, true, "toggleLarger", "control DIVIDE");
}
/*
* Update the input and action maps with a new ColumnAction
*/
private void installColumnAction(
boolean isSelectedColumn, boolean isAdjust, String key, String keyStroke)
{
Action action = new ColumnAction(isSelectedColumn, isAdjust);
KeyStroke ks = KeyStroke.getKeyStroke( keyStroke );
table.getInputMap().put(ks, key);
table.getActionMap().put(key, action);
}
/*
* Update the input and action maps with new ToggleAction
*/
private void installToggleAction(
boolean isToggleDynamic, boolean isToggleLarger, String key, String keyStroke)
{
Action action = new ToggleAction(isToggleDynamic, isToggleLarger);
KeyStroke ks = KeyStroke.getKeyStroke( keyStroke );
table.getInputMap().put(ks, key);
table.getActionMap().put(key, action);
}
/*
* Action to adjust or restore the width of a single column or all columns
*/
class ColumnAction extends AbstractAction
{
private boolean isSelectedColumn;
private boolean isAdjust;
public ColumnAction(boolean isSelectedColumn, boolean isAdjust)
{
this.isSelectedColumn = isSelectedColumn;
this.isAdjust = isAdjust;
}
@Override
public void actionPerformed(ActionEvent e)
{
// Handle selected column(s) width change actions
if (isSelectedColumn)
{
int[] columns = table.getSelectedColumns();
for (int i = 0; i < columns.length; i++)
{
if (isAdjust)
adjustColumn(columns[i]);
else
restoreColumn(columns[i]);
}
}
else
{
if (isAdjust)
adjustColumns();
else
restoreColumns();
}
}
}
/*
* Toggle properties of the TableColumnAdjuster so the user can
* customize the functionality to their preferences
*/
class ToggleAction extends AbstractAction
{
private boolean isToggleDynamic;
private boolean isToggleLarger;
public ToggleAction(boolean isToggleDynamic, boolean isToggleLarger)
{
this.isToggleDynamic = isToggleDynamic;
this.isToggleLarger = isToggleLarger;
}
@Override
public void actionPerformed(ActionEvent e)
{
if (isToggleDynamic)
{
setDynamicAdjustment(! isDynamicAdjustment);
return;
}
if (isToggleLarger)
{
setOnlyAdjustLarger(! isOnlyAdjustLarger);
return;
}
}
}
}
上記のコードから、データが少ない場合は、PDFの幅が正しくマップされます。ただし、列の値が大きく調整されている場合、計算された列の幅は正しいpdfCellの幅にマップされません。
主なハイライトは次のとおりです。-
- データはjtable内に入力されます
- テーブル幅はTableColumnAdjusterを使用して調整されます
- 新しい列幅はリスト内に保存されます
- 印刷ボタンでは、これらの幅を使用してPdfPTableにPdfCellを印刷します。
PDFにテーブルのセル幅をマッピングするための何かを提案してください。