Community Documentation

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

Last updated August 27, 2009. Created by Dublin Drupaller on January 25, 2006.
Edited by bekasu, AjK. Log in to edit this page.

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.

Comments

i think there is a typo

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.

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..

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

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

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 6 ?

Do you have ported your code to Drupal 6 ?

Sidecontent helps with this too

Hi,

I know it's a bit different, but I thought I'd mention that there's a great module called sidecontent:

http://drupal.org/project/sidecontent

That lets you easily modify the left or right column from the page you're editing. It basically creates an extra Title and Body field on each page that is included in the left or right column, this gives you great control over a 2 column layout.

If you're looking for something simple that doesn't require coding, I'd recommend this.

www.1websitedesigner.com

D7 Custom Formatters version

Hi all,

I put together a quick D7 Custom Formatters version of this:

$formatter = new stdClass;
$formatter->disabled = FALSE; /* Edit this to true to make a default formatter disabled initially */
$formatter->api_version = 2;
$formatter->name = 'text_multicolumn';
$formatter->label = 'Text: Multi-column layout';
$formatter->description = '';
$formatter->mode = 'php';
$formatter->field_types = 'text_long';
$formatter->code = '$output = "";

$columns = 3;
$width = floor((100 / $columns) * 0.9);
$padding = floor((100 / $columns) * 0.1);

drupal_add_css(".text-columns .column { float: left; padding-right: {$padding}%; width: {$width}% }", \'inline\');
foreach ($variables[\'#items\'] as $item) {
  $length = ceil(drupal_strlen($item[\'safe_value\']) / $columns);
  $words = explode(" ", $item[\'safe_value\']);
  $index = 0;
  while ($words) {
    $column[$index] = !isset($column[$index]) ? array_shift($words) . \' \' : $column[$index] . array_shift($words) . \' \';
    $index = drupal_strlen($column[$index]) >= $length ? $index + 1 : $index;
  }
  $output .= \'<div class="text-columns clearfix"><div class="column"><p>\' . implode(\'</p></div><div class="column"><p>\', $column) . \'</p></div></div>\';
}

return $output;';

Simply change the $columns code variable as needed. When CF eventually gets support for Formatter Settings I'll add it as a setting.

Cheers,
Deciphered.

CSS3 Browser Compatibility

Why aren't we using CSS3 for this?

http://www.w3schools.com/css3/css3_multiple_columns.asp

http://www.koodoz.com.au/klog/web-design-html-xhtml-css-cascading-style-...
language-javascript-flash-php/newspaper-style-columns-with-and-without-css3/

It just seems a simpler solution. I'd like to hear your reasons for not using CSS....

paulhomebus - Web Developer/Designer - http://paulhomebus.developingcraftsmanship.com/
The Harvest - Ministry, Books - http://homepages.maxnet.co.nz/paulb/

_

Your welcome to use CSS3 for this, I chose not to because I like my design to actually work (http://en.wikipedia.org/wiki/Comparison_of_layout_engines_%28Cascading_S...)

Hi guys,

Just wanted to follow-up quickly on this post.

I thought another solution could potentially be using the Grid Field Formatter module, which

provides a simple way to overridde the display of multi-value fields to show as a grid/table with a certain number of columns.

With a multi-value long text/textarea field, each value could potentially be configured to display in a table cell, based on the number of columns. So the amount of text entered in each value would have to be manually balanced, but it would still provide a very quick and simple solution to get text formatted in a table layout.

For more information on the use cases, see project's page, the paragraph called Integration.

I have read through other solutions suggested in this post and a lot of them are very different, which is great, since it provides more potential solutions for different use cases.
I thought Grid Field Formatter module could also come as a very handy solution in some cases and assumed it would be worth adding this method to this list of comments.

Feel free to let us know if I missed anything, this doesn't work, or if you would have any more questions, comments or issues related with this requirement or module, I would be glad to provide more information or explain in more details.

Any more feedback, reporting, testing, comments or questions would be highly appreciated.
Thanks to all in advance!
Cheers!

Davyin Internet Solutions - Drupal in China
http://www.davyin.com

About this page

Drupal version
Drupal 4.5.x or older, Drupal 4.6.x, Drupal 4.7.x
Audience
Designers/themers

Theming Guide

Drupal’s online documentation is © 2000-2013 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License. Comments on documentation pages are used to improve content and then deleted.
nobody click here