[reader] Foundation of latex handling

This commit is contained in:
Laury
2021-10-22 21:23:35 +02:00
parent 9c9758fcb6
commit 73f2a7ecac
6 changed files with 225 additions and 25 deletions

View File

@@ -7,6 +7,7 @@ app_sreader_src = $(addprefix apps/reader/,\
utility.cpp \
read_book_controller \
word_wrap_view.cpp \
tex_parser.cpp \
)
apps_src += $(app_sreader_src)

124
apps/reader/tex_parser.cpp Normal file
View File

@@ -0,0 +1,124 @@
#include "tex_parser.h"
#include <ion/unicode/utf8_decoder.h>
namespace Reader {
TexParser::TexParser(const char * text, const char * endOfText) :
m_text(text),
m_endOfText(endOfText),
m_hasError(false)
{
}
Layout TexParser::getLayout() {
HorizontalLayout layout = HorizontalLayout::Builder();
const char * start = m_text;
while (m_text < m_endOfText) {
if (*m_text == '\\') {
if (start != m_text) {
layout.addOrMergeChildAtIndex(LayoutHelper::String(start, m_text - start), layout.numberOfChildren(), false);
}
m_text ++;
layout.addOrMergeChildAtIndex(popCommand(), layout.numberOfChildren(), false);
start = m_text;
}
else if (*m_text == " ") {
if (start != m_text) {
layout.addOrMergeChildAtIndex(LayoutHelper::String(start, m_text - start), layout.numberOfChildren(), false);
}
m_text ++;
start = m_text;
}
else if (*m_text == "^") {
if (start != m_text) {
layout.addOrMergeChildAtIndex(LayoutHelper::String(start, m_text - start), layout.numberOfChildren(), false);
}
m_text ++;
layout.addOrMergeChildAtIndex(popCommand(), layout.numberOfChildren(), false);
start = m_text;
}
else {
m_text ++;
}
}
if (start != m_text) {
layout.addOrMergeChildAtIndex(LayoutHelper::String(start, m_text - start), layout.numberOfChildren(), false);
}
if (m_hasError) {
return CodePointLayout::Builder(CodePoint(0xFFD));
}
return layout;
}
Layout TexParser::popBlock() {
while (*m_text == ' ') {
m_text ++;
}
if (*m_text == '{') {
m_text ++;
return popText('}');
}
if (*m_text == '\\') {
m_text ++;
return popCommand();
}
if (m_text >= m_endOfText) {
m_hasError = true;
}
UTF8Decoder decoder(m_text);
m_text ++;
return CodePointLayout::Builder(decoder.nextCodePoint());
}
Layout TexParser::popText(char stop) {
if (*m_text == '\\') {
m_text ++;
return popCommand();
}
HorizontalLayout layout = HorizontalLayout::Builder();
const char * start = m_text;
while (m_text < m_endOfText) {
if (*m_text == '\\') {
layout.addOrMergeChildAtIndex(LayoutHelper::String(start, m_text - start), layout.numberOfChildren(), false);
m_text ++;
layout.addOrMergeChildAtIndex(popCommand(), layout.numberOfChildren(), false);
start = m_text;
}
m_text ++;
}
if (start != m_text) {
layout.addOrMergeChildAtIndex(LayoutHelper::String(start, m_text - start), layout.numberOfChildren(), false);
}
return layout;
}
Layout TexParser::popCommand() {
if (strncmp(k_fracCommand, m_text, strlen(k_fracCommand)) == 0) {
m_text += strlen(k_fracCommand);
if (*m_text == ' ' || *m_text == '{') {
return popFracCommand();
}
}
return popFracCommand();
}
Layout TexParser::popFracCommand() {
Layout firstBlock = popBlock();
Layout secondBlock = popBlock();
return FractionLayout::Builder(firstBlock, secondBlock);
}
}

31
apps/reader/tex_parser.h Normal file
View File

@@ -0,0 +1,31 @@
#ifndef __TEX_PARSER_H__
#define __TEX_PARSER_H__
#include <poincare_layouts.h>
#include <poincare/layout_helper.h>
#include <string.h>
using namespace Poincare;
namespace Reader
{
/// @brief Class used in the WordWrapTextView class to parse a Tex expression
class TexParser {
public:
TexParser(const char * text, const char * endOfText);
Layout getLayout();
private:
Layout popBlock();
Layout popText(char stop);
Layout popCommand();
Layout popFracCommand();
const char * m_text;
const char * m_endOfText;
bool m_hasError;
static constexpr char const * k_fracCommand = "frac";
};
}
#endif

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 != '%') {
while (codePoint != '\n' && codePoint != ' ' && codePoint != '%' && codePoint != '$') {
result = decoder.stringPosition();
if (result >= end) {
break;
@@ -127,4 +127,21 @@ const char * EndOfPrintableWord(const char * word, const char * end) {
return result;
}
const char * StartOfPrintableWord(const char * word, const char * start) {
if (word == start) {
return word;
}
UTF8Decoder decoder(word);
CodePoint codePoint = decoder.previousCodePoint();
const char * result = word;
while (codePoint != '\n' && codePoint != ' ' && codePoint != '%' && codePoint != '$') {
result = decoder.stringPosition();
if (result >= start) {
break;
}
codePoint = decoder.previousCodePoint();
}
return result;
}
}

View File

@@ -12,6 +12,7 @@ bool stringEndsWith(const char* str, const char* end);
int filesWithExtension(const char* extension, External::Archive::File* files, int filesSize);
void stringNCopy(char* dest, int max, const char* src, int len);
const char * EndOfPrintableWord(const char * word, const char * end);
const char * StartOfPrintableWord(const char * word, const char * start);
}
#endif

View File

@@ -1,6 +1,7 @@
#include "word_wrap_view.h"
#include "utility.h"
#include "tex_parser.h"
#include <poincare/expression.h>
#include "../shared/poincare_helpers.h"
#include <poincare/undefined.h>
@@ -38,16 +39,17 @@ void WordWrapTextView::previousPage() {
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 - 1;
const char * startOfWord = UTF8Helper::BeginningOfWord(text(), endOfWord);
const char * startOfWord = StartOfPrintableWord(endOfWord, text());
KDSize textSize = KDSizeZero;
KDPoint textEndPosition(m_frame.width() - k_margin, m_frame.height() - k_margin);
while(startOfWord>=text()) {
startOfWord = UTF8Helper::BeginningOfWord(text(), endOfWord);
endOfWord = UTF8Helper::EndOfWord(startOfWord);
startOfWord = StartOfPrintableWord(endOfWord, text());
endOfWord = EndOfPrintableWord(startOfWord, endOfFile);
if (*startOfWord == '%') {
if (updateTextColorBackward(startOfWord)) {
@@ -55,20 +57,28 @@ void WordWrapTextView::previousPage() {
continue;
}
}
if (*startOfWord == '$' && *(endOfWord-1) == '$') {
const int wordMaxLength = 128;
char word[wordMaxLength];
stringNCopy(word, wordMaxLength, startOfWord + 1, endOfWord-startOfWord-2);
Poincare::Expression expr = Poincare::Expression::Parse(word, nullptr);
if (expr.isUninitialized()) {
expr = Poincare::Undefined::Builder();
if (*endOfWord == '$') {
startOfWord = endOfWord - 1;
while (*startOfWord != '$') {
if (startOfWord < text()) {
break; // File isn't rightly formated
}
startOfWord --;
}
Poincare::Layout layout = Shared::PoincareHelpers::CreateLayout(expr);
textSize = layout.layoutSize();
startOfWord --;
TexParser parser = TexParser(startOfWord + 1, endOfWord - 2);
Poincare::Layout layout = parser.getLayout();
textSize = layout.layoutSize();
}
else {
textSize = m_font->stringSizeUntil(startOfWord, endOfWord);
if (*startOfWord == '\\' || *(startOfWord + 1) == '$') {
textSize = m_font->stringSizeUntil(startOfWord + 1, endOfWord);
}
else {
textSize = m_font->stringSizeUntil(startOfWord, endOfWord);
}
}
KDPoint textStartPosition = KDPoint(textEndPosition.x()-textSize.width(), textEndPosition.y());
@@ -109,7 +119,7 @@ void WordWrapTextView::previousPage() {
m_pageOffset = 0;
}
else {
m_pageOffset = UTF8Helper::EndOfWord(startOfWord) - text() + 1;
m_pageOffset = EndOfPrintableWord(startOfWord, endOfFile) - text() + 1;
}
markRectAsDirty(bounds());
}
@@ -119,7 +129,12 @@ void WordWrapTextView::drawRect(KDContext * ctx, KDRect rect) const {
const char * endOfFile = text() + m_length;
const char * startOfWord = text() + m_pageOffset;
const char * endOfWord = EndOfPrintableWord(startOfWord, endOfFile);
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;
@@ -152,18 +167,26 @@ void WordWrapTextView::drawRect(KDContext * ctx, KDRect rect) const {
continue;
}
}
if (*startOfWord == '$' && *(endOfWord-1) == '$') { // Look for expression
stringNCopy(word, wordMaxLength, startOfWord + 1, endOfWord-startOfWord-2);
Poincare::Expression expr = Poincare::Expression::Parse(word, nullptr);
if (expr.isUninitialized()) {
expr = Poincare::Undefined::Builder();
if (*startOfWord == '$') { // Look for expression
endOfWord = startOfWord + 1;
while (*endOfWord != '$') {
if (endOfWord > endOfFile) {
break; // If we are here, it's bad...
}
endOfWord ++;
}
layout = Shared::PoincareHelpers::CreateLayout(expr);
endOfWord ++;
TexParser parser = TexParser(startOfWord + 1, endOfWord - 1);
layout = parser.getLayout();
textSize = layout.layoutSize();
toDraw = ToDraw::Expression;
}
else {
if (*startOfWord == '\\' || *(startOfWord + 1) == '$') {
startOfWord ++;
}
textSize = m_font->stringSizeUntil(startOfWord, endOfWord);
stringNCopy(word, wordMaxLength, startOfWord, endOfWord-startOfWord);
toDraw = ToDraw::Text;
@@ -222,7 +245,10 @@ void WordWrapTextView::drawRect(KDContext * ctx, KDRect rect) const {
}
textPosition = nextTextPosition;
endOfWord = EndOfPrintableWord(startOfWord+1, endOfFile);
if (*startOfWord != '$') {
endOfWord = EndOfPrintableWord(startOfWord+1, endOfFile);
} // Else we don't need to update endOfWord
}
m_nextPageOffset = startOfWord - text();