fixed unreadable formatting
[wxWidgets.git] / src / html / winpars.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/html/winpars.cpp
3 // Purpose: wxHtmlParser class (generic parser)
4 // Author: Vaclav Slavik
5 // RCS-ID: $Id$
6 // Copyright: (c) 1999 Vaclav Slavik
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #include "wx/wxprec.h"
11
12 #ifdef __BORLANDC__
13 #pragma hdrstop
14 #endif
15
16 #if wxUSE_HTML && wxUSE_STREAMS
17
18 #ifndef WX_PRECOMP
19 #include "wx/intl.h"
20 #include "wx/dc.h"
21 #include "wx/log.h"
22 #include "wx/settings.h"
23 #endif
24
25 #include "wx/html/htmldefs.h"
26 #include "wx/html/winpars.h"
27 #include "wx/html/htmlwin.h"
28 #include "wx/fontmap.h"
29 #include "wx/uri.h"
30
31
32 //-----------------------------------------------------------------------------
33 // wxHtmlWinParser
34 //-----------------------------------------------------------------------------
35
36 IMPLEMENT_ABSTRACT_CLASS(wxHtmlWinParser, wxHtmlParser)
37
38 wxList wxHtmlWinParser::m_Modules;
39
40 wxHtmlWinParser::wxHtmlWinParser(wxHtmlWindowInterface *wndIface)
41 {
42 m_tmpStrBuf = NULL;
43 m_tmpStrBufSize = 0;
44 m_windowInterface = wndIface;
45 m_Container = NULL;
46 m_DC = NULL;
47 m_CharHeight = m_CharWidth = 0;
48 m_UseLink = false;
49 #if !wxUSE_UNICODE
50 m_nbsp = 0;
51 m_EncConv = NULL;
52 m_InputEnc = wxFONTENCODING_ISO8859_1;
53 m_OutputEnc = wxFONTENCODING_DEFAULT;
54 #endif
55 m_lastWordCell = NULL;
56
57 {
58 int i, j, k, l, m;
59 for (i = 0; i < 2; i++)
60 for (j = 0; j < 2; j++)
61 for (k = 0; k < 2; k++)
62 for (l = 0; l < 2; l++)
63 for (m = 0; m < 7; m++)
64 {
65 m_FontsTable[i][j][k][l][m] = NULL;
66 m_FontsFacesTable[i][j][k][l][m] = wxEmptyString;
67 #if !wxUSE_UNICODE
68 m_FontsEncTable[i][j][k][l][m] = wxFONTENCODING_DEFAULT;
69 #endif
70 }
71
72 SetFonts(wxEmptyString, wxEmptyString, NULL);
73 }
74
75 // fill in wxHtmlParser's tables:
76 wxList::compatibility_iterator node = m_Modules.GetFirst();
77 while (node)
78 {
79 wxHtmlTagsModule *mod = (wxHtmlTagsModule*) node->GetData();
80 mod->FillHandlersTable(this);
81 node = node->GetNext();
82 }
83 }
84
85 wxHtmlWinParser::~wxHtmlWinParser()
86 {
87 int i, j, k, l, m;
88
89 for (i = 0; i < 2; i++)
90 for (j = 0; j < 2; j++)
91 for (k = 0; k < 2; k++)
92 for (l = 0; l < 2; l++)
93 for (m = 0; m < 7; m++)
94 {
95 if (m_FontsTable[i][j][k][l][m] != NULL)
96 delete m_FontsTable[i][j][k][l][m];
97 }
98 #if !wxUSE_UNICODE
99 delete m_EncConv;
100 #endif
101 delete[] m_tmpStrBuf;
102 }
103
104 void wxHtmlWinParser::AddModule(wxHtmlTagsModule *module)
105 {
106 m_Modules.Append(module);
107 }
108
109 void wxHtmlWinParser::RemoveModule(wxHtmlTagsModule *module)
110 {
111 m_Modules.DeleteObject(module);
112 }
113
114 // build all HTML font sizes (1..7) from the given base size
115 static void wxBuildFontSizes(int *sizes, int size)
116 {
117 // using a fixed factor (1.2, from CSS2) is a bad idea as explained at
118 // http://www.w3.org/TR/CSS21/fonts.html#font-size-props but this is by far
119 // simplest thing to do so still do it like this for now
120 sizes[0] = int(size * 0.75); // exception to 1.2 rule, otherwise too small
121 sizes[1] = int(size * 0.83);
122 sizes[2] = size;
123 sizes[3] = int(size * 1.2);
124 sizes[4] = int(size * 1.44);
125 sizes[5] = int(size * 1.73);
126 sizes[6] = int(size * 2);
127 }
128
129 static int wxGetDefaultHTMLFontSize()
130 {
131 // base the default font size on the size of the default system font but
132 // also ensure that we have a font of reasonable size, otherwise small HTML
133 // fonts are unreadable
134 int size = wxNORMAL_FONT->GetPointSize();
135 if ( size < 10 )
136 size = 10;
137 return size;
138 }
139
140 void wxHtmlWinParser::SetFonts(const wxString& normal_face,
141 const wxString& fixed_face,
142 const int *sizes)
143 {
144 static int default_sizes[7] = { 0 };
145 if ( !sizes )
146 {
147 if ( !default_sizes[0] )
148 wxBuildFontSizes(default_sizes, wxGetDefaultHTMLFontSize());
149
150 sizes = default_sizes;
151 }
152
153 int i, j, k, l, m;
154
155 for (i = 0; i < 7; i++)
156 m_FontsSizes[i] = sizes[i];
157
158 m_FontFaceFixed = fixed_face;
159 m_FontFaceNormal = normal_face;
160
161 #if !wxUSE_UNICODE
162 SetInputEncoding(m_InputEnc);
163 #endif
164
165 for (i = 0; i < 2; i++)
166 for (j = 0; j < 2; j++)
167 for (k = 0; k < 2; k++)
168 for (l = 0; l < 2; l++)
169 for (m = 0; m < 7; m++) {
170 if (m_FontsTable[i][j][k][l][m] != NULL)
171 {
172 delete m_FontsTable[i][j][k][l][m];
173 m_FontsTable[i][j][k][l][m] = NULL;
174 }
175 }
176 }
177
178 void wxHtmlWinParser::SetStandardFonts(int size,
179 const wxString& normal_face,
180 const wxString& fixed_face)
181 {
182 if (size == -1)
183 size = wxGetDefaultHTMLFontSize();
184
185 int f_sizes[7];
186 wxBuildFontSizes(f_sizes, size);
187
188 wxString normal = normal_face;
189 if ( normal.empty() )
190 normal = wxNORMAL_FONT->GetFaceName();
191
192 SetFonts(normal, fixed_face, f_sizes);
193 }
194
195 void wxHtmlWinParser::InitParser(const wxString& source)
196 {
197 wxHtmlParser::InitParser(source);
198 wxASSERT_MSG(m_DC != NULL, wxT("no DC assigned to wxHtmlWinParser!!"));
199
200 m_FontBold = m_FontItalic = m_FontUnderlined = m_FontFixed = FALSE;
201 m_FontSize = 3; //default one
202 CreateCurrentFont(); // we're selecting default font into
203
204 // we're not using GetCharWidth/Height() because of
205 // differences under X and win
206 wxCoord w,h;
207 m_DC->GetTextExtent( wxT("H"), &w, &h);
208 m_CharWidth = w;
209 m_CharHeight = h;
210
211 m_UseLink = false;
212 m_Link = wxHtmlLinkInfo( wxEmptyString );
213 m_LinkColor.Set(0, 0, 0xFF);
214 m_ActualColor.Set(0, 0, 0);
215 m_Align = wxHTML_ALIGN_LEFT;
216 m_ScriptMode = wxHTML_SCRIPT_NORMAL;
217 m_ScriptBaseline = 0;
218 m_tmpLastWasSpace = false;
219 m_lastWordCell = NULL;
220
221 // open the toplevel container that contains everything else and that
222 // is never closed (this makes parser's life easier):
223 OpenContainer();
224
225 // then open the first container into which page's content will go:
226 OpenContainer();
227
228 #if !wxUSE_UNICODE
229 wxString charset = ExtractCharsetInformation(source);
230 if (!charset.empty())
231 {
232 wxFontEncoding enc = wxFontMapper::Get()->CharsetToEncoding(charset);
233 if (enc != wxFONTENCODING_SYSTEM)
234 SetInputEncoding(enc);
235 }
236 #endif
237
238 m_Container->InsertCell(new wxHtmlColourCell(m_ActualColor));
239 wxColour windowColour = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW) ;
240
241 m_Container->InsertCell
242 (
243 new wxHtmlColourCell
244 (
245 m_windowInterface
246 ? m_windowInterface->GetHTMLBackgroundColour()
247 : windowColour,
248 wxHTML_CLR_BACKGROUND
249 )
250 );
251
252 m_Container->InsertCell(new wxHtmlFontCell(CreateCurrentFont()));
253 }
254
255 void wxHtmlWinParser::DoneParser()
256 {
257 m_Container = NULL;
258 #if !wxUSE_UNICODE
259 SetInputEncoding(wxFONTENCODING_ISO8859_1); // for next call
260 #endif
261 wxHtmlParser::DoneParser();
262 }
263
264 #if WXWIN_COMPATIBILITY_2_6
265 wxHtmlWindow *wxHtmlWinParser::GetWindow()
266 {
267 if (!m_windowInterface)
268 return NULL;
269 return wxDynamicCast(m_windowInterface->GetHTMLWindow(), wxHtmlWindow);
270 }
271 #endif
272
273 wxObject* wxHtmlWinParser::GetProduct()
274 {
275 wxHtmlContainerCell *top;
276
277 CloseContainer();
278 OpenContainer();
279
280 top = m_Container;
281 while (top->GetParent()) top = top->GetParent();
282 top->RemoveExtraSpacing(true, true);
283
284 return top;
285 }
286
287 wxFSFile *wxHtmlWinParser::OpenURL(wxHtmlURLType type,
288 const wxString& url) const
289 {
290 if ( !m_windowInterface )
291 return wxHtmlParser::OpenURL(type, url);
292
293 wxString myurl(url);
294 wxHtmlOpeningStatus status;
295 for (;;)
296 {
297 wxString myfullurl(myurl);
298
299 // consider url as absolute path first
300 wxURI current(myurl);
301 myfullurl = current.BuildUnescapedURI();
302
303 // if not absolute then ...
304 if( current.IsReference() )
305 {
306 wxString basepath = GetFS()->GetPath();
307 wxURI base(basepath);
308
309 // ... try to apply base path if valid ...
310 if( !base.IsReference() )
311 {
312 wxURI path(myfullurl);
313 path.Resolve( base );
314 myfullurl = path.BuildUnescapedURI();
315 }
316 else
317 {
318 // ... or force such addition if not included already
319 if( !current.GetPath().Contains(base.GetPath()) )
320 {
321 basepath += myurl;
322 wxURI connected( basepath );
323 myfullurl = connected.BuildUnescapedURI();
324 }
325 }
326 }
327
328 wxString redirect;
329 status = m_windowInterface->OnHTMLOpeningURL(type, myfullurl, &redirect);
330 if ( status != wxHTML_REDIRECT )
331 break;
332
333 myurl = redirect;
334 }
335
336 if ( status == wxHTML_BLOCK )
337 return NULL;
338
339 int flags = wxFS_READ;
340 if (type == wxHTML_URL_IMAGE)
341 flags |= wxFS_SEEKABLE;
342
343 return GetFS()->OpenFile(myurl, flags);
344 }
345
346 void wxHtmlWinParser::AddText(const wxString& txt)
347 {
348 register wxChar d;
349 int templen = 0;
350
351 size_t lng = txt.length();
352 if (lng+1 > m_tmpStrBufSize)
353 {
354 delete[] m_tmpStrBuf;
355 m_tmpStrBuf = new wxChar[lng+1];
356 m_tmpStrBufSize = lng+1;
357 }
358 wxChar *temp = m_tmpStrBuf;
359
360 wxString::const_iterator i = txt.begin();
361 wxString::const_iterator end = txt.end();
362
363 if (m_tmpLastWasSpace)
364 {
365 while ( (i < end) &&
366 (*i == wxT('\n') || *i == wxT('\r') || *i == wxT(' ') ||
367 *i == wxT('\t')) )
368 {
369 ++i;
370 }
371 }
372
373 while (i < end)
374 {
375 size_t x = 0;
376 d = temp[templen++] = *i;
377 if ((d == wxT('\n')) || (d == wxT('\r')) || (d == wxT(' ')) || (d == wxT('\t')))
378 {
379 ++i, ++x;
380 while ( (i < end) &&
381 (*i == wxT('\n') || *i == wxT('\r') ||
382 *i == wxT(' ') || *i == wxT('\t')) )
383 {
384 ++i;
385 ++x;
386 }
387 }
388 else
389 ++i;
390
391 if (x)
392 {
393 temp[templen-1] = wxT(' ');
394 DoAddText(temp, templen);
395 m_tmpLastWasSpace = true;
396 }
397 }
398
399 if (templen && (templen > 1 || temp[0] != wxT(' ')))
400 {
401 DoAddText(temp, templen);
402 m_tmpLastWasSpace = false;
403 }
404 }
405
406 void wxHtmlWinParser::DoAddText(wxChar *temp, int& templen)
407 {
408 #define NBSP_UNICODE_VALUE 160
409 #if !wxUSE_UNICODE
410 if ( m_nbsp == 0 )
411 m_nbsp = GetEntitiesParser()->GetCharForCode(NBSP_UNICODE_VALUE);
412 #define CUR_NBSP_VALUE m_nbsp
413 #else
414 #define CUR_NBSP_VALUE NBSP_UNICODE_VALUE
415 #endif
416
417 temp[templen] = 0;
418 templen = 0;
419 #if !wxUSE_UNICODE
420 if (m_EncConv)
421 m_EncConv->Convert(temp);
422 #endif
423 size_t len = wxStrlen(temp);
424 for (size_t j = 0; j < len; j++)
425 {
426 if (temp[j] == CUR_NBSP_VALUE)
427 temp[j] = wxT(' ');
428 }
429
430 wxHtmlCell *c = new wxHtmlWordCell(temp, *(GetDC()));
431
432 ApplyStateToCell(c);
433
434 m_Container->InsertCell(c);
435 ((wxHtmlWordCell*)c)->SetPreviousWord(m_lastWordCell);
436 m_lastWordCell = (wxHtmlWordCell*)c;
437 }
438
439
440
441 wxHtmlContainerCell* wxHtmlWinParser::OpenContainer()
442 {
443 m_Container = new wxHtmlContainerCell(m_Container);
444 m_Container->SetAlignHor(m_Align);
445 m_tmpLastWasSpace = true;
446 /* to avoid space being first character in paragraph */
447 return m_Container;
448 }
449
450
451
452 wxHtmlContainerCell* wxHtmlWinParser::SetContainer(wxHtmlContainerCell *c)
453 {
454 m_tmpLastWasSpace = true;
455 /* to avoid space being first character in paragraph */
456 return m_Container = c;
457 }
458
459
460
461 wxHtmlContainerCell* wxHtmlWinParser::CloseContainer()
462 {
463 m_Container = m_Container->GetParent();
464 return m_Container;
465 }
466
467
468 void wxHtmlWinParser::SetFontSize(int s)
469 {
470 if (s < 1)
471 s = 1;
472 else if (s > 7)
473 s = 7;
474 m_FontSize = s;
475 }
476
477
478
479 wxFont* wxHtmlWinParser::CreateCurrentFont()
480 {
481 int fb = GetFontBold(),
482 fi = GetFontItalic(),
483 fu = GetFontUnderlined(),
484 ff = GetFontFixed(),
485 fs = GetFontSize() - 1 /*remap from <1;7> to <0;6>*/ ;
486
487 wxString face = ff ? m_FontFaceFixed : m_FontFaceNormal;
488 wxString *faceptr = &(m_FontsFacesTable[fb][fi][fu][ff][fs]);
489 wxFont **fontptr = &(m_FontsTable[fb][fi][fu][ff][fs]);
490 #if !wxUSE_UNICODE
491 wxFontEncoding *encptr = &(m_FontsEncTable[fb][fi][fu][ff][fs]);
492 #endif
493
494 if (*fontptr != NULL && (*faceptr != face
495 #if !wxUSE_UNICODE
496 || *encptr != m_OutputEnc
497 #endif
498 ))
499 {
500 delete *fontptr;
501 *fontptr = NULL;
502 }
503
504 if (*fontptr == NULL)
505 {
506 *faceptr = face;
507 *fontptr = new wxFont(
508 (int) (m_FontsSizes[fs] * m_PixelScale),
509 ff ? wxMODERN : wxSWISS,
510 fi ? wxITALIC : wxNORMAL,
511 fb ? wxBOLD : wxNORMAL,
512 fu ? true : false, face
513 #if wxUSE_UNICODE
514 );
515 #else
516 , m_OutputEnc);
517 *encptr = m_OutputEnc;
518 #endif
519 }
520 m_DC->SetFont(**fontptr);
521 return (*fontptr);
522 }
523
524
525
526 void wxHtmlWinParser::SetLink(const wxHtmlLinkInfo& link)
527 {
528 m_Link = link;
529 m_UseLink = (link.GetHref() != wxEmptyString);
530 }
531
532 void wxHtmlWinParser::SetFontFace(const wxString& face)
533 {
534 if (GetFontFixed())
535 m_FontFaceFixed = face;
536 else
537 m_FontFaceNormal = face;
538
539 #if !wxUSE_UNICODE
540 if (m_InputEnc != wxFONTENCODING_DEFAULT)
541 SetInputEncoding(m_InputEnc);
542 #endif
543 }
544
545 void wxHtmlWinParser::ApplyStateToCell(wxHtmlCell *cell)
546 {
547 // set the link:
548 if (m_UseLink)
549 cell->SetLink(GetLink());
550
551 // apply current script mode settings:
552 cell->SetScriptMode(GetScriptMode(), GetScriptBaseline());
553 }
554
555
556 #if !wxUSE_UNICODE
557 void wxHtmlWinParser::SetInputEncoding(wxFontEncoding enc)
558 {
559 // the character used for non-breakable space may change:
560 m_nbsp = 0;
561
562 m_InputEnc = m_OutputEnc = wxFONTENCODING_DEFAULT;
563 if (m_EncConv)
564 {
565 delete m_EncConv;
566 m_EncConv = NULL;
567 }
568
569 if (enc == wxFONTENCODING_DEFAULT)
570 return;
571
572 wxFontEncoding altfix, altnorm;
573 bool availfix, availnorm;
574
575 availnorm = wxFontMapper::Get()->IsEncodingAvailable(enc, m_FontFaceNormal);
576 availfix = wxFontMapper::Get()->IsEncodingAvailable(enc, m_FontFaceFixed);
577
578 if (availnorm && availfix)
579 {
580 // exact match?
581 m_OutputEnc = enc;
582 }
583
584 else if (wxFontMapper::Get()->GetAltForEncoding(enc, &altnorm, m_FontFaceNormal, false) &&
585 wxFontMapper::Get()->GetAltForEncoding(enc, &altfix, m_FontFaceFixed, false) &&
586 altnorm == altfix)
587 {
588 // alternatives?
589 m_OutputEnc = altnorm;
590 }
591 else if (availnorm)
592 {
593 // at least normal face?
594 m_OutputEnc = enc;
595 }
596 else if (wxFontMapper::Get()->GetAltForEncoding(enc, &altnorm, m_FontFaceNormal, false))
597 {
598 m_OutputEnc = altnorm;
599 }
600 else
601 {
602 #ifndef __WXMAC__
603 // okay, let's convert to ISO_8859-1, available always
604 m_OutputEnc = wxFONTENCODING_DEFAULT;
605 #else
606 m_OutputEnc = wxLocale::GetSystemEncoding() ;
607 #endif
608 }
609
610 m_InputEnc = enc;
611 if (m_OutputEnc == wxFONTENCODING_DEFAULT)
612 {
613 GetEntitiesParser()->SetEncoding(wxFONTENCODING_SYSTEM);
614 }
615 else
616 {
617 GetEntitiesParser()->SetEncoding(m_OutputEnc);
618 }
619
620 if (m_InputEnc == m_OutputEnc)
621 return;
622
623 m_EncConv = new wxEncodingConverter();
624 if (!m_EncConv->Init(m_InputEnc,
625 (m_OutputEnc == wxFONTENCODING_DEFAULT) ?
626 wxFONTENCODING_ISO8859_1 : m_OutputEnc,
627 wxCONVERT_SUBSTITUTE))
628 { // total failure :-(
629 wxLogError(_("Failed to display HTML document in %s encoding"),
630 wxFontMapper::GetEncodingName(enc).c_str());
631 m_InputEnc = m_OutputEnc = wxFONTENCODING_DEFAULT;
632 delete m_EncConv;
633 m_EncConv = NULL;
634 }
635 }
636 #endif
637
638
639
640
641 //-----------------------------------------------------------------------------
642 // wxHtmlWinTagHandler
643 //-----------------------------------------------------------------------------
644
645 IMPLEMENT_ABSTRACT_CLASS(wxHtmlWinTagHandler, wxHtmlTagHandler)
646
647 //-----------------------------------------------------------------------------
648 // wxHtmlTagsModule
649 //-----------------------------------------------------------------------------
650
651 // NB: This is *NOT* winpars.cpp's initialization and shutdown code!!
652 // This module is an ancestor for tag handlers modules defined
653 // in m_*.cpp files with TAGS_MODULE_BEGIN...TAGS_MODULE_END construct.
654 //
655 // Do not add any winpars.cpp shutdown or initialization code to it,
656 // create a new module instead!
657
658 IMPLEMENT_DYNAMIC_CLASS(wxHtmlTagsModule, wxModule)
659
660 bool wxHtmlTagsModule::OnInit()
661 {
662 wxHtmlWinParser::AddModule(this);
663 return true;
664 }
665
666 void wxHtmlTagsModule::OnExit()
667 {
668 wxHtmlWinParser::RemoveModule(this);
669 }
670
671 #endif