static bool nsfont_position_in_string(const plot_font_style_t *fstyle, const char *string, size_t length, int x, size_t *char_offset, int *actual_x) { uint32_t ucs4; size_t nxtchr = 0; FT_Glyph glyph; int prev_x = 0; *actual_x = 0; while (nxtchr < length) { ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr); glyph = fb_getglyph(fstyle, ucs4); if (glyph == NULL) continue; *actual_x += glyph->advance.x >> 16; if (*actual_x > x) break; prev_x = *actual_x; nxtchr = utf8_next(string, length, nxtchr); } /* choose nearest of previous and last x */ if (abs(*actual_x - x) > abs(prev_x - x)) *actual_x = prev_x; *char_offset = nxtchr; return true; }
static utf8_convert_ret utf8_convert_html_chunk(iconv_t cd, const char *chunk, size_t inlen, char **out, size_t *outlen) { size_t ret, esclen; uint32_t ucs4; char *pescape, escape[11]; while (inlen > 0) { ret = iconv(cd, (void *) &chunk, &inlen, (void *) out, outlen); if (ret != (size_t) -1) break; if (errno != EILSEQ) return UTF8_CONVERT_NOMEM; ucs4 = utf8_to_ucs4(chunk, inlen); esclen = snprintf(escape, sizeof(escape), "&#x%06x;", ucs4); pescape = escape; ret = iconv(cd, (void *) &pescape, &esclen, (void *) out, outlen); if (ret == (size_t) -1) return UTF8_CONVERT_NOMEM; esclen = utf8_next(chunk, inlen, 0); chunk += esclen; inlen -= esclen; } return UTF8_CONVERT_OK; }
static bool match_star(unsigned c, int flags, struct utf8_iter expr, struct utf8_iter text, char **end) { for (; !match_here(expr, text, end); utf8_next(&text)) if (text.chr == 0 || !match_char(c, flags, &text)) return false; return true; }
/* Finds the position of the first candidate line break in the 今日は utf8-encoded text str. Returns -1 if no candidate could be found. Returns -2 if the string is empty. */ int laytext_find_first_break(char * str, float max_width, laytext_callback * callback, void * extra) { int length = 0; int total_length = 0; long character; float width, delta_width; float height; int candidate = -1; char * index = str; width = 0.0; while (index) { callback(index, total_length, extra, &delta_width, &height); width = delta_width; if((width - max_width) > 0.5) { if (candidate == -1) { /* No break candidate, break before this character in stead. */ return index - str - 1; } else { /* break at candidate. */ return candidate; } } if ((*index) == ' ') { candidate = index - str; } /* If a newline the break is here. */ if((*index) == '\n') { return index - str; } length = utf8_next(index, &index, &character); total_length += length; } /* If we get here the strin is short, no break needed. Negative indicates * this. */ return -1; }
void unicode_init(struct unicode_parser* parser, const char* str) { memset(&parser->prev, 0, sizeof(parser->prev)); utf8_init(&parser->current, str); parser->next = parser->current; utf8_next(&parser->next); parser->unicode = parser->current.unicode; // unicode_decode(&parser); }
/** * Calculate the length (in bytes) of a bounded UTF-8 string * * \param s The string * \param l Maximum length of input (in bytes) * \param c Maximum number of characters to measure * \return Length of string, in bytes */ size_t utf8_bounded_byte_length(const char *s, size_t l, size_t c) { size_t len = 0; while (len < l && c-- > 0) len = utf8_next(s, l, len); return len; }
static bool match_plus(unsigned c, int flags, struct utf8_iter expr, struct utf8_iter text, char **end) { if (!match_char(c, flags, &text)) return false; do utf8_next(&text); while (text.chr != 0 && match_char(c, flags, &text)); return match_here(expr, text, end); }
char *match(char *expr_, char *text_, char **end) { struct utf8_iter expr; struct utf8_iter text; utf8_iter(&expr, expr_); utf8_iter(&text, text_); utf8_next(&text); if (utf8_next(&expr) == '^') { utf8_next(&expr); return match_here(expr, text, end) ? text.head : NULL; } do if (match_here(expr, text, end)) return text.head; while (text.chr != 0 && utf8_next(&text)); return NULL; }
char unicode_next(struct unicode_parser* parser) { if (utf8_current(&parser->current)) { // Try to move to next symbol parser->prev = parser->current; parser->current = parser->next; utf8_next(&parser->next); unicode_decode(parser); return 1; } return 0; }
size_t append_utf8_char(String * string, const char * mbs) { /* Copy exactly one multi-byte character to buf */ char buf[10]; size_t n = utf8_next(mbs); assert(n<10, "Multi-byte character is too long!"); strncpy(buf, mbs, n); buf[n] = 0; append_string(string, "%s", buf); return n; }
static void glyphstring_create(FT_Face face, Text *text, FT_Glyph *glyph_string, FT_Vector *pos) { const uint8_t *string = text->string; FT_Bool has_kerning; FT_UInt glyph_index, previous; FT_Vector pen, delta; uint32_t charcode; int i; has_kerning = FT_HAS_KERNING(face); previous = 0; i = 0; pen.x = pen.y = 0; while (string[0] != '\0') { charcode = utf8_next(&string); glyph_index = FT_Get_Char_Index(face, charcode); if (has_kerning && previous && glyph_index) FT_Get_Kerning(face, previous, glyph_index, FT_KERNING_DEFAULT, &delta); else delta.x = 0; if (glyph_index == 0) log_err("Glyph for character U+%X missing\n", charcode); if (FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER) != 0) { log_err("Error loading glyph for character U+%X\n", charcode); continue; } if (FT_Get_Glyph(face->glyph, &glyph_string[i]) != 0) { log_err("Error copying glyph for character U+%X\n", charcode); continue; } pen.x += delta.x; pos[i] = pen; pen.x += face->glyph->advance.x; pen.y += face->glyph->advance.y; previous = glyph_index; i++; } text->num_glyphs = i; }
static void nsgtk_tree_window_input_method_commit(GtkIMContext *ctx, const gchar *str, gpointer data) { struct nsgtk_treeview *tw = (struct nsgtk_treeview *) data; size_t len = strlen(str), offset = 0; while (offset < len) { uint32_t nskey = utf8_to_ucs4(str + offset, len - offset); tree_keypress(tw->tree, nskey); offset = utf8_next(str, len, offset); } }
static bool match_here(struct utf8_iter expr, struct utf8_iter text, char **end) { unsigned pre = expr.chr; int flags = 0; if (pre == 0) { if (end != NULL) *end = text.head; return true; } if (pre == '~') { pre = utf8_next(&expr); flags |= NEGATE; } if (pre == '%') { pre = utf8_next(&expr); switch (pre) { case 'a': /* alpha */ case 'd': /* digit */ case 'f': /* float */ case 'g': /* graphic */ case 'n': /* newline */ case 's': /* space */ case 'x': /* hex */ case '?': /* anything */ flags |= CLASS; } } utf8_next(&expr); if (expr.chr == '*') { utf8_next(&expr); return match_star(pre, flags, expr, text, end); } else if (expr.chr == '+') { utf8_next(&expr); return match_plus(pre, flags, expr, text, end); } if (pre == '$' && expr.chr == 0) { if (end != NULL) *end = text.head; return text.chr == 0; } if (text.chr != 0 && match_char(pre, flags, &text)) { utf8_next(&text); return match_here(expr, text, end); } return false; }
static bool framebuffer_plot_text(int x, int y, const char *text, size_t length, const plot_font_style_t *fstyle) { uint32_t ucs4; size_t nxtchr = 0; FT_Glyph glyph; FT_BitmapGlyph bglyph; nsfb_bbox_t loc; while (nxtchr < length) { ucs4 = utf8_to_ucs4(text + nxtchr, length - nxtchr); nxtchr = utf8_next(text, length, nxtchr); glyph = fb_getglyph(fstyle, ucs4); if (glyph == NULL) continue; if (glyph->format == FT_GLYPH_FORMAT_BITMAP) { bglyph = (FT_BitmapGlyph)glyph; loc.x0 = x + bglyph->left; loc.y0 = y - bglyph->top; loc.x1 = loc.x0 + bglyph->bitmap.width; loc.y1 = loc.y0 + bglyph->bitmap.rows; /* now, draw to our target surface */ if (bglyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { nsfb_plot_glyph1(nsfb, &loc, bglyph->bitmap.buffer, bglyph->bitmap.pitch, fstyle->foreground); } else { nsfb_plot_glyph8(nsfb, &loc, bglyph->bitmap.buffer, bglyph->bitmap.pitch, fstyle->foreground); } } x += glyph->advance.x >> 16; } return true; }
/** * @ingroup hanjadictionary * @brief 한자 사전에서 뒷부분이 매치되는 키를 가진 엔트리를 찾는 함수 * @param table 한자 사전 object * @param key 찾을 키, UTF-8 인코딩 * @return 찾은 결과를 HanjaList object로 리턴한다. 찾은 것이 없거나 에러가 * 있으면 NULL을 리턴한다. * * @a key 값과 같거나 뒷부분이 같은 키를 가진 엔트리를 검색한다. * 그리고 key를 앞에서부터 한자씩 줄여가면서 검색을 계속한다. * 예로 들면 "삼국사기"를 검색하면 "삼국사기", "국사기", "사기", "기"를 * 각각 모두 검색한다. * 리턴된 결과는 다 사용하고 나면 반드시 hanja_list_delete() 함수로 free해야 * 한다. */ HanjaList* hanja_table_match_suffix(const HanjaTable* table, const char *key) { const char* p; HanjaList* ret = NULL; if (key == NULL || key[0] == '\0' || table == NULL) return NULL; p = key; while (p[0] != '\0') { hanja_table_match(table, p, &ret); p = utf8_next(p); } return ret; }
/** * Insert text into the text area * * \param self Text area * \param index 0-based character index to insert at * \param text UTF-8 text to insert */ void ro_textarea_insert_text(uintptr_t self, unsigned int index, const char *text) { struct text_area *ta; unsigned int b_len = strlen(text); size_t b_off, c_len; ta = (struct text_area *)self; if (!ta || ta->magic != MAGIC) { LOG(("magic doesn't match")); return; } c_len = utf8_length(ta->text); /* Find insertion point */ if (index > c_len) index = c_len; for (b_off = 0; index-- > 0; b_off = utf8_next(ta->text, ta->text_len, b_off)) ; /* do nothing */ if (b_len + ta->text_len >= ta->text_alloc) { char *temp = realloc(ta->text, b_len + ta->text_len + 64); if (!temp) { LOG(("realloc failed")); return; } ta->text = temp; ta->text_alloc = b_len + ta->text_len + 64; } /* Shift text following up */ memmove(ta->text + b_off + b_len, ta->text + b_off, ta->text_len - b_off); /* Insert new text */ memcpy(ta->text + b_off, text, b_len); ta->text_len += b_len; /** \todo calculate line to reflow from */ ro_textarea_reflow(ta, 0); }
/** * Get the caret's position * * \param self Text area * \return 0-based character index of caret location, or -1 on error */ unsigned int ro_textarea_get_caret(uintptr_t self) { struct text_area *ta; size_t c_off = 0, b_off; ta = (struct text_area *)self; if (!ta || ta->magic != MAGIC) { LOG(("magic doesn't match")); return -1; } /* Calculate character offset of this line's start */ for (b_off = 0; b_off < ta->lines[ta->caret_pos.line].b_start; b_off = utf8_next(ta->text, ta->text_len, b_off)) c_off++; return c_off + ta->caret_pos.char_off; }
// UTF-8 -> ISO-8859-1 // コード範囲外の値は、下8ビッドだけ取る static const char *utf8_to_8bit(const char *src) { if (src != NULL) { int len = strlen(src); const char *p = src; const char *end = p + len; char *c_buf = Mem_get(&fg->st_mem, len + 1); int i = 0; while (p < end) { int ch = utf8_codepoint_at(p); c_buf[i++] = ch & 0xFF; utf8_next(&p, end); } c_buf[i] = '\0'; return c_buf; } else { return NULL; } }
static bool nsfont_split(const plot_font_style_t *fstyle, const char *string, size_t length, int x, size_t *char_offset, int *actual_x) { uint32_t ucs4; size_t nxtchr = 0; int last_space_x = 0; int last_space_idx = 0; FT_Glyph glyph; *actual_x = 0; while (nxtchr < length) { ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr); glyph = fb_getglyph(fstyle, ucs4); if (glyph == NULL) continue; if (ucs4 == 0x20) { last_space_x = *actual_x; last_space_idx = nxtchr; } *actual_x += glyph->advance.x >> 16; if (*actual_x > x) { /* string has exceeded available width return previous * space */ *actual_x = last_space_x; *char_offset = last_space_idx; return true; } nxtchr = utf8_next(string, length, nxtchr); } *char_offset = nxtchr; return true; }
/** * Measure the width of a string. * * \param fstyle 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 bool nsfont_width(const plot_font_style_t *fstyle, const char *string, size_t length, int *width) { uint32_t ucs4; size_t nxtchr = 0; FT_Glyph glyph; *width = 0; while (nxtchr < length) { ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr); nxtchr = utf8_next(string, length, nxtchr); glyph = fb_getglyph(fstyle, ucs4); if (glyph == NULL) continue; *width += glyph->advance.x >> 16; } return true; }
//#else static bool framebuffer_plot_text_internal(int x, int y, const char *text, size_t length, const plot_font_style_t *fstyle) { enum fb_font_style style = fb_get_font_style(fstyle); int size = fb_get_font_size(fstyle); const uint8_t *chrp; size_t nxtchr = 0; nsfb_bbox_t loc; uint32_t ucs4; int p = FB_FONT_PITCH * size; int w = FB_FONT_WIDTH * size; int h = FB_FONT_HEIGHT * size; y -= ((h * 3) / 4); /* the coord is the bottom-left of the pixels offset by 1 to make * it work since fb coords are the top-left of pixels */ y += 1; while (nxtchr < length) { ucs4 = utf8_to_ucs4(text + nxtchr, length - nxtchr); nxtchr = utf8_next(text, length, nxtchr); if (!codepoint_displayable(ucs4)) continue; loc.x0 = x; loc.y0 = y; loc.x1 = loc.x0 + w; loc.y1 = loc.y0 + h; chrp = fb_get_glyph_internal(ucs4, style, size); nsfb_plot_glyph1(nsfb, &loc, chrp, p, fstyle->foreground); x += w; } return true; }
/** * Convert a UTF-8 encoded string into the system local encoding * * \param string The string to convert * \param len The length (in bytes) of the string, or 0 * \param result Pointer to location in which to store result * \return An nserror code */ nserror utf8_to_local_encoding(const char *string, size_t len, char **result) { os_error *error; int alphabet, i; size_t off, prev_off; char *temp, *cur_pos; const char *enc; nserror err; assert(string); assert(result); /* get length, if necessary */ if (len == 0) len = strlen(string); /* read system alphabet */ error = xosbyte1(osbyte_ALPHABET_NUMBER, 127, 0, &alphabet); if (error) alphabet = territory_ALPHABET_LATIN1; /* UTF-8 -> simply copy string */ if (alphabet == 111 /* UTF-8 */) { *result = strndup(string, len); return NSERROR_OK; } /* get encoding name */ enc = (alphabet <= CONT_ENC_END ? localencodings[alphabet - 100] : (alphabet == 120 ? localencodings[CONT_ENC_END - 100 + 1] : localencodings[0])); /* create output buffer */ *(result) = malloc(len + 1); if (!(*result)) return NSERROR_NOMEM; *(*result) = '\0'; prev_off = 0; cur_pos = (*result); /* Iterate over string, converting input between unconvertable * characters and inserting appropriate output for characters * that iconv can't handle. */ for (off = 0; off < len; off = utf8_next(string, len, off)) { if (string[off] != 0xE2 && string[off] != 0xC5 && string[off] != 0xEF) continue; for (i = 0; i != NOF_ELEMENTS(special_chars); i++) { if (strncmp(string + off, special_chars[i].utf, special_chars[i].len) != 0) continue; /* 0 length has a special meaning to utf8_to_enc */ if (off - prev_off > 0) { err = utf8_to_enc(string + prev_off, enc, off - prev_off, &temp); if (err != NSERROR_OK) { assert(err != NSERROR_BAD_ENCODING); free(*result); return NSERROR_NOMEM; } strcat(cur_pos, temp); cur_pos += strlen(temp); free(temp); } *cur_pos = special_chars[i].local; *(++cur_pos) = '\0'; prev_off = off + special_chars[i].len; } } /* handle last chunk * NB. 0 length has a special meaning to utf8_to_enc */ if (prev_off < len) { err = utf8_to_enc(string + prev_off, enc, len - prev_off, &temp); if (err != NSERROR_OK) { assert(err != NSERROR_BAD_ENCODING); free(*result); return NSERROR_NOMEM; } strcat(cur_pos, temp); free(temp); } return NSERROR_OK; }
size_t text_renderer_render(text_renderer_p renderer, int32_t font_handle, char* text, size_t x, size_t y, float* buffer_ptr, size_t buffer_size) { text_renderer_font_p font = hash_get_ptr(renderer->fonts, font_handle); if (!font) return 0; // The current position is the baseline of the text so start one line down // from the position. Otherwise we would render above it. size_t pos_x = x, pos_y = y + (font->face->size->metrics.height / 64); float* p = buffer_ptr; FT_UInt glyph_index = 0, prev_glyph_index = 0; glBindTexture(GL_TEXTURE_RECTANGLE, renderer->texture); // Lookup the size of our texture (needed by find_free_cell_or_revoke_unused_cell() for some checks) GLint texture_width = 0, texture_height = 0; glGetTexLevelParameteriv(GL_TEXTURE_RECTANGLE, 0, GL_TEXTURE_WIDTH, &texture_width); glGetTexLevelParameteriv(GL_TEXTURE_RECTANGLE, 0, GL_TEXTURE_HEIGHT, &texture_height); GLint unpack_alignment = 0; glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_alignment); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); for(utf8_iterator_t it = utf8_first(text); it.code_point != 0; it = utf8_next(it)) { // Handle line break. if (it.code_point == '\n') { pos_y += font->face->size->metrics.height / 64; pos_x = x; // Don't do kerning at the next character after the line break. prev_glyph_index = 0; continue; } // Look if this glyph is present in the texture. If so this font has a cell reference // for this code point. text_renderer_cell_ref_p cell_ref = hash_get_ptr(font->cell_refs, it.code_point); // Glyph not rendered yet, try to render it if (!cell_ref) { glyph_index = FT_Get_Char_Index(font->face, it.code_point); if (glyph_index == 0) goto render_error_glyph; FT_Error error = FT_Load_Glyph(font->face, glyph_index, FT_LOAD_RENDER); if (error) goto render_error_glyph; // Look for a free cell to store the rendered glyph uint32_t gw = font->face->glyph->bitmap.width, gh = font->face->glyph->bitmap.rows; size_t line_idx = 0, cell_idx = 0; text_renderer_cell_p free_cell = find_free_cell_or_revoke_unused_cell(renderer, font, gw, gh, texture_width, texture_height, &line_idx, &cell_idx); if (free_cell == NULL) goto render_error_glyph; free_cell->glyph_index = glyph_index; free_cell->hori_bearing_x = font->face->glyph->metrics.horiBearingX / 64; free_cell->hori_bearing_y = font->face->glyph->metrics.horiBearingY / 64; free_cell->hori_advance = font->face->glyph->metrics.horiAdvance / 64; /* printf("%c: %2ux%2u %2u bytes, pitch %u, x: %u, y: %u hori_bearing: %2u/%2u, adv: %2u\n", it.code_point, gw, gh, gw*gh, font->face->glyph->bitmap.pitch, free_cell->x, free_cell->y, free_cell->hori_bearing_x, free_cell->hori_bearing_y, free_cell->hori_advance); */ glPixelStorei(GL_UNPACK_ROW_LENGTH, font->face->glyph->bitmap.pitch); glTexSubImage2D(GL_TEXTURE_RECTANGLE, 0, free_cell->x, free_cell->y, gw, gh, GL_RED, GL_UNSIGNED_BYTE, font->face->glyph->bitmap.buffer); cell_ref = hash_put_ptr(font->cell_refs, it.code_point); cell_ref->line_idx = line_idx; cell_ref->cell_idx = cell_idx; } // Glyph rendered successfully, do kerning and generate vertices for it text_renderer_line_t line = array_elem(renderer->lines, text_renderer_line_t, cell_ref->line_idx); text_renderer_cell_t cell = array_elem(line.cells, text_renderer_cell_t, cell_ref->cell_idx); if (cell.glyph_index && prev_glyph_index) { FT_Vector delta; FT_Get_Kerning(font->face, prev_glyph_index, glyph_index, FT_KERNING_DEFAULT, &delta); pos_x += delta.x / 64; } // We have the texture coordinates of the glyph, generate the vertex buffer if ( (p + 6*4 - buffer_ptr) * sizeof(float) < buffer_size ) { float w = cell.width, h = line.height; float cx = pos_x + cell.hori_bearing_x; float cy = pos_y - cell.hori_bearing_y; float tl_x = cx + 0, tl_y = cy + 0, tl_u = cell.x, tl_v = cell.y; float tr_x = cx + w, tr_y = cy + 0, tr_u = cell.x + w, tr_v = cell.y; float bl_x = cx + 0, bl_y = cy + h, bl_u = cell.x, bl_v = cell.y + h; float br_x = cx + w, br_y = cy + h, br_u = cell.x + w, br_v = cell.y + h; *(p++) = tl_x; *(p++) = tl_y; *(p++) = tl_u; *(p++) = tl_v; *(p++) = tr_x; *(p++) = tr_y; *(p++) = tr_u; *(p++) = tr_v; *(p++) = bl_x; *(p++) = bl_y; *(p++) = bl_u; *(p++) = bl_v; *(p++) = tr_x; *(p++) = tr_y; *(p++) = tr_u; *(p++) = tr_v; *(p++) = br_x; *(p++) = br_y; *(p++) = br_u; *(p++) = br_v; *(p++) = bl_x; *(p++) = bl_y; *(p++) = bl_u; *(p++) = bl_v; pos_x += cell.hori_advance; } prev_glyph_index = glyph_index; continue; render_error_glyph: // For now just output nothing when we fail to render a glyph. // TODO: Figure out how to render a kind of error glyph. prev_glyph_index = glyph_index; continue; } glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_alignment); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glBindTexture(GL_TEXTURE_RECTANGLE, 0); return (p - buffer_ptr) * sizeof(float); }
void unicode_decode(struct unicode_parser* parser) { unsigned int prev = utf8_current(&parser->prev); unsigned int current = utf8_current(&parser->current); unsigned int next = utf8_current(&parser->next); unsigned char prev_connects = 0; // Can the previous character connect to me unsigned char next_connects = 0; // Can the previous character connect to me unsigned char i_connect_left = 1; // Can I connect left unsigned char i_connect_right = 1; // Can I connect right int idx; int i; char found = 0; parser->unicode = current; return; // TODO This only applies to arabic chars // previous and next chars only connect if they are not whitespace or start/end of line prev_connects = connects_prev_next(prev, -1); next_connects = connects_prev_next(next, 1); // Find Ligatures for (idx=0; idx<sizeof(ligatures) / sizeof(ligatures[0]); idx+=3) { if (ligatures[idx] == current && ligatures[idx+1] == next) { utf8_next(&parser->next); parser->unicode = ligatures[idx+2]; return; } } for (idx=0; idx<sizeof(contextual_forms) / sizeof(contextual_forms[0]); idx+=2) { if (contextual_forms[idx] == current) { found = 1; break; } } if (found) { // Characters which do only connect left for (i=0; i<sizeof(contextual_forms_nomiddle_noend) / sizeof(contextual_forms_nomiddle_noend[0]); i++) { if (current == contextual_forms_nomiddle_noend[i]) { i_connect_right = 0; } if (prev == contextual_forms_nomiddle_noend[i]) { prev_connects = 0; } } // If only previous an I can connect if (prev_connects && i_connect_left && !(i_connect_right && next_connects)) current = contextual_forms[idx+1] + 1; // If only next an I can connect if (!(prev_connects && i_connect_left) && i_connect_right && next_connects) current = contextual_forms[idx+1] + 2; // Both I and previous and next char can connect if (prev_connects && i_connect_left && i_connect_right && next_connects) current = contextual_forms[idx+1] + 3; } parser->unicode = current; }
/** * Set the caret's position * * \param self Text area * \param x X position of caret on the screen * \param y Y position of caret on the screen */ void ro_textarea_set_caret_xy(uintptr_t self, int x, int y) { struct text_area *ta; wimp_window_state state; size_t b_off, c_off, temp; int line; 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; } if (ta->flags & TEXTAREA_READONLY) return; 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); state.w = ta->window; error = xwimp_get_window_state(&state); if (error) { LOG(("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess)); return; } x = x - (state.visible.x0 - state.xscroll) - MARGIN_LEFT; y = (state.visible.y1 - state.yscroll) - y; line = y / ta->line_height; if (line < 0) line = 0; if (ta->line_count - 1 < (unsigned)line) line = ta->line_count - 1; code = rufl_x_to_offset(ta->font_family, ta->font_style, ta->font_size, ta->text + ta->lines[line].b_start, ta->lines[line].b_length, x, &b_off, &x); if (code != rufl_OK) { if (code == rufl_FONT_MANAGER_ERROR) LOG(("rufl_x_to_offset: 0x%x: %s", rufl_fm_error->errnum, rufl_fm_error->errmess)); else LOG(("rufl_x_to_offset: 0x%x", code)); return; } for (temp = 0, c_off = 0; temp < b_off + ta->lines[line].b_start; temp = utf8_next(ta->text, ta->text_len, temp)) c_off++; ro_textarea_set_caret((uintptr_t)ta, c_off); }
/** * 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; } }
/** * Replace text in a text area * * \param self Text area * \param start Start character index of replaced section (inclusive) * \param end End character index of replaced section (exclusive) * \param text UTF-8 text to insert */ void ro_textarea_replace_text(uintptr_t self, unsigned int start, unsigned int end, const char *text) { struct text_area *ta; int b_len = strlen(text); size_t b_start, b_end, c_len, diff; ta = (struct text_area *)self; if (!ta || ta->magic != MAGIC) { LOG(("magic doesn't match")); return; } c_len = utf8_length(ta->text); if (start > c_len) start = c_len; if (end > c_len) end = c_len; if (start == end) return ro_textarea_insert_text(self, start, text); if (start > end) { int temp = end; end = start; start = temp; } diff = end - start; for (b_start = 0; start-- > 0; b_start = utf8_next(ta->text, ta->text_len, b_start)) ; /* do nothing */ for (b_end = b_start; diff-- > 0; b_end = utf8_next(ta->text, ta->text_len, b_end)) ; /* do nothing */ if (b_len + ta->text_len - (b_end - b_start) >= ta->text_alloc) { char *temp = realloc(ta->text, b_len + ta->text_len - (b_end - b_start) + 64); if (!temp) { LOG(("realloc failed")); return; } ta->text = temp; ta->text_alloc = b_len + ta->text_len - (b_end - b_start) + 64; } /* Shift text following to new position */ memmove(ta->text + b_start + b_len, ta->text + b_end, ta->text_len - b_end); /* Insert new text */ memcpy(ta->text + b_start, text, b_len); ta->text_len += b_len - (b_end - b_start); /** \todo calculate line to reflow from */ ro_textarea_reflow(ta, 0); }
/** * 文字コードを変換しながらStreamに出力する * v : stream (optional,sb==NULLの場合必須) * sb : StrBuf : (optional) */ static int stream_write_sub_s(Value v, StrBuf *sb, const char *s_p, int s_size, RefTextIO *tio) { if (s_size < 0) { s_size = strlen(s_p); } // TODO:未検証 if (v != VALUE_NULL) { RefBytesIO *mb = Value_vp(v); if (mb->rh.type == fs->cls_bytesio) { sb = &mb->buf; } } if (sb != NULL) { // sprintfの場合tio==NULL if (tio == NULL || tio->cs->utf8) { if (!StrBuf_add(sb, s_p, s_size)) { return FALSE; } return TRUE; } else { if (tio->out.ic == (void*)-1) { if (!IconvIO_open(&tio->out, fs->cs_utf8, tio->cs, tio->trans ? "?" : NULL)) { return FALSE; } } if (!IconvIO_conv(&tio->out, sb, s_p, s_size, TRUE, TRUE)) { return FALSE; } return TRUE; } } else { if (tio == NULL || tio->cs->utf8) { return stream_write_data(v, s_p, s_size); } else { Ref *r = Value_ref(v); IconvIO *ic = &tio->out; Value *vmb = &r->v[INDEX_WRITE_MEMIO]; RefBytesIO *mb; int max = Value_integral(r->v[INDEX_WRITE_MAX]); if (max == -1) { throw_error_select(THROW_NOT_OPENED_FOR_WRITE); return FALSE; } if (tio->out.ic == (void*)-1) { if (!IconvIO_open(&tio->out, fs->cs_utf8, tio->cs, tio->trans ? "?" : NULL)) { return FALSE; } } if (*vmb == VALUE_NULL) { *vmb = vp_Value(bytesio_new_sub(NULL, BUFFER_SIZE)); max = BUFFER_SIZE; r->v[INDEX_WRITE_MAX] = int32_Value(max); // STDOUTだけ特別扱い if (fs->cgi_mode) { if (r == Value_vp(fg->v_cio)) { send_headers(); } } } mb = Value_vp(*vmb); ic->inbuf = s_p; ic->inbytesleft = s_size; ic->outbuf = mb->buf.p + mb->buf.size; ic->outbytesleft = max - mb->buf.size; for (;;) { switch (IconvIO_next(ic)) { case ICONV_OK: mb->buf.size = ic->outbuf - mb->buf.p; goto BREAK; case ICONV_OUTBUF: mb->buf.size = ic->outbuf - mb->buf.p; if (!stream_flush_sub(v)) { return FALSE; } ic->outbuf = mb->buf.p; ic->outbytesleft = max; break; case ICONV_INVALID: if (tio->trans) { const char *ptr; if (ic->outbytesleft == 0) { mb->buf.size = ic->outbuf - mb->buf.p; if (!stream_flush_sub(v)) { return FALSE; } ic->outbuf = mb->buf.p; ic->outbytesleft = max; } ptr = ic->inbuf; *ic->outbuf++ = '?'; ic->outbytesleft--; // 1文字進める utf8_next(&ptr, ptr + ic->inbytesleft); ic->inbytesleft -= ptr - ic->inbuf; ic->inbuf = ptr; } else { RefCharset *cs = tio->cs; throw_errorf(fs->mod_lang, "CharsetError", "Cannot convert %U to %S", utf8_codepoint_at(ic->inbuf), cs->name); return FALSE; } break; } } BREAK: return TRUE; } } }
/** * Convert a UTF-8 encoded string into a string of the given encoding, * applying HTML escape sequences where necessary. * * \param string String to convert (NUL-terminated) * \param encname Name of encoding to convert to * \param len Length, in bytes, of the input string, or 0 * \param result Pointer to location to receive result * \return Appropriate utf8_convert_ret value */ utf8_convert_ret utf8_to_html(const char *string, const char *encname, size_t len, char **result) { iconv_t cd; const char *in; char *out, *origout; size_t off, prev_off, inlen, outlen, origoutlen, esclen; utf8_convert_ret ret; char *pescape, escape[11]; if (len == 0) len = strlen(string); cd = iconv_open(encname, "UTF-8"); if (cd == (iconv_t) -1) { if (errno == EINVAL) return UTF8_CONVERT_BADENC; /* default to no memory */ return UTF8_CONVERT_NOMEM; } /* Worst case is ASCII -> UCS4, with all characters escaped: * "&#xYYYYYY;", thus each input character may become a string * of 10 UCS4 characters, each 4 bytes in length */ origoutlen = outlen = len * 10 * 4; origout = out = malloc(outlen); if (out == NULL) { iconv_close(cd); return UTF8_CONVERT_NOMEM; } /* Process input in chunks between characters we must escape */ prev_off = off = 0; while (off < len) { /* Must escape '&', '<', and '>' */ if (string[off] == '&' || string[off] == '<' || string[off] == '>') { if (off - prev_off > 0) { /* Emit chunk */ in = string + prev_off; inlen = off - prev_off; ret = utf8_convert_html_chunk(cd, in, inlen, &out, &outlen); if (ret != UTF8_CONVERT_OK) { free(origout); iconv_close(cd); return ret; } } /* Emit mandatory escape */ esclen = snprintf(escape, sizeof(escape), "&#x%06x;", string[off]); pescape = escape; ret = utf8_convert_html_chunk(cd, pescape, esclen, &out, &outlen); if (ret != UTF8_CONVERT_OK) { free(origout); iconv_close(cd); return ret; } prev_off = off = utf8_next(string, len, off); } else { off = utf8_next(string, len, off); } } /* Process final chunk */ if (prev_off < len) { in = string + prev_off; inlen = len - prev_off; ret = utf8_convert_html_chunk(cd, in, inlen, &out, &outlen); if (ret != UTF8_CONVERT_OK) { free(origout); iconv_close(cd); return ret; } } iconv_close(cd); /* Shrink-wrap */ *result = realloc(origout, origoutlen - outlen + 4); if (*result == NULL) { free(origout); return UTF8_CONVERT_NOMEM; } memset(*result + (origoutlen - outlen), 0, 4); return UTF8_CONVERT_OK; }