[reader] Make reader works !

This commit is contained in:
Laury
2022-01-09 21:46:39 +01:00
parent 64a90422c4
commit 685e73c52d
5 changed files with 295 additions and 163 deletions

View File

@@ -117,7 +117,7 @@ const char * EndOfPrintableWord(const char * word, const char * end) {
UTF8Decoder decoder(word);
CodePoint codePoint = decoder.nextCodePoint();
const char * result = word;
while (codePoint != '\n' && codePoint != ' ' && codePoint != '%' && codePoint != '$') {
while (codePoint != '\n' && codePoint != ' ' && codePoint != '%' && codePoint != '$' && codePoint != '\\') {
result = decoder.stringPosition();
if (result >= end) {
break;

View File

@@ -14,15 +14,20 @@ WordWrapTextView::WordWrapTextView() :
m_pageOffset(0),
m_nextPageOffset(0),
m_length(0),
m_isRichTextFile(false) // Value isn't important, it will change when the file is loaded
m_isRichTextFile(false), // Value isn't important, it will change when the file is loaded
m_lastPagesOffsetsIndex(0)
{
for (int i = 0; i < k_lastOffsetsBufferSize; i++) {
m_lastPagesOffsets[i] = -1; // -1 Means : no informations
}
}
void WordWrapTextView::nextPage() {
if(m_nextPageOffset >= m_length) {
return;
}
m_lastPagesOffsets[m_lastPagesOffsetsIndex] = m_pageOffset;
m_lastPagesOffsetsIndex = (m_lastPagesOffsetsIndex + 1) % k_lastOffsetsBufferSize;
m_pageOffset = m_nextPageOffset;
markRectAsDirty(bounds());
}
@@ -38,223 +43,342 @@ void WordWrapTextView::previousPage() {
return;
}
/* We check if we have available data in our buffer */
int offsetToCheck = (m_lastPagesOffsetsIndex + k_lastOffsetsBufferSize - 1) % k_lastOffsetsBufferSize;
if (m_lastPagesOffsets[offsetToCheck] != -1) {
m_lastPagesOffsetsIndex = offsetToCheck;
m_pageOffset = m_lastPagesOffsets[offsetToCheck];
m_lastPagesOffsets[offsetToCheck] = -1;
markRectAsDirty(bounds());
return;
}
const int charWidth = m_font->glyphSize().width();
const int charHeight = m_font->glyphSize().height();
const char * endOfFile = text() + m_length;
const char * endOfWord = text() + m_pageOffset;
const char * startOfWord = StartOfPrintableWord(endOfWord, text());
const char * endOfWord = text() + m_pageOffset - 1;
KDSize textSize = KDSizeZero;
KDCoordinate baseline = charHeight;
KDPoint textEndPosition(m_frame.width() - k_margin, m_frame.height() - k_margin);
KDPoint textBottomEndPosition = KDPoint(m_frame.width() - k_margin, m_frame.height() - k_margin);
KDCoordinate lineHeight = charHeight;
while(startOfWord>=text()) {
startOfWord = StartOfPrintableWord(endOfWord-1, text());
//endOfWord = EndOfPrintableWord(startOfWord, endOfFile);
while(endOfWord >= text()) {
// 1. Skip whitespaces and line jumps
while(endOfWord >= text() && (*endOfWord == ' ' || *endOfWord == '\n')) {
if(*endOfWord == '\n') {
textBottomEndPosition = KDPoint(m_frame.width() - k_margin, textBottomEndPosition.y() - lineHeight);
lineHeight = charHeight;
// We check if we must change page
if (textBottomEndPosition.y() - lineHeight <= k_margin) {
break;
}
} else {
textBottomEndPosition = KDPoint(textBottomEndPosition.x() - charWidth, textBottomEndPosition.y());
}
endOfWord--;
}
// 3. If word is a color change
if (*endOfWord == '%' && *(endOfWord - 1) != '\\') {
const char * startOfWord = endOfWord - 2;
while (*startOfWord != '%') {
startOfWord--;
}
if (*startOfWord == '%') {
if (updateTextColorBackward(startOfWord)) {
endOfWord = startOfWord - 1;
endOfWord = startOfWord - 1; // Update next endOfWord
continue;
} else {
// TODO: print error
}
}
if (*endOfWord == '$') {
startOfWord = endOfWord - 1;
while (*startOfWord != '$') {
if (startOfWord < text()) {
KDSize textSize = KDSizeZero;
// 4. If word is a mathematical expression
if (*endOfWord == '$' && *(endOfWord - 1) != '\\') {
// We go to the end of the expression + 1
const char * expressionStart = --endOfWord;
while (*expressionStart != '$') {
if (expressionStart < text()) {
break; // File isn't rightly formated
}
startOfWord --;
expressionStart ++;
}
startOfWord --;
TexParser parser = TexParser(startOfWord + 1, endOfWord - 2);
Poincare::Layout layout = parser.getLayout();
textSize = layout.layoutSize();
TexParser parser = TexParser(expressionStart, endOfWord);
Layout layout = parser.getLayout();
KDCoordinate layoutBaseline = layout.baseline();
// We check if we must change baseline
if (layoutBaseline > baseline) {
baseline = layoutBaseline;
}
KDSize layoutSize = layout.layoutSize();
textSize = KDSize(layoutSize.width(), layoutSize.height() + baseline - layoutBaseline);
endOfWord = expressionStart;
}
// 5. Else it's text
else {
if (*startOfWord == '\\' || *(startOfWord + 1) == '$') {
textSize = m_font->stringSizeUntil(startOfWord + 1, endOfWord);
}
else {
textSize = m_font->stringSizeUntil(startOfWord, endOfWord);
// We go to the start of the word
const char * startOfWord = StartOfPrintableWord(endOfWord, text());
textSize = m_font->stringSizeUntil(startOfWord, endOfWord + 1);
endOfWord = startOfWord;
}
// 6. We check if we must change line
if (textBottomEndPosition.x() - textSize.width() <= k_margin) {
textBottomEndPosition = KDPoint(m_frame.width() - k_margin, textBottomEndPosition.y() - lineHeight);
lineHeight = 0;
// We will check if we must change page below
}
textBottomEndPosition = KDPoint(textBottomEndPosition.x() - textSize.width(), textBottomEndPosition.y());
// 7. We update height of the line if needed
if (textSize.height() > lineHeight) {
lineHeight = textSize.height();
// We check if we must change page
if (textBottomEndPosition.y() - lineHeight <= k_margin) {
break;
}
}
KDPoint textStartPosition = KDPoint(textEndPosition.x()-textSize.width(), textEndPosition.y());
if(textStartPosition.x() < k_margin) {
textEndPosition = KDPoint(m_frame.width() - k_margin, textEndPosition.y() - charHeight);
textStartPosition = KDPoint(textEndPosition.x() - textSize.width(), textEndPosition.y());
}
if(textEndPosition.y() - textSize.height() < k_margin) {
break;
}
--startOfWord;
while(startOfWord >= text() && (*startOfWord == ' ' || *startOfWord == '\n')) {
if(*startOfWord == ' ') {
textStartPosition = KDPoint(textStartPosition.x() - charWidth, textStartPosition.y());
}
else {
textStartPosition = KDPoint(m_frame.width() - k_margin, textStartPosition.y() - charHeight);
}
--startOfWord;
}
if(textStartPosition.y() < k_margin) { // If out of page, quit
break;
}
if(textStartPosition.y() != textEndPosition.y()) { // If line changed, x is at start of line
textStartPosition = KDPoint(m_frame.width() - k_margin, textStartPosition.y());
}
if(textStartPosition.x() < k_margin) { // Go to line if left overflow
textStartPosition = KDPoint(m_frame.width() - k_margin, textStartPosition.y() - charHeight);
}
textEndPosition = textStartPosition;
endOfWord = startOfWord + 1;
endOfWord -= 1;
}
if(startOfWord + 1 == text()) {
m_pageOffset = 0;
}
else {
m_pageOffset = EndOfPrintableWord(startOfWord, endOfFile) - text() + 1;
if (endOfWord + 1 == text()) {
m_pageOffset = 0;
} else {
m_pageOffset = endOfWord - text();
}
// Because we ask for a redraw, m_endTextPosition must auto update at the bottom of drawRect...
markRectAsDirty(bounds());
}
void WordWrapTextView::drawRect(KDContext * ctx, KDRect rect) const {
ctx->fillRect(KDRect(0, 0, bounds().width(), bounds().height()), m_backgroundColor);
const char * endOfFile = text() + m_length;
const char * startOfWord = text() + m_pageOffset;
const char * endOfWord;
if (*startOfWord != '$') {
endOfWord = EndOfPrintableWord(startOfWord, endOfFile);
} // Else we don't need to update endOfWord
KDPoint textPosition(k_margin, k_margin);
const int wordMaxLength = 128;
char word[wordMaxLength];
Poincare::Layout layout;
enum class ToDraw {
Text,
Expression,
Nothing
Expression
};
ToDraw toDraw = ToDraw::Text;
bool endOfPage = false;
const char * endOfFile = text() + m_length;
const char * startOfWord = text() + m_pageOffset;
const int charWidth = m_font->glyphSize().width();
const int charHeight = m_font->glyphSize().height();
int nextLineOffset = charHeight;
const int wordMaxLength = (m_frame.width() - 2*k_margin ) / charWidth;
char word[wordMaxLength];
KDSize textSize = KDSizeZero;
Layout layout;
KDPoint textPosition = KDPoint(k_margin, k_margin);
while (!endOfPage && startOfWord < endOfFile) {
// We process line by line
while(startOfWord < endOfFile) {
const char * firstReadIndex = startOfWord;
if (*startOfWord == '%') { // Look for color keyword (ex '%bl%')
if (updateTextColorForward(startOfWord)) {
startOfWord = endOfWord + 1;
endOfWord = EndOfPrintableWord(startOfWord, endOfFile);
// 1. We compute the size of what we are going to draw and the baseline
KDSize lineSize = KDSize(0, charHeight);
KDCoordinate baseline = charHeight / 2;
while (firstReadIndex < endOfFile) {
KDSize textSize = KDSizeZero;
// 1.1. And we check if we are at the end of the line
if(*firstReadIndex == '\n') {
break;
}
// 1.2. Check if we are in a color change
if (*firstReadIndex == '%') { // We assume each '%' non-escaped is announcing a color change // TODO : check file is rightly formated
// We go to the end of the color change + 1
do {
firstReadIndex ++;
} while (*firstReadIndex != '%');
firstReadIndex ++;
continue;
}
// 1.3. Check if we are in a math expression
if (*firstReadIndex == '$') {
// We go to the end of the expression + 1
const char * expressionStart = ++firstReadIndex;
while (*firstReadIndex != '$') {
if (firstReadIndex > endOfFile) {
break; // File isn't rightly formated
}
firstReadIndex ++;
}
TexParser parser = TexParser(expressionStart, firstReadIndex);
Layout layout = parser.getLayout();
KDCoordinate layoutBaseline = layout.baseline();
// We check if we must change baseline
if (layoutBaseline > baseline) {
baseline = layoutBaseline;
}
KDSize layoutSize = layout.layoutSize();
textSize = KDSize(layoutSize.width(), layoutSize.height() + baseline - layoutBaseline);
firstReadIndex ++;
}
// 1.4. Else it's text
else {
if ((*firstReadIndex == '\\' && *(firstReadIndex + 1) == '$') || (*firstReadIndex == '\\' && *(firstReadIndex + 1) == '%')) { // We escape '$' and '%' if needed
firstReadIndex ++;
}
const char * endOfWord = EndOfPrintableWord(firstReadIndex + 1, endOfFile);
textSize = m_font->stringSizeUntil(firstReadIndex, endOfWord);
firstReadIndex = endOfWord;
}
// 1.5. We update size
int newWidth = lineSize.width() + textSize.width();
// We check if the new text fit on the line
if (newWidth > m_frame.width() - 2 * k_margin) {
break;
}
int newHeight;
if (lineSize.height() > textSize.height()) {
newHeight = lineSize.height();
} else {
newHeight = textSize.height();
// We check if all the content can be displayed
if (textPosition.y() + newHeight > bounds().height() - k_margin) {
endOfPage = true;
break;
}
}
lineSize = KDSize(lineSize.width() + textSize.width(), newHeight);
// 1.6. We go to the next word
while (*firstReadIndex == ' ') {
lineSize = KDSize(lineSize.width() + charWidth, lineSize.height());
++firstReadIndex;
}
}
if (*startOfWord == '$') { // Look for expression
endOfWord = startOfWord + 1;
while (*endOfWord != '$') {
if (endOfWord > endOfFile) {
break; // If we are here, it's bad...
if (endOfPage) {
break;
}
// 2. And now... we read the line again to draw it !
while (startOfWord < endOfFile) {
//2.1. We check if we are at the end of the line
if (*startOfWord == '\n') {
startOfWord++;
textPosition = KDPoint(k_margin, textPosition.y() + lineSize.height());
break;
// We aren't supposed to be at the end of the page, else the loop on top would have stopped drawing
}
const char * endOfWord;
// 2.2. Check if we are in a color change
if (*startOfWord == '%') {
if (updateTextColorForward(startOfWord)) {
startOfWord += 2; // We can add at least 2 ('%' + the color first char)
while (*startOfWord != '%') {
startOfWord ++;
}
startOfWord ++;
continue;
}
else {
// TODO: Print exception
}
}
// 2.3. Check what we are going to draw and his size
KDSize textSize = KDSizeZero;
ToDraw toDraw;
// 2.3.1. Check if we are in a math expression
if (*startOfWord == '$') {
endOfWord = startOfWord + 1;
while (*endOfWord != '$') {
if (endOfWord > endOfFile) {
break; // File isn't rightly formated
}
endOfWord ++;
}
endOfWord ++;
TexParser parser = TexParser(startOfWord + 1, endOfWord - 1);
layout = parser.getLayout();
textSize = layout.layoutSize();
toDraw = ToDraw::Expression;
}
endOfWord ++;
TexParser parser = TexParser(startOfWord + 1, endOfWord - 1);
layout = parser.getLayout();
textSize = layout.layoutSize();
toDraw = ToDraw::Expression;
}
else {
if (*startOfWord == '\\' || *(startOfWord + 1) == '$') {
startOfWord ++;
// 2.3.2 Else it's text
else {
if ((*startOfWord == '\\' && *(startOfWord + 1) == '$') || (*startOfWord == '\\' && *(startOfWord + 1) == '%')) {
startOfWord ++;
}
endOfWord = EndOfPrintableWord(startOfWord + 1, endOfFile);
textSize = m_font->stringSizeUntil(startOfWord, endOfWord);
stringNCopy(word, wordMaxLength, startOfWord, endOfWord-startOfWord);
toDraw = ToDraw::Text;
}
textSize = m_font->stringSizeUntil(startOfWord, endOfWord);
stringNCopy(word, wordMaxLength, startOfWord, endOfWord-startOfWord);
toDraw = ToDraw::Text;
}
KDPoint nextTextPosition = KDPoint(textPosition.x()+textSize.width(), textPosition.y());
if(nextTextPosition.x() > m_frame.width() - k_margin) { // Right overflow
textPosition = KDPoint(k_margin, textPosition.y() + nextLineOffset);
nextTextPosition = KDPoint(k_margin + textSize.width(), textPosition.y());
nextLineOffset = charHeight;
}
if (nextLineOffset < textSize.height()) {
nextLineOffset = textSize.height();
}
// 2.4 We decide where to draw and if we must change line
KDPoint endTextPosition = KDPoint(textPosition.x() + textSize.width(), textPosition.y());
// 2.4.1. Check if we need to go to the next line
if(endTextPosition.x() > m_frame.width() - k_margin) {
textPosition = KDPoint(k_margin, textPosition.y() + lineSize.height());
break;
}
if(textPosition.y() + textSize.height() > m_frame.height() - k_margin) { // Bottom overflow
break;
}
if (toDraw == ToDraw::Expression) {
layout.draw(ctx, textPosition, m_textColor, m_backgroundColor);
}
else if (toDraw == ToDraw::Text) {
ctx->drawString(word, textPosition, m_font, m_textColor, m_backgroundColor);
}
while(*endOfWord == ' ' || *endOfWord == '\n') {
if(*endOfWord == ' ') {
nextTextPosition = KDPoint(nextTextPosition.x() + charWidth, nextTextPosition.y());
// 2.5. Now we draw !
if (toDraw == ToDraw::Expression) {
KDPoint position = KDPoint(textPosition.x(), textPosition.y() + baseline - layout.baseline());
layout.draw(ctx, position, m_textColor, m_backgroundColor);
}
else {
nextTextPosition = KDPoint(k_margin, nextTextPosition.y() + nextLineOffset);
nextLineOffset = charHeight;
KDPoint position = KDPoint(textPosition.x(), textPosition.y() + baseline - charHeight / 2);
ctx->drawString(word, position, m_font, m_textColor, m_backgroundColor);
}
++endOfWord;
}
//We must change value of startOfWord now to avoid having
//two times the same word if the break below is used
startOfWord = endOfWord;
// 2.6. Update the position
textPosition = endTextPosition;
if (endOfWord >= endOfFile) {
break;
}
if(nextTextPosition.y() + textSize.height() > m_frame.height() - k_margin) { // If out of page, quit
break;
}
if(nextTextPosition.y() != textPosition.y()) { // If line changed, x is at start of line
nextTextPosition = KDPoint(k_margin, nextTextPosition.y());
}
if(nextTextPosition.x() > m_frame.width() - k_margin) { // Go to line if right overflow
nextTextPosition = KDPoint(k_margin, nextTextPosition.y() + nextLineOffset);
nextLineOffset = charHeight;
}
textPosition = nextTextPosition;
if (*startOfWord != '$') {
endOfWord = EndOfPrintableWord(startOfWord+1, endOfFile);
} // Else we don't need to update endOfWord
// 2.7. And we go to the next word
while (*endOfWord == ' ') {
endOfWord++;
textPosition = KDPoint(textPosition.x() + charWidth, textPosition.y());
}
startOfWord = endOfWord;
}
}
m_nextPageOffset = startOfWord - text();
};
}
BookSave WordWrapTextView::getBookSave() const {
return {
@@ -270,9 +394,9 @@ void WordWrapTextView::setBookSave(BookSave save) {
bool WordWrapTextView::updateTextColorForward(const char * colorStart) const {
if (*(colorStart + 1) == '\\') {
if (*(colorStart + 1) == '\\' && (*(colorStart + 3) == '%' || *(colorStart + 4) == '%')) {
m_textColor = Palette::PrimaryText;
return (*(colorStart + 3) == '%' || *(colorStart + 4) == '%');
return true;
}
int keySize = 1;

View File

@@ -25,11 +25,19 @@ private:
bool updateTextColorForward(const char * colorStart) const;
bool updateTextColorBackward(const char * colorStart) const;
static const int k_margin = 10;
static const int k_lastOffsetsBufferSize = 10;
int m_pageOffset;
mutable int m_nextPageOffset;
int m_length;
bool m_isRichTextFile;
mutable KDColor m_textColor;
/*
* Beacause the text that we draw can be of different sizes, we can't
* exactly know where the last page starts.
* So we store into a buffer (a cyclic stack) the offsets of the last pages.
*/
int m_lastPagesOffsets[k_lastOffsetsBufferSize];
int m_lastPagesOffsetsIndex;
};
}

Binary file not shown.

Binary file not shown.