-
Bug
-
Resolution: Unresolved
-
P4
-
8, 9, 10
-
generic
-
generic
FULL PRODUCT VERSION :
java version "9.0.1"
Java(TM) SE Runtime Environment (build 9.0.1+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.1+11, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 10.0.16299.125]
A DESCRIPTION OF THE PROBLEM :
For a wrapped Textflow of several lines,containing Text words of different font sizes,it appears the baseline offset of any word is based on the words in line 1 of TextFlow, even though the following lines have different heights.
Even though Text nodes were used in the test case, this problem will occur for any type of node where the node heights vary within the TextFlow.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
run the test case
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Expected different baseline offset for each node of wrapped TextFlow, depending on the line height where the node is located.
ACTUAL -
Same baseline offset for any node on any line of the wrapped TextFlow.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
//
package tools;
//
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.Node;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.scene.layout.VBox;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.paint.Color;
import javafx.collections.ObservableMap;
import javafx.event.EventHandler;
//
//
public class TextBase extends Application {
/*
This app tests baseline offsets of Text words within a multiline
TextFlow, in which a few words have bigger font sizes. In all cases
it appears the baseline offset is based on the words in line 1 of
TextFlow, even though the following lines have different heights.
Is this a bug in TextFlow?
*/
private static final Logger LOG = Logger.getLogger(TextBase.class.getName());
//
//init fonts
private Font mainFont = Font.font("DejaVu Serif", FontWeight.NORMAL, 20);
private Font bigFont = Font.font("DejaVu Serif", FontWeight.NORMAL, 48);
//
//init panes
private BorderPane bp = new BorderPane();
private StackPane sp = new StackPane();
private VBox vbox = new VBox();
private TextFlow flow = new TextFlow();
//
//init sample string of words
private String s1 = "Electromagnetic waves cover a wide spectrum from radio waves to gamma rays. Waves can interfere. "
+ "Constructive interference occurs when two waves are in phase. Destructive interference occurs when two waves are "
+ "out of phase. When waves pass through a diffraction grating, they interfere and produce a pattern of bright and "
+ "dark spots on a screen.";
//
//comment out one of these bigIndexList declarations
//first big font word in line 2
//private int[] bigIndexList = {10, 17};
//
//first big font word in line 1
private int[] bigIndexList = {1, 10, 17};
/**
* Default Constructor
*/
public TextBase() {
}
public void test(){
try{
init();
addText(flow, s1);
vbox.getChildren().add(flow);
} catch (Exception ex) {
LOG.log(Level.SEVERE, ex.getMessage(), ex);
throw new RuntimeException(ex);
}
}
public void init(){
try{
//insets: top, right, bottom, left
bp.setPadding(new Insets(5, 5, 5, 5));
bp.setStyle(" -fx-border-color: green;-fx-border-width: 2px;-fx-border-style: solid;");
bp.setPrefSize(500,400);
//
sp.setPadding(new Insets(10, 5, 10, 5));
sp.setStyle(" -fx-border-color: blue;-fx-border-width: 1px;-fx-border-style: solid;");
//
vbox.setSpacing(8);
vbox.setPadding(new Insets(15, 5, 15, 5));
vbox.setStyle(" -fx-border-color: red;-fx-border-width: 1px;-fx-border-style: solid;");
//
} catch (Exception ex) {
LOG.log(Level.SEVERE, ex.getMessage(), ex);
throw new RuntimeException(ex);
}
}
public void addText(TextFlow flow, String s){
try{
LOG.log(Level.INFO, "\n\nText s: " + s);
//split string into words
String[] ss = s.split(" ");
int len = ss.length;
int bigIndex = 0;
int bigEnd = bigIndexList.length;
for(int i = 0; i<len; i++){
Text tx = new Text(ss[i] + " ");
if(bigIndex < bigEnd && i == bigIndexList[bigIndex]){
//set big font on a few words
tx.setFont(bigFont);
bigIndex++;
} else {
//set main font on most words
tx.setFont(mainFont);
}
//add mouse click handler
tx.setOnMouseReleased(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
if (event.getButton() == MouseButton.PRIMARY) {
event.consume();
int index = flow.getChildren().indexOf(tx);
//check baseline offset
checkBase(flow, index);
}
}
});
//add one word to flow
flow.getChildren().add(tx);
}
flow.setStyle(" -fx-border-color: orange;-fx-border-width: 1px;-fx-border-style: solid;");
} catch (Exception ex) {
LOG.log(Level.SEVERE, ex.getMessage(), ex);
throw new RuntimeException(ex);
}
}
public void checkBase(TextFlow flow, int index){
//check baselines of selected nodes
try{
//
Node n = flow.getChildren().get(index);
Text t = (Text)n;
//create rectangle around selected node
addBox(n);
//
Bounds layoutB = n.getLayoutBounds();
double minY = layoutB.getMinY();
double layY = n.getLayoutY();
// LOG.log(Level.INFO, "\n\n index: " + index + ", node layout bounds: " + layoutB);
//
double base = n.getBaselineOffset();
LOG.log(Level.INFO, "\n index: " + index + ", text: " + t.getText() + ", minY:" + minY + ", layY:" + layY + ", node baseline offset: " + base );
//
} catch (Exception ex) {
LOG.log(Level.SEVERE, ex.getMessage(), ex);
throw new RuntimeException(ex);
}
}
public void addBox(Node n){
//add box around of selected node
try{
//
//get text bounds within flow (parent)
Bounds parentB = n.getBoundsInParent();
LOG.log(Level.INFO, "\n\n node bounds in parent: " + parentB);
//convert to scene coords
Bounds sceneB = flow.localToScene(parentB);
// LOG.log(Level.INFO, "\n\n scene bounds: " + sceneB);
//create rectangle in scene coords
Rectangle r = new Rectangle( sceneB.getMinX(), sceneB.getMinY(), sceneB.getWidth(), sceneB.getHeight() );
r.setStroke(Color.GREEN);
r.setFill(Color.TRANSPARENT);
//add rectangle to border pane
bp.getChildren().add(r);
//
} catch (Exception ex) {
LOG.log(Level.SEVERE, ex.getMessage(), ex);
throw new RuntimeException(ex);
}
}
@Override
public void start(Stage primaryStage) throws Exception{
//add textflow to vbox
test();
// add stack pane to outer pane
sp.setAlignment(Pos.CENTER);
sp.getChildren().add(vbox);
bp.setCenter(sp);
//
// setup scene and stage
Scene scene = new Scene( bp, 600, 500 );
primaryStage.setScene(scene);
primaryStage.sizeToScene();
primaryStage.show();
//check baselines of selected nodes
}
public static void main(String[] args) {
launch(args);
}
}
---------- END SOURCE ----------
java version "9.0.1"
Java(TM) SE Runtime Environment (build 9.0.1+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.1+11, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 10.0.16299.125]
A DESCRIPTION OF THE PROBLEM :
For a wrapped Textflow of several lines,containing Text words of different font sizes,it appears the baseline offset of any word is based on the words in line 1 of TextFlow, even though the following lines have different heights.
Even though Text nodes were used in the test case, this problem will occur for any type of node where the node heights vary within the TextFlow.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
run the test case
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Expected different baseline offset for each node of wrapped TextFlow, depending on the line height where the node is located.
ACTUAL -
Same baseline offset for any node on any line of the wrapped TextFlow.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
//
package tools;
//
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.Node;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.scene.layout.VBox;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.paint.Color;
import javafx.collections.ObservableMap;
import javafx.event.EventHandler;
//
//
public class TextBase extends Application {
/*
This app tests baseline offsets of Text words within a multiline
TextFlow, in which a few words have bigger font sizes. In all cases
it appears the baseline offset is based on the words in line 1 of
TextFlow, even though the following lines have different heights.
Is this a bug in TextFlow?
*/
private static final Logger LOG = Logger.getLogger(TextBase.class.getName());
//
//init fonts
private Font mainFont = Font.font("DejaVu Serif", FontWeight.NORMAL, 20);
private Font bigFont = Font.font("DejaVu Serif", FontWeight.NORMAL, 48);
//
//init panes
private BorderPane bp = new BorderPane();
private StackPane sp = new StackPane();
private VBox vbox = new VBox();
private TextFlow flow = new TextFlow();
//
//init sample string of words
private String s1 = "Electromagnetic waves cover a wide spectrum from radio waves to gamma rays. Waves can interfere. "
+ "Constructive interference occurs when two waves are in phase. Destructive interference occurs when two waves are "
+ "out of phase. When waves pass through a diffraction grating, they interfere and produce a pattern of bright and "
+ "dark spots on a screen.";
//
//comment out one of these bigIndexList declarations
//first big font word in line 2
//private int[] bigIndexList = {10, 17};
//
//first big font word in line 1
private int[] bigIndexList = {1, 10, 17};
/**
* Default Constructor
*/
public TextBase() {
}
public void test(){
try{
init();
addText(flow, s1);
vbox.getChildren().add(flow);
} catch (Exception ex) {
LOG.log(Level.SEVERE, ex.getMessage(), ex);
throw new RuntimeException(ex);
}
}
public void init(){
try{
//insets: top, right, bottom, left
bp.setPadding(new Insets(5, 5, 5, 5));
bp.setStyle(" -fx-border-color: green;-fx-border-width: 2px;-fx-border-style: solid;");
bp.setPrefSize(500,400);
//
sp.setPadding(new Insets(10, 5, 10, 5));
sp.setStyle(" -fx-border-color: blue;-fx-border-width: 1px;-fx-border-style: solid;");
//
vbox.setSpacing(8);
vbox.setPadding(new Insets(15, 5, 15, 5));
vbox.setStyle(" -fx-border-color: red;-fx-border-width: 1px;-fx-border-style: solid;");
//
} catch (Exception ex) {
LOG.log(Level.SEVERE, ex.getMessage(), ex);
throw new RuntimeException(ex);
}
}
public void addText(TextFlow flow, String s){
try{
LOG.log(Level.INFO, "\n\nText s: " + s);
//split string into words
String[] ss = s.split(" ");
int len = ss.length;
int bigIndex = 0;
int bigEnd = bigIndexList.length;
for(int i = 0; i<len; i++){
Text tx = new Text(ss[i] + " ");
if(bigIndex < bigEnd && i == bigIndexList[bigIndex]){
//set big font on a few words
tx.setFont(bigFont);
bigIndex++;
} else {
//set main font on most words
tx.setFont(mainFont);
}
//add mouse click handler
tx.setOnMouseReleased(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
if (event.getButton() == MouseButton.PRIMARY) {
event.consume();
int index = flow.getChildren().indexOf(tx);
//check baseline offset
checkBase(flow, index);
}
}
});
//add one word to flow
flow.getChildren().add(tx);
}
flow.setStyle(" -fx-border-color: orange;-fx-border-width: 1px;-fx-border-style: solid;");
} catch (Exception ex) {
LOG.log(Level.SEVERE, ex.getMessage(), ex);
throw new RuntimeException(ex);
}
}
public void checkBase(TextFlow flow, int index){
//check baselines of selected nodes
try{
//
Node n = flow.getChildren().get(index);
Text t = (Text)n;
//create rectangle around selected node
addBox(n);
//
Bounds layoutB = n.getLayoutBounds();
double minY = layoutB.getMinY();
double layY = n.getLayoutY();
// LOG.log(Level.INFO, "\n\n index: " + index + ", node layout bounds: " + layoutB);
//
double base = n.getBaselineOffset();
LOG.log(Level.INFO, "\n index: " + index + ", text: " + t.getText() + ", minY:" + minY + ", layY:" + layY + ", node baseline offset: " + base );
//
} catch (Exception ex) {
LOG.log(Level.SEVERE, ex.getMessage(), ex);
throw new RuntimeException(ex);
}
}
public void addBox(Node n){
//add box around of selected node
try{
//
//get text bounds within flow (parent)
Bounds parentB = n.getBoundsInParent();
LOG.log(Level.INFO, "\n\n node bounds in parent: " + parentB);
//convert to scene coords
Bounds sceneB = flow.localToScene(parentB);
// LOG.log(Level.INFO, "\n\n scene bounds: " + sceneB);
//create rectangle in scene coords
Rectangle r = new Rectangle( sceneB.getMinX(), sceneB.getMinY(), sceneB.getWidth(), sceneB.getHeight() );
r.setStroke(Color.GREEN);
r.setFill(Color.TRANSPARENT);
//add rectangle to border pane
bp.getChildren().add(r);
//
} catch (Exception ex) {
LOG.log(Level.SEVERE, ex.getMessage(), ex);
throw new RuntimeException(ex);
}
}
@Override
public void start(Stage primaryStage) throws Exception{
//add textflow to vbox
test();
// add stack pane to outer pane
sp.setAlignment(Pos.CENTER);
sp.getChildren().add(vbox);
bp.setCenter(sp);
//
// setup scene and stage
Scene scene = new Scene( bp, 600, 500 );
primaryStage.setScene(scene);
primaryStage.sizeToScene();
primaryStage.show();
//check baselines of selected nodes
}
public static void main(String[] args) {
launch(args);
}
}
---------- END SOURCE ----------