The Birdfont Source Code


All Repositories / birdfont.git / commit – RSS feed

Merge branch 'master' of github.com:johanmattssonm/birdfont

These changes was commited to the Birdfont repository Sat, 05 Aug 2017 16:11:01 +0000.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git
author Johan Mattsson <johan.mattsson.m@gmail.com>
Sat, 05 Aug 2017 16:11:01 +0000 (18:11 +0200)
committer Johan Mattsson <johan.mattsson.m@gmail.com>
Sat, 05 Aug 2017 16:11:01 +0000 (18:11 +0200)
commit 1f89b20a1881b8b9b66a0a9569e008cf7fed060a
tree c7cb975932ce9ea35c999b7886e8ef3466acfe41
parent 642585441f419855e2a03843709413675401c91c
parent cda8525e8cf56bae219d0b122f6ea5e18073803d
Merge branch 'master' of github.com:johanmattssonm/birdfont

232 files changed:
.gitignore
.travis.yml [deleted ]
NEWS
README.md
birdfont-test/TestRunner.vala
birdfont/GtkWindow.vala
birdfont/Main.vala
birdui/BoxLayout.vala [deleted ]
birdui/Component.vala [deleted ]
birdui/GtkWidget.vala [deleted ]
birdui/GtkWindow.vala [deleted ]
birdui/HBox.vala [deleted ]
birdui/Overflow.vala [deleted ]
birdui/Style.vala [deleted ]
birdui/SvgComponent.vala [deleted ]
birdui/VBox.vala [deleted ]
build.py
configure
dodo.py
install.py
libbirdfont/AbstractMenu.vala
libbirdfont/AlternateSets.vala
libbirdfont/BackgroundImage.vala
libbirdfont/BackgroundTab.vala
libbirdfont/BackgroundTool.vala
libbirdfont/BackgroundTools.vala
libbirdfont/BezierPoints.vala [new ]
libbirdfont/BezierTool.vala
libbirdfont/BirdFont.vala
libbirdfont/BirdFontFile.vala
libbirdfont/CanvasSettings.vala [deleted ]
libbirdfont/CharDatabase.vala
libbirdfont/CharDatabaseParser.vala
libbirdfont/ClipTool.vala
libbirdfont/Color.vala
libbirdfont/ColorPicker.vala
libbirdfont/DrawingTools.vala
libbirdfont/EditPoint.vala
libbirdfont/EditPointHandle.vala
libbirdfont/EmbeddedSvg.vala [deleted ]
libbirdfont/EmptyTab.vala
libbirdfont/Expander.vala
libbirdfont/ExportSettings.vala
libbirdfont/ExportTool.vala
libbirdfont/FileDialogTab.vala
libbirdfont/Font.vala
libbirdfont/FontDisplay.vala
libbirdfont/Glyph.vala
libbirdfont/GlyphCanvas.vala
libbirdfont/GlyphMaster.vala
libbirdfont/GlyphRange.vala
libbirdfont/GlyphSelection.vala
libbirdfont/GlyphSequence.vala
libbirdfont/GlyphTab.vala [new ]
libbirdfont/Gradient.vala [new ]
libbirdfont/Help.vala [deleted ]
libbirdfont/HiddenTools.vala
libbirdfont/ImportUtils.vala
libbirdfont/KerningClasses.vala
libbirdfont/KerningDisplay.vala
libbirdfont/KerningRange.vala
libbirdfont/KerningTools.vala
libbirdfont/LabelTool.vala
libbirdfont/Layer.vala [new ]
libbirdfont/LayerLabel.vala
libbirdfont/LayerUtils.vala [deleted ]
libbirdfont/LicenseDialog.vala
libbirdfont/Ligatures.vala
libbirdfont/LoadCallback.vala
libbirdfont/MainWindow.vala
libbirdfont/Menu.vala
libbirdfont/MenuItem.vala
libbirdfont/MenuTab.vala
libbirdfont/MoveTool.vala
libbirdfont/NativeWindow.vala
libbirdfont/OpenFontFormat/AlternateFeature.vala
libbirdfont/OpenFontFormat/CodePageBits.vala
libbirdfont/OpenFontFormat/ContextualLigature.vala
libbirdfont/OpenFontFormat/DirectoryTable.vala
libbirdfont/OpenFontFormat/FontData.vala
libbirdfont/OpenFontFormat/GlyfData.vala
libbirdfont/OpenFontFormat/GlyfTable.vala
libbirdfont/OpenFontFormat/GposTable.vala
libbirdfont/OpenFontFormat/HheaTable.vala
libbirdfont/OpenFontFormat/HmtxTable.vala
libbirdfont/OpenFontFormat/KernList.vala
libbirdfont/OpenFontFormat/KernSplitter.vala [new ]
libbirdfont/OpenFontFormat/KerningPair.vala
libbirdfont/OpenFontFormat/OpenFontFormatReader.vala
libbirdfont/OpenFontFormat/OpenFontFormatWriter.vala
libbirdfont/OpenFontFormat/OtfInputStream.vala
libbirdfont/OpenFontFormat/OtfTable.vala
libbirdfont/OpenFontFormat/PostTable.vala
libbirdfont/OpenFontFormat/SvgTable.vala [deleted ]
libbirdfont/OpenFontFormat/SvgTableEntry.vala [deleted ]
libbirdfont/OrientationTool.vala
libbirdfont/OtfFeatureTable.vala
libbirdfont/OverView.vala
libbirdfont/OverViewItem.vala
libbirdfont/OverviewTools.vala
libbirdfont/Path.vala
libbirdfont/PathList.vala
libbirdfont/PathObject.vala [deleted ]
libbirdfont/PenTool.vala
libbirdfont/PointConverter.vala
libbirdfont/PointTool.vala
libbirdfont/Preferences.vala
libbirdfont/Preview.vala
libbirdfont/PreviewTools.vala
libbirdfont/QuestionDialog.vala
libbirdfont/RecentFiles.vala
libbirdfont/Renderer/CachedFont.vala [new ]
libbirdfont/Renderer/Drawing.vala [new ]
libbirdfont/Renderer/FallbackFont.vala [new ]
libbirdfont/Renderer/FontCache.vala [new ]
libbirdfont/Renderer/LineTextArea.vala [new ]
libbirdfont/Renderer/Text.vala [new ]
libbirdfont/Renderer/TextArea.vala [new ]
libbirdfont/Renderer/fontconfig.c [new ]
libbirdfont/ResizeTool.vala
libbirdfont/SaveCallback.vala
libbirdfont/Scrollbar.vala [new ]
libbirdfont/SearchPaths.vala
libbirdfont/SettingsTab.vala
libbirdfont/SpacingClass.vala
libbirdfont/SpacingClassTab.vala
libbirdfont/SpacingClassTools.vala [new ]
libbirdfont/SpacingData.vala
libbirdfont/SpacingTab.vala
libbirdfont/SpinButton.vala
libbirdfont/Stop.vala [new ]
libbirdfont/StrokeTool.vala
libbirdfont/Svg.vala
libbirdfont/SvgArc.vala [new ]
libbirdfont/SvgFont.vala
libbirdfont/SvgFontFormatWriter.vala
libbirdfont/SvgParser.vala
libbirdfont/SvgStyle.vala [new ]
libbirdfont/TabBar.vala
libbirdfont/TabContent.vala
libbirdfont/TableLayout.vala
libbirdfont/Task.vala
libbirdfont/Test.vala
libbirdfont/TestCases.vala
libbirdfont/TextRendering/CachedFont.vala [deleted ]
libbirdfont/TextRendering/Drawing.vala [deleted ]
libbirdfont/TextRendering/FallbackFont.vala [deleted ]
libbirdfont/TextRendering/FontCache.vala [deleted ]
libbirdfont/TextRendering/LineTextArea.vala [deleted ]
libbirdfont/TextRendering/Text.vala [deleted ]
libbirdfont/TextRendering/TextArea.vala [deleted ]
libbirdfont/TextRendering/fontconfig.c [deleted ]
libbirdfont/Theme.vala
libbirdfont/ThemeTab.vala
libbirdfont/Tool.vala
libbirdfont/ToolItem.vala
libbirdfont/Toolbox.vala
libbirdfont/TrackTool.vala
libbirdfont/VersionList.vala
libbirdfont/ZoomTool.vala
libbirdgems/fit_cubic.c
libsvgbird/AttributePattern.vala [deleted ]
libsvgbird/BezierPoints.vala [deleted ]
libsvgbird/Circle.vala [deleted ]
libsvgbird/ClipPath.vala [deleted ]
libsvgbird/Color.vala [deleted ]
libsvgbird/Defs.vala [deleted ]
libsvgbird/Doubles.vala [deleted ]
libsvgbird/Ellipse.vala [deleted ]
libsvgbird/EmptyObject.vala [deleted ]
libsvgbird/Gradient.vala [deleted ]
libsvgbird/Layer.vala [deleted ]
libsvgbird/Line.vala [deleted ]
libsvgbird/LineCap.vala [deleted ]
libsvgbird/Object.vala [deleted ]
libsvgbird/ObjectGroup.vala [deleted ]
libsvgbird/PointValue.vala [deleted ]
libsvgbird/Points.vala [deleted ]
libsvgbird/Polygon.vala [deleted ]
libsvgbird/Polyline.vala [deleted ]
libsvgbird/Rectangle.vala [deleted ]
libsvgbird/Selector.vala [deleted ]
libsvgbird/SelectorPattern.vala [deleted ]
libsvgbird/SelectorTag.vala [deleted ]
libsvgbird/Stop.vala [deleted ]
libsvgbird/StyleSheet.vala [deleted ]
libsvgbird/SvgArc.vala [deleted ]
libsvgbird/SvgDrawing.vala [deleted ]
libsvgbird/SvgFile.vala [deleted ]
libsvgbird/SvgPath.vala [deleted ]
libsvgbird/SvgStyle.vala [deleted ]
libsvgbird/SvgTransform.vala [deleted ]
libsvgbird/SvgTransforms.vala [deleted ]
libsvgbird/point_value.h [deleted ]
po/birdfont.pot
po/cs.po
po/de.po
po/el.po
po/es.po
po/fi.po
po/fr.po
po/he.po
po/id.po
po/it.po
po/nb.po
po/nl.po
po/oc.po
po/pl.po
po/pt.po
po/pt_BR.po
po/ru.po
po/sk.po
po/sr.po
po/sv.po
po/tr.po
po/uk.po
resources/Roboto-Regular.ttf [new ]
resources/dark.theme
resources/icons.bf
resources/linux/birdfont-import.1
scripts/builder.py
scripts/configfile.py
scripts/release.sh
scripts/snap.py [new ]
scripts/snap.sh [new ]
scripts/snapcraft.yaml [new ]
scripts/version.py
webkit2gtk-3.0.deps [new ]
webkit2gtk-3.0.vapi [new ]
webkitgtk-3.0.deps [deleted ]
webkitgtk-3.0.vapi [deleted ]
--- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ scripts/config.py libbirdgems.vapi libbirdfont.vapi + *~
diff --git .travis.yml(deleted)
--- a/.travis.yml +++ /dev/null @@ -1,14 +1,1 @@ - language: c - compiler: gcc - before_install: - - sudo add-apt-repository ppa:vala-team/ppa -y - - sudo add-apt-repository ppa:birdfont-team/birdfont -y - - sudo apt-get update -qq - - sudo apt-get install -qq libglib2.0-dev libgtk-3-dev - libsqlite3-dev valac libxmlbird-dev libgee-dev - libwebkitgtk-3.0-dev libnotify-dev valac-0.22 - python3 - script: - - python3 ./configure --nonnull - - python3 ./scripts/linux_build.py
diff --git a/NEWS b/NEWS
--- a/NEWS +++ b/NEWS @@ -1,9 +1,44 @@ + birdfont (2.19.0) stable; + + * Huge amount of kerning pairs supported in the extended GPOS table + + -- Johan Mattsson <johan.mattsson.m@gmail.com> mån 10 okt 2016 14:38:53 CEST + + birdfont (2.16.12) stable; + + * Platform independet scrollbar + * Bug fixes + + -- Johan Mattsson <johan.mattsson.m@gmail.com> mån 10 okt 2016 14:38:53 CEST + + birdfont (2.16.9) stable; + + * RTL (right-to-left) writing in kerning tab + * Bug fixes + + -- Johan Mattsson <johan.mattsson.m@gmail.com> lör 1 okt 2016 09:38:00 CEST + + birdfont (2.16.0) stable; + + * New freehand tool + * Rotate objects and point handles with 45 degree steps + * Bug fixes + + -- Johan Mattsson <johan.mattsson.m@gmail.com> Sun Jun 12 19:53:43 CEST 2016 + + birdfont (2.15.6) stable; + + * Editable .notdef character + * Bug fixes in the SVG parser + + -- Johan Mattsson <johan.mattsson.m@gmail.com> Fri Jan 22 13:10:07 CET 2016 + birdfont (2.15.0) stable; * Speed optimizations * Bug fixes - -- Johan Mattsson <johan.mattsson.m@gmail.com Fri Dec 11 22:26:42 CET 2015 + -- Johan Mattsson <johan.mattsson.m@gmail.com> Fri Dec 11 22:26:42 CET 2015 birdfont (2.14.0) stable; @@ -11,7 +46,7 @@ * Sandbox support on Mac OS * Bug fixes - -- Johan Mattsson <johan.mattsson.m@gmail.com Thu Nov 12 20:45:02 CET 2015 + -- Johan Mattsson <johan.mattsson.m@gmail.com> Thu Nov 12 20:45:02 CET 2015 birdfont (2.13.0) stable; @@ -22,7 +57,7 @@ * Swashes (OpenType feature tag: swsh) * Parse circles, ellipses and lines in SVG files - -- Johan Mattsson <johan.mattsson.m@gmail.com Mon Oct 26 11:33:09 CET 2015 + -- Johan Mattsson <johan.mattsson.m@gmail.com> Mon Oct 26 11:33:09 CET 2015 birdfont (2.12.0) stable;
--- a/README.md +++ b/README.md @@ -9,9 +9,6 @@ License: GNU GPL v3 Webpage: http://birdfont.org Bugtracker: http://birdfont.org/bugtracker/my_view_page.php - - [![Build Status](https://travis-ci.org/johanmattssonm/birdfont.svg)] - (https://travis-ci.org/johanmattssonm/birdfont) ## Building from Source @@ -19,15 +16,14 @@ packages with a -dev or -devel affix: valac - font-roboto python3-doit - libgee-dev + libxmlbird-dev + libgee-0.8-dev libglib2.0-dev libgtk-3-dev - libwebkitgtk-3.0-dev + libwebkit2gtk-3.0-dev libnotify-dev libsqlite3-dev - libxmlbird-dev XML Bird is available from [birdfont.org][xmlbird].
--- a/birdfont-test/TestRunner.vala +++ b/birdfont-test/TestRunner.vala @@ -37,7 +37,7 @@ if (type == "SVG") { File f = File.new_for_path (file); Font font = new Font (); - import_svg_file (font, f, SvgType.REGULAR); + import_svg_file (font, f); } else if (type == "BF") { Font font = new Font (); font.set_font_file (file);
--- a/birdfont/GtkWindow.vala +++ b/birdfont/GtkWindow.vala @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 2013 2014 2015 Johan Mattsson + Copyright (C) 2012s 2013 2014 Johan Mattsson This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -40,24 +40,12 @@ Clipboard clipboard; string clipboard_svg = ""; string inkscape_clipboard = ""; - - Scrollbar scrollbar; - bool scrollbar_supress_signal = false; - - /** Text input and callbacks. */ - public static bool text_input_is_active = false; - TextListener text_listener = new TextListener ("", "", ""); - Label text_input_label; - Entry text_entry; - Box text_box; - Gtk.Button submit_text_button; ToolboxCanvas toolbox; Task background_task = new Task(idle); public GtkWindow (string title) { - scrollbar = new Scrollbar (Orientation.VERTICAL, new Adjustment (0, 0, 1, 1, 0.01, 0.1)); ((Gtk.Window)this).set_title ("BirdFont"); } @@ -65,21 +53,10 @@ } public void init () { - Notify.init ("Fonts have been exported."); - - clipboard = Clipboard.get_for_display (get_display (), Gdk.SELECTION_CLIPBOARD); - + Notify.init ("BirdFont"); Signal.connect(this, "notify::is-active", (GLib.Callback) window_focus, null); - scrollbar.value_changed.connect (() => { - double p; - - if (!scrollbar_supress_signal) { - p = scrollbar.get_value () / (1 - scrollbar.adjustment.page_size); - FontDisplay display = MainWindow.get_current_display (); - display.scroll_to (p); - } - }); + clipboard = Clipboard.get_for_display (get_display (), Gdk.SELECTION_CLIPBOARD); delete_event.connect (() => { MenuTab.quit (); @@ -91,26 +68,25 @@ glyph_canvas_area = new GlyphCanvasArea (MainWindow.glyph_canvas); html_canvas = new WebView (); - WebKit.set_cache_model (CacheModel.DOCUMENT_VIEWER); - html_canvas.get_settings ().enable_default_context_menu = false; - html_box = new ScrolledWindow (null, null); html_box.set_policy (PolicyType.NEVER, PolicyType.AUTOMATIC); html_box.add (html_canvas); - html_canvas.set_editable (true); MainWindow.get_tab_bar ().signal_tab_selected.connect ((f, tab) => { string uri = ""; string html = ""; FontDisplay fd = tab.get_display (); - - scrollbar.set_visible (fd.has_scrollbar ()); if (fd.get_name () == "Preview") { uri = Preview.get_uri (); html = Preview.get_html_with_absolute_paths (); - html_canvas.load_html_string (html, uri); + try { + html_canvas.load_html (html, uri); + } catch (Error e) { + warning (e.message); + warning ("Failed to load html into canvas."); + } // show the webview when loading has finished html_box.set_visible (true); @@ -131,30 +107,9 @@ canvas_box = new Box (Orientation.HORIZONTAL, 0); canvas_box.pack_start (glyph_canvas_area, true, true, 0); canvas_box.pack_start (html_box, true, true, 0); - canvas_box.pack_start (scrollbar, false, true, 0); - - submit_text_button = new Gtk.Button (); - submit_text_button.set_label ("Submit"); - text_input_label = new Label (" " + "Text"); - text_entry = new Entry (); - text_box = new Box (Orientation.HORIZONTAL, 6); - text_box.pack_start (text_input_label, false, false, 0); - text_box.pack_start (text_entry, true, true, 0); - text_box.pack_start (submit_text_button, false, false, 0); - - text_entry.changed.connect (() => { - text_listener.signal_text_input (text_entry.text); - }); - - submit_text_button.clicked.connect (() => { - text_listener.signal_submit (text_entry.text); - text_input_is_active = false; - }); - - tab_box = new Box (Orientation.VERTICAL, 0); + tab_box = new Box (Orientation.VERTICAL, 0); tab_box.pack_start (new TabbarCanvas (MainWindow.get_tab_bar ()), false, false, 0); - tab_box.pack_start (text_box, false, false, 5); tab_box.pack_start (canvas_box, true, true, 0); toolbox = new ToolboxCanvas (MainWindow.get_toolbox ()); @@ -165,7 +120,7 @@ Box vbox = new Box (Orientation.VERTICAL, 0); vbox.pack_start(list_box, true, true, 0); add (vbox); - + try { set_icon_from_file ((!) SearchPaths.find_file (null, "birdfont_window_icon.png").get_path ()); } catch (GLib.Error e) { @@ -173,18 +128,13 @@ } key_press_event.connect ((t, event) => { - if (!GtkWindow.text_input_is_active) { - GtkWindow.reset_modifier (event.state); - TabContent.key_press (event.keyval); - } - + TabContent.key_press (event.keyval); + return false; }); key_release_event.connect ((t, event) => { - if (!GtkWindow.text_input_is_active) { - TabContent.key_release (event.keyval); - } + TabContent.key_release (event.keyval); return false; }); @@ -192,14 +142,14 @@ size_allocate.connect(() => { GlyphCanvas.redraw (); }); - + show_all (); - - scrollbar.set_visible (false); - hide_text_input (); - - MainWindow.open_recent_files_tab (); + MainWindow.open_recent_files_tab (); + + #if FREE + MainWindow.show_license_dialog (); + #endif } public void window_focus (void* data) { @@ -231,17 +181,6 @@ public void font_loaded () { Font f = BirdFont.get_current_font (); set_title (@"$(f.full_name)"); - } - - public void set_scrollbar_size (double size) { - scrollbar.adjustment.page_size = size; - scrollbar.set_visible (size != 0); - } - - public void set_scrollbar_position (double position) { - scrollbar_supress_signal = true; - scrollbar.adjustment.value = position * (1 - scrollbar.adjustment.page_size); - scrollbar_supress_signal = false; } public void dump_clipboard_content (Clipboard clipboard, SelectionData selection_data) { @@ -426,26 +365,6 @@ } return fn; - } - - public void hide_text_input () { - text_listener = new TextListener ("", "", ""); - text_box.hide (); - text_input_is_active = false; - } - - public void set_text_listener (TextListener listener) { - text_listener = listener; - text_input_label.set_text (" " + listener.label); - submit_text_button.set_label (listener.button_label); - text_box.show (); - text_entry.set_text (listener.default_text); - text_entry.activate.connect (() => { - text_listener.signal_submit (text_entry.text); - text_input_is_active = false; - }); - text_entry.grab_focus (); - text_input_is_active = true; } public bool convert_to_png (string from, string to) { @@ -481,8 +400,8 @@ } catch (GLib.Error e) { warning (e.message); } - } - + } + public void run_non_blocking_background_thread (Task t) { unowned Thread<void*> bg; @@ -643,20 +562,10 @@ draw.connect ((t, e)=> { Gtk.Allocation alloc; - Context cr; - StyleContext context; - Gdk.RGBA color; - + Context cr; + cr = cairo_create (get_window ()); get_allocation (out alloc); - - context = get_style_context (); - context.add_class (STYLE_CLASS_BUTTON); - color = context.get_background_color (Gtk.StateFlags.NORMAL); - - if (color.alpha > 0) { - tabbar.set_background_color (color.red, color.green, color.blue); - } tabbar.draw (cr, alloc.width, alloc.height); return true;
--- a/birdfont/Main.vala +++ b/birdfont/Main.vala @@ -21,7 +21,7 @@ GtkWindow native_window; MainWindow window; BirdFont.BirdFont birdfont; - + birdfont = new BirdFont.BirdFont (); birdfont.init (arg, null, "birdfont", null); Gtk.init (ref arg); @@ -31,12 +31,10 @@ window.set_native (native_window); native_window.init (); + birdfont.load_font_from_command_line (); - BirdFont.BirdFont.load_font_from_command_line (); + Gtk.main (); - Gtk.main (); return 0; } - -
diff --git birdui/BoxLayout.vala(deleted)
--- a/birdui/BoxLayout.vala +++ /dev/null @@ -1,216 +1,1 @@ - /* - Copyright (C) 2016 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using Gee; - using SvgBird; - using B; - using Cairo; - - namespace Bird { - - public enum BoxOrientation { - HORIZONTAL, - VERTICAL - } - - class BoxLayout : Component { - - public BoxOrientation orientation { get; private set; } - - public BoxLayout (XmlElement layout_tag, BoxOrientation orientation, Defs defs) { - base (layout_tag, defs); - this.orientation = orientation; - } - - public override void draw (Context cairo) { - cairo.save (); - cairo.translate (padded_x, padded_y); - clip (cairo); - - foreach (Component component in components) { - component.draw (cairo); - } - - cairo.restore (); - } - - bool is_width_remainder (Component component) { - return component.style.property_equals ("width", "remainder"); - } - - bool is_height_remainder (Component component) { - return component.style.property_equals ("height", "remainder"); - } - - /** The smallest size this layout can be. */ - public override void get_min_size (out double min_width, out double min_height) { - min_width = 0; - min_height = 0; - - if (orientation == BoxOrientation.HORIZONTAL) { - foreach (Component component in components) { - double w, h; - - component.get_min_size (out w, out h); - min_width += w; - - if (h > min_height) { - min_height = h; - } - } - } else { - foreach (Component component in components) { - double w, h; - - component.get_min_size (out w, out h); - min_height += h; - - if (w > min_width) { - min_width = w; - } - } - } - - min_width += get_padding_left (); - min_width += get_padding_right (); - min_height += get_padding_top (); - min_height += get_padding_bottom (); - - limit_size (ref min_width, ref min_height); - } - - public override void layout (double parent_width, double parent_height) { - double w, h; - - get_min_size (out w, out h); - int remainders = count_remainders (); - - if (!is_width_remainder (this)) { - width = w; - } - - if (!is_height_remainder (this)) { - height = h; - } - - if (remainders > 0) { - double remainder_size; - - if (orientation == BoxOrientation.HORIZONTAL) { - remainder_size = (parent_width - w) / remainders; - layout_variable_size (remainder_size, parent_height); - } else { - remainder_size = (parent_height - h) / remainders; - layout_variable_size (remainder_size, parent_width); - } - } - - if (orientation == BoxOrientation.HORIZONTAL) { - foreach (Component component in components) { - component.layout (parent_width, h); - } - } else { - foreach (Component component in components) { - component.layout (w, parent_height); - } - } - - layout_positions (); - } - - int count_remainders () { - int remainders = 0; - - if (orientation == BoxOrientation.HORIZONTAL) { - foreach (Component component in components) { - if (is_width_remainder (component)) { - remainders++; - } - } - } else { - foreach (Component component in components) { - if (is_height_remainder (component)) { - remainders++; - } - } - } - - return remainders; - } - - void layout_variable_size (double remainder_size, double fixed_size) { - double w, h; - - w = 0; - h = 0; - foreach (Component component in components) { - bool width_remainder = is_width_remainder (component); - bool height_remainder = is_height_remainder (component); - - if (width_remainder && orientation == BoxOrientation.HORIZONTAL) { - w = remainder_size; - w -= component.get_padding_left (); - w -= component.get_padding_right (); - h = fixed_size; - } - - if (height_remainder && orientation == BoxOrientation.VERTICAL) { - w = fixed_size; - h = remainder_size; - h -= component.get_padding_top (); - h -= component.get_padding_bottom (); - } - - if (width_remainder || height_remainder) { - double min_width, min_height; - component.get_min_size (out min_width, out min_height); - - if (w < min_width) { - w = min_width; - } - - if (h < min_height) { - h = min_height; - } - - component.limit_size (ref w, ref h); - component.width = w; - component.height = h; - } - } - } - - void layout_positions () { - double child_x = 0; - double child_y = 0; - - foreach (Component component in components) { - component.x = child_x + component.get_padding_left (); - component.y = child_y + component.get_padding_top (); - - if (orientation == BoxOrientation.HORIZONTAL) { - child_x = component.x + component.padded_width + get_padding_right (); - } else { - child_y = component.y + component.padded_height + get_padding_bottom (); - } - } - } - - public override string to_string () { - return "BoxLayout"; - } - } - - }
diff --git birdui/Component.vala(deleted)
--- a/birdui/Component.vala +++ /dev/null @@ -1,410 +1,1 @@ - /* - Copyright (C) 2016 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using B; - using SvgBird; - using Gee; - using Cairo; - - namespace Bird { - - public abstract class Component : GLib.Object { - public double width { get; protected set; } - public double height { get; protected set; } - - public double padded_width { - get { - return width + get_padding_top () + get_padding_bottom (); - } - } - - public double padded_height { - get { - return height + get_padding_left () + get_padding_right (); - } - } - - public double padded_x { - get { - return x + get_padding_top (); - } - } - - public double padded_y { - get { - return y + get_padding_left (); - } - } - - /** Vertical placement for this component relative to the parent container. */ - public double x { get; protected set; } - - /** Horizontal placement for this component relative to the parent container. */ - public double y { get; protected set; } - - /** The parts this component is made of. */ - protected ArrayList<Component> components = new ArrayList<Component> (); - - XmlElement component_tag; - - /** Style sheet and other SVG definitions. */ - Defs defs = new Defs (); - protected SvgStyle style = new SvgStyle (); - - string? css_class = null; - string? id = null; - - Overflow overflow = Overflow.VISIBLE; - - public Component.load (string file_name, double width, double height) { - this.width = width; - this.height = height; - load_file (file_name, null); - layout (width, height); - } - - /** Parse component ui file but don't do layout. */ - internal Component.load_component (string file_name, Component parent) { - load_file (file_name, parent); - } - - public Component (XmlElement component_tag, Defs defs) { - this.defs = defs; - this.component_tag = component_tag; - set_identification (); - parse (component_tag); - } - - public Component.embedded (XmlElement component_tag, Defs defs) { - this.defs = defs; - this.component_tag = component_tag; - set_identification (); - } - - void set_identification () { - foreach (Attribute attribute in component_tag.get_attributes ()) { - string attribute_name = attribute.get_name (); - - if (attribute_name == "id") { - id = attribute.get_content (); - } else if (attribute_name == "class") { - css_class = attribute.get_content (); - } - } - } - - private void inherit_styles_sheet (Defs defs) { - this.defs = defs; - } - - protected void parse_style (XmlElement style_tag) { - defs = defs.shallow_copy (); - StyleSheet style_sheet = StyleSheet.parse (defs, style_tag); - defs.style_sheet.merge (style_sheet); - } - - protected void parse_svg (XmlElement svg_tag) { - foreach (Attribute attribute in svg_tag.get_attributes ()) { - string attribute_name = attribute.get_name (); - - if (attribute_name == "file") { - string file_name = attribute.get_content (); - SvgComponent svg = new SvgComponent (svg_tag, defs, file_name); - add_component (svg); - } - } - } - - protected void add_component (Component component) { - components.add (component); - component.set_style_properties (this); - } - - protected void set_style_properties (Component parent) { - inherit_styles_sheet (parent.defs); - style = SvgStyle.parse (parent.defs, parent.style, component_tag); - set_overflow_property_from_css (); - set_identification (); - } - - protected void set_overflow_property_from_css () { - string? css_overflow = style.get_css_property ("overflow"); - - if (css_overflow != null) { - string overflow_property = (!) css_overflow; - - if (overflow_property == "hidden") { - overflow = Overflow.HIDDEN; - } else if (overflow_property == "visible") { - overflow = Overflow.VISIBLE; - } - } - } - - protected void parse_layout (XmlElement layout_tag) { - string type = ""; - - foreach (Attribute attribute in layout_tag.get_attributes ()) { - string attribute_name = attribute.get_name (); - - if (attribute_name == "type") { - type = attribute.get_content (); - } - } - - if (type == "hbox") { - HBox hbox = new HBox (layout_tag, defs); - add_component (hbox); - } else if (type == "vbox") { - VBox vbox = new VBox (layout_tag, defs); - add_component (vbox); - } else { - warning (type + " layout of type is supported in this verison."); - } - } - - protected void parse_component (XmlElement component_tag) { - foreach (Attribute attribute in component_tag.get_attributes ()) { - string attribute_name = attribute.get_name (); - - if (attribute_name == "file") { - string file_name = attribute.get_content (); - UI ui = new UI.load_component (file_name, this); - - foreach (Component component in ui.components) { - add_component (component); - - SvgStyle copied_style; - copied_style = SvgStyle.parse (defs, style, component_tag); - copied_style.apply (component.style); - component.style = copied_style; - } - } - } - } - - protected void parse (XmlElement component_tag) { - foreach (XmlElement tag in component_tag) { - string tag_name = tag.get_name (); - - if (tag_name == "ui") { - parse (tag); - } else if (tag_name == "layout") { - parse_layout (tag); - } else if (tag_name == "component") { - parse_component (tag); - } else if (tag_name == "svg") { - parse_svg (tag); - } else if (tag_name == "style") { - parse_style (tag); - } else { - unused_tag (tag_name); - } - } - } - - internal void unused_attribute (string attribute) { - warning ("The attribute " + attribute + " is not known in this version."); - } - - internal void unused_tag (string tag_name) { - warning ("The tag " + tag_name + " is not known in this version."); - } - - public void load_file (string file_name, Component? parent) { - if (file_name.has_suffix (".ui")) { - load_layout (file_name, parent); - } else if (file_name.has_suffix (".svg")) { - SvgComponent svg = new SvgComponent.for_file (file_name); - add_component (svg); - } else { - warning (file_name + " is not a ui file or svg file."); - } - } - - public void load_layout (string file_name, Component? parent) { - string? path = find_file (file_name); - - if (path == null) { - warning (file_name + " not found."); - return; - } - - string xml_data; - File layout_file = File.new_for_path ((!) path); - - try { - FileUtils.get_contents((!) layout_file.get_path (), out xml_data); - } catch (GLib.Error error) { - warning (error.message); - return; - } - - XmlTree xml_parser = new XmlTree (xml_data); - XmlElement tag = xml_parser.get_root (); - - if (parent == null) { - component_tag = tag; - defs = new Defs (); - } else { - Component p = (!) parent; - component_tag = p.component_tag; - defs = p.defs; - } - - parse (tag); - } - - public static string find_file (string file_name) { - File file = File.new_for_path ("birdui/" + file_name); - - if (file.query_exists ()) { - return (!) file.get_path (); - } - - return file_name; - } - - public virtual void get_min_size (out double min_width, out double min_height) { - min_width = width; - min_height = height; - - if (unlikely (components.size > 1)) { - warning ("A component has several parts but no layout has been set."); - } - - foreach (Component component in components) { - component.get_min_size (out min_width, out min_height); - } - - min_width += get_padding_left (); - min_width += get_padding_right (); - min_height += get_padding_top (); - min_height += get_padding_bottom (); - } - - public virtual void layout (double parent_width, double parent_height) { - warning ("A component without a layout was added to the view."); - - double w, h; - get_min_size (out w, out h); - - width = w; - height = h; - - foreach (Component component in components) { - component.layout (parent_width, parent_height); - } - } - - public void clip (Context cairo) { - if (overflow == Overflow.HIDDEN) { - cairo.rectangle (0, 0, width, height); - cairo.clip (); - } - } - - public abstract void draw (Context cairo); - - public virtual void motion_notify_event (double x, double y) { - } - - public virtual void button_press_event (uint button, double x, double y) { - } - - public virtual string to_string () { - return @"$(component_tag.get_name ())"; - } - - public void print_tree () { - print_tree_level (0); - } - - protected void print_tree_level (int indent) { - for (int i = 0; i < indent; i++) { - print ("\t"); - } - - print (@"$(to_string ()) width: $padded_width, height: $padded_height x $x y $y $(style)\n"); - - foreach (Component component in components) { - component.print_tree_level (indent + 1); - } - } - - protected double get_padding_bottom () { - return SvgFile.parse_number (style.get_css_property ("padding-bottom")); - } - - protected double get_padding_top () { - return SvgFile.parse_number (style.get_css_property ("padding-top")); - } - - protected double get_padding_left () { - return SvgFile.parse_number (style.get_css_property ("padding-left")); - } - - protected double get_padding_right () { - return SvgFile.parse_number (style.get_css_property ("padding-right")); - } - - protected void limit_size (ref double width, ref double height) { - string? min_width = style.get_css_property ("min-width"); - if (min_width != null) { - double w = SvgFile.parse_number (min_width); - if (width < w) { - width = w; - } - } - - string? min_height = style.get_css_property ("min-height"); - if (min_height != null) { - double h = SvgFile.parse_number (min_height); - - if (height < h) { - height = h; - } - } - - string? max_width = style.get_css_property ("max-width"); - if (max_width != null) { - double w = SvgFile.parse_number (max_width); - - if (width > w) { - width = w; - } - } - - string? max_height = style.get_css_property ("max-height"); - if (max_height != null) { - double h = SvgFile.parse_number (max_height); - - if (height > h) { - height = h; - } - } - - if (width < 0) { - width = 0; - } - - if (height < 0) { - height = 0; - } - } - } - - } -
diff --git birdui/GtkWidget.vala(deleted)
--- a/birdui/GtkWidget.vala +++ /dev/null @@ -1,60 +1,1 @@ - /* - Copyright (C) 2016 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using Gtk; - using Gdk; - using Cairo; - using Math; - - namespace Bird { - - public class Widget : DrawingArea { - - UI component; - - public Widget (UI main_component) { - component = main_component; - - add_events (EventMask.BUTTON_PRESS_MASK - | EventMask.POINTER_MOTION_MASK - | EventMask.LEAVE_NOTIFY_MASK); - - motion_notify_event.connect ((event)=> { - component.motion_notify_event (event.x, event.y); - return true; - }); - - button_press_event.connect ((event)=> { - component.button_press_event (event.button, event.x, event.y); - return true; - }); - - draw.connect ((event) => { - Context cairo_context = cairo_create (get_window ()); - component.draw (cairo_context); - return true; - }); - - int width = (int) rint (component.padded_width); - int height = (int) rint (component.padded_height); - set_size_request (width, height); - - size_allocate.connect((allocation) => { - component.resize (allocation.width, allocation.height); - }); - } - } - - }
diff --git birdui/GtkWindow.vala(deleted)
--- a/birdui/GtkWindow.vala +++ /dev/null @@ -1,40 +1,1 @@ - /* - Copyright (C) 2016 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using Bird; - using Gtk; - - int main (string[] args) { - Gtk.init (ref args); - - Window window = new Window (); - window.set_title ("UI Bird"); - window.destroy.connect (Gtk.main_quit); - - UI layout = new UI ("test.ui", 700, 200); - - layout.print_tree (); - - Bird.Widget primary_layout = new Bird.Widget (layout); - - Box vbox = new Box (Orientation.VERTICAL, 0); - vbox.pack_start(primary_layout, true, true, 0); - window.add (vbox); - - window.show_all (); - - Gtk.main (); - return 0; - }
diff --git birdui/HBox.vala(deleted)
--- a/birdui/HBox.vala +++ /dev/null @@ -1,32 +1,1 @@ - /* - Copyright (C) 2016 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using Gee; - using B; - using SvgBird; - - namespace Bird { - - class HBox : BoxLayout { - public HBox (XmlElement layout, Defs defs) { - base (layout, BoxOrientation.HORIZONTAL, defs); - } - - public override string to_string () { - return "HBox"; - } - } - - }
diff --git birdui/Overflow.vala(deleted)
--- a/birdui/Overflow.vala +++ /dev/null @@ -1,23 +1,1 @@ - /* - Copyright (C) 2016 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - namespace Bird { - - public enum Overflow { - HIDDEN, - VISIBLE - } - - }
diff --git birdui/Style.vala(deleted)
--- a/birdui/Style.vala +++ /dev/null @@ -1,35 +1,1 @@ - /* - Copyright (C) 2016 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using B; - using SvgBird; - using Gee; - using Cairo; - - namespace Bird { - - public class Style : GLib.Object { - public SvgStyle svg_style; - - public Style () { - svg_style = new SvgStyle (); - } - - public Style.for_svg (SvgStyle svg_style) { - this.svg_style = svg_style; - } - } - - }
diff --git birdui/SvgComponent.vala(deleted)
--- a/birdui/SvgComponent.vala +++ /dev/null @@ -1,103 +1,1 @@ - /* - Copyright (C) 2016 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using B; - using SvgBird; - using Gee; - using Cairo; - - namespace Bird { - - class SvgComponent : Component { - string? path = null; - string file_name = ""; - SvgDrawing? drawing = null; - - public SvgComponent (XmlElement svg_component_tag, Defs defs, string svg_file) { - base.embedded (svg_component_tag, defs); - load_svg (svg_file); - } - - public SvgComponent.for_file (string svg_file) { - base.embedded (new XmlElement.empty (), new Defs ()); - load_svg (svg_file); - } - - public override void get_min_size (out double min_width, out double min_height) { - min_width = 0; - min_height = 0; - - if (drawing != null) { - SvgDrawing svg = (!) drawing; - min_width = svg.width + get_padding_left () + get_padding_right (); - min_height = svg.height + get_padding_bottom () + get_padding_top (); - } - } - - public override void layout (double parent_width, double parent_height) { - if (unlikely (components.size > 0)) { - warning ("SVG files can not have subviews."); - } - - if (drawing != null) { - SvgDrawing svg = (!) drawing; - width = svg.width; - height = svg.height; - } - } - - public override string to_string () { - return "Svg: " + file_name; - } - - private void load_svg (string file_name) { - this.file_name = file_name; - path = find_file (file_name); - - if (path == null) { - warning (file_name + " not found."); - return; - } - - string xml_data; - File svg_file = File.new_for_path ((!) path); - try { - FileUtils.get_contents((!) svg_file.get_path (), out xml_data); - } catch (GLib.Error error) { - warning (error.message); - return; - } - - SvgFile svg_parser = new SvgFile (); - drawing = svg_parser.parse_svg_data (xml_data); - } - - public override void draw (Context cairo) { - cairo.save (); - cairo.translate (padded_x, padded_y); - clip (cairo); - - if (drawing != null) { - SvgDrawing svg = (!) drawing; - svg.draw (cairo); - } - - cairo.restore (); - } - - } - - } -
diff --git birdui/VBox.vala(deleted)
--- a/birdui/VBox.vala +++ /dev/null @@ -1,33 +1,1 @@ - /* - Copyright (C) 2016 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using Gee; - using B; - using SvgBird; - - namespace Bird { - - class VBox : BoxLayout { - public VBox (XmlElement layout, Defs defs) { - base (layout, BoxOrientation.VERTICAL, defs); - } - - public override string to_string () { - return "VBox"; - } - - } - - }
diff --git a/build.py b/build.py
--- a/build.py +++ b/build.py @@ -10,25 +10,18 @@ if platform == 'msys': process_tasks(dodo.make_libbirdgems('libbirdgems.dll', [])) - process_tasks(dodo.make_libbirdgems('libsvgbird.dll', [])) - process_tasks(dodo.make_libbirdfont('libbirdfont.dll', ['libbirdgems.dll', 'libsvgbird.dll'])) - process_tasks(dodo.make_libbirdfont('libbirdfont.dll', ['libbirdgems.dll', 'libsvgbird.dll'])) - process_tasks(dodo.make_birdfont_test('birdfont-test.exe', - ['libbirdgems.dll', 'libbirdfont.dll', 'libsvgbird.dll'])) + process_tasks(dodo.make_libbirdfont('libbirdfont.dll', ['libbirdgems.dll'])) + process_tasks(dodo.make_birdfont_test('birdfont-test.exe', ['libbirdgems.so', 'libbirdfont.so'])) elif platform == 'darwin': gems = "libbirdgems." + str(version.LIBBIRDGEMS_SO_VERSION) + '.dylib' bird = "libbirdfont." + str(version.SO_VERSION) + '.dylib'; - svg = "libsvgbird." + str(version.LIBSVGBIRD_SO_VERSION) + '.dylib'; - process_tasks(dodo.make_libsvgbird(svg, [])) process_tasks(dodo.make_libbirdgems(gems, [])) process_tasks(dodo.make_libbirdfont(bird, [gems])) process_tasks(dodo.task_man()) else: - process_tasks(dodo.task_libsvgbird()) process_tasks(dodo.task_libbirdgems()) process_tasks(dodo.task_libbirdfont()) - process_tasks(dodo.make_birdfont_test('birdfont-test', - ['libsvgbird.so', 'libbirdgems.so', 'libbirdfont.so'])) + process_tasks(dodo.make_birdfont_test('birdfont-test', ['libbirdgems.so', 'libbirdfont.so'])) if config.GTK: process_tasks(dodo.task_birdfont())
--- a/configure +++ b/configure @@ -11,8 +11,7 @@ from scripts.run import run TARGETS = ['libbirdfont', - 'libbirdgems', - 'libsvgbird', + 'libbirdgems', 'birdfont', 'birdfont-autotrace', 'birdfont-export', @@ -50,34 +49,13 @@ version = [int(n) for n in v.split ('.')] return [a,b,c] <= version - def test_library_version (lib, required=True, version=None): + def test_library_version(pkg_config, lib, required=True, version=None): print ('Looking for library: ' + lib + '\t\t') - process = subprocess.Popen ('pkg-config --modversion ' + lib, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + process = subprocess.Popen(pkg_config + ' --modversion ' + lib, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) v = process.stdout.readline().decode('utf-8') process.communicate()[0] - - if not process.returncode == 0: - if required: - print (FAIL + lib + ' not found' + ENDC) - exit (1) - else: - return False - - if version == None: - return True - - installed_version = v.split ('.'); - library_version = version.split ('.'); - - if installed_version < library_version: - if required: - print (FAIL + lib + ' version >= ' + version + ' not found.' + ENDC) - exit (1) - else: - return False - - return True - + return process.returncode == 0 + def has_posixvala (): posixvala = test_library_version ('posixvala') if not posixvala: @@ -87,10 +65,10 @@ print (OKGREEN + 'Using posix profile.' + ENDC) return 'True' - def configure(gtk, libbgee): + def configure(gtk, libbgee, valac, pkg_config): global gee - if not test_program_version ('valac', 0, 16, 0): + if not test_program_version(valac, 0, 16, 0): print (FAIL + 'valac is too old.' + ENDC) exit (1) @@ -101,10 +79,11 @@ 'gio-2.0', 'glib-2.0', 'gtk+-3.0', - 'webkitgtk-3.0', + 'webkit2gtk-4.0', 'libsoup-2.4', 'libnotify', 'sqlite3', + 'xmlbird' ] else: libs = [ @@ -112,23 +91,25 @@ 'glib-2.0', 'sqlite3', 'fontconfig', + 'xmlbird' ] - test_library_version ('xmlbird', True, '1.2.0') + test_library_version(pkg_config, 'xmlbird', True, '1.2.0') for lib in libs: - test_library_version (lib) + test_library_version(pkg_config, lib) if libbgee == 'Any': - if test_library_version ('gee-0.8', False): + if test_library_version(pkg_config, 'gee-0.8', False): gee = 'gee-0.8' - elif test_library_version ('gee-1.0', False): + elif test_library_version(pkg_config, 'gee-1.0', False): gee = 'gee-1.0' else: print (FAIL + 'Can not find libgee (version 0.8 or version 1.0).' + ENDC) exit (1) else: - if not test_library_version (libbgee): + if not test_library_version(pkg_config, libbgee): + print (FAIL + 'Can not find lib gee.' + ENDC) exit (1) gee = libbgee; @@ -146,6 +127,7 @@ parser.add_option('-g', '--gtk', dest='gtk', help='Build Gtk version, default is True', metavar='GTK') parser.add_option('-e', '--gee', dest='gee', help='Version of libgee', metavar='GEE') parser.add_option('-v', '--valac', dest='valac', help='Vala compiler', metavar='VALAC') + parser.add_option('-P', '--pkg-config', dest='pkg_config', help='Package config tool', metavar='PKG_CONFIG') parser.add_option('-n', '--nonnull', dest='nonnull', action="store_true", help='Enable compiletime checks for null pointers', metavar='NONNULL') parser.add_option('', '--valac-flags', dest='valac_flags', help='Vala compiler flags for all targets', metavar='VALAC_FLAGS', default='') @@ -198,12 +180,14 @@ options.gee = 'Any' if not options.valac: options.valac = 'valac' + if not options.pkg_config: + options.pkg_config = 'pkg-config' if not options.nonnull: options.nonnull = False else: options.nonnull = True - configure(options.gtk, options.gee) + configure(options.gtk, options.gee, options.valac, options.pkg_config) configfile.write_config(options.prefix) configfile.write_compile_parameters(options.prefix, @@ -211,9 +195,10 @@ options.cc, gee, options.valac, + options.pkg_config, options.nonnull, valacflags, cflags, ldflags, options.gtk)
diff --git a/dodo.py b/dodo.py
--- a/dodo.py +++ b/dodo.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2012 - 2016 Eduardo Naufel Schettino and Johan Mattsson + Copyright (C) 2012 2013 2014 2015 Eduardo Naufel Schettino and Johan Mattsson This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -46,13 +46,6 @@ LIBBIRDGEMS_SO_VERSION='${LIBbirdgems_VERSION}' else: LIBBIRDGEMS_SO_VERSION=version.LIBBIRDGEMS_SO_VERSION - - if "kfreebsd" in sys.platform: - LIBSVGBIRD_SO_VERSION=version.LIBSVGBIRD_SO_VERSION - elif "openbsd" in sys.platform: - LIBSVGBIRD_SO_VERSION='${LIBbirdgems_VERSION}' - else: - LIBSVGBIRD_SO_VERSION=version.LIBSVGBIRD_SO_VERSION if "kfreebsd" in sys.platform: SO_VERSION=version.SO_VERSION @@ -68,56 +61,54 @@ return '-Wl,-soname,' + target_binary def make_birdfont(target_binary, deps): - valac_command = config.VALAC + """\ + valac_command = """{valac} \ -C \ --vapidir=./ \ --basedir build/birdfont/ \ - """ + config.NON_NULL + """ \ - """ + config.VALACFLAGS.get("birdfont", "") + """ \ + {non_null} \ + {valacflags[birdfont]} \ --enable-experimental \ birdfont/*.vala \ --vapidir=./ \ - --pkg """ + config.GEE + """ \ + --pkg {gee} \ --pkg gio-2.0 \ --pkg cairo \ --pkg libsoup-2.4 \ --pkg gdk-pixbuf-2.0 \ - --pkg webkitgtk-3.0 \ + --pkg webkit2gtk-4.0 \ --pkg libnotify \ --pkg xmlbird \ --pkg libbirdfont \ - --pkg libsvgbird \ - """ + """.format(**config.SETTINGS) - cc_command = config.CC + " " + config.CFLAGS.get("birdfont", "") + """ \ + cc_command = """{cc} {cflags[birdfont]} \ -c C_SOURCE \ -D 'GETTEXT_PACKAGE="birdfont"' \ -I./build/libbirdfont \ - -I./build/libsvgbird \ - $(pkg-config --cflags sqlite3) \ - $(pkg-config --cflags """ + config.GEE + """) \ - $(pkg-config --cflags gio-2.0) \ - $(pkg-config --cflags cairo) \ - $(pkg-config --cflags glib-2.0) \ - $(pkg-config --cflags gdk-pixbuf-2.0) \ - $(pkg-config --cflags webkitgtk-3.0) \ - $(pkg-config --cflags libnotify) \ - -o OBJECT_FILE""" + $({pkg-config} --cflags sqlite3) \ + $({pkg-config} --cflags {gee}) \ + $({pkg-config} --cflags gio-2.0) \ + $({pkg-config} --cflags cairo) \ + $({pkg-config} --cflags glib-2.0) \ + $({pkg-config} --cflags gdk-pixbuf-2.0) \ + $({pkg-config} --cflags webkit2gtk-4.0) \ + $({pkg-config} --cflags libnotify) \ + -o OBJECT_FILE""".format(**config.SETTINGS) - linker_command = config.CC + " " + config.LDFLAGS.get("birdfont", "") + """ \ + linker_command = """{cc} {ldflags[birdfont]} \ build/birdfont/*.o \ -L./build/bin -lbirdfont \ - $(pkg-config --libs sqlite3) \ - $(pkg-config --libs """ + config.GEE + """) \ - $(pkg-config --libs gio-2.0) \ - $(pkg-config --libs cairo) \ - $(pkg-config --libs glib-2.0) \ - $(pkg-config --libs gdk-pixbuf-2.0) \ - $(pkg-config --libs webkitgtk-3.0) \ - $(pkg-config --libs xmlbird) \ - $(pkg-config --libs libnotify) \ - -L./build -L./build/bin -l birdgems -l svgbird \ - -o build/bin/""" + target_binary + $({pkg-config} --libs sqlite3) \ + $({pkg-config} --libs {gee}) \ + $({pkg-config} --libs gio-2.0) \ + $({pkg-config} --libs cairo) \ + $({pkg-config} --libs glib-2.0) \ + $({pkg-config} --libs gdk-pixbuf-2.0) \ + $({pkg-config} --libs webkit2gtk-4.0) \ + $({pkg-config} --libs xmlbird) \ + $({pkg-config} --libs libnotify) \ + -L./build -L./build/bin -l birdgems \ + -o build/bin/""".format(**config.SETTINGS) + target_binary birdfont = Builder('birdfont', valac_command, @@ -130,50 +121,47 @@ yield birdfont.build() def task_birdfont(): - yield make_birdfont('birdfont', ['libbirdgems.so', 'libbirdfont.so', 'libsvgbird.so']) + yield make_birdfont('birdfont', ['libbirdgems.so', 'libbirdfont.so']) def make_birdfont_export(target_binary, deps): - valac_command = config.VALAC + """ \ + valac_command = """{valac} \ -C \ --enable-experimental \ --basedir build/birdfont-export/ \ - """ + config.NON_NULL + """ \ - """ + config.VALACFLAGS.get("birdfont-export", "") + """ \ + {non_null} \ + {valacflags[birdfont-export]} \ birdfont-export/*.vala \ --vapidir=./ \ - --pkg """ + config.GEE + """ \ + --pkg {gee} \ --pkg gio-2.0 \ --pkg cairo \ --pkg xmlbird \ - --pkg libsvgbird \ --pkg libbirdfont \ - --pkg libsvgbird \ - """ + """.format(**config.SETTINGS) - cc_command = config.CC + " " + config.CFLAGS.get("birdfont-export", "") + """ \ + cc_command = """{cc} {cflags[birdfont-export]} \ -c C_SOURCE \ -D 'GETTEXT_PACKAGE="birdfont"' \ -I./build/libbirdfont \ - -I./build/libsvgbird \ - $(pkg-config --cflags sqlite3) \ - $(pkg-config --cflags """ + config.GEE + """) \ - $(pkg-config --cflags gio-2.0) \ - $(pkg-config --cflags cairo) \ - $(pkg-config --cflags glib-2.0) \ - -o OBJECT_FILE""" + $({pkg-config} --cflags sqlite3) \ + $({pkg-config} --cflags {gee}) \ + $({pkg-config} --cflags gio-2.0) \ + $({pkg-config} --cflags cairo) \ + $({pkg-config} --cflags glib-2.0) \ + -o OBJECT_FILE""".format(**config.SETTINGS) - linker_command = config.CC + " " + config.LDFLAGS.get("birdfont-export", "") + """ \ + linker_command = """{cc} {ldflags[birdfont-export]} \ build/birdfont-export/*.o \ -Lbuild/bin/ -lbirdfont \ -lm \ - $(pkg-config --libs sqlite3) \ - $(pkg-config --libs """ + config.GEE + """) \ - $(pkg-config --libs gio-2.0) \ - $(pkg-config --libs cairo) \ - $(pkg-config --libs glib-2.0) \ - $(pkg-config --libs xmlbird) \ - -L./build -L./build/bin -l birdgems -l svgbird \ - -o ./build/bin/""" + target_binary + $({pkg-config} --libs sqlite3) \ + $({pkg-config} --libs {gee}) \ + $({pkg-config} --libs gio-2.0) \ + $({pkg-config} --libs cairo) \ + $({pkg-config} --libs glib-2.0) \ + $({pkg-config} --libs xmlbird) \ + -L./build -L./build/bin -l birdgems \ + -o ./build/bin/""".format(**config.SETTINGS) + target_binary birdfont_export = Builder('birdfont-export', valac_command, @@ -186,49 +174,47 @@ yield birdfont_export.build() def task_birdfont_export(): - yield make_birdfont_export('birdfont-export', ['libbirdgems.so', 'libbirdfont.so', 'libsvgbird.so']) + yield make_birdfont_export('birdfont-export', ['libbirdgems.so', 'libbirdfont.so']) def make_birdfont_import(target_binary, deps): - valac_command = config.VALAC + """\ + valac_command = """{valac} \ -C \ --enable-experimental \ --basedir build/birdfont-import/ \ - """ + config.NON_NULL + """ \ - """ + config.VALACFLAGS.get("birdfont-import", "") + """ \ + {non_null} \ + {valacflags[birdfont-import]} \ birdfont-import/*.vala \ --vapidir=./ \ - --pkg """ + config.GEE + """ \ + --pkg {gee} \ --pkg gio-2.0 \ --pkg cairo \ --pkg xmlbird \ --pkg libbirdfont \ - --pkg libsvgbird \ - """ + """.format(**config.SETTINGS) - cc_command = config.CC + " " + config.CFLAGS.get("birdfont-import", "") + """ \ + cc_command = """{cc} {cflags[birdfont-import]} \ -c C_SOURCE \ -D 'GETTEXT_PACKAGE="birdfont"' \ -I./build/libbirdfont \ - -I./build/libsvgbird \ - $(pkg-config --cflags sqlite3) \ - $(pkg-config --cflags """ + config.GEE + """) \ - $(pkg-config --cflags gio-2.0) \ - $(pkg-config --cflags cairo) \ - $(pkg-config --cflags glib-2.0) \ - -o OBJECT_FILE""" + $({pkg-config} --cflags sqlite3) \ + $({pkg-config} --cflags {gee}) \ + $({pkg-config} --cflags gio-2.0) \ + $({pkg-config} --cflags cairo) \ + $({pkg-config} --cflags glib-2.0) \ + -o OBJECT_FILE""".format(**config.SETTINGS) - linker_command = config.CC + " " + config.LDFLAGS.get("birdfont-import", "") + """ \ + linker_command = """{cc} {ldflags[birdfont-import]} \ build/birdfont-import/*.o \ -Lbuild/bin/ -lbirdfont \ -lm \ - $(pkg-config --libs sqlite3) \ - $(pkg-config --libs """ + config.GEE + """) \ - $(pkg-config --libs gio-2.0) \ - $(pkg-config --libs cairo) \ - $(pkg-config --libs glib-2.0) \ - $(pkg-config --libs xmlbird) \ - -L./build -L./build/bin -l birdgems -l svgbird \ - -o ./build/bin/""" + target_binary + $({pkg-config} --libs sqlite3) \ + $({pkg-config} --libs {gee}) \ + $({pkg-config} --libs gio-2.0) \ + $({pkg-config} --libs cairo) \ + $({pkg-config} --libs glib-2.0) \ + $({pkg-config} --libs xmlbird) \ + -L./build -L./build/bin -l birdgems \ + -o ./build/bin/""".format(**config.SETTINGS) + target_binary birdfont_import = Builder('birdfont-import', valac_command, @@ -241,51 +227,48 @@ yield birdfont_import.build() def task_birdfont_import(): - yield make_birdfont_import('birdfont-import', ['libbirdgems.so', 'libbirdfont.so', 'libsvgbird.so']) + yield make_birdfont_import('birdfont-import', ['libbirdgems.so', 'libbirdfont.so']) def make_birdfont_autotrace(target_binary, deps): - valac_command = config.VALAC + """\ + valac_command = """{valac} \ -C \ --enable-experimental \ --basedir build/birdfont-autotrace/ \ - """ + config.NON_NULL + """ \ - """ + config.VALACFLAGS.get("birdfont-autotrace", "") + """ \ + {non_null} \ + {valacflags[birdfont-autotrace]} \ birdfont-autotrace/*.vala \ --vapidir=./ \ - --pkg """ + config.GEE + """ \ + --pkg {gee} \ --pkg gio-2.0 \ --pkg cairo \ --pkg xmlbird \ --pkg libbirdfont \ - --pkg libsvgbird \ - """ + """.format(**config.SETTINGS) - cc_command = config.CC + " " + config.CFLAGS.get("birdfont-autotrace", "") + """ \ + cc_command = """{cc} {cflags[birdfont-autotrace]} \ -c C_SOURCE \ -D 'GETTEXT_PACKAGE="birdfont"' \ -I./build/libbirdfont \ - -I./build/libsvgbird \ - $(pkg-config --cflags sqlite3) \ - $(pkg-config --cflags """ + config.GEE + """) \ - $(pkg-config --cflags gio-2.0) \ - $(pkg-config --cflags cairo) \ - $(pkg-config --cflags glib-2.0) \ - -o OBJECT_FILE""" + $({pkg-config} --cflags sqlite3) \ + $({pkg-config} --cflags {gee}) \ + $({pkg-config} --cflags gio-2.0) \ + $({pkg-config} --cflags cairo) \ + $({pkg-config} --cflags glib-2.0) \ + -o OBJECT_FILE""".format(**config.SETTINGS) - linker_command = config.CC + " " + config.LDFLAGS.get("birdfont-autotrace", "") + """ \ + linker_command = """{cc} {ldflags[birdfont-autotrace]} \ build/birdfont-autotrace/*.o \ -I./build/libbirdfont \ - -I./build/libsvgbird \ -Lbuild/bin/ -lbirdfont \ -lm \ - $(pkg-config --libs sqlite3) \ - $(pkg-config --libs """ + config.GEE + """) \ - $(pkg-config --libs gio-2.0) \ - $(pkg-config --libs cairo) \ - $(pkg-config --libs glib-2.0) \ - $(pkg-config --libs xmlbird) \ - -L./build -L./build/bin -l birdgems -l svgbird\ - -o ./build/bin/""" + target_binary + $({pkg-config} --libs sqlite3) \ + $({pkg-config} --libs {gee}) \ + $({pkg-config} --libs gio-2.0) \ + $({pkg-config} --libs cairo) \ + $({pkg-config} --libs glib-2.0) \ + $({pkg-config} --libs xmlbird) \ + -L./build -L./build/bin -l birdgems \ + -o ./build/bin/""".format(**config.SETTINGS) + target_binary birdfont_autotrace = Builder('birdfont-autotrace', valac_command, @@ -298,62 +281,60 @@ yield birdfont_autotrace.build() def task_birdfont_autotrace(): - yield make_birdfont_autotrace('birdfont-autotrace', ['libbirdgems.so', 'libbirdfont.so', 'libsvgbird.so']) + yield make_birdfont_autotrace('birdfont-autotrace', ['libbirdgems.so', 'libbirdfont.so']) def make_libbirdfont(target_binary, deps): - valac_command = config.VALAC + """\ + valac_command = """{valac} \ -C \ --vapidir=./ \ --basedir build/libbirdfont/ \ - """ + config.NON_NULL + """ \ - """ + config.VALACFLAGS.get("libbirdfont", "") + """ \ + {non_null} \ + {valacflags[libbirdfont]} \ --enable-experimental \ --library libbirdfont \ -H build/libbirdfont/birdfont.h \ libbirdfont/*.vala \ libbirdfont/OpenFontFormat/*.vala \ - libbirdfont/TextRendering/*.vala \ + libbirdfont/Renderer/*.vala \ --pkg posix \ - --pkg """ + config.GEE + """ \ + --pkg {gee} \ --pkg gio-2.0 \ --pkg cairo \ --pkg xmlbird \ --pkg libbirdgems \ - --pkg libsvgbird \ --pkg sqlite3 \ --pkg gdk-pixbuf-2.0 \ - """ + """.format(**config.SETTINGS) - cc_command = config.CC + " " + config.CFLAGS.get("libbirdfont", "") + """ \ + cc_command = """{cc} {cflags[libbirdfont]} \ -c C_SOURCE \ -fPIC \ -D 'GETTEXT_PACKAGE="birdfont"' \ -I ./build/libbirdfont \ -I ./build/libbirdgems \ - -I ./build/libsvgbird \ - $(pkg-config --cflags sqlite3) \ - $(pkg-config --cflags fontconfig) \ - $(pkg-config --cflags """ + config.GEE + """) \ - $(pkg-config --cflags gio-2.0) \ - $(pkg-config --cflags cairo) \ - $(pkg-config --cflags glib-2.0) \ - $(pkg-config --cflags xmlbird) \ - -o OBJECT_FILE""" + $({pkg-config} --cflags sqlite3) \ + $({pkg-config} --cflags fontconfig) \ + $({pkg-config} --cflags {gee}) \ + $({pkg-config} --cflags gio-2.0) \ + $({pkg-config} --cflags cairo) \ + $({pkg-config} --cflags glib-2.0) \ + $({pkg-config} --cflags xmlbird) \ + -o OBJECT_FILE""".format(**config.SETTINGS) - linker_command = config.CC + " " + config.LDFLAGS.get("libbirdfont", "") + """ \ + linker_command = ("""{cc} {ldflags[libbirdfont]} \ -shared \ """ + soname(target_binary) + """ \ build/libbirdfont/*.o \ - $(pkg-config --libs sqlite3) \ + $({pkg-config} --libs sqlite3) \ $(freetype-config --libs) \ - $(pkg-config --libs """ + config.GEE + """) \ - $(pkg-config --libs gio-2.0) \ - $(pkg-config --libs fontconfig) \ - $(pkg-config --libs cairo) \ - $(pkg-config --libs glib-2.0) \ - $(pkg-config --libs xmlbird) \ - -L./build -L./build/bin -l birdgems -l svgbird \ - -o ./build/bin/""" + target_binary + $({pkg-config} --libs {gee}) \ + $({pkg-config} --libs gio-2.0) \ + $({pkg-config} --libs fontconfig) \ + $({pkg-config} --libs cairo) \ + $({pkg-config} --libs glib-2.0) \ + $({pkg-config} --libs xmlbird) \ + -L./build -L./build/bin -l birdgems \ + -o ./build/bin/""").format(**config.SETTINGS) + target_binary libbirdfont = Builder('libbirdfont', valac_command, @@ -366,94 +347,38 @@ yield libbirdfont.build() def task_libbirdfont(): - yield make_libbirdfont('libbirdfont.so.' + SO_VERSION, ['libbirdgems.so', 'libsvgbird.so']) + yield make_libbirdfont('libbirdfont.so.' + SO_VERSION, ['libbirdgems.so']) - def make_libsvgbird(target_binary, deps): - valac_command = config.VALAC + """\ - -C \ - --vapidir=./ \ - --basedir build/libsvgbird/ \ - """ + config.NON_NULL + """ \ - """ + config.VALACFLAGS.get("libsvgbird", "") + """ \ - --enable-experimental \ - --library libsvgbird \ - -H build/libsvgbird/svgbird.h \ - libsvgbird/*.vala \ - --pkg posix \ - --pkg """ + config.GEE + """ \ - --pkg gio-2.0 \ - --pkg cairo \ - --pkg xmlbird \ - """ - cc_command = config.CC + " " + config.CFLAGS.get("libsvgbird", "") + """ \ - -c C_SOURCE \ - -fPIC \ - -I ./build/libsvgbird \ - $(pkg-config --cflags """ + config.GEE + """) \ - $(pkg-config --cflags gio-2.0) \ - $(pkg-config --cflags cairo) \ - $(pkg-config --cflags glib-2.0) \ - $(pkg-config --cflags xmlbird) \ - -o OBJECT_FILE""" - - linker_command = config.CC + " " + config.LDFLAGS.get("libsvgbird", "") + """ \ - -shared \ - """ + soname(target_binary) + """ \ - build/libsvgbird/*.o \ - $(pkg-config --libs """ + config.GEE + """) \ - $(pkg-config --libs gio-2.0) \ - $(pkg-config --libs cairo) \ - $(pkg-config --libs glib-2.0) \ - $(pkg-config --libs xmlbird) \ - -L./build -L./build/bin \ - -o ./build/bin/""" + target_binary - - link_name = 'libsvgbird.so' - source_directory = 'libsvgbird' - - libsvgbird = Builder(source_directory, - valac_command, - cc_command, - linker_command, - target_binary, - link_name, - deps) - - yield libsvgbird.build() - - def task_libsvgbird(): - yield make_libsvgbird('libsvgbird.so.' + LIBSVGBIRD_SO_VERSION, []) - def make_libbirdgems(target_binary, deps): - valac_command = config.VALAC + """\ + valac_command = """{valac} \ -C \ -H build/libbirdgems/birdgems.h \ --pkg posix \ --vapidir=./ \ --basedir=build/libbirdgems/ \ - """ + config.NON_NULL + """ \ - """ + config.VALACFLAGS.get("libbirdgems", "") + """ \ + {non_null} \ + {valacflags[libbirdgems]} \ --enable-experimental \ --library libbirdgems \ libbirdgems/*.vala \ - """ + """.format(**config.SETTINGS) - cc_command = config.CC + " " + config.CFLAGS.get("libbirdgems", "") + """ \ + cc_command = """{cc} {cflags[libbirdgems]} \ -fPIC \ - $(pkg-config --cflags glib-2.0) \ + $({pkg-config} --cflags glib-2.0) \ -c C_SOURCE \ -o OBJECT_FILE \ - """ + """.format(**config.SETTINGS) - linker_command = config.CC + " " + config.LDFLAGS.get("libbirdgems", "") + """ \ + linker_command = ("""{cc} {ldflags[libbirdgems]} \ -shared \ """ + soname(target_binary) + """ \ -fPIC \ build/libbirdgems/*.o \ - $(pkg-config --libs glib-2.0) \ - $(pkg-config --libs gobject-2.0) \ - -o build/bin/""" + target_binary + $({pkg-config} --libs glib-2.0) \ + $({pkg-config} --libs gobject-2.0) \ + -o build/bin/""").format(**config.SETTINGS) + target_binary libbirdgems = Builder('libbirdgems', valac_command, @@ -505,46 +430,44 @@ } def make_birdfont_test(target_binary, deps): - valac_command = config.VALAC + """\ + valac_command = """{valac} \ -C \ --vapidir=./ \ --basedir build/birdfont-test/ \ - """ + config.NON_NULL + """ \ - """ + config.VALACFLAGS.get("birdfont-test", "") + """ \ + {non_null} \ + {valacflags[birdfont-test]} \ --enable-experimental \ birdfont-test/*.vala \ --vapidir=./ \ - --pkg """ + config.GEE + """ \ + --pkg {gee} \ --pkg gio-2.0 \ --pkg cairo \ --pkg xmlbird \ --pkg libbirdfont \ - --pkg libsvgbird \ - """ + """.format(**config.SETTINGS) - cc_command = config.CC + " " + config.CFLAGS.get("birdfont-test", "") + """ \ + cc_command = """{cc} {cflags[birdfont-test]} \ -c C_SOURCE \ -D 'GETTEXT_PACKAGE="birdfont"' \ -I./build/libbirdfont \ - -I./build/libsvgbird \ - $(pkg-config --cflags sqlite3) \ - $(pkg-config --cflags """ + config.GEE + """) \ - $(pkg-config --cflags gio-2.0) \ - $(pkg-config --cflags cairo) \ - $(pkg-config --cflags glib-2.0) \ - -o OBJECT_FILE""" - - linker_command = config.CC + " " + config.LDFLAGS.get("birdfont-test", "") + """ \ + $({pkg-config} --cflags sqlite3) \ + $({pkg-config} --cflags {gee}) \ + $({pkg-config} --cflags gio-2.0) \ + $({pkg-config} --cflags cairo) \ + $({pkg-config} --cflags glib-2.0) \ + -o OBJECT_FILE""".format(**config.SETTINGS) + + linker_command = """{cc} {ldflags[birdfont-test]} \ build/birdfont-test/*.o \ -L./build/bin -lbirdfont \ - $(pkg-config --libs sqlite3) \ - $(pkg-config --libs """ + config.GEE + """) \ - $(pkg-config --libs gio-2.0) \ - $(pkg-config --libs cairo) \ - $(pkg-config --libs glib-2.0) \ - $(pkg-config --libs xmlbird) \ - -L./build -L./build/bin -l birdgems -l svgbird\ - -o build/bin/""" + target_binary + $({pkg-config} --libs sqlite3) \ + $({pkg-config} --libs {gee}) \ + $({pkg-config} --libs gio-2.0) \ + $({pkg-config} --libs cairo) \ + $({pkg-config} --libs glib-2.0) \ + $({pkg-config} --libs xmlbird) \ + -L./build -L./build/bin -l birdgems \ + -o build/bin/""".format(**config.SETTINGS) + target_binary test = Builder('birdfont-test', valac_command, @@ -558,57 +481,3 @@ def task_birdfont_test(): yield make_birdfont_test('birdfont-test', ['libbirdgems.so', 'libbirdfont.so']) - - def make_birdui(target_binary, deps): - valac_command = config.VALAC + """\ - -C \ - --pkg posix \ - --pkg """ + config.GEE + """ \ - --pkg gtk+-3.0 \ - --pkg libsvgbird \ - --pkg xmlbird \ - --vapidir=./ \ - --basedir=build/birdui/ \ - """ + config.NON_NULL + """ \ - --enable-experimental \ - birdui/*.vala \ - """ - - cc_command = config.CC + """ \ - -fPIC \ - $(pkg-config --cflags gtk+-3.0) \ - $(pkg-config --cflags glib-2.0) \ - $(pkg-config --cflags xmlbird) \ - $(pkg-config --cflags """ + config.GEE + """) \ - -g \ - -I ./build/libsvgbird \ - -c C_SOURCE \ - -o OBJECT_FILE \ - """ - - linker_command = config.CC + """ \ - """ + soname(target_binary) + """ \ - -fPIC \ - -g \ - build/birdui/*.o \ - $(pkg-config --libs gtk+-3.0) \ - $(pkg-config --libs glib-2.0) \ - $(pkg-config --libs gobject-2.0) \ - $(pkg-config --libs xmlbird) \ - $(pkg-config --libs """ + config.GEE + """) \ - -L ./build/bin -l m -l svgbird \ - -o build/bin/""" + target_binary - - libbirdgems = Builder('birdui', - valac_command, - cc_command, - linker_command, - target_binary, - None, - deps) - - yield libbirdgems.build() - - def task_birdui(): - yield make_birdui('birdui', []) -
--- a/install.py +++ b/install.py @@ -1,6 +1,5 @@ #!/usr/bin/python3 - """ - Copyright (C) 2013 2014 2015 Johan Mattsson + """ Copyright (C) 2013 2014 2015 Johan Mattsson This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -107,6 +106,7 @@ install ('resources/linux/birdfont.desktop', '/share/applications', 644) install ('resources/ucd.sqlite', '/share/birdfont', 644) install ('resources/codepages.sqlite', '/share/birdfont', 644) + install ('resources/Roboto-Regular.ttf', '/share/birdfont', 644) install ('resources/linux/256x256/birdfont.png', '/share/icons/hicolor/256x256/apps', 644) install ('resources/linux/128x128/birdfont.png', '/share/icons/hicolor/128x128/apps', 644) @@ -130,7 +130,8 @@ if platform.dist()[0] == 'Ubuntu' or platform.dist()[0] == 'Debian': process = subprocess.Popen(['dpkg-architecture', '-qDEB_HOST_MULTIARCH'], stdout=subprocess.PIPE) out, err = process.communicate() - libdir = '/lib/' + out.decode('UTF-8').rstrip ('\n') + out = out.decode('ascii') + libdir = '/lib/' + out.rstrip ('\n') else: p = platform.machine() if p == 'i386' or p == 's390' or p == 'ppc' or p == 'armv7hl':
--- a/libbirdfont/AbstractMenu.vala +++ b/libbirdfont/AbstractMenu.vala @@ -75,7 +75,8 @@ return null; } - public void process_key_binding_events (uint keyval) { + /** @return true if the keybindings consumes the event. */ + public bool process_key_binding_events (uint keyval) { string display; FontDisplay current_display = MainWindow.get_current_display (); ToolItem tm; @@ -83,7 +84,7 @@ display = current_display.get_name (); - if (current_display is Glyph) { + if (current_display is GlyphTab) { display = "Glyph"; } @@ -96,7 +97,7 @@ if (!SettingsDisplay.update_key_bindings && !(item is ToolItem)) { item.action (); - return; + return true; } if (item is ToolItem) { @@ -106,16 +107,18 @@ if (tm.tool.editor_events) { MainWindow.get_toolbox ().set_current_tool (tm.tool); tm.tool.select_action (tm.tool); - return; + return true; } else { tm.tool.select_action (tm.tool); - return; + return true; } } } } } } + + return false; } public void load_key_bindings () { @@ -231,6 +234,7 @@ public void add_tool_key_bindings () { ToolItem tool_item; + foreach (ToolCollection tool_set in MainWindow.get_toolbox ().tool_sets) { foreach (Expander e in tool_set.get_expanders ()) { foreach (Tool t in e.tool) {
--- a/libbirdfont/AlternateSets.vala +++ b/libbirdfont/AlternateSets.vala @@ -51,7 +51,30 @@ return alt; } - + + public Gee.ArrayList<Alternate> get_alt_with_glyph (string tag, Font font) { + Gee.ArrayList<Alternate> alt; + alt = new Gee.ArrayList<Alternate> (); + + foreach (Alternate a in alternates) { + Alternate available = new Alternate (a.glyph_name, a.tag); + + foreach (string substitution in a.alternates) { + if (font.has_glyph (substitution)) { + available.alternates.add (substitution); + } + } + + if (available.tag == tag && available.alternates.size > 0) { + if (font.has_glyph (available.glyph_name)) { + alt.add (available); + } + } + } + + return alt; + } + public void remove_empty_sets () { int i = 0; foreach (Alternate a in alternates) {
--- a/libbirdfont/BackgroundImage.vala +++ b/libbirdfont/BackgroundImage.vala @@ -17,7 +17,7 @@ namespace BirdFont { - public class BackgroundImage { + public class BackgroundImage : GLib.Object { public string name = ""; public Gee.ArrayList<BackgroundSelection> selections; @@ -733,9 +733,13 @@ cr.scale (g.view_zoom, g.view_zoom); - if (selected_handle == 2) Theme.color (cr, "Highlighted 1"); - else if (active_handle == 2) Theme.color (cr, "Default Background"); - else Theme.color (cr, "Menu Background"); + if (selected_handle == 2) { + Theme.color (cr, "Highlighted 1"); + } else if (active_handle == 2) { + Theme.color (cr, "Default Background"); + } else { + Theme.color (cr, "Menu Background"); + } x = img_offset_x - g.view_offset_x + (size_margin / 2) * img_scale_x; y = img_offset_y - g.view_offset_y + (size_margin / 2) * img_scale_y;
--- a/libbirdfont/BackgroundTab.vala +++ b/libbirdfont/BackgroundTab.vala @@ -14,7 +14,6 @@ using Math; using Cairo; - using SvgBird; namespace BirdFont {
--- a/libbirdfont/BackgroundTool.vala +++ b/libbirdfont/BackgroundTool.vala @@ -13,6 +13,7 @@ */ using Cairo; + using Math; namespace BirdFont { @@ -40,8 +41,12 @@ static BackgroundImage imported_background; static ImageSurface imported_surface; + static bool on_axis = false; + double rotation_position_x = 0; + double rotation_position_y = 0; + public BackgroundTool (string name) { - base (name, t_("Move, resize and rotate background image")); + base (name, ""); top_limit = 0; bottom_limit = 0; @@ -103,7 +108,7 @@ coordinate_y = Glyph.path_coordinate_y (y); if (background.selected_handle == 2) { - background.set_img_rotation_from_coordinate (coordinate_x, coordinate_y); + background.set_img_rotation_from_coordinate (rotation_position_x, rotation_position_y); } img_offset_x = background.img_offset_x; @@ -111,6 +116,7 @@ background.handler_release (x, y); + on_axis = false; move_bg = false; }); @@ -135,7 +141,7 @@ break; case Key.RIGHT: move (1, 0); - break; + break; default: break; } @@ -152,7 +158,19 @@ ((!) background_image).draw_handle (cairo_context, glyph); }); } - + + public override string get_tip () { + string tip = t_("Move, resize and rotate background image") + "\n"; + tip += HiddenTools.move_along_axis.get_key_binding (); + tip += " - "; + tip += t_ ("on axis") + "\n"; + return tip; + } + + public static void move_handle_on_axis () { + on_axis = true; + } + void move (int x, int y) { Glyph g = MainWindow.get_current_glyph (); BackgroundImage? background_image = g.get_background_image (); @@ -176,13 +194,43 @@ dx *= PenTool.precision; dy *= PenTool.precision; + // rotation handle if (bg.selected_handle == 2) { coordinate_x = Glyph.path_coordinate_x (x); coordinate_y = Glyph.path_coordinate_y (y); view_zoom = MainWindow.get_current_glyph ().view_zoom; - bg.preview_img_rotation_from_coordinate (coordinate_x, coordinate_y, view_zoom); + + rotation_position_x = coordinate_x; + rotation_position_y = coordinate_y; + + if (on_axis) { + double length = fabs (Path.distance (bg.img_middle_x, coordinate_x, + bg.img_middle_y, coordinate_y)); + + double min = double.MAX; + double circle_edge; + double circle_x; + double circle_y; + + for (double circle_angle = 0; circle_angle < 2 * PI; circle_angle += PI / 4) { + circle_x = bg.img_middle_x + cos (circle_angle) * length; + circle_y = bg.img_middle_y + sin (circle_angle) * length; + + circle_edge = fabs (Path.distance (coordinate_x, circle_x, + coordinate_y, circle_y)); + + if (circle_edge < min) { + rotation_position_x = circle_x; + rotation_position_y = circle_y; + min = circle_edge; + } + } + } + + bg.preview_img_rotation_from_coordinate (rotation_position_x, rotation_position_y, view_zoom); } + // resize handle if (bg.selected_handle == 1) { xscale = img_scale_x * ((img_width - dx) / img_width); yscale = xscale;
--- a/libbirdfont/BackgroundTools.vala +++ b/libbirdfont/BackgroundTools.vala @@ -112,7 +112,6 @@ add_part (selection); } - parts.clear_cache (); parts.redraw (); } @@ -182,13 +181,13 @@ }); label.has_delete_button = true; parts.add_tool (label, 0); - - parts.redraw (); - parts.clear_cache (); + parts.redraw (); if (!is_null (MainWindow.get_toolbox ())) { - MainWindow.get_toolbox ().update_expanders (); - Toolbox.redraw_tool_box (); + MainWindow.get_toolbox ().update_expanders (); + parts.clear_cache (); + Toolbox.redraw_tool_box (); + GlyphCanvas.redraw (); } }
--- /dev/null +++ b/libbirdfont/BezierPoints.vala @@ -1,1 +1,34 @@ + /* + Copyright (C) 2014 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + namespace BirdFont { + + /** Bezier point container for the SVG parser. */ + public class BezierPoints { + public unichar type = '\0'; + public unichar svg_type = '\0'; + public double x0 = 0; + public double y0 = 0; + public double x1 = 0; + public double y1 = 0; + public double x2 = 0; + public double y2 = 0; + + public string to_string () { + return @"$((!)type.to_string ()) $x0,$y0 $x1,$y1 $x2,$y2 SVG:$((!)svg_type.to_string ())"; + } + } + + }
--- a/libbirdfont/BezierTool.vala +++ b/libbirdfont/BezierTool.vala @@ -366,21 +366,21 @@ GlyphCanvas.redraw (); } else if (state == MOVE_HANDLE_ON_AXIS) { EditPointHandle h = current_point.get_right_handle (); - double horizontal, vertical; - - vertical = Path.distance (px, h.parent.x, py, py); - horizontal = Path.distance (h.parent.y, py, py, py); current_path.hide_end_handle = false; current_point.set_reflective_handles (true); current_point.convert_to_curve (); - - if (horizontal < vertical) { - h.move_to_coordinate (px, current_point.y); - } else { - h.move_to_coordinate (current_point.x, py); - } - + + double tied_x = 0; + double tied_y = 0; + + PointTool.tie_angle (h.parent.x, h.parent.y, + px, py, out tied_x, out tied_y); + + h.x = tied_x; + h.y = tied_y; + + current_path.reset_stroke (); GlyphCanvas.redraw (); } @@ -398,6 +398,8 @@ if (s > 2) { p = current_path.points.get (s - 2); + p.set_tie_handle (false); + p.set_reflective_handles (false); p.get_right_handle ().convert_to_line (); current_point.get_left_handle ().convert_to_line (); current_path.recalculate_linear_handles_for_point (p); @@ -442,6 +444,8 @@ current_path.delete_last_point (); current_path.reset_stroke (); current_point = current_path.get_last_point (); + current_point.set_tie_handle (false); + current_point.set_reflective_handles (false); state = MOVE_HANDLES; } else { state = swap ? MOVE_LAST_HANDLE_RIGHT : MOVE_LAST_HANDLE_LEFT;
--- a/libbirdfont/BirdFont.vala +++ b/libbirdfont/BirdFont.vala @@ -90,6 +90,7 @@ public static Font current_font; public static GlyphCollection current_glyph_collection; + public static Drawing? drawing = null; public static string? settings_subdirectory = null; @@ -165,8 +166,6 @@ Process.exit (0); } #endif - - CanvasSettings.init (); Preferences.load (); // always load default theme when names in theme does change @@ -177,7 +176,6 @@ Theme.set_default_colors (); if (theme_version == "" || int.parse (theme_version) < default_theme_version) { - Theme.load_theme ("dark.theme"); Preferences.set ("theme", "dark.theme"); } else { @@ -347,14 +345,17 @@ } public static File get_preview_directory () { - File settings = get_settings_directory (); - File backup = get_child(settings, "preview"); - - if (!backup.query_exists ()) { - DirUtils.create ((!) backup.get_path (), 0755); + string? export = BirdFont.get_current_font ().get_export_directory (); + + if (export == null) { + warning ("No export directory is set."); + export = ""; } - return backup; + File e = File.new_for_path ((!) export); + File p = get_child(e, "preview"); + + return p; } public static void set_settings_subdir (string? subdir) { @@ -499,7 +500,13 @@ #if ANDROID return t; #else - return _(t); + string translate = Preferences.get ("translate"); + + if (translate == "" || translate == "true") { + return _(t); + } else { + return t; + } #endif }
--- a/libbirdfont/BirdFontFile.vala +++ b/libbirdfont/BirdFontFile.vala @@ -11,9 +11,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. */ - using B; - using SvgBird; namespace BirdFont { @@ -277,11 +275,25 @@ foreach (SpacingClass sc in s.classes) { os.put_string ("<spacing "); os.put_string ("first=\""); - os.put_string (Font.to_hex (sc.first.get_char ())); + + if (sc.first.char_count () == 1) { + os.put_string (Font.to_hex (sc.first.get_char ())); + } else { + os.put_string ("name:"); + os.put_string (XmlParser.encode (sc.first)); + } + os.put_string ("\" "); os.put_string ("next=\""); - os.put_string (Font.to_hex (sc.next.get_char ())); + + if (sc.next.char_count () == 1) { + os.put_string (Font.to_hex (sc.next.get_char ())); + } else { + os.put_string ("name:"); + os.put_string (XmlParser.encode (sc.next)); + } + os.put_string ("\" "); os.put_string ("/>\n"); @@ -434,7 +446,7 @@ public void write_glyph (Glyph g, DataOutputStream os) throws GLib.Error { os.put_string (@"\t<glyph id=\"$(g.version_id)\" left=\"$(double_to_string (g.left_limit))\" right=\"$(double_to_string (g.right_limit))\">\n"); - foreach (Layer layer in g.layers.get_sublayers ()) { + foreach (Layer layer in g.layers.subgroups) { write_layer (layer, os); } @@ -442,110 +454,38 @@ os.put_string ("\t</glyph>\n"); } - void write_embedded_svg (EmbeddedSvg svg, DataOutputStream os) throws GLib.Error { - XmlParser xml = new XmlParser ((!) svg.svg_data); + void write_layer (Layer layer, DataOutputStream os) throws GLib.Error { + string data; - if (xml.validate ()) { - os.put_string (@"<embedded "); - os.put_string (@"type=\"svg\" "); - os.put_string (@"x=\"$(round (svg.x))\""); - os.put_string (@"y=\"$(round (svg.y))\""); - os.put_string (@">\n"); - - Tag tag = xml.get_root_tag (); - - os.put_string ("<"); - os.put_string (tag.get_name ()); - - os.put_string (" "); - write_tag_attributes (os, tag); - - string content = tag.get_content (); - - if (content == "") { - os.put_string (" /"); - } - - os.put_string (">"); - - os.put_string (content); - - os.put_string ("</"); - os.put_string (tag.get_name ()); - os.put_string (">\n"); - - os.put_string ("</embedded>\n"); - } - } - - void write_tag_attributes (DataOutputStream os, Tag tag) throws GLib.Error { - bool first = true; - - foreach (Attribute attribute in tag.get_attributes ()) { - string ns = attribute.get_namespace (); - - if (!first) { - os.put_string (" "); - } - - if (ns != "") { - os.put_string (ns); - os.put_string (":"); - } - - os.put_string (attribute.get_name ()); - os.put_string ("="); - os.put_string ("\""); - os.put_string (attribute.get_content ()); - os.put_string ("\""); - - first = false; - } - } - - void write_layer (Layer layer, DataOutputStream os) throws GLib.Error { + // FIXME: name etc. os.put_string (@"\t\t<layer name= \"$(layer.name)\" visible=\"$(layer.visible)\">\n"); - foreach (SvgBird.Object o in layer.objects.objects) { - - if (o is EmbeddedSvg) { - write_embedded_svg ((EmbeddedSvg) o, os); + foreach (Path p in layer.get_all_paths ().paths) { + data = get_point_data (p); + if (data != "") { + os.put_string (@"\t\t\t<path "); + + if (p.stroke != 0) { + os.put_string (@"stroke=\"$(double_to_string (p.stroke))\" "); + } + + if (p.line_cap != LineCap.BUTT) { + if (p.line_cap == LineCap.ROUND) { + os.put_string (@"cap=\"round\" "); + } else if (p.line_cap == LineCap.SQUARE) { + os.put_string (@"cap=\"square\" "); + } + } + + if (p.skew != 0) { + os.put_string (@"skew=\"$(double_to_string (p.skew))\" "); + } + + os.put_string (@"data=\"$(data)\" />\n"); } - - if (o is PathObject) { - Path p = ((PathObject) o).get_path (); - write_path_object (p, os); - } } os.put_string ("\t\t</layer>\n"); - } - - void write_path_object (Path p, DataOutputStream os) throws GLib.Error { - string data; - - data = get_point_data (p); - if (data != "") { - os.put_string (@"\t\t\t<path "); - - if (p.stroke != 0) { - os.put_string (@"stroke=\"$(double_to_string (p.stroke))\" "); - } - - if (p.line_cap != LineCap.BUTT) { - if (p.line_cap == LineCap.ROUND) { - os.put_string (@"cap=\"round\" "); - } else if (p.line_cap == LineCap.SQUARE) { - os.put_string (@"cap=\"square\" "); - } - } - - if (p.skew != 0) { - os.put_string (@"skew=\"$(double_to_string (p.skew))\" "); - } - - os.put_string (@"data=\"$(data)\" />\n"); - } } public static string double_to_string (double n) { @@ -1128,6 +1068,7 @@ private void parse_spacing_class (Tag tag) { string first, next; + string name; SpacingData spacing = font.get_spacing (); first = ""; @@ -1135,11 +1076,21 @@ foreach (Attribute attr in tag.get_attributes ()) { if (attr.get_name () == "first") { - first = (!) Font.to_unichar (attr.get_content ()).to_string (); + if (attr.get_content ().has_prefix ("U+")) { + first = (!) Font.to_unichar (attr.get_content ()).to_string (); + } else if (attr.get_content ().has_prefix ("name:")) { + name = attr.get_content ().substring ("name:".length); + first = XmlParser.decode (name); + } } if (attr.get_name () == "next") { - next = (!) Font.to_unichar (attr.get_content ()).to_string (); + if (attr.get_content ().has_prefix ("U+")) { + next = (!) Font.to_unichar (attr.get_content ()).to_string (); + } else if (attr.get_content ().has_prefix ("name:")) { + name = attr.get_content ().substring ("name:".length); + next = XmlParser.decode (name); + } } } @@ -1157,6 +1108,7 @@ foreach (Attribute attr in tag.get_attributes ()) { if (attr.get_name () == "left") { + range_left.parse_ranges (unserialize (attr.get_content ())); } @@ -1455,7 +1407,7 @@ foreach (Tag t in tag) { if (t.get_name () == "background") { parse_background_scale (glyph, t); - } + } } foreach (Path p in glyph.get_all_paths ()) { @@ -1466,33 +1418,6 @@ gc.set_unassigned (unassigned); master.insert_glyph (glyph, selected || selected_id == id); - } - - void parse_embedded_svg (Layer layer, Tag tag) { - string type = ""; - double x = 0; - double y = 0; - - foreach (Attribute attribute in tag.get_attributes ()) { - if (attribute.get_name () == "x") { - x = parse_double (attribute.get_content ()); - } - - if (attribute.get_name () == "y") { - y = parse_double (attribute.get_content ()); - } - - if (attribute.get_name () == "type") { - type = attribute.get_content (); - } - } - - if (type == "svg") { - EmbeddedSvg svg = SvgParser.parse_embedded_svg_data (tag.get_content ()); - svg.x = x; - svg.y = y; - layer.add_object (svg); - } } Layer parse_layer (Tag tag) { @@ -1512,11 +1437,7 @@ foreach (Tag t in tag) { if (t.get_name () == "path") { path = parse_path (t); - LayerUtils.add_path (layer, path); - } - - if (t.get_name () == "embedded") { - parse_embedded_svg (layer, t); + layer.add_path (path); } } @@ -1551,7 +1472,7 @@ } } - return path; + return path; } private static void line (Path path, string px, string py) { @@ -1957,8 +1878,7 @@ ligatures = font.get_ligatures (); ligatures.add_ligature (sequence, ligature); } - } }
diff --git libbirdfont/CanvasSettings.vala(deleted)
--- a/libbirdfont/CanvasSettings.vala +++ /dev/null @@ -1,93 +1,1 @@ - /* - Copyright (C) 2016 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using B; - using Cairo; - - namespace BirdFont { - - public class CanvasSettings { - - public static double stroke_width { - get { - settings_mutex.lock (); - double r = stroke_width_setting; - - if (unlikely (stroke_width_setting < 1)) { - string width = Preferences.get ("stroke_width"); - if (width != "") { - stroke_width_setting = double.parse (width); - } - } - - if (stroke_width_setting < 1) { - stroke_width_setting = 1; - } - - settings_mutex.unlock (); - return r; - } - - set { - settings_mutex.lock (); - stroke_width_setting = value; - settings_mutex.unlock (); - } - } - - public static bool show_all_line_handles { - get { - settings_mutex.lock (); - bool r = show_all_line_handles_setting; - settings_mutex.unlock (); - return r; - } - - set { - settings_mutex.lock (); - show_all_line_handles_setting = value; - settings_mutex.unlock (); - } - } - - public static bool fill_open_path { - get { - settings_mutex.lock (); - bool r = fill_open_path_setting; - settings_mutex.unlock (); - return r; - } - - set { - settings_mutex.lock (); - fill_open_path_setting = value; - settings_mutex.unlock (); - } - } - - static Mutex settings_mutex; - - /** The stroke of an outline when the path is not filled. */ - static double stroke_width_setting = 0; - static bool show_all_line_handles_setting = true; - static bool fill_open_path_setting = false; - - public static void init () { - settings_mutex = new Mutex (); - } - - } - - }
--- a/libbirdfont/CharDatabase.vala +++ b/libbirdfont/CharDatabase.vala @@ -33,7 +33,7 @@ public static void open_database () { File f = get_database_file (); - int rc = Database.open ((!) f.get_path (), out database); + int rc = Database.open_v2 ((!) f.get_path (), out database, OPEN_READONLY); db = (!) database;
--- a/libbirdfont/CharDatabaseParser.vala +++ b/libbirdfont/CharDatabaseParser.vala @@ -46,7 +46,7 @@ f.delete (); } - open_database (); + open_database (OPEN_READWRITE); create_tables (); parse_all_entries (); } catch (GLib.Error e) { @@ -54,9 +54,9 @@ } } - public void open_database () { + public void open_database (int access_mode) { File f = get_database_file (); - int rc = Database.open ((!) f.get_path (), out database); + int rc = Database.open_v2 ((!) f.get_path (), out database, access_mode); db = (!) database;
--- a/libbirdfont/ClipTool.vala +++ b/libbirdfont/ClipTool.vala @@ -11,8 +11,6 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. */ - - using SvgBird; namespace BirdFont { @@ -34,7 +32,7 @@ string bf_data; string data; - if (fd is Glyph) { + if (fd is GlyphTab) { svg_data = ExportTool.export_selected_paths_to_svg (); bf_data = export_selected_paths_to_birdfont_clipboard (); @@ -89,7 +87,7 @@ double x, y, w, h; double dx, dy; - if (fd is Glyph) { + if (fd is GlyphTab) { paste_paths (false); g.selection_boundaries (out x, out y, out w, out h); @@ -97,7 +95,7 @@ dx = g.motion_x - x - w / 2.0; dy = g.motion_y - y + h / 2.0; - foreach (SvgBird.Object path in g.active_paths) { + foreach (Path path in g.active_paths) { path.move (dx, dy); } } else if (fd is KerningDisplay) { @@ -117,7 +115,7 @@ // Determine if clipboard contains data in birdfont format. is_bf_clipboard = clipboard_data.index_of ("BirdFontClipboard") > -1; - if (fd is Glyph) { + if (fd is GlyphTab) { paste_to_glyph (is_bf_clipboard, paste_guide_lines); } @@ -134,9 +132,10 @@ FontDisplay fd = MainWindow.get_current_display (); Glyph? destination = null; string data; - return_if_fail (fd is Glyph); + return_if_fail (fd is GlyphTab); - destination = (Glyph) fd; + GlyphTab glyph_tab = (GlyphTab) fd; + destination = glyph_tab.glyphs.get_current (); ((!)destination).store_undo_state (); ((!)destination).clear_active_paths (); @@ -262,7 +261,7 @@ } } } else if (glyph.get_visible_paths ().size > 0) { - foreach (Path path in glyph.get_active_paths ()) { // FIXME: other objects + foreach (Path path in glyph.active_paths) { s.append ("BF path: "); s.append (BirdFontFile.get_point_data (path)); s.append ("\n"); @@ -407,9 +406,9 @@ string cap = p.replace ("cap: ", ""); if (cap == "round") { - path.line_cap = SvgBird.LineCap.ROUND; + path.line_cap = LineCap.ROUND; } else if (cap == "square") { - path.line_cap = SvgBird.LineCap.SQUARE; + path.line_cap = LineCap.SQUARE; } } } @@ -449,13 +448,13 @@ if (path.points.size > 0) { PenTool.clear_directions (); glyph.add_path (path); - glyph.add_active_path (null, path); + glyph.active_paths.add (path); path.update_region_boundaries (); } PenTool.remove_all_selected_points (); - foreach (Path p in glyph.get_active_paths ()) { + foreach (Path p in glyph.active_paths) { if (p.is_open ()) { foreach (EditPoint e in p.points) { e.set_selected (true);
--- a/libbirdfont/Color.vala +++ b/libbirdfont/Color.vala @@ -22,19 +22,24 @@ namespace BirdFont { - public class Color : SvgBird.Color { + public class Color { + public double r; + public double g; + public double b; + public double a; + public Color (double r, double g, double b, double a) { - base (r, g, b, a); + this.r = r; + this.g = g; + this.b = b; + this.a = a; } - public Color.create_copy (SvgBird.Color color) { - base (color.r, color.g, color.b, color.a); - } - public Color.hsba (double h, double s, double v, double a) { double hue, saturation, value; double f, p, q, t; - double r, g, b; + + this.a = a; if (s == 0.0) { r = v; @@ -95,8 +100,6 @@ assert_not_reached (); } } - - base (r, g, b, a); } public void to_hsva (out double h, out double s, out double v, out double a) { @@ -201,11 +204,23 @@ public static Color magenta () { return new Color (103.0 / 255, 33.0 / 255, 120.0 / 255, 1); } - - public new Color copy () { + + public string to_string () { + return @"r: $r, g: $g, b: $b, a: $a"; + } + + public Color copy () { return new Color (r, g, b, a); + } + + public string to_rgb_hex () { + string s = "#"; + s += Font.to_hex_code ((unichar) Math.rint (r * 254)); + s += Font.to_hex_code ((unichar) Math.rint (g * 254)); + s += Font.to_hex_code ((unichar) Math.rint (b * 254)); + return s; } } }
--- a/libbirdfont/ColorPicker.vala +++ b/libbirdfont/ColorPicker.vala @@ -13,7 +13,6 @@ */ using Cairo; - using SvgBird; namespace BirdFont { @@ -120,7 +119,7 @@ int g = (int) ((tx / Toolbox.allocation_width) * gradient.stops.size); return_if_fail (0 <= g < gradient.stops.size); current_stop = gradient.stops.get (g); - set_color (new Color.create_copy (current_stop.color)); + set_color (current_stop.color); } } @@ -237,7 +236,7 @@ int stop_size = (int) ((double) Toolbox.allocation_width / gradient.stops.size); for (int i = 0; i < gradient.stops.size; i++) { Stop s = gradient.stops.get (i); - c = new Color.create_copy (s.color); + c = s.color; cr.save (); cr.set_source_rgba (c.r, c.g, c.b, c.a); cr.rectangle (i * stop_size, y + 4 * bar_height, stop_size, bar_height);
--- a/libbirdfont/DrawingTools.vala +++ b/libbirdfont/DrawingTools.vala @@ -14,7 +14,6 @@ using Cairo; using Math; - using SvgBird; namespace BirdFont { @@ -192,9 +191,9 @@ }); draw_tools.add_tool (move_background); - move_canvas = new Tool ("move_canvas", - t_("Move canvas"), - t_("Press space and click to move the canvas.")); + move_canvas = new Tool ("move_canvas", t_("Move canvas") + + "\n" + t_("Ctrl + Shift + Click") + + "\n" + t_("Space + Click") + "\n"); move_canvas.select_action.connect ((self) => { update_drawing_and_background_tools (self); }); @@ -241,10 +240,7 @@ key_tools.add_tool (insert_point_on_path_tool); // quadratic Bézier points - quadratic_points = new Tool ("quadratic_points", - t_("Create quadratic Bézier curves"), - t_("All control points will be converted to quadratic points in the TTF format.")); - + quadratic_points = new Tool ("quadratic_points", t_("Create quadratic Bézier curves")); quadratic_points.select_action.connect ((self) => { point_type = PointType.QUADRATIC; Preferences.set ("point_type", "quadratic_points"); @@ -298,7 +294,7 @@ glyph.selection_boundaries (out x, out y, out w, out h); delta = x_coordinate.get_value () - x + glyph.left_limit; - foreach (SvgBird.Object path in glyph.active_paths) { + foreach (Path path in glyph.active_paths) { path.move (delta, 0); } @@ -341,7 +337,7 @@ glyph.selection_boundaries (out x, out y, out w, out h); - foreach (Path path in glyph.get_active_paths ()) { + foreach (Path path in glyph.active_paths) { path.move (0, y_coordinate.get_value () - (y - h) - font.base_line); } @@ -381,7 +377,7 @@ double x, y, w, h; Glyph glyph = MainWindow.get_current_glyph (); double angle = (self.get_value () / 360) * 2 * PI; - SvgBird.Object last_path; + Path last_path; glyph.selection_boundaries (out x, out y, out w, out h); x += w / 2; @@ -486,7 +482,7 @@ tie = !p.tie_handles; // don't tie end points - foreach (Path path in MainWindow.get_current_glyph ().get_active_paths ()) { + foreach (Path path in MainWindow.get_current_glyph ().active_paths) { if (path.is_open ()) { if (p == path.get_first_point () || p == path.get_last_point ()) { tie = false; @@ -541,7 +537,7 @@ if (!p.path.is_open () || !end_point) { p.point.set_reflective_handles (!symmetrical); - p.point.process_symmetrical_handles (); + p.point.get_right_handle ().process_symmetrical_handle (); if (symmetrical) { ep.point.set_tie_handle (false); @@ -604,9 +600,9 @@ Glyph g = MainWindow.get_current_glyph (); Layer layer = g.get_current_layer (); - foreach (SvgBird.Object p in g.active_paths) { - layer.remove (p); - layer.objects.objects.insert (0, p); + foreach (Path p in g.active_paths) { + layer.paths.remove (p); + layer.paths.paths.insert (0, p); } GlyphCanvas.redraw (); @@ -685,7 +681,7 @@ }); bg_selection.select_action.connect((self) => { - if (MainWindow.get_current_display () is Glyph) { + if (MainWindow.get_current_display () is GlyphTab) { BackgroundTool.import_background_image (); } }); @@ -804,12 +800,12 @@ g.store_undo_state (); if (StrokeTool.add_stroke) { - foreach (SvgBird.Object p in g.active_paths) { + foreach (Path p in g.active_paths) { p.stroke = StrokeTool.stroke_width; p.line_cap = StrokeTool.line_cap; } } else { - foreach (SvgBird.Object p in g.active_paths) { + foreach (Path p in g.active_paths) { p.stroke = 0; } } @@ -844,13 +840,9 @@ StrokeTool.stroke_width = object_stroke.get_value (); if (tool && StrokeTool.add_stroke) { - foreach (SvgBird.Object p in g.active_paths) { + foreach (Path p in g.active_paths) { p.stroke = StrokeTool.stroke_width; - - if (p is PathObject) { - Path path = ((PathObject) p).get_path (); - path.reset_stroke (); - } + p.reset_stroke (); } } @@ -886,15 +878,12 @@ g = MainWindow.get_current_glyph (); g.store_undo_state (); - foreach (SvgBird.Object p in g.active_paths) { - p.line_cap = SvgBird.LineCap.BUTT; - - if (p is PathObject) { - ((PathObject) p).get_path ().reset_stroke (); - } + foreach (Path p in g.active_paths) { + p.line_cap = LineCap.BUTT; + p.reset_stroke (); } - StrokeTool.line_cap = SvgBird.LineCap.BUTT; + StrokeTool.line_cap = LineCap.BUTT; Font f = BirdFont.get_current_font (); f.settings.set_setting ("line_cap", @"butt"); @@ -913,15 +902,12 @@ g = MainWindow.get_current_glyph (); g.store_undo_state (); - foreach (SvgBird.Object p in g.active_paths) { - p.line_cap = SvgBird.LineCap.ROUND; - - if (p is PathObject) { - ((PathObject) p).get_path ().reset_stroke (); - } + foreach (Path p in g.active_paths) { + p.line_cap = LineCap.ROUND; + p.reset_stroke (); } - StrokeTool.line_cap = SvgBird.LineCap.ROUND; + StrokeTool.line_cap = LineCap.ROUND; Font f = BirdFont.get_current_font (); f.settings.set_setting ("line_cap", @"round"); @@ -941,15 +927,12 @@ g = MainWindow.get_current_glyph (); g.store_undo_state (); - foreach (SvgBird.Object p in g.active_paths) { - p.line_cap = SvgBird.LineCap.SQUARE; - - if (p is PathObject) { - ((PathObject) p).get_path ().reset_stroke (); - } + foreach (Path p in g.active_paths) { + p.line_cap = LineCap.SQUARE; + p.reset_stroke (); } - StrokeTool.line_cap = SvgBird.LineCap.SQUARE; + StrokeTool.line_cap = LineCap.SQUARE; Font f = BirdFont.get_current_font (); f.settings.set_setting ("line_cap", @"square"); @@ -1105,6 +1088,7 @@ zoom_tool.store_current_view (); glyph_canvas.get_current_display ().reset_zoom (); glyph_canvas.redraw_area(0, 0, GlyphCanvas.allocation.width, GlyphCanvas.allocation.height); + self.set_selected (false); }); zoombar_tool.add_tool (reset_zoom); reset_zoom.set_tool_visibility (false); @@ -1113,6 +1097,7 @@ full_glyph.select_action.connect((self) => { zoom_tool.store_current_view (); zoom_tool.zoom_full_glyph (); + self.set_selected (false); }); zoombar_tool.add_tool (full_glyph); @@ -1120,6 +1105,7 @@ zoom_boundaries.select_action.connect((self) => { zoom_tool.store_current_view (); glyph_canvas.get_current_display ().zoom_max (); + self.set_selected (false); }); zoombar_tool.add_tool (zoom_boundaries); @@ -1130,18 +1116,21 @@ ZoomTool.zoom_full_background_image (); glyph_canvas.redraw_area(0, 0, GlyphCanvas.allocation.width, GlyphCanvas.allocation.height); } + self.set_selected (false); }); zoombar_tool.add_tool (zoom_bg); Tool zoom_prev = new Tool ("prev", t_("Previous view")); zoom_prev.select_action.connect((self) => { zoom_tool.previous_view (); + self.set_selected (false); }); zoombar_tool.add_tool (zoom_prev); Tool zoom_next = new Tool ("next", t_("Next view")); zoom_next.select_action.connect((self) => { zoom_tool.next_view (); + self.set_selected (false); }); zoombar_tool.add_tool (zoom_next); // view_tools zoom_next.set_tool_visibility (false); @@ -1255,9 +1244,11 @@ // update selelction when the user switches tab MainWindow.get_tab_bar ().signal_tab_selected.connect((tab) => { Glyph glyph; + GlyphTab glyph_tab; - if (tab.get_display () is Glyph) { - glyph = (Glyph) tab.get_display (); + if (tab.get_display () is GlyphTab) { + glyph_tab = (GlyphTab) tab.get_display (); + glyph = glyph_tab.glyphs.get_current (); show_bg.set_selected (glyph.get_background_visible ()); update_line_selection (glyph); } @@ -1268,7 +1259,7 @@ bool stroke = false; Glyph g = MainWindow.get_current_glyph (); - foreach (SvgBird.Object p in g.active_paths) { + foreach (Path p in g.active_paths) { if (p.stroke > 0) { stroke = true; } @@ -1626,7 +1617,7 @@ int i = 0; layer_tools.tool.clear (); - foreach (Layer layer in g.layers.get_sublayers ()) { + foreach (Layer layer in g.layers.subgroups) { LayerLabel label = new LayerLabel (layer); layer_tools.add_tool (label, 0); @@ -1660,10 +1651,12 @@ line_cap_butt.visible = StrokeTool.add_stroke; line_cap_round.visible = StrokeTool.add_stroke; line_cap_square.visible = StrokeTool.add_stroke; - MainWindow.get_toolbox ().update_expanders (); + stroke_expander.clear_cache (); stroke_expander.redraw (); + MainWindow.get_toolbox ().update_expanders (); } + } }
--- a/libbirdfont/EditPoint.vala +++ b/libbirdfont/EditPoint.vala @@ -219,6 +219,12 @@ /** Make handles symmetrical. */ public void set_reflective_handles (bool symmetrical) { reflective_point = symmetrical; + + if (symmetrical) { + get_left_handle ().convert_to_curve (); + get_right_handle ().convert_to_curve (); + process_tied_handle (); + } } /** Flip handles if next point on path is in the other direction. @@ -265,12 +271,6 @@ public void set_tie_handle (bool tie) { tie_handles = tie; - } - - public void process_symmetrical_handles () { - process_tied_handle (); - right_handle.process_symmetrical_handle (); - left_handle.process_symmetrical_handle (); } public void to_curve () { @@ -294,7 +294,6 @@ } } - /** This can only be performed if the path has been closed. */ public void process_tied_handle () requires (next != null && prev != null) { double a, b, c, length, angle;
--- a/libbirdfont/EditPointHandle.vala +++ b/libbirdfont/EditPointHandle.vala @@ -48,8 +48,10 @@ } if (parent.reflective_point) { - parent.process_symmetrical_handles (); - } + process_symmetrical_handle (); + } + + process_connected_handle (); } } @@ -73,8 +75,10 @@ } if (parent.reflective_point) { - parent.process_symmetrical_handles (); + process_symmetrical_handle (); } + + process_connected_handle (); } } @@ -289,16 +293,18 @@ if (!is_left_handle ()) { if (parent.next != null) { h = parent.get_next ().get_left_handle (); + h.parent.set_reflective_handles (false); h.parent.set_tie_handle (false); h.type = PointType.QUADRATIC; - h.move_to_coordinate_internal (px (), py ()); + h.move_to_coordinate_internal (x, y); } } else { if (parent.prev != null) { h = parent.get_prev ().get_right_handle (); + h.parent.set_reflective_handles (false); h.parent.set_tie_handle (false); h.type = PointType.QUADRATIC; - h.move_to_coordinate_internal (px (), py ()); + h.move_to_coordinate_internal (x, y); } } }
diff --git libbirdfont/EmbeddedSvg.vala(deleted)
--- a/libbirdfont/EmbeddedSvg.vala +++ /dev/null @@ -1,115 +1,1 @@ - /* - Copyright (C) 2016 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using B; - using Math; - using Cairo; - using SvgBird; - - namespace BirdFont { - - public class EmbeddedSvg : SvgBird.Object { - public string svg_data = ""; - public SvgDrawing drawing = new SvgDrawing (); - - public double x { get; set; } - public double y { get; set; } - - public override double xmin { - get { - return x; - } - - set { - } - } - - public override double xmax { - get { - return x + drawing.width; - } - - set { - } - } - - - public override double ymin { - get { - return y - drawing.height; - } - - set { - } - } - - public override double ymax { - get { - return y; - } - - set { - } - } - - public EmbeddedSvg (SvgDrawing drawing) { - this.drawing = drawing; - } - - public override void update_region_boundaries () { - drawing.update_region_boundaries (); - } - - public override bool is_over (double x, double y) { - return (this.x <= x <= this.x + drawing.width) - && (this.y - drawing.height <= y <= this.y); - } - - public void draw_embedded_svg (Context cr) { - cr.save (); - cr.translate (Glyph.xc () + x, Glyph.yc () - y); - drawing.draw (cr); - cr.restore (); - } - - public override void draw_outline (Context cr) { - } - - public override SvgBird.Object copy () { - EmbeddedSvg svg = new EmbeddedSvg (drawing); - svg.svg_data = svg_data; - return svg; - } - - public override void move (double dx, double dy) { - x += dx; - y += dy; - } - - public override void rotate (double theta, double xc, double yc) { - drawing.rotate (theta, xc, yc); - } - - public override bool is_empty () { - return drawing.is_empty (); - } - - public override void resize (double ratio_x, double ratio_y) { - drawing.resize (ratio_x, ratio_y); - } - - } - - }
--- a/libbirdfont/EmptyTab.vala +++ b/libbirdfont/EmptyTab.vala @@ -40,8 +40,8 @@ cr.rectangle (0, 0, allocation.width, allocation.height); cr.fill (); cr.restore (); - } + } } }
--- a/libbirdfont/Expander.vala +++ b/libbirdfont/Expander.vala @@ -61,10 +61,6 @@ public void clear_cache () { cached = null; - - foreach (Tool t in tool) { - t.clear_cache (); - } } public void set_headline (Text h) {
--- a/libbirdfont/ExportSettings.vala +++ b/libbirdfont/ExportSettings.vala @@ -121,7 +121,7 @@ svg.updated.connect ((c) => { Font f = BirdFont.get_current_font (); string v = c ? "true" : "false"; - f.settings.set_setting ("export_eot", v); + f.settings.set_setting ("export_svg", v); }); svg.checked = export_svg_setting (font); svg.margin_bottom = margin;
--- a/libbirdfont/ExportTool.vala +++ b/libbirdfont/ExportTool.vala @@ -13,13 +13,36 @@ */ using B; - using SvgBird; namespace BirdFont { public class ExportTool : GLib.Object { + + public static string? error_message = null; public ExportTool (string n) { + } + + public static void set_output_directory () { + #if MAC + Font font = BirdFont.get_current_font (); + string? path = font.get_export_directory (); + + FileChooser fc = new FileChooser (); + fc.file_selected.connect ((p) => { + path = p; + }); + + if (path == null) { + File export_path_handle = File.new_for_path (path); + + if (!can_write (export_path_handle)) { + MainWindow.file_chooser (t_("Export"), fc, FileChooser.LOAD | FileChooser.DIRECTORY); + } + } + + font.export_directory = path; + #endif } public static string export_selected_paths_to_svg () { @@ -45,7 +68,7 @@ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> <svg version="1.0" id="glyph_""" + name + """" - mlns="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" @@ -96,50 +119,37 @@ name = glyph.get_name (); - Gee.ArrayList<SvgBird.Object> pl; + Gee.ArrayList<Path> pl; s = new StringBuilder (); glyph_svg = ""; - pl = only_selected_paths ? glyph.active_paths : glyph.get_visible_objects (); - - foreach (SvgBird.Object o in pl) { - - if (o is PathObject) { - Path p = ((PathObject) o).get_path (); - - if (p.stroke > 0) { - s.append (@"<path "); - s.append (@"style=\""); - s.append (@"fill:none;"); - s.append (@"stroke:#000000;"); - s.append (@"stroke-width:$(p.stroke)px;"); - - if (p.line_cap == LineCap.ROUND) { - s.append (@"stroke-linecap:round;"); - } else if (p.line_cap == LineCap.SQUARE) { - s.append (@"stroke-linecap:square;"); - } - - s.append (@"\" "); - - s.append (@"d=\"$(Svg.to_svg_path (p, glyph))\" id=\"path_$(name)_$(id)\" />\n"); - id++; + pl = only_selected_paths ? glyph.active_paths : glyph.get_visible_paths (); + foreach (Path p in pl) { + if (p.stroke > 0) { + s.append (@"<path "); + s.append (@"style=\""); + s.append (@"fill:none;"); + s.append (@"stroke:#000000;"); + s.append (@"stroke-width:$(p.stroke)px;"); + + if (p.line_cap == LineCap.ROUND) { + s.append (@"stroke-linecap:round;"); + } else if (p.line_cap == LineCap.SQUARE) { + s.append (@"stroke-linecap:square;"); } - } else { - warning ("Copy and paste for other objects not implemented."); + + s.append (@"\" "); + + s.append (@"d=\"$(Svg.to_svg_path (p, glyph))\" id=\"path_$(name)_$(id)\" />\n"); + id++; } } if (only_selected_paths) { - foreach (SvgBird.Object p in glyph.active_paths) { - if (p is PathObject) { - Path path = ((PathObject) p).get_path (); - if (path.stroke == 0) { - glyph_svg += Svg.to_svg_path (path, glyph); - } - } else { - warning ("Not implemented"); + foreach (Path p in glyph.active_paths) { + if (p.stroke == 0) { + glyph_svg += Svg.to_svg_path (p, glyph); } } } else { @@ -173,14 +183,14 @@ string fn; int i; - if (fd is Glyph) { + if (fd is GlyphTab || fd is Glyph) { glyph = MainWindow.get_current_glyph (); } else if (fd is OverView) { OverView overview = MainWindow.get_overview (); Glyph? g = overview.get_selected_glyph (); if (g == null) { - warning("No glyhp selected in overview."); + warning("No glyph selected in overview."); return; } @@ -192,6 +202,7 @@ name = glyph.get_name (); if (selected_file == null) { + warning ("No selected file."); return; } @@ -212,7 +223,6 @@ glyph_svg = export_to_string (glyph, false); os = new DataOutputStream (file.create(FileCreateFlags.REPLACE_DESTINATION)); os.put_string (glyph_svg); - } catch (Error e) { stderr.printf (@"Export \"$svg_file\" \n"); critical (@"$(e.message)"); @@ -415,11 +425,7 @@ <div> <h3 class="big"></h3> - <p class="big"> - <span class="capstosmallcaps">OTF</span> features, - <span class="swashes">like swashes </span> - <span class="alternates">alternates &amp; </span> - <span class="smallcaps">small caps</span>, can be added + <p class="big">OTF features, like swashes alternates &amp; </span> small caps, can be added to the font.</span> </p> </div> @@ -447,22 +453,31 @@ public static string get_export_folder () { Font font = BirdFont.get_current_font (); - string? sandbox = BirdFont.get_sandbox_directory (); + string? d = font.get_export_directory (); + + if (d == null) { + warning ("No export path is not set"); + return ""; + } + + return (!) d; + } - if (sandbox != null) { - File s = File.new_for_path ((!) sandbox); - File f = get_child (s, "Fonts"); - try { - if (!f.query_exists ()) { - f.make_directory (); - } - } catch (GLib.Error e) { - warning(e.message); + static bool can_write (File folder) { + File test = get_child (folder, "text.tmp"); + bool writable = false; + + try { + writable = FileUtils.set_contents ((!) test.get_path (), "test"); + + if (writable) { + FileUtils.remove ((!) test.get_path ()); } - return (!) get_child (f, font.full_name).get_path (); - } else { - return (!) font.get_folder ().get_path (); + } catch (GLib.Error e) { + writable = false; } + + return writable; } public static File get_export_dir () { @@ -579,16 +594,20 @@ OpenFontFormatWriter fo = new OpenFontFormatWriter (f.units_per_em); File file = (!) File.new_for_path (ttf); - File file_mac = (!) File.new_for_path (ttf_mac); + File file_mac = (!) File.new_for_path (ttf_mac); + + error_message = null; try { fo.open (file, file_mac); fo.write_ttf_font (f); fo.close (); } catch (Error e) { - warning (@"Can't write TTF font to $ttf"); + warning (@"Can't create TTF font to $ttf"); critical (@"$(e.message)"); - } + error_message = e.message; + f.export_directory = null; + } } static void write_eot (string ttf, string eot) {
--- a/libbirdfont/FileDialogTab.vala +++ b/libbirdfont/FileDialogTab.vala @@ -203,14 +203,11 @@ } files.sort (); - update_rows (); layout (); base.selected_canvas (); scroll_to (0); - MainWindow.show_scrollbar (); - update_scrollbar (); } public void show_text_area (string text) { @@ -263,8 +260,6 @@ action.file_selected ((!) f.get_path ()); } } - - MainWindow.show_scrollbar (); } public override string get_label () { @@ -278,7 +273,6 @@ public override void button_release (int button, double ex, double ey) { base.button_release (button, ex, ey); show_text_area (selected_filename); - MainWindow.show_scrollbar (); } class SelectedFile : GLib.Object {
--- a/libbirdfont/Font.vala +++ b/libbirdfont/Font.vala @@ -63,6 +63,7 @@ public Gee.ArrayList<Line> custom_guides = new Gee.ArrayList<Line> (); public string? font_file = null; + public string? export_directory = null; bool modified = false; @@ -187,6 +188,18 @@ font_deleted (); } + public string? get_export_directory () { + #if MAC + return export_directory; + #endif + return get_folder_path (); + } + + public void add_default_characters () { + add_glyph_collection (get_notdef_character ()); + add_glyph_collection (get_space ()); + } + public Alternate? get_alternate (string glyph_name, string tag) { Gee.ArrayList<Alternate> alt = alternates.get_alt (tag); @@ -322,7 +335,7 @@ return fn; } else { file = File.new_for_path (fn); - return (!) file.resolve_relative_path ("").get_path (); + return (!) file.resolve_relative_path (".").get_path (); } } @@ -433,9 +446,16 @@ public GlyphCollection get_nonmarking_return () { Glyph g; GlyphCollection gc; + GlyphCollection? non_marking; if (has_glyph ("nonmarkingreturn")) { - return (!) get_glyph_collection ("nonmarkingreturn"); + non_marking = get_glyph_collection ("nonmarkingreturn"); + + if (non_marking == null) { + warning ("Non marking return not created."); + } else { + return (!)non_marking; + } } gc = new GlyphCollection ('\r', "nonmarkingreturn"); @@ -457,9 +477,16 @@ public GlyphCollection get_null_character () { Glyph n; GlyphCollection gc; + GlyphCollection? none; if (has_glyph ("null")) { - return (!) get_glyph_collection ("null"); + none = get_glyph_collection ("null"); + + if (none == null) { + warning("Null character not created."); + } else { + return (!) none; + } } gc = new GlyphCollection ('\0', "null"); @@ -480,14 +507,19 @@ public GlyphCollection get_space () { Glyph n; - GlyphCollection gc; + GlyphCollection gc; + GlyphCollection? space = null; if (has_glyph (" ")) { - return (!) get_glyph_collection (" "); + space = get_glyph_collection (" "); } if (has_glyph ("space")) { - return (!) get_glyph_collection ("space"); + space = get_glyph_collection ("space"); + } + + if (space != null) { + return (!) space; } gc = new GlyphCollection (' ', "space"); @@ -506,7 +538,7 @@ return gc; } - public GlyphCollection get_not_def_character () { + public GlyphCollection get_notdef_character () { Glyph g; GlyphCollection gc; @@ -900,6 +932,7 @@ if (loaded) { settings.load (get_file_name ()); kerning_strings.load (this); + add_default_characters (); } return loaded;
--- a/libbirdfont/FontDisplay.vala +++ b/libbirdfont/FontDisplay.vala @@ -74,7 +74,7 @@ public virtual void double_click (uint button, double ex, double ey) { } - public virtual void magnify (double magnification) { + public virtual void magnify (double magnification) { } public virtual void tap_down (int finger, int x, int y) {
--- a/libbirdfont/Glyph.vala +++ b/libbirdfont/Glyph.vala @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 2013 2014 2015 Johan Mattsson + Copyright (C) 2012 - 2016 Johan Mattsson This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -15,8 +15,6 @@ using Cairo; using Math; using Gee; - using B; - using SvgBird; namespace BirdFont { @@ -122,7 +120,7 @@ Line left_line; Line right_line; - /** Cache for Cairo rendering */ + /** Cache for Cairo surface rendering */ HashMap<string, Surface> glyph_cache = new HashMap<string, Surface> (); public const double CANVAS_MIN = -10000; @@ -133,24 +131,20 @@ public Layer layers = new Layer (); public int current_layer = 0; - public Gee.ArrayList<SvgBird.Object> active_paths = new Gee.ArrayList<SvgBird.Object> (); + public Gee.ArrayList<Path> active_paths = new Gee.ArrayList<Path> (); + public Gee.ArrayList<Layer> selected_groups = new Gee.ArrayList<Layer> (); - // used if this glyph originates from a fallback font + // used if this glyph is fetched from a fallback font public double top_limit = 0; public double baseline = 0; public double bottom_limit = 0; public Surface? overview_thumbnail = null; - public double selection_box_width = 0; - public double selection_box_height = 0; - public double selection_box_x = 0; - public double selection_box_y = 0; - public Glyph (string name, unichar unichar_code = 0) { this.name = name; this.unichar_code = unichar_code; - + add_help_lines (); left_limit = -28; @@ -160,20 +154,20 @@ public Glyph.no_lines (string name, unichar unichar_code = 0) { this.name = name; this.unichar_code = unichar_code; + } + + public Gee.ArrayList<Path> get_active_paths () { + return active_paths; } public Layer get_current_layer () { - if (unlikely (!(0 <= current_layer < layers.objects.size))) { - warning ("Layer index out of bounds."); - return new Layer (); - } - - return layers.get_sublayers ().get (current_layer); + return_val_if_fail (0 <= current_layer < layers.subgroups.size, new Layer ()); + return layers.subgroups.get (current_layer); } public void set_current_layer (Layer layer) { int i = 0; - foreach (Layer l in layers.get_sublayers ()) { + foreach (Layer l in layers.subgroups) { if (likely (l == layer)) { current_layer = i; return; @@ -182,42 +176,29 @@ } warning ("Layer is not added to glyph."); - } - - public Gee.ArrayList<SvgBird.Object> get_visible_objects () { - return LayerUtils.get_visible_objects (layers); } public Gee.ArrayList<Path> get_visible_paths () { - return LayerUtils.get_visible_paths (layers).paths; + return layers.get_visible_paths ().paths; } public PathList get_visible_path_list () { - return LayerUtils.get_visible_paths (layers); - } - - public Gee.ArrayList<SvgBird.Object> get_objects_in_current_layer () { - return get_current_layer ().objects.objects; + return layers.get_visible_paths (); } public Gee.ArrayList<Path> get_paths_in_current_layer () { - return LayerUtils.get_all_paths (get_current_layer ()).paths; + return get_current_layer ().get_all_paths ().paths; } public Gee.ArrayList<Path> get_all_paths () { - return LayerUtils.get_all_paths (layers).paths; + return layers.get_all_paths ().paths; } public void add_new_layer () { layers.add_layer (new Layer ()); - current_layer = layers.objects.size - 1; + current_layer = layers.subgroups.size - 1; } - public void add_layer (Layer layer) { - layers.add_layer (layer); - current_layer = layers.objects.size - 1; - } - public int get_layer_index (Layer layer) { return layers.index_of (layer); } @@ -257,10 +238,6 @@ public override void close () { undo_list.clear (); redo_list.clear (); - - foreach (Path path in get_all_paths ()) { - path.set_editable (false); - } } public void set_empty_ttf (bool e) { @@ -272,69 +249,46 @@ } public void clear_active_paths () { + selected_groups.clear (); active_paths.clear (); } public void add_active_path (Layer? group, Path? p) { + Path path; + Layer g; + if (p != null) { - PathObject path = new PathObject.for_path ((!) p); - add_active_object (group, path); - } else { - add_active_object (group, null); - } - } - - // FIXME: delete group - public void add_active_object (Layer? group, SvgBird.Object? o) { - SvgBird.Object object; + path = (!) p; - if (o != null) { - object = (!) o; + if (Toolbox.get_move_tool ().is_selected ()) { + if (path.stroke > 0) { + Toolbox.set_object_stroke (path.stroke); + } + } - if (!active_paths_contains (object)) { - active_paths.add (object); - } - - if (object is PathObject) { - PathObject path = (PathObject) object; - if (Toolbox.get_move_tool ().is_selected ()) { - if (path.get_path ().stroke > 0) { - Toolbox.set_object_stroke (path.get_path ().stroke); - } - } - - PenTool.active_path = path.get_path (); + if (!active_paths.contains (path)) { + active_paths.add (path); } + PenTool.active_path = path; } - } - public bool active_paths_contains (SvgBird.Object object) { - Glyph glyph = MainWindow.get_current_glyph (); - - if (glyph.active_paths.contains (object)) { - return true; - } - - if (object is PathObject) { - PathObject path = (PathObject) object; - - foreach (SvgBird.Object active in glyph.active_paths) { - if (active is PathObject) { - PathObject path_active = (PathObject) active; - if (path_active.get_path () == path.get_path ()) { - return true; - } - } + if (group != null) { + g = (!) group; + if (!selected_groups.contains (g)) { + selected_groups.add (g); } } - - return false; } - + public void delete_background () { store_undo_state (); background_image = null; GlyphCanvas.redraw (); + } + + public Path? get_active_path () { + return_val_if_fail (active_paths.size > 0, null); + return active_paths.get (active_paths.size - 1); } public bool boundaries (out double x1, out double y1, out double x2, out double y2) { @@ -386,7 +340,7 @@ px2 = -10000; py2 = -10000; - foreach (SvgBird.Object p in active_paths) { + foreach (Path p in active_paths) { if (p.xmin < px) { px = p.xmin; } @@ -470,40 +424,21 @@ public override void scroll_wheel (double x, double y, double pixeldelta_x, double pixeldelta_y) { - if (KeyBindings.has_alt () || KeyBindings.has_ctrl ()) { - if (pixeldelta_y > 0) { - zoom_in_at_point (x, y, pixeldelta_y); - } else { - zoom_out_at_point (x, y, pixeldelta_y); - } + if (pixeldelta_y > 0) { + zoom_in_at_point (x, y, pixeldelta_y); } else { - if (!KeyBindings.has_shift ()) { - view_offset_x -= pixeldelta_x / view_zoom; - view_offset_y -= pixeldelta_y / view_zoom; - } else { - // move canvas a long x axis instead of y - view_offset_x -= pixeldelta_y / view_zoom; - view_offset_y -= pixeldelta_x / view_zoom; - } + zoom_out_at_point (x, y, pixeldelta_y); } - redraw_area (0, 0, allocation.width, allocation.height); + GlyphCanvas.redraw (); } public virtual void add_path (Path p) { - if (layers.objects.size == 0) { + if (layers.subgroups.size == 0) { layers.add_layer (new Layer ()); } - LayerUtils.add_path (get_current_layer (), p); - } - - public void add_object (SvgBird.Object object) { - if (layers.objects.size == 0) { - layers.add_layer (new Layer ()); - } - - get_current_layer ().add_object (object); + get_current_layer ().add_path (p); } public override void selected_canvas () { @@ -528,7 +463,7 @@ GridTool.update_lines (); if (!is_null (MainWindow.native_window)) { - MainWindow.native_window.set_scrollbar_size (0); + MainWindow.scrollbar.set_size (0); } update_zoom_bar (); @@ -539,13 +474,16 @@ if (index != "") { int i = int.parse (index); - if (0 <= i < layers.objects.size) { + if (0 <= i < layers.subgroups.size) { current_layer = i; } } DrawingTools.update_layers (); MainWindow.get_toolbox ().update_expanders (); + + Tool current_tool = MainWindow.get_toolbox ().get_current_tool (); + current_tool.select_action (current_tool); } void update_zoom_bar () { @@ -792,14 +730,10 @@ return; } } - } - - public void delete_object (SvgBird.Object o) { - layers.remove (o); } public void delete_path (Path p) { - LayerUtils.remove_path(layers, p); + layers.remove_path(p); } public string get_svg_data () { @@ -949,9 +883,8 @@ add_path (path); path.reopen (); path.create_list (); - - PathObject object = new PathObject.for_path (path); - add_active_object (null, object); + + add_active_path (null, path); } if (remaining_points.paths.size > 0) { @@ -1050,7 +983,9 @@ return; } - if (move_canvas || DrawingTools.move_canvas.is_selected ()) { + if (move_canvas + || DrawingTools.move_canvas.is_selected () + || (KeyBindings.has_ctrl () && KeyBindings.has_shift ())) { view_is_moving = true; move_offset_x = view_offset_x; move_offset_y = view_offset_y; @@ -1064,7 +999,7 @@ public void set_active_path (Path p) { p.reopen (); clear_active_paths (); - add_active_object (null, new PathObject.for_path (p)); + add_active_path (null, p); } /** Move view port centrum to this coordinate. */ @@ -1134,19 +1069,9 @@ } public void show_zoom_area (int sx, int sy, int nx, int ny) { - double x, y, w, h; - set_zoom_area (sx, sy, nx, ny); - zoom_area_is_visible = true; - - x = Math.fmin (zoom_x1, zoom_x2) - 50; - y = Math.fmin (zoom_y1, zoom_y2) - 50; - - w = Math.fabs (zoom_x1 - zoom_x2) + 100; - h = Math.fabs (zoom_y1 - zoom_y2) + 100; - - redraw_area ((int)x, (int)y, (int)w, (int)h); + GlyphCanvas.redraw (); } public void set_zoom_area (int sx, int sy, int nx, int ny) { @@ -1208,17 +1133,61 @@ return -y; } - public SvgBird.Object? get_object_at (double x, double y) { - Layer layer = get_current_layer (); - for (int i = layer.objects.size - 1; i >= 0; i--) { - SvgBird.Object o = layer.objects.get_object (i); - - if (o.is_over (x, y)) { - return o; + public Layer? get_path_at (double x, double y) { + Layer? group = null; + bool found = false; + + foreach (Layer layer in get_current_layer ().subgroups) { + foreach (Path pt in layer.paths.paths) { + if (pt.is_over (x, y)) { + found = true; + group = layer; + } } } - - return null; + + if (!found) { + foreach (Path pt in get_paths_in_current_layer ()) { + if (pt.is_over (x, y)) { + Layer layer = new Layer (); + layer.is_counter = true; + layer.single_path = true; + layer.add_path (pt); + group = layer; + } + } + } + + return group; + } + + public bool select_path (double x, double y) { + Path? p = null; + bool found = false; + + foreach (Path pt in get_paths_in_current_layer ()) { + if (pt.is_over (x, y)) { + p = pt; + found = true; + } + } + + if (!KeyBindings.has_shift ()) { + clear_active_paths (); + } + + add_active_path (null, p); + + return found; + } + + public bool is_over_selected_path (double x, double y) { + foreach (Path pt in active_paths) { + if (pt.is_over (x, y)) { + return true; + } + } + return false; } public void queue_redraw_path (Path path) { @@ -1235,6 +1204,48 @@ double xtb = -view_offset_x - xmax; redraw_area ((int)xtb - 10, (int)yta - 10, (int)(xtb - xta) + 10, (int) (yta - ytb) + 10); + } + + public Path get_closeset_path (double x, double y) { + double d; + EditPoint ep = new EditPoint (); + + Path min_point = new Path (); + double min_distance = double.MAX; + + double xt = path_coordinate_x (x); + double yt = path_coordinate_y (y); + var paths = get_visible_paths (); + + foreach (Path p in paths) { + if (p.is_over (xt, yt)) { + return p; + } + } + + foreach (Path p in paths) { + if (p.points.size == 0) continue; + + p.get_closest_point_on_path (ep, xt, yt); + d = Math.pow (ep.x - xt, 2) + Math.pow (ep.y - yt, 2); + + if (d < min_distance) { + min_distance = d; + min_point = p; + } + + } + + // a path without any editpoints + if (paths.size > 0) { + return paths.get (0); + } + + if (unlikely (min_distance == double.MAX)) { + warning (@"No path found in path_list."); + } + + return min_point; } public void move_selected_edit_point_coordinates (EditPoint selected_point, double xt, double yt) { @@ -1287,9 +1298,8 @@ return; } - foreach (SvgBird.Object object in active_paths) { + foreach (Path path in active_paths) { EditPoint p; - Path path = ((PathObject) object).get_path (); EditPoint pl = path.get_last_point (); if (pl.prev != null) { @@ -1303,6 +1313,7 @@ if (px > x) px -= tw + 60; if (py > y) py -= th + 60; + } else { px = x - 60; py = y - 60; @@ -1343,7 +1354,7 @@ } public void open_path () { - foreach (Path p in get_all_paths ()) { + foreach (Path p in get_visible_paths ()) { p.set_editable (true); p.recalculate_linear_handles (); @@ -1588,91 +1599,157 @@ cr.stroke (); } - public void draw_layers (Context cr) { - foreach (SvgBird.Object object in layers.objects) { - if (object is Layer) { - draw_layer (cr, (Layer) object); - } - } + /** Draw filled paths. */ + public void draw_paths (Context cr, Color? c = null) { + PathList stroke; + Color color; + bool open; - draw_bird_font_paths (cr); - } + cr.save (); + cr.new_path (); + foreach (Path p in get_visible_paths ()) { + if (c != null) { + color = (!) c; + } else if (p.color != null) { + color = (!) p.color; + } else { + color = Color.black (); + } - public void draw_layer (Context cr, Layer sublayers) { - foreach (SvgBird.Object object in sublayers.objects) { - if (object is EmbeddedSvg) { - EmbeddedSvg svg = (EmbeddedSvg) object; - svg.draw_embedded_svg (cr); + if (p.stroke > 0) { + stroke = p.get_stroke_fast (); + draw_path_list (stroke, cr, color); + } else { + open = p.is_open (); + + if (open) { + p.close (); + p.recalculate_linear_handles (); + } + + p.draw_path (cr, this, color); + + if (open) { + p.reopen (); + } } } + cr.fill (); + cr.restore (); } - - public void draw_bird_font_paths (Context cr) { - Tool selected_tool = MainWindow.get_toolbox ().get_current_tool (); - - bool draw_control_points = (selected_tool is PenTool) - || (selected_tool is PointTool) - || (selected_tool is TrackTool) - || (selected_tool is BezierTool); - bool has_path = false; - - if (!draw_control_points) { - foreach (SvgBird.Object object in get_visible_objects ()) { - if (object is PathObject - && object.stroke > 0) { - - has_path = true; - PathObject object_path = (PathObject) object; - object_path.draw_path (cr); + public void draw_path (Context cr) { + PathList stroke; + Color color; + + cr.save (); + cr.new_path (); + foreach (Path p in get_visible_paths ()) { + if (p.stroke > 0) { + stroke = p.get_stroke_fast (); + + if (p.is_editable ()) { + color = Theme.get_color ("Filled Stroke"); + color.a = 0.8; + } else { + color = Color.black (); } + + draw_path_list (stroke, cr, color); } + } + cr.fill (); + cr.restore (); - if (has_path) { - cr.set_fill_rule (FillRule.WINDING); - cr.set_source_rgba (0, 0, 0, 1); - cr.fill (); + if (!(MainWindow.get_toolbox ().get_current_tool () is PenTool) + && !(MainWindow.get_toolbox ().get_current_tool () is PointTool) + && !(MainWindow.get_toolbox ().get_current_tool () is TrackTool) + && !(MainWindow.get_toolbox ().get_current_tool () is BezierTool)) { + cr.save (); + cr.new_path (); + foreach (Path p in active_paths) { + if (p.stroke > 0) { + stroke = p.get_stroke_fast (); + color = Theme.get_color ("Selected Objects"); + draw_path_list (stroke, cr, color); + } } + cr.fill (); + cr.restore (); } - - has_path = false; - - if (!draw_control_points) { - foreach (SvgBird.Object object in get_visible_objects ()) { - if (object is PathObject - && object.stroke == 0) { - - has_path = true; - PathObject object_path = (PathObject) object; - object_path.draw_path (cr); + + if (is_open () && Path.fill_open_path) { + cr.save (); + cr.new_path (); + foreach (Path p in get_visible_paths ()) { + if (p.stroke == 0) { + color = p.color == null ? get_path_fill_color () : (!) p.color; + p.draw_path (cr, this, color); } } + cr.fill (); + cr.restore (); + } - if (has_path) { - cr.set_fill_rule (FillRule.EVEN_ODD); - Theme.color (cr, "Objects"); - cr.fill (); + if (is_open ()) { + cr.save (); + cr.new_path (); + foreach (Path p in get_visible_paths ()) { + p.draw_outline (cr); + p.draw_edit_points (cr); } + cr.restore (); } - - if (draw_control_points) { - foreach (SvgBird.Object object in get_visible_objects ()) { - if (object is PathObject) { - PathObject object_path = (PathObject) object; - Glyph g = MainWindow.get_current_glyph (); - cr.set_line_width (CanvasSettings.stroke_width / g.view_zoom); - object_path.path.draw_path (cr); - object_path.path.draw_control_points (cr); + + if (!is_open ()) { + // This was good for testing but it is way too slow: + // Svg.draw_svg_path (cr, get_svg_data (), Glyph.xc () + left, Glyph.yc () - baseline); + + cr.save (); + cr.new_path (); + foreach (Path p in get_visible_paths ()) { + if (p.stroke == 0) { + color = p.color == null ? Color.black () : (!) p.color; + p.draw_path (cr, this, color); } + } + cr.close_path (); + cr.fill (); + cr.restore (); + + foreach (Path p in active_paths) { + cr.save (); + cr.new_path (); + if (p.stroke == 0) { + p.draw_path (cr, this); + } + cr.close_path (); + cr.fill (); + cr.restore (); } } if (show_orientation_arrow) { foreach (Path p in get_visible_paths ()) { - if (p.stroke == 0) { + if (p.stroke > 0) { + stroke = p.get_stroke_fast (); + foreach (Path ps in stroke.paths) { + ps.draw_orientation_arrow (cr, orientation_arrow_opacity); + } + } else { p.draw_orientation_arrow (cr, orientation_arrow_opacity); } } + } + } + + private Color get_path_fill_color () { + return Theme.get_color ("Fill Color"); + } + + public void draw_path_list (PathList pl, Context cr, Color? c = null) { + foreach (Path p in pl.paths) { + p.draw_path (cr, this, c); } } @@ -1712,8 +1789,8 @@ } if (unlikely (Preferences.draw_boundaries)) { - foreach (SvgBird.Object o in get_visible_objects ()) { - draw_boundaries (o, cmp); + foreach (Path p in get_visible_paths ()) { + p.draw_boundaries (cmp); } } @@ -1730,29 +1807,22 @@ cmp.restore (); } - cmp.save (); - cmp.scale (view_zoom, view_zoom); - cmp.translate (-view_offset_x, -view_offset_y); - draw_layers (cmp); - cmp.restore (); - - Tool selected_tool = MainWindow.get_toolbox ().get_current_tool (); - bool draw_selection = active_paths.size > 0 - && (selected_tool is MoveTool || selected_tool is ResizeTool); - - if (draw_selection) { - update_selection_boundaries (); - draw_selection_box (cmp); + if (!is_empty ()) { + cmp.save (); + cmp.scale (view_zoom, view_zoom); + cmp.translate (-view_offset_x, -view_offset_y); + draw_path (cmp); + cmp.restore (); } - + cmp.save (); tool = MainWindow.get_toolbox ().get_current_tool (); tool.draw_action (tool, cmp, this); cmp.restore (); } - private void zoom_in_at_point (double x, double y, double zoom = 15) { - int n = (int) (-zoom); + private void zoom_in_at_point (double x, double y, double amount = 15) { + int n = (int) (-amount); zoom_at_point (x, y, n); } @@ -1788,7 +1858,7 @@ private void move_view_offset (double x, double y) { view_offset_x = move_offset_x + (pointer_begin_x - x) * (1/view_zoom); view_offset_y = move_offset_y + (pointer_begin_y - y) * (1/view_zoom); - redraw_area (0, 0, allocation.width, allocation.height); + GlyphCanvas.redraw (); } public void store_undo_state (bool clear_redo = false) { @@ -1819,10 +1889,10 @@ g.add_line (line.copy ()); } - g.layers = (Layer) layers.copy (); + g.layers = layers.copy (); - foreach (SvgBird.Object o in active_paths) { - g.active_paths.add (o); + foreach (Path p in active_paths) { + g.active_paths.add (p); } if (background_image != null) { @@ -1892,7 +1962,7 @@ void set_glyph_data (Glyph g) { current_layer = g.current_layer; - layers = (Layer) g.layers.copy (); + layers = g.layers.copy (); left_limit = g.left_limit; right_limit = g.right_limit; @@ -1909,8 +1979,8 @@ } clear_active_paths (); - foreach (SvgBird.Object p in g.active_paths) { - add_active_object (null, p); + foreach (Path p in g.active_paths) { + add_active_path (null, p); } redraw_area (0, 0, allocation.width, allocation.height); @@ -2243,7 +2313,7 @@ SpacingData sd; sd = font.get_spacing (); - s = sd.get_all_connections ((!) unichar_code.to_string ()); + s = sd.get_all_connections (get_name ()); foreach (string l in s) { if (l != (!) unichar_code.to_string ()) { @@ -2310,12 +2380,12 @@ public void move_layer_up () { Layer layer = get_current_layer (); - if (current_layer + 2 <= layers.objects.size) { - return_if_fail (0 <= current_layer + 2 <= layers.objects.size); - layers.objects.objects.insert (current_layer + 2, layer); + if (current_layer + 2 <= layers.subgroups.size) { + return_if_fail (0 <= current_layer + 2 <= layers.subgroups.size); + layers.subgroups.insert (current_layer + 2, layer); - return_if_fail (0 <= current_layer + 1 < layers.objects.size); - layers.objects.objects.remove_at (current_layer); + return_if_fail (0 <= current_layer + 1 < layers.subgroups.size); + layers.subgroups.remove_at (current_layer); set_current_layer (layer); } @@ -2325,11 +2395,11 @@ Layer layer = get_current_layer (); if (current_layer >= 1) { - return_if_fail (0 <= current_layer - 1 < layers.objects.size); - layers.objects.objects.insert (current_layer - 1, layer); + return_if_fail (0 <= current_layer - 1 < layers.subgroups.size); + layers.subgroups.insert (current_layer - 1, layer); - return_if_fail (0 <= current_layer + 1 < layers.objects.size); - layers.objects.objects.remove_at (current_layer + 1); + return_if_fail (0 <= current_layer + 1 < layers.subgroups.size); + layers.subgroups.remove_at (current_layer + 1); set_current_layer (layer); } @@ -2402,140 +2472,8 @@ } return g2; - } - - // FIXME: convert everything to the new SvgBird.Object code - public Gee.ArrayList<Path> get_active_paths () { - Gee.ArrayList<Path> paths = new Gee.ArrayList<Path> (); - - foreach (SvgBird.Object object in active_paths) { - if (object is PathObject) { - paths.add (((PathObject) object).get_path ()); - } - } - - return paths; - } - - public void draw_boundaries (SvgBird.Object object, Context cr) { - double x = Glyph.reverse_path_coordinate_x (object.xmin); - double y = Glyph.reverse_path_coordinate_y (object.ymin); - double x2 = Glyph.reverse_path_coordinate_x (object.xmax); - double y2 = Glyph.reverse_path_coordinate_y (object.ymax); - - cr.save (); - - Theme.color (cr, "Default Background"); - cr.set_line_width (2); - cr.rectangle (x, y, x2 - x, y2 - y); - cr.stroke (); - - cr.restore (); - } - - void draw_selection_box (Context cr) { - double x, y, w, h; - double hook_width; - - x = reverse_path_coordinate_x (selection_box_x); - y = reverse_path_coordinate_y (selection_box_y); - w = selection_box_width / ivz (); - h = selection_box_height / ivz (); - - hook_width = w > 20 ? 10 : w * 0.2; - - update_selection_boundaries (); - cr.save (); - - // FIXME: use color theme - cr.set_source_rgba (0, 0, 0, 1); - - cr.set_line_width (1); - cr.move_to (x, y + hook_width); - cr.line_to (x, y); - cr.line_to (x + hook_width, y); - cr.stroke (); - - cr.move_to (x + w - hook_width, y); - cr.line_to (x + w, y); - cr.line_to (x + w, y + hook_width); - cr.stroke (); - - cr.move_to (x + w, y + h - hook_width); - cr.line_to (x + w, y + h); - cr.line_to (x + w - hook_width, y + h); - - cr.move_to (x + hook_width, y + h); - cr.line_to (x, y + h); - cr.line_to (x, y + h - hook_width); - - cr.stroke (); - cr.restore (); - - cr.save (); - cr.set_source_rgba (1, 1, 1, 1); - - cr.set_line_width (1); - cr.move_to (x + 1, y + hook_width); - cr.line_to (x + 1, y + 1); - cr.line_to (x + hook_width, y + 1); - cr.stroke (); - - cr.move_to (x + w - hook_width, y + 1); - cr.line_to (x + w - 1, y + 1); - cr.line_to (x + w - 1, y + hook_width); - cr.stroke (); - - cr.move_to (x + w - 1, y + h - hook_width); - cr.line_to (x + w - 1, y + h - 1); - cr.line_to (x + w - hook_width, y + h - 1); - - cr.move_to (x + hook_width, y + h - 1); - cr.line_to (x + 1, y + h - 1); - cr.line_to (x + 1, y + h - hook_width); - - cr.stroke (); - cr.restore (); - } - - public void update_selection_boundaries () { - get_selection_box_boundaries (out selection_box_x, - out selection_box_y, out selection_box_width, - out selection_box_height); - } - - public void get_selection_box_boundaries (out double x, out double y, out double w, out double h) { - double px, py, px2, py2; - - px = CANVAS_MAX; - py = CANVAS_MAX; - px2 = CANVAS_MIN; - py2 = CANVAS_MIN; - - foreach (Path p in get_active_paths ()) { - if (px > p.xmin) { - px = p.xmin; - } - - if (py > p.ymin) { - py = p.ymin; - } - - if (px2 < p.xmax) { - px2 = p.xmax; - } - - if (py2 < p.ymax) { - py2 = p.ymax; - } - } - - w = px2 - px; - h = py2 - py; - x = px; - y = py2; } } }
--- a/libbirdfont/GlyphCanvas.vala +++ b/libbirdfont/GlyphCanvas.vala @@ -81,20 +81,21 @@ } public void redraw_area (int x, int y, int w, int h) { - if (unlikely (MenuTab.has_suppress_event ())) { + if (MenuTab.has_suppress_event ()) { warning ("Do not call redraw from background thread."); } else { - signal_redraw_area (x, y, w, h); + signal_redraw_area (x, y, w + (int) MainWindow.scrollbar.width, h); } } public static void redraw () { GlyphCanvas c = MainWindow.get_glyph_canvas (); - if (!is_null (c)) { - c.redraw_area (0, 0, allocation.width, allocation.height); + if (!is_null (c) && !is_null (MainWindow.scrollbar)) { + int w = (int) (allocation.width + MainWindow.scrollbar.width); + c.redraw_area (0, 0, w, allocation.height); } } } }
--- a/libbirdfont/GlyphMaster.vala +++ b/libbirdfont/GlyphMaster.vala @@ -114,7 +114,6 @@ GlyphMaster n = new GlyphMaster (); foreach (Glyph g in glyphs) { - n.glyphs.add (g); n.glyphs.add (g); }
--- a/libbirdfont/GlyphRange.vala +++ b/libbirdfont/GlyphRange.vala @@ -182,7 +182,11 @@ } if (w.char_count () == 1) { - add_single (w.get_char ()); + unichar c = w.get_char_validated (); + + if (c > 0) { + add_single (c); + } } else if (w == "space") { add_single (' '); } else if (w == "divis") { @@ -475,9 +479,10 @@ } public unichar get_character (uint32 index) { - UniRange r; - unichar c; + int64 ti; string chr; + UniRange r; + unichar c; UniRange? range; uint32 range_start_index; @@ -532,7 +537,9 @@ uns = unserialize (c); if (uns.char_count () != 1) { - warning (@"Expecting a single character got $c"); + // the glyph was not found by its name because it is not in the list of + // unassigned characters + return false; } s = uns.get_char ();
--- a/libbirdfont/GlyphSelection.vala +++ b/libbirdfont/GlyphSelection.vala @@ -27,7 +27,7 @@ FontDisplay.dirty_scrollbar = true; open_glyph_signal.connect ((gc) => { - selected_glyph (gc); + selected_glyph (gc); Toolbox.redraw_tool_box (); });
--- a/libbirdfont/GlyphSequence.vala +++ b/libbirdfont/GlyphSequence.vala @@ -100,18 +100,9 @@ foreach (Alternate a in alternates) { GlyphSequence old = new GlyphSequence (); - string name; - Glyph? g; - - name = a.glyph_name; - - if (name == "space") { - name = " "; - } - - g = font.get_glyph_by_name (name); + Glyph? g = font.get_glyph_by_name (a.glyph_name); - if (likely (g != null)) { + if (g != null) { old.add (g); if (a.alternates.size > 0) { @@ -119,7 +110,7 @@ string alt_name = a.alternates.get (0); Glyph? alt = font.get_glyph_by_name (alt_name); - if (likely (alt != null)) { + if (alt != null) { GlyphSequence replacement = new GlyphSequence (); replacement.add (alt); ligature_sequence.replace (old, replacement);
diff --git libbirdfont/GlyphTab.vala(new)
--- /dev/null +++ b/libbirdfont/GlyphTab.vala @@ -1,1 +1,156 @@ + /* + Copyright (C) 2016 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + using Cairo; + using Math; + + namespace BirdFont { + + public class GlyphTab : FontDisplay { + + public GlyphCollection glyphs; + + public GlyphTab (GlyphCollection glyphs) { + this.glyphs = glyphs; + } + + public override string get_name () { + return glyphs.get_current ().get_name (); + } + + public override string get_label () { + return glyphs.get_current ().get_label (); + } + + public override void close () { + glyphs.get_current ().close (); + } + + public override bool has_scrollbar () { + return glyphs.get_current ().has_scrollbar (); + } + + public override void update_scrollbar () { + glyphs.get_current ().update_scrollbar (); + } + + public override void scroll_to (double percent) { + glyphs.get_current ().scroll_to (percent); + } + + public override void draw (WidgetAllocation allocation, Context cr) { + glyphs.get_current ().draw (allocation, cr); + } + + public override void selected_canvas () { + glyphs.get_current ().selected_canvas (); + } + + public override void key_press (uint keyval) { + glyphs.get_current ().key_press (keyval); + } + + public override void key_release (uint keyval) { + glyphs.get_current ().key_release (keyval); + } + + public override void motion_notify (double x, double y) { + glyphs.get_current ().motion_notify (x, y); + } + + public override void button_release (int button, double x, double y) { + glyphs.get_current ().button_release (button, x, y); + } + + public override void button_press (uint button, double x, double y) { + glyphs.get_current ().button_press (button, x, y); + } + + public override void double_click (uint button, double ex, double ey) { + glyphs.get_current ().double_click (button, ex, ey); + } + + public override void magnify (double magnification) { + glyphs.get_current ().magnify (magnification); + } + + public override void tap_down (int finger, int x, int y) { + glyphs.get_current ().tap_down (finger, x, y); + } + + public override void tap_up (int finger, int x, int y) { + glyphs.get_current ().tap_up (finger, x, y); + } + + public override void tap_move (int finger, int x, int y) { + glyphs.get_current ().tap_move (finger, x, y); + } + + public override void zoom_in () { + glyphs.get_current ().zoom_in (); + } + + public override void zoom_out () { + glyphs.get_current ().zoom_out (); + } + + public override void zoom_max () { + glyphs.get_current ().zoom_max (); + } + + public override void zoom_min () { + glyphs.get_current ().zoom_min (); + } + + public override void move_view (double x, double y) { + glyphs.get_current ().move_view (x, y); + } + + public override void reset_zoom () { + glyphs.get_current ().reset_zoom (); + } + + public override void store_current_view () { + glyphs.get_current ().store_current_view (); + } + + public override void restore_last_view () { + glyphs.get_current ().restore_last_view (); + } + + public override void next_view () { + glyphs.get_current ().next_view (); + } + + public override void scroll_wheel (double x, double y, + double pixeldelta_x, double pixeldelta_y) { + glyphs.get_current ().scroll_wheel (x, y, pixeldelta_x, pixeldelta_y); + } + + public override void undo () { + glyphs.get_current ().undo (); + } + + public override void redo () { + glyphs.get_current ().redo (); + } + + /** returns false if bindings to a single key works in the display. */ + public override bool needs_modifier () { + return glyphs.get_current ().needs_modifier (); + } + } + + }
diff --git libbirdfont/Gradient.vala(new)
--- /dev/null +++ b/libbirdfont/Gradient.vala @@ -1,1 +1,54 @@ + /* + Copyright (C) 2015 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + using Cairo; + using Math; + + namespace BirdFont { + + public class Gradient : GLib.Object { + public double x1; + public double y1; + public double x2; + public double y2; + + public Gee.ArrayList<Stop> stops; + + public int id = -1; + + public Gradient () { + x1 = 0; + y1 = 0; + x2 = 0; + y2 = 0; + stops = new Gee.ArrayList<Stop> (); + } + + public Gradient copy () { + Gradient g = new Gradient (); + g.x1 = x1; + g.y1 = y1; + g.x2 = x2; + g.y2 = y2; + + foreach (Stop s in stops) { + g.stops.add (s.copy ()); + } + + return g; + } + } + + }
diff --git libbirdfont/Help.vala(deleted)
--- a/libbirdfont/Help.vala +++ /dev/null @@ -1,124 +1,1 @@ - /* - Copyright (C) 2016 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using Cairo; - - namespace BirdFont { - - public class Help { - - bool visible; - TextArea help_text; - Text close; - const int box_margin = 7; - - public Help () { - string v = Preferences.get ("help_visible"); - visible = v == "true"; - help_text = create_help_text (t_("BirdFont is a font editor.")); - - Color color = Theme.get_color ("Menu Foreground"); - close = new Text ("close", 30, 0, color); - close.load_font ("icons.bf"); - } - - public static TextArea create_help_text (string lines) { - Color color = Theme.get_color ("Menu Foreground"); - TextArea text = new TextArea (17, color); - text.min_width = 200; - text.min_height = 100; - text.width = 200; - text.height = 100; - text.allocation = new WidgetAllocation.for_area (0, 0, (int)text.min_width, (int)text.min_height); - text.set_text (lines); - text.layout (); - text.set_editable (false); - text.draw_border = false; - return text; - } - - public void set_help_text (TextArea text) { - if (text != help_text && is_visible ()) { - help_text = text; - GlyphCanvas.redraw (); - } - } - - public void set_visible (bool v) { - visible = v; - Preferences.set ("help_visible", @"$v"); - } - - public bool is_visible () { - return visible; - } - - public void draw (Context cr, WidgetAllocation allocation) { - int margin_x = 10; - int margin_y = 15; - - help_text.allocation = allocation; - help_text.widget_x = allocation.width - help_text.width - margin_x; - help_text.widget_y = allocation.height - help_text.height - margin_y; - - draw_box (cr, allocation); - - help_text.draw (cr); - - double close_x = allocation.width; - close_x -= close.get_sidebearing_extent (); - close_x -= margin_x; - close_x -= box_margin; - - double close_y = allocation.height; - close_y -= help_text.height; - close_y -= margin_y; - close_y -= 2 * box_margin; - - close.widget_x = close_x; - close.widget_y = close_y; - close.draw (cr); - } - - public bool button_press (uint button, double x, double y) { - if (close.widget_x < x < close.widget_x + close.get_sidebearing_extent () - && close.widget_y < y < close.widget_y + close.font_size) { - - set_visible (false); - GlyphCanvas.redraw (); - return true; - } - - return false; - } - - public void draw_box (Context cr, WidgetAllocation allocation) { - cr.save(); - - Theme.color (cr, "Menu Background"); - double x, y, w, h; - x = help_text.widget_x - 2 * box_margin; - y = help_text.widget_y - 2 * box_margin; - w = help_text.width + 2 * box_margin; - h = help_text.height + 2 * box_margin; - Widget.draw_rounded_rectangle (cr, x, y, w, h, 7); - cr.fill (); - - cr.restore(); - } - - } - - }
--- a/libbirdfont/HiddenTools.vala +++ b/libbirdfont/HiddenTools.vala @@ -67,6 +67,8 @@ DrawingTools.bezier_tool.move_handle_on_axis (); } else if (t is PenTool || t is PointTool) { PenTool.move_handle_on_axis (); + } else if (t is BackgroundTool) { + BackgroundTool.move_handle_on_axis (); } }); move_along_axis.is_tool_modifier = true;
--- a/libbirdfont/ImportUtils.vala +++ b/libbirdfont/ImportUtils.vala @@ -34,7 +34,6 @@ BirdFont.current_font = new Font (); BirdFont.current_glyph_collection = new GlyphCollection.with_glyph ('\0', ""); MainWindow.init (); - SvgType type = SvgType.REGULAR; if (arg.length < 3) { print_import_help (arg); @@ -44,11 +43,7 @@ bf_file = build_absoulute_path (arg[1]); for (int i = 2; i < arg.length; i++) { - if (arg[i] == "--color") { - type = SvgType.COLOR; - } else { - svg_files.add (arg[i]); - } + svg_files.add (arg[i]); } bf = File.new_for_path (bf_file); @@ -84,7 +79,7 @@ foreach (string f in svg_files) { svg = File.new_for_path (f); - imported = import_svg_file (font, svg, type); + imported = import_svg_file (font, svg); if (!imported) { stdout.printf (t_("Failed to import") + " " + f + "\n"); @@ -98,7 +93,7 @@ return 0; } - public static bool import_svg_file (Font font, File svg_file, SvgType type) { + public static bool import_svg_file (Font font, File svg_file) { string file_name = (!) svg_file.get_basename (); string glyph_name; StringBuilder n; @@ -163,14 +158,10 @@ stdout.printf (@"$(glyph.version_id)"); stdout.printf ("\n"); - if (type == SvgType.COLOR) { - SvgParser.import_color_svg (glyph, (!) svg_file.get_path ()); - } else { - SvgParser.import_svg ((!) svg_file.get_path ()); - } + SvgParser.import_svg ((!) svg_file.get_path ()); return true; } }
--- a/libbirdfont/KerningClasses.vala +++ b/libbirdfont/KerningClasses.vala @@ -78,6 +78,27 @@ warning (e.message); } } + } + + /** Copy kerning pairs for newly created spacing classes. */ + public void copy_single_kerning_pairs (string from_spacing_class, string to_spacing_class) { + double? kerning; + + foreach (string left in single_kerning_letters_left) { + kerning = get_kerning_for_single_glyphs (left, from_spacing_class); + + if (kerning != null) { + set_kerning_for_single_glyphs (left, to_spacing_class, (!) kerning); + } + } + + foreach (string right in single_kerning_letters_right) { + kerning = get_kerning_for_single_glyphs (from_spacing_class, right); + + if (kerning != null) { + set_kerning_for_single_glyphs (to_spacing_class, right, (!) kerning); + } + } } /** Class based gpos kerning. */ @@ -125,36 +146,21 @@ } return 0; - } - - public void update_space_class (string c) { - double? k; - - foreach (string l in single_kerning_letters_left) { - k = get_kerning_for_single_glyphs (l, c); - - if (k != null) { - set_kerning_for_single_glyphs (l, c, (!) k); - } - } - - foreach (string r in single_kerning_letters_right) { - k = get_kerning_for_single_glyphs (c, r); - - if (k != null) { - set_kerning_for_single_glyphs (c, r, (!) k); - } - } } public double? get_kerning_for_single_glyphs (string first, string next) { double? k = null; + double? kerning = null; string left = GlyphRange.serialize (first); string right = GlyphRange.serialize (next); foreach (string l in get_spacing_class (left)) { foreach (string r in get_spacing_class (right)) { - k = single_kerning.get (@"$l - $r"); + kerning = single_kerning.get (@"$l - $r"); + + if (kerning != null) { + k = kerning; + } } }
--- a/libbirdfont/KerningDisplay.vala +++ b/libbirdfont/KerningDisplay.vala @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 2014 2015 2016 Johan Mattsson + Copyright (C) 2012 2014 2015 Johan Mattsson This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -45,8 +45,10 @@ public bool adjust_side_bearings = false; public bool right_side_bearing = true; + + public static bool right_to_left = false; - TextArea description; + WidgetAllocation allocation = new WidgetAllocation (); public KerningDisplay () { GlyphSequence w = new GlyphSequence (); @@ -56,8 +58,6 @@ redo_items = new Gee.ArrayList <UndoItem> (); w.set_otf_tags (KerningTools.get_otf_tags ()); first_row.add (w); - - description = Help.create_help_text (t_("Kerning is the process of adjusting the space between two letters. You can cahnge the space between one letter and all other letters in the spacing tab.")); } public GlyphSequence get_first_row () { @@ -136,6 +136,8 @@ Font font = BirdFont.get_current_font (); double item_size = 1.0 / KerningTools.font_size; double item_size2 = 2.0 / KerningTools.font_size; + + this.allocation = allocation; i = 0; @@ -154,12 +156,16 @@ row_height = get_row_height (); alpha = 1; - y = get_row_height () + font.base_line + 20; + y = get_row_height () - font.base_line + 20; x = 20; w = 0; prev = null; kern = 0; + if (right_to_left) { + x = (allocation.width - 20) / KerningTools.font_size; + } + foreach (GlyphSequence word in get_all_rows ()) { wi = 0; word_with_ligatures = word.process_ligatures (font); @@ -192,8 +198,14 @@ cr.save (); glyph.add_help_lines (); - cr.translate (kern + x - glyph.get_lsb () - Glyph.xc (), glyph.get_baseline () + y - Glyph.yc ()); - glyph.draw_layers (cr); + + if (right_to_left) { + cr.translate (-kern + x - glyph.get_lsb () - glyph.get_width () - Glyph.xc (), glyph.get_baseline () + y - Glyph.yc ()); + } else { + cr.translate (kern + x - glyph.get_lsb () - Glyph.xc (), glyph.get_baseline () + y - Glyph.yc ()); + } + + glyph.draw_paths (cr); cr.restore (); w = glyph.get_width (); @@ -201,7 +213,11 @@ // handle if (first_row && (active_handle == i || selected_handle == i)) { - x2 = x + kern / 2.0; + if (right_to_left) { + x2 = x - kern / 2.0; + } else { + x2 = x + kern / 2.0; + } cr.save (); @@ -251,7 +267,11 @@ cr.restore (); } - x += w + kern; + if (right_to_left) { + x -= w + kern; + } else { + x += w + kern; + } // caption if (g == null || ((!)g).is_empty ()) { @@ -272,7 +292,7 @@ // draw caret if (first_row) { x2 = x; - caret_y = get_row_height () + font.base_line + 20; + caret_y = get_row_height () - font.base_line + 20; cr.save (); cr.set_line_width (1.0 / KerningTools.font_size); Theme.color_opacity (cr, "Foreground 1", 0.5); @@ -285,7 +305,13 @@ } y += row_height + 20; - x = 20; + + if (right_to_left) { + x = (allocation.width - 20) / KerningTools.font_size; + } else { + x = 20; + } + first_row = false; if (y > allocation.height) { @@ -492,7 +518,6 @@ public override void selected_canvas () { KeyBindings.set_require_modifier (true); - MainWindow.get_help ().set_help_text (description); } public void add_kerning_class (int index) { @@ -577,6 +602,8 @@ kd = (KerningDisplay) fd; kd.set_selected_handle (kd.selected_handle - 1); } + + GlyphCanvas.redraw (); } public static void next_pair () { @@ -588,6 +615,7 @@ if (fd is SpacingTab) { st = (SpacingTab) fd; + if (st.right_side_bearing) { st.right_side_bearing = false; } else { @@ -598,6 +626,8 @@ kd = (KerningDisplay) fd; kd.set_selected_handle (kd.selected_handle + 1); } + + GlyphCanvas.redraw (); } private static string round (double d) { @@ -772,6 +802,11 @@ k = (ex - last_handle_x) / y; // y-axis is for variable precision k /= KerningTools.font_size; + + if (right_to_left) { + k *= -1; + } + set_space (selected_handle, k); GlyphCanvas.redraw (); } @@ -789,7 +824,11 @@ Glyph glyph = new Glyph.no_lines (""); double fs = KerningTools.font_size; double x = 20; - + + if (right_to_left) { + x = (allocation.width - 20) / KerningTools.font_size; + } + GlyphRange? gr_left, gr_right; Glyph? prev = null; @@ -825,8 +864,12 @@ kern = get_kerning_for_pair (((!)prev).get_name (), ((!)g).get_name (), gr_left, gr_right); } - - d = Math.pow (fs * (x + kern) - ex, 2); + + if (right_to_left) { + d = Math.pow (fs * (x - kern) - ex, 2); + } else { + d = Math.pow (fs * (x + kern) - ex, 2); + } if (d < min) { min = d; @@ -848,7 +891,13 @@ } prev = g; - x += w + kern; + + if (right_to_left) { + x -= w + kern; + } else { + x += w + kern; + } + i++; col_index++; }
--- a/libbirdfont/KerningRange.vala +++ b/libbirdfont/KerningRange.vala @@ -113,7 +113,8 @@ TextListener listener = new TextListener (t_("Kerning class"), ranges, t_("Set")); listener.signal_text_input.connect ((text) => { set_ranges (text); - Toolbox.redraw_tool_box (); + KerningTools.classes.clear_cache (); + KerningTools.classes.redraw (); }); listener.signal_submit.connect (() => {
--- a/libbirdfont/KerningTools.vala +++ b/libbirdfont/KerningTools.vala @@ -54,7 +54,12 @@ active_otf_features = new OtfTags (); Expander kerning_tools = new Expander (t_("Kerning Tools")); - classes = new Expander (); + + if (is_null (classes)) { + classes = new Expander (); + update_kerning_classes (); + } + expanders = new Gee.ArrayList<Expander> (); Expander font_name = new Expander (); @@ -86,6 +91,7 @@ KerningRange kr = new KerningRange (f, @"$label $(++next_class)"); classes.add_tool (kr); self.set_selected (false); + classes.clear_cache (); classes.redraw (); }); kerning_tools.add_tool (new_kerning_class); @@ -121,6 +127,15 @@ }); kerning_tools.add_tool (insert_unicode); + Tool right_to_left = new Tool ("right_to_left", t_("Right to left")); + right_to_left.select_action.connect ((self) => { + KerningDisplay d = MainWindow.get_kerning_display (); + d.right_to_left = !d.right_to_left; + right_to_left.set_selected (d.right_to_left); + GlyphCanvas.redraw (); + }); + kerning_tools.add_tool (right_to_left); + string empty_kerning_text = t_("Open a text file with kerning strings first."); previous_kerning_string = new Tool ("previous_kerning_string", t_("Previous kerning string")); @@ -176,8 +191,8 @@ expanders.add (font_name); expanders.add (zoom_expander); expanders.add (kerning_tools); + expanders.add (otf_features); expanders.add (classes); - expanders.add (otf_features); } public static void add_otf_label (string tag) { @@ -266,6 +281,9 @@ add_unique_class (kr); } } + + classes.clear_cache (); + classes.redraw (); } private static void remove_all_kerning_classes () {
--- a/libbirdfont/LabelTool.vala +++ b/libbirdfont/LabelTool.vala @@ -79,8 +79,7 @@ }); } - public override void clear_cache () { - base.clear_cache (); + void clear_cache () { selected_cache = null; deselected_cache = null; } @@ -92,29 +91,31 @@ if (is_selected ()) { if (selected_cache == null) { - selected_cache = Screen.create_background_surface ((int) w, (int) h + 2); + selected_cache = Screen.create_background_surface ((int) (w * Screen.get_scale ()), (int) ((h + 2) * Screen.get_scale ())); Context c = new Context ((!) selected_cache); - c.scale (1 / Screen.get_scale (), 1 / Screen.get_scale ()); + c.scale (Screen.get_scale (), Screen.get_scale ()); draw_tool_surface (c, x, 2, true); } cr.save (); + cr.scale (1 / Screen.get_scale (), 1 / Screen.get_scale ()); cr.set_antialias (Cairo.Antialias.NONE); - cr.set_source_surface ((!) selected_cache, 0, (int) y - 2); + cr.set_source_surface ((!) selected_cache, 0, (int) ((y - 2) * Screen.get_scale ())); cr.paint (); cr.restore (); } else { if (deselected_cache == null) { - deselected_cache = Screen.create_background_surface ((int) w, (int) h + 2); + deselected_cache = Screen.create_background_surface ((int) (w * Screen.get_scale ()), (int) ((h + 2) * Screen.get_scale ())); Context c = new Context ((!) deselected_cache); - c.scale (1 / Screen.get_scale (), 1 / Screen.get_scale ()); + c.scale (Screen.get_scale (), Screen.get_scale ()); draw_tool_surface (c, x, 2, false); } cr.save (); + cr.scale (1 / Screen.get_scale (), 1 / Screen.get_scale ()); cr.set_antialias (Cairo.Antialias.NONE); - cr.set_source_surface ((!) deselected_cache, 0, (int) y - 2); + cr.set_source_surface ((!) deselected_cache, 0, (int) ((y - 2) * Screen.get_scale ())); cr.paint (); cr.restore (); }
diff --git libbirdfont/Layer.vala(new)
--- /dev/null +++ b/libbirdfont/Layer.vala @@ -1,1 +1,163 @@ + /* + Copyright (C) 2015 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + namespace BirdFont { + + public class Layer : GLib.Object { + public PathList paths; + public Gee.ArrayList<Layer> subgroups; + public bool visible = true; + public string name = "Layer"; + + public bool is_counter = false; + public Gradient? gradient = null; + public bool single_path = false; + + public Layer () { + paths = new PathList (); + subgroups = new Gee.ArrayList<Layer> (); + } + + public int index_of (Layer sublayer) { + return subgroups.index_of (sublayer); + } + + public PathList get_all_paths () { + PathList p = new PathList (); + + p.append (paths); + + foreach (Layer sublayer in subgroups) { + p.append (sublayer.get_all_paths ()); + } + + return p; + } + + public PathList get_visible_paths () { + PathList p = new PathList (); + + if (visible) { + p.append (paths); + } + + foreach (Layer sublayer in subgroups) { + if (sublayer.visible) { + p.append (sublayer.get_all_paths ()); + } + } + + return p; + } + + public void add_layer (Layer layer) { + subgroups.add (layer); + } + + public void add_path (Path path) { + paths.add (path); + } + + public void remove_path (Path path) { + paths.remove (path); + foreach (Layer sublayer in subgroups) { + sublayer.remove_path (path); + } + } + + public void remove_layer (Layer layer) { + subgroups.remove (layer); + foreach (Layer sublayer in subgroups) { + sublayer.remove_layer (layer); + } + } + + public Layer copy () { + Layer layer = new Layer (); + + layer.name = name; + layer.paths = paths.copy (); + layer.visible = visible; + + foreach (Layer l in subgroups) { + layer.subgroups.add (l.copy ()); + } + + if (gradient != null) { + layer.gradient = ((!) gradient).copy (); + } + + layer.single_path = single_path; + + return layer; + } + + public void get_boundaries (out double x, out double y, out double w, out double h) { + double px, py, px2, py2; + + px = Glyph.CANVAS_MAX; + py = Glyph.CANVAS_MAX; + px2 = Glyph.CANVAS_MIN; + py2 = Glyph.CANVAS_MIN; + + foreach (Path p in get_all_paths ().paths) { + if (px > p.xmin) { + px = p.xmin; + } + + if (py > p.ymin) { + py = p.ymin; + } + + if (px2 < p.xmax) { + px2 = p.xmax; + } + + if (py2 < p.ymax) { + py2 = p.ymax; + } + } + + w = px2 - px; + h = py2 - py; + x = px; + y = py2; + } + + public void print (int indent = 0) { + foreach (Path p in paths.paths) { + for (int i = 0; i < indent; i++) { + stdout.printf ("\t"); + } + stdout.printf (@"Path open: $(p.is_open ())"); + + if (p.color != null) { + stdout.printf (" %s", ((!) p.color).to_rgb_hex ()); + } + + stdout.printf ("\n"); + } + + foreach (Layer l in subgroups) { + for (int i = 0; i < indent; i++) { + stdout.printf ("\t"); + } + stdout.printf ("%s\n", l.name); + l.print (indent + 1); + } + } + } + + }
--- a/libbirdfont/LayerLabel.vala +++ b/libbirdfont/LayerLabel.vala @@ -13,7 +13,6 @@ */ using Cairo; - using SvgBird; namespace BirdFont { @@ -27,15 +26,9 @@ /** Add margin when layer is moves. */ bool active_layer = false; - - static TextArea? help_text_hide = null; public LayerLabel (Layer layer) { base (); - - if (help_text_hide == null) { - update_description (); - } this.layer = layer; this.label = layer.name; @@ -46,10 +39,10 @@ panel_press_action.connect ((selected, button, tx, ty) => { if (y <= ty <= y + h) { - if (tx >= w - 30) { + if (tx >= w - 30 * Toolbox.get_scale ()) { DrawingTools.deselect_layers (); remove_layer (); - } else if (tx < 25) { + } if (tx < 25 * Toolbox.get_scale ()) { layer.visible = !layer.visible; GlyphCanvas.redraw (); BirdFont.get_current_font ().touch (); @@ -81,13 +74,6 @@ MainWindow.get_toolbox ().update_expanders (); redraw (); - } - - if (y <= ty <= y + h) { - if (tx < 25 && help_text_hide != null) { - Help help = MainWindow.get_help (); - help.set_help_text ((!) help_text_hide); - } } return false; @@ -97,16 +83,7 @@ active_layer = false; }); } - - void update_description () { - help_text_hide = Help.create_help_text (t_("Hide the layer.")); - } - - public override void clear_cache () { - base.clear_cache (); - update_description (); - } - + void move_layer_up () { int i; Glyph g = MainWindow.get_current_glyph ();
diff --git libbirdfont/LayerUtils.vala(deleted)
--- a/libbirdfont/LayerUtils.vala +++ /dev/null @@ -1,129 +1,1 @@ - /* - Copyright (C) 2015 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using SvgBird; - - namespace BirdFont { - - public class LayerUtils { - - public static Gee.ArrayList<SvgBird.Object> get_visible_objects (Layer layer) { - ObjectGroup group = new ObjectGroup (); - add_visible_objects (layer, group); - return group.objects; - } - - public static void add_visible_objects (Layer layer, ObjectGroup objects) { - foreach (SvgBird.Object o in layer.objects) { - if (o is Layer) { - Layer sublayer = (Layer) o; - - if (sublayer.visible) { - add_visible_objects (sublayer, objects); - } - } else { - if (o.visible) { - objects.add (o); - } - } - } - } - - public static PathList get_all_paths (Layer layer) { - PathList paths = new PathList (); - add_paths_to_group (layer, paths); - return paths; - } - - public static void add_paths_to_group (Layer layer, PathList paths) { - foreach (SvgBird.Object o in layer.objects) { - if (o is PathObject) { - PathObject p = (PathObject) o; - paths.add (p.get_path ()); - } else if (o is Layer) { - add_visible_paths_to_group ((Layer) o, paths); - } - } - } - - public static PathList get_visible_paths (Layer layer) { - PathList paths = new PathList (); - add_visible_paths_to_group (layer, paths); - return paths; - } - - public static void add_visible_paths_to_group (Layer layer, PathList paths) { - foreach (SvgBird.Object o in layer.objects) { - if (o.visible) { - if (o is PathObject) { - PathObject p = (PathObject) o; - paths.add (p.get_path ()); - } else if (o is Layer) { - add_visible_paths_to_group ((Layer) o, paths); - } - } - } - } - - public static void add_path (Layer layer, Path path) { - PathObject p = new PathObject.for_path (path); - layer.add_object (p); - } - - public static void append_paths (Layer layer, PathList path_list) { - foreach (Path p in path_list.paths) { - add_path (layer, p); - } - } - - private static PathObject? get_object_path (Layer layer, Path path) { - foreach (SvgBird.Object o in layer.objects) { - if (o is PathObject) { - PathObject p = (PathObject) o; - if (p.get_path () == path) { - return p; - } - } - } - - return null; - } - - public static void remove_path (Layer layer, Path path) { - PathObject? p = get_object_path (layer, path); - - if (p != null) { - layer.objects.remove ((!) p); - } - - foreach (SvgBird.Layer sublayer in layer.get_sublayers ()) { - remove_path (sublayer, path); - } - } - - public static PathList get_paths_in_layer (Layer layer) { - PathList paths = new PathList (); - - foreach (SvgBird.Object object in layer.objects) { - if (object is PathObject) { - paths.add (((PathObject) object).get_path ()); - } - } - - return paths; - } - } - - }
--- a/libbirdfont/LicenseDialog.vala +++ b/libbirdfont/LicenseDialog.vala @@ -28,11 +28,11 @@ static const double margin = 20; public LicenseDialog () { - Color color = Theme.get_color ("Text Tool Box"); - agreement = new TextArea (font_size, color); + agreement = new TextArea (font_size); agreement.min_width = 300; agreement.set_editable (false); agreement.draw_border = false; + agreement.text_color = Theme.get_color ("Text Tool Box"); agreement.set_text ("BirdFont is developed with donations, please consider donating to the project.\n\nThis is the freeware version of BirdFont. You may use it for creating fonts under the SIL Open Font License.\n\nWhich license do you want to use for your font?"); decline = new Button ("Commercial License");
--- a/libbirdfont/Ligatures.vala +++ b/libbirdfont/Ligatures.vala @@ -58,10 +58,6 @@ lig = new GlyphSequence (); foreach (string n in font.get_names (ligature)) { - if (n == "space") { - n = " "; - } - gc = font.get_glyph_collection_by_name (n); if (gc == null) { @@ -73,10 +69,6 @@ gs = new GlyphSequence (); foreach (string s in subst_names) { - if (s == "space") { - s = " "; - } - gc = font.get_glyph_collection_by_name (s); if (gc == null) {
--- a/libbirdfont/LoadCallback.vala +++ b/libbirdfont/LoadCallback.vala @@ -85,8 +85,6 @@ f.delete_backup (); f = BirdFont.new_font (); - - MainWindow.clear_glyph_cache (); f.set_file ((!) fn); Preferences.add_recent_files ((!) fn);
--- a/libbirdfont/MainWindow.vala +++ b/libbirdfont/MainWindow.vala @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 2014 2016 Johan Mattsson + Copyright (C) 2012 2014 Johan Mattsson This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -32,7 +32,7 @@ public static Dialog dialog; public static SpacingTab spacing_tab; public static Task blocking_background_task; - public static Help help; + public static Scrollbar scrollbar; /** Number of pixels per mm */ public static double units = 1; @@ -56,15 +56,15 @@ dialog = new Dialog (); spacing_tab = new SpacingTab (); blocking_background_task = new Task (null); - help = new Help (); + scrollbar = new Scrollbar (); tools.select_tool (DrawingTools.bezier_tool); - } - - public static Help get_help () { - return help; } + public static bool has_scrollbar () { + return scrollbar.is_visible (); + } + public static void abort_task () { blocking_background_task.cancel (); } @@ -112,7 +112,14 @@ } } - public static void show_message (string text) { + public static void show_message (string text) { + Tab t = MainWindow.get_tab_bar ().get_selected_tab (); + string tab_name = t.get_display ().get_name (); + + if (tab_name == "Preview") { + MenuTab.select_overview (); + } + MessageDialog md = new MessageDialog (text); show_dialog (md); } @@ -221,16 +228,6 @@ public static bool select_tab (Tab t) { return tabs.selected_open_tab (t); - } - - public static OverView get_overview () { - foreach (Tab t in tabs.tabs) { - if (t.get_display () is OverView) { - return (OverView) t.get_display (); - } - } - - return overview; } public static SpacingClassTab get_spacing_class_tab () { @@ -259,40 +256,27 @@ public static void hide_scrollbar () { if (!is_null (MainWindow.native_window)) { - MainWindow.native_window.set_scrollbar_size (-1); + MainWindow.scrollbar.set_size (-1); } } public static void show_scrollbar () { if (!is_null (MainWindow.native_window)) { - MainWindow.native_window.set_scrollbar_size (scrollbar_size); + MainWindow.scrollbar.set_size (scrollbar_size); } } public static void set_scrollbar_size (double size) { if (!is_null (MainWindow.native_window)) { scrollbar_size = size; - MainWindow.native_window.set_scrollbar_size (size); + MainWindow.scrollbar.set_size (size); } } public static void set_scrollbar_position (double position) { if (!is_null (MainWindow.native_window)) { - MainWindow.native_window.set_scrollbar_position (position); + MainWindow.scrollbar.set_position (position); } - } - - /** Reaload all paths and help lines from disk. */ - public static void clear_glyph_cache () { - Glyph g; - foreach (Tab t in get_tab_bar ().tabs) { - if (t.get_display () is Glyph) { - g = (Glyph) t.get_display (); - g.add_help_lines (); - } - } - - GlyphCanvas.redraw (); } public static void close_all_tabs () { @@ -346,8 +330,18 @@ public static void set_toolbox (Toolbox tb) { tools = tb; + } + + public static OverView get_overview () { + foreach (Tab t in tabs.tabs) { + if (t.get_display () is OverView) { + return (OverView) t.get_display (); + } + } + + return overview; } } }
--- a/libbirdfont/Menu.vala +++ b/libbirdfont/Menu.vala @@ -1,5 +1,5 @@ /* - Copyright (C) 2014 - 2016 Johan Mattsson + Copyright (C) 2014 2015 Johan Mattsson This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -287,37 +287,17 @@ MenuItem import_svg = add_menu_item (t_("Import SVG file"), "import svg file", "Glyph"); import_svg.action.connect (() => { - SvgParser.import (SvgType.REGULAR); + SvgParser.import (); show_menu = false; }); export_menu.items.add (import_svg); - + MenuItem import_svg_folder = add_menu_item (t_("Import SVG folder"), "import svg folder", ""); import_svg_folder.action.connect (() => { - SvgParser.import_folder (SvgType.REGULAR); + SvgParser.import_folder (); show_menu = false; }); export_menu.items.add (import_svg_folder); - - if (BirdFont.has_argument ("--test")) { - MenuItem import_color_svg; - import_color_svg = add_menu_item (t_("Import SVG file") + ", " + t_("color"), - "import svg file color", "Glyph"); - import_color_svg.action.connect (() => { - SvgParser.import (SvgType.COLOR); - show_menu = false; - }); - export_menu.items.add (import_color_svg); - - MenuItem import_svg_color_folder; - import_svg_color_folder = add_menu_item (t_("Import SVG folder") + ", " + t_("color"), - "import svg folder color", ""); - import_svg_color_folder.action.connect (() => { - SvgParser.import_folder (SvgType.COLOR); - show_menu = false; - }); - export_menu.items.add (import_svg_color_folder); - } MenuItem import_background_image = add_menu_item (t_("Import Background Image"), "import background image"); import_background_image.action.connect (() => { @@ -412,7 +392,12 @@ MenuItem next_kerning_pair = add_menu_item (t_("Select Next Kerning Pair"), "select next kerning pair"); next_kerning_pair.action.connect (() => { - KerningDisplay.next_pair (); + if (KerningDisplay.right_to_left) { + KerningDisplay.previous_pair (); + } else { + KerningDisplay.next_pair (); + } + show_menu = false; }); next_kerning_pair.add_display("Kerning"); @@ -421,7 +406,12 @@ MenuItem previous_kerning_pair = add_menu_item (t_("Select Previous Kerning Pair"), "select previous kerning pair"); previous_kerning_pair.action.connect (() => { - KerningDisplay.previous_pair (); + if (KerningDisplay.right_to_left) { + KerningDisplay.next_pair (); + } else { + KerningDisplay.previous_pair (); + } + show_menu = false; }); previous_kerning_pair.add_display("Kerning"); @@ -509,16 +499,7 @@ show_menu = false; }); menu.items.add (version); - - MenuItem help = add_menu_item (t_("Help"), "help"); - help.action.connect (() => { - Help help_box = MainWindow.get_help (); - help_box.set_visible (!help_box.is_visible ()); - GlyphCanvas.redraw (); - show_menu = false; - }); - menu.items.add (help); - + set_current_menu (menu); top_menu = menu;
--- a/libbirdfont/MenuItem.vala +++ b/libbirdfont/MenuItem.vala @@ -23,8 +23,8 @@ public double y; // key bindings - public virtual uint modifiers { get; set; default = NONE; } - public virtual unichar key { get; set; default = '\0'; } + public uint modifiers = NONE; + public unichar key = '\0'; public Gee.ArrayList<string> displays = new Gee.ArrayList<string> ();
--- a/libbirdfont/MenuTab.vala +++ b/libbirdfont/MenuTab.vala @@ -11,8 +11,6 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. */ - - using SvgBird; namespace BirdFont { @@ -124,7 +122,8 @@ Font current_font = BirdFont.get_current_font (); string ttf_name = ExportSettings.get_file_name (current_font) + ".ttf"; string ttf_name_mac = ExportSettings.get_file_name_mac (current_font) + ".ttf"; - + + print (@"$ttf_name == $ttf_name_mac"); if (ttf_name == ttf_name_mac) { MainWindow.show_message (t_("You need to choose a different name for the TTF file with Mac adjustmets.")); ttf_name_mac = ExportSettings.get_file_name_mac (current_font) + " Mac.ttf"; @@ -211,7 +210,12 @@ public static void signal_file_exported () { IdleSource idle = new IdleSource (); idle.set_callback (() => { - export_callback.file_exported (); + export_callback.file_exported (); + + if (ExportTool.error_message != null) { + MainWindow.show_message (t_("Can't create TTF font.") + "\n" + (!) ExportTool.error_message); + } + return false; }); idle.attach (null); @@ -276,7 +280,6 @@ } DrawingTools.set_stroke_tool_visibility (); - string lock_grid = f.settings.get_setting ("lock_grid"); bool lg = bool.parse (lock_grid); @@ -328,7 +331,6 @@ MainWindow.get_toolbox ().update_expanders (); MainWindow.get_toolbox ().update_all_expanders (); Toolbox.redraw_tool_box (); - OverViewItem.glyph_scale = 1; } // FIXME: background thread @@ -436,6 +438,8 @@ } else { MainWindow.show_dialog (new SaveDialog (dialog)); } + + MainWindow.native_window.update_window_size (); } public static void show_export_settings_tab () { @@ -647,29 +651,29 @@ Gee.ArrayList<Path> paths = new Gee.ArrayList<Path> (); // selected objects - foreach (Path p in g.get_active_paths ()) { + foreach (Path p in g.active_paths) { paths.add (PenTool.simplify (p, false, PenTool.simplification_threshold)); } // selected segments if (paths.size == 0) { - foreach (Path p in g.get_active_paths ()) { + foreach (Path p in g.get_all_paths ()) { g.add_active_path (null, p); } - foreach (Path p in g.get_active_paths ()) { + foreach (Path p in g.active_paths) { paths.add (PenTool.simplify (p, true, PenTool.simplification_threshold)); } } g.store_undo_state (); - foreach (SvgBird.Object o in g.active_paths) { - g.layers.remove (o); + foreach (Path p in g.active_paths) { + g.layers.remove_path (p); } - foreach (Path p in g.get_active_paths ()) { - LayerUtils.remove_path (g.layers, p); + foreach (Path p in g.active_paths) { + g.layers.remove_path (p); } foreach (Path p in paths) {
--- a/libbirdfont/MoveTool.vala +++ b/libbirdfont/MoveTool.vala @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 2013 2015 2016 Johan Mattsson + Copyright (C) 2012 2013 Johan Mattsson This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -14,7 +14,6 @@ using Math; using Cairo; - using SvgBird; namespace BirdFont { @@ -38,8 +37,8 @@ public signal void objects_moved (); public signal void objects_deselected (); - public MoveTool (string n) { - base (n, t_("Move paths")); + public MoveTool (string name) { + base (name, t_("Move paths")); selection_changed.connect (() => { update_selection_boundaries (); @@ -95,13 +94,8 @@ g.store_undo_state (); } - foreach (SvgBird.Object p in g.active_paths) { - if (p is PathObject) { - LayerUtils.remove_path (g.layers, ((PathObject) p).get_path ()); - } else { - g.layers.remove (p); - } - + foreach (Path p in g.active_paths) { + g.layers.remove_path (p); g.update_view (); } @@ -115,8 +109,9 @@ public void move (int x, int y) { Glyph glyph = MainWindow.get_current_glyph (); - double dx = Glyph.path_coordinate_x (last_x) - Glyph.path_coordinate_x (x); - double dy = Glyph.path_coordinate_y (last_y) - Glyph.path_coordinate_y (y); + double dx = last_x - x; + double dy = last_y - y; + double p = PenTool.precision; double delta_x, delta_y; if (!move_path) { @@ -126,11 +121,21 @@ if (move_path && (fabs(dx) > 0 || fabs (dy) > 0)) { moved = true; - delta_x = -dx; - delta_y = -dy; + delta_x = Glyph.ivz () * -dx * p; + delta_y = Glyph.ivz () * dy * p; + + foreach (Layer group in glyph.selected_groups) { + if (group.gradient != null) { + Gradient g = (!) group.gradient; + g.x1 += delta_x; + g.x2 += delta_x; + g.y1 += delta_y; + g.y2 += delta_y; + } + } - foreach (SvgBird.Object object in glyph.active_paths) { - object.move (delta_x, delta_y); + foreach (Path path in glyph.active_paths) { + path.move (delta_x, delta_y); } } @@ -157,7 +162,7 @@ if (GridTool.is_visible () && moved) { tie_paths_to_grid (glyph); } else if (GridTool.has_ttf_grid ()) { - foreach (SvgBird.Object p in glyph.active_paths) { + foreach (Path p in glyph.active_paths) { tie_path_to_ttf_grid (p); } } @@ -174,11 +179,8 @@ objects_moved (); DrawingTools.resize_tool.signal_objects_rotated (); - foreach (SvgBird.Object o in glyph.active_paths) { - if (o is PathObject) { - PathObject path = (PathObject) o; - path.get_path ().create_full_stroke (); - } + foreach (Path p in glyph.active_paths) { + p.create_full_stroke (); } } else { objects_deselected (); @@ -187,36 +189,42 @@ public void press (int b, int x, int y) { Glyph glyph = MainWindow.get_current_glyph (); - SvgBird.Object object; + Path p; bool selected = false; - SvgBird.Object? o; + Layer? group; + Layer g; - glyph.store_undo_state (); - double px = Glyph.path_coordinate_x (x); - double py = Glyph.path_coordinate_y (y); - o = glyph.get_object_at (px, py); - - if (o != null) { - object = (!) o; - selected = glyph.active_paths_contains (object); - + glyph.store_undo_state (); + group_selection = false; + + group = glyph.get_path_at (x, y); + + if (group != null) { + g = (!) group; + return_if_fail (g.paths.paths.size > 0); + p = g.paths.paths.get (0); + selected = glyph.active_paths.contains (p); + if (!selected && !KeyBindings.has_shift ()) { glyph.clear_active_paths (); } - if (selected && KeyBindings.has_shift ()) { - glyph.active_paths.remove (object); - } else { - glyph.add_active_object (null, object); - } + foreach (Path lp in g.paths.paths) { + if (selected && KeyBindings.has_shift ()) { + glyph.selected_groups.remove ((!) group); + glyph.active_paths.remove (lp); + } else { + glyph.add_active_path ((!) group, lp); + } + } } else if (!KeyBindings.has_shift ()) { glyph.clear_active_paths (); } + + move_path = true; update_selection_boundaries (); - - move_path = true; - + last_x = x; last_y = y; @@ -230,7 +238,8 @@ selection_changed (); GlyphCanvas.redraw (); } - + + void select_group () { double x1 = Glyph.path_coordinate_x (Math.fmin (selection_x, last_x)); double y1 = Glyph.path_coordinate_y (Math.fmin (selection_y, last_y)); @@ -240,10 +249,10 @@ glyph.clear_active_paths (); - foreach (SvgBird.Object p in glyph.get_objects_in_current_layer ()) { + foreach (Path p in glyph.get_paths_in_current_layer ()) { if (p.xmin > x1 && p.xmax < x2 && p.ymin < y1 && p.ymax > y2) { - if (!p.is_empty ()) { - glyph.add_active_object (null, p); + if (p.points.size > 0) { + glyph.add_active_path (null, p); } } } @@ -264,13 +273,30 @@ get_selection_box_boundaries (out x, out y, out w, out h); - foreach (SvgBird.Object path in glyph.active_paths) { + foreach (Path path in glyph.active_paths) { path.move (glyph.left_limit - x + w / 2, font.base_line - y + h / 2); } update_selection_boundaries (); objects_moved (); GlyphCanvas.redraw (); + } + + static void draw_selection_box (Context cr) { + double x = Math.fmin (selection_x, last_x); + double y = Math.fmin (selection_y, last_y); + + double w = Math.fabs (selection_x - last_x); + double h = Math.fabs (selection_y - last_y); + + cr.save (); + + Theme.color (cr, "Foreground 1"); + cr.set_line_width (2); + cr.rectangle (x, y, w, h); + cr.stroke (); + + cr.restore (); } public static void get_selection_box_boundaries (out double x, out double y, out double w, out double h) { @@ -282,26 +308,23 @@ px2 = -10000; py2 = -10000; - foreach (SvgBird.Object o in glyph.active_paths) { - if (o is PathObject) { - Path p = ((PathObject) o).get_path (); - p.update_region_boundaries (); - - if (px > p.xmin) { - px = p.xmin; - } + foreach (Path p in glyph.active_paths) { + p.update_region_boundaries (); + + if (px > p.xmin) { + px = p.xmin; + } - if (py > p.ymin) { - py = p.ymin; - } + if (py > p.ymin) { + py = p.ymin; + } - if (px2 < p.xmax) { - px2 = p.xmax; - } - - if (py2 < p.ymax) { - py2 = p.ymax; - } + if (px2 < p.xmax) { + px2 = p.xmax; + } + + if (py2 < p.ymax) { + py2 = p.ymax; } } @@ -335,7 +358,7 @@ break; } - foreach (SvgBird.Object path in glyph.active_paths) { + foreach (Path path in glyph.active_paths) { path.move (x * Glyph.ivz (), y * Glyph.ivz ()); } @@ -343,10 +366,10 @@ PenTool.reset_stroke (); update_selection_boundaries (); objects_moved (); - glyph.redraw_area (0, 0, glyph.allocation.width, glyph.allocation.height); + GlyphCanvas.redraw (); } - static void tie_path_to_ttf_grid (SvgBird.Object p) { + static void tie_path_to_ttf_grid (Path p) { double sx, sy, qx, qy; sx = p.xmax; @@ -396,7 +419,7 @@ dx_min = Math.fabs (qx - minx); dx_max = Math.fabs (sx - maxx); - foreach (SvgBird.Object p in g.active_paths) { + foreach (Path p in g.active_paths) { if (dy_min < dy_max) { p.move (0, qy - miny); } else { @@ -415,10 +438,8 @@ public static void update_boundaries_for_selection () { Glyph glyph = MainWindow.get_current_glyph (); - foreach (SvgBird.Object o in glyph.active_paths) { - if (o is PathObject) { - ((PathObject)o).get_path ().update_region_boundaries (); - } + foreach (Path p in glyph.active_paths) { + p.update_region_boundaries (); } } @@ -440,19 +461,14 @@ xc = selection_box_center_x; yc = selection_box_center_y; - foreach (SvgBird.Object p in glyph.active_paths) { - if (p is PathObject) { - Path path = ((PathObject) p).get_path (); - - // FIXME: move to object - if (vertical) { - path.flip_vertical (); - } else { - path.flip_horizontal (); - } - - path.reverse (); + foreach (Path p in glyph.active_paths) { + if (vertical) { + p.flip_vertical (); + } else { + p.flip_horizontal (); } + + p.reverse (); } get_selection_box_boundaries (out xc2, out yc2, out w, out h); @@ -460,7 +476,7 @@ dx = -(xc2 - xc); dy = -(yc2 - yc); - foreach (SvgBird.Object p in glyph.active_paths) { + foreach (Path p in glyph.active_paths) { p.move (dx, dy); } @@ -474,9 +490,9 @@ Glyph g = MainWindow.get_current_glyph (); g.clear_active_paths (); - foreach (SvgBird.Object p in g.get_objects_in_current_layer ()) { - if (!p.is_empty ()) { - g.add_active_object (null, p); + foreach (Path p in g.get_paths_in_current_layer ()) { + if (p.points.size > 0) { + g.add_active_path (null, p); } } @@ -484,23 +500,8 @@ update_selection_boundaries (); objects_moved (); - } - - static void draw_selection_box (Context cr) { - double x = Math.fmin (selection_x, last_x); - double y = Math.fmin (selection_y, last_y); - - double w = Math.fabs (selection_x - last_x); - double h = Math.fabs (selection_y - last_y); - - cr.save (); - Theme.color (cr, "Foreground 1"); - cr.set_line_width (2); - cr.rectangle (x, y, w, h); - cr.stroke (); - cr.restore (); } } }
--- a/libbirdfont/NativeWindow.vala +++ b/libbirdfont/NativeWindow.vala @@ -27,9 +27,6 @@ public abstract void set_clipboard (string data); public abstract void set_inkscape_clipboard (string data); - public abstract void set_scrollbar_size (double size); - public abstract void set_scrollbar_position (double position); - /** Notify the UI about the new font. */ public abstract void font_loaded ();
--- a/libbirdfont/OpenFontFormat/AlternateFeature.vala +++ b/libbirdfont/OpenFontFormat/AlternateFeature.vala @@ -24,7 +24,7 @@ this.tag = tag; this.glyf_table = glyf_table; - alternates = font.alternates.get_alt (tag); + alternates = font.alternates.get_alt_with_glyph (tag, font); alternates.sort ((a, b) => { Alternate alt1 = (Alternate) a;
--- a/libbirdfont/OpenFontFormat/CodePageBits.vala +++ b/libbirdfont/OpenFontFormat/CodePageBits.vala @@ -28,7 +28,7 @@ public CodePageBits () { if (database == null) { - open_database (get_database_file ()); + open_database (get_database_file (), OPEN_READONLY); } } @@ -192,7 +192,7 @@ f.delete (); } - open_database (f); + open_database (f, OPEN_READWRITE); create_tables (); default_range = new PageBit (-1, "null-"); @@ -242,8 +242,8 @@ } } - void open_database (File db_file) { - int rc = Database.open ((!) db_file.get_path (), out database); + void open_database (File db_file, int access) { + int rc = Database.open_v2 ((!) db_file.get_path (), out database, access); db = (!) database; @@ -333,4 +333,4 @@ } } - +
--- a/libbirdfont/OpenFontFormat/ContextualLigature.vala +++ b/libbirdfont/OpenFontFormat/ContextualLigature.vala @@ -169,11 +169,6 @@ gs = new GlyphSequence (); foreach (string s in font.get_names (context)) { - - if (s == "space") { - s = " "; - } - gc = font.get_glyph_collection_by_name (s); if (gc == null) {
--- a/libbirdfont/OpenFontFormat/DirectoryTable.vala +++ b/libbirdfont/OpenFontFormat/DirectoryTable.vala @@ -33,7 +33,6 @@ public Os2Table os_2_table; public PostTable post_table; public LocaTable loca_table; - public SvgTable svg_table; public OffsetTable offset_table; @@ -58,7 +57,6 @@ name_table = new NameTable (); os_2_table = new Os2Table (glyf_table, hmtx_table, hhea_table); post_table = new PostTable (glyf_table); - svg_table = new SvgTable (); id = "Directory table"; @@ -83,7 +81,6 @@ post_table.process (); kern_table.process (); gpos_table.process (glyf_table); - svg_table.process (glyf_table); offset_table.process (); process_directory (); // this table @@ -104,10 +101,6 @@ tables.add (gsub_table); tables.add (os_2_table); - - if (svg_table.has_glyphs ()) { - tables.add (svg_table); - } // tables.append (gdef_table); // invalid table
--- a/libbirdfont/OpenFontFormat/FontData.vala +++ b/libbirdfont/OpenFontFormat/FontData.vala @@ -14,7 +14,7 @@ namespace BirdFont { - public class FontData : GLib.Object { + public class FontData : Object { // Read pointer uint rp = 0;
--- a/libbirdfont/OpenFontFormat/GlyfData.vala +++ b/libbirdfont/OpenFontFormat/GlyfData.vala @@ -16,7 +16,7 @@ namespace BirdFont { - public class CoordinateFlags : GLib.Object { + public class CoordinateFlags { /** TTF coordinate flags. */ public static const uint8 NONE = 0; @@ -43,7 +43,7 @@ public Gee.ArrayList<int16> coordinate_y = new Gee.ArrayList<int16> (); uint16 end_point = 0; - int16 nflags = 0; + uint16 nflags = 0; Glyph glyph; @@ -58,53 +58,48 @@ public GlyfData (Glyph g) { bool process; - PathList qp = g.get_quadratic_paths (); + PathList all_quadratic = g.get_quadratic_paths (); + PathList qp = new PathList (); glyph = g; - foreach (Path p in qp.paths) { + int i = 0; + foreach (Path p in all_quadratic.paths) { if (p.points.size > 0) { - if (!is_empty (p)) { + if (likely (!is_empty (p))) { // Add points at extrema p.add_extrema (); + qp.add (p); + } else { + warning (@"Path number $i is empty in $(glyph.get_name ())"); } + i++; } } - process = true; - - while (process) { - points.clear (); - paths.clear (); - foreach (Path p in qp.paths) { - if (!is_empty (p)) { - paths.add (p); - foreach (EditPoint ep in p.points) { - points.add (ep); - } - } - } - - if (paths.size == 0) { - break; + points.clear (); + paths.clear (); + foreach (Path p in qp.paths) { + paths.add (p); + + foreach (EditPoint ep in p.points) { + points.add (ep); } + } + if (paths.size > 0) { process_end_points (); process_flags (); process_x (); - - // error checking is done here - process = !process_y (); - + process_y (); process_bounding_box (); - } + } } bool is_empty (Path p) { EditPoint? last = null; if (unlikely (p.points.size < 2)) { - warning (@"A path in $(glyph.get_name ()) has less than three points, it will not be exported."); return true; } @@ -114,8 +109,7 @@ } last = ep; } - - warning (@"A path in $(glyph.get_name ()) ($(glyph.get_hex ())) has no area but $(p.points.size) points at $(p.get_first_point ().x),$(p.get_first_point ().y)."); + return true; } @@ -127,9 +121,26 @@ return (int16) paths.size; } - public int16 get_nflags () { + public uint16 get_nflags () { return nflags; } + + /** Count off curve points and on curve points. + * @return the number of points or uint16.MAX if more than uint16.MAX points where found. + */ + public int get_num_points () { + int points = 0; + + foreach (Path quadratic in paths) { + points += 2 * quadratic.points.size; + + if (points >= uint16.MAX) { + return uint16.MAX; + } + } + + return points; + } void process_end_points () { uint16 last_end_point = 0; @@ -138,6 +149,7 @@ end_points.clear (); end_point = 0; + int path_number = 0; foreach (Path quadratic in paths) { if (unlikely (quadratic.points.size == 0)) { warning (@"No points in path (before conversion $(quadratic.points.size))"); @@ -145,20 +157,26 @@ } if (unlikely (quadratic.points.size < 2)) { - warning ("A path contains less than three points, it will not be exported."); + warning ("A path contains less than three points, it will not be exported. Path number: $path_number"); continue; } foreach (EditPoint e in quadratic.points) { + if (unlikely (nflags == uint16.MAX - 1)) { + warning (@"Too many end points in $(glyph.get_name ())"); + break; + } + end_point++; type = e.get_right_handle ().type; // off curve - end_point++; - - if (end_point >= 0xFFFF) { - warning ("Too many points"); + if (unlikely (nflags == uint16.MAX - 1)) { + warning (@"Too many end points in $(glyph.get_name ())"); + break; } + + end_point++; } end_points.add (end_point - 1); @@ -189,7 +207,13 @@ // off curve flags.add (CoordinateFlags.NONE); - nflags++; + + if (unlikely (nflags == uint16.MAX)) { + warning (@"Too many flags in $(glyph.get_name ())"); + return; + } + + nflags++; } } } @@ -230,7 +254,7 @@ } } - bool process_y () { + void process_y () { double prev = 0; double y; Font font = OpenFontFormatWriter.get_current_font (); @@ -238,6 +262,8 @@ int epi = 0; coordinate_y.clear (); + + int path_number = 0; foreach (Path p in paths) { foreach (EditPoint e in p.points) { @@ -245,17 +271,8 @@ coordinate_y.add ((int16) y); if ((int16) y == 0 && (int16) coordinate_x.get (coordinate_y.size - 1) == 0) { - warning (@"Point on point in TTF. Index $(coordinate_y.size - 1)"); - - // FIXME: distorted shape - /* - if (BirdFont.has_argument ("--test")) { - print (glyph.get_name () + "\n"); - print (points.get (epi).to_string ()); - PenTool.remove_point_simplify (new PointSelection (points.get (epi), p)); - return false; - } - */ + warning (@"Point on point in TTF. Index $(coordinate_y.size - 1) " + + @"Path: $path_number in $(glyph.get_name ())"); } prev = rint (e.y * UNITS - font.base_line * UNITS); @@ -265,25 +282,13 @@ // off curve y = rint (e.get_right_handle ().y * UNITS - prev - font.base_line * UNITS); coordinate_y.add ((int16) y); - - if ((int16) y == 0 && (int16) coordinate_x.get (coordinate_y.size - 1) == 0) { - warning (@"Point on point in TTF (off curve) Index: $(coordinate_y.size - 1) "); - if (BirdFont.has_argument ("--test")) { - print (glyph.get_name () + "\n"); - print (points.get (epi).to_string ()); - - // FIXME: distorted shape - PenTool.remove_point_simplify (new PointSelection (points.get (epi), p)); - return false; - } - } prev = rint (e.get_right_handle ().y * UNITS - font.base_line * UNITS); epi++; } + + path_number++; } - - return true; } void process_bounding_box () {
--- a/libbirdfont/OpenFontFormat/GlyfTable.vala +++ b/libbirdfont/OpenFontFormat/GlyfTable.vala @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 2013 2014 Johan Mattsson + Copyright (C) 2012, 2013, 2014 Johan Mattsson This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -35,7 +35,7 @@ public HeadTable head_table; public HmtxTable hmtx_table; public LocaTable loca_table; - public CmapTable cmap_table; // cmap and post is null when inistialized and set in parse method + public CmapTable cmap_table; // cmap and post is tables are null when initialized and set in parse method public PostTable post_table; public KernTable kern_table; @@ -113,15 +113,17 @@ foreach (GlyphCollection gc in glyphs) { g = gc.get_current (); + + printd (@"adding glyph: $(gc.get_name ())\n"); + // set values for loca table assert (fd.length () % 4 == 0); location_offsets.add (fd.length ()); process_glyph (g, fd); - printd (@"adding glyph: $(g.name)\n"); printd (@"glyf length: $(fd.length () - last_len)\n"); printd (@"loca fd.length (): $(fd.length ())\n"); - + last_len = fd.length (); } @@ -144,7 +146,7 @@ bool unassigned; // add notdef character and other special characters first - glyphs.add (font.get_not_def_character ()); + glyphs.add (font.get_notdef_character ()); glyphs.add (font.get_null_character ()); glyphs.add (font.get_nonmarking_return ()); glyphs.add (font.get_space ()); @@ -163,7 +165,7 @@ g.remove_empty_paths (); unassigned = gc.is_unassigned (); - if (unassigned && gc.get_name () != ".notdef") { + if (unassigned) { unassigned_glyphs.add (gc); } @@ -193,6 +195,13 @@ foreach (GlyphCollection ug in unassigned_glyphs) { glyphs.add (ug); + } + + + int gid = 0; + foreach (GlyphCollection ug in glyphs) { + printd (@"Glyph: $(ug.get_name ()) GID: $(gid)\n"); + gid++; } } @@ -200,7 +209,7 @@ uint16 end_point; uint16 npoints; int16 ncontours; - int16 nflags; + uint16 nflags; int glyph_offset; uint len; uint coordinate_length; @@ -214,6 +223,12 @@ g.remove_empty_paths (); glyf_data = g.get_ttf_data (); + + int points = glyf_data.get_num_points (); + if (unlikely (points >= uint16.MAX)) { + warning (@"Too many points in glyph $(g.get_name ()) ($points)"); + throw new FileError.FAILED (t_("Too many control points") + " " + t_("in") + @" $(g.get_name ())"); + } this.glyf_data.add (glyf_data); @@ -266,6 +281,7 @@ // flags nflags = glyf_data.get_nflags (); if (unlikely (nflags != npoints)) { + print ("glyf table data:\n"); fd.dump (); warning (@"(nflags != npoints) ($nflags != $npoints) in glyph $(g.name). ncontours: $ncontours"); } @@ -298,18 +314,22 @@ // save bounding box for head table if (glyf_data.bounding_box_xmin < this.xmin) { + printd (@"YMin in $(g.get_name ())\n"); this.xmin = glyf_data.bounding_box_xmin; } if (glyf_data.bounding_box_ymin < this.ymin) { + printd (@"YMin in $(g.get_name ())\n"); this.ymin = glyf_data.bounding_box_ymin; } if (glyf_data.bounding_box_xmax > this.xmax) { + printd (@"XMax in $(g.get_name ())\n"); this.xmax = glyf_data.bounding_box_xmax; } if (glyf_data.bounding_box_ymax > this.ymax) { + printd (@"YMax in $(g.get_name ())\n"); this.ymax = glyf_data.bounding_box_ymax; } @@ -477,7 +497,7 @@ end_points = new uint16[ncontours + 1]; for (int i = 0; i < ncontours; i++) { - end_points[i] = dis.read_ushort (); // FIXME: mind shot vector is negative + end_points[i] = dis.read_ushort (); // FIXA: mind shot vector is negative if (i > 0 && end_points[i] < end_points[i -1]) { warning (@"Next endpoint has bad value in $(name.str). (end_points[i] > end_points[i -1]) ($(end_points[i]) > $(end_points[i -1])) i: $i ncontours: $ncontours");
--- a/libbirdfont/OpenFontFormat/GposTable.vala +++ b/libbirdfont/OpenFontFormat/GposTable.vala @@ -1,5 +1,5 @@ /* - Copyright (C) 2012, 2013, 2014 Johan Mattsson + Copyright (C) 2012 2013 2014 2017 Johan Mattsson This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -34,14 +34,14 @@ this.glyf_table = glyf_table; this.pairs = new KernList (glyf_table); - printd ("Process GPOS\n"); + printd ("Processing GPOS\n"); fd.add_ulong (0x00010000); // table version fd.add_ushort (10); // offset to script list fd.add_ushort (30); // offset to feature list fd.add_ushort (44); // offset to lookup list - // script list ? + // script list fd.add_ushort (1); // number of items in script list fd.add_tag ("DFLT"); // default script fd.add_ushort (8); // offset to script table from script list @@ -65,33 +65,107 @@ fd.add_ushort (0); // feature prameters (null) fd.add_ushort (1); // number of lookups fd.add_ushort (0); // lookup index + + Gee.ArrayList<FontData> pair_set_data = new Gee.ArrayList<FontData>(); + pairs.fetch_all_pairs (); + + KernSplitter kern_splitter = new KernSplitter (pairs); + + while (true) { + if (offset < 0) { + break; + } + + KernList pairs_subset = kern_splitter.get_subset (offset); + + if (pairs_subset.get_length () == 0) { + break; + } + + FontData pairs = get_pair_pos_format1 (pairs_subset); + pair_set_data.add (pairs); + + offset += pairs_subset.get_length (); + } // lookup table fd.add_ushort (1); // number of lookups fd.add_ushort (4); // offset to lookup 1 - fd.add_ushort (2); // lookup type - fd.add_ushort (0); // lookup flags - fd.add_ushort (1); // number of subtables - fd.add_ushort (8); // array of offsets to subtables + int16 extention_pos = (int16) (pair_set_data.size * 2); - // MarkFilteringSet + for (int16 index = 0; index < pair_set_data.size; index++) { + extention_pos += index * 8; + } + + int64 pair_set_data_length = 0; + int64 extension_length = 8; + + foreach (FontData pair_set in pair_set_data) { + pair_set_data_length += pair_set.length_with_padding (); + extension_length += 8; + } + + if (pair_set_data.size > 0) { + FontData last_pair_set = pair_set_data.get (pair_set_data.size - 1); + uint64 last_externsion_offset = extension_length + pair_set_data_length; - fd.append (get_pair_pos_format1 ()); + last_externsion_offset -= 8; + last_externsion_offset -= last_pair_set.length (); + + if (last_externsion_offset > uint32.MAX) { + warning ("Too manu kerning pairs for the extension positioning table." + + @"last_externsion_offset: $last_externsion_offset > " + + @"$(uint32.MAX)"); + pair_set_data.clear (); + } + } + + fd.add_ushort (9); // lookup type + fd.add_ushort (0); // lookup flags + fd.add_ushort ((uint16) pair_set_data.size); // number of subtables + + if (pair_set_data.size > 0) { + for (int j = 0; j < pair_set_data.size; j++) { + uint16 lookup_size = (uint16) pair_set_data.size * 2; + uint16 offset_to_extension = (uint16) (6 + lookup_size + j * 8); + fd.add_ushort (offset_to_extension); // array of offsets to subtables + } + + // extension positioning table + int k = pair_set_data.size - 1; + while (extension_length > 0 && k >= 0) { + FontData pair_set = pair_set_data.get (k); + pair_set_data_length -= pair_set.length (); + extension_length -= 8; + + uint64 externsion_offset = extension_length + pair_set_data_length; + + if (externsion_offset > uint32.MAX) { + warning ("Too manu kerning pairs for extension positioning table."); + } + + fd.add_ushort (1); // format + fd.add_ushort (2); // lookup type + fd.add_ulong ((uint32) externsion_offset); // extension offset + k--; + } + + // MarkFilteringSet + + foreach (FontData pair_set in pair_set_data) { + fd.append (pair_set); + } + } fd.pad (); this.font_data = fd; - } - - uint16 get_max_pairs () { - return (uint16) ((uint16.MAX - 10) / 3.0); } // PairPosFormat1 subtable - FontData get_pair_pos_format1 () throws GLib.Error { + FontData get_pair_pos_format1 (KernList pairs_subset) throws GLib.Error { FontData fd = new FontData (); uint coverage_offset; - uint16 max_pairs_per_table = get_max_pairs (); uint16 num_pairs; int i; uint pair_set_offset; @@ -100,26 +174,19 @@ uint last_gid_left; uint last_gid_right; uint16 pair_set_count; - - if (pairs.get_length () == 0) { - pairs.fetch_all_pairs (); - } - - // FIXME: add another table instead of truncating it - if (pairs.get_length () > max_pairs_per_table) { - warning (@"Too many kerning pairs. $(pairs.get_length ())"); - num_pairs = max_pairs_per_table; - } - num_pairs = (pairs.get_length () < max_pairs_per_table) ? - (uint16) pairs.get_length () : max_pairs_per_table; + num_pairs = (uint16) pairs_subset.get_length (); - pair_set_count = (uint16) pairs.get_length_left (); // FIXME: boundaries + pair_set_count = (uint16) pairs_subset.get_length_left (); // FIXME: boundaries - coverage_offset = 10 + pairs_offset_length () + pairs_set_length (); + coverage_offset = 10 + pairs_offset_length (pairs_subset) + pairs_set_length (pairs_subset); if (coverage_offset > uint16.MAX) { - warning (@"Invalid coverage offset. Number of pairs: $(pairs.get_length ())"); + warning (@"Invalid coverage offset." + + @"Total: $(pairs.get_length ()), " + + @"subset: $(pairs_subset.get_length ())" + + @"coverage_offset: $(coverage_offset) > 65535"); + num_pairs = 0; coverage_offset = 10; } @@ -132,11 +199,11 @@ fd.add_ushort (pair_set_count); // n pairs // pair offsets orderd by coverage index - pair_set_offset = 10 + pairs_offset_length (); + pair_set_offset = 10 + pairs_offset_length (pairs_subset); written = 0; written_pairs = 0; - pairs.all_pairs_format1 ((k) => { + pairs_subset.all_pairs_format1 ((k) => { try { if (pair_set_offset > uint16.MAX) { warning ("Invalid offset."); @@ -156,8 +223,8 @@ } }, num_pairs); - if (unlikely (written != pairs_offset_length ())) { - warning (@"Bad pairs_offset_length () calculated: $(pairs_offset_length ()), real length $written"); + if (unlikely (written != pairs_offset_length (pairs_subset))) { + warning (@"Bad pairs_offset_length () calculated: $(pairs_offset_length (pairs_subset)), real length $written"); } // pair table @@ -165,7 +232,7 @@ written = 0; last_gid_left = 0; last_gid_right = 0; - pairs.all_pairs_format1 ((pn) => { + pairs_subset.all_pairs_format1 ((pn) => { try { PairFormat1 p = pn; uint pairset_length = p.pairs.size; @@ -216,13 +283,13 @@ } }, num_pairs); - if (unlikely (pairs_set_length () != written)) { - warning (@"Bad pair set length: $(pairs_set_length ()), real length: $written"); + if (unlikely (pairs_set_length (pairs_subset) != written)) { + warning (@"Bad pair set length: $(pairs_set_length (pairs_subset)), real length: $written"); } if (unlikely (fd.length () != coverage_offset)) { warning (@"Bad coverage offset, coverage_offset: $coverage_offset, real length: $(fd.length ())"); - warning (@"pairs_offset_length: $(pairs_offset_length ()) pairs_set_length: $(pairs_set_length ())"); + warning (@"pairs_offset_length: $(pairs_offset_length (pairs_subset)) pairs_set_length: $(pairs_set_length (pairs_subset))"); } // coverage @@ -230,7 +297,7 @@ fd.add_ushort (pair_set_count); written = 0; - pairs.all_pairs_format1 ((p) => { + pairs_subset.all_pairs_format1 ((p) => { try { fd.add_ushort (p.left); // gid written += 2; @@ -240,26 +307,26 @@ }, num_pairs); if (unlikely (written != 2 * pair_set_count)) { - warning ("written != 2 * pair_set_count"); + warning (@"written != 2 * pair_set_count: $written != 2 * $(pair_set_count)"); } return fd; } - public uint pairs_set_length () { + public static uint pairs_set_length (KernList kerning_list) { uint len = 0; - pairs.all_pairs_format1 ((p) => { + kerning_list.all_pairs_format1 ((p) => { len += 2 + 4 * p.pairs.size; }); return len; } - public uint pairs_offset_length () { - return 2 * pairs.pairs.size; + public static uint pairs_offset_length (KernList kerning_list) { + return 2 * kerning_list.pairs.size; } } }
--- a/libbirdfont/OpenFontFormat/HheaTable.vala +++ b/libbirdfont/OpenFontFormat/HheaTable.vala @@ -111,7 +111,8 @@ FontData fd = new FontData (); Fixed version = 1 << 16; Font font = OpenFontFormatWriter.get_current_font (); - + int upm, total_height; + fd.add_fixed (version); // table version ascender = (int16) rint (font.top_limit * HeadTable.UNITS);
--- a/libbirdfont/OpenFontFormat/HmtxTable.vala +++ b/libbirdfont/OpenFontFormat/HmtxTable.vala @@ -161,7 +161,7 @@ } if (extent < 0) { - warning ("Negative extent."); + warning (@"Negative extent in $(gc.get_name ())."); } advance_width[nmetrics] = (uint16) extent;
--- a/libbirdfont/OpenFontFormat/KernList.vala +++ b/libbirdfont/OpenFontFormat/KernList.vala @@ -20,15 +20,15 @@ public delegate void KernIterator (Kern k); public delegate void PairFormat1Iterator (PairFormat1 k); - GlyfTable glyf_table; - uint num_pairs; + public GlyfTable glyf_table; + public uint num_pairs; public KernList (GlyfTable glyf_table) { this.glyf_table = glyf_table; num_pairs = 0; pairs = new Gee.ArrayList<PairFormat1> (); } - + /** @return number of pairs. */ public uint fetch_all_pairs () { PairFormat1 current_pairs = new PairFormat1 (); @@ -50,21 +50,37 @@ if (unlikely (kerning_pair.character.get_name () == "")) { warning ("No name for glyph"); } - + + + string glyph_name = kerning_pair.character.get_name (); current_pairs = new PairFormat1 (); - gid_left = (uint16) glyf_table.get_gid (kerning_pair.character.get_name ()); + gid_left = (uint16) glyf_table.get_gid (glyph_name); + + if (unlikely (gid_left == -1)) { + warning("Ignoring kerning for missing character: $(glyph_name)"); + return; + } + current_pairs.left = gid_left; pairs.add (current_pairs); if (unlikely (kerning_pair.kerning.size == 0)) { - warning (@"No kerning pairs for character: $((kerning_pair.character.get_name ()))"); + warning (@"No kerning pairs for character (left): $(glyph_name)"); } i = 0; num_pairs += kerning_pair.kerning.size; foreach (Kerning k in kerning_pair.kerning) { - gid_right = (uint16) glyf_table.get_gid (k.get_glyph ().get_name ()); - current_pairs.pairs.add (new Kern (gid_left, gid_right, (int16) Math.rint (k.val * HeadTable.UNITS))); + string right_name = k.get_glyph ().get_name (); + gid_right = (uint16) glyf_table.get_gid (right_name); + + if (unlikely (gid_right == -1)) { + warning("Ignoring kerning for missing character (right): $(right_name)"); + } else { + int16 kerning_value = (int16) Math.rint (k.val * HeadTable.UNITS); + Kern kern = new Kern (gid_left, gid_right, kerning_value); + current_pairs.pairs.add (kern); + } } current_pairs.pairs.sort ((a, b) => { @@ -122,9 +138,21 @@ iter (p); i++; + } + } + + public void all_single_kern (PairFormat1Iterator iter) { + foreach (PairFormat1 p in pairs) { + foreach (Kern k in p.pairs) { + PairFormat1 single = new PairFormat1 (); + single.left = p.left; + single.pairs.add (k); + + iter (single); + } } } } }
--- /dev/null +++ b/libbirdfont/OpenFontFormat/KernSplitter.vala @@ -1,1 +1,77 @@ + /* + Copyright (C) 2017 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + namespace BirdFont { + + public class KernSplitter : GLib.Object { + public Gee.ArrayList<PairFormat1> pairs; + private KernList source_list; + + public KernSplitter (KernList kerning_list) { + source_list = kerning_list; + pairs = new Gee.ArrayList<PairFormat1> (); + + kerning_list.all_single_kern((pair) => { + pairs.add (pair); + }); + } + + /** Prevent integer overflow in coverage table of the PairPos1 subtable. */ + public bool is_full (KernList kerning_pairs) { + uint length = 10; + length += GposTable.pairs_offset_length (kerning_pairs); + length += GposTable.pairs_set_length (kerning_pairs); + + return length > (uint16.MAX - 10); + } + + public KernList get_subset (uint offset) { + int count = 0; + KernList result = new KernList (source_list.glyf_table); + + PairFormat1 current = new PairFormat1 (); + current.left = 0xFFFF; + + for (uint i = offset; i < pairs.size; i++) { + PairFormat1 next = pairs.get ((int) i); + + if (is_full (result)) { + break; + } + + if (next.left != current.left) { + current = new PairFormat1 (); + current.left = next.left; + result.pairs.add (current); + } + + if (unlikely (next.pairs.size != 1)) { + warning ("Splitting kerning pairs failed. " + + @"next.pairs.size: != $(next.pairs.size)"); + } + + current.pairs.add (next.pairs.get (0)); + count++; + } + + foreach (PairFormat1 kerning in result.pairs) { + result.num_pairs += kerning.pairs.size; + } + + return result; + } + } + + }
--- a/libbirdfont/OpenFontFormat/KerningPair.vala +++ b/libbirdfont/OpenFontFormat/KerningPair.vala @@ -21,8 +21,7 @@ public class KerningPair : GLib.Object { public Glyph character; public Gee.ArrayList<Kerning> kerning; - - Gee.ArrayList<Glyph> right; + public Gee.ArrayList<Glyph> right; public KerningPair (Glyph left) { character = left;
--- a/libbirdfont/OpenFontFormat/OpenFontFormatReader.vala +++ b/libbirdfont/OpenFontFormat/OpenFontFormatReader.vala @@ -58,7 +58,7 @@ PARSE } - public class OpenFontFormatReader : GLib.Object { + public class OpenFontFormatReader : Object { public DirectoryTable directory_table; public FontData font_data = new FontData ();
--- a/libbirdfont/OpenFontFormat/OpenFontFormatWriter.vala +++ b/libbirdfont/OpenFontFormat/OpenFontFormatWriter.vala @@ -14,7 +14,7 @@ namespace BirdFont { - public class OpenFontFormatWriter : GLib.Object { + public class OpenFontFormatWriter : Object { DataOutputStream os; DataOutputStream os_mac;
--- a/libbirdfont/OpenFontFormat/OtfInputStream.vala +++ b/libbirdfont/OpenFontFormat/OtfInputStream.vala @@ -15,7 +15,7 @@ namespace BirdFont { /** Reader for otf data types. */ - public class OtfInputStream : GLib.Object { + public class OtfInputStream : Object { public FileInputStream fin; public DataInputStream din;
--- a/libbirdfont/OpenFontFormat/OtfTable.vala +++ b/libbirdfont/OpenFontFormat/OtfTable.vala @@ -14,7 +14,7 @@ namespace BirdFont { - public class OtfTable : GLib.Object { + public class OtfTable : Object { public string id = "NO_ID";
--- a/libbirdfont/OpenFontFormat/PostTable.vala +++ b/libbirdfont/OpenFontFormat/PostTable.vala @@ -1101,6 +1101,8 @@ assert (names.size == 0); add_standard_names (); + + print ("Adding post names\n"); for (int i = 1; i < glyf_table.glyphs.size; i++) { gc = glyf_table.glyphs.get (i);
--- a/libbirdfont/OpenFontFormat/SvgTable.vala +++ /dev/null @@ -1,244 +1,1 @@ - /* - Copyright (C) 2015 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using B; - - namespace BirdFont { - - public class SvgTable : OtfTable { - - int glyphs_in_table = 0; - Gee.ArrayList<SvgTableEntry> entries; - - public SvgTable () { - id = "SVG "; - entries = new Gee.ArrayList<SvgTableEntry> (); - } - - public bool has_glyphs () { - return glyphs_in_table > 0; - } - - public void process (GlyfTable glyf_table) throws GLib.Error { - Font font = OpenFontFormatWriter.get_current_font (); - GlyphCollection? glyph_collection; - GlyphCollection glyphs; - int gid; - Gee.ArrayList<EmbeddedSvg> embedded_svg; - - for (int index = 0; index < font.length (); index++) { - glyph_collection = font.get_glyph_collection_index (index); - - if (glyph_collection != null) { - glyphs = (!) glyph_collection; - embedded_svg = get_embedded_svg (glyphs); - - if (embedded_svg.size > 0) { - gid = glyf_table.get_gid (glyphs.get_name ()); - - StringBuilder svg = new StringBuilder (); - svg.append ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"); - - foreach (EmbeddedSvg embedded in embedded_svg) { - svg.append ("<svg>"); - svg.append ("\n\n"); - svg.append ("<g id="); - svg.append ("\""); - svg.append ("glyph"); - svg.append (@"$gid"); - svg.append ("\" "); - - // scale the internal coordinates from 100 units per em to the - // number of units per em in this font and move the glyph - // in to the em box - Glyph glyph = glyphs.get_current (); - double scale = HeadTable.UNITS; - double x = embedded.x - glyph.left_limit; - double y = font.base_line - embedded.y; - svg.append (@"transform=\"scale($scale) translate($x, $y)\""); - - svg.append (">"); - svg.append ("\n\n"); - - append_svg_glyph (svg, embedded.svg_data, glyphs); - - svg.append ("\n\n"); - svg.append ("</g>\n"); - svg.append ("</svg>"); - } - - SvgTableEntry entry; - entry = new SvgTableEntry ((uint16) gid, svg.str); - entries.add (entry); - - glyphs_in_table++; - } - } - } - - process_svg_data (); - } - - Gee.ArrayList<EmbeddedSvg> get_embedded_svg (GlyphCollection glyphs) { - Gee.ArrayList<EmbeddedSvg> svg = new Gee.ArrayList<EmbeddedSvg> (); - - foreach (Object object in glyphs.get_current ().get_visible_objects ()) { - if (object is EmbeddedSvg) { - svg.add ((EmbeddedSvg) object); - } - } - - return svg; - } - - void append_svg_glyph (StringBuilder svg, string svg_data, GlyphCollection glyphs) { - Gee.ArrayList<Tag> layer_content; - Gee.ArrayList<Tag> svg_tags; - Gee.ArrayList<Tag> meta; - XmlParser xml; - Tag svg_root_tag; - Font font; - - font = OpenFontFormatWriter.get_current_font (); - - layer_content = new Gee.ArrayList<Tag> (); - svg_tags = new Gee.ArrayList<Tag> (); - meta = new Gee.ArrayList<Tag> (); - xml = new XmlParser (svg_data); - - if (!xml.validate ()) { - warning("Invalid SVG data in TTF table."); - return; - } - - svg_root_tag = xml.get_root_tag (); - - foreach (Tag tag in svg_root_tag) { - string name = tag.get_name(); - - if (name == "defs") { - svg_tags.add (tag); - } else if (name == "style") { - svg_tags.add (tag); - } else if (name == "metadata") { - meta.add (tag); - } else if (name == "sodipodi") { - meta.add (tag); - } else { - layer_content.add (tag); - } - } - - svg.append ("<"); - svg.append (svg_root_tag.get_name ()); - svg.append (" "); - append_tag_attributes (svg, svg_root_tag); - svg.append (">"); - - foreach (Tag tag in svg_tags) { - append_tag (svg, tag); - } - - foreach (Tag tag in layer_content) { - append_tag (svg, tag); - } - - svg.append ("</"); - svg.append (svg_root_tag.get_name ()); - svg.append (">\n"); - } - - public void append_tag (StringBuilder svg, Tag tag) { - string content = tag.get_content (); - - svg.append ("<"); - svg.append (tag.get_name ()); - - svg.append (" "); - append_tag_attributes (svg, tag); - - if (content == "") { - svg.append (" /"); - } - - svg.append (">"); - - if (content != "") { - svg.append (content); - svg.append ("</"); - svg.append (tag.get_name ()); - svg.append (">"); - } - } - - public void append_tag_attributes (StringBuilder svg, Tag tag) { - bool first = true; - - foreach (Attribute attribute in tag.get_attributes ()) { - string ns = attribute.get_namespace (); - - if (!first) { - svg.append (" "); - } - - if (ns != "") { - svg.append (ns); - svg.append (":"); - } - - svg.append (attribute.get_name ()); - svg.append ("="); - svg.append ("\""); - svg.append (attribute.get_content ()); - svg.append ("\""); - - first = false; - } - } - - public void process_svg_data () throws GLib.Error { - FontData fd = new FontData (); - - int32 svg_index_offset = 10; - - fd.add_ushort (0); // version - fd.add_ulong (svg_index_offset); - fd.add_ulong (0); // reserved - - uint32 document_offset = 2 + 12 * entries.size; - - // SVG Documents Index - fd.add_ushort ((uint16) entries.size); - - foreach (SvgTableEntry entry in entries) { - fd.add_ushort (entry.glyph_id); // start - fd.add_ushort (entry.glyph_id); // end - fd.add_ulong (document_offset); // offset - fd.add_ulong (entry.data.length_with_padding ()); // length - - document_offset += entry.data.length_with_padding (); - } - - foreach (SvgTableEntry entry in entries) { - fd.append (entry.data); - } - - fd.pad (); - - this.font_data = fd; - } - } - - }
--- a/libbirdfont/OpenFontFormat/SvgTableEntry.vala +++ /dev/null @@ -1,29 +1,1 @@ - /* - Copyright (C) 2015 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - namespace BirdFont { - - public class SvgTableEntry : GLib.Object { - public FontData data; - public uint16 glyph_id; - - public SvgTableEntry (uint16 gid, string svg) { - glyph_id = gid; - data = new FontData (); - data.add_str (svg); - } - } - - }
--- a/libbirdfont/OrientationTool.vala +++ b/libbirdfont/OrientationTool.vala @@ -30,11 +30,8 @@ select_action.connect ((self) => { Glyph g = MainWindow.get_current_glyph (); - foreach (Object o in g.active_paths) { - if (o is PathObject) { - PathObject p = (PathObject) o; - p.get_path ().reverse (); - } + foreach (Path p in g.active_paths) { + p.reverse (); } count_down = true; @@ -57,17 +54,13 @@ bool has_clockwise_paths = false; bool has_counter_clockwise_paths = false; - foreach (Object o in glyph.active_paths) { - if (o is PathObject) { - Path p = ((PathObject) o).get_path (); - - if (p.is_clockwise ()) { - has_clockwise_paths = true; - } - - if (!p.is_clockwise ()) { - has_counter_clockwise_paths = true; - } + foreach (Path p in glyph.active_paths) { + if (p.is_clockwise ()) { + has_clockwise_paths = true; + } + + if (!p.is_clockwise ()) { + has_counter_clockwise_paths = true; } }
--- a/libbirdfont/OtfFeatureTable.vala +++ b/libbirdfont/OtfFeatureTable.vala @@ -26,7 +26,7 @@ static const int REPLACEMENT_GLYPH = 3; static const int ALTERNATE_ENTRY = 4; - GlyphCollection glyph_collection; + GlyphCollection? glyph_collection = null; GlyphCollection? replacement_glyph = null; string alternate_name = ""; TextListener listener; @@ -34,9 +34,24 @@ Gee.ArrayList<AlternateItem> undo_items; // FIXME: implement redo - public OtfFeatureTable (GlyphCollection gc) { + bool ignore_input = false; + + public OtfFeatureTable (GlyphCollection? gc) { glyph_collection = gc; undo_items = new Gee.ArrayList<AlternateItem> (); + } + + public override void selected_canvas () { + ignore_input = true; // make sure that tripple clicks in overview are ignored + + TimeoutSource input_delay = new TimeoutSource (250); + input_delay.set_callback(() => { + ignore_input = false; + return false; + }); + input_delay.attach (null); + + base.selected_canvas (); } public override Gee.ArrayList<Row> get_rows () { @@ -45,15 +60,21 @@ public override void selected_row (Row row, int column, bool delete_button) { int row_index = row.get_index (); - GLib.Object o; + Object o; String s; AlternateItem a; + + if (ignore_input) { + return; + } if (row_index == SOURCE_GLYPH) { GlyphSelection gs = new GlyphSelection (); gs.selected_glyph.connect ((gc) => { - glyph_collection = gc; + glyph_collection = gc; + replacement_glyph = null; + Tool.yield (); MainWindow.get_tab_bar ().select_tab_name (get_name ()); }); @@ -62,7 +83,8 @@ GlyphSelection gs = new GlyphSelection (); gs.selected_glyph.connect ((gc) => { - replacement_glyph = gc; + replacement_glyph = gc; + Tool.yield (); MainWindow.get_tab_bar ().select_tab_name (get_name ()); }); @@ -102,7 +124,15 @@ row = new Row.headline (t_("Glyph Substitutions")); rows.add (row); - row = new Row.columns_1 (t_("Glyph") + ": " + glyph_collection.get_name (), SOURCE_GLYPH, false); + string glyph = ""; + + if (glyph_collection == null) { + glyph = t_("New glyph"); + } else { + glyph = ((!) glyph_collection).get_name (); + } + + row = new Row.columns_1 (t_("Glyph") + ": " + glyph, SOURCE_GLYPH, false); rows.add (row); string replacement = t_("New glyph"); @@ -176,7 +206,14 @@ } public void add_new_alternate (string tag) { - GlyphCollection gc = glyph_collection; + GlyphCollection gc; + + if (glyph_collection == null) { + MainWindow.show_message (t_("Select a glyph to create an alternate for.")); + return; + } + + gc = (!) glyph_collection; listener = new TextListener (t_("Glyph name"), "", t_("Add")); @@ -190,19 +227,15 @@ OverView overview = MainWindow.get_overview (); font = BirdFont.get_current_font (); - - if (alternate_name == "" || gc.is_unassigned ()) { - MainWindow.tabs.close_display (this); - return; - } - + if (font.glyph_name.has_key (alternate_name)) { MainWindow.show_message (t_("All glyphs must have unique names.")); } else { alt = new GlyphCollection.with_glyph ('\0', alternate_name); alt.set_unassigned (true); font.add_new_alternate (gc, alt, tag); - MainWindow.tabs.close_display (this); + update_rows (); + GlyphCanvas.redraw (); MainWindow.get_overview ().update_item_list (); overview.open_glyph_signal (alt); } @@ -212,7 +245,8 @@ GlyphCollection replacement = (!) replacement_glyph; Font f = BirdFont.get_current_font (); f.add_alternate (gc.get_name (), replacement.get_name (), tag); - MainWindow.tabs.close_display (this); + update_rows (); + GlyphCanvas.redraw (); } else { TabContent.show_text_input (listener); }
--- a/libbirdfont/OverView.vala +++ b/libbirdfont/OverView.vala @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 2014 2015 Johan Mattsson + Copyright (C) 2012 - 2016 Johan Mattsson This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -13,7 +13,6 @@ */ using Cairo; - using SvgBird; namespace BirdFont { @@ -51,7 +50,7 @@ string search_query = ""; - Gee.ArrayList<OverViewItem> visible_items = new Gee.ArrayList<OverViewItem> (); + public Gee.ArrayList<OverViewItem> visible_items = new Gee.ArrayList<OverViewItem> (); /** List of undo commands. */ public Gee.ArrayList<OverViewUndoItem> undo_items = new Gee.ArrayList<OverViewUndoItem> (); @@ -94,22 +93,14 @@ TabBar tabs = MainWindow.get_tab_bar (); string n = glyph_collection.get_current ().name; bool selected = tabs.select_char (n); - GlyphCanvas canvas; Glyph g = glyph_collection.get_current (); + GlyphTab glyph_tab; if (!selected) { - canvas = MainWindow.get_glyph_canvas (); - tabs.add_tab (g, true, glyph_collection); - canvas.set_current_glyph_collection (glyph_collection); - set_initial_zoom (); + glyph_tab = new GlyphTab (glyph_collection); + tabs.add_tab (glyph_tab, true, glyph_collection); + set_glyph_zoom (glyph_collection); PenTool.update_orientation (); - } - - if (glyph_collection.get_name () == ".notdef") { - Font font = BirdFont.get_current_font (); - if (!font.has_glyph (".notdef")) { - font.add_glyph_collection (glyph_collection); - } } }); @@ -126,7 +117,7 @@ if (default_character_set) { IdleSource idle = new IdleSource (); - idle.set_callback (() => { + idle.set_callback (() => { use_default_character_set (); selected_canvas (); return false; @@ -138,6 +129,16 @@ update_item_list (); update_scrollbar (); reset_zoom (); + + string? zoom = Preferences.get ("overview_zoom"); + + if (zoom != null) { + string z = (!) zoom; + + if (z != "") { + set_zoom (double.parse (z)); + } + } } public Glyph? get_selected_glyph () { @@ -184,6 +185,7 @@ Glyph glyph; GlyphCollection glyph_collection; GlyphCanvas canvas; + GlyphTab glyph_tab; glyph_collection = MainWindow.get_current_glyph_collection (); name.append_unichar (character); @@ -191,17 +193,17 @@ if (!selected) { glyph_collection = add_character_to_font (character); - + glyph_tab = new GlyphTab (glyph_collection); glyph = glyph_collection.get_current (); glyph.layers.add_layer (new Layer ()); - tabs.add_tab (glyph, true, glyph_collection); + tabs.add_tab (glyph_tab, true, glyph_collection); selected_items.add (glyph_collection); canvas = MainWindow.get_glyph_canvas (); canvas.set_current_glyph_collection (glyph_collection); - set_initial_zoom (); + set_glyph_zoom (glyph_collection); } else { warning ("Glyph is already open"); } @@ -289,15 +291,18 @@ return null; } - public void set_initial_zoom () { + public void set_glyph_zoom (GlyphCollection glyphs) { + GlyphCanvas canvas; + canvas = MainWindow.get_glyph_canvas (); + canvas.set_current_glyph_collection (glyphs); Toolbox tools = MainWindow.get_toolbox (); ZoomTool z = (ZoomTool) tools.get_tool ("zoom_tool"); z.store_current_view (); - MainWindow.get_current_glyph ().default_zoom (); + glyphs.get_current ().default_zoom (); z.store_current_view (); OverViewItem.reset_label (); } - + public double get_height () { double l; Font f; @@ -395,6 +400,7 @@ KeyBindings.set_require_modifier (true); update_scrollbar (); update_zoom_bar (); + OverViewItem.glyph_scale = 1; update_item_list (); selected_item = get_selected_item (); GlyphCanvas.redraw (); @@ -415,7 +421,17 @@ OverViewItem.margin = OverViewItem.DEFAULT_MARGIN * z; update_item_list (); OverViewItem.reset_label (); - GlyphCanvas.redraw (); + Preferences.set ("overview_zoom", @"$zoom"); + + Font font = BirdFont.get_current_font (); + for (int index = 0; index < font.length (); index++) { + GlyphCollection? glyphs = font.get_glyph_collection_index ((uint32) index); + return_if_fail (glyphs != null); + GlyphCollection g = (!) glyphs; + g.get_current ().overview_thumbnail = null; + } + + GlyphCanvas.redraw (); } public override void zoom_min () { @@ -504,7 +520,9 @@ return selected_item; } - return visible_items.get (selected); + OverViewItem item = visible_items.get (selected); + item.selected = true; + return item; } int get_items_per_row () { @@ -527,7 +545,7 @@ public void process_item_list_update () { string character_string; - Font font = BirdFont.get_current_font (); + Font f = BirdFont.get_current_font (); GlyphCollection? glyphs = null; uint32 index; OverViewItem item; @@ -551,11 +569,10 @@ y = OverViewItem.margin; if (all_available) { - uint font_length = font.length (); - int i; + uint font_length = f.length (); - for (i = 0; i < item_list_length && index < font_length; i++) { - glyphs = font.get_glyph_collection_index ((uint32) index); + for (int i = 0; i < item_list_length && index < font_length; i++) { + glyphs = f.get_glyph_collection_index ((uint32) index); return_if_fail (glyphs != null); glyph = ((!) glyphs).get_current (); @@ -565,20 +582,10 @@ item = new OverViewItem (); item.set_character (character); item.set_glyphs (glyphs); - item.generate_graphics (); - item.selected = (selected_items.index_of ((!) glyphs) != -1); - + item.x = x; + item.y = y; visible_items.add (item); index++; - } - - if (i < item_list_length && !font.has_glyph (".notdef")) { - item = new OverViewItem (); - item.set_glyphs (font.get_not_def_character ()); - item.generate_graphics (false); - item.version_menu = null; - item.selected = (selected_items.index_of ((!) glyphs) != -1); - visible_items.add (item); } } else { uint32 glyph_range_size = glyph_range.get_length (); @@ -606,17 +613,8 @@ visible_size = visible_items.size; for (int i = 0; i < visible_size; i++) { item = visible_items.get (i); - glyphs = font.get_glyph_collection_by_name ((!) item.character.to_string ()); + glyphs = f.get_glyph_collection_by_name ((!) item.character.to_string ()); item.set_glyphs (glyphs); - - if (glyphs != null) { - item.selected = (selected_items.index_of ((!) glyphs) != -1); - } - } - - for (int i = 0; i < visible_size; i++) { - item = visible_items.get (i); - item.generate_graphics (); } } @@ -624,16 +622,30 @@ y = OverViewItem.margin; visible_size = visible_items.size; + int selected_index; + bool selected_item; double full_width = OverViewItem.full_width (); for (int i = 0; i < visible_size; i++) { item = visible_items.get (i); - if (item.glyphs == null) { - item.selected |= (i == selected); + selected_item = false; + + if (all_available) { + glyphs = f.get_glyph_collection_index ((uint32) i); + } else { + glyphs = f.get_glyph_collection_by_name ((!) item.character.to_string ()); + } + + if (glyphs != null) { + selected_index = selected_items.index_of ((!) glyphs); + selected_item = (selected_index != -1); } item.adjust_scale (); + + selected_item |= (i == selected); + item.selected = selected_item; item.x = x + view_offset_x; item.y = y + view_offset_y; @@ -682,8 +694,9 @@ void draw_empty_canvas (WidgetAllocation allocation, Context cr) { Text t; + cr.save (); - t = new Text ("No glyphs in this view.", 24); + t = new Text (t_("No glyphs in this view."), 24); Theme.text_color (t, "Text Foreground"); t.widget_x = 40; t.widget_y = 30; @@ -1035,8 +1048,10 @@ } store_undo_items (undo_item); - foreach (GlyphCollection gc in selected_items) { - font.delete_glyph (gc); + foreach (GlyphCollection glyph_collection in selected_items) { + font.delete_glyph (glyph_collection); + string name = glyph_collection.get_name (); + MainWindow.get_tab_bar ().close_background_tab_by_name (name); } update_item_list (); @@ -1060,6 +1075,16 @@ if (g.length () > 0) { font.add_glyph_collection (g); + } + + TabBar tabs = MainWindow.get_tab_bar (); + Tab? tab = tabs.get_tab (g.get_name ()); + + if (tab != null) { + Tab t = (!) tab; + set_glyph_zoom (g); + t.set_glyph_collection (g); + t.set_display (new GlyphTab (g)); } } @@ -1067,6 +1092,7 @@ f.alternates = previous_collection.alternate_sets.copy (); undo_items.remove_at (undo_items.size - 1); + update_item_list (); GlyphCanvas.redraw (); } @@ -1085,8 +1111,17 @@ foreach (GlyphCollection g in previous_collection.glyphs) { font.delete_glyph (g); font.add_glyph_collection (g); + + TabBar tabs = MainWindow.get_tab_bar (); + Tab? tab = tabs.get_tab (g.get_name ()); + + if (tab != null) { + Tab t = (!) tab; + set_glyph_zoom (g); + t.set_glyph_collection (g); + t.set_display (new GlyphTab (g)); + } } - font.alternates = previous_collection.alternate_sets.copy (); redo_items.remove_at (redo_items.size - 1); @@ -1133,6 +1168,15 @@ if (o.get_name () == name) { selected = i; selected_item = get_selected_item (); + + if (selected_item.y > allocation.height - OverViewItem.height) { + view_offset_y -= (selected_item.y + OverViewItem.height) - allocation.height; + } + + if (selected_item.y < 0) { + view_offset_y = 0; + } + return true; } @@ -1183,12 +1227,12 @@ if (all_available) { // don't search for glyphs in huge CJK fonts - if (font.length () > 300) { + if (font.length () > 500) { r = 0; } else { // FIXME: too slow for (r = 0; r < font.length (); r += items_per_row) { - for (i = 0; i < items_per_row; i++) { + for (i = 0; i < items_per_row && i < font.length (); i++) { glyphs = font.get_glyph_collection_index ((uint32) r + i); return_if_fail (glyphs != null); glyph = ((!) glyphs).get_current (); @@ -1225,6 +1269,7 @@ if (index > -1) { first_visible = r; + process_item_list_update (); update_item_list (); select_visible_glyph (ch); } @@ -1245,9 +1290,12 @@ } public void open_overview_item (OverViewItem i) { + return_if_fail (!is_null (i)); + if (i.glyphs != null) { open_glyph_signal ((!) i.glyphs); - ((!) i.glyphs).get_current ().close_path (); + GlyphCollection gc = (!) i.glyphs; + gc.get_current ().close_path (); } else { open_new_glyph_signal (i.character); } @@ -1294,6 +1342,10 @@ character_info = null; GlyphCanvas.redraw (); return; + } + + if (!KeyBindings.has_shift ()) { + selected_items.clear (); } for (int j = 0; j < visible_items.size; j++) { @@ -1305,6 +1357,7 @@ if (KeyBindings.has_shift ()) { if (selected_item.glyphs != null) { + selected_index = selected_items.index_of ((!) selected_item.glyphs); if (selected_index == -1) { selected_items.add ((!) selected_item.glyphs); @@ -1315,8 +1368,6 @@ selected_item = get_selected_item (); } } - - update = true; } else { selected_items.clear (); if (selected_item.glyphs != null) { @@ -1324,16 +1375,15 @@ } } - if (is_null(i.version_menu)) { + if (!is_null (i.version_menu)) { + update = !((!)i).version_menu.menu_visible; + } else { update = true; - } else { - VersionList v = (!) i.version_menu; - update = !v.menu_visible; } } index++; } - + if (update) { update_item_list (); } @@ -1379,7 +1429,20 @@ } public void open_current_glyph () { - open_overview_item (selected_item); + // keep this object even if open_glyph_signal closes the display + this.ref (); + + selected_item = get_selected_item (); + if (selected_item.glyphs != null) { + open_glyph_signal ((!) selected_item.glyphs); + GlyphCollection? gc2 = selected_item.glyphs; + GlyphCollection gc = (!) selected_item.glyphs; + gc.get_current ().close_path (); + } else { + open_new_glyph_signal (selected_item.character); + } + + this.unref (); } public override void update_scrollbar () { @@ -1432,6 +1495,7 @@ double character_height; entry = ((!)character_info).get_entry (); + lines = entry.split ("\n"); foreach (string line in entry.split ("\n")) { @@ -1511,6 +1575,7 @@ character_start = y + 10 + i * UCD_LINE_HEIGHT; character_height = h - character_start; + draw_fallback_character (cr, x, character_start, character_height); } } @@ -1519,7 +1584,11 @@ void draw_fallback_character (Context cr, double x, double y, double height) requires (character_info != null) { unichar c = ((!)character_info).unicode; - + + if (height < 0) { + return; + } + cr.save (); Text character = new Text (); character.set_use_cache (false); @@ -1644,6 +1713,13 @@ } f.touch (); + + update_item_list (); + GlyphCanvas.redraw (); + } + + public override bool needs_modifier () { + return true; } public class OverViewUndoItem {
--- a/libbirdfont/OverViewItem.vala +++ b/libbirdfont/OverViewItem.vala @@ -25,11 +25,7 @@ public GlyphCollection? glyphs; public double x; public double y; - - public bool selected { - get; set; - } - + public bool selected = false; public CharacterInfo info; public static double DEFAULT_WIDTH = 100; @@ -42,7 +38,7 @@ public static double glyph_scale = 1.0; - public VersionList? version_menu = null; + public VersionList version_menu; Text label; private Surface? cache = null; @@ -60,25 +56,20 @@ } public void set_glyphs (GlyphCollection? gc) { - glyphs = gc; - } - - public void generate_graphics (bool generate_versions = true) { - if (glyphs != null && generate_versions) { - VersionList versions = new VersionList ((!) glyphs); - - versions.add_glyph_item.connect ((glyph) => { + glyphs = gc; + + if (glyphs != null) { + version_menu = new VersionList ((!) glyphs); + version_menu.add_glyph_item.connect ((glyph) => { ((!) glyphs).insert_glyph (glyph, true); }); - versions.signal_delete_item.connect ((glyph_index) => { + version_menu.signal_delete_item.connect ((glyph_index) => { OverView v = MainWindow.get_overview (); version_menu = new VersionList ((!) glyphs); v.update_item_list (); GlyphCanvas.redraw (); }); - - version_menu = versions; } info = new CharacterInfo (character, glyphs); @@ -90,7 +81,16 @@ truncate_label (); } - draw_background (); + draw_background (); + } + + public void clear_cache () { + cache = null; + + if (glyphs != null) { + Glyph g = ((!) glyphs).get_current (); + g.overview_thumbnail = null; + } } public void draw_glyph_from_font () { @@ -99,6 +99,7 @@ } Glyph g; + Font font; double gx, gy; double x1, x2, y1, y2; double scale_box; @@ -106,17 +107,18 @@ double glyph_width, glyph_height; Surface s; Context c; + Color color = Color.black (); g = ((!) glyphs).get_current (); - if (g.overview_thumbnail != null) { + if (likely (g.overview_thumbnail != null)) { cache = g.overview_thumbnail; return; } - + w = width; h = height; - + scale_box = width / DEFAULT_WIDTH; s = Screen.create_background_surface ((int) width, (int) height - 20); @@ -124,37 +126,51 @@ c.save (); g.boundaries (out x1, out y1, out x2, out y2); - + glyph_width = x2 - x1; glyph_height = y2 - y1; - + c.save (); c.scale (glyph_scale * Screen.get_scale (), glyph_scale * Screen.get_scale ()); g.add_help_lines (); - + gx = ((w / glyph_scale) - glyph_width) / 2 - g.get_left_side_bearing (); gy = (h / glyph_scale) - 25 / glyph_scale; + c.translate (gx - Glyph.xc () - g.get_lsb (), g.get_baseline () + gy - Glyph.yc ()); - g.draw_layers (c); + g.draw_paths (c, color); c.restore (); cache = s; + g.overview_thumbnail = s; + GlyphCanvas.redraw (); } public void draw_background () { + Glyph g; + Font font; + double gx, gy; + double x1, x2, y1, y2; double scale_box; + double w, h; + double glyph_width, glyph_height; Surface s; Context c; - + Color color = Color.black (); + + w = width; + h = height; + scale_box = width / DEFAULT_WIDTH; + adjust_scale (); s = Screen.create_background_surface ((int) width, (int) height - 20); c = new Context (s); - if (glyphs != null) { + if (glyphs != null) { // FIXME: lock draw_glyph_from_font (); } else { c.scale (Screen.get_scale (), Screen.get_scale ()); @@ -209,6 +225,10 @@ s.append_unichar (character); return s.str; + } + + public void set_selected (bool s) { + selected = s; } public static double full_width () { @@ -224,17 +244,16 @@ GlyphCollection g; bool s = (x <= px <= x + width) && (y <= py <= y + height); - if (has_icons () && glyphs != null && version_menu != null) { + if (has_icons () && glyphs != null) { g = (!) glyphs; - VersionList versions = (!) version_menu; - versions.set_position (x + width - 21, y + height - 18); - a = versions.menu_item_action (px, py); // select one item on the menu + version_menu.set_position (x + width - 21, y + height - 18); + a = version_menu.menu_item_action (px, py); // select one item on the menu if (a) { return s; } - versions.menu_icon_action (px, py); // click in the open menu + version_menu.menu_icon_action (px, py); // click in the open menu } info.set_position (x + width - 17, y + height - 22.5); @@ -271,8 +290,9 @@ cr.set_line_width (1); cr.stroke (); cr.restore (); - - draw_thumbnail (cr, x, y + height); + + draw_thumbnail (cr, x, y + height); + draw_caption (cr); draw_menu (cr); } @@ -285,22 +305,21 @@ if (glyphs != null) { font = BirdFont.get_current_font (); g = ((!) glyphs).get_current (); - - if (g.boundaries (out x1, out y1, out x2, out y2)) { - glyph_width = x2 - x1; - glyph_height = y2 - y1; + g.boundaries (out x1, out y1, out x2, out y2); + + glyph_width = x2 - x1; + glyph_height = y2 - y1; - if (glyph_scale == 1) { - // caption height is 20 - glyph_scale = (height - 20) / (font.top_limit - font.bottom_limit); - } - - scale = glyph_scale; - gx = ((width / scale) - glyph_width) / 2; + if (glyph_scale == 1) { + // caption height is 20 + glyph_scale = (height - 20) / (font.top_limit - font.bottom_limit); + } - if (gx < 0) { - glyph_scale = 1 + 2 * gx / width; - } + scale = glyph_scale; + gx = ((width / scale) - glyph_width) / 2; + + if (gx < 0) { + glyph_scale = 1 + 2 * gx / width; } } } @@ -356,10 +375,7 @@ cc.fill (); if (has_icons ()) { - if (version_menu != null) { - draw_menu_icon (cc, false); - } - + draw_menu_icon (cc, false); draw_character_info_icon (cc); } @@ -416,10 +432,6 @@ bool has_menu () { return glyphs != null; - } - - public void clear_cache () { - cache = null; } public void draw_label_background (Context cr) { @@ -456,8 +468,7 @@ public void hide_menu () { if (!is_null (version_menu)) { - VersionList v = (!) version_menu; - v.menu_visible = false; + version_menu.menu_visible = false; } } @@ -477,23 +488,13 @@ } private void draw_menu (Context cr) { - if (glyphs == null) { - return; - } - - if (version_menu == null) { - return; - } - - VersionList v = (!) version_menu; - - if (!v.menu_visible) { + if (likely (glyphs == null || !version_menu.menu_visible)) { return; } - v.draw_menu (cr); + version_menu.draw_menu (cr); } } }
--- a/libbirdfont/OverviewTools.vala +++ b/libbirdfont/OverviewTools.vala @@ -15,6 +15,11 @@ using Cairo; namespace BirdFont { + + public enum Transform { + SLANT, + SIZE + } public class OverviewTools : ToolCollection { @@ -38,7 +43,8 @@ Expander font_name = new Expander (); Expander character_sets = new Expander (t_("Character Sets")); Expander zoom_expander = new Expander (t_("Zoom")); - Expander transform_expander = new Expander (t_("Transform")); + Expander transform_slant_expander = new Expander (t_("Transform")); + Expander transform_size_expander = new Expander (); Expander glyph_expander = new Expander (t_("Glyph")); Expander multi_master = new Expander (t_("Multi-Master")); @@ -93,44 +99,72 @@ skew = new SpinButton ("skew_overview", t_("Skew")); skew.set_big_number (true); skew.set_int_value ("0.000"); + + FontSettings settings = BirdFont.get_current_font ().settings; + string? skew_value = settings.get_setting ("skew_overview"); + + if (skew_value != null) { + skew.set_int_value ((!) skew_value); + } + skew.set_int_step (1); skew.set_min (-100); skew.set_max (100); skew.show_icon (true); skew.set_persistent (false); - skew.new_value_action.connect ((self) => { - resize.set_value_round (100, false, false); + transform_slant_expander.add_tool (skew); + + Tool transform_slant = new Tool ("transform_slant", t_("Transform")); + transform_slant.set_icon ("transform"); + transform_slant.select_action.connect ((self) => { + FontSettings fs = BirdFont.get_current_font ().settings; + fs.set_setting ("skew_overview", @"$(skew.get_value ())"); + transform_slant.selected = false; + + process_transform (Transform.SLANT); + + BirdFont.get_current_font ().touch (); }); - transform_expander.add_tool (skew); + transform_slant.selected = false; + transform_slant.set_persistent (false); + transform_slant_expander.add_tool (transform_slant); resize = new SpinButton ("resize_overview", t_("Resize")); resize.set_big_number (true); - resize.set_int_value ("0.000"); + resize.set_int_value ("100.0"); + + string? resize_value = settings.get_setting ("resize_overview"); + + if (resize_value != null) { + resize.set_int_value ((!) resize_value); + + if (resize.get_value () <= 0) { + resize.set_int_value ("100.0"); + } + } + resize.set_int_step (1); resize.set_min (0); resize.set_max (300); resize.show_icon (true); resize.set_persistent (false); - resize.new_value_action.connect ((self) => { - skew.set_value_round (0, false, false); - }); - transform_expander.add_tool (resize); + transform_size_expander.add_tool (resize); + + Tool transform_size = new Tool ("transform_size", t_("Transform")); + transform_size.set_icon ("transform"); + transform_size.select_action.connect ((self) => { + FontSettings fs = BirdFont.get_current_font ().settings; + fs.set_setting ("resize_overview", @"$(resize.get_value ())"); + transform_size.selected = false; - Tool transform = new Tool ("transform", t_("Transform")); - transform.select_action.connect ((self) => { - FontSettings fs = BirdFont.get_current_font ().settings; - - fs.set_setting ("skew_overview", @"$(skew.get_value ())"); - transform.selected = false; - - process_transform (); + process_transform (Transform.SIZE); BirdFont.get_current_font ().touch (); }); - transform.selected = false; - transform.set_persistent (false); - transform_expander.add_tool (transform); - + transform_size.selected = false; + transform_size.set_persistent (false); + transform_size_expander.add_tool (transform_size); + Tool alternate = new Tool ("alternate", t_("Create alternate")); alternate.select_action.connect (add_new_alternate); glyph_expander.add_tool (alternate); @@ -179,7 +213,8 @@ expanders.add (font_name); expanders.add (zoom_expander); expanders.add (character_sets); - expanders.add (transform_expander); + expanders.add (transform_slant_expander); + expanders.add (transform_size_expander); expanders.add (glyph_expander); if (BirdFont.has_argument ("--test")) { @@ -244,19 +279,20 @@ public void add_new_alternate (Tool tool) { OverView o = MainWindow.get_overview (); OverViewItem oi = o.selected_item; - GlyphCollection gc; + GlyphCollection? gc; tool.set_selected (false); - if (oi.glyphs == null || ((!) oi.glyphs).is_unassigned ()) { - return; + gc = (!) oi.glyphs; + + if (oi.glyphs != null && ((!) oi.glyphs).is_unassigned ()) { + gc = null; } - gc = (!) oi.glyphs; MainWindow.tabs.add_tab (new OtfFeatureTable (gc)); } - public void process_transform () { + public void process_transform (Transform transform) { OverView o; Glyph g; OverView.OverViewUndoItem ui; @@ -273,15 +309,24 @@ ui.glyphs.add (gc.copy_deep ()); g.add_help_lines (); - if (skew.get_value () != 0) { - DrawingTools.resize_tool.skew_glyph (g, -skew.get_value (), 0, false); + if (transform == Transform.SLANT) { + if (skew.get_value () != 0) { + DrawingTools.resize_tool.skew_glyph (g, -skew.get_value (), 0, false); + } } - if (resize.get_value () != 100) { - double scale = resize.get_value () / 100; - DrawingTools.resize_tool.resize_glyph (g, scale, scale, false); + if (transform == Transform.SIZE) { + if (resize.get_value () != 100) { + double scale = resize.get_value () / 100; + DrawingTools.resize_tool.resize_glyph (g, scale, scale, false); + } } } + } + + foreach (OverViewItem item in o.visible_items) { + item.clear_cache (); + item.draw_glyph_from_font (); } o.undo_items.add (ui); @@ -310,16 +355,9 @@ GlyphRange gr; uint size; OverView overview; - Font font; - - // All characters including .notdef - font = BirdFont.get_current_font (); - size = font.length (); - - if (!font.has_glyph (".notdef")) { - size++; - } - + + // All characters + size = BirdFont.get_current_font ().length (); all_glyphs.number = get_display_value (size); // Default
--- a/libbirdfont/Path.vala +++ b/libbirdfont/Path.vala @@ -14,7 +14,6 @@ using Cairo; using Math; - using SvgBird; namespace BirdFont { @@ -68,7 +67,7 @@ private double path_stroke_width = 0; - public SvgBird.LineCap line_cap = SvgBird.LineCap.BUTT; + public LineCap line_cap = LineCap.BUTT; public PathList? full_stroke = null; PathList? fast_stroke = null; StrokeTask? stroke_creator; @@ -84,8 +83,14 @@ bool clockwise_direction = true; // Iterate over each pixel in a path - public delegate bool RasterIterator (double x, double y, double step); + public delegate bool RasterIterator (double x, double y, double step); + public delegate bool SegmentIterator (EditPoint start, EditPoint stop); + + /** The stroke of an outline when the path is not filled. */ + public static double stroke_width = 0; + public static bool show_all_line_handles = true; + public static bool fill_open_path {get; set;} public double rotation = 0; public double skew = 0; @@ -94,14 +99,27 @@ public bool highlight_last_segment = false; public string point_data = ""; - - private static Text? arrow = null; public Color? color = null; public Color? stroke_color = null; + public Gradient? gradient = null; + + private static Text? arrow = null; public Path () { + string width; + + if (unlikely (stroke_width < 1)) { + width = Preferences.get ("stroke_width"); + if (width != "") { + stroke_width = double.parse (width); + } + } + + if (stroke_width < 1) { + stroke_width = 1; + } } public bool is_filled () { @@ -155,6 +173,22 @@ public bool empty () { return points.size == 0; + } + + public void draw_boundaries (Context cr) { + double x = Glyph.reverse_path_coordinate_x (xmin); + double y = Glyph.reverse_path_coordinate_y (ymin); + double x2 = Glyph.reverse_path_coordinate_x (xmax); + double y2 = Glyph.reverse_path_coordinate_y (ymax); + + cr.save (); + + Theme.color (cr, "Default Background"); + cr.set_line_width (2); + cr.rectangle (x, y, x2 - x, y2 - y); + cr.stroke (); + + cr.restore (); } public void draw_outline (Context cr) { @@ -183,7 +217,7 @@ i++; } - // closed path + // close path if (!is_open () && n != null) { if (highlight_last_segment) { cr.stroke (); @@ -208,20 +242,19 @@ } } - public void draw_control_points (Context cr) { - // control points for curvature - foreach (EditPoint e in points) { - if (CanvasSettings.show_all_line_handles - || e.selected_point - || e.selected_handle > 0) { - - draw_edit_point_handles (e, cr); + public void draw_edit_points (Context cr) { + if (is_editable ()) { + // control points for curvature + foreach (EditPoint e in points) { + if (show_all_line_handles || e.selected_point || e.selected_handle > 0) { + draw_edit_point_handles (e, cr); + } + } + + // control points + foreach (EditPoint e in points) { + draw_edit_point (e, cr); } - } - - // on curve control points - foreach (EditPoint e in points) { - draw_edit_point (e, cr); } } @@ -229,7 +262,7 @@ * Call Context.new_path (); before this method and Context.fill () * to show the path. */ - public void draw_path (Context cr, Color? color = null) { + public void draw_path (Context cr, Glyph glyph, Color? color = null) { unowned EditPoint? n = null; unowned EditPoint en; unowned EditPoint em; @@ -241,8 +274,8 @@ return; } - center_x = Glyph.xc (); - center_y = Glyph.yc (); + center_x = glyph.allocation.width / 2.0; + center_y = glyph.allocation.height / 2.0; ex = center_x + points.get (0).x; ey = center_y - points.get (0).y; @@ -275,6 +308,12 @@ } else if (color != null) { c = (!) color; cr.set_source_rgba (c.r, c.g, c.b, c.a); + } else { + if (is_clockwise ()) { + Theme.color_opacity (cr, "Selected Objects", 0.4); + } else { + Theme.color_opacity (cr, "Selected Objects", 0.8); + } } } @@ -307,13 +346,17 @@ y = Glyph.yc () - top.y - sin (angle + PI / 2) * 10 * Glyph.ivz (); if (points.size > 0) { - cr.save (); - cr.translate (x, y); double inverted_zoom = Glyph.ivz (); - cr.rotate (-angle); - cr.translate (-x, -y); + double zoom = 1 / inverted_zoom; cr.scale (inverted_zoom, inverted_zoom); - arrow_icon.draw_at_baseline (cr, x, y); + + cr.save (); + cr.translate (x * zoom, y * zoom); + cr.rotate (-angle); + cr.translate (-x * zoom, -y * zoom); + + arrow_icon.draw_at_baseline (cr, x * zoom, y * zoom); + cr.restore (); } } @@ -347,6 +390,7 @@ } private static void draw_curve (EditPoint e, EditPoint en, Context cr, bool highlighted = false, double alpha = 1) { + Glyph g = MainWindow.get_current_glyph (); double xa, ya, xb, yb, xc, yc, xd, yd; PointType t = e.get_right_handle ().type; PointType u = en.get_left_handle ().type; @@ -358,6 +402,8 @@ } else { Theme.color (cr, "Highlighted Guide"); } + + cr.set_line_width (stroke_width / g.view_zoom); cr.line_to (xa, ya); // this point makes sense only if it is in the first or last position @@ -424,7 +470,7 @@ get_line_points (e, en, out ax, out ay, out bx, out by); Theme.color (cr, "Handle Color"); - cr.set_line_width (1.7 * (CanvasSettings.stroke_width / g.view_zoom)); + cr.set_line_width (1.7 * (stroke_width / g.view_zoom)); cr.line_to (ax, ay); cr.line_to (bx, by); @@ -554,7 +600,7 @@ public static void draw_control_point (Context cr, double x, double y, Color color, double size = 3.5) { Glyph g = MainWindow.get_current_glyph (); double ivz = 1 / g.view_zoom; - double width = size * Math.sqrt (CanvasSettings.stroke_width) * ivz; + double width = size * Math.sqrt (stroke_width) * ivz; double xc = g.allocation.width / 2.0; double yc = g.allocation.height / 2.0; @@ -777,6 +823,17 @@ new_path.highlight_last_segment = highlight_last_segment; return new_path; + } + + public bool is_over (double x, double y) { + Glyph g = MainWindow.get_current_glyph (); + + x = x * Glyph.ivz () + g.view_offset_x - Glyph.xc (); + y = y * Glyph.ivz () + g.view_offset_y - Glyph.yc (); + + y *= -1; + + return is_over_coordinate (x, y); } public bool is_over_coordinate (double x, double y) { @@ -938,7 +995,6 @@ points.add (p); p.prev = previous_point; p.next = previous_point.next; - previous_point.next = p; } last_point = p; @@ -1478,7 +1534,7 @@ if (steps == -1) { steps = (int) (10 * get_length_from (start, stop)); - } + } if (right == PointType.DOUBLE_CURVE || left == PointType.DOUBLE_CURVE) { return all_of_double (start.x, start.y, start.get_right_handle ().x, start.get_right_handle ().y, stop.get_left_handle ().x, stop.get_left_handle ().y, stop.x, stop.y, iter, steps, min_t, max_t); @@ -2210,6 +2266,12 @@ PathList lines = new PathList (); lines = pl; + + /** // FIXME: Check automatic orientation. + foreach (Path p in pl.paths) { + lines.add (SvgParser.get_lines (p)); + } + */ foreach (Path p in lines.paths) { if (p.points.size > 1 && p != path @@ -2252,10 +2314,11 @@ if (points.size == 0) { return; } - + for (int i = 0; i < points.size + 1; i++) { EditPoint ep = points.get (i % points.size); - if (ep.get_right_handle ().length < t3 + if ((ep.flags & EditPoint.STROKE_OFFSET) > 0 + && ep.get_right_handle ().length < t3 && ep.get_left_handle ().length < t3 && !is_endpoint (ep) && (ep.flags & EditPoint.CURVE_KEEP) == 0
--- a/libbirdfont/PathList.vala +++ b/libbirdfont/PathList.vala @@ -11,8 +11,6 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. */ - - using SvgBird; namespace BirdFont { @@ -79,22 +77,8 @@ } return pl; - } - - public void apply_style (SvgStyle style) { - foreach (Path p in paths) { - if (style.has_stroke ()) { - p.stroke = style.get_stroke_width (); - } else { - p.stroke = 0; - } - - p.line_cap = style.get_line_cap (); - p.reset_stroke (); - p.update_region_boundaries (); - } } } }
diff --git libbirdfont/PathObject.vala(deleted)
--- a/libbirdfont/PathObject.vala +++ /dev/null @@ -1,155 +1,1 @@ - /* - Copyright (C) 2015 2016 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using Cairo; - using SvgBird; - - namespace BirdFont { - - public class PathObject : SvgBird.Object { - - public Path path; - - public override double stroke { - get { - return path.stroke; - } - - set { - path.stroke = value; - } - } - - - // FIXME: flip y axis - public override double xmin { - get { - return path.xmin; - } - - set { - path.xmin = value; - } - - default = Glyph.CANVAS_MAX; - } - - public override double xmax { - get { - return path.xmax; - } - - set { - path.xmax = value; - } - - default = Glyph.CANVAS_MIN; - } - - public override double ymin { - get { - return path.ymin; - } - - set { - path.ymin = value; - } - - default = Glyph.CANVAS_MAX; - } - - public override double ymax { - get { - return path.ymax; - } - - set { - path.ymax = value; - } - - default = Glyph.CANVAS_MIN; - } - - public PathObject () { - base (); - path = new Path (); - update_region_boundaries (); - } - - public PathObject.create_copy (PathObject p) { - base.create_copy (p); - path = p.path.copy (); - } - - public PathObject.for_path (Path path) { - base (); - this.path = path; - } - - public override bool is_over (double x, double y) { - return path.is_over_coordinate (x, y); - } - - public override void draw_outline (Context cr) { - // drawing is handled in Glyph.draw_bird_font_paths - } - - public void draw_path (Context cr) { - path.draw_path (cr); - } - - public static void draw_path_list (PathList pl, Context cr) { - foreach (Path p in pl.paths) { - p.draw_path (cr); - } - } - - public override void move (double dx, double dy) { - path.move (dx, dy); - path.reset_stroke (); - } - - public Path get_path () { - return path; - } - - public override void update_region_boundaries () { - path.update_region_boundaries (); - } - - public override void rotate (double theta, double xc, double yc) { - path.rotate (theta, xc, yc); - } - - public override bool is_empty () { - return path.points.size == 0; - } - - public override void resize (double ratio_x, double ratio_y) { - path.resize (ratio_x, ratio_y); - path.reset_stroke (); - } - - public override SvgBird.Object copy () { - return new PathObject.create_copy (this); - } - - public override string to_string () { - return "PathObject"; - } - - } - - }
--- a/libbirdfont/PenTool.vala +++ b/libbirdfont/PenTool.vala @@ -765,11 +765,8 @@ p.path.reset_stroke (); } - foreach (Object path in g.active_paths) { - if (path is PathObject) { - PathObject p = (PathObject) path; - p.get_path ().reset_stroke (); - } + foreach (Path path in g.active_paths) { + path.reset_stroke (); } } @@ -818,7 +815,7 @@ GridTool.tie_coordinate (ref coordinate_x, ref coordinate_y); delta_coordinate_x = coordinate_x - last_point_x; delta_coordinate_y = coordinate_y - last_point_y; - selected_handle.move_to_coordinate (selected_handle.x + delta_coordinate_x, selected_handle.y + delta_coordinate_y); + selected_handle.move_delta_coordinate (delta_coordinate_x, delta_coordinate_y); } else if (GridTool.has_ttf_grid ()) { coordinate_x = Glyph.path_coordinate_x (x); coordinate_y = Glyph.path_coordinate_y (y); @@ -829,21 +826,17 @@ } else { coordinate_x = Glyph.path_coordinate_x (x); coordinate_y = Glyph.path_coordinate_y (y); - delta_coordinate_x = coordinate_x - last_point_x; - delta_coordinate_y = coordinate_y - last_point_y; - selected_handle.move_delta_coordinate (delta_coordinate_x, delta_coordinate_y); + + selected_handle.move_to_coordinate (coordinate_x, coordinate_y); if (on_axis) { - double horizontal, vertical; - - horizontal = Path.distance (selected_handle.parent.x, selected_handle.x, selected_handle.y, selected_handle.y); - vertical = Path.distance (selected_handle.x, selected_handle.x, selected_handle.parent.y, selected_handle.y); - - if (horizontal < vertical) { - selected_handle.move_to_coordinate (selected_handle.parent.x, selected_handle.y); - } else { - selected_handle.move_to_coordinate (selected_handle.x, selected_handle.parent.y); - } + double tied_x = 0; + double tied_y = 0; + + PointTool.tie_angle (selected_handle.parent.x, selected_handle.parent.y, + coordinate_x, coordinate_y, out tied_x, out tied_y); + + selected_handle.move_to_coordinate (tied_x, tied_y); } } @@ -1058,11 +1051,10 @@ // don't use set point to reflective to on open ends reflective = true; - foreach (SvgBird.Object path in MainWindow.get_current_glyph ().active_paths) { - if (path.is_open () && !path.is_empty () && path is PathObject) { - Path p = ((PathObject) path).get_path (); - if (selected_handle.parent == p.get_first_point () - || selected_handle.parent == p.get_last_point ()) { + foreach (Path path in MainWindow.get_current_glyph ().active_paths) { + if (path.is_open () && path.points.size > 0) { + if (selected_handle.parent == path.get_first_point () + || selected_handle.parent == path.get_last_point ()) { reflective = false; } } @@ -1233,10 +1225,7 @@ if (active_edit_point != null) { glyph.clear_active_paths (); - - PathObject path = new PathObject.for_path (active_path); - glyph.add_active_object (null, path); - + glyph.add_active_path (null, active_path); DrawingTools.update_stroke_settings (); if (KeyBindings.modifier != SHIFT) { @@ -1251,9 +1240,9 @@ } // alt+click creates a point with symmetrical handles - if (KeyBindings.has_alt () || KeyBindings.has_ctrl ()) { + if (KeyBindings.has_alt () || KeyBindings.has_ctrl ()) { selected_point.set_reflective_handles (true); - selected_point.process_symmetrical_handles (); + selected_point.get_right_handle ().process_symmetrical_handle (); GlyphCanvas.redraw (); } } @@ -1415,10 +1404,8 @@ foreach (Path merge in paths) { if (merge.points.size > 0) { - PathObject merged_path = new PathObject.for_path (merge); - - if (is_close_to_point (merge.points.get (merge.points.size - 1), px, py)) { - glyph.add_active_object (null, merged_path); + if (is_close_to_point (merge.points.get (merge.points.size - 1), px, py)) { + glyph.add_active_path (null, merge); active_path = merge; merge.reopen (); glyph.open_path (); @@ -1426,7 +1413,7 @@ } if (is_close_to_point (merge.points.get (0), px, py)) { - glyph.add_active_object (null, merged_path); + glyph.add_active_path (null, merge); active_path = merge; clear_directions (); merge.reopen (); @@ -1456,15 +1443,14 @@ } // join path with it self - if (is_close_to_point (path.points.get (0), px, py) && path.points.size > 2) { + if (is_close_to_point (path.points.get (0), px, py)) { + close_path (path); glyph.close_path (); force_direction (); glyph.clear_active_paths (); - - PathObject closed_path = new PathObject.for_path (path); - glyph.add_active_object (null, closed_path); + glyph.add_active_path (null, path); if (direction_changed) { path.reverse (); @@ -1503,12 +1489,11 @@ } else { union = merge_open_paths (path, merge); - PathObject union_path = new PathObject.for_path (union); glyph.add_path (union); glyph.delete_path (path); glyph.delete_path (merge); glyph.clear_active_paths (); - glyph.add_active_object (null, union_path); + glyph.add_active_path (null, union); union.reopen (); union.create_list (); @@ -1789,19 +1774,13 @@ active_edit_point = new_point.point; return_val_if_fail (glyph.active_paths.size > 0, new PointSelection.empty ()); - SvgBird.Object object = glyph.active_paths.get (glyph.active_paths.size - 1); - - if (object is PathObject) { - Path path = ((PathObject) object).get_path (); - - add_selected_point (selected_point, path); + add_selected_point (selected_point, glyph.active_paths.get (glyph.active_paths.size - 1)); - active_path = new_point.path; - glyph.clear_active_paths (); - glyph.add_active_object (null, object); - - move_selected = true; - } + active_path = new_point.path; + glyph.clear_active_paths (); + glyph.add_active_path (null, new_point.path); + + move_selected = true; return new_point; } @@ -1837,7 +1816,6 @@ EditPoint inserted; bool stroke = StrokeTool.add_stroke; Glyph g = MainWindow.get_current_glyph (); - PathObject path; if (g.active_paths.size == 0) { np = new Path (); @@ -1845,8 +1823,7 @@ np.stroke = stroke ? StrokeTool.stroke_width : 0; np.line_cap = StrokeTool.line_cap; - path = new PathObject.for_path (np); - g.add_active_object (null, path); + g.add_active_path (null, np); active_path = np; selected_path = np; @@ -1872,8 +1849,7 @@ } g.clear_active_paths (); - path = new PathObject.for_path (np); - g.add_active_object (null, path); + g.add_active_path (null, np); active_path = np; selected_path = np; @@ -1924,7 +1900,7 @@ Glyph g = MainWindow.get_current_glyph (); double distance_to_edit_point = g.view_zoom * get_distance_to_closest_edit_point (event_x, event_y); - if (!CanvasSettings.show_all_line_handles) { + if (!Path.show_all_line_handles) { foreach (PointSelection selected_corner in selected_points) { if (is_close_to_handle (selected_corner.point, event_x, event_y, distance_to_edit_point)) { return true; @@ -1994,7 +1970,7 @@ foreach (Path p in g.get_paths_in_current_layer ()) { foreach (EditPoint ep in p.points) { - if (ep.is_selected () || CanvasSettings.show_all_line_handles) { + if (ep.is_selected () || Path.show_all_line_handles) { left = ep.get_left_handle (); right = ep.get_right_handle (); @@ -2083,8 +2059,7 @@ selected_handle.selected = true; active_path = p.path; - PathObject path = new PathObject.for_path (active_path); - g.add_active_object (null, path); + g.add_active_path (null, active_path); } public static void add_selected_point (EditPoint p, Path path) {
--- a/libbirdfont/PointConverter.vala +++ b/libbirdfont/PointConverter.vala @@ -1,5 +1,5 @@ /* - Copyright (C) 2014 Johan Mattsson + Copyright (C) 2014 2016 Johan Mattsson This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -47,6 +47,8 @@ PenTool.convert_point_segment_type (e, e.get_next (), PointType.DOUBLE_CURVE); } } + + if (!original_path.is_open ()) { if (quadratic_path.get_last_point ().get_right_handle ().type == PointType.CUBIC) { @@ -91,8 +93,9 @@ quadratic_segment_start = segment_start.copy (); quadratic_segment_stop = segment_stop.copy (); - PenTool.convert_point_segment_type (quadratic_segment_start, quadratic_segment_stop, PointType.DOUBLE_CURVE); - + PenTool.convert_point_segment_type (quadratic_segment_start, quadratic_segment_stop, PointType.DOUBLE_CURVE); + + // add new points to estimate path distance = 0; e = new EditPoint (); if (segment_start.get_right_handle ().is_line () @@ -122,7 +125,7 @@ out double distance, out EditPoint new_point, out double step) { double max_d; double min_d; - int steps = (int) (1.6 * Path.get_length_from (a0, a1)); + int steps; double x_out, y_out; double step_out;
--- a/libbirdfont/PointTool.vala +++ b/libbirdfont/PointTool.vala @@ -21,7 +21,7 @@ public class PointTool : Tool { public PointTool (string name) { - base (name, t_ ("Move control points")); + base (name, ""); select_action.connect ((self) => { }); @@ -78,12 +78,52 @@ Tool p = pen (); p.draw_action (p, cairo_context, glyph); }); + } + + public override string get_tip () { + string tip = t_ ("Move control points") + "\n"; + + tip += HiddenTools.move_along_axis.get_key_binding (); + tip += " - "; + tip += t_ ("on axis") + "\n"; + + return tip; } public static Tool pen () { return MainWindow.get_toolbox ().get_tool ("pen_tool"); + } + + public static void tie_angle (double center_x, double center_y, + double coordinate_x, double coordinate_y, + out double tied_x, out double tied_y) { + + double length = fabs (Path.distance (center_x, coordinate_x, + center_y, coordinate_y)); + + tied_x = 0; + tied_y = 0; + + double min = double.MAX; + double circle_edge; + double circle_x; + double circle_y; + + for (double circle_angle = 0; circle_angle < 2 * PI; circle_angle += PI / 4) { + circle_x = center_x + cos (circle_angle) * length; + circle_y = center_y + sin (circle_angle) * length; + + circle_edge = fabs (Path.distance (coordinate_x, circle_x, + coordinate_y, circle_y)); + + if (circle_edge < min) { + tied_x = circle_x; + tied_y = circle_y; + min = circle_edge; + } + } } } }
--- a/libbirdfont/Preferences.vala +++ b/libbirdfont/Preferences.vala @@ -77,6 +77,25 @@ set ("recent_files", @"$(recent.str)"); } + public static void set_window_size (int x, int y, int width, int height) { + set ("window_x", @"$x"); + set ("window_y", @"$y"); + set ("window_width", @"$width"); + set ("window_height", @"$height"); + } + + public static int get_window_x () { + string wp = get ("window_x"); + int x = int.parse (wp); + return x; + } + + public static int get_window_y () { + string wp = get ("window_y"); + int y = int.parse (wp); + return y; + } + public static int get_window_width() { string wp = get ("window_width"); int w = int.parse (wp);
--- a/libbirdfont/Preview.vala +++ b/libbirdfont/Preview.vala @@ -28,7 +28,7 @@ } public override void selected_canvas () { - MainWindow.set_scrollbar_size (0); + MainWindow.set_scrollbar_size (0); } public static string get_html_path () { @@ -113,11 +113,26 @@ try { dis = new DataInputStream (get_file ().read ()); - preview_directory = BirdFont.get_preview_directory (); + string? d = font.get_export_directory (); + + if (d == null) { + warning ("Export dir is not set."); + ExportTool.set_output_directory (); + d = font.get_export_directory (); + } + + preview_directory = File.new_for_path ((!) d); + + printd (@"previwdir $((!) d)"); + + if (ExportTool.get_export_folder () == "") { + ExportTool.set_output_directory (); + } - f_ttf = get_child (ExportTool.get_export_dir (), @"$(ExportSettings.get_file_name (font)).ttf"); - f_eot = get_child (ExportTool.get_export_dir (), @"$(ExportSettings.get_file_name (font)).eot"); - f_svg = get_child (ExportTool.get_export_dir (), @"$(ExportSettings.get_file_name (font)).svg"); + File dir = File.new_for_path ((!) d); + f_ttf = get_child (dir, @"$(ExportSettings.get_file_name (font)).ttf"); + f_eot = get_child (dir, @"$(ExportSettings.get_file_name (font)).eot"); + f_svg = get_child (dir, @"$(ExportSettings.get_file_name (font)).svg"); if (!f_ttf.query_exists ()) { warning ("TTF file does not exist."); @@ -127,10 +142,12 @@ warning ("SVG file does not exist."); } + string name = ExportSettings.get_file_name (font); + while ((line = dis.read_line (null)) != null) { - line = ((!) line).replace (@"$(ExportSettings.get_file_name (font)).ttf", @"$(TabContent.path_to_uri ((!) f_ttf.get_path ()))?$rid"); - line = ((!) line).replace (@"$(ExportSettings.get_file_name (font)).eot", @"$(TabContent.path_to_uri ((!) f_eot.get_path ()))?$rid"); - line = ((!) line).replace (@"$(ExportSettings.get_file_name (font)).svg", @"$(TabContent.path_to_uri ((!) f_svg.get_path ()))?$rid"); + line = ((!) line).replace (@"$name.ttf", @"$(TabContent.path_to_uri ((!) f_ttf.get_path ()))?$rid"); + line = ((!) line).replace (@"$name.eot", @"$(TabContent.path_to_uri ((!) f_eot.get_path ()))?$rid"); + line = ((!) line).replace (@"$name.svg", @"$(TabContent.path_to_uri ((!) f_svg.get_path ()))?$rid"); sb.append ((!) line); } @@ -138,6 +155,7 @@ warning (e.message); warning ("Failed to load html into canvas."); } + return sb.str; }
--- a/libbirdfont/PreviewTools.vala +++ b/libbirdfont/PreviewTools.vala @@ -62,7 +62,10 @@ Preview.generate_html_document (); } - MainWindow.tabs.select_tab_name ("Preview"); + if (ExportTool.error_message == null) { + MainWindow.tabs.select_tab_name ("Preview"); + } + return false; });
--- a/libbirdfont/QuestionDialog.vala +++ b/libbirdfont/QuestionDialog.vala @@ -28,12 +28,12 @@ double height = 0; public QuestionDialog (string message) { - Color color = Theme.get_color ("Text Tool Box"); - question = new TextArea (font_size, color); + question = new TextArea (font_size); question.min_width = 300; question.min_height = font_size; question.set_editable (false); question.draw_border = false; + question.text_color = Theme.get_color ("Text Tool Box"); question.set_text (message); buttons = new Gee.ArrayList<Button> (); }
--- a/libbirdfont/RecentFiles.vala +++ b/libbirdfont/RecentFiles.vala @@ -42,12 +42,16 @@ } else if (row.get_index () == RECENT_FONT) { return_if_fail (row.get_row_data () is Font); f = (Font) row.get_row_data (); + MainWindow.scrollbar.set_size (0); + GlyphCanvas.redraw (); load_font (f.get_path ()); } else if (row.get_index () == BACKUP) { return_if_fail (row.get_row_data () is Font); f = (Font) row.get_row_data (); delete_backup (f.get_file_name ()); } + + GlyphCanvas.redraw (); } public override void update_rows () { @@ -240,7 +244,6 @@ f = BirdFont.new_font (); - MainWindow.clear_glyph_cache (); MainWindow.close_all_tabs (); f.set_file (fn);
--- /dev/null +++ b/libbirdfont/Renderer/CachedFont.vala @@ -1,1 +1,100 @@ + /* + Copyright (C) 2015 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + using Gee; + + namespace BirdFont { + + public class CachedFont : GLib.Object { + public Font? font; + + public double top_limit { + get { return _top_limit; } + set { _top_limit = value; } + } + + public double bottom_limit { + get { return _bottom_limit; } + set { _bottom_limit = value; } + } + + public double base_line = 0; + double _top_limit = 92.77; // FIXME: load before first glyph + double _bottom_limit = -24.4; + + static FallbackFont fallback_font { + get { + if (_fallback_font == null) { + _fallback_font = new FallbackFont (); + } + + return (!) _fallback_font; + } + } + static FallbackFont? _fallback_font = null; + + public CachedFont (Font? font) { + Glyph? g; + Glyph glyph; + + this.font = font; + + g = get_glyph_by_name ("a"); + if (g != null) { + glyph = (!) g; + base_line = glyph.baseline; + top_limit = glyph.top_limit; + bottom_limit = glyph.bottom_limit; + } else { + warning("No default chararacter found in font."); + } + } + + public Glyph? get_glyph_by_name (string name) { + Glyph? g = null; + Font f; + Glyph glyph; + + if (font != null) { + f = (!) font; + g = f.get_glyph_by_name (name); + + if (g != null) { + glyph = (!) g; + glyph.top_limit = f.top_limit; + glyph.baseline = f.base_line; + glyph.bottom_limit = f.bottom_limit; + } + } + + if (g == null && name.char_count () == 1) { + f = fallback_font.get_single_glyph_font (name.get_char (0)); + g = f.get_glyph_by_name (name); + + if (g == null) { + return null; + } + + glyph = (!) g; + glyph.top_limit = f.top_limit; + glyph.baseline = f.base_line; + glyph.bottom_limit = f.bottom_limit; + } + + return g; + } + } + + }
--- /dev/null +++ b/libbirdfont/Renderer/Drawing.vala @@ -1,1 +1,51 @@ + /* + Copyright (C) 2014 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + namespace BirdFont { + + /** Interface for creating drawing callbacks. */ + [Compact] + [CCode (ref_function = "bird_font_drawing_ref", unref_function = "bird_font_drawing_unref")] + public class Drawing { + + public int iterator_refcount = 1; + + public Drawing () { + } + + public void new_path (double x, double y) { + } + + public void curve_to (double xb, double yb, double xc, double yc, double xd, double yd) { + } + + public void close_path (double x, double y) { + } + + public unowned Drawing @ref () { + iterator_refcount++; + return this; + } + + public void unref () { + if (--iterator_refcount == 0) { + this.free (); + } + } + + private extern void free (); + } + + }
--- /dev/null +++ b/libbirdfont/Renderer/FallbackFont.vala @@ -1,1 +1,337 @@ + /* + Copyright (C) 2015 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + using Gee; + + [SimpleType] + [CCode (has_type_id = false)] + public extern struct FcConfig { + } + + [CCode (cname = "FcInitLoadConfigAndFonts")] + public extern FcConfig* FcInitLoadConfigAndFonts (); + + [CCode (cname = "FcConfigAppFontAddDir")] + public extern string* FcConfigAppFontAddDir (FcConfig* config, string path); + + [CCode (cname = "FcConfigSetSysRoot")] + public extern void FcConfigSetSysRoot (FcConfig* config, string path); + + [CCode (cname = "FcConfigParseAndLoad")] + public extern bool FcConfigParseAndLoad (FcConfig* config, string path, bool complain); + + [CCode (cname = "FcConfigSetCurrent")] + public extern void FcConfigSetCurrent (FcConfig* config); + + [CCode (cname = "FcConfigCreate")] + public extern FcConfig* FcConfigCreate (); + + [CCode (cname = "FcConfigFilename")] + public extern string FcConfigFilename (string path); + + [CCode (cname = "find_font")] + public extern string? find_font (FcConfig* font_config, string characters); + + [CCode (cname = "find_font_family")] + public extern string? find_font_family (FcConfig* font_config, string characters); + + [CCode (cname = "find_font_file")] + public extern string? find_font_file (FcConfig* font_config, string font_name); + + namespace BirdFont { + + // TODO: use font config + public class FallbackFont : GLib.Object { + Gee.ArrayList<File> font_directories; + + FontFace* default_font = null; + public static FcConfig* font_config = null; + static bool font_config_started = false; + + string default_font_file_name = "Roboto-Regular.ttf"; + string default_font_family_name = "Roboto"; + + Gee.HashMap<unichar, CachePair> glyphs; + Gee.ArrayList<CachePair> cached; + + public int max_cached_fonts = 300; + + string? default_font_file = null; + + public FallbackFont () { + string home = Environment.get_home_dir (); + font_directories = new Gee.ArrayList<File> (); + + if (!font_config_started) { + font_config_started = true; + + IdleSource idle = new IdleSource (); + idle.set_callback (() => { + Task t = new Task (init_font_config); + MainWindow.native_window.run_non_blocking_background_thread (t); + return false; + }); + idle.attach (null); + } + + add_font_folder ("/usr/share/fonts/"); + add_font_folder ("/usr/local/share/fonts/"); + add_font_folder (home + "/.local/share/fonts"); + add_font_folder (home + "/.fonts"); + add_font_folder ("C:\\Windows\\Fonts"); + add_font_folder (home + "/Library/Fonts"); + add_font_folder ("/Library/Fonts"); + add_font_folder ("/Network/Library/Fonts"); + add_font_folder ("/System/Library/Fonts"); + add_font_folder ("/System Folder/Fonts"); + + glyphs = new Gee.HashMap<unichar, CachePair> (); + cached = new Gee.ArrayList<CachePair> (); + + open_default_font (); + } + + ~FallbackFont () { + if (default_font != null) { + close_font (default_font); + } + } + + public void init_font_config () { + FcConfig* config; + + #if MAC + config = FcConfigCreate(); + + string bundle = (!) BirdFont.get_settings_directory ().get_path (); + FcConfigSetSysRoot(config, bundle); + + string path = FcConfigFilename((!) SearchPaths.search_file(null, "fontconfig.settings").get_path ()); + bool loaded = FcConfigParseAndLoad(config, path, true); + + if (!loaded) { + warning ("Cannot load fontconfig."); + } + + FcConfigSetCurrent (config); + #else + config = FcInitLoadConfigAndFonts (); + #endif + + IdleSource idle = new IdleSource (); + + idle.set_callback (() => { + font_config = config; + return false; + }); + idle.attach (null); + } + + public Font get_single_glyph_font (unichar c) { + Font f; + unichar last; + CachePair p; + + if (likely (glyphs.has_key (c))) { + p = glyphs.get (c); + + if (p.referenced < int.MAX) { + p.referenced++; + } + + return p.font; + } + + // remove glyphs from cache if it is full + if (cached.size > max_cached_fonts - 100) { + + cached.sort ((a, b) => { + CachePair pa = (CachePair) a; + CachePair pb = (CachePair) b; + return pb.referenced - pa.referenced; + }); + + int j = 0; + for (int i = cached.size - 1; i > 0; i--) { + if (j > 100) { + break; + } + + j++; + + last = cached.get (i).character; + glyphs.unset (last); + cached.remove_at (i); + } + } + + f = get_single_fallback_glyph_font (c); + p = new CachePair (f, c); + + glyphs.set (c, p); + cached.add (p); + + return (Font) f; + } + + Font get_single_fallback_glyph_font (unichar c) { + string? font_file; + BirdFontFile bf_parser; + Font bf_font; + StringBuilder? glyph_data; + FontFace* font; + + bf_font = new Font (); + font_file = null; + glyph_data = null; + + // don't use fallback font in private use area + if (0xe000 <= c <= 0xf8ff) { + return bf_font; + } + + // control characters + if (c <= 0x001f || (0x007f <= c <= 0x008d)) { + return bf_font; + } + + // check if glyph is available in roboto + if (default_font != null) { + glyph_data = get_glyph_in_font ((!) default_font, c); + } + + // use fontconfig to find a fallback font + if (glyph_data == null) { + font_file = find_font (font_config, (!) c.to_string ()); + if (font_file != null) { + font = open_font ((!) font_file); + glyph_data = get_glyph_in_font (font, c); + close_font (font); + } + } + + if (glyph_data != null) { + bf_parser = new BirdFontFile (bf_font); + bf_parser.load_data (((!) glyph_data).str); + } + + return bf_font; + } + + public StringBuilder? get_glyph_in_font (FontFace* font, unichar c) { + StringBuilder? glyph_data = null; + GlyphCollection gc; + + gc = new GlyphCollection (c, (!)c.to_string ()); + glyph_data = load_glyph (font, (uint) c); + + return glyph_data; + } + + void add_font_folder (string f) { + File folder = File.new_for_path (f); + FileInfo? file_info; + string fn; + string file_attributes; + try { + if (folder.query_exists ()) { + font_directories.add (folder); + + file_attributes = FileAttribute.STANDARD_NAME; + file_attributes += ","; + file_attributes += FileAttribute.STANDARD_TYPE; + var enumerator = folder.enumerate_children (file_attributes, 0); + + while ((file_info = enumerator.next_file ()) != null) { + fn = ((!) file_info).get_name (); + + if (((!)file_info).get_file_type () == FileType.DIRECTORY) { + add_font_folder ((!) get_child (folder, fn).get_path ()); + } + } + } + } catch (GLib.Error e) { + warning (e.message); + } + } + + File search_font_file (string font_file) { + File d, f; + + for (int i = font_directories.size - 1; i >= 0; i--) { + d = font_directories.get (i); + f = get_child (d, font_file); + + if (f.query_exists ()) { + return f; + } + } + + warning (@"The font $font_file not found"); + return File.new_for_path (font_file); + } + + public string? get_default_font_file () { + File font_file; + string? fn = null; + + if (likely (default_font_file != null)) { + return default_font_file; + } + + font_file = SearchPaths.search_file (null, default_font_file_name); + + if (font_file.query_exists ()) { + fn = (!) font_file.get_path (); + } else { + font_file = search_font_file (default_font_file_name); + + if (font_file.query_exists ()) { + fn = (!) font_file.get_path (); + } else { + fn = find_font_file (font_config, default_font_family_name); + } + } + + if (likely (fn != null)) { + default_font_file = fn; + return fn; + } + + warning(default_font_family_name + " not found"); + return null; + } + + void open_default_font () { + string? fn = get_default_font_file (); + + if (fn != null) { + default_font = open_font ((!) fn); + } + } + + class CachePair : GLib.Object { + public Font font; + public unichar character; + public int referenced = 1; + + public CachePair (Font f, unichar c) { + font = f; + character = c; + } + } + } + + }
--- /dev/null +++ b/libbirdfont/Renderer/FontCache.vala @@ -1,1 +1,84 @@ + /* + Copyright (C) 2014 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + using Gee; + + namespace BirdFont { + + /** Thread specific font cache. */ + public class FontCache { + public static FallbackFont fallback_font; + + static FontCache? default_cache = null; + Gee.HashMap<string, CachedFont> fonts; + CachedFont fallback; + + public FontCache () { + if (is_null (fallback_font)) { + fallback_font = new FallbackFont (); + } + + fallback = new CachedFont (null); + fonts = new Gee.HashMap<string, CachedFont> (); + } + + public CachedFont get_font (string file_name) { + CachedFont c; + Font f; + bool ok; + + if (file_name == "") { + return fallback; + } + + if (fonts.has_key (file_name)) { + c = fonts.get (file_name); + return c; + } + + f = new Font (); + f.set_file (file_name); + ok = f.load (); + if (!ok) { + stderr.printf ("Can't load %s\n", file_name); + return new CachedFont (null); + } + + c = new CachedFont (f); + + if (file_name == "") { + warning ("No file."); + return c; + } + + fonts.set (file_name, c); + return c; + } + + public static FontCache get_default_cache () { + if (default_cache == null) { + default_cache = new FontCache (); + } + + return (!) default_cache; + } + + public CachedFont get_fallback () { + return fallback; + } + + } + + }
--- /dev/null +++ b/libbirdfont/Renderer/LineTextArea.vala @@ -1,1 +1,34 @@ + /* + Copyright (C) 2014 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + using Cairo; + using Math; + + namespace BirdFont { + + public class LineTextArea : TextArea { + + public LineTextArea (double size) { + base (size); + + single_line = true; + min_height = size; + height = min_height; + + layout (); + } + } + + }
--- /dev/null +++ b/libbirdfont/Renderer/Text.vala @@ -1,1 +1,592 @@ + /* + Copyright (C) 2014 2015 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + using Cairo; + using Math; + + namespace BirdFont { + + /** Test implementation of a birdfont rendering engine. */ + public class Text : Widget { + FontCache font_cache; + public CachedFont cached_font; + + Surface? cache = null; + + public string text; + + private bool use_cache = true; + + GlyphSequence glyph_sequence { + get { + if (gs == null) { + gs = generate_glyphs (); + } + + return (!) gs; + } + } + + Gee.ArrayList<string> glyph_names; + GlyphSequence? gs = null; + + public delegate void Iterator (Glyph glyph, double kerning, bool last); + public double font_size; + double sidebearing_extent = 0; + + public double r = 0; + public double g = 0; + public double b = 0; + public double a = 1; + double truncated_width = -1; + + double margin_left = 0; + + public Text (string text = "", double size = 17, double margin_bottom = 0) { + this.margin_bottom = margin_bottom; + font_cache = FontCache.get_default_cache (); + cached_font = font_cache.get_fallback (); + + set_text (text); + set_font_size (size); + } + + public void set_use_cache (bool cache) { + use_cache = cache; + } + + public string get_text () { + return text; + } + + /** Set font for this text area. + * @param font_absolute path to the font file or a file name for one of the font files in search paths. + * @return true if the font was found + */ + public bool load_font (string font_file) { + File path; + File f; + FontCache fc; + + f = File.new_for_path (font_file); + path = (f.query_exists ()) ? f : SearchPaths.find_file (null, font_file); + + fc = FontCache.get_default_cache (); + cached_font = fc.get_font ((!) path.get_path ()); + gs = generate_glyphs (); + + return cached_font.font != null; + } + + public void set_font_size (double height_in_pixels) { + font_size = height_in_pixels; + sidebearing_extent = 0; + + if (gs == null) { // ensure height is loaded for the font + gs = generate_glyphs (); + } + } + + public void set_font_cache (FontCache font_cache) { + this.font_cache = font_cache; + } + + public void set_text (string text) { + this.text = text; + gs = null; + sidebearing_extent = 0; + cache = null; + } + + private GlyphSequence generate_glyphs () { + int index; + unichar c; + string name; + Glyph? g; + GlyphSequence gs; + + gs = new GlyphSequence (); + + glyph_names = new Gee.ArrayList<string> (); + index = 0; + while (text.get_next_char (ref index, out c)) { + name = (!) c.to_string (); + g = cached_font.get_glyph_by_name (name); + + gs.glyph.add (g); + glyph_names.add (name); + } + + return gs; + } + + public void iterate (Iterator iter) { + Glyph glyph; + double w, kern; + int wi; + Glyph? prev; + Glyph? g; + GlyphSequence word_with_ligatures; + GlyphRange? gr_left, gr_right; + GlyphSequence word; + KerningClasses kc; + Font empty = Font.empty; + + glyph = new Glyph.no_lines ("", '\0'); + + w = 0; + prev = null; + kern = 0; + + word = glyph_sequence; + wi = 0; + + if (cached_font.font != null) { + word_with_ligatures = word.process_ligatures ((!) cached_font.font); + } else { + word_with_ligatures = word.process_ligatures (new Font ()); + } + + gr_left = null; + gr_right = null; + + if (cached_font.font != null) { + kc = ((!) cached_font.font).get_kerning_classes (); + } else { + kc = new KerningClasses (empty); + } + + if (word_with_ligatures.glyph.size > 0) { + double none = 0; + g = word_with_ligatures.glyph.get (0); + if (g != null) { + margin_left = ((!) g).get_left_side_bearing (); + + if (margin_left < 0) { + margin_left = -margin_left; + } else { + margin_left = 0; + } + } + } + + for (int i = 0; i < word_with_ligatures.glyph.size; i++) { + g = word_with_ligatures.glyph.get (i); + + if (g == null || prev == null || wi == 0) { + kern = 0; + } else { + return_if_fail (wi < word_with_ligatures.ranges.size); + return_if_fail (wi - 1 >= 0); + + gr_left = word_with_ligatures.ranges.get (wi - 1); + gr_right = word_with_ligatures.ranges.get (wi); + + kern = kc.get_kerning_for_pair (((!) prev).get_name (), ((!) g).get_name (), gr_left, gr_right); + } + + // process glyph + if (g == null && (0 <= i < glyph_names.size)) { + g = cached_font.get_glyph_by_name (glyph_names.get (i)); + } + + glyph = (g == null) ? new Glyph ("") : (!) g; + iter (glyph, kern, i + 1 == word_with_ligatures.glyph.size); + prev = g; + wi++; + } + } + + // FIXME: some fonts doesn't have on curve extrema + public double get_extent () { + double x = 0; + + iterate ((glyph, kerning, last) => { + double x1, y1, x2, y2; + double lsb; + + lsb = glyph.left_limit; + + if (!last) { + x += (glyph.get_width () + kerning) * get_scale (glyph); + } else { + glyph.boundaries (out x1, out y1, out x2, out y2); + x += (x2 - lsb) * get_scale (glyph); + } + }); + + return x; + } + + public double get_sidebearing_extent () { + double x ; + + if (likely (sidebearing_extent > 0)) { + return sidebearing_extent; + } + + x = 0; + + iterate ((glyph, kerning, last) => { + double lsb; + lsb = glyph.left_limit; + x += (glyph.get_width () + kerning) * get_scale (glyph); + }); + + sidebearing_extent = x; + return x; + } + + public override double get_height () { + return font_size; + } + + public double get_acender () { + double max_height = 0; + + iterate ((glyph, kerning, last) => { + double x1, y1, x2, y2; + double h; + glyph.boundaries (out x1, out y1, out x2, out y2); + h = Math.fmax (y1, y2) - Math.fmin (y1, y2); + h *= get_scale (glyph) - glyph.baseline * get_scale (glyph); + if (h > max_height) { + max_height = h; + } + }); + + return max_height; + } + + public override double get_width () { + double x = 0; + bool first = true; + + iterate ((glyph, kerning, last) => { + double x1, y1, x2, y2; + double lsb; + + lsb = glyph.left_limit; + + if (first) { + glyph.boundaries (out x1, out y1, out x2, out y2); + x += (glyph.get_width () + kerning - Math.fmin (x1, x2)) * get_scale (glyph); + first = false; + } else if (!last) { + x += (glyph.get_width () + kerning) * get_scale (glyph); + } else { + glyph.boundaries (out x1, out y1, out x2, out y2); + x += (x2 - lsb) * get_scale (glyph); + } + }); + + return x; + } + + public double get_decender () { + double decender_max = get_max_decender (); + return decender_max > 0 ? decender_max : 0; + } + + private double get_max_decender () { + double decender = 0; + double decender_max = 0; + + iterate ((glyph, kerning, last) => { + double x1, y1, x2, y2; + double y; + glyph.boundaries (out x1, out y1, out x2, out y2); + y = Math.fmin (y1, y2); + decender = (glyph.baseline - y) * get_scale (glyph); + if (decender > decender_max) { + decender_max = decender; + } + }); + + return decender_max; + } + + public override void draw (Context cr) { + double descender = cached_font.bottom_limit + cached_font.base_line; + double y = widget_y + get_height () + get_font_scale () * descender; // FIXME: + draw_at_baseline (cr, widget_x, y); + } + + public void draw_at_top (Context cr, double px, double py, string cacheid = "") { + double s = get_font_scale (); + double y = py + s * (cached_font.top_limit - cached_font.base_line); + draw_at_baseline (cr, px, y, cacheid); + } + + public void set_source_rgba (double r, double g, double b, double a) { + if (this.r != r || + this.g != g || + this.b != b || + this.a != a) { + + this.r = r; + this.g = g; + this.b = b; + this.a = a; + cache = null; + } + } + + public string get_cache_id (int offset_x, int offset_y) { + string key; + int64 c; + + c = (((int64) (r * 255)) << 24) + | (((int64) (g * 255)) << 16) + | (((int64) (b * 255)) << 8) + | (((int64) (a * 255)) << 0); + + // FIXME: use binary key + key = @"$font_size $c $offset_x $offset_y"; + + return key; + } + + public void draw_at_baseline (Context cr, double px, double py, string cacheid = "") { + double x, y; + double ratio; + double cc_y; + + if (cache == null) { + cache = draw_on_cache_surface (cacheid); + } + + double screen_scale = Screen.get_scale (); + double font_scale = get_font_scale (); + double cache_y = py - font_scale * (cached_font.top_limit - cached_font.base_line); + + cr.save(); + cr.scale (1 / screen_scale, 1 / screen_scale); + double scaled_x = (px - margin_left) * screen_scale; + double scaled_y = cache_y * screen_scale; + cr.set_source_surface ((!) cache, (int) rint (scaled_x), (int) rint (scaled_y)); + cr.paint (); + cr.restore(); + } + + Surface draw_on_cache_surface (string cacheid) { + double y; + double ratio; + double cc_y; + Context cr; + Surface cache_surface; + double screen_scale = Screen.get_scale(); + double h = font_size * screen_scale + 1; + + ratio = get_font_scale (); + cc_y = (cached_font.top_limit - cached_font.base_line) * ratio; + + // double x = margin_left * ratio; + double x = 0; + double py = cc_y; + + double w = get_sidebearing_extent () * screen_scale + x + margin_left + 1; + + cache_surface = Screen.create_background_surface ((int) w, (int) h); + cr = new Context (cache_surface); + cr.scale (screen_scale, screen_scale); + + y = cc_y; + + if (unlikely (cached_font.base_line != 0)) { + warning ("Base line not zero."); + } + + iterate ((glyph, kerning, last) => { + double end; + + x += kerning * ratio; + end = x + glyph.get_width () * ratio; + + // truncation + if (truncated_width > 0 && end > truncated_width) { + return; + } + + if (use_cache) { + draw_chached (cr, glyph, kerning, last, x, y, cc_y, + ratio, cacheid); + } else { + draw_without_cache (cr, glyph, kerning, last, x, y, cc_y, ratio); + } + + x = end; + }); + + return cache_surface; + } + + void draw_without_cache (Context cr, Glyph glyph, double kerning, bool last, + double x, double y, double cc_y, double ratio) { + + double lsb; + + cr.save (); + cr.set_source_rgba (r, g, b, a); + cr.new_path (); + + lsb = glyph.left_limit; + + foreach (Path path in glyph.get_visible_paths ()) { + draw_path (cr, glyph, path, lsb, x, y, ratio); + } + + cr.fill (); + cr.restore (); + + } + + void draw_chached (Context cr, Glyph glyph, double kerning, bool last, + double x, double y, double cc_y, double ratio, + string cacheid = "") { + + double lsb; + Surface cache; + Surface cached_glyph; + Context cc; + string cache_id; + double glyph_margin_left = glyph.get_left_side_bearing (); + + if (glyph_margin_left < 0) { + glyph_margin_left = -glyph_margin_left; + } else { + glyph_margin_left = 0; + } + + double xp = (x - glyph_margin_left * ratio) * Screen.get_scale (); + double yp = (y - cc_y) * Screen.get_scale (); + int offset_x, offset_y; + + offset_x = (int) (10 * (xp - (int) xp)); + offset_y = (int) (10 * (yp - (int) yp)); + + cache_id = (cacheid == "") ? get_cache_id (offset_x, offset_y) : cacheid; + + if (!glyph.has_cache (cache_id)) { + int w = (int) ((2 * glyph_margin_left * ratio + glyph.get_width ()) * ratio) + 2; + int h = (int) font_size + 2; + cache = Screen.create_background_surface (w, h); + cc = new Context (cache); + + cc.scale(Screen.get_scale (), Screen.get_scale ()); + + lsb = glyph.left_limit - glyph_margin_left; + + cc.save (); + cc.set_source_rgba (r, g, b, a); + cc.new_path (); + + foreach (Path path in glyph.get_visible_paths ()) { + draw_path (cc, glyph, path, lsb, glyph_margin_left * ratio + offset_x / 10.0, cc_y + offset_y / 10.0, ratio); + } + + cc.fill (); + cc.restore (); + + if (use_cache) { + glyph.set_cache (cache_id, cache); + } + + cached_glyph = cache; + } else { + cached_glyph = glyph.get_cache (cache_id); + } + + cr.save (); + cr.set_antialias (Cairo.Antialias.NONE); + cr.scale(1 / Screen.get_scale (), 1 / Screen.get_scale ()); + cr.set_source_surface (cached_glyph, + (int) xp, + (int) yp); + cr.paint (); + cr.restore (); + } + + void draw_path (Context cr, Glyph glyph, Path path, + double lsb, double x, double y, double scale) { + + EditPoint e, prev; + double xa, ya, xb, yb, xc, yc, xd, yd; + double by; + double s = get_scale (glyph); + + if (path.points.size > 0) { + if (unlikely (path.is_open ())) { + warning (@"Path is open in $(glyph.get_name ())."); + } + + //path.add_hidden_double_points (); // FIXME: this distorts shapes + + prev = path.points.get (path.points.size - 1); + xa = (prev.x - lsb) * s + x; + ya = y - prev.y * s; + cr.move_to (xa, ya); + + by = (y - cached_font.base_line * s); + for (int i = 0; i < path.points.size; i++) { + e = path.points.get (i).copy (); + + PenTool.convert_point_segment_type (prev, e, PointType.CUBIC); + + xb = (prev.get_right_handle ().x - lsb) * s + x; + yb = by - prev.get_right_handle ().y * s; + + xc = (e.get_left_handle ().x - lsb) * s + x; + yc = by - e.get_left_handle ().y * s; + + xd = (e.x - lsb) * s + x; + yd = by - e.y * s; + + cr.curve_to (xb, yb, xc, yc, xd, yd); + cr.line_to (xd, yd); + + prev = e; + } + } + } + + public double get_baseline_to_bottom (Glyph g) { + return get_scale (g) * (-g.baseline - g.bottom_limit); + } + + public double get_scale (Glyph g) { + double s = g.top_limit - g.bottom_limit; + + if (s == 0) { + s = cached_font.top_limit - cached_font.bottom_limit; + } + + return font_size / s; + } + + public double get_font_scale () { + return font_size / (cached_font.top_limit - cached_font.bottom_limit); + } + + public double get_baseline_to_bottom_for_font () { + return get_font_scale () * (-cached_font.base_line - cached_font.bottom_limit); + } + + public void truncate (double max_width) { + truncated_width = max_width; + } + } + + }
--- /dev/null +++ b/libbirdfont/Renderer/TextArea.vala @@ -1,1 +1,1666 @@ + /* + Copyright (C) 2014 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + using Cairo; + using Math; + + namespace BirdFont { + + public class TextArea : Widget { + + public double min_width = 500; + public double min_height = 100; + public double font_size; + public double padding = 3.3; + public bool single_line = false; + public Color text_color = Color.black (); + + public bool draw_carret { + get { return carret_is_visible; } + set { + carret_is_visible = value; + if (!value) { + update_selection = false; + selection_end = carret.copy (); + } + } + } + public bool carret_is_visible = false; + public bool draw_border = true; + + public double width; + public double height; + + Carret carret = new Carret (); + Carret selection_end = new Carret (); + bool update_selection = false; + public bool show_selection = false; + + public signal void scroll (double pixels); + public signal void text_changed (string text); + public signal void enter (string text); + + Gee.ArrayList<Paragraph> paragraphs = new Gee.ArrayList<Paragraph> (); + private static const int DONE = -2; + + int last_paragraph = 0; + string text; + int text_length; + + Gee.ArrayList<TextUndoItem> undo_items = new Gee.ArrayList<TextUndoItem> (); + Gee.ArrayList<TextUndoItem> redo_items = new Gee.ArrayList<TextUndoItem> (); + + bool store_undo_state_at_next_event = false; + + public bool editable; + + public TextArea (double font_size = 20, Color? c = null) { + this.font_size = font_size; + width = min_width; + height = min_height; + editable = true; + + if (c != null) { + text_color = (!) c; + } + } + + public override void focus (bool focus) { + draw_carret = focus; + } + + public override double get_height () { + return height + 2 * padding; + } + + public override double get_width () { + return width + 2 * padding; + } + + public void set_font_size (double z) { + font_size = z; + } + + bool generate_paragraphs () { + Paragraph paragraph; + + int next_paragraph = -1; + + if (is_null (text)) { + warning ("No text"); + return false; + } + + if (last_paragraph == DONE) { + return false; + } + + next_paragraph = text.index_of ("\n", last_paragraph); + + if (next_paragraph == -1) { + paragraph = new Paragraph (text.substring (last_paragraph), font_size, paragraphs.size, text_color); + paragraphs.add (paragraph); + last_paragraph = DONE; + } else { + next_paragraph += "\n".length; + paragraph = new Paragraph (text.substring (last_paragraph, next_paragraph - last_paragraph), font_size, paragraphs.size, text_color); + paragraphs.add (paragraph); + last_paragraph = next_paragraph; + } + + return last_paragraph != DONE; + } + + void generate_all_paragraphs () { + while (generate_paragraphs ()) { + } + } + + public override void key_press (uint keyval) { + unichar c; + TextUndoItem ui; + + if (!editable) { + return; + } + + c = (unichar) keyval; + + switch (c) { + case ' ': + store_undo_edit_state (); + add_character (keyval); + break; + case 'a': + if (KeyBindings.has_ctrl () || KeyBindings.has_logo ()) { + select_all (); + } else { + add_character (keyval); + } + break; + case 'c': + if (KeyBindings.has_ctrl () || KeyBindings.has_logo ()) { + ClipTool.copy_text (this); + } else { + add_character (keyval); + } + break; + case 'v': + if (KeyBindings.has_ctrl () || KeyBindings.has_logo ()) { + ClipTool.paste_text (this); + store_undo_state_at_next_event = true; + } else { + add_character (keyval); + } + break; + case 'y': + if (KeyBindings.has_ctrl () || KeyBindings.has_logo ()) { + redo (); + } else { + add_character (keyval); + } + break; + case 'z': + if (KeyBindings.has_ctrl () || KeyBindings.has_logo ()) { + undo (); + } else { + add_character (keyval); + } + break; + case Key.RIGHT: + check_selection (); + move_carret_next (); + break; + case Key.LEFT: + check_selection (); + move_carret_previous (); + break; + case Key.DOWN: + check_selection (); + move_carret_next_row (); + break; + case Key.UP: + check_selection (); + move_carret_previous_row (); + break; + case Key.END: + check_selection (); + move_carret_to_end_of_line (); + break; + case Key.HOME: + check_selection (); + move_carret_to_beginning_of_line (); + break; + case Key.BACK_SPACE: + if (has_selection ()) { + ui = delete_selected_text (); + undo_items.add (ui); + redo_items.clear (); + store_undo_state_at_next_event = true; + } else { + ui = remove_last_character (); + undo_items.add (ui); + redo_items.clear (); + store_undo_state_at_next_event = true; + } + text_changed (get_text ()); + break; + case Key.ENTER: + store_undo_edit_state (); + insert_text ("\n"); + + if (single_line) { + enter (get_text ()); + } + break; + case Key.DEL: + if (has_selection ()) { + ui = delete_selected_text (); + undo_items.add (ui); + redo_items.clear (); + store_undo_state_at_next_event = true; + } else { + ui = remove_next_character (); + undo_items.add (ui); + redo_items.clear (); + store_undo_state_at_next_event = true; + } + text_changed (get_text ()); + break; + default: + if (!KeyBindings.has_ctrl () && !KeyBindings.has_logo ()) { + add_character (keyval); + } + break; + } + + GlyphCanvas.redraw (); + } + + void check_selection () { + if (!has_selection () && KeyBindings.has_shift ()) { + show_selection = true; + selection_end = carret.copy (); + } + + if (!KeyBindings.has_shift ()) { + show_selection = false; + } + } + + private void add_character (uint keyval) { + unichar c = (unichar) keyval; + string s; + + if (!is_modifier_key (keyval) + && !KeyBindings.has_ctrl () + && !KeyBindings.has_alt ()) { + + s = (!) c.to_string (); + + if (s.validate ()) { + if (store_undo_state_at_next_event) { + store_undo_edit_state (); + store_undo_state_at_next_event = false; + } + + insert_text (s); + } + } + } + + Paragraph get_current_paragraph () { + Paragraph p; + + if (unlikely (!(0 <= carret.paragraph < paragraphs.size))) { + warning (@"No paragraph, index: $(carret.paragraph), size: $(paragraphs.size)"); + p = new Paragraph ("", 0, 0, text_color); + paragraphs.add (p); + return p; + } + + p = paragraphs.get (carret.paragraph); + return p; + } + + public void set_text (string t) { + int tl; + + if (single_line) { + text = t.replace ("\n", "").replace ("\r", ""); + } else { + text = t; + } + + tl = t.length; + text_length += tl; + + paragraphs.clear (); + generate_paragraphs (); + + return_if_fail (paragraphs.size != 0); + + carret.paragraph = paragraphs.size - 1; + carret.character_index = paragraphs.get (paragraphs.size - 1).text.length; + selection_end = carret.copy (); + show_selection = false; + + text_changed (get_text ()); + } + + Carret get_selection_start () { + if (carret.paragraph == selection_end.paragraph) { + return carret.character_index < selection_end.character_index ? carret : selection_end; + } + + return carret.paragraph < selection_end.paragraph ? carret : selection_end; + } + + Carret get_selection_stop () { + if (carret.paragraph == selection_end.paragraph) { + return carret.character_index > selection_end.character_index ? carret : selection_end; + } + + return carret.paragraph > selection_end.paragraph ? carret : selection_end; + } + + public string get_selected_text () { + Carret selection_start, selection_stop; + int i; + Paragraph pg; + StringBuilder sb; + + sb = new StringBuilder (); + + if (!has_selection ()) { + return "".dup (); + } + + selection_start = get_selection_start (); + selection_stop = get_selection_stop (); + + if (selection_start.paragraph == selection_stop.paragraph) { + pg = paragraphs.get (selection_start.paragraph); + return pg.text.substring (selection_start.character_index, selection_stop.character_index - selection_start.character_index); + } + + pg = paragraphs.get (selection_start.paragraph); + sb.append (pg.text.substring (selection_start.character_index)); + + for (i = selection_start.paragraph + 1; i < selection_stop.paragraph; i++) { + return_val_if_fail (0 <= i < paragraphs.size, "".dup ()); + pg = paragraphs.get (i); + sb.append (pg.text); + } + + pg = paragraphs.get (selection_stop.paragraph); + sb.append (pg.text.substring (0, selection_stop.character_index)); + + return sb.str; + } + + public void select_all () { + while (last_paragraph != DONE) { + generate_paragraphs (); + } + + if (paragraphs.size > 0) { + carret.paragraph = 0; + carret.character_index = 0; + selection_end.paragraph = paragraphs.size - 1; + selection_end.character_index = paragraphs.get (paragraphs.size - 1).text_length; + show_selection = true; + } + } + + public TextUndoItem delete_selected_text () { + Carret selection_start, selection_stop; + int i; + Paragraph pg, pge; + string e, s, n; + bool same; + TextUndoItem ui; + + ui = new TextUndoItem (carret); + + e = ""; + s = ""; + n = ""; + + if (!has_selection ()) { + warning ("No selected text."); + return ui; + } + + selection_start = get_selection_start (); + selection_stop = get_selection_stop (); + + same = selection_start.paragraph == selection_stop.paragraph; + + if (!same) { + return_val_if_fail (0 <= selection_start.paragraph < paragraphs.size, ui); + pg = paragraphs.get (selection_start.paragraph); + s = pg.text.substring (0, selection_start.character_index); + + return_val_if_fail (0 <= selection_stop.paragraph < paragraphs.size, ui); + pge = paragraphs.get (selection_stop.paragraph); + e = pge.text.substring (selection_stop.character_index); + + if (!s.has_suffix ("\n")) { + ui.deleted.add (pge.copy ()); + ui.edited.add (pg.copy ()); + + pg.set_text (s + e); + pge.set_text (""); + } else { + ui.edited.add (pg.copy ()); + ui.edited.add (pge.copy ()); + + pg.set_text (s); + pge.set_text (e); + } + } else { + return_val_if_fail (0 <= selection_start.paragraph < paragraphs.size, ui); + + pg = paragraphs.get (selection_start.paragraph); + n = pg.text.substring (0, selection_start.character_index); + n += pg.text.substring (selection_stop.character_index); + + if (n == "") { + ui.deleted.add (pg.copy ()); + paragraphs.remove_at (selection_start.paragraph); + } else { + ui.edited.add (pg.copy ()); + } + + pg.set_text (n); + } + + if (e == "" && !same) { + paragraphs.remove_at (selection_stop.paragraph); + } + + for (i = selection_stop.paragraph - 1; i > selection_start.paragraph; i--) { + return_val_if_fail (0 <= i < paragraphs.size, ui); + ui.deleted.add (paragraphs.get (i)); + paragraphs.remove_at (i); + } + + if (s == "" && !same) { + return_val_if_fail (0 <= selection_start.paragraph < paragraphs.size, ui); + paragraphs.remove_at (selection_start.paragraph); + } + + carret = selection_start.copy (); + selection_end = carret.copy (); + + show_selection = false; + update_paragraph_index (); + layout (); + + return ui; + } + + void update_paragraph_index () { + int i = 0; + foreach (Paragraph p in paragraphs) { + p.index = i; + i++; + } + } + + public TextUndoItem remove_last_character () { + TextUndoItem ui; + move_carret_previous (); + ui = remove_next_character (); + return ui; + } + + public TextUndoItem remove_next_character () { + Paragraph paragraph; + Paragraph next_paragraph; + int index; + unichar c; + string np; + TextUndoItem ui; + + ui = new TextUndoItem (carret); + + return_val_if_fail (0 <= carret.paragraph < paragraphs.size, ui); + paragraph = paragraphs.get (carret.paragraph); + + index = carret.character_index; + + paragraph.text.get_next_char (ref index, out c); + + if (index >= paragraph.text_length) { + np = paragraph.text.substring (0, carret.character_index); + + if (carret.paragraph + 1 < paragraphs.size) { + next_paragraph = paragraphs.get (carret.paragraph + 1); + paragraphs.remove_at (carret.paragraph + 1); + + np = np + next_paragraph.text; + + ui.deleted.add (next_paragraph); + } + + paragraph.set_text (np); + ui.edited.add (paragraph); + } else { + np = paragraph.text.substring (0, carret.character_index) + paragraph.text.substring (index); + paragraph.set_text (np); + + if (np == "") { + return_val_if_fail (carret.paragraph > 0, ui); + carret.paragraph--; + paragraph = paragraphs.get (carret.paragraph); + carret.character_index = paragraph.text_length; + + ui.deleted.add (paragraphs.get (carret.paragraph + 1)); + + paragraphs.remove_at (carret.paragraph + 1); + } else { + ui.edited.add (paragraph); + } + } + + update_paragraph_index (); + layout (); + + return ui; + } + + public void move_carret_next () { + unichar c; + + move_carret_one_character (); + + if (KeyBindings.has_ctrl ()) { + while (true) { + c = move_carret_one_character (); + + if (c == '\0' || c == ' ') { + break; + } + } + } + } + + unichar move_carret_one_character () { + Paragraph paragraph; + int index; + unichar c; + + return_val_if_fail (0 <= carret.paragraph < paragraphs.size, '\0'); + paragraph = paragraphs.get (carret.paragraph); + + index = carret.character_index; + + paragraph.text.get_next_char (ref index, out c); + + if (index >= paragraph.text_length && carret.paragraph + 1 < paragraphs.size) { + carret.paragraph++; + carret.character_index = 0; + c = ' '; + } else { + carret.character_index = index; + } + + return c; + } + + public void move_carret_previous () { + unichar c; + + move_carret_back_one_character (); + + if (KeyBindings.has_ctrl ()) { + while (true) { + c = move_carret_back_one_character (); + + if (c == '\0' || c == ' ') { + break; + } + } + } + } + + unichar move_carret_back_one_character () { + Paragraph paragraph; + int index, last_index; + unichar c; + + return_val_if_fail (0 <= carret.paragraph < paragraphs.size, '\0'); + paragraph = paragraphs.get (carret.paragraph); + + index = 0; + last_index = -1; + + while (paragraph.text.get_next_char (ref index, out c) && index < carret.character_index) { + last_index = index; + } + + if (last_index <= 0 && carret.paragraph > 0) { + carret.paragraph--; + + return_val_if_fail (0 <= carret.paragraph < paragraphs.size, '\0'); + paragraph = paragraphs.get (carret.paragraph); + carret.character_index = paragraph.text_length; + + if (paragraph.text.has_suffix ("\n")) { + carret.character_index -= "\n".length; + } + + c = ' '; + } else if (last_index > 0) { + carret.character_index = last_index; + } else { + carret.character_index = 0; + c = ' '; + } + + return_val_if_fail (0 <= carret.paragraph < paragraphs.size, '\0'); + + return c; + } + + public void move_carret_next_row () { + double nr = font_size; + + if (carret.desired_y + 2 * font_size >= allocation.height) { + scroll (2 * font_size); + nr = -font_size; + } + + if (carret.desired_y + nr < widget_y + height - padding) { + carret = get_carret_at (carret.desired_x - widget_x - padding, carret.desired_y + nr); + } + } + + public void move_carret_to_end_of_line () { + carret = get_carret_at (widget_x + padding + width, carret.desired_y, false); + } + + public void move_carret_to_beginning_of_line () { + carret = get_carret_at (widget_x, carret.desired_y, false); + } + + public void move_carret_previous_row () { + double nr = -font_size; + + if (carret.desired_y - 2 * font_size < 0) { + scroll (-2 * font_size); + nr = font_size; + } + + if (carret.desired_y + nr > widget_y + padding) { + carret = get_carret_at (carret.desired_x, carret.desired_y + nr); + } + } + + public bool has_selection () { + return show_selection && selection_is_visible (); + } + + private bool selection_is_visible () { + return carret.paragraph != selection_end.paragraph || carret.character_index != selection_end.character_index; + } + + public void insert_text (string t) { + string s; + Paragraph paragraph; + TextUndoItem ui; + Gee.ArrayList<string> pgs; + bool u = false; + + pgs = new Gee.ArrayList<string> (); + + if (single_line) { + s = t.replace ("\n", "").replace ("\r", ""); + pgs.add (s); + } else { + if (t.last_index_of ("\n") > 0) { + string[] parts = t.split ("\n"); + int i; + for (i = 0; i < parts.length -1; i++) { + pgs.add (parts[i]); + pgs.add ("\n"); + } + + pgs.add (parts[parts.length - 1]); + + if (t.has_suffix ("\n")) { + pgs.add ("\n"); + } + } else { + s = t; + pgs.add (s); + } + } + + if (has_selection () && show_selection) { + ui = delete_selected_text (); + u = true; + + if (paragraphs.size == 0) { + paragraphs.add (new Paragraph ("", font_size, 0, text_color)); + } + } else { + ui = new TextUndoItem (carret); + } + + return_if_fail (0 <= carret.paragraph < paragraphs.size); + paragraph = paragraphs.get (carret.paragraph); + + if (pgs.size > 0) { + if (!u) { + ui.edited.add (paragraph.copy ()); + } + + string first = pgs.get (0); + + string end; + string nt = paragraph.text.substring (0, carret.character_index); + + nt += first; + end = paragraph.text.substring (carret.character_index); + + paragraph.set_text (nt); + + int paragraph_index = carret.paragraph; + Paragraph next_paragraph = paragraph; + for (int i = 1; i < pgs.size; i++) { + paragraph_index++; + string next = pgs.get (i); + next_paragraph = new Paragraph (next, font_size, paragraph_index, text_color); + paragraphs.insert (paragraph_index, next_paragraph); + ui.added.add (next_paragraph); + u = true; + } + + carret.paragraph = paragraph_index; + carret.character_index = next_paragraph.text.length; + + next_paragraph.set_text (next_paragraph.text + end); + } + + if (u) { + undo_items.add (ui); + redo_items.clear (); + } + + update_paragraph_index (); + layout (); + + text_changed (get_text ()); + show_selection = false; + } + + public string get_text () { + StringBuilder sb = new StringBuilder (); + + generate_all_paragraphs (); + + foreach (Paragraph p in paragraphs) { + sb.append (p.text); + } + + return sb.str; + } + + Carret get_carret_at (double click_x, double click_y, + bool check_boundaries = true) { + + int i = 0; + double tx, ty; + double p; + string w; + int ch_index; + double min_d = double.MAX; + Carret c = new Carret (); + double dt; + + c.paragraph = -1; + c.desired_x = click_x; + c.desired_y = click_y; + + foreach (Paragraph paragraph in paragraphs) { + if (!check_boundaries || paragraph.text_is_on_screen (allocation, widget_y)) { + ch_index = 0; + + if (paragraph.start_y + widget_y - font_size <= click_y <= paragraph.end_y + widget_y + font_size) { + foreach (Text next_word in paragraph.words) { + double tt_click = click_y - widget_y - padding + font_size; + + w = next_word.text; + + if (next_word.widget_y <= tt_click <= next_word.widget_y + font_size) { + + p = next_word.get_sidebearing_extent (); + + if ((next_word.widget_y <= tt_click <= next_word.widget_y + font_size) + && (next_word.widget_x + widget_x <= click_x <= next_word.widget_x + widget_x + padding + next_word.get_sidebearing_extent ())) { + + tx = widget_x + next_word.widget_x + padding; + ty = widget_y + next_word.widget_y + padding; + + next_word.iterate ((glyph, kerning, last) => { + double cw; + int ci; + double d; + string gc = (!) glyph.get_unichar ().to_string (); + + d = Math.fabs (click_x - tx); + + if (d <= min_d) { + min_d = d; + c.character_index = ch_index; + c.paragraph = i; + } + + cw = (glyph.get_width ()) * next_word.get_font_scale () + kerning; + ci = gc.length; + + tx += cw; + ch_index += ci; + }); + + dt = Math.fabs (click_x - (tx + widget_x + padding)); + if (dt < min_d) { + min_d = dt; + c.character_index = ch_index; + c.paragraph = i; + } + } else { + dt = Math.fabs (click_x - (next_word.widget_x + widget_x + padding + next_word.get_sidebearing_extent ())); + + if (dt < min_d) { + min_d = dt; + c.character_index = ch_index + w.length; + + if (w.has_suffix ("\n")) { + c.character_index -= "\n".length; + } + + c.paragraph = i; + } + + ch_index += w.length; + } + } else { + ch_index += w.length; + } + } + } + } + i++; + } + + if (unlikely (c.paragraph < 0)) { + c.paragraph = paragraphs.size > 0 ? paragraphs.size - 1 : 0; + c.character_index = paragraphs.size > 0 ? paragraphs.get (c.paragraph).text.length : 0; + } + + store_undo_state_at_next_event = true; + + return c; + } + + /** @return offset to click in text. */ + public override void layout () { + double p; + double tx, ty; + string w; + double xmax = 0; + int i = 0; + double dd; + + tx = 0; + ty = font_size; + + if (allocation.width <= 0 || allocation.height <= 0) { + warning ("Parent widget allocation is not set."); + } + + for (i = paragraphs.size - 1; i >= 0 && paragraphs.size > 1; i--) { + if (unlikely (paragraphs.get (i).is_empty ())) { + warning ("Empty paragraph."); + paragraphs.remove_at (i); + update_paragraph_index (); + } + } + + i = 0; + foreach (Paragraph paragraph in paragraphs) { + if (paragraph.need_layout + || (paragraph.text_area_width != width + && paragraph.text_is_on_screen (allocation, widget_y))) { + + paragraph.start_y = ty; + paragraph.start_x = tx; + + paragraph.cached_surface = null; + + foreach (Text next_word in paragraph.words) { + next_word.set_font_size (font_size); + + w = next_word.text; + p = next_word.get_sidebearing_extent (); + + if (unlikely (p == 0)) { + warning (@"Zero width word: $(w)"); + } + + if (w == "") { + break; + } + + if (w == "\n") { + next_word.widget_x = tx; + next_word.widget_y = ty; + + tx = 0; + ty += next_word.font_size; + } else { + if (!single_line) { + if (tx + p + 2 * padding > width || w == "\n") { + tx = 0; + ty += next_word.font_size; + } + } + + if (tx + p > xmax) { + xmax = tx + p; + } + + next_word.widget_x = tx; + next_word.widget_y = ty; + + if (w != "\n") { + tx += p; + } + } + } + + if (tx > xmax) { + xmax = tx; + } + + paragraph.text_area_width = width; + paragraph.width = xmax; + paragraph.end_x = tx; + paragraph.end_y = ty; + paragraph.need_layout = false; + } + + if (xmax > width) { + break; + } + + tx = paragraph.end_x; + ty = paragraph.end_y; + i++; + } + + if (xmax > width) { + this.width = xmax + 2 * padding; + layout (); + return; + } + + this.height = fmax (min_height, ty + 2 * padding); + + if (last_paragraph != DONE) { + this.height = (text_length / (double) last_paragraph) * ty + 2 * padding; // estimate height + } + + if (ty + widget_y < allocation.height && last_paragraph != DONE) { + generate_paragraphs (); + layout (); + return; + } + + ty = font_size; + tx = 0; + + foreach (Paragraph paragraph in paragraphs) { + dd = ty - paragraph.start_y; + + if (dd != 0) { + paragraph.start_y += dd; + paragraph.end_y += dd; + foreach (Text word in paragraph.words) { + word.widget_y += dd; + } + } + + ty = paragraph.end_y; + } + } + + public override void button_press (uint button, double x, double y) { + if (is_over (x, y)) { + carret = get_carret_at (x, y); + selection_end = carret.copy (); + update_selection = true; + } + } + + public override void button_release (uint button, double x, double y) { + update_selection = false; + show_selection = selection_is_visible (); + } + + public override bool motion (double x, double y) { + if (update_selection) { + selection_end = get_carret_at (x, y); + show_selection = selection_is_visible (); + } + + return update_selection; + } + + public override void draw (Context cr) { + Text word; + double tx, ty; + string w; + double scale; + double width; + double x = widget_x; + double y = widget_y; + Carret selection_start, selection_stop; + double carret_x; + double carret_y; + + layout (); + + if (draw_border) { + // background + cr.save (); + cr.set_line_width (1); + Theme.color (cr, "Text Area Background"); + draw_rounded_rectangle (cr, x, y, this.width, this.height - padding, padding); + cr.fill (); + cr.restore (); + + // border + cr.save (); + cr.set_line_width (1); + Theme.color (cr, "Foreground 1"); + draw_rounded_rectangle (cr, x, y, this.width, this.height - padding, padding); + cr.stroke (); + cr.restore (); + } + + cr.save (); + + word = new Text (); + + width = this.width - padding; + x += padding; + scale = word.get_font_scale (); + y += font_size; + + // draw selection background + if (has_selection ()) { + tx = 0; + ty = 0; + + selection_start = get_selection_start (); + selection_stop = get_selection_stop (); + + cr.save (); + Theme.color (cr, "Highlighted 1"); + + for (int i = selection_start.paragraph; i <= selection_stop.paragraph; i++) { + return_if_fail (0 <= i < paragraphs.size); + Paragraph pg = paragraphs.get (i); + + if (pg.text_is_on_screen (allocation, widget_y)) { + int char_index = 0; + + foreach (Text next_word in pg.words) { + double cw = next_word.get_sidebearing_extent (); + bool paint_background = false; + bool partial_start = false; + bool partial_stop = false; + int wl; + + w = next_word.text; + wl = w.length; + scale = next_word.get_font_scale (); + + if (selection_start.paragraph == selection_stop.paragraph) { + partial_start = true; + partial_stop = true; + } else if (selection_start.paragraph < i < selection_stop.paragraph) { + paint_background = true; + } else if (selection_start.paragraph == i) { + paint_background = true; + partial_start = true; + } else if (selection_stop.paragraph == i) { + paint_background = char_index + wl < selection_stop.character_index; + partial_stop = !paint_background; + } + + if (paint_background && !(partial_start || partial_stop)) { + double selection_y = widget_y + next_word.widget_y + scale * -next_word.cached_font.bottom_limit - font_size; + cr.rectangle (widget_x + padding + next_word.widget_x - 1, selection_y, cw + 1, font_size); + cr.fill (); + } + + if (partial_start || partial_stop) { + int index = char_index; + double bx = widget_x + padding + next_word.widget_x + (partial_start ? 0 : 1); + + next_word.iterate ((glyph, kerning, last) => { + double cwi; + int ci; + bool draw = (index >= selection_start.character_index && partial_start && !partial_stop) + || (index < selection_stop.character_index && !partial_start && partial_stop) + || (selection_start.character_index <= index < selection_stop.character_index && partial_start && partial_stop); + + cwi = (glyph.get_width ()) * next_word.get_font_scale () + kerning; + + if (draw) { + double selection_y = widget_y + next_word.widget_y + scale * -next_word.cached_font.bottom_limit - font_size; + cr.rectangle (bx - 1, selection_y, cwi + 1, font_size); + cr.fill (); + } + + bx += cwi; + ci = ((!) glyph.get_unichar ().to_string ()).length; + index += ci; + }); + } + + char_index += w.length; + } + } + } + + cr.restore (); + } + + tx = 0; + ty = 0; + + int first_visible = 0; + int last_visible; + int paragraphs_size = paragraphs.size; + while (first_visible < paragraphs_size) { + if (paragraphs.get (first_visible).text_is_on_screen (allocation, widget_y)) { + break; + } + first_visible++; + } + + last_visible = first_visible; + while (last_visible < paragraphs_size) { + if (!paragraphs.get (last_visible).text_is_on_screen (allocation, widget_y)) { + last_visible++; + break; + } + last_visible++; + } + + if (paragraphs_size == 0) { + if (carret_is_visible) { + draw_carret_at (cr, widget_x + padding, widget_y + font_size + padding); + } + + return; + } + + Context cc; // cached context + Paragraph paragraph; + paragraph = paragraphs.get (0); + + tx = paragraph.start_x; + ty = paragraph.start_y; + + for (int i = first_visible; i < last_visible; i++) { + paragraph = paragraphs.get (i); + + tx = paragraph.start_x; + ty = paragraph.start_y; + + if (paragraph.cached_surface == null) { + paragraph.cached_surface = Screen.create_background_surface ((int) width + 2, paragraph.get_height () + (int) font_size + 2); + cc = new Context ((!) paragraph.cached_surface); + cc.scale (Screen.get_scale(), Screen.get_scale()); + + foreach (Text next_word in paragraph.words) { + if (next_word.text != "\n") { + next_word.draw_at_top (cc, next_word.widget_x, next_word.widget_y - ty); + } + } + } + + if (likely (paragraph.cached_surface != null)) { + // FIXME: subpixel offset in text area + Screen.paint_background_surface(cr, + (!) paragraph.cached_surface, + (int) (x + tx), + (int) (widget_y + paragraph.start_y - font_size + padding)); + } else { + warning ("No paragraph image."); + } + } + + if (carret_is_visible) { + get_carret_position (carret, out carret_x, out carret_y); + + if (carret_y < 0) { + draw_carret_at (cr, widget_x + padding, widget_y + font_size + padding); + } else { + draw_carret_at (cr, carret_x, carret_y); + } + } + + if (has_selection ()) { + get_carret_position (selection_end, out carret_x, out carret_y); + + if (carret_y < 0) { + draw_carret_at (cr, widget_x + padding, widget_y + font_size + padding); + } else { + draw_carret_at (cr, carret_x, carret_y); + } + } + } + + void get_carret_position (Carret carret, out double carret_x, out double carret_y) { + Paragraph paragraph; + double tx; + double ty; + int ch_index; + int wl; + double pos_x, pos_y; + + ch_index = 0; + + carret_x = -1; + carret_y = -1; + + return_if_fail (0 <= carret.paragraph < paragraphs.size); + paragraph = paragraphs.get (carret.paragraph); + + pos_x = -1; + pos_y = -1; + + foreach (Text next_word in paragraph.words) { + string w = next_word.text; + wl = w.length; + + if (carret.character_index == ch_index) { + pos_x = next_word.widget_x + widget_x + padding; + pos_y = widget_y + next_word.widget_y + next_word.get_baseline_to_bottom_for_font (); + } else if (carret.character_index >= ch_index + wl) { + pos_x = next_word.widget_x + next_word.get_sidebearing_extent () + widget_x + padding; + pos_y = widget_y + next_word.widget_y + next_word.get_baseline_to_bottom_for_font (); + + if (next_word.text.has_suffix ("\n")) { + pos_x = widget_x + padding; + pos_y += next_word.font_size; + } + } else if (ch_index < carret.character_index <= ch_index + wl) { + tx = widget_x + next_word.widget_x; + ty = widget_y + next_word.widget_y + next_word.get_baseline_to_bottom_for_font (); + + if (carret.character_index <= ch_index) { + pos_x = widget_x + padding; + pos_y = ty; + } + + next_word.iterate ((glyph, kerning, last) => { + double cw; + int ci; + + cw = (glyph.get_width ()) * next_word.get_font_scale () + kerning; + ci = ((!) glyph.get_unichar ().to_string ()).length; + + if (ch_index < carret.character_index <= ch_index + ci) { + pos_x = tx + cw + padding; + pos_y = ty; + + if (glyph.get_unichar () == '\n') { + pos_x = widget_x + padding; + pos_y += next_word.font_size; + } + } + + tx += cw; + ch_index += ci; + }); + } + + ch_index += wl; + } + + carret_x = pos_x; + carret_y = pos_y; + } + + void draw_carret_at (Context cr, double x, double y) { + cr.save (); + cr.set_source_rgba (0, 0, 0, 0.5); + cr.set_line_width (1); + cr.move_to (x, y); + cr.line_to (x, y - font_size); + cr.stroke (); + cr.restore (); + } + + public void store_undo_edit_state () { + TextUndoItem ui = new TextUndoItem (carret); + ui.edited.add (get_current_paragraph ().copy ()); + undo_items.add (ui); + redo_items.clear (); + } + + public void redo () { + TextUndoItem i; + TextUndoItem undo_item; + + if (redo_items.size > 0) { + i = redo_items.get (redo_items.size - 1); + + undo_item = new TextUndoItem (i.carret); + + i.deleted.sort ((a, b) => { + Paragraph pa = (Paragraph) a; + Paragraph pb = (Paragraph) b; + return pb.index - pa.index; + }); + + i.added.sort ((a, b) => { + Paragraph pa = (Paragraph) a; + Paragraph pb = (Paragraph) b; + return pa.index - pb.index; + }); + + foreach (Paragraph p in i.deleted) { + if (unlikely (!(0 <= p.index < paragraphs.size))) { + warning ("Paragraph not found."); + } else { + undo_item.deleted.add (p.copy ()); + paragraphs.remove_at (p.index); + } + } + + foreach (Paragraph p in i.added) { + if (p.index == paragraphs.size) { + paragraphs.add (p.copy ()); + } else { + if (unlikely (!(0 <= p.index < paragraphs.size))) { + warning (@"Index: $(p.index) out of bounds, size: $(paragraphs.size)"); + } else { + undo_item.added.add (paragraphs.get (p.index).copy ()); + paragraphs.insert (p.index, p.copy ()); + } + } + } + + foreach (Paragraph p in i.edited) { + if (unlikely (!(0 <= p.index < paragraphs.size))) { + warning (@"Index: $(p.index ) out of bounds, size: $(paragraphs.size)"); + return; + } + + undo_item.edited.add (paragraphs.get (p.index).copy ()); + paragraphs.set (p.index, p.copy ()); + } + + redo_items.remove_at (redo_items.size - 1); + undo_items.add (undo_item); + + carret = i.carret.copy (); + layout (); + } + } + + public void undo () { + TextUndoItem i; + TextUndoItem redo_item; + + if (undo_items.size > 0) { + i = undo_items.get (undo_items.size - 1); + redo_item = new TextUndoItem (i.carret); + + i.deleted.sort ((a, b) => { + Paragraph pa = (Paragraph) a; + Paragraph pb = (Paragraph) b; + return pa.index - pb.index; + }); + + i.added.sort ((a, b) => { + Paragraph pa = (Paragraph) a; + Paragraph pb = (Paragraph) b; + return pb.index - pa.index; + }); + + foreach (Paragraph p in i.added) { + if (unlikely (!(0 <= p.index < paragraphs.size))) { + warning ("Paragraph not found."); + } else { + redo_item.added.add (paragraphs.get (p.index).copy ()); + paragraphs.remove_at (p.index); + } + } + + foreach (Paragraph p in i.deleted) { + if (p.index == paragraphs.size) { + paragraphs.add (p.copy ()); + } else { + if (unlikely (!(0 <= p.index < paragraphs.size))) { + warning (@"Index: $(p.index) out of bounds, size: $(paragraphs.size)"); + } else { + redo_item.deleted.add (p.copy ()); + paragraphs.insert (p.index, p.copy ()); + } + } + } + + foreach (Paragraph p in i.edited) { + if (unlikely (!(0 <= p.index < paragraphs.size))) { + warning (@"Index: $(p.index ) out of bounds, size: $(paragraphs.size)"); + return; + } + + redo_item.edited.add (paragraphs.get (p.index).copy ()); + paragraphs.set (p.index, p.copy ()); + } + + undo_items.remove_at (undo_items.size - 1); + redo_items.add (redo_item); + + carret = i.carret.copy (); + layout (); + } + } + + public void set_editable (bool editable) { + this.editable = editable; + } + + public class TextUndoItem : GLib.Object { + public Carret carret; + public Gee.ArrayList<Paragraph> added = new Gee.ArrayList<Paragraph> (); + public Gee.ArrayList<Paragraph> edited = new Gee.ArrayList<Paragraph> (); + public Gee.ArrayList<Paragraph> deleted = new Gee.ArrayList<Paragraph> (); + + public TextUndoItem (Carret c) { + carret = c.copy (); + } + } + + public class Paragraph : GLib.Object { + public double end_x = -10000; + public double end_y = -10000; + + public double start_x = -10000; + public double start_y = -10000; + + public double width = -10000; + public double text_area_width = -10000; + + public string text; + + public Gee.ArrayList<Text> words { + get { + if (words_in_paragraph.size == 0) { + generate_words (); + } + + return words_in_paragraph; + } + } + + private Gee.ArrayList<Text> words_in_paragraph = new Gee.ArrayList<Text> (); + public int text_length; + public bool need_layout = true; + public Surface? cached_surface = null; + double font_size; + public int index; + Color text_color; + + public Paragraph (string text, double font_size, int index, Color c) { + this.index = index; + this.font_size = font_size; + text_color = c; + set_text (text); + } + + public Paragraph copy () { + Paragraph p = new Paragraph (text.dup (), font_size, index, text_color); + p.need_layout = true; + return p; + } + + public bool is_empty () { + return text == ""; + } + + public void set_text (string t) { + this.text = t; + text_length = t.length; + need_layout = true; + words.clear (); + cached_surface = null; + } + + public int get_height () { + return (int) (end_y - start_y) + 1; + } + + public int get_width () { + return (int) width + 1; + } + + public bool text_is_on_screen (WidgetAllocation alloc, double widget_y) { + bool v = (0 <= start_y + widget_y <= alloc.height) + || (0 <= end_y + widget_y <= alloc.height) + || (start_y + widget_y <= 0 && alloc.height <= end_y + widget_y); + return v; + } + + private void generate_words () { + string w; + int p = 0; + bool carret_at_word_end = false; + Text word; + int carret = 0; + int iter_pos = 0; + + return_if_fail (words_in_paragraph.size == 0); + + while (p < text_length) { + w = get_next_word (out carret_at_word_end, ref iter_pos, carret); + + if (w == "") { + break; + } + + word = new Text (w, font_size); + + word.r = text_color.r; + word.g = text_color.g; + word.b = text_color.b; + word.a = text_color.a; + + words_in_paragraph.add (word); + } + } + + string get_next_word (out bool carret_at_end_of_word, ref int iter_pos, int carret) { + int i; + int ni; + int pi; + string n; + int nl; + + carret_at_end_of_word = false; + + if (iter_pos >= text_length) { + carret_at_end_of_word = true; + return "".dup (); + } + + if (text.get_char (iter_pos) == '\n') { + iter_pos += "\n".length; + carret_at_end_of_word = (iter_pos == carret); + return "\n".dup (); + } + + i = text.index_of (" ", iter_pos); + pi = i + " ".length; + + ni = text.index_of ("\t", iter_pos); + if (ni != -1 && ni < pi || i == -1) { + i = ni; + pi = i + "\t".length; + } + + ni = text.index_of ("\n", iter_pos); + if (ni != -1 && ni < pi || i == -1) { + i = ni; + pi = i; + } + + if (iter_pos + iter_pos - pi > text_length || i == -1) { + n = text.substring (iter_pos); + } else { + n = text.substring (iter_pos, pi - iter_pos); + } + + nl = n.length; + if (iter_pos < carret < iter_pos + nl) { + n = text.substring (iter_pos, carret - iter_pos); + nl = n.length; + carret_at_end_of_word = true; + } + + iter_pos += nl; + + if (iter_pos == carret) { + carret_at_end_of_word = true; + } + + return n; + } + } + + public class Carret : GLib.Object { + + public int paragraph = 0; + + public int character_index { + get { + return ci; + } + + set { + ci = value; + } + } + + private int ci = 0; + + public double desired_x = 0; + public double desired_y = 0; + + public Carret () { + } + + public void print () { + stdout.printf (@"paragraph: $paragraph, character_index: $character_index\n"); + } + + public Carret copy () { + Carret c = new Carret (); + + c.paragraph = paragraph; + c.character_index = character_index; + + c.desired_x = desired_x; + c.desired_y = desired_y; + + return c; + } + } + } + + }
--- /dev/null +++ b/libbirdfont/Renderer/fontconfig.c @@ -1,1 +1,141 @@ + /* + Copyright (C) 2015 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + #include <stdio.h> + #include <glib.h> + #include <fontconfig/fontconfig.h> + + gchar* find_font_with_property (FcConfig* fontconfig, const gchar* characters, const gchar* property) { + FcPattern* pattern; + FcCharSet* character_set; + FcObjectSet* font_properties; + FcFontSet* fonts; + FcPattern* font; + FcChar8* path; + gchar* result; + gchar* remaining_characters; + gunichar character; + + if (fontconfig == NULL) { + g_warning("Font config not loaded."); + return NULL; + } + + result = NULL; + pattern = FcPatternCreate (); + + character_set = FcCharSetCreate (); + + remaining_characters = (gchar*) characters; + while (TRUE) { + character = g_utf8_get_char (remaining_characters); + + if (character == '\0') { + break; + } + + FcCharSetAddChar(character_set, character); + + remaining_characters = g_utf8_next_char (remaining_characters); + } + + FcPatternAddCharSet (pattern, FC_CHARSET, character_set); + FcCharSetDestroy (character_set); + FcPatternAddInteger (pattern, FC_SLANT, FC_SLANT_ROMAN); + + FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); + font_properties = FcObjectSetBuild (property, NULL); + fonts = FcFontList (fontconfig, pattern, font_properties); + + if (fonts && fonts->nfont > 0) { + font = fonts->fonts[0]; + if (FcPatternGetString(font, property, 0, &path) == FcResultMatch) { + result = g_strdup ((gchar*) path); + } + } + + if (fonts) { + FcFontSetDestroy(fonts); + } + + if (pattern) { + FcPatternDestroy(pattern); + } + + return result; + } + + /** Find a fallback font for a set of characters. + * @return A path to the font file. + */ + gchar* find_font (FcConfig* fontconfig, const gchar* characters) { + return find_font_with_property (fontconfig, characters, FC_FILE); + } + + /** Find a fallback font for a set of characters. + * @return Family name of the font. + */ + gchar* find_font_family (FcConfig* fontconfig, const gchar* characters) { + return find_font_with_property (fontconfig, characters, FC_FAMILY); + } + + /** Find a font file from its family name. + * @param font_config fontconfig instance + * @param font_name name of the font + * @return full path to the font file + */ + gchar* find_font_file (FcConfig* font_config, const gchar* font_name) { + const FcChar8* name; + FcPattern* search_pattern; + FcPattern* font; + FcChar8* file; + gchar* path; + FcObjectSet* font_properties; + FcFontSet* fonts; + int i; + + if (font_config == NULL) { + g_warning("Font config not loaded."); + return NULL; + } + + path = NULL; + name = font_name; + + search_pattern = FcPatternCreate (); + FcPatternAddString (search_pattern, FC_FAMILY, name); + FcPatternAddBool (search_pattern, FC_SCALABLE, FcTrue); + FcPatternAddInteger (search_pattern, FC_WEIGHT, FC_WEIGHT_MEDIUM); + FcPatternAddInteger (search_pattern, FC_SLANT, FC_SLANT_ROMAN); + + font_properties = FcObjectSetBuild (FC_FILE, NULL); + fonts = FcFontList (font_config, search_pattern, font_properties); + + if (fonts->nfont > 0) { + for (i = 0; i < fonts->nfont; i++) { + font = fonts->fonts[i]; + + if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch) { + path = g_strdup ((gchar*) file); + break; + } + } + FcPatternDestroy (font); + } + + FcPatternDestroy (search_pattern); + + return path; + }
--- a/libbirdfont/ResizeTool.vala +++ b/libbirdfont/ResizeTool.vala @@ -14,7 +14,6 @@ using Math; using Cairo; - using SvgBird; namespace BirdFont { @@ -22,7 +21,7 @@ bool resize_path_proportional = false; bool resize_width = false; - SvgBird.Object? resized_path = null; + Path? resized_path = null; double last_resize_y; double last_resize_x; @@ -64,13 +63,13 @@ }); press_action.connect((self, b, x, y) => { - SvgBird.Object last_path; + Path last_path; Glyph glyph; glyph = MainWindow.get_current_glyph (); glyph.store_undo_state (); - foreach (SvgBird.Object p in glyph.active_paths) { + foreach (Path p in glyph.active_paths) { if (is_over_resize_handle (p, x, y)) { resize_path_proportional = true; resized_path = p; @@ -88,7 +87,7 @@ } } - foreach (SvgBird.Object p in glyph.active_paths) { + foreach (Path p in glyph.active_paths) { if (is_over_rotate_handle (p, x, y)) { rotate_path = true; return; @@ -122,11 +121,8 @@ update_selection_box (); GlyphCanvas.redraw (); - foreach (SvgBird.Object p in MainWindow.get_current_glyph ().active_paths) { - if (p is PathObject) { - PathObject path = (PathObject) p; - path.get_path ().create_full_stroke (); - } + foreach (Path p in MainWindow.get_current_glyph ().active_paths) { + p.create_full_stroke (); } }); @@ -154,6 +150,10 @@ || resize_width) { glyph = MainWindow.get_current_glyph (); + + foreach (Path selected_path in glyph.active_paths) { + selected_path.reset_stroke (); + } GlyphCanvas.redraw (); } @@ -234,9 +234,9 @@ public void rotate_selected_paths (double angle, double cx, double cy) { Glyph glyph = MainWindow.get_current_glyph (); double dx, dy, xc2, yc2, w, h; - SvgBird.Object last_path; + Path last_path; - foreach (SvgBird.Object p in glyph.active_paths) { + foreach (Path p in glyph.active_paths) { p.rotate (angle, cx, cy); } @@ -245,7 +245,7 @@ dx = -(xc2 - cx); dy = -(yc2 - cy); - foreach (SvgBird.Object p in glyph.active_paths) { + foreach (Path p in glyph.active_paths) { p.move (dx, dy); } @@ -287,7 +287,7 @@ rotate_selected_paths (rotation - last_rotate, selection_box_center_x, selection_box_center_y); } - static bool is_over_rotate_handle (SvgBird.Object p, double x, double y) { + static bool is_over_rotate_handle (Path p, double x, double y) { double cx, cy, hx, hy; double size = 10; bool inx, iny; @@ -364,16 +364,17 @@ if (!selected) { glyph.clear_active_paths (); - foreach (SvgBird.Object path in glyph.get_visible_objects ()) { - glyph.add_active_object (null, path); + foreach (Path path in glyph.get_visible_paths ()) { + glyph.add_active_path (null, path); } } get_selection_min (out resize_pos_x, out resize_pos_y); // resize paths - foreach (SvgBird.Object selected_path in glyph.active_paths) { + foreach (Path selected_path in glyph.active_paths) { selected_path.resize (ratio_x, ratio_y); + selected_path.reset_stroke (); } // move paths relative to the updated xmin and xmax @@ -381,7 +382,7 @@ dx = resize_pos_x - selection_minx; dy = resize_pos_y - selection_miny; - foreach (SvgBird.Object selected_path in glyph.active_paths) { + foreach (Path selected_path in glyph.active_paths) { selected_path.move (dx, dy); } @@ -469,7 +470,7 @@ DrawingTools.move_tool.move_to_baseline (); - foreach (SvgBird.Object path in glyph.active_paths) { + foreach (Path path in glyph.active_paths) { path.move (0, -descender * scale); } @@ -480,7 +481,7 @@ Glyph glyph = MainWindow.get_current_glyph (); x = double.MAX; y = double.MAX; - foreach (SvgBird.Object p in glyph.active_paths) { + foreach (Path p in glyph.active_paths) { if (p.xmin < x) { x = p.xmin; } @@ -496,11 +497,11 @@ double h, w; double ratio = get_resize_ratio (x, y); - foreach (SvgBird.Object selected_path in glyph.active_paths) { + foreach (Path selected_path in glyph.active_paths) { h = selected_path.ymax - selected_path.ymin; w = selected_path.xmax - selected_path.xmin; - if (selected_path.is_empty ()) { // FIXME: test with one point + if (selected_path.points.size <= 1) { continue; } @@ -512,13 +513,13 @@ return true; } - bool is_over_resize_handle (SvgBird.Object p, double x, double y) { + bool is_over_resize_handle (Path p, double x, double y) { double handle_x, handle_y; get_resize_handle_position (out handle_x, out handle_y); return Path.distance (handle_x, x, handle_y, y) < 12 * MainWindow.units; } - bool is_over_horizontal_resize_handle (SvgBird.Object p, double x, double y) { + bool is_over_horizontal_resize_handle (Path p, double x, double y) { double handle_x, handle_y; get_horizontal_reseize_handle_position (out handle_x, out handle_y); return Path.distance (handle_x, x, handle_y, y) < 12 * MainWindow.units; @@ -539,28 +540,26 @@ if (!selected_paths) { glyph.clear_active_paths (); - foreach (SvgBird.Object path in glyph.get_visible_objects ()) { - glyph.add_active_object (null, path); + foreach (Path path in glyph.get_visible_paths ()) { + glyph.add_active_path (null, path); } } glyph.selection_boundaries (out x, out y, out w, out h); - foreach (SvgBird.Object path in glyph.active_paths) { - if (path is PathObject) { // FIXME: other objects - Path p = ((PathObject) path).get_path (); - SvgParser.apply_matrix (p, 1, 0, s, 1, 0, 0); - p.skew = skew; - path.update_region_boundaries (); - } + foreach (Path path in glyph.active_paths) { + SvgParser.apply_matrix (path, 1, 0, s, 1, 0, 0); + path.skew = skew; + path.update_region_boundaries (); } glyph.selection_boundaries (out nx, out y, out nw, out h); dx = -(nx - x); - foreach (SvgBird.Object p in glyph.active_paths) { + foreach (Path p in glyph.active_paths) { p.move (dx, 0); + p.reset_stroke (); } dw = (nw - w);
--- a/libbirdfont/SaveCallback.vala +++ b/libbirdfont/SaveCallback.vala @@ -28,7 +28,7 @@ } public void save_as () { - if (unlikely (MenuTab.has_suppress_event ())) { + if (MenuTab.has_suppress_event ()) { warn_if_test ("Event suppressed"); return; } @@ -54,7 +54,6 @@ file_name = @"$(f)"; file = File.new_for_path (file_name); font_file_path = (!) file.get_path (); - if (!file.query_exists ()) { save (); } else {
diff --git libbirdfont/Scrollbar.vala(new)
--- /dev/null +++ b/libbirdfont/Scrollbar.vala @@ -1,1 +1,155 @@ + /* + Copyright (C) 2016 Johan Mattsson + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + using Cairo; + + namespace BirdFont { + + public class Scrollbar : GLib.Object { + + public double position = 1; + public double size = 1; + public double width = 0; + public double left_x = 0; + public double height = 0; + public double corner = 0; + public double scroll_max = 1; + public double margin = 0; + + public double motion_x = 0; + public double motion_y = 0; + public bool move = false; + + public Scrollbar () { + } + + public bool button_press (uint button, double x, double y) { + if (!is_visible ()) { + return false; + } + + double h = height * position * scroll_max; + + if (left_x < x < left_x + width && + h - corner < y < h + height * size + corner + 2 * margin) { + + motion_x = x; + motion_y = y; + + move = true; + } + + return left_x < x < left_x + width && 0 < size < 1; + } + + public bool button_release (uint button, double x, double y) { + if (!is_visible ()) { + return false; + } + + if (move) { + move = false; + return true; + } else if (left_x < x < left_x + width) { + double h = height * position * scroll_max; + + if (y > h + size * height) { + position += size; + } + + if (y < h) { + position -= size; + } + + if (position > 1) { + position = 1; + } else if (position < 0) { + position = 0; + } + + TabContent.scroll_to (position); + GlyphCanvas.redraw (); + return true; + } + + return false; + } + + + public bool motion (double x, double y) { + if (!move || !is_visible ()) { + return false; + } + + double p = (y - motion_y) / (height - size * height); + position += p; + + if (position > 1) { + position = 1; + } else if (position < 0) { + position = 0; + } + + TabContent.scroll_to (position); + + GlyphCanvas.redraw (); + + motion_y = y; + motion_x = x; + + return false; + } + + public void draw (Context cr, WidgetAllocation content_allocation, double width) { + if (!is_visible ()) { + return; + } + + cr.save (); + + this.width = width; + this.left_x = content_allocation.width; + this.height = content_allocation.height; + this.corner = 4 * Screen.get_scale (); + this.scroll_max = 1 - size - 2 * corner / height; + this.margin = 2 * Screen.get_scale (); + + Theme.color (cr, "Table Background 1"); + cr.rectangle (left_x, 0, width, height); + cr.fill (); + + Theme.color (cr, "Tool Foreground"); + double h = height * position * scroll_max; + + Widget.draw_rounded_rectangle (cr, left_x + margin, h, width - 2 * margin, height * size + 2 * margin, corner); + cr.fill (); + + cr.restore (); + } + + public bool is_visible () { + return 0 < size < 1; + } + + public void set_size (double size) { + this.size = size; + } + + public void set_position (double position) { + this.position = position; + } + } + + } +
--- a/libbirdfont/SearchPaths.vala +++ b/libbirdfont/SearchPaths.vala @@ -94,7 +94,7 @@ f = get_file (@"/usr/share/birdfont/" + d + "/", name); if (likely (f.query_exists ())) return f; - return f; + return f; } public static string get_locale_directory () {
--- a/libbirdfont/SettingsTab.vala +++ b/libbirdfont/SettingsTab.vala @@ -1,5 +1,5 @@ /* - Copyright (C) 2014 2015 Johan Mattsson + Copyright (C) 2014 2015 2016 Johan Mattsson This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -18,6 +18,8 @@ namespace BirdFont { public class SettingsTab : SettingsDisplay { + + string restart_message = "You need to restart the program in order to apply this setting."; public SettingsTab () { base (); @@ -43,13 +45,13 @@ stroke_width.new_value_action.connect ((self) => { Glyph g = MainWindow.get_current_glyph (); - CanvasSettings.stroke_width = stroke_width.get_value (); + Path.stroke_width = stroke_width.get_value (); g.redraw_area (0, 0, g.allocation.width, g.allocation.height); Preferences.set ("stroke_width_for_open_paths", stroke_width.get_display_value ()); MainWindow.get_toolbox ().redraw ((int) stroke_width.x, (int) stroke_width.y, 70, 70); }); - CanvasSettings.stroke_width = stroke_width.get_value (); + Path.stroke_width = stroke_width.get_value (); // adjust precision string precision_value = Preferences.get ("precision"); @@ -81,7 +83,7 @@ Tool show_all_line_handles = new Tool ("show_all_line_handles"); show_all_line_handles.select_action.connect((self) => { - CanvasSettings.show_all_line_handles = !CanvasSettings.show_all_line_handles; + Path.show_all_line_handles = !Path.show_all_line_handles; Glyph g = MainWindow.get_current_glyph (); g.redraw_area (0, 0, g.allocation.width, g.allocation.height); }); @@ -89,11 +91,11 @@ Tool fill_open_path = new Tool ("fill_open_path"); fill_open_path.select_action.connect((self) => { - CanvasSettings.fill_open_path = true; + Path.fill_open_path = true; }); fill_open_path.deselect_action.connect((self) => { - CanvasSettings.fill_open_path = false; + Path.fill_open_path = false; }); tools.add (new SettingsItem (fill_open_path, t_("Fill open paths."))); @@ -141,6 +143,25 @@ DrawingTools.pen_tool.set_simplification_threshold (simplification_threshold.get_value ()); }); + Tool translate_ui = new Tool ("translate"); + translate_ui.select_action.connect((self) => { + Preferences.set ("translate", @"true"); + ThemeTab.redraw_ui (); + translate_ui.selected = true; + MainWindow.show_dialog (new MessageDialog (restart_message)); + }); + + translate_ui.deselect_action.connect((self) => { + Preferences.set ("translate", @"false"); + translate_ui.selected = false; + MainWindow.show_dialog (new MessageDialog (restart_message)); + ThemeTab.redraw_ui (); + }); + + string translate_setting = Preferences.get ("translate"); + translate_ui.selected = translate_setting == "" || translate_setting == "true"; + tools.add (new SettingsItem (translate_ui, t_("Translate"))); + Tool themes = new Tool ("open_theme_tab"); themes.set_icon ("theme"); themes.select_action.connect((self) => {
--- a/libbirdfont/SpacingClass.vala +++ b/libbirdfont/SpacingClass.vala @@ -39,6 +39,11 @@ public void set_next () { update_first = false; update (next); + } + + public void update_class (string value, bool first) { + update_first = first; + update (value); } void update (string val) { @@ -48,7 +53,7 @@ string v = text; if (v.has_prefix ("U+") || v.has_prefix ("u+")) { - v = (!) Font.to_unichar (val).to_string (); + v = ((!) Font.to_unichar (v).to_string ()).dup (); } if (update_first) { @@ -56,12 +61,11 @@ } else { next = v.dup (); } - - updated (this); }); listener.signal_submit.connect (() => { TabContent.hide_text_input (); + updated (this); }); TabContent.show_text_input (listener);
--- a/libbirdfont/SpacingClassTab.vala +++ b/libbirdfont/SpacingClassTab.vala @@ -20,12 +20,25 @@ public static int NEW_CLASS = -1; Gee.ArrayList<Row> rows = new Gee.ArrayList<Row> (); + public static SpacingClass current_class; + public static bool current_class_first_element; public SpacingClassTab () { + current_class = new SpacingClass ("", ""); } public override Gee.ArrayList<Row> get_rows () { return rows; + } + + public static void set_class (string glyph) { + if (current_class_first_element) { + current_class.first = glyph; + } else { + current_class.next = glyph; + } + + MainWindow.get_spacing_class_tab ().update_rows (); } public override void selected_row (Row row, int column, bool delete_button) { @@ -51,11 +64,15 @@ warning (@"Index: $(row.get_index ()) classes.size: $(spacing.classes.size)"); return; } - spacing.classes.get (row.get_index ()).set_first (); + current_class = spacing.classes.get (row.get_index ()); + current_class.set_first (); + current_class_first_element = true; font.touch (); } else if (column == 2) { return_if_fail (0 <= row.get_index () < spacing.classes.size); - spacing.classes.get (row.get_index ()).set_next (); + current_class = spacing.classes.get (row.get_index ()); + current_class.set_next (); + current_class_first_element = false; font.touch (); } }
--- /dev/null +++ b/libbirdfont/SpacingClassTools.vala @@ -1,1 +1,67 @@ + /* + Copyright (C) 2016 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + using Cairo; + using Math; + + namespace BirdFont { + + public class SpacingClassTools : ToolCollection { + public static Gee.ArrayList<Expander> expanders; + + public SpacingClassTools () { + expanders = new Gee.ArrayList<Expander> (); + + Expander font_name = new Expander (); + font_name.add_tool (new FontName ()); + + Expander spacing_class_tools = new Expander (); + Tool insert = new Tool ("insert_glyph_from_overview_in_spacing_class", t_("Insert glyph from overview")); + + insert.set_icon ("insert_glyph_from_overview"); + + insert.select_action.connect ((self) => { + GlyphSelection glyph_selection = new GlyphSelection (); + + glyph_selection.selected_glyph.connect ((glyph_collection) => { + SpacingClassTab.set_class (glyph_collection.get_name ()); + MainWindow.get_tab_bar ().select_tab_name ("SpacingClasses"); + }); + + GlyphCanvas.set_display (glyph_selection); + self.set_selected (false); + + TabContent.hide_text_input (); + }); + spacing_class_tools.add_tool (insert); + + expanders.add (font_name); + + expanders.add (spacing_class_tools); + } + + public override Gee.ArrayList<Expander> get_expanders () { + return expanders; + } + + public override Gee.ArrayList<string> get_displays () { + Gee.ArrayList<string> d = new Gee.ArrayList<string> (); + d.add ("SpacingClasses"); + return d; + } + + } + + }
--- a/libbirdfont/SpacingData.vala +++ b/libbirdfont/SpacingData.vala @@ -69,7 +69,7 @@ add_connections (s.next); } } - + if (s.next == glyph) { if (!has_connection (s.first)) { add_connections (s.first); @@ -83,11 +83,11 @@ } public void add_class (string first, string next) { - SpacingClass s = new SpacingClass (first, next); - s.updated.connect (update_all_rows); - s.updated.connect (update_kerning); - classes.add (s); - update_kerning (s); + SpacingClass spacing_class = new SpacingClass (first, next); + spacing_class.updated.connect (update_all_rows); + spacing_class.updated.connect (update_kerning); + classes.add (spacing_class); + update_kerning (spacing_class); } void update_all_rows (SpacingClass s) { @@ -103,14 +103,18 @@ return; } - kerning_classes.update_space_class (s.next); + if (s.next == "" || s.first == "") { + return; + } + + kerning_classes.copy_single_kerning_pairs (s.first, s.next); + g = font.get_glyph_collection (s.next); if (g != null) { gc = (!) g; gc.get_current ().update_spacing_class (); } - kerning_classes.update_space_class (s.first); g = font.get_glyph_collection (s.first); if (g != null) { gc = (!) g;
--- a/libbirdfont/SpacingTab.vala +++ b/libbirdfont/SpacingTab.vala @@ -250,6 +250,7 @@ v = double.parse (submitted_value); text_input_glyph.left_limit -= v - text_input_glyph.get_left_side_bearing (); + text_input_glyph.update_other_spacing_classes (); }); suppress_input = true; @@ -294,6 +295,7 @@ v = double.parse (submitted_value); text_input_glyph.right_limit += v - text_input_glyph.get_right_side_bearing (); + text_input_glyph.update_other_spacing_classes (); }); suppress_input = true;
--- a/libbirdfont/SpinButton.vala +++ b/libbirdfont/SpinButton.vala @@ -44,12 +44,6 @@ /** Lock the button to a fixed value. */ public bool locked = false; - - static Gee.ArrayList<Text> digits; - static double text_height = 14; - static Text period; - static Text comma; - static Text minus; public SpinButton (string? name = null, string tip = "") { base (null , tip); @@ -57,7 +51,7 @@ if (name != null) { base.name = (!) name; } - + set_icon ("spin_button"); panel_press_action.connect ((selected, button, tx, ty) => { @@ -150,23 +144,6 @@ decrease (); return true; }); - - if (is_null (digits)) { - add_digits (); - } - } - - void add_digits () { - digits = new Gee.ArrayList<Text> (); - - for (int i = 0; i < 10; i++) { - Text digit = new Text (@"$i", text_height); - digits.add (digit); - } - - period = new Text (".", text_height); - comma = new Text (",", text_height); - minus = new Text ("-", text_height); } public void show_icon (bool i) { @@ -474,31 +451,18 @@ } return v; - } - - Text get_glyph (unichar character) { - Text text; - - if ('0' <= character <= '9') { - int digit_index = int.parse ((!) character.to_string ()); - text = digits.get (digit_index); - } else if (character == '.') { - text = period; - } else if (character == ',') { - text = comma; - } else if (character == '-') { - text = minus; - } else { - text = new Text ((!) character.to_string (), text_height); - } - - return text; } public override void draw_tool (Context cr, double px, double py) { + double scale = Toolbox.get_scale (); + double text_height = 14 * scale; + string display_value = get_short_display_value (); + Text text = new Text (display_value, text_height); double x = x - px; double y = y - py; - string display_value = get_short_display_value (); + + double text_x = x + (w - text.get_sidebearing_extent ()) / 2 + 1; + double text_y = y + (h - text_height) / 2; if (!show_icon_tool_icon || waiting_for_icon_switch) { if (is_selected ()) { @@ -517,51 +481,18 @@ base.draw_tool (cr, px, py); if (!show_icon_tool_icon || waiting_for_icon_switch) { - unichar digit; - int index; - Text text; - double extent = 0; - double decender = 0; - double carret = 0; - double total_extent = 0; - double x_offset; - - index = 0; - while (display_value.get_next_char (ref index, out digit)) { - text = get_glyph (digit); - total_extent += text.get_sidebearing_extent (); + if (is_selected ()) { + Theme.text_color (text, "Selected Tool Foreground"); + } else { + Theme.text_color (text, "Tool Foreground"); } - x_offset = (w - total_extent) / 2 + 1; - - index = 0; - while (display_value.get_next_char (ref index, out digit)) { - text = get_glyph (digit); - extent = text.get_sidebearing_extent (); - - if (decender < text.get_decender ()) { - decender = text.get_decender (); - } - - if (is_selected ()) { - Theme.text_color (text, "Selected Tool Foreground"); - } else { - Theme.text_color (text, "Tool Foreground"); - } - - double text_x = x + carret + x_offset;; - double text_y = y + (h - text_height) / 2; - - text.widget_x = text_x; - text.widget_y = text_y + decender; - text.draw (cr); - - carret += extent; - - } + text.widget_x = text_x; + text.widget_y = text_y + text.get_decender (); + text.draw (cr); } } } }
diff --git libbirdfont/Stop.vala(new)
--- /dev/null +++ b/libbirdfont/Stop.vala @@ -1,1 +1,36 @@ + /* + Copyright (C) 2015 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + using Cairo; + using Math; + + namespace BirdFont { + + public class Stop : GLib.Object { + public Color color = Color.black (); + public double offset = 0; + + public Stop () { + } + + public Stop copy () { + Stop s = new Stop (); + s.color = color.copy (); + s.offset = offset; + return s; + } + } + + }
--- a/libbirdfont/StrokeTool.vala +++ b/libbirdfont/StrokeTool.vala @@ -14,9 +14,14 @@ using Cairo; using Math; - using SvgBird; namespace BirdFont { + + public enum LineCap { + BUTT, + SQUARE, + ROUND + } public class StrokeTool : GLib.Object { @@ -26,7 +31,7 @@ public static bool show_stroke_tools = false; public static bool convert_stroke = false; - public static SvgBird.LineCap line_cap = SvgBird.LineCap.BUTT; + public static LineCap line_cap = LineCap.BUTT; StrokeTask task; @@ -46,27 +51,22 @@ convert_stroke = true; g.store_undo_state (); - foreach (SvgBird.Object o in g.active_paths) { - if (o is PathObject) { - PathObject path = (PathObject) o; - Path p = path.get_path (); - - if (p.stroke > 0) { - paths.append (p.get_completed_stroke ()); - } + foreach (Path p in g.active_paths) { + if (p.stroke > 0) { + paths.append (p.get_completed_stroke ()); } } if (paths.paths.size > 0) { - foreach (SvgBird.Object o in g.active_paths) { - g.layers.remove (o); + foreach (Path p in g.active_paths) { + g.layers.remove_path (p); } g.active_paths.clear (); foreach (Path np in paths.paths) { g.add_path (np); - g.active_paths.add (new PathObject.for_path (np)); + g.active_paths.add (np); } PenTool.update_orientation (); @@ -134,15 +134,11 @@ g.store_undo_state (); - foreach (SvgBird.Object object in g.active_paths) { - if (object is PathObject) { - Path p = ((PathObject) object).get_path (); - - if (p.stroke == 0) { - o.add (p); - } else { - o.append (p.get_completed_stroke ()); - } + foreach (Path p in g.active_paths) { + if (p.stroke == 0) { + o.add (p); + } else { + o.append (p.get_completed_stroke ()); } } @@ -221,8 +217,8 @@ return; } - foreach (SvgBird.Object object in g.active_paths) { - g.layers.remove (object); + foreach (Path p in g.active_paths) { + g.delete_path (p); } g.clear_active_paths (); @@ -230,9 +226,8 @@ remove_merged_curve_parts (new_paths); foreach (Path p in new_paths.paths) { - PathObject path = new PathObject.for_path (p); - g.add_object (path); - g.add_active_object (null, path); + g.add_path (p); + g.add_active_path (null, p); } PenTool.update_orientation (); @@ -1076,7 +1071,7 @@ return simplified; } - Path fit_bezier_path (Path p, int start, int stop, double error) { + public static Path fit_bezier_path (Path p, int start, int stop, double error) { int index, size; Path simplified; double[] lines; @@ -1146,9 +1141,9 @@ } void add_line_cap (Path path, Path stroke1, Path stroke2, bool last_cap) { - if (path.line_cap == SvgBird.LineCap.SQUARE) { + if (path.line_cap == LineCap.SQUARE) { add_square_cap (path, stroke1, stroke2, last_cap); - } else if (path.line_cap == SvgBird.LineCap.ROUND) { + } else if (path.line_cap == LineCap.ROUND) { add_round_cap (path, stroke1, stroke2, last_cap); } }
--- a/libbirdfont/Svg.vala +++ b/libbirdfont/Svg.vala @@ -15,8 +15,6 @@ using Cairo; namespace BirdFont { - - // FIXME: substrings public class Svg { @@ -106,13 +104,14 @@ private static void add_abs_start (EditPoint ep, StringBuilder svg, Glyph g, bool to_glyph) { double left = g.left_limit; double baseline = -BirdFont.get_current_font ().base_line; - double height = g.get_height (); + Font font = BirdFont.get_current_font (); + double height = font.top_limit - font.base_line; svg.append_printf ("M"); if (!to_glyph) { svg.append_printf ("%s ", round (ep.x - left)); - svg.append_printf ("%s ", round (-ep.y + height / 2)); + svg.append_printf ("%s ", round (-ep.y + height)); } else { svg.append_printf ("%s ", round (ep.x - left)); svg.append_printf ("%s ", round (ep.y + baseline)); @@ -126,7 +125,9 @@ private static void add_abs_line_to (EditPoint start, EditPoint stop, StringBuilder svg, Glyph g, bool to_glyph) { double baseline = -BirdFont.get_current_font ().base_line; double left = g.left_limit; - double height = g.get_height (); + Font font = BirdFont.get_current_font (); + double height = font.top_limit - font.base_line; + double xa, ya, xb, yb; @@ -139,7 +140,7 @@ if (!to_glyph) { svg.append_printf ("%s ", round (xb - center_x - left)); - svg.append_printf ("%s ", round (yb - center_y + height / 2)); + svg.append_printf ("%s ", round (yb - center_y + height)); } else { svg.append_printf ("%s ", round (xb - center_x - left)); svg.append_printf ("%s ", round (-yb + center_y + baseline)); @@ -163,7 +164,8 @@ private static void add_quadratic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g, bool to_glyph) { double left = g.left_limit; double baseline = -BirdFont.get_current_font ().base_line; - double height = g.get_height (); + Font font = BirdFont.get_current_font (); + double height = font.top_limit - font.base_line; double xa, ya, xb, yb, xc, yc, xd, yd; @@ -177,10 +179,10 @@ svg.append_printf ("Q"); svg.append_printf ("%s ", round (xb - center_x - left)); - svg.append_printf ("%s ", round (yb - center_y + height / 2)); + svg.append_printf ("%s ", round (yb - center_y + height)); svg.append_printf ("%s ", round (xd - center_x - left)); - svg.append_printf ("%s ", round (yd - center_y + height / 2)); + svg.append_printf ("%s ", round (yd - center_y + height)); } else { svg.append_printf ("Q"); @@ -196,7 +198,8 @@ private static void add_cubic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g, bool to_glyph) { double left = g.left_limit; double baseline = -BirdFont.get_current_font ().base_line; - double height = g.get_height (); + Font font = BirdFont.get_current_font (); + double height = font.top_limit - font.base_line; double xa, ya, xb, yb, xc, yc, xd, yd; @@ -210,13 +213,13 @@ svg.append_printf ("C"); svg.append_printf ("%s ", round (xb - center_x - left)); - svg.append_printf ("%s ", round (yb - center_y + height / 2)); + svg.append_printf ("%s ", round (yb - center_y + height)); svg.append_printf ("%s ", round (xc - center_x - left)); - svg.append_printf ("%s ", round (yc - center_y + height / 2)); + svg.append_printf ("%s ", round (yc - center_y + height)); svg.append_printf ("%s ", round (xd - center_x - left)); - svg.append_printf ("%s ", round (yd - center_y + height / 2)); + svg.append_printf ("%s ", round (yd - center_y + height)); } else { svg.append_printf ("C");
diff --git libbirdfont/SvgArc.vala(new)
--- /dev/null +++ b/libbirdfont/SvgArc.vala @@ -1,1 +1,182 @@ + /* + * BirdFont code from SVG Salamander + * + * Copyright (c) 2004, Mark McKay + * Copyright (c) 2014, Johan Mattsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Mark McKay can be contacted at mark@kitfox.com. Salamander and other + * projects can be found at http://www.kitfox.com + * + * Created on January 26, 2004, 8:40 PM + * Adapded to BirdFont on Juli 2, 2014, 5:01 PM + */ + + using Math; + + namespace BirdFont { + + /** Convert an SVG arc instruction to a Beziér path. */ + static void add_arc_points (BezierPoints[] bezier_points, ref int bi, double x0, double y0, double rx, double ry, double angle, bool largeArcFlag, bool sweepFlag, double x, double y) { + + // + // Elliptical arc implementation based on the SVG specification notes + // + + double dx2, dy2, cosAngle, sinAngle; + double x1, y1, Prx, Pry, Px1, Py1, radiiCheck; + double sign, sq, coef, cx1, cy1; + double sx2, sy2, cx, cy; + double ux, uy, vx, vy, p, n; + double angleStart, angleExtent; + double s, step, theta; + + // Compute the half distance between the current and the final point + dx2 = (x0 - x) / 2.0; + dy2 = (y0 - y) / 2.0; + + // Convert angle from degrees to radians + angle = 2 * PI * ((angle % 360.0) / 360.0); + + cosAngle = cos (angle); + sinAngle = sin (angle); + + // + // Step 1 : Compute (x1, y1) + // + x1 = cosAngle * dx2 + sinAngle * dy2; + y1 = -sinAngle * dx2 + cosAngle * dy2; + + // Ensure radii are large enough + rx = fabs(rx); + ry = fabs(ry); + Prx = rx * rx; + Pry = ry * ry; + Px1 = x1 * x1; + Py1 = y1 * y1; + + + // Check that radii are large enough + radiiCheck = Px1 / Prx + Py1 / Pry; + + if (radiiCheck > 1) { + rx = sqrt (radiiCheck) * rx; + ry = sqrt (radiiCheck) * ry; + Prx = rx * rx; + Pry = ry * ry; + } + + // + // Step 2 : Compute (cx1, cy1) + // + sign = (largeArcFlag == sweepFlag) ? -1 : 1; + sq = ((Prx * Pry) - (Prx * Py1) - (Pry * Px1)) / ((Prx * Py1) + (Pry * Px1)); + sq = (sq < 0) ? 0 : sq; + coef = (sign * Math.sqrt(sq)); + cx1 = coef * ((rx * y1) / ry); + cy1 = coef * -((ry * x1) / rx); + + // + // Step 3 : Compute (cx, cy) from (cx1, cy1) + // + + sx2 = (x0 + x) / 2.0; + sy2 = (y0 + y) / 2.0; + cx = sx2 - (cosAngle * cx1 - sinAngle * cy1); + cy = sy2 - (sinAngle * cx1 + cosAngle * cy1); + + // + // Step 4 : Compute the angleStart (angle1) and the angleExtent (dangle) + // + + ux = (x1 - cx1) / rx; + uy = (y1 - cy1) / ry; + vx = (-x1 - cx1) / rx; + vy = (-y1 - cy1) / ry; + + // Compute the angle start + n = sqrt((ux * ux) + (uy * uy)); + p = ux; // (1 * ux) + (0 * uy) + sign = (uy < 0) ? -1d : 1d; + angleStart = sign * acos(p / n); + + // Compute the angle extent + n = Math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy)); + p = ux * vx + uy * vy; + sign = (ux * vy - uy * vx < 0) ? -1d : 1d; + angleExtent = sign * Math.acos(p / n); + + if(!sweepFlag && angleExtent > 0) { + angleExtent -= 2 *PI; + } else if (sweepFlag && angleExtent < 0) { + angleExtent += 2 *PI; + } + angleExtent %= 2 * PI; + angleStart %= 2 * PI; + + angleExtent *= -1; + angleStart *= -1; + + // Approximate the path with Beziér points + s = (angleExtent > 0) ? 1 : -1; + step = fabs (angleExtent) / (2 * fabs (angleExtent)); + + theta = PI - angleStart - angleExtent; + + bezier_points[bi].type = 'L'; + bezier_points[bi].svg_type = 'a'; + + bezier_points[bi].x0 = cx + rx * cos (theta); + bezier_points[bi].y0 = cy + ry * sin (theta); + + bi++; + + for (double a = 0; a < fabs (angleExtent); a += step) { + theta = PI - angleStart - angleExtent + s * a; + + return_if_fail (0 <= bi < bezier_points.length); + + bezier_points[bi].type = 'S'; + bezier_points[bi].svg_type = 'a'; + + bezier_points[bi].x0 = cx + rx * cos (theta); + bezier_points[bi].y0 = cy + ry * sin (theta); + + bezier_points[bi].x1 = cx + rx * cos (theta + 1 * step / 4); + bezier_points[bi].y1 = cy + ry * sin (theta + 1 * step / 4); + + bezier_points[bi].x2 = cx + rx * cos (theta + 2 * step / 4); + bezier_points[bi].y2 = cy + ry * sin (theta + 2 * step / 4); + + bi++; + } + } + + } +