Implement size limit for Litehtml image cache
[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) 1999-2015 the Claws Mail Team
4  * == Fancy Plugin ==
5  * This file Copyright (C) 2009-2015 Salvatore De Paolis
6  * <iwkse@claws-mail.org> and the Claws Mail Team
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write tothe Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #include "claws-features.h"
23 #endif
24
25 #include "container_linux.h"
26
27 #include <cairo-ft.h>
28
29 #define _USE_MATH_DEFINES
30 #include <math.h>
31
32 #ifndef M_PI
33 #       define M_PI    3.14159265358979323846
34 #endif
35
36 container_linux::container_linux(void)
37 {
38         m_temp_surface  = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 2, 2);
39         m_temp_cr               = cairo_create(m_temp_surface);
40 }
41
42 container_linux::~container_linux(void)
43 {
44         clear_images();
45         cairo_surface_destroy(m_temp_surface);
46         cairo_destroy(m_temp_cr);
47 }
48
49 litehtml::uint_ptr container_linux::create_font( const litehtml::tchar_t* faceName, int size, int weight, litehtml::font_style italic, unsigned int decoration, litehtml::font_metrics* fm )
50 {
51         litehtml::string_vector fonts;
52         litehtml::split_string(faceName, fonts, ",");
53         if (! fonts.empty()) {
54             litehtml::trim(fonts[0]);
55         }
56
57         cairo_font_face_t* fnt = 0;
58
59         FcPattern *pattern = FcPatternCreate();
60         bool found = false;
61         for(litehtml::string_vector::iterator i = fonts.begin(); i != fonts.end(); i++)
62         {
63                 if(FcPatternAddString(pattern, FC_FAMILY, (unsigned char *) i->c_str()))
64                 {
65                         found = true;
66                         break;
67                 }
68         }
69         if(found)
70         {
71                 if(italic == litehtml::fontStyleItalic )
72                 {
73                         FcPatternAddInteger (pattern, FC_SLANT, FC_SLANT_ITALIC);
74                 } else
75                 {
76                         FcPatternAddInteger (pattern, FC_SLANT, FC_SLANT_ROMAN);
77                 }
78
79                 int fc_weight = FC_WEIGHT_NORMAL;
80                 if(weight >= 0 && weight < 150)                 fc_weight = FC_WEIGHT_THIN;
81                 else if(weight >= 150 && weight < 250)  fc_weight = FC_WEIGHT_EXTRALIGHT;
82                 else if(weight >= 250 && weight < 350)  fc_weight = FC_WEIGHT_LIGHT;
83                 else if(weight >= 350 && weight < 450)  fc_weight = FC_WEIGHT_NORMAL;
84                 else if(weight >= 450 && weight < 550)  fc_weight = FC_WEIGHT_MEDIUM;
85                 else if(weight >= 550 && weight < 650)  fc_weight = FC_WEIGHT_SEMIBOLD;
86                 else if(weight >= 650 && weight < 750)  fc_weight = FC_WEIGHT_BOLD;
87                 else if(weight >= 750 && weight < 850)  fc_weight = FC_WEIGHT_EXTRABOLD;
88                 else if(weight >= 950)                                  fc_weight = FC_WEIGHT_BLACK;
89
90                 FcPatternAddInteger (pattern, FC_WEIGHT, fc_weight);
91
92                 fnt = cairo_ft_font_face_create_for_pattern(pattern);
93         }
94
95         FcPatternDestroy(pattern);
96
97         cairo_font* ret = 0;
98
99         if(fm && fnt)
100         {
101                 cairo_save(m_temp_cr);
102
103                 cairo_set_font_face(m_temp_cr, fnt);
104                 cairo_set_font_size(m_temp_cr, size);
105                 cairo_font_extents_t ext;
106                 cairo_font_extents(m_temp_cr, &ext);
107
108                 cairo_text_extents_t tex;
109                 cairo_text_extents(m_temp_cr, "x", &tex);
110
111                 fm->ascent              = (int) ext.ascent;
112                 fm->descent             = (int) ext.descent;
113                 fm->height              = (int) (ext.ascent + ext.descent);
114                 fm->x_height    = (int) tex.height;
115
116                 cairo_restore(m_temp_cr);
117
118                 ret = new cairo_font;
119                 ret->font               = fnt;
120                 ret->size               = size;
121                 ret->strikeout  = (decoration & litehtml::font_decoration_linethrough) ? true : false;
122                 ret->underline  = (decoration & litehtml::font_decoration_underline) ? true : false;
123
124         }
125
126         return (litehtml::uint_ptr) ret;
127 }
128
129 void container_linux::delete_font( litehtml::uint_ptr hFont )
130 {
131         cairo_font* fnt = (cairo_font*) hFont;
132         if(fnt)
133         {
134                 cairo_font_face_destroy(fnt->font);
135                 delete fnt;
136         }
137 }
138
139 int container_linux::text_width( const litehtml::tchar_t* text, litehtml::uint_ptr hFont )
140 {
141         cairo_font* fnt = (cairo_font*) hFont;
142
143         cairo_save(m_temp_cr);
144
145         if (fnt) {
146             cairo_set_font_size(m_temp_cr, fnt->size);
147             cairo_set_font_face(m_temp_cr, fnt->font);
148         }
149         cairo_text_extents_t ext;
150         cairo_text_extents(m_temp_cr, text, &ext);
151
152         cairo_restore(m_temp_cr);
153
154         return (int) ext.x_advance;
155 }
156
157 void container_linux::draw_text( litehtml::uint_ptr hdc, const litehtml::tchar_t* text, litehtml::uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos )
158 {
159         cairo_font* fnt = (cairo_font*) hFont;
160         cairo_t* cr             = (cairo_t*) hdc;
161         cairo_save(cr);
162
163         apply_clip(cr);
164
165         if (fnt) {
166             cairo_set_font_face(cr, fnt->font);
167             cairo_set_font_size(cr, fnt->size);
168         }
169         cairo_font_extents_t ext;
170         cairo_font_extents(cr, &ext);
171
172         int x = pos.left();
173         int y = pos.bottom()    - ext.descent;
174
175         set_color(cr, color);
176
177         cairo_move_to(cr, x, y);
178         cairo_show_text(cr, text);
179
180         int tw = 0;
181
182         if (fnt) {
183             if(fnt->underline || fnt->strikeout)
184             {
185                 tw = text_width(text, hFont);
186             }
187
188             if(fnt->underline)
189             {
190                 cairo_set_line_width(cr, 1);
191                 cairo_move_to(cr, x, y + 1.5);
192                 cairo_line_to(cr, x + tw, y + 1.5);
193                 cairo_stroke(cr);
194             }
195             if(fnt->strikeout)
196             {
197                 cairo_text_extents_t tex;
198                 cairo_text_extents(cr, "x", &tex);
199
200                 int ln_y = y - tex.height / 2.0;
201
202                 cairo_set_line_width(cr, 1);
203                 cairo_move_to(cr, x, (double) ln_y - 0.5);
204                 cairo_line_to(cr, x + tw, (double) ln_y - 0.5);
205                 cairo_stroke(cr);
206             }
207         }   
208
209         cairo_restore(cr);
210 }
211
212 int container_linux::pt_to_px( int pt )
213 {
214         GdkScreen* screen = gdk_screen_get_default();
215         double dpi = gdk_screen_get_resolution(screen);
216
217         return (int) ((double) pt * dpi / 72.0);
218 }
219
220 int container_linux::get_default_font_size() const
221 {
222         return 16;
223 }
224
225 void container_linux::draw_list_marker( litehtml::uint_ptr hdc, const litehtml::list_marker& marker )
226 {
227         if(!marker.image.empty())
228         {
229                 /*litehtml::tstring url;
230                 make_url(marker.image.c_str(), marker.baseurl, url);
231
232                 lock_images_cache();
233                 images_map::iterator img_i = m_images.find(url.c_str());
234                 if(img_i != m_images.end())
235                 {
236                         if(img_i->second)
237                         {
238                                 draw_txdib((cairo_t*) hdc, img_i->second, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height);
239                         }
240                 }
241                 unlock_images_cache();*/
242         } else
243         {
244                 switch(marker.marker_type)
245                 {
246                 case litehtml::list_style_type_circle:
247                         {
248                                 draw_ellipse((cairo_t*) hdc, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height, marker.color, 0.5);
249                         }
250                         break;
251                 case litehtml::list_style_type_disc:
252                         {
253                                 fill_ellipse((cairo_t*) hdc, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height, marker.color);
254                         }
255                         break;
256                 case litehtml::list_style_type_square:
257                         if(hdc)
258                         {
259                                 cairo_t* cr = (cairo_t*) hdc;
260                                 cairo_save(cr);
261
262                                 cairo_new_path(cr);
263                                 cairo_rectangle(cr, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height);
264
265                                 set_color(cr, marker.color);
266                                 cairo_fill(cr);
267                                 cairo_restore(cr);
268                         }
269                         break;
270                 default:
271                         /*do nothing*/
272                         break;
273                 }
274         }
275 }
276
277 void container_linux::load_image( const litehtml::tchar_t* src, const litehtml::tchar_t* baseurl, bool redraw_on_ready )
278 {
279         litehtml::tstring url;
280         make_url(src, baseurl, url);
281         bool found = false;
282
283         for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) {
284                 const image *i = &(*ii);
285
286                 if (!strcmp(i->first.c_str(), url.c_str())) {
287                         found = true;
288                         break;
289                 }
290         }
291
292         if(!found)
293         {
294                 try
295                 {
296                         GdkPixbuf *img = get_image(url.c_str(), true);
297                         if(img)
298                         {
299                                 m_images.push_back(std::make_pair(url, img));
300                         }
301                 } catch(...)
302                 {
303                         int iii=0;
304                         iii++;
305                 }
306         }
307 }
308
309 void container_linux::get_image_size( const litehtml::tchar_t* src, const litehtml::tchar_t* baseurl, litehtml::size& sz )
310 {
311         litehtml::tstring url;
312         make_url(src, baseurl, url);
313         bool found = false;
314         const image *img = NULL;
315
316         for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) {
317                 const image *i = &(*ii);
318                 if (i->first == url) {
319                         img = i;
320                         found = true;
321                         break;
322                 }
323         }
324
325         if(img != NULL)
326         {
327                 sz.width        = gdk_pixbuf_get_width(img->second);
328                 sz.height       = gdk_pixbuf_get_height(img->second);
329         } else
330         {
331                 sz.width        = 0;
332                 sz.height       = 0;
333         }
334 }
335
336 void container_linux::draw_background( litehtml::uint_ptr hdc, const litehtml::background_paint& bg )
337 {
338         cairo_t* cr = (cairo_t*) hdc;
339         cairo_save(cr);
340         apply_clip(cr);
341
342         rounded_rectangle(cr, bg.border_box, bg.border_radius);
343         cairo_clip(cr);
344
345         cairo_rectangle(cr, bg.clip_box.x, bg.clip_box.y, bg.clip_box.width, bg.clip_box.height);
346         cairo_clip(cr);
347
348         if(bg.color.alpha)
349         {
350                 set_color(cr, bg.color);
351                 cairo_paint(cr);
352         }
353
354         litehtml::tstring url;
355         make_url(bg.image.c_str(), bg.baseurl.c_str(), url);
356
357         //lock_images_cache();
358         bool found = false;
359         const image *img_i = NULL;
360
361         for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) {
362                 const image *i = &(*ii);
363                 if (i->first == url) {
364                         img_i = i;
365                         found = true;
366                         break;
367                 }
368         }
369
370         if(img_i != NULL && img_i->second)
371         {
372                 GdkPixbuf *bgbmp = img_i->second;
373
374                 GdkPixbuf *new_img;
375                 if(bg.image_size.width != gdk_pixbuf_get_width(bgbmp) || bg.image_size.height != gdk_pixbuf_get_height(bgbmp))
376                 {
377                         new_img = gdk_pixbuf_scale_simple(bgbmp, bg.image_size.width, bg.image_size.height, GDK_INTERP_BILINEAR);
378                         bgbmp = new_img;
379                 }
380
381                 cairo_surface_t* img = surface_from_pixbuf(bgbmp);
382                 cairo_pattern_t *pattern = cairo_pattern_create_for_surface(img);
383                 cairo_matrix_t flib_m;
384                 cairo_matrix_init_identity(&flib_m);
385                 cairo_matrix_translate(&flib_m, -bg.position_x, -bg.position_y);
386                 cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
387                 cairo_pattern_set_matrix (pattern, &flib_m);
388
389                 switch(bg.repeat)
390                 {
391                 case litehtml::background_repeat_no_repeat:
392                         draw_pixbuf(cr, bgbmp, bg.position_x, bg.position_y, gdk_pixbuf_get_width(bgbmp), gdk_pixbuf_get_height(bgbmp));
393                         break;
394
395                 case litehtml::background_repeat_repeat_x:
396                         cairo_set_source(cr, pattern);
397                         cairo_rectangle(cr, bg.clip_box.left(), bg.position_y, bg.clip_box.width, gdk_pixbuf_get_height(bgbmp));
398                         cairo_fill(cr);
399                         break;
400
401                 case litehtml::background_repeat_repeat_y:
402                         cairo_set_source(cr, pattern);
403                         cairo_rectangle(cr, bg.position_x, bg.clip_box.top(), gdk_pixbuf_get_width(bgbmp), bg.clip_box.height);
404                         cairo_fill(cr);
405                         break;
406
407                 case litehtml::background_repeat_repeat:
408                         cairo_set_source(cr, pattern);
409                         cairo_rectangle(cr, bg.clip_box.left(), bg.clip_box.top(), bg.clip_box.width, bg.clip_box.height);
410                         cairo_fill(cr);
411                         break;
412                 }
413
414                 cairo_pattern_destroy(pattern);
415                 cairo_surface_destroy(img);
416
417         }
418 //      unlock_images_cache();
419         cairo_restore(cr);
420 }
421
422 void container_linux::make_url(const litehtml::tchar_t* url,    const litehtml::tchar_t* basepath, litehtml::tstring& out)
423 {
424         out = url;
425 }
426
427 void container_linux::add_path_arc(cairo_t* cr, double x, double y, double rx, double ry, double a1, double a2, bool neg)
428 {
429         if(rx > 0 && ry > 0)
430         {
431
432                 cairo_save(cr);
433
434                 cairo_translate(cr, x, y);
435                 cairo_scale(cr, 1, ry / rx);
436                 cairo_translate(cr, -x, -y);
437
438                 if(neg)
439                 {
440                         cairo_arc_negative(cr, x, y, rx, a1, a2);
441                 } else
442                 {
443                         cairo_arc(cr, x, y, rx, a1, a2);
444                 }
445
446                 cairo_restore(cr);
447         } else
448         {
449                 cairo_move_to(cr, x, y);
450         }
451 }
452
453 void container_linux::draw_borders(litehtml::uint_ptr hdc, const litehtml::borders& borders, const litehtml::position& draw_pos, bool root)
454 {
455         cairo_t* cr = (cairo_t*) hdc;
456         cairo_save(cr);
457         apply_clip(cr);
458
459         cairo_new_path(cr);
460
461         int bdr_top             = 0;
462         int bdr_bottom  = 0;
463         int bdr_left    = 0;
464         int bdr_right   = 0;
465
466         if(borders.top.width != 0 && borders.top.style > litehtml::border_style_hidden)
467         {
468                 bdr_top = (int) borders.top.width;
469         }
470         if(borders.bottom.width != 0 && borders.bottom.style > litehtml::border_style_hidden)
471         {
472                 bdr_bottom = (int) borders.bottom.width;
473         }
474         if(borders.left.width != 0 && borders.left.style > litehtml::border_style_hidden)
475         {
476                 bdr_left = (int) borders.left.width;
477         }
478         if(borders.right.width != 0 && borders.right.style > litehtml::border_style_hidden)
479         {
480                 bdr_right = (int) borders.right.width;
481         }
482
483         // draw right border
484         if(bdr_right)
485         {
486                 set_color(cr, borders.right.color);
487
488                 double r_top    = borders.radius.top_right_x;
489                 double r_bottom = borders.radius.bottom_right_x;
490
491                 if(r_top)
492                 {
493                         double end_angle        = 2 * M_PI;
494                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_top / (double) bdr_right + 1);
495
496                         add_path_arc(cr,
497                                 draw_pos.right() - r_top,
498                                 draw_pos.top() + r_top,
499                                 r_top - bdr_right,
500                                 r_top - bdr_right + (bdr_right - bdr_top),
501                                 end_angle,
502                                 start_angle, true);
503
504                         add_path_arc(cr,
505                                 draw_pos.right() - r_top,
506                                 draw_pos.top() + r_top,
507                                 r_top,
508                                 r_top,
509                                 start_angle,
510                                 end_angle, false);
511                 } else
512                 {
513                         cairo_move_to(cr, draw_pos.right() - bdr_right, draw_pos.top() + bdr_top);
514                         cairo_line_to(cr, draw_pos.right(), draw_pos.top());
515                 }
516
517                 if(r_bottom)
518                 {
519                         cairo_line_to(cr, draw_pos.right(),     draw_pos.bottom() - r_bottom);
520
521                         double start_angle      = 0;
522                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_bottom / (double) bdr_right + 1);
523
524                         add_path_arc(cr,
525                                 draw_pos.right() - r_bottom,
526                                 draw_pos.bottom() - r_bottom,
527                                 r_bottom,
528                                 r_bottom,
529                                 start_angle,
530                                 end_angle, false);
531
532                         add_path_arc(cr,
533                                 draw_pos.right() - r_bottom,
534                                 draw_pos.bottom() - r_bottom,
535                                 r_bottom - bdr_right,
536                                 r_bottom - bdr_right + (bdr_right - bdr_bottom),
537                                 end_angle,
538                                 start_angle, true);
539                 } else
540                 {
541                         cairo_line_to(cr, draw_pos.right(),     draw_pos.bottom());
542                         cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.bottom() - bdr_bottom);
543                 }
544
545                 cairo_fill(cr);
546         }
547
548         // draw bottom border
549         if(bdr_bottom)
550         {
551                 set_color(cr, borders.bottom.color);
552
553                 double r_left   = borders.radius.bottom_left_x;
554                 double r_right  = borders.radius.bottom_right_x;
555
556                 if(r_left)
557                 {
558                         double start_angle      = M_PI / 2.0;
559                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_left / (double) bdr_bottom + 1);
560
561                         add_path_arc(cr,
562                                 draw_pos.left() + r_left,
563                                 draw_pos.bottom() - r_left,
564                                 r_left - bdr_bottom + (bdr_bottom - bdr_left),
565                                 r_left - bdr_bottom,
566                                 start_angle,
567                                 end_angle, false);
568
569                         add_path_arc(cr,
570                                 draw_pos.left() + r_left,
571                                 draw_pos.bottom() - r_left,
572                                 r_left,
573                                 r_left,
574                                 end_angle,
575                                 start_angle, true);
576                 } else
577                 {
578                         cairo_move_to(cr, draw_pos.left(), draw_pos.bottom());
579                         cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.bottom() - bdr_bottom);
580                 }
581
582                 if(r_right)
583                 {
584                         cairo_line_to(cr, draw_pos.right() - r_right,   draw_pos.bottom());
585
586                         double end_angle        = M_PI / 2.0;
587                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_right / (double) bdr_bottom + 1);
588
589                         add_path_arc(cr,
590                                 draw_pos.right() - r_right,
591                                 draw_pos.bottom() - r_right,
592                                 r_right,
593                                 r_right,
594                                 end_angle,
595                                 start_angle, true);
596
597                         add_path_arc(cr,
598                                 draw_pos.right() - r_right,
599                                 draw_pos.bottom() - r_right,
600                                 r_right - bdr_bottom + (bdr_bottom - bdr_right),
601                                 r_right - bdr_bottom,
602                                 start_angle,
603                                 end_angle, false);
604                 } else
605                 {
606                         cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.bottom() - bdr_bottom);
607                         cairo_line_to(cr, draw_pos.right(),     draw_pos.bottom());
608                 }
609
610                 cairo_fill(cr);
611         }
612
613         // draw top border
614         if(bdr_top)
615         {
616                 set_color(cr, borders.top.color);
617
618                 double r_left   = borders.radius.top_left_x;
619                 double r_right  = borders.radius.top_right_x;
620
621                 if(r_left)
622                 {
623                         double end_angle        = M_PI * 3.0 / 2.0;
624                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_left / (double) bdr_top + 1);
625
626                         add_path_arc(cr,
627                                 draw_pos.left() + r_left,
628                                 draw_pos.top() + r_left,
629                                 r_left,
630                                 r_left,
631                                 end_angle,
632                                 start_angle, true);
633
634                         add_path_arc(cr,
635                                 draw_pos.left() + r_left,
636                                 draw_pos.top() + r_left,
637                                 r_left - bdr_top + (bdr_top - bdr_left),
638                                 r_left - bdr_top,
639                                 start_angle,
640                                 end_angle, false);
641                 } else
642                 {
643                         cairo_move_to(cr, draw_pos.left(), draw_pos.top());
644                         cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top);
645                 }
646
647                 if(r_right)
648                 {
649                         cairo_line_to(cr, draw_pos.right() - r_right,   draw_pos.top() + bdr_top);
650
651                         double start_angle      = M_PI * 3.0 / 2.0;
652                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_right / (double) bdr_top + 1);
653
654                         add_path_arc(cr,
655                                 draw_pos.right() - r_right,
656                                 draw_pos.top() + r_right,
657                                 r_right - bdr_top + (bdr_top - bdr_right),
658                                 r_right - bdr_top,
659                                 start_angle,
660                                 end_angle, false);
661
662                         add_path_arc(cr,
663                                 draw_pos.right() - r_right,
664                                 draw_pos.top() + r_right,
665                                 r_right,
666                                 r_right,
667                                 end_angle,
668                                 start_angle, true);
669                 } else
670                 {
671                         cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.top() + bdr_top);
672                         cairo_line_to(cr, draw_pos.right(),     draw_pos.top());
673                 }
674
675                 cairo_fill(cr);
676         }
677
678         // draw left border
679         if(bdr_left)
680         {
681                 set_color(cr, borders.left.color);
682
683                 double r_top    = borders.radius.top_left_x;
684                 double r_bottom = borders.radius.bottom_left_x;
685
686                 if(r_top)
687                 {
688                         double start_angle      = M_PI;
689                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_top / (double) bdr_left + 1);
690
691                         add_path_arc(cr,
692                                 draw_pos.left() + r_top,
693                                 draw_pos.top() + r_top,
694                                 r_top - bdr_left,
695                                 r_top - bdr_left + (bdr_left - bdr_top),
696                                 start_angle,
697                                 end_angle, false);
698
699                         add_path_arc(cr,
700                                 draw_pos.left() + r_top,
701                                 draw_pos.top() + r_top,
702                                 r_top,
703                                 r_top,
704                                 end_angle,
705                                 start_angle, true);
706                 } else
707                 {
708                         cairo_move_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top);
709                         cairo_line_to(cr, draw_pos.left(), draw_pos.top());
710                 }
711
712                 if(r_bottom)
713                 {
714                         cairo_line_to(cr, draw_pos.left(),      draw_pos.bottom() - r_bottom);
715
716                         double end_angle        = M_PI;
717                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_bottom / (double) bdr_left + 1);
718
719                         add_path_arc(cr,
720                                 draw_pos.left() + r_bottom,
721                                 draw_pos.bottom() - r_bottom,
722                                 r_bottom,
723                                 r_bottom,
724                                 end_angle,
725                                 start_angle, true);
726
727                         add_path_arc(cr,
728                                 draw_pos.left() + r_bottom,
729                                 draw_pos.bottom() - r_bottom,
730                                 r_bottom - bdr_left,
731                                 r_bottom - bdr_left + (bdr_left - bdr_bottom),
732                                 start_angle,
733                                 end_angle, false);
734                 } else
735                 {
736                         cairo_line_to(cr, draw_pos.left(),      draw_pos.bottom());
737                         cairo_line_to(cr, draw_pos.left() + bdr_left,   draw_pos.bottom() - bdr_bottom);
738                 }
739
740                 cairo_fill(cr);
741         }
742         cairo_restore(cr);
743 }
744
745 void container_linux::transform_text(litehtml::tstring& text, litehtml::text_transform tt)
746 {
747
748 }
749
750 void container_linux::set_clip( const litehtml::position& pos, const litehtml::border_radiuses& bdr_radius, bool valid_x, bool valid_y )
751 {
752         litehtml::position clip_pos = pos;
753         litehtml::position client_pos;
754         get_client_rect(client_pos);
755         if(!valid_x)
756         {
757                 clip_pos.x              = client_pos.x;
758                 clip_pos.width  = client_pos.width;
759         }
760         if(!valid_y)
761         {
762                 clip_pos.y              = client_pos.y;
763                 clip_pos.height = client_pos.height;
764         }
765         m_clips.emplace_back(clip_pos, bdr_radius);
766 }
767
768 void container_linux::del_clip()
769 {
770         if(!m_clips.empty())
771         {
772                 m_clips.pop_back();
773         }
774 }
775
776 void container_linux::apply_clip( cairo_t* cr )
777 {
778         for(const auto& clip_box : m_clips)
779         {
780                 rounded_rectangle(cr, clip_box.box, clip_box.radius);
781                 cairo_clip(cr);
782         }
783 }
784
785 void container_linux::draw_ellipse( cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color, int line_width )
786 {
787         if(!cr) return;
788         cairo_save(cr);
789
790         apply_clip(cr);
791
792         cairo_new_path(cr);
793
794         cairo_translate (cr, x + width / 2.0, y + height / 2.0);
795         cairo_scale (cr, width / 2.0, height / 2.0);
796         cairo_arc (cr, 0, 0, 1, 0, 2 * M_PI);
797
798         set_color(cr, color);
799         cairo_set_line_width(cr, line_width);
800         cairo_stroke(cr);
801
802         cairo_restore(cr);
803 }
804
805 void container_linux::fill_ellipse( cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color )
806 {
807         if(!cr) return;
808         cairo_save(cr);
809
810         apply_clip(cr);
811
812         cairo_new_path(cr);
813
814         cairo_translate (cr, x + width / 2.0, y + height / 2.0);
815         cairo_scale (cr, width / 2.0, height / 2.0);
816         cairo_arc (cr, 0, 0, 1, 0, 2 * M_PI);
817
818         set_color(cr, color);
819         cairo_fill(cr);
820
821         cairo_restore(cr);
822 }
823
824 void container_linux::clear_images()
825 {
826         for(auto i = m_images.begin(); i != m_images.end(); ++i) {
827                 image *img = &(*i);
828
829                 if (img->second) {
830                         g_object_unref(img->second);
831                 }
832         }
833
834         m_images.clear();
835 }
836
837 void container_linux::clear_images(gint desired_size)
838 {
839         gint size = 0;
840
841         /* First, tally up size of all the stored GdkPixbufs and
842          * deallocate those which make the total size be above
843          * the desired_size limit. We will remove their list
844          * elements later. */
845         for (auto i = m_images.rbegin(); i != m_images.rend(); ++i) {
846                 image *img = &(*i);
847                 gint cursize;
848
849                 if (img->second == NULL)
850                         continue;
851
852                 cursize = gdk_pixbuf_get_byte_length(img->second);
853
854                 if (size + cursize > desired_size) {
855                         g_object_unref(img->second);
856                         img->second = NULL;
857                 } else {
858                         size += cursize;
859                 }
860         }
861
862         /* Remove elements whose GdkPixbuf pointers point to NULL. */
863         m_images.remove_if([&](image _img) -> bool {
864                         if (_img.second == NULL)
865                                 return true;
866                         return false;
867                         });
868 }
869
870 const litehtml::tchar_t* container_linux::get_default_font_name() const
871 {
872         return "Times New Roman";
873 }
874
875 std::shared_ptr<litehtml::element>      container_linux::create_element(const litehtml::tchar_t *tag_name,
876                                                                                                                                           const litehtml::string_map &attributes,
877                                                                                                                                           const std::shared_ptr<litehtml::document> &doc)
878 {
879         return 0;
880 }
881
882 void container_linux::rounded_rectangle( cairo_t* cr, const litehtml::position &pos, const litehtml::border_radiuses &radius )
883 {
884         cairo_new_path(cr);
885         if(radius.top_left_x)
886         {
887                 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);
888         } else
889         {
890                 cairo_move_to(cr, pos.left(), pos.top());
891         }
892
893         cairo_line_to(cr, pos.right() - radius.top_right_x, pos.top());
894
895         if(radius.top_right_x)
896         {
897                 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);
898         }
899
900         cairo_line_to(cr, pos.right(), pos.bottom() - radius.bottom_right_x);
901
902         if(radius.bottom_right_x)
903         {
904                 cairo_arc(cr, pos.right() - radius.bottom_right_x, pos.bottom() - radius.bottom_right_x, radius.bottom_right_x, 0, M_PI / 2.0);
905         }
906
907         cairo_line_to(cr, pos.left() - radius.bottom_left_x, pos.bottom());
908
909         if(radius.bottom_left_x)
910         {
911                 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);
912         }
913 }
914
915 void container_linux::draw_pixbuf(cairo_t* cr, const GdkPixbuf *bmp, int x,     int y, int cx, int cy)
916 {
917         cairo_save(cr);
918
919         {
920                 cairo_matrix_t flib_m;
921                 cairo_matrix_init(&flib_m, 1, 0, 0, -1, 0, 0);
922
923                 if(cx != gdk_pixbuf_get_width(bmp) || cy != gdk_pixbuf_get_height(bmp))
924                 {
925                         GdkPixbuf *new_img = gdk_pixbuf_scale_simple(bmp, cx, cy, GDK_INTERP_BILINEAR);
926                         gdk_cairo_set_source_pixbuf(cr, new_img, x, y);
927                         cairo_paint(cr);
928                 } else
929                 {
930                         gdk_cairo_set_source_pixbuf(cr, bmp, x, y);
931                         cairo_paint(cr);
932                 }
933         }
934
935         cairo_restore(cr);
936 }
937
938 cairo_surface_t* container_linux::surface_from_pixbuf(const GdkPixbuf *bmp)
939 {
940         cairo_surface_t* ret = NULL;
941
942         if(gdk_pixbuf_get_has_alpha(bmp))
943         {
944                 ret = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, gdk_pixbuf_get_width(bmp), gdk_pixbuf_get_height(bmp));
945         } else
946         {
947                 ret = cairo_image_surface_create(CAIRO_FORMAT_RGB24, gdk_pixbuf_get_width(bmp), gdk_pixbuf_get_height(bmp));
948         }
949
950 //      Cairo::RefPtr<Cairo::Surface> surface(new Cairo::Surface(ret, false));
951 //      Cairo::RefPtr<Cairo::Context> ctx = Cairo::Context::create(surface);
952 //      Gdk::Cairo::set_source_pixbuf(ctx, bmp, 0.0, 0.0);
953         cairo_t *ctx = cairo_create(ret);
954         cairo_paint(ctx);
955         cairo_destroy(ctx);
956
957         return ret;
958 }
959
960 void container_linux::get_media_features(litehtml::media_features& media) const
961 {
962         litehtml::position client;
963     get_client_rect(client);
964         media.type                      = litehtml::media_type_screen;
965         media.width                     = client.width;
966         media.height            = client.height;
967         media.device_width      = gdk_screen_width();
968         media.device_height     = gdk_screen_height();
969         media.color                     = 8;
970         media.monochrome        = 0;
971         media.color_index       = 256;
972         media.resolution        = 96;
973 }
974
975 void container_linux::get_language(litehtml::tstring& language, litehtml::tstring& culture) const
976 {
977         language = _t("en");
978         culture = _t("");
979 }
980
981 void container_linux::link(const std::shared_ptr<litehtml::document> &ptr, const litehtml::element::ptr& el)
982 {
983
984 }