1 /*
2  * Copyright (c) 2007, 2012, 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 6425068 7157659 8132890
27  * @summary Confirm that text prints where we expect to the length we expect.
28  * @run main/manual=yesno PrintTextTest
29  */
30 
31 import java.awt.*;
32 import java.awt.event.*;
33 import java.text.*;
34 import java.util.*;
35 import java.awt.font.*;
36 import java.awt.geom.*;
37 import java.awt.print.*;
38 import javax.swing.*;
39 
40 public class PrintTextTest extends Component implements Printable {
41 
42     static int preferredSize;
43     Font textFont;
44     AffineTransform gxTx;
45     String page;
46     boolean useFM;
47 
main(String args[])48     public static void main(String args[]) {
49         String[] instructions =
50         {
51             "This tests that printed text renders similarly to on-screen",
52             "under a variety of APIs and graphics and font transforms",
53             "Print to your preferred printer. Collect the output.",
54             "Refer to the onscreen buttons to cycle through the on-screen",
55             "content",
56             "For each page, confirm that the printed content corresponds to",
57             "the on-screen rendering for that *same* page.",
58             "Some cases may look odd but its intentional. Verify",
59             "it looks the same on screen and on the printer.",
60             "Note that text does not scale linearly from screen to printer",
61             "so some differences are normal and not a bug.",
62             "The easiest way to spot real problems is to check that",
63             "any underlines are the same length as the underlined text",
64             "and that any rotations are the same in each case.",
65             "Note that each on-screen page is printed in both portrait",
66             "and landscape mode",
67             "So for example, Page 1/Portrait, and Page 1/Landscape when",
68             "rotated to view properly, should both match Page 1 on screen.",
69         };
70         Sysout.createDialogWithInstructions(instructions);
71 
72 
73         PrinterJob pjob = PrinterJob.getPrinterJob();
74         PageFormat portrait = pjob.defaultPage();
75         portrait.setOrientation(PageFormat.PORTRAIT);
76         preferredSize = (int)portrait.getImageableWidth();
77 
78         PageFormat landscape = pjob.defaultPage();
79         landscape.setOrientation(PageFormat.LANDSCAPE);
80 
81         Book book = new Book();
82 
83         JTabbedPane p = new JTabbedPane();
84 
85         int page = 1;
86         Font font = new Font("Dialog", Font.PLAIN, 18);
87         String name = "Page " + new Integer(page++);
88         PrintTextTest ptt = new PrintTextTest(name, font, null, false);
89         p.add(name, ptt);
90         book.append(ptt, portrait);
91         book.append(ptt, landscape);
92 
93         font = new Font("Dialog", Font.PLAIN, 18);
94         name = "Page " + new Integer(page++);
95         ptt = new PrintTextTest(name, font, null, true);
96         p.add(name, ptt);
97         book.append(ptt, portrait);
98         book.append(ptt, landscape);
99 
100         font = new Font("Lucida Sans", Font.PLAIN, 18);
101         name = "Page " + new Integer(page++);
102         ptt = new PrintTextTest(name, font, null, false);
103         p.add(name, ptt);
104         book.append(ptt, portrait);
105         book.append(ptt, landscape);
106 
107         font = new Font("Lucida Sans", Font.PLAIN, 18);
108         AffineTransform rotTx = AffineTransform.getRotateInstance(0.15);
109         rotTx.translate(60,0);
110         name = "Page " + new Integer(page++);
111         ptt = new PrintTextTest(name, font, rotTx, false);
112         p.add(name, ptt);
113         book.append(ptt, portrait);
114         book.append(ptt, landscape);
115 
116         font = new Font("Dialog", Font.PLAIN, 18);
117         AffineTransform scaleTx = AffineTransform.getScaleInstance(1.25, 1.25);
118         name = "Page " + new Integer(page++);
119         ptt = new PrintTextTest(name, font, scaleTx, false);
120         p.add(name, ptt);
121         book.append(ptt, portrait);
122         book.append(ptt, landscape);
123 
124         font = new Font("Dialog", Font.PLAIN, 18);
125         scaleTx = AffineTransform.getScaleInstance(-1.25, 1.25);
126         scaleTx.translate(-preferredSize/1.25, 0);
127         name = "Page " + new Integer(page++);
128         ptt = new PrintTextTest(name, font, scaleTx, false);
129         p.add(name, ptt);
130         book.append(ptt, portrait);
131         book.append(ptt, landscape);
132 
133         font = new Font("Dialog", Font.PLAIN, 18);
134         scaleTx = AffineTransform.getScaleInstance(1.25, -1.25);
135         scaleTx.translate(0, -preferredSize/1.25);
136         name = "Page " + new Integer(page++);
137         ptt = new PrintTextTest(name, font, scaleTx, false);
138         p.add(name, ptt);
139         book.append(ptt, portrait);
140         book.append(ptt, landscape);
141 
142         font = font.deriveFont(rotTx);
143         name = "Page " + new Integer(page++);
144         ptt = new PrintTextTest(name, font, null, false);
145         p.add(ptt, BorderLayout.CENTER);
146         p.add(name, ptt);
147         book.append(ptt, portrait);
148         book.append(ptt, landscape);
149 
150         font = new Font("Monospaced", Font.PLAIN, 12);
151         name = "Page " + new Integer(page++);
152         ptt = new PrintTextTest(name, font, null, false);
153         p.add(ptt, BorderLayout.CENTER);
154         p.add(name, ptt);
155         book.append(ptt, portrait);
156         book.append(ptt, landscape);
157 
158         Font xfont = font.deriveFont(AffineTransform.getScaleInstance(1.5, 1));
159         name = "Page " + new Integer(page++);
160         ptt = new PrintTextTest(name, xfont, null, false);
161         p.add(ptt, BorderLayout.CENTER);
162         p.add(name, ptt);
163         book.append(ptt, portrait);
164         book.append(ptt, landscape);
165 
166         Font yfont = font.deriveFont(AffineTransform.getScaleInstance(1, 1.5));
167         name = "Page " + new Integer(page++);
168         ptt = new PrintTextTest(name, yfont, null, false);
169         p.add(ptt, BorderLayout.CENTER);
170         p.add(name, ptt);
171         book.append(ptt, portrait);
172         book.append(ptt, landscape);
173 
174         if (System.getProperty("os.name").startsWith("Windows")) {
175             font = new Font("MS Gothic", Font.PLAIN, 12);
176             name = "Page " + new Integer(page++);
177             ptt = new PrintJAText(name, font, null, true);
178             p.add(ptt, BorderLayout.CENTER);
179             p.add(name, ptt);
180             book.append(ptt, portrait);
181             book.append(ptt, landscape);
182 
183             font = new Font("MS Gothic", Font.PLAIN, 12);
184             name = "Page " + new Integer(page++);
185             rotTx = AffineTransform.getRotateInstance(0.15);
186             ptt = new PrintJAText(name, font, rotTx, true);
187             p.add(ptt, BorderLayout.CENTER);
188             p.add(name, ptt);
189             book.append(ptt, portrait);
190             book.append(ptt, landscape);
191         }
192 
193         pjob.setPageable(book);
194 
195         JFrame f = new JFrame();
196         f.add(BorderLayout.CENTER, p);
197         f.addWindowListener(new WindowAdapter() {
198             public void windowClosing(WindowEvent e) {System.exit(0);}
199         });
200         f.pack();
201         f.show();
202 
203         try {
204             if (pjob.printDialog()) {
205                 pjob.print();
206             }
207         } catch (PrinterException e) {
208             throw new RuntimeException(e.getMessage());
209         }
210     }
211 
PrintTextTest(String page, Font font, AffineTransform gxTx, boolean fm)212     public PrintTextTest(String page, Font font, AffineTransform gxTx,
213                          boolean fm) {
214         this.page = page;
215         textFont = font;
216         this.gxTx = gxTx;
217         this.useFM = fm;
218         setBackground(Color.white);
219     }
220 
getIterator(String s)221     public static AttributedCharacterIterator getIterator(String s) {
222         return new AttributedString(s).getIterator();
223     }
224 
orient(PageFormat pf)225     static String orient(PageFormat pf) {
226         if (pf.getOrientation() == PageFormat.PORTRAIT) {
227             return "Portrait";
228         } else {
229             return "Landscape";
230         }
231     }
232 
print(Graphics g, PageFormat pf, int pageIndex)233     public int print(Graphics g, PageFormat pf, int pageIndex) {
234 
235         Graphics2D g2d = (Graphics2D)g;
236         g2d.translate(pf.getImageableX(),  pf.getImageableY());
237         g.drawString(page+" "+orient(pf),50,20);
238         g.translate(0, 25);
239         paint(g);
240         return PAGE_EXISTS;
241     }
242 
getMinimumSize()243     public Dimension getMinimumSize() {
244         return getPreferredSize();
245     }
246 
getPreferredSize()247     public Dimension getPreferredSize() {
248         return new Dimension(preferredSize, preferredSize);
249     }
250 
paint(Graphics g)251     public void paint(Graphics g) {
252 
253         /* fill with white before any transformation is applied */
254         g.setColor(Color.white);
255         g.fillRect(0, 0, getSize().width, getSize().height);
256 
257 
258         Graphics2D g2d = (Graphics2D) g;
259         if (gxTx != null) {
260             g2d.transform(gxTx);
261         }
262         if (useFM) {
263             g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
264                                  RenderingHints.VALUE_FRACTIONALMETRICS_ON);
265         }
266 
267         g.setFont(textFont);
268         FontMetrics fm = g.getFontMetrics();
269 
270         String s;
271         int LS = 30;
272         int ix=10, iy=LS+10;
273         g.setColor(Color.black);
274 
275         s = "drawString(String str, int x, int y)";
276         g.drawString(s, ix, iy);
277         if (!textFont.isTransformed()) {
278             g.drawLine(ix, iy+1, ix+fm.stringWidth(s), iy+1);
279         }
280 
281         iy += LS;
282         s = "drawString(AttributedCharacterIterator iterator, int x, int y)";
283         g.drawString(getIterator(s), ix, iy);
284 
285         iy += LS;
286         s = "\tdrawChars(\t\r\nchar[], int off, int len, int x, int y\t)";
287         g.drawChars(s.toCharArray(), 0, s.length(), ix, iy);
288         if (!textFont.isTransformed()) {
289             g.drawLine(ix, iy+1, ix+fm.stringWidth(s), iy+1);
290         }
291 
292         iy += LS;
293         s = "drawBytes(byte[], int off, int len, int x, int y)";
294         byte data[] = new byte[s.length()];
295         for (int i = 0; i < data.length; i++) {
296             data[i] = (byte) s.charAt(i);
297         }
298         g.drawBytes(data, 0, data.length, ix, iy);
299 
300         Font f = g2d.getFont();
301         FontRenderContext frc = g2d.getFontRenderContext();
302 
303         iy += LS;
304         s = "drawString(String s, float x, float y)";
305         g2d.drawString(s, (float) ix, (float) iy);
306         if (!textFont.isTransformed()) {
307             g.drawLine(ix, iy+1, ix+fm.stringWidth(s), iy+1);
308         }
309 
310         iy += LS;
311         s = "drawString(AttributedCharacterIterator iterator, "+
312             "float x, float y)";
313         g2d.drawString(getIterator(s), (float) ix, (float) iy);
314 
315         iy += LS;
316         s = "drawGlyphVector(GlyphVector g, float x, float y)";
317         GlyphVector gv = f.createGlyphVector(frc, s);
318         g2d.drawGlyphVector(gv, ix, iy);
319         Point2D adv = gv.getGlyphPosition(gv.getNumGlyphs());
320         if (!textFont.isTransformed()) {
321             g.drawLine(ix, iy+1, ix+(int)adv.getX(), iy+1);
322         }
323 
324         iy += LS;
325         s = "GlyphVector with position adjustments";
326 
327         gv = f.createGlyphVector(frc, s);
328         int ng = gv.getNumGlyphs();
329         adv = gv.getGlyphPosition(ng);
330         for (int i=0; i<ng; i++) {
331             Point2D gp = gv.getGlyphPosition(i);
332             double gx = gp.getX();
333             double gy = gp.getY();
334             if (i % 2 == 0) {
335                 gy+=5;
336             } else {
337                 gy-=5;
338             }
339             gp.setLocation(gx, gy);
340             gv.setGlyphPosition(i, gp);
341         }
342         g2d.drawGlyphVector(gv, ix, iy);
343         if (!textFont.isTransformed()) {
344             g.drawLine(ix, iy+1, ix+(int)adv.getX(), iy+1);
345         }
346 
347         iy +=LS;
348         s = "drawString: \u0924\u094d\u0930 \u0915\u0948\u0930\u0947 End.";
349         g.drawString(s, ix, iy);
350         if (!textFont.isTransformed()) {
351             g.drawLine(ix, iy+1, ix+fm.stringWidth(s), iy+1);
352         }
353 
354         iy += LS;
355         s = "TextLayout 1: \u0924\u094d\u0930 \u0915\u0948\u0930\u0947 End.";
356         TextLayout tl = new TextLayout(s, new HashMap(), frc);
357         tl.draw(g2d,  ix, iy);
358 
359         iy += LS;
360         s = "TextLayout 2: \u0924\u094d\u0930 \u0915\u0948\u0930\u0947 End.";
361         tl = new TextLayout(s, f, frc);
362         tl.draw(g2d,  ix, iy);
363     }
364 }
365 
366 class PrintJAText extends PrintTextTest {
367 
368 
PrintJAText(String page, Font font, AffineTransform gxTx, boolean fm)369     public PrintJAText(String page, Font font, AffineTransform gxTx,
370                          boolean fm) {
371         super(page, font, gxTx, fm);
372     }
373 
374     private static final String TEXT =
375         "\u3042\u3044\u3046\u3048\u304a\u30a4\u30ed\u30cf" +
376         "\u30cb\u30db\u30d8\u30c8\u4e00\u4e01\u4e02\u4e05\uff08";
377 
378 
paint(Graphics g)379     public void paint(Graphics g) {
380 
381         /* fill with white before any transformation is applied */
382         g.setColor(Color.white);
383         g.fillRect(0, 0, getSize().width, getSize().height);
384 
385 
386         Graphics2D g2d = (Graphics2D) g;
387         if (gxTx != null) {
388             g2d.transform(gxTx);
389         }
390         if (useFM) {
391             g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
392                                  RenderingHints.VALUE_FRACTIONALMETRICS_ON);
393         }
394 
395         String text = TEXT + TEXT + TEXT;
396         g.setColor(Color.black);
397         int y = 20;
398         float origSize = 7f;
399         for (int i=0;i<11;i++) {
400             float size = origSize+(i*0.1f);
401             g2d.translate(0, size+6);
402             Font f = textFont.deriveFont(size);
403             g2d.setFont(f);
404             FontMetrics fontMetrics = g2d.getFontMetrics();
405             int stringWidth = fontMetrics.stringWidth(text);
406             g.drawLine(0, y+1, stringWidth, y+1);
407             g.drawString(text, 0, y);
408             y +=10;
409         }
410     }
411 }
412 
413 class Sysout
414  {
415    private static TestDialog dialog;
416 
createDialogWithInstructions( String[] instructions )417    public static void createDialogWithInstructions( String[] instructions )
418     {
419       dialog = new TestDialog( new Frame(), "Instructions" );
420       dialog.printInstructions( instructions );
421       dialog.show();
422       println( "Any messages for the tester will display here." );
423     }
424 
createDialog( )425    public static void createDialog( )
426     {
427       dialog = new TestDialog( new Frame(), "Instructions" );
428       String[] defInstr = { "Instructions will appear here. ", "" } ;
429       dialog.printInstructions( defInstr );
430       dialog.show();
431       println( "Any messages for the tester will display here." );
432     }
433 
434 
printInstructions( String[] instructions )435    public static void printInstructions( String[] instructions )
436     {
437       dialog.printInstructions( instructions );
438     }
439 
440 
println( String messageIn )441    public static void println( String messageIn )
442     {
443       dialog.displayMessage( messageIn );
444     }
445 
446  }// Sysout  class
447 
448 /**
449   This is part of the standard test machinery.  It provides a place for the
450    test instructions to be displayed, and a place for interactive messages
451    to the user to be displayed.
452   To have the test instructions displayed, see Sysout.
453   To have a message to the user be displayed, see Sysout.
454   Do not call anything in this dialog directly.
455   */
456 class TestDialog extends Dialog
457  {
458 
459    TextArea instructionsText;
460    TextArea messageText;
461    int maxStringLength = 80;
462 
463    //DO NOT call this directly, go through Sysout
TestDialog( Frame frame, String name )464    public TestDialog( Frame frame, String name )
465     {
466       super( frame, name );
467       int scrollBoth = TextArea.SCROLLBARS_BOTH;
468       instructionsText = new TextArea( "", 20, maxStringLength, scrollBoth );
469       add( "North", instructionsText );
470 
471       messageText = new TextArea( "", 5, maxStringLength, scrollBoth );
472       add("South", messageText);
473 
474       pack();
475 
476       show();
477     }// TestDialog()
478 
479    //DO NOT call this directly, go through Sysout
printInstructions( String[] instructions )480    public void printInstructions( String[] instructions )
481     {
482       //Clear out any current instructions
483       instructionsText.setText( "" );
484 
485       //Go down array of instruction strings
486 
487       String printStr, remainingStr;
488       for( int i=0; i < instructions.length; i++ )
489        {
490      //chop up each into pieces maxSringLength long
491      remainingStr = instructions[ i ];
492      while( remainingStr.length() > 0 )
493       {
494         //if longer than max then chop off first max chars to print
495         if( remainingStr.length() >= maxStringLength )
496          {
497            //Try to chop on a word boundary
498            int posOfSpace = remainingStr.
499           lastIndexOf( ' ', maxStringLength - 1 );
500 
501            if( posOfSpace <= 0 ) posOfSpace = maxStringLength - 1;
502 
503            printStr = remainingStr.substring( 0, posOfSpace + 1 );
504            remainingStr = remainingStr.substring( posOfSpace + 1 );
505          }
506         //else just print
507         else
508          {
509            printStr = remainingStr;
510            remainingStr = "";
511          }
512 
513             instructionsText.append( printStr + "\n" );
514 
515       }// while
516 
517        }// for
518 
519     }//printInstructions()
520 
521    //DO NOT call this directly, go through Sysout
displayMessage( String messageIn )522    public void displayMessage( String messageIn )
523     {
524       messageText.append( messageIn + "\n" );
525     }
526 
527 }// TestDialog  class
528