public virtual void SearchAndExtractSubString(MHObjectRef success, MHSequence <MHParameter> args, MHEngine engine) { if (args.Size == 5) { // Find a substring within a string and return an index to the position // and the prefix to the substring. MHOctetString str = new MHOctetString(); MHOctetString searchString = new MHOctetString(); GetString(args.GetAt(0), str, engine); int nStart = GetInt(args.GetAt(1), engine); if (nStart < 1) { nStart = 1; } GetString(args.GetAt(2), searchString, engine); // Strings are indexed from one. int nPos; for (nPos = nStart - 1; nPos <= str.Size - searchString.Size; nPos++) { int i; for (i = 0; i < searchString.Size; i++) { if (searchString.GetAt(i) != str.GetAt(i + nPos)) { break; // Doesn't match } } if (i == searchString.Size) { break; // Found a match. } } // Set the results. MHParameter pResString = args.GetAt(3); MHParameter pResInt = args.GetAt(4); SetSuccessFlag(success, true, engine); // Set this first. if (nPos <= str.Size - searchString.Size) { // Found // Set the index to the position AFTER the string, counting from 1. engine.FindObject(pResInt.GetReference()).SetVariableValue(new MHUnion(nPos + 1 + searchString.Size)); // Return the sequence from nStart - 1 of length nPos - nStart + 1 MHOctetString resultString = new MHOctetString(str, nStart - 1, nPos - nStart + 1); engine.FindObject(pResString.GetReference()).SetVariableValue(new MHUnion(resultString)); } else { // Not found. Set the result string to empty and the result index to -1 engine.FindObject(pResInt.GetReference()).SetVariableValue(new MHUnion(-1)); engine.FindObject(pResString.GetReference()).SetVariableValue(new MHUnion(new MHOctetString(""))); } } else { SetSuccessFlag(success, false, engine); } }
public virtual void SearchSubString(MHObjectRef success, MHSequence <MHParameter> args, MHEngine engine) { if (args.Size == 4) { // Find a substring within a string and return an index to the position. MHOctetString str = new MHOctetString(); MHOctetString searchString = new MHOctetString(); GetString(args.GetAt(0), str, engine); int nStart = GetInt(args.GetAt(1), engine); if (nStart < 1) { nStart = 1; } GetString(args.GetAt(2), searchString, engine); // Strings are indexed from one. int nPos; for (nPos = nStart - 1; nPos <= str.Size - searchString.Size; nPos++) { int i; for (i = 0; i < searchString.Size; i++) { if (searchString.GetAt(i) != str.GetAt(i + nPos)) { break; } } if (i == searchString.Size) { break; // Found a match. } } // Set the result. MHParameter pResInt = args.GetAt(3); SetSuccessFlag(success, true, engine); // Set this first. if (nPos <= str.Size - searchString.Size) { // Found // Set the index to the position of the string, counting from 1. engine.FindObject(pResInt.GetReference()).SetVariableValue(new MHUnion(nPos + 1)); } else { // Not found. Set the result index to -1 engine.FindObject(pResInt.GetReference()).SetVariableValue(new MHUnion(-1)); } } else { SetSuccessFlag(success, false, engine); } }
// UK MHEG. Interpret the font attributes. protected void InterpretAttributes(MHOctetString attrs, out int style, out int size, out int lineSpace, out int letterSpace) { // Set the defaults. style = 0; size = 0x18; lineSpace = 0x18; letterSpace = 0; if (attrs.Size == 5) // Short form. { style = attrs.GetAt(0); // Only the bottom nibble is significant. size = attrs.GetAt(1); lineSpace = attrs.GetAt(2); // Is this big-endian or little-endian? Assume big. letterSpace = attrs.GetAt(3) * 256 + attrs.GetAt(4); if (letterSpace > 32767) { letterSpace -= 65536; // Signed. } } else // Textual form. { String str = attrs.ToString() + "."; Logging.Assert(str != null); int q = str.IndexOf('.'); // Find the terminating dot if (q != -1) // plain, italic etc. { string type = str.Substring(0, q); str = str.Substring(q + 1); if (type.Equals("italic")) { style = 1; } else if (type.Equals("bold")) { style = 2; } else if (type.Equals("bold-italic")) { style = 3; } // else it's plain. q = str.IndexOf('.'); } if (q != -1) // Size { string s = str.Substring(0, q); str = str.Substring(q + 1); size = Convert.ToInt32(s); if (size == 0) { size = 0x18; } q = str.IndexOf('.'); // Find the next dot. } if (q != -1) // lineSpacing { string ls = str.Substring(0, q); str = str.Substring(q + 1); lineSpace = Convert.ToInt32(ls); if (lineSpace == 0) { size = 0x18; } q = str.IndexOf('.'); // Find the next dot. } if (q != -1) // letter spacing. May be zero or negative { string ls = str.Substring(0, q); letterSpace = Convert.ToInt32(ls); } } }
// UK MHEG specifies the use of the Tiresias font and broadcasters appear to // assume that all MHEG applications will lay the text out in the same way. // Recreate the image. protected void Redraw() { if (!RunningStatus || m_pDisplay == null) { return; } if (m_nBoxWidth == 0 || m_nBoxHeight == 0) { return; // Can't draw zero sized boxes. } m_pDisplay.SetSize(m_nBoxWidth, m_nBoxHeight); m_pDisplay.Clear(); MHRgba textColour = GetColour(m_textColour); // Process any escapes in the text and construct the text arrays. MHSequence <MHTextLine> theText = new MHSequence <MHTextLine>(); // Set up the first item on the first line. MHTextItem pCurrItem = new MHTextItem(); MHTextLine pCurrLine = new MHTextLine(); pCurrLine.Items.Append(pCurrItem); theText.Append(pCurrLine); Stack <MHRgba> m_ColourStack = new Stack <MHRgba>(); // Stack to handle nested colour codes. m_ColourStack.Push(textColour); pCurrItem.Colour = textColour; int i = 0; while (i < m_Content.Size) { char ch = m_Content.GetAt(i++); if (ch == '\t') // Tab - start a new item if we have any text in the existing one. { if (pCurrItem.Text.Size != 0) { pCurrItem = pCurrItem.NewItem(); pCurrLine.Items.Append(pCurrItem); } pCurrItem.TabCount++; } else if (ch == '\r') // CR - line break. // TODO: Two CRs next to one another are treated as </P> rather than <BR><BR> // This should also include the sequence CRLFCRLF. { pCurrLine = new MHTextLine(); theText.Append(pCurrLine); pCurrItem = pCurrItem.NewItem(); pCurrLine.Items.Append(pCurrItem); } else if (ch == 0x1b) // Escape - special codes. { if (i == m_Content.Size) { break; } char code = m_Content.GetAt(i); // The only codes we are interested in are the start and end of colour. // TODO: We may also need "bold" and some hypertext colours. if (code >= 0x40 && code <= 0x5e) // Start code // Start codes are followed by a parameter count and a number of parameter bytes. { if (++i == m_Content.Size) { break; } char paramCount = m_Content.GetAt(i); i++; if (code == 0x43 && paramCount == 4 && i + paramCount <= m_Content.Size) { // Start of colour. if (pCurrItem.Text.Size != 0) { pCurrItem = pCurrItem.NewItem(); pCurrLine.Items.Append(pCurrItem); } pCurrItem.Colour = new MHRgba(m_Content.GetAt(i), m_Content.GetAt(i + 1), m_Content.GetAt(i + 2), 255 - m_Content.GetAt(i + 3)); // Push this colour onto the colour stack. m_ColourStack.Push(pCurrItem.Colour); } else { Logging.Log(Logging.MHLogWarning, "Unknown text escape code " + code); } i += paramCount; // Skip the parameters } else if (code >= 0x60 && code <= 0x7e) // End code. { i++; if (code == 0x63) { if (m_ColourStack.Count > 1) { m_ColourStack.Pop(); // Start a new item since we're using a new colour. if (pCurrItem.Text.Size != 0) { pCurrItem = pCurrItem.NewItem(); pCurrLine.Items.Append(pCurrItem); } // Set the subsequent text in the colour we're using now. pCurrItem.Colour = m_ColourStack.Peek(); } } } } else if (ch <= 0x1f) { // Certain characters including LF and the marker codes between 0x1c and 0x1f are // explicitly intended to be ignored. Include all the other codes. } else // Add to the current text. { int nStart = i - 1; while (i < m_Content.Size && m_Content.GetAt(i) >= 0x20) { i++; } pCurrItem.Text.Append(new MHOctetString(m_Content, nStart, i - nStart)); } } // Set up the initial attributes. int style, size, lineSpace, letterSpace; InterpretAttributes(m_fontAttrs, out style, out size, out lineSpace, out letterSpace); // Create a font with this information. m_pDisplay.SetFont(size, (style & 2) != 0, (style & 1) != 0); // Calculate the layout of each section. for (i = 0; i < theText.Size; i++) { MHTextLine pLine = theText.GetAt(i); pLine.LineWidth = 0; for (int j = 0; j < pLine.Items.Size; j++) { MHTextItem pItem = pLine.Items.GetAt(j); // Set any tabs. for (int k = 0; k < pItem.TabCount; k++) { pLine.LineWidth += TABSTOP - pLine.LineWidth % TABSTOP; } if (pItem.UnicodeLength == 0) { // Convert UTF-8 to Unicode. int s = pItem.Text.Size; pItem.Unicode = pItem.Text.ToString(); pItem.UnicodeLength = pItem.Unicode.Length; } // Fit the text onto the line. int nFullText = pItem.UnicodeLength; // Get the box size and update pItem.m_nUnicode to the number that will fit. Rectangle rect = m_pDisplay.GetBounds(pItem.Unicode, ref nFullText, m_nBoxWidth - pLine.LineWidth); if (nFullText == pItem.UnicodeLength || !m_fTextWrap) { // All the characters fit or we're not wrapping. pItem.Width = rect.Width; pLine.LineWidth += rect.Width; } /* else if (m_fTextWrap) * { // No, we have to word-wrap. * int nTruncated = pItem.UnicodeLength; // Just in case. * // Now remove characters until we find a word-break character. * while (pItem.UnicodeLength > 0 && pItem.Unicode[pItem.UnicodeLength] != ' ') pItem.UnicodeLength--; * // If there are now word-break characters we truncate the text. * if (pItem.UnicodeLength == 0) pItem.UnicodeLength = nTruncated; * // Special case to avoid infinite loop if the box is very narrow. * if (pItem.UnicodeLength == 0) pItem.UnicodeLength = 1; * * // We need to move the text we've cut off this line into a new line. * int nNewWidth = nFullText - pItem.UnicodeLength; * int nNewStart = pItem.UnicodeLength; * // Remove any spaces at the start of the new section. * while (nNewWidth != 0 && pItem.Unicode[nNewStart] == ' ') { nNewStart++; nNewWidth--; } * if (nNewWidth != 0) { * // Create a new line from the extra text. * MHTextLine pNewLine = new MHTextLine(); * theText.InsertAt(pNewLine, i+1); * // The first item on the new line is the rest of the text. * MHTextItem pNewItem = pItem.NewItem(); * pNewLine.Items.Append(pNewItem); * pNewItem.Unicode = pItem.Unicode.Substring(nNewStart, nNewWidth); * pNewItem.UnicodeLength = nNewWidth; * } * // Remove any spaces at the end of the old section. If we don't do that and * // we are centering or right aligning the text we'll get it wrong. * while (pItem.UnicodeLength > 1 && pItem.Unicode[pItem.UnicodeLength - 1] == ' ') pItem.UnicodeLength--; * int uniLength = pItem.UnicodeLength; * rect = m_pDisplay.GetBounds(pItem.Unicode, ref uniLength, 0); * pItem.Width = rect.Width; * pLine.LineWidth += rect.Width; * } */} } // Now output the text. int yOffset = 0; // If there isn't space for all the lines we should drop extra lines. int nNumLines = theText.Size; do { if (m_VertJ == End) { yOffset = m_nBoxHeight - nNumLines * lineSpace; } else if (m_VertJ == Centre) { yOffset = (m_nBoxHeight - nNumLines * lineSpace) / 2; } if (yOffset < 0) { nNumLines--; } } while (yOffset < 0); for (i = 0; i < nNumLines; i++) { MHTextLine pLine = theText.GetAt(i); int xOffset = 0; if (m_HorizJ == End) { xOffset = m_nBoxWidth - pLine.LineWidth; } else if (m_HorizJ == Centre) { xOffset = (m_nBoxWidth - pLine.LineWidth) / 2; } //ASSERT(xOffset >= 0); for (int j = 0; j < pLine.Items.Size; j++) { MHTextItem pItem = pLine.Items.GetAt(j); // Tab across if necessary. for (int k = 0; k < pItem.TabCount; k++) { xOffset += TABSTOP - xOffset % TABSTOP; } if (pItem.Unicode.Length != 0) // We may have blank lines. { m_pDisplay.AddText(xOffset, yOffset, // Jas removed this cos it doesn't make sense yOffset + lineSpace pItem.Unicode.Substring(0, pItem.UnicodeLength), pItem.Colour); } xOffset += pItem.Width; } yOffset += lineSpace; if (yOffset + lineSpace > m_nBoxHeight) { break; } } }
public virtual void FormatDate(MHObjectRef success, MHSequence <MHParameter> args, MHEngine engine) { if (args.Size == 4) { // This is a bit like strftime but not quite. MHOctetString format = new MHOctetString(); GetString(args.GetAt(0), format, engine); int date = GetInt(args.GetAt(1), engine); // As produced in GCD int time = GetInt(args.GetAt(2), engine); string result = ""; DateTime dt = new DateTime(1858, 11, 17); dt = dt.AddDays(date).AddSeconds(time); for (int i = 0; i < format.Size; i++) { char ch = format.GetAt(i); string part; // Largest text is 4 chars for a year + null terminator if (ch == '%') { i++; if (i == format.Size) { break; } ch = format.GetAt(i); switch (ch) { case 'Y': part = dt.ToString("yyyy"); break; case 'y': part = dt.ToString("yy"); break; case 'X': part = dt.ToString("MM"); break; case 'x': part = dt.ToString("%M"); break; case 'D': part = dt.ToString("dd"); break; case 'd': part = dt.ToString("%d"); break; case 'H': part = dt.ToString("HH"); break; case 'h': part = dt.ToString("%H"); break; case 'I': part = dt.ToString("hh"); break; case 'i': part = dt.ToString("%h"); break; case 'M': part = dt.ToString("mm"); break; case 'm': part = dt.ToString("%m"); break; case 'S': part = dt.ToString("ss"); break; case 's': part = dt.ToString("%s"); break; // TODO: These really should be localised. case 'A': part = dt.ToString("tt"); break; case 'a': part = dt.ToString("tt").ToLower(); break; default: part = ""; break; } result += part; } else { result += ch; } } MHOctetString theResult = new MHOctetString(result); MHParameter pResString = args.GetAt(3); engine.FindObject(pResString.GetReference()).SetVariableValue(new MHUnion(theResult)); SetSuccessFlag(success, true, engine); } else { SetSuccessFlag(success, false, engine); } }