Use Pango to render text in 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) 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 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::load_image( const litehtml::tchar_t* src, const litehtml::tchar_t* baseurl, bool redraw_on_ready )
110 {
111         litehtml::tstring url;
112         make_url(src, baseurl, url);
113         bool found = false;
114
115         for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) {
116                 const image *i = &(*ii);
117
118                 if (!strcmp(i->first.c_str(), url.c_str())) {
119                         found = true;
120                         break;
121                 }
122         }
123
124         if(!found)
125         {
126                 try
127                 {
128                         GdkPixbuf *img = get_image(url.c_str(), true);
129                         if(img)
130                         {
131                                 m_images.push_back(std::make_pair(url, img));
132                         }
133                 } catch(...)
134                 {
135                         int iii=0;
136                         iii++;
137                 }
138         }
139 }
140
141 void container_linux::get_image_size( const litehtml::tchar_t* src, const litehtml::tchar_t* baseurl, litehtml::size& sz )
142 {
143         litehtml::tstring url;
144         make_url(src, baseurl, url);
145         bool found = false;
146         const image *img = NULL;
147
148         for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) {
149                 const image *i = &(*ii);
150                 if (i->first == url) {
151                         img = i;
152                         found = true;
153                         break;
154                 }
155         }
156
157         if(img != NULL)
158         {
159                 sz.width        = gdk_pixbuf_get_width(img->second);
160                 sz.height       = gdk_pixbuf_get_height(img->second);
161         } else
162         {
163                 sz.width        = 0;
164                 sz.height       = 0;
165         }
166 }
167
168 void container_linux::draw_background( litehtml::uint_ptr hdc, const litehtml::background_paint& bg )
169 {
170         cairo_t* cr = (cairo_t*) hdc;
171         cairo_save(cr);
172         apply_clip(cr);
173
174         rounded_rectangle(cr, bg.border_box, bg.border_radius);
175         cairo_clip(cr);
176
177         cairo_rectangle(cr, bg.clip_box.x, bg.clip_box.y, bg.clip_box.width, bg.clip_box.height);
178         cairo_clip(cr);
179
180         if(bg.color.alpha)
181         {
182                 set_color(cr, bg.color);
183                 cairo_paint(cr);
184         }
185
186         litehtml::tstring url;
187         make_url(bg.image.c_str(), bg.baseurl.c_str(), url);
188
189         //lock_images_cache();
190         bool found = false;
191         const image *img_i = NULL;
192
193         for (auto ii = m_images.cbegin(); ii != m_images.cend(); ++ii) {
194                 const image *i = &(*ii);
195                 if (i->first == url) {
196                         img_i = i;
197                         found = true;
198                         break;
199                 }
200         }
201
202         if(img_i != NULL && img_i->second)
203         {
204                 GdkPixbuf *bgbmp = img_i->second;
205
206                 GdkPixbuf *new_img;
207                 if(bg.image_size.width != gdk_pixbuf_get_width(bgbmp) || bg.image_size.height != gdk_pixbuf_get_height(bgbmp))
208                 {
209                         new_img = gdk_pixbuf_scale_simple(bgbmp, bg.image_size.width, bg.image_size.height, GDK_INTERP_BILINEAR);
210                         bgbmp = new_img;
211                 }
212
213                 cairo_surface_t* img = surface_from_pixbuf(bgbmp);
214                 cairo_pattern_t *pattern = cairo_pattern_create_for_surface(img);
215                 cairo_matrix_t flib_m;
216                 cairo_matrix_init_identity(&flib_m);
217                 cairo_matrix_translate(&flib_m, -bg.position_x, -bg.position_y);
218                 cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
219                 cairo_pattern_set_matrix (pattern, &flib_m);
220
221                 switch(bg.repeat)
222                 {
223                 case litehtml::background_repeat_no_repeat:
224                         draw_pixbuf(cr, bgbmp, bg.position_x, bg.position_y, gdk_pixbuf_get_width(bgbmp), gdk_pixbuf_get_height(bgbmp));
225                         break;
226
227                 case litehtml::background_repeat_repeat_x:
228                         cairo_set_source(cr, pattern);
229                         cairo_rectangle(cr, bg.clip_box.left(), bg.position_y, bg.clip_box.width, gdk_pixbuf_get_height(bgbmp));
230                         cairo_fill(cr);
231                         break;
232
233                 case litehtml::background_repeat_repeat_y:
234                         cairo_set_source(cr, pattern);
235                         cairo_rectangle(cr, bg.position_x, bg.clip_box.top(), gdk_pixbuf_get_width(bgbmp), bg.clip_box.height);
236                         cairo_fill(cr);
237                         break;
238
239                 case litehtml::background_repeat_repeat:
240                         cairo_set_source(cr, pattern);
241                         cairo_rectangle(cr, bg.clip_box.left(), bg.clip_box.top(), bg.clip_box.width, bg.clip_box.height);
242                         cairo_fill(cr);
243                         break;
244                 }
245
246                 cairo_pattern_destroy(pattern);
247                 cairo_surface_destroy(img);
248
249         }
250 //      unlock_images_cache();
251         cairo_restore(cr);
252 }
253
254 void container_linux::make_url(const litehtml::tchar_t* url,    const litehtml::tchar_t* basepath, litehtml::tstring& out)
255 {
256         out = url;
257 }
258
259 void container_linux::add_path_arc(cairo_t* cr, double x, double y, double rx, double ry, double a1, double a2, bool neg)
260 {
261         if(rx > 0 && ry > 0)
262         {
263
264                 cairo_save(cr);
265
266                 cairo_translate(cr, x, y);
267                 cairo_scale(cr, 1, ry / rx);
268                 cairo_translate(cr, -x, -y);
269
270                 if(neg)
271                 {
272                         cairo_arc_negative(cr, x, y, rx, a1, a2);
273                 } else
274                 {
275                         cairo_arc(cr, x, y, rx, a1, a2);
276                 }
277
278                 cairo_restore(cr);
279         } else
280         {
281                 cairo_move_to(cr, x, y);
282         }
283 }
284
285 void container_linux::draw_borders(litehtml::uint_ptr hdc, const litehtml::borders& borders, const litehtml::position& draw_pos, bool root)
286 {
287         cairo_t* cr = (cairo_t*) hdc;
288         cairo_save(cr);
289         apply_clip(cr);
290
291         cairo_new_path(cr);
292
293         int bdr_top             = 0;
294         int bdr_bottom  = 0;
295         int bdr_left    = 0;
296         int bdr_right   = 0;
297
298         if(borders.top.width != 0 && borders.top.style > litehtml::border_style_hidden)
299         {
300                 bdr_top = (int) borders.top.width;
301         }
302         if(borders.bottom.width != 0 && borders.bottom.style > litehtml::border_style_hidden)
303         {
304                 bdr_bottom = (int) borders.bottom.width;
305         }
306         if(borders.left.width != 0 && borders.left.style > litehtml::border_style_hidden)
307         {
308                 bdr_left = (int) borders.left.width;
309         }
310         if(borders.right.width != 0 && borders.right.style > litehtml::border_style_hidden)
311         {
312                 bdr_right = (int) borders.right.width;
313         }
314
315         // draw right border
316         if(bdr_right)
317         {
318                 set_color(cr, borders.right.color);
319
320                 double r_top    = borders.radius.top_right_x;
321                 double r_bottom = borders.radius.bottom_right_x;
322
323                 if(r_top)
324                 {
325                         double end_angle        = 2 * M_PI;
326                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_top / (double) bdr_right + 1);
327
328                         add_path_arc(cr,
329                                 draw_pos.right() - r_top,
330                                 draw_pos.top() + r_top,
331                                 r_top - bdr_right,
332                                 r_top - bdr_right + (bdr_right - bdr_top),
333                                 end_angle,
334                                 start_angle, true);
335
336                         add_path_arc(cr,
337                                 draw_pos.right() - r_top,
338                                 draw_pos.top() + r_top,
339                                 r_top,
340                                 r_top,
341                                 start_angle,
342                                 end_angle, false);
343                 } else
344                 {
345                         cairo_move_to(cr, draw_pos.right() - bdr_right, draw_pos.top() + bdr_top);
346                         cairo_line_to(cr, draw_pos.right(), draw_pos.top());
347                 }
348
349                 if(r_bottom)
350                 {
351                         cairo_line_to(cr, draw_pos.right(),     draw_pos.bottom() - r_bottom);
352
353                         double start_angle      = 0;
354                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_bottom / (double) bdr_right + 1);
355
356                         add_path_arc(cr,
357                                 draw_pos.right() - r_bottom,
358                                 draw_pos.bottom() - r_bottom,
359                                 r_bottom,
360                                 r_bottom,
361                                 start_angle,
362                                 end_angle, false);
363
364                         add_path_arc(cr,
365                                 draw_pos.right() - r_bottom,
366                                 draw_pos.bottom() - r_bottom,
367                                 r_bottom - bdr_right,
368                                 r_bottom - bdr_right + (bdr_right - bdr_bottom),
369                                 end_angle,
370                                 start_angle, true);
371                 } else
372                 {
373                         cairo_line_to(cr, draw_pos.right(),     draw_pos.bottom());
374                         cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.bottom() - bdr_bottom);
375                 }
376
377                 cairo_fill(cr);
378         }
379
380         // draw bottom border
381         if(bdr_bottom)
382         {
383                 set_color(cr, borders.bottom.color);
384
385                 double r_left   = borders.radius.bottom_left_x;
386                 double r_right  = borders.radius.bottom_right_x;
387
388                 if(r_left)
389                 {
390                         double start_angle      = M_PI / 2.0;
391                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_left / (double) bdr_bottom + 1);
392
393                         add_path_arc(cr,
394                                 draw_pos.left() + r_left,
395                                 draw_pos.bottom() - r_left,
396                                 r_left - bdr_bottom + (bdr_bottom - bdr_left),
397                                 r_left - bdr_bottom,
398                                 start_angle,
399                                 end_angle, false);
400
401                         add_path_arc(cr,
402                                 draw_pos.left() + r_left,
403                                 draw_pos.bottom() - r_left,
404                                 r_left,
405                                 r_left,
406                                 end_angle,
407                                 start_angle, true);
408                 } else
409                 {
410                         cairo_move_to(cr, draw_pos.left(), draw_pos.bottom());
411                         cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.bottom() - bdr_bottom);
412                 }
413
414                 if(r_right)
415                 {
416                         cairo_line_to(cr, draw_pos.right() - r_right,   draw_pos.bottom());
417
418                         double end_angle        = M_PI / 2.0;
419                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_right / (double) bdr_bottom + 1);
420
421                         add_path_arc(cr,
422                                 draw_pos.right() - r_right,
423                                 draw_pos.bottom() - r_right,
424                                 r_right,
425                                 r_right,
426                                 end_angle,
427                                 start_angle, true);
428
429                         add_path_arc(cr,
430                                 draw_pos.right() - r_right,
431                                 draw_pos.bottom() - r_right,
432                                 r_right - bdr_bottom + (bdr_bottom - bdr_right),
433                                 r_right - bdr_bottom,
434                                 start_angle,
435                                 end_angle, false);
436                 } else
437                 {
438                         cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.bottom() - bdr_bottom);
439                         cairo_line_to(cr, draw_pos.right(),     draw_pos.bottom());
440                 }
441
442                 cairo_fill(cr);
443         }
444
445         // draw top border
446         if(bdr_top)
447         {
448                 set_color(cr, borders.top.color);
449
450                 double r_left   = borders.radius.top_left_x;
451                 double r_right  = borders.radius.top_right_x;
452
453                 if(r_left)
454                 {
455                         double end_angle        = M_PI * 3.0 / 2.0;
456                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_left / (double) bdr_top + 1);
457
458                         add_path_arc(cr,
459                                 draw_pos.left() + r_left,
460                                 draw_pos.top() + r_left,
461                                 r_left,
462                                 r_left,
463                                 end_angle,
464                                 start_angle, true);
465
466                         add_path_arc(cr,
467                                 draw_pos.left() + r_left,
468                                 draw_pos.top() + r_left,
469                                 r_left - bdr_top + (bdr_top - bdr_left),
470                                 r_left - bdr_top,
471                                 start_angle,
472                                 end_angle, false);
473                 } else
474                 {
475                         cairo_move_to(cr, draw_pos.left(), draw_pos.top());
476                         cairo_line_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top);
477                 }
478
479                 if(r_right)
480                 {
481                         cairo_line_to(cr, draw_pos.right() - r_right,   draw_pos.top() + bdr_top);
482
483                         double start_angle      = M_PI * 3.0 / 2.0;
484                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_right / (double) bdr_top + 1);
485
486                         add_path_arc(cr,
487                                 draw_pos.right() - r_right,
488                                 draw_pos.top() + r_right,
489                                 r_right - bdr_top + (bdr_top - bdr_right),
490                                 r_right - bdr_top,
491                                 start_angle,
492                                 end_angle, false);
493
494                         add_path_arc(cr,
495                                 draw_pos.right() - r_right,
496                                 draw_pos.top() + r_right,
497                                 r_right,
498                                 r_right,
499                                 end_angle,
500                                 start_angle, true);
501                 } else
502                 {
503                         cairo_line_to(cr, draw_pos.right() - bdr_right, draw_pos.top() + bdr_top);
504                         cairo_line_to(cr, draw_pos.right(),     draw_pos.top());
505                 }
506
507                 cairo_fill(cr);
508         }
509
510         // draw left border
511         if(bdr_left)
512         {
513                 set_color(cr, borders.left.color);
514
515                 double r_top    = borders.radius.top_left_x;
516                 double r_bottom = borders.radius.bottom_left_x;
517
518                 if(r_top)
519                 {
520                         double start_angle      = M_PI;
521                         double end_angle        = start_angle + M_PI / 2.0  / ((double) bdr_top / (double) bdr_left + 1);
522
523                         add_path_arc(cr,
524                                 draw_pos.left() + r_top,
525                                 draw_pos.top() + r_top,
526                                 r_top - bdr_left,
527                                 r_top - bdr_left + (bdr_left - bdr_top),
528                                 start_angle,
529                                 end_angle, false);
530
531                         add_path_arc(cr,
532                                 draw_pos.left() + r_top,
533                                 draw_pos.top() + r_top,
534                                 r_top,
535                                 r_top,
536                                 end_angle,
537                                 start_angle, true);
538                 } else
539                 {
540                         cairo_move_to(cr, draw_pos.left() + bdr_left, draw_pos.top() + bdr_top);
541                         cairo_line_to(cr, draw_pos.left(), draw_pos.top());
542                 }
543
544                 if(r_bottom)
545                 {
546                         cairo_line_to(cr, draw_pos.left(),      draw_pos.bottom() - r_bottom);
547
548                         double end_angle        = M_PI;
549                         double start_angle      = end_angle - M_PI / 2.0  / ((double) bdr_bottom / (double) bdr_left + 1);
550
551                         add_path_arc(cr,
552                                 draw_pos.left() + r_bottom,
553                                 draw_pos.bottom() - r_bottom,
554                                 r_bottom,
555                                 r_bottom,
556                                 end_angle,
557                                 start_angle, true);
558
559                         add_path_arc(cr,
560                                 draw_pos.left() + r_bottom,
561                                 draw_pos.bottom() - r_bottom,
562                                 r_bottom - bdr_left,
563                                 r_bottom - bdr_left + (bdr_left - bdr_bottom),
564                                 start_angle,
565                                 end_angle, false);
566                 } else
567                 {
568                         cairo_line_to(cr, draw_pos.left(),      draw_pos.bottom());
569                         cairo_line_to(cr, draw_pos.left() + bdr_left,   draw_pos.bottom() - bdr_bottom);
570                 }
571
572                 cairo_fill(cr);
573         }
574         cairo_restore(cr);
575 }
576
577 void container_linux::transform_text(litehtml::tstring& text, litehtml::text_transform tt)
578 {
579
580 }
581
582 void container_linux::set_clip( const litehtml::position& pos, const litehtml::border_radiuses& bdr_radius, bool valid_x, bool valid_y )
583 {
584         litehtml::position clip_pos = pos;
585         litehtml::position client_pos;
586         get_client_rect(client_pos);
587         if(!valid_x)
588         {
589                 clip_pos.x              = client_pos.x;
590                 clip_pos.width  = client_pos.width;
591         }
592         if(!valid_y)
593         {
594                 clip_pos.y              = client_pos.y;
595                 clip_pos.height = client_pos.height;
596         }
597         m_clips.emplace_back(clip_pos, bdr_radius);
598 }
599
600 void container_linux::del_clip()
601 {
602         if(!m_clips.empty())
603         {
604                 m_clips.pop_back();
605         }
606 }
607
608 void container_linux::apply_clip( cairo_t* cr )
609 {
610         for(const auto& clip_box : m_clips)
611         {
612                 rounded_rectangle(cr, clip_box.box, clip_box.radius);
613                 cairo_clip(cr);
614         }
615 }
616
617 void container_linux::draw_ellipse( cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color, int line_width )
618 {
619         if(!cr) return;
620         cairo_save(cr);
621
622         apply_clip(cr);
623
624         cairo_new_path(cr);
625
626         cairo_translate (cr, x + width / 2.0, y + height / 2.0);
627         cairo_scale (cr, width / 2.0, height / 2.0);
628         cairo_arc (cr, 0, 0, 1, 0, 2 * M_PI);
629
630         set_color(cr, color);
631         cairo_set_line_width(cr, line_width);
632         cairo_stroke(cr);
633
634         cairo_restore(cr);
635 }
636
637 void container_linux::fill_ellipse( cairo_t* cr, int x, int y, int width, int height, const litehtml::web_color& color )
638 {
639         if(!cr) return;
640         cairo_save(cr);
641
642         apply_clip(cr);
643
644         cairo_new_path(cr);
645
646         cairo_translate (cr, x + width / 2.0, y + height / 2.0);
647         cairo_scale (cr, width / 2.0, height / 2.0);
648         cairo_arc (cr, 0, 0, 1, 0, 2 * M_PI);
649
650         set_color(cr, color);
651         cairo_fill(cr);
652
653         cairo_restore(cr);
654 }
655
656 void container_linux::clear_images()
657 {
658         for(auto i = m_images.begin(); i != m_images.end(); ++i) {
659                 image *img = &(*i);
660
661                 if (img->second) {
662                         g_object_unref(img->second);
663                 }
664         }
665
666         m_images.clear();
667 }
668
669 gint container_linux::clear_images(gint desired_size)
670 {
671         gint size = 0;
672         gint num = 0;
673
674         /* First, tally up size of all the stored GdkPixbufs and
675          * deallocate those which make the total size be above
676          * the desired_size limit. We will remove their list
677          * elements later. */
678         for (auto i = m_images.rbegin(); i != m_images.rend(); ++i) {
679                 image *img = &(*i);
680                 gint cursize;
681
682                 if (img->second == NULL)
683                         continue;
684
685                 cursize = gdk_pixbuf_get_byte_length(img->second);
686
687                 if (size + cursize > desired_size) {
688                         g_object_unref(img->second);
689                         img->second = NULL;
690                         num++;
691                 } else {
692                         size += cursize;
693                 }
694         }
695
696         /* Remove elements whose GdkPixbuf pointers point to NULL. */
697         m_images.remove_if([&](image _img) -> bool {
698                         if (_img.second == NULL)
699                                 return true;
700                         return false;
701                         });
702
703         return num;
704 }
705
706 std::shared_ptr<litehtml::element>      container_linux::create_element(const litehtml::tchar_t *tag_name,
707                                                                                                                                           const litehtml::string_map &attributes,
708                                                                                                                                           const std::shared_ptr<litehtml::document> &doc)
709 {
710         return 0;
711 }
712
713 void container_linux::rounded_rectangle( cairo_t* cr, const litehtml::position &pos, const litehtml::border_radiuses &radius )
714 {
715         cairo_new_path(cr);
716         if(radius.top_left_x)
717         {
718                 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);
719         } else
720         {
721                 cairo_move_to(cr, pos.left(), pos.top());
722         }
723
724         cairo_line_to(cr, pos.right() - radius.top_right_x, pos.top());
725
726         if(radius.top_right_x)
727         {
728                 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);
729         }
730
731         cairo_line_to(cr, pos.right(), pos.bottom() - radius.bottom_right_x);
732
733         if(radius.bottom_right_x)
734         {
735                 cairo_arc(cr, pos.right() - radius.bottom_right_x, pos.bottom() - radius.bottom_right_x, radius.bottom_right_x, 0, M_PI / 2.0);
736         }
737
738         cairo_line_to(cr, pos.left() - radius.bottom_left_x, pos.bottom());
739
740         if(radius.bottom_left_x)
741         {
742                 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);
743         }
744 }
745
746 void container_linux::draw_pixbuf(cairo_t* cr, const GdkPixbuf *bmp, int x,     int y, int cx, int cy)
747 {
748         cairo_save(cr);
749
750         {
751                 cairo_matrix_t flib_m;
752                 cairo_matrix_init(&flib_m, 1, 0, 0, -1, 0, 0);
753
754                 if(cx != gdk_pixbuf_get_width(bmp) || cy != gdk_pixbuf_get_height(bmp))
755                 {
756                         GdkPixbuf *new_img = gdk_pixbuf_scale_simple(bmp, cx, cy, GDK_INTERP_BILINEAR);
757                         gdk_cairo_set_source_pixbuf(cr, new_img, x, y);
758                         cairo_paint(cr);
759                 } else
760                 {
761                         gdk_cairo_set_source_pixbuf(cr, bmp, x, y);
762                         cairo_paint(cr);
763                 }
764         }
765
766         cairo_restore(cr);
767 }
768
769 cairo_surface_t* container_linux::surface_from_pixbuf(const GdkPixbuf *bmp)
770 {
771         cairo_surface_t* ret = NULL;
772
773         if(gdk_pixbuf_get_has_alpha(bmp))
774         {
775                 ret = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, gdk_pixbuf_get_width(bmp), gdk_pixbuf_get_height(bmp));
776         } else
777         {
778                 ret = cairo_image_surface_create(CAIRO_FORMAT_RGB24, gdk_pixbuf_get_width(bmp), gdk_pixbuf_get_height(bmp));
779         }
780
781 //      Cairo::RefPtr<Cairo::Surface> surface(new Cairo::Surface(ret, false));
782 //      Cairo::RefPtr<Cairo::Context> ctx = Cairo::Context::create(surface);
783 //      Gdk::Cairo::set_source_pixbuf(ctx, bmp, 0.0, 0.0);
784         cairo_t *ctx = cairo_create(ret);
785         cairo_paint(ctx);
786         cairo_destroy(ctx);
787
788         return ret;
789 }
790
791 void container_linux::get_media_features(litehtml::media_features& media) const
792 {
793         litehtml::position client;
794     get_client_rect(client);
795         media.type                      = litehtml::media_type_screen;
796         media.width                     = client.width;
797         media.height            = client.height;
798         media.device_width      = gdk_screen_width();
799         media.device_height     = gdk_screen_height();
800         media.color                     = 8;
801         media.monochrome        = 0;
802         media.color_index       = 256;
803         media.resolution        = 96;
804 }
805
806 void container_linux::get_language(litehtml::tstring& language, litehtml::tstring& culture) const
807 {
808         language = _t("en");
809         culture = _t("");
810 }
811
812 void container_linux::link(const std::shared_ptr<litehtml::document> &ptr, const litehtml::element::ptr& el)
813 {
814
815 }