set( 'isRemoteEnabled', false ); $options->set( 'isPhpEnabled', false ); $options->set( 'isJavascriptEnabled', false ); $options->set( 'defaultFont', 'DejaVu Sans' ); $options->setAllowedProtocols( array( 'data://', 'file://', 'https://' ) ); if ( defined( 'LMCC_PLUGIN_DIR' ) ) { $plugin_root = realpath( LMCC_PLUGIN_DIR ); $options->setChroot( array( false !== $plugin_root ? $plugin_root : LMCC_PLUGIN_DIR ) ); } if ( function_exists( 'get_temp_dir' ) ) { $options->setTempDir( (string) get_temp_dir() ); } $dompdf = new $dompdf_class( $options ); $dompdf->setPaper( 'A4', 'portrait' ); $dompdf->loadHtml( $this->html( $document ), 'UTF-8' ); $dompdf->render(); $this->add_page_numbers( $dompdf, $document->language() ); $output = $dompdf->output(); if ( ! is_string( $output ) || 0 !== strpos( $output, '%PDF-' ) ) { throw new \RuntimeException( 'Dompdf did not return a valid PDF byte stream.' ); } return $output; } private function add_page_numbers( object $dompdf, string $language ): void { $canvas = $dompdf->getCanvas(); $font_metrics = $dompdf->getFontMetrics(); $page_label = $this->texts->get( $language, 'page' ); $canvas->page_script( static function ( $page_number, $page_count, $page_canvas, $metrics ) use ( $page_label ): void { $font = $metrics->getFont( 'DejaVu Sans', 'normal' ); $font_size = 7.5; $text = $page_label . ' ' . $page_number . '/' . $page_count; $x = $page_canvas->get_width() - 51 - $metrics->getTextWidth( $text, $font, $font_size ); $y = $page_canvas->get_height() - 35; $page_canvas->text( $x, $y, $text, $font, $font_size, array( 0.44, 0.44, 0.44 ) ); } ); } private function html( BasicOrderDocument $document ): string { $language = $document->language(); $merchant = $document->merchant(); $billing = $document->billing(); $shipping = $document->shipping(); $rows = ''; foreach ( $document->items() as $item ) { $rows .= '' . $this->e( (string) ( $item['name'] ?? '' ) ) . '' . $this->e( (string) ( $item['sku'] ?? '' ) ) . '' . (int) ( $item['quantity'] ?? 0 ) . '' . $this->e( (string) ( $item['unit_price'] ?? '' ) ) . '' . $this->e( (string) ( $item['line_total'] ?? '' ) ) . ''; } $merchant_lines = $this->identity_lines( array( (string) ( $merchant['name'] ?? '' ), (string) ( $merchant['address'] ?? '' ), $this->labeled( $language, 'registration', (string) ( $merchant['registration_number'] ?? '' ) ), $this->labeled( $language, 'tax_id', (string) ( $merchant['tax_id'] ?? '' ) ), $this->labeled( $language, 'vat_number', (string) ( $merchant['vat_number'] ?? '' ) ), $this->labeled( $language, 'email', (string) ( $merchant['email'] ?? '' ) ), $this->labeled( $language, 'phone', (string) ( $merchant['phone'] ?? '' ) ), ) ); $billing_lines = $this->identity_lines( array( (string) ( $billing['name'] ?? '' ), (string) ( $billing['company'] ?? '' ), (string) ( $billing['address'] ?? '' ), (string) ( $billing['email'] ?? '' ), (string) ( $billing['phone'] ?? '' ) ) ); $shipping_lines = $this->identity_lines( array( (string) ( $shipping['name'] ?? '' ), (string) ( $shipping['company'] ?? '' ), (string) ( $shipping['address'] ?? '' ) ) ); return '' . '

' . $this->e( $this->texts->get( $language, 'document_title' ) . ' ' . $document->order_number() ) . '

' . ( '' !== $document->preview_label() ? '
' . $this->e( $document->preview_label() ) . '
' : '' ) . '
' . $this->e( $this->texts->get( $language, 'order_date' ) ) . ': ' . $this->e( $document->order_date() ) . '
' . '

' . $this->e( $this->texts->get( $language, 'seller' ) ) . '

' . $merchant_lines . '

' . $this->e( $this->texts->get( $language, 'customer' ) ) . '

' . $billing_lines . '
' . ( '' !== $shipping_lines ? '

' . $this->e( $this->texts->get( $language, 'shipping' ) ) . '

' . $shipping_lines . '
' : '' ) . '' . $rows . '
' . $this->e( $this->texts->get( $language, 'product' ) ) . '' . $this->e( $this->texts->get( $language, 'sku' ) ) . '' . $this->e( $this->texts->get( $language, 'quantity' ) ) . '' . $this->e( $this->texts->get( $language, 'unit_price' ) ) . '' . $this->e( $this->texts->get( $language, 'line_total' ) ) . '
' . '
' . $this->e( $this->texts->get( $language, 'subtotal' ) ) . '' . $this->e( $document->subtotal() ) . '
' . $this->e( $this->texts->get( $language, 'discount' ) ) . '' . $this->e( $document->discount() ) . '
' . $this->e( $this->texts->get( $language, 'shipping_total' ) ) . '' . $this->e( $document->shipping_total() ) . '
' . $this->e( $this->texts->get( $language, 'tax' ) ) . '' . $this->e( $document->tax_total() ) . '
' . $this->e( $this->texts->get( $language, 'grand_total' ) ) . '' . $this->e( $document->total() ) . '
' . '
' . $this->e( $this->texts->get( $language, 'payment_method' ) ) . ': ' . $this->e( $document->payment_method() ) . '
' . $this->e( $this->texts->get( $language, 'shipping_method' ) ) . ': ' . $this->e( $document->shipping_method() ) . '
' . '
' . $this->e( $this->texts->get( $language, 'notice' ) ) . '
' . ''; } /** @param string[] $lines */ private function identity_lines( array $lines ): string { $clean = array(); foreach ( $lines as $line ) { $line = trim( $line ); if ( '' !== $line ) { $clean[] = $this->e( $line ); } } return implode( '
', $clean ); } private function labeled( string $language, string $key, string $value ): string { $value = trim( $value ); return '' === $value ? '' : $this->texts->get( $language, $key ) . ': ' . $value; } private function e( string $value ): string { return htmlspecialchars( $value, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5, 'UTF-8' ); } }