rework intl strategy, titler upgrades, fonts fixes, igor ru.po
[goodguy/history.git] / cinelerra-5.1 / plugins / titler / titler.C
index ba9a5fce5d7c35ea89213561ee9591d691be8611..dde50aa37bc9a0b7c98f572d31891a831d69059d 100644 (file)
@@ -46,6 +46,7 @@
 #include "language.h"
 #include "mwindow.inc"
 #include "overlayframe.h"
+#include "plugin.h"
 #include "renderengine.h"
 #include "titler.h"
 #include "titlerwindow.h"
 #include <endian.h>
 #include <byteswap.h>
 #include <iconv.h>
-#include <wctype.h>
 #include <sys/stat.h>
 #include <fontconfig/fontconfig.h>
 
+#define FIXED_FONT "bitstream vera sans mono (bitstream)"
 #define SMALL (1.0 / 64.0)
 
 #define MAX_FLT  3.40282347e+38
@@ -80,38 +81,36 @@ REGISTER_PLUGIN(TitleMain)
 
 TitleConfig::TitleConfig()
 {
+       strcpy(font, "fixed");
+       strcpy(encoding, DEFAULT_ENCODING);
        style = 0;
+       size = 24;
        color = BLACK;
        alpha = 0xff;
+       outline_size = 0.;
+       outline_color = WHITE;
        outline_alpha = 0xff;
-       size = 24;
+       color_stroke = 0xff0000;
+       stroke_width = 1.0;
        motion_strategy = NO_MOTION;
-       loop = 0;
        line_pitch = 0;
+       loop = 0;
        hjustification = JUSTIFY_CENTER;
        vjustification = JUSTIFY_MID;
-       fade_in = 0.0;
-       fade_out = 0.0;
+       fade_in = 0.0;  fade_out = 0.0;
+       pixels_per_second = 100.0;
+       wtext[0] = 0;  wlen = 0;
        title_x = title_y = 0.0;
        title_w = title_h = 0;
-       dropshadow = 2;
-       strcpy(font, "fixed");
-       strcpy(encoding, DEFAULT_ENCODING);
-       timecode_format = DEFAULT_TIMECODEFORMAT;
-       pixels_per_second = 1.0;
-       timecode = 0;
-       stroke_width = 1.0;
-       wtext[0] = 0;  wlen = 0;
-       color_stroke = 0xff0000;
-       outline_color = WHITE;
-       background = 0;
-       strcpy(background_path, "");
-
-       outline_size = 0;
-       window_w = 800;
+       window_w = 860;
        window_h = 460;
        next_keyframe_position = 0;
        prev_keyframe_position = 0;
+       timecode = 0;
+       dropshadow = 2;
+       background = 0;
+       strcpy(background_path, "");
+       timecode_format = DEFAULT_TIMECODEFORMAT;
        drag = 0;
        loop_playback = 0;
 }
@@ -123,102 +122,118 @@ TitleConfig::~TitleConfig()
 int TitleConfig::equivalent(TitleConfig &that)
 {
        return !strcasecmp(font, that.font) &&
+               !strcasecmp(encoding, that.encoding) &&
                style == that.style &&
                size == that.size &&
                color == that.color &&
-               color_stroke == that.color_stroke &&
-               stroke_width == that.stroke_width &&
-               outline_color == that.outline_color &&
                alpha == that.alpha &&
+               outline_size == that.outline_size &&
+               outline_color == that.outline_color &&
                outline_alpha == that.outline_alpha &&
-               EQUIV(pixels_per_second, that.pixels_per_second) &&
-               motion_strategy == that.motion_strategy &&
-               loop == that.loop &&
+               color_stroke == that.color_stroke &&
+               stroke_width == that.stroke_width &&
+// dont require redraw for these
+//             motion_strategy == that.motion_strategy &&
                line_pitch == that.line_pitch &&
+//             loop == that.loop &&
                hjustification == that.hjustification &&
                vjustification == that.vjustification &&
-               fade_in == that.fade_in && fade_out == that.fade_out &&
-               title_x == that.title_x && title_y == that.title_y &&
+//             fade_in == that.fade_in && fade_out == that.fade_out &&
+//             EQUIV(pixels_per_second, that.pixels_per_second) &&
+               wlen == that.wlen &&
+               !memcmp(wtext, that.wtext, wlen * sizeof(wchar_t)) &&
+//             title_x == that.title_x && title_y == that.title_y &&
                title_w == that.title_w && title_h == that.title_h &&
-               dropshadow == that.dropshadow &&
+//             window_w == that.window_w && window_h == that.window_h &&
                timecode == that.timecode &&
+               dropshadow == that.dropshadow &&
+               background == that.background &&
+               !strcmp(background_path, that.background_path) &&
                timecode_format == that.timecode_format &&
-               outline_size == that.outline_size &&
-               !strcasecmp(encoding, that.encoding) &&
-               wlen == that.wlen &&
-               !memcmp(wtext, that.wtext, wlen * sizeof(wchar_t));
+//             drag == that.drag &&
+               loop_playback == that.loop_playback;
 }
 
 void TitleConfig::copy_from(TitleConfig &that)
 {
        strcpy(font, that.font);
+       strcpy(encoding, that.encoding);
        style = that.style;
        size = that.size;
        color = that.color;
-       color_stroke = that.color_stroke;
-       stroke_width = that.stroke_width;
-       outline_color = that.outline_color;
        alpha = that.alpha;
+       outline_size = that.outline_size;
+       outline_color = that.outline_color;
        outline_alpha = that.outline_alpha;
-       pixels_per_second = that.pixels_per_second;
+       color_stroke = that.color_stroke;
+       stroke_width = that.stroke_width;
        motion_strategy = that.motion_strategy;
-       loop = that.loop;
        line_pitch = that.line_pitch;
+       loop = that.loop;
        hjustification = that.hjustification;
        vjustification = that.vjustification;
        fade_in = that.fade_in;
        fade_out = that.fade_out;
-       title_x = that.title_x;
-       title_y = that.title_y;
-       title_w = that.title_w;
-       title_h = that.title_h;
-       dropshadow = that.dropshadow;
+       pixels_per_second = that.pixels_per_second;
+       wlen = that.wlen;
+       memcpy(wtext, that.wtext, that.wlen * sizeof(wchar_t));
+       title_x = that.title_x;  title_y = that.title_y;
+       title_w = that.title_w;  title_h = that.title_h;
+       window_w = that.window_w;  window_h = that.window_h;
        timecode = that.timecode;
+       dropshadow = that.dropshadow;
+       background = that.background;
+       strcpy(background_path, that.background_path);
        timecode_format = that.timecode_format;
-       outline_size = that.outline_size;
-       strcpy(encoding, that.encoding);
-       memcpy(wtext, that.wtext, that.wlen * sizeof(wchar_t));
-       wlen = that.wlen;
-       window_w = that.window_w;
-       window_h = that.window_h;
+       drag = that.drag;
+       loop_playback = that.loop_playback;
 }
 
 void TitleConfig::interpolate(TitleConfig &prev, TitleConfig &next,
        int64_t prev_frame, int64_t next_frame, int64_t current_frame)
 {
+       double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
+       double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
        strcpy(font, prev.font);
        strcpy(encoding, prev.encoding);
        style = prev.style;
        size = prev.size;
        color = prev.color;
-       color_stroke = prev.color_stroke;
-       stroke_width = prev.stroke_width;
-       outline_color = prev.outline_color;
        alpha = prev.alpha;
+       outline_size = prev.outline_size;
+       outline_color = prev.outline_color;
        outline_alpha = prev.outline_alpha;
+       color_stroke = prev.color_stroke;
+       stroke_width = prev.stroke_width;
        motion_strategy = prev.motion_strategy;
-       loop = prev.loop;
        line_pitch = prev.line_pitch;
+       loop = prev.loop;
        hjustification = prev.hjustification;
        vjustification = prev.vjustification;
        fade_in = prev.fade_in;
        fade_out = prev.fade_out;
-       outline_size = prev.outline_size;
        pixels_per_second = prev.pixels_per_second;
-       memcpy(wtext, prev.wtext, prev.wlen * sizeof(wchar_t));
        wlen = prev.wlen;
+       memcpy(wtext, prev.wtext, prev.wlen * sizeof(wchar_t));
        wtext[wlen] = 0;
-
-       double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
-       double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
-       this->title_x = prev.title_x * prev_scale + next.title_x * next_scale;
-       this->title_y = prev.title_y * prev_scale + next.title_y * next_scale;
-//     this->title_x = prev.title_x;
-//     this->title_y = prev.title_y;
+       this->title_x = prev.title_x == next.title_x ? prev.title_x :
+               prev.title_x * prev_scale + next.title_x * next_scale;
+       this->title_y = prev.title_y == next.title_y ? prev.title_y :
+               prev.title_y * prev_scale + next.title_y * next_scale;
+       this->title_w = prev.title_w == next.title_w ? prev.title_w :
+               prev.title_w * prev_scale + next.title_w * next_scale;
+       this->title_h = prev.title_h == next.title_h ? prev.title_h :
+               prev.title_h * prev_scale + next.title_h * next_scale;
+       window_w = prev.window_w;
+       window_h = prev.window_h;
        timecode = prev.timecode;
+       this->dropshadow = prev.dropshadow == next.dropshadow ? prev.dropshadow :
+               prev.dropshadow * prev_scale + next.dropshadow * next_scale;
+       background = prev.background;
+       strcpy(background_path, prev.background_path);
        timecode_format = prev.timecode_format;
-       this->dropshadow = prev.dropshadow * prev_scale + next.dropshadow * next_scale;
-//     this->dropshadow = prev.dropshadow;
+       drag = prev.drag;
+       loop_playback = prev.loop_playback;
 }
 
 void TitleConfig::to_wtext(const char *from_enc, const char *text, int tlen)
@@ -286,13 +301,16 @@ void GlyphUnit::process_package(LoadPackage *package)
                        glyph->font->path);
                result = 1;
        }
-
        if( !result ) {
                int gindex = FT_Get_Char_Index(freetype_face, glyph->char_code);
-
-//printf("GlyphUnit::process_package 1 %c\n", glyph->char_code);
-// Char not found
+               if( !gindex && !freetype_face->charmap && // if no default charmap
+                   freetype_face->charmaps && freetype_face->charmaps[0] &&
+                   !FT_Select_Charmap(freetype_face, freetype_face->charmaps[0]->encoding) ) {
+                       gindex = FT_Get_Char_Index(freetype_face, glyph->char_code);
+               }
                if( gindex == 0 ) {
+printf("GlyphUnit::process_package 1 glyph not found (%s) %04x, '%c'\n",
+ glyph->font->displayname, (unsigned)glyph->char_code, (unsigned)glyph->char_code);
                        BC_Resources *resources =  BC_WindowBase::get_resources();
                        // Search replacement font
                        if( resources->find_font_by_char(glyph->char_code, new_path, freetype_face) ) {
@@ -1147,6 +1165,7 @@ void TitleMain::build_previews(TitleWindow *gui)
        const char *test_string = "Aa";
        char new_path[BCTEXTLEN];
        int text_height = gui->get_text_height(LARGEFONT);
+       int max_height = 3*text_height/2, max_width = 2 * max_height;
        int text_color = BC_WindowBase::get_resources()->default_text_color;
        int r = (text_color >> 16) & 0xff;
        int g = (text_color >> 8) & 0xff;
@@ -1177,9 +1196,7 @@ void TitleMain::build_previews(TitleWindow *gui)
                        }
 
                        if( skip ) continue;
-
-                       int current_x = 0, current_w = 0, current_h = 0, current_ascent = 0;
-                       if( pass == 1 ) {
+                       if( pass > 0 ) {
                                font->image = new VFrame;
                                font->image->set_use_shm(0);
                                font->image->reallocate(0, -1, 0, 0, 0,
@@ -1187,7 +1204,10 @@ void TitleMain::build_previews(TitleWindow *gui)
                                font->image->clear_frame();
                        }
 
+                       int current_w = 1, current_h = 1;
+                       int current_x = 0, current_ascent = 0;
                        int len = strlen(test_string);
+
                        for( int j=0; j<len; ++j ) {
                                FT_ULong c = test_string[j];
 // memory leaks here are fatal
@@ -1199,30 +1219,31 @@ void TitleMain::build_previews(TitleWindow *gui)
                                if( load_freetype_face(freetype_library, freetype_face, new_path)) continue;
                                FT_Set_Pixel_Sizes(freetype_face, text_height, 0);
                                if( FT_Load_Char(freetype_face, c, FT_LOAD_RENDER) ) continue;
+                               int glyph_w = freetype_face->glyph->bitmap.width;
+                               int glyph_h = freetype_face->glyph->bitmap.rows;
+                               if( glyph_h > max_height ) glyph_h = max_height;
+                               int glyph_a = freetype_face->glyph->advance.x >> 6;
+                               int glyph_t = freetype_face->glyph->bitmap_top;
                                if( pass == 0 ) {
-                                       current_w = current_x + freetype_face->glyph->bitmap.width;
-                                       if( (int)freetype_face->glyph->bitmap_top > current_ascent )
-                                       current_ascent = freetype_face->glyph->bitmap_top;
-                                       if( (int)freetype_face->glyph->bitmap.rows > total_h )
-                                               total_h = freetype_face->glyph->bitmap.rows;
-                                       if( (int)freetype_face->glyph->bitmap.rows > current_h )
-                                               current_h = freetype_face->glyph->bitmap.rows;
+                                       current_w = current_x + glyph_w;
+                                       if( current_w > max_width ) current_w = max_width;
+                                       if( total_w < current_w ) total_w = current_w;
+                                       if( current_ascent < glyph_t ) current_ascent = glyph_t;
+                                       if( current_h < glyph_h ) current_h = glyph_h;
+                                       if( total_h < glyph_h ) total_h = glyph_h;
                                }
                                else {
 // copy 1 row at a time, center vertically
-                                       int out_y = (total_h - height[font_number]) / 2 +
-                                               ascent[font_number] - freetype_face->glyph->bitmap_top;
-                                       for( int in_y = 0;
-                                            in_y < (int)freetype_face->glyph->bitmap.rows && out_y < total_h;
-                                            ++in_y, ++out_y ) {
-                                               unsigned char *out_row = font->image->get_rows()[out_y] +
-                                                       current_x * 4;
+                                       int out_y = (total_h-height[font_number])/2 + ascent[font_number]-glyph_t;
+                                       if( out_y < 0 ) out_y = 0;
+                                       for( int in_y = 0; in_y < glyph_h && out_y < total_h; ++in_y, ++out_y ) {
                                                unsigned char *in_row = freetype_face->glyph->bitmap.buffer +
                                                        freetype_face->glyph->bitmap.pitch * in_y;
+                                               int out_x = current_x;
+                                               unsigned char *out_row = font->image->get_rows()[out_y] +
+                                                       out_x * 4;
 
-                                               for( int out_x = 0;
-                                                    out_x < (int)freetype_face->glyph->bitmap.width && out_x < total_w;
-                                                    ++out_x ) {
+                                               for( int in_x = 0; in_x < glyph_w && out_x < total_w; ++in_x, ++out_x ) {
                                                        *out_row = (*in_row * r +
                                                                (0xff - *in_row) * *out_row) / 0xff; ++out_row;
                                                        *out_row = (*in_row * g +
@@ -1233,13 +1254,11 @@ void TitleMain::build_previews(TitleWindow *gui)
                                                        in_row++;
                                                }
                                        }
-                                       current_x += freetype_face->glyph->advance.x >> 6;
                                }
+                               current_x += glyph_a;
                        }
-
                        height[font_number] = current_h;
                        ascent[font_number] = current_ascent;
-                       if( pass == 0 && current_w > total_w ) total_w = current_w;
                }
        }
 
@@ -1457,36 +1476,43 @@ void TitleMain::draw_background()
 
 BC_FontEntry* TitleMain::get_font(const char *font_name, int style)
 {
-       int flavor =
+       if( !strcmp("fixed", font_name) )
+               font_name = FIXED_FONT;
+       int flavor = FL_WIDTH_MASK |
            ((style & BC_FONT_ITALIC) != 0 ? FL_SLANT_ITALIC | FL_SLANT_OBLIQUE : FL_SLANT_ROMAN) |
            ((style & BC_FONT_BOLD) != 0 ? FL_WEIGHT_BOLD | FL_WEIGHT_DEMIBOLD |
                        FL_WEIGHT_EXTRABOLD| FL_WEIGHT_BLACK | FL_WEIGHT_EXTRABLACK :
                        FL_WEIGHT_BOOK | FL_WEIGHT_NORMAL | FL_WEIGHT_MEDIUM |
                        FL_WEIGHT_LIGHT | FL_WEIGHT_EXTRALIGHT | FL_WEIGHT_THIN);
 
-       int mask = FL_WEIGHT_MASK | FL_SLANT_MASK;
+       int mask = FL_WEIGHT_MASK | FL_SLANT_MASK | FL_WIDTH_MASK;
 
        BC_Resources *resources =  BC_WindowBase::get_resources();
-       return resources->find_fontentry(font_name, flavor, mask, style);
+       BC_FontEntry *font = resources->find_fontentry(font_name, flavor, mask, style);
+       if( font && strcmp(font_name, font->displayname) ) font = 0;
+       return font;
 }
 BC_FontEntry* TitleMain::config_font()
 {
        BC_FontEntry *font = get_font(config.font, config.style);
-       if( font && load_font(font) ) font = 0;
+       if( !font || load_font(font) )
+               load_font(font = get_font(FIXED_FONT,0));
        return font;
 }
 
 
-static inline bool is_ltr(wchar_t wch) { return (wch>='a' && wch<='z') || (wch>='A' && wch<='Z'); }
-static inline bool is_nbr(wchar_t wch) { return (wch>='0' && wch<='9'); }
+static inline bool is_ltr(wchar_t wch) { return iswalpha(wch); }
+static inline bool is_nbr(wchar_t wch) { return iswdigit(wch); }
 static inline bool is_ws(wchar_t wch) { return wch==' ' || wch=='\t'; }
 static inline bool is_idch(wchar_t wch) { return is_ltr(wch) || is_nbr(wch) || wch=='_'; }
 
 // return eof=-1, chr=0, opener=1, closer=2
 int TitleParser::wget(wchar_t &wch)
 {
-       char *ip = id, *tp = text;  *ip = 0;  *tp = 0;
-       int ilen = sizeof(id), tlen = sizeof(text), ich;
+       wchar_t *wip = wid, *wtp = wtxt;  *wip = 0;  *wtp = 0;
+       int ilen = sizeof(wid)/sizeof(wid[0]);
+       int tlen = sizeof(wtxt)/sizeof(wtxt[0]);
+       int ich;
        while( (ich=wnext()) >= 0 ) {
                if( ich == '\\' ) {
                        if( (ich=wnext()) == '\n' ) continue;
@@ -1502,23 +1528,38 @@ int TitleParser::wget(wchar_t &wch)
        int ret = 1;  long pos = tell();
        if( (ich=wnext()) == '/' ) { ret = 2; ich = wnext(); }
        if( is_ltr(ich) ) {
-               *ip++ = ich;
+               *wip++ = ich;
                while( is_idch(ich=wnext()) )
-                       if( --ilen > 0 ) *ip++ = ich;
+                       if( --ilen > 0 ) *wip++ = ich;
        }
-       *ip = 0;
+       *wip = 0;
        while( is_ws(ich) ) ich = wnext();
        while( ich >= 0 && ich != '>' ) {
                if( ich == '\n' || ich == '<' ) { ich = -1;  break; }
                if( ich == '\\' && (ich=wnext()) < 0 ) break;
-               if( --tlen > 0 ) *tp++ = ich;
+               if( --tlen > 0 ) *wtp++ = ich;
                ich = wnext();
        }
-       *tp = 0;
+       *wtp = 0;
        if( ich < 0 ) { ich = '<';  seek(pos);  ret = 0; }
        wch = ich;
        return ret;
 }
+int TitleParser::tget(wchar_t &wch)
+{
+       int ret = wget(wch);
+       if( ret > 0 ) {
+               int wid_len = wcslen(wid)+1;
+               BC_Resources::encode(
+                       BC_Resources::wide_encoding, plugin->config.encoding,
+                       (char*)wid,wid_len*sizeof(wid[0]), (char *)id,sizeof(id));
+               int wtxt_len = wcslen(wtxt)+1;
+               BC_Resources::encode(
+                       BC_Resources::wide_encoding, plugin->config.encoding,
+                       (char*)wtxt,wtxt_len*sizeof(wtxt[0]), (char *)text,sizeof(text));
+       }
+       return ret;
+}
 
 TitleGlyph *TitleMain::get_glyph(FT_ULong char_code, BC_FontEntry *font, int size, int style)
 {
@@ -1537,10 +1578,11 @@ int TitleMain::get_width(TitleGlyph *cur, TitleGlyph *nxt)
        int result = cur->advance_x;
        if( !nxt ) return result;
        FT_Vector kerning;
-       FT_Get_Kerning(freetype_face,
-               cur->freetype_index, nxt->freetype_index,
-               ft_kerning_default, &kerning);
-       return result + (kerning.x >> 6);
+       if( !FT_Get_Kerning(freetype_face,
+           cur->freetype_index, nxt->freetype_index,
+           ft_kerning_default, &kerning) )
+               result += (kerning.x >> 6);
+       return result;
 }
 
 
@@ -1770,18 +1812,18 @@ int TitleCurNudge::set(const char *txt)
 
 int TitleParser::set_attributes(int ret)
 {
-        if( !strcmp(id,_("nudge")) )  return ret>1 ? cur_nudge.unset(text)  : cur_nudge.set(text);
-        if( !strcmp(id,_("color")) )  return ret>1 ? cur_color.unset(text)  : cur_color.set(text);
-        if( !strcmp(id,_("alpha")) )  return ret>1 ? cur_alpha.unset(text)  : cur_alpha.set(text);
-        if( !strcmp(id,_("font")) )   return ret>1 ? cur_font.unset(text)   : cur_font.set(text);
-        if( !strcmp(id,_("size")) )   return ret>1 ? cur_size.unset(text)   : cur_size.set(text);
-        if( !strcmp(id,_("bold")) )   return ret>1 ? cur_bold.unset(text)   : cur_bold.set(text);
-        if( !strcmp(id,_("italic")) ) return ret>1 ? cur_italic.unset(text) : cur_italic.set(text);
-        if( !strcmp(id,_("caps")) )   return ret>1 ? cur_caps.unset(text)   : cur_caps.set(text);
-        if( !strcmp(id,_("ul")) )     return ret>1 ? cur_under.unset(text)  : cur_under.set(text);
-        if( !strcmp(id,_("blink")) )  return ret>1 ? cur_blink.unset(text)  : cur_blink.set(text);
-        if( !strcmp(id,_("fixed")) )  return ret>1 ? cur_fixed.unset(text)  : cur_fixed.set(text);
-        if( !strcmp(id,_("sup")) )    return ret>1 ? cur_super.unset(text)  : cur_super.set(text);
+        if( !strcmp(id,KW_NUDGE) )  return ret>1 ? cur_nudge.unset(text)  : cur_nudge.set(text);
+        if( !strcmp(id,KW_COLOR) )  return ret>1 ? cur_color.unset(text)  : cur_color.set(text);
+        if( !strcmp(id,KW_ALPHA) )  return ret>1 ? cur_alpha.unset(text)  : cur_alpha.set(text);
+        if( !strcmp(id,KW_FONT) )   return ret>1 ? cur_font.unset(text)   : cur_font.set(text);
+        if( !strcmp(id,KW_SIZE) )   return ret>1 ? cur_size.unset(text)   : cur_size.set(text);
+        if( !strcmp(id,KW_BOLD) )   return ret>1 ? cur_bold.unset(text)   : cur_bold.set(text);
+        if( !strcmp(id,KW_ITALIC) ) return ret>1 ? cur_italic.unset(text) : cur_italic.set(text);
+        if( !strcmp(id,KW_CAPS) )   return ret>1 ? cur_caps.unset(text)   : cur_caps.set(text);
+        if( !strcmp(id,KW_UL) )     return ret>1 ? cur_under.unset(text)  : cur_under.set(text);
+        if( !strcmp(id,KW_BLINK) )  return ret>1 ? cur_blink.unset(text)  : cur_blink.set(text);
+        if( !strcmp(id,KW_FIXED) )  return ret>1 ? cur_fixed.unset(text)  : cur_fixed.set(text);
+        if( !strcmp(id,KW_SUP) )    return ret>1 ? cur_super.unset(text)  : cur_super.set(text);
        return 1;
 }
 
@@ -1795,45 +1837,45 @@ void TitleMain::load_glyphs()
        while( !wchrs.eof() ) {
                wchar_t wch1 = wchrs.wcur(), wch;
                long ipos = wchrs.tell();
-               int ret = wchrs.wget(wch);
+               int ret = wchrs.tget(wch);
                if( ret > 0 ) {
                        if( !wchrs.set_attributes(ret) ) continue;
-                       if( !strcmp(wchrs.id,"png") && add_image(wchrs.text) ) continue;
+                       if( !strcmp(wchrs.id,KW_PNG) && add_image(wchrs.text) ) continue;
                        wch = wch1;  wchrs.seek(ipos+1);
                        ret = 0;
                }
-               if( !ret ) {
-                       int cur_caps = wchrs.cur_caps;
-                       if( cur_caps > 0 ) wch = towupper(wch);
-                       else if( cur_caps < 0 ) wch = towlower(wch);
-                       BC_FontEntry *cur_font = wchrs.cur_font;
-                       int cur_size = wchrs.cur_size;
-                       int cur_style = 0;
-                       int cur_bold  = wchrs.cur_bold;
-                       if( cur_bold ) cur_style |= BC_FONT_BOLD;
-                       int cur_italic  = wchrs.cur_italic;
-                       if( cur_italic ) cur_style |= BC_FONT_ITALIC;
-                       int cur_super = wchrs.cur_super;
-                       if( cur_super ) cur_size /= 2;
-                       int exists = 0;
-                       for( int j=0; j<title_glyphs.count(); ++j ) {
-                               TitleGlyph *glyph = title_glyphs[j];
-                               if( glyph->char_code == (FT_ULong)wch && glyph->font == cur_font &&
-                                   glyph->size == cur_size && glyph->style == cur_style ) {
-                                       exists = 1;   break;
-                               }
-                       }
+               if( ret || wch == '\n' ) continue;
 
-                       if( !exists ) {
-                               total_packages++;
-                               TitleGlyph *glyph = new TitleGlyph;
-                               glyph->char_code = (FT_ULong)wch;
-                               glyph->font = cur_font;
-                               glyph->size = cur_size;
-                               glyph->style = cur_style;
-                               title_glyphs.append(glyph);
+               int cur_caps = wchrs.cur_caps;
+               if( cur_caps > 0 ) wch = towupper(wch);
+               else if( cur_caps < 0 ) wch = towlower(wch);
+               BC_FontEntry *cur_font = wchrs.cur_font;
+               int cur_size = wchrs.cur_size;
+               int cur_style = 0;
+               int cur_bold  = wchrs.cur_bold;
+               if( cur_bold ) cur_style |= BC_FONT_BOLD;
+               int cur_italic  = wchrs.cur_italic;
+               if( cur_italic ) cur_style |= BC_FONT_ITALIC;
+               int cur_super = wchrs.cur_super;
+               if( cur_super ) cur_size /= 2;
+               int exists = 0;
+               for( int j=0; j<title_glyphs.count(); ++j ) {
+                       TitleGlyph *glyph = title_glyphs[j];
+                       if( glyph->char_code == (FT_ULong)wch && glyph->font == cur_font &&
+                           glyph->size == cur_size && glyph->style == cur_style ) {
+                               exists = 1;   break;
                        }
                }
+
+               if( !exists && cur_font ) {
+                       total_packages++;
+                       TitleGlyph *glyph = new TitleGlyph;
+                       glyph->char_code = (FT_ULong)wch;
+                       glyph->font = cur_font;
+                       glyph->size = cur_size;
+                       glyph->style = cur_style;
+                       title_glyphs.append(glyph);
+               }
        }
 
        if( !glyph_engine )
@@ -1900,7 +1942,7 @@ int TitleMain::get_text()
                TitleChar *chr = 0;
                long ipos = wchrs.tell();
                wchar_t wch1 = wchrs.wcur(), wch;
-               int ret = wchrs.wget(wch);
+               int ret = wchrs.tget(wch);
                if( ret < 0 || wch == '\n' ) {
                        if( row->x1 > row->x2 ) row->x1 = row->x2 = 0;
                        if( row->y2 > row->y1 ) row->y1 = row->y2 = 0;
@@ -1935,7 +1977,7 @@ int TitleMain::get_text()
                if( ret > 0 ) {
                        if( !wchrs.set_attributes(ret) ) continue;
                        ret = -1;
-                       if( !strcmp(wchrs.id,"png") ) {
+                       if( !strcmp(wchrs.id,KW_PNG) ) {
                                VFrame *png_image = get_image(wchrs.text);
                                if( png_image ) {
                                        chr = title_chars.add(CHAR_IMAGE, png_image);
@@ -2049,7 +2091,7 @@ int TitleMain::get_visible_text()
        case RIGHT_TO_LEFT:
        case LEFT_TO_RIGHT: {
                float magnitude = config.pixels_per_second *
-                       (get_source_position() - config.prev_keyframe_position) /
+                       (get_source_position() - get_source_start()) /
                        PluginVClient::project_frame_rate;
                if( config.loop ) {
                        int loop_size = text_w + title_w;
@@ -2264,21 +2306,21 @@ int TitleMain::draw_underline(VFrame *mask, int alpha)
 
 void TitleMain::draw_overlay()
 {
-
 //printf("TitleMain::draw_overlay 1\n");
         fade = 1;
         if( !EQUIV(config.fade_in, 0) ) {
-               int fade_len = lroundf(config.fade_in * PluginVClient::project_frame_rate);
-               int fade_position = get_source_position() - config.prev_keyframe_position;
+               int64_t plugin_start = server->plugin->startproject;
+               int64_t fade_len = lroundf(config.fade_in * PluginVClient::project_frame_rate);
+               int64_t fade_position = get_source_position() - plugin_start;
 
                if( fade_position >= 0 && fade_position < fade_len ) {
                        fade = (float)fade_position / fade_len;
                }
        }
         if( !EQUIV(config.fade_out, 0) ) {
-               int fade_len = lroundf(config.fade_out * PluginVClient::project_frame_rate);
-               int fade_position = config.next_keyframe_position - get_source_position();
-
+               int64_t plugin_end = server->plugin->startproject + server->plugin->length;
+               int64_t fade_len = lroundf(config.fade_out * PluginVClient::project_frame_rate);
+               int64_t fade_position = plugin_end - get_source_position();
 
                if( fade_position >= 0 && fade_position < fade_len ) {
                        fade = (float)fade_position / fade_len;
@@ -2632,25 +2674,25 @@ void TitleMain::read_data(KeyFrame *keyframe)
        }
 }
 
-void TitleMain::insert_text(const char *txt, int pos)
+void TitleMain::insert_text(const wchar_t *wtxt, int pos)
 {
-       int ilen = strlen(txt);
+       int len = wcslen(wtxt);
        wchar_t *wtext = config.wtext;
        int wsize = sizeof(config.wtext)-1;
        int wlen = config.wlen;
        if( pos < 0 ) pos = 0;
        if( pos > wlen ) pos = wlen;
 
-       for( int i=wlen-1, j=wlen+ilen-1; i>=pos; --i,--j ) {
+       for( int i=wlen-1, j=wlen+len-1; i>=pos; --i,--j ) {
                if( j >= wsize ) continue;
                wtext[j] = wtext[i];
        }
-       for( int i=pos, j=0; j<ilen; ++i,++j ) {
+       for( int i=pos, j=0; j<len; ++i,++j ) {
                if( i >= wsize ) break;
-               wtext[i] = txt[j];
+               wtext[i] = wtxt[j];
        }
 
-       if( (wlen+=ilen) > wsize ) wlen = wsize;
+       if( (wlen+=len) > wsize ) wlen = wsize;
        wtext[wlen] = 0;
        config.wlen = wlen;
 }