├── .gitattributes ├── .gitignore ├── CONTRIBUTING.md ├── Demo ├── App.xaml ├── App.xaml.cs ├── Assets │ ├── LockScreenLogo.scale-200.png │ ├── SplashScreen.scale-200.png │ ├── Square150x150Logo.scale-200.png │ ├── Square44x44Logo.scale-200.png │ ├── Square44x44Logo.targetsize-24_altform-unplated.png │ ├── StoreLogo.png │ └── Wide310x150Logo.scale-200.png ├── MainPage.xaml ├── MainPage.xaml.cs ├── MyCertificate.cs ├── MyScript.InteractiveInk.Demo.Uwp.csproj ├── MyScript.InteractiveInk.Demo.Uwp_TemporaryKey.pfx ├── Package.appxmanifest └── Properties │ ├── AssemblyInfo.cs │ └── Default.rd.xml ├── GetStarted ├── App.xaml ├── App.xaml.cs ├── Assets │ ├── LockScreenLogo.scale-200.png │ ├── SplashScreen.scale-200.png │ ├── Square150x150Logo.scale-200.png │ ├── Square44x44Logo.scale-200.png │ ├── Square44x44Logo.targetsize-24_altform-unplated.png │ ├── StoreLogo.png │ └── Wide310x150Logo.scale-200.png ├── MainPage.xaml ├── MainPage.xaml.cs ├── MyCertificate.cs ├── MyScript.InteractiveInk.GetStarted.Uwp.csproj ├── MyScript.InteractiveInk.GetStarted.Uwp_TemporaryKey.pfx ├── Package.appxmanifest └── Properties │ ├── AssemblyInfo.cs │ └── Default.rd.xml ├── LICENSES ├── inter.txt └── stix.pdf ├── MyScript.InteractiveInk.Examples.Uwp.sln ├── README.md ├── UIReferenceImplementation ├── Canvas.cs ├── Constants │ └── StyleKeys.cs ├── Converters │ └── BoolNegationConverter.cs ├── EditorListener.cs ├── Extensions │ ├── Primitives.cs │ └── SDK.cs ├── FontMetricsProvider.cs ├── ImageLoader.cs ├── ImagePainter.cs ├── Layer.cs ├── MyScript.InteractiveInk.UIReferenceImplementation.Uwp.csproj ├── Path.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── MyScript.InteractiveInk.UIReferenceImplementation.rd.xml │ └── iink_sdk_examples_uwp.UIRefImpl.rd.xml ├── RendererListener.cs └── UserControls │ ├── EditorUserControl.xaml │ ├── EditorUserControl.xaml.cs │ ├── InkToolbar.xaml │ ├── InkToolbar.xaml.cs │ ├── SmartGuideUserControl.xaml │ └── SmartGuideUserControl.xaml.cs ├── configurations ├── Raw Content │ ├── drawing.json │ └── text_math_shape.json └── interactivity.json ├── fonts ├── MyScriptInter-Bold.otf ├── MyScriptInter-Regular.otf ├── STIXTwoMath-Regular.otf └── STIXTwoText-Italic.otf └── getRecognitionAssets.ps1 /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | # git 4 | .gitignore text 5 | 6 | # source & misc files 7 | *.c text diff=cpp whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,no-newline-at-eof 8 | *.cpp text diff=cpp whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,no-newline-at-eof 9 | *.cs text diff=csharp whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 10 | *.h text diff=cpp whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,no-newline-at-eof 11 | *.hpp text diff=cpp whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,no-newline-at-eof 12 | *.html text diff=html whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 13 | *.java text diff=java whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 14 | *.js text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 15 | *.l text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 16 | *.m text diff=objc whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,no-newline-at-eof 17 | *.mm text diff=objc whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent,no-newline-at-eof 18 | *.pl text diff=perl whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 19 | *.py text diff=python whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 20 | *.rb text diff=ruby whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 21 | *.tex text diff=text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 22 | *.txt text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 23 | *.xml text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 24 | *.y text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 25 | *.tsv text whitespace=-blank-at-eol,blank-at-eof,-space-before-tab,-tab-in-indent 26 | *.groovy text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 27 | 28 | # Shell scripts 29 | *.sh diff=bash text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=lf executable=maybe 30 | _sh/* text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=lf executable=maybe 31 | *.bat text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=crlf executable=maybe 32 | *.ps1 text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=crlf executable=maybe 33 | 34 | # Python scripts 35 | *.py text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=lf executable=maybe 36 | 37 | # Awk scripts 38 | *.awk text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=lf 39 | 40 | # GNU Makefile 41 | Makefile text whitespace=blank-at-eol,blank-at-eof,space-before-tab eol=lf 42 | 43 | # CMake files 44 | CMakeLists.txt text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=lf 45 | *.cmake text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=lf 46 | 47 | # Autotools 48 | *.am text whitespace=blank-at-eol,blank-at-eof,space-before-tab eol=lf 49 | 50 | # Android 51 | *.mk text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=lf 52 | *.gradle text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent 53 | gradlew text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=lf executable 54 | gradlew.bat text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=crlf executable=maybe 55 | 56 | # Xcode files 57 | *.pbxproj text whitespace=blank-at-eol,blank-at-eof,space-before-tab eol=lf 58 | 59 | # Visual Studio files 60 | *.sln text whitespace=blank-at-eol,blank-at-eof,space-before-tab eol=crlf bomb 61 | *.vcxproj text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=crlf bomb 62 | *.vcxproj.filters text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=crlf bomb 63 | *.csproj text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=crlf bomb 64 | *.targets text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=crlf bomb 65 | *.targets.template text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=crlf bomb 66 | *.vsprops text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=crlf bomb 67 | *.props text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=crlf bomb 68 | *.xaml text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=crlf bomb 69 | *.config text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent bomb=maybe 70 | *.resx text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=crlf bomb 71 | *.settings text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent bomb=maybe 72 | *.appxmanifest text whitespace=blank-at-eol,blank-at-eof,space-before-tab,tab-in-indent eol=crlf bomb=maybe 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.jfm 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | 254 | # CodeRush 255 | .cr/ 256 | 257 | # Python Tools for Visual Studio (PTVS) 258 | __pycache__/ 259 | *.pyc 260 | 261 | # Recognition Assets 262 | recognition-assets/ 263 | INFO/ -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Demo/App.xaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | -------------------------------------------------------------------------------- /Demo/App.xaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | using System; 4 | using Windows.ApplicationModel; 5 | using Windows.ApplicationModel.Activation; 6 | using Windows.UI.Popups; 7 | using Windows.UI.Xaml; 8 | using Windows.UI.Xaml.Controls; 9 | using Windows.UI.Xaml.Navigation; 10 | 11 | namespace MyScript.IInk.Demo 12 | { 13 | sealed partial class App : Application 14 | { 15 | public static Engine Engine { get; private set; } 16 | 17 | public App() 18 | { 19 | InitializeComponent(); 20 | Suspending += OnSuspending; 21 | UnhandledException += OnUnhandledException; 22 | } 23 | 24 | private static void Initialize(Engine engine) 25 | { 26 | // Folders "conf" and "resources" are currently parts of the layout 27 | // (for each conf/res file of the project => properties => "Build Action = content") 28 | var confDirs = new string[1]; 29 | confDirs[0] = "conf"; 30 | engine.Configuration.SetStringArray("configuration-manager.search-path", confDirs); 31 | 32 | var localFolder = Windows.Storage.ApplicationData.Current.LocalFolder.Path; 33 | var tempFolder = System.IO.Path.Combine(localFolder, "tmp"); 34 | engine.Configuration.SetString("content-package.temp-folder", tempFolder); 35 | 36 | EnableStrokePrediction(engine, true, 16); 37 | 38 | // Configure multithreading for text recognition 39 | SetMaxRecognitionThreadCount(engine, 1); 40 | } 41 | 42 | private static void EnableStrokePrediction(Engine engine, bool enable, uint durationMs = 16) 43 | { 44 | engine.Configuration.SetBoolean("renderer.prediction.enable", enable); 45 | engine.Configuration.SetNumber("renderer.prediction.duration", durationMs); 46 | } 47 | 48 | private static void SetMaxRecognitionThreadCount(Engine engine, uint threadCount) 49 | { 50 | engine.Configuration.SetNumber("max-recognition-thread-count", threadCount); 51 | } 52 | 53 | private static async void OnUnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e) 54 | { 55 | e.Handled = true; 56 | await ShowErrorDialog(e.Message); 57 | } 58 | 59 | private static async System.Threading.Tasks.Task ShowErrorDialog(string message) 60 | { 61 | var dialog = new MessageDialog("Error: " + message); 62 | 63 | dialog.Commands.Add(new UICommand("Abort", delegate 64 | { 65 | Current.Exit(); 66 | })); 67 | 68 | await dialog.ShowAsync(); 69 | return false; 70 | } 71 | 72 | protected override async void OnLaunched(LaunchActivatedEventArgs e) 73 | { 74 | var rootFrame = Window.Current.Content as Frame; 75 | 76 | try 77 | { 78 | // Initialize Interactive Ink runtime environment 79 | Initialize(Engine = Engine.Create((byte[])(Array)Certificate.MyCertificate.Bytes)); 80 | } 81 | catch (Exception err) 82 | { 83 | await ShowErrorDialog(err.Message); 84 | } 85 | 86 | if (rootFrame == null) 87 | { 88 | rootFrame = new Frame(); 89 | 90 | rootFrame.NavigationFailed += OnNavigationFailed; 91 | 92 | Window.Current.Content = rootFrame; 93 | } 94 | 95 | if (!e.PrelaunchActivated) 96 | { 97 | if (rootFrame.Content == null) 98 | { 99 | rootFrame.Navigate(typeof(MainPage), e.Arguments); 100 | } 101 | 102 | Window.Current.Activate(); 103 | } 104 | } 105 | 106 | static void OnNavigationFailed(object sender, NavigationFailedEventArgs e) 107 | { 108 | throw new Exception("Failed to load Page " + e.SourcePageType.FullName); 109 | } 110 | 111 | private static void OnSuspending(object sender, SuspendingEventArgs e) 112 | { 113 | var deferral = e.SuspendingOperation.GetDeferral(); 114 | 115 | deferral.Complete(); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /Demo/Assets/LockScreenLogo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-uwp/c100f83d8914347839529b1e88dac069d7d4a26a/Demo/Assets/LockScreenLogo.scale-200.png -------------------------------------------------------------------------------- /Demo/Assets/SplashScreen.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-uwp/c100f83d8914347839529b1e88dac069d7d4a26a/Demo/Assets/SplashScreen.scale-200.png -------------------------------------------------------------------------------- /Demo/Assets/Square150x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-uwp/c100f83d8914347839529b1e88dac069d7d4a26a/Demo/Assets/Square150x150Logo.scale-200.png -------------------------------------------------------------------------------- /Demo/Assets/Square44x44Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-uwp/c100f83d8914347839529b1e88dac069d7d4a26a/Demo/Assets/Square44x44Logo.scale-200.png -------------------------------------------------------------------------------- /Demo/Assets/Square44x44Logo.targetsize-24_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-uwp/c100f83d8914347839529b1e88dac069d7d4a26a/Demo/Assets/Square44x44Logo.targetsize-24_altform-unplated.png -------------------------------------------------------------------------------- /Demo/Assets/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-uwp/c100f83d8914347839529b1e88dac069d7d4a26a/Demo/Assets/StoreLogo.png -------------------------------------------------------------------------------- /Demo/Assets/Wide310x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-uwp/c100f83d8914347839529b1e88dac069d7d4a26a/Demo/Assets/Wide310x150Logo.scale-200.png -------------------------------------------------------------------------------- /Demo/MainPage.xaml: -------------------------------------------------------------------------------- 1 |  8 | 9 | 10 | 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 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 52 | 53 | 58 | 59 | 65 | 66 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Demo/MyScript.InteractiveInk.Demo.Uwp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | obj 6 | bin 7 | 8 | 9 | 10 | Debug 11 | x64 12 | {785F77C0-DF74-4F40-AB84-8AAFCEDF2619} 13 | AppContainerExe 14 | Properties 15 | MyScript.IInk.Demo 16 | MyScript.InteractiveInk.Demo.Uwp 17 | en-US 18 | UAP 19 | 10.0.22621.0 20 | 10.0.17763.0 21 | 14 22 | 512 23 | {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 24 | true 25 | MyScript.InteractiveInk.Demo.Uwp_TemporaryKey.pfx 26 | False 27 | Never 28 | x64 29 | True 30 | 31 | 32 | 33 | 6.2.13 34 | 35 | 36 | 37 | true 38 | $(BaseOutputPath)\$(Platform)\$(Configuration)\ 39 | $(BaseIntermediateOutputPath)\$(Platform)\$(Configuration)\ 40 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 41 | ;2008 42 | full 43 | x64 44 | false 45 | prompt 46 | true 47 | 48 | 49 | $(BaseOutputPath)\$(Platform)\$(Configuration)\ 50 | $(BaseIntermediateOutputPath)\$(Platform)\$(Configuration)\ 51 | TRACE;NETFX_CORE;WINDOWS_UWP 52 | true 53 | ;2008 54 | pdbonly 55 | x64 56 | false 57 | prompt 58 | true 59 | true 60 | 61 | 62 | PackageReference 63 | 64 | 65 | 66 | App.xaml 67 | 68 | 69 | MainPage.xaml 70 | 71 | 72 | 73 | 74 | 75 | 76 | Designer 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | configurations\%(RecursiveDir)%(FileName)%(Extension) 92 | 93 | 94 | conf\diagram.conf 95 | 96 | 97 | conf\raw-content.conf 98 | 99 | 100 | conf\raw-content2.conf 101 | 102 | 103 | conf\en_US.conf 104 | 105 | 106 | conf\math.conf 107 | 108 | 109 | conf\math2.conf 110 | 111 | 112 | fonts\MyScriptInter-Bold.otf 113 | 114 | 115 | fonts\MyScriptInter-Regular.otf 116 | 117 | 118 | fonts\STIXTwoText-Italic.otf 119 | 120 | 121 | fonts\STIXTwoMath-Regular.otf 122 | 123 | 124 | resources\analyzer\ank-diagram.res 125 | 126 | 127 | resources\analyzer\ank-raw-content.res 128 | 129 | 130 | resources\document_layout\dl-raw-content.res 131 | 132 | 133 | resources\en_US\en_US-ak-cur.res 134 | 135 | 136 | resources\en_US\en_US-lk-text.res 137 | 138 | 139 | resources\math\math-ak.res 140 | 141 | 142 | resources\math\math-grm-standard.res 143 | 144 | 145 | resources\math\math-sr.res 146 | 147 | 148 | resources\shape\shk-diagram.res 149 | 150 | 151 | 152 | 153 | MSBuild:Compile 154 | Designer 155 | 156 | 157 | MSBuild:Compile 158 | Designer 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | {667892f3-1761-4ea9-bdfa-71944c768bad} 167 | MyScript.InteractiveInk.UIReferenceImplementation.Uwp 168 | 169 | 170 | 171 | 15.0 172 | 173 | 174 | 175 | powershell.exe -NonInteractive -executionpolicy unrestricted -File "$(SolutionDir)getRecognitionAssets.ps1" 176 | 177 | 184 | -------------------------------------------------------------------------------- /Demo/MyScript.InteractiveInk.Demo.Uwp_TemporaryKey.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-uwp/c100f83d8914347839529b1e88dac069d7d4a26a/Demo/MyScript.InteractiveInk.Demo.Uwp_TemporaryKey.pfx -------------------------------------------------------------------------------- /Demo/Package.appxmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | iink Demo 7 | MyScript 8 | Assets\StoreLogo.png 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Demo/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.Demo.Uwp")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("MyScript")] 12 | [assembly: AssemblyProduct("MyScript.InteractiveInk.Demo.Uwp")] 13 | [assembly: AssemblyCopyright("Copyright @ MyScript. All rights reserved.")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Version information for an assembly consists of the following four values: 18 | // 19 | // Major Version 20 | // Minor Version 21 | // Build Number 22 | // Revision 23 | // 24 | // You can specify all the values or you can default the Build and Revision Numbers 25 | // by using the '*' as shown below: 26 | // [assembly: AssemblyVersion("1.0.*")] 27 | [assembly: AssemblyVersion("4.1.0.0")] 28 | [assembly: AssemblyFileVersion("4.1.0.0")] 29 | [assembly: ComVisible(false)] 30 | -------------------------------------------------------------------------------- /Demo/Properties/Default.rd.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /GetStarted/App.xaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | -------------------------------------------------------------------------------- /GetStarted/App.xaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | using System; 4 | using Windows.ApplicationModel; 5 | using Windows.ApplicationModel.Activation; 6 | using Windows.UI.Popups; 7 | using Windows.UI.Xaml; 8 | using Windows.UI.Xaml.Controls; 9 | using Windows.UI.Xaml.Navigation; 10 | 11 | namespace MyScript.IInk.GetStarted 12 | { 13 | sealed partial class App : Application 14 | { 15 | public static Engine Engine { get; private set; } 16 | 17 | public App() 18 | { 19 | InitializeComponent(); 20 | Suspending += OnSuspending; 21 | UnhandledException += OnUnhandledException; 22 | } 23 | 24 | private static void Initialize(Engine engine) 25 | { 26 | // Folders "conf" and "resources" are currently parts of the layout 27 | // (for each conf/res file of the project => properties => "Build Action = content") 28 | var confDirs = new string[1]; 29 | confDirs[0] = "conf"; 30 | engine.Configuration.SetStringArray("configuration-manager.search-path", confDirs); 31 | 32 | var localFolder = Windows.Storage.ApplicationData.Current.LocalFolder.Path; 33 | var tempFolder = System.IO.Path.Combine(localFolder, "tmp"); 34 | engine.Configuration.SetString("content-package.temp-folder", tempFolder); 35 | } 36 | 37 | private async void OnUnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e) 38 | { 39 | e.Handled = true; 40 | await ShowErrorDialog(e.Message); 41 | } 42 | 43 | private async System.Threading.Tasks.Task ShowErrorDialog(string message) 44 | { 45 | var dialog = new MessageDialog("Error: " + message); 46 | 47 | dialog.Commands.Add(new UICommand("Abort", delegate (IUICommand command) 48 | { 49 | Current.Exit(); 50 | })); 51 | 52 | await dialog.ShowAsync(); 53 | return false; 54 | } 55 | 56 | protected override async void OnLaunched(LaunchActivatedEventArgs e) 57 | { 58 | var rootFrame = Window.Current.Content as Frame; 59 | 60 | try 61 | { 62 | // Initialize Interactive Ink runtime environment 63 | Initialize(Engine = Engine.Create((byte[])(Array)Certificate.MyCertificate.Bytes)); 64 | } 65 | catch (Exception err) 66 | { 67 | await ShowErrorDialog(err.Message); 68 | } 69 | 70 | if (rootFrame == null) 71 | { 72 | rootFrame = new Frame(); 73 | 74 | rootFrame.NavigationFailed += OnNavigationFailed; 75 | 76 | if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) 77 | { 78 | } 79 | 80 | Window.Current.Content = rootFrame; 81 | } 82 | 83 | if (e.PrelaunchActivated == false) 84 | { 85 | if (rootFrame.Content == null) 86 | { 87 | rootFrame.Navigate(typeof(MainPage), e.Arguments); 88 | } 89 | 90 | Window.Current.Activate(); 91 | } 92 | } 93 | 94 | void OnNavigationFailed(object sender, NavigationFailedEventArgs e) 95 | { 96 | throw new Exception("Failed to load Page " + e.SourcePageType.FullName); 97 | } 98 | 99 | private void OnSuspending(object sender, SuspendingEventArgs e) 100 | { 101 | var deferral = e.SuspendingOperation.GetDeferral(); 102 | deferral.Complete(); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /GetStarted/Assets/LockScreenLogo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-uwp/c100f83d8914347839529b1e88dac069d7d4a26a/GetStarted/Assets/LockScreenLogo.scale-200.png -------------------------------------------------------------------------------- /GetStarted/Assets/SplashScreen.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-uwp/c100f83d8914347839529b1e88dac069d7d4a26a/GetStarted/Assets/SplashScreen.scale-200.png -------------------------------------------------------------------------------- /GetStarted/Assets/Square150x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-uwp/c100f83d8914347839529b1e88dac069d7d4a26a/GetStarted/Assets/Square150x150Logo.scale-200.png -------------------------------------------------------------------------------- /GetStarted/Assets/Square44x44Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-uwp/c100f83d8914347839529b1e88dac069d7d4a26a/GetStarted/Assets/Square44x44Logo.scale-200.png -------------------------------------------------------------------------------- /GetStarted/Assets/Square44x44Logo.targetsize-24_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-uwp/c100f83d8914347839529b1e88dac069d7d4a26a/GetStarted/Assets/Square44x44Logo.targetsize-24_altform-unplated.png -------------------------------------------------------------------------------- /GetStarted/Assets/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-uwp/c100f83d8914347839529b1e88dac069d7d4a26a/GetStarted/Assets/StoreLogo.png -------------------------------------------------------------------------------- /GetStarted/Assets/Wide310x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-uwp/c100f83d8914347839529b1e88dac069d7d4a26a/GetStarted/Assets/Wide310x150Logo.scale-200.png -------------------------------------------------------------------------------- /GetStarted/MainPage.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 34 | 37 | 39 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /GetStarted/MainPage.xaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | using System; 4 | using System.Linq; 5 | using Windows.Graphics.Display; 6 | using Windows.UI.Xaml; 7 | using MyScript.IInk.UIReferenceImplementation; 8 | 9 | namespace MyScript.IInk.GetStarted 10 | { 11 | public sealed partial class MainPage 12 | { 13 | public static readonly DependencyProperty EditorProperty = 14 | DependencyProperty.Register("Editor", typeof(Editor), typeof(MainPage), 15 | new PropertyMetadata(default(Editor))); 16 | 17 | public Editor Editor 18 | { 19 | get => GetValue(EditorProperty) as Editor; 20 | set => SetValue(EditorProperty, value); 21 | } 22 | } 23 | 24 | public sealed partial class MainPage 25 | { 26 | // Offscreen rendering 27 | private float _dpiX = 96; 28 | private float _dpiY = 96; 29 | 30 | // Defines the type of content (possible values are: "Text Document", "Text", "Diagram", "Math", "Drawing" and "Raw Content") 31 | private const string PartType = "Text Document"; 32 | 33 | public MainPage() 34 | { 35 | InitializeComponent(); 36 | Initialize(App.Engine); 37 | } 38 | 39 | private void Initialize(Engine engine) 40 | { 41 | // Initialize the editor with the engine 42 | var info = DisplayInformation.GetForCurrentView(); 43 | _dpiX = info.RawDpiX; 44 | _dpiY = info.RawDpiY; 45 | var pixelDensity = UcEditor.GetPixelDensity(); 46 | 47 | if (pixelDensity > 0.0f) 48 | { 49 | _dpiX /= pixelDensity; 50 | _dpiY /= pixelDensity; 51 | } 52 | 53 | // RawDpi properties can return 0 when the monitor does not provide physical dimensions and when the user is 54 | // in a clone or duplicate multiple -monitor setup. 55 | if (_dpiX == 0 || _dpiY == 0) 56 | _dpiX = _dpiY = 96; 57 | 58 | var renderer = engine.CreateRenderer(_dpiX, _dpiY, UcEditor); 59 | renderer.AddListener(new RendererListener(UcEditor)); 60 | var toolController = engine.CreateToolController(); 61 | Initialize(Editor = engine.CreateEditor(renderer, toolController)); 62 | Initialize(Editor.ToolController); 63 | 64 | NewFile(); 65 | } 66 | 67 | private void Initialize(Editor editor) 68 | { 69 | editor.SetViewSize((int)ActualWidth, (int)ActualHeight); 70 | editor.SetFontMetricsProvider(new FontMetricsProvider(_dpiX, _dpiY)); 71 | editor.AddListener(new EditorListener(UcEditor)); 72 | } 73 | 74 | private static void Initialize(ToolController controller) 75 | { 76 | controller.SetToolForType(PointerType.MOUSE, PointerTool.PEN); 77 | controller.SetToolForType(PointerType.PEN, PointerTool.PEN); 78 | controller.SetToolForType(PointerType.TOUCH, PointerTool.PEN); 79 | } 80 | 81 | private void AppBar_UndoButton_Click(object sender, RoutedEventArgs e) 82 | { 83 | Editor.Undo(); 84 | } 85 | 86 | private void AppBar_RedoButton_Click(object sender, RoutedEventArgs e) 87 | { 88 | Editor.Redo(); 89 | } 90 | 91 | private void AppBar_ClearButton_Click(object sender, RoutedEventArgs e) 92 | { 93 | Editor.Clear(); 94 | } 95 | 96 | private async void AppBar_ConvertButton_Click(object sender, RoutedEventArgs e) 97 | { 98 | try 99 | { 100 | var supportedStates = Editor.GetSupportedTargetConversionStates(null); 101 | 102 | if ( (supportedStates != null) && (supportedStates.Count() > 0) ) 103 | Editor.Convert(null, supportedStates[0]); 104 | } 105 | catch (Exception ex) 106 | { 107 | var msgDialog = new Windows.UI.Popups.MessageDialog(ex.ToString()); 108 | await msgDialog.ShowAsync(); 109 | } 110 | } 111 | 112 | private void ClosePackage() 113 | { 114 | var part = Editor.Part; 115 | var package = part?.Package; 116 | Editor.Part = null; 117 | part?.Dispose(); 118 | package?.Dispose(); 119 | Title.Text = ""; 120 | } 121 | 122 | private async void NewFile() 123 | { 124 | try 125 | { 126 | // Close current package 127 | ClosePackage(); 128 | 129 | // Create package and part 130 | var packageName = MakeUntitledFilename(); 131 | var package = Editor.Engine.CreatePackage(packageName); 132 | var part = package.CreatePart(PartType); 133 | Editor.Part = part; 134 | Title.Text = "Type: " + PartType; 135 | } 136 | catch (Exception ex) 137 | { 138 | ClosePackage(); 139 | 140 | var msgDialog = new Windows.UI.Popups.MessageDialog(ex.ToString()); 141 | await msgDialog.ShowAsync(); 142 | Application.Current.Exit(); 143 | } 144 | } 145 | 146 | private static string MakeUntitledFilename() 147 | { 148 | var localFolder = Windows.Storage.ApplicationData.Current.LocalFolder.Path; 149 | var num = 0; 150 | string name; 151 | 152 | do 153 | { 154 | var baseName = "File" + (++num) + ".iink"; 155 | name = System.IO.Path.Combine(localFolder, baseName); 156 | } 157 | while (System.IO.File.Exists(name)); 158 | 159 | return name; 160 | } 161 | 162 | private void OnPenClick(object sender, RoutedEventArgs e) 163 | { 164 | if (!(Editor?.ToolController is ToolController controller)) return; 165 | controller.SetToolForType(PointerType.MOUSE, PointerTool.PEN); 166 | controller.SetToolForType(PointerType.PEN, PointerTool.PEN); 167 | controller.SetToolForType(PointerType.TOUCH, PointerTool.PEN); 168 | } 169 | 170 | private void OnTouchClick(object sender, RoutedEventArgs e) 171 | { 172 | if (!(Editor?.ToolController is ToolController controller)) return; 173 | controller.SetToolForType(PointerType.MOUSE, PointerTool.HAND); 174 | controller.SetToolForType(PointerType.PEN, PointerTool.HAND); 175 | controller.SetToolForType(PointerType.TOUCH, PointerTool.HAND); 176 | } 177 | 178 | private void OnAutoClick(object sender, RoutedEventArgs e) 179 | { 180 | if (!(Editor?.ToolController is ToolController controller)) return; 181 | controller.SetToolForType(PointerType.MOUSE, PointerTool.PEN); 182 | controller.SetToolForType(PointerType.PEN, PointerTool.HAND); 183 | controller.SetToolForType(PointerType.TOUCH, PointerTool.PEN); 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /GetStarted/MyScript.InteractiveInk.GetStarted.Uwp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | obj 6 | bin 7 | 8 | 9 | 10 | Debug 11 | x64 12 | {01436395-FD36-494A-87F9-FD0EF5B033FF} 13 | AppContainerExe 14 | Properties 15 | MyScript.IInk.GetStarted 16 | MyScript.InteractiveInk.GetStarted.Uwp 17 | en-US 18 | UAP 19 | 10.0.22621.0 20 | 10.0.17763.0 21 | 14 22 | 512 23 | {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 24 | true 25 | MyScript.InteractiveInk.GetStarted.Uwp_TemporaryKey.pfx 26 | False 27 | Never 28 | True 29 | 30 | 31 | 32 | 6.2.13 33 | 34 | 35 | 36 | true 37 | $(BaseOutputPath)\$(Platform)\$(Configuration)\ 38 | $(BaseIntermediateOutputPath)\$(Platform)\$(Configuration)\ 39 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 40 | ;2008 41 | full 42 | x64 43 | false 44 | prompt 45 | true 46 | 47 | 48 | $(BaseOutputPath)\$(Platform)\$(Configuration)\ 49 | $(BaseIntermediateOutputPath)\$(Platform)\$(Configuration)\ 50 | TRACE;NETFX_CORE;WINDOWS_UWP 51 | true 52 | ;2008 53 | pdbonly 54 | x64 55 | false 56 | prompt 57 | true 58 | true 59 | 60 | 61 | PackageReference 62 | 63 | 64 | 65 | App.xaml 66 | 67 | 68 | MainPage.xaml 69 | 70 | 71 | 72 | 73 | 74 | 75 | Designer 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | conf\diagram.conf 91 | 92 | 93 | conf\raw-content.conf 94 | 95 | 96 | conf\raw-content2.conf 97 | 98 | 99 | conf\en_US.conf 100 | 101 | 102 | conf\math.conf 103 | 104 | 105 | conf\math2.conf 106 | 107 | 108 | resources\analyzer\ank-diagram.res 109 | 110 | 111 | resources\analyzer\ank-raw-content.res 112 | 113 | 114 | resources\document_layout\dl-raw-content.res 115 | 116 | 117 | resources\en_US\en_US-ak-cur.res 118 | 119 | 120 | resources\en_US\en_US-lk-text.res 121 | 122 | 123 | resources\math\math-ak.res 124 | 125 | 126 | resources\math\math-grm-standard.res 127 | 128 | 129 | resources\math\math-sr.res 130 | 131 | 132 | resources\shape\shk-diagram.res 133 | 134 | 135 | 136 | 137 | MSBuild:Compile 138 | Designer 139 | 140 | 141 | MSBuild:Compile 142 | Designer 143 | 144 | 145 | 146 | 147 | {667892f3-1761-4ea9-bdfa-71944c768bad} 148 | MyScript.InteractiveInk.UIReferenceImplementation.Uwp 149 | 150 | 151 | 152 | 153 | 154 | 155 | 15.0 156 | 157 | 158 | 159 | powershell.exe -NonInteractive -executionpolicy unrestricted -File "$(SolutionDir)getRecognitionAssets.ps1" 160 | 161 | 168 | -------------------------------------------------------------------------------- /GetStarted/MyScript.InteractiveInk.GetStarted.Uwp_TemporaryKey.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-uwp/c100f83d8914347839529b1e88dac069d7d4a26a/GetStarted/MyScript.InteractiveInk.GetStarted.Uwp_TemporaryKey.pfx -------------------------------------------------------------------------------- /GetStarted/Package.appxmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | iink GetStarted 7 | MyScript 8 | Assets\StoreLogo.png 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /GetStarted/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.GetStarted.Uwp")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("MyScript")] 12 | [assembly: AssemblyProduct("MyScript.InteractiveInk.GetStarted.Uwp")] 13 | [assembly: AssemblyCopyright("Copyright @ MyScript. All rights reserved.")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Version information for an assembly consists of the following four values: 18 | // 19 | // Major Version 20 | // Minor Version 21 | // Build Number 22 | // Revision 23 | // 24 | // You can specify all the values or you can default the Build and Revision Numbers 25 | // by using the '*' as shown below: 26 | // [assembly: AssemblyVersion("1.0.*")] 27 | [assembly: AssemblyVersion("4.1.0.0")] 28 | [assembly: AssemblyFileVersion("4.1.0.0")] 29 | [assembly: ComVisible(false)] 30 | -------------------------------------------------------------------------------- /GetStarted/Properties/Default.rd.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /LICENSES/inter.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-2020 The Inter Project Authors. 2 | "Inter" is trademark of Rasmus Andersson. 3 | https://github.com/rsms/inter 4 | 5 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 6 | This license is copied below, and is also available with a FAQ at: 7 | http://scripts.sil.org/OFL 8 | 9 | ----------------------------------------------------------- 10 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 11 | ----------------------------------------------------------- 12 | 13 | PREAMBLE 14 | The goals of the Open Font License (OFL) are to stimulate worldwide 15 | development of collaborative font projects, to support the font creation 16 | efforts of academic and linguistic communities, and to provide a free and 17 | open framework in which fonts may be shared and improved in partnership 18 | with others. 19 | 20 | The OFL allows the licensed fonts to be used, studied, modified and 21 | redistributed freely as long as they are not sold by themselves. The 22 | fonts, including any derivative works, can be bundled, embedded, 23 | redistributed and/or sold with any software provided that any reserved 24 | names are not used by derivative works. The fonts and derivatives, 25 | however, cannot be released under any other type of license. The 26 | requirement for fonts to remain under this license does not apply 27 | to any document created using the fonts or their derivatives. 28 | 29 | DEFINITIONS 30 | "Font Software" refers to the set of files released by the Copyright 31 | Holder(s) under this license and clearly marked as such. This may 32 | include source files, build scripts and documentation. 33 | 34 | "Reserved Font Name" refers to any names specified as such after the 35 | copyright statement(s). 36 | 37 | "Original Version" refers to the collection of Font Software components as 38 | distributed by the Copyright Holder(s). 39 | 40 | "Modified Version" refers to any derivative made by adding to, deleting, 41 | or substituting -- in part or in whole -- any of the components of the 42 | Original Version, by changing formats or by porting the Font Software to a 43 | new environment. 44 | 45 | "Author" refers to any designer, engineer, programmer, technical 46 | writer or other person who contributed to the Font Software. 47 | 48 | PERMISSION AND CONDITIONS 49 | Permission is hereby granted, free of charge, to any person obtaining 50 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 51 | redistribute, and sell modified and unmodified copies of the Font 52 | Software, subject to the following conditions: 53 | 54 | 1) Neither the Font Software nor any of its individual components, 55 | in Original or Modified Versions, may be sold by itself. 56 | 57 | 2) Original or Modified Versions of the Font Software may be bundled, 58 | redistributed and/or sold with any software, provided that each copy 59 | contains the above copyright notice and this license. These can be 60 | included either as stand-alone text files, human-readable headers or 61 | in the appropriate machine-readable metadata fields within text or 62 | binary files as long as those fields can be easily viewed by the user. 63 | 64 | 3) No Modified Version of the Font Software may use the Reserved Font 65 | Name(s) unless explicit written permission is granted by the corresponding 66 | Copyright Holder. This restriction only applies to the primary font name as 67 | presented to the users. 68 | 69 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 70 | Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except to acknowledge the contribution(s) of the 72 | Copyright Holder(s) and the Author(s) or with their explicit written 73 | permission. 74 | 75 | 5) The Font Software, modified or unmodified, in part or in whole, 76 | must be distributed entirely under this license, and must not be 77 | distributed under any other license. The requirement for fonts to 78 | remain under this license does not apply to any document created 79 | using the Font Software. 80 | 81 | TERMINATION 82 | This license becomes null and void if any of the above conditions are 83 | not met. 84 | 85 | DISCLAIMER 86 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 87 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 88 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 89 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 90 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 91 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 92 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 93 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 94 | OTHER DEALINGS IN THE FONT SOFTWARE. -------------------------------------------------------------------------------- /LICENSES/stix.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-uwp/c100f83d8914347839529b1e88dac069d7d4a26a/LICENSES/stix.pdf -------------------------------------------------------------------------------- /MyScript.InteractiveInk.Examples.Uwp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 17 4 | VisualStudioVersion = 17.6.33723.286 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyScript.InteractiveInk.GetStarted.Uwp", "GetStarted\MyScript.InteractiveInk.GetStarted.Uwp.csproj", "{01436395-FD36-494A-87F9-FD0EF5B033FF}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyScript.InteractiveInk.UIReferenceImplementation.Uwp", "UIReferenceImplementation\MyScript.InteractiveInk.UIReferenceImplementation.Uwp.csproj", "{667892F3-1761-4EA9-BDFA-71944C768BAD}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyScript.InteractiveInk.Demo.Uwp", "Demo\MyScript.InteractiveInk.Demo.Uwp.csproj", "{785F77C0-DF74-4F40-AB84-8AAFCEDF2619}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|x64 = Debug|x64 15 | Release|x64 = Release|x64 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {01436395-FD36-494A-87F9-FD0EF5B033FF}.Debug|x64.ActiveCfg = Debug|x64 19 | {01436395-FD36-494A-87F9-FD0EF5B033FF}.Debug|x64.Build.0 = Debug|x64 20 | {01436395-FD36-494A-87F9-FD0EF5B033FF}.Debug|x64.Deploy.0 = Debug|x64 21 | {01436395-FD36-494A-87F9-FD0EF5B033FF}.Release|x64.ActiveCfg = Release|x64 22 | {01436395-FD36-494A-87F9-FD0EF5B033FF}.Release|x64.Build.0 = Release|x64 23 | {01436395-FD36-494A-87F9-FD0EF5B033FF}.Release|x64.Deploy.0 = Release|x64 24 | {667892F3-1761-4EA9-BDFA-71944C768BAD}.Debug|x64.ActiveCfg = Debug|x64 25 | {667892F3-1761-4EA9-BDFA-71944C768BAD}.Debug|x64.Build.0 = Debug|x64 26 | {667892F3-1761-4EA9-BDFA-71944C768BAD}.Release|x64.ActiveCfg = Release|x64 27 | {667892F3-1761-4EA9-BDFA-71944C768BAD}.Release|x64.Build.0 = Release|x64 28 | {785F77C0-DF74-4F40-AB84-8AAFCEDF2619}.Debug|x64.ActiveCfg = Debug|x64 29 | {785F77C0-DF74-4F40-AB84-8AAFCEDF2619}.Debug|x64.Build.0 = Debug|x64 30 | {785F77C0-DF74-4F40-AB84-8AAFCEDF2619}.Debug|x64.Deploy.0 = Debug|x64 31 | {785F77C0-DF74-4F40-AB84-8AAFCEDF2619}.Release|x64.ActiveCfg = Release|x64 32 | {785F77C0-DF74-4F40-AB84-8AAFCEDF2619}.Release|x64.Build.0 = Release|x64 33 | {785F77C0-DF74-4F40-AB84-8AAFCEDF2619}.Release|x64.Deploy.0 = Release|x64 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | GlobalSection(ExtensibilityGlobals) = postSolution 39 | SolutionGuid = {B3499168-F628-4339-9491-AFFC7FC82E02} 40 | EndGlobalSection 41 | EndGlobal 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## What is it about? 2 | 3 | Interactive Ink SDK is the best way to integrate handwriting recognition capabilities into your UWP 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 UWP 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 UWP platform. 8 | 9 | ## Getting started 10 | 11 | ### Installation 12 | 13 | 1. Clone the examples repository `git clone https://github.com/MyScript/interactive-ink-examples-uwp.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.Uwp.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.Uwp` nuget. Also copy `UIReferenceImplementation` directory into your project. More details available in the [developer guide](https://developer.myscript.com/docs/interactive-ink/latest/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 | -------------------------------------------------------------------------------- /UIReferenceImplementation/Canvas.cs: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | using MyScript.IInk.Graphics; 4 | using MyScript.IInk.UIReferenceImplementation.UserControls; 5 | using Microsoft.Graphics.Canvas; 6 | using Microsoft.Graphics.Canvas.Geometry; 7 | using Microsoft.Graphics.Canvas.Text; 8 | using System; 9 | using System.Numerics; 10 | using System.Collections.Generic; 11 | using Windows.Foundation; 12 | using MyScript.IInk.UIReferenceImplementation.Extensions; 13 | 14 | namespace MyScript.IInk.UIReferenceImplementation 15 | { 16 | public class Canvas : ICanvas 17 | { 18 | private CanvasDrawingSession _session; 19 | private IRenderTarget _target; 20 | private ImageLoader _imageLoader; 21 | 22 | private Transform _transform; 23 | private float _strokeWidth { get; set; } 24 | private Windows.UI.Color _strokeColor { get; set; } 25 | private CanvasStrokeStyle _strokeStyle { get; set; } 26 | private Windows.UI.Color _fillColor { get; set; } 27 | private CanvasFilledRegionDetermination _fillRule { get; set; } 28 | private CanvasTextFormat _fontProperties { get; set; } 29 | private Dictionary _layers; 30 | private float _baseline; 31 | 32 | private CanvasActiveLayer _activeLayer; 33 | private Rect _activeLayerRect; 34 | 35 | #region Privates (Drop Shadow) 36 | 37 | private float _dropShadowOffsetX = 0.5f; 38 | private float _dropShadowOffsetY = 0.5f; 39 | private float _dropShadowRadius = 2.0f; 40 | private Windows.UI.Color _dropShadowColor = default(Windows.UI.Color); 41 | 42 | #endregion 43 | 44 | public bool ClearOnStartDraw { get; set; } = true; 45 | 46 | public Canvas(CanvasDrawingSession session, IRenderTarget target, ImageLoader imageLoader) 47 | { 48 | _session = session; 49 | _target = target; 50 | _imageLoader = imageLoader; 51 | 52 | _transform = new Transform(1, 0, 0, 0, 1, 0); 53 | 54 | _strokeWidth = 1.0f; 55 | _strokeColor = Windows.UI.Colors.Transparent; 56 | 57 | _strokeStyle = new CanvasStrokeStyle 58 | { 59 | StartCap = CanvasCapStyle.Flat, 60 | EndCap = CanvasCapStyle.Flat, 61 | DashCap = CanvasCapStyle.Flat, 62 | LineJoin = CanvasLineJoin.Miter 63 | }; 64 | 65 | _fillColor = Windows.UI.Colors.Black; 66 | _fillRule = CanvasFilledRegionDetermination.Winding; 67 | 68 | _fontProperties = new CanvasTextFormat 69 | { 70 | FontStyle = Windows.UI.Text.FontStyle.Normal, 71 | FontWeight = Windows.UI.Text.FontWeights.Normal, 72 | WordWrapping = CanvasWordWrapping.NoWrap, 73 | Options = FontMetricsProvider.UseColorFont ? CanvasDrawTextOptions.EnableColorFont : CanvasDrawTextOptions.Default 74 | }; 75 | 76 | _baseline = 1.0f; 77 | 78 | _layers = new Dictionary(); 79 | _activeLayer = null; 80 | _activeLayerRect = Rect.Empty; 81 | } 82 | 83 | public void DisposeSession() 84 | { 85 | if (_session != null) 86 | { 87 | _session.Dispose(); 88 | _session = null; 89 | } 90 | } 91 | 92 | public void Begin() 93 | { 94 | _session.Antialiasing = CanvasAntialiasing.Antialiased; 95 | _session.TextAntialiasing = CanvasTextAntialiasing.Auto; 96 | 97 | var defaultStyle = new Style(); 98 | defaultStyle.SetChangeFlags((uint)StyleFlag.StyleFlag_ALL); 99 | defaultStyle.ApplyTo(this); 100 | } 101 | 102 | public void End() 103 | { 104 | } 105 | 106 | public void Clear(Color color) 107 | { 108 | var color_ = Windows.UI.Color.FromArgb((byte)color.A, (byte)color.R, (byte)color.G, (byte)color.B); 109 | _session.Clear(color_); 110 | } 111 | 112 | public Transform Transform 113 | { 114 | get { return _transform; } 115 | set 116 | { 117 | var prevTr = new Transform(_transform); 118 | _transform = value; 119 | _session.Transform = new Matrix3x2((float)Transform.XX, (float)Transform.XY, 120 | (float)Transform.YX, (float)Transform.YY, 121 | (float)Transform.TX, (float)Transform.TY); 122 | // Update clipping, if any 123 | if (_activeLayer != null) 124 | { 125 | var invTransform = new Transform(_transform); 126 | invTransform.Invert(); 127 | invTransform.Multiply(prevTr); 128 | 129 | var corners = new Graphics.Point[4]; 130 | corners[0] = invTransform.Apply((float)_activeLayerRect.Left, (float)_activeLayerRect.Top); 131 | corners[1] = invTransform.Apply((float)_activeLayerRect.Right, (float)_activeLayerRect.Top); 132 | corners[2] = invTransform.Apply((float)_activeLayerRect.Right, (float)_activeLayerRect.Bottom); 133 | corners[3] = invTransform.Apply((float)_activeLayerRect.Left, (float)_activeLayerRect.Bottom); 134 | 135 | Graphics.Point topLeft = new Graphics.Point(corners[0].X, corners[0].Y); 136 | Graphics.Point bottomRight = new Graphics.Point(corners[0].X, corners[0].Y); 137 | for (int i = 1; i < 4; i++) 138 | { 139 | if (corners[i].X < topLeft.X) 140 | topLeft.X = corners[i].X; 141 | if (corners[i].X > bottomRight.X) 142 | bottomRight.X = corners[i].X; 143 | 144 | if (corners[i].Y < topLeft.Y) 145 | topLeft.Y = corners[i].Y; 146 | if (corners[i].Y > bottomRight.Y) 147 | bottomRight.Y = corners[i].Y; 148 | } 149 | 150 | _activeLayerRect.X = topLeft.X; 151 | _activeLayerRect.Y = topLeft.Y; 152 | _activeLayerRect.Width = bottomRight.X - topLeft.X; 153 | _activeLayerRect.Height = bottomRight.Y - topLeft.Y; 154 | 155 | _activeLayer.Dispose(); 156 | _activeLayer = _session.CreateLayer(1.0f, _activeLayerRect); 157 | } 158 | } 159 | } 160 | 161 | public void SetStrokeColor(Color color) 162 | { 163 | _strokeColor = color.ToPlatform(); 164 | } 165 | 166 | public void SetStrokeWidth(float width) 167 | { 168 | _strokeWidth = width; 169 | } 170 | 171 | public void SetStrokeLineCap(LineCap lineCap) 172 | { 173 | if (lineCap == LineCap.BUTT) 174 | { 175 | _strokeStyle.StartCap = CanvasCapStyle.Flat; 176 | _strokeStyle.EndCap = CanvasCapStyle.Flat; 177 | _strokeStyle.DashCap = CanvasCapStyle.Flat; 178 | } 179 | else if (lineCap == LineCap.ROUND) 180 | { 181 | _strokeStyle.StartCap = CanvasCapStyle.Round; 182 | _strokeStyle.EndCap = CanvasCapStyle.Round; 183 | _strokeStyle.DashCap = CanvasCapStyle.Round; 184 | } 185 | else if (lineCap == LineCap.SQUARE) 186 | { 187 | _strokeStyle.StartCap = CanvasCapStyle.Square; 188 | _strokeStyle.EndCap = CanvasCapStyle.Square; 189 | _strokeStyle.DashCap = CanvasCapStyle.Square; 190 | } 191 | } 192 | 193 | public void SetStrokeLineJoin(LineJoin lineJoin) 194 | { 195 | if (lineJoin == LineJoin.BEVEL) 196 | _strokeStyle.LineJoin = CanvasLineJoin.Bevel; 197 | else if (lineJoin == LineJoin.MITER) 198 | _strokeStyle.LineJoin = CanvasLineJoin.Miter; 199 | else if (lineJoin == LineJoin.ROUND) 200 | _strokeStyle.LineJoin = CanvasLineJoin.Round; 201 | } 202 | 203 | public void SetStrokeMiterLimit(float limit) 204 | { 205 | _strokeStyle.MiterLimit = limit; 206 | } 207 | 208 | public void SetStrokeDashArray(float[] array) 209 | { 210 | _strokeStyle.CustomDashStyle = array; 211 | } 212 | 213 | public void SetStrokeDashOffset(float offset) 214 | { 215 | _strokeStyle.DashOffset = offset; 216 | } 217 | 218 | public void SetFillColor(Color color) 219 | { 220 | _fillColor = color.ToPlatform(); 221 | } 222 | 223 | public void SetFillRule(FillRule rule) 224 | { 225 | if (rule == FillRule.NONZERO) 226 | _fillRule = CanvasFilledRegionDetermination.Winding; 227 | else if (rule == FillRule.EVENODD) 228 | _fillRule = CanvasFilledRegionDetermination.Alternate; 229 | } 230 | 231 | public void SetDropShadow(float xOffset, float yOffset, float radius, Color color) 232 | { 233 | _dropShadowOffsetX = xOffset; 234 | _dropShadowOffsetY = yOffset; 235 | _dropShadowRadius = radius; 236 | _dropShadowColor = color.ToPlatform(); 237 | } 238 | 239 | public void SetFontProperties(string family, float lineHeight, float size, string style, string variant, int weight) 240 | { 241 | _fontProperties.FontFamily = FontMetricsProvider.ToPlatformFontFamily(family, style, weight); 242 | _fontProperties.FontStyle = FontMetricsProvider.ToPlatformFontStyle(style); 243 | _fontProperties.FontWeight = FontMetricsProvider.ToPlatformFontWeight(weight); 244 | _fontProperties.FontSize = size; 245 | 246 | using (var canvasTextLayout = new CanvasTextLayout(_session.Device, "k", _fontProperties, float.MaxValue, float.MaxValue)) 247 | { 248 | _baseline = canvasTextLayout.LineMetrics[0].Baseline; 249 | } 250 | } 251 | 252 | public void StartGroup(string id, float x, float y, float width, float height, bool clipContent) 253 | { 254 | if (clipContent) 255 | { 256 | _layers.Add(id, _activeLayerRect); 257 | 258 | if (_activeLayer != null) 259 | { 260 | _activeLayer.Dispose(); 261 | _activeLayer = null; 262 | _activeLayerRect = Rect.Empty; 263 | } 264 | 265 | if (_session != null) 266 | { 267 | _activeLayerRect = new Rect(x, y, width, height); 268 | _activeLayer = _session.CreateLayer(1.0f, _activeLayerRect); 269 | } 270 | } 271 | } 272 | 273 | public void EndGroup(string id) 274 | { 275 | if (_layers.ContainsKey(id)) 276 | { 277 | var rect = _layers[id]; 278 | 279 | _layers.Remove(id); 280 | 281 | if (_activeLayer != null) 282 | { 283 | _activeLayer.Dispose(); 284 | _activeLayer = null; 285 | _activeLayerRect = Rect.Empty; 286 | } 287 | 288 | if (rect.IsEmpty == false) 289 | { 290 | if (_session != null) 291 | { 292 | _activeLayerRect = rect; 293 | _activeLayer = _session.CreateLayer(1.0f, _activeLayerRect); 294 | } 295 | } 296 | } 297 | } 298 | 299 | public void StartItem(string id) 300 | { 301 | } 302 | 303 | public void EndItem(string id) 304 | { 305 | } 306 | 307 | public IPath CreatePath() 308 | { 309 | return new Path(_session.Device); 310 | } 311 | 312 | /// Draw Path to canvas according path 313 | public void DrawPath(IPath path) 314 | { 315 | var geometry = ((Path)path).CreateGeometry(); 316 | var dropShadowTranslation = Matrix3x2.CreateTranslation(_dropShadowOffsetX, _dropShadowOffsetY).Translation; 317 | 318 | if (_fillColor.A > 0) 319 | { 320 | if (_dropShadowColor.A > 0) 321 | { 322 | _session.FillGeometry(geometry, dropShadowTranslation, _dropShadowColor); 323 | } 324 | 325 | _session.FillGeometry(geometry, _fillColor); 326 | } 327 | 328 | if (_strokeColor.A > 0) 329 | { 330 | if (_dropShadowColor.A > 0) 331 | { 332 | _session.DrawGeometry(geometry, dropShadowTranslation, _dropShadowColor, _strokeWidth, _strokeStyle); 333 | } 334 | 335 | _session.DrawGeometry(geometry, _strokeColor, _strokeWidth, _strokeStyle); 336 | } 337 | } 338 | 339 | /// Draw Rectangle to canvas according to region 340 | public void DrawRectangle(float x, float y, float width, float height) 341 | { 342 | if (_fillColor.A > 0) 343 | { 344 | if (_dropShadowColor.A > 0) 345 | { 346 | _session.FillRectangle(x + _dropShadowOffsetX, y + _dropShadowOffsetY, width, height, _dropShadowColor); 347 | } 348 | 349 | _session.FillRectangle(x, y, width, height, _fillColor); 350 | } 351 | 352 | if (_strokeColor.A > 0) 353 | { 354 | if (_dropShadowColor.A > 0) 355 | { 356 | _session.DrawRectangle(x + _dropShadowOffsetX, y + _dropShadowOffsetY, width, height, _dropShadowColor, _strokeWidth, _strokeStyle); 357 | } 358 | 359 | _session.DrawRectangle(x, y, width, height, _strokeColor, _strokeWidth, _strokeStyle); 360 | } 361 | } 362 | 363 | /// Draw Line to canvas according coordinates 364 | public void DrawLine(float x1, float y1, float x2, float y2) 365 | { 366 | if (_strokeColor.A > 0) 367 | { 368 | if (_dropShadowColor.A > 0) 369 | { 370 | _session.DrawLine(x1 + _dropShadowOffsetX, y1 + _dropShadowOffsetY, x2 + _dropShadowOffsetX, y2 + _dropShadowOffsetY, _dropShadowColor, _strokeWidth, _strokeStyle); 371 | } 372 | 373 | _session.DrawLine(x1, y1, x2, y2, _strokeColor, _strokeWidth, _strokeStyle); 374 | } 375 | } 376 | 377 | public void DrawObject(string url, string mimeType, float x, float y, float width, float height) 378 | { 379 | if (_imageLoader == null) 380 | return; 381 | 382 | var transform = _transform; 383 | var screenMin = transform.Apply(x, y); 384 | var screenMax = transform.Apply(x + width, y + height); 385 | 386 | var image = _imageLoader.getImage(url, mimeType); 387 | 388 | if (image == null) 389 | { 390 | // image is not ready yet... 391 | if (_fillColor.A > 0) 392 | { 393 | _session.FillRectangle(x, y, width, height, _fillColor); 394 | } 395 | } 396 | else 397 | { 398 | // adjust rectangle so that the image gets fit into original rectangle 399 | var size = image.SizeInPixels; 400 | float fx = width / (float)size.Width; 401 | float fy = height / (float)size.Height; 402 | 403 | if (fx > fy) 404 | { 405 | float w = (float)(size.Width) * fy; 406 | x += (width - w) / 2; 407 | width = w; 408 | } 409 | else 410 | { 411 | float h = (float)(size.Height) * fx; 412 | y += (height - h) / 2; 413 | height = h; 414 | } 415 | 416 | // draw the image 417 | var rect = new Rect(x, y, width, height); 418 | _session.DrawImage(image, rect, new Rect(0, 0, size.Width, size.Height)); 419 | } 420 | } 421 | 422 | /// Draw Text to canvas according coordinates and label 423 | public void DrawText(string label, float x, float y, float xmin, float ymin, float xmax, float ymax) 424 | { 425 | if (_fillColor.A > 0) 426 | { 427 | if (_dropShadowColor.A > 0) 428 | { 429 | _session.DrawText(label, x + _dropShadowOffsetX, y + _dropShadowOffsetY - _baseline, _dropShadowColor, _fontProperties); 430 | } 431 | 432 | _session.DrawText(label, x, y - _baseline, _fillColor, _fontProperties); 433 | } 434 | } 435 | 436 | 437 | public void StartDraw(int x, int y, int width, int height) 438 | { 439 | Begin(); 440 | 441 | if (ClearOnStartDraw) 442 | { 443 | EditorUserControl editor = _target as EditorUserControl; 444 | if (editor?.SupportsOffscreenRendering() ?? false) 445 | { 446 | var drawingLayerRect = new Rect(x, y, width, height); 447 | using (var drawingLayer = _session.CreateLayer(1.0f, drawingLayerRect)) 448 | { 449 | var color = Windows.UI.Color.FromArgb((byte)0, (byte)0, (byte)0, (byte)0); 450 | _session.Clear(color); 451 | } 452 | } 453 | } 454 | } 455 | 456 | public void EndDraw() 457 | { 458 | End(); 459 | } 460 | 461 | public void BlendOffscreen(UInt32 id, 462 | float src_x, float src_y, float src_width, float src_height, 463 | float dest_x, float dest_y, float dest_width, float dest_height, 464 | Color color) 465 | { 466 | EditorUserControl editor = _target as EditorUserControl; 467 | if (editor?.SupportsOffscreenRendering() ?? false) 468 | { 469 | CanvasRenderTarget bitmap = editor.GetImage(id); 470 | 471 | Rect srcRect = new Rect( src_x, src_y, src_width, src_height); 472 | Rect dstRect = new Rect(dest_x, dest_y, dest_width, dest_height); 473 | 474 | _session.DrawImage(bitmap, dstRect, srcRect, color.A_f); 475 | } 476 | } 477 | } 478 | } 479 | -------------------------------------------------------------------------------- /UIReferenceImplementation/Constants/StyleKeys.cs: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | namespace MyScript.IInk.UIReferenceImplementation.Constants 4 | { 5 | public static class StyleKeys 6 | { 7 | public const string Color = "color"; 8 | public const string MyScriptPenWidth = "-myscript-pen-width"; 9 | } 10 | } -------------------------------------------------------------------------------- /UIReferenceImplementation/Converters/BoolNegationConverter.cs: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | using System; 4 | using Windows.UI.Xaml.Data; 5 | 6 | namespace MyScript.IInk.UIReferenceImplementation.Converters 7 | { 8 | public class BoolNegationConverter : IValueConverter 9 | { 10 | public object Convert(object value, Type targetType, object parameter, string language) 11 | { 12 | return value is bool b && !b; 13 | } 14 | 15 | public object ConvertBack(object value, Type targetType, object parameter, string language) 16 | { 17 | throw new NotImplementedException(); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /UIReferenceImplementation/EditorListener.cs: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | using Windows.UI.Core; 4 | using Windows.UI.Popups; 5 | using MyScript.IInk.UIReferenceImplementation.UserControls; 6 | 7 | namespace MyScript.IInk.UIReferenceImplementation 8 | { 9 | public class EditorListener : IEditorListener 10 | { 11 | private EditorUserControl _ucEditor; 12 | 13 | public EditorListener(EditorUserControl ucEditor) 14 | { 15 | _ucEditor = ucEditor; 16 | } 17 | 18 | public void PartChanged(Editor editor) 19 | { 20 | if (_ucEditor.SmartGuideEnabled && _ucEditor.SmartGuide != null) 21 | { 22 | var dispatcher = _ucEditor.Dispatcher; 23 | var task = dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { _ucEditor.SmartGuide.OnPartChanged(); }); 24 | } 25 | } 26 | 27 | public void ContentChanged(Editor editor, string[] blockIds) 28 | { 29 | if (_ucEditor.SmartGuideEnabled && _ucEditor.SmartGuide != null) 30 | { 31 | var dispatcher = _ucEditor.Dispatcher; 32 | var task = dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { _ucEditor.SmartGuide.OnContentChanged(blockIds); }); 33 | } 34 | } 35 | 36 | public void SelectionChanged(Editor editor) 37 | { 38 | if (_ucEditor.SmartGuideEnabled && _ucEditor.SmartGuide != null) 39 | { 40 | using (var selection = editor.GetSelection()) 41 | { 42 | var mode = editor.GetSelectionMode(); 43 | var blockIds = editor.GetIntersectingBlocks(selection); 44 | var dispatcher = _ucEditor.Dispatcher; 45 | var task = dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { _ucEditor.SmartGuide.OnSelectionChanged(blockIds, mode); }); 46 | } 47 | } 48 | } 49 | 50 | public void ActiveBlockChanged(Editor editor, string blockId) 51 | { 52 | if (_ucEditor.SmartGuideEnabled && _ucEditor.SmartGuide != null) 53 | { 54 | var dispatcher = _ucEditor.Dispatcher; 55 | var task = dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { _ucEditor.SmartGuide.OnActiveBlockChanged(blockId); }); 56 | } 57 | } 58 | 59 | public void OnError(Editor editor, string blockId, EditorError error, string message) 60 | { 61 | var dispatcher = _ucEditor.Dispatcher; 62 | var task = dispatcher.RunAsync(CoreDispatcherPriority.Normal, 63 | () => 64 | { 65 | var dlg = new MessageDialog(message); 66 | var dlgTask = dlg.ShowAsync(); 67 | }); 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /UIReferenceImplementation/Extensions/Primitives.cs: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | namespace MyScript.IInk.UIReferenceImplementation.Extensions 4 | { 5 | public static class Primitives 6 | { 7 | private const float MillimetersPerInch = 25.4f; 8 | 9 | public static float FromPixelToMillimeter(this double source, float dpi) 10 | { 11 | return ((float)source).FromPixelToMillimeter(dpi); 12 | } 13 | 14 | public static float FromMillimeterToPixel(this float source, float dpi) 15 | { 16 | // DPI: dots or pixels per inch 17 | // => dpi = pixels / inch 18 | // => pixels = dpi * inch 19 | var inch = source / MillimetersPerInch; 20 | return dpi * inch; 21 | } 22 | 23 | public static float FromPixelToMillimeter(this float source, float dpi) 24 | { 25 | // DPI: dots or pixels per inch 26 | // => dpi = pixels / inch 27 | // => inch = pixels / dpi 28 | var inch = source / dpi; 29 | return inch * MillimetersPerInch; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /UIReferenceImplementation/Extensions/SDK.cs: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | using System; 4 | using System.Diagnostics; 5 | using Windows.Devices.Input; 6 | using Windows.UI.Input.Inking; 7 | using MyScript.IInk.Graphics; 8 | using MyScript.IInk.UIReferenceImplementation.Constants; 9 | 10 | namespace MyScript.IInk.UIReferenceImplementation.Extensions 11 | { 12 | public static class SDK 13 | { 14 | #region Editor 15 | 16 | public static void Apply(this Editor source, InkDrawingAttributes attributes) 17 | { 18 | if (!(source.ToolController is ToolController controller)) return; 19 | var dpi = source.Renderer?.DpiX ?? 96; 20 | var color = attributes.Color.ToNative().ToHex(); 21 | var strokeWidth = attributes.DrawAsHighlighter ? attributes.Size.Height : attributes.Size.Width; 22 | var css = 23 | $"{StyleKeys.Color}: {color}; " + 24 | $"{StyleKeys.MyScriptPenWidth}: {strokeWidth.FromPixelToMillimeter(dpi)}"; 25 | var pointerTool = attributes.DrawAsHighlighter ? PointerTool.HIGHLIGHTER : PointerTool.PEN; 26 | controller.SetToolStyle(pointerTool, css); 27 | } 28 | 29 | #endregion 30 | 31 | #region Pointer Type 32 | 33 | public static PointerType ToNative(this PointerDeviceType source) 34 | { 35 | switch (source) 36 | { 37 | case PointerDeviceType.Touch: 38 | return PointerType.TOUCH; 39 | case PointerDeviceType.Pen: 40 | return PointerType.PEN; 41 | case PointerDeviceType.Mouse: 42 | return PointerType.MOUSE; 43 | default: 44 | throw new ArgumentOutOfRangeException(nameof(source), source, null); 45 | } 46 | } 47 | 48 | #endregion 49 | 50 | #region Colors 51 | 52 | public static string ToHex(this Color source) 53 | { 54 | return $"#{source.R:X2}{source.G:X2}{source.B:X2}{source.A:X2}"; 55 | } 56 | 57 | public static Color ToNative(this Windows.UI.Color source) 58 | { 59 | return new Color(source.R, source.G, source.B, source.A); 60 | } 61 | 62 | public static Windows.UI.Color ToPlatform(this Color source) 63 | { 64 | return new Windows.UI.Color 65 | { A = (byte)source.A, B = (byte)source.B, G = (byte)source.G, R = (byte)source.R }; 66 | } 67 | 68 | #endregion 69 | } 70 | } -------------------------------------------------------------------------------- /UIReferenceImplementation/FontMetricsProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | using Microsoft.Graphics.Canvas; 4 | using Microsoft.Graphics.Canvas.Text; 5 | using MyScript.IInk.Graphics; 6 | using MyScript.IInk.Text; 7 | using System.Collections.Generic; 8 | using System.Globalization; 9 | using System.Linq; 10 | 11 | namespace MyScript.IInk.UIReferenceImplementation 12 | { 13 | internal static class FontFamilies 14 | { 15 | public static string MyScriptInter { get { return _myScriptInter ?? _defaultFamily; } private set { _myScriptInter = value; } } 16 | public static string MyScriptInterBold { get { return _myScriptInterBold ?? _defaultFamily; } private set { _myScriptInterBold = value; } } 17 | public static string StixRegular { get { return _stixRegular ?? _defaultStixFamily; } private set { _stixRegular = value; } } 18 | public static string StixItalic { get { return _stixItalic ?? _defaultStixFamily; } private set { _stixItalic = value; } } 19 | 20 | private const string _defaultFamily = "Segoe UI"; 21 | private const string _defaultStixFamily = "STIX"; 22 | 23 | private const string _fontFolder = "fonts"; 24 | 25 | private static bool _initialized = false; 26 | private static string _myScriptInter; 27 | private static string _myScriptInterBold; 28 | private static string _stixRegular; 29 | private static string _stixItalic; 30 | 31 | private static string RegisterFontFamily(string filename, string name, string defaultFamily) 32 | { 33 | var localPath = System.IO.Path.Combine(_fontFolder, filename); 34 | var fullPath = System.IO.Path.Combine(Windows.ApplicationModel.Package.Current.InstalledLocation.Path, localPath); 35 | 36 | if (System.IO.File.Exists(fullPath)) 37 | return "ms-appx:///" + localPath + "#" + name; 38 | 39 | return defaultFamily; 40 | } 41 | 42 | public static void Initialize() 43 | { 44 | if (_initialized) 45 | return; 46 | 47 | MyScriptInter = RegisterFontFamily("MyScriptInter-Regular.otf", "MyScriptInter", _defaultFamily); 48 | MyScriptInterBold = RegisterFontFamily("MyScriptInter-Bold.otf", "MyScriptInter", _defaultFamily); 49 | StixRegular = RegisterFontFamily("STIXTwoMath-Regular.otf", "STIX Two Math", _defaultStixFamily); 50 | StixItalic = RegisterFontFamily("STIXTwoText-Italic.otf", "STIX Two Text", _defaultStixFamily); 51 | 52 | _initialized = true; 53 | } 54 | } 55 | 56 | public class FontMetricsProvider : IFontMetricsProvider 57 | { 58 | public const bool UseColorFont = true; 59 | 60 | private float _dpiX; 61 | private float _dpiY; 62 | 63 | public static void Initialize() 64 | { 65 | FontFamilies.Initialize(); 66 | } 67 | 68 | public FontMetricsProvider(float dpiX, float dpiY) 69 | { 70 | _dpiX = dpiX; 71 | _dpiY = dpiY; 72 | } 73 | 74 | public static string ToPlatformFontFamily(string family, string style, int weight) 75 | { 76 | if (family == "MyScriptInter") 77 | return ToPlatformFontWeight(weight).Equals(Windows.UI.Text.FontWeights.Bold) ? FontFamilies.MyScriptInterBold : FontFamilies.MyScriptInter; 78 | else if (family == "STIX") 79 | return (ToPlatformFontStyle(style) == Windows.UI.Text.FontStyle.Italic) ? FontFamilies.StixItalic : FontFamilies.StixRegular; 80 | 81 | return family; 82 | } 83 | 84 | public static Windows.UI.Text.FontWeight ToPlatformFontWeight(int weight) 85 | { 86 | var fontWeight = Windows.UI.Text.FontWeights.Normal; 87 | 88 | if (weight >= 700) 89 | fontWeight = Windows.UI.Text.FontWeights.Bold; 90 | else if (weight >= 400) 91 | fontWeight = Windows.UI.Text.FontWeights.Normal; 92 | else 93 | fontWeight = Windows.UI.Text.FontWeights.Light; 94 | 95 | return fontWeight; 96 | } 97 | 98 | public static Windows.UI.Text.FontStyle ToPlatformFontStyle(string style) 99 | { 100 | var fontStyle = Windows.UI.Text.FontStyle.Normal; 101 | 102 | if (style.Equals("italic")) 103 | fontStyle = Windows.UI.Text.FontStyle.Italic; 104 | else if (style.Equals("oblique")) 105 | fontStyle = Windows.UI.Text.FontStyle.Oblique; 106 | 107 | return fontStyle; 108 | } 109 | 110 | private static float px2mm(float px, float dpi) 111 | { 112 | return 25.4f * (px / dpi); 113 | } 114 | 115 | private static float mm2px(float mmm, float dpi) 116 | { 117 | return (mmm / 25.4f) * dpi; 118 | } 119 | 120 | private class FontKey 121 | { 122 | public string FontFamily { get; } 123 | public float FontSize { get; } 124 | public Windows.UI.Text.FontWeight FontWeight { get; } 125 | public Windows.UI.Text.FontStyle FontStyle { get; } 126 | 127 | public FontKey(string fontFamily, float fontSize, Windows.UI.Text.FontWeight fontWeight, Windows.UI.Text.FontStyle fontStyle) 128 | { 129 | this.FontFamily = fontFamily; 130 | this.FontSize = fontSize; 131 | this.FontWeight = fontWeight; 132 | this.FontStyle = fontStyle; 133 | } 134 | 135 | public override bool Equals(object obj) 136 | { 137 | if (obj.GetType() != this.GetType()) 138 | return false; 139 | 140 | FontKey other = (FontKey)obj; 141 | return this.FontFamily == other.FontFamily && this.FontSize == other.FontSize && this.FontWeight.Weight == other.FontWeight.Weight && this.FontStyle == other.FontStyle; 142 | } 143 | 144 | public override int GetHashCode() 145 | { 146 | return FontFamily.GetHashCode() ^ FontSize.GetHashCode() ^ FontWeight.Weight.GetHashCode() ^ FontStyle.GetHashCode(); 147 | } 148 | } 149 | private Dictionary> cache = new Dictionary>(); 150 | 151 | private FontKey FontKeyFromStyle(Style style) 152 | { 153 | var fontFamily = ToPlatformFontFamily(style.FontFamily, style.FontStyle, style.FontWeight); 154 | var fontSize = mm2px(style.FontSize, _dpiY); 155 | var fontWeight = ToPlatformFontWeight(style.FontWeight); 156 | var fontStyle = ToPlatformFontStyle(style.FontStyle); 157 | return new FontKey(fontFamily, fontSize, fontWeight, fontStyle); 158 | } 159 | 160 | private GlyphMetrics GetGlyphMetrics(FontKey fontKey, string glyphLabel, CanvasDevice canvasDevice) 161 | { 162 | Dictionary fontCache = null; 163 | if (!cache.TryGetValue(fontKey, out fontCache)) 164 | { 165 | fontCache = new Dictionary(); 166 | cache[fontKey] = fontCache; 167 | } 168 | 169 | GlyphMetrics value = null; 170 | if (!fontCache.TryGetValue(glyphLabel, out value)) 171 | { 172 | var textFormat = new CanvasTextFormat() 173 | { 174 | FontSize = fontKey.FontSize, 175 | FontFamily = fontKey.FontFamily, 176 | FontStyle = fontKey.FontStyle, 177 | FontWeight = fontKey.FontWeight, 178 | WordWrapping = CanvasWordWrapping.NoWrap, 179 | Options = UseColorFont ? CanvasDrawTextOptions.EnableColorFont : CanvasDrawTextOptions.Default 180 | }; 181 | 182 | using (var canvasCharLayout = new CanvasTextLayout(canvasDevice, glyphLabel, textFormat, 0.0f, 0.0f)) 183 | { 184 | int charCount = 0; 185 | if (canvasCharLayout.ClusterMetrics != null) 186 | { 187 | foreach (var c in canvasCharLayout.ClusterMetrics) 188 | charCount += c.CharacterCount; 189 | } 190 | 191 | var rect = canvasCharLayout.DrawBounds; 192 | var left = (float)rect.Left; 193 | var top = (float)rect.Top - canvasCharLayout.LineMetrics[0].Baseline; 194 | var leftBearing = (float)(-rect.Left); 195 | var height = (float)rect.Height; 196 | 197 | var charEnd = (charCount > 0) ? (charCount - 1) : 0; 198 | var advance = canvasCharLayout.GetCaretPosition(charEnd, true); 199 | var width = (float)rect.Width; 200 | var right = (float)rect.Right; 201 | var rightBearing = (float)advance.X - right; 202 | 203 | var glyphX = px2mm(left, _dpiX); 204 | var glyphY = px2mm(top, _dpiY); 205 | var glyphW = px2mm(width, _dpiX); 206 | var glyphH = px2mm(height, _dpiY); 207 | var glyphRect = new Rectangle(glyphX, glyphY, glyphW, glyphH); 208 | var glyphLeftBearing = px2mm(leftBearing, _dpiX); 209 | var glyphRightBearing = px2mm(rightBearing, _dpiX); 210 | 211 | value = new GlyphMetrics(glyphRect, glyphLeftBearing, glyphRightBearing); 212 | fontCache[glyphLabel] = value; 213 | } 214 | } 215 | 216 | return new GlyphMetrics(new Rectangle(value.BoundingBox.X, value.BoundingBox.Y, value.BoundingBox.Width, value.BoundingBox.Height), value.LeftSideBearing, value.RightSideBearing); 217 | } 218 | 219 | public Rectangle[] GetCharacterBoundingBoxes(MyScript.IInk.Text.Text text, TextSpan[] spans) 220 | { 221 | var glyphMetrics = GetGlyphMetrics(text, spans); 222 | 223 | Rectangle[] rectangles = new Rectangle[glyphMetrics.Length]; 224 | for (int i = 0; i < glyphMetrics.Length; ++i) 225 | rectangles[i] = glyphMetrics[i].BoundingBox; 226 | 227 | return rectangles; 228 | } 229 | 230 | public float GetFontSizePx(Style style) 231 | { 232 | return style.FontSize; 233 | } 234 | 235 | public bool SupportsGlyphMetrics() 236 | { 237 | return true; 238 | } 239 | 240 | public GlyphMetrics[] GetGlyphMetrics(MyScript.IInk.Text.Text text, TextSpan[] spans) 241 | { 242 | CanvasDevice canvasDevice = CanvasDevice.GetSharedDevice(); 243 | 244 | GlyphMetrics[] glyphMetrics = new GlyphMetrics[text.GlyphCount]; 245 | 246 | var firstStyle = spans[0].Style; 247 | var firstFontKey = FontKeyFromStyle(firstStyle); 248 | 249 | if (text.GlyphCount == 1) 250 | { 251 | glyphMetrics[0] = GetGlyphMetrics(firstFontKey, text.Label, canvasDevice); 252 | } 253 | else 254 | { 255 | var textFormat = new CanvasTextFormat() 256 | { 257 | FontSize = firstFontKey.FontSize, 258 | FontFamily = firstFontKey.FontFamily, 259 | FontStyle = firstFontKey.FontStyle, 260 | FontWeight = firstFontKey.FontWeight, 261 | WordWrapping = CanvasWordWrapping.NoWrap, 262 | Options = UseColorFont ? CanvasDrawTextOptions.EnableColorFont : CanvasDrawTextOptions.Default 263 | }; 264 | 265 | using (var canvasTextLayout = new CanvasTextLayout(canvasDevice, text.Label, textFormat, 0.0f, 0.0f)) 266 | { 267 | for (int i = 0; i < spans.Length; ++i) 268 | { 269 | var charIndex = text.GetGlyphBeginAt(spans[i].BeginPosition); 270 | var charCount = text.GetGlyphEndAt(spans[i].EndPosition - 1) - charIndex; 271 | 272 | var style = spans[i].Style; 273 | var fontKey = FontKeyFromStyle(style); 274 | 275 | canvasTextLayout.SetFontFamily(charIndex, charCount, fontKey.FontFamily); 276 | canvasTextLayout.SetFontSize(charIndex, charCount, fontKey.FontSize); 277 | canvasTextLayout.SetFontWeight(charIndex, charCount, fontKey.FontWeight); 278 | canvasTextLayout.SetFontStyle(charIndex, charCount, fontKey.FontStyle); 279 | } 280 | 281 | for (int i = 0; i < text.GlyphCount; ++i) 282 | { 283 | var glyphLabel = text.GetGlyphLabelAt(i); 284 | var glyphCharStart = text.GetGlyphBeginAt(i); 285 | var glyphCharEnd = text.GetGlyphEndAt(i); 286 | 287 | var glyphFontKey = new FontKey ( canvasTextLayout.GetFontFamily(glyphCharStart) 288 | , canvasTextLayout.GetFontSize(glyphCharStart) 289 | , canvasTextLayout.GetFontWeight(glyphCharStart) 290 | , canvasTextLayout.GetFontStyle(glyphCharStart)); 291 | 292 | var glyphMetrics_ = GetGlyphMetrics(glyphFontKey, glyphLabel, canvasDevice); 293 | 294 | // Find cluster associated with element 295 | // (Use of ClusterMetrics to identify ligatures in the CanvasTextLayout) 296 | int cluster = -1; 297 | int clusterCharStart = 0; 298 | 299 | if (canvasTextLayout.ClusterMetrics != null) 300 | { 301 | for (int c = 0; c < canvasTextLayout.ClusterMetrics.Length; ++c) 302 | { 303 | var clusterCharCount = canvasTextLayout.ClusterMetrics[c].CharacterCount; 304 | if ((glyphCharStart >= clusterCharStart) && (glyphCharStart < (clusterCharStart + clusterCharCount))) 305 | { 306 | cluster = c; 307 | break; 308 | } 309 | 310 | clusterCharStart += clusterCharCount; 311 | } 312 | } 313 | 314 | if ( (i > 0) && (cluster >= 0) && (glyphCharStart > clusterCharStart) ) 315 | { 316 | // Ligature with the previous glyph 317 | // The position is not accurate because of glyphs substitution at rendering 318 | // but it makes the illusion. 319 | var prevGlyphMetrics = glyphMetrics[i-1]; 320 | glyphMetrics_.BoundingBox.X = prevGlyphMetrics.BoundingBox.X 321 | + prevGlyphMetrics.BoundingBox.Width 322 | + prevGlyphMetrics.RightSideBearing 323 | + glyphMetrics_.LeftSideBearing; 324 | } 325 | else 326 | { 327 | float glyphX = 0.0f; 328 | var charRegions = canvasTextLayout.GetCharacterRegions(glyphCharStart, glyphCharEnd - glyphCharStart); 329 | 330 | if ((charRegions != null) && (charRegions.Length > 0)) 331 | { 332 | glyphX = (float)charRegions[0].LayoutBounds.X; 333 | } 334 | else 335 | { 336 | var glyphPos = canvasTextLayout.GetCaretPosition(glyphCharStart, false); 337 | glyphX = (float)glyphPos.X; 338 | } 339 | 340 | glyphMetrics_.BoundingBox.X += px2mm(glyphX, _dpiX); 341 | } 342 | 343 | glyphMetrics[i] = glyphMetrics_; 344 | } 345 | } 346 | } 347 | 348 | return glyphMetrics; 349 | } 350 | }; 351 | } 352 | -------------------------------------------------------------------------------- /UIReferenceImplementation/ImageLoader.cs: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | using Microsoft.Graphics.Canvas; 4 | using System; 5 | using Windows.UI; 6 | using System.Collections.Generic; 7 | 8 | namespace MyScript.IInk.UIReferenceImplementation 9 | { 10 | // LruImgCache 11 | public class LruImgCache 12 | { 13 | private LinkedList _lru = new LinkedList(); 14 | private Dictionary _cache = new Dictionary(); 15 | private int _maxBytes; 16 | private int _curBytes; 17 | 18 | // ImageNode 19 | public struct ImageNode 20 | { 21 | public ImageNode(CanvasBitmap image, int cost) 22 | { 23 | Image = image; 24 | Cost = cost; 25 | } 26 | 27 | public CanvasBitmap Image { get; } 28 | public int Cost { get; } 29 | } 30 | 31 | public LruImgCache(int maxBytes) 32 | { 33 | _maxBytes = maxBytes; 34 | _curBytes = 0; 35 | } 36 | 37 | public bool containsBitmap(string url) 38 | { 39 | return _cache.ContainsKey(url); 40 | } 41 | 42 | public CanvasBitmap getBitmap(string url) 43 | { 44 | // Update LRU 45 | _lru.Remove(url); 46 | _lru.AddFirst(url); 47 | 48 | return _cache[url].Image; 49 | } 50 | 51 | public void putBitmap(string url, Editor editor) 52 | { 53 | CanvasBitmap image = loadBitmap(url, editor); 54 | var imageBytes = image.GetPixelBytes().Length; 55 | 56 | // Too big for cache 57 | if (imageBytes > _maxBytes) 58 | { 59 | // Use fallback (cache it to avoid reloading it each time for size check) 60 | image = createFallbackBitmap(); 61 | imageBytes = 4; 62 | } 63 | 64 | // Remove LRUs if max size reached 65 | while (_curBytes + imageBytes > _maxBytes) 66 | { 67 | string lruKey = _lru.Last.Value; 68 | ImageNode lruNode = _cache[lruKey]; 69 | _curBytes -= lruNode.Cost; 70 | _cache.Remove(lruKey); 71 | _lru.RemoveLast(); 72 | } 73 | 74 | // Add to cache 75 | _cache.Add(url, new ImageNode(image, imageBytes)); 76 | _curBytes += imageBytes; 77 | _lru.AddFirst(url); 78 | } 79 | 80 | private CanvasBitmap loadBitmap(string url, Editor editor) 81 | { 82 | var dpi = Math.Max(editor.Renderer.DpiX, editor.Renderer.DpiY); 83 | var path = System.IO.Path.GetFullPath(url); 84 | 85 | try 86 | { 87 | CanvasBitmap image = null; 88 | var task = System.Threading.Tasks.Task.Run(async () 89 | => { image = await CanvasBitmap.LoadAsync(CanvasDevice.GetSharedDevice(), path, dpi); }); 90 | if (task != null) 91 | System.Threading.Tasks.Task.WaitAll(task); 92 | 93 | if (image != null) 94 | return image; 95 | } 96 | catch 97 | { 98 | // Error: use fallback bitmap 99 | } 100 | 101 | // Fallback 102 | return createFallbackBitmap(); 103 | } 104 | 105 | public static CanvasBitmap createFallbackBitmap() 106 | { 107 | // Fallback 1x1 bitmap 108 | var dpi = 96; 109 | var image = new CanvasRenderTarget(CanvasDevice.GetSharedDevice(), 1, 1, dpi); 110 | Color[] color = new Color[] { Color.FromArgb(255, 255, 255, 255) }; 111 | image.SetPixelColors(color, 0, 0, 1, 1); 112 | 113 | return image; 114 | } 115 | } 116 | 117 | // ImageLoader 118 | public class ImageLoader 119 | { 120 | private Editor _editor; 121 | private LruImgCache _cache; 122 | private const int CACHE_MAX_BYTES = 200 * 1000000; // 200M (in Bytes) 123 | 124 | public Editor Editor 125 | { 126 | get 127 | { 128 | return _editor; 129 | } 130 | } 131 | 132 | public ImageLoader(Editor editor, string cacheDirectory) 133 | { 134 | _editor = editor; 135 | _cache = new LruImgCache(CACHE_MAX_BYTES); 136 | } 137 | 138 | public CanvasBitmap getImage(string url, string mimeType) 139 | { 140 | CanvasBitmap image = null; 141 | 142 | lock (_cache) 143 | { 144 | if (!_cache.containsBitmap(url)) 145 | _cache.putBitmap(url, _editor); 146 | image = _cache.getBitmap(url); 147 | } 148 | 149 | if (image == null) 150 | image = LruImgCache.createFallbackBitmap(); 151 | 152 | return image; 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /UIReferenceImplementation/ImagePainter.cs: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | using Microsoft.Graphics.Canvas; 4 | using MyScript.IInk.Graphics; 5 | using System; 6 | 7 | namespace MyScript.IInk.UIReferenceImplementation 8 | { 9 | public class ImagePainter : IImagePainter 10 | { 11 | private static Color _defaultBackgroundColor = new Color(0xffffffff); 12 | 13 | private CanvasRenderTarget _image; 14 | private CanvasDrawingSession _session; 15 | private Canvas _canvas; 16 | 17 | public ImageLoader ImageLoader { get; set; } 18 | public Graphics.Color BackgroundColor { get; set; } 19 | 20 | public ImagePainter() 21 | { 22 | BackgroundColor = _defaultBackgroundColor; 23 | } 24 | 25 | public void PrepareImage(int width, int height, float dpi) 26 | { 27 | if (_image != null) 28 | _image = null; 29 | 30 | // Use 96 dpi to match the default DIP unit used by UWP 31 | _image = new CanvasRenderTarget(CanvasDevice.GetSharedDevice(), width, height, 96); 32 | _session = _image.CreateDrawingSession(); 33 | 34 | var color = (BackgroundColor != null) ? BackgroundColor : _defaultBackgroundColor; 35 | _canvas = new Canvas(_session, null, ImageLoader); 36 | _canvas.Begin(); 37 | _canvas.Clear(color); 38 | } 39 | 40 | public void SaveImage(string path) 41 | { 42 | if ((_image != null) && !string.IsNullOrWhiteSpace(path)) 43 | { 44 | _canvas.End(); 45 | _session.Dispose(); 46 | _session = null; 47 | var task = System.Threading.Tasks.Task.Run(async() => { await _image.SaveAsync(path); }); 48 | if (task != null) 49 | System.Threading.Tasks.Task.WaitAll(task); 50 | } 51 | _image = null; 52 | } 53 | 54 | public ICanvas CreateCanvas() 55 | { 56 | return _canvas; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /UIReferenceImplementation/Layer.cs: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | using Microsoft.Graphics.Canvas.UI.Xaml; 4 | using Windows.Foundation; 5 | using Windows.UI.Core; 6 | 7 | namespace MyScript.IInk.UIReferenceImplementation 8 | { 9 | public class Layer 10 | { 11 | public ImageLoader ImageLoader { get; set; } 12 | 13 | private CanvasVirtualControl _control; 14 | private IRenderTarget _target; 15 | private Renderer _renderer ; 16 | 17 | public Layer(CanvasVirtualControl control, IRenderTarget target, Renderer renderer) 18 | { 19 | _control = control; 20 | _target = target; 21 | _renderer = renderer; 22 | } 23 | 24 | public void Update() 25 | { 26 | // It must be done on UI thread 27 | var task = _control.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, 28 | () => 29 | { 30 | _control.Invalidate(); 31 | }); 32 | } 33 | 34 | private void Update_(int x, int y, int width, int height) 35 | { 36 | // Clamp region's coordinates into control's rect 37 | // (control.Invalidate may raise an exception) 38 | var region = ClampRect(x, y, width, height); 39 | 40 | if (region.Width > 0 && region.Height > 0) 41 | { 42 | _control.Invalidate(region); 43 | } 44 | } 45 | 46 | public void Update(int x, int y, int width, int height) 47 | { 48 | // It must be done on UI thread 49 | var task = _control.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, 50 | () => 51 | { 52 | Update_(x, y, width, height); 53 | }); 54 | } 55 | 56 | /// Retranscribe draw with renderer 57 | public void OnPaint(int x, int y, int width, int height) 58 | { 59 | // Clamp region's coordinates into control's rect 60 | // (control.CreateDrawingSession may raise an exception) 61 | var region = ClampRect(x, y, width, height); 62 | if (region.Width <= 0 || region.Height <= 0) 63 | return; 64 | 65 | using (var session = _control.CreateDrawingSession(region)) 66 | { 67 | var canvas = new Canvas(session, _target, ImageLoader); 68 | 69 | canvas.ClearOnStartDraw = true; 70 | _renderer.DrawModel(x, y, width, height, canvas); 71 | 72 | canvas.ClearOnStartDraw = false; 73 | _renderer.DrawCaptureStrokes(x, y, width, height, canvas); 74 | } 75 | } 76 | 77 | /// Clamp rect's coordinates into control's rect 78 | private Rect ClampRect(int x, int y, int width, int height) 79 | { 80 | if (x < 0) 81 | { 82 | width += x; 83 | x = 0; 84 | } 85 | if (y < 0) 86 | { 87 | height += y; 88 | y = 0; 89 | } 90 | if ((x + width) > _control.ActualWidth) 91 | { 92 | width = (int)(_control.ActualWidth - x); 93 | } 94 | if ((y + height) > _control.ActualHeight) 95 | { 96 | height = (int)(_control.ActualHeight - y); 97 | } 98 | 99 | return new Rect(x, y, width > 0 ? width : 0, height > 0 ? height : 0); 100 | } 101 | }; 102 | } 103 | -------------------------------------------------------------------------------- /UIReferenceImplementation/MyScript.InteractiveInk.UIReferenceImplementation.Uwp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | obj 6 | bin 7 | 8 | 9 | 10 | Debug 11 | AnyCPU 12 | {667892F3-1761-4EA9-BDFA-71944C768BAD} 13 | Library 14 | Properties 15 | MyScript.IInk.UIReferenceImplementation 16 | MyScript.InteractiveInk.UIReferenceImplementation.Uwp 17 | en-US 18 | UAP 19 | 10.0.22621.0 20 | 10.0.17763.0 21 | 14 22 | 512 23 | {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 24 | 25 | 26 | 27 | 6.2.13 28 | 29 | 30 | 4.1.0 31 | 32 | 33 | 4.7.1 34 | 35 | 36 | 1.25.0 37 | 38 | 39 | 40 | x64 41 | true 42 | $(BaseOutputPath)\$(Platform)\$(Configuration)\ 43 | $(BaseIntermediateOutputPath)\$(Platform)\$(Configuration)\ 44 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 45 | ;2008 46 | full 47 | x64 48 | false 49 | prompt 50 | 51 | 52 | x64 53 | $(BaseOutputPath)\$(Platform)\$(Configuration)\ 54 | $(BaseIntermediateOutputPath)\$(Platform)\$(Configuration)\ 55 | TRACE;NETFX_CORE;WINDOWS_UWP 56 | true 57 | ;2008 58 | pdbonly 59 | x64 60 | false 61 | prompt 62 | 63 | 64 | PackageReference 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | EditorUserControl.xaml 82 | 83 | 84 | InkToolbar.xaml 85 | 86 | 87 | SmartGuideUserControl.xaml 88 | 89 | 90 | 91 | 92 | 93 | MSBuild:Compile 94 | Designer 95 | 96 | 97 | MSBuild:Compile 98 | Designer 99 | 100 | 101 | MSBuild:Compile 102 | Designer 103 | 104 | 105 | 106 | 15.0 107 | 108 | 109 | 116 | 117 | -------------------------------------------------------------------------------- /UIReferenceImplementation/Path.cs: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | using MyScript.IInk.Graphics; 4 | using Microsoft.Graphics.Canvas; 5 | using Microsoft.Graphics.Canvas.Geometry; 6 | 7 | namespace MyScript.IInk.UIReferenceImplementation 8 | { 9 | public class Path : IPath 10 | { 11 | private CanvasPathBuilder _pathBuilder; 12 | private CanvasGeometry _cachedPath; // still valid after _pathBuilder is disposed 13 | private bool _isInFigure; 14 | private bool _isConfigured; 15 | 16 | public Path(CanvasDevice device) 17 | { 18 | _pathBuilder = new CanvasPathBuilder(device); 19 | _cachedPath = null; 20 | _isInFigure = false; 21 | _isConfigured = false; 22 | } 23 | 24 | public uint UnsupportedOperations 25 | { 26 | get { return (uint)PathOperation.ARC_OPS; } 27 | } 28 | 29 | public void MoveTo(float x, float y) 30 | { 31 | if (!_isConfigured) 32 | { 33 | // Settings must be set from Canvas properties and before BeginFigure 34 | _pathBuilder.SetSegmentOptions(CanvasFigureSegmentOptions.None); 35 | _pathBuilder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.Winding); 36 | _isConfigured = true; 37 | } 38 | 39 | if (_isInFigure) 40 | { 41 | _pathBuilder.EndFigure(CanvasFigureLoop.Open); 42 | } 43 | 44 | _pathBuilder.BeginFigure(x, y); 45 | _isInFigure = true; 46 | _cachedPath = null; 47 | } 48 | 49 | public void LineTo(float x, float y) 50 | { 51 | _pathBuilder.AddLine(x, y); 52 | _cachedPath = null; 53 | } 54 | 55 | public void CurveTo(float x1, float y1, float x2, float y2, float x, float y) 56 | { 57 | var controlPoint1 = new System.Numerics.Vector2(x1, y1); 58 | var controlPoint2 = new System.Numerics.Vector2(x2, y2); 59 | var endPoint = new System.Numerics.Vector2(x, y); 60 | 61 | _pathBuilder.AddCubicBezier(controlPoint1, controlPoint2, endPoint); 62 | _cachedPath = null; 63 | } 64 | 65 | public void QuadTo(float x1, float y1, float x, float y) 66 | { 67 | var controlPoint = new System.Numerics.Vector2(x1, y1); 68 | var endPoint = new System.Numerics.Vector2(x, y); 69 | 70 | _pathBuilder.AddQuadraticBezier(controlPoint, endPoint); 71 | _cachedPath = null; 72 | } 73 | 74 | public void ArcTo(float rx, float ry, float phi, bool fA, bool fS, float x, float y) 75 | { 76 | // not supported, see unsupportedOperations 77 | } 78 | 79 | public void ClosePath() 80 | { 81 | _pathBuilder.EndFigure(CanvasFigureLoop.Closed); 82 | _isInFigure = false; 83 | _cachedPath = null; 84 | } 85 | 86 | public CanvasGeometry CreateGeometry() 87 | { 88 | if (_isInFigure) 89 | { 90 | _pathBuilder.EndFigure(CanvasFigureLoop.Open); 91 | _isInFigure = false; 92 | } 93 | else if (_cachedPath != null) 94 | { 95 | return _cachedPath; 96 | } 97 | _cachedPath = CanvasGeometry.CreatePath(_pathBuilder); 98 | return _cachedPath; 99 | } 100 | }; 101 | } 102 | -------------------------------------------------------------------------------- /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.Uwp")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("MyScript")] 12 | [assembly: AssemblyProduct("MyScript.InteractiveInk.UIReferenceImplementation.Uwp")] 13 | [assembly: AssemblyCopyright("Copyright @ MyScript. All rights reserved.")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Version information for an assembly consists of the following four values: 18 | // 19 | // Major Version 20 | // Minor Version 21 | // Build Number 22 | // Revision 23 | // 24 | // You can specify all the values or you can default the Build and Revision Numbers 25 | // by using the '*' as shown below: 26 | // [assembly: AssemblyVersion("1.0.*")] 27 | [assembly: AssemblyVersion("4.1.0.0")] 28 | [assembly: AssemblyFileVersion("4.1.0.0")] 29 | [assembly: ComVisible(false)] 30 | -------------------------------------------------------------------------------- /UIReferenceImplementation/Properties/MyScript.InteractiveInk.UIReferenceImplementation.rd.xml: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /UIReferenceImplementation/Properties/iink_sdk_examples_uwp.UIRefImpl.rd.xml: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /UIReferenceImplementation/RendererListener.cs: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | using Windows.UI.Core; 4 | using MyScript.IInk.UIReferenceImplementation.UserControls; 5 | 6 | namespace MyScript.IInk.UIReferenceImplementation 7 | { 8 | public class RendererListener : IRendererListener 9 | { 10 | private EditorUserControl _ucEditor; 11 | 12 | public RendererListener(EditorUserControl ucEditor) 13 | { 14 | _ucEditor = ucEditor; 15 | } 16 | 17 | public void ViewTransformChanged(Renderer renderer) 18 | { 19 | if (_ucEditor.SmartGuideEnabled && _ucEditor.SmartGuide != null) 20 | { 21 | var dispatcher = _ucEditor.Dispatcher; 22 | var task = dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { _ucEditor.SmartGuide.OnTransformChanged(); }); 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /UIReferenceImplementation/UserControls/EditorUserControl.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 21 | 22 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /UIReferenceImplementation/UserControls/EditorUserControl.xaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Windows.UI.Xaml; 6 | using Windows.UI.Xaml.Input; 7 | using Windows.UI.Core; 8 | using Windows.UI.Popups; 9 | using Windows.System; 10 | using Windows.Graphics.Display; 11 | using Microsoft.Graphics.Canvas; 12 | using Microsoft.Graphics.Canvas.UI.Xaml; 13 | 14 | using MyScript.IInk.Graphics; 15 | using MyScript.IInk.UIReferenceImplementation.Extensions; 16 | 17 | // The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236 18 | namespace MyScript.IInk.UIReferenceImplementation.UserControls 19 | { 20 | public sealed partial class EditorUserControl 21 | { 22 | public static readonly DependencyProperty EditorProperty = 23 | DependencyProperty.Register("Editor", typeof(Editor), typeof(EditorUserControl), 24 | new PropertyMetadata(default(Editor))); 25 | 26 | public Editor Editor 27 | { 28 | get => GetValue(EditorProperty) as Editor; 29 | set 30 | { 31 | SetValue(EditorProperty, value); 32 | if (!(value is Editor editor))return; 33 | Initialize(Editor); 34 | } 35 | } 36 | } 37 | 38 | public sealed partial class EditorUserControl : IRenderTarget 39 | { 40 | private ImageLoader _loader; 41 | private bool _smartGuideEnabled = true; 42 | 43 | public ImageLoader ImageLoader => _loader; 44 | public SmartGuideUserControl SmartGuide => smartGuide; 45 | 46 | private Layer _renderLayer; 47 | 48 | private uint _nextOffscreenRenderId = 0; 49 | private IDictionary _bitmaps = new Dictionary(); 50 | 51 | public bool SmartGuideEnabled 52 | { 53 | get 54 | { 55 | return _smartGuideEnabled; 56 | } 57 | 58 | set 59 | { 60 | EnableSmartGuide(value); 61 | } 62 | } 63 | 64 | private int _pointerId = -1; 65 | private bool _onScroll = false; 66 | private Graphics.Point _lastPointerPosition; 67 | private System.Int64 _eventTimeOffset = 0; 68 | 69 | public EditorUserControl() 70 | { 71 | InitializeComponent(); 72 | 73 | var msFromEpoch = System.DateTimeOffset.Now.ToUnixTimeMilliseconds(); 74 | var msFromBoot = System.Environment.TickCount; 75 | _eventTimeOffset = msFromEpoch - msFromBoot; 76 | } 77 | 78 | private void Initialize(Editor editor) 79 | { 80 | var engine = editor.Engine; 81 | 82 | var dpiX = Editor?.Renderer?.DpiX ?? 96; 83 | var dpiY = Editor?.Renderer?.DpiY ?? 96; 84 | 85 | var render = editor.Renderer; 86 | _renderLayer = new Layer(renderCanvas, this, render); 87 | 88 | var tempFolder = engine.Configuration.GetString("content-package.temp-folder"); 89 | _loader = new ImageLoader(Editor, tempFolder); 90 | 91 | _renderLayer.ImageLoader = _loader; 92 | 93 | float verticalMarginPX = 60; 94 | float horizontalMarginPX = 40; 95 | var verticalMarginMM = 25.4f * verticalMarginPX / dpiY; 96 | var horizontalMarginMM = 25.4f * horizontalMarginPX / dpiX; 97 | engine.Configuration.SetNumber("text.margin.top", verticalMarginMM); 98 | engine.Configuration.SetNumber("text.margin.left", horizontalMarginMM); 99 | engine.Configuration.SetNumber("text.margin.right", horizontalMarginMM); 100 | engine.Configuration.SetNumber("math.margin.top", verticalMarginMM); 101 | engine.Configuration.SetNumber("math.margin.bottom", verticalMarginMM); 102 | engine.Configuration.SetNumber("math.margin.left", horizontalMarginMM); 103 | engine.Configuration.SetNumber("math.margin.right", horizontalMarginMM); 104 | } 105 | 106 | /// Force ink layer to be redrawn 107 | public void Invalidate(LayerType layers) 108 | { 109 | if (!(Editor?.Renderer is Renderer renderer)) return; 110 | Invalidate(renderer, layers); 111 | } 112 | 113 | /// Force ink layer to be redrawn 114 | public void Invalidate(Renderer renderer, LayerType layers) 115 | { 116 | _renderLayer.Update(); 117 | } 118 | 119 | /// Force ink layer to be redrawn according region 120 | public void Invalidate(Renderer renderer, int x, int y, int width, int height, LayerType layers) 121 | { 122 | if (width > 0 && height > 0) 123 | _renderLayer?.Update(x, y, width, height); 124 | } 125 | 126 | public bool SupportsOffscreenRendering() 127 | { 128 | return true; 129 | } 130 | 131 | public float GetPixelDensity() 132 | { 133 | var info = DisplayInformation.GetForCurrentView(); 134 | return info.RawPixelsPerViewPixel > 0 ? (float)info.RawPixelsPerViewPixel : 135 | info.ResolutionScale != ResolutionScale.Invalid ? (float)info.ResolutionScale / 100.0f : 1.0f; 136 | } 137 | 138 | public uint CreateOffscreenRenderSurface(int width, int height, bool alphaMask) 139 | { 140 | // Use DPI 96 to specify 1:1 dip <-> pixel mapping 141 | CanvasDevice device = CanvasDevice.GetSharedDevice(); 142 | CanvasRenderTarget offscreen = new CanvasRenderTarget(device, width, height, 96); 143 | 144 | uint offscreenRenderId = _nextOffscreenRenderId++; 145 | _bitmaps.Add(offscreenRenderId, offscreen); 146 | return offscreenRenderId; 147 | } 148 | 149 | public void ReleaseOffscreenRenderSurface(uint surfaceId) 150 | { 151 | CanvasRenderTarget offscreen; 152 | if (!_bitmaps.TryGetValue(surfaceId, out offscreen)) 153 | throw new System.NullReferenceException(); 154 | 155 | _bitmaps.Remove(surfaceId); 156 | offscreen.Dispose(); 157 | } 158 | 159 | public ICanvas CreateOffscreenRenderCanvas(uint offscreenID) 160 | { 161 | CanvasRenderTarget offscreen; 162 | if ( !_bitmaps.TryGetValue(offscreenID, out offscreen) ) 163 | throw new System.NullReferenceException(); 164 | 165 | return new Canvas(offscreen.CreateDrawingSession(), this, _loader); 166 | } 167 | 168 | public void ReleaseOffscreenRenderCanvas(ICanvas canvas) 169 | { 170 | // The previously created DrawingSession (in CreateOffscreenRenderCanvas) must be disposed 171 | // before we can ask the offscreen surface (CanvasRenderTarget) to recreate a new one. 172 | // So, we ask the canvas to dispose and set to null its DrawingSession; the canvas should be destroyed soon after. 173 | Canvas canvas_ = (Canvas)canvas; 174 | canvas_.DisposeSession(); 175 | } 176 | 177 | public CanvasRenderTarget GetImage(uint offscreenID) 178 | { 179 | CanvasRenderTarget offscreen; 180 | if ( !_bitmaps.TryGetValue(offscreenID, out offscreen) ) 181 | throw new System.NullReferenceException(); 182 | 183 | return offscreen; 184 | } 185 | 186 | 187 | private void EnableSmartGuide(bool enable) 188 | { 189 | if (_smartGuideEnabled == enable) 190 | return; 191 | 192 | _smartGuideEnabled = enable; 193 | 194 | if (!_smartGuideEnabled && smartGuide != null) 195 | smartGuide.Visibility = Visibility.Collapsed; 196 | } 197 | 198 | public void OnResize(int width, int height) 199 | { 200 | Editor?.SetViewSize(width, height); 201 | } 202 | 203 | /// Resize editor when one canvas size has been changed 204 | private void Canvas_SizeChanged(object sender, SizeChangedEventArgs e) 205 | { 206 | if (sender == renderCanvas) 207 | { 208 | OnResize((int)renderCanvas.ActualWidth, (int)renderCanvas.ActualHeight); 209 | } 210 | 211 | ((CanvasVirtualControl)(sender)).Invalidate(); 212 | } 213 | 214 | /// Redrawing Canvas 215 | private void Canvas_OnRegionsInvalidated(CanvasVirtualControl sender, CanvasRegionsInvalidatedEventArgs args) 216 | { 217 | foreach (var region in args.InvalidatedRegions) 218 | { 219 | if (region.Width > 0 && region.Height > 0) 220 | { 221 | var x = (int)System.Math.Floor(region.X); 222 | var y = (int)System.Math.Floor(region.Y); 223 | var width = (int)System.Math.Ceiling(region.X + region.Width) - x; 224 | var height = (int)System.Math.Ceiling(region.Y + region.Height) - y; 225 | 226 | if (sender == renderCanvas) 227 | _renderLayer.OnPaint(x, y, width, height); 228 | } 229 | } 230 | } 231 | 232 | private System.Int64 GetTimestamp(Windows.UI.Input.PointerPoint point) 233 | { 234 | // Convert the timestamp (from boot time) to milliseconds 235 | // and add offset to get the time from EPOCH 236 | return _eventTimeOffset + (System.Int64)(point.Timestamp / 1000); 237 | } 238 | 239 | public int GetPointerId(RoutedEventArgs e) 240 | { 241 | if (e is PointerRoutedEventArgs) 242 | return (int)((PointerRoutedEventArgs)e).Pointer.PointerDeviceType; 243 | else if (e is HoldingRoutedEventArgs) 244 | return (int)((HoldingRoutedEventArgs)e).PointerDeviceType; 245 | 246 | return -1; 247 | } 248 | 249 | [System.Flags] 250 | public enum ContextualActions 251 | { 252 | NONE = 0, 253 | ADD_BLOCK = 1 << 0, /// Add block. See Editor.GetSupportedAddBlockTypes. 254 | REMOVE = 1 << 1, /// Remove selection. 255 | CONVERT = 1 << 2, /// Convert. See Editor.GetSupportedTargetConversionStates. 256 | COPY = 1 << 3, /// Copy selection. 257 | OFFICE_CLIPBOARD = 1 << 4, /// Copy selection to Microsoft Office clipboard. 258 | PASTE = 1 << 5, /// Paste. 259 | IMPORT = 1 << 6, /// Import. See Editor.GetSupportedImportMimeTypes. 260 | EXPORT = 1 << 7, /// Export. See Editor.GetSupportedExportMimeTypes. 261 | FORMAT_TEXT = 1 << 8, /// Change Text blocks format. 262 | SELECTION_MODE = 1 << 9, /// Change selection mode. 263 | SELECTION_TYPE = 1 << 10 /// Change selection type. 264 | } 265 | 266 | public ContextualActions GetAvailableActions(ContentBlock contentBlock) 267 | { 268 | if (contentBlock == null || Editor == null) 269 | return ContextualActions.NONE; 270 | 271 | var part = Editor.Part; 272 | if (part == null) 273 | return ContextualActions.NONE; 274 | 275 | var actions = ContextualActions.NONE; 276 | 277 | using (var rootBlock = Editor.GetRootBlock()) 278 | { 279 | var isRoot = contentBlock.Id == rootBlock.Id; 280 | if (!isRoot && (contentBlock.Type == "Container")) 281 | return ContextualActions.NONE; 282 | 283 | var onRawContent = part.Type == "Raw Content"; 284 | var onTextDocument = part.Type == "Text Document"; 285 | 286 | var isEmpty = Editor.IsEmpty(contentBlock); 287 | 288 | var supportedBlocks = Editor.SupportedAddBlockTypes; 289 | var supportedExports = Editor.GetSupportedExportMimeTypes(onRawContent ? rootBlock : contentBlock); 290 | var supportedImports = Editor.GetSupportedImportMimeTypes(contentBlock); 291 | var supportedStates = Editor.GetSupportedTargetConversionStates(contentBlock); 292 | var supportedFormats = Editor.GetSupportedTextFormats(contentBlock); 293 | var supportedModes = Editor.GetAvailableSelectionModes(); 294 | var supportedTypes = Editor.GetAvailableSelectionTypes(contentBlock); 295 | 296 | var hasBlocks = (supportedBlocks != null) && supportedBlocks.Any(); 297 | var hasExports = (supportedExports != null) && supportedExports.Any(); 298 | var hasImports = (supportedImports != null) && supportedImports.Any(); 299 | var hasStates = (supportedStates != null) && supportedStates.Any(); 300 | var hasFormats = (supportedFormats != null) && supportedFormats.Any(); 301 | var hasModes = (supportedModes != null) && supportedModes.Any(); 302 | var hasTypes = (supportedTypes != null) && supportedTypes.Any(); 303 | 304 | if (hasBlocks && (!onTextDocument || isRoot)) 305 | actions |= ContextualActions.ADD_BLOCK; 306 | if (!isRoot) 307 | actions |= ContextualActions.REMOVE; 308 | if (hasStates && !isEmpty) 309 | actions |= ContextualActions.CONVERT; 310 | if (!onTextDocument || !isRoot) 311 | actions |= ContextualActions.COPY; 312 | if (hasExports && supportedExports.Contains(MimeType.OFFICE_CLIPBOARD)) 313 | actions |= ContextualActions.OFFICE_CLIPBOARD; 314 | if (isRoot) 315 | actions |= ContextualActions.PASTE; 316 | if (hasImports) 317 | actions |= ContextualActions.IMPORT; 318 | if (hasExports) 319 | actions |= ContextualActions.EXPORT; 320 | if (hasFormats) 321 | actions |= ContextualActions.FORMAT_TEXT; 322 | if (hasModes) 323 | actions |= ContextualActions.SELECTION_MODE; 324 | if (hasTypes) 325 | actions |= ContextualActions.SELECTION_TYPE; 326 | } 327 | 328 | return actions; 329 | } 330 | 331 | public ContextualActions GetAvailableActions(ContentSelection contentSelection) 332 | { 333 | if (contentSelection == null || Editor == null || Editor.IsEmpty(contentSelection)) 334 | return ContextualActions.NONE; 335 | 336 | var part = Editor.Part; 337 | if (part == null) 338 | return ContextualActions.NONE; 339 | 340 | var actions = ContextualActions.NONE; 341 | 342 | var supportedExports = Editor.GetSupportedExportMimeTypes(contentSelection); 343 | var supportedStates = Editor.GetSupportedTargetConversionStates(contentSelection); 344 | var supportedFormats = Editor.GetSupportedTextFormats(contentSelection); 345 | var supportedModes = Editor.GetAvailableSelectionModes(); 346 | var supportedTypes = Editor.GetAvailableSelectionTypes(contentSelection); 347 | 348 | var hasExports = (supportedExports != null) && supportedExports.Any(); 349 | var hasStates = (supportedStates != null) && supportedStates.Any(); 350 | var hasFormats = (supportedFormats != null) && supportedFormats.Any(); 351 | var hasModes = (supportedModes != null) && supportedModes.Any(); 352 | var hasTypes = (supportedTypes != null) && supportedTypes.Any(); 353 | 354 | // Erase 355 | actions |= ContextualActions.REMOVE; 356 | if (hasStates) 357 | actions |= ContextualActions.CONVERT; 358 | // Copy 359 | actions |= ContextualActions.COPY; 360 | if (hasExports && supportedExports.Contains(MimeType.OFFICE_CLIPBOARD)) 361 | actions |= ContextualActions.OFFICE_CLIPBOARD; 362 | if (hasExports) 363 | actions |= ContextualActions.EXPORT; 364 | if (hasFormats) 365 | actions |= ContextualActions.FORMAT_TEXT; 366 | if (hasModes) 367 | actions |= ContextualActions.SELECTION_MODE; 368 | if (hasTypes) 369 | actions |= ContextualActions.SELECTION_TYPE; 370 | 371 | return actions; 372 | } 373 | 374 | public void CancelSampling(int pointerId) 375 | { 376 | Editor?.PointerCancel(pointerId); 377 | _pointerId = -1; 378 | } 379 | 380 | private bool HasPart() 381 | { 382 | return Editor?.Part != null; 383 | } 384 | 385 | private void Capture_PointerPressed(object sender, PointerRoutedEventArgs e) 386 | { 387 | var uiElement = sender as UIElement; 388 | var p = e.GetCurrentPoint(uiElement); 389 | 390 | if (!HasPart()) 391 | return; 392 | 393 | if (_pointerId != -1) 394 | return; 395 | 396 | // Consider left button only 397 | if ( (!p.Properties.IsLeftButtonPressed) || (p.Properties.PointerUpdateKind != Windows.UI.Input.PointerUpdateKind.LeftButtonPressed) ) 398 | return; 399 | 400 | // Capture the pointer to the target. 401 | uiElement?.CapturePointer(e.Pointer); 402 | 403 | try 404 | { 405 | _pointerId = (int)e.Pointer.PointerId; 406 | _lastPointerPosition = new Graphics.Point((float)p.Position.X, (float)p.Position.Y); 407 | _onScroll = false; 408 | 409 | // Send pointer down event to the editor 410 | Editor?.PointerDown((float)p.Position.X, (float)p.Position.Y, GetTimestamp(p), p.Properties.Pressure, e.Pointer.PointerDeviceType.ToNative(), GetPointerId(e)); 411 | } 412 | catch (System.Exception ex) 413 | { 414 | if (ex.HResult == (int)MyScript.IInk.ExceptionHResult.POINTER_SEQUENCE_ERROR) 415 | { 416 | // Special case: pointerDown already called, discard previous and retry 417 | Editor?.PointerCancel(GetPointerId(e)); 418 | Editor?.PointerDown((float)p.Position.X, (float)p.Position.Y, GetTimestamp(p), p.Properties.Pressure, e.Pointer.PointerDeviceType.ToNative(), GetPointerId(e)); 419 | } 420 | else 421 | { 422 | var dlg = new MessageDialog(ex.Message); 423 | var dlgTask = dlg.ShowAsync(); 424 | } 425 | } 426 | // Prevent most handlers along the event route from handling the same event again. 427 | e.Handled = true; 428 | } 429 | 430 | private void Capture_PointerMoved(object sender, PointerRoutedEventArgs e) 431 | { 432 | var uiElement = sender as UIElement; 433 | var p = e.GetCurrentPoint(uiElement); 434 | 435 | if (!HasPart()) 436 | return; 437 | 438 | if (_pointerId != (int)e.Pointer.PointerId) 439 | return; 440 | 441 | // Ignore pointer move when the pointing device is up 442 | if (!p.IsInContact) 443 | return; 444 | 445 | // Consider left button only 446 | if (!p.Properties.IsLeftButtonPressed) 447 | return; 448 | 449 | var pointerType = e.Pointer.PointerDeviceType.ToNative(); 450 | var previousPosition = _lastPointerPosition; 451 | _lastPointerPosition = new Graphics.Point((float)p.Position.X, (float)p.Position.Y); 452 | 453 | var pointerTool = Editor.ToolController.GetToolForType(pointerType); 454 | if (!_onScroll && pointerTool == PointerTool.HAND) 455 | { 456 | var deltaMin = 3.0f; 457 | var deltaX = _lastPointerPosition.X - previousPosition.X; 458 | var deltaY = _lastPointerPosition.Y - previousPosition.Y; 459 | 460 | _onScroll = Editor.IsScrollAllowed() && ((System.Math.Abs(deltaX) > deltaMin) || (System.Math.Abs(deltaY) > deltaMin)); 461 | 462 | if (_onScroll) 463 | { 464 | // Entering scrolling mode, cancel previous pointerDown event 465 | Editor?.PointerCancel(GetPointerId(e)); 466 | } 467 | } 468 | 469 | if (_onScroll) 470 | { 471 | // Scroll the view 472 | var deltaX = _lastPointerPosition.X - previousPosition.X; 473 | var deltaY = _lastPointerPosition.Y - previousPosition.Y; 474 | Scroll(-deltaX, -deltaY); 475 | } 476 | else 477 | { 478 | var pointList = e.GetIntermediatePoints(uiElement); 479 | if (pointList.Count > 0) 480 | { 481 | var events = new PointerEvent[pointList.Count]; 482 | 483 | // Intermediate points are stored in reverse order: 484 | // Revert the list and send the pointer events all at once 485 | int j = 0; 486 | for (int i = pointList.Count - 1; i >= 0; i--) 487 | { 488 | var p_ = pointList[i]; 489 | events[j++] = new PointerEvent(PointerEventType.MOVE, (float)p_.Position.X, (float)p_.Position.Y, GetTimestamp(p_), p_.Properties.Pressure, pointerType, GetPointerId(e)); 490 | } 491 | 492 | // Send pointer move events to the editor 493 | try 494 | { 495 | Editor?.PointerEvents(events); 496 | } 497 | catch 498 | { 499 | // Don't show error for every move event 500 | } 501 | } 502 | } 503 | 504 | // Prevent most handlers along the event route from handling the same event again. 505 | e.Handled = true; 506 | } 507 | 508 | private void Capture_PointerReleased(object sender, PointerRoutedEventArgs e) 509 | { 510 | var uiElement = sender as UIElement; 511 | var p = e.GetCurrentPoint(uiElement); 512 | 513 | if (!HasPart()) 514 | return; 515 | 516 | if (_pointerId != (int)e.Pointer.PointerId) 517 | return; 518 | 519 | // Consider left button only 520 | if ( (p.Properties.IsLeftButtonPressed) || (p.Properties.PointerUpdateKind != Windows.UI.Input.PointerUpdateKind.LeftButtonReleased) ) 521 | return; 522 | 523 | var previousPosition = _lastPointerPosition; 524 | _lastPointerPosition = new Graphics.Point((float)p.Position.X, (float)p.Position.Y); 525 | 526 | if (_onScroll) 527 | { 528 | // Scroll the view 529 | var deltaX = _lastPointerPosition.X - previousPosition.X; 530 | var deltaY = _lastPointerPosition.Y - previousPosition.Y; 531 | Scroll(-deltaX, -deltaY); 532 | 533 | // Exiting scrolling mode 534 | _onScroll = false; 535 | } 536 | else 537 | { 538 | // Send pointer up event to the editor 539 | try 540 | { 541 | Editor?.PointerUp((float)p.Position.X, (float)p.Position.Y, GetTimestamp(p), p.Properties.Pressure, e.Pointer.PointerDeviceType.ToNative(), GetPointerId(e)); 542 | } 543 | catch 544 | { 545 | // Don't show error for up event 546 | } 547 | } 548 | 549 | _pointerId = -1; 550 | 551 | // Release the pointer captured from the target 552 | uiElement?.ReleasePointerCapture(e.Pointer); 553 | 554 | // Prevent most handlers along the event route from handling the same event again. 555 | e.Handled = true; 556 | } 557 | 558 | private void Capture_PointerCanceled(object sender, PointerRoutedEventArgs e) 559 | { 560 | var uiElement = sender as UIElement; 561 | var p = e.GetCurrentPoint(uiElement); 562 | 563 | if (!HasPart()) 564 | return; 565 | 566 | if (_pointerId != (int)e.Pointer.PointerId) 567 | return; 568 | 569 | // When using mouse consider left button only 570 | if (e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Mouse) 571 | { 572 | if (!p.Properties.IsLeftButtonPressed) 573 | return; 574 | } 575 | 576 | if (_onScroll) 577 | { 578 | // Exiting scrolling mode 579 | _onScroll = false; 580 | } 581 | else 582 | { 583 | // Send pointer cancel event to the editor 584 | Editor.PointerCancel(GetPointerId(e)); 585 | } 586 | 587 | _pointerId = -1; 588 | 589 | // Prevent most handlers along the event route from handling the same event again. 590 | e.Handled = true; 591 | } 592 | 593 | private void Capture_PointerWheelChanged(object sender, PointerRoutedEventArgs e) 594 | { 595 | var uiElement = renderCanvas; //sender as UIElement; 596 | var properties = e.GetCurrentPoint(uiElement).Properties; 597 | 598 | if (!HasPart()) 599 | return; 600 | 601 | if (properties.IsHorizontalMouseWheel == false) 602 | { 603 | var WHEEL_DELTA = 120; 604 | 605 | var controlDown = CoreWindow.GetForCurrentThread().GetKeyState(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down); 606 | var shiftDown = CoreWindow.GetForCurrentThread().GetKeyState(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down); 607 | var wheelDelta = properties.MouseWheelDelta / WHEEL_DELTA; 608 | 609 | if (controlDown) 610 | { 611 | if (wheelDelta > 0) 612 | ZoomIn((uint)wheelDelta); 613 | else if (wheelDelta < 0) 614 | ZoomOut((uint)(-wheelDelta)); 615 | } 616 | else 617 | { 618 | var SCROLL_SPEED = 100; 619 | var delta = (float)(-SCROLL_SPEED * wheelDelta); 620 | var deltaX = shiftDown ? delta : 0.0f; 621 | var deltaY = shiftDown ? 0.0f : delta; 622 | 623 | Scroll(deltaX, deltaY); 624 | } 625 | } 626 | 627 | // Prevent most handlers along the event route from handling the same event again. 628 | e.Handled = true; 629 | } 630 | 631 | public void ResetView(bool forceInvalidate) 632 | { 633 | if (!(Editor?.Renderer is Renderer renderer) || !HasPart()) 634 | return; 635 | 636 | // Reset view offset and scale 637 | renderer.ViewScale = 1; 638 | renderer.ViewOffset = new Graphics.Point(0, 0); 639 | 640 | // Get new view transform (keep only scale and offset) 641 | var tr = renderer.GetViewTransform(); 642 | tr = new Graphics.Transform(tr.XX, tr.YX, 0, tr.XY, tr.YY, 0); 643 | 644 | // Compute new view offset 645 | var offset = new Graphics.Point(0, 0); 646 | 647 | if (Editor.Part.Type == "Raw Content") 648 | { 649 | // Center view on the center of content for "Raw Content" parts 650 | var contentBox = Editor.GetRootBlock().Box; 651 | var contentCenter = new Graphics.Point(contentBox.X + (contentBox.Width * 0.5f), contentBox.Y + (contentBox.Height * 0.5f)); 652 | 653 | // From model coordinates to view coordinates 654 | contentCenter = tr.Apply(contentCenter.X, contentCenter.Y); 655 | 656 | var viewCenter = new Graphics.Point(Editor.ViewWidth * 0.5f, Editor.ViewHeight * 0.5f); 657 | offset.X = contentCenter.X - viewCenter.X; 658 | offset.Y = contentCenter.Y - viewCenter.Y; 659 | } 660 | else 661 | { 662 | // Move the origin to the top-left corner of the page for other types of parts 663 | var boxV = Editor.Part.ViewBox; 664 | 665 | offset.X = boxV.X; 666 | offset.Y = boxV.Y; 667 | 668 | // From model coordinates to view coordinates 669 | offset = tr.Apply(offset.X, offset.Y); 670 | } 671 | 672 | // Set new view offset 673 | renderer.ViewOffset = offset; 674 | 675 | if (forceInvalidate) 676 | Invalidate(renderer, LayerType.LayerType_ALL); 677 | } 678 | 679 | public void ZoomIn(uint delta) 680 | { 681 | if (!(Editor?.Renderer is Renderer renderer)) return; 682 | renderer.Zoom((float)delta * (110.0f / 100.0f)); 683 | Invalidate(renderer, LayerType.LayerType_ALL); 684 | } 685 | 686 | public void ZoomOut(uint delta) 687 | { 688 | if (!(Editor?.Renderer is Renderer renderer)) return; 689 | renderer.Zoom((float)delta * (100.0f / 110.0f)); 690 | Invalidate(renderer, LayerType.LayerType_ALL); 691 | } 692 | 693 | private void Scroll(float deltaX, float deltaY) 694 | { 695 | if (!(Editor?.Renderer is Renderer renderer)) return; 696 | var oldOffset = renderer.ViewOffset; 697 | var newOffset = new Graphics.Point(oldOffset.X + deltaX, oldOffset.Y + deltaY); 698 | 699 | renderer.ViewOffset = newOffset; 700 | Invalidate(renderer, LayerType.LayerType_ALL); 701 | } 702 | } 703 | } 704 | -------------------------------------------------------------------------------- /UIReferenceImplementation/UserControls/InkToolbar.xaml: -------------------------------------------------------------------------------- 1 |  11 | 12 | 13 | 14 | 15 | 16 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /UIReferenceImplementation/UserControls/InkToolbar.xaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright @ MyScript. All rights reserved. 2 | 3 | using System; 4 | using Windows.Foundation; 5 | using Windows.UI; 6 | using Windows.UI.Input.Inking; 7 | using Windows.UI.Xaml; 8 | using Windows.UI.Xaml.Controls; 9 | using Windows.UI.Xaml.Media; 10 | using MyScript.IInk.UIReferenceImplementation.Extensions; 11 | 12 | namespace MyScript.IInk.UIReferenceImplementation.UserControls 13 | { 14 | public sealed partial class InkToolbar 15 | { 16 | public static readonly DependencyProperty EditorProperty = 17 | DependencyProperty.Register("Editor", typeof(Editor), typeof(InkToolbar), 18 | new PropertyMetadata(default(Editor))); 19 | 20 | public static readonly DependencyProperty IsActivePenEnabledProperty = 21 | DependencyProperty.Register("IsActivePenEnabled", typeof(bool), typeof(InkToolbar), 22 | new PropertyMetadata(true, OnIsActivePenEnabledValueChanged)); 23 | 24 | public InkToolbar() 25 | { 26 | InitializeComponent(); 27 | } 28 | 29 | /// 30 | /// 31 | /// 32 | public Editor Editor 33 | { 34 | get => GetValue(EditorProperty) as Editor; 35 | set => SetValue(EditorProperty, value); 36 | } 37 | 38 | public bool IsActivePenEnabled 39 | { 40 | get => (bool)GetValue(IsActivePenEnabledProperty); 41 | set => SetValue(IsActivePenEnabledProperty, value); 42 | } 43 | 44 | private static void OnIsActivePenEnabledValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 45 | { 46 | if (!(d is InkToolbar toolbar) || !(toolbar.Editor?.ToolController is ToolController controller)) return; 47 | var isActivePenEnabled = e.NewValue is bool b && b; 48 | var pointerTool = controller.GetToolForType(PointerType.PEN); 49 | controller.SetToolForType(PointerType.TOUCH, isActivePenEnabled ? PointerTool.HAND : pointerTool); 50 | // if active pen is activated and the hand tool is previously selected, 51 | // it will be disabled and the active tool will fall back to the pen tool. 52 | if (isActivePenEnabled && pointerTool == PointerTool.HAND) 53 | toolbar.Toolbar.ActiveTool = toolbar.Toolbar.GetToolButton(InkToolbarTool.BallpointPen); 54 | } 55 | 56 | private void OnActiveToolChanged(Windows.UI.Xaml.Controls.InkToolbar sender, object args) 57 | { 58 | if (!(Editor?.ToolController is ToolController controller)) return; 59 | var tool = sender.ActiveTool; 60 | switch (tool.ToolKind) 61 | { 62 | case InkToolbarTool.BallpointPen: 63 | case InkToolbarTool.Pencil: 64 | OnPenActivated(controller, IsActivePenEnabled); 65 | break; 66 | case InkToolbarTool.Highlighter: 67 | OnHighlighterActivated(controller, IsActivePenEnabled); 68 | break; 69 | case InkToolbarTool.Eraser: 70 | OnEraserActivated(controller, IsActivePenEnabled); 71 | break; 72 | case InkToolbarTool.CustomPen: 73 | break; 74 | case InkToolbarTool.CustomTool: 75 | if (tool == HandTool) OnHandToolActivated(controller); 76 | else if (tool == SelectorTool) OnSelectorToolActivated(controller, IsActivePenEnabled); 77 | break; 78 | default: 79 | throw new ArgumentOutOfRangeException(); 80 | } 81 | } 82 | 83 | private void OnEraseAllClicked(Windows.UI.Xaml.Controls.InkToolbar sender, object args) 84 | { 85 | Editor?.Clear(); 86 | } 87 | 88 | private void OnInkDrawingAttributesChanged(Windows.UI.Xaml.Controls.InkToolbar sender, object args) 89 | { 90 | Editor?.Apply(sender.InkDrawingAttributes); 91 | } 92 | 93 | private void OnInkToolbarPenButtonLoaded(object sender, RoutedEventArgs e) 94 | { 95 | if (!(sender is InkToolbarPenButton target)) return; 96 | Editor?.Apply(new InkDrawingAttributes 97 | { 98 | Color = target.SelectedBrush is SolidColorBrush brush ? brush.Color : default(Color), 99 | DrawAsHighlighter = target is InkToolbarHighlighterButton, 100 | Size = new Size(target.SelectedStrokeWidth, target.SelectedStrokeWidth) 101 | }); 102 | } 103 | 104 | #region On Tool Activated (Eraser, Hand, Highlighter, Pen, Selector) 105 | 106 | private static void OnEraserActivated(ToolController controller, bool isActivePenEnabled) 107 | { 108 | controller.SetToolForType(PointerType.PEN, PointerTool.ERASER); 109 | controller.SetToolForType(PointerType.MOUSE, PointerTool.ERASER); 110 | controller.SetToolForType(PointerType.TOUCH, 111 | isActivePenEnabled ? PointerTool.HAND : PointerTool.ERASER); 112 | } 113 | 114 | private static void OnHandToolActivated(ToolController controller) 115 | { 116 | controller.SetToolForType(PointerType.MOUSE, PointerTool.HAND); 117 | controller.SetToolForType(PointerType.TOUCH, PointerTool.HAND); 118 | controller.SetToolForType(PointerType.PEN, PointerTool.HAND); 119 | } 120 | 121 | private static void OnHighlighterActivated(ToolController controller, bool isActivePenEnabled) 122 | { 123 | controller.SetToolForType(PointerType.PEN, PointerTool.HIGHLIGHTER); 124 | controller.SetToolForType(PointerType.MOUSE, PointerTool.HIGHLIGHTER); 125 | controller.SetToolForType(PointerType.TOUCH, 126 | isActivePenEnabled ? PointerTool.HAND : PointerTool.HIGHLIGHTER); 127 | } 128 | 129 | private static void OnPenActivated(ToolController controller, bool isActivePenEnabled) 130 | { 131 | controller.SetToolForType(PointerType.PEN, PointerTool.PEN); 132 | controller.SetToolForType(PointerType.MOUSE, PointerTool.PEN); 133 | controller.SetToolForType(PointerType.TOUCH, 134 | isActivePenEnabled ? PointerTool.HAND : PointerTool.PEN); 135 | } 136 | 137 | private static void OnSelectorToolActivated(ToolController controller, bool isActivePenEnabled) 138 | { 139 | controller.SetToolForType(PointerType.MOUSE, PointerTool.SELECTOR); 140 | controller.SetToolForType(PointerType.PEN, PointerTool.SELECTOR); 141 | if (!isActivePenEnabled) controller.SetToolForType(PointerType.TOUCH, PointerTool.SELECTOR); 142 | } 143 | 144 | #endregion 145 | } 146 | } -------------------------------------------------------------------------------- /UIReferenceImplementation/UserControls/SmartGuideUserControl.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 25 | 26 | 27 | 32 | 35 | 40 | 45 | 50 | 51 | 52 | 53 | 56 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /configurations/interactivity.json: -------------------------------------------------------------------------------- 1 | { 2 | "raw-content": { 3 | "classification": { 4 | "types": [ "text", "drawing" ] 5 | }, 6 | "recognition": { 7 | "types": [ "text", "math" ] 8 | }, 9 | "convert": { 10 | "shape-on-hold": true 11 | }, 12 | "shape": { 13 | "snap-axis": [ "triangle", "rectangle", "rhombus", "parallelogram", "ellipse" ] 14 | }, 15 | "eraser": { 16 | "erase-precisely": false, 17 | "dynamic-radius": true 18 | }, 19 | "auto-connection": true, 20 | "interactive-blocks": { 21 | "feedback": [ "math" ] 22 | }, 23 | "pen": { 24 | "gestures": [ "underline", "scratch-out", "strike-through" ] 25 | }, 26 | "line-pattern": "grid", 27 | "guides": { 28 | "show": [ "alignment", "text", "square", "square-inside", "image-aspect-ratio", "rotation" ], 29 | "snap": [ "alignment", "text", "square", "square-inside", "image-aspect-ratio", "rotation" ] 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /fonts/MyScriptInter-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-uwp/c100f83d8914347839529b1e88dac069d7d4a26a/fonts/MyScriptInter-Bold.otf -------------------------------------------------------------------------------- /fonts/MyScriptInter-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-uwp/c100f83d8914347839529b1e88dac069d7d4a26a/fonts/MyScriptInter-Regular.otf -------------------------------------------------------------------------------- /fonts/STIXTwoMath-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-uwp/c100f83d8914347839529b1e88dac069d7d4a26a/fonts/STIXTwoMath-Regular.otf -------------------------------------------------------------------------------- /fonts/STIXTwoText-Italic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyScript/interactive-ink-examples-uwp/c100f83d8914347839529b1e88dac069d7d4a26a/fonts/STIXTwoText-Italic.otf -------------------------------------------------------------------------------- /getRecognitionAssets.ps1: -------------------------------------------------------------------------------- 1 | Set-PSDebug -Trace 1 2 | $shell_app=new-object -com shell.application 3 | $destination = $shell_app.namespace("$PSScriptRoot") 4 | 5 | 6 | if (-not[System.IO.File]::Exists("$PSScriptRoot\recognition-assets\conf\diagram.conf") -Or 7 | -not[System.IO.File]::Exists("$PSScriptRoot\recognition-assets\conf\raw-content.conf") -Or 8 | -not[System.IO.File]::Exists("$PSScriptRoot\recognition-assets\conf\raw-content2.conf") -Or 9 | -not[System.IO.File]::Exists("$PSScriptRoot\recognition-assets\conf\math.conf") -Or 10 | -not[System.IO.File]::Exists("$PSScriptRoot\recognition-assets\conf\math2.conf") -Or 11 | -not[System.IO.File]::Exists("$PSScriptRoot\recognition-assets\conf\en_US.conf")) 12 | 13 | { 14 | if ( Test-Path "$PSScriptRoot\recognition-assets") 15 | { 16 | Remove-Item "$PSScriptRoot\recognition-assets\*" -Recurse 17 | } 18 | 19 | # Download myscript-iink-recognition-math.zip 20 | $clnt = new-object System.Net.WebClient 21 | $url = "https://download.myscript.com/iink/recognitionAssets_iink_4.1/myscript-iink-recognition-math.zip" 22 | $file = "$PSScriptRoot\myscript-iink-recognition-math.zip" 23 | 24 | $clnt.DownloadFile($url,$file) 25 | 26 | # Unzip myscript-iink-recognition-math.zip 27 | 28 | $zip_file = $shell_app.namespace($file) 29 | $destination.Copyhere($zip_file.items(),16) 30 | Remove-Item $file 31 | 32 | # Download myscript-iink-recognition-math2.zip 33 | 34 | $clnt = new-object System.Net.WebClient 35 | $url = "https://download.myscript.com/iink/recognitionAssets_iink_4.1/myscript-iink-recognition-math2.zip" 36 | $file = "$PSScriptRoot\myscript-iink-recognition-math2.zip" 37 | $clnt.DownloadFile($url,$file) 38 | 39 | # Unzip myscript-iink-recognition-math2.zip 40 | 41 | $zip_file = $shell_app.namespace($file) 42 | $destination.Copyhere($zip_file.items(),16) 43 | Remove-Item $file 44 | 45 | # Download myscript-iink-recognition-diagram.zip 46 | 47 | $clnt = new-object System.Net.WebClient 48 | $url = "https://download.myscript.com/iink/recognitionAssets_iink_4.1/myscript-iink-recognition-diagram.zip" 49 | $file = "$PSScriptRoot\myscript-iink-recognition-diagram.zip" 50 | $clnt.DownloadFile($url,$file) 51 | 52 | # Unzip myscript-iink-recognition-diagram.zip 53 | 54 | $zip_file = $shell_app.namespace($file) 55 | $destination.Copyhere($zip_file.items(),16) 56 | Remove-Item $file 57 | 58 | # Download myscript-iink-recognition-raw-content.zip 59 | 60 | $clnt = new-object System.Net.WebClient 61 | $url = "https://download.myscript.com/iink/recognitionAssets_iink_4.1/myscript-iink-recognition-raw-content.zip" 62 | $file = "$PSScriptRoot\myscript-iink-recognition-raw-content.zip" 63 | $clnt.DownloadFile($url,$file) 64 | 65 | # Unzip myscript-iink-recognition-raw-content.zip 66 | 67 | $zip_file = $shell_app.namespace($file) 68 | $destination.Copyhere($zip_file.items(),16) 69 | Remove-Item $file 70 | 71 | # Download myscript-iink-recognition-raw-content2.zip 72 | 73 | $clnt = new-object System.Net.WebClient 74 | $url = "https://download.myscript.com/iink/recognitionAssets_iink_4.1/myscript-iink-recognition-raw-content2.zip" 75 | $file = "$PSScriptRoot\myscript-iink-recognition-raw-content2.zip" 76 | $clnt.DownloadFile($url,$file) 77 | 78 | # Unzip myscript-iink-recognition-raw-content2.zip 79 | 80 | $zip_file = $shell_app.namespace($file) 81 | $destination.Copyhere($zip_file.items(),16) 82 | Remove-Item $file 83 | 84 | # Download myscript-iink-recognition-text-en_US.zip 85 | 86 | $clnt = new-object System.Net.WebClient 87 | $url = "https://download.myscript.com/iink/recognitionAssets_iink_4.1/myscript-iink-recognition-text-en_US.zip" 88 | $file = "$PSScriptRoot\myscript-iink-recognition-text-en_US.zip" 89 | $clnt.DownloadFile($url,$file) 90 | 91 | # Unzip myscript-iink-recognition-text-en_US.zip 92 | 93 | $zip_file = $shell_app.namespace($file) 94 | $destination.Copyhere($zip_file.items(),16) 95 | Remove-Item $file 96 | } 97 | --------------------------------------------------------------------------------