1 /*
   2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   3  *
   4  * This code is free software; you can redistribute it and/or modify it
   5  * under the terms of the GNU General Public License version 2 only, as
   6  * published by the Free Software Foundation.  Oracle designates this
   7  * particular file as subject to the "Classpath" exception as provided
   8  * by Oracle in the LICENSE file that accompanied this code.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 2 along with this work; if not, write to the Free Software Foundation,
  18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19  *
  20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  23  *
  24  */
  25 
  26 /*
  27  *
  28  * (C) Copyright IBM Corp. 2003 - All Rights Reserved
  29  */
  30 
  31 package sun.font;
  32 
  33 import sun.font.GlyphLayout.*;
  34 import java.awt.geom.Point2D;
  35 import java.lang.ref.SoftReference;
  36 import java.util.concurrent.ConcurrentHashMap;
  37 import java.util.Locale;
  38 import java.util.WeakHashMap;
  39 
  40 /*
  41  * different ways to do this
  42  * 1) each physical font2d keeps a hashtable mapping scripts to layout
  43  * engines, we query and fill this cache.
  44  * 2) we keep a mapping independent of font using the key Most likely
  45  * few fonts will be used, so option 2 seems better
  46  *
  47  * Once we know which engine to use for a font, we always know, so we
  48  * shouldn't have to recheck each time we do layout.  So the cache is
  49  * ok.
  50  *
  51  * Should we reuse engines?  We could instantiate an engine for each
  52  * font/script pair.  The engine would hold onto the table(s) from the
  53  * font that it needs.  If we have multiple threads using the same
  54  * engine, we still need to keep the state separate, so the native
  55  * engines would still need to be allocated for each call, since they
  56  * keep their state in themselves.  If they used the passed-in GVData
  57  * arrays directly (with some checks for space) then since each GVData
  58  * is different per thread, we could reuse the layout engines.  This
  59  * still requires a separate layout engine per font, because of the
  60  * table state in the engine.  If we pushed that out too and passed it
  61  * in with the native call as well, we'd be ok if the layout engines
  62  * keep all their process state on the stack, but I don't know if this
  63  * is true.  Then we'd basically just be down to an engine index which
  64  * we pass into native and then invoke the engine code (now a
  65  * procedure call, not an object invocation) based on a switch on the
  66  * index.  There would be only half a dozen engine objects then, not
  67  * potentially half a dozen per font.  But we'd have to stack-allocate
  68  * some state that included the pointer to the required font tables.
  69  *
  70  * Seems for now that the way to do things is to come in with a
  71  * selector and the font.  The selector indicates which engine to use,
  72  * the engine is stack allocated and initialized with the required
  73  * font tables (the selector indicates which).  Then layout is called,
  74  * the contents are copied (or not), and the stack is destroyed on
  75  * exit. So the association is between the font/script (layout engine
  76  * desc) and one of a few permanent engine objects, which are
  77  * handed the key when they need to process something.  In the native
  78  * case, the engine holds an index, and just passes it together with
  79  * the key info down to native.  Some default cases are the 'default
  80  * layout' case that just runs the c2gmapper, this stays in java and
  81  * just uses the mapper from the font/strike.  Another default case
  82  * might be the unicode arabic shaper, since this doesn't care about
  83  * the font (or script or lang?) it wouldn't need to extract this
  84  * data.  It could be (yikes) ported back to java even to avoid
  85  * upcalls to check if the font supports a particular unicode
  86  * character.
  87  *
  88  * I'd expect that the majority of scripts use the default mapper for
  89  * a particular font.  Loading the hastable with 40 or so keys 30+ of
  90  * which all map to the same object is unfortunate.  It might be worth
  91  * instead having a per-font list of 'scripts with non-default
  92  * engines', e.g. the factory has a hashtable mapping fonts to 'script
  93  * lists' (the factory has this since the design potentially has other
  94  * factories, though I admit there's no client for this yet and no
  95  * public api) and then the script list is queried for the script in
  96  * question.  it can be preloaded at creation time with all the
  97  * scripts that don't have default engines-- either a list or a hash
  98  * table, so a null return from the table means 'default' and not 'i
  99  * don't know yet'.
 100  *
 101  * On the other hand, in most all cases the number of unique
 102  * script/font combinations will be small, so a flat hashtable should
 103  * suffice.
 104  * */
 105 public final class SunLayoutEngine implements LayoutEngine, LayoutEngineFactory {
 106     static {
 107         FontManagerNativeLibrary.load();
 108     }
 109 
 110     private LayoutEngineKey key;
 111 
 112     private static LayoutEngineFactory instance;
 113 
 114     public static LayoutEngineFactory instance() {
 115         if (instance == null) {
 116             instance = new SunLayoutEngine();
 117         }
 118         return instance;
 119     }
 120 
 121     private SunLayoutEngine() {
 122         // actually a factory, key is null so layout cannot be called on it
 123     }
 124 
 125     public LayoutEngine getEngine(Font2D font, int script, int lang) {
 126         return getEngine(new LayoutEngineKey(font, script, lang));
 127     }
 128 
 129   // !!! don't need this unless we have more than one sun layout engine...
 130     public LayoutEngine getEngine(LayoutEngineKey key) {
 131         ConcurrentHashMap<LayoutEngineKey, LayoutEngine> cache = cacheref.get();
 132         if (cache == null) {
 133             cache = new ConcurrentHashMap<>();
 134             cacheref = new SoftReference<>(cache);
 135         }
 136 
 137         LayoutEngine e = cache.get(key);
 138         if (e == null) {
 139             LayoutEngineKey copy = key.copy();
 140             e = new SunLayoutEngine(copy);
 141             cache.put(copy, e);
 142         }
 143         return e;
 144     }
 145     private SoftReference<ConcurrentHashMap<LayoutEngineKey, LayoutEngine>> cacheref =
 146         new SoftReference<>(null);
 147 
 148     private SunLayoutEngine(LayoutEngineKey key) {
 149         this.key = key;
 150     }
 151 
 152     static WeakHashMap<Font2D, Boolean> aatInfo = new WeakHashMap<>();
 153 
 154     private boolean isAAT(Font2D font) {
 155        Boolean aatObj;
 156        synchronized (aatInfo) {
 157            aatObj = aatInfo.get(font);
 158        }
 159        if (aatObj != null) {
 160           return aatObj.booleanValue();
 161        }
 162        boolean aat = false;
 163        if (font instanceof TrueTypeFont) {
 164            TrueTypeFont ttf = (TrueTypeFont)font;
 165            aat =  ttf.getDirectoryEntry(TrueTypeFont.morxTag) != null ||
 166                   ttf.getDirectoryEntry(TrueTypeFont.mortTag) != null;
 167        } else if (font instanceof PhysicalFont) {
 168            PhysicalFont pf = (PhysicalFont)font;
 169            aat =  pf.getTableBytes(TrueTypeFont.morxTag) != null ||
 170                   pf.getTableBytes(TrueTypeFont.mortTag) != null;
 171        }
 172        synchronized (aatInfo) {
 173            aatInfo.put(font, Boolean.valueOf(aat));
 174        }
 175        return aat;
 176     }
 177 
 178     public void layout(FontStrikeDesc desc, float[] mat, float ptSize, int gmask,
 179                        int baseIndex, TextRecord tr, int typo_flags,
 180                        Point2D.Float pt, GVData data) {
 181         Font2D font = key.font();
 182         FontStrike strike = font.getStrike(desc);
 183         long layoutTables = font.getLayoutTableCache();
 184         long pNativeFont = font.getPlatformNativeFontPtr(); // used on OSX
 185         // pScaler probably not needed long term.
 186         long pScaler = 0L;
 187         if (font instanceof FileFont) {
 188             pScaler = ((FileFont)font).getScaler().nativeScaler;
 189         }
 190         shape(font, strike, ptSize, mat, pScaler, pNativeFont,
 191               layoutTables, isAAT(font),
 192               tr.text, data, key.script(),
 193               tr.start, tr.limit, baseIndex, pt,
 194               typo_flags, gmask);
 195     }
 196 
 197     /* Native method to invoke harfbuzz layout engine */
 198     private static native boolean
 199         shape(Font2D font, FontStrike strike, float ptSize, float[] mat,
 200               long pscaler, long pNativeFont, long layoutTables, boolean aat,
 201               char[] chars, GVData data,
 202               int script, int offset, int limit,
 203               int baseIndex, Point2D.Float pt, int typo_flags, int slot);
 204 }