$width_modules ) {
if ( $is_bar ) {
$x = $offset * $args['scale'] + $args['padding'];
$w = $width_modules * $args['scale'];
$h = ( ! empty( $guard[ $index ] ) && $args['text'] ) ? $guard_height : $bar_height;
$parts[] = sprintf(
'',
self::num( $x ),
self::num( $bar_top ),
self::num( $w ),
self::num( $h ),
$args['bar_color']
);
}
$offset += $width_modules;
$is_bar = ! $is_bar;
}
if ( $args['text'] ) {
$parts[] = self::text_layer( $symbol, $args, $quiet, $bar_top + $bar_height );
}
$attributes = array(
'xmlns' => 'http://www.w3.org/2000/svg',
'viewBox' => '0 0 ' . self::num( $width ) . ' ' . self::num( $height ),
'width' => self::num( $width ),
'height' => self::num( $height ),
'role' => 'img',
'aria-label' => $label,
'preserveAspectRatio' => 'xMidYMid meet',
'shape-rendering' => 'crispEdges',
);
if ( '' !== $args['class'] ) {
$attributes['class'] = $args['class'];
}
if ( 'transparent' !== $args['bg_color'] ) {
array_unshift(
$parts,
sprintf(
'',
self::num( $width ),
self::num( $height ),
$args['bg_color']
)
);
}
$markup = '';
return $markup;
}
/**
* Build the human readable digit layer.
*
* @param array $symbol Encoded symbol.
* @param array $args Parsed settings.
* @param array $quiet Quiet zone widths in modules.
* @param float $bar_bottom Y offset of the bottom of the bars.
* @return string
*/
protected static function text_layer( $symbol, $args, $quiet, $bar_bottom ) {
$families = isset( $symbol['groups'] ) ? $symbol['groups'] : array();
$baseline = $bar_bottom + $args['text_size'];
$style = sprintf(
'font-family:%s;font-size:%spx;fill:%s;',
$args['font_family'],
self::num( $args['text_size'] ),
$args['text_color']
);
$out = '';
if ( $symbol['lead'] !== '' ) {
$out .= sprintf(
'%s',
self::num( $args['padding'] + $quiet[0] * $args['scale'] / 2 ),
self::num( $baseline ),
$style,
esc_html( $symbol['lead'] )
);
}
foreach ( $families as $group ) {
$center = ( $quiet[0] + ( $group['from'] + $group['to'] + 1 ) / 2 ) * $args['scale'] + $args['padding'];
$out .= sprintf(
'%s',
self::num( $center ),
self::num( $baseline ),
$style,
esc_html( $group['text'] )
);
}
if ( $symbol['tail'] !== '' ) {
$out .= sprintf(
'%s',
self::num( $args['padding'] + ( $quiet[0] + array_sum( $symbol['elements'] ) + $quiet[1] / 2 ) * $args['scale'] ),
self::num( $baseline ),
$style,
esc_html( $symbol['tail'] )
);
}
if ( ! $families && '' === $symbol['lead'] && '' === $symbol['tail'] ) {
$out .= sprintf(
'%s',
self::num( $args['padding'] + ( $quiet[0] + array_sum( $symbol['elements'] ) / 2 ) * $args['scale'] ),
self::num( $baseline ),
$style,
esc_html( $symbol['text'] )
);
}
return $out;
}
/**
* Merge user settings with defaults and validate every value.
*
* @param array $args Raw settings.
* @return array
*/
public static function parse_args( $args ) {
$defaults = array(
'scale' => 2,
'height' => 60,
'text' => true,
'text_size' => 14,
'bar_color' => '#000000',
'bg_color' => 'transparent',
'text_color' => '',
'padding' => 0,
'class' => 'bcgn-barcode',
'font_family' => 'ui-monospace, SFMono-Regular, Menlo, Consolas, monospace',
);
$args = array_merge( $defaults, array_intersect_key( $args, $defaults ) );
$args['scale'] = max( 0.5, min( 10, (float) $args['scale'] ) );
$args['height'] = (int) max( 10, min( 400, (int) $args['height'] ) );
$args['text_size'] = (int) max( 6, min( 48, (int) $args['text_size'] ) );
$args['padding'] = (int) max( 0, min( 100, (int) $args['padding'] ) );
$args['text'] = self::truthy( $args['text'] );
$args['bar_color'] = self::color( $args['bar_color'], '#000000' );
$args['bg_color'] = self::color( $args['bg_color'], 'transparent' );
$args['class'] = self::class_names( $args['class'] );
if ( '' === $args['text_color'] ) {
$args['text_color'] = $args['bar_color'];
} else {
$args['text_color'] = self::color( $args['text_color'], $args['bar_color'] );
}
return $args;
}
/**
* Interpret common truthy values coming from shortcode attributes.
*
* @param mixed $value Raw value.
* @return bool
*/
protected static function truthy( $value ) {
if ( is_bool( $value ) ) {
return $value;
}
return ! in_array( strtolower( (string) $value ), array( '', '0', 'false', 'no', 'off' ), true );
}
/**
* Validate a colour, falling back to a safe default.
*
* @param mixed $value Raw colour.
* @param string $fallback Fallback colour.
* @return string
*/
public static function color( $value, $fallback ) {
$value = trim( (string) $value );
if ( 'transparent' === $value || 'none' === $value ) {
return 'transparent';
}
if ( preg_match( '/^#([0-9a-f]{3}|[0-9a-f]{6})$/i', $value ) ) {
return strtolower( $value );
}
if ( preg_match( '/^rgba?\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*(,\s*(0|1|0?\.\d+)\s*)?\)$/', $value ) ) {
return preg_replace( '/\s+/', '', strtolower( $value ) );
}
if ( preg_match( '/^[a-z]{3,20}$/i', $value ) ) {
return strtolower( $value );
}
return $fallback;
}
/**
* Keep CSS class names to a safe subset.
*
* @param mixed $value Raw class list.
* @return string
*/
protected static function class_names( $value ) {
$names = preg_split( '/\s+/', trim( (string) $value ) );
$clean = array();
foreach ( (array) $names as $name ) {
if ( preg_match( '/^-?[_a-zA-Z][_a-zA-Z0-9-]*$/', $name ) ) {
$clean[] = $name;
}
}
return implode( ' ', array_slice( $clean, 0, 5 ) );
}
/**
* Human readable label for a format slug.
*
* @param string $format Format slug.
* @return string
*/
protected static function format_label( $format ) {
$formats = BCGN_Barcode::formats();
if ( isset( $formats[ $format ] ) ) {
return $formats[ $format ]['label'];
}
return strtoupper( $format );
}
/**
* Format a number without trailing zeros.
*
* @param float $value Number to format.
* @return string
*/
protected static function num( $value ) {
$value = round( (float) $value, 3 );
$text = number_format( $value, 3, '.', '' );
return rtrim( rtrim( $text, '0' ), '.' );
}
}