├── .gitignore ├── FontSetViewer.cpp ├── FontSetViewer.h ├── FontSetViewer.png ├── FontSetViewer.sln ├── FontSetViewer.vcxproj ├── License.txt ├── README.md ├── common ├── AutoResource.h ├── Common.cpp ├── Common.h ├── FileHelpers.cpp ├── FileHelpers.h ├── Macros.h ├── Pointers.h ├── TextTreeParser.cpp ├── TextTreeParser.h ├── Unicode.cpp ├── Unicode.h ├── WindowUtility.cpp ├── WindowUtility.h └── precomp.h ├── font ├── DWritEx.cpp ├── DWritEx.h ├── FontDownloader.cpp └── precomp.h ├── precomp.cpp ├── precomp.h ├── precomp.inc ├── resources ├── DWriteIcon.ico ├── FontSetFilterIcons.bmp ├── FontSetFilterIcons24bit.bmp ├── FontSetViewer.manifest ├── FontSetViewer.rc ├── family-blank.ico ├── font-blank.ico └── resource.h └── sdk_headers ├── DCommon.h └── DWrite_3.h /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # Hidden directories 5 | .* 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | [Xx]64/ 22 | [Xx]86/ 23 | [Bb]uild/ 24 | Win32_Debug/ 25 | Win32_Release/ 26 | x64_Debug/ 27 | x64_Release/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | 32 | # Visual Studio 2015 cache/options directory 33 | .vs/ 34 | # Uncomment if you have tasks that create the project's static files in wwwroot 35 | #wwwroot/ 36 | 37 | # MSTest test Results 38 | [Tt]est[Rr]esult*/ 39 | [Bb]uild[Ll]og.* 40 | 41 | # NUNIT 42 | *.VisualState.xml 43 | TestResult.xml 44 | 45 | # Build Results of an ATL Project 46 | [Dd]ebugPS/ 47 | [Rr]eleasePS/ 48 | dlldata.c 49 | 50 | # DNX 51 | project.lock.json 52 | artifacts/ 53 | 54 | *.ifc 55 | *_i.c 56 | *_p.c 57 | *_i.h 58 | *.ilk 59 | *.meta 60 | *.obj 61 | *.ifc 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tlog 72 | *.tmp 73 | *.tmp_proj 74 | *.log 75 | *.vspscc 76 | *.vssscc 77 | .builds 78 | *.pidb 79 | *.svclog 80 | *.scc 81 | 82 | # Chutzpah Test files 83 | _Chutzpah* 84 | 85 | # Visual C++ cache files 86 | ipch/ 87 | *.aps 88 | *.ncb 89 | *.opendb 90 | *.opensdf 91 | *.sdf 92 | *.cachefile 93 | *.VC.db 94 | 95 | # Visual Studio profiler 96 | *.psess 97 | *.vsp 98 | *.vspx 99 | *.sap 100 | 101 | # TFS 2012 Local Workspace 102 | $tf/ 103 | 104 | # Guidance Automation Toolkit 105 | *.gpState 106 | 107 | # ReSharper is a .NET coding add-in 108 | _ReSharper*/ 109 | *.[Rr]e[Ss]harper 110 | *.DotSettings.user 111 | 112 | # JustCode is a .NET coding add-in 113 | .JustCode 114 | 115 | # TeamCity is a build add-in 116 | _TeamCity* 117 | 118 | # DotCover is a Code Coverage Tool 119 | *.dotCover 120 | 121 | # NCrunch 122 | _NCrunch_* 123 | .*crunch*.local.xml 124 | nCrunchTemp_* 125 | 126 | # MightyMoose 127 | *.mm.* 128 | AutoTest.Net/ 129 | 130 | # Web workbench (sass) 131 | .sass-cache/ 132 | 133 | # Installshield output folder 134 | [Ee]xpress/ 135 | 136 | # DocProject is a documentation generator add-in 137 | DocProject/buildhelp/ 138 | DocProject/Help/*.HxT 139 | DocProject/Help/*.HxC 140 | DocProject/Help/*.hhc 141 | DocProject/Help/*.hhk 142 | DocProject/Help/*.hhp 143 | DocProject/Help/Html2 144 | DocProject/Help/html 145 | 146 | # Click-Once directory 147 | publish/ 148 | 149 | # Publish Web Output 150 | *.[Pp]ublish.xml 151 | *.azurePubxml 152 | 153 | # TODO: Un-comment the next line if you do not want to checkin 154 | # your web deploy settings because they may include unencrypted 155 | # passwords 156 | #*.pubxml 157 | *.publishproj 158 | 159 | # NuGet Packages 160 | *.nupkg 161 | # The packages folder can be ignored because of Package Restore 162 | **/packages/* 163 | # except build/, which is used as an MSBuild target. 164 | !**/packages/build/ 165 | # Uncomment if necessary however generally it will be regenerated when needed 166 | #!**/packages/repositories.config 167 | # NuGet v3's project.json files produces more ignoreable files 168 | *.nuget.props 169 | *.nuget.targets 170 | 171 | # Microsoft Azure Build Output 172 | csx/ 173 | *.build.csdef 174 | 175 | # Microsoft Azure Emulator 176 | ecf/ 177 | rcf/ 178 | 179 | # Windows Store app package directory 180 | AppPackages/ 181 | BundleArtifacts/ 182 | 183 | # Visual Studio cache files 184 | # files ending in .cache can be ignored 185 | *.[Cc]ache 186 | # but keep track of directories ending in .cache 187 | !*.[Cc]ache/ 188 | 189 | # Others 190 | ClientBin/ 191 | [Ss]tyle[Cc]op.* 192 | ~$* 193 | *~ 194 | *.dbmdl 195 | *.dbproj.schemaview 196 | *.pfx 197 | *.publishsettings 198 | node_modules/ 199 | orleans.codegen.cs 200 | 201 | # RIA/Silverlight projects 202 | Generated_Code/ 203 | 204 | # Backup & report files from converting an old project file 205 | # to a newer Visual Studio version. Backup files are not needed, 206 | # because we have git ;-) 207 | _UpgradeReport_Files/ 208 | Backup*/ 209 | UpgradeLog*.XML 210 | UpgradeLog*.htm 211 | 212 | # SQL Server files 213 | *.mdf 214 | *.ldf 215 | 216 | # Business Intelligence projects 217 | *.rdl.data 218 | *.bim.layout 219 | *.bim_*.settings 220 | 221 | # Microsoft Fakes 222 | FakesAssemblies/ 223 | 224 | # GhostDoc plugin setting file 225 | *.GhostDoc.xml 226 | 227 | # Node.js Tools for Visual Studio 228 | .ntvs_analysis.dat 229 | 230 | # Visual Studio 6 build log 231 | *.plg 232 | 233 | # Visual Studio 6 workspace options file 234 | *.opt 235 | 236 | # Visual Studio LightSwitch build output 237 | **/*.HTMLClient/GeneratedArtifacts 238 | **/*.DesktopClient/GeneratedArtifacts 239 | **/*.DesktopClient/ModelManifest.xml 240 | **/*.Server/GeneratedArtifacts 241 | **/*.Server/ModelManifest.xml 242 | _Pvt_Extensions 243 | 244 | # LightSwitch generated files 245 | GeneratedArtifacts/ 246 | ModelManifest.xml 247 | 248 | # Paket dependency manager 249 | .paket/paket.exe 250 | 251 | # FAKE - F# Make 252 | .fake/ 253 | /RemoveFontResource.vcxproj.filters 254 | /x64_Debug 255 | /x64_Debug 256 | -------------------------------------------------------------------------------- /FontSetViewer.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | // 8 | // Contents: Main user interface window. 9 | // 10 | //---------------------------------------------------------------------------- 11 | #pragma once 12 | 13 | 14 | class MainWindow 15 | { 16 | public: 17 | MainWindow(HWND hwnd); 18 | HRESULT Initialize(); 19 | static WPARAM RunMessageLoop(); 20 | 21 | static ATOM RegisterWindowClass(); 22 | static INT_PTR CALLBACK StaticDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); 23 | DialogProcResult CALLBACK DialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); 24 | 25 | STDMETHODIMP ApplyLogFontFilter(const LOGFONT& logFont); 26 | 27 | public: 28 | const static wchar_t* g_windowClassName; 29 | 30 | enum class FontCollectionFilterMode 31 | { 32 | None, // Display all font face references without any grouping. 33 | 34 | // Modes that have a 1:1 correspondence with DWRITE_FONT_PROPERTY_ID. 35 | WssFamilyName, 36 | TypographicFamilyName, 37 | WssFaceName, 38 | FullName, 39 | Win32FamilyName, 40 | PostscriptName, 41 | DesignedScriptTag, 42 | SupportedScriptTag, 43 | SemanticTag, 44 | Weight, 45 | Stretch, 46 | Style, 47 | TypographicFaceName, 48 | 49 | Total, 50 | }; 51 | 52 | protected: 53 | struct FontCollectionEntry 54 | { 55 | std::wstring name; // Name of the entry, depending on the property type (current filter mode). 56 | uint32_t firstFontIndex; // Index of the first font for this entry. 57 | uint32_t fontCount; // number of fonts this entry contains. 58 | 59 | // Cached information about the font to use for this entry 60 | // (which corresponds to firstFontIndex). 61 | std::wstring familyName; 62 | DWRITE_FONT_WEIGHT fontWeight; // = DWRITE_FONT_WEIGHT_NORMAL; 63 | DWRITE_FONT_STRETCH fontStretch;// = DWRITE_FONT_STRETCH_NORMAL; 64 | DWRITE_FONT_STYLE fontStyle; // = DWRITE_FONT_STYLE_NORMAL; 65 | 66 | std::vector fontAxisValues; 67 | std::vector fontAxisRanges; 68 | std::wstring filePath; 69 | uint32_t fontFaceIndex; // Within an OpenType collection. 70 | DWRITE_FONT_SIMULATIONS fontSimulations; 71 | 72 | int CompareStrings(const std::wstring& a, const std::wstring& b) const 73 | { 74 | return ::CompareStringW( 75 | LOCALE_INVARIANT, 76 | NORM_IGNORECASE, 77 | a.c_str(), 78 | static_cast(a.length()), 79 | b.c_str(), 80 | static_cast(b.length()) 81 | ); 82 | } 83 | 84 | bool operator < (const FontCollectionEntry& other) 85 | { 86 | int comparison; 87 | comparison = CompareStrings(name, other.name); 88 | if (comparison != CSTR_EQUAL ) return comparison == CSTR_LESS_THAN; 89 | 90 | if (fontWeight != other.fontWeight ) return fontWeight < other.fontWeight; 91 | if (fontStretch != other.fontStretch ) return fontStretch < other.fontStretch; 92 | if (fontStyle != other.fontStyle ) return fontStyle < other.fontStyle; 93 | 94 | comparison = CompareStrings(familyName, other.familyName); 95 | if (comparison != CSTR_EQUAL ) return comparison == CSTR_LESS_THAN; 96 | 97 | return false; 98 | } 99 | }; 100 | 101 | struct FontCollectionFilter 102 | { 103 | FontCollectionFilterMode mode; 104 | uint32_t selectedFontIndex; // Previous font collection index used when applying this filter. 105 | std::wstring parameter; 106 | }; 107 | 108 | protected: 109 | void OnSize(); 110 | void OnMove(); 111 | DialogProcResult CALLBACK OnCommand(HWND hwnd, WPARAM wParam, LPARAM lParam); 112 | DialogProcResult CALLBACK OnNotification(HWND hwnd, WPARAM wParam, LPARAM lParam); 113 | DialogProcResult CALLBACK OnDragAndDrop(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); 114 | void OnKeyDown(UINT keyCode); 115 | void OnMenuPopup(HMENU menu, UINT position, BOOL isSystemWindowMenu); 116 | void MarkNeedsRepaint(); 117 | bool ProcessCommandLine(_In_z_ const wchar_t* commandLine); 118 | static void ShowHelp(); 119 | STDMETHODIMP ParseCommandLine(_In_z_ const wchar_t* commandLine); 120 | 121 | STDMETHODIMP OnChooseFont(); 122 | STDMETHODIMP OpenFontFiles(); 123 | STDMETHODIMP ReloadSystemFontSet(); 124 | STDMETHODIMP ChooseColor(IN OUT uint32_t& color); 125 | STDMETHODIMP CopyToClipboard(bool copyPlainText = false); 126 | STDMETHODIMP CopyImageToClipboard(); 127 | STDMETHODIMP PasteFromClipboard(); 128 | STDMETHODIMP InitializeLanguageMenu(); 129 | STDMETHODIMP InitializeFontCollectionListUI(); 130 | STDMETHODIMP InitializeFontCollectionFilterUI(); 131 | STDMETHODIMP UpdateFontCollectionListUI(uint32_t newSelectedItem = 0); 132 | STDMETHODIMP UpdateFontCollectionFilterUI(); 133 | STDMETHODIMP RebuildFontCollectionList(); 134 | STDMETHODIMP DrawFontCollectionIconPreview(const NMLVCUSTOMDRAW* customDraw); 135 | STDMETHODIMP RebuildFontCollectionListFromFileNames(_In_opt_z_ wchar_t const* baseFilePath, array_ref fileNames); 136 | STDMETHODIMP InitializeBlankFontCollection(); 137 | void ResetFontList(); 138 | 139 | STDMETHODIMP GetFontProperty( 140 | IDWriteFont* font, 141 | FontCollectionFilterMode filterMode, 142 | wchar_t const* languageName, 143 | _Out_ std::wstring& fontPropertyValue, 144 | _Out_ std::vector >& fontPropertyValueTokens 145 | ); 146 | 147 | STDMETHODIMP AddFontToFontCollectionList( 148 | IDWriteFont* font, 149 | uint32_t firstFontIndex, 150 | _In_z_ wchar_t const* languageName, 151 | const std::wstring& name 152 | ); 153 | 154 | bool DoesFontFilterApply( 155 | FontCollectionFilterMode filterMode, 156 | const std::wstring& filterParameter, 157 | const std::wstring& fontPropertyValue, 158 | const std::vector > fontPropertyValueTokens 159 | ); 160 | 161 | bool SplitFontProperty( 162 | FontCollectionFilterMode filterMode, 163 | const std::wstring& fontPropertyValue, 164 | OUT std::vector& fontPropertyValueArray 165 | ); 166 | 167 | HRESULT SetCurrentFilter( 168 | FontCollectionFilterMode filterMode_ = FontCollectionFilterMode::None 169 | ); 170 | 171 | HRESULT PushFilter( 172 | uint32_t selectedFontIndex // Must be < fontCollectionList_.size() 173 | ); 174 | 175 | HRESULT PopFilter( 176 | uint32_t newFilterLevel 177 | ); 178 | 179 | HRESULT CreateDefaultFontSet(); 180 | 181 | enum AppendLogMode 182 | { 183 | AppendLogModeImmediate, 184 | AppendLogModeCached, 185 | AppendLogModeMessageBox, 186 | }; 187 | void AppendLog(AppendLogMode appendLogMode, const wchar_t* logMessage, ...); 188 | 189 | protected: 190 | HWND hwnd_ = nullptr; 191 | HMONITOR hmonitor_ = nullptr; 192 | HMODULE dwriteModule_ = nullptr; 193 | HIMAGELIST fontCollectionImageList_ = nullptr; 194 | 195 | ComPtr dwriteFactory_; 196 | ComPtr renderingParams_; 197 | ComPtr renderTarget_; 198 | ComPtr fontSet_; 199 | ComPtr fontCollection_; 200 | 201 | uint32_t fontColor_ = 0xFF000000; 202 | uint32_t backgroundColor_ = 0xFFFFFFFF; 203 | uint32_t highlightColor_ = 0xFFFFFFFF; 204 | uint32_t highlightBackgroundColor_ = 0xFFFF0000; 205 | uint32_t faintSelectionColor_ = 0xFF808080; 206 | uint32_t currentLanguageIndex_ = 0; // English US 207 | bool includeRemoteFonts_ = false; 208 | bool wantSortedFontList_ = true; 209 | bool showFontPreview_ = true; 210 | 211 | std::vector fontCollectionList_; 212 | std::vector fontCollectionFilters_; 213 | std::wstring cachedLog_; 214 | std::map fontCollectionListStringMap_; 215 | FontCollectionFilterMode filterMode_ = FontCollectionFilterMode::TypographicFamilyName; 216 | 217 | private: 218 | // No copy construction allowed. 219 | MainWindow(const MainWindow& b); 220 | MainWindow& operator=(const MainWindow&); 221 | public: 222 | ~MainWindow(); 223 | }; 224 | -------------------------------------------------------------------------------- /FontSetViewer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fdwr/FontSetViewer/76c7ac6e16f3c01d1523fda439e021e0e1c0ce39/FontSetViewer.png -------------------------------------------------------------------------------- /FontSetViewer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26228.12 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FontSetViewer", "FontSetViewer.vcxproj", "{04E57A88-8763-438D-8E73-8573C01A6AC4}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {04E57A88-8763-438D-8E73-8573C01A6AC4}.Debug|x64.ActiveCfg = Debug|x64 17 | {04E57A88-8763-438D-8E73-8573C01A6AC4}.Debug|x64.Build.0 = Debug|x64 18 | {04E57A88-8763-438D-8E73-8573C01A6AC4}.Debug|x86.ActiveCfg = Debug|Win32 19 | {04E57A88-8763-438D-8E73-8573C01A6AC4}.Debug|x86.Build.0 = Debug|Win32 20 | {04E57A88-8763-438D-8E73-8573C01A6AC4}.Release|x64.ActiveCfg = Release|x64 21 | {04E57A88-8763-438D-8E73-8573C01A6AC4}.Release|x64.Build.0 = Release|x64 22 | {04E57A88-8763-438D-8E73-8573C01A6AC4}.Release|x86.ActiveCfg = Release|Win32 23 | {04E57A88-8763-438D-8E73-8573C01A6AC4}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /FontSetViewer.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {04E57A88-8763-438D-8E73-8573C01A6AC4} 24 | Win32Proj 25 | 10.0.16299.0 26 | 27 | 28 | 29 | Application 30 | true 31 | v141 32 | Unicode 33 | 34 | 35 | Application 36 | false 37 | v141 38 | Unicode 39 | true 40 | 41 | 42 | Application 43 | true 44 | v141 45 | Unicode 46 | 47 | 48 | Application 49 | false 50 | v141 51 | Unicode 52 | true 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | true 74 | $(SolutionDir)\.$(Configuration)_$(PlatformTarget)\ 75 | $(SolutionDir)\.$(Configuration)_$(PlatformTarget)\ 76 | false 77 | 78 | 79 | false 80 | $(SolutionDir)\.$(Configuration)_$(PlatformTarget)\ 81 | $(SolutionDir)\.$(Configuration)_$(PlatformTarget)\ 82 | false 83 | 84 | 85 | $(SolutionDir)\.$(Configuration)_$(PlatformTarget)\ 86 | $(SolutionDir)\.$(Configuration)_$(PlatformTarget)\ 87 | false 88 | 89 | 90 | $(SolutionDir)\.$(Configuration)_$(PlatformTarget)\ 91 | $(SolutionDir)\.$(Configuration)_$(PlatformTarget)\ 92 | false 93 | false 94 | 95 | 96 | 97 | WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) 98 | MultiThreadedDebugDLL 99 | Level3 100 | ProgramDatabase 101 | Disabled 102 | Use 103 | precomp.h 104 | 105 | 106 | MachineX86 107 | true 108 | Windows 109 | d2d1.lib;DWrite.lib;comctl32.lib;msimg32.lib;usp10.lib;shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 110 | 111 | 112 | 113 | 114 | WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 115 | MultiThreaded 116 | Level3 117 | ProgramDatabase 118 | Use 119 | precomp.h 120 | true 121 | 122 | 123 | MachineX86 124 | true 125 | Windows 126 | true 127 | true 128 | d2d1.lib;DWrite.lib;comctl32.lib;msimg32.lib;usp10.lib;shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 129 | 130 | 131 | 132 | 133 | d2d1.lib;DWrite.lib;comctl32.lib;msimg32.lib;usp10.lib;shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 134 | 135 | 136 | Use 137 | 138 | 139 | precomp.h 140 | 141 | 142 | 143 | 144 | d2d1.lib;DWrite.lib;comctl32.lib;msimg32.lib;usp10.lib;shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 145 | true 146 | 147 | 148 | MultiThreaded 149 | Use 150 | precomp.h 151 | true 152 | ProgramDatabase 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | Create 165 | Create 166 | Create 167 | Create 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | MIT License with more permissive conditions 2 | 3 | Copyright (c) 2018 Dwayne Robinson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | substantial portions of the software. Copyright and attribution may be omitted 14 | from copied code snippets or even entire code files, so long as the project 15 | isn't copied in substantial whole and marked as your own. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DWrite FontSetViewer 2 | Simple utility to display items in an IDWriteFontSet. 3 | 4 | - Preview item in font 5 | - Displays axis information (on Windows 10+ RS3 Fall Creator's Update Build 10.0.16170 1709) 6 | - Browse/sort/filter by item name strings 7 | - Load custom fonts (no need to install them first, just File/Open or drag&drop) 8 | - Copy names to clipboard 9 | 10 | C++, compiled with Visual Studio 2017 RC. 11 | 12 | ![Image of FontSetViewer](FontSetViewer.png) 13 | -------------------------------------------------------------------------------- /common/AutoResource.h: -------------------------------------------------------------------------------- 1 | //+--------------------------------------------------------------------------- 2 | // 3 | // Copyright (c) Microsoft, 2008. All rights reserved. 4 | // 5 | // Contents: Automatic resource management. 6 | // 7 | // Author: Dwayne Robinson (dwayner@microsoft.com) 8 | // 9 | // History: 2007-07-30 dwayner Created for GDI 10 | // 2008-02-09 dwayner Ported to DWrite 11 | // 2009-12-16 dwayner Updated to support __cdecl too (fclose) 12 | // 2010-07-08 dwayner Updated to handle ref counting policies 13 | // 2014-10-06 dwayner Changed to template typedefs 14 | // 15 | //---------------------------------------------------------------------------- 16 | #pragma once 17 | 18 | 19 | // This class mostly obviates the following custom classes 20 | // (which are mostly just variants of various smart resource holders): 21 | // 22 | // Common/SmartPtr.h : ScopedPtr 23 | // Platform/AccessToken.h : AccessToken 24 | // Platform/Win32/Win32Handle.h : Win32Handle 25 | // Platform/Win32/SecurityHelpers.h : AutoLocalFree 26 | // Platform/Win32/GdiHelpers.h : GdiHandle 27 | // 28 | // It partially obviates these, satisfying the destructors 29 | // and getters, but the classes have more complex constructors, 30 | // meaning they would require inheritance. 31 | // 32 | // Platform/MemorySection.h : MemoryView, MemorySection 33 | // 34 | // With a free function (given a resource releaser that requires multiple 35 | // parameters), it satisfies these too. 36 | // 37 | // Platform/Win32/WaitHandle.h 38 | // 39 | // Example usage: 40 | // 41 | // GdiPenHandle gdiPenHandle; 42 | // WaitHandleResource waitHandle; 43 | // ComPtr comPtr; 44 | 45 | 46 | // Policy to determine how the resource is released when done, acquired if more owners want to hold 47 | // a reference to it, or zero-initialized. The resource type itself should generally be a simple 48 | // trivially copyable thing such as a pointer or handle type. 49 | template // type of the resource held onto 50 | struct DefaultResourceTypePolicy 51 | { 52 | inline static void InitializeEmpty(_Out_ ResourceType* resource) throw() 53 | { 54 | // Most resources (pointers to memory, HGLOBALS, HGDIOBJ...) 55 | // are indicated as empty by setting to 0/NULL. If a resource 56 | // has special semantics, override this method, or supply a 57 | // different policy class. Any implementations of this function 58 | // should directly initialize the resource to the empty state 59 | // without calling any freeing functions. The AutoResource will 60 | // call Release first, which is where that policy should happen. 61 | 62 | *resource = 0; 63 | } 64 | 65 | inline static bool IsNull(ResourceType resource) throw() 66 | { 67 | return (resource == 0); 68 | } 69 | 70 | inline static void Acquire(ResourceType resource) throw() 71 | { 72 | // Do nothing. 73 | } 74 | 75 | inline static void Release(ResourceType resource) throw() 76 | { 77 | // Do nothing. 78 | } 79 | 80 | // Define as true if the type's policy (such as a COM interface) allows multiple AutoResource's 81 | // to hold the same resource, either because it is reference counted or because releasing it 82 | // will not destroy the resource. If false, then exactly one AutoResource should be the true 83 | // owner, and any other references should be weak. To avoid run-time surprises, it is a 84 | // compile-time error to call certain methods using another AutoResource as a parameter, since 85 | // that would lead to a two-owner situation. You can always still initialize from the raw 86 | // unscoped resource though (such as void* or HANDLE). 87 | static const bool AllowsMultipleReferences = false; 88 | }; 89 | 90 | 91 | // Holds either a simple non-ref counted resource, such as a handle, raw 92 | // memory, or a ref-counted interface, freeing it upon destruction (or not, 93 | // depending on the policy implementation). 94 | template < 95 | typename ResourceType = void*, // type of the resource held onto 96 | typename ResourceTypePolicy = DefaultResourceTypePolicy, 97 | typename BaseResourceType = ResourceType // type as known by the resource releaser (like HGDIOBJ vs HBITMAP) 98 | > 99 | class AutoResource 100 | { 101 | public: 102 | using Self = AutoResource; 103 | 104 | AutoResource(ResourceType resource) 105 | : resource_(resource) 106 | { 107 | // Acquire is generally a no-op, but it matters for ref-counted objects. 108 | ResourceTypePolicy::Acquire(resource_); 109 | } 110 | 111 | AutoResource(Self const& other) 112 | : resource_(other.resource_) 113 | { 114 | // Acquire is generally a no-op, but it matters for ref-counted objects. 115 | static_assert(ResourceTypePolicy::AllowsMultipleReferences, "This function is only useable on resource types that allow multiple strong references."); 116 | ResourceTypePolicy::Acquire(resource_); 117 | } 118 | 119 | inline AutoResource() 120 | { 121 | ResourceTypePolicy::InitializeEmpty(Cast(&resource_)); 122 | } 123 | 124 | inline ~AutoResource() throw() 125 | { 126 | // Notice we merely free the resource and do not zero the resource, 127 | // since we actually prefer to leave the value intact, not because 128 | // it's one fewer write back out to memory, but because it leaves a 129 | // more debuggable trace). If the derived class is a non-owning pointer, 130 | // the free is a no-op. 131 | 132 | ResourceTypePolicy::Release(resource_); 133 | } 134 | 135 | // Compiler generated assignment and copy construction do the right thing 136 | // here, since simple resources don't have any complex behavior. 137 | 138 | // Implicitly promote to the resource type for all the times the resource 139 | // handle/pointer is passed by value to a function. 140 | inline operator ResourceType() const throw() 141 | { 142 | return resource_; 143 | } 144 | 145 | // Explicit getter for times when the compiler becomes confused 146 | // during overload resolution. 147 | inline ResourceType Get() const throw() 148 | { 149 | return resource_; 150 | } 151 | 152 | // Implicitly promote to a reference to the resource type for when passed 153 | // to functions that want a reference, and for swapping resources in/out. 154 | // If modified directly, the caller is responsible for managing the object. 155 | // This intentionally does NOT have evil behavior of freeing the resource 156 | // since needing the address of a resource is not only for the sake of 157 | // out params - it's perfectly legitimate as an in param too, such as with 158 | // WaitForMultipleObjects(). 159 | inline operator ResourceType&() throw() 160 | { 161 | return resource_; 162 | } 163 | 164 | // Used when passed as an out parameter to any function, 165 | // or when the caller needs a pointer to a pointer. 166 | // If modified directly, the caller is responsible for managing the object. 167 | inline ResourceType* operator&() throw() 168 | { 169 | return &resource_; 170 | } 171 | 172 | // Explicitly named form. 173 | inline ResourceType* Address() throw() 174 | { 175 | return &resource_; 176 | } 177 | 178 | // Explicitly named form. 179 | inline ResourceType& Reference() throw() 180 | { 181 | return resource_; 182 | } 183 | 184 | // For calling methods of an interface. 185 | // 186 | // ComPtr cat; 187 | // cat->Meow(); 188 | inline ResourceType operator->() const throw() 189 | { 190 | return resource_; 191 | } 192 | 193 | // Set a new resource, acquiring the new resource before releasing the 194 | // old one, to prevent premature freeing issues with ref-counted objects 195 | // and because acquiring a resource is more likely to fail (with a 196 | // potential exception) than realising. For non-ref-counted objects, 197 | // the acquire is generally a no-op. 198 | inline ResourceType Set(ResourceType resource) 199 | { 200 | if (resource != resource_) 201 | { 202 | std::swap(resource_, resource); 203 | ResourceTypePolicy::Acquire(resource_); 204 | ResourceTypePolicy::Release(resource); 205 | } 206 | return resource_; 207 | } 208 | 209 | inline ResourceType Set(const Self& other) 210 | { 211 | static_assert(ResourceTypePolicy::AllowsMultipleReferences, "This function is only useable on resource types that allow multiple strong references."); 212 | return Set(other.resource_); 213 | } 214 | 215 | inline ResourceType Set(Self&& other) 216 | { 217 | if (other.resource_ != resource_) 218 | { 219 | ResourceType oldResource = resource_; 220 | resource_ = other.resource_; 221 | ResourceTypePolicy::InitializeEmpty(Cast(&other.resource_)); 222 | ResourceTypePolicy::Release(oldResource); 223 | } 224 | return resource_; 225 | } 226 | 227 | inline Self& operator=(ResourceType resource) 228 | { 229 | Set(resource); 230 | return *this; 231 | } 232 | 233 | inline Self& operator=(const Self& other) 234 | { 235 | static_assert(ResourceTypePolicy::AllowsMultipleReferences, "This function is only useable on resource types that allow multiple strong references."); 236 | Set(other.resource_); 237 | return *this; 238 | } 239 | 240 | // No check. Just set it directly. 241 | inline ResourceType SetDirectly(ResourceType resource) 242 | { 243 | DEBUG_ASSERT(IsNull()); 244 | resource_ = resource; 245 | return resource_; 246 | } 247 | 248 | void Clear() 249 | { 250 | ResourceType oldResource = resource_; 251 | ResourceTypePolicy::InitializeEmpty(Cast(&resource_)); 252 | ResourceTypePolicy::Release(oldResource); 253 | } 254 | 255 | // Abandon the resource without freeing it. 256 | inline void Abandon() throw() 257 | { 258 | ResourceTypePolicy::InitializeEmpty(Cast(&resource_)); 259 | } 260 | 261 | // Like ATL's CComPtr, VS's _com_ptr_t, and the CLR's ptr::Attach, 262 | // this Attach does not increment the ref count, which is symmetric 263 | // to Detach(). 264 | // * No self-assignment checking is done here. 265 | ResourceType Attach(ResourceType resource) throw() 266 | { 267 | DEBUG_ASSERT(resource != resource_); 268 | std::swap(resource_, resource); 269 | ResourceTypePolicy::Release(resource); 270 | return resource_; 271 | } 272 | 273 | // Lets go of the resource without freeing it, but returning it 274 | // to the caller. 275 | ResourceType Detach() throw() 276 | { 277 | ResourceType resource = resource_; 278 | ResourceTypePolicy::InitializeEmpty(Cast(&resource_)); 279 | return resource; 280 | } 281 | 282 | // For the common transfer usage, where one container directly steals another, 283 | // it simplifies to: 284 | // 285 | // p1.Attach(p2.Detach()) -> p1.Steal(p2) 286 | // 287 | inline void Steal(Self& other) throw() 288 | { 289 | swap(other); 290 | other.Clear(); 291 | } 292 | 293 | inline bool IsNull() const throw() 294 | { 295 | return ResourceTypePolicy::IsNull(resource_); 296 | } 297 | 298 | inline bool IsSet() const throw() 299 | { 300 | return !ResourceTypePolicy::IsNull(resource_); 301 | } 302 | 303 | //////////////////// 304 | // STL aliases 305 | 306 | inline void clear() 307 | { 308 | Clear(); 309 | } 310 | 311 | inline void swap(ResourceType& resource) throw() 312 | { 313 | std::swap(resource_, resource); 314 | } 315 | 316 | inline void reset(ResourceType resource) 317 | { 318 | Set(resource); 319 | } 320 | 321 | // there is no 'release' alias, since the C++ auto_ptr does something 322 | // different than typically expected (detaches rather than frees). 323 | 324 | protected: 325 | inline BaseResourceType* Cast(ResourceType* resource) 326 | { 327 | // Cast resource to the base type, such as an interface: 328 | // IDWriteFont/IDWriteTextLayout/IShellFolder -> IUnknown 329 | // or handle: 330 | // HBITMAP/HPEN/HFONT -> HGDIOBJ. 331 | // 332 | // Such an explicit cast isn't normally needed since the resource 333 | // is passed by pointer and implicitly castable, but a pointer to 334 | // a pointer is not directly castable. Trying to pass IShellFolder** 335 | // to a function that takes IUnknown** is normally a compile error 336 | // (even though it would actually work just fine). 337 | 338 | return reinterpret_cast(resource); 339 | } 340 | 341 | ResourceType resource_; // could be void*, HANDLE, FILE*, GDIOBJ... 342 | }; 343 | 344 | 345 | // Overload std::swap for AutoResource so that it can work with standard algorithms. 346 | namespace std 347 | { 348 | template < 349 | typename ResourceType, // type of the resource held onto 350 | typename ResourceTypePolicy, // policy for acquiring/releasing this resource 351 | typename BaseResourceType // type as known by the resource releaser (like HGDIOBJ vs HBITMAP) 352 | > 353 | void swap( 354 | AutoResource& lhs, 355 | AutoResource& rhs 356 | ) throw() 357 | { 358 | lhs.swap(rhs); 359 | } 360 | } 361 | 362 | 363 | //////////////////////////////////////// 364 | // Various handle types 365 | 366 | template < 367 | typename ResourceType, // underlying type of the resource held onto (e.g. HGDIOBJ instead of HFONT) 368 | typename ResourceReleaserSignature, // function prototype of the releasing function 369 | ResourceReleaserSignature ResourceReleaser // address of function to release object 370 | > 371 | struct HandleResourceTypePolicy : public DefaultResourceTypePolicy 372 | { 373 | inline static void Release(ResourceType resource) throw() 374 | { 375 | if (resource != 0) 376 | { 377 | ResourceReleaser(resource); 378 | } 379 | } 380 | 381 | // Handles are owned by exactly one owner. 382 | static const bool AllowsMultipleReferences = false; 383 | }; 384 | 385 | 386 | // Releasing function for WaitHandleResource to pass to use with HandleResourceTypePolicy. 387 | inline void WaitHandleUnregister(HANDLE handle) throw() 388 | { 389 | if (handle != nullptr) 390 | { 391 | UnregisterWaitEx(handle, nullptr); // return value unactionable 392 | } 393 | } 394 | 395 | 396 | using GdiDeviceContext = AutoResource >; 397 | using GdiPenHandle = AutoResource, HGDIOBJ>; 398 | using GdiFontHandle = AutoResource, HGDIOBJ>; 399 | using GdiBitmapHandle = AutoResource, HGDIOBJ>; 400 | using GdiRegionHandle = AutoResource, HGDIOBJ>; 401 | using GlobalMemoryResource = AutoResource >; 402 | using LocalMemoryResource = AutoResource >; 403 | using FileHandle = AutoResource >; 404 | using CstdioFileHandle = AutoResource >; 405 | using ScopedMemory = AutoResource >; 406 | using ModuleHandle = AutoResource >; 407 | using WindowHandle = AutoResource >; 408 | using MemoryViewResource = AutoResource >; 409 | using MemorySectionResource = AutoResource >; 410 | using WaitHandleResource = AutoResource>; 411 | 412 | 413 | //////////////////////////////////////// 414 | // Basic COM pointer. 415 | 416 | struct ComResourceTypePolicy : public DefaultResourceTypePolicy 417 | { 418 | inline static void Acquire(_Inout_opt_ IUnknown* resource) throw() 419 | { 420 | if (resource != nullptr) 421 | { 422 | resource->AddRef(); 423 | } 424 | } 425 | 426 | inline static void Release(_Inout_opt_ IUnknown* resource) throw() 427 | { 428 | if (resource != nullptr) 429 | { 430 | resource->Release(); 431 | } 432 | } 433 | 434 | static const bool AllowsMultipleReferences = true; 435 | }; 436 | 437 | template 438 | using ComPtr = AutoResource; 439 | 440 | 441 | //////////////////////////////////////// 442 | // Raw unowned pointer, which is not notably useful, but it can be used as a 443 | // template argument without the caller/container worrying about whether it 444 | // is raw or owned, and the reader is clear that it's a weak pointer. 445 | 446 | static_assert(sizeof(uint32_t*) == sizeof(void*), "Expect all pointers have same size, such as void* and resourceType*"); 447 | 448 | template 449 | struct UnownedMemoryPointerPolicy : public DefaultResourceTypePolicy 450 | { 451 | // Allow multiple references because the non-owning AutoResource does not strongly hold it anyway. 452 | static const bool AllowsMultipleReferences = true; 453 | }; 454 | 455 | template 456 | using UnownedMemoryPointer = AutoResource, void*>; 457 | 458 | 459 | //////////////////////////////////////// 460 | // Scoped memory pointer. 461 | // No two scoped pointers may own the same object, and trying to assign one to another is a compile 462 | // error (to avoid suprises such as with std::auto_ptr), but you can transfer them explicitly by 463 | // Steal'ing from another, or by Detach/Attach. 464 | 465 | template 466 | struct OwnedMemoryPointerPolicy : public DefaultResourceTypePolicy 467 | { 468 | inline static void Release(ResourceType resource) throw() 469 | { 470 | if (resource != nullptr) 471 | { 472 | delete resource; 473 | } 474 | } 475 | 476 | static const bool AllowsMultipleReferences = false; 477 | }; 478 | 479 | template 480 | using OwnedMemoryPointer = AutoResource, ResourceType*>; 481 | -------------------------------------------------------------------------------- /common/Common.cpp: -------------------------------------------------------------------------------- 1 | //+--------------------------------------------------------------------------- 2 | // 3 | // Copyright (c) Microsoft, 2008. All rights reserved. 4 | // 5 | // Contents: Shared utility functions. 6 | // 7 | //---------------------------------------------------------------------------- 8 | #include "precomp.h" 9 | 10 | 11 | // Internal version. Should call the other two overloads publicly. 12 | void GetFormattedString(IN OUT std::wstring& returnString, bool shouldConcatenate, const wchar_t* formatString, va_list vargs) 13 | { 14 | if (formatString != nullptr) 15 | { 16 | const size_t increasedLen = _vscwprintf(formatString, vargs) + 1; // Get string length, plus one for NUL 17 | const size_t oldLen = shouldConcatenate ? returnString.size() : 0; 18 | const size_t newLen = oldLen + increasedLen; 19 | returnString.resize(newLen); 20 | _vsnwprintf_s(&returnString[oldLen], increasedLen, increasedLen, formatString, vargs); 21 | returnString.resize(newLen - 1); // trim off the NUL 22 | } 23 | } 24 | 25 | 26 | void GetFormattedString(OUT std::wstring& returnString, const wchar_t* formatString, ...) 27 | { 28 | va_list vargs = nullptr; 29 | va_start(vargs, formatString); // initialize variable arguments 30 | GetFormattedString(OUT returnString, /*shouldConcatenate*/false, formatString, vargs); 31 | va_end(vargs); // Reset variable arguments 32 | } 33 | 34 | 35 | void AppendFormattedString(IN OUT std::wstring& returnString, const wchar_t* formatString, ...) 36 | { 37 | va_list vargs = nullptr; 38 | va_start(vargs, formatString); // initialize variable arguments 39 | GetFormattedString(OUT returnString, /*shouldConcatenate*/true, formatString, vargs); 40 | va_end(vargs); // Reset variable arguments 41 | } 42 | 43 | 44 | namespace 45 | { 46 | struct LastError 47 | { 48 | LastError() 49 | { 50 | hr = S_OK; 51 | SetLastError(NOERROR); 52 | } 53 | 54 | // Update the HRESULT if a Win32 error occurred. 55 | // Otherwise, if success or already have an error, 56 | // leave the existing error code alone. This 57 | // preserves the first error code that happens, and 58 | // it avoids later check's of cleanup functions from 59 | // clobbering the error. 60 | void Check() 61 | { 62 | if (SUCCEEDED(hr)) 63 | { 64 | DWORD lastError = GetLastError(); 65 | if (lastError != NOERROR) 66 | { 67 | hr = HRESULT_FROM_WIN32(GetLastError()); 68 | } 69 | } 70 | } 71 | 72 | HRESULT hr; 73 | }; 74 | } 75 | 76 | 77 | HRESULT CopyToClipboard(const std::wstring& text) 78 | { 79 | // Copies selected text to clipboard. 80 | 81 | LastError lastError; 82 | 83 | const uint32_t textLength = static_cast(text.length()); 84 | const wchar_t* rawInputText = text.c_str(); 85 | 86 | // Open and empty existing contents. 87 | if (OpenClipboard(nullptr)) 88 | { 89 | if (EmptyClipboard()) 90 | { 91 | // Allocate room for the text 92 | const uint32_t byteSize = sizeof(wchar_t) * (textLength + 1); 93 | HGLOBAL hClipboardData = GlobalAlloc(GMEM_DDESHARE | GMEM_ZEROINIT, byteSize); 94 | 95 | if (hClipboardData != nullptr) 96 | { 97 | void* memory = GlobalLock(hClipboardData); 98 | wchar_t* rawOutputText = reinterpret_cast(memory); 99 | 100 | if (memory != nullptr) 101 | { 102 | // Copy text to memory block. 103 | memcpy(rawOutputText, rawInputText, byteSize); 104 | rawOutputText[textLength] = '\0'; // explicit nul terminator in case there is none 105 | GlobalUnlock(hClipboardData); 106 | 107 | if (SetClipboardData(CF_UNICODETEXT, hClipboardData) != nullptr) 108 | { 109 | hClipboardData = nullptr; // system now owns the clipboard, so don't touch it. 110 | // lastError.hr = S_OK; 111 | } 112 | lastError.Check(); 113 | } 114 | lastError.Check(); 115 | GlobalFree(hClipboardData); // free if failed (still non-null) 116 | } 117 | lastError.Check(); 118 | } 119 | lastError.Check(); 120 | CloseClipboard(); 121 | } 122 | lastError.Check(); 123 | 124 | return lastError.hr; 125 | } 126 | 127 | 128 | HRESULT CopyImageToClipboard( 129 | HWND hwnd, 130 | HDC hdc, 131 | bool isUpsideDown 132 | ) 133 | { 134 | DIBSECTION sourceBitmapInfo = {}; 135 | HBITMAP sourceBitmap = (HBITMAP)GetCurrentObject(hdc, OBJ_BITMAP); 136 | GetObject(sourceBitmap, sizeof(sourceBitmapInfo), &sourceBitmapInfo); 137 | 138 | LastError lastError; 139 | 140 | if (sourceBitmapInfo.dsBm.bmBitsPixel <= 8 141 | || sourceBitmapInfo.dsBm.bmBits == nullptr 142 | || sourceBitmapInfo.dsBm.bmWidth <= 0 143 | || sourceBitmapInfo.dsBm.bmHeight <= 0) 144 | { 145 | // Don't support paletted images, only true color. 146 | // Only support bitmaps where the pixels are accessible. 147 | return E_NOTIMPL; 148 | } 149 | 150 | if (OpenClipboard(hwnd)) 151 | { 152 | if (EmptyClipboard()) 153 | { 154 | const size_t pixelsByteSize = sourceBitmapInfo.dsBmih.biSizeImage; 155 | const size_t bufferLength = sizeof(sourceBitmapInfo.dsBmih) + pixelsByteSize; 156 | HGLOBAL bufferHandle = GlobalAlloc(GMEM_MOVEABLE, bufferLength); 157 | uint8_t* buffer = reinterpret_cast(GlobalLock(bufferHandle)); 158 | 159 | // Copy the header. 160 | memcpy(buffer, &sourceBitmapInfo.dsBmih, sizeof(sourceBitmapInfo.dsBmih)); 161 | 162 | if (isUpsideDown) 163 | { 164 | // The image is a bottom-up orientation. Though, since 165 | // Windows legacy bitmaps are upside down too, the two 166 | // upside-downs cancel out. 167 | memcpy(buffer + sizeof(sourceBitmapInfo.dsBmih), sourceBitmapInfo.dsBm.bmBits, pixelsByteSize); 168 | } 169 | else 170 | { 171 | // We have a standard top-down image, but DIBs when shared 172 | // on the clipboard are actually upside down. Simply flipping 173 | // the height to negative works for a few applications, but 174 | // it confuses most. 175 | 176 | const size_t bytesPerRow = sourceBitmapInfo.dsBm.bmWidthBytes; 177 | uint8_t* destRow = buffer + sizeof(sourceBitmapInfo.dsBmih); 178 | uint8_t* sourceRow = reinterpret_cast(sourceBitmapInfo.dsBm.bmBits) 179 | + (sourceBitmapInfo.dsBm.bmHeight - 1) * bytesPerRow; 180 | 181 | // Copy each scanline in backwards order. 182 | for (long y = 0; y < sourceBitmapInfo.dsBm.bmHeight; ++y) 183 | { 184 | memcpy(destRow, sourceRow, bytesPerRow); 185 | sourceRow = PtrAddByteOffset(sourceRow, -ptrdiff_t(bytesPerRow)); 186 | destRow = PtrAddByteOffset(destRow, bytesPerRow); 187 | } 188 | } 189 | 190 | GlobalUnlock(bufferHandle); 191 | 192 | if (SetClipboardData(CF_DIB, bufferHandle) == nullptr) 193 | { 194 | lastError.Check(); 195 | GlobalFree(bufferHandle); 196 | } 197 | } 198 | lastError.Check(); 199 | 200 | CloseClipboard(); 201 | } 202 | lastError.Check(); 203 | 204 | return lastError.hr; 205 | } 206 | 207 | 208 | HRESULT PasteFromClipboard(OUT std::wstring& text) 209 | { 210 | // Copy Unicode text from clipboard. 211 | 212 | LastError lastError; 213 | 214 | if (OpenClipboard(nullptr)) 215 | { 216 | HGLOBAL hClipboardData = GetClipboardData(CF_UNICODETEXT); 217 | 218 | if (hClipboardData != nullptr) 219 | { 220 | // Get text and size of text. 221 | size_t byteSize = GlobalSize(hClipboardData); 222 | void* memory = GlobalLock(hClipboardData); // [byteSize] in bytes 223 | const wchar_t* rawText = reinterpret_cast(memory); 224 | uint32_t textLength = static_cast(wcsnlen(rawText, byteSize / sizeof(wchar_t))); 225 | 226 | if (memory != nullptr) 227 | { 228 | try 229 | { 230 | text.assign(rawText, textLength); 231 | // lastError.hr = S_OK; 232 | } 233 | catch (std::bad_alloc) 234 | { 235 | lastError.hr = E_OUTOFMEMORY; 236 | } 237 | 238 | GlobalUnlock(hClipboardData); 239 | } 240 | lastError.Check(); 241 | } 242 | lastError.Check(); 243 | CloseClipboard(); 244 | } 245 | lastError.Check(); 246 | 247 | return lastError.hr; 248 | } 249 | 250 | 251 | void TrimSpaces(IN OUT std::wstring& text) 252 | { 253 | // Trim space (U+0020) and tab. It does not trim all whitespace, like U+200X 254 | // or the new line controls. 255 | 256 | // Trim trailing spaces 257 | size_t lastPos = text.find_last_not_of(L" \t"); 258 | if (lastPos != std::string::npos) 259 | { 260 | text.erase(lastPos+1); 261 | } 262 | 263 | // Trim leading spaces 264 | size_t firstPos = text.find_first_not_of(L" \t"); 265 | if (firstPos != 0) 266 | { 267 | if (firstPos == std::string::npos) 268 | firstPos = text.size(); 269 | text.erase(0, firstPos); 270 | } 271 | } 272 | 273 | 274 | void GetCommandLineArguments(_Inout_ std::wstring& commandLine) 275 | { 276 | // Get the original command line argument string as a single string, 277 | // not the preparsed argv or CommandLineToArgvW, which fragments everything 278 | // into separate words which aren't really useable when you have your own 279 | // syntax to parse. Skip the module filename because we're only interested 280 | // in the parameters following it. 281 | 282 | // Skip the module name. 283 | const wchar_t* initialCommandLine = GetCommandLine(); 284 | wchar_t ch = initialCommandLine[0]; 285 | if (ch == '"') 286 | { 287 | do 288 | { 289 | ch = *(++initialCommandLine); 290 | } while (ch != '\0' && ch != '"'); 291 | if (ch == '"') 292 | ++initialCommandLine; 293 | } 294 | else 295 | { 296 | while (ch != '\0' && ch != ' ') 297 | { 298 | ch = *(++initialCommandLine); 299 | } 300 | } 301 | 302 | // Assign it and strip any leading or trailing spaces. 303 | auto commandLineLength = wcslen(initialCommandLine); 304 | commandLine.assign(initialCommandLine, &initialCommandLine[commandLineLength]); 305 | TrimSpaces(IN OUT commandLine); 306 | } 307 | 308 | 309 | void ConvertText( 310 | array_ref utf8text, 311 | OUT std::wstring& utf16text 312 | ) 313 | { 314 | // This function can only throw if out-of-memory when resizing utf16text. 315 | // If utf16text is already reserve()'d, no exception will happen. 316 | 317 | // Skip byte-order-mark. 318 | const static uint8_t utf8bom[] = {0xEF,0xBB,0xBF}; 319 | int32_t startingOffset = 0; 320 | if (utf8text.size() >= ARRAY_SIZE(utf8bom) 321 | && memcmp(utf8text.data(), utf8bom, ARRAY_SIZE(utf8bom)) == 0) 322 | { 323 | startingOffset = ARRAYSIZE(utf8bom); 324 | } 325 | 326 | utf16text.resize(utf8text.size()); 327 | 328 | // Convert UTF-8 to UTF-16. 329 | int32_t charsConverted = 330 | MultiByteToWideChar( 331 | CP_UTF8, 332 | 0, // no flags for UTF8 (we allow invalid characters for testing) 333 | (LPCSTR)&utf8text[startingOffset], 334 | int32_t(utf8text.size() - startingOffset), 335 | OUT const_cast(utf16text.data()), // workaround issue http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#2391 336 | int32_t(utf16text.size()) 337 | ); 338 | 339 | // Shrink to actual size. 340 | utf16text.resize(charsConverted); 341 | } 342 | 343 | 344 | void ConvertText( 345 | array_ref utf16text, 346 | OUT std::string& utf8text 347 | ) 348 | { 349 | // Convert UTF-8 to UTF-16. 350 | int32_t charsConverted = 351 | WideCharToMultiByte( 352 | CP_UTF8, 353 | 0, // no flags for UTF8 (we allow invalid characters for testing) 354 | utf16text.data(), 355 | int32_t(utf16text.size()), 356 | nullptr, // null destination buffer the first time to get estimate. 357 | 0, 358 | nullptr, // defaultChar 359 | nullptr // usedDefaultChar 360 | ); 361 | 362 | utf8text.resize(charsConverted); 363 | 364 | // Convert UTF-8 to UTF-16. 365 | WideCharToMultiByte( 366 | CP_UTF8, 367 | 0, // no flags for UTF8 (we allow invalid characters for testing) 368 | utf16text.data(), 369 | int32_t(utf16text.size()), 370 | OUT &utf8text[0], 371 | int32_t(utf8text.size()), 372 | nullptr, // defaultChar 373 | nullptr // usedDefaultChar 374 | ); 375 | } 376 | -------------------------------------------------------------------------------- /common/Common.h: -------------------------------------------------------------------------------- 1 | // Include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | #pragma once 5 | 6 | 7 | //////////////////////////////////////// 8 | // Basic application execution functions: 9 | 10 | template void ZeroStructure(T& structure) 11 | { 12 | memset(&structure, 0, sizeof(T)); 13 | } 14 | 15 | bool TestBit(void const* memoryBase, size_t bitIndex) throw(); 16 | bool ClearBit(void* memoryBase, size_t bitIndex) throw(); 17 | bool SetBit(void* memoryBase, size_t bitIndex) throw(); 18 | 19 | 20 | template 21 | T* PtrAddByteOffset(T* p, size_t offset) 22 | { 23 | return reinterpret_cast(reinterpret_cast(p) + offset); 24 | } 25 | 26 | template 27 | const T* PtrAddByteOffset(const T* p, size_t offset) 28 | { 29 | return reinterpret_cast(reinterpret_cast(p) + offset); 30 | } 31 | 32 | 33 | template 34 | struct DeferCleanupType 35 | { 36 | public: 37 | explicit DeferCleanupType(FunctorType f) : f_(f) {} 38 | ~DeferCleanupType() { f_(); } 39 | 40 | private: 41 | FunctorType f_; 42 | }; 43 | 44 | template 45 | DeferCleanupType DeferCleanup(FunctorType f) { return DeferCleanupType(f); } 46 | 47 | 48 | template class vector; 49 | 50 | template 51 | class array_ref 52 | { 53 | // Enable constructors to read other variants, mainly for copy constructors 54 | // from a non-const to const array_ref. 55 | template 56 | friend class array_ref; 57 | 58 | public: 59 | // Types 60 | using value_type = T; 61 | using pointer = T*; 62 | using reference = T&; 63 | using iterator = pointer; 64 | using const_reference = T const&; 65 | using const_iterator = T const*; 66 | using reverse_iterator = std::reverse_iterator; 67 | using const_reverse_iterator = std::reverse_iterator; 68 | using size_type = size_t; 69 | using difference_type = ptrdiff_t; 70 | 71 | // construct/copy 72 | array_ref() = default; 73 | array_ref(array_ref const& other) = default; 74 | //array_ref(reference item) : begin_(&item), end_(begin_ + sizeof(T)) {} 75 | array_ref(pointer array, size_t length) : begin_(array), end_(array + length) {} 76 | array_ref(pointer begin, pointer end) : begin_(begin), end_(end) {} 77 | template array_ref(array_ref const& other) : begin_(other.begin_), end_(other.end_) {} 78 | template array_ref(const T(&a)[N]) : begin_(&a[0]), end_(begin_ + N) {} 79 | 80 | // Otherwise try to construct from a standard container like std::vector, std::string, std::array. 81 | // The const_cast is unfortunate, due to std::basic_string missing the non-const version of data() 82 | // like std::vector and std::array do. So, leaving until the issue is fixed. 83 | // http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#2391 84 | template array_ref(V& v) : begin_(const_cast(v.data())), end_(begin_ + v.size()) {} 85 | template array_ref(V const& v) : begin_(v.data()), end_(begin_ + v.size()) {} 86 | 87 | template array_ref& operator=(array_ref const& other) { begin_ = other.begin_; end_ = other.end_; return *this; } 88 | 89 | // Most useful for recasting any array_ref into an uint8_t array. 90 | // Note you should use this with the same judiciousness as reinterpret_cast. 91 | template static array_ref reinterpret(V& v) 92 | { 93 | return array_ref( 94 | reinterpret_cast(const_cast(v.data())), 95 | reinterpret_cast(const_cast(v.data() + v.size())) 96 | ); 97 | } 98 | 99 | template 100 | static array_ref wrap(U& u) 101 | { 102 | array_ref singleElementArray(std::addressof(u), std::addressof(u) + 1); 103 | return reinterpret(singleElementArray); 104 | } 105 | 106 | //template static array_ref reinterpret(V const& v) 107 | //{ 108 | // return array_ref( 109 | // reinterpret_cast(v.data()), 110 | // reinterpret_cast(v.data() + v.size()) 111 | // ); 112 | //} 113 | 114 | // Iterators 115 | iterator begin() { return begin_; } 116 | iterator end() { return end_; } 117 | const_iterator cbegin() const { return begin_; } 118 | const_iterator cend() const { return end_; }; 119 | reverse_iterator rbegin() { return reverse_iterator(begin_); } 120 | reverse_iterator rend() { return reverse_iterator(end_); } 121 | const_reverse_iterator crbegin() const { return const_reverse_iterator(begin_); } 122 | const_reverse_iterator crend() const { return const_reverse_iterator(end_); } 123 | 124 | // Capacity 125 | size_type size() const { return end_ - begin_; }; 126 | size_type max_size() const { return size_t / sizeof(T); } 127 | bool empty() const { return begin_ == end_; } 128 | 129 | // Element access 130 | T& operator[](size_t i) { return begin_[i]; } 131 | T const& operator[](size_t i) const { return begin_[i]; } 132 | T& front() { return *begin_; } 133 | T const& front() const { return *begin_; } 134 | T& back() { return *(end_ - 1); } 135 | T const& back() const { return *(end_ - 1); } 136 | T* data() { return begin_; } 137 | T const* data() const { return begin_; } 138 | 139 | // Outgoing conversion operators 140 | // explicit operator vector() const ; 141 | // template 142 | // explicit operator basic_string< T, traits, Allocator >() const ; 143 | 144 | // Mutators 145 | void clear() { begin_ = end_ = nullptr; } 146 | void remove_prefix(size_type n) { begin_ += n; assert(begin_ <= end_); } 147 | void remove_suffix(size_type n) { end_ -= n; assert(begin_ <= end_); }; 148 | void pop_back() { --end_; assert(begin_ <= end_); } 149 | void pop_front() { ++begin_; assert(begin_ <= end_); } 150 | 151 | protected: 152 | pointer begin_ = nullptr; 153 | pointer end_ = nullptr; 154 | }; 155 | 156 | 157 | using byte_array_ref = array_ref; 158 | using const_byte_array_ref = array_ref; 159 | 160 | 161 | // Range of iterators that can be used in a ranged for loop. 162 | template 163 | class iterator_range 164 | { 165 | public: // types 166 | typedef ForwardIteratorType iterator; 167 | typedef ForwardIteratorType const_iterator; 168 | typedef size_t size_type; 169 | 170 | public: // construction, assignment 171 | template 172 | iterator_range(iterator begin, iterator end) 173 | : begin_(begin), end_(end) 174 | { } 175 | 176 | template 177 | iterator_range(const IteratorRangeType& range) 178 | : begin_(range.begin()), end_(range.end()) 179 | { } 180 | 181 | template 182 | iterator_range& operator=(const IteratorRangeType& range) 183 | { 184 | begin_ = range.begin(); 185 | end_ = range.end(); 186 | return *this; 187 | } 188 | 189 | iterator const& begin() const 190 | { 191 | return begin_; 192 | } 193 | 194 | iterator const& end() const 195 | { 196 | return end_; 197 | } 198 | 199 | bool equals(const iterator_range& other) const 200 | { 201 | return begin_ == other.begin_ && end_ == other.end_; 202 | } 203 | 204 | bool operator ==(const iterator_range& other) const 205 | { 206 | return equals(other); 207 | } 208 | 209 | bool empty() const 210 | { 211 | return begin_ == end_; 212 | } 213 | 214 | size_type size() const 215 | { 216 | return std::distance(begin_, end_); 217 | } 218 | 219 | protected: 220 | iterator begin_; 221 | iterator end_; 222 | }; 223 | 224 | 225 | template 226 | iterator_range make_iterator_range(T begin, T end) 227 | { 228 | return iterator_range(begin, end); 229 | } 230 | 231 | 232 | // Format into a std::string rather than raw character buffer or ios stream. 233 | void GetFormattedString(OUT std::wstring& returnString, const wchar_t* formatString, ...); 234 | void AppendFormattedString(IN OUT std::wstring& returnString, const wchar_t* formatString, ...); 235 | 236 | // Variable length version. Should always call the other two overloads unless you forward a variable parameter list. 237 | void GetFormattedString(IN OUT std::wstring& returnString, bool shouldConcatenate, const wchar_t* formatString, va_list vargs); 238 | 239 | HRESULT ReadTextFile(const wchar_t* filename, OUT std::wstring& text); 240 | HRESULT ReadBinaryFile(const wchar_t* filename, OUT std::vector& fileBytes); 241 | HRESULT CopyToClipboard(const std::wstring& text); 242 | HRESULT PasteFromClipboard(OUT std::wstring& text); 243 | 244 | void GetCommandLineArguments(_Inout_ std::wstring& commandLine); 245 | 246 | void TrimSpaces(IN OUT std::wstring& text); 247 | void ConvertText(array_ref utf8text, OUT std::wstring& utf16text); 248 | void ConvertText(array_ref utf16text, OUT std::string& utf8text); 249 | -------------------------------------------------------------------------------- /common/FileHelpers.h: -------------------------------------------------------------------------------- 1 | //+--------------------------------------------------------------------------- 2 | // 3 | // Contents: Helper for enumerating files. 4 | // 5 | // History: 2013-10-04 dwayner Created 6 | // 7 | //---------------------------------------------------------------------------- 8 | #pragma once 9 | 10 | HRESULT ReadTextFile(const wchar_t* filename, OUT std::wstring& text) throw(); // Read UTF-8 or ASCII 11 | HRESULT WriteTextFile(const wchar_t* filename, const std::wstring& text) throw(); // Write as UTF-8 12 | HRESULT WriteTextFile(const wchar_t* filename, __in_ecount(textLength) const wchar_t* text, uint32_t textLength) throw(); // Write as UTF-8 13 | HRESULT ReadBinaryFile(const wchar_t* filename, OUT std::vector& fileBytes); 14 | HRESULT WriteBinaryFile(const wchar_t* filename, const std::vector& fileData); 15 | HRESULT WriteBinaryFile(_In_z_ const wchar_t* filename, _In_reads_bytes_(fileDataSize) const void* fileData, uint32_t fileDataSize); 16 | 17 | std::wstring GetActualFileName(array_ref fileName); 18 | std::wstring GetFullFileName(array_ref fileName); 19 | 20 | // Expand the given path and mask into a list of nul-separated filenames. 21 | HRESULT EnumerateMatchingFiles( 22 | __in_z_opt const wchar_t* fileDirectory, 23 | __in_z_opt wchar_t const* originalFileMask, 24 | IN OUT std::wstring& fileNames // Appended onto any existing names. It's safe for this to alias fileDirectory. 25 | ); 26 | 27 | uint8_t* NtosCompressData(uint8_t* sourceBuffer, uint32_t sourceBufferSize, DWORD* dwSizeOut); 28 | 29 | uint8_t* NtosDecompressData(_In_reads_bytes_(sourceBufferSize) uint8_t* sourceBuffer, uint32_t sourceBufferSize, DWORD* dwSizeOut); 30 | 31 | const wchar_t* FindFileNameStart(array_ref fileName); 32 | 33 | bool FileContainsWildcard(array_ref fileName); 34 | 35 | #if 0 36 | //+--------------------------------------------------------------------------- 37 | // 38 | // Contents: Helper for enumerating files. 39 | // 40 | // History: 2013-10-04 dwayner Created 41 | // 42 | //---------------------------------------------------------------------------- 43 | #pragma once 44 | 45 | HRESULT ReadTextFile(const wchar_t* filename, OUT std::wstring& text); // Read UTF-8 or ASCII 46 | HRESULT WriteTextFile(const wchar_t* filename, const std::wstring& text); // Write as UTF-8 47 | HRESULT WriteTextFile(const wchar_t* filename, __in_ecount(textLength) const wchar_t* text, uint32_t textLength); // Write as UTF-8 48 | HRESULT ReadBinaryFile(const wchar_t* filename, OUT std::vector& fileBytes); 49 | HRESULT WriteBinaryFile(const wchar_t* filename, const std::vector& fileData); 50 | HRESULT WriteBinaryFile(_In_z_ const wchar_t* filename, _In_reads_bytes_(fileDataSize) const void* fileData, uint32_t fileDataSize); 51 | 52 | // Expand the given path and mask into a list of nul-separated filenames. 53 | HRESULT EnumerateMatchingFiles( 54 | __in_z_opt const wchar_t* fileDirectory, 55 | __in_z_opt wchar_t const* originalFileMask, 56 | IN OUT std::wstring& fileNames // Appended onto any existing names. It's safe for this to alias fileDirectory. 57 | ); 58 | 59 | uint8_t* NtosCompressData(uint8_t* sourceBuffer, size_t sourceBufferSize, DWORD* dwSizeOut); 60 | 61 | uint8_t* NtosDecompressData(_In_reads_bytes_(sourceBufferSize) uint8_t* sourceBuffer, size_t sourceBufferSize, DWORD* dwSizeOut); 62 | 63 | const wchar_t* FindFileNameStart(array_ref fileName); 64 | 65 | bool FileContainsWildcard(array_ref fileName); 66 | #endif 67 | -------------------------------------------------------------------------------- /common/Macros.h: -------------------------------------------------------------------------------- 1 | // Include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | #pragma once 5 | 6 | 7 | //////////////////////////////////////// 8 | // Common macro definitions: 9 | 10 | // Disable unknown pragma warnings (for when when compiling directly with VC) 11 | #pragma warning(disable: 4068) 12 | 13 | // The C++0x keyword is supported in Visual Studio 2010 14 | #if _MSC_VER < 1600 15 | #ifndef nullptr 16 | #define nullptr 0 17 | #endif 18 | #endif 19 | 20 | // The static assertion keyword is supported in Visual Studio 2010 21 | #if _MSC_VER < 1600 22 | #ifndef static_assert 23 | #define static_assert(exp,msg) 24 | #endif 25 | #endif 26 | 27 | #ifndef _USE_MATH_DEFINES 28 | #define _USE_MATH_DEFINES // yes, we DO want PI defined 29 | #endif 30 | 31 | #if !defined(DEBUG) && !defined(NDEBUG) 32 | #define DEBUG 33 | #endif 34 | 35 | #if (_MSC_VER >= 1400) // ensure that a virtual method overrides an actual base method. 36 | #define OVERRIDE override 37 | #else 38 | #define OVERRIDE 39 | #endif 40 | 41 | #ifndef UUIDOF 42 | #define UUIDOF(iid) __uuidof(iid) 43 | #endif 44 | 45 | #ifndef ARRAY_SIZE 46 | #define ARRAY_SIZE ARRAYSIZE 47 | #endif 48 | 49 | #ifndef IFR 50 | #define IFR(hrIn) { HRESULT hrOut = (hrIn); if (FAILED(hrOut)) {return hrOut; } } 51 | #endif 52 | 53 | #ifndef RETURN_ON_FAIL // For any negative HRESULT code 54 | #define RETURN_ON_FAIL(hrin, retval) { HRESULT hr = (hrin); if (FAILED(hr)) {return (retval); } } 55 | #endif 56 | 57 | #ifndef RETURN_ON_ZERO // For null, zero, or false 58 | #define RETURN_ON_ZERO(exp, retval) if (!(exp)) {return (retval);} 59 | #endif 60 | 61 | // Use the double macro technique to stringize the actual value of s 62 | #ifndef STRINGIZE_ 63 | #define STRINGIZE_(s) STRINGIZE2_(s) 64 | #define STRINGIZE2_(s) #s 65 | #endif 66 | 67 | // Weird... windowsx.h lacks this function 68 | #ifndef SetWindowStyle 69 | #define SetWindowStyle(hwnd, value) ((DWORD)SetWindowLong(hwnd, GWL_STYLE, value)) 70 | #endif 71 | 72 | #ifndef FAILURE_LOCATION 73 | //-#define FAILURE_LOCATION L"\r\nFunction: " __FUNCTION__ L"\r\nLine: " STRINGIZE_(__LINE__) 74 | #define FAILURE_LOCATION 75 | #endif 76 | 77 | #ifndef ALIGNOF 78 | // ALIGNOF macro yields the byte alignment of the specified type. 79 | #ifdef _MSC_VER 80 | // Use Microsoft language extension 81 | #define ALIGNOF(T) __alignof(T) 82 | #else 83 | // Use GCC language extension 84 | #define ALIGNOF(T) __alignof__(T) 85 | #endif 86 | #endif 87 | -------------------------------------------------------------------------------- /common/Pointers.h: -------------------------------------------------------------------------------- 1 | //+--------------------------------------------------------------------------- 2 | // 3 | // Copyright (c) Microsoft, 2008. All rights reserved. 4 | // 5 | // Contents: Smart pointers. 6 | // 7 | //---------------------------------------------------------------------------- 8 | #pragma once 9 | 10 | 11 | // Helper to return multiple supported interfaces. 12 | // 13 | // Example: 14 | // 15 | // STDMETHOD(QueryInterface)(IID const& iid, __out void** object) OVERRIDE 16 | // { 17 | // COM_BASE_RETURN_INTERFACE(iid, IUnknown, object); 18 | // COM_BASE_RETURN_INTERFACE(iid, IDWriteInlineObject, object); 19 | // COM_BASE_RETURN_NO_INTERFACE(object); 20 | // } 21 | // 22 | #define COM_BASE_RETURN_INTERFACE(iid, U, object) \ 23 | if (iid == UUIDOF(U)) \ 24 | { \ 25 | U* p = static_cast(this); \ 26 | p->AddRef(); \ 27 | *object = p; \ 28 | return S_OK; \ 29 | } 30 | 31 | // For those cases when diamond inheritance causes the ambiguous cast compilation error. 32 | #define COM_BASE_RETURN_INTERFACE_AMBIGUOUS(iid, U, object, subthis) \ 33 | if (iid == UUIDOF(U)) \ 34 | { \ 35 | U* p = static_cast(subthis); \ 36 | p->AddRef(); \ 37 | *object = p; \ 38 | return S_OK; \ 39 | } 40 | 41 | #define COM_BASE_RETURN_NO_INTERFACE(object) \ 42 | *object = nullptr; \ 43 | return E_NOINTERFACE; 44 | 45 | 46 | // RefCountBase implementation for local reference-counted objects. 47 | class RefCountBase 48 | { 49 | public: 50 | explicit RefCountBase() throw() 51 | : refValue_() 52 | { } 53 | 54 | explicit RefCountBase(ULONG refValue) throw() 55 | : refValue_(refValue) 56 | { } 57 | 58 | unsigned long IncrementRef() throw() 59 | { 60 | return InterlockedIncrement(&refValue_); 61 | } 62 | 63 | unsigned long DecrementRef() throw() 64 | { 65 | ULONG newCount = InterlockedDecrement(&refValue_); 66 | if (newCount == 0) 67 | delete this; 68 | return newCount; 69 | } 70 | 71 | // Ensure we have a v-table pointer and that the destructor is always 72 | // called on the most derived class. 73 | virtual ~RefCountBase() 74 | { } 75 | 76 | protected: 77 | unsigned long refValue_; 78 | }; 79 | 80 | 81 | class RefCountBaseStatic : public RefCountBase 82 | { 83 | public: 84 | typedef RefCountBase Base; 85 | 86 | explicit RefCountBaseStatic() throw() 87 | : Base() 88 | { } 89 | 90 | explicit RefCountBaseStatic(ULONG refValue) throw() 91 | : Base(refValue) 92 | { } 93 | 94 | // Just use inherited IncrementRef. 95 | 96 | // Do not delete the reference. 97 | unsigned long DecrementRef() throw() 98 | { 99 | return InterlockedDecrement(&refValue_); 100 | } 101 | }; 102 | 103 | 104 | // COM base implementation for IUnknown. 105 | // 106 | // Example: 107 | // 108 | // class RenderTarget : public ComBase 109 | // 110 | template 111 | class ComBase : public RCB, public T 112 | { 113 | public: 114 | typedef RCB Base; 115 | typedef T BaseInterface; 116 | 117 | /* 118 | ** Leave the definition of QI to the subclass. 119 | ** 120 | // IUnknown interface 121 | STDMETHOD(QueryInterface)(IID const& iid, __out void** object) OVERRIDE 122 | { 123 | COM_BASE_RETURN_INTERFACE(iid, IUnknown, object); 124 | COM_BASE_RETURN_NO_INTERFACE(object); 125 | } 126 | */ 127 | 128 | IFACEMETHODIMP_(unsigned long) AddRef() 129 | { 130 | return RCB::IncrementRef(); 131 | } 132 | 133 | IFACEMETHODIMP_(unsigned long) Release() 134 | { 135 | return RCB::DecrementRef(); 136 | } 137 | }; 138 | -------------------------------------------------------------------------------- /common/Unicode.cpp: -------------------------------------------------------------------------------- 1 | // Include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | #include "precomp.h" 5 | 6 | 7 | __out_range(0, destMax) 8 | size_t ConvertUtf16ToUtf32( 9 | __in_ecount(sourceCount) const wchar_t* sourceChars, 10 | __in size_t sourceCount, 11 | __out_ecount_part(destMax,0) char32_t* destChars, 12 | __in size_t destMax 13 | ) throw() 14 | { 15 | // Can have more UTF16 code units than UTF32, 16 | // but never the other way around. 17 | 18 | size_t si = 0, di = 0; 19 | for ( ; di < destMax; ++di) 20 | { 21 | if (si >= sourceCount) 22 | break; 23 | 24 | char32_t ch = sourceChars[si++]; 25 | 26 | // Just use the character if not a surrogate code point. 27 | // For unpaired surrogates, pass the isolated surrogate 28 | // through (rather than remap to U+FFFD). 29 | if (IsLeadingSurrogate(ch) && si < sourceCount) 30 | { 31 | char32_t leading = ch; 32 | char32_t trailing = sourceChars[si]; 33 | if (IsTrailingSurrogate(trailing)) 34 | { 35 | ch = MakeUnicodeCodepoint(leading, trailing); 36 | ++si; 37 | } 38 | } 39 | destChars[di] = ch; 40 | } 41 | 42 | return di; 43 | } 44 | 45 | 46 | __out_range(0, destMax) 47 | size_t ConvertUtf32ToUtf16( 48 | __in_ecount(sourceCount) const char32_t* sourceChars, 49 | __in size_t sourceCount, 50 | __out_ecount_part(destMax,0) wchar_t* destChars, 51 | __in size_t destMax 52 | ) throw() 53 | { 54 | if ((signed)destMax <= 0) 55 | return 0; 56 | 57 | size_t si = 0, di = 0; 58 | 59 | for ( ; si < sourceCount; ++si) 60 | { 61 | if (di >= destMax) 62 | break; 63 | 64 | char32_t ch = sourceChars[si]; 65 | 66 | if (ch > 0xFFFF && destMax - di >= 2) 67 | { 68 | // Split into leading and trailing surrogatse. 69 | // From http://unicode.org/faq/utf_bom.html#35 70 | destChars[di + 0] = GetLeadingSurrogate(ch); 71 | destChars[di + 1] = GetTrailingSurrogate(ch); 72 | ++di; 73 | } 74 | else 75 | { 76 | // A BMP character (or isolated surrogate) 77 | destChars[di + 0] = wchar_t(ch); 78 | } 79 | ++di; 80 | } 81 | 82 | return di; 83 | } 84 | 85 | 86 | struct UnicodeCharacterReader 87 | { 88 | const wchar_t* current; 89 | const wchar_t* end; 90 | 91 | bool IsAtEnd() 92 | { 93 | return current >= end; 94 | } 95 | 96 | char32_t ReadNext() 97 | { 98 | if (current >= end) 99 | return 0; 100 | 101 | char32_t ch = *current; 102 | ++current; 103 | 104 | // Just use the character if not a surrogate code point. 105 | // For unpaired surrogates, pass the isolated surrogate 106 | // through (rather than remap to U+FFFD). 107 | if (IsLeadingSurrogate(ch) && current < end) 108 | { 109 | char32_t leading = ch; 110 | char32_t trailing = *current; 111 | if (IsTrailingSurrogate(trailing)) 112 | { 113 | ch = MakeUnicodeCodepoint(leading, trailing); 114 | ++current; 115 | } 116 | } 117 | 118 | return ch; 119 | } 120 | }; 121 | -------------------------------------------------------------------------------- /common/Unicode.h: -------------------------------------------------------------------------------- 1 | // Include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | #pragma once 5 | 6 | 7 | enum UnicodeCodePoint 8 | { 9 | UnicodeNbsp = 0x0000A0, 10 | UnicodeSoftHyphen = 0x0000AD, 11 | UnicodeEnQuadSpace = 0x002000, 12 | UnicodeZeroWidthSpace = 0x00200B, 13 | UnicodeIdeographicSpace = 0x003000, 14 | UnicodeInlineObject = 0x00FFFC, // for embedded objects 15 | UnicodeReplacementCharacter = 0x00FFFD, // for invalid sequences 16 | UnicodeMax = 0x10FFFF, 17 | UnicodeTotal = 0x110000, 18 | }; 19 | 20 | 21 | inline bool IsSurrogate(UINT32 ch) throw() 22 | { 23 | // 0xD800 <= ch <= 0xDFFF 24 | return (ch & 0xF800) == 0xD800; 25 | } 26 | 27 | 28 | inline bool IsLeadingSurrogate(UINT32 ch) throw() 29 | { 30 | // 0xD800 <= ch <= 0xDBFF 31 | return (ch & 0xFC00) == 0xD800; 32 | } 33 | 34 | 35 | inline bool IsTrailingSurrogate(UINT32 ch) throw() 36 | { 37 | // 0xDC00 <= ch <= 0xDFFF 38 | return (ch & 0xFC00) == 0xDC00; 39 | } 40 | 41 | 42 | inline bool IsCharacterBeyondBmp(char32_t ch) throw() 43 | { 44 | return ch >= 0x10000; 45 | } 46 | 47 | 48 | // Split into leading and trailing surrogatse. 49 | // From http://unicode.org/faq/utf_bom.html#35 50 | inline wchar_t GetLeadingSurrogate(char32_t ch) 51 | { 52 | return wchar_t(0xD800 + (ch >> 10) - (0x10000 >> 10)); 53 | } 54 | 55 | 56 | inline wchar_t GetTrailingSurrogate(char32_t ch) 57 | { 58 | return wchar_t(0xDC00 + (ch & 0x3FF)); 59 | } 60 | 61 | 62 | inline char32_t MakeUnicodeCodepoint(uint32_t utf16Leading, uint32_t utf16Trailing) throw() 63 | { 64 | return ((utf16Leading & 0x03FF) << 10 | (utf16Trailing & 0x03FF)) + 0x10000; 65 | } 66 | 67 | 68 | __out_range(0, return) 69 | size_t ConvertUtf16ToUtf32( 70 | __in_ecount(sourceCount) const wchar_t* sourceChars, 71 | __in size_t sourceCount, 72 | __out_ecount_part(destMax,return) char32_t* destChars, 73 | __in size_t destMax 74 | ) throw(); 75 | 76 | 77 | __out_range(0, return) 78 | size_t ConvertUtf32ToUtf16( 79 | __in_ecount(sourceCount) const char32_t* sourceChars, 80 | __in size_t sourceCount, 81 | __out_ecount_part(destMax,return) wchar_t* destChars, 82 | __in size_t destMax 83 | ) throw(); 84 | 85 | 86 | bool IsCharacterSimple(char32_t ch); 87 | bool IsStringSimple( 88 | __in_ecount(textLength) const wchar_t* text, 89 | __in size_t textLength 90 | ); 91 | 92 | 93 | void GetSimpleCharacters(__out std::vector& characters); 94 | 95 | 96 | __if_exists(DWRITE_UNICODE_RANGE) 97 | { 98 | typedef DWRITE_UNICODE_RANGE UnicodeRange; 99 | } 100 | __if_not_exists(DWRITE_UNICODE_RANGE) 101 | { 102 | /// 103 | /// Range of Unicode codepoints. 104 | /// 105 | struct UnicodeRange 106 | { 107 | /// 108 | /// The first codepoint in the Unicode range. 109 | /// 110 | UINT32 first; 111 | 112 | /// 113 | /// The last codepoint in the Unicode range. 114 | /// 115 | UINT32 last; 116 | }; 117 | }; 118 | -------------------------------------------------------------------------------- /common/WindowUtility.cpp: -------------------------------------------------------------------------------- 1 | //+--------------------------------------------------------------------------- 2 | // 3 | // Copyright (c) Microsoft, 2008. All rights reserved. 4 | // 5 | // Contents: Window layout functions. 6 | // 7 | // Author: Dwayne Robinson (dwayner@microsoft.com) 8 | // 9 | // History: 2008-02-11 dwayner Created 10 | // 11 | //---------------------------------------------------------------------------- 12 | #include "precomp.h" 13 | #include "WindowUtility.h" 14 | 15 | 16 | WindowPosition::WindowPosition() 17 | : hwnd(), 18 | options(PositionOptionsDefault), 19 | rect() 20 | { 21 | } 22 | 23 | 24 | WindowPosition::WindowPosition(HWND newHwnd, PositionOptions newOptions) 25 | : hwnd(newHwnd), 26 | options(newOptions) 27 | { 28 | POINT pt = {}; 29 | ScreenToClient(newHwnd, &pt); 30 | GetWindowRect(newHwnd, &rect); 31 | OffsetRect(&rect, pt.x, pt.y); 32 | } 33 | 34 | 35 | WindowPosition::WindowPosition(HWND newHwnd, PositionOptions newOptions, const RECT& newRect) 36 | : hwnd(newHwnd), 37 | options(newOptions), 38 | rect(newRect) 39 | { 40 | } 41 | 42 | 43 | long WindowPosition::GetWidth() 44 | { 45 | return rect.right - rect.left; 46 | } 47 | 48 | 49 | long WindowPosition::GetHeight() 50 | { 51 | return rect.bottom - rect.top; 52 | } 53 | 54 | 55 | 56 | void WindowPosition::Update(HDWP hdwp) 57 | { 58 | if (hwnd == nullptr) 59 | return; 60 | 61 | int width = GetWidth(); 62 | int height = GetHeight(); 63 | 64 | if (width < 0) width = 0; 65 | if (height < 0) height = 0; 66 | 67 | if (hdwp != nullptr) 68 | { 69 | DeferWindowPos( 70 | hdwp, 71 | hwnd, 72 | nullptr, 73 | rect.left, 74 | rect.top, 75 | width, 76 | height, 77 | SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOZORDER 78 | ); 79 | } 80 | else 81 | { 82 | SetWindowPos( 83 | hwnd, 84 | nullptr, 85 | rect.left, 86 | rect.top, 87 | width, 88 | height, 89 | SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOZORDER 90 | ); 91 | } 92 | } 93 | 94 | 95 | void WindowPosition::Update( 96 | __inout_ecount(windowPositionsCount) WindowPosition* windowPositions, 97 | __inout uint32_t windowPositionsCount 98 | ) 99 | { 100 | HDWP hdwp = BeginDeferWindowPos(windowPositionsCount); 101 | for (uint32_t i = 0; i < windowPositionsCount; ++i) 102 | { 103 | windowPositions[i].Update(hdwp); 104 | } 105 | EndDeferWindowPos(hdwp); 106 | } 107 | 108 | 109 | RECT WindowPosition::GetBounds( 110 | __inout_ecount(windowPositionsCount) WindowPosition* windowPositions, 111 | uint32_t windowPositionsCount 112 | ) 113 | { 114 | RECT bounds; 115 | bounds.left = INT_MAX; 116 | bounds.right = -INT_MAX; 117 | bounds.top = INT_MAX; 118 | bounds.bottom = -INT_MAX; 119 | 120 | for (uint32_t i = 0; i < windowPositionsCount; ++i) 121 | { 122 | UnionRect(OUT &bounds, &bounds, &windowPositions[i].rect); 123 | } 124 | 125 | return bounds; 126 | } 127 | 128 | 129 | void WindowPosition::Offset( 130 | __inout_ecount(windowPositionsCount) WindowPosition* windowPositions, 131 | uint32_t windowPositionsCount, 132 | long dx, 133 | long dy 134 | ) 135 | { 136 | for (uint32_t i = 0; i < windowPositionsCount; ++i) 137 | { 138 | OffsetRect(IN OUT &windowPositions[i].rect, dx, dy); 139 | } 140 | } 141 | 142 | 143 | // Align the window to the given rectangle, using the window 144 | // rectangle and positioning flags. 145 | void WindowPosition::AlignToRect( 146 | RECT rect 147 | ) 148 | { 149 | const PositionOptions horizontalAlignment = this->options & PositionOptionsAlignHMask; 150 | const PositionOptions verticalAlignment = this->options & PositionOptionsAlignVMask; 151 | 152 | long windowWidth = this->rect.right - this->rect.left; 153 | long windowHeight = this->rect.bottom - this->rect.top; 154 | long rectWidth = rect.right - rect.left; 155 | long rectHeight = rect.bottom - rect.top; 156 | 157 | long x = 0; 158 | switch (horizontalAlignment) 159 | { 160 | case PositionOptionsAlignHStretch: 161 | windowWidth = rectWidth; 162 | // fall through 163 | case PositionOptionsAlignLeft: 164 | default: 165 | x = 0; 166 | break; 167 | case PositionOptionsAlignRight: 168 | x = rectWidth - windowWidth; 169 | break; 170 | case PositionOptionsAlignHCenter: 171 | x = (rectWidth - windowWidth) / 2; 172 | break; 173 | } 174 | 175 | long y = 0; 176 | switch (verticalAlignment) 177 | { 178 | case PositionOptionsAlignVStretch: 179 | windowHeight = rectHeight; 180 | // fall through 181 | case PositionOptionsAlignTop: 182 | default: 183 | y = 0; 184 | break; 185 | case PositionOptionsAlignBottom: 186 | y = rectHeight - windowHeight; 187 | break; 188 | case PositionOptionsAlignVCenter: 189 | y = (rectHeight - windowHeight) / 2; 190 | break; 191 | } 192 | 193 | this->rect.left = x + rect.left; 194 | this->rect.top = y + rect.top; 195 | this->rect.right = this->rect.left + windowWidth; 196 | this->rect.bottom = this->rect.top + windowHeight; 197 | } 198 | 199 | 200 | struct GridDimension 201 | { 202 | PositionOptions options; // combined options for this grid cell (they come from the respective windows) 203 | long offset; // x in horizontal, y in vertical 204 | long size; // width in horizontal, height in vertical 205 | }; 206 | 207 | 208 | void WindowPosition::DistributeGridDimensions( 209 | __inout_ecount(gridDimensionsCount) GridDimension* gridDimensions, 210 | uint32_t gridDimensionsCount, 211 | long clientSize, 212 | long spacing, 213 | PositionOptions optionsMask // can be PositionOptionsUseSlackHeight or PositionOptionsUseSlackWidth 214 | ) 215 | { 216 | if (gridDimensionsCount <= 0) 217 | { 218 | return; // nothing to do 219 | } 220 | 221 | // Determine the maximum size along a single dimension, 222 | // and assign all offsets. 223 | 224 | long accumulatedSize = 0; 225 | uint32_t gridFillCount = 0; 226 | 227 | for (uint32_t i = 0; i < gridDimensionsCount; ++i) 228 | { 229 | if (gridDimensions[i].options & PositionOptionsIgnored) 230 | continue; 231 | 232 | // Add spacing between all controls, but not before the first control, 233 | // not after zero width controls. 234 | if (i > 0) 235 | accumulatedSize += spacing; 236 | gridDimensions[i].offset = accumulatedSize; 237 | accumulatedSize += gridDimensions[i].size; 238 | 239 | if (gridDimensions[i].options & optionsMask) 240 | ++gridFillCount; // found another grid cell to fill 241 | } 242 | 243 | // Check if we still have leftover space inside the parent's client rect. 244 | 245 | const long slackSizeToRedistribute = clientSize - accumulatedSize; 246 | if (gridFillCount <= 0 || slackSizeToRedistribute <= 0) 247 | { 248 | return; // nothing more to do 249 | } 250 | 251 | // Distribute any slack space remaining throughout windows that want it. 252 | 253 | uint32_t gridFillIndex = 0; 254 | long offsetAdjustment = 0; 255 | 256 | for (uint32_t i = 0; i < gridDimensionsCount; ++i) 257 | { 258 | if (gridDimensions[i].options & PositionOptionsIgnored) 259 | continue; 260 | 261 | // Shift the offset over from any earlier slack space contribution. 262 | gridDimensions[i].offset += offsetAdjustment; 263 | 264 | // Contribute the slack space into this row/col. 265 | if (gridDimensions[i].options & optionsMask) 266 | { 267 | long sizeForGridCell = (slackSizeToRedistribute * (gridFillIndex + 1) / gridFillCount) 268 | - (slackSizeToRedistribute * gridFillIndex / gridFillCount); 269 | 270 | // Increase the size of subsequent offsets. 271 | gridDimensions[i].size += sizeForGridCell; 272 | offsetAdjustment += sizeForGridCell; 273 | ++gridFillIndex; 274 | } 275 | } 276 | } 277 | 278 | RECT WindowPosition::ReflowGrid( 279 | __inout_ecount(windowPositionsCount) WindowPosition* windowPositions, 280 | uint32_t windowPositionsCount, 281 | const RECT& clientRect, 282 | long spacing, 283 | uint32_t maxCols, 284 | PositionOptions parentOptions, 285 | bool queryOnly 286 | ) 287 | { 288 | // There can be up to n rows and n columns, so allocate up to 2n. 289 | // Rather than allocate two separate arrays, use the first half 290 | // for columns and second half for rows. Note the interpretation 291 | // of rows and columns flips if the layout is vertical. 292 | std::vector gridDimensions(windowPositionsCount * 2); 293 | 294 | bool isVertical = (parentOptions & PositionOptionsFlowVertical) != 0; 295 | bool isRtl = (parentOptions & PositionOptionsReverseFlowDu) != 0; 296 | bool isWrapped = (parentOptions & PositionOptionsUnwrapped) == 0; 297 | 298 | if (maxCols < windowPositionsCount) 299 | { 300 | // Set the flag to make logic easier in the later loop. 301 | gridDimensions[maxCols].options |= PositionOptionsNewLine; 302 | } 303 | else 304 | { 305 | maxCols = windowPositionsCount; 306 | } 307 | 308 | //////////////////////////////////////// 309 | // Arrange all the children, flowing left-to-right for horizontal, 310 | // top-to-bottom for vertical (flipping at the end if RTL). Note 311 | // that all the coordinates are rotated 90 degrees when vertical 312 | // (so x is really y). 313 | 314 | long colOffset = 0; // current column offset 315 | long rowOffset = 0; // current column offset 316 | long totalColSize = 0; // total accumulated column size 317 | long totalRowSize = 0; // total accumulated row size 318 | long maxColSize = clientRect.right - clientRect.left; 319 | long maxRowSize = clientRect.bottom - clientRect.top; 320 | if (isVertical) 321 | std::swap(maxColSize, maxRowSize); 322 | 323 | uint32_t colCount = 0; // number of columns (reset per row) 324 | uint32_t rowCount = 0; // number of rows 325 | uint32_t gridStartingColIndex = 0; // starting column index on current row 326 | uint32_t gridColIndex = 0; // array index, not column offset 327 | uint32_t gridRowIndex = windowPositionsCount; // array index, not row offset 328 | 329 | for (size_t windowIndex = 0; windowIndex < windowPositionsCount; ++windowIndex) 330 | { 331 | const WindowPosition& window = windowPositions[windowIndex]; 332 | PositionOptions options = window.options; 333 | 334 | // Skip any windows that invisible, floating, or resized by the caller. 335 | if (options & PositionOptionsIgnored) 336 | { 337 | continue; 338 | } 339 | 340 | // Expand the grid if the window is larger than any previous. 341 | long colSize = window.rect.right - window.rect.left; 342 | long rowSize = window.rect.bottom - window.rect.top; 343 | 344 | // If the row or column should be stretched, zero the size so that it 345 | // does not prematurely increase the size. Otherwise resizing the same 346 | // control repeatedly would end up only ever growing the control. 347 | if ((options & PositionOptionsAlignHMask) == PositionOptionsAlignHStretch) 348 | { 349 | colSize = 0; 350 | } 351 | if ((options & PositionOptionsAlignVMask) == PositionOptionsAlignVStretch) 352 | { 353 | rowSize = 0; 354 | } 355 | 356 | if (isVertical) 357 | std::swap(colSize, rowSize); 358 | 359 | // Flow down to a new line if wider than maximum column count or column size, 360 | // or if windows hints says it should be on a new line. 361 | if (colCount > 0) 362 | { 363 | if ((options & PositionOptionsNewLine) 364 | || (maxCols > 0 && colCount >= maxCols) 365 | || (isWrapped && colOffset + colSize > maxColSize)) 366 | { 367 | // Distribute the columns. For a regular grid, we'll distribute 368 | // later once we've looked at all the rows. For irregular flow, 369 | // distribute now while we still know the range of windows on 370 | // the current row. 371 | if (maxCols == 0) 372 | { 373 | DistributeGridDimensions( 374 | &gridDimensions[gridStartingColIndex], 375 | colCount, 376 | maxColSize, 377 | spacing, 378 | isVertical ? PositionOptionsUseSlackHeight : PositionOptionsUseSlackWidth 379 | ); 380 | gridStartingColIndex += colCount; 381 | } 382 | else 383 | { 384 | gridColIndex = 0; 385 | } 386 | 387 | // Reset and advance. 388 | ++rowCount; 389 | ++gridRowIndex; 390 | colCount = 0; 391 | colOffset = 0; 392 | rowOffset = totalRowSize + spacing; 393 | 394 | // Set this flag to make later logic easier so that it doesn't 395 | // need to recheck a number of wrapping conditions. 396 | options |= PositionOptionsNewLine; 397 | } 398 | } 399 | 400 | // Combine the positioning options of the window into the grid column/row. 401 | gridDimensions[gridColIndex].options |= options; 402 | gridDimensions[gridRowIndex].options |= options; 403 | 404 | // If this window is larger than any previous, increase the grid row/column size, 405 | // and add to the accumulated total rect. 406 | if (colSize > gridDimensions[gridColIndex].size) 407 | { 408 | gridDimensions[gridColIndex].size = std::min(colSize, maxColSize); 409 | } 410 | if (rowSize > gridDimensions[gridRowIndex].size) 411 | { 412 | gridDimensions[gridRowIndex].size = std::min(rowSize, maxRowSize); 413 | } 414 | 415 | totalColSize = std::max(colOffset + colSize, totalColSize); 416 | totalRowSize = std::max(rowOffset + rowSize, totalRowSize); 417 | 418 | // Next column... 419 | colOffset = totalColSize + spacing; 420 | ++colCount; 421 | ++gridColIndex; 422 | } 423 | if (colCount > 0) 424 | { 425 | ++rowCount; // count the last row 426 | } 427 | 428 | //////////////////////////////////////// 429 | // Return the bounding rect if wanted. 430 | 431 | RECT resultRect = {0,0,totalColSize,totalRowSize}; 432 | if (isVertical) 433 | std::swap(resultRect.right, resultRect.bottom); 434 | 435 | if (queryOnly) 436 | return resultRect; 437 | 438 | //////////////////////////////////////// 439 | // Distribute the any remaining slack space into rows and columns. 440 | 441 | DistributeGridDimensions( 442 | &gridDimensions.data()[gridStartingColIndex], 443 | (maxCols > 0) ? maxCols : colCount, 444 | maxColSize, 445 | spacing, 446 | isVertical ? PositionOptionsUseSlackHeight : PositionOptionsUseSlackWidth 447 | ); 448 | 449 | gridRowIndex = windowPositionsCount; // array index, not row offset 450 | DistributeGridDimensions( 451 | &gridDimensions.data()[gridRowIndex], 452 | rowCount, 453 | maxRowSize, 454 | spacing, 455 | !isVertical ? PositionOptionsUseSlackHeight : PositionOptionsUseSlackWidth 456 | ); 457 | 458 | //////////////////////////////////////// 459 | // Update window rects with the new positions from the grid. 460 | 461 | // Reset for second loop. 462 | colCount = 0; 463 | gridColIndex = 0; 464 | gridRowIndex = windowPositionsCount; 465 | 466 | for (size_t windowIndex = 0; windowIndex < windowPositionsCount; ++windowIndex) 467 | { 468 | WindowPosition& window = windowPositions[windowIndex]; 469 | 470 | // Skip any invisible/floating windows. 471 | if (window.options & PositionOptionsIgnored) 472 | { 473 | continue; 474 | } 475 | 476 | const bool isNewLine = !!(gridDimensions[gridColIndex].options & PositionOptionsNewLine); 477 | if (isNewLine && colCount > 0) 478 | { 479 | ++gridRowIndex; 480 | if (maxCols > 0) 481 | { 482 | gridColIndex = 0; 483 | } 484 | } 485 | 486 | RECT gridRect = { 487 | clientRect.left + gridDimensions[gridColIndex].offset, 488 | clientRect.top + gridDimensions[gridRowIndex].offset, 489 | gridRect.left + gridDimensions[gridColIndex].size, 490 | gridRect.top + gridDimensions[gridRowIndex].size, 491 | }; 492 | if (isVertical) 493 | { 494 | std::swap(gridRect.left, gridRect.top); 495 | std::swap(gridRect.right, gridRect.bottom); 496 | } 497 | 498 | window.AlignToRect(gridRect); 499 | 500 | ++colCount; 501 | ++gridColIndex; 502 | } 503 | 504 | if (isRtl) 505 | { 506 | for (size_t windowIndex = 0; windowIndex < windowPositionsCount; ++windowIndex) 507 | { 508 | WindowPosition& window = windowPositions[windowIndex]; 509 | long adjustment = clientRect.right - window.rect.right + clientRect.left - window.rect.left; 510 | window.rect.left += adjustment; 511 | window.rect.right += adjustment; 512 | } 513 | } 514 | 515 | return resultRect; 516 | } 517 | 518 | 519 | // Seriously, why does GetSubMenu not support either by-position or by-id like GetMenuItemInfo? 520 | HMENU WINAPI GetSubMenu( 521 | __in HMENU parentMenu, 522 | __in UINT item, 523 | __in BOOL byPosition 524 | ) 525 | { 526 | MENUITEMINFO menuItemInfo; 527 | menuItemInfo.cbSize = sizeof(menuItemInfo); 528 | menuItemInfo.fMask = MIIM_ID | MIIM_SUBMENU; 529 | if (!GetMenuItemInfo(parentMenu, item, byPosition, OUT &menuItemInfo)) 530 | return nullptr; 531 | 532 | return menuItemInfo.hSubMenu; 533 | } 534 | 535 | 536 | bool CopyTextToClipboard( 537 | HWND hwnd, 538 | _In_reads_(textLength) wchar_t const* text, 539 | size_t textLength 540 | ) 541 | { 542 | bool succeeded = false; 543 | 544 | if (OpenClipboard(hwnd)) 545 | { 546 | if (EmptyClipboard()) 547 | { 548 | const size_t bufferLength = textLength * sizeof(text[0]); 549 | HGLOBAL bufferHandle = GlobalAlloc(GMEM_MOVEABLE, bufferLength); 550 | if (bufferHandle != nullptr) 551 | { 552 | wchar_t* buffer = reinterpret_cast(GlobalLock(bufferHandle)); 553 | 554 | memcpy(buffer, text, bufferLength); 555 | 556 | GlobalUnlock(bufferHandle); 557 | 558 | if (SetClipboardData(CF_UNICODETEXT, bufferHandle) != nullptr) 559 | { 560 | succeeded = true; 561 | } 562 | else 563 | { 564 | GlobalFree(bufferHandle); 565 | } 566 | } 567 | } 568 | CloseClipboard(); 569 | } 570 | 571 | return succeeded; 572 | } 573 | 574 | 575 | void CopyListTextToClipboard( 576 | HWND listViewHwnd, 577 | _In_z_ wchar_t const* separator 578 | ) 579 | { 580 | std::wstring text = GetListViewText(listViewHwnd, separator); 581 | CopyTextToClipboard(listViewHwnd, text.c_str(), text.length() + 1); 582 | } 583 | 584 | 585 | std::wstring GetListViewText( 586 | HWND listViewHwnd, 587 | __in_z const wchar_t* separator 588 | ) 589 | { 590 | std::wstring text; 591 | wchar_t itemTextBuffer[256]; 592 | wchar_t groupTextBuffer[256]; 593 | 594 | LVITEMINDEX listViewIndex = {-1, -1}; 595 | 596 | // Setup the item to retrieve text. 597 | LVITEM listViewItem; 598 | listViewItem.mask = LVIF_TEXT | LVIF_GROUPID; 599 | listViewItem.iSubItem = 0; 600 | 601 | // Setup the group to retrieve text. 602 | LVGROUP listViewGroup; 603 | listViewGroup.cbSize = sizeof(LVGROUP); 604 | listViewGroup.mask = LVGF_HEADER | LVGF_GROUPID; 605 | listViewGroup.iGroupId = 0; 606 | int previousGroupId = -1; 607 | 608 | const uint32_t columnCount = Header_GetItemCount(ListView_GetHeader(listViewHwnd)); 609 | std::vector columnOrdering(columnCount); 610 | ListView_GetColumnOrderArray(listViewHwnd, columnCount, OUT columnOrdering.data()); 611 | 612 | // Find the first selected row. If nothing is selected, then we'll copy the entire text. 613 | uint32_t searchMask = LVNI_SELECTED; 614 | ListView_GetNextItemIndex(listViewHwnd, &listViewIndex, searchMask); 615 | if (listViewIndex.iItem < 0) 616 | { 617 | searchMask = LVNI_ALL; 618 | ListView_GetNextItemIndex(listViewHwnd, &listViewIndex, searchMask); 619 | } 620 | 621 | // Build up a concatenated string. 622 | // Append next item's column to the text. (either the next selected or all) 623 | while (listViewIndex.iItem >= 0) 624 | { 625 | for (uint32_t column = 0; column < columnCount; ++column) 626 | { 627 | int32_t columnIndex = columnOrdering[column]; 628 | 629 | if (columnCount > 1 && ListView_GetColumnWidth(listViewHwnd, columnIndex) <= 0) 630 | continue; // Skip zero-width columns. 631 | 632 | // Set up fields for call. We must reset the string pointer 633 | // and count for virtual listviews because while it copies the 634 | // text out to our buffer, it also messes with the pointer. 635 | // Otherwise the next call will crash. 636 | itemTextBuffer[0] = '\0'; 637 | listViewItem.iItem = listViewIndex.iItem; 638 | listViewItem.iSubItem = columnIndex; 639 | listViewItem.pszText = (LPWSTR)itemTextBuffer; 640 | listViewItem.cchTextMax = ARRAYSIZE(itemTextBuffer); 641 | ListView_GetItem(listViewHwnd, &listViewItem); 642 | 643 | // Append the group name if we changed to a different group. 644 | if (listViewItem.iGroupId != previousGroupId) 645 | { 646 | // Add an extra blank line between the previous group. 647 | if (previousGroupId >= 0) 648 | { 649 | text += L"\r\n"; 650 | } 651 | previousGroupId = listViewItem.iGroupId; 652 | 653 | // Add group header (assuming it belongs to a group). 654 | if (listViewItem.iGroupId >= 0) 655 | { 656 | groupTextBuffer[0] = '\0'; 657 | listViewGroup.pszHeader = (LPWSTR)groupTextBuffer; 658 | listViewGroup.cchHeader = ARRAYSIZE(groupTextBuffer); 659 | ListView_GetGroupInfo(listViewHwnd, listViewItem.iGroupId, &listViewGroup); 660 | 661 | text += L"― "; 662 | text += groupTextBuffer; 663 | text += L" ―\r\n"; 664 | } 665 | } 666 | 667 | text += itemTextBuffer; 668 | 669 | // Append a new line if last column, or the separator if between columns. 670 | if (column + 1 >= columnCount) 671 | { 672 | text += L"\r\n"; 673 | } 674 | else 675 | { 676 | text += separator; // may be a tab, or other separator such as comma or field/value pair ": " 677 | } 678 | } // for column 679 | 680 | if (!ListView_GetNextItemIndex(listViewHwnd, &listViewIndex, searchMask)) 681 | break; 682 | } // while iItem 683 | 684 | text.shrink_to_fit(); 685 | 686 | return text; 687 | } 688 | -------------------------------------------------------------------------------- /common/WindowUtility.h: -------------------------------------------------------------------------------- 1 | //+--------------------------------------------------------------------------- 2 | // 3 | // Copyright (c) Microsoft, 2008. All rights reserved. 4 | // 5 | // Contents: Window layout functions. 6 | // 7 | // Author: Dwayne Robinson (dwayner@microsoft.com) 8 | // 9 | // History: 2008-02-11 dwayner Created 10 | // 11 | //---------------------------------------------------------------------------- 12 | 13 | #pragma once 14 | 15 | 16 | enum PositionOptions 17 | { 18 | PositionOptionsUseSlackWidth = 1, // use any remaining width of parent's client rect (use FillWidth unless you want specific alignment) 19 | PositionOptionsUseSlackHeight = 2, // use any remaining height of parent's client rect (use FillHeight unless you want specific alignment) 20 | PositionOptionsAlignHCenter = 0, // center horizontally within allocated space 21 | PositionOptionsAlignLeft = 16, // align left within allocated space 22 | PositionOptionsAlignRight = 32, // align right within allocated space 23 | PositionOptionsAlignHStretch = 16|32, // stretch left within allocated space 24 | PositionOptionsAlignHMask = 16|32, // mask for horizontal alignment 25 | PositionOptionsAlignVCenter = 0, // center vertically within allocated space 26 | PositionOptionsAlignTop = 64, // align top within allocated space 27 | PositionOptionsAlignBottom = 128, // align bottom within allocated space 28 | PositionOptionsAlignVStretch = 64|128, // stretch within allocated space 29 | PositionOptionsAlignVMask = 64|128, // mask for vertical alignment 30 | 31 | PositionOptionsNewLine = 256, // start this control on a new line 32 | 33 | // Control options 34 | PositionOptionsUnwrapped = 512, // do not wrap / allow controls to flow off the edge even if larger than containing parent width/height 35 | PositionOptionsReverseFlowDu = 1024, // reverse the primary direction (ltr->rtl in horz, ttb->btt in vert) 36 | PositionOptionsReverseFlowDv = 2048, // reverse the secondary direction (ttb->btt in horz, ltr->rtl in vert) 37 | PositionOptionsFlowHorizontal = 0, // flow primarily horizontal, then vertical 38 | PositionOptionsFlowVertical = 4096, // flow primarily vertical, then horizontal 39 | PositionOptionsIgnored = 8192, // window will not be adjusted (either hidden or floating or out-of-band) 40 | 41 | PositionOptionsDefault = PositionOptionsAlignLeft|PositionOptionsAlignTop, 42 | PositionOptionsFillWidth = PositionOptionsUseSlackWidth|PositionOptionsAlignHStretch, 43 | PositionOptionsFillHeight = PositionOptionsUseSlackHeight|PositionOptionsAlignVStretch, 44 | }; 45 | DEFINE_ENUM_FLAG_OPERATORS(PositionOptions); 46 | 47 | 48 | struct WindowPosition 49 | { 50 | HWND hwnd; 51 | PositionOptions options; 52 | RECT rect; 53 | 54 | 55 | WindowPosition(); 56 | WindowPosition(HWND newHwnd, PositionOptions newOptions = PositionOptionsDefault); 57 | WindowPosition(HWND newHwnd, PositionOptions newOptions, const RECT& newRect); 58 | 59 | inline long GetWidth(); 60 | inline long GetHeight(); 61 | 62 | // Updates the real system window to match the rectangle in the structure. 63 | // If a deferred window position handle is passed, it will apply it to that, 64 | // otherwise the effect is immediate. 65 | void Update(HDWP hdwp = nullptr); 66 | 67 | static void Update( 68 | __inout_ecount(windowPositionsCount) WindowPosition* windowPositions, 69 | __inout uint32_t windowPositionsCount 70 | ); 71 | 72 | // Align the window to the given rectangle, using the window 73 | // rectangle and positioning flags. 74 | void AlignToRect( 75 | RECT rect 76 | ); 77 | 78 | static RECT GetBounds( 79 | __inout_ecount(windowPositionsCount) WindowPosition* windowPositions, 80 | uint32_t windowPositionsCount 81 | ); 82 | 83 | static void Offset( 84 | __inout_ecount(windowPositionsCount) WindowPosition* windowPositions, 85 | uint32_t windowPositionsCount, 86 | long dx, 87 | long dy 88 | ); 89 | 90 | static RECT ReflowGrid( 91 | __inout_ecount(windowPositionsCount) WindowPosition* windowPositions, 92 | uint32_t windowPositionsCount, 93 | const RECT& clientRect, 94 | long spacing, 95 | uint32_t maxCols, 96 | PositionOptions parentOptions, 97 | bool queryOnly = false 98 | ); 99 | 100 | private: 101 | struct GridDimension 102 | { 103 | PositionOptions options; // combined options for this grid cell (they come from the respective windows) 104 | long offset; // x in horizontal, y in vertical 105 | long size; // width in horizontal, height in vertical 106 | }; 107 | 108 | static void DistributeGridDimensions( 109 | __inout_ecount(gridDimensionsCount) GridDimension* gridDimensions, 110 | uint32_t gridDimensionsCount, 111 | long clientSize, 112 | long spacing, 113 | PositionOptions optionsMask // can be PositionOptionsUseSlackHeight or PositionOptionsUseSlackWidth 114 | ); 115 | }; 116 | 117 | 118 | template 119 | T* GetClassFromWindow(HWND hwnd) 120 | { 121 | return reinterpret_cast(::GetWindowLongPtr(hwnd, DWLP_USER)); 122 | } 123 | 124 | struct DialogProcResult 125 | { 126 | INT_PTR handled; // whether it was handled or not 127 | LRESULT value; // the return value 128 | 129 | DialogProcResult(INT_PTR newHandled, LRESULT newValue) 130 | : handled(newHandled), 131 | value(newValue) 132 | { } 133 | 134 | DialogProcResult(bool newHandled) 135 | : handled(newHandled), 136 | value() 137 | { } 138 | }; 139 | 140 | 141 | HMENU WINAPI GetSubMenu( 142 | __in HMENU parentMenu, 143 | __in UINT item, 144 | __in BOOL byPosition 145 | ); 146 | 147 | bool CopyTextToClipboard( 148 | HWND hwnd, 149 | _In_reads_(textLength) wchar_t const* text, 150 | size_t textLength 151 | ); 152 | 153 | 154 | std::wstring GetListViewText( 155 | HWND listViewHwnd, 156 | __in_z const wchar_t* separator 157 | ); 158 | 159 | void CopyListTextToClipboard( 160 | HWND listViewHwnd, 161 | _In_z_ wchar_t const* separator 162 | ); 163 | -------------------------------------------------------------------------------- /common/precomp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../precomp.inc" 4 | -------------------------------------------------------------------------------- /font/DWritEx.cpp: -------------------------------------------------------------------------------- 1 | //+--------------------------------------------------------------------------- 2 | // 3 | // Contents: DWrite helper functions for commonly needed tasks. 4 | // 5 | // History: 2009-09-09 dwayner 6 | // 7 | //---------------------------------------------------------------------------- 8 | 9 | #include "precomp.h" 10 | #include 11 | #include 12 | #include "DwritEx.h" 13 | 14 | 15 | //////////////////////////////////////// 16 | 17 | 18 | const static DWRITE_MATRIX identityTransform = {1,0,0,1,0,0}; 19 | 20 | 21 | // Load it dynamically. 22 | HRESULT LoadDWrite( 23 | _In_z_ const wchar_t* dllPath, 24 | DWRITE_FACTORY_TYPE factoryType, // DWRITE_FACTORY_TYPE_SHARED 25 | _COM_Outptr_ IDWriteFactory** factory, 26 | _Out_ HMODULE& moduleHandle 27 | ) throw() 28 | { 29 | typedef HRESULT (__stdcall DWriteCreateFactory_t)( 30 | __in DWRITE_FACTORY_TYPE, 31 | __in REFIID, 32 | _COM_Outptr_ IUnknown** 33 | ); 34 | 35 | *factory = nullptr; 36 | moduleHandle = nullptr; 37 | 38 | ModuleHandle dwriteModule = LoadLibrary(dllPath); 39 | if (dwriteModule == nullptr) 40 | return HRESULT_FROM_WIN32(GetLastError()); 41 | 42 | DWriteCreateFactory_t* factoryFunction = (DWriteCreateFactory_t*) GetProcAddress(dwriteModule, "DWriteCreateFactory"); 43 | if (factoryFunction == nullptr) 44 | return HRESULT_FROM_WIN32(GetLastError()); 45 | 46 | // Use an isolated factory to prevent polluting the global cache. 47 | HRESULT hr = factoryFunction( 48 | DWRITE_FACTORY_TYPE_ISOLATED, 49 | __uuidof(IDWriteFactory), 50 | OUT (IUnknown**)factory 51 | ); 52 | 53 | if (SUCCEEDED(hr)) 54 | { 55 | moduleHandle = dwriteModule.Detach(); 56 | } 57 | 58 | return hr; 59 | } 60 | 61 | 62 | class CustomCollectionLocalFontFileEnumerator : public ComBase 63 | { 64 | protected: 65 | IFACEMETHODIMP QueryInterface(IID const& iid, __out void** object) OVERRIDE 66 | { 67 | COM_BASE_RETURN_INTERFACE(iid, IDWriteFontFileEnumerator, object); 68 | COM_BASE_RETURN_INTERFACE(iid, IUnknown, object); 69 | COM_BASE_RETURN_NO_INTERFACE(object); 70 | } 71 | 72 | 73 | public: 74 | CustomCollectionLocalFontFileEnumerator() 75 | : remainingFontFileNames_(nullptr), 76 | findHandle_(INVALID_HANDLE_VALUE) 77 | { } 78 | 79 | 80 | ~CustomCollectionLocalFontFileEnumerator() 81 | { 82 | if (findHandle_ != INVALID_HANDLE_VALUE) 83 | { 84 | FindClose(findHandle_); 85 | } 86 | } 87 | 88 | 89 | HRESULT Initialize( 90 | _In_ IDWriteFactory* factory, 91 | _In_reads_(fontFileNamesSize) const wchar_t* fontFileNames, 92 | uint32_t fontFileNamesSize 93 | ) throw() 94 | { 95 | if (factory == nullptr || fontFileNames == nullptr) 96 | return E_INVALIDARG; 97 | 98 | factory_ = factory; 99 | remainingFontFileNames_ = fontFileNames; 100 | remainingFontFileNamesEnd_ = fontFileNames + fontFileNamesSize; 101 | return S_OK; 102 | } 103 | 104 | 105 | IFACEMETHODIMP MoveNext(_Out_ BOOL* hasCurrentFile) 106 | { 107 | HRESULT hr = S_OK; 108 | *hasCurrentFile = false; 109 | currentFontFile_ = nullptr; 110 | 111 | try 112 | { 113 | // Get next filename. 114 | for (;;) 115 | { 116 | // Check for the end of the list, either reaching the end 117 | // of the key or double-nul termination. 118 | if (remainingFontFileNames_ >= remainingFontFileNamesEnd_ 119 | || remainingFontFileNames_[0] == '\0') 120 | { 121 | return hr; 122 | } 123 | 124 | if (findHandle_ == INVALID_HANDLE_VALUE) 125 | { 126 | // Get the first file matching the mask. 127 | findHandle_ = FindFirstFile(remainingFontFileNames_, OUT &findData_); 128 | if (findHandle_ == INVALID_HANDLE_VALUE) 129 | { 130 | auto errorCode = GetLastError(); 131 | if (errorCode == ERROR_FILE_NOT_FOUND) 132 | return S_OK; 133 | 134 | return HRESULT_FROM_WIN32(errorCode); 135 | } 136 | } 137 | else if (!FindNextFile(findHandle_, OUT &findData_)) 138 | { 139 | // Move to next filename (skipping the nul). 140 | remainingFontFileNames_ += wcslen(remainingFontFileNames_) + 1; 141 | 142 | FindClose(findHandle_); 143 | findHandle_ = INVALID_HANDLE_VALUE; 144 | 145 | continue; // Move to next file mask. 146 | } 147 | 148 | if (!(findData_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) // Skip directories. 149 | break; // Have our file. 150 | } 151 | 152 | // Concatenate the path and current file name. 153 | const wchar_t* fileNameStart = FindFileNameStart({ remainingFontFileNames_, remainingFontFileNamesEnd_ }); 154 | fullPath_.assign(remainingFontFileNames_, fileNameStart); 155 | fullPath_ += findData_.cFileName; 156 | 157 | #if 0 158 | wprintf(L"%s\n", fullPath_.c_str()); 159 | #endif 160 | 161 | currentFontFile_.Clear(); 162 | IFR(factory_->CreateFontFileReference( 163 | fullPath_.c_str(), 164 | &findData_.ftLastWriteTime, 165 | ¤tFontFile_ 166 | )); 167 | 168 | *hasCurrentFile = true; 169 | } 170 | catch (std::bad_alloc) 171 | { 172 | return HRESULT_FROM_WIN32(E_OUTOFMEMORY); // This is the only exception type we need to worry about. 173 | } 174 | 175 | return hr; 176 | } 177 | 178 | IFACEMETHODIMP GetCurrentFontFile(_COM_Outptr_ IDWriteFontFile** fontFile) 179 | { 180 | *fontFile = currentFontFile_; 181 | currentFontFile_->AddRef(); 182 | return S_OK; 183 | } 184 | 185 | private: 186 | ComPtr factory_; 187 | ComPtr currentFontFile_; 188 | const wchar_t* remainingFontFileNames_; 189 | const wchar_t* remainingFontFileNamesEnd_; 190 | HANDLE findHandle_; 191 | WIN32_FIND_DATA findData_; 192 | std::wstring fullPath_; 193 | }; 194 | 195 | 196 | class CustomCollectionFontFileEnumerator : public ComBase 197 | { 198 | protected: 199 | IFACEMETHODIMP QueryInterface(IID const& iid, __out void** object) OVERRIDE 200 | { 201 | COM_BASE_RETURN_INTERFACE(iid, IDWriteFontFileEnumerator, object); 202 | COM_BASE_RETURN_INTERFACE(iid, IUnknown, object); 203 | COM_BASE_RETURN_NO_INTERFACE(object); 204 | } 205 | 206 | 207 | public: 208 | CustomCollectionFontFileEnumerator() 209 | : currentFontFile_(nullptr), 210 | remainingFontFiles_(nullptr), 211 | remainingFontFilesEnd_(nullptr) 212 | { } 213 | 214 | 215 | HRESULT Initialize( 216 | _In_ IDWriteFactory* factory, 217 | _In_reads_(fontFileCount) IDWriteFontFile* const* fontFiles, 218 | uint32_t fontFilesCount 219 | ) throw() 220 | { 221 | if (factory == nullptr || (fontFiles == nullptr && fontFilesCount > 0)) 222 | return E_INVALIDARG; 223 | 224 | remainingFontFiles_ = fontFiles; 225 | remainingFontFilesEnd_ = fontFiles + fontFilesCount; 226 | return S_OK; 227 | } 228 | 229 | IFACEMETHODIMP MoveNext(_Out_ BOOL* hasCurrentFile) 230 | { 231 | *hasCurrentFile = (remainingFontFiles_ < remainingFontFilesEnd_); 232 | currentFontFile_ = *remainingFontFiles_; 233 | ++remainingFontFiles_; 234 | return S_OK; 235 | } 236 | 237 | IFACEMETHODIMP GetCurrentFontFile(_COM_Outptr_ IDWriteFontFile** fontFile) 238 | { 239 | *fontFile = currentFontFile_; 240 | currentFontFile_->AddRef(); 241 | return S_OK; 242 | } 243 | 244 | private: 245 | IDWriteFontFile* currentFontFile_; 246 | IDWriteFontFile* const* remainingFontFiles_; 247 | IDWriteFontFile* const* remainingFontFilesEnd_; 248 | }; 249 | 250 | 251 | class CustomFontCollectionLoader : public ComBase 252 | { 253 | protected: 254 | IFACEMETHODIMP QueryInterface(IID const& iid, __out void** object) OVERRIDE 255 | { 256 | COM_BASE_RETURN_INTERFACE(iid, IDWriteFontCollectionLoader, object); 257 | COM_BASE_RETURN_INTERFACE(iid, IUnknown, object); 258 | COM_BASE_RETURN_NO_INTERFACE(object); 259 | } 260 | 261 | public: 262 | IFACEMETHODIMP CreateEnumeratorFromKey( 263 | _In_ IDWriteFactory* factory, 264 | _In_bytecount_(collectionKeySize) void const* collectionKey, 265 | uint32_t collectionKeySize, 266 | _COM_Outptr_ IDWriteFontFileEnumerator** fontFileEnumerator 267 | ) 268 | { 269 | if (collectionKey == nullptr || collectionKeySize < sizeof(IDWriteFontFileEnumerator)) 270 | return E_INVALIDARG; 271 | 272 | // The collectionKey actually points to the address of the custom IDWriteFontFileEnumerator. 273 | IDWriteFontFileEnumerator* enumerator = *reinterpret_cast(collectionKey); 274 | enumerator->AddRef(); 275 | 276 | *fontFileEnumerator = enumerator; 277 | 278 | return S_OK; 279 | } 280 | 281 | static IDWriteFontCollectionLoader* GetInstance() 282 | { 283 | return &singleton_; 284 | } 285 | 286 | private: 287 | static CustomFontCollectionLoader singleton_; 288 | }; 289 | 290 | CustomFontCollectionLoader CustomFontCollectionLoader::singleton_; 291 | 292 | 293 | HRESULT CreateFontCollection( 294 | _In_ IDWriteFactory* factory, 295 | _In_reads_(fontFileNamesSize) const wchar_t* fontFileNames, 296 | _In_ uint32_t fontFileNamesSize, 297 | _COM_Outptr_ IDWriteFontCollection** fontCollection 298 | ) throw() 299 | { 300 | RETURN_ON_ZERO(CustomFontCollectionLoader::GetInstance(), E_FAIL); 301 | 302 | HRESULT hr = S_OK; 303 | 304 | CustomCollectionLocalFontFileEnumerator enumerator; 305 | IFR(enumerator.Initialize(factory, fontFileNames, fontFileNamesSize)); 306 | auto* enumeratorAddress = static_cast(&enumerator); 307 | enumeratorAddress->AddRef(); 308 | 309 | // Pass the address of the enumerator as the unique key. 310 | IFR(factory->RegisterFontCollectionLoader(CustomFontCollectionLoader::GetInstance())); 311 | hr = factory->CreateCustomFontCollection(CustomFontCollectionLoader::GetInstance(), &enumeratorAddress, sizeof(&enumeratorAddress), OUT fontCollection); 312 | IFR(factory->UnregisterFontCollectionLoader(CustomFontCollectionLoader::GetInstance())); 313 | 314 | return hr; 315 | } 316 | 317 | 318 | HRESULT CreateFontCollection( 319 | _In_ IDWriteFactory* factory, 320 | _In_reads_(fontFilesCount) IDWriteFontFile* const* fontFiles, 321 | uint32_t fontFilesCount, 322 | _COM_Outptr_ IDWriteFontCollection** fontCollection 323 | ) 324 | { 325 | RETURN_ON_ZERO(CustomFontCollectionLoader::GetInstance(), E_FAIL); 326 | 327 | HRESULT hr = S_OK; 328 | 329 | CustomCollectionFontFileEnumerator enumerator; 330 | IFR(enumerator.Initialize(factory, fontFiles, fontFilesCount)); 331 | auto* enumeratorAddress = static_cast(&enumerator); 332 | 333 | IFR(factory->RegisterFontCollectionLoader(CustomFontCollectionLoader::GetInstance())); 334 | hr = factory->CreateCustomFontCollection(CustomFontCollectionLoader::GetInstance(), &enumeratorAddress, sizeof(&enumeratorAddress), OUT fontCollection); 335 | IFR(factory->UnregisterFontCollectionLoader(CustomFontCollectionLoader::GetInstance())); 336 | 337 | return hr; 338 | } 339 | 340 | 341 | HRESULT CreateFontFaceFromFile( 342 | IDWriteFactory* factory, 343 | _In_z_ const wchar_t* fontFilePath, 344 | uint32_t fontFaceIndex, 345 | DWRITE_FONT_SIMULATIONS fontSimulations, 346 | _COM_Outptr_ IDWriteFontFace** fontFace 347 | ) throw() 348 | { 349 | ComPtr fontFile; 350 | IFR(factory->CreateFontFileReference( 351 | fontFilePath, 352 | nullptr, 353 | OUT &fontFile 354 | )); 355 | 356 | BOOL isSupportedFontType; 357 | DWRITE_FONT_FILE_TYPE fontFileType; 358 | DWRITE_FONT_FACE_TYPE fontFaceType; 359 | uint32_t numberOfFaces; 360 | 361 | IFR(fontFile->Analyze( 362 | OUT &isSupportedFontType, 363 | OUT &fontFileType, 364 | OUT &fontFaceType, 365 | OUT &numberOfFaces 366 | )); 367 | 368 | if (!isSupportedFontType) 369 | { 370 | return DWRITE_E_FILEFORMAT; 371 | } 372 | 373 | assert(fontFaceIndex < numberOfFaces); 374 | 375 | IDWriteFontFile* fontFileArray[] = {fontFile}; 376 | IFR(factory->CreateFontFace( 377 | fontFaceType, 378 | ARRAYSIZE(fontFileArray), 379 | fontFileArray, 380 | fontFaceIndex , 381 | fontSimulations, 382 | OUT fontFace 383 | )); 384 | 385 | return S_OK; 386 | } 387 | 388 | 389 | HRESULT CreateTextLayout( 390 | IDWriteFactory* factory, 391 | __in_ecount(textLength) wchar_t const* text, 392 | uint32_t textLength, 393 | IDWriteTextFormat* textFormat, 394 | float maxWidth, 395 | float maxHeight, 396 | DWRITE_MEASURING_MODE measuringMode, 397 | __out IDWriteTextLayout** textLayout 398 | ) throw() 399 | { 400 | if (measuringMode == DWRITE_MEASURING_MODE_NATURAL) 401 | { 402 | return factory->CreateTextLayout( 403 | text, 404 | textLength, 405 | textFormat, 406 | maxWidth, 407 | maxHeight, 408 | OUT textLayout 409 | ); 410 | } 411 | else 412 | { 413 | return factory->CreateGdiCompatibleTextLayout( 414 | text, 415 | textLength, 416 | textFormat, 417 | maxWidth, 418 | maxHeight, 419 | 1, // pixels per DIP 420 | nullptr, // no transform 421 | (measuringMode == DWRITE_MEASURING_MODE_GDI_NATURAL) ? true : false, 422 | OUT textLayout 423 | ); 424 | } 425 | } 426 | 427 | 428 | HRESULT GetLocalFileLoaderAndKey( 429 | IDWriteFontFace* fontFace, 430 | _Out_ void const** fontFileReferenceKey, 431 | _Out_ uint32_t& fontFileReferenceKeySize, 432 | _Out_ IDWriteLocalFontFileLoader** localFontFileLoader 433 | ) 434 | { 435 | *localFontFileLoader = nullptr; 436 | *fontFileReferenceKey = nullptr; 437 | fontFileReferenceKeySize = 0; 438 | 439 | if (fontFace == nullptr) 440 | return E_INVALIDARG; 441 | 442 | ComPtr fontFiles[8]; 443 | uint32_t fontFileCount = 0; 444 | 445 | IFR(fontFace->GetFiles(OUT &fontFileCount, nullptr)); 446 | if (fontFileCount > ARRAYSIZE(fontFiles)) 447 | return E_NOT_SUFFICIENT_BUFFER; 448 | 449 | IFR(fontFace->GetFiles(IN OUT &fontFileCount, OUT &fontFiles[0])); 450 | 451 | if (fontFileCount == 0 || fontFiles[0] == nullptr) 452 | return E_FAIL; 453 | 454 | ComPtr fontFileLoader; 455 | IFR(fontFiles[0]->GetLoader(OUT &fontFileLoader)); 456 | IFR(fontFileLoader->QueryInterface(OUT localFontFileLoader)); 457 | 458 | IFR(fontFiles[0]->GetReferenceKey(fontFileReferenceKey, OUT &fontFileReferenceKeySize)); 459 | 460 | return S_OK; 461 | } 462 | 463 | 464 | HRESULT GetFilePath( 465 | IDWriteFontFace* fontFace, 466 | _Out_ std::wstring& filePath 467 | ) throw() 468 | { 469 | filePath.clear(); 470 | wchar_t filePathBuffer[MAX_PATH]; 471 | filePathBuffer[0] = '\0'; 472 | 473 | ComPtr localFileLoader; 474 | uint32_t fontFileReferenceKeySize = 0; 475 | const void* fontFileReferenceKey = nullptr; 476 | 477 | IFR(GetLocalFileLoaderAndKey(fontFace, &fontFileReferenceKey, OUT fontFileReferenceKeySize, OUT &localFileLoader)); 478 | 479 | IFR(localFileLoader->GetFilePathFromKey( 480 | fontFileReferenceKey, 481 | fontFileReferenceKeySize, 482 | OUT filePathBuffer, 483 | ARRAY_SIZE(filePathBuffer) 484 | )); 485 | 486 | try 487 | { 488 | filePath.assign(filePathBuffer); 489 | } 490 | catch (std::bad_alloc) 491 | { 492 | return HRESULT_FROM_WIN32(E_OUTOFMEMORY); // This is the only exception type we need to worry about. 493 | } 494 | 495 | return S_OK; 496 | } 497 | 498 | 499 | HRESULT GetFileModifiedDate( 500 | IDWriteFontFace* fontFace, 501 | _Out_ FILETIME& fileTime 502 | ) throw() 503 | { 504 | const static FILETIME zeroFileTime = {}; 505 | fileTime = zeroFileTime; 506 | ComPtr localFileLoader; 507 | uint32_t fontFileReferenceKeySize = 0; 508 | const void* fontFileReferenceKey = nullptr; 509 | 510 | IFR(GetLocalFileLoaderAndKey(fontFace, &fontFileReferenceKey, OUT fontFileReferenceKeySize, OUT &localFileLoader)); 511 | 512 | IFR(localFileLoader->GetLastWriteTimeFromKey( 513 | fontFileReferenceKey, 514 | fontFileReferenceKeySize, 515 | OUT &fileTime 516 | )); 517 | 518 | return S_OK; 519 | } 520 | 521 | 522 | HRESULT GetLocalizedStringLanguage( 523 | IDWriteLocalizedStrings* strings, 524 | uint32_t stringIndex, 525 | OUT std::wstring& value 526 | ) 527 | { 528 | value.clear(); 529 | 530 | if (strings == nullptr) 531 | return E_INVALIDARG; 532 | 533 | uint32_t length = 0; 534 | strings->GetLocaleNameLength(stringIndex, OUT &length); 535 | if (length == 0 || length == UINT32_MAX) 536 | return S_OK; 537 | 538 | try 539 | { 540 | value.resize(length); 541 | } 542 | catch (std::bad_alloc) 543 | { 544 | return HRESULT_FROM_WIN32(E_OUTOFMEMORY); // This is the only exception type we need to worry about. 545 | } 546 | IFR(strings->GetLocaleName(stringIndex, OUT &value[0], length + 1)); 547 | 548 | return S_OK; 549 | } 550 | 551 | 552 | HRESULT GetLocalizedString( 553 | IDWriteLocalizedStrings* strings, 554 | _In_opt_z_ const wchar_t* preferredLanguage, 555 | OUT std::wstring& value 556 | ) 557 | { 558 | value.clear(); 559 | 560 | if (strings == nullptr) 561 | return E_INVALIDARG; 562 | 563 | uint32_t stringIndex = 0; 564 | BOOL exists = false; 565 | if (preferredLanguage != nullptr) 566 | { 567 | strings->FindLocaleName(preferredLanguage, OUT &stringIndex, OUT &exists); 568 | } 569 | 570 | if (!exists) 571 | { 572 | strings->FindLocaleName(L"en-us", OUT &stringIndex, OUT &exists); 573 | } 574 | 575 | if (!exists) 576 | { 577 | stringIndex = 0; // Just try the first index. 578 | } 579 | 580 | return GetLocalizedString(strings, stringIndex, OUT value); 581 | } 582 | 583 | 584 | HRESULT GetLocalizedString( 585 | IDWriteLocalizedStrings* strings, 586 | uint32_t stringIndex, 587 | OUT std::wstring& value 588 | ) 589 | { 590 | value.clear(); 591 | if (strings == nullptr || strings->GetCount() == 0) 592 | return S_OK; 593 | 594 | uint32_t length = 0; 595 | IFR(strings->GetStringLength(stringIndex, OUT &length)); 596 | if (length == 0 || length == UINT32_MAX) 597 | return S_OK; 598 | 599 | try 600 | { 601 | value.resize(length); 602 | } 603 | catch (std::bad_alloc) 604 | { 605 | return HRESULT_FROM_WIN32(E_OUTOFMEMORY); // This is the only exception type we need to worry about. 606 | } 607 | IFR(strings->GetString(stringIndex, OUT &value[0], length + 1)); 608 | 609 | return S_OK; 610 | } 611 | 612 | 613 | HRESULT GetFontFaceNameWws( 614 | IDWriteFont* font, 615 | _In_z_ wchar_t const* languageName, 616 | OUT std::wstring& value 617 | ) 618 | { 619 | value.clear(); 620 | if (font == nullptr) 621 | return E_INVALIDARG; 622 | 623 | ComPtr fontFaceNames; 624 | IFR(font->GetFaceNames(OUT &fontFaceNames)); 625 | IFR(GetLocalizedString(fontFaceNames, languageName, OUT value)); 626 | 627 | return S_OK; 628 | } 629 | 630 | 631 | HRESULT GetFontFamilyNameWws( 632 | IDWriteFont* font, 633 | _In_z_ wchar_t const* languageName, 634 | OUT std::wstring& value 635 | ) 636 | { 637 | value.clear(); 638 | if (font == nullptr) 639 | return E_INVALIDARG; 640 | 641 | ComPtr fontFamilyNames; 642 | ComPtr fontFamily; 643 | IFR(font->GetFontFamily(OUT &fontFamily)); 644 | IFR(fontFamily->GetFamilyNames(OUT &fontFamilyNames)); 645 | IFR(GetLocalizedString(fontFamilyNames, languageName, OUT value)); 646 | 647 | return S_OK; 648 | } 649 | 650 | 651 | HRESULT GetFontFamilyNameWws( 652 | IDWriteFontFamily* fontFamily, 653 | _In_z_ wchar_t const* languageName, 654 | OUT std::wstring& value 655 | ) 656 | { 657 | value.clear(); 658 | if (fontFamily == nullptr) 659 | return E_INVALIDARG; 660 | 661 | ComPtr fontFamilyNames; 662 | IFR(fontFamily->GetFamilyNames(OUT &fontFamilyNames)); 663 | IFR(GetLocalizedString(fontFamilyNames, languageName, OUT value)); 664 | 665 | return S_OK; 666 | } 667 | 668 | 669 | HRESULT GetLocalizedString( 670 | IDWriteFontSet* fontSet, 671 | uint32_t fontSetIndex, 672 | DWRITE_FONT_PROPERTY_ID fontPropertyId, 673 | _In_opt_z_ const wchar_t* preferredLanguage, 674 | OUT std::wstring& value 675 | ) throw() 676 | { 677 | value.clear(); 678 | if (fontSet == nullptr) 679 | return S_OK; 680 | 681 | ComPtr localizedStrings; 682 | BOOL dummyExists; 683 | IFR(fontSet->GetPropertyValues(fontSetIndex, fontPropertyId, OUT &dummyExists, OUT &localizedStrings)); 684 | 685 | return GetLocalizedString(localizedStrings, preferredLanguage, OUT value); 686 | } 687 | 688 | 689 | bool IsKnownFontFileExtension(_In_z_ const wchar_t* fileExtension) throw() 690 | { 691 | return (_wcsicmp(fileExtension, L".otf") == 0 692 | || _wcsicmp(fileExtension, L".ttf") == 0 693 | || _wcsicmp(fileExtension, L".ttc") == 0 694 | || _wcsicmp(fileExtension, L".tte") == 0 695 | ); 696 | } 697 | 698 | 699 | class BitmapRenderTargetTextRenderer : public IDWriteTextRenderer 700 | { 701 | public: 702 | 703 | BitmapRenderTargetTextRenderer( 704 | IDWriteBitmapRenderTarget* renderTarget, 705 | IDWriteRenderingParams* renderingParams 706 | ) 707 | : renderTarget_(renderTarget), 708 | renderingParams_(renderingParams) 709 | { } 710 | 711 | HRESULT STDMETHODCALLTYPE DrawGlyphRun( 712 | _In_ void* clientDrawingContext, 713 | _In_ float baselineOriginX, 714 | _In_ float baselineOriginY, 715 | DWRITE_MEASURING_MODE measuringMode, 716 | _In_ DWRITE_GLYPH_RUN const* glyphRun, 717 | _In_ DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription, 718 | _In_ IUnknown* clientDrawingEffects 719 | ) throw() 720 | { 721 | if (glyphRun->glyphCount <= 0) 722 | return S_OK; 723 | 724 | uint32_t textColor = 0x000000; 725 | renderTarget_->DrawGlyphRun( 726 | baselineOriginX, 727 | baselineOriginY, 728 | DWRITE_MEASURING_MODE_NATURAL, 729 | glyphRun, 730 | renderingParams_, 731 | textColor, 732 | nullptr // don't need blackBoxRect 733 | ); 734 | 735 | return S_OK; 736 | } 737 | 738 | HRESULT STDMETHODCALLTYPE DrawUnderline( 739 | _In_ void* clientDrawingContext, 740 | _In_ float baselineOriginX, 741 | _In_ float baselineOriginY, 742 | _In_ DWRITE_UNDERLINE const* underline, 743 | _In_ IUnknown* clientDrawingEffects 744 | ) throw() 745 | { 746 | return S_OK; 747 | } 748 | 749 | HRESULT STDMETHODCALLTYPE DrawStrikethrough( 750 | _In_ void* clientDrawingContext, 751 | _In_ float baselineOriginX, 752 | _In_ float baselineOriginY, 753 | _In_ DWRITE_STRIKETHROUGH const* strikethrough, 754 | _In_ IUnknown* clientDrawingEffects 755 | ) throw() 756 | { 757 | return S_OK; 758 | } 759 | 760 | HRESULT STDMETHODCALLTYPE DrawInlineObject( 761 | _In_ void* clientDrawingContext, 762 | _In_ float originX, 763 | _In_ float originY, 764 | _In_ IDWriteInlineObject* inlineObject, 765 | _In_ BOOL isSideways, 766 | _In_ BOOL isRightToLeft, 767 | _In_ IUnknown* clientDrawingEffects 768 | ) throw() 769 | { 770 | return S_OK; 771 | } 772 | 773 | HRESULT STDMETHODCALLTYPE IsPixelSnappingDisabled( 774 | _In_opt_ void* clientDrawingContext, 775 | _Out_ BOOL* isDisabled 776 | ) throw() 777 | { 778 | *isDisabled = false; 779 | return S_OK; 780 | } 781 | 782 | HRESULT STDMETHODCALLTYPE GetCurrentTransform( 783 | _In_opt_ void* clientDrawingContext, 784 | _Out_ DWRITE_MATRIX* transform 785 | ) throw() 786 | { 787 | *transform = identityTransform; 788 | return S_OK; 789 | } 790 | 791 | HRESULT STDMETHODCALLTYPE GetPixelsPerDip( 792 | _In_opt_ void* clientDrawingContext, 793 | _Out_ float* pixelsPerDip 794 | ) throw() 795 | { 796 | *pixelsPerDip = 1.0; 797 | return S_OK; 798 | } 799 | 800 | HRESULT STDMETHODCALLTYPE QueryInterface(IID const& iid, _Out_ void** object) throw() 801 | { 802 | *object = nullptr; 803 | return E_NOINTERFACE; 804 | } 805 | 806 | unsigned long STDMETHODCALLTYPE AddRef() throw() 807 | { 808 | return 1; // Static stack class 809 | } 810 | 811 | unsigned long STDMETHODCALLTYPE Release() throw() 812 | { 813 | return 1; // Static stack class 814 | } 815 | 816 | IDWriteBitmapRenderTarget* renderTarget_; // Weak pointers because class is stack local. 817 | IDWriteRenderingParams* renderingParams_; 818 | }; 819 | 820 | 821 | HRESULT DrawTextLayout( 822 | IDWriteBitmapRenderTarget* renderTarget, 823 | IDWriteRenderingParams* renderingParams, 824 | IDWriteTextLayout* textLayout, 825 | float x, 826 | float y 827 | ) 828 | { 829 | if (renderTarget == nullptr || renderingParams == nullptr || textLayout == nullptr) 830 | return E_INVALIDARG; 831 | 832 | BitmapRenderTargetTextRenderer textRenderer(renderTarget, renderingParams); 833 | return textLayout->Draw(nullptr, &textRenderer, x, y); 834 | } 835 | -------------------------------------------------------------------------------- /font/DWritEx.h: -------------------------------------------------------------------------------- 1 | //+--------------------------------------------------------------------------- 2 | // 3 | // Contents: DWrite helper functions for commonly needed tasks. 4 | // 5 | // History: 2009-09-09 dwayner 6 | // 7 | //---------------------------------------------------------------------------- 8 | 9 | HRESULT LoadDWrite( 10 | _In_z_ const wchar_t* dllPath, 11 | DWRITE_FACTORY_TYPE factoryType, // DWRITE_FACTORY_TYPE_SHARED 12 | _COM_Outptr_ IDWriteFactory** factory, 13 | _Out_ HMODULE& moduleHandle 14 | ) throw(); 15 | 16 | HRESULT CreateFontFaceFromFile( 17 | IDWriteFactory* factory, 18 | _In_z_ const wchar_t* fontFilePath, 19 | uint32_t fontFaceIndex, 20 | DWRITE_FONT_SIMULATIONS fontSimulations, // usually just DWRITE_FONT_SIMULATIONS_NONE 21 | _COM_Outptr_ IDWriteFontFace** fontFace 22 | ) throw(); 23 | 24 | HRESULT CreateFontCollection( 25 | _In_ IDWriteFactory* factory, 26 | _In_bytecount_(fontFileNamesSize) const wchar_t* fontFileNames, 27 | _In_ uint32_t fontFileNamesSize, // Number of wchar_t's, not number file name count 28 | _COM_Outptr_ IDWriteFontCollection** fontCollection 29 | ) throw(); 30 | 31 | HRESULT CreateFontCollection( 32 | _In_ IDWriteFactory* factory, 33 | _In_reads_(fontFilesCount) IDWriteFontFile* const* fontFiles, 34 | uint32_t fontFilesCount, 35 | _COM_Outptr_ IDWriteFontCollection** fontCollection 36 | ) throw(); 37 | 38 | HRESULT GetFilePath( 39 | IDWriteFontFace* fontFace, 40 | OUT std::wstring& filePath 41 | ) throw(); 42 | 43 | HRESULT GetFileModifiedDate( 44 | IDWriteFontFace* fontFace, 45 | _Out_ FILETIME& fileTime 46 | ) throw(); 47 | 48 | HRESULT CreateTextLayout( 49 | IDWriteFactory* factory, 50 | __in_ecount(textLength) wchar_t const* text, 51 | uint32_t textLength, 52 | IDWriteTextFormat* textFormat, 53 | float maxWidth, 54 | float maxHeight, 55 | DWRITE_MEASURING_MODE measuringMode, 56 | __out IDWriteTextLayout** textLayout 57 | ) throw(); 58 | 59 | HRESULT GetLocalizedStringLanguage( 60 | IDWriteLocalizedStrings* strings, 61 | uint32_t stringIndex, 62 | OUT std::wstring& value 63 | ) throw(); 64 | 65 | // Try to get the string in the preferred language, else English, else the first index. 66 | // The function does not return an error if the string does exist, just empty string. 67 | HRESULT GetLocalizedString( 68 | IDWriteLocalizedStrings* strings, 69 | _In_opt_z_ const wchar_t* preferredLanguage, 70 | OUT std::wstring& value 71 | ) throw(); 72 | 73 | // Get the string from the given index. 74 | // The function does not return an error if the string does exist, just empty string. 75 | HRESULT GetLocalizedString( 76 | IDWriteLocalizedStrings* strings, 77 | uint32_t stringIndex, 78 | OUT std::wstring& value 79 | ) throw(); 80 | 81 | HRESULT GetFontFaceNameWws( 82 | IDWriteFont* font, 83 | _In_z_ wchar_t const* languageName, 84 | OUT std::wstring& value 85 | ); 86 | 87 | // Returns the family name of thte font (in the language requested, if available, else the default English name). 88 | HRESULT GetFontFamilyNameWws( 89 | IDWriteFont* font, 90 | _In_z_ wchar_t const* languageName, 91 | OUT std::wstring& value 92 | ) throw(); 93 | 94 | HRESULT GetFontFamilyNameWws( 95 | IDWriteFontFamily* fontFamily, 96 | _In_z_ wchar_t const* languageName, 97 | OUT std::wstring& value 98 | ) throw(); 99 | 100 | HRESULT GetLocalizedString( 101 | IDWriteFontSet* fontSet, 102 | uint32_t fontSetIndex, 103 | DWRITE_FONT_PROPERTY_ID fontPropertyId, 104 | _In_opt_z_ const wchar_t* preferredLanguage, 105 | OUT std::wstring& value 106 | ) throw(); 107 | 108 | bool IsKnownFontFileExtension(_In_z_ const wchar_t* fileExtension) throw(); 109 | 110 | // Draw a text layout to a bitmap render target. 111 | HRESULT DrawTextLayout( 112 | IDWriteBitmapRenderTarget* renderTarget, 113 | IDWriteRenderingParams* renderingParams, 114 | IDWriteTextLayout* textLayout, 115 | float x, 116 | float y 117 | ); 118 | -------------------------------------------------------------------------------- /font/FontDownloader.cpp: -------------------------------------------------------------------------------- 1 | #include "../precomp.inc" 2 | #include 3 | #include 4 | 5 | using InternetHandle = AutoResource >; 6 | 7 | 8 | class InternetDownloader 9 | { 10 | protected: 11 | InternetHandle internetSession_; 12 | InternetHandle internetConnection_; 13 | InternetHandle internetRequest_; 14 | 15 | public: 16 | HRESULT EnsureInternetSession() 17 | { 18 | if (internetSession_ == nullptr) 19 | { 20 | // Use WinHttpOpen to obtain a session handle. 21 | internetSession_ = 22 | WinHttpOpen(L"FontSetViewer Test Application 1.0", 23 | WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, 24 | WINHTTP_NO_PROXY_NAME, 25 | WINHTTP_NO_PROXY_BYPASS, 26 | 0 27 | ); 28 | 29 | if (internetSession_ == nullptr) 30 | return HRESULT_FROM_WIN32(GetLastError()); 31 | } 32 | 33 | return S_OK; 34 | } 35 | 36 | 37 | static HRESULT GetServerAndFilePathFromUrl( 38 | std::wstring const& url, 39 | _Out_ std::wstring& serverName, 40 | _Out_ std::wstring& filePath 41 | ) 42 | { 43 | URL_COMPONENTS urlComponents = {}; 44 | urlComponents.dwStructSize = sizeof(urlComponents); 45 | urlComponents.dwHostNameLength = (unsigned long)-1; 46 | urlComponents.dwUrlPathLength = (unsigned long)-1; 47 | 48 | if (!WinHttpCrackUrl(url.c_str(), static_cast(url.size()), 0, OUT &urlComponents)) 49 | return HRESULT_FROM_WIN32(GetLastError()); 50 | 51 | serverName.reserve(urlComponents.dwHostNameLength); 52 | filePath.reserve(urlComponents.dwUrlPathLength); 53 | 54 | serverName.assign(urlComponents.lpszHostName, urlComponents.lpszHostName + urlComponents.dwHostNameLength); 55 | filePath.assign(urlComponents.lpszUrlPath, urlComponents.lpszUrlPath + urlComponents.dwUrlPathLength); 56 | 57 | return S_OK; 58 | } 59 | 60 | 61 | void clear() 62 | { 63 | internetRequest_.clear(); 64 | internetConnection_.clear(); 65 | internetSession_.clear(); 66 | } 67 | 68 | 69 | HRESULT VerifySuccessStatusCode() 70 | { 71 | uint32_t statusCode = 0; 72 | unsigned long bufferByteLength = sizeof(statusCode); 73 | if (!WinHttpQueryHeaders( 74 | internetRequest_, 75 | WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, 76 | WINHTTP_HEADER_NAME_BY_INDEX, 77 | OUT &statusCode, 78 | IN OUT &bufferByteLength, 79 | WINHTTP_NO_HEADER_INDEX 80 | )) 81 | { 82 | return HRESULT_FROM_WIN32(GetLastError()); 83 | } 84 | 85 | // Accept any informational or successful status codes, but not errors or redirects. 86 | if (statusCode < 100 || statusCode >= 300) 87 | return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 88 | 89 | return S_OK; 90 | } 91 | 92 | 93 | HRESULT PrepareDownloadRequest( 94 | std::wstring const& url 95 | ) 96 | { 97 | std::wstring serverName; 98 | std::wstring filePath; 99 | IFR(GetServerAndFilePathFromUrl(url, OUT serverName, OUT filePath)); 100 | 101 | IFR(EnsureInternetSession()); 102 | 103 | // Specify an HTTP server. 104 | if (internetConnection_ == nullptr) 105 | { 106 | internetConnection_ = 107 | WinHttpConnect( 108 | internetSession_, 109 | serverName.c_str(), 110 | INTERNET_DEFAULT_HTTP_PORT, // INTERNET_DEFAULT_HTTPS_PORT 111 | 0 // reserved 112 | ); 113 | 114 | if (internetConnection_ == nullptr) 115 | return HRESULT_FROM_WIN32(GetLastError()); 116 | } 117 | 118 | // Create an HTTP request handle. 119 | internetRequest_ = 120 | WinHttpOpenRequest( 121 | internetConnection_, 122 | L"GET", 123 | filePath.c_str(), 124 | NULL, // Version, use HTTP 1.1 125 | WINHTTP_NO_REFERER, 126 | WINHTTP_DEFAULT_ACCEPT_TYPES, 127 | 0 // WINHTTP_FLAG_SECURE 128 | ); 129 | if (internetRequest_ == nullptr) 130 | return HRESULT_FROM_WIN32(GetLastError()); 131 | 132 | return S_OK; 133 | } 134 | 135 | 136 | HRESULT SendDownloadRequest( 137 | uint64_t fileOffset, 138 | uint64_t fragmentSize 139 | ) 140 | { 141 | // Send a request. 142 | // Range: bytes=1073152-64313343 143 | std::wstring additionalHeader; 144 | if (fileOffset != 0 || fragmentSize != UINT64_MAX) 145 | { 146 | // If the range is anything except the entire file, request a specific range. 147 | GetFormattedString(OUT additionalHeader, L"Range: bytes=%lld-%lld", fileOffset, fileOffset + fragmentSize - 1); 148 | } 149 | 150 | if (!WinHttpSendRequest( 151 | internetRequest_, 152 | additionalHeader.c_str(), // Or else WINHTTP_NO_ADDITIONAL_HEADERS and length 0 153 | static_cast(additionalHeader.size()), 154 | WINHTTP_NO_REQUEST_DATA, 0, 155 | 0, 156 | 0 157 | )) 158 | { 159 | return HRESULT_FROM_WIN32(GetLastError()); 160 | } 161 | 162 | if (!WinHttpReceiveResponse(internetRequest_, nullptr)) 163 | return HRESULT_FROM_WIN32(GetLastError()); 164 | 165 | IFR(VerifySuccessStatusCode()); 166 | 167 | return S_OK; 168 | } 169 | 170 | 171 | HRESULT DownloadFile( 172 | std::wstring const& url, 173 | _Out_ std::vector& buffer 174 | ) 175 | { 176 | // This function only works with small enough files that fit into memory. 177 | 178 | IFR(PrepareDownloadRequest(url)); 179 | IFR(SendDownloadRequest(0, UINT64_MAX)); 180 | 181 | // Read the file size. 182 | uint64_t fileSize = 0; 183 | unsigned long headerBufferLength = sizeof(fileSize); 184 | if (!WinHttpQueryHeaders( 185 | internetRequest_, 186 | WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER, 187 | WINHTTP_HEADER_NAME_BY_INDEX, 188 | OUT &fileSize, 189 | IN OUT &headerBufferLength, 190 | WINHTTP_NO_HEADER_INDEX 191 | )) 192 | { 193 | return HRESULT_FROM_WIN32(GetLastError()); 194 | } 195 | 196 | if (fileSize >= UINT32_MAX) 197 | { 198 | return E_OUTOFMEMORY; 199 | } 200 | 201 | uint64_t bytesActuallyRead; 202 | buffer.resize(static_cast(fileSize)); 203 | IFR(DownloadRequestIntoBuffer(buffer.data(), fileSize, OUT bytesActuallyRead)); 204 | 205 | return S_OK; 206 | } 207 | 208 | 209 | HRESULT DownloadRequestIntoBuffer( 210 | _Out_writes_bytes_(bufferSize) void* buffer, 211 | uint64_t bytesToRead, 212 | _Out_ uint64_t& bytesActuallyRead 213 | ) 214 | { 215 | // Read data until there is nothing left. 216 | 217 | uint8_t* byteBuffer = reinterpret_cast(buffer); 218 | uint64_t bytesReadTotal = 0; 219 | unsigned long bytesAvailable = 0; 220 | do 221 | { 222 | // Check for available data. 223 | bytesAvailable = 0; 224 | if (!WinHttpQueryDataAvailable(internetRequest_, OUT &bytesAvailable)) 225 | return HRESULT_FROM_WIN32(GetLastError()); 226 | 227 | if (bytesAvailable > bytesToRead) 228 | bytesAvailable = static_cast(bytesToRead); 229 | 230 | unsigned long bytesRead = 0; 231 | if (!WinHttpReadData(internetRequest_, OUT &byteBuffer[bytesReadTotal], bytesAvailable, OUT &bytesRead)) 232 | return HRESULT_FROM_WIN32(GetLastError()); 233 | 234 | bytesReadTotal += bytesRead; 235 | bytesToRead -= bytesRead; 236 | 237 | } while (bytesAvailable > 0 && bytesToRead > 0); 238 | 239 | bytesActuallyRead = bytesReadTotal; 240 | 241 | return S_OK; 242 | } 243 | 244 | 245 | HRESULT GetFileSizeAndDate( 246 | std::wstring const& url, 247 | _Out_ uint64_t& fileSize, 248 | _Out_ FILETIME& fileTime 249 | ) 250 | { 251 | fileSize = 0; 252 | fileTime = {0}; 253 | 254 | IFR(EnsureInternetSession()); 255 | 256 | std::wstring serverName; 257 | std::wstring filePath; 258 | IFR(GetServerAndFilePathFromUrl(url, OUT serverName, OUT filePath)); 259 | 260 | // Specify an HTTP server. 261 | internetConnection_ = 262 | WinHttpConnect( 263 | internetSession_, 264 | serverName.c_str(), 265 | INTERNET_DEFAULT_HTTP_PORT, // INTERNET_DEFAULT_HTTPS_PORT 266 | 0 // reserved 267 | ); 268 | 269 | if (internetConnection_ == nullptr) 270 | return HRESULT_FROM_WIN32(GetLastError()); 271 | 272 | // Create an HTTP request handle. 273 | internetRequest_ = 274 | WinHttpOpenRequest( 275 | internetConnection_, 276 | L"HEAD", 277 | filePath.c_str(), 278 | nullptr, // Version, use HTTP 1.1 279 | WINHTTP_NO_REFERER, 280 | WINHTTP_DEFAULT_ACCEPT_TYPES, 281 | 0 // WINHTTP_FLAG_SECURE 282 | ); 283 | 284 | if (internetRequest_ == nullptr) 285 | return HRESULT_FROM_WIN32(GetLastError()); 286 | 287 | if (!WinHttpSendRequest( 288 | internetRequest_, 289 | WINHTTP_NO_ADDITIONAL_HEADERS, 0, 290 | WINHTTP_NO_REQUEST_DATA, 0, 291 | 0, // totalLength 292 | NULL // context 293 | )) 294 | { 295 | return HRESULT_FROM_WIN32(GetLastError()); 296 | } 297 | 298 | if (!WinHttpReceiveResponse(internetRequest_, nullptr)) 299 | return HRESULT_FROM_WIN32(GetLastError()); 300 | 301 | IFR(VerifySuccessStatusCode()); 302 | 303 | // Read the file size. 304 | uint32_t fileSizeUint32; 305 | unsigned long headerBufferLength = sizeof(fileSizeUint32); 306 | if (!WinHttpQueryHeaders( 307 | internetRequest_, 308 | WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER, 309 | WINHTTP_HEADER_NAME_BY_INDEX, 310 | OUT &fileSizeUint32, 311 | IN OUT &headerBufferLength, 312 | WINHTTP_NO_HEADER_INDEX 313 | )) 314 | { 315 | return HRESULT_FROM_WIN32(GetLastError()); 316 | } 317 | fileSize = fileSizeUint32; // Do I need to parse the number myself to get values larger than 32bits? 318 | 319 | // Read the file date. 320 | SYSTEMTIME systemTime; 321 | headerBufferLength = sizeof(systemTime); 322 | if (!WinHttpQueryHeaders( 323 | internetRequest_, 324 | WINHTTP_QUERY_LAST_MODIFIED | WINHTTP_QUERY_FLAG_SYSTEMTIME, 325 | WINHTTP_HEADER_NAME_BY_INDEX, 326 | OUT &systemTime, 327 | IN OUT &headerBufferLength, 328 | WINHTTP_NO_HEADER_INDEX 329 | )) 330 | { 331 | return HRESULT_FROM_WIN32(GetLastError()); 332 | } 333 | SystemTimeToFileTime(&systemTime, OUT &fileTime); 334 | 335 | return S_OK; 336 | } 337 | }; 338 | 339 | 340 | interface RemoteFontFileStreamInterfaceBinding : public IDWriteRemoteFontFileStream 341 | { 342 | }; 343 | 344 | class RemoteFontFileStream : public ComBase 345 | { 346 | public: 347 | static const uint64_t chunkSize_ = 16384; 348 | 349 | protected: 350 | std::wstring url_; 351 | std::wstring fileName_; 352 | std::vector chunkMap_; 353 | ComPtr downloadManager_; // optionally null 354 | ComPtr fontFileLoader_; 355 | uint64_t fileSize_ = 0; 356 | uint8_t* streamMemory_ = nullptr; 357 | FileHandle fileHandle_; 358 | FileHandle chunkMapFileHandle_; 359 | InternetDownloader internetDownloader_; 360 | 361 | protected: 362 | IFACEMETHODIMP QueryInterface(IID const& iid, __out void** object) OVERRIDE 363 | { 364 | COM_BASE_RETURN_INTERFACE(iid, IDWriteRemoteFontFileStream, object); 365 | COM_BASE_RETURN_INTERFACE(iid, IDWriteFontFileStream, object); 366 | COM_BASE_RETURN_INTERFACE_AMBIGUOUS(iid, IUnknown, object, static_cast(this)); 367 | COM_BASE_RETURN_NO_INTERFACE(object); 368 | } 369 | 370 | public: 371 | RemoteFontFileStream() 372 | { } 373 | 374 | HRESULT Initialize( 375 | const wchar_t* url, 376 | IDWriteFontDownloadQueue* downloadManager, 377 | IDWriteFontFileLoader* fontFileLoader 378 | ) 379 | { 380 | if (url == nullptr) 381 | return E_INVALIDARG; 382 | 383 | downloadManager_ = downloadManager; 384 | fontFileLoader_ = fontFileLoader; 385 | streamMemory_ = nullptr; 386 | url_ = url; 387 | 388 | // Create the file name from the URL. 389 | // Just use a simple munging to underscores for invalid characters. 390 | GetFileNameFromUrl(url_, OUT fileName_); 391 | 392 | fileHandle_ = CreateFile( 393 | fileName_.c_str(), 394 | GENERIC_READ | GENERIC_WRITE, 395 | FILE_SHARE_READ | FILE_SHARE_WRITE, 396 | nullptr, 397 | OPEN_EXISTING, 398 | FILE_ATTRIBUTE_NORMAL, 399 | nullptr 400 | ); 401 | 402 | if (fileHandle_ == INVALID_HANDLE_VALUE) 403 | { 404 | auto lastError = GetLastError(); 405 | if (lastError == ERROR_FILE_NOT_FOUND || lastError == ERROR_PATH_NOT_FOUND) 406 | { 407 | if (downloadManager_ != nullptr) 408 | { 409 | // downloadManager_->EnqueueFileFragmentDownload( 410 | // fontFileLoader_, 411 | // url_.c_str(), 412 | // (url_.size() + 1) * sizeof(url_[0]), 413 | // 0, // fileOffset, 414 | // 0 // fragmentSize 415 | // ); 416 | } 417 | 418 | return DWRITE_E_REMOTEFONT; 419 | } 420 | return HRESULT_FROM_WIN32(GetLastError()); 421 | } 422 | 423 | // Do not change the file time upon writes, leaving it the same as the 424 | // original server time, since later writes are just partial hydration 425 | // rather than actual meaningful content changes. 426 | FILETIME fileTime = { 0xFFFFFFFF, 0xFFFFFFFF }; 427 | SetFileTime(fileHandle_, nullptr, &fileTime, &fileTime); 428 | 429 | LARGE_INTEGER fileSize; 430 | ::GetFileSizeEx(fileHandle_, OUT &fileSize); 431 | fileSize_ = fileSize.QuadPart; 432 | auto numberOfChunks = static_cast((fileSize_ + chunkSize_ - 1) / chunkSize_); 433 | chunkMap_.resize(numberOfChunks); 434 | 435 | // Allocate space for later file reads, equal to the file size. 436 | streamMemory_ = reinterpret_cast(malloc(size_t(fileSize_))); 437 | if (streamMemory_ == nullptr) 438 | return HRESULT_FROM_WIN32(E_OUTOFMEMORY); // This is the only exception type we need to worry about. 439 | 440 | // Read the initial file contents into memory. 441 | ReadFile(fileHandle_, OUT &streamMemory_[0], static_cast(fileSize_), nullptr, nullptr); 442 | 443 | // Read the chunk map in too. 444 | std::wstring chunkMapFileName = fileName_; 445 | chunkMapFileName.append(L"_ChunkMap"); 446 | chunkMapFileHandle_ = CreateFile( 447 | chunkMapFileName.c_str(), 448 | GENERIC_READ | GENERIC_WRITE, 449 | FILE_SHARE_READ | FILE_SHARE_WRITE, 450 | nullptr, 451 | OPEN_ALWAYS, 452 | FILE_ATTRIBUTE_NORMAL, 453 | nullptr 454 | ); 455 | if (chunkMapFileHandle_ == INVALID_HANDLE_VALUE) 456 | { 457 | return HRESULT_FROM_WIN32(GetLastError()); 458 | } 459 | ReadFile(chunkMapFileHandle_, OUT chunkMap_.data(), static_cast(chunkMap_.size()), nullptr, nullptr); 460 | 461 | return S_OK; 462 | } 463 | 464 | 465 | ~RemoteFontFileStream() 466 | { 467 | free(streamMemory_); 468 | } 469 | 470 | 471 | static void GetCachedFontFilesPath(_Out_ std::wstring& cachePath) 472 | { 473 | cachePath.resize(MAX_PATH + 1); 474 | auto fileNameStartingIndex = GetTempPath(MAX_PATH + 1, OUT &cachePath[0]); 475 | cachePath.resize(fileNameStartingIndex); 476 | } 477 | 478 | 479 | static void GetFileNameFromUrl(std::wstring const& url, _Inout_ std::wstring& fileName) 480 | { 481 | GetCachedFontFilesPath(OUT fileName); 482 | 483 | // Grab the file name from the URL. 484 | // Just use a simple munging to underscores for invalid characters. 485 | const size_t filePrefixLength = 11; // 'CachedFont_' 486 | auto fileNameStartingIndex = fileName.size(); 487 | fileNameStartingIndex += filePrefixLength; 488 | fileName.append(L"CachedFont_", filePrefixLength); 489 | fileName.append(FindFileNameStart(url.c_str(), url.c_str() + url.size())); 490 | for (size_t i = fileNameStartingIndex, ci = fileName.size(); i < ci; ++i) 491 | { 492 | auto c = fileName[i]; 493 | switch (c) 494 | { 495 | case '/': 496 | case '\\': 497 | case '?': 498 | case '*': 499 | case ':': 500 | fileName[i] = '_'; 501 | } 502 | } 503 | } 504 | 505 | 506 | static HRESULT DeleteCachedFontFiles() 507 | { 508 | WIN32_FIND_DATA findData; 509 | 510 | // Get the first file matching the mask. 511 | wchar_t filePath[MAX_PATH + 1]; 512 | auto fileNameStartingIndex = GetTempPath(ARRAYSIZE(filePath), OUT &filePath[0]); 513 | wcsncat_s(IN OUT filePath, L"CachedFont_*", ARRAYSIZE(filePath)); 514 | 515 | HANDLE findHandle = FindFirstFile(filePath, OUT &findData); 516 | if (findHandle == INVALID_HANDLE_VALUE) 517 | { 518 | auto errorCode = GetLastError(); 519 | if (errorCode == ERROR_FILE_NOT_FOUND) 520 | return S_OK; 521 | 522 | return HRESULT_FROM_WIN32(errorCode); 523 | } 524 | 525 | do 526 | { 527 | if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 528 | continue; // Skip directories. 529 | 530 | filePath[fileNameStartingIndex] = '\0'; 531 | wcsncat_s(IN OUT filePath, findData.cFileName, ARRAYSIZE(filePath)); 532 | DeleteFile(filePath); 533 | 534 | } while (FindNextFile(findHandle, OUT &findData)); 535 | 536 | FindClose(findHandle); 537 | return S_OK; 538 | } 539 | 540 | 541 | IFACEMETHODIMP DownloadFileInformation() 542 | { 543 | return E_NOTIMPL; 544 | } 545 | 546 | 547 | HRESULT CheckFileFragment( 548 | uint64_t fileOffset, 549 | uint64_t fragmentSize, 550 | _Out_ BOOL* fragmentIsReady 551 | ) 552 | { 553 | *fragmentIsReady = false; 554 | 555 | if (fileOffset + fragmentSize < fileOffset || fileOffset + fragmentSize > fileSize_) 556 | return E_INVALIDARG; 557 | 558 | static_assert(((chunkSize_ - 1) & chunkSize_) == 0, "Chunk size must be a power of two."); 559 | uint32_t const lowFilePosition = fileOffset & ~(chunkSize_ - 1); 560 | uint32_t const highFilePosition = (fileOffset + fragmentSize + chunkSize_ - 1) & ~(chunkSize_ - 1); 561 | uint32_t const lowChunkMapIndex = uint32_t(lowFilePosition / chunkSize_); 562 | uint32_t const highChunkMapIndex = std::min(uint32_t(highFilePosition / chunkSize_), static_cast(chunkMap_.size())); 563 | 564 | bool allChunksArePresent = true; 565 | for (auto chunkMapIndex = lowChunkMapIndex; chunkMapIndex < highChunkMapIndex; ++chunkMapIndex) 566 | { 567 | if (!chunkMap_[chunkMapIndex]) 568 | { 569 | allChunksArePresent = false; 570 | break; 571 | } 572 | } 573 | 574 | if (allChunksArePresent) 575 | { 576 | *fragmentIsReady = true; 577 | } 578 | else if (downloadManager_ != nullptr) 579 | { 580 | // downloadManager_->EnqueueFileFragmentDownload( 581 | // fontFileLoader_, 582 | // url_.c_str(), 583 | // (url_.size() + 1) * sizeof(url_[0]), 584 | // fileOffset, 585 | // fragmentSize 586 | // ); 587 | } 588 | 589 | return S_OK; 590 | } 591 | 592 | 593 | IFACEMETHODIMP DownloadFileFragments( 594 | _In_reads_(fragmentCount) DWRITE_FILE_FRAGMENT const* fileFragments, 595 | uint32_t fragmentCount 596 | ) 597 | { 598 | return E_NOTIMPL; 599 | } 600 | 601 | 602 | IFACEMETHODIMP ReadFileFragment( 603 | _Outptr_result_bytebuffer_(fragmentSize) void const** fragmentStart, 604 | uint64_t fileOffset, 605 | uint64_t fragmentSize, 606 | _Out_ void** fragmentContext 607 | ) 608 | { 609 | *fragmentStart = &streamMemory_[fileOffset]; 610 | *fragmentContext = nullptr; 611 | 612 | BOOL fileFragmentIsReady = false; 613 | IFR(CheckFileFragment(fileOffset, fragmentSize, OUT &fileFragmentIsReady)); 614 | if (!fileFragmentIsReady) 615 | return DWRITE_E_REMOTEFONT; 616 | 617 | return S_OK; 618 | } 619 | 620 | 621 | IFACEMETHODIMP DownloadFileFragment( 622 | uint64_t fileOffset, 623 | uint64_t fragmentSize 624 | ) 625 | { 626 | if (fragmentSize == 0) 627 | return S_OK; 628 | 629 | if (fileOffset + fragmentSize < fileOffset || fileOffset + fragmentSize > fileSize_) 630 | return E_INVALIDARG; 631 | 632 | static_assert(((chunkSize_ - 1) & chunkSize_) == 0, "Chunk size must be a power of two."); 633 | uint32_t const lowFilePosition = fileOffset & ~(chunkSize_ - 1); 634 | uint32_t const highFilePosition = (fileOffset + fragmentSize + chunkSize_ - 1) & ~(chunkSize_ - 1); 635 | uint32_t const totalBytesToRead = highFilePosition - lowFilePosition; 636 | uint32_t const lowChunkMapIndex = uint32_t(lowFilePosition / chunkSize_); 637 | uint32_t const highChunkMapIndex = std::min(uint32_t(highFilePosition / chunkSize_), static_cast(chunkMap_.size())); 638 | 639 | // Read the file from the server. 640 | uint64_t totalBytesActuallyRead; 641 | IFR(internetDownloader_.PrepareDownloadRequest(url_)); 642 | IFR(internetDownloader_.SendDownloadRequest(lowFilePosition, totalBytesToRead)); 643 | IFR(internetDownloader_.DownloadRequestIntoBuffer(&streamMemory_[lowFilePosition], totalBytesToRead, OUT totalBytesActuallyRead)); 644 | 645 | LARGE_INTEGER filePosition; filePosition.QuadPart = lowFilePosition; 646 | SetFilePointerEx(fileHandle_, filePosition, nullptr, FILE_BEGIN); 647 | WriteFile(fileHandle_, &streamMemory_[lowFilePosition], static_cast(totalBytesActuallyRead), nullptr, nullptr); 648 | 649 | // Update the chunk map. 650 | for (auto chunkMapIndex = lowChunkMapIndex; chunkMapIndex < highChunkMapIndex; ++chunkMapIndex) 651 | { 652 | chunkMap_[chunkMapIndex] = true; 653 | } 654 | 655 | // Updated the chunkmap file contents. 656 | SetFilePointer(chunkMapFileHandle_, lowChunkMapIndex, nullptr, FILE_BEGIN); 657 | WriteFile(chunkMapFileHandle_, chunkMap_.data() + lowChunkMapIndex, static_cast(highChunkMapIndex - lowChunkMapIndex), nullptr, nullptr); 658 | 659 | return S_OK; 660 | } 661 | 662 | 663 | IFACEMETHODIMP IsFileFragmentLocal( 664 | uint64_t fileOffset, 665 | uint64_t fragmentSize, 666 | _Out_ BOOL* isLocal 667 | ) override 668 | { 669 | *isLocal = false; 670 | return E_NOTIMPL; 671 | } 672 | 673 | 674 | IFACEMETHODIMP_(DWRITE_LOCALITY) GetLocality() override 675 | { 676 | return DWRITE_LOCALITY_REMOTE; 677 | } 678 | 679 | 680 | IFACEMETHODIMP_(void) ReleaseFileFragment( 681 | void* fragmentContext 682 | ) 683 | { 684 | // Nothing to do. 685 | } 686 | 687 | 688 | IFACEMETHODIMP GetFileSize( 689 | _Out_ uint64_t* fileSize 690 | ) 691 | { 692 | *fileSize = fileSize_; 693 | return S_OK; 694 | } 695 | 696 | 697 | IFACEMETHODIMP GetLocalFileSize( 698 | _Out_ UINT64* localFileSize 699 | ) 700 | { 701 | // hack: should check chunkmap. 702 | *localFileSize = fileSize_; 703 | return S_OK; 704 | } 705 | 706 | 707 | STDMETHOD(GetLastWriteTime)( 708 | _Out_ uint64_t* lastWriteTime 709 | ) 710 | { 711 | if (!GetFileTime(fileHandle_, nullptr, nullptr, OUT reinterpret_cast(lastWriteTime))) 712 | { 713 | *lastWriteTime = 0; 714 | } 715 | return S_OK; 716 | } 717 | 718 | 719 | IFACEMETHODIMP PrefetchRemoteFileFragment( 720 | uint64_t fileOffset, 721 | uint64_t fragmentSize, 722 | _Out_ BOOL* fileFragmentIsReady 723 | ) 724 | { 725 | IFR(CheckFileFragment(fileOffset, fragmentSize, OUT fileFragmentIsReady)); 726 | return S_OK; 727 | } 728 | 729 | IFACEMETHODIMP CancelDownload() throw() override 730 | { 731 | return E_NOTIMPL; 732 | } 733 | }; 734 | 735 | 736 | class RemoteStreamFontFileLoader : public ComBase 737 | { 738 | protected: 739 | ComPtr downloadManager_; 740 | InternetDownloader internetDownloader_; 741 | 742 | protected: 743 | IFACEMETHODIMP QueryInterface(IID const& iid, __out void** object) OVERRIDE 744 | { 745 | COM_BASE_RETURN_INTERFACE(iid, IDWriteRemoteFontFileLoader, object); 746 | COM_BASE_RETURN_INTERFACE(iid, IDWriteFontFileLoader, object); 747 | COM_BASE_RETURN_INTERFACE(iid, IUnknown, object); 748 | COM_BASE_RETURN_NO_INTERFACE(object); 749 | } 750 | 751 | public: 752 | IFACEMETHODIMP CreateStreamFromKey( 753 | _In_reads_bytes_(fontFileReferenceKeySize) void const* fontFileReferenceKey, 754 | uint32_t fontFileReferenceKeySize, 755 | _COM_Outptr_ IDWriteFontFileStream** fontFileStream 756 | ) 757 | { 758 | if (fontFileReferenceKey == nullptr || fontFileReferenceKeySize < sizeof(wchar_t)) 759 | return E_INVALIDARG; 760 | 761 | const wchar_t* urlPointer = reinterpret_cast(fontFileReferenceKey); 762 | 763 | auto* newFontFileStream = new RemoteFontFileStream(); 764 | ComPtr fontFileStreamScope(newFontFileStream); 765 | IFR(newFontFileStream->Initialize(urlPointer, downloadManager_, this)); 766 | *fontFileStream = fontFileStreamScope.Detach(); 767 | 768 | return S_OK; 769 | } 770 | 771 | 772 | static RemoteStreamFontFileLoader* GetInstance() 773 | { 774 | return &singleton_; 775 | } 776 | 777 | 778 | IFACEMETHODIMP GetLocalityFromKey( 779 | _In_reads_bytes_(fontFileReferenceKeySize) void const* fontFileReferenceKey, 780 | uint32_t fontFileReferenceKeySize, 781 | _Out_ DWRITE_LOCALITY* fileLocality 782 | ) override 783 | { 784 | // Hack - always return partial. 785 | *fileLocality = DWRITE_LOCALITY_PARTIAL; 786 | return S_OK; 787 | } 788 | 789 | 790 | IFACEMETHODIMP CreateRemoteStreamFromKey( 791 | _In_reads_bytes_(fontFileReferenceKeySize) void const* fontFileReferenceKey, 792 | UINT32 fontFileReferenceKeySize, 793 | _COM_Outptr_ IDWriteRemoteFontFileStream** fontFileStream 794 | ) override 795 | { 796 | return E_NOTIMPL; 797 | } 798 | 799 | 800 | IFACEMETHODIMP DownloadStreamInformationFromKey( 801 | _In_reads_bytes_(fontFileReferenceKeySize) void const* fontFileReferenceKey, 802 | uint32_t fontFileReferenceKeySize 803 | ) 804 | { 805 | const wchar_t* urlPointer = reinterpret_cast(fontFileReferenceKey); 806 | std::wstring url(urlPointer); 807 | std::wstring fileName; 808 | RemoteFontFileStream::GetFileNameFromUrl(url, OUT fileName); 809 | 810 | auto fileAttributes = GetFileAttributes(fileName.c_str()); 811 | if (fileAttributes != INVALID_FILE_ATTRIBUTES && !(fileAttributes & FILE_ATTRIBUTE_DIRECTORY)) 812 | { 813 | return S_OK; // Already created. 814 | } 815 | 816 | // Read the date from the server. 817 | uint64_t fileSize; 818 | FILETIME fileTime = { 0xFFFFFFFF, 0xFFFFFFFF }; 819 | internetDownloader_.GetFileSizeAndDate(url, OUT fileSize, OUT fileTime); 820 | 821 | // Create the sparse file. 822 | FileHandle fileHandle = CreateFile( 823 | fileName.c_str(), 824 | GENERIC_READ|GENERIC_WRITE, 825 | 0, // No file sharing allowed until size and date or set, not even FILE_SHARE_READ. 826 | nullptr, 827 | OPEN_ALWAYS, 828 | FILE_ATTRIBUTE_NORMAL, 829 | nullptr 830 | ); 831 | if (fileHandle == INVALID_HANDLE_VALUE) 832 | return HRESULT_FROM_WIN32(GetLastError()); 833 | 834 | DWORD bytesReturned; 835 | DeviceIoControl( 836 | fileHandle, 837 | FSCTL_SET_SPARSE, 838 | NULL, 839 | 0, 840 | NULL, 841 | 0, 842 | OUT &bytesReturned, 843 | NULL 844 | ); 845 | // If an error occurs trying to set it to sparse, oh well. It's continuable. 846 | 847 | // Set the initial size and date. 848 | LARGE_INTEGER fileSizeLi; 849 | fileSizeLi.QuadPart = fileSize; 850 | SetFilePointerEx(fileHandle, fileSizeLi, nullptr, FILE_BEGIN); 851 | SetEndOfFile(fileHandle); 852 | SetFileTime(fileHandle, nullptr, &fileTime, &fileTime); 853 | 854 | fileHandle.clear(); 855 | 856 | return S_OK; 857 | } 858 | 859 | 860 | void SetDownloadManager(IDWriteFontDownloadQueue* downloadManager) 861 | { 862 | downloadManager_ = downloadManager; 863 | } 864 | 865 | void Finalize() override 866 | { 867 | downloadManager_.clear(); 868 | internetDownloader_.clear(); 869 | } 870 | 871 | private: 872 | static RemoteStreamFontFileLoader singleton_; 873 | }; 874 | 875 | RemoteStreamFontFileLoader RemoteStreamFontFileLoader::singleton_; 876 | 877 | 878 | class RemoteFontDownloadManager : public ComBase 879 | { 880 | private: 881 | struct EnqueuedRequest 882 | { 883 | ComPtr fontLoader; 884 | std::vector fileKey; 885 | std::vector ranges; 886 | }; 887 | 888 | struct RangeComparer 889 | { 890 | bool operator() (const Range& lhs, const Range& rhs) const throw() 891 | { 892 | if (lhs.begin < rhs.begin) return true; 893 | if (lhs.begin > rhs.begin) return false; 894 | return (lhs.end < rhs.end); 895 | } 896 | }; 897 | 898 | static RemoteFontDownloadManager singleton_; 899 | std::vector enqueuedRequests_; 900 | 901 | protected: 902 | IFACEMETHODIMP QueryInterface(IID const& iid, __out void** object) OVERRIDE 903 | { 904 | COM_BASE_RETURN_INTERFACE(iid, IDWriteFontDownloadQueue, object); 905 | COM_BASE_RETURN_INTERFACE(iid, IUnknown, object); 906 | COM_BASE_RETURN_NO_INTERFACE(object); 907 | } 908 | 909 | public: 910 | static IDWriteFontDownloadQueue* GetInstance() 911 | { 912 | return &singleton_; 913 | } 914 | 915 | STDMETHODIMP RegisterRemoteFontHandler( 916 | IDWriteFontDownloadListener* remoteFontHandler 917 | ) throw() 918 | { 919 | return E_NOTIMPL; 920 | } 921 | 922 | IFACEMETHODIMP UnregisterRemoteFontHandler( 923 | IDWriteFontDownloadListener* remoteFontHandler 924 | ) throw() 925 | { 926 | return E_NOTIMPL; 927 | } 928 | 929 | IFACEMETHODIMP AddListener( 930 | IDWriteFontDownloadListener* listener, 931 | _Out_ uint32_t* token 932 | ) throw() override 933 | { 934 | return E_NOTIMPL; 935 | } 936 | 937 | IFACEMETHODIMP RemoveListener( 938 | uint32_t token 939 | ) throw() override 940 | { 941 | return E_NOTIMPL; 942 | } 943 | 944 | IFACEMETHODIMP_(BOOL) IsQueueEmpty() throw() override 945 | { 946 | return enqueuedRequests_.empty(); 947 | } 948 | 949 | IFACEMETHODIMP EnqueueFileFragmentDownload( 950 | IDWriteFontFileLoader* fontLoader, 951 | _In_reads_(fileKeySize) void const* fileKey, 952 | UINT32 fileKeySize, 953 | UINT64 fileOffset, 954 | UINT64 fragmentSize 955 | ) throw() 956 | { 957 | EnqueuedRequest* newRequest = nullptr; 958 | for (auto& existingRequest : enqueuedRequests_) 959 | { 960 | if (existingRequest.fileKey.size() == fileKeySize 961 | && memcmp(existingRequest.fileKey.data(), fileKey, fileKeySize) == 0) 962 | { 963 | // Found an existing request with the same file key. 964 | newRequest = &existingRequest; 965 | } 966 | } 967 | if (newRequest == nullptr) 968 | { 969 | enqueuedRequests_.resize(enqueuedRequests_.size() + 1); 970 | newRequest = &enqueuedRequests_.back(); 971 | uint8_t const* byteKey = reinterpret_cast(fileKey); 972 | newRequest->fontLoader = fontLoader; 973 | newRequest->fileKey.assign(byteKey, byteKey + fileKeySize); 974 | } 975 | Range range = { uint32_t(fileOffset), uint32_t(fileOffset + fragmentSize) }; 976 | newRequest->ranges.push_back(range); 977 | 978 | return S_OK; 979 | } 980 | 981 | IFACEMETHODIMP EnqueueFontDownload( 982 | IDWriteFontFaceReference* fontFaceReference 983 | ) throw() 984 | { 985 | return E_NOTIMPL; 986 | } 987 | 988 | IFACEMETHODIMP EnqueueCharactersDownload( 989 | IDWriteFontFaceReference* fontFaceReference, 990 | _In_reads_(characterCount) WCHAR const* characters, 991 | UINT32 characterCount 992 | ) throw() 993 | { 994 | return E_NOTIMPL; 995 | } 996 | 997 | IFACEMETHODIMP EnqueueGlyphsDownload( 998 | IDWriteFontFaceReference* fontFaceReference, 999 | _In_reads_(glyphCount) UINT16 const* glyphs, 1000 | UINT32 glyphCount 1001 | ) throw() 1002 | { 1003 | return E_NOTIMPL; 1004 | } 1005 | 1006 | IFACEMETHODIMP BeginDownload(_In_opt_ IUnknown* context) throw() override 1007 | { 1008 | if (enqueuedRequests_.empty()) 1009 | return S_FALSE; 1010 | 1011 | for (auto& request : enqueuedRequests_) 1012 | { 1013 | if (request.fontLoader == nullptr || request.ranges.empty()) 1014 | continue; 1015 | 1016 | // Sort all the range requests into ascending order, 1017 | // coalescing adjacent ranges. 1018 | 1019 | auto& ranges = request.ranges; 1020 | std::sort(ranges.begin(), ranges.end(), RangeComparer()); 1021 | size_t const oldRangesSize = ranges.size(); 1022 | size_t currentRangeIndex = 1, newRangesSize = 1; 1023 | 1024 | for ( ; currentRangeIndex < oldRangesSize; ++currentRangeIndex) 1025 | { 1026 | auto& previousRange = ranges[newRangesSize - 1]; 1027 | auto& currentRange = ranges[currentRangeIndex]; 1028 | if (currentRange.begin <= previousRange.end) 1029 | { 1030 | previousRange.end = std::max(currentRange.end, previousRange.end); // Extend range. 1031 | } 1032 | else 1033 | { 1034 | ranges[newRangesSize++] = currentRange; // Copy over a new range. 1035 | } 1036 | } 1037 | ranges.resize(newRangesSize); 1038 | 1039 | // If there is an empty range, extend it to the granularity. 1040 | if (ranges.size() == 1 && ranges.front().end == 0) 1041 | { 1042 | ranges.front().end = RemoteFontFileStream::chunkSize_; 1043 | } 1044 | 1045 | ComPtr remoteFontFileLoader; 1046 | request.fontLoader->QueryInterface(OUT &remoteFontFileLoader); 1047 | if (remoteFontFileLoader != nullptr) 1048 | { 1049 | // remoteFontFileLoader->DownloadStreamInformationFromKey( 1050 | // request.fileKey.data(), 1051 | // uint32_t(request.fileKey.size()) 1052 | // ); 1053 | } 1054 | 1055 | ComPtr fontFileStream; 1056 | ComPtr remoteFontFileStream; 1057 | request.fontLoader->CreateStreamFromKey( 1058 | request.fileKey.data(), 1059 | uint32_t(request.fileKey.size()), 1060 | OUT &fontFileStream 1061 | ); 1062 | 1063 | if (fontFileStream != nullptr) 1064 | { 1065 | fontFileStream->QueryInterface(OUT &remoteFontFileStream); 1066 | } 1067 | if (remoteFontFileStream != nullptr) 1068 | { 1069 | for (auto const& range : ranges) 1070 | { 1071 | // remoteFontFileStream->DownloadFileFragment(range.begin, range.end - range.begin); 1072 | } 1073 | } 1074 | 1075 | request.fontLoader.Clear(); 1076 | } 1077 | 1078 | CancelDownload(); 1079 | return S_OK; 1080 | } 1081 | 1082 | IFACEMETHODIMP CancelDownload() throw() override 1083 | { 1084 | enqueuedRequests_.clear(); 1085 | return E_NOTIMPL; 1086 | } 1087 | 1088 | void Finalize() override 1089 | { 1090 | //downloadManager_.clear(); 1091 | } 1092 | }; 1093 | 1094 | RemoteFontDownloadManager RemoteFontDownloadManager::singleton_; 1095 | 1096 | 1097 | #if 0 1098 | auto fontFileLoader = RemoteStreamFontFileLoader::GetInstance(); 1099 | IFR(dwriteFactory3p->RegisterFontFileLoader(fontFileLoader)); 1100 | fontFileLoader->SetDownloadManager(RemoteFontDownloadManager::GetInstance()); 1101 | 1102 | const wchar_t* urlList[] = { 1103 | L"http://dwayner-test/fonts/GoogleFontDirectory/alice/Alice-Regular.ttf", 1104 | L"http://dwayner-test/fonts/AdobeOpenType_fonts/ACaslonPro-Bold.otf", 1105 | L"http://dwayner-test/fonts/www.dafont.com/15x5.ttf", 1106 | L"http://dwayner-test/fonts/www.dafont.com/HVD_Peace.ttf", 1107 | L"http://dwayner-test/Fonts/AdobeOpenType_fonts/KozGoPr6N-Bold.otf", 1108 | L"http://dwayner-test/Fonts/www.dafont.com/catcrypt.ttf", 1109 | }; 1110 | DWRITE_FONT_PROPERTY properties[][2] = { 1111 | { { DWRITE_FONT_PROPERTY_ID_FULL_NAME, L"Alice", L"en-us" },{ DWRITE_FONT_PROPERTY_ID_WEIGHT_STRETCH_STYLE_FAMILY_NAME, L"Alice", L"en-us" }, }, 1112 | { { DWRITE_FONT_PROPERTY_ID_FULL_NAME, L"Adobe Carlson Pro", L"en-us" },{ DWRITE_FONT_PROPERTY_ID_WEIGHT_STRETCH_STYLE_FAMILY_NAME, L"Adobe Carlson Pro", L"en-us" }, }, 1113 | { { DWRITE_FONT_PROPERTY_ID_FULL_NAME, L"15x5", L"en-us" },{ DWRITE_FONT_PROPERTY_ID_WEIGHT_STRETCH_STYLE_FAMILY_NAME, L"15x5", L"en-us" }, }, 1114 | { { DWRITE_FONT_PROPERTY_ID_FULL_NAME, L"HVD Peace", L"en-us" },{ DWRITE_FONT_PROPERTY_ID_WEIGHT_STRETCH_STYLE_FAMILY_NAME, L"HVD Peace", L"en-us" }, }, 1115 | { { DWRITE_FONT_PROPERTY_ID_FULL_NAME, L"Kozuka Gothic Pro", L"en-us" },{ DWRITE_FONT_PROPERTY_ID_WEIGHT_STRETCH_STYLE_FAMILY_NAME, L"Kozuka Gothic Pro", L"en-us" }, }, 1116 | { { DWRITE_FONT_PROPERTY_ID_FULL_NAME, L"Cat Crypt", L"en-us" },{ DWRITE_FONT_PROPERTY_ID_WEIGHT_STRETCH_STYLE_FAMILY_NAME, L"Cat Crypt", L"en-us" }, }, 1117 | }; 1118 | static_assert(ARRAYSIZE(urlList) == ARRAYSIZE(properties), "Array sizes must match"); 1119 | 1120 | auto fontFileLoader = RemoteStreamFontFileLoader::GetInstance(); 1121 | dwriteFactory3p->RegisterFontFileLoader(fontFileLoader); 1122 | fontFileLoader->SetDownloadManager(RemoteFontDownloadManager::GetInstance()); 1123 | 1124 | for (uint32_t i = 0; i < ARRAYSIZE(urlList); ++i) 1125 | { 1126 | ComPtr fontFile; 1127 | ComPtr fontFaceReference; 1128 | 1129 | wchar_t const* fontFileReferenceKey = urlList[i]; 1130 | uint32_t fontFileReferenceKeySize = (wcslen(fontFileReferenceKey) + 1) * sizeof(wchar_t); 1131 | IFR(dwriteFactory3p->CreateCustomFontFileReference( 1132 | fontFileReferenceKey, 1133 | fontFileReferenceKeySize, 1134 | fontFileLoader, 1135 | OUT &fontFile 1136 | )); 1137 | 1138 | IFR(dwriteFactory3p->CreateFontFaceReference( 1139 | fontFile, 1140 | 0, // faceIndex 1141 | DWRITE_FONT_SIMULATIONS_NONE, 1142 | OUT &fontFaceReference 1143 | )); 1144 | 1145 | fontSetBuilder->AddFontFaceReference( 1146 | fontFaceReference, 1147 | &properties[i][0], 1148 | 2 // propertiesCount 1149 | ); 1150 | } 1151 | 1152 | //////////////////////////////////////// 1153 | // Download the font set from the server. 1154 | 1155 | std::vector rawJsonFile; 1156 | InternetDownloader internetDownloader; 1157 | IFR(internetDownloader.DownloadFile(g_fontSetUrl, OUT rawJsonFile)); 1158 | internetDownloader.clear(); 1159 | 1160 | // Read all commands into a text tree. 1161 | std::wstring fontSetText; 1162 | ConvertText(rawJsonFile, OUT fontSetText); 1163 | TextTree nodes; 1164 | uint32_t textLength = static_cast(fontSetText.size()); 1165 | JsonexParser parser(fontSetText.data(), textLength, TextTreeParser::OptionsNoEscapeSequence); 1166 | parser.ReadNodes(IN OUT nodes); 1167 | 1168 | uint32_t nodeIndex = 0; 1169 | if (!nodes.AdvanceChildNode(IN OUT nodeIndex) // skip the root node. 1170 | || nodes.GetNode(nodeIndex).type != TextTree::Node::TypeArray 1171 | || !nodes.AdvanceChildNode(IN OUT nodeIndex)) 1172 | { 1173 | return S_FALSE; 1174 | } 1175 | 1176 | std::wstring filePath; 1177 | std::wstring familyName; 1178 | std::wstring fullName; 1179 | std::wstring weight; 1180 | std::wstring stretch; 1181 | std::wstring slope; 1182 | std::wstring value; 1183 | uint32_t faceIndex = 0; 1184 | 1185 | DWRITE_FONT_PROPERTY properties[5] = { 1186 | { DWRITE_FONT_PROPERTY_ID_FULL_NAME, L"FullName", L"en-us" }, 1187 | { DWRITE_FONT_PROPERTY_ID_WEIGHT_STRETCH_STYLE_FAMILY_NAME, L"WssFamilyName", L"en-us" }, 1188 | { DWRITE_FONT_PROPERTY_ID_WEIGHT, L"Weight", L"" }, 1189 | { DWRITE_FONT_PROPERTY_ID_STRETCH, L"Stretch", L"" }, 1190 | { DWRITE_FONT_PROPERTY_ID_STYLE, L"Slope", L"" }, 1191 | }; 1192 | 1193 | while (nodeIndex < nodes.GetNodeCount()) 1194 | { 1195 | if (nodes.GetNode(nodeIndex).type == TextTree::Node::TypeObject) 1196 | { 1197 | uint32_t subnodeIndex; 1198 | nodes.GetKeyValue(nodeIndex, L"Path", OUT filePath); 1199 | 1200 | if (nodes.FindKey(nodeIndex, L"FullName", OUT subnodeIndex)) 1201 | { 1202 | nodes.GetKeyValue(subnodeIndex, L"en-us", OUT fullName); 1203 | } 1204 | if (nodes.FindKey(nodeIndex, L"WssFamilyName", OUT subnodeIndex)) 1205 | { 1206 | nodes.GetKeyValue(subnodeIndex, L"en-us", OUT familyName); 1207 | } 1208 | if (nodes.GetKeyValue(nodeIndex, L"FaceIndex", OUT value)) 1209 | { 1210 | faceIndex = _wtoi(value.c_str()); 1211 | } 1212 | nodes.GetKeyValue(nodeIndex, L"Weight", OUT weight); 1213 | nodes.GetKeyValue(nodeIndex, L"Stretch", OUT stretch); 1214 | nodes.GetKeyValue(nodeIndex, L"Slope", OUT slope); 1215 | 1216 | ComPtr fontFile; 1217 | ComPtr fontFaceReference; 1218 | 1219 | wchar_t const* fontFileReferenceKey = filePath.data(); 1220 | uint32_t fontFileReferenceKeySize = (static_cast(filePath.size()) + 1) * sizeof(wchar_t); 1221 | IFR(dwriteFactory3p->CreateCustomFontFileReference( 1222 | fontFileReferenceKey, 1223 | fontFileReferenceKeySize, 1224 | fontFileLoader, 1225 | OUT &fontFile 1226 | )); 1227 | 1228 | IFR(dwriteFactory3p->CreateFontFaceReference( 1229 | fontFile, 1230 | faceIndex, 1231 | DWRITE_FONT_SIMULATIONS_NONE, 1232 | OUT &fontFaceReference 1233 | )); 1234 | 1235 | static_assert(ARRAYSIZE(properties) == 5, "Update this code to match the size"); 1236 | properties[0].propertyValue = fullName.c_str(); 1237 | properties[1].propertyValue = familyName.c_str(); 1238 | properties[2].propertyValue = weight.c_str(); 1239 | properties[3].propertyValue = stretch.c_str(); 1240 | properties[4].propertyValue = slope.c_str(); 1241 | fontSetBuilder->AddFontFaceReference( 1242 | fontFaceReference, 1243 | &properties[0], 1244 | ARRAYSIZE(properties) // propertiesCount 1245 | ); 1246 | } 1247 | 1248 | if (!nodes.AdvanceNextNode(IN OUT nodeIndex)) 1249 | break; 1250 | } 1251 | #endif 1252 | -------------------------------------------------------------------------------- /font/precomp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../precomp.inc" 4 | -------------------------------------------------------------------------------- /precomp.cpp: -------------------------------------------------------------------------------- 1 | #include "precomp.h" 2 | -------------------------------------------------------------------------------- /precomp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "precomp.inc" 4 | -------------------------------------------------------------------------------- /precomp.inc: -------------------------------------------------------------------------------- 1 | // include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | #pragma once 5 | 6 | #ifndef UNICODE 7 | #define UNICODE 8 | #endif 9 | #ifndef _UNICODE 10 | #define _UNICODE 11 | #endif 12 | 13 | #include "sdkddkver.h" 14 | 15 | // Disable unknown pragma warnings (for when when compiling directly with VC) 16 | // and not under build.exe. 17 | #pragma warning(disable: 4068) 18 | 19 | // Modify the following defines if you have to target a platform prior to the ones specified below. 20 | // Refer to MSDN for the latest info on corresponding values for different platforms. 21 | #ifndef WINVER // Allow use of features specific to Windows 7 or later. 22 | #define WINVER 0x0701 // Change this to the appropriate value to target other versions of Windows. 23 | #endif 24 | 25 | #ifndef _WIN32_WINNT // Allow use of features specific to Windows 7 or later. 26 | #define _WIN32_WINNT 0x0601 // Change this to the appropriate value to target other versions of Windows. 27 | #endif 28 | 29 | #ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later. 30 | #define _WIN32_WINDOWS 0x0710 // Change this to the appropriate value to target Windows Me or later. 31 | #endif 32 | 33 | #ifndef _WIN32_IE // Allow use of features specific to IE 6.0 or later. 34 | #define _WIN32_IE 0x0700 // Change this to the appropriate value to target other versions of IE. 35 | #endif 36 | 37 | #ifndef WIN32_LEAN_AND_MEAN 38 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 39 | #define NOMINMAX 40 | #endif 41 | 42 | ////////////////////////////// 43 | 44 | #ifndef BUILD_OPTIMIZATION_STRING 45 | #if defined(DEBUG) 46 | #define BUILD_OPTIMIZATION_STRING L"Debug" 47 | #else 48 | #define BUILD_OPTIMIZATION_STRING L"Release" 49 | #endif 50 | #endif 51 | 52 | #ifndef BUILD_ARCHITECTURE_STRING 53 | #if defined(_M_IX86) 54 | #define BUILD_ARCHITECTURE_STRING L"x86 32-bit" 55 | #elif defined(_M_X64) 56 | #define BUILD_ARCHITECTURE_STRING L"64-bit" 57 | #else 58 | #define BUILD_ARCHITECTURE_STRING L"Unknown" 59 | #endif 60 | #endif 61 | 62 | #ifndef BUILD_TITLE_STRING 63 | #define BUILD_TITLE_STRING L"FontCollectionViewer" 64 | #endif 65 | 66 | #ifndef APPLICATION_TITLE 67 | #define APPLICATION_TITLE L"Font Collection Viewer" 68 | #endif 69 | 70 | 71 | ////////////////////////////// 72 | 73 | #include "common\Macros.h" 74 | 75 | ////////////////////////////// 76 | // C RunTime Header Files 77 | #include 78 | #include 79 | #include 80 | #include 81 | #include 82 | #include 83 | #include 84 | #include 85 | #include 86 | #include 87 | #include 88 | #include 89 | #include 90 | #include 91 | #include 92 | #include 93 | #include 94 | 95 | 96 | // Work around conflict in VS2010 of IntSafe.h conflicting with stdint.h 97 | #define _INTSAFE_H_INCLUDED_ 98 | 99 | #ifndef NTDDI_WIN10_RS3 100 | #define NTDDI_WIN10_RS3 0x0A000003 /* ABRACADABRA_WIN10_RS3 */ 101 | #endif 102 | #undef NTDDI_VERSION 103 | #define NTDDI_VERSION NTDDI_WIN10_RS3 104 | 105 | 106 | ////////////////////////////// 107 | // Windows Header Files: 108 | 109 | #include 110 | #include 111 | #include 112 | #include 113 | 114 | #include 115 | #include 116 | #include // for font folder path 117 | #include 118 | #include 119 | 120 | ////////////////////////////// 121 | // DirectX headers 122 | 123 | #include 124 | #include 125 | #include 126 | 127 | ////////////////////////////// 128 | // Common headers 129 | 130 | #include "common/Common.h" 131 | #include "common/AutoResource.h" 132 | #include "common/Pointers.h" 133 | #include "common/Unicode.h" 134 | #include "common/FileHelpers.h" 135 | #include "Common/TextTreeParser.h" 136 | #include "Common/WindowUtility.h" 137 | 138 | ////////////////////////////// 139 | 140 | // Need this for common controls. 141 | // Otherwise the app either looks ugly, 142 | // or it doesn't show anything at all 143 | // (except an empty window). 144 | #ifdef _UNICODE 145 | #if defined _M_IX86 146 | #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") 147 | 148 | #elif defined _M_IA64 149 | #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"") 150 | 151 | #elif defined _M_X64 152 | #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") 153 | 154 | #else 155 | #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") 156 | 157 | #endif 158 | #endif 159 | 160 | #ifndef HINST_THISCOMPONENT 161 | EXTERN_C IMAGE_DOS_HEADER __ImageBase; 162 | #define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase) 163 | // If __ImageBase does not exist in your linker, 164 | // try GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, &pObjectInModule, &handle) instead. 165 | #endif 166 | -------------------------------------------------------------------------------- /resources/DWriteIcon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fdwr/FontSetViewer/76c7ac6e16f3c01d1523fda439e021e0e1c0ce39/resources/DWriteIcon.ico -------------------------------------------------------------------------------- /resources/FontSetFilterIcons.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fdwr/FontSetViewer/76c7ac6e16f3c01d1523fda439e021e0e1c0ce39/resources/FontSetFilterIcons.bmp -------------------------------------------------------------------------------- /resources/FontSetFilterIcons24bit.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fdwr/FontSetViewer/76c7ac6e16f3c01d1523fda439e021e0e1c0ce39/resources/FontSetFilterIcons24bit.bmp -------------------------------------------------------------------------------- /resources/FontSetViewer.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | DWrite custom layout sample 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | true 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /resources/FontSetViewer.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fdwr/FontSetViewer/76c7ac6e16f3c01d1523fda439e021e0e1c0ce39/resources/FontSetViewer.rc -------------------------------------------------------------------------------- /resources/family-blank.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fdwr/FontSetViewer/76c7ac6e16f3c01d1523fda439e021e0e1c0ce39/resources/family-blank.ico -------------------------------------------------------------------------------- /resources/font-blank.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fdwr/FontSetViewer/76c7ac6e16f3c01d1523fda439e021e0e1c0ce39/resources/font-blank.ico -------------------------------------------------------------------------------- /resources/resource.h: -------------------------------------------------------------------------------- 1 | // 2 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 3 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 4 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 5 | // PARTICULAR PURPOSE. 6 | // 7 | // Copyright (c) Microsoft Corporation. All rights reserved 8 | // 9 | //---------------------------------------------------------------------------- 10 | 11 | #define IddMainWindow 129 12 | 13 | #define IdiMain 1 14 | #define IdiFamilyBlank 2 15 | #define IdiFontBlank 3 16 | #define IdbFontCollectionFilter 4 17 | 18 | #define IdcLog 1000 19 | #define IdcTags 1001 20 | #define IdcFontCollectionList 1002 21 | #define IdcFontCollectionFilter 1003 22 | #define IdcActivateFont 1004 23 | #define IdcActivateFontCollectionFilter 1005 24 | #define IdcSelectViaChooseFont 1006 25 | #define IdcIncludeRemoteFonts 1007 26 | #define IdcDownloadRemoteFonts 1008 27 | #define IdcClearRemoteFontCache 1009 28 | #define IdcViewRemoteFontCache 1010 29 | #define IdcViewSortedFonts 1011 30 | #define IdcOpenFontFiles 1012 31 | #define IdcReloadSystemFontSet 1013 32 | #define IdcViewFontPreview 1014 33 | #define IdcCopyListNames 1015 34 | 35 | #define MenuIdMain 1 36 | #define MenuIdOptions 32769 37 | #define MenuIdText 32770 38 | #define MenuIdEdit 32771 39 | #define MenuIdReadingDirection 32772 40 | #define MenuIdOrientation 32773 41 | 42 | 43 | #define MenuIdListView 32780 44 | #define MenuIdListViewFirst 32781 45 | #define MenuIdListViewIcon 32781 46 | #define MenuIdListViewReport 32782 47 | #define MenuIdListViewSmallIcon 32783 48 | #define MenuIdListViewList 32784 49 | #define MenuIdListViewTile 32785 50 | #define MenuIdListViewLast 32785 51 | 52 | #define MenuIdLanguage 32790 53 | #define MenuIdLanguageFirst 32791 54 | #define MenuIdLanguageLast 32809 55 | 56 | // #define LV_VIEW_ICON 0x0000 57 | // #define LV_VIEW_DETAILS 0x0001 58 | // #define LV_VIEW_SMALLICON 0x0002 59 | // #define LV_VIEW_LIST 0x0003 60 | // #define LV_VIEW_TILE 0x0004 61 | 62 | // 63 | -------------------------------------------------------------------------------- /sdk_headers/DCommon.h: -------------------------------------------------------------------------------- 1 | //+-------------------------------------------------------------------------- 2 | // 3 | // Copyright (c) Microsoft Corporation. All rights reserved. 4 | // 5 | // Abstract: 6 | // Public API definitions shared by DWrite, D2D, and DImage. 7 | // 8 | //---------------------------------------------------------------------------- 9 | #pragma once 10 | 11 | #ifndef DCOMMON_H_INCLUDED 12 | #define DCOMMON_H_INCLUDED 13 | 14 | #include 15 | 16 | #ifndef DX_DECLARE_INTERFACE 17 | #define DX_DECLARE_INTERFACE(x) DECLSPEC_UUID(x) DECLSPEC_NOVTABLE 18 | #endif 19 | 20 | #ifndef CHECKMETHOD 21 | #define CHECKMETHOD(method) virtual DECLSPEC_NOTHROW _Must_inspect_result_ HRESULT STDMETHODCALLTYPE method 22 | #endif 23 | 24 | #pragma warning(push) 25 | #pragma warning(disable:4201) // anonymous unions warning 26 | 27 | // 28 | // Forward declarations 29 | // 30 | struct IDXGISurface; 31 | 32 | /// 33 | /// The measuring method used for text layout. 34 | /// 35 | typedef enum DWRITE_MEASURING_MODE 36 | { 37 | /// 38 | /// Text is measured using glyph ideal metrics whose values are independent to the current display resolution. 39 | /// 40 | DWRITE_MEASURING_MODE_NATURAL, 41 | 42 | /// 43 | /// Text is measured using glyph display compatible metrics whose values tuned for the current display resolution. 44 | /// 45 | DWRITE_MEASURING_MODE_GDI_CLASSIC, 46 | 47 | /// 48 | // Text is measured using the same glyph display metrics as text measured by GDI using a font 49 | // created with CLEARTYPE_NATURAL_QUALITY. 50 | /// 51 | DWRITE_MEASURING_MODE_GDI_NATURAL 52 | 53 | } DWRITE_MEASURING_MODE; 54 | 55 | #if NTDDI_VERSION >= NTDDI_WIN10_RS1 56 | 57 | /// 58 | /// Fonts may contain multiple drawable data formats for glyphs. These flags specify which formats 59 | /// are supported in the font, either at a font-wide level or per glyph, and the app may use them 60 | /// to tell DWrite which formats to return when splitting a color glyph run. 61 | /// 62 | enum DWRITE_GLYPH_IMAGE_FORMATS 63 | { 64 | /// 65 | /// Indicates no data is available for this glyph. 66 | /// 67 | DWRITE_GLYPH_IMAGE_FORMATS_NONE = 0x00000000, 68 | 69 | /// 70 | /// The glyph has TrueType outlines. 71 | /// 72 | DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE = 0x00000001, 73 | 74 | /// 75 | /// The glyph has CFF outlines. 76 | /// 77 | DWRITE_GLYPH_IMAGE_FORMATS_CFF = 0x00000002, 78 | 79 | /// 80 | /// The glyph has multilayered COLR data. 81 | /// 82 | DWRITE_GLYPH_IMAGE_FORMATS_COLR = 0x00000004, 83 | 84 | /// 85 | /// The glyph has SVG outlines as standard XML. 86 | /// 87 | /// 88 | /// Fonts may store the content gzip'd rather than plain text, 89 | /// indicated by the first two bytes as gzip header {0x1F 0x8B}. 90 | /// 91 | DWRITE_GLYPH_IMAGE_FORMATS_SVG = 0x00000008, 92 | 93 | /// 94 | /// The glyph has PNG image data, with standard PNG IHDR. 95 | /// 96 | DWRITE_GLYPH_IMAGE_FORMATS_PNG = 0x00000010, 97 | 98 | /// 99 | /// The glyph has JPEG image data, with standard JIFF SOI header. 100 | /// 101 | DWRITE_GLYPH_IMAGE_FORMATS_JPEG = 0x00000020, 102 | 103 | /// 104 | /// The glyph has TIFF image data. 105 | /// 106 | DWRITE_GLYPH_IMAGE_FORMATS_TIFF = 0x00000040, 107 | 108 | /// 109 | /// The glyph has raw 32-bit premultiplied BGRA data. 110 | /// 111 | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8 = 0x00000080, 112 | }; 113 | 114 | #ifdef DEFINE_ENUM_FLAG_OPERATORS 115 | DEFINE_ENUM_FLAG_OPERATORS(DWRITE_GLYPH_IMAGE_FORMATS); 116 | #endif 117 | 118 | #endif 119 | 120 | /// 121 | /// Qualifies how alpha is to be treated in a bitmap or render target containing 122 | /// alpha. 123 | /// 124 | typedef enum D2D1_ALPHA_MODE 125 | { 126 | 127 | /// 128 | /// Alpha mode should be determined implicitly. Some target surfaces do not supply 129 | /// or imply this information in which case alpha must be specified. 130 | /// 131 | D2D1_ALPHA_MODE_UNKNOWN = 0, 132 | 133 | /// 134 | /// Treat the alpha as premultipled. 135 | /// 136 | D2D1_ALPHA_MODE_PREMULTIPLIED = 1, 137 | 138 | /// 139 | /// Opacity is in the 'A' component only. 140 | /// 141 | D2D1_ALPHA_MODE_STRAIGHT = 2, 142 | 143 | /// 144 | /// Ignore any alpha channel information. 145 | /// 146 | D2D1_ALPHA_MODE_IGNORE = 3, 147 | 148 | D2D1_ALPHA_MODE_FORCE_DWORD = 0xffffffff 149 | 150 | } D2D1_ALPHA_MODE; 151 | 152 | /// 153 | /// Description of a pixel format. 154 | /// 155 | typedef struct D2D1_PIXEL_FORMAT 156 | { 157 | DXGI_FORMAT format; 158 | D2D1_ALPHA_MODE alphaMode; 159 | 160 | } D2D1_PIXEL_FORMAT; 161 | 162 | /// 163 | /// Represents an x-coordinate and y-coordinate pair in two-dimensional space. 164 | /// 165 | typedef struct D2D_POINT_2U 166 | { 167 | UINT32 x; 168 | UINT32 y; 169 | 170 | } D2D_POINT_2U; 171 | 172 | /// 173 | /// Represents an x-coordinate and y-coordinate pair in two-dimensional space. 174 | /// 175 | typedef struct D2D_POINT_2F 176 | { 177 | FLOAT x; 178 | FLOAT y; 179 | 180 | } D2D_POINT_2F; 181 | 182 | typedef POINT D2D_POINT_2L; 183 | 184 | /// 185 | /// A vector of 2 FLOAT values (x, y). 186 | /// 187 | typedef struct D2D_VECTOR_2F 188 | { 189 | FLOAT x; 190 | FLOAT y; 191 | 192 | } D2D_VECTOR_2F; 193 | 194 | 195 | /// 196 | /// A vector of 3 FLOAT values (x, y, z). 197 | /// 198 | typedef struct D2D_VECTOR_3F 199 | { 200 | FLOAT x; 201 | FLOAT y; 202 | FLOAT z; 203 | 204 | } D2D_VECTOR_3F; 205 | 206 | 207 | /// 208 | /// A vector of 4 FLOAT values (x, y, z, w). 209 | /// 210 | typedef struct D2D_VECTOR_4F 211 | { 212 | FLOAT x; 213 | FLOAT y; 214 | FLOAT z; 215 | FLOAT w; 216 | 217 | } D2D_VECTOR_4F; 218 | 219 | 220 | /// 221 | /// Represents a rectangle defined by the coordinates of the upper-left corner 222 | /// (left, top) and the coordinates of the lower-right corner (right, bottom). 223 | /// 224 | typedef struct D2D_RECT_F 225 | { 226 | FLOAT left; 227 | FLOAT top; 228 | FLOAT right; 229 | FLOAT bottom; 230 | 231 | } D2D_RECT_F; 232 | 233 | 234 | /// 235 | /// Represents a rectangle defined by the coordinates of the upper-left corner 236 | /// (left, top) and the coordinates of the lower-right corner (right, bottom). 237 | /// 238 | typedef struct D2D_RECT_U 239 | { 240 | UINT32 left; 241 | UINT32 top; 242 | UINT32 right; 243 | UINT32 bottom; 244 | 245 | } D2D_RECT_U; 246 | 247 | typedef RECT D2D_RECT_L; 248 | 249 | /// 250 | /// Stores an ordered pair of floats, typically the width and height of a rectangle. 251 | /// 252 | typedef struct D2D_SIZE_F 253 | { 254 | FLOAT width; 255 | FLOAT height; 256 | 257 | } D2D_SIZE_F; 258 | 259 | 260 | /// 261 | /// Stores an ordered pair of integers, typically the width and height of a 262 | /// rectangle. 263 | /// 264 | typedef struct D2D_SIZE_U 265 | { 266 | UINT32 width; 267 | UINT32 height; 268 | 269 | } D2D_SIZE_U; 270 | 271 | 272 | /// 273 | /// Represents a 3-by-2 matrix. 274 | /// 275 | typedef struct D2D_MATRIX_3X2_F 276 | { 277 | union 278 | { 279 | struct 280 | { 281 | /// 282 | /// Horizontal scaling / cosine of rotation 283 | /// 284 | FLOAT m11; 285 | 286 | /// 287 | /// Vertical shear / sine of rotation 288 | /// 289 | FLOAT m12; 290 | 291 | /// 292 | /// Horizontal shear / negative sine of rotation 293 | /// 294 | FLOAT m21; 295 | 296 | /// 297 | /// Vertical scaling / cosine of rotation 298 | /// 299 | FLOAT m22; 300 | 301 | /// 302 | /// Horizontal shift (always orthogonal regardless of rotation) 303 | /// 304 | FLOAT dx; 305 | 306 | /// 307 | /// Vertical shift (always orthogonal regardless of rotation) 308 | /// 309 | FLOAT dy; 310 | }; 311 | 312 | struct 313 | { 314 | FLOAT _11, _12; 315 | FLOAT _21, _22; 316 | FLOAT _31, _32; 317 | }; 318 | 319 | FLOAT m[3][2]; 320 | }; 321 | 322 | } D2D_MATRIX_3X2_F; 323 | 324 | 325 | 326 | /// 327 | /// Represents a 4-by-3 matrix. 328 | /// 329 | typedef struct D2D_MATRIX_4X3_F 330 | { 331 | union 332 | { 333 | struct 334 | { 335 | FLOAT _11, _12, _13; 336 | FLOAT _21, _22, _23; 337 | FLOAT _31, _32, _33; 338 | FLOAT _41, _42, _43; 339 | }; 340 | 341 | FLOAT m[4][3]; 342 | }; 343 | 344 | } D2D_MATRIX_4X3_F; 345 | 346 | 347 | /// 348 | /// Represents a 4-by-4 matrix. 349 | /// 350 | typedef struct D2D_MATRIX_4X4_F 351 | { 352 | union 353 | { 354 | struct 355 | { 356 | FLOAT _11, _12, _13, _14; 357 | FLOAT _21, _22, _23, _24; 358 | FLOAT _31, _32, _33, _34; 359 | FLOAT _41, _42, _43, _44; 360 | }; 361 | 362 | FLOAT m[4][4]; 363 | }; 364 | 365 | } D2D_MATRIX_4X4_F; 366 | 367 | 368 | /// 369 | /// Represents a 5-by-4 matrix. 370 | /// 371 | typedef struct D2D_MATRIX_5X4_F 372 | { 373 | union 374 | { 375 | struct 376 | { 377 | FLOAT _11, _12, _13, _14; 378 | FLOAT _21, _22, _23, _24; 379 | FLOAT _31, _32, _33, _34; 380 | FLOAT _41, _42, _43, _44; 381 | FLOAT _51, _52, _53, _54; 382 | }; 383 | 384 | FLOAT m[5][4]; 385 | }; 386 | 387 | } D2D_MATRIX_5X4_F; 388 | 389 | 390 | typedef D2D_POINT_2F D2D1_POINT_2F; 391 | typedef D2D_POINT_2U D2D1_POINT_2U; 392 | typedef D2D_POINT_2L D2D1_POINT_2L; 393 | typedef D2D_RECT_F D2D1_RECT_F; 394 | typedef D2D_RECT_U D2D1_RECT_U; 395 | typedef D2D_RECT_L D2D1_RECT_L; 396 | typedef D2D_SIZE_F D2D1_SIZE_F; 397 | typedef D2D_SIZE_U D2D1_SIZE_U; 398 | typedef D2D_MATRIX_3X2_F D2D1_MATRIX_3X2_F; 399 | 400 | 401 | #pragma warning(pop) 402 | 403 | #endif /* DCOMMON_H_INCLUDED */ 404 | --------------------------------------------------------------------------------