1818-- luacheck: ignore 11./global_dialog
1919
2020local utils = require (" library.utils" )
21+ local client = require (" library.client" )
2122local library = require (" library.general_library" )
2223local mixin = require (" library.mixin" )
2324local smufl_glyphs = require (" library.smufl_glyphs" )
@@ -31,19 +32,14 @@ context = {
3132 current_directory = finenv .RunningLuaFolderPath ()
3233}
3334
34- local function format_codepoint (cp )
35- local codepoint_desc = " [" .. string.format (" U+%04X" , cp ) .. " ]"
36- local glyph_name = smufl_glyphs .get_glyph_info (cp )
37- if glyph_name then
38- return " '" .. glyph_name .. " ' " .. codepoint_desc
35+ local function format_mapping (mapping )
36+ local codepoint_desc = " [" .. utils .format_codepoint (mapping .codepoint ) .. " ]"
37+ if mapping .glyph then
38+ return " '" .. mapping .glyph .. " ' " .. codepoint_desc
3939 end
4040 return codepoint_desc
4141end
4242
43- local function parse_codepoint (codepoint_string )
44- return tonumber (codepoint_string :match (" U%+(%x+)" ), 16 )
45- end
46-
4743local function enable_disable (dialog )
4844 local delable = # (dialog :GetControl (" legacy_box" ):GetText ()) > 0
4945 local addable = delable and # (dialog :GetControl (" smufl_box" ):GetText ()) > 0
9086
9187local function on_popup (popup )
9288 local legacy_codepoint = context .popup_keys [popup :GetSelectedItem () + 1 ] or 0
93- local smufl_codepoint = legacy_codepoint > 0 and context .current_mapping [legacy_codepoint ] or 0
89+ local current_mapping = legacy_codepoint > 0 and context .current_mapping [legacy_codepoint ]
90+ local smufl_codepoint = current_mapping and current_mapping .codepoint or 0
9491 local dialog = popup :GetParent ()
9592 set_codepoint (dialog :GetControl (" legacy_box" ), legacy_codepoint )
9693 set_codepoint (dialog :GetControl (" smufl_box" ), smufl_codepoint )
@@ -105,7 +102,7 @@ local function update_popup(popup, current_codepoint)
105102 popup :Clear ()
106103 local current_index
107104 for k , v in ipairs (context .popup_keys ) do
108- popup :AddString (tostring (v ) .. " maps to " .. format_codepoint (context .current_mapping [v ]))
105+ popup :AddString (tostring (v ) .. " maps to " .. format_mapping (context .current_mapping [v ]))
109106 if v == current_codepoint then
110107 current_index = k - 1
111108 end
@@ -138,7 +135,7 @@ local function on_select_file(control)
138135 local open_dialog = mixin .FCMFileOpenDialog (dialog :CreateChildUI ())
139136 :SetWindowTitle (finale .FCString (" Select existing JSON file" ))
140137 :SetInitFolder (finale .FCString (context .current_directory ))
141- :AddFilter (finale .FCString (" *.json" ), finale .FCString (" Font Mapping" ))
138+ :AddFilter (finale .FCString (" *.json" ), finale .FCString (" Legacy Font Mapping" ))
142139 if not open_dialog :Execute () then
143140 return
144141 end
@@ -154,16 +151,27 @@ local function on_select_file(control)
154151 dialog :CreateChildUI ():AlertError (" Font " .. name .. " is a SMuFL font." , " SMuFL Font" )
155152 return
156153 end
157- local file = io.open (selected_file .LuaString )
154+ local file = io.open (client . encode_with_client_codepage ( selected_file .LuaString ) )
158155 if file then
159156 local json_contents = file :read (" *a" )
160157 file :close ()
161158 local json = cjson .decode (json_contents )
162159 context .current_directory = path
163160 change_font (dialog , font_info )
164161 context .current_mapping = {}
165- for _ , v in pairs (json ) do
166- context .current_mapping [tonumber (v .legacyCodepoint )] = parse_codepoint (v .codepoint )
162+ for glyph , v in pairs (json ) do
163+ local t = {}
164+ t .glyph = glyph
165+ t .codepoint = utils .parse_codepoint (v .codepoint )
166+ t .nameIsMakeMusic = v .nameIsMakeMusic
167+ if t .codepoint == 0xFFFD then
168+ local smufl_box = dialog :GetControl (" smufl_box" )
169+ local _ , info = smufl_glyphs .get_glyph_info (glyph , smufl_box :CreateFontInfo ())
170+ if info then
171+ t .codepoint = info .codepoint
172+ end
173+ end
174+ context .current_mapping [tonumber (v .legacyCodepoint )] = t
167175 end
168176 update_popup (dialog :GetControl (" mappings" ))
169177 end
@@ -206,13 +214,21 @@ local function on_add_mapping(control)
206214 local legacy_point = get_codepoint (dialog :GetControl (" legacy_box" ))
207215 if legacy_point == 0 then return end
208216 local smufl_point = get_codepoint (dialog :GetControl (" smufl_box" ))
209- if (smufl_point == 0 ) then return end
210- if context .current_mapping [legacy_point ] then
211- if finale .YESRETURN ~= dialog :CreateChildUI ():AlertYesNo (" Symbol " .. legacy_point .. " is already mapped to " .. format_codepoint (smufl_point ) .. " . Continue?" , " Already Mapped" ) then
217+ if smufl_point == 0 then return end
218+ local current_mapping = context .current_mapping [legacy_point ]
219+ if current_mapping then
220+ if finale .YESRETURN ~= dialog :CreateChildUI ():AlertYesNo (" Symbol " .. legacy_point .. " is already mapped to " .. format_mapping (current_mapping ) .. " . Continue?" , " Already Mapped" ) then
212221 return
213222 end
214223 end
215- context .current_mapping [legacy_point ] = smufl_point
224+ current_mapping = {codepoint = smufl_point }
225+ local glyph , info = smufl_glyphs .get_glyph_info (smufl_point , dialog :GetControl (" smufl_box" ):CreateFontInfo ())
226+ if info then
227+ current_mapping .glyph = glyph
228+ else
229+ current_mapping .glyph = utils .format_codepoint (smufl_point )
230+ end
231+ context .current_mapping [legacy_point ] = current_mapping
216232 update_popup (popup , legacy_point )
217233end
218234
@@ -228,6 +244,84 @@ local function on_delete_mapping(control)
228244 end
229245end
230246
247+ -- use hand-crafted json encoder to control order of elements
248+ local function emit_json (mapping , reverse_lookup )
249+ local function quote (str )
250+ return ' "' .. tostring (str ):gsub (' \\ ' , ' \\\\ ' ):gsub (' "' , ' \\ "' ) .. ' "'
251+ end
252+
253+ local function emit_entry (entry , legacy_codepoint )
254+ local parts = {}
255+ if type (entry .nameIsMakeMusic ) == " boolean" then
256+ table.insert (parts , ' \t\t "nameIsMakeMusic": ' .. tostring (entry .nameIsMakeMusic ))
257+ end
258+ if entry .codepoint then
259+ table.insert (parts , ' \t\t "codepoint": ' .. quote (utils .format_codepoint (entry .codepoint )))
260+ end
261+ table.insert (parts , ' \t\t "legacyCodepoint": ' .. quote (tostring (legacy_codepoint )))
262+ if entry .description then
263+ table.insert (parts , ' \t\t "description": ' .. quote (entry .description ))
264+ else
265+ table.insert (parts , ' \t\t "description": ' .. quote (" " ))
266+ end
267+ return " {\n " .. table.concat (parts , " ,\n " ) .. " \n\t }"
268+ end
269+
270+ local lines = { " {" }
271+ local first = true
272+ for key , entry in pairsbykeys (mapping ) do
273+ if reverse_lookup [entry .glyph ] then
274+ if not first then
275+ lines [# lines ] = lines [# lines ] .. " ," -- ← append comma to previous line
276+ end
277+ table.insert (lines , " \t " .. quote (entry .glyph ) .. " : " .. emit_entry (entry , key ))
278+ first = false
279+ end
280+ end
281+ table.insert (lines , " }" )
282+ return table.concat (lines , " \n " )
283+ end
284+
285+ local function on_save (control )
286+ local dialog = control :GetParent ()
287+ if type (context .current_mapping ) ~= " table" or not next (context .current_mapping ) then
288+ dialog :CreateChildUI ():AlertInfo (" Nothing has been mapped." , " No Mapping" )
289+ return
290+ end
291+ local save_dialog = finale .FCFileSaveAsDialog (dialog :CreateChildUI ())
292+ save_dialog :SetWindowTitle (finale .FCString (" Save mapping as" ))
293+ save_dialog :AddFilter (finale .FCString (" *.json" ), finale .FCString (" Legacy Font Mapping" ))
294+ save_dialog :SetInitFolder (finale .FCString (context .current_directory ))
295+ save_dialog :SetFileName (finale .FCString (context .current_font .Name .. " .json" ))
296+ save_dialog :AssureFileExtension (" json" )
297+ if not save_dialog :Execute () then
298+ return
299+ end
300+ local path_fstr = finale .FCString ()
301+ save_dialog :GetFileName (path_fstr )
302+ local reverse_lookup = {}
303+ for legacy_codepoint , info in pairs (context .current_mapping ) do
304+ if type (info .glyph ) ~= " string" then
305+ dialog :CreateChildUI ():AlertError (" No glyph name found for legacy codepoint " .. legacy_codepoint .. " ." ,
306+ " No Glyph Name" )
307+ return
308+ elseif reverse_lookup [info .glyph ] then
309+ if finale .YESRETURN ~= dialog :CreateChildUI ():AlertYesNo (" Glyph name " .. info .glyph .. " is mapped more than once. Continue?" , " Duplicate Glyph Name" ) then
310+ return
311+ end
312+ end
313+ reverse_lookup [info .glyph ] = true
314+ end
315+ local result = emit_json (context .current_mapping , reverse_lookup )
316+ local file = io.open (client .encode_with_client_codepage (path_fstr .LuaString ), " w" )
317+ if not file then
318+ dialog :CreateChildUI ():AlertError (" Unable to write to file " .. path_fstr .LuaString .. " ." , " File Error" )
319+ return
320+ end
321+ file :write (result )
322+ file :close ()
323+ end
324+
231325function font_map_legacy ()
232326 local dialog = mixin .FCXCustomLuaWindow ()
233327 :SetTitle (" Map Legacy Fonts to SMuFL" )
@@ -283,14 +377,14 @@ function font_map_legacy()
283377 on_symbol_select (control :GetParent ():GetControl (" legacy_box" ))
284378 end )
285379 dialog :CreateButton (0 , current_y + editor_height / 2 - button_height , " add_mapping" )
286- :SetText (" Add Mapping" )
287- :SetWidth (120 )
380+ :SetText (" Add/Update Mapping" )
381+ :SetWidth (140 )
288382 :SetEnable (false )
289383 :AssureNoHorizontalOverlap (dialog :GetControl (" legacy_box" ), editor_width / 2 )
290384 :AddHandleCommand (on_add_mapping )
291385 dialog :CreateButton (0 , current_y + editor_height / 2 + y_increment , " delete_mapping" )
292386 :SetText (" Delete Mapping" )
293- :SetWidth (120 )
387+ :SetWidth (140 )
294388 :SetEnable (false )
295389 :AssureNoHorizontalOverlap (dialog :GetControl (" legacy_box" ), editor_width / 2 )
296390 :AddHandleCommand (on_delete_mapping )
@@ -311,8 +405,16 @@ function font_map_legacy()
311405 dialog :CreatePopup (0 , current_y , " mappings" )
312406 :StretchToAlignWithRight ()
313407 :AddHandleCommand (on_popup )
314- -- close button
315- dialog :CreateCancelButton (" cancel" ):SetText (" Close" )
408+ current_y = current_y + button_height + y_increment
409+ -- save and close buttons
410+ dialog :CreateButton (0 , current_y , " save" )
411+ :SetText (" Save..." )
412+ :DoAutoResizeWidth (0 )
413+ :AddHandleCommand (on_save )
414+ dialog :CreateCloseButton (0 , current_y , " close" )
415+ :SetText (" Close" )
416+ :DoAutoResizeWidth (0 )
417+ :HorizontallyAlignRightWithFurthest ()
316418 -- registrations
317419 dialog :RegisterInitWindow (function (self )
318420 on_smufl_popup (self :GetControl (" smufl_list" ))
0 commit comments