/** * Measure the width of a string. * * \param fstyle plot style for this text * \param string UTF-8 string to measure * \param length length of string * \param width updated to width of string[0..length) * \return true on success, false on error and error reported */ static nserror ro_font_width(const plot_font_style_t *fstyle, const char *string, size_t length, int *width) { const char *font_family; unsigned int font_size; rufl_style font_style; rufl_code code; nsfont_read_style(fstyle, &font_family, &font_size, &font_style); if (font_size == 0) { *width = 0; return NSERROR_OK; } code = rufl_width(font_family, font_style, font_size, string, length, width); if (code != rufl_OK) { if (code == rufl_FONT_MANAGER_ERROR) LOG("rufl_width: rufl_FONT_MANAGER_ERROR: 0x%x: %s", rufl_fm_error->errnum, rufl_fm_error->errmess); else LOG("rufl_width: 0x%x", code); /* ro_warn_user("MiscError", "font error"); */ *width = 0; return NSERROR_INVALID; } *width /= 2; return NSERROR_OK; }
bool nsfont_split(const plot_font_style_t *fstyle, const char *string, size_t length, int x, size_t *char_offset, int *actual_x) { const char *font_family; unsigned int font_size; rufl_style font_style; rufl_code code; nsfont_read_style(fstyle, &font_family, &font_size, &font_style); if (font_size == 0) { *char_offset = 0; *actual_x = 0; return true; } code = rufl_split(font_family, font_style, font_size, string, length, x * 2, char_offset, actual_x); if (code != rufl_OK) { if (code == rufl_FONT_MANAGER_ERROR) LOG(("rufl_split: rufl_FONT_MANAGER_ERROR: " "0x%x: %s", rufl_fm_error->errnum, rufl_fm_error->errmess)); else LOG(("rufl_split: 0x%x", code)); /* warn_user("MiscError", "font error"); */ return false; } while (*char_offset && string[*char_offset] != ' ') (*char_offset)--; code = rufl_width(font_family, font_style, font_size, string, *char_offset, actual_x); if (code != rufl_OK) { if (code == rufl_FONT_MANAGER_ERROR) LOG(("rufl_width: rufl_FONT_MANAGER_ERROR: 0x%x: %s", rufl_fm_error->errnum, rufl_fm_error->errmess)); else LOG(("rufl_width: 0x%x", code)); /* warn_user("MiscError", "font error"); */ return false; } *actual_x /= 2; return true; }
static PyObject * pyrufl_width(PyObject *self /* Not used */, PyObject *args) { const char *font_family; rufl_style font_style; unsigned int font_size; const char *string; int length; int width = 0; if (!PyArg_ParseTuple(args, "siIs#", &font_family, &font_style, &font_size, &string, &length)) return NULL; rufl_width(font_family, font_style, font_size, string, length, &width); return PyInt_FromLong(width); }
int main(void) { char utf8_test[] = "Hello, world! ὕαλον " "Uherské Hradiště. 𐀀"; int width; size_t char_offset; int x; int actual_x; struct rufl_decomp_funcs funcs = { move_to, line_to, cubic_to }; int bbox[4]; try(rufl_init(), "rufl_init"); rufl_dump_state(); try(rufl_paint("NewHall", rufl_WEIGHT_400, 240, utf8_test, sizeof utf8_test - 1, 1200, 1000, 0), "rufl_paint"); try(rufl_width("NewHall", rufl_WEIGHT_400, 240, utf8_test, sizeof utf8_test - 1, &width), "rufl_width"); printf("width: %i\n", width); for (x = 0; x < width + 100; x += 100) { try(rufl_x_to_offset("NewHall", rufl_WEIGHT_400, 240, utf8_test, sizeof utf8_test - 1, x, &char_offset, &actual_x), "rufl_x_to_offset"); printf("x to offset: %i -> %i %zi \"%s\"\n", x, actual_x, char_offset, utf8_test + char_offset); try(rufl_split("NewHall", rufl_WEIGHT_400, 240, utf8_test, sizeof utf8_test - 1, x, &char_offset, &actual_x), "rufl_split"); printf("split: %i -> %i %zi \"%s\"\n", x, actual_x, char_offset, utf8_test + char_offset); } try(rufl_decompose_glyph("Homerton", rufl_WEIGHT_400, 1280, "A", 1, &funcs, 0), "rufl_decompose_glyph"); try(rufl_paint_callback("NewHall", rufl_WEIGHT_400, 240, utf8_test, sizeof utf8_test - 1, 1200, 1000, callback, 0), "rufl_paint_callback"); try(rufl_font_bbox("NewHall", rufl_WEIGHT_400, 240, bbox), "rufl_font_bbox"); printf("bbox: %i %i %i %i\n", bbox[0], bbox[1], bbox[2], bbox[3]); rufl_quit(); return 0; } void try(rufl_code code, const char *context) { if (code == rufl_OK) return; else if (code == rufl_OUT_OF_MEMORY) printf("error: %s: out of memory\n", context); else if (code == rufl_FONT_MANAGER_ERROR) printf("error: %s: Font Manager error %x %s\n", context, rufl_fm_error->errnum, rufl_fm_error->errmess); else if (code == rufl_FONT_NOT_FOUND) printf("error: %s: font not found\n", context); else if (code == rufl_IO_ERROR) printf("error: %s: io error: %i %s\n", context, errno, strerror(errno)); else if (code == rufl_IO_EOF) printf("error: %s: eof\n", context); else printf("error: %s: unknown error\n", context); rufl_quit(); exit(1); } int move_to(os_coord *to, void *user) { (void) user; printf("Move to (%d,%d)\n", to->x, to->y); return 0; } int line_to(os_coord *to, void *user) { (void) user; printf("Line to (%d,%d)\n", to->x, to->y); return 0; } int cubic_to(os_coord *control1, os_coord *control2, os_coord *to, void *user) { (void) user; printf("Bezier to (%d,%d),(%d,%d),(%d,%d)\n", control1->x, control1->y, control2->x, control2->y, to->x, to->y); return 0; } void callback(void *context, const char *font_name, unsigned int font_size, const char *s8, unsigned short *s16, unsigned int n, int x, int y) { (void) context; printf("callback: \"%s\", %u, ", font_name, font_size); if (s8) printf("s8 \"%.*s\" ", n, s8); else { printf("s16 \""); for (unsigned int i = 0; i != n; i++) printf("%x ", (unsigned int) s16[i]); printf("\" "); } printf("%i %i\n", x, y); }
/** * Find where to split a string to make it fit a width. * * \param fstyle style for this text * \param string UTF-8 string to measure * \param length length of string, in bytes * \param x width available * \param char_offset updated to offset in string of actual_x, [1..length] * \param actual_x updated to x coordinate of character closest to x * \return true on success, false on error and error reported * * On exit, char_offset indicates first character after split point. * * Note: char_offset of 0 should never be returned. * * Returns: * char_offset giving split point closest to x, where actual_x <= x * else * char_offset giving split point closest to x, where actual_x > x * * Returning char_offset == length means no split possible */ static nserror ro_font_split(const plot_font_style_t *fstyle, const char *string, size_t length, int x, size_t *char_offset, int *actual_x) { const char *font_family; unsigned int font_size; rufl_style font_style; rufl_code code; nsfont_read_style(fstyle, &font_family, &font_size, &font_style); if (font_size == 0) { *char_offset = 0; *actual_x = 0; return NSERROR_OK; } code = rufl_split(font_family, font_style, font_size, string, length, x * 2, char_offset, actual_x); if (code != rufl_OK) { if (code == rufl_FONT_MANAGER_ERROR) { LOG("rufl_split: rufl_FONT_MANAGER_ERROR: ""0x%x: %s", rufl_fm_error->errnum, rufl_fm_error->errmess); } else { LOG("rufl_split: 0x%x", code); } /* ro_warn_user("MiscError", "font error"); */ *char_offset = 0; *actual_x = 0; return NSERROR_INVALID; } if (*char_offset != length) { /* we found something to split at */ size_t orig = *char_offset; /* ensure a space at <= the split point we found */ while (*char_offset && string[*char_offset] != ' ') { (*char_offset)--; } /* nothing valid found <= split point, advance to next space */ if (*char_offset == 0) { *char_offset = orig; while ((*char_offset != length) && (string[*char_offset] != ' ')) { (*char_offset)++; } } } code = rufl_width(font_family, font_style, font_size, string, *char_offset, actual_x); if (code != rufl_OK) { if (code == rufl_FONT_MANAGER_ERROR) { LOG("rufl_width: rufl_FONT_MANAGER_ERROR: 0x%x: %s", rufl_fm_error->errnum, rufl_fm_error->errmess); } else { LOG("rufl_width: 0x%x", code); } /* ro_warn_user("MiscError", "font error"); */ *char_offset = 0; *actual_x = 0; return NSERROR_INVALID; } *actual_x /= 2; return NSERROR_OK; }
/** * Set the caret's position * * \param self Text area * \param caret 0-based character index to place caret at */ void ro_textarea_set_caret(uintptr_t self, unsigned int caret) { struct text_area *ta; size_t c_len, b_off; unsigned int i; size_t index; int x; os_coord os_line_height; rufl_code code; os_error *error; ta = (struct text_area *)self; if (!ta || ta->magic != MAGIC) { LOG(("magic doesn't match")); return; } c_len = utf8_length(ta->text); if (caret > c_len) caret = c_len; /* Find byte offset of caret position */ for (b_off = 0; caret > 0; caret--) b_off = utf8_next(ta->text, ta->text_len, b_off); /* Now find line in which byte offset appears */ for (i = 0; i < ta->line_count - 1; i++) if (ta->lines[i + 1].b_start > b_off) break; ta->caret_pos.line = i; /* Now calculate the char. offset of the caret in this line */ for (c_len = 0, ta->caret_pos.char_off = 0; c_len < b_off - ta->lines[i].b_start; c_len = utf8_next(ta->text + ta->lines[i].b_start, ta->lines[i].b_length, c_len)) ta->caret_pos.char_off++; /* Finally, redraw the WIMP caret */ index = ro_textarea_get_caret(self); os_line_height.x = 0; os_line_height.y = (int)((float)(ta->line_height - ta->line_spacing) * 0.62) + 1; ro_convert_pixels_to_os_units(&os_line_height, (os_mode)-1); for (b_off = 0; index-- > 0; b_off = utf8_next(ta->text, ta->text_len, b_off)) ; /* do nothing */ code = rufl_width(ta->font_family, ta->font_style, ta->font_size, ta->text + ta->lines[ta->caret_pos.line].b_start, b_off - ta->lines[ta->caret_pos.line].b_start, &x); if (code != rufl_OK) { if (code == rufl_FONT_MANAGER_ERROR) LOG(("rufl_width: 0x%x: %s", rufl_fm_error->errnum, rufl_fm_error->errmess)); else LOG(("rufl_width: 0x%x", code)); return; } error = xwimp_set_caret_position(ta->window, -1, x + MARGIN_LEFT, -((ta->caret_pos.line + 1) * ta->line_height) - ta->line_height / 4 + ta->line_spacing, os_line_height.y, -1); if (error) { LOG(("xwimp_set_caret_position: 0x%x: %s", error->errnum, error->errmess)); return; } }
void pencil_save_pass1(struct pencil_save_context *context, struct pencil_item *item, unsigned int depth) { rufl_code code; struct pencil_item *child; assert(item); /* Initialise item bounding box */ item->bbox.x0 = INT_MAX; item->bbox.y0 = INT_MAX; item->bbox.x1 = INT_MIN; item->bbox.y1 = INT_MIN; for (child = item->children; child; child = child->next) { pencil_save_pass1(context, child, depth + 1); if (context->code != pencil_OK) return; /* Update item bounding box to include child */ if (child->bbox.x0 < item->bbox.x0) item->bbox.x0 = child->bbox.x0; if (child->bbox.y0 < item->bbox.y0) item->bbox.y0 = child->bbox.y0; if (child->bbox.x1 > item->bbox.x1) item->bbox.x1 = child->bbox.x1; if (child->bbox.y1 > item->bbox.y1) item->bbox.y1 = child->bbox.y1; } switch (item->type) { case pencil_GROUP: if (!item->children || MAX_DEPTH <= depth || (item->bbox.x0 == INT_MAX && item->bbox.y0 == INT_MAX && item->bbox.x1 == INT_MIN && item->bbox.y1 == INT_MIN)) break; context->size += 36; break; case pencil_TEXT: { int bbox[4]; int width; code = rufl_paint_callback(item->font_family, item->font_style, item->font_size, item->text, strlen(item->text), item->x, item->y, pencil_save_pass1_text_callback, context); if (code != rufl_OK) context->code = code; if (context->code != pencil_OK) return; code = rufl_font_bbox(item->font_family, item->font_style, item->font_size, bbox); if (code != rufl_OK) context->code = code; if (context->code != pencil_OK) return; code = rufl_width(item->font_family, item->font_style, item->font_size, item->text, strlen(item->text), &width); if (code != rufl_OK) context->code = code; if (context->code != pencil_OK) return; item->bbox.x0 = item->x * 256; item->bbox.y0 = item->y * 256; item->bbox.x1 = (item->x + width) * 256; item->bbox.y1 = (item->y + (bbox[3] - bbox[1])) * 256; } break; case pencil_PATH: context->size += 24 + 16 + item->path_size * 4; if (item->pattern != pencil_SOLID) context->size += 12; /* Calculate bounding box */ for (unsigned int i = 0; i != item->path_size; ) { switch (item->path[i]) { case 0: case 5: i++; break; case 2: case 6: case 8: { int points = item->path[i++] == 6 ? 3 : 1; for (; points > 0; points--) { int x = item->path[i++] * 256; int y = item->path[i++] * 256; if (x < item->bbox.x0) item->bbox.x0 = x; if (y < item->bbox.y0) item->bbox.y0 = y; if (x > item->bbox.x1) item->bbox.x1 = x; if (y > item->bbox.y1) item->bbox.y1 = y; } } break; default: assert(0); } } break; case pencil_SPRITE: context->size += 24 + ((const osspriteop_header *) item->sprite)->size; item->bbox.x0 = item->x * 256; item->bbox.y0 = item->y * 256; item->bbox.x1 = (item->x + item->width) * 256; item->bbox.y1 = (item->y + item->height) * 256; break; default: assert(0); } /* Update global bounding box */ if (item->bbox.x0 < context->bbox.x0) context->bbox.x0 = item->bbox.x0; if (item->bbox.y0 < context->bbox.y0) context->bbox.y0 = item->bbox.y0; if (item->bbox.x1 > context->bbox.x1) context->bbox.x1 = item->bbox.x1; if (item->bbox.y1 > context->bbox.y1) context->bbox.y1 = item->bbox.y1; }