From 353f38b9f6991761c6d239b0bcd8d00b902f01af Mon Sep 17 00:00:00 2001 From: alihassan143 Date: Sat, 30 Sep 2023 13:08:02 +0500 Subject: [PATCH 1/4] fix: html codec parser fixes --- CHANGELOG.md | 6 ++ example.pdf | Bin 5238 -> 0 bytes example/lib/main.dart | 75 ++++++++++------ example/pubspec.lock | 46 +++++----- example/pubspec.yaml | 1 - lib/src/attributes.dart | 92 +++++++++---------- lib/src/extension/color_extension.dart | 18 +++- lib/src/html_tags.dart | 22 ++--- lib/src/html_to_widgets.dart | 119 +++++++++++++------------ lib/src/pdfwidgets/bullet_list.dart | 31 ++++--- lib/src/pdfwidgets/number_list.dart | 22 +++-- lib/src/pdfwidgets/quote_widget.dart | 29 ++++-- pubspec.yaml | 10 ++- 13 files changed, 268 insertions(+), 203 deletions(-) delete mode 100644 example.pdf diff --git a/CHANGELOG.md b/CHANGELOG.md index 28a80ba..300c737 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.0.9 + +* optimiz parse logic +* documentation fixs +* using override dependency for pdf due to underline and italic issues + ## 0.0.8+2 * support for html table tag added diff --git a/example.pdf b/example.pdf deleted file mode 100644 index 71a5dda700865c53110002c3f8eecf3760d1fb80..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5238 zcmc&&dpuO>8#mpQ3T=f5r`_c4%=OF+l5va3N>XlVjAM+L(aex!?QTi63EiYE`;(BU z7Adu?4S5dIcj{%lE15kTOlvs|?Y;06;aWGV+4>Pgn%lY9FA?l4wWg>|f zrCG51-5}7pCo!Hc3C7VyGTdDv#s@vY2*ZP;p14dBB^BT@kdlW~B3Ova{ZOzs)C-T1 zqb?C3LA!otAAmYZ#B!kL#6nm^EGIU?h65Y$1A(PDOr2Pnj|YqRc9IxBDwwjkEDJiD z#o{6yCe4Dw;j%HfQL!B8%MfP22Xen~s>sqw6fVc5s8cv!jyvE2xG6VWER=^LG!7M% zNG6x!{0NnpJ?*}(DR(gSkEYhg*5lpr)9)ZI3y}59F|kIm)H3&`oA1RXsz1N4UzwO~ zG%qct!y{dfk;{4%WQAX}vo&5_uEb}v{;w+|V#D&%6=hiLW@U4~Ml)I-A zN4#!bUU}hxYzpC7k}62eBAjmekuBr-K+NCQ&HEtGXD~rj%)nNcRDjDGuezo zjWs2{j*RR`FXJe8M)GydA9^|h<9%M}m9|cpD^m2c1i(qojewnfkGkS1aDsrdVqkCmAO^$;{{5$L5Vv z%bK+7?a}IFN{%HXf5xn&t_|aQ^vf?VTXXy=)wq1riWQS`y@bD)wAp)hXlGNlCI(71 zwr%BMK}~zstN%9EE1$JC)*rj1cIEY}Z)R(MZzuxpn)Tqutj<)6LGPm5=w>hHZ*!-7&j4bW088I=X z_UkeMMaO&ON;h8d&f4%1rOL$>@up)lr>?sKTxjaN2agKuHVZv z&I!t!)G~dH#4pSCzVh-}X#$ESlVv)k;tZnm)4oWv+vl=o#z$-JQkwKDPxGASq5$ix z09`$w#WSx8R9{_8SUf$^{KijDJ2aZrx+7MnNIC)xFRAD(NO@A)o{`hzt?N>zwkzvo zbCLajtO}n{D=sd5VR|n$CqIt1JpcB-C~x=IeH(V2P2O55H;M6?C)2w$x6UMOCGTyB z*{84@*1E}|dbdu6y{A~G6i?E*{L#+tbZU#A{gxKB)_W6H)FlKpPkj{EG*x-B<`T2Z zksgMAQ@ys0IisSnsyS1r+LOkuPN!N*DtJe7^Jn`CpUhO=obKYPcF@!REYIa=;@a)1 zS?oudD_iTTKE@Yr@Z8RO;I&2lAnMa}R`;-qozv;ii8;l4?`%sgYPTC(N(tc_ZMu1A z?X0%%rp|or#=AP@t@*;#G!92nVJk~}{=rA^c;=_+elDiUI-xCD2C2FWrtQ}^Uh~wP zZMmD$>#TuIZcVebtj;w^9e<)LE5I?W{q8Su>e-RyMi-BspKYQy&26iP_mAma4t8oj zclRXJbUHcvbUkJl&(XygJ>4;hYdif-SMAg*s4{xCIe()4xUxgG*dEDVdF<&~nOiqr zTZQUHr(F6lXWNI)10D8juIyN+di}lIdGA{nM%GsO|5)JOrJ7~?{9%dYkMnbPY|igo zzh3HMaMGWi=HcJ|PSxWKpyIOq}k_`Ath0b4{0LX%o23dF#f;8vW9jyWf3iyf(f*j9bAQA&5)k=BPHAd(s`&T%j;^wtuKzw6FH3m$Jc%tZ6q>QsZA_;m=Wjh|QI88pu6@5r zyrp&CgEKzZ?g~nFq((h+bl6H0hlI(8=56x7h`9!88;wq;XLWYAHXkry8Y_tw(a;;ak(NQV) zRy!GWI{PDM0)w^FfN_6i>jCxzfI((z}HrF!T zE^fq|0qSsqK4LEpDv1XQ6XuXWQw>_IaDG zhD>yt7QFGtP>b35+0ng;e(KbRZ%pUbT|S?Z5q!^m>DBw60zR5|s~x1caNr|U@k~^_ zJiy2F&__OefTMPN8BPqG@$hI|E)wv~?IhvBsFO%4liP>#r3e*u<12=A77JZ03YLe; z{OD{B@^2G^!9bX7Ccy1z(&q0t=?D5C38T>v zE}KQfg7HYgFg60$*q}B<9)Lsta17-f5TDQma;Qt{4cI|FAm=if1SjQW-jEYH0H4Gd zaOfAtAUTx7F{Gh;g=1JBa*gB+SRaKyC?oP!Jn%!v0B6Fe3K^6`B8x!!Cgd1+$pwBn4CVkqJ@Vj(b0T}<)IpNODJnR0 zI+O+sft=`uAsi+~cF2I9qC?2e7`o}9V|da-=j3>3Y&eFS_%nj>zqAH@K{??4mjDM` zCXk9E)`-3 z=$yE45YC1}RXATLLl|^I!4BRt%xU1j&FORwY&y_37(D>x$pr950>&ux3CB(-xJVc(N7ytDx=@Zscq23>l^8iAG$5NVlF7g>2pB{#Ds?b@c>5Wi z{ul7|Z=n_b5B^Oen}osY?|OLP1ayEVrg6c4SMg6FpaV1s)Q2+Cj%>ES;{*D@2W&Qi zKr9-SHi&;@=aU^z{v)6aEDEs>bA@(57v=~#NelofbPNC=;6wqy8U_om@C#!?KLn%< zbxa80?&ttA`&XeU{9`KI!?fu1A?NjFY=wcq3Ku*yNlU^l2t0JdWNer$9K)nYY-KWm zEjYme8Cb)pV8c)bd=mtV0gC|enA{X02iOYjIiUCivHK5!I5@Dq;hfkitm5zm=yWKN zoiiL);T$GLcF2I1fuG_$`#;<*@x(*Wg`!v-)CFDa2igDul~H`1VuB}x z;yqjm{3uEoAQ7M=N)(EZ5@l?z5@q6CC5q}8CCXR(lqi$^lsHeading Example -

This is a paragraph.

- Example Image -
This is a quote.
-
    -
  • First item
  • -
  • Second item
  • -
  • Third item
  • -
- - - - - - - - - - - - - - - - -
CompanyContactCountry
Alfreds FutterkisteMaria AndersGermany
Centro comercial MoctezumaFrancisco ChangMexico
+const htmlText = '''

AppFlowyEditor

+

👋 Welcome to AppFlowy Editor

+

AppFlowy Editor is a highly customizable rich-text editor

+ +
+ +

Here is an example your you can give a try

+ +Span element + +Span element two + +Span element three + +This is an anchor tag! + + + +

Features!

+ +
    +
  • [x] Customizable
  • +
  • [x] Test-covered
  • +
  • [ ] more to come!
  • +
+ +
    +
  1. First item
  2. +
  3. Second item
  4. +
+ +
  • List element
  • + +
    +

    This is a quote!

    +
    + + + Code block + + +Italic one Italic two + +Bold tag +AppFlowy +

    You can also use AppFlowy Editor as a component to build your own app.

    +

    Awesome features

    +

    If you have questions or feedback, please submit an issue on Github or join the community along with 1000+ builders!

    + '''; createDocument() async { diff --git a/example/pubspec.lock b/example/pubspec.lock index 8b5f047..e128eab 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: archive - sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a" + sha256: d4dc11707abb32ef756ab95678c0d6df54003d98277f7c9aeda14c48e7a38c2f url: "https://pub.dev" source: hosted - version: "3.3.7" + version: "3.4.3" async: dependency: transitive description: @@ -29,18 +29,18 @@ packages: dependency: transitive description: name: bidi - sha256: dc00274c7edabae2ab30c676e736ea1eb0b1b7a1b436cb5fe372e431ccb39ab0 + sha256: "1a7d0c696324b2089f72e7671fd1f1f64fef44c980f3cebc84e803967c597b63" url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.0.10" collection: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.18.0" convert: dependency: transitive description: @@ -61,33 +61,33 @@ packages: dependency: transitive description: name: csslib - sha256: "831883fb353c8bdc1d71979e5b342c7d88acfbc643113c14ae51e2442ea0f20f" + sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" url: "https://pub.dev" source: hosted - version: "0.17.3" + version: "1.0.0" flutter_lints: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.3" html: dependency: transitive description: name: html - sha256: "58e3491f7bf0b6a4ea5110c0c688877460d1a6366731155c4a4580e7ded773e8" + sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" url: "https://pub.dev" source: hosted - version: "0.15.3" + version: "0.15.4" htmltopdfwidgets: dependency: "direct main" description: path: ".." relative: true source: path - version: "0.0.8+1" + version: "0.0.8+2" http: dependency: transitive description: @@ -108,10 +108,10 @@ packages: dependency: transitive description: name: image - sha256: a72242c9a0ffb65d03de1b7113bc4e189686fc07c7147b8b41811d0dd0e0d9bf + sha256: "028f61960d56f26414eb616b48b04eb37d700cbe477b7fb09bf1d7ce57fd9271" url: "https://pub.dev" source: hosted - version: "4.0.17" + version: "4.1.3" js: dependency: transitive description: @@ -132,10 +132,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" path: dependency: transitive description: @@ -164,10 +164,10 @@ packages: dependency: transitive description: name: petitparser - sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + sha256: eeb2d1428ee7f4170e2bd498827296a18d4e7fc462b71727d111c0ac7707cfa6 url: "https://pub.dev" source: hosted - version: "5.4.0" + version: "6.0.1" pointycastle: dependency: transitive description: @@ -188,10 +188,10 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" string_scanner: dependency: transitive description: @@ -228,9 +228,9 @@ packages: dependency: transitive description: name: xml - sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" + sha256: af5e77e9b83f2f4adc5d3f0a4ece1c7f45a2467b695c2540381bac793e34e556 url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.4.2" sdks: dart: ">=3.0.3 <4.0.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 3a2c85c..46b7aa6 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -14,7 +14,6 @@ dependencies: htmltopdfwidgets: path: ../ - dev_dependencies: diff --git a/lib/src/attributes.dart b/lib/src/attributes.dart index 6421e7e..6931fd4 100644 --- a/lib/src/attributes.dart +++ b/lib/src/attributes.dart @@ -1,60 +1,54 @@ -/// -/// Supported partial rendering types: -/// bold, italic, -/// underline, strikethrough, -/// color, font, -/// href -/// -/// Supported global rendering types: -/// heading: h1, h2, h3, h4, h5, h6, ... -/// block quote, -/// list: ordered list, bulleted list, -/// code block -/// +/// This class defines various constants for supported rendering types +/// and attributes in a text processing system. It categorizes them into +/// partial rendering types and global rendering types. + class BuiltInAttributeKey { - static String bold = 'bold'; - static String italic = 'italic'; - static String underline = 'underline'; - static String strikethrough = 'strikethrough'; - static String color = 'color'; - static String backgroundColor = 'backgroundColor'; - static String font = 'font'; - static String href = 'href'; + // Partial rendering types + static String bold = 'bold'; // Text should be displayed in bold. + static String italic = 'italic'; // Text should be displayed in italic. + static String underline = 'underline'; // Text should be underlined. + static String strikethrough = 'strikethrough'; // Text should have a strikethrough line. + static String color = 'color'; // Text color customization. + static String backgroundColor = 'backgroundColor'; // Background color customization. + static String font = 'font'; // Font customization. + static String href = 'href'; // Hyperlink attribute for text. - static String subtype = 'subtype'; - static String heading = 'heading'; - static String h1 = 'h1'; - static String h2 = 'h2'; - static String h3 = 'h3'; - static String h4 = 'h4'; - static String h5 = 'h5'; - static String h6 = 'h6'; + // Global rendering types + static String subtype = 'subtype'; // Subtype for customizing rendering behavior. + static String heading = 'heading'; // Text should be treated as a heading (h1, h2, h3, etc.). + static String h1 = 'h1'; // Heading level 1. + static String h2 = 'h2'; // Heading level 2. + static String h3 = 'h3'; // Heading level 3. + static String h4 = 'h4'; // Heading level 4. + static String h5 = 'h5'; // Heading level 5. + static String h6 = 'h6'; // Heading level 6. - static String bulletedList = 'bulleted-list'; - static String numberList = 'number-list'; + static String bulletedList = 'bulleted-list'; // Text should be displayed in a bulleted list. + static String numberList = 'number-list'; // Text should be displayed in a numbered list. - static String quote = 'quote'; - static String checkbox = 'checkbox'; - static String code = 'code'; - static String number = 'number'; + static String quote = 'quote'; // Text should be displayed as a blockquote. + static String checkbox = 'checkbox'; // Text should be displayed as a checkbox. + static String code = 'code'; // Text should be displayed as code. + static String number = 'number'; // Text should be displayed as a numbered item. + // Lists of partial style keys and global style keys static List partialStyleKeys = [ - BuiltInAttributeKey.bold, - BuiltInAttributeKey.italic, - BuiltInAttributeKey.underline, - BuiltInAttributeKey.strikethrough, - BuiltInAttributeKey.backgroundColor, - BuiltInAttributeKey.color, - BuiltInAttributeKey.href, - BuiltInAttributeKey.code, + bold, + italic, + underline, + strikethrough, + backgroundColor, + color, + href, + code, ]; static List globalStyleKeys = [ - BuiltInAttributeKey.subtype, - BuiltInAttributeKey.heading, - BuiltInAttributeKey.checkbox, - BuiltInAttributeKey.bulletedList, - BuiltInAttributeKey.numberList, - BuiltInAttributeKey.quote, + subtype, + heading, + checkbox, + bulletedList, + numberList, + quote, ]; } diff --git a/lib/src/extension/color_extension.dart b/lib/src/extension/color_extension.dart index 5a07c15..043fef1 100644 --- a/lib/src/extension/color_extension.dart +++ b/lib/src/extension/color_extension.dart @@ -1,42 +1,50 @@ import 'package:pdf/pdf.dart'; +// Define an extension for PdfColor to add additional functionality. extension ColorExtension on PdfColor { - /// Try to parse the `rgba(red, greed, blue, alpha)` - /// from the string. + /// Try to parse the `rgba(red, green, blue, alpha)` from the string. static PdfColor? tryFromRgbaString(String colorString) { 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; + return null; // Return null if the provided string does not match the expected format. } if (match.groupCount < 4) { - return null; + return null; // Return null if there are not enough color components. } + final redStr = match.group(1); final greenStr = match.group(2); final blueStr = match.group(3); final alphaStr = match.group(4); + // Attempt to parse color components as integers. 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 any component parsing fails, return null. if (red == null || green == null || blue == null || alpha == null) { return null; } + // Create a PdfColor from the parsed RGBA values. return PdfColor.fromInt( hexOfRGBA(red, green, blue, opacity: alpha.toDouble())); } + // Convert PdfColor to an RGBA string format. String toRgbaString() { return 'rgba($red, $green, $blue, $alpha)'; } } +// Function to calculate the hex representation of an RGBA color. int hexOfRGBA(int r, int g, int b, {double opacity = 1}) { + // Ensure that color values and opacity are within valid ranges. r = (r < 0) ? -r : r; g = (g < 0) ? -g : g; b = (b < 0) ? -b : b; @@ -46,6 +54,8 @@ int hexOfRGBA(int r, int g, int b, {double opacity = 1}) { g = (g > 255) ? 255 : g; b = (b > 255) ? 255 : b; int a = opacity.toInt(); + + // Calculate and return the hex representation of the color. return int.parse( '0x${a.toRadixString(16)}${r.toRadixString(16)}${g.toRadixString(16)}${b.toRadixString(16)}'); } diff --git a/lib/src/html_tags.dart b/lib/src/html_tags.dart index 9969f9d..e10cdce 100644 --- a/lib/src/html_tags.dart +++ b/lib/src/html_tags.dart @@ -29,6 +29,9 @@ class HTMLTags { static const br = 'br'; static const tableheader = "th"; static const tabledata = "td"; + static const section = 'section'; + static const font = 'font'; + static const mark = 'mark'; static List formattingElements = [ HTMLTags.anchor, @@ -41,6 +44,8 @@ class HTMLTags { HTMLTags.span, HTMLTags.code, HTMLTags.strikethrough, + HTMLTags.font, + HTMLTags.mark, ]; static List specialElements = [ @@ -59,22 +64,9 @@ class HTMLTags { HTMLTags.paragraph, HTMLTags.blockQuote, HTMLTags.checkbox, - HTMLTags.image + HTMLTags.image, + HTMLTags.section, ]; - - static bool isTopLevel(String tag) { - return tag == h1 || - tag == h2 || - tag == h3 || - tag == h4 || - tag == h5 || - tag == h6 || - tag == table || - tag == checkbox || - tag == paragraph || - tag == div || - tag == blockQuote; - } } enum AttributeType { table, tablerow, none } diff --git a/lib/src/html_to_widgets.dart b/lib/src/html_to_widgets.dart index 3905ff4..3b25266 100644 --- a/lib/src/html_to_widgets.dart +++ b/lib/src/html_to_widgets.dart @@ -48,15 +48,15 @@ class WidgetsHTMLDecoder { Future> _parseElement( Iterable domNodes, ) async { - final List delta = []; final result = []; //find dom node in and check if its element or not than convert it according to its specs for (final domNode in domNodes) { if (domNode is dom.Element) { final localName = domNode.localName; + // Check if the element is a simple formatting element like , , or if (HTMLTags.formattingElements.contains(localName)) { - final attributes = await _parserFormattingElementAttributes(domNode); + final attributes = _parserFormattingElementAttributes(domNode); result.add(Text(domNode.text, style: attributes)); } else if (HTMLTags.specialElements.contains(localName)) { @@ -70,16 +70,14 @@ class WidgetsHTMLDecoder { } } else if (domNode is dom.Text) { // Process text nodes and add them to delta - delta.add(Text(domNode.text, + result.add(Text(domNode.text, style: TextStyle(font: font, fontFallback: fontFallback))); } else { assert(false, 'Unknown node type: $domNode'); } } // If there are text nodes in delta, wrap them in a Wrap widget and add to the result - if (delta.isNotEmpty) { - result.add(Wrap(children: delta)); - } + return result; } @@ -92,22 +90,22 @@ class WidgetsHTMLDecoder { switch (localName) { // Handle heading level 1 case HTMLTags.h1: - return [await _parseHeadingElement(element, level: 1)]; + return [_parseHeadingElement(element, level: 1)]; // Handle heading level 2 case HTMLTags.h2: - return [await _parseHeadingElement(element, level: 2)]; + return [_parseHeadingElement(element, level: 2)]; // Handle heading level 3 case HTMLTags.h3: - return [await _parseHeadingElement(element, level: 3)]; + return [_parseHeadingElement(element, level: 3)]; // Handle heading level 4 case HTMLTags.h4: - return [await _parseHeadingElement(element, level: 4)]; + return [_parseHeadingElement(element, level: 4)]; // Handle heading level 5 case HTMLTags.h5: - return [await _parseHeadingElement(element, level: 5)]; + return [_parseHeadingElement(element, level: 5)]; // Handle heading level 6 case HTMLTags.h6: - return [await _parseHeadingElement(element, level: 6)]; + return [_parseHeadingElement(element, level: 6)]; // Handle unorder list case HTMLTags.unorderedList: return await _parseUnOrderListElement(element); @@ -145,33 +143,24 @@ class WidgetsHTMLDecoder { } /// Parses the attributes of a formatting element and returns a TextStyle. - Future _parserFormattingElementAttributes( - dom.Element element) async { + TextStyle _parserFormattingElementAttributes(dom.Element element) { final localName = element.localName; TextStyle attributes = TextStyle(fontFallback: fontFallback, font: font); final List decoration = []; switch (localName) { // Handle element - case HTMLTags.bold: - attributes = attributes.copyWith(fontWeight: FontWeight.bold) - ..merge(customStyles.boldStyle); - break; - // Handle element - case HTMLTags.strong: + case HTMLTags.bold || HTMLTags.strong: attributes = attributes.copyWith(fontWeight: FontWeight.bold) ..merge(customStyles.boldStyle); break; - // Handle element - case HTMLTags.em: - attributes = attributes.copyWith(fontStyle: FontStyle.italic) - ..merge(customStyles.italicStyle); - break; - // Handle element - case HTMLTags.italic: + + // Handle element + case HTMLTags.italic || HTMLTags.em: attributes = attributes.copyWith(fontStyle: FontStyle.italic) ..merge(customStyles.italicStyle); break; + // Handle element case HTMLTags.underline: decoration.add(TextDecoration.underline); @@ -181,8 +170,8 @@ class WidgetsHTMLDecoder { decoration.add(TextDecoration.lineThrough); break; - // Handle element - case HTMLTags.span: + // Handle element + case HTMLTags.span || HTMLTags.mark: final deltaAttributes = _getDeltaAttributesFromHtmlAttributes( element.attributes, ); @@ -202,10 +191,7 @@ class WidgetsHTMLDecoder { ..merge(customStyles.linkStyle); } break; - // Handle

    element for additional safety - case HTMLTags.paragraph: - attributes = attributes..merge(customStyles.paragraphStyle); - break; + // Handle element case HTMLTags.code: attributes = attributes.copyWith( @@ -217,12 +203,13 @@ class WidgetsHTMLDecoder { } for (final child in element.children) { - final nattributes = await _parserFormattingElementAttributes(child); + final nattributes = _parserFormattingElementAttributes(child); attributes = attributes.merge(nattributes); if (nattributes.decoration != null) { decoration.add(nattributes.decoration!); } } + //will combine style get from the children return attributes.copyWith(decoration: TextDecoration.combine(decoration)); } @@ -305,7 +292,7 @@ class WidgetsHTMLDecoder { final localName = element.localName; // Check if the element is a simple formatting element like , , or if (HTMLTags.formattingElements.contains(localName)) { - final attributes = await _parserFormattingElementAttributes(element); + final attributes = _parserFormattingElementAttributes(element); result.add(Text(element.text, style: attributes)); } else if (HTMLTags.specialElements.contains(localName)) { @@ -332,15 +319,15 @@ class WidgetsHTMLDecoder { } // Function to parse a heading element and return a RichText widget - Future _parseHeadingElement( + Widget _parseHeadingElement( dom.Element element, { required int level, - }) async { + }) { final delta = []; final children = element.nodes.toList(); for (final child in children) { if (child is dom.Element) { - final attributes = await _parserFormattingElementAttributes(child); + final attributes = _parserFormattingElementAttributes(child); delta.add(TextSpan(text: child.text, style: attributes)); } else { delta.add(TextSpan( @@ -360,9 +347,14 @@ class WidgetsHTMLDecoder { // Function to parse a block quote element and return a list of widgets Future> _parseBlockQuoteElement(dom.Element element) async { final result = []; - for (final child in element.children) { - result.addAll( - await _parseListElement(child, type: BuiltInAttributeKey.quote)); + if (element.children.isNotEmpty) { + for (final child in element.children) { + result.addAll( + await _parseListElement(child, type: BuiltInAttributeKey.quote)); + } + } else { + result.add( + buildQuotewidget(Text(element.text), customStyles: customStyles)); } return result; } @@ -370,9 +362,15 @@ class WidgetsHTMLDecoder { // Function to parse an unordered list element and return a list of widgets Future> _parseUnOrderListElement(dom.Element element) async { final result = []; - for (final child in element.children) { - result.addAll(await _parseListElement(child, - type: BuiltInAttributeKey.bulletedList)); + + if (element.children.isNotEmpty) { + for (final child in element.children) { + result.addAll(await _parseListElement(child, + type: BuiltInAttributeKey.bulletedList)); + } + } else { + result.add( + buildBulletwidget(Text(element.text), customStyles: customStyles)); } return result; } @@ -380,10 +378,16 @@ class WidgetsHTMLDecoder { // Function to parse an ordered list element and return a list of widgets Future> _parseOrderListElement(dom.Element element) async { final result = []; - for (var i = 0; i < element.children.length; i++) { - final child = element.children[i]; - result.addAll(await _parseListElement(child, - type: BuiltInAttributeKey.numberList, index: i + 1)); + + if (element.children.isNotEmpty) { + for (var i = 0; i < element.children.length; i++) { + final child = element.children[i]; + result.addAll(await _parseListElement(child, + type: BuiltInAttributeKey.numberList, index: i + 1)); + } + } else { + result.add(buildNumberwdget(Text(element.text), + fontFallback: fontFallback, customStyles: customStyles, index: 1)); } return result; } @@ -456,10 +460,12 @@ class WidgetsHTMLDecoder { final delta = []; final children = element.nodes.toList(); final childNodes = []; + for (final child in children) { // Recursively parse child elements if (child is dom.Element) { - if (child.children.isNotEmpty) { + if (child.children.isNotEmpty && + HTMLTags.formattingElements.contains(child.localName) == false) { childNodes.addAll(await _parseElement(child.children)); } else { // Handle special elements (e.g., headings, lists) within a paragraph @@ -472,14 +478,15 @@ class WidgetsHTMLDecoder { ); } else { // Parse text and attributes within the paragraph - final attributes = await _parserFormattingElementAttributes(child) + final attributes = _parserFormattingElementAttributes(child) ..merge(customStyles.paragraphStyle); - delta.add(Text(child.text, style: attributes)); + delta.add(Text(child.text.replaceAll(RegExp(r'\n+$'), ''), + style: attributes)); } } } else { // Process text nodes and add them to delta variable - delta.add(Text(child.text ?? "", + delta.add(Text(child.text?.replaceAll(RegExp(r'\n+$'), '') ?? "", style: TextStyle(font: font, fontFallback: fontFallback) ..merge(customStyles.paragraphStyle))); } @@ -492,20 +499,18 @@ class WidgetsHTMLDecoder { // Utility function to convert a CSS string to a map of CSS properties static Map _cssStringToMap(String? cssString) { - final result = {}; + final Map result = {}; if (cssString == null) { return result; } -// Split the CSS string into key-value pairs and add them to the result map - final entries = cssString.split(";"); + final entries = cssString.split(';'); for (final entry in entries) { - final tuples = entry.split(":"); + final tuples = entry.split(':'); if (tuples.length < 2) { continue; } result[tuples[0].trim()] = tuples[1].trim(); } - return result; } diff --git a/lib/src/pdfwidgets/bullet_list.dart b/lib/src/pdfwidgets/bullet_list.dart index 8be170e..b2b1e13 100644 --- a/lib/src/pdfwidgets/bullet_list.dart +++ b/lib/src/pdfwidgets/bullet_list.dart @@ -1,23 +1,29 @@ +// Import the necessary dependencies from the 'htmltopdfwidgets.dart' file. import '../../htmltopdfwidgets.dart'; -Widget buildBulletwidget(Widget childValue, - {required HtmlTagStyle customStyles}) { +// This function creates a bullet list child widget with a bullet icon and content. +// It takes a 'childValue' widget and 'customStyles' for styling. +Widget buildBulletwidget(Widget childValue, {required HtmlTagStyle customStyles}) { + // Create a container to hold the child elements. Widget child = Container( child: Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - _BulletedListIcon(style: customStyles), - Flexible(child: childValue), + _BulletedListIcon(style: customStyles), // Include the bullet icon. + Flexible(child: childValue), // Include the main content child widget. ], ), ); - return child; + return child; // Return the resulting child widget. } +// This private class represents the bullet list icon. class _BulletedListIcon extends StatelessWidget { final HtmlTagStyle style; + + // Constructor to initialize the 'style' property. _BulletedListIcon({required this.style}); @override @@ -28,12 +34,15 @@ class _BulletedListIcon extends StatelessWidget { child: Padding( padding: const EdgeInsets.only(right: 5.0), child: Center( - child: Container( - width: 5, - height: 5, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: style.bulletListIconColor ?? PdfColors.black))), + child: Container( + width: 5, + height: 5, + decoration: BoxDecoration( + shape: BoxShape.circle, // Bullet icon is circular. + color: style.bulletListIconColor ?? PdfColors.black, // Apply custom color. + ), + ), + ), ), ); } diff --git a/lib/src/pdfwidgets/number_list.dart b/lib/src/pdfwidgets/number_list.dart index 5b6532d..311c68a 100644 --- a/lib/src/pdfwidgets/number_list.dart +++ b/lib/src/pdfwidgets/number_list.dart @@ -1,5 +1,9 @@ +// Import the necessary dependencies from the 'htmltopdfwidgets.dart' file. import '../../htmltopdfwidgets.dart'; +// This function creates a default index (number) widget for a numbered list. +// It takes an 'index' (the current number), a 'font', a list of 'fontFallback' fonts, +// and 'customStyles' for styling. Widget defaultIndex(int index, {Font? font, required List fontFallback, @@ -7,15 +11,18 @@ Widget defaultIndex(int index, return Container( width: 20, padding: const EdgeInsets.only(right: 5.0), - child: Text('$index.', + child: Text('$index.', // Display the index as text. style: TextStyle( - font: font, - fontFallback: fontFallback, - )..merge(customStyles.listIndexStyle)), + font: font, // Apply the specified font. + fontFallback: fontFallback, // Use font fallbacks if needed. + )..merge( + customStyles.listIndexStyle)), // Apply custom styles for the index. ); } -//return the number list child with its current number and its all properties +// This function creates a numbered list child widget with its current number and properties. +// It takes a 'childValue' widget, 'index' (the current number), a 'font', +// a list of 'fontFallback' fonts, and 'customStyles' for styling. Widget buildNumberwdget(Widget childValue, {required int index, Font? font, @@ -29,9 +36,10 @@ Widget buildNumberwdget(Widget childValue, children: [ defaultIndex(index, fontFallback: fontFallback, font: font, customStyles: customStyles), - Flexible(child: childValue), + // Include the default index widget with specified properties. + Flexible(child: childValue), // Include the main content child widget. ], ), ); - return child; + return child; // Return the resulting child widget. } diff --git a/lib/src/pdfwidgets/quote_widget.dart b/lib/src/pdfwidgets/quote_widget.dart index d6e12c7..2246ef0 100644 --- a/lib/src/pdfwidgets/quote_widget.dart +++ b/lib/src/pdfwidgets/quote_widget.dart @@ -1,21 +1,34 @@ +// Import the necessary dependencies from the 'htmltopdfwidgets.dart' file. import '../../htmltopdfwidgets.dart'; -Widget buildQuotewidget(Widget childValue, - {required HtmlTagStyle customStyles}) { +// Define a function named 'buildQuotewidget' that takes a 'childValue' Widget +// and a required 'customStyles' parameter of type 'HtmlTagStyle'. +Widget buildQuotewidget(Widget childValue, {required HtmlTagStyle customStyles}) { + // Create a Widget named 'child' which will be returned by this function. Widget child = Container( + // Create a Container widget to hold the child elements. child: Row( + // Create a Row widget to arrange its children horizontally. crossAxisAlignment: CrossAxisAlignment.start, + // Align children vertically at the top of the row. mainAxisAlignment: MainAxisAlignment.start, + // Align children horizontally to the start of the row. mainAxisSize: MainAxisSize.min, + // Allow the row to occupy the minimum horizontal space necessary. children: [ SizedBox( - width: 20, - height: 20, - child: VerticalDivider( - color: customStyles.quoteBarColor ?? PdfColors.black)), - Flexible(child: childValue), + width: 20, + height: 20, + child: VerticalDivider( + // Create a SizedBox with a fixed width and height and add a VerticalDivider. + color: customStyles.quoteBarColor ?? PdfColors.black, + // Set the divider color to 'customStyles.quoteBarColor', if defined, + // otherwise use PdfColors.black as the default color. + ), + ), + Flexible(child: childValue), // Add the 'childValue' Widget inside a Flexible container. ], ), ); - return child; + return child; // Return the 'child' widget as the result of this function. } diff --git a/pubspec.yaml b/pubspec.yaml index ec39b44..8c46090 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,15 +1,21 @@ name: htmltopdfwidgets description: Htmlt to pdf widgets library convert html text to pdf widgets -version: 0.0.8+2 +version: 0.0.9 homepage: https://github.com/alihassan143/htmltopdfwidgets environment: - sdk: ">=2.18.0 <4.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: pdf: '>=3.10.3 <4.0.0' html: '>=0.15.3 <1.0.0' http: '>=0.13.1 <2.0.0' +dependency_overrides: + pdf: + git: + url: https://github.com/DavBfr/dart_pdf.git + path: pdf/ + ref: master dev_dependencies: flutter_lints: ^2.0.0 test: ">=1.16.0 <2.0.0" \ No newline at end of file From 199592f56ed088f28fa412782527ce312bd72d5b Mon Sep 17 00:00:00 2001 From: alihassan143 Date: Sat, 30 Sep 2023 13:12:02 +0500 Subject: [PATCH 2/4] code formatted --- CHANGELOG.md | 6 ++++++ lib/src/attributes.dart | 24 ++++++++++++++++-------- lib/src/pdfwidgets/bullet_list.dart | 6 ++++-- lib/src/pdfwidgets/quote_widget.dart | 7 +++++-- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 300c737..a93b017 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.0.9+1 + +* optimiz parse logic +* documentation fixs +* using override dependency for pdf due to underline and italic issues +* update readme ## 0.0.9 * optimiz parse logic diff --git a/lib/src/attributes.dart b/lib/src/attributes.dart index 6931fd4..95072d0 100644 --- a/lib/src/attributes.dart +++ b/lib/src/attributes.dart @@ -7,15 +7,19 @@ class BuiltInAttributeKey { static String bold = 'bold'; // Text should be displayed in bold. static String italic = 'italic'; // Text should be displayed in italic. static String underline = 'underline'; // Text should be underlined. - static String strikethrough = 'strikethrough'; // Text should have a strikethrough line. + static String strikethrough = + 'strikethrough'; // Text should have a strikethrough line. static String color = 'color'; // Text color customization. - static String backgroundColor = 'backgroundColor'; // Background color customization. + static String backgroundColor = + 'backgroundColor'; // Background color customization. static String font = 'font'; // Font customization. static String href = 'href'; // Hyperlink attribute for text. // Global rendering types - static String subtype = 'subtype'; // Subtype for customizing rendering behavior. - static String heading = 'heading'; // Text should be treated as a heading (h1, h2, h3, etc.). + static String subtype = + 'subtype'; // Subtype for customizing rendering behavior. + static String heading = + 'heading'; // Text should be treated as a heading (h1, h2, h3, etc.). static String h1 = 'h1'; // Heading level 1. static String h2 = 'h2'; // Heading level 2. static String h3 = 'h3'; // Heading level 3. @@ -23,13 +27,17 @@ class BuiltInAttributeKey { static String h5 = 'h5'; // Heading level 5. static String h6 = 'h6'; // Heading level 6. - static String bulletedList = 'bulleted-list'; // Text should be displayed in a bulleted list. - static String numberList = 'number-list'; // Text should be displayed in a numbered list. + static String bulletedList = + 'bulleted-list'; // Text should be displayed in a bulleted list. + static String numberList = + 'number-list'; // Text should be displayed in a numbered list. static String quote = 'quote'; // Text should be displayed as a blockquote. - static String checkbox = 'checkbox'; // Text should be displayed as a checkbox. + static String checkbox = + 'checkbox'; // Text should be displayed as a checkbox. static String code = 'code'; // Text should be displayed as code. - static String number = 'number'; // Text should be displayed as a numbered item. + static String number = + 'number'; // Text should be displayed as a numbered item. // Lists of partial style keys and global style keys static List partialStyleKeys = [ diff --git a/lib/src/pdfwidgets/bullet_list.dart b/lib/src/pdfwidgets/bullet_list.dart index b2b1e13..c0980e2 100644 --- a/lib/src/pdfwidgets/bullet_list.dart +++ b/lib/src/pdfwidgets/bullet_list.dart @@ -3,7 +3,8 @@ import '../../htmltopdfwidgets.dart'; // This function creates a bullet list child widget with a bullet icon and content. // It takes a 'childValue' widget and 'customStyles' for styling. -Widget buildBulletwidget(Widget childValue, {required HtmlTagStyle customStyles}) { +Widget buildBulletwidget(Widget childValue, + {required HtmlTagStyle customStyles}) { // Create a container to hold the child elements. Widget child = Container( child: Row( @@ -39,7 +40,8 @@ class _BulletedListIcon extends StatelessWidget { height: 5, decoration: BoxDecoration( shape: BoxShape.circle, // Bullet icon is circular. - color: style.bulletListIconColor ?? PdfColors.black, // Apply custom color. + color: style.bulletListIconColor ?? + PdfColors.black, // Apply custom color. ), ), ), diff --git a/lib/src/pdfwidgets/quote_widget.dart b/lib/src/pdfwidgets/quote_widget.dart index 2246ef0..6ee0779 100644 --- a/lib/src/pdfwidgets/quote_widget.dart +++ b/lib/src/pdfwidgets/quote_widget.dart @@ -3,7 +3,8 @@ import '../../htmltopdfwidgets.dart'; // Define a function named 'buildQuotewidget' that takes a 'childValue' Widget // and a required 'customStyles' parameter of type 'HtmlTagStyle'. -Widget buildQuotewidget(Widget childValue, {required HtmlTagStyle customStyles}) { +Widget buildQuotewidget(Widget childValue, + {required HtmlTagStyle customStyles}) { // Create a Widget named 'child' which will be returned by this function. Widget child = Container( // Create a Container widget to hold the child elements. @@ -26,7 +27,9 @@ Widget buildQuotewidget(Widget childValue, {required HtmlTagStyle customStyles}) // otherwise use PdfColors.black as the default color. ), ), - Flexible(child: childValue), // Add the 'childValue' Widget inside a Flexible container. + Flexible( + child: + childValue), // Add the 'childValue' Widget inside a Flexible container. ], ), ); From 75904f05c0a770b6d4bfed64871a2de2292f8288 Mon Sep 17 00:00:00 2001 From: alihassan143 Date: Sat, 30 Sep 2023 13:12:41 +0500 Subject: [PATCH 3/4] readme changes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3c2cfe0..b070671 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Add the following dependency to your `pubspec.yaml` file: ```yaml dependencies: - htmltopdfwidgets: ^0.0.8+2 + htmltopdfwidgets: ^0.0.9+1 ``` ## Usage From 963cba23fe2b02599c90667894232f11e3f95023 Mon Sep 17 00:00:00 2001 From: alihassan143 Date: Sat, 30 Sep 2023 13:13:42 +0500 Subject: [PATCH 4/4] yml file change --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 8c46090..e3d0908 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: htmltopdfwidgets description: Htmlt to pdf widgets library convert html text to pdf widgets -version: 0.0.9 +version: 0.0.9+1 homepage: https://github.com/alihassan143/htmltopdfwidgets environment: