tex_text.dart 5.03 KB
library tex_text;

import 'package:flutter/material.dart';
import 'package:flutter_math_fork/flutter_math.dart';
export 'package:flutter_math_fork/flutter_math.dart';

enum TexAlignment {
  /// [TexAlignment.start] aligns the words at the start of the text
  start,

  /// It aligns the words at the end of the text
  end,

  /// It aligns the words at the center of the text
  center,
}

/// A LaTex text view.
///
/// Example:
/// ```dart
/// TexText(r"The equation is <m>x^2+y^2=z^2<m>") //Output: The equation is <LaTex formatted equation>
///
/// // <m><m> shows <m> result
/// TexText(r"The equation is <m><m>") //Output: The equation is <m>
/// ```
class TexText extends StatelessWidget {
  const TexText(this.text,
      {super.key,
      TextStyle? style,
      this.mathStyle = MathStyle.text,
      this.alignment = TexAlignment.start})
      : _style = style;
  final String text;
  final TextStyle? _style;
  final MathStyle mathStyle;
  final TexAlignment alignment;

  /// LaTex to HTML parser
  String toHtml() {
    return toHtmlData(text);
  }

  static String _newEasySyntax(String data) {
    return data
        .replaceAll(r"\frac", r"\cfrac")
        .replaceAll(r"\left[", r"[")
        .replaceAll(r"\right]", r"]")
        .replaceAll(r"[", r"{\left[")
        .replaceAll(r"]", r"\right]}")
        .replaceAll(r"\left\{", r"\{")
        .replaceAll(r"\right\}", r"\}")
        .replaceAll(r"\{", r"{\left\{")
        .replaceAll(r"\}", r"\right\}}")
        .replaceAll(r"\left(", r"(")
        .replaceAll(r"\right)", r")")
        .replaceAll(r"(", r"{\left(")
        .replaceAll(r")", r"\right)}")
        .replaceAll(RegExp(r"\\tf"), r"\therefore")
        .replaceAll(RegExp(r"\\bc"), r"\because")
        .replaceAllMapped(
            RegExp(r"([^A-Za-z]|^)(sin|cos|tan|cosec|sec|cot)([^A-Za-z]|$)"),
            (match) => "{\\${match[2].toString()}}${match[3].toString()}")
        .replaceAll(r"=>", r"{\Rightarrow}")
        .replaceAll(RegExp(r"\\AA(\s|$)"), r"{Å}")
        .replaceAll(r"*", r"{\times}")
        .replaceAllMapped(
            RegExp(r"\\([a-z]?)mat(\s+?\S.*?)\\([a-z]?)mat"),
            (match) =>
                "\\begin{${match[1].toString()}matrix}${match[2].toString().replaceAll("\\&", r"{\&}").replaceAll(",", "&").replaceAll("&&", "\\\\")}\\end{${match[3].toString()}matrix}");
  }

  /// LaTex to HTML parser
  static String toHtmlData(String data) {
    const dollar = r"\[-~`::36]";
    return data.replaceAll("\\\$", dollar).splitMapJoin(
      RegExp(
        r"(?!\\)\$(.*?)(?!\\)\$",
      ),
      onMatch: (p0) {
        return "\\(${_newEasySyntax(p0[1].toString().replaceAll(dollar, "\\\$"))}\\)";
      },
      onNonMatch: (p0) {
        return p0
            .replaceAll(dollar, "\$")
            .replaceAll("\n", "</br>")
            .replaceAll(" ", r"&nbsp;");
      },
    ).trim();
  }

  Widget _generateWidget(BuildContext context, String e) {
    TextStyle? style = _style ?? Theme.of(context).textTheme.bodyMedium;
    const dollar = r"\[-~`::36]";
    List<InlineSpan> widgets = [];

    e.replaceAll("\\\$", dollar).splitMapJoin(
      RegExp(
        r"\$(.*?)\$",
      ),
      onMatch: (p0) {
        widgets.add(
          WidgetSpan(
            alignment: PlaceholderAlignment.baseline,
            baseline: TextBaseline.alphabetic,
            child: Math.tex(
              _newEasySyntax(p0[1].toString().replaceAll(dollar, "\\\$")),
              textStyle: style?.copyWith(
                fontFamily: "SansSerif",
              ),
              mathStyle: mathStyle,
              textScaleFactor: 1.3,
              options: MathOptions(
                sizeUnderTextStyle: MathSize.large,
                color: style?.color ?? Theme.of(context).colorScheme.onSurface,
                fontSize: style?.fontSize ??
                    Theme.of(context).textTheme.bodyMedium?.fontSize,
                mathFontOptions: FontOptions(
                  fontFamily: "Main",
                  fontWeight: style?.fontWeight ?? FontWeight.normal,
                ),
                textFontOptions: FontOptions(
                  fontFamily: "Main",
                  fontWeight: style?.fontWeight ?? FontWeight.normal,
                ),
                style: mathStyle,
              ),
              onErrorFallback: (err) {
                return Text(
                  "\$${p0[1]}\$",
                  style: style?.copyWith(
                          color: Theme.of(context).colorScheme.error) ??
                      TextStyle(color: Theme.of(context).colorScheme.error),
                );
              },
            ),
          ),
        );
        return p0[1].toString();
      },
      onNonMatch: (p0) {
        widgets.add(
          TextSpan(
            text: p0.toString().replaceAll(dollar, "\$"),
            style: style,
          ),
        );
        return p0;
      },
    );
    return Text.rich(
      TextSpan(
        children: widgets,
      ),
      textAlign: TextAlign.values[alignment.index],
    );
  }

  @override
  Widget build(BuildContext context) {
    return _generateWidget(context, text);
  }
}