Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
  3.  *           2008 Vincent Sanders <vince@simtec.co.uk>
  4.  *
  5.  * This file is part of NetSurf, http://www.netsurf-browser.org/
  6.  *
  7.  * NetSurf 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; version 2 of the License.
  10.  *
  11.  * NetSurf 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.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  */
  19.  
  20. #include <inttypes.h>
  21. #include <assert.h>
  22.  
  23. #include <ft2build.h>
  24. #include FT_CACHE_H
  25.  
  26. #include "css/css.h"
  27. #include "css/utils.h"
  28. #include "render/font.h"
  29. #include "utils/filepath.h"
  30. #include "utils/utf8.h"
  31. #include "utils/log.h"
  32. #include "desktop/options.h"
  33.  
  34. #include "framebuffer/gui.h"
  35. #include "framebuffer/font.h"
  36. #include "framebuffer/findfile.h"
  37. #include "font_freetype.h"
  38.  
  39. /* glyph cache minimum size */
  40. #define CACHE_MIN_SIZE (100 * 1024)
  41.  
  42. #define BOLD_WEIGHT 700
  43.  
  44. static FT_Library library;
  45. static FTC_Manager ft_cmanager;
  46. static FTC_CMapCache ft_cmap_cache ;
  47. static FTC_ImageCache ft_image_cache;
  48.  
  49. int ft_load_type;
  50.  
  51. /* cache manager faceID data to create freetype faceid on demand */
  52. typedef struct fb_faceid_s {
  53.         char *fontfile; /* path to font */
  54.         int index; /* index of font */
  55.         int cidx; /* character map index for unicode */
  56. } fb_faceid_t;
  57.  
  58.  
  59. enum fb_face_e {
  60.         FB_FACE_SANS_SERIF = 0,
  61.         FB_FACE_SANS_SERIF_BOLD = 1,
  62.         FB_FACE_SANS_SERIF_ITALIC = 2,
  63.         FB_FACE_SANS_SERIF_ITALIC_BOLD = 3,
  64.         FB_FACE_SERIF = 4,
  65.         FB_FACE_SERIF_BOLD = 5,
  66.         FB_FACE_MONOSPACE = 6,
  67.         FB_FACE_MONOSPACE_BOLD = 7,
  68.         FB_FACE_CURSIVE = 8,
  69.         FB_FACE_FANTASY = 9,
  70. };
  71.  
  72. /* defines for accesing the faces */
  73. #define FB_FACE_DEFAULT 0
  74. #define FB_FACE_COUNT 10
  75.  
  76. static fb_faceid_t *fb_faces[FB_FACE_COUNT];
  77.  
  78.  
  79. utf8_convert_ret utf8_to_local_encoding(const char *string,
  80.                                        size_t len,
  81.                                        char **result)
  82. {
  83.         return utf8_to_enc(string, "UTF-8", len, result);
  84. }
  85.  
  86. utf8_convert_ret utf8_from_local_encoding(const char *string,
  87.                                         size_t len,
  88.                                         char **result)
  89. {
  90.         *result = malloc(len + 1);
  91.         if (*result == NULL) {
  92.                 return UTF8_CONVERT_NOMEM;
  93.         }
  94.  
  95.         memcpy(*result, string, len);
  96.  
  97.         (*result)[len] = '\0';
  98.  
  99.         return UTF8_CONVERT_OK;
  100. }
  101.  
  102. /* map cache manager handle to face id */
  103. static FT_Error ft_face_requester(FTC_FaceID face_id, FT_Library  library, FT_Pointer request_data, FT_Face *face )
  104. {
  105.         FT_Error error;
  106.         fb_faceid_t *fb_face = (fb_faceid_t *)face_id;
  107.         int cidx;
  108.  
  109.         error = FT_New_Face(library, fb_face->fontfile, fb_face->index, face);
  110.         if (error) {
  111.                 LOG(("Could not find font (code %d)\n", error));
  112.         } else {
  113.  
  114.                 error = FT_Select_Charmap(*face, FT_ENCODING_UNICODE);
  115.                 if (error) {
  116.                         LOG(("Could not select charmap (code %d)\n", error));
  117.                 } else {
  118.                         for (cidx = 0; cidx < (*face)->num_charmaps; cidx++) {
  119.                                 if ((*face)->charmap == (*face)->charmaps[cidx]) {
  120.                                         fb_face->cidx = cidx;
  121.                                         break;
  122.                                 }
  123.                         }
  124.                 }
  125.         }
  126.         LOG(("Loaded face from %s\n", fb_face->fontfile));
  127.  
  128.         return error;
  129. }
  130.  
  131. /* create new framebuffer face and cause it to be loaded to check its ok */
  132. static fb_faceid_t *
  133. fb_new_face(const char *option, const char *resname, const char *fontname)
  134. {
  135.         fb_faceid_t *newf;
  136.         FT_Error error;
  137.         FT_Face aface;
  138.         char buf[PATH_MAX];
  139.  
  140.         newf = calloc(1, sizeof(fb_faceid_t));
  141.  
  142.         if (option != NULL) {
  143.                 newf->fontfile = strdup(option);
  144.         } else {
  145.                 filepath_sfind(respaths, buf, fontname);
  146.                 newf->fontfile = strdup(buf);
  147.         }
  148.  
  149.         error = FTC_Manager_LookupFace(ft_cmanager, (FTC_FaceID)newf, &aface);
  150.         if (error) {
  151.                 LOG(("Could not find font face %s (code %d)\n", fontname, error));
  152.                 free(newf);
  153.                 newf = NULL;
  154.         }
  155.  
  156.         return newf;
  157. }
  158.  
  159. #include <menuet/os.h>
  160.  
  161. /* initialise font handling */
  162. bool fb_font_init(void)
  163. {
  164.         FT_Error error;
  165.         FT_ULong max_cache_size;
  166.         FT_UInt max_faces = 6;
  167.         fb_faceid_t *fb_face;
  168.  
  169. LOG(("Freetype init..."));
  170.  
  171.         nsoptions.fb_font_monochrome = false;                  
  172.         nsoptions.fb_font_cachesize = 2048;                    
  173.         nsoptions.fb_face_sans_serif = NULL;                   
  174.         nsoptions.fb_face_sans_serif_bold = NULL;              
  175.         nsoptions.fb_face_sans_serif_italic = NULL;            
  176.         nsoptions.fb_face_sans_serif_italic_bold = NULL;               
  177.         nsoptions.fb_face_serif = NULL;                        
  178.         nsoptions.fb_face_serif_bold = NULL;                   
  179.         nsoptions.fb_face_monospace = NULL;                    
  180.         nsoptions.fb_face_monospace_bold = NULL;                       
  181.         nsoptions.fb_face_cursive = NULL;                      
  182.         nsoptions.fb_face_fantasy = NULL;              
  183.  
  184.  
  185.         /* freetype library initialise */
  186.         error = FT_Init_FreeType( &library );
  187.         if (error) {
  188.                 LOG(("Freetype could not initialised (code %d)\n", error));
  189.                 return false;
  190.         }
  191.  
  192.         /* set the Glyph cache size up */
  193.         max_cache_size = nsoption_int(fb_font_cachesize) * 1024;
  194.  
  195.         if (max_cache_size < CACHE_MIN_SIZE) {
  196.                 max_cache_size = CACHE_MIN_SIZE;
  197.         }
  198.  
  199. LOG(("Freetype cache..."));
  200. __menuet__debug_out("Ft cache\n");
  201.         /* cache manager initialise */
  202.         error = FTC_Manager_New(library,
  203.                                 max_faces,
  204.                                 0,
  205.                                 max_cache_size,
  206.                                 ft_face_requester,
  207.                                 NULL,
  208.                                 &ft_cmanager);
  209.         if (error) {
  210.                 LOG(("Freetype could not initialise cache manager (code %d)\n", error));
  211.                 FT_Done_FreeType(library);
  212.                 return false;
  213.         }
  214.  
  215.  
  216. LOG(("Freetype map cache..."));
  217. __menuet__debug_out("Ft map cache\n");
  218.         error = FTC_CMapCache_New(ft_cmanager, &ft_cmap_cache);
  219.  
  220.         error = FTC_ImageCache_New(ft_cmanager, &ft_image_cache);
  221.  
  222.         /* need to obtain the generic font faces */
  223.  
  224.  
  225. LOG(("Freetype load fonts..."));
  226. __menuet__debug_out("Ft load fonts\n");
  227.  
  228.         /* Start with the sans serif font */
  229.         fb_face = fb_new_face(nsoption_charp(fb_face_sans_serif),
  230.                               "sans_serif.ttf",
  231.                               NETSURF_FB_FONT_SANS_SERIF);
  232.         if (fb_face == NULL) {
  233.                
  234. LOG(("Freetype load fonts failed due SANS unavailable :(..."));
  235. __menuet__debug_out("Ft Z:(((\n");
  236.                 /* The sans serif font is the default and must be found. */
  237.                 LOG(("Could not find the default font\n"));
  238.                 FTC_Manager_Done(ft_cmanager);
  239.                 FT_Done_FreeType(library);
  240.                 return false;
  241.         } else {
  242.                 fb_faces[FB_FACE_SANS_SERIF] = fb_face;
  243.         }
  244.  
  245. LOG(("Freetype loaded sans.."));
  246. __menuet__debug_out("Ft sans loaded:)\n");
  247.  
  248.         /* Bold sans serif face */
  249.         fb_face = fb_new_face(nsoption_charp(fb_face_sans_serif_bold),
  250.                             "sans_serif_bold.ttf",
  251.                             NETSURF_FB_FONT_SANS_SERIF_BOLD);
  252.         if (fb_face == NULL) {
  253.                 /* seperate bold face unavailabe use the normal weight version */
  254.                 fb_faces[FB_FACE_SANS_SERIF_BOLD] = fb_faces[FB_FACE_SANS_SERIF];
  255.         } else {
  256.                 fb_faces[FB_FACE_SANS_SERIF_BOLD] = fb_face;
  257.         }
  258.  
  259.         /* Italic sans serif face */
  260.         fb_face = fb_new_face(nsoption_charp(fb_face_sans_serif_italic),
  261.                               "sans_serif_italic.ttf",
  262.                               NETSURF_FB_FONT_SANS_SERIF_ITALIC);
  263.         if (fb_face == NULL) {
  264.                 /* seperate italic face unavailabe use the normal weight version */
  265.                 fb_faces[FB_FACE_SANS_SERIF_ITALIC] = fb_faces[FB_FACE_SANS_SERIF];
  266.         } else {
  267.                 fb_faces[FB_FACE_SANS_SERIF_ITALIC] = fb_face;
  268.         }
  269.  
  270.         /* Bold italic sans serif face */
  271.         fb_face = fb_new_face(nsoption_charp(fb_face_sans_serif_italic_bold),
  272.                               "sans_serif_italic_bold.ttf",
  273.                               NETSURF_FB_FONT_SANS_SERIF_ITALIC_BOLD);
  274.         if (fb_face == NULL) {
  275.                 /* seperate italic face unavailabe use the normal weight version */
  276.                 fb_faces[FB_FACE_SANS_SERIF_ITALIC_BOLD] = fb_faces[FB_FACE_SANS_SERIF];
  277.         } else {
  278.                 fb_faces[FB_FACE_SANS_SERIF_ITALIC_BOLD] = fb_face;
  279.         }
  280.  
  281.         /* serif face */
  282.         fb_face = fb_new_face(nsoption_charp(fb_face_serif),
  283.                             "serif.ttf",
  284.                               NETSURF_FB_FONT_SERIF);
  285.         if (fb_face == NULL) {
  286.                 /* serif face unavailabe use the default */
  287.                 fb_faces[FB_FACE_SERIF] = fb_faces[FB_FACE_SANS_SERIF];
  288.         } else {
  289.                 fb_faces[FB_FACE_SERIF] = fb_face;
  290.         }
  291.  
  292.         /* bold serif face*/
  293.         fb_face = fb_new_face(nsoption_charp(fb_face_serif_bold),
  294.                               "serif_bold.ttf",
  295.                               NETSURF_FB_FONT_SERIF_BOLD);
  296.         if (fb_face == NULL) {
  297.                 /* bold serif face unavailabe use the normal weight */
  298.                 fb_faces[FB_FACE_SERIF_BOLD] = fb_faces[FB_FACE_SERIF];
  299.         } else {
  300.                 fb_faces[FB_FACE_SERIF_BOLD] = fb_face;
  301.         }
  302.  
  303.  
  304.         /* monospace face */
  305.         fb_face = fb_new_face(nsoption_charp(fb_face_monospace),
  306.                               "monospace.ttf",
  307.                               NETSURF_FB_FONT_MONOSPACE);
  308.         if (fb_face == NULL) {
  309.                 /* serif face unavailabe use the default */
  310.                 fb_faces[FB_FACE_MONOSPACE] = fb_faces[FB_FACE_SANS_SERIF];
  311.         } else {
  312.                 fb_faces[FB_FACE_MONOSPACE] = fb_face;
  313.         }
  314.  
  315.         /* bold monospace face*/
  316.         fb_face = fb_new_face(nsoption_charp(fb_face_monospace_bold),
  317.                               "monospace_bold.ttf",
  318.                               NETSURF_FB_FONT_MONOSPACE_BOLD);
  319.         if (fb_face == NULL) {
  320.                 /* bold serif face unavailabe use the normal weight */
  321.                 fb_faces[FB_FACE_MONOSPACE_BOLD] = fb_faces[FB_FACE_MONOSPACE];
  322.         } else {
  323.                 fb_faces[FB_FACE_MONOSPACE_BOLD] = fb_face;
  324.         }
  325.  
  326.         /* cursive face */
  327.         fb_face = fb_new_face(nsoption_charp(fb_face_cursive),
  328.                               "cursive.ttf",
  329.                               NETSURF_FB_FONT_CURSIVE);
  330.         if (fb_face == NULL) {
  331.                 /* cursive face unavailabe use the default */
  332.                 fb_faces[FB_FACE_CURSIVE] = fb_faces[FB_FACE_SANS_SERIF];
  333.         } else {
  334.                 fb_faces[FB_FACE_CURSIVE] = fb_face;
  335.         }
  336.  
  337.         /* fantasy face */
  338.         fb_face = fb_new_face(nsoption_charp(fb_face_fantasy),
  339.                               "fantasy.ttf",
  340.                               NETSURF_FB_FONT_FANTASY);
  341.         if (fb_face == NULL) {
  342.                 /* fantasy face unavailabe use the default */
  343.                 fb_faces[FB_FACE_FANTASY] = fb_faces[FB_FACE_SANS_SERIF];
  344.         } else {
  345.                 fb_faces[FB_FACE_FANTASY] = fb_face;
  346.         }
  347.  
  348. LOG(("Freetype fonts ready..."));
  349. __menuet__debug_out("Ft ready :)\n");
  350.        
  351.         /* set the default render mode */
  352.         if (nsoption_bool(fb_font_monochrome) == true)
  353.                 ft_load_type = FT_LOAD_MONOCHROME; /* faster but less pretty */
  354.         else
  355.                 ft_load_type = 0;
  356.        
  357.         return true;
  358. }
  359.  
  360. bool fb_font_finalise(void)
  361. {
  362.         FTC_Manager_Done(ft_cmanager );
  363.         FT_Done_FreeType(library);
  364.         return true;
  365. }
  366.  
  367. static void fb_fill_scalar(const plot_font_style_t *fstyle, FTC_Scaler srec)
  368. {
  369.         int selected_face = FB_FACE_DEFAULT;
  370.  
  371.         switch (fstyle->family) {
  372.                                
  373.         case PLOT_FONT_FAMILY_SERIF:
  374.                 if (fstyle->weight >= BOLD_WEIGHT) {
  375.                         selected_face = FB_FACE_SERIF_BOLD;
  376.                 } else {
  377.                         selected_face = FB_FACE_SERIF;
  378.                 }
  379.                 break;
  380.  
  381.         case PLOT_FONT_FAMILY_MONOSPACE:
  382.                 if (fstyle->weight >= BOLD_WEIGHT) {
  383.                         selected_face = FB_FACE_MONOSPACE_BOLD;
  384.                 } else {
  385.                         selected_face = FB_FACE_MONOSPACE;
  386.                 }
  387.                 break;
  388.  
  389.         case PLOT_FONT_FAMILY_CURSIVE:
  390.                 selected_face = FB_FACE_CURSIVE;
  391.                 break;
  392.  
  393.         case PLOT_FONT_FAMILY_FANTASY:
  394.                 selected_face = FB_FACE_FANTASY;
  395.                 break;
  396.  
  397.         case PLOT_FONT_FAMILY_SANS_SERIF:
  398.         default:
  399.                 if ((fstyle->flags & FONTF_ITALIC) ||
  400.                     (fstyle->flags & FONTF_OBLIQUE)) {
  401.                         if (fstyle->weight >= BOLD_WEIGHT) {
  402.                                 selected_face = FB_FACE_SANS_SERIF_ITALIC_BOLD;
  403.                         } else {
  404.                                 selected_face = FB_FACE_SANS_SERIF_ITALIC;
  405.                         }
  406.                 } else {
  407.                         if (fstyle->weight >= BOLD_WEIGHT) {
  408.                                 selected_face = FB_FACE_SANS_SERIF_BOLD;
  409.                         } else {
  410.                                 selected_face = FB_FACE_SANS_SERIF;
  411.                         }
  412.                 }
  413.         }
  414.  
  415.         srec->face_id = (FTC_FaceID)fb_faces[selected_face];
  416.  
  417.         srec->width = srec->height = (fstyle->size * 64) / FONT_SIZE_SCALE;
  418.         srec->pixel = 0;
  419.  
  420.         srec->x_res = srec->y_res = FIXTOINT(nscss_screen_dpi);
  421. }
  422.  
  423. FT_Glyph fb_getglyph(const plot_font_style_t *fstyle, uint32_t ucs4)
  424. {
  425.         FT_UInt glyph_index;
  426.         FTC_ScalerRec srec;
  427.         FT_Glyph glyph;
  428.         FT_Error error;
  429.         fb_faceid_t *fb_face;
  430.  
  431.         fb_fill_scalar(fstyle, &srec);
  432.  
  433.         fb_face = (fb_faceid_t *)srec.face_id;
  434.  
  435.         glyph_index = FTC_CMapCache_Lookup(ft_cmap_cache, srec.face_id,
  436.                         fb_face->cidx, ucs4);
  437.  
  438.         error = FTC_ImageCache_LookupScaler(ft_image_cache,
  439.                                             &srec,
  440.                                             FT_LOAD_RENDER |
  441.                                             FT_LOAD_FORCE_AUTOHINT |
  442.                                             ft_load_type,
  443.                                             glyph_index,
  444.                                             &glyph,
  445.                                             NULL);
  446.         if (error != 0)
  447.                 return NULL;
  448.  
  449.         return glyph;
  450. }
  451.  
  452.  
  453. /**
  454.  * Measure the width of a string.
  455.  *
  456.  * \param  fstyle  style for this text
  457.  * \param  string  UTF-8 string to measure
  458.  * \param  length  length of string
  459.  * \param  width   updated to width of string[0..length)
  460.  * \return  true on success, false on error and error reported
  461.  */
  462. static bool nsfont_width(const plot_font_style_t *fstyle,
  463.                          const char *string, size_t length,
  464.                          int *width)
  465. {
  466.         uint32_t ucs4;
  467.         size_t nxtchr = 0;
  468.         FT_Glyph glyph;
  469.  
  470.         *width = 0;
  471.         while (nxtchr < length) {
  472.                 ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr);
  473.                 nxtchr = utf8_next(string, length, nxtchr);
  474.  
  475.                 glyph = fb_getglyph(fstyle, ucs4);
  476.                 if (glyph == NULL)
  477.                         continue;
  478.  
  479.                 *width += glyph->advance.x >> 16;
  480.         }
  481.  
  482.         return true;
  483. }
  484.  
  485. /**
  486.  * Find the position in a string where an x coordinate falls.
  487.  *
  488.  * \param  fstyle       style for this text
  489.  * \param  string       UTF-8 string to measure
  490.  * \param  length       length of string
  491.  * \param  x            x coordinate to search for
  492.  * \param  char_offset  updated to offset in string of actual_x, [0..length]
  493.  * \param  actual_x     updated to x coordinate of character closest to x
  494.  * \return  true on success, false on error and error reported
  495.  */
  496.  
  497. static bool nsfont_position_in_string(const plot_font_style_t *fstyle,
  498.                 const char *string, size_t length,
  499.                 int x, size_t *char_offset, int *actual_x)
  500. {
  501.         uint32_t ucs4;
  502.         size_t nxtchr = 0;
  503.         FT_Glyph glyph;
  504.         int prev_x = 0;
  505.  
  506.         *actual_x = 0;
  507.         while (nxtchr < length) {
  508.                 ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr);
  509.  
  510.                 glyph = fb_getglyph(fstyle, ucs4);
  511.                 if (glyph == NULL)
  512.                         continue;
  513.  
  514.                 *actual_x += glyph->advance.x >> 16;
  515.                 if (*actual_x > x)
  516.                         break;
  517.  
  518.                 prev_x = *actual_x;
  519.                 nxtchr = utf8_next(string, length, nxtchr);
  520.         }
  521.  
  522.         /* choose nearest of previous and last x */
  523.         if (abs(*actual_x - x) > abs(prev_x - x))
  524.                 *actual_x = prev_x;
  525.  
  526.         *char_offset = nxtchr;
  527.         return true;
  528. }
  529.  
  530.  
  531. /**
  532.  * Find where to split a string to make it fit a width.
  533.  *
  534.  * \param  fstyle       style for this text
  535.  * \param  string       UTF-8 string to measure
  536.  * \param  length       length of string
  537.  * \param  x            width available
  538.  * \param  char_offset  updated to offset in string of actual_x, [0..length]
  539.  * \param  actual_x     updated to x coordinate of character closest to x
  540.  * \return  true on success, false on error and error reported
  541.  *
  542.  * On exit, [char_offset == 0 ||
  543.  *           string[char_offset] == ' ' ||
  544.  *           char_offset == length]
  545.  */
  546.  
  547. static bool nsfont_split(const plot_font_style_t *fstyle,
  548.                 const char *string, size_t length,
  549.                 int x, size_t *char_offset, int *actual_x)
  550. {
  551.         uint32_t ucs4;
  552.         size_t nxtchr = 0;
  553.         int last_space_x = 0;
  554.         int last_space_idx = 0;
  555.         FT_Glyph glyph;
  556.  
  557.         *actual_x = 0;
  558.         while (nxtchr < length) {
  559.                 ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr);
  560.  
  561.                 glyph = fb_getglyph(fstyle, ucs4);
  562.                 if (glyph == NULL)
  563.                         continue;
  564.  
  565.                 if (ucs4 == 0x20) {
  566.                         last_space_x = *actual_x;
  567.                         last_space_idx = nxtchr;
  568.                 }
  569.  
  570.                 *actual_x += glyph->advance.x >> 16;
  571.                 if (*actual_x > x) {
  572.                         /* string has exceeded available width return previous
  573.                          * space
  574.                          */
  575.                         *actual_x = last_space_x;
  576.                         *char_offset = last_space_idx;
  577.                         return true;
  578.                 }
  579.  
  580.                 nxtchr = utf8_next(string, length, nxtchr);
  581.         }
  582.  
  583.         *char_offset = nxtchr;
  584.  
  585.         return true;
  586. }
  587.  
  588. const struct font_functions nsfont = {
  589.         nsfont_width,
  590.         nsfont_position_in_string,
  591.         nsfont_split
  592. };
  593.  
  594. /*
  595.  * Local Variables:
  596.  * c-basic-offset:8
  597.  * End:
  598.  */
  599.