Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F116967
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
11 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/WordWrapper.cpp b/src/WordWrapper.cpp
index a5d5b71..8817d03 100644
--- a/src/WordWrapper.cpp
+++ b/src/WordWrapper.cpp
@@ -1,307 +1,311 @@
/*
* Copyright (C) 2019 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "WordWrapper.h"
#include "HyphenationManager.h"
#include "HyphenationRule.h"
#include "UTF8Functions.h"
#include <assert.h>
#include <SDL_ttf_fontfallback.h>
int WordWrapper::getTextWidth(const std::string& s) {
if (s.empty()) return 0;
int w = 0;
if (font) {
TTF_SizeUTF8(font, s.c_str(), &w, NULL);
} else {
const size_t m = s.size();
U8STRING_FOR_EACH_CHARACTER_DO_BEGIN(s, i, m, ch, REPLACEMENT_CHARACTER);
w++;
U8STRING_FOR_EACH_CHARACTER_DO_END();
}
return w;
}
int WordWrapper::getGlyphWidth(int ch) {
if (font) {
int w = 0;
TTF_GlyphMetrics(font, ch, NULL, NULL, NULL, NULL, &w);
return w;
} else {
return 1;
}
}
WordWrapper::WordWrapper()
: font(NULL)
, maxWidth(0)
, wordWrap(false)
, reserveHyperlinks(false)
{
}
WordWrapper::~WordWrapper() {
}
bool WordWrapper::isReserved(const std::string& word) {
if (reserveHyperlinks) {
const char *s = word.c_str();
const size_t m = word.size();
for (size_t i = 0; i < m; i++) {
// we only support http or https
if ((s[i] == 'H' || s[i] == 'h')
&& (s[i + 1] == 'T' || s[i + 1] == 't')
&& (s[i + 2] == 'T' || s[i + 2] == 't')
&& (s[i + 3] == 'P' || s[i + 3] == 'p'))
{
if (s[i + 4] == ':' && s[i + 5] == '/' && s[i + 6] == '/') {
// http
return true;
} else if ((s[i + 4] == 'S' || s[i + 4] == 's') && s[i + 5] == ':' && s[i + 6] == '/' && s[i + 7] == '/') {
// https
return true;
}
}
}
}
for (const std::string& s : reservedWords) {
if (word == s) return true;
}
+ for (const std::string& s : reservedFragments) {
+ if (word.find(s) != std::string::npos) return true;
+ }
+
return false;
}
void WordWrapper::addString(std::vector<std::string>& output, const std::string& input) {
std::string line;
for (char c : input) {
if (c == '\r') {
} else if (c == '\n') {
addLine(output, line);
line.clear();
} else {
line.push_back(c);
}
}
addLine(output, line);
}
// Add a word to line, output the line only if the line+newWord doesn't fit the width and in this case put the newWord to the line.
void WordWrapper::addWord(std::vector<std::string>& output, std::string& line, int& lineWidth, const std::string& spaces, const std::string& nonSpaces) {
int w1 = getTextWidth(spaces);
{
int w2 = getTextWidth(nonSpaces);
//Check if it fits into current line.
if (lineWidth + w1 + w2 <= maxWidth) {
line += spaces + nonSpaces;
lineWidth += w1 + w2;
return;
}
//Now it doesn't fit into current line.
//Check if we should skip the hyphenation.
if (hyphen.empty() || isReserved(nonSpaces)) {
if (line.empty()) {
//A line consists of at least one word, so we append it forcefully.
line += spaces + nonSpaces;
lineWidth += w1 + w2;
} else {
//We output current line.
output.push_back(line);
//And add a new line consisting of new word (but we remove spaces in it).
line = nonSpaces;
lineWidth = w2;
}
return;
}
}
auto hm = getHyphenationManager();
auto hyphenator = hyphenatorLanguage.empty() ? hm->getHyphenator() : hm->getHyphenator(hyphenatorLanguage);
auto rules = hyphenator->applyHyphenationRules(nonSpaces);
const size_t m = nonSpaces.size();
std::string tmp, prev;
int skip = 0, prevSkip = 0, prevWidth = 0;
size_t prevIndex = 0;
for (size_t i = 0;; i++) {
const Hyphenate::HyphenationRule *rule = (i < m) ? (*rules)[i] : NULL;
if (rule || i == m) {
std::string tmp2 = tmp;
if (rule) rule->apply_first(tmp2, hyphen);
int newWidth = getTextWidth(tmp2);
/*//debug
printf("%-5d %s\n", newWidth, tmp2.c_str());*/
//Check if we should output current line directly.
if (lineWidth + w1 + newWidth > maxWidth && prev.empty() && !line.empty()) {
//We output current line.
output.push_back(line);
line.clear();
lineWidth = 0;
w1 = 0;
}
//Check if the line is still too long.
if (lineWidth + w1 + newWidth > maxWidth) {
//Check if we have previous available hyphenation
if (prev.empty()) {
//Line is empty, we have to append it forcefully.
assert(line.empty());
if (w1 > 0) line += spaces;
line += tmp2;
if (i < m) {
output.push_back(line);
line.clear();
lineWidth = 0;
w1 = 0;
} else {
lineWidth += w1 + newWidth;
}
//Update buffer
tmp.clear();
if (rule) skip += rule->apply_second(tmp);
} else {
//We use previous available hyphenation
if (w1 > 0) line += spaces;
output.push_back(line + prev);
line.clear();
lineWidth = 0;
w1 = 0;
//Rewind
prev.clear();
prevWidth = 0;
skip = prevSkip;
i = prevIndex;
//Update buffer
tmp.clear();
rule = (*rules)[i];
assert(rule != NULL);
skip += rule->apply_second(tmp);
}
} else if (i == m) {
//Output last part
if (w1 > 0) line += spaces;
line += tmp2;
lineWidth += w1 + newWidth;
} else if (newWidth > prevWidth) {
//Update prev hyphenation
prev = tmp2;
prevSkip = skip;
prevWidth = newWidth;
prevIndex = i;
}
}
if (i >= m) break;
if (skip > 0) skip--;
else tmp.push_back(nonSpaces[i]);
}
}
void WordWrapper::addLine(std::vector<std::string>& output, const std::string& input) {
if (!wordWrap) {
//Word wrap is not enabled, simply add it to output
output.push_back(input);
return;
}
const size_t m = input.size();
std::string spaces, nonSpaces, line;
int lineWidth = 0;
bool prevIsCJK = false, prevIsCJKStarting = false;
U8STRING_FOR_EACH_CHARACTER_DO_BEGIN(input, i, m, ch, REPLACEMENT_CHARACTER);
//A word consists of a sequence of white spaces and a sequence of non-white-spaces.
//TODO: For CJK should only read one CJK character (possibly with a punctuation mark)
if (utf32IsSpace(ch)) {
prevIsCJK = false;
prevIsCJKStarting = false;
if (!nonSpaces.empty()) {
addWord(output, line, lineWidth, spaces, nonSpaces);
spaces.clear();
nonSpaces.clear();
}
U8_ENCODE(ch, spaces.push_back);
} else {
bool isCJK = utf32IsCJK(ch);
bool isCJKStarting = utf32IsCJKStartingPunctuation(ch);
if (prevIsCJK) {
//Output the CJK character immediately unless current character can't be at start of line
if (!utf32IsCJKEndingPunctuation(ch)) {
addWord(output, line, lineWidth, spaces, nonSpaces);
spaces.clear();
nonSpaces.clear();
}
} else if (isCJK && !nonSpaces.empty()) {
//Output the existing non-CJK character immediately unless it can't be at end of line
if (!prevIsCJKStarting) {
addWord(output, line, lineWidth, spaces, nonSpaces);
spaces.clear();
nonSpaces.clear();
}
}
prevIsCJK = isCJK;
prevIsCJKStarting = isCJKStarting;
U8_ENCODE(ch, nonSpaces.push_back);
}
U8STRING_FOR_EACH_CHARACTER_DO_END();
//FIXME: Here we temporarily ignore trailing spaces
if (!nonSpaces.empty()) {
addWord(output, line, lineWidth, spaces, nonSpaces);
}
//Output the remaining text.
output.push_back(line);
}
void WordWrapper::addLines(std::vector<std::string>& output, const std::vector<std::string>& input) {
for (const std::string& s : input) {
addLine(output, s);
}
}
diff --git a/src/WordWrapper.h b/src/WordWrapper.h
index 695cd75..59ee9a0 100644
--- a/src/WordWrapper.h
+++ b/src/WordWrapper.h
@@ -1,82 +1,85 @@
/*
* Copyright (C) 2019 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef WORDWRAPPER_H
#define WORDWRAPPER_H
#include <vector>
#include <string>
#include <set>
// The forward declaration of TTF_Font is clunky
struct _TTF_Font;
typedef struct _TTF_Font TTF_Font;
class WordWrapper {
public:
// The font.
TTF_Font *font;
// The maximal width (in pixels if font is not NULL, in characters if font is NULL)
int maxWidth;
// The hyphen used for the hyphenator. If it's empty, the hyphenator is disabled.
std::string hyphen;
// Enable word wrapping.
bool wordWrap;
// Don't hyphenate the words containing http:// or https://.
bool reserveHyperlinks;
// Don't hyphenate the following reserved words (case sensitive).
std::set<std::string> reservedWords;
+ // Don't hyphenate the words if it contains the following fragments (case sensitive).
+ std::set<std::string> reservedFragments;
+
// The language used for the hyphenator.
// "" means use current language.
std::string hyphenatorLanguage;
private:
// Calculate text width (in pixels if font is not NULL, in characters if font is NULL).
int getTextWidth(const std::string& s);
// Calculate glyph width (in pixels if font is not NULL, in characters if font is NULL).
int getGlyphWidth(int ch);
// Check if a word is reserved.
bool isReserved(const std::string& word);
// Internal function.
void addWord(std::vector<std::string>& output, std::string& line, int& lineWidth, const std::string& spaces, const std::string& nonSpaces);
public:
WordWrapper();
~WordWrapper();
// Add a string (possible multiline) to output.
void addString(std::vector<std::string>& output, const std::string& input);
// Add a single line string (subject to wordwrap) to output.
void addLine(std::vector<std::string>& output, const std::string& input);
// Add a list of lines (assume each line is a single line string, subject to wordwrap) to output.
void addLines(std::vector<std::string>& output, const std::vector<std::string>& input);
};
#endif
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, May 11, 2:54 PM (5 d, 12 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
62789
Default Alt Text
(11 KB)
Attached To
Mode
R79 meandmyshadow
Attached
Detach File
Event Timeline