-
Bug
-
Resolution: Fixed
-
P3
-
9, 10, 11, 12
-
b24
-
x86_64
-
windows_10
ADDITIONAL SYSTEM INFORMATION :
Reproduced on Windows 10 using Java 11. The table prints fine with the JScrollPane with Java 1.8.0_181
A DESCRIPTION OF THE PROBLEM :
A simple JTable doesn't print correctly if the table is contained in a JScrollPane. The same table prints correctly if it's added directly to a JPanel without a JScrollPane.
Side note: Setting up the JScrollPane to always hide the scroll bars doesn't change the result.
REGRESSION : Last worked in version 8u181
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
- Run source code below.
- Print to "Microsoft Print to PDF" printer
- Open saved pdf
- Notice printed table doesn't match table visible in program window
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Table printed in pdf should have same number of rows as table appearing in program window
ACTUAL -
Table printed in pdf has a complete header but has no row data.
---------- BEGIN SOURCE ----------
import java.awt.*;
import java.awt.print.*;
import java.awt.event.WindowEvent;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
public class TablePrintTest extends JFrame {
public static void main(String[] args) {
new TablePrintTest();
}
public TablePrintTest() {
// Create table with a JScrollPane
TestTable testTable = new TestTable(true); // Table row contents don't print
// Create table without JScrollPane
//TestTable testTable = new TestTable(false); // Table prints correctly
add(testTable);
pack();
setVisible(true);
PrintUtilities printerJob = new PrintUtilities(testTable);
printerJob.print("Test Table Print");
}
public class TestTable extends JPanel {
public TestTable(Boolean useScrollPane) {
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
DefaultTableModel model = new DefaultTableModel();
model.addColumn("Column 1");
model.addColumn("Column 2");
model.addColumn("Column 3");
model.addColumn("Column 4");
for (int row=1;row<=5;row++)
model.addRow(new Object[] {
"R"+row+" C1", "R"+row+" C2", "R"+row+" C3", "R"+row+" C4" });
JTable table = new JTable(model);
if (useScrollPane == true) {
JScrollPane sp = new JScrollPane(table,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
sp.getViewport().setScrollMode(JViewport.BACKINGSTORE_SCROLL_MODE);
add(sp);
} else {
add(table.getTableHeader());
add(table);
}
}
}
/** A simple utility class that lets you very simply print
* an arbitrary component. Just pass the component to the
* PrintUtilities.printComponent. The component you want to
* print doesn't need a print method and doesn't have to
* implement any interface or do anything special at all.
* <P>
* If you are going to be printing many times, it is marginally more
* efficient to first do the following:
* <PRE>
* PrintUtilities printHelper = new PrintUtilities(theComponent);
* </PRE>
* then later do printHelper.print(). But this is a very tiny
* difference, so in most cases just do the simpler
* PrintUtilities.printComponent(componentToBePrinted).
*
* 7/99 Marty Hall, http://www.apl.jhu.edu/~hall/java/
* May be freely used or adapted.
*/
class PrintUtilities implements Printable {
private Component componentToBePrinted;
public void printComponent(Component c, String jobname) {
new PrintUtilities(c).print(jobname);
}
public PrintUtilities(Component componentToBePrinted) {
this.componentToBePrinted = componentToBePrinted;
}
public void print(String jobname) {
PrinterJob printJob = PrinterJob.getPrinterJob();
PageFormat pf = printJob.defaultPage();
pf.setOrientation(PageFormat.PORTRAIT);
// set margins to 1/2"
Paper p = new Paper();
p.setImageableArea(36, 36, p.getWidth()-72, p.getHeight()-72);
pf.setPaper(p);
printJob.setPrintable(this, pf);
printJob.setJobName(jobname);
if (printJob.printDialog()) {
try {
printJob.print();
} catch(PrinterException pe) {
System.out.println("Error printing: " + pe);
}
//componentToBePrinted.setMaximumSize(new SimDimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
}
}
public int print(Graphics g, PageFormat pageFormat, int pageIndex) {
if (pageIndex > 0) {
return(NO_SUCH_PAGE);
} else {
Graphics2D g2d = (Graphics2D)g;
g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
Component c= componentToBePrinted;
double panelX= c.getWidth();
double panelY= c.getHeight();
float imageableX = (float) pageFormat.getImageableWidth()-1;
float imageableY = (float) pageFormat.getImageableHeight()-1;
// JPR - Try to do a distortion free scale by scaling both dimensions by the same
// amount, based on the dimension that needs to be scaled the most
// orig code - g2d.scale(imageableX/panelX,imageableY/panelY);
double xscale = imageableX/panelX;
double yscale = imageableY/panelY;
double optimalScale;
if (xscale < yscale)
optimalScale = xscale;
else
optimalScale = yscale;
if (optimalScale > 1)
optimalScale = 1; // Don't make the image bigger
g2d.scale(optimalScale, optimalScale);
disableDoubleBuffering(c);
c.paint(g2d);
enableDoubleBuffering(c);
return(PAGE_EXISTS);
}
}
/** The speed and quality of printing suffers dramatically if
* any of the containers have double buffering turned on.
* So this turns if off globally.
* @see enableDoubleBuffering
*/
public void disableDoubleBuffering(Component c) {
RepaintManager currentManager = RepaintManager.currentManager(c);
currentManager.setDoubleBufferingEnabled(false);
}
/** Re-enables double buffering globally. */
public void enableDoubleBuffering(Component c) {
RepaintManager currentManager = RepaintManager.currentManager(c);
currentManager.setDoubleBufferingEnabled(true);
}
}
protected void processWindowEvent(WindowEvent e) {
if (e.getID() == WindowEvent.WINDOW_CLOSING) {
System.exit(0);
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Don't put the table in a JScrollPane. Instead add the table header and table manually to the parent JPanel and print that.
FREQUENCY : always
Reproduced on Windows 10 using Java 11. The table prints fine with the JScrollPane with Java 1.8.0_181
A DESCRIPTION OF THE PROBLEM :
A simple JTable doesn't print correctly if the table is contained in a JScrollPane. The same table prints correctly if it's added directly to a JPanel without a JScrollPane.
Side note: Setting up the JScrollPane to always hide the scroll bars doesn't change the result.
REGRESSION : Last worked in version 8u181
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
- Run source code below.
- Print to "Microsoft Print to PDF" printer
- Open saved pdf
- Notice printed table doesn't match table visible in program window
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Table printed in pdf should have same number of rows as table appearing in program window
ACTUAL -
Table printed in pdf has a complete header but has no row data.
---------- BEGIN SOURCE ----------
import java.awt.*;
import java.awt.print.*;
import java.awt.event.WindowEvent;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
public class TablePrintTest extends JFrame {
public static void main(String[] args) {
new TablePrintTest();
}
public TablePrintTest() {
// Create table with a JScrollPane
TestTable testTable = new TestTable(true); // Table row contents don't print
// Create table without JScrollPane
//TestTable testTable = new TestTable(false); // Table prints correctly
add(testTable);
pack();
setVisible(true);
PrintUtilities printerJob = new PrintUtilities(testTable);
printerJob.print("Test Table Print");
}
public class TestTable extends JPanel {
public TestTable(Boolean useScrollPane) {
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
DefaultTableModel model = new DefaultTableModel();
model.addColumn("Column 1");
model.addColumn("Column 2");
model.addColumn("Column 3");
model.addColumn("Column 4");
for (int row=1;row<=5;row++)
model.addRow(new Object[] {
"R"+row+" C1", "R"+row+" C2", "R"+row+" C3", "R"+row+" C4" });
JTable table = new JTable(model);
if (useScrollPane == true) {
JScrollPane sp = new JScrollPane(table,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
sp.getViewport().setScrollMode(JViewport.BACKINGSTORE_SCROLL_MODE);
add(sp);
} else {
add(table.getTableHeader());
add(table);
}
}
}
/** A simple utility class that lets you very simply print
* an arbitrary component. Just pass the component to the
* PrintUtilities.printComponent. The component you want to
* print doesn't need a print method and doesn't have to
* implement any interface or do anything special at all.
* <P>
* If you are going to be printing many times, it is marginally more
* efficient to first do the following:
* <PRE>
* PrintUtilities printHelper = new PrintUtilities(theComponent);
* </PRE>
* then later do printHelper.print(). But this is a very tiny
* difference, so in most cases just do the simpler
* PrintUtilities.printComponent(componentToBePrinted).
*
* 7/99 Marty Hall, http://www.apl.jhu.edu/~hall/java/
* May be freely used or adapted.
*/
class PrintUtilities implements Printable {
private Component componentToBePrinted;
public void printComponent(Component c, String jobname) {
new PrintUtilities(c).print(jobname);
}
public PrintUtilities(Component componentToBePrinted) {
this.componentToBePrinted = componentToBePrinted;
}
public void print(String jobname) {
PrinterJob printJob = PrinterJob.getPrinterJob();
PageFormat pf = printJob.defaultPage();
pf.setOrientation(PageFormat.PORTRAIT);
// set margins to 1/2"
Paper p = new Paper();
p.setImageableArea(36, 36, p.getWidth()-72, p.getHeight()-72);
pf.setPaper(p);
printJob.setPrintable(this, pf);
printJob.setJobName(jobname);
if (printJob.printDialog()) {
try {
printJob.print();
} catch(PrinterException pe) {
System.out.println("Error printing: " + pe);
}
//componentToBePrinted.setMaximumSize(new SimDimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
}
}
public int print(Graphics g, PageFormat pageFormat, int pageIndex) {
if (pageIndex > 0) {
return(NO_SUCH_PAGE);
} else {
Graphics2D g2d = (Graphics2D)g;
g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
Component c= componentToBePrinted;
double panelX= c.getWidth();
double panelY= c.getHeight();
float imageableX = (float) pageFormat.getImageableWidth()-1;
float imageableY = (float) pageFormat.getImageableHeight()-1;
// JPR - Try to do a distortion free scale by scaling both dimensions by the same
// amount, based on the dimension that needs to be scaled the most
// orig code - g2d.scale(imageableX/panelX,imageableY/panelY);
double xscale = imageableX/panelX;
double yscale = imageableY/panelY;
double optimalScale;
if (xscale < yscale)
optimalScale = xscale;
else
optimalScale = yscale;
if (optimalScale > 1)
optimalScale = 1; // Don't make the image bigger
g2d.scale(optimalScale, optimalScale);
disableDoubleBuffering(c);
c.paint(g2d);
enableDoubleBuffering(c);
return(PAGE_EXISTS);
}
}
/** The speed and quality of printing suffers dramatically if
* any of the containers have double buffering turned on.
* So this turns if off globally.
* @see enableDoubleBuffering
*/
public void disableDoubleBuffering(Component c) {
RepaintManager currentManager = RepaintManager.currentManager(c);
currentManager.setDoubleBufferingEnabled(false);
}
/** Re-enables double buffering globally. */
public void enableDoubleBuffering(Component c) {
RepaintManager currentManager = RepaintManager.currentManager(c);
currentManager.setDoubleBufferingEnabled(true);
}
}
protected void processWindowEvent(WindowEvent e) {
if (e.getID() == WindowEvent.WINDOW_CLOSING) {
System.exit(0);
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Don't put the table in a JScrollPane. Instead add the table header and table manually to the parent JPanel and print that.
FREQUENCY : always
- relates to
-
JDK-8164032 JViewport backing store image is not scaled on HiDPI display
- Resolved