Пример #1
0
 protected function extractChildElements(\DOMNode $elem, $skipWhitespace = false, $parentIsBlock = false)
 {
     /** @var \DOMNode $child */
     $first = true;
     foreach ($elem->childNodes as $child) {
         if ($skipWhitespace && $child->nodeName === '#text' && ctype_space($child->nodeValue)) {
             // do nothing
         } elseif ($child->nodeName === '#text') {
             $this->elements[] = new ElementEvent(ElementEvents::TEXT, $this->normalizeSpace($child->nodeValue, $parentIsBlock && $first), $this->attributes->values());
         } else {
             switch ($child->nodeName) {
                 case 'p':
                     $this->elements[] = new ElementEvent(ElementEvents::PARAGRAPH_START);
                     $this->extractChildElements($child, false, true);
                     $this->elements[] = new ElementEvent(ElementEvents::PARAGRAPH_END);
                     break;
                 case 'ul':
                     $this->attributes->setMany(['list_depth' => $this->attributes->get('list_depth', 0) + 1, 'list_numbered' => false]);
                     $this->elements[] = new ElementEvent(ElementEvents::LIST_START, '', [ElementAttributes::NUMBERED => false]);
                     $this->extractChildElements($child, true);
                     $this->elements[] = new ElementEvent(ElementEvents::LIST_END, '', [ElementAttributes::NUMBERED => false]);
                     $this->attributes->undo();
                     break;
                 case 'ol':
                     $this->attributes->setMany(['list_depth' => $this->attributes->get('list_depth', 0) + 1, 'list_numbered' => true]);
                     $this->elements[] = new ElementEvent(ElementEvents::LIST_START, '', [ElementAttributes::NUMBERED => true]);
                     $this->extractChildElements($child, true);
                     $this->elements[] = new ElementEvent(ElementEvents::LIST_END, '', [ElementAttributes::NUMBERED => true]);
                     $this->attributes->undo();
                     break;
                 case 'li':
                     $this->elements[] = new ElementEvent(ElementEvents::LIST_ITEM_START, '', [ElementAttributes::DEPTH => $this->attributes['list_depth'] - 1, ElementAttributes::NUMBERED => $this->attributes['list_numbered']]);
                     $this->extractChildElements($child, false, true);
                     $this->elements[] = new ElementEvent(ElementEvents::LIST_ITEM_END, '', [ElementAttributes::DEPTH => $this->attributes['list_depth'] - 1, ElementAttributes::NUMBERED => $this->attributes['list_numbered']]);
                     break;
                 case 'table':
                     $this->elements[] = new ElementEvent(ElementEvents::TABLE_START);
                     $this->extractChildElements($child, true);
                     $this->elements[] = new ElementEvent(ElementEvents::TABLE_END);
                     break;
                 case 'tr':
                     $this->elements[] = new ElementEvent(ElementEvents::TABLE_ROW_START);
                     $this->extractChildElements($child, true);
                     $this->elements[] = new ElementEvent(ElementEvents::TABLE_ROW_END);
                     break;
                 case 'th':
                 case 'td':
                     $this->elements[] = new ElementEvent(ElementEvents::TABLE_CELL_START, '', [ElementAttributes::HEADER => $child->nodeName === 'th']);
                     $this->extractChildElements($child);
                     $this->elements[] = new ElementEvent(ElementEvents::TABLE_CELL_END, '', [ElementAttributes::HEADER => $child->nodeName === 'th']);
                     break;
                 case 'tbody':
                 case 'thead':
                     $this->extractChildElements($child, true);
                     break;
                 case 'h1':
                     $this->elements[] = new ElementEvent(ElementEvents::TITLE, $this->normalizeSpace($child->nodeValue), [ElementAttributes::DEPTH => 0]);
                     break;
                 case 'h2':
                     $this->elements[] = new ElementEvent(ElementEvents::TITLE, $this->normalizeSpace($child->nodeValue), [ElementAttributes::DEPTH => 1]);
                     break;
                 case 'h3':
                     $this->elements[] = new ElementEvent(ElementEvents::TITLE, $this->normalizeSpace($child->nodeValue), [ElementAttributes::DEPTH => 2]);
                     break;
                 case 'h4':
                     $this->elements[] = new ElementEvent(ElementEvents::TITLE, $this->normalizeSpace($child->nodeValue), [ElementAttributes::DEPTH => 3]);
                     break;
                 case 'h5':
                     $this->elements[] = new ElementEvent(ElementEvents::TITLE, $this->normalizeSpace($child->nodeValue), [ElementAttributes::DEPTH => 4]);
                     break;
                 case 'h6':
                     $this->elements[] = new ElementEvent(ElementEvents::TITLE, $this->normalizeSpace($child->nodeValue), [ElementAttributes::DEPTH => 5]);
                     break;
                 case 'div':
                     $this->extractChildElements($child, true);
                     break;
                 case 'span':
                     $this->extractChildElements($child);
                     break;
                 case 'strong':
                 case 'b':
                     $this->attributes[ElementAttributes::BOLD] = true;
                     $this->extractChildElements($child);
                     $this->attributes->undo();
                     break;
                 case 'em':
                 case 'i':
                     $this->attributes[ElementAttributes::ITALIC] = true;
                     $this->extractChildElements($child);
                     $this->attributes->undo();
                     break;
                 case 'u':
                     $this->attributes[ElementAttributes::UNDERLINE] = 'single';
                     $this->extractChildElements($child);
                     $this->attributes->undo();
                     break;
                 case 'del':
                     $this->attributes[ElementAttributes::STRIKETHROUGH] = true;
                     $this->extractChildElements($child);
                     $this->attributes->undo();
                     break;
                 case 'sup':
                     $this->attributes[ElementAttributes::SUPERSCRIPT] = true;
                     $this->extractChildElements($child);
                     $this->attributes->undo();
                     break;
                 case 'sub':
                     $this->attributes[ElementAttributes::SUBSCRIPT] = true;
                     $this->extractChildElements($child);
                     $this->attributes->undo();
                     break;
                 case 'a':
                     $href = $child->attributes->getNamedItem('href')->nodeValue;
                     $this->attributes[ElementAttributes::LINK] = $href;
                     $this->elements[] = new ElementEvent(ElementEvents::LINK, $this->normalizeSpace($child->nodeValue), $this->attributes->values());
                     $this->attributes->undo();
                     break;
                 case 'img':
                     $this->elements[] = new ElementEvent(ElementEvents::IMAGE, $child->attributes->getNamedItem('src')->nodeValue);
                     break;
                 default:
                     throw new \RuntimeException("Element of type {$child->nodeName} not supported");
                     break;
             }
         }
         $first = false;
     }
 }
Пример #2
0
 public function onElement(ElementEvent $event)
 {
     if (null === $this->section) {
         throw new \RuntimeException("No document section provided");
     }
     /** @var AbstractContainer $current */
     $current = $this->context->get('current', $this->section);
     if ($current === $this->section) {
         $this->dispatcher->dispatch(DocumentEvents::BEFORE_ROOT_ELEMENT, new DocumentConstructionEvent($this->section));
     }
     switch ($event->getElement()) {
         case ElementEvents::TITLE:
             /** @var Section $current */
             $title = $current->addTitle($event->getContent());
             $this->dispatcher->dispatch(DocumentEvents::TITLE_CREATED, new DocumentEvent($this->section, $title, $event));
             break;
         case ElementEvents::PARAGRAPH_START:
             $this->context->setMany(['current' => $current->addTextRun(), 'event' => $event]);
             break;
         case ElementEvents::TABLE_START:
             $this->context->setMany(['current' => $current->addTable(['width' => 5000, 'unit' => 'pct']), 'event' => $event]);
             break;
         case ElementEvents::TABLE_ROW_START:
             /** @var Table $current */
             $this->context->setMany(['current' => $current->addRow(), 'event' => $event]);
             break;
         case ElementEvents::TABLE_CELL_START:
             /** @var Row $current */
             $this->context->setMany(['current' => $current->addCell(5000), 'event' => $event]);
             break;
         case ElementEvents::LIST_END:
             // close the last item in the list
             if ($current instanceof ListItemRun) {
                 $this->dispatcher->dispatch(DocumentEvents::LIST_ITEM_CREATED, new DocumentEvent($this->section, $current, $this->context['event']));
                 $this->context->undo();
             }
             break;
         case ElementEvents::LIST_ITEM_END:
         case ElementEvents::LIST_START:
             break;
         case ElementEvents::LIST_ITEM_START:
             // close the previous list item
             if ($current instanceof ListItemRun) {
                 $this->dispatcher->dispatch(DocumentEvents::LIST_ITEM_CREATED, new DocumentEvent($this->section, $current, $this->context['event']));
                 $this->context->undo();
                 $current = $this->context->get('current', $this->section);
             }
             $is_numbered = $event->getAttribute(ElementAttributes::NUMBERED, false);
             $this->context->setMany(['current' => $current->addListItemRun($event->getAttribute(ElementAttributes::DEPTH), ['listType' => $is_numbered ? ListItem::TYPE_NUMBER : ListItem::TYPE_BULLET_FILLED]), 'event' => $event]);
             break;
         case ElementEvents::TEXT:
             $current->addText($event->getContent(), $event->getAttributes());
             break;
         case ElementEvents::IMAGE:
             $image = $current->addImage($event->getContent());
             $this->dispatcher->dispatch(DocumentEvents::IMAGE_CREATED, new DocumentEvent($this->section, $image, $event));
             break;
         case ElementEvents::LINK:
             $link = $current->addLink($event->getAttribute(ElementAttributes::LINK), $event->getContent());
             $this->dispatcher->dispatch(DocumentEvents::LINK_CREATED, new DocumentEvent($this->section, $link, $event));
             break;
         case ElementEvents::TABLE_END:
             $this->dispatcher->dispatch(DocumentEvents::TABLE_CREATED, new DocumentEvent($this->section, $current, $this->context['event']));
             $this->context->undo();
             break;
         case ElementEvents::PARAGRAPH_END:
             $this->dispatcher->dispatch(DocumentEvents::PARAGRAPH_CREATED, new DocumentEvent($this->section, $current, $this->context['event']));
             $this->context->undo();
             break;
         case ElementEvents::TABLE_ROW_END:
             $this->dispatcher->dispatch(DocumentEvents::TABLE_ROW_CREATED, new DocumentEvent($this->section, $current, $this->context['event']));
             $this->context->undo();
             break;
         case ElementEvents::TABLE_CELL_END:
             $this->dispatcher->dispatch(DocumentEvents::TABLE_CELL_CREATED, new DocumentEvent($this->section, $current, $this->context['event']));
             $this->context->undo();
             break;
         default:
             throw new \RuntimeException("Cannot handle element of type '{$event->getElement()}'");
     }
     $current = $this->context->get('current', $this->section);
     if ($current === $this->section) {
         $this->dispatcher->dispatch(DocumentEvents::AFTER_ROOT_ELEMENT, new DocumentConstructionEvent($this->section));
     }
 }