├── LICENSES ├── stix.pdf └── inter.txt ├── Demo ├── StoreLogo.ico ├── App.config ├── Properties │ ├── Settings.settings │ ├── Settings.Designer.cs │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ └── Resources.resx ├── App.xaml.cs ├── MyCertificate.cs ├── ImportDialog.xaml ├── ImportDialog.xaml.cs ├── TypeOfContentDialog.xaml ├── TypeOfContentDialog.xaml.cs ├── App.xaml ├── MyScript.InteractiveInk.Demo.Wpf.csproj └── MainWindow.xaml ├── GetStarted ├── StoreLogo.ico ├── App.config ├── Properties │ ├── Settings.settings │ ├── Settings.Designer.cs │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ └── Resources.resx ├── App.xaml.cs ├── MyCertificate.cs ├── MainWindow.xaml ├── App.xaml ├── MainWindow.xaml.cs └── MyScript.InteractiveInk.GetStarted.Wpf.csproj ├── fonts ├── MyScriptInter-Bold.otf ├── STIXTwoMath-Regular.otf ├── STIXTwoText-Italic.otf └── MyScriptInter-Regular.otf ├── CONTRIBUTING.md ├── LICENSE ├── configurations ├── Raw Content │ ├── drawing.json │ └── text_math_shape.json └── interactivity.json ├── UIReferenceImplementation ├── LayerControl.cs ├── Properties │ └── AssemblyInfo.cs ├── UserControls │ ├── EditorUserControl.xaml │ └── SmartGuideUserControl.xaml ├── Path.cs ├── ResourceDictionary.xaml ├── ImagePainter.cs ├── MyScript.InteractiveInk.UIReferenceImplementation.Wpf.csproj ├── ImageLoader.cs ├── DisplayResolution.cs ├── FontMetricsProvider.cs └── Canvas.cs ├── MyScript.InteractiveInk.Examples.Wpf.sln ├── README.md ├── getRecognitionAssets.ps1 ├── .gitattributes └── .gitignore /LICENSES/stix.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-wpf/HEAD/LICENSES/stix.pdf -------------------------------------------------------------------------------- /Demo/StoreLogo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-wpf/HEAD/Demo/StoreLogo.ico -------------------------------------------------------------------------------- /GetStarted/StoreLogo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-wpf/HEAD/GetStarted/StoreLogo.ico -------------------------------------------------------------------------------- /fonts/MyScriptInter-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-wpf/HEAD/fonts/MyScriptInter-Bold.otf -------------------------------------------------------------------------------- /fonts/STIXTwoMath-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-wpf/HEAD/fonts/STIXTwoMath-Regular.otf -------------------------------------------------------------------------------- /fonts/STIXTwoText-Italic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-wpf/HEAD/fonts/STIXTwoText-Italic.otf -------------------------------------------------------------------------------- /fonts/MyScriptInter-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-wpf/HEAD/fonts/MyScriptInter-Regular.otf -------------------------------------------------------------------------------- /Demo/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /GetStarted/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Demo/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /GetStarted/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Demo/App.xaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | using System.Windows; 4 | 5 | namespace MyScript.IInk.Demo 6 | { 7 | /// 8 | /// Interaction logic for App.xaml 9 | /// 10 | public partial class App : Application 11 | { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /GetStarted/App.xaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | using System.Windows; 4 | 5 | namespace MyScript.IInk.GetStarted 6 | { 7 | /// 8 | /// Interaction logic for App.xaml 9 | /// 10 | public partial class App : Application 11 | { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We gladly welcome pull requests. If you have any questions, or want help solving a problem, feel free to stop by the [#MyScript forum](https://developer.myscript.com/support/). 4 | 5 | ## License 6 | 7 | Those examples are licensed under the [Apache 2.0](https://opensource.org/license/apache-2-0). 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright MyScript. 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software 7 | distributed under the License is distributed on an "AS IS" BASIS, 8 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | See the License for the specific language governing permissions and 10 | limitations under the License. 11 | -------------------------------------------------------------------------------- /Demo/MyCertificate.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * MyCertificate.cs 3 | * Replace this file with with the one you received by mail when you registered 4 | * as a developer on https://developer.myscript.com. 5 | */ 6 | namespace MyScript.Certificate 7 | { 8 | public sealed class MyCertificate 9 | { 10 | public static sbyte[] Bytes 11 | { 12 | get 13 | { 14 | throw new System.Exception("Please replace the content of MyCertificate.cs with the certificate you received from the developer portal"); 15 | } 16 | } 17 | 18 | } // end of: class MyCertificate 19 | 20 | } // end of: namespace 21 | -------------------------------------------------------------------------------- /GetStarted/MyCertificate.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * MyCertificate.cs 3 | * Replace this file with with the one you received by mail when you registered 4 | * as a developer on https://developer.myscript.com. 5 | */ 6 | namespace MyScript.Certificate 7 | { 8 | public sealed class MyCertificate 9 | { 10 | public static sbyte[] Bytes 11 | { 12 | get 13 | { 14 | throw new System.Exception("Please replace the content of MyCertificate.cs with the certificate you received from the developer portal"); 15 | } 16 | } 17 | 18 | } // end of: class MyCertificate 19 | 20 | } // end of: namespace 21 | -------------------------------------------------------------------------------- /configurations/Raw Content/drawing.json: -------------------------------------------------------------------------------- 1 | { 2 | "raw-content": { 3 | "line-pattern": "none", 4 | "recognition": { 5 | "types": [ ] 6 | }, 7 | "classification": { 8 | "types": [ ] 9 | }, 10 | "convert": { 11 | "shape-on-hold": false 12 | }, 13 | "shape": { 14 | "snap-axis": [ ] 15 | }, 16 | "interactive-blocks": { 17 | "auto-classified": [ ] 18 | }, 19 | "eraser": { 20 | "erase-precisely": true, 21 | "dynamic-radius": true 22 | }, 23 | "auto-connection": false, 24 | "guides": { 25 | "show": [ ], 26 | "snap": [ ] 27 | }, 28 | "pen": { 29 | "gestures": [ ] 30 | }, 31 | "rotation": [ "image" ] 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Demo/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 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 MyScript.IInk.Demo.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.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 | -------------------------------------------------------------------------------- /GetStarted/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 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 MyScript.IInk.GetStarted.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.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 | -------------------------------------------------------------------------------- /configurations/interactivity.json: -------------------------------------------------------------------------------- 1 | { 2 | "diagram": { 3 | "configuration": { 4 | "analyzer": { 5 | "bundle": "raw-content", 6 | "name": "text-block" 7 | } 8 | } 9 | }, 10 | "raw-content": { 11 | "configuration": { 12 | "analyzer": { 13 | "bundle": "raw-content", 14 | "name": "text-block" 15 | }, 16 | "math": { 17 | "bundle": "math", 18 | "name": "standard" 19 | } 20 | }, 21 | "classification": { 22 | "types": [ "text", "drawing" ] 23 | }, 24 | "recognition": { 25 | "types": [ "text", "math" ] 26 | }, 27 | "convert": { 28 | "shape-on-hold": true 29 | }, 30 | "shape": { 31 | "snap-axis": [ "triangle", "rectangle", "rhombus", "parallelogram", "ellipse" ] 32 | }, 33 | "eraser": { 34 | "erase-precisely": false, 35 | "dynamic-radius": true 36 | }, 37 | "auto-connection": true, 38 | "interactive-blocks": { 39 | "feedback": [ "math" ] 40 | }, 41 | "pen": { 42 | "gestures": [ "underline", "scratch-out", "strike-through" ] 43 | }, 44 | "line-pattern": "grid", 45 | "guides": { 46 | "show": [ "alignment", "text", "square", "square-inside", "image-aspect-ratio", "rotation" ], 47 | "snap": [ "alignment", "text", "square", "square-inside", "image-aspect-ratio", "rotation" ] 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /UIReferenceImplementation/LayerControl.cs: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | using System; 4 | using System.Windows.Controls; 5 | using System.Windows.Media; 6 | 7 | namespace MyScript.IInk.UIReferenceImplementation 8 | { 9 | public class LayerControl : Label 10 | { 11 | public Renderer Renderer { get; set; } 12 | public ImageLoader ImageLoader { get; set; } 13 | 14 | /// Redraw the Layer Control 15 | protected override void OnRender(DrawingContext drawingContext) 16 | { 17 | base.OnRender(drawingContext); 18 | 19 | if (Renderer != null) 20 | { 21 | int x = 0; 22 | int y = 0; 23 | int width = (int)Math.Round(ActualWidth); 24 | int height = (int)Math.Round(ActualHeight); 25 | float pixelsPerDip = (float)DisplayResolution.GetPixelsPerDip(this); 26 | Canvas canvas = new Canvas(drawingContext, pixelsPerDip, ImageLoader); 27 | 28 | Renderer.DrawModel(x, y, width, height, canvas); 29 | Renderer.DrawCaptureStrokes(x, y, width, height, canvas); 30 | } 31 | } 32 | 33 | /// Force to redraw the Layer Control 34 | public void Update() 35 | { 36 | this.Dispatcher.BeginInvoke(new Action(() => 37 | { 38 | this.InvalidateVisual(); 39 | })); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /UIReferenceImplementation/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("MyScript.InteractiveInk.UIReferenceImplementation.Wpf")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("MyScript")] 12 | [assembly: AssemblyProduct("MyScript.InteractiveInk.UIReferenceImplementation.Wpf")] 13 | [assembly: AssemblyCopyright("Copyright @ MyScript. All rights reserved.")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("8ed3cb31-d4b5-4b82-8c15-f6c31bbe520d")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("4.2.1.0")] 36 | [assembly: AssemblyFileVersion("4.2.1.0")] 37 | -------------------------------------------------------------------------------- /configurations/Raw Content/text_math_shape.json: -------------------------------------------------------------------------------- 1 | { 2 | "raw-content": { 3 | "configuration": { 4 | "analyzer": { 5 | "bundle": "raw-content2", 6 | "name": "standard" 7 | }, 8 | "math": { 9 | "bundle": "math2", 10 | "name": "standard" 11 | } 12 | }, 13 | "classification": { 14 | "types": [ "text", "shape", "math", "drawing" ] 15 | }, 16 | "recognition": { 17 | "types": [ "text", "shape", "math" ] 18 | }, 19 | "convert": { 20 | "shape-on-hold": true 21 | }, 22 | "shape": { 23 | "snap-axis": [ ] 24 | }, 25 | "eraser": { 26 | "erase-precisely": false, 27 | "dynamic-radius": true 28 | }, 29 | "auto-connection": false, 30 | "edge": { 31 | "policy": [ ] 32 | }, 33 | "interactive-blocks": { 34 | "auto-classified": [ "text", "math", "shape" ], 35 | "feedback": [ "text", "math" ], 36 | "feedback-hints": [ "text", "math" ] 37 | }, 38 | "pen": { 39 | "gestures": [ "scratch-out", "strike-through" ] 40 | }, 41 | "line-pattern": "grid", 42 | "guides": { 43 | "show": [ "alignment", "text", "square", "square-inside", "image-aspect-ratio", "rotation" ], 44 | "snap": [ "alignment", "text", "square", "square-inside", "image-aspect-ratio", "rotation" ] 45 | } 46 | }, 47 | "convert": { 48 | "convert-on-double-tap": false 49 | }, 50 | "math": { 51 | "solver": { 52 | "options": "algebraic", 53 | "numerical-computation": [ "at-right-of-equal-sign", "at-question-mark" ], 54 | "enable-syntactic-correction": false, 55 | "display-implicit-multiply": false, 56 | "auto-variable-management": { 57 | "enable": true, 58 | "scoping-policy": "closest" 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Demo/ImportDialog.xaml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 23 | 24 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Demo/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("MyScript.InteractiveInk.Demo.Wpf")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("MyScript")] 14 | [assembly: AssemblyProduct("MyScript.InteractiveInk.Demo.Wpf")] 15 | [assembly: AssemblyCopyright("Copyright @ MyScript. All rights reserved.")] 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("4.2.1.0")] 55 | [assembly: AssemblyFileVersion("4.2.1.0")] 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## What is it about? 2 | 3 | Interactive Ink SDK is the best way to integrate handwriting recognition capabilities into your WPF application. Interactive Ink extends digital ink to allow users to more intuitively create, interact with, and share content in digital form. Handwritten text, mathematical equations or even diagrams are interpreted in real-time to be editable via simple gestures, responsive and easy to convert to a neat output. 4 | 5 | This repository contains a "get started" example, a complete example and a reference implementation of the WPF integration part that developers using Interactive Ink SDK can reuse inside their projects. All of those are written in C# and XAML. 6 | 7 | The repository content targets WPF platform. 8 | 9 | ## Getting started 10 | 11 | ### Installation 12 | 13 | 1. Clone the examples repository `git clone https://github.com/MyScript/interactive-ink-examples-wpf.git` 14 | 15 | 2. Claim a certificate to receive the free license to start develop your application by following the first steps of [Getting Started](https://developer.myscript.com/getting-started) 16 | 17 | 3. Copy this certificate to `GetStarted\MyCertificate.cs` and `Demo\MyCertificate.cs` 18 | 19 | 4. Open `MyScript.InteractiveInk.Examples.Wpf.sln` file. `GetStarted` project is the most simple example and is design to help you understand what Interactive Ink is about and how easy it is to integrate it into your application. `Demo` project contains a complete example and helps you build your own integration. You can select which project to launch by right-clicking the project in the solution browser and selecting "Set as startup project". 20 | 21 | ## Building your own integration 22 | 23 | In your application add the dependency to `MyScript.InteractiveInk.Wpf` nuget. Also copy `UIReferenceImplementation` directory into your project. More details available in the [developer guide](https://developer.myscript.com/docs/interactive-ink/1.0/windows/). 24 | 25 | ## Documentation 26 | 27 | A complete guide is available on [MyScript Developer website](https://developer.myscript.com/docs/interactive-ink/latest/windows/). 28 | 29 | The API Reference is available in Visual Studio as soon as the Nugets packages are downloaded. 30 | 31 | ## Getting support 32 | 33 | You can get some support from the dedicated section on [MyScript Developer website](https://devportal.corp.myscript.com/support/). 34 | 35 | ## Sharing your feedback ? 36 | 37 | Made a cool app with Interactive Ink? Ready to cross join our marketing efforts? We would love to hear about you! 38 | We’re planning to showcase apps using it so let us know by sending a quick mail to [myapp@myscript.com](mailto://myapp@myscript.com). 39 | -------------------------------------------------------------------------------- /GetStarted/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("MyScript.InteractiveInk.GetStarted.Wpf")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("MyScript")] 14 | [assembly: AssemblyProduct("MyScript.InteractiveInk.GetStarted.Wpf")] 15 | [assembly: AssemblyCopyright("Copyright @ MyScript. All rights reserved.")] 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("4.2.1.0")] 55 | [assembly: AssemblyFileVersion("4.2.1.0")] 56 | -------------------------------------------------------------------------------- /UIReferenceImplementation/ResourceDictionary.xaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /GetStarted/MainWindow.xaml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 51 | 56 | 61 | 62 | 63 | 64 | 69 | 74 | 75 | 76 | 77 | 82 | 87 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 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 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /UIReferenceImplementation/FontMetricsProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | using MyScript.IInk.Text; 4 | using MyScript.IInk.Graphics; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Windows; 8 | using System.Windows.Controls; 9 | using System.Windows.Documents; 10 | using System.Windows.Media; 11 | using System; 12 | using System.Threading; 13 | 14 | namespace MyScript.IInk.UIReferenceImplementation 15 | { 16 | internal static class FontFamilies 17 | { 18 | public static string MyScriptInter { get { return _myScriptInter ?? _defaultFamily; } private set { _myScriptInter = value; } } 19 | public static string MyScriptInterBold { get { return _myScriptInterBold ?? _defaultFamily; } private set { _myScriptInterBold = value; } } 20 | public static string StixRegular { get { return _stixRegular ?? _defaultStixFamily; } private set { _stixRegular = value; } } 21 | public static string StixItalic { get { return _stixItalic ?? _defaultStixFamily; } private set { _stixItalic = value; } } 22 | 23 | private const string _defaultFamily = "Segoe UI"; 24 | private const string _defaultStixFamily = "STIX"; 25 | 26 | private const string _fontFolder = "fonts"; 27 | 28 | private static bool _initialized = false; 29 | private static string _myScriptInter; 30 | private static string _myScriptInterBold; 31 | private static string _stixRegular; 32 | private static string _stixItalic; 33 | 34 | private static string RegisterFontFamily(string filename, string name, string defaultFamily) 35 | { 36 | var localPath = System.IO.Path.Combine(_fontFolder, filename); 37 | var fullPath = System.IO.Path.GetFullPath(localPath); 38 | 39 | if (System.IO.File.Exists(fullPath)) 40 | return fullPath + "#" + name; 41 | 42 | return defaultFamily; 43 | } 44 | 45 | public static void Initialize() 46 | { 47 | if (_initialized) 48 | return; 49 | 50 | MyScriptInter = RegisterFontFamily("MyScriptInter-Regular.otf", "MyScriptInter", _defaultFamily); 51 | MyScriptInterBold = RegisterFontFamily("MyScriptInter-Bold.otf", "MyScriptInter", _defaultFamily); 52 | StixRegular = RegisterFontFamily("STIXTwoMath-Regular.otf", "STIX Two Math", _defaultStixFamily); 53 | StixItalic = RegisterFontFamily("STIXTwoText-Italic.otf", "STIX Two Text", _defaultStixFamily); 54 | 55 | _initialized = true; 56 | } 57 | } 58 | 59 | public class FontMetricsProvider : IFontMetricsProvider 60 | { 61 | private float _dpiX; 62 | private float _dpiY; 63 | private float _pixelsPerDip; 64 | 65 | public static void Initialize() 66 | { 67 | FontFamilies.Initialize(); 68 | } 69 | 70 | public FontMetricsProvider(float dpiX, float dpiY, float pixelsPerDip) 71 | { 72 | _dpiX = dpiX; 73 | _dpiY = dpiY; 74 | _pixelsPerDip = pixelsPerDip; 75 | } 76 | 77 | public static string ToPlatformFontFamily(string family, string style, int weight) 78 | { 79 | if (family == "MyScriptInter") 80 | return (ToPlatformFontWeight(weight) == FontWeights.Bold) ? FontFamilies.MyScriptInterBold : FontFamilies.MyScriptInter; 81 | else if (family == "STIX") 82 | return (ToPlatformFontStyle(style) == FontStyles.Italic) ? FontFamilies.StixItalic : FontFamilies.StixRegular; 83 | 84 | return family; 85 | } 86 | 87 | public static FontWeight ToPlatformFontWeight(int weight) 88 | { 89 | var fontWeight = FontWeight.FromOpenTypeWeight(weight); 90 | 91 | if (weight >= 700) 92 | fontWeight = FontWeights.Bold; 93 | else if (weight >= 400) 94 | fontWeight = FontWeights.Normal; 95 | else 96 | fontWeight = FontWeights.Light; 97 | 98 | return fontWeight; 99 | } 100 | 101 | public static FontStyle ToPlatformFontStyle(string style) 102 | { 103 | var fontStyle = FontStyles.Normal; 104 | 105 | if (style.Equals("italic")) 106 | fontStyle = FontStyles.Italic; 107 | else if (style.Equals("oblique")) 108 | fontStyle = FontStyles.Oblique; 109 | 110 | return fontStyle; 111 | } 112 | 113 | private static float px2mm(float px, float dpi) 114 | { 115 | return 25.4f * (px / dpi); 116 | } 117 | 118 | private static float mm2px(float mmm, float dpi) 119 | { 120 | return (mmm / 25.4f) * dpi; 121 | } 122 | 123 | private class FontKey 124 | { 125 | public FontFamily FontFamily { get; } 126 | public float FontSize { get; } 127 | public FontWeight FontWeight { get; } 128 | public FontStretch FontStretch{ get; } 129 | public FontStyle FontStyle { get; } 130 | 131 | public FontKey(FontFamily fontFamily, float fontSize, FontWeight fontWeight, FontStretch fontStretch, FontStyle fontStyle) 132 | { 133 | this.FontFamily = fontFamily; 134 | this.FontSize = fontSize; 135 | this.FontWeight = fontWeight; 136 | this.FontStretch = fontStretch; 137 | this.FontStyle = fontStyle; 138 | } 139 | 140 | public override bool Equals(object obj) 141 | { 142 | if (obj.GetType() != this.GetType()) 143 | return false; 144 | 145 | FontKey other = (FontKey)obj; 146 | return ( (this.FontFamily.Equals(other.FontFamily)) 147 | && (this.FontSize == other.FontSize) 148 | && (this.FontWeight == other.FontWeight) 149 | && (this.FontStretch == other.FontStretch) 150 | && (this.FontStyle == other.FontStyle) ); 151 | } 152 | 153 | public override int GetHashCode() 154 | { 155 | return FontFamily.GetHashCode() ^ FontSize.GetHashCode() ^ FontWeight.GetHashCode() ^ FontStretch.GetHashCode() ^ FontStyle.GetHashCode(); 156 | } 157 | } 158 | private Dictionary> cache = new Dictionary>(); 159 | 160 | private FontKey FontKeyFromStyle(MyScript.IInk.Graphics.Style style) 161 | { 162 | var fontFamily = new FontFamily(ToPlatformFontFamily(style.FontFamily, style.FontStyle, style.FontWeight)); 163 | var fontSize = mm2px(style.FontSize, _dpiY); 164 | var fontWeight = ToPlatformFontWeight(style.FontWeight); 165 | var fontStretch = FontStretches.Normal; 166 | var fontStyle = ToPlatformFontStyle(style.FontStyle); 167 | return new FontKey(fontFamily, fontSize, fontWeight, fontStretch, fontStyle); 168 | } 169 | 170 | private GlyphMetrics GetGlyphMetrics(FontKey fontKey, string glyphLabel) 171 | { 172 | Dictionary fontCache = null; 173 | if (!cache.TryGetValue(fontKey, out fontCache)) 174 | { 175 | fontCache = new Dictionary(); 176 | cache[fontKey] = fontCache; 177 | } 178 | 179 | GlyphMetrics value = null; 180 | if (!fontCache.TryGetValue(glyphLabel, out value)) 181 | { 182 | var typeFace = new Typeface(fontKey.FontFamily, fontKey.FontStyle, fontKey.FontWeight, fontKey.FontStretch); 183 | var formattedChar = new FormattedText 184 | ( 185 | glyphLabel, System.Globalization.CultureInfo.CurrentCulture, 186 | FlowDirection.LeftToRight, typeFace, fontKey.FontSize, Brushes.Black, 187 | _pixelsPerDip 188 | ); 189 | 190 | formattedChar.TextAlignment = TextAlignment.Left; 191 | 192 | var geometry = formattedChar.BuildGeometry(new System.Windows.Point(0.0f, 0.0f)); 193 | var rect = geometry.Bounds; 194 | 195 | // For glyph without geometry (space) 196 | if (rect.IsEmpty) 197 | rect = new Rect(0.0, 0.0, formattedChar.WidthIncludingTrailingWhitespace, formattedChar.Height); 198 | 199 | var rectX = (float)rect.X; 200 | var rectY = (float)rect.Y; 201 | var rectW = (float)rect.Width; 202 | var rectH = (float)rect.Height; 203 | 204 | var leftBearing = -(float)(rect.X); 205 | var rightBearing = 0.0f; 206 | 207 | var glyphX = px2mm(rectX, _dpiX); 208 | var glyphY = px2mm(rectY, _dpiY); 209 | var glyphW = px2mm(rectW, _dpiX); 210 | var glyphH = px2mm(rectH, _dpiY); 211 | var glyphRect = new Rectangle(glyphX, glyphY, glyphW, glyphH); 212 | var glyphLeftBearing = px2mm(leftBearing, _dpiX); 213 | var glyphRightBearing = px2mm(rightBearing, _dpiX); 214 | 215 | value = new GlyphMetrics(glyphRect, glyphLeftBearing, glyphRightBearing); 216 | fontCache[glyphLabel] = value; 217 | } 218 | 219 | return new GlyphMetrics(new Rectangle(value.BoundingBox.X, value.BoundingBox.Y, value.BoundingBox.Width, value.BoundingBox.Height), value.LeftSideBearing, value.RightSideBearing); 220 | } 221 | 222 | private void GetGlyphMetrics(MyScript.IInk.Text.Text text, TextSpan[] spans, List glyphMetrics) 223 | { 224 | // Process glyph one by one to generate one box per glyph 225 | for (int s = 0; s < spans.Length; ++s) 226 | { 227 | var textSpan = spans[s]; 228 | var fontKey = FontKeyFromStyle(textSpan.Style); 229 | 230 | for (int j = textSpan.BeginPosition; j < textSpan.EndPosition; ++j) 231 | { 232 | var glyphLabel = text.GetGlyphLabelAt(j); 233 | var glyphMetrics_ = GetGlyphMetrics(fontKey, glyphLabel); 234 | 235 | glyphMetrics.Add(glyphMetrics_); 236 | } 237 | } 238 | 239 | if (glyphMetrics.Count == 0) 240 | return; 241 | 242 | // Draw text to get data for glyphs 243 | FormattedText formattedText; 244 | { 245 | var firstStyle = spans[0].Style; 246 | var firstFontKey = FontKeyFromStyle(firstStyle); 247 | var firstFontTypeFace = new Typeface(firstFontKey.FontFamily, firstFontKey.FontStyle, firstFontKey.FontWeight, firstFontKey.FontStretch); 248 | 249 | formattedText = new FormattedText 250 | ( 251 | text.Label, System.Globalization.CultureInfo.CurrentCulture, 252 | FlowDirection.LeftToRight, firstFontTypeFace, firstFontKey.FontSize, 253 | Brushes.Black, _pixelsPerDip 254 | ); 255 | 256 | formattedText.TextAlignment = TextAlignment.Left; 257 | 258 | for (int s = 0; s < spans.Length; ++s) 259 | { 260 | var textSpan = spans[s]; 261 | var charIndex = textSpan.BeginPosition; 262 | var charCount = textSpan.EndPosition - textSpan.BeginPosition; 263 | var fontKey = FontKeyFromStyle(textSpan.Style); 264 | var fontTypeFace = new Typeface(fontKey.FontFamily, fontKey.FontStyle, fontKey.FontWeight, fontKey.FontStretch); 265 | 266 | formattedText.SetFontFamily(fontKey.FontFamily, charIndex, charCount); 267 | formattedText.SetFontSize(fontKey.FontSize, charIndex, charCount); 268 | formattedText.SetFontWeight(fontKey.FontWeight, charIndex, charCount); 269 | formattedText.SetFontStretch(fontKey.FontStretch, charIndex, charCount); 270 | formattedText.SetFontStyle(fontKey.FontStyle, charIndex, charCount); 271 | formattedText.SetFontTypeface(fontTypeFace, charIndex, charCount); 272 | } 273 | } 274 | 275 | var drawing = new DrawingGroup(); 276 | { 277 | var ctx = drawing.Open(); 278 | ctx.DrawText(formattedText, new System.Windows.Point(0.0, 0.0)); 279 | ctx.Close(); 280 | } 281 | 282 | // Apply baseline and offsets of glyphs to bounding boxes 283 | float baseline = (float)formattedText.Baseline; 284 | WalkDrawingForText(drawing, glyphMetrics, baseline); 285 | } 286 | 287 | private static void WalkDrawingForText(Drawing drawing, List glyphMetrics, ref int idx, ref float x, float baseline, float dpiX, float dpiY) 288 | { 289 | // Parse the Drawing tree recursively depending on the real type of each node 290 | // - node is a DrawingGroup, parse the children 291 | // - node is a GlyphRunDrawing, retrieve the glyphs data 292 | 293 | if (idx >= glyphMetrics.Count) 294 | return; 295 | 296 | var glyphs = drawing as GlyphRunDrawing; 297 | 298 | if (glyphs != null) 299 | { 300 | var glyphRun = glyphs.GlyphRun; 301 | 302 | if ( (glyphRun.Characters != null) && (glyphRun.ClusterMap != null) 303 | && (glyphRun.Characters.Count == glyphRun.ClusterMap.Count) ) 304 | { 305 | var text = new String(glyphRun.Characters.ToArray()); 306 | var tee = System.Globalization.StringInfo.GetTextElementEnumerator(text); 307 | 308 | while (tee.MoveNext()) 309 | { 310 | if (idx >= glyphMetrics.Count) 311 | break; 312 | 313 | var g = tee.ElementIndex; 314 | 315 | if ( (g > 0) && (idx > 0) && (glyphRun.ClusterMap[g] == glyphRun.ClusterMap[g-1]) ) 316 | { 317 | // Ligature with the previous glyph 318 | // The position is not accurate because of glyphs substitution at rendering 319 | // but it makes the illusion. 320 | var prevGlyphMetrics = glyphMetrics[idx-1]; 321 | glyphMetrics[idx].BoundingBox.Y -= px2mm(baseline, dpiY); 322 | glyphMetrics[idx].BoundingBox.X = prevGlyphMetrics.BoundingBox.X 323 | + prevGlyphMetrics.BoundingBox.Width 324 | + prevGlyphMetrics.RightSideBearing 325 | + glyphMetrics[idx].LeftSideBearing; 326 | } 327 | else 328 | { 329 | glyphMetrics[idx].BoundingBox.Y -= px2mm(baseline, dpiY); 330 | glyphMetrics[idx].BoundingBox.X += px2mm(x, dpiX); 331 | x += (float)glyphRun.AdvanceWidths[glyphRun.ClusterMap[g]]; 332 | } 333 | 334 | ++idx; 335 | } 336 | } 337 | else 338 | { 339 | // Fallback in case we have no data for characters and clusters 340 | var glyphCount = glyphRun.AdvanceWidths.Count; 341 | 342 | for (int g = 0; g < glyphCount; ++g) 343 | { 344 | if (idx >= glyphMetrics.Count) 345 | break; 346 | 347 | glyphMetrics[idx].BoundingBox.Y -= px2mm(baseline, dpiY); 348 | glyphMetrics[idx].BoundingBox.X += px2mm(x, dpiX); 349 | 350 | x += (float)glyphRun.AdvanceWidths[g]; 351 | ++idx; 352 | } 353 | } 354 | } 355 | else 356 | { 357 | var group = drawing as DrawingGroup; 358 | 359 | if (group != null) 360 | { 361 | foreach (var child in group.Children) 362 | WalkDrawingForText(child, glyphMetrics, ref idx, ref x, baseline, dpiX, dpiY); 363 | } 364 | } 365 | } 366 | 367 | private void WalkDrawingForText(Drawing drawing, List glyphMetrics, float baseline) 368 | { 369 | int glyphIdx = 0; 370 | float glyphX = 0.0f; 371 | WalkDrawingForText(drawing, glyphMetrics, ref glyphIdx, ref glyphX, baseline, _dpiX, _dpiY); 372 | } 373 | 374 | public Rectangle[] GetCharacterBoundingBoxes(MyScript.IInk.Text.Text text, TextSpan[] spans) 375 | { 376 | var glyphMetrics = new List(); 377 | var rectangles = new List(); 378 | 379 | GetGlyphMetrics(text, spans, glyphMetrics); 380 | 381 | foreach (var metrics in glyphMetrics) 382 | rectangles.Add(metrics.BoundingBox); 383 | 384 | return rectangles.ToArray(); 385 | } 386 | 387 | public float GetFontSizePx(MyScript.IInk.Graphics.Style style) 388 | { 389 | return style.FontSize; 390 | } 391 | 392 | public bool SupportsGlyphMetrics() 393 | { 394 | return true; 395 | } 396 | 397 | public GlyphMetrics[] GetGlyphMetrics(MyScript.IInk.Text.Text text, TextSpan[] spans) 398 | { 399 | var glyphMetrics = new List(); 400 | GetGlyphMetrics(text, spans, glyphMetrics); 401 | return glyphMetrics.ToArray(); 402 | } 403 | } 404 | } 405 | -------------------------------------------------------------------------------- /UIReferenceImplementation/Canvas.cs: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | using MyScript.IInk.Graphics; 4 | using System; 5 | using System.Windows; 6 | using System.Collections.Generic; 7 | 8 | 9 | namespace MyScript.IInk.UIReferenceImplementation 10 | { 11 | public class Canvas : ICanvas 12 | { 13 | private struct Group 14 | { 15 | public Rect Bound { get; } 16 | public Transform Transform { get; } 17 | 18 | public Group(Rect bound, Transform transform) 19 | { 20 | Bound = bound; 21 | Transform = transform; 22 | } 23 | }; 24 | 25 | private System.Windows.Media.DrawingContext _drawingContext = null; 26 | private ImageLoader _imageLoader; 27 | private float _pixelsPerDip; 28 | 29 | private Transform _transform; 30 | private System.Windows.Media.Pen _stroke; 31 | private System.Windows.Media.SolidColorBrush _strokeColor; 32 | private System.Windows.Media.SolidColorBrush _fillColor; 33 | private System.Windows.Media.FillRule _fillRule; 34 | private System.Windows.Media.FontFamily _fontFamily; 35 | private FontWeight _fontWeight; 36 | private FontStretch _fontStretch; 37 | private FontStyle _fontStyle; 38 | private double _fontSize; 39 | private float _dropShadow_xOffset; 40 | private float _dropShadow_yOffset; 41 | private float _dropShadow_radius; 42 | private System.Windows.Media.SolidColorBrush _dropShadow_color; 43 | 44 | private Dictionary _groups; 45 | private Group _activeGroup; 46 | 47 | public Canvas(System.Windows.Media.DrawingContext drawingContext, float pixelsPerDip, ImageLoader imageLoader) 48 | { 49 | _drawingContext = drawingContext; 50 | _pixelsPerDip = pixelsPerDip; 51 | 52 | _imageLoader = imageLoader; 53 | 54 | _transform = new Transform(); 55 | 56 | _strokeColor = new System.Windows.Media.SolidColorBrush(); 57 | _stroke = new System.Windows.Media.Pen(_strokeColor, 1); 58 | 59 | _fillColor = new System.Windows.Media.SolidColorBrush(); 60 | _fontStyle = FontStyles.Normal; 61 | _fontWeight = FontWeights.Normal; 62 | _fontStretch = FontStretches.Normal; 63 | 64 | _dropShadow_xOffset = 0.5f; 65 | _dropShadow_yOffset = 0.5f; 66 | _dropShadow_radius = 2.0f; 67 | _dropShadow_color = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromArgb(0, 0, 0, 0)); 68 | 69 | _groups = new Dictionary(); 70 | _activeGroup = new Group(Rect.Empty, new Transform()); 71 | } 72 | 73 | public Transform Transform 74 | { 75 | get 76 | { 77 | return _transform; 78 | } 79 | set 80 | { 81 | _transform = value; 82 | } 83 | } 84 | 85 | public void SetStrokeColor(Color color) 86 | { 87 | var c = System.Windows.Media.Color.FromArgb((byte)color.A, (byte)color.R, (byte)color.G, (byte)color.B); 88 | _strokeColor = new System.Windows.Media.SolidColorBrush(c); 89 | _stroke.Brush = _strokeColor; 90 | } 91 | 92 | public void SetStrokeWidth(float width) 93 | { 94 | _stroke.Thickness = width; 95 | } 96 | 97 | public void SetStrokeLineCap(LineCap lineCap) 98 | { 99 | if (lineCap == LineCap.BUTT) 100 | { 101 | _stroke.StartLineCap = System.Windows.Media.PenLineCap.Flat; 102 | _stroke.EndLineCap = System.Windows.Media.PenLineCap.Flat; 103 | _stroke.DashCap = System.Windows.Media.PenLineCap.Flat; 104 | } 105 | else if (lineCap == LineCap.ROUND) 106 | { 107 | _stroke.StartLineCap = System.Windows.Media.PenLineCap.Round; 108 | _stroke.EndLineCap = System.Windows.Media.PenLineCap.Round; 109 | _stroke.DashCap = System.Windows.Media.PenLineCap.Round; 110 | } 111 | else if (lineCap == LineCap.SQUARE) 112 | { 113 | _stroke.StartLineCap = System.Windows.Media.PenLineCap.Square; 114 | _stroke.EndLineCap = System.Windows.Media.PenLineCap.Square; 115 | _stroke.DashCap = System.Windows.Media.PenLineCap.Square; 116 | } 117 | } 118 | 119 | public void SetStrokeLineJoin(LineJoin lineJoin) 120 | { 121 | if (lineJoin == LineJoin.BEVEL) 122 | _stroke.LineJoin = System.Windows.Media.PenLineJoin.Bevel; 123 | else if (lineJoin == LineJoin.MITER) 124 | _stroke.LineJoin = System.Windows.Media.PenLineJoin.Miter; 125 | else if (lineJoin == LineJoin.ROUND) 126 | _stroke.LineJoin = System.Windows.Media.PenLineJoin.Round; 127 | } 128 | 129 | public void SetStrokeMiterLimit(float limit) 130 | { 131 | _stroke.MiterLimit = limit; 132 | } 133 | 134 | public void SetStrokeDashArray(float[] array) 135 | { 136 | if (_stroke.DashStyle.IsFrozen) 137 | { 138 | var dashStyle = _stroke.DashStyle.Clone(); 139 | _stroke.DashStyle = dashStyle; 140 | } 141 | 142 | _stroke.DashStyle.Dashes.Clear(); 143 | foreach (float f in array) 144 | _stroke.DashStyle.Dashes.Add(f); 145 | } 146 | 147 | public void SetStrokeDashOffset(float offset) 148 | { 149 | if (_stroke.DashStyle.IsFrozen) 150 | { 151 | var dashStyle = _stroke.DashStyle.Clone(); 152 | _stroke.DashStyle = dashStyle; 153 | } 154 | 155 | _stroke.DashStyle.Offset = offset; 156 | } 157 | 158 | public void SetFillColor(Color color) 159 | { 160 | _fillColor = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromArgb((byte)color.A, (byte)color.R, (byte)color.G, (byte)color.B)); 161 | } 162 | 163 | public void SetFillRule(FillRule rule) 164 | { 165 | switch (rule) 166 | { 167 | case FillRule.NONZERO: 168 | _fillRule = System.Windows.Media.FillRule.Nonzero; 169 | break; 170 | case FillRule.EVENODD: 171 | _fillRule = System.Windows.Media.FillRule.EvenOdd; 172 | break; 173 | default: 174 | break; 175 | } 176 | } 177 | 178 | public void SetDropShadow(float xOffset, float yOffset, float radius, Color color) 179 | { 180 | _dropShadow_xOffset = xOffset; 181 | _dropShadow_yOffset = yOffset; 182 | _dropShadow_radius = radius; 183 | _dropShadow_color = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromArgb((byte)color.A, (byte)color.R, (byte)color.G, (byte)color.B)); 184 | } 185 | 186 | public void SetFontProperties(string family, float lineHeight, float size, string style, string variant, int weight) 187 | { 188 | _fontFamily = new System.Windows.Media.FontFamily(FontMetricsProvider.ToPlatformFontFamily(family, style, weight)); 189 | _fontStyle = FontMetricsProvider.ToPlatformFontStyle(style); 190 | _fontWeight = FontMetricsProvider.ToPlatformFontWeight(weight); 191 | _fontStretch = FontStretches.Normal; 192 | _fontSize = size; 193 | } 194 | 195 | public void StartGroup(string id, float x, float y, float width, float height, bool clipContent) 196 | { 197 | if (clipContent) 198 | { 199 | // Save previous 200 | _groups.Add(id, _activeGroup); 201 | 202 | // Apply transform to clipping rect 203 | var rect = new Rect(x, y, width, height); 204 | var transform = new System.Windows.Media.Matrix( _transform.XX, _transform.XY, 205 | _transform.YX, _transform.YY, 206 | _transform.TX, _transform.TY ); 207 | rect = Rect.Transform(rect, transform); 208 | 209 | // Push clipping rect 210 | _activeGroup = new Group(rect, _transform); 211 | var geometry = new System.Windows.Media.RectangleGeometry(_activeGroup.Bound); 212 | _drawingContext.PushClip(geometry); 213 | } 214 | } 215 | 216 | public void EndGroup(string id) 217 | { 218 | // Restore previous (if any) 219 | if (_groups.ContainsKey(id)) 220 | { 221 | _activeGroup = _groups[id]; 222 | _groups.Remove(id); 223 | 224 | // Pop clipping rect 225 | _drawingContext.Pop(); 226 | } 227 | } 228 | 229 | public void StartItem(string id) 230 | { 231 | } 232 | 233 | public void EndItem(string id) 234 | { 235 | } 236 | 237 | private void PushRenderStates() 238 | { 239 | // Push current transform 240 | var transform = new System.Windows.Media.MatrixTransform( _transform.XX, _transform.XY, 241 | _transform.YX, _transform.YY, 242 | _transform.TX, _transform.TY ); 243 | _drawingContext.PushTransform(transform); 244 | } 245 | 246 | private void PopRenderStates() 247 | { 248 | // Pop current transform 249 | _drawingContext.Pop(); 250 | } 251 | 252 | /// Clear canvas with color according to region 253 | public void Clear(float x, float y, float width, float height, Color color) 254 | { 255 | var color_ = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromArgb((byte)color.A, (byte)color.R, (byte)color.G, (byte)color.B)); 256 | var rect = new Rect(x, y, width, height); 257 | 258 | PushRenderStates(); 259 | _drawingContext.DrawRectangle(color_, null, rect); 260 | PopRenderStates(); 261 | } 262 | 263 | /// Draw Path to canvas according path 264 | public void DrawPath(IPath path) 265 | { 266 | var p = (RenderPath)path; 267 | 268 | if (_dropShadow_color.Color.A > 0) 269 | DrawShadowPath(p, _fillColor.Color.A > 0, _strokeColor.Color.A > 0); 270 | 271 | var draw = (_fillColor.Color.A > 0) || (_strokeColor.Color.A > 0); 272 | 273 | if (draw) 274 | { 275 | PushRenderStates(); 276 | var geometry = p.FinalizeGeometry(); 277 | 278 | if (_fillColor.Color.A > 0) 279 | { 280 | geometry.FillRule = _fillRule; 281 | _drawingContext.DrawGeometry(_fillColor, null, geometry); 282 | } 283 | 284 | if (_strokeColor.Color.A > 0) 285 | { 286 | foreach (var figure in geometry.Figures) 287 | { 288 | foreach (var segment in figure.Segments) 289 | { 290 | segment.IsStroked = true; 291 | } 292 | } 293 | 294 | _drawingContext.DrawGeometry(null, _stroke.Clone(), geometry); 295 | } 296 | 297 | PopRenderStates(); 298 | } 299 | } 300 | 301 | /// Draw Rectangle to canvas according to region 302 | public void DrawRectangle(float x, float y, float width, float height) 303 | { 304 | if (_dropShadow_color.Color.A > 0) 305 | DrawShadowRect(x, y, width, height, _fillColor.Color.A > 0, _strokeColor.Color.A > 0); 306 | 307 | var draw = (_fillColor.Color.A > 0) || (_strokeColor.Color.A > 0); 308 | 309 | if (draw) 310 | { 311 | PushRenderStates(); 312 | 313 | var rect = new Rect(x, y, width, height); 314 | 315 | if (_fillColor.Color.A > 0) 316 | { 317 | _drawingContext.DrawRectangle(_fillColor, null, rect); 318 | } 319 | 320 | if (_strokeColor.Color.A > 0) 321 | { 322 | _drawingContext.DrawRectangle(null, _stroke.Clone(), rect); 323 | } 324 | 325 | PopRenderStates(); 326 | } 327 | } 328 | 329 | /// Draw Line to canvas according coordinates 330 | public void DrawLine(float x1, float y1, float x2, float y2) 331 | { 332 | if (_strokeColor.Color.A > 0) 333 | { 334 | if (_dropShadow_color.Color.A > 0) 335 | DrawShadowLine(x1, y1, x2, y2); 336 | 337 | PushRenderStates(); 338 | _drawingContext.DrawLine(_stroke.Clone(), new System.Windows.Point(x1, y1), new System.Windows.Point(x2, y2)); 339 | PopRenderStates(); 340 | } 341 | } 342 | 343 | public void DrawObject(string url, string mimeType, float x, float y, float width, float height) 344 | { 345 | if (_imageLoader == null) 346 | return; 347 | 348 | var transform = _transform; 349 | var tl = transform.Apply(x, y); 350 | var br = transform.Apply(x + width, y + height); 351 | var screenMin = new Graphics.Point(Math.Min(tl.X, br.X), Math.Min(tl.Y, br.Y)); 352 | var screenMax = new Graphics.Point(Math.Max(tl.X, br.X), Math.Max(tl.Y, br.Y)); 353 | 354 | if (_dropShadow_color.Color.A > 0) 355 | DrawShadowRect(x, y, width, height, _fillColor.Color.A > 0, _strokeColor.Color.A > 0); 356 | 357 | var image = _imageLoader.getImage(url, mimeType); 358 | 359 | if (image == null) 360 | { 361 | // image is not ready yet... 362 | if (_fillColor.Color.A > 0) 363 | { 364 | var rect = new Rect(x, y, width, height); 365 | PushRenderStates(); 366 | _drawingContext.DrawRectangle(_fillColor, null, rect); 367 | PopRenderStates(); 368 | } 369 | } 370 | else 371 | { 372 | // draw the image 373 | var rect = new Rect(x, y, width, height); 374 | PushRenderStates(); 375 | _drawingContext.DrawImage(image, rect); 376 | PopRenderStates(); 377 | } 378 | } 379 | 380 | /// Draw Text to canvas according coordinates and label 381 | public void DrawText(string label, float x, float y, float xmin, float ymin, float xmax, float ymax) 382 | { 383 | if (_fillColor.Color.A > 0) 384 | { 385 | // Create formatted text in a particular font at a particular size 386 | var typeFace = new System.Windows.Media.Typeface(_fontFamily, _fontStyle, _fontWeight, _fontStretch); 387 | var ft = new System.Windows.Media.FormattedText( 388 | label, System.Globalization.CultureInfo.CurrentCulture, 389 | FlowDirection.LeftToRight, typeFace, _fontSize, _fillColor, 390 | _pixelsPerDip 391 | ); 392 | 393 | ft.TextAlignment = TextAlignment.Left; 394 | 395 | // Draw the text 396 | var baseline = (float)ft.Baseline; 397 | var origin = new System.Windows.Point(x, y - baseline); 398 | 399 | if (_dropShadow_color.Color.A > 0) 400 | DrawShadowText(label, typeFace, origin); 401 | 402 | PushRenderStates(); 403 | _drawingContext.DrawText(ft, origin); 404 | PopRenderStates(); 405 | } 406 | } 407 | 408 | public IPath CreatePath() 409 | { 410 | return new RenderPath(); 411 | } 412 | 413 | private void DrawShadowPath(RenderPath path, bool fill, bool outline) 414 | { 415 | PushRenderStates(); 416 | 417 | var geometry = path.FinalizeGeometry().Clone(); 418 | geometry.Transform = new System.Windows.Media.TranslateTransform(_dropShadow_xOffset, _dropShadow_yOffset); 419 | 420 | if (fill) 421 | { 422 | geometry.FillRule = _fillRule; 423 | _drawingContext.DrawGeometry(_dropShadow_color, null, geometry); 424 | } 425 | 426 | if (outline) 427 | { 428 | foreach (var figure in geometry.Figures) 429 | { 430 | foreach (var segment in figure.Segments) 431 | { 432 | segment.IsStroked = true; 433 | } 434 | } 435 | var pen = _stroke.Clone(); 436 | pen.Brush = _dropShadow_color; 437 | _drawingContext.DrawGeometry(null, pen, geometry); 438 | } 439 | PopRenderStates(); 440 | } 441 | 442 | private void DrawShadowRect(float x, float y, float width, float height, bool fill, bool outline) 443 | { 444 | PushRenderStates(); 445 | 446 | var rect = new Rect(x + _dropShadow_xOffset, y + _dropShadow_yOffset, width, height); 447 | 448 | if (fill) 449 | { 450 | _drawingContext.DrawRectangle(_dropShadow_color, null, rect); 451 | } 452 | 453 | if (outline) 454 | { 455 | var pen = _stroke.Clone(); 456 | pen.Brush = _dropShadow_color; 457 | _drawingContext.DrawRectangle(null, pen, rect); 458 | } 459 | 460 | PopRenderStates(); 461 | } 462 | 463 | private void DrawShadowLine(float x1, float y1, float x2, float y2) 464 | { 465 | PushRenderStates(); 466 | 467 | var p1 = new System.Windows.Point(x1 + _dropShadow_xOffset, y1 + _dropShadow_yOffset); 468 | var p2 = new System.Windows.Point(x2 + _dropShadow_xOffset, y2 + _dropShadow_yOffset); 469 | var pen = _stroke.Clone(); 470 | pen.Brush = _dropShadow_color; 471 | _drawingContext.DrawLine(pen, p1, p2); 472 | 473 | PopRenderStates(); 474 | } 475 | 476 | private void DrawShadowText(string label, System.Windows.Media.Typeface typeFace, System.Windows.Point origin) 477 | { 478 | PushRenderStates(); 479 | 480 | // Create formatted text in a particular font at a particular size 481 | var ft = new System.Windows.Media.FormattedText( 482 | label, System.Globalization.CultureInfo.CurrentCulture, 483 | FlowDirection.LeftToRight, typeFace, _fontSize, _dropShadow_color, 484 | _pixelsPerDip 485 | ); 486 | ft.TextAlignment = TextAlignment.Left; 487 | 488 | var p = new System.Windows.Point(origin.X + _dropShadow_xOffset, origin.Y + _dropShadow_yOffset); 489 | _drawingContext.DrawText(ft, p); 490 | 491 | PopRenderStates(); 492 | } 493 | 494 | public void StartDraw(int x, int y, int width, int height) 495 | { 496 | } 497 | 498 | public void EndDraw() 499 | { 500 | } 501 | 502 | public void BlendOffscreen(uint id, float src_x, float src_y, float src_width, float src_height, float dest_x, float dest_y, float dest_width, float dest_height, Color color) 503 | { 504 | throw new NotImplementedException(); 505 | } 506 | } 507 | } 508 | --------------------------------------------------------------------------------