--- old/src/windows/classes/sun/awt/windows/WPathGraphics.java 2015-11-02 17:51:18.975384200 +0300 +++ new/src/windows/classes/sun/awt/windows/WPathGraphics.java 2015-11-02 17:51:18.427352800 +0300 @@ -494,24 +494,48 @@ */ float fontSize = font.getSize2D(); + double devResX = wPrinterJob.getXRes(); + double devResY = wPrinterJob.getYRes(); + + double fontDevScaleY = devResY / DEFAULT_USER_RES; + + int orient = getPageFormat().getOrientation(); + if (orient == PageFormat.LANDSCAPE || + orient == PageFormat.REVERSE_LANDSCAPE) + { + double tmp = devResX; + devResX = devResY; + devResY = tmp; + } + + double devScaleX = devResX / DEFAULT_USER_RES; + double devScaleY = devResY / DEFAULT_USER_RES; + fontTransform.scale(1.0/devScaleX, 1.0/devScaleY); + Point2D.Double pty = new Point2D.Double(0.0, 1.0); fontTransform.deltaTransform(pty, pty); double scaleFactorY = Math.sqrt(pty.x*pty.x+pty.y*pty.y); - float scaledFontSizeY = (float)(fontSize * scaleFactorY); + float scaledFontSizeY = (float)(fontSize * scaleFactorY * fontDevScaleY); Point2D.Double ptx = new Point2D.Double(1.0, 0.0); fontTransform.deltaTransform(ptx, ptx); double scaleFactorX = Math.sqrt(ptx.x*ptx.x+ptx.y*ptx.y); - float scaledFontSizeX = (float)(fontSize * scaleFactorX); float awScale = getAwScale(scaleFactorX, scaleFactorY); int iangle = getAngle(ptx); + ptx = new Point2D.Double(1.0, 0.0); + deviceTransform.deltaTransform(ptx, ptx); + double advanceScaleX = Math.sqrt(ptx.x*ptx.x+ptx.y*ptx.y); + pty = new Point2D.Double(0.0, 1.0); + deviceTransform.deltaTransform(pty, pty); + double advanceScaleY = Math.sqrt(pty.x*pty.x+pty.y*pty.y); + Font2D font2D = FontUtilities.getFont2D(font); if (font2D instanceof TrueTypeFont) { textOut(str, font, (TrueTypeFont)font2D, frc, scaledFontSizeY, iangle, awScale, - deviceTransform, scaleFactorX, + advanceScaleX, advanceScaleY, x, y, devpos.x, devpos.y, targetW); } else if (font2D instanceof CompositeFont) { /* Composite fonts are made up of multiple fonts and each @@ -542,7 +566,7 @@ PhysicalFont slotFont = compFont.getSlotFont(slot); textOut(substr, font, slotFont, frc, scaledFontSizeY, iangle, awScale, - deviceTransform, scaleFactorX, + advanceScaleX, advanceScaleY, userx, usery, devx, devy, 0f); Rectangle2D bds = font.getStringBounds(substr, frc); float xAdvance = (float)bds.getWidth(); @@ -635,18 +659,42 @@ */ float fontSize = font.getSize2D(); + double devResX = wPrinterJob.getXRes(); + double devResY = wPrinterJob.getYRes(); + + double fontDevScaleY = devResY / DEFAULT_USER_RES; + + int orient = getPageFormat().getOrientation(); + if (orient == PageFormat.LANDSCAPE || + orient == PageFormat.REVERSE_LANDSCAPE) + { + double tmp = devResX; + devResX = devResY; + devResY = tmp; + } + + double devScaleX = devResX / DEFAULT_USER_RES; + double devScaleY = devResY / DEFAULT_USER_RES; + fontTransform.scale(1.0/devScaleX, 1.0/devScaleY); + Point2D.Double pty = new Point2D.Double(0.0, 1.0); fontTransform.deltaTransform(pty, pty); double scaleFactorY = Math.sqrt(pty.x*pty.x+pty.y*pty.y); - float scaledFontSizeY = (float)(fontSize * scaleFactorY); + float scaledFontSizeY = (float)(fontSize * scaleFactorY * fontDevScaleY); - Point2D.Double pt = new Point2D.Double(1.0, 0.0); - fontTransform.deltaTransform(pt, pt); - double scaleFactorX = Math.sqrt(pt.x*pt.x+pt.y*pt.y); - float scaledFontSizeX = (float)(fontSize * scaleFactorX); + Point2D.Double ptx = new Point2D.Double(1.0, 0.0); + fontTransform.deltaTransform(ptx, ptx); + double scaleFactorX = Math.sqrt(ptx.x*ptx.x+ptx.y*ptx.y); float awScale = getAwScale(scaleFactorX, scaleFactorY); - int iangle = getAngle(pt); + int iangle = getAngle(ptx); + + ptx = new Point2D.Double(1.0, 0.0); + deviceTransform.deltaTransform(ptx, ptx); + double advanceScaleX = Math.sqrt(ptx.x*ptx.x+ptx.y*ptx.y); + pty = new Point2D.Double(0.0, 1.0); + deviceTransform.deltaTransform(pty, pty); + double advanceScaleY = Math.sqrt(pty.x*pty.x+pty.y*pty.y); int numGlyphs = gv.getNumGlyphs(); int[] glyphCodes = gv.getGlyphCodes(0, numGlyphs, null); @@ -705,8 +753,7 @@ * rotation element of the deviceTransform. */ AffineTransform advanceTransform = - new AffineTransform(deviceTransform); - advanceTransform.rotate(iangle*Math.PI/1800.0); + AffineTransform.getScaleInstance(advanceScaleX, advanceScaleY); float[] glyphAdvPos = new float[glyphPos.length]; advanceTransform.transform(glyphPos, 0, //source @@ -784,8 +831,7 @@ Font font, PhysicalFont font2D, FontRenderContext frc, float deviceSize, int rotation, float awScale, - AffineTransform deviceTransform, - double scaleFactorX, + double scaleFactorX, double scaleFactorY, float userx, float usery, float devx, float devy, float targetW) { @@ -826,8 +872,7 @@ * See earlier comment in printGlyphVector() for details. */ AffineTransform advanceTransform = - new AffineTransform(deviceTransform); - advanceTransform.rotate(rotation*Math.PI/1800.0); + AffineTransform.getScaleInstance(scaleFactorX, scaleFactorY); float[] glyphAdvPos = new float[glyphPos.length]; advanceTransform.transform(glyphPos, 0, //source @@ -841,11 +886,11 @@ /* If 2D and GDI agree on the advance of the string we do not * need to explicitly assign glyph positions. * If we are to use the GDI advance, require it to agree with - * JDK to a precision of <= 0.2% - ie 1 pixel in 500 + * JDK to a precision of <= 1.0% - ie 1 pixel in 100 * discrepancy after rounding the 2D advance to the * nearest pixel and is greater than one pixel in total. - * ie strings < 500 pixels in length will be OK so long - * as they differ by only 1 pixel even though that is > 0.02% + * ie strings < 100 pixels in length will be OK so long + * as they differ by only 1 pixel even though that is > 1% * The bounds from 2D are in user space so need to * be scaled to device space for comparison with GDI. * scaleX is the scale from user space to device space needed for this. @@ -863,7 +908,7 @@ if (ratio < 1) { ratio = 1/ratio; } - return diff <= 1 || ratio < 1.002; + return diff <= 1 || ratio < 1.01; } return true; } --- /dev/null 2015-11-02 17:51:23.000000000 +0300 +++ new/test/java/awt/print/PrinterJob/PrintTextTest.html 2015-11-02 17:51:22.261572100 +0300 @@ -0,0 +1,46 @@ + + + + + +This tests that printed text renders similarly to on-screen, +under a variety of APIs and graphics and font transforms +Print to your preferred printer. Collect the output. +Refer to the onscreen buttons to cycle through the on-screen +content +For each page, confirm that the printed content corresponds to +the on-screen rendering for that *same* page. +Some cases may look odd but its intentional. Verify +it looks the same on screen and on the printer. +Note that text does not scale linearly from screen to printer +so some differences are normal and not a bug. +The easiest way to spot real problems is to check that +any underlines are the same length as the underlined text +and that any rotations are the same in each case. +Note that each on-screen page is printed in both portrait +and landscape mode +So for example, Page 1/Portrait, and Page 1/Landscape when +rotated to view properly, should both match Page 1 on screen.; + + --- /dev/null 2015-11-02 17:51:25.000000000 +0300 +++ new/test/java/awt/print/PrinterJob/PrintTextTest.java 2015-11-02 17:51:24.871721400 +0300 @@ -0,0 +1,395 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/** + * @test + * @bug 6425068 7157659 8132890 + * @summary Confirm that text prints where we expect to the length we expect. + * @run applet/manual=yesno PrintTextTest.html + */ + +import java.awt.*; +import java.awt.event.*; +import java.text.*; +import java.util.*; +import java.awt.font.*; +import java.awt.geom.*; +import java.awt.print.*; +import javax.swing.*; + +public class PrintTextTest extends JApplet { + public void start() { + StandalonePrintTextTest.main(null); + } +} + +class StandalonePrintTextTest extends Component implements Printable { + + static int preferredSize; + Font textFont; + AffineTransform gxTx; + String page; + boolean useFM; + + public static void main(String args[]) { + + PrinterJob pjob = PrinterJob.getPrinterJob(); + PageFormat portrait = pjob.defaultPage(); + portrait.setOrientation(PageFormat.PORTRAIT); + preferredSize = (int)portrait.getImageableWidth(); + + PageFormat landscape = pjob.defaultPage(); + landscape.setOrientation(PageFormat.LANDSCAPE); + + Book book = new Book(); + + JTabbedPane p = new JTabbedPane(); + + int page = 1; + Font font = new Font("Dialog", Font.PLAIN, 18); + String name = "Page " + new Integer(page++); + StandalonePrintTextTest ptt = new StandalonePrintTextTest(name, font, null, false); + p.add(name, ptt); + book.append(ptt, portrait); + book.append(ptt, landscape); + + font = new Font("Dialog", Font.PLAIN, 18); + name = "Page " + new Integer(page++); + ptt = new StandalonePrintTextTest(name, font, null, true); + p.add(name, ptt); + book.append(ptt, portrait); + book.append(ptt, landscape); + + font = new Font("Lucida Sans", Font.PLAIN, 18); + name = "Page " + new Integer(page++); + ptt = new StandalonePrintTextTest(name, font, null, false); + p.add(name, ptt); + book.append(ptt, portrait); + book.append(ptt, landscape); + + font = new Font("Lucida Sans", Font.PLAIN, 18); + AffineTransform rotTx = AffineTransform.getRotateInstance(0.15); + rotTx.translate(60,0); + name = "Page " + new Integer(page++); + ptt = new StandalonePrintTextTest(name, font, rotTx, false); + p.add(name, ptt); + book.append(ptt, portrait); + book.append(ptt, landscape); + + font = new Font("Dialog", Font.PLAIN, 18); + AffineTransform scaleTx = AffineTransform.getScaleInstance(1.25, 1.25); + name = "Page " + new Integer(page++); + ptt = new StandalonePrintTextTest(name, font, scaleTx, false); + p.add(name, ptt); + book.append(ptt, portrait); + book.append(ptt, landscape); + + font = new Font("Dialog", Font.PLAIN, 18); + scaleTx = AffineTransform.getScaleInstance(-1.25, 1.25); + scaleTx.translate(-preferredSize/1.25, 0); + name = "Page " + new Integer(page++); + ptt = new StandalonePrintTextTest(name, font, scaleTx, false); + p.add(name, ptt); + book.append(ptt, portrait); + book.append(ptt, landscape); + + font = new Font("Dialog", Font.PLAIN, 18); + scaleTx = AffineTransform.getScaleInstance(1.25, -1.25); + scaleTx.translate(0, -preferredSize/1.25); + name = "Page " + new Integer(page++); + ptt = new StandalonePrintTextTest(name, font, scaleTx, false); + p.add(name, ptt); + book.append(ptt, portrait); + book.append(ptt, landscape); + + font = font.deriveFont(rotTx); + name = "Page " + new Integer(page++); + ptt = new StandalonePrintTextTest(name, font, null, false); + p.add(ptt, BorderLayout.CENTER); + p.add(name, ptt); + book.append(ptt, portrait); + book.append(ptt, landscape); + + font = new Font("Monospaced", Font.PLAIN, 12); + name = "Page " + new Integer(page++); + ptt = new StandalonePrintTextTest(name, font, null, false); + p.add(ptt, BorderLayout.CENTER); + p.add(name, ptt); + book.append(ptt, portrait); + book.append(ptt, landscape); + + Font xfont = font.deriveFont(AffineTransform.getScaleInstance(1.5, 1)); + name = "Page " + new Integer(page++); + ptt = new StandalonePrintTextTest(name, xfont, null, false); + p.add(ptt, BorderLayout.CENTER); + p.add(name, ptt); + book.append(ptt, portrait); + book.append(ptt, landscape); + + Font yfont = font.deriveFont(AffineTransform.getScaleInstance(1, 1.5)); + name = "Page " + new Integer(page++); + ptt = new StandalonePrintTextTest(name, yfont, null, false); + p.add(ptt, BorderLayout.CENTER); + p.add(name, ptt); + book.append(ptt, portrait); + book.append(ptt, landscape); + + if (System.getProperty("os.name").startsWith("Windows")) { + font = new Font("MS Gothic", Font.PLAIN, 12); + name = "Page " + new Integer(page++); + ptt = new PrintJAText(name, font, null, true); + p.add(ptt, BorderLayout.CENTER); + p.add(name, ptt); + book.append(ptt, portrait); + book.append(ptt, landscape); + + font = new Font("MS Gothic", Font.PLAIN, 12); + name = "Page " + new Integer(page++); + rotTx = AffineTransform.getRotateInstance(0.15); + ptt = new PrintJAText(name, font, rotTx, true); + p.add(ptt, BorderLayout.CENTER); + p.add(name, ptt); + book.append(ptt, portrait); + book.append(ptt, landscape); + } + + pjob.setPageable(book); + + JFrame f = new JFrame(); + f.add(BorderLayout.CENTER, p); + f.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) {System.exit(0);} + }); + f.pack(); + f.show(); + + try { + if (pjob.printDialog()) { + pjob.print(); + } + } catch (PrinterException e) { + throw new RuntimeException(e.getMessage()); + } + } + + public StandalonePrintTextTest(String page, Font font, AffineTransform gxTx, + boolean fm) { + this.page = page; + textFont = font; + this.gxTx = gxTx; + this.useFM = fm; + setBackground(Color.white); + } + + public static AttributedCharacterIterator getIterator(String s) { + return new AttributedString(s).getIterator(); + } + + static String orient(PageFormat pf) { + if (pf.getOrientation() == PageFormat.PORTRAIT) { + return "Portrait"; + } else { + return "Landscape"; + } + } + + public int print(Graphics g, PageFormat pf, int pageIndex) { + + Graphics2D g2d = (Graphics2D)g; + g2d.translate(pf.getImageableX(), pf.getImageableY()); + g.drawString(page+" "+orient(pf),50,20); + g.translate(0, 25); + paint(g); + return PAGE_EXISTS; + } + + public Dimension getMinimumSize() { + return getPreferredSize(); + } + + public Dimension getPreferredSize() { + return new Dimension(preferredSize, preferredSize); + } + + public void paint(Graphics g) { + + /* fill with white before any transformation is applied */ + g.setColor(Color.white); + g.fillRect(0, 0, getSize().width, getSize().height); + + + Graphics2D g2d = (Graphics2D) g; + if (gxTx != null) { + g2d.transform(gxTx); + } + if (useFM) { + g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, + RenderingHints.VALUE_FRACTIONALMETRICS_ON); + } + + g.setFont(textFont); + FontMetrics fm = g.getFontMetrics(); + + String s; + int LS = 30; + int ix=10, iy=LS+10; + g.setColor(Color.black); + + s = "drawString(String str, int x, int y)"; + g.drawString(s, ix, iy); + if (!textFont.isTransformed()) { + g.drawLine(ix, iy+1, ix+fm.stringWidth(s), iy+1); + } + + iy += LS; + s = "drawString(AttributedCharacterIterator iterator, int x, int y)"; + g.drawString(getIterator(s), ix, iy); + + iy += LS; + s = "\tdrawChars(\t\r\nchar[], int off, int len, int x, int y\t)"; + g.drawChars(s.toCharArray(), 0, s.length(), ix, iy); + if (!textFont.isTransformed()) { + g.drawLine(ix, iy+1, ix+fm.stringWidth(s), iy+1); + } + + iy += LS; + s = "drawBytes(byte[], int off, int len, int x, int y)"; + byte data[] = new byte[s.length()]; + for (int i = 0; i < data.length; i++) { + data[i] = (byte) s.charAt(i); + } + g.drawBytes(data, 0, data.length, ix, iy); + + Font f = g2d.getFont(); + FontRenderContext frc = g2d.getFontRenderContext(); + + iy += LS; + s = "drawString(String s, float x, float y)"; + g2d.drawString(s, (float) ix, (float) iy); + if (!textFont.isTransformed()) { + g.drawLine(ix, iy+1, ix+fm.stringWidth(s), iy+1); + } + + iy += LS; + s = "drawString(AttributedCharacterIterator iterator, "+ + "float x, float y)"; + g2d.drawString(getIterator(s), (float) ix, (float) iy); + + iy += LS; + s = "drawGlyphVector(GlyphVector g, float x, float y)"; + GlyphVector gv = f.createGlyphVector(frc, s); + g2d.drawGlyphVector(gv, ix, iy); + Point2D adv = gv.getGlyphPosition(gv.getNumGlyphs()); + if (!textFont.isTransformed()) { + g.drawLine(ix, iy+1, ix+(int)adv.getX(), iy+1); + } + + iy += LS; + s = "GlyphVector with position adjustments"; + + gv = f.createGlyphVector(frc, s); + int ng = gv.getNumGlyphs(); + adv = gv.getGlyphPosition(ng); + for (int i=0; i