mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2025-12-06 22:10:54 +03:00
HTML: fixed wrong rendering if HTML text contains <style> tag with attributes (e.g. <style type='text/css'>) (issue #905; regression in 3.5)
Some checks failed
CI / build (11, ) (push) Has been cancelled
CI / build (17, ) (push) Has been cancelled
CI / build (21, ) (push) Has been cancelled
CI / build (23, ) (push) Has been cancelled
CI / build (8, ) (push) Has been cancelled
CI / snapshot (push) Has been cancelled
CI / release (push) Has been cancelled
Some checks failed
CI / build (11, ) (push) Has been cancelled
CI / build (17, ) (push) Has been cancelled
CI / build (21, ) (push) Has been cancelled
CI / build (23, ) (push) Has been cancelled
CI / build (8, ) (push) Has been cancelled
CI / snapshot (push) Has been cancelled
CI / release (push) Has been cancelled
This commit is contained in:
@@ -1,6 +1,14 @@
|
|||||||
FlatLaf Change Log
|
FlatLaf Change Log
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
## 3.5.3-SNAPSHOT
|
||||||
|
|
||||||
|
#### Fixed bugs
|
||||||
|
|
||||||
|
- HTML: Fixed wrong rendering if HTML text contains `<style>` tag with
|
||||||
|
attributes (e.g. `<style type='text/css'>`). (issue #905; regression in 3.5.1)
|
||||||
|
|
||||||
|
|
||||||
## 3.5.2
|
## 3.5.2
|
||||||
|
|
||||||
#### Fixed bugs
|
#### Fixed bugs
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ import java.beans.PropertyChangeEvent;
|
|||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
import javax.swing.AbstractButton;
|
import javax.swing.AbstractButton;
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
@@ -93,7 +93,7 @@ debug*/
|
|||||||
text = ((JToolTip)c).getTipText();
|
text = ((JToolTip)c).getTipText();
|
||||||
else
|
else
|
||||||
return;
|
return;
|
||||||
if( text == null )
|
if( text == null || !BasicHTML.isHTMLString( text ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// BASE_SIZE rule is parsed in javax.swing.text.html.StyleSheet.addRule()
|
// BASE_SIZE rule is parsed in javax.swing.text.html.StyleSheet.addRule()
|
||||||
@@ -101,15 +101,14 @@ debug*/
|
|||||||
String openTag = "";
|
String openTag = "";
|
||||||
String closeTag = "";
|
String closeTag = "";
|
||||||
|
|
||||||
String lowerText = text.toLowerCase( Locale.ENGLISH );
|
|
||||||
int headIndex;
|
int headIndex;
|
||||||
int styleIndex;
|
int styleIndex;
|
||||||
|
|
||||||
int insertIndex;
|
int insertIndex;
|
||||||
if( (headIndex = lowerText.indexOf( "<head>" )) >= 0 ) {
|
if( (headIndex = indexOfTag( text, "head", true )) >= 0 ) {
|
||||||
// there is a <head> tag --> insert after <head> tag
|
// there is a <head> tag --> insert after <head> tag
|
||||||
insertIndex = headIndex + "<head>".length();
|
insertIndex = headIndex;
|
||||||
} else if( (styleIndex = lowerText.indexOf( "<style>" )) >= 0 ) {
|
} else if( (styleIndex = indexOfTag( text, "style", false )) >= 0 ) {
|
||||||
// there is a <style> tag --> insert before <style> tag
|
// there is a <style> tag --> insert before <style> tag
|
||||||
insertIndex = styleIndex;
|
insertIndex = styleIndex;
|
||||||
} else {
|
} else {
|
||||||
@@ -124,6 +123,41 @@ debug*/
|
|||||||
+ text.substring( insertIndex );
|
+ text.substring( insertIndex );
|
||||||
|
|
||||||
BasicHTML.updateRenderer( c, newText );
|
BasicHTML.updateRenderer( c, newText );
|
||||||
|
|
||||||
|
// for unit tests
|
||||||
|
if( testUpdateRenderer != null )
|
||||||
|
testUpdateRenderer.accept( c, newText );
|
||||||
|
}
|
||||||
|
|
||||||
|
// for unit tests
|
||||||
|
static BiConsumer<JComponent, String> testUpdateRenderer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns start or end index of a HTML tag.
|
||||||
|
* Checks only for leading '<' character and (case-ignore) tag name.
|
||||||
|
*/
|
||||||
|
private static int indexOfTag( String html, String tag, boolean endIndex ) {
|
||||||
|
int tagLength = tag.length();
|
||||||
|
int maxLength = html.length() - tagLength - 2;
|
||||||
|
char lastTagChar = tag.charAt( tagLength - 1 );
|
||||||
|
|
||||||
|
for( int i = "<html>".length(); i < maxLength; i++ ) {
|
||||||
|
// check for leading '<' and last tag name character
|
||||||
|
if( html.charAt( i ) == '<' && Character.toLowerCase( html.charAt( i + tagLength ) ) == lastTagChar ) {
|
||||||
|
// compare tag characters from last to first
|
||||||
|
for( int j = tagLength - 2; j >= 0; j-- ) {
|
||||||
|
if( Character.toLowerCase( html.charAt( i + 1 + j ) ) != tag.charAt( j ) )
|
||||||
|
break; // not equal
|
||||||
|
|
||||||
|
if( j == 0 ) {
|
||||||
|
// tag found
|
||||||
|
return endIndex ? html.indexOf( '>', i + tagLength ) + 1 : i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Set<String> absoluteSizeKeywordsSet = new HashSet<>( Arrays.asList(
|
private static final Set<String> absoluteSizeKeywordsSet = new HashSet<>( Arrays.asList(
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 FormDev Software GmbH
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.formdev.flatlaf.ui;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import java.util.Locale;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.plaf.basic.BasicHTML;
|
||||||
|
import javax.swing.text.BadLocationException;
|
||||||
|
import javax.swing.text.Document;
|
||||||
|
import javax.swing.text.View;
|
||||||
|
import org.junit.jupiter.api.AfterAll;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karl Tauber
|
||||||
|
*/
|
||||||
|
public class TestFlatHTML
|
||||||
|
{
|
||||||
|
private final String body = "some <small>small</small> text";
|
||||||
|
private final String bodyInBody = "<body>" + body + "</body>";
|
||||||
|
private final String bodyPlain = "some small text";
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void setup() {
|
||||||
|
TestUtils.setup( false );
|
||||||
|
TestUtils.scaleFont( 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
static void cleanup() {
|
||||||
|
TestUtils.cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void simple() {
|
||||||
|
testHtmlBaseSize( "<html>${BASE_SIZE_IN_HEAD}" + body + "</html>", bodyPlain );
|
||||||
|
testHtmlBaseSize( "<html>${BASE_SIZE_IN_HEAD}" + bodyInBody + "</html>", bodyPlain );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void htmlWithHeadTag() {
|
||||||
|
testHtmlBaseSize( "<html><head>${BASE_SIZE}<title>test</title><head>" + body + "</html>", bodyPlain );
|
||||||
|
testHtmlBaseSize( "<html><head>${BASE_SIZE}<title>test</title><head>" + bodyInBody + "</html>", bodyPlain );
|
||||||
|
|
||||||
|
testHtmlBaseSize( "<html><head id=\"abc\">${BASE_SIZE}<title>test</title><head>" + body + "</html>", bodyPlain );
|
||||||
|
testHtmlBaseSize( "<html><head id=\"abc\">${BASE_SIZE}<title>test</title><head>" + bodyInBody + "</html>", bodyPlain );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void htmlWithStyleTag() {
|
||||||
|
testHtmlBaseSize( "<html>${BASE_SIZE}<style>body { color: #f00; }</style>" + bodyInBody + "</html>", bodyPlain );
|
||||||
|
testHtmlBaseSize( "<html>${BASE_SIZE}<style>body { color: #f00; }</style><h1>header1</h1>" + body + "</html>", "header1\n" + bodyPlain );
|
||||||
|
|
||||||
|
testHtmlBaseSize( "<html>${BASE_SIZE}<style type='text/css'>body { color: #f00; }</style>" + bodyInBody + "</html>", bodyPlain );
|
||||||
|
testHtmlBaseSize( "<html>${BASE_SIZE}<style type='text/css'>body { color: #f00; }</style><h1>header1</h1>" + body + "</html>", "header1\n" + bodyPlain );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testHtmlBaseSize( String html, String expectedPlain ) {
|
||||||
|
testHtmlBaseSizeImpl( html, expectedPlain );
|
||||||
|
testHtmlBaseSizeImpl( html.toUpperCase( Locale.ENGLISH ), expectedPlain.toUpperCase( Locale.ENGLISH ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testHtmlBaseSizeImpl( String html, String expectedPlain ) {
|
||||||
|
String baseSize = "<style>BASE_SIZE " + UIManager.getFont( "Label.font" ).getSize() + "</style>";
|
||||||
|
String baseSizeInHead = "<head>" + baseSize + "</head>";
|
||||||
|
|
||||||
|
String expectedHtml = html.replace( "${BASE_SIZE}", baseSize ).replace( "${BASE_SIZE_IN_HEAD}", baseSizeInHead );
|
||||||
|
html = html.replace( "${BASE_SIZE}", "" ).replace( "${BASE_SIZE_IN_HEAD}", "" );
|
||||||
|
|
||||||
|
testHtml( html, expectedHtml, expectedPlain );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testHtml( String html, String expectedHtml, String expectedPlain ) {
|
||||||
|
FlatHTML.testUpdateRenderer = (c, newHtml) -> {
|
||||||
|
assertEquals( expectedHtml, newHtml );
|
||||||
|
assertEquals( expectedPlain, getPlainText( c ) );
|
||||||
|
};
|
||||||
|
new JLabel( html );
|
||||||
|
FlatHTML.testUpdateRenderer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPlainText( JComponent c ) {
|
||||||
|
View view = (View) c.getClientProperty( BasicHTML.propertyKey );
|
||||||
|
if( view == null )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Document doc = view.getDocument();
|
||||||
|
try {
|
||||||
|
return doc.getText( 0, doc.getLength() ).trim();
|
||||||
|
} catch( BadLocationException ex ) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
flatlaf.releaseVersion = 3.5.2
|
flatlaf.releaseVersion = 3.5.2
|
||||||
flatlaf.developmentVersion = 3.6-SNAPSHOT
|
flatlaf.developmentVersion = 3.5.3-SNAPSHOT
|
||||||
|
|
||||||
org.gradle.parallel = true
|
org.gradle.parallel = true
|
||||||
# org.gradle.warning.mode = all
|
# org.gradle.warning.mode = all
|
||||||
|
|||||||
Reference in New Issue
Block a user