import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:sonnat/core/html/html_parser.dart'; import 'package:sonnat/core/html/src/css_parser.dart'; import 'package:sonnat/core/html/src/style/fontsize.dart'; import 'package:sonnat/core/html/src/style/length.dart'; import 'package:sonnat/core/html/src/style/lineheight.dart'; import 'package:sonnat/core/html/src/style/margin.dart'; import 'package:sonnat/core/html/src/style/marker.dart'; import 'package:sonnat/core/html/src/style/size.dart'; class Style { Color? backgroundColor; Color? color; Map? counterIncrement; Map? counterReset; TextDirection? direction; Display? display; String? fontFamily; List? fontFamilyFallback; List? fontFeatureSettings; FontSize? fontSize; FontStyle? fontStyle; FontWeight? fontWeight; Height? height; double? letterSpacing; ListStyleImage? listStyleImage; ListStyleType? listStyleType; ListStylePosition? listStylePosition; EdgeInsets? padding; Marker? marker; Margins? margin; TextAlign? textAlign; TextDecoration? textDecoration; /// CSS attribute "`text-decoration-color`" /// /// Inherited: no, /// Default: Current color Color? textDecorationColor; /// CSS attribute "`text-decoration-style`" /// /// Inherited: no, /// Default: TextDecorationStyle.solid, TextDecorationStyle? textDecorationStyle; /// Loosely based on CSS attribute "`text-decoration-thickness`" /// /// Uses a percent modifier based on the font size. /// /// Inherited: no, /// Default: 1.0 (specified by font size) // TODO(Sub6Resources): Possibly base this more closely on the CSS attribute. double? textDecorationThickness; /// CSS attribute "`text-shadow`" /// /// Inherited: yes, /// Default: none, List? textShadow; /// CSS attribute "`vertical-align`" /// /// Inherited: no, /// Default: VerticalAlign.BASELINE, VerticalAlign? verticalAlign; /// CSS attribute "`white-space`" /// /// Inherited: yes, /// Default: WhiteSpace.NORMAL, WhiteSpace? whiteSpace; /// CSS attribute "`width`" /// /// Inherited: no, /// Default: Width.auto() Width? width; /// CSS attribute "`word-spacing`" /// /// Inherited: yes, /// Default: normal (0) double? wordSpacing; LineHeight? lineHeight; String? before; String? after; Border? border; Alignment? alignment; Widget? markerContent; /// MaxLine /// /// /// /// int? maxLines; /// TextOverflow /// /// /// /// TextOverflow? textOverflow; TextTransform? textTransform; Style({ this.backgroundColor = Colors.transparent, this.color, this.counterIncrement, this.counterReset, this.direction, this.display, this.fontFamily, this.fontFamilyFallback, this.fontFeatureSettings, this.fontSize, this.fontStyle, this.fontWeight, this.height, this.lineHeight, this.letterSpacing, this.listStyleImage, this.listStyleType, this.listStylePosition, this.padding, this.marker, this.margin, this.textAlign, this.textDecoration, this.textDecorationColor, this.textDecorationStyle, this.textDecorationThickness, this.textShadow, this.verticalAlign, this.whiteSpace, this.width, this.wordSpacing, this.before, this.after, this.border, this.alignment, this.markerContent, this.maxLines, this.textOverflow, this.textTransform = TextTransform.none, }) { if (alignment == null && (display == Display.block || display == Display.listItem)) { alignment = Alignment.centerLeft; } } static Map fromThemeData(ThemeData theme) => { 'h1': Style.fromTextStyle(theme.textTheme.displayLarge!), 'h2': Style.fromTextStyle(theme.textTheme.displayMedium!), 'h3': Style.fromTextStyle(theme.textTheme.displaySmall!), 'h4': Style.fromTextStyle(theme.textTheme.headlineMedium!), 'h5': Style.fromTextStyle(theme.textTheme.headlineSmall!), 'h6': Style.fromTextStyle(theme.textTheme.titleLarge!), 'body': Style.fromTextStyle(theme.textTheme.bodyMedium!), }; static Map fromCss(String css, OnCssParseError? onCssParseError) { final declarations = parseExternalCss(css, onCssParseError); Map styleMap = {}; declarations.forEach((key, value) { styleMap[key] = declarationsToStyle(value); }); return styleMap; } TextStyle generateTextStyle() { return TextStyle( backgroundColor: backgroundColor, color: color, decoration: textDecoration, decorationColor: textDecorationColor, decorationStyle: textDecorationStyle, decorationThickness: textDecorationThickness, fontFamily: fontFamily, fontFamilyFallback: fontFamilyFallback, fontFeatures: fontFeatureSettings, fontSize: fontSize?.value, fontStyle: fontStyle, fontWeight: fontWeight, letterSpacing: letterSpacing, shadows: textShadow, wordSpacing: wordSpacing, height: lineHeight?.size ?? 1.0, ); } @override String toString() { return 'Style'; } Style merge(Style other) { return copyWith( backgroundColor: other.backgroundColor, color: other.color, counterIncrement: other.counterIncrement, counterReset: other.counterReset, direction: other.direction, display: other.display, fontFamily: other.fontFamily, fontFamilyFallback: other.fontFamilyFallback, fontFeatureSettings: other.fontFeatureSettings, fontSize: other.fontSize, fontStyle: other.fontStyle, fontWeight: other.fontWeight, height: other.height, lineHeight: other.lineHeight, letterSpacing: other.letterSpacing, listStyleImage: other.listStyleImage, listStyleType: other.listStyleType, listStylePosition: other.listStylePosition, padding: other.padding, margin: other.margin, marker: other.marker, textAlign: other.textAlign, textDecoration: other.textDecoration, textDecorationColor: other.textDecorationColor, textDecorationStyle: other.textDecorationStyle, textDecorationThickness: other.textDecorationThickness, textShadow: other.textShadow, verticalAlign: other.verticalAlign, whiteSpace: other.whiteSpace, width: other.width, wordSpacing: other.wordSpacing, before: other.before, after: other.after, border: other.border, alignment: other.alignment, markerContent: other.markerContent, maxLines: other.maxLines, textOverflow: other.textOverflow, textTransform: other.textTransform, ); } Style copyOnlyInherited(Style child) { FontSize? finalFontSize = FontSize.inherit(fontSize, child.fontSize); LineHeight? finalLineHeight = child.lineHeight != null ? child.lineHeight?.units == 'length' ? LineHeight(child.lineHeight!.size! / (finalFontSize == null ? 14 : finalFontSize.value) * 1.2) : child.lineHeight : lineHeight; return child.copyWith( backgroundColor: child.backgroundColor != Colors.transparent ? child.backgroundColor : backgroundColor, color: child.color ?? color, direction: child.direction ?? direction, display: display == Display.none ? display : child.display, fontFamily: child.fontFamily ?? fontFamily, fontFamilyFallback: child.fontFamilyFallback ?? fontFamilyFallback, fontFeatureSettings: child.fontFeatureSettings ?? fontFeatureSettings, fontSize: finalFontSize, fontStyle: child.fontStyle ?? fontStyle, fontWeight: child.fontWeight ?? fontWeight, lineHeight: finalLineHeight, letterSpacing: child.letterSpacing ?? letterSpacing, listStyleImage: child.listStyleImage ?? listStyleImage, listStyleType: child.listStyleType ?? listStyleType, listStylePosition: child.listStylePosition ?? listStylePosition, textAlign: child.textAlign ?? textAlign, textDecoration: TextDecoration.combine([ child.textDecoration ?? TextDecoration.none, textDecoration ?? TextDecoration.none, ]), textShadow: child.textShadow ?? textShadow, whiteSpace: child.whiteSpace ?? whiteSpace, wordSpacing: child.wordSpacing ?? wordSpacing, maxLines: child.maxLines ?? maxLines, textOverflow: child.textOverflow ?? textOverflow, textTransform: child.textTransform ?? textTransform, ); } Style copyWith({ Color? backgroundColor, Color? color, Map? counterIncrement, Map? counterReset, TextDirection? direction, Display? display, String? fontFamily, List? fontFamilyFallback, List? fontFeatureSettings, FontSize? fontSize, FontStyle? fontStyle, FontWeight? fontWeight, Height? height, LineHeight? lineHeight, double? letterSpacing, ListStyleImage? listStyleImage, ListStyleType? listStyleType, ListStylePosition? listStylePosition, EdgeInsets? padding, Margins? margin, Marker? marker, TextAlign? textAlign, TextDecoration? textDecoration, Color? textDecorationColor, TextDecorationStyle? textDecorationStyle, double? textDecorationThickness, List? textShadow, VerticalAlign? verticalAlign, WhiteSpace? whiteSpace, Width? width, double? wordSpacing, String? before, String? after, Border? border, Alignment? alignment, Widget? markerContent, int? maxLines, TextOverflow? textOverflow, TextTransform? textTransform, bool? beforeAfterNull, }) { return Style( backgroundColor: backgroundColor ?? this.backgroundColor, color: color ?? this.color, counterIncrement: counterIncrement ?? this.counterIncrement, counterReset: counterReset ?? this.counterReset, direction: direction ?? this.direction, display: display ?? this.display, fontFamily: fontFamily ?? this.fontFamily, fontFamilyFallback: fontFamilyFallback ?? this.fontFamilyFallback, fontFeatureSettings: fontFeatureSettings ?? this.fontFeatureSettings, fontSize: fontSize ?? this.fontSize, fontStyle: fontStyle ?? this.fontStyle, fontWeight: fontWeight ?? this.fontWeight, height: height ?? this.height, lineHeight: lineHeight ?? this.lineHeight, letterSpacing: letterSpacing ?? this.letterSpacing, listStyleImage: listStyleImage ?? this.listStyleImage, listStyleType: listStyleType ?? this.listStyleType, listStylePosition: listStylePosition ?? this.listStylePosition, padding: padding ?? this.padding, margin: margin ?? this.margin, marker: marker ?? this.marker, textAlign: textAlign ?? this.textAlign, textDecoration: textDecoration ?? this.textDecoration, textDecorationColor: textDecorationColor ?? this.textDecorationColor, textDecorationStyle: textDecorationStyle ?? this.textDecorationStyle, textDecorationThickness: textDecorationThickness ?? this.textDecorationThickness, textShadow: textShadow ?? this.textShadow, verticalAlign: verticalAlign ?? this.verticalAlign, whiteSpace: whiteSpace ?? this.whiteSpace, width: width ?? this.width, wordSpacing: wordSpacing ?? this.wordSpacing, before: beforeAfterNull == true ? null : before ?? this.before, after: beforeAfterNull == true ? null : after ?? this.after, border: border ?? this.border, alignment: alignment ?? this.alignment, markerContent: markerContent ?? this.markerContent, maxLines: maxLines ?? this.maxLines, textOverflow: textOverflow ?? this.textOverflow, textTransform: textTransform ?? this.textTransform, ); } Style.fromTextStyle(TextStyle textStyle) { backgroundColor = textStyle.backgroundColor; color = textStyle.color; textDecoration = textStyle.decoration; textDecorationColor = textStyle.decorationColor; textDecorationStyle = textStyle.decorationStyle; textDecorationThickness = textStyle.decorationThickness; fontFamily = textStyle.fontFamily; fontFamilyFallback = textStyle.fontFamilyFallback; fontFeatureSettings = textStyle.fontFeatures; fontSize = textStyle.fontSize != null ? FontSize(textStyle.fontSize!) : null; fontStyle = textStyle.fontStyle; fontWeight = textStyle.fontWeight; letterSpacing = textStyle.letterSpacing; textShadow = textStyle.shadows; wordSpacing = textStyle.wordSpacing; lineHeight = LineHeight(textStyle.height ?? 1.2); textTransform = TextTransform.none; } /// Sets any dimensions set to rem or em to the computed size void setRelativeValues(double remValue, double emValue) { if (width?.unit == Unit.rem) { width = Width(width!.value * remValue); } else if (width?.unit == Unit.em) { width = Width(width!.value * emValue); } if (height?.unit == Unit.rem) { height = Height(height!.value * remValue); } else if (height?.unit == Unit.em) { height = Height(height!.value * emValue); } if (fontSize?.unit == Unit.rem) { fontSize = FontSize(fontSize!.value * remValue); } else if (fontSize?.unit == Unit.em) { fontSize = FontSize(fontSize!.value * emValue); } Margin? marginLeft; Margin? marginTop; Margin? marginRight; Margin? marginBottom; if (margin?.left?.unit == Unit.rem) { marginLeft = Margin(margin!.left!.value * remValue); } else if (margin?.left?.unit == Unit.em) { marginLeft = Margin(margin!.left!.value * emValue); } if (margin?.top?.unit == Unit.rem) { marginTop = Margin(margin!.top!.value * remValue); } else if (margin?.top?.unit == Unit.em) { marginTop = Margin(margin!.top!.value * emValue); } if (margin?.right?.unit == Unit.rem) { marginRight = Margin(margin!.right!.value * remValue); } else if (margin?.right?.unit == Unit.em) { marginRight = Margin(margin!.right!.value * emValue); } if (margin?.bottom?.unit == Unit.rem) { marginBottom = Margin(margin!.bottom!.value * remValue); } else if (margin?.bottom?.unit == Unit.em) { marginBottom = Margin(margin!.bottom!.value * emValue); } margin = margin?.copyWith( left: marginLeft, top: marginTop, right: marginRight, bottom: marginBottom, ); } } enum Display { block, inline, inlineBlock, listItem, none, } enum ListStyleType { arabicIndic('arabic-indic'), armenian('armenian'), lowerArmenian('lower-armenian'), upperArmenian('upper-armenian'), bengali('bengali'), cambodian('cambodian'), khmer('khmer'), circle('circle'), cjkDecimal('cjk-decimal'), cjkEarthlyBranch('cjk-earthly-branch'), cjkHeavenlyStem('cjk-heavenly-stem'), cjkIdeographic('cjk-ideographic'), decimal('decimal'), decimalLeadingZero('decimal-leading-zero'), devanagari('devanagari'), disc('disc'), disclosureClosed('disclosure-closed'), disclosureOpen('disclosure-open'), ethiopicNumeric('ethiopic-numeric'), georgian('georgian'), gujarati('gujarati'), gurmukhi('gurmukhi'), hebrew('hebrew'), hiragana('hiragana'), hiraganaIroha('hiragana-iroha'), japaneseFormal('japanese-formal'), japaneseInformal('japanese-informal'), kannada('kannada'), katakana('katakana'), katakanaIroha('katakana-iroha'), koreanHangulFormal('korean-hangul-formal'), koreanHanjaInformal('korean-hanja-informal'), koreanHanjaFormal('korean-hanja-formal'), lao('lao'), lowerAlpha('lower-alpha'), lowerGreek('lower-greek'), lowerLatin('lower-latin'), lowerRoman('lower-roman'), malayalam('malayalam'), mongolian('mongolian'), myanmar('myanmar'), none('none'), oriya('oriya'), persian('persian'), simpChineseFormal('simp-chinese-formal'), simpChineseInformal('simp-chinese-informal'), square('square'), tamil('tamil'), telugu('telugu'), thai('thai'), tibetan('tibetan'), tradChineseFormal('trad-chinese-formal'), tradChineseInformal('trad-chinese-informal'), upperAlpha('upper-alpha'), upperLatin('upper-latin'), upperRoman('upper-roman'); final String counterStyle; const ListStyleType(this.counterStyle); factory ListStyleType.fromName(String name) { return ListStyleType.values.firstWhere((value) { return name == value.counterStyle; }); } } class ListStyleImage { final String uriText; const ListStyleImage(this.uriText); } enum ListStylePosition { outside, inside, } enum TextTransform { uppercase, lowercase, capitalize, none, } enum VerticalAlign { baseline, sub, sup, } enum WhiteSpace { normal, pre, }