a2b190859eed45253b19eefb9d11e28d98443f48
[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 void container_linux::draw_list_marker( litehtml::uint_ptr hdc, const litehtml::list_marker& marker )
221 {
222         if(!marker.image.empty())
223         {
224                 /*litehtml::tstring url;
225                 make_url(marker.image.c_str(), marker.baseurl, url);
226
227                 lock_images_cache();
228                 images_map::iterator img_i = m_images.find(url.c_str());
229                 if(img_i != m_images.end())
230                 {
231                         if(img_i->second)
232                         {
233                                 draw_txdib((cairo_t*) hdc, img_i->second, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height);
234                         }
235                 }
236                 unlock_images_cache();*/
237         } else
238         {
239                 switch(marker.marker_type)
240                 {
241                 case litehtml::list_style_type_circle:
242                         {
243                                 draw_ellipse((cairo_t*) hdc, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height, marker.color, 0.5);
244                         }
245                         break;
246                 case litehtml::list_style_type_disc:
247                         {
248                                 fill_ellipse((cairo_t*) hdc, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height, marker.color);
249                         }
250                         break;
251                 case litehtml::list_style_type_square:
252                         if(hdc)
253                         {
254                                 cairo_t* cr = (cairo_t*) hdc;
255                                 cairo_save(cr);
256
257                                 cairo_new_path(cr);
258                                 cairo_rectangle(cr, marker.pos.x, marker.pos.y, marker.pos.width, marker.pos.height);
259
260                                 set_color(cr, marker.color);
261                                 cairo_fill(cr);
262                                 cairo_restore(cr);
263                         }
264                         break;
265                 default:
266                         /*do nothing*/
267                         break;
268                 }
269         }
270 }
271
272 void container_linux::load_image( const litehtml::tchar_t* src, const litehtml::tchar_t* baseurl, bool redraw_on_ready )
273 {
274         litehtml::tstring url;
275         make_url(src, baseurl, url);
276         bool found = false;
277
278         for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) {
279                 const image *i = &(*ii);
280
281                 if (!strcmp(i->first.c_str(), url.c_str())) {
282                         found = true;
283                         break;
284                 }
285         }
286
287         if(!found)
288         {
289                 try
290                 {
291                         GdkPixbuf *img = get_image(url.c_str(), true);
292                         if(img)
293                         {
294                                 m_images.push_back(std::make_pair(url, img));
295                         }
296                 } catch(...)
297                 {
298                         int iii=0;
299                         iii++;
300                 }
301         }
302 }
303
304 void container_linux::get_image_size( const litehtml::tchar_t* src, const litehtml::tchar_t* baseurl, litehtml::size& sz )
305 {
306         litehtml::tstring url;
307         make_url(src, baseurl, url);
308         bool found = false;
309         const image *img = NULL;
310
311         for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) {
312                 const image *i = &(*ii);
313                 if (i->first == url) {
314                         img = i;
315                         found = true;
316                         break;
317                 }
318         }
319
320         if(img != NULL)
321         {
322                 sz.width        = gdk_pixbuf_get_width(img->second);
323                 sz.height       = gdk_pixbuf_get_height(img->second);
324         } else
325         {
326                 sz.width        = 0;
327                 sz.height       = 0;
328         }
329 }
330
331 void container_linux::draw_background( litehtml::uint_ptr hdc, const litehtml::background_paint& bg )
332 {
333         cairo_t* cr = (cairo_t*) hdc;
334         cairo_save(cr);
335         apply_clip(cr);
336
337         rounded_rectangle(cr, bg.border_box, bg.border_radius);
338         cairo_clip(cr);
339
340         cairo_rectangle(cr, bg.clip_box.x, bg.clip_box.y, bg.clip_box.width, bg.clip_box.height);
341         cairo_clip(cr);
342
343         if(bg.color.alpha)
344         {
345                 set_color(cr, bg.color);
346                 cairo_paint(cr);
347         }
348
349         litehtml::tstring url;
350         make_url(bg.image.c_str(), bg.baseurl.c_str(), url);
351
352         //lock_images_cache();
353         bool found = false;
354         const image *img_i = NULL;
355
356         for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) {
357                 const image *i = &(*ii);
358                 if (i->first == url) {
359                         img_i = i;
360                         found = true;
361                         break;
362                 }
363         }
364
365         if(img_i != NULL && img_i->second)
366         {
367                 GdkPixbuf *bgbmp = img_i->second;
368
369                 GdkPixbuf *new_img;
370                 if(bg.image_size.width != gdk_pixbuf_get_width(bgbmp) || bg.image_size.height != gdk_pixbuf_get_height(bgbmp))
371                 {
372                         new_img = gdk_pixbuf_scale_simple(bgbmp, bg.image_size.width, bg.image_size.height, GDK_INTERP_BILINEAR);
373                         bgbmp = new_img;
374                 }
375
376                 cairo_surface_t* img = surface_from_pixbuf(bgbmp);
377                 cairo_pattern_t *pattern = cairo_pattern_create_for_surface(img);
378                 cairo_matrix_t flib_m;
379                 cairo_matrix_init_identity(&flib_m);
380                 cairo_matrix_translate(&flib_m, -bg.position_x, -bg.position_y);
381                 cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
382                 cairo_pattern_set_matrix (pattern, &flib_m);
383
384                 switch(bg.repeat)
385                 {
386                 case litehtml::background_repeat_no_repeat:
387                         draw_pixbuf(cr, bgbmp, bg.position_x, bg.position_y, gdk_pixbuf_get_width(bgbmp), gdk_pixbuf_get_height(bgbmp));
388                         break;
389
390                 case litehtml::background_repeat_repeat_x:
391                         cairo_set_source(cr, pattern);
392                         cairo_rectangle(cr, bg.clip_box.left(), bg.position_y, bg.clip_box.width, gdk_pixbuf_get_height(bgbmp));
393                         cairo_fill(cr);
394                         break;
395
396                 case litehtml::background_repeat_repeat_y:
397                         cairo_set_source(cr, pattern);
398                         cairo_rectangle(cr, bg.position_x, bg.clip_box.top(), gdk_pixbuf_get_width(bgbmp), bg.clip_box.height);
399                         cairo_fill(cr);
400                         break;
401
402                 case litehtml::background_repeat_repeat:
403                         cairo_set_source(cr, pattern);
404                         cairo_rectangle(cr, bg.clip_box.left(), bg.clip_box.top(), bg.clip_box.width, bg.clip_box.height);
405                         cairo_fill(cr);
406                         break;
407                 }
408
409                 cairo_pattern_destroy(pattern);
410                 cairo_surface_destroy(img);
411
412         }
413 //      unlock_images_cache();
414         cairo_restore(cr);
415 }
416
417 void container_linux::make_url(const litehtml::tchar_t* url,    const litehtml::tchar_t* basepath, litehtml::tstring& out)
418 {
419         out = url;
420 }
421
422 void container_linux::add_path_arc(cairo_t* cr, double x, double y, double rx, double ry, double a1, double a2, bool neg)
423 {
424         if(rx > 0 && ry > 0)
425         {
426
427                 cairo_save(cr);
428
429                 cairo_translate(cr, x, y);
430                 cairo_scale(cr, 1, ry / rx);
431                 cairo_translate(cr, -x, -y);
432
433                 if(neg)
434                 {
435                         cairo_arc_negative(cr, x, y, rx, a1, a2);
436                 } else
437                 {
438                         cairo_arc(cr, x, y, rx, a1, a2);
439                 }
440
441                 cairo_restore(cr);
442         } else
443         {
444                 cairo_move_to(cr, x, y);
445         }
446 }
447
448 void container_linux::draw_borders(litehtml::uint_ptr hdc, const litehtml::borders& borders, const litehtml::position& draw_pos, bool root)
449 {
450         cairo_t* cr = (cairo_t*) hdc;
451         cairo_save(cr);
452         apply_clip(cr);
453
454         cairo_new_path(cr);
455
456         int bdr_top             = 0;
457         int bdr_bottom  = 0;
458         int bdr_left    = 0;
459         int bdr_right   = 0;
460
461         if(borders.top.width != 0 && borders.top.style > litehtml::border_style_hidden)
462         {
463                 bdr_top = (int) borders.top.width;
464         }
465         if(borders.bottom.width != 0 && borders.bottom.style > litehtml::border_style_hidden)
466         {
467                 bdr_bottom = (int) borders.bottom.width;
468         }
469         if(borders.left.width != 0 && borders.left.style > litehtml::border_style_hidden)
470         {
471                 bdr_left = (int) borders.left.width;
472         }
473         if(borders.right.width != 0 && borders.right.style > litehtml::border_style_hidden)
474         {
475                 bdr_right = (int) borders.right.width;
476         }
477
478         // draw right border
479         if(bdr_right)
480         {
481                 set_color(cr, borders.right.color);
482
483                 double r_top    = borders.radius.top_right_x;
484                 double r_bottom = borders.radius.bottom_right_x;
485
486                 if(r_top)
487                 {
488                         double end_angle        = 2 * M_PI;
489                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_top / (double) bdr_right + 1);
490
491                         add_path_arc(cr,
492                                 draw_pos.right() - r_top,
493                                 draw_pos.top() + r_top,
494                                 r_top - bdr_right,
495                                 r_top - bdr_right + (bdr_right - bdr_top),
496                                 end_angle,
497                                 start_angle, true);
498
499                         add_path_arc(cr,
500                                 draw_pos.right() - r_top,
501                                 draw_pos.top() + r_top,
502                                 r_top,
503                                 r_top,
504                                 start_angle,
505                                 end_angle, false);
506                 } else
507                 {
508                         cairo_move_to(cr, draw_pos.right() - bdr_right, draw_pos.top() + bdr_top);
509                         cairo_line_to(cr, draw_pos.right(), draw_pos.top());
510                 }
511
512                 if(r_bottom)
513                 {
514                         cairo_line_to(cr, draw_pos.right(),     draw_pos.bottom() - r_bottom);
515
516                         double start_angle      = 0;
517                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_bottom / (double) bdr_right + 1);
518
519                         add_path_arc(cr,
520                                 draw_pos.right() - r_bottom,
521                                 draw_pos.bottom() - r_bottom,
522                                 r_bottom,
523                                 r_bottom,
524                                 start_angle,
525                                 end_angle, false);
526
527                         add_path_arc(cr,
528                                 draw_pos.right() - r_bottom,
529                                 draw_pos.bottom() - r_bottom,
530                                 r_bottom - bdr_right,
531                                 r_bottom - bdr_right + (bdr_right - bdr_bottom),
532                                 end_angle,
533                                 start_angle, true);
534                 } else
535                 {
536                         cairo_line_to(cr, draw_pos.right(),     draw_pos.bottom());
537                         cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.bottom() - bdr_bottom);
538                 }
539
540                 cairo_fill(cr);
541         }
542
543         // draw bottom border
544         if(bdr_bottom)
545         {
546                 set_color(cr, borders.bottom.color);
547
548                 double r_left   = borders.radius.bottom_left_x;
549                 double r_right  = borders.radius.bottom_right_x;
550
551                 if(r_left)
552                 {
553                         double start_angle      = M_PI / 2.0;
554                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_left / (double) bdr_bottom + 1);
555
556                         add_path_arc(cr,
557                                 draw_pos.left() + r_left,
558                                 draw_pos.bottom() - r_left,
559                                 r_left - bdr_bottom + (bdr_bottom - bdr_left),
560                                 r_left - bdr_bottom,
561                                 start_angle,
562                                 end_angle, false);
563
564                         add_path_arc(cr,
565                                 draw_pos.left() + r_left,
566                                 draw_pos.bottom() - r_left,
567                                 r_left,
568                                 r_left,
569                                 end_angle,
570                                 start_angle, true);
571                 } else
572                 {
573                         cairo_move_to(cr, draw_pos.left(), draw_pos.bottom());
574                         cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.bottom() - bdr_bottom);
575                 }
576
577                 if(r_right)
578                 {
579                         cairo_line_to(cr, draw_pos.right() - r_right,   draw_pos.bottom());
580
581                         double end_angle        = M_PI / 2.0;
582                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_right / (double) bdr_bottom + 1);
583
584                         add_path_arc(cr,
585                                 draw_pos.right() - r_right,
586                                 draw_pos.bottom() - r_right,
587                                 r_right,
588                                 r_right,
589                                 end_angle,
590                                 start_angle, true);
591
592                         add_path_arc(cr,
593                                 draw_pos.right() - r_right,
594                                 draw_pos.bottom() - r_right,
595                                 r_right - bdr_bottom + (bdr_bottom - bdr_right),
596                                 r_right - bdr_bottom,
597                                 start_angle,
598                                 end_angle, false);
599                 } else
600                 {
601                         cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.bottom() - bdr_bottom);
602                         cairo_line_to(cr, draw_pos.right(),     draw_pos.bottom());
603                 }
604
605                 cairo_fill(cr);
606         }
607
608         // draw top border
609         if(bdr_top)
610         {
611                 set_color(cr, borders.top.color);
612
613                 double r_left   = borders.radius.top_left_x;
614                 double r_right  = borders.radius.top_right_x;
615
616                 if(r_left)
617                 {
618                         double end_angle        = M_PI * 3.0 / 2.0;
619                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_left / (double) bdr_top + 1);
620
621                         add_path_arc(cr,
622                                 draw_pos.left() + r_left,
623                                 draw_pos.top() + r_left,
624                                 r_left,
625                                 r_left,
626                                 end_angle,
627                                 start_angle, true);
628
629                         add_path_arc(cr,
630                                 draw_pos.left() + r_left,
631                                 draw_pos.top() + r_left,
632                                 r_left - bdr_top + (bdr_top - bdr_left),
633                                 r_left - bdr_top,
634                                 start_angle,
635                                 end_angle, false);
636                 } else
637                 {
638                         cairo_move_to(cr, draw_pos.left(), draw_pos.top());
639                         cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top);
640                 }
641
642                 if(r_right)
643                 {
644                         cairo_line_to(cr, draw_pos.right() - r_right,   draw_pos.top() + bdr_top);
645
646                         double start_angle      = M_PI * 3.0 / 2.0;
647                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_right / (double) bdr_top + 1);
648
649                         add_path_arc(cr,
650                                 draw_pos.right() - r_right,
651                                 draw_pos.top() + r_right,
652                                 r_right - bdr_top + (bdr_top - bdr_right),
653                                 r_right - bdr_top,
654                                 start_angle,
655                                 end_angle, false);
656
657                         add_path_arc(cr,
658                                 draw_pos.right() - r_right,
659                                 draw_pos.top() + r_right,
660                                 r_right,
661                                 r_right,
662                                 end_angle,
663                                 start_angle, true);
664                 } else
665                 {
666                         cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.top() + bdr_top);
667                         cairo_line_to(cr, draw_pos.right(),     draw_pos.top());
668                 }
669
670                 cairo_fill(cr);
671         }
672
673         // draw left border
674         if(bdr_left)
675         {
676                 set_color(cr, borders.left.color);
677
678                 double r_top    = borders.radius.top_left_x;
679                 double r_bottom = borders.radius.bottom_left_x;
680
681                 if(r_top)
682                 {
683                         double start_angle      = M_PI;
684                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_top / (double) bdr_left + 1);
685
686                         add_path_arc(cr,
687                                 draw_pos.left() + r_top,
688                                 draw_pos.top() + r_top,
689                                 r_top - bdr_left,
690                                 r_top - bdr_left + (bdr_left - bdr_top),
691                                 start_angle,
692                                 end_angle, false);
693
694                         add_path_arc(cr,
695                                 draw_pos.left() + r_top,
696                                 draw_pos.top() + r_top,
697                                 r_top,
698                                 r_top,
699                                 end_angle,
700                                 start_angle, true);
701                 } else
702                 {
703                         cairo_move_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top);
704                         cairo_line_to(cr, draw_pos.left(), draw_pos.top());
705                 }
706
707                 if(r_bottom)
708                 {
709                         cairo_line_to(cr, draw_pos.left(),      draw_pos.bottom() - r_bottom);
710
711                         double end_angle        = M_PI;
712                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_bottom / (double) bdr_left + 1);
713
714                         add_path_arc(cr,
715                                 draw_pos.left() + r_bottom,
716                                 draw_pos.bottom() - r_bottom,
717                                 r_bottom,
718                                 r_bottom,
719                                 end_angle,
720                                 start_angle, true);
721
722                         add_path_arc(cr,
723                                 draw_pos.left() + r_bottom,
724                                 draw_pos.bottom() - r_bottom,
725                                 r_bottom - bdr_left,
726                                 r_bottom - bdr_left + (bdr_left - bdr_bottom),
727                                 start_angle,
728                                 end_angle, false);
729                 } else
730                 {
731                         cairo_line_to(cr, draw_pos.left(),      draw_pos.bottom());
732                         cairo_line_to(cr, draw_pos.left() + bdr_left,   draw_pos.bottom() - bdr_bottom);
733                 }
734
735                 cairo_fill(cr);
736         }
737         cairo_restore(cr);
738 }
739
740 void container_linux::transform_text(litehtml::tstring& text, litehtml::text_transform tt)
741 {
742
743 }
744
745 void container_linux::set_clip( const litehtml::position& pos, const litehtml::border_radiuses& bdr_radius, bool valid_x, bool valid_y )
746 {
747         litehtml::position clip_pos = pos;
748         litehtml::position client_pos;
749         get_client_rect(client_pos);
750         if(!valid_x)
751         {
752                 clip_pos.x              = client_pos.x;
753                 clip_pos.width  = client_pos.width;
754         }
755         if(!valid_y)
756         {
757                 clip_pos.y              = client_pos.y;
758                 clip_pos.height = client_pos.height;
759         }
760         m_clips.emplace_back(clip_pos, bdr_radius);
761 }
762
763 void container_linux::del_clip()
764 {
765         if(!m_clips.empty())
766         {
767                 m_clips.pop_back();
768         }
769 }
770
771 void container_linux::apply_clip( cairo_t* cr )
772 {
773         for(const auto& clip_box : m_clips)
774         {
775                 rounded_rectangle(cr, clip_box.box, clip_box.radius);
776                 cairo_clip(cr);
777         }
778 }
779
780 void container_linux::draw_ellipse( cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color, int line_width )
781 {
782         if(!cr) return;
783         cairo_save(cr);
784
785         apply_clip(cr);
786
787         cairo_new_path(cr);
788
789         cairo_translate (cr, x + width / 2.0, y + height / 2.0);
790         cairo_scale (cr, width / 2.0, height / 2.0);
791         cairo_arc (cr, 0, 0, 1, 0, 2 * M_PI);
792
793         set_color(cr, color);
794         cairo_set_line_width(cr, line_width);
795         cairo_stroke(cr);
796
797         cairo_restore(cr);
798 }
799
800 void container_linux::fill_ellipse( cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color )
801 {
802         if(!cr) return;
803         cairo_save(cr);
804
805         apply_clip(cr);
806
807         cairo_new_path(cr);
808
809         cairo_translate (cr, x + width / 2.0, y + height / 2.0);
810         cairo_scale (cr, width / 2.0, height / 2.0);
811         cairo_arc (cr, 0, 0, 1, 0, 2 * M_PI);
812
813         set_color(cr, color);
814         cairo_fill(cr);
815
816         cairo_restore(cr);
817 }
818
819 void container_linux::clear_images()
820 {
821         for(auto i = m_images.begin(); i != m_images.end(); ++i) {
822                 image *img = &(*i);
823
824                 if (img->second) {
825                         g_object_unref(img->second);
826                 }
827         }
828
829         m_images.clear();
830 }
831
832 gint container_linux::clear_images(gint desired_size)
833 {
834         gint size = 0;
835         gint num = 0;
836
837         /* First, tally up size of all the stored GdkPixbufs and
838          * deallocate those which make the total size be above
839          * the desired_size limit. We will remove their list
840          * elements later. */
841         for (auto i = m_images.rbegin(); i != m_images.rend(); ++i) {
842                 image *img = &(*i);
843                 gint cursize;
844
845                 if (img->second == NULL)
846                         continue;
847
848                 cursize = gdk_pixbuf_get_byte_length(img->second);
849
850                 if (size + cursize > desired_size) {
851                         g_object_unref(img->second);
852                         img->second = NULL;
853                         num++;
854                 } else {
855                         size += cursize;
856                 }
857         }
858
859         /* Remove elements whose GdkPixbuf pointers point to NULL. */
860         m_images.remove_if([&](image _img) -> bool {
861                         if (_img.second == NULL)
862                                 return true;
863                         return false;
864                         });
865
866         return num;
867 }
868
869 std::shared_ptr<litehtml::element>      container_linux::create_element(const litehtml::tchar_t *tag_name,
870                                                                                                                                           const litehtml::string_map &attributes,
871                                                                                                                                           const std::shared_ptr<litehtml::document> &doc)
872 {
873         return 0;
874 }
875
876 void container_linux::rounded_rectangle( cairo_t* cr, const litehtml::position &pos, const litehtml::border_radiuses &radius )
877 {
878         cairo_new_path(cr);
879         if(radius.top_left_x)
880         {
881                 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);
882         } else
883         {
884                 cairo_move_to(cr, pos.left(), pos.top());
885         }
886
887         cairo_line_to(cr, pos.right() - radius.top_right_x, pos.top());
888
889         if(radius.top_right_x)
890         {
891                 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);
892         }
893
894         cairo_line_to(cr, pos.right(), pos.bottom() - radius.bottom_right_x);
895
896         if(radius.bottom_right_x)
897         {
898                 cairo_arc(cr, pos.right() - radius.bottom_right_x, pos.bottom() - radius.bottom_right_x, radius.bottom_right_x, 0, M_PI / 2.0);
899         }
900
901         cairo_line_to(cr, pos.left() - radius.bottom_left_x, pos.bottom());
902
903         if(radius.bottom_left_x)
904         {
905                 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);
906         }
907 }
908
909 void container_linux::draw_pixbuf(cairo_t* cr, const GdkPixbuf *bmp, int x,     int y, int cx, int cy)
910 {
911         cairo_save(cr);
912
913         {
914                 cairo_matrix_t flib_m;
915                 cairo_matrix_init(&flib_m, 1, 0, 0, -1, 0, 0);
916
917                 if(cx != gdk_pixbuf_get_width(bmp) || cy != gdk_pixbuf_get_height(bmp))
918                 {
919                         GdkPixbuf *new_img = gdk_pixbuf_scale_simple(bmp, cx, cy, GDK_INTERP_BILINEAR);
920                         gdk_cairo_set_source_pixbuf(cr, new_img, x, y);
921                         cairo_paint(cr);
922                 } else
923                 {
924                         gdk_cairo_set_source_pixbuf(cr, bmp, x, y);
925                         cairo_paint(cr);
926                 }
927         }
928
929         cairo_restore(cr);
930 }
931
932 cairo_surface_t* container_linux::surface_from_pixbuf(const GdkPixbuf *bmp)
933 {
934         cairo_surface_t* ret = NULL;
935
936         if(gdk_pixbuf_get_has_alpha(bmp))
937         {
938                 ret = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, gdk_pixbuf_get_width(bmp), gdk_pixbuf_get_height(bmp));
939         } else
940         {
941                 ret = cairo_image_surface_create(CAIRO_FORMAT_RGB24, gdk_pixbuf_get_width(bmp), gdk_pixbuf_get_height(bmp));
942         }
943
944 //      Cairo::RefPtr<Cairo::Surface> surface(new Cairo::Surface(ret, false));
945 //      Cairo::RefPtr<Cairo::Context> ctx = Cairo::Context::create(surface);
946 //      Gdk::Cairo::set_source_pixbuf(ctx, bmp, 0.0, 0.0);
947         cairo_t *ctx = cairo_create(ret);
948         cairo_paint(ctx);
949         cairo_destroy(ctx);
950
951         return ret;
952 }
953
954 void container_linux::get_media_features(litehtml::media_features& media) const
955 {
956         litehtml::position client;
957     get_client_rect(client);
958         media.type                      = litehtml::media_type_screen;
959         media.width                     = client.width;
960         media.height            = client.height;
961         media.device_width      = gdk_screen_width();
962         media.device_height     = gdk_screen_height();
963         media.color                     = 8;
964         media.monochrome        = 0;
965         media.color_index       = 256;
966         media.resolution        = 96;
967 }
968
969 void container_linux::get_language(litehtml::tstring& language, litehtml::tstring& culture) const
970 {
971         language = _t("en");
972         culture = _t("");
973 }
974
975 void container_linux::link(const std::shared_ptr<litehtml::document> &ptr, const litehtml::element::ptr& el)
976 {
977
978 }