diff --git a/aegisub/src/Makefile b/aegisub/src/Makefile index 657618fcb..0d3647b32 100644 --- a/aegisub/src/Makefile +++ b/aegisub/src/Makefile @@ -98,8 +98,7 @@ audio_player.o: CXXFLAGS += $(CFLAGS_ALSA) $(CFLAGS_PORTAUDIO) $( audio_provider.o: CXXFLAGS += $(CFLAGS_FFMS2) auto4_base.o: CXXFLAGS += $(CFLAGS_FREETYPE) charset_detect.o: CXXFLAGS += -D_X86_ -font_file_lister_fontconfig.o: CXXFLAGS += $(CFLAGS_FONTCONFIG) $(CFLAGS_FREETYPE) -font_file_lister.o: CXXFLAGS += $(CFLAGS_FREETYPE) +font_file_lister_fontconfig.o: CXXFLAGS += $(CFLAGS_FONTCONFIG) text_file_reader.o: CXXFLAGS += -D_X86_ video_provider_manager.o: CXXFLAGS += $(CFLAGS_FFMS2) diff --git a/aegisub/src/dialog_fonts_collector.cpp b/aegisub/src/dialog_fonts_collector.cpp index 7d116d501..7ce185090 100644 --- a/aegisub/src/dialog_fonts_collector.cpp +++ b/aegisub/src/dialog_fonts_collector.cpp @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2012, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. // -// * 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. +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // // Aegisub Project http://www.aegisub.org/ // @@ -34,653 +21,357 @@ /// @ingroup tools_ui font_collector /// - -//////////// -// Includes #include "config.h" -#ifndef AGI_PRE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#endif - -#include "ass_dialogue.h" -#include "ass_file.h" -#include "ass_override.h" -#include "ass_style.h" -#include "compat.h" #include "dialog_fonts_collector.h" + #include "font_file_lister.h" +#include "font_file_lister_fontconfig.h" +#include "font_file_lister_freetype.h" + +#include "ass_file.h" +#include "compat.h" #include "help_button.h" #include "libresrc/libresrc.h" #include "main.h" #include "scintilla_text_ctrl.h" #include "selection_controller.h" +#include "standard_paths.h" #include "utils.h" +#include -/// DOCME -enum IDs { +#ifndef AGI_PRE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif - /// DOCME - START_BUTTON = 1150, - - /// DOCME - BROWSE_BUTTON, - - /// DOCME - RADIO_BOX +enum FcMode { + CheckFontsOnly = 0, + CopyToFolder = 1, + CopyToScriptFolder = 2, + CopyToZip = 3 }; +wxDEFINE_EVENT(EVT_ADD_TEXT, wxThreadEvent); +wxDEFINE_EVENT(EVT_COLLECTION_DONE, wxThreadEvent); -///////// -// Event -DECLARE_EVENT_TYPE(EVT_ADD_TEXT, -1) -DEFINE_EVENT_TYPE(EVT_ADD_TEXT) +/// @class FontsCollectorThread +/// @brief Worker thread for the font collector dialog +class FontsCollectorThread : public wxThread { + AssFile *subs; ///< Subtitle file to process + wxString destination; ///< Path to write fonts to for modes 2 and 3 + wxEvtHandler *collector; ///< Parent dialog + FcMode oper; ///< Copying mode + void Collect() { + using namespace std::tr1::placeholders; + FontCollectorStatusCallback callback(bind(&FontsCollectorThread::AppendText, this, _1, _2)); + +#ifdef WITH_FONTCONFIG + FontConfigFontFileLister lister(callback); +#elif defined(WITH_FREETYPE2) + FreetypeFontFileLister lister(callback); +#else + AppendText(_("Aegisub was built without any font file listers enabled"), 2); + struct DummyLister : public FontFileLister { + std::vector GetFontPaths(wxString const&, int, bool) { return std::vector(); } + } lister; +#endif + std::vector paths = FontCollector(callback, lister).GetFontPaths(subs->Line); + if (paths.empty()) return; + + // Copy fonts + switch (oper) { + case CheckFontsOnly: return; + case CopyToScriptFolder: + case CopyToFolder: AppendText(_("Copying fonts to folder...\n")); break; + case CopyToZip: AppendText(_("Copying fonts to archive...\n")); break; + } + + // Open zip stream if saving to compressed archive + agi::scoped_ptr out; + agi::scoped_ptr zip; + if (oper == CopyToZip) { + out.reset(new wxFFileOutputStream(destination)); + zip.reset(new wxZipOutputStream(*out)); + } + + bool allOk = true; + for (std::vector::iterator cur = paths.begin(); cur != paths.end(); ++cur) { + int ret = 0; + switch (oper) { + case CopyToScriptFolder: + case CopyToFolder: { + wxString dest = destination + wxFileName(*cur).GetFullName(); + if (wxFileName::FileExists(dest)) + ret = 2; + else + ret = wxCopyFile(*cur, dest, true); + } + break; + case CopyToZip: { + wxFFileInputStream in(*cur); + if (!in.IsOk()) + ret = false; + else { + ret = zip->PutNextEntry(wxFileName(*cur).GetFullName()); + zip->Write(in); + } + } + default: break; + } + + if (ret == 1) + AppendText(wxString::Format(_("* Copied %s.\n"), *cur), 1); + else if (ret == 2) + AppendText(wxString::Format(_("* %s already exists on destination.\n"), wxFileName(*cur).GetFullName()), 3); + else { + AppendText(wxString::Format(_("* Failed to copy %s.\n"), *cur), 2); + allOk = false; + } + } + + if (allOk) + AppendText(_("Done. All fonts copied."), 1); + else + AppendText(_("Done. Some fonts could not be copied."), 2); + } + + /// @brief Tell the dialog to add text to the textbox + /// @param text Text to add + /// @param colour 0: neutral; 1: success; 2: error + void AppendText(wxString text, int colour=0) { + wxThreadEvent event(EVT_ADD_TEXT); + event.SetPayload(std::make_pair(colour, text)); + collector->AddPendingEvent(event); + } + + /// Do the font collection + wxThread::ExitCode Entry() { + Collect(); + collector->AddPendingEvent(wxThreadEvent(EVT_COLLECTION_DONE)); + return 0; + } +public: + FontsCollectorThread(AssFile *subs, wxString destination, FcMode oper, wxEvtHandler *collector) + : wxThread(wxTHREAD_DETACHED) + , subs(subs) + , destination(destination) + , collector(collector) + , oper(oper) + { + Create(); + Run(); + } +}; -/// @brief Constructor -/// @param parent -/// DialogFontsCollector::DialogFontsCollector(wxWindow *parent, AssFile *ass) -: wxDialog(parent,-1,_("Fonts Collector"),wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE) +: wxDialog(parent, -1, _("Fonts Collector")) , subs(ass) { - // Set icon SetIcon(BitmapToIcon(GETIMAGE(font_collector_button_24))); - // Destination box - wxString dest = lagi_wxString(OPT_GET("Path/Fonts Collector Destination")->GetString()); - if (dest == "?script") { - wxFileName filename(subs->filename); - dest = filename.GetPath(); - } - while (dest.Right(1) == "/") dest = dest.Left(dest.Length()-1); - 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); + wxString modes[] = { + _("Check fonts for availability"), + _("Copy fonts to folder"), + _("Copy fonts to subtitle file's folder"), + _("Copy fonts to zipped archive") + }; + collection_mode = new wxRadioBox(this, -1, "Action", wxDefaultPosition, wxDefaultSize, 4, modes, 1); + collection_mode->SetSelection(mid(0, OPT_GET("Tool/Fonts Collector/Action")->GetInt(), 3)); - // Action radio box - wxArrayString choices; - choices.Add(_("Check fonts for availability")); - choices.Add(_("Copy fonts to folder")); - choices.Add(_("Copy fonts to zipped archive")); - choices.Add(_("Attach fonts to current subtitles")); -#ifdef _DEBUG - choices.Add(_("DEBUG: Verify all fonts in system")); -#endif - CollectAction = new wxRadioBox(this,RADIO_BOX, "Action",wxDefaultPosition,wxDefaultSize,choices,1); - size_t lastAction = OPT_GET("Tool/Fonts Collector/Action")->GetInt(); - if (lastAction >= choices.GetCount()) lastAction = 0; - CollectAction->SetSelection(lastAction); + if (!ass->filename) + collection_mode->Enable(2, false); - // Log box - LogBox = new ScintillaTextCtrl(this,-1,"",wxDefaultPosition,wxSize(300,210)); - 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); + wxStaticBoxSizer *destination_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Destination")); - // Buttons sizer - StartButton = new wxButton(this,START_BUTTON,_("&Start!")); - CloseButton = new wxButton(this,wxID_CANCEL); - StartButton->SetDefault(); - wxStdDialogButtonSizer *ButtonSizer = new wxStdDialogButtonSizer(); - ButtonSizer->AddButton(StartButton); - ButtonSizer->AddButton(CloseButton); - ButtonSizer->AddButton(new HelpButton(this,"Fonts Collector")); - ButtonSizer->SetAffirmativeButton(StartButton); - ButtonSizer->Realize(); + dest_label = new wxStaticText(destination_box->GetStaticBox(), -1, "\n"); + dest_ctrl = new wxTextCtrl(destination_box->GetStaticBox(), -1, StandardPaths::DecodePath(lagi_wxString(OPT_GET("Path/Fonts Collector Destination")->GetString()))); + dest_browse_button = new wxButton(destination_box->GetStaticBox(), -1, _("&Browse...")); - // 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); + wxSizer *dest_browse_sizer = new wxBoxSizer(wxHORIZONTAL); + dest_browse_sizer->Add(dest_ctrl, wxSizerFlags(1).Border(wxRIGHT)); + dest_browse_sizer->Add(dest_browse_button, wxSizerFlags()); - // Set sizer - SetSizer(MainSizer); - MainSizer->SetSizeHints(this); + destination_box->Add(dest_label, wxSizerFlags().Border(wxBOTTOM)); + destination_box->Add(dest_browse_sizer, wxSizerFlags().Expand()); + + wxStaticBoxSizer *log_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Log")); + collection_log = new ScintillaTextCtrl(log_box->GetStaticBox(), -1, "", wxDefaultPosition, wxSize(300, 210)); + collection_log->SetWrapMode(wxSTC_WRAP_WORD); + collection_log->SetMarginWidth(1, 0); + collection_log->SetReadOnly(true); + collection_log->StyleSetForeground(1, wxColour(0, 200, 0)); + collection_log->StyleSetForeground(2, wxColour(200, 0, 0)); + collection_log->StyleSetForeground(3, wxColour(200, 100, 0)); + log_box->Add(collection_log, wxSizerFlags().Border()); + + wxStdDialogButtonSizer *button_sizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP); + start_btn = button_sizer->GetAffirmativeButton(); + close_btn = button_sizer->GetCancelButton(); + start_btn->SetLabel(_("&Start!")); + start_btn->SetDefault(); + + wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL); + main_sizer->Add(collection_mode, wxSizerFlags().Expand().Border()); + main_sizer->Add(destination_box, wxSizerFlags().Expand().Border(wxALL & ~wxTOP)); + main_sizer->Add(log_box, wxSizerFlags().Border(wxALL & ~wxTOP)); + main_sizer->Add(button_sizer, wxSizerFlags().Right().Border(wxALL & ~wxTOP)); + + SetSizerAndFit(main_sizer); CenterOnParent(); - // Run dummy event to update label + // Update the browse button and label wxCommandEvent evt; - evt.SetInt(-1); OnRadio(evt); + + start_btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &DialogFontsCollector::OnStart, this); + dest_browse_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &DialogFontsCollector::OnBrowse, this); + collection_mode->Bind(wxEVT_COMMAND_RADIOBOX_SELECTED, &DialogFontsCollector::OnRadio, this); + button_sizer->GetHelpButton()->Bind(wxEVT_COMMAND_BUTTON_CLICKED, std::tr1::bind(&HelpButton::OpenPage, "Fonts Collector")); + Bind(EVT_ADD_TEXT, &DialogFontsCollector::OnAddText, this); + Bind(EVT_COLLECTION_DONE, &DialogFontsCollector::OnCollectionComplete, this); } +void DialogFontsCollector::OnStart(wxCommandEvent &) { + collection_log->SetReadOnly(false); + collection_log->ClearAll(); + collection_log->SetReadOnly(true); + wxString dest; + int action = collection_mode->GetSelection(); + OPT_SET("Tool/Fonts Collector/Action")->SetInt(action); + if (action != CheckFontsOnly) { + if (action == CopyToScriptFolder) + dest = "?script/"; + else + dest = dest_ctrl->GetValue(); + dest = StandardPaths::DecodePath(dest); + wxFileName folder = dest; -/// @brief Destructor -/// -DialogFontsCollector::~DialogFontsCollector() { - FontFileLister::ClearData(); -} + if (action != CopyToZip) { + if (dest.Last() != '/' && dest.Last() != '\\') { + dest += wxFileName::GetPathSeparator(); + folder = dest; + } - -/////////////// -// 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) - EVT_COMMAND(0,EVT_ADD_TEXT,DialogFontsCollector::OnAddText) -END_EVENT_TABLE() - - - -/// @brief Start processing -/// @param event -/// @return -/// -void DialogFontsCollector::OnStart(wxCommandEvent &event) { - // Clear - LogBox->SetReadOnly(false); - LogBox->ClearAll(); - LogBox->SetReadOnly(true); - - // 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.FileExists()) + wxMessageBox(_("Invalid destination."), _("Error"), wxICON_EXCLAMATION | wxOK); + if (!folder.DirExists()) + folder.Mkdir(0777, wxPATH_MKDIR_FULL); if (!folder.DirExists()) { - wxMessageBox(_("Could not create destination folder."),_("Error"),wxICON_EXCLAMATION | wxOK); + 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); + else if (folder.IsDir() || folder.GetName().empty()) { + wxMessageBox(_("Invalid path for .zip file."), _("Error"), wxICON_EXCLAMATION | wxOK); return; } } - // Start thread - wxThread *worker = new FontsCollectorThread(subs,foldername,this); - worker->Create(); - worker->Run(); - - // Set options - if (action == 1 || action == 2) { - wxString dest = foldername; - wxFileName filename(subs->filename); - if (filename.GetPath() == dest) { - dest = _T("?script"); - } + if (action != CheckFontsOnly) OPT_SET("Path/Fonts Collector Destination")->SetString(STD_STR(dest)); - } - OPT_SET("Tool/Fonts Collector/Action")->SetInt(action); - // 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(); + // Disable the UI while it runs as we don't support canceling + EnableCloseButton(false); + start_btn->Enable(false); + dest_browse_button->Enable(false); + dest_ctrl->Enable(false); + close_btn->Enable(false); + collection_mode->Enable(false); + dest_label->Enable(false); + + new FontsCollectorThread(subs, dest, static_cast(action), GetEventHandler()); } +void DialogFontsCollector::OnBrowse(wxCommandEvent &) { + wxString dest; + if (collection_mode->GetSelection() == CopyToZip) { + dest = wxFileSelector( + _("Select archive file name"), + dest_ctrl->GetValue(), + wxFileName(dest_ctrl->GetValue()).GetFullName(), + ".zip", _("Zip Archives (*.zip)|*.zip"), + wxFD_SAVE|wxFD_OVERWRITE_PROMPT); + } + else + dest = wxDirSelector(_("Select folder to save fonts on"), dest_ctrl->GetValue(), 0); - -/// @brief Close dialog -/// @param event -/// -void DialogFontsCollector::OnClose(wxCommandEvent &event) { - EndModal(0); + if (!dest.empty()) + dest_ctrl->SetValue(dest); } +void DialogFontsCollector::OnRadio(wxCommandEvent &) { + int value = collection_mode->GetSelection(); + wxString dst = dest_ctrl->GetValue(); - -/// @brief Browse location -/// @param event -/// -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(),".zip",_("Zip Archives (*.zip)|*.zip"),wxFD_SAVE|wxFD_OVERWRITE_PROMPT); - if (!dest.empty()) { - DestBox->SetValue(dest); - } + if (value == CheckFontsOnly || value == CopyToScriptFolder) { + dest_ctrl->Enable(false); + dest_browse_button->Enable(false); + dest_label->Enable(false); + dest_label->SetLabel(_("N/A")); } - - // Choose folder else { - wxString dest = wxDirSelector(_("Select folder to save fonts on"),DestBox->GetValue(),0); - if (!dest.empty()) { - DestBox->SetValue(dest); - } - } -} + dest_ctrl->Enable(true); + dest_browse_button->Enable(true); + dest_label->Enable(true); + if (value == CopyToFolder) { + dest_label->SetLabel(_("Choose the folder where the fonts will be collected to.\nIt will be created if it doesn't exist.")); - -/// @brief Radio box changed -/// @param event -/// -void DialogFontsCollector::OnRadio(wxCommandEvent &event) { - int value = event.GetInt(); - - // 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("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) == ".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) != ".zip") { - wxFileName fn(dst + "//"); - fn.SetFullName("fonts.zip"); - DestBox->SetValue(fn.GetFullPath()); - } - } -} - - - -/// @brief Add text -/// @param event -/// -void DialogFontsCollector::OnAddText(wxCommandEvent &event) { - ColourString *str = (ColourString*) event.GetClientData(); - LogBox->SetReadOnly(false); - int pos = LogBox->GetReverseUnicodePosition(LogBox->GetLength()); - LogBox->AppendText(str->text); - if (str->colour) { - LogBox->StartUnicodeStyling(pos,31); - LogBox->SetUnicodeStyling(pos,str->text.Length(),str->colour); - } - delete str; - LogBox->GotoPos(pos); - LogBox->SetReadOnly(true); -} - - - -/// @brief Collect font files -/// -void FontsCollectorThread::CollectFontData () { - FontFileLister::Initialize(); -} - - - -/// @brief Collector thread -/// @param _subs -/// @param _destination -/// @param _collector -/// -FontsCollectorThread::FontsCollectorThread(AssFile *_subs,wxString _destination,DialogFontsCollector *_collector) -: wxThread(wxTHREAD_DETACHED) -{ - subs = _subs; - destination = _destination; - collector = _collector; - instance = this; -} - - - -/// @brief Thread entry -/// @return -/// -wxThread::ExitCode FontsCollectorThread::Entry() { - try { - Collect(); - } - catch (...) { - collector->Update(); - if (IsDetached() && TestDestroy()) Delete(); - throw; - } - - // After done, restore status - collector->Update(); - - // Return - if (IsDetached() && TestDestroy()) Delete(); - return 0; -} - - - -/// @brief Collect -/// @return -/// -void FontsCollectorThread::Collect() { - // 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 - if (collector->CollectAction->GetSelection() != 4) { - AssDialogue *curDiag; - curLine = 0; - for (std::list::iterator cur=subs->Line.begin();cur!=subs->Line.end();cur++) { - // Collect from style - curStyle = dynamic_cast(*cur); - if (curStyle) { - AddFont(curStyle->font,0); - } - - // Collect from dialogue - else { - curDiag = dynamic_cast(*cur); - if (curDiag) { - curLine++; - curDiag->ParseASSTags(); - curDiag->ProcessParameters(GetFonts); - curDiag->ClearBlocks(); - } - } - } - } - - // For maitenance, gather all on system - else { - wxArrayString fonts = wxFontEnumerator::GetFacenames(); - for (size_t i=0;iClose(); - 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); + // Remove filename from browse box + if (dst.Right(4) == ".zip") + dest_ctrl->SetValue(wxFileName(dst).GetPath()); } else { - AppendText(_("Done. All fonts copied."),1); + dest_label->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.")); - // Modify file if it was attaching - if (oper == 3 && someOk) { - wxMutexGuiEnter(); - subs->Commit(_("font attachment"), AssFile::COMMIT_ATTACHMENT); - wxMutexGuiLeave(); + // Add filename to browse box + if (!dst.EndsWith(".zip")) { + wxFileName fn(dst + "//"); + fn.SetFullName("fonts.zip"); + dest_ctrl->SetValue(fn.GetFullPath()); } } } - else { - if (oper == 0) AppendText(_("Done. Some fonts could not be found."),2); - else AppendText(_("Done. Some fonts could not be copied."),2); - } } - - -/// @brief Process font -/// @param name -/// @return -/// -bool FontsCollectorThread::ProcessFont(wxString name) { - // Action - int action = collector->CollectAction->GetSelection(); - - // Font name - AppendText(wxString::Format("\"%s\"... ", name)); - - // 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; +void DialogFontsCollector::OnAddText(wxThreadEvent &event) { + std::pair str = event.GetPayload >(); + collection_log->SetReadOnly(false); + int pos = collection_log->GetReverseUnicodePosition(collection_log->GetLength()); + collection_log->AppendText(str.second); + if (str.first) { + collection_log->StartUnicodeStyling(pos, 31); + collection_log->SetUnicodeStyling(pos, str.second.size(), str.first); } - - // Just checking, found - else if (action == 0 || action == 4) { - AppendText(_("Found.\n"),1); - return true; - } - - // Copy font - AppendText(_T("\n")); - for (size_t i=0;iGotoPos(pos); + collection_log->SetReadOnly(true); } +void DialogFontsCollector::OnCollectionComplete(wxThreadEvent &) { + EnableCloseButton(); + start_btn->Enable(); + close_btn->Enable(); + collection_mode->Enable(); + if (!subs->filename) + collection_mode->Enable(2, false); - -/// @brief Copy font -/// @param filename -/// @return -/// -int FontsCollectorThread::CopyFont(wxString filename) { - wxFileName fn(filename); - wxString dstName = destFolder + _T("//") + fn.GetFullName(); - if (wxFileName::FileExists(dstName)) return 2; - return wxCopyFile(filename,dstName,true) ? 1 : 0; + wxCommandEvent evt; + OnRadio(evt); } - - - -/// @brief Archive font -/// @param filename -/// @return -/// -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; -} - - - -/// @brief Attach font -/// @param filename -/// @return -/// -bool FontsCollectorThread::AttachFont(wxString filename) { - try { - subs->InsertAttachment(filename); - } - catch (...) { - return false; - } - return true; -} - - - -/// @brief Get fonts from ass overrides -/// @param tagName -/// @param par_n -/// @param param -/// @param usr -/// -void FontsCollectorThread::GetFonts (wxString tagName,int par_n,AssOverrideParameter *param,void *usr) { - if (tagName == _T("\\fn")) { - if (instance) instance->AddFont(param->Get(),1); - } -} - - - -/// @brief Adds a font -/// @param fontname -/// @param mode -/// -void FontsCollectorThread::AddFont(wxString fontname,int mode) { - // @-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 (mode == 0) AppendText(wxString::Format(_("\"%s\" found on style \"%s\".\n"), fontname, curStyle->name)); - else if (mode == 1) AppendText(wxString::Format(_("\"%s\" found on dialogue line \"%d\".\n"), fontname, curLine)); - else AppendText(wxString::Format(_("\"%s\" found.\n"), fontname)); - } -} - - - -/// @brief Append text -/// @param text -/// @param colour -/// -void FontsCollectorThread::AppendText(wxString text,int colour) { - ColourString *str = new ColourString; - str->text = text; - str->colour = colour; - wxCommandEvent event(EVT_ADD_TEXT,0); - event.SetClientData(str); - collector->AddPendingEvent(event); -} - - - -/// DOCME -FontsCollectorThread *FontsCollectorThread::instance; - - diff --git a/aegisub/src/dialog_fonts_collector.h b/aegisub/src/dialog_fonts_collector.h index d86196ab5..0b1cf0698 100644 --- a/aegisub/src/dialog_fonts_collector.h +++ b/aegisub/src/dialog_fonts_collector.h @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2012, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. // -// * 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. +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // // Aegisub Project http://www.aegisub.org/ // @@ -35,72 +22,16 @@ /// #ifndef AGI_PRE -#include #include -#include -#include -#include -#include #endif class AssFile; -class AssStyle; -class AssOverrideParameter; -class DialogFontsCollector; -class wxZipOutputStream; class ScintillaTextCtrl; - -/// DOCME -/// @class FontsCollectorThread -/// @brief DOCME -/// -/// DOCME -class FontsCollectorThread : public wxThread { - /// DOCME - AssFile *subs; - - /// DOCME - AssStyle *curStyle; - - /// DOCME - wxString destination; - - /// DOCME - DialogFontsCollector *collector; - - /// DOCME - wxZipOutputStream *zip; - - /// DOCME - int curLine; - - /// DOCME - wxString destFolder; - - - /// DOCME - static FontsCollectorThread *instance; - - - /// DOCME - wxArrayString fonts; - - bool ProcessFont(wxString fontname); - int CopyFont(wxString filename); - bool ArchiveFont(wxString filename); - bool AttachFont(wxString filename); - - void Collect(); - void AddFont(wxString fontname,int mode); - void CollectFontData(); - void AppendText(wxString text,int colour=0); - -public: - FontsCollectorThread(AssFile *subs,wxString destination,DialogFontsCollector *collector); - wxThread::ExitCode Entry(); - - static void GetFonts (wxString tagName,int par_n,AssOverrideParameter *param,void *usr); -}; +class wxButton; +class wxRadioBox; +class wxStaticText; +class wxTextCtrl; +class wxThreadEvent; /// DOCME /// @class DialogFontsCollector @@ -108,52 +39,25 @@ public: /// /// DOCME class DialogFontsCollector : public wxDialog { - friend class FontsCollectorThread; - AssFile *subs; - /// DOCME - wxTextCtrl *DestBox; + ScintillaTextCtrl *collection_log; + wxButton *close_btn; + wxButton *dest_browse_button; + wxButton *start_btn; + wxRadioBox *collection_mode; + wxStaticText *dest_label; + wxTextCtrl *dest_ctrl; - /// DOCME - ScintillaTextCtrl *LogBox; + void OnStart(wxCommandEvent &); + void OnBrowse(wxCommandEvent &); + void OnRadio(wxCommandEvent &e); - /// DOCME - wxButton *BrowseButton; - - /// DOCME - wxButton *StartButton; - - /// DOCME - wxButton *CloseButton; - - /// DOCME - wxStaticText *DestLabel; - - /// DOCME - wxRadioBox *CollectAction; - - void OnStart(wxCommandEvent &event); - void OnClose(wxCommandEvent &event); - void OnBrowse(wxCommandEvent &event); - void OnRadio(wxCommandEvent &event); - void OnAddText(wxCommandEvent &event); + /// Append text to log message from worker thread + void OnAddText(wxThreadEvent &event); + /// Collection complete notification from the worker thread to reenable buttons + void OnCollectionComplete(wxThreadEvent &); public: DialogFontsCollector(wxWindow *parent, AssFile *subs); - ~DialogFontsCollector(); - - DECLARE_EVENT_TABLE() -}; - - - -/// DOCME -struct ColourString { - - /// DOCME - wxString text; - - /// DOCME - int colour; }; diff --git a/aegisub/src/font_file_lister.cpp b/aegisub/src/font_file_lister.cpp index 11bacd27d..971264093 100644 --- a/aegisub/src/font_file_lister.cpp +++ b/aegisub/src/font_file_lister.cpp @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2012, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. // -// * 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. +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // // Aegisub Project http://www.aegisub.org/ // @@ -34,201 +21,130 @@ /// @ingroup font_collector /// - -//////////// -// Includes - #include "config.h" -#ifndef AGI_PRE -#include -#endif +#include "ass_dialogue.h" +#include "ass_override.h" +#include "ass_style.h" -#ifdef WITH_FONTCONFIG #include "font_file_lister_fontconfig.h" -#define FontListerClass FontConfigFontFileLister -#elif defined(WITH_FREETYPE2) -#include "font_file_lister_freetype.h" -#define FontListerClass FreetypeFontFileLister -#else -#include "font_file_lister.h" + +#ifndef AGI_PRE +#include + +#include #endif -#include "standard_paths.h" -#include "text_file_reader.h" -#include "text_file_writer.h" +using namespace std::tr1::placeholders; -/// DOCME -FontFileLister *FontFileLister::instance = NULL; - -/// @brief Constructor -/// -FontFileLister::FontFileLister() { +FontCollector::FontCollector(FontCollectorStatusCallback status_callback, FontFileLister &lister) +: status_callback(status_callback) +, lister(lister) +{ } +void FontCollector::ProcessDialogueLine(AssDialogue *line) { + if (line->Comment) return; + line->ParseASSTags(); + bool text = false; + StyleInfo style = styles[line->Style]; + StyleInfo initial = style; -/// @brief Destructor -/// -FontFileLister::~FontFileLister() { -} - - - -/// @brief Get instance -/// -void FontFileLister::GetInstance() { -#ifdef FontListerClass - if (!instance) instance = new FontListerClass(); -#endif -} - - - -/// @brief Redirect statics to the instance -/// @param facename -/// @return -/// -wxArrayString FontFileLister::GetFilesWithFace(wxString facename) { - GetInstance(); - if (instance) - return instance->DoGetFilesWithFace(facename); - else { - wxArrayString ret; - return ret; - } -} - -/// @brief DOCME -/// -void FontFileLister::Initialize() { - GetInstance(); - if (instance) instance->DoInitialize(); -} - -/// @brief DOCME -/// -void FontFileLister::ClearData() { - GetInstance(); - if (instance) instance->DoClearData(); -} - - - -/// @brief Get list of files that match a specific face -/// @param facename -/// @return -/// -wxArrayString FontFileLister::CacheGetFilesWithFace(wxString facename) { - FontMap::iterator iter = fontTable.find(facename); - if (iter != fontTable.end()) return iter->second; - else { - iter = fontTable.find("*"+facename); - if (iter != fontTable.end()) return iter->second; - return wxArrayString(); - } -} - - - -/// @brief Clear data -/// -void FontFileLister::ClearCache() { - fontFiles.clear(); - fontTable.clear(); -} - - - -/// @brief Add font -/// @param filename -/// @param facename -/// @return -/// -void FontFileLister::AddFont(wxString filename,wxString facename) { - // See if it's a valid facename - facename.Trim(true).Trim(false); - if (facename.IsEmpty()) return; - if (facename.Lower().StartsWith("copyright ")) return; - - // Add filename to general list - if (fontFiles.Index(filename) == wxNOT_FOUND) { - fontFiles.Add(filename); - } - - // Add filename to mapping of this face - wxArrayString &arr = fontTable[facename]; - if (arr.Index(filename) == wxNOT_FOUND) arr.Add(filename); -} - - - -/// @brief Check if a filename is cached -/// @param filename -/// @return -/// -bool FontFileLister::IsFilenameCached(wxString filename) { - return fontFiles.Index(filename) != wxNOT_FOUND; -} - - - -/// @brief Save cache -/// -void FontFileLister::SaveCache() { - try { - // Open file - TextFileWriter file(StandardPaths::DecodePath("?user/fontscache.dat")); - - // For each face... - for (FontMap::iterator iter = fontTable.begin();iter!=fontTable.end();iter++) { - // Write face name - wxString line = iter->first + "?"; - size_t len = iter->second.Count(); - - // Write file names - for (size_t i=0;isecond[i]; - if (i != len-1) line += "|"; + for (size_t i = 0; i < line->Blocks.size(); ++i) { + if (AssDialogueBlockOverride *ovr = dynamic_cast(line->Blocks[i])) { + if (text) { + used_styles.insert(style); + text = false; } - // Write line - file.WriteLineToFile(line); - } - } - catch (...) { - } -} + for (size_t j = 0; j < ovr->Tags.size(); ++j) { + AssOverrideTag *tag = ovr->Tags[j]; + wxString name = tag->Name; - - -/// @brief Load cache -/// -void FontFileLister::LoadCache() { - try { - // Load cache - TextFileReader file(StandardPaths::DecodePath("?user/fontscache.dat")); - - // Read each line - while (file.HasMoreLines()) { - // Read line - wxString line = file.ReadLineFromFile(); - int pos = line.Find('?'); - - // Get face name - wxString face = line.Left(pos); - if (face.IsEmpty()) continue; - - // Get files - wxStringTokenizer tkn(line.Mid(pos+1),"|"); - while (tkn.HasMoreTokens()) { - wxString file = tkn.GetNextToken(); - if (!file.IsEmpty()) { - AddFont(file,face); - } + if (name == "\\r") + style = styles[tag->Params[0]->Get(line->Style)]; + if (name == "\\b") + style.bold = tag->Params[0]->Get(initial.bold); + else if (name == "\\i") + style.italic = tag->Params[0]->Get(initial.italic); + else if (name == "\\fn") + style.facename = tag->Params[0]->Get(initial.facename); + else + continue; } } + else if (AssDialogueBlockPlain *txt = dynamic_cast(line->Blocks[i])) { + text = text || !txt->GetText().empty(); + } + // Do nothing with drawing blocks } - catch (...) { + if (text) + used_styles.insert(style); + line->ClearBlocks(); +} + +void FontCollector::ProcessChunk(StyleInfo const& style) { + std::vector paths = lister.GetFontPaths(style.facename, style.bold, style.italic); + + if (paths.empty()) { + status_callback(wxString::Format("Could not find font '%s'\n", style.facename), 2); + ++missing; + } + else { + for (size_t i = 0; i < paths.size(); ++i) { + if (results.insert(paths[i]).second) + status_callback(wxString::Format("Found '%s' at '%s'\n", style.facename, paths[i]), 0); + } } } + +std::vector FontCollector::GetFontPaths(std::list const& file) { + missing = 0; + + status_callback(_("Parsing file\n"), 0); + for (std::list::const_iterator cur = file.begin(); cur != file.end(); ++cur) { + if (AssStyle *style = dynamic_cast(*cur)) { + StyleInfo &info = styles[style->name]; + info.facename = style->font; + info.bold = style->bold; + info.italic = style->italic; + } + else if (AssDialogue *diag = dynamic_cast(*cur)) + ProcessDialogueLine(diag); + } + + if (used_styles.empty()) { + status_callback(_("No non-empty dialogue lines found"), 2); + return std::vector(); + } + + status_callback(_("Searching for font files\n"), 0); + for_each(used_styles.begin(), used_styles.end(), bind(&FontCollector::ProcessChunk, this, _1)); + status_callback(_("Done\n\n"), 0); + + std::vector paths; + paths.reserve(results.size()); + paths.insert(paths.end(), results.begin(), results.end()); + + if (missing == 0) + status_callback(_("All fonts found.\n"), 1); + else + status_callback(wxString::Format(_("%d fonts could not be found.\n"), missing), 2); + + return paths; +} + +bool FontCollector::StyleInfo::operator<(StyleInfo const& rgt) const { +#define CMP(field) \ + if (field < rgt.field) return true; \ + if (field > rgt.field) return false + + CMP(facename); + CMP(bold); + CMP(italic); + +#undef CMP + + return false; +} diff --git a/aegisub/src/font_file_lister.h b/aegisub/src/font_file_lister.h index e8823241b..c4d241e99 100644 --- a/aegisub/src/font_file_lister.h +++ b/aegisub/src/font_file_lister.h @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2012, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. // -// * 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. +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // // Aegisub Project http://www.aegisub.org/ // @@ -34,71 +21,73 @@ /// @ingroup font_collector /// +#pragma once -//////////// -// Includes #ifndef AGI_PRE +#include #include +#include +#include +#include -#include #include #endif -#ifdef WITH_FREETYPE2 -/// DOCME -typedef struct FT_LibraryRec_ *FT_Library; -#endif +class AssEntry; +class AssDialogue; -/// DOCME -typedef std::map FontMap; +typedef std::tr1::function FontCollectorStatusCallback; - - -/// DOCME /// @class FontFileLister -/// @brief DOCME -/// -/// DOCME +/// @brief Font lister interface class FontFileLister { -private: - - /// DOCME - static FontFileLister *instance; - static void GetInstance(); - - /// DOCME - FontMap fontTable; - - /// DOCME - wxArrayString fontFiles; - -protected: - - /// @brief DOCME - /// @param facename - /// @return - /// - virtual wxArrayString DoGetFilesWithFace(wxString facename) { return CacheGetFilesWithFace(facename); } - virtual void DoInitialize()=0; - - /// @brief DOCME - /// - virtual void DoClearData() { ClearCache(); } - - FontFileLister(); - virtual ~FontFileLister(); - - wxArrayString CacheGetFilesWithFace(wxString facename); - bool IsFilenameCached(wxString filename); - void AddFont(wxString filename,wxString facename); - void SaveCache(); - void LoadCache(); - void ClearCache(); - public: - static wxArrayString GetFilesWithFace(wxString facename); - static void Initialize(); - static void ClearData(); + /// @brief Get the path to the font with the given styles + /// @param facename Name of font face + /// @param bold ASS font weight + /// @param italic Italic? + /// @return Path to the matching font file(s), or empty if not found + virtual std::vector GetFontPaths(wxString const& facename, int bold, bool italic) = 0; }; +/// @class FontCollector +/// @brief Class which collects the paths to all fonts used in a script +class FontCollector { + struct StyleInfo { + wxString facename; + int bold; + bool italic; + bool operator<(StyleInfo const& rgt) const; + }; + /// Message callback provider by caller + FontCollectorStatusCallback status_callback; + /// The actual lister to use to get font paths + FontFileLister &lister; + + /// A set of each combination of styles used in the file + std::set used_styles; + /// Style name -> ASS style definition + std::map styles; + /// Paths to found required font files + std::set results; + /// Number of fonts which could not be found + int missing; + + /// Gather all of the unique styles with text on a line + void ProcessDialogueLine(AssDialogue *line); + /// Get the font for a single style + void ProcessChunk(StyleInfo const& style); + +public: + /// Constructor + /// @param status_callback Function to pass status updates to + /// @param lister The actual font file lister + FontCollector(FontCollectorStatusCallback status_callback, FontFileLister &lister); + + /// @brief Get a list of the locations of all font files used in the file + /// @param file Lines in the subtitle file to check + /// @param status Callback function for messages + /// @return List of paths to fonts + std::vector GetFontPaths(std::list const& file); +}; diff --git a/aegisub/src/font_file_lister_fontconfig.cpp b/aegisub/src/font_file_lister_fontconfig.cpp index 7995979b8..993307688 100644 --- a/aegisub/src/font_file_lister_fontconfig.cpp +++ b/aegisub/src/font_file_lister_fontconfig.cpp @@ -1,29 +1,16 @@ -// Copyright (c) 2007, David Lamparter, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2012, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. // -// * 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. +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // // Aegisub Project http://www.aegisub.org/ // @@ -38,73 +25,169 @@ #ifdef WITH_FONTCONFIG #include "font_file_lister_fontconfig.h" -#include "charset_conv.h" +#include +#include "compat.h" -/// @brief Get files that contain the face -/// @param facename -/// @return -/// -wxArrayString FontConfigFontFileLister::DoGetFilesWithFace(wxString facename) { - wxArrayString results; +#include - // Code stolen from asa - FcPattern *final, *tmp1, *tmp2; - FcResult res; - FcChar8 *filename,*gotfamily; - int fontindex; - char buffer[1024]; - strcpy(buffer,facename.mb_str(wxConvUTF8)); +#ifndef AGI_PRE +#include +#endif - // Get data from fconfig or something - tmp1 = FcPatternBuild(NULL,FC_FAMILY, FcTypeString,buffer,NULL); - if (!tmp1) return results; - tmp2 = FcFontRenderPrepare(fontconf, tmp1, aux); - FcPatternDestroy(tmp1); - FcDefaultSubstitute(tmp2); - FcConfigSubstitute(fontconf, tmp2, FcMatchPattern); - final = FcFontMatch(fontconf, tmp2, &res); - FcPatternDestroy(tmp2); - if (!final) return results; - if (FcPatternGetString(final, FC_FILE, 0, &filename) == FcResultMatch && FcPatternGetInteger(final, FC_INDEX, 0, &fontindex) == FcResultMatch) { - FcPatternGetString(final, FC_FAMILY, fontindex, &gotfamily); - if (strcmp((const char*)gotfamily,buffer) == 0) { - results.Add(wxString((char*) filename)); +namespace { + // In SSA/ASS fonts are sometimes referenced by their "full name", + // which is usually a concatenation of family name and font + // style (ex. Ottawa Bold). Full name is available from + // FontConfig pattern element FC_FULLNAME, but it is never + // used for font matching. + // Therefore, I'm removing words from the end of the name one + // by one, and adding shortened names to the pattern. It seems + // that the first value (full name in this case) has + // precedence in matching. + // An alternative approach could be to reimplement FcFontSort + // using FC_FULLNAME instead of FC_FAMILY. + int add_families(FcPattern *pat, std::string family) { + int family_cnt = 1; + FcPatternAddString(pat, FC_FAMILY, (const FcChar8 *)family.c_str()); + + for (std::string::reverse_iterator p = family.rbegin(); p != family.rend(); ++p) { + if (*p == ' ' || *p == '-') { + *p = '\0'; + FcPatternAddString(pat, FC_FAMILY, (const FcChar8 *)family.c_str()); + ++family_cnt; + } + } + return family_cnt; + } + + int strcasecmp(std::string a, std::string b) { + agi::util::str_lower(a); + agi::util::str_lower(b); + return a.compare(b); + } +} + +FontConfigFontFileLister::FontConfigFontFileLister(FontCollectorStatusCallback cb) +: config(FcInitLoadConfig(), FcConfigDestroy) +{ + cb(_("Updating font cache\n"), 0); + FcConfigBuildFonts(config); +} + +FcFontSet *FontConfigFontFileLister::MatchFullname(const char *family, int weight, int slant) { + FcFontSet *sets[2]; + FcFontSet *result = FcFontSetCreate(); + int nsets = 0; + + if ((sets[nsets] = FcConfigGetFonts(config, FcSetSystem))) + nsets++; + if ((sets[nsets] = FcConfigGetFonts(config, FcSetApplication))) + nsets++; + + // Run over font sets and patterns and try to match against full name + for (int i = 0; i < nsets; i++) { + FcFontSet *set = sets[i]; + for (int fi = 0; fi < set->nfont; fi++) { + FcPattern *pat = set->fonts[fi]; + char *fullname; + int pi = 0, at; + FcBool ol; + while (FcPatternGetString(pat, FC_FULLNAME, pi++, (FcChar8 **)&fullname) == FcResultMatch) { + if (FcPatternGetBool(pat, FC_OUTLINE, 0, &ol) != FcResultMatch || ol != FcTrue) + continue; + if (FcPatternGetInteger(pat, FC_SLANT, 0, &at) != FcResultMatch || at < slant) + continue; + if (FcPatternGetInteger(pat, FC_WEIGHT, 0, &at) != FcResultMatch || at < weight) + continue; + if (strcasecmp(fullname, family) == 0) { + FcFontSetAdd(result, FcPatternDuplicate(pat)); + break; + } + } } } - FcPatternDestroy(final); - return results; + return result; } +std::vector FontConfigFontFileLister::GetFontPaths(wxString const& facename, int bold, bool italic) { + std::vector ret; + std::string family = STD_STR(facename); + if (family[0] == '@') + family.erase(0, 1); -/// @brief Constructor -/// -FontConfigFontFileLister::FontConfigFontFileLister() -: fontconf(0), aux(0) -{ + int weight = bold == 0 ? 80 : + bold == 1 ? 200 : + bold; + int slant = italic ? 110 : 0; + + scoped pat(FcPatternCreate(), FcPatternDestroy); + if (!pat) return ret; + + int family_cnt = add_families(pat, family); + + FcPatternAddBool(pat, FC_OUTLINE, true); + FcPatternAddInteger(pat, FC_SLANT, slant); + FcPatternAddInteger(pat, FC_WEIGHT, weight); + + FcDefaultSubstitute(pat); + + if (!FcConfigSubstitute(config, pat, FcMatchPattern)) return ret; + + FcResult result; + scoped fsorted(FcFontSort(config, pat, true, NULL, &result), FcFontSetDestroy); + scoped ffullname(MatchFullname(family.c_str(), weight, slant), FcFontSetDestroy); + if (!fsorted || !ffullname) return ret; + + scoped fset(FcFontSetCreate(), FcFontSetDestroy); + for (int cur_font = 0; cur_font < ffullname->nfont; ++cur_font) { + FcPattern *curp = ffullname->fonts[cur_font]; + FcPatternReference(curp); + FcFontSetAdd(fset, curp); + } + for (int cur_font = 0; cur_font < fsorted->nfont; ++cur_font) { + FcPattern *curp = fsorted->fonts[cur_font]; + FcPatternReference(curp); + FcFontSetAdd(fset, curp); + } + + int cur_font; + for (cur_font = 0; cur_font < fset->nfont; ++cur_font) { + FcBool outline; + FcResult result = FcPatternGetBool(fset->fonts[cur_font], FC_OUTLINE, 0, &outline); + if (result == FcResultMatch && outline) break; + } + + if (cur_font >= fset->nfont) return ret; + + // Remove all extra family names from original pattern. + // After this, FcFontRenderPrepare will select the most relevant family + // name in case there are more than one of them. + for (; family_cnt > 1; --family_cnt) + FcPatternRemove(pat, FC_FAMILY, family_cnt - 1); + + scoped rpat(FcFontRenderPrepare(config, pat, fset->fonts[cur_font]), FcPatternDestroy); + if (!rpat) return ret; + + FcChar8 *r_family; + if (FcPatternGetString(rpat, FC_FAMILY, 0, &r_family) != FcResultMatch) + return ret; + + FcChar8 *r_fullname; + if (FcPatternGetString(rpat, FC_FULLNAME, 0, &r_fullname) != FcResultMatch) + return ret; + + if (strcasecmp(family, (char*)r_family) && strcasecmp(family, (char*)r_fullname)) + return ret; + + FcChar8 *file; + if(FcPatternGetString(rpat, FC_FILE, 0, &file) != FcResultMatch) + return ret; + + ret.push_back((const char *)file); + return ret; } - - - -/// @brief Initialize -/// -void FontConfigFontFileLister::DoInitialize() { - fontconf = FcInitLoadConfigAndFonts(); - aux = FcPatternCreate(); -} - - - -/// @brief Clean up -/// -void FontConfigFontFileLister::DoClearData() { - if (aux) FcPatternDestroy(aux); -#ifdef HAVE_FCFINI - FcFini(); -#endif -} - #endif diff --git a/aegisub/src/font_file_lister_fontconfig.h b/aegisub/src/font_file_lister_fontconfig.h index a0b2684bb..516ce5811 100644 --- a/aegisub/src/font_file_lister_fontconfig.h +++ b/aegisub/src/font_file_lister_fontconfig.h @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2012, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. // -// * 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. +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // // Aegisub Project http://www.aegisub.org/ // @@ -34,37 +21,40 @@ /// @ingroup font_collector /// - - - -//////////// -// Includes -#include -#include +#ifdef WITH_FONTCONFIG #include "font_file_lister.h" +typedef struct _FcConfig FcConfig; +typedef struct _FcFontSet FcFontSet; -/// DOCME /// @class FontConfigFontFileLister -/// @brief DOCME -/// -/// DOCME +/// @brief fontconfig powered font lister class FontConfigFontFileLister : public FontFileLister { - friend class FontFileLister; -private: + template class scoped { + T data; + void (*destructor)(T); + public: + scoped(T data, void (*destructor)(T)) : data(data), destructor(destructor) { } + ~scoped() { if (data) destructor(data); } + operator T() { return data; } + T operator->() { return data; } + }; - /// DOCME - FcConfig *fontconf; + scoped config; - /// DOCME - FcPattern *aux; - - FontConfigFontFileLister(); + /// @brief Case-insensitive match ASS/SSA font family against full name. (also known as "name for humans") + /// @param family font fullname + /// @param bold weight attribute + /// @param italic italic attribute + /// @return font set + FcFontSet *MatchFullname(const char *family, int weight, int slant); +public: + /// Constructor + /// @param cb Callback for status logging + FontConfigFontFileLister(FontCollectorStatusCallback cb); - wxArrayString DoGetFilesWithFace(wxString facename); - void DoInitialize(); - void DoClearData(); + std::vector GetFontPaths(wxString const& facename, int bold, bool italic); }; - +#endif diff --git a/aegisub/src/font_file_lister_freetype.cpp b/aegisub/src/font_file_lister_freetype.cpp index aa2c8206b..d10a6d527 100644 --- a/aegisub/src/font_file_lister_freetype.cpp +++ b/aegisub/src/font_file_lister_freetype.cpp @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Niels Martin Hansen, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2012, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. // -// * 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. +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // // Aegisub Project http://www.aegisub.org/ // @@ -34,154 +21,162 @@ /// @ingroup font_collector /// - -//////////// -// Includes - #include "config.h" #ifdef WITH_FREETYPE2 - -#ifndef AGI_PRE -#include -#endif +#include "font_file_lister_freetype.h" #include #include FT_FREETYPE_H #include FT_GLYPH_H #include FT_SFNT_NAMES_H +#include FT_TRUETYPE_IDS_H -#ifdef WIN32 #include -#endif #include "charset_conv.h" -#include "font_file_lister_freetype.h" +#include "standard_paths.h" +#include "text_file_reader.h" +#include "text_file_writer.h" +namespace { + typedef std::map > FontMap; -/// @brief Constructor -/// -FreetypeFontFileLister::FreetypeFontFileLister() { - // Initialize freetype2 - FT_Init_FreeType(&ft2lib); -} + wxString get_font_folder() { + wchar_t szPath[MAX_PATH]; + if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_FONTS, NULL, 0, szPath))) + return wxString(szPath) + "\\"; + else + return wxGetOSDirectory() + "\\fonts\\"; + } + FT_UInt get_name_count(wxString const& filename, FT_Face face) { + wxString ext = filename.Right(4).Lower(); + if (ext == ".otf" || ext == ".ttf" || ext == ".ttc_") + return FT_Get_Sfnt_Name_Count(face); + return 0; + } + std::map > get_names(FT_Face face) { + std::map > final; -/// @brief Destructor -/// -FreetypeFontFileLister::~FreetypeFontFileLister() { -} + FT_UInt count = FT_Get_Sfnt_Name_Count(face); + for (FT_UInt i = 0; i < count; ++i) { + FT_SfntName name; + FT_Get_Sfnt_Name(face, i, &name); - - -/// @brief Get name from face -/// @param face -/// @param id -/// @return -/// -wxArrayString GetName(FT_Face &face,int id) { - // Get name - wxArrayString final; - int count = FT_Get_Sfnt_Name_Count(face); - - for (int i=0;i &indexed_files) { + try { + TextFileReader file(StandardPaths::DecodePath("?local/freetype_collector_index.dat"), "utf-16le"); + while (file.HasMoreLines()) { + wxString face = file.ReadLineFromFile(); + std::set& files = font_files[face]; + + while (file.HasMoreLines()) { + wxString filename = file.ReadLineFromFile(); + if (filename.empty()) break; + if (wxFileExists(filename)) { + files.insert(filename); + indexed_files.insert(filename); + } + } + } + } + catch (agi::FileNotAccessibleError const&) { } + } + + void save_cache(FontMap &font_files) { + TextFileWriter file(StandardPaths::DecodePath("?local/freetype_collector_index.dat"), "utf-16le"); + for (FontMap::iterator face_it = font_files.begin(); face_it != font_files.end(); ++face_it) { + file.WriteLineToFile(face_it->first); + for_each(face_it->second.begin(), face_it->second.end(), + bind(&TextFileWriter::WriteLineToFile, &file, std::tr1::placeholders::_1, true)); + file.WriteLineToFile(""); } } - - return final; } +FreetypeFontFileLister::FreetypeFontFileLister(FontCollectorStatusCallback AppendText) { + 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"), 0); + load_cache(font_files, indexed_files); + FT_Library ft2lib; + FT_Init_FreeType(&ft2lib); -/// @brief Gather data from system -/// -void FreetypeFontFileLister::DoInitialize() { - // Load cache - LoadCache(); - - // Get fonts folder - wxString source; -#ifdef WIN32 - TCHAR szPath[MAX_PATH]; - if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_FONTS,NULL,0,szPath))) { - source = wxString(szPath); - } - else source = wxGetOSDirectory() + _T("\\fonts"); - source += _T("\\"); -#else -# ifdef __APPLE__ - // XXXHACK: Is this always a correct assumption? - // Fonts might be instaled in more places, I think... - source = _T("/Library/Fonts/"); -# endif -#endif - - // Get the list of fonts in the fonts folder wxArrayString fontfiles; - wxDir::GetAllFiles(source, &fontfiles, wxEmptyString, wxDIR_FILES); + wxDir::GetAllFiles(get_font_folder(), &fontfiles, "", wxDIR_FILES); - // Loop through each file - int fterr; - for (unsigned int i=0;i 0) { + std::map > names = get_names(face); + std::vector& family = names[1]; + std::vector& style = names[2]; + std::vector& full_name = names[4]; - // Special names for TTF and OTF - int nameCount = 0; - wxString ext = fontfiles[i].Right(4).Lower(); - if (ext == _T(".otf") || ext == _T(".ttf") || ext == _T(".ttc_")) nameCount = FT_Get_Sfnt_Name_Count(face); - if (nameCount > 0) { - wxArrayString family = GetName(face,1); - wxArrayString subFamily = GetName(face,2); - wxArrayString fullName = GetName(face,4); - for (size_t j=0;jstyle_name) { - AddFont(fontfiles[i],wxString(face->family_name, csConvLocal) + _T(" ") + wxString(face->style_name, csConvLocal)); - AddFont(fontfiles[i],_T("*")+wxString(face->family_name, csConvLocal)); - } - else AddFont(fontfiles[i],wxString(face->family_name, csConvLocal)); + if (face->style_name) + AddFont(fontfiles[i], face->family_name, face->style_name); + else + AddFont(fontfiles[i], wxString(face->family_name)); } FT_Done_Face(face); } } - // Save cache - SaveCache(); + FT_Done_FreeType(ft2lib); + + AppendText(_("Done collecting font data.\n"), 0); + + save_cache(font_files); +} + +void FreetypeFontFileLister::AddFont(wxString const& filename, wxString facename) { + facename.Trim(true).Trim(false); + if (facename.size() && !facename.Lower().StartsWith("copyright ")) + font_files[facename].insert(filename); +} + +void FreetypeFontFileLister::AddFont(wxString const& filename, wxString const& family, wxString const& style) { + AddFont(filename, family + " " + style); + AddFont(filename, "*" + family); +} + +std::vector FreetypeFontFileLister::GetFontPaths(wxString const& facename, int, bool) { + std::vector ret; + ret.insert(ret.end(), font_files[facename].begin(), font_files[facename].end()); + if (ret.empty()) + ret.insert(ret.end(), font_files["*" + facename].begin(), font_files["*" + facename].end()); + return ret; } #endif WITH_FREETYPE2 - - diff --git a/aegisub/src/font_file_lister_freetype.h b/aegisub/src/font_file_lister_freetype.h index 139539343..e651afffd 100644 --- a/aegisub/src/font_file_lister_freetype.h +++ b/aegisub/src/font_file_lister_freetype.h @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2012, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. // -// * 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. +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // // Aegisub Project http://www.aegisub.org/ // @@ -34,35 +21,43 @@ /// @ingroup font_collector /// +#ifdef WITH_FREETYPE2 - - -//////////// -// Includes #include "font_file_lister.h" +#ifndef AGI_PRE +#include +#include -/// DOCME -typedef struct FT_LibraryRec_ *FT_Library; +#include +#endif - - -/// DOCME /// @class FreetypeFontFileLister -/// @brief DOCME -/// -/// DOCME +/// @brief Freetype2-based font collector class FreetypeFontFileLister : public FontFileLister { - friend class FontFileLister; -private: + /// Map of face name -> possible containing files + std::map > font_files; - /// DOCME - FT_Library ft2lib; + /// Set of files which were loaded from the cache + std::set indexed_files; - void DoInitialize(); + /// Add a font to the list of mappings + /// @param filename Full path to font file + /// @param facename Font name + void AddFont(wxString const& filename, wxString facename); - FreetypeFontFileLister(); - ~FreetypeFontFileLister(); + /// Add a font to the list of mappings + /// @param filename Full path to font file + /// @param family Family name + /// @param style Style name + void AddFont(wxString const& filename, wxString const& family, wxString const& style); + +public: + /// Constructor + /// @param cb Callback for status logging + FreetypeFontFileLister(FontCollectorStatusCallback cb); + + std::vector GetFontPaths(wxString const& facename, int, bool); }; - +#endif