// Copyright (c) 2007, Rodrigo Braz Monteiro // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of the Aegisub Group nor the names of its contributors // may be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // // ----------------------------------------------------------------------------- // // AEGISUB // // Website: http://aegisub.cellosoft.com // Contact: mailto:zeratul@cellosoft.com // //////////// // Includes #include <wx/config.h> #include <wx/filename.h> #include <wx/wfstream.h> #include <wx/zipstrm.h> #include "ass_override.h" #include "ass_file.h" #include "ass_dialogue.h" #include "ass_style.h" #include "dialog_fonts_collector.h" #include "utils.h" #include "options.h" #include "frame_main.h" #include "subs_grid.h" #include "main.h" #include "font_file_lister.h" #include "utils.h" /////// // IDs enum IDs { START_BUTTON = 1150, BROWSE_BUTTON, RADIO_BOX }; /////////////// // Constructor DialogFontsCollector::DialogFontsCollector(wxWindow *parent) : wxDialog(parent,-1,_("Fonts Collector"),wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE) { // Set icon SetIcon(BitmapToIcon(wxBITMAP(font_collector_button))); // Parent main = (FrameMain*) parent; // Destination box wxString dest = Options.AsText(_T("Fonts Collector Destination")); if (dest == _T("?script")) { wxFileName filename(AssFile::top->filename); dest = filename.GetPath(); } DestBox = new wxTextCtrl(this,-1,dest,wxDefaultPosition,wxSize(250,20),0); BrowseButton = new wxButton(this,BROWSE_BUTTON,_("&Browse...")); wxSizer *DestBottomSizer = new wxBoxSizer(wxHORIZONTAL); DestLabel = new wxStaticText(this,-1,_("Choose the folder where the fonts will be collected to.\nIt will be created if it doesn't exist.")); DestBottomSizer->Add(DestBox,1,wxEXPAND | wxRIGHT,5); DestBottomSizer->Add(BrowseButton,0,0,0); wxSizer *DestSizer = new wxStaticBoxSizer(wxVERTICAL,this,_("Destination")); DestSizer->Add(DestLabel,0,wxEXPAND | wxBOTTOM,5); DestSizer->Add(DestBottomSizer,0,wxEXPAND,0); // Action radio box wxArrayString choices; choices.Add(_T("Check fonts for availability")); choices.Add(_T("Copy fonts to folder")); choices.Add(_T("Copy fonts to zipped archive")); choices.Add(_T("Attach fonts to current subtitles")); CollectAction = new wxRadioBox(this,RADIO_BOX,_T("Action"),wxDefaultPosition,wxDefaultSize,choices,1); CollectAction->SetSelection(Options.AsInt(_T("Fonts Collector Action"))); // Log box LogBox = new wxStyledTextCtrl(this,-1,wxDefaultPosition,wxSize(300,210),0,_T("")); LogBox->SetWrapMode(wxSTC_WRAP_WORD); LogBox->SetMarginWidth(1,0); LogBox->SetReadOnly(true); LogBox->StyleSetForeground(1,wxColour(0,200,0)); LogBox->StyleSetForeground(2,wxColour(200,0,0)); LogBox->StyleSetForeground(3,wxColour(200,100,0)); wxSizer *LogSizer = new wxStaticBoxSizer(wxVERTICAL,this,_("Log")); LogSizer->Add(LogBox,1,wxEXPAND,0); // Buttons sizer StartButton = new wxButton(this,START_BUTTON,_("&Start!")); StartButton->SetDefault(); CloseButton = new wxButton(this,wxID_CANCEL,_T("Close")); wxSizer *ButtonSizer = new wxBoxSizer(wxHORIZONTAL); ButtonSizer->AddStretchSpacer(1); #ifdef __WXMAC__ ButtonSizer->Add(CloseButton,0,wxRIGHT,5); ButtonSizer->Add(StartButton); #else ButtonSizer->Add(StartButton,0,wxRIGHT,5); ButtonSizer->Add(CloseButton); #endif // Main sizer wxSizer *MainSizer = new wxBoxSizer(wxVERTICAL); MainSizer->Add(CollectAction,0,wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM,5); MainSizer->Add(DestSizer,0,wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM,5); MainSizer->Add(LogSizer,0,wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM,5); MainSizer->Add(ButtonSizer,0,wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM,5); // Set sizer SetSizer(MainSizer); MainSizer->SetSizeHints(this); CenterOnParent(); // Run dummy event to update label Update(); } ////////////// // Destructor DialogFontsCollector::~DialogFontsCollector() { FontFileLister::ClearData(); } /////////////// // Event table BEGIN_EVENT_TABLE(DialogFontsCollector, wxDialog) EVT_BUTTON(START_BUTTON,DialogFontsCollector::OnStart) EVT_BUTTON(BROWSE_BUTTON,DialogFontsCollector::OnBrowse) EVT_BUTTON(wxID_CLOSE,DialogFontsCollector::OnClose) EVT_RADIOBOX(RADIO_BOX,DialogFontsCollector::OnRadio) END_EVENT_TABLE() //////////////////// // Start processing void DialogFontsCollector::OnStart(wxCommandEvent &event) { // Action being done int action = CollectAction->GetSelection(); // Check if it's OK to do it wxString foldername = DestBox->GetValue(); if (action == 1) foldername += _T("//"); wxFileName folder(foldername); bool isFolder = folder.IsDir(); // Check if it's a folder if (action == 1 && !isFolder) { wxMessageBox(_("Invalid destination."),_("Error"),wxICON_EXCLAMATION | wxOK); return; } // Make folder if it doesn't exist if (action == 1 || action == 2) { if (!folder.DirExists()) { folder.Mkdir(0777,wxPATH_MKDIR_FULL); if (!folder.DirExists()) { wxMessageBox(_("Could not create destination folder."),_("Error"),wxICON_EXCLAMATION | wxOK); return; } } } // Check if we have a valid archive name if (action == 2) { if (isFolder || folder.GetName().IsEmpty() || folder.GetExt() != _T("zip")) { wxMessageBox(_("Invalid path for .zip file."),_("Error"),wxICON_EXCLAMATION | wxOK); return; } } // Start thread wxThread *worker = new FontsCollectorThread(AssFile::top,foldername,this); worker->Create(); worker->Run(); // Set options if (action == 1 || action == 2) { wxString dest = foldername; wxFileName filename(AssFile::top->filename); if (filename.GetPath() == dest) { dest = _T("?script"); } Options.SetText(_T("Fonts Collector Destination"),dest); } Options.SetInt(_T("Fonts Collector Action"),action); Options.Save(); // Set buttons StartButton->Enable(false); BrowseButton->Enable(false); DestBox->Enable(false); CloseButton->Enable(false); CollectAction->Enable(false); DestLabel->Enable(false); if (!worker->IsDetached()) worker->Wait(); } //////////////// // Close dialog void DialogFontsCollector::OnClose(wxCommandEvent &event) { EndModal(0); } /////////////////// // Browse location void DialogFontsCollector::OnBrowse(wxCommandEvent &event) { // Chose file name if (CollectAction->GetSelection()==2) { wxFileName fname(DestBox->GetValue()); wxString dest = wxFileSelector(_("Select archive file name"),DestBox->GetValue(),fname.GetFullName(),_T(".zip"),_("Zip Archives (*.zip)|*.zip"),wxFD_SAVE|wxFD_OVERWRITE_PROMPT); if (!dest.empty()) { DestBox->SetValue(dest); } } // Choose folder else { wxString dest = wxDirSelector(_("Select folder to save fonts on"),DestBox->GetValue(),0); if (!dest.empty()) { DestBox->SetValue(dest); } } } ///////////////////// // Radio box changed void DialogFontsCollector::OnRadio(wxCommandEvent &event) { Update(event.GetInt()); } /////////////////// // Update controls void DialogFontsCollector::Update(int value) { // Enable buttons CloseButton->Enable(true); StartButton->Enable(true); CollectAction->Enable(true); wxString dst = DestBox->GetValue(); // Get value if -1 if (value == -1) { value = CollectAction->GetSelection(); } // Check or attach if (value == 0 || value == 3) { DestBox->Enable(false); BrowseButton->Enable(false); DestLabel->Enable(false); DestLabel->SetLabel(_T("N/A\n")); } // Collect to folder else if (value == 1) { DestBox->Enable(true); BrowseButton->Enable(true); DestLabel->Enable(true); DestLabel->SetLabel(_("Choose the folder where the fonts will be collected to.\nIt will be created if it doesn't exist.")); // Remove filename from browse box if (dst.Right(4) == _T(".zip")) { wxFileName fn(dst); DestBox->SetValue(fn.GetPath()); } } // Collect to zip else if (value == 2) { DestBox->Enable(true); BrowseButton->Enable(true); DestLabel->Enable(true); DestLabel->SetLabel(_("Enter the name of the destination zip file to collect the fonts to.\nIf a folder is entered, a default name will be used.")); // Add filename to browse box if (dst.Right(4) != _T(".zip")) { wxFileName fn(dst + _T("//")); fn.SetFullName(_T("fonts.zip")); DestBox->SetValue(fn.GetFullPath()); } } } /////////////////////// // Collect font files void FontsCollectorThread::CollectFontData () { FontFileLister::Initialize(); } //////////////////// // Collector thread FontsCollectorThread::FontsCollectorThread(AssFile *_subs,wxString _destination,DialogFontsCollector *_collector) : wxThread(wxTHREAD_DETACHED) { subs = _subs; destination = _destination; collector = _collector; instance = this; } //////////////// // Thread entry wxThread::ExitCode FontsCollectorThread::Entry() { // Collect Collect(); // After done, restore status collector->Update(); // Return if (IsDetached()) Delete(); return 0; } /////////// // Collect void FontsCollectorThread::Collect() { // Clear log box wxMutexGuiEnter(); collector->LogBox->SetReadOnly(false); collector->LogBox->ClearAll(); collector->LogBox->SetReadOnly(true); wxMutexGuiLeave(); // Set destination folder int oper = collector->CollectAction->GetSelection(); destFolder = collector->DestBox->GetValue(); if (oper == 1 && !wxFileName::DirExists(destFolder)) { AppendText(_("Invalid destination directory."),1); return; } // Open zip stream if saving to compressed archive wxFFileOutputStream *out = NULL; zip = NULL; if (oper == 2) { out = new wxFFileOutputStream(destFolder); zip = new wxZipOutputStream(*out); } // Collect font data AppendText(_("Collecting font data from system. This might take a while, depending on the number of fonts installed. Results are cached and subsequent executions will be faster...\n")); CollectFontData(); AppendText(_("Done collecting font data.")); AppendText(_("Scanning file for fonts...")); // Scan file AssDialogue *curDiag; curLine = 0; for (std::list<AssEntry*>::iterator cur=subs->Line.begin();cur!=subs->Line.end();cur++) { // Collect from style curStyle = AssEntry::GetAsStyle(*cur); if (curStyle) { AddFont(curStyle->font,true); } // Collect from dialogue else { curDiag = AssEntry::GetAsDialogue(*cur); if (curDiag) { curLine++; curDiag->ParseASSTags(); curDiag->ProcessParameters(GetFonts); curDiag->ClearBlocks(); } } } // Copy fonts AppendText(wxString(_("Done.")) + _T("\n\n")); switch (oper) { case 0: AppendText(_("Checking fonts...\n")); break; case 1: AppendText(_("Copying fonts to folder...\n")); break; case 2: AppendText(_("Copying fonts to archive...\n")); break; case 3: AppendText(_("Attaching fonts to file...\n")); break; } bool ok = true; bool someOk = false; for (size_t i=0;i<fonts.Count();i++) { bool result = ProcessFont(fonts[i]); if (result) someOk = true; if (!result) ok = false; } // Close ZIP archive if (oper == 2) { zip->Close(); delete zip; delete out; AppendText(wxString::Format(_("\nFinished writing to %s.\n"),destination.c_str()),1); } // Final result if (ok) { if (oper == 0) AppendText(_("Done. All fonts found."),1); else { AppendText(_("Done. All fonts copied."),1); // Modify file if it was attaching if (oper == 3 && someOk) { wxMutexGuiEnter(); subs->FlagAsModified(_("font attachment")); collector->main->SubsBox->CommitChanges(); wxMutexGuiLeave(); } } } else { if (oper == 0) AppendText(_("Done. Some fonts could not be found."),2); else AppendText(_("Done. Some fonts could not be copied."),2); } } //////////////// // Process font bool FontsCollectorThread::ProcessFont(wxString name) { // Action int action = collector->CollectAction->GetSelection(); // Font name AppendText(wxString::Format(_T("\"%s\"... "),name.c_str())); // Get font list wxArrayString files = FontFileLister::GetFilesWithFace(name); bool result = files.Count() != 0; // No files found if (!result) { AppendText(_("Not found.\n"),2); return false; } // Just checking, found else if (action == 0) { AppendText(_("Found.\n"),1); return true; } // Copy font AppendText(_T("\n")); for (size_t i=0;i<files.Count();i++) { int tempResult = 0; switch (action) { case 1: tempResult = CopyFont(files[i]); break; case 2: tempResult = ArchiveFont(files[i]) ? 1 : 0; break; case 3: tempResult = AttachFont(files[i]) ? 1 : 0; break; } if (tempResult == 1) { AppendText(wxString::Format(_("* Copied %s.\n"),files[i].c_str()),1); } else if (tempResult == 2) { wxFileName fn(files[i]); AppendText(wxString::Format(_("* %s already exists on destination.\n"),fn.GetFullName().c_str()),3); } else { AppendText(wxString::Format(_("* Failed to copy %s.\n"),files[i].c_str()),2); result = false; } } // Done return result; } ///////////// // Copy font int FontsCollectorThread::CopyFont(wxString filename) { wxFileName fn(filename); wxString dstName = destFolder + _T("//") + fn.GetFullName(); if (wxFileName::FileExists(dstName)) return 2; return CopyFile(filename,dstName) ? 1 : 0; } //////////////// // Archive font bool FontsCollectorThread::ArchiveFont(wxString filename) { // Open file wxFFileInputStream in(filename); if (!in.IsOk()) return false; // Write to archive try { wxFileName fn(filename); zip->PutNextEntry(fn.GetFullName()); zip->Write(in); } catch (...) { return false; } return true; } /////////////// // Attach font bool FontsCollectorThread::AttachFont(wxString filename) { try { subs->InsertAttachment(filename); } catch (...) { return false; } return true; } //////////////////////////////// // Get fonts from ass overrides void FontsCollectorThread::GetFonts (wxString tagName,int par_n,AssOverrideParameter *param,void *usr) { if (tagName == _T("\\fn")) { instance->AddFont(param->AsText(),false); } } /////////////// // Adds a font void FontsCollectorThread::AddFont(wxString fontname,bool isStyle) { // @-fonts (CJK vertical layout variations) should be listed as the non-@ name if (fontname.StartsWith(_T("@"), 0)) fontname.Remove(0, 1); if (fonts.Index(fontname) == wxNOT_FOUND) { fonts.Add(fontname); if (isStyle) AppendText(wxString::Format(_("\"%s\" found on style \"%s\".\n"), fontname.c_str(), curStyle->name.c_str())); if (!isStyle) AppendText(wxString::Format(_("\"%s\" found on dialogue line \"%d\".\n"), fontname.c_str(), curLine)); } } /////////////// // Append text void FontsCollectorThread::AppendText(wxString text,int colour) { wxStyledTextCtrl *LogBox = collector->LogBox; wxMutexGuiEnter(); LogBox->SetReadOnly(false); int pos = LogBox->GetLength(); LogBox->AppendText(text); if (colour) { LogBox->StartStyling(pos,31); LogBox->SetStyling(text.Length(),colour); } LogBox->GotoPos(pos); LogBox->SetReadOnly(true); wxSafeYield(); wxMutexGuiLeave(); } /////////////////// // Static instance FontsCollectorThread *FontsCollectorThread::instance;