Home | History | Annotate | Download | only in font
      1 /*
      2  * Copyright (c) 2009-2010 jMonkeyEngine
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  * * Redistributions of source code must retain the above copyright
     10  *   notice, this list of conditions and the following disclaimer.
     11  *
     12  * * Redistributions in binary form must reproduce the above copyright
     13  *   notice, this list of conditions and the following disclaimer in the
     14  *   documentation and/or other materials provided with the distribution.
     15  *
     16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
     17  *   may be used to endorse or promote products derived from this software
     18  *   without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 package com.jme3.font;
     34 
     35 import com.jme3.export.*;
     36 import com.jme3.material.Material;
     37 import java.io.IOException;
     38 
     39 /**
     40  * Represents a font within jME that is generated with the AngelCode Bitmap Font Generator
     41  * @author dhdd
     42  */
     43 public class BitmapFont implements Savable {
     44 
     45     /**
     46      * Specifies horizontal alignment for text.
     47      *
     48      * @see BitmapText#setAlignment(com.jme3.font.BitmapFont.Align)
     49      */
     50     public enum Align {
     51 
     52         /**
     53          * Align text on the left of the text block
     54          */
     55         Left,
     56 
     57         /**
     58          * Align text in the center of the text block
     59          */
     60         Center,
     61 
     62         /**
     63          * Align text on the right of the text block
     64          */
     65         Right
     66     }
     67 
     68     /**
     69      * Specifies vertical alignment for text.
     70      *
     71      * @see BitmapText#setVerticalAlignment(com.jme3.font.BitmapFont.VAlign)
     72      */
     73     public enum VAlign {
     74         /**
     75          * Align text on the top of the text block
     76          */
     77         Top,
     78 
     79         /**
     80          * Align text in the center of the text block
     81          */
     82         Center,
     83 
     84         /**
     85          * Align text at the bottom of the text block
     86          */
     87         Bottom
     88     }
     89 
     90     private BitmapCharacterSet charSet;
     91     private Material[] pages;
     92 
     93     public BitmapFont() {
     94     }
     95 
     96     public BitmapText createLabel(String content){
     97         BitmapText label = new BitmapText(this);
     98         label.setSize(getCharSet().getRenderedSize());
     99         label.setText(content);
    100         return label;
    101     }
    102 
    103     public float getPreferredSize(){
    104         return getCharSet().getRenderedSize();
    105     }
    106 
    107     public void setCharSet(BitmapCharacterSet charSet) {
    108         this.charSet = charSet;
    109     }
    110 
    111     public void setPages(Material[] pages) {
    112         this.pages = pages;
    113         charSet.setPageSize(pages.length);
    114     }
    115 
    116     public Material getPage(int index) {
    117         return pages[index];
    118     }
    119 
    120     public int getPageSize() {
    121         return pages.length;
    122     }
    123 
    124     public BitmapCharacterSet getCharSet() {
    125         return charSet;
    126     }
    127 
    128     /**
    129      * Gets the line height of a StringBlock.
    130      * @param sb
    131      * @return
    132      */
    133     public float getLineHeight(StringBlock sb) {
    134         return charSet.getLineHeight() * (sb.getSize() / charSet.getRenderedSize());
    135     }
    136 
    137     public float getCharacterAdvance(char curChar, char nextChar, float size){
    138         BitmapCharacter c = charSet.getCharacter(curChar);
    139         if (c == null)
    140             return 0f;
    141 
    142         float advance = size * c.getXAdvance();
    143         advance += c.getKerning(nextChar) * size;
    144         return advance;
    145     }
    146 
    147     private int findKerningAmount(int newLineLastChar, int nextChar) {
    148         BitmapCharacter c = charSet.getCharacter(newLineLastChar);
    149         if (c == null)
    150             return 0;
    151         return c.getKerning(nextChar);
    152     }
    153 
    154     @Override
    155     public void write(JmeExporter ex) throws IOException {
    156         OutputCapsule oc = ex.getCapsule(this);
    157         oc.write(charSet, "charSet", null);
    158         oc.write(pages, "pages", null);
    159     }
    160 
    161     @Override
    162     public void read(JmeImporter im) throws IOException {
    163         InputCapsule ic = im.getCapsule(this);
    164         charSet = (BitmapCharacterSet) ic.readSavable("charSet", null);
    165         Savable[] pagesSavable = ic.readSavableArray("pages", null);
    166         pages = new Material[pagesSavable.length];
    167         System.arraycopy(pagesSavable, 0, pages, 0, pages.length);
    168     }
    169 
    170     public float getLineWidth(CharSequence text){
    171 
    172         // This method will probably always be a bit of a maintenance
    173         // nightmare since it basis its calculation on a different
    174         // routine than the Letters class.  The ideal situation would
    175         // be to abstract out letter position and size into its own
    176         // class that both BitmapFont and Letters could use for
    177         // positioning.
    178         // If getLineWidth() here ever again returns a different value
    179         // than Letters does with the same text then it might be better
    180         // just to create a Letters object for the sole purpose of
    181         // getting a text size.  It's less efficient but at least it
    182         // would be accurate.
    183 
    184         // And here I am mucking around in here again...
    185         //
    186         // A font character has a few values that are pertinent to the
    187         // line width:
    188         //  xOffset
    189         //  xAdvance
    190         //  kerningAmount(nextChar)
    191         //
    192         // The way BitmapText ultimately works is that the first character
    193         // starts with xOffset included (ie: it is rendered at -xOffset).
    194         // Its xAdvance is wider to accomodate that initial offset.
    195         // The cursor position is advanced by xAdvance each time.
    196         //
    197         // So, a width should be calculated in a similar way.  Start with
    198         // -xOffset + xAdvance for the first character and then each subsequent
    199         // character is just xAdvance more 'width'.
    200         //
    201         // The kerning amount from one character to the next affects the
    202         // cursor position of that next character and thus the ultimate width
    203         // and so must be factored in also.
    204 
    205         float lineWidth = 0f;
    206         float maxLineWidth = 0f;
    207         char lastChar = 0;
    208         boolean firstCharOfLine = true;
    209 //        float sizeScale = (float) block.getSize() / charSet.getRenderedSize();
    210         float sizeScale = 1f;
    211         for (int i = 0; i < text.length(); i++){
    212             char theChar = text.charAt(i);
    213             if (theChar == '\n'){
    214                 maxLineWidth = Math.max(maxLineWidth, lineWidth);
    215                 lineWidth = 0f;
    216                 firstCharOfLine = true;
    217                 continue;
    218             }
    219             BitmapCharacter c = charSet.getCharacter((int) theChar);
    220             if (c != null){
    221                 if (theChar == '\\' && i<text.length()-1 && text.charAt(i+1)=='#'){
    222                     if (i+5<text.length() && text.charAt(i+5)=='#'){
    223                         i+=5;
    224                         continue;
    225                     }else if (i+8<text.length() && text.charAt(i+8)=='#'){
    226                         i+=8;
    227                         continue;
    228                     }
    229                 }
    230                 if (!firstCharOfLine){
    231                     lineWidth += findKerningAmount(lastChar, theChar) * sizeScale;
    232                 } else {
    233                     // The first character needs to add in its xOffset but it
    234                     // is the only one... and negative offsets = postive width
    235                     // because we're trying to account for the part that hangs
    236                     // over the left.  So we subtract.
    237                     lineWidth -= c.getXOffset() * sizeScale;
    238                     firstCharOfLine = false;
    239                 }
    240                 float xAdvance = c.getXAdvance() * sizeScale;
    241 
    242                 // If this is the last character, then we really should have
    243                 // only add its width.  The advance may include extra spacing
    244                 // that we don't care about.
    245                 if (i == text.length() - 1) {
    246                     lineWidth += c.getWidth() * sizeScale;
    247 
    248                     // Since theh width includes the xOffset then we need
    249                     // to take it out again by adding it, ie: offset the width
    250                     // we just added by the appropriate amount.
    251                     lineWidth += c.getXOffset() * sizeScale;
    252                 } else {
    253                     lineWidth += xAdvance;
    254                 }
    255             }
    256         }
    257         return Math.max(maxLineWidth, lineWidth);
    258     }
    259 
    260 
    261     /**
    262      * Merge two fonts.
    263      * If two font have the same style, merge will fail.
    264      * @param styleSet Style must be assigned to this.
    265      * @author Yonghoon
    266      */
    267     public void merge(BitmapFont newFont) {
    268         charSet.merge(newFont.charSet);
    269         final int size1 = this.pages.length;
    270         final int size2 = newFont.pages.length;
    271 
    272         Material[] tmp = new Material[size1+size2];
    273         System.arraycopy(this.pages, 0, tmp, 0, size1);
    274         System.arraycopy(newFont.pages, 0, tmp, size1, size2);
    275 
    276         this.pages = tmp;
    277 
    278 //        this.pages = Arrays.copyOf(this.pages, size1+size2);
    279 //        System.arraycopy(newFont.pages, 0, this.pages, size1, size2);
    280     }
    281 
    282     public void setStyle(int style) {
    283         charSet.setStyle(style);
    284     }
    285 
    286 }