├── .gitattributes ├── .gitignore ├── README.md ├── dll ├── WpfRichText.dll ├── Xceed.Wpf.AvalonDock.Themes.Aero.dll ├── Xceed.Wpf.AvalonDock.Themes.Metro.dll ├── Xceed.Wpf.AvalonDock.Themes.VS2010.dll ├── Xceed.Wpf.AvalonDock.dll ├── Xceed.Wpf.DataGrid.dll ├── Xceed.Wpf.Toolkit.dll ├── de │ └── Xceed.Wpf.AvalonDock.resources.dll ├── es │ └── Xceed.Wpf.AvalonDock.resources.dll ├── fr │ └── Xceed.Wpf.AvalonDock.resources.dll ├── hu │ └── Xceed.Wpf.AvalonDock.resources.dll ├── it │ └── Xceed.Wpf.AvalonDock.resources.dll ├── pt-BR │ └── Xceed.Wpf.AvalonDock.resources.dll ├── ro │ └── Xceed.Wpf.AvalonDock.resources.dll ├── ru │ └── Xceed.Wpf.AvalonDock.resources.dll ├── sv │ └── Xceed.Wpf.AvalonDock.resources.dll └── zh-Hans │ └── Xceed.Wpf.AvalonDock.resources.dll ├── logo.png ├── nuget ├── WpfRichText.1.0.10.nupkg ├── WpfRichText.1.0.11.nupkg ├── WpfRichText.1.0.12.nupkg ├── WpfRichText.1.0.13.nupkg ├── WpfRichText.1.0.14.nupkg ├── WpfRichText.1.0.15.nupkg ├── WpfRichText.1.0.16.2.nupkg ├── WpfRichText.1.0.3.nupkg ├── WpfRichText.1.0.4.nupkg ├── WpfRichText.1.0.5.nupkg ├── WpfRichText.1.0.6.nupkg ├── WpfRichText.1.0.7.nupkg ├── WpfRichText.1.0.8.nupkg └── WpfRichText.1.0.9.nupkg └── src ├── WpfRichText.Ex ├── App.xaml ├── App.xaml.cs ├── HTMLPage1.html ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── Window1.xaml ├── Window1.xaml.cs ├── WpfRichText.Example.csproj ├── app.config └── packages.config ├── WpfRichText.sln └── WpfRichText ├── AttachedProperties └── RichTextboxAssistant.cs ├── Commands ├── CommandReference.cs └── DelegateCommand.cs ├── Controls ├── Images │ ├── charactergrowfont.png │ ├── charactershrinkfont.png │ ├── cross.png │ ├── cut.png │ ├── editredo.png │ ├── editundo.png │ ├── page_copy.png │ ├── page_paste.png │ ├── text_align_center.png │ ├── text_align_justify.png │ ├── text_align_left.png │ ├── text_align_right.png │ ├── text_bold.png │ ├── text_indent.png │ ├── text_indent_remove.png │ ├── text_italic.png │ ├── text_list_bullets.png │ ├── text_list_numbers.png │ ├── text_underline.png │ ├── tick.png │ └── world_link.png ├── RichTextEditor.xaml └── RichTextEditor.xaml.cs ├── Properties ├── AssemblyInfo.cs ├── Resources.Designer.cs ├── Resources.resx ├── Settings.Designer.cs └── Settings.settings ├── WpfRichText.csproj ├── XamlToHtmlParser ├── HtmlCssParser.cs ├── HtmlFromXamlConverter.cs ├── HtmlLexicalAnalyzer.cs ├── HtmlParser.cs ├── HtmlSchema.cs ├── HtmlToXamlConverter.cs └── HtmlTokenType.cs ├── packages.config └── self.snk /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set default behaviour to automatically normalize line endings. 2 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | [Bb]in/ 3 | [Oo]bj/ 4 | 5 | # mstest test results 6 | TestResults 7 | 8 | ## Ignore Visual Studio temporary files, build results, and 9 | ## files generated by popular Visual Studio add-ons. 10 | 11 | # User-specific files 12 | *.suo 13 | *.user 14 | *.sln.docstates 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Rr]elease/ 19 | x64/ 20 | *_i.c 21 | *_p.c 22 | *.ilk 23 | *.meta 24 | *.obj 25 | *.pch 26 | *.pdb 27 | *.pgc 28 | *.pgd 29 | *.rsp 30 | *.sbr 31 | *.tlb 32 | *.tli 33 | *.tlh 34 | *.tmp 35 | *.log 36 | *.vspscc 37 | *.vssscc 38 | .builds 39 | 40 | # Visual C++ cache files 41 | ipch/ 42 | *.aps 43 | *.ncb 44 | *.opensdf 45 | *.sdf 46 | 47 | # Visual Studio profiler 48 | *.psess 49 | *.vsp 50 | *.vspx 51 | 52 | # Guidance Automation Toolkit 53 | *.gpState 54 | 55 | # ReSharper is a .NET coding add-in 56 | _ReSharper* 57 | 58 | # NCrunch 59 | *.ncrunch* 60 | .*crunch*.local.xml 61 | 62 | # Installshield output folder 63 | [Ee]xpress 64 | 65 | # DocProject is a documentation generator add-in 66 | DocProject/buildhelp/ 67 | DocProject/Help/*.HxT 68 | DocProject/Help/*.HxC 69 | DocProject/Help/*.hhc 70 | DocProject/Help/*.hhk 71 | DocProject/Help/*.hhp 72 | DocProject/Help/Html2 73 | DocProject/Help/html 74 | 75 | # Click-Once directory 76 | publish 77 | 78 | # Publish Web Output 79 | *.Publish.xml 80 | 81 | # NuGet Packages Directory 82 | packages 83 | 84 | # Windows Azure Build Output 85 | csx 86 | *.build.csdef 87 | 88 | # Windows Store app package directory 89 | AppPackages/ 90 | 91 | # Others 92 | [Bb]in 93 | [Oo]bj 94 | sql 95 | TestResults 96 | [Tt]est[Rr]esult* 97 | *.Cache 98 | ClientBin 99 | [Ss]tyle[Cc]op.* 100 | ~$* 101 | *.dbmdl 102 | Generated_Code #added for RIA/Silverlight projects 103 | 104 | # Backup & report files from converting an old project file to a newer 105 | # Visual Studio version. Backup files are not needed, because we have git ;-) 106 | _UpgradeReport_Files/ 107 | Backup*/ 108 | UpgradeLog*.XML 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WPF-RichText-Editor 2 | =================== 3 | 4 | WPF RichText Editor Control with conversion from and to HTML (XAML) 5 | 6 | Code pulled from http://michaelsync.net/2009/06/09/bindable-wpf-richtext-editor-with-xamlhtml-convertor. 7 | Thanks to Michael Sync for this. 8 | 9 | Moved the code to a UserControlLibrary project and added a nuget package (https://nuget.org/packages/WpfRichText/) 10 | -------------------------------------------------------------------------------- /dll/WpfRichText.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/WpfRichText.dll -------------------------------------------------------------------------------- /dll/Xceed.Wpf.AvalonDock.Themes.Aero.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/Xceed.Wpf.AvalonDock.Themes.Aero.dll -------------------------------------------------------------------------------- /dll/Xceed.Wpf.AvalonDock.Themes.Metro.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/Xceed.Wpf.AvalonDock.Themes.Metro.dll -------------------------------------------------------------------------------- /dll/Xceed.Wpf.AvalonDock.Themes.VS2010.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/Xceed.Wpf.AvalonDock.Themes.VS2010.dll -------------------------------------------------------------------------------- /dll/Xceed.Wpf.AvalonDock.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/Xceed.Wpf.AvalonDock.dll -------------------------------------------------------------------------------- /dll/Xceed.Wpf.DataGrid.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/Xceed.Wpf.DataGrid.dll -------------------------------------------------------------------------------- /dll/Xceed.Wpf.Toolkit.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/Xceed.Wpf.Toolkit.dll -------------------------------------------------------------------------------- /dll/de/Xceed.Wpf.AvalonDock.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/de/Xceed.Wpf.AvalonDock.resources.dll -------------------------------------------------------------------------------- /dll/es/Xceed.Wpf.AvalonDock.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/es/Xceed.Wpf.AvalonDock.resources.dll -------------------------------------------------------------------------------- /dll/fr/Xceed.Wpf.AvalonDock.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/fr/Xceed.Wpf.AvalonDock.resources.dll -------------------------------------------------------------------------------- /dll/hu/Xceed.Wpf.AvalonDock.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/hu/Xceed.Wpf.AvalonDock.resources.dll -------------------------------------------------------------------------------- /dll/it/Xceed.Wpf.AvalonDock.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/it/Xceed.Wpf.AvalonDock.resources.dll -------------------------------------------------------------------------------- /dll/pt-BR/Xceed.Wpf.AvalonDock.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/pt-BR/Xceed.Wpf.AvalonDock.resources.dll -------------------------------------------------------------------------------- /dll/ro/Xceed.Wpf.AvalonDock.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/ro/Xceed.Wpf.AvalonDock.resources.dll -------------------------------------------------------------------------------- /dll/ru/Xceed.Wpf.AvalonDock.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/ru/Xceed.Wpf.AvalonDock.resources.dll -------------------------------------------------------------------------------- /dll/sv/Xceed.Wpf.AvalonDock.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/sv/Xceed.Wpf.AvalonDock.resources.dll -------------------------------------------------------------------------------- /dll/zh-Hans/Xceed.Wpf.AvalonDock.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/zh-Hans/Xceed.Wpf.AvalonDock.resources.dll -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/logo.png -------------------------------------------------------------------------------- /nuget/WpfRichText.1.0.10.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.10.nupkg -------------------------------------------------------------------------------- /nuget/WpfRichText.1.0.11.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.11.nupkg -------------------------------------------------------------------------------- /nuget/WpfRichText.1.0.12.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.12.nupkg -------------------------------------------------------------------------------- /nuget/WpfRichText.1.0.13.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.13.nupkg -------------------------------------------------------------------------------- /nuget/WpfRichText.1.0.14.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.14.nupkg -------------------------------------------------------------------------------- /nuget/WpfRichText.1.0.15.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.15.nupkg -------------------------------------------------------------------------------- /nuget/WpfRichText.1.0.16.2.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.16.2.nupkg -------------------------------------------------------------------------------- /nuget/WpfRichText.1.0.3.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.3.nupkg -------------------------------------------------------------------------------- /nuget/WpfRichText.1.0.4.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.4.nupkg -------------------------------------------------------------------------------- /nuget/WpfRichText.1.0.5.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.5.nupkg -------------------------------------------------------------------------------- /nuget/WpfRichText.1.0.6.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.6.nupkg -------------------------------------------------------------------------------- /nuget/WpfRichText.1.0.7.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.7.nupkg -------------------------------------------------------------------------------- /nuget/WpfRichText.1.0.8.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.8.nupkg -------------------------------------------------------------------------------- /nuget/WpfRichText.1.0.9.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.9.nupkg -------------------------------------------------------------------------------- /src/WpfRichText.Ex/App.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/WpfRichText.Ex/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Windows; 7 | 8 | namespace WpfRichText.Ex 9 | { 10 | /// 11 | /// Interaction logic for App.xaml 12 | /// 13 | public partial class App : Application 14 | { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/WpfRichText.Ex/HTMLPage1.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | New Bug Template 5 | 6 | 7 |

header 1

8 |

9 | RED TEXT 10 |
11 | 1. Text after break 12 |

13 |

header 2

14 |

15 | GREEN TEXT 16 |
17 | a hyperlink 18 |

19 |
20 | TEXT IN DIV 21 |
22 | 23 | -------------------------------------------------------------------------------- /src/WpfRichText.Ex/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("WpfRichText.Ex")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("WpfRichText.Ex")] 15 | [assembly: AssemblyCopyright("Copyright © 2009")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] 56 | -------------------------------------------------------------------------------- /src/WpfRichText.Ex/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.18034 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace WpfRichText.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WpfRichText.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 65 | ///<html> 66 | /// <head> 67 | /// <title>New Bug Template</title> 68 | /// </head> 69 | /// <body> 70 | /// <p> 71 | /// <span style="color:Red; font-weight:bold;">TITEL:</span> 72 | /// <br /> 73 | /// 1. Fehlerbereich angeben (Afterbuy,Shop,MSG) 74 | /// <br /> 75 | /// 2. Genaue Beschreibung des Bugs 76 | /// </p> 77 | /// <p> 78 | /// <span style="color:Red; font-weight:bold;">PROBLEMBESCHREIBUNG:</span> 79 | /// <br /> 80 | /// 1. Genaue Angabe um das Problem zu sehen 81 | /// <br /> 82 | /// 2. Genaue Beschreibung wie es zu dem Bug g [rest of string was truncated]";. 83 | /// 84 | internal static string HTMLPage1 { 85 | get { 86 | return ResourceManager.GetString("HTMLPage1", resourceCulture); 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/WpfRichText.Ex/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | ..\htmlpage1.html;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 123 | 124 | -------------------------------------------------------------------------------- /src/WpfRichText.Ex/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.18034 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace WpfRichText.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/WpfRichText.Ex/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/WpfRichText.Ex/Window1.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 591 | 592 | 593 | 594 | 606 | 607 | 608 | 609 | -------------------------------------------------------------------------------- /src/WpfRichText/Controls/RichTextEditor.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Globalization; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Windows; 8 | using System.Windows.Controls; 9 | using System.Windows.Data; 10 | using System.Windows.Documents; 11 | using System.Windows.Input; 12 | using System.Windows.Media; 13 | using System.Windows.Media.Imaging; 14 | using System.Windows.Navigation; 15 | using System.Windows.Shapes; 16 | 17 | namespace WpfRichText 18 | { 19 | /// 20 | /// Interaction logic for BindableRichTextbox.xaml 21 | /// 22 | public partial class RichTextEditor : UserControl 23 | { 24 | /// 25 | public static readonly DependencyProperty TextProperty = 26 | DependencyProperty.Register("Text", typeof(string), typeof(RichTextEditor), 27 | new PropertyMetadata(string.Empty)); 28 | 29 | /// 30 | public static readonly DependencyProperty IsToolBarVisibleProperty = 31 | DependencyProperty.Register("IsToolBarVisible", typeof(bool), typeof(RichTextEditor), 32 | new PropertyMetadata(true)); 33 | 34 | /// 35 | public static readonly DependencyProperty IsContextMenuEnabledProperty = 36 | DependencyProperty.Register("IsContextMenuEnabled", typeof(bool), typeof(RichTextEditor), 37 | new PropertyMetadata(true)); 38 | 39 | /// 40 | public static readonly DependencyProperty IsReadOnlyProperty = 41 | DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(RichTextEditor), 42 | new PropertyMetadata(false)); 43 | 44 | /// 45 | public static readonly DependencyProperty AvailableFontsProperty = 46 | DependencyProperty.Register("AvailableFonts", typeof(Collection), typeof(RichTextEditor), 47 | new PropertyMetadata(new Collection( 48 | new List(4) 49 | { 50 | "Arial", 51 | "Courier New", 52 | "Tahoma", 53 | "Times New Roman" 54 | } 55 | ))); 56 | 57 | 58 | private TextRange textRange = null; 59 | 60 | /// 61 | public RichTextEditor() 62 | { 63 | InitializeComponent(); 64 | } 65 | 66 | /// 67 | public string Text 68 | { 69 | get { return GetValue(TextProperty) as string; } 70 | set 71 | { 72 | SetValue(TextProperty, value); 73 | } 74 | } 75 | 76 | /// 77 | public bool IsToolBarVisible 78 | { 79 | get { return (GetValue(IsToolBarVisibleProperty) as bool? == true); } 80 | set 81 | { 82 | SetValue(IsToolBarVisibleProperty, value); 83 | //this.mainToolBar.Visibility = (value == true) ? Visibility.Visible : Visibility.Collapsed; 84 | } 85 | } 86 | 87 | /// 88 | public bool IsContextMenuEnabled 89 | { 90 | get 91 | { 92 | return (GetValue(IsContextMenuEnabledProperty) as bool? == true); 93 | } 94 | set 95 | { 96 | SetValue(IsContextMenuEnabledProperty, value); 97 | } 98 | } 99 | 100 | /// 101 | public bool IsReadOnly 102 | { 103 | get { return (GetValue(IsReadOnlyProperty) as bool? == true); } 104 | set 105 | { 106 | SetValue(IsReadOnlyProperty, value); 107 | SetValue(IsToolBarVisibleProperty, !value); 108 | SetValue(IsContextMenuEnabledProperty, !value); 109 | } 110 | } 111 | 112 | /// 113 | public Collection AvailableFonts 114 | { 115 | get { return GetValue(AvailableFontsProperty) as Collection; } 116 | set 117 | { 118 | SetValue(AvailableFontsProperty, value); 119 | } 120 | } 121 | 122 | private void FontColorPicker_SelectedColorChanged(object sender, RoutedPropertyChangedEventArgs e) 123 | { 124 | this.mainRTB.Selection.ApplyPropertyValue(ForegroundProperty, e.NewValue.GetValueOrDefault().ToString(CultureInfo.InvariantCulture)); 125 | } 126 | 127 | private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) 128 | { 129 | if (this.mainRTB != null && this.mainRTB.Selection != null) 130 | this.mainRTB.Selection.ApplyPropertyValue(FontFamilyProperty, e.AddedItems[0]); 131 | } 132 | 133 | private void insertLink_Click(object sender, RoutedEventArgs e) 134 | { 135 | this.textRange = new TextRange(this.mainRTB.Selection.Start, this.mainRTB.Selection.End); 136 | this.uriInputPopup.IsOpen = true; 137 | } 138 | 139 | private void uriCancelClick(object sender, RoutedEventArgs e) 140 | { 141 | e.Handled = true; 142 | this.uriInputPopup.IsOpen = false; 143 | this.uriInput.Text = string.Empty; 144 | } 145 | 146 | private void uriSubmitClick(object sender, RoutedEventArgs e) 147 | { 148 | e.Handled = true; 149 | this.uriInputPopup.IsOpen = false; 150 | this.mainRTB.Selection.Select(this.textRange.Start, this.textRange.End); 151 | if (!string.IsNullOrEmpty(this.uriInput.Text)) 152 | { 153 | this.textRange = new TextRange(this.mainRTB.Selection.Start, this.mainRTB.Selection.End); 154 | Hyperlink hlink = new Hyperlink(this.textRange.Start, this.textRange.End); 155 | hlink.NavigateUri = new Uri(this.uriInput.Text, UriKind.RelativeOrAbsolute); 156 | this.uriInput.Text = string.Empty; 157 | } 158 | else 159 | this.mainRTB.Selection.ClearAllProperties(); 160 | } 161 | 162 | private void uriInput_KeyPressed(object sender, KeyEventArgs e) 163 | { 164 | switch (e.Key) 165 | { 166 | case Key.Enter: 167 | this.uriSubmitClick(sender, e); 168 | break; 169 | case Key.Escape: 170 | this.uriCancelClick(sender, e); 171 | break; 172 | default: 173 | break; 174 | } 175 | } 176 | 177 | private void ContextMenu_ContextMenuOpening(object sender, ContextMenuEventArgs e) 178 | { 179 | if (!this.IsContextMenuEnabled == true) 180 | e.Handled = true; 181 | } 182 | 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/WpfRichText/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Runtime.InteropServices; 4 | using System.Windows; 5 | using System.Resources; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("WpfRichText")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("WpfRichText")] 15 | [assembly: AssemblyCopyright("Copyright © 2013")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | [assembly: CLSCompliant(true)] 25 | 26 | //In order to begin building localizable applications, set 27 | //CultureYouAreCodingWith in your .csproj file 28 | //inside a . For example, if you are using US english 29 | //in your source files, set the to en-US. Then uncomment 30 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 31 | //the line below to match the UICulture setting in the project file. 32 | 33 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 34 | 35 | 36 | [assembly:ThemeInfo( 37 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 38 | //(used if a resource is not found in the page, 39 | // or application resource dictionaries) 40 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 41 | //(used if a resource is not found in the page, 42 | // app, or any theme specific resource dictionaries) 43 | )] 44 | 45 | 46 | // Version information for an assembly consists of the following four values: 47 | // 48 | // Major Version 49 | // Minor Version 50 | // Build Number 51 | // Revision 52 | // 53 | // You can specify all the values or you can default the Build and Revision Numbers 54 | // by using the '*' as shown below: 55 | // [assembly: AssemblyVersion("1.0.*")] 56 | [assembly: AssemblyVersion("1.0.16.0")] 57 | [assembly: AssemblyFileVersion("1.0.16.0")] 58 | [assembly: NeutralResourcesLanguageAttribute("en")] 59 | -------------------------------------------------------------------------------- /src/WpfRichText/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.18034 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace WpfRichText.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WpfRichText.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized resource of type System.Drawing.Bitmap. 65 | /// 66 | internal static System.Drawing.Bitmap charactergrowfont { 67 | get { 68 | object obj = ResourceManager.GetObject("charactergrowfont", resourceCulture); 69 | return ((System.Drawing.Bitmap)(obj)); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized resource of type System.Drawing.Bitmap. 75 | /// 76 | internal static System.Drawing.Bitmap charactershrinkfont { 77 | get { 78 | object obj = ResourceManager.GetObject("charactershrinkfont", resourceCulture); 79 | return ((System.Drawing.Bitmap)(obj)); 80 | } 81 | } 82 | 83 | /// 84 | /// Looks up a localized resource of type System.Drawing.Bitmap. 85 | /// 86 | internal static System.Drawing.Bitmap cross { 87 | get { 88 | object obj = ResourceManager.GetObject("cross", resourceCulture); 89 | return ((System.Drawing.Bitmap)(obj)); 90 | } 91 | } 92 | 93 | /// 94 | /// Looks up a localized resource of type System.Drawing.Bitmap. 95 | /// 96 | internal static System.Drawing.Bitmap cut { 97 | get { 98 | object obj = ResourceManager.GetObject("cut", resourceCulture); 99 | return ((System.Drawing.Bitmap)(obj)); 100 | } 101 | } 102 | 103 | /// 104 | /// Looks up a localized resource of type System.Drawing.Bitmap. 105 | /// 106 | internal static System.Drawing.Bitmap editredo { 107 | get { 108 | object obj = ResourceManager.GetObject("editredo", resourceCulture); 109 | return ((System.Drawing.Bitmap)(obj)); 110 | } 111 | } 112 | 113 | /// 114 | /// Looks up a localized resource of type System.Drawing.Bitmap. 115 | /// 116 | internal static System.Drawing.Bitmap editundo { 117 | get { 118 | object obj = ResourceManager.GetObject("editundo", resourceCulture); 119 | return ((System.Drawing.Bitmap)(obj)); 120 | } 121 | } 122 | 123 | /// 124 | /// Looks up a localized resource of type System.Drawing.Bitmap. 125 | /// 126 | internal static System.Drawing.Bitmap page_copy { 127 | get { 128 | object obj = ResourceManager.GetObject("page_copy", resourceCulture); 129 | return ((System.Drawing.Bitmap)(obj)); 130 | } 131 | } 132 | 133 | /// 134 | /// Looks up a localized resource of type System.Drawing.Bitmap. 135 | /// 136 | internal static System.Drawing.Bitmap page_paste { 137 | get { 138 | object obj = ResourceManager.GetObject("page_paste", resourceCulture); 139 | return ((System.Drawing.Bitmap)(obj)); 140 | } 141 | } 142 | 143 | /// 144 | /// Looks up a localized resource of type System.Drawing.Bitmap. 145 | /// 146 | internal static System.Drawing.Bitmap text_align_center { 147 | get { 148 | object obj = ResourceManager.GetObject("text_align_center", resourceCulture); 149 | return ((System.Drawing.Bitmap)(obj)); 150 | } 151 | } 152 | 153 | /// 154 | /// Looks up a localized resource of type System.Drawing.Bitmap. 155 | /// 156 | internal static System.Drawing.Bitmap text_align_justify { 157 | get { 158 | object obj = ResourceManager.GetObject("text_align_justify", resourceCulture); 159 | return ((System.Drawing.Bitmap)(obj)); 160 | } 161 | } 162 | 163 | /// 164 | /// Looks up a localized resource of type System.Drawing.Bitmap. 165 | /// 166 | internal static System.Drawing.Bitmap text_align_left { 167 | get { 168 | object obj = ResourceManager.GetObject("text_align_left", resourceCulture); 169 | return ((System.Drawing.Bitmap)(obj)); 170 | } 171 | } 172 | 173 | /// 174 | /// Looks up a localized resource of type System.Drawing.Bitmap. 175 | /// 176 | internal static System.Drawing.Bitmap text_align_right { 177 | get { 178 | object obj = ResourceManager.GetObject("text_align_right", resourceCulture); 179 | return ((System.Drawing.Bitmap)(obj)); 180 | } 181 | } 182 | 183 | /// 184 | /// Looks up a localized resource of type System.Drawing.Bitmap. 185 | /// 186 | internal static System.Drawing.Bitmap text_bold { 187 | get { 188 | object obj = ResourceManager.GetObject("text_bold", resourceCulture); 189 | return ((System.Drawing.Bitmap)(obj)); 190 | } 191 | } 192 | 193 | /// 194 | /// Looks up a localized resource of type System.Drawing.Bitmap. 195 | /// 196 | internal static System.Drawing.Bitmap text_indent { 197 | get { 198 | object obj = ResourceManager.GetObject("text_indent", resourceCulture); 199 | return ((System.Drawing.Bitmap)(obj)); 200 | } 201 | } 202 | 203 | /// 204 | /// Looks up a localized resource of type System.Drawing.Bitmap. 205 | /// 206 | internal static System.Drawing.Bitmap text_indent_remove { 207 | get { 208 | object obj = ResourceManager.GetObject("text_indent_remove", resourceCulture); 209 | return ((System.Drawing.Bitmap)(obj)); 210 | } 211 | } 212 | 213 | /// 214 | /// Looks up a localized resource of type System.Drawing.Bitmap. 215 | /// 216 | internal static System.Drawing.Bitmap text_italic { 217 | get { 218 | object obj = ResourceManager.GetObject("text_italic", resourceCulture); 219 | return ((System.Drawing.Bitmap)(obj)); 220 | } 221 | } 222 | 223 | /// 224 | /// Looks up a localized resource of type System.Drawing.Bitmap. 225 | /// 226 | internal static System.Drawing.Bitmap text_list_bullets { 227 | get { 228 | object obj = ResourceManager.GetObject("text_list_bullets", resourceCulture); 229 | return ((System.Drawing.Bitmap)(obj)); 230 | } 231 | } 232 | 233 | /// 234 | /// Looks up a localized resource of type System.Drawing.Bitmap. 235 | /// 236 | internal static System.Drawing.Bitmap text_list_numbers { 237 | get { 238 | object obj = ResourceManager.GetObject("text_list_numbers", resourceCulture); 239 | return ((System.Drawing.Bitmap)(obj)); 240 | } 241 | } 242 | 243 | /// 244 | /// Looks up a localized resource of type System.Drawing.Bitmap. 245 | /// 246 | internal static System.Drawing.Bitmap text_underline { 247 | get { 248 | object obj = ResourceManager.GetObject("text_underline", resourceCulture); 249 | return ((System.Drawing.Bitmap)(obj)); 250 | } 251 | } 252 | 253 | /// 254 | /// Looks up a localized resource of type System.Drawing.Bitmap. 255 | /// 256 | internal static System.Drawing.Bitmap tick { 257 | get { 258 | object obj = ResourceManager.GetObject("tick", resourceCulture); 259 | return ((System.Drawing.Bitmap)(obj)); 260 | } 261 | } 262 | 263 | /// 264 | /// Looks up a localized resource of type System.Drawing.Bitmap. 265 | /// 266 | internal static System.Drawing.Bitmap world_link { 267 | get { 268 | object obj = ResourceManager.GetObject("world_link", resourceCulture); 269 | return ((System.Drawing.Bitmap)(obj)); 270 | } 271 | } 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /src/WpfRichText/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | ..\controls\images\charactergrowfont.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 123 | 124 | 125 | ..\controls\images\charactershrinkfont.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 126 | 127 | 128 | ..\controls\images\cross.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 129 | 130 | 131 | ..\controls\images\cut.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 132 | 133 | 134 | ..\controls\images\editredo.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 135 | 136 | 137 | ..\controls\images\editundo.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 138 | 139 | 140 | ..\controls\images\page_copy.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 141 | 142 | 143 | ..\controls\images\page_paste.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 144 | 145 | 146 | ..\controls\images\text_align_center.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 147 | 148 | 149 | ..\controls\images\text_align_justify.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 150 | 151 | 152 | ..\controls\images\text_align_left.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 153 | 154 | 155 | ..\controls\images\text_align_right.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 156 | 157 | 158 | ..\controls\images\text_bold.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 159 | 160 | 161 | ..\controls\images\text_indent.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 162 | 163 | 164 | ..\controls\images\text_indent_remove.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 165 | 166 | 167 | ..\controls\images\text_italic.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 168 | 169 | 170 | ..\controls\images\text_list_bullets.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 171 | 172 | 173 | ..\controls\images\text_list_numbers.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 174 | 175 | 176 | ..\controls\images\text_underline.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 177 | 178 | 179 | ..\controls\images\tick.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 180 | 181 | 182 | ..\controls\images\world_link.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 183 | 184 | -------------------------------------------------------------------------------- /src/WpfRichText/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.18034 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace WpfRichText.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/WpfRichText/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/WpfRichText/WpfRichText.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {78147C43-DB7B-4571-BC7A-E7F0075B4B78} 8 | library 9 | Properties 10 | WpfRichText 11 | WpfRichText 12 | v4.5 13 | 512 14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15 | 4 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | true 27 | full 28 | false 29 | bin\Debug\ 30 | DEBUG;TRACE 31 | prompt 32 | 4 33 | AllRules.ruleset 34 | bin\Debug\WpfRichText.XML 35 | true 36 | 37 | 38 | 39 | 40 | pdbonly 41 | true 42 | ..\..\dll\ 43 | TRACE 44 | prompt 45 | 4 46 | true 47 | 48 | 49 | true 50 | 51 | 52 | self.snk 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 4.0 66 | 67 | 68 | 69 | 70 | 71 | False 72 | ..\packages\Extended.Wpf.Toolkit.2.0.0\lib\net40\Xceed.Wpf.Toolkit.dll 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | RichTextEditor.xaml 81 | 82 | 83 | Code 84 | 85 | 86 | True 87 | True 88 | Resources.resx 89 | 90 | 91 | True 92 | Settings.settings 93 | True 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | ResXFileCodeGenerator 104 | Resources.Designer.cs 105 | Designer 106 | 107 | 108 | 109 | SettingsSingleFileGenerator 110 | Settings.Designer.cs 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | MSBuild:Compile 130 | Designer 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 166 | -------------------------------------------------------------------------------- /src/WpfRichText/XamlToHtmlParser/HtmlFromXamlConverter.cs: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | // 3 | // File: HtmlFromXamlConverter.cs 4 | // 5 | // Copyright (C) Microsoft Corporation. All rights reserved. 6 | // 7 | // Description: Prototype for Xaml - Html conversion 8 | // 9 | //--------------------------------------------------------------------------- 10 | 11 | namespace WpfRichText 12 | { 13 | using System; 14 | using System.Diagnostics; 15 | using System.Globalization; 16 | using System.IO; 17 | using System.Text; 18 | using System.Xml; 19 | 20 | /// 21 | /// HtmlToXamlConverter is a static class that takes an HTML string 22 | /// and converts it into XAML 23 | /// 24 | internal static class HtmlFromXamlConverter 25 | { 26 | // --------------------------------------------------------------------- 27 | // 28 | // Internal Methods 29 | // 30 | // --------------------------------------------------------------------- 31 | 32 | #region Internal Methods 33 | internal static string ConvertXamlToHtml(string xamlString) 34 | { 35 | return ConvertXamlToHtml(xamlString, true); 36 | } 37 | /// 38 | /// Main entry point for Xaml-to-Html converter. 39 | /// Converts a xaml string into html string. 40 | /// 41 | /// Xaml strinng to convert. 42 | /// 43 | /// 44 | /// Html string produced from a source xaml. 45 | /// 46 | internal static string ConvertXamlToHtml(string xamlString, bool asFlowDocument) 47 | { 48 | StringBuilder htmlStringBuilder; 49 | XmlTextWriter htmlWriter; 50 | 51 | if (!asFlowDocument) 52 | { 53 | xamlString = "" + xamlString + ""; 54 | } 55 | 56 | using (XmlTextReader xamlReader = new XmlTextReader(new StringReader(xamlString))) 57 | { 58 | htmlStringBuilder = new StringBuilder(100); 59 | using (StringWriter htmlStringWiter = new StringWriter(htmlStringBuilder, CultureInfo.InvariantCulture)) 60 | { 61 | htmlWriter = new XmlTextWriter(htmlStringWiter); 62 | 63 | if (!WriteFlowDocument(xamlReader, htmlWriter)) 64 | { 65 | return ""; 66 | } 67 | } 68 | 69 | string htmlString = htmlStringBuilder.ToString(); 70 | return htmlString; 71 | } 72 | } 73 | 74 | #endregion Internal Methods 75 | 76 | // --------------------------------------------------------------------- 77 | // 78 | // Private Methods 79 | // 80 | // --------------------------------------------------------------------- 81 | 82 | #region Private Methods 83 | /// 84 | /// Processes a root level element of XAML (normally it's FlowDocument element). 85 | /// 86 | /// 87 | /// XmlTextReader for a source xaml. 88 | /// 89 | /// 90 | /// XmlTextWriter producing resulting html 91 | /// 92 | private static bool WriteFlowDocument(XmlTextReader xamlReader, XmlTextWriter htmlWriter) 93 | { 94 | if (!ReadNextToken(xamlReader)) 95 | { 96 | // Xaml content is empty - nothing to convert 97 | return false; 98 | } 99 | 100 | if (xamlReader.NodeType != XmlNodeType.Element || xamlReader.Name != "FlowDocument") 101 | { 102 | // Root FlowDocument elemet is missing 103 | return false; 104 | } 105 | 106 | // Create a buffer StringBuilder for collecting css properties for inline STYLE attributes 107 | // on every element level (it will be re-initialized on every level). 108 | StringBuilder inlineStyle = new StringBuilder(); 109 | 110 | htmlWriter.WriteStartElement("HTML"); 111 | htmlWriter.WriteStartElement("BODY"); 112 | 113 | WriteFormattingProperties(xamlReader, htmlWriter, inlineStyle); 114 | 115 | WriteElementContent(xamlReader, htmlWriter, inlineStyle); 116 | 117 | htmlWriter.WriteEndElement(); 118 | htmlWriter.WriteEndElement(); 119 | 120 | return true; 121 | } 122 | 123 | /// 124 | /// Reads attributes of the current xaml element and converts 125 | /// them into appropriate html attributes or css styles. 126 | /// 127 | /// 128 | /// XmlTextReader which is expected to be at XmlNodeType.Element 129 | /// (opening element tag) position. 130 | /// The reader will remain at the same level after function complete. 131 | /// 132 | /// 133 | /// XmlTextWriter for output html, which is expected to be in 134 | /// after WriteStartElement state. 135 | /// 136 | /// 137 | /// String builder for collecting css properties for inline STYLE attribute. 138 | /// 139 | private static void WriteFormattingProperties(XmlTextReader xamlReader, XmlTextWriter htmlWriter, StringBuilder inlineStyle) 140 | { 141 | Debug.Assert(xamlReader.NodeType == XmlNodeType.Element); 142 | 143 | // Clear string builder for the inline style 144 | inlineStyle.Remove(0, inlineStyle.Length); 145 | 146 | if (!xamlReader.HasAttributes) 147 | { 148 | return; 149 | } 150 | 151 | bool borderSet = false; 152 | 153 | while (xamlReader.MoveToNextAttribute()) 154 | { 155 | string css = null; 156 | 157 | switch (xamlReader.Name) 158 | { 159 | // Character fomatting properties 160 | // ------------------------------ 161 | case "Background": 162 | css = "background-color:" + ParseXamlColor(xamlReader.Value) + ";"; 163 | break; 164 | case "FontFamily": 165 | css = "font-family:" + xamlReader.Value + ";"; 166 | break; 167 | case "FontStyle": 168 | css = "font-style:" + xamlReader.Value.ToLower(CultureInfo.InvariantCulture) + ";"; 169 | break; 170 | case "FontWeight": 171 | css = "font-weight:" + xamlReader.Value.ToLower(CultureInfo.InvariantCulture) + ";"; 172 | break; 173 | case "FontStretch": 174 | break; 175 | case "FontSize": 176 | css = "font-size:" + xamlReader.Value + ";"; 177 | break; 178 | case "Foreground": 179 | css = "color:" + ParseXamlColor(xamlReader.Value) + ";"; 180 | break; 181 | case "TextDecorations": 182 | css = "text-decoration:underline;"; 183 | break; 184 | case "TextEffects": 185 | break; 186 | case "Emphasis": 187 | break; 188 | case "StandardLigatures": 189 | break; 190 | case "Variants": 191 | break; 192 | case "Capitals": 193 | break; 194 | case "Fraction": 195 | break; 196 | 197 | // Paragraph formatting properties 198 | // ------------------------------- 199 | case "Padding": 200 | css = "padding:" + ParseXamlThickness(xamlReader.Value) + ";"; 201 | break; 202 | case "Margin": 203 | css = "margin:" + ParseXamlThickness(xamlReader.Value) + ";"; 204 | break; 205 | case "BorderThickness": 206 | css = "border-width:" + ParseXamlThickness(xamlReader.Value) + ";"; 207 | borderSet = true; 208 | break; 209 | case "BorderBrush": 210 | css = "border-color:" + ParseXamlColor(xamlReader.Value) + ";"; 211 | borderSet = true; 212 | break; 213 | case "LineHeight": 214 | break; 215 | case "TextIndent": 216 | css = "text-indent:" + xamlReader.Value + ";"; 217 | break; 218 | case "TextAlignment": 219 | css = "text-align:" + xamlReader.Value + ";"; 220 | break; 221 | case "IsKeptTogether": 222 | break; 223 | case "IsKeptWithNext": 224 | break; 225 | case "ColumnBreakBefore": 226 | break; 227 | case "PageBreakBefore": 228 | break; 229 | case "FlowDirection": 230 | break; 231 | 232 | // Table attributes 233 | // ---------------- 234 | case "Width": 235 | css = "width:" + xamlReader.Value + ";"; 236 | break; 237 | case "ColumnSpan": 238 | htmlWriter.WriteAttributeString("COLSPAN", xamlReader.Value); 239 | break; 240 | case "RowSpan": 241 | htmlWriter.WriteAttributeString("ROWSPAN", xamlReader.Value); 242 | break; 243 | 244 | // Hyperlink Attributes 245 | case "NavigateUri" : 246 | htmlWriter.WriteAttributeString("HREF", xamlReader.Value); 247 | break; 248 | } 249 | 250 | if (css != null) 251 | { 252 | inlineStyle.Append(css); 253 | } 254 | } 255 | 256 | if (borderSet) 257 | { 258 | inlineStyle.Append("border-style:solid;mso-element:para-border-div;"); 259 | } 260 | 261 | // Return the xamlReader back to element level 262 | xamlReader.MoveToElement(); 263 | Debug.Assert(xamlReader.NodeType == XmlNodeType.Element); 264 | } 265 | 266 | private static string ParseXamlColor(string color) 267 | { 268 | if (color.StartsWith("#", StringComparison.OrdinalIgnoreCase)) 269 | { 270 | // Remove transparancy value 271 | color = "#" + color.Substring(3); 272 | } 273 | return color; 274 | } 275 | 276 | private static string ParseXamlThickness(string thickness) 277 | { 278 | string[] values = thickness.Split(','); 279 | 280 | for (int i = 0; i < values.Length; i++) 281 | { 282 | double value; 283 | if (double.TryParse(values[i], NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out value)) 284 | { 285 | values[i] = Math.Ceiling(value).ToString(CultureInfo.InvariantCulture); 286 | } 287 | else 288 | { 289 | values[i] = "1"; 290 | } 291 | } 292 | 293 | string cssThickness; 294 | switch (values.Length) 295 | { 296 | case 1: 297 | cssThickness = thickness; 298 | break; 299 | case 2: 300 | cssThickness = values[1] + " " + values[0]; 301 | break; 302 | case 4: 303 | cssThickness = values[1] + " " + values[2] + " " + values[3] + " " + values[0]; 304 | break; 305 | default: 306 | cssThickness = values[0]; 307 | break; 308 | } 309 | 310 | return cssThickness; 311 | } 312 | 313 | /// 314 | /// Reads a content of current xaml element, converts it 315 | /// 316 | /// 317 | /// XmlTextReader which is expected to be at XmlNodeType.Element 318 | /// (opening element tag) position. 319 | /// 320 | /// 321 | /// May be null, in which case we are skipping the xaml element; 322 | /// witout producing any output to html. 323 | /// 324 | /// 325 | /// StringBuilder used for collecting css properties for inline STYLE attribute. 326 | /// 327 | private static void WriteElementContent(XmlTextReader xamlReader, XmlTextWriter htmlWriter, StringBuilder inlineStyle) 328 | { 329 | Debug.Assert(xamlReader.NodeType == XmlNodeType.Element); 330 | 331 | bool elementContentStarted = false; 332 | 333 | if (xamlReader.IsEmptyElement) 334 | { 335 | if (htmlWriter != null && !elementContentStarted && inlineStyle.Length > 0) 336 | { 337 | // Output STYLE attribute and clear inlineStyle buffer. 338 | htmlWriter.WriteAttributeString("STYLE", inlineStyle.ToString()); 339 | inlineStyle.Remove(0, inlineStyle.Length); 340 | } 341 | elementContentStarted = true; 342 | } 343 | else 344 | { 345 | while (ReadNextToken(xamlReader) && xamlReader.NodeType != XmlNodeType.EndElement) 346 | { 347 | switch (xamlReader.NodeType) 348 | { 349 | case XmlNodeType.Element: 350 | if (xamlReader.Name.Contains(".")) 351 | { 352 | AddComplexProperty(xamlReader, inlineStyle); 353 | } 354 | else 355 | { 356 | if (htmlWriter != null && !elementContentStarted && inlineStyle.Length > 0) 357 | { 358 | // Output STYLE attribute and clear inlineStyle buffer. 359 | htmlWriter.WriteAttributeString("STYLE", inlineStyle.ToString()); 360 | inlineStyle.Remove(0, inlineStyle.Length); 361 | } 362 | elementContentStarted = true; 363 | WriteElement(xamlReader, htmlWriter, inlineStyle); 364 | } 365 | Debug.Assert(xamlReader.NodeType == XmlNodeType.EndElement || xamlReader.NodeType == XmlNodeType.Element && xamlReader.IsEmptyElement); 366 | break; 367 | case XmlNodeType.Comment: 368 | if (htmlWriter != null) 369 | { 370 | if (!elementContentStarted && inlineStyle.Length > 0) 371 | { 372 | htmlWriter.WriteAttributeString("STYLE", inlineStyle.ToString()); 373 | } 374 | htmlWriter.WriteComment(xamlReader.Value); 375 | } 376 | elementContentStarted = true; 377 | break; 378 | case XmlNodeType.CDATA: 379 | case XmlNodeType.Text: 380 | case XmlNodeType.SignificantWhitespace: 381 | if (htmlWriter != null) 382 | { 383 | if (!elementContentStarted && inlineStyle.Length > 0) 384 | { 385 | htmlWriter.WriteAttributeString("STYLE", inlineStyle.ToString()); 386 | } 387 | htmlWriter.WriteString(xamlReader.Value); 388 | } 389 | elementContentStarted = true; 390 | break; 391 | } 392 | } 393 | 394 | Debug.Assert(xamlReader.NodeType == XmlNodeType.EndElement); 395 | } 396 | } 397 | 398 | /// 399 | /// Conberts an element notation of complex property into 400 | /// 401 | /// 402 | /// On entry this XmlTextReader must be on Element start tag; 403 | /// on exit - on EndElement tag. 404 | /// 405 | /// 406 | /// StringBuilder containing a value for STYLE attribute. 407 | /// 408 | private static void AddComplexProperty(XmlTextReader xamlReader, StringBuilder inlineStyle) 409 | { 410 | Debug.Assert(xamlReader.NodeType == XmlNodeType.Element); 411 | 412 | if (inlineStyle != null && xamlReader.Name.EndsWith(".TextDecorations", StringComparison.OrdinalIgnoreCase)) 413 | { 414 | inlineStyle.Append("text-decoration:underline;"); 415 | } 416 | 417 | // Skip the element representing the complex property 418 | WriteElementContent(xamlReader, /*htmlWriter:*/null, /*inlineStyle:*/null); 419 | } 420 | 421 | /// 422 | /// Converts a xaml element into an appropriate html element. 423 | /// 424 | /// 425 | /// On entry this XmlTextReader must be on Element start tag; 426 | /// on exit - on EndElement tag. 427 | /// 428 | /// 429 | /// May be null, in which case we are skipping xaml content 430 | /// without producing any html output 431 | /// 432 | /// 433 | /// StringBuilder used for collecting css properties for inline STYLE attributes on every level. 434 | /// 435 | private static void WriteElement(XmlTextReader xamlReader, XmlTextWriter htmlWriter, StringBuilder inlineStyle) 436 | { 437 | Debug.Assert(xamlReader.NodeType == XmlNodeType.Element); 438 | 439 | if (htmlWriter == null) 440 | { 441 | // Skipping mode; recurse into the xaml element without any output 442 | WriteElementContent(xamlReader, /*htmlWriter:*/null, null); 443 | } 444 | else 445 | { 446 | string htmlElementName = null; 447 | 448 | switch (xamlReader.Name) 449 | { 450 | case "Run" : 451 | case "Span": 452 | htmlElementName = "SPAN"; 453 | break; 454 | case "InlineUIContainer": 455 | htmlElementName = "SPAN"; 456 | break; 457 | case "Bold": 458 | htmlElementName = "B"; 459 | break; 460 | case "Italic" : 461 | htmlElementName = "I"; 462 | break; 463 | case "Paragraph" : 464 | htmlElementName = "P"; 465 | break; 466 | case "BlockUIContainer": 467 | htmlElementName = "DIV"; 468 | break; 469 | case "Section": 470 | htmlElementName = "DIV"; 471 | break; 472 | case "Table": 473 | htmlElementName = "TABLE"; 474 | break; 475 | case "TableColumn": 476 | htmlElementName = "COL"; 477 | break; 478 | case "TableRowGroup" : 479 | htmlElementName = "TBODY"; 480 | break; 481 | case "TableRow" : 482 | htmlElementName = "TR"; 483 | break; 484 | case "TableCell" : 485 | htmlElementName = "TD"; 486 | break; 487 | case "List" : 488 | string marker = xamlReader.GetAttribute("MarkerStyle"); 489 | if (marker == null || marker == "None" || marker == "Disc" || marker == "Circle" || marker == "Square" || marker == "Box") 490 | htmlElementName = "UL"; 491 | else 492 | htmlElementName = "OL"; 493 | break; 494 | case "ListItem" : 495 | htmlElementName = "LI"; 496 | break; 497 | case "Hyperlink" : 498 | htmlElementName = "A"; 499 | break; 500 | case "LineBreak" : 501 | htmlWriter.WriteRaw("
"); 502 | break; 503 | default : 504 | htmlElementName = null; // Ignore the element 505 | break; 506 | } 507 | 508 | if (htmlWriter != null && !String.IsNullOrEmpty(htmlElementName)) 509 | { 510 | htmlWriter.WriteStartElement(htmlElementName); 511 | 512 | WriteFormattingProperties(xamlReader, htmlWriter, inlineStyle); 513 | 514 | WriteElementContent(xamlReader, htmlWriter, inlineStyle); 515 | 516 | htmlWriter.WriteEndElement(); 517 | } 518 | else 519 | { 520 | // Skip this unrecognized xaml element 521 | WriteElementContent(xamlReader, /*htmlWriter:*/null, null); 522 | } 523 | } 524 | } 525 | 526 | // Reader advance helpers 527 | // ---------------------- 528 | 529 | /// 530 | /// Reads several items from xamlReader skipping all non-significant stuff. 531 | /// 532 | /// 533 | /// XmlTextReader from tokens are being read. 534 | /// 535 | /// 536 | /// True if new token is available; false if end of stream reached. 537 | /// 538 | private static bool ReadNextToken(XmlReader xamlReader) 539 | { 540 | while (xamlReader.Read()) 541 | { 542 | Debug.Assert(xamlReader.ReadState == ReadState.Interactive, "Reader is expected to be in Interactive state (" + xamlReader.ReadState + ")"); 543 | switch (xamlReader.NodeType) 544 | { 545 | case XmlNodeType.Element: 546 | case XmlNodeType.EndElement: 547 | case XmlNodeType.None: 548 | case XmlNodeType.CDATA: 549 | case XmlNodeType.Text: 550 | case XmlNodeType.SignificantWhitespace: 551 | return true; 552 | 553 | case XmlNodeType.Whitespace: 554 | if (xamlReader.XmlSpace == XmlSpace.Preserve) 555 | { 556 | return true; 557 | } 558 | // ignore insignificant whitespace 559 | break; 560 | 561 | case XmlNodeType.EndEntity: 562 | case XmlNodeType.EntityReference: 563 | // Implement entity reading 564 | //xamlReader.ResolveEntity(); 565 | //xamlReader.Read(); 566 | //ReadChildNodes( parent, parentBaseUri, xamlReader, positionInfo); 567 | break; // for now we ignore entities as insignificant stuff 568 | 569 | case XmlNodeType.Comment: 570 | return true; 571 | case XmlNodeType.ProcessingInstruction: 572 | case XmlNodeType.DocumentType: 573 | case XmlNodeType.XmlDeclaration: 574 | default: 575 | // Ignorable stuff 576 | break; 577 | } 578 | } 579 | return false; 580 | } 581 | 582 | #endregion Private Methods 583 | 584 | // --------------------------------------------------------------------- 585 | // 586 | // Private Fields 587 | // 588 | // --------------------------------------------------------------------- 589 | 590 | #region Private Fields 591 | 592 | #endregion Private Fields 593 | } 594 | } 595 | -------------------------------------------------------------------------------- /src/WpfRichText/XamlToHtmlParser/HtmlParser.cs: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | // 3 | // File: HtmlParser.cs 4 | // 5 | // Copyright (C) Microsoft Corporation. All rights reserved. 6 | // 7 | // Description: Parser for Html-to-Xaml converter 8 | // 9 | //--------------------------------------------------------------------------- 10 | 11 | using System; 12 | using System.Collections.Generic; 13 | using System.Globalization; // StringBuilder 14 | using System.Text; 15 | using System.Xml; 16 | 17 | // important TODOS: 18 | // TODO 1. Start tags: The ParseXmlElement function has been modified to be called after both the 19 | // angle bracket < and element name have been read, instead of just the < bracket and some valid name character, 20 | // previously the case. This change was made so that elements with optional closing tags could read a new 21 | // element's start tag and decide whether they were required to close. However, there is a question of whether to 22 | // handle this in the parser or lexical analyzer. It is currently handled in the parser - the lexical analyzer still 23 | // recognizes a start tag opener as a '<' + valid name start char; it is the parser that reads the actual name. 24 | // this is correct behavior assuming that the name is a valid html name, because the lexical analyzer should not know anything 25 | // about optional closing tags, etc. UPDATED: 10/13/2004: I am updating this to read the whole start tag of something 26 | // that is not an HTML, treat it as empty, and add it to the tree. That way the converter will know it's there, but 27 | // it will hvae no content. We could also partially recover by trying to look up and match names if they are similar 28 | // TODO 2. Invalid element names: However, it might make sense to give the lexical analyzer the ability to identify 29 | // a valid html element name and not return something as a start tag otherwise. For example, if we type , should 30 | // the lexical analyzer return that it has found the start of an element when this is not the case in HTML? But this will 31 | // require implementing a lookahead token in the lexical analyzer so that it can treat an invalid element name as text. One 32 | // character of lookahead will not be enough. 33 | // TODO 3. Attributes: The attribute recovery is poor when reading attribute values in quotes - if no closing quotes are found, 34 | // the lexical analyzer just keeps reading and if it eventually reaches the end of file, it would have just skipped everything. 35 | // There are a couple of ways to deal with this: 1) stop reading attributes when we encounter a '>' character - this doesn't allow 36 | // the '>' character to be used in attribute values, but it can still be used as an entity. 2) Maintain a HTML-specific list 37 | // of attributes and their values that each html element can take, and if we find correct attribute namesand values for an 38 | // element we use them regardless of the quotes, this way we could just ignore something invalid. One more option: 3) Read ahead 39 | // in the quoted value and if we find an end of file, we can return to where we were and process as text. However this requires 40 | // a lot of lookahead and a resettable reader. 41 | // TODO 4: elements with optional closing tags: For elements with optional closing tags, we always close the element if we find 42 | // that one of it's ancestors has closed. This condition may be too broad and we should develop a better heuristic. We should also 43 | // improve the heuristics for closing certain elements when the next element starts 44 | // TODO 5. Nesting: Support for unbalanced nesting, e.g. : this is not presently supported. To support it we may need 45 | // to maintain two xml elements, one the element that represents what has already been read and another represents what we are presently reading. 46 | // Then if we encounter an unbalanced nesting tag we could close the element that was supposed to close, save the current element 47 | // and store it in the list of already-read content, and then open a new element to which all tags that are currently open 48 | // can be applied. Is there a better way to do this? Should we do it at all? 49 | // TODO 6. Elements with optional starting tags: there are 4 such elements in the HTML 4 specification - html, tbody, body and head. 50 | // The current recovery doesn;t do anything for any of these elements except the html element, because it's not critical - head 51 | // and body elementscan be contained within html element, and tbody is contained within table. To extend this for XHTML 52 | // extensions, and to recover in case other elements are missing start tags, we would need to insert an extra recursive call 53 | // to ParseXmlElement for the missing start tag. It is suggested to do this by giving ParseXmlElement an argument that specifies 54 | // a name to use. If this argument is null, it assumes its name is the next token from the lexical analyzer and continues 55 | // exactly as it does now. However, if the argument contains a valid html element name then it takes that value as its name 56 | // and continues as before. This way, if the next token is the element that should actually be its child, it will see 57 | // the name in the next step and initiate a recursive call. We would also need to add some logic in the loop for when a start tag 58 | // is found - if the start tag is not compatible with current context and indicates that a start tag has been missed, then we 59 | // can initiate the extra recursive call and give it the name of the missed start tag. The issues are when to insert this logic, 60 | // and if we want to support it over multiple missing start tags. If we insert it at the time a start tag is read in element 61 | // text, then we can support only one missing start tag, since the extra call will read the next start tag and make a recursive 62 | // call without checking the context. This is a conceptual problem, and the check should be made just before a recursive call, 63 | // with the choice being whether we should supply an element name as argument, or leave it as NULL and read from the input 64 | // TODO 7: Context: Is it appropriate to keep track of context here? For example, should we only expect td, tr elements when 65 | // reading a table and ignore them otherwise? This may be too much of a load on the parser, I think it's better if the converter 66 | // deals with it 67 | 68 | 69 | namespace WpfRichText 70 | { 71 | /// 72 | /// HtmlParser class accepts a string of possibly badly formed Html, parses it and returns a string 73 | /// of well-formed Html that is as close to the original string in content as possible 74 | /// 75 | 76 | internal class HtmlParser : IDisposable 77 | { 78 | // --------------------------------------------------------------------- 79 | // 80 | // Constructors 81 | // 82 | // --------------------------------------------------------------------- 83 | 84 | #region Constructors 85 | 86 | /// 87 | /// Constructor. Initializes the _htmlLexicalAnalayzer element with the given input string 88 | /// 89 | /// 90 | /// string to parsed into well-formed Html 91 | /// 92 | private HtmlParser(string inputString) 93 | { 94 | // Create an output xml document 95 | _document = new XmlDocument(); 96 | 97 | // initialize open tag stack 98 | _openedElements = new Stack(); 99 | 100 | _pendingInlineElements = new Stack(); 101 | 102 | // initialize lexical analyzer 103 | _htmlLexicalAnalyzer = new HtmlLexicalAnalyzer(inputString); 104 | 105 | // get first token from input, expecting text 106 | _htmlLexicalAnalyzer.GetNextContentToken(); 107 | } 108 | 109 | #endregion Constructors 110 | 111 | // --------------------------------------------------------------------- 112 | // 113 | // Internal Methods 114 | // 115 | // --------------------------------------------------------------------- 116 | 117 | #region Internal Methods 118 | 119 | /// 120 | /// Instantiates an HtmlParser element and calls the parsing function on the given input string 121 | /// 122 | /// 123 | /// Input string of pssibly badly-formed Html to be parsed into well-formed Html 124 | /// 125 | /// 126 | /// XmlElement rep 127 | /// 128 | internal static XmlElement ParseHtml(string htmlString) 129 | { 130 | using (HtmlParser htmlParser = new HtmlParser(htmlString)) 131 | { 132 | 133 | XmlElement htmlRootElement = htmlParser.ParseHtmlContent(); 134 | 135 | return htmlRootElement; 136 | } 137 | } 138 | 139 | // ..................................................................... 140 | // 141 | // Html Header on Clipboard 142 | // 143 | // ..................................................................... 144 | 145 | // Html header structure. 146 | // Version:1.0 147 | // StartHTML:000000000 148 | // EndHTML:000000000 149 | // StartFragment:000000000 150 | // EndFragment:000000000 151 | // StartSelection:000000000 152 | // EndSelection:000000000 153 | internal const string HtmlHeader = "Version:1.0\r\nStartHTML:{0:D10}\r\nEndHTML:{1:D10}\r\nStartFragment:{2:D10}\r\nEndFragment:{3:D10}\r\nStartSelection:{4:D10}\r\nEndSelection:{5:D10}\r\n"; 154 | internal const string HtmlStartFragmentComment = ""; 155 | internal const string HtmlEndFragmentComment = ""; 156 | 157 | /// 158 | /// Extracts Html string from clipboard data by parsing header information in htmlDataString 159 | /// 160 | /// 161 | /// String representing Html clipboard data. This includes Html header 162 | /// 163 | /// 164 | /// String containing only the Html data part of htmlDataString, without header 165 | /// 166 | internal static string ExtractHtmlFromClipboardData(string htmlDataString) 167 | { 168 | int startHtmlIndex = htmlDataString.IndexOf("StartHTML:", StringComparison.OrdinalIgnoreCase); 169 | if (startHtmlIndex < 0) 170 | { 171 | return "ERROR: Urecognized html header"; 172 | } 173 | // TODO: We assume that indices represented by strictly 10 zeros ("0123456789".Length), 174 | // which could be wrong assumption. We need to implement more flrxible parsing here 175 | startHtmlIndex = Int32.Parse(htmlDataString.Substring(startHtmlIndex + "StartHTML:".Length, "0123456789".Length), CultureInfo.InvariantCulture); 176 | if (startHtmlIndex < 0 || startHtmlIndex > htmlDataString.Length) 177 | { 178 | return "ERROR: Urecognized html header"; 179 | } 180 | 181 | int endHtmlIndex = htmlDataString.IndexOf("EndHTML:", StringComparison.OrdinalIgnoreCase); 182 | if (endHtmlIndex < 0) 183 | { 184 | return "ERROR: Urecognized html header"; 185 | } 186 | // TODO: We assume that indices represented by strictly 10 zeros ("0123456789".Length), 187 | // which could be wrong assumption. We need to implement more flrxible parsing here 188 | endHtmlIndex = Int32.Parse(htmlDataString.Substring(endHtmlIndex + "EndHTML:".Length, "0123456789".Length), CultureInfo.InvariantCulture); 189 | if (endHtmlIndex > htmlDataString.Length) 190 | { 191 | endHtmlIndex = htmlDataString.Length; 192 | } 193 | 194 | return htmlDataString.Substring(startHtmlIndex, endHtmlIndex - startHtmlIndex); 195 | } 196 | 197 | /// 198 | /// Adds Xhtml header information to Html data string so that it can be placed on clipboard 199 | /// 200 | /// 201 | /// Html string to be placed on clipboard with appropriate header 202 | /// 203 | /// 204 | /// String wrapping htmlString with appropriate Html header 205 | /// 206 | internal static string AddHtmlClipboardHeader(string htmlString) 207 | { 208 | StringBuilder stringBuilder = new StringBuilder(); 209 | 210 | // each of 6 numbers is represented by "{0:D10}" in the format string 211 | // must actually occupy 10 digit positions ("0123456789") 212 | int startHTML = HtmlHeader.Length + 6 * ("0123456789".Length - "{0:D10}".Length); 213 | int endHTML = startHTML + htmlString.Length; 214 | int startFragment = htmlString.IndexOf(HtmlStartFragmentComment, 0, StringComparison.OrdinalIgnoreCase); 215 | if (startFragment >= 0) 216 | { 217 | startFragment = startHTML + startFragment + HtmlStartFragmentComment.Length; 218 | } 219 | else 220 | { 221 | startFragment = startHTML; 222 | } 223 | int endFragment = htmlString.IndexOf(HtmlEndFragmentComment, 0, StringComparison.OrdinalIgnoreCase); 224 | if (endFragment >= 0) 225 | { 226 | endFragment = startHTML + endFragment; 227 | } 228 | else 229 | { 230 | endFragment = endHTML; 231 | } 232 | 233 | // Create HTML clipboard header string 234 | stringBuilder.AppendFormat(CultureInfo.InvariantCulture,HtmlHeader, startHTML, endHTML, startFragment, endFragment, startFragment, endFragment); 235 | 236 | // Append HTML body. 237 | stringBuilder.Append(htmlString); 238 | 239 | return stringBuilder.ToString(); 240 | } 241 | 242 | #endregion Internal Methods 243 | 244 | // --------------------------------------------------------------------- 245 | // 246 | // Private methods 247 | // 248 | // --------------------------------------------------------------------- 249 | 250 | #region Private Methods 251 | 252 | private static void InvariantAssert(bool condition, string message) 253 | { 254 | if (!condition) 255 | { 256 | throw new ArgumentOutOfRangeException("Assertion error: " + message); 257 | } 258 | } 259 | 260 | /// 261 | /// Parses the stream of html tokens starting 262 | /// from the name of top-level element. 263 | /// Returns XmlElement representing the top-level 264 | /// html element 265 | /// 266 | private XmlElement ParseHtmlContent() 267 | { 268 | // Create artificial root elelemt to be able to group multiple top-level elements 269 | // We create "html" element which may be a duplicate of real HTML element, which is ok, as HtmlConverter will swallow it painlessly.. 270 | XmlElement htmlRootElement = _document.CreateElement("html", XhtmlNamespace); 271 | OpenStructuringElement(htmlRootElement); 272 | 273 | while (_htmlLexicalAnalyzer.NextTokenType != HtmlTokenType.EOF) 274 | { 275 | if (_htmlLexicalAnalyzer.NextTokenType == HtmlTokenType.OpeningTagStart) 276 | { 277 | _htmlLexicalAnalyzer.GetNextTagToken(); 278 | if (_htmlLexicalAnalyzer.NextTokenType == HtmlTokenType.Name) 279 | { 280 | string htmlElementName = _htmlLexicalAnalyzer.NextToken.ToLower(CultureInfo.InvariantCulture); 281 | _htmlLexicalAnalyzer.GetNextTagToken(); 282 | 283 | // Create an element 284 | XmlElement htmlElement = _document.CreateElement(htmlElementName, XhtmlNamespace); 285 | 286 | // Parse element attributes 287 | ParseAttributes(htmlElement); 288 | 289 | if (_htmlLexicalAnalyzer.NextTokenType == HtmlTokenType.EmptyTagEnd || HtmlSchema.IsEmptyElement(htmlElementName)) 290 | { 291 | // It is an element without content (because of explicit slash or based on implicit knowledge aboout html) 292 | AddEmptyElement(htmlElement); 293 | } 294 | else if (HtmlSchema.IsInlineElement(htmlElementName)) 295 | { 296 | // Elements known as formatting are pushed to some special 297 | // pending stack, which allows them to be transferred 298 | // over block tags - by doing this we convert 299 | // overlapping tags into normal heirarchical element structure. 300 | OpenInlineElement(htmlElement); 301 | } 302 | else if (HtmlSchema.IsBlockElement(htmlElementName) || HtmlSchema.IsKnownOpenableElement(htmlElementName)) 303 | { 304 | // This includes no-scope elements 305 | OpenStructuringElement(htmlElement); 306 | } 307 | else 308 | { 309 | // Do nothing. Skip the whole opening tag. 310 | // Ignoring all unknown elements on their start tags. 311 | // Thus we will ignore them on closinng tag as well. 312 | // Anyway we don't know what to do withthem on conversion to Xaml. 313 | } 314 | } 315 | else 316 | { 317 | // Note that the token following opening angle bracket must be a name - lexical analyzer must guarantee that. 318 | // Otherwise - we skip the angle bracket and continue parsing the content as if it is just text. 319 | // Add the following asserion here, right? or output "<" as a text run instead?: 320 | // InvariantAssert(false, "Angle bracket without a following name is not expected"); 321 | } 322 | } 323 | else if (_htmlLexicalAnalyzer.NextTokenType == HtmlTokenType.ClosingTagStart) 324 | { 325 | _htmlLexicalAnalyzer.GetNextTagToken(); 326 | if (_htmlLexicalAnalyzer.NextTokenType == HtmlTokenType.Name) 327 | { 328 | string htmlElementName = _htmlLexicalAnalyzer.NextToken.ToLower(CultureInfo.InvariantCulture); 329 | 330 | // Skip the name token. Assume that the following token is end of tag, 331 | // but do not check this. If it is not true, we simply ignore one token 332 | // - this is our recovery from bad xml in this case. 333 | _htmlLexicalAnalyzer.GetNextTagToken(); 334 | 335 | CloseElement(htmlElementName); 336 | } 337 | } 338 | else if (_htmlLexicalAnalyzer.NextTokenType == HtmlTokenType.Text) 339 | { 340 | AddTextContent(_htmlLexicalAnalyzer.NextToken); 341 | } 342 | else if (_htmlLexicalAnalyzer.NextTokenType == HtmlTokenType.Comment) 343 | { 344 | AddComment(_htmlLexicalAnalyzer.NextToken); 345 | } 346 | 347 | _htmlLexicalAnalyzer.GetNextContentToken(); 348 | } 349 | 350 | // Get rid of the artificial root element 351 | if (htmlRootElement.FirstChild is XmlElement && 352 | htmlRootElement.FirstChild == htmlRootElement.LastChild && 353 | htmlRootElement.FirstChild.LocalName.ToLower(CultureInfo.InvariantCulture) == "html") 354 | { 355 | htmlRootElement = (XmlElement)htmlRootElement.FirstChild; 356 | } 357 | 358 | return htmlRootElement; 359 | } 360 | 361 | private XmlElement CreateElementCopy(XmlElement htmlElement) 362 | { 363 | XmlElement htmlElementCopy = _document.CreateElement(htmlElement.LocalName, XhtmlNamespace); 364 | for (int i = 0; i < htmlElement.Attributes.Count; i++) 365 | { 366 | XmlAttribute attribute = htmlElement.Attributes[i]; 367 | htmlElementCopy.SetAttribute(attribute.Name, attribute.Value); 368 | } 369 | return htmlElementCopy; 370 | } 371 | 372 | private void AddEmptyElement(XmlElement htmlEmptyElement) 373 | { 374 | InvariantAssert(_openedElements.Count > 0, "AddEmptyElement: Stack of opened elements cannot be empty, as we have at least one artificial root element"); 375 | XmlElement htmlParent = _openedElements.Peek(); 376 | htmlParent.AppendChild(htmlEmptyElement); 377 | } 378 | 379 | private void OpenInlineElement(XmlElement htmlInlineElement) 380 | { 381 | _pendingInlineElements.Push(htmlInlineElement); 382 | } 383 | 384 | // Opens structurig element such as Div or Table etc. 385 | private void OpenStructuringElement(XmlElement htmlElement) 386 | { 387 | // Close all pending inline elements 388 | // All block elements are considered as delimiters for inline elements 389 | // which forces all inline elements to be closed and re-opened in the following 390 | // structural element (if any). 391 | // By doing that we guarantee that all inline elements appear only within most nested blocks 392 | if (HtmlSchema.IsBlockElement(htmlElement.LocalName)) 393 | { 394 | while (_openedElements.Count > 0 && HtmlSchema.IsInlineElement(_openedElements.Peek().LocalName)) 395 | { 396 | XmlElement htmlInlineElement = _openedElements.Pop(); 397 | InvariantAssert(_openedElements.Count > 0, "OpenStructuringElement: stack of opened elements cannot become empty here"); 398 | 399 | _pendingInlineElements.Push(CreateElementCopy(htmlInlineElement)); 400 | } 401 | } 402 | 403 | // Add this block element to its parent 404 | if (_openedElements.Count > 0) 405 | { 406 | XmlElement htmlParent = _openedElements.Peek(); 407 | 408 | // Check some known block elements for auto-closing (LI and P) 409 | if (HtmlSchema.ClosesOnNextElementStart(htmlParent.LocalName, htmlElement.LocalName)) 410 | { 411 | _openedElements.Pop(); 412 | htmlParent = _openedElements.Count > 0 ? _openedElements.Peek() : null; 413 | } 414 | 415 | if (htmlParent != null) 416 | { 417 | // NOTE: 418 | // Actually we never expect null - it would mean two top-level P or LI (without a parent). 419 | // In such weird case we will loose all paragraphs except the first one... 420 | htmlParent.AppendChild(htmlElement); 421 | } 422 | } 423 | 424 | // Push it onto a stack 425 | _openedElements.Push(htmlElement); 426 | } 427 | 428 | private bool IsElementOpened(string htmlElementName) 429 | { 430 | foreach (XmlElement openedElement in _openedElements) 431 | { 432 | if (openedElement.LocalName == htmlElementName) 433 | { 434 | return true; 435 | } 436 | } 437 | return false; 438 | } 439 | 440 | private void CloseElement(string htmlElementName) 441 | { 442 | // Check if the element is opened and already added to the parent 443 | InvariantAssert(_openedElements.Count > 0, "CloseElement: Stack of opened elements cannot be empty, as we have at least one artificial root element"); 444 | 445 | // Check if the element is opened and still waiting to be added to the parent 446 | if (_pendingInlineElements.Count > 0 && _pendingInlineElements.Peek().LocalName == htmlElementName) 447 | { 448 | // Closing an empty inline element. 449 | // Note that HtmlConverter will skip empty inlines, but for completeness we keep them here on parser level. 450 | XmlElement htmlInlineElement = _pendingInlineElements.Pop(); 451 | InvariantAssert(_openedElements.Count > 0, "CloseElement: Stack of opened elements cannot be empty, as we have at least one artificial root element"); 452 | XmlElement htmlParent = _openedElements.Peek(); 453 | htmlParent.AppendChild(htmlInlineElement); 454 | return; 455 | } 456 | else if (IsElementOpened(htmlElementName)) 457 | { 458 | while (_openedElements.Count > 1) // we never pop the last element - the artificial root 459 | { 460 | // Close all unbalanced elements. 461 | XmlElement htmlOpenedElement = _openedElements.Pop(); 462 | 463 | if (htmlOpenedElement.LocalName == htmlElementName) 464 | { 465 | return; 466 | } 467 | 468 | if (HtmlSchema.IsInlineElement(htmlOpenedElement.LocalName)) 469 | { 470 | // Unbalances Inlines will be transfered to the next element content 471 | _pendingInlineElements.Push(CreateElementCopy(htmlOpenedElement)); 472 | } 473 | } 474 | } 475 | 476 | // If element was not opened, we simply ignore the unbalanced closing tag 477 | return; 478 | } 479 | 480 | private void AddTextContent(string textContent) 481 | { 482 | OpenPendingInlineElements(); 483 | 484 | InvariantAssert(_openedElements.Count > 0, "AddTextContent: Stack of opened elements cannot be empty, as we have at least one artificial root element"); 485 | 486 | XmlElement htmlParent = _openedElements.Peek(); 487 | XmlText textNode = _document.CreateTextNode(textContent); 488 | htmlParent.AppendChild(textNode); 489 | } 490 | 491 | private void AddComment(string comment) 492 | { 493 | OpenPendingInlineElements(); 494 | 495 | InvariantAssert(_openedElements.Count > 0, "AddComment: Stack of opened elements cannot be empty, as we have at least one artificial root element"); 496 | 497 | XmlElement htmlParent = _openedElements.Peek(); 498 | XmlComment xmlComment = _document.CreateComment(comment); 499 | htmlParent.AppendChild(xmlComment); 500 | } 501 | 502 | // Moves all inline elements pending for opening to actual document 503 | // and adds them to current open stack. 504 | private void OpenPendingInlineElements() 505 | { 506 | if (_pendingInlineElements.Count > 0) 507 | { 508 | XmlElement htmlInlineElement = _pendingInlineElements.Pop(); 509 | 510 | OpenPendingInlineElements(); 511 | 512 | InvariantAssert(_openedElements.Count > 0, "OpenPendingInlineElements: Stack of opened elements cannot be empty, as we have at least one artificial root element"); 513 | 514 | XmlElement htmlParent = _openedElements.Peek(); 515 | htmlParent.AppendChild(htmlInlineElement); 516 | _openedElements.Push(htmlInlineElement); 517 | } 518 | } 519 | 520 | private void ParseAttributes(XmlElement xmlElement) 521 | { 522 | while (_htmlLexicalAnalyzer.NextTokenType != HtmlTokenType.EOF && // 523 | _htmlLexicalAnalyzer.NextTokenType != HtmlTokenType.TagEnd && // 524 | _htmlLexicalAnalyzer.NextTokenType != HtmlTokenType.EmptyTagEnd) 525 | { 526 | // read next attribute (name=value) 527 | if (_htmlLexicalAnalyzer.NextTokenType == HtmlTokenType.Name) 528 | { 529 | string attributeName = _htmlLexicalAnalyzer.NextToken; 530 | _htmlLexicalAnalyzer.GetNextEqualSignToken(); 531 | 532 | _htmlLexicalAnalyzer.GetNextAtomToken(); 533 | 534 | string attributeValue = _htmlLexicalAnalyzer.NextToken; 535 | xmlElement.SetAttribute(attributeName, attributeValue); 536 | } 537 | _htmlLexicalAnalyzer.GetNextTagToken(); 538 | } 539 | } 540 | 541 | #endregion Private Methods 542 | 543 | 544 | // --------------------------------------------------------------------- 545 | // 546 | // Private Fields 547 | // 548 | // --------------------------------------------------------------------- 549 | 550 | #region Private Fields 551 | 552 | internal const string XhtmlNamespace = "http://www.w3.org/1999/xhtml"; 553 | 554 | private HtmlLexicalAnalyzer _htmlLexicalAnalyzer = null; 555 | 556 | // document from which all elements are created 557 | private XmlDocument _document; 558 | 559 | // stack for open elements 560 | Stack _openedElements; 561 | Stack _pendingInlineElements; 562 | 563 | #endregion Private Fields 564 | 565 | private bool isDisposing = false; 566 | public void Dispose() 567 | { 568 | Dispose(isDisposing); 569 | } 570 | 571 | private void Dispose(bool disposing) 572 | { 573 | if (!disposing) 574 | { 575 | isDisposing = true; 576 | 577 | if (this._htmlLexicalAnalyzer != null) 578 | { 579 | this._htmlLexicalAnalyzer.Dispose(); 580 | this._htmlLexicalAnalyzer = null; 581 | } 582 | } 583 | } 584 | 585 | } 586 | } 587 | -------------------------------------------------------------------------------- /src/WpfRichText/XamlToHtmlParser/HtmlTokenType.cs: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------- 2 | // 3 | // File: HtmlTokenType.cs 4 | // 5 | // Copyright (C) Microsoft Corporation. All rights reserved. 6 | // 7 | // Description: Definition of token types supported by HtmlLexicalAnalyzer 8 | // 9 | //--------------------------------------------------------------------------- 10 | 11 | namespace WpfRichText 12 | { 13 | /// 14 | /// types of lexical tokens for html-to-xaml converter 15 | /// 16 | internal enum HtmlTokenType 17 | { 18 | OpeningTagStart, 19 | ClosingTagStart, 20 | TagEnd, 21 | EmptyTagEnd, 22 | EqualSign, 23 | Name, 24 | Atom, // any attribute value not in quotes 25 | Text, //text content when accepting text 26 | Comment, 27 | EOF, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/WpfRichText/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /src/WpfRichText/self.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/src/WpfRichText/self.snk --------------------------------------------------------------------------------