phew 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []