3D Studio Max – Modelling a Grundig 2440 antique radio

3D Studio Max - Modelling a Grundig 2440 antique radio

I am currently doing a website project for the “forum of community radios” of Mozambique. While struggling to think how to visualize this concept, I decided to model an actual radio to feature at the top  of the site. After thinking some more, I settled on an antique radio which I felt would be instantly recognizable as a radio, and some of these radios are probably still in use in rural communities.

So I set about modelling an actual Grundig 2440 radio (from this sample photo). Since I need the radio in extreme closeup, I needed to add lots of details. Texture maps were used for the fabric grille over the loudspeakers, and for the reflective dial plate. Read more


A working RTF to HTML converter in PHP

A working RTF to HTML converter in PHP

In a recent project, I desperately needed an RTF to HTML converter written in PHP. Googling around turned up some matches, but I could not get them to work properly. Also, one of them called passthru() to use a RTF2HTML executable, which is something I didn’t want. I was looking for a RTF2HTML converter written purely in PHP.

Since I couldn’t find anything ready-made, I sat down and coded one up myself. It’s short, and it works, implementing the subset of RTF tags that you’ll need in HTML and ignoring the rest. As it turns out, the RTF format isn’t that complicated when you really look at it, but it isn’t something you code a parser for in 15 minutes either.

How to use it

Include the file rtf.php somewhere in your project. Then do this:

$reader = new RtfReader();
$rtf = file_get_contents("test.rtf"); // or use a string
$reader->Parse($rtf);

If you’d like to see what the parser read, then call this:

$reader->root->dump();

To convert the parser’s parse tree to HTML, call this:

$formatter = new RtfHtml();
echo $formatter->Format($reader->root);

The code

  /**
   * RTF parser/formatter
   *
   * This code reads RTF files and formats the RTF data to HTML.
   *
   * PHP version 5
   *
   * @author     Alexander van Oostenrijk
   * @copyright  2014 Alexander van Oostenrijk
   * @license    GNU
   * @version    1
   * @link       http://www.websofia.com
   * 
   * Sample of use:
   * 
   * $reader = new RtfReader();
   * $rtf = file_get_contents("itc.rtf"); // or use a string
   * $reader->Parse($rtf);
   * //$reader->root->dump(); // to see what the reader read
   * $formatter = new RtfHtml();
   * echo $formatter->Format($reader->root);   
   */
 
  class RtfElement
  {
    protected function Indent($level)
    {
      for($i = 0; $i < $level * 2; $i++) echo "&nbsp;";
    }
  }
 
  class RtfGroup extends RtfElement
  {
    public $parent;
    public $children;
 
    public function __construct()
    {
      $this->parent = null;
      $this->children = array();
    }
 
    public function GetType()
    {
      // No children?
      if(sizeof($this->children) == 0) return null;
      // First child not a control word?
      $child = $this->children[0];
      if(get_class($child) != "RtfControlWord") return null;
      return $child->word;
    }    
 
    public function IsDestination()
    {
      // No children?
      if(sizeof($this->children) == 0) return null;
      // First child not a control symbol?
      $child = $this->children[0];
      if(get_class($child) != "RtfControlSymbol") return null;
      return $child->symbol == '*';
    }
 
    public function dump($level = 0)
    {
      echo "<div>";
      $this->Indent($level);
      echo "{";
      echo "</div>";
 
      foreach($this->children as $child)
      {
        if(get_class($child) == "RtfGroup")
        {
          if ($child->GetType() == "fonttbl") continue;
          if ($child->GetType() == "colortbl") continue;
          if ($child->GetType() == "stylesheet") continue;
          if ($child->GetType() == "info") continue;
          if ($child->IsDestination()) continue;
        }
        $child->dump($level + 2);
      }
 
      echo "<div>";
      $this->Indent($level);
      echo "}";
      echo "</div>";
    }
  }
 
  class RtfControlWord extends RtfElement
  {
    public $word;
    public $parameter;
 
    public function dump($level)
    {
      echo "<div style='color:green'>";
      $this->Indent($level);
      echo "WORD {$this->word} ({$this->parameter})";
      echo "</div>";
    }
  }
 
  class RtfControlSymbol extends RtfElement
  {
    public $symbol;
    public $parameter = 0;
 
    public function dump($level)
    {
      echo "<div style='color:blue'>";
      $this->Indent($level);
      echo "SYMBOL {$this->symbol} ({$this->parameter})";
      echo "</div>";
    }    
  }
 
  class RtfText extends RtfElement
  {
    public $text;
 
    public function dump($level)
    {
      echo "<div style='color:red'>";
      $this->Indent($level);
      echo "TEXT {$this->text}";
      echo "</div>";
    }    
  }
 
  class RtfReader
  {
    public $root = null;
 
    protected function GetChar()
    {
      $this->char = $this->rtf[$this->pos++];
    }
 
    protected function ParseStartGroup()
    {
      // Store state of document on stack.
      $group = new RtfGroup();
      if($this->group != null) $group->parent = $this->group;
      if($this->root == null)
      {
        $this->group = $group;
        $this->root = $group;
      }
      else
      {
        array_push($this->group->children, $group);
        $this->group = $group;
      }
    }
 
    protected function ParseEndGroup()
    {
      // Retrieve state of document from stack.
      $this->group = $this->group->parent;
    }
 
    protected function ParseControlWord()
    {
      $this->GetChar();
      $word = "";
      while(ctype_alpha($this->char))
      {
        $word .= $this->char;
        $this->GetChar();
      }
 
      // Read parameter (if any) consisting of digits.
      // Paramater may be negative.
      $parameter = null;
      $negative = false;
      if($this->char == '-') 
      {
        $this->GetChar();
        $negative = true;
      }
      while(ctype_digit($this->char))
      {
        if($parameter == null) $parameter = 0;
        $parameter = $parameter * 10 + $this->char;
        $this->GetChar();
      }
      if($parameter === null) $parameter = 1;
      if($negative) $parameter = -$parameter;
 
      // If this is \u, then the parameter will be followed by 
      // a character.
      if($word == "u") 
      {
      }
      // If the current character is a space, then
      // it is a delimiter. It is consumed.
      // If it's not a space, then it's part of the next
      // item in the text, so put the character back.
      else
      {
        if($this->char != ' ') $this->pos--; 
      }
 
      $rtfword = new RtfControlWord();
      $rtfword->word = $word;
      $rtfword->parameter = $parameter;
      array_push($this->group->children, $rtfword);
    }
 
    protected function ParseControlSymbol()
    {
      // Read symbol (one character only).
      $this->GetChar();
      $symbol = $this->char;
 
      // Symbols ordinarily have no parameter. However, 
      // if this is \', then it is followed by a 2-digit hex-code:
      $parameter = 0;
      if($symbol == '\'')
      {
        $this->GetChar(); 
        $parameter = $this->char;
        $this->GetChar(); 
        $parameter = hexdec($parameter . $this->char);
      }
 
      $rtfsymbol = new RtfControlSymbol();
      $rtfsymbol->symbol = $symbol;
      $rtfsymbol->parameter = $parameter;
      array_push($this->group->children, $rtfsymbol);
    }
 
    protected function ParseControl()
    {
      // Beginning of an RTF control word or control symbol.
      // Look ahead by one character to see if it starts with
      // a letter (control world) or another symbol (control symbol):
      $this->GetChar();
      $this->pos--;
      if(ctype_alpha($this->char)) 
        $this->ParseControlWord();
      else
        $this->ParseControlSymbol();
    }
 
    protected function SymbolToHTML($symbol)
    {
      return htmlentities(chr(hexdec($symbol)));
    }
 
    protected function UnicodeToHTML($code, $char)
    {
      return "&loz;";
    }
 
    protected function ParseText()
    {
      // Parse plain text up to backslash or brace,
      // unless escaped.
      $text = "";
 
      do
      {
        $terminate = false;
        $escape = false;
 
        // Is this an escape?
        if($this->char == '\\')
        {
          // Perform lookahead to see if this
          // is really an escape sequence.
          $this->GetChar();
          switch($this->char)
          {
            case '\\': $text .= '\\'; break;
            case '{': $text .= '{'; break;
            case '}': $text .= '}'; break;
            default:
              // Not an escape. Roll back.
              $this->pos = $this->pos - 2;
              $terminate = true;
              break;
          }
        }
        else if($this->char == '{' || $this->char == '}')
        {
          $this->pos--;
          $terminate = true;
        }
 
        if(!$terminate && !$escape)
        {
          $text .= $this->char;
          $this->GetChar();
        }
      }
      while(!$terminate && $this->pos < $this->len);
 
      $rtftext = new RtfText();
      $rtftext->text = $text;
      array_push($this->group->children, $rtftext);
    }
 
    public function Parse($rtf)
    {
      $this->rtf = $rtf;
      $this->pos = 0;
      $this->len = strlen($this->rtf);
      $this->group = null;
      $this->root = null;
 
      while($this->pos < $this->len)
      {
        // Read next character:
        $this->GetChar();
 
        // Ignore \r and \n
        if($this->char == "\n" || $this->char == "\r") continue;
 
        // What type of character is this?
        switch($this->char)
        {
          case '{':
            $this->ParseStartGroup();
            break;
          case '}':
            $this->ParseEndGroup();
            break;
          case '\\':
            $this->ParseControl();
            break;
          default:
            $this->ParseText();
            break;
        }
      }
    }
  }
 
  class RtfState
  {
    public function __construct()
    {
      $this->Reset();
    }
 
    public function Reset()
    {
      $this->bold = false;
      $this->italic = false;
      $this->underline = false;
      $this->strike = false;
      $this->hidden = false;
      $this->fontsize = 0;
    }
  }
 
  class RtfHtml
  {
    public function Format($root)
    {
      $this->output = "";
      // Create a stack of states:
      $this->states = array();
      $this->state = new RtfState();
      array_push($this->states, $this->state);
      $this->FormatGroup($root);
      return $this->output;
    }
 
    protected function FormatGroup($group)
    {
      // Can we ignore this group?
      if ($group->GetType() == "fonttbl") return;
      if ($group->GetType() == "colortbl") return;
      if ($group->GetType() == "stylesheet") return;
      if ($group->GetType() == "info") return;
      if ($group->IsDestination()) return;
 
      // Push a new state on the stack:
      $this->state = clone $this->state;
      array_push($this->states, $this->state);
 
      foreach($group->children as $child)
      {
        if(get_class($child) == "RtfGroup") $this->FormatGroup($child);
        if(get_class($child) == "RtfControlWord") $this->FormatControlWord($child);
        if(get_class($child) == "RtfControlSymbol") $this->FormatControlSymbol($child);
        if(get_class($child) == "RtfText") $this->FormatText($child);
      }
 
      // Pop state from stack.
      array_pop($this->states);
      $this->state = $this->states[sizeof($this->states)-1];
    }
 
    protected function FormatControlWord($word)
    {
      if($word->word == "plain") $this->state->Reset();
      if($word->word == "b") $this->state->bold = $word->parameter;
      if($word->word == "i") $this->state->italic = $word->parameter;
      if($word->word == "ul") $this->state->underline = $word->parameter;
      if($word->word == "strike") $this->state->strike = $word->parameter;
      if($word->word == "v") $this->state->hidden = $word->parameter;
      if($word->word == "fs") $this->state->fontsize = ceil(($word->parameter / 24) * 16);
 
      if($word->word == "par") $this->output .= "<p>";
 
      // Characters:
      if($word->word == "lquote") $this->output .= "&lsquo;";
      if($word->word == "rquote") $this->output .= "&rsquo;";
      if($word->word == "ldblquote") $this->output .= "&ldquo;";
      if($word->word == "rdblquote") $this->output .= "&rdquo;";
      if($word->word == "emdash") $this->output .= "&mdash;";
      if($word->word == "endash") $this->output .= "&ndash;";
      if($word->word == "bullet") $this->output .= "&bull;";
      if($word->word == "u") $this->output .= "&loz;";
    }
 
    protected function BeginState()
    {
      $span = "";
      if($this->state->bold) $span .= "font-weight:bold;";
      if($this->state->italic) $span .= "font-style:italic;";
      if($this->state->underline) $span .= "text-decoration:underline;";
      if($this->state->strike) $span .= "text-decoration:strikethrough;";
      if($this->state->hidden) $span .= "display:none;";
      if($this->state->fontsize != 0) $span .= "font-size: {$this->state->fontsize}px;";
      $this->output .= "<span style='{$span}'>";
    }
 
    protected function EndState()
    {
      $this->output .= "</span>";
    }
 
    protected function FormatControlSymbol($symbol)
    {
      if($symbol->symbol == '\'')
      {
        $this->BeginState();
        $this->output .= htmlentities(chr($symbol->parameter));
        $this->EndState();
      }
    }
 
    protected function FormatText($text)
    {
      $this->BeginState();
      $this->output .= $text->text;
      $this->EndState();
    }
  }

Developing a generic count() method for Idiorm/Paris

The minimalistic object relational mapping libraries for PHP, Idiorm and Paris, are wonderful stuff. They have a very small footprint and make your code a joy to read. They allow you to build queries using method chaining, avoiding tedious string concatenation. They’re safer than string concatenation too, since they use parameterized PDO queries under the hood.

And yet, minimalism comes at its own price. Not everything is possible with Idiorm/Paris. Still, whatever seems impossible could very well result from my not understanding the libraries’ potential, or SQL for that matter. Read more


Developing a REST-powered AJAX table control with the Slim Microframework and AngularJS (3)

Developing an REST-powered AJAX table control with the Slim Microframework and AngularJS

Welcome to the third part of a hands-on tutorial on writing reusable user interface components with AngularJS. In this tutorial, we’ll write a table control that asynchronously loads data from a server, display it, and allows the user to sort the data by clicking table headers. The sorting is done client-side. We’ll show how to write a REST-server in PHP, how to define custom HTML elements in AngularJS, and how to setup a controller for the elements. To top it off, we’ll write CSS for our table in such a way that it can be dropped into a user interface in a snap, and scales fluidly with the space allotted to it.

The third part of this tutorial adds CSS to style the output we already have, and adds automatic (client-side) sorting to our table control.

Don’t forget to read the first part and the second part of this tutorial.
Read more


Developing a REST-powered AJAX table control with the Slim Microframework and AngularJS (2)

Developing an REST-powered AJAX table control with the Slim Microframework and AngularJS

Welcome to the second part of a hands-on tutorial on writing reusable user interface components with AngularJS. In this tutorial, we’ll write a table control that asynchronously loads data from a server, display it, and allows the user to sort the data by clicking table headers. The sorting is done client-side. We’ll show how to write a REST-server in PHP, how to define custom HTML elements in AngularJS, and how to setup a controller for the elements. To top it off, we’ll write CSS for our table in such a way that it can be dropped into a user interface in a snap, and scales fluidly with the space allotted to it.

The second part of this tutorial focuses on AngularJS. With a RESTful web service in place, we will now set about writing a new HTML element for our dynamic table. (Read the first part here).
Read more


Developing a REST-powered AJAX table control with the Slim Microframework and AngularJS (1)

Developing an REST-powered AJAX table control with the Slim Microframework and AngularJS

Welcome to a hands-on tutorial on writing reusable user interface components with AngularJS. In this tutorial, we’ll write a table control that asynchronously loads data from a server, display it, and allows the user to sort the data by clicking table headers. The sorting is done client-side. We’ll show how to write a REST-server in PHP, how to define custom HTML elements in AngularJS, and how to setup a controller for the elements. To top it off, we’ll write CSS for our table in such a way that it can be dropped into a user interface in a snap, and scales fluidly with the space allotted to it.

The first part of this tutorial introduces AngularJS, and takes you through the use of the Slim PHP Microframework and the Idiorm and Paris ORM libraries in order to write a RESTful web service, which we’ll need for our table control to talk to.
Read more


Online Tools

I can see blonde, redhead...

Websofia presents a couple of online tools that can be handy when you’re examining hacking scripts on your web server. Often, you’ll find that malicious code is encoded using either base64_encode or str_rot13, and possibly gzip-compressed. You could scan all the files on your web server looking  for base64_decode, str_rot13 and gzinflate, but you’ll get a lot of false positives in the bargain. When you need to know what the encoded code actually looks like, run it through one of these tools to find out. Saves you having to write the scripts yourself.


A practical guide to URL rewriting for IIS (and Apache)

Microsoft IIS URL RewritingI use my own PHP-based CMS for almost all my web development. This is great, because since I know the code inside and out, I can make whatever the client wishes happen. It has a lot of nice, reusable features (plugins) that make development of a generic website pretty short. Still, all was not well because one of the things it didn’t support was URL rewriting. In this blog post, I’ll set out to describe how this is done, what pitfalls there are and how they can be avoided.
Read more


Automating website & MySQL backups

MySQLI have a web server with a number of clients’ websites on it. It’s necessary to backup these websites every day, since clients use a content management system to make changes regularly. These changes can be updates to a website’s MySQL database, or they can be changes to the files stored within these websites. What I’d like is to backup the MySQL database and the filesystem for each website, every day, at a specific time. The backups must rotate: when there are, say, five backups, I want the oldest one to be removed as the newest one is written. Also, I’d like the backup solution to send me an email every day after it’s completed the backups with a summary of the procedure. Read more