| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
* Awful (and by that, I mean awesome) ASCII (non-)art for how this works: * *
* | * | height is the full used height of the image * | * | ..XX.. } * | ..XX.. } * | ...... } * | XXXX.. } topExtent (top y is baseline - topExtent) * | ..XX.. } * | ..XX.. } dotted areas are where the image data * | ..XX.. } is actually located for the character * +---XXXXXX---- } (it extends to the right and down * | for power of two texture sizes) * ^^^^ leftExtent (amount to move over before drawing the image * * ^^^^^^^^^^^^^^ setWidth (width displaced by char) ** * @webref typography * @webBrief Grayscale bitmap font class used by Processing * @see PApplet#loadFont(String) * @see PApplet#createFont(String, float, boolean, char[]) * @see PGraphics#textFont(PFont) */ public class PFont implements PConstants { /** Number of character glyphs in this font. */ protected int glyphCount; /** * Actual glyph data. The length of this array won't necessarily be the * same size as glyphCount, in cases where lazy font loading is in use. */ protected Glyph[] glyphs; /** * Name of the font as seen by Java when it was created. * If the font is available, the native version will be used. */ protected String name; /** * Postscript name of the font that this bitmap was created from. */ protected String psname; /** * The original size of the font when it was first created */ protected int size; /** Default density set to 1 for backwards compatibility with loadFont(). */ protected int density = 1; /** true if smoothing was enabled for this font, used for native impl */ protected boolean smooth; /** * The ascent of the font. If the 'd' character is present in this PFont, * this value is replaced with its pixel height, because the values returned * by FontMetrics.getAscent() seem to be terrible. */ protected int ascent; /** * The descent of the font. If the 'p' character is present in this PFont, * this value is replaced with its lowest pixel height, because the values * returned by FontMetrics.getDescent() are gross. */ protected int descent; /** * A more efficient array lookup for straight ASCII characters. For Unicode * characters, a QuickSort-style search is used. */ protected int[] ascii; /** * True if this font is set to load dynamically. This is the default when * createFont() method is called without a character set. Bitmap versions of * characters are only created when prompted by an index() call. */ protected boolean lazy; /** * Native Java version of the font. If possible, this allows the * PGraphics subclass to just use Java's font rendering stuff * in situations where that's faster. */ protected Font font; /** * True if this font was loaded from an InputStream, rather than by name * from the OS. It's best to use the native version of a font loaded from * a TTF file, since that will ensure that the font is available when the * sketch is exported. */ protected boolean stream; /** * True if this font should return 'null' for getFont(), so that the native * font will be used to create a subset, but the native version of the font * will not be used. */ protected boolean subsetting; /** True if already tried to find the native AWT version of this font. */ protected boolean fontSearched; /** * The name of the font that Java uses when a font isn't found. * See {@link #findFont(String)} and {@link #loadFonts()} for more info. */ static protected String systemFontName; /** * Array of the native system fonts. Used to lookup native fonts by their * PostScript name. This is a workaround for a several year old Apple Java * bug that they can't be bothered to fix. */ static protected Font[] fonts; static protected HashMap fontDifferent; protected BufferedImage lazyImage; protected Graphics2D lazyGraphics; protected FontMetrics lazyMetrics; protected int[] lazySamples; // Debugging for https://github.com/processing/processing4/issues/278 private final boolean DEBUG_P4_0278 = false; /** * @nowebref */ public PFont() { } // for subclasses /** * ( begin auto-generated from PFont.xml ) * * PFont is the font class for Processing. To create a font to use with * Processing, select "Create Font..." from the Tools menu. This will * create a font in the format Processing requires and also adds it to the * current sketch's data directory. Processing displays fonts using the * .vlw font format, which uses images for each letter, rather than * defining them through vector data. The loadFont() function * constructs a new font and textFont() makes a font active. The * list() method creates a list of the fonts installed on the * computer, which is useful information to use with the * createFont() function for dynamically converting fonts into a * format to use with Processing. * * ( end auto-generated ) * * @nowebref * @param font font the font object to create from * @param smooth smooth true to enable smoothing/anti-aliasing */ public PFont(Font font, boolean smooth) { this(font, smooth, null); } /** * Create a new image-based font on the fly. If charset is set to null, * the characters will only be created as bitmaps when they're drawn. * * @nowebref * @param charset array of all unicode chars that should be included */ public PFont(Font font, boolean smooth, char[] charset) { // save this so that we can use the native version this.font = font; this.smooth = smooth; name = font.getName(); psname = font.getPSName(); size = font.getSize(); // no, i'm not interested in getting off the couch //lazy = true; // not sure what else to do here //mbox2 = 0; int initialCount = 10; glyphs = new Glyph[initialCount]; ascii = new int[128]; Arrays.fill(ascii, -1); int mbox3 = size * 3; lazyImage = new BufferedImage(mbox3, mbox3, BufferedImage.TYPE_INT_RGB); lazyGraphics = (Graphics2D) lazyImage.getGraphics(); lazyGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, smooth ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF); // adding this for post-1.0.9 lazyGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, smooth ? RenderingHints.VALUE_TEXT_ANTIALIAS_ON : RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); // Trying to track down https://github.com/processing/processing4/issues/278 // But at least on macOS, it's still smooth when this is called from Create Font. lazyGraphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, smooth ? RenderingHints.VALUE_INTERPOLATION_BICUBIC : RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); lazyGraphics.setFont(font); lazyMetrics = lazyGraphics.getFontMetrics(); lazySamples = new int[mbox3 * mbox3]; // These values are terrible/unusable. Verified again for Processing 1.1. // They vary widely per-platform and per-font, so instead we'll use the // calculate-by-hand method of measuring pixels in characters. //ascent = lazyMetrics.getAscent(); //descent = lazyMetrics.getDescent(); if (charset == null) { lazy = true; } else { // The charset needs to be sorted to make index lookup run quickly // https://download.processing.org/bugzilla/494.html // First make copy of charset[] so the user's array is not modified // https://github.com/processing/processing4/issues/197 char[] sortedCharset = Arrays.copyOf(charset, charset.length); Arrays.sort(sortedCharset); glyphs = new Glyph[sortedCharset.length]; glyphCount = 0; for (char c : sortedCharset) { if (font.canDisplay(c)) { Glyph glyf = new Glyph(c); if (glyf.value < 128) { ascii[glyf.value] = glyphCount; } glyf.index = glyphCount; glyphs[glyphCount++] = glyf; } } // shorten the array if necessary if (glyphCount != sortedCharset.length) { glyphs = (Glyph[]) PApplet.subset(glyphs, 0, glyphCount); } } // If not already created, just create these two characters to calculate // the ascent and descent values for the font. This was tested to only // require 5-10 ms on a 2.4 GHz MacBook Pro. // In versions 1.0.9 and earlier, fonts that could not display d or p // used the max up/down values as calculated by looking through the font. // That's no longer valid with the auto-generating fonts, so we'll just // use getAscent() and getDescent() in such (minor) cases. if (ascent == 0) { if (font.canDisplay('d')) { new Glyph('d'); } else { ascent = lazyMetrics.getAscent(); } } if (descent == 0) { if (font.canDisplay('p')) { new Glyph('p'); } else { descent = lazyMetrics.getDescent(); } } } /** * Adds a parameter that indicates the font came from a file, * not a built-in OS font. * * @nowebref */ public PFont(Font font, boolean smooth, char[] charset, boolean stream, int density) { this(font, smooth, charset); this.stream = stream; this.density = density; } /** * @nowebref * @param input InputStream */ public PFont(InputStream input) throws IOException { DataInputStream is = new DataInputStream(input); // number of character images stored in this font glyphCount = is.readInt(); // used to be the bitCount, but now used for version number. // version 8 is any font before 69, so 9 is anything from 83+ // 9 was buggy so gonna increment to 10. int version = is.readInt(); // this was formerly ignored, now it's the actual font size //mbox = is.readInt(); size = is.readInt(); // this was formerly mboxY, the one that was used // this will make new fonts downward compatible is.readInt(); // ignore the other mbox attribute ascent = is.readInt(); // formerly baseHt (zero/ignored) descent = is.readInt(); // formerly ignored struct padding // allocate enough space for the character info glyphs = new Glyph[glyphCount]; ascii = new int[128]; Arrays.fill(ascii, -1); // read the information about the individual characters for (int i = 0; i < glyphCount; i++) { Glyph glyph = new Glyph(is); // cache locations of the ascii charset if (glyph.value < 128) { ascii[glyph.value] = i; } glyph.index = i; glyphs[i] = glyph; } // not a roman font, so throw an error and ask to re-build. // that way can avoid a bunch of error checking hacks in here. if ((ascent == 0) && (descent == 0)) { throw new RuntimeException("Please use \"Create Font\" to " + "re-create this font."); } for (Glyph glyph : glyphs) { glyph.readBitmap(is); } if (version >= 10) { // includes the font name at the end of the file name = is.readUTF(); psname = is.readUTF(); } if (version == 11) { smooth = is.readBoolean(); } // See if there's a native version of this font that can be used, // in case that's of interest later. // findNative(); } /** * Write this PFont to an OutputStream. *
* This is used by the Create Font tool, or whatever anyone else dreams * up for messing with fonts themselves. *
* It is assumed that the calling class will handle closing * the stream when finished. */ public void save(OutputStream output) throws IOException { DataOutputStream os = new DataOutputStream(output); os.writeInt(glyphCount); if ((name == null) || (psname == null)) { name = ""; psname = ""; } os.writeInt(11); // formerly numBits, now used for version number os.writeInt(size); // formerly mboxX (was 64, now 48) os.writeInt(0); // formerly mboxY, now ignored os.writeInt(ascent); // formerly baseHt (was ignored) os.writeInt(descent); // formerly struct padding for c version for (int i = 0; i < glyphCount; i++) { glyphs[i].writeHeader(os); } for (int i = 0; i < glyphCount; i++) { glyphs[i].writeBitmap(os); } // version 11 os.writeUTF(name); os.writeUTF(psname); os.writeBoolean(smooth); os.flush(); } /** * Create a new glyph, and add the character to the current font. * @param c character to create an image for. */ protected void addGlyph(char c) { Glyph glyph = new Glyph(c); if (glyphCount == glyphs.length) { glyphs = (Glyph[]) PApplet.expand(glyphs); } if (glyphCount == 0) { glyph.index = 0; glyphs[glyphCount] = glyph; if (glyph.value < 128) { ascii[glyph.value] = 0; } } else if (glyphs[glyphCount-1].value < glyph.value) { glyphs[glyphCount] = glyph; if (glyph.value < 128) { ascii[glyph.value] = glyphCount; } } else { for (int i = 0; i < glyphCount; i++) { if (glyphs[i].value > c) { for (int j = glyphCount; j > i; --j) { glyphs[j] = glyphs[j-1]; if (glyphs[j].value < 128) { ascii[glyphs[j].value] = j; } } glyph.index = i; glyphs[i] = glyph; // cache locations of the ascii charset if (c < 128) ascii[c] = i; break; } } } glyphCount++; } public String getName() { return name; } public String getPostScriptName() { return psname; } /** * Set the native complement of this font. Might be set internally via the * findFont() function, or externally by a deriveFont() call if the font * is resized by PGraphicsJava2D. */ public void setNative(Object font) { this.font = (Font) font; } /** * Use the getNative() method instead, which allows library interfaces to be * written in a cross-platform fashion for desktop, Android, and others. */ @Deprecated public Font getFont() { return font; } /** * Return the native java.awt.Font associated with this PFont (if any). */ public Object getNative() { if (subsetting) { return null; // don't return the font for use } return font; } /** * Return size of this font. */ public int getSize() { return size; } // public void setDefaultSize(int size) { // defaultSize = size; // } /** * Returns the size that will be used when textFont(font) is called. * When drawing with 2x pixel density, bitmap fonts in OpenGL need to be * created (behind the scenes) at double the requested size. This ensures * that they're shown at half on displays (so folks don't have to change * their sketch code). */ public int getDefaultSize() { //return defaultSize; return size / density; } public boolean isSmooth() { return smooth; } public boolean isStream() { return stream; } public void setSubsetting() { subsetting = true; } /** * Attempt to find the native version of this font. * (Public so that it can be used by OpenGL or other renderers.) */ public Object findNative() { if (font == null) { if (!fontSearched) { // this font may or may not be installed font = new Font(name, Font.PLAIN, size); // if the ps name matches, then we're in fine shape if (!font.getPSName().equals(psname)) { // on osx java 1.4 (not 1.3.. ugh), you can specify the ps name // of the font, so try that in case this .vlw font was created on pc // and the name is different, but the ps name is found on the // java 1.4 mac that's currently running this sketch. font = new Font(psname, Font.PLAIN, size); } // check again, and if still bad, screw em if (!font.getPSName().equals(psname)) { font = null; } fontSearched = true; } } return font; } public Glyph getGlyph(char c) { int index = index(c); return (index == -1) ? null : glyphs[index]; } /** * Get index for the character. * @return index into arrays or -1 if not found */ protected int index(char c) { if (lazy) { int index = indexActual(c); if (index != -1) { return index; } if (font != null && font.canDisplay(c)) { // create the glyph addGlyph(c); // now where did i put that? return indexActual(c); } else { return -1; } } else { return indexActual(c); } } protected int indexActual(char c) { // degenerate case, but the find function will have trouble // if there are somehow zero chars in the lookup //if (value.length == 0) return -1; if (glyphCount == 0) return -1; // quicker lookup for the ascii fellers if (c < 128) return ascii[c]; // some other unicode char, hunt it out //return index_hunt(c, 0, value.length-1); return indexHunt(c, 0, glyphCount-1); } protected int indexHunt(int c, int start, int stop) { int pivot = (start + stop) / 2; // if this is the char, then return it if (c == glyphs[pivot].value) return pivot; // char doesn't exist, otherwise would have been the pivot //if (start == stop) return -1; if (start >= stop) return -1; // if it's in the lower half, continue searching that if (c < glyphs[pivot].value) return indexHunt(c, start, pivot-1); // if it's in the upper half, continue there return indexHunt(c, pivot+1, stop); } /** * Currently un-implemented for .vlw fonts, * but honored for layout in case subclasses use it. */ public float kern(char a, char b) { return 0; } /** * Returns the ascent of this font from the baseline. * The value is based on a font of size 1. */ public float ascent() { return ((float) ascent / (float) size); } /** * Returns how far this font descends from the baseline. * The value is based on a font size of 1. */ public float descent() { return ((float) descent / (float) size); } /** * Width of this character for a font of size 1. */ public float width(char c) { if (c == 32) return width('i'); int cc = index(c); if (cc == -1) return 0; return ((float) glyphs[cc].setWidth / (float) size); } ////////////////////////////////////////////////////////////// public int getGlyphCount() { return glyphCount; } public Glyph getGlyph(int i) { return glyphs[i]; } public PShape getShape(char ch) { return getShape(ch, 0); } public PShape getShape(char ch, float detail) { Font font = (Font) getNative(); if (font == null) { throw new IllegalArgumentException("getShape() only works on fonts loaded with createFont()"); } PShape s = new PShape(PShape.PATH); // six element array received from the Java2D path iterator float[] iterPoints = new float[6]; // array passed to createGlyphVector char[] textArray = { ch }; //Graphics2D graphics = (Graphics2D) this.getGraphics(); //FontRenderContext frc = graphics.getFontRenderContext(); @SuppressWarnings("deprecation") FontRenderContext frc = Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext(); GlyphVector gv = font.createGlyphVector(frc, textArray); Shape shp = gv.getOutline(); // make everything into moveto and lineto PathIterator iter = (detail == 0) ? shp.getPathIterator(null) : // maintain curves shp.getPathIterator(null, detail); // convert to line segments int contours = 0; s.beginShape(); s.noStroke(); s.fill(0); while (!iter.isDone()) { int type = iter.currentSegment(iterPoints); switch (type) { case PathIterator.SEG_MOVETO: // 1 point (2 vars) in textPoints if (contours > 0) { s.beginContour(); } ++contours; s.vertex(iterPoints[0], iterPoints[1]); break; case PathIterator.SEG_LINETO: // 1 point s.vertex(iterPoints[0], iterPoints[1]); break; case PathIterator.SEG_QUADTO: // 2 points s.quadraticVertex(iterPoints[0], iterPoints[1], iterPoints[2], iterPoints[3]); break; case PathIterator.SEG_CUBICTO: // 3 points s.bezierVertex(iterPoints[0], iterPoints[1], iterPoints[2], iterPoints[3], iterPoints[4], iterPoints[5]); break; case PathIterator.SEG_CLOSE: if (contours > 1) { s.endContour(); } break; } iter.next(); } s.endShape(CLOSE); return s; } ////////////////////////////////////////////////////////////// static final char[] EXTRA_CHARS = { 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00BA, 0x00BB, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FF, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x010C, 0x010D, 0x010E, 0x010F, 0x0110, 0x0111, 0x0118, 0x0119, 0x011A, 0x011B, 0x0131, 0x0139, 0x013A, 0x013D, 0x013E, 0x0141, 0x0142, 0x0143, 0x0144, 0x0147, 0x0148, 0x0150, 0x0151, 0x0152, 0x0153, 0x0154, 0x0155, 0x0158, 0x0159, 0x015A, 0x015B, 0x015E, 0x015F, 0x0160, 0x0161, 0x0162, 0x0163, 0x0164, 0x0165, 0x016E, 0x016F, 0x0170, 0x0171, 0x0178, 0x0179, 0x017A, 0x017B, 0x017C, 0x017D, 0x017E, 0x0192, 0x02C6, 0x02C7, 0x02D8, 0x02D9, 0x02DA, 0x02DB, 0x02DC, 0x02DD, 0x03A9, 0x03C0, 0x2013, 0x2014, 0x2018, 0x2019, 0x201A, 0x201C, 0x201D, 0x201E, 0x2020, 0x2021, 0x2022, 0x2026, 0x2030, 0x2039, 0x203A, 0x2044, 0x20AC, 0x2122, 0x2202, 0x2206, 0x220F, 0x2211, 0x221A, 0x221E, 0x222B, 0x2248, 0x2260, 0x2264, 0x2265, 0x25CA, 0xF8FF, 0xFB01, 0xFB02 }; /** * The default Processing character set. *
* This is the union of the Mac Roman and Windows ANSI (CP1250) * character sets. ISO 8859-1 Latin 1 is Unicode characters 0x80 -> 0xFF, * and would seem a good standard, but in practice, most P5 users would * rather have characters that they expect from their platform's fonts. *
* This is more of an interim solution until a much better * font solution can be determined. (i.e. create fonts on * the fly from some sort of vector format). *
* Not that I expect that to happen. */ static public char[] CHARSET; static { CHARSET = new char[126-33+1 + EXTRA_CHARS.length]; int index = 0; for (int i = 33; i
| Back | FazBrowse Home | New Git URL |