Subversion Repositories Kolibri OS

Rev

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