Sonnat Project
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

551 lines
16 KiB

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<String, int?>? counterIncrement;
Map<String, int?>? counterReset;
TextDirection? direction;
Display? display;
String? fontFamily;
List<String>? fontFamilyFallback;
List<FontFeature>? 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<Shadow>? 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<String, Style> 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<String, Style> fromCss(String css, OnCssParseError? onCssParseError) {
final declarations = parseExternalCss(css, onCssParseError);
Map<String, Style> 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<String, int?>? counterIncrement,
Map<String, int?>? counterReset,
TextDirection? direction,
Display? display,
String? fontFamily,
List<String>? fontFamilyFallback,
List<FontFeature>? 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<Shadow>? 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,
}