668 lines · 23.8 KB · Java
RawDownload
/* * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * * (C) Copyright IBM Corp. 1999-2003 - All Rights Reserved * * The original version of this source code and documentation is * copyrighted and owned by IBM. These materials are provided * under terms of a License Agreement between IBM and Sun. * This technology is protected by multiple US and International * patents. This notice and attribution to IBM may not be removed. */ /* * GlyphLayout is used to process a run of text into a run of run of * glyphs, optionally with position and char mapping info. * * The text has already been processed for numeric shaping and bidi. * The run of text that layout works on has a single bidi level.  It * also has a single font/style.  Some operations need context to work * on (shaping, script resolution) so context for the text run text is * provided.  It is assumed that the text array contains sufficient * context, and the offset and count delimit the portion of the text * that needs to actually be processed. * * The font might be a composite font.  Layout generally requires * tables from a single physical font to operate, and so it must * resolve the 'single' font run into runs of physical fonts. * * Some characters are supported by several fonts of a composite, and * in order to properly emulate the glyph substitution behavior of a * single physical font, these characters might need to be mapped to * different physical fonts.  The script code that is assigned * characters normally considered 'common script' can be used to * resolve which physical font to use for these characters. The input * to the char to glyph mapper (which assigns physical fonts as it * processes the glyphs) should include the script code, and the * mapper should operate on runs of a single script. * * To perform layout, call get() to get a new (or reuse an old) * GlyphLayout, call layout on it, then call done(GlyphLayout) when * finished.  There's no particular problem if you don't call done, * but it assists in reuse of the GlyphLayout. */ package sun.font; import java.lang.ref.SoftReference;import java.awt.Font;import java.awt.font.FontRenderContext;import java.awt.font.GlyphVector;import java.awt.geom.AffineTransform;import java.awt.geom.NoninvertibleTransformException;import java.awt.geom.Point2D;import java.util.ArrayList;import java.util.concurrent.ConcurrentHashMap; import static java.lang.Character.*; public final class GlyphLayout {    // data for glyph vector    private GVData _gvdata;     // cached glyph layout data for reuse    private static volatile GlyphLayout cache;  // reusable     private LayoutEngineFactory _lef;  // set when get is called, unset when done is called    private TextRecord _textRecord;    // the text we're working on, used by iterators    private ScriptRun _scriptRuns;     // iterator over script runs    private FontRunIterator _fontRuns; // iterator over physical fonts in a composite    private int _ercount;    private ArrayList _erecords;    private Point2D.Float _pt;    private FontStrikeDesc _sd;    private float[] _mat;    private int _typo_flags;    private int _offset;     public static final class LayoutEngineKey {        private Font2D font;        private int script;        private int lang;         LayoutEngineKey() {        }         LayoutEngineKey(Font2D font, int script, int lang) {            init(font, script, lang);        }         void init(Font2D font, int script, int lang) {            this.font = font;            this.script = script;            this.lang = lang;        }         LayoutEngineKey copy() {            return new LayoutEngineKey(font, script, lang);        }         Font2D font() {            return font;        }         int script() {            return script;        }         int lang() {            return lang;        }         public boolean equals(Object rhs) {            if (this == rhs) return true;            if (rhs == null) return false;            try {                LayoutEngineKey that = (LayoutEngineKey)rhs;                return this.script == that.script &&                       this.lang == that.lang &&                       this.font.equals(that.font);            }            catch (ClassCastException e) {                return false;            }        }         public int hashCode() {            return script ^ lang ^ font.hashCode();        }    }     public static interface LayoutEngineFactory {        /**         * Given a font, script, and language, determine a layout engine to use.         */        public LayoutEngine getEngine(Font2D font, int script, int lang);         /**         * Given a key, determine a layout engine to use.         */        public LayoutEngine getEngine(LayoutEngineKey key);    }     public static interface LayoutEngine {        /**         * Given a strike descriptor, text, rtl flag, and starting point, append information about         * glyphs, positions, and character indices to the glyphvector data, and advance the point.         *         * If the GVData does not have room for the glyphs, throws an IndexOutOfBoundsException and         * leave pt and the gvdata unchanged.         */        public void layout(FontStrikeDesc sd, float[] mat, int gmask,                           int baseIndex, TextRecord text, int typo_flags, Point2D.Float pt, GVData data);    }     /**     * Return a new instance of GlyphLayout, using the provided layout engine factory.     * If null, the system layout engine factory will be used.     */    public static GlyphLayout get(LayoutEngineFactory lef) {        if (lef == null) {            lef = SunLayoutEngine.instance();        }        GlyphLayout result = null;        synchronized(GlyphLayout.class) {            if (cache != null) {                result = cache;                cache = null;            }        }        if (result == null) {            result = new GlyphLayout();        }        result._lef = lef;        return result;    }     /**     * Return the old instance of GlyphLayout when you are done.  This enables reuse     * of GlyphLayout objects.     */    public static void done(GlyphLayout gl) {        gl._lef = null;        cache = gl; // object reference assignment is thread safe, it says here...    }     private static final class SDCache {        public Font key_font;        public FontRenderContext key_frc;         public AffineTransform dtx;        public AffineTransform invdtx;        public AffineTransform gtx;        public Point2D.Float delta;        public FontStrikeDesc sd;         private SDCache(Font font, FontRenderContext frc) {            key_font = font;            key_frc = frc;             // !!! add getVectorTransform and hasVectorTransform to frc?  then            // we could just skip this work...             dtx = frc.getTransform();            dtx.setTransform(dtx.getScaleX(), dtx.getShearY(),                             dtx.getShearX(), dtx.getScaleY(),                             0, 0);            if (!dtx.isIdentity()) {                try {                    invdtx = dtx.createInverse();                }                catch (NoninvertibleTransformException e) {                    throw new InternalError();                }            }             float ptSize = font.getSize2D();            if (font.isTransformed()) {                gtx = font.getTransform();                gtx.scale(ptSize, ptSize);                delta = new Point2D.Float((float)gtx.getTranslateX(),                                          (float)gtx.getTranslateY());                gtx.setTransform(gtx.getScaleX(), gtx.getShearY(),                                 gtx.getShearX(), gtx.getScaleY(),                                 0, 0);                gtx.preConcatenate(dtx);            } else {                delta = ZERO_DELTA;                gtx = new AffineTransform(dtx);                gtx.scale(ptSize, ptSize);            }             /* Similar logic to that used in SunGraphics2D.checkFontInfo().             * Whether a grey (AA) strike is needed is size dependent if             * AA mode is 'gasp'.             */            int aa =                FontStrikeDesc.getAAHintIntVal(frc.getAntiAliasingHint(),                                               FontUtilities.getFont2D(font),                                               (int)Math.abs(ptSize));            int fm = FontStrikeDesc.getFMHintIntVal                (frc.getFractionalMetricsHint());            sd = new FontStrikeDesc(dtx, gtx, font.getStyle(), aa, fm);        }         private static final Point2D.Float ZERO_DELTA = new Point2D.Float();         private static            SoftReference<ConcurrentHashMap<SDKey, SDCache>> cacheRef;         private static final class SDKey {            private final Font font;            private final FontRenderContext frc;            private final int hash;             SDKey(Font font, FontRenderContext frc) {                this.font = font;                this.frc = frc;                this.hash = font.hashCode() ^ frc.hashCode();            }             public int hashCode() {                return hash;            }             public boolean equals(Object o) {                try {                    SDKey rhs = (SDKey)o;                    return                        hash == rhs.hash &&                        font.equals(rhs.font) &&                        frc.equals(rhs.frc);                }                catch (ClassCastException e) {                }                return false;            }        }         public static SDCache get(Font font, FontRenderContext frc) {             // It is possible a translation component will be in the FRC.            // It doesn't affect us except adversely as we would consider            // FRC's which are really the same to be different. If we            // detect a translation component, then we need to exclude it            // by creating a new transform which excludes the translation.            if (frc.isTransformed()) {                AffineTransform transform = frc.getTransform();                if (transform.getTranslateX() != 0 ||                    transform.getTranslateY() != 0) {                    transform = new AffineTransform(transform.getScaleX(),                                                    transform.getShearY(),                                                    transform.getShearX(),                                                    transform.getScaleY(),                                                    0, 0);                    frc = new FontRenderContext(transform,                                                frc.getAntiAliasingHint(),                                                frc.getFractionalMetricsHint()                                                );                }            }             SDKey key = new SDKey(font, frc); // garbage, yuck...            ConcurrentHashMap<SDKey, SDCache> cache = null;            SDCache res = null;            if (cacheRef != null) {                cache = cacheRef.get();                if (cache != null) {                    res = cache.get(key);                }            }            if (res == null) {                res = new SDCache(font, frc);                if (cache == null) {                    cache = new ConcurrentHashMap<SDKey, SDCache>(10);                    cacheRef = new                       SoftReference<ConcurrentHashMap<SDKey, SDCache>>(cache);                } else if (cache.size() >= 512) {                    cache.clear();                }                cache.put(key, res);            }            return res;        }    }     /**     * Create a glyph vector.     * @param font the font to use     * @param frc the font render context     * @param text the text, including optional context before start and after start + count     * @param offset the start of the text to lay out     * @param count the length of the text to lay out     * @param flags bidi and context flags {@see #java.awt.Font}     * @param result a StandardGlyphVector to modify, can be null     * @return the layed out glyphvector, if result was passed in, it is returned     */    public StandardGlyphVector layout(Font font, FontRenderContext frc,                                      char[] text, int offset, int count,                                      int flags, StandardGlyphVector result)    {        if (text == null || offset < 0 || count < 0 || (count > text.length - offset)) {            throw new IllegalArgumentException();        }         init(count);         // need to set after init        // go through the back door for this        if (font.hasLayoutAttributes()) {            AttributeValues values = ((AttributeMap)font.getAttributes()).getValues();            if (values.getKerning() != 0) _typo_flags |= 0x1;            if (values.getLigatures() != 0) _typo_flags |= 0x2;        }         _offset = offset;         // use cache now - can we use the strike cache for this?         SDCache txinfo = SDCache.get(font, frc);        _mat[0] = (float)txinfo.gtx.getScaleX();        _mat[1] = (float)txinfo.gtx.getShearY();        _mat[2] = (float)txinfo.gtx.getShearX();        _mat[3] = (float)txinfo.gtx.getScaleY();        _pt.setLocation(txinfo.delta);         int lim = offset + count;         int min = 0;        int max = text.length;        if (flags != 0) {            if ((flags & Font.LAYOUT_RIGHT_TO_LEFT) != 0) {              _typo_flags |= 0x80000000; // RTL            }             if ((flags & Font.LAYOUT_NO_START_CONTEXT) != 0) {                min = offset;            }             if ((flags & Font.LAYOUT_NO_LIMIT_CONTEXT) != 0) {                max = lim;            }        }         int lang = -1; // default for now         Font2D font2D = FontUtilities.getFont2D(font);         _textRecord.init(text, offset, lim, min, max);        int start = offset;        if (font2D instanceof CompositeFont) {            _scriptRuns.init(text, offset, count); // ??? how to handle 'common' chars            _fontRuns.init((CompositeFont)font2D, text, offset, lim);            while (_scriptRuns.next()) {                int limit = _scriptRuns.getScriptLimit();                int script = _scriptRuns.getScriptCode();                while (_fontRuns.next(script, limit)) {                    Font2D pfont = _fontRuns.getFont();                    /* layout can't deal with NativeFont instances. The                     * native font is assumed to know of a suitable non-native                     * substitute font. This currently works because                     * its consistent with the way NativeFonts delegate                     * in other cases too.                     */                    if (pfont instanceof NativeFont) {                        pfont = ((NativeFont)pfont).getDelegateFont();                    }                    int gmask = _fontRuns.getGlyphMask();                    int pos = _fontRuns.getPos();                    nextEngineRecord(start, pos, script, lang, pfont, gmask);                    start = pos;                }            }        } else {            _scriptRuns.init(text, offset, count); // ??? don't worry about 'common' chars            while (_scriptRuns.next()) {                int limit = _scriptRuns.getScriptLimit();                int script = _scriptRuns.getScriptCode();                nextEngineRecord(start, limit, script, lang, font2D, 0);                start = limit;            }        }         int ix = 0;        int stop = _ercount;        int dir = 1;         if (_typo_flags < 0) { // RTL            ix = stop - 1;            stop = -1;            dir = -1;        }         //        _sd.init(dtx, gtx, font.getStyle(), frc.isAntiAliased(), frc.usesFractionalMetrics());        _sd = txinfo.sd;        for (;ix != stop; ix += dir) {            EngineRecord er = (EngineRecord)_erecords.get(ix);            for (;;) {                try {                    er.layout();                    break;                }                catch (IndexOutOfBoundsException e) {                    _gvdata.grow();                }            }        }         //        if (txinfo.invdtx != null) {        //            _gvdata.adjustPositions(txinfo.invdtx);        //        }         StandardGlyphVector gv = _gvdata.createGlyphVector(font, frc, result);        //        System.err.println("Layout returns: " + gv);        return gv;    }     //    // private methods    //     private GlyphLayout() {        this._gvdata = new GVData();        this._textRecord = new TextRecord();        this._scriptRuns = new ScriptRun();        this._fontRuns = new FontRunIterator();        this._erecords = new ArrayList(10);        this._pt = new Point2D.Float();        this._sd = new FontStrikeDesc();        this._mat = new float[4];    }     private void init(int capacity) {        this._typo_flags = 0;        this._ercount = 0;        this._gvdata.init(capacity);    }

Lines 1–500 of 668.