2009-09-02 12 views
2

有時,我們的應用程序需要使用ExtTextOut繪製非常長的字符串(例如6,000個字符)。有時ExtTextOut失敗並返回零,GetLastError也返回零。ExtTextOut以非常長的字符串失敗,除非指定較低的字體質量

要重新創建的情況下創建一個簡單的MFC單文檔應用程序,然後設置的OnDraw是:

void CTestExtTextView::OnDraw(CDC* pDC) 
{ 
CTestExtTextDoc* pDoc = GetDocument(); 
ASSERT_VALID(pDoc); 
if (!pDoc) 
    return; 

LOGFONT lfDetail = {0}; 
lfDetail.lfHeight = -(::MulDiv(100, pDC->GetDeviceCaps(LOGPIXELSY), 720)); 
lfDetail.lfCharSet = ANSI_CHARSET; 
lfDetail.lfOutPrecision = OUT_DEFAULT_PRECIS; 
lfDetail.lfQuality = CLEARTYPE_QUALITY; 
lfDetail.lfWeight = 400; 
_tcscpy_s(lfDetail.lfFaceName, LF_FACESIZE, _T("Arial")); 

CFont font; 
font.CreateFontIndirectW(&lfDetail); 

CFont * pold = pDC->SelectObject(&font); 

CString str = L"2 <office:document-content xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\" xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\" xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\" xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\" xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\" xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\" xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\" xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\" xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\" xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\" xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\" xmlns:math=\"http://www.w3.org/1998/Math/MathML\" xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\" xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\" xmlns:ooo=\"http://openoffice.org/2004/office\" xmlns:ooow=\"http://openoffice.org/2004/writer\" xmlns:oooc=\"http://openoffice.org/2004/calc\" xmlns:dom=\"http://www.w3.org/2001/xml-events\" xmlns:xforms=\"http://www.w3.org/2002/xforms\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" office:version=\"1.1\"><office:scripts/><office:font-face-decls><style:font-face style:name=\"Arial\" svg:font-family=\"Arial\" style:font-family-generic=\"swiss\" style:font-pitch=\"variable\"/><style:font-face style:name=\"Arial Unicode MS\" svg:font-family=\"&apos;Arial Unicode MS&apos;\" style:font-family-generic=\"system\" style:font-pitch=\"variable\"/><style:font-face style:name=\"Tahoma\" svg:font-family=\"Tahoma\" style:font-family-generic=\"system\" style:font-pitch=\"variable\"/></office:font-face-decls><office:automatic-styles><style:style style:name=\"co1\" style:family=\"table-column\"><style:table-column-properties fo:break-before=\"auto\" style:column-width=\"6.659cm\"/></style:style><style:style style:name=\"co2\" style:family=\"table-column\"><style:table-column-properties fo:break-before=\"auto\" style:column-width=\"7.408cm\"/></style:style><style:style style:name=\"co3\" style:family=\"table-column\"><style:table-column-properties fo:break-before=\"auto\" style:column-width=\"7.02cm\"/></style:style><style:style style:name=\"co4\" style:family=\"table-column\"><style:table-column-properties fo:break-before=\"auto\" style:column-width=\"7.214cm\"/></style:style><style:style style:name=\"co5\" style:family=\"table-column\"><style:table-column-properties fo:break-before=\"auto\" style:column-width=\"2.267cm\"/></style:style><style:style style:name=\"ro1\" style:family=\"table-row\"><style:table-row-properties style:row-height=\"0.473cm\" fo:break-before=\"auto\" style:use-optimal-row-height=\"true\"/></style:style><style:style style:name=\"ro2\" style:family=\"table-row\"><style:table-row-properties style:row-height=\"0.453cm\" fo:break-before=\"auto\" style:use-optimal-row-height=\"true\"/></style:style><style:style style:name=\"ta1\" style:family=\"table\" style:master-page-name=\"Default\"><style:table-properties table:display=\"true\" style:writing-mode=\"lr-tb\"/></style:style><style:style style:name=\"T1\" style:family=\"text\"><style:text-properties style:text-position=\"super 58%\"/></style:style></office:automatic-styles><office:body><office:spreadsheet><table:table table:name=\"Sheet1\" table:style-name=\"ta1\" table:print=\"false\"><office:forms form:automatic-focus=\"false\" form:apply-design-mode=\"false\"/><table:table-column table:style-name=\"co1\" table:default-cell-style-name=\"Default\"/><table:table-column table:style-name=\"co2\" table:default-cell-style-name=\"Default\"/><table:table-column table:style-name=\"co3\" table:default-cell-style-name=\"Default\"/><table:table-column table:style-name=\"co4\" table:default-cell-style-name=\"Default\"/><table:table-row table:style-name=\"ro1\"><table:table-cell office:value-type=\"string\"><text:p>Sample text for ExtTextOut</text:p></table:table-cell><table:table-cell office:value-type=\"string\"><text:p>Second Column Sample Text</text:p></table:table-cell><table:table-cell office:value-type=\"string\"><text:p>3<text:span text:style-name=\"T1\">rd</text:span> Column</text:p></table:table-cell><table:table-cell office:value-type=\"string\"><text:p>A More Lengthly bit of text just to make sure we increase the size of the line to something that might make ExtTextOut fail. It doesn&apos;t actually fail if we switch to Anti-Aliased Fonts. Not sure why. It also doesn&apos;t fail if we make the text shorter.</text:p></table:table-cell></table:table-row><table:table-row table:style-name=\"ro1\"><table:table-cell office:value-type=\"string\"><text:p>Sample text for ExtTextOut</text:p></table:table-cell><table:table-cell office:value-type=\"string\"><text:p>Second Column Sample Text</text:p></table:table-cell><table:table-cell office:value-type=\"string\"><text:p>3<text:span text:style-name=\"T1\">rd</text:span> Column</text:p></table:table-cell><table:table-cell office:value-type=\"string\"><text:p>A More Lengthly bit of text just to make sure we increase the size of the line to something that might make ExtTextOut fail. It doesn&apos;t actually fail if we switch to Anti-Aliased Fonts. Not sure why. It also doesn&apos;t fail if we make the text shorter.</text:p></table:table-cell></table:table-row><table:table-row table:style-name=\"ro1\"><table:table-cell office:value-type=\"string\"><text:p>Sample text for ExtTextOut</text:p></table:table-cell><table:table-cell office:value-type=\"string\"><text:p>Second Column Sample Text</text:p></table:table-cell><table:table-cell office:value-type=\"string\">"; 
pDC->ExtTextOutW(0,0, NULL, NULL, str, NULL); 
pDC->ExtTextOutW(0,50, NULL, NULL, CString(L"But this will print out"), NULL); 

pDC->SelectObject(pold); 
} 

當你運行應用程序,你應該看到一行「但是,這會打印出」在屏幕中間的一半。如果你設置了lfQuality = ANTIALIASED_QUALITY,那麼它確實會打印出一些東西,但看起來不正確。

我已經在Vista和XP上測試過了。

任何想法?

回答

1

進一步的實驗表明,它似乎並沒有專門針對線條的長度或結果寬度,即一些具有更多字符和更寬寬度的線條將會打印得很好(例如,帶有7,365個字符和40,360個字符的線條寬度打印良好,但不同的行有6,572個字符和36,113個寬度失敗)。但是,那些相同的較長行可能會因爲改變行的背景顏色而失敗。

這導致我相信這可能是由於文本行的複雜性,而不僅僅是行的長度,並且可能存在內部超時,即如果ExtTextOut發現它耗時過長它只是退出而不打印任何輸出。

我們的解決方案是將每一行切成500個字符的塊。因此,對於6000字符行,不要使用單個ExtTextOut,而是在最後一行打印12個ExtTextOut。這似乎很好地工作,只有很小的性能下降,並允許我們打印非常大的行(我在60,000個字符後停止測試)。

5

我創建了一個簡單的字體:

CFont font; 
font.CreatePointFont(720, _T("Times New Roman")); 
CFont * pold = pDC->SelectObject(&font); 

然後初始化字符串,直到它無法打印。 761個字符有效,762個失敗:

CString str('a', 761); // Works 
CString str('a', 762); // Fails 

我嘗試了一種不同的字體,它失敗的字符數更高。沒有任何意義,直到我把每個字符串的大小:

CSize s = pDC->GetTextExtent(str); 

兩個字符串的寬度是〜32700;右邊是32767的16位有符號限制。

我感覺這個16位座標限制碰到了以NT開頭的32位,所以我不知道爲什麼這樣做不起作用XP或Vista。我可以依稀記得有關這個主題的知識庫文章,但我找不到它。

我嘗試使用TextOut和DrawText並得到相同的結果。

然後我試着畫幾行,以確保他們的工作超出了16位限制:

pDC->MoveTo(10,0); 
pDC->LineTo(10,38000); 
pDC->MoveTo(10,38000); 
pDC->LineTo(100, 38000); 

它工作得很好,所以我的猜測是有一個在基於文本的GDI函數中的錯誤。

+0

非常感謝您的測試結果和意見。 – snowdude 2009-09-03 09:27:57

+0

這個答案肯定是錯誤的。在這裏看到我的答案。 – Elmue 2016-01-28 02:47:45

0
  1. 我觀察到有4000個字符同樣的問題在Windows 10(所以這個很老的問題仍然是在2016年的一個主題)
  2. 我觀察的Windows 7,但只有一臺計算機上,而它的工作原理的問題在Windows 7的另一臺計算機上。
  3. 當ExtTextOut失敗時,它返回FALSE。所以這似乎不是一個錯誤,因爲該函數已經注意到出了問題。

從這些觀察和從什麼別人寫到這裏我可以推斷出:

  1. 從脂肪貓王的結論,即在funcion 16位的限制肯定錯了,否則它會失敗的所有的Windows 7機器。
  2. 超時起作用的snowdude理論很有意義,因爲我在慢速Windows 7機器上觀察問題,而在更快的Windows 7機器上它正確地繪製了相同的字符串。 Probabaly圖形驅動程序有一個時間限制,它必須在其中繪製字符。此外,MSDN表示該字符串不應超過8192個字符。所以微軟已經說過,可能會出現字符串太長的問題。

該解決方案肯定不會使用問題中建議的另一種字體。 (低質量的字體使繪圖更快,這再次批准超時理論。)

我寫了一個代碼,最終解決了這個問題。功能高速優化。

// ATTENTION: 
// The function returns FALSE on error but you cannot use GetLastError()! 
BOOL ExtTextOutChunks(HDC h_Dc, int X, int Y, UINT u32_Flags, const RECT* pk_Rect, 
         const WCHAR* u16_String, UINT u32_StrLen, const int* ps32_DX) 
{ 
    // The maximum amount of characters that are printed at once. 
    // The slower the computer the lower the value must be. 
    const UINT CHUNK_SIZE = 500; 

    // Speed optimization 
    if (u32_StrLen <= CHUNK_SIZE) 
     return ExtTextOut(h_Dc, X, Y, u32_Flags, pk_Rect, u16_String, u32_StrLen, ps32_DX); 

    BOOL b_Return = TRUE; 
    UINT u32_TxtAlign = GetTextAlign(h_Dc); 
    BOOL b_SetFlag = (u32_TxtAlign & TA_UPDATECP) == 0; 

    // Set TA_UPDATECP to move the drawing position automagically after each drawing. 
    // This is much faster than calling GetTextExtentPoint32() each time. 
    if (b_SetFlag) 
    { 
     SetTextAlign(h_Dc, u32_TxtAlign | TA_UPDATECP); 
     MoveToEx(h_Dc, X, Y, NULL); 
    } 

    while (u32_StrLen > 0) 
    { 
     UINT u32_Count = min(u32_StrLen, CHUNK_SIZE); 

     if (!ExtTextOut(h_Dc, 0, 0, u32_Flags, pk_Rect, u16_String, u32_Count, ps32_DX)) 
     { 
      b_Return = FALSE; 
      break; 
     } 

     u32_StrLen -= u32_Count; 
     u16_String += u32_Count; 

     if (ps32_DX) ps32_DX += u32_Count; 
    } 

    // Reset the flag if it was not set before (ALWAYS!) 
    if (b_SetFlag) 
     SetTextAlign(h_Dc, u32_TxtAlign); 

    assert(b_Return); 
    return b_Return; 
}