/** * Method: getStartAndEndNodes * ========================================================================= * Searches the xml with the given blockname and returns * the corresponding start and end nodes. * * Parameters: * ------------------------------------------------------------------------- * - $xml: An instance of ```SimpleXMLElement``` * - $blockname: The name of the block to find. * * Returns: * ------------------------------------------------------------------------- * array */ protected function getStartAndEndNodes($xml, $blockname) { // Assume the nodes don't exist $startNode = false; $endNode = false; // Search for the block start and end tags foreach ($xml->xpath('//w:t') as $node) { if (Str::contains($node, $this->normaliseStartTag($blockname))) { $startNode = $node; continue; } if (Str::contains($node, $this->normaliseEndTag($blockname))) { $endNode = $node; break; } } // Bail out if we couldn't find anything if ($startNode === false || $endNode === false) { return false; } // Find the parent <w:p> node for the start tag $node = $startNode; $startNode = null; while (is_null($startNode)) { $node = $node->xpath('..')[0]; if ($node->getName() == 'p') { $startNode = $node; } } // Find the parent <w:p> node for the end tag $node = $endNode; $endNode = null; while (is_null($endNode)) { $node = $node->xpath('..')[0]; if ($node->getName() == 'p') { $endNode = $node; } } // Return the start and end node return [$startNode, $endNode]; }
/** * Performs some intial Setup. * * @param string $document This is either a filepath to a docx or html file. * Or it may be a HTML string. The HTML string must * contain a valid DOCTYPE. * * @param array $config Further configuration for the di container. * * @throws RuntimeException When not of the correct document type. */ public function __construct($document, $config = []) { // Configure the container parent::__construct($config); // Is the document a file if (is_file($document)) { // So that the save method can save the PDF in the same folder as // the original source document we need a refrence to it. $this->originalDocument = $this->file($document); // Grab the files extension $ext = $this->originalDocument->getExtension(); if ($ext !== 'docx' && $ext !== 'html') { throw new RuntimeException('Must be a DOCX or HTML file.'); } $this->documentType = $ext; // Save the document to a new temp file // In the case of DOCX files we may make changes to the document // before converting to PDF so to keep the API consitent lets create // a the temp file now. $this->document = $this->tempFile(file_get_contents($document), $ext); } elseif (Str::contains($document, 'DOCTYPE')) { // Again lets save a temp file $this->document = $this->tempFile($document, 'html'); $this->documentType = 'html'; } else { throw new RuntimeException('Unrecognised document type!'); } // Now create a new backend $class = '\\Gears\\Pdf\\' . ucfirst($this->documentType) . '\\Backend'; $this->backend = new $class($this->document, $config); }
/** * Method: convertDoc * ========================================================================= * This is where we actually do some converting of docx to pdf. * We use the command line utility unoconv. Which is basically a slightly * fancier way of using OpenOffice/LibreOffice Headless. * * See: http://dag.wiee.rs/home-made/unoconv/ * * Parameters: * ------------------------------------------------------------------------- * - $docx: This must be an instance of ```SplFileInfo``` * pointing to the document to convert. * * Returns: * ------------------------------------------------------------------------- * void */ public function convertDoc(TempFile $docx) { if (!is_executable($this->binary)) { throw new RuntimeException('The unoconv command ("' . $this->binary . '") ' . 'was not found or is not executable by the current user! '); } // Check to see if the profile dir exists and is writeable if (is_dir($this->profile) && !is_writable($this->profile)) { throw new RuntimeException('If unoconv does not have permissions to the User ' . 'Profile directory ("' . $this->profile . '") the conversion ' . 'will fail!'); } // Build the unoconv cmd $cmd = 'export HOME=' . $this->profile . ' && ' . $this->binary . ' ' . '--stdout ' . '-f pdf ' . '"' . $docx->getPathname() . '"'; // Run the command $process = $this->process($cmd); $process->run(); // Check for errors $error = null; if (!$process->isSuccessful()) { $error = $process->getErrorOutput(); // NOTE: For some really odd reason the first time the command runs // it does not complete successfully. The second time around it // works fine. It has something to do with the homedir setup... if (Str::contains($error, 'Error: Unable to connect')) { $process->run(); if (!$process->isSuccessful()) { $error = $process->getErrorOutput(); } else { $error = null; } } if (!is_null($error)) { throw new RuntimeException($error); } } // Clean up after ourselves exec('rm -rf ' . $this->profile); // Return the pdf data return $process->getOutput(); }