Add transparency to PDF with libharu

236 Views Asked by At

I want to create a pdf with partially transparent polygons as the ellipse in the below image using libharu. The polygons come as RGBA, but libharu only has these fill methods:

HPDF_Page_SetCMYKFill()
HPDF_Page_SetGrayFill()
HPDF_Page_SetRGBFill() 

There is no "A" channel for rgb. (There is a transparency mask for the whole image, but as I understand, this only makes a range of colors invisible in the whole image, which is not what I want.)

I am new to libharu, and not an expert in color schemes or PDF, so solutions on all these levels might work...

Partially transparent ellipse

1

There are 1 best solutions below

1
On BEST ANSWER

The following code works as a general solution as proven by Duke (the Original Poster).

The generic means to add variability in a PDF file is, first to declare a set of optional "Graphics States" and transparency was added in version 5 (%PDF-1.4) as an Extended Graphics State (/ExtGState) commonly assigned to /GS0 /GS1 /GS2.... but the declarative naming scheme is optional.

Thus in the PDF internal document resources you add for example <</ExtGState<</GS0 0.25/GS1 0.5 /GS...>>>> as required. Then whilst building a page those states can be introduced (usually seen as q /GS0 gs for any following objects until reset when required (usually Q for previous) for the subsequent page objects.

Every PDF builder will have some similar methodology and during discussion the following was proposed as the method for a minimal example using LibHaru. using HPDF_CreateExtGState(pdf_document_) and HPDF_Page_SetExtGState(pdf_page_, gstate)

#include ".../hpdf.h"
#include <vector>

int main(int argc, char* argv[])
{
std::vector<std::pair<float, float>> pol1{ {100, 100}, {900, 900}, {900, 100} };
std::vector<std::pair<float, float>> pol2{ {10, 10}, {10, 900}, {900, 10} };

auto pdf_document_ = HPDF_New([](HPDF_STATUS error_no, HPDF_STATUS detail_no, void* user_data) {
// Do some error handling
}, NULL);

auto pdf_page_ = HPDF_AddPage(pdf_document_);
HPDF_Page_SetHeight(pdf_page_, 1000);
HPDF_Page_SetWidth(pdf_page_, 1000);

// Polygon 1 (no transparency)
HPDF_Page_SetRGBFill(pdf_page_, 1, 1, 0); // Set color to yellow

HPDF_Page_MoveTo(pdf_page_, pol1[0].first, pol1[0].second);
for (int i = 1; i < pol1.size(); i++) {
HPDF_Page_LineTo(pdf_page_, pol1[i].first, pol1[i].second);
}

HPDF_Page_ClosePathFillStroke(pdf_page_);

// Polygon 2 (opacity 0.45)
HPDF_Page_SetRGBFill(pdf_page_, 1, 0, 1); // Set color to purple - because... why not ;-)

HPDF_ExtGState gstate{ HPDF_CreateExtGState(pdf_document_) }; // Create extended graphics state
HPDF_ExtGState_SetAlphaFill(gstate, 0.45); // Set the opacity
HPDF_Page_SetExtGState(pdf_page_, gstate); // Apply the gstate property to the page

HPDF_Page_MoveTo(pdf_page_, pol2[0].first, pol2[0].second);
for (int i = 1; i < pol2.size(); i++) {
HPDF_Page_LineTo(pdf_page_, pol2[i].first, pol2[i].second);
}

HPDF_Page_ClosePathFillStroke(pdf_page_);

HPDF_SaveToFile(pdf_document_, "my.pdf");
}

The result will be a Page Definition with /ExtGState labled /E1

4 0 obj
<</Type/Page/MediaBox [ 0 0 1000 1000 ]/Contents 5 0 R
/Resources<</ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
/ExtGState <</E1 7 0 R>>>>
/Parent 2 0 R>>
endobj

/E1 points to the Extended Graphic State, in this case just a change in color alpha but could have other extended attributes

7 0 obj
<</Type/ExtGState/ca 0.45>>
endobj

then later in page contents, that named state can be applied at that time as if /ca 0.45 was included

5 0 obj
<</Length 6 0 R>>
stream
% set color to yellow
1 1 0 rg
% draw solid state triangle diagonally right and up from bottom left
100 100 m
900 900 l
900 100 l
% close and fill
b
% Change colour to magenta
1 0 1 rg
% set gs to Extended Graphic State (Opacity 45%)
/E1 gs
10 10 m
10 900 l
900 10 l
b

enter image description here

I have condensed the true PDF result here to show them in less lines. So object 7 in notepad is laid out like below, and it is easy to edit from 0.45 to 0.75 opacity to experiment as seen on the above right in a real time preview (not Acrobat).

7 0 obj
<<
/Type /ExtGState
/ca 0.75
>>
endobj

NOTE: I use the term opacity for the alpha channel since 0 with infinite floats to 1 is a range from fully transparent (0) to fully opaque (1).

This can mean in RGBA terms that [0 0 0 0] is no color at all (totally translucent) while [1 1 1 1] is full color (totally solid white). But more confusing it means as a single "color" transparency is [# # # 0] to [# # # 1] thus on a scale of 256 bytes in images, all are shown as in effect a mono black softmask when stored and extracted independently, but that's a different very broad topic!