I'm trying to paint a JLabel to a BufferedImage using html as text but it always prints the text in the middle (vertically), and cuts off the text on the right.
Here is my code:
public static void test() throws IOException
{
int width = 100;
JLabel label = new JLabel();
label.setText("<html><body><p>asdf asdfasdf asdfsdf asdfasdf asdfasdf asdfasdfasdfasdf asdfasdfasdfa sdfasd fasdf asdfasdf asdfasdf asdfasdfasdfa sdfasdf</p></body></html>");
Dimension size = getPreferredSize(label.getText(), true, width);
label.setSize(size);
BufferedImage image =
new BufferedImage(
label.getWidth(),
label.getHeight(),
BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setColor(Color.LIGHT_GRAY);
g.fillRect(0, 0, 500, 900);
label.paint(g);
ImageIO.write(image, "png", new File("testImage.png"));
}
private static final JLabel resizer = new JLabel();
public static java.awt.Dimension getPreferredSize(
String html,
boolean width,
int prefSize)
{
resizer.setText(html);
View view =
(View) resizer
.getClientProperty(javax.swing.plaf.basic.BasicHTML.propertyKey);
view.setSize(width ? prefSize : 0, width ? 0 : prefSize);
float w = view.getPreferredSpan(View.X_AXIS);
float h = view.getPreferredSpan(View.Y_AXIS);
return new java.awt.Dimension((int) Math.ceil(w), (int) Math.ceil(h));
}
And here is the resulting image:
I think the html word wrapping is handled by something in the swing thread loop, if you are doing this form that loop as a actionlistener then it can't recalculate the html
Related
I have JLabels in a constrained space (JTable) and when the text inside the label is too long, it's truncated. Is there a way to make the text fit in the allotted space by only horizontal squishing?
See the upper Jlabel in these examples:
The text is HTML formatted so I can't just drawstring on a custom JPanel component myself. There's no icon.
Since I've solved this question while typing it, in accordance with meta I'll share the answer.
I set this as the UI for the JLabel:
It renders the text to an off-screen image, then resizes that image to the JLabel's proportions.
[Edit] This doesn't work correctly with transparent labels or labels with empty HTML text.
// Copied and modified from BasicLabelUI
private static class SquishLabelUI extends BasicLabelUI {
private final Rectangle paintIconR = new Rectangle();
private final Rectangle paintTextR = new Rectangle();
private String layout(JLabel label, FontMetrics fm, int width, int height) {
Insets insets = label.getInsets(null);
String text = label.getText();
Rectangle paintViewR = new Rectangle(insets.left,
insets.top,
width - (insets.left + insets.right),
height - (insets.top + insets.bottom));
paintIconR.setBounds(0, 0, 0, 0);
paintTextR.setBounds(0, 0, 0, 0);
return layoutCL(label, fm, text, null, paintViewR, paintIconR, paintTextR);
}
#Override
public void paint(Graphics g, JComponent c) {
JLabel label = (JLabel)c;
layout(label, SwingUtilities2.getFontMetrics(label, g), c.getWidth(), c.getHeight());
View v = (View)c.getClientProperty(BasicHTML.propertyKey);
Dimension size = getPreferredSize(label);
BufferedImage img = label.getGraphicsConfiguration()
.createCompatibleImage(size.width, size.height, TRANSLUCENT);
Graphics2D g2 = img.createGraphics();
try {
g2.setColor(label.getBackground());
g2.setClip(0, 0, size.width, size.height);
g2.fillRect(0, 0, size.width, size.height);
v.paint(g2, new Rectangle(0, 0, size.width, size.height));
int renderWidth = Math.min(size.width, paintTextR.width);
Image img2 = img.getScaledInstance(renderWidth, paintTextR.height, Image.SCALE_SMOOTH);
g.drawImage(img2, paintTextR.x, paintTextR.y, null);
} finally {
g2.dispose();
}
}
}
I want to create a String which displays the time in the format: 10h 30min, but the units (h and min) should have a smaller font than the numbers. When using a JLabel, I get this work with a html formatted string with span attributes.
Now I want to add such a String to a custom object and write it with the drawAlignedString method. But, here the html passing does not working. The custom object than shows my code and not the formatted String.
Is there a way to get this working or any other solution for drawing Strings with diffrent Substrings?
This is what I've tried:
String time = String.format(
"<html>%d<span style=\"font-family:Arial Unicode MS;font-size:12px;\">h </span> %d<span "
+ "style=\"font-family:Arial Unicode MS;font-size:12px;\">min</span></html>",
absSeconds / 3600, (absSeconds % 3600) / 60);
g2.setFont(this.centerTextFont);
g2.setPaint(this.centerTextColor);
TextUtilities.drawAlignedString(time, g2, (float) area.getCenterX(), (float) area.getCenterY(),
TextAnchor.CENTER);
Once the label is configured, pass the Graphics to the paint method of the label.
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class LabelRenderTest {
public static void main(String[] args) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
String title = "<html><body style='width: 200px; padding: 5px;'>"
+ "<h1>Do U C Me?</h1>"
+ "Here is a long string that will wrap. "
+ "The effect we want is a multi-line label.";
JFrame f = new JFrame("Label Render Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BufferedImage image = new BufferedImage(
400,
300,
BufferedImage.TYPE_INT_RGB);
Graphics2D imageGraphics = image.createGraphics();
GradientPaint gp = new GradientPaint(
20f,
20f,
Color.red,
380f,
280f,
Color.orange);
imageGraphics.setPaint(gp);
imageGraphics.fillRect(0, 0, 400, 300);
JLabel textLabel = new JLabel(title);
textLabel.setSize(textLabel.getPreferredSize());
Dimension d = textLabel.getPreferredSize();
BufferedImage bi = new BufferedImage(
d.width,
d.height,
BufferedImage.TYPE_INT_ARGB);
Graphics g = bi.createGraphics();
g.setColor(new Color(255, 255, 255, 128));
g.fillRoundRect(
0,
0,
bi.getWidth(f),
bi.getHeight(f),
15,
10);
g.setColor(Color.black);
textLabel.paint(g);
Graphics g2 = image.getGraphics();
g2.drawImage(bi, 20, 20, f);
ImageIcon ii = new ImageIcon(image);
JLabel imageLabel = new JLabel(ii);
f.getContentPane().add(imageLabel);
f.pack();
f.setLocationByPlatform(true);
f.setVisible(true);
}
});
}
}
I have the following:
public class ParametricEQView extends JPanel implements PluginView {
private static final int BAND_WIDTH = 3;
private static final int THROW_HEIGHT = 64;
private static final int WIDTH = 128*BAND_WIDTH + 2*MARGIN;
private static final int HEIGHT = 2*THROW_HEIGHT + 2*MARGIN;
private static final int MID_HEIGHT = THROW_HEIGHT + MARGIN;
private final ParametricEQ _peq;
public ParametricEQView(ParametricEQ peq) {
super();
_peq = peq;
SwingUtils.freezeSize(this, WIDTH, HEIGHT);
setToolTipText("Parametric Equalizer");
}
#Override
public void paint(Graphics g) {
final Graphics2D g2d = (Graphics2D) g;
final int max = findMax();
g.setColor(BACKGROUND);
g.fillRect(0, 0, WIDTH, HEIGHT);
g.setColor(DATA);
final double scalingFactor = -((double) THROW_HEIGHT) / max;
final double[] fineLevels = _peq.getFineLevels();
int x = MARGIN;
int h;
final int[] xPoints = new int[128];
final int[] yPoints = new int[128];
for (int i = 0; i < 128; ++i) {
h = (int) (fineLevels[i] * scalingFactor);
xPoints[i] = x;
yPoints[i] = MID_HEIGHT + h;
x += BAND_WIDTH;
}
g.drawPolyline(xPoints, yPoints, 128);
g.setColor(AXES);
g.drawLine(MARGIN, MARGIN, MARGIN, HEIGHT-MARGIN);
g.drawLine(MARGIN, MID_HEIGHT, WIDTH-MARGIN, MID_HEIGHT);
g.setFont(AXIS_FONT);
final FontMetrics metrics = g.getFontMetrics();
int width = (int) metrics.getStringBounds(AXIS_LABEL_INPUT_MIDINUM, g).getWidth();
g.drawString(AXIS_LABEL_INPUT_MIDINUM, WIDTH-MARGIN-width, HEIGHT-3);
final AffineTransform atx = new AffineTransform();
atx.setToRotation(-Math.PI/2, 0, HEIGHT);
g2d.setTransform(atx);
final String topLabel = "+" + max;
width = (int) metrics.getStringBounds(topLabel, g).getWidth();
g2d.drawString(topLabel, HEIGHT-MARGIN-width, HEIGHT+10);
width = (int) metrics.getStringBounds(AXIS_LABEL_OUTPUT_VELOCITY, g).getWidth();
g2d.drawString(AXIS_LABEL_OUTPUT_VELOCITY, MID_HEIGHT-(width/2), HEIGHT+10);
g2d.drawString("-" + max, MARGIN, HEIGHT+10);
}
private int findMax() {
int max = 3;
for (int i = 0; i < 128; ++i)
max = Math.max(max, (int) Math.ceil(Math.abs(_peq.getFineLevels()[i])));
return max;
}
}
This is what it looks like:
The ParametricEQView is the component with the white background filling most of the window. In this image its coordinates are (0,0) in the containing frame and everything is great. However, if I resize the window so that the ParametricEQView moves over a bit (it has a fixed size and is set to be centered in its available space), the rotated text stays relative to the (0,0) of the frame instead of the component:
Everything else draws relative to the panel, it's just the rotated text that doesn't. What am I doing wrong?
When you call g2d.setTransform(atx); you override the transform currently set in the Graphics object, i.e. the translation between the panel and its parent frame. That's why the text is drawn in the frame referential, and not in the panel referential.
The correct code would be to get the current transform and modify it or directly call Graphics2D.rotate(double).
1) For custom paintings you need to override protected void paintComponent(Graphics g) instead of public void paint(Graphics g). Read more about customPaintings.
2)Seems you have your problem, because you do something like next for creation of GUI:
JFrame frame = new JFrame();
JPanel p = new JPanel();
ParametricEQView view = new ParametricEQView();
view.setPreferredSize(new Dimension(200,200));
p.add(view);
frame.add(p);
in that case ParametricEQView doesn't resize as you want, because JPanel use FlowLayout as default.
You need to use another LayoutManager for your panel, for example BorderLayout.
Try something like next:
JFrame frame = new JFrame();
ParametricEQView view = new ParametricEQView();
frame.add(view);
or
JPanel panel = new JPanel(new BorderLayout());
panel.add(view,BorderLayout.CENTER);
frame.add(panel);
in that case your ParametricEQView panel will be painting in proper way.
I want to append bold text on image, only selected text should be bold.
String word="This is dummy text, this should be BOLD"
final BufferedImage image = ImageIO.read(new File(Background));
Graphics g = image.getGraphics();
g.drawString(word, curX, curY);
g.dispose();
ImageIO.write(image, "bmp", new File("output.bmp"));
You want to use an AttributedString and pass its iterator to drawString
static String Background = "input.png";
static int curX = 10;
static int curY = 50;
public static void main(String[] args) throws Exception {
AttributedString word= new AttributedString("This is text. This should be BOLD");
word.addAttribute(TextAttribute.FONT, new Font("TimesRoman", Font.PLAIN, 18));
word.addAttribute(TextAttribute.FOREGROUND, Color.BLACK);
// Sets the font to bold from index 29 (inclusive)
// to index 33 (exclusive)
word.addAttribute(TextAttribute.FONT, new Font("TimesRoman", Font.BOLD, 18), 29,33);
word.addAttribute(TextAttribute.FOREGROUND, Color.BLUE, 29,33);
final BufferedImage image = ImageIO.read(new File(Background));
Graphics g = image.getGraphics();
g.drawString(word.getIterator(), curX, curY);
g.dispose();
ImageIO.write(image, "png", new File("output.png"));
}
output.png:
You can set a Font on the Graphics object before you draw the String like this:
Font test = new Font("Arial",Font.BOLD,20);
g.setFont(test);
If you only want one word bold you'll have to call drawString twice, and set the font to bold only the second time.
Maybe this one will help - curX,curY should be updated after the first drawString probably, otherwise it will look quite nasty. :)
String word="This is text, this should be ";
final BufferedImage image = ImageIO.read(new File(Background));
Graphics g = image.getGraphics();
g.drawString(word, curX, curY);
Font f = new Font("TimesRoman", Font.Bold, 72);
g.setFont(f);
String word="BOLD";
g.drawString(word, curX, curY);
g.dispose();
ImageIO.write(image, "bmp", new File("output.bmp"));
I am dragging and dropping the jTable cell from one jTable to another jTable.For Now it is showing me default drag drop icon.
I am using TransferHandler class to implement this.
I Override getDragImage(image) to put my customize image But it is not working.
This way i implemented my code Implementation
I tried this code into this method.
File newFile = new File("./dragImage.jpeg");
Font font = new Font("Tahoma", Font.PLAIN, 11);
FontRenderContext frc = new FontRenderContext(null, true, true);
Rectangle2D bounds = font.getStringBounds(l_value, frc);
int w = (int) bounds.getWidth();
int h = (int) bounds.getHeight();
BufferedImage image = new BufferedImage(10,10, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, 10, 10);
g.setColor(Color.BLACK);
g.setFont(font);
g.drawString(l_value, (float) bounds.getX(), (float) -bounds.getY());
g.dispose();
return image;
This code is working in my main method but here in this function it is not working.