I have a grayscale image that serves as a soft mask and I want to use it on a group of PDF objects (images or paths).
The mask and the objects do not necessarily use the same transformation matrix, and there might be more than one object to mask, so that excludes the possibility of using the SMask
attribute of the ImageXObject dictionary.
So after reading some of the PDF specification, it looks like I should do the following: create a transparency group with the objects to mask, then draw it with the soft mask set on the graphics state.
Will that work? How can I achieve this, preferably with PDFBox?
[[4 0 100] [0 4 100]]
.[[2 0 100] [0 2 150]]
.So a transparency group is created with the image and the square, then it's drawn with the mask image. Here's the expected result:
Rather ugly as far as the effect goes, but that's just an example.
As far as I can see establishing an extended graphics state soft mask is a very manual task in PDFBox. You can do so as follows:
try ( PDDocument document = new PDDocument() ) {
final PDImageXObject image = RETRIEVE PHOTO IMAGE;
final PDImageXObject mask = RETRIEVE MASK IMAGE;
PDTransparencyGroupAttributes transparencyGroupAttributes = new PDTransparencyGroupAttributes();
transparencyGroupAttributes.getCOSObject().setItem(COSName.CS, COSName.DEVICEGRAY);
PDTransparencyGroup transparencyGroup = new PDTransparencyGroup(document);
transparencyGroup.setBBox(PDRectangle.A4);
transparencyGroup.setResources(new PDResources());
transparencyGroup.getCOSObject().setItem(COSName.GROUP, transparencyGroupAttributes);
try ( PDFormContentStream canvas = new PDFormContentStream(transparencyGroup) ) {
canvas.drawImage(mask, new Matrix(400, 0, 0, 400, 100, 100));
}
COSDictionary softMaskDictionary = new COSDictionary();
softMaskDictionary.setItem(COSName.S, COSName.LUMINOSITY);
softMaskDictionary.setItem(COSName.G, transparencyGroup);
PDExtendedGraphicsState extendedGraphicsState = new PDExtendedGraphicsState();
extendedGraphicsState.getCOSObject().setItem(COSName.SMASK, softMaskDictionary);
PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);
try ( PDPageContentStream canvas = new PDPageContentStream(document, page) ) {
canvas.saveGraphicsState();
canvas.setGraphicsStateParameters(extendedGraphicsState);
canvas.setNonStrokingColor(Color.BLACK);
canvas.addRect(100, 100, 400, 400);
canvas.fill();
canvas.drawImage(image, new Matrix(400, 0, 0, 300, 100, 150));
canvas.restoreGraphicsState();
}
document.save(new File(RESULT_FOLDER, "SoftMaskedImageAndRectangle.pdf"));
}
The result:
If I were you, though, I would not use a bitmap image for the soft mask but instead a PDF gradient. The result most likely will be much less pixelated.