public void OnTextSubtitle(ref TEXT_SUBTITLE sub)
        {
            try
            {
                if (sub.page == _activeSubPage)
                {
                    Log.Debug("Page: " + sub.page);
                    Log.Debug("Character table: " + sub.encoding);
                    Log.Debug("Timeout: " + sub.timeOut);
                    Log.Debug("Timestamp: " + sub.timeStamp);
                    Log.Debug("Language: " + sub.language);

                    String content = sub.text;
                    if (content == null)
                    {
                        Log.Error("OnTextSubtitle: sub.txt == null!");
                        return;
                    }
                    Log.Debug("Content: ");
                    if (content.Trim().Length > 0) // debug log subtitles
                    {
                        StringTokenizer st = new StringTokenizer(content, new char[] { '\n' });
                        while (st.HasMore)
                        {
                            Log.Debug(st.NextToken());
                        }
                    }
                    else
                    {
                        Log.Debug("Page: <BLANK PAGE>");
                    }
                }
            }
            catch (Exception e)
            {
                Log.Error("Problem with TEXT_SUBTITLE");
                Log.Error(e);
            }

            try
            {
                // if we dont need the subtitle
                if (!_renderSubtitles || _useBitmap || (_activeSubPage != sub.page))
                {
                    //
                    //chemelli: too much logging. You can check if logs have:
                    //          Log.Debug("Page: " + sub.page);  or Log.Debug("Page: <BLANK PAGE>");
                    //          and
                    //          Log.Debug("Text subtitle (page {0}) ACCEPTED: [...]
                    //          to know the evaluation of this if block
                    //
                    //Log.Debug("Text subtitle (page {0}) discarded: useBitmap is {1} and activeSubPage is {2}", sub.page, useBitmap,
                    //          activeSubPage);

                    return;
                }
                Log.Debug("Text subtitle (page {0}) ACCEPTED: useBitmap is {1} and activeSubPage is {2}", sub.page, _useBitmap,
                          _activeSubPage);

                Subtitle subtitle = new Subtitle();

                // TODO - RenderText should directly draw to a D3D texture
                subtitle.subBitmap   = RenderText(sub.lc);
                subtitle.timeOut     = sub.timeOut;
                subtitle.presentTime = sub.timeStamp / 90000.0f + _startPos;

                subtitle.height             = 576;
                subtitle.width              = 720;
                subtitle.screenHeight       = 576;
                subtitle.screenWidth        = 720;
                subtitle.firstScanLine      = 0;
                subtitle.horizontalPosition = 0;

                Texture texture = null;
                try
                {
                    // allocate new texture
                    texture = new Texture(GUIGraphicsContext.DX9Device, subtitle.subBitmap.Width,
                                          subtitle.subBitmap.Height, 1, Usage.Dynamic, Format.A8R8G8B8, Pool.Default);
                    int pitch;
                    using (GraphicsStream a = texture.LockRectangle(0, LockFlags.Discard, out pitch))
                    {
                        BitmapData bd = subtitle.subBitmap.LockBits(new Rectangle(0, 0, subtitle.subBitmap.Width,
                                                                                  subtitle.subBitmap.Height), ImageLockMode.ReadOnly,
                                                                    PixelFormat.Format32bppArgb);

                        // Quick copy of content
                        unsafe
                        {
                            byte *to   = (byte *)a.InternalDataPointer;
                            byte *from = (byte *)bd.Scan0.ToPointer();
                            for (int y = 0; y < bd.Height; ++y)
                            {
                                for (int x = 0; x < bd.Width * 4; ++x)
                                {
                                    to[pitch * y + x] = from[y * bd.Stride + x];
                                }
                            }
                        }

                        texture.UnlockRectangle(0);
                        subtitle.subBitmap.UnlockBits(bd);
                        subtitle.subBitmap.SafeDispose();
                        subtitle.subBitmap = null;
                        subtitle.texture   = texture;
                        a.Close();
                    }
                }
                catch (Exception e)
                {
                    Log.Debug("SubtitleRenderer: Failed to create subtitle surface!");
                    Log.Error(e);
                    return;
                }

                AddSubtitle(subtitle);
            }
            catch (Exception e)
            {
                Log.Error("Problem processing text subtitle");
                Log.Error(e);
            }
        }
    public void EndPage()
    {
      if (pageNumInProgress == -1)
      {
        return; // no page in progress
      }
      else if ((pageNumInProgress < 0 || pageNumInProgress >= 966))
      {
        Log.Debug("DANGER DANGER!, endpage with pageNumInProgress = %i", pageNumInProgress);
        return;
      }

      Log.Debug("Finished Page {0}", pageNumInProgress);
      //bool hasContent = false;

      for (int i = 0; i < 25; i++)
      {
        bool boxed = false;
        byte[] lineContent = GetLine(i);

        for (int j = 0; j < 40; j++)
        {
          // Remove spacing attributes ( see 12.2 of the draft)
          // FIXME: Some subtitles will have the attributed 'double height'
          // and therefore have an empty line between subs.

          // ís this content a space attribute?
          if (MSB3_NP(lineContent[j]) == 0)
          {
            if (LSB4(lineContent[j]) == SPACE_ATTRIB_BOX_START)
            {
              //LogDebug("BS - boxed is true");
              boxed = true;
              //hasContent = true;
            }
            else if (LSB4(lineContent[j]) == SPACE_ATTRIB_BOX_END)
            {
              //LogDebug("BE - boxed is false");
              boxed = false;
            }
            // remove spacing attribute
            lineContent[j] = TELETEXT_BLANK;
          }
          else if (!boxed)
          {
            // if we are not in boxed mode,
            // we dont want to keep the content
            lineContent[j] = TELETEXT_BLANK;
            assert(!boxed, "EndPage: Boxed not set as expected");
          }
        }
        SetLine(i, lineContent);
      }


      /*if(!hasContent) {
		        Log.Debug("(BLANK PAGE)");
	        }*/

      byte[] byte_text = new byte[TELETEXT_WIDTH * TELETEXT_LINES];
      Array.Copy(pageContent, byte_text, TELETEXT_LINES * TELETEXT_WIDTH);
      char[] text = TextConversion.Convert(language, byte_text);

      LineContent[] lc = new LineContent[TELETEXT_LINES];

      string realLang = "";

      lock (langInfo)
      {
        string langInfoInPgrs = null;
        if (langInfo.TryGetValue(pageNumInProgress, out langInfoInPgrs))
        {
          realLang = langInfoInPgrs;
        }
      }

      for (int line = 0; line < TELETEXT_LINES; line++)
      {
        StringBuilder lineBuilder = new StringBuilder();
        for (int c = 0; c < TELETEXT_WIDTH; c++)
        {
          lineBuilder.Append((char)text[line * TELETEXT_WIDTH + c]);
        }
        lc[line] = new LineContent();
        if (realLang != "")
        {
          lc[line].line = TextConversion.ConvertLineLangSpecific(realLang, lineBuilder.ToString());
        }
        else
        {
          lc[line].line = lineBuilder.ToString();
        }
        lc[line].doubleHeight = true;
      }

      StringBuilder textBuilder = new StringBuilder();
      for (int i = 0; i < text.Length; i++)
      {
        //sbuf.Append((char)text[i]);
        textBuilder.Append((char)text[i]);

        //sbuf.Append("" + ((int)pageContent[i]) + " ");
        if (((i + 1) % 40) == 0)
        {
          textBuilder.Append('\n');
        }
      }

      // prepare subtitle
      TEXT_SUBTITLE sub = new TEXT_SUBTITLE();
      sub.encoding = language;
      sub.page = pageNumInProgress;

      sub.language = realLang;

      sub.text = textBuilder.ToString();
      sub.lc = lc;
      sub.timeOut = ulong.MaxValue; // never timeout (will be replaced by other page)
      sub.timeStamp = presentTime;
      assert(sub.text != null, "Sub.text == null!");

      if (owner.SubPageInfoCallback != null)
      {
        TeletextPageEntry pageEntry = new TeletextPageEntry();
        pageEntry.language = String.Copy(sub.language);
        pageEntry.encoding = (TeletextCharTable)sub.encoding;
        pageEntry.page = sub.page;

        owner.SubPageInfoCallback(pageEntry);
      }

      owner.SubtitleRender.OnTextSubtitle(ref sub);
      pageNumInProgress = -1;
    }
    public void OnTextSubtitle(ref TEXT_SUBTITLE sub)
    {
      try
      {
        if (sub.page == _activeSubPage)
        {
          Log.Debug("Page: " + sub.page);
          Log.Debug("Character table: " + sub.encoding);
          Log.Debug("Timeout: " + sub.timeOut);
          Log.Debug("Timestamp: " + sub.timeStamp);
          Log.Debug("Language: " + sub.language);

          String content = sub.text;
          if (content == null)
          {
            Log.Error("OnTextSubtitle: sub.txt == null!");
            return;
          }
          Log.Debug("Content: ");
          if (content.Trim().Length > 0) // debug log subtitles
          {
            StringTokenizer st = new StringTokenizer(content, new char[] {'\n'});
            while (st.HasMore)
            {
              Log.Debug(st.NextToken());
            }
          }
          else
          {
            Log.Debug("Page: <BLANK PAGE>");
          }
        }
      }
      catch (Exception e)
      {
        Log.Error("Problem with TEXT_SUBTITLE");
        Log.Error(e);
      }

      try
      {
        // if we dont need the subtitle
        if (!_renderSubtitles || _useBitmap || (_activeSubPage != sub.page))
        {
          //
          //chemelli: too much logging. You can check if logs have:
          //          Log.Debug("Page: " + sub.page);  or Log.Debug("Page: <BLANK PAGE>");
          //          and
          //          Log.Debug("Text subtitle (page {0}) ACCEPTED: [...]
          //          to know the evaluation of this if block
          //
          //Log.Debug("Text subtitle (page {0}) discarded: useBitmap is {1} and activeSubPage is {2}", sub.page, useBitmap,
          //          activeSubPage);

          return;
        }
        Log.Debug("Text subtitle (page {0}) ACCEPTED: useBitmap is {1} and activeSubPage is {2}", sub.page, _useBitmap,
                  _activeSubPage);

        Subtitle subtitle = new Subtitle();

        // TODO - RenderText should directly draw to a D3D texture
        subtitle.subBitmap = RenderText(sub.lc);
        subtitle.timeOut = sub.timeOut;
        subtitle.presentTime = sub.timeStamp / 90000.0f + _startPos;

        subtitle.height = 576;
        subtitle.width = 720;
        subtitle.screenHeight = 576;
        subtitle.screenWidth = 720;
        subtitle.firstScanLine = 0;
        subtitle.horizontalPosition = 0;

        Texture texture = null;
        try
        {
          // allocate new texture
          texture = new Texture(GUIGraphicsContext.DX9Device, subtitle.subBitmap.Width,
                                subtitle.subBitmap.Height, 1, Usage.Dynamic, Format.A8R8G8B8, Pool.Default);
          int pitch;
          using (GraphicsStream a = texture.LockRectangle(0, LockFlags.Discard, out pitch))
          {
            BitmapData bd = subtitle.subBitmap.LockBits(new Rectangle(0, 0, subtitle.subBitmap.Width,
                                                                      subtitle.subBitmap.Height), ImageLockMode.ReadOnly,
                                                        PixelFormat.Format32bppArgb);

            // Quick copy of content
            unsafe
            {
              byte* to = (byte*)a.InternalDataPointer;
              byte* from = (byte*)bd.Scan0.ToPointer();
              for (int y = 0; y < bd.Height; ++y)
              {
                for (int x = 0; x < bd.Width * 4; ++x)
                {
                  to[pitch * y + x] = from[y * bd.Stride + x];
                }
              }
            }

            texture.UnlockRectangle(0);
            subtitle.subBitmap.UnlockBits(bd);
            subtitle.subBitmap.SafeDispose();
            subtitle.subBitmap = null;
            subtitle.texture = texture;
            a.Close();
          }
        }
        catch (Exception e)
        {
          Log.Debug("SubtitleRenderer: Failed to create subtitle surface!");
          Log.Error(e);
          return;
        }

        AddSubtitle(subtitle);
      }
      catch (Exception e)
      {
        Log.Error("Problem processing text subtitle");
        Log.Error(e);
      }
    }
        public void EndPage()
        {
            if (pageNumInProgress == -1)
            {
                return; // no page in progress
            }
            else if ((pageNumInProgress < 0 || pageNumInProgress >= 966))
            {
                Log.Debug("DANGER DANGER!, endpage with pageNumInProgress = %i", pageNumInProgress);
                return;
            }

            Log.Debug("Finished Page {0}", pageNumInProgress);
            //bool hasContent = false;

            for (int i = 0; i < 25; i++)
            {
                bool   boxed       = false;
                byte[] lineContent = GetLine(i);

                for (int j = 0; j < 40; j++)
                {
                    // Remove spacing attributes ( see 12.2 of the draft)
                    // FIXME: Some subtitles will have the attributed 'double height'
                    // and therefore have an empty line between subs.

                    // ís this content a space attribute?
                    if (MSB3_NP(lineContent[j]) == 0)
                    {
                        if (LSB4(lineContent[j]) == SPACE_ATTRIB_BOX_START)
                        {
                            //LogDebug("BS - boxed is true");
                            boxed = true;
                            //hasContent = true;
                        }
                        else if (LSB4(lineContent[j]) == SPACE_ATTRIB_BOX_END)
                        {
                            //LogDebug("BE - boxed is false");
                            boxed = false;
                        }
                        // remove spacing attribute
                        lineContent[j] = TELETEXT_BLANK;
                    }
                    else if (!boxed)
                    {
                        // if we are not in boxed mode,
                        // we dont want to keep the content
                        lineContent[j] = TELETEXT_BLANK;
                        assert(!boxed, "EndPage: Boxed not set as expected");
                    }
                }
                SetLine(i, lineContent);
            }


            /*if(!hasContent) {
             *                Log.Debug("(BLANK PAGE)");
             *        }*/

            byte[] byte_text = new byte[TELETEXT_WIDTH * TELETEXT_LINES];
            Array.Copy(pageContent, byte_text, TELETEXT_LINES * TELETEXT_WIDTH);
            char[] text = TextConversion.Convert(language, byte_text);

            LineContent[] lc = new LineContent[TELETEXT_LINES];

            string realLang = "";

            lock (langInfo)
            {
                string langInfoInPgrs = null;
                if (langInfo.TryGetValue(pageNumInProgress, out langInfoInPgrs))
                {
                    realLang = langInfoInPgrs;
                }
            }

            for (int line = 0; line < TELETEXT_LINES; line++)
            {
                StringBuilder lineBuilder = new StringBuilder();
                for (int c = 0; c < TELETEXT_WIDTH; c++)
                {
                    lineBuilder.Append((char)text[line * TELETEXT_WIDTH + c]);
                }
                lc[line] = new LineContent();
                if (realLang != "")
                {
                    lc[line].line = TextConversion.ConvertLineLangSpecific(realLang, lineBuilder.ToString());
                }
                else
                {
                    lc[line].line = lineBuilder.ToString();
                }
                lc[line].doubleHeight = true;
            }

            StringBuilder textBuilder = new StringBuilder();

            for (int i = 0; i < text.Length; i++)
            {
                //sbuf.Append((char)text[i]);
                textBuilder.Append((char)text[i]);

                //sbuf.Append("" + ((int)pageContent[i]) + " ");
                if (((i + 1) % 40) == 0)
                {
                    textBuilder.Append('\n');
                }
            }

            // prepare subtitle
            TEXT_SUBTITLE sub = new TEXT_SUBTITLE();

            sub.encoding = language;
            sub.page     = pageNumInProgress;

            sub.language = realLang;

            sub.text      = textBuilder.ToString();
            sub.lc        = lc;
            sub.timeOut   = ulong.MaxValue; // never timeout (will be replaced by other page)
            sub.timeStamp = presentTime;
            assert(sub.text != null, "Sub.text == null!");

            if (owner.SubPageInfoCallback != null)
            {
                TeletextPageEntry pageEntry = new TeletextPageEntry();
                pageEntry.language = String.Copy(sub.language);
                pageEntry.encoding = (TeletextCharTable)sub.encoding;
                pageEntry.page     = sub.page;

                owner.SubPageInfoCallback(pageEntry);
            }

            owner.SubtitleRender.OnTextSubtitle(ref sub);
            pageNumInProgress = -1;
        }