Fix infinite loop in wxHtmlEasyPrinting page break code.
[wxWidgets.git] / src / html / m_layout.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/html/m_layout.cpp
3 // Purpose: wxHtml module for basic paragraphs/layout handling
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/image.h"
20 #endif
21
22 #include "wx/html/forcelnk.h"
23 #include "wx/html/m_templ.h"
24
25 #include "wx/html/htmlwin.h"
26
27 FORCE_LINK_ME(m_layout)
28
29 #ifdef __WXWINCE__
30 #include "wx/msw/wince/missing.h" // for bsearch()
31 #else
32 #include <stdlib.h> // bsearch()
33 #endif
34
35 //-----------------------------------------------------------------------------
36 // wxHtmlPageBreakCell
37 //-----------------------------------------------------------------------------
38
39 // Since html isn't a page-layout language, it doesn't support page
40 // page breaks directly--that requires CSS2 support. But a page-break
41 // facility is handy, and has been requested more than once on the
42 // mailing lists. This wxHtml tag handler implements just enough of
43 // CSS2 to support a page break by recognizing only
44 // <div style="page-break-before:always">
45 //
46 // wxHtml maintains page breaks in wxHtmlPrintout::m_PageBreaks. The
47 // tag handler below adds appropriate offsets to that array member.
48 // wxHtmlDCRenderer::Render() accesses that array and makes a new page
49 // begin after each page-break tag.
50
51 // The page-break handler does all its work in AdjustPagebreak(). For
52 // all tag handlers, that function adjusts the page-break position.
53 // For other tags, it determines whether the html element can fit on
54 // the remainder of the page; if it cannot fit, but must not be split,
55 // then the function moves the page break provided in the argument up,
56 // and returns 'true' to inform the caller that the argument was
57 // modified.
58 //
59 // Due to its special purpose, the page-break facility differs from
60 // other tags. It takes up no space, but it behaves as though there is
61 // never enough room to fit it on the remainder of the page--it always
62 // forces a page break. Therefore, unlike other elements that trigger
63 // a page break, it would never 'fit' on the following page either.
64 // Therefore it's necessary to compare each pagebreak candidate to the
65 // array wxHtmlPrintout::m_PageBreaks of pagebreaks already set, and
66 // set a new one only if it's not in that array.
67
68 class wxHtmlPageBreakCell : public wxHtmlCell
69 {
70 public:
71 wxHtmlPageBreakCell() {}
72
73 bool AdjustPagebreak(int* pagebreak,
74 const wxArrayInt& known_pagebreaks,
75 int pageHeight) const;
76
77 void Draw(wxDC& WXUNUSED(dc),
78 int WXUNUSED(x), int WXUNUSED(y),
79 int WXUNUSED(view_y1), int WXUNUSED(view_y2),
80 wxHtmlRenderingInfo& WXUNUSED(info)) {}
81
82 private:
83 wxDECLARE_NO_COPY_CLASS(wxHtmlPageBreakCell);
84 };
85
86 bool
87 wxHtmlPageBreakCell::AdjustPagebreak(int* pagebreak,
88 const wxArrayInt& known_pagebreaks,
89 int WXUNUSED(pageHeight)) const
90 {
91 // When we are counting pages, 'known_pagebreaks' is non-NULL.
92 // That's the only time we change 'pagebreak'. Otherwise, pages
93 // were already counted, 'known_pagebreaks' is NULL, and we don't
94 // do anything except return false.
95 //
96 // We also simply return false if the 'pagebreak' argument is
97 // less than (vertically above) or the same as the current
98 // vertical position. Otherwise we'd be setting a pagebreak above
99 // the current cell, which is incorrect, or duplicating a
100 // pagebreak that has already been set.
101 if( known_pagebreaks.GetCount() == 0 || *pagebreak <= m_PosY)
102 {
103 return false;
104 }
105
106 // m_PosY is only the vertical offset from the parent. The pagebreak
107 // required here is the total page offset, so m_PosY must be added
108 // to the parent's offset and height.
109 int total_height = m_PosY;
110 for ( wxHtmlCell *parent = GetParent(); parent; parent = parent->GetParent() )
111 {
112 total_height += parent->GetPosY();
113 }
114
115
116 // Search the array of pagebreaks to see whether we've already set
117 // a pagebreak here.
118 int where = known_pagebreaks.Index( total_height);
119 // Add a pagebreak only if there isn't one already set here.
120 if( wxNOT_FOUND != where)
121 {
122 return false;
123 }
124 else
125 {
126 *pagebreak = m_PosY;
127 return true;
128 }
129 }
130
131
132
133 TAG_HANDLER_BEGIN(P, "P")
134 TAG_HANDLER_CONSTR(P) { }
135
136 TAG_HANDLER_PROC(tag)
137 {
138 if (m_WParser->GetContainer()->GetFirstChild() != NULL)
139 {
140 m_WParser->CloseContainer();
141 m_WParser->OpenContainer();
142 }
143 m_WParser->GetContainer()->SetIndent(m_WParser->GetCharHeight(), wxHTML_INDENT_TOP);
144 m_WParser->GetContainer()->SetAlign(tag);
145 return false;
146 }
147
148 TAG_HANDLER_END(P)
149
150
151
152 TAG_HANDLER_BEGIN(BR, "BR")
153 TAG_HANDLER_CONSTR(BR) { }
154
155 TAG_HANDLER_PROC(tag)
156 {
157 int al = m_WParser->GetContainer()->GetAlignHor();
158 wxHtmlContainerCell *c;
159
160 m_WParser->CloseContainer();
161 c = m_WParser->OpenContainer();
162 c->SetAlignHor(al);
163 c->SetAlign(tag);
164 c->SetMinHeight(m_WParser->GetCharHeight());
165 return false;
166 }
167
168 TAG_HANDLER_END(BR)
169
170
171
172 TAG_HANDLER_BEGIN(CENTER, "CENTER")
173 TAG_HANDLER_CONSTR(CENTER) { }
174
175 TAG_HANDLER_PROC(tag)
176 {
177 int old = m_WParser->GetAlign();
178 wxHtmlContainerCell *c = m_WParser->GetContainer();
179
180 m_WParser->SetAlign(wxHTML_ALIGN_CENTER);
181 if (c->GetFirstChild() != NULL)
182 {
183 m_WParser->CloseContainer();
184 m_WParser->OpenContainer();
185 }
186 else
187 c->SetAlignHor(wxHTML_ALIGN_CENTER);
188
189 if (tag.HasEnding())
190 {
191 ParseInner(tag);
192
193 m_WParser->SetAlign(old);
194 if (c->GetFirstChild() != NULL)
195 {
196 m_WParser->CloseContainer();
197 m_WParser->OpenContainer();
198 }
199 else
200 c->SetAlignHor(old);
201
202 return true;
203 }
204 else return false;
205 }
206
207 TAG_HANDLER_END(CENTER)
208
209
210
211 TAG_HANDLER_BEGIN(DIV, "DIV")
212 TAG_HANDLER_CONSTR(DIV) { }
213
214 TAG_HANDLER_PROC(tag)
215 {
216 if(tag.HasParam(wxT("STYLE")))
217 {
218 if(tag.GetParam(wxT("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")
284 TAG_HANDLER_CONSTR(TITLE) { }
285
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")
314 TAG_HANDLER_CONSTR(BODY) { }
315
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 if (tag.HasParam(wxT("BACKGROUND")))
335 {
336 wxFSFile *fileBgImage = m_WParser->OpenURL
337 (
338 wxHTML_URL_IMAGE,
339 tag.GetParam(wxT("BACKGROUND"))
340 );
341 if ( fileBgImage )
342 {
343 wxInputStream *is = fileBgImage->GetStream();
344 if ( is )
345 {
346 wxImage image(*is);
347 if ( image.IsOk() )
348 winIface->SetHTMLBackgroundImage(image);
349 }
350
351 delete fileBgImage;
352 }
353 }
354
355 if (tag.GetParamAsColour(wxT("BGCOLOR"), &clr))
356 {
357 m_WParser->GetContainer()->InsertCell(
358 new wxHtmlColourCell(clr, wxHTML_CLR_BACKGROUND));
359 winIface->SetHTMLBackgroundColour(clr);
360 }
361
362 return false;
363 }
364
365 TAG_HANDLER_END(BODY)
366
367
368
369 TAG_HANDLER_BEGIN(BLOCKQUOTE, "BLOCKQUOTE")
370 TAG_HANDLER_CONSTR(BLOCKQUOTE) { }
371
372 TAG_HANDLER_PROC(tag)
373 {
374 wxHtmlContainerCell *c;
375
376 m_WParser->CloseContainer();
377 c = m_WParser->OpenContainer();
378
379 if (c->GetAlignHor() == wxHTML_ALIGN_RIGHT)
380 c->SetIndent(5 * m_WParser->GetCharWidth(), wxHTML_INDENT_RIGHT);
381 else
382 c->SetIndent(5 * m_WParser->GetCharWidth(), wxHTML_INDENT_LEFT);
383
384 c->SetIndent(m_WParser->GetCharHeight(), wxHTML_INDENT_TOP);
385 m_WParser->OpenContainer();
386 ParseInner(tag);
387 c = m_WParser->CloseContainer();
388 c->SetIndent(m_WParser->GetCharHeight(), wxHTML_INDENT_BOTTOM);
389 m_WParser->CloseContainer();
390 m_WParser->OpenContainer();
391 return true;
392 }
393
394 TAG_HANDLER_END(BLOCKQUOTE)
395
396
397
398 TAG_HANDLER_BEGIN(SUBSUP, "SUB,SUP")
399
400 TAG_HANDLER_PROC(tag)
401 {
402 bool issub = (tag.GetName() == wxT("SUB"));
403 wxHtmlScriptMode oldmode = m_WParser->GetScriptMode();
404 int oldbase = m_WParser->GetScriptBaseline();
405 int oldsize = m_WParser->GetFontSize();
406
407 wxHtmlContainerCell *cont = m_WParser->GetContainer();
408 wxHtmlCell *c = cont->GetLastChild();
409
410 m_WParser->SetScriptMode(issub ? wxHTML_SCRIPT_SUB : wxHTML_SCRIPT_SUP);
411 m_WParser->SetScriptBaseline(
412 oldbase + c ? c->GetScriptBaseline() : 0);
413
414 // select smaller font
415 m_WParser->SetFontSize(m_WParser->GetFontSize()-2);
416 cont->InsertCell(new wxHtmlFontCell(m_WParser->CreateCurrentFont()));
417
418 ParseInner(tag);
419
420 // restore font size
421 m_WParser->SetFontSize(oldsize);
422 m_WParser->GetContainer()->InsertCell(
423 new wxHtmlFontCell(m_WParser->CreateCurrentFont()));
424
425 // restore base and alignment
426 m_WParser->SetScriptBaseline(oldbase);
427 m_WParser->SetScriptMode(oldmode);
428
429 return true;
430 }
431
432 TAG_HANDLER_END(SUBSUP)
433
434
435 // Tag handler for tags that we have to ignore, otherwise non-text data
436 // would show up as text:
437 TAG_HANDLER_BEGIN(DoNothing, "SCRIPT")
438 TAG_HANDLER_CONSTR(DoNothing) { }
439
440 TAG_HANDLER_PROC(WXUNUSED(tag))
441 {
442 return true;
443 }
444 TAG_HANDLER_END(DoNothing)
445
446
447
448
449
450 TAGS_MODULE_BEGIN(Layout)
451
452 TAGS_MODULE_ADD(P)
453 TAGS_MODULE_ADD(BR)
454 TAGS_MODULE_ADD(CENTER)
455 TAGS_MODULE_ADD(DIV)
456 TAGS_MODULE_ADD(TITLE)
457 TAGS_MODULE_ADD(BODY)
458 TAGS_MODULE_ADD(BLOCKQUOTE)
459 TAGS_MODULE_ADD(SUBSUP)
460 TAGS_MODULE_ADD(DoNothing)
461
462 TAGS_MODULE_END(Layout)
463
464 #endif