Skip to content

add opacity to doc.image options#1709

Merged
blikblum merged 3 commits intofoliojs:masterfrom
andreiaugustin:image-opacity
Apr 7, 2026
Merged

add opacity to doc.image options#1709
blikblum merged 3 commits intofoliojs:masterfrom
andreiaugustin:image-opacity

Conversation

@andreiaugustin
Copy link
Copy Markdown
Contributor

Feature

What kind of change does this PR introduce?

This PR introduces an opacity option to doc.image() which reuses the existing _doOpacity mechanism (ExtGState with ca set to the opacity value).

I think this would be helpful in order to control transparency of images on-the-fly, for example #837 wanted to add a watermark and one of the best options is to simply add an image directly on the page, but you would need the image with the alpha channel already set. With this, you could just have your watermark (e.g. could be your company's logo that you already use on your website) and chuck it into the middle of the page and make it transparent with ease.

Checklist:

  • Unit Tests
  • Documentation
  • Update CHANGELOG.md
  • Ready to be merged

@blikblum
Copy link
Copy Markdown
Member

This change adds an opacity for the whole page. It is not specific for image.

Can already be achieved with doc opacity method. Using is makes clear that is related to the page

@blikblum blikblum closed this Mar 21, 2026
@andreiaugustin
Copy link
Copy Markdown
Contributor Author

Sorry, I know it is not technically changing the image and simply layering an opacity layer over it, but I don't think it is doing it to the whole page?

In my tests, I have used the png.js:

const PDFDocument = require('..');
const fs = require('fs');

const doc = new PDFDocument();

doc.pipe(fs.createWriteStream('png.pdf'));

doc.image('images/test_294.png', 60, 64, { opacity: 0.5 });
doc.image('images/test_294.png', 300, 64, { opacity: 0.5 });

doc.text('PNG with transparency (palette 8bit):', 100, 100);
doc.text('PNG with transparency (palette 8bit):', 200, 200);

doc.end();

Now, because of where we push that opacity operator on doc.image, a save() is done beforehand and a restore() after, and after decoding with qdf, I can see the q/Q pairs that scope the gs opacity operator to just the image, not the whole page?


%% Contents for page 1
%% Original object ID: 5 0
9 0 obj
<<
  /Length 10 0 R
>>
stream
1 0 0 -1 0 792 cm
q
/Gs1 gs
297 0 0 -600 60 664 cm
/I1 Do
Q
q
/Gs1 gs
297 0 0 -600 300 664 cm
/I1 Do
Q
q
1 0 0 -1 0 792 cm
BT
1 0 0 1 100 683.384 Tm
/F1 12 Tf
[(PNG with tr) 10 (ansparency \(palette 8bit\):) 0] TJ
ET
Q
q
1 0 0 -1 0 792 cm
BT
1 0 0 1 200 583.384 Tm
/F1 12 Tf
[(PNG with tr) 10 (ansparency \(palette 8bit\):) 0] TJ
ET
Q
endstream
endobj

Please let me know if I'm wrong, I very well could be - but from my understanding, this shouldn't be covering the whole page, but just our image.

@blikblum
Copy link
Copy Markdown
Member

I actually did not tested. I relied on doOpacity implementation that add the dictionary to page this.page.ext_gstates[name] = dictionary;

I will reevaluate

@blikblum blikblum reopened this Mar 21, 2026
@blikblum blikblum merged commit 9f035c1 into foliojs:master Apr 7, 2026
3 checks passed
@blikblum
Copy link
Copy Markdown
Member

blikblum commented Apr 7, 2026

Many thanks. I confirmed it works fine

@mariusa
Copy link
Copy Markdown

mariusa commented Apr 10, 2026

Multumesc, Andrei! :)
When printing a color PDF, if opacity is set to say 0.9 for color images, would this result in less toner usage on laser printers?

@andreiaugustin
Copy link
Copy Markdown
Contributor Author

No problem my friend! :)

Would need to be tested, but probably yes? As far as I'm aware, even though the opacity is set as a layer on top, the PDF document should get rasterised into a bitmap for the printer by the driver, so any opacity should result in lighter pixels and therefore less toner usage. However, some printers also have a minimum toner floor, so savings may not be linear. Especially in laser printers, you cannot really vary the amount of ink per dot so to simulate lightness, it does vary the density of dots across an area. So for example a 10% reduction in opacity would in theory save 10% toner on that area, but in reality you're more likely to see closer to 5% or even less savings due to how the printer ends up halftoning the image.

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

Successfully merging this pull request may close these issues.

3 participants