diff --git a/demo/src/font.c b/demo/src/font.c index 69f46d0..f805ca6 100644 --- a/demo/src/font.c +++ b/demo/src/font.c @@ -30,6 +30,57 @@ void fontDraw() { textAlign(HALIGN_RIGHT, VALIGN_TOP); text(x, y, "Here is the\nright aligned\ntext. VAW."); + resetState(); + textFontDefault(); + + x = 196; + y = 384; + double w = 128; + double h = 96; + int haligns[] = { HALIGN_LEFT, HALIGN_CENTER, HALIGN_RIGHT }; + int valigns[] = { VALIGN_TOP, VALIGN_CENTER, VALIGN_BOTTOM }; + const char *ht[] = { "left", "center I", "right" }; + const char *vt[] = { "top", "center", "bottom q" }; + for(int j = 0; j < 3; ++j) { + for(int i = 0; i < 3; ++i) { + double xx = x + (1-i)*w/2; + double yy = y + (1-j)*h/2; + textAlign(haligns[i], valigns[j]); + textf(xx, yy, "%s\n%s", ht[i], vt[j]); + if (i == 1 && j == 1) { + saveState(); + strokeWidth(0.5); + TextLayout layout = createTextLayout("center I\ncenter q"); + double ex = 10; + double l = textLayoutGetLeft(layout); + double t = textLayoutGetTop(layout); + double w = textLayoutGetWidth(layout); + double h = textLayoutGetHeight(layout); + double a = textLayoutGetTopAscenderLine(layout); + double x = textLayoutGetTopXLine(layout); + double b = textLayoutGetBottomBaseline(layout); + line( xx+l-ex , yy+t , xx+l+w+ex , yy+t ); + line( xx+l-ex , yy+t+h , xx+l+w+ex , yy+t+h ); + line( xx+l , yy+t-ex , xx+l , yy+t+h+ex ); + line( xx+l+w , yy+t-ex , xx+l+w , yy+t+h+ex ); + line( xx+l-ex , yy+a , xx+l+w+ex , yy+a ); + line( xx+l-ex , yy+x , xx+l+w+ex , yy+x ); + line( xx+l-ex , yy+b , xx+l+w+ex , yy+b ); + restoreState(); + } + } + } + line(x - 1.5*w, y - h/2, x + 1.5*w, y - h/2); + line(x - 1.5*w, y + h/2, x + 1.5*w, y + h/2); + line(x - w/2, y - 1.5*h, x - w/2, y + 1.5*h); + line(x + w/2, y - 1.5*h, x + w/2, y + 1.5*h); + + noFill(); + textAlign(HALIGN_CENTER, VALIGN_CENTER); + const char *chars = "zlpvoxS"; + for(int i = 0; i < 7; ++i) + textf(x - w/2 + (i + 1)/8.0*w, y + h/2, "%c", chars[i]); + restoreState(); } diff --git a/doc/helianthus-doc-ru.odt b/doc/helianthus-doc-ru.odt index 424cc17..1b5bb43 100644 Binary files a/doc/helianthus-doc-ru.odt and b/doc/helianthus-doc-ru.odt differ diff --git a/doc/text-metrics.png b/doc/text-metrics.png new file mode 100644 index 0000000..564070e Binary files /dev/null and b/doc/text-metrics.png differ diff --git a/doc/text-metrics.svg b/doc/text-metrics.svg new file mode 100644 index 0000000..abd5bd6 --- /dev/null +++ b/doc/text-metrics.svg @@ -0,0 +1,574 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + LoremIpsum + + + + + + + + x + y + width + height + bottom baseline + top ascending line + top x-line + + + + + + + top + left + 0, 0 + + + + diff --git a/src/font.c b/src/font.c index 2216382..86f4696 100644 --- a/src/font.c +++ b/src/font.c @@ -44,6 +44,9 @@ struct _HeliFontInstance { char *path; FT_Face face; double height; + double xheight; + double ascender; + double descender; HeliGlyph *glyphs[FONT_MAX_GLYPHS]; int refcount; }; @@ -60,6 +63,9 @@ typedef struct _HeliLineDesc { int begin, end; double x, y; double l, t, r, b; + double xheight; + double ascender; + double descender; double height; double width; } HeliLineDesc; @@ -270,6 +276,26 @@ static HeliFontInstance* loadFont(const char *path) { if (fi->face) { FT_Set_Char_Size(fi->face, 0, FONT_BASE_SIZE*64, 72, 72); fi->height = fi->face->size->metrics.height/64.0; + double ascender = fi->face->size->metrics.ascender/64.0; + + unsigned int xchars[] = { 'x', 'z', 'v' }; + int count = 0; + double xheight = 0; + for(int i = 0; i < (int)(sizeof(xchars)/sizeof(*xchars)); ++i) { + int index = FT_Get_Char_Index(fi->face, xchars[i]); + if (!index) continue; + if (FT_Load_Glyph(fi->face, index, FT_LOAD_NO_HINTING)) + continue; + xheight += fi->face->glyph->metrics.horiBearingY/64.0; + ++count; + } + if (count > 0) xheight /= count; + if (xheight <= HELI_PRECISION || xheight > ascender) + xheight = ascender; + + fi->ascender = ascender; + fi->xheight = xheight; + fi->descender = fi->face->size->metrics.descender/64.0; } } @@ -385,6 +411,9 @@ TextLayout createTextLayout(const char *text) { layout->lines = calloc(linesAllocated, sizeof(HeliLineDesc)); HeliLineDesc *line = layout->lines; line->height = fonts[0]->height; + line->xheight = fonts[0]->xheight; + line->ascender = fonts[0]->ascender; + line->descender = fonts[0]->descender; layout->chars = calloc(strlen(text) + 1, sizeof(HeliCharDesc)); if ( drawingState->font && drawingState->font != defaultFont @@ -426,7 +455,10 @@ TextLayout createTextLayout(const char *text) { HeliCharDesc *cc = &layout->chars[line->end++]; cc->charpos = charpos - text; cc->glyph = glyph; - if (line->height < glyph->fi->height) line->height = glyph->fi->height; + if (line->height < glyph->fi->height ) line->height = glyph->fi->height; + if (line->xheight < glyph->fi->xheight ) line->xheight = glyph->fi->xheight; + if (line->ascender < glyph->fi->ascender ) line->ascender = glyph->fi->ascender; + if (line->descender < glyph->fi->descender) line->descender = glyph->fi->descender; if (line->begin < line->end - 1) { HeliCharDesc *cp = cc - 1; cc->pos = cp->pos + cp->glyph->advance; @@ -442,8 +474,16 @@ TextLayout createTextLayout(const char *text) { if (line->width < cc->pos + cc->glyph->advance) line->width = cc->pos + cc->glyph->advance; } - expand(&line->l, &line->r, cc->pos + cc->glyph->l); - expand(&line->l, &line->r, cc->pos + cc->glyph->r); + + if ( line->begin >= line->end - 1 + || ( cc->glyph->l + HELI_PRECISION < cc->glyph->r + && code != 10 + && code != 13 + && code != 0 )) + { + expand(&line->l, &line->r, cc->pos + cc->glyph->l); + expand(&line->l, &line->r, cc->pos + cc->glyph->r); + } expand(&line->t, &line->b, cc->glyph->t); expand(&line->t, &line->b, cc->glyph->b); expand(&line->t, &line->b, -glyph->fi->height); @@ -460,6 +500,9 @@ TextLayout createTextLayout(const char *text) { ++layout->linesCount; line->begin = line->end = (line - 1)->end; line->height = fonts[0]->height; + line->xheight = fonts[0]->xheight; + line->ascender = fonts[0]->ascender; + line->descender = fonts[0]->descender; } if (code == 0) break; } @@ -468,12 +511,15 @@ TextLayout createTextLayout(const char *text) { if (charsCount <= 0) return layout; // bounds + double lineHeight = layout->lines->height; + if (drawingState->vertAlign == VALIGN_CENTER) + lineHeight = layout->lines->xheight; double l = 0; double t = -layout->lines->height; double r = 0; double b = 0; double ll = 0; - double lt = -layout->lines->height; + double lt = -lineHeight; double lr = 0; double lb = 0; for(int i = 0; i < layout->linesCount; ++i) { @@ -485,8 +531,8 @@ TextLayout createTextLayout(const char *text) { if (t > line->t + line->y) t = line->t + line->y; if (b < line->b + line->y) b = line->b + line->y; - if (lr < line->width) lr = line->width; - if (lt > line->y - line->height) lt = line->y - line->height; + if (lr < line->r) lr = line->r; + if (lt > line->y - lineHeight) lt = line->y - lineHeight; if (lb < line->y) lb = line->y; } @@ -499,8 +545,8 @@ TextLayout createTextLayout(const char *text) { : -lt; for(int i = 0; i < layout->linesCount; ++i) { HeliLineDesc *line = layout->lines + i; - line->x = drawingState->horAlign == HALIGN_RIGHT ? -line->width - : drawingState->horAlign == HALIGN_CENTER ? -0.5*line->width + line->x = drawingState->horAlign == HALIGN_RIGHT ? -line->r + : drawingState->horAlign == HALIGN_CENTER ? -0.5*line->r : 0; line->y += dy; for(int j = line->begin; j < line->end; ++j) { @@ -704,6 +750,20 @@ void textf(double x, double y, const char *format, ...) { } +double textLayoutGetLeft(TextLayout layout) { + HeliDrawingState *drawingState = heliDrawingGetState(); + double fontScale = drawingState->fontSize/FONT_BASE_SIZE; + return layout->l*fontScale; +} + + +double textLayoutGetTop(TextLayout layout) { + HeliDrawingState *drawingState = heliDrawingGetState(); + double fontScale = drawingState->fontSize/FONT_BASE_SIZE; + return layout->t*fontScale; +} + + double textLayoutGetWidth(TextLayout layout) { HeliDrawingState *drawingState = heliDrawingGetState(); double fontScale = drawingState->fontSize/FONT_BASE_SIZE; @@ -718,6 +778,27 @@ double textLayoutGetHeight(TextLayout layout) { } +double textLayoutGetTopAscenderLine(TextLayout layout) { + HeliDrawingState *drawingState = heliDrawingGetState(); + double fontScale = drawingState->fontSize/FONT_BASE_SIZE; + return (layout->lines->y - layout->lines->xheight)*fontScale; +} + + +double textLayoutGetTopXLine(TextLayout layout) { + HeliDrawingState *drawingState = heliDrawingGetState(); + double fontScale = drawingState->fontSize/FONT_BASE_SIZE; + return (layout->lines->y - layout->lines->ascender)*fontScale; +} + + +double textLayoutGetBottomBaseline(TextLayout layout) { + HeliDrawingState *drawingState = heliDrawingGetState(); + double fontScale = drawingState->fontSize/FONT_BASE_SIZE; + return layout->lines[layout->linesCount - 1].y * fontScale; +} + + void heliFontFinish() { defaultFont = NULL; unicodeFont = NULL; diff --git a/src/font.h b/src/font.h index fd1c479..0d6b000 100644 --- a/src/font.h +++ b/src/font.h @@ -41,8 +41,13 @@ void textLayoutDestroy(TextLayout layout); void textLayoutDraw(TextLayout layout, double x, double y); void textLayoutDrawFrom(TextLayout layout, double x, double y, int start); void textLayoutDrawSubstr(TextLayout layout, double x, double y, int start, int length); +double textLayoutGetLeft(TextLayout layout); +double textLayoutGetTop(TextLayout layout); double textLayoutGetWidth(TextLayout layout); double textLayoutGetHeight(TextLayout layout); +double textLayoutGetTopAscenderLine(TextLayout layout); +double textLayoutGetTopXLine(TextLayout layout); +double textLayoutGetBottomBaseline(TextLayout layout); int textLayoutCursorUp(TextLayout layout, int cursor); int textLayoutCursorDown(TextLayout layout, int cursor); double textLayoutCursorGetX(TextLayout layout, int cursor);