JDK-8257664 : HTMLEditorKit: Wrong CSS relative font sizes
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 8u271,9,11,15
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_10
  • CPU: x86_64
  • Submitted: 2020-11-25
  • Updated: 2021-02-12
  • Resolved: 2021-01-20
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 17
17 b06Fixed
Related Reports
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
java version "15.0.1" 2020-10-20
Java(TM) SE Runtime Environment (build 15.0.1+9-18)
Java HotSpot(TM) 64-Bit Server VM (build 15.0.1+9-18, mixed mode, sharing)


A DESCRIPTION OF THE PROBLEM :
CSS relative font sizes such as "font-size: 120%" produce wrong results when specified on HTML block-level elements:

<style>
  h3, .h3 { font-size: 120% }
</style>

<h3>120%</h3><!-- The displayed font-size ends up: 1.2 * 1.2 -->

<div class="h3">120%</div><!-- The displayed font-size ends up: 1.2 * 1.2 * 1.2 -->

<h3 style="font-size: 100%"><span class="h3">120%</span></h3><!-- Produces expected result -->


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the executable test case and observe the editor's content.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
All, except for the non-standard (purple) workaround "xxx%" text tokens should have same matching size.
ACTUAL -
Red "xxx%" text tokens are displayed with wildly different sizes.

---------- BEGIN SOURCE ----------
package net.example.swing;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.io.IOException;
import javax.swing.AbstractAction;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.text.Document;

@SuppressWarnings("serial")
public class RelativeFontSizeTest extends JFrame {

    private JEditorPane editor;

    public RelativeFontSizeTest() {
        super("Relative Font Size Test");

        Container contentPane = super.getContentPane();
        editor = new JEditorPane();
        editor.setEditable(false);
        editor.putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, true);
        editor.putClientProperty(JEditorPane.W3C_LENGTH_UNITS, false);
        contentPane.add(new JScrollPane(editor), BorderLayout.CENTER);

        editor.getActionMap().put("Reload-Page", new AbstractAction("Reload-Page") {
            @Override public void actionPerformed(ActionEvent evt) { loadPage(); }
        });
        editor.getActionMap().put("Debug-Page", new AbstractAction("Debug-Page") {
            @Override public void actionPerformed(ActionEvent evt) { debugPage(); }
        });

        KeyStroke ctrlR = KeyStroke.getKeyStroke(KeyEvent.VK_R, KeyEvent.CTRL_DOWN_MASK);
        editor.getInputMap().put(ctrlR, "Reload-Page");

        KeyStroke ctrlShiftD = KeyStroke
                .getKeyStroke(KeyEvent.VK_D, KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK);
        editor.getInputMap().put(ctrlShiftD, "Debug-Page");

        loadPage();
    }

    void loadPage() {
        try {
            editor.setContentType("text/html");
            editor.getDocument().putProperty(Document.StreamDescriptionProperty, null); // Reload
            editor.setPage(RelativeFontSizeTest.class.getResource("relative-font-size-test.html"));
        } catch (IOException e) {
            editor.setContentType("text/plain");
            editor.setText(e.toString());
            editor.setForeground(Color.red);
        }
    }

    void debugPage() {
        System.out.println(editor.getDocument().getDefaultRootElement());
        System.out.println(editor.getUI().getRootView(editor));
    }

    public static void main(String[] args) throws Exception {
        SwingUtilities.invokeLater(() -> {
            RelativeFontSizeTest frame = new RelativeFontSizeTest();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(500, 800);
            frame.setLocation(0, 0);
            frame.setVisible(true);
        });
    }

}

--"relative-font-size-test.html"
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Relative Font-size Test</title>
  <style>
    body { font-size: 12px; font-size: 12;
           line-height: 1.3; font-family: sans-serif;
           margin-left: 7; margin-right: 7 }
    .inherit { font-size: 100% }
    h1, .h1, h1.h0 span { font-size: 160% }
    h2, .h2, h2.h0 span { font-size: 140% }
    h3, .h3, h3.h0 span { font-size: 120% }
    h4, .h4, h4.h0 span { font-size: 100% }
    p,  .h0  { font-size: 100% }
    h1, h2, h3, h4, ol, ul, p,
    .h1, .h2, .h3, .h4 { font-weight: bold;
                         margin-top: 0;
                         margin-bottom: 0;
                         /*border: 1px dashed silver*/ }
    .bug { color: red }
    .workaround { color: #1E90FF }
    .workaround-2 { color: purple }
    .ok { color: black }
    hr { margin-top: 10px; margin-bottom: 10px;
         margin-top: 10; margin-bottom: 10 }
  </style>
</head>
<body>

<ul>
<li class="bug">Bug</li>
<li class="workaround">Workaround</li>
<li class="workaround-2">Workaround (non-standard)</li>
<li>Reference / <span class="ok">Correct</span></li>
</ul>

<hr width="60%" size="1" noshade style="margin: 10">

<div>
  <span class="h4">100%</span>
  <span class="h3">120%</span>
  <span class="h2">140%</span>
  <span class="h1">160%</span>
</div>

<h4>100%</h4>
<h3 class="bug">120%</h3>
<h2 class="bug">140%</h2>
<h1 class="bug">160%</h1>

<!-- base-size(12) * x, Use absolute size -->
<h4 style="font-size: 12" class="workaround">100%</h4>
<h3 style="font-size: 14" class="workaround">120%</h3>
<h2 style="font-size: 17" class="workaround">140%</h2>
<h1 style="font-size: 19" class="workaround">160%</h1>

<!-- Error: base-size(12) * x * x, Use sqrt(x)-->
<h4 style="font-size: 100%">100%</h4>
<h3 style="font-size: 109.54%" class="workaround-2">120%</h3>
<h2 style="font-size: 118.32%" class="workaround-2">140%</h2>
<h1 style="font-size: 126.49%" class="workaround-2">160%</h1>

<h4><span class="inherit">100%</span></h4>
<h3 class="workaround"><span class="inherit">120%</span></h3>
<h2 class="workaround"><span class="inherit">140%</span></h2>
<h1 class="workaround"><span class="inherit">160%</span></h1>

<h4 class="h0"><span>100%</span></h4>
<h3 class="h0"><span>120%</span></h3>
<h2 class="h0"><span>140%</span></h2>
<h1 class="h0"><span>160%</span></h1>

<hr width="60%" size="1" noshade style="margin: 10">

<div>
  <span class="h4">100%</span>
  <span class="h3">120%</span>
  <span class="h2">140%</span>
  <span class="h1">160%</span>
</div>

<div            ><div class="h4">100%</div></div>
<div class="bug"><div class="h3">120%</div></div>
<div class="bug"><div class="h2">140%</div></div>
<div class="bug"><div class="h1">160%</div></div>

<!-- Error: base-size(12) * 1.6 * 1.6 * 1.6, Use cuberoot(x) -->
<div style="font-size: 100%; font-weight: bold"                       >100%</div>
<div style="font-size: 106.266%; font-weight: bold" class="workaround-2">120%</div>
<div style="font-size: 111.869%; font-weight: bold" class="workaround-2">140%</div>
<div style="font-size: 116.96%; font-weight: bold"  class="workaround-2">160%</div>

<div            ><div class="h4"><span class="inherit">100%</span></div></div>
<div class="bug"><div class="h3"><span class="inherit">120%</span></div></div>
<div class="bug"><div class="h2"><span class="inherit">140%</span></div></div>
<div class="bug"><div class="h1"><span class="inherit">160%</span></div></div>

<div                   ><div class="h4"><div class="inherit">100%</div></div></div>
<div class="workaround"><div class="h3"><div class="inherit">120%</div></div></div>
<div class="workaround"><div class="h2"><div class="inherit">140%</div></div></div>
<div class="workaround"><div class="h1"><div class="inherit">160%</div></div></div>

<hr width="60%" size="1" noshade style="margin: 10">

<div>
  <span class="h4">100%</span>
  <span class="h3">120%</span>
  <span class="h2">140%</span>
  <span class="h1">160%</span>
</div>

<ol class="bug">
<li class="h4"><span class="ok">100%</span></li>
<li class="h3">120%</li>
<li class="h2">140%</li>
<li class="h1">160%</li>
</ol>

<ol class="bug">
<li class="h4"><span class="inherit"><span class="ok">100%</span></span></li>
<li class="h3"><span class="inherit">120%</span></li>
<li class="h2"><span class="inherit">140%</span></li>
<li class="h1"><span class="inherit">160%</span></li>
</ol>

<ol class="h4"          ><li            >100%</li></ol>
<ol class="h3" start="2"><li class="bug">120%</li></ol>
<ol class="h2" start="3"><li class="bug">140%</li></ol>
<ol class="h1" start="4"><li class="bug">160%</li></ol>

<div class="workaround">
<ol class="h4"          ><li class="inherit"><span class="ok">100%</span></li></ol>
<ol class="h3" start="2"><li class="inherit">120%</li></ol>
<ol class="h2" start="3"><li class="inherit">140%</li></ol>
<ol class="h1" start="4"><li class="inherit">160%</li></ol>
</div>

<hr width="60%" size="1" noshade style="margin: 10">

<div>
  <span class="h4">100%</span>
  <span class="h3">120%</span>
  <span class="h2">140%</span>
  <span class="h1">160%</span>
</div>

<div class="h4">
  <ol><li>100%</li></ol>
</div>
<div class="h3">
  <ol start="2" class="bug"><li>120%</li></ol>
</div>
<div class="h2">
  <ol start="3" class="bug"><li>140%</li></ol>
</div>
<div class="h1">
  <ol start="4" class="bug"><li>160%</li></ol>
</div>

<div class="h4">
  <ol class="inherit"><li>100%</li></ol>
</div>
<div class="h3">
  <ol start="2" class="inherit"><li class="workaround">120%</li></ol>
</div>
<div class="h2">
  <ol start="3" class="inherit"><li class="workaround">140%</li></ol>
</div>
<div class="h1">
  <ol start="4" class="inherit"><li class="workaround">160%</li></ol>
</div>

<div class="h4">
  <ol><li class="inherit">100%</li></ol>
</div>
<div class="h3">
  <ol start="2" class="bug"><li class="inherit">120%</li></ol>
</div>
<div class="h2">
  <ol start="3" class="bug"><li class="inherit">140%</li></ol>
</div>
<div class="h1">
  <ol start="4" class="bug"><li class="inherit">160%</li></ol>
</div>

</body>
</html>

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

FREQUENCY : always



Comments
The fix to this bug uncovered another bug in font size inheritance, see JDK-8260687. It proved to be not a regression. When backporting this fix, consider backporting JDK-8260687 too.
12-02-2021

Changeset: 70b5b311 Author: Stanimir Stamenkov <stanio@yahoo.com> Committer: Alexey Ivanov <aivanov@openjdk.org> Date: 2021-01-20 13:34:52 +0000 URL: https://git.openjdk.java.net/jdk/commit/70b5b311
20-01-2021

Additional Information from submitter: =========================== Here's a more test document: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Relative Font-size Test (Minimal)</title> <style> body { font-weight: bold } h1, .h1 { font-size: 1.6em } h2, .h2 { font-size: 1.4em } h3, .h3 { font-size: 1.2em } h4, .h4 { font-size: 1.0em } </style> </head> <body> <div> <span class="h4">100%</span> <span class="h3">120%</span> <span class="h2">140%</span> <span class="h1">160%</span> </div> <h4>100%</h4> <h3>120%</h3> <h2>140%</h2> <h1>160%</h1> <div class="h4">100%</div> <div class="h3">120%</div> <div class="h2">140%</div> <div class="h1">160%</div> </body> </html> I've traced the problem down to javax.swing.text.html.CSS.FontSize.getValue(AttributeSet, StyleSheet). There's a logic to resolve the inherited value from the parent, but then the inerited value should be the parent's computed value [1]: > The final value of a property is the result of a four-step calculation: the value is determined through specification (the "specified value"), then resolved into a value that is used for inheritance (the "computed value"), then converted into an absolute value if necessary (the "used value"), and finally transformed according to the limitations of the local environment (the "actual value"). The computed value for the font-size property should be converted to an absolute size: https://www.w3.org/TR/CSS2/fonts.html#propdef-font-size font-size is probably the only such exception. For other inherited CSS properties the computed value generally matches the specified value. [1] https://www.w3.org/TR/CSS2/cascade.html Related to my previous finding (CSS.FontSize.getValue()) I've been able to create a workaround (have not tested it for performance): <PLEASE><SEE><IN><THE><ATTACHMENT><HTMLEditorKitWorkaround.java>
08-12-2020

On Windows 10, the HTML font size appears larger as appears outside HTML. The issue is reproducible frequently in JDK 8u271, 11.0.9 and 16 ea b26. Test Result (Windows 10): ====================== 8u271: Fail 11.0.9: Fail 11 : Fail 15.0.1: Fail 16ea : Fail To verify, run the attached test case with respective JDK versions. Also see attached screenshot as reference.
03-12-2020