Many thanks to @Jonney3099 for his help with this. PD's font selection box will now preferentially supply custom preview text in the following order of script support: CJK Arabic Hebrew Latin [Unknown scripts] More scripts are forthcoming, but I wanted a good mix of ideographs and RTL languages for this initial test. The Latin chars "AaBbCc 123" are appended to all previews, as all fonts are likely to support these base ASCII characters. If a font supports any of the listed scripts, extra text specific to that script is appended to the preview. This is awesome for getting an "at-a-glance" idea of what kind of Unicode coverage a font provides. I've also revisited a bunch of the sizing decisions with the control. The drop-down is now wider, with much more whitespace, and the font previews are significantly larger than before. This is important for ideographic scripts, which need a lot of room to breathe. Finally, to do this 100% correctly, I need to provide a UI where the user can specify a language of choice. This is relevant not just for translations, but for knowing which script to preferentially preview in the font dropdown. If the user has a preferred script, we could always display previews in that script (if supported by a font). Until I've gotten that framework implemented, however, I think this is a nice intermediary solution.
This not only works, it works brilliantly and very quickly - much more quickly than I anticipated! Fonts are not required to explicitly list the scripts they support, so this mechanism isn't foolproof (though it's really only a problem for amateur fonts). The only "foolproof" method, if such a thing exists, is to manually probe Unicode character ranges, then map these to expected script ranges. That's incredibly cumbersome so I'm not apt to try it.
Windows sometimes iterates the same font twice - but not in order, bizarrely - so this lets us catch and remove the duplicates post-sorting.
Hard linebreaks in the original string are still problematic, but with this commit, we have everything we need for *automatic* word-wrapping (which was a blocker for all remaining typography tool tasks, so yay for finishing it!). My main hold-up with hard line breaks is figuring out which class should assume responsibility for them. Uniscribe doesn't touch hard line breaks. (The paragraph is the largest unit of text it addresses.) Theoretically, the caller is supposed to break strings into paragraphs prior to interacting with Uniscribe. After Uniscribe processes all paragraphs, the caller then (somehow) manually merges the multiple Uniscribe results into one conglomerate run suited for display. This separation of responsibility isn't ideal for PD, as the text renderer doesn't handle character-level details. It just forwards the relevant text to Uniscribe, then awaits a list of glyphs it can blindly iterate. Ideally, line-breaks would exist in that list of glyphs, perhaps as specially marked "non-display" glyphs that are easily recognized in the pre-rendering layout loop. The only way to easily maintain this division of labor is to have PD's Uniscribe class "inject" fake linebreak glyphs into the final glyph collection, which is messy as there's not a 1:1 correlation between characters (what the class receives) and glyphs (what the class returns). As I see it, I'll need to pre-scan the string, make a note of all hard linebreak positions (and the usual Cr/Lf normalizing), replace linebreaks with spaces, do all Uniscribe processing as if it's a contiguous paragraph, then use the logical cluster information from ScriptShape to figure out which glyphs correlate to which characters in the original string, and use that to convert placeholder space glyphs back into linebreak markers. What a headache. :/ That said, there are two big benefits to offloading linebreak handling onto pdUniscribe: 1) When provided with a clipping width, pdUniscribe can return the glyph collection already divided into lines. This will make pdTextRenderer's work much, much simpler. 2) It's a little unwieldy, but Uniscribe is capable of calculating justified text positioning, which is awesome as most image editors are restricted to left/center/right only. (Justifying Arabic text in particular is a nightmare, but Uniscribe can handle this via the specialized ScriptJustify function.) I wasn't planning on handling fully justified alignment, but I may as well if we're going to be doing other alignment stuff inside pdUniscribe. Anyway, writing all this out is mostly for my own sake as I'm thinking of tackling some rendering-specific bits before finishing PD's Uniscribe interface, and I don't want to forget my plans in the interim!
1) Fixed an incorrect struct definition 2) Relative substring coordinates are now stored within each item, making it easier to calculate coordinates between the original string and individual items. 3) Neutral items are now merged, which improves performance by preventing Uniscribe from its default behavior of breaking strings at punctuation marks, even if the marks are not script-dependent (e.g. a compound English sentence would normally be split at the comma, which generates extra items and kills performance). 4) ScriptBreak function stub added
With its last round of updates, this has become one of PD's finest tools, and it deserves more attention.
Missing glyphs are now properly filled with blank character markers, and we now detect Uniscribe return values in a way that will make future font fallback support easier.
Man, this was a ton of work. PD's custom glyph renderer can now handle intermixed LTR and RTL text without trouble. This is disastrously complex and it requires a ton of unsafe pointer manipulation, but these are the hoops through which we jump when interacting with complicated low-level APIs. This work was also a prerequisite to proper line-breaking, which is a prerequisite to implementing horizontal and vertical alignment options, so it needed to be done. With this commit, strings with missing glyphs (e.g. characters not supported by the current font) are going to cause trouble. I'm working on an intermediate fallback until I have font linking working, but for now, the output will be unpredictable gibberish.
It's tough to wrap my head around where bits lie in these complicated structs, since we have to modify the types to fix alignment issues. Bidi levels are now being marked correctly (though they aren't being processed on a per-run basis just yet).
Add this to the "holy crap I can't believe this worked" pile. ;) To demonstrate the *awesome* text features PD can now provide, try the following on Win 7 or later: 1) Create a new image with default settings 2) Switch to the typography tool (the fancy "t" icon) 3) Set the font to Segoe Script and choose a nice, big size (100 px) 4) Click-drag a large text layer, then type something like "we want all the things" If you watch the image as you type each character, you can see how the scripting engine customizes the shape of each letter contextually, depending on the surrounding characters. Segoe Script is an OpenType font with support for contextual swashes and alternates, and Uniscribe uses that data to provide customization far beyond anything GDI or GDI+ can do. (You can verify this by switching between the Text and Typography tools, and seeing how the text changes when using the default WAPI renderer.) This is just the tip of the iceberg, and there are a ton of things still to fix (like line breaks), but this is a significant landmark. (Also, I'm pretty sure this is the only open-source implementation of Uniscribe features in VB, so yay for PD!)
… data Better yet, it looks like Uniscribe is actually returning valid data! Now it's time to see if we can use that data to actually render text...
I'll take victories wherever I can, especially when working with cryptic APIs! The four most important Uniscribe steps now return success, and they process without crashing - which is something new, as they've crashed like crazy over the past few days. There were some weird reference counter issues when passing arrays ByRef, but switching to naked pointers seems to have resolved it (for now??). Also, MSDN is really cryptic about various Uniscribe structures, so some trial and error has been required to figure out how to dimension various buffers. Right now, PD's Uniscribe interface only processes the first returned run, so intermixing LTR and RTL languages won't work. (Also, certain punctuation characters will cause runs to break.) I'll obviously expand this once I have single runs working, as some messy management is required to properly handle multiple runs intelligently. But for now, my next challenge is figuring out ScriptBreak (https://msdn.microsoft.com/en-us/library/windows/desktop/dd319118%28v=vs.85%29.aspx) so I can predict character and word boundaries. This is the last major piece of information I need for PD's glyph renderer, as it's necessary for calculating line break positions. Once that data is ready, the last step will be taking the relevant information returned by the HUGE variety of Uniscribe returns (seriously, there are at least a dozen different buffers with various pieces of data), condensing them into some kind of VB-friendly struct, and then returning that to the glyph renderer. Ideally, the current glyph renderer code shouldn't need to change too much; the main change will be retrieving glyphs by glyph ID (instead of character ID), using the improved Uniscribe positioning data for kerning and such, and implementing smart line-breaks using the the output of ScriptBreak. (whew)
This is going to be a hideous project, but I've gotta start somewhere. This commit is mostly an API copy+paste job to list basic Uniscribe types, enums, and functions, modified to be VB-compatible, and allowing PD to still compile. As noted in the module header: "Many thanks to Michael Kaplan for his endless work in demystifying Windows text handling. Of particular value to this module is this link from his personal blog: http://www.siao2.com/2006/06/12/628714.aspx"
The goal here is to have three tools - Text, Typography, and WordArt - which the user can switch between non-destructively. Text layers now store all data for all text tools, so if a user tries to make something work with e.g. WordArt, but doesn't like the result, they can switch right over to Basic Text and pick up with the previous settings they used (or vice-versa). PD accomplishes this by storing all data for all layer types in a pdTextRenderer object, then simply requesting a different font rendering engine when the user switches tools. Settings shared by each text renderer (font face, size, etc) are re-used, while settings unique to each tool are only accessed as-needed. With this commit, PDI files are also compatible with the new Typography tool.
...if left margin presents overhang. The tough thing with right margins is that we can't easily predict where GDI or GDI+ will choose to wordwrap international text. As such, all we can do is check for fonts with known overhang, and estimate an amount of right-side padding that will minimize clipping. When we draw all our own glyphs, this problem won't exist, as we'll know wordwrap positions in advance and can pad accordingly.
Fonts are so unpredictable, and testing on XP shows some differing metrics. This new approach should guarantee plenty of whitespace, regardless of font size or contents.
This is useless under Windows 7, but it may prove helpful on XP where we don't have access to the new Uniscribe OpenType functions. As such, I'm leaving it even though the new font combo box doesn't actually make use of it.
PD now provides a font-specific dropdown control, with support for font previews. I've implemented a custom caching system to avoid the need for excessive font creation/destruction, and I think the final result is pretty great! I looked at a lot of different software when designing how fonts would actually be "previewed". I'm not a big fan of Microsoft's method of rendering the font name itself in its own font. Symbol fonts like Wingdings make this method unpredictable and ugly, not to mention that it's very difficult to compare fonts as each one is rendered with different letters. PhotoShop's method of columns is a little better, but I find it aesthetically unappealing, and the tiny font sizes and sample text is odd. PD's method is to render the font name (in the program UI font) on the left, and a live preview on the right. Preview text is hard-coded, but I really want to find a way to determine a font's preferred character set, and use different preview text for different languages (e.g. preview a Chinese font with Chinese characters, instead of English ones), but I haven't figured out how to do it just yet. In the meantime, PD's solution is fast, lightweight, and way less prone to flicker than its competitors - so a win all around, I think. Some clipping is possible if the preview font has enormous characters. I'm debating whether to let the left margin of the preview text float, if the font name is short enough to allow it. This has some performance implications as we have to manually measure each string, but it may look a little prettier... I'll do some testing to see if it's viable.
The font-specific variant of PD's combo box UC is going to be as lightweight as possible. There's no need for AddItem or RemoveItem functions, as it simply queries the program's font cache for a list of fonts, and displays them accordingly. With this commit, list management, sizing, and display positioning has all been implemented. Still to-do is the actual "render a font sample using each font" bit.
PD now exerts much greater control over Got/LostFocus events, to ensure that non-destructive changes are correctly tracked and processed.
These effects can now be properly Undone/Redone on a per-effect basis, while correctly triggering save state changes. Even better, this update allowed me to finally kill off the old "image checkpoint" method for detecting changes to an image. This will greatly improve the overall reliability of PD's Undo/Redo stack, while providing way more useful feedback to the user on what will actually be undone/redone at any given point.
…pipeline Only Opacity and Blend Mode have been moved, and Macro integration hasn't been touched yet. Next up is changes raised from the layer toolbox (which will be unpleasant, since they don't have normal per-control Got/LostFocus events).
Macro recording is such an ugly hack at present. The whole engine desperately needs an overhaul, but it's just not a major priority compared to other tasks, do I'll have to settle for cobbling together temporary fixes, while maintaining copious notes on what to improve in the future. With that in mind, this is mostly a "proof of concept" implementation for the new non-destructive processor branch. Text layer creation (and non-destructive modifications!) can successfully be recorded and played back on another image. Usefulness will vary, as text positioning is absolute, so playing back the macro on an image of a different size will likely yield poor results. Anyway, I know this is less likely to be helpful than non-destructive layer changes (opacity and blend mode, in particular), but now that I know the non-destructive processor pipeline works, I can start reworking those changes to work with the pipeline.
pdInputMouse may raise mouse events before a control receives focus notifications. I have now manually synchronized those values, so the user can safely switch from an API window (like a text box) to a non-API window (like a pdButtonToolbox) and still have focus events generated in the proper order. This fixes my last-known bug with the new vector-safe Undo/Redo support. Additional bug reports welcome if you notice any strange behavior with Undoing text layer stuff.
…lics) This was a pretty severe issue with e.g. Times New Roman, Italicized, huge font size. Both GDI and GDI+ were affected, because they always assume an ABC width where A = 0. Times New Roman, Italics, size 500 font has an A width of -24, or nearly 5% of the glyph width! PD now manually calculates the left offset required to solve this issue, as it varies depending on the initial glyph of the layer. Both GDI+ and GDI rendering paths have been fixed.
Time to start solving some more problematic text layout issues for this tool, e.g. http://stackoverflow.com/questions/23737591/qpainter-drawtext-italic-writes-outside-of-the-rectangle. While this commit doesn't actually solve it yet, I'm starting to lay the framework for retrieving and processing more detailed text metrics.