Clean up SubtitleFormat

Document all of the SubtitleFormat methods.

Add default implementations of CanReadFile and CanWriteFile that check
against the appropriate wildcard list.

Clean up and simplify a lot of very odd code.

Throw typed exceptions in all subtitle readers rather than strings.

Originally committed to SVN as r5617.
This commit is contained in:
Thomas Goyne 2011-09-28 19:44:53 +00:00
parent 156885b56d
commit 4ec507f814
22 changed files with 511 additions and 1182 deletions

View file

@ -104,16 +104,6 @@ void AssFile::Load(const wxString &_filename,wxString charset,bool addToRecent)
catch (agi::UserCancelException const&) {
return;
}
catch (const char *except) {
wxMessageBox(except,"Error loading file",wxICON_ERROR | wxOK);
return;
}
catch (wxString &except) {
wxMessageBox(except,"Error loading file",wxICON_ERROR | wxOK);
return;
}
// Real exception
catch (agi::Exception &e) {
wxMessageBox(lagi_wxString(e.GetChainedMessage()), "Error loading file", wxICON_ERROR|wxOK);

View file

@ -360,6 +360,7 @@ namespace Automation4 {
///
FeatureSubtitleFormat::FeatureSubtitleFormat(const wxString &_name, const wxString &_extension)
: Feature(SCRIPTFEATURE_SUBFORMAT, _name)
, SubtitleFormat(_name)
, extension(_extension)
{
// nothing to do

View file

@ -41,6 +41,7 @@
#include <wx/choicdlg.h> // Keep this last so wxUSE_CHOICEDLG is set.
#endif
#include "ass_dialogue.h"
#include "ass_file.h"
#include "subtitle_format.h"
#include "subtitle_format_ass.h"
@ -52,47 +53,42 @@
#include "subtitle_format_transtation.h"
#include "subtitle_format_ttxt.h"
#include "subtitle_format_txt.h"
#include "utils.h"
#include "video_context.h"
/// @brief Constructor
///
SubtitleFormat::SubtitleFormat() {
Line = NULL;
Register();
isCopy = false;
using namespace std::tr1::placeholders;
SubtitleFormat::SubtitleFormat(wxString const& name)
: name(name)
, isCopy(0)
, Line(0)
{
formats.push_back(this);
}
/// @brief Destructor
///
SubtitleFormat::~SubtitleFormat () {
Remove();
SubtitleFormat::~SubtitleFormat() {
formats.remove(this);
}
/// DOCME
std::list<SubtitleFormat*> SubtitleFormat::formats;
/// DOCME
bool SubtitleFormat::loaded = false;
/// @brief Set target
/// @param file
///
void SubtitleFormat::SetTarget(AssFile *file) {
ClearCopy();
if (!file) Line = NULL;
else Line = &file->Line;
Line = file ? &file->Line : 0;
assFile = file;
}
/// @brief Create copy
///
bool SubtitleFormat::CanReadFile(wxString const& filename) const {
return GetReadWildcards().Index(filename.AfterLast('.'), false) != wxNOT_FOUND;
}
bool SubtitleFormat::CanWriteFile(wxString const& filename) const {
return GetWriteWildcards().Index(filename.AfterLast('.'), false) != wxNOT_FOUND;
}
void SubtitleFormat::CreateCopy() {
SetTarget(new AssFile(*assFile));
isCopy = true;
}
/// @brief Clear copy
///
void SubtitleFormat::ClearCopy() {
if (isCopy) {
delete assFile;
@ -101,184 +97,19 @@ void SubtitleFormat::ClearCopy() {
}
}
/// @brief Clear subtitles
///
void SubtitleFormat::Clear() {
assFile->Clear();
}
/// @brief Load default
/// @param defline
///
void SubtitleFormat::LoadDefault(bool defline) {
assFile->LoadDefault(defline);
}
/// @brief Add line
/// @param data
/// @param group
/// @param version
/// @param outgroup
void SubtitleFormat::AddLine(wxString data,wxString group,int &version,wxString *outgroup) {
assFile->AddLine(data,group,version,outgroup);
}
/// @brief Add formats
///
void SubtitleFormat::LoadFormats () {
if (!loaded) {
new ASSSubtitleFormat();
new SRTSubtitleFormat();
new TXTSubtitleFormat();
new TTXTSubtitleFormat();
new MicroDVDSubtitleFormat();
new MKVSubtitleFormat();
new EncoreSubtitleFormat();
new TranStationSubtitleFormat();
#ifdef _DEBUG
new DVDSubtitleFormat();
#endif
}
loaded = true;
}
/// @brief Destroy formats
///
void SubtitleFormat::DestroyFormats () {
std::list<SubtitleFormat*>::iterator cur;
for (cur=formats.begin();cur!=formats.end();cur = formats.begin()) {
delete *cur;
}
formats.clear();
}
/// @brief Get an appropriate reader
/// @param filename
/// @return
///
SubtitleFormat *SubtitleFormat::GetReader(wxString filename) {
LoadFormats();
std::list<SubtitleFormat*>::iterator cur;
SubtitleFormat *reader;
for (cur=formats.begin();cur!=formats.end();cur++) {
reader = *cur;
if (reader->CanReadFile(filename)) return reader;
}
return NULL;
}
/// @brief Get an appropriate writer
/// @param filename
/// @return
///
SubtitleFormat *SubtitleFormat::GetWriter(wxString filename) {
LoadFormats();
std::list<SubtitleFormat*>::iterator cur;
SubtitleFormat *writer;
for (cur=formats.begin();cur!=formats.end();cur++) {
writer = *cur;
if (writer->CanWriteFile(filename)) return writer;
}
return NULL;
}
/// @brief Register
/// @return
///
void SubtitleFormat::Register() {
std::list<SubtitleFormat*>::iterator cur;
for (cur=formats.begin();cur!=formats.end();cur++) {
if (*cur == this) return;
}
formats.push_back(this);
}
/// @brief Remove
/// @return
///
void SubtitleFormat::Remove() {
std::list<SubtitleFormat*>::iterator cur;
for (cur=formats.begin();cur!=formats.end();cur++) {
if (*cur == this) {
formats.erase(cur);
return;
}
}
}
/// @brief Get read wildcards
/// @return
///
wxArrayString SubtitleFormat::GetReadWildcards() {
return wxArrayString();
}
/// @brief Get write wildcards
/// @return
///
wxArrayString SubtitleFormat::GetWriteWildcards() {
return wxArrayString();
}
/// @brief Get wildcard list
/// @param mode
/// @return
///
wxString SubtitleFormat::GetWildcards(int mode) {
// Ensure it's loaded
LoadFormats();
// Variables
wxArrayString all;
wxArrayString cur;
wxString wild;
wxString final;
wxString temp1;
wxString temp2;
// For each format
std::list<SubtitleFormat*>::iterator curIter;
SubtitleFormat *format;
for (curIter=formats.begin();curIter!=formats.end();curIter++) {
// Get list
format = *curIter;
if (mode == 0) cur = format->GetReadWildcards();
else if (mode == 1) cur = format->GetWriteWildcards();
temp1.Clear();
temp2.Clear();
// Has wildcards
if (cur.Count()) {
// Process entries
for (unsigned int i=0;i<cur.Count();i++) {
wild = "*." + cur[i];
all.Add(wild);
temp1 += wild + ",";
temp2 += wild + ";";
}
// Assemble final name
final += format->GetName() + " (" + temp1.Left(temp1.Length()-1) + ")|" + temp2.Left(temp2.Length()-1) + "|";
}
}
// Add "all formats" list
temp1.Clear();
temp2.Clear();
for (unsigned int i=0;i<all.Count();i++) {
temp1 += all[i] + ",";
temp2 += all[i] + ";";
}
final = wxString(_("All Supported Formats")) + " (" + temp1.Left(temp1.Length()-1) + ")|" + temp2.Left(temp2.Length()-1) + "|" + final.Left(final.Length()-1);
// Return final list
return final;
void SubtitleFormat::AddLine(wxString data, wxString group, int &version, wxString *outgroup) {
assFile->AddLine(data, group, version, outgroup);
}
/// @brief Ask the user to enter the FPS
/// @param showSMPTE
/// @return
///
SubtitleFormat::FPSRational SubtitleFormat::AskForFPS(bool showSMPTE) {
wxArrayString choices;
FPSRational fps_rat;
@ -290,8 +121,8 @@ SubtitleFormat::FPSRational SubtitleFormat::AskForFPS(bool showSMPTE) {
if (vidLoaded) {
wxString vidFPS;
if (context->FPS().IsVFR()) vidFPS = "VFR";
else vidFPS = wxString::Format("%.3f",context->FPS().FPS());
choices.Add(wxString::Format("From video (%s)",vidFPS));
else vidFPS = wxString::Format("%.3f", context->FPS().FPS());
choices.Add(wxString::Format("From video (%s)", vidFPS));
}
// Standard FPS values
@ -310,7 +141,7 @@ SubtitleFormat::FPSRational SubtitleFormat::AskForFPS(bool showSMPTE) {
choices.Add(_("120.000 FPS"));
// Ask
int choice = wxGetSingleChoiceIndex(_("Please choose the appropriate FPS for the subtitles:"),_("FPS"),choices);
int choice = wxGetSingleChoiceIndex(_("Please choose the appropriate FPS for the subtitles:"), _("FPS"), choices);
if (choice == -1) {
fps_rat.num = 0;
fps_rat.den = 0;
@ -364,191 +195,143 @@ SubtitleFormat::FPSRational SubtitleFormat::AskForFPS(bool showSMPTE) {
return fps_rat;
}
/// @brief Sort lines
///
void SubtitleFormat::SortLines() {
AssFile::Sort(*Line);
}
/// @brief Convert tags
/// @param format
/// @param lineEnd
///
void SubtitleFormat::ConvertTags(int format,const wxString &lineEnd,bool mergeLineBreaks) {
using std::list;
list<AssEntry*>::iterator next;
for (list<AssEntry*>::iterator cur=Line->begin();cur!=Line->end();cur++) {
AssDialogue *current = dynamic_cast<AssDialogue*>(*cur);
if (current) {
void SubtitleFormat::ConvertTags(int format, const wxString &lineEnd, bool mergeLineBreaks) {
for (std::list<AssEntry*>::iterator cur = Line->begin(); cur != Line->end(); ++cur) {
if (AssDialogue *current = dynamic_cast<AssDialogue*>(*cur)) {
// Strip tags
if (format == 1) current->StripTags();
else if (format == 2) current->ConvertTagsToSRT();
// Replace line breaks
current->Text.Replace("\\h"," ",true);
current->Text.Replace("\\n",lineEnd,true);
current->Text.Replace("\\N",lineEnd,true);
current->Text.Replace("\\h", " ");
current->Text.Replace("\\n", lineEnd);
current->Text.Replace("\\N", lineEnd);
if (mergeLineBreaks) {
while (current->Text.Replace(lineEnd+lineEnd,lineEnd,true)) {};
while (current->Text.Replace(lineEnd+lineEnd, lineEnd));
}
}
}
}
/// @brief 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 = dynamic_cast<AssDialogue*>(*cur);
if (dlg && (dlg->Comment || dlg->Text.IsEmpty())) {
delete *cur;
Line->erase(cur);
for (std::list<AssEntry*>::iterator it = Line->begin(); it != Line->end(); ) {
AssDialogue *diag = dynamic_cast<AssDialogue*>(*it);
if (!diag || (!diag->Comment && diag->Text.size()))
++it;
else {
delete *it;
Line->erase(it++);
}
}
}
/// @brief 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 (!dynamic_cast<AssDialogue*>(*cur)) {
delete *cur;
Line->erase(cur);
for (std::list<AssEntry*>::iterator it = Line->begin(); it != Line->end(); ) {
if (dynamic_cast<AssDialogue*>(*it))
++it;
else {
delete *it;
Line->erase(it++);
}
}
}
/// @brief Helper function for RecombineOverlaps()
/// @param list
/// @param next
/// @param newdlg
///
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 = dynamic_cast<AssDialogue*>(*insertpos);
if (candidate && candidate->Start >= newdlg->Start) {
list.insert(insertpos, newdlg);
inserted = true;
break;
}
insertpos++;
}
if (!inserted) {
list.push_back(newdlg);
}
static bool dialog_start_lt(AssEntry *pos, AssDialogue *to_insert) {
AssDialogue *diag = dynamic_cast<AssDialogue*>(pos);
return diag && diag->Start > to_insert->Start;
}
/// @brief Split and merge lines so there are no overlapping lines
///
/// Algorithm described at http://devel.aegisub.org/wiki/Technical/SplitMerge
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;
std::list<AssEntry*>::iterator cur, next = Line->begin();
cur = next++;
for (; next != Line->end(); cur = next++) {
AssDialogue *prevdlg = dynamic_cast<AssDialogue*>(*cur);
AssDialogue *curdlg = dynamic_cast<AssDialogue*>(*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 = dynamic_cast<AssDialogue*>(prevdlg->Clone());
newdlg->Start = prevdlg->Start;
newdlg->End = curdlg->Start;
newdlg->Text = prevdlg->Text;
InsertLineSortedIntoList(*Line, next, newdlg);
}
// Overlapping A+B part
{
AssDialogue *newdlg = dynamic_cast<AssDialogue*>(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 + "\\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 = dynamic_cast<AssDialogue*>(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 = dynamic_cast<AssDialogue*>(prevdlg->Clone());
newdlg->Start = prevdlg->End;
newdlg->End = curdlg->End;
newdlg->Text = curdlg->Text;
InsertLineSortedIntoList(*Line, next, newdlg);
}
next--;
if (!curdlg || !prevdlg) continue;
if (prevdlg->End <= curdlg->Start) continue;
// Use names like in the algorithm description and prepare for erasing
// old dialogues from the list
std::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 = dynamic_cast<AssDialogue*>(prevdlg->Clone());
newdlg->Start = prevdlg->Start;
newdlg->End = curdlg->Start;
newdlg->Text = prevdlg->Text;
Line->insert(find_if(next, Line->end(), bind(dialog_start_lt, _1, newdlg)), newdlg);
}
// Overlapping A+B part
{
AssDialogue *newdlg = dynamic_cast<AssDialogue*>(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 + "\\N" + prevdlg->Text;
Line->insert(find_if(next, Line->end(), bind(dialog_start_lt, _1, newdlg)), newdlg);
}
// Is there an A part after the overlap?
if (prevdlg->End > curdlg->End) {
// Produce new entry with correct values
AssDialogue *newdlg = dynamic_cast<AssDialogue*>(prevdlg->Clone());
newdlg->Start = curdlg->End;
newdlg->End = prevdlg->End;
newdlg->Text = prevdlg->Text;
Line->insert(find_if(next, Line->end(), bind(dialog_start_lt, _1, newdlg)), newdlg);
}
// Is there a B part after the overlap?
if (curdlg->End > prevdlg->End) {
// Produce new entry with correct values
AssDialogue *newdlg = dynamic_cast<AssDialogue*>(prevdlg->Clone());
newdlg->Start = prevdlg->End;
newdlg->End = curdlg->End;
newdlg->Text = curdlg->Text;
Line->insert(find_if(next, Line->end(), bind(dialog_start_lt, _1, newdlg)), newdlg);
}
next--;
}
}
/// @brief Merge identical lines that follow each other
///
void SubtitleFormat::MergeIdentical() {
using std::list;
list<AssEntry*>::iterator next;
std::list<AssEntry*>::iterator cur, next = Line->begin();
cur = next++;
for (list<AssEntry*>::iterator cur = Line->begin(); cur != Line->end(); cur = next) {
next = cur;
next++;
if (next == Line->end()) break;
for (; next != Line->end(); cur = next++) {
AssDialogue *curdlg = dynamic_cast<AssDialogue*>(*cur);
AssDialogue *nextdlg = dynamic_cast<AssDialogue*>(*next);
if (curdlg && nextdlg && curdlg->End == nextdlg->Start && 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);
nextdlg->Start = std::min(nextdlg->Start, curdlg->Start);
nextdlg->End = std::max(nextdlg->End, curdlg->End);
// Remove duplicate line
delete *cur;
@ -556,3 +339,66 @@ void SubtitleFormat::MergeIdentical() {
}
}
}
std::list<SubtitleFormat*> SubtitleFormat::formats;
void SubtitleFormat::LoadFormats() {
if (formats.empty()) {
new ASSSubtitleFormat();
new EncoreSubtitleFormat();
new MKVSubtitleFormat();
new MicroDVDSubtitleFormat();
new SRTSubtitleFormat();
new TTXTSubtitleFormat();
new TXTSubtitleFormat();
new TranStationSubtitleFormat();
#ifdef _DEBUG
new DVDSubtitleFormat();
#endif
}
}
void SubtitleFormat::DestroyFormats() {
for (std::list<SubtitleFormat*>::iterator it = formats.begin(); it != formats.end(); )
delete *it++;
}
template<class Cont, class Pred>
SubtitleFormat *find_or_null(Cont &container, Pred pred) {
typename Cont::iterator it = find_if(container.begin(), container.end(), pred);
if (it == container.end())
return 0;
return *it;
}
SubtitleFormat *SubtitleFormat::GetReader(wxString const& filename) {
LoadFormats();
return find_or_null(formats, bind(&SubtitleFormat::CanReadFile, _1, filename));
}
SubtitleFormat *SubtitleFormat::GetWriter(wxString const& filename) {
LoadFormats();
return find_or_null(formats, bind(&SubtitleFormat::CanWriteFile, _1, filename));
}
wxString SubtitleFormat::GetWildcards(int mode) {
LoadFormats();
wxArrayString all;
wxString final;
std::list<SubtitleFormat*>::iterator curIter;
for (curIter=formats.begin();curIter!=formats.end();curIter++) {
SubtitleFormat *format = *curIter;
wxArrayString cur = mode == 0 ? format->GetReadWildcards() : format->GetWriteWildcards();
if (cur.empty()) continue;
for_each(cur.begin(), cur.end(), bind(&wxString::Prepend, _1, "*."));
copy(cur.begin(), cur.end(), std::back_inserter(all));
final += "|" + format->GetName() + " (" + wxJoin(cur, ',') + ")|" + wxJoin(cur, ';');
}
final.Prepend(_("All Supported Formats") + " (" + wxJoin(all, ',') + ")|" + wxJoin(all, ';'));
return final;
}

View file

@ -54,96 +54,113 @@ class AssEntry;
///
/// DOCME
class SubtitleFormat {
/// DOCME
wxString name;
bool isCopy;
/// DOCME
AssFile *assFile;
void Register();
void Remove();
/// Get this format's wildcards for a load dialog
virtual wxArrayString GetReadWildcards() const { return wxArrayString(); }
/// Get this format's wildcards for a save dialog
virtual wxArrayString GetWriteWildcards() const { return wxArrayString(); }
/// DOCME
/// List of loaded subtitle formats
static std::list<SubtitleFormat*> formats;
/// DOCME
static bool loaded;
protected:
/// DOCME
struct FPSRational {
/// DOCME
int num;
/// DOCME
int den;
/// DOCME
bool smpte_dropframe;
};
/// DOCME
std::list<AssEntry*> *Line;
/// Copy the input subtitles file; must be called before making any changes
void CreateCopy();
/// Delete the subtitle file if we own it; should be called after processing
/// if CreateCopy was used
void ClearCopy();
/// Sort the lines by start time
void SortLines();
void ConvertTags(int format,const wxString &lineEnd,bool mergeLineBreaks=true);
//void Merge(bool identical,bool overlaps,bool stripComments,bool stripNonDialogue);
/// Strip tags or convert them to SRT
/// @param format 1: strip tags 2: SRT
/// @param lineEnd Newline character(s)
/// @param mergeLineBreaks Should multiple consecutive line breaks be merged into one?
void ConvertTags(int format, const wxString &lineEnd, bool mergeLineBreaks=true);
/// Remove All commented and empty lines
void StripComments();
/// Remove everything but the dialogue lines
void StripNonDialogue();
/// @brief Split and merge lines so there are no overlapping lines
///
/// Algorithm described at http://devel.aegisub.org/wiki/Technical/SplitMerge
void RecombineOverlaps();
/// Merge sequential identical lines
void MergeIdentical();
/// Clear the subtitle file
void Clear();
/// Load the default file
/// @param defline Add a blank line?
void LoadDefault(bool defline=true);
/// @brief DOCME
/// @return
///
AssFile *GetAssFile() { return assFile; }
/// Add a line to the output file
/// @param data Line data
/// @param group File section
void AddLine(wxString data,wxString group,int &version,wxString *outgroup=NULL);
/// Prompt the user for a framerate to use
/// @param showSMPTE Include SMPTE as an option?
FPSRational AskForFPS(bool showSMPTE=false);
virtual wxString GetName()=0;
virtual wxArrayString GetReadWildcards();
virtual wxArrayString GetWriteWildcards();
public:
SubtitleFormat();
/// Constructor
/// @param Subtitle format name
/// @note Automatically registers the format
SubtitleFormat(wxString const& name);
/// Destructor
/// @note Automatically unregisters the format
virtual ~SubtitleFormat();
/// Set the target file to write
void SetTarget(AssFile *file);
/// Get this format's name
wxString GetName() const { return name; }
/// @brief Check if the given file can be read by this format
///
/// Default implemention simply checks if the file's extension is in the
/// format's wildcard list
virtual bool CanReadFile(wxString const& filename) const;
/// @brief Check if the given file can be written by this format
///
/// Default implemention simply checks if the file's extension is in the
/// format's wildcard list
virtual bool CanWriteFile(wxString const& filename) const;
/// Load a subtitle file
/// @param filename File to load
/// @param forceEncoding Encoding to use or empty string for default
virtual void ReadFile(wxString const& filename, wxString const& forceEncoding="") { }
/// Save a subtitle file
/// @param filename File to write to
/// @param forceEncoding Encoding to use or empty string for default
virtual void WriteFile(wxString const& filename, wxString const& encoding="") { }
/// Get the wildcards for a save or load dialog
/// @param mode 0: load 1: save
static wxString GetWildcards(int mode);
/// @brief DOCME
/// @param filename
/// @return
///
virtual bool CanReadFile(wxString filename) { return false; };
/// @brief DOCME
/// @param filename
/// @return
///
virtual bool CanWriteFile(wxString filename) { return false; };
/// @brief DOCME
/// @param filename
/// @param forceEncoding
///
virtual void ReadFile(wxString filename,wxString forceEncoding="") { };
/// @brief DOCME
/// @param filename
/// @param encoding
///
virtual void WriteFile(wxString filename,wxString encoding="") { };
static SubtitleFormat *GetReader(wxString filename);
static SubtitleFormat *GetWriter(wxString filename);
/// Get a subtitle format that can read the given file or NULL if none can
static SubtitleFormat *GetReader(wxString const& filename);
/// Get a subtitle format that can write the given file or NULL if none can
static SubtitleFormat *GetWriter(wxString const& filename);
/// Initialize subtitle formats
static void LoadFormats();
/// Deinitialize subtitle formats
static void DestroyFormats();
};

View file

@ -34,148 +34,92 @@
/// @ingroup subtitle_io
///
///////////
// Headers
#include "config.h"
#include "ass_dialogue.h"
#include "subtitle_format_ass.h"
#include "ass_dialogue.h"
#include "compat.h"
#include "text_file_reader.h"
#include "text_file_writer.h"
DEFINE_SIMPLE_EXCEPTION(AssParseError, SubtitleFormatParseError, "subtitle_io/parse/ass")
/// @brief Can read?
/// @param filename
/// @return
///
bool ASSSubtitleFormat::CanReadFile(wxString filename) {
return (filename.Right(4).Lower() == ".ass" || filename.Right(4).Lower() == ".ssa");
ASSSubtitleFormat::ASSSubtitleFormat()
: SubtitleFormat("Advanced Substation Alpha")
{
}
/// @brief Get name
/// @return
///
wxString ASSSubtitleFormat::GetName() {
return "Advanced Substation Alpha";
}
/// @brief Get read wildcards
/// @return
///
wxArrayString ASSSubtitleFormat::GetReadWildcards() {
wxArrayString ASSSubtitleFormat::GetReadWildcards() const {
wxArrayString formats;
formats.Add("ass");
formats.Add("ssa");
return formats;
}
/// @brief Get write wildcards
/// @return
///
wxArrayString ASSSubtitleFormat::GetWriteWildcards() {
wxArrayString ASSSubtitleFormat::GetWriteWildcards() const {
wxArrayString formats;
formats.Add("ass");
formats.Add("ssa");
return formats;
}
/// @brief Read file
/// @param filename
/// @param encoding
///
void ASSSubtitleFormat::ReadFile(wxString filename,wxString encoding) {
void ASSSubtitleFormat::ReadFile(wxString const& filename, wxString const& encoding) {
using namespace std;
// Reader
TextFileReader file(filename,encoding);
int version = 1;
if (filename.Right(4).Lower() == ".ssa") version = 0;
TextFileReader file(filename, encoding);
int version = filename.Right(4).Lower() != ".ssa";
// Parse file
wxString curgroup;
wxString wxbuffer;
while (file.HasMoreLines()) {
// Reads line
wxbuffer = file.ReadLineFromFile();
wxString line = file.ReadLineFromFile();
// Make sure that the first non-blank non-comment non-group-header line
// is really [Script Info]
if (curgroup.IsEmpty() && !wxbuffer.IsEmpty() && wxbuffer[0] != ';' && wxbuffer[0] != '[') {
if (curgroup.empty() && !line.empty() && line[0] != ';' && line[0] != '[') {
curgroup = "[Script Info]";
AddLine(curgroup,curgroup,version,&curgroup);
AddLine(curgroup, curgroup, version, &curgroup);
}
// Convert v4 styles to v4+ styles
if (!wxbuffer.IsEmpty() && wxbuffer[0] == '[') {
if (!line.empty() && line[0] == '[') {
// Ugly hacks to allow intermixed v4 and v4+ style sections
wxString low = wxbuffer.Lower();
wxString low = line.Lower();
if (low == "[v4 styles]") {
wxbuffer = "[V4+ Styles]";
curgroup = wxbuffer;
line = "[V4+ Styles]";
curgroup = line;
version = 0;
}
else if (low == "[v4+ styles]") {
wxbuffer = "[V4+ Styles]";
curgroup = wxbuffer;
line = "[V4+ Styles]";
curgroup = line;
version = 1;
}
else if (low == "[v4++ styles]") {
wxbuffer = "[V4+ Styles]";
curgroup = wxbuffer;
line = "[V4+ Styles]";
curgroup = line;
version = 2;
}
// Not-so-special case for other groups, just set it
else {
curgroup = wxbuffer;
curgroup = line;
}
}
// Add line
try {
AddLine(wxbuffer,curgroup,version,&curgroup);
AddLine(line, curgroup, version, &curgroup);
}
catch (const char *err) {
Clear();
throw wxString("Error processing line: ") + wxbuffer + ": " + wxString(err);
}
catch (...) {
Clear();
throw wxString("Error processing line: ") + wxbuffer;
throw AssParseError("Error processing line: " + STD_STR(line) + ": " + err, 0);
}
}
}
/// @brief Can write to file?
/// @param filename
/// @return
///
bool ASSSubtitleFormat::CanWriteFile(wxString filename) {
return (filename.Right(4).Lower() == ".ass" || filename.Right(4).Lower() == ".ssa");
}
/// @brief Write file
/// @param _filename
/// @param encoding
///
void ASSSubtitleFormat::WriteFile(wxString filename,wxString encoding) {
// Open file
TextFileWriter file(filename,encoding);
void ASSSubtitleFormat::WriteFile(wxString const& filename, wxString const& encoding) {
TextFileWriter file(filename, encoding);
bool ssa = filename.Right(4).Lower() == ".ssa";
// Write lines
std::list<AssEntry*>::iterator last = Line->end(); --last;
wxString group = Line->front()->group;
for (std::list<AssEntry*>::iterator cur=Line->begin(); cur!=Line->end(); ++cur) {
@ -189,9 +133,7 @@ void ASSSubtitleFormat::WriteFile(wxString filename,wxString encoding) {
bool lineBreak = cur != last;
// Write line
if (ssa) file.WriteLineToFile((*cur)->GetSSAText(),lineBreak);
else file.WriteLineToFile((*cur)->GetEntryData(),lineBreak);
if (ssa) file.WriteLineToFile((*cur)->GetSSAText(), lineBreak);
else file.WriteLineToFile((*cur)->GetEntryData(), lineBreak);
}
}

View file

@ -34,20 +34,8 @@
/// @ingroup subtitle_io
///
///////////
// Headers
#include "subtitle_format.h"
//////////////
// Prototypes
class AssDialogue;
/// DOCME
/// @class ASSSubtitleFormat
/// @brief DOCME
@ -55,15 +43,11 @@ class AssDialogue;
/// DOCME
class ASSSubtitleFormat : public SubtitleFormat {
public:
wxString GetName();
wxArrayString GetReadWildcards();
wxArrayString GetWriteWildcards();
ASSSubtitleFormat();
bool CanReadFile(wxString filename);
void ReadFile(wxString filename,wxString forceEncoding);
wxArrayString GetReadWildcards() const;
wxArrayString GetWriteWildcards() const;
bool CanWriteFile(wxString filename);
void WriteFile(wxString filename,wxString encoding);
void ReadFile(wxString const& filename, wxString const& forceEncoding);
void WriteFile(wxString const& filename, wxString const& encoding);
};

View file

@ -34,19 +34,16 @@
/// @ingroup subtitle_io vobsub
///
///////////
// Headers
#include "config.h"
#include "subtitle_format_dvd.h"
#include "ass_dialogue.h"
#include "ass_file.h"
#include "subtitle_format_dvd.h"
#include "include/aegisub/subtitles_provider.h"
#include "video_provider_dummy.h"
/// DOCME
#undef _OPENMP
#ifdef _OPENMP
#include <omp.h>
@ -58,41 +55,17 @@
//
//#pragma comment(lib, "tessdll.lib")
/// @brief Format name
/// @return
///
wxString DVDSubtitleFormat::GetName() {
return "DVD Subpictures";
DVDSubtitleFormat::DVDSubtitleFormat()
: SubtitleFormat("DVD Subpictures")
{
}
/// @brief Extensions
/// @return
///
wxArrayString DVDSubtitleFormat::GetWriteWildcards() {
wxArrayString DVDSubtitleFormat::GetWriteWildcards() const {
wxArrayString results;
results.Add("sup");
return results;
}
/// @brief Can write
/// @param filename
/// @return
///
bool DVDSubtitleFormat::CanWriteFile(wxString filename) {
return (filename.Lower().EndsWith(".sup"));
}
/// @brief Get subpicture list
/// @param pics
///
void DVDSubtitleFormat::GetSubPictureList(std::vector<SubPicture> &pics) {
// Create video frame
int w = 720;
@ -361,7 +334,7 @@ void DVDSubtitleFormat::GetSubPictureList(std::vector<SubPicture> &pics) {
/// @param filename
/// @param encoding
///
void DVDSubtitleFormat::WriteFile(wxString filename,wxString encoding) {
void DVDSubtitleFormat::WriteFile(wxString const& filename, wxString const& encoding) {
// Prepare subtitles
CreateCopy();
SortLines();

View file

@ -34,18 +34,12 @@
/// @ingroup subtitle_io vobsub
///
///////////
// Headers
#ifndef AGI_PRE
#include <vector>
#endif
#include "subtitle_format.h"
/// DOCME
struct SubPicture {
@ -55,17 +49,17 @@ struct SubPicture {
/// DOCME
/// DOCME
int x,y;
int x, y;
/// DOCME
/// DOCME
int w,h;
int w, h;
/// DOCME
/// DOCME
int start,end;
int start, end;
};
@ -81,12 +75,7 @@ struct RLEGroup {
/// DOCME
bool eol;
/// @brief DOCME
/// @param _col
/// @param _len
/// @param _eol
///
RLEGroup(int _col,int _len,bool _eol) { col = _col; len = _len; eol = _eol; }
RLEGroup(int col, int len, bool eol) : col(col), len(len), eol(eol) { }
};
@ -97,14 +86,10 @@ struct RLEGroup {
///
/// DOCME
class DVDSubtitleFormat : public SubtitleFormat {
private:
void GetSubPictureList(std::vector<SubPicture> &pics);
public:
wxString GetName();
wxArrayString GetWriteWildcards();
bool CanWriteFile(wxString filename);
void WriteFile(wxString filename,wxString encoding);
DVDSubtitleFormat();
wxArrayString GetWriteWildcards() const;
void WriteFile(wxString const& filename, wxString const& encoding);
};

View file

@ -34,57 +34,29 @@
/// @ingroup subtitle_io
///
///////////
// Headers
#include "config.h"
#include "ass_dialogue.h"
#include "subtitle_format_encore.h"
#include "ass_dialogue.h"
#include "text_file_writer.h"
/// @brief Name
/// @return
///
wxString EncoreSubtitleFormat::GetName() {
return "Adobe Encore";
EncoreSubtitleFormat::EncoreSubtitleFormat()
: SubtitleFormat("Adobe Encore")
{
}
/// @brief Wildcards
/// @return
///
wxArrayString EncoreSubtitleFormat::GetWriteWildcards() {
wxArrayString EncoreSubtitleFormat::GetWriteWildcards() const {
wxArrayString formats;
formats.Add("encore.txt");
return formats;
}
/// @brief Can write file?
/// @param filename
/// @return
///
bool EncoreSubtitleFormat::CanWriteFile(wxString filename) {
return (filename.Right(11).Lower() == ".encore.txt");
}
/// @brief Write file
/// @param _filename
/// @param encoding
///
void EncoreSubtitleFormat::WriteFile(wxString _filename,wxString encoding) {
// Get FPS
void EncoreSubtitleFormat::WriteFile(wxString const& filename, wxString const& encoding) {
FPSRational fps_rat = AskForFPS(true);
if (fps_rat.num <= 0 || fps_rat.den <= 0) return;
// Open file
TextFileWriter file(_filename,encoding);
TextFileWriter file(filename, encoding);
// Convert to encore
CreateCopy();
@ -92,25 +64,20 @@ void EncoreSubtitleFormat::WriteFile(wxString _filename,wxString encoding) {
StripComments();
RecombineOverlaps();
MergeIdentical();
ConvertTags(1,"\r\n");
ConvertTags(1, "\r\n");
// Write lines
using std::list;
int i = 0;
// Encore wants ; instead of : if we're dealing with NTSC dropframe stuff
FractionalTime ft(fps_rat.smpte_dropframe ? ";" : ":", fps_rat.num, fps_rat.den, fps_rat.smpte_dropframe);
for (list<AssEntry*>::iterator cur=Line->begin();cur!=Line->end();cur++) {
AssDialogue *current = dynamic_cast<AssDialogue*>(*cur);
if (current && !current->Comment) {
for (std::list<AssEntry*>::iterator cur=Line->begin();cur!=Line->end();cur++) {
if (AssDialogue *current = dynamic_cast<AssDialogue*>(*cur)) {
++i;
file.WriteLineToFile(wxString::Format("%i %s %s %s", i, ft.FromAssTime(current->Start), ft.FromAssTime(current->End), current->Text));
}
}
// Clean up
ClearCopy();
}

View file

@ -34,11 +34,6 @@
/// @ingroup subtitle_io
///
///////////
// Headers
#include "subtitle_format.h"
@ -49,10 +44,7 @@
/// DOCME
class EncoreSubtitleFormat : public SubtitleFormat {
public:
wxString GetName();
wxArrayString GetWriteWildcards();
bool CanWriteFile(wxString filename);
void WriteFile(wxString filename,wxString encoding);
EncoreSubtitleFormat();
wxArrayString GetWriteWildcards() const;
void WriteFile(wxString const& filename, wxString const& encoding);
};

View file

@ -47,41 +47,38 @@
#include "text_file_writer.h"
#include "video_context.h"
wxString MicroDVDSubtitleFormat::GetName() {
return "MicroDVD";
MicroDVDSubtitleFormat::MicroDVDSubtitleFormat()
: SubtitleFormat("MicroDVD")
{
}
wxArrayString MicroDVDSubtitleFormat::GetReadWildcards() {
wxArrayString MicroDVDSubtitleFormat::GetReadWildcards() const {
wxArrayString formats;
formats.Add("sub");
return formats;
}
wxArrayString MicroDVDSubtitleFormat::GetWriteWildcards() {
wxArrayString MicroDVDSubtitleFormat::GetWriteWildcards() const {
return GetReadWildcards();
}
bool MicroDVDSubtitleFormat::CanReadFile(wxString filename) {
bool MicroDVDSubtitleFormat::CanReadFile(wxString const& filename) const {
// Return false immediately if extension is wrong
if (filename.Right(4).Lower() != ".sub") return false;
// Since there is an infinity of .sub formats, load first line and check if it's valid
TextFileReader file(filename);
if (file.HasMoreLines()) {
wxRegEx exp("^[\\{\\[]([0-9]+)[\\}\\]][\\{\\[]([0-9]+)[\\}\\]](.*)$",wxRE_ADVANCED);
wxRegEx exp("^[\\{\\[]([0-9]+)[\\}\\]][\\{\\[]([0-9]+)[\\}\\]](.*)$", wxRE_ADVANCED);
return exp.Matches(file.ReadLineFromFile());
}
return false;
}
bool MicroDVDSubtitleFormat::CanWriteFile(wxString filename) {
return (filename.Right(4).Lower() == ".sub");
}
void MicroDVDSubtitleFormat::ReadFile(wxString filename,wxString forceEncoding) {
void MicroDVDSubtitleFormat::ReadFile(wxString const& filename, wxString const& forceEncoding) {
TextFileReader file(filename);
wxRegEx exp("^[\\{\\[]([0-9]+)[\\}\\]][\\{\\[]([0-9]+)[\\}\\]](.*)$",wxRE_ADVANCED);
wxRegEx exp("^[\\{\\[]([0-9]+)[\\}\\]][\\{\\[]([0-9]+)[\\}\\]](.*)$", wxRE_ADVANCED);
LoadDefault(false);
@ -94,10 +91,10 @@ void MicroDVDSubtitleFormat::ReadFile(wxString filename,wxString forceEncoding)
while (file.HasMoreLines()) {
wxString line = file.ReadLineFromFile();
if (exp.Matches(line)) {
long f1,f2;
exp.GetMatch(line,1).ToLong(&f1);
exp.GetMatch(line,2).ToLong(&f2);
wxString text = exp.GetMatch(line,3);
long f1, f2;
exp.GetMatch(line, 1).ToLong(&