Skip to content

Commit

Permalink
Merged some minor improvements from horstoeko/zugferd
Browse files Browse the repository at this point in the history
  • Loading branch information
HorstOeko committed Oct 13, 2024
1 parent 64245e9 commit e427371
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 37 deletions.
13 changes: 12 additions & 1 deletion src/OrderDocumentPdfBuilderAbstract.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@ public function saveDocument(string $toFilename)
return $this;
}

/**
* Returns the PDF as an inline file
*
* @param string $toFilename
* @return string
*/
public function saveDocumentInline(string $toFilename): string
{
return $this->pdfWriter->Output($toFilename, 'I');
}

/**
* Returns the PDF as a string
*
Expand Down Expand Up @@ -155,7 +166,7 @@ private function startCreatePdf(): void
for ($pageNumber = 1; $pageNumber <= $pageCount; ++$pageNumber) {
$pageContent = $this->pdfWriter->importPage($pageNumber, '/MediaBox');
$this->pdfWriter->AddPage();
$this->pdfWriter->useTemplate($pageContent);
$this->pdfWriter->useTemplate($pageContent, 0, 0, null, null, true);
}

// Set PDF version 1.7 according to PDF/A-3 ISO 32000-1
Expand Down
139 changes: 105 additions & 34 deletions src/OrderDocumentPdfReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@

namespace horstoeko\orderx;

use Exception;
use horstoeko\orderx\exception\OrderFileNotFoundException;
use horstoeko\orderx\exception\OrderNoValidAttachmentFoundInPdfException;
use JMS\Serializer\Exception\RuntimeException;
use Smalot\PdfParser\Parser as PdfParser;
use horstoeko\orderx\exception\OrderFileNotFoundException;
use horstoeko\orderx\exception\OrderFileNotReadableException;

/**
* Class representing the document reader for incoming PDF/A-Documents with
Expand All @@ -30,64 +28,137 @@ class OrderDocumentPdfReader
/**
* List of filenames which are possible in PDF
*/
const ATTACHMENT_FILEAMES = ['order-x.xml'];
const ATTACHMENT_FILENAMES = ['order-x.xml'];

/**
* Load a PDF file (Order-X)
*
* @param string $pdfFilename
* Contains a full-qualified filename which must exist and must be readable
* @param string $pdfFilename Contains a full-qualified filename which must exist and must be readable
* @return null|OrderDocumentReader
* @throws OrderFileNotFoundException
* @throws Exception
* @throws RuntimeException
* @throws OrderNoValidAttachmentFoundInPdfException
*/
public static function readAndGuessFromFile(string $pdfFilename): ?OrderDocumentReader
{
if (!file_exists($pdfFilename)) {
throw new OrderFileNotFoundException($pdfFilename);
}
if (!is_readable($pdfFilename)) {

$pdfContent = file_get_contents($pdfFilename);

if ($pdfContent === false) {
throw new OrderFileNotReadableException($pdfFilename);
}

return static::readAndGuessFromContent($pdfContent);
}

/**
* Tries to load an attachment content from PDF and return a OrderDocumentReader
* If any erros occured or no attachments were found null is returned
*
* @param string $pdfContent String Containing the binary pdf data
* @return OrderDocumentReader|null
* @throws Exception
*/
public static function readAndGuessFromContent(string $pdfContent): ?OrderDocumentReader
{
$xmlContent = static::internalExtractXMLFromPdfContent($pdfContent);

if (is_null($xmlContent)) {
return null;
}

try {
return OrderDocumentReader::readAndGuessFromContent($xmlContent);
} catch (\Exception $e) {
return null;
}
}

/**
* Returns a XML content from a PDF file
*
* @param string $pdfFilename
* Contains a full-qualified filename which must exist and must be readable
* @return string|null
* @throws OrderFileNotFoundException
* @throws OrderFileNotReadableException
* @throws Exception
*/
public static function getXmlFromFile(string $pdfFilename): ?string
{
if (!file_exists($pdfFilename)) {
throw new OrderFileNotFoundException($pdfFilename);
}

$pdfContent = file_get_contents($pdfFilename);

if ($pdfContent === false) {
throw new OrderFileNotReadableException($pdfFilename);
}

return static::getXmlFromContent($pdfContent);
}

/**
* Returns a XML content from a PDF binary stream (string)
*
* @param string $pdfContent String Containing the binary pdf data
* @return string|null
* @throws Exception
*/
public static function getXmlFromContent(string $pdfContent): ?string
{
return static::internalExtractXMLFromPdfContent($pdfContent);
}

/**
* Get the attachment content from XML.
* See the allowed filenames which are supported
*
* @param string $pdfContent
* @return null|string
* @throws Exception
*/
protected static function internalExtractXMLFromPdfContent(string $pdfContent): ?string
{
$pdfParser = new PdfParser();
$pdfParsed = $pdfParser->parseFile($pdfFilename);
$pdfParsed = $pdfParser->parseContent($pdfContent);
$filespecs = $pdfParsed->getObjectsByType('Filespec');

$attachmentFound = false;
$attachmentIndex = 0;
$embeddedFileIndex = 0;
$orderDocument = null;
$returnValue = null;

foreach ($filespecs as $filespec) {
$filespecDetails = $filespec->getDetails();
if (in_array($filespecDetails['F'], static::ATTACHMENT_FILEAMES)) {
$attachmentFound = true;
break;
}
$attachmentIndex++;
}

if (true == $attachmentFound) {
/**
* @var array<\Smalot\PdfParser\PDFObject>
*/
$embeddedFiles = $pdfParsed->getObjectsByType('EmbeddedFile');
foreach ($embeddedFiles as $embeddedFile) {
if ($attachmentIndex == $embeddedFileIndex) {
$orderDocument = OrderDocumentReader::readAndGuessFromContent($embeddedFile->getContent());
try {
foreach ($filespecs as $filespec) {
$filespecDetails = $filespec->getDetails();
if (in_array($filespecDetails['F'], static::ATTACHMENT_FILENAMES)) {
$attachmentFound = true;
break;
}
$embeddedFileIndex++;
$attachmentIndex++;
}
}

if (is_null($orderDocument)) {
throw new OrderNoValidAttachmentFoundInPdfException();
if (true == $attachmentFound) {
/**
* @var array<\Smalot\PdfParser\PDFObject>
*/
$embeddedFiles = $pdfParsed->getObjectsByType('EmbeddedFile');
foreach ($embeddedFiles as $embeddedFile) {
if ($attachmentIndex == $embeddedFileIndex) {
$returnValue = $embeddedFile->getContent();
break;
}
$embeddedFileIndex++;
}
}
} catch (\Exception $e) {
$returnValue = null;
}

return $orderDocument;
return $returnValue;
}
}
61 changes: 61 additions & 0 deletions src/exception/OrderFileNotReadableException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

/**
* This file is a part of horstoeko/orderx.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace horstoeko\orderx\exception;

use Throwable;

/**
* Class representing an exception for non-readable a file
*
* @category Order-X
* @package Order-X
* @author D. Erling <horstoeko@erling.com.de>
* @license https://opensource.org/licenses/MIT MIT
* @link https://github.com/horstoeko/orderx
*/
class OrderFileNotReadableException extends Exception
{
/**
* The context of the type element
*
* @var string
*/
private $filePath = "";

/**
* Constructor
*
* @param string $filePath
*/
public function __construct(string $filePath)
{
$this->filePath = $filePath;

parent::__construct($this->buildMessage());
}

/**
* @inheritDoc
*/
public function __toString()
{
return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
}

/**
* Build the message
*
* @return string
*/
private function buildMessage(): string
{
return sprintf("The filer %s is not readable", $this->filePath);
}
}
2 changes: 1 addition & 1 deletion tests/testcases/OrderDocumentPdfReaderComfortTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ public function testPdfReadNotExists(): void

public function testPdfReadInvalid(): void
{
$this->expectException(OrderNoValidAttachmentFoundInPdfException::class);
self::$document = OrderDocumentPdfReader::readAndGuessFromFile(dirname(__FILE__) . '/../assets/reader-invalid.pdf');
$this->assertNull(self::$document);
}

public function testPdfRead(): void
Expand Down
2 changes: 1 addition & 1 deletion tests/testcases/OrderDocumentPdfReaderExtendedTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ public function testPdfReadNotExists(): void

public function testPdfReadInvalid(): void
{
$this->expectException(OrderNoValidAttachmentFoundInPdfException::class);
self::$document = OrderDocumentPdfReader::readAndGuessFromFile(dirname(__FILE__) . '/../assets/reader-invalid.pdf');
$this->assertNull(self::$document);
}

public function testPdfRead(): void
Expand Down

0 comments on commit e427371

Please sign in to comment.