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...
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
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!