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