JDK-6446474 : Handle ZWSP as described in Unicode 4.1
  • Type: Enhancement
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 6
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: linux
  • CPU: x86
  • Submitted: 2006-07-05
  • Updated: 2025-08-07
  • Resolved: 2025-08-07
Related Reports
Duplicate :  
Relates :  
Relates :  
Description
A DESCRIPTION OF THE REQUEST :
Currently (Mustang builds < 90) ZWSP handling relies on individual fonts for presentation of ZWSP. This frequently results in the presentation of either a non-zero-width space or an undefined character.

JUSTIFICATION :
In The Unicode Standard 4.1, General Punctuation 2000 - 206F, ZWSP is described as:
"this character is intended for line break control; it has no width, but its presence between two characters does no prevent increased letter spacing in justification."

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
ZWSP is undetectable in presentation, except in the case where a line breaks after the ZWSP.
ACTUAL -
As in description.

---------- BEGIN SOURCE ----------
import javax.swing.*;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextLayout;
import java.util.Hashtable;
import java.util.Locale;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import java.text.BreakIterator;
import java.awt.font.TextAttribute;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

/**
 * This class demonstrates how to line-break and draw a paragraph
 * of text using LineBreakMeasurer and TextLayout.
 *
 * This class constructs a LineBreakMeasurer from an
 * AttributedCharacterIterator.  It uses the LineBreakMeasurer
 * to create and draw TextLayouts (lines of text) which fit within
 * the Component's width.
 */

public class LineBreakSample3 extends JApplet {
    
    // The LineBreakMeasurer used to line-break the paragraph.
    private LineBreakMeasurer lineMeasurer;
    
    // The index in the LineBreakMeasurer of the first character
    // in the paragraph.
    private int paragraphStart;
    
    // The index in the LineBreakMeasurer of the first character
    // after the end of the paragraph.
    private int paragraphEnd;
    
    private static Font font = new Font("Luxi Serif Regular", Font.PLAIN, 1);
    static {
	font = font.deriveFont(18.0f);
    }

    private static final Hashtable map = new Hashtable();
    static {
	map.put(TextAttribute.FONT, font);
        map.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_LOW_ONE_PIXEL);
        map.put(TextAttribute.FOREGROUND, Color.red);
        map.put(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);
        map.put(TextAttribute.SIZE, new Float(18.0));
    }
    
    private static String text =
        "Many people believe that Vin\u200Bcent van Gogh painted his best works " +
        "during the two-year period he spent in Provence. Here is where he " +
        "painted \"The Starry Night\"--which some consider to be his greatest " +
        "work of all. However, as his artistic bril\u00ADliance reached new heights " +
        "in Provence, Vincent's physical and mental health plummeted.  " +
        "The Jones' new car is blue.";
    private static AttributedString vanGogh = new AttributedString(
            text, map);
    
    public void buildUI(Container container){
        LineBreakPanel lineBreakPanel = new LineBreakPanel();
        container.add(lineBreakPanel, BorderLayout.CENTER);
    }
    
    class LineBreakPanel extends JPanel {
        
        public LineBreakPanel() {
            //setFont(Font.decode("utopia-plain-14"));
            AttributedCharacterIterator paragraph = vanGogh.getIterator();
            paragraphStart = paragraph.getBeginIndex();
            paragraphEnd = paragraph.getEndIndex();
            
            // Create a new LineBreakMeasurer from the paragraph.
            lineMeasurer = new LineBreakMeasurer(paragraph,
                    new FontRenderContext(null, true, true));
        }
        
        public void paintComponent(Graphics g) {
            
            super.paintComponent(g);
            setBackground(Color.white);
            
            Graphics2D graphics2D = (Graphics2D) g;
            
            // Set formatting width to width of Component.
            Dimension size = getSize();
            float formatWidth = (float) size.width;
            
            float drawPosY = 0;
            
            lineMeasurer.setPosition(paragraphStart);
            
            // Get lines from lineMeasurer until the entire
            // paragraph has been displayed.
            while (lineMeasurer.getPosition() < paragraphEnd) {
                
                // Retrieve next layout.
                TextLayout layout = lineMeasurer.nextLayout(formatWidth);
                // Move y-coordinate by the ascent of the layout.
                drawPosY += layout.getAscent();
                
                // Compute pen x position.  If the paragraph is
                // right-to-left, we want to align the TextLayouts
                // to the right edge of the panel.
                float drawPosX;
                if (layout.isLeftToRight()) {
                    drawPosX = 0;
                }
                else {
                    drawPosX = formatWidth - layout.getAdvance();
                }
                
                // Draw the TextLayout at (drawPosX, drawPosY).
                layout.draw(graphics2D, drawPosX, drawPosY);
                
                // Move y-coordinate in preparation for next layout.
                drawPosY += layout.getDescent() + layout.getLeading();
            }
            
        }
    }
    
    public static void main(String[] args) {
        
        JFrame f = new JFrame("HitTestSample");
        
        f.addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        
        LineBreakSample3 controller = new LineBreakSample3();
        controller.buildUI(f.getContentPane());
        f.setSize(new Dimension(400, 250));
        f.setVisible(true);
    }
    
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Remove ZWSP from the text before presentation.

Comments
Closing as duplicate of JDK-8208377.
07-08-2025

The correct process is to mark it as a duplicate of the fixed bug. Do not mark this one fixed or mention a fix release.
07-08-2025

[~prr] I think this one was fixed as a side effect of JDK-8208377 and JDK-8270265 (both fixed in 25), and I don't see any issues in my local manual testing. Am I OK to mark this one fixed in 25?
06-08-2025

Great, thanks for the quick reply :-)
06-08-2025

[~dgredler] well that’s a blast from the distant past.. but yeah, just a Quick Look at the linkedissue, if that is fixed it should fix this one so go ahead and close.
06-08-2025

[~srl] I think this is working as expected after JDK-8208377 and JDK-8270265. Can we close it?
06-08-2025