Write text to image in multiple fonts with wrapping in java - java

I am trying to create an image with a given text and style. eg;
" textStyle(Offer ends 25/12/2016. Exclusions Apply., disclaimer) textStyle(See Details,underline) "
In above line i am splitting and creating a map that stores the first parameter of textStyle block as key and second parameter as value where second param defines the style to be applied on first param. Hence an entry of map will look like .
Now when i iterate over this map to write the text to image i check if the text is overflowing the width. If yes then it breaks the text and adds it to next line in the horizontal center. So for example lets say i am trying to write "Offer ends 25/12/2016. Exclusions Apply." with Arial and font size 12. While writing i find that i can write till "Offer ends 23/12/2016. " only and "Exclusions apply" has to go in next line. But it writes the text in horizontal center neglecting that as there is space left horizontally i can write "See Details" too in the same line.
Please help. Below is the code what i have tried. I have also tried creating a JTextPane and then converting it to image but this cannot be an option as it first creates the frame, makes it visible, writes it and then disposes it. And most of the times i was getting Nullpointer exception on SwingUtilities.invokeAndWait.
Actual : http://imgur.com/7aIlcEQ
Expected http://imgur.com/038zQTZ
public static BufferedImage getTextImage(String textWithoutStyle, Map<String, String> textToThemeMap, Properties prop, int height, int width) {
BufferedImage img = new BufferedImage(width,height,BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D g2d = img.createGraphics();
g2d.setPaint(Color.WHITE);
FontMetrics fm = g2d.getFontMetrics();
Map<String, Font> textToFontMap = new LinkedHashMap<String, Font>();
for(Map.Entry<String, String> entry : textToThemeMap.entrySet()) {
if(StringUtils.isNotBlank(entry.getKey()) && StringUtils.isNotBlank(entry.getValue())) {
Font font = getFont(prop, entry.getValue().trim());
g2d.setFont(font);
fm = g2d.getFontMetrics();
String string = entry.getKey();
char[] chars = null;
int i = 0, pixelWidth = 0;
List<String> newTextList = new ArrayList<String>();
if(fm.stringWidth(string) > (width - 10)) {
chars = string.toCharArray();
for (i = 0; i < chars.length; i++) {
pixelWidth = pixelWidth + fm.charWidth(chars[i]);
if(pixelWidth >= (width - 10)) {
break;
}
}
String newString = WordUtils.wrap(string, i, "\n",false);
String[] splitString = newString.split("\n");
for(String str : splitString) {
newTextList.add(str);
textToFontMap.put(string, font);
}
} else {
newTextList.add(string);
textToFontMap.put(string, font);
}
}
}
Font font = new Font("Arial", Font.BOLD, 14);
int spaceOfLineHeight = (textToFontMap.size() - 1) * 7;
int spaceOfText = textToFontMap.size() * font.getSize();
int totalSpace = spaceOfLineHeight + spaceOfText ;
int marginRemaining = height - totalSpace;
int tempHt = marginRemaining / 2 + 10;
String txt = null;
for(Map.Entry<String, Font> entry : textToFontMap.entrySet()) {
txt = entry.getKey();
font = entry.getValue();
g2d.setFont(font);
fm = g2d.getFontMetrics();
int x = (width - fm.stringWidth(txt)) / 2;
int y = tempHt;
g2d.drawString(txt, x, y);
tempHt = tempHt + fm.getHeight();
}
// g2d.drawString(text.getIterator(), 0, (int)lm.getAscent() + lm.getHeight());
// g2d.dispose();
return img;
}
// Code with JTextPane ------------------------------------------
public static BufferedImage getTextImage(final Map < String, String > textToThemeMap, final Properties prop, final int height, final int width) throws Exception {
JFrame f = new JFrame();
f.setSize(width, height);
final StyleContext sc = new StyleContext();
DefaultStyledDocument doc = new DefaultStyledDocument(sc);
final JTextPane pane = new JTextPane(doc);
pane.setSize(width, height);
// Build the styles
final Paragraph[] content = new Paragraph[1];
Run[] runArray = new Run[textToThemeMap.size()];
int i = 0;
for (Map.Entry < String, String > entry: textToThemeMap.entrySet()) {
if (StringUtils.isNotBlank(entry.getValue().trim()) && StringUtils.isNotBlank(entry.getKey().trim())) {
Run run = new Run(entry.getValue().trim(), entry.getKey());
runArray[i++] = run;
}
}
content[0] = new Paragraph(null, runArray);
/*createDocumentStyles(sc, prop,textToThemeMap.values());
addText(pane, sc, sc.getStyle("default"), content);
pane.setEditable(false);*/
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
try {
createDocumentStyles(sc, prop, textToThemeMap.values());
} catch (MalformedURLException e) {
//e.printStackTrace();
}
addText(pane, sc, sc.getStyle("default"), content);
pane.setEditable(false);
}
});
} catch (Exception e) {
System.out.println("Exception when constructing document: " + e);
}
f.getContentPane().add(pane);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D gd = img.createGraphics();
f.paint(gd);
f.dispose();
/*ImageIO.write(img, "png", new File("C:\\Users\\spande0\\Desktop\\a.png"));
System.out.println("done");*/
return img;
}

I suspect the issue is in your 'Y' computation.
int spaceOfLineHeight = (newTextList.size() - 1) * 7;
int spaceOfText = newTextList.size() * font.getSize();
int totalSpace = spaceOfLineHeight + spaceOfText;
int marginRemaining = height - totalSpace;
int tempHt = marginRemaining / 2 + 10;
You have to keep the height occupied by the previous lines, and add it to the current 'Y'.
At the moment, for all the lines, the 'Y' values is same.
Declare prevHeight outside the for loop. and then do the following.
int tempHt = marginRemaining / 2 + 10;
tempHT += prevHeight;
prevHeight = tempHeight
Based on the comments, I will suggest you to break down your function into two smaller functions.
// Loop through the strings and find out how lines are split and calculate the X, Y
// This function will give the expected lines
splitLinesAndComputeResult
// Just render the lines
renderLines

Related

Find dynamic image on sikuliX created problems with buffer

I'm trying to find an image that I create previously inside an empty template with this function that insert text on it receiving color, content and font and return the path of generate image:
the template
public String insertTextOnBlanck(String colorLetter,String text,Font font) {
//path is a private varibable initialized with the constructor
File blankFile = new File("images/dinamic/"+path);
BufferedImage image = null;
String exit_path = null;
try {
image = ImageIO.read(blankFile);
int type = image.getType() == 0? BufferedImage.TYPE_INT_ARGB : image.getType();
Graphics2D g2 = image.createGraphics();
FontMetrics metrics = g2.getFontMetrics(font);
BufferedImage resizeImage = resizeImage(image,type, text,metrics);
image.flush();
int w = resizeImage.getWidth();
int h = resizeImage.getHeight();
g2 = resizeImage.createGraphics();
g2.setColor(Color.decode(colorLetter));
g2.setFont(font);
// Get the FontMetrics
int x = (w - metrics.stringWidth(text)) / 2;
int y = (metrics.getAscent() + (h - (metrics.getAscent() + metrics.getDescent())) / 2);
g2.setBackground(Color.decode("#d1e8f8"));
g2.drawString(text, x, y);
g2.dispose();
//create image with text
exit_path = "images/dinamic/changed_"+path;
File file = new File(exit_path);
ImageIO.write(resizeImage, "png", file);
resizeImage.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return exit_path;
}
and this works the fist time when i call this other function
public void dinamicClick(String path,String input,String fontLetter,String colorLetter, int fontType,int size) throws FindFailed {
DinamicImg DimImg = new DinamicImg();
DimImg.setPath(path);
String modPath = DimImg.insertTextOnBlanck(
colorLetter,
input,//Inventario de recurso
new Font(fontLetter,fontType, size)
);
Iterator<Match> myIt = s.findAll(modPath);
while (myIt.hasNext()) {
Location loc = myIt.next().getTarget();
s.click(loc);
}
myIt.remove();
removeFile(modPath);
}
the removeFile function is:
private void removeFile(String toRemove) {
File file = new File(toRemove);
if(file.delete()){
System.out.println(file.getName() + " is deleted!");
}else{
System.out.println("Delete operation is failed.");
}
}
The result:
but next calls dont work at all, just when i change the name of exit path, so i thought was a cache problem but adding ImageIO.setUseCache(false); at start of "insertTextOnBlanck" function still doesn`t work. Im out of ideas please help, thanks.
I resolve it , with the libary org.sikuli.script.ImagePath you ve to reset the paths of the internal cache of SikuliX with ImagePath.reset().

Codename one rounded URLImage not displaying

I am attempting to pull an image from a url using URLImage.createToStorage. However I want that picture to appear rounded so I add a Mask to the image. However when I run the label only shows the placeholder image, not the url image. When I comment out the code that adds the rounded mask to the image the image displays. Is there something wrong with my rounded image code. I used Display.getInstance().callSerially().
//Where I display the image.
public void setUpProfile(Form f) {
Label imageLabel = findMyImage(f);
Image img = getImageFromRes("myprofile.png");
Image scaled = img.scaledWidth(f.getWidth() / 2);
EncodedImage enc = EncodedImage.createFromImage(scaled, false);
Display.getInstance().callSerially(new Runnable() {
#Override
public void run() {
imageLabel.setIcon(getRoundedImage(URLImage.createToStorage(enc,
"profileImage8", me.getPicture(), URLImage.RESIZE_SCALE_TO_FILL)));
f.revalidate();
}
});
findProfNameLabel(f).setText(me.getName());
findProfAgeLabel(f).setText(me.getAge() + " Years old");
findProfPrefLabel(f).setText("Interested in " + me.getPref());
}
public Image getRoundedImage(Image img) {
int w = img.getWidth();
int h = img.getHeight();
Image maskImage = Image.createImage(w, h);
Graphics g = maskImage.getGraphics();
g.setColor(0xffffff);
g.fillArc(0, 0, w, h, 0, 360);
Object mask = maskImage.createMask();
Image ret = img.applyMask(mask);
return ret;
}
The setUpProfile() method is called in the beforeShow of the Form.
EDIT: I edited in the working setUpProfile() method which uses URLImage.createMaskAdapter. and achieves a rounded image.
public void setUpProfile(Form f) {
Label imageLabel = findMyImage(f);
Image mask = getImageFromRes("rounded-mask.png");
Image placeholder = getImageFromRes("myprofile.png").scaled(mask.getWidth(), mask.getHeight());
EncodedImage enc = EncodedImage.createFromImage(placeholder.applyMask(mask.createMask()),
false);
System.out.println("SetUpProfile picture " + me.getPicture());
imageLabel.setIcon(URLImage.createToStorage(enc, "profileImage8",
me.getPicture(), URLImage.createMaskAdapter(mask)));
findProfNameLabel(f).setText(me.getName());
findProfAgeLabel(f).setText(me.getAge() + " Years old");
findProfPrefLabel(f).setText("Interested in " + me.getPref());
}
You can achieve this by creating a custom ImageAdapter that generates a round-mask automatically for you while downloading the image.
public static final URLImage.ImageAdapter RESIZE_SCALE_WITH_ROUND_MASK = new URLImage.ImageAdapter() {
#Override
public EncodedImage adaptImage(EncodedImage downloadedImage, EncodedImage placeholderImage) {
Image tmp = downloadedImage.scaledLargerRatio(placeholderImage.getWidth(), placeholderImage.getHeight());
if (tmp.getWidth() > placeholderImage.getWidth()) {
int diff = tmp.getWidth() - placeholderImage.getWidth();
int x = diff / 2;
tmp = tmp.subImage(x, 0, placeholderImage.getWidth(), placeholderImage.getHeight(), true);
} else if (tmp.getHeight() > placeholderImage.getHeight()) {
int diff = tmp.getHeight() - placeholderImage.getHeight();
int y = diff / 2;
tmp = tmp.subImage(0, y, Math.min(placeholderImage.getWidth(), tmp.getWidth()),
Math.min(placeholderImage.getHeight(), tmp.getHeight()), true);
}
Image roundMask = Image.createImage(tmp.getWidth(), tmp.getHeight(), 0xff000000);
Graphics gr = roundMask.getGraphics();
gr.setColor(0xffffff);
gr.fillArc(0, 0, tmp.getWidth(), tmp.getHeight(), 0, 360);
Object mask = roundMask.createMask();
tmp = tmp.applyMask(mask);
return EncodedImage.createFromImage(tmp, false);
}
#Override
public boolean isAsyncAdapter() {
return true;
}
};
Then apply it this way:
public void setUpProfile(Form f) {
Label imageLabel = findMyImage(f);
Image img = getImageFromRes("myprofile.png");
Image scaled = img.scaledWidth(f.getWidth() / 2);
EncodedImage enc = EncodedImage.createFromImage(scaled, false);
Display.getInstance().callSerially(new Runnable() {
#Override
public void run() {
imageLabel.setIcon(URLImage.createToStorage(enc,
"profileImage8", me.getPicture(), RESIZE_SCALE_WITH_ROUND_MASK));
f.revalidate();
}
});
findProfNameLabel(f).setText(me.getName());
findProfAgeLabel(f).setText(me.getAge() + " Years old");
findProfPrefLabel(f).setText("Interested in " + me.getPref());
}

Write multi line text on an image using Java

I looked at java.awt.Graphics documentation, stackoverflow, could not find a solution. I have in input two things, an image file and the multi line text (paragraph). I need to write the multi line text on the image file and then save it as a new image. Wondering if I am missing something really simple here.
I am open to using any good third party libraries as well.
final BufferedImage image = ImageIO.read(new File("c:/anil/Lenna.png"));
Graphics g = image.getGraphics();
g.setFont(g.getFont().deriveFont(30f));
g.drawString("Hello world", 100, 100);
g.dispose();
Above code writes just a single line on the image.
if you want to draw several lines you have to do it explicitly...
so first step is to 'detect' lines
String str = ... //some text with line breaks;
String [] lines = str.spilt("\n"); //breaking the lines into an array
second step is to draw all lines
Graphics g = image.getGraphics();
g.setFont(g.getFont().deriveFont(30f));
int lineHeight = g.getFontMetrics().getHeight();
//here comes the iteration over all lines
for(int lineCount = 0; lineCount < lines.length; lineCount ++){ //lines from above
int xPos = 100;
int yPos = 100 + lineCount * lineHeight;
String line = lines[lineCount];
g.drawString(line, xpos, yPos);
}
g.dispose();
JLabel accepts simple html to format text. Then you can paint it on your image:
JLabel l=new JLabel("<html>line1<br>line2");
l.setSize(l.getPrefferedSize());
g.translate(10,10); // fixed location
l.paint(g);
edit: complete example
public class OP_32110247 extends JPanel {
private final JLabel l = new JLabel();
private final BufferedImage image;
public OP_32110247(String imgfile, String txt) throws IOException {
image = ImageIO.read(new URL(imgfile));
l.setText(txt);
l.setFont(getFont().deriveFont(Font.BOLD,30f));
l.setSize(l.getPreferredSize());
l.setForeground(Color.GREEN);
Dimension d = new Dimension(image.getWidth(), image.getHeight());
setPreferredSize(d);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Dimension d = getSize();
g.drawImage(image, 0, 0, null);
//place text in center of image
g.translate((d.width-l.getWidth())/2, (d.height-l.getHeight())/2);
l.paint(g);
}
public static void main(String[] args) throws IOException {
String txt = "<html>line1<br>line2";
String image = "http://kysoft.pl/proj/java/j+c.png";
JFrame f = new JFrame();
f.setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
f.setContentPane(new OP_32110247(image,txt));
f.pack();
f.setVisible(true);
}
}
The best solution is to extend JLabel and override paintComponent. Create the Label with the image required. After calling super.paintComponent draw your text, each line positioning below another using the font metrics. Something like below:
class ImageWithTextLabel extends JLabel {
List<String> lines = new ArrayList<>();
Point textPosition = new Point(0,0);
private Font textFont;
private ImageWithTextLabel(Icon image) {
super(image);
}
public void addText(String text) {
lines.add(text);
}
public void setTextPosition(Point textPosition) {
this.textPosition = textPosition;
}
#Override protected void paintComponent(Graphics g) {
super.paintComponent(g);
int from = textPosition.y ;
g.setFont(textFont);
for (String line : lines) {
g.drawString(line, textPosition.x, from);
from += g.getFontMetrics().getHeight();
}
}
public void setTextFont(Font font) {
textFont = font;
}
}
see Drawing Multiple Lines of Text(Oracle Java Tutorials) and complete code
use LineBreakMeasurer:
int width = 400;
int height = 500;
BufferedImage bufferedImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bufferedImage.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2d.setColor(Color.MAGENTA);
Hashtable<TextAttribute,Object> map = new Hashtable<TextAttribute,Object>();
map.put(TextAttribute.FAMILY, "微软雅黑");
map.put(TextAttribute.SIZE,new Float(18.0));
AttributedString vanGogh = new AttributedString(
"Many people 中国 believe that Vincent van Gogh painted his best works " +
"during the two-year period he spent in Provence. Here is where he " +
"painted The Starry Night--which some consider to be his greatest " +
"work of all. However, as his artistic brilliance reached new " +
"heights in Provence, his physical and mental health plummeted. ",
map);
AttributedCharacterIterator paragraph = vanGogh.getIterator();
int paragraphStart = paragraph.getBeginIndex();
int paragraphEnd = paragraph.getEndIndex();
FontRenderContext frc = g2d.getFontRenderContext();
LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(paragraph, frc);
float breakWidth = 250f;
float drawPosY = 20;
float drawPosx = 0f;
lineMeasurer.setPosition(paragraphStart);
while(lineMeasurer.getPosition()< paragraphEnd ){
TextLayout layout = lineMeasurer.nextLayout(breakWidth);
drawPosx = layout.isLeftToRight()?0:breakWidth-layout.getAdvance();
drawPosY += layout.getAscent();
layout.draw(g2d,drawPosx,drawPosY);
drawPosY += layout.getDescent() + layout.getLeading();
}
g2d.dispose();
File file = new File("myimage.png");
ImageIO.write(bufferedImage,"png",file);
file = new File("myimage.jpg");
ImageIO.write(bufferedImage,"jpg",file);

Printable not working properly

I have a system where the user can register students, subjects, and the student grade in each subject, together with the topic and the date. You can search a particular student grades in a particular subject by entering the code of the student, and by selecting the subject from a combobox. If you search it, those things are displayed in the jTable1.
Then, I have a PRINT button. When the user clicks the Print button, the content that is being displayed in the jTable1, goes to a jTable2, the difference between those 2 tables is that the jTable1 displays the name of the student, and the name of the subject, but the jTable2 doesn't. Here is a pic for better understanding:
http://i.stack.imgur.com/37SNh.png
So, when the user clicked the button to Print the jTable2, I was using this code right here:
MessageFormat header = new MessageFormat("Ficha Pedagógica - "+jComboBox1.getSelectedItem());
MessageFormat footer = new MessageFormat("Página {0,number,integer}");
try{
jTable2.print(JTable.PrintMode.NORMAL, header, null);
}
catch(java.awt.print.PrinterException e){
System.out.println("error");
}
The fact is, that I wanted 2 headlines to be printed, but such thing couldn't be achieved using the built-in print function. So, here in Stack Overflow, I found this topic:
How to print multiple header lines with MessageFormat using a JTable
After I found this, I tried using the code given there. Since I'm a beginner, even with all the comments in the code, I couldn't fully understand it. So, I tried to implement it, but now, when I click the "Print" button, nothing happens. This is my code of the Print button:
private void jButton4ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
try{
Class.forName(driver);
con = DriverManager.getConnection(str_conn,usuario,senha);
stmt = con.createStatement();
sql = "select topico, nota, datanota from notas where notas.cod_aluno ="+jTextField1.getText()+" and notas.cod_curso ="+jTextField2.getText()+" order by notas.datanota";
rs = stmt.executeQuery(sql);
if(rs == null){
return;
}
ResultSetMetaData rsmd;
rsmd = rs.getMetaData();
Vector vetColuna = new Vector();
for(int i = 0;i<rsmd.getColumnCount();i++){
vetColuna.add(rsmd.getColumnLabel(i+1));
}
Vector vetLinhas = new Vector();
while(rs.next()){
Vector vetLinha = new Vector();
for(int i = 0;i<rsmd.getColumnCount();i++){
vetLinha.add(rs.getObject(i+1));
}
vetLinhas.add(vetLinha);
jTable2.setModel(new DefaultTableModel(vetLinhas,vetColuna));
}
}catch(ClassNotFoundException ex){
JOptionPane.showMessageDialog(null,"Erro\nNão foi possível carregar o driver.");
System.out.println("Nao foi possivel carregar o driver");
ex.printStackTrace();
}catch(SQLException ex){
JOptionPane.showMessageDialog(null,"Erro\nCertifique-se de que todos os\ncampos estejam preenchidos corretamente.");
System.out.println("Problema com o SQL");
ex.printStackTrace();
}
/*MessageFormat header = new MessageFormat("Ficha Pedagógica - "+jComboBox1.getSelectedItem());
MessageFormat footer = new MessageFormat("Página {0,number,integer}");
try{
jTable2.print(JTable.PrintMode.NORMAL, header, null);
}
catch(java.awt.print.PrinterException e){
System.out.println("gsgd");
}*/
DefaultTableModel dtm = new DefaultTableModel(new String[] { "Column 1" }, 1);
JTable jTable2 = new JTable(dtm) {
#Override
public Printable getPrintable(PrintMode printMode, MessageFormat headerFormat, MessageFormat footerFormat) {
return new TablePrintable(this, printMode, headerFormat, footerFormat);
}
};
}
The code for the "TablePrintable" class is the following:
static class TablePrintable implements Printable {
private final JTable table;
private final JTableHeader header;
private final TableColumnModel colModel;
private final int totalColWidth;
private final JTable.PrintMode printMode;
private final MessageFormat headerFormat;
private final MessageFormat footerFormat;
private int last = -1;
private int row = 0;
private int col = 0;
private final Rectangle clip = new Rectangle(0, 0, 0, 0);
private final Rectangle hclip = new Rectangle(0, 0, 0, 0);
private final Rectangle tempRect = new Rectangle(0, 0, 0, 0);
private static final int H_F_SPACE = 8;
private static final float HEADER_FONT_SIZE = 18.0f;
private static final float FOOTER_FONT_SIZE = 12.0f;
private final Font headerFont;
private final Font footerFont;
public TablePrintable(JTable table, JTable.PrintMode printMode, MessageFormat headerFormat,
MessageFormat footerFormat) {
this.table = table;
header = table.getTableHeader();
colModel = table.getColumnModel();
totalColWidth = colModel.getTotalColumnWidth();
if (header != null) {
// the header clip height can be set once since it's unchanging
hclip.height = header.getHeight();
}
this.printMode = printMode;
this.headerFormat = headerFormat;
this.footerFormat = footerFormat;
// derive the header and footer font from the table's font
headerFont = table.getFont().deriveFont(Font.BOLD, HEADER_FONT_SIZE);
footerFont = table.getFont().deriveFont(Font.PLAIN, FOOTER_FONT_SIZE);
}
#Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
// for easy access to these values
final int imgWidth = (int) pageFormat.getImageableWidth();
final int imgHeight = (int) pageFormat.getImageableHeight();
if (imgWidth <= 0) {
throw new PrinterException("Width of printable area is too small.");
}
// to pass the page number when formatting the header and footer
// text
Object[] pageNumber = new Object[] { Integer.valueOf(pageIndex + 1) };
// fetch the formatted header text, if any
String headerText = null;
if (headerFormat != null) {
headerText = headerFormat.format(pageNumber);
}
// fetch the formatted footer text, if any
String footerText = null;
if (footerFormat != null) {
footerText = footerFormat.format(pageNumber);
}
// to store the bounds of the header and footer text
Rectangle2D hRect = null;
Rectangle2D fRect = null;
// the amount of vertical space needed for the header and footer
// text
int headerTextSpace = 0;
int footerTextSpace = 0;
// the amount of vertical space available for printing the table
int availableSpace = imgHeight;
// if there's header text, find out how much space is needed for it
// and subtract that from the available space
if (headerText != null) {
graphics.setFont(headerFont);
int nbLines = headerText.split("\n").length;
hRect = graphics.getFontMetrics().getStringBounds(headerText, graphics);
hRect = new Rectangle2D.Double(hRect.getX(), Math.abs(hRect.getY()), hRect.getWidth(),
hRect.getHeight() * nbLines);
headerTextSpace = (int) Math.ceil(hRect.getHeight() * nbLines);
availableSpace -= headerTextSpace + H_F_SPACE;
}
// if there's footer text, find out how much space is needed for it
// and subtract that from the available space
if (footerText != null) {
graphics.setFont(footerFont);
fRect = graphics.getFontMetrics().getStringBounds(footerText, graphics);
footerTextSpace = (int) Math.ceil(fRect.getHeight());
availableSpace -= footerTextSpace + H_F_SPACE;
}
if (availableSpace <= 0) {
throw new PrinterException("Height of printable area is too small.");
}
// depending on the print mode, we may need a scale factor to
// fit the table's entire width on the page
double sf = 1.0D;
if (printMode == JTable.PrintMode.FIT_WIDTH && totalColWidth > imgWidth) {
// if not, we would have thrown an acception previously
assert imgWidth > 0;
// it must be, according to the if-condition, since imgWidth > 0
assert totalColWidth > 1;
sf = (double) imgWidth / (double) totalColWidth;
}
// dictated by the previous two assertions
assert sf > 0;
// This is in a loop for two reasons:
// First, it allows us to catch up in case we're called starting
// with a non-zero pageIndex. Second, we know that we can be called
// for the same page multiple times. The condition of this while
// loop acts as a check, ensuring that we don't attempt to do the
// calculations again when we are called subsequent times for the
// same page.
while (last < pageIndex) {
// if we are finished all columns in all rows
if (row >= table.getRowCount() && col == 0) {
return NO_SUCH_PAGE;
}
// rather than multiplying every row and column by the scale
// factor
// in findNextClip, just pass a width and height that have
// already
// been divided by it
int scaledWidth = (int) (imgWidth / sf);
int scaledHeight = (int) ((availableSpace - hclip.height) / sf);
// calculate the area of the table to be printed for this page
findNextClip(scaledWidth, scaledHeight);
last++;
}
// create a copy of the graphics so we don't affect the one given to
// us
Graphics2D g2d = (Graphics2D) graphics.create();
// translate into the co-ordinate system of the pageFormat
g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
// to save and store the transform
AffineTransform oldTrans;
// if there's footer text, print it at the bottom of the imageable
// area
if (footerText != null) {
oldTrans = g2d.getTransform();
g2d.translate(0, imgHeight - footerTextSpace);
String[] lines = footerText.split("\n");
printText(g2d, lines, fRect, footerFont, imgWidth);
g2d.setTransform(oldTrans);
}
// if there's header text, print it at the top of the imageable area
// and then translate downwards
if (headerText != null) {
String[] lines = headerText.split("\n");
printText(g2d, lines, hRect, headerFont, imgWidth);
g2d.translate(0, headerTextSpace + H_F_SPACE);
}
// constrain the table output to the available space
tempRect.x = 0;
tempRect.y = 0;
tempRect.width = imgWidth;
tempRect.height = availableSpace;
g2d.clip(tempRect);
// if we have a scale factor, scale the graphics object to fit
// the entire width
if (sf != 1.0D) {
g2d.scale(sf, sf);
// otherwise, ensure that the current portion of the table is
// centered horizontally
} else {
int diff = (imgWidth - clip.width) / 2;
g2d.translate(diff, 0);
}
// store the old transform and clip for later restoration
oldTrans = g2d.getTransform();
Shape oldClip = g2d.getClip();
// if there's a table header, print the current section and
// then translate downwards
if (header != null) {
hclip.x = clip.x;
hclip.width = clip.width;
g2d.translate(-hclip.x, 0);
g2d.clip(hclip);
header.print(g2d);
// restore the original transform and clip
g2d.setTransform(oldTrans);
g2d.setClip(oldClip);
// translate downwards
g2d.translate(0, hclip.height);
}
// print the current section of the table
g2d.translate(-clip.x, -clip.y);
g2d.clip(clip);
table.print(g2d);
// restore the original transform and clip
g2d.setTransform(oldTrans);
g2d.setClip(oldClip);
// draw a box around the table
g2d.setColor(Color.BLACK);
g2d.drawRect(0, 0, clip.width, hclip.height + clip.height);
// dispose the graphics copy
g2d.dispose();
return PAGE_EXISTS;
}
private void printText(Graphics2D g2d, String[] lines, Rectangle2D rect, Font font, int imgWidth) {
g2d.setColor(Color.BLACK);
g2d.setFont(font);
for (int i = 0; i < lines.length; i++) {
int tx;
// if the text is small enough to fit, center it
if (rect.getWidth() < imgWidth) {
tx = (int) (imgWidth / 2 - g2d.getFontMetrics().getStringBounds(lines[i], g2d).getWidth() / 2);
// otherwise, if the table is LTR, ensure the left side of
// the text shows; the right can be clipped
} else if (table.getComponentOrientation().isLeftToRight()) {
tx = 0;
// otherwise, ensure the right side of the text shows
} else {
tx = -(int) (Math.ceil(rect.getWidth()) - imgWidth);
}
int ty = (int) Math.ceil(Math.abs(rect.getY() + i * rect.getHeight() / lines.length));
g2d.drawString(lines[i], tx, ty);
}
}
private void findNextClip(int pw, int ph) {
final boolean ltr = table.getComponentOrientation().isLeftToRight();
// if we're ready to start a new set of rows
if (col == 0) {
if (ltr) {
// adjust clip to the left of the first column
clip.x = 0;
} else {
// adjust clip to the right of the first column
clip.x = totalColWidth;
}
// adjust clip to the top of the next set of rows
clip.y += clip.height;
// adjust clip width and height to be zero
clip.width = 0;
clip.height = 0;
// fit as many rows as possible, and at least one
int rowCount = table.getRowCount();
int rowHeight = table.getRowHeight(row);
do {
clip.height += rowHeight;
if (++row >= rowCount) {
break;
}
rowHeight = table.getRowHeight(row);
} while (clip.height + rowHeight <= ph);
}
// we can short-circuit for JTable.PrintMode.FIT_WIDTH since
// we'll always fit all columns on the page
if (printMode == JTable.PrintMode.FIT_WIDTH) {
clip.x = 0;
clip.width = totalColWidth;
return;
}
if (ltr) {
// adjust clip to the left of the next set of columns
clip.x += clip.width;
}
// adjust clip width to be zero
clip.width = 0;
// fit as many columns as possible, and at least one
int colCount = table.getColumnCount();
int colWidth = colModel.getColumn(col).getWidth();
do {
clip.width += colWidth;
if (!ltr) {
clip.x -= colWidth;
}
if (++col >= colCount) {
// reset col to 0 to indicate we're finished all columns
col = 0;
break;
}
colWidth = colModel.getColumn(col).getWidth();
} while (clip.width + colWidth <= pw);
}
}
But, like I said, when I click the "print" button, nothing happens. What could be going wrong?
With your newly modify JTable, you should only need to call it's print method.
DefaultTableModel dtm = new DefaultTableModel(new String[] { "Column 1" }, 1);
JTable jTable2 = new JTable(dtm) {
#Override
public Printable getPrintable(PrintMode printMode, MessageFormat headerFormat, MessageFormat footerFormat) {
return new TablePrintable(this, printMode, headerFormat, footerFormat);
}
};
try{
jTable2.print(JTable.PrintMode.NORMAL, header, null);
}
catch(java.awt.print.PrinterException e){
System.out.println("error");
}
Because you've overridden the getPrintable method to return your own implementation, this is what will be used to physically print the table...
Updated
The header text needs to be separated by a \n, for example...
MessageFormat header = new MessageFormat("Testing, 01\n02\n03");
Which can produce...
Updated
As near as I can tell, without been able to fully run the code, your print code should look something like...
private void jButton4ActionPerformed(java.awt.event.ActionEvent evt) {
Vector vetColuna = new Vector();
Vector vetLinhas = new Vector();
try {
Class.forName(driver);
con = DriverManager.getConnection(str_conn, usuario, senha);
stmt = con.createStatement();
sql = "select topico, nota, datanota from notas where notas.cod_aluno =" + jTextField1.getText() + " and notas.cod_curso =" + jTextField2.getText() + " order by notas.datanota";
rs = stmt.executeQuery(sql);
if (rs == null) {
return;
}
ResultSetMetaData rsmd;
rsmd = rs.getMetaData();
for (int i = 0; i < rsmd.getColumnCount(); i++) {
vetColuna.add(rsmd.getColumnLabel(i + 1));
}
while (rs.next()) {
Vector vetLinha = new Vector();
for (int i = 0; i < rsmd.getColumnCount(); i++) {
vetLinha.add(rs.getObject(i + 1));
}
vetLinhas.add(vetLinha);
}
} catch (ClassNotFoundException ex) {
JOptionPane.showMessageDialog(null, "Erro\nNão foi possível carregar o driver.");
System.out.println("Nao foi possivel carregar o driver");
ex.printStackTrace();
} catch (SQLException ex) {
JOptionPane.showMessageDialog(null, "Erro\nCertifique-se de que todos os\ncampos estejam preenchidos corretamente.");
System.out.println("Problema com o SQL");
ex.printStackTrace();
}
MessageFormat header = new MessageFormat("Ficha Pedagógica - " + jComboBox1.getSelectedItem() + "\nNome do Aluno - " + jTextField1.getText());
DefaultTableModel dtm = new DefaultTableModel(vetLinhas, vetColuna);
JTable jTable2 = new JTable(dtm) {
#Override
public Printable getPrintable(PrintMode printMode, MessageFormat headerFormat, MessageFormat footerFormat) {
return new TablePrintable(this, printMode, headerFormat, footerFormat);
}
};
try {
jTable2.setSize(jTable2.getPreferredSize());
JTableHeader tableHeader = jTable2.getTableHeader();
tableHeader.setSize(tableHeader.getPreferredSize());
jTable2.print(JTable.PrintMode.FIT_WIDTH);
} catch (PrinterException ex) {
ex.printStackTrace();
}
}

Loading an animated image to a BufferedImage array

I'm trying to implement animated textures into an OpenGL game seamlessly. I made a generic ImageDecoder class to translate any BufferedImage into a ByteBuffer. It works perfectly for now, though it doesn't load animated images.
I'm not trying to load an animated image as an ImageIcon. I need the BufferedImage to get an OpenGL-compliant ByteBuffer.
How can I load every frames as a BufferedImage array in an animated image ?
On a similar note, how can I get the animation rate / period ?
Does Java handle APNG ?
The following code is an adaption from my own implementation to accommodate the "into array" part.
The problem with gifs is: There are different disposal methods which have to be considered, if you want this to work with all of them. The code below tries to compensate for that. For example there is a special implementation for "doNotDispose" mode, which takes all frames from start to N and paints them on top of each other into a BufferedImage.
The advantage of this method over the one posted by chubbsondubs is that it does not have to wait for the gif animation delays, but can be done basically instantly.
BufferedImage[] array = null;
ImageInputStream imageInputStream = ImageIO.createImageInputStream(new ByteArrayInputStream(data)); // or any other source stream
Iterator<ImageReader> imageReaders = ImageIO.getImageReaders(imageInputStream);
while (imageReaders.hasNext())
{
ImageReader reader = (ImageReader) imageReaders.next();
try
{
reader.setInput(imageInputStream);
frames = reader.getNumImages(true);
array = new BufferedImage[frames];
for (int frameId : frames)
{
int w = reader.getWidth(0);
int h = reader.getHeight(0);
int fw = reader.getWidth(frameId);
int fh = reader.getHeight(frameId);
if (h != fh || w != fw)
{
GifMeta gm = getGifMeta(reader.getImageMetadata(frameId));
// disposalMethodNames: "none", "doNotDispose","restoreToBackgroundColor","restoreToPrevious",
if ("doNotDispose".equals(gm.disposalMethod))
{
image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = (Graphics2D) image.getGraphics();
for (int f = 0; f <= frameId; f++)
{
gm = getGifMeta(reader.getImageMetadata(f));
if ("doNotDispose".equals(gm.disposalMethod))
{
g.drawImage(reader.read(f), null, gm.imageLeftPosition, gm.imageTopPosition);
}
else
{
// XXX "Unimplemented disposalMethod (" + getName() + "): " + gm.disposalMethod);
}
}
g.dispose();
}
else
{
image = reader.read(frameId);
// XXX "Unimplemented disposalMethod (" + getName() + "): " + gm.disposalMethod;
}
}
else
{
image = reader.read(frameId);
}
if (image == null)
{
throw new NullPointerException();
}
array[frame] = image;
}
}
finally
{
reader.dispose();
}
}
return array;
private final static class GifMeta
{
String disposalMethod = "none";
int imageLeftPosition = 0;
int imageTopPosition = 0;
int delayTime = 0;
}
private GifMeta getGifMeta(IIOMetadata meta)
{
GifMeta gm = new GifMeta();
final IIOMetadataNode gifMeta = (IIOMetadataNode) meta.getAsTree("javax_imageio_gif_image_1.0");
NodeList childNodes = gifMeta.getChildNodes();
for (int i = 0; i < childNodes.getLength(); ++i)
{
IIOMetadataNode subnode = (IIOMetadataNode) childNodes.item(i);
if (subnode.getNodeName().equals("GraphicControlExtension"))
{
gm.disposalMethod = subnode.getAttribute("disposalMethod");
gm.delayTime = Integer.parseInt(subnode.getAttribute("delayTime"));
}
else if (subnode.getNodeName().equals("ImageDescriptor"))
{
gm.imageLeftPosition = Integer.parseInt(subnode.getAttribute("imageLeftPosition"));
gm.imageTopPosition = Integer.parseInt(subnode.getAttribute("imageTopPosition"));
}
}
return gm;
}
I don't think Java supports APNG by default, but you can use an 3rd party library to parse it:
http://code.google.com/p/javapng/source/browse/trunk/javapng2/src/apng/com/sixlegs/png/AnimatedPngImage.java?r=300
That might be your easiest method. As for getting the frames from an animated gif you have to register an ImageObserver:
new ImageIcon( url ).setImageObserver( new ImageObserver() {
public void imageUpdate( Image img, int infoFlags, int x, int y, int width, int height ) {
if( infoFlags & ImageObserver.FRAMEBITS == ImageObserver.FRAMEBITS ) {
// another frame was loaded do something with it.
}
}
});
This loads asynchronously on another thread so imageUpdate() won't be called immediately. But it will be called for each frame as it parses it.
http://docs.oracle.com/javase/1.4.2/docs/api/java/awt/image/ImageObserver.html

Categories