////////////////////////////////////////////////////////////////////////////// // // // Module Name : LPK_USER.c // // // // Entry points (formal interfaces) for GDI32 to call // // and route their APIs, so that we can implement our language-specific // // features. // // // // Created : Nov 6, 1996 // // Author : Mohamed AbdEl Hamid [mhamid] // // // // Copyright (c) 1996, Microsoft Corporation. All rights reserved. // ////////////////////////////////////////////////////////////////////////////// #include "precomp.hxx" ///// Shared definitions for USER code #define IS_ALTDC_TYPE(h) (LO_TYPE(h) != LO_DC_TYPE) /// #undef TRACE #define TRACE(a,b) ////////////////////////////////////////////////////////////////////////////// // USER32 TabbedTextOut will call this function for supporting Multilingual // // Tabbed Text handling. // // LpkTabbedTextOut( HDC hdc , int x, int y, LPCWSTR lpstring, UINT nCount, // // int nTabPositions, LPINT lpTabPositions, int iTabOrigin,// // BOOL fDrawTheText, int cxCharWidth, int cyCharHeight, // // int charSet) // // hDC : Handle of device context // // x : x-coordinate of text to render // // y : y-coordinate of text to render // // lpstring : Input string // // nCount : Count of characters in input string // // nTabPositions : Specifies the number of values in the array of // // tab-stop positions. // // lpTabPositions : The tab-stop positions array (increasing order)(+/-)// // iTabOrigin : The X-coordinate position to start expand tabs // // fDrawTheText : Draw the Text or expand tha tabs only // // cxCharWidth : Character width to be use to expand the tabs // // cxCharHeight : Character Height to be use to expand the tabs // // charSet : Indicates character set of codes. to optimizing the work. ??// // // // Return : // // If the function succeeds, return the string dimensions // // Else, the return value is 0. // // And we seted the error by call SetLastError. // // // // History : // // Nov 6, 1996 -by- Mohamed AbdEl Hamid [mhamid] // ////////////////////////////////////////////////////////////////////////////// LONG LpkTabbedTextOut( HDC hdc, int x, int y, WCHAR *pwcInChars, int nCount, int nTabPositions, int *pTabPositions, int iTabOrigin, BOOL fDrawTheText, int cxCharWidth, int cyCharHeight, int iCharset) { SIZE textextent; SIZE viewextent; SIZE windowextent; int initialx = x; int cch; WCHAR *pwc; int iOneTab = 0; RECT rc; UINT uOpaque; BOOL fStrStart = TRUE; BOOL fRTLreading; int ySign = 1; //Assume y increases in down direction. UINT OldTextAlign; HRESULT hr; DWORD dwObjType; RECT rcRTL; STRING_ANALYSIS *psa; uOpaque = (GetBkMode(hdc) == OPAQUE) ? ETO_OPAQUE : 0; /* * If no tabstop positions are specified, then use a default of 8 system * font ave char widths or use the single fixed tab stop. */ if (!pTabPositions) { // no tab stops specified -- default to a tab stop every 8 characters iOneTab = 8 * cxCharWidth; } else if (nTabPositions == 1) { // one tab stop specified -- treat value as the tab increment, one // tab stop every increment iOneTab = pTabPositions[0]; if (!iOneTab) { iOneTab = 1; } } // Calculate if the y increases or decreases in the down direction using // the ViewPortExtent and WindowExtents. // If this call fails, hdc must be invalid if (!GetViewportExtEx(hdc, &viewextent)) { TRACEMSG(("LpkTabbedTextOut: GetViewportExtEx failed")); return 0; } GetWindowExtEx(hdc, &windowextent); if ((viewextent.cy ^ windowextent.cy) & 0x80000000) { ySign = -1; } OldTextAlign = GetTextAlign(hdc); fRTLreading = OldTextAlign & TA_RTLREADING; SetTextAlign(hdc, (OldTextAlign & ~(TA_CENTER|TA_RIGHT)) | TA_LEFT); rc.left = initialx; rc.right= initialx; rc.top = y; if (OldTextAlign & TA_BOTTOM) { rc.bottom = rc.top; } else { rc.bottom = rc.top + (ySign * cyCharHeight); } while (TRUE) { // count the number of characters until the next tab character // this set of characters (substring) will be the working set for // each iteration of this loop for (cch = nCount, pwc = pwcInChars; cch && (*pwc != TEXT('\t')); pwc++, cch--) { } // Compute the number of characters to be drawn with textout. cch = nCount - cch; // Compute the number of characters remaining. nCount -= cch; // + 1; // get height and width of substring if (cch == 0) { textextent.cx = 0; textextent.cy = cyCharHeight; psa = NULL; } else { dwObjType = GetObjectType(hdc); hr = LpkStringAnalyse( hdc, pwcInChars, cch, 0, -1, SSA_GLYPHS | (dwObjType == OBJ_METADC || dwObjType == OBJ_ENHMETADC ? SSA_METAFILE : 0) | (iCharset==-1 || GdiIsPlayMetafileDC(hdc) ? SSA_FALLBACK : SSA_LPKANSIFALLBACK) | (fRTLreading ? SSA_RTL : 0), -1, 0, NULL, NULL, NULL, NULL, NULL, &psa); if (FAILED(hr)) { ASSERTHR(hr, ("LpkTabbedTextOut - LpkStringAnalyse")); return FALSE; } textextent = psa->size; } if (fStrStart) { // first iteration should just spit out the first substring // no tabbing occurs until the first tab character is encountered fStrStart = FALSE; } else { // not the first iteration -- tab accordingly int xTab; int i; if (!iOneTab) { // look thru tab stop array for next tab stop after existing // text to put this substring for (i = 0; i != nTabPositions; i++) { xTab = pTabPositions[i]; if (xTab < 0) { // calc length needed to use this right justified tab xTab = iTabOrigin - xTab - textextent.cx; } else { // calc length needed to use this left justified tab xTab = iTabOrigin + xTab; } if ((xTab - x) > 0) { // we found a tab with enough room -- let's use it x = xTab; break; } } if (i == nTabPositions) { // we've exhausted all of the given tab positions // go back to default of a tab stop every 8 characters iOneTab = 8 * cxCharWidth; } } // we have to recheck iOneTab here (instead of just saying "else") // because iOneTab will be set if we've run out of tab stops if (iOneTab) { if (iOneTab < 0) { // calc next available right justified tab stop xTab = x + textextent.cx - iTabOrigin; xTab = ((xTab / iOneTab) * iOneTab) - iOneTab - textextent.cx + iTabOrigin; } else { // calc next available left justified tab stop xTab = x - iTabOrigin; xTab = ((xTab / iOneTab) * iOneTab) + iOneTab + iTabOrigin; } x = xTab; } } if (fDrawTheText && (cch!=0)) { /* * Output all text up to the tab (or end of string) and get its * extent. */ rc.right = x + textextent.cx; // All the calculations are made as if it is LTR and we flip the coordinates // if we have RTL. if (fRTLreading) { rcRTL = rc; rcRTL.left = (2 * initialx) - rc.right; rcRTL.right= rcRTL.left + (rc.right - rc.left) ; ScriptStringOut(psa, rcRTL.left , y, uOpaque, &rcRTL, 0, 0, FALSE); } else { ScriptStringOut(psa, x, y, uOpaque, &rc, 0, 0, FALSE); } rc.left = rc.right; } if (cch) { ScriptStringFree((void**)&psa); } // Skip over the tab and the characters we just drew. x = x + textextent.cx; // Skip over the characters we just drew. pwcInChars += cch; // See if we have more to draw OR see if this string ends in // a tab character that needs to be drawn. if ((nCount > 0) && (*pwcInChars == TEXT('\t'))) { pwcInChars++; // Skip over the tab nCount--; continue; } else { break; // Break from the loop. } } // if we have at the end of the text some Tabs then wen need to drae the background // for it. if (fDrawTheText && x>rc.right && uOpaque) { rc.right = x; if (fRTLreading) { rcRTL = rc; rcRTL.left = (2 * initialx) - rc.right; rcRTL.right= rcRTL.left + (rc.right - rc.left) ; ExtTextOutW(hdc, rcRTL.left, y, uOpaque|ETO_IGNORELANGUAGE, &rcRTL, L"", 0, NULL); } else { ExtTextOutW(hdc, rc.left, y, uOpaque|ETO_IGNORELANGUAGE, &rc, L"", 0, NULL); } } SetTextAlign(hdc, OldTextAlign); return MAKELONG((x - initialx), (short)textextent.cy); } ////////////////////////////////////////////////////////////////////////////// // USER32 PSMTextOut will call this function for supporting Multilingual // // Menu handling. // // LpkPSMTextOut( HDC hdc, int xLeft, int yTop, LPCWSTR lpString, // // int nCount) // // hDC : Handle of device context // // xLeft : x-coordinate of text to render // // yTop : y-coordinate of text to render // // lpString : Input string // // nCount : Count of characters in input string // // // // Return : Nothing // // // // History : // // Nov 6, 1996 -by- Mohamed AbdEl Hamid [mhamid] // ////////////////////////////////////////////////////////////////////////////// ///// LpkInternalPSMtextOut // // Called from LPK_USRC.C int LpkInternalPSMTextOut( HDC hdc, int xLeft, int yTop, const WCHAR *pwcInChars, int nCount, DWORD dwFlags) { HRESULT hr; int iTextAlign; STRING_ANALYSIS *psa; int iWidth; DWORD dwObjType; if (!nCount || !pwcInChars) { // No action required TRACE(GDI, ("LpkPSMTextOut: No string: nCount %d, pwcInChars %x", nCount, pwcInChars)); return 0; // That was easy! } dwObjType = GetObjectType(hdc); hr = LpkStringAnalyse( hdc, pwcInChars, nCount, 0, -1, SSA_GLYPHS | (dwFlags & DT_NOPREFIX ? 0 : (dwFlags & DT_HIDEPREFIX ? SSA_HIDEHOTKEY : (dwFlags & DT_PREFIXONLY ? SSA_HOTKEYONLY : SSA_HOTKEY))) | (dwObjType == OBJ_METADC || dwObjType == OBJ_ENHMETADC ? SSA_METAFILE : 0) | SSA_FALLBACK | ((((iTextAlign = GetTextAlign(hdc)) & TA_RTLREADING) && (iTextAlign != -1)) ? SSA_RTL : 0), -1, 0, NULL, NULL, NULL, NULL, NULL, &psa); if (SUCCEEDED(hr)) { iWidth = psa->size.cx; ScriptStringOut(psa, xLeft, yTop, 0, NULL, 0, 0, FALSE); ScriptStringFree((void**)&psa); } else { iWidth = 0; ASSERTHR(hr, ("LpkInternalPSMTextOut - LpkStringAnalyse")); psa = NULL; } return iWidth; } #ifdef LPKBREAKAWORD ////////////////////////////////////////////////////////////////////////////// // // // LpkBreakAWord : DrawText calls this routine when the length of a word // // is longer than the line width. // // // // return - character position to break a non-breakable word // // // ////////////////////////////////////////////////////////////////////////////// ///// LpkBreakAWord // // Called from LPK_USRC.C int LpkBreakAWord( HDC hdc, LPCWSTR lpchStr, int cchStr, int iMaxWidth) { if (!lpchStr || cchStr <= 0 || iMaxWidth <= 0) return 0; STRING_ANALYSIS* psa; int cOutChars; HRESULT hr; hr = LpkStringAnalyse( hdc, lpchStr, cchStr, 0, -1, SSA_GLYPHS | SSA_CLIP, -1, iMaxWidth, NULL, NULL, NULL, NULL, NULL, &psa); if (FAILED(hr)) { ASSERTHR(hr, ("LpkBreakAWord - qLpkStringAnalyse")); return 0; } cOutChars = psa->cOutChars; ScriptStringFree((void**)&psa); return max(0, cOutChars); } #endif ////////////////////////////////////////////////////////////////////////////// // // // LpkGetNextWord // // return - offset to the next word // // // ////////////////////////////////////////////////////////////////////////////// #define CR 0x000D #define LF 0x000A ///// LpkgetNextWord // // Called from LPK_USRC.C int LpkGetNextWord( HDC hdc, LPCWSTR lpchStr, int cchCount, int iCharset) { WCHAR *pRun; WCHAR *pRunEnd; int cchRun; int i=0; WCHAR wchRun; HRESULT hr; STRING_ANALYSIS *psa; // instantly advance 1 if current char located at whitespaces. if (*lpchStr == '\t' || *lpchStr == ' ') { return 1; } // try to find the shortest text run that are going to be analysed cchRun = 0; pRun = (PWSTR)lpchStr; pRunEnd = (PWSTR)(lpchStr + cchCount); while (pRun < pRunEnd) { wchRun = *pRun; if (wchRun == CR || wchRun == LF || wchRun == '\t' || wchRun == ' ') { break; } pRun++; cchRun++; } if (cchRun == 0) { return 0; } hr = LpkStringAnalyse( hdc, lpchStr, cchRun, 0, -1, SSA_BREAK, -1, 0, NULL, NULL, NULL, NULL, NULL, &psa); if (FAILED(hr)) { ASSERTHR(hr, ("LpkGetNextWord - qLpkStringAnalyse")); return 0; } // We only return next wordbreak if the first item is a breakable one. if (g_ppScriptProperties[psa->pItems->a.eScript]->fComplex) { for (i=1; i < cchRun; i++) { if (psa->pLogAttr[i].fSoftBreak ) break; } } ScriptStringFree((void**)&psa); return i; } ////////////////////////////////////////////////////////////////////////////// // USER32 DrawTextEx will call this function for supporting Multilingual // // DrawTextEx handling. // // LpkDrawTextEx(HDC hdc, int xLeft, int yTop,LPCWSTR pwcInChars, int cchCount// // , BOOL fDraw, WORD wFormat, LPDRAWTEXTDATA lpDrawInfo, // // UNIT bAction) // // hDC : Handle of device context // // xLeft : x-coordinate of text to render // // yTop : y-coordinate of text to render // // lpchStr : Input string // // cchCount : Count of characters in input string // // fDraw : Draw the Text or expand tha tabs only // // wFormat : Same as dwDTFormat options for DrawTextEx // // lpDrawInfo : Internal Structure // // bAction : DT_CHARSETDRAW OR DT_GETNEXTWORD // // // // Return : Nothing // // // // History : // // Nov 15, 1996 -by- Mohamed AbdEl Hamid [mhamid] // // Mar 26, 1997 Adding DT_GETNEXTWORD -[wchao] // ////////////////////////////////////////////////////////////////////////////// ///// LpkCharsetDraw // // Called from LPK_USRC.C // // Note: Doesn't implement user defined tabstops int LpkCharsetDraw( HDC hdc, int xLeft, int cxOverhang, int iTabOrigin, int iTabLength, int yTop, PCWSTR pcwString, int cchCount, BOOL fDraw, DWORD dwFormat, int iCharset) { HRESULT hr; int iTextAlign; int iWidth; STRING_ANALYSIS *psa; SCRIPT_TABDEF std; DWORD dwObjType; if (cchCount <= 0) { return 0; // That was easy! } if (dwFormat & DT_EXPANDTABS) { std.cTabStops = 1; std.pTabStops = &iTabLength; std.iTabOrigin = 0; std.iScale = 4; // Tab stops in pixels (avg ch width already applied in USER) } dwObjType = GetObjectType(hdc); hr = LpkStringAnalyse( hdc, pcwString, cchCount, 0, -1, SSA_GLYPHS | (dwFormat & DT_NOPREFIX ? 0 : (dwFormat & DT_HIDEPREFIX ? SSA_HIDEHOTKEY : (dwFormat & DT_PREFIXONLY ? SSA_HOTKEYONLY : SSA_HOTKEY))) | (dwFormat & DT_EXPANDTABS ? SSA_TAB : 0) | (dwObjType == OBJ_METADC || dwObjType == OBJ_ENHMETADC ? SSA_METAFILE : 0) | (iCharset==-1 || GdiIsPlayMetafileDC(hdc) ? SSA_FALLBACK : SSA_LPKANSIFALLBACK) | ( dwFormat & DT_RTLREADING || (((iTextAlign = GetTextAlign(hdc)) & TA_RTLREADING) && (iTextAlign != -1)) ? SSA_RTL : 0), -1, 0, NULL, NULL, NULL, dwFormat & DT_EXPANDTABS ? &std : NULL, NULL, &psa); if (SUCCEEDED(hr)) { iWidth = psa->size.cx; if (fDraw && (!(dwFormat & DT_CALCRECT))) { ScriptStringOut(psa, xLeft, yTop, 0, NULL, 0, 0, FALSE); } ScriptStringFree((void**)&psa); } else { iWidth = 0; ASSERTHR(hr, ("LpkCharsetDraw - LpkStringAnalyse")); psa = NULL; } return iWidth; }