#include "litehtml_c.h"
#include <litehtml.h>
#include <litehtml/render_item.h>
#include <cstring>
#include <string>
#include <type_traits>
static_assert(std::is_standard_layout<litehtml::font_description>::value,
"font_description must be standard layout for safe reinterpret_cast");
static_assert(sizeof(litehtml::font_description) > 0,
"font_description must not be empty");
static_assert(std::is_standard_layout<litehtml::list_marker>::value,
"list_marker must be standard layout for safe reinterpret_cast");
static_assert(sizeof(litehtml::list_marker) > 0,
"list_marker must not be empty");
static_assert(std::is_standard_layout<litehtml::background_layer>::value,
"background_layer must be standard layout for safe reinterpret_cast");
static_assert(sizeof(litehtml::background_layer) > 0,
"background_layer must not be empty");
static_assert(sizeof(litehtml::background_layer::linear_gradient) > 0,
"linear_gradient must not be empty");
static_assert(sizeof(litehtml::background_layer::radial_gradient) > 0,
"radial_gradient must not be empty");
static_assert(sizeof(litehtml::background_layer::conic_gradient) > 0,
"conic_gradient must not be empty");
static lh_position_t to_c(const litehtml::position& p)
{
lh_position_t r;
r.x = p.x;
r.y = p.y;
r.width = p.width;
r.height = p.height;
return r;
}
static litehtml::position to_cpp(const lh_position_t& p)
{
return litehtml::position(p.x, p.y, p.width, p.height);
}
static lh_size_t to_c(const litehtml::size& s)
{
lh_size_t r;
r.width = s.width;
r.height = s.height;
return r;
}
static litehtml::size to_cpp(const lh_size_t& s)
{
return litehtml::size(s.width, s.height);
}
static lh_web_color_t to_c(const litehtml::web_color& c)
{
lh_web_color_t r;
r.red = c.red;
r.green = c.green;
r.blue = c.blue;
r.alpha = c.alpha;
r.is_current_color = c.is_current_color ? 1 : 0;
return r;
}
static litehtml::web_color to_cpp(const lh_web_color_t& c)
{
litehtml::web_color r(c.red, c.green, c.blue, c.alpha);
r.is_current_color = (c.is_current_color != 0);
return r;
}
static lh_font_metrics_t to_c(const litehtml::font_metrics& m)
{
lh_font_metrics_t r;
r.font_size = m.font_size;
r.height = m.height;
r.ascent = m.ascent;
r.descent = m.descent;
r.x_height = m.x_height;
r.ch_width = m.ch_width;
r.draw_spaces = m.draw_spaces ? 1 : 0;
r.sub_shift = m.sub_shift;
r.super_shift = m.super_shift;
return r;
}
static void from_c(const lh_font_metrics_t& c, litehtml::font_metrics& out)
{
out.font_size = c.font_size;
out.height = c.height;
out.ascent = c.ascent;
out.descent = c.descent;
out.x_height = c.x_height;
out.ch_width = c.ch_width;
out.draw_spaces = (c.draw_spaces != 0);
out.sub_shift = c.sub_shift;
out.super_shift = c.super_shift;
}
static lh_border_radiuses_t to_c(const litehtml::border_radiuses& br)
{
lh_border_radiuses_t r;
r.top_left_x = br.top_left_x;
r.top_left_y = br.top_left_y;
r.top_right_x = br.top_right_x;
r.top_right_y = br.top_right_y;
r.bottom_right_x = br.bottom_right_x;
r.bottom_right_y = br.bottom_right_y;
r.bottom_left_x = br.bottom_left_x;
r.bottom_left_y = br.bottom_left_y;
return r;
}
static litehtml::border_radiuses to_cpp(const lh_border_radiuses_t& br)
{
litehtml::border_radiuses r;
r.top_left_x = br.top_left_x;
r.top_left_y = br.top_left_y;
r.top_right_x = br.top_right_x;
r.top_right_y = br.top_right_y;
r.bottom_right_x = br.bottom_right_x;
r.bottom_right_y = br.bottom_right_y;
r.bottom_left_x = br.bottom_left_x;
r.bottom_left_y = br.bottom_left_y;
return r;
}
static lh_border_t to_c(const litehtml::border& b)
{
lh_border_t r;
r.width = b.width;
r.style = static_cast<int>(b.style);
r.color = to_c(b.color);
return r;
}
static lh_borders_t to_c(const litehtml::borders& b)
{
lh_borders_t r;
r.left = to_c(b.left);
r.top = to_c(b.top);
r.right = to_c(b.right);
r.bottom = to_c(b.bottom);
r.radius = to_c(b.radius);
return r;
}
static lh_media_features_t to_c(const litehtml::media_features& mf)
{
lh_media_features_t r;
r.type = static_cast<int>(mf.type);
r.width = mf.width;
r.height = mf.height;
r.device_width = mf.device_width;
r.device_height = mf.device_height;
r.color = mf.color;
r.color_index = mf.color_index;
r.monochrome = mf.monochrome;
r.resolution = mf.resolution;
return r;
}
static void from_c(const lh_media_features_t& c, litehtml::media_features& out)
{
out.type = static_cast<litehtml::media_type>(c.type);
out.width = c.width;
out.height = c.height;
out.device_width = c.device_width;
out.device_height = c.device_height;
out.color = c.color;
out.color_index = c.color_index;
out.monochrome = c.monochrome;
out.resolution = c.resolution;
}
static lh_point_t to_c(const litehtml::pointF& p)
{
lh_point_t r;
r.x = p.x;
r.y = p.y;
return r;
}
class CDocumentContainer;
struct lh_document_internal
{
litehtml::document::ptr doc;
CDocumentContainer* container;
};
class CDocumentContainer : public litehtml::document_container
{
public:
lh_container_vtable_t* vtable;
void* user_data;
CDocumentContainer(lh_container_vtable_t* vt, void* ud)
: vtable(vt), user_data(ud) {}
litehtml::uint_ptr create_font(
const litehtml::font_description& descr,
const litehtml::document* ,
litehtml::font_metrics* fm) override
{
if (!vtable->create_font) return 0;
auto* fd = reinterpret_cast<const lh_font_description_t*>(&descr);
lh_font_metrics_t c_fm = {};
litehtml::uint_ptr result = vtable->create_font(user_data, fd, &c_fm);
if (fm)
from_c(c_fm, *fm);
return result;
}
void delete_font(litehtml::uint_ptr hFont) override
{
if (vtable->delete_font)
vtable->delete_font(user_data, hFont);
}
litehtml::pixel_t text_width(const char* text, litehtml::uint_ptr hFont) override
{
if (!vtable->text_width) return 0;
return vtable->text_width(user_data, text, hFont);
}
void draw_text(litehtml::uint_ptr hdc,
const char* text,
litehtml::uint_ptr hFont,
litehtml::web_color color,
const litehtml::position& pos) override
{
if (!vtable->draw_text) return;
vtable->draw_text(user_data, hdc, text, hFont, to_c(color), to_c(pos));
}
litehtml::pixel_t pt_to_px(float pt) const override
{
if (!vtable->pt_to_px) return pt;
return vtable->pt_to_px(user_data, pt);
}
litehtml::pixel_t get_default_font_size() const override
{
if (!vtable->get_default_font_size) return 16;
return vtable->get_default_font_size(user_data);
}
const char* get_default_font_name() const override
{
if (!vtable->get_default_font_name) return "serif";
return vtable->get_default_font_name(user_data);
}
void draw_list_marker(litehtml::uint_ptr hdc,
const litehtml::list_marker& marker) override
{
if (!vtable->draw_list_marker) return;
auto* m = reinterpret_cast<const lh_list_marker_t*>(&marker);
vtable->draw_list_marker(user_data, hdc, m);
}
void load_image(const char* src, const char* baseurl,
bool redraw_on_ready) override
{
if (!vtable->load_image) return;
vtable->load_image(user_data, src, baseurl, redraw_on_ready ? 1 : 0);
}
void get_image_size(const char* src, const char* baseurl,
litehtml::size& sz) override
{
if (!vtable->get_image_size) return;
lh_size_t c_sz = to_c(sz);
vtable->get_image_size(user_data, src, baseurl, &c_sz);
sz.width = c_sz.width;
sz.height = c_sz.height;
}
void draw_image(litehtml::uint_ptr hdc,
const litehtml::background_layer& layer,
const std::string& url,
const std::string& base_url) override
{
if (!vtable->draw_image) return;
auto* bl = reinterpret_cast<const lh_background_layer_t*>(&layer);
vtable->draw_image(user_data, hdc, bl, url.c_str(), base_url.c_str());
}
void draw_solid_fill(litehtml::uint_ptr hdc,
const litehtml::background_layer& layer,
const litehtml::web_color& color) override
{
if (!vtable->draw_solid_fill) return;
auto* bl = reinterpret_cast<const lh_background_layer_t*>(&layer);
vtable->draw_solid_fill(user_data, hdc, bl, to_c(color));
}
void draw_linear_gradient(
litehtml::uint_ptr hdc,
const litehtml::background_layer& layer,
const litehtml::background_layer::linear_gradient& gradient) override
{
if (!vtable->draw_linear_gradient) return;
auto* bl = reinterpret_cast<const lh_background_layer_t*>(&layer);
auto* lg = reinterpret_cast<const lh_linear_gradient_t*>(&gradient);
vtable->draw_linear_gradient(user_data, hdc, bl, lg);
}
void draw_radial_gradient(
litehtml::uint_ptr hdc,
const litehtml::background_layer& layer,
const litehtml::background_layer::radial_gradient& gradient) override
{
if (!vtable->draw_radial_gradient) return;
auto* bl = reinterpret_cast<const lh_background_layer_t*>(&layer);
auto* rg = reinterpret_cast<const lh_radial_gradient_t*>(&gradient);
vtable->draw_radial_gradient(user_data, hdc, bl, rg);
}
void draw_conic_gradient(
litehtml::uint_ptr hdc,
const litehtml::background_layer& layer,
const litehtml::background_layer::conic_gradient& gradient) override
{
if (!vtable->draw_conic_gradient) return;
auto* bl = reinterpret_cast<const lh_background_layer_t*>(&layer);
auto* cg = reinterpret_cast<const lh_conic_gradient_t*>(&gradient);
vtable->draw_conic_gradient(user_data, hdc, bl, cg);
}
void draw_borders(litehtml::uint_ptr hdc,
const litehtml::borders& borders,
const litehtml::position& draw_pos,
bool root) override
{
if (!vtable->draw_borders) return;
vtable->draw_borders(user_data, hdc,
to_c(borders), to_c(draw_pos),
root ? 1 : 0);
}
void set_caption(const char* caption) override
{
if (vtable->set_caption)
vtable->set_caption(user_data, caption);
}
void set_base_url(const char* base_url) override
{
if (vtable->set_base_url)
vtable->set_base_url(user_data, base_url);
}
void link(const std::shared_ptr<litehtml::document>& ,
const litehtml::element::ptr& ) override
{
if (vtable->link)
vtable->link(user_data);
}
void on_anchor_click(const char* url,
const litehtml::element::ptr& ) override
{
if (vtable->on_anchor_click)
vtable->on_anchor_click(user_data, url);
}
void on_mouse_event(const litehtml::element::ptr& ,
litehtml::mouse_event event) override
{
if (vtable->on_mouse_event)
vtable->on_mouse_event(user_data, static_cast<int>(event));
}
void set_cursor(const char* cursor) override
{
if (vtable->set_cursor)
vtable->set_cursor(user_data, cursor);
}
void transform_text(litehtml::string& text,
litehtml::text_transform tt) override
{
if (!vtable->transform_text) return;
litehtml::string result = text;
litehtml::string* result_ptr = &result;
vtable->transform_text(
user_data,
text.c_str(),
static_cast<int>(tt),
[](void* ctx, const char* r) {
auto* p = static_cast<litehtml::string*>(ctx);
*p = r ? r : "";
},
result_ptr);
text = result;
}
void import_css(litehtml::string& text,
const litehtml::string& url,
litehtml::string& baseurl) override
{
if (!vtable->import_css) return;
litehtml::string result;
litehtml::string* result_ptr = &result;
litehtml::string new_baseurl;
litehtml::string* baseurl_ptr = &new_baseurl;
auto set_string = [](void* ctx, const char* r) {
auto* p = static_cast<litehtml::string*>(ctx);
*p = r ? r : "";
};
vtable->import_css(
user_data,
url.c_str(),
baseurl.c_str(),
set_string,
result_ptr,
set_string,
baseurl_ptr);
text = result;
if (!new_baseurl.empty()) {
baseurl = new_baseurl;
}
}
void set_clip(const litehtml::position& pos,
const litehtml::border_radiuses& bdr_radius) override
{
if (vtable->set_clip)
vtable->set_clip(user_data, to_c(pos), to_c(bdr_radius));
}
void del_clip() override
{
if (vtable->del_clip)
vtable->del_clip(user_data);
}
void get_viewport(litehtml::position& viewport) const override
{
if (!vtable->get_viewport) return;
lh_position_t c_vp = to_c(viewport);
vtable->get_viewport(user_data, &c_vp);
viewport = to_cpp(c_vp);
}
litehtml::element::ptr create_element(
const char* ,
const litehtml::string_map& ,
const std::shared_ptr<litehtml::document>& ) override
{
return nullptr;
}
void get_media_features(litehtml::media_features& media) const override
{
if (!vtable->get_media_features) return;
lh_media_features_t c_mf = to_c(media);
vtable->get_media_features(user_data, &c_mf);
from_c(c_mf, media);
}
void get_language(litehtml::string& language,
litehtml::string& culture) const override
{
if (!vtable->get_language) return;
struct LangCtx {
litehtml::string* lang;
litehtml::string* cult;
} ctx = {&language, &culture};
vtable->get_language(
user_data,
[](void* c, const char* lang, const char* cult) {
auto* lc = static_cast<LangCtx*>(c);
*lc->lang = lang ? lang : "";
*lc->cult = cult ? cult : "";
},
&ctx);
}
};
extern "C" {
const char* lh_font_description_family(const lh_font_description_t* fd)
{
try {
if (!fd) return "";
const auto* d = reinterpret_cast<const litehtml::font_description*>(fd);
return d->family.c_str();
} catch (...) {
return "";
}
}
float lh_font_description_size(const lh_font_description_t* fd)
{
try {
if (!fd) return 0.0f;
const auto* d = reinterpret_cast<const litehtml::font_description*>(fd);
return d->size;
} catch (...) {
return 0.0f;
}
}
int lh_font_description_style(const lh_font_description_t* fd)
{
try {
if (!fd) return 0;
const auto* d = reinterpret_cast<const litehtml::font_description*>(fd);
return static_cast<int>(d->style);
} catch (...) {
return 0;
}
}
int lh_font_description_weight(const lh_font_description_t* fd)
{
try {
if (!fd) return 0;
const auto* d = reinterpret_cast<const litehtml::font_description*>(fd);
return d->weight;
} catch (...) {
return 0;
}
}
int lh_font_description_decoration_line(const lh_font_description_t* fd)
{
try {
if (!fd) return 0;
const auto* d = reinterpret_cast<const litehtml::font_description*>(fd);
return d->decoration_line;
} catch (...) {
return 0;
}
}
int lh_font_description_decoration_thickness_is_predefined(const lh_font_description_t* fd)
{
try {
if (!fd) return 1;
const auto* d = reinterpret_cast<const litehtml::font_description*>(fd);
return d->decoration_thickness.is_predefined() ? 1 : 0;
} catch (...) {
return 1;
}
}
int lh_font_description_decoration_thickness_predef(const lh_font_description_t* fd)
{
try {
if (!fd) return 0;
const auto* d = reinterpret_cast<const litehtml::font_description*>(fd);
return d->decoration_thickness.is_predefined() ? d->decoration_thickness.predef() : 0;
} catch (...) {
return 0;
}
}
float lh_font_description_decoration_thickness_value(const lh_font_description_t* fd)
{
try {
if (!fd) return 0.0f;
const auto* d = reinterpret_cast<const litehtml::font_description*>(fd);
return d->decoration_thickness.is_predefined() ? 0.0f : d->decoration_thickness.val();
} catch (...) {
return 0.0f;
}
}
int lh_font_description_decoration_style(const lh_font_description_t* fd)
{
try {
if (!fd) return 0;
const auto* d = reinterpret_cast<const litehtml::font_description*>(fd);
return static_cast<int>(d->decoration_style);
} catch (...) {
return 0;
}
}
lh_web_color_t lh_font_description_decoration_color(const lh_font_description_t* fd)
{
try {
if (!fd) return {};
const auto* d = reinterpret_cast<const litehtml::font_description*>(fd);
return to_c(d->decoration_color);
} catch (...) {
return {};
}
}
const char* lh_font_description_emphasis_style(const lh_font_description_t* fd)
{
try {
if (!fd) return "";
const auto* d = reinterpret_cast<const litehtml::font_description*>(fd);
return d->emphasis_style.c_str();
} catch (...) {
return "";
}
}
lh_web_color_t lh_font_description_emphasis_color(const lh_font_description_t* fd)
{
try {
if (!fd) return {};
const auto* d = reinterpret_cast<const litehtml::font_description*>(fd);
return to_c(d->emphasis_color);
} catch (...) {
return {};
}
}
int lh_font_description_emphasis_position(const lh_font_description_t* fd)
{
try {
if (!fd) return 0;
const auto* d = reinterpret_cast<const litehtml::font_description*>(fd);
return d->emphasis_position;
} catch (...) {
return 0;
}
}
const char* lh_list_marker_image(const lh_list_marker_t* m)
{
try {
if (!m) return "";
const auto* mk = reinterpret_cast<const litehtml::list_marker*>(m);
return mk->image.c_str();
} catch (...) {
return "";
}
}
const char* lh_list_marker_baseurl(const lh_list_marker_t* m)
{
try {
if (!m) return "";
const auto* mk = reinterpret_cast<const litehtml::list_marker*>(m);
return mk->baseurl;
} catch (...) {
return "";
}
}
int lh_list_marker_type(const lh_list_marker_t* m)
{
try {
if (!m) return 0;
const auto* mk = reinterpret_cast<const litehtml::list_marker*>(m);
return static_cast<int>(mk->marker_type);
} catch (...) {
return 0;
}
}
lh_web_color_t lh_list_marker_color(const lh_list_marker_t* m)
{
try {
if (!m) return {};
const auto* mk = reinterpret_cast<const litehtml::list_marker*>(m);
return to_c(mk->color);
} catch (...) {
return {};
}
}
lh_position_t lh_list_marker_pos(const lh_list_marker_t* m)
{
try {
if (!m) return {};
const auto* mk = reinterpret_cast<const litehtml::list_marker*>(m);
return to_c(mk->pos);
} catch (...) {
return {};
}
}
int lh_list_marker_index(const lh_list_marker_t* m)
{
try {
if (!m) return 0;
const auto* mk = reinterpret_cast<const litehtml::list_marker*>(m);
return mk->index;
} catch (...) {
return 0;
}
}
uintptr_t lh_list_marker_font(const lh_list_marker_t* m)
{
try {
if (!m) return 0;
const auto* mk = reinterpret_cast<const litehtml::list_marker*>(m);
return mk->font;
} catch (...) {
return 0;
}
}
lh_position_t lh_background_layer_border_box(const lh_background_layer_t* layer)
{
try {
if (!layer) return {};
const auto* bl = reinterpret_cast<const litehtml::background_layer*>(layer);
return to_c(bl->border_box);
} catch (...) {
return {};
}
}
lh_border_radiuses_t lh_background_layer_border_radius(const lh_background_layer_t* layer)
{
try {
if (!layer) return {};
const auto* bl = reinterpret_cast<const litehtml::background_layer*>(layer);
return to_c(bl->border_radius);
} catch (...) {
return {};
}
}
lh_position_t lh_background_layer_clip_box(const lh_background_layer_t* layer)
{
try {
if (!layer) return {};
const auto* bl = reinterpret_cast<const litehtml::background_layer*>(layer);
return to_c(bl->clip_box);
} catch (...) {
return {};
}
}
lh_position_t lh_background_layer_origin_box(const lh_background_layer_t* layer)
{
try {
if (!layer) return {};
const auto* bl = reinterpret_cast<const litehtml::background_layer*>(layer);
return to_c(bl->origin_box);
} catch (...) {
return {};
}
}
int lh_background_layer_attachment(const lh_background_layer_t* layer)
{
try {
if (!layer) return 0;
const auto* bl = reinterpret_cast<const litehtml::background_layer*>(layer);
return static_cast<int>(bl->attachment);
} catch (...) {
return 0;
}
}
int lh_background_layer_repeat(const lh_background_layer_t* layer)
{
try {
if (!layer) return 0;
const auto* bl = reinterpret_cast<const litehtml::background_layer*>(layer);
return static_cast<int>(bl->repeat);
} catch (...) {
return 0;
}
}
int lh_background_layer_is_root(const lh_background_layer_t* layer)
{
try {
if (!layer) return 0;
const auto* bl = reinterpret_cast<const litehtml::background_layer*>(layer);
return bl->is_root ? 1 : 0;
} catch (...) {
return 0;
}
}
lh_point_t lh_linear_gradient_start(const lh_linear_gradient_t* g)
{
try {
if (!g) return {};
const auto* lg = reinterpret_cast<
const litehtml::background_layer::linear_gradient*>(g);
return to_c(lg->start);
} catch (...) {
return {};
}
}
lh_point_t lh_linear_gradient_end(const lh_linear_gradient_t* g)
{
try {
if (!g) return {};
const auto* lg = reinterpret_cast<
const litehtml::background_layer::linear_gradient*>(g);
return to_c(lg->end);
} catch (...) {
return {};
}
}
int lh_linear_gradient_color_points_count(const lh_linear_gradient_t* g)
{
try {
if (!g) return 0;
const auto* lg = reinterpret_cast<
const litehtml::background_layer::linear_gradient*>(g);
return static_cast<int>(lg->color_points.size());
} catch (...) {
return 0;
}
}
float lh_linear_gradient_color_point_offset(const lh_linear_gradient_t* g,
int idx)
{
try {
if (!g) return 0.0f;
const auto* lg = reinterpret_cast<
const litehtml::background_layer::linear_gradient*>(g);
if (idx < 0 || idx >= static_cast<int>(lg->color_points.size()))
return 0.0f;
return lg->color_points[idx].offset;
} catch (...) {
return 0.0f;
}
}
lh_web_color_t lh_linear_gradient_color_point_color(
const lh_linear_gradient_t* g, int idx)
{
try {
if (!g) return {};
const auto* lg = reinterpret_cast<
const litehtml::background_layer::linear_gradient*>(g);
if (idx < 0 || idx >= static_cast<int>(lg->color_points.size()))
{
lh_web_color_t zero = {};
return zero;
}
return to_c(lg->color_points[idx].color);
} catch (...) {
return {};
}
}
int lh_linear_gradient_color_space(const lh_linear_gradient_t* g)
{
try {
if (!g) return 0;
const auto* lg = reinterpret_cast<
const litehtml::background_layer::linear_gradient*>(g);
return static_cast<int>(lg->color_space);
} catch (...) {
return 0;
}
}
int lh_linear_gradient_hue_interpolation(const lh_linear_gradient_t* g)
{
try {
if (!g) return 0;
const auto* lg = reinterpret_cast<
const litehtml::background_layer::linear_gradient*>(g);
return static_cast<int>(lg->hue_interpolation);
} catch (...) {
return 0;
}
}
lh_point_t lh_radial_gradient_position(const lh_radial_gradient_t* g)
{
try {
if (!g) return {};
const auto* rg = reinterpret_cast<
const litehtml::background_layer::radial_gradient*>(g);
return to_c(rg->position);
} catch (...) {
return {};
}
}
lh_point_t lh_radial_gradient_radius(const lh_radial_gradient_t* g)
{
try {
if (!g) return {};
const auto* rg = reinterpret_cast<
const litehtml::background_layer::radial_gradient*>(g);
return to_c(rg->radius);
} catch (...) {
return {};
}
}
int lh_radial_gradient_color_points_count(const lh_radial_gradient_t* g)
{
try {
if (!g) return 0;
const auto* rg = reinterpret_cast<
const litehtml::background_layer::radial_gradient*>(g);
return static_cast<int>(rg->color_points.size());
} catch (...) {
return 0;
}
}
float lh_radial_gradient_color_point_offset(const lh_radial_gradient_t* g,
int idx)
{
try {
if (!g) return 0.0f;
const auto* rg = reinterpret_cast<
const litehtml::background_layer::radial_gradient*>(g);
if (idx < 0 || idx >= static_cast<int>(rg->color_points.size()))
return 0.0f;
return rg->color_points[idx].offset;
} catch (...) {
return 0.0f;
}
}
lh_web_color_t lh_radial_gradient_color_point_color(
const lh_radial_gradient_t* g, int idx)
{
try {
if (!g) return {};
const auto* rg = reinterpret_cast<
const litehtml::background_layer::radial_gradient*>(g);
if (idx < 0 || idx >= static_cast<int>(rg->color_points.size()))
{
lh_web_color_t zero = {};
return zero;
}
return to_c(rg->color_points[idx].color);
} catch (...) {
return {};
}
}
int lh_radial_gradient_color_space(const lh_radial_gradient_t* g)
{
try {
if (!g) return 0;
const auto* rg = reinterpret_cast<
const litehtml::background_layer::radial_gradient*>(g);
return static_cast<int>(rg->color_space);
} catch (...) {
return 0;
}
}
int lh_radial_gradient_hue_interpolation(const lh_radial_gradient_t* g)
{
try {
if (!g) return 0;
const auto* rg = reinterpret_cast<
const litehtml::background_layer::radial_gradient*>(g);
return static_cast<int>(rg->hue_interpolation);
} catch (...) {
return 0;
}
}
lh_point_t lh_conic_gradient_position(const lh_conic_gradient_t* g)
{
try {
if (!g) return {};
const auto* cg = reinterpret_cast<
const litehtml::background_layer::conic_gradient*>(g);
return to_c(cg->position);
} catch (...) {
return {};
}
}
float lh_conic_gradient_angle(const lh_conic_gradient_t* g)
{
try {
if (!g) return 0.0f;
const auto* cg = reinterpret_cast<
const litehtml::background_layer::conic_gradient*>(g);
return cg->angle;
} catch (...) {
return 0.0f;
}
}
float lh_conic_gradient_radius(const lh_conic_gradient_t* g)
{
try {
if (!g) return 0.0f;
const auto* cg = reinterpret_cast<
const litehtml::background_layer::conic_gradient*>(g);
return cg->radius;
} catch (...) {
return 0.0f;
}
}
int lh_conic_gradient_color_points_count(const lh_conic_gradient_t* g)
{
try {
if (!g) return 0;
const auto* cg = reinterpret_cast<
const litehtml::background_layer::conic_gradient*>(g);
return static_cast<int>(cg->color_points.size());
} catch (...) {
return 0;
}
}
float lh_conic_gradient_color_point_offset(const lh_conic_gradient_t* g,
int idx)
{
try {
if (!g) return 0.0f;
const auto* cg = reinterpret_cast<
const litehtml::background_layer::conic_gradient*>(g);
if (idx < 0 || idx >= static_cast<int>(cg->color_points.size()))
return 0.0f;
return cg->color_points[idx].offset;
} catch (...) {
return 0.0f;
}
}
lh_web_color_t lh_conic_gradient_color_point_color(
const lh_conic_gradient_t* g, int idx)
{
try {
if (!g) return {};
const auto* cg = reinterpret_cast<
const litehtml::background_layer::conic_gradient*>(g);
if (idx < 0 || idx >= static_cast<int>(cg->color_points.size()))
{
lh_web_color_t zero = {};
return zero;
}
return to_c(cg->color_points[idx].color);
} catch (...) {
return {};
}
}
int lh_conic_gradient_color_space(const lh_conic_gradient_t* g)
{
try {
if (!g) return 0;
const auto* cg = reinterpret_cast<
const litehtml::background_layer::conic_gradient*>(g);
return static_cast<int>(cg->color_space);
} catch (...) {
return 0;
}
}
int lh_conic_gradient_hue_interpolation(const lh_conic_gradient_t* g)
{
try {
if (!g) return 0;
const auto* cg = reinterpret_cast<
const litehtml::background_layer::conic_gradient*>(g);
return static_cast<int>(cg->hue_interpolation);
} catch (...) {
return 0;
}
}
lh_document_t* lh_document_create_from_string(
const char* html,
lh_container_vtable_t* vtable,
void* user_data,
const char* master_css,
const char* user_styles)
{
try {
if (!html || !vtable) return nullptr;
auto* container = new CDocumentContainer(vtable, user_data);
std::string master = master_css ? master_css : litehtml::master_css;
std::string user = user_styles ? user_styles : "";
litehtml::document::ptr doc =
litehtml::document::createFromString(html, container, master, user);
if (!doc) {
delete container;
return nullptr;
}
auto* internal = new lh_document_internal;
internal->doc = doc;
internal->container = container;
return reinterpret_cast<lh_document_t*>(internal);
} catch (...) {
return nullptr;
}
}
void lh_document_destroy(lh_document_t* doc)
{
try {
if (!doc) return;
auto* internal = reinterpret_cast<lh_document_internal*>(doc);
auto* container = internal->container;
delete internal;
delete container;
} catch (...) {
}
}
float lh_document_render(lh_document_t* doc, float max_width)
{
try {
if (!doc) return 0;
auto* internal = reinterpret_cast<lh_document_internal*>(doc);
return internal->doc->render(max_width);
} catch (...) {
return 0;
}
}
void lh_document_draw(lh_document_t* doc,
uintptr_t hdc,
float x, float y,
const lh_position_t* clip)
{
try {
if (!doc) return;
auto* internal = reinterpret_cast<lh_document_internal*>(doc);
if (clip) {
litehtml::position cpp_clip = to_cpp(*clip);
internal->doc->draw(hdc, x, y, &cpp_clip);
} else {
internal->doc->draw(hdc, x, y, nullptr);
}
} catch (...) {
}
}
float lh_document_width(const lh_document_t* doc)
{
try {
if (!doc) return 0;
const auto* internal =
reinterpret_cast<const lh_document_internal*>(doc);
return internal->doc->width();
} catch (...) {
return 0;
}
}
float lh_document_height(const lh_document_t* doc)
{
try {
if (!doc) return 0;
const auto* internal =
reinterpret_cast<const lh_document_internal*>(doc);
return internal->doc->height();
} catch (...) {
return 0;
}
}
void lh_document_add_stylesheet(lh_document_t* doc,
const char* css_text,
const char* baseurl,
const char* media)
{
try {
if (!doc || !css_text || !css_text[0]) return;
auto* internal = reinterpret_cast<lh_document_internal*>(doc);
litehtml::css stylesheet;
litehtml::media_query_list_list::ptr mq;
if (media && media[0]) {
auto mq_list = litehtml::parse_media_query_list(media, internal->doc);
mq = std::make_shared<litehtml::media_query_list_list>();
mq->add(mq_list);
}
stylesheet.parse_css_stylesheet(
css_text,
baseurl ? baseurl : "",
internal->doc,
mq);
stylesheet.sort_selectors();
auto root = internal->doc->root();
if (root) {
root->apply_stylesheet(stylesheet);
root->compute_styles();
}
} catch (...) {
}
}
lh_element_t* lh_document_root(lh_document_t* doc)
{
try {
if (!doc) return nullptr;
auto* internal = reinterpret_cast<lh_document_internal*>(doc);
auto root = internal->doc->root();
return reinterpret_cast<lh_element_t*>(root.get());
} catch (...) {
return nullptr;
}
}
void lh_document_append_children_from_string(lh_document_t* doc,
lh_element_t* parent,
const char* html,
int replace_existing)
{
try {
if (!doc || !parent || !html) return;
auto* internal = reinterpret_cast<lh_document_internal*>(doc);
auto* elem = reinterpret_cast<litehtml::element*>(parent);
internal->doc->append_children_from_string(*elem, html, replace_existing != 0);
} catch (...) {
}
}
int lh_document_on_mouse_over(lh_document_t* doc,
float x, float y,
float client_x, float client_y)
{
try {
if (!doc) return 0;
auto* internal = reinterpret_cast<lh_document_internal*>(doc);
litehtml::position::vector redraw_boxes;
bool changed = internal->doc->on_mouse_over(x, y, client_x, client_y,
redraw_boxes);
return changed ? 1 : 0;
} catch (...) {
return 0;
}
}
int lh_document_on_lbutton_down(lh_document_t* doc,
float x, float y,
float client_x, float client_y)
{
try {
if (!doc) return 0;
auto* internal = reinterpret_cast<lh_document_internal*>(doc);
litehtml::position::vector redraw_boxes;
bool changed = internal->doc->on_lbutton_down(x, y, client_x, client_y,
redraw_boxes);
return changed ? 1 : 0;
} catch (...) {
return 0;
}
}
int lh_document_on_lbutton_up(lh_document_t* doc,
float x, float y,
float client_x, float client_y)
{
try {
if (!doc) return 0;
auto* internal = reinterpret_cast<lh_document_internal*>(doc);
litehtml::position::vector redraw_boxes;
bool changed = internal->doc->on_lbutton_up(x, y, client_x, client_y,
redraw_boxes);
return changed ? 1 : 0;
} catch (...) {
return 0;
}
}
int lh_document_on_mouse_leave(lh_document_t* doc)
{
try {
if (!doc) return 0;
auto* internal = reinterpret_cast<lh_document_internal*>(doc);
litehtml::position::vector redraw_boxes;
bool changed = internal->doc->on_mouse_leave(redraw_boxes);
return changed ? 1 : 0;
} catch (...) {
return 0;
}
}
int lh_document_media_changed(lh_document_t* doc)
{
try {
if (!doc) return 0;
auto* internal = reinterpret_cast<lh_document_internal*>(doc);
return internal->doc->media_changed() ? 1 : 0;
} catch (...) {
return 0;
}
}
lh_element_t* lh_element_parent(lh_element_t* el)
{
try {
if (!el) return nullptr;
auto* elem = reinterpret_cast<litehtml::element*>(el);
auto parent = elem->parent();
if (!parent) return nullptr;
return reinterpret_cast<lh_element_t*>(parent.get());
} catch (...) {
return nullptr;
}
}
int lh_element_children_count(lh_element_t* el)
{
try {
if (!el) return 0;
auto* elem = reinterpret_cast<litehtml::element*>(el);
return static_cast<int>(elem->children().size());
} catch (...) {
return 0;
}
}
lh_element_t* lh_element_child_at(lh_element_t* el, int index)
{
try {
if (!el) return nullptr;
auto* elem = reinterpret_cast<litehtml::element*>(el);
const auto& kids = elem->children();
if (index < 0 || index >= static_cast<int>(kids.size()))
return nullptr;
auto it = kids.begin();
std::advance(it, index);
return reinterpret_cast<lh_element_t*>(it->get());
} catch (...) {
return nullptr;
}
}
int lh_element_is_text(lh_element_t* el)
{
try {
if (!el) return 0;
auto* elem = reinterpret_cast<litehtml::element*>(el);
return elem->is_text() ? 1 : 0;
} catch (...) {
return 0;
}
}
uintptr_t lh_element_get_font(lh_element_t* el)
{
try {
if (!el) return 0;
auto* elem = reinterpret_cast<litehtml::element*>(el);
return elem->css().get_font();
} catch (...) {
return 0;
}
}
float lh_element_get_font_size(lh_element_t* el)
{
try {
if (!el) return 0.0f;
auto* elem = reinterpret_cast<litehtml::element*>(el);
return elem->css().get_font_size();
} catch (...) {
return 0.0f;
}
}
lh_element_t* lh_element_select_one(lh_element_t* el, const char* selector)
{
try {
if (!el || !selector) return nullptr;
auto* elem = reinterpret_cast<litehtml::element*>(el);
auto found = elem->select_one(selector);
if (!found) return nullptr;
return reinterpret_cast<lh_element_t*>(found.get());
} catch (...) {
return nullptr;
}
}
void lh_element_get_placement(lh_element_t* el, lh_position_t* pos)
{
try {
if (!el || !pos) return;
auto* elem = reinterpret_cast<litehtml::element*>(el);
litehtml::position p = elem->get_placement();
*pos = to_c(p);
} catch (...) {
}
}
void lh_element_get_text(lh_element_t* el,
void (*cb)(void* ctx, const char* text),
void* ctx)
{
try {
if (!el || !cb) return;
auto* elem = reinterpret_cast<litehtml::element*>(el);
litehtml::string text;
elem->get_text(text);
cb(ctx, text.c_str());
} catch (...) {
}
}
lh_element_t* lh_document_get_element_by_point(lh_document_t* doc,
float x, float y,
float client_x, float client_y)
{
try {
if (!doc) return nullptr;
auto* internal = reinterpret_cast<lh_document_internal*>(doc);
auto root_render = internal->doc->root_render();
if (!root_render) return nullptr;
auto el = root_render->get_element_by_point(x, y, client_x, client_y,
[](const std::shared_ptr<litehtml::render_item>&) { return true; });
if (!el) return nullptr;
return reinterpret_cast<lh_element_t*>(el.get());
} catch (...) {
return nullptr;
}
}
static void compute_ri_offset(const std::shared_ptr<litehtml::render_item>& ri,
float& ox, float& oy)
{
litehtml::position placement = ri->get_placement();
litehtml::position pos = ri->pos();
ox = placement.x - pos.x;
oy = placement.y - pos.y;
}
int lh_element_get_inline_boxes_count(lh_element_t* el)
{
try {
if (!el) return 0;
auto* elem = reinterpret_cast<litehtml::element*>(el);
auto ri = elem->get_render_item();
if (!ri) return 0;
litehtml::position::vector boxes;
ri->get_inline_boxes(boxes);
return static_cast<int>(boxes.size());
} catch (...) {
return 0;
}
}
void lh_element_get_inline_box_at(lh_element_t* el, int index, lh_position_t* pos)
{
try {
if (!el || !pos) return;
auto* elem = reinterpret_cast<litehtml::element*>(el);
auto ri = elem->get_render_item();
if (!ri) return;
litehtml::position::vector boxes;
ri->get_inline_boxes(boxes);
if (index < 0 || index >= static_cast<int>(boxes.size()))
return;
float ox, oy;
compute_ri_offset(ri, ox, oy);
litehtml::position box = boxes[index];
box.x += ox;
box.y += oy;
*pos = to_c(box);
} catch (...) {
}
}
void lh_element_get_inline_boxes(lh_element_t* el, lh_inline_box_callback cb, void* ctx)
{
try {
if (!el || !cb) return;
auto* elem = reinterpret_cast<litehtml::element*>(el);
auto ri = elem->get_render_item();
if (!ri) return;
litehtml::position::vector boxes;
ri->get_inline_boxes(boxes);
if (boxes.empty()) return;
float ox, oy;
compute_ri_offset(ri, ox, oy);
for (const auto& box_ : boxes)
{
litehtml::position abs = box_;
abs.x += ox;
abs.y += oy;
lh_position_t pos = to_c(abs);
cb(&pos, ctx);
}
} catch (...) {
}
}
int lh_element_get_text_align(lh_element_t* el)
{
try {
if (!el) return 0;
auto* elem = reinterpret_cast<litehtml::element*>(el);
return static_cast<int>(elem->css().get_text_align());
} catch (...) {
return 0;
}
}
float lh_element_get_line_height(lh_element_t* el)
{
try {
if (!el) return 0.0f;
auto* elem = reinterpret_cast<litehtml::element*>(el);
return elem->css().line_height().computed_value;
} catch (...) {
return 0.0f;
}
}
}