diff options
| author | Veit Olschinski <veit@olschinski.de> | 2010-04-05 10:29:41 +0000 |
|---|---|---|
| committer | Veit Olschinski <veit@olschinski.de> | 2010-04-05 10:29:41 +0000 |
| commit | 5bc7b36708d272081ffd2df5658ddf12275447dd (patch) | |
| tree | 7884d88bd634f62d94e0239c911305ac4349dba8 /includes/functions/functions_rtl.php | |
| parent | 5dbb23df7f03aeb32c6c5a3fa6139dd479f9c0b4 (diff) | |
| download | webtrees-5bc7b36708d272081ffd2df5658ddf12275447dd.tar.gz webtrees-5bc7b36708d272081ffd2df5658ddf12275447dd.tar.bz2 webtrees-5bc7b36708d272081ffd2df5658ddf12275447dd.zip | |
Diffstat (limited to 'includes/functions/functions_rtl.php')
| -rw-r--r-- | includes/functions/functions_rtl.php | 1298 |
1 files changed, 1298 insertions, 0 deletions
diff --git a/includes/functions/functions_rtl.php b/includes/functions/functions_rtl.php new file mode 100644 index 0000000000..8cb8865b31 --- /dev/null +++ b/includes/functions/functions_rtl.php @@ -0,0 +1,1298 @@ +<?php +/** + * RTL Functions + * + * The functions in this file are common to all PGV pages and include date conversion + * routines and sorting functions. + * + * webtrees: Web based Family History software + * Copyright (C) 2010 webtrees development team. + * + * Derived from PhpGedView + * Copyright (C) 2002 to 2009 PGV Development Team. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @package webtrees + * @version $Id$ + */ + +if (!defined('WT_WEBTREES')) { + header('HTTP/1.0 403 Forbidden'); + exit; +} + +define('WT_FUNCTIONS_RTL_PHP', ''); + +$SpecialChar = array(' ','.',',','"','\'','/','\\','|',':',';','+','&','#','@','-','=','*','%','!','?','$','<','>',"\n"); +$SpecialPar = array('(',')','[',']','{','}'); +$SpecialNum = array('0','1','2','3','4','5','6','7','8','9'); +$RTLOrd = array(215,216,217,218,219); + +$openPar = '([{'; +$closePar = ')]}'; +$numbers = '0123456789'; +$numberPrefix = '+-'; // Treat these like numbers when at beginning or end of numeric strings +$numberPunctuation = '- ,.:/'; // Treat these like numbers when inside numeric strings +$punctuation = ',.:;?!'; + +function getLRM(){ + return "‎"; +} + +function getRLM(){ + return "‏"; +} + +/** + * This function strips ‎ and ‏ from the input string. It should be used for all + * text that has been passed through the PrintReady() function before that text is stored + * in the database. The database should NEVER contain these characters. + * + * @param string The string from which the ‎ and ‏ characters should be stripped + * @return string The input string, with ‎ and ‏ stripped + */ +function stripLRMRLM($inputText) { + return str_replace(array(WT_UTF8_LRM, WT_UTF8_RLM, WT_UTF8_LRO, WT_UTF8_RLO, WT_UTF8_LRE, WT_UTF8_RLE, WT_UTF8_PDF, "‎", "‏", "&LRM;", "&RLM;"), "", $inputText); +} + +/** + * This function encapsulates all texts in the input with <span dir='xxx'> and </span> + * according to the directionality specified. + * + * @param string Raw input + * @param string Directionality (LTR, BOTH, RTL) default BOTH + * @param string Additional text to insert into output <span dir="xxx"> (such as 'class="yyy"') + * @return string The string with all texts encapsulated as required + */ +function spanLTRRTL($inputText, $direction='BOTH', $class='') { + global $TEXT_DIRECTION; + global $openPar, $closePar, $punctuation; + global $numbers, $numberPrefix, $numberPunctuation; + global $previousState, $currentState, $posSpanStart, $waitingText; + global $startLTR, $endLTR, $startRTL, $endRTL, $lenStart, $lenEnd; + static $spanNumber = 0; + + if ($inputText == '') return ''; // Nothing to do + + $debug = false; // false for normal operation (no calls of the DumpString function) + + $spanNumber ++; + if ($debug) {echo '<br /><b>Input ', $spanNumber, ':</b>'; DumpString($inputText);} + + $workingText = str_replace("\n", '<br />', $inputText); + $workingText = str_replace(array('<span class="starredname"><br />', '<span<br />class="starredname">'), '<br /><span class="starredname">',$workingText); // Reposition some incorrectly placed line breaks + $workingText = stripLRMRLM($workingText); // Get rid of any existing UTF8 control codes + + $nothing = '‌'; // Zero Width Non-Joiner (not sure whether this is still needed to work around a TCPDF bug) + + $startLTR = '<LTR>'; // This will become '<span dir="ltr">' at the end + $endLTR = '</LTR>'; // This will become '</span>' at the end + $startRTL = '<RTL>'; // This will become '<span dir="rtl">' at the end + $endRTL = '</RTL>'; // This will become '</span>' at the end + $lenStart = strlen($startLTR); // RTL version MUST have same length + $lenEnd = strlen($endLTR); // RTL version MUST have same length + + $previousState = ''; + $currentState = strtoupper($TEXT_DIRECTION); + $numberState = false; // Set when we're inside a numeric string + $result = ''; + $waitingText = ''; + $openParDirection = array(); + + beginCurrentSpan($result); + + while ($workingText != '') { + $charArray = getChar($workingText, 0); // Get the next ASCII or UTF-8 character + $currentLetter = $charArray['letter']; + $currentLen = $charArray['length']; + + $openParIndex = strpos($openPar, $currentLetter); // Which opening parenthesis is this? + $closeParIndex = strpos($closePar, $currentLetter); // Which closing parenthesis is this? + + switch ($currentLetter) { + case '<': + // Assume this '<' starts an HTML element + $endPos = strpos($workingText, '>'); // look for the terminating '>' + if ($endPos === false) $endPos = 0; + $currentLen += $endPos; + $element = substr($workingText, 0, $currentLen); + $temp = strtolower(substr($element, 0, 3)); + if (strlen($element < 7) && $temp == '<br') { // assume we have '<br />' or a variant thereof + if ($numberState) { + $numberState = false; + $waitingText .= WT_UTF8_PDF; + } + breakCurrentSpan($result); + } else if ($waitingText == '') { + $result .= $element; + } else { + $waitingText .= $element; + } + $workingText = substr($workingText, $currentLen); + break; + case '&': + // Assume this '&' starts an HTML entity + $endPos = strpos($workingText, ';'); // look for the terminating ';' + if ($endPos === false) $endPos = 0; + $currentLen += $endPos; + $entity = substr($workingText, 0, $currentLen); + if (substr($entity, 0, 2 == '&#')) { + // look for possible New Line codes + if ((substr($entity, 2, 1) == 'x') || (substr($entity, 2, 1) == 'X')) { + // the entity is a hexadecimal number + $ordinal = hexdec(substr($entity, 3, -1)); + } else { + // the entity is a decimal number + $ordinal = intval(substr($entity, 2, -1)); + } + if ($ordinal == 10) { + // we have a New-Line code + if ($numberState) { + $numberState = false; + $waitingText .= WT_UTF8_PDF; + } + breakCurrentSpan($result); + $workingText = substr($workingText, $currentLen); + } + } else { + if (strtolower($entity) == ' ') { + $entity .= ' '; // Ensure consistent case for this entity + } + if ($waitingText == '') { + $result .= $entity; + } else { + $waitingText .= $entity; + } + $workingText = substr($workingText, $currentLen); + } + break; + case '{': + if (substr($workingText, 1, 1) == '{') { + // Assume this '{{' starts a TCPDF directive + $endPos = strpos($workingText, '}}'); // look for the terminating '}}' + if ($endPos === false) $endPos = 0; + $currentLen = $endPos + 2; + $directive = substr($workingText, 0, $currentLen); + $workingText = substr($workingText, $currentLen); + $result = $result . $waitingText . $directive; + $waitingText = ''; + break; + } + default: + // Look for strings of numbers with optional leading or trailing + or - + // and with optional embedded numeric punctuation + if ($numberState) { + // If we're inside a numeric string, look for reasons to end it + $offset = 0; // Be sure to look at the current character first + $charArray = getChar($workingText."\n", $offset); + if (strpos($numbers, $charArray['letter']) === false) { + // This is not a digit. Is it numeric punctuation? + if (substr($workingText."\n", $offset, 6) == ' ') { + $offset += 6; // This could be numeric punctuation + } else if (strpos($numberPunctuation, $charArray['letter']) !== false) { + $offset += $charArray['length']; // This could be numeric punctuation + } + // If the next character is a digit, the current character is numeric punctuation + $charArray = getChar($workingText."\n", $offset); + if (strpos($numbers, $charArray['letter']) === false) { + // This is not a digit. End the run of digits and punctuation. + $numberState = false; + if (strpos($numberPrefix, $currentLetter) === false) { + $currentLetter = WT_UTF8_PDF . $currentLetter; + } else { + $currentLetter = $currentLetter . WT_UTF8_PDF; // Include a trailing + or - in the run + } + } + } + } else { + // If we're outside a numeric string, look for reasons to start it + if (strpos($numberPrefix, $currentLetter) !== false) { + // This might be a number lead-in + $offset = $currentLen; + $nextChar = substr($workingText."\n", $offset, 1); + if (strpos($numbers, $nextChar) !== false) { + $numberState = true; // We found a digit: the lead-in is therefore numeric + $currentLetter = WT_UTF8_LRE . $currentLetter; + } + } else if (strpos($numbers, $currentLetter) !== false) { + $numberState = true; // The current letter is a digit + $currentLetter = WT_UTF8_LRE . $currentLetter; + } + } + + // Determine the directionality of the current UTF-8 character + $newState = $currentState; + while (true) { + if (utf8_direction($currentLetter)=='rtl') { + if ($currentState == '') { + $newState = 'RTL'; + break; + } + + if ($currentState == 'RTL') break; + // Switch to RTL only if this isn't a solitary RTL letter + $tempText = substr($workingText, $currentLen); + while ($tempText != '') { + $nextCharArray = getChar($tempText, 0); + $nextLetter = $nextCharArray['letter']; + $nextLen = $nextCharArray['length']; + $tempText = substr($tempText, $nextLen); + + if (utf8_direction($nextLetter)=='rtl') { + $newState = 'RTL'; + break 2; + } + + if (strpos($punctuation, $nextLetter) !== false || strpos($openPar, $nextLetter) !== false) { + $newState = 'RTL'; + break 2; + } + + if ($nextLetter == ' ') break; + $nextLetter .= substr($tempText."\n", 0, 5); + if ($nextLetter == ' ') { + $tempText = substr($tempText, 5); + break; + } + } + // This is a solitary RTL letter : wrap it in UTF8 control codes to force LTR directionality + $currentLetter = WT_UTF8_LRO . $currentLetter . WT_UTF8_PDF; + $newState = 'LTR'; + break; + } + if (($currentLen != 1) || ($currentLetter >= 'A' && $currentLetter <= 'Z') || ($currentLetter >= 'a' && $currentLetter <= 'z')) { + // Since it's neither Hebrew nor Arabic, this UTF-8 character or ASCII letter must be LTR + $newState = 'LTR'; + break; + } + if ($closeParIndex !== false) { + // This closing parenthesis has to inherit the matching opening parenthesis' directionality + if (!empty($openParDirection[$closeParIndex]) && $openParDirection[$closeParIndex] != '?') { + $newState = $openParDirection[$closeParIndex]; + } + $openParDirection[$closeParIndex] = ''; + break; + } + if ($openParIndex !== false) { + // Opening parentheses always inherit the following directionality + $waitingText .= $currentLetter; + $workingText = substr($workingText, $currentLen); + while (true) { + if ($workingText == '') break; + if (substr($workingText, 0, 1) == ' ') { + // Spaces following this left parenthesis inherit the following directionality too + $waitingText .= ' '; + $workingText = substr($workingText, 1); + continue; + } + if (substr($workingText, 0, 6) == ' ') { + // Spaces following this left parenthesis inherit the following directionality too + $waitingText .= ' '; + $workingText = substr($workingText, 6); + continue; + } + break; + } + $openParDirection[$openParIndex] = '?'; + break 2; // double break because we're waiting for more information + } + + // We have a digit or a "normal" special character. + // + // When this character is not at the start of the input string, it inherits the preceding directionality; + // at the start of the input string, it assumes the following directionality. + // + // Exceptions to this rule will be handled later during final clean-up. + // + $waitingText .= $currentLetter; + $workingText = substr($workingText, $currentLen); + if ($currentState != '') { + $result .= $waitingText; + $waitingText = ''; + } + break 2; // double break because we're waiting for more information + } + if ($newState != $currentState) { + // A direction change has occurred + finishCurrentSpan($result, false); + $previousState = $currentState; + $currentState = $newState; + beginCurrentSpan($result); + } + $waitingText .= $currentLetter; + $workingText = substr($workingText, $currentLen); + $result .= $waitingText; + $waitingText = ''; + + foreach($openParDirection as $index => $value) { + // Since we now know the proper direction, remember it for all waiting opening parentheses + if ($value == '?') { + $openParDirection[$index] = $currentState; + } + } + + break; + } + } + + // We're done. Finish last <span> if necessary + if ($numberState) { + $numberState = false; + if ($waitingText == '') { + $result .= WT_UTF8_PDF; + } else { + $waitingText .= WT_UTF8_PDF; + } + } + finishCurrentSpan($result, true); + + // Get rid of any waiting text + if ($waitingText != '') { + if ($TEXT_DIRECTION == 'rtl' && $currentState == 'LTR') { + $result .= $startRTL; + $result .= $waitingText; + $result .= $endRTL; + } else { + $result .= $startLTR; + $result .= $waitingText; + $result .= $endLTR; + } + $waitingText = ''; + } + + // Lastly, do some more cleanups + if ($debug) {echo '<b>Interim Output:</b>'; DumpString($result);} + + // Move leading RTL numeric strings to following LTR text + // (this happens when the page direction is RTL and the original text begins with a number and is followed by LTR text) + while (substr($result, 0, $lenStart+3) == $startRTL.WT_UTF8_LRE) { + $spanEnd = strpos($result, $endRTL.$startLTR); + if ($spanEnd === false) { + break; + } + $textSpan = stripLRMRLM(substr($result, $lenStart+3, $spanEnd-$lenStart-3)); + $langSpan = utf8_script($textSpan); + if ($langSpan == 'hebrew' || $langSpan == 'arabic') { + break; + } + $result = $startLTR . substr($result, $lenStart, $spanEnd-$lenStart) . substr($result, $spanEnd+$lenStart+$lenEnd); + break; + } + + // On RTL pages, put trailing "." in RTL numeric strings into its own RTL span + if ($TEXT_DIRECTION == 'rtl') { + $result = str_replace(WT_UTF8_PDF.'.'.$endRTL, WT_UTF8_PDF.$endRTL.$startRTL.'.'.$endRTL, $result); + } + + // Trim trailing blanks preceding <br /> in LTR text + while ($previousState != 'RTL') { + if (strpos($result, ' <LTRbr />') !== false) { + $result = str_replace(' <LTRbr />', '<LTRbr />', $result); + continue; + } + if (strpos($result, ' <LTRbr />') !== false) { + $result = str_replace(' <LTRbr />', '<LTRbr />', $result); + continue; + } + if (strpos($result, ' <br />') !== false) { + $result = str_replace(' <br />', '<br />', $result); + continue; + } + if (strpos($result, ' <br />') !== false) { + $result = str_replace(' <br />', '<br />', $result); + continue; + } + break; // Neither space nor : we're done + } + + // Trim trailing blanks preceding <br /> in RTL text + while (true) { + if (strpos($result, ' <RTLbr />') !== false) { + $result = str_replace(' <RTLbr />', '<RTLbr />', $result); + continue; + } + if (strpos($result, ' <RTLbr />') !== false) { + $result = str_replace(' <RTLbr />', '<RTLbr />', $result); + continue; + } + break; // Neither space nor : we're done + } + + // Convert '<LTRbr />' and '<RTLbr /' + $result = str_replace(array('<LTRbr />', '<RTLbr />'), array($endLTR.'<br />'.$startLTR, $endRTL.'<br />'.$startRTL), $result); + + // Include leading indeterminate directional text in whatever follows + if (substr($result."\n", 0, $lenStart) != $startLTR && substr($result."\n", 0, $lenStart) != $startRTL && substr($result."\n", 0, 6) != '<br />') { + $leadingText = ''; + while (true) { + if ($result == '') { + $result = $leadingText; + break; + } + if (substr($result."\n", 0, $lenStart) != $startLTR && substr($result."\n", 0, $lenStart) != $startRTL) { + $leadingText .= substr($result, 0, 1); + $result = substr($result, 1); + continue; + } + $result = substr($result, 0, $lenStart) . $leadingText . substr($result, $lenStart); + break; + } + } + + // Include solitary "-" and "+" in surrounding RTL text + $result = str_replace(array($endRTL.$startLTR.'-'.$endLTR.$startRTL, $endRTL.$startLTR.'-'.$endLTR.$startRTL), array('-', '+'), $result); + + // Remove empty spans + $result = str_replace(array($startLTR.$endLTR, $startRTL.$endRTL), '', $result); + + // Finally, correct '<LTR>', '</LTR>', '<RTL>', and '</RTL>' + switch ($direction) { + case 'BOTH': + case 'both': + // LTR text: <span dir="ltr"> text </span> + // RTL text: <span dir="rtl"> text </span> + $sLTR = '<span dir="ltr" '.$class.'>'.$nothing; + $eLTR = $nothing.'</span>'; + $sRTL = '<span dir="rtl" '.$class.'>'.$nothing; + $eRTL = $nothing.'</span>'; + break; + case 'LTR': + case 'ltr': + // LTR text: <span dir="ltr"> text </span> + // RTL text: text + $sLTR = '<span dir="ltr" '.$class.'>'.$nothing; + $eLTR = $nothing.'</span>'; + $sRTL = ''; + $eRTL = ''; + break; + case 'RTL': + case 'rtl': + default: + // LTR text: text + // RTL text: <span dir="rtl"> text </span> + $sLTR = ''; + $eLTR = ''; + $sRTL = '<span dir="rtl" '.$class.'>'.$nothing; + $eRTL = $nothing.'</span>'; + break; + } + $result = str_replace(array($startLTR, $endLTR, $startRTL, $endRTL), array($sLTR, $eLTR, $sRTL, $eRTL), $result); + if ($debug) {echo '<b>Final Output:</b>'; DumpString($result);} + return $result; +} + +/* + * Wrap words that have an asterisk suffix in <u> and </u> tags. This should underline + * starred names to show the preferred name + */ +function starredName($textSpan, $direction) { + global $TEXT_DIRECTION; + + // To avoid a TCPDF bug that mixes up the word order, insert those <u> and </u> tags + // only when page and span directions are identical. + if ($direction == strtoupper($TEXT_DIRECTION)) { + while (true) { + $starPos = strpos($textSpan, '*'); + if ($starPos === false) break; + $trailingText = substr($textSpan, $starPos+1); + $textSpan = substr($textSpan, 0, $starPos); + $wordStart = strrpos($textSpan, ' '); // Find the start of the word + if ($wordStart !== false) { + $leadingText = substr($textSpan, 0, $wordStart+1); + $wordText = substr($textSpan, $wordStart+1); + } else { + $leadingText = ''; + $wordText = $textSpan; + } + $textSpan = $leadingText . '<u>' . $wordText . '</u>' . $trailingText; + } + $textSpan = preg_replace('~<span class="starredname">(.*)</span>~', '<u>\1</u>', $textSpan); + // The is a work-around for a TCPDF bug eating blanks. + $textSpan = str_replace(array(' <u>', '</u> '), array(' <u>', '</u> '), $textSpan); + } else { + // Text and page directions differ: remove the <span> and </span> + $textSpan = preg_replace('~(.*)\*~', '\1', $textSpan); + $textSpan = preg_replace('~<span class="starredname">(.*)</span>~', '\1', $textSpan); + } + + return $textSpan; +} + +/** + * Get the next character from an input string + */ +function getChar($text, $offset) { + + if ($text == '') return array('letter'=>'', 'length'=>0); + + $char = substr($text, $offset, 1); + $length = 1; + if ((ord($char) & 0xE0) == 0xC0) $length = 2; // 2-byte sequence + if ((ord($char) & 0xF0) == 0xE0) $length = 3; // 3-byte sequence + if ((ord($char) & 0xF8) == 0xF0) $length = 4; // 4-byte sequence + $letter = substr($text, $offset, $length); + + return array('letter'=>$letter, 'length'=>$length); +} + +/** + * Insert <br /> into current span + */ +function breakCurrentSpan(&$result) { + global $currentState, $waitingText; + + // Interrupt the current span, insert that <br />, and then continue the current span + $result .= $waitingText; + $waitingText = ''; + + $breakString = '<' . $currentState . 'br />'; + $result .= $breakString; + + return; +} + +/** + * Begin current span + */ +function beginCurrentSpan(&$result) { + global $currentState, $startLTR, $startRTL, $posSpanStart; + + if ($currentState == 'LTR') $result .= $startLTR; + if ($currentState == 'RTL') $result .= $startRTL; + + $posSpanStart = strlen($result); + + return; +} + +/** + * Finish current span + */ +function finishCurrentSpan(&$result, $theEnd=false) { + global $previousState, $currentState, $posSpanStart, $TEXT_DIRECTION, $waitingText; + global $startLTR, $endLTR, $startRTL, $endRTL, $lenStart, $lenEnd; + global $numbers, $punctuation; + + $textSpan = substr($result, $posSpanStart); + $result = substr($result, 0, $posSpanStart); + + // Get rid of empty spans, so that our check for presence of RTL will work + $result = str_replace(array($startLTR.$endLTR, $startRTL.$endRTL), '', $result); + + // Look for numeric strings that are times (hh:mm:ss). These have to be separated from surrounding numbers. + $tempResult = ''; + while ($textSpan != '') { + $posColon = strpos($textSpan, ':'); + if ($posColon === false) break; // No more possible time strings + $posLRE = strpos($textSpan, WT_UTF8_LRE); + if ($posLRE === false) break; // No more numeric strings + $posPDF = strpos($textSpan, WT_UTF8_PDF, $posLRE); + if ($posPDF === false) break; // No more numeric strings + + $tempResult .= substr($textSpan, 0, $posLRE+3); // Copy everything preceding the numeric string + $numericString = substr($textSpan, $posLRE+3, $posPDF-$posLRE); // Separate the entire numeric string + $textSpan = substr($textSpan, $posPDF+3); + $posColon = strpos($numericString, ':'); + if ($posColon === false) { + // Nothing that looks like a time here + $tempResult .= $numericString; + continue; + } + $posBlank = strpos($numericString.' ', ' '); + $posNbsp = strpos($numericString.' ', ' '); + if ($posBlank < $posNbsp) { + $posSeparator = $posBlank; + $lengthSeparator = 1; + } else { + $posSeparator = $posNbsp; + $lengthSeparator = 6; + } + if ($posColon > $posSeparator) { + // We have a time string preceded by a blank: Exclude that blank from the numeric string + $tempResult .= substr($numericString, 0, $posSeparator); + $tempResult .= WT_UTF8_PDF; + $tempResult .= substr($numericString, $posSeparator, $lengthSeparator); + $tempResult .= WT_UTF8_LRE; + $numericString = substr($numericString, $posSeparator+$lengthSeparator); + } + + $posBlank = strpos($numericString, ' '); + $posNbsp = strpos($numericString, ' '); + if ($posBlank === false && $posNbsp === false) { + // The time string isn't followed by a blank + $textSpan = $numericString . $textSpan; + continue; + } + + // We have a time string followed by a blank: Exclude that blank from the numeric string + if ($posBlank === false) { + $posSeparator = $posNbsp; + $lengthSeparator = 6; + } else if ($posNbsp === false) { + $posSeparator = $posBlank; + $lengthSeparator = 1; + } else if ($posBlank < $posNbsp) { + $posSeparator = $posBlank; + $lengthSeparator = 1; + } else { + $posSeparator = $posNbsp; + $lengthSeparator = 6; + } + $tempResult .= substr($numericString, 0, $posSeparator); + $tempResult .= WT_UTF8_PDF; + $tempResult .= substr($numericString, $posSeparator, $lengthSeparator); + $posSeparator += $lengthSeparator; + $numericString = substr($numericString, $posSeparator); + $textSpan = WT_UTF8_LRE . $numericString . $textSpan; + } + $textSpan = $tempResult . $textSpan; + $trailingBlanks = ''; + $trailingBreaks = ''; + +/* ****************************** LTR text handling ******************************** */ + + if ($currentState == 'LTR') { + // Move trailing numeric strings to the following RTL text. Include any blanks preceding or following the numeric text too. + if ($TEXT_DIRECTION == 'rtl' && $previousState == 'RTL' && !$theEnd) { + $trailingString = ''; + $savedSpan = $textSpan; + while ($textSpan != '') { + // Look for trailing spaces and tentatively move them + if (substr($textSpan, -1) == ' ') { + $trailingString = ' ' . $trailingString; + $textSpan = substr($textSpan, 0, -1); + continue; + } + if (substr($textSpan, -6) == ' ') { + $trailingString = ' ' . $trailingString; + $textSpan = substr($textSpan, 0, -1); + continue; + } + if (substr($textSpan, -3) != WT_UTF8_PDF) { + // There is no trailing numeric string + $textSpan = $savedSpan; + break; + } + + // We have a numeric string + $posStartNumber = strrpos($textSpan, WT_UTF8_LRE); + if ($posStartNumber === false) $posStartNumber = 0; + $trailingString = substr($textSpan, $posStartNumber, strlen($textSpan)-$posStartNumber) . $trailingString; + $textSpan = substr($textSpan, 0, $posStartNumber); + + // Look for more spaces and move them too + while ($textSpan != '') { + if (substr($textSpan, -1) == ' ') { + $trailingString = ' ' . $trailingString; + $textSpan = substr($textSpan, 0, -1); + continue; + } + if (substr($textSpan, -6) == ' ') { + $trailingString = ' ' . $trailingString; + $textSpan = substr($textSpan, 0, -1); + continue; + } + break; + } + + $waitingText = $trailingString . $waitingText; + break; + } + } + + $savedSpan = $textSpan; + // Move any trailing <br />, optionally preceded or followed by blanks, outside this LTR span + while ($textSpan != '') { + if (substr($textSpan, -1) == ' ') { + $trailingBlanks = ' ' . $trailingBlanks; + $textSpan = substr($textSpan, 0, -1); + continue; + } + if (substr('......'.$textSpan, -6) == ' ') { + $trailingBlanks = ' ' . $trailingBlanks; + $textSpan = substr($textSpan, 0, -6); + continue; + } + break; + } + while (substr($textSpan, -9) == '<LTRbr />') { + $trailingBreaks = '<br />' . $trailingBreaks; // Plain <br /> because it's outside a span + $textSpan = substr($textSpan, 0, -9); + } + if ($trailingBreaks != '') { + while ($textSpan != '') { + if (substr($textSpan, -1) == ' ') { + $trailingBreaks = ' ' . $trailingBreaks; + $textSpan = substr($textSpan, 0, -1); + continue; + } + if (substr('......'.$textSpan, -6) == ' ') { + $trailingBreaks = ' ' . $trailingBreaks; + $textSpan = substr($textSpan, 0, -6); + continue; + } + break; + } + $waitingText = $trailingBlanks . $waitingText; // Put those trailing blanks inside the following span + } else { + $textSpan = $savedSpan; + } + + $savedSpan = $textSpan; + $trailingBlanks = ''; + $trailingPunctuation = ''; + $trailingID = ''; + $trailingSeparator = ''; + $leadingSeparator = ''; + while ($TEXT_DIRECTION == 'rtl') { + if (strpos($result, $startRTL) !== false) { + // Remove trailing blanks for inclusion in a separate LTR span + while ($textSpan != '') { + if (substr($textSpan, -1) == ' ') { + $trailingBlanks = ' ' . $trailingBlanks; + $textSpan = substr($textSpan, 0, -1); + continue; + } + if (substr($textSpan, -6) == ' ') { + $trailingBlanks = ' ' . $trailingBlanks; + $textSpan = substr($textSpan, 0, -1); + continue; + } + break; + } + + // Remove trailing punctuation for inclusion in a separate LTR span + if ($textSpan == '') $trailingChar = "\n"; + else $trailingChar = substr($textSpan, -1); + if (strpos($punctuation, $trailingChar) !== false) { + $trailingPunctuation = $trailingChar; + $textSpan = substr($textSpan, 0, -1); + } + } + + // Remove trailing ID numbers that look like "(xnnn)" for inclusion in a separate LTR span + while (true) { + if (substr($textSpan, -1) != ')') break; // There is no trailing ')' + $posLeftParen = strrpos($textSpan, '('); + if ($posLeftParen === false) break; // There is no leading '(' + $temp = stripLRMRLM(substr($textSpan, $posLeftParen)); // Get rid of UTF8 control codes + + // If the parenthesized text doesn't look like an ID number, + // we don't want to touch it. + // This check won't work if somebody uses ID numbers with an unusual format. + $offset = 1; + $charArray = getchar($temp, $offset); // Get 1st character of parenthesized text + if (strpos($numbers, $charArray['letter']) !== false) break; + $offset += $charArray['length']; // Point at 2nd character of parenthesized text + if (strpos($numbers, substr($temp, $offset, 1)) === false) break; + // 1st character of parenthesized text is alpha, 2nd character is a digit; last has to be a digit too + if (strpos($numbers, substr($temp, -2, 1)) === false) break; + + $trailingID = substr($textSpan, $posLeftParen); + $textSpan = substr($textSpan, 0, $posLeftParen); + break; + } + + // Look for " - " or blank preceding the ID number and remove it for inclusion in a separate LTR span + $savedSpan = $textSpan; + if ($trailingID != '') { + while ($textSpan != '') { + if (substr($textSpan, -1) == ' ') { + $trailingSeparator = ' ' . $trailingSeparator; + $textSpan = substr($textSpan, 0, -1); + continue; + } + if (substr($textSpan, -6) == ' ') { + $trailingSeparator = ' ' . $trailingSeparator; + $textSpan = substr($textSpan, 0, -6); + continue; + } + if (substr($textSpan, -1) == '-') { + $trailingSeparator = '-' . $trailingSeparator; + $textSpan = substr($textSpan, 0, -1); + continue; + } + break; + } + } + + // Look for " - " preceding the text and remove it for inclusion in a separate LTR span + $foundSeparator = false; + $savedSpan = $textSpan; + while ($textSpan != '') { + if (substr($textSpan, 0, 1) == ' ') { + $leadingSeparator = ' ' . $leadingSeparator; + $textSpan = substr($textSpan, 1); + continue; + } + if (substr($textSpan, 0, 6) == ' ') { + $leadingSeparator = ' ' . $leadingSeparator; + $textSpan = substr($textSpan, 6); + continue; + } + if (substr($textSpan, 0, 1) == '-') { + $leadingSeparator = '-' . $leadingSeparator; + $textSpan = substr($textSpan, 1); + $foundSeparator = true; + continue; + } + break; + } + if (!$foundSeparator) { + $textSpan = $savedSpan; + $leadingSeparator = ''; + } + break; + } + + // We're done: finish the span + $textSpan = starredName($textSpan, 'LTR'); // Wrap starred name in <u> and </u> tags + while (true) { + // Remove blanks that precede <LTRbr /> + if (strpos($textSpan, ' <LTRbr />') !== false) { + $textSpan = str_replace(' <LTRbr />', '<LTRbr />', $textSpan); + continue; + } + if (strpos($textSpan, ' <LTRbr />') !== false) { + $textSpan = str_replace(' <LTRbr />', '<LTRbr />', $textSpan); + continue; + } + break; + } + if ($leadingSeparator != '') $result = $result . $startLTR . $leadingSeparator . $endLTR; + $result = $result . $textSpan . $endLTR; + if ($trailingSeparator != '') $result = $result . $startLTR . $trailingSeparator . $endLTR; + if ($trailingID != '') $result = $result . $startLTR . $trailingID . $endLTR; + if ($trailingPunctuation != '') $result = $result . $startLTR . $trailingPunctuation . $endLTR; + if ($trailingBlanks != '') $result = $result . $startLTR . $trailingBlanks . $endLTR; + } + +/* ****************************** RTL text handling ******************************** */ + + if ($currentState == 'RTL') { + $savedSpan = $textSpan; + + // Move any trailing <br />, optionally followed by blanks, outside this RTL span + while ($textSpan != '') { + if (substr($textSpan, -1) == ' ') { + $trailingBlanks = ' ' . $trailingBlanks; + $textSpan = substr($textSpan, 0, -1); + continue; + } + if (substr('......'.$textSpan, -6) == ' ') { + $trailingBlanks = ' ' . $trailingBlanks; + $textSpan = substr($textSpan, 0, -6); + continue; + } + break; + } + while (substr($textSpan, -9) == '<RTLbr />') { + $trailingBreaks = '<br />' . $trailingBreaks; // Plain <br /> because it's outside a span + $textSpan = substr($textSpan, 0, -9); + } + if ($trailingBreaks != '') { + $waitingText = $trailingBlanks . $waitingText; // Put those trailing blanks inside the following span + } else { + $textSpan = $savedSpan; + } + + // Move trailing numeric strings to the following LTR text. Include any blanks preceding or following the numeric text too. + if (!$theEnd && $TEXT_DIRECTION != 'rtl') { + $trailingString = ''; + $savedSpan = $textSpan; + while ($textSpan != '') { + // Look for trailing spaces and tentatively move them + if (substr($textSpan, -1) == ' ') { + $trailingString = ' ' . $trailingString; + $textSpan = substr($textSpan, 0, -1); + continue; + } + if (substr($textSpan, -6) == ' ') { + $trailingString = ' ' . $trailingString; + $textSpan = substr($textSpan, 0, -1); + continue; + } + if (substr($textSpan, -3) != WT_UTF8_PDF) { + // There is no trailing numeric string + $textSpan = $savedSpan; + break; + } + + // We have a numeric string + $posStartNumber = strrpos($textSpan, WT_UTF8_LRE); + if ($posStartNumber === false) $posStartNumber = 0; + $trailingString = substr($textSpan, $posStartNumber, strlen($textSpan)-$posStartNumber) . $trailingString; + $textSpan = substr($textSpan, 0, $posStartNumber); + + // Look for more spaces and move them too + while ($textSpan != '') { + if (substr($textSpan, -1) == ' ') { + $trailingString = ' ' . $trailingString; + $textSpan = substr($textSpan, 0, -1); + continue; + } + if (substr($textSpan, -6) == ' ') { + $trailingString = ' ' . $trailingString; + $textSpan = substr($textSpan, 0, -1); + continue; + } + break; + } + + $waitingText = $trailingString . $waitingText; + break; + } + } + + // Trailing " - " needs to be prefixed to the following span + if (!$theEnd && substr('...'.$textSpan, -3) == ' - ') { + $textSpan = substr($textSpan, 0, -3); + $waitingText = ' - ' . $waitingText; + } + + while ($TEXT_DIRECTION == 'rtl') { + // Look for " - " preceding <RTLbr /> and relocate it to the front of the string + $posDashString = strpos($textSpan, ' - <RTLbr />'); + if ($posDashString === false) break; + $posStringStart = strrpos(substr($textSpan, 0, $posDashString), '<RTLbr />'); + if ($posStringStart === false) $posStringStart = 0; + else $posStringStart += 9; // Point to the first char following the last <RTLbr /> + + $textSpan = substr($textSpan, 0, $posStringStart) . ' - ' . substr($textSpan, $posStringStart, $posDashString-$posStringStart) . substr($textSpan, $posDashString+3); + } + + // Strip leading spaces from the RTL text + $countLeadingSpaces = 0; + while ($textSpan != '') { + if (substr($textSpan, 0, 1) == ' ') { + $countLeadingSpaces ++; + $textSpan = substr($textSpan, 1); + continue; + } + if (substr($textSpan, 0, 6) == ' ') { + $countLeadingSpaces ++; + $textSpan = substr($textSpan, 6); + continue; + } + break; + } + + // Strip trailing spaces from the RTL text + $countTrailingSpaces = 0; + while ($textSpan != '') { + if (substr($textSpan, -1) == ' ') { + $countTrailingSpaces ++; + $textSpan = substr($textSpan, 0, -1); + continue; + } + if (substr($textSpan, -6) == ' ') { + $countTrailingSpaces ++; + $textSpan = substr($textSpan, 0, -6); + continue; + } + break; + } + + // Look for trailing " -", reverse it, and relocate it to the front of the string + if (substr($textSpan, -2) == ' -') { + $posDashString = strlen($textSpan) - 2; + $posStringStart = strrpos(substr($textSpan, 0, $posDashString), '<RTLbr />'); + if ($posStringStart === false) $posStringStart = 0; + else $posStringStart += 9; // Point to the first char following the last <RTLbr /> + + $textSpan = substr($textSpan, 0, $posStringStart) . '- ' . substr($textSpan, $posStringStart, $posDashString-$posStringStart) . substr($textSpan, $posDashString+2); + } + + if ($countLeadingSpaces != 0) { + $newLength = strlen($textSpan) + $countLeadingSpaces; + $textSpan = str_pad($textSpan, $newLength, ' ', ($TEXT_DIRECTION=='rtl' ? STR_PAD_LEFT:STR_PAD_RIGHT)); + } + if ($countTrailingSpaces != 0) { + if ($TEXT_DIRECTION == 'ltr') { + if ($trailingBreaks == '') { + // Move trailing RTL spaces to front of following LTR span + $newLength = strlen($waitingText) + $countTrailingSpaces; + $waitingText = str_pad($waitingText, $newLength, ' ', STR_PAD_LEFT); + } + } else { + $newLength = strlen($textSpan) + $countTrailingSpaces; + $textSpan = str_pad($textSpan, $newLength, ' ', STR_PAD_RIGHT); + } + } + + // We're done: finish the span + $textSpan = starredName($textSpan, 'RTL'); // Wrap starred name in <u> and </u> tags + $result = $result . $textSpan . $endRTL; + } + + if ($currentState != 'LTR' && $currentState != 'RTL') { + $result = $result . $textSpan; + } + + $result .= $trailingBreaks; // Get rid of any waiting <br /> + + return; +} + +/** + * convert HTML entities to to their original characters + * + * original found at http://www.php.net/manual/en/function.get-html-translation-table.php + * @see http://www.php.net/manual/en/function.get-html-translation-table.php + * @param string $string the string to remove the entities from + * @return string the string with entities converted + */ +function unhtmlentities($string) { + $trans_tbl=array_flip(get_html_translation_table (HTML_ENTITIES)); + $trans_tbl['‎']=WT_UTF8_LRM; + $trans_tbl['‏']=WT_UTF8_RLM; + return preg_replace('/&#(\d+);/e', "chr(\\1)", strtr($string, $trans_tbl)); +} + +/** + * process a string according to bidirectional rules + * + * this function will take a text string and reverse it for RTL languages + * according to bidi rules. + * @param string $text String to change + * @return string the new bidi string + * @todo add other RTL langauges + */ +function bidi_text($text) { + global $RTLOrd; + + // דו"ח אישי + //קראטוןםפ שדגכעיחלךף זסבה� מצתץ עברי איתה מאיה (אתקה) שם משפחה + //מספר מזהה (SSN) + + $found = false; + foreach($RTLOrd as $indexval => $ord) { + if (strpos($text, chr($ord))!==false) $found=true; + } + if (!$found) return $text; + + $special_chars = array(' ','"','\'','(',')','[',']',':',"\n"); + $newtext = ""; + $parts = array(); + $temp = ""; + $state = 0; + $p = 0; + for($i=0; $i<strlen($text); $i++) { + $letter = $text{$i}; + //print $letter.ord($letter).","; + //-- handle Hebrew chars + if (in_array(ord($letter),$RTLOrd)) { + if (!empty($temp)) { + //-- just in case the $temp is a Hebrew char push it onto the stack + if (in_array(ord($temp{0}),$RTLOrd)); + //-- if the $temp starts with a char in the special_chars array then remove the space and push it onto the stack seperately + else if (in_array($temp{strlen($temp)-1}, $special_chars)) { + $char = substr($temp, strlen($temp)-1); + $temp = substr($temp, 0, strlen($temp)-1); + if ($char=="[") $char = "]"; + else if ($char=="(") $char = ")"; + array_push($parts, $temp); + array_push($parts, $char); + } + //-- otherwise push it onto the begining of the stack + else array_unshift($parts, $temp); + } + $temp = $letter . $text{$i+1}; + $i++; + if ($i < strlen($text)-1) { + $l = $text{$i+1}; + if (in_array($l, $special_chars)) { + if ($l=="]") $l = "["; + else if ($l==")") $l = "("; + $temp = $l . $temp; + $i++; + } + } + array_push($parts, $temp); + $temp = ""; + } + else if (ord($letter)==226) { + if ($i < strlen($text)-2) { + $l = $letter.$text{$i+1}.$text{$i+2}; + $i += 2; + if (($l==WT_UTF8_LRM)||($l==WT_UTF8_RLM)) { + if (!empty($temp)) { + $last = array_pop($parts); + if ($temp{0}==")") $last = '(' . $last; + else if ($temp{0}=="(") $last = ')' . $last; + else if ($temp{0}=="]") $last = '[' . $last; + else if ($temp{0}=="[") $last = ']' . $last; + array_push($parts, $last); + $temp = ""; + } + } + } + } + else $temp .= $letter; + } + if (!empty($temp)) { + if (in_array(ord($temp{0}),$RTLOrd)) array_push($parts, $temp); + else array_push($parts, $temp); + } + + //-- loop through and check if parenthesis are correct... if parenthesis were broken by + //-- rtl text then they need to be reversed + for($i=0; $i<count($parts); $i++) { + $bef = ""; + $aft = ""; + $wt = preg_match("/^(\s*).*(\s*)$/", $parts[$i], $match); + if ($wt>0) { + $bef = $match[1]; + $aft = $match[2]; + } + $temp = trim($parts[$i]); + if (!empty($temp)) { + if ($temp{0}=="(" && $temp{strlen($temp)-1}!=")") $parts[$i] = $bef.substr($temp, 1).")".$aft; + if ($temp{0}=="[" && $temp{strlen($temp)-1}!="]") $parts[$i] = $bef.substr($temp, 1)."]".$aft; + if ($temp{0}!="(" && $temp{strlen($temp)-1}==")") $parts[$i] = $bef."(".substr($temp, 0, strlen($temp)-1).$aft; + if ($temp{0}!="[" && $temp{strlen($temp)-1}=="]") $parts[$i] = $bef."[".substr($temp, 0, strlen($temp)-1).$aft; + } + } + //print_r($parts); + $parts = array_reverse($parts); + $newtext = implode("", $parts); + return $newtext; +} + +/** + * Verify if text is a RtL character + * + * This will verify if text is a RtL character + * @param string $text to verify + */ +function oneRTLText($text) { + global $RTLOrd; + //--- What if gedcom in ANSI? + return (strlen($text)==2 && in_array(ord($text),$RTLOrd)); +} + +/** + * Verify if text starts by a RtL character + * + * This will verify if text starts by a RtL character + * @param string $text to verify + */ +function begRTLText($text) { + global $RTLOrd; + //--- What if gedcom in ANSI? + return (in_array(ord(substr(trim($text),0,2)),$RTLOrd) || in_array(ord(substr(trim($text),1,2)),$RTLOrd)); +} + +/** + * Verify if text ends by a RtL character + * + * This will verify if text ends by a RtL character + * @param string $text to verify + */ +function endRTLText($text) { + global $RTLOrd; + //--- What if gedcom in ANSI? -- I believe that not used + return (in_array(ord(substr(trim($text),strlen(trim($text))-2,2)),$RTLOrd) || in_array(ord(substr(trim($text),strlen(trim($text))-3,2)),$RTLOrd)); +} + +/** + * Verify if text is RtL + * + * This will verify if text has RtL characters + * @param string $text to verify + */ +function hasRTLText($text) { + global $RTLOrd; + //--- What if gedcom in ANSI? + // if (!(strpos($text, chr(215))=== false)) return true; // OK? + for ($i=0; $i<strlen($text); $i++) { + if (in_array(ord(substr(trim($text),$i,2)),$RTLOrd)) return true; + } + return false; + +} + +/** + * Verify if text is LtR + * + * This will verify if text has LtR characters that are not special characters + * @param string $text to verify + */ +function hasLTRText($text) { + global $SpecialChar, $SpecialPar, $SpecialNum, $RTLOrd; + //--- What if gedcom in ANSI? + //--- Should have one fullspecial characters array in PGV - + + for ($i=0; $i<strlen($text); $i++) { + if (in_array(ord(substr(trim($text),$i,2)),$RTLOrd) || in_array(ord(substr(trim($text),$i-1,2)),$RTLOrd)) $i++; + else { + if (substr($text,$i,26)=='<span class="starredname">') $i+=25; + else if (substr($text,$i,7)=="</span>") $i+=6; + else { + $byte = substr(trim($text),$i,1); + if (!in_array($byte,$SpecialChar) && !in_array($byte,$SpecialPar) && !in_array($byte,$SpecialNum)) return true; + } + } + } + return false; +} + +/* + * Function to reverse RTL text for proper appearance on charts. + * + * GoogleChart and the GD library don't handle RTL text properly. They assume that all text is LTR. + * This function reverses the input text so that it will appear properly when rendered by GoogleChart + * and by the GD library (the Circle Diagram). + * + * Note 1: Numbers must always be rendered LTR, even when the rest of the text is RTL. + * Note 2: The visual direction of paired characters such as parentheses, brackets, directional + * quotation marks, etc. must be reversed so that the appearance of the RTL text is preserved. + */ +function reverseText($text) { + $UTF8_numbers=WT_UTF8_DIGITS; + $UTF8_brackets=WT_UTF8_PARENTHESES; + + $text = strip_tags(html_entity_decode($text,ENT_COMPAT,'UTF-8')); + $text = str_replace(array('‎', '‏', WT_UTF8_LRM, WT_UTF8_RLM), '', $text); + $textLanguage = utf8_script($text); + if ($textLanguage!='hebrew' && $textLanguage!='arabic') return $text; + + $reversedText = ''; + $numbers = ''; + while ($text!='') { + $charLen = 1; + $letter = substr($text, 0, 1); + if ((ord($letter) & 0xE0) == 0xC0) $charLen = 2; // 2-byte sequence + if ((ord($letter) & 0xF0) == 0xE0) $charLen = 3; // 3-byte sequence + if ((ord($letter) & 0xF8) == 0xF0) $charLen = 4; // 4-byte sequence + + $letter = substr($text, 0, $charLen); + $text = substr($text, $charLen); + if (in_array($letter, $UTF8_numbers)) $numbers .= $letter; // accumulate numbers in LTR mode + else { + $reversedText = $numbers.$reversedText; // emit any waiting LTR numbers now + $numbers = ''; + if (isset($UTF8_brackets[$letter])) $reversedText = $UTF8_brackets[$letter].$reversedText; + else $reversedText = $letter.$reversedText; + } + } + + $reversedText = $numbers.$reversedText; // emit any waiting LTR numbers now + return $reversedText; +} + +?> |
