BidiModule

Alaa's picture
Submitted by Alaa on Thu, 10/06/2004 - 11:30.
::
<?php // -*-php-*-
/*
 * Original module by Alaa Abd El Fatah.
 * Mohammed Sameer: My Modifications Copyright (c) 2004 Mohammed Sameer, 
 *                  under the GNU GPL v2 or later.
 * Mohammed Sameer: 2004 10 30
 *                  * Ported to drupal 4.5 API.
 *
 * 04/04/2005 Ported to drupal 4.6 API by Amr Mostafa
 */

setlocale(LC_ALL"ar_EG.UTF-8");


/**
 * Implementation of hook_help()
 */
function bidi_help($section) {
  
$output "";

  switch (
$section) {
    case 
'admin/modules#description':
      
$output t("Automaticaly sets line direction");
      break;
  }

  return 
$output;
}

/**
 * Implementation of hook_filter_tips()
 */
function bidi_filter_tips($delta$format$long false) {
  if (
$long) {
    return 
t("You may write mixed Arabic and English freely, line direction will be computed automaticaly");
  }
  else {
    return 
t("You may write mixed Arabic and English freely, line direction will be computed automaticaly");
  }
}

/**
 * Implementation of hook_filter()
 */
function bidi_filter($op$delta 0$format = -1$text "") {
  switch (
$op) {
    case 
'list':
      return array(
=> t('BiDi'));
    case 
'description':
      return 
t("Bidi filter");
    case 
"process":
        return 
_bidi_filter_process($text);
    default:
        return 
$text;
  }
}

/**
 * Determine text language (arabic or english).
 * This function needs to be split into a single include file so it can be used on theme and other places.
 *
 * @param $text
 *   The text needs to be.
 * @return
 *   right2left or left2right depending on the detected language.
 */
function _get_dir($text) {
  
$text strtolower($text);
  
$len strlen($text);
  
  
$arabic 0;
  
$english 0;
  
  
// constants
  
$a ord('a');
  
$z ord('z');
  
  
// arabic UTF-8 letters have one of these values in the 1st byte
  
$ar1 0xd8;
  
$ar2 0xd9;
  
  
// this calculates the dominant language in the text, most bidi implementations just use the first letter (which may be more efficient)
  
for ($i 0$i $len; ++$i) {
    
$bin ord($text[$i]);
    
    if (
$bin == $ar1 || $bin == $ar2) {
      ++
$arabic;
      ++
$i;
    }
    else if (
$bin >= $a && $bin <= $z) {
      ++
$english;
    }
  }
  
  
// should this return a bool instead?
  
if ($english || $arabic) {
    if (
$english $arabic)    
      return 
' dir="LTR" ';
    else
      return 
' dir="RTL" ';
  }
  else {
    return 
"";
  }
}

/**
 * Sets HTML text line directions (rtl or ltr) as needed.
 *
 * @param $text
 *   The HTML text needs to be processed.
 *
 * @return
 *   The processed result, with line directions are set using css
 *   classes "righ2left" and "left2right".
 */
function _bidi_filter_process($text) {
  
$output "";
  
  
$len strlen($text);
  
$i 0;
  
  while (
$i $len) {
    
// find next tag
    
$e strpos(substr($text$i), "<");
    if (
$e === FALSE) {
      break;
    }
    
    
$output .= substr($text$i$e);
    
    
$i += $e;
    
    
// find end of next tag
    
$e strcspn(substr($text$i), "/> ");
    
$tag substr($text$i+1$e-1);
    
    
$output .= substr($text$i$e);
    
$i += $e;
    
    switch (
$tag) {
      
// single tag entity
      
case "img":
      case 
"br":
      case 
"hr":
        continue;
        break;
      
      
//nested cases
      
case "ul":
      case 
"ol":
      case 
"dl":
      case 
"div":
        
$e 0;
        
// stack counter
        
$j 1;
        
        
// if $j = 0 then stack is empty
        
while ($j 0) {
          
$e += strpos(substr($text$i+$e) , $tag);
          
          
// yeah I know I'm assuming certain sizes for tags, sue me
          // new nested block
          
if ($text[$i+$e-1] == '<') {
            ++
$j;
          }
          
// end of block
          
else if ($text[$i+$e-1] == '/') {
            --
$j;
          }
          
// skip current tag
          
$e += 2;
        }
        
        
$content substr($text$i+1$e); 
        
$output .= _get_dir(strip_tags($content));
        
$e += strlen($tag);
        
$output.= substr($text$i$e);
        
$i += $e;
        break;
      
      
// block elements these get out attention
      
case "p":
      case 
"blockquote":
      case 
"pre":
      case 
"h1":
      case 
"h2":
      case 
"h3":
      case 
"h4":
      case 
"h5":
      case 
"h6":
      
      
// nested lists are proken better think of a fix
      
case "li":
        
$close_tag "</".$tag.">";
        
// breaks on extra whitespaces, maybe I should use regexps instead
        
$e strpos(substr($text$i), $close_tag);
        
$content substr($text$i+1$e-1);
        
// calculate directions
        
$output.= _get_dir(strip_tags($content));
        
$e += strlen($close_tag);
        
$output.= substr($text$i$e);
        
$i += $e;
        break;
      
      
// non block tags we are not handeling
      
default:
        
$close_tag "</".$tag.">";
        
$e strpos(substr($text$i), $close_tag) + strlen($close_tag);
        
$output.= substr($text$i$e);
        
$i += $e;
      }
  }
  
  
// any missing content
  
$output .= substr($text$i);
  
  return 
$output;
}

?>