Split a text field into multiple columns, like a newspaper/magazine article.

description

This recyclable snippet allows you to automatically convert a textfield in your custom-layout.tpl.php files to newspaper style columns.

This has been tested and works with Drupal 4.5.x, Drupal 4.6.x and Drupal 4.7.

usage

The code (called the drupalicious_convert2columns function) sits in your template.php file so you can call it from various tpl.php layout files, such as node.tpl.php, node-image.tpl.php, flexinode.tpl.php etc. using just one simple line of code.

The drupalicious_convert2columns function uses a simple table to convert your text field to columns. (A DIV version of the same will follow.)

Step 1 of 2

In a text editor like notepad.exe, create a file called template.php using the following snippet. If you already have a template.php file, simply add it to your existing one.

<?php
function drupalicious_convert2columns ($colcontent, $columns, $column_spacing) {
 
$coloutput "<table border=\"0\" cellpadding=\"$column_spacing\"><tr>";
 
$bodytext = array("$colcontent");
 
$text = implode(",", $bodytext); //prepare bodytext
 
$length = strlen($text); //determine the length of the text
 
$length = ceil($length/$columns); //divide length by number of columns
 
$words = explode(" ",$text); // prepare text for word count and split the body into columns
 
$c = count($words);
 
$l = 0;
  for(
$i=1;$i<=$columns;$i++) {
   
$new_string = "";
   
$coloutput .= "<td style=\"text-align:justify\" valign=\"top\">";
  for(
$g=$l;$g<=$c;$g++) {
    if(
strlen($new_string) <= $length || $i == $columns)
   
$new_string.=$words[$g]." ";
    else {
     
$l = $g;
    break;
      }
     }
   
$coloutput .= $new_string;
   
$coloutput .= "</td>";
  }
 
$coloutput .= "</tr></table>";
  return
$coloutput;
}
?>

Upload your new or edited template.php file to your active theme folder.

Step 2 of 2

Insert the following snippet in your custom-layout.tpl.php file.

<?php
$columns
= 3;
$column_spacing = 8;
print
drupalicious_convert2columns($textfieldname, $columns, $column_spacing);
?>

$textfieldname = the name of the field you want trimmed.
$columns = the number of columns, increase or decrease to suit.
$column_spacing = the "gutters" or column spacing in pixels, increase or decrease to suit.

notes

  • Because the drupalicious_convert2columns function is sitting in your template.php file, this snippet is recyclable so you can use the short step 2 of 2 snippet above as many times as you like.

i think there is a typo

quixoticlife - September 1, 2006 - 17:52

In this line:

$coloutput .= "<td style=\"text-align:justify\" valign=\"top\">"';

I think there is an extra apostrophe near the end -- it should be:

$coloutput .= "<td style=\"text-align:justify\" valign=\"top\">";

With the original version, I got a parse error.

Nice, but take a look at this.

Lancelight - April 13, 2007 - 22:51

This isnt bad, but it didnt fit what I needed. I needed a non-tables newspaper column layout that had columns that were uneven heights and also accounted for br's and p's. This might help others that ran into the same problem I had. The following uses CSS for the columns to its very easy to style and place the columns anywhere on the page. You can add/remove columns simply by cut/pasting the column code to have as many columns as you need and modifying the calculations a little bit. It's not the prettiest looking thing on the planet but it works. If someone can come up with a better version of what I'm showing here, then by all means post away!

<?php
//Released under the GPL 2.0 by Ryan Coulombe
//     <a href="http://crystaldawn.net
//Usage:
echo" title="http://crystaldawn.net
//Usage:
echo" rel="nofollow">http://crystaldawn.net
//Usage:
echo</a> newspapercolumns("
Your string variable should go here");


function strsplit($str, $l=1)
{
   //If you run PHP5, you could replace this crap with something simple like str_split($str, 1);  PHP3/4 dont
   //have this type of function, although I wish it did.
   do {
      $ret[]=substr($str,0,$l);
      $str=substr($str,$l);
      }
   while($str != "");
   return $ret;
}

function newspapercolumns($str)
{

//This function *could* take an argument called columns IE newspapercolumns($str, $num_columns)
//but I chose not to do this because my particular setup requires uneven column heights/widths.  I
//can only assume that others would run into the same problem so I've left it the way I use it myself
//so that you can modify each column individually.

//This function also accomodates <br> and <p>.  Since these characters take up more space than a normal
//character (each takes up the width of a column), they need to be accounted for.  I could not find any
//function on the web that does newspaper columns and also accounts for this weirdness so I had to
//whip up my own.  It's not the best code on the planet but it definatly gets the job done.  Its just
//not very human readable code..

//Get the size of our string that came in
$total_size = strlen($str);

//Setup a our columns
//My particular setup requires that the 1st/2nd columns be the same height while the 3rd is much smaller.
//If you want all 3 to be of equal size, simply change total_size * 3/7 to 2/3 and change the 3rd column to
//be the $col3_max_chars = $col2_max_chars;
$col1_max_chars = ceil($total_size * 3/7);
$col2_max_chars = $col1_max_chars;
$col3_max_chars = $total_size - ($col1_max_chars *2);

//Split the string into an array thats usable.  There is probably a better way to do this, but I didnt
//feel like coming up with one myself so I swiped it from someone else
$str = strsplit($str);

//Initialize the position variable
$position = 0;

//The br/p weight should be equal to the total width of a column (which is 47 chars for me)
//This will vary for you depending on the width you set for each column as well as the font size you are using.
//To find out how many chars this is, simply copy/paste a complete lorum impsum row from your text and paste it into an
//editor that can tell you how many chars are in that line (IE: OpenOffice Writer or MS Word).  It should count spaces
//as well.

$br_weight = 47;

//Initialize the column variable with 1 not 0
$current_column = 1;

//Loop through each character in our string array and assign it to a column.
foreach ($str AS $character)
{
   //Increment the position variable
    $position++;
   
    /*********************  Column 1  *********************/
   if ($current_column == 1)
   {
      //Ignore \n characters since they are not displayed in browser space anyways.
      if ($character == "
n") { continue; }
  
      //If this is the first trip through for column 1, we setup the DIV
      if ($counter == 0)
      {
      $col1 = "
<div style='float:left; width:230px;  text-align:justify; margin-left:10px;'>n";
      $counter++;
      }
     
      //Add <br> and <p> allowances since they take up more space than a single char
      if
      (
         ($character == '<') && (stristr($str[$position], 'p')) && ($str[$position+1] == '>')
      )
      {
       //if we've found a <p> reduce the column max by.the weight we gave <br>/<p>  This helps keep our text in the div on our page
       $col1_max_chars -= $br_weight;
      }
      if
      (
         (($character == '<') && ($str[$position] == 'b') && ($str[$position+1] == 'r') && ($str[$position+2] == '>')) &&
         (
            (($str[$position+3] == '<') && (stristr($str[$position], 'b')) && ($str[$position+5] == 'r') && ($str[$position+6] == '>')) ||
            (($str[$position+3] == "
n") && ($str[$position+4] == '<') && (stristr($str[$position+5], 'b')) && (stristr($str[$position+6], 'r')) && ($str[$position+7] == '>'))
         )
      )
      {
      //if we've found TWO <br>'s reduce the column max by.the weight we gave <br>/<p>  This helps keep our text in the div on our page
       $col1_max_chars -= $br_weight;
      }
      //END <br><p> allowance code
     
   //Fill the first column with our character
   $col1 .= $character;
  
   //Increment the $char_counter variable so we can move to the next character in our string array on the next loop.
   $char_counter++;
  
      //If we are at the maximium height for the first column, we end it here and increment the $current_column variable to start
      //the loop working on the 2nd column.
      if ((strlen($col1) >= $col1_max_chars) && ($col1[strlen($col1)-1] == ' '))
      {
      $current_column++;
      $col1 .= '</div>';
      }
     
      //Sanitize the variables so they can be re-used in the other columns
      $num_brs = 0;
      $num_ps = 0;
      $num_crs = 0;
      $add_extra_space_for_br = 0;
     
      //Stop the code here and start the loop again.  This prevents the code from trying to build the other columns before it's time.
      continue;
   }
  
    /*********************  Column 2  *********************/
   if ($current_column == 2)
   {
   if ($character == "
n") { continue; }
      if ($counter == 1)
      {
      $col2 = "
<div style='float:left; width:230px;  text-align:justify; margin-left:10px;'>n";
      $counter++;
      }
     
      //Add <br> and <p> allowances since they take up more space than a single char
      if
      (
         ($character == '<') && (stristr($str[$position], 'p')) && ($str[$position+1] == '>')
      )
      {
       $col2_max_chars -= $br_weight;
      }
      if
      (
         (($character == '<') && ($str[$position] == 'b') && ($str[$position+1] == 'r') && ($str[$position+2] == '>')) &&
         (
            (($str[$position+3] == '<') && (stristr($str[$position], 'b')) && ($str[$position+5] == 'r') && ($str[$position+6] == '>')) ||
            (($str[$position+3] == "
n") && ($str[$position+4] == '<') && (stristr($str[$position+5], 'b')) && (stristr($str[$position+6], 'r')) && ($str[$position+7] == '>'))
         )
      )
      {
       $col2_max_chars -= $br_weight;
      }
     
   $col2 .= $character;
   $char_counter++;
      if ((strlen($col2) >= $col2_max_chars) && ($col2[strlen($col2)-1] == ' '))
      {
      $current_column++;
      $col2 .= '</div>';
      }
   $num_brs = 0;
   $num_ps = 0;
   $num_crs = 0;
   $add_extra_space_for_br = 0;
   continue;
   }

     /*********************  Column 3  *********************/
   if ($current_column == 3)
   {
   if ($character == "
n") { continue; }
      if ($counter == 2)
      {
      $col3 = "
<div style='float:left; width:230px;  text-align:justify; margin-left:10px;'>n";
      $counter++;
      }
     
      //Add <br> and <p> allowances since they take up more space than a single char
      if
      (
         ($character == '<') && (stristr($str[$position], 'p')) && ($str[$position+1] == '>')
      )
      {
       $col3_max_chars -= $br_weight;
      }
      if
      (
         (($character == '<') && ($str[$position] == 'b') && ($str[$position+1] == 'r') && ($str[$position+2] == '>')) &&
         (
            (($str[$position+3] == '<') && (stristr($str[$position], 'b')) && ($str[$position+5] == 'r') && ($str[$position+6] == '>')) ||
            (($str[$position+3] == "
n") && ($str[$position+4] == '<') && (stristr($str[$position+5], 'b')) && (stristr($str[$position+6], 'r')) && ($str[$position+7] == '>'))
         )
      )
      {
       $col3_max_chars -= $br_weight;
      }
   $col3 .= $character;
   $char_counter++;
      if ((strlen($col3) >= $col3_max_chars) && ($col3[strlen($col3)-1] == '.'))
      {
      $current_column++;
      $col3 .= '</div>';
      }
   $num_brs = 0;
   $num_ps = 0;
   $num_crs = 0;
   $add_extra_space_for_br = 0;
      continue;
   }
}

   //Fill up the $text variable, clean it up, and return it.
$text = $col1;
$text .= $col2;

//In some cases the 3rd column will come back without the div attached to it.  We check for this sillyness and fix it.
if (!strstr($col3, '</div>')) { $col3 .= '</div>'; }
$text .= $col3;

//Send off our nice shiny new newspaper lookin text block
return $text;
}
?>

Works but..

kenitech - September 6, 2007 - 19:16

Hey,

This seems to work pretty good but I have a few questions. Why would you print 'n' at the beginning of a line where you don't want '\n' to be displayed? This might be moot if the forum is just stripping out the backslash.

//Ignore \n characters since they are not displayed in browser space anyways.
      if ($character == "n") { continue; }
 
      //If this is the first trip through for column 1, we setup the DIV
      if ($counter == 0)
      {
      $col1 = "<div style='float:left; width:230px;  text-align:justify; margin-left:10px;'>n";
      $counter++;
      }

Should it read more like this?

//Ignore \n characters since they are not displayed in browser space anyways.
      if ($character == "\n") { continue; }
 
      //If this is the first trip through for column 1, we setup the DIV
      if ($counter == 0)
      {
      $col1 = "<div style='float:left; width:230px;  text-align:justify; margin-left:10px;'>";
      $counter++;
      }

Also, I think this needs to be tweaked so that if the first character in the column is a paragraph or line break it is stripped. This way the first line does not contain a space which is what is happening in my versions of Firefox and IE. Only problem is you'd have to deal with the closing tag. A good solution might be to modify the CSS class for p or br if it is the first paragraph.

I'm not using this in drupal just another php project.

Adjust P style

kenitech - September 6, 2007 - 19:25

This seems to fix the p issue by adding to the top of the page.

<style>
p { margin-top: 10px; margin-bottom: 0px; }
</style>

You probably don't want to modify the rest of the content on the page so maybe contain the 3 column code inside a div that you could reference in the css:

<style>
#3columns p { margin-top: 10px; margin-bottom: 0px; }
</style>


<div id="3columns">
The 3 column code here
</div>

You could probably try different variations of padding and margin spacing depending on your page layout. I'm not totally familiar with drupal but some wysywg editors might add

marks so you'd probably want to control this better.

DIV version

styleplus - May 7, 2007 - 06:05

Hey, I modifed the original code to create a DIV version. I hope someone finds this useful...

<?php


//text columns
function drupalicious_convert2columns ($colcontent, $columns, $column_spacing) {
$coloutput = "";
$bodytext = array("$colcontent");
$text = implode(",", $bodytext); //prepare bodytext
$length = strlen($text); //determine the length of the text
$length = ceil($length/$columns); //divide length by number of columns
$words = explode(" ",$text); // prepare text for word count and split the body into columns
$c = count($words);
$l = 0;
for(
$i=1;$i<=$columns;$i++) {
  
$new_string = "";
  
$coloutput .= "<div id=\"txt-column-$i\" style=\"text-align:justify\" valign=\"top\">";
for(
$g=$l;$g<=$c;$g++) {
   if(
strlen($new_string) <= $length || $i == $columns)

  
$new_string.=$words[$g]." ";

   else {
    
$l = $g;
   break;
     }
    }
  
$coloutput .= $new_string;
  
$coloutput .= "</div>";
}
return
$coloutput;
}
?>

 
 

Drupal is a registered trademark of Dries Buytaert.