0

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 : https://i.stack.imgur.com/47fFk.jpg Expected https://i.stack.imgur.com/xiRhd.jpg

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;
}
user973179
  • 21
  • 7
  • Why not use a `JTextPane`? – trashgod Jul 25 '16 at 11:51
  • JTextPane cannot be an option as it first creates the frame, makes it visible, writes it and then disposes it. I dont want that popup to come and disappear. I have attached the code that i used using jtextpane. Check that. And most of the times i was getting Nullpointer exception on SwingUtilities.invokeAndWait. – user973179 Jul 25 '16 at 12:33
  • You can validate the pane without making it visible, as suggested [here](http://stackoverflow.com/a/13139308/230513); see also [*Initial Threads*](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html). – trashgod Jul 25 '16 at 15:38

1 Answers1

0

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
Rajan Panneer Selvam
  • 1,279
  • 10
  • 24
  • Yeah, thats right but thats not the issue i want to solve. Issue i want to solve here is that when it figures out that line will overflow, then it splits it. So when it writes the second line fromt hat split, it should take care of next string also i.e "See details" as it can also be accommodated in same line instead of sending it to next line. See my updated code that solves the issue that you mentioned. – user973179 Jul 25 '16 at 10:44
  • Can you please update your post with expected and actuals? (probably you can use "code" formatting).. Just to make it clear. – Rajan Panneer Selvam Jul 25 '16 at 10:53
  • I dont have reputation to ad images, please see the links i have added – user973179 Jul 25 '16 at 11:03
  • You have to split the calculation and rendering to achieve this.. see my edits. – Rajan Panneer Selvam Jul 25 '16 at 12:16
  • Well, thats what i needed help for. Can you please give me some pseudocode for that – user973179 Jul 25 '16 at 12:30