* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link http://labs.fi/mlinvoice.eng.php */ require_once 'invoice_printer_base.php'; require_once 'htmlfuncs.php'; require_once 'miscfuncs.php'; require_once 'pdf.php'; require_once 'markdown.php'; /** * XSLT invoice * * @category MLInvoice * @package MLInvoice\Base * @author Ere Maijala * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link http://labs.fi/mlinvoice.eng.php */ class InvoicePrinterXslt extends InvoicePrinterBase { /** * XSLT parameters * * @var array */ protected $xsltParams = []; /** * Transform the invoice to XML * * @param string $xslt XSLT file name * @param string $xsd Optional XSD name used to check the results * * @return void */ protected function transform($xslt, $xsd = '') { if (!class_exists('XSLTProcessor')) { die( <<This printout requires the PHP XSL extension, more specifically the XSLTProcessor class, which seems to be missing. Please install the XSL extension or request your server administrator to do it.

Many Linux distributions offer the XSL extension in a separate package that can be installed with a package manager. E.g. in Ubuntu the package might be php5-xsl, php7.0-xsl or php7.1-xsl depending on the PHP version.

More information about the XSL extension is available in the PHP Manual. EOT ); } $xml = new SimpleXMLElement(''); $sender = $xml->addChild('sender'); $this->arrayToXML($this->senderData, $sender); $recipient = $xml->addChild('recipient'); $this->arrayToXML($this->recipientData, $recipient); $invoice = $xml->addChild('invoice'); $invoiceData = $this->invoiceData; $invoiceData['totalsum'] = $this->totalSum; $invoiceData['totalvat'] = $this->totalVAT; $invoiceData['totalsumvat'] = $this->totalSumVAT; $invoiceData['paidsum'] = $invoiceData['invoice_unpaid'] ? $this->partialPayments : $this->totalSumVAT; $invoiceData['formatted_ref_number'] = $this->refNumber; $invoiceData['barcode'] = $this->barcode; $invoiceData['groupedvats'] = $this->groupedVATs; $this->arrayToXML($invoiceData, $invoice); $rows = $invoice->addChild('rows'); $this->arrayToXML($this->getInvoiceRowData(), $rows, 'row'); $type = $invoice->addChild('invoicetype'); $this->arrayToXML($this->invoiceTypeData, $type, 'invoicetype'); if ($this->attachments) { $attachments = $invoice->addChild('attachments'); foreach ($this->attachments as $attachment) { $attachment = getInvoiceAttachment($attachment['id']); $xmlAttachment = $attachments->addChild('attachment'); $xmlAttachment->addChild( 'name', $attachment['name'] ? $attachment['name'] : $attachment['filename'] ); $xmlAttachment->addChild('mimetype', $attachment['mimetype']); $xmlAttachment->addChild('filesize', $attachment['filesize']); $xmlAttachment->addChild('filename', $attachment['filename']); if ('application/pdf' !== $attachment['mimetype']) { // Image to PDF $pdf = new PDF('P', 'mm', 'A4', _CHARSET_ == 'UTF-8', _CHARSET_, false); $pdf->AddPage(); $pdf->Image( '@' . $attachment['filedata'], $this->left, $this->autoPageBreakMargin, $this->width, 0, '', '', 'B', false, 300, 'C' ); $pdf->SetXY($this->left, $pdf->GetY() + 5); $pdf->SetFont('Helvetica', '', 10); $pdf->multiCellMD( $this->width, 5, $attachment['name'] ? $attachment['name'] : $attachment['filename'], 'L' ); $xmlAttachment->addChild('filedata', base64_encode($pdf->Output('', 'S'))); } else { $xmlAttachment->addChild('filedata', base64_encode($attachment['filedata'])); } } } include 'settings_def.php'; $settingsData = []; foreach ($arrSettings as $key => $value) { if (substr($key, 0, 8) == 'invoice_' && $value['type'] != 'LABEL') { switch ($key) { case 'invoice_terms_of_payment' : $settingsData[$key] = $this->getTermsOfPayment( getPaymentDays($invoiceData['company_id']) ); break; case 'invoice_pdf_filename' : $settingsData[$key] = $this->getPrintOutFileName( getSetting('invoice_pdf_filename') ); break; default : $settingsData[$key] = getSetting($key); } } } $settingsData['invoice_penalty_interest_desc'] = $this->translate('PenaltyInterestDesc') . ': ' . miscRound2OptDecim(getSetting('invoice_penalty_interest'), 1) . ' %'; $settingsData['current_time_year'] = date('Y'); $settingsData['current_time_mon'] = date('m'); $settingsData['current_time_day'] = date('d'); $settingsData['current_time_hour'] = date('H'); $settingsData['current_time_min'] = date('i'); $settingsData['current_time_sec'] = date('s'); $settingsData['current_timestamp'] = date('c'); $settingsData['current_timestamp_utc'] = gmdate('Y-m-d\TH:i:s\Z'); $settings = $xml->addChild('settings'); $this->arrayToXML($settingsData, $settings); $xsltproc = new XSLTProcessor(); $xsl = new DOMDocument(); $xsl->load($xslt); $xsltproc->importStylesheet($xsl); $xsltproc->setParameter('', 'stylesheet', $this->printStyle); foreach ($this->xsltParams as $param => $value) { $xsltproc->setParameter('', $param, $value); } $domDoc = dom_import_simplexml($xml)->ownerDocument; $this->xml = $xsltproc->transformToXML($domDoc); if ($xsd) { libxml_use_internal_errors(true); $xmlDoc = new DOMDocument(); $xmlDoc->loadXML($this->xml); if (!$xmlDoc->schemaValidate($xsd)) { header('Content-Type: text/plain'); echo "Result XML validation failed:\n\n"; $errors = libxml_get_errors(); foreach ($errors as $error) { switch ($error->level) { case LIBXML_ERR_WARNING : $type = 'Warning'; break; case LIBXML_ERR_FATAL : $type = 'Fatal'; break; default : $type = 'Error'; } echo "$type {$error->code}({$error->level}) at {$error->line}:{$error->column}: {$error->message}\n"; } echo "\n\nXML:\n\n"; $lineno = 1; foreach (explode("\n", $this->xml) as $line) { echo "$lineno\t$line\n"; ++$lineno; } exit(1); } } } /** * Convert an array to XML * * @param array $array Array * @param SimpleXMLElement $xml XML * @param string $subnodename Sub node * * @return void */ protected function arrayToXML($array, &$xml, $subnodename = '') { foreach ($array as $key => $value) { if (is_array($value)) { $node = $xml->addChild( '' !== $subnodename ? $subnodename : $key ); if (!is_numeric($key)) { $this->arrayToXML($value, $node); } else { $this->arrayToXML($value, $node); } } else { if ($key != 'logo_filedata') { $xml->addChild($key, str_replace('&', '&', $value)); } else { $xml->addChild($key, base64_encode($value)); } } } } /** * Preprocess and return invoice rows * * @return array */ protected function getInvoiceRowData() { // Preprocess invoice rows if (getSetting('printout_markdown')) { $markdown = new MLMarkdown(); } else { $markdown = null; } $rows = []; foreach ($this->invoiceRowData as $data) { $data['type'] = $this->translate($data['type']); if ($markdown) { foreach (['product_name', 'product_code', 'description'] as $key) { if (!empty($data[$key])) { $markdownData = $markdown->transform($data[$key]); $data[$key] = trim( strip_tags( html_entity_decode( $markdownData, ENT_COMPAT | ENT_HTML401, 'UTF-8' ) ) ); } } } $rowDesc = ''; if (!empty($data['product_name']) && !empty($data['description'])) { $rowDesc = $data['product_name'] . ' (' . $data['description'] . ')'; } else { $rowDesc = (null !== $data['product_name'] ? $data['product_name'] : '') . $data['description']; } $data['row_description'] = $rowDesc; $rows[] = $data; } return $rows; } }