phew 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # The gem's dependencies are specified in the gemspec
4
+ gemspec
5
+
6
+ gem 'pry', '~> 0.10.0', type: :development
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # Phew
2
+
3
+ Phew will be a font viewer.
4
+
5
+ ## Goals
6
+
7
+ The primary use case that prompted the development of this program was
8
+ needing to figure out which of the installed fonts provided characters
9
+ for writing Japanese.
10
+
11
+ As a user, in order to determine which fonts to keep, I want to know
12
+ which fonts provide characters for a particular script, and compare them
13
+ using a sample text.
14
+
15
+ Many fonts provide characters for the latin script in addition to their
16
+ primary focus on some other scripts. For example, many CJK fonts also
17
+ provide latin letters, often of a lesser quality than fonts designed
18
+ specifically for latin. Therefore, I want to be able to exclude such
19
+ fonts when comparing latin fonts.
20
+
21
+ As a user, in order to determine which font to use for a particular
22
+ document, I want to know which fonts cover the characters I want to use,
23
+ and compare them. I should be able to manipulate the initial selection
24
+ by adding and removing fonts.
25
+
26
+ Tentatively, there would be a filtering system, and we would have two
27
+ lists of fonts that satisfy the filters, namely those that have been
28
+ selected (the default state), and those that have been rejected.
29
+
30
+ ## License
31
+
32
+ Copyright (c) 2012, 2015 Matijs van Zuijlen
33
+
34
+ Phew is free software; you can redistribute it and/or modify it under the terms
35
+ of the GNU General Public License as published by the Free Software Foundation,
36
+ either version 3 of the License, or (at your option) any later version.
37
+
38
+ This application is distributed in the hope that it will be useful, but WITHOUT
39
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
40
+ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
41
+
42
+ You should have received a copy of the GNU General Public License along with
43
+ this application. If not, see <http://www.gnu.org/licenses/>.
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ require 'rake/clean'
2
+ require 'bundler/gem_helper'
3
+ require 'rake/testtask'
4
+
5
+ Bundler::GemHelper.install_tasks
6
+
7
+ namespace :test do
8
+ Rake::TestTask.new(:unit) do |t|
9
+ t.libs = ['lib']
10
+ t.test_files = FileList['test/unit/*_test.rb']
11
+ t.warning = true
12
+ end
13
+
14
+ Rake::TestTask.new(:end_to_end) do |t|
15
+ t.libs = ['lib']
16
+ t.test_files = FileList['test/end_to_end/*_test.rb']
17
+ t.warning = true
18
+ end
19
+
20
+ task all: [:unit, :end_to_end]
21
+ end
22
+
23
+ task test: 'test:all'
24
+
25
+ task default: 'test'
data/bin/phew ADDED
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env ruby
2
+ require 'phew'
3
+
4
+ Gtk.init
5
+
6
+ module Phew
7
+ # Drop-down list of available scripts.
8
+ class ScriptList < Gtk::ComboBoxText
9
+ # FIXME: Should be able to fill inside #initialize
10
+ def fill
11
+ Pango::Script::Enum.symbols.
12
+ map(&:to_s).
13
+ each { |str| append str, str }
14
+ end
15
+ end
16
+
17
+ # Main Phew window
18
+ class Window < Gtk::Window
19
+ # Set up generic signal handlers for the window:
20
+ # - Closing the window ends the application
21
+ # - Pressing Ctrl-Q closes the window.
22
+ def connect_signals
23
+ signal_connect('destroy') { on_destroy_event }
24
+ signal_connect('key-press-event') { |_, evt, _| on_key_press_event evt }
25
+ end
26
+
27
+ def on_destroy_event
28
+ Gtk.main_quit
29
+ false
30
+ end
31
+
32
+ def on_key_press_event evt
33
+ destroy if evt.state == :control_mask && evt.keyval == 'q'.ord
34
+ false
35
+ end
36
+ end
37
+
38
+ # Main Phew application.
39
+ class Application
40
+ def combo
41
+ @combo ||= ScriptList.new.tap(&:fill)
42
+ end
43
+
44
+ def textview
45
+ @textview ||= Gtk::TextView.new
46
+ end
47
+
48
+ def build_vbox
49
+ vbox = Gtk::VBox.new false, 0
50
+
51
+ vbox.pack_start combo, false, false, 0
52
+ vbox.pack_start textview, false, false, 0
53
+ vbox.pack_start script_list_scroller, true, true, 0
54
+ vbox
55
+ end
56
+
57
+ def vbox
58
+ @vbox ||= build_vbox
59
+ end
60
+
61
+ def script_list_scroller
62
+ @script_list_scroller ||= Gtk::ScrolledWindow.new(nil, nil).tap do |scr|
63
+ lst = script_list
64
+ scr.add lst
65
+ lst.hadjustment = scr.hadjustment
66
+ lst.vadjustment = scr.vadjustment
67
+ end
68
+ end
69
+
70
+ def script_list
71
+ @script_list ||= Gtk::TreeView.new_with_model(scriptmodel).tap do |view|
72
+ renderer = Gtk::CellRendererText.new
73
+ col = Gtk::TreeViewColumn.new
74
+ col.set_title 'Font Name'
75
+ col.pack_start renderer, true
76
+ col.add_attribute renderer, 'text', 0
77
+ view.append_column col
78
+ end
79
+ end
80
+
81
+ def scriptmodel
82
+ @scriptmodel ||= Gtk::ListStore.new [GObject::TYPE_STRING]
83
+ end
84
+
85
+ def build_win
86
+ win = Window.new :toplevel
87
+ win.add vbox
88
+ win
89
+ end
90
+
91
+ def win
92
+ @win ||= build_win
93
+ end
94
+
95
+ # Set up all signal handlers
96
+ def connect_signals
97
+ win.connect_signals
98
+ combo.signal_connect('changed') { on_combo_changed_signal }
99
+ end
100
+
101
+ def on_combo_changed_signal
102
+ script = Script.new combo.active_text.to_sym
103
+ textview.buffer.text = script.sample_string
104
+ fill_font_list script
105
+ end
106
+
107
+ def initialize
108
+ @context = Gdk.pango_context_get
109
+ @font_repository = FontRepository.new @context
110
+ connect_signals
111
+ win.show_all
112
+ end
113
+
114
+ def fill_font_list script
115
+ scriptmodel.clear
116
+
117
+ fontmap = @context.get_font_map
118
+
119
+ fontmap.list_families.each do |fam|
120
+ font = @font_repository.get_font fam.name
121
+
122
+ sample_cov = font.coverage_summary script.sample_string
123
+ mostly = [:none, :fallback, :approximate, :exact].max_by { |i| sample_cov[i] }
124
+
125
+ if mostly == :exact
126
+ row = scriptmodel.append
127
+ scriptmodel.set_value row, 0, fam.name
128
+ end
129
+ end
130
+ end
131
+
132
+ def run
133
+ Gtk.main
134
+ end
135
+ end
136
+ end
137
+
138
+ Phew::Application.new.run
Binary file
Binary file
data/doc/gfontview.png ADDED
Binary file
Binary file
data/doc/prior-art.md ADDED
@@ -0,0 +1,40 @@
1
+ # Prior Art
2
+
3
+ ## Font Viewer
4
+
5
+ [Website](http://fontviewer.net/)
6
+
7
+ ![FontViewer screen shot](fontviewer.gif)
8
+
9
+ Pleasantly sparse interface with obnoxious branding.
10
+
11
+ The grid display of different font weights is a nice touch.
12
+
13
+ ## GFontView
14
+
15
+ [Website](http://gfontview.sourceforge.net/)
16
+
17
+ ![GFontView screen shot](gfontview.png)
18
+
19
+ Based on files rather than installed fonts.
20
+
21
+ Only shows one font at a time.
22
+
23
+ ## Gnome Specimen
24
+
25
+ ![Specimen screen shot](gnome_specimen.png)
26
+
27
+ Clean interface. Does not allow filtering by desired script or text.
28
+
29
+
30
+ ## Waterfall
31
+
32
+ ![Waterfall screen shot](waterfall.png)
33
+
34
+ Allows easy switching between different parts of the character set.
35
+
36
+ ## Character Map
37
+
38
+ ![Character map screen shot](character_map.png)
39
+
40
+ A different way of selecting scripts. Nice balanced UI.
data/doc/waterfall.png ADDED
Binary file
data/lib/phew/font.rb ADDED
@@ -0,0 +1,27 @@
1
+ module Phew
2
+ # Font family. Handles coverage, among other things.
3
+ class Font
4
+ # Initialize the font from a text description. The text description should be
5
+ # in the format accepted by Pango::FontDescription.from_string.
6
+ #
7
+ # @param [Pango::Context] context Pango context to retrieve font from.
8
+ # @param [String] text_description Description of the font to create.
9
+ def initialize context, text_description
10
+ fd = Pango::FontDescription.from_string text_description
11
+ fontmap = context.get_font_map
12
+ @font = fontmap.load_font context, fd
13
+ end
14
+
15
+ # Summarize coverage of the given text by the glyphs in the font.
16
+ #
17
+ # @return A hash with keys :none, :fallback, :approximate, :exact, and values
18
+ # indicating the number of characters in the text that have that coverage.
19
+ def coverage_summary text
20
+ lang = Pango::Language.new
21
+ cov = @font.get_coverage lang
22
+ text_cov = text.each_codepoint.map { |cp| cov.get cp }
23
+ Hash[
24
+ Pango::CoverageLevel::Enum.symbols.map { |lvl| [lvl, text_cov.count(lvl)] }]
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,17 @@
1
+ module Phew
2
+ # Cache for font information retrieved from a Pango context
3
+ class FontRepository
4
+ # @param [Pango::Context] context Pango context to retrieve fonts from.
5
+ def initialize context
6
+ @store = {}
7
+ @context = context
8
+ end
9
+ # Retrieve a font based on the given text description. The text description
10
+ # should be in the format accepted by Font#new.
11
+ #
12
+ # @param [String] text_description Description of the font to retrieve.
13
+ def get_font text_description
14
+ @store[text_description] ||= Font.new @context, text_description
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ module Phew
2
+ # A script.
3
+ class Script
4
+ def initialize name
5
+ symbol = name.to_sym
6
+ @symbol = symbol
7
+ @lang = Pango.script_get_sample_language symbol
8
+ end
9
+
10
+ def sample_string
11
+ if @lang.nil?
12
+ 'No sample available'
13
+ else
14
+ @lang.get_sample_string
15
+ end
16
+ end
17
+ end
18
+ end
data/lib/phew.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'gir_ffi-gtk3'
2
+ require 'gir_ffi-pango'
3
+
4
+ require 'phew/script'
5
+ require 'phew/font'
6
+ require 'phew/font_repository'
@@ -0,0 +1,159 @@
1
+ require File.expand_path('../test_helper.rb', File.dirname(__FILE__))
2
+
3
+ require 'gir_ffi'
4
+
5
+ GirFFI.setup :Atspi
6
+ Atspi.load_class :Accessible
7
+
8
+ module Atspi
9
+ # Utility monkey-patches for the Atspi::Accessible class
10
+ class Accessible
11
+ def each_child
12
+ child_count.times do |i|
13
+ yield get_child_at_index i
14
+ end
15
+ end
16
+
17
+ def find_role role, regex = //
18
+ return self if role == self.role && name =~ regex
19
+ each_child do |child|
20
+ result = child.find_role role, regex
21
+ return result if result
22
+ end
23
+ nil
24
+ end
25
+
26
+ def inspect_recursive level = 0, maxlevel = 4
27
+ each_child do |child|
28
+ puts "#{' ' * level} > name: #{child.name}; role: #{child.role}"
29
+ child.inspect_recursive(level + 1) unless level >= maxlevel
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ def find_app name
36
+ desktop = Atspi.get_desktop(0)
37
+ desktop.each_child do |child|
38
+ next if child.nil?
39
+ return child if child.name == name
40
+ end
41
+ nil
42
+ end
43
+
44
+ def try_repeatedly
45
+ 100.times.each do |num|
46
+ result = yield
47
+ return result if result
48
+ sleep_time = 0.01 * (num + 1)
49
+ sleep sleep_time
50
+ end
51
+ yield
52
+ end
53
+
54
+ def press_ctrl_q
55
+ Atspi.generate_keyboard_event(37, nil, :press)
56
+ Atspi.generate_keyboard_event(24, nil, :pressrelease)
57
+ Atspi.generate_keyboard_event(37, nil, :release)
58
+ end
59
+
60
+ # Test driver for the Phew application. Takes care of boot and shutdown, and
61
+ # provides a handle on the GUI's main UI frame.
62
+ class PhewDriver
63
+ def initialize
64
+ @app_file = File.expand_path('../../bin/phew', File.dirname(__FILE__))
65
+ @lib_dir = File.expand_path('../../lib', File.dirname(__FILE__))
66
+ @pid = nil
67
+ @killed = false
68
+ end
69
+
70
+ def boot timeout = 10
71
+ raise 'Already booted' if @pid
72
+ @pid = Process.spawn "ruby -I#{@lib_dir} #{@app_file}"
73
+
74
+ @killed = false
75
+ @cleanup = false
76
+
77
+ Thread.new do
78
+ ((timeout - 1) * 10).times do
79
+ break if @cleanup
80
+ sleep 0.1
81
+ end
82
+
83
+ 10.times do
84
+ break unless @pid
85
+ sleep 0.1
86
+ end
87
+
88
+ if @pid
89
+ warn "About to kill child process #{@pid}"
90
+ @killed = true
91
+ Process.kill 'KILL', @pid
92
+ end
93
+ end
94
+ end
95
+
96
+ def cleanup
97
+ return unless @pid
98
+ @cleanup = true
99
+ _, status = Process.wait2 @pid
100
+ @pid = nil
101
+ status
102
+ end
103
+
104
+ def find_and_focus_frame
105
+ acc = try_repeatedly { find_app 'phew' }
106
+ acc.wont_be_nil
107
+
108
+ frame = acc.get_child_at_index 0
109
+ frame.role.must_equal :frame
110
+ frame.grab_focus
111
+ sleep 0.1
112
+
113
+ frame
114
+ end
115
+ end
116
+
117
+ describe 'The Phew application' do
118
+ before do
119
+ @driver = PhewDriver.new
120
+ @driver.boot
121
+ end
122
+
123
+ it 'starts and can be quit with Ctrl-q' do
124
+ @driver.find_and_focus_frame
125
+
126
+ press_ctrl_q
127
+
128
+ status = @driver.cleanup
129
+ status.exitstatus.must_equal 0
130
+ end
131
+
132
+ it 'shows a dropdown list of scripts' do
133
+ frame = @driver.find_and_focus_frame
134
+
135
+ box = frame.find_role :combo_box
136
+
137
+ latin = box.find_role :menu_item, /latin/
138
+ latin.wont_be_nil
139
+
140
+ textbox = frame.find_role :text
141
+ textbox.wont_be_nil
142
+ textbox.get_text(0, 100).must_equal ''
143
+
144
+ box.get_action_name(0).must_equal 'press'
145
+ box.do_action 0
146
+ latin.get_action_name(0).must_equal 'click'
147
+ latin.do_action 0
148
+
149
+ textbox.get_text(0, 100).must_equal 'The quick brown fox jumps over the lazy dog.'
150
+
151
+ press_ctrl_q
152
+ status = @driver.cleanup
153
+ status.exitstatus.must_equal 0
154
+ end
155
+
156
+ after do
157
+ @driver.cleanup
158
+ end
159
+ end
@@ -0,0 +1,6 @@
1
+ require 'minitest/autorun'
2
+
3
+ require 'phew'
4
+
5
+ # Gtk must be initialized for all the font retrieval methods to work.
6
+ Gtk.init
@@ -0,0 +1,20 @@
1
+ require File.expand_path('../test_helper.rb', File.dirname(__FILE__))
2
+
3
+ describe Phew::FontRepository do
4
+ describe '#get_font' do
5
+ it 'returns an instance of Phew::Font' do
6
+ ctx = Gdk.pango_context_get
7
+ repo = Phew::FontRepository.new ctx
8
+ font = repo.get_font 'Sans'
9
+ font.must_be_instance_of Phew::Font
10
+ end
11
+
12
+ it 'returns the same object if called again with the same description' do
13
+ ctx = Gdk.pango_context_get
14
+ repo = Phew::FontRepository.new ctx
15
+ font = repo.get_font 'Sans'
16
+ font_again = repo.get_font 'Sans'
17
+ font_again.must_be_same_as font
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ require File.expand_path('../test_helper.rb', File.dirname(__FILE__))
2
+
3
+ require 'gir_ffi-pango'
4
+
5
+ describe Phew::Font do
6
+ describe '#coverage_summary' do
7
+ it 'returns summarized coverage information for the given string' do
8
+ ctx = Gdk.pango_context_get
9
+
10
+ pfont = Phew::Font.new ctx, 'Sans'
11
+
12
+ test_string = 'This is a test'
13
+ sum = pfont.coverage_summary test_string
14
+
15
+ sum.keys.sort.must_equal [:none, :fallback, :approximate, :exact].sort
16
+ sum.values.inject(:+).must_equal test_string.size
17
+ end
18
+ end
19
+ end
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: phew
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Matijs van Zuijlen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: gir_ffi-gtk
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.7.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.7.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: gir_ffi-pango
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.0.5
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.0.5
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.1'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5.5'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5.5'
69
+ description: List and compare installed fonts on GNOME
70
+ email:
71
+ - matijs@matijs.net
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - COPYING
77
+ - Gemfile
78
+ - README.md
79
+ - Rakefile
80
+ - bin/phew
81
+ - doc/character_map.png
82
+ - doc/fontviewer.gif
83
+ - doc/gfontview.png
84
+ - doc/gnome_specimen.png
85
+ - doc/prior-art.md
86
+ - doc/waterfall.png
87
+ - lib/phew.rb
88
+ - lib/phew/font.rb
89
+ - lib/phew/font_repository.rb
90
+ - lib/phew/script.rb
91
+ - test/end_to_end/basic_run_test.rb
92
+ - test/test_helper.rb
93
+ - test/unit/font_repository_test.rb
94
+ - test/unit/font_test.rb
95
+ homepage: http://www.github.com/mvz/phew-font-viewer
96
+ licenses:
97
+ - GPL-3
98
+ metadata: {}
99
+ post_install_message:
100
+ rdoc_options:
101
+ - "--main"
102
+ - README.md
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: 1.9.3
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ requirements: []
116
+ rubyforge_project:
117
+ rubygems_version: 2.4.5
118
+ signing_key:
119
+ specification_version: 4
120
+ summary: A GNOME Font Viewer
121
+ test_files: []