- Joined
- Jun 14, 2024
- Messages
- 2
- Reaction score
- 0
I have been trying to make my own custom PDF editor & I have made a PDF parser that works absolutely fine for any PDF feature until images came up. First of all, the images in my PDFs are compressed with the standard DEFLATE/INFLATE algorithm & they all have the FlateDecode filter. the images, compressed & decompressed will have a lot of raw bytes that may be corrupted as a string or a char so I used a vector of uint8_t to be safe. here's my decompression/inflation function :
std::vector<uint8_t> parser::inflate_stream_to_raw(const std::vector<uint8_t>& deflated_stream) {
z_stream stream{};
int ret = inflateInit(&stream);
if (ret != Z_OK) std::cout << "\ncould not init zlib\n";
std::vector<uint8_t> inflated_stream;
const int chunk_size = 16384; // 16 KB chunk size
std::vector<uint8_t> buffer(chunk_size);
stream.next_in = const_cast<Bytef*>(deflated_stream.data());
stream.avail_in = static_cast<uInt>(deflated_stream.size());
while (ret != Z_STREAM_END) {
stream.next_out = reinterpret_cast<Bytef*>(buffer.data());
stream.avail_out = chunk_size;
ret = inflate(&stream, Z_NO_FLUSH);
if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
std::cerr << "Error: zlib stream error. Error code: " << ret << std::endl;
inflateEnd(&stream);
return {}; // Return an empty vector on error
}
inflated_stream.insert(inflated_stream.end(), buffer.data(), buffer.data() + chunk_size - stream.avail_out);
}
inflateEnd(&stream);
return inflated_stream;
}
this supposedly works fine, a version of this that returns the data as a string has worked fine for the PDF contents without any issues. So, when it comes time to render it, I have a .bmp that is the same as the image in the PDF i am testing & its size is about 12 MB, & testing the size of my decompressed image stream it is also around 12 MB, so I don't see how it could be the decompression failing. But when I get it to render, it doesnt look right. its an image of a fox sitting in snow and what i get rendered is a repeating image with the snow rendered fine but the fox is blue for some reason. i have verified the image is RGB
I've tried many ways to even get the bitmap image to render & this is the only way that seems to work so I don't know how to fix it if it is a problem with winGDI or how I render it. here;s the code I use to render it:
#include <C:/code-libs/pdfCoder/pdfCoder.hpp>
#include <Windows.h>
#include <wingdi.h>
#include <string>
#include <sstream>
#include <vector>
#pragma comment(lib, "C:/code-libs/pdfCoder/pdfCoder.lib")
#pragma comment(lib, "gdi32.lib")
LRESULT CALLBACK main_wnd_proc(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param);
HWND main_wnd;
std::vector<image_object> img_objs;
parser pdf;
int pt_to_dip(double pt, double dpi) {
if (dpi == 96) return static_cast<int>(std::round((pt / 72) * dpi)); // if DPI is 96, then PX & DIP are equal
else return static_cast<int>(std::round(((pt / 72) * dpi) / (dpi / 96))); // else find PX first then convert to DIP
}
int WINAPI wWinMain(HINSTANCE cur_instance, HINSTANCE prev_instance, LPWSTR cmd_line, int cmd_count) {
const wchar_t* MAIN_WND = L"main_window";
WNDCLASS wc{};
wc.hInstance = cur_instance;
wc.lpszClassName = MAIN_WND;
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpfnWndProc = main_wnd_proc;
RegisterClass(&wc);
pdf.open("C:\\Coding Projects\\test field\\test4.pdf");
objects_root pdf_root = pdf.get_root();
page_object page = pdf.parse_page(pdf_root.pages[0]);
img_objs = pdf.parse_page_images(page);
main_wnd = CreateWindow(MAIN_WND, L"File Organiser",
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT,
1500, 1500,
nullptr, nullptr, nullptr, nullptr);
if (main_wnd == NULL) return 1;
ShowWindow(main_wnd, SW_SHOW);
UpdateWindow(main_wnd);
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK main_wnd_proc(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param) {
HDC hdc;
switch (msg) {
case WM_PAINT:
OutputDebugStringA("\nPAINTING...");
PAINTSTRUCT ps;
hdc = BeginPaint(hwnd, &ps);
for (const image_object& img_obj : img_objs) {
OutputDebugStringA("looping images");
std::stringstream ss;
ss << img_obj.image_stream.size();
OutputDebugStringA(ss.str().c_str());
BITMAPINFOHEADER bmp_header = {};
bmp_header.biSize = sizeof(BITMAPINFOHEADER);
bmp_header.biWidth = pt_to_dip(img_obj.width, 96);
bmp_header.biHeight = -pt_to_dip(img_obj.height, 96);
bmp_header.biPlanes = 1;
bmp_header.biBitCount = img_obj.bits_per_component * 3;
bmp_header.biCompression = BI_RGB;
bmp_header.biSizeImage = 0;
BITMAPINFO bmi = {};
bmi.bmiHeader = bmp_header;
void* raw_bits = malloc(img_obj.image_stream.size());
if (raw_bits != nullptr) {
memcpy(raw_bits, img_obj.image_stream.data(), img_obj.image_stream.size());
}
StretchDIBits(hdc, 0, 0, 1500, 1500,
0, 0, pt_to_dip(img_obj.width, 96), pt_to_dip(img_obj.height, 96),
raw_bits, &bmi,
BI_RGB,
SRCCOPY);
free(raw_bits);
HDC hdc = GetDC(hwnd);
ReleaseDC(hwnd, hdc);
}
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd, msg, w_param, l_param);
}
std::vector<uint8_t> parser::inflate_stream_to_raw(const std::vector<uint8_t>& deflated_stream) {
z_stream stream{};
int ret = inflateInit(&stream);
if (ret != Z_OK) std::cout << "\ncould not init zlib\n";
std::vector<uint8_t> inflated_stream;
const int chunk_size = 16384; // 16 KB chunk size
std::vector<uint8_t> buffer(chunk_size);
stream.next_in = const_cast<Bytef*>(deflated_stream.data());
stream.avail_in = static_cast<uInt>(deflated_stream.size());
while (ret != Z_STREAM_END) {
stream.next_out = reinterpret_cast<Bytef*>(buffer.data());
stream.avail_out = chunk_size;
ret = inflate(&stream, Z_NO_FLUSH);
if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
std::cerr << "Error: zlib stream error. Error code: " << ret << std::endl;
inflateEnd(&stream);
return {}; // Return an empty vector on error
}
inflated_stream.insert(inflated_stream.end(), buffer.data(), buffer.data() + chunk_size - stream.avail_out);
}
inflateEnd(&stream);
return inflated_stream;
}
this supposedly works fine, a version of this that returns the data as a string has worked fine for the PDF contents without any issues. So, when it comes time to render it, I have a .bmp that is the same as the image in the PDF i am testing & its size is about 12 MB, & testing the size of my decompressed image stream it is also around 12 MB, so I don't see how it could be the decompression failing. But when I get it to render, it doesnt look right. its an image of a fox sitting in snow and what i get rendered is a repeating image with the snow rendered fine but the fox is blue for some reason. i have verified the image is RGB
I've tried many ways to even get the bitmap image to render & this is the only way that seems to work so I don't know how to fix it if it is a problem with winGDI or how I render it. here;s the code I use to render it:
#include <C:/code-libs/pdfCoder/pdfCoder.hpp>
#include <Windows.h>
#include <wingdi.h>
#include <string>
#include <sstream>
#include <vector>
#pragma comment(lib, "C:/code-libs/pdfCoder/pdfCoder.lib")
#pragma comment(lib, "gdi32.lib")
LRESULT CALLBACK main_wnd_proc(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param);
HWND main_wnd;
std::vector<image_object> img_objs;
parser pdf;
int pt_to_dip(double pt, double dpi) {
if (dpi == 96) return static_cast<int>(std::round((pt / 72) * dpi)); // if DPI is 96, then PX & DIP are equal
else return static_cast<int>(std::round(((pt / 72) * dpi) / (dpi / 96))); // else find PX first then convert to DIP
}
int WINAPI wWinMain(HINSTANCE cur_instance, HINSTANCE prev_instance, LPWSTR cmd_line, int cmd_count) {
const wchar_t* MAIN_WND = L"main_window";
WNDCLASS wc{};
wc.hInstance = cur_instance;
wc.lpszClassName = MAIN_WND;
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpfnWndProc = main_wnd_proc;
RegisterClass(&wc);
pdf.open("C:\\Coding Projects\\test field\\test4.pdf");
objects_root pdf_root = pdf.get_root();
page_object page = pdf.parse_page(pdf_root.pages[0]);
img_objs = pdf.parse_page_images(page);
main_wnd = CreateWindow(MAIN_WND, L"File Organiser",
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT,
1500, 1500,
nullptr, nullptr, nullptr, nullptr);
if (main_wnd == NULL) return 1;
ShowWindow(main_wnd, SW_SHOW);
UpdateWindow(main_wnd);
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK main_wnd_proc(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param) {
HDC hdc;
switch (msg) {
case WM_PAINT:
OutputDebugStringA("\nPAINTING...");
PAINTSTRUCT ps;
hdc = BeginPaint(hwnd, &ps);
for (const image_object& img_obj : img_objs) {
OutputDebugStringA("looping images");
std::stringstream ss;
ss << img_obj.image_stream.size();
OutputDebugStringA(ss.str().c_str());
BITMAPINFOHEADER bmp_header = {};
bmp_header.biSize = sizeof(BITMAPINFOHEADER);
bmp_header.biWidth = pt_to_dip(img_obj.width, 96);
bmp_header.biHeight = -pt_to_dip(img_obj.height, 96);
bmp_header.biPlanes = 1;
bmp_header.biBitCount = img_obj.bits_per_component * 3;
bmp_header.biCompression = BI_RGB;
bmp_header.biSizeImage = 0;
BITMAPINFO bmi = {};
bmi.bmiHeader = bmp_header;
void* raw_bits = malloc(img_obj.image_stream.size());
if (raw_bits != nullptr) {
memcpy(raw_bits, img_obj.image_stream.data(), img_obj.image_stream.size());
}
StretchDIBits(hdc, 0, 0, 1500, 1500,
0, 0, pt_to_dip(img_obj.width, 96), pt_to_dip(img_obj.height, 96),
raw_bits, &bmi,
BI_RGB,
SRCCOPY);
free(raw_bits);
HDC hdc = GetDC(hwnd);
ReleaseDC(hwnd, hdc);
}
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd, msg, w_param, l_param);
}