    CImg<T>& _draw_text(const int x0, const int y0,
                        const char *const text,
                        const tc1 *const foreground_color, const tc2 *const background_color,
                        const float opacity, const CImgList<t>& font,
                        const bool is_native_font) {
      if (!text) return *this;
      if (!font)
        throw CImgArgumentException(_cimg_instance
                                    "draw_text(): Empty specified font.",
                                    cimg_instance);

      const unsigned int text_length = (unsigned int)std::strlen(text);
      const int padding_x = font[0]._height<48?1:font[0]._height<128?(int)std::ceil(font[0]._height/51.0f + 0.745f):4;
      unsigned char o_ch, ch = 0;
      int x, y, w;
      CImg<intT> left_paddings(text_length,1,1,1,0);
      const CImg<t> empty = CImg<t>::empty();

      if (is_empty() || is_native_font) {
        // Pre-compute necessary size of the image as well as left paddings of each character.
        x = y = w = 0;
        o_ch = 0;
        for (unsigned int i = 0; i<text_length; ++i) {
          ch = (unsigned char)text[i];
          switch (ch) {
          case '\n' : y+=font[0]._height; if (x>w) w = x; x = 0; break;
          case '\t' : x+=4*font[(int)' ']._width; break;
          case ' ' : x+=font[(int)' ']._width; break;
          default : if (ch<font._width) {
              int left_padding = 0;
              if (is_native_font && font[0]._height<128) {
                // Determine left padding from various rules.
                if (ch==':' || ch=='!' || ch=='.' || ch==';')
                  left_padding = 2*padding_x;
                else if (o_ch==',' || (o_ch=='.' && (ch<'0' || ch>'9')) || o_ch==';' || o_ch==':' || o_ch=='!')
                  left_padding = 4*padding_x;
                else if (((o_ch=='i' || o_ch=='l' || o_ch=='I' || o_ch=='J' || o_ch=='M' || o_ch=='N') &&
                          ((ch>='0' && ch<='9') ||
                           (ch>='a' && ch<='z' && ch!='v' && ch!='x' && ch!='y') ||
                           (ch>='B' && ch<='Z' && ch!='J' && ch!='T' && ch!='V' && ch!='X' && ch!='Y'))) ||
                         o_ch=='.' || o_ch=='\'' || ch=='\'')
                  left_padding = padding_x;
                else if ((o_ch<'0' || o_ch>'9') && ch!='-') {
                  const CImg<t> &mask = ch + 256U<font._width?font[ch + 256]:empty;
                  if (o_ch && ch>' ' && o_ch>' ' && mask._height>13) {
                    const CImg<t> &o_mask = o_ch + 256U<font._width?font[o_ch + 256]:empty;
                    if (o_mask._height>13) {
                      const int w1 = mask.width()>0?o_mask.width() - 1:0, w2 = w1>1?w1 - 1:0, w3 = w2>1?w2 - 1:0;
                      left_padding = -10;
                      cimg_forY(mask,k) {
                        const int
                          lpad = o_mask(w1,k)>=8?0:
                                 o_mask._width<=2 || o_mask(w2,k)>=8?-1:
                                 o_mask._width<=3 || o_mask(w3,k)>=8?-2:-3,
                          rpad = mask(0,k)>=8?0:
                                 mask._width<=2 || mask(1,k)>=8?-1:
                                 mask._width<=3 || mask(2,k)>=8?-2:-3;
                        left_padding = std::max(left_padding,lpad + rpad);
                      }
                    }
                  }
                }
                left_paddings[i] = left_padding;
              }
              x+=left_padding + font[ch]._width + padding_x;
              o_ch = ch;
            }
          }
        }
        if (x!=0 || ch=='\n') { if (x>w) w = x; y+=font[0]._height; }
        if (is_empty()) assign(x0 + w,y0 + y,1,is_native_font?1:font[0]._spectrum,(T)0);
      }

      // Draw font characters on image.
      x = x0; y = y0;
      for (unsigned int i = 0; i<text_length; ++i) {
        ch = (unsigned char)text[i];
        switch (ch) {
        case '\n' : y+=font[0]._height; x = x0; break;
        case '\t' :
        case ' ' : {
          const unsigned int lw = (ch=='\t'?4:1)*font[(int)' ']._width, lh = font[(int)' ']._height;
          if (background_color) draw_rectangle(x,y,x + lw - 1,y + lh - 1,background_color,opacity);
          x+=lw;
        } break;
        default : if (ch<font._width) {
            CImg<T> letter = font[ch];
            if (letter) {
              const CImg<t> &mask = ch + 256U<font._width?font[ch + 256]:empty;
              const int posx = x + left_paddings[i] + padding_x;
              if (is_native_font && _spectrum>letter._spectrum)
                letter.assign(letter.get_resize(-100,-100,1,_spectrum,0,2),false);
              const unsigned int cmin = std::min(_spectrum,letter._spectrum);
              if (foreground_color)
                for (unsigned int c = 0; c<cmin; ++c)
                  if (foreground_color[c]!=1) letter.get_shared_channel(c)*=foreground_color[c];
              if (mask) { // Letter has mask
                if (background_color)
                  for (unsigned int c = 0; c<cmin; ++c)
                    draw_rectangle(x,y,0,c,posx + letter._width - 1,y + letter._height - 1,0,c,
                                   background_color[c],opacity);
                draw_image(posx,y,letter,font[ch + 256],opacity,255.f);
              } else draw_image(posx,y,letter,opacity); // Letter has no mask
              x = posx + letter._width;
            }
          }
        }
      }
      return *this;
    }
    CImg<T>& _draw_text(const int x0, const int y0,
                        const char *const text,
                        const tc1 *const foreground_color, const tc2 *const background_color,
                        const float opacity, const CImgList<t>& font,
                        const bool is_native_font) {
      if (!text) return *this;
      if (!font)
        throw CImgArgumentException(_cimg_instance
                                    "draw_text(): Empty specified font.",
                                    cimg_instance);

      const unsigned int text_length = (unsigned int)std::strlen(text);
      const int padding_x = font[0]._height<48?1:font[0]._height<128?(int)std::ceil(font[0]._height/51.0f + 0.745f):4;
      unsigned char o_ch, ch = 0;
      int x, y, w;
      CImg<intT> left_paddings(text_length,1,1,1,0);
      const CImg<t> empty = CImg<t>::empty();

      if (is_empty() || is_native_font) {
         
        x = y = w = 0;
        o_ch = 0;
        for (unsigned int i = 0; i<text_length; ++i) {
          ch = (unsigned char)text[i];
          switch (ch) {
          case '\n' : y+=font[0]._height; if (x>w) w = x; x = 0; break;
          case '\t' : x+=4*font[(int)' ']._width; break;
          case ' ' : x+=font[(int)' ']._width; break;
          default : if (ch<font._width) {
              int left_padding = 0;
              if (is_native_font && font[0]._height<128) {
                 
                if (ch==':' || ch=='!' || ch=='.' || ch==';')
                  left_padding = 2*padding_x;
                else if (o_ch==',' || (o_ch=='.' && (ch<'0' || ch>'9')) || o_ch==';' || o_ch==':' || o_ch=='!')
                  left_padding = 4*padding_x;
                else if (((o_ch=='i' || o_ch=='l' || o_ch=='I' || o_ch=='J' || o_ch=='M' || o_ch=='N') &&
                          ((ch>='0' && ch<='9') ||
                           (ch>='a' && ch<='z' && ch!='v' && ch!='x' && ch!='y') ||
                           (ch>='B' && ch<='Z' && ch!='J' && ch!='T' && ch!='V' && ch!='X' && ch!='Y'))) ||
                         o_ch=='.' || o_ch=='\'' || ch=='\'')
                  left_padding = padding_x;
                else if ((o_ch<'0' || o_ch>'9') && ch!='-') {
                  const CImg<t> &mask = ch + 256U<font._width?font[ch + 256]:empty;
                  if (o_ch && ch>' ' && o_ch>' ' && mask._height>13) {
                    const CImg<t> &o_mask = o_ch + 256U<font._width?font[o_ch + 256]:empty;
                    if (o_mask._height>13) {
                      const int w1 = mask.width()>0?o_mask.width() - 1:0, w2 = w1>1?w1 - 1:0, w3 = w2>1?w2 - 1:0;
                      left_padding = -10;
                      cimg_forY(mask,k) {
                        const int
                          lpad = o_mask(w1,k)>=8?0:
                                 o_mask._width<=2 || o_mask(w2,k)>=8?-1:
                                 o_mask._width<=3 || o_mask(w3,k)>=8?-2:-3,
                          rpad = mask(0,k)>=8?0:
                                 mask._width<=2 || mask(1,k)>=8?-1:
                                 mask._width<=3 || mask(2,k)>=8?-2:-3;
                        left_padding = std::max(left_padding,lpad + rpad);
                      }
                    }
                  }
                }
                left_paddings[i] = left_padding;
              }
              x+=left_padding + font[ch]._width + padding_x;
              o_ch = ch;
            }
          }
        }
        if (x!=0 || ch=='\n') { if (x>w) w = x; y+=font[0]._height; }
        if (is_empty()) assign(x0 + w,y0 + y,1,is_native_font?1:font[0]._spectrum,(T)0);
      }

       
      x = x0; y = y0;
      for (unsigned int i = 0; i<text_length; ++i) {
        ch = (unsigned char)text[i];
        switch (ch) {
        case '\n' : y+=font[0]._height; x = x0; break;
        case '\t' :
        case ' ' : {
          const unsigned int lw = (ch=='\t'?4:1)*font[(int)' ']._width, lh = font[(int)' ']._height;
          if (background_color) draw_rectangle(x,y,x + lw - 1,y + lh - 1,background_color,opacity);
          x+=lw;
        } break;
        default : if (ch<font._width) {
            CImg<T> letter = font[ch];
            if (letter) {
              const CImg<t> &mask = ch + 256U<font._width?font[ch + 256]:empty;
              const int posx = x + left_paddings[i] + padding_x;
              if (is_native_font && _spectrum>letter._spectrum)
                letter.assign(letter.get_resize(-100,-100,1,_spectrum,0,2),false);
              const unsigned int cmin = std::min(_spectrum,letter._spectrum);
              if (foreground_color)
                for (unsigned int c = 0; c<cmin; ++c)
                  if (foreground_color[c]!=1) letter.get_shared_channel(c)*=foreground_color[c];
              if (mask) {  
                if (background_color)
                  for (unsigned int c = 0; c<cmin; ++c)
                    draw_rectangle(x,y,0,c,posx + letter._width - 1,y + letter._height - 1,0,c,
                                   background_color[c],opacity);
                draw_image(posx,y,letter,font[ch + 256],opacity,255.f);
              } else draw_image(posx,y,letter,opacity);  
              x = posx + letter._width;
            }
          }
        }
      }
      return *this;
    }