Move character count stuff to libaegisub

This commit is contained in:
Thomas Goyne 2014-04-18 17:18:41 -07:00
parent bd53302907
commit af32733797
10 changed files with 133 additions and 80 deletions

View file

@ -46,6 +46,7 @@
<ClInclude Include="$(SrcDir)include\libaegisub\cajun\visitor.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\cajun\writer.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\calltip_provider.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\character_count.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\charset.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\charset_conv.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\charset_conv_win.h" />
@ -91,6 +92,7 @@
<ClCompile Include="$(SrcDir)common\cajun\reader.cpp" />
<ClCompile Include="$(SrcDir)common\cajun\writer.cpp" />
<ClCompile Include="$(SrcDir)common\calltip_provider.cpp" />
<ClCompile Include="$(SrcDir)common\character_count.cpp" />
<ClCompile Include="$(SrcDir)common\charset.cpp" />
<ClCompile Include="$(SrcDir)common\charset_6937.cpp" />
<ClCompile Include="$(SrcDir)common\charset_conv.cpp" />

View file

@ -167,6 +167,9 @@
<ClInclude Include="$(SrcDir)include\libaegisub\file_mapping.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)include\libaegisub\character_count.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(SrcDir)windows\lagi_pre.cpp">
@ -274,6 +277,9 @@
<ClCompile Include="$(SrcDir)common\file_mapping.cpp">
<Filter>Source Files\Common</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)common\character_count.cpp">
<Filter>Source Files\Common</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="$(SrcDir)include\libaegisub\charsets.def">

View file

@ -19,6 +19,7 @@ SRC += \
common/cajun/reader.cpp \
common/cajun/writer.cpp \
common/calltip_provider.cpp \
common/character_count.cpp \
common/charset.cpp \
common/charset_6937.cpp \
common/charset_conv.cpp \

View file

@ -0,0 +1,92 @@
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
#include "libaegisub/character_count.h"
#include "libaegisub/ass/dialogue_parser.h"
#include <boost/locale/boundary.hpp>
#include <boost/range/algorithm_ext.hpp>
#include <unicode/uchar.h>
#include <unicode/utf8.h>
namespace {
template <typename Iterator>
size_t count_in_range(Iterator begin, Iterator end, bool ignore_whitespace) {
using namespace boost::locale::boundary;
const ssegment_index characters(character, begin, end);
if (!ignore_whitespace)
return boost::distance(characters);
// characters.rule(word_any) doesn't seem to work for character indexes (everything is word_none)
size_t count = 0;
for (auto const& chr : characters) {
UChar32 c;
int i = 0;
U8_NEXT_UNSAFE(chr.begin(), i, c);
if (!u_isUWhiteSpace(c))
++count;
}
return count;
}
}
namespace agi {
size_t CharacterCount(std::string const& str) {
size_t characters = 0;
auto pos = begin(str);
do {
auto it = std::find(pos, end(str), '{');
characters += count_in_range(pos, it, true);
if (it == end(str)) break;
pos = std::find(pos, end(str), '}');
if (pos == end(str)) {
characters += count_in_range(it, pos, true);
break;
}
} while (++pos != end(str));
return characters;
}
size_t MaxLineLength(std::string const& text, bool ignore_whitespace) {
auto tokens = agi::ass::TokenizeDialogueBody(text);
agi::ass::MarkDrawings(text, tokens);
size_t pos = 0;
size_t max_line_length = 0;
size_t current_line_length = 0;
for (auto token : tokens) {
if (token.type == agi::ass::DialogueTokenType::LINE_BREAK) {
if (text[pos + 1] == 'h') {
if (!ignore_whitespace)
current_line_length += 1;
}
else { // N or n
max_line_length = std::max(max_line_length, current_line_length);
current_line_length = 0;
}
}
else if (token.type == agi::ass::DialogueTokenType::TEXT)
current_line_length += count_in_range(begin(text) + pos, begin(text) + pos + token.length, ignore_whitespace);
pos += token.length;
}
return std::max(max_line_length, current_line_length);
}
}

View file

@ -0,0 +1,23 @@
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
#include <string>
namespace agi {
/// Get the length in characters of the longest line in the given text
size_t MaxLineLength(std::string const& text, bool ignore_whitespace);
size_t CharacterCount(std::string const& str);
}

View file

@ -21,9 +21,10 @@
#include "compat.h"
#include "include/aegisub/context.h"
#include "options.h"
#include "utils.h"
#include "video_context.h"
#include <libaegisub/character_count.h>
#include <wx/dc.h>
int WidthHelper::operator()(boost::flyweight<std::string> const& str) {
@ -223,28 +224,13 @@ struct GridColumnCPS final : GridColumn {
bool RefreshOnTextChange() const override { return true; }
wxString Value(const AssDialogue *d, const agi::Context *) const override {
int characters = 0;
int duration = d->End - d->Start;
auto const& text = d->Text.get();
if (duration <= 0 || text.size() > static_cast<size_t>(duration))
return wxS("");
auto pos = begin(text);
do {
auto it = std::find(pos, end(text), '{');
characters += CharacterCount(pos, it, true);
if (it == end(text)) break;
pos = std::find(pos, end(text), '}');
if (pos == end(text)) {
characters += CharacterCount(it, pos, true);
break;
}
} while (++pos != end(text));
return std::to_wstring(characters * 1000 / duration);
return std::to_wstring(agi::CharacterCount(text) * 1000 / duration);
}
int Width(const agi::Context *c, WidthHelper &helper, bool) const override {

View file

@ -14,12 +14,11 @@
//
// Aegisub Project http://www.aegisub.org/
/// @file placeholder_ctrl.h
/// @ingroup custom_control
///
#include <wx/settings.h>
// Defined in osx_utils.mm
void SetPlaceholderText(wxWindow *window, wxString const& placeholder);
/// @class Placeholder
/// @brief A wrapper around a control to add placeholder text
///

View file

@ -50,10 +50,10 @@
#include "text_selection_controller.h"
#include "timeedit_ctrl.h"
#include "tooltip_manager.h"
#include "utils.h"
#include "validators.h"
#include "video_context.h"
#include <libaegisub/character_count.h>
#include <libaegisub/dispatch.h>
#include <libaegisub/util.h>
@ -228,7 +228,7 @@ wxTextCtrl *SubsEditBox::MakeMarginCtrl(wxString const& tooltip, int margin, wxS
middle_left_sizer->Add(ctrl, wxSizerFlags().Center());
Bind(wxEVT_TEXT, [=](wxCommandEvent&) {
int value = mid(0, atoi(ctrl->GetValue().utf8_str()), 9999);
int value = agi::util::mid(0, atoi(ctrl->GetValue().utf8_str()), 9999);
SetSelectedRows([&](AssDialogue *d) { d->Margin[margin] = value; },
commit_msg, AssFile::COMMIT_DIAG_META);
}, ctrl->GetId());
@ -587,7 +587,7 @@ void SubsEditBox::CallCommand(const char *cmd_name) {
void SubsEditBox::UpdateCharacterCount(std::string const& text) {
auto ignore_whitespace = OPT_GET("Subtitle/Character Counter/Ignore Whitespace")->GetBool();
agi::dispatch::Background().Async([=]{
size_t length = MaxLineLength(text, ignore_whitespace);
size_t length = agi::MaxLineLength(text, ignore_whitespace);
agi::dispatch::Main().Async([=]{
char_count->SetValue(wxString::Format("%lu", (unsigned long)length));
size_t limit = (size_t)OPT_GET("Subtitle/Character Limit")->GetInt();

View file

@ -39,7 +39,6 @@
#include "options.h"
#include "retina_helper.h"
#include <libaegisub/ass/dialogue_parser.h>
#include <libaegisub/dispatch.h>
#include <libaegisub/fs.h>
#include <libaegisub/log.h>
@ -49,11 +48,7 @@
#endif
#include <boost/filesystem.hpp>
#include <boost/format.hpp>
#include <boost/locale/boundary.hpp>
#include <boost/range/algorithm_ext.hpp>
#include <map>
#include <unicode/uchar.h>
#include <unicode/utf8.h>
#include <wx/clipbrd.h>
#include <wx/filedlg.h>
@ -219,51 +214,6 @@ void CleanCache(agi::fs::path const& directory, std::string const& file_type, ui
});
}
size_t CharacterCount(std::string::const_iterator begin, std::string::const_iterator end, bool ignore_whitespace) {
using namespace boost::locale::boundary;
const ssegment_index characters(character, begin, end);
if (!ignore_whitespace)
return boost::distance(characters);
// characters.rule(word_any) doesn't seem to work for character indexes (everything is word_none)
size_t count = 0;
for (auto const& chr : characters) {
UChar32 c;
int i = 0;
U8_NEXT_UNSAFE(chr.begin(), i, c);
if (!u_isUWhiteSpace(c))
++count;
}
return count;
}
size_t MaxLineLength(std::string const& text, bool ignore_whitespace) {
auto tokens = agi::ass::TokenizeDialogueBody(text);
agi::ass::MarkDrawings(text, tokens);
size_t pos = 0;
size_t max_line_length = 0;
size_t current_line_length = 0;
for (auto token : tokens) {
if (token.type == agi::ass::DialogueTokenType::LINE_BREAK) {
if (text[pos + 1] == 'h') {
if (!ignore_whitespace)
current_line_length += 1;
}
else { // N or n
max_line_length = std::max(max_line_length, current_line_length);
current_line_length = 0;
}
}
else if (token.type == agi::ass::DialogueTokenType::TEXT)
current_line_length += CharacterCount(begin(text) + pos, begin(text) + pos + token.length, ignore_whitespace);
pos += token.length;
}
return std::max(max_line_length, current_line_length);
}
#ifndef __WXOSX_COCOA__
// OS X implementation in osx_utils.mm
void AddFullScreenButton(wxWindow *) { }

View file

@ -60,10 +60,6 @@ std::string float_to_string(double val);
/// Algorithm from http://bob.allegronetwork.com/prog/tricks.html
int SmallestPowerOf2(int x);
/// Get the length in characters of the longest line in the given text
size_t MaxLineLength(std::string const& text, bool ignore_whitespace);
size_t CharacterCount(std::string::const_iterator begin, std::string::const_iterator end, bool ignore_whitespace);
/// @brief Launch a new copy of Aegisub.
///
/// Contrary to what the name suggests, this does not close the currently
@ -75,8 +71,6 @@ void AddFullScreenButton(wxWindow *window);
void SetFloatOnParent(wxWindow *window);
void SetPlaceholderText(wxWindow *window, wxString const& placeholder);
/// Forward a mouse wheel event to the window under the mouse if needed
/// @param source The initial target of the wheel event
/// @param evt The event