001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.openimaj.image.typography;
031
032import java.text.AttributedCharacterIterator;
033import java.text.AttributedString;
034
035import org.openimaj.image.renderer.ImageRenderer;
036import org.openimaj.image.typography.FontStyle.HorizontalAlignment;
037import org.openimaj.image.typography.FontStyle.VerticalAlignment;
038import org.openimaj.math.geometry.shape.Rectangle;
039
040/**
041 * The FontRenderer represents an object capable of rendering text with 
042 * a given font and style to an image. 
043 * 
044 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
045 *
046 * @param <T> type of image pixels
047 * @param <Q> type of {@link FontStyle}
048 */
049public abstract class FontRenderer<T, Q extends FontStyle<T>> {
050        /**
051         * Render the given text string to the image starting at (x, y) with the
052         * given style.
053         * 
054         * @param renderer the renderer
055         * @param text the text
056         * @param x the x-ordinate
057         * @param y the y-ordinate
058         * @param style the style
059         */
060        public abstract void renderText(ImageRenderer<T,?> renderer, String text, int x, int y, Q style);
061        
062        /**
063         * Calculate the bounding box of the rendered text with the given style. 
064         * @param string the text
065         * @param style the style
066         * @return the bounding box
067         */
068        public abstract Rectangle getSize(String string, Q style);
069        
070        /**
071         * Calculate the bounding box of the rendered text with the given style. 
072         * @param string the text
073         * @param x the x to render the font
074         * @param y the y to render the font
075         * @param sty the style
076         * @return the bounding box
077         */
078        public Rectangle getBounds(String string, int x, int y, Q sty){
079                Rectangle rect = this.getSize(string, sty);
080                // if we have a non-standard horizontal alignment
081                if ((sty.getHorizontalAlignment() != HorizontalAlignment.HORIZONTAL_LEFT)) {
082                        // find the length of the string in pixels ...
083                        float len = (float) rect.getWidth();
084                        // if we are center aligned
085                        if (sty.getHorizontalAlignment() == HorizontalAlignment.HORIZONTAL_CENTER) {
086                                x -= len/2;
087                        } else {
088                                x -= len;
089                        }
090                        
091                }
092                
093                if(sty.getVerticalAlignment() != VerticalAlignment.VERTICAL_TOP){
094                        switch (sty.getVerticalAlignment()) {
095                        case VERTICAL_BOTTOM:
096                                y -= rect.getHeight();
097                                break;
098                        case VERTICAL_HALF:
099                                y -= rect.getHeight()/2f;
100                                break;
101                        default:
102                                break;
103                        }
104                }
105                
106                rect.x = x;
107                rect.y = y;
108                return rect;
109        }
110        
111        
112        /**
113         * Render the given {@link AttributedString} to the image starting at (x,y).
114         * @param <T> the pixel type of the image
115         * @param renderer the image renderer
116         * @param text the text
117         * @param x the x-ordinate
118         * @param y the y-ordinate
119         */
120        @SuppressWarnings({ "unchecked", "rawtypes" })
121        public static <T> void renderText(ImageRenderer<T,?> renderer, AttributedString text, int x, int y) {
122                AttributedCharacterIterator iterator = text.getIterator();
123                
124                while (true) {
125                        Character c = iterator.current();
126                        if (c == AttributedCharacterIterator.DONE) break;
127                        
128                        FontStyle sty = FontStyle.parseAttributes(iterator.getAttributes(), renderer);
129                        FontRenderer fontRenderer = sty.getRenderer(renderer);
130                        
131                        Rectangle rect = fontRenderer.getSize(c.toString(), sty);
132                        fontRenderer.renderText(renderer, c.toString(), x, y, sty);
133                        x += rect.width;
134                        y += rect.height;
135                        
136                        iterator.next();
137                }
138        }
139        
140        /**
141         * Calculate the bounding box of the given {@link AttributedString}. The image
142         * will not be modified by this call, but is used to properly configure the styles
143         * for bounds estimation.
144         * 
145         * @param <T> the pixel type
146         * @param text the text
147         * @param imageRenderer the target image renderer
148         * @return the bounding box
149         */
150        @SuppressWarnings({ "rawtypes", "unchecked" })
151        public static <T> Rectangle getBounds(AttributedString text, ImageRenderer<T,?> imageRenderer) {
152                AttributedCharacterIterator iterator = text.getIterator();
153                int x=0, y=0;
154                
155                while (true) {
156                        Character c = iterator.current();
157                        if (c == AttributedCharacterIterator.DONE) break;
158                        
159                        FontStyle sty = FontStyle.parseAttributes(iterator.getAttributes(), imageRenderer);
160                        FontRenderer renderer = sty.getRenderer(imageRenderer);
161                        
162                        Rectangle rect = renderer.getSize(c.toString(), sty);
163                        x += rect.width;
164                        y += rect.height;
165                        
166                        iterator.next();
167                }
168                
169                return new Rectangle(0,0,x,y);
170        }
171}