From c2a295d9cda911a428c235d8b68d5989d0a23f40 Mon Sep 17 00:00:00 2001 From: Vincent Chan Date: Wed, 10 Aug 2022 14:36:39 +0800 Subject: [PATCH] feat: copy styles of text delta --- .../lib/infra/html_converter.dart | 103 ++++++++++++++---- 1 file changed, 84 insertions(+), 19 deletions(-) diff --git a/frontend/app_flowy/packages/flowy_editor/lib/infra/html_converter.dart b/frontend/app_flowy/packages/flowy_editor/lib/infra/html_converter.dart index 7117c82eaa..db016c5fc5 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/infra/html_converter.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/infra/html_converter.dart @@ -4,7 +4,7 @@ import 'package:flowy_editor/document/attributes.dart'; import 'package:flowy_editor/document/node.dart'; import 'package:flowy_editor/document/text_delta.dart'; import 'package:flowy_editor/render/rich_text/rich_text_style.dart'; -import 'package:flowy_editor/service/internal_key_event_handlers/delete_text_handler.dart'; +import 'package:flutter/material.dart'; import 'package:html/parser.dart' show parse; import 'package:html/dom.dart' as html; @@ -22,6 +22,12 @@ const String tagStrong = "strong"; const String tagSpan = "span"; const String tagCode = "code"; +extension on Color { + String toRgbaString() { + return 'rgba($red, $green, $blue, $alpha)'; + } +} + /// Converting the HTML to nodes class HTMLToNodesConverter { final html.Document _document; @@ -104,29 +110,78 @@ class HTMLToNodesConverter { return node; } + Map _cssStringToMap(String? cssString) { + final result = {}; + if (cssString == null) { + return result; + } + + final entries = cssString.split(";"); + for (final entry in entries) { + final tuples = entry.split(":"); + if (tuples.length < 2) { + continue; + } + result[tuples[0]] = tuples[1]; + } + + return result; + } + Attributes? _getDeltaAttributesFromHtmlAttributes( LinkedHashMap htmlAttributes) { final attrs = {}; final styleString = htmlAttributes["style"]; - if (styleString != null) { - final entries = styleString.split(";"); - for (final entry in entries) { - final tuples = entry.split(":"); - if (tuples.length < 2) { - continue; - } - if (tuples[0] == "font-weight") { - int? weight = int.tryParse(tuples[1]); - if (weight != null && weight > 500) { - attrs["bold"] = true; - } - } + final cssMap = _cssStringToMap(styleString); + + final fontWeightStr = cssMap["font-weight"]; + if (fontWeightStr != null) { + int? weight = int.tryParse(fontWeightStr); + if (weight != null && weight > 500) { + attrs["bold"] = true; } } + final backgroundColorStr = cssMap["background-color"]; + final backgroundColor = _tryParseCssColorString(backgroundColorStr); + if (backgroundColor != null) { + attrs[StyleKey.backgroundColor] = + '0x${backgroundColor.value.toRadixString(16)}'; + } + return attrs.isEmpty ? null : attrs; } + Color? _tryParseCssColorString(String? colorString) { + if (colorString == null) { + return null; + } + final reg = RegExp(r'rgba\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)'); + final match = reg.firstMatch(colorString); + if (match == null) { + return null; + } + + if (match.groupCount < 4) { + return null; + } + final redStr = match.group(1); + final greenStr = match.group(2); + final blueStr = match.group(3); + final alphaStr = match.group(4); + + final red = redStr != null ? int.tryParse(redStr) : null; + final green = greenStr != null ? int.tryParse(greenStr) : null; + final blue = blueStr != null ? int.tryParse(blueStr) : null; + final alpha = alphaStr != null ? int.tryParse(alphaStr) : null; + + if (red == null || green == null || blue == null || alpha == null) { + return null; + } + + return Color.fromARGB(alpha, red, green, blue); + } + _handleRichTextElement(Delta delta, html.Element element) { if (element.localName == tagSpan) { delta.insert(element.text, @@ -321,9 +376,19 @@ class NodesToHTMLConverter { String _attributesToCssStyle(Map attributes) { final cssMap = {}; if (attributes[StyleKey.backgroundColor] != null) { - cssMap["background-color"] = attributes[StyleKey.backgroundColor]; - } else if (attributes[StyleKey.color] != null) { - cssMap["color"] = attributes[StyleKey.color]; + final color = Color( + int.parse(attributes[StyleKey.backgroundColor]), + ); + cssMap["background-color"] = color.toRgbaString(); + } + if (attributes[StyleKey.color] != null) { + final color = Color( + int.parse(attributes[StyleKey.color]), + ); + cssMap["color"] = color.toRgbaString(); + } + if (attributes[StyleKey.bold] == true) { + cssMap["font-weight"] = "bold"; } return _cssMapToCssStyle(cssMap); } @@ -334,7 +399,7 @@ class NodesToHTMLConverter { if (previousValue.isEmpty) { return kv; } - return '$previousValue; $kv"'; + return '$previousValue; $kv'; }); } @@ -360,7 +425,7 @@ class NodesToHTMLConverter { for (final op in delta.operations) { if (op is TextInsert) { final attributes = op.attributes; - if (attributes != null && attributes[StyleKey.bold] == true) { + if (attributes != null) { if (attributes.length == 1 && attributes[StyleKey.bold] == true) { final strong = html.Element.tag(tagStrong); strong.append(html.Text(op.content));