// root) throws during parsing. Fail closed with a harmless comment,
// but log the cause so a genuinely broken template is diagnosable.
Logger::log(
sprintf(
'Thermal receipt render failed for template %s: %s',
isset( $template['id'] ) ? (string) $template['id'] : 'unknown',
$e->getMessage()
)
);
echo '';
return;
}
// The emitter leaves its output width-agnostic (the PDF path constrains it
// via the physical page size). On the browser-print surface the page can be
// wider than the roll, so constrain to the resolved paper width here or the
// row tables would stretch across the whole page.
$open = '';
// $html is built by the thermal emitter, which escapes receipt data; $open
// is a fixed wrapper with an escaped numeric width.
echo $open . $html . '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
* Format a pixel value for inline CSS without a trailing decimal point.
*
* @param float $px Pixel value.
*
* @return string
*/
private function format_px( float $px ): string {
return rtrim( rtrim( number_format( $px, 2, '.', '' ), '0' ), '.' );
}
/**
* Resolve the template's paper width in CSS pixels (96dpi).
*
* Template metadata declares the physical roll ('58mm' / '80mm'); fall back
* to 80mm when absent or out of range so the emitter can scale the character
* grid to fit the page.
*
* @param array $template Template metadata/content.
*
* @return float Paper width in CSS px.
*/
private function paper_width_px( array $template ): float {
$raw = isset( $template['paper_width'] ) ? (string) $template['paper_width'] : '';
$mm = (float) $raw; // Leading-number cast: '58mm' -> 58.0.
if ( $mm < 25.0 || $mm > 250.0 ) {
$mm = 80.0;
}
return round( $mm * 96 / 25.4, 2 );
}
}