singerdmx / flutter-quill

Rich text editor for Flutter
https://pub.dev/packages/flutter_quill
MIT License
2.53k stars 813 forks source link

[Request] Please add raw html string to initialize at controller #1100

Closed paraslamsong closed 1 day ago

paraslamsong commented 1 year ago

Faced the problem to use html string but the package is not allowing to use html string as input.

paraslamsong commented 1 year ago

For now the solution is


 _controller.document = quill.Document.fromJson(
        jsonDecode(markdownToDelta(html2md.convert(htmlString))),
      );

With the use of other packages, html2md and delta_markdown

mmasdivins commented 1 year ago

The problem with converting HTML to Markdown is that we lose all the following properties:

baoletrangia commented 1 year ago

You can write your own converter from HTML to Delta. It works for me

YanusScevola commented 1 year ago

The problem with converting HTML to Markdown is that we lose all the following properties:

  • Background Color
  • Color
  • Font
  • Size
  • Strikethrough
  • Superscript/Subscript
  • Underline
  • Text Alignment
  • Text Direction
  • Formula
  • Video

You can use my html to Delta converter i wrote.


class HtmlToDeltaConverter {
  static const _COLOR_PATTERN = r'color: rgb\((\d+), (\d+), (\d+)\);';

  static quill.Delta _parseInlineStyles(htmlDom.Element element) {
    var delta = quill.Delta();

    for (final node in element.nodes) {
      final attributes = _parseElementStyles(element);

      if (node is htmlDom.Text) {
        delta.insert(node.text, attributes);
      }else if (node is htmlDom.Element && node.localName == 'img') {
        final src = node.attributes['src'];
        if (src != null) {
          delta..insert({'image': src});
        }
      } else if (node is htmlDom.Element) {
        delta = delta.concat(_parseInlineStyles(node));
      }

    }

    return delta;
  }

  static Map<String, dynamic> _parseElementStyles(htmlDom.Element element) {
    Map<String, dynamic> attributes = {};

    if (element.localName == 'strong') attributes['bold'] = true;
    if (element.localName == 'em') attributes['italic'] = true;
    if (element.localName == 'u') attributes['underline'] = true;
    if (element.localName == 'del') attributes['strike'] = true;

    final style = element.attributes['style'];
    if (style != null) {
      final colorValue = _parseColorFromStyle(style);
      if (colorValue != null) attributes['color'] = colorValue;

      final bgColorValue = _parseBackgroundColorFromStyle(style);
      if (bgColorValue != null) attributes['background'] = bgColorValue;
    }

    return attributes;
  }

  static String? _parseColorFromStyle(String style) {
    if (RegExp(r'(^|\s)color:(\s|$)').hasMatch(style)) {
      return _parseRgbColorFromMatch(RegExp(_COLOR_PATTERN).firstMatch(style));
    }
    return null;
  }

  static String? _parseBackgroundColorFromStyle(String style) {
    if (RegExp(r'(^|\s)background-color:(\s|$)').hasMatch(style)) {
      return _parseRgbColorFromMatch(RegExp(_COLOR_PATTERN).firstMatch(style));
    }
    return null;
  }

  static String? _parseRgbColorFromMatch(RegExpMatch? colorMatch) {
    if (colorMatch != null) {
      try {
        final red = int.parse(colorMatch.group(1)!);
        final green = int.parse(colorMatch.group(2)!);
        final blue = int.parse(colorMatch.group(3)!);
        return '#${red.toRadixString(16).padLeft(2, '0')}${green.toRadixString(16).padLeft(2, '0')}${blue.toRadixString(16).padLeft(2, '0')}';
      } catch (e) {
        debugPrintStack(label: e.toString());
      }
    }
    return null;
  }

  static quill.Delta htmlToDelta(String html) {
    final document = htmlParse.parse(html);
    var delta = quill.Delta();

    for (final node in document.body?.nodes ?? []) {
      if (node is htmlDom.Element) {
        switch (node.localName) {
          case 'p':
            delta = delta.concat(_parseInlineStyles(node))..insert('\n');
            break;
          case 'br':
            delta..insert('\n');
            break;
        }
      }
    }

    return html.isNotEmpty ? delta : quill.Delta()..insert('\n');
  }
}

Using the сonverter:

@override
  void initState() {
    var delta = HtmlToDeltaConverter.htmlToDelta(widget.htmlString);

    _controller = quill.QuillController(
      document: quill.Document.fromDelta(delta),
      selection: TextSelection.collapsed(offset: 0),
    );

    super.initState();
  }
mmasdivins commented 1 year ago

I tried your converter with the following html:

<div class="Section0">
<p style="text-align:left;page-break-inside:auto;page-break-after:auto;page-break-before:avoid;line-height:normal;margin-top:0pt;margin-bottom:0pt;margin-left:0pt;text-indent:0pt;margin-right:0pt;border-top-style:hidden;border-left-style:hidden;border-right-style:hidden;border-bottom-style:hidden;">
<span lang="en-US" style="font-family:Arial;font-size: 16px;text-transform:none;font-weight:normal;font-style:normal;font-variant:normal;text-decoration: none;">Some text</span>
</p>
</div>

And it didn't work. Also which packages are you using for htmlDom and htmlParser? I used the following:

import 'package:html/parser.dart' as htmlParse;
import 'package:html/dom.dart' as htmlDom;
EchoEllet commented 10 months ago

Thank you for the report, we will take a look at this soon.

EchoEllet commented 9 months ago

Reopened because the converter is experimental and not perfect at all and it used only for pasting contents from external websites

wahkim commented 4 months ago

将HTML转换为Markdown的问题是,我们丢失了以下所有属性:

  • 背景颜色
  • 颜色
  • 字体
  • 尺寸
  • 罢工
  • 上标/下标
  • 下划线
  • 文本对齐
  • 文本方向
  • 公式
  • 视频

Has this problem been solved? How was it solved?

CatHood0 commented 1 day ago

You can use flutter_quill_delta_from_html to initialize the QuillController from a Delta.

I will close since this feature is not supported by default in Flutter Quill and it is not a standard of Quill js.