Added support for local image attachments to the Litehtml plugin
[claws.git] / src / plugins / litehtml_viewer / container_linux.cpp
1 /*
2  * Claws Mail -- A GTK+ based, lightweight, and fast e-mail client
3  * Copyright(C) 2019 the Claws Mail Team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write tothe Free Software
15  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16  */
17
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #include "claws-features.h"
21 #endif
22
23 #include "container_linux.h"
24
25 #include <cairo-ft.h>
26
27 #define _USE_MATH_DEFINES
28 #include <math.h>
29
30 #ifndef M_PI
31 #       define M_PI    3.14159265358979323846
32 #endif
33
34 container_linux::container_linux(void)
35 {
36         m_temp_surface  = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 2, 2);
37         m_temp_cr               = cairo_create(m_temp_surface);
38         g_rec_mutex_init(&m_images_lock);
39 }
40
41 container_linux::~container_linux(void)
42 {
43         clear_images();
44         cairo_surface_destroy(m_temp_surface);
45         cairo_destroy(m_temp_cr);
46         g_rec_mutex_clear(&m_images_lock);
47 }
48
49 int container_linux::pt_to_px( int pt )
50 {
51         GdkScreen* screen = gdk_screen_get_default();
52         double dpi = gdk_screen_get_resolution(screen);
53
54         return (int) ((double) pt * dpi / 72.0);
55 }
56
57 void container_linux::draw_list_marker( litehtml::uint_ptr hdc, const litehtml::list_marker& marker )
58 {
59         if(!marker.image.empty())
60         {
61                 /*litehtml::tstring url;
62                 make_url(marker.image.c_str(), marker.baseurl, url);
63
64                 lock_images_cache();
65                 images_map::iterator img_i = m_images.find(url.c_str());
66                 if(img_i != m_images.end())
67                 {
68                         if(img_i->second)
69                         {
70                                 draw_txdib((cairo_t*) hdc, img_i->second, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height);
71                         }
72                 }
73                 unlock_images_cache();*/
74         } else
75         {
76                 switch(marker.marker_type)
77                 {
78                 case litehtml::list_style_type_circle:
79                         {
80                                 draw_ellipse((cairo_t*) hdc, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height, marker.color, 0.5);
81                         }
82                         break;
83                 case litehtml::list_style_type_disc:
84                         {
85                                 fill_ellipse((cairo_t*) hdc, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height, marker.color);
86                         }
87                         break;
88                 case litehtml::list_style_type_square:
89                         if(hdc)
90                         {
91                                 cairo_t* cr = (cairo_t*) hdc;
92                                 cairo_save(cr);
93
94                                 cairo_new_path(cr);
95                                 cairo_rectangle(cr, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height);
96
97                                 set_color(cr, marker.color);
98                                 cairo_fill(cr);
99                                 cairo_restore(cr);
100                         }
101                         break;
102                 default:
103                         /*do nothing*/
104                         break;
105                 }
106         }
107 }
108
109 void container_linux::draw_background( litehtml::uint_ptr hdc, const litehtml::background_paint& bg )
110 {
111         cairo_t* cr = (cairo_t*) hdc;
112         cairo_save(cr);
113         apply_clip(cr);
114
115         rounded_rectangle(cr, bg.border_box, bg.border_radius);
116         cairo_clip(cr);
117
118         cairo_rectangle(cr, bg.clip_box.x, bg.clip_box.y, bg.clip_box.width, bg.clip_box.height);
119         cairo_clip(cr);
120
121         if(bg.color.alpha)
122         {
123                 set_color(cr, bg.color);
124                 cairo_paint(cr);
125         }
126
127         litehtml::tstring url;
128         make_url(bg.image.c_str(), bg.baseurl.c_str(), url);
129
130         const image *img_i = NULL;
131
132         lock_images_cache();
133
134         for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) {
135                 const image *i = &(*ii);
136                 if (i->first == url) {
137                         img_i = i;
138                         break;
139                 }
140         }
141
142         if(img_i != NULL && img_i->second)
143         {
144                 GdkPixbuf *bgbmp = img_i->second;
145
146                 GdkPixbuf *new_img;
147                 if(bg.image_size.width != gdk_pixbuf_get_width(bgbmp) || bg.image_size.height != gdk_pixbuf_get_height(bgbmp))
148                 {
149                         new_img = gdk_pixbuf_scale_simple(bgbmp, bg.image_size.width, bg.image_size.height, GDK_INTERP_BILINEAR);
150                         bgbmp = new_img;
151                 }
152
153                 cairo_surface_t* img = surface_from_pixbuf(bgbmp);
154                 cairo_pattern_t *pattern = cairo_pattern_create_for_surface(img);
155                 cairo_matrix_t flib_m;
156                 cairo_matrix_init_identity(&flib_m);
157                 cairo_matrix_translate(&flib_m, -bg.position_x, -bg.position_y);
158                 cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
159                 cairo_pattern_set_matrix (pattern, &flib_m);
160
161                 switch(bg.repeat)
162                 {
163                 case litehtml::background_repeat_no_repeat:
164                         draw_pixbuf(cr, bgbmp, bg.position_x, bg.position_y, gdk_pixbuf_get_width(bgbmp), gdk_pixbuf_get_height(bgbmp));
165                         break;
166
167                 case litehtml::background_repeat_repeat_x:
168                         cairo_set_source(cr, pattern);
169                         cairo_rectangle(cr, bg.clip_box.left(), bg.position_y, bg.clip_box.width, gdk_pixbuf_get_height(bgbmp));
170                         cairo_fill(cr);
171                         break;
172
173                 case litehtml::background_repeat_repeat_y:
174                         cairo_set_source(cr, pattern);
175                         cairo_rectangle(cr, bg.position_x, bg.clip_box.top(), gdk_pixbuf_get_width(bgbmp), bg.clip_box.height);
176                         cairo_fill(cr);
177                         break;
178
179                 case litehtml::background_repeat_repeat:
180                         cairo_set_source(cr, pattern);
181                         cairo_rectangle(cr, bg.clip_box.left(), bg.clip_box.top(), bg.clip_box.width, bg.clip_box.height);
182                         cairo_fill(cr);
183                         break;
184                 }
185
186                 cairo_pattern_destroy(pattern);
187                 cairo_surface_destroy(img);
188
189         }
190
191         unlock_images_cache();
192         cairo_restore(cr);
193 }
194
195 void container_linux::make_url(const litehtml::tchar_t* url,    const litehtml::tchar_t* basepath, litehtml::tstring& out)
196 {
197         out = url;
198 }
199
200 void container_linux::add_path_arc(cairo_t* cr, double x, double y, double rx, double ry, double a1, double a2, bool neg)
201 {
202         if(rx > 0 && ry > 0)
203         {
204
205                 cairo_save(cr);
206
207                 cairo_translate(cr, x, y);
208                 cairo_scale(cr, 1, ry / rx);
209                 cairo_translate(cr, -x, -y);
210
211                 if(neg)
212                 {
213                         cairo_arc_negative(cr, x, y, rx, a1, a2);
214                 } else
215                 {
216                         cairo_arc(cr, x, y, rx, a1, a2);
217                 }
218
219                 cairo_restore(cr);
220         } else
221         {
222                 cairo_move_to(cr, x, y);
223         }
224 }
225
226 void container_linux::draw_borders(litehtml::uint_ptr hdc, const litehtml::borders& borders, const litehtml::position& draw_pos, bool root)
227 {
228         cairo_t* cr = (cairo_t*) hdc;
229         cairo_save(cr);
230         apply_clip(cr);
231
232         cairo_new_path(cr);
233
234         int bdr_top             = 0;
235         int bdr_bottom  = 0;
236         int bdr_left    = 0;
237         int bdr_right   = 0;
238
239         if(borders.top.width != 0 && borders.top.style > litehtml::border_style_hidden)
240         {
241                 bdr_top = (int) borders.top.width;
242         }
243         if(borders.bottom.width != 0 && borders.bottom.style > litehtml::border_style_hidden)
244         {
245                 bdr_bottom = (int) borders.bottom.width;
246         }
247         if(borders.left.width != 0 && borders.left.style > litehtml::border_style_hidden)
248         {
249                 bdr_left = (int) borders.left.width;
250         }
251         if(borders.right.width != 0 && borders.right.style > litehtml::border_style_hidden)
252         {
253                 bdr_right = (int) borders.right.width;
254         }
255
256         // draw right border
257         if(bdr_right)
258         {
259                 set_color(cr, borders.right.color);
260
261                 double r_top    = borders.radius.top_right_x;
262                 double r_bottom = borders.radius.bottom_right_x;
263
264                 if(r_top)
265                 {
266                         double end_angle        = 2 * M_PI;
267                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_top / (double) bdr_right + 1);
268
269                         add_path_arc(cr,
270                                 draw_pos.right() - r_top,
271                                 draw_pos.top() + r_top,
272                                 r_top - bdr_right,
273                                 r_top - bdr_right + (bdr_right - bdr_top),
274                                 end_angle,
275                                 start_angle, true);
276
277                         add_path_arc(cr,
278                                 draw_pos.right() - r_top,
279                                 draw_pos.top() + r_top,
280                                 r_top,
281                                 r_top,
282                                 start_angle,
283                                 end_angle, false);
284                 } else
285                 {
286                         cairo_move_to(cr, draw_pos.right() - bdr_right, draw_pos.top() + bdr_top);
287                         cairo_line_to(cr, draw_pos.right(), draw_pos.top());
288                 }
289
290                 if(r_bottom)
291                 {
292                         cairo_line_to(cr, draw_pos.right(),     draw_pos.bottom() - r_bottom);
293
294                         double start_angle      = 0;
295                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_bottom / (double) bdr_right + 1);
296
297                         add_path_arc(cr,
298                                 draw_pos.right() - r_bottom,
299                                 draw_pos.bottom() - r_bottom,
300                                 r_bottom,
301                                 r_bottom,
302                                 start_angle,
303                                 end_angle, false);
304
305                         add_path_arc(cr,
306                                 draw_pos.right() - r_bottom,
307                                 draw_pos.bottom() - r_bottom,
308                                 r_bottom - bdr_right,
309                                 r_bottom - bdr_right + (bdr_right - bdr_bottom),
310                                 end_angle,
311                                 start_angle, true);
312                 } else
313                 {
314                         cairo_line_to(cr, draw_pos.right(),     draw_pos.bottom());
315                         cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.bottom() - bdr_bottom);
316                 }
317
318                 cairo_fill(cr);
319         }
320
321         // draw bottom border
322         if(bdr_bottom)
323         {
324                 set_color(cr, borders.bottom.color);
325
326                 double r_left   = borders.radius.bottom_left_x;
327                 double r_right  = borders.radius.bottom_right_x;
328
329                 if(r_left)
330                 {
331                         double start_angle      = M_PI / 2.0;
332                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_left / (double) bdr_bottom + 1);
333
334                         add_path_arc(cr,
335                                 draw_pos.left() + r_left,
336                                 draw_pos.bottom() - r_left,
337                                 r_left - bdr_bottom + (bdr_bottom - bdr_left),
338                                 r_left - bdr_bottom,
339                                 start_angle,
340                                 end_angle, false);
341
342                         add_path_arc(cr,
343                                 draw_pos.left() + r_left,
344                                 draw_pos.bottom() - r_left,
345                                 r_left,
346                                 r_left,
347                                 end_angle,
348                                 start_angle, true);
349                 } else
350                 {
351                         cairo_move_to(cr, draw_pos.left(), draw_pos.bottom());
352                         cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.bottom() - bdr_bottom);
353                 }
354
355                 if(r_right)
356                 {
357                         cairo_line_to(cr, draw_pos.right() - r_right,   draw_pos.bottom());
358
359                         double end_angle        = M_PI / 2.0;
360                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_right / (double) bdr_bottom + 1);
361
362                         add_path_arc(cr,
363                                 draw_pos.right() - r_right,
364                                 draw_pos.bottom() - r_right,
365                                 r_right,
366                                 r_right,
367                                 end_angle,
368                                 start_angle, true);
369
370                         add_path_arc(cr,
371                                 draw_pos.right() - r_right,
372                                 draw_pos.bottom() - r_right,
373                                 r_right - bdr_bottom + (bdr_bottom - bdr_right),
374                                 r_right - bdr_bottom,
375                                 start_angle,
376                                 end_angle, false);
377                 } else
378                 {
379                         cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.bottom() - bdr_bottom);
380                         cairo_line_to(cr, draw_pos.right(),     draw_pos.bottom());
381                 }
382
383                 cairo_fill(cr);
384         }
385
386         // draw top border
387         if(bdr_top)
388         {
389                 set_color(cr, borders.top.color);
390
391                 double r_left   = borders.radius.top_left_x;
392                 double r_right  = borders.radius.top_right_x;
393
394                 if(r_left)
395                 {
396                         double end_angle        = M_PI * 3.0 / 2.0;
397                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_left / (double) bdr_top + 1);
398
399                         add_path_arc(cr,
400                                 draw_pos.left() + r_left,
401                                 draw_pos.top() + r_left,
402                                 r_left,
403                                 r_left,
404                                 end_angle,
405                                 start_angle, true);
406
407                         add_path_arc(cr,
408                                 draw_pos.left() + r_left,
409                                 draw_pos.top() + r_left,
410                                 r_left - bdr_top + (bdr_top - bdr_left),
411                                 r_left - bdr_top,
412                                 start_angle,
413                                 end_angle, false);
414                 } else
415                 {
416                         cairo_move_to(cr, draw_pos.left(), draw_pos.top());
417                         cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top);
418                 }
419
420                 if(r_right)
421                 {
422                         cairo_line_to(cr, draw_pos.right() - r_right,   draw_pos.top() + bdr_top);
423
424                         double start_angle      = M_PI * 3.0 / 2.0;
425                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_right / (double) bdr_top + 1);
426
427                         add_path_arc(cr,
428                                 draw_pos.right() - r_right,
429                                 draw_pos.top() + r_right,
430                                 r_right - bdr_top + (bdr_top - bdr_right),
431                                 r_right - bdr_top,
432                                 start_angle,
433                                 end_angle, false);
434
435                         add_path_arc(cr,
436                                 draw_pos.right() - r_right,
437                                 draw_pos.top() + r_right,
438                                 r_right,
439                                 r_right,
440                                 end_angle,
441                                 start_angle, true);
442                 } else
443                 {
444                         cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.top() + bdr_top);
445                         cairo_line_to(cr, draw_pos.right(),     draw_pos.top());
446                 }
447
448                 cairo_fill(cr);
449         }
450
451         // draw left border
452         if(bdr_left)
453         {
454                 set_color(cr, borders.left.color);
455
456                 double r_top    = borders.radius.top_left_x;
457                 double r_bottom = borders.radius.bottom_left_x;
458
459                 if(r_top)
460                 {
461                         double start_angle      = M_PI;
462                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_top / (double) bdr_left + 1);
463
464                         add_path_arc(cr,
465                                 draw_pos.left() + r_top,
466                                 draw_pos.top() + r_top,
467                                 r_top - bdr_left,
468                                 r_top - bdr_left + (bdr_left - bdr_top),
469                                 start_angle,
470                                 end_angle, false);
471
472                         add_path_arc(cr,
473                                 draw_pos.left() + r_top,
474                                 draw_pos.top() + r_top,
475                                 r_top,
476                                 r_top,
477                                 end_angle,
478                                 start_angle, true);
479                 } else
480                 {
481                         cairo_move_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top);
482                         cairo_line_to(cr, draw_pos.left(), draw_pos.top());
483                 }
484
485                 if(r_bottom)
486                 {
487                         cairo_line_to(cr, draw_pos.left(),      draw_pos.bottom() - r_bottom);
488
489                         double end_angle        = M_PI;
490                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_bottom / (double) bdr_left + 1);
491
492                         add_path_arc(cr,
493                                 draw_pos.left() + r_bottom,
494                                 draw_pos.bottom() - r_bottom,
495                                 r_bottom,
496                                 r_bottom,
497                                 end_angle,
498                                 start_angle, true);
499
500                         add_path_arc(cr,
501                                 draw_pos.left() + r_bottom,
502                                 draw_pos.bottom() - r_bottom,
503                                 r_bottom - bdr_left,
504                                 r_bottom - bdr_left + (bdr_left - bdr_bottom),
505                                 start_angle,
506                                 end_angle, false);
507                 } else
508                 {
509                         cairo_line_to(cr, draw_pos.left(),      draw_pos.bottom());
510                         cairo_line_to(cr, draw_pos.left() + bdr_left,   draw_pos.bottom() - bdr_bottom);
511                 }
512
513                 cairo_fill(cr);
514         }
515         cairo_restore(cr);
516 }
517
518 void container_linux::transform_text(litehtml::tstring& text, litehtml::text_transform tt)
519 {
520
521 }
522
523 void container_linux::set_clip( const litehtml::position& pos, const litehtml::border_radiuses& bdr_radius, bool valid_x, bool valid_y )
524 {
525         litehtml::position clip_pos = pos;
526         litehtml::position client_pos;
527         get_client_rect(client_pos);
528         if(!valid_x)
529         {
530                 clip_pos.x              = client_pos.x;
531                 clip_pos.width  = client_pos.width;
532         }
533         if(!valid_y)
534         {
535                 clip_pos.y              = client_pos.y;
536                 clip_pos.height = client_pos.height;
537         }
538         m_clips.emplace_back(clip_pos, bdr_radius);
539 }
540
541 void container_linux::del_clip()
542 {
543         if(!m_clips.empty())
544         {
545                 m_clips.pop_back();
546         }
547 }
548
549 void container_linux::apply_clip( cairo_t* cr )
550 {
551         for(const auto& clip_box : m_clips)
552         {
553                 rounded_rectangle(cr, clip_box.box, clip_box.radius);
554                 cairo_clip(cr);
555         }
556 }
557
558 void container_linux::draw_ellipse( cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color, int line_width )
559 {
560         if(!cr) return;
561         cairo_save(cr);
562
563         apply_clip(cr);
564
565         cairo_new_path(cr);
566
567         cairo_translate (cr, x + width / 2.0, y + height / 2.0);
568         cairo_scale (cr, width / 2.0, height / 2.0);
569         cairo_arc (cr, 0, 0, 1, 0, 2 * M_PI);
570
571         set_color(cr, color);
572         cairo_set_line_width(cr, line_width);
573         cairo_stroke(cr);
574
575         cairo_restore(cr);
576 }
577
578 void container_linux::fill_ellipse( cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color )
579 {
580         if(!cr) return;
581         cairo_save(cr);
582
583         apply_clip(cr);
584
585         cairo_new_path(cr);
586
587         cairo_translate (cr, x + width / 2.0, y + height / 2.0);
588         cairo_scale (cr, width / 2.0, height / 2.0);
589         cairo_arc (cr, 0, 0, 1, 0, 2 * M_PI);
590
591         set_color(cr, color);
592         cairo_fill(cr);
593
594         cairo_restore(cr);
595 }
596
597 std::shared_ptr<litehtml::element>      container_linux::create_element(const litehtml::tchar_t *tag_name,
598                                                                                                                                           const litehtml::string_map &attributes,
599                                                                                                                                           const std::shared_ptr<litehtml::document> &doc)
600 {
601         return 0;
602 }
603
604 void container_linux::rounded_rectangle( cairo_t* cr, const litehtml::position &pos, const litehtml::border_radiuses &radius )
605 {
606         cairo_new_path(cr);
607         if(radius.top_left_x)
608         {
609                 cairo_arc(cr, pos.left() + radius.top_left_x, pos.top() + radius.top_left_x, radius.top_left_x, M_PI, M_PI * 3.0 / 2.0);
610         } else
611         {
612                 cairo_move_to(cr, pos.left(), pos.top());
613         }
614
615         cairo_line_to(cr, pos.right() - radius.top_right_x, pos.top());
616
617         if(radius.top_right_x)
618         {
619                 cairo_arc(cr, pos.right() - radius.top_right_x, pos.top() + radius.top_right_x, radius.top_right_x, M_PI * 3.0 / 2.0, 2.0 * M_PI);
620         }
621
622         cairo_line_to(cr, pos.right(), pos.bottom() - radius.bottom_right_x);
623
624         if(radius.bottom_right_x)
625         {
626                 cairo_arc(cr, pos.right() - radius.bottom_right_x, pos.bottom() - radius.bottom_right_x, radius.bottom_right_x, 0, M_PI / 2.0);
627         }
628
629         cairo_line_to(cr, pos.left() - radius.bottom_left_x, pos.bottom());
630
631         if(radius.bottom_left_x)
632         {
633                 cairo_arc(cr, pos.left() + radius.bottom_left_x, pos.bottom() - radius.bottom_left_x, radius.bottom_left_x, M_PI / 2.0, M_PI);
634         }
635 }
636
637 void container_linux::draw_pixbuf(cairo_t* cr, const GdkPixbuf *bmp, int x,     int y, int cx, int cy)
638 {
639         cairo_save(cr);
640
641         {
642                 cairo_matrix_t flib_m;
643                 cairo_matrix_init(&flib_m, 1, 0, 0, -1, 0, 0);
644
645                 if(cx != gdk_pixbuf_get_width(bmp) || cy != gdk_pixbuf_get_height(bmp))
646                 {
647                         GdkPixbuf *new_img = gdk_pixbuf_scale_simple(bmp, cx, cy, GDK_INTERP_BILINEAR);
648                         gdk_cairo_set_source_pixbuf(cr, new_img, x, y);
649                         cairo_paint(cr);
650                 } else
651                 {
652                         gdk_cairo_set_source_pixbuf(cr, bmp, x, y);
653                         cairo_paint(cr);
654                 }
655         }
656
657         cairo_restore(cr);
658 }
659
660 cairo_surface_t* container_linux::surface_from_pixbuf(const GdkPixbuf *bmp)
661 {
662         cairo_surface_t* ret = NULL;
663
664         if(gdk_pixbuf_get_has_alpha(bmp))
665         {
666                 ret = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, gdk_pixbuf_get_width(bmp), gdk_pixbuf_get_height(bmp));
667         } else
668         {
669                 ret = cairo_image_surface_create(CAIRO_FORMAT_RGB24, gdk_pixbuf_get_width(bmp), gdk_pixbuf_get_height(bmp));
670         }
671
672 //      Cairo::RefPtr<Cairo::Surface> surface(new Cairo::Surface(ret, false));
673 //      Cairo::RefPtr<Cairo::Context> ctx = Cairo::Context::create(surface);
674 //      Gdk::Cairo::set_source_pixbuf(ctx, bmp, 0.0, 0.0);
675         cairo_t *ctx = cairo_create(ret);
676         cairo_paint(ctx);
677         cairo_destroy(ctx);
678
679         return ret;
680 }
681
682 void container_linux::get_media_features(litehtml::media_features& media) const
683 {
684         litehtml::position client;
685     get_client_rect(client);
686         media.type                      = litehtml::media_type_screen;
687         media.width                     = client.width;
688         media.height            = client.height;
689         media.device_width      = gdk_screen_width();
690         media.device_height     = gdk_screen_height();
691         media.color                     = 8;
692         media.monochrome        = 0;
693         media.color_index       = 256;
694         media.resolution        = 96;
695 }
696
697 void container_linux::get_language(litehtml::tstring& language, litehtml::tstring& culture) const
698 {
699         language = _t("en");
700         culture = _t("");
701 }
702
703 void container_linux::link(const std::shared_ptr<litehtml::document> &ptr, const litehtml::element::ptr& el)
704 {
705
706 }