From: Goran on
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
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
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
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
"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
> .
>
First  |  Prev  |  Next  |  Last
Pages: 1 2 3 4 5 6
Prev: Task
Next: compile error note