/** * Initialise the font to be used based on configuration * * @param baseFont The AWT font to render * @param size The point size of the font to generated * @param bold True if the font should be rendered in bold typeface * @param italic True if the font should be rendered in bold typeface */ private void initializeFont(Font baseFont, int size, boolean bold, boolean italic) { Map attributes = baseFont.getAttributes(); attributes.put(TextAttribute.SIZE, new Float(size)); attributes.put(TextAttribute.WEIGHT, bold ? TextAttribute.WEIGHT_BOLD : TextAttribute.WEIGHT_REGULAR); attributes.put(TextAttribute.POSTURE, italic ? TextAttribute.POSTURE_OBLIQUE : TextAttribute.POSTURE_REGULAR); try { attributes.put(TextAttribute.class.getDeclaredField("KERNING").get(null), TextAttribute.class.getDeclaredField( "KERNING_ON").get(null)); } catch (Exception ignored) { } font = baseFont.deriveFont(attributes); FontMetrics metrics = GlyphPage.getScratchGraphics().getFontMetrics(font); ascent = metrics.getAscent(); descent = metrics.getDescent(); leading = metrics.getLeading(); // Determine width of space glyph (getGlyphPixelBounds gives a width of zero). char[] chars = " ".toCharArray(); GlyphVector vector = font.layoutGlyphVector(GlyphPage.renderContext, chars, 0, chars.length, Font.LAYOUT_LEFT_TO_RIGHT); spaceWidth = vector.getGlyphLogicalBounds(0).getBounds().width; }
/** * Returns the glyph for the specified codePoint. If the glyph does not exist yet, * it is created and queued to be loaded. * * @param glyphCode The code of the glyph to locate * @param codePoint The code point associated with the glyph * @param bounds The bounds of the glyph on the page * @param vector The vector the glyph is part of * @param index The index of the glyph within the vector * @return The glyph requested */ private Glyph getGlyph (int glyphCode, int codePoint, Rectangle bounds, GlyphVector vector, int index) { if (glyphCode < 0 || glyphCode >= MAX_GLYPH_CODE) { // GlyphVector#getGlyphCode sometimes returns negative numbers on OS X. return new Glyph(codePoint, bounds, vector, index, this) { public boolean isMissing () { return true; } }; } int pageIndex = glyphCode / PAGE_SIZE; int glyphIndex = glyphCode & (PAGE_SIZE - 1); Glyph glyph = null; Glyph[] page = glyphs[pageIndex]; if (page != null) { glyph = page[glyphIndex]; if (glyph != null) return glyph; } else page = glyphs[pageIndex] = new Glyph[PAGE_SIZE]; // Add glyph so size information is available and queue it so its image can be loaded later. glyph = page[glyphIndex] = new Glyph(codePoint, bounds, vector, index, this); queuedGlyphs.add(glyph); return glyph; }
public static void main(String[] args) { Font defaultFont = new Font(null); FontRenderContext defaultFrc = new FontRenderContext(new AffineTransform(), true, true); GlyphVector gv = defaultFont.createGlyphVector(defaultFrc, "test"); //this causes the bounds to be cached //which is necessary to trigger the bug gv.getGlyphLogicalBounds(0); //this correctly gets the position of the overall advance Point2D glyphPosition = gv.getGlyphPosition(gv.getNumGlyphs()); // this sets the position of the overall advance, // but also incorrectly tries to clear the bounds cache // of a specific glyph indexed by the glyphIndex parameter // even if the glyphIndex represents the overall advance // (i.e. if glyphIndex == getNumGlyphs()) gv.setGlyphPosition(gv.getNumGlyphs(), glyphPosition); }
public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D)g; FontRenderContext frc = new FontRenderContext(null, true, true); Font f = helvFont.deriveFont(Font.PLAIN, 40); System.out.println("font = " +f.getFontName()); GlyphVector gv = f.createGlyphVector(frc, codes); g.setFont(f); g.setColor(Color.white); g.fillRect(0,0,400,400); g.setColor(Color.black); g2.drawGlyphVector(gv, 5,200); g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); g2.drawString(str, 5, 250); }
private Rectangle cacheVector(GlyphVector vector) { /* * Compute the exact area that the rendered string will take up in the image buffer. Note that * the string will actually be drawn at a positive (x,y) offset from (0,0) to leave enough room * for the ascent above the baseline and to correct for a few glyphs that appear to have negative * horizontal bearing (e.g. U+0423 Cyrillic uppercase letter U on Windows 7). */ final Rectangle vectorBounds = vector.getPixelBounds(fontContext, 0, 0); /* Enlage the stringImage if it is too small to store the entire rendered string */ if (vectorBounds.width > stringImage.getWidth() || vectorBounds.height > stringImage.getHeight()) allocateStringImage(Math.max(vectorBounds.width, stringImage.getWidth()), Math.max(vectorBounds.height, stringImage.getHeight())); /* Erase the upper-left corner where the string will get drawn*/ stringGraphics.clearRect(0, 0, vectorBounds.width, vectorBounds.height); /* Draw string with opaque white color and baseline adjustment so the upper-left corner of the image is at (0,0) */ stringGraphics.drawGlyphVector(vector, -vectorBounds.x, -vectorBounds.y); return vectorBounds; }
/** * @param text the string to find the width of * @param logical whether to add the space the letters should occupy on the end * @return width of string. */ private int findWidth(String text,boolean logical) { char[] chars = text.toCharArray(); GlyphVector vector = font.layoutGlyphVector(GlyphPage.renderContext, chars, 0, chars.length, Font.LAYOUT_LEFT_TO_RIGHT); int width = 0; int extraX = 0; boolean startNewLine = false; for (int glyphIndex = 0, n = vector.getNumGlyphs(); glyphIndex < n; glyphIndex++) { int charIndex = vector.getGlyphCharIndex(glyphIndex); int codePoint = text.codePointAt(charIndex); Rectangle bounds = logical ? vector.getLogicalBounds().getBounds() : getGlyphBounds(vector, glyphIndex, codePoint); if (startNewLine && codePoint != '\n') extraX = -bounds.x; if (glyphIndex > 0) extraX += paddingLeft + paddingRight + paddingAdvanceX; width = Math.max(width, bounds.x + extraX + bounds.width); if (codePoint == '\n') startNewLine = true; } return width; }
public void drawGlyphVector(SunGraphics2D sg2d, GlyphVector g, float x, float y) { FontRenderContext frc = g.getFontRenderContext(); FontInfo info = sg2d.getGVFontInfo(g.getFont(), frc); switch (info.aaHint) { case SunHints.INTVAL_TEXT_ANTIALIAS_OFF: super.drawGlyphVector(sg2d, g, x, y); return; case SunHints.INTVAL_TEXT_ANTIALIAS_ON: SurfaceData.aaTextRenderer.drawGlyphVector(sg2d, g, x, y); return; case SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB: case SunHints.INTVAL_TEXT_ANTIALIAS_LCD_VRGB: SurfaceData.lcdTextRenderer.drawGlyphVector(sg2d, g, x, y); return; default: } }
public void setFromGlyphVector(FontInfo info, GlyphVector gv, float x, float y) { this.x = x; this.y = y; this.lcdRGBOrder = info.lcdRGBOrder; this.lcdSubPixPos = info.lcdSubPixPos; /* A GV may be rendered in different Graphics. It is possible it is * used for one case where LCD text is available, and another where * it is not. Pass in the "info". to ensure get a suitable one. */ StandardGlyphVector sgv = StandardGlyphVector.getStandardGV(gv, info); // call before ensureCapacity :- usePositions = sgv.needsPositions(info.devTx); len = sgv.getNumGlyphs(); ensureCapacity(len); strikelist = sgv.setupGlyphImages(images, usePositions ? positions : null, info.devTx); glyphindex = -1; }
public void drawGlyphVector(SunGraphics2D sg2d, GlyphVector gv, float x, float y) { FontRenderContext frc = gv.getFontRenderContext(); FontInfo info = sg2d.getGVFontInfo(gv.getFont(), frc); if (info.pixelHeight > OutlineTextRenderer.THRESHHOLD) { SurfaceData.outlineTextRenderer.drawGlyphVector(sg2d, gv, x, y); return; } if (sg2d.transformState >= SunGraphics2D.TRANSFORM_TRANSLATESCALE) { double origin[] = {x, y}; sg2d.transform.transform(origin, 0, origin, 0, 1); x = (float) origin[0]; y = (float) origin[1]; } else { x += sg2d.transX; // don't use the glyph info origin, already in gv. y += sg2d.transY; } GlyphList gl = GlyphList.getInstance(); gl.setFromGlyphVector(info, gv, x, y); drawGlyphList(sg2d, gl, info.aaHint); gl.dispose(); }
public void drawGlyphVector(GlyphVector gv, float x, float y) { if (gv == null) { throw new NullPointerException("GlyphVector is null"); } try { textpipe.drawGlyphVector(this, gv, x, y); } catch (InvalidPipeException e) { try { revalidateAll(); textpipe.drawGlyphVector(this, gv, x, y); } catch (InvalidPipeException e2) { // Still catching the exception; we are not yet ready to // validate the surfaceData correctly. Fail for now and // try again next time around. } } finally { surfaceData.markDirty(); } }
/** * 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; } }
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; }
public void drawGlyphVector(SunGraphics2D sg2d, GlyphVector g, float x, float y) { FontRenderContext frc = g.getFontRenderContext(); FontInfo info = sg2d.getGVFontInfo(g.getFont(), frc); switch (info.aaHint) { case SunHints.INTVAL_TEXT_ANTIALIAS_OFF: super.drawGlyphVector(sg2d, g, x, y); return; case SunHints.INTVAL_TEXT_ANTIALIAS_ON: sg2d.surfaceData.aaTextRenderer.drawGlyphVector(sg2d, g, x, y); return; case SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB: case SunHints.INTVAL_TEXT_ANTIALIAS_LCD_VRGB: sg2d.surfaceData.lcdTextRenderer.drawGlyphVector(sg2d, g, x, y); return; default: } }
public void runTest(Object ctx, int numReps) { GVContext gvctx = (GVContext)ctx; GlyphVector gv = gvctx.gv; GlyphMetrics gm; do { for (int i = 0, e = gv.getNumGlyphs(); i < e; ++i) { gm = gv.getGlyphMetrics(i); } } while (--numReps >= 0); }
private void myDrawChars(Graphics g, /* OLD char buf[], */ Line l, int start, int howmany, int xoff, int baseline) { if (xferBuf.length < l.length()) { xferBuf = new char[l.length()]; } // OLD final char buf[] = l.XcharArray(); l.getChars(xferBuf); // Use rendering hints (antialiasing etc.) Map<?,?> hints = renderingHints; if ((hints != null) && (g instanceof Graphics2D)) { ((Graphics2D) g).setRenderingHints(hints); } if (metrics.isMultiCell()) { // slow way // This looks expensive but it is in fact a whole lot faster // than issuing a g.drawChars() _per_ character Graphics2D g2 = (Graphics2D) g; FontRenderContext frc = g2.getFontRenderContext(); // Gaaah, why doesn't createGlyphVector() take a (char[],offset,len) // triple? char[] tmp = new char[howmany]; System.arraycopy(xferBuf, start, tmp, 0, howmany); GlyphVector gv = getFont().createGlyphVector(frc, tmp); massage_glyphs(gv, start, howmany, l); g2.drawGlyphVector(gv, xoff, baseline); } else { // fast way g.drawChars(xferBuf, start, howmany, xoff, baseline); } }
private final GlyphVector getGV() { if (gv == null) { gv = createGV(); } return gv; }
/** * Returns the logical bounds of the specified array of characters * in the specified <code>FontRenderContext</code>. The logical * bounds contains the origin, ascent, advance, and height, which * includes the leading. The logical bounds does not always enclose * all the text. For example, in some languages and in some fonts, * accent marks can be positioned above the ascent or below the * descent. To obtain a visual bounding box, which encloses all the * text, use the {@link TextLayout#getBounds() getBounds} method of * <code>TextLayout</code>. * <p>Note: The returned bounds is in baseline-relative coordinates * (see {@link java.awt.Font class notes}). * @param chars an array of characters * @param beginIndex the initial offset in the array of * characters * @param limit the end offset in the array of characters * @param frc the specified <code>FontRenderContext</code> * @return a <code>Rectangle2D</code> that is the bounding box of the * specified array of characters in the specified * <code>FontRenderContext</code>. * @throws IndexOutOfBoundsException if <code>beginIndex</code> is * less than zero, or <code>limit</code> is greater than the * length of <code>chars</code>, or <code>beginIndex</code> * is greater than <code>limit</code>. * @see FontRenderContext * @see Font#createGlyphVector * @since 1.2 */ public Rectangle2D getStringBounds(char [] chars, int beginIndex, int limit, FontRenderContext frc) { if (beginIndex < 0) { throw new IndexOutOfBoundsException("beginIndex: " + beginIndex); } if (limit > chars.length) { throw new IndexOutOfBoundsException("limit: " + limit); } if (beginIndex > limit) { throw new IndexOutOfBoundsException("range length: " + (limit - beginIndex)); } // this code should be in textlayout // quick check for simple text, assume GV ok to use if simple boolean simple = values == null || (values.getKerning() == 0 && values.getLigatures() == 0 && values.getBaselineTransform() == null); if (simple) { simple = ! FontUtilities.isComplexText(chars, beginIndex, limit); } if (simple) { GlyphVector gv = new StandardGlyphVector(this, chars, beginIndex, limit - beginIndex, frc); return gv.getLogicalBounds(); } else { // need char array constructor on textlayout String str = new String(chars, beginIndex, limit - beginIndex); TextLayout tl = new TextLayout(str, this, frc); return new Rectangle2D.Float(0, -tl.getAscent(), tl.getAdvance(), tl.getAscent() + tl.getDescent() + tl.getLeading()); } }
/** * @see org.newdawn.slick.Font#getHeight(java.lang.String) */ public int getHeight (String text) { if (text == null) throw new IllegalArgumentException("text cannot be null."); if (text.length() == 0) return 0; if (displayListCaching) { DisplayList displayList = (DisplayList)displayLists.get(text); if (displayList != null) return displayList.height; } char[] chars = text.toCharArray(); GlyphVector vector = font.layoutGlyphVector(GlyphPage.renderContext, chars, 0, chars.length, Font.LAYOUT_LEFT_TO_RIGHT); int lines = 0, height = 0; for (int i = 0, n = vector.getNumGlyphs(); i < n; i++) { int charIndex = vector.getGlyphCharIndex(i); int codePoint = text.codePointAt(charIndex); if (codePoint == ' ') continue; Rectangle bounds = getGlyphBounds(vector, i, codePoint); height = Math.max(height, ascent + bounds.y + bounds.height); if (codePoint == '\n') { lines++; height = 0; } } return lines * getLineHeight() + height; }
private int[] getGlyphMetrics (Font font, int codePoint) { // xOffset and xAdvance will be incorrect for unicode characters such as combining marks or non-spacing characters // (eg Pnujabi's "\u0A1C\u0A47") that require the context of surrounding glyphs to determine spacing, but thisis the // best we can do with the BMFont format. char[] chars = Character.toChars(codePoint); GlyphVector vector = font.layoutGlyphVector(GlyphPage.renderContext, chars, 0, chars.length, Font.LAYOUT_LEFT_TO_RIGHT); GlyphMetrics metrics = vector.getGlyphMetrics(0); int xOffset = vector.getGlyphPixelBounds(0, null, 0, 0).x - unicodeFont.getPaddingLeft(); int xAdvance = (int)(metrics.getAdvanceX() + unicodeFont.getPaddingAdvanceX() + unicodeFont.getPaddingLeft() + unicodeFont .getPaddingRight()); return new int[] {xOffset, xAdvance}; }
protected BufferedImage createImage(Color bgColor) { BufferedImage bufferedImage = new BufferedImage(END_X, END_Y, BufferedImage.TYPE_INT_RGB); // create graphics and graphics2d final Graphics graphics = bufferedImage.getGraphics(); final Graphics2D g2d = (Graphics2D) graphics; // set the background color g2d.setBackground(bgColor == null ? Color.gray : bgColor); g2d.clearRect(START_X, START_Y, END_X, END_Y); // create a pattern for the background createPattern(g2d); // set the fonts and font rendering hints Font font = new Font("Helvetica", Font.ITALIC, 30); g2d.setFont(font); FontRenderContext frc = g2d.getFontRenderContext(); g2d.translate(10, 24); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setStroke(new BasicStroke(3)); // sets the foreground color g2d.setPaint(Color.DARK_GRAY); GlyphVector gv = font.createGlyphVector(frc, message); int numGlyphs = gv.getNumGlyphs(); for (int ii = 0; ii < numGlyphs; ii++) { AffineTransform at; Point2D p = gv.getGlyphPosition(ii); at = AffineTransform.getTranslateInstance(p.getX(), p.getY()); at.rotate(Math.PI / 8); Shape shape = gv.getGlyphOutline(ii); Shape sss = at.createTransformedShape(shape); g2d.fill(sss); } return blurImage(bufferedImage); }
/** Draws the given string at (x,y) */ public void drawString(String text, int x, int y) { if (text.length() == 0) return; if (gr != null) { gr.drawString(text, x, y); return; } calc(); Font font = (fontBoldness ? cachedBoldFont : cachedPlainFont); GlyphVector gv = font.createGlyphVector(new FontRenderContext(null, false, false), text); translate(x, y); draw(gv.getOutline(), true); translate(-x, -y); }
public static void main(String[] args) { Font font = new Font(Font.MONOSPACED, Font.PLAIN, 12); FontRenderContext frc = new FontRenderContext(null, false, false); GlyphVector gv = font.layoutGlyphVector(frc, "abc".toCharArray(), 1, 3, Font.LAYOUT_LEFT_TO_RIGHT); int idx0 = gv.getGlyphCharIndex(0); if (idx0 != 0) { throw new RuntimeException("Expected 0, got " + idx0); } }
public void runTest(Object ctx, int numReps) { GVContext gvctx = (GVContext)ctx; Graphics2D g2d = gvctx.g2d; GlyphVector gv = gvctx.gv; do { g2d.drawGlyphVector(gv, 40, 40); } while (--numReps >= 0); }
public void runTest(Object ctx, int numReps) { GVContext gvctx = (GVContext)ctx; GlyphVector gv = gvctx.gv; Rectangle2D r; do { r = gv.getVisualBounds(); } while (--numReps >= 0); }
public void runTest(Object ctx, int numReps) { GVContext gvctx = (GVContext)ctx; GlyphVector gv = gvctx.gv; Rectangle2D r; do { r = gv.getLogicalBounds(); } while (--numReps >= 0); }
public void runTest(Object ctx, int numReps) { GVContext gvctx = (GVContext)ctx; GlyphVector gv = gvctx.gv; AffineTransform tx; do { for (int i = 0, e = gv.getNumGlyphs(); i < e; ++i) { tx = gv.getGlyphTransform(i); } } while (--numReps >= 0); }
/** * Returns the logical bounds of the specified array of characters * in the specified {@code FontRenderContext}. The logical * bounds contains the origin, ascent, advance, and height, which * includes the leading. The logical bounds does not always enclose * all the text. For example, in some languages and in some fonts, * accent marks can be positioned above the ascent or below the * descent. To obtain a visual bounding box, which encloses all the * text, use the {@link TextLayout#getBounds() getBounds} method of * {@code TextLayout}. * <p>Note: The returned bounds is in baseline-relative coordinates * (see {@link java.awt.Font class notes}). * @param chars an array of characters * @param beginIndex the initial offset in the array of * characters * @param limit the end offset in the array of characters * @param frc the specified {@code FontRenderContext} * @return a {@code Rectangle2D} that is the bounding box of the * specified array of characters in the specified * {@code FontRenderContext}. * @throws IndexOutOfBoundsException if {@code beginIndex} is * less than zero, or {@code limit} is greater than the * length of {@code chars}, or {@code beginIndex} * is greater than {@code limit}. * @see FontRenderContext * @see Font#createGlyphVector * @since 1.2 */ public Rectangle2D getStringBounds(char [] chars, int beginIndex, int limit, FontRenderContext frc) { if (beginIndex < 0) { throw new IndexOutOfBoundsException("beginIndex: " + beginIndex); } if (limit > chars.length) { throw new IndexOutOfBoundsException("limit: " + limit); } if (beginIndex > limit) { throw new IndexOutOfBoundsException("range length: " + (limit - beginIndex)); } // this code should be in textlayout // quick check for simple text, assume GV ok to use if simple boolean simple = values == null || (values.getKerning() == 0 && values.getLigatures() == 0 && values.getBaselineTransform() == null); if (simple) { simple = ! FontUtilities.isComplexText(chars, beginIndex, limit); } if (simple) { GlyphVector gv = new StandardGlyphVector(this, chars, beginIndex, limit - beginIndex, frc); return gv.getLogicalBounds(); } else { // need char array constructor on textlayout String str = new String(chars, beginIndex, limit - beginIndex); TextLayout tl = new TextLayout(str, this, frc); return new Rectangle2D.Float(0, -tl.getAscent(), tl.getAdvance(), tl.getAscent() + tl.getDescent() + tl.getLeading()); } }
public void runTest(Object ctx, int numReps) { TCContext tcctx = (TCContext)ctx; final Font font = tcctx.font; final CharacterIterator ci = tcctx.ci; final FontRenderContext frc = tcctx.frc; GlyphVector gv; do { gv = font.createGlyphVector(frc, ci); } while (--numReps >= 0); }
public void runTest(Object ctx, int numReps) { TCContext tcctx = (TCContext)ctx; final Font font = tcctx.font; final char[] chars = tcctx.chars1; final int start = 1; final int limit = chars.length - 1; final FontRenderContext frc = tcctx.frc; final int flags = tcctx.flags; GlyphVector gv; do { gv = font.layoutGlyphVector(frc, chars, start, limit, flags); } while (--numReps >= 0); }