Advertisement
Guest User

PDFparser

a guest
Mar 26th, 2012
2,176
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 24.10 KB | None | 0 0
  1. <?php
  2. namespace PDF;
  3. /**
  4.  * Класс разбирающий PHP на страници, картинки в них и наборы строк
  5.  *
  6.  */
  7. class PDFEngine implements \PageFormatHandler{
  8.     private $_PDFVersion='';
  9.     /**
  10.      * Путь к PDF файлу
  11.      * @var null
  12.      */
  13.     private $filePath=NULL;
  14.     /**
  15.      * Указатель на открытый файл
  16.      * @var null|resource
  17.      */
  18.     public $filePointer=NULL;
  19.     /**
  20.      * Размер файла
  21.      * @var int
  22.      */
  23.     private $fileSize = 0;
  24.     /**
  25.      * Номер текущей страницы
  26.      * @var int
  27.      */
  28.     private $currentPage=0;
  29.     /**
  30.      * Таблица перекрестных ссылок.<br />
  31.      * Ключ массива -- Номер PDF Объекта <br />
  32.      * Значение -- Адрес началала объекта в файле. Массив отсортирован по значению.
  33.      * @var array
  34.      */
  35.     public $RefTable=array();
  36.     /**
  37.      * Костыль для Таблицы перекрестных ссылок. <br />
  38.      * Ключ массива -- Номер PDF Объекта <br />
  39.      * Значение -- Адрес конца объекта в файле.<br />
  40.      * Костыль сделан потому, что PHP не позволяет узнать следующий элемент массива иначе, как пробегая каждый раз
  41.      * от первого элемента до искомого, а так как это была бы слишком частая операция, было решено пожертвовать
  42.      * некоторым количеством памяти в обмен на производительность
  43.      * @var array
  44.      */
  45.     public $RefTableNext = array();
  46.     /**
  47.      * Таблица страниц<br />
  48.      * Ключ массива -- номер страницы<br />
  49.      * Значение -- номер объекта в PDF соответствующий данной странице
  50.      * @var array
  51.      */
  52.     public $pageTable=array();
  53.     /**
  54.      * В конструкторе Выполняется подключение к файлу, сохраняется его путь и раззмер.
  55.      * Так же в конструкторе создается перекрестная таблица ссылок для быстрого ддоступа к объектам, не загружая
  56.      * при этом весь файл в память. Здесь же создается таблица для бысрого доступа к страницам.
  57.      * @param $file
  58.      */
  59.     public function __construct($file){
  60.         $this->filePath= $file;
  61.         $this->filePointer = fopen($this->filePath, 'rb');
  62.         $this->fileSize = filesize($this->filePath);
  63.         $this->get_ref_table();
  64.         $this->get_page_table();
  65.     }
  66.     /**
  67.      * Просто закрывает за собой файл
  68.      */
  69.     public function __destruct(){
  70.         try{
  71.             fclose($this->filePointer);
  72.         }
  73.         catch(\Exception $e){
  74.             //Ничего не делаем потому, как по видимому файл просто уже закрыт=)
  75.         }
  76.     }
  77.     /**
  78.      * Самая нужная для пользователя функция.
  79.      * Она возвращает данные о запрошенной странице.
  80.      * //ToDo: Или NULL если страницы закончились
  81.      * @param int $pageNum
  82.      */
  83.     public function get_page($pageNum = 1){
  84.         if(isset($this->pageTable[$pageNum-1])){
  85.             $pageStart = $this->RefTable[$this->pageTable[$pageNum-1]];
  86.             $pageEnd =$this->RefTableNext[$this->pageTable[$pageNum-1]];
  87.             $page = new Page($pageStart,$pageEnd, $this);
  88.             $page->get_text();
  89.             $page->convert_to_paragraph();
  90.             return $page;
  91.         }
  92.         else{
  93.         return NULL;
  94.         }
  95.     }
  96.     /**
  97.      * <b>Функция перенесена в класс абстрактного Объекта ПДФ</b>
  98.      * Функция парсит объект PDF типа stram. Возвращает его содержимое
  99.      * @param $objectID
  100.      * @return string
  101.      * @depricated
  102.      */
  103.     protected function get_stream_content($objectID){
  104.         $object = $this->get_obj_by_key($objectID);
  105.         preg_match('/\/Filter\[?\s?(.*)\]?\W/', $object, $matches);
  106.         preg_match('/(\s?\/(\w+))+/',$matches[1],$filter);
  107.         preg_match('/stream(.*)endstream/ismU',$object, $streamcontent);
  108.         $stream = trim($streamcontent[1]);
  109.         if($filter[1]='FlateDecode'){
  110.             return @gzuncompress($stream);
  111.  
  112.         }
  113.         else return $stream;
  114.     }
  115.     /**
  116.      * Функция извлекает таблицу объектов содержащих страницы. Результаты раскидывает по свойствам объекта
  117.      * @return bool
  118.      */
  119.     private function  get_page_table(){
  120.         $currentObj = '';
  121.  
  122.         reset($this->RefTable);
  123.         $key = key($this->RefTable);
  124.         $matches = NULL;
  125.         $nextPage = NULL;
  126.         $pages = array();
  127.         while((preg_match('/\x2F\x54\x79\x70\x65\x2F\x43\x61\x74\x61\x6C\x6F\x67/',$currentObj) != 1) ||
  128.             ($currentObj === false)
  129.         ){
  130.  
  131.             $currentObj = $this->get_obj_by_key($key);
  132.             next($this->RefTable);
  133.             $key = key($this->RefTable);
  134.         }
  135.         preg_match('/\x2F\x50\x61\x67\x65\x73\x20(\d+)\x20\x30\x20\x52/', $currentObj, $matches);
  136.         $currentObj = $this->get_obj_by_key($matches[1]);
  137.         preg_match('/\x2FKids\[(.*)\]/',$currentObj, $kids);
  138.         preg_match_all('/\s?(\d+)\s\d+\sR/',$kids[1], $matches);
  139.         foreach($matches[1] as $value){
  140.             $pages[] = $value;
  141.         }
  142.         foreach($pages as $key => $value){
  143.             if(isset($this->RefTable[$value])){
  144.                 $page=$this->get_obj_by_key($value);
  145.                 if(preg_match('/\/Type\/Page\W/',$page) == 1){
  146.                     $this->pageTable[] = $value;
  147.                 }
  148.                 $this->pageTable = array_merge($this->pageTable,$this->getChildren($value));
  149.             }
  150.         }
  151.         return true;
  152.     }
  153.     /**
  154.      * Получить все дочерние страницы.
  155.      * @param $Obj
  156.      * @return array
  157.      */
  158.     private function getChildren($Obj){
  159.         $pages = array();
  160.         $pagesArr = array();
  161.         $currentObj = $this->get_obj_by_key($Obj);
  162.         preg_match('/\x2FKids\[(.*)\]/',$currentObj, $kids);
  163.         if(isset($kids[1])){
  164.             if(preg_match_all('/\s?(\d+)\s\d+\sR/',$kids[1], $matches) > 0){
  165.                 foreach($matches[1] as $value){
  166.                     $pages[] = $value;
  167.                 }
  168.                 foreach($pages as $key => $value){
  169.                     if(isset($this->RefTable[$value])){
  170.                         $page=$this->get_obj_by_key($value);
  171.                         if(preg_match('/\/Type\/Page\W/',$page) == 1){
  172.                             $pagesArr[] = $value;
  173.                         }
  174.                         $pagesArr = array_merge($pagesArr,$this->getChildren($value));
  175.                     }
  176.                 }
  177.                 return $pagesArr;
  178.             }
  179.             else return array();
  180.         }
  181.         return array();
  182.     }
  183.  
  184.     /**
  185.      * Парсинг ссылочной таблицы. На объекты.
  186.      */
  187.     private function get_ref_table(){
  188.         $currentString = '';
  189.         $matches=NULL;
  190.         $tableLength = 0;
  191.         $lastTable = false;
  192.  
  193.         fseek($this->filePointer, -32, SEEK_END);
  194.         $nextTableLink='';
  195.         while(preg_match('/startxref/', $nextTableLink)!=1 && $nextTableLink!==false){
  196.             $nextTableLink = fgets($this->filePointer);
  197.         }
  198.         $nextTableLink = fgets($this->filePointer)+0;
  199.         while($lastTable!== true){
  200.             fseek($this->filePointer, $nextTableLink, SEEK_SET);
  201.             fgets($this->filePointer);
  202.             $currentString = fgets($this->filePointer);
  203.             preg_match('/(\d+)\x20(\d+)/', $currentString, $matches);
  204.             $tableLength = $matches[2];
  205.             $startIndex = $matches[1];
  206.             for($i=0; $i<$tableLength; $i++){
  207.                 $currentString = fgets($this->filePointer);
  208.                 preg_match('/(\d+)\x20\d+\x20\x6E/', $currentString, $matches);
  209.                 if(isset($matches[1]))
  210.                     $this->RefTable[$startIndex+$i]=$matches[1];
  211.             }
  212.             fgets($this->filePointer);
  213.             $currentString = fgets($this->filePointer);
  214.             if(preg_match('/\x2FPrev\x20(\d+)/', $currentString, $matches)==1)
  215.                 $nextTableLink = $matches[1]+0;
  216.             else
  217.                 $lastTable = true;
  218.         }
  219.         asort($this->RefTable, SORT_NUMERIC);
  220.         reset($this->RefTable);
  221.         $pointerKey=NULL;
  222.         foreach($this->RefTable as $key => $value){
  223.             if($pointerKey!=NULL)
  224.                 $this->RefTableNext[$pointerKey]=&$this->RefTable[$key];
  225.             $pointerKey = $key;
  226.         }
  227.         if($pointerKey!=NULL)
  228.             $this->RefTableNext[$pointerKey]= $nextTableLink;
  229.     }
  230.     /**
  231.      * <b>Функция перенесена в класс абсрактного объекта</b>
  232.      * Получение объекта по ключу(По адресу из перекрестной таблицы ссылок)
  233.      * @param $key
  234.      * @return string
  235.      * @depricated
  236.      */
  237.     public function get_obj_by_key($key){
  238.         fseek($this->filePointer, $this->RefTable[$key]);
  239.         return fread($this->filePointer, $this->RefTableNext[$key]-$this->RefTable[$key]);
  240.     }
  241.     private function get_bin_obj($key){
  242.         return file_get_contents($this->filePath, FILE_BINARY, NULL, $this->RefTable[$key], $this->RefTableNext[$key]-$this->RefTable[$key]);
  243.     }
  244.  
  245. }
  246. /**
  247.  * Это не Абстрактный класс объекта, а класс Абстрактного объекта.<br />
  248.  * В него включены методы для более удобной работы с объектами.<br />
  249.  * Все ыажные данные объекта должны храниться в массиве data[]. например: <br />data['ParmName']=value<br />
  250.  * К таким "переменным" можно получить доступ извне только для чтения благодаря магическому __GET()<br />
  251.  * $object->ParamName;<br />
  252.  *
  253.  */
  254. class AbstractPDFObject{
  255.     protected $properties = array();
  256.     protected $data = array();
  257.     protected $resources;
  258.  
  259.     protected $parentPDFEngine = NULL;
  260.  
  261.     public function __construct($pageStart, $PageEnd, &$PDFEngine){
  262.         if(!isset($this->parentPDFEngine))
  263.             $this->parentPDFEngine = $PDFEngine;
  264.         $this->parse_properties($pageStart,$PageEnd,$this->parentPDFEngine->filePointer);
  265.     }
  266.     /**
  267.      * получить элемент массива data[]
  268.      * @param $paramType
  269.      * @return null
  270.      */
  271.     public function get_data($paramType){
  272.         if(isset($this->data[$paramType])){
  273.             return $this->data[$paramType];
  274.         }
  275.         else return NULL;
  276.     }
  277.     /**
  278.      * Магический $__get возвращает элемент из data
  279.      * @param $name
  280.      * @return null
  281.      */
  282.     public function __get($name){
  283.         return $this->get_data($name);
  284.     }
  285.     /**
  286.      * Устанавливает значение в массив data
  287.      * @param $paramType
  288.      * @param $paramValue
  289.      * @return AbstractPDFObject
  290.      */
  291.     public function set_data($paramType, $paramValue){
  292.         if(!is_scalar($paramType) && !is_scalar($paramValue)){
  293.             foreach($paramType as $key => $name){
  294.                 $this->data[$name] = $paramValue[$key];
  295.             }
  296.         }
  297.         elseif(is_scalar($paramType) && is_scalar($paramValue))
  298.             $this->data[$paramType] = $paramValue;
  299.         return $this;
  300.     }
  301.     /**
  302.      * Устанавливает значение в массив propertis
  303.      * @param $paramType
  304.      * @param $paramValue
  305.      * @return AbstractPDFObject
  306.      */
  307.     public function set_property($paramType, $paramValue){
  308.         if(!is_scalar($paramType) && !is_scalar($paramValue)){
  309.             foreach($paramType as $key => $name){
  310.                 $this->properties[$name] = $paramValue[$key];
  311.             }
  312.         }
  313.         elseif(is_scalar($paramType) && is_scalar($paramValue))
  314.             $this->properties[$paramType] = $paramValue;
  315.         return $this;
  316.     }
  317.     /**
  318.      * Получить элемент из массива propertis
  319.      * @param $paramType
  320.      * @return null
  321.      */
  322.     public function get_property($paramType){
  323.         if(isset($this->properties[$paramType])){
  324.             return $this->properties[$paramType];
  325.         }
  326.         else return NULL;
  327.     }
  328.  
  329.     /**
  330.      * Функция достает из объекта тест PDF dictionry и сохраняет его в $this->data['propertiesString']
  331.      * Так же эта функция извлекает поток объекта и по возможности декодирует. В любом случае сохраняет его в
  332.      * $this->data['stream']
  333.      * @param $startObj
  334.      * @param $endObj
  335.      */
  336.     protected function parse_properties($startObj, $endObj, $file){
  337.         $this->data['stream'] = '';
  338.         fseek($file, $startObj);
  339.         $fullObjContent = fread($file, $endObj-$startObj);
  340.         preg_match('|<<(.*)>>|', $fullObjContent, $matches);
  341.         if(isset($matches[1]))
  342.             $this->data['propertiesString'] = $matches[1];
  343.         $this->data['stream'] .= $this->decode_stream($fullObjContent);
  344.         preg_match('|/Contents ?\[?(( ?\d+ \d+ R ?)+)\]?|', $this->data['propertiesString'], $matches);
  345.         if(isset($matches[1])){
  346.             $contentsObj=$matches[1];
  347.             preg_match_all('|(\d+) \d+ R|', $contentsObj, $matches);
  348.             foreach($matches[1] as $value){
  349.                 $fullObjContent = $this->parentPDFEngine->get_obj_by_key($value);
  350.                 if(isset($matches[1]))
  351.                     $this->data['stream'] .= $this->decode_stream($fullObjContent);
  352.             }
  353.         }
  354.     }
  355.     protected function parse_resurce(){
  356.         $this->data['resources']='';
  357.         preg_match('|/Resources ?\[?(( ?\d+ \d+ R ?)+)\]?|ims', $this->data['propertiesString'], $matches);
  358.         if(isset($matches[1])){
  359.             $ResourceObj=$matches[1];
  360.             preg_match_all('|(\d+) \d+ R|', $ResourceObj, $matches);
  361.             foreach($matches[1] as $value){
  362.                     $this->data['resources'] .= $this->parentPDFEngine->get_obj_by_key($value);
  363.             }
  364.         }
  365.     }
  366.  
  367.     /**
  368.      *  Декодирует поток. Прежде чем запускать: необходимо текст
  369.      * свойств объекта поместить в $this->data['propertiesString']
  370.      * @param $stream
  371.      * @return string
  372.      */
  373.     protected function decode_stream($objString){
  374.         preg_match('|stream(.*)endstream|ms', $objString, $matches);
  375.         if(isset($matches[1])){
  376.             $stream = trim($matches[1]);
  377.             preg_match('|/Filter ?\[(.*)\]|', $objString, $matches);
  378.             if(isset($matches[1])){
  379.                 $filter = $matches[1];
  380.                 preg_match_all('|(/\w+)|', $filter, $matches);
  381.                 foreach($matches[1] as $value){
  382.                     if($value == '/FlateDecode'){
  383.                         return gzuncompress($stream);
  384.                     }
  385.                 }
  386.             }
  387.             else{
  388.                 preg_match('|/Filter ?/FlateDecode|', $objString, $matches);
  389.                 if(isset($matches[0])){
  390.                     return gzuncompress($stream);
  391.                 }
  392.                 else{
  393.                     return $stream;
  394.                 }
  395.             }
  396.             return $stream;
  397.         }
  398.         return '';
  399.     }
  400. }
  401. /**
  402.  * Объект страницы PDF
  403.  * Частный случай Абстрактного объекта. Извлекает все данные о странице: Текст, шрифты, изображения, оглавления и т д
  404.  */
  405. class Page extends AbstractPDFObject{
  406.     /**
  407.      * Конструктор. При инициализации обзекта находит все ресурсы объекта и парсит их в атомарные для данной абстракции
  408.      * элементы:строки, изображения и т д
  409.      * Получает инстанс PDF обработчика
  410.      * @param $pageNum
  411.      * @param $PDFEngine
  412.      */
  413.     public function __construct($pageStart, $PageEnd, &$PDFEngine){
  414.         $this->parentPDFEngine = $PDFEngine;
  415.         $this->parse_properties($pageStart,$PageEnd,$this->parentPDFEngine->filePointer);
  416.         $this->parse_resurce();
  417.         $this->get_fonts_obj();
  418.     }
  419.     public function get_fonts_obj(){
  420.         preg_match_all('|/Font ?<{0,2}(( ?/[0-9a-z]+ \d+ \d+ R ?)+)>{0,2} ?|i', $this->propertiesString, $fontsString);
  421.         if(!isset($fontsString[1][0])){
  422.             preg_match_all('|/Font ?<{0,2}(( ?/[0-9a-z]+ \d+ \d+ R ?)+)>{0,2} ?|i', $this->data['resources'], $fontsString);
  423.         }
  424.         preg_match_all('| ?(/[0-9a-z]+) (\d+) \d+ R ?|i', $fontsString[1][0], $matches);
  425.         foreach($matches[1] as $key => $value){
  426.             $this->data['Fonts'][$value]['TrnsfrmTable'] = array();
  427.             $this->data['Fonts'][$value]['objString']= $this->parentPDFEngine->get_obj_by_key($matches[2][$key]);
  428.             //Ищем свойство /ToUnicode Если его нет, значит строку можно будет писать "как есть". Если есть, то там будет таблица замещения
  429.             preg_match('|/ToUnicode (\d+) \d+ R|', $this->data['Fonts'][$value]['objString'], $toUnicodeString);
  430.             if(isset($toUnicodeString[1])){
  431.                 //Если /toUnicode существует, то находим соответствующий ему объект и декодируем поток. Получаем данные
  432.                 $this->data['Fonts'][$value]['ToUnicode']= $this->decode_stream($this->parentPDFEngine->get_obj_by_key($toUnicodeString[1]));
  433.                 //извлекаем блоки для замены одиночных символов
  434.                 preg_match_all('|beginbfchar(.*?)endbfchar|ms', $this->data['Fonts'][$value]['ToUnicode'], $SinglCharTrnsfrmBlock);
  435.                 foreach($SinglCharTrnsfrmBlock[1] as $blockData){
  436.                     preg_match_all('|<([0-9a-f]{2,4})> +<([0-9a-f]{4,512})> *$|ims',$blockData, $SinglCharTrnsfrm);
  437.                     foreach($SinglCharTrnsfrm[1] as $tuUnicKey => $toUnicValue){
  438.                         $SinglCharTrnsfrm[1][$tuUnicKey] = strtolower("\x".substr_replace($SinglCharTrnsfrm[1][$tuUnicKey], "\x", 2, 0));
  439.                         $SinglCharTrnsfrm[2][$tuUnicKey] = mb_convert_encoding(chr(hexdec(substr($SinglCharTrnsfrm[2][$tuUnicKey], 0, 2)))
  440.                             .chr(hexdec(substr($SinglCharTrnsfrm[2][$tuUnicKey], -2))), "UTF-8", "UTF-16");
  441.                     }
  442.                     $this->data['Fonts'][$value]['TrnsfrmTable'] = array_merge($this->data['Fonts'][$value]['TrnsfrmTable'],
  443.                                                                                     array_combine($SinglCharTrnsfrm[1],$SinglCharTrnsfrm[2]));
  444.                 }
  445.                 //Извлекаем блоки с диапозонами для замены
  446.                 if(preg_match_all('|beginbfrange(.*?)endbfrange|ims', $this->data['Fonts'][$value]['ToUnicode'], $CharRangeTrnsfrmBlock)){
  447.                     foreach($CharRangeTrnsfrmBlock[1] as  $blockData){
  448.                         //разбираем первый способ записи диапозона (см документацию на PDF ISO_32000_1_2008 страница 294)
  449.                         if(preg_match_all('|<([0-9a-f]{4})>\s+<([0-9a-f]{4})>\s+<([0-9a-f]{4})> *$|imsU', $blockData, $CharRangeTrnsfrm)){
  450.                         foreach($CharRangeTrnsfrm[1] as $key => $CharRange){
  451.                             for($i = hexdec($CharRangeTrnsfrm[1][$key]); $i <= hexdec($CharRangeTrnsfrm[2][$key]); $i++){
  452.                                 $from = "\x".substr_replace(str_pad(dechex($i),4,'0',STR_PAD_LEFT), "\x", 2, 0);
  453.                                 $to = mb_convert_encoding(chr(hexdec(substr(str_pad($CharRangeTrnsfrm[3][$key],4,'0',STR_PAD_LEFT), 0, 2)))
  454.                                                          .chr(hexdec(substr(str_pad($CharRangeTrnsfrm[3][$key],4,'0',STR_PAD_LEFT), -2))), "UTF-8", "UTF-16");
  455.                                 $this->data['Fonts'][$value]['TrnsfrmTable'][strtolower($from)] = $to;
  456.                                 $CharRangeTrnsfrm[3][$key] = str_pad(dechex(hexdec($CharRangeTrnsfrm[3][$key])+1),4,'0',STR_PAD_LEFT);
  457.                             }
  458.                         }
  459.                         }
  460.                         //разбираем второй способ записи диапозона (см документацию на PDF ISO_32000_1_2008 страница 294)
  461.                         if(preg_match_all('|<([0-9a-f]{4})>\s+<([0-9a-f]{4})>\s+\[(.*)\] *$|ismU',$blockData, $CharRangeTrnsfrm)){
  462.                             foreach($CharRangeTrnsfrm[1] as $key => $CharRange){
  463.                                 preg_match_all('|<([0-9a-f]{4})>|i',$CharRangeTrnsfrm[3][$key], $AssignChars);
  464.                                 for($i = hexdec($CharRangeTrnsfrm[1][$key]); $i <= hexdec($CharRangeTrnsfrm[2][$key]); $i++){
  465.                                     $from = "\x".substr_replace(str_pad(dechex($i),4,'0',STR_PAD_LEFT), "\x", 2, 0);
  466.                                     $to = mb_convert_encoding(chr(hexdec(substr(str_pad($AssignChars[1][$key],4,'0',STR_PAD_LEFT), 0, 2)))
  467.                                         .chr(hexdec(substr(str_pad($CharRangeTrnsfrm[3][$key],4,'0',STR_PAD_LEFT), -2))), "UTF-8", "UTF-16");
  468.                                     $this->data['Fonts'][$value]['TrnsfrmTable'][strtolower($from)] = $to;
  469.                                 }
  470.                             }
  471.                         }
  472.                     }
  473.                 }
  474.             }
  475.         }
  476.     }
  477.     public function get_text(){
  478.         $this->data['clearText'] = '';
  479.         $this->data['logicBlocks'] = array();
  480.         $textBlockCount = 0;
  481.         $prevFont = NULL;
  482.         $currentLogicalBlock = NULL;
  483.         //Разбиваем весь поток на PDF-блоки текста
  484.         preg_match_all('|^(.*?)BT(.*?)ET|ms', $this->stream, $dirtyTextBlocks);
  485.         //ToDo: Дальше -- ГОВНОКОД. Подумать и исправить, чтоб работало быстреее.
  486.         foreach($dirtyTextBlocks[2] as $blockNum => $blockString){
  487.             //Получаем Шрифты и PDFмассив блоков текста. $fontAndText[1] -- шрифт, $fontAndText[2] -- набор текстовых блоков
  488.             if(preg_match_all("|([\r\n]{1,2})?((/[0-9a-z]+)\s\d+\.?\d*\sTf)?.*\[(.*)\]|ism", $blockString, $fontAndText)){
  489.                 //Пишем в следующий текстовый блок.
  490.                 $textBlockCount++;
  491.                 //получим чистый массив логических блоков
  492.  
  493.                 if(isset($dirtyTextBlocks[1][$blockNum]) && $dirtyTextBlocks[1][$blockNum] != "" &&
  494.                     $currentLogicalBlock !== $blockNum){
  495.                     $this->data['logicBlocks'][$textBlockCount] = $dirtyTextBlocks[1][$blockNum];
  496.                     $currentLogicalBlock = $blockNum;
  497.                 }
  498.                 //перебираем текстовые блоки(и шрифты к ним)
  499.                 foreach($fontAndText[4] as $key => $textBlocks){
  500.                     $this->data['clearText'][$textBlockCount]['text'] = "";
  501.                     if($fontAndText[3][$key]){
  502.                         $prevFont = $fontAndText[3][$key];
  503.                     }
  504.                     else{
  505.                         $fontAndText[3][$key] = $prevFont;
  506.                     }
  507.                     //Вынимаем два типа блоков текста: HEX-строки в <> и текстовые строки в ()
  508.                     preg_match_all('#[<\(].*?[\)>]#', $fontAndText[4][$key], $textBlock);
  509.                     //перебираем блоки
  510.                     foreach($textBlock[0] as $data){
  511.                         //Если блок с HEX  Значениями , то
  512.                         if(preg_match('#<(.*?)>#', $data, $hexString)){
  513.                             //Если строку надо декодировать
  514.                             if(isset($this->data['Fonts'][$fontAndText[3][$key]]['ToUnicode'])){
  515.                                 while($hexString[1]){
  516.                                     //Если есть значение в таблице декодирования
  517.                                     if(isset($this->data['Fonts'][$fontAndText[3][$key]]['TrnsfrmTable']["\x".substr_replace(substr(strtolower($hexString[1]),0,4), "\x", 2, 0)])){
  518.                                         $this->data['clearText'][$textBlockCount]['text'] .= $this->data['Fonts'][$fontAndText[3][$key]]['TrnsfrmTable']["\x".substr_replace(substr(strtolower($hexString[1]),0,4), "\x", 2, 0)];
  519.                                     }
  520.                                     //Если такого значения нет
  521.                                     else{
  522.                                         $this->data['clearText'][$textBlockCount]['text'] .= mb_convert_encoding(chr(hexdec(substr(substr(strtolower($hexString[1]),0,4), 0, 2)))
  523.                                                                                                                 .chr(hexdec(substr(substr(strtolower($hexString[1]),0,4), -2))), "UTF-8", "UTF-16");
  524.                                     }
  525.                                     $hexString[1] = substr($hexString[1],4);
  526.                                 }
  527.                             //Если не надо декодировать
  528.                             }else{
  529.                                 while($hexString[1]){
  530.                                     $this->data['clearText'][$textBlockCount]['text'] .= mb_convert_encoding(chr(hexdec(substr(substr(strtolower($hexString[1]),0,4), 0, 2)))
  531.                                         .chr(hexdec(substr(substr(strtolower($hexString[1]),0,4), -2))), "UTF-8", "UTF-16");
  532.                                     $hexString[1] = substr($hexString[1],4);
  533.                                 }
  534.                             }
  535.                         }
  536.                         //Если блок с обычными строками
  537.                         else{
  538.                             $fontAndText[4][$key] = preg_replace('|\((.*)\)|', '$1', $fontAndText[4][$key]);
  539.                             if(isset($this->data['Fonts'][$fontAndText[3][$key]]['ToUnicode'])){
  540.                                 $transformedText = '';
  541.                                 foreach($this->data['Fonts'][$fontAndText[3][$key]]['TrnsfrmTable'] as $from => $to){
  542.                                     //$this->data['clearText'] .= str_replace('\uccc'.$from,'\ucccc'.$to, $fontAndText[2][$key]);
  543.                                     //$this->data['clearText'] .= preg_replace('|\x{'.$from.'}|u','\x{'.$to.'}',$fontAndText[2][$key]);
  544.                                     $transformedText = preg_replace("|".$from."|", $to, $fontAndText[4][$key]);
  545.                                 }
  546.                                 $this->data['clearText'][$textBlockCount]['text'] .= $transformedText;
  547.                             }
  548.                             else{
  549.                                 $this->data['clearText'][$textBlockCount]['text'] .= $fontAndText[4][$key];
  550.                             }
  551.                         }
  552.                     }
  553.                 }
  554.             }
  555.         }
  556.     }
  557.  
  558.     public function convert_to_paragraph(){
  559.         $this->data['clearParagraph'] = array();
  560.         $paragraphCounter = 0;
  561.         $firstBlock = NULL;
  562.         foreach($this->data['logicBlocks'] as $blockNum => $logicValue){
  563.             if(!isset($firstBlock)){
  564.                 $firstBlock = $blockNum;
  565.             }
  566.             else{
  567.                 $paragraphCounter++;
  568.                 $this->data['clearParagraph'][$paragraphCounter]='';
  569.                 for($firstBlock; $firstBlock<$blockNum; $firstBlock++){
  570.                     $this->data['clearParagraph'][$paragraphCounter] .= $this->data['clearText'][$firstBlock]['text'];
  571.                 }
  572.             }
  573.         }
  574.     }
  575.  
  576. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement