Save This Page
Home » openjdk-7 » java » awt » font » [javadoc | source]
    1   /*
    2    * Portions Copyright 1999-2005 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    *
   25    */
   26   
   27   /*
   28    * (C) Copyright IBM Corp. 1999,  All rights reserved.
   29    */
   30   package java.awt.font;
   31   
   32   import java.awt.Font;
   33   import java.awt.Toolkit;
   34   import java.awt.im.InputMethodHighlight;
   35   import java.text.Annotation;
   36   import java.text.AttributedCharacterIterator;
   37   import java.util.Vector;
   38   import java.util.HashMap;
   39   import java.util.Map;
   40   import sun.font.Decoration;
   41   import sun.font.FontResolver;
   42   import sun.text.CodePointIterator;
   43   
   44   /**
   45    * This class stores Font, GraphicAttribute, and Decoration intervals
   46    * on a paragraph of styled text.
   47    * <p>
   48    * Currently, this class is optimized for a small number of intervals
   49    * (preferrably 1).
   50    */
   51   final class StyledParagraph {
   52   
   53       // the length of the paragraph
   54       private int length;
   55   
   56       // If there is a single Decoration for the whole paragraph, it
   57       // is stored here.  Otherwise this field is ignored.
   58   
   59       private Decoration decoration;
   60   
   61       // If there is a single Font or GraphicAttribute for the whole
   62       // paragraph, it is stored here.  Otherwise this field is ignored.
   63       private Object font;
   64   
   65       // If there are multiple Decorations in the paragraph, they are
   66       // stored in this Vector, in order.  Otherwise this vector and
   67       // the decorationStarts array are null.
   68       private Vector decorations;
   69       // If there are multiple Decorations in the paragraph,
   70       // decorationStarts[i] contains the index where decoration i
   71       // starts.  For convenience, there is an extra entry at the
   72       // end of this array with the length of the paragraph.
   73       int[] decorationStarts;
   74   
   75       // If there are multiple Fonts/GraphicAttributes in the paragraph,
   76       // they are
   77       // stored in this Vector, in order.  Otherwise this vector and
   78       // the fontStarts array are null.
   79       private Vector fonts;
   80       // If there are multiple Fonts/GraphicAttributes in the paragraph,
   81       // fontStarts[i] contains the index where decoration i
   82       // starts.  For convenience, there is an extra entry at the
   83       // end of this array with the length of the paragraph.
   84       int[] fontStarts;
   85   
   86       private static int INITIAL_SIZE = 8;
   87   
   88       /**
   89        * Create a new StyledParagraph over the given styled text.
   90        * @param aci an iterator over the text
   91        * @param chars the characters extracted from aci
   92        */
   93       public StyledParagraph(AttributedCharacterIterator aci,
   94                              char[] chars) {
   95   
   96           int start = aci.getBeginIndex();
   97           int end = aci.getEndIndex();
   98           length = end - start;
   99   
  100           int index = start;
  101           aci.first();
  102   
  103           do {
  104               final int nextRunStart = aci.getRunLimit();
  105               final int localIndex = index-start;
  106   
  107               Map attributes = aci.getAttributes();
  108               attributes = addInputMethodAttrs(attributes);
  109               Decoration d = Decoration.getDecoration(attributes);
  110               addDecoration(d, localIndex);
  111   
  112               Object f = getGraphicOrFont(attributes);
  113               if (f == null) {
  114                   addFonts(chars, attributes, localIndex, nextRunStart-start);
  115               }
  116               else {
  117                   addFont(f, localIndex);
  118               }
  119   
  120               aci.setIndex(nextRunStart);
  121               index = nextRunStart;
  122   
  123           } while (index < end);
  124   
  125           // Add extra entries to starts arrays with the length
  126           // of the paragraph.  'this' is used as a dummy value
  127           // in the Vector.
  128           if (decorations != null) {
  129               decorationStarts = addToVector(this, length, decorations, decorationStarts);
  130           }
  131           if (fonts != null) {
  132               fontStarts = addToVector(this, length, fonts, fontStarts);
  133           }
  134       }
  135   
  136       /**
  137        * Adjust indices in starts to reflect an insertion after pos.
  138        * Any index in starts greater than pos will be increased by 1.
  139        */
  140       private static void insertInto(int pos, int[] starts, int numStarts) {
  141   
  142           while (starts[--numStarts] > pos) {
  143               starts[numStarts] += 1;
  144           }
  145       }
  146   
  147       /**
  148        * Return a StyledParagraph reflecting the insertion of a single character
  149        * into the text.  This method will attempt to reuse the given paragraph,
  150        * but may create a new paragraph.
  151        * @param aci an iterator over the text.  The text should be the same as the
  152        *     text used to create (or most recently update) oldParagraph, with
  153        *     the exception of inserting a single character at insertPos.
  154        * @param chars the characters in aci
  155        * @param insertPos the index of the new character in aci
  156        * @param oldParagraph a StyledParagraph for the text in aci before the
  157        *     insertion
  158        */
  159       public static StyledParagraph insertChar(AttributedCharacterIterator aci,
  160                                                char[] chars,
  161                                                int insertPos,
  162                                                StyledParagraph oldParagraph) {
  163   
  164           // If the styles at insertPos match those at insertPos-1,
  165           // oldParagraph will be reused.  Otherwise we create a new
  166           // paragraph.
  167   
  168           char ch = aci.setIndex(insertPos);
  169           int relativePos = Math.max(insertPos - aci.getBeginIndex() - 1, 0);
  170   
  171           Map attributes = addInputMethodAttrs(aci.getAttributes());
  172           Decoration d = Decoration.getDecoration(attributes);
  173           if (!oldParagraph.getDecorationAt(relativePos).equals(d)) {
  174               return new StyledParagraph(aci, chars);
  175           }
  176           Object f = getGraphicOrFont(attributes);
  177           if (f == null) {
  178               FontResolver resolver = FontResolver.getInstance();
  179               int fontIndex = resolver.getFontIndex(ch);
  180               f = resolver.getFont(fontIndex, attributes);
  181           }
  182           if (!oldParagraph.getFontOrGraphicAt(relativePos).equals(f)) {
  183               return new StyledParagraph(aci, chars);
  184           }
  185   
  186           // insert into existing paragraph
  187           oldParagraph.length += 1;
  188           if (oldParagraph.decorations != null) {
  189               insertInto(relativePos,
  190                          oldParagraph.decorationStarts,
  191                          oldParagraph.decorations.size());
  192           }
  193           if (oldParagraph.fonts != null) {
  194               insertInto(relativePos,
  195                          oldParagraph.fontStarts,
  196                          oldParagraph.fonts.size());
  197           }
  198           return oldParagraph;
  199       }
  200   
  201       /**
  202        * Adjust indices in starts to reflect a deletion after deleteAt.
  203        * Any index in starts greater than deleteAt will be increased by 1.
  204        * It is the caller's responsibility to make sure that no 0-length
  205        * runs result.
  206        */
  207       private static void deleteFrom(int deleteAt, int[] starts, int numStarts) {
  208   
  209           while (starts[--numStarts] > deleteAt) {
  210               starts[numStarts] -= 1;
  211           }
  212       }
  213   
  214       /**
  215        * Return a StyledParagraph reflecting the insertion of a single character
  216        * into the text.  This method will attempt to reuse the given paragraph,
  217        * but may create a new paragraph.
  218        * @param aci an iterator over the text.  The text should be the same as the
  219        *     text used to create (or most recently update) oldParagraph, with
  220        *     the exception of deleting a single character at deletePos.
  221        * @param chars the characters in aci
  222        * @param deletePos the index where a character was removed
  223        * @param oldParagraph a StyledParagraph for the text in aci before the
  224        *     insertion
  225        */
  226       public static StyledParagraph deleteChar(AttributedCharacterIterator aci,
  227                                                char[] chars,
  228                                                int deletePos,
  229                                                StyledParagraph oldParagraph) {
  230   
  231           // We will reuse oldParagraph unless there was a length-1 run
  232           // at deletePos.  We could do more work and check the individual
  233           // Font and Decoration runs, but we don't right now...
  234           deletePos -= aci.getBeginIndex();
  235   
  236           if (oldParagraph.decorations == null && oldParagraph.fonts == null) {
  237               oldParagraph.length -= 1;
  238               return oldParagraph;
  239           }
  240   
  241           if (oldParagraph.getRunLimit(deletePos) == deletePos+1) {
  242               if (deletePos == 0 || oldParagraph.getRunLimit(deletePos-1) == deletePos) {
  243                   return new StyledParagraph(aci, chars);
  244               }
  245           }
  246   
  247           oldParagraph.length -= 1;
  248           if (oldParagraph.decorations != null) {
  249               deleteFrom(deletePos,
  250                          oldParagraph.decorationStarts,
  251                          oldParagraph.decorations.size());
  252           }
  253           if (oldParagraph.fonts != null) {
  254               deleteFrom(deletePos,
  255                          oldParagraph.fontStarts,
  256                          oldParagraph.fonts.size());
  257           }
  258           return oldParagraph;
  259       }
  260   
  261       /**
  262        * Return the index at which there is a different Font, GraphicAttribute, or
  263        * Dcoration than at the given index.
  264        * @param index a valid index in the paragraph
  265        * @return the first index where there is a change in attributes from
  266        *      those at index
  267        */
  268       public int getRunLimit(int index) {
  269   
  270           if (index < 0 || index >= length) {
  271               throw new IllegalArgumentException("index out of range");
  272           }
  273           int limit1 = length;
  274           if (decorations != null) {
  275               int run = findRunContaining(index, decorationStarts);
  276               limit1 = decorationStarts[run+1];
  277           }
  278           int limit2 = length;
  279           if (fonts != null) {
  280               int run = findRunContaining(index, fontStarts);
  281               limit2 = fontStarts[run+1];
  282           }
  283           return Math.min(limit1, limit2);
  284       }
  285   
  286       /**
  287        * Return the Decoration in effect at the given index.
  288        * @param index a valid index in the paragraph
  289        * @return the Decoration at index.
  290        */
  291       public Decoration getDecorationAt(int index) {
  292   
  293           if (index < 0 || index >= length) {
  294               throw new IllegalArgumentException("index out of range");
  295           }
  296           if (decorations == null) {
  297               return decoration;
  298           }
  299           int run = findRunContaining(index, decorationStarts);
  300           return (Decoration) decorations.elementAt(run);
  301       }
  302   
  303       /**
  304        * Return the Font or GraphicAttribute in effect at the given index.
  305        * The client must test the type of the return value to determine what
  306        * it is.
  307        * @param index a valid index in the paragraph
  308        * @return the Font or GraphicAttribute at index.
  309        */
  310       public Object getFontOrGraphicAt(int index) {
  311   
  312           if (index < 0 || index >= length) {
  313               throw new IllegalArgumentException("index out of range");
  314           }
  315           if (fonts == null) {
  316               return font;
  317           }
  318           int run = findRunContaining(index, fontStarts);
  319           return fonts.elementAt(run);
  320       }
  321   
  322       /**
  323        * Return i such that starts[i] <= index < starts[i+1].  starts
  324        * must be in increasing order, with at least one element greater
  325        * than index.
  326        */
  327       private static int findRunContaining(int index, int[] starts) {
  328   
  329           for (int i=1; true; i++) {
  330               if (starts[i] > index) {
  331                   return i-1;
  332               }
  333           }
  334       }
  335   
  336       /**
  337        * Append the given Object to the given Vector.  Add
  338        * the given index to the given starts array.  If the
  339        * starts array does not have room for the index, a
  340        * new array is created and returned.
  341        */
  342       private static int[] addToVector(Object obj,
  343                                        int index,
  344                                        Vector v,
  345                                        int[] starts) {
  346   
  347           if (!v.lastElement().equals(obj)) {
  348               v.addElement(obj);
  349               int count = v.size();
  350               if (starts.length == count) {
  351                   int[] temp = new int[starts.length*2];
  352                   System.arraycopy(starts, 0, temp, 0, starts.length);
  353                   starts = temp;
  354               }
  355               starts[count-1] = index;
  356           }
  357           return starts;
  358       }
  359   
  360       /**
  361        * Add a new Decoration run with the given Decoration at the
  362        * given index.
  363        */
  364       private void addDecoration(Decoration d, int index) {
  365   
  366           if (decorations != null) {
  367               decorationStarts = addToVector(d,
  368                                              index,
  369                                              decorations,
  370                                              decorationStarts);
  371           }
  372           else if (decoration == null) {
  373               decoration = d;
  374           }
  375           else {
  376               if (!decoration.equals(d)) {
  377                   decorations = new Vector(INITIAL_SIZE);
  378                   decorations.addElement(decoration);
  379                   decorations.addElement(d);
  380                   decorationStarts = new int[INITIAL_SIZE];
  381                   decorationStarts[0] = 0;
  382                   decorationStarts[1] = index;
  383               }
  384           }
  385       }
  386   
  387       /**
  388        * Add a new Font/GraphicAttribute run with the given object at the
  389        * given index.
  390        */
  391       private void addFont(Object f, int index) {
  392   
  393           if (fonts != null) {
  394               fontStarts = addToVector(f, index, fonts, fontStarts);
  395           }
  396           else if (font == null) {
  397               font = f;
  398           }
  399           else {
  400               if (!font.equals(f)) {
  401                   fonts = new Vector(INITIAL_SIZE);
  402                   fonts.addElement(font);
  403                   fonts.addElement(f);
  404                   fontStarts = new int[INITIAL_SIZE];
  405                   fontStarts[0] = 0;
  406                   fontStarts[1] = index;
  407               }
  408           }
  409       }
  410   
  411       /**
  412        * Resolve the given chars into Fonts using FontResolver, then add
  413        * font runs for each.
  414        */
  415       private void addFonts(char[] chars, Map attributes, int start, int limit) {
  416   
  417           FontResolver resolver = FontResolver.getInstance();
  418           CodePointIterator iter = CodePointIterator.create(chars, start, limit);
  419           for (int runStart = iter.charIndex(); runStart < limit; runStart = iter.charIndex()) {
  420               int fontIndex = resolver.nextFontRunIndex(iter);
  421               addFont(resolver.getFont(fontIndex, attributes), runStart);
  422           }
  423       }
  424   
  425       /**
  426        * Return a Map with entries from oldStyles, as well as input
  427        * method entries, if any.
  428        */
  429       static Map addInputMethodAttrs(Map oldStyles) {
  430   
  431           Object value = oldStyles.get(TextAttribute.INPUT_METHOD_HIGHLIGHT);
  432   
  433           try {
  434               if (value != null) {
  435                   if (value instanceof Annotation) {
  436                       value = ((Annotation)value).getValue();
  437                   }
  438   
  439                   InputMethodHighlight hl;
  440                   hl = (InputMethodHighlight) value;
  441   
  442                   Map imStyles = null;
  443                   try {
  444                       imStyles = hl.getStyle();
  445                   } catch (NoSuchMethodError e) {
  446                   }
  447   
  448                   if (imStyles == null) {
  449                       Toolkit tk = Toolkit.getDefaultToolkit();
  450                       imStyles = tk.mapInputMethodHighlight(hl);
  451                   }
  452   
  453                   if (imStyles != null) {
  454                       HashMap newStyles = new HashMap(5, (float)0.9);
  455                       newStyles.putAll(oldStyles);
  456   
  457                       newStyles.putAll(imStyles);
  458   
  459                       return newStyles;
  460                   }
  461               }
  462           }
  463           catch(ClassCastException e) {
  464           }
  465   
  466           return oldStyles;
  467       }
  468   
  469       /**
  470        * Extract a GraphicAttribute or Font from the given attributes.
  471        * If attributes does not contain a GraphicAttribute, Font, or
  472        * Font family entry this method returns null.
  473        */
  474       private static Object getGraphicOrFont(Map attributes) {
  475   
  476           Object value = attributes.get(TextAttribute.CHAR_REPLACEMENT);
  477           if (value != null) {
  478               return value;
  479           }
  480           value = attributes.get(TextAttribute.FONT);
  481           if (value != null) {
  482               return value;
  483           }
  484   
  485           if (attributes.get(TextAttribute.FAMILY) != null) {
  486               return Font.getFont(attributes);
  487           }
  488           else {
  489               return null;
  490           }
  491       }
  492   }

Save This Page
Home » openjdk-7 » java » awt » font » [javadoc | source]