diff --git a/current_build/Glyphr_Studio.min.js b/current_build/Glyphr_Studio.min.js index c985f1ca..e86dcf4b 100644 --- a/current_build/Glyphr_Studio.min.js +++ b/current_build/Glyphr_Studio.min.js @@ -1,4 +1,4 @@ -function insertGlobalDOMElements(){var a='
',b='',c='';document.body.innerHTML='
',document.body.innerHTML+=a,document.body.innerHTML+=b,document.body.innerHTML+=c,window.onbeforeunload=function(){return _GP.projectsettings.stoppagenavigation&&!_UI.debug?"\n\nOh Noes!\nUnless you specifically saved your Glyphr Project, all your progress will be lost.\n\n":void 0}}function makePanelSuperTitle(){var a="";if(!_UI.popout){var b,c=getSelectedChar();a+="

"+_UI.navhere.toUpperCase(),c?(b=c.charname||c.charhtml||c.shape.name||"[no shape outline yet]",c.charname&&(b=b.replace(/latin /i,"")),a+="❯❯",a+=b):"kerning"===_UI.navhere&&(a+="❯❯",a+=getSelectedKern().getName()),a+="

"}return a}function debug(a,b){(_UI.debug||b)&&("string"==typeof a&&(a=a.replace(/</gi,"<"),a=a.replace(/>/gi,">")),console.log(a))}function json(a,b){if(b)return JSON.stringify(a);var c=JSON.stringify(a,void 0," ");return c?c.replace(/\n/g,"\r\n"):""}function closeDialog(){document.getElementById("dialog_box").style.display="none",document.getElementById("dialog_bg").style.display="none",document.getElementById("dialogRightContent").innerHTML="Error: unspecified dialog box content.",document.body.focus()}function openDialog(a){document.body.focus();var b=document.getElementById("dialogRightContent");b.innerHTML=a,b.style.height>800?b.style.height=800:b.style.width="auto",document.getElementById("dialog_box").style.display="block",document.getElementById("dialog_bg").style.display="block"}function makeErrorMessageBox(){var a='';return a}function showErrorMessageBox(a){var b=document.getElementById("errormessagecontent"),c=document.getElementById("errormessagebox");b.innerHTML=a,c.style.display="block",console.warn(a)}function closeErrorMessageBox(){document.getElementById("errormessagecontent").innerHTML="",document.getElementById("errormessagebox").style.display="none"}function setProjectAsSaved(){_UI.projectsaved=!0,_UI.popout?(document.title="Glyphr Studio - Tools",_UI.popout.document.title="Glyphr Studio - Canvas"):document.title="Glyphr Studio",updateSaveIcon()}function setProjectAsUnsaved(){_UI.projectsaved=!1,_UI.popout?(document.title=" ❖ Glyphr Studio - Tools",_UI.popout.document.title=" ❖ Glyphr Studio - Canvas"):document.title=" ❖ Glyphr Studio",updateSaveIcon()}function saveTextFile(a,b){var c=new Blob([b],{type:"text/plain;charset=utf-8",endings:"native"});try{return void window.navigator.msSaveBlob(c,a)}catch(d){var e=document.createElement("a");window.URL=window.URL||window.webkitURL,e.href=window.URL.createObjectURL(c),e.download=a;var f=document.createEvent("MouseEvents");return f.initEvent("click",!0,!1),void e.dispatchEvent(f)}console.error("File could not be saved: "+a)}function clone(a){var b=a instanceof Array?[]:{};for(var c in a)b[c]=a[c]&&"object"==typeof a[c]?clone(a[c]):a[c];return b}function toggle(val){"string"==typeof val?eval(val+" = !"+val):val=!val}function round(a,b){return a?(b=isval(b)?b:0,Number(Math.round(a+"e"+b)+"e-"+b)):0}function strSan(a){return a.replace(/[<>'"\\]/g,"")}function trim(a){try{return a=a.replace(/^\s+|\s+$/g,""),a.replace(/(\r\n|\n|\r|\t)/gm,"")}catch(b){return""}}function isval(a){return 0===a?!0:a===!1?!0:"{}"===JSON.stringify(a)?!1:!!a}function getFirstID(a){for(var b in a)if(a.hasOwnProperty(b))return b;return!1}function generateNewID(a,b){var c=1;b=b||"id";for(var d=""+b+c;a.hasOwnProperty(d);)d=""+b+ ++c;return d}function getLength(a){var b=0;for(var c in a)a.hasOwnProperty(c)&&b++;return b}function genEmailContent(){var a="Have a feature idea or ran into an issue%3F We%27d be happy to help!";return a+="%0A%0A%0A%0A___________________________________________%0A",a+="version %09Glyphr Studio "+_UI.thisGlyphrStudioVersion+"%0A",a+="app name %09"+navigator.appName+"%0A",a+="language %09"+navigator.language+"%0A",a+="platform %09"+navigator.platform+"%0A",a+="user agent %09"+encodeURIComponent(navigator.userAgent)+"%0A"}function shiftColor(a,b,c){b=Math.max(0,Math.min(b,1));var d={};return"#"===a.charAt(0)?(a=a.substring(1,7),d.r=parseInt(a.substring(0,2),16),d.g=parseInt(a.substring(2,4),16),d.b=parseInt(a.substring(4,6),16)):"rgb("===a.substring(0,4)?(a=a.split("(")[1].split(")")[0].split(","),d.r=a[0],d.g=a[1],d.b=a[2]):(d.r=0,d.g=0,d.b=0),d.r=Math.max(0,Math.min(d.r,255)),d.g=Math.max(0,Math.min(d.g,255)),d.b=Math.max(0,Math.min(d.b,255)),c?(d.r=round((255-1*d.r)*b+1*d.r),d.g=round((255-1*d.g)*b+1*d.g),d.b=round((255-1*d.b)*b+1*d.b)):(d.r=round(1*d.r-d.r*b),d.g=round(1*d.g-d.g*b),d.b=round(1*d.b-d.b*b)),"rgb("+d.r+","+d.g+","+d.b+")"}function makeIcon(a){var b=a.size||50,c=a.color||"rgb(76,81,86)",d=a.hovercolor||"rgb(0,170,225)";a.hovercolor===!1&&(d=c);var e=' ',e+=" ",e+='',e+=_UI.icons[a.name],e+="",e+=""}function makeGlyphrStudioLogo(a){a=a||{};var b=a.fill||_UI.colors.accent_65,c=a.width||184,d=c*(55/184),e='';return e}function makeFloatLogo(){return'"}function makeToolButton(a){var b=_UI.colors.accent_65,c="transparent";a.selected&&(b="black",c="white");var d='',d+=e.fill,d+=""),d+='',d+=e.outline,d+="",d+=""}function makePointButton(a,b){var c=_UI.colors.gray_40,d="transparent";b&&(c=_UI.colors.accent_65,d=_UI.colors.offwhite);var e="";switch(e+='"}function lockUI(varname){var idname=varname.split("()");idname=idname[idname.length-1];var currbool=eval(varname),restcolor=_UI.colors.gray_90,selcolor=_UI.colors.accent_65,re='";return re+='',re+=""}function checkUI(varname,doredraw,invert){var idname=varname.split(".");idname=idname[idname.length-1];var currbool=eval(varname);invert&&(currbool=!currbool);var re='';return b}function editPage_Content(){return'
[ERROR: Uninitialized content]
'+makeFloatLogo()}function redraw(){Date.now();if(!_UI.redrawing){switch(_UI.redrawing=!1,_UI.chareditctx.clearRect(0,0,_UI.chareditcanvassize,_UI.chareditcanvassize),_UI.navhere){case"character edit":redraw_CharacterEdit();break;case"linked shapes":redraw_LinkedShapes();break;case"ligatures":redraw_CharacterEdit();break;case"kerning":redraw_Kerning();break;case"test drive":redraw_TestDrive()}update_ToolsArea(),update_NavPanels(),_UI.redrawing=!1}}function update_ToolsArea(){if(onCanvasEditPage()){if(!isWorkItemSelected())return void(getEditDocument().getElementById("toolsarea").innerHTML="");var a="",b=!0,c="character edit"===_UI.navhere,d="linked shapes"===_UI.navhere,e="ligatures"===_UI.navhere,f="kerning"===_UI.navhere,g=ss("Charedit: UpdateTools");d&&(_GP.linkedshapes[_UI.selectedshape]||(g=!1)),"pathedit"===_UI.selectedtool?a="buttonsel tool":g.link&&"linked shapes"!==_UI.navhere?(a="buttondis tool",b=!1):a="tool";var h=_UI.selectedtool,i="";onCanvasEditPage()&&(i+=_UI.popout?"":"",i+="
 
");var j="";j+="",j+="",j+="
",j+="",j+="",j+="
",j+="",j+="
 
";var k="";k+="",k+="",k+="",k+="
";var l="",m="";m+="",m+="","newpath"===_UI.selectedtool&&(m+="
 
",m+="");var n="",o="";o+=i,o+=j,(c||e)&&(o+=k);var p=getSelectedChar();d&&p&&!p.shape&&(o+=k),o+=l,(c||d||e)&&(o+=m),f&&(o+=n),_GP.projectsettings.showkeyboardtipsicon&&(o+='");try{getEditDocument().getElementById("toolsarea").innerHTML=o}catch(q){console.error(" ...failure! update_ToolsArea div could not be found.")}}}function clickTool(a){_UI.selectedtool=a;var b=ss("clicktool");_UI.eventhandlers.eh_addpath.firstpoint=!0,"newrect"===a||"newoval"===a?_UI.selectedshape=-1:"newpath"===a?_UI.selectedshape=-1:"pathedit"===a?b&&b.path&&b.path.selectPathPoint(0):"shaperesize"===a&&b&&b.path&&b.path.calcMaxes(),redraw("clicktool")}function toggleKeyboardTips(){if("block"===document.getElementById("dialog_box").style.display)closeDialog();else{var a="

Keyboard and Mouse Shortcuts

";a+=makeKeyboardShortcutsTable(),a+="
"+checkUI("_GP.projectsettings.showkeyboardtipsicon")+"
",openDialog(a)}}function makeKeyboardShortcutsTable(){var a="
?toggles this shortcuts dialog
ctrlssave a Glyphr Project file

 

shapes and paths:

ctrlccopy selected shape
ctrlvpaste shape
ctrlzundo
deletedelete selected shape

nudges the selected shape
or point "+_GP.projectsettings.spinnervaluechange+" em units

 

edit canvas:

spacebarpan the edit canvas
ctrlmouse wheelzoom the edit canvas
ctrl+zoom in the edit canvas
ctrlzoom out the edit canvas
ctrl0reset edit canvas zoom
";return a}function setView(a){var b="kerning"===_UI.navhere?getSelectedKernID():getSelectedCharID(),c=_UI.views;isval(c[b])||(c[b]=getView("setView")),isval(a.dx)&&(c[b].dx=a.dx),isval(a.dy)&&(c[b].dy=a.dy),isval(a.dz)&&(c[b].dz=a.dz)}function getView(){var a="kerning"===_UI.navhere,b=a?getSelectedKernID():getSelectedCharID(),c=_UI.views;return clone(isval(c[b])?c[b]:a?_UI.defaultkernview:_UI.defaultview)}function viewZoom(a){var b=getView(),c=_UI.eventhandlers.mousex-b.dx,d=_UI.eventhandlers.mousey-b.dy;setView({dz:round(getView("viewZoom").dz*=a,2),dx:_UI.eventhandlers.mousex-c*a,dy:_UI.eventhandlers.mousey-d*a}),redraw("viewZoom")}function resetThumbView(){var a=(_UI.thumbsize-2*_UI.thumbgutter)/_GP.projectsettings.upm;_UI.thumbview={dx:_UI.thumbgutter,dy:_UI.thumbgutter+_GP.projectsettings.ascent*a,dz:a}}function calculateDefaultView(){_GP.projectsettings.upm>2e3&&(_UI.defaultview={dx:200,dy:550,dz:.3},_UI.defaultkernview={dx:400,dy:400,dz:.2})}function isWorkItemSelected(){switch(_UI.navhere){case"character edit":return!0;case"linked shapes":return _UI.selectedlinkedshape;case"ligatures":return _UI.selectedchar;case"kerning":return _UI.selectedkern}return!1}function getCurrentWorkItemName(){switch(_UI.navhere){case"character edit":case"linked shapes":return getSelectedCharName();case"ligatures":return"ligature "+getSelectedCharName();case"kerning":return getSelectedKern().getName()}return"no working object"}function ss(a){a=a||"[probably a dynamically-generated page control]";var b=getSelectedCharShapes();return"linked shapes"===_UI.navhere?b[0]||!1:-1!==_UI.selectedshape?_UI.selectedshape>=0&&_UI.selectedshape=c.xmin;h-=d)f(h);for(var i=b.dy;i=c.ymin;j-=d)e(j)}}function drawGuides(){if(isWorkItemSelected()){var a=_GP.projectsettings,b="character edit"===_UI.navhere||"ligatures"===_UI.navhere,c="kerning"===_UI.navhere;if(_UI.showguides){var d;for(var e in a.guides)a.guides.hasOwnProperty(e)&&(d=a.guides[e],d.editable&&d.draw());if(a.guides.xheight.location=a.xheight,a.guides.capheight.location=a.capheight,a.guides.ascent.location=a.ascent,a.guides.baseline.location=0,a.guides.descent.location=a.ascent-a.upm,a.guides.leftside.location=0,a.guides.rightside.location=a.upm,_UI.showovershoots){var f=a.overshoot;a.guides.xheight.draw(-1*f),a.guides.ascent.draw(-1*f),a.guides.baseline.draw(f),a.guides.descent.draw(f)}var g=getSelectedChar();if(b){a.guides.leftside.draw(-1*getSelectedCharLeftSideBearing());var h=g.charwidth;_UI.eventhandlers.tempnewbasicshape&&(h=Math.max(h,_UI.eventhandlers.tempnewbasicshape.xmax)),a.guides.rightside.location=h,a.guides.rightside.draw(getSelectedCharRightSideBearing()),a.guides.rightside.draw()}else c&&(_UI.guides.leftgroup_xmax.location=getSelectedKern().value);if(a.guides.xheight.draw(),a.guides.capheight.draw(),a.guides.ascent.draw(),a.guides.descent.draw(),c||a.guides.leftside.draw(),a.guides.baseline.draw(),c&&_UI.guides.leftgroup_xmax.draw(),c&&_UI.guides.rightgroup_xmin.draw(),!c&&(a.guides.baseline.visible||a.guides.leftside.visible)){var i=getView("guides");_UI.chareditctx.fillStyle=a.guides.baseline.color,_UI.chareditctx.beginPath(),_UI.chareditctx.moveTo(i.dx,i.dy),_UI.chareditctx.lineTo(i.dx,i.dy+2*a.pointsize),_UI.chareditctx.lineTo(i.dx-2*a.pointsize,i.dy),_UI.chareditctx.closePath(),_UI.chareditctx.fill()}}}}function setupGhostCanvas(){_UI.ishereghostcanvas=getEditDocument().getElementById("ishereghostcanvas"),_UI.ishereghostcanvas.height=_UI.chareditcanvassize,_UI.ishereghostcanvas.width=_UI.chareditcanvassize,_UI.ishereghostctx=_UI.ishereghostcanvas.getContext("2d"),_UI.ishereghostctx.fillStyle="cyan",_UI.ishereghostctx.globalAlpha=.5,_UI.ishereghostcanvas.style.backgroundColor="transparent"}function setupEditCanvas(){_UI.chareditcanvas=getEditDocument().getElementById("chareditcanvas"),_UI.chareditcanvas.height=_UI.chareditcanvassize,_UI.chareditcanvas.width=_UI.chareditcanvassize,_UI.chareditctx=_UI.chareditcanvas.getContext("2d"),_UI.chareditcanvas.onselectstart=function(){return!1},_UI.chareditcanvas.onmouseout=mouseoutcec,_UI.chareditcanvas.onmouseover=mouseovercec}function resetCursor(){getEditDocument().body.style.cursor="default"}function initEventHandlers(){function a(a){switch(mouseovercec(),document.onselectstart=function(){return!1},(a.layerX||a.layerX)&&(_UI.eventhandlers.mousex=a.layerX,_UI.eventhandlers.mousey=a.layerY),(a.offsetX||a.offsetX)&&(_UI.eventhandlers.mousex=a.offsetX,_UI.eventhandlers.mousey=a.offsetY),resetCursor(),_UI.selectedtool){case"pathedit":b=_UI.eventhandlers.eh_shapesel;break;case"shaperesize":b=_UI.eventhandlers.eh_shaperesize;break;case"pan":getEditDocument().body.style.cursor="move",b=_UI.eventhandlers.eh_pantool;break;case"newpath":getEditDocument().body.style.cursor="crosshair",b=_UI.eventhandlers.eh_addpath;break;case"newrect":case"newoval":getEditDocument().body.style.cursor="crosshair",b=_UI.eventhandlers.eh_addrectoval;break;case"kern":getEditDocument().body.style.cursor="col-resize",b=_UI.eventhandlers.eh_kern}b[a.type](a)}var b=new Tool_PathEdit;_UI.eventhandlers.eh_addrectoval=new Tool_NewBasicShape,_UI.eventhandlers.eh_shapesel=new Tool_PathEdit,_UI.eventhandlers.eh_shaperesize=new Tool_ShapeResize,_UI.eventhandlers.eh_pantool=new Tool_Pan,_UI.eventhandlers.eh_addpath=new Tool_NewPath,_UI.eventhandlers.eh_addrectoval=new Tool_NewBasicShape,_UI.eventhandlers.eh_kern=new Tool_Kern,_UI.chareditcanvas.addEventListener("mousedown",a,!1),_UI.chareditcanvas.addEventListener("mousemove",a,!1),_UI.chareditcanvas.addEventListener("mouseup",a,!1),_UI.chareditcanvas.onmouseover=mouseovercec,_UI.chareditcanvas.onmouseout=mouseoutcec,_UI.chareditcanvas.addEventListener("wheel",mousewheel,!1),document.getElementById("navarea_panel")&&document.getElementById("navarea_panel").addEventListener("wheel",function(a){a.stopPropagation()},!1),getEditDocument().addEventListener("keypress",keypress,!1),getEditDocument().addEventListener("keydown",keypress,!1),getEditDocument().addEventListener("keyup",keyup,!1)}function Tool_NewPath(){this.dragging=!1,this.firstpoint=!0,this.currpt={},this.mousedown=function(){var a=new PathPoint({P:new Coord({x:cx_sx(_UI.eventhandlers.mousex),y:cy_sy(_UI.eventhandlers.mousey)}),H1:new Coord({x:cx_sx(_UI.eventhandlers.mousex-100),y:cy_sy(_UI.eventhandlers.mousey)}),H2:new Coord({x:cx_sx(_UI.eventhandlers.mousex+100),y:cy_sy(_UI.eventhandlers.mousey)}),type:"flat",selected:!0,useh1:!1,useh2:!1});if(this.firstpoint){var b=new Path({pathpoints:[a]}),c="linked shapes"===_UI.navhere?getLength(_GP.linkedshapes):getSelectedCharShapes().length,d=addShape(new Shape({name:"Path "+c,path:b}));d.path.selectPathPoint(0)}else{var e=ss("Event Handler New Path").path,f=e.isOverControlPoint(cx_sx(_UI.eventhandlers.mousex),cy_sy(_UI.eventhandlers.mousey));if("P"===f&&e.pathpoints.length>1){var g=e.pathpoints[0],h=_GP.projectsettings.pointsize/getView("Event Handler Tool_NewPath mousedown").dz;if(g.P.x+h>cx_sx(_UI.eventhandlers.mousex)&&g.P.x-hcy_sy(_UI.eventhandlers.mousey)&&g.P.y-h2*_GP.projectsettings.pointsize||Math.abs(this.currpt.P.y-cy_sy(_UI.eventhandlers.mousey))>2*_GP.projectsettings.pointsize)&&(this.currpt.useh1=!0,this.currpt.useh2=!0,this.currpt.H2.x=cx_sx(_UI.eventhandlers.mousex),this.currpt.H2.y=cy_sy(_UI.eventhandlers.mousey),this.currpt.makeSymmetric("H2")),_UI.eventhandlers.lastx=_UI.eventhandlers.mousex,_UI.eventhandlers.lasty=_UI.eventhandlers.mousey,_UI.eventhandlers.uqhaschanged=!0,redraw("Event Handler Tool_NewPath mousemove"))}}function Tool_NewBasicShape(){this.mousedown=function(){_UI.eventhandlers.tempnewbasicshape={xmax:cx_sx(_UI.eventhandlers.mousex),xmin:cx_sx(_UI.eventhandlers.mousex),ymax:cy_sy(_UI.eventhandlers.mousey),ymin:cy_sy(_UI.eventhandlers.mousey)};var a=new Shape({visible:!1,name:"..."});a.path.maxes=_UI.eventhandlers.tempnewbasicshape,a=addShape(a),_UI.eventhandlers.firstx=cx_sx(_UI.eventhandlers.mousex),_UI.eventhandlers.firsty=cy_sy(_UI.eventhandlers.mousey),redraw("Event Handler Tool_NewBasicShape mousedown")},this.mousemove=function(){_UI.eventhandlers.tempnewbasicshape&&(_UI.eventhandlers.tempnewbasicshape.xmax=Math.max(_UI.eventhandlers.firstx,cx_sx(_UI.eventhandlers.mousex)),_UI.eventhandlers.tempnewbasicshape.xmin=Math.min(_UI.eventhandlers.firstx,cx_sx(_UI.eventhandlers.mousex)),_UI.eventhandlers.tempnewbasicshape.ymax=Math.max(_UI.eventhandlers.firsty,cy_sy(_UI.eventhandlers.mousey)),_UI.eventhandlers.tempnewbasicshape.ymin=Math.min(_UI.eventhandlers.firsty,cy_sy(_UI.eventhandlers.mousey)),ss().path.maxes=_UI.eventhandlers.tempnewbasicshape,_UI.eventhandlers.uqhaschanged=!0,redraw("Event Handler Tool_NewBasicShape mousemove"))},this.mouseup=function(){var a=ss("NEWSHAPE MOUSEUP"),b=_UI.eventhandlers.tempnewbasicshape;if(Math.abs(b.xmax-b.xmin)>_GP.projectsettings.pointsize&&Math.abs(b.ymax-b.ymin)>_GP.projectsettings.pointsize){var c="linked shapes"===_UI.navhere?getLength(_GP.linkedshapes):getSelectedCharShapes().length;"newrect"===_UI.selectedtool?(a.name="Rectangle "+c,a.path=rectPathFromMaxes(b)):(a.name="Oval "+c,a.path=ovalPathFromMaxes(b)),a.visible=!0}else deleteShape();_UI.eventhandlers.firstx=-100,_UI.eventhandlers.firsty=-100,_UI.eventhandlers.tempnewbasicshape=!1,history_put("New Basic Shape tool"),_UI.eventhandlers.uqhaschanged=!1,clickTool("pathedit")}}function Tool_PathEdit(){this.moving=!1,this.controlpoint=!1,this.mousedown=function(){var a=ss("Path Edit - Mouse Down");this.controlpoint=a?a.path.isOverControlPoint(cx_sx(_UI.eventhandlers.mousex),cy_sy(_UI.eventhandlers.mousey)):!1,this.controlpoint?(this.moving=!0,_UI.eventhandlers.lastx=_UI.eventhandlers.mousex,_UI.eventhandlers.lasty=_UI.eventhandlers.mousey):clickSelectShape(_UI.eventhandlers.mousex,_UI.eventhandlers.mousey)?(_UI.eventhandlers.lastx=_UI.eventhandlers.mousex,_UI.eventhandlers.lasty=_UI.eventhandlers.mousey):(a&&a.path.calcMaxes(),clickEmptySpace()),redraw("Event Handler Tool_PathEdit mousedown") +function insertGlobalDOMElements(){var a='
',b='',c='';document.body.innerHTML='
',document.body.innerHTML+=a,document.body.innerHTML+=b,document.body.innerHTML+=c,window.onbeforeunload=function(){return _GP.projectsettings.stoppagenavigation&&!_UI.debug?"\n\nOh Noes!\nUnless you specifically saved your Glyphr Project, all your progress will be lost.\n\n":void 0}}function makePanelSuperTitle(){var a="";if(!_UI.popout){var b,c=getSelectedChar();a+="

"+_UI.navhere.toUpperCase(),c?(b=c.charname||c.charhtml||c.shape.name||"[no shape outline yet]",c.charname&&(b=b.replace(/latin /i,"")),a+="❯❯",a+=b):"kerning"===_UI.navhere&&(b=getSelectedKern(),a+=b?"❯❯"+b.getName():""),a+="

"}return a}function debug(a,b){(_UI.debug||b)&&("string"==typeof a&&(a=a.replace(/</gi,"<"),a=a.replace(/>/gi,">")),console.log(a))}function json(a,b){if(b)return JSON.stringify(a);var c=JSON.stringify(a,void 0," ");return c?c.replace(/\n/g,"\r\n"):""}function closeDialog(){document.getElementById("dialog_box").style.display="none",document.getElementById("dialog_bg").style.display="none",document.getElementById("dialogRightContent").innerHTML="Error: unspecified dialog box content.",document.body.focus()}function openDialog(a){document.body.focus();var b=document.getElementById("dialogRightContent");b.innerHTML=a,b.style.height>800?b.style.height=800:b.style.width="auto",document.getElementById("dialog_box").style.display="block",document.getElementById("dialog_bg").style.display="block"}function makeErrorMessageBox(){var a='';return a}function showErrorMessageBox(a){var b=document.getElementById("errormessagecontent"),c=document.getElementById("errormessagebox");b.innerHTML=a,c.style.display="block",console.warn(a)}function closeErrorMessageBox(){document.getElementById("errormessagecontent").innerHTML="",document.getElementById("errormessagebox").style.display="none"}function setProjectAsSaved(){_UI.projectsaved=!0,_UI.popout?(document.title="Glyphr Studio - Tools",_UI.popout.document.title="Glyphr Studio - Canvas"):document.title="Glyphr Studio",updateSaveIcon()}function setProjectAsUnsaved(){_UI.projectsaved=!1,_UI.popout?(document.title=" ❖ Glyphr Studio - Tools",_UI.popout.document.title=" ❖ Glyphr Studio - Canvas"):document.title=" ❖ Glyphr Studio",updateSaveIcon()}function saveTextFile(a,b){var c=new Blob([b],{type:"text/plain;charset=utf-8",endings:"native"});try{return void window.navigator.msSaveBlob(c,a)}catch(d){var e=document.createElement("a");window.URL=window.URL||window.webkitURL,e.href=window.URL.createObjectURL(c),e.download=a;var f=document.createEvent("MouseEvents");return f.initEvent("click",!0,!1),void e.dispatchEvent(f)}console.error("File could not be saved: "+a)}function clone(a){var b=a instanceof Array?[]:{};for(var c in a)b[c]=a[c]&&"object"==typeof a[c]?clone(a[c]):a[c];return b}function toggle(val){"string"==typeof val?eval(val+" = !"+val):val=!val}function round(a,b){return a?(b=isval(b)?b:0,Number(Math.round(a+"e"+b)+"e-"+b)):0}function strSan(a){return a.replace(/[<>'"\\]/g,"")}function trim(a){try{return a=a.replace(/^\s+|\s+$/g,""),a.replace(/(\r\n|\n|\r|\t)/gm,"")}catch(b){return""}}function isval(a){return 0===a?!0:a===!1?!0:"{}"===JSON.stringify(a)?!1:!!a}function getFirstID(a){for(var b in a)if(a.hasOwnProperty(b))return b;return!1}function generateNewID(a,b){var c=1;b=b||"id";for(var d=""+b+c;a.hasOwnProperty(d);)d=""+b+ ++c;return d}function getLength(a){var b=0;for(var c in a)a.hasOwnProperty(c)&&b++;return b}function genEmailContent(){var a="Have a feature idea or ran into an issue%3F We%27d be happy to help!";return a+="%0A%0A%0A%0A___________________________________________%0A",a+="version %09Glyphr Studio "+_UI.thisGlyphrStudioVersion+"%0A",a+="app name %09"+navigator.appName+"%0A",a+="language %09"+navigator.language+"%0A",a+="platform %09"+navigator.platform+"%0A",a+="user agent %09"+encodeURIComponent(navigator.userAgent)+"%0A"}function shiftColor(a,b,c){b=Math.max(0,Math.min(b,1));var d={};return"#"===a.charAt(0)?(a=a.substring(1,7),d.r=parseInt(a.substring(0,2),16),d.g=parseInt(a.substring(2,4),16),d.b=parseInt(a.substring(4,6),16)):"rgb("===a.substring(0,4)?(a=a.split("(")[1].split(")")[0].split(","),d.r=a[0],d.g=a[1],d.b=a[2]):(d.r=0,d.g=0,d.b=0),d.r=Math.max(0,Math.min(d.r,255)),d.g=Math.max(0,Math.min(d.g,255)),d.b=Math.max(0,Math.min(d.b,255)),c?(d.r=round((255-1*d.r)*b+1*d.r),d.g=round((255-1*d.g)*b+1*d.g),d.b=round((255-1*d.b)*b+1*d.b)):(d.r=round(1*d.r-d.r*b),d.g=round(1*d.g-d.g*b),d.b=round(1*d.b-d.b*b)),"rgb("+d.r+","+d.g+","+d.b+")"}function makeIcon(a){var b=a.size||50,c=a.color||"rgb(76,81,86)",d=a.hovercolor||"rgb(0,170,225)";a.hovercolor===!1&&(d=c);var e=' ',e+=" ",e+='',e+=_UI.icons[a.name],e+="",e+=""}function makeGlyphrStudioLogo(a){a=a||{};var b=a.fill||_UI.colors.accent_65,c=a.width||184,d=c*(55/184),e='';return e}function makeFloatLogo(){return'"}function makeToolButton(a){var b=_UI.colors.accent_65,c="transparent";a.selected&&(b="black",c="white");var d='',d+=e.fill,d+=""),d+='',d+=e.outline,d+="",d+=""}function makePointButton(a,b){var c=_UI.colors.gray_40,d="transparent";b&&(c=_UI.colors.accent_65,d=_UI.colors.offwhite);var e="";switch(e+='"}function lockUI(varname){var idname=varname.split("()");idname=idname[idname.length-1];var currbool=eval(varname),restcolor=_UI.colors.gray_90,selcolor=_UI.colors.accent_65,re='";return re+='',re+=""}function checkUI(varname,doredraw,invert){var idname=varname.split(".");idname=idname[idname.length-1];var currbool=eval(varname);invert&&(currbool=!currbool);var re='';return b}function editPage_Content(){return'
[ERROR: Uninitialized content]
'+makeFloatLogo()}function redraw(){Date.now();if(!_UI.redrawing){switch(_UI.redrawing=!1,_UI.chareditctx.clearRect(0,0,_UI.chareditcanvassize,_UI.chareditcanvassize),_UI.navhere){case"character edit":redraw_CharacterEdit();break;case"linked shapes":redraw_LinkedShapes();break;case"ligatures":redraw_CharacterEdit();break;case"kerning":redraw_Kerning();break;case"test drive":redraw_TestDrive()}update_ToolsArea(),update_NavPanels(),_UI.redrawing=!1}}function update_ToolsArea(){if(onCanvasEditPage()){if(!isWorkItemSelected())return void(getEditDocument().getElementById("toolsarea").innerHTML="");var a="",b=!0,c="character edit"===_UI.navhere,d="linked shapes"===_UI.navhere,e="ligatures"===_UI.navhere,f="kerning"===_UI.navhere,g=ss("Charedit: UpdateTools");d&&(_GP.linkedshapes[_UI.selectedshape]||(g=!1)),"pathedit"===_UI.selectedtool?a="buttonsel tool":g.link&&"linked shapes"!==_UI.navhere?(a="buttondis tool",b=!1):a="tool";var h=_UI.selectedtool,i="";onCanvasEditPage()&&(i+=_UI.popout?"":"",i+="
 
");var j="";j+="",j+="",j+="
",j+="",j+="",j+="
",j+="",j+="
 
";var k="";k+="",k+="",k+="",k+="
";var l="",m="";m+="",m+="","newpath"===_UI.selectedtool&&(m+="
 
",m+="");var n="",o="";o+=i,o+=j,(c||e)&&(o+=k);var p=getSelectedChar();d&&p&&!p.shape&&(o+=k),o+=l,(c||d||e)&&(o+=m),f&&(o+=n),_GP.projectsettings.showkeyboardtipsicon&&(o+='");try{getEditDocument().getElementById("toolsarea").innerHTML=o}catch(q){console.error(" ...failure! update_ToolsArea div could not be found.")}}}function clickTool(a){_UI.selectedtool=a;var b=ss("clicktool");_UI.eventhandlers.eh_addpath.firstpoint=!0,"newrect"===a||"newoval"===a?_UI.selectedshape=-1:"newpath"===a?_UI.selectedshape=-1:"pathedit"===a?b&&b.path&&b.path.selectPathPoint(0):"shaperesize"===a&&b&&b.path&&b.path.calcMaxes(),redraw("clicktool")}function toggleKeyboardTips(){if("block"===document.getElementById("dialog_box").style.display)closeDialog();else{var a="

Keyboard and Mouse Shortcuts

";a+=makeKeyboardShortcutsTable(),a+="
"+checkUI("_GP.projectsettings.showkeyboardtipsicon")+"
",openDialog(a)}}function makeKeyboardShortcutsTable(){var a="
?toggles this shortcuts dialog
ctrlssave a Glyphr Project file

 

shapes and paths:

ctrlccopy selected shape
ctrlvpaste shape
ctrlzundo
deletedelete selected shape

nudges the selected shape
or point "+_GP.projectsettings.spinnervaluechange+" em units

 

edit canvas:

spacebarpan the edit canvas
ctrlmouse wheelzoom the edit canvas
ctrl+zoom in the edit canvas
ctrlzoom out the edit canvas
ctrl0reset edit canvas zoom
";return a}function setView(a){var b="kerning"===_UI.navhere?getSelectedKernID():getSelectedCharID(),c=_UI.views;isval(c[b])||(c[b]=getView("setView")),isval(a.dx)&&(c[b].dx=a.dx),isval(a.dy)&&(c[b].dy=a.dy),isval(a.dz)&&(c[b].dz=a.dz)}function getView(){var a="kerning"===_UI.navhere,b=a?getSelectedKernID():getSelectedCharID(),c=_UI.views;return clone(isval(c[b])?c[b]:a?_UI.defaultkernview:_UI.defaultview)}function viewZoom(a){var b=getView(),c=_UI.eventhandlers.mousex-b.dx,d=_UI.eventhandlers.mousey-b.dy;setView({dz:round(getView("viewZoom").dz*=a,2),dx:_UI.eventhandlers.mousex-c*a,dy:_UI.eventhandlers.mousey-d*a}),redraw("viewZoom")}function resetThumbView(){var a=(_UI.thumbsize-2*_UI.thumbgutter)/_GP.projectsettings.upm;_UI.thumbview={dx:_UI.thumbgutter,dy:_UI.thumbgutter+_GP.projectsettings.ascent*a,dz:a}}function calculateDefaultView(){_GP.projectsettings.upm>2e3&&(_UI.defaultview={dx:200,dy:550,dz:.3},_UI.defaultkernview={dx:400,dy:400,dz:.2})}function isWorkItemSelected(){switch(_UI.navhere){case"character edit":return!0;case"linked shapes":return _UI.selectedlinkedshape;case"ligatures":return _UI.selectedchar;case"kerning":return _UI.selectedkern}return!1}function getCurrentWorkItemName(){switch(_UI.navhere){case"character edit":case"linked shapes":return getSelectedCharName();case"ligatures":return"ligature "+getSelectedCharName();case"kerning":return getSelectedKern().getName()}return"no working object"}function ss(a){a=a||"[probably a dynamically-generated page control]";var b=getSelectedCharShapes();return"linked shapes"===_UI.navhere?b[0]||!1:-1!==_UI.selectedshape?_UI.selectedshape>=0&&_UI.selectedshape=c.xmin;h-=d)f(h);for(var i=b.dy;i=c.ymin;j-=d)e(j)}}function drawGuides(){if(isWorkItemSelected()){var a=_GP.projectsettings,b="character edit"===_UI.navhere||"ligatures"===_UI.navhere,c="kerning"===_UI.navhere;if(_UI.showguides){var d;for(var e in a.guides)a.guides.hasOwnProperty(e)&&(d=a.guides[e],d.editable&&d.draw());if(a.guides.xheight.location=a.xheight,a.guides.capheight.location=a.capheight,a.guides.ascent.location=a.ascent,a.guides.baseline.location=0,a.guides.descent.location=a.ascent-a.upm,a.guides.leftside.location=0,a.guides.rightside.location=a.upm,_UI.showovershoots){var f=a.overshoot;a.guides.xheight.draw(-1*f),a.guides.ascent.draw(-1*f),a.guides.baseline.draw(f),a.guides.descent.draw(f)}var g=getSelectedChar();if(b){a.guides.leftside.draw(-1*getSelectedCharLeftSideBearing());var h=g.charwidth;_UI.eventhandlers.tempnewbasicshape&&(h=Math.max(h,_UI.eventhandlers.tempnewbasicshape.xmax)),a.guides.rightside.location=h,a.guides.rightside.draw(getSelectedCharRightSideBearing()),a.guides.rightside.draw()}else c&&(_UI.guides.leftgroup_xmax.location=getSelectedKern().value);if(a.guides.xheight.draw(),a.guides.capheight.draw(),a.guides.ascent.draw(),a.guides.descent.draw(),c||a.guides.leftside.draw(),a.guides.baseline.draw(),c&&_UI.guides.leftgroup_xmax.draw(),c&&_UI.guides.rightgroup_xmin.draw(),!c&&(a.guides.baseline.visible||a.guides.leftside.visible)){var i=getView("guides");_UI.chareditctx.fillStyle=a.guides.baseline.color,_UI.chareditctx.beginPath(),_UI.chareditctx.moveTo(i.dx,i.dy),_UI.chareditctx.lineTo(i.dx,i.dy+2*a.pointsize),_UI.chareditctx.lineTo(i.dx-2*a.pointsize,i.dy),_UI.chareditctx.closePath(),_UI.chareditctx.fill()}}}}function setupGhostCanvas(){_UI.ishereghostcanvas=getEditDocument().getElementById("ishereghostcanvas"),_UI.ishereghostcanvas.height=_UI.chareditcanvassize,_UI.ishereghostcanvas.width=_UI.chareditcanvassize,_UI.ishereghostctx=_UI.ishereghostcanvas.getContext("2d"),_UI.ishereghostctx.fillStyle="cyan",_UI.ishereghostctx.globalAlpha=.5,_UI.ishereghostcanvas.style.backgroundColor="transparent"}function setupEditCanvas(){_UI.chareditcanvas=getEditDocument().getElementById("chareditcanvas"),_UI.chareditcanvas.height=_UI.chareditcanvassize,_UI.chareditcanvas.width=_UI.chareditcanvassize,_UI.chareditctx=_UI.chareditcanvas.getContext("2d"),_UI.chareditcanvas.onselectstart=function(){return!1},_UI.chareditcanvas.onmouseout=mouseoutcec,_UI.chareditcanvas.onmouseover=mouseovercec}function resetCursor(){getEditDocument().body.style.cursor="default"}function initEventHandlers(){function a(a){switch(mouseovercec(),document.onselectstart=function(){return!1},(a.layerX||a.layerX)&&(_UI.eventhandlers.mousex=a.layerX,_UI.eventhandlers.mousey=a.layerY),(a.offsetX||a.offsetX)&&(_UI.eventhandlers.mousex=a.offsetX,_UI.eventhandlers.mousey=a.offsetY),resetCursor(),_UI.selectedtool){case"pathedit":b=_UI.eventhandlers.eh_shapesel;break;case"shaperesize":b=_UI.eventhandlers.eh_shaperesize;break;case"pan":getEditDocument().body.style.cursor="move",b=_UI.eventhandlers.eh_pantool;break;case"newpath":getEditDocument().body.style.cursor="crosshair",b=_UI.eventhandlers.eh_addpath;break;case"newrect":case"newoval":getEditDocument().body.style.cursor="crosshair",b=_UI.eventhandlers.eh_addrectoval;break;case"kern":getEditDocument().body.style.cursor="col-resize",b=_UI.eventhandlers.eh_kern}b[a.type](a)}var b=new Tool_PathEdit;_UI.eventhandlers.eh_addrectoval=new Tool_NewBasicShape,_UI.eventhandlers.eh_shapesel=new Tool_PathEdit,_UI.eventhandlers.eh_shaperesize=new Tool_ShapeResize,_UI.eventhandlers.eh_pantool=new Tool_Pan,_UI.eventhandlers.eh_addpath=new Tool_NewPath,_UI.eventhandlers.eh_addrectoval=new Tool_NewBasicShape,_UI.eventhandlers.eh_kern=new Tool_Kern,_UI.chareditcanvas.addEventListener("mousedown",a,!1),_UI.chareditcanvas.addEventListener("mousemove",a,!1),_UI.chareditcanvas.addEventListener("mouseup",a,!1),_UI.chareditcanvas.onmouseover=mouseovercec,_UI.chareditcanvas.onmouseout=mouseoutcec,_UI.chareditcanvas.addEventListener("wheel",mousewheel,!1),document.getElementById("navarea_panel")&&document.getElementById("navarea_panel").addEventListener("wheel",function(a){a.stopPropagation()},!1),getEditDocument().addEventListener("keypress",keypress,!1),getEditDocument().addEventListener("keydown",keypress,!1),getEditDocument().addEventListener("keyup",keyup,!1)}function Tool_NewPath(){this.dragging=!1,this.firstpoint=!0,this.currpt={},this.mousedown=function(){var a=new PathPoint({P:new Coord({x:cx_sx(_UI.eventhandlers.mousex),y:cy_sy(_UI.eventhandlers.mousey)}),H1:new Coord({x:cx_sx(_UI.eventhandlers.mousex-100),y:cy_sy(_UI.eventhandlers.mousey)}),H2:new Coord({x:cx_sx(_UI.eventhandlers.mousex+100),y:cy_sy(_UI.eventhandlers.mousey)}),type:"flat",selected:!0,useh1:!1,useh2:!1});if(this.firstpoint){var b=new Path({pathpoints:[a]}),c="linked shapes"===_UI.navhere?getLength(_GP.linkedshapes):getSelectedCharShapes().length,d=addShape(new Shape({name:"Path "+c,path:b}));d.path.selectPathPoint(0)}else{var e=ss("Event Handler New Path").path,f=e.isOverControlPoint(cx_sx(_UI.eventhandlers.mousex),cy_sy(_UI.eventhandlers.mousey));if("P"===f&&e.pathpoints.length>1){var g=e.pathpoints[0],h=_GP.projectsettings.pointsize/getView("Event Handler Tool_NewPath mousedown").dz;if(g.P.x+h>cx_sx(_UI.eventhandlers.mousex)&&g.P.x-hcy_sy(_UI.eventhandlers.mousey)&&g.P.y-h2*_GP.projectsettings.pointsize||Math.abs(this.currpt.P.y-cy_sy(_UI.eventhandlers.mousey))>2*_GP.projectsettings.pointsize)&&(this.currpt.useh1=!0,this.currpt.useh2=!0,this.currpt.H2.x=cx_sx(_UI.eventhandlers.mousex),this.currpt.H2.y=cy_sy(_UI.eventhandlers.mousey),this.currpt.makeSymmetric("H2")),_UI.eventhandlers.lastx=_UI.eventhandlers.mousex,_UI.eventhandlers.lasty=_UI.eventhandlers.mousey,_UI.eventhandlers.uqhaschanged=!0,redraw("Event Handler Tool_NewPath mousemove"))}}function Tool_NewBasicShape(){this.mousedown=function(){_UI.eventhandlers.tempnewbasicshape={xmax:cx_sx(_UI.eventhandlers.mousex),xmin:cx_sx(_UI.eventhandlers.mousex),ymax:cy_sy(_UI.eventhandlers.mousey),ymin:cy_sy(_UI.eventhandlers.mousey)};var a=new Shape({visible:!1,name:"..."});a.path.maxes=_UI.eventhandlers.tempnewbasicshape,a=addShape(a),_UI.eventhandlers.firstx=cx_sx(_UI.eventhandlers.mousex),_UI.eventhandlers.firsty=cy_sy(_UI.eventhandlers.mousey),redraw("Event Handler Tool_NewBasicShape mousedown")},this.mousemove=function(){_UI.eventhandlers.tempnewbasicshape&&(_UI.eventhandlers.tempnewbasicshape.xmax=Math.max(_UI.eventhandlers.firstx,cx_sx(_UI.eventhandlers.mousex)),_UI.eventhandlers.tempnewbasicshape.xmin=Math.min(_UI.eventhandlers.firstx,cx_sx(_UI.eventhandlers.mousex)),_UI.eventhandlers.tempnewbasicshape.ymax=Math.max(_UI.eventhandlers.firsty,cy_sy(_UI.eventhandlers.mousey)),_UI.eventhandlers.tempnewbasicshape.ymin=Math.min(_UI.eventhandlers.firsty,cy_sy(_UI.eventhandlers.mousey)),ss().path.maxes=_UI.eventhandlers.tempnewbasicshape,_UI.eventhandlers.uqhaschanged=!0,redraw("Event Handler Tool_NewBasicShape mousemove"))},this.mouseup=function(){var a=ss("NEWSHAPE MOUSEUP"),b=_UI.eventhandlers.tempnewbasicshape;if(Math.abs(b.xmax-b.xmin)>_GP.projectsettings.pointsize&&Math.abs(b.ymax-b.ymin)>_GP.projectsettings.pointsize){var c="linked shapes"===_UI.navhere?getLength(_GP.linkedshapes):getSelectedCharShapes().length;"newrect"===_UI.selectedtool?(a.name="Rectangle "+c,a.path=rectPathFromMaxes(b)):(a.name="Oval "+c,a.path=ovalPathFromMaxes(b)),a.visible=!0}else deleteShape();_UI.eventhandlers.firstx=-100,_UI.eventhandlers.firsty=-100,_UI.eventhandlers.tempnewbasicshape=!1,history_put("New Basic Shape tool"),_UI.eventhandlers.uqhaschanged=!1,clickTool("pathedit")}}function Tool_PathEdit(){this.moving=!1,this.controlpoint=!1,this.mousedown=function(){var a=ss("Path Edit - Mouse Down");this.controlpoint=a?a.path.isOverControlPoint(cx_sx(_UI.eventhandlers.mousex),cy_sy(_UI.eventhandlers.mousey)):!1,this.controlpoint?(this.moving=!0,_UI.eventhandlers.lastx=_UI.eventhandlers.mousex,_UI.eventhandlers.lasty=_UI.eventhandlers.mousey):clickSelectShape(_UI.eventhandlers.mousex,_UI.eventhandlers.mousey)?(_UI.eventhandlers.lastx=_UI.eventhandlers.mousex,_UI.eventhandlers.lasty=_UI.eventhandlers.mousey):(a&&a.path.calcMaxes(),clickEmptySpace()),redraw("Event Handler Tool_PathEdit mousedown") },this.mousemove=function(){if(this.moving){var a=ss("Path Edit - Mouse Move"),b=a.path.sp();_UI.eventhandlers.toolhandoff&&(b.H2.x=cx_sx(_UI.eventhandlers.mousex),b.H2.y=cy_sy(_UI.eventhandlers.mousey),_UI.eventhandlers.toolhandoff=!1);var c=0,d=0,e=getView("Event Handler Tool_PathEdit mousemove").dz;switch(this.controlpoint){case"P":b.P.xlock||(c=(_UI.eventhandlers.mousex-_UI.eventhandlers.lastx)/e),b.P.ylock||(d=(_UI.eventhandlers.lasty-_UI.eventhandlers.mousey)/e);break;case"H1":b.H1.xlock||(c=(_UI.eventhandlers.mousex-_UI.eventhandlers.lastx)/e),b.H1.ylock||(d=(_UI.eventhandlers.lasty-_UI.eventhandlers.mousey)/e);break;case"H2":b.H2.xlock||(c=(_UI.eventhandlers.mousex-_UI.eventhandlers.lastx)/e),b.H2.ylock||(d=(_UI.eventhandlers.lasty-_UI.eventhandlers.mousey)/e)}b.updatePathPointPosition(this.controlpoint,c,d),a.path.calcMaxes(),_UI.eventhandlers.lastx=_UI.eventhandlers.mousex,_UI.eventhandlers.lasty=_UI.eventhandlers.mousey,_UI.eventhandlers.uqhaschanged=!0,redraw("Event Handler Tool_PathEdit mousemove")}},this.mouseup=function(){this.moving=!1,_UI.eventhandlers.lastx=-100,_UI.eventhandlers.lasty=-100,_UI.eventhandlers.uqhaschanged&&(ss("Path Edit - Mouse Up").path.calcMaxes(),updateCurrentCharWidth(),history_put("Path Edit tool"),_UI.eventhandlers.uqhaschanged=!1,redraw("Event Handler Tool_PathEdit mouseup"))}}function Tool_ShapeResize(){this.dragging=!1,this.resizing=!1,_UI.eventhandlers.corner=!1,this.mousedown=function(){var a=ss("eventHandler - mousedown");_UI.eventhandlers.corner=a?a.isOverHandle(_UI.eventhandlers.mousex,_UI.eventhandlers.mousey):!1,_UI.eventhandlers.lastx=_UI.eventhandlers.mousex,_UI.eventhandlers.firstx=_UI.eventhandlers.mousex,_UI.eventhandlers.lasty=_UI.eventhandlers.mousey,_UI.eventhandlers.firsty=_UI.eventhandlers.mousey,_UI.eventhandlers.corner?(this.resizing=!0,this.dragging=!1):clickSelectShape(_UI.eventhandlers.mousex,_UI.eventhandlers.mousey)?(this.dragging=!0,this.resizing=!1,redraw("Event Handler Tool_ShapeResize mousedown")):clickEmptySpace()},this.mousemove=function(){var a=ss("eventHandler - Tool_ShapeResize mousemove"),b=!1,c=getView("Event Handler Tool_ShapeResize mousemove").dz;if(a.link)this.dragging&&!a.uselinkedshapexy&&(a.xpos+=(_UI.eventhandlers.mousex-_UI.eventhandlers.lastx)/c,a.ypos+=(_UI.eventhandlers.lasty-_UI.eventhandlers.mousey)/c,b=!0,resetCursor());else{if(this.dragging){var d=a.xlock?0:d=(_UI.eventhandlers.mousex-_UI.eventhandlers.lastx)/c,e=a.ylock?0:e=(_UI.eventhandlers.lasty-_UI.eventhandlers.mousey)/c;a.path.updatePathPosition(d,e),resetCursor(),b=!0}else this.resizing&&(evHanShapeResize(a,_UI.eventhandlers.corner),b=!0);a&&a.isOverHandle(_UI.eventhandlers.mousex,_UI.eventhandlers.mousey)}b&&(_UI.eventhandlers.lastx=_UI.eventhandlers.mousex,_UI.eventhandlers.lasty=_UI.eventhandlers.mousey,_UI.eventhandlers.uqhaschanged=!0,redraw("Event Handler Tool_ShapeResize mousemove"))},this.mouseup=function(){resetCursor();var a=ss("eventHandler - mouseup");_UI.eventhandlers.tempnewbasicshape&&(_UI.eventhandlers.tempnewbasicshape=!1,a.hidden=!1,_UI.eventhandlers.lastx=_UI.eventhandlers.firstx,_UI.eventhandlers.lasty=_UI.eventhandlers.firsty,evHanShapeResize(a,_UI.eventhandlers.corner)),this.resizing&&a.path.calcMaxes(),updateCurrentCharWidth(),this.dragging=!1,this.resizing=!1,_UI.eventhandlers.lastx=-100,_UI.eventhandlers.lasty=-100,_UI.eventhandlers.firstx=-100,_UI.eventhandlers.firsty=-100,_UI.eventhandlers.uqhaschanged&&history_put("Path Edit tool"),_UI.eventhandlers.uqhaschanged=!1,redraw("Event Handler Tool_ShapeResize mouseup")}}function Tool_Pan(){this.dragging=!1,this.deltax=0,this.deltay=0,this.mousedown=function(){var a=getView("Event Handler Tool_Pan mousedown");this.deltax=_UI.eventhandlers.mousex-a.dx,this.deltay=_UI.eventhandlers.mousey-a.dy,this.dragging=!0},this.mouseup=function(){this.dragging=!1,this.deltax=0,this.deltay=0},this.mousemove=function(){this.dragging&&(setView({dx:_UI.eventhandlers.mousex-this.deltax,dy:_UI.eventhandlers.mousey-this.deltay}),redraw("Event Handler Tool_Pan mousemove"))}}function Tool_Kern(){this.dragging=!1,this.deltax=0,this.mousedown=function(){getView("Event Handler Tool_Kern mousedown");this.deltax=_UI.eventhandlers.mousex,this.dragging=!0},this.mouseup=function(){this.dragging=!1,this.deltax=0,history_put("Kern Adjustment: "+getSelectedKern().value)},this.mousemove=function(){if(this.dragging){var a=getSelectedKern(),b=1*a.value;a.value=b+1*(_UI.eventhandlers.mousex-this.deltax)/getView().dz,this.deltax=_UI.eventhandlers.mousex,redraw()}}}function cx_sx(a){var b=getView("cx_sx");return(a-b.dx)/b.dz}function cy_sy(a){var b=getView("cy_sy");return(b.dy-a)/b.dz}function clickEmptySpace(){var a=ss("Click Empty Space");a&&(a.path.selectPathPoint(!1),a.path.calcMaxes()),_UI.selectedshape=-1}function evHanShapeResize(a,b){var c=cx_sx(_UI.eventhandlers.mousex),d=cy_sy(_UI.eventhandlers.mousey),e=cx_sx(_UI.eventhandlers.lastx),f=cy_sy(_UI.eventhandlers.lasty),g=f-d,h=e-c,i=a.path.maxes.xmin,j=a.path.maxes.ymax,k=!a.wlock&&!a.hlock&&a.ratiolock;switch(b){case"n":canResize("n")&&(h=0,g*=-1,k&&(h=g),a.path.updatePathSize(h,g));break;case"ne":canResize("ne")&&(h*=-1,g*=-1,k&&(g=h=getRatioLockValue(g,h)),a.path.updatePathSize(h,g),a.path.setPathPosition(!1,j+g));break;case"e":canResize("e")&&(g=0,h*=-1,k&&(g=h),a.path.updatePathSize(h,g),a.path.setPathPosition(!1,j+g));break;case"se":canResize("se")&&(h*=-1,k&&(g=h=getRatioLockValue(g,h)),a.path.updatePathSize(h,g),a.path.setPathPosition(i,j));break;case"s":canResize("s")&&(h=0,k&&(h=g),a.path.updatePathSize(h,g),a.path.setPathPosition(i,j));break;case"sw":canResize("sw")&&(k&&(g=h=getRatioLockValue(g,h)),a.path.updatePathSize(h,g),a.path.setPathPosition(i-h,j));break;case"w":canResize("w")&&(g=0,k&&(g=h),a.path.updatePathSize(h,g),a.path.setPathPosition(i-h,j+g));break;case"nw":canResize("nw")&&(g*=-1,k&&(g=h=getRatioLockValue(g,h)),a.path.updatePathSize(h,g),a.path.setPathPosition(i-h,j+g))}}function updateTNBS(a,b,c,d){_UI.eventhandlers.tempnewbasicshape.xmin+=a,_UI.eventhandlers.tempnewbasicshape.ymax+=b,_UI.eventhandlers.tempnewbasicshape.xmax+=c+a,_UI.eventhandlers.tempnewbasicshape.ymin+=d+b}function canResize(a){var b=ss("canResize");switch(a){case"nw":return!(b.ylock||b.hlock||b.xlock||b.wlock);case"n":return!b.ylock&&!b.hlock;case"ne":return!b.ylock&&!b.hlock&&!b.wlock;case"e":return!b.wlock;case"se":return!b.hlock&&!b.wlock;case"s":return!b.hlock;case"sw":return!b.hlock&&!b.xlock&&!b.wlock;case"w":return!b.xlock&&!b.wlock}return!0}function mousewheel(a){var b=-1*a.deltaY,c="character edit"===_UI.navhere||"linked shapes"===_UI.navhere;c=c&&"block"!==document.getElementById("dialog_box").style.display,c&&a.ctrlKey&&(a.preventDefault(),viewZoom(b>0?1.1:.9))}function mouseovercec(){_UI.eventhandlers.ismouseovercec=!0,document.onselectstart=function(){return!1}}function mouseoutcec(){_UI.eventhandlers.ismouseovercec=!1,document.onselectstart=function(){},resetCursor()}function keyup(a){if(onCanvasEditPage()){var b=getKeyFromEvent(a),c=_UI.eventhandlers;"space"===b&&c.ismouseovercec&&(_UI.selectedtool=c.lastTool,c.isSpaceDown=!1,resetCursor(),redraw("Event Handler - Keyup Spacebar for pan toggle"))}}function keypress(a){if(onCanvasEditPage()&&"kerning"!==_UI.navhere&&"keydown"===a.type){var b=(ss("keypress event"),_UI.eventhandlers),c=getKeyFromEvent(a);"space"===c&&b.ismouseovercec&&(a.preventDefault(),b.isSpaceDown||(b.lastTool=_UI.selectedtool,_UI.selectedtool="pan",b.isSpaceDown=!0,getEditDocument().body.style.cursor="move",redraw("Event Handler - Keydown Spacebar for pan toggle"))),"esc"===c&&closeDialog(),("?"===c||"¿"===c)&&(a.preventDefault(),toggleKeyboardTips()),("undo"===c||a.ctrlKey&&"z"===c)&&(a.preventDefault(),history_pull()),"del"===c&&(a.preventDefault(),deleteShape(),history_put("Delete Shape"),redraw("Keypress DEL")),a.ctrlKey&&"c"===c&&(a.preventDefault(),copyShape()),a.ctrlKey&&"v"===c&&(a.preventDefault(),pasteShape(),history_put("Paste Shape"),redraw("Paste Shape")),a.ctrlKey&&"s"===c&&(a.preventDefault(),saveGlyphrProjectFile()),a.ctrlKey&&"plus"===c&&(a.preventDefault(),viewZoom(1.1),redraw("Zoom Keyboard Shortcut")),a.ctrlKey&&"minus"===c&&(a.preventDefault(),viewZoom(.9),redraw("Zoom Keyboard Shortcut")),a.ctrlKey&&"0"===c&&(a.preventDefault(),setView(clone(_UI.defaultview)),redraw("Zoom Keyboard Shortcut")),"up"===c&&(a.preventDefault(),nudge(0,1)),"down"===c&&(a.preventDefault(),nudge(0,-1)),"left"===c&&(a.preventDefault(),nudge(-1,0)),"right"===c&&(a.preventDefault(),nudge(1,0))}}function getKeyFromEvent(a){var b={8:"backspace",9:"tab",13:"enter",16:"shift",17:"ctrl",18:"alt",20:"capslock",26:"undo",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"ins",46:"del",91:"meta",93:"meta",187:"plus",189:"minus",224:"meta"};return b[parseInt(a.which)]||String.fromCharCode(a.which).toLowerCase()}function nudge(a,b){var c,d,e,f=ss("Nudge");f&&(d=a*_GP.projectsettings.spinnervaluechange,e=b*_GP.projectsettings.spinnervaluechange,c=f.path.sp(),c?c.updatePathPointPosition("P",d,e):f.path.updatePathPosition(d,e),redraw("Nudge"))}function navigate(a){"firstrun"===_UI.navhere?makeLayout_Firstrun():_UI.popout?onCanvasEditPage()?makeLayout_PopOut():(popIn(),makeLayout_PopIn(a)):makeLayout_PopIn(a),loadPageContent(),document.body.focus()}function update_NavPanels(){_UI.popout?make_NavPanels_PopOut():make_NavPanels_PopIn()}function makeLayout_Firstrun(){document.getElementById("primaryScreenLayout").innerHTML='
'}function popOut(){_UI.popout=window.open("","glyphr_popout");var a=_UI.popout,b=_UI.popout.document;b.write('Glyphr Studio - Canvas
'),document.title="Glyphr Studio - Tools",document.body.classList.add("poppedOut"),b.head.appendChild(document.styleSheets[0].ownerNode.cloneNode(!0)),a.onBeforeUnload=popIn,a.clickTool=clickTool,a.viewZoom=viewZoom,a.setView=setView,a.popIn=popIn,a.toggleKeyboardTips=toggleKeyboardTips,b.getElementById("mainwrapper").style.overflowY="hidden",navigate()}function makeLayout_PopOut(){var a="linked shapes"===_UI.navhere,b="kerning"===_UI.navhere,c='';c+='',b||(c+=''),c+='',c+='',a||b||(c+=''),b||(c+=''),c+='',c+="
",document.getElementById("primaryScreenLayout").innerHTML=c,make_NavPanels_PopOut()}function make_NavPanels_PopOut(){var a="linked shapes"===_UI.navhere,b="character edit"===_UI.navhere,c="ligatures"===_UI.navhere,d="test drive"===_UI.navhere,e="kerning"===_UI.navhere;document.getElementById("popout_pagenav").innerHTML=makePanel_PageNav(),b?document.getElementById("popout_charchooser").innerHTML=makePanel_CharChooser():a?document.getElementById("popout_charchooser").innerHTML=makePanel_LinkedShapeChooser():c&&(document.getElementById("popout_charchooser").innerHTML=makePanel_LigatureChooser()),document.getElementById("popout_history").innerHTML=makePanel_History(),a||e||(document.getElementById("popout_layerchooser").innerHTML=makePanel_LayerChooser()),e||(document.getElementById("popout_actions").innerHTML=makePanel_Actions()),document.getElementById("popout_guides").innerHTML=makePanel_Guides(),document.getElementById("popout_attributes").innerHTML=d?makePanel_TestDriveAttributes():e?makePanel_KerningAttributes():makePanel_CharAttributes(),updateSaveIcon()}function popIn(){try{_UI.popout.close()}catch(a){}document.body.classList.remove("poppedOut"),_UI.popout=!1,navigate()}function makeLayout_PopIn(a){var b='
';b+='',b+='',document.getElementById("primaryScreenLayout").innerHTML=b,mouseoutcec();var c=_UI.navhere;if(a)_UI.navprimaryhere=a;else switch(c){case"character edit":_UI.navprimaryhere="npChooser";break;case"linked shapes":_UI.navprimaryhere="npChooser";break;case"ligatures":_UI.navprimaryhere="npChooser";break;case"kerning":_UI.navprimaryhere="npAttributes";break;case"test drive":_UI.navprimaryhere="npAttributes";break;case"font settings":_UI.navprimaryhere="npNav";break;case"project settings":_UI.navprimaryhere="npNav";break;case"export font":_UI.navprimaryhere="npNav";break;case"import svg":_UI.navprimaryhere="npChooser";break;case"help":_UI.navprimaryhere="npNav";break;case"about":_UI.navprimaryhere="npNav"}onCanvasEditPage()?document.getElementById("mainwrapper").style.overflowY="hidden":(make_NavPanels_PopIn(),document.getElementById("mainwrapper").style.overflowY="scroll")}function onCanvasEditPage(){var a=_UI.navhere;return"character edit"===a||"linked shapes"===a||"kerning"===a||"ligatures"===a}function onNoNavPage(){var a=_UI.navhere;return"font settings"===a||"project settings"===a||"export font"===a||"help"===a||"about"===a}function make_NavPanels_PopIn(){var a=document.getElementById("navarea_panel");if(document.getElementById("navarea_tabs").innerHTML=makePanel_NavTabs(),a.innerHTML="",updateSaveIcon(),onNoNavPage())return _UI.navprimaryhere="npNav",void(a.innerHTML=makePanel_PageNav());switch(_UI.navprimaryhere){case"npChooser":switch(_UI.navhere){case"character edit":a.innerHTML=makePanel_CharChooser("selectChar");break;case"import svg":a.innerHTML=makePanel_CharChooser("importSVG_selectChar");break;case"linked shapes":a.innerHTML=makePanel_LinkedShapeChooser();break;case"ligatures":a.innerHTML=makePanel_LigatureChooser()}break;case"npAttributes":switch(_UI.navhere){case"character edit":case"linked shapes":case"ligatures":a.innerHTML=makePanel_CharAttributes(),a.innerHTML+=makePanel_Actions();break;case"kerning":a.innerHTML=makePanel_KerningAttributes();break;case"test drive":a.innerHTML=makePanel_TestDriveAttributes()}break;case"npNav":a.innerHTML=makePanel_PageNav();break;case"npLayers":a.innerHTML=makePanel_LayerChooser();break;case"npGuides":a.innerHTML=makePanel_Guides();break;case"npHistory":a.innerHTML=makePanel_History();break;case"npSave":saveGlyphrProjectFile()}}function getEditDocument(){return _UI.popout?_UI.popout.document:document}function loadPageContent(){switch(_UI.navhere){case"firstrun":loadPage_firstrun();break;case"font settings":loadPage_fontsettings();break;case"project settings":loadPage_projectsettings();break;case"export font":loadPage_exportfont();break;case"import svg":loadPage_importsvg();break;case"help":loadPage_help();break;case"about":loadPage_about();break;case"test drive":loadPage_testdrive();break;case"linked shapes":loadPage_linkedshapes();break;case"character edit":loadPage_charedit();break;case"kerning":loadPage_kerning();break;case"ligatures":loadPage_ligatures()}}function updateSaveIcon(){var a=onNoNavPage()?_UI.colors.offwhite:_UI.colors.gray_90;_UI.projectsaved||(a="white"),document.getElementById("npSave").innerHTML=makeIcon({name:"button_npSave",color:a,hovercolor:"white"})}function makePanel_NavTabs(){var a=[];a.push("npNav");var b=isWorkItemSelected();if("npNav"!==_UI.navprimaryhere)switch(_UI.navhere){case"character edit":a.push("npChooser"),a.push("npLayers"),a.push("npAttributes"),a.push("npHistory"),a.push("npGuides");break;case"linked shapes":a.push("npChooser"),b&&a.push("npAttributes"),b&&a.push("npHistory"),b&&a.push("npGuides");break;case"ligatures":a.push("npChooser"),b&&a.push("npLayers"),b&&a.push("npAttributes"),b&&a.push("npHistory"),b&&a.push("npGuides");break;case"kerning":a.push("npAttributes"),b&&a.push("npHistory"),b&&a.push("npGuides");break;case"test drive":a.push("npAttributes");break;case"import svg":a.push("npChooser")}var c="",d=_UI.colors.accent_85,e=_UI.colors.offwhite;"npNav"===_UI.navprimaryhere?(d=_UI.colors.accent_35,e=_UI.colors.accent_35,document.getElementById("navarea_tabs").style.backgroundColor=_UI.colors.gray_90):document.getElementById("navarea_tabs").style.backgroundColor=_UI.colors.gray_80,"npNav"===_UI.navprimaryhere?c+='':(c+='");for(var f=1;f";var g=onNoNavPage()?_UI.colors.offwhite:_UI.colors.gray_90;return c+='
",c+='
',_UI.debug&&(c+='",c+="
devtools",c+="


"),c+='give
back!
',c+="
"}function makePanel_PageNav(){var a=["character edit","linked shapes","ligatures","kerning","test drive","_","font settings","project settings","_","import svg","export font","_","help","about","_","feature","bug","issue"],b='";b+='
';for(var c,d,e=0;e
':"feature"===a[e]?b+='suggest a feature or improvement
':"bug"===a[e]?b+='email the glyphr studio team
':"issue"===a[e]?b+='create a new issue on github
':(c="nav_"+a[e].replace(" ",""),b+='");return _UI.popout&&(b+='
',b+='',b+='",b+="
"),b+=""}function SatChooser(a){a=a||{},this.clickCallback=a.clickCallback||function(a){console.log("Click callback function returned object: "+JSON.stringify(a))},this.clickCallbackArgs={},this.cellSize=1,this.pointerSize=a.pointerSize||20,this.borderSize=a.borderSize||10,this.step=a.step||51,this.width=this.getDiscreetWidth(a.width?a.width:200),this.height=this.width-this.borderSize,this.screenx=-1e3,this.screeny=-1e3,this.showSat=a.showSat||!0,this.bhcID=a.bhcID||"satchooser",this.borderColor=a.borderColor||"rgb(76,81,86)",this.can=document.createElement("canvas"),this.can.style.backgroundColor="transparent",this.can.style.borderWidth="0px",this.can.style.display="none",this.can.style.position="absolute",this.can.width=this.width,this.can.height=this.height,this.can.setAttribute("id",this.bhcID),this.ctx=this.can.getContext("2d"),this.draw(),this.imgData=this.ctx.getImageData(0,0,this.can.width,this.can.height),this.can.onmouseout=function(a){a.target.style.display="none"},this.can.onblur=function(a){a.target.style.display="none"};var b=this;this.can.onclick=function(a){if(a.hasOwnProperty("offsetX")||(a.offsetX=a.layerX,a.offsetY=a.layerY),!(a.offsetYb.height-b.borderSize||a.offsetXb.width-b.borderSize)){var c=4*Math.floor(a.offsetX-1)+Math.floor(a.offsetY-1)*b.imgData.width*4,d=b.imgData.data[c+0],e=b.imgData.data[c+1],f=b.imgData.data[c+2],g="rgb("+d+","+e+","+f+")",h={r:d,g:e,b:f};b.clickCallbackArgs.colorobject=h,b.clickCallbackArgs.colorstring=g,b.clickCallback(b.clickCallbackArgs)}}}function decToHex(a){for(var b=Number(a).toString(16);b.length<4;)b="0"+b;return"0x"+b.toUpperCase()}function decToHTML(a){return hexToHTML(decToHex(a))}function charToHex(a){for(var b="",c=0;cb.end){var c=b.begin;b.begin=b.end,b.end=c}return a&&(b.begin=Math.max(b.begin,_UI.charrange.latinextendedb.end+1),b.end=Math.max(b.end,_UI.charrange.latinextendedb.end+2)),b.begin=Math.min(b.begin,65534),b.end=Math.min(b.end,65535),b.begin=decToHex(b.begin),b.end=decToHex(b.end),b}function updateCustomRangeTable(){var a=_GP.projectsettings.charrange.custom,b="";if(a.length){b+='Existing custom character ranges:
';for(var c=0;c";b+="
',b+=a[c].begin+"  through  "+a[c].end+"  ",b+="",b+='',b+="

",b+="Note, removing a custom range will not delete character data from your Glyphr Project. ",b+="Custom ranges only determine what is shown in the UI, and what is exported to fonts."}document.getElementById("customrangetable").innerHTML=b}function removeCustomCharacterRange(a){var b=_GP.projectsettings.charrange.custom;b.splice(a,1),updateCustomRangeTable()}function getUnicodeName(a){a=""+a;var b=_UI.unicodenames[a],c=1*a;return c>19968&&40960>c?"CJK Unified Ideograph "+a.substr(2):b||"[name not found]"}function ioSVG_exportSVGfont(){var a=_GP.projectsettings,b=_GP.metadata,c=b.font_family,d=genDateStampSuffix(),e=d.split("-");e[0]=e[0].replace(/\./g,"-"),e[1]=e[1].replace(/\./g,":"),e=e.join(" at ");var f='\n\n \n\n Project: '+a.name+"\n Font exported on "+e+"\n\n Created with Glyphr Studio - the free, web-based font editor\n Version: "+_UI.thisGlyphrStudioVersion+'\n\n Find out more at www.glyphrstudio.com\n\n \n \n \n \n \n \n \n';f+="\n",f+=ioSVG_makeMissingGlyph(),f+="\n\n",f+=ioSVG_makeAllCharsAndLigatures(),f+="\n",f+=ioSVG_makeAllKernPairs(),f+="\n",f+=" \n \n",f+=' "+c+"\n",f+=' ABCDEFGHIJKLMNOPQRSTUVWXYZ\n",f+=' abcdefghijklmnopqrstuvwxyz\n",f+=' 1234567890\n",f+=' !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~\n",f+="";var g=a.name+" - SVG Font - "+d+".svg";saveTextFile(g,f)}function ioSVG_makeFontFace(){debug("\n ioSVG_makeFontFace - START"),calcFontMaxes();var a=" ",b=_GP.metadata,c=_GP.projectsettings,d=_UI.fontmetrics,e="";e+=a+'units-per-em="'+c.upm+'"\n',e+=a+'cap-height="'+c.capheight+'"\n',e+=a+'x-height="'+c.xheight+'"\n',e+=a+'ascent="'+c.ascent+'"\n',e+=a+'descent="'+(c.ascent-c.upm)+'"\n',e+=a+'bbox="'+d.maxes.xmin+", "+d.maxes.ymin+", "+d.maxes.xmax+", "+d.maxes.ymax+'"\n',e+=a+'unicode-range="U+20-'+d.maxchar+'"\n';for(var f in b)b.hasOwnProperty(f)&&(e+=a,e+=f.replace(/_/g,"-"),e+='="',e+=b[f],e+='"\n');return e=e.substring(0,e.length-1),e+=">",debug(" ioSVG_makeFontFace - END\n"),e}function ioSVG_makeMissingGlyph(){debug("\n ioSVG_makeMissingGlyph - START");var a=" ",b=_GP.projectsettings.ascent,c=round(.618*b),d=round(b/100);return a+=' \n";for(var d in c)c.hasOwnProperty(d)&&(b+=ioSVG_makeOneCharOrLigature(c[d],d));b+="\n",b+=" \n";for(var e in a)a.hasOwnProperty(e)&&(b+=ioSVG_makeOneCharOrLigature(a[e],e));return debug(" ioSVG_makeAllCharsAndLigatures - END\n"),b}function ioSVG_makeOneCharOrLigature(a,b){if(!a.charshapes.length)return"";b=b.split("0x"),b.forEach(function(a,b,c){a&&(c[b]="&#x"+a+";")}),b=b.join("");var c=a.makeSVGpathData(),d=" ";return d+='\n'}function ioSVG_makeAllKernPairs(){debug("\n ioSVG_makeAllKernPairs - START");var a=_GP.kerning,b=" \n";for(var c in a)if(a.hasOwnProperty(c))for(var d=0;d\n';return debug(" ioSVG_makeAllKernPairs - END\n"),b}function ioSVG_importSVGfont(a){function b(a){y=(y+2)%360,x.style.transform="rotate("+y+"deg)",w.innerHTML=a}function c(){b("Reading font data..."),_GP=new GlyphrProject,z=z.replace(/&#x/g,"0x");var c;try{c=convertXMLtoJSON(z)}catch(e){return loadPage_firstrun(),void showErrorMessageBox("There was a problem reading the SVG file:
"+e.message)}j=ioSVG_getFirstTagInstance(c,"font"),l=ioSVG_getTags(j,"hkern"),k=ioSVG_getTags(j,"glyph"),k.length<600||a?setTimeout(d,1):document.getElementById("firstruntableright").innerHTML=make_ImportFilter(k.length,l.length)}function d(){b("Importing Character 1 of "+k.length),setTimeout(e,4)}function e(){if(b("Importing Character "+H+" of "+k.length),H>=k.length)return void setTimeout(g,1);if(m=k[H].attributes,o=parseUnicodeInput(m.unicode)," "===m.unicode&&(o=["0x0020"]),o===!1)k.splice(H,1);else if(f(o))k.splice(H,1);else{if(E=[],D=0,n=m.d){n=n.replace(/Z/gi,"z"),n=n.split("z");for(var a=0;a0&&(r=!1):q=!1,1===o.length?(o=o[0],B=Math.min(B,o),A=Math.max(A,o),1*o>_UI.charrange.latinextendedb.end&&C.push(o),F[o]=new Char({charshapes:E,charhex:o,charwidth:q,isautowide:r}),"[name not found]"===getUnicodeName(o)&&(_GP.projectsettings.charrange.filternoncharpoints=!1)):(o=o.join(""),G[o]=new Char({charshapes:E,charhex:o,charwidth:q,isautowide:r})),H++}setTimeout(e,1)}function f(b){if(!a)return!1;if(!b.length)return!0;for(var c=0;c_UI.importrange.end||parseInt(b[c])<_UI.importrange.begin)return!0;return!1}function g(){return J>=l.length?(b("Finalizing the imported font..."),void setTimeout(h,1)):(b("Importing Kern Pair "+J+" of "+l.length),t=[],u=[],s=l[J],t=getKernMembersByName(s.attributes.g1,k,t,_UI.charrange.latinextendedb.end),u=getKernMembersByName(s.attributes.g2,k,u,_UI.charrange.latinextendedb.end),t=getKernMembersByUnicodeID(s.attributes.u1,k,t,_UI.charrange.latinextendedb.end),u=getKernMembersByUnicodeID(s.attributes.u2,k,u,_UI.charrange.latinextendedb.end),t.length&&u.length?(v=generateNewID(I,"kern"),kernval=s.attributes.k||0,I[v]=new HKern({leftgroup:t,rightgroup:u,value:kernval}),J++):l.splice(J,1),void setTimeout(g,1)) }function h(){b("Finalizing the imported font..."),setTimeout(i,4)}function i(){for(var a in _UI.charrange)if(_UI.charrange.hasOwnProperty(a)){rstart=1*_UI.charrange[a].begin,rend=1*_UI.charrange[a].end+1;for(var b=rstart;rend>b;b++)if(getChar(b)){_GP.projectsettings.charrange[a]=!0;break}}C.length&&(C=C.sort(),_GP.projectsettings.charrange.custom.push({begin:C[0],end:C[C.length-1]})),_GP.fontchars=F,_GP.ligatures=G,_GP.kerning=I;var c=ioSVG_getFirstTagInstance(j,"font-face").attributes,d=_GP.projectsettings,e=_GP.metadata,f=c["font-family"]||"My Font";d.upm=1*c["units-per-em"]||1e3,d.name=f,d.ascent=1*c.ascent||700,d.capheight=1*c["cap-height"]||675,d.xheight=1*c["x-height"]||400,d.overshoot=round(d.upm/100),e.font_family=f,e.panose_1=c["panose-1"]||"0 0 0 0 0 0 0 0 0 0",e.font_weight=1*c["font-weight"]||400,e.font_stretch=c["font-stretch"]||"normal",e.underline_position=1*c["underline-position"]||-50,e.underline_thickness=1*c["underline-thickness"]||10,e.strikethrough_position=1*c["strikethrough-position"]||300,e.strikethrough_thickness=1*c["strikethrough-thickness"]||10,e.overline_position=1*c["overline-position"]||750,e.overline_thickness=1*c["overline-thickness"]||10,finalizeGlyphrProject(),closeDialog(),navigate()}document.getElementById("firstruntableright").innerHTML=make_LoadingAnimation(!1),setTimeout(c,10);var j,k,l,m,n,o,p,q,r,s,t,u,v,w=document.getElementById("fontimportstatus"),x=document.getElementById("sweep"),y=0,z=_UI.droppedFileContent,A=0,B=65535,C=[],D=0,E=[],F={},G={},H=0,I={},J=0}function make_LoadingAnimation(){var a="";return a+='
',a+="

Importing Font

",a+='
Reading font data...
',a+='
'+a+" characters and "+b+" kern pairs. Glyphr Studio has a hard time with super-large fonts like this. We recommend pairing it down a little:

";return c+="",c+='",c+='",c+='",c+="
',c+="

Only import Latin characters

This includes Latin and Latin Extended Unicode ranges
(0x0020 - 0x024F).

",c+="
',c+='

Import a custom range of characters

A nice overview of character ranges can be found at
Wikipedia\'s Unicode Block page.
begin:
end:

'+helpUI(unicodeInputHelp())+'
bad range input

',c+="
',c+="

Import all the characters

Don't say we did't try to warn you.",c+="
",c+='

'}function setFontImportRange(){var a=getCustomRange(!1);a&&(_UI.importrange=a,document.getElementById("customrangebegin").value=a.begin,document.getElementById("customrangeend").value=a.end)}function checkFilter(a){"basic"===a?(document.getElementById("basic").checked=!0,document.getElementById("custom").checked=!1,document.getElementById("everything").checked=!1,_UI.importrange.begin=32,_UI.importrange.end=591):"custom"===a?(document.getElementById("basic").checked=!1,document.getElementById("custom").checked=!0,document.getElementById("everything").checked=!1,setFontImportRange()):"everything"===a&&(document.getElementById("basic").checked=!1,document.getElementById("custom").checked=!1,document.getElementById("everything").checked=!0,_UI.importrange.begin=0,_UI.importrange.end=65535),document.getElementById("importfontbutton").disabled=!1}function getKernMembersByName(a,b,c,d){d=d||65535;var e;if(a){a=a.split(",");for(var f=0;f1*e&&(c=c.concat(e)))}return c}function getKernMembersByUnicodeID(a,b,c,d){d=d||65535;var e;if(a){a=a.split(",");for(var f=0;f1*e&&(c=c.concat(e)))}return c}function ioSVG_convertTagsToChar(a){var b,c=[],d={},e=0,f=!1,g=["path","rect","polyline","polygon","ellipse","circle"];try{b=convertXMLtoJSON(a)}catch(h){return void showErrorMessageBox(h.message)}for(var i=ioSVG_getTags(b,g),j={},k=0;k-1&&(c=[a]);return c}function ioSVG_getFirstTagInstance(a,b){if(b===a.name)return a;if(!a.content)return!1;for(var c=0;c99999)return void showErrorMessageBox("Data longer than 100,000 characters is super uncool.");b++}a=a.replace(/-/g,",-")," "===a.charAt(0)&&(a=a.slice(1)),a=a.replace(/,+/g,","),","===a.charAt(a.length-1)&&(a=a.slice(0,-1)),","===a.charAt(0)&&(a=a.slice(1)),a=a.split(",");var c,d=[],e=0,f=[];for(b=1;b<=a.length;){if(ioSVG_isPathCommand(a[b])){f=a.slice(e+1,b),c=a[e];for(var g=0;g-1?a:!1}function ioSVG_handlePathChunk(a,b,c){var d,e,f=a.command,g=function(a){return a.indexOf(f)>-1},h=b[b.length-1]||new PathPoint({P:new Coord({x:0,y:0})}),i=h.P.x,j=h.P.y;if(g("MmLlHhVv")){var k=i,l=j;switch(f){case"L":case"M":k=a.data[0],l=a.data[1];break;case"l":case"m":k=a.data[0]+i,l=a.data[1]+j;break;case"H":k=a.data[0];break;case"h":k=a.data[0]+i;break;case"V":l=a.data[0];break;case"v":l=a.data[0]+j}d=new Coord({x:k,y:l}),h.useh2=!1,b.push(new PathPoint({P:d,H1:clone(d),H2:clone(d),type:"corner",useh1:!1,useh2:!0}))}else if(g("CcQqTt")){var m=!1;if(g("Qq")&&(m=new Coord({x:a.data[0],y:a.data[1]}),a.data="q"===f?convertQuadraticToCubic(a.data,0,0):convertQuadraticToCubic(a.data,i,j)),g("Tt")){var n=h.findQuadraticSymmetric();a.data.unshift(n.y),a.data.unshift(n.x),"t"===f?(a.data[0]-=i,a.data[1]-=j,m=new Coord({x:a.data[0],y:a.data[1]}),a.data=convertQuadraticToCubic(a.data,0,0)):(m=new Coord({x:a.data[0],y:a.data[1]}),a.data=convertQuadraticToCubic(a.data,i,j))}for(var o=[];a.data.length;){if(o=[],o=a.data.splice(0,6),o.length%6!==0)for(showErrorMessageBox("Bezier path command (C or c) was expecting 6 arguments, was passed ["+o+']\n
Failing "gracefully" by filling in default data.');o.length<6;)o.push(o[o.length-1]+100);h.H2=new Coord({x:o[0],y:o[1]}),h.useh2=!0,h.resolvePointType(),e=new Coord({x:o[2],y:o[3]}),d=new Coord({x:o[4],y:o[5]}),g("cqt")&&(h.H2.x+=i,h.H2.y+=j,e.x+=i,e.y+=j,d.x+=i,d.y+=j,m&&(m.x+=i,m.y+=j)),b.push(new PathPoint({P:clone(d),H1:clone(e),H2:clone(d),Q:clone(m),useh1:!0,useh2:!0,type:"corner"}))}}else g("Ss")?(h.makeSymmetric("H1"),h.useh2=!0,e=new Coord({x:a.data[0],y:a.data[1]}),d=new Coord({x:a.data[2],y:a.data[3]}),g("s")&&(e.x+=i,e.y+=j,d.x+=i,d.y+=j),b.push(new PathPoint({P:clone(d),H1:clone(e),H2:clone(d),type:"corner",useh1:!0,useh2:!0}))):g("Zz")||showErrorMessageBox("Unrecognized path command "+f+", ignoring and moving on...");var p=b[b.length-1];return c&&p.resolvePointType(),b}function convertQuadraticToCubic(a,b,c){for(var d,e,f,g,h,i,j,k,l,m,n,o=[],p=b,q=c;a.length;){if(d=[],d=a.splice(0,4),d.length%4!==0)for(showErrorMessageBox("Quadratic Bezier path command (Q or q) was expecting 4 arguments, was passed ["+d+']\n
Failing "gracefully" by filling in default data.');d.length<4;)d.push(d[d.length-1]+100);i=p,j=q,k=d[0],l=d[1],m=d[2],n=d[3],e=i+2/3*(k-i),f=j+2/3*(l-j),g=m+2/3*(k-m),h=n+2/3*(l-n),o.push(e),o.push(f),o.push(g),o.push(h),o.push(m),o.push(n),p=m,q=n}return o}function convertXMLtoJSON(a){function b(a){var d=a.childNodes;if(0===d.length)return trim(a.nodeValue);for(var e,f,g,h,i=[],j=0;j-1){if(c=_GP.ligatures[a])return c;if(b)return _GP.ligatures[a]=new Char({charhex:a}),_GP.ligatures[a]}else{if(!(a.indexOf("0x")>-1))return _GP.linkedshapes[a]||!1;if(c=_GP.fontchars[a])return c;if(b)return _GP.fontchars[a]=new Char({charhex:a}),_GP.fontchars[a]}return!1}function getCharName(a){if(a=""+a,!a)return!1;var b=getUnicodeName(a);if("[name not found]"!==b)return b;var c=getChar(a);return c&&c.shape?c.shape.name:a.indexOf("0x",2)>-1?hexToHTML(a):"[name not found]"}function getFirstCharID(){return _GP.fontchars["0x0041"]?"0x0041":getFirstID(_GP.fontchars)}function getSelectedChar(){var a;return"linked shapes"===_UI.navhere?a=getChar(_UI.selectedlinkedshape):"firstrun"===_UI.navhere?!1:"kerning"!==_UI.navhere?a=getChar(_UI.selectedchar,!0):!1}function getSelectedCharID(){return"linked shapes"===_UI.navhere?_UI.selectedlinkedshape:_UI.selectedchar}function getSelectedCharName(){return getCharName("linked shapes"===_UI.navhere?_UI.selectedlinkedshape:_UI.selectedchar)}function getSelectedCharShapes(){var a=getSelectedChar();return a&&"linkedshape"===a.objtype?[a.shape]:a?a.charshapes:[]}function getSelectedCharLeftSideBearing(){var a=getSelectedChar();return a?"linkedshape"===a.objtype?0:a.isautowide?a.leftsidebearing||_GP.projectsettings.defaultlsb:0:0}function getSelectedCharRightSideBearing(){var a=getSelectedChar();return a?"linkedshape"===a.objtype?0:a.isautowide?a.rightsidebearing||_GP.projectsettings.defaultrsb:0:0}function selectChar(a,b){_UI.selectedchar=a,_UI.selectedshape=-1,b||navigate("npAttributes")}function updateCurrentCharWidth(){var a=getSelectedChar();if("character edit"===_UI.navhere)a.calcCharMaxes();else if("linked shapes"===_UI.navhere&&a){var b=a.usedin;if(b)for(var c=0;c
',openDialog(b)}else openDialog('

Add Linked Shape

No Linked Shapes exist. First, create some Linked Shapes, then you can insert them into characters.
')}function insertLinkedShape(a,b){var c=new LinkedShapeInstance({link:a,xpos:100,ypos:100}),d=getChar(b,!0);d.charshapes.push(c),d.calcCharMaxes(),addToUsedIn(a,b),closeDialog(),history_put("insert linked shape from charedit"),redraw("insertLinkedShape")}function turnLinkedShapeIntoAShape(){var a=ss(),b=clone(_GP.linkedshapes[a.link].shape);b.name="Linked Shape Instance"===a.name?b.name.replace("Linked Shape from ",""):a.name,deleteShape(),addShape(b),redraw("turnLinkedShapeIntoAShape")}function makeLinkedShapeThumbs(){var a,b="",c=getSelectedCharID(),d=_GP.linkedshapes;for(var e in d)d.hasOwnProperty(e)&&(a=getChar(e),a.shape&&(b+="
",b+='
'+b+"
"),b}function addToUsedIn(a,b){var c=_GP.linkedshapes[a].usedin;c.push(""+b),c.sort(function(a,b){return a-b})}function removeFromUsedIn(a,b){var c=_GP.linkedshapes[a].usedin,d=c.indexOf(""+b);-1!==d&&c.splice(d,1)}function Path(a){if(this.objtype="path",this.pathpoints=!1,a.pathpoints&&a.pathpoints.length){this.pathpoints=[];for(var b=0;bb?a:b:Math.abs(a)>Math.abs(b)?a:b}function getBounds(a,b,c,d,e,f,g,h){var i,j,k,l,m,n,o={minx:Math.min(a,g),miny:Math.min(b,h),maxx:Math.max(a,g),maxy:Math.max(b,h)},p=c-a,q=d-b,r=e-c,s=f-d,t=g-e,u=h-f;return(co.maxx||eo.maxx)&&(p+t!=2*r&&(r+=.01),i=2*(p-r),j=2*(p-2*r+t),k=(2*r-2*p)*(2*r-2*p)-2*p*j,l=Math.sqrt(k),m=(i+l)/j,n=(i-l)/j,m>0&&1>m&&checkXbounds(o,getBezierValue(m,a,c,e,g)),n>0&&1>n&&checkXbounds(o,getBezierValue(n,a,c,e,g))),(do.maxy||fo.maxy)&&(q+u!=2*s&&(s+=.01),i=2*(q-s),j=2*(q-2*s+u),k=(2*s-2*q)*(2*s-2*q)-2*q*j,l=Math.sqrt(k),m=(i+l)/j,n=(i-l)/j,m>0&&1>m&&checkYbounds(o,getBezierValue(m,b,d,f,h)),n>0&&1>n&&checkYbounds(o,getBezierValue(n,b,d,f,h))),o}function checkXbounds(a,b){a.minx>b?a.minx=b:a.maxxb?a.miny=b:a.maxy0&&_UI.selectedshape>=0&&(a.splice(_UI.selectedshape,1),a.length===_UI.selectedshape&&(_UI.selectedshape=_UI.selectedshape-1)),_UI.selectedshape>=0&&a[_UI.selectedshape].link&&(_UI.selectedtool="shaperesize"),updateCurrentCharWidth()}function turnSelectedShapeIntoALinkedShape(){var a=clone(ss());deleteShape(),a.name="Linked Shape from "+a.name;var b=addLinkedShape(a);insertLinkedShape(b,getSelectedCharID()),_UI.selectedshape=getSelectedCharShapes().length-1,redraw("turnSelectedShapeIntoALinkedShape")}function clickSelectShape(a,b){if("linked shapes"===_UI.navhere)return clickSelectLinkedShape(a,b);for(var c,d=getSelectedCharShapes().length-1;d>=0;d--)if(c=getSelectedCharShapes()[d],c.isHere(a,b))return c.link||c.path.selectPathPoint(!1),d!==_UI.selectedshape&&(_UI.selectedshape=d,c.link&&(_UI.selectedtool="shaperesize")),_UI.navprimaryhere="npAttributes",!0;return _UI.selectedshape=-1,!1}function loadPage_about(){var a="
"+makeGlyphrStudioLogo({width:376,fill:_UI.colors.accent_55})+"

"+_UI.thisGlyphrStudioVersion+"

Website:     glyphrstudio.com for all the info.
Email:     mail@glyphrstudio.com with any questions, and we'd be happy to help out.
Twitter:     @glyphrstudio for short updates and announcements.
Blog:     glyphrstudio.com/blog for verbose updates and announcements.
GitHub:     github.com/mattlag/Glyphr-Studio for dev and code related stuff.
Feedback:     glyphrstudio.uservoice.com to suggest improvements or new feature ideas.

This Glyphr Project

The currently opened project was created with: "+_GP.projectsettings.version+"

Glyphr Projects may be incompatible with different Beta versions of Glyphr Studio.

"+make_ContributeHTML()+"

License

Glyphr Studio is licensed under a GNU General Public License.
Which is a free / open source 'copyleft' license. You are free to use, distribute, and modify Glyphr Studio as long as this license and its freeness stays intact.



"; getEditDocument().getElementById("mainwrapper").innerHTML=a}function make_ContributeHTML(){var a="

Contribute!

If you think Glyphr Studio is pretty cool, there are two huge ways you can make it better!

  • Send Feedback - Use the beta and let us know if you run into issues. Follow us on Twitter and read the Blog, and participate in discussions. Be vocal, and let us know what we should do next!
  • Make a Monetary Contribution - Glyphr Studio will always be free, and we think that is very important. But, it does take some money to keep it going. Contributions of even small amounts of money help keep the Glyphr Studio effort going strong!

    "+'

';return a}function loadPage_charedit(){getEditDocument().getElementById("mainwrapper").innerHTML=editPage_Content(),setupEditCanvas(),initEventHandlers(),_UI.selectedtool="pathedit",_UI.selectedshape=-1,_UI.devselectedshape&&(_UI.selectedshape=_UI.devselectedshape,_UI.devselectedshape=!1),_UI.selectedchar.length>6&&(_UI.selectedchar=getFirstCharID()),redraw("loadPage_charedit")}function redraw_CharacterEdit(){_UI.redrawing=!0;var a=getSelectedChar();a&&a.calcCharMaxes(),drawGrid(),drawGuides(),a&&a.drawCharToArea(_UI.chareditctx,getView("Redraw"));var b=ss("Redraw");b&&(b.drawSelectOutline(b.link!==!1),b.link&&(_UI.selectedtool="shaperesize")),_UI.redrawing=!1}function loadPage_exportfont(){var a='

Export Font

';a+='
'+makeIcon({name:"nav_exportsvg",size:60,color:_UI.colors.accent_55,hovercolor:!1})+'

SVG Font

Scalable Vector Graphics Fonts are text-file-based fonts that some browsers can use natively to display web page text. You can also convert SVG Fonts to any other kind of font using free online services.




',a+='
'+makeIcon({name:"nav_exportotf",size:60,color:_UI.colors.gray_80,hovercolor:!1})+'

OTF Font  coming soon!

Open Type Fonts are font files that can be installed on any computer, then used for things like word processing or graphics.

Exporting OTF Fonts will be available in the next release of Glyphr Studio!
',a+="
",getEditDocument().getElementById("mainwrapper").innerHTML=a}function loadPage_firstrun(){var a="
"+_UI.thisGlyphrStudioVersion+"

For more informaiton visit www.glyphrstudio.com
Glyphr Studio is licensed under a GNU General Public License.
Which is a free / open source 'copyleft' license. You are free to use, distribute, and modify Glyphr Studio as long as this license and its freeness stays intact.
"+make_ImportOrCreateNew()+"
",b=document.getElementById("mainwrapper");b.innerHTML=a,b.style.marginLeft="0px",document.getElementById("firstruntableright").addEventListener("dragover",handleDragOver,!1),document.getElementById("firstruntableright").addEventListener("drop",handleDrop,!1),document.getElementById("firstruntableright").addEventListener("dragleave",handleDragLeave,!1),document.getElementById("firstruntableleft").addEventListener("dragover",handleDragOver,!1),document.getElementById("firstruntableleft").addEventListener("drop",handleDrop,!1),document.getElementById("firstruntableleft").addEventListener("dragleave",handleDragLeave,!1),window.addEventListener("message",handleMessage,!1),window.opener&&window.opener.postMessage("ready","*"),document.getElementById("splashscreenlogo").innerHTML=makeGlyphrStudioLogo({fill:"white",width:400})}function make_ImportOrCreateNew(){var a="

drag and drop to load a file

Glyphr Project File (.txt)
SVG Font File (.svg)
"+makeErrorMessageBox()+"
";return a+="

Start a new Glyphr Project

Project name:  
"}function handleDrop(a){document.getElementById("droptarget").innerHTML="Loading File...",document.getElementById("firstruntableright").style.backgroundColor=_UI.colors.offwhite,a.stopPropagation(),a.preventDefault();var b=a.dataTransfer.files[0],c=new FileReader;c.onload=function(){return function(){debug("\n reader.onload - START"),debug(" filename: "+b.name);var a=b.name.split(".");a=a[a.length-1].toLowerCase();var d;"svg"===a?(_UI.droppedFileContent=c.result,ioSVG_importSVGfont(!1)):"txt"===a?(importGlyphrProjectFromText(c.result),navigate()):(d="Could not read ."+a+" file type.",d+="
Try loading another .svg or .txt file...",document.getElementById("droptarget").innerHTML=d,document.getElementById("firstruntableright").style.backgroundColor=_UI.colors.offwhite),debug(" reader.onload - END\n")}}(b),c.readAsText(b)}function handleMessage(a){_UI.droppedFileContent=a.data,ioSVG_importSVGfont(!1)}function handleDragOver(a){a.stopPropagation(),a.preventDefault(),a.dataTransfer.dropEffect="move",document.getElementById("firstruntableright").style.backgroundColor=_UI.colors.accent_95,document.getElementById("droptarget").innerHTML="Drop it!"}function handleDragLeave(a){a.stopPropagation(),a.preventDefault(),document.getElementById("firstruntableright").style.backgroundColor=_UI.colors.offwhite,document.getElementById("droptarget").innerHTML="Glyphr Project File (.txt)
SVG Font File (.svg)"}function importGlyphrProjectFromText(a){var b=JSON.parse(a),c=b.projectsettings.versionnum,d=b.projectsettings.version;if(d){c||(c="0.3.0"),c=c.split(".");var e=1*c[0],f=1*c[1];0===e?(3===f&&(b=migrateFromBetaThreeToFour(b),f=4),4===f&&(b=migrateFromBetaFourToFive(b),f=5),5===f&&hydrateGlyphrProject(b),f>5&&(document.getElementById("droptarget").innerHTML="drop file here...",alert("Your Glyphr Project was created with a later version of Glyphr Studio. This version of Glyphr Studio cannot open project files created in the future O_o (whoa). Please go to glyphrstudio.com to get the latest release."))):(document.getElementById("droptarget").innerHTML="drop file here...",alert("Your Glyphr Project was created with a later version of Glyphr Studio. This version of Glyphr Studio cannot open project files created in the future O_o (whoa). Please go to glyphrstudio.com to get the latest release."))}else document.getElementById("droptarget").innerHTML="drop file here...",alert("File does not appear to be a Glyphr Project. No version information was found. Please try a different file...")}function migrateFromBetaFourToFive(a){for(var b,c=0;cFont Settings
";c+="

Font Name

",c+="
",c+="

Character Proportions

",c+="

Key Metrics

Ascent height: (em units)
Cap height: (em units)
x Height: (em units)
Descent height: (em units)
Total Units per Em: (em units)

",c+="

Default Side Bearings

Side Bearings are the amount of blank space that is added to the left or right of characters when they are displayed. This metric can be set individually per character, but will default to this value if not set.
Left Side Bearing: (em units)
Right Side Bearing: (em units)

",c+="

Grids and Guides

",c+="

Grid System

",c+="Defining a grid system to use while editing characters in this font makes stuff a whole lot easier. This number is the number of vertical and horizontal divisions to use, it should divide evenly into the Units per Em.
Units per Em:(total)
Grid Divisions(number)
Grid Square Size:(em units)

",c+="

Overshoot

Round letters usually extend a little above the x height line and below the baseline. A light guideline will show this overshoot distance.
Overshoot:(em units)

",c+="

Character Ranges

Character ranges are based on the Unicode Standard, which assigns a hexadecimal number to all possible characters in a font. ",c+="

Standard Character Ranges "+helpUI(unicodeInputHelp())+"

The most common character sets are built into Glyphr Studio, and can be toggled with the checkboxes below.",c+="
"+checkUI("_GP.projectsettings.charrange.basiclatin")+"
 
";for(var d=_UI.basiclatinorder,e=0;e
 
";for(var f=_UI.charrange.latinsuppliment.begin;f<=_UI.charrange.latinsuppliment.end;f++)c+=decToHTML(f)+" ";c+="
",c+="
"+checkUI("_GP.projectsettings.charrange.latinextendeda")+"
 
";for(var g=_UI.charrange.latinextendeda.begin;g<=_UI.charrange.latinextendeda.end;g++)c+=hexToChar(g)+" ";c+="
",c+="
"+checkUI("_GP.projectsettings.charrange.latinextendedb")+"
 
";for(var h=_UI.charrange.latinextendedb.begin;h<=_UI.charrange.latinextendedb.end;h++)c+=hexToChar(h)+" ";c+="
",c+="

Custom Character Ranges "+helpUI(unicodeInputHelp())+"

Additional character ranges above 0x024F can be included here. A nice overview of character ranges can be found at Wikipedia's Unicode Block page.
Custom character ranges are inclusive, must be unique (non-overlapping), must be greater than 0x024F and less than 0xFFFF.

"+checkUI("_GP.projectsettings.charrange.filternoncharpoints")+"
begin:
end:


bad range input


",c+="

Font Metadata

These properties are based on the CSS @font-face standard. More information can be found at the W3C's Fonts Page and their CSS @font-face Page.",c+="";for(var i in b)b.hasOwnProperty(i)&&"font_family"!==i&&(c+="",c+="",c+="",c+="",c+="");c+="
"+i.replace(/_/g,"-")+""+_UI.metadatahelp[i]+"
",c+="

",getEditDocument().getElementById("mainwrapper").innerHTML=c,updateCustomRangeTable()}function updateAscender(a){var b=_GP.projectsettings;b.ascent=Math.max(0,Math.min(b.upm,round(a))),document.getElementById("metric-des").value=b.ascent-b.upm}function loadPage_help(){var a="

Help

";a+="

Peruse the document, but if you have any other questions, you can email mail@glyphrstudio.com, and we'd be happy to help out.

Have a new feature idea? Or a suggestion about how to make a current feature better? Help us out by voting for new features or suggesting improvements.


Jump to a section
Common Character Editing Pages:
Editing:  Shape EditingEdit Canvas ToolsKeyboard Shortcuts
Panels:  CharacterShapePath PointLinked Shape InstanceEdit HistoryGuides

Pages:
Ligatures PageKerning PageTest Drive PageFont Settings PageProject Settings PageImport SVG PageExport Font PageAbout Page

Navigation and Layout

The Glyphr Studio work space has three vertical areas: from left to right, the Navigation Bar, the Panel, and the Main Content area. Selecting an icon in the Navigation Bar will update the Panel and the Main Content areas. Pressing the Glyphr Studio logo in the upper left part of the Navigation Bar displays all the main pages in the Panel, allowing you to navigate around Glyphr Studio.

Some of the pages have additional Navigation Bar icons - like Attributes, Layers, and Character Selection - that are specific to character editing, or other activities.

The bottom-most icon in the Navigation Bar is a save icon - it does not actually navigate anywhere, but instead is just an omnipresent shortcut to save your Glyphr Project. When there are changes that have not been saved, the save icon becomes slightly highlighted, and a diamond icon ❖ will be added to the browser title.

back to top

Common Character Editing Pages

Character Edit, Ligatures, and Linked Shapes pages have many shape editing controls in common. Linked Shapes are single outlines that can be inserted into many characters of a font. Updating the Linked Shape will also update all the Linked Shape instances. Characters and Ligatures in your font can have many shapes (both linked and not), so there is an added concept of Layers on the Character Edit and Ligatures pages.

Linked Shapes are individual shapes that can be added to many Characters or Ligatures. Any time the Linked Shape is edited, all the Characters or Ligatures that use that Linked Shape are updated. There are many individual letter forms that are shared across characters in a single font - for example the round o form of the letters b,d,g,o,p,q. Linked Shapes were designed to make it easy to keep similar letter forms consistent across a font.

Creating new Linked Shapes, and adding a Linked Shape to a character can be done through the Actions list in the Attributes Panel. The Linked Shapes page is very similar to the Character Edit page. Each Linked Shape is just a single Shape, so there are no shape layers, or add shape buttons. The Linked Shapes page also displays all the shapes that use the current Linked Shape - they are displayed as a thumbnail in the lower left, and update as the Linked Shape is edited. Clicking on one of these thumbnails will navigate to the Character Edit or Ligature page for that character.

back to top

Shape Editing

Shape editing concepts can be found on the Character Edit, Ligatures, and Linked Shapes pages. Shape and Path attributes, along with metadata, can be edited either in the Attributes Panel, or interactively using the Edit Canvas in the Content Area.

Many of these attributes can be locked by selecting the small lock icon to the left of the attribute value. This will stop this attribute from accidentally being changed, and in most cases, will limit what can be done on the Edit Canvas.

Unless otherwise noted, most attributes are in Em units.

back to top

Edit Canvas Tools

In the Upper Left hand side of the Edit Canvas there are 12 tools. The currently selected tool has a blue background. The tools, from top to bottom, are:

  • Dual Screen Mode - the Edit Canvas can be torn out into its own window, and all the Panels are laid out side-by-side.
  • 1:1 Button - Sets the Zoom level where 1 Pixel=1 Em Unit.
  • M Button (Em Square Button) - Sets the Zoom level to display the entire Em square area
  • Zoom In / Zoom Out - The plus / minus buttons will increase or decrease the size of the Edit Canvas.
    Keyboard Shortcut: Ctrl+ or Ctrl-
  • Zoom Percent - Displays the current zoom level (read only).
  • New Rectangle / New Oval - Click and drag to draw new Rectangles or Ovals.
  • New Path - Draws a new path. Single clicking will create Path Points that do not have handles. Clicking and dragging will add a Path Point where the click began, and also a symmetrical handle where the mouse drag ends.
  • Pan Tool - Moves the canvas up/down/left/right.
    Keyboard Shortcut: Spacebar
  • Point Select - Selects and edits individual Path Points and Handles. It cannot move or resize shapes.
  • Shape Select - Selects, moves, and resizes shapes. It cannot edit the individual path points.
back to top

Keyboard Shortcuts

"+makeKeyboardShortcutsTable()+" back to top

Attributes Panel - Character

(Character Edit and Ligatures Pages)

When no shape is selected, the Attributes Panel shows attributes for the currently selected Character.

Bulk-transform character shapes

If there are more than one shape in the character, there are position and dimension controls that will change all the shapes in the character.

  • X Position - the farthest-left position of the character
  • Y Position - the most-top position of the character
  • Width - the overall width of the character
  • Height - the overall height of the character
  • Lock Aspect Ratio - this will force width and height changes to happen proportionally
  • Flip Vertical / Flip Horizontal - flip the entire character

Width Metrics

These metrics are used for exporting a font file

  • Character Width - by default, Glyphr Studio automatically calculates the width of a character. By unchecking the 'automatically calculate' checkbox, you can manually set the character width.
  • Left and Right Side Bearing - This is a small amount of space that is automatically added before and after all characters. There is a global default that is applied to all characters, but if you want to specify a custom side bearing for a certain character, uncheck the 'use default' checkbox here.
back to top

Attributes Panel - Shape

(Character Edit, Ligatures, and Linked Shape Pages)

  • Name - Any name you want to give this shape.
  • Shape X / Shape Y - The shape's coordinates, as defined by the blue bounding box's upper left corner. These attributes are set in Em Units. These attributes are lockable.
  • Height / Width - Overall size dimensions of the shape. These attributes are set in Em Units. These attributes are lockable.
  • Overlap Mode - The clockwise or counterclockwise direction of Path Points along a Path determine if that path will additively or subtractively overlap with other Shapes in the character.
back to top

Attributes Panel - Path Point

(Character Edit, Ligatures, and Linked Shape Pages)

  • Selected Point - Displays which point is currently selected. 0 is the first Path Point.
  • Point Type - Each Path Point has two handles that control the curve of the path before and after it. There are three kinds of Point Types: Corner (Handles can be anywhere), Flat (Handles will be in line with each other), and Symmetric (Handles will be in-line with each other and equidistant from the Path Point).
  • Point X / Point Y - The Path Point's coordinates. These attributes are set in Em Units. These attributes are lockable.
  • Use Handle 1 / Use Handle 2 - Deselecting the checkbox by the handle title will remove the handle, such that it will not impact the curvature of the path. These options are only available for Flat and Corner point types.
  • Handle 1 X/Y & Handle 2 X/Y - The Handle's coordinates. These attributes are set in Em Units. These attributes are lockable.
  • Handle 1 Angle / Handle 2 Angle - read-only degree of the handle away from an imaginary horizontal line drawn through the point. Zero degrees is to the left, 180 degrees to the right.
back to top

Attributes Panel - Linked Shape Instance

(Linked Shape Page)

  • Name - Any name you want to give this Linked Shape Instance. Can be different than the Linked Shape name itself.
  • Use Linked Shape Position - By default, a Linked Shape Instance is locked to the position of the Linked Shape.  Unchecking this option will give you the option to move the Linked Shape Instance.
  • ΔX / ΔY - When Use Linked Shape Position is unselected, these coordinates are used to move the Linked Shape Instance to a new location. These are delta values, meaning they are relative to the original Linked Shape.
  • Linked Shape Name - A read-only value of the Linked Shape that this Linked Shape Instance is linked to.
  • Edit This Linked Shape (action button) - This will navigate to the Linked Shapes page, and allow you to make changes to the original Linked Shape.
back to top

Edit History Panel

(Character Edit, Ligatures, Linked Shapes, and Kerning Pages)

This panel shows a list of edits that are available to undo. There is a small description of the action, along with a timestamp, and these actions are grouped by Character name. Pressing the 'Undo' button, or Ctrl+z will reverse edits that are listed here.

Character Edit, Ligatures, and Linked Shapes pages all have separate undo lists. The Edit History Panel will only show edits that have taken place on that particular page, not across Glyphr Studio as a whole.

back to top

Guides Panel

(Character Edit, Ligatures, Linked Shapes, and Kerning Pages)

The Guides Panel shows many options for viewing and specifying grids and guides. In general, each guide line is displayed with the following properties (from left to right):

  • Guide Color - for custom guides click the colored box to choose a new color for this guide. System guides do not have editable colors.
  • View Checkbox - check or uncheck the checkbox to view / hide that particular guide.
  • Guide Type - guides are either horizontal or vertical. For custom guides, click to toggle between vertical and horizontal. System guides cannot toggle type.
  • Guide Name - a name for this guide that is displayed as a label on the edit canvas. System Guides do not have editable names.
  • Guide Location - A number, in Em units, that shows where the guide is located on the edit canvas. System Guides do not have editable locations.
  • Delete Guide - a small 'x' button on the very right of custom guides allows you to delete that guide. System guides cannot be deleted.

Options

These toggles show and hide some global aspects of the grid and guides.

System Guides

These guides reflect key metrics of the font. Their values cannot be edited from the Guides Panel - to edit their values, go to the Font Properties page.

Custom Guides

These guides are defined by you, and can be in any location you specify.

back to top

Ligatures Page

Ligatures are when two or more characters get combined into one glyph. Generically, when you define a ligature, two or more characters are specified, and instead of showing those characters when a user is typing, those characters get replaced with a single new character.

Some common lowercase ligatures involve combining the crossbars of characters like f and t, forming ligatures like ff and ſt. A common uppercase ligature is combining the characters A and E into the ligature Æ. Ligatures can also be used for any other sequence replacement, for example fractions: the three characters 1 and / and 4 could be turned into the ligature ¼.

Creating a new ligature is as easy as specifying two or more characters to combine. Designing the resulting character uses exactly the same tools and processes as designing regular characters on the Character Edit page.

back to top

Kerning Page

Kerning allows you to adjust the distance between two specific characters, or two sets of characters. Many characters look odd if they appear together using default spacing - for example, A and V. The space between these two characters is usually reduced with kerning so they 'fit' better together.

The left or right side of the kern pair can also be a group of letters. For instance, you may decide to kern the letter A on the left with V, W, and Y on the right. This effectively creates three individual kern pairs: AV, AW, and AY.

The Kerning Pairs attributes panel shows a list of all the existing kern pairs. The blue rectangle on the left of each line shows which one is currently selected. Specific kern values can be entered in the input on the right of each line, or by clicking and dragging on the edit canvas itself. There is a 'x' button on the far right of each line to delete that kern pair.

back to top

Test Drive Page

The Test Drive page is where your font can be tried out in real time. Typing in the upper textbox will display that same text in your font face in the lower box.

In the Attributes Panel, there is a list of Pangram buttons that will populate the upper textbox with sentences that contain all the letters of the alphabet. Similarly, there are buttons that will populate the upper textbox with certain character sets.

The Options area lets you change how your font is drawn to the lower box, including options for Font Size, Line Spacing, and Character Spacing.

The 'Generate PNG File' button will launch a new tab with an image of whatever is displayed in the lower box. Right-click the image to save the PNG file.

back to top

Font Settings Page

Font Name will be used when exporting your font.

Character Proportions deal with global attributes that affect your font. Glyphr Studio uses the properties in the Font Settings section to display and edit shapes:

  • Key Metrics - Characters have a total height of 1000 Units, called Em Units. The baseline of that character splits the upper and lower portions of the character vertically. Input an Ascent Height, and the Descent Height will be calculated automatically. X Height and Cap Height are common guides for lowercase and uppercase letters in your font. Note: these metrics may be used to scale imported SVG outlines.
  • Default Side Bearing - All characters have a small amount of space to their left and right that separates them from another characters. Individual Side Bearings can be edited in the Attributes Panel of that character. But, to make things easier, if a specific Left or Right Side Bearing is not set, this Default Side Bearing will be applied.

Grids and Guides

  • Grid System - Divide your Em square into a specified number of sub-units. This will show up as a light gray grid behind your character editing canvas.
  • Overshoot - Round characters are usually slightly larger than their square counterparts so that they visually look the same size. Specify an overshoot measurement here. Note: this metric may be used to scale imported SVG outlines.

Character Ranges

  • Standard Character Ranges are a list of the four basic Latin character ranges that can be specified for fonts. These common ranges can be enabled or disabled with a checkbox, and their Unicode range values will automatically be added to your Glyphr Studio project.
  • Custom Character Ranges can also be added to projects. If you want to design characters that don't fall into the common Latin character ranges above, you can specify a new range here. You'll have to look up the starting and ending Unicode values for the range you want to work with.

Font Metadata is a set of information that describes your font in various ways, and specifies some extra options - like underline and strikethrough attributes. These attributes are exported with your SVG font.

back to top

Project Settings Page

This information does not necessarily have a direct effect on the Font itself, but is used to help with designing your font. This information is saved when a Glyphr Project is saved, and will be imported when a saved project is loaded.

  • Project Name - Initially, this project name is used as the Font Name as well. But, the Glyphr Project can have a different name from the Font itself, which can be edited here.
  • UI Behavior - this is a set of options that you can adjust that will change how some UI controls behave.
back to top

Import SVG Page

Importing

There are many pieces of design software that allow you to create SVG outlines. For example, Adobe Illustrator, or its open-source equivalent Inkscape. However you create the vector outlines of your characters, you can import them via SVG on this page.

This page is for importing individual outlines to specific characters. Importing whole SVG fonts can be done from the initial Open Project page.

Importing outlines happens one character at a time, and could be accomplished thusly:

  • Select a character in the selection pane on the left. This will be the destination for your imported outlines.
  • Specify scaling and moving options. Many times your design software coordinate system setup won't match Glyphr Studio's coordinate system. Enter the specific height metrics for this character (Does it go above the x-height line, or below the baseline? Is it a rounded character that has overshoot?) then select Scale and Move options. Your character outlines will be re-sized and moved to the correct location.
  • If your file is a single character, you can drag and drop it onto the area specified. Otherwise, copy and paste the SVG code into the code box. The importing will not happen until the Import SVG button is pressed, so you can double-check your code before you proceed.
  • Pressing the Import SVG button will parse the SVG code, translate it into Glyphr Studio shapes and paths, and insert it into the specified character. There is also a button to jump to the Character Edit page for the selected character, and a button to clear the code box.

Notes about SVG

SVG is a huge language that is capable of defining a much larger set of graphic design concepts than are needed for font glyph design. As a result, Glyphr Studio ignores much of the SVG code that gets imported.

What Glyphr Studio imports:

  • Outline data from the following tags: path, rect, polygon, polyline, circle, and ellipse.
  • The following commands from the 'd' attribute in the path tag: Move To (Mm), Line To (Ll), Horizontal Line To (Hh), Vertical Line To (Vv), Bezier Line To (Cc), Smooth Bezier Line To (Ss), Quadratic Bezier Line To (Qq), Smooth Quadratic Bezier Line To (Tt) Close Path (Zz).

What Glyphr Studio ignores:

  • The 'line' tag. And, basically any tag not mentioned in the previous section.
  • All stroke attributes, like stroke-width. Paths are assumed to be outlines of shapes.
  • The Arc To (Aa) path commands in the 'd' attribute
  • The 'g' tag, and all its attributes. Any transformations are ignored.
  • The 'svg' tag itself. ViewBox, x, y, width, height attributes are all ignored.

Things to keep in mind:

  • All lines or paths will be closed by Glyphr Studio. AKA: the last point in any path will be connected with the first point.
  • Path direction (or 'winding') is not important in SVG, but it is important in glyph design. Winding determines if two overlapping shapes both appear filled, or if one 'cuts out' from the other. This can be toggled in the Shape Attributes Panel with the 'overlap mode' attribute.
  • Adobe Illustrator artboards don't get exported to SVG. If you're importing from Illustrator, and not using the Scale option in Glyphr Studio, then imported shapes may end up in a surprising location, like way outside the Em square.
  • Some rectangles that have been rotated in Adobe Illustrator export as SVG rectangles with a transform applied (which Glyphr Studio ignores). You can remedy this by right-clicking the rotated rectangle in Illustrator, and selecting “Make Compound Path”.
back to top

Export Font Page

For Beta 5, Glyphr Studio can export an SVG font straight from your browser. In Beta 6, Glyphr Studio will be able to export Open Type files (.otf), and possibly some other formats.  Luckily, there are some free services online that will allow you to convert a SVG Font to whatever format you'd like. So, for now, export a SVG Font and convert it to a 'real' font elsewhere. We're working on making this more streamlined in the future.

TTX support was deprecated in Beta 4, and removed in Beta 5 - if you super <3 Glyphr Studio plus TTX, you'll have to stick with Beta 4. Them's the breaks.

back to top

About Page

Displays various information about the currently loaded Glyphr Project, and the current Glyphr Studio being used. Glyphr Projects created with different versions of Glyphr Studio may not play nicely together.

Please consider making a contribution to the Glyphr Studio effort. Glyphr Studio is, and will always, be totally free... but it does take some cash money to keep it going.

If you are a lovely individual who has already contributed, thank you! You are awesome!

",a+="
",getEditDocument().getElementById("mainwrapper").innerHTML=a