Getting text bounds within JavaFX - java

I am using JavaFX to produce bitmap previews of my music documents.
At certain points, within the paint process, I need to know the dimensions of a given string.
Upto now, I have used the following :
bounds = TextBuilder.create().text(string).font(m_Font).build().getLayoutBounds();
However, eclipse informs me that this is depreciated. It is so depreciated that the bounds object is empty (all set to zero).
How do I go about getting the bounds - width and height - of a single line string now ?
Please note that there are no on screen controls being used to display this stuff. Everything is generated in memory and "dumped" out to a png bitmap.
I have scavenged around on the net and have not found the answer (or I missed it entirely).
Any expert help available ?

As I mentioned in my comment, getLayoutBounds() is not deprecated and is perfectly valid to use. It's just the builder which is deprecated.
That said, I have created the following test application which produced seemingly correct output, using builders and creating objects directly:
import javafx.geometry.Bounds;
import javafx.scene.text.Text;
import javafx.scene.text.TextBuilder;
import javafx.stage.Stage;
import javafx.scene.text.Font;
public class stack extends javafx.application.Application {
public static void main(String[] args)
{
// Builder
Bounds b = TextBuilder.create().text("hello").build().getLayoutBounds();
System.out.println(b.getHeight() + ", " + b.getWidth());
b = TextBuilder.create().text("heeeeello").build().getLayoutBounds();
System.out.println(b.getHeight() + ", " + b.getWidth());
// No builder
b = new Text("hello").getLayoutBounds();
System.out.println(b.getHeight() + ", " + b.getWidth());
b = new Text("heeeeello").getLayoutBounds();
System.out.println(b.getHeight() + ", " + b.getWidth());
// With bad font, zero sized
Font my_font = new Font("i am not a font", 0);
Text text = new Text("heeeeello");
text.setFont(my_font);
b = text.getLayoutBounds();
System.out.println(b.getHeight() + ", " + b.getWidth());
// With bad font, arbitrary size
my_font = new Font("i am not a font", 20);
text = new Text("heeeeello");
text.setFont(my_font);
b = text.getLayoutBounds();
System.out.println(b.getHeight() + ", " + b.getWidth());
}
#Override
public void start(Stage primaryStage) throws Exception { }
}
Output:
15.9609375, 25.91015625
15.9609375, 51.01171875
15.9609375, 25.91015625
15.9609375, 51.01171875
0.0, 0.0
26.6015625, 85.01953125
I would hypothesise that your font is screwing things up, possibly the size is set to zero or some other error.

Related

Strip some words from a List collection

I am doing a selenium test against a web page which returns a table with some rows and columns showing payment data. I'm trying to strip some characters/words from the result of the XPATH i'm using because i dont need the part while doing an assertion (check if the data in table is correct).
Normally the webpage also returns a "Dropdown Button" as text (there is an icon), just before the identification number (e.g 168.3285.6021 as seen below).
What i used is it.set(it.next().replaceAll("DropDown Arrow ","")); so the DropDown Arrow text is replaced with nothing, which only works for the first line, but the other 2 lines don't get replaced. Any tips?
public void check_receivals() {
// Check how many lines and assert the size (from xpath)
List<WebElement> Receivals = driver.findElements(By.xpath("//*[#id='received-overview']//div[#class='bdpo-overview-table-row']/div[#class='claims']"));
System.out.println(Receivals.size() + " receival lines found");
assertEquals(7, Receivals.size());
// Test data to compare against..aka expectedResultList
List<String> expectedResultList = new ArrayList<>();
expectedResultList.add ("168.3285.6021\n" + "Payment 2015\n" + "01-01-2015\n" + "€ 246");
expectedResultList.add ("143.8407.8413\n" + "Payment 2015\n" + "01-01-2015\n" + "€ 233");
expectedResultList.add ("154.2841.2407\n" + "Payment 2015\n" + "01-01-2015\n" + "€ 253");
// Assert
List<WebElement> ReceivalLines = driver.findElements(By.xpath("//*[#id='received-overview']//div[#class='bdpo-overview-table-row']/div[#class='claims']"));
List<String> ReceivalLines_List = ReceivalLines.stream().map(WebElement::getText).collect(Collectors.toList());
ListIterator<String> it = ReceivalLines_List.listIterator();
while(it.hasNext()) {
it.set(it.next().replaceAll("DropDown Arrow ",""));
assertEquals(ReceivalLines_List, expectedResultList);
THe issue is that you are modifying the iterator as you are working with it. I would suggest making the replace part of the stream operation using the map function.
List<String> ReceivalLines_List = ReceivalLines.stream().map(WebElement::getText).map(s -> s.replaceAll("DropDown Arrow ","")).collect(Collectors.toList());

Why does JavaFX ImageView snapshot shows a wrong size?

I have a large image that I'm loading from a file. I need to put it on two ImageView's (ivInput,ivLayer1) and resize it to 32x32. ivInput is resizing to 32x32 succesfully, but when I take its snapshot it has a wrong size (32x25).
Image imgLetterBox = loadFromFile("m1.png");
ivInput.setImage(imgLetterBox);
ivInput.setFitWidth(32);
ivInput.setFitHeight(32);
System.out.println("ImgInput:" + ivInput.getFitWidth() + ", " + ivInput.getFitHeight());//32x32, it's ok
Image imgLayer1 = ivInput.snapshot(null,null);
System.out.println("ImgLayer1:" + imgLayer1.getWidth() + ", " + imgLayer1.getHeight());//32x25. why?
Problem was in PreserveRatio property, it was on true value. I changed it on disable value and it is working well now.

Apache POI PPT (Java) - Updating TextShape keeping text formatting/shape formatting

I am trying to produce several reports (i.e. N PPTX files) based on different inputs/for different users on the same PPTX template I created.
I have several preformatted XSLFTextShape on the PPTX template that contains a single XSLFTextParagraph already formatted (i.e. both the shape and the text). Each shape contains a particular placeholder that I need to substitute with a dynimic value. I have this value in a Map (placeholder,newValue). I am successful in updating the placeholder with the new value using:
textShape.clearText();
XSLFTextRun run = paragraph.addNewTextRun();
run.setText(newText);
So, when I produce the PPTX in output the text is updated but font color, font formatting, font size are changed compared to those I defined in the template. How can I keep the same formatting?
Any solutions to simply change the text while keeping original formatting?
Thanks in advance!
For everybody which may be interested in this topic in the future - I post the solution (working if one TextBox has a single Paragraph). This solution loops on all text boxes and in the case one contain one of the vales specified in the Placeholder->newValue map, it will update it maintaining the formatting.
public static void updateTextBoxesWithDesiredValues(XMLSlideShow ppt, Map<String, String> placeHolderDefinedValue) {
logger.info("ElapsedTime: " + tM.getTimeElapsedReadableFormat() + " ########## Updating single text box content...");
List<XSLFSlide> allSlides = ppt.getSlides();
int updatedElements = 0;
for (XSLFSlide currentSlide : allSlides) {
for (XSLFShape shape : currentSlide.getShapes()) {
if (shape instanceof XSLFTextShape) {
XSLFTextShape textBox = (XSLFTextShape) shape;
String elementTextContent = textBox.getText();
for (Object key : placeHolderDefinedValue.keySet()) {
if (elementTextContent.equals(key)) {
List<XSLFTextParagraph> textBoxParagraphs = textBox.getTextParagraphs();
List<XSLFTextRun> textBoxParagraphTextRuns = textBoxParagraphs.get(0).getTextRuns();
//System.out.println("########################## check paragraph number in textbox: " + textBoxParagraphs.size() + " - TextRuns: " + textBoxParagraphs.get(0).getTextRuns().size());
logger.info("ElapsedTime: " + tM.getTimeElapsedReadableFormat() + updatedElements + ") Updating: " + textBox.getText() + " --> " + placeHolderDefinedValue.get(key));
for (XSLFTextRun r : textBoxParagraphTextRuns) {
r.setText(placeHolderDefinedValue.get(key));
}
updatedElements++;
//break;
}
}
}
}
}
logger.info("ElapsedTime: " + tM.getTimeElapsedReadableFormat() + " Total Text Element Content Updated: " + updatedElements + " #########################");
}
It's kind of horrible - but yeah there's a reason they called it "POI".
Here's my approach to "only reset text" of an existing XSLFTextShape (that must have at least some text pre-set!):
textShape.getTextParagraphs().get(0).getTextRuns().get(0).setText(text);
for (int i = 1; i < textShape.getTextParagraphs().get(0).getTextRuns().size(); i++) {
textShape.getTextParagraphs().get(0).getTextRuns().get(i).setText("");
}
for (int i = 1; i < textShape.getTextParagraphs().size(); i++) {
textShape.getTextParagraphs().get(i).getTextRuns().stream().filter(tr -> !tr.getRawText().equals("\n")).forEach(tr -> tr.setText(""));
}
It will replace all existing text(paragraphs/runs) with "empty" text, but linebreaks can't be replaced for some reason. So this might leave you with some trailing lines - as they usually(!) are transparent this won't really hurt a lot.
.clearText / removing paragraphs either destoyed the formatting for me, or didn't work. Trying to reset the style (fontColor, fontFamily, fontSize, isBold, isItalit, ...) didn't result in satisfying results :(

JavaFX ToggleButton is not visible but registered as Children

im creating a JavaFX based Discord Bot where you can choose which Guilds are allowed to listen to Commands, and it creates as many ToggleButtons as the amount of Servers the Bot is connected to.
Here is my Method:
public void generateButtons() {
int y = 14;
discordVBox = new VBox();
JFXToggleButton tglBtn;
discordVBox.setSpacing(30);
for (final Guild g : DiscordBot.jda.getGuilds()) {
if (g == null || g.getTextChannels().isEmpty() || !DiscordBot.isDiscordBotOnline()) {
PrintConsole.printError("Error creating Toggle Buttons.");
return;
} else {
tglBtn = new JFXToggleButton();
tglBtn.setText(g.getName());
tglBtn.setStyle("-jfx-toggle-color: #d35400;");
tglBtn.setLayoutX(14);
tglBtn.setLayoutY(y);
tglBtn.setPrefHeight(56);
tglBtn.setPrefWidth(141);
discordVBox.getChildren().add(tglBtn);
y += 63;
System.out.println("DISABLED: " + tglBtn.isDisabled());
System.out.println("VISIBLE: " + tglBtn.isVisible());
System.out.println("PARENT: " + tglBtn.getParent());
tglBtn.setVisible(true);
}
}
System.out.println("VBOX PARENT: " + discordVBox.getParent());
System.out.println("VBOX CHILDREN: " + discordVBox.getChildren());
}
Ignore all the outputs, i was trying to debug all of them and per code it seemed fine, but no visible to the eye.
The Scene itself is mostly via FXML and im using a Custom Library for JavaFX called Jfoenix
The Method "generateButtons" is executed as soon the Discord Bot goes online.
It's actually the NEW VBox you created that is not visible.
If you loaded the scene with FXMLLoader, all of the scene contents were created for you and references were provided to the loaded objects. Use the VBox that you specified with the FXML code instead of creating a new one.
You can remove line 3 of your sample code and this should run.
discordVBox = new VBox(); // <- don't need this, there is already an VBox instantiated

JavaFX why does ScrollPane require two group wrappings to re-compute its scollbars?

In this example, an image of a map is set on a ScrollPane in the FXML file.
http://hg.openjdk.java.net/openjfx/8u-dev/rt/rev/36a59c629605
The image is resizable by some other method that changes the scale of the Group - zoomGroup - containing the image.
+ private void zoom(double scaleValue) {
+// System.out.println("airportapp.Controller.zoom, scaleValue: " + scaleValue);
+ double scrollH = map_scrollpane.getHvalue();
+ double scrollV = map_scrollpane.getVvalue();
+ zoomGroup.setScaleX(scaleValue);
+ zoomGroup.setScaleY(scaleValue);
+ map_scrollpane.setHvalue(scrollH);
+ map_scrollpane.setVvalue(scrollV);
+ }
But in order to make sure the ScrollPane re-computes its scrollbars (so the scrollable area effectively changes to include the entire image at any scale) the Group that wraps the Image, zoomGroup, is then wrapped in another Group, contentGroup, which is set as the ScrollPane's content.
+ // Wrap scroll content in a Group so ScrollPane re-computes scroll bars
+ Group contentGroup = new Group();
+ zoomGroup = new Group();
+ contentGroup.getChildren().add(zoomGroup);
+ zoomGroup.getChildren().add(map_scrollpane.getContent());
+ map_scrollpane.setContent(contentGroup);
Can someone explain why this is necessary?
This is the video that accompanies the files:
https://www.youtube.com/watch?v=ij0HwRAlCmo

Categories