Search in sources :

Example 1 with Font2DHandle

use of sun.font.Font2DHandle in project jdk8u_jdk by JetBrains.

the class PathGraphics method printedSimpleGlyphVector.

/* GlyphVectors are usually encountered because TextLayout is in use.
     * Some times TextLayout is needed to handle complex text or some
     * rendering attributes trigger it.
     * We try to print GlyphVectors by reconstituting into a String,
     * as that is most recoverable for applications that export to formats
     * such as Postscript or PDF. In some cases (eg where its not complex
     * text and its just that positions aren't what we'd expect) we print
     * one character at a time. positioning individually.
     * Failing that, if we can directly send glyph codes to the printer
     * then we do that (printGlyphVector).
     * As a last resort we return false and let the caller print as filled
     * shapes.
     */
boolean printedSimpleGlyphVector(GlyphVector g, float x, float y) {
    int flags = g.getLayoutFlags();
    /* We can't handle RTL, re-ordering, complex glyphs etc by
         * reconstituting glyphs into a String. So if any flags besides
         * position adjustments are set, see if we can directly
         * print the GlyphVector as glyph codes, using the positions
         * layout has assigned. If that fails return false;
         */
    if (flags != 0 && flags != GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS) {
        return printGlyphVector(g, x, y);
    }
    Font font = g.getFont();
    Font2D font2D = FontUtilities.getFont2D(font);
    if (font2D.handle.font2D != font2D) {
        /* suspicious, may be a bad font. lets bail */
        return false;
    }
    Hashtable<Font2DHandle, Object> fontMap;
    synchronized (PathGraphics.class) {
        fontMap = fontMapRef.get();
        if (fontMap == null) {
            fontMap = new Hashtable<Font2DHandle, Object>();
            fontMapRef = new SoftReference<Hashtable<Font2DHandle, Object>>(fontMap);
        }
    }
    int numGlyphs = g.getNumGlyphs();
    int[] glyphCodes = g.getGlyphCodes(0, numGlyphs, null);
    char[] glyphToCharMap = null;
    char[][] mapArray = null;
    CompositeFont cf = null;
    /* Build the needed maps for this font in a synchronized block */
    synchronized (fontMap) {
        if (font2D instanceof CompositeFont) {
            cf = (CompositeFont) font2D;
            int numSlots = cf.getNumSlots();
            mapArray = (char[][]) fontMap.get(font2D.handle);
            if (mapArray == null) {
                mapArray = new char[numSlots][];
                fontMap.put(font2D.handle, mapArray);
            }
            for (int i = 0; i < numGlyphs; i++) {
                int slot = glyphCodes[i] >>> 24;
                if (slot >= numSlots) {
                    /* shouldn't happen */
                    return false;
                }
                if (mapArray[slot] == null) {
                    Font2D slotFont = cf.getSlotFont(slot);
                    char[] map = (char[]) fontMap.get(slotFont.handle);
                    if (map == null) {
                        map = getGlyphToCharMapForFont(slotFont);
                    }
                    mapArray[slot] = map;
                }
            }
        } else {
            glyphToCharMap = (char[]) fontMap.get(font2D.handle);
            if (glyphToCharMap == null) {
                glyphToCharMap = getGlyphToCharMapForFont(font2D);
                fontMap.put(font2D.handle, glyphToCharMap);
            }
        }
    }
    char[] chars = new char[numGlyphs];
    if (cf != null) {
        for (int i = 0; i < numGlyphs; i++) {
            int gc = glyphCodes[i];
            char[] map = mapArray[gc >>> 24];
            gc = gc & 0xffffff;
            if (map == null) {
                return false;
            }
            /* X11 symbol & dingbats fonts used only for global metrics,
                 * so the glyph codes we have really refer to Lucida Sans
                 * Regular.
                 * So its possible the glyph code may appear out of range.
                 * Note that later on we double-check the glyph codes that
                 * we get from re-creating the GV from the string are the
                 * same as those we started with.
                 *
                 * If the glyphcode is INVISIBLE_GLYPH_ID then this may
                 * be \t, \n or \r which are mapped to that by layout.
                 * This is a case we can handle. It doesn't matter what
                 * character we use (we use \n) so long as layout maps it
                 * back to this in the verification, since the invisible
                 * glyph isn't visible :)
                 */
            char ch;
            if (gc == CharToGlyphMapper.INVISIBLE_GLYPH_ID) {
                ch = '\n';
            } else if (gc < 0 || gc >= map.length) {
                return false;
            } else {
                ch = map[gc];
            }
            if (ch != CharToGlyphMapper.INVISIBLE_GLYPH_ID) {
                chars[i] = ch;
            } else {
                return false;
            }
        }
    } else {
        for (int i = 0; i < numGlyphs; i++) {
            int gc = glyphCodes[i];
            char ch;
            if (gc == CharToGlyphMapper.INVISIBLE_GLYPH_ID) {
                ch = '\n';
            } else if (gc < 0 || gc >= glyphToCharMap.length) {
                return false;
            } else {
                ch = glyphToCharMap[gc];
            }
            if (ch != CharToGlyphMapper.INVISIBLE_GLYPH_ID) {
                chars[i] = ch;
            } else {
                return false;
            }
        }
    }
    FontRenderContext gvFrc = g.getFontRenderContext();
    GlyphVector gv2 = font.createGlyphVector(gvFrc, chars);
    if (gv2.getNumGlyphs() != numGlyphs) {
        return printGlyphVector(g, x, y);
    }
    int[] glyphCodes2 = gv2.getGlyphCodes(0, numGlyphs, null);
    /*
         * Needed to double-check remapping of X11 symbol & dingbats.
         */
    for (int i = 0; i < numGlyphs; i++) {
        if (glyphCodes[i] != glyphCodes2[i]) {
            return printGlyphVector(g, x, y);
        }
    }
    FontRenderContext g2dFrc = getFontRenderContext();
    boolean compatibleFRC = gvFrc.equals(g2dFrc);
    /* If differ only in specifying A-A or a translation, these are
         * also compatible FRC's, and we can do one drawString call.
         */
    if (!compatibleFRC && gvFrc.usesFractionalMetrics() == g2dFrc.usesFractionalMetrics()) {
        AffineTransform gvAT = gvFrc.getTransform();
        AffineTransform g2dAT = getTransform();
        double[] gvMatrix = new double[4];
        double[] g2dMatrix = new double[4];
        gvAT.getMatrix(gvMatrix);
        g2dAT.getMatrix(g2dMatrix);
        compatibleFRC = true;
        for (int i = 0; i < 4; i++) {
            if (gvMatrix[i] != g2dMatrix[i]) {
                compatibleFRC = false;
                break;
            }
        }
    }
    String str = new String(chars, 0, numGlyphs);
    int numFonts = platformFontCount(font, str);
    if (numFonts == 0) {
        return false;
    }
    float[] positions = g.getGlyphPositions(0, numGlyphs, null);
    boolean noPositionAdjustments = ((flags & GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS) == 0) || samePositions(gv2, glyphCodes2, glyphCodes, positions);
    /* We have to consider that the application may be directly
         * creating a GlyphVector, rather than one being created by
         * TextLayout or indirectly from drawString. In such a case, if the
         * font has layout attributes, the text may measure differently
         * when we reconstitute it into a String and ask for the length that
         * drawString would use. For example, KERNING will be applied in such
         * a case but that Font attribute is not applied when the application
         * directly created a GlyphVector. So in this case we need to verify
         * that the text measures the same in both cases - ie that the
         * layout attribute has no effect. If it does we can't always
         * use the drawString call unless we can coerce the drawString call
         * into measuring and displaying the string to the same length.
         * That is the case where there is only one font used and we can
         * specify the overall advance of the string. (See below).
         */
    Point2D gvAdvancePt = g.getGlyphPosition(numGlyphs);
    float gvAdvanceX = (float) gvAdvancePt.getX();
    boolean layoutAffectsAdvance = false;
    if (font.hasLayoutAttributes() && printingGlyphVector && noPositionAdjustments) {
        /* If TRACKING is in use then the glyph vector will report
             * position adjustments, then that ought to be sufficient to
             * tell us we can't just ask native to do "drawString". But layout
             * always sets the position adjustment flag, so we don't believe
             * it and verify the positions are really different than
             * createGlyphVector() (with no layout) would create. However
             * inconsistently, TRACKING is applied when creating a GlyphVector,
             * since it doesn't actually require "layout" (even though its
             * considered a layout attribute), it just requires a fractional
             * tweak to the[default]advances. So we need to specifically
             * check for tracking until such time as as we can trust
             * the GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS bit.
             */
        Map<TextAttribute, ?> map = font.getAttributes();
        Object o = map.get(TextAttribute.TRACKING);
        boolean tracking = o != null && (o instanceof Number) && (((Number) o).floatValue() != 0f);
        if (tracking) {
            noPositionAdjustments = false;
        } else {
            Rectangle2D bounds = font.getStringBounds(str, gvFrc);
            float strAdvanceX = (float) bounds.getWidth();
            if (Math.abs(strAdvanceX - gvAdvanceX) > 0.00001) {
                layoutAffectsAdvance = true;
            }
        }
    }
    if (compatibleFRC && noPositionAdjustments && !layoutAffectsAdvance) {
        drawString(str, x, y, font, gvFrc, 0f);
        return true;
    }
    /* If positions have not been explicitly assigned, we can
         * ask the string to be drawn adjusted to this width.
         * This call is supported only in the PS generator.
         * GDI has API to specify the advance for each glyph in a
         * string which could be used here too, but that is not yet
         * implemented, and we'd need to update the signature of the
         * drawString method to take the advances (ie relative positions)
         * and use that instead of the width.
         */
    if (numFonts == 1 && canDrawStringToWidth() && noPositionAdjustments) {
        drawString(str, x, y, font, gvFrc, gvAdvanceX);
        return true;
    }
    /* In some scripts chars drawn individually do not have the
         * same representation (glyphs) as when combined with other chars.
         * The logic here is erring on the side of caution, in particular
         * in including supplementary characters.
         */
    if (FontUtilities.isComplexText(chars, 0, chars.length)) {
        return printGlyphVector(g, x, y);
    }
    /* If we reach here we have mapped all the glyphs back
         * one-to-one to simple unicode chars that we know are in the font.
         * We can call "drawChars" on each one of them in turn, setting
         * the position based on the glyph positions.
         * There's typically overhead in this. If numGlyphs is 'large',
         * it may even be better to try printGlyphVector() in this case.
         * This may be less recoverable for apps, but sophisticated apps
         * should be able to recover the text from simple glyph vectors
         * and we can avoid penalising the more common case - although
         * this is already a minority case.
         */
    if (numGlyphs > 10 && printGlyphVector(g, x, y)) {
        return true;
    }
    for (int i = 0; i < numGlyphs; i++) {
        String s = new String(chars, i, 1);
        drawString(s, x + positions[i * 2], y + positions[i * 2 + 1], font, gvFrc, 0f);
    }
    return true;
}
Also used : Font2D(sun.font.Font2D) TextAttribute(java.awt.font.TextAttribute) Font(java.awt.Font) CompositeFont(sun.font.CompositeFont) Point2D(java.awt.geom.Point2D) Font2DHandle(sun.font.Font2DHandle) CompositeFont(sun.font.CompositeFont) GlyphVector(java.awt.font.GlyphVector) Hashtable(java.util.Hashtable) Rectangle2D(java.awt.geom.Rectangle2D) RoundRectangle2D(java.awt.geom.RoundRectangle2D) Paint(java.awt.Paint) AffineTransform(java.awt.geom.AffineTransform) FontRenderContext(java.awt.font.FontRenderContext)

Aggregations

Font (java.awt.Font)1 Paint (java.awt.Paint)1 FontRenderContext (java.awt.font.FontRenderContext)1 GlyphVector (java.awt.font.GlyphVector)1 TextAttribute (java.awt.font.TextAttribute)1 AffineTransform (java.awt.geom.AffineTransform)1 Point2D (java.awt.geom.Point2D)1 Rectangle2D (java.awt.geom.Rectangle2D)1 RoundRectangle2D (java.awt.geom.RoundRectangle2D)1 Hashtable (java.util.Hashtable)1 CompositeFont (sun.font.CompositeFont)1 Font2D (sun.font.Font2D)1 Font2DHandle (sun.font.Font2DHandle)1