JDK-4167752 : Accessibility support for character bounds broken for HTML table text
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 1.2.0
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: generic,windows_nt
  • CPU: unknown,x86
  • Submitted: 1998-08-20
  • Updated: 1998-10-21
  • Resolved: 1998-10-21
Related Reports
Duplicate :  
Description
JEditorPane.getAccessibleContext().getAccessibleText().getCharacterBounds() doens't return a valid rectangle for text generated by the HTMLEditorKit for HTML tables.  It returns null instead.  This is because JTextComponent.modelToView() for the character index returned in JTextComponent.viewToModel(Point p) for the point under which the table cell is rendered throwing a BadLocationException().

The following code illustrates the bug:

/**
 * Program tests:
 *   bug #4167752 (HTML character bounds broken for HTML table)
 *   bug #4171509 (HTMLEditorKit should support StyleConstants)
 *
 * To test these bugs, create an HTML file with the following content
 * below, and invoke HtmlBug as follows:
 *     java HtmlBug file://bugtest.html
 *
 * bugtest.html:
      <HTML><HEAD><TITLE>bugtest</TITLE></HEAD>
      <BODY>
      Normal, <b>bold,</b> <i>italic,</i> <u>underline</u> <p>
      <TABLE BORDER CELLSPACING=4 CELLPADDING=4 COLS=2 WIDTH="50%">
      <CAPTION>Table caption</CAPTION>
      <TR><TD>r1c1</TD><TD>r1c2</TD></TR>
      </TABLE>
      </BODY></HTML>
 */

import java.awt.*;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.Properties;
import java.io.IOException;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.text.html.*;
import javax.accessibility.*;
import java.io.*;


/**
 * Simple wrapper around JEditorPane to browse html.
 * Useful to browse the javadoc for swing... 
 * java HtmlBug file:/path-to-swing-docs/packages.html
 */
public class HtmlBug implements HyperlinkListener {

    JEditorPane html;

    public HtmlBug(URL u) {
        try {
            Properties props =  System.getProperties(); 
            props.put("http.proxyHost", "webcache1.eng");
            props.put("http.proxyPort", "8080");
            html = new JEditorPane(u);
            html.setBackground(Color.white);
            html.addHyperlinkListener(this);
            html.setEditable(true);
            JScrollPane scroller = new JScrollPane();
            JViewport vp = scroller.getViewport();
            vp.add(html);
            vp.setBackingStoreEnabled(true);


            JFrame f = new JFrame("testing");
            f.getContentPane().setLayout(new BorderLayout());
            f.getContentPane().add("Center", scroller);
            f.pack();
            f.setSize(600, 600);
            f.setVisible(true);

           
        }  catch (IOException ioe) {
            System.err.println("IOException: " + ioe.getMessage());
            System.exit(1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Notification of a change relative to a 
     * hyperlink.
     */
    public void hyperlinkUpdate(HyperlinkEvent e) {
        if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
            if (e instanceof HTMLFrameHyperlinkEvent) {
                HTMLFrameHyperlinkEvent  evt = (HTMLFrameHyperlinkEvent)e;
                HTMLDocument doc = (HTMLDocument)html.getDocument();
                doc.processHTMLFrameHyperlinkEvent(evt);
            } else {
                try {
                    System.err.println("setPage called with: " + e.getURL());
                    html.setPage(e.getURL());
                } catch (IOException ioe) {
                    System.err.println("ioexception: " + ioe);
                }
            }
        }
    }

    /**
     * Walk through the document, dumping the contents via the
     * Java Accessibility API
     */
    public void dumpContentsViaAccessibilityAPI() {
	AccessibleContext ac = html.getAccessibleContext();
	AccessibleText at = ac.getAccessibleText();
	int count = at.getCharCount();
	for (int i = 0; i < count; i++) {
	    dumpCharacterInfo(at, i);
	}
    }

    /**
     * Perform the calls in AccessibleJTextComponent.getCharacterBounds()
     * one at a time, to better illustrate failure
     */
    public void detailedRectLook(int i) {
	System.out.println("Detailed Rect look at #" + i);
	Document model = html.getDocument();
        if (i < 0 || i > model.getLength()-1) {
	    System.out.println("  -> i < 0 || i > model.getLength()-1");
	    return;
        }
	System.out.println("  -> i > 0 && i < model.getLength()-1");
        Rectangle rect;
        try {
            rect = html.modelToView(i);
	    System.out.println("  -> modelToView(" + i + ") succedded!");
	    if (rect != null) {
		System.out.println("  -> rect = " + rect.toString());
	    } else {
		System.out.println("  -> rect = (null)");
	    }
        } catch (BadLocationException e) {
	    System.out.println("  -> modelToView(" + i + ") failed!");
	    System.out.println("  -> received BadLocationException = " + e.toString());
        }
    }

    /**
     *
     */
    public void dumpCharacterInfo(AccessibleText at, int index) {
	System.out.println("Examining character # " + index + ": " + at.getAtIndex(AccessibleText.CHARACTER, index));
	Rectangle r = at.getCharacterBounds(index);
	if (r != null) {
	    System.out.println("  -> rect = " + r.toString());
	} else {
	    System.out.println("  -> rect = (null)");
	}
	String attrString = new String();
	AttributeSet as = at.getCharacterAttribute(index);
        if (StyleConstants.isBold(as)) { attrString += "bold"; }
        if (StyleConstants.isItalic(as)) { attrString += "; italic"; }
        if (StyleConstants.isUnderline(as)) { attrString += "; underline"; }
	System.out.println("  -> attributes: " + attrString);
    }

    public static void main(String[] args) {
        if (args.length != 1) {
            System.err.println("need URL argument");
            System.exit(1);
        }
        try {
            URL u = new URL(args[0]);
            HtmlBug bug = new HtmlBug(u);
	    bug.dumpContentsViaAccessibilityAPI();
	    bug.detailedRectLook(52);	// char offset 52 is in a table
        } catch (MalformedURLException e) {
            System.err.println("Bad url");
            System.exit(1);
        }
    }
}

Comments
EVALUATION This could be a problem with the caller (the accessibility api implementation) not calling with the proper bias (introduced for bidi support) or it might be a problem with multi-column/multi-row cells. In either case, a solution can be plugged in independant of the JDK release so this isn't a show stop to the release process. This does however have high priority attention in the lineup of bugs to fix. timothy.prinzing@eng 1998-09-22 This needs an example. The modelToView method is used to render the selections, which works. I need a specific example of a failure to proceed. timothy.prinzing@eng 1998-09-30 The table has a caption which is not currently represented in view space, it is also a known bug. timothy.prinzing@eng 1998-10-20
30-09-1998