static void layout_row( ME_DisplayItem *start, const ME_DisplayItem *end ) { ME_DisplayItem *p; int i, num_runs = 0; int buf[16 * 5]; /* 5 arrays - 4 of int & 1 of BYTE, alloc space for 5 of ints */ int *vis_to_log = buf, *log_to_vis, *widths, *pos; BYTE *levels; BOOL found_black = FALSE; for (p = end->prev; p != start->prev; p = p->prev) { if (p->type == diRun) { if (!found_black) found_black = !(p->member.run.nFlags & (MERF_WHITESPACE | MERF_ENDPARA)); if (found_black) num_runs++; } } TRACE("%d runs\n", num_runs); if (!num_runs) return; if (num_runs > sizeof(buf) / (sizeof(buf[0]) * 5)) vis_to_log = heap_alloc( num_runs * sizeof(int) * 5 ); log_to_vis = vis_to_log + num_runs; widths = vis_to_log + 2 * num_runs; pos = vis_to_log + 3 * num_runs; levels = (BYTE*)(vis_to_log + 4 * num_runs); for (i = 0, p = start; i < num_runs; p = p->next) { if (p->type == diRun) { levels[i] = p->member.run.script_analysis.s.uBidiLevel; widths[i] = p->member.run.nWidth; TRACE( "%d: level %d width %d\n", i, levels[i], widths[i] ); i++; } } ScriptLayout( num_runs, levels, vis_to_log, log_to_vis ); pos[0] = start->member.run.para->pt.x; for (i = 1; i < num_runs; i++) pos[i] = pos[i - 1] + widths[ vis_to_log[ i - 1 ] ]; for (i = 0, p = start; i < num_runs; p = p->next) { if (p->type == diRun) { p->member.run.pt.x = pos[ log_to_vis[ i ] ]; TRACE( "%d: x = %d\n", i, p->member.run.pt.x ); i++; } } if (vis_to_log != buf) heap_free( vis_to_log ); }
/*------------------------------------------------------------------------ Function: BidiLines Implements the Line-by-Line phases of the Unicode Bidi Algorithm Input: Count of characters Array of character directions Inp/Out: Input text Array of levels ------------------------------------------------------------------------*/ static void BidiLines(int baselevel, LPWSTR pszOutLine, LPCWSTR pszLine, const WORD * pclsLine, BYTE * plevelLine, int cchPara, const BOOL * pbrk) { int cchLine = 0; int done = 0; int *run; run = HeapAlloc(GetProcessHeap(), 0, cchPara * sizeof(int)); if (!run) { WARN("Out of memory\n"); return; } do { /* break lines at LS */ cchLine = resolveLines(pszLine, pbrk, cchPara); /* resolve whitespace */ resolveWhitespace(baselevel, pclsLine, plevelLine, cchLine); if (pszOutLine) { int i; /* reorder each line in place */ ScriptLayout(cchLine, plevelLine, NULL, run); for (i = 0; i < cchLine; i++) pszOutLine[done+run[i]] = pszLine[i]; } pszLine += cchLine; plevelLine += cchLine; pbrk += pbrk ? cchLine : 0; pclsLine += cchLine; cchPara -= cchLine; done += cchLine; } while (cchPara); HeapFree(GetProcessHeap(), 0, run); }
/************************************************************* * BIDI_Reorder * * Returns TRUE if reordering was required and done. */ BOOL BIDI_Reorder( HDC hDC, /*[in] Display DC */ LPCWSTR lpString, /* [in] The string for which information is to be returned */ INT uCount, /* [in] Number of WCHARs in string. */ DWORD dwFlags, /* [in] GetCharacterPlacement compatible flags specifying how to process the string */ DWORD dwWineGCP_Flags, /* [in] Wine internal flags - Force paragraph direction */ LPWSTR lpOutString, /* [out] Reordered string */ INT uCountOut, /* [in] Size of output buffer */ UINT *lpOrder, /* [out] Logical -> Visual order map */ WORD **lpGlyphs, /* [out] reordered, mirrored, shaped glyphs to display */ INT *cGlyphs /* [out] number of glyphs generated */ ) { WORD *chartype; BYTE *levels; unsigned i, done, glyph_i; BOOL is_complex; int maxItems; int nItems; SCRIPT_CONTROL Control; SCRIPT_STATE State; SCRIPT_ITEM *pItems; HRESULT res; SCRIPT_CACHE psc = NULL; WORD *run_glyphs = NULL; WORD *pwLogClust = NULL; SCRIPT_VISATTR *psva = NULL; DWORD cMaxGlyphs = 0; BOOL doGlyphs = TRUE; TRACE("%s, %d, 0x%08x lpOutString=%p, lpOrder=%p\n", debugstr_wn(lpString, uCount), uCount, dwFlags, lpOutString, lpOrder); memset(&Control, 0, sizeof(Control)); memset(&State, 0, sizeof(State)); if (lpGlyphs) *lpGlyphs = NULL; if (!(dwFlags & GCP_REORDER)) { FIXME("Asked to reorder without reorder flag set\n"); return FALSE; } if (lpOutString && uCountOut < uCount) { FIXME("lpOutString too small\n"); return FALSE; } chartype = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(WORD)); if (!chartype) { WARN("Out of memory\n"); return FALSE; } if (lpOutString) memcpy(lpOutString, lpString, uCount * sizeof(WCHAR)); is_complex = FALSE; for (i = 0; i < uCount && !is_complex; i++) { if ((lpString[i] >= 0x900 && lpString[i] <= 0xfff) || (lpString[i] >= 0x1cd0 && lpString[i] <= 0x1cff) || (lpString[i] >= 0xa840 && lpString[i] <= 0xa8ff)) is_complex = TRUE; } /* Verify reordering will be required */ if ((WINE_GCPW_FORCE_RTL == (dwWineGCP_Flags&WINE_GCPW_DIR_MASK)) || ((dwWineGCP_Flags&WINE_GCPW_DIR_MASK) == WINE_GCPW_LOOSE_RTL)) State.uBidiLevel = 1; else if (!is_complex) { done = 1; classify(lpString, chartype, uCount); for (i = 0; i < uCount; i++) switch (chartype[i]) { case R: case AL: case RLE: case RLO: done = 0; break; } if (done) { HeapFree(GetProcessHeap(), 0, chartype); if (lpOrder) { for (i = 0; i < uCount; i++) lpOrder[i] = i; } return TRUE; } } levels = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(BYTE)); if (!levels) { WARN("Out of memory\n"); HeapFree(GetProcessHeap(), 0, chartype); return FALSE; } maxItems = 5; pItems = HeapAlloc(GetProcessHeap(),0, maxItems * sizeof(SCRIPT_ITEM)); if (!pItems) { WARN("Out of memory\n"); HeapFree(GetProcessHeap(), 0, chartype); HeapFree(GetProcessHeap(), 0, levels); return FALSE; } if (lpGlyphs) { cMaxGlyphs = 1.5 * uCount + 16; run_glyphs = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * cMaxGlyphs); if (!run_glyphs) { WARN("Out of memory\n"); HeapFree(GetProcessHeap(), 0, chartype); HeapFree(GetProcessHeap(), 0, levels); HeapFree(GetProcessHeap(), 0, pItems); return FALSE; } pwLogClust = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * uCount); if (!pwLogClust) { WARN("Out of memory\n"); HeapFree(GetProcessHeap(), 0, chartype); HeapFree(GetProcessHeap(), 0, levels); HeapFree(GetProcessHeap(), 0, pItems); HeapFree(GetProcessHeap(), 0, run_glyphs); return FALSE; } psva = HeapAlloc(GetProcessHeap(),0,sizeof(SCRIPT_VISATTR) * uCount); if (!psva) { WARN("Out of memory\n"); HeapFree(GetProcessHeap(), 0, chartype); HeapFree(GetProcessHeap(), 0, levels); HeapFree(GetProcessHeap(), 0, pItems); HeapFree(GetProcessHeap(), 0, run_glyphs); HeapFree(GetProcessHeap(), 0, pwLogClust); return FALSE; } } done = 0; glyph_i = 0; while (done < uCount) { unsigned j; classify(lpString + done, chartype, uCount - done); /* limit text to first block */ i = resolveParagraphs(chartype, uCount - done); for (j = 0; j < i; ++j) switch(chartype[j]) { case B: case S: case WS: case ON: chartype[j] = N; default: continue; } if ((dwWineGCP_Flags&WINE_GCPW_DIR_MASK) == WINE_GCPW_LOOSE_RTL) State.uBidiLevel = 1; else if ((dwWineGCP_Flags&WINE_GCPW_DIR_MASK) == WINE_GCPW_LOOSE_LTR) State.uBidiLevel = 0; if (dwWineGCP_Flags & WINE_GCPW_LOOSE_MASK) { for (j = 0; j < i; ++j) if (chartype[j] == L) { State.uBidiLevel = 0; break; } else if (chartype[j] == R || chartype[j] == AL) { State.uBidiLevel = 1; break; } } res = ScriptItemize(lpString + done, i, maxItems, &Control, &State, pItems, &nItems); while (res == E_OUTOFMEMORY) { maxItems = maxItems * 2; pItems = HeapReAlloc(GetProcessHeap(), 0, pItems, sizeof(SCRIPT_ITEM) * maxItems); if (!pItems) { WARN("Out of memory\n"); HeapFree(GetProcessHeap(), 0, chartype); HeapFree(GetProcessHeap(), 0, levels); return FALSE; } res = ScriptItemize(lpString + done, i, maxItems, &Control, &State, pItems, &nItems); } if (lpOutString || lpOrder) for (j = 0; j < nItems; j++) { int k; for (k = pItems[j].iCharPos; k < pItems[j+1].iCharPos; k++) levels[k] = pItems[j].a.s.uBidiLevel; } if (lpOutString) { /* assign directional types again, but for WS, S this time */ classify(lpString + done, chartype, i); BidiLines(State.uBidiLevel, lpOutString + done, lpString + done, chartype, levels, i, 0); } if (lpOrder) { int k, lastgood; for (j = lastgood = 0; j < i; ++j) if (levels[j] != levels[lastgood]) { --j; if (odd(levels[lastgood])) for (k = j; k >= lastgood; --k) lpOrder[done + k] = done + j - k; else for (k = lastgood; k <= j; ++k) lpOrder[done + k] = done + k; lastgood = ++j; } if (odd(levels[lastgood])) for (k = j - 1; k >= lastgood; --k) lpOrder[done + k] = done + j - 1 - k; else for (k = lastgood; k < j; ++k) lpOrder[done + k] = done + k; } if (lpGlyphs && doGlyphs) { int j; BYTE runOrder[maxItems]; int visOrder[maxItems]; SCRIPT_ITEM *curItem; for (j = 0; j < nItems; j++) runOrder[j] = pItems[j].a.s.uBidiLevel; ScriptLayout(nItems, runOrder, visOrder, NULL); for (j = 0; j < nItems; j++) { int k; int cChars,cOutGlyphs; curItem = &pItems[visOrder[j]]; cChars = pItems[visOrder[j]+1].iCharPos - curItem->iCharPos; res = ScriptShape(hDC, &psc, lpString + done + curItem->iCharPos, cChars, cMaxGlyphs, &curItem->a, run_glyphs, pwLogClust, psva, &cOutGlyphs); while (res == E_OUTOFMEMORY) { cMaxGlyphs *= 2; run_glyphs = HeapReAlloc(GetProcessHeap(), 0, run_glyphs, sizeof(WORD) * cMaxGlyphs); if (!run_glyphs) { WARN("Out of memory\n"); HeapFree(GetProcessHeap(), 0, chartype); HeapFree(GetProcessHeap(), 0, levels); HeapFree(GetProcessHeap(), 0, pItems); HeapFree(GetProcessHeap(), 0, psva); HeapFree(GetProcessHeap(), 0, pwLogClust); HeapFree(GetProcessHeap(), 0, *lpGlyphs); ScriptFreeCache(&psc); *lpGlyphs = NULL; return FALSE; } res = ScriptShape(hDC, &psc, lpString + done + curItem->iCharPos, cChars, cMaxGlyphs, &curItem->a, run_glyphs, pwLogClust, psva, &cOutGlyphs); } if (res) { if (res == USP_E_SCRIPT_NOT_IN_FONT) TRACE("Unable to shape with currently selected font\n"); else FIXME("Unable to shape string (%x)\n",res); j = nItems; doGlyphs = FALSE; HeapFree(GetProcessHeap(), 0, *lpGlyphs); *lpGlyphs = NULL; } else { if (*lpGlyphs) *lpGlyphs = HeapReAlloc(GetProcessHeap(), 0, *lpGlyphs, sizeof(WORD) * (glyph_i + cOutGlyphs)); else *lpGlyphs = HeapAlloc(GetProcessHeap(), 0, sizeof(WORD) * (glyph_i + cOutGlyphs)); for (k = 0; k < cOutGlyphs; k++) (*lpGlyphs)[glyph_i+k] = run_glyphs[k]; glyph_i += cOutGlyphs; } } } done += i; } if (cGlyphs) *cGlyphs = glyph_i; HeapFree(GetProcessHeap(), 0, chartype); HeapFree(GetProcessHeap(), 0, levels); HeapFree(GetProcessHeap(), 0, pItems); HeapFree(GetProcessHeap(), 0, run_glyphs); HeapFree(GetProcessHeap(), 0, pwLogClust); HeapFree(GetProcessHeap(), 0, psva); ScriptFreeCache(&psc); return TRUE; }
bool MCTextLayout(const unichar_t *p_chars, uint32_t p_char_count, MCFontStruct *p_font, MCTextLayoutCallback p_callback, void *p_context) { bool t_success; t_success = true; // The state structure we use to record the list of runs and the dc we use // for processing. MCTextLayoutState self; MCMemoryClear(&self, sizeof(MCTextLayoutState)); if (t_success) { self . dc = CreateCompatibleDC(nil); if (self . dc == nil) t_success = false; } // Fetch a layout font for the provided HFONT. if (t_success) t_success = MCTextLayoutFontFromHFONT(p_font -> fid, self . primary_font); // First thing we need to do is itemize the input string. The ScriptItemize // function splits up the chars into runs, each run being potentially // processed differently by Uniscribe. // Unfortunately, there is no way to predict for an arbitrary string how // many items might be generated, nor is the ScriptItemize function // incremental, thus we must loop with an every increasing buffer until // we have enough room. SCRIPT_ITEM *t_items; uint32_t t_item_limit, t_item_count; SCRIPT_STATE t_script_state; SCRIPT_CONTROL t_script_control; t_items = nil; t_item_limit = 0; t_item_count = 0; MCMemoryClear(&t_script_state, sizeof(SCRIPT_STATE)); MCMemoryClear(&t_script_control, sizeof(SCRIPT_CONTROL)); while(t_success) { // Increase the item array by 32 each time if (t_success) t_success = MCMemoryResizeArray(t_item_limit + 32, t_items, t_item_limit); // Attempt to itemize HRESULT t_result; if (t_success) { t_result = ScriptItemize(p_chars, p_char_count, t_item_limit, &t_script_control, &t_script_state, t_items, (int *)&t_item_count); if (t_result != S_OK && t_result != E_OUTOFMEMORY) t_success = false; } if (t_success && t_result == S_OK) break; } // Next we loop through the items one by one, processing them as we go, this // process is slightly recursive - LayoutItem may recurse to fill in any // 'holes' caused by glyphs not in the primary font. for(uint32_t i = 0; i < t_item_count && t_success; i++) t_success = MCTextLayoutStyleItem( self, t_items[i] . a, p_chars + t_items[i] . iCharPos, t_items[i + 1] . iCharPos - t_items[i] . iCharPos, self . primary_font); // At this point we should have an array of runs to render. First though we // need to compute the visual to logical mapping. uint8_t *t_levels; int *t_map; t_levels = nil; t_map = nil; if (t_success) t_success = MCMemoryNewArray(self . run_count, t_levels) && MCMemoryNewArray(self . run_count, t_map); // Work out the run mapping, but only if we have runs to map! if (t_success && self . run_count > 0) { for(uint32_t i = 0; i < self . run_count; i++) t_levels[i] = self . runs[i] . embedding_level; if (ScriptLayout(self . run_count, t_levels, t_map, nil) != S_OK) t_success = false; } // Now we have the mapping we loop through the runs in the correct order // dispatching them to the callback. if (t_success && p_callback != nil) { double t_x; t_x = 0.0; for(uint32_t i = 0; i < self . run_count && t_success; i++) { MCTextLayoutRun *t_run; t_run = &self . runs[t_map[i]]; // Allocate a temporary array for the glyph structures MCTextLayoutGlyph *t_glyphs; t_glyphs = nil; if (t_success) t_success = MCMemoryNewArray(t_run -> glyph_count, t_glyphs); if (t_success) { // Compute the position for each glyph, keeping a running // total of the advance width. for(uint32_t i = 0; i < t_run -> glyph_count; i++) { t_glyphs[i] . index = t_run -> glyphs[i]; t_glyphs[i] . x = t_x + t_run -> goffsets[i] . du; t_glyphs[i] . y = t_run -> goffsets[i] . dv; t_x += t_run -> advances[i]; } // Dispatch the span to the callback. MCTextLayoutSpan t_span; t_span . chars = t_run -> chars; t_span . clusters = t_run -> clusters; t_span . char_count = t_run -> char_count; t_span . glyphs = t_glyphs; t_span . glyph_count = t_run -> glyph_count; t_span . font = t_run -> font -> handle; t_success = p_callback(p_context, &t_span); } // Free the temporary array. MCMemoryDeleteArray(t_glyphs); } } // Free all the arrays and other resources that have been allocated. MCMemoryDeleteArray(t_map); MCMemoryDeleteArray(t_levels); for(uint32_t i = 0; i < self . run_count; i++) { MCMemoryDeallocate(self . runs[i] . chars); MCMemoryDeallocate(self . runs[i] . clusters); MCMemoryDeallocate(self . runs[i] . glyphs); MCMemoryDeallocate(self . runs[i] . advances); MCMemoryDeallocate(self . runs[i] . goffsets); } MCMemoryDeleteArray(self . runs); MCMemoryDeleteArray(t_items); if (self . dc != nil) DeleteDC(self . dc); return t_success; }