1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/richtext/richtexthtml.cpp
3 // Purpose:     HTML I/O for wxRichTextCtrl
4 // Author:      Julian Smart
5 // Modified by:
6 // Created:     2005-09-30
7 // RCS-ID:      $Id: richtexthtml.cpp 64162 2010-04-27 16:10:27Z JS $
8 // Copyright:   (c) Julian Smart
9 // Licence:     wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11 
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14 
15 #ifdef __BORLANDC__
16     #pragma hdrstop
17 #endif
18 
19 #if wxUSE_RICHTEXT
20 
21 #include "wx/richtext/richtexthtml.h"
22 #include "wx/richtext/richtextstyles.h"
23 
24 #ifndef WX_PRECOMP
25 #endif
26 
27 #include "wx/filename.h"
28 #include "wx/wfstream.h"
29 #include "wx/txtstrm.h"
30 
31 #if wxUSE_FILESYSTEM
32 #include "wx/filesys.h"
33 #include "wx/fs_mem.h"
34 #endif
35 
36 IMPLEMENT_DYNAMIC_CLASS(wxRichTextHTMLHandler, wxRichTextFileHandler)
37 
38 int wxRichTextHTMLHandler::sm_fileCounter = 1;
39 
wxRichTextHTMLHandler(const wxString & name,const wxString & ext,int type)40 wxRichTextHTMLHandler::wxRichTextHTMLHandler(const wxString& name, const wxString& ext, int type)
41     : wxRichTextFileHandler(name, ext, type), m_buffer(NULL), m_font(false), m_inTable(false)
42 {
43     m_fontSizeMapping.Add(8);
44     m_fontSizeMapping.Add(10);
45     m_fontSizeMapping.Add(13);
46     m_fontSizeMapping.Add(17);
47     m_fontSizeMapping.Add(22);
48     m_fontSizeMapping.Add(30);
49     m_fontSizeMapping.Add(100);
50 }
51 
52 /// Can we handle this filename (if using files)? By default, checks the extension.
CanHandle(const wxString & filename) const53 bool wxRichTextHTMLHandler::CanHandle(const wxString& filename) const
54 {
55     wxString path, file, ext;
56     wxSplitPath(filename, & path, & file, & ext);
57 
58     return (ext.Lower() == wxT("html") || ext.Lower() == wxT("htm"));
59 }
60 
61 
62 #if wxUSE_STREAMS
DoLoadFile(wxRichTextBuffer * WXUNUSED (buffer),wxInputStream & WXUNUSED (stream))63 bool wxRichTextHTMLHandler::DoLoadFile(wxRichTextBuffer *WXUNUSED(buffer), wxInputStream& WXUNUSED(stream))
64 {
65     return false;
66 }
67 
68 /*
69  * We need to output only _changes_ in character formatting.
70  */
71 
DoSaveFile(wxRichTextBuffer * buffer,wxOutputStream & stream)72 bool wxRichTextHTMLHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
73 {
74     m_buffer = buffer;
75 
76     ClearTemporaryImageLocations();
77 
78     buffer->Defragment();
79 
80 #if wxUSE_UNICODE
81     wxCSConv* customEncoding = NULL;
82     wxMBConv* conv = NULL;
83     if (!GetEncoding().IsEmpty())
84     {
85         customEncoding = new wxCSConv(GetEncoding());
86         if (!customEncoding->IsOk())
87         {
88             delete customEncoding;
89             customEncoding = NULL;
90         }
91     }
92     if (customEncoding)
93         conv = customEncoding;
94     else
95         conv = & wxConvUTF8;
96 #endif
97 
98     {
99 #if wxUSE_UNICODE
100         wxTextOutputStream str(stream, wxEOL_NATIVE, *conv);
101 #else
102         wxTextOutputStream str(stream, wxEOL_NATIVE);
103 #endif
104 
105         wxTextAttrEx currentParaStyle = buffer->GetAttributes();
106         wxTextAttrEx currentCharStyle = buffer->GetAttributes();
107 
108         if ((GetFlags() & wxRICHTEXT_HANDLER_NO_HEADER_FOOTER) == 0)
109             str << wxT("<html><head></head><body>\n");
110 
111         OutputFont(currentParaStyle, str);
112 
113         m_font = false;
114         m_inTable = false;
115 
116         m_indents.Clear();
117         m_listTypes.Clear();
118 
119         wxRichTextObjectList::compatibility_iterator node = buffer->GetChildren().GetFirst();
120         while (node)
121         {
122             wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
123             wxASSERT (para != NULL);
124 
125             if (para)
126             {
127                 wxTextAttrEx paraStyle(para->GetCombinedAttributes());
128 
129                 BeginParagraphFormatting(currentParaStyle, paraStyle, str);
130 
131                 wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst();
132                 while (node2)
133                 {
134                     wxRichTextObject* obj = node2->GetData();
135                     wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
136                     if (textObj && !textObj->IsEmpty())
137                     {
138                         wxTextAttrEx charStyle(para->GetCombinedAttributes(obj->GetAttributes()));
139                         BeginCharacterFormatting(currentCharStyle, charStyle, paraStyle, str);
140 
141                         wxString text = textObj->GetText();
142 
143                         if (charStyle.HasTextEffects() && (charStyle.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS))
144                             text.MakeUpper();
145 
146                         wxString toReplace = wxRichTextLineBreakChar;
147                         text.Replace(toReplace, wxT("<br>"));
148 
149                         str << text;
150 
151                         EndCharacterFormatting(currentCharStyle, charStyle, paraStyle, str);
152                     }
153 
154                     wxRichTextImage* image = wxDynamicCast(obj, wxRichTextImage);
155                     if( image && (!image->IsEmpty() || image->GetImageBlock().GetData()))
156                         WriteImage( image, stream );
157 
158                     node2 = node2->GetNext();
159                 }
160 
161                 EndParagraphFormatting(currentParaStyle, paraStyle, str);
162 
163                 str << wxT("\n");
164             }
165             node = node->GetNext();
166         }
167 
168         CloseLists(-1, str);
169 
170         str << wxT("</font>");
171 
172         if ((GetFlags() & wxRICHTEXT_HANDLER_NO_HEADER_FOOTER) == 0)
173             str << wxT("</body></html>");
174 
175         str << wxT("\n");
176     }
177 
178 #if wxUSE_UNICODE
179     if (customEncoding)
180         delete customEncoding;
181 #endif
182 
183     m_buffer = NULL;
184 
185     return true;
186 }
187 
BeginCharacterFormatting(const wxTextAttrEx & currentStyle,const wxTextAttrEx & thisStyle,const wxTextAttrEx & WXUNUSED (paraStyle),wxTextOutputStream & str)188 void wxRichTextHTMLHandler::BeginCharacterFormatting(const wxTextAttrEx& currentStyle, const wxTextAttrEx& thisStyle, const wxTextAttrEx& WXUNUSED(paraStyle), wxTextOutputStream& str)
189 {
190     wxString style;
191 
192     // Is there any change in the font properties of the item?
193     if (thisStyle.GetFont().GetFaceName() != currentStyle.GetFont().GetFaceName())
194     {
195         wxString faceName(thisStyle.GetFont().GetFaceName());
196         style += wxString::Format(wxT(" face=\"%s\""), faceName.c_str());
197     }
198     if (thisStyle.GetFont().GetPointSize() != currentStyle.GetFont().GetPointSize())
199         style += wxString::Format(wxT(" size=\"%ld\""), PtToSize(thisStyle.GetFont().GetPointSize()));
200     if (thisStyle.GetTextColour() != currentStyle.GetTextColour() )
201     {
202         wxString color(thisStyle.GetTextColour().GetAsString(wxC2S_HTML_SYNTAX));
203         style += wxString::Format(wxT(" color=\"%s\""), color.c_str());
204     }
205 
206     if (style.size())
207     {
208         str << wxString::Format(wxT("<font %s >"), style.c_str());
209         m_font = true;
210     }
211 
212     if (thisStyle.GetFont().GetWeight() == wxBOLD)
213         str << wxT("<b>");
214     if (thisStyle.GetFont().GetStyle() == wxITALIC)
215         str << wxT("<i>");
216     if (thisStyle.GetFont().GetUnderlined())
217         str << wxT("<u>");
218 
219     if (thisStyle.HasURL())
220         str << wxT("<a href=\"") << thisStyle.GetURL() << wxT("\">");
221 }
222 
EndCharacterFormatting(const wxTextAttrEx & WXUNUSED (currentStyle),const wxTextAttrEx & thisStyle,const wxTextAttrEx & WXUNUSED (paraStyle),wxTextOutputStream & stream)223 void wxRichTextHTMLHandler::EndCharacterFormatting(const wxTextAttrEx& WXUNUSED(currentStyle), const wxTextAttrEx& thisStyle, const wxTextAttrEx& WXUNUSED(paraStyle), wxTextOutputStream& stream)
224 {
225     if (thisStyle.HasURL())
226         stream << wxT("</a>");
227 
228     if (thisStyle.GetFont().GetUnderlined())
229         stream << wxT("</u>");
230     if (thisStyle.GetFont().GetStyle() == wxITALIC)
231         stream << wxT("</i>");
232     if (thisStyle.GetFont().GetWeight() == wxBOLD)
233         stream << wxT("</b>");
234 
235     if (m_font)
236     {
237         m_font = false;
238         stream << wxT("</font>");
239     }
240 }
241 
242 /// Begin paragraph formatting
BeginParagraphFormatting(const wxTextAttrEx & WXUNUSED (currentStyle),const wxTextAttrEx & thisStyle,wxTextOutputStream & str)243 void wxRichTextHTMLHandler::BeginParagraphFormatting(const wxTextAttrEx& WXUNUSED(currentStyle), const wxTextAttrEx& thisStyle, wxTextOutputStream& str)
244 {
245     if (thisStyle.HasPageBreak())
246     {
247         str << wxT("<div style=\"page-break-after:always\"></div>\n");
248     }
249 
250     if (thisStyle.HasLeftIndent() && thisStyle.GetLeftIndent() != 0)
251     {
252         if (thisStyle.HasBulletStyle())
253         {
254             int indent = thisStyle.GetLeftIndent();
255 
256             // Close levels high than this
257             CloseLists(indent, str);
258 
259             if (m_indents.GetCount() > 0 && indent == m_indents.Last())
260             {
261                 // Same level, no need to start a new list
262             }
263             else if (m_indents.GetCount() == 0 || indent > m_indents.Last())
264             {
265                 m_indents.Add(indent);
266 
267                 wxString tag;
268                 int listType = TypeOfList(thisStyle, tag);
269                 m_listTypes.Add(listType);
270 
271                 // wxHTML needs an extra <p> before a list when using <p> ... </p> in previous paragraphs.
272                 // TODO: pass a flag that indicates we're using wxHTML.
273                 str << wxT("<p>\n");
274 
275                 str << tag;
276             }
277 
278             str << wxT("<li> ");
279         }
280         else
281         {
282             CloseLists(-1, str);
283 
284             wxString align = GetAlignment(thisStyle);
285             str << wxString::Format(wxT("<p align=\"%s\""), align.c_str());
286 
287             wxString styleStr;
288 
289             if ((GetFlags() & wxRICHTEXT_HANDLER_USE_CSS) && thisStyle.HasParagraphSpacingBefore())
290             {
291                 float spacingBeforeMM = thisStyle.GetParagraphSpacingBefore() / 10.0;
292 
293                 styleStr += wxString::Format(wxT("margin-top: %.2fmm; "), spacingBeforeMM);
294             }
295             if ((GetFlags() & wxRICHTEXT_HANDLER_USE_CSS) && thisStyle.HasParagraphSpacingAfter())
296             {
297                 float spacingAfterMM = thisStyle.GetParagraphSpacingAfter() / 10.0;
298 
299                 styleStr += wxString::Format(wxT("margin-bottom: %.2fmm; "), spacingAfterMM);
300             }
301 
302             float indentLeftMM = (thisStyle.GetLeftIndent() + thisStyle.GetLeftSubIndent())/10.0;
303             if ((GetFlags() & wxRICHTEXT_HANDLER_USE_CSS) && (indentLeftMM > 0.0))
304             {
305                 styleStr += wxString::Format(wxT("margin-left: %.2fmm; "), indentLeftMM);
306             }
307             float indentRightMM = thisStyle.GetRightIndent()/10.0;
308             if ((GetFlags() & wxRICHTEXT_HANDLER_USE_CSS) && thisStyle.HasRightIndent() && (indentRightMM > 0.0))
309             {
310                 styleStr += wxString::Format(wxT("margin-right: %.2fmm; "), indentRightMM);
311             }
312             // First line indentation
313             float firstLineIndentMM = - thisStyle.GetLeftSubIndent() / 10.0;
314             if ((GetFlags() & wxRICHTEXT_HANDLER_USE_CSS) && (firstLineIndentMM > 0.0))
315             {
316                 styleStr += wxString::Format(wxT("text-indent: %.2fmm; "), firstLineIndentMM);
317             }
318 
319             if (!styleStr.IsEmpty())
320                 str << wxT(" style=\"") << styleStr << wxT("\"");
321 
322             str << wxT(">");
323 
324             // TODO: convert to pixels
325             int indentPixels = indentLeftMM*10/4;
326 
327             if ((GetFlags() & wxRICHTEXT_HANDLER_USE_CSS) == 0)
328             {
329                 // Use a table to do indenting if we don't have CSS
330                 str << wxString::Format(wxT("<table border=0 cellpadding=0 cellspacing=0><tr><td width=\"%d\"></td><td>"), indentPixels);
331                 m_inTable = true;
332             }
333 
334             if (((GetFlags() & wxRICHTEXT_HANDLER_USE_CSS) == 0) && (thisStyle.GetLeftSubIndent() < 0))
335             {
336                 str << SymbolicIndent( - thisStyle.GetLeftSubIndent());
337             }
338         }
339     }
340     else
341     {
342         CloseLists(-1, str);
343 
344         wxString align = GetAlignment(thisStyle);
345         str << wxString::Format(wxT("<p align=\"%s\""), align.c_str());
346 
347         wxString styleStr;
348 
349         if ((GetFlags() & wxRICHTEXT_HANDLER_USE_CSS) && thisStyle.HasParagraphSpacingBefore())
350         {
351             float spacingBeforeMM = thisStyle.GetParagraphSpacingBefore() / 10.0;
352 
353             styleStr += wxString::Format(wxT("margin-top: %.2fmm; "), spacingBeforeMM);
354         }
355         if ((GetFlags() & wxRICHTEXT_HANDLER_USE_CSS) && thisStyle.HasParagraphSpacingAfter())
356         {
357             float spacingAfterMM = thisStyle.GetParagraphSpacingAfter() / 10.0;
358 
359             styleStr += wxString::Format(wxT("margin-bottom: %.2fmm; "), spacingAfterMM);
360         }
361 
362         if (!styleStr.IsEmpty())
363             str << wxT(" style=\"") << styleStr << wxT("\"");
364 
365         str << wxT(">");
366     }
367     OutputFont(thisStyle, str);
368 }
369 
370 /// End paragraph formatting
EndParagraphFormatting(const wxTextAttrEx & WXUNUSED (currentStyle),const wxTextAttrEx & thisStyle,wxTextOutputStream & stream)371 void wxRichTextHTMLHandler::EndParagraphFormatting(const wxTextAttrEx& WXUNUSED(currentStyle), const wxTextAttrEx& thisStyle, wxTextOutputStream& stream)
372 {
373     if (thisStyle.HasFont())
374         stream << wxT("</font>");
375 
376     if (m_inTable)
377     {
378         stream << wxT("</td></tr></table></p>\n");
379         m_inTable = false;
380     }
381     else if (!thisStyle.HasBulletStyle())
382         stream << wxT("</p>\n");
383 }
384 
385 /// Closes lists to level (-1 means close all)
CloseLists(int level,wxTextOutputStream & str)386 void wxRichTextHTMLHandler::CloseLists(int level, wxTextOutputStream& str)
387 {
388     // Close levels high than this
389     int i = m_indents.GetCount()-1;
390     while (i >= 0)
391     {
392         int l = m_indents[i];
393         if (l > level)
394         {
395             if (m_listTypes[i] == 0)
396                 str << wxT("</ol>");
397             else
398                 str << wxT("</ul>");
399             m_indents.RemoveAt(i);
400             m_listTypes.RemoveAt(i);
401         }
402         else
403             break;
404         i --;
405      }
406 }
407 
408 /// Output font tag
OutputFont(const wxTextAttrEx & style,wxTextOutputStream & stream)409 void wxRichTextHTMLHandler::OutputFont(const wxTextAttrEx& style, wxTextOutputStream& stream)
410 {
411     if (style.HasFont())
412     {
413         stream << wxString::Format(wxT("<font face=\"%s\" size=\"%ld\""), style.GetFont().GetFaceName().c_str(), PtToSize(style.GetFont().GetPointSize()));
414         if (style.HasTextColour())
415             stream << wxString::Format(wxT(" color=\"%s\""), style.GetTextColour().GetAsString(wxC2S_HTML_SYNTAX).c_str());
416         stream << wxT(" >");
417     }
418 }
419 
TypeOfList(const wxTextAttrEx & thisStyle,wxString & tag)420 int wxRichTextHTMLHandler::TypeOfList( const wxTextAttrEx& thisStyle, wxString& tag )
421 {
422     // We can use number attribute of li tag but not all the browsers support it.
423     // also wxHtmlWindow doesn't support type attribute.
424 
425     bool m_is_ul = false;
426     if (thisStyle.GetBulletStyle() == (wxTEXT_ATTR_BULLET_STYLE_ARABIC|wxTEXT_ATTR_BULLET_STYLE_PERIOD))
427         tag = wxT("<ol type=\"1\">");
428     else if (thisStyle.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER)
429         tag = wxT("<ol type=\"A\">");
430     else if (thisStyle.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER)
431         tag = wxT("<ol type=\"a\">");
432     else if (thisStyle.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER)
433         tag = wxT("<ol type=\"I\">");
434     else if (thisStyle.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)
435         tag = wxT("<ol type=\"i\">");
436     else
437     {
438         tag = wxT("<ul>");
439         m_is_ul = true;
440     }
441 
442     if (m_is_ul)
443         return 1;
444     else
445         return 0;
446 }
447 
GetAlignment(const wxTextAttrEx & thisStyle)448 wxString wxRichTextHTMLHandler::GetAlignment( const wxTextAttrEx& thisStyle )
449 {
450     switch( thisStyle.GetAlignment() )
451     {
452     case wxTEXT_ALIGNMENT_LEFT:
453         return  wxT("left");
454     case wxTEXT_ALIGNMENT_RIGHT:
455         return wxT("right");
456     case wxTEXT_ALIGNMENT_CENTER:
457         return wxT("center");
458     case wxTEXT_ALIGNMENT_JUSTIFIED:
459         return wxT("justify");
460     default:
461         return wxT("left");
462     }
463 }
464 
WriteImage(wxRichTextImage * image,wxOutputStream & stream)465 void wxRichTextHTMLHandler::WriteImage(wxRichTextImage* image, wxOutputStream& stream)
466 {
467     wxTextOutputStream str(stream);
468 
469     str << wxT("<img src=\"");
470 
471 #if wxUSE_FILESYSTEM
472     if (GetFlags() & wxRICHTEXT_HANDLER_SAVE_IMAGES_TO_MEMORY)
473     {
474         if (!image->GetImage().Ok() && image->GetImageBlock().GetData())
475             image->LoadFromBlock();
476         if (image->GetImage().Ok() && !image->GetImageBlock().GetData())
477             image->MakeBlock();
478 
479         if (image->GetImage().Ok())
480         {
481             wxString ext(image->GetImageBlock().GetExtension());
482             wxString tempFilename(wxString::Format(wxT("image%d.%s"), sm_fileCounter, (const wxChar*) ext));
483             wxMemoryFSHandler::AddFile(tempFilename, image->GetImage(), image->GetImageBlock().GetImageType());
484 
485             m_imageLocations.Add(tempFilename);
486 
487             str << wxT("memory:") << tempFilename;
488         }
489         else
490             str << wxT("memory:?");
491 
492         sm_fileCounter ++;
493     }
494     else if (GetFlags() & wxRICHTEXT_HANDLER_SAVE_IMAGES_TO_FILES)
495     {
496         if (!image->GetImage().Ok() && image->GetImageBlock().GetData())
497             image->LoadFromBlock();
498         if (image->GetImage().Ok() && !image->GetImageBlock().GetData())
499             image->MakeBlock();
500 
501         if (image->GetImage().Ok())
502         {
503             wxString tempDir(GetTempDir());
504             if (tempDir.IsEmpty())
505                 tempDir = wxFileName::GetTempDir();
506 
507             wxString ext(image->GetImageBlock().GetExtension());
508             wxString tempFilename(wxString::Format(wxT("%s/image%d.%s"), (const wxChar*) tempDir, sm_fileCounter, (const wxChar*) ext));
509             image->GetImageBlock().Write(tempFilename);
510 
511             m_imageLocations.Add(tempFilename);
512 
513             str << wxFileSystem::FileNameToURL(tempFilename);
514         }
515         else
516             str << wxT("file:?");
517 
518         sm_fileCounter ++;
519     }
520     else // if (GetFlags() & wxRICHTEXT_HANDLER_SAVE_IMAGES_TO_BASE64) // this is implied
521 #endif
522     {
523         str << wxT("data:");
524         str << GetMimeType(image->GetImageBlock().GetImageType());
525         str << wxT(";base64,");
526 
527         if (image->GetImage().Ok() && !image->GetImageBlock().GetData())
528             image->MakeBlock();
529 
530         wxChar* data = b64enc( image->GetImageBlock().GetData(), image->GetImageBlock().GetDataSize() );
531         str << data;
532 
533         delete[] data;
534     }
535 
536     str << wxT("\" />");
537 }
538 
PtToSize(long size)539 long wxRichTextHTMLHandler::PtToSize(long size)
540 {
541     int i;
542     int len = m_fontSizeMapping.GetCount();
543     for (i = 0; i < len; i++)
544         if (size <= m_fontSizeMapping[i])
545             return i+1;
546     return 7;
547 }
548 
SymbolicIndent(long indent)549 wxString wxRichTextHTMLHandler::SymbolicIndent(long indent)
550 {
551     wxString in;
552     for(;indent > 0; indent -= 20)
553         in.Append( wxT("&nbsp;") );
554     return in;
555 }
556 
GetMimeType(int imageType)557 const wxChar* wxRichTextHTMLHandler::GetMimeType(int imageType)
558 {
559     switch(imageType)
560     {
561     case wxBITMAP_TYPE_BMP:
562         return wxT("image/bmp");
563     case wxBITMAP_TYPE_TIF:
564         return wxT("image/tiff");
565     case wxBITMAP_TYPE_GIF:
566         return wxT("image/gif");
567     case wxBITMAP_TYPE_PNG:
568         return wxT("image/png");
569     case wxBITMAP_TYPE_JPEG:
570         return wxT("image/jpeg");
571     default:
572         return wxT("image/unknown");
573     }
574 }
575 
576 // exim-style base64 encoder
b64enc(unsigned char * input,size_t in_len)577 wxChar* wxRichTextHTMLHandler::b64enc( unsigned char* input, size_t in_len )
578 {
579     // elements of enc64 array must be 8 bit values
580     // otherwise encoder will fail
581     // hmmm.. Does wxT macro define a char as 16 bit value
582     // when compiling with UNICODE option?
583     static const wxChar enc64[] = wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
584     wxChar* output = new wxChar[4*((in_len+2)/3)+1];
585     wxChar* p = output;
586 
587     while( in_len-- > 0 )
588     {
589         register wxChar a, b;
590 
591         a = *input++;
592 
593         *p++ = enc64[ (a >> 2) & 0x3f ];
594 
595         if( in_len-- == 0 )
596         {
597             *p++ = enc64[ (a << 4 ) & 0x30 ];
598             *p++ = '=';
599             *p++ = '=';
600             break;
601         }
602 
603         b = *input++;
604 
605         *p++ = enc64[(( a << 4 ) | ((b >> 4) &0xf )) & 0x3f];
606 
607         if( in_len-- == 0 )
608         {
609             *p++ = enc64[ (b << 2) & 0x3f ];
610             *p++ = '=';
611             break;
612         }
613 
614         a = *input++;
615 
616         *p++ = enc64[ ((( b << 2 ) & 0x3f ) | ((a >> 6)& 0x3)) & 0x3f ];
617 
618         *p++ = enc64[ a & 0x3f ];
619     }
620     *p = 0;
621 
622     return output;
623 }
624 #endif
625 // wxUSE_STREAMS
626 
627 /// Delete the in-memory or temporary files generated by the last operation
DeleteTemporaryImages()628 bool wxRichTextHTMLHandler::DeleteTemporaryImages()
629 {
630     return DeleteTemporaryImages(GetFlags(), m_imageLocations);
631 }
632 
633 /// Delete the in-memory or temporary files generated by the last operation
DeleteTemporaryImages(int flags,const wxArrayString & imageLocations)634 bool wxRichTextHTMLHandler::DeleteTemporaryImages(int flags, const wxArrayString& imageLocations)
635 {
636     size_t i;
637     for (i = 0; i < imageLocations.GetCount(); i++)
638     {
639         wxString location = imageLocations[i];
640 
641         if (flags & wxRICHTEXT_HANDLER_SAVE_IMAGES_TO_MEMORY)
642         {
643 #if wxUSE_FILESYSTEM
644             wxMemoryFSHandler::RemoveFile(location);
645 #endif
646         }
647         else if (flags & wxRICHTEXT_HANDLER_SAVE_IMAGES_TO_FILES)
648         {
649             if (wxFileExists(location))
650                 wxRemoveFile(location);
651         }
652     }
653 
654     return true;
655 }
656 
657 
658 #endif
659 // wxUSE_RICHTEXT
660 
661