 (ch[j] == '\0') )
        {
            x = xpos;
            y++;
            if (y >= (int)( this->m_h >> 3))
            {
                break;
            }
            page_offset++;
            if (page_offset == this->m_font->getPages())
            {
                text_index = j;
                page_offset = 0;
                if (ch[j] == '\0')
                {
                    break;
                }
            }
            else
            {
                j = text_index;
            }
            m_intf.endBlock();
            m_intf.startBlock(xpos, y, this->m_w - xpos);
        }
        uint16_t unicode;
        do
        {
            unicode = this->m_font->unicode16FromUtf8(ch[j]);
            j++;
        } while ( unicode == SSD1306_MORE_CHARS_REQUIRED );
        SCharInfo char_info;
        this->m_font->getCharBitmap(unicode, &char_info);
        ldata = 0;
        x += char_info.width + char_info.spacing;
        if (char_info.height > page_offset * 8)
        {
            char_info.glyph += page_offset * char_info.width;
            for( i = char_info.width; i>0; i--)
            {
                uint8_t data;
                if ( style == STYLE_NORMAL )
                {
                    data = char_info.glyph[0];
                }
                else if ( style == STYLE_BOLD )
                {
                    uint8_t temp = char_info.glyph[0];
                    data = temp | ldata;
                    ldata = temp;
                }
                else
                {
                    uint8_t temp = char_info.glyph[1];
                    data = (temp & 0xF0) | ldata;
                    ldata = (temp & 0x0F);
                }
                m_intf.send(data^s_ssd1306_invertByte);
                char_info.glyph++;
            }
        }
        else
        {
            char_info.spacing += char_info.width;
        }
        for (i = 0; i < char_info.spacing; i++)
            m_intf.send(s_ssd1306_invertByte);
    }
    m_intf.endBlock();
}

template<>
void NanoDisplayOpsX<1>::drawBitmap1(int x, int y, unsigned int w, unsigned int h, const uint8_t *bitmap)
{
    unsigned int origin_width = w;
    uint8_t offset = y & 0x07;
    uint8_t complexFlag = 0;
    uint8_t mainFlag = 1;
    uint8_t max_pages;
    uint8_t pages;
    unsigned int i, j;
    if (y + (int)h <= 0) return;
    if (y >= (int)this->m_h) return;
    if (x + (int)w <= 0) return;
    if (x >= (int)this->m_w)  return;
    if (y < 0)
    {
         bitmap += ((unsigned int)((-y) + 7) >> 3) * w;
         h += y;
         y = 0;
         complexFlag = 1;
    }
    if (x < 0)
    {
         bitmap += -x;
         w += x;
         x = 0;
    }
    max_pages = (unsigned int)(h + 15 - offset) >> 3;
    if ((unsigned int)((unsigned int)y + h) > (unsigned int)this->m_h)
    {
         h = (unsigned int)(this->m_h - (unsigned int)y);
    }
    if ((unsigned int)((unsigned int)x + w) > (unsigned int)this->m_w)
    {
         w = (unsigned int)(this->m_w - (unsigned int)x);
    }
    pages = ((y + h - 1) >> 3) - (y >> 3) + 1;

    uint8_t color = this->m_color? 0xFF: 0x00;
    m_intf.startBlock(x, y >> 3, w);
    for(j=0; j < pages; j++)
    {
        if ( j == (unsigned int)(max_pages - 1) ) mainFlag =!offset;
        for( i=w; i > 0; i--)
        {
            uint8_t data = 0;
            if ( mainFlag )    data |= (( bitmap[0] << offset) & color);
            if ( complexFlag ) data |= (( (bitmap - origin_width)[0] >> (8 - offset)) & color);
            bitmap++;
            m_intf.send(s_ssd1306_invertByte^data);
        }
        bitmap += origin_width - w;
        complexFlag = offset;
        m_intf.nextBlock();
    }
    m_intf.endBlock();
}

template<>
uint8_t NanoDisplayOpsX<1>::printChar(uint8_t c)
{
    uint16_t unicode = this->m_font->unicode16FromUtf8(c);
    if (unicode == SSD1306_MORE_CHARS_REQUIRED) return 0;
    SCharInfo char_info;
    this->m_font->getCharBitmap(unicode, &char_info);
    uint8_t mode = this->m_textMode;
    for (uint8_t i = 0; i<(this->m_fontStyle == STYLE_BOLD? 2: 1); i++)
    {
        this->drawBitmap1(this->m_cursorX + i,
                    this->m_cursorY,
                    char_info.width,
                    char_info.height,
                    char_info.glyph );
        this->m_textMode |= CANVAS_MODE_TRANSPARENT;
    }
    this->m_textMode = mode;
    this->m_cursorX += (int)(char_info.width + char_info.spacing);
    if ( ( (this->m_textMode & CANVAS_TEXT_WRAP_LOCAL) &&
           (this->m_cursorX > ((int)this->m_w - (int)this->m_font->getHeader().width) ) )
       || ( (this->m_textMode & CANVAS_TEXT_WRAP) &&
           (this->m_cursorX > ((int)this->m_w - (int)this->m_font->getHeader().width)) ) )
    {
        this->m_cursorY += (int)this->m_font->getHeader().height;
        this->m_cursorX = 0;
        if ( (this->m_textMode & CANVAS_TEXT_WRAP_LOCAL) &&
             (this->m_cursorY > ((int)this->m_h - (int)this->m_font->getHeader().height)) )
        {
            this->m_cursorY = 0;
        }
    }
    return 1;
}


template<>
size_t NanoDisplayOpsX<1>::write(uint8_t c)
{
    if (c == '\n')
    {
        this->m_cursorY += (int)this->m_font->getHeader().height;
        this->m_cursorX = 0;
    }
    else if (c == '\r')
    {
        // skip non-printed char
    }
    else
    {
        return printChar( c );
    }
    return 1;
}


template<>
void NanoDisplayOpsX<1>::printFixed_oldStyle(uint8_t xpos, uint8_t y, const char *ch, EFontStyle style)
{
    uint8_t i, j=0;
    uint8_t text_index = 0;
    uint8_t page_offset = 0;
    uint8_t x = xpos;
    y >>= 3;
    m_intf.startBlock(xpos, y, this->m_w - xpos);
    for(;;)
    {
        uint8_t c;
        uint8_t ldata;
        uint16_t offset;
        if( (x > this->m_w - this->m_font->getHeader().width) || (ch[j] == '\0') )
        {
            x = xpos