From fdef2b5d6e6a64235ebb43f2a5f8c6aa349d0ed8 Mon Sep 17 00:00:00 2001 From: Mino1289 Date: Tue, 14 Dec 2021 22:51:15 +0100 Subject: [PATCH] Merge branch 'upsilon-dev-latex' --- Makefile | 4 + apps/reader/Makefile | 5 +- apps/reader/README.md | 4 +- apps/reader/TexParser.html | 1901 +++++++++++++++++++++ apps/reader/TexParser.md | 51 + apps/reader/tex_parser.cpp | 268 +++ apps/reader/tex_parser.h | 57 + apps/reader/utility.cpp | 19 +- apps/reader/utility.h | 1 + apps/reader/word_wrap_view.cpp | 78 +- build/targets.device.mak | 4 +- build/targets.device.n0110.mak | 2 +- build/targets.mak | 2 +- kandinsky/Makefile | 53 +- kandinsky/fonts/code_points.h | 204 ++- kandinsky/fonts/rasterizer.c | 28 +- kandinsky/src/font.cpp | 2 +- poincare/Makefile | 1 + poincare/include/poincare/layout_cursor.h | 1 + poincare/include/poincare/layout_node.h | 1 + poincare/include/poincare/vector_layout.h | 53 + poincare/include/poincare_layouts.h | 1 + poincare/src/tree_handle.cpp | 1 + poincare/src/vector_layout.cpp | 78 + 24 files changed, 2763 insertions(+), 56 deletions(-) create mode 100644 apps/reader/TexParser.html create mode 100644 apps/reader/TexParser.md create mode 100644 apps/reader/tex_parser.cpp create mode 100644 apps/reader/tex_parser.h create mode 100644 poincare/include/poincare/vector_layout.h create mode 100644 poincare/src/vector_layout.cpp diff --git a/Makefile b/Makefile index d3b59008c..1d9969a62 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,10 @@ ifeq (${MODEL}, n0100) endif endif +ifeq ($(filter reader,$(apps_list)),) + HAS_READER := 1 +endif + ifeq (${MODEL}, n0110) apps_list = ${EPSILON_APPS} else diff --git a/apps/reader/Makefile b/apps/reader/Makefile index f786e1510..0af0aefdf 100644 --- a/apps/reader/Makefile +++ b/apps/reader/Makefile @@ -1,12 +1,15 @@ apps += Reader::App app_headers += apps/reader/app.h +SFLAGS += -DHAS_READER + app_sreader_src = $(addprefix apps/reader/,\ app.cpp \ list_book_controller.cpp \ utility.cpp \ read_book_controller \ word_wrap_view.cpp \ + tex_parser.cpp \ ) apps_src += $(app_sreader_src) @@ -15,4 +18,4 @@ app_images += apps/reader/reader_icon.png i18n_files += $(call i18n_without_universal_for,reader/base) -$(eval $(call depends_on_image,apps/reader/app.cpp,apps/reader/reader_icon.png)) \ No newline at end of file +$(eval $(call depends_on_image,apps/reader/app.cpp,apps/reader/reader_icon.png)) diff --git a/apps/reader/README.md b/apps/reader/README.md index 1b160f6f4..1405f99e8 100644 --- a/apps/reader/README.md +++ b/apps/reader/README.md @@ -6,8 +6,10 @@ Thanks to [Gabriel79](https://github.com/Gabriel79) for the original reader app, # Rich text format Reader app supports now a rich text format : - * `$` around a mathematical expression **without spaces** to render it + * `$` around a LaTeX expression to render it * `%` around a color-code (see below) to change the color of the text +### LaTeX expressions +You can read the documentation for the LaTeX Parser [here](TexParser.md). ### Color codes : |code|color| | --:| ---:| diff --git a/apps/reader/TexParser.html b/apps/reader/TexParser.html new file mode 100644 index 000000000..fdf7fcbac --- /dev/null +++ b/apps/reader/TexParser.html @@ -0,0 +1,1901 @@ + + + + + TexParser + + + + + + + + +
+

LaTeX Parser

+ +

In the reader app, you can read a txt file. You can also read a txt file with LaTeX expression inside of it.

+

All the symbols you can use are listed here :

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CommandOutputCommandOutput
MathExpressions
\frac{ab}{cd} + + + + + a + b + + + c + d + + + + \frac{ab}{cd} + + \sqrt[n]{x} + + + + x + n + + + \sqrt[n]{x} + +
MathSymbols
\times + + + × + + \times + + \div + + + ÷ + + \div + +
\forall + + + + + \forall + + \exists + + + + + \exists + +
\partial + + + + + \partial + + \pm + + + ± + + \pm + +
\infty + + + + + \infty + + \approx + + + + + \approx + +
\neq + + + + + \neq + + \equiv + + + + + \equiv + +
\leq + + + + + \leq + + \geq + + + + + \geq + +
SimpleArrowsDoubleArrows
\leftarrow + + + + + \leftarrow + + \Leftarrow + + + + + \Leftarrow + +
\rightarrow + + + + + \rightarrow + + \Rightarrow + + + + + \Rightarrow + +
\uparrow + + + + + \uparrow + + \Uparrow + + + + + \Uparrow + +
\downarrow + + + + + \downarrow + + \Downarrow + + + + + \Downarrow + +
\leftrightarrow + + + + + \leftrightarrow + +
\updownarrow + + + + + \updownarrow + +
Greek CapitalLettersGreek SmallLetters
\Alpha + + + A + + \Alpha + + \alpha + + + α + + \alpha + +
\Beta + + + B + + \Beta + + \beta + + + β + + \beta + +
\Gamma + + + Γ + + \Gamma + + \gamma + + + γ + + \gamma + +
\Delta + + + Δ + + \Delta + + \delta + + + δ + + \delta + +
\Epsilon + + + E + + \Epsilon + + \epsilon + + + ϵ + + \epsilon + +
\Zeta + + + Z + + \Zeta + + \zeta + + + ζ + + \zeta + +
\Eta + + + H + + \Eta + + \eta + + + η + + \eta + +
\Theta + + + Θ + + \Theta + + \theta + + + θ + + \theta + +
\Iota + + + I + + \Iota + + \iota + + + ι + + \iota + +
\Kappa + + + K + + \Kappa + + \kappa + + + κ + + \kappa + +
\Lambda + + + Λ + + \Lambda + + \lambda + + + λ + + \lambda + +
\Mu + + + M + + \Mu + + \mu + + + μ + + \mu + +
\Nu + + + N + + \Nu + + \nu + + + ν + + \nu + +
\Xi + + + Ξ + + \Xi + + \xi + + + ξ + + \xi + +
\Omicron + + + O + + \Omicron + +
\Pi + + + Π + + \Pi + + \pi + + + π + + \pi + +
\Rho + + + P + + \Rho + + \rho + + + ρ + + \rho + +
\Sigma + + + Σ + + \Sigma + + \sigma + + + σ + + \sigma + +
\Tau + + + T + + \Tau + + \tau + + + τ + + \tau + +
\Upsilon + + + Υ + + \Upsilon + + \upsilon + + + υ + + \upsilon + +
\Phi + + + Φ + + \Phi + + \phi + + + ϕ + + \phi + +
\Chi + + + X + + \Chi + + \chi + + + χ + + \chi + +
\Psi + + + Ψ + + \Psi + + \psi + + + ψ + + \psi + +
\Omega + + + Ω + + \Omega + + \omega + + + ω + + \omega + +
+ +
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/reader/TexParser.md b/apps/reader/TexParser.md new file mode 100644 index 000000000..b530498cd --- /dev/null +++ b/apps/reader/TexParser.md @@ -0,0 +1,51 @@ +# LaTeX Parser + +In the reader app, you can read a txt file. You can also read a txt file with LaTeX expression inside of it. + +All the symbols you can use are listed here : + +|Command|Output||Command|Output| +|--:|:--:|--:|--:|:--:| +|Math|Expressions|||| +|`\frac{ab}{cd}`|$\frac{ab}{cd}$||`\sqrt[n]{x}`|$\sqrt[n]{x}$| +|Math|Symbols||| +|`\times`|$\times$||`\div`|$\div$| +|`\forall`|$\forall$||`\exists`|$\exists$| +|`\partial`|$\partial$||`\pm`|$\pm$| +|`\infty`|$\infty$||`\approx`|$\approx$| +|`\neq`|$\neq$||`\equiv`|$\equiv$| +|`\leq`|$\leq$||`\geq`|$\geq$| +|Simple|Arrows||Double|Arrows| +|`\leftarrow`|$\leftarrow$||`\Leftarrow`|$\Leftarrow$| +|`\rightarrow`|$\rightarrow$||`\Rightarrow`|$\Rightarrow$| +|`\uparrow`|$\uparrow$||`\Uparrow`|$\Uparrow$| +|`\downarrow`|$\downarrow$||`\Downarrow`|$\Downarrow$| +|`\leftrightarrow`|$\leftrightarrow$|||| +|`\updownarrow`|$\updownarrow$|||| +|Greek Capital|Letters||Greek Small|Letters| +|`\Alpha`|$\Alpha$||`\alpha`|$\alpha$| +|`\Beta`|$\Beta$||`\beta`|$\beta$| +|`\Gamma`|$\Gamma$||`\gamma`|$\gamma$| +|`\Delta`|$\Delta$||`\delta`|$\delta$| +|`\Epsilon`|$\Epsilon$||`\epsilon`|$\epsilon$| +|`\Zeta`|$\Zeta$||`\zeta`|$\zeta$| +|`\Eta`|$\Eta$||`\eta`|$\eta$| +|`\Theta`|$\Theta$||`\theta`|$\theta$| +|`\Iota`|$\Iota$||`\iota`|$\iota$| +|`\Kappa`|$\Kappa$||`\kappa`|$\kappa$| +|`\Lambda`|$\Lambda$||`\lambda`|$\lambda$| +|`\Mu`|$\Mu$||`\mu`|$\mu$| +|`\Nu`|$\Nu$||`\nu`|$\nu$| +|`\Xi`|$\Xi$||`\xi`|$\xi$| +|`\Omicron`|$\Omicron$||| +|`\Pi`|$\Pi$||`\pi`|$\pi$| +|`\Rho`|$\Rho$||`\rho`|$\rho$| +|`\Sigma`|$\Sigma$||`\sigma`|$\sigma$| +|`\Tau`|$\Tau$||`\tau`|$\tau$| +|`\Upsilon`|$\Upsilon$||`\upsilon`|$\upsilon$| +|`\Phi`|$\Phi$||`\phi`|$\phi$| +|`\Chi`|$\Chi$||`\chi`|$\chi$| +|`\Psi`|$\Psi$||`\psi`|$\psi$| +|`\Omega`|$\Omega$||`\omega`|$\omega$| + + diff --git a/apps/reader/tex_parser.cpp b/apps/reader/tex_parser.cpp new file mode 100644 index 000000000..f2b69afee --- /dev/null +++ b/apps/reader/tex_parser.cpp @@ -0,0 +1,268 @@ +#include "tex_parser.h" +#include + +namespace Reader { + + // List of available Symbols + static constexpr char const * k_SymbolsCommands[] = { + "times", "div", "forall", "partial", "exists", "pm", "approx", "infty", "neq", "equiv", "leq", "geq", + "leftarrow", "uparrow", "rightarrow", "downarrow", "leftrightarrow", "updownarrow", "Leftarrow", "Uparrow", "Rightarrow", "Downarrow", + "Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta", "Eta", "Theta", "Iota", "Kappa", "Lambda", + "Mu", "Nu", "Xi", "Omicron", "Pi", "Rho", "Sigma", "Tau", "Upsilon", "Phi", "Chi", "Psi","Omega", + "alpha", "beta", "gamma", "delta", "epsilon", "zeta", "eta", "theta", "iota", "kappa", "lambda", + "mu", "nu", "xi", "omicron", "pi", "rho", "sigma", "tau", "upsilon", "phi", "chi", "psi", "omega", + "sim", + }; + + // List of the available Symbol's CodePoints in the same order of the Symbol's list + static constexpr uint32_t const k_SymbolsCodePoints[] = { + 0xd7, 0xf7, 0x2200, 0x2202, 0x2203, 0xb1, 0x2248, 0x221e, 0x2260, 0x2261, 0x2264, 0x2265, + 0x2190, 0x2191, 0x2192, 0x2193, 0x2194, 0x2195, 0x21d0, 0x21d1, 0x21d2, 0x21d3, + 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, 0x399, 0x39a, 0x39b, + 0x39c, 0x39d, 0x39e, 0x39f, 0x3a0, 0x3a1, 0x3a3, 0x3a4, 0x3a5, 0x3a6, 0x3a7, 0x3a8, 0x3a9, + 0x3b1, 0x3b2, 0x3b3, 0x3b4, 0x3b5, 0x3b6, 0x3b7, 0x3b8, 0x3b9, 0x3ba, 0x3bb, + 0x3bc, 0x3bd, 0x3be, 0x3bf, 0x3c0, 0x3c1, 0x3c3, 0x3c4, 0x3c5, 0x3c6, 0x3c7, 0x3c8, 0x3c9, + 0x7e, + }; + + // List of available Function Commands that don't require a specific handling + static constexpr char const * k_FunctionCommands[] = { + "arcos", "arcsin", "arctan", "arg", "cos", "cosh", "cot", "coth", + "csc", "deg", "det", "dim", "exp", "gcd", "hom", "inf", + "ker", "lg", "lim", "liminf", "limsup", "ln", "log", "max", + "min", "Pr", "sec", "sin", "sinh", "sup", "tan", "tanh" + }; + +TexParser::TexParser(const char * text, const char * endOfText) : + m_text(text), + m_endOfText(endOfText), + m_hasError(false) +{ + +} + +Layout TexParser::getLayout() { + Layout layout = popText(0); + + if (m_hasError) { + return CodePointLayout::Builder(CodePoint(0xfffd)); + } + + 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) { + HorizontalLayout layout = HorizontalLayout::Builder(); + const char * start = m_text; + + while (m_text < m_endOfText && *m_text != stop) { + switch (*m_text) { + // TODO: Factorize this code + case '\\': + 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; + break; + case ' ': + if (start != m_text) { + layout.addOrMergeChildAtIndex(LayoutHelper::String(start, m_text - start), layout.numberOfChildren(), false); + } + m_text ++; + start = m_text; + break; + case '^': + if (start != m_text) { + layout.addOrMergeChildAtIndex(LayoutHelper::String(start, m_text - start), layout.numberOfChildren(), false); + } + m_text ++; + layout.addOrMergeChildAtIndex(VerticalOffsetLayout::Builder(popBlock(), VerticalOffsetLayoutNode::Position::Superscript), layout.numberOfChildren(), false); + start = m_text; + break; + case '_': + if (start != m_text) { + layout.addOrMergeChildAtIndex(LayoutHelper::String(start, m_text - start), layout.numberOfChildren(), false); + } + m_text ++; + layout.addOrMergeChildAtIndex(VerticalOffsetLayout::Builder(popBlock(), VerticalOffsetLayoutNode::Position::Subscript), layout.numberOfChildren(), false); + start = m_text; + break; + default: + m_text ++; + } + } + + if (start != m_text) { + layout.addOrMergeChildAtIndex(LayoutHelper::String(start, m_text - start), layout.numberOfChildren(), false); + } + + m_text ++; + + if (layout.numberOfChildren() == 1) { + return layout.squashUnaryHierarchyInPlace(); + } + + return layout; +} + +Layout TexParser::popCommand() { + // TODO: Factorize this code + if (strncmp(k_ceilCommand, m_text, strlen(k_ceilCommand)) == 0) { + if (isCommandEnded(*(m_text + strlen(k_ceilCommand)))) { + m_text += strlen(k_ceilCommand); + return popCeilCommand(); + } + } + if (strncmp(k_floorCommand, m_text, strlen(k_floorCommand)) == 0) { + if (isCommandEnded(*(m_text + strlen(k_floorCommand)))) { + m_text += strlen(k_floorCommand); + return popFloorCommand(); + } + } + if (strncmp(k_fracCommand, m_text, strlen(k_fracCommand)) == 0) { + if (isCommandEnded(*(m_text + strlen(k_fracCommand)))) { + m_text += strlen(k_fracCommand); + return popFracCommand(); + } + } + if (strncmp(k_leftCommand, m_text, strlen(k_leftCommand)) == 0) { + if (isCommandEnded(*(m_text + strlen(k_leftCommand)))) { + m_text += strlen(k_leftCommand); + return popLeftCommand(); + } + } + if (strncmp(k_rightCommand, m_text, strlen(k_rightCommand)) == 0) { + if (isCommandEnded(*(m_text + strlen(k_rightCommand)))) { + m_text += strlen(k_rightCommand); + return popRightCommand(); + } + } + if (strncmp(k_sqrtCommand, m_text, strlen(k_sqrtCommand)) == 0) { + if (isCommandEnded(*(m_text + strlen(k_sqrtCommand)))) { + m_text += strlen(k_sqrtCommand); + return popSqrtCommand(); + } + } + + if (strncmp(k_spaceCommand, m_text, strlen(k_spaceCommand)) == 0) { + if (isCommandEnded(*(m_text + strlen(k_spaceCommand)))) { + m_text += strlen(k_spaceCommand); + return popSpaceCommand(); + } + } + if (strncmp(k_overrightArrowCommand, m_text, strlen(k_overrightArrowCommand)) == 0) { + if (isCommandEnded(*(m_text + strlen(k_overrightArrowCommand)))) { + m_text += strlen(k_overrightArrowCommand); + return popOverrightarrowCommand(); + } + } + + for (int i = 0; i < k_NumberOfSymbols; i++) { + if (strncmp(k_SymbolsCommands[i], m_text, strlen(k_SymbolsCommands[i])) == 0) { + if (isCommandEnded(*(m_text + strlen(k_SymbolsCommands[i])))) { + m_text += strlen(k_SymbolsCommands[i]); + return popSymbolCommand(i); + } + } + } + + for (int i = 0; i < k_NumberOfFunctionCommands; i++) { + if (strncmp(k_FunctionCommands[i], m_text, strlen(k_FunctionCommands[i])) == 0) { + if (isCommandEnded(*(m_text + strlen(k_FunctionCommands[i])))) { + m_text += strlen(k_FunctionCommands[i]); + return LayoutHelper::String(k_FunctionCommands[i], strlen(k_FunctionCommands[i])); + } + } + } + + m_hasError = true; + return EmptyLayout::Builder(); +} + +// Expressions +Layout TexParser::popCeilCommand() { + Layout ceil = popBlock(); + return CeilingLayout::Builder(ceil); +} + +Layout TexParser::popFloorCommand() { + Layout floor = popBlock(); + return FloorLayout::Builder(floor); +} + +Layout TexParser::popFracCommand() { + Layout numerator = popBlock(); + Layout denominator = popBlock(); + FractionLayout l = FractionLayout::Builder(numerator, denominator); + return l; +} + +Layout TexParser::popLeftCommand() { + m_text++; + return LeftParenthesisLayout::Builder(); +} + +Layout TexParser::popRightCommand() { + m_text++; + return RightParenthesisLayout::Builder(); +} + +Layout TexParser::popSqrtCommand() { + while (*m_text == ' ') { + m_text ++; + } + if (*m_text == '[') { + m_text ++; + Layout rootFactor = popText(']'); + Layout belowRoot = popBlock(); + return NthRootLayout::Builder(belowRoot, rootFactor); + } + else { + return NthRootLayout::Builder(popBlock()); + } +} + +Layout TexParser::popSpaceCommand() { + return LayoutHelper::String(" ", 1); +} + +Layout TexParser::popOverrightarrowCommand() { + return VectorLayout::Builder(popBlock()); +} + +Layout TexParser::popSymbolCommand(int SymbolIndex) { + uint32_t codePoint = k_SymbolsCodePoints[SymbolIndex]; + return CodePointLayout::Builder(codePoint); +} + +inline bool TexParser::isCommandEnded(char c) const { + return !(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z'); +} + +} \ No newline at end of file diff --git a/apps/reader/tex_parser.h b/apps/reader/tex_parser.h new file mode 100644 index 000000000..82e2c1f06 --- /dev/null +++ b/apps/reader/tex_parser.h @@ -0,0 +1,57 @@ +#ifndef __TEX_PARSER_H__ +#define __TEX_PARSER_H__ + +#include +#include +#include + +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(); + + // Expressions + Layout popCeilCommand(); + Layout popFloorCommand(); + Layout popFracCommand(); + Layout popLeftCommand(); + Layout popRightCommand(); + Layout popSqrtCommand(); + Layout popSpaceCommand(); + Layout popOverrightarrowCommand(); + + //Symbols + Layout popSymbolCommand(int SymbolIndex); + + const char * m_text; + const char * m_endOfText; + bool m_hasError; + + inline bool isCommandEnded(char c) const; + + // Expressions that require specific handling + static constexpr char const * k_ceilCommand = "ceil"; + static constexpr char const * k_floorCommand = "floor"; + static constexpr char const * k_fracCommand = "frac"; + static constexpr char const * k_leftCommand = "left"; + static constexpr char const * k_rightCommand = "right"; + static constexpr char const * k_sqrtCommand = "sqrt"; + static constexpr char const * k_spaceCommand = "space"; + static constexpr char const * k_overrightArrowCommand = "overrightarrow"; + + static constexpr int const k_NumberOfSymbols = 71; + static constexpr int const k_NumberOfFunctionCommands = 32; +}; + +} + +#endif diff --git a/apps/reader/utility.cpp b/apps/reader/utility.cpp index d941039cd..8ed4cd4f0 100644 --- a/apps/reader/utility.cpp +++ b/apps/reader/utility.cpp @@ -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(start, 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; +} + } \ No newline at end of file diff --git a/apps/reader/utility.h b/apps/reader/utility.h index 55a6e8c33..61aabbf2c 100644 --- a/apps/reader/utility.h +++ b/apps/reader/utility.h @@ -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 \ No newline at end of file diff --git a/apps/reader/word_wrap_view.cpp b/apps/reader/word_wrap_view.cpp index acbcbd8ef..c116e620e 100644 --- a/apps/reader/word_wrap_view.cpp +++ b/apps/reader/word_wrap_view.cpp @@ -1,6 +1,7 @@ #include "word_wrap_view.h" #include "utility.h" +#include "tex_parser.h" #include #include "../shared/poincare_helpers.h" #include @@ -40,16 +41,17 @@ void WordWrapTextView::previousPage() { const int charWidth = m_font->glyphSize().width(); const int charHeight = m_font->glyphSize().height(); - const char * endOfWord = text() + m_pageOffset - 1; - const char * startOfWord = UTF8Helper::BeginningOfWord(text(), endOfWord); + const char * endOfFile = text() + m_length; + const char * endOfWord = text() + m_pageOffset; + 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-1, text()); + //endOfWord = EndOfPrintableWord(startOfWord, endOfFile); if (*startOfWord == '%') { if (updateTextColorBackward(startOfWord)) { @@ -57,20 +59,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()); @@ -111,7 +121,7 @@ void WordWrapTextView::previousPage() { m_pageOffset = 0; } else { - m_pageOffset = UTF8Helper::EndOfWord(startOfWord) - text() + 1; + m_pageOffset = EndOfPrintableWord(startOfWord, endOfFile) - text() + 1; } markRectAsDirty(bounds()); } @@ -121,7 +131,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; @@ -154,18 +169,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; @@ -187,7 +210,7 @@ void WordWrapTextView::drawRect(KDContext * ctx, KDRect rect) const { } if (toDraw == ToDraw::Expression) { - layout.draw(ctx, textPosition, m_textColor); + layout.draw(ctx, textPosition, m_textColor, m_backgroundColor); } else if (toDraw == ToDraw::Text) { ctx->drawString(word, textPosition, m_font, m_textColor, m_backgroundColor); @@ -224,7 +247,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(); diff --git a/build/targets.device.mak b/build/targets.device.mak index eb3ad0a8f..97293a6cd 100644 --- a/build/targets.device.mak +++ b/build/targets.device.mak @@ -39,7 +39,7 @@ openocd: # The flasher target is defined here because otherwise $(%_src) has not been # fully filled -flasher_src = $(ion_src) $(ion_device_flasher_src) $(liba_src) $(kandinsky_src) +flasher_src = $(ion_src) $(ion_device_flasher_src) $(liba_src) $(simple_kandinsky_src) $(BUILD_DIR)/flasher.light.$(EXE): $(call flavored_object_for,$(flasher_src),light usbxip) $(BUILD_DIR)/flasher.verbose.$(EXE): $(call flavored_object_for,$(flasher_src),usbxip) $(BUILD_DIR)/flasher.verbose.flash.$(EXE): $(call flavored_object_for,$(flasher_src)) @@ -48,7 +48,7 @@ $(BUILD_DIR)/flasher.%.$(EXE): LDSCRIPT = ion/src/$(PLATFORM)/shared/ram.ld $(BUILD_DIR)/flasher.%.flash.$(EXE): LDSCRIPT = ion/src/$(PLATFORM)/$(MODEL)/internal_flash.ld #TODO Do not build all apps... Put elsewhere? -bench_src = $(ion_src) $(liba_src) $(kandinsky_src) $(poincare_src) $(libaxx_src) $(app_shared_src) $(ion_device_bench_src) +bench_src = $(ion_src) $(liba_src) $(default_kandinsky_src) $(poincare_src) $(libaxx_src) $(app_shared_src) $(ion_device_bench_src) $(BUILD_DIR)/bench.ram.$(EXE): $(call flavored_object_for,$(bench_src),consoleuart usbxip) $(BUILD_DIR)/bench.ram.$(EXE): LDFLAGS += -Lion/src/$(PLATFORM)/bench $(BUILD_DIR)/bench.ram.$(EXE): LDSCRIPT = ion/src/$(PLATFORM)/shared/ram.ld diff --git a/build/targets.device.n0110.mak b/build/targets.device.n0110.mak index 77bc1bf8c..5b1f9acda 100644 --- a/build/targets.device.n0110.mak +++ b/build/targets.device.n0110.mak @@ -1,7 +1,7 @@ HANDY_TARGETS += test.external_flash.write test.external_flash.read $(BUILD_DIR)/test.external_flash.%.$(EXE): LDSCRIPT = ion/test/device/n0110/external_flash_tests.ld -test_external_flash_src = $(ion_src) $(liba_src) $(libaxx_src) $(kandinsky_src) $(poincare_src) $(ion_device_dfu_relogated_src) $(runner_src) +test_external_flash_src = $(ion_src) $(liba_src) $(libaxx_src) $(default_kandinsky_src) $(poincare_src) $(ion_device_dfu_relogated_src) $(runner_src) $(BUILD_DIR)/test.external_flash.read.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_external_flash_read_symbols.o $(call object_for,$(test_external_flash_src) $(test_ion_external_flash_read_src)) $(BUILD_DIR)/test.external_flash.write.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_external_flash_write_symbols.o $(call object_for,$(test_external_flash_src) $(test_ion_external_flash_write_src)) diff --git a/build/targets.mak b/build/targets.mak index b570237f2..aa082ad90 100644 --- a/build/targets.mak +++ b/build/targets.mak @@ -7,7 +7,7 @@ HANDY_TARGETS_EXTENSIONS ?= # Epsilon base target -base_src = $(ion_src) $(liba_src) $(kandinsky_src) $(escher_src) $(libaxx_src) $(poincare_src) $(python_src) +base_src = $(ion_src) $(liba_src) $(default_kandinsky_src) $(escher_src) $(libaxx_src) $(poincare_src) $(python_src) epsilon_src = $(base_src) $(apps_src) diff --git a/kandinsky/Makefile b/kandinsky/Makefile index 16cfdc668..88cb6885a 100644 --- a/kandinsky/Makefile +++ b/kandinsky/Makefile @@ -21,10 +21,8 @@ kandinsky_src += $(addprefix kandinsky/src/,\ rect.cpp \ ) -kandinsky_src += $(addprefix kandinsky/fonts/, \ - LargeFont.ttf \ - SmallFont.ttf \ -) +simple_kandinsky_src := $(kandinsky_src) +default_kandinsky_src := $(kandinsky_src) tests_src += $(addprefix kandinsky/test/,\ color.cpp\ @@ -53,16 +51,53 @@ $(eval $(call rule_for, \ RASTERIZER := $(BUILD_DIR)/kandinsky/fonts/rasterizer -# Define a rasterizing recipe. Parameters : font name, size, packed_width, packed_height +# Define a rasterizing recipe. Parameters : font source, font name, size, packed_width, packed_height define raster_font $(call rule_for, \ RASTER, \ - kandinsky/fonts/$(1).cpp, \ + kandinsky/fonts/$(2).cpp, \ kandinsky/fonts/$(1).ttf $$(RASTERIZER), \ - $$(RASTERIZER) $$< $(2) $(2) $(3) $(4) $(1) $$@ $(if $(HAS_LIBPNG),$$(basename $$@).png), \ + $$(RASTERIZER) $$< $(3) $(4) $(4) $(5) $(6) $(1) $$@ $(if $(HAS_LIBPNG),$$(basename $$@).png), \ global \ ) endef -$(eval $(call raster_font,SmallFont,12,7,14)) -$(eval $(call raster_font,LargeFont,16,10,18)) +ifdef HAS_READER + +kandinsky_src += $(addprefix kandinsky/fonts/, \ + LargeFontExtended.ttf \ + SmallFontExtended.ttf \ + LargeFontSimple.ttf \ + SmallFontSimple.ttf \ +) + +default_kandinsky_src += $(addprefix kandinsky/fonts/, \ + LargeFontExtended.ttf \ + SmallFontExtended.ttf \ +) + +simple_kandinsky_src += $(addprefix kandinsky/fonts/, \ + LargeFontSimple.ttf \ + SmallFontSimple.ttf \ +) + +$(eval $(call raster_font,SmallFont,SmallFontExtended,1,12,7,14)) +$(eval $(call raster_font,LargeFont,LargeFontExtended,1,16,10,18)) + +$(eval $(call raster_font,SmallFont,SmallFontSimple,0,12,7,14)) +$(eval $(call raster_font,LargeFont,LargeFontSimple,0,16,10,18)) + +else + +kandinsky_src += $(addprefix kandinsky/fonts/, \ + LargeFont.ttf \ + SmallFont.ttf \ +) + +simple_kandinsky_src = $(kandinsky_src) +default_kandinsky_src = $(kandinsky_src) + +$(eval $(call raster_font,SmallFont,SmallFontSimple,0,12,7,14)) +$(eval $(call raster_font,LargeFont,LargeFontSimple,0,16,10,18)) + +endif diff --git a/kandinsky/fonts/code_points.h b/kandinsky/fonts/code_points.h index 8671637f9..0c1509b8b 100644 --- a/kandinsky/fonts/code_points.h +++ b/kandinsky/fonts/code_points.h @@ -8,7 +8,7 @@ /* This array lists the code points that are rasterized by rasterizer.c. We put * most characters from the LATIN charset, and some mathematical characters. */ -uint32_t CodePoints[] = { +uint32_t SimpleCodePoints[] = { 0x20, // // SPACE 0x21, // ! // EXCLAMATION MARK 0x22, // " // QUOTATION MARK @@ -153,6 +153,206 @@ uint32_t CodePoints[] = { 0x1d422, // 𝐢 // MATHEMATICAL BOLD SMALL I" }; -int NumberOfCodePoints = sizeof(CodePoints)/sizeof(CodePoints[0]); +uint32_t ExtendedCodePoints[] = { + 0x20, // // SPACE + 0x21, // ! // EXCLAMATION MARK + 0x22, // " // QUOTATION MARK + 0x23, // # // NUMBER SIGN + 0x24, // $ // DOLLAR SIGN + 0x25, // % // PERCENT SIGN + 0x26, // & // AMPERSAND + 0x27, // ' // APOSTROPHE + 0x28, // ( // LEFT PARENTHESIS + 0x29, // ) // RIGHT PARENTHESIS + 0x2a, // * // ASTERISK + 0x2b, // + // PLUS SIGN + 0x2c, // , // COMMA + 0x2d, // - // HYPHEN-MINUS + 0x2e, // . // FULL STOP + 0x2f, // / // SOLIDUS + 0x30, // 0 // DIGIT ZERO + 0x31, // 1 // DIGIT ONE + 0x32, // 2 // DIGIT TWO + 0x33, // 3 // DIGIT THREE + 0x34, // 4 // DIGIT FOUR + 0x35, // 5 // DIGIT FIVE + 0x36, // 6 // DIGIT SIX + 0x37, // 7 // DIGIT SEVEN + 0x38, // 8 // DIGIT EIGHT + 0x39, // 9 // DIGIT NINE + 0x3a, // : // COLON + 0x3b, // ; // SEMICOLON + 0x3c, // < // LESS-THAN SIGN + 0x3d, // = // EQUALS SIGN + 0x3e, // > // GREATER-THAN SIGN + 0x3f, // ? // QUESTION MARK + 0x40, // @ // COMMERCIAL AT + 0x41, // A // LATIN CAPITAL LETTER A + 0x42, // B // LATIN CAPITAL LETTER B + 0x43, // C // LATIN CAPITAL LETTER C + 0x44, // D // LATIN CAPITAL LETTER D + 0x45, // E // LATIN CAPITAL LETTER E + 0x46, // F // LATIN CAPITAL LETTER F + 0x47, // G // LATIN CAPITAL LETTER G + 0x48, // H // LATIN CAPITAL LETTER H + 0x49, // I // LATIN CAPITAL LETTER I + 0x4a, // J // LATIN CAPITAL LETTER J + 0x4b, // K // LATIN CAPITAL LETTER K + 0x4c, // L // LATIN CAPITAL LETTER L + 0x4d, // M // LATIN CAPITAL LETTER M + 0x4e, // N // LATIN CAPITAL LETTER N + 0x4f, // O // LATIN CAPITAL LETTER O + 0x50, // P // LATIN CAPITAL LETTER P + 0x51, // Q // LATIN CAPITAL LETTER Q + 0x52, // R // LATIN CAPITAL LETTER R + 0x53, // S // LATIN CAPITAL LETTER S + 0x54, // T // LATIN CAPITAL LETTER T + 0x55, // U // LATIN CAPITAL LETTER U + 0x56, // V // LATIN CAPITAL LETTER V + 0x57, // W // LATIN CAPITAL LETTER W + 0x58, // X // LATIN CAPITAL LETTER X + 0x59, // Y // LATIN CAPITAL LETTER Y + 0x5a, // Z // LATIN CAPITAL LETTER Z + 0x5b, // [ // LEFT SQUARE BRACKET + 0x5c, // \ // REVERSE SOLIDUS + 0x5d, // ] // RIGHT SQUARE BRACKET + 0x5e, // ^ // CIRCUMFLEX ACCENT + 0x5f, // _ // LOW LINE + 0x60, // ` // GRAVE ACCENT + 0x61, // a // LATIN SMALL LETTER A + 0x62, // b // LATIN SMALL LETTER B + 0x63, // c // LATIN SMALL LETTER C + 0x64, // d // LATIN SMALL LETTER D + 0x65, // e // LATIN SMALL LETTER E + 0x66, // f // LATIN SMALL LETTER F + 0x67, // g // LATIN SMALL LETTER G + 0x68, // h // LATIN SMALL LETTER H + 0x69, // i // LATIN SMALL LETTER I + 0x6a, // j // LATIN SMALL LETTER J + 0x6b, // k // LATIN SMALL LETTER K + 0x6c, // l // LATIN SMALL LETTER L + 0x6d, // m // LATIN SMALL LETTER M + 0x6e, // n // LATIN SMALL LETTER N + 0x6f, // o // LATIN SMALL LETTER O + 0x70, // p // LATIN SMALL LETTER P + 0x71, // q // LATIN SMALL LETTER Q + 0x72, // r // LATIN SMALL LETTER R + 0x73, // s // LATIN SMALL LETTER S + 0x74, // t // LATIN SMALL LETTER T + 0x75, // u // LATIN SMALL LETTER U + 0x76, // v // LATIN SMALL LETTER V + 0x77, // w // LATIN SMALL LETTER W + 0x78, // x // LATIN SMALL LETTER X + 0x79, // y // LATIN SMALL LETTER Y + 0x7a, // z // LATIN SMALL LETTER Z + 0x7b, // { // LEFT CURLY BRACKET + 0x7c, // | // VERTICAL LINE + 0x7d, // } // RIGHT CURLY BRACKET + 0x7e, // ~ // TILDE + + 0xb0, // ° // DEGREE SIGN + 0xb1, // ± // PLUS OR MINUS + 0xb7, // · // MIDDLE DOT + + 0xc6, // Æ // LATIN CAPITAL LETTER AE + 0xd0, // Ð // LATIN CAPITAL LETTER ETH + 0xd7, // × // MULTIPLICATION SIGN + 0xd8, // Ø // LATIN CAPITAL LETTER O WITH STROKE + 0xde, // Þ // LATIN CAPITAL LETTER THORN + 0xdf, // ß // LATIN SMALL LETTER SHARP S + 0xe6, // æ // LATIN SMALL LETTER AE + 0xf0, // ð // LATIN SMALL LETTER ETH + 0xf7, // ÷ // DIVISION SIGN + 0xf8, // ø // LATIN SMALL LETTER O WITH STROKE + 0xfe, // þ // LATIN SMALL LETTER THORN + + 0x300, // ̀ // COMBINING GRAVE ACCENT + 0x301, // ́ // COMBINING ACUTE ACCENT + 0x302, // ̂ // COMBINING CIRCUMFLEX ACCENT + 0x303, // ̃ // COMBINING TILDE + 0x305, // ̅ // COMBINING OVERLINE + 0x308, // ̈ // COMBINING DIAERESIS + 0x30a, // ̊ // COMBINING RING ABOVE + 0x30b, // ˝// COMBINING DOUBLE ACUTE ACCENT + 0x327, // ̧ // COMBINING CEDILLA + + 0x391, // Α // GREEK CAPITAL LETTER ALPHA + 0x392, // Β // GREEK CAPITAL LETTER BETA + 0x393, // Γ // GREEK CAPITAL LETTER GAMMA + 0x394, // Δ // GREEK CAPITAL LETTER DELTA + 0x395, // Ε // GREEK CAPITAL LETTER EPSILON + 0x396, // Ζ // GREEK CAPITAL LETTER ZETA + 0x397, // Η // GREEK CAPITAL LETTER ETA + 0x398, // Θ // GREEK CAPITAL LETTER THETA + 0x399, // Ι // GREEK CAPITAL LETTER IOTA + 0x39a, // Κ // GREEK CAPITAL LETTER KAPPA + 0x39b, // Λ // GREEK CAPITAL LETTER LAMBDA + 0x39c, // Μ // GREEK CAPITAL LETTER MU + 0x39d, // Ν // GREEK CAPITAL LETTER NU + 0x39e, // Ξ // GREEK CAPITAL LETTER KSI + 0x39f, // Ο // GREEK CAPITAL LETTER OMICRON + 0x3a0, // Π // GREEK CAPITAL LETTER PI + 0x3a1, // Ρ // GREEK CAPITAL LETTER RHO + 0x3a3, // Σ // GREEK CAPITAL LETTER SIGMA + 0x3a4, // Τ // GREEK CAPITAL LETTER TAU + 0x3a5, // Υ // GREEK CAPITAL LETTER UPSILON + 0x3a6, // Φ // GREEK CAPITAL LETTER PHI + 0x3a7, // Χ // GREEK CAPITAL LETTER KHI + 0x3a8, // Ψ // GREEK CAPITAL LETTER PSI + 0x3a9, // Ω // GREEK CAPITAL LETTER OMEGA + 0x3b1, // α // GREEK SMALL LETTER ALPHA + 0x3b2, // β // GREEK SMALL LETTER BETA + 0x3b3, // γ // GREEK SMALL LETTER GAMMA + 0x3b4, // δ // GREEK SMALL LETTER DELTA + 0x3b5, // ε // GREEK SMALL LETTER EPSILON + 0x3b6, // ζ // GREEK SMALL LETTER ZETA + 0x3b7, // η // GREEK SMALL LETTER ETA + 0x3b8, // θ // GREEK SMALL LETTER THETA + 0x3b9, // ι // GREEK SMALL LETTER IOTA + 0x3ba, // κ // GREEK SMALL LETTER KAPPA + 0x3bb, // λ // GREEK SMALL LETTER LAMBDA + 0x3bc, // μ // GREEK SMALL LETTER MU + 0x3bd, // ν // GREEK SMALL LETTER NU + 0x3be, // ξ // GREEK SMALL LETTER KSI + 0x3bf, // ο // GREEK SMALL LETTER OMICRON + 0x3c0, // π // GREEK SMALL LETTER PI + 0x3c1, // ρ // GREEK SMALL LETTER RHO + 0x3c3, // σ // GREEK SMALL LETTER SIGMA + 0x3c4, // τ // GREEK SMALL LETTER TAU + 0x3c5, // υ // GREEK SMALL LETTER UPSILON + 0x3c6, // φ // GREEK SMALL LETTER PHI + 0x3c7, // χ // GREEK SMALL LETTER KHI + 0x3c8, // ψ // GREEK SMALL LETTER PSI + 0x3c9, // ω // GREEK SMALL LETTER OMEGA + 0x1d07, // ᴇ // LATIN LETTER SMALL CAPITAL E + 0x212f, // ℯ // SCRIPT SMALL E + 0x2190, // ← // BACKWARD ARROW (leftarrow) + 0x2191, // ↑ // TOP ARROW (uparrow) + 0x2192, // → // FORWARD ARROW (rightarrow) + 0x2193, // ↓ // BOTTOM ARROW (downarrow) + 0x2194, // ↔ // BACKWARD FORWARD ARROW (leftrightarrow) + 0x2195, // ↕ // TOP BOTTOM ARROW (updownarrow) + 0x21d0, // ⇐ // DOUBLE BACKWARD ARROW (Leftarrow) + 0x21d1, // ⇑ // DOUBLE TOP ARROW (Uparrow) + 0x21d2, // ⇒ // DOUBLE FORWARD ARROW (Rightarrow) + 0x21d3, // ⇓ // DOUBLE BOTTOM ARROW (Downarrow) + 0x2200, // ∀ // FORALL + 0x2202, // ∂ // PARTIAL + 0x2203, // ∃ // EXIST + 0x2211, // ∑ // N-ARY SUMMATION + 0x221a, // √ // SQUARE ROOT + 0x221e, // ∞ // INFINITY + 0x222b, // ∫ // INTEGRAL + 0x2248, // ≈ // ALMOST EQUAL TO + 0x2260, // ≠ // NOT EQUAL TO + 0x2261, // ≡ // IS CONGRUENT TO + 0x2264, // ≤ // LESS-THAN OR EQUAL TO + 0x2265, // ≥ // GREATER-THAN OR EQUAL TO + 0xFFFD, // � // REPLACEMENT CHARACTER + 0x1d422, // 𝐢 // MATHEMATICAL BOLD SMALL I" +}; + +int NumberOfSimpleCodePoints = sizeof(SimpleCodePoints)/sizeof(SimpleCodePoints[0]); +int NumberOfExtendedCodePoints = sizeof(ExtendedCodePoints)/sizeof(ExtendedCodePoints[0]); #endif diff --git a/kandinsky/fonts/rasterizer.c b/kandinsky/fonts/rasterizer.c index 5c953f2de..7e4e84359 100644 --- a/kandinsky/fonts/rasterizer.c +++ b/kandinsky/fonts/rasterizer.c @@ -16,6 +16,7 @@ #include FT_FREETYPE_H #include "code_points.h" + #include "../../ion/src/external/lz4/lz4hc.h" @@ -49,17 +50,18 @@ int main(int argc, char * argv[]) { FT_Face face; image_t bitmap_image; - int expectedNumberOfArguments = 8; + int expectedNumberOfArguments = 9; #ifdef GENERATE_PNG - expectedNumberOfArguments = 9; + expectedNumberOfArguments = 10; #endif if (argc != expectedNumberOfArguments) { #ifdef GENERATE_PNG - fprintf(stderr, "Usage: %s font_file glyph_width glyph_height font_name output_cpp output_png\n", argv[0]); + fprintf(stderr, "Usage: %s font_file extended glyph_width glyph_height font_name output_cpp output_png\n", argv[0]); #else - fprintf(stderr, "Usage: %s font_file glyph_width glyph_height font_name output_cpp\n", argv[0]); + fprintf(stderr, "Usage: %s font_file extended glyph_width glyph_height font_name output_cpp\n", argv[0]); #endif fprintf(stderr, " font_file: Path of the font file to load\n"); + fprintf(stderr, " extended: 0 if simple set of code points, else 1\n"); fprintf(stderr, " glyph_width: Width of bitmap glyphs, in pixels\n"); fprintf(stderr, " glyph_height: Height of bitmap glyphs, in pixels\n"); fprintf(stderr, " packed_glyph_width: Minimal glyph width in pixels. Pass 0 if unsure.\n"); @@ -73,16 +75,20 @@ int main(int argc, char * argv[]) { } char * font_file = argv[1]; - int requested_glyph_width = atoi(argv[2]); - int requested_glyph_height = atoi(argv[3]); - int packed_glyph_width = atoi(argv[4]); - int packed_glyph_height = atoi(argv[5]); - char * font_name = argv[6]; - char * output_cpp = argv[7]; + int extended_set = atoi(argv[2]); + int requested_glyph_width = atoi(argv[3]); + int requested_glyph_height = atoi(argv[4]); + int packed_glyph_width = atoi(argv[5]); + int packed_glyph_height = atoi(argv[6]); + char * font_name = argv[7]; + char * output_cpp = argv[8]; #ifdef GENERATE_PNG - char * output_png = argv[8]; + char * output_png = argv[9]; #endif + uint32_t * CodePoints = extended_set ? ExtendedCodePoints : SimpleCodePoints; + int NumberOfCodePoints = extended_set ? NumberOfExtendedCodePoints : NumberOfSimpleCodePoints; + ENSURE(!FT_Init_FreeType(&library), "Initializing library"); // 0 means we're picking the first face in the provided file diff --git a/kandinsky/src/font.cpp b/kandinsky/src/font.cpp index 25d82e93a..a1d217521 100644 --- a/kandinsky/src/font.cpp +++ b/kandinsky/src/font.cpp @@ -155,7 +155,7 @@ KDFont::GlyphIndex KDFont::indexForCodePoint(CodePoint c) const { return endPair->glyphIndex(); } NoMatchingGlyph: - assert(CodePoints[IndexForReplacementCharacterCodePoint] == 0xFFFD); + assert(SimpleCodePoints[IndexForReplacementCharacterCodePoint] == 0xFFFD); return IndexForReplacementCharacterCodePoint; #endif } diff --git a/poincare/Makefile b/poincare/Makefile index eca0b7ed8..380f200ab 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -26,6 +26,7 @@ poincare_src += $(addprefix poincare/src/,\ right_square_bracket_layout.cpp \ sequence_layout.cpp \ sum_layout.cpp \ + vector_layout.cpp \ vertical_offset_layout.cpp \ ) diff --git a/poincare/include/poincare/layout_cursor.h b/poincare/include/poincare/layout_cursor.h index de552e798..b8d5c2f58 100644 --- a/poincare/include/poincare/layout_cursor.h +++ b/poincare/include/poincare/layout_cursor.h @@ -23,6 +23,7 @@ class LayoutCursor final { friend class MatrixLayoutNode; friend class NthRootLayoutNode; friend class SequenceLayoutNode; + friend class VectorLayoutNode; friend class VerticalOffsetLayoutNode; public: constexpr static KDCoordinate k_cursorWidth = 1; diff --git a/poincare/include/poincare/layout_node.h b/poincare/include/poincare/layout_node.h index e2aaf7a89..d033788aa 100644 --- a/poincare/include/poincare/layout_node.h +++ b/poincare/include/poincare/layout_node.h @@ -40,6 +40,7 @@ public: RightParenthesisLayout, RightSquareBracketLayout, SumLayout, + VectorLayout, VectorNormLayout, VerticalOffsetLayout }; diff --git a/poincare/include/poincare/vector_layout.h b/poincare/include/poincare/vector_layout.h new file mode 100644 index 000000000..3d3a3e676 --- /dev/null +++ b/poincare/include/poincare/vector_layout.h @@ -0,0 +1,53 @@ +#ifndef POINCARE_VECTOR_LAYOUT_NODE_H +#define POINCARE_VECTOR_LAYOUT_NODE_H + +#include +#include +#include + +namespace Poincare { + +class VectorLayoutNode final : public LayoutNode { +public: + // Layout + Type type() const override { return Type::VectorLayout; } + + // SerializationHelperInterface + int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override { + return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, "vector", true, 0); + } + + virtual void moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool forSelection = false); + virtual void moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool forSelection = false); + + // TreeNode + size_t size() const override { return sizeof(VectorLayoutNode); } + int numberOfChildren() const override { return 1; } +#if POINCARE_TREE_LOG + void logNodeName(std::ostream & stream) const override { + stream << "VectorLayout"; + } +#endif + + constexpr static KDCoordinate k_arrowWidth = 5; + constexpr static KDCoordinate k_arrowHeight = 9; +protected: + virtual KDSize computeSize(); + virtual KDCoordinate computeBaseline(); + virtual KDPoint positionOfChild(LayoutNode * child); +private: + virtual void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor, Layout * selectionStart = nullptr, Layout * selectionEnd = nullptr, KDColor selectionColor = KDColorRed); + constexpr static KDCoordinate k_sideMargin = 2; + constexpr static KDCoordinate k_topMargin = 1; + constexpr static KDCoordinate k_arrowLineHeight = 1; // k_arrowHeight - k_arrowLineHeight must be even +}; + +class VectorLayout final : public Layout { +public: + static VectorLayout Builder(Layout child) { return TreeHandle::FixedArityBuilder({child}); } + VectorLayout() = delete; +}; + +} + +#endif diff --git a/poincare/include/poincare_layouts.h b/poincare/include/poincare_layouts.h index 72cb49ac6..f8a4319af 100644 --- a/poincare/include/poincare_layouts.h +++ b/poincare/include/poincare_layouts.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include diff --git a/poincare/src/tree_handle.cpp b/poincare/src/tree_handle.cpp index ea1712371..3c1a6cdfd 100644 --- a/poincare/src/tree_handle.cpp +++ b/poincare/src/tree_handle.cpp @@ -375,6 +375,7 @@ template VectorCross TreeHandle::FixedArityBuilder template VectorDot TreeHandle::FixedArityBuilder(const Tuple &); template VectorNorm TreeHandle::FixedArityBuilder(const Tuple &); template VectorNormLayout TreeHandle::FixedArityBuilder(const Tuple &); +template VectorLayout TreeHandle::FixedArityBuilder(const Tuple &); template MatrixLayout TreeHandle::NAryBuilder(const Tuple &); } diff --git a/poincare/src/vector_layout.cpp b/poincare/src/vector_layout.cpp new file mode 100644 index 000000000..5d9b816f7 --- /dev/null +++ b/poincare/src/vector_layout.cpp @@ -0,0 +1,78 @@ +#include + +namespace Poincare +{ + const uint8_t arrowMask[VectorLayoutNode::k_arrowHeight][VectorLayoutNode::k_arrowWidth] = { + {0xff, 0xf7, 0xff, 0xff, 0xff}, + {0xf3, 0x2c, 0xd9, 0xff, 0xff}, + {0xff, 0x93, 0x46, 0xfb, 0xff}, + {0xff, 0xfb, 0x46, 0x93, 0xff}, + {0x13, 0x13, 0x13, 0x13, 0xf0}, + {0xff, 0xfb, 0x46, 0x93, 0xff}, + {0xff, 0x93, 0x46, 0xfb, 0xff}, + {0xf3, 0x2c, 0xd9, 0xff, 0xff}, + {0xff, 0xf7, 0xff, 0xff, 0xff} + }; + void VectorLayoutNode::moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool forSelection) { + if (cursor->layoutNode() == childAtIndex(0) + && cursor->position() == LayoutCursor::Position::Left) + { + // Case: Left of the operand. Go Left of the brackets. + cursor->setLayout(this); + return; + } + assert(cursor->layoutNode() == this); + if (cursor->position() == LayoutCursor::Position::Right) { + // Case: Right of the brackets. Go Right of the operand. + cursor->setLayout(childAtIndex(0)); + return; + } + assert(cursor->position() == LayoutCursor::Position::Left); + // Case: Left of the brackets. Ask the parent. + LayoutNode * parentNode = parent(); + if (parentNode != nullptr) { + parentNode->moveCursorLeft(cursor, shouldRecomputeLayout); + } + } + + void VectorLayoutNode::moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool forSelection) { + if (cursor->layoutNode() == childAtIndex(0) + && cursor->position() == LayoutCursor::Position::Right) + { + // Case: Right of the operand. Go Right of the brackets. + cursor->setLayout(this); + return; + } + assert(cursor->layoutNode() == this); + if (cursor->position() == LayoutCursor::Position::Left) { + // Case: Left of the brackets. Go Left of the operand. + cursor->setLayout(childAtIndex(0)); + return; + } + assert(cursor->position() == LayoutCursor::Position::Right); + // Case: Right of the brackets. Ask the parent. + LayoutNode * parentNode = parent(); + if (parentNode != nullptr) { + parentNode->moveCursorRight(cursor, shouldRecomputeLayout); + } + } + KDSize VectorLayoutNode::computeSize() { + KDSize size = childAtIndex(0)->layoutSize(); + return KDSize(2 * k_sideMargin + size.width() + k_arrowWidth + k_sideMargin, k_topMargin + (k_arrowHeight+k_arrowLineHeight)/2 + size.height()); + } + + KDCoordinate VectorLayoutNode::computeBaseline() { + return childAtIndex(0)->baseline() + (k_arrowHeight+k_arrowLineHeight)/2 + k_arrowLineHeight + k_topMargin; + } + + KDPoint VectorLayoutNode::positionOfChild(LayoutNode * child) { + assert(child == childAtIndex(0)); + return KDPoint(k_sideMargin * 2, k_topMargin + (k_arrowHeight+k_arrowLineHeight)/2 + k_arrowLineHeight); + } + void VectorLayoutNode::render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor, Layout * selectionStart, Layout * selectionEnd, KDColor selectionColor) { + KDColor workingBuffer[k_arrowWidth * k_arrowHeight]; + ctx->fillRect(KDRect(p.x() + k_sideMargin, p.y() + k_topMargin + (k_arrowHeight-k_arrowLineHeight)/2, 2 * k_sideMargin + childAtIndex(0)->layoutSize().width(), k_arrowLineHeight), expressionColor); + ctx->blendRectWithMask(KDRect(p.x() + 2 * k_sideMargin + childAtIndex(0)->layoutSize().width(), p.y() + k_topMargin, k_arrowWidth, k_arrowHeight), expressionColor, (const uint8_t *)arrowMask, workingBuffer); + } + +}