Back to home page

EIC code displayed by LXR

 
 

    


Warning, /include/Geant4/toolx/sg/text_freetype is written in an unsupported language. File is not indexed.

0001 // Copyright (C) 2010, Guy Barrand. All rights reserved.
0002 // See the file tools.license for terms.
0003 
0004 #ifndef toolx_sg_text_freetype
0005 #define toolx_sg_text_freetype
0006 
0007 
0008 #include <tools/sg/base_freetype>
0009 #include <tools/sg/render_action>
0010 #include <tools/sg/pick_action>
0011 #include <tools/sg/bbox_action>
0012 #include <tools/sg/enums>
0013 #include <tools/lina/vec3d>
0014 #include <tools/file>
0015 #include <tools/nostream>
0016 #include <tools/lina/box_3f>
0017 
0018 // font_pixmap :
0019 #include <tools/sg/group>
0020 #include <tools/sg/blend>
0021 #include <tools/sg/tex_quadrilateral>
0022 
0023 #include <tools/glutess/glutess>
0024 
0025 #include <map> //for errors
0026 
0027 #ifndef SWIG
0028 #include <ft2build.h>
0029 #include FT_FREETYPE_H
0030 #include FT_GLYPH_H
0031 #include FT_OUTLINE_H
0032 #endif
0033 
0034 namespace toolx {
0035 namespace sg {
0036 
0037 class text_freetype : public tools::sg::base_freetype {
0038   TOOLS_NODE(text_freetype,toolx::sg::text_freetype,tools::sg::base_freetype)
0039 protected:
0040   text_freetype& self() const {return const_cast<text_freetype&>(*this);}
0041 protected:
0042   TOOLS_CLASS_STRING(TOOLS_FONT_PATH)
0043 protected:
0044   enum wndg_type {
0045     wndg_ccw,
0046     wndg_cw,
0047     wndg_not_done
0048   };
0049 protected: //gstos
0050   virtual unsigned int create_gsto(std::ostream& a_out,tools::sg::render_manager& a_mgr) {
0051     std::vector<float> gsto_data;
0052 
0053    {tools_vforcit(line_t,m_lines,it) {
0054       const line_t& item = *it;
0055       size_t pos = item.first;
0056       size_t num = item.second;
0057 
0058       if(num<2) {
0059         a_out << "toolx::sg::text_freetype::create_gsto :"
0060               << " strange line with " << num << " points."
0061               << std::endl;
0062         continue; //do we have the case num = 1 ?
0063       }
0064 
0065       const float* data = m_xys.data()+pos;
0066 
0067       // data is a line_strip(), convert it to lines.
0068 
0069       size_t nsegs = num-1;
0070 
0071       size_t ngsto = nsegs*2*3; //3 = have a z (Windows GL).
0072       size_t sz = gsto_data.size();
0073       gsto_data.resize(sz+ngsto);
0074       float* pxyz = gsto_data.data()+sz;
0075 
0076       tools::gl::line_strip_to_lines_2to3(num,data,pxyz);
0077     }}
0078 
0079     m_gsto_lines_sz = gsto_data.size();
0080 
0081    {tools_vforcit(gl_triangle_t,m_triangles,it) {
0082       const std::pair<GLUenum,triangle_t>& item = *it;
0083       size_t pos = item.second.first;
0084       size_t num = item.second.second;
0085 
0086       if(num<3) {
0087         a_out << "toolx::sg::text_freetype::create_gsto :"
0088               << " strange triangle primitive with " << num << " points."
0089               << " Primitive kind is " << (*it).first << "."
0090               << std::endl;
0091         continue;
0092       }
0093 
0094       const float* data = m_xys.data()+pos;
0095 
0096       if((*it).first==tools::gl::triangles()) {
0097 
0098         size_t ntri = num/3;
0099 
0100         size_t ngsto = ntri*3*3;
0101         size_t sz = gsto_data.size();
0102         gsto_data.resize(sz+ngsto);
0103         float* pxyz = gsto_data.data()+sz;
0104 
0105         tools::gl::cvt_2to3(num,data,pxyz);
0106 
0107       } else if((*it).first==tools::gl::triangle_fan()) {
0108 
0109         size_t ntri = num-2;
0110 
0111         size_t ngsto = ntri*3*3;
0112         size_t sz = gsto_data.size();
0113         gsto_data.resize(sz+ngsto);
0114         float* pxyz = gsto_data.data()+sz;
0115 
0116         tools::gl::triangle_fan_to_triangles_2to3(num,data,pxyz);
0117 
0118       } else if((*it).first==tools::gl::triangle_strip()) {
0119 
0120         size_t ntri = num-2;
0121 
0122         size_t ngsto = ntri*3*3;
0123         size_t sz = gsto_data.size();
0124         gsto_data.resize(sz+ngsto);
0125         float* pxyz = gsto_data.data()+sz;
0126 
0127         tools::gl::triangle_strip_to_triangles_2to3(num,data,pxyz);
0128 
0129       } else {
0130         a_out << "toolx::sg::text_freetype::create_gsto :"
0131               << " unknown triangle primitive kind " << (*it).first << "."
0132               << std::endl;
0133       }
0134 
0135     }}
0136 
0137     m_gsto_sz = gsto_data.size();
0138 
0139     if(gsto_data.empty()) {
0140       return 0;
0141     }
0142 
0143     return a_mgr.create_gsto_from_data(gsto_data);
0144   }
0145 
0146 public:
0147   virtual void render(tools::sg::render_action& a_action) {
0148    {bool _color_touched = color_touched(a_action.state());
0149     bool _char_height_touched = char_height_touched(a_action.state());
0150     if(touched()||_color_touched||_char_height_touched) {
0151       update_sg(a_action.out(),font.touched());
0152       reset_touched();
0153     }}
0154 
0155     const tools::sg::state& state = a_action.state();
0156 
0157          if(m_wndg==wndg_ccw) a_action.set_winding(tools::sg::winding_ccw);
0158     else if(m_wndg==wndg_cw) a_action.set_winding(tools::sg::winding_cw);
0159 
0160     if(state.m_use_gsto) {
0161       unsigned int _id = get_gsto_id(a_action.out(),a_action.render_manager());
0162       if(_id) {
0163         a_action.begin_gsto(_id);
0164         size_t sz_tris = m_gsto_sz-m_gsto_lines_sz;
0165         if(m_gsto_lines_sz) {
0166           a_action.set_line_smooth(true);
0167           a_action.draw_gsto_v(tools::gl::lines(),m_gsto_lines_sz/3,0);
0168           a_action.set_polygon_offset(true);
0169         }
0170         if(m_gsto_lines_sz && sz_tris) {
0171           a_action.set_line_smooth(state.m_GL_LINE_SMOOTH);
0172           a_action.set_polygon_offset(state.m_GL_POLYGON_OFFSET_FILL);
0173         }
0174         tools::sg::bufpos pos = m_gsto_lines_sz*sizeof(float);
0175         a_action.draw_gsto_v(tools::gl::triangles(),sz_tris/3,pos);
0176         a_action.end_gsto();
0177 
0178         a_action.set_line_smooth(state.m_GL_LINE_SMOOTH);
0179         a_action.set_polygon_offset(state.m_GL_POLYGON_OFFSET_FILL);
0180         a_action.set_winding(state.m_winding);
0181         return;
0182       } else {
0183         // use immediate rendering.
0184       }
0185     } else {
0186       clean_gstos(&a_action.render_manager());
0187     }
0188 
0189     // immediate rendering :
0190 
0191     ///////////////////////////////////////////////////////////
0192     /// lines /////////////////////////////////////////////////
0193     ///////////////////////////////////////////////////////////
0194 
0195     a_action.set_line_smooth(true);
0196 
0197    {tools_vforcit(line_t,m_lines,it) {
0198       const line_t& item = *it;
0199       size_t pos = item.first;
0200       size_t num = item.second;
0201       if(!num) continue;
0202 
0203       const float* data = m_xys.data()+pos;
0204 
0205       a_action.draw_vertex_array_xy(tools::gl::line_strip(),num*2,data);
0206     }}
0207 
0208     if(m_lines.size()) a_action.set_polygon_offset(true);
0209 
0210     ///////////////////////////////////////////////////////////
0211     /// triangles /////////////////////////////////////////////
0212     ///////////////////////////////////////////////////////////
0213    {tools_vforcit(gl_triangle_t,m_triangles,it) {
0214       const std::pair<GLUenum,triangle_t>& item = *it;
0215       size_t pos = item.second.first;
0216       size_t num = item.second.second;
0217       if(!num) continue;
0218 
0219       const float* data = m_xys.data()+pos;
0220 
0221       a_action.draw_vertex_array_xy((*it).first,num*2,data);
0222     }}
0223 
0224     a_action.set_line_smooth(state.m_GL_LINE_SMOOTH);
0225     a_action.set_polygon_offset(state.m_GL_POLYGON_OFFSET_FILL);
0226     a_action.set_winding(state.m_winding);
0227 
0228     ///////////////////////////////////////////////////////////
0229     /// bitmap ////////////////////////////////////////////////
0230     ///////////////////////////////////////////////////////////
0231     m_bitmaps.render(a_action);
0232   }
0233 
0234   virtual void pick(tools::sg::pick_action& a_action) {
0235    {bool _char_height_touched = char_height_touched(a_action.state());
0236     if(touched()||_char_height_touched) {
0237       update_sg(a_action.out(),font.touched());
0238       reset_touched();
0239     }}
0240 
0241     ///////////////////////////////////////////////////////////
0242     /// lines /////////////////////////////////////////////////
0243     ///////////////////////////////////////////////////////////
0244 
0245    {tools_vforcit(line_t,m_lines,it) {
0246       const line_t& item = *it;
0247       size_t pos = item.first;
0248       size_t num = item.second;
0249       const float* data = m_xys.data()+pos;
0250       if(a_action.add__line_strip_xy(*this,2*num,data,true)) return;
0251     }}
0252 
0253 
0254     ///////////////////////////////////////////////////////////
0255     /// triangles /////////////////////////////////////////////
0256     ///////////////////////////////////////////////////////////
0257    {tools_vforcit(gl_triangle_t,m_triangles,it) {
0258       const std::pair<GLUenum,triangle_t>& item = *it;
0259       size_t pos = item.second.first;
0260       size_t num = item.second.second;
0261       const float* data = m_xys.data()+pos;
0262       if(a_action.add__primitive_xy(*this,item.first,2*num,data,true)) return;
0263     }}
0264 
0265     ///////////////////////////////////////////////////////////
0266     /// bitmap ////////////////////////////////////////////////
0267     ///////////////////////////////////////////////////////////
0268     m_bitmaps.pick(a_action);
0269   }
0270 
0271   virtual void bbox(tools::sg::bbox_action& a_action) {
0272    {bool _char_height_touched = char_height_touched(a_action.state());
0273     if(touched()||_char_height_touched) {
0274       update_sg(a_action.out(),font.touched());
0275       reset_touched();
0276     }}
0277 
0278    {tools_vforcit(line_t,m_lines,it) {
0279       const line_t& item = *it;
0280       size_t num = item.second;
0281       const float* data = m_xys.data()+item.first;
0282 
0283       float px,py,pz;
0284       float* pos = (float*)data;
0285       for(size_t index=0;index<num;index++) {
0286         px = *pos;pos++;
0287         py = *pos;pos++;
0288         pz = 0;
0289         a_action.add_one_point(px,py,pz);
0290       }
0291 
0292     }}
0293 
0294     ///////////////////////////////////////////////////////////
0295     /// triangles /////////////////////////////////////////////
0296     ///////////////////////////////////////////////////////////
0297    {tools_vforcit(gl_triangle_t,m_triangles,it) {
0298       const std::pair<GLUenum,triangle_t>& item = *it;
0299       size_t num = item.second.second;
0300       const float* data = m_xys.data()+item.second.first;
0301 
0302       float px,py,pz;
0303       float* pos = (float*)data;
0304       for(size_t index=0;index<num;index++) {
0305         px = *pos;pos++;
0306         py = *pos;pos++;
0307         pz = 0;
0308         a_action.add_one_point(px,py,pz);
0309       }
0310     }}
0311 
0312     ///////////////////////////////////////////////////////////
0313     /// bitmap ////////////////////////////////////////////////
0314     ///////////////////////////////////////////////////////////
0315     m_bitmaps.bbox(a_action);
0316   }
0317 
0318 public:
0319   text_freetype()
0320   :parent()
0321 
0322   ,m_library(0)
0323   ,m_face(0)
0324   ,m_encoding_offset(0)
0325   ,m_verbose(false)
0326   ,m_scale(1)
0327   ,m_pos(0)
0328   ,m_char_height(0)
0329   {
0330     if(!initialize()){} //throw
0331   }
0332   virtual ~text_freetype(){
0333     if(m_face) ::FT_Done_Face(m_face);
0334     if(m_library) ::FT_Done_FreeType(m_library);
0335     clear_trids();
0336   }
0337 public:
0338   text_freetype(const text_freetype& a_from)
0339   :parent(a_from)
0340 
0341   ,m_library(0)
0342   ,m_face(0)
0343   ,m_encoding_offset(0)
0344   ,m_verbose(a_from.m_verbose)
0345   ,m_scale(1)
0346   ,m_pos(0)
0347   ,m_char_height(0)
0348   {
0349     if(!initialize()){} //throw
0350   }
0351 
0352   text_freetype& operator=(const text_freetype& a_from){
0353     parent::operator=(a_from);
0354     if(&a_from==this) return *this;
0355 
0356     if(m_face) {::FT_Done_Face(m_face);m_face = 0;}
0357     if(m_library) {::FT_Done_FreeType(m_library);m_library = 0;}
0358     clear_trids();
0359 
0360     m_encoding_offset = 0;
0361     m_verbose = a_from.m_verbose;
0362     m_scale = 1;
0363     m_char_height = 0;
0364 
0365     if(!initialize()){} //throw
0366 
0367     return *this;
0368   }
0369 public: //tools::sg::base_text :
0370   virtual float ascent(float a_height) const {
0371     tools::nostream out;
0372     if(!m_face) self().load_face(out);
0373     if(!m_face) return 0;
0374     float value;
0375     if(!ascent(out,self().m_face,a_height,value)) return 0;
0376     return value;
0377   }
0378 
0379   virtual float descent(float a_height) const {
0380     tools::nostream out;
0381     if(!m_face) self().load_face(out);
0382     if(!m_face) return 0;
0383     float value;
0384     if(!descent(out,self().m_face,a_height,value)) return 0;
0385     return value;
0386   }
0387 
0388   virtual float y_advance(float a_height) const {
0389     tools::nostream out;
0390     if(!m_face) self().load_face(out);
0391     if(!m_face) return 0;
0392     float value;
0393     if(!y_advance(out,self().m_face,a_height,value)) return 0;
0394     return value;
0395   }
0396 
0397   virtual void get_bounds(float a_height,
0398                           float& a_mn_x,float& a_mn_y,float& a_mn_z,
0399                           float& a_mx_x,float& a_mx_y,float& a_mx_z) const {
0400     tools::nostream out;
0401     if(!m_face) self().load_face(out);
0402     if(!m_face) return;
0403     if(strings.values().size()) {
0404       if(!get_bounds(out,self().m_face,a_height,strings.values(),a_mn_x,a_mn_y,a_mn_z,a_mx_x,a_mx_y,a_mx_z)) return;
0405     } else if(unitext.values().size()) {
0406       if(!get_bounds(out,self().m_face,a_height,unitext.values(),a_mn_x,a_mn_y,a_mn_z,a_mx_x,a_mx_y,a_mx_z)) return;
0407     }
0408   }
0409 
0410   virtual bool truncate(const std::string& a_string,float a_height,float a_cut_width,std::string& a_out) const {
0411     a_out.clear();
0412     tools::nostream out;
0413     if(!m_face) self().load_face(out);
0414     if(!m_face) return false;
0415     if(!truncate(out,self().m_face,a_height,a_string,a_cut_width,a_out)) return false;
0416     return true;
0417   }
0418 
0419   void dump_unitext(std::ostream& a_out) {
0420     a_out << "unitext size : " << unitext.values().size() << std::endl;
0421     tools_vforcit(uniline,unitext.values(),vit) {
0422       const uniline& line = *vit;
0423       a_out << "beg line :" << std::endl;
0424       tools_vforcit(unichar,line,it) {
0425         a_out << ((unsigned int)*it) << std::endl;
0426       }
0427       a_out << "end line." << std::endl;
0428     }
0429   }
0430 
0431 protected:
0432   bool initialize() { //called from constructors.
0433     FT_Error error = ::FT_Init_FreeType(&m_library);
0434     if(error) {
0435       m_library = 0;
0436       return false;
0437     }
0438     // cast because of const/not const according freetype version.
0439     // (Recent version have "const FT_Vector*" in args).
0440     m_funcs.move_to = (FT_Outline_MoveToFunc)outline_move_to;
0441     m_funcs.line_to = (FT_Outline_LineToFunc)outline_line_to;
0442     m_funcs.conic_to = (FT_Outline_ConicToFunc)outline_conic_to;
0443     m_funcs.cubic_to = (FT_Outline_CubicToFunc)outline_cubic_to;
0444     m_funcs.shift = 0;
0445     m_funcs.delta = 0;
0446 
0447     // Comment from OGLFT :
0448     //   Default number of steps to break TrueType and Type1 arcs into.
0449     //   (Note: this looks good to me, anyway)
0450     m_steps = 4;
0451     m_delta = 1.0f /(float)m_steps;
0452     m_delta2 = m_delta * m_delta;
0453     m_delta3 = m_delta2 * m_delta;
0454 
0455     return true;
0456   }
0457 
0458   ////////////////////////////////////////////////////////
0459   /// outline lines //////////////////////////////////////
0460   ////////////////////////////////////////////////////////
0461   enum update_what {
0462     faces = 0,
0463     lines = 1,
0464     faces_and_lines
0465   };
0466 
0467   bool color_touched(const tools::sg::state& a_state) {
0468     if(modeling!=tools::sg::font_pixmap) return false;
0469     if(a_state.m_color==m_front_color) return false;
0470     m_front_color = a_state.m_color;
0471     return true;
0472   }
0473 
0474   bool char_height_touched(const tools::sg::state& a_state) {
0475     if(modeling!=tools::sg::font_pixmap) return false;
0476 
0477     float ymn,ymx;
0478    {float x,y,z,w;
0479     x = 0;y = -height.value()*0.5f;z = 0;
0480     if(!a_state.project_point(x,y,z,w)) return false;
0481     ymn = y;
0482     x = 0;y = height.value()*0.5f;z = 0;
0483     if(!a_state.project_point(x,y,z,w)) return false;
0484     ymx = y;}
0485     float screen_height = ymx-ymn;
0486     if(a_state.m_wh) screen_height *= a_state.m_wh; else screen_height = 100;
0487 
0488     if(screen_height==m_char_height) return false;
0489     m_char_height = screen_height;
0490     return true;
0491   }
0492 
0493   void update_sg(std::ostream& a_out,bool a_load_font) {
0494     if(a_load_font) load_face(a_out);
0495 
0496     clean_gstos(); //must reset for all render_manager.
0497 
0498     if(!m_face) return;
0499 
0500     m_encoding_offset = 0;
0501     if(!encoding_offset(m_face,m_encoding_offset)) {
0502       a_out << "toolx::sg::text_freetype::update_sg :"
0503             << " encoding_offset failed."
0504             << std::endl;
0505       ::FT_Done_Face(m_face);
0506       m_face = 0;
0507       return;
0508     }
0509 
0510     m_xys.clear();
0511     m_pos = 0;
0512     m_lines.clear();
0513     m_triangles.clear();
0514     m_bitmaps.clear();
0515     m_tqs.clear();
0516 
0517     m_wndg = wndg_not_done;
0518 
0519     if(modeling==tools::sg::font_pixmap) {
0520       if(m_char_height<=0) return;
0521       tools::sg::blend* blend = new tools::sg::blend;
0522       blend->on = true; //to handle background transparency.
0523       m_bitmaps.add(blend);
0524       if(!bitmap_2_gl(a_out)) {m_bitmaps.clear();m_tqs.clear();return;}
0525     } else {
0526       update_what _what = lines;
0527       if(modeling==tools::sg::font_filled) _what = faces;
0528       if((_what==faces)||(_what==faces_and_lines)) outline_triangles_2_gl(a_out);
0529       if((_what==lines)||(_what==faces_and_lines)) outline_lines_2_gl(a_out);
0530     }
0531 
0532     if(vjust==tools::sg::bottom) {
0533     } else if(vjust==tools::sg::middle) {
0534       float mn_x,mn_y,mn_z;
0535       float mx_x,mx_y,mx_z;
0536       get_bounds(height,mn_x,mn_y,mn_z,mx_x,mx_y,mx_z);
0537       float szy = mx_y - mn_y;
0538 
0539      {tools_vforit(line_t,m_lines,it) {
0540         line_t& item = *it;
0541         size_t pos = item.first;
0542         size_t npt = item.second;
0543         float* data = m_xys.data()+pos+1;
0544         for(size_t i=0;i<npt;i++,data+=2) *data -= 0.5F * szy;
0545       }}
0546 
0547      {tools_vforit(gl_triangle_t,m_triangles,it) {
0548         std::pair<GLUenum,triangle_t>& item = *it;
0549         size_t pos = item.second.first;
0550         size_t npt = item.second.second;
0551         float* data = m_xys.data()+pos+1;
0552         for(size_t i=0;i<npt;i++,data+=2) *data -= 0.5F * szy;
0553       }}
0554 
0555      {tools_vforcit(tools::sg::tex_quadrilateral*,m_tqs,itqs) {
0556         std::vector<tools::vec3f>& vcs = (*itqs)->corners.values();
0557         for(size_t i=0;i<4;i++) vcs[i][1] -= 0.5f * szy;
0558       }}
0559 
0560     } else if(vjust==tools::sg::top) {
0561       float mn_x,mn_y,mn_z;
0562       float mx_x,mx_y,mx_z;
0563       get_bounds(height,mn_x,mn_y,mn_z,mx_x,mx_y,mx_z);
0564       float szy = mx_y - mn_y;
0565 
0566      {tools_vforit(line_t,m_lines,it) {
0567         line_t& item = *it;
0568         size_t pos = item.first;
0569         size_t npt = item.second;
0570         float* data = m_xys.data()+pos+1;
0571         for(size_t i=0;i<npt;i++,data+=2) *data -= szy;
0572       }}
0573 
0574      {tools_vforit(gl_triangle_t,m_triangles,it) {
0575         std::pair<GLUenum,triangle_t>& item = *it;
0576         size_t pos = item.second.first;
0577         size_t npt = item.second.second;
0578         float* data = m_xys.data()+pos+1;
0579         for(size_t i=0;i<npt;i++,data+=2) *data -= szy;
0580       }}
0581 
0582      {tools_vforcit(tools::sg::tex_quadrilateral*,m_tqs,itqs) {
0583         std::vector<tools::vec3f>& vcs = (*itqs)->corners.values();
0584         for(size_t i=0;i<4;i++) vcs[i][1] -= szy;
0585       }}
0586 
0587     }
0588 
0589     m_tqs.clear();
0590   }
0591 
0592   void outline_lines_2_gl(std::ostream& a_out) {
0593     if(!set_char_size(a_out,m_face,height.value(),m_scale)) return;
0594     FT_Pos face_height = m_face->size->metrics.height; //FT_Pos (long)
0595 
0596     m_tobj = 0; //IMPORTANT.
0597 
0598     if(strings.values().size()) {
0599       float yline = 0;
0600       tools_vforcit(std::string,strings.values(),vit) {
0601         const std::string& line = *vit;
0602         //a_out << line << std::endl;
0603         m_trans_x = 0;
0604         m_trans_y = yline;
0605         size_t ibeg = m_lines.size(); //for hjust.
0606         for(auto it = line.cbegin(); it != line.cend(); ++it) {
0607           if(!char_outline_2_gl(a_out,*it + m_encoding_offset)) return;
0608         }
0609         yline += -float(face_height)*m_scale;   //height >0
0610 
0611        {float sx = m_trans_x;
0612         if(hjust==tools::sg::center) {
0613           size_t num = m_lines.size();
0614           for(size_t index=ibeg;index<num;index++) {
0615             line_t& item = m_lines[index];
0616             size_t pos = item.first;
0617             size_t npt = item.second;
0618             float* data = m_xys.data()+pos;
0619             for(size_t i=0;i<npt;i++,data+=2) *data -= 0.5F * sx;
0620           }
0621         } else if(hjust==tools::sg::right) {
0622           size_t num = m_lines.size();
0623           for(size_t index=ibeg;index<num;index++) {
0624             line_t& item = m_lines[index];
0625             size_t pos = item.first;
0626             size_t npt = item.second;
0627             float* data = m_xys.data()+pos;
0628             for(size_t i=0;i<npt;i++,data+=2) *data -= sx;
0629           }
0630         }}
0631       }
0632 
0633     } else if(unitext.values().size()) {
0634       float yline = 0;
0635       tools_vforcit(uniline,unitext.values(),vit) {
0636         const uniline& line = *vit;
0637         m_trans_x = 0;
0638         m_trans_y = yline;
0639         size_t ibeg = m_lines.size(); //for hjust.
0640         tools_vforcit(unichar,line,it) {
0641           if(!char_outline_2_gl(a_out,*it)) return;
0642         }
0643         yline += -float(face_height)*m_scale; //height>0
0644 
0645        {float sx = m_trans_x;
0646         if(hjust==tools::sg::center) {
0647           size_t num = m_lines.size();
0648           for(size_t index=ibeg;index<num;index++) {
0649             line_t& item = m_lines[index];
0650             size_t pos = item.first;
0651             size_t npt = item.second;
0652             float* data = m_xys.data()+pos;
0653             for(size_t i=0;i<npt;i++,data+=2) *data -= 0.5F*sx;
0654           }
0655         } else if(hjust==tools::sg::center) {
0656           size_t num = m_lines.size();
0657           for(size_t index=ibeg;index<num;index++) {
0658             line_t& item = m_lines[index];
0659             size_t pos = item.first;
0660             size_t npt = item.second;
0661             float* data = m_xys.data()+pos;
0662             for(size_t i=0;i<npt;i++,data+=2) *data -= sx;
0663           }
0664         }}
0665       }
0666     }
0667   }
0668 
0669   bool char_outline_2_gl(std::ostream& a_out,unsigned int a_unichar) {
0670     FT_ULong charcode = a_unichar; //charcode is UTF-32.
0671     FT_UInt glyph_index = ::FT_Get_Char_Index(m_face,charcode);
0672     //NOTE : if not found -> glyph_index = 0 which is the "missing glyph".
0673     if((FT_Long)glyph_index>=m_face->num_glyphs) {
0674       a_out << "toolx::sg::text_freetype::char_outline_2_gl :"
0675             << " FT_Get_Char_Index : failed for char : " << a_unichar
0676             << std::endl;
0677       ::FT_Done_Face(m_face);
0678       m_face = 0;
0679       return false;
0680     }
0681 
0682    {FT_Error error = ::FT_Load_Glyph(m_face,glyph_index,load_flags());
0683     if(error) {
0684       a_out << "toolx::sg::text_freetype::char_outline_2_gl :"
0685             << " for character " << a_unichar
0686             << ",FT_Load_Glyph : error : " << serror(error)
0687             << std::endl;
0688       ::FT_Done_Face(m_face);
0689       m_face = 0;
0690       return false;
0691     }}
0692 
0693     //FT_GlyphSlot FT_Face.glyph;
0694     if(m_face->glyph->format!=FT_GLYPH_FORMAT_OUTLINE) {
0695       a_out << "toolx::sg::text_freetype::char_outline_2_gl :"
0696             << " for font " << tools::sout(font.value())
0697             << " and for character " << a_unichar
0698             << " glyph not at format outline."
0699             << std::endl;
0700       ::FT_Done_Face(m_face);
0701       m_face = 0;
0702       return false;
0703     }
0704 
0705     FT_Outline outline = m_face->glyph->outline;
0706 
0707    {FT_Error error = ::FT_Outline_Decompose(&outline,&m_funcs,this);
0708     if(error) {
0709       a_out << "toolx::sg::text_freetype::char_outline_2_gl :"
0710             << " for character " << a_unichar
0711             << ",FT_Outline_Decompose : error : " << serror(error)
0712             << std::endl;
0713       ::FT_Done_Face(m_face);
0714       m_face = 0;
0715       return false;
0716     }}
0717 
0718     flush_line();
0719 
0720     m_trans_x += float(m_face->glyph->advance.x)*m_scale;
0721     m_trans_y += float(m_face->glyph->advance.y)*m_scale;
0722 
0723    {wndg_type wdg = ((outline.flags & FT_OUTLINE_REVERSE_FILL)?wndg_ccw:wndg_cw);
0724     if(m_wndg==wndg_not_done) {
0725       m_wndg = wdg;
0726     } else if(m_wndg!=wdg) {
0727       a_out << "toolx::sg::text_freetype::char_outline_2_gl :"
0728             << " for character " << a_unichar << ", winding anomaly."
0729             << std::endl;
0730     }}
0731 
0732     return true;
0733   }
0734 
0735 protected:
0736   void flush_line(){
0737     size_t num = (m_xys.size()-m_pos)/2;
0738     if(num) {
0739       m_lines.push_back(line_t(m_pos,num));
0740     }
0741     m_pos = m_xys.size();
0742   }
0743 
0744   ////////////////////////////////////////////////////////
0745   /// outline triangles //////////////////////////////////
0746   ////////////////////////////////////////////////////////
0747   void outline_triangles_2_gl(std::ostream& a_out) {
0748     if(!set_char_size(a_out,m_face,height.value(),m_scale)) return;
0749     FT_Pos face_height = m_face->size->metrics.height; //FT_Pos (long)
0750 
0751     m_tobj = gluNewTess();
0752 
0753     // NOTE : the gluTessCallback_<enum>() functions are tools/glutess specific.
0754     //        They had been introduced to avoid g++-8.1.0 warnings :
0755     //          warnings : cast between incompatible function types.
0756     ::gluTessCallback_GLU_TESS_BEGIN_DATA   (m_tobj,begin_cbk);
0757     ::gluTessCallback_GLU_TESS_END_DATA     (m_tobj,end_cbk);
0758     ::gluTessCallback_GLU_TESS_VERTEX_DATA  (m_tobj,vertex_cbk);
0759     ::gluTessCallback_GLU_TESS_COMBINE_DATA (m_tobj,combine_cbk);
0760     ::gluTessCallback_GLU_TESS_ERROR_DATA   (m_tobj,error_cbk);
0761 
0762     ::gluTessProperty(m_tobj,(GLUenum)GLU_TESS_WINDING_RULE,GLU_TESS_WINDING_ODD);
0763 
0764     if(strings.values().size()) {
0765       float yline = 0;
0766       tools_vforcit(std::string,strings.values(),vit) {
0767         const std::string& line = *vit;
0768         m_trans_x = 0;
0769         m_trans_y = yline;
0770         size_t ibeg = m_triangles.size(); //for hjust.
0771         for(auto it = line.cbegin(); it != line.cend(); ++it) {
0772           if(!char_triangles_2_gl(a_out,*it + m_encoding_offset)) {
0773             ::gluDeleteTess(m_tobj);
0774             m_tobj = 0;
0775             return;
0776           }
0777         }
0778         yline += -float(face_height)*m_scale; //height>0
0779 
0780        {float sx = m_trans_x;
0781         if(hjust==tools::sg::center) {
0782           size_t num = m_triangles.size();
0783           for(size_t index=ibeg;index<num;index++) {
0784             std::pair<GLUenum,triangle_t>& item = m_triangles[index];
0785             size_t pos = item.second.first;
0786             size_t npt = item.second.second;
0787             float* data = m_xys.data()+pos;
0788             for(size_t i=0;i<npt;i++,data+=2) *data -= sx*0.5f;
0789           }
0790         } else if(hjust==tools::sg::right) {
0791           size_t num = m_triangles.size();
0792           for(size_t index=ibeg;index<num;index++) {
0793             std::pair<GLUenum,triangle_t>& item = m_triangles[index];
0794             size_t pos = item.second.first;
0795             size_t npt = item.second.second;
0796             float* data = m_xys.data()+pos;
0797             for(size_t i=0;i<npt;i++,data+=2) *data -= sx;
0798           }
0799         }}
0800       }
0801     } else if(unitext.values().size()) {
0802       float yline = 0;
0803       tools_vforcit(uniline,unitext.values(),vit) {
0804         const uniline& line = *vit;
0805         m_trans_x = 0;
0806         m_trans_y = yline;
0807         size_t ibeg = m_triangles.size(); //for hjust.
0808         tools_vforcit(unichar,line,it) {
0809           if(!char_triangles_2_gl(a_out,*it)) {
0810             ::gluDeleteTess(m_tobj);
0811             m_tobj = 0;
0812             return;
0813           }
0814         }
0815         yline += -float(face_height)*m_scale; //height>0
0816 
0817        {float sx = m_trans_x;
0818         if(hjust==tools::sg::center) {
0819           size_t num = m_triangles.size();
0820           for(size_t index=ibeg;index<num;index++) {
0821             std::pair<GLUenum,triangle_t>& item = m_triangles[index];
0822             size_t pos = item.second.first;
0823             size_t npt = item.second.second;
0824             float* data = m_xys.data()+pos;
0825             for(size_t i=0;i<npt;i++,data+=2) *data -= sx*0.5f;
0826           }
0827         } else if(hjust==tools::sg::right) {
0828           size_t num = m_triangles.size();
0829           for(size_t index=ibeg;index<num;index++) {
0830             std::pair<GLUenum,triangle_t>& item = m_triangles[index];
0831             size_t pos = item.second.first;
0832             size_t npt = item.second.second;
0833             float* data = m_xys.data()+pos;
0834             for(size_t i=0;i<npt;i++,data+=2) *data -= sx;
0835           }
0836         }}
0837       }
0838     }
0839 
0840     ::gluDeleteTess(m_tobj);
0841     m_tobj = 0;
0842   }
0843 
0844   bool char_triangles_2_gl(std::ostream& a_out,unsigned int a_unichar) {
0845     FT_ULong charcode = a_unichar; //charcode is UTF-32.
0846     FT_UInt glyph_index = ::FT_Get_Char_Index(m_face,charcode);
0847     //NOTE : if not found -> glyph_index = 0 which is the "missing glyph".
0848     if((FT_Long)glyph_index>=m_face->num_glyphs) {
0849       a_out << "toolx::sg::text_freetype::char_triangles_2_gl :"
0850             << " FT_Get_Char_Index : failed for char : " << a_unichar
0851             << std::endl;
0852       ::FT_Done_Face(m_face);
0853       m_face = 0;
0854       return false;
0855     }
0856 
0857    {FT_Error error = ::FT_Load_Glyph(m_face,glyph_index,load_flags());
0858     if(error) {
0859       a_out << "toolx::sg::text_freetype::char_triangles_2_gl :"
0860             << " for character " << a_unichar
0861             << ",FT_Load_Glyph : error : " << serror(error)
0862             << std::endl;
0863       ::FT_Done_Face(m_face);
0864       m_face = 0;
0865       return false;
0866     }}
0867 
0868     //FT_GlyphSlot FT_Face.glyph;
0869     if(m_face->glyph->format!=FT_GLYPH_FORMAT_OUTLINE) {
0870       a_out << "toolx::sg::text_freetype::char_triangles_2_gl :"
0871             << " for font " << tools::sout(font.value())
0872             << " and for character " << a_unichar
0873             << " glyph not at format outline."
0874             << std::endl;
0875       ::FT_Done_Face(m_face);
0876       m_face = 0;
0877       return false;
0878     }
0879 
0880     FT_Outline outline = m_face->glyph->outline;
0881 
0882     m_glutess_trids_num = 0;
0883     m_combine_trids_num = 0;
0884 
0885     m_contour_open = false;
0886 
0887     ::gluTessBeginPolygon(m_tobj,this);
0888 
0889    {FT_Error error = ::FT_Outline_Decompose(&outline,&m_funcs,this);
0890     if(error) {
0891       a_out << "toolx::sg::text_freetype::char_triangles_2_gl :"
0892             << " for character " << a_unichar
0893             << ",FT_Outline_Decompose : error : " << serror(error)
0894             << std::endl;
0895       ::FT_Done_Face(m_face);
0896       m_face = 0;
0897       return false;
0898     }}
0899 
0900     if(m_contour_open) {
0901       ::gluTessEndContour(m_tobj);
0902       m_contour_open = false;
0903     }
0904 
0905     ::gluTessEndPolygon(m_tobj); //triggers callbacks and fill m_triangles.
0906 
0907     m_trans_x += float(m_face->glyph->advance.x)*m_scale;
0908     m_trans_y += float(m_face->glyph->advance.y)*m_scale;
0909 
0910    {wndg_type wdg = ((outline.flags & FT_OUTLINE_REVERSE_FILL)?wndg_ccw:wndg_cw);
0911     if(m_wndg==wndg_not_done) {
0912       m_wndg = wdg;
0913     } else if(m_wndg!=wdg) {
0914       a_out << "toolx::sg::text_freetype::char_triangles_2_gl :"
0915             << " for character " << a_unichar << ", winding anomaly."
0916             << std::endl;
0917     }}
0918 
0919     return true;
0920   }
0921 
0922   ////////////////////////////////////////////////////////
0923   /// bitmap /////////////////////////////////////////////
0924   ////////////////////////////////////////////////////////
0925   bool bitmap_2_gl(std::ostream& a_out) {
0926     FT_F26Dot6 wchar = (FT_F26Dot6)(m_char_height*64);
0927     FT_F26Dot6 hchar = (FT_F26Dot6)(m_char_height*64);
0928     FT_UInt hres = 72;
0929     FT_UInt vres = 72;
0930     FT_Error error = ::FT_Set_Char_Size(m_face,wchar,hchar,hres,vres);
0931     if(error) {
0932       a_out << "toolx::sg::text_freetype::bitmap_2_gl :"
0933             << " FT_Set_Char_Size : error : " << serror(error) << "."
0934             << std::endl;
0935       ::FT_Done_Face(m_face);
0936       m_face = 0;
0937       return false;
0938     }
0939     m_scale = height.value()/float(hchar);
0940 
0941     FT_Pos face_height = m_face->size->metrics.height; //FT_Pos (long)
0942     if(strings.values().size()) {
0943       float yline = 0;
0944       tools_vforcit(std::string,strings.values(),vit) {
0945         const std::string& line = *vit;
0946         m_trans_x = 0;
0947         m_trans_y = yline;
0948         size_t ibeg = m_tqs.size(); //for hjust.
0949         for(auto it = line.cbegin(); it != line.cend(); ++it) {
0950           if(!char_2_bitmap(a_out,*it + m_encoding_offset)) return false;
0951         }
0952         yline += -float(face_height)*m_scale; //height>0
0953 
0954        {float sx = m_trans_x;
0955         if(hjust==tools::sg::center) {
0956           size_t num = m_tqs.size();
0957           for(size_t index=ibeg;index<num;index++) {
0958             std::vector<tools::vec3f>& vcs = m_tqs[index]->corners.values();
0959             for(size_t i=0;i<4;i++) vcs[i][0] -= sx*0.5f;
0960           }
0961         } else if(hjust==tools::sg::right) {
0962           size_t num = m_tqs.size();
0963           for(size_t index=ibeg;index<num;index++) {
0964             std::vector<tools::vec3f>& vcs = m_tqs[index]->corners.values();
0965             for(size_t i=0;i<4;i++) vcs[i][0] -= sx;
0966           }
0967         }}
0968       }
0969     } else if(unitext.values().size()) {
0970       float yline = 0;
0971       tools_vforcit(uniline,unitext.values(),vit) {
0972         const uniline& line = *vit;
0973         m_trans_x = 0;
0974         m_trans_y = yline;
0975         size_t ibeg = m_tqs.size(); //for hjust.
0976         tools_vforcit(unichar,line,it) {
0977           if(!char_2_bitmap(a_out,*it)) return false;
0978         }
0979         yline += -float(face_height)*m_scale; //height>0
0980 
0981        {float sx = m_trans_x;
0982         if(hjust==tools::sg::center) {
0983           size_t num = m_tqs.size();
0984           for(size_t index=ibeg;index<num;index++) {
0985             std::vector<tools::vec3f>& vcs = m_tqs[index]->corners.values();
0986             for(size_t i=0;i<4;i++) vcs[i][0] -= sx*0.5f;
0987           }
0988         } else if(hjust==tools::sg::right) {
0989           size_t num = m_tqs.size();
0990           for(size_t index=ibeg;index<num;index++) {
0991             std::vector<tools::vec3f>& vcs = m_tqs[index]->corners.values();
0992             for(size_t i=0;i<4;i++) vcs[i][0] -= sx;
0993           }
0994         }}
0995       }
0996     }
0997     return true;
0998   }
0999 
1000   bool char_2_bitmap(std::ostream& a_out,unsigned int a_unichar) {
1001     FT_ULong charcode = a_unichar; //charcode is UTF-32.
1002     FT_UInt glyph_index = ::FT_Get_Char_Index(m_face,charcode);
1003     //NOTE : if not found -> glyph_index = 0 which is the "missing glyph".
1004     if((FT_Long)glyph_index>=m_face->num_glyphs) {
1005       a_out << "toolx::sg::text_freetype::char_2_bitmap :"
1006             << " FT_Get_Char_Index : failed for char : " << a_unichar
1007             << std::endl;
1008       ::FT_Done_Face(m_face);
1009       m_face = 0;
1010       return false;
1011     }
1012 
1013    {FT_Error error = ::FT_Load_Glyph(m_face,glyph_index,load_flags());
1014     if(error) {
1015       a_out << "toolx::sg::text_freetype::char_2_bitmap :"
1016             << " for character " << a_unichar
1017             << ",FT_Load_Glyph : error : " << serror(error)
1018             << std::endl;
1019       ::FT_Done_Face(m_face);
1020       m_face = 0;
1021       return false;
1022     }}
1023 
1024     FT_Glyph glyph;
1025    {FT_Error error = ::FT_Get_Glyph(m_face->glyph,&glyph);
1026     if (error) {
1027       a_out << "toolx::sg::text_freetype::char_2_bitmap :"
1028             << " for font " << tools::sout(font.value())
1029             << " and for character " << a_unichar
1030             << " could not get glyph."
1031             << std::endl;
1032       ::FT_Done_Face(m_face);
1033       m_face = 0;
1034       return false;
1035     }}
1036 
1037     bool smoothing = true;
1038    {FT_Error error = ::FT_Glyph_To_Bitmap(&glyph,(smoothing?ft_render_mode_normal:ft_render_mode_mono),0,1);
1039     if (error) {
1040       a_out << "toolx::sg::text_freetype::char_2_bitmap :"
1041             << " for font " << tools::sout(font.value())
1042             << " and for character " << a_unichar
1043             << " could not get glyph bitmap."
1044             << std::endl;
1045       ::FT_Done_Glyph(glyph);
1046       ::FT_Done_Face(m_face);
1047       m_face = 0;
1048       return false;
1049     }}
1050 
1051     FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyph;
1052 
1053     if( (bitmap->bitmap.pixel_mode!=ft_pixel_mode_mono) &&
1054         (bitmap->bitmap.pixel_mode!=ft_pixel_mode_grays) ){
1055       a_out << "toolx::sg::text_freetype::char_2_bitmap :"
1056             << " for font " << tools::sout(font.value())
1057             << " and for character " << a_unichar
1058             << " not a mono or grays pixmap."
1059             << std::endl;
1060       ::FT_Done_Glyph(glyph);
1061       ::FT_Done_Face(m_face);
1062       m_face = 0;
1063       return false;
1064     }
1065 
1066     if(bitmap->bitmap.pitch<0) {
1067       a_out << "toolx::sg::text_freetype::char_2_bitmap :"
1068             << " for font " << tools::sout(font.value())
1069             << " and for character " << a_unichar
1070             << " negative bitmap pitch."
1071             << std::endl;
1072       ::FT_Done_Glyph(glyph);
1073       ::FT_Done_Face(m_face);
1074       m_face = 0;
1075       return false;
1076     }
1077 
1078     unsigned int img_w = 0;
1079     unsigned int img_h = 0;
1080     unsigned int img_bpp = 4;
1081     size_t img_sz = 0;
1082 
1083     if(bitmap->bitmap.pixel_mode==ft_pixel_mode_mono) {
1084       a_out << "toolx::sg::text_freetype::char_2_bitmap : mode_mono : not yet handled." << std::endl;
1085 
1086       img_h = bitmap->bitmap.rows;
1087       //WARNING : bitmap->bitmap.pitch != int((bitmap->bitmap.width+7)/8) !!!
1088       // OpenGL wants the below for cols.
1089       img_w = (bitmap->bitmap.width+7)/8;
1090       img_sz = img_w * img_h;
1091 
1092       ::FT_Done_Glyph(glyph);
1093       ::FT_Done_Face(m_face);
1094       m_face = 0;
1095       return false;
1096 
1097     } else { //ft_pixel_mode_grays
1098       if(int(bitmap->bitmap.width)!=bitmap->bitmap.pitch) {
1099         a_out << "toolx::sg::text_freetype::char_2_bitmap :"
1100               << " for font " << tools::sout(font.value())
1101               << " and for character " << a_unichar
1102               << "bitmap pitch (" << bitmap->bitmap.pitch << ") != width (" << bitmap->bitmap.width << ")."
1103               << std::endl;
1104         ::FT_Done_Glyph(glyph);
1105         ::FT_Done_Face(m_face);
1106         m_face = 0;
1107         return false;
1108       }
1109       img_w = bitmap->bitmap.width;
1110       img_h = bitmap->bitmap.rows;
1111       img_sz = img_w * img_h * img_bpp;
1112     }
1113 
1114     if(img_sz<=0) {
1115       // This may happen (for example for the space character).
1116     } else {
1117       tools::byte* img_buffer = new tools::byte[img_sz];
1118       if(!img_buffer) {
1119         a_out << "toolx::sg::text_freetype::char_2_bitmap :"
1120               << " for font " << tools::sout(font.value())
1121               << " and for character " << a_unichar
1122               << ", can't alloc bitmap buffer for character."
1123               << std::endl;
1124         ::FT_Done_Glyph(glyph);
1125         ::FT_Done_Face(m_face);
1126         m_face = 0;
1127         return false;
1128       }
1129       // The bitmap is upside down for OpenGL.
1130       float a,b;
1131       tools::colorf back_color = tools::colorf_white();
1132       back_color.set_a(0); //transparent background.
1133       typedef unsigned char uchar;
1134       for(unsigned int row=0;row<img_h;++row) {
1135         unsigned char* from = (unsigned char*)bitmap->bitmap.buffer + (bitmap->bitmap.rows-row-1)*bitmap->bitmap.pitch;
1136         unsigned char* to = img_buffer + row * img_w * img_bpp;
1137         for(unsigned int col=0;col<img_w;++col,++from) {
1138           a = float(*from)/255.0f;
1139           b = float(255-*from)/255.0f;
1140           *to = uchar(m_front_color.ruchar()*a+back_color.ruchar()*b);to++;
1141           *to = uchar(m_front_color.guchar()*a+back_color.guchar()*b);to++;
1142           *to = uchar(m_front_color.buchar()*a+back_color.buchar()*b);to++;
1143           *to = uchar(m_front_color.auchar()*a+back_color.auchar()*b);to++;
1144         }
1145       }
1146 
1147       FT_BBox _bbox;
1148       FT_Glyph_Get_CBox(glyph,ft_glyph_bbox_pixels,&_bbox);
1149 
1150       std::vector<tools::vec3f> vcs;
1151       float scale = height.value()/m_char_height;
1152       float x_min = float(_bbox.xMin)*scale+m_trans_x;
1153       float x_max = float(_bbox.xMax)*scale+m_trans_x;
1154       float y_min = float(_bbox.yMin)*scale+m_trans_y;
1155       float y_max = float(_bbox.yMax)*scale+m_trans_y;
1156 
1157       vcs.push_back(tools::vec3f(x_min,y_min,0));
1158       vcs.push_back(tools::vec3f(x_max,y_min,0));
1159       vcs.push_back(tools::vec3f(x_max,y_max,0));
1160       vcs.push_back(tools::vec3f(x_min,y_max,0));
1161 
1162       tools::sg::tex_quadrilateral* _node = new tools::sg::tex_quadrilateral;
1163       _node->img.value().set(img_w,img_h,img_bpp,img_buffer,true);
1164       _node->expand = true;
1165       _node->back_color = tools::colorf_white(); //used as back pixel when expanding.
1166       _node->back_color.value().set_a(0);        //transparent background.
1167       //_node->nearest = false;                    //to have antialiasing on texture.
1168       _node->corners.set_values(vcs);
1169 
1170       m_bitmaps.add(_node);
1171       m_tqs.push_back(_node);
1172     }
1173 
1174     m_trans_x += float(m_face->glyph->advance.x)*m_scale;
1175     m_trans_y += float(m_face->glyph->advance.y)*m_scale;
1176 
1177     ::FT_Done_Glyph(glyph);
1178 
1179     return true;
1180   }
1181 
1182   typedef _GLUfuncptr Func;
1183 
1184   static void GLUAPIENTRY begin_cbk(GLUenum a_which,void* a_this) {
1185     text_freetype& self = *((text_freetype*)a_this);
1186     self.m_mode = a_which;
1187     self.m_pos = self.m_xys.size();
1188   }
1189 
1190   static void GLUAPIENTRY vertex_cbk(void* a_vertex,void* a_this) {
1191     text_freetype& self = *((text_freetype*)a_this);
1192     double* vertex = (double*)a_vertex;
1193     self.add_xy(float(vertex[0]),float(vertex[1]));
1194   }
1195 
1196   static void GLUAPIENTRY end_cbk(void* a_this){
1197     text_freetype& self = *((text_freetype*)a_this);
1198     size_t num = (self.m_xys.size()-self.m_pos)/2;
1199     if(num) {
1200       triangle_t t(self.m_pos,num);
1201       self.m_triangles.push_back(std::pair<GLUenum,triangle_t>(self.m_mode,t));
1202     }
1203   }
1204 
1205   static void GLUAPIENTRY combine_cbk(double a_coords[3],
1206               void* /*a_vertex_data*/[4],
1207               float /*a_weight*/[4],
1208               void** a_data_out,
1209               void* a_this) {
1210     text_freetype& self = *((text_freetype*)a_this);
1211     double* v = self.add_combine_vec3d(a_coords[0],a_coords[1],a_coords[2]);
1212     //if(!v) ???
1213     *a_data_out = v;
1214   }
1215 
1216   static void GLUAPIENTRY error_cbk(GLUenum,void*) {
1217   }
1218 
1219   ////////////////////////////////////////////////////////
1220   /// outline triangles : end ////////////////////////////
1221   ////////////////////////////////////////////////////////
1222 
1223   ////////////////////////////////////////////////////////
1224   /// FT_Outline_Decompose callbacks /////////////////////
1225   ////////////////////////////////////////////////////////
1226   static int outline_move_to(const FT_Vector* a_to,void* a_this){
1227     // NOTE : get x coords in units of wchar,
1228     //        get y coords in units of hchar.
1229     //        Exa : if char_width is 100*64 get some x of
1230     //        the same magnitude (in [0,6400]).
1231     text_freetype& self = *((text_freetype*)a_this);
1232 
1233     float gx,gy;
1234     self.set_g(gx,gy,float(a_to->x),float(a_to->y));
1235 
1236     if(self.m_tobj) {
1237       if(self.m_contour_open) {
1238         ::gluTessEndContour(self.m_tobj);
1239         self.m_contour_open = false;
1240       }
1241 
1242       ::gluTessBeginContour(self.m_tobj);
1243       self.m_contour_open = true;
1244 
1245      {double* v = self.add_glutess_vec3d(gx,gy,0);
1246       ::gluTessVertex(self.m_tobj,v,v);}
1247 
1248     } else {
1249       self.flush_line();
1250       self.add_xy(gx,gy);
1251     }
1252 
1253     self.m_last_x = float(a_to->x);
1254     self.m_last_y = float(a_to->y);
1255 
1256     return 0;
1257   }
1258   static int outline_line_to(const FT_Vector* a_to,void* a_this){
1259     text_freetype& self = *((text_freetype*)a_this);
1260     float gx,gy;
1261     self.set_g(gx,gy,float(a_to->x),float(a_to->y));
1262 
1263     if(self.m_tobj) {
1264       double* v = self.add_glutess_vec3d(gx,gy,0);
1265       ::gluTessVertex(self.m_tobj,v,v);
1266     } else {
1267       self.add_xy(gx,gy);
1268     }
1269 
1270     self.m_last_x = float(a_to->x);
1271     self.m_last_y = float(a_to->y);
1272 
1273     return 0;
1274   }
1275   static int outline_conic_to(const FT_Vector* a_ctrl,const FT_Vector* a_to,void* a_this){
1276     text_freetype& self = *((text_freetype*)a_this);
1277 
1278     // it must be fast. We avoid vec3f manipulations.
1279 
1280     //self.m_out << "toolx::sg::text_freetype::outline_conic_to :"
1281     //           << " ctrl x " << a_ctrl->x
1282     //           << " ctrl y " << a_ctrl->y
1283     //           << " x " << a_to->x
1284     //           << " y " << a_to->y
1285     //           << std::endl;
1286 
1287     float ctrlx = float(a_ctrl->x);
1288     float ctrly = float(a_ctrl->y);
1289 
1290     float fromx = self.m_last_x;
1291     float fromy = self.m_last_y;
1292 
1293     float tox = float(a_to->x);
1294     float toy = float(a_to->y);
1295 
1296     // logic taken from OGLFT.
1297 
1298     float bx = fromx - 2 * ctrlx + tox;
1299     float by = fromy - 2 * ctrly + toy;
1300 
1301     float cx = -2 * fromx + 2 * ctrlx;
1302     float cy = -2 * fromy + 2 * ctrly;
1303 
1304     float dfx = cx * self.m_delta + bx * self.m_delta2;
1305     float dfy = cy * self.m_delta + by * self.m_delta2;
1306 
1307     float df2x = 2 * bx * self.m_delta2;
1308     float df2y = 2 * by * self.m_delta2;
1309 
1310     // if steps = 4, num = 3
1311     //   from (i=0) (i=1) (i=2) (to)
1312     // then we have four steps between [from,to]
1313 
1314     // from = starting point.
1315 
1316     float gx,gy;
1317 
1318     size_t num = self.m_steps - 1;
1319     for(size_t i=0;i<num;i++) {
1320       fromx += dfx;
1321       fromy += dfy;
1322 
1323       self.set_g(gx,gy,fromx,fromy);
1324 
1325       if(self.m_tobj) {
1326         double* v = self.add_glutess_vec3d(gx,gy,0);
1327         ::gluTessVertex(self.m_tobj,v,v);
1328       } else {
1329         self.add_xy(gx,gy);
1330       }
1331 
1332       dfx += df2x;
1333       dfy += df2y;
1334     }
1335 
1336     self.set_g(gx,gy,tox,toy);
1337 
1338     if(self.m_tobj) {
1339       double* v = self.add_glutess_vec3d(gx,gy,0);
1340       ::gluTessVertex(self.m_tobj,v,v);
1341     } else {
1342       self.add_xy(gx,gy);
1343     }
1344 
1345     //self.m_last = to;
1346     self.m_last_x = tox;
1347     self.m_last_y = toy;
1348 
1349     return 0;
1350   }
1351   void set_g(float& a_gx,float& a_gy,float a_x,float a_y) const {
1352     a_gx = a_x*m_scale+m_trans_x;
1353     a_gy = a_y*m_scale+m_trans_y;
1354   }
1355   static int outline_cubic_to(const FT_Vector* a_ctrl1,const FT_Vector* a_ctrl2,const FT_Vector* a_to,void* a_this){
1356     text_freetype& self = *((text_freetype*)a_this);
1357 
1358     // it must be fast. We avoid vec3f manipulations.
1359 
1360     float ctrl1x = float(a_ctrl1->x);
1361     float ctrl1y = float(a_ctrl1->y);
1362 
1363     float ctrl2x = float(a_ctrl2->x);
1364     float ctrl2y = float(a_ctrl2->y);
1365 
1366     float fromx = self.m_last_x;
1367     float fromy = self.m_last_y;
1368 
1369     float tox = float(a_to->x);
1370     float toy = float(a_to->y);
1371 
1372     // logic taken from OGLFT.
1373 
1374     float ax = -fromx + 3 * ctrl1x - 3 * ctrl2x + tox;
1375     float ay = -fromy + 3 * ctrl1y - 3 * ctrl2y + toy;
1376 
1377     float bx = 3 * fromx - 6 * ctrl1x + 3 * ctrl2x;
1378     float by = 3 * fromy - 6 * ctrl1y + 3 * ctrl2y;
1379 
1380     float cx = -3 * fromx + 3 * ctrl1x;
1381     float cy = -3 * fromy + 3 * ctrl1y;
1382 
1383     float dfx = cx * self.m_delta + bx * self.m_delta2 + ax * self.m_delta3;
1384     float dfy = cy * self.m_delta + by * self.m_delta2 + ay * self.m_delta3;
1385 
1386     float df2x = 2 * bx * self.m_delta2 + 6 * ax * self.m_delta3;
1387     float df2y = 2 * by * self.m_delta2 + 6 * ay * self.m_delta3;
1388 
1389     float df3x = 6 * ax * self.m_delta3;
1390     float df3y = 6 * ay * self.m_delta3;
1391 
1392     // if steps = 4, num = 3
1393     //   from (i=0) (i=1) (i=2) (to)
1394     // then we have four steps between [from,to]
1395 
1396     // from = starting point.
1397     float gx,gy;
1398 
1399     size_t num = self.m_steps - 1;
1400     for(size_t i=0;i<num;i++) {
1401       fromx += dfx;
1402       fromy += dfy;
1403 
1404       self.set_g(gx,gy,fromx,fromy);
1405 
1406       if(self.m_tobj) {
1407         double* v = self.add_glutess_vec3d(gx,gy,0);
1408         ::gluTessVertex(self.m_tobj,v,v);
1409       } else {
1410         self.add_xy(gx,gy);
1411       }
1412 
1413       dfx += df2x;
1414       dfy += df2y;
1415 
1416       df2x += df3x;
1417       df2y += df3y;
1418     }
1419 
1420     //g = to;
1421     self.set_g(gx,gy,tox,toy);
1422 
1423     if(self.m_tobj) {
1424       double* v = self.add_glutess_vec3d(gx,gy,0);
1425       ::gluTessVertex(self.m_tobj,v,v);
1426     } else {
1427       self.add_xy(gx,gy);
1428     }
1429 
1430     //self.m_last = to;
1431     self.m_last_x = tox;
1432     self.m_last_y = toy;
1433 
1434     return 0;
1435   }
1436 
1437 
1438 public:
1439 #ifndef SWIG
1440   class serrors : public std::map<int,std::string> {
1441     typedef std::map<int,std::string> parent;
1442   public:
1443     serrors(){
1444 #undef __FTERRORS_H__
1445 #define FT_ERROR_START_LIST
1446 #define FT_ERROR_END_LIST
1447 #define FT_ERRORDEF( e, v, s )  parent::operator[](e) = s;
1448 #include FT_ERRORS_H
1449     }
1450     virtual ~serrors() {}
1451   protected:
1452     serrors(const serrors& a_from):parent(a_from) {}
1453     serrors& operator=(const serrors&){return *this;}
1454   };
1455   static std::string serror(int a_FT_Error) {
1456     static const serrors errs;
1457     std::map<int,std::string>::const_iterator it = errs.find(a_FT_Error);
1458     if(it!=errs.end()) return (*it).second;
1459     return "unknown";
1460   }
1461 #endif
1462 protected:
1463   void add_xy(float a_x,float a_y) {
1464     m_xys.push_back(a_x);
1465     m_xys.push_back(a_y);
1466   }
1467 
1468   double* add_glutess_vec3d(float a_x,float a_y,float a_z) {
1469     double* v = 0;
1470     if(m_glutess_trids_num>=m_glutess_trids.size()) {
1471       v = new double[3];
1472       m_glutess_trids.push_back(v);
1473     } else {
1474       v = m_glutess_trids[m_glutess_trids_num];
1475     }
1476     m_glutess_trids_num++;
1477 
1478     v[0] = a_x;
1479     v[1] = a_y;
1480     v[2] = a_z;
1481 
1482     return v;
1483   }
1484 
1485   double* add_combine_vec3d(double a_x,double a_y,double a_z) {
1486     double* v = 0;
1487     if(m_combine_trids_num>=m_combine_trids.size()) {
1488       v = new double[3];
1489       m_combine_trids.push_back(v);
1490     } else {
1491       v = m_combine_trids[m_combine_trids_num];
1492     }
1493     m_combine_trids_num++;
1494 
1495     v[0] = a_x;
1496     v[1] = a_y;
1497     v[2] = a_z;
1498 
1499     return v;
1500   }
1501 
1502   void clear_trids() {
1503    {tools_vforit(double*,m_glutess_trids,it) delete [] *it;
1504     m_glutess_trids.clear();}
1505 
1506    {tools_vforit(double*,m_combine_trids,it) delete [] *it;
1507     m_combine_trids.clear();}
1508   }
1509 
1510   void load_face(std::ostream& a_out) {
1511     if(!m_library) {
1512       a_out << "toolx::sg::text_freetype::load_face :"
1513             << " freetype library not initialized."
1514             << std::endl;
1515       return;
1516     }
1517 
1518     if(m_verbose) {
1519       a_out << "toolx::sg::text_freetype::load_face :"
1520             << " font is " << tools::sout(font.value()) << "."
1521             << std::endl;
1522     }
1523 
1524     if(m_face) {
1525       ::FT_Done_Face(m_face);
1526       m_face = 0;
1527     }
1528 
1529     if(font.value().empty()) {
1530       a_out << "toolx::sg::text_freetype::load_face :"
1531             << " no font given."
1532             << std::endl;
1533       return;
1534     }
1535 
1536     // look for embedded fonts:
1537    {unsigned int size;
1538     const unsigned char* buffer;
1539     if(parent::find_embedded_font(font.value(),size,buffer)) {
1540       FT_Error error =
1541         ::FT_New_Memory_Face(m_library,(const FT_Byte*)buffer,(FT_Long)size,0,&m_face);
1542       if(error) {
1543         a_out << "toolx::sg::text_freetype::load_face :"
1544               << " FT_New_Memory_Face : error : " << serror(error) << "."
1545               << std::endl;
1546         m_face = 0;
1547         return;
1548       }
1549       if(m_verbose) a_out << "toolx::sg::text_freetype::load_face : load embedded font ok." << std::endl;
1550       return;
1551     }}
1552 
1553     std::string file;
1554 
1555     if(parent::find_font_with_finders(font.value(),file)) {
1556     } else {
1557       tools::file::find_with_env(a_out,s_TOOLS_FONT_PATH(),font.value(),file,false);
1558     }
1559     
1560     if(file.empty()) {
1561       a_out << "toolx::sg::text_freetype::load_face :"
1562             << " font file not found for font "
1563             << tools::sout(font.value()) << "."
1564             << std::endl;
1565       return;
1566     }
1567 
1568     if(m_verbose) {
1569       a_out << "toolx::sg::text_freetype::load_face :"
1570             << " load font file " << tools::sout(file) << " ..."
1571             << std::endl;
1572     }
1573 
1574     FT_Error error = ::FT_New_Face(m_library,file.c_str(),0,&m_face);
1575     if(error) {
1576       a_out << "toolx::sg::text_freetype::load_face :"
1577             << " FT_New_Face : error : " << serror(error) << "."
1578             << " for font file " << tools::sout(file) << "."
1579             << std::endl;
1580       m_face = 0;
1581       return;
1582     }
1583 
1584     if(m_verbose) {
1585       a_out << "toolx::sg::text_freetype::load_face :"
1586             << " load ok."
1587             << std::endl;
1588     }
1589 
1590   }
1591 
1592 protected:
1593   static int load_flags() {
1594     return FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING;
1595   }
1596 
1597   static bool set_char_size(std::ostream& a_out,FT_Face& a_face,float a_height,float& a_scale) {
1598 
1599     // arrange char_width, char_height and m_scale so
1600     // that text height be 1 in world coordinates.
1601 
1602     // What these fancy 26.6, 64, 72 number mean ?
1603     // In what unit do we receive points in callbacks ?
1604     // Knowing char_height and vres can we know height of points ?
1605 
1606     FT_F26Dot6 wchar = 1000*64;
1607     FT_F26Dot6 hchar = 1000*64;
1608     FT_UInt hres = 72;
1609     FT_UInt vres = 72;
1610 
1611     FT_Error error = ::FT_Set_Char_Size(a_face,wchar,hchar,hres,vres);
1612     if(error) {
1613       a_out << "toolx::sg::text_freetype::set_char_size :"
1614             << " FT_Set_Char_Size : error : " << serror(error) << "."
1615             << std::endl;
1616       ::FT_Done_Face(a_face);
1617       a_face = 0;
1618       a_scale = 1;
1619       return false;
1620     }
1621     a_scale = a_height/float(wchar);
1622     return true;
1623   }
1624 
1625   static bool ascent(std::ostream& a_out,FT_Face& a_face,float a_height,float& a_value) {
1626     float scale;
1627     if(!set_char_size(a_out,a_face,a_height,scale)) {a_value = 0;return false;}
1628     FT_Pos ascent = a_face->size->metrics.ascender; //FT_Pos (long)
1629     a_value = float(ascent) * scale;
1630     return true;
1631   }
1632 
1633   static bool descent(std::ostream& a_out,FT_Face& a_face,float a_height,float& a_value) {
1634     float scale;
1635     if(!set_char_size(a_out,a_face,a_height,scale)) {a_value = 0;return false;}
1636     FT_Pos descent = a_face->size->metrics.descender; //FT_Pos (long) //<0
1637     a_value = -(float(descent) * scale);
1638     return true;
1639   }
1640 
1641   static bool y_advance(std::ostream& a_out,FT_Face& a_face,float a_height,float& a_adv) {
1642     float scale;
1643     if(!set_char_size(a_out,a_face,a_height,scale)) {a_adv = 0;return false;}
1644     FT_Pos face_height = a_face->size->metrics.height; //FT_Pos (long)
1645     a_adv = float(face_height)*scale;
1646     return true;
1647   }
1648 
1649   static bool truncate(std::ostream& a_out,FT_Face& a_face,float a_height,
1650                         const std::string& a_string,float a_cut_width,std::string& a_sout) {
1651     a_sout.clear();
1652 
1653     float scale;
1654     if(!set_char_size(a_out,a_face,a_height,scale)) return false;
1655 
1656     float width = 0;
1657 
1658     unsigned short offset;
1659     if(!encoding_offset(a_face,offset)) return false;
1660 
1661     for(auto it = a_string.cbegin(); it != a_string.cend(); ++it) {
1662       FT_ULong charcode = *it + offset; //charcode is UTF-32.
1663       FT_UInt glyph_index = ::FT_Get_Char_Index(a_face,charcode);
1664       //NOTE : if not found -> glyph_index = 0 which is the "missing glyph".
1665       if((FT_Long)glyph_index>=a_face->num_glyphs) {
1666         a_sout.clear();
1667         ::FT_Done_Face(a_face);
1668         a_face = 0;
1669         return false;
1670       }
1671 
1672      {FT_Error error = ::FT_Load_Glyph(a_face,glyph_index,load_flags());
1673       if(error) {
1674         a_sout.clear();
1675         ::FT_Done_Face(a_face);
1676         a_face = 0;
1677         return false;
1678       }}
1679 
1680       float cwidth = float(a_face->glyph->metrics.width)*scale;
1681       float advance = float(a_face->glyph->advance.x)*scale;
1682       if((width+cwidth)>=a_cut_width) return true;
1683       a_sout += *it;
1684       width += advance;
1685     }
1686 
1687     return true;
1688   }
1689 
1690   static bool get_bounds(std::ostream& a_out,FT_Face& a_face,float a_height,
1691                          const std::vector<std::string>& a_text,
1692                          float& a_mn_x,float& a_mn_y,float& a_mn_z,
1693                          float& a_mx_x,float& a_mx_y,float& a_mx_z){
1694     tools::box_3f_make_empty(a_mn_x,a_mn_y,a_mn_z,a_mx_x,a_mx_y,a_mx_z);
1695 
1696     if(a_text.empty()) return true;
1697 
1698     float scale;
1699     if(!set_char_size(a_out,a_face,a_height,scale)) return false;
1700 
1701     float xmx = 0;
1702 
1703     unsigned short offset;
1704     if(!encoding_offset(a_face,offset)) return false;
1705 
1706     tools_vforcit(std::string,a_text,vit) {
1707       const std::string& line = *vit;
1708 
1709       float width = 0;
1710       for(auto it = line.cbegin(); it != line.cend(); ++it) {
1711         FT_ULong charcode = *it + offset; //charcode is UTF-32.
1712         FT_UInt glyph_index = ::FT_Get_Char_Index(a_face,charcode);
1713         //NOTE : if not found -> glyph_index = 0 which is the "missing glyph".
1714         if((FT_Long)glyph_index>=a_face->num_glyphs) {
1715           ::FT_Done_Face(a_face);
1716           a_face = 0;
1717           return false;
1718         }
1719 
1720        {FT_Error error = ::FT_Load_Glyph(a_face,glyph_index,load_flags());
1721         if(error) {
1722           ::FT_Done_Face(a_face);
1723           a_face = 0;
1724           return false;
1725         }}
1726 
1727         float advance = float(a_face->glyph->advance.x)*scale;
1728         width += advance;
1729       }
1730 
1731       xmx = tools::max_of<float>(xmx,width);
1732     }
1733 
1734     FT_Pos ascent = a_face->size->metrics.ascender; //FT_Pos (long)
1735     FT_Pos descent = a_face->size->metrics.descender; //FT_Pos (long) //<0
1736     FT_Pos face_height = a_face->size->metrics.height; //FT_Pos (long)
1737 
1738     float ymn = -float(face_height)*scale*(a_text.size()-1) //height>0
1739                 +float(descent)*scale;
1740 
1741     a_mn_x = 0;
1742     a_mn_y = ymn;
1743     a_mn_z = 0;
1744     a_mx_x = xmx;
1745     a_mx_y = float(ascent)*scale;
1746     a_mx_z = 0;
1747 
1748     return true;
1749   }
1750 
1751   static bool get_bounds(std::ostream& a_out,FT_Face& a_face,float a_height,
1752                          const std::vector<uniline>& a_text,
1753                          float& a_mn_x,float& a_mn_y,float& a_mn_z,
1754                          float& a_mx_x,float& a_mx_y,float& a_mx_z){
1755     tools::box_3f_make_empty(a_mn_x,a_mn_y,a_mn_z,a_mx_x,a_mx_y,a_mx_z);
1756 
1757     if(a_text.empty()) return true;
1758 
1759     float scale;
1760     if(!set_char_size(a_out,a_face,a_height,scale)) return false;
1761 
1762     float xmx = 0;
1763 
1764     tools_vforcit(uniline,a_text,vit) {
1765       const uniline& line = *vit;
1766       float width = 0;
1767 
1768       tools_vforcit(unichar,line,it) {
1769 
1770         FT_ULong charcode = *it; //charcode is UTF-32.
1771         FT_UInt glyph_index = ::FT_Get_Char_Index(a_face,charcode);
1772         //NOTE : if not found -> glyph_index = 0 which is the "missing glyph".
1773         if((FT_Long)glyph_index>=a_face->num_glyphs) {
1774           ::FT_Done_Face(a_face);
1775           a_face = 0;
1776           return false;
1777         }
1778 
1779        {FT_Error error = ::FT_Load_Glyph(a_face,glyph_index,load_flags());
1780         if(error) {
1781           ::FT_Done_Face(a_face);
1782           a_face = 0;
1783           return false;
1784         }}
1785 
1786         float advance = float(a_face->glyph->advance.x)*scale;
1787         width += advance;
1788       }
1789 
1790       xmx = tools::max_of<float>(xmx,width);
1791     }
1792 
1793     FT_Pos ascent = a_face->size->metrics.ascender; //FT_Pos (long)
1794     FT_Pos descent = a_face->size->metrics.descender; //FT_Pos (long) //<0
1795     FT_Pos face_height = a_face->size->metrics.height; //FT_Pos (long)
1796 
1797     float ymn = -float(face_height)*scale*(a_text.size()-1) //height>0
1798                 +float(descent)*scale;
1799 
1800     a_mn_x = 0;
1801     a_mn_y = ymn;
1802     a_mn_z = 0;
1803     a_mx_x = xmx;
1804     a_mx_y = float(ascent)*scale;
1805     a_mx_z = 0;
1806 
1807     return true;
1808   }
1809 
1810   static bool encoding_offset(FT_Face& a_face,unsigned short& a_offset) {
1811 
1812     // arialbd.ttf :
1813     //   num charmap 2
1814     //     charmap 0, platform 1, encoding 0.
1815     //     charmap 1, platform 3, encoding 1.
1816 
1817     // symbol.ttf :
1818     //   num charmap 2
1819     //     charmap 0, platform 1, encoding 0.
1820     //     charmap 1, platform 3, encoding 0.
1821 
1822     // stixgeneral.otf :
1823     //   num charmap 6.
1824     //   charmap 0, platform 0, encoding 3.
1825     //   charmap 1, platform 0, encoding 4.
1826     //   charmap 2, platform 1, encoding 0.
1827     //   charmap 3, platform 3, encoding 1.
1828 
1829     a_offset = 0;
1830 
1831     // cooking to handle symbol.ttf and wingding.ttf :
1832     FT_Int n = a_face->num_charmaps;
1833     FT_Int i;
1834     for ( i = 0; i < n; i++ ) {
1835       FT_CharMap charmap = a_face->charmaps[i];
1836       unsigned short platform = charmap->platform_id;
1837       unsigned short encoding = charmap->encoding_id;
1838 
1839       if ( (platform == 3 && encoding == 1 )  ||
1840            (platform == 3 && encoding == 0 )  ||
1841            (platform == 0 && encoding == 0 ) ) {
1842           FT_Error error = FT_Set_Charmap(a_face,charmap);
1843           if(error) {
1844             ::FT_Done_Face(a_face);
1845             a_face = 0;
1846             a_offset = 0;
1847             return false;
1848           }
1849           // For symbol.ttf and wingding.ttf
1850           if (platform == 3 && encoding == 0 ) a_offset = 0xF000;
1851           return true;
1852 
1853       } else {
1854       }
1855     }
1856 
1857     ::FT_Done_Face(a_face);
1858     a_face = 0;
1859     a_offset = 0;
1860     return false;
1861   }
1862 
1863 protected:
1864   FT_Library m_library;
1865   FT_Face m_face;
1866   unsigned short m_encoding_offset;
1867   bool m_verbose; //append _ to avoid clash with tools/sg/guib::m_verbose
1868   ////////////////////////////////////////////////////////
1869   /// outline ////////////////////////////////////////////
1870   ////////////////////////////////////////////////////////
1871   FT_Outline_Funcs m_funcs; //See doc in ftimage.h
1872   float m_last_x,m_last_y;
1873   float m_scale;
1874   float m_trans_x,m_trans_y;
1875   size_t m_steps;
1876   float m_delta;
1877   float m_delta2;
1878   float m_delta3;
1879 
1880   std::vector<float> m_xys;
1881   ////////////////////////////////////////////////////////
1882   /// outline lines //////////////////////////////////////
1883   ////////////////////////////////////////////////////////
1884   typedef std::pair<size_t,size_t> line_t; //pos in m_xys.
1885   typedef std::vector<line_t> lines_t;
1886   lines_t m_lines;
1887   ////////////////////////////////////////////////////////
1888   /// outline triangles //////////////////////////////////
1889   ////////////////////////////////////////////////////////
1890   GLUtesselator* m_tobj;
1891   bool m_contour_open;
1892   std::vector<double*> m_glutess_trids;
1893   size_t m_glutess_trids_num;
1894   std::vector<double*> m_combine_trids;
1895   size_t m_combine_trids_num;
1896   GLUenum m_mode;
1897   typedef std::pair<size_t,size_t> triangle_t; //pos in m_xys.
1898   typedef std::pair<GLUenum,triangle_t> gl_triangle_t;
1899   typedef std::vector<gl_triangle_t> triangles_t;
1900   triangles_t m_triangles;
1901   size_t m_pos;
1902   size_t m_gsto_lines_sz;
1903   size_t m_gsto_sz;
1904   wndg_type m_wndg;
1905   tools::colorf m_front_color;
1906   tools::sg::group m_bitmaps;
1907   std::vector<tools::sg::tex_quadrilateral*> m_tqs;
1908   float m_char_height;
1909 };
1910 
1911 }}
1912 
1913 
1914 #endif