',"\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, "‎", "‏", "‎", "‏"), "", $inputText); } /** * This function encapsulates all texts in the input with and * 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 (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 '
Input ', $spanNumber, ':'; DumpString($inputText);} $workingText = str_replace("\n", '
', $inputText); $workingText = str_replace(array('
', '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 = ''; // This will become '' at the end $endLTR = ''; // This will become '' at the end $startRTL = ''; // This will become '' at the end $endRTL = ''; // This will become '
' 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 == '' 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 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 'Interim Output:'; 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
in LTR text while ($previousState != 'RTL') { if (strpos($result, ' ') !== false) { $result = str_replace(' ', '', $result); continue; } if (strpos($result, ' ') !== false) { $result = str_replace(' ', '', $result); continue; } if (strpos($result, '
') !== false) { $result = str_replace('
', '
', $result); continue; } if (strpos($result, ' 
') !== false) { $result = str_replace(' 
', '
', $result); continue; } break; // Neither space nor   : we're done } // Trim trailing blanks preceding
in RTL text while (true) { if (strpos($result, ' ') !== false) { $result = str_replace(' ', '', $result); continue; } if (strpos($result, ' ') !== false) { $result = str_replace(' ', '', $result); continue; } break; // Neither space nor   : we're done } // Convert '' and '', ''), array($endLTR.'
'.$startLTR, $endRTL.'
'.$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) != '
') { $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 '', '', '', and '' switch ($direction) { case 'BOTH': case 'both': // LTR text: text // RTL text: text $sLTR = ''.$nothing; $eLTR = $nothing.''; $sRTL = ''.$nothing; $eRTL = $nothing.''; break; case 'LTR': case 'ltr': // LTR text: text // RTL text: text $sLTR = ''.$nothing; $eLTR = $nothing.''; $sRTL = ''; $eRTL = ''; break; case 'RTL': case 'rtl': default: // LTR text: text // RTL text: text $sLTR = ''; $eLTR = ''; $sRTL = ''.$nothing; $eRTL = $nothing.''; break; } $result = str_replace(array($startLTR, $endLTR, $startRTL, $endRTL), array($sLTR, $eLTR, $sRTL, $eRTL), $result); if ($debug) {echo 'Final Output:'; DumpString($result);} return $result; } /* * Wrap words that have an asterisk suffix in and 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 and 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 . '' . $wordText . '' . $trailingText; } $textSpan = preg_replace('~(.*)~', '\1', $textSpan); // The   is a work-around for a TCPDF bug eating blanks. $textSpan = str_replace(array(' ', ' '), array(' ', ' '), $textSpan); } else { // Text and page directions differ: remove the and $textSpan = preg_replace('~(.*)\*~', '\1', $textSpan); $textSpan = preg_replace('~(.*)~', '\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
into current span */ function breakCurrentSpan(&$result) { global $currentState, $waitingText; // Interrupt the current span, insert that
, 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
, 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) == '') { $trailingBreaks = '
' . $trailingBreaks; // Plain
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 and tags while (true) { // Remove blanks that precede if (strpos($textSpan, ' ') !== false) { $textSpan = str_replace(' ', '', $textSpan); continue; } if (strpos($textSpan, ' ') !== false) { $textSpan = str_replace(' ', '', $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
, 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) == '') { $trailingBreaks = '
' . $trailingBreaks; // Plain
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 and relocate it to the front of the string $posDashString = strpos($textSpan, ' - '); if ($posDashString === false) break; $posStringStart = strrpos(substr($textSpan, 0, $posDashString), ''); if ($posStringStart === false) $posStringStart = 0; else $posStringStart += 9; // Point to the first char following the last $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), ''); if ($posStringStart === false) $posStringStart = 0; else $posStringStart += 9; // Point to the first char following the last $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 and tags $result = $result . $textSpan . $endRTL; } if ($currentState != 'LTR' && $currentState != 'RTL') { $result = $result . $textSpan; } $result .= $trailingBreaks; // Get rid of any waiting
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; $i0) { $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') $i+=25; else if (substr($text,$i,7)=="
") $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) { $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 (strpos(WT_UTF8_DIGITS, $letter)!==false) { $numbers .= $letter; // accumulate numbers in LTR mode } else { $reversedText = $numbers.$reversedText; // emit any waiting LTR numbers now $numbers = ''; if (strpos(WT_UTF8_PARENTHESES1, $letter)!==false) { $reversedText = substr(WT_UTF8_PARENTHESES2, strpos(WT_UTF8_PARENTHESES1, $letter), strlen($letter)).$reversedText; } else { $reversedText = $letter.$reversedText; } } } $reversedText = $numbers.$reversedText; // emit any waiting LTR numbers now return $reversedText; }