1+ // "Therefore those skilled at the unorthodox
2+ // are infinite as heaven and earth,
3+ // inexhaustible as the great rivers.
4+ // When they come to an end,
5+ // they begin again,
6+ // like the days and months;
7+ // they die and are reborn,
8+ // like the four seasons."
9+ //
10+ // - Sun Tsu,
11+ // "The Art of War"
12+
13+ using HtmlRenderer . Core . Dom ;
14+ using SkiaSharp ;
15+ using Svg . Skia ;
16+ using System ;
17+ using TheArtOfDev . HtmlRenderer . Core ;
18+ using TheArtOfDev . HtmlRenderer . Core . Entities ;
19+ using TheArtOfDev . HtmlRenderer . Core . Utils ;
20+ using TheArtOfDev . HtmlRenderer . SkiaSharp . Adapters ;
21+ using TheArtOfDev . HtmlRenderer . SkiaSharp . Utilities ;
22+
23+ namespace TheArtOfDev . HtmlRenderer . SkiaSharp
24+ {
25+ /// <summary>
26+ /// TODO:a add doc
27+ /// </summary>
28+ public static class ImageGenerator
29+ {
30+ /// <summary>
31+ /// Adds a font mapping from <paramref name="fromFamily"/> to <paramref name="toFamily"/> iff the <paramref name="fromFamily"/> is not found.<br/>
32+ /// When the <paramref name="fromFamily"/> font is used in rendered html and is not found in existing
33+ /// fonts (installed or added) it will be replaced by <paramref name="toFamily"/>.<br/>
34+ /// </summary>
35+ /// <remarks>
36+ /// This fonts mapping can be used as a fallback in case the requested font is not installed in the client system.
37+ /// </remarks>
38+ /// <param name="fromFamily">the font family to replace</param>
39+ /// <param name="toFamily">the font family to replace with</param>
40+ public static void AddFontFamilyMapping ( string fromFamily , string toFamily )
41+ {
42+ ArgChecker . AssertArgNotNullOrEmpty ( fromFamily , "fromFamily" ) ;
43+ ArgChecker . AssertArgNotNullOrEmpty ( toFamily , "toFamily" ) ;
44+
45+ SkiaSharpAdapter . Instance . AddFontFamilyMapping ( fromFamily , toFamily ) ;
46+ }
47+
48+ /// <summary>
49+ /// Parse the given stylesheet to <see cref="CssData"/> object.<br/>
50+ /// If <paramref name="combineWithDefault"/> is true the parsed css blocks are added to the
51+ /// default css data (as defined by W3), merged if class name already exists. If false only the data in the given stylesheet is returned.
52+ /// </summary>
53+ /// <seealso cref="http://www.w3.org/TR/CSS21/sample.html"/>
54+ /// <param name="stylesheet">the stylesheet source to parse</param>
55+ /// <param name="combineWithDefault">true - combine the parsed css data with default css data, false - return only the parsed css data</param>
56+ /// <returns>the parsed css data</returns>
57+ public static CssData ParseStyleSheet ( string stylesheet , bool combineWithDefault = true )
58+ {
59+ return CssData . Parse ( SkiaSharpAdapter . Instance , stylesheet , combineWithDefault ) ;
60+ }
61+
62+ /// <summary>
63+ /// Create Svg document from given HTML.<br/>
64+ /// </summary>
65+ /// <param name="html">HTML source to create image from</param>
66+ /// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
67+ /// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
68+ /// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
69+ /// <returns>the generated image of the html</returns>
70+ public static async Task < SKCanvas > GenerateSvgAsync (
71+ string html ,
72+ Stream outputStream ,
73+ SKSize size ,
74+ CssData cssData = null ,
75+ EventHandler < HtmlStylesheetLoadEventArgs > stylesheetLoad = null ,
76+ EventHandler < HtmlImageLoadEventArgs > imageLoad = null )
77+ {
78+ // create svg document to render the HTML into
79+ var canvas = SKSvgCanvas . Create ( new SKRect ( 0 , 0 , size . Width , size . Height ) , outputStream ) ;
80+
81+ // add rendered image
82+ await DrawSvgAsync ( canvas , html , size , cssData , stylesheetLoad , imageLoad ) ;
83+ canvas . Dispose ( ) ;
84+
85+ return canvas ;
86+ }
87+
88+ /// <summary>
89+ /// Writes html to a bitmap image
90+ /// </summary>
91+ /// <param name="html">HTML source to create image from</param>
92+ /// <param name="size">The size of the image</param>
93+ /// <param name="imageFormat">The file format used to encode the image.</param>
94+ /// <param name="quality">The quality level to use for the image. Quality range from 0-100. Higher values correspond to improved visual quality, but less compression.</param>
95+ /// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
96+ /// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
97+ /// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
98+ /// <returns></returns>
99+ public static async Task < SKCanvas > GenerateBitmapAsync (
100+ string html ,
101+ Stream outputStream ,
102+ SKSize size ,
103+ SKEncodedImageFormat imageFormat ,
104+ int quality ,
105+ CssData cssData = null ,
106+ EventHandler < HtmlStylesheetLoadEventArgs > stylesheetLoad = null ,
107+ EventHandler < HtmlImageLoadEventArgs > imageLoad = null )
108+ {
109+
110+ var bitmap = new SKBitmap ( ( int ) size . Width , ( int ) size . Height ) ;
111+ var canvas = new SKCanvas ( bitmap ) ;
112+
113+ // add rendered image
114+ await DrawSvgAsync ( canvas , html , size , cssData , stylesheetLoad , imageLoad ) ;
115+ bitmap . Encode ( outputStream , imageFormat , quality ) ;
116+
117+ return canvas ;
118+ }
119+
120+ /// <summary>
121+ /// Create image pages from given HTML and appends them to the provided image document.<br/>
122+ /// </summary>
123+ /// <param name="canvas">canvas to draw to</param>
124+ /// <param name="html">HTML source to create image from</param>
125+ /// <param name="cssData">optional: the style to use for html rendering (default - use W3 default style)</param>
126+ /// <param name="stylesheetLoad">optional: can be used to overwrite stylesheet resolution logic</param>
127+ /// <param name="imageLoad">optional: can be used to overwrite image resolution logic</param>
128+ /// <returns>the generated image of the html</returns>
129+ public static async Task DrawSvgAsync (
130+ SKCanvas canvas ,
131+ string html ,
132+ SKSize size ,
133+ CssData cssData = null ,
134+ EventHandler < HtmlStylesheetLoadEventArgs > stylesheetLoad = null ,
135+ EventHandler < HtmlImageLoadEventArgs > imageLoad = null )
136+ {
137+ using var container = new HtmlContainer ( ) ;
138+ if ( stylesheetLoad != null )
139+ container . StylesheetLoad += stylesheetLoad ;
140+ if ( imageLoad != null )
141+ container . ImageLoad += imageLoad ;
142+
143+ container . Location = new SKPoint ( 0 , 0 ) ;
144+ //container.MaxSize = size;
145+ container . MaxSize = new SKSize ( size . Width , 0 ) ;
146+ container . PageSize = size ;
147+ container . MarginBottom = 0 ;
148+ container . MarginLeft = 0 ;
149+ container . MarginRight = 0 ;
150+ container . MarginTop = 0 ;
151+ container . ScrollOffset = new SKPoint ( 0 , 0 ) ;
152+
153+ await container . SetHtml ( html , cssData ) ;
154+
155+ // layout the HTML with the page width restriction to know how many pages are required
156+ await container . PerformLayout ( canvas ) ;
157+ await container . PerformPaint ( canvas ) ;
158+ }
159+ }
160+ }
0 commit comments