Skip to content

add opacity to doc.image options#1709

Open
andreiaugustin wants to merge 2 commits intofoliojs:masterfrom
andreiaugustin:image-opacity
Open

add opacity to doc.image options#1709
andreiaugustin wants to merge 2 commits intofoliojs:masterfrom
andreiaugustin:image-opacity

Conversation

@andreiaugustin
Copy link
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
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
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
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
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.

2 participants