/** * Process this text Token to optimize tokens and evaluate the token bounding box. * * @param g Graphics to get the font metrics. * @param text Text to add to the current textTokan. * @param textTok Current TextToken. * @param isText True if the previous textToken was a Text Token so we can merge it with this. */ private void updateText(Graphics g, String text, TextToken textTok, boolean isText) { // The text exists! if (text.length() > 0) { FontMetrics fm = g.getFontMetrics(); int a = fm.getAscent(), d = fm.getDescent(), w = fm.stringWidth(text), h = fm.getHeight(); // The previous token was a text too so we must merge it with this new one. if (isText) { textTok = (TextToken) m_tokens.lastElement(); textTok.m_text += text; textTok.m_bounds.width += w; if (textTok.m_bounds.height < h) textTok.m_bounds.height = h; } // The previous token was a formating one. else { m_tokens.addElement(textTok); textTok.m_text = text; textTok.m_bounds = new Rectangle(0, 0, w, h); } if (a > m_body.m_aMax) m_body.m_aMax = a; if (d > m_body.m_dMax) m_body.m_dMax = d; m_wCur += w; } }
/** * Creates a new HTMLText and sets its default formatting properties. * * @param inCol Color of the bounding box background. * @param outCol Color of the bounding box border. * @param textCol Default text color. * @param fontSiz Default font size. * @param fontStl Default font style. * @param fontNam Default font name. * @param flags Default text alignment flags. * @param margin Default margins size. */ public HTMLText( Color inCol, Color outCol, int textCol, int fontSiz, int fontStl, String fontNam, int blur, int rounded, int flags, Insets margin) { m_body = new FormatToken(); m_body.m_flags = flags; m_body.m_margin = margin; m_inCol = inCol; m_outCol = outCol; m_color = textCol; m_bkCol = -1; m_style = fontStl; m_size = fontSiz; m_name = fontNam; m_blur = blur; m_rounded = rounded; m_wCur = 0; m_tokens = new Vector(); m_heap = new Vector(); m_heap.addElement("c=#" + Integer.toHexString(textCol)); m_heap.addElement("s=" + fontSiz); m_heap.addElement("f=" + fontNam); if ((fontStl & Font.BOLD) != 0) m_heap.addElement("b"); if ((fontStl & Font.ITALIC) != 0) m_heap.addElement("i"); }
/** * get next format token Note: Supported format specifieres i,d,u - decimal o - octal x,X - * hexa-decimal c - character e,E,f,F,g,G,a,A - float/double s - string S - string with quotes y - * boolean Supported options: #,0,-, ,+,* - flags 1-9 - width . - precision {<name>}s - * enumeration <name> <x>s, <x>S - quoting character <x> <space>s,<space>S - accept spaces, ignore * quotes *s - accept all to eol h,l,j,z,t - length modifier * * @param format format string * @param formatIndex index in format string * @param formatToken format token * @return next format string index */ private static int getNextFormatToken(String format, int formatIndex, FormatToken formatToken) { formatToken.token = new StringBuffer(); formatToken.length = 0; formatToken.alternateFlag = false; formatToken.zeroPaddingFlag = false; formatToken.leftAdjustedFlag = false; formatToken.blankFlag = false; formatToken.greedyFlag = false; formatToken.width = 0; formatToken.precision = 0; formatToken.lengthType = LengthTypes.INTEGER; formatToken.quoteChar = '\0'; formatToken.enumClassName = null; formatToken.conversionChar = '\0'; /* format start character */ assert format.charAt(formatIndex) == '%'; formatToken.token.append('%'); formatIndex++; if (formatIndex >= format.length()) { return -1; } /* flags */ while ((formatIndex < format.length()) && ((format.charAt(formatIndex) == '#') || (format.charAt(formatIndex) == '0') || (format.charAt(formatIndex) == '-') || (format.charAt(formatIndex) == ' ') || (format.charAt(formatIndex) == '+') || (format.charAt(formatIndex) == '*'))) { formatToken.token.append(format.charAt(formatIndex)); switch (format.charAt(formatIndex)) { case '#': formatToken.alternateFlag = true; break; case '0': formatToken.zeroPaddingFlag = true; break; case '-': formatToken.leftAdjustedFlag = true; break; case ' ': formatToken.blankFlag = true; break; case '+': formatToken.blankFlag = true; break; case '*': formatToken.greedyFlag = true; break; default: return -1; } formatIndex++; } if (formatIndex >= format.length()) { return -1; } /* width, precision */ while ((formatIndex < format.length()) && (Character.isDigit(format.charAt(formatIndex)))) { formatToken.token.append(format.charAt(formatIndex)); formatToken.width = formatToken.width * 10 + (format.charAt(formatIndex) - '0'); formatIndex++; } if (formatIndex >= format.length()) { return -1; } /* precision */ if (format.charAt(formatIndex) == '.') { formatToken.token.append(format.charAt(formatIndex)); formatIndex++; while ((formatIndex < format.length()) && Character.isDigit(format.charAt(formatIndex))) { formatToken.token.append(format.charAt(formatIndex)); formatToken.precision = formatToken.precision * 10 + (format.charAt(formatIndex) - '0'); formatIndex++; } } /* quoting character */ if ((formatIndex + 1 < format.length()) && (format.charAt(formatIndex) != '{') && ((format.charAt(formatIndex + 1) == 's') || (format.charAt(formatIndex + 1) == 'S'))) { formatToken.quoteChar = format.charAt(formatIndex); formatIndex++; } /* length modifier */ if ((formatIndex + 1 < format.length()) && (format.charAt(formatIndex) == 'h') && (format.charAt(formatIndex + 1) == 'h')) { formatToken.token.append(format.charAt(formatIndex + 0)); formatToken.token.append(format.charAt(formatIndex + 1)); formatToken.lengthType = LengthTypes.INTEGER; formatIndex += 2; } else if ((formatIndex < format.length()) && (format.charAt(formatIndex) == 'h')) { formatToken.token.append(format.charAt(formatIndex)); formatToken.lengthType = LengthTypes.INTEGER; formatIndex++; } else if ((formatIndex + 1 < format.length()) && (format.charAt(formatIndex) == 'l') && (format.charAt(formatIndex + 1) == 'l')) { formatToken.token.append(format.charAt(formatIndex + 0)); formatToken.token.append(format.charAt(formatIndex + 1)); formatToken.lengthType = LengthTypes.LONG; formatIndex += 2; } else if ((formatIndex < format.length()) && (format.charAt(formatIndex) == 'l')) { formatToken.token.append(format.charAt(formatIndex)); formatToken.lengthType = LengthTypes.LONG; formatIndex++; } else if ((formatIndex < format.length()) && (format.charAt(formatIndex) == 'j')) { formatToken.token.append(format.charAt(formatIndex)); formatToken.lengthType = LengthTypes.INTEGER; formatIndex++; } else if ((formatIndex < format.length()) && (format.charAt(formatIndex) == 'z')) { formatToken.token.append(format.charAt(formatIndex)); formatToken.lengthType = LengthTypes.INTEGER; formatIndex++; } else if ((formatIndex < format.length()) && (format.charAt(formatIndex) == 't')) { formatToken.token.append(format.charAt(formatIndex)); formatToken.lengthType = LengthTypes.INTEGER; formatIndex++; } if ((formatIndex < format.length()) && (format.charAt(formatIndex) == '{')) { /* enum name */ formatToken.token.append(format.charAt(formatIndex)); formatIndex++; StringBuffer buffer = new StringBuffer(); while ((formatIndex < format.length()) && (format.charAt(formatIndex) != '}')) { char ch = format.charAt(formatIndex); formatToken.token.append(ch); buffer.append((ch != '.') ? ch : '$'); formatIndex++; } formatIndex++; formatToken.enumClassName = buffer.toString(); } if (formatIndex >= format.length()) { return -1; } /* conversion character */ switch (format.charAt(formatIndex)) { case 'S': formatToken.token.append('s'); formatToken.conversionChar = 'S'; break; default: formatToken.token.append(format.charAt(formatIndex)); formatToken.conversionChar = format.charAt(formatIndex); break; } formatIndex++; return formatIndex; }
/** * Creates a new TextToken by parsing a pseudo-HTML tag. * * @param g Graphics used to retrieve the font metric. * @param tag A pseudo HTML tag without '<' and '>'. * @return a new TextToken initialized according to the tag. */ private TextToken updateTag(Graphics g, String tag) { String tempTag; TextToken textTok = null; char begChar; if (tag.length() > 0) { tag = tag.toLowerCase(); begChar = tag.charAt(0); // End of Tag, we returns except for the case </p> if (begChar == '/') { char nxtChar = tag.charAt(1); tempTag = (String) m_heap.lastElement(); if (tempTag.charAt(0) == nxtChar) // ! very simple verification ! { textTok = closeTag(g, tempTag); if (nxtChar != 'p') return textTok; } else { System.out.println("[updateTag] no corresponding opened Tag : " + tag); return null; } } FormatToken prevTok = m_curTok; Insets prevMrg = prevTok.m_margin, margin = m_body.m_margin; int flags = m_body.m_flags, width = m_wCur + (prevMrg != null ? prevMrg.left + prevMrg.right : 0); // Start of Tag + </p> if (tag.equals("br") || begChar == 'p' || tag.equals("/p")) { if (tag.equals("br")) { if (prevMrg != null) margin = prevMrg; flags = prevTok.m_flags; } // We specify new margins if (begChar == 'p') { String alignStr = readAtt(tag, "a"); if (alignStr != null) { char align = Character.toLowerCase(alignStr.charAt(0)); flags = align == 'r' ? RIGHT_BIT : (align == 'c' ? CENTER_BIT : 0); } margin = readMargin(tag); if (tag.length() > 1 && alignStr == null && margin == null) { flags = m_body.m_flags; System.out.println("[updateTag] syntax error Tag : " + tag); return null; } else { m_heap.addElement(tag); } } // update pr�vious format Token prevTok.m_aMax = m_body.m_aMax; prevTok.m_dMax = m_body.m_dMax; prevTok.m_width = width; // reset current vars m_body.m_aMax = 0; m_body.m_dMax = 0; m_wCur = 0; // Stores the max width of all lines including its margins if (width > m_body.m_width) m_body.m_width = width; m_curTok = new FormatToken(); m_curTok.m_flags = flags; m_curTok.m_margin = margin == null ? m_body.m_margin : margin; m_tokens.addElement(m_curTok); textTok = new TextToken(); textTok.m_color = new Color(m_color); textTok.m_font = new Font(m_name, m_style, m_size); } else if (isGfx(begChar)) { textTok = updateGfx(g, tag); m_heap.addElement(tag); } else { System.out.println("[updateTag] Unknown Tag : " + tag); textTok = null; } } return textTok; }
/** * Parses this to extract the Tokens using a line of text. This is necessary to evaluate the * rendering of the text (color, size, alignment...). * * @param g The graphics used to retrieve the font metrics. * @param htmlText A string of text with or without HTML tags to parse. */ protected void parseText(Graphics g, String htmlText) { StringTokenizer tokenizer = new StringTokenizer(htmlText, "<>", true); String tokenStr, nextStr, prevStr = tokenizer.nextToken(); boolean hasMore = tokenizer.hasMoreTokens(), isText = false; Font font = new Font(m_name, m_style, m_size); TextToken textTok = new TextToken(); textTok.m_color = new Color(m_color); textTok.m_font = font; m_curTok = new FormatToken(); m_curTok.m_flags = m_body.m_flags; m_curTok.m_margin = m_body.m_margin; m_tokens.addElement(m_curTok); g.setFont(font); while (hasMore) { tokenStr = tokenizer.nextToken(); hasMore = tokenizer.hasMoreTokens(); // A start of Tag if (prevStr.equals("<")) { nextStr = hasMore ? tokenizer.nextToken() : null; // A closed Tag if (hasMore && nextStr.equals(">")) // tag { textTok = updateTag(g, tokenStr); // An real Tag if (textTok != null) { isText = false; } // An unknown Tag. Handle it as normal text. else { textTok = new TextToken(); updateText(g, "<" + tokenStr + ">", textTok, isText); isText = true; } prevStr = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null; } // An unclosed Tag. Handle it as normal text. else { updateText(g, "<" + tokenStr, textTok, isText); prevStr = nextStr; isText = true; } } // Normal text else { updateText(g, prevStr, textTok, isText); prevStr = tokenStr; isText = true; } hasMore = tokenizer.hasMoreTokens(); } // Don't forget the last or only piece of text if (prevStr != null) { updateText(g, prevStr, textTok, isText); } updateTag(g, "br"); // to set last line position updateBounds(); }