diff --git a/libraries/pico_vector/alright-fonts.h b/libraries/pico_vector/alright-fonts.h index a773c810..20426a3a 100644 --- a/libraries/pico_vector/alright-fonts.h +++ b/libraries/pico_vector/alright-fonts.h @@ -31,6 +31,7 @@ #include #include #include +#include #ifdef AF_MALLOC #ifndef PP_MALLOC @@ -104,8 +105,8 @@ typedef struct { bool af_load_font_file(AF_FILE file, af_face_t *face); void af_render_character(af_face_t *face, const char codepoint, af_text_metrics_t *tm); -void af_render(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm); -pp_rect_t af_measure(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm); +void af_render(af_face_t *face, const char *text, size_t tlen, float max_line_width, float max_height, af_text_metrics_t *tm); +pp_rect_t af_measure(af_face_t *face, const char *text, size_t tlen, float max_line_width, af_text_metrics_t *tm); #ifdef AF_USE_PRETTY_POLY #endif @@ -240,20 +241,30 @@ void af_render_character(af_face_t *face, const char c, af_text_metrics_t *tm) { af_render_glyph(glyph, tm); } -int get_line_width(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm) { - int line_width = 0; - char *end = (char *)text + tlen; +float get_line_width(af_face_t *face, const char *text, size_t *tlen, float max_line_width, af_text_metrics_t *tm) { + float line_width = 0; + const char *start = text; + const char *end = text + *tlen; + const char *last_space = nullptr; for(char c = *text; text < end; text++, c = *text) { af_glyph_t *glyph = find_glyph(face, c); if(!glyph) { continue; } + float char_width; if(c == L' ') { - line_width += (glyph->advance * tm->word_spacing) / 100.0f; + char_width = (glyph->advance * tm->word_spacing) / 100.0f; + last_space = text; } else { - line_width += (glyph->advance * tm->letter_spacing) / 100.0f; + char_width = (glyph->advance * tm->letter_spacing) / 100.0f; } + + if (max_line_width > 0 && line_width + char_width > max_line_width && last_space) { + *tlen = last_space - start; + break; + } + line_width += char_width; } return line_width; } @@ -270,14 +281,14 @@ size_t line_length(const char *text, const char *end) { return line_ending - text; } -int get_max_line_width(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm) { - int max_width = 0; +float get_max_line_width(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm) { + float max_width = 0; char *line = (char *)text; char *tend = line + tlen; size_t line_len = line_length(line, tend); while(line_len) { - int width = get_line_width(face, line, line_len, tm); + float width = get_line_width(face, line, &line_len, 0, tm); max_width = max_width < width ? width : max_width; line += line_len + 1; line_len = line_length(line, tend); @@ -286,7 +297,7 @@ int get_max_line_width(af_face_t *face, const char *text, size_t tlen, af_text_m return max_width; } -void af_render(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm) { +void af_render(af_face_t *face, const char *text, size_t tlen, float max_line_width, float max_height, af_text_metrics_t *tm) { char *line = (char *)text; char *tend = line + tlen; size_t line_len = 0; @@ -304,15 +315,23 @@ void af_render(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t caret.y = 0; // find maximum line length - int max_line_width = get_max_line_width(face, text, tlen, tm); + if (max_line_width == 0.f) { + max_line_width = get_max_line_width(face, text, tlen, tm); + } else { + max_line_width /= scale; + } + if (max_height == 0.f) { + max_height = FLT_MAX; + } else { + max_height /= scale; + } line_len = line_length(line, tend); - while(line_len) { + while(line_len && caret.y + line_height <= max_height) { + int line_width = get_line_width(face, line, &line_len, max_line_width, tm); char *end = line + line_len; - int line_width = get_line_width(face, line, line_len, tm); - for(char c = *line; line < end; line++, c = *line) { af_glyph_t *glyph = find_glyph(face, c); if(!glyph) { @@ -356,10 +375,10 @@ void af_render(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t } void _af_render(af_face_t *face, const char *text, af_text_metrics_t *tm) { - af_render(face, text, strlen(text), tm); + af_render(face, text, strlen(text), 0, 0, tm); } -pp_rect_t af_measure(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm) { +pp_rect_t af_measure(af_face_t *face, const char *text, size_t tlen, float max_line_width, af_text_metrics_t *tm) { pp_rect_t result; bool first = true; char *line = (char *)text; @@ -377,15 +396,14 @@ pp_rect_t af_measure(af_face_t *face, const char *text, size_t tlen, af_text_met caret.y = 0; // find maximum line length - int max_line_width = get_max_line_width(face, text, tlen, tm); + if (max_line_width == 0.f) max_line_width = get_max_line_width(face, text, tlen, tm); line_len = line_length(line, tend); while(line_len) { + int line_width = get_line_width(face, line, &line_len, max_line_width, tm); char *end = line + line_len; - int line_width = get_line_width(face, line, line_len, tm); - for(char c = *line; line < end; line++, c = *line) { af_glyph_t *glyph = find_glyph(face, c); if(!glyph) { diff --git a/libraries/pico_vector/pico_vector.cpp b/libraries/pico_vector/pico_vector.cpp index 41d15882..43079a49 100644 --- a/libraries/pico_vector/pico_vector.cpp +++ b/libraries/pico_vector/pico_vector.cpp @@ -60,93 +60,13 @@ namespace pimoroni { } } - pp_point_t PicoVector::text(std::string_view text, pp_mat3_t *t) { + pp_point_t PicoVector::text(std::string_view text, int max_width, int max_height, pp_mat3_t *t) { pp_point_t caret = {0, 0}; text_metrics.transform = t; - af_render(text_metrics.face, text.data(), text.size(), &text_metrics); + af_render(text_metrics.face, text.data(), text.size(), max_width, max_height, &text_metrics); return caret; -/* - // Align text from the bottom left - caret.y += (PP_COORD_TYPE)text_metrics.line_height; - - caret = pp_point_transform(&caret, t); - caret.x += offset.x; - caret.y += offset.y; - - pp_point_t space; - pp_point_t carriage_return = {0, -(PP_COORD_TYPE)text_metrics.line_height}; - - char spc = ' '; - - space.x = af_measure(text_metrics.face, &spc, &text_metrics).w; - if (space.x == 0) { - space.x = text_metrics.word_spacing; - } - - space = pp_point_transform(&space, t); - carriage_return = pp_point_transform(&carriage_return, t); - - pp_point_t initial_carriage_return = carriage_return; - - size_t i = 0; - - while(i < text.length()) { - size_t next_space = text.find(' ', i + 1); - - if(next_space == std::string::npos) { - next_space = text.length(); - } - - size_t next_linebreak = text.find('\n', i + 1); - - if(next_linebreak == std::string::npos) { - next_linebreak = text.length(); - } - - size_t next_break = std::min(next_space, next_linebreak); - - uint16_t word_width = 0; - for(size_t j = i; j < next_break; j++) { - word_width += af_measure(text_metrics.face, &text[j], &text_metrics).w; - word_width += text_metrics.letter_spacing; - } - - if(caret.x != 0 && caret.x + word_width > graphics->clip.w) { - caret = pp_point_sub(&caret, &carriage_return); - carriage_return = initial_carriage_return; - } - - for(size_t j = i; j < std::min(next_break + 1, text.length()); j++) { - if (text[j] == '\n') { // Linebreak - caret = pp_point_sub(&caret, &carriage_return); - carriage_return = initial_carriage_return; - } else if (text[j] == ' ') { // Space - caret = pp_point_add(&caret, &space); - carriage_return = pp_point_add(&carriage_return, &space); - } else { - // apply the caret offset... - pp_mat3_t pos = pp_mat3_identity(); - pp_mat3_mul(&pos, t); - pp_mat3_translate(&pos, caret.x, caret.y); - text_metrics.transform = &pos; - af_render_character(text_metrics.face, text[j], &text_metrics); - } - pp_point_t advance = { - (PP_COORD_TYPE)af_measure(text_metrics.face, &text[j], &text_metrics).w + text_metrics.letter_spacing, - (PP_COORD_TYPE)0 - }; - advance = pp_point_transform(&advance, t); - caret = pp_point_add(&caret, &advance); - carriage_return = pp_point_add(&carriage_return, &advance); - } - - i = next_break + 1; - } - - return {caret.x, caret.y}; -*/ } } \ No newline at end of file diff --git a/libraries/pico_vector/pico_vector.hpp b/libraries/pico_vector/pico_vector.hpp index 883004f8..ff21c4d4 100644 --- a/libraries/pico_vector/pico_vector.hpp +++ b/libraries/pico_vector/pico_vector.hpp @@ -93,7 +93,7 @@ namespace pimoroni { pp_rect_t measure_text(std::string_view text, pp_mat3_t *t) { text_metrics.transform = t; - return af_measure(text_metrics.face, text.data(), text.size(), &text_metrics); + return af_measure(text_metrics.face, text.data(), text.size(), 0, &text_metrics); } bool set_font(std::string_view font_path, unsigned int font_size) { @@ -124,7 +124,7 @@ namespace pimoroni { return result; } - pp_point_t text(std::string_view text, pp_mat3_t *t=nullptr); + pp_point_t text(std::string_view text, int max_width, int max_height, pp_mat3_t *t=nullptr); void transform(pp_path_t *path, pp_mat3_t *t); void transform(pp_poly_t *poly, pp_mat3_t *t); diff --git a/micropython/modules/picovector/picovector.cpp b/micropython/modules/picovector/picovector.cpp index c791197f..4f637f70 100644 --- a/micropython/modules/picovector/picovector.cpp +++ b/micropython/modules/picovector/picovector.cpp @@ -699,13 +699,15 @@ mp_obj_t VECTOR_set_antialiasing(mp_obj_t self_in, mp_obj_t aa) { } mp_obj_t VECTOR_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_self, ARG_text, ARG_x, ARG_y, ARG_angle }; + enum { ARG_self, ARG_text, ARG_x, ARG_y, ARG_angle, ARG_max_width, ARG_max_height }; static const mp_arg_t allowed_args[] = { { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_text, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT }, { MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_angle, MP_ARG_OBJ, {.u_obj = mp_const_none} } + { MP_QSTR_angle, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_max_width, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_max_height, MP_ARG_INT, {.u_int = 0} } }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -723,6 +725,8 @@ mp_obj_t VECTOR_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) int x = args[ARG_x].u_int; int y = args[ARG_y].u_int; + int max_width = args[ARG_max_width].u_int; + int max_height = args[ARG_max_height].u_int; pp_mat3_t tt = pp_mat3_identity(); @@ -732,7 +736,7 @@ mp_obj_t VECTOR_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) pp_mat3_translate(&tt, (float)x, (float)y); - self->vector->text(t, &tt); + self->vector->text(t, max_width, max_height, &tt); return mp_const_none; }