package ij.gui; import java.awt.*; import ij.*; import ij.process.*; import ij.util.*; import ij.macro.Interpreter; /** This class is a rectangular ROI containing text. */ public class TextRoi extends Roi { public static final int LEFT=0, CENTER=1, RIGHT=2; static final int MAX_LINES = 50; private static final String line1 = "Enter text, then press"; private static final String line2 = "ctrl+b to add to overlay"; private static final String line3 = "or ctrl+d to draw."; private String[] theText = new String[MAX_LINES]; private static String name = "SansSerif"; private static int style = Font.PLAIN; private static int size = 18; private Font instanceFont; private static boolean newFont = true; private static boolean antialiasedText = true; // global flag used by text tool private static int globalJustification; private int justification; private boolean antialiased = antialiasedText; private static boolean recordSetFont = true; private double previousMag; private boolean firstChar = true; private boolean firstMouseUp = true; private int cline = 0; /** Creates a new TextRoi.*/ public TextRoi(int x, int y, String text) { this(x, y, text, null); } /** Creates a new TextRoi with the specified location and Font. * @see ij.gui.Roi#setStrokeColor * @see ij.gui.Roi#setNonScalable * @see ij.ImagePlus#setOverlay(ij.gui.Overlay) */ public TextRoi(int x, int y, String text, Font font) { super(x, y, 1, 1); String[] lines = Tools.split(text, "\n"); int count = Math.min(lines.length, MAX_LINES); for (int i=0; i<count; i++) theText[i] = lines[i]; if (font==null) font = new Font(name, style, size); instanceFont = font; firstChar = false; if (IJ.debugMode) IJ.log("TextRoi: "+theText[0]+" "+width+","+height); } /** @deprecated */ public TextRoi(int x, int y, String text, Font font, Color color) { super(x, y, 1, 1); if (font==null) font = new Font(name, style, size); instanceFont = font; IJ.error("TextRoi", "API has changed. See updated example at\nhttp://imagej.nih.gov/ij/macros/js/TextOverlay.js"); } public TextRoi(int x, int y, ImagePlus imp) { super(x, y, imp); ImageCanvas ic = imp.getCanvas(); double mag = getMagnification(); if (mag>1.0) mag = 1.0; if (size<(12/mag)) size = (int)(12/mag); theText[0] = line1; theText[1] = line2; theText[2] = line3; if (previousRoi!=null && (previousRoi instanceof TextRoi)) { firstMouseUp = false; //IJ.write(""+previousRoi.getBounds()); previousRoi = null; } instanceFont = new Font(name, style, size); justification = globalJustification; } /** This method is used by the text tool to add typed characters to displayed text selections. */ public void addChar(char c) { if (imp==null) return; if (!(c>=' ' || c=='\b' || c=='\n')) return; if (firstChar) { cline = 0; theText[cline] = new String(""); for (int i=1; i<MAX_LINES; i++) theText[i] = null; } if ((int)c=='\b') { // backspace if (theText[cline].length()>0) theText[cline] = theText[cline].substring(0, theText[cline].length()-1); else if (cline>0) { theText[cline] = null; cline--; } imp.draw(clipX, clipY, clipWidth, clipHeight); firstChar = false; return; } else if ((int)c=='\n') { // newline if (cline<(MAX_LINES-1)) cline++; theText[cline] = ""; updateBounds(null); updateText(); } else { char[] chr = {c}; theText[cline] += new String(chr); updateBounds(null); updateText(); firstChar = false; return; } } Font getScaledFont() { if (nonScalable) return instanceFont; else { if (instanceFont==null) instanceFont = new Font(name, style, size); double mag = getMagnification(); return instanceFont.deriveFont((float)(instanceFont.getSize()*mag)); } } /** Renders the text on the image. */ public void drawPixels(ImageProcessor ip) { ip.setFont(instanceFont); ip.setAntialiasedText(antialiased); FontMetrics metrics = ip.getFontMetrics(); int fontHeight = metrics.getHeight(); int descent = metrics.getDescent(); int i = 0; int yy = 0; while (i<MAX_LINES && theText[i]!=null) { switch (justification) { case LEFT: ip.drawString(theText[i], x, y+yy+fontHeight); break; case CENTER: int tw = metrics.stringWidth(theText[i]); ip.drawString(theText[i], x+(width-tw)/2, y+yy+fontHeight); break; case RIGHT: tw = metrics.stringWidth(theText[i]); ip.drawString(theText[i], x+width-tw, y+yy+fontHeight); break; } i++; yy += fontHeight; } } /** Draws the text on the screen, clipped to the ROI. */ public void draw(Graphics g) { if (IJ.debugMode) IJ.log("draw: "+theText[0]+" "+width+","+height); if (Interpreter.isBatchMode() && ic!=null && ic.getDisplayList()!=null) return; if (newFont || width==1) updateBounds(g); super.draw(g); // draw the rectangle double mag = getMagnification(); int sx = screenX(x); int sy = screenY(y); int swidth = (int)(width*mag); int sheight = (int)(height*mag); Rectangle r = null; r = g.getClipBounds(); g.setClip(sx, sy, swidth, sheight); drawText(g); if (r!=null) g.setClip(r.x, r.y, r.width, r.height); } public void drawOverlay(Graphics g) { drawText(g); } void drawText(Graphics g) { g.setColor( strokeColor!=null? strokeColor:ROIColor); Java2.setAntialiasedText(g, antialiased); if (newFont || width==1) updateBounds(g); double mag = getMagnification(); int sx = nonScalable?x:screenX(x); int sy = nonScalable?y:screenY(y); int sw = nonScalable?width:(int)(getMagnification()*width); int sh = nonScalable?height:(int)(getMagnification()*height); Font font = getScaledFont(); FontMetrics metrics = g.getFontMetrics(font); int fontHeight = metrics.getHeight(); int descent = metrics.getDescent(); g.setFont(font); int i = 0; if (fillColor!=null) { if (getStrokeWidth()<10) { Color saveFillColor = fillColor; setStrokeWidth(10); fillColor = saveFillColor; } updateBounds(g); Color c = g.getColor(); int alpha = fillColor.getAlpha(); g.setColor(fillColor); Graphics2D g2d = (Graphics2D)g; g.fillRect(sx-5, sy-5, sw+10, sh+10); g.setColor(c); } while (i<MAX_LINES && theText[i]!=null) { switch (justification) { case LEFT: g.drawString(theText[i], sx, sy+fontHeight-descent); break; case CENTER: int tw = metrics.stringWidth(theText[i]); g.drawString(theText[i], sx+(sw-tw)/2, sy+fontHeight-descent); break; case RIGHT: tw = metrics.stringWidth(theText[i]); g.drawString(theText[i], sx+sw-tw, sy+fontHeight-descent); break; } i++; sy += fontHeight; } } /* void handleMouseUp(int screenX, int screenY) { if (width<size || height<size) grow(x+Math.max(size*5,width), y+Math.max((int)(size*1.5),height)); super.handleMouseUp(screenX, screenY); } */ /** Returns the name of the global font. */ public static String getFont() { return name; } /** Returns the global font size. */ public static int getSize() { return size; } /** Returns the global font style. */ public static int getStyle() { return style; } /** Set the current (instance) font. */ public void setCurrentFont(Font font) { instanceFont = font; updateBounds(null); } /** Returns the current (instance) font. */ public Font getCurrentFont() { return instanceFont; } /** Returns the state of global 'antialiasedText' variable, which is used by the "Fonts" widget. */ public static boolean isAntialiased() { return antialiasedText; } /** Sets the 'antialiased' instance variable. */ public void setAntialiased(boolean antialiased) { this.antialiased = antialiased; } /** Returns the state of the 'antialiased' instance variable. */ public boolean getAntialiased() { return antialiased; } /** Sets the 'justification' instance variable (must be LEFT, CENTER or RIGHT) */ public static void setGlobalJustification(int justification) { if (justification<0 || justification>RIGHT) justification = LEFT; globalJustification = justification; ImagePlus imp = WindowManager.getCurrentImage(); if (imp!=null) { Roi roi = imp.getRoi(); if (roi instanceof TextRoi) { ((TextRoi)roi).setJustification(justification); imp.draw(); } } } /** Returns the value of the 'justification' instance variable (LEFT, CENTER or RIGHT). */ public static int getGlobalJustification() { return globalJustification; } /** Sets the 'justification' instance variable (must be LEFT, CENTER or RIGHT) */ public void setJustification(int justification) { if (justification<0 || justification>RIGHT) justification = LEFT; this.justification = justification; } /** Returns the value of the 'justification' instance variable (LEFT, CENTER or RIGHT). */ public int getJustification() { return justification; } /** Sets the global font face, size and style that will be used by TextROIs interactively created using the text tool. */ public static void setFont(String fontName, int fontSize, int fontStyle) { setFont(fontName, fontSize, fontStyle, true); } /** Sets the font face, size, style and antialiasing mode that will be used by TextROIs interactively created using the text tool. */ public static void setFont(String fontName, int fontSize, int fontStyle, boolean antialiased) { recordSetFont = true; name = fontName; size = fontSize; style = fontStyle; globalJustification = LEFT; antialiasedText = antialiased; newFont = true; ImagePlus imp = WindowManager.getCurrentImage(); if (imp!=null) { Roi roi = imp.getRoi(); if (roi instanceof TextRoi) { ((TextRoi)roi).setAntialiased(antialiased); ((TextRoi)roi).setCurrentFont(new Font(name, style, size)); imp.draw(); } } } protected void handleMouseUp(int screenX, int screenY) { super.handleMouseUp(screenX, screenY); if (firstMouseUp) { updateBounds(null); updateText(); firstMouseUp = false; } else { if (width<5 || height<5) imp.killRoi(); } } /** Increases the size of bounding rectangle so it's large enough to hold the text. */ void updateBounds(Graphics g) { //IJ.log("adjustSize1: "+theText[0]+" "+width+","+height); if (ic==null || (theText[0]!=null && theText[0].equals(line1))) return; double mag = ic.getMagnification(); if (nonScalable) mag = 1.0; Font font = getScaledFont(); newFont = false; boolean nullg = g==null; if (nullg) g = ic.getGraphics(); Java2.setAntialiasedText(g, antialiased); FontMetrics metrics = g.getFontMetrics(font); int fontHeight = (int)(metrics.getHeight()/mag); int descent = metrics.getDescent(); int i=0, nLines=0; oldX = x; oldY = y; oldWidth = width; oldHeight = height; int newWidth = 10; while (i<MAX_LINES && theText[i]!=null) { nLines++; int w = (int)Math.round(stringWidth(theText[i],metrics,g)/mag); if (w>newWidth) newWidth = w; i++; } if (nullg) g.dispose(); newWidth += 2; width = newWidth; switch (justification) { case LEFT: if (xMax!=0 && x+newWidth>xMax) x = xMax-width; break; case CENTER: x = oldX+oldWidth/2 - newWidth/2; break; case RIGHT: x = oldX+oldWidth - newWidth; break; } height = nLines*fontHeight+2; if (yMax!=0) { if (height>yMax) height = yMax; if (y+height>yMax) y = yMax-height; } //IJ.log("adjustSize2: "+theText[0]+" "+width+","+height); } void updateText() { if (imp!=null) { updateClipRect(); imp.draw(clipX, clipY, clipWidth, clipHeight); } } double stringWidth(String s, FontMetrics metrics, Graphics g) { java.awt.geom.Rectangle2D r = metrics.getStringBounds(s, g); return r.getWidth(); } public String getMacroCode(ImageProcessor ip) { String code = ""; if (recordSetFont) { String options = ""; if (style==Font.BOLD) options += "bold"; if (style==Font.ITALIC) options += " italic"; if (antialiasedText) options += " antialiased"; if (options.equals("")) options = "plain"; code += "setFont(\""+name+"\", "+size+", \""+options+"\");\n"; recordSetFont = false; } FontMetrics metrics = ip.getFontMetrics(); int fontHeight = metrics.getHeight(); String text = ""; for (int i=0; i<MAX_LINES; i++) { if (theText[i]==null) break; text += theText[i]; if (theText[i+1]!=null) text += "\\n"; } code += "makeText(\""+text+"\", "+x+", "+(y+fontHeight)+");\n"; code += "//drawString(\""+text+"\", "+x+", "+(y+fontHeight)+");\n"; return (code); } public String getText() { String text = ""; for (int i=0; i<MAX_LINES; i++) { if (theText[i]==null) break; text += theText[i]+"\n"; } return text; } public static void recordSetFont() { recordSetFont = true; } public boolean isDrawingTool() { return true; } public void clear(ImageProcessor ip) { if (instanceFont==null) ip.fill(); else { ip.setFont(instanceFont); ip.setAntialiasedText(antialiasedText); int i=0, width=0; while (i<MAX_LINES && theText[i]!=null) { int w = ip.getStringWidth(theText[i]); if (w>width) width = w; i++; } Rectangle r = ip.getRoi(); if (width>r.width) { r.width = width; ip.setRoi(r); } ip.fill(); } } /** Returns a copy of this TextRoi. */ public synchronized Object clone() { TextRoi tr = (TextRoi)super.clone(); tr.theText = new String[MAX_LINES]; for (int i=0; i<MAX_LINES; i++) tr.theText[i] = theText[i]; return tr; } }