Wordwrap / Cut Text in HTML string
As I wrote in a comment, you first need to find the textual offset where to do the cut.
First of all I setup a DOMDocument
containing the HTML fragment and then selecting the body which represents it in the DOM:
$htmlFragment = <<<HTML
<p>
<span class="Underline"><span class="Bold">Test to be cut</span></span>
</p><p>Some text </p>
HTML;
$dom = new DOMDocument();
$dom->loadHTML($htmlFragment);
$parent = $dom->getElementsByTagName('body')->item(0);
if (!$parent)
{
throw new Exception('Parent element not found.');
}
Then I use my TextRange
class to find the place where the cut needs to be done and I use the TextRange
to actually do the cut and locate the DOMNode
that should become the last node of the fragment:
$range = new TextRange($parent);
// find position where to cut the HTML textual represenation
// by looking for a word or the at least matching whitespace
// with a regular expression.
$width = 17;
$pattern = sprintf('~^.{0,%d}(?<=\S)(?=\s)|^.{0,%1$d}(?=\s)~su', $width);
$r = preg_match($pattern, $range, $matches);
if (FALSE === $r)
{
throw new Exception('Wordcut regex failed.');
}
if (!$r)
{
throw new Exception(sprintf('Text "%s" is not cut-able (should not happen).', $range));
}
This regular expression finds the offset where to cut things in the textual representation made available by $range
. The regex pattern is inspired by another answer which discusses it more detailed and has been slightly modified to fit this answers needs.
// chop-off the textnodes to make a cut in DOM possible
$range->split($matches[0]);
$nodes = $range->getNodes();
$cutPosition = end($nodes);
As it can be possible that there is nothing to cut (e.g. the body
will become empty), I need to deal with that special case. Otherwise - as noted in the comment - all following nodes need to be removed:
// obtain list of elements to remove with xpath
if (FALSE === $cutPosition)
{
// if there is no node, delete all parent children
$cutPosition = $parent;
$xpath = 'child::node()';
}
else
{
$xpath = 'following::node()';
}
The rest is straight forward: Query the xpath, remove the nodes and output the result:
// execute xpath
$xp = new DOMXPath($dom);
$remove = $xp->query($xpath, $cutPosition);
if (!$remove)
{
throw new Exception('XPath query failed to obtain elements to remove');
}
// remove nodes
foreach($remove as $node)
{
$node->parentNode->removeChild($node);
}
// inner HTML (PHP >= 5.3.6)
foreach($parent->childNodes as $node)
{
echo $dom->saveHTML($node);
}
The full code example is available on viper codepad incl. the TextRange
class. The codepad has a bug so it's result is not properly (Related: XPath query result order). The actual output is the following:
<p>
<span class="Underline"><span class="Bold">Test to</span></span></p>
So take care you have a current libxml version (normally the case) and the output foreach
at the end makes use of a PHP function saveHTML
which is available with that parameter since PHP 5.3.6. If you don't have that PHP version, take some alternative like outlined in How to get the xml content of a node as a string? or a similar question.
When you closely look in my example code you might notice that the cut length is quite large ($width = 17;
). That is because there are many whitespace characters in front of the text. This could be tweaked by making the regular expression drop any number of whitespace in fron t of it and/or by trimming the TextRange
first. The second option does need more functionality, I wrote something quick that can be used after creating the initial range:
...
$range = new TextRange($parent);
$trimmer = new TextRangeTrimmer($range);
$trimmer->trim();
...
That would remove the needless whitespace on left and right inside your HTML fragment. The TextRangeTrimmer
code is the following:
class TextRangeTrimmer
{
/**
* @var TextRange
*/
private $range;
/**
* @var array
*/
private $charlist;
public function __construct(TextRange $range, Array $charlist = NULL)
{
$this->range = $range;
$this->setCharlist($charlist);
}
/**
* @param array $charlist list of UTF-8 encoded characters
* @throws InvalidArgumentException
*/
public function setCharlist(Array $charlist = NULL)
{
if (NULL === $charlist)
$charlist = str_split(" \t\n\r\0\x0B")
;
$list = array();
foreach($charlist as $char)
{
if (!is_string($char))
{
throw new InvalidArgumentException('Not an Array of strings.');
}
if (strlen($char))
{
$list[] = $char;
}
}
$this->charlist = array_flip($list);
}
/**
* @return array characters
*/
public function getCharlist()
{
return array_keys($this->charlist);
}
public function trim()
{
if (!$this->charlist) return;
$this->ltrim();
$this->rtrim();
}
/**
* number of consecutive charcters of $charlist from $start to $direction
*
* @param array $charlist
* @param int $start offset
* @param int $direction 1: forward, -1: backward
* @throws InvalidArgumentException
*/
private function lengthOfCharacterSequence(Array $charlist, $start, $direction = 1)
{
$start = (int) $start;
$direction = max(-1, min(1, $direction));
if (!$direction) throw new InvalidArgumentException('Direction must be 1 or -1.');
$count = 0;
for(;$char = $this->range->getCharacter($start), $char !== ''; $start += $direction, $count++)
if (!isset($charlist[$char])) break;
return $count;
}
public function ltrim()
{
$count = $this->lengthOfCharacterSequence($this->charlist, 0);
if ($count)
{
$remainder = $this->range->split($count);
foreach($this->range->getNodes() as $textNode)
{
$textNode->parentNode->removeChild($textNode);
}
$this->range->setNodes($remainder->getNodes());
}
}
public function rtrim()
{
$count = $this->lengthOfCharacterSequence($this->charlist, -1, -1);
if ($count)
{
$chop = $this->range->split(-$count);
foreach($chop->getNodes() as $textNode)
{
$textNode->parentNode->removeChild($textNode);
}
}
}
}
Hope this is helpful.
PHP, WordWrap how to cut only text and don't cut html code
You’ll want to use the strip_tags()
function to remove HTML from your string.
wordwrap(strip_tags($getPagina[$i]["message"]), 60, "<br/>", true)
How can I force a long string without any blank to be wrapped?
for block elements:
<textarea style="width:100px; word-wrap:break-word;"> ACTGATCGAGCTGAAGCGCAGTGCGATGCTTCGATGATGCTGACGATGCTACGATGCGAGCATCTACGATCAGTC</textarea>
How to word wrap text in HTML?
Try this:
div {
width: 200px;
word-wrap: break-word;
}
wordwrap a very long string
This question has been asked here before:
- Who has solved the long-word-breaks-my-div problem? (hint: not stackoverflow)
- word wrap in css / js
- CSS: how can I force a long string (without any blank) to be wrapped in XUL and/or HTML
- CSS overflow with long URL
Long story short:
As far as CSS solutions go you have: overflow: scroll;
to force the element to show scrollbars and overflow:hidden;
to just cut off any extra text. There is text-overflow:ellipsis;
and word-wrap: break-word;
but they are IE only (break-word
is in the CSS3 draft, however, so it will be the solution to this 5 years from now).
Bottom line is that if it is very important for you to stop this from happening with wrapping the text over to the next line the only reasonable solution is to inject
(soft hyphen), <wbr>
(word break tag), or
(zero-width space, same effect as
minus hyphen) in your string server side. If you don't mind Javascript, however, there is the hyphenator which seems to be pretty solid.
how to cut off the text when it exceed the width of a div
Take a look at this jsfiddle: http://jsfiddle.net/yM2vL/1/
basically, you need to set
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
in CSS (or hard-code the style, but CSS is cleaner). If you want to completely cut the text off, use clip
instead of ellipsis
.
See: https://developer.mozilla.org/en-US/docs/Web/CSS/text-overflow for other options
How do I wrap text in a pre tag?
The answer, from this page in CSS:
pre {
white-space: pre-wrap; /* Since CSS 2.1 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}
css word wrap that wraps whole word without breaking them?
You don't need to do anything special. Normally the text will break between words.
If that doesn't happen, then
- either the container is too wide
- the container is explicitly set to not break on spaces at all. For instance if it is a
<pre>
element, or has the csswhite-space:pre;
orwhite-space:nowrap;
. - the text contains non-breaking spaces (
Solution: use
white-space: pre-wrap;
It will make the element behave like a pre
element, except it also wraps when the line is too long. See also:
http://css-tricks.com/almanac/properties/w/whitespace/
cut text after html a ending tag
Well, I think I did it. If anyone has any suggestion, feel free to modify this answer or comment.
function cut_text($string, $length = 350, $append = "…")
{
$string = trim($string);
$string_length = strlen($string);
$original_string = $string;
if ($string_length > $length) {
$remaining_chars = $string_length - $length;
if (strpos($string, '<') !== false && strpos($string, '>') !== false) {
$string = wordwrap($string, $length);
$string = explode("\n", $string, 2);
$string = $string[0] . $append;
$fillimi = substr_count($string, '<');
$fundi = substr_count($string, '>');
if ($fillimi == $fundi) {
$string = $string;
} else {
$i = 1;
while ($i <= $remaining_chars) {
$string = wordwrap($original_string, $length + $i);
$string = explode("\n", $string, 2);
$new_remaining_chars = $string_length - ($length + $i);
if ($new_remaining_chars > 0) {
$string = $string[0] . $append;
} else {
$string = $string[0];
}
$fillimi = substr_count($string, '<');
$fundi = substr_count($string, '>');
if ($fillimi == $fundi) {
$string = $string;
break;
}
$i++;
}
}
} else {
$string = trim($string);
$string = wordwrap($string, $length);
$string = explode("\n", $string, 2);
$string = $string[0] . $append;
}
}
return $string;
}
How to wrap text of HTML button with fixed width?
I found that you can make use of the white-space CSS property:
white-space: normal;
And it will break the words as normal text.
Related Topics
Shuffles Random Numbers with No Repetition in JavaScript/Php
How to Load a PHP File into a Variable
Pdo Multiple Named Placeholders Doesnt Retrieve Data
Combining and Compressing Multiple JavaScript Files in PHP
Tracking Email with PHP and Image
Printing to Pos Printer from PHP
How to Convert the Time from Am/Pm to 24 Hour Format in PHP
Read in Text File Line by Line PHP - Newline Not Being Detected
In Laravel How to Add Values to a Request Array
Supplied Argument Is Not a Valid MySQL Result Resource
How to Format Numbers to Have Only Two Decimal Places
Passing JavaScript Variables to PHP
Setting Element of Array from Twig
Minifying Final HTML Output Using Regular Expressions with Codeigniter
How Is Annotation Useful in PHP
Why Are Certain Types of Prepared Queries Using Pdo in PHP with MySQL Slow