1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/html/m_layout.cpp
3 // Purpose:     wxHtml module for basic paragraphs/layout handling
4 // Author:      Vaclav Slavik
5 // Copyright:   (c) 1999 Vaclav Slavik
6 // Licence:     wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8 
9 #include "wx/wxprec.h"
10 
11 #ifdef __BORLANDC__
12     #pragma hdrstop
13 #endif
14 
15 #if wxUSE_HTML && wxUSE_STREAMS
16 
17 #ifndef WX_PRECOMP
18     #include "wx/image.h"
19 #endif
20 
21 #include "wx/html/forcelnk.h"
22 #include "wx/html/m_templ.h"
23 
24 #include "wx/html/htmlwin.h"
25 
26 FORCE_LINK_ME(m_layout)
27 
28 #ifdef __WXWINCE__
29     #include "wx/msw/wince/missing.h"       // for bsearch()
30 #else
31     #include <stdlib.h>                     // bsearch()
32 #endif
33 
34 //-----------------------------------------------------------------------------
35 // wxHtmlPageBreakCell
36 //-----------------------------------------------------------------------------
37 
38 // Since html isn't a page-layout language, it doesn't support page
39 // page breaks directly--that requires CSS2 support. But a page-break
40 // facility is handy, and has been requested more than once on the
41 // mailing lists. This wxHtml tag handler implements just enough of
42 // CSS2 to support a page break by recognizing only
43 //   <div style="page-break-before:always">
44 //
45 // wxHtml maintains page breaks in wxHtmlPrintout::m_PageBreaks. The
46 // tag handler below adds appropriate offsets to that array member.
47 // wxHtmlDCRenderer::Render() accesses that array and makes a new page
48 // begin after each page-break tag.
49 
50 // The page-break handler does all its work in AdjustPagebreak(). For
51 // all tag handlers, that function adjusts the page-break position.
52 // For other tags, it determines whether the html element can fit on
53 // the remainder of the page; if it cannot fit, but must not be split,
54 // then the function moves the page break provided in the argument up,
55 // and returns 'true' to inform the caller that the argument was
56 // modified.
57 //
58 // Due to its special purpose, the page-break facility differs from
59 // other tags. It takes up no space, but it behaves as though there is
60 // never enough room to fit it on the remainder of the page--it always
61 // forces a page break. Therefore, unlike other elements that trigger
62 // a page break, it would never 'fit' on the following page either.
63 // Therefore it's necessary to compare each pagebreak candidate to the
64 // array wxHtmlPrintout::m_PageBreaks of pagebreaks already set, and
65 // set a new one only if it's not in that array.
66 
67 class wxHtmlPageBreakCell : public wxHtmlCell
68 {
69 public:
wxHtmlPageBreakCell()70     wxHtmlPageBreakCell() {}
71 
72     bool AdjustPagebreak(int* pagebreak,
73                          const wxArrayInt& known_pagebreaks,
74                          int pageHeight) const;
75 
Draw(wxDC & WXUNUSED (dc),int WXUNUSED (x),int WXUNUSED (y),int WXUNUSED (view_y1),int WXUNUSED (view_y2),wxHtmlRenderingInfo & WXUNUSED (info))76     void Draw(wxDC& WXUNUSED(dc),
77               int WXUNUSED(x), int WXUNUSED(y),
78               int WXUNUSED(view_y1), int WXUNUSED(view_y2),
79               wxHtmlRenderingInfo& WXUNUSED(info)) {}
80 
81 private:
82     wxDECLARE_NO_COPY_CLASS(wxHtmlPageBreakCell);
83 };
84 
85 bool
AdjustPagebreak(int * pagebreak,const wxArrayInt & known_pagebreaks,int WXUNUSED (pageHeight)) const86 wxHtmlPageBreakCell::AdjustPagebreak(int* pagebreak,
87                                      const wxArrayInt& known_pagebreaks,
88                                      int WXUNUSED(pageHeight)) const
89 {
90     // When we are counting pages, 'known_pagebreaks' is non-NULL.
91     // That's the only time we change 'pagebreak'. Otherwise, pages
92     // were already counted, 'known_pagebreaks' is NULL, and we don't
93     // do anything except return false.
94     //
95     // We also simply return false if the 'pagebreak' argument is
96     // less than (vertically above) or the same as the current
97     // vertical position. Otherwise we'd be setting a pagebreak above
98     // the current cell, which is incorrect, or duplicating a
99     // pagebreak that has already been set.
100     if( known_pagebreaks.GetCount() == 0 || *pagebreak <= m_PosY)
101     {
102         return false;
103     }
104 
105     // m_PosY is only the vertical offset from the parent. The pagebreak
106     // required here is the total page offset, so m_PosY must be added
107     // to the parent's offset and height.
108     int total_height = m_PosY;
109     for ( wxHtmlCell *parent = GetParent(); parent; parent = parent->GetParent() )
110     {
111         total_height += parent->GetPosY();
112     }
113 
114 
115     // Search the array of pagebreaks to see whether we've already set
116     // a pagebreak here.
117     int where = known_pagebreaks.Index( total_height);
118     // Add a pagebreak only if there isn't one already set here.
119     if( wxNOT_FOUND != where)
120     {
121         return false;
122     }
123     else
124     {
125         *pagebreak = m_PosY;
126         return true;
127     }
128 }
129 
130 
131 
132 TAG_HANDLER_BEGIN(P, "P")
TAG_HANDLER_CONSTR(P)133     TAG_HANDLER_CONSTR(P) { }
134 
TAG_HANDLER_PROC(tag)135     TAG_HANDLER_PROC(tag)
136     {
137         if (m_WParser->GetContainer()->GetFirstChild() != NULL)
138         {
139             m_WParser->CloseContainer();
140             m_WParser->OpenContainer();
141         }
142         m_WParser->GetContainer()->SetIndent(m_WParser->GetCharHeight(), wxHTML_INDENT_TOP);
143         m_WParser->GetContainer()->SetAlign(tag);
144         return false;
145     }
146 
147 TAG_HANDLER_END(P)
148 
149 
150 
151 TAG_HANDLER_BEGIN(BR, "BR")
TAG_HANDLER_CONSTR(BR)152     TAG_HANDLER_CONSTR(BR) { }
153 
TAG_HANDLER_PROC(tag)154     TAG_HANDLER_PROC(tag)
155     {
156         int al = m_WParser->GetContainer()->GetAlignHor();
157         wxHtmlContainerCell *c;
158 
159         m_WParser->CloseContainer();
160         c = m_WParser->OpenContainer();
161         c->SetAlignHor(al);
162         c->SetAlign(tag);
163         c->SetMinHeight(m_WParser->GetCharHeight());
164         return false;
165     }
166 
167 TAG_HANDLER_END(BR)
168 
169 
170 
171 TAG_HANDLER_BEGIN(CENTER, "CENTER")
TAG_HANDLER_CONSTR(CENTER)172     TAG_HANDLER_CONSTR(CENTER) { }
173 
TAG_HANDLER_PROC(tag)174     TAG_HANDLER_PROC(tag)
175     {
176         int old = m_WParser->GetAlign();
177         wxHtmlContainerCell *c = m_WParser->GetContainer();
178 
179         m_WParser->SetAlign(wxHTML_ALIGN_CENTER);
180         if (c->GetFirstChild() != NULL)
181         {
182             m_WParser->CloseContainer();
183             m_WParser->OpenContainer();
184         }
185         else
186             c->SetAlignHor(wxHTML_ALIGN_CENTER);
187 
188         if (tag.HasEnding())
189         {
190             ParseInner(tag);
191 
192             m_WParser->SetAlign(old);
193             if (c->GetFirstChild() != NULL)
194             {
195                 m_WParser->CloseContainer();
196                 m_WParser->OpenContainer();
197             }
198             else
199                 c->SetAlignHor(old);
200 
201             return true;
202         }
203         else return false;
204     }
205 
206 TAG_HANDLER_END(CENTER)
207 
208 
209 
210 TAG_HANDLER_BEGIN(DIV, "DIV")
TAG_HANDLER_CONSTR(DIV)211     TAG_HANDLER_CONSTR(DIV) { }
212 
TAG_HANDLER_PROC(tag)213     TAG_HANDLER_PROC(tag)
214     {
215         wxString style;
216         if(tag.GetParamAsString(wxT("STYLE"), &style))
217         {
218             if(style.IsSameAs(wxT("PAGE-BREAK-BEFORE:ALWAYS"), false))
219             {
220                 m_WParser->CloseContainer();
221                 m_WParser->OpenContainer()->InsertCell(new wxHtmlPageBreakCell);
222                 m_WParser->CloseContainer();
223                 m_WParser->OpenContainer();
224                 return false;
225             }
226             else
227             {
228                 // Treat other STYLE parameters here when they're supported.
229                 return false;
230             }
231         }
232         else if(tag.HasParam(wxT("ALIGN")))
233         {
234             int old = m_WParser->GetAlign();
235             wxHtmlContainerCell *c = m_WParser->GetContainer();
236             if (c->GetFirstChild() != NULL)
237             {
238                 m_WParser->CloseContainer();
239                 m_WParser->OpenContainer();
240                 c = m_WParser->GetContainer();
241                 c->SetAlign(tag);
242                 m_WParser->SetAlign(c->GetAlignHor());
243             }
244             else
245             {
246                 c->SetAlign(tag);
247                 m_WParser->SetAlign(c->GetAlignHor());
248             }
249 
250             ParseInner(tag);
251 
252             m_WParser->SetAlign(old);
253             if (c->GetFirstChild() != NULL)
254             {
255                 m_WParser->CloseContainer();
256                 m_WParser->OpenContainer();
257             }
258             else
259                 c->SetAlignHor(old);
260 
261             return true;
262         }
263         else
264         {
265             // Same as BR
266             int al = m_WParser->GetContainer()->GetAlignHor();
267             wxHtmlContainerCell *c;
268 
269             m_WParser->CloseContainer();
270             c = m_WParser->OpenContainer();
271             c->SetAlignHor(al);
272             c->SetAlign(tag);
273             c->SetMinHeight(m_WParser->GetCharHeight());
274             return false;
275         }
276     }
277 
278 TAG_HANDLER_END(DIV)
279 
280 
281 
282 
283 TAG_HANDLER_BEGIN(TITLE, "TITLE")
TAG_HANDLER_CONSTR(TITLE)284     TAG_HANDLER_CONSTR(TITLE) { }
285 
TAG_HANDLER_PROC(tag)286     TAG_HANDLER_PROC(tag)
287     {
288         wxHtmlWindowInterface *winIface = m_WParser->GetWindowInterface();
289         if (winIface)
290         {
291             wxString title(tag.GetBeginIter(), tag.GetEndIter1());
292 #if !wxUSE_UNICODE
293             const wxFontEncoding enc = m_WParser->GetInputEncoding();
294             if ( enc != wxFONTENCODING_DEFAULT )
295             {
296                 // need to convert to the current one
297                 title = wxString(title.wc_str(wxCSConv(enc)), wxConvLocal);
298             }
299 #endif // !wxUSE_UNICODE
300 
301             title = m_WParser->GetEntitiesParser()->Parse(title);
302 
303             winIface->SetHTMLWindowTitle(title);
304         }
305         return true;
306     }
307 
308 TAG_HANDLER_END(TITLE)
309 
310 
311 
312 
313 TAG_HANDLER_BEGIN(BODY, "BODY")
TAG_HANDLER_CONSTR(BODY)314     TAG_HANDLER_CONSTR(BODY) { }
315 
TAG_HANDLER_PROC(tag)316     TAG_HANDLER_PROC(tag)
317     {
318         wxColour clr;
319 
320         if (tag.GetParamAsColour(wxT("TEXT"), &clr))
321         {
322             m_WParser->SetActualColor(clr);
323             m_WParser->GetContainer()->InsertCell(new wxHtmlColourCell(clr));
324         }
325 
326         if (tag.GetParamAsColour(wxT("LINK"), &clr))
327             m_WParser->SetLinkColor(clr);
328 
329         wxHtmlWindowInterface *winIface = m_WParser->GetWindowInterface();
330         // the rest of this function requires a window:
331         if ( !winIface )
332             return false;
333 
334         wxString bg;
335         if (tag.GetParamAsString(wxT("BACKGROUND"), &bg))
336         {
337             wxFSFile *fileBgImage = m_WParser->OpenURL(wxHTML_URL_IMAGE, bg);
338             if ( fileBgImage )
339             {
340                 wxInputStream *is = fileBgImage->GetStream();
341                 if ( is )
342                 {
343                     wxImage image(*is);
344                     if ( image.IsOk() )
345                         winIface->SetHTMLBackgroundImage(image);
346                 }
347 
348                 delete fileBgImage;
349             }
350         }
351 
352         if (tag.GetParamAsColour(wxT("BGCOLOR"), &clr))
353         {
354             m_WParser->GetContainer()->InsertCell(
355                 new wxHtmlColourCell(clr, wxHTML_CLR_TRANSPARENT_BACKGROUND));
356             winIface->SetHTMLBackgroundColour(clr);
357         }
358 
359         return false;
360     }
361 
362 TAG_HANDLER_END(BODY)
363 
364 
365 
366 TAG_HANDLER_BEGIN(BLOCKQUOTE, "BLOCKQUOTE")
TAG_HANDLER_CONSTR(BLOCKQUOTE)367     TAG_HANDLER_CONSTR(BLOCKQUOTE) { }
368 
TAG_HANDLER_PROC(tag)369     TAG_HANDLER_PROC(tag)
370     {
371         wxHtmlContainerCell *c;
372 
373         m_WParser->CloseContainer();
374         c = m_WParser->OpenContainer();
375 
376         if (c->GetAlignHor() == wxHTML_ALIGN_RIGHT)
377             c->SetIndent(5 * m_WParser->GetCharWidth(), wxHTML_INDENT_RIGHT);
378         else
379             c->SetIndent(5 * m_WParser->GetCharWidth(), wxHTML_INDENT_LEFT);
380 
381         c->SetIndent(m_WParser->GetCharHeight(), wxHTML_INDENT_TOP);
382         m_WParser->OpenContainer();
383         ParseInner(tag);
384         c = m_WParser->CloseContainer();
385         c->SetIndent(m_WParser->GetCharHeight(), wxHTML_INDENT_BOTTOM);
386         m_WParser->CloseContainer();
387         m_WParser->OpenContainer();
388         return true;
389     }
390 
391 TAG_HANDLER_END(BLOCKQUOTE)
392 
393 
394 
395 TAG_HANDLER_BEGIN(SUBSUP, "SUB,SUP")
396 
TAG_HANDLER_PROC(tag)397     TAG_HANDLER_PROC(tag)
398     {
399         bool issub = (tag.GetName() == wxT("SUB"));
400         wxHtmlScriptMode oldmode = m_WParser->GetScriptMode();
401         int oldbase = m_WParser->GetScriptBaseline();
402         int oldsize = m_WParser->GetFontSize();
403 
404         wxHtmlContainerCell *cont = m_WParser->GetContainer();
405         wxHtmlCell *c = cont->GetLastChild();
406 
407         m_WParser->SetScriptMode(issub ? wxHTML_SCRIPT_SUB : wxHTML_SCRIPT_SUP);
408         m_WParser->SetScriptBaseline(
409                 oldbase + (c ? c->GetScriptBaseline() : 0));
410 
411         // select smaller font
412         m_WParser->SetFontSize(m_WParser->GetFontSize()-2);
413         cont->InsertCell(new wxHtmlFontCell(m_WParser->CreateCurrentFont()));
414 
415         ParseInner(tag);
416 
417         // restore font size
418         m_WParser->SetFontSize(oldsize);
419         m_WParser->GetContainer()->InsertCell(
420             new wxHtmlFontCell(m_WParser->CreateCurrentFont()));
421 
422         // restore base and alignment
423         m_WParser->SetScriptBaseline(oldbase);
424         m_WParser->SetScriptMode(oldmode);
425 
426         return true;
427     }
428 
429 TAG_HANDLER_END(SUBSUP)
430 
431 
432 // Tag handler for tags that we have to ignore, otherwise non-text data
433 // would show up as text:
434 TAG_HANDLER_BEGIN(DoNothing, "SCRIPT")
TAG_HANDLER_CONSTR(DoNothing)435     TAG_HANDLER_CONSTR(DoNothing) { }
436 
TAG_HANDLER_PROC(WXUNUSED (tag))437     TAG_HANDLER_PROC(WXUNUSED(tag))
438     {
439         return true;
440     }
441 TAG_HANDLER_END(DoNothing)
442 
443 
444 
445 
446 
447 TAGS_MODULE_BEGIN(Layout)
448 
449     TAGS_MODULE_ADD(P)
450     TAGS_MODULE_ADD(BR)
451     TAGS_MODULE_ADD(CENTER)
452     TAGS_MODULE_ADD(DIV)
453     TAGS_MODULE_ADD(TITLE)
454     TAGS_MODULE_ADD(BODY)
455     TAGS_MODULE_ADD(BLOCKQUOTE)
456     TAGS_MODULE_ADD(SUBSUP)
457     TAGS_MODULE_ADD(DoNothing)
458 
459 TAGS_MODULE_END(Layout)
460 
461 #endif
462