Package flash.fonts

Source Code of flash.fonts.JREFontManager

/*
*
*  Licensed to the Apache Software Foundation (ASF) under one or more
*  contributor license agreements.  See the NOTICE file distributed with
*  this work for additional information regarding copyright ownership.
*  The ASF licenses this file to You under the Apache License, Version 2.0
*  (the "License"); you may not use this file except in compliance with
*  the License.  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*
*/

package flash.fonts;

import flash.swf.builder.types.PathIteratorWrapper;
import flash.swf.builder.types.ShapeBuilder;
import flash.swf.types.GlyphEntry;
import flash.swf.types.Shape;
import flash.swf.SwfConstants;
import flash.util.Trace;

import java.awt.font.FontRenderContext;
import java.awt.font.GlyphMetrics;
import java.awt.font.GlyphVector;
import java.awt.font.TextAttribute;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.BufferedInputStream;
import java.net.URL;
import java.util.Locale;
import java.util.Map;
import java.util.ArrayList;
import java.util.HashMap;

/**
* The <code>JREFontManager</code> attempts to optimize the loading of
* JDK fonts through caching of <code>FontSet</code>s. A
* <code>FontSet</code> keeps track of the different styles (faces)
* for a given font family as <code>FontFace</code> instances. A
* manager can derive available styles from a single
* <code>FontFace</code> through its reference to a
* <code>java.awt.Font</code> object.
*
* @author Peter Farland
*/
@SuppressWarnings("unchecked")
public class JREFontManager extends CachedFontManager
{
    private boolean readLocalFonts;
    private Map<String, LocalFont> localFonts;
    public static String LOCAL_FONTS_SNAPSHOT = "flex.fonts-snapshot";
    private String localFontsFile;

    public void initialize(Map map)
    {
        super.initialize(map);
        if (map != null)
        {
            localFontsFile = (String)map.get(LOCAL_FONTS_SNAPSHOT);
        }
        if (localFontsFile == null)
        {
            localFontsFile = "localFonts.ser";
        }
    }

    protected String createFontFromLocation(Object location, int requestedStyle, boolean useTwips)
    {
        String family = null;
        InputStream is = null;

        try
        {
            if (location != null && location instanceof URL)
            {
                URL url = (URL)location;

                if (url.getProtocol().toLowerCase().indexOf("file") > -1)
                {
                    File f = new File(url.getFile());
                    // Do NOT buffer this stream, Font.createFont() does it for us.
                    is = new FileInputStream(f);
                }
                else
                {
                    is = url.openStream();
                }

                Font font = Font.createFont(Font.TRUETYPE_FONT, is);

                // Get the family and fontName info before we derive a new size as
                // the name might be reset to dialog!
                family = font.getFamily();
                String fontName = font.getFontName(Locale.ENGLISH);

                // Prior to Flex 4 we tried to validate that the requested style
                // matched the style of the font.
                if (majorCompatibilityVersion < 4)
                {
                    // We need to examine the name for the real style as
                    // Font.createFont always sets style to PLAIN and the user
                    // may have given us the wrong style info
                    int guessedStyle = CachedFontFace.guessStyleFromSubFamilyName(fontName);

                    // Only bother deriving a font and storing the work if this
                    // really was the style requested
                    if (requestedStyle != guessedStyle)
                        return null;
                }

                // Now that we've processed the font with the JRE font manager, we now need to get the fsType
                // and copyright info from from Batik.  This is a bit strange, as one of the advantages of the
                // JRE font manager is that it can process some fonts that Batik cannot.  This check is needed,
                // though, anytime we create a font.  So it may mean we don't need the JRE font manager anymore,
                // but since I'm not sure of that, it'll be kept in for awhile.
                String locationStr = BatikFontManager.processLocation(location);
                org.apache.batik.svggen.font.Font batikFont = org.apache.batik.svggen.font.Font.create(locationStr);
                if (batikFont == null)
                {
                    throw new FontFormatException ("Unable to create font." );
                }
                FSType type = FSType.getFSType(batikFont);
                if (! type.usableByFlex)
                {
                    throw new BatikFontManager.UnusableFontLicense(location + "", type.description);
                }
                String copyright = batikFont.getNameTable().getRecord((short)0);
                String trademark = batikFont.getNameTable().getRecord((short)7);

                // Set to a default size so metrics are established
                // getAttributes() returns a Map which is based on a Hashtable of <Obj,Obj>, or <TA,Obj> in practice
                // we need to suppress the cast because getAttributes defines the return type as <TA,?>
                Map<TextAttribute, Object> attributes = (Map<TextAttribute, Object>) font.getAttributes();

                attributes.put(TextAttribute.FAMILY, family);
                attributes.put(TextAttribute.SIZE, DEFAULT_FONT_SIZE_OBJECT);
                attributes.put(TextAttribute.POSTURE, CachedFontFace.isItalic(requestedStyle) ? TextAttribute.POSTURE_OBLIQUE : TextAttribute.POSTURE_REGULAR);
                attributes.put(TextAttribute.WEIGHT, CachedFontFace.isBold(requestedStyle) ? TextAttribute.WEIGHT_BOLD : TextAttribute.WEIGHT_REGULAR);

                font = font.deriveFont(attributes);

                FontSet fontSet = (FontSet)getFontCache().get(family);
                if (fontSet == null)
                {
                    fontSet = new FontSet(maxFacesPerFont);
                    getFontCache().put(family, fontSet);
                }

                fontSet.put(requestedStyle, new JREFontFace(font, requestedStyle, maxGlyphsPerFace, type, copyright, trademark, useTwips));
            }
        }
        catch (FileNotFoundException ex)
        {
            return null;
        }
        catch (FontFormatException ex)
        {
            return null;
        }
        catch (IOException ex)
        {
            return null;
        }
        finally
        {
            try
            {
                is.close();
            }
            catch (Throwable t)
            {
            }
        }

        return family;
    }

    /**
     * Attempts to load a font from the cache by location or from disk if it is the first
     * request at this address. The location is bound to a font family name after the initial
     * loading, and the relationship exists for the lifetime of the cache.
     *
     * @param location
     * @param style
     * @return FontSet.FontFace
     */
    public FontFace getEntryFromLocation(URL location, int style, boolean useTwips)
    {
        FontFace entry = null;
        Object fam = getFontFileCache().get(location);

        if (fam == null)
        {
            fam = createFontFromLocation(location, style, useTwips);
        }

        if (fam != null)
        {
            String family = fam.toString();

            FontSet fontSet = (FontSet)getFontCache().get(family);

            // The font file cache should still have this family
            // from the location fetch above...
            if (fontSet != null)
            {
                entry = fontSet.get(style);
            }
        }

        return entry;
    }

    private static ArrayList<String> systemFontNames = null;

    protected FontSet createSetForSystemFont(String family, int style, boolean useTwips)
    {
        FontSet fontSet = null;
        if (family != null)
        {
            if (systemFontNames == null)
                initializeSystemFonts();

            if (systemFontNames != null && !systemFontNames.contains(family.trim().toLowerCase()))
            {
                if (Trace.font)
                    Trace.trace("Font family '" + family + "' not known to JRE.");

                return null;
            }

            //Load a font by family and style, set size to 240 for greater granularity
            Font font = Font.decode(family + "-" + getStyleAsString(style) + "-" + DEFAULT_FONT_SIZE_STRING);

            fontSet = new FontSet(maxFacesPerFont);
            fontSet.put(font.getStyle(), new JREFontFace(font, font.getStyle(), maxGlyphsPerFace, null, null, null, useTwips));
        }
        return fontSet;
    }

    /**
     * Attempts to locate a font by family name and style from the JRE's list of
     * fonts, which are primarily system registered fonts.
     *
     * @param familyName
     * @param style      - either Font.PLAIN, Font.BOLD, Font.ITALIC or Font.BOLD+Font.ITALIC
     * @return FontSet.FontFace
     */
    public FontFace getEntryFromSystem(String familyName, int style, boolean useTwips)
    {
        if (! readLocalFonts)
          {
              readLocalFonts();
          }

        FontFace entry = null;

        FontSet fontSet = (FontSet)getFontCache().get(familyName);

        // This is likely to be the first time looking for this family
        if (fontSet == null)
        {
            fontSet = createSetForSystemFont(familyName, style, useTwips);
        }

        // If the family was invalid on the OS there's nothing more we can do here
        if (fontSet != null)
        {
            entry = fontSet.get(style);
        }

        if (entry != null)
        {
            LocalFont font = localFonts.get(entry.getPostscriptName());
            if (font == null)
            {
                // silent failure
                if (Trace.font)
                {
                    Trace.trace("Information on font " + familyName + " could not be found.  Run FontSnapshot to get a list of the current local fonts.");
                }
            }
            else
            {
                entry.setCopyright(font.copyright);
                entry.setTrademark(font.trademark);
                entry.setFSType(FSType.getFSType(font.fsType));
            }
        }

        return entry;
    }

  private void readLocalFonts()
    {
        readLocalFonts = true;

        localFonts = new HashMap<String, LocalFont>();
//    initDefaultLocalFonts();
        try
        {
            InputStream buffStream = new BufferedInputStream(new FileInputStream(localFontsFile));
            ObjectInputStream in = new ObjectInputStream(buffStream);
           
            // there's no way around suppressing the cast warning from Object, the types get erased anyway
            Map<String, LocalFont> customLocalFonts = (Map<String, LocalFont>)in.readObject();
           
            localFonts.putAll(customLocalFonts);
        }
        catch(FileNotFoundException fnfe)
        {
            // ignore... a message will be printed out later if needed
            if (Trace.font)
            {
                fnfe.printStackTrace();
            }
        }
        catch(Exception fnfe)
        {
            if (Trace.font)
            {
                Trace.trace("Could not read localFonts.ser: " + fnfe);
            }
        }
    }

    private String getStyleAsString(int style)
    {
        String styleName;

        switch (style)
        {
            case 1:
                styleName = "bold";
                break;
            case 2:
                styleName = "italic";
                break;
            case 3:
                styleName = "bolditalic";
                break;
            default:
                styleName = "plain";
        }

        return styleName;
    }

    private static void initializeSystemFonts()
    {
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        String[] fnts = ge.getAvailableFontFamilyNames();

        if (fnts != null)
        {
            systemFontNames = new ArrayList<String>(fnts.length);
            for (int i = 0; i < fnts.length; i++)
            {
                systemFontNames.add(fnts[i].trim().toLowerCase());
            }
        }
    }

    public static class JREFontFace extends CachedFontFace
    {
      // FIXME: need to deal with useTwips differently for caching

        public JREFontFace(Font font, int style, int maxGlyph, FSType fsType, String copyright, String trademark, boolean useTwips)
        {
            super(maxGlyph, style, fsType, copyright, trademark, useTwips);
            this.font = font;
            init();
        }

        private void init()
        {
            //Convert from device to grid co-ordinates, fixed at 72dpi
            emScale = SWF_EM_SQUARE / getPointSize(); //If you want to correct for resolution, multiply this value by 72/resolution

            scaleTransform = new AffineTransform();
            scaleTransform.setToScale(emScale, emScale);

            //We use a BufferedImage to get to the system FontMetrics...
            //Feel free to suggest a better way of getting this object.
            if (graphics == null)
            {
                BufferedImage bi = new BufferedImage(50, 50, BufferedImage.TYPE_INT_RGB);
                graphics = bi.createGraphics();
                graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
                graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            }

            fontMetrics = graphics.getFontMetrics(font);
            frc = new FontRenderContext(null, true, false);

            ascent = (int)Math.rint(fontMetrics.getAscent() * emScale * (useTwips ? SwfConstants.TWIPS_PER_PIXEL : 1));
            descent = (int)Math.rint(fontMetrics.getDescent() * emScale * (useTwips ? SwfConstants.TWIPS_PER_PIXEL : 1));

            //Ignore JDK leading values, they never match Flash MX - using an estimation technique similar to Breeze instead.
            //leading = (int)StrictMath.rint(fontMetrics.getLeading() * emScale);
            //if (leading == 0)
            lineGap = (int)Math.rint(Math.abs((getPointSize() - ascent - descent)));
        }

        public boolean canDisplay(char c)
        {
            return font.canDisplay(c);
        }

        public String getFamily()
        {
            return font.getName();
        }

        public int getFirstChar()
        {
            return 0;
        }

        public GlyphEntry getGlyphEntry(char c)
        {
            return (GlyphEntry)glyphCache.get(c);
        }

        protected GlyphEntry createGlyphEntry(char c)
        {
            return createGlyphEntry(c, c);
        }

        public GlyphEntry createGlyphEntry(char c, char referenceChar)
        {
            Shape swfShape = null;
            int advance = 0;

            GlyphVector gv = font.createGlyphVector(frc, new char[]{referenceChar});
            java.awt.Shape glyphOutline = gv.getGlyphOutline(0);

            GlyphMetrics metrics = gv.getGlyphMetrics(0);
            advance = (int)Math.rint(metrics.getAdvance()); //Do not scale here, DefineText needs values unscaled

            java.awt.Shape scaledShape = scaleTransform.createTransformedShape(glyphOutline);
            swfShape = createGlyphShape(scaledShape);

            GlyphEntry ge = new GlyphEntry();
            ge = new GlyphEntry(); //Note: we will set the index on building DefineFont2 tag
            ge.advance = (int)(advance * emScale * (useTwips ? SwfConstants.TWIPS_PER_PIXEL : 1));
            ge.shape = swfShape;
          //Glyph bounds are not used by the Flash Player so no need to calculate
          //Rectangle2D bs = scaledShape.getBounds2D();
          //bounds = new Rect((int)StrictMath.rint(bs.getMinX() * (useTwips ? SwfConstants.TWIPS_PER_PIXEL : 1)),
      //        (int)StrictMath.rint(bs.getMaxX() * (useTwips ? SwfConstants.TWIPS_PER_PIXEL : 1)),
      //        (int)StrictMath.rint(bs.getMinY() * (useTwips ? SwfConstants.TWIPS_PER_PIXEL : 1)),
      //        (int)StrictMath.rint(bs.getMaxY()) * (useTwips ? SwfConstants.TWIPS_PER_PIXEL : 1));
            //ge.bounds = bounds;
            ge.character = c;

            return ge;
        }

        private Shape createGlyphShape(java.awt.Shape outline)
        {
            ShapeBuilder shape = new ShapeBuilder(useTwips);
            shape.setCurrentLineStyle(0);
            shape.setCurrentFillStyle1(1);
            shape.setUseFillStyle1(true);
            shape.processShape(new PathIteratorWrapper(outline.getPathIterator(null)));

            return shape.build();
        }

        public int getAdvance(char c)
        {
            return 0//To change body of implemented methods use File | Settings | File Templates.
        }

        public Font getFont()
        {
            return font;
        }

        public int getMissingGlyphCode()
        {
            return font.getMissingGlyphCode();
        }

        public double getPointSize()
        {
            return font.getSize2D();
        }

        public FontRenderContext getFontRenderContext()
        {
            return frc;
        }

        public int getAscent()
        {
            return ascent;
        }

        public int getDescent()
        {
            return descent;
        }

        public int getLineGap()
        {
            return lineGap;
        }

        public int getNumGlyphs()
        {
            return font.getNumGlyphs();
        }

        public double getEmScale()
        {
            return emScale;
        }

        public String getPostscriptName()
        {
            return font.getPSName();
        }

        private Font font;
        private static Graphics2D graphics;
        private FontRenderContext frc;
        private FontMetrics fontMetrics;
        private int ascent;
        private int descent;
        private int lineGap;
        private double emScale;
        private AffineTransform scaleTransform;
    }
}
TOP

Related Classes of flash.fonts.JREFontManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.