Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Add feature to optionally generate barcodes by width (px) not width factor #195

Open
objecttothis opened this issue Mar 19, 2024 · 18 comments

Comments

@objecttothis
Copy link

For barcode generators which allow float width factors, everything is already there to allow this.

Let's say I want to have an SVG barcode generated to 250px every time (dynamic width factor) In my code I could do this:

//Get the original width of the barcode
$barcodeData = $generator->getBarcodeData($barcode_value, $barcode_config['barcode_type']);
$originalWidth = $barcodeData->getWidth();

//Calculate the width factor
$desiredWidth = 250; // The desired width in pixels
$widthFactor = $desiredWidth / $originalWidth;

then call $generator->getBarcode($barcode_value, $barcode_type, $widthFactor, $barcode_height]); The problem with this is that getBarcodeData() is protected so I can't call it outside of the BarcodeGeneratorSVG class.

Please consider adding the ability to send the width in pixels instead of the width factor and do the above calculation in the function.

There are two ways this can be done without breaking existing uses of the function:

  1. Modify the existing function signature to: getBarcode(string $barcode, $type, float $widthFactor = 2, float $height = 30, string $foregroundColor = 'black', bool $dynamicWidthFactor = false): string. Then in the function itself if $dynamicWidthFactor is true, assume the value of $widthFactor is in pixels and do the above calculation of what the width factor should be. The rest of the function remains the same. When working with SVGs, pixels can be a float, so widthFactor being a float doesn't break this.
  2. Create BarcodeGeneratorDynamicSVG which operates as a clone of BarcodeGeneratorSVG but with the following function signature getBarcode(string $barcode, $type, int $width, float $height = 30, string $foregroundColor = 'black'): string and of course the width factor calculation above, but otherwise the function working the same.

It seems like it would be a waste to do 2 since the only difference between the two is the width factor being calculated, but it depends on what you're going for.

@objecttothis
Copy link
Author

objecttothis commented Mar 19, 2024

I suppose another (maybe better) option could be to add another function within BarcodeGeneratorSVG called getFixedWidthBarcode() that has the same function signature as 2 but just calculates the needed widthFactor and calls getBarcode with that calculated value.

@objecttothis
Copy link
Author

If you're taking contributions, I could probably submit a PR, but would only want to do that with input as to which option you'd want me to implement.

@casperbakker
Copy link
Member

It would add some complexity to support both ways, and not all generators support a float width so that would make it more complex to explain in the docs.

It is also not really needed, because you can always display the SVG as 250px wide, without the need for the SVG sizing inside the document to add up to 250px.

Maybe for a next big release it will become possible to do the barcode encoding separate from the image encoding, that way you could probably easily do what you suggest here without any extra methods. But that is not something for the coming months.

@objecttothis
Copy link
Author

It would add some complexity to support both ways, and not all generators support a float width so that would make it more complex to explain in the docs.

What I'm suggesting would only be for generators that support floats.

It is also not really needed, because you can always display the SVG as 250px wide, without the need for the SVG sizing inside the document to add up to 250px.

That's what I thought since SVG is vector, I thought it should stretch. I tried wrapping it in a <div style="width:250px, height:50px;"> but that had no effect on the SVG. I think that is because the generator is generating code with width and height attributes, so it doesn't stretch.

<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="202" height="50" viewBox="0 0 202 50" version="1.1" xmlns="http://www.w3.org/2000/svg">
	<desc>600180220171</desc>
	<g id="bars" fill="black" stroke="none">
...
	</g>
</svg>

You can see from the code that it's specifying the width and the viewbox. Just to see if it would work, I tried injecting preserveAspectRatio="none" as an attribute in the SVG but that had no effect on the barcode for me. I'm open to the idea that I'm doing it wrong, but I don't think I am.

Maybe for a next big release it will become possible to do the barcode encoding separate from the image encoding, that way you could probably easily do what you suggest here without any extra methods. But that is not something for the coming months.

@casperbakker
Copy link
Member

That's what I thought since SVG is vector, I thought it should stretch. I tried wrapping it in a

but that had no effect on the SVG. I think that is because the generator is generating code with width and height attributes, so it doesn't stretch.

That is because you need to target the SVG, not the div around it. See here an example how you can size the SVG perfectly with CSS. The first example is only targeting the div, the second example is setting the SVG width to 100%, making it match the outer div.

https://codepen.io/casperbakker/pen/jORLZqV

@objecttothis
Copy link
Author

objecttothis commented Apr 1, 2024

That's what I thought since SVG is vector, I thought it should stretch. I tried wrapping it in a but that had no effect on the SVG. I think that is because the generator is generating code with width and height attributes, so it doesn't stretch.

That is because you need to target the SVG, not the div around it. See here an example how you can size the SVG perfectly with CSS. The first example is only targeting the div, the second example is setting the SVG width to 100%, making it match the outer div.

https://codepen.io/casperbakker/pen/jORLZqV

@casperbakker I appreciate you going the extra mile to show me targeting the SVG, but even this code isn't correctly doing it. If you look at your codepen you'll see that .barcode2 is scaling by retaining the aspect ratio and this is because the generated barcode does not have preserveAspectRatio='none' in the svg, so it won't break the aspect ratio to scale properly. Take this code for example:

CSS

.barcode {
  width: 250px;
  height: 50px;
  border: 1px solid red;
}
.barcode svg {
  width: 100%;
  height: 100%;  
}

HTML

<div class='barcode'><?xml version="1.0" standalone="no" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="180" height="30" viewBox="0 0 180 50" version="1.1" xmlns="http://www.w3.org/2000/svg">
	<desc>0877842604</desc>
	<g id="bars" fill="black" stroke="none">
		<rect x="0" y="0" width="4" height="50" />
		<rect x="6" y="0" width="2" height="50" />
		<rect x="12" y="0" width="6" height="50" />
		<rect x="22" y="0" width="2" height="50" />
		<rect x="30" y="0" width="4" height="50" />
		<rect x="38" y="0" width="2" height="50" />
		<rect x="44" y="0" width="8" height="50" />
		<rect x="54" y="0" width="6" height="50" />
		<rect x="62" y="0" width="2" height="50" />
		<rect x="66" y="0" width="2" height="50" />
		<rect x="72" y="0" width="8" height="50" />
		<rect x="82" y="0" width="2" height="50" />
		<rect x="88" y="0" width="6" height="50" />
		<rect x="98" y="0" width="2" height="50" />
		<rect x="104" y="0" width="4" height="50" />
		<rect x="110" y="0" width="2" height="50" />
		<rect x="116" y="0" width="2" height="50" />
		<rect x="124" y="0" width="4" height="50" />
		<rect x="132" y="0" width="6" height="50" />
		<rect x="142" y="0" width="2" height="50" />
		<rect x="146" y="0" width="4" height="50" />
		<rect x="154" y="0" width="4" height="50" />
		<rect x="164" y="0" width="6" height="50" />
		<rect x="172" y="0" width="2" height="50" />
		<rect x="176" y="0" width="4" height="50" />
	</g>
</svg>
</div>

image

You'll notice that it doesn't stretch the width of the barcode. This is because your code is not generating SVG tags with the preserveAspectRatio="none" attribute. Adding that as <svg preserveAspectRatio="none" width="180" height="30" viewBox="0 0 180 50" version="1.1" xmlns="http://www.w3.org/2000/svg"> causes the barcode to stretch properly.

image

So even if you don't add the ability to programmatically set the width in px, please consider adding preserveAspectRatio="none" as an attribute in the generated SVG so that users can target the svg and stretch it to their liking.

@objecttothis
Copy link
Author

Also, when I played around with it, having preserveAspectRatio="none" appears that it wouldn't likely be a breaking change since not targeting the SVG doesn't distort the SVG even with attribute there. That said, if you wanted to make sure it didn't break existing code you could just add it as an optional parameter . bool $preserveAspectRatio = true) and only add the preserveAspectRatio="none" attribute if the value is false.

@objecttothis
Copy link
Author

@casperbakker if it's a time thing, I'd be happy to submit a PR for adding the attribute with or without the optional parameter in the barcodeGeneratorSVG() function.

@casperbakker
Copy link
Member

@objecttothis Thanks for the example with a bigger container. Weird thing is that if you use a smaller container then the image, it works. And that is how I have always used it:

Screenshot 2024-04-01 at 21 13 15

I don't like to add too many options that are only for very specific use cases. I don't think that preserveAspectRatio deserves it's own setting.

I am in the process of building version 3, where SVG's default method is to give it the desired width and height. If I understand you right, that is what will fix your use case right?

I don't want to add preserveAspectRatio right now, because I am looking into how to support text below the barcode. Maybe that option will interfere with it, but I will keep this in mind for v3. Would be nice if you can scale the image bigger and smaller.

@casperbakker casperbakker reopened this Apr 1, 2024
@objecttothis
Copy link
Author

objecttothis commented Apr 1, 2024

@objecttothis Thanks for the example with a bigger container. Weird thing is that if you use a smaller container then the image, it works. And that is how I have always used it:

Yeah, I'm not sure what's going on there either.

I don't like to add too many options that are only for very specific use cases. I don't think that preserveAspectRatio deserves it's own setting.

I understand about not liking to add too many options, however I respectfully disagree that the desire for fixed width is a specific use case if you mean rare that someone would want that. After all, you're already providing a fixed height option.

I am in the process of building version 3, where SVG's default method is to give it the desired width and height. If I understand you right, that is what will fix your use case right?

Yes, this would fix our use case as it is exactly what we need.

I don't want to add preserveAspectRatio right now, because I am looking into how to support text below the barcode. Maybe that option will interfere with it, but I will keep this in mind for v3. Would be nice if you can scale the image bigger and smaller.

Understood. Last thing anyone needs are breaking changes to their code. We generate our text below the barcode separately and would probably keep it that way even if it were an option since we already know what that text is when feeding it to php-barcode-generator. I suspect that since preserveAspectRatio is an SVG tag attribute that if the text were generated as part of the SVG, it would distort the text. If the text is generated outside of the SVG then preserveAspectRatio likely would have no effect.

If you need anyone to test for you, I have a vested interest in helping, so let me know.

@casperbakker
Copy link
Member

If you need anyone to test for you, I have a vested interest in helping, so let me know.

If you could see if the new setup in v3 is understandable, that would be really helpful. In that v3 branch I have updated the readme with the new setup. Can you create a SVG with a fixed width with the new SvgRenderer?

The more confident I am in the new setup, the sooner I can release it.

@objecttothis
Copy link
Author

I did see the readme note the other day

## Upgrading to v3
There is no need to change anything when upgrading from v2 to v3. Above you find the new preferred way of using this library since v3. But the old style still works.

If you want to convert to the new style, here is an example:
php
// Old style
$generator = new Picqer\Barcode\BarcodeGeneratorSVG();
echo $generator->getBarcode('081231723897', $generator::TYPE_CODE_128);

// New style
$barcode = (new Picqer\Barcode\Types\TypeCode128())->getBarcode('081231723897');
$renderer = new Picqer\Barcode\Renderers\SvgRenderer();
echo $renderer->render($barcode);

The new style seems less readable to me, but I'm assuming that this is so that you can keep backward compatibility with the old style. I looked briefly at the code and if I understand correctly the way I would generate an SVG with width 250px and height 50px is

$barcode = (new Picqer\Barcode\Types\TypeCode128())->getBarcode('081231723897');
$renderer = new Picqer\Barcode\Renderers\SvgRenderer();
echo $renderer->render($barcode, 250, 50);

I usually use npm to install picqer but I assume for the sake of testing I need to move the contents of the V3 branch into my project in place of the existing code. Is that right?

@casperbakker
Copy link
Member

The new style seems less readable to me, but I'm assuming that this is so that you can keep backward compatibility with the old style.

It is a bit more verbose, but the benefits are that we can make all renderers different so they can have their own options. Also you can create your own barcode types or renderers because they are not linked anymore.

Your example is correct.

I usually use npm to install picqer but I assume for the sake of testing I need to move the contents of the V3 branch into my project in place of the existing code. Is that right?

Do you mean installing via Composer? You can try something like (I am in mobile now): composer require picqer/php-barcode-generator@dev-v3, or change the version in composer.json to 'dev-v3'.

@objecttothis
Copy link
Author

The new style seems less readable to me, but I'm assuming that this is so that you can keep backward compatibility with the old style.

It is a bit more verbose, but the benefits are that we can make all renderers different so they can have their own options. Also you can create your own barcode types or renderers because they are not linked anymore.

Your example is correct.

I usually use npm to install picqer but I assume for the sake of testing I need to move the contents of the V3 branch into my project in place of the existing code. Is that right?

Do you mean installing via Composer? You can try something like (I am in mobile now): composer require picqer/php-barcode-generator@dev-v3, or change the version in composer.json to 'dev-v3'.

Sorry, you are correct. I was just interacting with someone trying to get our project's npm build running so I had that on the brain. I'll just do it through composer. I'm working on another project today through Friday but if I can sneak some time in the evening I'll try it out and let you know. Otherwise I'll do it Monday morning.

@objecttothis
Copy link
Author

@casperbakker Looks like dev-v3 isn't a thing, but I was able to get v3.x-dev. The only problem I see so far is that it's requiring php 8.2. We are trying to keep our app backward compatible to at least 8.0. Is this just a dev build requirement or is there a reason for it to require 8.2?

@objecttothis
Copy link
Author

hmmm. @casperbakker For some reason the SVG renderer is generating embedded png, not SVG

image

	{
		try
		{
			$barcode_value = $this->get_barcode_value($item, $barcode_config);
			$barcode = (new TypeCode128())->getBarcode($barcode_value);
			$renderer = new SvgRenderer();
			return $renderer->render($barcode, $barcode_config['barcode_width'], $barcode_config['barcode_height']);
		}

There is another problem. In our application the barcode type is stored as a string in our database and this worked because the barcode type was being passed as a parameter.

			$generator = new BarcodeGeneratorSVG();
			$barcode_value = $this->get_barcode_value($item, $barcode_config);

			return $generator->getBarcode($barcode_value, $barcode_config['barcode_type'], 2, $barcode_config['barcode_height']);

The new style calls getBarcode() from the Barcode Type class. If I still want to store the selected barcode type in the database I now have to do something like

        // Get the barcode type from the config
        $barcode_type = $barcode_config['barcode_type'];
        
        // Create the fully qualified class name
        $class_name = "\\Picqer\\Barcode\\Types\\Type" . $barcode_type;
        
        // Check if the class exists
        if (!class_exists($class_name)) {
            throw new Exception("Barcode type {$barcode_type} is not supported.");
        }
        
        // Create an instance of the barcode type class
        $barcode_type_instance = new $class_name();
        
        // Generate the barcode
        $barcode = $barcode_type_instance->getBarcode($barcode_value);

It also means me adding use statements for all supported barcode types. This is not desirable.

@objecttothis
Copy link
Author

To be clear, the png isn't the correct width either. It is the correct width in the sense that the image is that dimension, but it's padded whitespace rather than the barcode being stretched to fit the width.

@objecttothis
Copy link
Author

@casperbakker did you see my testing results above?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants