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

PNG grayscale support #190

Open
phaseOne opened this issue Jan 13, 2021 · 4 comments
Open

PNG grayscale support #190

phaseOne opened this issue Jan 13, 2021 · 4 comments

Comments

@phaseOne
Copy link

phaseOne commented Jan 13, 2021

The feature request

It would be fantastic if bwip-js could export grayscale (monochromatic) PNGs in place of RGB. This would offer more control when printing and when importing into applications that support CMYK. Thanks!

I'm not too familiar with the PNG data structures at the moment, but it seems the place to start adding this functionality would be in DrawingZlibPng(opts, callback)

function DrawingZlibPng(opts, callback) {

The issue

I'm facing a bit of a challenge when bringing barcode PNGs into Adobe InDesign for CMYK documents. Since bwip-js exports PNGs as RGB, the barcode will print as CMYK(75, 68, 67, 90) which can result in a messy barcode when there's misalignment between the plates. I'm looking for that sweet CMYK(0,0,0,100).

Workaround

Apply a Generic Gray Profile as a post-process step. You can do this pretty quickly in macOS Automator.
CleanShot 2021-01-12 at 17 19 08@2x

Alternative solutions

I'd use CMYK in SVG, but that isn't completely standardized and adopted yet.

If there would be a way to get an EPS out of bwip-js, that could work as well.

@metafloor
Copy link
Owner

I need to review the source for the built-in drawing modules. There is common core code used by both the html canvas interface and node that would be impacted by this request.

@metafloor
Copy link
Owner

I think it best to punt on this issue for right now. Changing the built-in drawing code is problematic as it has no a priori knowledge of color depth.

Conversely, it takes very little code to post-process an RGBA PNG to grayscale. For example, using the pngjs module, you could change your callback (or Promise then) handler to do the conversion:

const PNG = require('pngjs').PNG;

bwipjs.toBuffer({ ... }, function(err, png) {
    new PNG({ colorType:0 }).parse(png).on("parsed", function() {
        this.pack().pipe(fs.createWriteStream("gray.png"));
    });
});

colorType:0 creates a grayscale image with default #ffffff substituted/alpha-blended for any transparent pixels.

Not sure your experience with javascript so if you need more details on this, let me know.

@phaseOne
Copy link
Author

@metafloor thanks for the pngjs suggestion and sample code! It would be nice to reduce the number of dependencies and have bwip-js generate the desired output, but I'm not constrained on resources for this application, so this is a reasonable approach for now. I'll give this a try and see if InDesign likes it. Looks like pngjs colorType: 4 is also an option for those who would like to convert to grayscale, but retain the alpha channel. Thanks again!

You're welcome to close this issue to reduce the backlog if you prefer that. I don't think this rises to a priority level for me to work on a PR at this time, but if it gets more votes maybe I'll come back to it.

I think I understand why this would be a big lift to support. Looks like you're creating the image bitmap in RGBA (either as a canvas buffer or node.js Buffer)

img = ctx.getImageData(0, 0, width, height);

image_buffer = Buffer.alloc ? Buffer.alloc(width * height * 4 + height)
: new Buffer(width * height * 4 + height);
image_width = width;
image_height = height;
// Set background
if (/^[0-9a-fA-F]{6}$/.test(''+opts.backgroundcolor)) {
var rgb = opts.backgroundcolor;
fillRGB(parseInt(rgb.substr(0,2), 16),
parseInt(rgb.substr(2,2), 16),
parseInt(rgb.substr(4,2), 16));
}

And further manipulation (drawing of foreground shapes, background) is done directly in the RGBA bitmap.

function set(x, y, a) {
// translate/rotate
x += gs_dx;
y += gs_dy;
var tx = tx0 * x + tx1 * y + tx2 * (gs_width-1) + tx3 * (gs_height-1);
var ty = ty0 * x + ty1 * y + ty2 * (gs_width-1) + ty3 * (gs_height-1);
// https://en.wikipedia.org/wiki/Alpha_compositing
var offs = (ty * gs_width + tx) * 4 + (ty+1) * gs_rowbyte;
var dsta = gs_image[offs+3] / 255;
var srca = a / 255;
var inva = (1 - srca) * dsta;
var outa = srca + inva;
gs_image[offs+0] = ((gs_r * srca + gs_image[offs+0] * inva) / outa)|0;
gs_image[offs+1] = ((gs_g * srca + gs_image[offs+1] * inva) / outa)|0;
gs_image[offs+2] = ((gs_b * srca + gs_image[offs+2] * inva) / outa)|0;
gs_image[offs+3] = (255 * outa)|0;
}

So all of that code would have to change to support a grayscale bitmap.

@metafloor
Copy link
Owner

I think you are misunderstanding the issue. The actual code to produce a gray scale png is not difficult. The issue is knowing when it is safe to do so. For example, ultracode requires colors. And many of the BWIPP options can specify RGB values. So all of those conditions would have to be checked. The drawing interfaces get the original options object passed to bwip-js, so it is just a matter of checking whether any of the color-specific BWIPP options specify a non-gray value, and whether the barcode symbol itself requires color support. The code is not difficult but does require vigilance. I don't always pay attention to what new symbols Terry has added to BWIPP. For example, I was surprised to find ultracode in the bwip-js demo one day....

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

No branches or pull requests

2 participants