1 /* 2 * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /** 25 * @test 26 * @bug 6531728 27 * @summary Test printing of images which need to have src area clipped 28 * @run main/manual=yesno/timeout=900 ClippedImages 29 */ 30 31 import java.io.*; 32 import java.awt.*; 33 import java.awt.geom.*; 34 import java.awt.event.*; 35 import java.awt.print.*; 36 import java.awt.image.BufferedImage; 37 import javax.print.*; 38 import javax.print.attribute.*; 39 40 public class ClippedImages extends Frame implements ActionListener { 41 42 private ClippedImageCanvas c; 43 main(String args[])44 public static void main(String args[]) { 45 46 ClippedImages f = new ClippedImages(); 47 f.setVisible(true); 48 } 49 ClippedImages()50 public ClippedImages() { 51 super("Clipped Src Area Image Printing Test"); 52 c = new ClippedImageCanvas(); 53 add("Center", c); 54 55 Button paintButton = new Button("Toggle Contents"); 56 paintButton.addActionListener(this); 57 58 Button printThisButton = new Button("Print This"); 59 printThisButton.addActionListener(this); 60 61 Button printAllButton = new Button("Print All"); 62 printAllButton.addActionListener(this); 63 64 Panel p = new Panel(); 65 p.add(paintButton); 66 p.add(printThisButton); 67 p.add(printAllButton); 68 add("South", p); 69 add("North", getInstructions()); 70 addWindowListener(new WindowAdapter() { 71 public void windowClosing(WindowEvent e) { 72 System.exit(0); 73 } 74 }); 75 76 pack(); 77 } 78 getInstructions()79 private TextArea getInstructions() { 80 TextArea ta = new TextArea(18, 60); 81 ta.setFont(new Font("Dialog", Font.PLAIN, 11)); 82 ta.setText 83 ("This is a manual test as it requires that you compare "+ 84 "the on-screen rendering with the printed output.\n"+ 85 "Select the 'Print All' button to print out the test\n"+ 86 "It will generate 4 sides of content: as it will print "+ 87 "each of 2 sets of transformed images in portrait, \n"+ 88 "and landscape orientations. \n"+ 89 "The sets of images are in turn made up\n"+ 90 "of two similar sets of pages: one is 'random' images,\n "+ 91 " the other is 16 squares.\n"+ 92 "Use the 'Toggle Contents' button to view the screen rendering\n"+ 93 "For each page compare the printed content to the same\n"+ 94 "on-screen one taking careful note of\n"+ 95 "a) the positions of the red/blue circles on the corners\n"+ 96 "b) that numerical text on the image is displayed similarly\n"+ 97 "e) that the green quadrilaterals match on-screen\n"+ 98 "f) that the rendering is clipped at the default (typically 1 inch) "+ 99 "margins of the page.\n"+ 100 "The test PASSES if the onscreen and printed rendering match"); 101 return ta; 102 } 103 actionPerformed(ActionEvent e)104 public void actionPerformed(ActionEvent e) { 105 106 if (e.getActionCommand().equals("Print This")) { 107 printOne(); 108 } else if (e.getActionCommand().equals("Print All")) { 109 printAll(); 110 } else if (e.getActionCommand().equals("Toggle Contents")) { 111 c.toggleContents(); 112 c.repaint(); 113 } 114 } 115 printOne()116 private void printOne() { 117 PrinterJob pj = PrinterJob.getPrinterJob(); 118 119 PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet(); 120 if (pj != null && (false||pj.printDialog(attrs))) { 121 c.setPrinterJob(pj, false); 122 pj.setPrintable(c); 123 try { 124 pj.print(attrs); 125 } catch (PrinterException pe) { 126 pe.printStackTrace(); 127 throw new RuntimeException("Exception whilst printing."); 128 } finally { 129 System.out.println("PRINT RETURNED OK."); 130 } 131 } 132 } 133 printAll()134 private void printAll() { 135 PrinterJob pj = PrinterJob.getPrinterJob(); 136 PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet(); 137 if (pj != null && (false||pj.printDialog(attrs))) { 138 c.setPrinterJob(pj, true); 139 pj.setPageable(c); 140 try { 141 pj.print(attrs); 142 } catch (PrinterException pe) { 143 pe.printStackTrace(); 144 throw new RuntimeException("Exception whilst printing."); 145 } finally { 146 System.out.println("PRINT RETURNED OK."); 147 } 148 } 149 } 150 } 151 152 class ClippedImageCanvas extends Component implements Printable, Pageable { 153 154 BufferedImage img = null; 155 int sw=50, sh=50; 156 ClippedImageCanvas()157 ClippedImageCanvas() { 158 img = new BufferedImage(sw, sh, BufferedImage.TYPE_INT_RGB); 159 Graphics2D g2d = img.createGraphics(); 160 g2d.setColor(Color.red); 161 g2d.fillRect(0 ,0, sw, sh); 162 g2d.setColor(Color.black); 163 int cnt = 0; 164 Font font = new Font("Serif", Font.PLAIN, 11); 165 g2d.setFont(font); 166 FontMetrics fm = g2d.getFontMetrics(); 167 for (int y=12;y<sh;y+=12) { 168 int x = 0; 169 while (x < sw) { 170 String s = (new Integer(++cnt)).toString(); 171 g2d.drawString(s, x, y); 172 x+= fm.stringWidth(s); 173 } 174 } 175 } 176 177 private boolean paintSquares = true; toggleContents()178 void toggleContents() { 179 paintSquares = !paintSquares; 180 } 181 getNumberOfPages()182 public int getNumberOfPages() { 183 if (pageable) { 184 return 4; 185 } else { 186 return 1; 187 } 188 } 189 190 boolean pageable = false; 191 PrinterJob myPrinterJob; setPrinterJob(PrinterJob job, boolean pageable)192 void setPrinterJob(PrinterJob job, boolean pageable) { 193 this.myPrinterJob = job; 194 this.pageable = pageable; 195 } 196 getPageFormat(int pageIndex)197 public PageFormat getPageFormat(int pageIndex) 198 throws IndexOutOfBoundsException { 199 200 if (pageIndex < 0 || pageIndex >= getNumberOfPages()) { 201 throw new IndexOutOfBoundsException(); 202 } 203 204 PageFormat pf = myPrinterJob.defaultPage(); 205 switch (pageIndex % 2) { 206 207 case 0 : 208 pf.setOrientation(PageFormat.PORTRAIT); 209 break; 210 211 case 1: 212 pf.setOrientation(PageFormat.LANDSCAPE); 213 break; 214 } 215 return pf; 216 } 217 getOrientStr(PageFormat pf)218 String getOrientStr(PageFormat pf) { 219 if (pf.getOrientation() == PageFormat.PORTRAIT) { 220 return "Portrait Orientation, "; 221 } else { 222 return "Landscape Orientation,"; 223 } 224 } 225 getPrintable(int pageIndex)226 public Printable getPrintable(int pageIndex) 227 throws IndexOutOfBoundsException { 228 229 if (pageIndex < 0 || pageIndex >= getNumberOfPages()) { 230 throw new IndexOutOfBoundsException(); 231 } 232 if (pageIndex < 2) { 233 paintSquares = true; 234 } else { 235 paintSquares = false; 236 } 237 return this; 238 } 239 print(Graphics g, PageFormat pgFmt, int pgIndex)240 public int print(Graphics g, PageFormat pgFmt, int pgIndex) { 241 242 if (pgIndex > getNumberOfPages()-1) { 243 return Printable.NO_SUCH_PAGE; 244 } 245 Graphics2D g2d = (Graphics2D)g; 246 g2d.translate(pgFmt.getImageableX(), pgFmt.getImageableY()); 247 g.drawString(getOrientStr(pgFmt), 0, 12); 248 paint(g2d); 249 return Printable.PAGE_EXISTS; 250 } 251 drawImage(Graphics g, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2)252 private void drawImage(Graphics g, 253 int dx1, int dy1, int dx2, int dy2, 254 int sx1, int sy1, int sx2, int sy2) { 255 256 int rx = (dx1 < dx2) ? dx1 : dx2; 257 int ry = (dy1 < dy2) ? dy1 : dy2; 258 int rw = dx2-dx1; 259 if (rw < 0) rw = -rw; 260 int rh = dy2-dy1; 261 if (rh < 0) rh = -rh; 262 263 g.setColor(Color.green); 264 g.drawRect(rx-1 ,ry-1, rw+1, rh+1); 265 g.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null); 266 g.setColor(Color.blue); 267 int r=5; 268 g.drawOval(dx1-r, dy1-r, 2*r, 2*r); 269 g.setColor(Color.red); 270 g.drawOval(dx2-r, dy2-r, 2*r, 2*r); 271 } 272 273 private AffineTransform savedTx = null; 274 saveTx(Graphics2D g2d)275 private void saveTx(Graphics2D g2d) { 276 savedTx = g2d.getTransform(); 277 } 278 restoreTx(Graphics2D g2d)279 private void restoreTx(Graphics2D g2d) { 280 g2d.setTransform(savedTx); 281 } 282 paint(Graphics g)283 public void paint(Graphics g) { 284 Dimension size = getSize(); 285 g.setColor(Color.black); 286 for (int p=0;p<size.width;p+=20) { 287 g.drawLine(p, 0, p, size.height); 288 } 289 for (int p=0;p<size.height;p+=20) { 290 g.drawLine(0, p, size.width, p); 291 } 292 if (paintSquares) { 293 paintSquares(g); 294 } else { 295 paintRandom(g); 296 } 297 } 298 paintRandom(Graphics g)299 private void paintRandom(Graphics g) { 300 301 int dx, dy, dw, dh; 302 303 Graphics2D g2d = (Graphics2D)g; 304 g.setColor(Color.black); 305 306 saveTx(g2d); 307 int sx = -20, sy=-20; 308 309 dx=300; dy=10; dw=50; dh=50; 310 311 drawImage(g, dx, dy, dx+dw, dy+dh ,sx,sy,1,1); 312 313 dx=20; dy=20; dw=400; dh=80; 314 g2d.shear(0.0, Math.PI/20); 315 drawImage(g, dx, dy, dx+dw, dy+dh, sx, sy, dw/2, dh/2); 316 317 dx=125; dy=40; 318 restoreTx(g2d); 319 320 g2d.rotate(Math.PI/4); 321 drawImage(g, dx, dy, dx+dw, dy+dh, sx, sy, dw/2, dh/2); 322 323 restoreTx(g2d); 324 325 dx=290; dy=180; dw=20; dh=20; 326 drawImage(g, dx, dy, dx+dw*10, dy+dh*10, 30, sy, dw, dh); 327 g2d.scale(-1, -1); 328 dx=-280; dy=-200; 329 drawImage(g, dx, dy, dx+dw*2, dy+dh*2, 30, sy, dw, dh); 330 331 restoreTx(g2d); 332 333 g2d.scale(1, -1); 334 dx=430; dy=-150; 335 drawImage(g, dx, dy, dx+dw*5, dy+dh*2, 30, sy, dw, dh); 336 337 restoreTx(g2d); 338 339 dx = 10; dy = 290; dw = 200; dh = 200; 340 drawImage(g, dx, dy, dx+dw, dy+dh, sx, sy, sw, sh); 341 342 dx = 0; dy = 400; dw=-30; dh=-50; 343 drawImage(g, dx, dy, dx-dw, dy-dh, dx, dy, dx-dw, dy-dh); 344 } 345 paintSquares(Graphics g)346 private void paintSquares(Graphics g) { 347 348 /* drawImage is required to handle mapping sx1,sy1 -> dx1,dy1 and 349 * sx2,sy2 -> dx2,dy2 which may imply flips and scales. 350 * To test this we need to test all combinations of these parameters 351 * with drawImage. 352 * If we have a source rectangle with vertices, sA, sB, sC, sD 353 * there are 4 combinations : sA+sD, sD+sA, sB+sC, sC+sB. 354 * Similarly for the destination with vertices, dA, dB, dC, dD 355 * there are 4 combinations : dA+dD, dD+dA, dB+dC, dC+dB. 356 * Thus we need 16 calls to test all combinations. 357 * Note that we set the source area coordinates (x and y -20->80) 358 * to be beyond the image size (50x50) so clipping is always needed. 359 */ 360 int sxa = -20, sya = -20; 361 int sxb = 80, syb = -20; 362 int sxc = -20, syc = 80; 363 int sxd = 80, syd = 80; 364 365 int dxa = 0, dya = 0; 366 int dxb = 80, dyb = 0; 367 int dxc = 0, dyc = 80; 368 int dxd = 80, dyd = 80; 369 370 int incX = 100; 371 int incY = 100; 372 373 g.translate(20, 20); 374 375 /* sA + sD -> dA + dD - the normal untransformed case */ 376 drawImage(g, dxa, dya, dxd, dyd, sxa, sya, sxd, syd); 377 g.translate(incX, 0); 378 379 /* sD + sA -> dA + dD */ 380 drawImage(g, dxa, dya, dxd, dyd, sxd, syd, sxa, sya); 381 g.translate(incX, 0); 382 383 /* sB + sC -> dA + dD */ 384 drawImage(g, dxa, dya, dxd, dyd, sxb, syb, sxc, syc); 385 g.translate(incX, 0); 386 387 /* sC + sB -> dA + dD */ 388 drawImage(g, dxa, dya, dxd, dyd, sxc, syc, sxb, syb); 389 390 g.translate(-3*incX, incY); 391 /******/ 392 393 /* sA + sD -> dD + dA */ 394 drawImage(g, dxd, dyd, dxa, dya, sxa, sya, sxd, syd); 395 g.translate(incX, 0); 396 397 /* sD + sA -> dD + dA */ 398 drawImage(g, dxd, dyd, dxa, dya, sxd, syd, sxa, sya); 399 g.translate(incX, 0); 400 401 /* sB + sC -> dD + dA */ 402 drawImage(g, dxd, dyd, dxa, dya, sxb, syb, sxc, syc); 403 g.translate(incX, 0); 404 405 /* sC + sB -> dD + dA */ 406 drawImage(g, dxd, dyd, dxa, dya, sxc, syc, sxb, syb); 407 408 g.translate(-3*incX, incY); 409 /******/ 410 411 /* sA + sD -> dB + dC */ 412 drawImage(g, dxb, dyb, dxc, dyc, sxa, sya, sxd, syd); 413 g.translate(incX, 0); 414 415 /* sD + sA -> dB + dC */ 416 drawImage(g, dxb, dyb, dxc, dyc, sxd, syd, sxa, sya); 417 g.translate(incX, 0); 418 419 /* sB + sC -> dB + dC */ 420 drawImage(g, dxb, dyb, dxc, dyc, sxb, syb, sxc, syc); 421 g.translate(incX, 0); 422 423 /* sC + sB -> dB + dC */ 424 drawImage(g, dxb, dyb, dxc, dyc, sxc, syc, sxb, syb); 425 426 g.translate(-3*incX, incY); 427 /******/ 428 429 430 /* sA + sD -> dC + dB */ 431 drawImage(g, dxc, dyc, dxb, dyb, sxa, sya, sxd, syd); 432 g.translate(incX, 0); 433 434 /* sD + sA -> dC + dB */ 435 drawImage(g, dxc, dyc, dxb, dyb, sxd, syd, sxa, sya); 436 g.translate(incX, 0); 437 438 /* sB + sC -> dC + dB */ 439 drawImage(g, dxc, dyc, dxb, dyb, sxb, syb, sxc, syc); 440 g.translate(incX, 0); 441 442 /* sC + sB -> dC + dB */ 443 drawImage(g, dxc, dyc, dxb, dyb, sxc, syc, sxb, syb); 444 } 445 446 447 448 /* Size is chosen to match default imageable width of a NA letter 449 * page. This means there will be clipping, what is clipped will 450 * depend on PageFormat orientation. 451 */ getPreferredSize()452 public Dimension getPreferredSize() { 453 return new Dimension(468, 468); 454 } 455 456 } 457