void Font::drawText(Bitmap *dest, Point2i pos, const std::string &text) const { int initial = pos.x; for (size_t i=0; i<text.length(); i++) { char character = text[i]; if (character == '\r') continue; if (character == '\n') { pos.x = initial; pos.y += (int) (getMaxVerticalBearing()*4.0/3.0); continue; } const Font::Glyph &glyph = getGlyph(character); Point2i targetOffset = pos + Vector2i( glyph.horizontalBearing, getMaxVerticalBearing() - glyph.verticalBearing - 1 ); Point2i sourceOffset( (int) (glyph.tx.x * m_bitmap->getWidth()), (int) (glyph.tx.y * m_bitmap->getHeight())); dest->accumulate(m_bitmap.get(), sourceOffset, targetOffset, glyph.size); pos.x += glyph.horizontalAdvance; if (i+1 < text.length()) pos.x += getKerning(character, text[i+1]); } }
float ofxTextAlign::getWidth(const char *str, bool single_line) { float ret = 0; float tmp = 0; const char *ptr = str; char prev = -1; while(*ptr != '\0' && !(single_line && *ptr == '\n')) { switch(*ptr) { case '\n': ret = max(ret, tmp); tmp = 0; prev = -1; break; default: if(prev != -1) { tmp += getKerning(*ptr, prev); } prev = *ptr; tmp += getAdvance(*ptr); break; } ++ptr; } return max(ret, tmp); }
Vector2i Font::getSize(const std::string &text) const { Vector2i size(0, getMaxVerticalBearing()); int pos = 0; for (size_t i=0; i<text.length(); i++) { char character = text[i]; if (character == '\r') continue; if (character == '\n') { size.y += (int) (getMaxVerticalBearing()*(4.0 / 3.0)); size.x = std::max(size.x, pos); pos = 0; continue; } const Font::Glyph &glyph = getGlyph(character); pos += glyph.horizontalAdvance; if (i+1 < text.length()) pos += getKerning(character, text[i+1]); } size.x = std::max(size.x, pos); return size; }
//----------------------------------------------------------- ofTexture ofTrueTypeFont::getStringTexture(const std::string& str, bool vflip) const{ string str_valid; utf8::replace_invalid(str.begin(),str.end(),back_inserter(str_valid)); vector<glyph> glyphs; vector<ofVec2f> glyphPositions; utf8::iterator<const char*> it(&str_valid.front(), &str_valid.front(), (&str_valid.back())+1); utf8::iterator<const char*> end((&str_valid.back())+1, &str_valid.front(), (&str_valid.back())+1); int x = 0; int y = 0; long height = 0; uint32_t prevC = 0; while(it != end){ try{ auto c = *it; if (c == '\n') { y += lineHeight; x = 0 ; //reset X Pos back to zero prevC = 0; } else { glyphs.push_back(loadGlyph(c)); if(prevC>0){ x += getKerning(c,prevC); }else if(settings.direction == ofTtfSettings::RightToLeft){ x += glyphs.back().props.width; } glyphPositions.emplace_back(static_cast<float>(x), static_cast<float>(y)); x += glyphs.back().props.advance * letterSpacing; height = max(height, glyphs.back().props.ymax + y + long(getLineHeight())); } ++it; prevC = c; }catch(...){ break; } } ofTexture tex; ofPixels totalPixels; totalPixels.allocate(x, height, OF_PIXELS_GRAY_ALPHA); //-------------------------------- clear data: totalPixels.set(0,255); // every luminance pixel = 255 totalPixels.set(1,0); size_t i = 0; for(auto & g: glyphs){ if(settings.direction == ofTtfSettings::LeftToRight){ g.pixels.blendInto(totalPixels, glyphPositions[i].x, glyphPositions[i].y + getLineHeight() + g.props.ymin + getDescenderHeight() ); }else{ g.pixels.blendInto(totalPixels, x-glyphPositions[i].x, glyphPositions[i].y + getLineHeight() + g.props.ymin + getDescenderHeight() ); } i++; if(i==glyphPositions.size()){ break; } } tex.allocate(totalPixels); return tex; }
void Font::writeInternal(float x, float y, float z, const char *text, int count, float spacing) { for (int n = 0; n < count;) { int glyph = getTextChar(text, n, &n); x += renderGlyph(x, y, z, glyph); if (glyph == ' ') x += spacing; if (n < count) x += getKerning(glyph, getTextChar(text, n)); } }
void FontDescription::updateTypesettingFeatures() { m_fields.m_typesettingFeatures = s_defaultTypesettingFeatures; switch (textRendering()) { case AutoTextRendering: break; case OptimizeSpeed: m_fields.m_typesettingFeatures &= ~(blink::Kerning | Ligatures); break; case GeometricPrecision: case OptimizeLegibility: m_fields.m_typesettingFeatures |= blink::Kerning | Ligatures; break; } switch (getKerning()) { case FontDescription::NoneKerning: m_fields.m_typesettingFeatures &= ~blink::Kerning; break; case FontDescription::NormalKerning: m_fields.m_typesettingFeatures |= blink::Kerning; break; case FontDescription::AutoKerning: break; } // As per CSS (http://dev.w3.org/csswg/css-text-3/#letter-spacing-property), // When the effective letter-spacing between two characters is not zero (due // to either justification or non-zero computed letter-spacing), user agents // should not apply optional ligatures. if (m_letterSpacing == 0) { switch (commonLigaturesState()) { case FontDescription::DisabledLigaturesState: m_fields.m_typesettingFeatures &= ~blink::Ligatures; break; case FontDescription::EnabledLigaturesState: m_fields.m_typesettingFeatures |= blink::Ligatures; break; case FontDescription::NormalLigaturesState: break; } if (discretionaryLigaturesState() == FontDescription::EnabledLigaturesState || historicalLigaturesState() == FontDescription::EnabledLigaturesState || contextualLigaturesState() == FontDescription::EnabledLigaturesState) { m_fields.m_typesettingFeatures |= blink::Ligatures; } } if (variantCaps() != CapsNormal) m_fields.m_typesettingFeatures |= blink::Caps; }
float StringFontWidth( const char *str ) { float len = 0; float kerning = 0; while ( *str ) { int i = *str; if ( i >= ' ' && i < 128 ) { len += glyphRects[i-32].xadvance + kerning; kerning = getKerning( str ); } str++; } return len; }
/* ================== iphoneDrawText Returns the width in pixels ================== */ float iphoneDrawText( float x, float y, float scale, const char *str ) { float fx = x; float fy = y; float kerning = 0; PK_BindTexture( arialFontTexture ); glBegin( GL_QUADS ); while ( *str ) { int i = *str; if ( i >= ' ' && i < 128 ) { GlyphRect *glyph = &glyphRects[i-32]; // the glyphRects don't include the shadow outline float x0 = ( glyph->x0 - 1 ) / 256.0; float y0 = ( glyph->y0 - 1 ) / 256.0; float x1 = ( glyph->x1 + 2 ) / 256.0; float y1 = ( glyph->y1 + 2 ) / 256.0; float width = ( x1 - x0 ) * 256 * scale; float height = ( y1 - y0 ) * 256 * scale; float xoff = ( glyph->xoff - 1 ) * scale + kerning; float yoff = ( glyph->yoff - 1 ) * scale; glTexCoord2f( x0, y0 ); glVertex2f( fx + xoff, fy + yoff ); glTexCoord2f( x1, y0 ); glVertex2f( fx + xoff + width, fy + yoff ); glTexCoord2f( x1, y1 ); glVertex2f( fx + xoff + width, fy + yoff + height ); glTexCoord2f( x0, y1 ); glVertex2f( fx + xoff, fy + yoff + height ); fx += glyph->xadvance * scale + kerning; // fx += ceil(glyph->xadvance); // with the outline, ceil is probably the right thing kerning = getKerning( str ); } str++; } glEnd(); return fx - x; }
const char* ofxTextAlign::drawLine(const char *str, float x, float y) { const char *ptr = str; int letter_count = getLetterCount(str, true); float extra_spacing = letter_count>1?(getDrawWidth(str, true)-getWidth(str, true))/(float)(letter_count-1):0; while(*ptr != '\0') { if(*ptr=='\n') { ++ptr; break; } float interval = getAdvance(*ptr) + getKerning(*(ptr+1), *ptr) + extra_spacing; ptr = drawChar(ptr, x, y); x += interval; } return ptr; }
float getStringWidth (const String& text) { const CharPointer_UTF16 utf16 (text.toUTF16()); const size_t numChars = utf16.length(); HeapBlock<int16> results (numChars + 1); results[numChars] = -1; float x = 0; if (GetGlyphIndices (dc, utf16, (int) numChars, reinterpret_cast <WORD*> (results.getData()), GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR) { for (size_t i = 0; i < numChars; ++i) x += getKerning (dc, results[i], results[i + 1]); } return x; }
void ofTrueTypeFont::iterateString(const string & str, float x, float y, bool vFlipped, std::function<void(uint32_t, ofVec2f)> f) const{ ofVec2f pos(x,y); int newLineDirection = 1; if(!vFlipped){ // this would align multiline texts to the last line when vflip is disabled //int lines = ofStringTimesInString(c,"\n"); //Y = lines*lineHeight; newLineDirection = -1; } int directionX = settings.direction == ofTtfSettings::LeftToRight?1:-1; string str_valid; utf8::replace_invalid(str.begin(),str.end(),back_inserter(str_valid)); utf8::iterator<const char*> it(&str_valid.front(), &str_valid.front(), (&str_valid.back())+1); utf8::iterator<const char*> end((&str_valid.back())+1, &str_valid.front(), (&str_valid.back())+1); uint32_t prevC = 0; while(it != end){ try{ auto c = *it; if (c == '\n') { pos.y += lineHeight*newLineDirection; pos.x = x ; //reset X Pos back to zero prevC = 0; }if (c == '\t') { pos.x += getGlyphProperties(' ').advance * letterSpacing * 4 * directionX; prevC = c; } else if(isValidGlyph(c)) { const auto & props = getGlyphProperties(c); if(prevC>0){ pos.x += getKerning(c,prevC);// * directionX; } f(c,pos); pos.x += props.advance * letterSpacing * directionX; prevC = c; } ++it; }catch(...){ break; } } }
void ofTrueTypeFont::iterateString(const string & str, float x, float y, bool vFlipped, std::function<void(uint32_t, ofVec2f)> f) const { ofVec2f pos(x,y); int newLineDirection = 1; if(!vFlipped) { // this would align multiline texts to the last line when vflip is disabled //int lines = ofStringTimesInString(c,"\n"); //Y = lines*lineHeight; newLineDirection = -1; } int directionX = settings.direction == ofTtfSettings::LeftToRight?1:-1; uint32_t prevC = 0; for(auto c: ofUTF8Iterator(str)) { try { if (c == '\n') { pos.y += lineHeight*newLineDirection; pos.x = x ; //reset X Pos back to zero prevC = 0; } if (c == '\t') { pos.x += getGlyphProperties(' ').advance * letterSpacing * 4 * directionX; prevC = c; } else if(isValidGlyph(c)) { const auto & props = getGlyphProperties(c); if(prevC>0) { pos.x += getKerning(c,prevC);// * directionX; } f(c,pos); pos.x += props.advance * letterSpacing * directionX; prevC = c; } } catch(...) { break; } } }
void getGlyphPositions (const String& text, Array <int>& resultGlyphs, Array <float>& xOffsets) { const CharPointer_UTF16 utf16 (text.toUTF16()); const size_t numChars = utf16.length(); HeapBlock<int16> results (numChars + 1); results[numChars] = -1; float x = 0; if (GetGlyphIndices (dc, utf16, (int) numChars, reinterpret_cast <WORD*> (results.getData()), GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR) { resultGlyphs.ensureStorageAllocated ((int) numChars); xOffsets.ensureStorageAllocated ((int) numChars + 1); for (size_t i = 0; i < numChars; ++i) { resultGlyphs.add (results[i]); xOffsets.add (x); x += getKerning (dc, results[i], results[i + 1]); } } xOffsets.add (x); }
/* * FTLIB_StrlenForWidth * returns the len allowed for the string to fit inside a given width when using a given font. */ size_t FTLIB_StrlenForWidth( const char *str, qfontface_t *font, size_t maxwidth, int flags ) { const char *s, *olds; size_t width = 0; int gc; int advance = 0; wchar_t num, prev_num = 0; qglyph_t *glyph, *prev_glyph = NULL; renderString_f renderString; getKerning_f getKerning; bool hasKerning; if( !str || !font ) return 0; renderString = font->f->renderString; getKerning = font->f->getKerning; hasKerning = ( flags & TEXTDRAWFLAG_KERNING ) && font->hasKerning; for( s = str; s; ) { olds = s; gc = FTLIB_GrabChar( &s, &num, NULL, flags ); if( gc == GRABCHAR_CHAR ) { if( num == '\n' ) break; if( num < ' ' ) continue; glyph = FTLIB_GetGlyph( font, num ); if( !glyph ) { num = FTLIB_REPLACEMENT_GLYPH; glyph = FTLIB_GetGlyph( font, num ); } if( !glyph->shader ) renderString( font, olds ); advance = glyph->x_advance; if( hasKerning && prev_num ) { advance += getKerning( font, prev_glyph, glyph ); } if( maxwidth && ( ( width + advance ) > maxwidth ) ) { s = olds; break; } width += advance; prev_num = num; prev_glyph = glyph; } else if( gc == GRABCHAR_COLOR ) continue; else if( gc == GRABCHAR_END ) break; else assert( 0 ); } return (unsigned int)( s - str ); }
/* * FTLIB_DrawClampString */ void FTLIB_DrawClampString( int x, int y, const char *str, int xmin, int ymin, int xmax, int ymax, qfontface_t *font, vec4_t color, int flags ) { int xoffset = 0; vec4_t scolor; int colorindex; wchar_t num, prev_num = 0; const char *s = str, *olds; int gc; qglyph_t *glyph, *prev_glyph = NULL; renderString_f renderString; getKerning_f getKerning; bool hasKerning; if( !str || !font ) return; if( ( xmax <= xmin ) || ( ymax <= ymin ) || ( x > xmax ) || ( y > ymax ) ) return; Vector4Copy( color, scolor ); renderString = font->f->renderString; getKerning = font->f->getKerning; hasKerning = ( flags & TEXTDRAWFLAG_KERNING ) && font->hasKerning; while( 1 ) { olds = s; gc = FTLIB_GrabChar( &s, &num, &colorindex, flags ); if( gc == GRABCHAR_CHAR ) { if( num == '\n' ) break; if( num < ' ' ) continue; glyph = FTLIB_GetGlyph( font, num ); if( !glyph ) { num = FTLIB_REPLACEMENT_GLYPH; glyph = FTLIB_GetGlyph( font, num ); } if( !glyph->shader ) renderString( font, olds ); if( prev_num ) { xoffset += prev_glyph->x_advance; if( hasKerning ) xoffset += getKerning( font, prev_glyph, glyph ); } if( x + xoffset > xmax ) break; FTLIB_DrawClampChar( x + xoffset, y, num, xmin, ymin, xmax, ymax, font, scolor ); prev_num = num; prev_glyph = glyph; } else if( gc == GRABCHAR_COLOR ) { assert( ( unsigned )colorindex < MAX_S_COLORS ); VectorCopy( color_table[colorindex], scolor ); } else if( gc == GRABCHAR_END ) break; else assert( 0 ); } }
/* FTLIB_DrawMultilineString * * Draws a string with word wrap. */ int FTLIB_DrawMultilineString( int x, int y, const char *str, int halign, int maxwidth, int maxlines, qfontface_t *font, vec4_t color, int flags ) { bool ended = false; // whether to stop drawing lines // characters and glyphs const char *oldstr; int gc, colorindex; wchar_t num, prev_num; qglyph_t *glyph, *prev_glyph; int glyph_width; renderString_f renderString; getKerning_f getKerning; bool hasKerning; // words const char *word; // beginning of the current word int word_chars, space_chars; // length of the current word and number of spaces before it int word_width, space_width; // width of the current word and the spaces before it vec4_t word_color; // starting color of the current word bool in_space; // whether currently in a sequence of spaces // line drawing const char *line; // beginning of the line int line_chars; // number of characters to draw in this line int line_width; // width of the current line int line_x; // x position of the current character in line vec4_t line_color, line_next_color; // first color in the line int line_height; // height of a single line int lines = 0; // number of lines drawn - the return value if( !str || !font || ( maxwidth <= 0 ) ) return 0; halign = halign % 3; // ignore vertical alignment renderString = font->f->renderString; getKerning = font->f->getKerning; hasKerning = ( flags & TEXTDRAWFLAG_KERNING ) && font->hasKerning; VectorCopy( color, line_next_color ); line_color[3] = color[3]; line_height = FTLIB_FontHeight( font ); do { // reset word_chars = space_chars = 0; word_width = space_width = 0; in_space = true; // assume starting from a whitespace so preceding whitespaces can be skipped line_chars = 0; line_width = 0; VectorCopy( line_next_color, line_color ); // find where to wrap prev_num = 0; prev_glyph = NULL; while( str ) { oldstr = str; gc = FTLIB_GrabChar( &str, &num, &colorindex, flags ); if( gc == GRABCHAR_CHAR ) { if( num == '\n' ) { if( !word_chars ) space_chars = space_width = 0; VectorCopy( color, line_next_color ); break; } if( num < ' ' ) continue; glyph = FTLIB_GetGlyph( font, num ); if( !glyph ) { num = FTLIB_REPLACEMENT_GLYPH; glyph = FTLIB_GetGlyph( font, num ); } if( !glyph->shader ) renderString( font, oldstr ); if( Q_IsBreakingSpaceChar( num ) ) { if( in_space ) { if( !line_chars ) continue; // skip preceding whitespaces in a line } else { in_space = true; // reached the space without wrapping - send the current word to the line line_chars += space_chars + word_chars; word_chars = space_chars = 0; line_width += space_width + word_width; word_width = space_width = 0; } space_chars++; if( hasKerning && prev_num ) space_width += getKerning( font, prev_glyph, glyph ); space_width += glyph->x_advance; } else { in_space = false; glyph_width = glyph->x_advance; if( hasKerning && prev_num ) glyph_width += getKerning( font, prev_glyph, glyph ); if( !word_chars ) { word = oldstr; VectorCopy( line_next_color, word_color ); } if( line_chars ) { // wrap after the previous word, ignoring spaces between the words if( ( line_width + space_width + word_width + glyph_width ) > maxwidth ) { str = word; VectorCopy( word_color, line_next_color ); word_chars = space_chars = 0; word_width = space_width = 0; break; } } else { line = word; if( word_chars ) // always draw at least 1 character in a line { if( ( word_width + glyph_width ) > maxwidth ) { str = oldstr; break; } } } word_chars++; word_width += glyph_width; } prev_num = num; prev_glyph = glyph; } else if( gc == GRABCHAR_COLOR ) { assert( ( unsigned )colorindex < MAX_S_COLORS ); VectorCopy( color_table[colorindex], line_next_color ); if( !line_chars && !word_chars ) VectorCopy( line_next_color, line_color ); } else if( gc == GRABCHAR_END ) { ended = true; break; } else assert( 0 ); } // add the remaining part of the word line_chars += space_chars + word_chars; line_width += space_width + word_width; // draw the line if( line_chars > 0 ) { line_x = x; if( halign == ALIGN_CENTER_TOP ) line_x -= line_width >> 1; else if( halign == ALIGN_RIGHT_TOP ) line_x -= line_width; prev_num = 0; prev_glyph = NULL; while( ( line_chars > 0 ) && line ) { gc = FTLIB_GrabChar( &line, &num, &colorindex, flags ); if( gc == GRABCHAR_CHAR ) { if( num < ' ' ) continue; line_chars--; glyph = FTLIB_GetGlyph( font, num ); if( !glyph ) { num = FTLIB_REPLACEMENT_GLYPH; glyph = FTLIB_GetGlyph( font, num ); } if( hasKerning && prev_num ) line_x += getKerning( font, prev_glyph, glyph ); FTLIB_DrawRawChar( line_x, y, num, font, line_color ); line_x += glyph->x_advance; prev_num = num; prev_glyph = glyph; } else if( gc == GRABCHAR_COLOR ) { assert( ( unsigned )colorindex < MAX_S_COLORS ); VectorCopy( color_table[colorindex], line_color ); } else if( gc == GRABCHAR_END ) break; else assert( 0 ); } }
/* * FTLIB_DrawRawString - Doesn't care about aligning. Returns drawn len. * It can stop when reaching maximum width when a value has been parsed. */ size_t FTLIB_DrawRawString( int x, int y, const char *str, size_t maxwidth, int *width, qfontface_t *font, vec4_t color, int flags ) { unsigned int xoffset = 0; vec4_t scolor; const char *s, *olds; int gc, colorindex; wchar_t num, prev_num = 0; qglyph_t *glyph, *prev_glyph = NULL; renderString_f renderString; getKerning_f getKerning; bool hasKerning; if( !str || !font ) return 0; Vector4Copy( color, scolor ); renderString = font->f->renderString; getKerning = font->f->getKerning; hasKerning = ( flags & TEXTDRAWFLAG_KERNING ) && font->hasKerning; for( s = str; s; ) { olds = s; gc = FTLIB_GrabChar( &s, &num, &colorindex, flags ); if( gc == GRABCHAR_CHAR ) { if( num == '\n' ) break; if( num < ' ' ) continue; glyph = FTLIB_GetGlyph( font, num ); if( !glyph ) { num = FTLIB_REPLACEMENT_GLYPH; glyph = FTLIB_GetGlyph( font, num ); } if( !glyph->shader ) renderString( font, olds ); // ignore kerning at this point so the full width of the previous character will always be returned if( maxwidth && ( ( xoffset + glyph->x_advance ) > maxwidth ) ) { s = olds; break; } if( hasKerning && prev_num ) xoffset += getKerning( font, prev_glyph, glyph ); FTLIB_DrawRawChar( x + xoffset, y, num, font, scolor ); xoffset += glyph->x_advance; prev_num = num; prev_glyph = glyph; } else if( gc == GRABCHAR_COLOR ) { assert( ( unsigned )colorindex < MAX_S_COLORS ); VectorCopy( color_table[colorindex], scolor ); } else if( gc == GRABCHAR_END ) break; else assert( 0 ); } if( width ) *width = xoffset; return ( s - str ); }
/* * FTLIB_strWidth * doesn't count invisible characters. Counts up to given length, if any. */ size_t FTLIB_strWidth( const char *str, qfontface_t *font, size_t maxlen, int flags ) { const char *s = str, *olds; size_t width = 0; wchar_t num, prev_num = 0; qglyph_t *glyph, *prev_glyph = NULL; renderString_f renderString; getKerning_f getKerning; bool hasKerning; if( !str || !font ) return 0; renderString = font->f->renderString; getKerning = font->f->getKerning; hasKerning = ( flags & TEXTDRAWFLAG_KERNING ) && font->hasKerning; while( *s && *s != '\n' ) { if( maxlen && (size_t)( s - str ) >= maxlen ) // stop counting at desired len return width; olds = s; switch( FTLIB_GrabChar( &s, &num, NULL, flags ) ) { case GRABCHAR_CHAR: if( num < ' ' ) break; glyph = FTLIB_GetGlyph( font, num ); if( !glyph ) { num = FTLIB_REPLACEMENT_GLYPH; glyph = FTLIB_GetGlyph( font, num ); } if( !glyph->shader ) renderString( font, olds ); if( prev_num && hasKerning ) width += getKerning( font, prev_glyph, glyph ); width += glyph->x_advance; prev_num = num; prev_glyph = glyph; break; case GRABCHAR_COLOR: break; case GRABCHAR_END: return width; default: assert( 0 ); } } return width; }