Prev: Task
Next: compile error note
From: Goran on 28 Apr 2010 02:36 On Apr 27, 5:08 pm, Hector Santos <sant9...(a)nospam.gmail.com> wrote: > Goran wrote: > > On Apr 27, 4:20 pm, Hector Santos <sant9...(a)nospam.gmail.com> wrote: > >> The problem with the particular code you have is that it doesn't > >> support reading a RTF file saved as UNI-GARBAGE A.K.A UNICODE. > > >> It appears you need to convert each block read in into multi-byte > >> array. It works find when the RTF file is not stored in unicode. > > > Indeed, my file was not stored as unicode. But how do I save it so? > > (Wordpad or swriter do not offer an easy option, e.g. something on the > > menu, or in "save as" dialog). > > > Goran. > > I tried Write.exe "save as" but it saves it as Unicode Code text > removing the RTF stuff. Ah, OK. Yeah, that figures. I took a look right now at my file, in binary, and now I am sure that write.exe (a.k.a Wordpad) does not save RTF as Unicode (it has ... ahem... MBCS bytes in it, and no BOM). Instead, it changes code page inside the RTF "on the fly", and uses RTF escaping mechanism (whatever that is) to save non Win-1252 characters (my system is on Win-1252). I also took a look at Wikipedia page as you suggested (Wikipedia FTW!), and indeed there they speak about Unicode encoding, but they effectively stop at the basic multilingual plane ("If the Unicode character is over 65535, it cannot be expressed in RTF.") Meh. I guess that means RTF is not the best "international" file format. This is more directed towards original poster - if his EULA has to be in a language that steps out of the BMP, then perhaps it's going to be tough with RTF; it will depend what level of RTF spec is supported by the RichEdit on the target system; even in the latest spec ("Word2007RTFSpec9.docx", wikipedia links to it), they still speak about 64k characters. I don't care much about RTF, nor do I know it, I just wanted to express my amazement on it not really following Unicode. Goran.
From: Goran on 28 Apr 2010 02:53 On Apr 28, 8:06 am, JY <s...(a)nospamgroup.com> wrote: > "Hector Santos" wrote: > > Jy, you should read about RTF in wikipedia in regards to UNICODE: > > > http://en.wikipedia.org/wiki/Rich_Text_Format > > > Unless you really need it, I would suggest using an ANSI version of > > your EULA, that way people can read it using ANY editor with no > > excuses. :) > > I need to support it in multiple languages and so ANSI is of no help. > - Jy It depends on what these languages are. See my comment: http://groups.google.com/group/microsoft.public.vc.mfc/msg/3117dc12e602e288 (it's in this very thread). I didn't try, but if you look at the spec published by MS, page 13 has a list of languages supported by RTF. If these are yours, you're OK. If not, I would suggest that you simply use Unicode text without RTF markup and a standard Edit, not rich edit (I hope you are using RTF because you want to change fonts, boldness etc, NOT because you want your text in multiple languages?). Or alternatively, you could use HTML saved in unicode (UTF-8) and display it in CHtmlEditCtrl (in fact, if you asked me how to show "formatted" text in any language, that's what I'd suggest first, without any thinking). +1 for what Santos said: you saved as Unicode and it seems that rich edit control can't make sense out of that. Now, converting your Unicode text back to MBCS is a bad idea: if you don't get the code page right, you will still display garbage. Also, about your error handling in your other examples: You are preparing only for CFileException, but you don't really know that it's the only exception type you might have. "catch (CException*)" is better and you don't really lose anything in the way of error reporting, but you gain safety, especially in the light on future changes to the code. Goran.
From: Joseph M. Newcomer on 28 Apr 2010 08:51 I just reviewed the code, and I don't even SEE anyplace where there is a major disconnect because of Unicode! Could you try to correlate your comment with particular lines of text? Unicode is important because you can't address a large number of international markets without having support for it. As I regularly tell my students: If your manager comes in and says, "We just made a big sale to <Korea, Japan, China> and we need Unicode support in the app NOW! How long will it take?" you have two possible answers: 1. Give me a couple days and I'll tell you how many weeks will be required 2. When can you get the translator in here? Which do you want to give, you are the programmer? Which do you want to hear, if you are the manager? The reason I don't understand why Unicode is a problem is that I have been writing Unicode-aware apps since 1996, and I don't know any other way to write an app today. And a number of those have been transitioned to Unicode seamlessly and painlessly. So I really don't have any patience when people start whining about Unicode; been there, done that, it's easy and straightforward! The mass of code below has NOTHING to do with Unicode and EVERYTHING to do with how complex the interface to a Rich Edit Control is. joe On Tue, 27 Apr 2010 10:26:50 -0400, Hector Santos <sant9442(a)nospam.gmail.com> wrote: >Joe, IMV, this is a MAJOR step back in the programming solid code in >order to support this UNICODE stuff. Wow!! Are you kidding me? Why >is UNICODE such an on-going issue with so many people even after all >these years. Its terrible. Wow! > >Personally, in this topic, I think understanding more what the >CRichEditCtrl wants for UNICODE support is whats important here. I >don't see any consistent information about this. > >-- >HLS > >Joseph M. Newcomer wrote: > >> See below... >> On Tue, 27 Apr 2010 01:18:08 -0700, JY <sd(a)nospamgroup.com> wrote: >> >>> Hi, >>> >>> I have a Wizard based application, in which I have some property pages. In >>> one of the pages, I have a CRichEditCtrl, and I try to populate its contents >>>from a RTF file. The porblem is, it works correctly if the RTF file is small >>> (about 4-5 KB), but when I use a larger file, its contents either don't >>> display at all, or gets truncated. >>> >>> The code is shown below. What can I do to show the entire contents of the >>> RTF file in the control? Also, it should work for all languages - I have >>> UNICODE defined in the project. m_RECtrl is the rich edit control. >>> >>> static DWORD CALLBACK MyStreamInCallback(DWORD dwCookie, LPBYTE pbBuff, LONG >> **** >> Why did you declare the cookie as a DWORD and not a DWORD_PTR? This is incorrect, >> although in 32-bit Windows it would not have any impact >> **** >>> cb, LONG *pcb) >>> { >>> CFile* pFile = (CFile*)(DWORD_PTR)dwCookie; >>> *pcb = pFile->Read(pbBuff, cb); >> **** >> Note that the Read should be contained in a try/catch(CFileException * e) structure if you >> plan to detect errors correctly >> **** >>> return 0; >>> } >>> >>> BOOL CREPage::OnInitDialog() >>> { >>> CBasePage::OnInitDialog(); >>> >>> if (m_strRTFFilePath.GetLength()) >>> { >>> m_RECtrl.ShowScrollBar(SB_VERT, TRUE); >>> CFile eulaFile(m_strRTFFilePath, CFile::modeRead); >> **** >> Note that this constructor must be in a try/catch(CFileException * e) block, since if >> there is a problem (such as the file does not exist along the path) then it will throw an >> exception >> **** >>> EDITSTREAM es; >>> >>> es.dwCookie = (DWORD_PTR)&eulaFile; >>> es.pfnCallback = (EDITSTREAMCALLBACK)MyStreamInCallback; >>> m_RECtrl.StreamIn(SF_RTF, es); >>> } >> >>> return TRUE; >>> } >>> >>> int CREPage::OnCreate(LPCREATESTRUCT lpCreateStruct) >>> { >>> if (CBasePage::OnCreate(lpCreateStruct) == -1) >>> return -1; >>> >>> CRect rect; >>> GetClientRect(&rect); >>> rect.bottom -= 25; >>> >>> m_RECtrl.Create(WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE, rect, >>> this, IDC_RICHEDIT_EULA); >>> m_RECtrl.SetOptions(ECOOP_OR, ECO_AUTOVSCROLL | ECO_AUTOHSCROLL | >>> ECO_READONLY); >>> m_RECtrl.ModifyStyleEx(0, WS_EX_CLIENTEDGE, SWP_FRAMECHANGED); >>> >>> return 0; >>> } >>> >>> TIA, >>> JY >> **** >> Here's the stream callback from my handler: >> >> class StreamInCookie { >> public: >> CFile * file; >> DWORD_PTR remaining; >> DWORD err; >> }; >> **** >> This is retrieving data from a precomputed cache which is in rtf format, and if there is >> any timestamp change on an input, it resets all the content and will recompute it from >> scratch, ignoring the cache. The cache starts with a binary timestamp and a binary length >> value. It also marks the hyperlinks. If you want to see the hyperlink code, I've >> attached it >> ***** >> BOOL CIndex::RetrieveFromCache() >> { >> CString filename; >> if(!GetCacheFile(filename)) >> return FALSE; >> >> CFile f; >> >> if(!f.Open(filename, CFile::modeRead)) >> return FALSE; // open failed >> >> try { /* try read */ >> StreamInCookie streamIn; // >> streamIn.file = &f; >> >> //**************************************************************** >> // [Timestamp] >> //**************************************************************** >> >> TimeStamp ft; >> if(f.Read(&ft, sizeof(TimeStamp)) == 0) >> { /* failed */ >> ResetAllContent(); >> f.Close(); >> return FALSE; >> } /* failed */ >> >> //**************************************************************** >> // [eod] End of data position for RTF data >> //**************************************************************** >> >> if(f.Read(&streamIn.remaining, sizeof(DWORD)) == 0) >> { /* failed */ >> ResetAllContent(); >> f.Close(); >> return FALSE; >> } /* failed */ >> >> // Now convert from offset to length >> streamIn.remaining -= f.GetPosition(); >> >> //**************************************************************** >> // <...> RTF data >> //**************************************************************** >> EDITSTREAM es; >> es.dwCookie = (DWORD_PTR)&streamIn; >> es.pfnCallback = StreamInCallback; >> c_Index.StreamIn(SF_RTF, es); >> >> if(streamIn.err != ERROR_SUCCESS) >> { /* failed */ >> f.Close(); >> ResetAllContent(); >> return FALSE; >> } /* failed */ >> >> //**************************************************************** >> // Read the hyperlink length >> //**************************************************************** >> DWORD len; >> if(f.Read(&len, sizeof(DWORD)) == 0) >> { /* failed to get links */ >> f.Close(); >> ResetAllContent(); >> return FALSE; >> } /* failed to get links */ >> >> hyperlinks.SetSize(len); >> >> //**************************************************************** >> // Read the hyperlink data >> //**************************************************************** >> >> for(DWORD i = 0; i < len; i++) >> { /* read each */ >> hyperlinks[i] = new Reference; >> if(!hyperlinks[i]->Read(f)) >> { /* failed */ >> if(::GetLastError() == ERROR_HANDLE_EOF) >> break; // "failure" is EOF (see spec on Reference::Read) >> f.Close(); >> ResetAllContent(); >> return FALSE; >> } /* failed */ >> MarkLink(c_Index, hyperlinks[i]->range); >> } /* read each */ >> >> //**************************************************************** >> // [eod] Read EOD for Fastlink RTF data >> //**************************************************************** >> >> if(f.Read(&streamIn.remaining, sizeof(DWORD)) == 0) >> { /* failed */ >> ResetAllContent(); >> f.Close(); >> return FALSE; >> } /* failed */ >> >> streamIn.remaining -= f.GetPosition(); // convert from offset to length >> >> //**************************************************************** >> // <...> RTF data >> //**************************************************************** >> c_FastIndex.StreamIn(SF_RTF, es); >> >> if(streamIn.err != ERROR_SUCCESS) >> { /* failed */ >> ResetAllContent(); >> f.Close(); >> return FALSE; >> } /* failed */ >> >> //**************************************************************** >> // Read the hyperlink length >> //**************************************************************** >> >> if(f.Read(&len, sizeof(DWORD)) == 0) >> { /* failed to get links */ >> ResetAllContent(); >> f.Close(); >> return FALSE; >> } /* failed to get links */ >> >> fastlinks.SetSize(len); >> >> //**************************************************************** >> // Read the hyperlink data >> //**************************************************************** >> >> for(i = 0; i < len; i++) >> { /* read each */ >> if(!fastlinks[i].Read(f)) >> { /* failed */ >> if(::GetLastError() == ERROR_HANDLE_EOF) >> break; >> f.Close(); >> ResetAllContent(); >> return FALSE; >> } /* failed */ >> MarkLink(c_FastIndex, fastlinks[i].range); >> } /* read each */ >> //**************************************************************** >> } /* try read */ >> catch(CFileException * e) >> { /* read error */ >> e->Delete(); >> ResetAllContent(); >> f.Close(); >> return FALSE; >> } /* read error */ >> f.Close(); >> c_Index.SetSel(0,0); >> c_FastIndex.SetSel(0,0); >> return TRUE; >> } // CIndex::RetrieveFromCache >> >> void CIndex::MarkLink(CRichEditCtrlEx & ctl, CHARRANGE & range) >> { >> CHARFORMAT2 linkfmt; >> >> ctl.SetSel(range); >> >> linkfmt.cbSize = sizeof(CHARFORMAT2); >> linkfmt.dwMask = CFM_LINK; >> linkfmt.dwEffects = CFE_LINK; >> linkfmt.dwMask = CFM_LINK; >> ctl.SetSelectionCharFormat(linkfmt); >> } // CIndex::MarkLink >> >> /* static */ DWORD CALLBACK CIndex::StreamInCallback(DWORD_PTR cookie, LPBYTE buffer, LONG >> count, LONG * pcb) >> { >> StreamInCookie * streamIn = (StreamInCookie *)cookie; >> if(streamIn->remaining == 0) >> { /* all done */ >> *pcb = 0; >> streamIn->err = ERROR_SUCCESS; >> return 1; // nonzero value terminates read >> } /* all done */ >> >> DWORD bytesToRead = min(streamIn->remaining, (DWORD)count); >> >> UINT bytesRead; >> try { >> bytesRead = streamIn->file->Read(buffer, bytesToRead); >> } >> catch(CFileException * e) >> { /* catch */ >> streamIn->err = e->m_lOsError; >> e->Delete(); >> streamIn->remaining = 0; >> return 1; >> } /* catch */ >> >> if(bytesRead == 0) >> { /* read error */ >> streamIn->err = ::GetLastError(); >> *pcb = 0; >> return 1; // return nonzero to stop operation >> } /* read error */ >> >> streamIn->remaining -= bytesRead; >> *pcb = bytesRead; >> streamIn->err = ERROR_SUCCESS; >> return 0; >> } // CIndex::StreamInCallback >> Joseph M. Newcomer [MVP] >> email: newcomer(a)flounder.com >> Web: http://www.flounder.com >> MVP Tips: http://www.flounder.com/mvp_tips.htm Joseph M. Newcomer [MVP] email: newcomer(a)flounder.com Web: http://www.flounder.com MVP Tips: http://www.flounder.com/mvp_tips.htm
From: Joseph M. Newcomer on 28 Apr 2010 08:54 Although you test the "remaining" field, I don't see where you set it. Have you single-stepped any of this to see where it is terminating the read, and why? Or put breakpoints on the 'return 1' statements? joe On Tue, 27 Apr 2010 08:20:06 -0700, JY <sd(a)nospamgroup.com> wrote: >Hi Joe, > >I've taken some snippets from what you'd posted and I still have the same >problem as earlier. Only a truncated portion of the file appears in the >CRichEditCtrl. Here's my latest code: > >class StreamInCookie >{ >public: > CFile *file; > DWORD_PTR remaining; > DWORD err; >}; > >static DWORD CALLBACK EULAStreamInCallback(DWORD_PTR dwCookie, LPBYTE >pbBuff, LONG cb, LONG *pcb) >{ > StreamInCookie * streamIn = (StreamInCookie *)dwCookie; > if(streamIn->remaining == 0) > { > *pcb = 0; > streamIn->err = ERROR_SUCCESS; > return 1; // nonzero value terminates read > } > > DWORD bytesToRead = min(streamIn->remaining, (DWORD)cb); > > UINT bytesRead; > try > { > bytesRead = streamIn->file->Read(pbBuff, bytesToRead); > } > catch(CFileException * e) > { > streamIn->err = e->m_lOsError; > e->Delete(); > streamIn->remaining = 0; > return 1; > } > > if(bytesRead == 0) > { > streamIn->err = ::GetLastError(); > *pcb = 0; > return 1; // return nonzero to stop operation > } > > streamIn->remaining -= bytesRead; > *pcb = bytesRead; > streamIn->err = ERROR_SUCCESS; > return 0; >} > >BOOL CREPage::OnInitDialog() >{ > CBasePage::OnInitDialog(); > > if (m_strRTFFilePath.GetLength()) > { > m_RECtrl.ShowScrollBar(SB_VERT, TRUE); > > try > { > StreamInCookie streamIn; > CFile eulaFile(m_strRTFFilePath, CFile::modeRead|CFile::shareExclusive); > streamIn.file = &eulaFile; > > EDITSTREAM es; > es.dwCookie = (DWORD_PTR)&streamIn; > es.pfnCallback = (EDITSTREAMCALLBACK)EULAStreamInCallback; > m_RECtrl.StreamIn(SF_RTF, es); > } > catch (CFileException *pEx) > { > //not shown for brevity > } > } > > return TRUE; >} > >Thanks, >JY Joseph M. Newcomer [MVP] email: newcomer(a)flounder.com Web: http://www.flounder.com MVP Tips: http://www.flounder.com/mvp_tips.htm
From: JY on 28 Apr 2010 09:59
"Joseph M. Newcomer" wrote: > Although you test the "remaining" field, I don't see where you set it. > > Have you single-stepped any of this to see where it is terminating the read, and why? Or > put breakpoints on the 'return 1' statements? > joe I do it in OnInitDialog(), I missed that line earlier: StreamInCookie streamIn; streamIn.file = &eulaFile; streamIn.remaining = (DWORD_PTR)eulaFile.GetLength(); EDITSTREAM es; ...... Yes, I've set breakpoints and noticed that the callbacks don't come after some time. At the last point the callback was received, the "remaining" field was 36671, and 0 was successfully returned from the callback. The size of the EULA RTF file is quite big, about 13 pages (about 140 KB). Not sure if it's got anything to do with the size. I've created a sample dialog based application to test this, but I don't think I could upload that here, so you could see the complete code. Interestingly, the sample that I found here also gives the exact same behavior, when I try to load the RTF file. It gets truncated at the exact same point. http://www.codeproject.com/KB/edit/rulerricheditctrl.aspx?msg=1110746 Thanks again, JY > On Tue, 27 Apr 2010 08:20:06 -0700, JY <sd(a)nospamgroup.com> wrote: > > >Hi Joe, > > > >I've taken some snippets from what you'd posted and I still have the same > >problem as earlier. Only a truncated portion of the file appears in the > >CRichEditCtrl. Here's my latest code: > > > >class StreamInCookie > >{ > >public: > > CFile *file; > > DWORD_PTR remaining; > > DWORD err; > >}; > > > >static DWORD CALLBACK EULAStreamInCallback(DWORD_PTR dwCookie, LPBYTE > >pbBuff, LONG cb, LONG *pcb) > >{ > > StreamInCookie * streamIn = (StreamInCookie *)dwCookie; > > if(streamIn->remaining == 0) > > { > > *pcb = 0; > > streamIn->err = ERROR_SUCCESS; > > return 1; // nonzero value terminates read > > } > > > > DWORD bytesToRead = min(streamIn->remaining, (DWORD)cb); > > > > UINT bytesRead; > > try > > { > > bytesRead = streamIn->file->Read(pbBuff, bytesToRead); > > } > > catch(CFileException * e) > > { > > streamIn->err = e->m_lOsError; > > e->Delete(); > > streamIn->remaining = 0; > > return 1; > > } > > > > if(bytesRead == 0) > > { > > streamIn->err = ::GetLastError(); > > *pcb = 0; > > return 1; // return nonzero to stop operation > > } > > > > streamIn->remaining -= bytesRead; > > *pcb = bytesRead; > > streamIn->err = ERROR_SUCCESS; > > return 0; > >} > > > >BOOL CREPage::OnInitDialog() > >{ > > CBasePage::OnInitDialog(); > > > > if (m_strRTFFilePath.GetLength()) > > { > > m_RECtrl.ShowScrollBar(SB_VERT, TRUE); > > > > try > > { > > StreamInCookie streamIn; > > CFile eulaFile(m_strRTFFilePath, CFile::modeRead|CFile::shareExclusive); > > streamIn.file = &eulaFile; > > > > EDITSTREAM es; > > es.dwCookie = (DWORD_PTR)&streamIn; > > es.pfnCallback = (EDITSTREAMCALLBACK)EULAStreamInCallback; > > m_RECtrl.StreamIn(SF_RTF, es); > > } > > catch (CFileException *pEx) > > { > > //not shown for brevity > > } > > } > > > > return TRUE; > >} > > > >Thanks, > >JY > Joseph M. Newcomer [MVP] > email: newcomer(a)flounder.com > Web: http://www.flounder.com > MVP Tips: http://www.flounder.com/mvp_tips.htm > . > |