mirror of https://github.com/odrling/Aegisub
Rewrite the font collector
The fontconfig collector should now always pick the exact font files used by libass rather than a giant mishmash of vaguely related files which may or may not include the correct font. Make the freetype font collector windows-only, as it's far inferior to the fontconfig collector and is only present as a fallback. Add option to copy the fonts to the script's folder. Closes #1059. Originally committed to SVN as r6279.
This commit is contained in:
parent
7dd6cfe37d
commit
988ade0c00
|
@ -98,8 +98,7 @@ audio_player.o: CXXFLAGS += $(CFLAGS_ALSA) $(CFLAGS_PORTAUDIO) $(
|
||||||
audio_provider.o: CXXFLAGS += $(CFLAGS_FFMS2)
|
audio_provider.o: CXXFLAGS += $(CFLAGS_FFMS2)
|
||||||
auto4_base.o: CXXFLAGS += $(CFLAGS_FREETYPE)
|
auto4_base.o: CXXFLAGS += $(CFLAGS_FREETYPE)
|
||||||
charset_detect.o: CXXFLAGS += -D_X86_
|
charset_detect.o: CXXFLAGS += -D_X86_
|
||||||
font_file_lister_fontconfig.o: CXXFLAGS += $(CFLAGS_FONTCONFIG) $(CFLAGS_FREETYPE)
|
font_file_lister_fontconfig.o: CXXFLAGS += $(CFLAGS_FONTCONFIG)
|
||||||
font_file_lister.o: CXXFLAGS += $(CFLAGS_FREETYPE)
|
|
||||||
text_file_reader.o: CXXFLAGS += -D_X86_
|
text_file_reader.o: CXXFLAGS += -D_X86_
|
||||||
video_provider_manager.o: CXXFLAGS += $(CFLAGS_FFMS2)
|
video_provider_manager.o: CXXFLAGS += $(CFLAGS_FFMS2)
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,16 @@
|
||||||
// Copyright (c) 2007, Rodrigo Braz Monteiro
|
// Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
// All rights reserved.
|
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
// modification, are permitted provided that the following conditions are met:
|
// 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,
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
// this list of conditions and the following disclaimer.
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
// and/or other materials provided with the distribution.
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
// may be used to endorse or promote products derived from this software
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 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 Project http://www.aegisub.org/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
//
|
//
|
||||||
|
@ -34,653 +21,357 @@
|
||||||
/// @ingroup tools_ui font_collector
|
/// @ingroup tools_ui font_collector
|
||||||
///
|
///
|
||||||
|
|
||||||
|
|
||||||
////////////
|
|
||||||
// Includes
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#ifndef AGI_PRE
|
|
||||||
#include <wx/config.h>
|
|
||||||
#include <wx/dirdlg.h>
|
|
||||||
#include <wx/filedlg.h>
|
|
||||||
#include <wx/filename.h>
|
|
||||||
#include <wx/fontenum.h>
|
|
||||||
#include <wx/msgdlg.h>
|
|
||||||
#include <wx/sizer.h>
|
|
||||||
#include <wx/wfstream.h>
|
|
||||||
#include <wx/zipstrm.h>
|
|
||||||
#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 "dialog_fonts_collector.h"
|
||||||
|
|
||||||
#include "font_file_lister.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 "help_button.h"
|
||||||
#include "libresrc/libresrc.h"
|
#include "libresrc/libresrc.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "scintilla_text_ctrl.h"
|
#include "scintilla_text_ctrl.h"
|
||||||
#include "selection_controller.h"
|
#include "selection_controller.h"
|
||||||
|
#include "standard_paths.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <libaegisub/scoped_ptr.h>
|
||||||
|
|
||||||
/// DOCME
|
#ifndef AGI_PRE
|
||||||
enum IDs {
|
#include <wx/button.h>
|
||||||
|
#include <wx/config.h>
|
||||||
|
#include <wx/dirdlg.h>
|
||||||
|
#include <wx/filedlg.h>
|
||||||
|
#include <wx/filename.h>
|
||||||
|
#include <wx/msgdlg.h>
|
||||||
|
#include <wx/radiobox.h>
|
||||||
|
#include <wx/sizer.h>
|
||||||
|
#include <wx/statbox.h>
|
||||||
|
#include <wx/stattext.h>
|
||||||
|
#include <wx/stc/stc.h>
|
||||||
|
#include <wx/textctrl.h>
|
||||||
|
#include <wx/wfstream.h>
|
||||||
|
#include <wx/zipstrm.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
/// DOCME
|
enum FcMode {
|
||||||
START_BUTTON = 1150,
|
CheckFontsOnly = 0,
|
||||||
|
CopyToFolder = 1,
|
||||||
/// DOCME
|
CopyToScriptFolder = 2,
|
||||||
BROWSE_BUTTON,
|
CopyToZip = 3
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
RADIO_BOX
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
wxDEFINE_EVENT(EVT_ADD_TEXT, wxThreadEvent);
|
||||||
|
wxDEFINE_EVENT(EVT_COLLECTION_DONE, wxThreadEvent);
|
||||||
|
|
||||||
/////////
|
/// @class FontsCollectorThread
|
||||||
// Event
|
/// @brief Worker thread for the font collector dialog
|
||||||
DECLARE_EVENT_TYPE(EVT_ADD_TEXT, -1)
|
class FontsCollectorThread : public wxThread {
|
||||||
DEFINE_EVENT_TYPE(EVT_ADD_TEXT)
|
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<wxString> GetFontPaths(wxString const&, int, bool) { return std::vector<wxString>(); }
|
||||||
|
} lister;
|
||||||
|
#endif
|
||||||
|
std::vector<wxString> 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<wxFFileOutputStream> out;
|
||||||
|
agi::scoped_ptr<wxZipOutputStream> zip;
|
||||||
|
if (oper == CopyToZip) {
|
||||||
|
out.reset(new wxFFileOutputStream(destination));
|
||||||
|
zip.reset(new wxZipOutputStream(*out));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool allOk = true;
|
||||||
|
for (std::vector<wxString>::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)
|
DialogFontsCollector::DialogFontsCollector(wxWindow *parent, AssFile *ass)
|
||||||
: wxDialog(parent,-1,_("Fonts Collector"),wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE)
|
: wxDialog(parent, -1, _("Fonts Collector"))
|
||||||
, subs(ass)
|
, subs(ass)
|
||||||
{
|
{
|
||||||
// Set icon
|
|
||||||
SetIcon(BitmapToIcon(GETIMAGE(font_collector_button_24)));
|
SetIcon(BitmapToIcon(GETIMAGE(font_collector_button_24)));
|
||||||
|
|
||||||
// Destination box
|
wxString modes[] = {
|
||||||
wxString dest = lagi_wxString(OPT_GET("Path/Fonts Collector Destination")->GetString());
|
_("Check fonts for availability"),
|
||||||
if (dest == "?script") {
|
_("Copy fonts to folder"),
|
||||||
wxFileName filename(subs->filename);
|
_("Copy fonts to subtitle file's folder"),
|
||||||
dest = filename.GetPath();
|
_("Copy fonts to zipped archive")
|
||||||
}
|
};
|
||||||
while (dest.Right(1) == "/") dest = dest.Left(dest.Length()-1);
|
collection_mode = new wxRadioBox(this, -1, "Action", wxDefaultPosition, wxDefaultSize, 4, modes, 1);
|
||||||
DestBox = new wxTextCtrl(this,-1,dest,wxDefaultPosition,wxSize(250,20),0);
|
collection_mode->SetSelection(mid<int>(0, OPT_GET("Tool/Fonts Collector/Action")->GetInt(), 3));
|
||||||
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
|
if (!ass->filename)
|
||||||
wxArrayString choices;
|
collection_mode->Enable(2, false);
|
||||||
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);
|
|
||||||
|
|
||||||
// Log box
|
wxStaticBoxSizer *destination_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Destination"));
|
||||||
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);
|
|
||||||
|
|
||||||
// Buttons sizer
|
dest_label = new wxStaticText(destination_box->GetStaticBox(), -1, "\n");
|
||||||
StartButton = new wxButton(this,START_BUTTON,_("&Start!"));
|
dest_ctrl = new wxTextCtrl(destination_box->GetStaticBox(), -1, StandardPaths::DecodePath(lagi_wxString(OPT_GET("Path/Fonts Collector Destination")->GetString())));
|
||||||
CloseButton = new wxButton(this,wxID_CANCEL);
|
dest_browse_button = new wxButton(destination_box->GetStaticBox(), -1, _("&Browse..."));
|
||||||
StartButton->SetDefault();
|
|
||||||
wxStdDialogButtonSizer *ButtonSizer = new wxStdDialogButtonSizer();
|
|
||||||
ButtonSizer->AddButton(StartButton);
|
|
||||||
ButtonSizer->AddButton(CloseButton);
|
|
||||||
ButtonSizer->AddButton(new HelpButton(this,"Fonts Collector"));
|
|
||||||
ButtonSizer->SetAffirmativeButton(StartButton);
|
|
||||||
ButtonSizer->Realize();
|
|
||||||
|
|
||||||
// Main sizer
|
wxSizer *dest_browse_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
wxSizer *MainSizer = new wxBoxSizer(wxVERTICAL);
|
dest_browse_sizer->Add(dest_ctrl, wxSizerFlags(1).Border(wxRIGHT));
|
||||||
MainSizer->Add(CollectAction,0,wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM,5);
|
dest_browse_sizer->Add(dest_browse_button, wxSizerFlags());
|
||||||
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
|
destination_box->Add(dest_label, wxSizerFlags().Border(wxBOTTOM));
|
||||||
SetSizer(MainSizer);
|
destination_box->Add(dest_browse_sizer, wxSizerFlags().Expand());
|
||||||
MainSizer->SetSizeHints(this);
|
|
||||||
|
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();
|
CenterOnParent();
|
||||||
|
|
||||||
// Run dummy event to update label
|
// Update the browse button and label
|
||||||
wxCommandEvent evt;
|
wxCommandEvent evt;
|
||||||
evt.SetInt(-1);
|
|
||||||
OnRadio(evt);
|
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
|
if (action != CopyToZip) {
|
||||||
///
|
if (dest.Last() != '/' && dest.Last() != '\\') {
|
||||||
DialogFontsCollector::~DialogFontsCollector() {
|
dest += wxFileName::GetPathSeparator();
|
||||||
FontFileLister::ClearData();
|
folder = dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (folder.FileExists())
|
||||||
///////////////
|
wxMessageBox(_("Invalid destination."), _("Error"), wxICON_EXCLAMATION | wxOK);
|
||||||
// Event table
|
if (!folder.DirExists())
|
||||||
BEGIN_EVENT_TABLE(DialogFontsCollector, wxDialog)
|
folder.Mkdir(0777, wxPATH_MKDIR_FULL);
|
||||||
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.DirExists()) {
|
if (!folder.DirExists()) {
|
||||||
wxMessageBox(_("Could not create destination folder."),_("Error"),wxICON_EXCLAMATION | wxOK);
|
wxMessageBox(_("Could not create destination folder."), _("Error"), wxICON_EXCLAMATION | wxOK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else if (folder.IsDir() || folder.GetName().empty()) {
|
||||||
|
wxMessageBox(_("Invalid path for .zip file."), _("Error"), wxICON_EXCLAMATION | wxOK);
|
||||||
// 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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start thread
|
if (action != CheckFontsOnly)
|
||||||
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");
|
|
||||||
}
|
|
||||||
OPT_SET("Path/Fonts Collector Destination")->SetString(STD_STR(dest));
|
OPT_SET("Path/Fonts Collector Destination")->SetString(STD_STR(dest));
|
||||||
}
|
|
||||||
OPT_SET("Tool/Fonts Collector/Action")->SetInt(action);
|
|
||||||
|
|
||||||
// Set buttons
|
// Disable the UI while it runs as we don't support canceling
|
||||||
StartButton->Enable(false);
|
EnableCloseButton(false);
|
||||||
BrowseButton->Enable(false);
|
start_btn->Enable(false);
|
||||||
DestBox->Enable(false);
|
dest_browse_button->Enable(false);
|
||||||
CloseButton->Enable(false);
|
dest_ctrl->Enable(false);
|
||||||
CollectAction->Enable(false);
|
close_btn->Enable(false);
|
||||||
DestLabel->Enable(false);
|
collection_mode->Enable(false);
|
||||||
if (!worker->IsDetached()) worker->Wait();
|
dest_label->Enable(false);
|
||||||
|
|
||||||
|
new FontsCollectorThread(subs, dest, static_cast<FcMode>(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);
|
||||||
|
|
||||||
|
if (!dest.empty())
|
||||||
/// @brief Close dialog
|
dest_ctrl->SetValue(dest);
|
||||||
/// @param event
|
|
||||||
///
|
|
||||||
void DialogFontsCollector::OnClose(wxCommandEvent &event) {
|
|
||||||
EndModal(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DialogFontsCollector::OnRadio(wxCommandEvent &) {
|
||||||
|
int value = collection_mode->GetSelection();
|
||||||
|
wxString dst = dest_ctrl->GetValue();
|
||||||
|
|
||||||
|
if (value == CheckFontsOnly || value == CopyToScriptFolder) {
|
||||||
/// @brief Browse location
|
dest_ctrl->Enable(false);
|
||||||
/// @param event
|
dest_browse_button->Enable(false);
|
||||||
///
|
dest_label->Enable(false);
|
||||||
void DialogFontsCollector::OnBrowse(wxCommandEvent &event) {
|
dest_label->SetLabel(_("N/A"));
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Choose folder
|
|
||||||
else {
|
else {
|
||||||
wxString dest = wxDirSelector(_("Select folder to save fonts on"),DestBox->GetValue(),0);
|
dest_ctrl->Enable(true);
|
||||||
if (!dest.empty()) {
|
dest_browse_button->Enable(true);
|
||||||
DestBox->SetValue(dest);
|
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."));
|
||||||
|
|
||||||
|
// Remove filename from browse box
|
||||||
/// @brief Radio box changed
|
if (dst.Right(4) == ".zip")
|
||||||
/// @param event
|
dest_ctrl->SetValue(wxFileName(dst).GetPath());
|
||||||
///
|
|
||||||
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<AssEntry*>::iterator cur=subs->Line.begin();cur!=subs->Line.end();cur++) {
|
|
||||||
// Collect from style
|
|
||||||
curStyle = dynamic_cast<AssStyle*>(*cur);
|
|
||||||
if (curStyle) {
|
|
||||||
AddFont(curStyle->font,0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect from dialogue
|
|
||||||
else {
|
|
||||||
curDiag = dynamic_cast<AssDialogue*>(*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;i<fonts.Count();i++) AddFont(fonts[i],2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
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
|
// Add filename to browse box
|
||||||
if (oper == 3 && someOk) {
|
if (!dst.EndsWith(".zip")) {
|
||||||
wxMutexGuiEnter();
|
wxFileName fn(dst + "//");
|
||||||
subs->Commit(_("font attachment"), AssFile::COMMIT_ATTACHMENT);
|
fn.SetFullName("fonts.zip");
|
||||||
wxMutexGuiLeave();
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DialogFontsCollector::OnAddText(wxThreadEvent &event) {
|
||||||
|
std::pair<int, wxString> str = event.GetPayload<std::pair<int, wxString> >();
|
||||||
/// @brief Process font
|
collection_log->SetReadOnly(false);
|
||||||
/// @param name
|
int pos = collection_log->GetReverseUnicodePosition(collection_log->GetLength());
|
||||||
/// @return
|
collection_log->AppendText(str.second);
|
||||||
///
|
if (str.first) {
|
||||||
bool FontsCollectorThread::ProcessFont(wxString name) {
|
collection_log->StartUnicodeStyling(pos, 31);
|
||||||
// Action
|
collection_log->SetUnicodeStyling(pos, str.second.size(), str.first);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
collection_log->GotoPos(pos);
|
||||||
// Just checking, found
|
collection_log->SetReadOnly(true);
|
||||||
else if (action == 0 || action == 4) {
|
|
||||||
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]),1);
|
|
||||||
}
|
|
||||||
else if (tempResult == 2) {
|
|
||||||
wxFileName fn(files[i]);
|
|
||||||
AppendText(wxString::Format(_("* %s already exists on destination.\n"),fn.GetFullName()),3);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
AppendText(wxString::Format(_("* Failed to copy %s.\n"),files[i]),2);
|
|
||||||
result = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Done
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DialogFontsCollector::OnCollectionComplete(wxThreadEvent &) {
|
||||||
|
EnableCloseButton();
|
||||||
|
start_btn->Enable();
|
||||||
|
close_btn->Enable();
|
||||||
|
collection_mode->Enable();
|
||||||
|
if (!subs->filename)
|
||||||
|
collection_mode->Enable(2, false);
|
||||||
|
|
||||||
|
wxCommandEvent evt;
|
||||||
/// @brief Copy font
|
OnRadio(evt);
|
||||||
/// @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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// @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<wxString>(),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;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,16 @@
|
||||||
// Copyright (c) 2007, Rodrigo Braz Monteiro
|
// Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
// All rights reserved.
|
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
// modification, are permitted provided that the following conditions are met:
|
// 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,
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
// this list of conditions and the following disclaimer.
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
// and/or other materials provided with the distribution.
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
// may be used to endorse or promote products derived from this software
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 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 Project http://www.aegisub.org/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
//
|
//
|
||||||
|
@ -35,72 +22,16 @@
|
||||||
///
|
///
|
||||||
|
|
||||||
#ifndef AGI_PRE
|
#ifndef AGI_PRE
|
||||||
#include <wx/button.h>
|
|
||||||
#include <wx/dialog.h>
|
#include <wx/dialog.h>
|
||||||
#include <wx/radiobox.h>
|
|
||||||
#include <wx/stattext.h>
|
|
||||||
#include <wx/stc/stc.h>
|
|
||||||
#include <wx/textctrl.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class AssFile;
|
class AssFile;
|
||||||
class AssStyle;
|
|
||||||
class AssOverrideParameter;
|
|
||||||
class DialogFontsCollector;
|
|
||||||
class wxZipOutputStream;
|
|
||||||
class ScintillaTextCtrl;
|
class ScintillaTextCtrl;
|
||||||
|
class wxButton;
|
||||||
/// DOCME
|
class wxRadioBox;
|
||||||
/// @class FontsCollectorThread
|
class wxStaticText;
|
||||||
/// @brief DOCME
|
class wxTextCtrl;
|
||||||
///
|
class wxThreadEvent;
|
||||||
/// 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);
|
|
||||||
};
|
|
||||||
|
|
||||||
/// DOCME
|
/// DOCME
|
||||||
/// @class DialogFontsCollector
|
/// @class DialogFontsCollector
|
||||||
|
@ -108,52 +39,25 @@ public:
|
||||||
///
|
///
|
||||||
/// DOCME
|
/// DOCME
|
||||||
class DialogFontsCollector : public wxDialog {
|
class DialogFontsCollector : public wxDialog {
|
||||||
friend class FontsCollectorThread;
|
|
||||||
|
|
||||||
AssFile *subs;
|
AssFile *subs;
|
||||||
|
|
||||||
/// DOCME
|
ScintillaTextCtrl *collection_log;
|
||||||
wxTextCtrl *DestBox;
|
wxButton *close_btn;
|
||||||
|
wxButton *dest_browse_button;
|
||||||
|
wxButton *start_btn;
|
||||||
|
wxRadioBox *collection_mode;
|
||||||
|
wxStaticText *dest_label;
|
||||||
|
wxTextCtrl *dest_ctrl;
|
||||||
|
|
||||||
/// DOCME
|
void OnStart(wxCommandEvent &);
|
||||||
ScintillaTextCtrl *LogBox;
|
void OnBrowse(wxCommandEvent &);
|
||||||
|
void OnRadio(wxCommandEvent &e);
|
||||||
|
|
||||||
/// DOCME
|
/// Append text to log message from worker thread
|
||||||
wxButton *BrowseButton;
|
void OnAddText(wxThreadEvent &event);
|
||||||
|
/// Collection complete notification from the worker thread to reenable buttons
|
||||||
/// DOCME
|
void OnCollectionComplete(wxThreadEvent &);
|
||||||
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);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DialogFontsCollector(wxWindow *parent, AssFile *subs);
|
DialogFontsCollector(wxWindow *parent, AssFile *subs);
|
||||||
~DialogFontsCollector();
|
|
||||||
|
|
||||||
DECLARE_EVENT_TABLE()
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
struct ColourString {
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
wxString text;
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
int colour;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,29 +1,16 @@
|
||||||
// Copyright (c) 2007, Rodrigo Braz Monteiro
|
// Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
// All rights reserved.
|
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
// modification, are permitted provided that the following conditions are met:
|
// 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,
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
// this list of conditions and the following disclaimer.
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
// and/or other materials provided with the distribution.
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
// may be used to endorse or promote products derived from this software
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 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 Project http://www.aegisub.org/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
//
|
//
|
||||||
|
@ -34,201 +21,130 @@
|
||||||
/// @ingroup font_collector
|
/// @ingroup font_collector
|
||||||
///
|
///
|
||||||
|
|
||||||
|
|
||||||
////////////
|
|
||||||
// Includes
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#ifndef AGI_PRE
|
#include "ass_dialogue.h"
|
||||||
#include <wx/tokenzr.h>
|
#include "ass_override.h"
|
||||||
#endif
|
#include "ass_style.h"
|
||||||
|
|
||||||
#ifdef WITH_FONTCONFIG
|
|
||||||
#include "font_file_lister_fontconfig.h"
|
#include "font_file_lister_fontconfig.h"
|
||||||
#define FontListerClass FontConfigFontFileLister
|
|
||||||
#elif defined(WITH_FREETYPE2)
|
#ifndef AGI_PRE
|
||||||
#include "font_file_lister_freetype.h"
|
#include <algorithm>
|
||||||
#define FontListerClass FreetypeFontFileLister
|
|
||||||
#else
|
#include <wx/intl.h>
|
||||||
#include "font_file_lister.h"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "standard_paths.h"
|
using namespace std::tr1::placeholders;
|
||||||
#include "text_file_reader.h"
|
|
||||||
#include "text_file_writer.h"
|
|
||||||
|
|
||||||
/// DOCME
|
FontCollector::FontCollector(FontCollectorStatusCallback status_callback, FontFileLister &lister)
|
||||||
FontFileLister *FontFileLister::instance = NULL;
|
: status_callback(status_callback)
|
||||||
|
, lister(lister)
|
||||||
/// @brief Constructor
|
{
|
||||||
///
|
|
||||||
FontFileLister::FontFileLister() {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FontCollector::ProcessDialogueLine(AssDialogue *line) {
|
||||||
|
if (line->Comment) return;
|
||||||
|
|
||||||
|
line->ParseASSTags();
|
||||||
|
bool text = false;
|
||||||
|
StyleInfo style = styles[line->Style];
|
||||||
|
StyleInfo initial = style;
|
||||||
|
|
||||||
/// @brief Destructor
|
for (size_t i = 0; i < line->Blocks.size(); ++i) {
|
||||||
///
|
if (AssDialogueBlockOverride *ovr = dynamic_cast<AssDialogueBlockOverride *>(line->Blocks[i])) {
|
||||||
FontFileLister::~FontFileLister() {
|
if (text) {
|
||||||
}
|
used_styles.insert(style);
|
||||||
|
text = false;
|
||||||
|
|
||||||
|
|
||||||
/// @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;i<len;i++) {
|
|
||||||
line += iter->second[i];
|
|
||||||
if (i != len-1) line += "|";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write line
|
for (size_t j = 0; j < ovr->Tags.size(); ++j) {
|
||||||
file.WriteLineToFile(line);
|
AssOverrideTag *tag = ovr->Tags[j];
|
||||||
}
|
wxString name = tag->Name;
|
||||||
}
|
|
||||||
catch (...) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (name == "\\r")
|
||||||
|
style = styles[tag->Params[0]->Get(line->Style)];
|
||||||
/// @brief Load cache
|
if (name == "\\b")
|
||||||
///
|
style.bold = tag->Params[0]->Get(initial.bold);
|
||||||
void FontFileLister::LoadCache() {
|
else if (name == "\\i")
|
||||||
try {
|
style.italic = tag->Params[0]->Get(initial.italic);
|
||||||
// Load cache
|
else if (name == "\\fn")
|
||||||
TextFileReader file(StandardPaths::DecodePath("?user/fontscache.dat"));
|
style.facename = tag->Params[0]->Get(initial.facename);
|
||||||
|
else
|
||||||
// Read each line
|
continue;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (AssDialogueBlockPlain *txt = dynamic_cast<AssDialogueBlockPlain *>(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<wxString> 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<wxString> FontCollector::GetFontPaths(std::list<AssEntry*> const& file) {
|
||||||
|
missing = 0;
|
||||||
|
|
||||||
|
status_callback(_("Parsing file\n"), 0);
|
||||||
|
for (std::list<AssEntry*>::const_iterator cur = file.begin(); cur != file.end(); ++cur) {
|
||||||
|
if (AssStyle *style = dynamic_cast<AssStyle*>(*cur)) {
|
||||||
|
StyleInfo &info = styles[style->name];
|
||||||
|
info.facename = style->font;
|
||||||
|
info.bold = style->bold;
|
||||||
|
info.italic = style->italic;
|
||||||
|
}
|
||||||
|
else if (AssDialogue *diag = dynamic_cast<AssDialogue*>(*cur))
|
||||||
|
ProcessDialogueLine(diag);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (used_styles.empty()) {
|
||||||
|
status_callback(_("No non-empty dialogue lines found"), 2);
|
||||||
|
return std::vector<wxString>();
|
||||||
|
}
|
||||||
|
|
||||||
|
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<wxString> 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;
|
||||||
|
}
|
||||||
|
|
|
@ -1,29 +1,16 @@
|
||||||
// Copyright (c) 2007, Rodrigo Braz Monteiro
|
// Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
// All rights reserved.
|
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
// modification, are permitted provided that the following conditions are met:
|
// 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,
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
// this list of conditions and the following disclaimer.
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
// and/or other materials provided with the distribution.
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
// may be used to endorse or promote products derived from this software
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 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 Project http://www.aegisub.org/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
//
|
//
|
||||||
|
@ -34,71 +21,73 @@
|
||||||
/// @ingroup font_collector
|
/// @ingroup font_collector
|
||||||
///
|
///
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
////////////
|
|
||||||
// Includes
|
|
||||||
#ifndef AGI_PRE
|
#ifndef AGI_PRE
|
||||||
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <tr1/functional>
|
||||||
|
#include <set>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <wx/arrstr.h>
|
|
||||||
#include <wx/string.h>
|
#include <wx/string.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_FREETYPE2
|
class AssEntry;
|
||||||
/// DOCME
|
class AssDialogue;
|
||||||
typedef struct FT_LibraryRec_ *FT_Library;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// DOCME
|
typedef std::tr1::function<void (wxString, int)> FontCollectorStatusCallback;
|
||||||
typedef std::map<wxString,wxArrayString> FontMap;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
/// @class FontFileLister
|
/// @class FontFileLister
|
||||||
/// @brief DOCME
|
/// @brief Font lister interface
|
||||||
///
|
|
||||||
/// DOCME
|
|
||||||
class FontFileLister {
|
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:
|
public:
|
||||||
static wxArrayString GetFilesWithFace(wxString facename);
|
/// @brief Get the path to the font with the given styles
|
||||||
static void Initialize();
|
/// @param facename Name of font face
|
||||||
static void ClearData();
|
/// @param bold ASS font weight
|
||||||
|
/// @param italic Italic?
|
||||||
|
/// @return Path to the matching font file(s), or empty if not found
|
||||||
|
virtual std::vector<wxString> 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<StyleInfo> used_styles;
|
||||||
|
/// Style name -> ASS style definition
|
||||||
|
std::map<wxString, StyleInfo> styles;
|
||||||
|
/// Paths to found required font files
|
||||||
|
std::set<wxString> 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<wxString> GetFontPaths(std::list<AssEntry*> const& file);
|
||||||
|
};
|
||||||
|
|
|
@ -1,29 +1,16 @@
|
||||||
// Copyright (c) 2007, David Lamparter, Rodrigo Braz Monteiro
|
// Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
// All rights reserved.
|
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
// modification, are permitted provided that the following conditions are met:
|
// 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,
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
// this list of conditions and the following disclaimer.
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
// and/or other materials provided with the distribution.
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
// may be used to endorse or promote products derived from this software
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 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 Project http://www.aegisub.org/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
//
|
//
|
||||||
|
@ -38,73 +25,169 @@
|
||||||
|
|
||||||
#ifdef WITH_FONTCONFIG
|
#ifdef WITH_FONTCONFIG
|
||||||
#include "font_file_lister_fontconfig.h"
|
#include "font_file_lister_fontconfig.h"
|
||||||
#include "charset_conv.h"
|
|
||||||
|
|
||||||
|
#include <fontconfig/fontconfig.h>
|
||||||
|
|
||||||
|
#include "compat.h"
|
||||||
|
|
||||||
/// @brief Get files that contain the face
|
#include <libaegisub/util.h>
|
||||||
/// @param facename
|
|
||||||
/// @return
|
|
||||||
///
|
|
||||||
wxArrayString FontConfigFontFileLister::DoGetFilesWithFace(wxString facename) {
|
|
||||||
wxArrayString results;
|
|
||||||
|
|
||||||
// Code stolen from asa
|
#ifndef AGI_PRE
|
||||||
FcPattern *final, *tmp1, *tmp2;
|
#include <wx/intl.h>
|
||||||
FcResult res;
|
#endif
|
||||||
FcChar8 *filename,*gotfamily;
|
|
||||||
int fontindex;
|
|
||||||
char buffer[1024];
|
|
||||||
strcpy(buffer,facename.mb_str(wxConvUTF8));
|
|
||||||
|
|
||||||
// Get data from fconfig or something
|
namespace {
|
||||||
tmp1 = FcPatternBuild(NULL,FC_FAMILY, FcTypeString,buffer,NULL);
|
// In SSA/ASS fonts are sometimes referenced by their "full name",
|
||||||
if (!tmp1) return results;
|
// which is usually a concatenation of family name and font
|
||||||
tmp2 = FcFontRenderPrepare(fontconf, tmp1, aux);
|
// style (ex. Ottawa Bold). Full name is available from
|
||||||
FcPatternDestroy(tmp1);
|
// FontConfig pattern element FC_FULLNAME, but it is never
|
||||||
FcDefaultSubstitute(tmp2);
|
// used for font matching.
|
||||||
FcConfigSubstitute(fontconf, tmp2, FcMatchPattern);
|
// Therefore, I'm removing words from the end of the name one
|
||||||
final = FcFontMatch(fontconf, tmp2, &res);
|
// by one, and adding shortened names to the pattern. It seems
|
||||||
FcPatternDestroy(tmp2);
|
// that the first value (full name in this case) has
|
||||||
if (!final) return results;
|
// precedence in matching.
|
||||||
if (FcPatternGetString(final, FC_FILE, 0, &filename) == FcResultMatch && FcPatternGetInteger(final, FC_INDEX, 0, &fontindex) == FcResultMatch) {
|
// An alternative approach could be to reimplement FcFontSort
|
||||||
FcPatternGetString(final, FC_FAMILY, fontindex, &gotfamily);
|
// using FC_FULLNAME instead of FC_FAMILY.
|
||||||
if (strcmp((const char*)gotfamily,buffer) == 0) {
|
int add_families(FcPattern *pat, std::string family) {
|
||||||
results.Add(wxString((char*) filename));
|
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<wxString> FontConfigFontFileLister::GetFontPaths(wxString const& facename, int bold, bool italic) {
|
||||||
|
std::vector<wxString> ret;
|
||||||
|
|
||||||
|
std::string family = STD_STR(facename);
|
||||||
|
if (family[0] == '@')
|
||||||
|
family.erase(0, 1);
|
||||||
|
|
||||||
/// @brief Constructor
|
int weight = bold == 0 ? 80 :
|
||||||
///
|
bold == 1 ? 200 :
|
||||||
FontConfigFontFileLister::FontConfigFontFileLister()
|
bold;
|
||||||
: fontconf(0), aux(0)
|
int slant = italic ? 110 : 0;
|
||||||
{
|
|
||||||
|
scoped<FcPattern*> 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<FcFontSet*> fsorted(FcFontSort(config, pat, true, NULL, &result), FcFontSetDestroy);
|
||||||
|
scoped<FcFontSet*> ffullname(MatchFullname(family.c_str(), weight, slant), FcFontSetDestroy);
|
||||||
|
if (!fsorted || !ffullname) return ret;
|
||||||
|
|
||||||
|
scoped<FcFontSet*> 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<FcPattern*> 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
|
#endif
|
||||||
|
|
|
@ -1,29 +1,16 @@
|
||||||
// Copyright (c) 2007, Rodrigo Braz Monteiro
|
// Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
// All rights reserved.
|
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
// modification, are permitted provided that the following conditions are met:
|
// 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,
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
// this list of conditions and the following disclaimer.
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
// and/or other materials provided with the distribution.
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
// may be used to endorse or promote products derived from this software
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 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 Project http://www.aegisub.org/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
//
|
//
|
||||||
|
@ -34,37 +21,40 @@
|
||||||
/// @ingroup font_collector
|
/// @ingroup font_collector
|
||||||
///
|
///
|
||||||
|
|
||||||
|
#ifdef WITH_FONTCONFIG
|
||||||
|
|
||||||
|
|
||||||
////////////
|
|
||||||
// Includes
|
|
||||||
#include <fontconfig/fontconfig.h>
|
|
||||||
#include <fontconfig/fcfreetype.h>
|
|
||||||
|
|
||||||
#include "font_file_lister.h"
|
#include "font_file_lister.h"
|
||||||
|
|
||||||
|
typedef struct _FcConfig FcConfig;
|
||||||
|
typedef struct _FcFontSet FcFontSet;
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
/// @class FontConfigFontFileLister
|
/// @class FontConfigFontFileLister
|
||||||
/// @brief DOCME
|
/// @brief fontconfig powered font lister
|
||||||
///
|
|
||||||
/// DOCME
|
|
||||||
class FontConfigFontFileLister : public FontFileLister {
|
class FontConfigFontFileLister : public FontFileLister {
|
||||||
friend class FontFileLister;
|
template<typename T> class scoped {
|
||||||
private:
|
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
|
scoped<FcConfig*> config;
|
||||||
FcConfig *fontconf;
|
|
||||||
|
|
||||||
/// DOCME
|
/// @brief Case-insensitive match ASS/SSA font family against full name. (also known as "name for humans")
|
||||||
FcPattern *aux;
|
/// @param family font fullname
|
||||||
|
/// @param bold weight attribute
|
||||||
FontConfigFontFileLister();
|
/// @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);
|
std::vector<wxString> GetFontPaths(wxString const& facename, int bold, bool italic);
|
||||||
void DoInitialize();
|
|
||||||
void DoClearData();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,29 +1,16 @@
|
||||||
// Copyright (c) 2007, Niels Martin Hansen, Rodrigo Braz Monteiro
|
// Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
// All rights reserved.
|
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
// modification, are permitted provided that the following conditions are met:
|
// 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,
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
// this list of conditions and the following disclaimer.
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
// and/or other materials provided with the distribution.
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
// may be used to endorse or promote products derived from this software
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 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 Project http://www.aegisub.org/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
//
|
//
|
||||||
|
@ -34,154 +21,162 @@
|
||||||
/// @ingroup font_collector
|
/// @ingroup font_collector
|
||||||
///
|
///
|
||||||
|
|
||||||
|
|
||||||
////////////
|
|
||||||
// Includes
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#ifdef WITH_FREETYPE2
|
#ifdef WITH_FREETYPE2
|
||||||
|
#include "font_file_lister_freetype.h"
|
||||||
#ifndef AGI_PRE
|
|
||||||
#include <wx/dir.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <ft2build.h>
|
#include <ft2build.h>
|
||||||
#include FT_FREETYPE_H
|
#include FT_FREETYPE_H
|
||||||
#include FT_GLYPH_H
|
#include FT_GLYPH_H
|
||||||
#include FT_SFNT_NAMES_H
|
#include FT_SFNT_NAMES_H
|
||||||
|
#include FT_TRUETYPE_IDS_H
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "charset_conv.h"
|
#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<wxString, std::set<wxString> > FontMap;
|
||||||
|
|
||||||
/// @brief Constructor
|
wxString get_font_folder() {
|
||||||
///
|
wchar_t szPath[MAX_PATH];
|
||||||
FreetypeFontFileLister::FreetypeFontFileLister() {
|
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_FONTS, NULL, 0, szPath)))
|
||||||
// Initialize freetype2
|
return wxString(szPath) + "\\";
|
||||||
FT_Init_FreeType(&ft2lib);
|
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<FT_UShort, std::vector<wxString> > get_names(FT_Face face) {
|
||||||
|
std::map<FT_UShort, std::vector<wxString> > final;
|
||||||
|
|
||||||
/// @brief Destructor
|
FT_UInt count = FT_Get_Sfnt_Name_Count(face);
|
||||||
///
|
for (FT_UInt i = 0; i < count; ++i) {
|
||||||
FreetypeFontFileLister::~FreetypeFontFileLister() {
|
FT_SfntName name;
|
||||||
}
|
FT_Get_Sfnt_Name(face, i, &name);
|
||||||
|
|
||||||
|
// truetype string encoding is an absolute nightmare, so just try
|
||||||
|
// UTF-16BE and local charsets
|
||||||
/// @brief Get name from face
|
if (name.string_len) {
|
||||||
/// @param face
|
if ((name.platform_id == TT_PLATFORM_MICROSOFT && name.encoding_id == TT_MS_ID_UNICODE_CS) ||
|
||||||
/// @param id
|
name.platform_id == TT_PLATFORM_APPLE_UNICODE ||
|
||||||
/// @return
|
name.string[0])
|
||||||
///
|
{
|
||||||
wxArrayString GetName(FT_Face &face,int id) {
|
final[name.name_id].push_back(wxString(name.string, wxMBConvUTF16BE(), name.string_len));
|
||||||
// Get name
|
}
|
||||||
wxArrayString final;
|
else
|
||||||
int count = FT_Get_Sfnt_Name_Count(face);
|
final[name.name_id].push_back(wxString(name.string, name.string_len));
|
||||||
|
|
||||||
for (int i=0;i<count;i++) {
|
|
||||||
FT_SfntName name;
|
|
||||||
FT_Get_Sfnt_Name(face,i,&name);
|
|
||||||
if (name.name_id == id) {
|
|
||||||
char *str = new char[name.string_len+2];
|
|
||||||
memcpy(str,name.string,name.string_len);
|
|
||||||
str[name.string_len] = 0;
|
|
||||||
str[name.string_len+1] = 0;
|
|
||||||
if (name.encoding_id == 0) final.Add(wxString(str));
|
|
||||||
else if (name.encoding_id == 1) {
|
|
||||||
wxMBConvUTF16BE conv;
|
|
||||||
wxString string(str,conv);
|
|
||||||
final.Add(string);
|
|
||||||
}
|
}
|
||||||
delete [] str;
|
}
|
||||||
|
|
||||||
|
return final;
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_cache(FontMap &font_files, std::set<wxString> &indexed_files) {
|
||||||
|
try {
|
||||||
|
TextFileReader file(StandardPaths::DecodePath("?local/freetype_collector_index.dat"), "utf-16le");
|
||||||
|
while (file.HasMoreLines()) {
|
||||||
|
wxString face = file.ReadLineFromFile();
|
||||||
|
std::set<wxString>& 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;
|
wxArrayString fontfiles;
|
||||||
wxDir::GetAllFiles(source, &fontfiles, wxEmptyString, wxDIR_FILES);
|
wxDir::GetAllFiles(get_font_folder(), &fontfiles, "", wxDIR_FILES);
|
||||||
|
|
||||||
// Loop through each file
|
for (size_t i = 0; i < fontfiles.size(); ++i) {
|
||||||
int fterr;
|
if (indexed_files.count(fontfiles[i])) continue;
|
||||||
for (unsigned int i=0;i<fontfiles.Count(); i++) {
|
|
||||||
// Check if it's cached
|
|
||||||
if (IsFilenameCached(fontfiles[i])) continue;
|
|
||||||
|
|
||||||
// Loop through each face in the file
|
FT_Face face;
|
||||||
for (int facenum=0;true;facenum++) {
|
for (FT_Long i = 0; FT_New_Face(ft2lib, fontfiles[i].mb_str(*wxConvFileName), i, &face) == 0; ++i) {
|
||||||
// Get font face
|
if (get_name_count(fontfiles[i], face) > 0) {
|
||||||
FT_Face face;
|
std::map<FT_UShort, std::vector<wxString> > names = get_names(face);
|
||||||
fterr = FT_New_Face(ft2lib, fontfiles[i].mb_str(*wxConvFileName), facenum, &face);
|
std::vector<wxString>& family = names[1];
|
||||||
if (fterr) break;
|
std::vector<wxString>& style = names[2];
|
||||||
|
std::vector<wxString>& full_name = names[4];
|
||||||
|
|
||||||
// Special names for TTF and OTF
|
for (size_t j = 0; j < family.size() && j < style.size(); ++j) {
|
||||||
int nameCount = 0;
|
if (style[j] != "Regular")
|
||||||
wxString ext = fontfiles[i].Right(4).Lower();
|
AddFont(fontfiles[i], family[j], style[j]);
|
||||||
if (ext == _T(".otf") || ext == _T(".ttf") || ext == _T(".ttc_")) nameCount = FT_Get_Sfnt_Name_Count(face);
|
else
|
||||||
if (nameCount > 0) {
|
AddFont(fontfiles[i], family[j]);
|
||||||
wxArrayString family = GetName(face,1);
|
|
||||||
wxArrayString subFamily = GetName(face,2);
|
|
||||||
wxArrayString fullName = GetName(face,4);
|
|
||||||
for (size_t j=0;j<family.Count() && j<subFamily.Count();j++) {
|
|
||||||
if (subFamily[j] != _T("Regular")) {
|
|
||||||
AddFont(fontfiles[i],family[j] + _T(" ") + subFamily[j]);
|
|
||||||
AddFont(fontfiles[i],_T("*")+family[j]);
|
|
||||||
}
|
|
||||||
else AddFont(fontfiles[i],family[j]);
|
|
||||||
}
|
}
|
||||||
for (size_t j=0;j<fullName.Count();j++) AddFont(fontfiles[i],fullName[j]);
|
for (size_t j = 0; j < full_name.size(); ++j)
|
||||||
|
AddFont(fontfiles[i], full_name[j]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ordinary fonts
|
|
||||||
else {
|
else {
|
||||||
if (face->style_name) {
|
if (face->style_name)
|
||||||
AddFont(fontfiles[i],wxString(face->family_name, csConvLocal) + _T(" ") + wxString(face->style_name, csConvLocal));
|
AddFont(fontfiles[i], face->family_name, face->style_name);
|
||||||
AddFont(fontfiles[i],_T("*")+wxString(face->family_name, csConvLocal));
|
else
|
||||||
}
|
AddFont(fontfiles[i], wxString(face->family_name));
|
||||||
else AddFont(fontfiles[i],wxString(face->family_name, csConvLocal));
|
|
||||||
}
|
}
|
||||||
FT_Done_Face(face);
|
FT_Done_Face(face);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save cache
|
FT_Done_FreeType(ft2lib);
|
||||||
SaveCache();
|
|
||||||
|
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<wxString> FreetypeFontFileLister::GetFontPaths(wxString const& facename, int, bool) {
|
||||||
|
std::vector<wxString> 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
|
#endif WITH_FREETYPE2
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,16 @@
|
||||||
// Copyright (c) 2007, Rodrigo Braz Monteiro
|
// Copyright (c) 2012, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
// All rights reserved.
|
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
// modification, are permitted provided that the following conditions are met:
|
// 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,
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
// this list of conditions and the following disclaimer.
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
// and/or other materials provided with the distribution.
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
// may be used to endorse or promote products derived from this software
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 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 Project http://www.aegisub.org/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
//
|
//
|
||||||
|
@ -34,35 +21,43 @@
|
||||||
/// @ingroup font_collector
|
/// @ingroup font_collector
|
||||||
///
|
///
|
||||||
|
|
||||||
|
#ifdef WITH_FREETYPE2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
////////////
|
|
||||||
// Includes
|
|
||||||
#include "font_file_lister.h"
|
#include "font_file_lister.h"
|
||||||
|
|
||||||
|
#ifndef AGI_PRE
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
/// DOCME
|
#include <wx/string.h>
|
||||||
typedef struct FT_LibraryRec_ *FT_Library;
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// DOCME
|
|
||||||
/// @class FreetypeFontFileLister
|
/// @class FreetypeFontFileLister
|
||||||
/// @brief DOCME
|
/// @brief Freetype2-based font collector
|
||||||
///
|
|
||||||
/// DOCME
|
|
||||||
class FreetypeFontFileLister : public FontFileLister {
|
class FreetypeFontFileLister : public FontFileLister {
|
||||||
friend class FontFileLister;
|
/// Map of face name -> possible containing files
|
||||||
private:
|
std::map<wxString, std::set<wxString> > font_files;
|
||||||
|
|
||||||
/// DOCME
|
/// Set of files which were loaded from the cache
|
||||||
FT_Library ft2lib;
|
std::set<wxString> 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();
|
/// Add a font to the list of mappings
|
||||||
~FreetypeFontFileLister();
|
/// @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<wxString> GetFontPaths(wxString const& facename, int, bool);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue