diff --git a/data/default.css b/data/default.css index bae96a6..819219a 100644 --- a/data/default.css +++ b/data/default.css @@ -29,9 +29,6 @@ * Only one family : no comma separated list of families like "font1, font2, ..., Sans-Serif". * Font family can be any font installed, or a generic family name (Serif, Sans-Serif, Monospace, Cursive, Fantasy). * Font weight and font style : no upper case when string. - * Weight <= 500 (or lighter, normal, medium) is rendered as normal. - * Weight > 500 (or bolder, bold) is rendered as bold. - * Oblique and italic style supports depend on the font family and seem to be rendered identically. * */ diff --git a/draw.js b/draw.js index a4d79dd..ad076eb 100644 --- a/draw.js +++ b/draw.js @@ -28,7 +28,8 @@ const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; const Lang = imports.lang; -const PangoMatrix = imports.gi.Pango.Matrix; +const Pango = imports.gi.Pango; +const PangoCairo = imports.gi.PangoCairo; const St = imports.gi.St; const BoxPointer = imports.ui.boxpointer; @@ -63,17 +64,25 @@ const FILLRULE_EVENODD_ICON_PATH = ICON_DIR.get_child('fillrule-evenodd-symbolic const DASHED_LINE_ICON_PATH = ICON_DIR.get_child('dashed-line-symbolic.svg').get_path(); const FULL_LINE_ICON_PATH = ICON_DIR.get_child('full-line-symbolic.svg').get_path(); +const reverseEnumeration = function(obj) { + return Object.fromEntries(Object.entries(obj).map(entry => [entry[1], entry[0].slice(0,1) + entry[0].slice(1).toLowerCase()])); +}; + const Shapes = { NONE: 0, LINE: 1, ELLIPSE: 2, RECTANGLE: 3, TEXT: 4, POLYGON: 5, POLYLINE: 6 }; const Manipulations = { MOVE: 100, RESIZE: 101, MIRROR: 102 }; var Tools = Object.assign({}, Shapes, Manipulations); const Transformations = { TRANSLATION: 0, ROTATION: 1, SCALE_PRESERVE: 2, STRETCH: 3, REFLECTION: 4, INVERSION: 5 }; const ToolNames = { 0: "Free drawing", 1: "Line", 2: "Ellipse", 3: "Rectangle", 4: "Text", 5: "Polygon", 6: "Polyline", 100: "Move", 101: "Resize", 102: "Mirror" }; -const LineCapNames = { 0: 'Butt', 1: 'Round', 2: 'Square' }; -const LineJoinNames = { 0: 'Miter', 1: 'Round', 2: 'Bevel' }; +const LineCapNames = reverseEnumeration(Cairo.LineCap); +const LineJoinNames = reverseEnumeration(Cairo.LineJoin); const FillRuleNames = { 0: 'Nonzero', 1: 'Evenodd' }; -const FontWeightNames = { 0: 'Normal', 1: 'Bold' }; -const FontStyleNames = { 0: 'Normal', 1: 'Italic', 2: 'Oblique' }; -const FontFamilyNames = { 0: 'Theme', 1: 'Sans-Serif', 2: 'Serif', 3: 'Monospace', 4: 'Cursive', 5: 'Fantasy' }; +const FontGenericNames = { 0: 'Theme', 1: 'Sans-Serif', 2: 'Serif', 3: 'Monospace', 4: 'Cursive', 5: 'Fantasy' }; +const FontStyleNames = reverseEnumeration(Pango.Style); +const FontWeightNames = Object.assign( + reverseEnumeration(Pango.Weight), + { 200: "Ultra-light", 350: "Semi-light", 600: "Semi-bold", 800: "Ultra-bold" } +); +delete FontWeightNames[Pango.Weight.ULTRAHEAVY]; const getDateString = function() { let date = GLib.DateTime.new_now_local(); @@ -136,7 +145,7 @@ var DrawingArea = new Lang.Class({ this.undoneElements = []; this.currentElement = null; this.currentTool = Shapes.NONE; - this.currentFontFamilyId = 0; + this.currentFontGeneric = 0; this.isSquareArea = false; this.hasGrid = false; this.hasBackground = false; @@ -191,7 +200,7 @@ var DrawingArea = new Lang.Class({ } let font = themeNode.get_font(); this.newThemeAttributes.ThemeFontFamily = font.get_family(); - this.newThemeAttributes.FontWeight = font.get_weight(); + try { this.newThemeAttributes.FontWeight = font.get_weight(); } catch(e) { this.newThemeAttributes.FontWeight = Pango.Weight.NORMAL; } this.newThemeAttributes.FontStyle = font.get_style(); this.newThemeAttributes.LineWidth = themeNode.get_length('-drawing-line-width'); this.newThemeAttributes.LineJoin = themeNode.get_double('-drawing-line-join'); @@ -214,13 +223,12 @@ var DrawingArea = new Lang.Class({ this.colors[i] = this.colors[i].alpha ? this.colors[i] : this.colors[0]; } this.currentColor = this.currentColor || this.colors[1]; + // SVG does not support 'Ultra-heavy' weight (1000) + this.newThemeAttributes.FontWeight = Math.min(this.newThemeAttributes.FontWeight, 900); this.newThemeAttributes.LineWidth = (this.newThemeAttributes.LineWidth > 0) ? this.newThemeAttributes.LineWidth : 3; this.newThemeAttributes.LineJoin = ([0, 1, 2].indexOf(this.newThemeAttributes.LineJoin) != -1) ? this.newThemeAttributes.LineJoin : Cairo.LineJoin.ROUND; this.newThemeAttributes.LineCap = ([0, 1, 2].indexOf(this.newThemeAttributes.LineCap) != -1) ? this.newThemeAttributes.LineCap : Cairo.LineCap.ROUND; this.newThemeAttributes.FillRule = ([0, 1].indexOf(this.newThemeAttributes.FillRule) != -1) ? this.newThemeAttributes.FillRule : Cairo.FillRule.WINDING; - this.newThemeAttributes.FontWeight = this.newThemeAttributes.FontWeight > 500 ? 1 : 0 ; - // font style enum order of Cairo and Pango are different - this.newThemeAttributes.FontStyle = this.newThemeAttributes.FontStyle == 2 ? 1 : ( this.newThemeAttributes.FontStyle == 1 ? 2 : 0); for (const attributeName in this.newThemeAttributes) { if (this.newThemeAttributes[attributeName] != this.oldThemeAttributes[attributeName]) { this.oldThemeAttributes[attributeName] = this.newThemeAttributes[attributeName]; @@ -559,13 +567,13 @@ var DrawingArea = new Lang.Class({ fillRule: this.currentFillRule, eraser: eraser, transform: { active: false, center: [0, 0], angle: 0, startAngle: 0, ratio: 1 }, - text: '', - font: { family: (this.currentFontFamilyId == 0 ? this.currentThemeFontFamily : FontFamilyNames[this.currentFontFamilyId]), weight: this.currentFontWeight, style: this.currentFontStyle }, points: [] }); if (this.currentTool == Shapes.TEXT) { this.currentElement.fill = false; + this.currentElement.font = { family: (this.currentFontGeneric == 0 ? this.currentThemeFontFamily : FontGenericNames[this.currentFontGeneric]), + weight: this.currentFontWeight, style: this.currentFontStyle }; this.currentElement.text = _("Text"); this.currentElement.rtl = ENABLE_RTL && this.get_text_direction() == Clutter.TextDirection.RTL; } @@ -869,17 +877,19 @@ var DrawingArea = new Lang.Class({ }, toggleFontWeight: function() { - this.currentFontWeight = this.currentFontWeight == 1 ? 0 : this.currentFontWeight + 1; - if (this.currentElement) { + let fontWeights = Object.keys(FontWeightNames).map(key => Number(key)); + let index = fontWeights.indexOf(this.currentFontWeight); + this.currentFontWeight = index == fontWeights.length - 1 ? fontWeights[0] : fontWeights[index + 1]; + if (this.currentElement && this.currentElement.font) { this.currentElement.font.weight = this.currentFontWeight; this._redisplay(); } - this.emit('show-osd', null, `${_(FontWeightNames[this.currentFontWeight])}`, "", -1); + this.emit('show-osd', null, `${_(FontWeightNames[this.currentFontWeight])}`, "", -1); }, toggleFontStyle: function() { this.currentFontStyle = this.currentFontStyle == 2 ? 0 : this.currentFontStyle + 1; - if (this.currentElement) { + if (this.currentElement && this.currentElement.font) { this.currentElement.font.style = this.currentFontStyle; this._redisplay(); } @@ -887,9 +897,9 @@ var DrawingArea = new Lang.Class({ }, toggleFontFamily: function() { - this.currentFontFamilyId = this.currentFontFamilyId == 5 ? 0 : this.currentFontFamilyId + 1; - let currentFontFamily = this.currentFontFamilyId == 0 ? this.currentThemeFontFamily : FontFamilyNames[this.currentFontFamilyId]; - if (this.currentElement) { + this.currentFontGeneric = this.currentFontGeneric == 5 ? 0 : this.currentFontGeneric + 1; + let currentFontFamily = this.currentFontGeneric == 0 ? this.currentThemeFontFamily : FontGenericNames[this.currentFontGeneric]; + if (this.currentElement && this.currentElement.font) { this.currentElement.font.family = currentFontFamily; this._redisplay(); } @@ -1187,8 +1197,14 @@ const DrawingElement = new Lang.Class({ this.fillRule = Cairo.FillRule.WINDING; if (params.transformations === undefined) this.transformations = []; - if (params.shape == Shapes.TEXT && params.rtl === undefined) - this.rtl = false; + if (params.shape == Shapes.TEXT) { + if (params.rtl === undefined) + this.rtl = false; + if (params.font && params.font.weight === 0) + this.font.weight = 400; + if (params.font && params.font.weight === 1) + this.font.weight = 700; + } if (params.transform && params.transform.center) { let angle = (params.transform.angle || 0) + (params.transform.startAngle || 0); @@ -1328,44 +1344,32 @@ const DrawingElement = new Lang.Class({ cr.closePath(); } else if (shape == Shapes.TEXT && points.length == 2) { - cr.selectFontFace(this.font.family, this.font.style, this.font.weight); - cr.setFontSize(Math.abs(points[1][1] - points[0][1])); - - let textWidth = 0; - - if (this.rtl) { - cr.save(); - cr.setSourceRGBA(0, 0, 0, 0); - cr.setOperator(Cairo.Operator.OVER); - cr.moveTo(points[1][0], Math.max(points[0][1], points[1][1])); - cr.showText(this.text); - textWidth = cr.getCurrentPoint()[0] - points[1][0]; - cr.restore(); - } + let layout = PangoCairo.create_layout(cr); + let fontSize = Math.abs(points[1][1] - points[0][1]) * Pango.SCALE; + let fontDescription = new Pango.FontDescription(); + fontDescription.set_family(this.font.family); + fontDescription.set_style(this.font.style); + fontDescription.set_weight(this.font.weight); + fontDescription.set_absolute_size(fontSize); + layout.set_font_description(fontDescription); + layout.set_text(this.text, -1); + this.textWidth = layout.get_pixel_size()[0]; + cr.moveTo(points[1][0] - (this.rtl ? this.textWidth : 0), Math.max(points[0][1],points[1][1]) - layout.get_baseline() / Pango.SCALE); + layout.set_text(this.text, -1); + PangoCairo.show_layout(cr, layout); if (params.showTextCursor) { let cursorPosition = this.cursorPosition == -1 ? this.text.length : this.cursorPosition; - let texts = [this.text.slice(0, cursorPosition), this.text.slice(cursorPosition)]; - cr.moveTo(points[1][0] - textWidth, Math.max(points[0][1], points[1][1])); - cr.showText(texts[0]); - let currentPoint1 = cr.getCurrentPoint(); - cr.rectangle(currentPoint1[0], currentPoint1[1], Math.abs(points[1][1] - points[0][1]) / 25, - Math.abs(points[1][1] - points[0][1])); + layout.set_text(this.text.slice(0, cursorPosition), -1); + let width = layout.get_pixel_size()[0]; + cr.rectangle(points[1][0] - (this.rtl ? this.textWidth : 0) + width, Math.max(points[0][1],points[1][1]), + Math.abs(points[1][1] - points[0][1]) / 25, - Math.abs(points[1][1] - points[0][1])); cr.fill(); - cr.moveTo(currentPoint1[0], currentPoint1[1]); - cr.showText(texts[1]); - textWidth = this.rtl ? textWidth : (cr.getCurrentPoint()[0] - points[1][0]); - } else { - cr.moveTo(points[1][0] - textWidth, Math.max(points[0][1], points[1][1])); - cr.showText(this.text); - textWidth = this.rtl ? textWidth : (cr.getCurrentPoint()[0] - points[1][0]); } - if (!params.showTextCursor) - this.textWidth = textWidth; - if (params.showTextRectangle || params.drawTextRectangle) { - cr.rectangle(points[1][0] - (this.rtl ? textWidth : 0), Math.max(points[0][1], points[1][1]), - textWidth, - Math.abs(points[1][1] - points[0][1])); + cr.rectangle(points[1][0] - (this.rtl ? this.textWidth : 0), Math.max(points[0][1], points[1][1]), + this.textWidth, - Math.abs(points[1][1] - points[0][1])); if (params.showTextRectangle) setDummyStroke(cr); else @@ -1490,7 +1494,7 @@ const DrawingElement = new Lang.Class({ `stroke-opacity="0" ` + `font-family="${this.font.family}" ` + `font-size="${Math.abs(points[1][1] - points[0][1])}" ` + - `font-weight="${FontWeightNames[this.font.weight].toLowerCase()}" ` + + `font-weight="${this.font.weight}" ` + `font-style="${FontStyleNames[this.font.style].toLowerCase()}"`; row += ` { for (let i in obj) { let text; - if (targetProperty == 'currentFontFamilyId') + if (targetProperty == 'currentFontGeneric') text = `${_(obj[i])}`; else if (targetProperty == 'currentFontWeight') - text = `${_(obj[i])}`; + text = `${_(obj[i])}`; else if (targetProperty == 'currentFontStyle') text = `${_(obj[i])}`; else diff --git a/locale/draw-on-your-screen.pot b/locale/draw-on-your-screen.pot index 7a2bec5..fc3702a 100644 --- a/locale/draw-on-your-screen.pot +++ b/locale/draw-on-your-screen.pot @@ -436,12 +436,39 @@ msgstr "" #msgid "Evenodd" #msgstr "" +#msgid "Thin" +#msgstr "" + +#msgid "Ultra-light" +#msgstr "" + +#msgid "Light" +#msgstr "" + +#msgid "Semi-light" +#msgstr "" + +#msgid "Book" +#msgstr "" + #msgid "Normal" #msgstr "" +#msgid "Medium" +#msgstr "" + +#msgid "Semi-bold" +#msgstr "" + #msgid "Bold" #msgstr "" +#msgid "Ultra-bold" +#msgstr "" + +#msgid "Heavy" +#msgstr "" + #msgid "Italic" #msgstr ""