// encode message from UTF8 LPSTR __cdecl cpp_encodeU(HANDLE context, LPCSTR msg) { pCNTX ptr = get_context_on_id(context); if (!ptr) return NULL; cpp_alloc_pdata(ptr); pSIMDATA p = (pSIMDATA)ptr->pdata; if (!p->KeyX) { ptr->error = ERROR_NO_KEYX; return NULL; } LPSTR szNewMsg = NULL; LPSTR szOldMsg = (LPSTR)msg; if (ptr->features & FEATURES_UTF8) { // utf8 message: encrypt. szNewMsg = cpp_encrypt(ptr, szOldMsg); } else { // utf8 message: convert to ansi and encrypt. LPWSTR wstring = utf8decode(szOldMsg); int wlen = (int)wcslen(wstring) + 1; LPSTR astring = (LPSTR)alloca(wlen); WideCharToMultiByte(CP_ACP, 0, (LPWSTR)szOldMsg, -1, astring, wlen, 0, 0); szNewMsg = cpp_encrypt(ptr, astring); } return szNewMsg; }
// decode message from UTF8 if need, return ANSIzUCS2z LPSTR __cdecl cpp_decode(HANDLE context, LPCSTR szEncMsg) { pCNTX ptr = get_context_on_id(context); if (!ptr) return NULL; cpp_alloc_pdata(ptr); pSIMDATA p = (pSIMDATA)ptr->pdata; if (!p->KeyX) { ptr->error = ERROR_NO_KEYX; return NULL; } LPSTR szNewMsg = NULL; LPSTR szOldMsg = cpp_decrypt(ptr, szEncMsg); if (szOldMsg) { if (ptr->features & FEATURES_UTF8) { // utf8 message: convert to unicode -> ansii LPWSTR wstring = utf8decode(szOldMsg); int wlen = (int)wcslen(wstring) + 1; szNewMsg = (LPSTR)mir_alloc(wlen*(sizeof(WCHAR)+2)); // [email protected] WideCharToMultiByte(CP_ACP, 0, wstring, -1, szNewMsg, wlen, 0, 0); memcpy(szNewMsg + strlen(szNewMsg) + 1, wstring, wlen*sizeof(WCHAR)); // [email protected] } else { // ansi message: convert to unicode int slen = (int)strlen(szOldMsg) + 1; szNewMsg = (LPSTR)mir_alloc(slen*(sizeof(WCHAR)+1)); memcpy(szNewMsg, szOldMsg, slen); WCHAR* wstring = (LPWSTR)alloca(slen*sizeof(WCHAR)); MultiByteToWideChar(CP_ACP, 0, szOldMsg, -1, wstring, slen*sizeof(WCHAR)); memcpy(szNewMsg + slen, wstring, slen*sizeof(WCHAR)); } } replaceStr(ptr->tmp, szNewMsg); return szNewMsg; }
/* * Render paragraph. TODO: fix line break in long text without spaces. */ int32_t osd_paragraph(struct osd_t* osd, char *text, uint32_t text_size, uint32_t x, uint32_t y, uint32_t w, uint32_t h) { uint32_t width; char* iso_text = NULL; char* str; char* tmp; char* space; int text_y = y; int done = 0; if ((!text) || (strlen(text) == 0)) { return 0; } iso_text = malloc(strlen(text) + 1); utf8decode(text, iso_text); str = iso_text; do { width = osd_fontWidth(osd, str, strlen(str)); if (width <= w) { /* We can display the whole line */ graphics_resource_render_text_ext(osd->img, x, text_y, GRAPHICS_RESOURCE_WIDTH, GRAPHICS_RESOURCE_HEIGHT, GRAPHICS_RGBA32(0xff,0xff,0xff,0xff), /* fg */ GRAPHICS_RGBA32(0,0,0,0xc0), /* bg */ str, strlen(str), 40); done = 1; } else { tmp = malloc(strlen(str) + 1); strcpy(tmp, str); while (width > w) { space = strrchr(tmp, ' '); tmp[space-tmp] = '\0'; width = osd_fontWidth(osd, tmp, space - tmp); } graphics_resource_render_text_ext(osd->img, x, text_y, GRAPHICS_RESOURCE_WIDTH, GRAPHICS_RESOURCE_HEIGHT, GRAPHICS_RGBA32(0xff,0xff,0xff,0xff), /* fg */ GRAPHICS_RGBA32(0,0,0,0xc0), /* bg */ tmp, strlen(tmp), 40); text_y += 50; if (text_y > (y + h)) { done = 1; } str += strlen(tmp) + 1; free(tmp); } } while(!done); free(iso_text); return 0; }
size_t utf8chsiz(char *c, size_t clen) { if(clen <= 0) { return 0; } long cp; size_t chsiz = utf8decode(c, &cp, MIN(UTF_SIZ, clen)); return cp != UTF_INVALID ? chsiz : 1; }
/* * UTF8 decode and render text */ void osd_text(struct osd_t* osd, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t fg_color, uint32_t bg_color, char *str) { char* iso_text = NULL; if (str == NULL) { return; } iso_text = malloc(strlen(str) + 1); utf8decode(str, iso_text); graphics_resource_render_text_ext(osd->img, x, y, w, h, fg_color, bg_color, iso_text, strlen(iso_text), 40); free(iso_text); }
int get_text_width(const font_t* font, const char* text) { uint8_t ch_byte; uint32_t cp; uint32_t utf8state; int width = 0; for (;;) { utf8state = UTF8_ACCEPT; while (utf8decode(&utf8state, &cp, ch_byte = *text++) > UTF8_REJECT); if (utf8state == UTF8_REJECT && ch_byte == '\0') --text; // don't eat NUL terminator cp = cp == 0x20AC ? 128 : cp == 0x201A ? 130 : cp == 0x0192 ? 131 : cp == 0x201E ? 132 : cp == 0x2026 ? 133 : cp == 0x2020 ? 134 : cp == 0x2021 ? 135 : cp == 0x02C6 ? 136 : cp == 0x2030 ? 137 : cp == 0x0160 ? 138 : cp == 0x2039 ? 139 : cp == 0x0152 ? 140 : cp == 0x017D ? 142 : cp == 0x2018 ? 145 : cp == 0x2019 ? 146 : cp == 0x201C ? 147 : cp == 0x201D ? 148 : cp == 0x2022 ? 149 : cp == 0x2013 ? 150 : cp == 0x2014 ? 151 : cp == 0x02DC ? 152 : cp == 0x2122 ? 153 : cp == 0x0161 ? 154 : cp == 0x203A ? 155 : cp == 0x0153 ? 156 : cp == 0x017E ? 158 : cp == 0x0178 ? 159 : cp; cp = utf8state == UTF8_ACCEPT ? cp < (uint32_t)font->num_glyphs ? cp : 0x1A : 0x1A; if (cp == '\0') break; width += font->glyphs[cp].width; } return width; }
static const char* utf8back(const char* p, const char* start, const char* end) { const char* a; int len; // if we are not pointing at a continuation character, we are done: if ((*p&0xc0) != 0x80) return p; // search backwards for a 0xc0 starting the character: for (a = p-1; ; --a) { if (a < start) return p; if (!(a[0]&0x80)) return p; if ((a[0]&0x40)) break; } utf8decode(a,end,&len); if (a+len > p) return a; return p; }
void osd_show_info(struct osd_t* osd, int channel_id, int timeout) { char str[128]; int server; channels_geteventid(channel_id,&osd->event,&server); channels_getnexteventid(channel_id,&osd->nextEvent,&server); struct event_t* event = event_copy(osd->event,server); struct event_t* nextEvent = event_copy(osd->nextEvent,server); fprintf(stderr,"***OSD: event=%d\n",(event ? event->eventId : -1)); event_dump(event); fprintf(stderr,"***OSD: nextEvent=%d\n",(nextEvent ? nextEvent->eventId : -1)); event_dump(nextEvent); fprintf(stderr,"******\n"); snprintf(str,sizeof(str),"%03d - %s",channels_getlcn(channel_id),channels_getname(channel_id)); char* iso_text = malloc(strlen(str)+1); utf8decode(str,iso_text); pthread_mutex_lock(&osd->osd_mutex); osd_show_channelname(osd,iso_text); osd_show_time(osd); osd_show_eventinfo(osd,event,nextEvent); graphics_update_displayed_resource(osd->img, 0, 0, 0, 0); pthread_mutex_unlock(&osd->osd_mutex); free(iso_text); osd->osd_state = OSD_INFO; if (timeout) { osd->osd_cleartime = get_time() + timeout; } event_free(event); event_free(nextEvent); }
int main(int argc, char* argv[]) { iconv_t sConv; const char* pszSrcEncoding; const char* pszDstEncoding = "UTF-8"; int i; int nLastIdentical = -1; if( argc != 2 ) { fprintf(stderr, "Usage: generate_encoding_table encoding_name\n"); return 1; } pszSrcEncoding = argv[1]; sConv = iconv_open( pszDstEncoding, pszSrcEncoding ); if ( sConv == (iconv_t)-1 ) { fprintf(stderr, "Recode from %s to %s failed with the error: \"%s\".", pszSrcEncoding, pszDstEncoding, strerror(errno) ); return 1; } for(i = 0; i < 256; i++) { char szSrcBuf[2] = {(char)i, 0}; char szDstBuf[5] = {0,0,0,0,0}; char *pszSrcBuf = szSrcBuf; char *pszDstBuf = szDstBuf; size_t nSrcLen = strlen( szSrcBuf ); size_t nDstLen = sizeof(szDstBuf); size_t nConverted = iconv( sConv, &pszSrcBuf, &nSrcLen, &pszDstBuf, &nDstLen ); int nUnicode = -1; if( nConverted == -1 ) { if ( errno == EILSEQ ) { /* fprintf(stderr, "EILSEQ for %d\n", i); */ } else if ( errno == E2BIG ) { fprintf(stderr, "E2BIG for %d\n", i); return 1; } else { fprintf(stderr, "other error for %d\n", i); return 1; } } else { int len; nUnicode = utf8decode(szDstBuf, szDstBuf + strlen(szDstBuf), &len); if( nUnicode == 0xfffd ) nUnicode = -1; } if( nLastIdentical >= 0 && i != nUnicode ) { if( nLastIdentical + 1 == i ) printf("info->map[0x%02X] = 0x%02X;\n", nLastIdentical, nLastIdentical); else { printf("for(i = 0x%02X; i < 0x%02X; i++)\n", nLastIdentical, i); printf(" info->map[i] = i;\n"); } nLastIdentical = -1; } if( nUnicode < 0 ) printf("info->map[0x%02X] = -1;\n", i); else if (nUnicode <= 0xFF ) { if( i == nUnicode ) { if( nLastIdentical < 0 ) nLastIdentical = i; } else printf("info->map[0x%02X] = 0x%02X;\n", i, nUnicode); } else if (nUnicode <= 0xFFFF ) printf("info->map[0x%02X] = 0x%04X;\n", i, nUnicode); else if (nUnicode <= 0xFFFFFF ) printf("info->map[0x%02X] = 0x%06X;\n", i, nUnicode); else printf("info->map[0x%02X] = 0x%08X;\n", i, nUnicode); } if( nLastIdentical >= 0 ) { if( nLastIdentical + 1 == i ) printf("info->map[0x%02X] = 0x%02X;\n", nLastIdentical, nLastIdentical); else { printf("for(i = 0x%02X; i < 0x%02X; i++)\n", nLastIdentical, i); printf(" info->map[i] = i;\n"); } nLastIdentical = -1; } iconv_close( sConv ); return 0; }
/* * Convert the src string to UTF8 coding dst string, and cut to length */ int string2utf8(unsigned char *src, unsigned char* dst, unsigned int length) { unsigned char *pt; unsigned char ch; unsigned short ucode; unsigned int type; unsigned int len; len = 0; type = 0; pt = src; while(*pt) { pt = utf8decode(pt, &ucode); if(ucode < 0x4e00) { if(ucode == 0 || ucode > 0x7F) { type = 1; break; } } else if(ucode > 0x9FCF) { type = 1; break; } else len++; if(len >= 3) break; //There is enough UTF8, so it is, to save time(>_*) } if(type == 0) //UTF8 { while(*src) { ch = *src++; *dst++ = ch; if(ch < 0x80) { if(length > 1) length -= 1; else break; } else if (ch < 0xe0) { /* U-00000080 - U-000007FF, 2 bytes */ if(length > 2) length -= 2; else break; *dst++ = *src++; } else if (ch < 0xf0) { /* U-00000800 - U-0000FFFF, 3 bytes */ if(length > 3) length -= 3; else break; *dst++ = *src++; *dst++ = *src++; } else if (ch < 0xf5) { /* U-00010000 - U-001FFFFF, 4 bytes */ if(length > 4) length -= 4; else break; *dst++ = *src++; *dst++ = *src++; *dst++ = *src++; } else { break; } } *dst = '\0'; } else //assume it is GBK code { //GBK to UTF8 while(*src) { ch = *src; if(ch < 0x80) { if(length > 1) length -= 1; else break; *dst++= ch; src ++; } else { ucode = charsets_gbk_to_ucs(src); if (ucode < 0x800) //2 bytes { if(length > 2) length -= 2; else break; *dst++ = 0xC0 | ((ucode >> 6) & 0x1F); *dst++ = 0x80 | (ucode & 0x3F); } else //3 bytes { if(length > 3) length -= 3; else break; *dst++ = 0xE0 | (ucode >> 12); *dst++ = 0x80 | ((ucode >>6) & 0x3F); *dst++ = 0x80 | (ucode & 0x3F); } src += 2; } } *dst = '\0'; }
static void osd_show_eventinfo(struct osd_t* osd, struct event_t* event, struct event_t* nextEvent) { char str[64]; struct tm start_time; struct tm stop_time; int duration; int width = 1920-2*OSD_XMARGIN; int height = 380-OSD_YMARGIN; osd_draw_window(osd,OSD_XMARGIN,700,width,height); if (event==NULL) return; /* Start/stop time - current event */ localtime_r((time_t*)&event->start,&start_time); localtime_r((time_t*)&event->stop,&stop_time); duration = event->stop - event->start; snprintf(str,sizeof(str),"%02d:%02d - %02d:%02d",start_time.tm_hour,start_time.tm_min,stop_time.tm_hour,stop_time.tm_min); graphics_resource_render_text_ext(osd->img, OSD_XMARGIN+50, 720, width, height, GRAPHICS_RGBA32(0xff,0xff,0xff,0xff), /* fg */ GRAPHICS_RGBA32(0,0,0,0x80), /* bg */ str, strlen(str), 40); /* Title - current event */ if (event->title) { char* iso_text = malloc(strlen(event->title)+1); utf8decode(event->title,iso_text); graphics_resource_render_text_ext(osd->img, OSD_XMARGIN+350, 720, width, height, GRAPHICS_RGBA32(0xff,0xff,0xff,0xff), /* fg */ GRAPHICS_RGBA32(0,0,0,0x80), /* bg */ iso_text, strlen(iso_text), 40); free(iso_text); } snprintf(str,sizeof(str),"%dh %02dm",duration/3600,(duration%3600)/60); graphics_resource_render_text_ext(osd->img, OSD_XMARGIN+50, 800, width, height, GRAPHICS_RGBA32(0xff,0xff,0xff,0xff), /* fg */ GRAPHICS_RGBA32(0,0,0,0x80), /* bg */ str, strlen(str), 30); if ((event->episodeNumber) || (event->seasonNumber)) { if (!event->episodeNumber) { snprintf(str,sizeof(str),"Season %d",event->seasonNumber); } else if (!event->seasonNumber) { snprintf(str,sizeof(str),"Episode %d",event->episodeNumber); } else { snprintf(str,sizeof(str),"Season %d, Ep. %d",event->seasonNumber,event->episodeNumber); } graphics_resource_render_text_ext(osd->img, OSD_XMARGIN+50, 838, width, height, GRAPHICS_RGBA32(0xff,0xff,0xff,0xff), /* fg */ GRAPHICS_RGBA32(0,0,0,0x80), /* bg */ str, strlen(str), 30); } if (event->description) { char* iso_text = malloc(strlen(event->description)+1); utf8decode(event->description,iso_text); render_paragraph(osd->img,iso_text,30,OSD_XMARGIN+350,800); free(iso_text); } if (nextEvent) { osd_draw_window(osd,OSD_XMARGIN,1002,width,78-OSD_YMARGIN); /* Start/stop time - next event */ localtime_r((time_t*)&nextEvent->start,&start_time); localtime_r((time_t*)&nextEvent->stop,&stop_time); snprintf(str,sizeof(str),"%02d:%02d - %02d:%02d",start_time.tm_hour,start_time.tm_min,stop_time.tm_hour,stop_time.tm_min); graphics_resource_render_text_ext(osd->img, OSD_XMARGIN+50, 1020, width, height, GRAPHICS_RGBA32(0xff,0xff,0xff,0xff), /* fg */ GRAPHICS_RGBA32(0,0,0,0x80), /* bg */ str, strlen(str), 40); if (nextEvent->title) { char* iso_text = malloc(strlen(nextEvent->title)+1); utf8decode(nextEvent->title,iso_text); graphics_resource_render_text_ext(osd->img, OSD_XMARGIN+350, 1020, width, height, GRAPHICS_RGBA32(0xff,0xff,0xff,0xff), /* fg */ GRAPHICS_RGBA32(0,0,0,0x80), /* bg */ iso_text, strlen(iso_text), 40); free(iso_text); } } //fprintf(stderr,"Title: %s\n",event->title); //fprintf(stderr,"Start: %04d-%02d-%02d %02d:%02d:%02d\n",start_time.tm_year+1900,start_time.tm_mon+1,start_time.tm_mday,start_time.tm_hour,start_time.tm_min,start_time.tm_sec); //fprintf(stderr,"Stop: %04d-%02d-%02d %02d:%02d:%02d\n",stop_time.tm_year+1900,stop_time.tm_mon+1,stop_time.tm_mday,stop_time.tm_hour,stop_time.tm_min,stop_time.tm_sec); //fprintf(stderr,"Duration: %02d:%02d:%02d\n", //fprintf(stderr,"Description: %s\n",event->description); }
int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text) { char buf[1024]; int tx, ty, th; Extnts tex; XftDraw *d = NULL; Fnt *curfont, *nextfont; size_t i, len; int utf8strlen, utf8charlen, render; long utf8codepoint = 0; const char *utf8str; FcCharSet *fccharset; FcPattern *fcpattern; FcPattern *match; XftResult result; int charexists = 0; if (!drw->scheme || !drw->fontcount) return 0; if (!(render = x || y || w || h)) { w = ~w; } else { XSetForeground(drw->dpy, drw->gc, drw->scheme->bg->pix); XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); d = XftDrawCreate(drw->dpy, drw->drawable, DefaultVisual(drw->dpy, drw->screen), DefaultColormap(drw->dpy, drw->screen)); } curfont = drw->fonts[0]; while (1) { utf8strlen = 0; utf8str = text; nextfont = NULL; while (*text) { utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); for (i = 0; i < drw->fontcount; i++) { charexists = charexists || XftCharExists(drw->dpy, drw->fonts[i]->xfont, utf8codepoint); if (charexists) { if (drw->fonts[i] == curfont) { utf8strlen += utf8charlen; text += utf8charlen; } else { nextfont = drw->fonts[i]; } break; } } if (!charexists || (nextfont && nextfont != curfont)) break; else charexists = 0; } if (utf8strlen) { drw_font_getexts(curfont, utf8str, utf8strlen, &tex); /* shorten text if necessary */ for (len = MIN(utf8strlen, (sizeof buf) - 1); len && (tex.w > w - drw->fonts[0]->h || w < drw->fonts[0]->h); len--) drw_font_getexts(curfont, utf8str, len, &tex); if (len) { memcpy(buf, utf8str, len); buf[len] = '\0'; if (len < utf8strlen) for (i = len; i && i > len - 3; buf[--i] = '.'); if (render) { th = curfont->ascent + curfont->descent; ty = y + (h / 2) - (th / 2) + curfont->ascent; tx = x + (h / 2); XftDrawStringUtf8(d, &drw->scheme->fg->rgb, curfont->xfont, tx, ty, (XftChar8 *)buf, len); } x += tex.w; w -= tex.w; } } if (!*text) { break; } else if (nextfont) { charexists = 0; curfont = nextfont; } else { /* Regardless of whether or not a fallback font is found, the * character must be drawn. */ charexists = 1; if (drw->fontcount >= DRW_FONT_CACHE_SIZE) continue; fccharset = FcCharSetCreate(); FcCharSetAddChar(fccharset, utf8codepoint); if (!drw->fonts[0]->pattern) { /* Refer to the comment in drw_font_xcreate for more * information. */ die("the first font in the cache must be loaded from a font string.\n"); } fcpattern = FcPatternDuplicate(drw->fonts[0]->pattern); FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); FcDefaultSubstitute(fcpattern); match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); FcCharSetDestroy(fccharset); FcPatternDestroy(fcpattern); if (match) { curfont = drw_font_xcreate(drw, NULL, match); if (curfont && XftCharExists(drw->dpy, curfont->xfont, utf8codepoint)) { drw->fonts[drw->fontcount++] = curfont; } else { drw_font_free(curfont); curfont = drw->fonts[0]; } } } } if (d) XftDrawDestroy(d); return x; }
wraptext_t* word_wrap_text(const font_t* font, const char* text, int width) { char* buffer = NULL; uint8_t ch_byte; char* carry; size_t ch_size; uint32_t cp; int glyph_width; bool is_line_end = false; int line_idx; int line_width; int max_lines = 10; char* last_break; char* last_space; char* last_tab; char* line_buffer; size_t line_length; char* new_buffer; size_t pitch; uint32_t utf8state; wraptext_t* wraptext; const char *p, *start; if (!(wraptext = calloc(1, sizeof(wraptext_t)))) goto on_error; // allocate initial buffer get_font_metrics(font, &glyph_width, NULL, NULL); pitch = 4 * (glyph_width > 0 ? width / glyph_width : width) + 3; if (!(buffer = malloc(max_lines * pitch))) goto on_error; if (!(carry = malloc(pitch))) goto on_error; // run through one character at a time, carrying as necessary line_buffer = buffer; line_buffer[0] = '\0'; line_idx = 0; line_width = 0; line_length = 0; memset(line_buffer, 0, pitch); // fill line with NULs p = text; do { utf8state = UTF8_ACCEPT; start = p; while (utf8decode(&utf8state, &cp, ch_byte = *p++) > UTF8_REJECT); if (utf8state == UTF8_REJECT && ch_byte == '\0') --p; // don't eat NUL terminator ch_size = p - start; cp = cp == 0x20AC ? 128 : cp == 0x201A ? 130 : cp == 0x0192 ? 131 : cp == 0x201E ? 132 : cp == 0x2026 ? 133 : cp == 0x2020 ? 134 : cp == 0x2021 ? 135 : cp == 0x02C6 ? 136 : cp == 0x2030 ? 137 : cp == 0x0160 ? 138 : cp == 0x2039 ? 139 : cp == 0x0152 ? 140 : cp == 0x017D ? 142 : cp == 0x2018 ? 145 : cp == 0x2019 ? 146 : cp == 0x201C ? 147 : cp == 0x201D ? 148 : cp == 0x2022 ? 149 : cp == 0x2013 ? 150 : cp == 0x2014 ? 151 : cp == 0x02DC ? 152 : cp == 0x2122 ? 153 : cp == 0x0161 ? 154 : cp == 0x203A ? 155 : cp == 0x0153 ? 156 : cp == 0x017E ? 158 : cp == 0x0178 ? 159 : cp; cp = utf8state == UTF8_ACCEPT ? cp < (uint32_t)font->num_glyphs ? cp : 0x1A : 0x1A; switch (cp) { case '\n': case '\r': // explicit newline if (cp == '\r' && *p == '\n') ++text; // CRLF is_line_end = true; break; case '\t': // tab line_buffer[line_length++] = cp; line_width += get_text_width(font, " "); is_line_end = false; break; case '\0': // NUL terminator is_line_end = line_length > 0; // commit last line on EOT break; default: // default case, copy character as-is memcpy(line_buffer + line_length, start, ch_size); line_length += ch_size; line_width += get_glyph_width(font, cp); is_line_end = false; } if (is_line_end) carry[0] = '\0'; if (line_width > width || line_length >= pitch - 1) { // wrap width exceeded, carry current word to next line is_line_end = true; last_space = strrchr(line_buffer, ' '); last_tab = strrchr(line_buffer, '\t'); last_break = last_space > last_tab ? last_space : last_tab; if (last_break != NULL) // word break (space or tab) found strcpy(carry, last_break + 1); else // no word break, so just carry last character sprintf(carry, "%c", line_buffer[line_length - 1]); line_buffer[line_length - strlen(carry)] = '\0'; } if (is_line_end) { // do we need to enlarge the buffer? if (++line_idx >= max_lines) { max_lines *= 2; if (!(new_buffer = realloc(buffer, max_lines * pitch))) goto on_error; buffer = new_buffer; line_buffer = buffer + line_idx * pitch; } else line_buffer += pitch; memset(line_buffer, 0, pitch); // fill line with NULs // copy carry text into new line line_width = get_text_width(font, carry); line_length = strlen(carry); strcpy(line_buffer, carry); } } while (cp != '\0'); free(carry); wraptext->num_lines = line_idx; wraptext->buffer = buffer; wraptext->pitch = pitch; return wraptext; on_error: free(buffer); free(wraptext); return NULL; }
void draw_text(const font_t* font, color_t color, int x, int y, text_align_t alignment, const char* text) { uint8_t ch_byte; uint32_t cp; int tab_width; uint32_t utf8state; if (alignment == TEXT_ALIGN_CENTER) x -= get_text_width(font, text) / 2; else if (alignment == TEXT_ALIGN_RIGHT) x -= get_text_width(font, text); tab_width = font->glyphs[' '].width * 3; al_hold_bitmap_drawing(true); for (;;) { utf8state = UTF8_ACCEPT; while (utf8decode(&utf8state, &cp, ch_byte = *text++) > UTF8_REJECT); if (utf8state == UTF8_REJECT && ch_byte == '\0') --text; // don't eat NUL terminator cp = cp == 0x20AC ? 128 : cp == 0x201A ? 130 : cp == 0x0192 ? 131 : cp == 0x201E ? 132 : cp == 0x2026 ? 133 : cp == 0x2020 ? 134 : cp == 0x2021 ? 135 : cp == 0x02C6 ? 136 : cp == 0x2030 ? 137 : cp == 0x0160 ? 138 : cp == 0x2039 ? 139 : cp == 0x0152 ? 140 : cp == 0x017D ? 142 : cp == 0x2018 ? 145 : cp == 0x2019 ? 146 : cp == 0x201C ? 147 : cp == 0x201D ? 148 : cp == 0x2022 ? 149 : cp == 0x2013 ? 150 : cp == 0x2014 ? 151 : cp == 0x02DC ? 152 : cp == 0x2122 ? 153 : cp == 0x0161 ? 154 : cp == 0x203A ? 155 : cp == 0x0153 ? 156 : cp == 0x017E ? 158 : cp == 0x0178 ? 159 : cp; cp = utf8state == UTF8_ACCEPT ? cp < (uint32_t)font->num_glyphs ? cp : 0x1A : 0x1A; if (cp == '\0') break; else if (cp == '\t') x += tab_width; else { draw_image_masked(font->glyphs[cp].image, color, x, y); x += font->glyphs[cp].width; } } al_hold_bitmap_drawing(false); }