Exemplo n.º 1
0
 public function parse()
 {
     parent::parse();
     // В файле обязан быть поток Current User.
     $cuStreamID = $this->getStreamIdByName("Current User");
     if ($cuStreamID === false) {
         return false;
     }
     // Получаем этот поток, проверяем хеш (а перед нами ли PowerPoint-презентация?)
     // и читаем смещение до первой структуры UserEditAtom
     $cuStream = $this->getStreamById($cuStreamID);
     if ($this->getLong(12, $cuStream) == 4090610911.0) {
         return false;
     }
     $offsetToCurrentEdit = $this->getLong(16, $cuStream);
     // Находим в файле поток PowerPoint Document.
     $ppdStreamID = $this->getStreamIdByName("PowerPoint Document");
     if ($ppdStreamID === false) {
         return false;
     }
     $ppdStream = $this->getStreamById($ppdStreamID);
     // В нём начинаем искать все UserEditAtom'ы, которые требуются нам для получения
     // смещений до PersistDirectory.
     $offsetLastEdit = $offsetToCurrentEdit;
     $persistDirEntry = array();
     $live = null;
     $offsetPersistDirectory = array();
     do {
         $userEditAtom = $this->getRecord($ppdStream, $offsetLastEdit, 0xff5);
         $live =& $userEditAtom;
         array_unshift($offsetPersistDirectory, $this->getLong(12, $userEditAtom));
         $offsetLastEdit = $this->getLong(8, $userEditAtom);
     } while ($offsetLastEdit != 0x0);
     // Перебираем все полученные смещения. До этого здесь была *серьёзная* ошибка.
     for ($j = 0; $j < count($offsetPersistDirectory); $j++) {
         $rgPersistDirEntry = $this->getRecord($ppdStream, $offsetPersistDirectory[$j], 0x1772);
         if ($rgPersistDirEntry === false) {
             return false;
         }
         // Теперь читаем по четыре байта: первые 20 бит - это начальный ID вхождения в PersistDirectory,
         // следующие 12 - количество последующих смещений.
         for ($k = 0; $k < strlen($rgPersistDirEntry);) {
             $persist = $this->getLong($k, $rgPersistDirEntry);
             $persistId = $persist & 0xfffff;
             $cPersist = ($persist & 4293918720.0) >> 20 & 0xfff;
             $k += 4;
             // Заполняем массив PersistDirectory, исходя из полученных данных.
             for ($i = 0; $i < $cPersist; $i++) {
                 $offset = $this->getLong($k + $i * 4, $rgPersistDirEntry);
                 $persistDirEntry[$persistId + $i] = $this->getLong($k + $i * 4, $rgPersistDirEntry);
             }
             $k += $cPersist * 4;
         }
     }
     // В последней прочитанной записи ищем ID вхождения с DocumentContainer'ом.
     $docPersistIdRef = $this->getLong(16, $live);
     $documentContainer = $this->getRecord($ppdStream, $persistDirEntry[$docPersistIdRef], 0x3e8);
     // Теперь нам нужно пропустить много мусора до SlideList'а.
     $offset = 40 + 8;
     $exObjList = $this->getRecord($documentContainer, $offset, 0x409);
     if ($exObjList) {
         $offset += strlen($exObjList) + 8;
     }
     $documentTextInfo = $this->getRecord($documentContainer, $offset, 0x3f2);
     $offset += strlen($documentTextInfo) + 8;
     $soundCollection = $this->getRecord($documentContainer, $offset, 0x7e4);
     if ($soundCollection) {
         $offset += strlen($soundCollection) + 8;
     }
     $drawingGroup = $this->getRecord($documentContainer, $offset, 0x40b);
     $offset += strlen($drawingGroup) + 8;
     $masterList = $this->getRecord($documentContainer, $offset, 0xff0);
     $offset += strlen($masterList) + 8;
     $docInfoList = $this->getRecord($documentContainer, $offset, 0x7d0);
     if ($docInfoList) {
         $offset += strlen($docInfoList) + 8;
     }
     $slideHF = $this->getRecord($documentContainer, $offset, 0xfd9);
     if ($slideHF) {
         $offset += strlen($slideHF) + 8;
     }
     $notesHF = $this->getRecord($documentContainer, $offset, 0xfd9);
     if ($notesHF) {
         $offset += strlen($notesHF) + 8;
     }
     // Избавляемся от прочитанного мусора.
     unset($exObjList, $documentTextInfo, $soundCollection, $drawingGroup, $masterList, $docInfoList, $slideHF, $notesHF);
     // Читаем структуру SlideList.
     $slideList = $this->getRecord($documentContainer, $offset, 0xff0);
     $out = "";
     for ($i = 0; $i < strlen($slideList);) {
         // Читаем текущий блок и определяем, что нам делать по его типу.
         $block = $this->getRecord($slideList, $i);
         switch ($this->getRecordType($slideList, $i)) {
             case 0x3f3:
                 # RT_SlidePersistAtom
                 // Вариант худший, если перед нами указатель на слайд, тогда мы должны
                 // обратиться к PersistDirectory для получения этого слайда.
                 $pid = $this->getLong(0, $block);
                 $slide = $this->getRecord($ppdStream, @$persistDirEntry[$pid], 0x3ee);
                 // Опять пропускаем всякое-разное до структуры Drawing.
                 $offset = 32;
                 $slideShowSlideInfoAtom = $this->getRecord($slide, $offset, 0x3f9);
                 if ($slideShowSlideInfoAtom) {
                     $offset += strlen($slideShowSlideInfoAtom) + 8;
                 }
                 $perSlideHFContainer = $this->getRecord($slide, $offset, 0xfd9);
                 if ($perSlideHFContainer) {
                     $offset += strlen($perSlideHFContainer) + 8;
                 }
                 $rtSlideSyncInfo12 = $this->getRecord($slide, $offset, 0x3714);
                 if ($rtSlideSyncInfo12) {
                     $offset += strlen($rtSlideSyncInfo12) + 8;
                 }
                 // Drawing - это объект MS Drawing, который имеет подобную PPT заголовочную структуру.
                 // Чтобы не разбирать все возможные вложения структур одна в другую, поищем текст напрямую.
                 $drawing = $this->getRecord($slide, $offset, 0x40c);
                 $from = 0;
                 while (preg_match("#(�|�)#", $drawing, $pocket, PREG_OFFSET_CAPTURE, $from)) {
                     $pocket = @$pocket[1];
                     // Обязательно проверим, что заголовок блока начинается с двух "нулей", иначе возможно мы
                     // нашли что-то в середине других данных.
                     if (substr($drawing, $pocket[1] - 2, 2) == "") {
                         // Читаем либо Plain текст, либо Unicode.
                         if (ord($pocket[0]) == 0xa8) {
                             $out .= htmlspecialchars($this->getRecord($drawing, $pocket[1] - 2, 0xfa8)) . " ";
                         } else {
                             $out .= $this->unicode_to_utf8($this->getRecord($drawing, $pocket[1] - 2, 0xfa0)) . " ";
                         }
                     }
                     // Ищем следующее вхождение
                     $from = $pocket[1] + 2;
                 }
                 break;
             case 0xfa0:
                 # RT_TextCharsAtom
                 // Варианты по проще: мы нашли Unicode-символьное вхождение
                 $out .= $this->unicode_to_utf8($block) . " ";
                 break;
             case 0xfa8:
                 # RT_TextBytesAtom
                 // Или обычный читый текст.
                 $out .= htmlspecialchars($block) . " ";
                 break;
                 # some other skipped
         }
         // Сдвигаемся на длину блока с заголовком.
         $i += strlen($block) + 8;
     }
     // Возвращаем UTF-8 текст.
     return html_entity_decode(iconv("windows-1251", "utf-8", $out), ENT_QUOTES, "UTF-8");
 }
Exemplo n.º 2
0
 public function parse()
 {
     parent::parse();
     // Для чтения DOC'а нам нужны два потока - WordDocument и 0Table или
     // 1Table в зависимости от ситуации. Для начала найдћм первый - в нћм
     // (потоке) разбросаны кусочки текста, которые нам нужной поймать.
     $wdStreamID = $this->getStreamIdByName("WordDocument");
     if ($wdStreamID === false) {
         return false;
     }
     // Поток нашли, читаем его в переменную
     $wdStream = $this->getStreamById($wdStreamID);
     // Далее нам нужно получить кое-что из FIB - специальный блок под названием
     // File Information Block в начале потока WordDocument.
     $bytes = $this->getShort(0xa, $wdStream);
     // Считываем какую именно таблицу нам нужно будет читать - первую или нулевую.
     // Для этого прочитаем один маленький бит из заголовка по известному смещению.
     $fWhichTblStm = ($bytes & 0x200) == 0x200;
     // Теперь нам нужно узнать позицию CLX в табличном потоке. Ну и размер этого самого
     // CLX - пусть ему пусто будет.
     $fcClx = $this->getLong(0x1a2, $wdStream);
     $lcbClx = $this->getLong(0x1a6, $wdStream);
     // Читаем несколько значений, чтобы отделить позиции от размерности в clx
     $ccpText = $this->getLong(0x4c, $wdStream);
     $ccpFtn = $this->getLong(0x50, $wdStream);
     $ccpHdd = $this->getLong(0x54, $wdStream);
     $ccpMcr = $this->getLong(0x58, $wdStream);
     $ccpAtn = $this->getLong(0x5c, $wdStream);
     $ccpEdn = $this->getLong(0x60, $wdStream);
     $ccpTxbx = $this->getLong(0x64, $wdStream);
     $ccpHdrTxbx = $this->getLong(0x68, $wdStream);
     // С помощью вышенайденных значений, находим значение последнего CP - character position
     $lastCP = $ccpFtn + $ccpHdd + $ccpMcr + $ccpAtn + $ccpEdn + $ccpTxbx + $ccpHdrTxbx;
     $lastCP += ($lastCP != 0) + $ccpText;
     // Находим в файле нужную нам табличку.
     $tStreamID = $this->getStreamIdByName(intval($fWhichTblStm) . "Table");
     if ($tStreamID === false) {
         return false;
     }
     // И считываем из нећ поток в переменную
     $tStream = $this->getStreamById($tStreamID);
     // Потом находим в потоке CLX
     $clx = substr($tStream, $fcClx, $lcbClx);
     // А теперь нам в CLX (complex, ага) нужно найти кусок со смещениями и размерностями
     // кусочков текста.
     $lcbPieceTable = 0;
     $pieceTable = "";
     // Отмечу, что здесь вааааааааааще жопа. В документации на сайте толком не сказано
     // сколько гона может быть до pieceTable в этом CLX, поэтому будем исходить из тупого
     // перебора - ищем возможное начало pieceTable (обязательно начинается на 0х02), затем
     // читаем следующие 4 байта - размерность pieceTable. Если размерность по факту и
     // размерность, записанная по смещению, то бинго! мы нашли нашу pieceTable. Нет?
     // ищем дальше.
     $from = 0;
     // Ищем 0х02 с текущего смещения в CLX
     while (($i = strpos($clx, chr(0x2), $from)) !== false) {
         // Находим размер pieceTable
         $lcbPieceTable = $this->getLong($i + 1, $clx);
         // Находим pieceTable
         $pieceTable = substr($clx, $i + 5);
         // Если размер фактический отличается от нужного, то это не то -
         // едем дальше.
         if (strlen($pieceTable) != $lcbPieceTable) {
             $from = $i + 1;
             continue;
         }
         // Хотя нет - вроде нашли, break, товарищи!
         break;
     }
     // Теперь заполняем массив character positions, пока не наткнћмся
     // на последний CP.
     $cp = array();
     $i = 0;
     while (($cp[] = $this->getLong($i, $pieceTable)) != $lastCP) {
         $i += 4;
     }
     // Остаток идћт на PCD (piece descriptors)
     $pcd = str_split(substr($pieceTable, $i + 4), 8);
     $text = "";
     // Ура! мы подошли к главному - чтение текста из файла.
     // Идћм по декскрипторам кусочков
     for ($i = 0; $i < count($pcd); $i++) {
         // Получаем слово со смещением и флагом компрессии
         $fcValue = $this->getLong(2, $pcd[$i]);
         // Смотрим - что перед нами тупой ANSI или Unicode
         $isANSI = ($fcValue & 0x40000000) == 0x40000000;
         // Остальное без макушки идћт на смещение
         $fc = $fcValue & 0x3fffffff;
         // Получаем длину кусочка текста
         $lcb = $cp[$i + 1] - $cp[$i];
         // Если перед нами Unicode, то мы должны прочитать в два раза больше файлов
         if (!$isANSI) {
             $lcb *= 2;
         } else {
             $fc /= 2;
         }
         // Читаем кусок с учћтом смещения и размера из WordDocument-потока
         $part = substr($wdStream, $fc, $lcb);
         // Если перед нами Unicode, то преобразовываем его в нормальное состояние
         if (!$isANSI) {
             $part = $this->unicode_to_utf8($part);
         }
         // Добавляем кусочек к общему тексту
         $text .= $part;
     }
     // Удаляем из файла вхождения с внедрћнными объектами
     $text = preg_replace("/HYPER13 *(INCLUDEPICTURE|HTMLCONTROL)(.*)HYPER15/iU", "", $text);
     $text = preg_replace("/HYPER13(.*)HYPER14(.*)HYPER15/iU", "\$2", $text);
     // Возвращаем результат
     return $text;
 }
Exemplo n.º 3
0
 public function parse()
 {
     parent::parse();
     $wdStreamID = $this->getStreamIdByName("WordDocument");
     if ($wdStreamID === false) {
         return false;
     }
     $wdStream = $this->getStreamById($wdStreamID);
     $bytes = $this->getShort(0xa, $wdStream);
     $fWhichTblStm = ($bytes & 0x200) == 0x200;
     $fcClx = $this->getLong(0x1a2, $wdStream);
     $lcbClx = $this->getLong(0x1a6, $wdStream);
     $ccpText = $this->getLong(0x4c, $wdStream);
     $ccpFtn = $this->getLong(0x50, $wdStream);
     $ccpHdd = $this->getLong(0x54, $wdStream);
     $ccpMcr = $this->getLong(0x58, $wdStream);
     $ccpAtn = $this->getLong(0x5c, $wdStream);
     $ccpEdn = $this->getLong(0x60, $wdStream);
     $ccpTxbx = $this->getLong(0x64, $wdStream);
     $ccpHdrTxbx = $this->getLong(0x68, $wdStream);
     $lastCP = $ccpFtn + $ccpHdd + $ccpMcr + $ccpAtn + $ccpEdn + $ccpTxbx + $ccpHdrTxbx;
     $lastCP += ($lastCP != 0) + $ccpText;
     $tStreamID = $this->getStreamIdByName(intval($fWhichTblStm) . "Table");
     if ($tStreamID === false) {
         return false;
     }
     $tStream = $this->getStreamById($tStreamID);
     $clx = substr($tStream, $fcClx, $lcbClx);
     $lcbPieceTable = 0;
     $pieceTable = "";
     $from = 0;
     while (($i = strpos($clx, chr(0x2), $from)) !== false) {
         $lcbPieceTable = $this->getLong($i + 1, $clx);
         $pieceTable = substr($clx, $i + 5);
         if (strlen($pieceTable) != $lcbPieceTable) {
             $from = $i + 1;
             continue;
         }
         break;
     }
     $cp = array();
     $i = 0;
     while (($cp[] = $this->getLong($i, $pieceTable)) != $lastCP) {
         $i += 4;
     }
     $pcd = str_split(substr($pieceTable, $i + 4), 8);
     $text = "";
     for ($i = 0; $i < count($pcd); $i++) {
         $fcValue = $this->getLong(2, $pcd[$i]);
         $isANSI = ($fcValue & 0x40000000) == 0x40000000;
         $fc = $fcValue & 0x3fffffff;
         $lcb = $cp[$i + 1] - $cp[$i];
         if (!$isANSI) {
             $lcb *= 2;
         } else {
             $fc /= 2;
         }
         $part = substr($wdStream, $fc, $lcb);
         if (!$isANSI) {
             $part = $this->unicode_to_utf8($part);
         }
         $text .= $part;
     }
     $text = preg_replace("/HYPER13 *(INCLUDEPICTURE|HTMLCONTROL)(.*)HYPER15/iU", "", $text);
     $text = preg_replace("/HYPER13(.*)HYPER14(.*)HYPER15/iU", "\$2", $text);
     return $text;
 }