forked from mia/Aegisub
Rewrite recombining of overlapping lines for export to simple subtitle formats, and reorganisation of other functions also used in those exports
Originally committed to SVN as r2267.
This commit is contained in:
parent
2d7b2527a7
commit
270ca4f876
8 changed files with 278 additions and 11 deletions
|
@ -365,9 +365,172 @@ void SubtitleFormat::ConvertTags(int format,wxString lineEnd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////
|
||||||
|
// Remove all comment lines
|
||||||
|
void SubtitleFormat::StripComments() {
|
||||||
|
using std::list;
|
||||||
|
list<AssEntry*>::iterator next;
|
||||||
|
|
||||||
|
for (list<AssEntry*>::iterator cur = Line->begin(); cur != Line->end(); cur = next) {
|
||||||
|
next = cur;
|
||||||
|
next++;
|
||||||
|
|
||||||
|
AssDialogue *dlg = AssEntry::GetAsDialogue(*cur);
|
||||||
|
if (dlg && (dlg->Comment || dlg->Text.IsEmpty())) {
|
||||||
|
delete *cur;
|
||||||
|
Line->erase(cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////
|
||||||
|
// Remove all non-dialogue lines
|
||||||
|
void SubtitleFormat::StripNonDialogue() {
|
||||||
|
using std::list;
|
||||||
|
list<AssEntry*>::iterator next;
|
||||||
|
|
||||||
|
for (list<AssEntry*>::iterator cur = Line->begin(); cur != Line->end(); cur = next) {
|
||||||
|
next = cur;
|
||||||
|
next++;
|
||||||
|
|
||||||
|
if (!AssEntry::GetAsDialogue(*cur)) {
|
||||||
|
delete *cur;
|
||||||
|
Line->erase(cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////
|
||||||
|
// Helper function for RecombineOverlaps()
|
||||||
|
static void InsertLineSortedIntoList(std::list<AssEntry*> &list, std::list<AssEntry*>::iterator next, AssDialogue *newdlg) {
|
||||||
|
std::list<AssEntry*>::iterator insertpos = next;
|
||||||
|
bool inserted = false;
|
||||||
|
while (insertpos != list.end()) {
|
||||||
|
AssDialogue *candidate = AssEntry::GetAsDialogue(*insertpos);
|
||||||
|
if (candidate && candidate->Start >= newdlg->Start) {
|
||||||
|
list.insert(insertpos, newdlg);
|
||||||
|
inserted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
insertpos++;
|
||||||
|
}
|
||||||
|
if (!inserted) {
|
||||||
|
list.push_back(newdlg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
// Split and merge lines so there are no overlapping lines
|
||||||
|
// http://malakith.net/aegiwiki/Split-merge_algorithm_for_converting_to_simple_subtitle_formats
|
||||||
|
void SubtitleFormat::RecombineOverlaps() {
|
||||||
|
using std::list;
|
||||||
|
list<AssEntry*>::iterator next;
|
||||||
|
|
||||||
|
for (list<AssEntry*>::iterator cur = Line->begin(); cur != Line->end(); cur = next) {
|
||||||
|
next = cur;
|
||||||
|
next++;
|
||||||
|
|
||||||
|
if (next == Line->end()) break;
|
||||||
|
|
||||||
|
AssDialogue *prevdlg = AssEntry::GetAsDialogue(*cur);
|
||||||
|
AssDialogue *curdlg = AssEntry::GetAsDialogue(*next);
|
||||||
|
|
||||||
|
if (curdlg && prevdlg && prevdlg->End > curdlg->Start) {
|
||||||
|
// Use names like in the algorithm description and prepare for erasing
|
||||||
|
// old dialogues from the list
|
||||||
|
list<AssEntry*>::iterator prev = cur;
|
||||||
|
cur = next;
|
||||||
|
next++;
|
||||||
|
|
||||||
|
// std::list::insert() inserts items before the given iterator, so
|
||||||
|
// we need 'next' for inserting. 'prev' and 'cur' can safely be erased
|
||||||
|
// from the list now.
|
||||||
|
Line->erase(prev);
|
||||||
|
Line->erase(cur);
|
||||||
|
|
||||||
|
//Is there an A part before the overlap?
|
||||||
|
if (curdlg->Start > prevdlg->Start) {
|
||||||
|
// Produce new entry with correct values
|
||||||
|
AssDialogue *newdlg = AssEntry::GetAsDialogue(prevdlg->Clone());
|
||||||
|
newdlg->Start = prevdlg->Start;
|
||||||
|
newdlg->End = curdlg->Start;
|
||||||
|
newdlg->Text = prevdlg->Text;
|
||||||
|
|
||||||
|
InsertLineSortedIntoList(*Line, next, newdlg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overlapping A+B part
|
||||||
|
{
|
||||||
|
AssDialogue *newdlg = AssEntry::GetAsDialogue(prevdlg->Clone());
|
||||||
|
newdlg->Start = curdlg->Start;
|
||||||
|
newdlg->End = (prevdlg->End < curdlg->End) ? prevdlg->End : curdlg->End;
|
||||||
|
// Put an ASS format hard linewrap between lines
|
||||||
|
newdlg->Text = curdlg->Text + _T("\\N") + prevdlg->Text;
|
||||||
|
|
||||||
|
InsertLineSortedIntoList(*Line, next, newdlg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is there an A part after the overlap?
|
||||||
|
if (prevdlg->End > curdlg->End) {
|
||||||
|
// Produce new entry with correct values
|
||||||
|
AssDialogue *newdlg = AssEntry::GetAsDialogue(prevdlg->Clone());
|
||||||
|
newdlg->Start = curdlg->End;
|
||||||
|
newdlg->End = prevdlg->End;
|
||||||
|
newdlg->Text = prevdlg->Text;
|
||||||
|
|
||||||
|
InsertLineSortedIntoList(*Line, next, newdlg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is there a B part after the overlap?
|
||||||
|
if (curdlg->End > prevdlg->End) {
|
||||||
|
// Produce new entry with correct values
|
||||||
|
AssDialogue *newdlg = AssEntry::GetAsDialogue(prevdlg->Clone());
|
||||||
|
newdlg->Start = prevdlg->End;
|
||||||
|
newdlg->End = curdlg->End;
|
||||||
|
newdlg->Text = curdlg->Text;
|
||||||
|
|
||||||
|
InsertLineSortedIntoList(*Line, next, newdlg);
|
||||||
|
}
|
||||||
|
|
||||||
|
next--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
// Merge identical lines that follow each other
|
||||||
|
void SubtitleFormat::MergeIdentical() {
|
||||||
|
using std::list;
|
||||||
|
list<AssEntry*>::iterator next;
|
||||||
|
|
||||||
|
for (list<AssEntry*>::iterator cur = Line->begin(); cur != Line->end(); cur = next) {
|
||||||
|
next = cur;
|
||||||
|
next++;
|
||||||
|
|
||||||
|
if (next == Line->end()) break;
|
||||||
|
|
||||||
|
AssDialogue *curdlg = AssEntry::GetAsDialogue(*cur);
|
||||||
|
AssDialogue *nextdlg = AssEntry::GetAsDialogue(*next);
|
||||||
|
|
||||||
|
if (curdlg && nextdlg && curdlg->Text == nextdlg->Text) {
|
||||||
|
// Merge timing
|
||||||
|
nextdlg->Start = (nextdlg->Start < curdlg->Start ? nextdlg->Start : curdlg->Start);
|
||||||
|
nextdlg->End = (nextdlg->End > curdlg->End ? nextdlg->End : curdlg->End);
|
||||||
|
|
||||||
|
// Remove duplicate line
|
||||||
|
delete *cur;
|
||||||
|
Line->erase(cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////
|
////////////////////////////////////////////
|
||||||
// Merge identical and/or overlapping lines
|
// Merge identical and/or overlapping lines
|
||||||
void SubtitleFormat::Merge(bool identical,bool overlaps,bool stripComments,bool stripNonDialogue) {
|
/*void SubtitleFormat::Merge(bool identical,bool overlaps,bool stripComments,bool stripNonDialogue) {
|
||||||
using std::list;
|
using std::list;
|
||||||
list<AssEntry*>::iterator next;
|
list<AssEntry*>::iterator next;
|
||||||
list<AssEntry*>::iterator prev = Line->end();
|
list<AssEntry*>::iterator prev = Line->end();
|
||||||
|
@ -389,9 +552,9 @@ void SubtitleFormat::Merge(bool identical,bool overlaps,bool stripComments,bool
|
||||||
|
|
||||||
// Proper line
|
// Proper line
|
||||||
else {
|
else {
|
||||||
// Check for duplication
|
|
||||||
if (previous != NULL) {
|
if (previous != NULL) {
|
||||||
if (previous->Text == current->Text) {
|
// Check for duplication
|
||||||
|
if (identical && previous->Text == current->Text) {
|
||||||
if (abs(current->Start.GetMS() - previous->End.GetMS()) < 20) {
|
if (abs(current->Start.GetMS() - previous->End.GetMS()) < 20) {
|
||||||
current->Start = (current->Start < previous->Start ? current->Start : previous->Start);
|
current->Start = (current->Start < previous->Start ? current->Start : previous->Start);
|
||||||
current->End = (current->End > previous->End ? current->End : previous->End);
|
current->End = (current->End > previous->End ? current->End : previous->End);
|
||||||
|
@ -399,6 +562,94 @@ void SubtitleFormat::Merge(bool identical,bool overlaps,bool stripComments,bool
|
||||||
Line->erase(prev);
|
Line->erase(prev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for overlap
|
||||||
|
//
|
||||||
|
if (overlaps && previous->End.GetMS() > current->Start.GetMS()) {
|
||||||
|
// Grab the data
|
||||||
|
int prev_s = previous->Start.GetMS();
|
||||||
|
int prev_e = previous->End.GetMS();
|
||||||
|
int cur_s = current->Start.GetMS();
|
||||||
|
int cur_e = current->End.GetMS();
|
||||||
|
wxString prev_t = previous->Text;
|
||||||
|
wxString cur_t = current->Text;
|
||||||
|
|
||||||
|
// Make sure cur points to the line before those we're working with
|
||||||
|
--cur;
|
||||||
|
// Remove 'cur' and 'prev' from list
|
||||||
|
Line->erase(prev);
|
||||||
|
Line->erase(prev);
|
||||||
|
// Now make sure that 'cur' points to the item after the old 'current'
|
||||||
|
++cur;
|
||||||
|
|
||||||
|
// A-only part
|
||||||
|
if (prev_s < cur_s) {
|
||||||
|
// Actually lines generated from this one never move
|
||||||
|
// So we can assume it can just be inserted at cur
|
||||||
|
AssDialogue *part = AssEntry::GetAsDialogue(previous->Clone());
|
||||||
|
part->End.SetMS(prev_s);
|
||||||
|
// Nothing to do about start-time or text, they are already correct
|
||||||
|
|
||||||
|
// Insert into list
|
||||||
|
Line->insert(cur, part);
|
||||||
|
// 'cur' now points to one past newly inserted item
|
||||||
|
}
|
||||||
|
|
||||||
|
// A+B part
|
||||||
|
{
|
||||||
|
// These lines never move either, so just insert
|
||||||
|
AssDialogue *part = AssEntry::GetAsDialogue(current->Clone());
|
||||||
|
// Fix values
|
||||||
|
part->Start.SetMS(cur_s);
|
||||||
|
part->End.SetMS(prev_e);
|
||||||
|
// Inserting an ASS hard linebreak
|
||||||
|
part->Text = cur_t + _T("\\N") + prev_t;
|
||||||
|
|
||||||
|
// Insert into list
|
||||||
|
Line->insert(cur, part);
|
||||||
|
// 'cur' now points to one past newly inserted item
|
||||||
|
}
|
||||||
|
|
||||||
|
// B-only part
|
||||||
|
if (cur_e > prev_e) {
|
||||||
|
// We need to seek for the right position here
|
||||||
|
AssDialogue *part = AssEntry::GetAsDialogue(current->Clone());
|
||||||
|
// Fix values
|
||||||
|
part->Start.SetMS(prev_e);
|
||||||
|
part->End.SetMS(cur_e);
|
||||||
|
part->Text = cur_t;
|
||||||
|
|
||||||
|
list<AssEntry*>::iterator newpos = cur;
|
||||||
|
|
||||||
|
// Insert into list
|
||||||
|
// Make sure 'cur' never moves during this
|
||||||
|
if (newpos == Line->end()) {
|
||||||
|
Line->push_back(part);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
while (newpos != Line->end()) {
|
||||||
|
AssDialogue *newline = AssEntry::GetAsDialogue(*newpos);
|
||||||
|
if (newline && part->Start.GetMS() <= newline->Start.GetMS()) {
|
||||||
|
// Suitable position
|
||||||
|
Line->insert(newpos, part);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++newpos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now 'cur' has moved around a lot so make sure everything is correct
|
||||||
|
// 'cur' currently points to one past the last inserted item
|
||||||
|
// It should really point to the last inserted item (that wasn't
|
||||||
|
// inserted at a later position) so we need to move it one back.
|
||||||
|
// In fact, 'next' should have that value.
|
||||||
|
next = cur--;
|
||||||
|
// 'prev' should already be correct
|
||||||
|
// And update the rest too
|
||||||
|
previous = AssEntry::GetAsDialogue(*prev);
|
||||||
|
current = AssEntry::GetAsDialogue(*cur);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set as previous
|
// Set as previous
|
||||||
|
@ -415,4 +666,4 @@ void SubtitleFormat::Merge(bool identical,bool overlaps,bool stripComments,bool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
|
@ -71,7 +71,11 @@ protected:
|
||||||
void ClearCopy();
|
void ClearCopy();
|
||||||
void SortLines();
|
void SortLines();
|
||||||
void ConvertTags(int format,wxString lineEnd);
|
void ConvertTags(int format,wxString lineEnd);
|
||||||
void Merge(bool identical,bool overlaps,bool stripComments,bool stripNonDialogue);
|
//void Merge(bool identical,bool overlaps,bool stripComments,bool stripNonDialogue);
|
||||||
|
void StripComments();
|
||||||
|
void StripNonDialogue();
|
||||||
|
void RecombineOverlaps();
|
||||||
|
void MergeIdentical();
|
||||||
|
|
||||||
void Clear();
|
void Clear();
|
||||||
void LoadDefault(bool defline=true);
|
void LoadDefault(bool defline=true);
|
||||||
|
|
|
@ -347,7 +347,9 @@ void DVDSubtitleFormat::WriteFile(wxString filename,wxString encoding) {
|
||||||
// Prepare subtitles
|
// Prepare subtitles
|
||||||
CreateCopy();
|
CreateCopy();
|
||||||
SortLines();
|
SortLines();
|
||||||
Merge(true,true,true,false);
|
StripComments();
|
||||||
|
RecombineOverlaps();
|
||||||
|
MergeIdentical();
|
||||||
|
|
||||||
// Get subpictures
|
// Get subpictures
|
||||||
std::vector<SubPicture> pics;
|
std::vector<SubPicture> pics;
|
||||||
|
|
|
@ -77,7 +77,9 @@ void EncoreSubtitleFormat::WriteFile(wxString _filename,wxString encoding) {
|
||||||
// Convert to encore
|
// Convert to encore
|
||||||
CreateCopy();
|
CreateCopy();
|
||||||
SortLines();
|
SortLines();
|
||||||
Merge(true,true,true,false);
|
StripComments();
|
||||||
|
RecombineOverlaps();
|
||||||
|
MergeIdentical();
|
||||||
ConvertTags(1,_T("\r\n"));
|
ConvertTags(1,_T("\r\n"));
|
||||||
|
|
||||||
// Write lines
|
// Write lines
|
||||||
|
|
|
@ -178,7 +178,9 @@ void MicroDVDSubtitleFormat::WriteFile(wxString filename,wxString encoding) {
|
||||||
// Convert file
|
// Convert file
|
||||||
CreateCopy();
|
CreateCopy();
|
||||||
SortLines();
|
SortLines();
|
||||||
Merge(true,true,true,false);
|
StripComments();
|
||||||
|
RecombineOverlaps();
|
||||||
|
MergeIdentical();
|
||||||
ConvertTags(1,_T("|"));
|
ConvertTags(1,_T("|"));
|
||||||
|
|
||||||
// Open file
|
// Open file
|
||||||
|
|
|
@ -182,7 +182,9 @@ void SRTSubtitleFormat::WriteFile(wxString _filename,wxString encoding) {
|
||||||
// Convert to SRT
|
// Convert to SRT
|
||||||
CreateCopy();
|
CreateCopy();
|
||||||
SortLines();
|
SortLines();
|
||||||
Merge(true,true,true,false);
|
StripComments();
|
||||||
|
RecombineOverlaps();
|
||||||
|
MergeIdentical();
|
||||||
ConvertTags(2,_T("\r\n"));
|
ConvertTags(2,_T("\r\n"));
|
||||||
|
|
||||||
// Write lines
|
// Write lines
|
||||||
|
|
|
@ -79,7 +79,9 @@ void TranStationSubtitleFormat::WriteFile(wxString _filename,wxString encoding)
|
||||||
// Convert to TranStation
|
// Convert to TranStation
|
||||||
CreateCopy();
|
CreateCopy();
|
||||||
SortLines();
|
SortLines();
|
||||||
Merge(true,true,true,false);
|
StripComments();
|
||||||
|
RecombineOverlaps();
|
||||||
|
MergeIdentical();
|
||||||
|
|
||||||
// Write lines
|
// Write lines
|
||||||
using std::list;
|
using std::list;
|
||||||
|
|
|
@ -322,7 +322,9 @@ void TTXTSubtitleFormat::WriteLine(wxXmlNode *root, AssDialogue *line) {
|
||||||
void TTXTSubtitleFormat::ConvertToTTXT () {
|
void TTXTSubtitleFormat::ConvertToTTXT () {
|
||||||
// Convert
|
// Convert
|
||||||
SortLines();
|
SortLines();
|
||||||
Merge(true,true,true,false);
|
StripComments();
|
||||||
|
RecombineOverlaps();
|
||||||
|
MergeIdentical();
|
||||||
ConvertTags(1,_T("\r\n"));
|
ConvertTags(1,_T("\r\n"));
|
||||||
|
|
||||||
// Find last line
|
// Find last line
|
||||||
|
|
Loading…
Reference in a new issue