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

  1. import 'dart:ui';
  2. import 'package:flutter/material.dart';
  3. import 'package:sonnat/core/html/html_parser.dart';
  4. import 'package:sonnat/core/html/src/css_parser.dart';
  5. import 'package:sonnat/core/html/src/style/fontsize.dart';
  6. import 'package:sonnat/core/html/src/style/length.dart';
  7. import 'package:sonnat/core/html/src/style/lineheight.dart';
  8. import 'package:sonnat/core/html/src/style/margin.dart';
  9. import 'package:sonnat/core/html/src/style/marker.dart';
  10. import 'package:sonnat/core/html/src/style/size.dart';
  11. class Style {
  12. Color? backgroundColor;
  13. Color? color;
  14. Map<String, int?>? counterIncrement;
  15. Map<String, int?>? counterReset;
  16. TextDirection? direction;
  17. Display? display;
  18. String? fontFamily;
  19. List<String>? fontFamilyFallback;
  20. List<FontFeature>? fontFeatureSettings;
  21. FontSize? fontSize;
  22. FontStyle? fontStyle;
  23. FontWeight? fontWeight;
  24. Height? height;
  25. double? letterSpacing;
  26. ListStyleImage? listStyleImage;
  27. ListStyleType? listStyleType;
  28. ListStylePosition? listStylePosition;
  29. EdgeInsets? padding;
  30. Marker? marker;
  31. Margins? margin;
  32. TextAlign? textAlign;
  33. TextDecoration? textDecoration;
  34. /// CSS attribute "`text-decoration-color`"
  35. ///
  36. /// Inherited: no,
  37. /// Default: Current color
  38. Color? textDecorationColor;
  39. /// CSS attribute "`text-decoration-style`"
  40. ///
  41. /// Inherited: no,
  42. /// Default: TextDecorationStyle.solid,
  43. TextDecorationStyle? textDecorationStyle;
  44. /// Loosely based on CSS attribute "`text-decoration-thickness`"
  45. ///
  46. /// Uses a percent modifier based on the font size.
  47. ///
  48. /// Inherited: no,
  49. /// Default: 1.0 (specified by font size)
  50. // TODO(Sub6Resources): Possibly base this more closely on the CSS attribute.
  51. double? textDecorationThickness;
  52. /// CSS attribute "`text-shadow`"
  53. ///
  54. /// Inherited: yes,
  55. /// Default: none,
  56. List<Shadow>? textShadow;
  57. /// CSS attribute "`vertical-align`"
  58. ///
  59. /// Inherited: no,
  60. /// Default: VerticalAlign.BASELINE,
  61. VerticalAlign? verticalAlign;
  62. /// CSS attribute "`white-space`"
  63. ///
  64. /// Inherited: yes,
  65. /// Default: WhiteSpace.NORMAL,
  66. WhiteSpace? whiteSpace;
  67. /// CSS attribute "`width`"
  68. ///
  69. /// Inherited: no,
  70. /// Default: Width.auto()
  71. Width? width;
  72. /// CSS attribute "`word-spacing`"
  73. ///
  74. /// Inherited: yes,
  75. /// Default: normal (0)
  76. double? wordSpacing;
  77. LineHeight? lineHeight;
  78. String? before;
  79. String? after;
  80. Border? border;
  81. Alignment? alignment;
  82. Widget? markerContent;
  83. /// MaxLine
  84. ///
  85. ///
  86. ///
  87. ///
  88. int? maxLines;
  89. /// TextOverflow
  90. ///
  91. ///
  92. ///
  93. ///
  94. TextOverflow? textOverflow;
  95. TextTransform? textTransform;
  96. Style({
  97. this.backgroundColor = Colors.transparent,
  98. this.color,
  99. this.counterIncrement,
  100. this.counterReset,
  101. this.direction,
  102. this.display,
  103. this.fontFamily,
  104. this.fontFamilyFallback,
  105. this.fontFeatureSettings,
  106. this.fontSize,
  107. this.fontStyle,
  108. this.fontWeight,
  109. this.height,
  110. this.lineHeight,
  111. this.letterSpacing,
  112. this.listStyleImage,
  113. this.listStyleType,
  114. this.listStylePosition,
  115. this.padding,
  116. this.marker,
  117. this.margin,
  118. this.textAlign,
  119. this.textDecoration,
  120. this.textDecorationColor,
  121. this.textDecorationStyle,
  122. this.textDecorationThickness,
  123. this.textShadow,
  124. this.verticalAlign,
  125. this.whiteSpace,
  126. this.width,
  127. this.wordSpacing,
  128. this.before,
  129. this.after,
  130. this.border,
  131. this.alignment,
  132. this.markerContent,
  133. this.maxLines,
  134. this.textOverflow,
  135. this.textTransform = TextTransform.none,
  136. }) {
  137. if (alignment == null && (display == Display.block || display == Display.listItem)) {
  138. alignment = Alignment.centerLeft;
  139. }
  140. }
  141. static Map<String, Style> fromThemeData(ThemeData theme) => {
  142. 'h1': Style.fromTextStyle(theme.textTheme.displayLarge!),
  143. 'h2': Style.fromTextStyle(theme.textTheme.displayMedium!),
  144. 'h3': Style.fromTextStyle(theme.textTheme.displaySmall!),
  145. 'h4': Style.fromTextStyle(theme.textTheme.headlineMedium!),
  146. 'h5': Style.fromTextStyle(theme.textTheme.headlineSmall!),
  147. 'h6': Style.fromTextStyle(theme.textTheme.titleLarge!),
  148. 'body': Style.fromTextStyle(theme.textTheme.bodyMedium!),
  149. };
  150. static Map<String, Style> fromCss(String css, OnCssParseError? onCssParseError) {
  151. final declarations = parseExternalCss(css, onCssParseError);
  152. Map<String, Style> styleMap = {};
  153. declarations.forEach((key, value) {
  154. styleMap[key] = declarationsToStyle(value);
  155. });
  156. return styleMap;
  157. }
  158. TextStyle generateTextStyle() {
  159. return TextStyle(
  160. backgroundColor: backgroundColor,
  161. color: color,
  162. decoration: textDecoration,
  163. decorationColor: textDecorationColor,
  164. decorationStyle: textDecorationStyle,
  165. decorationThickness: textDecorationThickness,
  166. fontFamily: fontFamily,
  167. fontFamilyFallback: fontFamilyFallback,
  168. fontFeatures: fontFeatureSettings,
  169. fontSize: fontSize?.value,
  170. fontStyle: fontStyle,
  171. fontWeight: fontWeight,
  172. letterSpacing: letterSpacing,
  173. shadows: textShadow,
  174. wordSpacing: wordSpacing,
  175. height: lineHeight?.size ?? 1.0,
  176. );
  177. }
  178. @override
  179. String toString() {
  180. return 'Style';
  181. }
  182. Style merge(Style other) {
  183. return copyWith(
  184. backgroundColor: other.backgroundColor,
  185. color: other.color,
  186. counterIncrement: other.counterIncrement,
  187. counterReset: other.counterReset,
  188. direction: other.direction,
  189. display: other.display,
  190. fontFamily: other.fontFamily,
  191. fontFamilyFallback: other.fontFamilyFallback,
  192. fontFeatureSettings: other.fontFeatureSettings,
  193. fontSize: other.fontSize,
  194. fontStyle: other.fontStyle,
  195. fontWeight: other.fontWeight,
  196. height: other.height,
  197. lineHeight: other.lineHeight,
  198. letterSpacing: other.letterSpacing,
  199. listStyleImage: other.listStyleImage,
  200. listStyleType: other.listStyleType,
  201. listStylePosition: other.listStylePosition,
  202. padding: other.padding,
  203. margin: other.margin,
  204. marker: other.marker,
  205. textAlign: other.textAlign,
  206. textDecoration: other.textDecoration,
  207. textDecorationColor: other.textDecorationColor,
  208. textDecorationStyle: other.textDecorationStyle,
  209. textDecorationThickness: other.textDecorationThickness,
  210. textShadow: other.textShadow,
  211. verticalAlign: other.verticalAlign,
  212. whiteSpace: other.whiteSpace,
  213. width: other.width,
  214. wordSpacing: other.wordSpacing,
  215. before: other.before,
  216. after: other.after,
  217. border: other.border,
  218. alignment: other.alignment,
  219. markerContent: other.markerContent,
  220. maxLines: other.maxLines,
  221. textOverflow: other.textOverflow,
  222. textTransform: other.textTransform,
  223. );
  224. }
  225. Style copyOnlyInherited(Style child) {
  226. FontSize? finalFontSize = FontSize.inherit(fontSize, child.fontSize);
  227. LineHeight? finalLineHeight = child.lineHeight != null
  228. ? child.lineHeight?.units == 'length'
  229. ? LineHeight(child.lineHeight!.size! / (finalFontSize == null ? 14 : finalFontSize.value) * 1.2)
  230. : child.lineHeight
  231. : lineHeight;
  232. return child.copyWith(
  233. backgroundColor: child.backgroundColor != Colors.transparent ? child.backgroundColor : backgroundColor,
  234. color: child.color ?? color,
  235. direction: child.direction ?? direction,
  236. display: display == Display.none ? display : child.display,
  237. fontFamily: child.fontFamily ?? fontFamily,
  238. fontFamilyFallback: child.fontFamilyFallback ?? fontFamilyFallback,
  239. fontFeatureSettings: child.fontFeatureSettings ?? fontFeatureSettings,
  240. fontSize: finalFontSize,
  241. fontStyle: child.fontStyle ?? fontStyle,
  242. fontWeight: child.fontWeight ?? fontWeight,
  243. lineHeight: finalLineHeight,
  244. letterSpacing: child.letterSpacing ?? letterSpacing,
  245. listStyleImage: child.listStyleImage ?? listStyleImage,
  246. listStyleType: child.listStyleType ?? listStyleType,
  247. listStylePosition: child.listStylePosition ?? listStylePosition,
  248. textAlign: child.textAlign ?? textAlign,
  249. textDecoration: TextDecoration.combine([
  250. child.textDecoration ?? TextDecoration.none,
  251. textDecoration ?? TextDecoration.none,
  252. ]),
  253. textShadow: child.textShadow ?? textShadow,
  254. whiteSpace: child.whiteSpace ?? whiteSpace,
  255. wordSpacing: child.wordSpacing ?? wordSpacing,
  256. maxLines: child.maxLines ?? maxLines,
  257. textOverflow: child.textOverflow ?? textOverflow,
  258. textTransform: child.textTransform ?? textTransform,
  259. );
  260. }
  261. Style copyWith({
  262. Color? backgroundColor,
  263. Color? color,
  264. Map<String, int?>? counterIncrement,
  265. Map<String, int?>? counterReset,
  266. TextDirection? direction,
  267. Display? display,
  268. String? fontFamily,
  269. List<String>? fontFamilyFallback,
  270. List<FontFeature>? fontFeatureSettings,
  271. FontSize? fontSize,
  272. FontStyle? fontStyle,
  273. FontWeight? fontWeight,
  274. Height? height,
  275. LineHeight? lineHeight,
  276. double? letterSpacing,
  277. ListStyleImage? listStyleImage,
  278. ListStyleType? listStyleType,
  279. ListStylePosition? listStylePosition,
  280. EdgeInsets? padding,
  281. Margins? margin,
  282. Marker? marker,
  283. TextAlign? textAlign,
  284. TextDecoration? textDecoration,
  285. Color? textDecorationColor,
  286. TextDecorationStyle? textDecorationStyle,
  287. double? textDecorationThickness,
  288. List<Shadow>? textShadow,
  289. VerticalAlign? verticalAlign,
  290. WhiteSpace? whiteSpace,
  291. Width? width,
  292. double? wordSpacing,
  293. String? before,
  294. String? after,
  295. Border? border,
  296. Alignment? alignment,
  297. Widget? markerContent,
  298. int? maxLines,
  299. TextOverflow? textOverflow,
  300. TextTransform? textTransform,
  301. bool? beforeAfterNull,
  302. }) {
  303. return Style(
  304. backgroundColor: backgroundColor ?? this.backgroundColor,
  305. color: color ?? this.color,
  306. counterIncrement: counterIncrement ?? this.counterIncrement,
  307. counterReset: counterReset ?? this.counterReset,
  308. direction: direction ?? this.direction,
  309. display: display ?? this.display,
  310. fontFamily: fontFamily ?? this.fontFamily,
  311. fontFamilyFallback: fontFamilyFallback ?? this.fontFamilyFallback,
  312. fontFeatureSettings: fontFeatureSettings ?? this.fontFeatureSettings,
  313. fontSize: fontSize ?? this.fontSize,
  314. fontStyle: fontStyle ?? this.fontStyle,
  315. fontWeight: fontWeight ?? this.fontWeight,
  316. height: height ?? this.height,
  317. lineHeight: lineHeight ?? this.lineHeight,
  318. letterSpacing: letterSpacing ?? this.letterSpacing,
  319. listStyleImage: listStyleImage ?? this.listStyleImage,
  320. listStyleType: listStyleType ?? this.listStyleType,
  321. listStylePosition: listStylePosition ?? this.listStylePosition,
  322. padding: padding ?? this.padding,
  323. margin: margin ?? this.margin,
  324. marker: marker ?? this.marker,
  325. textAlign: textAlign ?? this.textAlign,
  326. textDecoration: textDecoration ?? this.textDecoration,
  327. textDecorationColor: textDecorationColor ?? this.textDecorationColor,
  328. textDecorationStyle: textDecorationStyle ?? this.textDecorationStyle,
  329. textDecorationThickness: textDecorationThickness ?? this.textDecorationThickness,
  330. textShadow: textShadow ?? this.textShadow,
  331. verticalAlign: verticalAlign ?? this.verticalAlign,
  332. whiteSpace: whiteSpace ?? this.whiteSpace,
  333. width: width ?? this.width,
  334. wordSpacing: wordSpacing ?? this.wordSpacing,
  335. before: beforeAfterNull == true ? null : before ?? this.before,
  336. after: beforeAfterNull == true ? null : after ?? this.after,
  337. border: border ?? this.border,
  338. alignment: alignment ?? this.alignment,
  339. markerContent: markerContent ?? this.markerContent,
  340. maxLines: maxLines ?? this.maxLines,
  341. textOverflow: textOverflow ?? this.textOverflow,
  342. textTransform: textTransform ?? this.textTransform,
  343. );
  344. }
  345. Style.fromTextStyle(TextStyle textStyle) {
  346. backgroundColor = textStyle.backgroundColor;
  347. color = textStyle.color;
  348. textDecoration = textStyle.decoration;
  349. textDecorationColor = textStyle.decorationColor;
  350. textDecorationStyle = textStyle.decorationStyle;
  351. textDecorationThickness = textStyle.decorationThickness;
  352. fontFamily = textStyle.fontFamily;
  353. fontFamilyFallback = textStyle.fontFamilyFallback;
  354. fontFeatureSettings = textStyle.fontFeatures;
  355. fontSize = textStyle.fontSize != null ? FontSize(textStyle.fontSize!) : null;
  356. fontStyle = textStyle.fontStyle;
  357. fontWeight = textStyle.fontWeight;
  358. letterSpacing = textStyle.letterSpacing;
  359. textShadow = textStyle.shadows;
  360. wordSpacing = textStyle.wordSpacing;
  361. lineHeight = LineHeight(textStyle.height ?? 1.2);
  362. textTransform = TextTransform.none;
  363. }
  364. /// Sets any dimensions set to rem or em to the computed size
  365. void setRelativeValues(double remValue, double emValue) {
  366. if (width?.unit == Unit.rem) {
  367. width = Width(width!.value * remValue);
  368. } else if (width?.unit == Unit.em) {
  369. width = Width(width!.value * emValue);
  370. }
  371. if (height?.unit == Unit.rem) {
  372. height = Height(height!.value * remValue);
  373. } else if (height?.unit == Unit.em) {
  374. height = Height(height!.value * emValue);
  375. }
  376. if (fontSize?.unit == Unit.rem) {
  377. fontSize = FontSize(fontSize!.value * remValue);
  378. } else if (fontSize?.unit == Unit.em) {
  379. fontSize = FontSize(fontSize!.value * emValue);
  380. }
  381. Margin? marginLeft;
  382. Margin? marginTop;
  383. Margin? marginRight;
  384. Margin? marginBottom;
  385. if (margin?.left?.unit == Unit.rem) {
  386. marginLeft = Margin(margin!.left!.value * remValue);
  387. } else if (margin?.left?.unit == Unit.em) {
  388. marginLeft = Margin(margin!.left!.value * emValue);
  389. }
  390. if (margin?.top?.unit == Unit.rem) {
  391. marginTop = Margin(margin!.top!.value * remValue);
  392. } else if (margin?.top?.unit == Unit.em) {
  393. marginTop = Margin(margin!.top!.value * emValue);
  394. }
  395. if (margin?.right?.unit == Unit.rem) {
  396. marginRight = Margin(margin!.right!.value * remValue);
  397. } else if (margin?.right?.unit == Unit.em) {
  398. marginRight = Margin(margin!.right!.value * emValue);
  399. }
  400. if (margin?.bottom?.unit == Unit.rem) {
  401. marginBottom = Margin(margin!.bottom!.value * remValue);
  402. } else if (margin?.bottom?.unit == Unit.em) {
  403. marginBottom = Margin(margin!.bottom!.value * emValue);
  404. }
  405. margin = margin?.copyWith(
  406. left: marginLeft,
  407. top: marginTop,
  408. right: marginRight,
  409. bottom: marginBottom,
  410. );
  411. }
  412. }
  413. enum Display {
  414. block,
  415. inline,
  416. inlineBlock,
  417. listItem,
  418. none,
  419. }
  420. enum ListStyleType {
  421. arabicIndic('arabic-indic'),
  422. armenian('armenian'),
  423. lowerArmenian('lower-armenian'),
  424. upperArmenian('upper-armenian'),
  425. bengali('bengali'),
  426. cambodian('cambodian'),
  427. khmer('khmer'),
  428. circle('circle'),
  429. cjkDecimal('cjk-decimal'),
  430. cjkEarthlyBranch('cjk-earthly-branch'),
  431. cjkHeavenlyStem('cjk-heavenly-stem'),
  432. cjkIdeographic('cjk-ideographic'),
  433. decimal('decimal'),
  434. decimalLeadingZero('decimal-leading-zero'),
  435. devanagari('devanagari'),
  436. disc('disc'),
  437. disclosureClosed('disclosure-closed'),
  438. disclosureOpen('disclosure-open'),
  439. ethiopicNumeric('ethiopic-numeric'),
  440. georgian('georgian'),
  441. gujarati('gujarati'),
  442. gurmukhi('gurmukhi'),
  443. hebrew('hebrew'),
  444. hiragana('hiragana'),
  445. hiraganaIroha('hiragana-iroha'),
  446. japaneseFormal('japanese-formal'),
  447. japaneseInformal('japanese-informal'),
  448. kannada('kannada'),
  449. katakana('katakana'),
  450. katakanaIroha('katakana-iroha'),
  451. koreanHangulFormal('korean-hangul-formal'),
  452. koreanHanjaInformal('korean-hanja-informal'),
  453. koreanHanjaFormal('korean-hanja-formal'),
  454. lao('lao'),
  455. lowerAlpha('lower-alpha'),
  456. lowerGreek('lower-greek'),
  457. lowerLatin('lower-latin'),
  458. lowerRoman('lower-roman'),
  459. malayalam('malayalam'),
  460. mongolian('mongolian'),
  461. myanmar('myanmar'),
  462. none('none'),
  463. oriya('oriya'),
  464. persian('persian'),
  465. simpChineseFormal('simp-chinese-formal'),
  466. simpChineseInformal('simp-chinese-informal'),
  467. square('square'),
  468. tamil('tamil'),
  469. telugu('telugu'),
  470. thai('thai'),
  471. tibetan('tibetan'),
  472. tradChineseFormal('trad-chinese-formal'),
  473. tradChineseInformal('trad-chinese-informal'),
  474. upperAlpha('upper-alpha'),
  475. upperLatin('upper-latin'),
  476. upperRoman('upper-roman');
  477. final String counterStyle;
  478. const ListStyleType(this.counterStyle);
  479. factory ListStyleType.fromName(String name) {
  480. return ListStyleType.values.firstWhere((value) {
  481. return name == value.counterStyle;
  482. });
  483. }
  484. }
  485. class ListStyleImage {
  486. final String uriText;
  487. const ListStyleImage(this.uriText);
  488. }
  489. enum ListStylePosition {
  490. outside,
  491. inside,
  492. }
  493. enum TextTransform {
  494. uppercase,
  495. lowercase,
  496. capitalize,
  497. none,
  498. }
  499. enum VerticalAlign {
  500. baseline,
  501. sub,
  502. sup,
  503. }
  504. enum WhiteSpace {
  505. normal,
  506. pre,
  507. }