MythTV master
Text.cpp
Go to the documentation of this file.
1/* Text.cpp
2
3 Copyright (C) David C. J. Matthews 2004, 2008 dm at prolingua.co.uk
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
18 Or, point your browser to http://www.gnu.org/copyleft/gpl.html
19
20*/
21#include "Text.h"
22
23#include <algorithm>
24#include <array>
25#include <cstdint>
26#include <cstdio>
27#include <cstdlib>
28#include <cstring>
29
30#include <QRect>
31#include <QString>
32
33#include "Visible.h"
34#include "Presentable.h"
35#include "Ingredients.h"
36#include "Root.h"
37#include "BaseClasses.h"
38#include "ParseNode.h"
39#include "ASN1Codes.h"
40#include "Engine.h"
41#include "Logging.h"
42#include "freemheg.h"
43
44MHText::MHText(const MHText &ref) // Copy constructor for cloning.
45 : MHVisible(ref),
46 m_nCharSet(ref.m_nCharSet),
47 m_horizJ(ref.m_horizJ),
48 m_vertJ(ref.m_vertJ),
49 m_lineOrientation(ref.m_lineOrientation),
50 m_startCorner(ref.m_startCorner),
51 m_fTextWrap(ref.m_fTextWrap),
52 m_fNeedsRedraw(ref.m_fNeedsRedraw)
53{
58}
59
61{
62 delete(m_pDisplay);
63}
64
65
67{
68 MHVisible::Initialise(p, engine);
69 // Font and attributes.
70 MHParseNode *pFontBody = p->GetNamedArg(C_ORIGINAL_FONT);
71
72 if (pFontBody)
73 {
74 m_origFont.Initialise(pFontBody->GetArgN(0), engine);
75 }
76
77 MHParseNode *pFontAttrs = p->GetNamedArg(C_FONT_ATTRIBUTES);
78
79 if (pFontAttrs)
80 {
82 }
83
84 // Colours
85 MHParseNode *pTextColour = p->GetNamedArg(C_TEXT_COLOUR);
86
87 if (pTextColour)
88 {
89 m_originalTextColour.Initialise(pTextColour->GetArgN(0), engine);
90 }
91
92 MHParseNode *pBGColour = p->GetNamedArg(C_BACKGROUND_COLOUR);
93
94 if (pBGColour)
95 {
96 m_originalBgColour.Initialise(pBGColour->GetArgN(0), engine);
97 }
98
99 // Character set
100 MHParseNode *pChset = p->GetNamedArg(C_CHARACTER_SET);
101
102 if (pChset)
103 {
104 m_nCharSet = pChset->GetArgN(0)->GetIntValue();
105 }
106
107 // Justification
108 MHParseNode *pHJust = p->GetNamedArg(C_HORIZONTAL_JUSTIFICATION);
109
110 if (pHJust)
111 {
112 m_horizJ = (enum Justification)pHJust->GetArgN(0)->GetEnumValue();
113 }
114
115 MHParseNode *pVJust = p->GetNamedArg(C_VERTICAL_JUSTIFICATION);
116
117 if (pVJust)
118 {
119 m_vertJ = (enum Justification)pVJust->GetArgN(0)->GetEnumValue();
120 }
121
122 // Line orientation
123 MHParseNode *pLineO = p->GetNamedArg(C_LINE_ORIENTATION);
124
125 if (pLineO)
126 {
128 }
129
130 // Start corner
131 MHParseNode *pStartC = p->GetNamedArg(C_START_CORNER);
132
133 if (pStartC)
134 {
135 m_startCorner = (enum StartCorner)pStartC->GetArgN(0)->GetEnumValue();
136 }
137
138 // Text wrapping
139 MHParseNode *pTextWrap = p->GetNamedArg(C_TEXT_WRAPPING);
140
141 if (pTextWrap)
142 {
143 m_fTextWrap = pTextWrap->GetArgN(0)->GetBoolValue();
144 }
145
146 m_pDisplay = engine->GetContext()->CreateText();
147 m_fNeedsRedraw = true;
148}
149
150static const std::array<const QString,4> rchJustification
151{
152 "start", // 1
153 "end",
154 "centre",
155 "justified" // 4
156};
157
158// Look up the Justification. Returns zero if it doesn't match. Used in the text parser only.
159int MHText::GetJustification(const QString& str)
160{
161 for (size_t i = 0; i < rchJustification.size(); i++)
162 {
163 if (str.compare(rchJustification[i], Qt::CaseInsensitive) == 0)
164 {
165 return (i + 1); // Numbered from 1
166 }
167 }
168
169 return 0;
170}
171
172static const std::array<const QString,2> rchlineOrientation
173{
174 "vertical", // 1
175 "horizontal"
176};
177
178int MHText::GetLineOrientation(const QString& str)
179{
180 for (size_t i = 0; i < rchlineOrientation.size(); i++)
181 {
182 if (str.compare(rchlineOrientation[i], Qt::CaseInsensitive) == 0)
183 {
184 return (i + 1);
185 }
186 }
187
188 return 0;
189}
190
191static const std::array<const QString,4> rchStartCorner
192{
193 "upper-left", // 1
194 "upper-right",
195 "lower-left",
196 "lower-right" // 4
197};
198
199int MHText::GetStartCorner(const QString& str)
200{
201 for (size_t i = 0; i < rchStartCorner.size(); i++)
202 {
203 if (str.compare(rchStartCorner[i], Qt::CaseInsensitive) == 0)
204 {
205 return (i + 1);
206 }
207 }
208
209 return 0;
210}
211
212void MHText::PrintMe(FILE *fd, int nTabs) const
213{
214 PrintTabs(fd, nTabs);
215 fprintf(fd, "{:Text ");
216 MHVisible::PrintMe(fd, nTabs + 1);
217
218 if (m_origFont.IsSet())
219 {
220 PrintTabs(fd, nTabs + 1);
221 fprintf(fd, ":OrigFont ");
222 m_origFont.PrintMe(fd, nTabs + 1);
223 fprintf(fd, "\n");
224 }
225
226 if (m_originalFontAttrs.Size() > 0)
227 {
228 PrintTabs(fd, nTabs + 1);
229 fprintf(fd, ":FontAttributes ");
230 m_originalFontAttrs.PrintMe(fd, nTabs + 1);
231 fprintf(fd, "\n");
232 }
233
235 {
236 PrintTabs(fd, nTabs + 1);
237 fprintf(fd, ":TextColour ");
238 m_originalTextColour.PrintMe(fd, nTabs + 1);
239 fprintf(fd, "\n");
240 }
241
243 {
244 PrintTabs(fd, nTabs + 1);
245 fprintf(fd, ":BackgroundColour ");
246 m_originalBgColour.PrintMe(fd, nTabs + 1);
247 fprintf(fd, "\n");
248 }
249
250 if (m_nCharSet >= 0)
251 {
252 PrintTabs(fd, nTabs + 1);
253 fprintf(fd, ":CharacterSet %d\n", m_nCharSet);
254 }
255
256 if (m_horizJ != Start)
257 {
258 PrintTabs(fd, nTabs + 1);
259 fprintf(fd, ":HJustification %s\n", qPrintable(rchJustification[m_horizJ-1]));
260 }
261
262 if (m_vertJ != Start)
263 {
264 PrintTabs(fd, nTabs + 1);
265 fprintf(fd, ":VJustification %s\n", qPrintable(rchJustification[m_vertJ-1]));
266 }
267
269 {
270 PrintTabs(fd, nTabs + 1);
271 fprintf(fd, ":LineOrientation %s\n", qPrintable(rchlineOrientation[m_lineOrientation-1]));
272 }
273
275 {
276 PrintTabs(fd, nTabs + 1);
277 fprintf(fd, ":StartCorner %s\n", qPrintable(rchStartCorner[m_startCorner-1]));
278 }
279
280 if (m_fTextWrap)
281 {
282 PrintTabs(fd, nTabs + 1);
283 fprintf(fd, ":TextWrapping true\n");
284 }
285
286 PrintTabs(fd, nTabs);
287 fprintf(fd, "}\n");
288}
289
291{
292 if (m_fAvailable)
293 {
294 return;
295 }
296
297 // Set the colours and font up from the originals if specified otherwise use the application defaults.
298 // if (m_origFont.IsSet()) m_font.Copy(m_origFont);
299 // else m_font.Copy(engine->m_DefaultFont);
301 {
303 }
304 else
305 {
307 }
308
310 {
312 }
313 else
314 {
316 }
317
318 if (m_originalFontAttrs.Size() > 0)
319 {
321 }
322 else
323 {
325 }
326
328
329 if (m_pDisplay == nullptr)
330 {
331 m_pDisplay = engine->GetContext()->CreateText();
332 }
333
335 m_fNeedsRedraw = true;
336}
337
338// Content preparation. If it's included we can set up the content.
340{
342
343 if (m_contentType == IN_NoContent)
344 {
345 MHERROR("Text object must have content");
346 }
347
348 if (m_contentType == IN_IncludedContent)
349 {
351 }
352}
353
354// Called when external content is available.
355void MHText::ContentArrived(const unsigned char *data, int length, MHEngine *engine)
356{
357 CreateContent(data, length, engine);
358 MHLOG(MHLogDetail, QString("Content arrived %1 := '%2'")
360 // Now signal that the content is available.
362 m_fNeedsRedraw = true;
363}
364
365//
366void MHText::CreateContent(const unsigned char *p, int s, MHEngine *engine)
367{
368 m_content.Copy(MHOctetString((const char *)p, s));
369 engine->Redraw(GetVisibleArea()); // Have to redraw if the content has changed.
370 m_fNeedsRedraw = true;
371 // fprintf(fd, "Text content is now "); m_content.PrintMe(0); fprintf(fd, "\n");
372}
373
374void MHText::SetTextColour(const MHColour &colour, MHEngine *engine)
375{
376 m_textColour.Copy(colour);
377 m_fNeedsRedraw = true;
378 engine->Redraw(GetVisibleArea());
379}
380
382{
383 m_bgColour.Copy(colour);
384 // Setting the background colour doesn't affect the text image but we have to
385 // redraw it onto the display.
386 engine->Redraw(GetVisibleArea());
387}
388
389void MHText::SetFontAttributes(const MHOctetString &fontAttrs, MHEngine *engine)
390{
391 m_fontAttrs.Copy(fontAttrs);
392 m_fNeedsRedraw = true;
393 engine->Redraw(GetVisibleArea());
394}
395
396// UK MHEG. Interpret the font attributes.
397static void InterpretAttributes(const MHOctetString &attrs, int &style, int &size, int &lineSpace, int &letterSpace)
398{
399 // Set the defaults.
400 style = 0;
401 size = 0x18;
402 lineSpace = 0x18;
403 letterSpace = 0;
404
405 if (attrs.Size() == 5) // Short form.
406 {
407 style = attrs.GetAt(0); // Only the bottom nibble is significant.
408 size = attrs.GetAt(1);
409 lineSpace = attrs.GetAt(2);
410 // Is this big-endian or little-endian? Assume big.
411 letterSpace = (attrs.GetAt(3) * 256) + attrs.GetAt(4);
412
413 if (letterSpace > 32767)
414 {
415 letterSpace -= 65536; // Signed.
416 }
417 }
418 else // Textual form.
419 {
420 const unsigned char *str = attrs.Bytes();
421 char *p = (char *)str;
422 char *q = strchr(p, '.'); // Find the terminating dot
423
424 if (q != nullptr) // plain, italic etc.
425 {
426 if (q - p == 6 && strncmp(p, "italic", 6) == 0)
427 {
428 style = 1;
429 }
430 else if (q - p == 4 && strncmp(p, "bold", 4) == 0)
431 {
432 style = 2;
433 }
434 else if (q - p == 11 && strncmp(p, "bold-italic", 11) == 0)
435 {
436 style = 3;
437 }
438
439 // else it's plain.
440 p = q + 1;
441 q = strchr(p, '.'); // Find the next dot.
442 }
443
444 if (q != nullptr) // Size
445 {
446 size = atoi(p);
447
448 if (size == 0)
449 {
450 size = 0x18;
451 }
452
453 p = q + 1;
454 q = strchr(p, '.'); // Find the next dot.
455 }
456
457 if (q != nullptr) // lineSpacing
458 {
459 lineSpace = atoi(p);
460
461 if (lineSpace == 0)
462 {
463 lineSpace = 0x18;
464 }
465
466 p = q + 1;
467 q = strchr(p, '.'); // Find the next dot.
468 }
469
470 if (q != nullptr) // letter spacing. May be zero or negative
471 {
472 letterSpace = atoi(p);
473 }
474 }
475}
476
477// We break the text up into pieces of line segment.
479{
480 public:
481 MHTextItem();
482 MHOctetString m_text; // UTF-8 text
483 QString m_unicode; // Unicode text
484 int m_nUnicode{0}; // Number of characters in it
485 int m_width{0}; // Size of this block
486 MHRgba m_colour; // Colour of the text
487 int m_nTabCount{0}; // Number of tabs immediately before this (usually zero)
488
489 // Generate new items inheriting properties from the previous
490 MHTextItem *NewItem() const;
491};
492
494{
495 m_colour = MHRgba(0, 0, 0, 255);
496}
497
499{
500 auto *pItem = new MHTextItem;
501 pItem->m_colour = m_colour;
502 return pItem;
503}
504
505// A line consists of one or more sequences of text items.
507{
508 public:
509 MHTextLine() = default;
510 ~MHTextLine();
514 int m_nDescent {0};
515};
516
518{
519 for (int i = 0; i < m_items.Size(); i++)
520 {
521 delete(m_items.GetAt(i));
522 }
523}
524
525// Tabs are set every 56 pixels
526// = (FONT_WIDTHRES * 56)/ 72 points - see libmythtv/mhi.cpp
527// = (54 * 56)/72 = 42
528static constexpr int8_t TABSTOP { 42 }; // pts
529static inline int Tabs(int nXpos, int nTabCount)
530{
531 int nNextTab = nXpos;
532 if (nTabCount-- > 0)
533 {
534 nNextTab += TABSTOP - (nXpos % TABSTOP);
535 nNextTab += nTabCount * TABSTOP;
536 }
537 return nNextTab;
538}
539
540// I attempted to use QSimpleRichText but that does not give sufficient fine control over
541// the layout. UK MHEG specifies the use of the Tiresias font and broadcasters appear to
542// assume that all MHEG applications will lay the text out in the same way.
543
544// Recreate the image.
546{
547 if (! m_fRunning || !m_pDisplay)
548 {
549 return;
550 }
551
552 if (m_nBoxWidth == 0 || m_nBoxHeight == 0)
553 {
554 return; // Can't draw zero sized boxes.
555 }
556
558 m_pDisplay->Clear();
559
560 MHRgba textColour = GetColour(m_textColour);
561 // Process any escapes in the text and construct the text arrays.
563 // Set up the first item on the first line.
564 auto *pCurrItem = new MHTextItem;
565 auto *pCurrLine = new MHTextLine;
566 pCurrLine->m_items.Append(pCurrItem);
567 theText.Append(pCurrLine);
568 MHStack <MHRgba> colourStack; // Stack to handle nested colour codes.
569 colourStack.Push(textColour);
570 pCurrItem->m_colour = textColour;
571
572// FILE *fd=stdout; fprintf(fd, "Redraw Text "); m_content.PrintMe(fd, 0); fprintf(fd, "\n");
573 int i = 0;
574
575 while (i < m_content.Size())
576 {
577 unsigned char ch = m_content.GetAt(i++);
578
579 if (ch == 0x09) // Tab - start a new item if we have any text in the existing one.
580 {
581 if (pCurrItem->m_text.Size() != 0)
582 {
583 pCurrItem = pCurrItem->NewItem();
584 pCurrLine->m_items.Append(pCurrItem);
585 }
586 if (m_horizJ == Start)
587 pCurrItem->m_nTabCount++;
588 }
589
590 else if (ch == 0x0d) // CR - line break.
591 {
592 // TODO: Two CRs next to one another are treated as </P> rather than <BR><BR>
593 // This should also include the sequence CRLFCRLF.
594 pCurrLine = new MHTextLine;
595 theText.Append(pCurrLine);
596 pCurrItem = pCurrItem->NewItem();
597 pCurrLine->m_items.Append(pCurrItem);
598 }
599
600 else if (ch == 0x1b) // Escape - special codes.
601 {
602 if (i == m_content.Size())
603 {
604 break;
605 }
606
607 unsigned char code = m_content.GetAt(i);
608 // The only codes we are interested in are the start and end of colour.
609 // TODO: We may also need "bold" and some hypertext colours.
610
611 if (code >= 0x40 && code <= 0x5e) // Start code
612 {
613 // Start codes are followed by a parameter count and a number of parameter bytes.
614 if (++i == m_content.Size())
615 {
616 break;
617 }
618
619 unsigned char paramCount = m_content.GetAt(i);
620 i++;
621
622 if (code == 0x43 && paramCount == 4 && i + paramCount <= m_content.Size())
623 {
624 // Start of colour.
625 if (pCurrItem->m_text.Size() != 0)
626 {
627 pCurrItem = pCurrItem->NewItem();
628 pCurrLine->m_items.Append(pCurrItem);
629 }
630
631 pCurrItem->m_colour = MHRgba(m_content.GetAt(i), m_content.GetAt(i + 1),
632 m_content.GetAt(i + 2), 255 - m_content.GetAt(i + 3));
633 // Push this colour onto the colour stack.
634 colourStack.Push(pCurrItem->m_colour);
635 }
636 else
637 {
638 MHLOG(MHLogWarning, QString("Unknown text escape code 0x%1").arg(code, 2, 16));
639 }
640
641 i += paramCount; // Skip the parameters
642 }
643 else if (code >= 0x60 && code <= 0x7e) // End code.
644 {
645 i++;
646
647 if (code == 0x63)
648 {
649 if (colourStack.Size() > 1)
650 {
651 colourStack.Pop();
652
653 // Start a new item since we're using a new colour.
654 if (pCurrItem->m_text.Size() != 0)
655 {
656 pCurrItem = pCurrItem->NewItem();
657 pCurrLine->m_items.Append(pCurrItem);
658 }
659
660 // Set the subsequent text in the colour we're using now.
661 pCurrItem->m_colour = colourStack.Top();
662 }
663 }
664 else
665 {
666 MHLOG(MHLogWarning, QString("Unknown text escape code 0x%1").arg(code,2,16));
667 }
668 }
669 else
670 {
671 MHLOG(MHLogWarning, QString("Unknown text escape code 0x%1").arg(code,2,16));
672 }
673 }
674
675 else if (ch <= 0x1f)
676 {
677 // Certain characters including LF and the marker codes between 0x1c and 0x1f are
678 // explicitly intended to be ignored. Include all the other codes.
679 }
680
681 else // Add to the current text.
682 {
683 int nStart = i - 1;
684
685 while (i < m_content.Size() && m_content.GetAt(i) >= 0x20)
686 {
687 i++;
688 }
689
690 pCurrItem->m_text.Append(MHOctetString(m_content, nStart, i - nStart));
691 }
692 }
693
694 // Set up the initial attributes.
695 int style = 0;
696 int size = 0;
697 int lineSpace = 0;
698 int letterSpace = 0;
699 InterpretAttributes(m_fontAttrs, style, size, lineSpace, letterSpace);
700 // Create a font with this information.
701 m_pDisplay->SetFont(size, (style & 2) != 0, (style & 1) != 0);
702
703 // Calculate the layout of each section.
704 for (i = 0; i < theText.Size(); i++)
705 {
706 MHTextLine *pLine = theText.GetAt(i);
707 pLine->m_nLineWidth = 0;
708
709 for (int j = 0; j < pLine->m_items.Size(); j++)
710 {
711 MHTextItem *pItem = pLine->m_items.GetAt(j);
712
713 // Set any tabs.
714 pLine->m_nLineWidth = Tabs(pLine->m_nLineWidth, pItem->m_nTabCount);
715
716 if (pItem->m_unicode.isEmpty()) // Convert UTF-8 to Unicode.
717 {
718 int s = pItem->m_text.Size();
719 pItem->m_unicode = QString::fromUtf8((const char *)pItem->m_text.Bytes(), s);
720 pItem->m_nUnicode = pItem->m_unicode.length();
721 }
722
723 // Fit the text onto the line.
724 int nFullText = pItem->m_nUnicode;
725 // Get the box size and update pItem->m_nUnicode to the number that will fit.
726 QRect rect = m_pDisplay->GetBounds(pItem->m_unicode, pItem->m_nUnicode, m_nBoxWidth - pLine->m_nLineWidth);
727
728 if (nFullText != pItem->m_nUnicode && m_fTextWrap) // Doesn't fit, we have to word-wrap.
729 {
730 int nTruncated = pItem->m_nUnicode; // Just in case.
731 // Now remove characters until we find a word-break character.
732 while (pItem->m_nUnicode > 0 && pItem->m_unicode[pItem->m_nUnicode] != ' ')
733 {
734 pItem->m_nUnicode--;
735 }
736
737 // If there are now word-break characters we truncate the text.
738 if (pItem->m_nUnicode == 0)
739 {
740 pItem->m_nUnicode = nTruncated;
741 }
742
743 // Special case to avoid infinite loop if the box is very narrow.
744 if (pItem->m_nUnicode == 0)
745 {
746 pItem->m_nUnicode = 1;
747 }
748
749 // We need to move the text we've cut off this line into a new line.
750 int nNewWidth = nFullText - pItem->m_nUnicode;
751 int nNewStart = pItem->m_nUnicode;
752
753 // Remove any spaces at the start of the new section.
754 while (nNewWidth != 0 && pItem->m_unicode[nNewStart] == ' ')
755 {
756 nNewStart++;
757 nNewWidth--;
758 }
759
760 if (nNewWidth != 0)
761 {
762 // Create a new line from the extra text.
763 auto *pNewLine = new MHTextLine;
764 theText.InsertAt(pNewLine, i + 1);
765 // The first item on the new line is the rest of the text.
766 MHTextItem *pNewItem = pItem->NewItem();
767 pNewLine->m_items.Append(pNewItem);
768 pNewItem->m_unicode = pItem->m_unicode.mid(nNewStart, nNewWidth);
769 pNewItem->m_nUnicode = nNewWidth;
770
771 // Move any remaining items, e.g. in a different colour, from this line onto the new line.
772 while (pLine->m_items.Size() > j + 1)
773 {
774 pNewLine->m_items.Append(pLine->m_items.GetAt(j + 1));
775 pLine->m_items.RemoveAt(j + 1);
776 }
777 }
778
779 // Remove any spaces at the end of the old section. If we don't do that and
780 // we are centering or right aligning the text we'll get it wrong.
781 while (pItem->m_nUnicode > 1 && pItem->m_unicode[pItem->m_nUnicode-1] == ' ')
782 {
783 pItem->m_nUnicode--;
784 }
785
786 rect = m_pDisplay->GetBounds(pItem->m_unicode, pItem->m_nUnicode);
787 }
788
789 pItem->m_width = rect.width();
790 pLine->m_nLineWidth += rect.width();
791
792 pLine->m_nLineHeight = std::max(rect.height(), pLine->m_nLineHeight);
793
794 pLine->m_nDescent = std::max(rect.bottom(), pLine->m_nDescent);
795 }
796 }
797
798 // Now output the text.
799 int yOffset = 0;
800 // If there isn't space for all the lines we should drop extra lines.
801 int nNumLines = theText.Size();
802 int divisor = (m_vertJ == Centre) ? 2 : 1;
803 yOffset = (m_nBoxHeight - (nNumLines * lineSpace)) / divisor;
804 while (yOffset < 0)
805 {
806 nNumLines--;
807 yOffset = (m_nBoxHeight - (nNumLines * lineSpace)) / divisor;
808 }
809
810 for (i = 0; i < nNumLines; i++)
811 {
812 MHTextLine *pLine = theText.GetAt(i);
813 int xOffset = 0;
814
815 if (m_horizJ == End)
816 {
817 xOffset = m_nBoxWidth - pLine->m_nLineWidth;
818 }
819 else if (m_horizJ == Centre)
820 {
821 xOffset = (m_nBoxWidth - pLine->m_nLineWidth) / 2;
822 }
823
824 for (int j = 0; j < pLine->m_items.Size(); j++)
825 {
826 MHTextItem *pItem = pLine->m_items.GetAt(j);
827
828 // Tab across if necessary.
829 xOffset = Tabs(xOffset, pItem->m_nTabCount);
830
831 if (! pItem->m_unicode.isEmpty()) // We may have blank lines.
832 {
833 m_pDisplay->AddText(xOffset, yOffset + ((pLine->m_nLineHeight + lineSpace) / 2) - pLine->m_nDescent,
834 pItem->m_unicode.left(pItem->m_nUnicode), pItem->m_colour);
835 }
836
837 xOffset += pItem->m_width;
838 }
839
840 yOffset += lineSpace;
841
842 if (yOffset + lineSpace > m_nBoxHeight)
843 {
844 break;
845 }
846 }
847
848 // Clean up.
849 for (int k = 0; k < theText.Size(); k++)
850 {
851 delete(theText.GetAt(k));
852 }
853}
854
856{
857 if (! m_fRunning || ! m_pDisplay || m_nBoxWidth == 0 || m_nBoxHeight == 0)
858 {
859 return; // Can't draw zero sized boxes.
860 }
861
862 // We only need to recreate the display if something has changed.
863 if (m_fNeedsRedraw)
864 {
865 Redraw();
866 m_fNeedsRedraw = false;
867 }
868
869 // Draw the background first, then the text.
872}
873
874// Return the area actually obscured. This is empty unless the background is opaque.
876{
877 if (! m_fRunning || (GetColour(m_bgColour)).alpha() != 255)
878 {
879 return {};
880 }
881 return {QRect(m_nPosX, m_nPosY, m_nBoxWidth, m_nBoxHeight)};
882}
883
884
886{
887 MHText::Initialise(p, engine);
889 //
890}
891
892void MHHyperText::PrintMe(FILE *fd, int nTabs) const
893{
894 PrintTabs(fd, nTabs);
895 fprintf(fd, "{:HyperText ");
896 MHText::PrintMe(fd, nTabs + 1);
897 MHInteractible::PrintMe(fd, nTabs + 1);
898 fprintf(fd, "****TODO\n");
899 PrintTabs(fd, nTabs);
900 fprintf(fd, "}\n");
901}
902
903
905{
906 MHElemAction::Initialise(p, engine); // Target
907 m_fontAttrs.Initialise(p->GetArgN(1), engine); // New font attrs
908}
909
911{
912 // Get the new font attributes.
913 MHOctetString newAttrs;
914 m_fontAttrs.GetValue(newAttrs, engine);
915 Target(engine)->SetFontAttributes(newAttrs, engine);
916}
@ C_FONT_ATTRIBUTES
Definition: ASN1Codes.h:79
@ C_BACKGROUND_COLOUR
Definition: ASN1Codes.h:75
@ C_HORIZONTAL_JUSTIFICATION
Definition: ASN1Codes.h:123
@ C_TEXT_WRAPPING
Definition: ASN1Codes.h:127
@ C_TEXT_COLOUR
Definition: ASN1Codes.h:77
@ C_START_CORNER
Definition: ASN1Codes.h:126
@ C_CHARACTER_SET
Definition: ASN1Codes.h:74
@ C_VERTICAL_JUSTIFICATION
Definition: ASN1Codes.h:124
@ C_ORIGINAL_FONT
Definition: ASN1Codes.h:122
@ C_LINE_ORIENTATION
Definition: ASN1Codes.h:125
#define MHERROR(__text)
Definition: Logging.h:42
#define MHLOG(__level, __text)
Definition: Logging.h:36
void PrintTabs(FILE *fd, int n)
Definition: ParseNode.cpp:34
@ EventContentAvailable
Definition: Root.h:34
static const std::array< const QString, 2 > rchlineOrientation
Definition: Text.cpp:173
static const std::array< const QString, 4 > rchJustification
Definition: Text.cpp:151
static void InterpretAttributes(const MHOctetString &attrs, int &style, int &size, int &lineSpace, int &letterSpace)
Definition: Text.cpp:397
static constexpr int8_t TABSTOP
Definition: Text.cpp:528
static int Tabs(int nXpos, int nTabCount)
Definition: Text.cpp:529
static const std::array< const QString, 4 > rchStartCorner
Definition: Text.cpp:192
bool IsSet() const
Definition: BaseClasses.h:144
void Initialise(MHParseNode *p, MHEngine *engine)
void PrintMe(FILE *fd, int nTabs) const
void Copy(const MHColour &col)
virtual MHTextDisplay * CreateText(void)=0
virtual void DrawRect(int xPos, int yPos, int width, int height, MHRgba colour)=0
MHRoot * Target(MHEngine *engine)
Definition: BaseActions.cpp:46
virtual void Initialise(MHParseNode *p, MHEngine *engine)
Definition: BaseActions.cpp:31
void Redraw(const QRegion &region)
Definition: Engine.cpp:926
void GetDefaultBGColour(MHColour &colour)
Definition: Engine.cpp:1402
MHContext * GetContext()
Definition: Engine.h:154
void EventTriggered(MHRoot *pSource, enum EventType ev)
Definition: Engine.h:94
void GetDefaultTextColour(MHColour &colour)
Definition: Engine.cpp:1416
void GetDefaultFontAttrs(MHOctetString &str)
Definition: Engine.cpp:1505
void Copy(const MHFontBody &fb)
void PrintMe(FILE *fd, int nTabs) const
bool IsSet() const
Definition: BaseClasses.h:315
void Initialise(MHParseNode *p, MHEngine *engine)
void Initialise(MHParseNode *p, MHEngine *engine)
void GetValue(MHOctetString &str, MHEngine *engine) const
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Text.cpp:885
void PrintMe(FILE *fd, int nTabs) const override
Definition: Text.cpp:892
void ContentPreparation(MHEngine *engine) override
enum MHIngredient::@4 IN_NoContent
MHOctetString m_includedContent
Definition: Ingredients.h:77
@ IN_IncludedContent
Definition: Ingredients.h:71
void PrintMe(FILE *fd, int nTabs) const
Definition: Visible.cpp:532
void Initialise(MHParseNode *p, MHEngine *engine)
Definition: Visible.cpp:506
QString Printable() const
void Copy(const MHOctetString &str)
const unsigned char * Bytes() const
Definition: BaseClasses.h:125
int Size() const
Definition: BaseClasses.h:120
unsigned char GetAt(int i) const
Definition: BaseClasses.h:124
void PrintMe(FILE *fd, int nTabs) const
QString Printable() const
Definition: BaseClasses.h:128
MHParseNode * GetArgN(int n)
Definition: ParseNode.cpp:78
int GetEnumValue()
Definition: ParseNode.cpp:181
void GetStringValue(MHOctetString &str)
Definition: ParseNode.cpp:203
bool GetBoolValue()
Definition: ParseNode.cpp:192
int GetIntValue()
Definition: ParseNode.cpp:170
MHObjectRef m_ObjectReference
Definition: Root.h:248
bool m_fRunning
Definition: Root.h:253
bool m_fAvailable
Definition: Root.h:252
virtual void SetFontAttributes(const MHOctetString &, MHEngine *)
Definition: Root.h:119
void RemoveAt(int i)
Definition: BaseClasses.h:66
int Size() const
Definition: BaseClasses.h:47
void Append(BASE b)
Definition: BaseClasses.h:64
void InsertAt(BASE b, int n)
Definition: BaseClasses.h:53
BASE GetAt(int i) const
Definition: BaseClasses.h:49
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Text.cpp:904
MHGenericOctetString m_fontAttrs
Definition: Text.h:159
void Perform(MHEngine *engine) override
Definition: Text.cpp:910
void Push(BASE b)
Definition: BaseClasses.h:94
int Size() const
Definition: BaseClasses.h:100
BASE Pop()
Definition: BaseClasses.h:89
BASE Top()
Definition: BaseClasses.h:96
virtual void SetFont(int size, bool isBold, bool isItalic)=0
virtual QRect GetBounds(const QString &str, int &strLen, int maxSize=-1)=0
virtual void SetSize(int width, int height)=0
virtual void Clear(void)=0
virtual void Draw(int x, int y)=0
virtual void AddText(int x, int y, const QString &, MHRgba colour)=0
int m_nTabCount
Definition: Text.cpp:487
int m_width
Definition: Text.cpp:485
MHTextItem()
Definition: Text.cpp:493
MHRgba m_colour
Definition: Text.cpp:486
MHOctetString m_text
Definition: Text.cpp:482
int m_nUnicode
Definition: Text.cpp:484
QString m_unicode
Definition: Text.cpp:483
MHTextItem * NewItem() const
Definition: Text.cpp:498
MHSequence< MHTextItem * > m_items
Definition: Text.cpp:511
int m_nDescent
Definition: Text.cpp:514
int m_nLineHeight
Definition: Text.cpp:513
MHTextLine()=default
~MHTextLine()
Definition: Text.cpp:517
int m_nLineWidth
Definition: Text.cpp:512
Definition: Text.h:38
MHColour m_bgColour
Definition: Text.h:90
LineOrientation
Definition: Text.h:80
@ Horizontal
Definition: Text.h:80
static int GetStartCorner(const QString &str)
Definition: Text.cpp:199
void SetTextColour(const MHColour &colour, MHEngine *engine) override
Definition: Text.cpp:374
Justification m_horizJ
Definition: Text.h:82
MHColour m_originalTextColour
Definition: Text.h:76
LineOrientation m_lineOrientation
Definition: Text.h:84
bool m_fNeedsRedraw
Definition: Text.h:95
MHColour m_originalBgColour
Definition: Text.h:76
void Redraw()
Definition: Text.cpp:545
MHText()=default
static int GetLineOrientation(const QString &str)
Definition: Text.cpp:178
~MHText() override
Definition: Text.cpp:60
MHTextDisplay * m_pDisplay
Definition: Text.h:94
MHOctetString m_fontAttrs
Definition: Text.h:91
void Display(MHEngine *engine) override
Definition: Text.cpp:855
void SetBackgroundColour(const MHColour &colour, MHEngine *engine) override
Definition: Text.cpp:381
StartCorner
Definition: Text.h:81
@ UpperLeft
Definition: Text.h:81
MHColour m_textColour
Definition: Text.h:90
Justification m_vertJ
Definition: Text.h:83
static int GetJustification(const QString &str)
Definition: Text.cpp:159
bool m_fTextWrap
Definition: Text.h:86
MHOctetString m_content
Definition: Text.h:92
MHOctetString m_originalFontAttrs
Definition: Text.h:75
void Preparation(MHEngine *engine) override
Definition: Text.cpp:290
int m_nCharSet
Definition: Text.h:77
StartCorner m_startCorner
Definition: Text.h:85
MHFontBody m_origFont
Definition: Text.h:74
void CreateContent(const unsigned char *p, int s, MHEngine *engine)
Definition: Text.cpp:366
void PrintMe(FILE *fd, int nTabs) const override
Definition: Text.cpp:212
void ContentPreparation(MHEngine *engine) override
Definition: Text.cpp:339
void ContentArrived(const unsigned char *data, int length, MHEngine *engine) override
Definition: Text.cpp:355
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Text.cpp:66
Justification
Definition: Text.h:79
@ Centre
Definition: Text.h:79
@ Start
Definition: Text.h:79
@ End
Definition: Text.h:79
QRegion GetOpaqueArea() override
Definition: Text.cpp:875
void SetFontAttributes(const MHOctetString &fontAttrs, MHEngine *engine) override
Definition: Text.cpp:389
void PrintMe(FILE *fd, int nTabs) const override
Definition: Visible.cpp:90
virtual QRegion GetVisibleArea()
Definition: Visible.cpp:203
int m_nBoxWidth
Definition: Visible.h:79
int m_nBoxHeight
Definition: Visible.h:80
static MHRgba GetColour(const MHColour &colour)
Definition: Visible.cpp:162
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Visible.cpp:56
int m_nPosY
Definition: Visible.h:82
void Preparation(MHEngine *engine) override
Definition: Visible.cpp:111
int m_nPosX
Definition: Visible.h:81
@ MHLogWarning
Definition: freemheg.h:78
@ MHLogDetail
Definition: freemheg.h:83
int FILE
Definition: mythburn.py:138