1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/html/winpars.cpp
3 // Purpose: wxHtmlParser class (generic parser)
4 // Author: Vaclav Slavik
6 // Copyright: (c) 1999 Vaclav Slavik
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
10 #include "wx/wxprec.h"
16 #if wxUSE_HTML && wxUSE_STREAMS
22 #include "wx/settings.h"
25 #include "wx/html/htmldefs.h"
26 #include "wx/html/winpars.h"
27 #include "wx/html/htmlwin.h"
28 #include "wx/html/styleparams.h"
29 #include "wx/fontmap.h"
33 //-----------------------------------------------------------------------------
35 //-----------------------------------------------------------------------------
37 IMPLEMENT_ABSTRACT_CLASS(wxHtmlWinParser
, wxHtmlParser
)
39 wxList
wxHtmlWinParser::m_Modules
;
41 wxHtmlWinParser::wxHtmlWinParser(wxHtmlWindowInterface
*wndIface
)
45 m_windowInterface
= wndIface
;
48 m_CharHeight
= m_CharWidth
= 0;
53 m_InputEnc
= wxFONTENCODING_ISO8859_1
;
54 m_OutputEnc
= wxFONTENCODING_DEFAULT
;
56 m_whitespaceMode
= Whitespace_Normal
;
57 m_lastWordCell
= NULL
;
62 for (i
= 0; i
< 2; i
++)
63 for (j
= 0; j
< 2; j
++)
64 for (k
= 0; k
< 2; k
++)
65 for (l
= 0; l
< 2; l
++)
66 for (m
= 0; m
< 7; m
++)
68 m_FontsTable
[i
][j
][k
][l
][m
] = NULL
;
69 m_FontsFacesTable
[i
][j
][k
][l
][m
] = wxEmptyString
;
71 m_FontsEncTable
[i
][j
][k
][l
][m
] = wxFONTENCODING_DEFAULT
;
75 SetFonts(wxEmptyString
, wxEmptyString
, NULL
);
78 // fill in wxHtmlParser's tables:
79 wxList::compatibility_iterator node
= m_Modules
.GetFirst();
82 wxHtmlTagsModule
*mod
= (wxHtmlTagsModule
*) node
->GetData();
83 mod
->FillHandlersTable(this);
84 node
= node
->GetNext();
88 wxHtmlWinParser::~wxHtmlWinParser()
92 for (i
= 0; i
< 2; i
++)
93 for (j
= 0; j
< 2; j
++)
94 for (k
= 0; k
< 2; k
++)
95 for (l
= 0; l
< 2; l
++)
96 for (m
= 0; m
< 7; m
++)
98 if (m_FontsTable
[i
][j
][k
][l
][m
] != NULL
)
99 delete m_FontsTable
[i
][j
][k
][l
][m
];
104 delete[] m_tmpStrBuf
;
107 void wxHtmlWinParser::AddModule(wxHtmlTagsModule
*module)
109 m_Modules
.Append(module);
112 void wxHtmlWinParser::RemoveModule(wxHtmlTagsModule
*module)
114 m_Modules
.DeleteObject(module);
117 // build all HTML font sizes (1..7) from the given base size
118 static void wxBuildFontSizes(int *sizes
, int size
)
120 // using a fixed factor (1.2, from CSS2) is a bad idea as explained at
121 // http://www.w3.org/TR/CSS21/fonts.html#font-size-props but this is by far
122 // simplest thing to do so still do it like this for now
123 sizes
[0] = int(size
* 0.75); // exception to 1.2 rule, otherwise too small
124 sizes
[1] = int(size
* 0.83);
126 sizes
[3] = int(size
* 1.2);
127 sizes
[4] = int(size
* 1.44);
128 sizes
[5] = int(size
* 1.73);
129 sizes
[6] = int(size
* 2);
132 static int wxGetDefaultHTMLFontSize()
134 // base the default font size on the size of the default system font but
135 // also ensure that we have a font of reasonable size, otherwise small HTML
136 // fonts are unreadable
137 int size
= wxNORMAL_FONT
->GetPointSize();
143 void wxHtmlWinParser::SetFonts(const wxString
& normal_face
,
144 const wxString
& fixed_face
,
147 static int default_sizes
[7] = { 0 };
150 if ( !default_sizes
[0] )
151 wxBuildFontSizes(default_sizes
, wxGetDefaultHTMLFontSize());
153 sizes
= default_sizes
;
158 for (i
= 0; i
< 7; i
++)
159 m_FontsSizes
[i
] = sizes
[i
];
161 m_FontFaceFixed
= fixed_face
;
162 m_FontFaceNormal
= normal_face
;
165 SetInputEncoding(m_InputEnc
);
168 for (i
= 0; i
< 2; i
++)
169 for (j
= 0; j
< 2; j
++)
170 for (k
= 0; k
< 2; k
++)
171 for (l
= 0; l
< 2; l
++)
172 for (m
= 0; m
< 7; m
++) {
173 if (m_FontsTable
[i
][j
][k
][l
][m
] != NULL
)
175 delete m_FontsTable
[i
][j
][k
][l
][m
];
176 m_FontsTable
[i
][j
][k
][l
][m
] = NULL
;
181 void wxHtmlWinParser::SetStandardFonts(int size
,
182 const wxString
& normal_face
,
183 const wxString
& fixed_face
)
186 size
= wxGetDefaultHTMLFontSize();
189 wxBuildFontSizes(f_sizes
, size
);
191 wxString normal
= normal_face
;
192 if ( normal
.empty() )
193 normal
= wxNORMAL_FONT
->GetFaceName();
195 SetFonts(normal
, fixed_face
, f_sizes
);
198 void wxHtmlWinParser::InitParser(const wxString
& source
)
200 wxHtmlParser::InitParser(source
);
201 wxASSERT_MSG(m_DC
!= NULL
, wxT("no DC assigned to wxHtmlWinParser!!"));
203 m_FontBold
= m_FontItalic
= m_FontUnderlined
= m_FontFixed
= FALSE
;
204 m_FontSize
= 3; //default one
205 CreateCurrentFont(); // we're selecting default font into
207 // we're not using GetCharWidth/Height() because of
208 // differences under X and win
210 m_DC
->GetTextExtent( wxT("H"), &w
, &h
);
215 m_Link
= wxHtmlLinkInfo( wxEmptyString
);
216 m_LinkColor
.Set(0, 0, 0xFF);
217 m_ActualColor
.Set(0, 0, 0);
218 const wxColour windowColour
= wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW
) ;
219 m_ActualBackgroundColor
= m_windowInterface
220 ? m_windowInterface
->GetHTMLBackgroundColour()
222 m_ActualBackgroundMode
= wxTRANSPARENT
;
223 m_Align
= wxHTML_ALIGN_LEFT
;
224 m_ScriptMode
= wxHTML_SCRIPT_NORMAL
;
225 m_ScriptBaseline
= 0;
226 m_tmpLastWasSpace
= false;
227 m_lastWordCell
= NULL
;
229 // open the toplevel container that contains everything else and that
230 // is never closed (this makes parser's life easier):
233 // then open the first container into which page's content will go:
237 wxString charset
= ExtractCharsetInformation(source
);
238 if (!charset
.empty())
240 wxFontEncoding enc
= wxFontMapper::Get()->CharsetToEncoding(charset
);
241 if (enc
!= wxFONTENCODING_SYSTEM
)
242 SetInputEncoding(enc
);
246 m_Container
->InsertCell(new wxHtmlColourCell(m_ActualColor
));
248 m_Container
->InsertCell
252 m_ActualBackgroundColor
,
253 m_ActualBackgroundMode
== wxTRANSPARENT
? wxHTML_CLR_TRANSPARENT_BACKGROUND
: wxHTML_CLR_BACKGROUND
257 m_Container
->InsertCell(new wxHtmlFontCell(CreateCurrentFont()));
260 void wxHtmlWinParser::DoneParser()
264 SetInputEncoding(wxFONTENCODING_ISO8859_1
); // for next call
266 wxHtmlParser::DoneParser();
269 #if WXWIN_COMPATIBILITY_2_6
270 wxHtmlWindow
*wxHtmlWinParser::GetWindow()
272 if (!m_windowInterface
)
274 return wxDynamicCast(m_windowInterface
->GetHTMLWindow(), wxHtmlWindow
);
278 wxObject
* wxHtmlWinParser::GetProduct()
280 wxHtmlContainerCell
*top
;
286 while (top
->GetParent()) top
= top
->GetParent();
287 top
->RemoveExtraSpacing(true, true);
292 wxFSFile
*wxHtmlWinParser::OpenURL(wxHtmlURLType type
,
293 const wxString
& url
) const
295 if ( !m_windowInterface
)
296 return wxHtmlParser::OpenURL(type
, url
);
299 wxHtmlOpeningStatus status
;
302 wxString
myfullurl(myurl
);
304 // consider url as absolute path first
305 wxURI
current(myurl
);
306 myfullurl
= current
.BuildUnescapedURI();
308 // if not absolute then ...
309 if( current
.IsReference() )
311 wxString basepath
= GetFS()->GetPath();
312 wxURI
base(basepath
);
314 // ... try to apply base path if valid ...
315 if( !base
.IsReference() )
317 wxURI
path(myfullurl
);
318 path
.Resolve( base
);
319 myfullurl
= path
.BuildUnescapedURI();
323 // ... or force such addition if not included already
324 if( !current
.GetPath().Contains(base
.GetPath()) )
327 wxURI
connected( basepath
);
328 myfullurl
= connected
.BuildUnescapedURI();
334 status
= m_windowInterface
->OnHTMLOpeningURL(type
, myfullurl
, &redirect
);
335 if ( status
!= wxHTML_REDIRECT
)
341 if ( status
== wxHTML_BLOCK
)
344 int flags
= wxFS_READ
;
345 if (type
== wxHTML_URL_IMAGE
)
346 flags
|= wxFS_SEEKABLE
;
348 return GetFS()->OpenFile(myurl
, flags
);
351 #define NBSP_UNICODE_VALUE (wxChar(160))
353 #define CUR_NBSP_VALUE m_nbsp
355 #define CUR_NBSP_VALUE NBSP_UNICODE_VALUE
358 void wxHtmlWinParser::AddText(const wxString
& txt
)
362 m_nbsp
= GetEntitiesParser()->GetCharForCode(NBSP_UNICODE_VALUE
);
365 if ( m_whitespaceMode
== Whitespace_Normal
)
369 size_t lng
= txt
.length();
370 if (lng
+1 > m_tmpStrBufSize
)
372 delete[] m_tmpStrBuf
;
373 m_tmpStrBuf
= new wxChar
[lng
+1];
374 m_tmpStrBufSize
= lng
+1;
376 wxChar
*temp
= m_tmpStrBuf
;
378 wxString::const_iterator i
= txt
.begin();
379 const wxString::const_iterator end
= txt
.end();
381 if (m_tmpLastWasSpace
)
384 (*i
== wxT('\n') || *i
== wxT('\r') || *i
== wxT(' ') ||
394 const wxChar d
= temp
[templen
++] = *i
;
395 if ((d
== wxT('\n')) || (d
== wxT('\r')) || (d
== wxT(' ')) || (d
== wxT('\t')))
399 (*i
== wxT('\n') || *i
== wxT('\r') ||
400 *i
== wxT(' ') || *i
== wxT('\t')) )
413 temp
[templen
-1] = wxT(' ');
414 FlushWordBuf(temp
, templen
);
415 m_tmpLastWasSpace
= true;
419 if (templen
&& (templen
> 1 || temp
[0] != wxT(' ')))
421 FlushWordBuf(temp
, templen
);
422 m_tmpLastWasSpace
= false;
425 else // m_whitespaceMode == Whitespace_Pre
427 if ( txt
.find(CUR_NBSP_VALUE
) != wxString::npos
)
429 // we need to substitute spaces for here just like we
430 // did in the Whitespace_Normal branch above
432 txt2
.Replace(CUR_NBSP_VALUE
, ' ');
440 // don't eat any whitespace in <pre> block
441 m_tmpLastWasSpace
= false;
445 void wxHtmlWinParser::FlushWordBuf(wxChar
*buf
, int& len
)
449 for ( int i
= 0; i
< len
; i
++ )
451 if ( buf
[i
] == CUR_NBSP_VALUE
)
457 m_EncConv
->Convert(buf
);
460 AddWord(wxString(buf
, len
));
465 void wxHtmlWinParser::AddWord(wxHtmlWordCell
*word
)
467 ApplyStateToCell(word
);
469 m_Container
->InsertCell(word
);
470 word
->SetPreviousWord(m_lastWordCell
);
471 m_lastWordCell
= word
;
474 void wxHtmlWinParser::AddPreBlock(const wxString
& text
)
476 if ( text
.find('\t') != wxString::npos
)
479 text2
.reserve(text
.length());
481 const wxString::const_iterator end
= text
.end();
482 wxString::const_iterator copyFrom
= text
.begin();
484 int posColumn
= m_posColumn
;
485 for ( wxString::const_iterator i
= copyFrom
; i
!= end
; ++i
, ++pos
)
490 text2
.append(copyFrom
, i
);
492 const unsigned SPACES_PER_TAB
= 8;
493 const size_t expandTo
= SPACES_PER_TAB
- posColumn
% SPACES_PER_TAB
;
494 text2
.append(expandTo
, ' ');
496 posColumn
+= expandTo
;
504 if ( copyFrom
!= text
.end() )
505 text2
.append(copyFrom
, text
.end());
507 AddWord(new wxHtmlWordWithTabsCell(text2
, text
, m_posColumn
, *(GetDC())));
509 m_posColumn
= posColumn
;
513 // no special formatting needed
515 m_posColumn
+= text
.length();
520 wxHtmlContainerCell
* wxHtmlWinParser::OpenContainer()
522 m_Container
= new wxHtmlContainerCell(m_Container
);
523 m_Container
->SetAlignHor(m_Align
);
525 m_tmpLastWasSpace
= true;
526 /* to avoid space being first character in paragraph */
532 wxHtmlContainerCell
* wxHtmlWinParser::SetContainer(wxHtmlContainerCell
*c
)
534 m_tmpLastWasSpace
= true;
535 /* to avoid space being first character in paragraph */
536 return m_Container
= c
;
541 wxHtmlContainerCell
* wxHtmlWinParser::CloseContainer()
543 m_Container
= m_Container
->GetParent();
548 void wxHtmlWinParser::SetFontSize(int s
)
558 void wxHtmlWinParser::SetDC(wxDC
*dc
, double pixel_scale
, double font_scale
)
561 m_PixelScale
= pixel_scale
;
562 m_FontScale
= font_scale
;
565 void wxHtmlWinParser::SetFontPointSize(int pt
)
567 if (pt
<= m_FontsSizes
[0])
569 else if (pt
>= m_FontsSizes
[6])
573 // Find the font closest to the given value with a simple linear search
574 // (binary search is not worth it here for so small number of elements)
575 for ( int n
= 0; n
< 6; n
++ )
577 if ( (pt
> m_FontsSizes
[n
]) && (pt
<= m_FontsSizes
[n
+ 1]) )
579 if ( (pt
- m_FontsSizes
[n
]) >= (m_FontsSizes
[n
+ 1] - pt
) )
581 // The actual size is closer to the next entry than to this
586 // Notice that m_FontSize starts from 1, hence +1 here.
595 wxFont
* wxHtmlWinParser::CreateCurrentFont()
597 int fb
= GetFontBold(),
598 fi
= GetFontItalic(),
599 fu
= GetFontUnderlined(),
601 fs
= GetFontSize() - 1 /*remap from <1;7> to <0;6>*/ ;
603 wxString face
= ff
? m_FontFaceFixed
: m_FontFaceNormal
;
604 wxString
*faceptr
= &(m_FontsFacesTable
[fb
][fi
][fu
][ff
][fs
]);
605 wxFont
**fontptr
= &(m_FontsTable
[fb
][fi
][fu
][ff
][fs
]);
607 wxFontEncoding
*encptr
= &(m_FontsEncTable
[fb
][fi
][fu
][ff
][fs
]);
610 if (*fontptr
!= NULL
&& (*faceptr
!= face
612 || *encptr
!= m_OutputEnc
619 if (*fontptr
== NULL
)
622 *fontptr
= new wxFont(
623 (int) (m_FontsSizes
[fs
] * m_FontScale
),
624 ff
? wxMODERN
: wxSWISS
,
625 fi
? wxITALIC
: wxNORMAL
,
626 fb
? wxBOLD
: wxNORMAL
,
627 fu
? true : false, face
632 *encptr
= m_OutputEnc
;
635 m_DC
->SetFont(**fontptr
);
641 void wxHtmlWinParser::SetLink(const wxHtmlLinkInfo
& link
)
644 m_UseLink
= (link
.GetHref() != wxEmptyString
);
647 void wxHtmlWinParser::SetFontFace(const wxString
& face
)
650 m_FontFaceFixed
= face
;
652 m_FontFaceNormal
= face
;
655 if (m_InputEnc
!= wxFONTENCODING_DEFAULT
)
656 SetInputEncoding(m_InputEnc
);
660 void wxHtmlWinParser::ApplyStateToCell(wxHtmlCell
*cell
)
664 cell
->SetLink(GetLink());
666 // apply current script mode settings:
667 cell
->SetScriptMode(GetScriptMode(), GetScriptBaseline());
672 void wxHtmlWinParser::SetInputEncoding(wxFontEncoding enc
)
674 // the character used for non-breakable space may change:
677 m_InputEnc
= m_OutputEnc
= wxFONTENCODING_DEFAULT
;
680 if (enc
== wxFONTENCODING_DEFAULT
)
683 wxFontEncoding altfix
, altnorm
;
684 bool availfix
, availnorm
;
686 availnorm
= wxFontMapper::Get()->IsEncodingAvailable(enc
, m_FontFaceNormal
);
687 availfix
= wxFontMapper::Get()->IsEncodingAvailable(enc
, m_FontFaceFixed
);
689 if (availnorm
&& availfix
)
695 else if (wxFontMapper::Get()->GetAltForEncoding(enc
, &altnorm
, m_FontFaceNormal
, false) &&
696 wxFontMapper::Get()->GetAltForEncoding(enc
, &altfix
, m_FontFaceFixed
, false) &&
700 m_OutputEnc
= altnorm
;
704 // at least normal face?
707 else if (wxFontMapper::Get()->GetAltForEncoding(enc
, &altnorm
, m_FontFaceNormal
, false))
709 m_OutputEnc
= altnorm
;
714 // okay, let's convert to ISO_8859-1, available always
715 m_OutputEnc
= wxFONTENCODING_DEFAULT
;
717 m_OutputEnc
= wxLocale::GetSystemEncoding() ;
722 if (m_OutputEnc
== wxFONTENCODING_DEFAULT
)
724 GetEntitiesParser()->SetEncoding(wxFONTENCODING_SYSTEM
);
728 GetEntitiesParser()->SetEncoding(m_OutputEnc
);
731 if (m_InputEnc
== m_OutputEnc
)
734 m_EncConv
= new wxEncodingConverter();
735 if (!m_EncConv
->Init(m_InputEnc
,
736 (m_OutputEnc
== wxFONTENCODING_DEFAULT
) ?
737 wxFONTENCODING_ISO8859_1
: m_OutputEnc
,
738 wxCONVERT_SUBSTITUTE
))
739 { // total failure :-(
740 wxLogError(_("Failed to display HTML document in %s encoding"),
741 wxFontMapper::GetEncodingName(enc
).c_str());
742 m_InputEnc
= m_OutputEnc
= wxFONTENCODING_DEFAULT
;
751 //-----------------------------------------------------------------------------
752 // wxHtmlWinTagHandler
753 //-----------------------------------------------------------------------------
755 IMPLEMENT_ABSTRACT_CLASS(wxHtmlWinTagHandler
, wxHtmlTagHandler
)
757 void wxHtmlWinTagHandler::ApplyStyle(const wxHtmlStyleParams
&styleParams
)
761 str
= styleParams
.GetParam(wxS("color"));
765 if ( wxHtmlTag::ParseAsColour(str
, &clr
) )
767 m_WParser
->SetActualColor(clr
);
768 m_WParser
->GetContainer()->InsertCell(new wxHtmlColourCell(clr
));
772 str
= styleParams
.GetParam(wxS("background-color"));
776 if ( wxHtmlTag::ParseAsColour(str
, &clr
) )
778 m_WParser
->SetActualBackgroundColor(clr
);
779 m_WParser
->SetActualBackgroundMode(wxSOLID
);
780 m_WParser
->GetContainer()->InsertCell(new wxHtmlColourCell(clr
, wxHTML_CLR_BACKGROUND
));
784 str
= styleParams
.GetParam(wxS("font-size"));
788 int foundIndex
= str
.Find(wxS("pt"));
789 if (foundIndex
!= wxNOT_FOUND
)
791 str
.Truncate(foundIndex
);
794 if (str
.ToLong(&sizeValue
) == true)
797 m_WParser
->SetFontPointSize(sizeValue
);
798 m_WParser
->GetContainer()->InsertCell(
799 new wxHtmlFontCell(m_WParser
->CreateCurrentFont()));
802 // else: check for other ways of specifying size (TODO)
805 str
= styleParams
.GetParam(wxS("font-weight"));
808 // Only bold and normal supported just now
809 if ( str
== wxS("bold") )
811 m_WParser
->SetFontBold(true);
812 m_WParser
->GetContainer()->InsertCell(
813 new wxHtmlFontCell(m_WParser
->CreateCurrentFont()));
815 else if ( str
== wxS("normal") )
817 m_WParser
->SetFontBold(false);
818 m_WParser
->GetContainer()->InsertCell(
819 new wxHtmlFontCell(m_WParser
->CreateCurrentFont()));
823 str
= styleParams
.GetParam(wxS("font-style"));
826 // "oblique" and "italic" are more or less the same.
827 // "inherit" (using the parent font) is not supported.
828 if ( str
== wxS("oblique") || str
== wxS("italic") )
830 m_WParser
->SetFontItalic(true);
831 m_WParser
->GetContainer()->InsertCell(
832 new wxHtmlFontCell(m_WParser
->CreateCurrentFont()));
834 else if ( str
== wxS("normal") )
836 m_WParser
->SetFontItalic(false);
837 m_WParser
->GetContainer()->InsertCell(
838 new wxHtmlFontCell(m_WParser
->CreateCurrentFont()));
842 str
= styleParams
.GetParam(wxS("text-decoration"));
845 // Only underline is supported.
846 if ( str
== wxS("underline") )
848 m_WParser
->SetFontUnderlined(true);
849 m_WParser
->GetContainer()->InsertCell(
850 new wxHtmlFontCell(m_WParser
->CreateCurrentFont()));
854 str
= styleParams
.GetParam(wxS("font-family"));
857 m_WParser
->SetFontFace(str
);
858 m_WParser
->GetContainer()->InsertCell(
859 new wxHtmlFontCell(m_WParser
->CreateCurrentFont()));
863 //-----------------------------------------------------------------------------
865 //-----------------------------------------------------------------------------
867 // NB: This is *NOT* winpars.cpp's initialization and shutdown code!!
868 // This module is an ancestor for tag handlers modules defined
869 // in m_*.cpp files with TAGS_MODULE_BEGIN...TAGS_MODULE_END construct.
871 // Do not add any winpars.cpp shutdown or initialization code to it,
872 // create a new module instead!
874 IMPLEMENT_DYNAMIC_CLASS(wxHtmlTagsModule
, wxModule
)
876 bool wxHtmlTagsModule::OnInit()
878 wxHtmlWinParser::AddModule(this);
882 void wxHtmlTagsModule::OnExit()
884 wxHtmlWinParser::RemoveModule(this);