draw(new Polygon(xPoints, yPoints, nPoints)); } /** * Draws the outline of a polygon defined by the specified * <code>Polygon</code> object. * @param p the polygon to draw. * @see java.awt.Graphics#fillPolygon * @see java.awt.Graphics#drawPolyline */ public void drawPolygon(Polygon p) { draw(p); } /** * Fills a closed polygon defined by * arrays of <i>x</i> and <i>y</i> coordinates. * <p> * This method draws the polygon defined by <code>nPoint</code> line * segments, where the first <code>nPoint - 1</code> * line segments are line segments from * <code>(xPoints[i - 1], yPoints[i - 1])</code> * to <code>(xPoints[i], yPoints[i])</code>, for * 1 ≤ <i>i</i> ≤ <code>nPoints</code>. * The figure is automatically closed by drawing a line connecting * the final point to the first point, if those points are different. * <p> * The area inside the polygon is defined using an * even-odd fill rule, also known as the alternating rule. * @param xPoints a an array of <code>x</code> coordinates. * @param yPoints a an array of <code>y</code> coordinates. * @param nPoints a the total number of points. * @see java.awt.Graphics#drawPolygon(int[], int[], int) */ public void fillPolygon(int xPoints[], int yPoints[], int nPoints) { fill(new Polygon(xPoints, yPoints, nPoints)); } /** * Fills the polygon defined by the specified Polygon object with * the graphics context's current color. * <p> * The area inside the polygon is defined using an * even-odd fill rule, also known as the alternating rule. * @param p the polygon to fill. * @see java.awt.Graphics#drawPolygon(int[], int[], int) */ public void fillPolygon(Polygon p) { fill(p); } /** * Draws the text given by the specified string, using this * graphics context's current font and color. The baseline of the * first character is at position (<i>x</i>, <i>y</i>) in this * graphics context's coordinate system. * @param str the string to be drawn. * @param x the <i>x</i> coordinate. * @param y the <i>y</i> coordinate. * @see java.awt.Graphics#drawBytes * @see java.awt.Graphics#drawChars * @since JDK1.0 */ public void drawString(String str, int x, int y) { drawString(str, (float) x, (float) y); } public void drawString(String str, float x, float y) { if (str.length() == 0) { return; } TextLayout layout = new TextLayout(str, getFont(), getFontRenderContext()); layout.draw(this, x, y); } protected void drawString(String str, float x, float y, Font font, FontRenderContext frc, float w) { TextLayout layout = new TextLayout(str, font, frc); Shape textShape = layout.getOutline(AffineTransform.getTranslateInstance(x, y)); fill(textShape); } /** * Draws the text given by the specified iterator, using this * graphics context's current color. The iterator has to specify a font * for each character. The baseline of the * first character is at position (<i>x</i>, <i>y</i>) in this * graphics context's coordinate system. * @param iterator the iterator whose text is to be drawn * @param x the <i>x</i> coordinate. * @param y the <i>y</i> coordinate. * @see java.awt.Graphics#drawBytes * @see java.awt.Graphics#drawChars */ public void drawString(AttributedCharacterIterator iterator, int x, int y) { drawString(iterator, (float) x, (float) y); } public void drawString(AttributedCharacterIterator iterator, float x, float y) { if (iterator == null) { throw new NullPointerException("attributedcharacteriterator is null"); } TextLayout layout = new TextLayout(iterator, getFontRenderContext()); layout.draw(this, x, y); } /** * Draws a GlyphVector. * The rendering attributes applied include the clip, transform, * paint or color, and composite attributes. The GlyphVector specifies * individual glyphs from a Font. * @param g The GlyphVector to be drawn. * @param x,y The coordinates where the glyphs should be drawn. * @see #setPaint * @see java.awt.Graphics#setColor * @see #transform * @see #setTransform * @see #setComposite * @see #clip * @see #setClip */ public void drawGlyphVector(GlyphVector g, float x, float y) { /* We should not reach here if printingGlyphVector is already true. * Add an assert so this can be tested if need be. * But also ensure that we do at least render properly by filling * the outline. */ if (printingGlyphVector) { assert !printingGlyphVector; // ie false. fill(g.getOutline(x, y)); return; } try { printingGlyphVector = true; if (RasterPrinterJob.shapeTextProp || !printedSimpleGlyphVector(g, x, y)) { fill(g.getOutline(x, y)); } } finally { printingGlyphVector = false; } } protected static SoftReference<Hashtable<Font2DHandle,Object>> fontMapRef = new SoftReference<Hashtable<Font2DHandle,Object>>(null); protected int platformFontCount(Font font, String str) { return 0; } /** * Default implementation returns false. * Callers of this method must always be prepared for this, * and delegate to outlines or some other solution. */ protected boolean printGlyphVector(GlyphVector gv, float x, float y) { return false; } /* 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; } /* The same codes must be in the same positions for this to return true. * This would look cleaner if it took the original GV as a parameter but * we already have the codes and will need to get the positions array * too in most cases anyway. So its cheaper to pass them in. * This call wouldn't be necessary if layout didn't always set the * FLAG_HAS_POSITION_ADJUSTMENTS even if the default advances are used * and there was no re-ordering (this should be fixed some day). */ private boolean samePositions(GlyphVector gv, int[] gvcodes, int[] origCodes, float[] origPositions) { int numGlyphs = gv.getNumGlyphs(); float[] gvpos = gv.getGlyphPositions(0, numGlyphs, null); /* this shouldn't happen here, but just in case */ if (numGlyphs != gvcodes.length || /* real paranoia here */ origCodes.length != gvcodes.length || origPositions.length != gvpos.length) { return false; } for (int i=0; i<numGlyphs; i++) { if (gvcodes[i] != origCodes[i] || gvpos[i] != origPositions[i]) { return false; } } return true; } protected boolean canDrawStringToWidth() { return false; } /* return an array which can map glyphs back to char codes. * Glyphs which aren't mapped from a simple unicode code point * will have no mapping in this array, and will be assumed to beLines 501–1000 of 1896. Previous lines Load more
draw(new Polygon(xPoints, yPoints, nPoints)); } /** * Draws the outline of a polygon defined by the specified * <code>Polygon</code> object. * @param p the polygon to draw. * @see java.awt.Graphics#fillPolygon * @see java.awt.Graphics#drawPolyline */ public void drawPolygon(Polygon p) { draw(p); } /** * Fills a closed polygon defined by * arrays of <i>x</i> and <i>y</i> coordinates. * <p> * This method draws the polygon defined by <code>nPoint</code> line * segments, where the first <code>nPoint - 1</code> * line segments are line segments from * <code>(xPoints[i - 1], yPoints[i - 1])</code> * to <code>(xPoints[i], yPoints[i])</code>, for * 1 ≤ <i>i</i> ≤ <code>nPoints</code>. * The figure is automatically closed by drawing a line connecting * the final point to the first point, if those points are different. * <p> * The area inside the polygon is defined using an * even-odd fill rule, also known as the alternating rule. * @param xPoints a an array of <code>x</code> coordinates. * @param yPoints a an array of <code>y</code> coordinates. * @param nPoints a the total number of points. * @see java.awt.Graphics#drawPolygon(int[], int[], int) */ public void fillPolygon(int xPoints[], int yPoints[], int nPoints) { fill(new Polygon(xPoints, yPoints, nPoints)); } /** * Fills the polygon defined by the specified Polygon object with * the graphics context's current color. * <p> * The area inside the polygon is defined using an * even-odd fill rule, also known as the alternating rule. * @param p the polygon to fill. * @see java.awt.Graphics#drawPolygon(int[], int[], int) */ public void fillPolygon(Polygon p) { fill(p); } /** * Draws the text given by the specified string, using this * graphics context's current font and color. The baseline of the * first character is at position (<i>x</i>, <i>y</i>) in this * graphics context's coordinate system. * @param str the string to be drawn. * @param x the <i>x</i> coordinate. * @param y the <i>y</i> coordinate. * @see java.awt.Graphics#drawBytes * @see java.awt.Graphics#drawChars * @since JDK1.0 */ public void drawString(String str, int x, int y) { drawString(str, (float) x, (float) y); } public void drawString(String str, float x, float y) { if (str.length() == 0) { return; } TextLayout layout = new TextLayout(str, getFont(), getFontRenderContext()); layout.draw(this, x, y); } protected void drawString(String str, float x, float y, Font font, FontRenderContext frc, float w) { TextLayout layout = new TextLayout(str, font, frc); Shape textShape = layout.getOutline(AffineTransform.getTranslateInstance(x, y)); fill(textShape); } /** * Draws the text given by the specified iterator, using this * graphics context's current color. The iterator has to specify a font * for each character. The baseline of the * first character is at position (<i>x</i>, <i>y</i>) in this * graphics context's coordinate system. * @param iterator the iterator whose text is to be drawn * @param x the <i>x</i> coordinate. * @param y the <i>y</i> coordinate. * @see java.awt.Graphics#drawBytes * @see java.awt.Graphics#drawChars */ public void drawString(AttributedCharacterIterator iterator, int x, int y) { drawString(iterator, (float) x, (float) y); } public void drawString(AttributedCharacterIterator iterator, float x, float y) { if (iterator == null) { throw new NullPointerException("attributedcharacteriterator is null"); } TextLayout layout = new TextLayout(iterator, getFontRenderContext()); layout.draw(this, x, y); } /** * Draws a GlyphVector. * The rendering attributes applied include the clip, transform, * paint or color, and composite attributes. The GlyphVector specifies * individual glyphs from a Font. * @param g The GlyphVector to be drawn. * @param x,y The coordinates where the glyphs should be drawn. * @see #setPaint * @see java.awt.Graphics#setColor * @see #transform * @see #setTransform * @see #setComposite * @see #clip * @see #setClip */ public void drawGlyphVector(GlyphVector g, float x, float y) { /* We should not reach here if printingGlyphVector is already true. * Add an assert so this can be tested if need be. * But also ensure that we do at least render properly by filling * the outline. */ if (printingGlyphVector) { assert !printingGlyphVector; // ie false. fill(g.getOutline(x, y)); return; } try { printingGlyphVector = true; if (RasterPrinterJob.shapeTextProp || !printedSimpleGlyphVector(g, x, y)) { fill(g.getOutline(x, y)); } } finally { printingGlyphVector = false; } } protected static SoftReference<Hashtable<Font2DHandle,Object>> fontMapRef = new SoftReference<Hashtable<Font2DHandle,Object>>(null); protected int platformFontCount(Font font, String str) { return 0; } /** * Default implementation returns false. * Callers of this method must always be prepared for this, * and delegate to outlines or some other solution. */ protected boolean printGlyphVector(GlyphVector gv, float x, float y) { return false; } /* 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; } /* The same codes must be in the same positions for this to return true. * This would look cleaner if it took the original GV as a parameter but * we already have the codes and will need to get the positions array * too in most cases anyway. So its cheaper to pass them in. * This call wouldn't be necessary if layout didn't always set the * FLAG_HAS_POSITION_ADJUSTMENTS even if the default advances are used * and there was no re-ordering (this should be fixed some day). */ private boolean samePositions(GlyphVector gv, int[] gvcodes, int[] origCodes, float[] origPositions) { int numGlyphs = gv.getNumGlyphs(); float[] gvpos = gv.getGlyphPositions(0, numGlyphs, null); /* this shouldn't happen here, but just in case */ if (numGlyphs != gvcodes.length || /* real paranoia here */ origCodes.length != gvcodes.length || origPositions.length != gvpos.length) { return false; } for (int i=0; i<numGlyphs; i++) { if (gvcodes[i] != origCodes[i] || gvpos[i] != origPositions[i]) { return false; } } return true; } protected boolean canDrawStringToWidth() { return false; } /* return an array which can map glyphs back to char codes. * Glyphs which aren't mapped from a simple unicode code point * will have no mapping in this array, and will be assumed to be