JDK-8021786 : WPathGraphics.drawString renders some glyphs on top of each other
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 7u25
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_7
  • Submitted: 2013-07-26
  • Updated: 2013-07-29
  • Resolved: 2013-07-29
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8
8Resolved
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version  " 1.7.0_25 " 
Java(TM) SE Runtime Environment (build 1.7.0_25-b17)
Java HotSpot(TM) 64-Bit Server VM (build 23.25-b01, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]

A DESCRIPTION OF THE PROBLEM :
Print the string  " foo \u2013 bar "  (\u2013 is the unicode character for the NDASH = – in html text) on Windows with a PrinterJob. Then the string is splitted into three parts:  " foo  " , the n-dash, and  "  bar " , which are rendered on top of each other.
The problem occurs always, when the used font is an instance of sunfont.CompositeFont and the contained characters are mapped to different physical fonts. Comment from WPathGraphics.drawString(String str, float x, float y, Font font, FontRenderContext frc, float targetW):
            /* Composite fonts are made up of multiple fonts and each
             * substring that uses a particular component font needs to
             * be separately sent to GDI.
             * This works for standard composite fonts, alternate ones,
             * Fonts that are a physical font backed by a standard composite,
             * and with fallback fonts.
             */

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
In a MS Windows environment construct a PrinterJob and print the text  " foo \u2013 bar "  with graphics.drawString( " foo \u2013 bar " , 50, 50). Or simply construct a JTextComponent with  " foo \u2013 bar "  as text and call print() on the component.


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The output should be  " foo - bar " .
ACTUAL -
 " foo " ,  " - " , and  " bar "  are printed on top of each other.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.awt.Font;
import java.awt.Graphics;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;

public class WPathGraphicsBug {

public WPathGraphicsBug() throws PrinterException {
PrinterJob printerJob = PrinterJob.getPrinterJob();
if (! " sun.awt.windows.WPrinterJob " .equals(printerJob.getClass().getName())) {
System.out.println( " This bug occurs only in Windows. " );
return;
}
printerJob.setPrintable(new Printable() {
@Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
if (pageIndex > 0) return NO_SUCH_PAGE;
// U+2013 is the unicode character NDASH = – in html text
String text =  " foo \u2013 bar " ;
Font font = new Font(Font.SANS_SERIF, Font.PLAIN, 12);
graphics.setFont(font);
graphics.drawString(text, 50, 50);
// font is a instanceof sun.font.CompositeFont
// The NDASH character is based on an other PhysicalFont than the common letters.
// The drawString implementation of sun.awt.windows.WPathGraphics splits the
// text into three parts  " foo  " ,  " \u2013 " , and  "  bar "  and calls the
// textOut method for each of them.
// It intends to advance the horizontal position for each chunk, but
// unfortunately this doesn't work:
//
// code snippet from WPathGraphics.drawString(String str, float x, float y,
//                                Font font, FontRenderContext frc, float targetW)
// ...
///* Composite fonts are made up of multiple fonts and each
// * substring that uses a particular component font needs to
// * be separately sent to GDI.
// * This works for standard composite fonts, alternate ones,
// * Fonts that are a physical font backed by a standard composite,
// * and with fallback fonts.
// */
//CompositeFont compFont = (CompositeFont)font2D;
//float userx = x, usery = y;
//float devx = devpos.x, devy = devpos.y;
//char[] chars = str.toCharArray();
//int len = chars.length;
//int[] glyphs = new int[len];
//compFont.getMapper().charsToGlyphs(len, chars, glyphs);
//
//int startChar = 0, endChar = 0, slot = 0;
//while (endChar < len) {
//
//    startChar = endChar;
//    slot = glyphs[startChar] >>> 24;
//
//    while (endChar < len && ((glyphs[endChar] >>> 24) == slot)) {
//        endChar++;
//    }
//    String substr = new String(chars, startChar,endChar-startChar);
//    PhysicalFont slotFont = compFont.getSlotFont(slot);
//    textOut(substr, font, slotFont, frc,
//            scaledFontSizeY, iangle, awScale,
//            deviceTransform, scaleFactorX,
//            userx, usery, devx, devy, 0f);
//    Rectangle2D bds = font.getStringBounds(substr, frc);
//    float xAdvance = (float)bds.getWidth();
//    userx += xAdvance;
//    userpos.x += xAdvance;
//    deviceTransform.transform(userpos, devpos);
//}
//
// The xAdvance is not added to devx, which remains unchanged at the starting position.
// The deviceTransform.transform(userpos, devpos) call updates devpos, which is unused.
// textOut gets userx and devx as arguments, but if okGDIMetrics(...) returns true,
// devx is forwarded to the printerJob.textOut call.
// Proposed solution: devx and devy shouldn't be used.
// Use devpos.x and devpos.y as arguments in the textOut call.
return PAGE_EXISTS;
}
});
if (printerJob.printDialog()) {
printerJob.print();
}
}

public static void main(String[] args) throws PrinterException {
new WPathGraphicsBug();
}
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Set the public static property RasterPrinterJob.shapeTextProp to true.