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