mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
[reader] Foundation of latex handling
This commit is contained in:
@@ -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
124
apps/reader/tex_parser.cpp
Normal 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
31
apps/reader/tex_parser.h
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user