
\), группа обёрнута в фигурные скобки ({ и }).a до z) и может быть завершено численным параметром (возможно отрицательным). Как вариант, слово может содержать один не цифро-буквенный ascii-символ. Всё, что не подпадает под эти правила, не является частью управляющего слова. Таким образом, последовательность вида \rtf1\ansi\ansicpg1251 без проблем делится на три слова rtf с параметром 1 (major-версия формата), ansi (текущая кодировка) и ansicpg с параметром 1251 (текущая кодовая страница под номером 1251 — т.е. Windows-1251).This is {\b bold} text, This is \b bold \b0 text = This is <b>bold</b> text.\ansicpg), конечно же. Для этого в RTF была введена последовательность вида \'hh, где hh — это двоичный hex-код символа из таблицы ASCII.\uABCD с цифровым параметром ABCD. ABCD в данном случае код unicode-символа в десятичной системе счисления. Всё опять просто, как вы могли заметить. \ucN, которое тесно связано с Unicode. Дело в том, что формат RTF очень рьяно поддерживает совместимость со старыми устройствами, на которых возможно придётся открывать данный файл. Как вариант, подобное устройство (ну например компьютер с Windows 3.11 :) не сможет прочитать Unicode, что ему делать? Для этого после каждого unicode-символа, шифрованного ключевым словом \u может быть указано от нуля до нескольких символов, которые должны быть отображены в случае, если rtf-viewer не способен отобразить или разобрать текущие данные (по документации, если просмотрщик не может отобразить верно данные, он должен их пропустить).Lab\u915GValue. Зададимся вопросом — сколько символов требуется отобразить, если нет возможности показать Unicode. Всё опять же не очень сложно — указанное выше ключевое слово \ucN в качестве параметра N как раз и предоставляет это значение. Т.е. перед Unicode-данными обязательно появится что-то типа \uc1, что скажет нам пропустить один символ после unicode'а.Код с комментариями вы можете получить на GitHub'е.
- function rtf_isPlainText($s) {
- $failAt = array("*", "fonttbl", "colortbl", "datastore", "themedata");
- for ($i = 0; $i < count($failAt); $i++)
- if (!empty($s[$failAt[$i]])) return false;
- return true;
- }
- function rtf2text($filename) {
- $text = file_get_contents($filename);
- if (!strlen($text))
- return "";
- $document = "";
- $stack = array();
- $j = -1;
- for ($i = 0; $i < strlen($text); $i++) {
- $c = $text[$i];
- switch ($c) {
- case "\\":
- $nc = $text[$i + 1];
- if ($nc == '\\' && rtf_isPlainText($stack[$j])) $document .= '\\';
- elseif ($nc == '~' && rtf_isPlainText($stack[$j])) $document .= ' ';
- elseif ($nc == '_' && rtf_isPlainText($stack[$j])) $document .= '-';
- elseif ($nc == '*') $stack[$j]["*"] = true;
- elseif ($nc == "'") {
- $hex = substr($text, $i + 2, 2);
- if (rtf_isPlainText($stack[$j]))
- $document .= html_entity_decode("&#".hexdec($hex).";");
- $i += 2;
- } elseif ($nc >= 'a' && $nc <= 'z' || $nc >= 'A' && $nc <= 'Z') {
- $word = "";
- $param = null;
- for ($k = $i + 1, $m = 0; $k < strlen($text); $k++, $m++) {
- $nc = $text[$k];
- if ($nc >= 'a' && $nc <= 'z' || $nc >= 'A' && $nc <= 'Z') {
- if (empty($param))
- $word .= $nc;
- else
- break;
- } elseif ($nc >= '0' && $nc <= '9')
- $param .= $nc;
- elseif ($nc == '-') {
- if (empty($param))
- $param .= $nc;
- else
- break;
- } else
- break;
- }
- $i += $m - 1;
- $toText = "";
- switch (strtolower($word)) {
- case "u":
- $toText .= html_entity_decode("&#x".dechex($param).";");
- $ucDelta = @$stack[$j]["uc"];
- if ($ucDelta > 0)
- $i += $ucDelta;
- break;
- case "par": case "page": case "column": case "line": case "lbr":
- $toText .= "\n";
- break;
- case "emspace": case "enspace": case "qmspace":
- $toText .= " ";
- break;
- case "tab": $toText .= "\t"; break;
- case "chdate": $toText .= date("m.d.Y"); break;
- case "chdpl": $toText .= date("l, j F Y"); break;
- case "chdpa": $toText .= date("D, j M Y"); break;
- case "chtime": $toText .= date("H:i:s"); break;
- case "emdash": $toText .= html_entity_decode("—"); break;
- case "endash": $toText .= html_entity_decode("–"); break;
- case "bullet": $toText .= html_entity_decode("•"); break;
- case "lquote": $toText .= html_entity_decode("‘"); break;
- case "rquote": $toText .= html_entity_decode("’"); break;
- case "ldblquote": $toText .= html_entity_decode("«"); break;
- case "rdblquote": $toText .= html_entity_decode("»"); break;
- default:
- $stack[$j][strtolower($word)] = empty($param) ? true : $param;
- break;
- }
- if (rtf_isPlainText($stack[$j]))
- $document .= $toText;
- }
- $i++;
- break;
- case "{":
- array_push($stack, $stack[$j++]);
- break;
- case "}":
- array_pop($stack);
- $j--;
- break;
- case '\0': case '\r': case '\f': case '\n': break;
- default:
- if (rtf_isPlainText($stack[$j]))
- $document .= $c;
- break;
- }
- }
- return $document;
- }
\*). Во-вторых же, стоит ещё распарсить кодировку и кодовую страницу, для того чтобы вернее отобразить ключевые слова вида \'hh.Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.
комментарии (41)