├── efficient ├── __init__.py ├── work_tracker.py ├── journald_event_listener.py ├── console_display.py ├── display.py ├── color.py ├── runloop.py ├── self_host.py ├── journald_logging.py ├── tracker.py ├── countdown_timer.py ├── tracker_events.py ├── led_display.py ├── efficient.py ├── network_host.py └── fonts │ └── tom-thumb.bdf ├── efficient.service ├── LICENSE ├── .gitignore └── requirements.md /efficient/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /efficient/work_tracker.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /efficient/journald_event_listener.py: -------------------------------------------------------------------------------- 1 | # Acts like a event stream for tracker 2 | # Listens for events from the log source 3 | # Creates typed events 4 | # Auto generates events for AutoExpiringEvents 5 | -------------------------------------------------------------------------------- /efficient/console_display.py: -------------------------------------------------------------------------------- 1 | from subprocess import run 2 | from display import Display 3 | 4 | class ConsoleDisplay(Display): 5 | def write(self, message, color=None): 6 | self.clear() 7 | print(message) 8 | 9 | def clear(self): 10 | run('clear') -------------------------------------------------------------------------------- /efficient/display.py: -------------------------------------------------------------------------------- 1 | from abc import ABC 2 | from abc import abstractmethod 3 | 4 | class Display(ABC): 5 | @abstractmethod 6 | def write(self, message, color): 7 | raise NotImplementedError('abstract type') 8 | 9 | @abstractmethod 10 | def clear(self): 11 | raise NotImplementedError('abstract type') -------------------------------------------------------------------------------- /efficient.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Efficiency tracker for a productive day 3 | After=multi-user.target 4 | 5 | [Service] 6 | User=root 7 | Group=root 8 | WorkingDirectory=/home/gogsbread/efficient 9 | ExecStart=/usr/bin/python3 /home/gogsbread/efficient/network_host.py -b 70 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | -------------------------------------------------------------------------------- /efficient/color.py: -------------------------------------------------------------------------------- 1 | class Color(object): 2 | def __init__(self, r, g, b): 3 | self._r = r 4 | self._g = g 5 | self._b = b 6 | 7 | @property 8 | def R(self): 9 | return self._r 10 | 11 | @property 12 | def G(self): 13 | return self._g 14 | 15 | @property 16 | def B(self): 17 | return self._b -------------------------------------------------------------------------------- /efficient/runloop.py: -------------------------------------------------------------------------------- 1 | import time 2 | from threading import Thread 3 | 4 | class Runloop(object): 5 | grace_timeout_seconds = 2 6 | 7 | def __init__(self, delay): 8 | self._delay = delay.total_seconds() 9 | self._loop = None 10 | self._terminate = False 11 | 12 | def start(self, action, action_args): 13 | if self._loop: 14 | return 15 | 16 | self._loop = Thread(name='runloop', target=self._run, args=(action, action_args)) 17 | self._loop.daemon = True 18 | self._loop.start() 19 | 20 | def wait_until_stopped(self): 21 | self._loop.join() 22 | 23 | def stop(self): 24 | if not self._loop: 25 | return 26 | 27 | self._terminate = True 28 | self._loop.join(Runloop.grace_timeout_seconds) 29 | 30 | self._reset() 31 | 32 | def _run(self, action, action_args): 33 | while not self._terminate: 34 | action(action_args) 35 | time.sleep(self._delay) 36 | 37 | def _reset(self): 38 | self._loop = None 39 | self._terminate = False 40 | -------------------------------------------------------------------------------- /efficient/self_host.py: -------------------------------------------------------------------------------- 1 | import signal 2 | from datetime import datetime, timedelta 3 | from sys import exit 4 | 5 | from efficient import Efficient 6 | from console_display import ConsoleDisplay 7 | from tracker import WorkDayTracker 8 | from tracker_events import * 9 | 10 | def terminate(signum, frame): 11 | efficient.stop() 12 | 13 | if __name__ == '__main__': 14 | signal.signal(signal.SIGINT, terminate) 15 | 16 | display = ConsoleDisplay() 17 | 18 | # Sample test data 19 | ws = WorkStartEvent(datetime.utcnow()-timedelta(hours=5), datetime.utcnow()-timedelta(hours=5)) 20 | ls = LunchStartEvent(datetime.utcnow()-timedelta(hours=2), datetime.utcnow()-timedelta(hours=2)) 21 | bs = MiniBreakStartEvent(datetime.utcnow()-timedelta(hours=1), datetime.utcnow()-timedelta(hours=1)) 22 | tracker = WorkDayTracker() 23 | tracker.handle(ws) 24 | tracker.handle(ls) 25 | tracker.handle(bs) 26 | 27 | efficient = Efficient(display, tracker) 28 | efficient.start(timedelta(hours=8, minutes=0, seconds=0), lambda: efficient.stop()) 29 | print('Runloop started') 30 | efficient.wait_until_stopped() 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Antony Deepak Thomas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /efficient/journald_logging.py: -------------------------------------------------------------------------------- 1 | from systemd import journal 2 | 3 | class LogManager(object): 4 | @staticmethod 5 | def get_logger(name): 6 | return Logger(name) 7 | 8 | class Logger(object): 9 | def __init__(self, name): 10 | self._name = name 11 | 12 | def debug(self, message): 13 | journal.send(message, PRIORITY=journal.LOG_DEBUG, LOGGER=self._name) 14 | 15 | def info(self, message): 16 | journal.send(message, PRIORITY=journal.LOG_INFO, LOGGER=self._name) 17 | 18 | def error(self, message): 19 | journal.send(message, PRIORITY=journal.LOG_ERR, LOGGER=self._name) 20 | 21 | def event(self, name, metadata): 22 | message = "Recording event '{0}'".format(name) #just not required but lazy to deal with `sendv` api 23 | journal.send(message, PRIORITY=journal.LOG_INFO, LOGGER=self._name, EVENT_NAME=name, **(Logger._format_metadata(metadata))) 24 | 25 | @staticmethod 26 | def _format_metadata(metadata): 27 | """ 28 | Convert keys in kwargs to uppercase. This is journald style guide 29 | """ 30 | return {key.upper(): str(value) for key,value in metadata.items()} 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .nox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *.cover 48 | .hypothesis/ 49 | .pytest_cache/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | db.sqlite3 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # IPython 77 | profile_default/ 78 | ipython_config.py 79 | 80 | # pyenv 81 | .python-version 82 | 83 | # celery beat schedule file 84 | celerybeat-schedule 85 | 86 | # SageMath parsed files 87 | *.sage.py 88 | 89 | # Environments 90 | .env 91 | .venv 92 | env/ 93 | venv/ 94 | ENV/ 95 | env.bak/ 96 | venv.bak/ 97 | 98 | # Spyder project settings 99 | .spyderproject 100 | .spyproject 101 | 102 | # Rope project settings 103 | .ropeproject 104 | 105 | # mkdocs documentation 106 | /site 107 | 108 | # mypy 109 | .mypy_cache/ 110 | .dmypy.json 111 | dmypy.json 112 | -------------------------------------------------------------------------------- /requirements.md: -------------------------------------------------------------------------------- 1 | Functional: 2 | 3 | Intent of the project is to track the work efficiency and provide insights to improve\change habits and be a more productive worker. 4 | The above objective can be achieved by building the following modules. 5 | a) Track 6 | a. Standard and Overtime hours - log files & Ux display 7 | b. Productive\non-productive sub hours - log files (later??) 8 | b) Analyze 9 | a. Offline (scripts) 10 | b. Realtime (Display Ux) 11 | c) Act 12 | a. Human part (Hoping the insights will help make the change) 13 | 14 | Technical: 15 | Functional objectives can be realized with the following technical modules adding some fun 16 | a) Hardware - 17 | a. Rpi 18 | i. Make it work 19 | b. LED matrix 20 | i. Make it work 21 | ii. Make a stand so it doesn’t fall 22 | b) Software 23 | a. Logger 24 | i. Some Python logger 25 | b. Controller 26 | i. TCP commands over local network 27 | 1) Start day - First in morning 28 | 2) Pause day - Lunch break 29 | 3) End day - After OT. Concludes the day 30 | 4) Reset day - Emergency reset option if something is wrong 31 | 5) Configuration 32 | a) Standard work hour duration (8hr) 33 | ii. Modules 34 | 1) CountdownTimer 35 | 2) SimpleTimeTracker(or CountupTimer (unbounded) - for afterwork) 36 | 3) Display module 37 | 4) Program with Apis 38 | 5) Network Service in systemd 39 | 40 | c. Display 41 | i. Countdown timer for tracking standard work hours and showing real-time feedback progress 42 | ii. Countup timer for tracking overtime work hours and showing real-time feedback and progress 43 | -------------------------------------------------------------------------------- /efficient/tracker.py: -------------------------------------------------------------------------------- 1 | from abc import ABC 2 | from abc import abstractmethod 3 | from calendar import timegm 4 | from datetime import datetime 5 | from time import localtime 6 | from threading import RLock 7 | 8 | from tracker_events import WorkStartEvent,WorkEndEvent,LunchStartEvent,MiniBreakStartEvent 9 | 10 | class Tracker(ABC): 11 | @abstractmethod 12 | def handled_events(self): 13 | raise NotImplementedError('abstract type') 14 | 15 | @abstractmethod 16 | def handle(self): 17 | raise NotImplementedError('abstract type') 18 | 19 | @abstractmethod 20 | def summarize(self, dt, aggregate): 21 | raise NotImplementedError('abstract type') 22 | 23 | class WorkDayTracker(Tracker): 24 | _handled_events = ( 25 | WorkStartEvent, 26 | WorkEndEvent, 27 | LunchStartEvent, 28 | MiniBreakStartEvent) 29 | 30 | def __init__(self): 31 | self._events = {} 32 | self._lock = RLock() 33 | 34 | def handled_events(self): 35 | return WorkDayTracker._handled_events 36 | 37 | def handle(self, event): 38 | assert(isinstance(event, WorkDayTracker._handled_events)) 39 | 40 | with self._lock: 41 | events = self._get_events_for_date(event.client_time_utc) 42 | events.append(event) 43 | events.sort(key = lambda x: x.client_time_utc) 44 | 45 | def summarize(self, dt, aggregate): 46 | events = self._get_events_for_date(dt) 47 | return aggregate(events) 48 | 49 | def _get_events_for_date(self, dt): 50 | key = WorkDayTracker._get_key(dt) 51 | if key not in self._events: 52 | self._events[key] = [] 53 | return self._events[key] 54 | 55 | @staticmethod 56 | def _get_key(dt): 57 | local_time = WorkDayTracker._utc_to_local(dt) 58 | return f"{local_time.tm_year}{local_time.tm_mon}{local_time.tm_mday}" 59 | 60 | @staticmethod 61 | def _utc_to_local(utc): 62 | # Idea is to convert the utc to epoch and reconvert the epoch to localtime 63 | epoch = timegm(utc.timetuple()) 64 | return localtime(epoch) -------------------------------------------------------------------------------- /efficient/countdown_timer.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from datetime import timedelta 3 | from threading import Timer 4 | 5 | class CountdownTimer(object): 6 | ZeroDelta = timedelta(0) 7 | def __init__(self, duration, on_elapsed): 8 | self._duration = duration 9 | self._on_elapsed = on_elapsed 10 | 11 | self._remaining = self._duration 12 | self._start_time = None 13 | self._end_timer = None 14 | 15 | @property 16 | def remaining(self): 17 | if not self._is_running(): 18 | return self._remaining 19 | 20 | elapsed = datetime.utcnow() - self._start_time 21 | return (self._remaining - elapsed) if (self._remaining > elapsed) else CountdownTimer.ZeroDelta 22 | 23 | def start(self): 24 | if self._is_running(): 25 | raise CountdownTimerException("Already running") 26 | if self._remaining == CountdownTimer.ZeroDelta: 27 | raise CountdownTimerException("Timer has elapsed") 28 | 29 | self._start_time = datetime.utcnow() 30 | self._end_timer = Timer(self._remaining.total_seconds(), self._elapsed) 31 | self._end_timer.daemon = True 32 | self._end_timer.start() 33 | 34 | def stop(self): 35 | if (not self._is_running()) or (self._remaining == CountdownTimer.ZeroDelta): 36 | return 37 | 38 | elapsed = datetime.utcnow() - self._start_time 39 | self._remaining = (self._remaining - elapsed) if (self._remaining > elapsed) else CountdownTimer.ZeroDelta 40 | 41 | self._reset_time() 42 | 43 | def reset(self): 44 | self._remaining = self._duration 45 | self._reset_time() 46 | 47 | def _is_running(self): 48 | return self._start_time != None 49 | 50 | def _elapsed(self): 51 | self._remaining = CountdownTimer.ZeroDelta 52 | self._reset_time() 53 | 54 | if self._on_elapsed: 55 | self._on_elapsed() 56 | 57 | def _reset_time(self): 58 | self._start_time = None 59 | if self._end_timer: 60 | self._end_timer.cancel() 61 | self._end_timer = None 62 | 63 | class CountdownTimerException(Exception): 64 | def __init__(self, message): 65 | self._message = message 66 | -------------------------------------------------------------------------------- /efficient/tracker_events.py: -------------------------------------------------------------------------------- 1 | from datetime import timedelta 2 | 3 | class Event(object): 4 | def __init__(self, name, client_time_utc, server_time_utc): 5 | self.name = name 6 | self.client_time_utc = client_time_utc 7 | self.server_time_utc = server_time_utc 8 | 9 | class RangeEvent(Event): 10 | def __init__(self, name, client_time_utc, server_time_utc, start_event_type, end_event_type): 11 | super().__init__(name, client_time_utc, server_time_utc) 12 | self.start_event_type = start_event_type 13 | self.end_event_type = end_event_type 14 | 15 | class AutoExpiringRangeEvent(RangeEvent): 16 | def __init__(self, name, client_time_utc, server_time_utc, start_event_type, end_event_type, expiry): 17 | super().__init__(name, client_time_utc, server_time_utc, start_event_type, end_event_type) 18 | self.expiry = expiry 19 | 20 | class WorkEvent(RangeEvent): 21 | def __init__(self, name, client_time_utc, server_time_utc): 22 | super().__init__(name, client_time_utc, server_time_utc, WorkStartEvent, WorkEndEvent) 23 | 24 | class WorkStartEvent(WorkEvent): 25 | def __init__(self, client_time_utc, server_time_utc): 26 | super().__init__("start", client_time_utc, server_time_utc) 27 | 28 | class WorkEndEvent(WorkEvent): 29 | def __init__(self, client_time_utc, server_time_utc): 30 | super().__init__("end", client_time_utc, server_time_utc) 31 | 32 | class LunchEvent(AutoExpiringRangeEvent): 33 | def __init__(self, name, client_time_utc, server_time_utc): 34 | super().__init__(name, client_time_utc, server_time_utc, LunchStartEvent, LunchEndEvent, timedelta(minutes=45)) 35 | 36 | class LunchStartEvent(LunchEvent): 37 | def __init__(self, client_time_utc, server_time_utc): 38 | super().__init__("start", client_time_utc, server_time_utc) 39 | 40 | class LunchEndEvent(LunchEvent): 41 | def __init__(self, client_time_utc, server_time_utc): 42 | super().__init__("end", client_time_utc, server_time_utc) 43 | 44 | class MiniBreakEvent(AutoExpiringRangeEvent): 45 | def __init__(self, name, client_time_utc, server_time_utc): 46 | super().__init__(name, client_time_utc, server_time_utc, MiniBreakStartEvent, MiniBreakEndEvent, timedelta(minutes=15)) 47 | 48 | class MiniBreakStartEvent(MiniBreakEvent): 49 | def __init__(self, client_time_utc, server_time_utc): 50 | super().__init__("start", client_time_utc, server_time_utc) 51 | 52 | class MiniBreakEndEvent(MiniBreakEvent): 53 | def __init__(self, client_time_utc, server_time_utc): 54 | super().__init__("end", client_time_utc, server_time_utc) -------------------------------------------------------------------------------- /efficient/led_display.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | from journald_logging import LogManager 3 | from rgbmatrix import RGBMatrix, RGBMatrixOptions, graphics 4 | 5 | from color import Color 6 | from display import Display 7 | 8 | class LedFont(graphics.Font): 9 | def __init__(self, font_file): 10 | super().__init__() 11 | super().LoadFont(font_file) 12 | 13 | class LedColor: 14 | Red,Green,Blue = Color(255, 0, 0), Color(0, 255, 0), Color(0, 0, 255) 15 | 16 | class LedDisplay(Display): 17 | defaultFont = LedFont("fonts/tom-thumb.bdf") 18 | defaultColor = LedColor.Green 19 | 20 | def __init__(self, options, font=defaultFont): 21 | self._logger = LogManager.get_logger(__name__) 22 | self._logger.debug("Initializing display with '{0}'".format(options)) 23 | 24 | matrix_options = RGBMatrixOptions() 25 | 26 | matrix_options.hardware_mapping = options.led_gpio_mapping 27 | matrix_options.rows = options.led_rows 28 | matrix_options.cols = options.led_cols 29 | matrix_options.chain_length = options.led_chain 30 | matrix_options.parallel = options.led_parallel 31 | matrix_options.row_address_type = options.led_row_addr_type 32 | matrix_options.multiplexing = options.led_multiplexing 33 | matrix_options.pwm_bits = options.led_pwm_bits 34 | matrix_options.brightness = options.led_brightness 35 | matrix_options.pwm_lsb_nanoseconds = options.led_pwm_lsb_nanoseconds 36 | matrix_options.led_rgb_sequence = options.led_rgb_sequence 37 | matrix_options.show_refresh_rate = options.led_show_refresh 38 | matrix_options.gpio_slowdown = options.led_slowdown_gpio 39 | matrix_options.disable_hardware_pulsing = options.led_no_hardware_pulse 40 | 41 | self._matrix = RGBMatrix(options = matrix_options) 42 | self._offscreen_canvas = self._matrix.CreateFrameCanvas() 43 | 44 | self._font = font 45 | 46 | def write(self, message, color=defaultColor): 47 | self._offscreen_canvas.Clear() 48 | 49 | x = 0 50 | y = self._font.baseline 51 | for c in message: 52 | # support for '\n' 53 | if c == "\n": 54 | x = 0 55 | y += (self._font.baseline + 1) # add an extra space so that the next line doesn't hug the baseline 56 | continue 57 | 58 | x += self._font.DrawGlyph(self._offscreen_canvas, x, y, graphics.Color(color.R, color.G, color.B), ord(c)) 59 | 60 | self._offscreen_canvas = self._matrix.SwapOnVSync(self._offscreen_canvas) 61 | 62 | def clear(self): 63 | self._offscreen_canvas.Clear() 64 | self._offscreen_canvas = self._matrix.SwapOnVSync(self._offscreen_canvas) -------------------------------------------------------------------------------- /efficient/efficient.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime,timedelta 2 | from itertools import islice 3 | from threading import RLock 4 | 5 | from countdown_timer import CountdownTimer 6 | from runloop import Runloop 7 | from tracker_events import * 8 | 9 | class Efficient(object): 10 | ''' 11 | The intent of Efficient is to provide insights to improve\change habits and to be a more productive. 12 | In its current form, the Efficient system shows a count-down timer that provides a start/pause/resume/stop methods. This timer 13 | gives a ticking metric for the user to complete his work. 14 | In addition, the Efficient system also provides insights into events that occur during an activity. These events are tracked and 15 | shown as metrics. 16 | ''' 17 | 18 | def __init__(self, display, tracker): 19 | self._display = display 20 | self._tracker = tracker 21 | 22 | self._lock = RLock() 23 | self._timer = None 24 | self._runloop = None 25 | 26 | def start(self, duration, elapsed): 27 | ''' 28 | Starts a timer. Has only one active timer at a point in time 29 | ''' 30 | 31 | if self._timer: 32 | return 33 | 34 | with self._lock: 35 | if not self._timer: 36 | self._timer = CountdownTimer(duration, elapsed) 37 | self._timer.start() 38 | 39 | self._runloop = Runloop(delay=timedelta(hours=0, minutes=0, seconds=1)) 40 | self._runloop.start(action=self._update, action_args=(self._display, self._timer, self._tracker)) 41 | 42 | def pause(self): 43 | ''' 44 | Pauses the timer. 45 | ''' 46 | 47 | self._assert_timer_started() 48 | 49 | with self._lock: 50 | self._timer.stop() 51 | 52 | def resume(self): 53 | ''' 54 | Resumes a paused timer. 55 | ''' 56 | 57 | self._assert_timer_started() 58 | 59 | with self._lock: 60 | self._timer.start() 61 | 62 | def stop(self): 63 | ''' 64 | Stops the timer. 65 | ''' 66 | 67 | with self._lock: 68 | self._runloop.stop() 69 | self._timer.reset() 70 | self._display.clear() 71 | 72 | self._runloop = None 73 | self._timer = None 74 | 75 | def wait_until_stopped(self): 76 | ''' 77 | Provides a mechanism to block the current thread until efficient.stop() method is called 78 | ''' 79 | 80 | self._assert_timer_started() 81 | 82 | self._runloop.wait_until_stopped() 83 | 84 | def _update(self, args): 85 | ''' 86 | Called on every cycle of the update loop. 87 | ''' 88 | 89 | display = args[0] 90 | timer = args[1] 91 | tracker = args[2] 92 | 93 | hours,minutes,seconds = Efficient._parse(timer.remaining) 94 | message = '{:02}:{:02}:{:02}'.format(hours, minutes, seconds) 95 | 96 | event_durations = tracker.summarize(datetime.now(), aggregate=Efficient._aggregate_range_events) 97 | event_durations = Efficient._pretty_format(event_durations) 98 | top_2_event_durations = islice(sorted(event_durations, key=lambda x: x[1], reverse=True), 2) 99 | for event_duration in top_2_event_durations: 100 | ev,du = event_duration 101 | message += '\n{0} {1:02}'.format(ev, du) 102 | 103 | display.write(message) 104 | 105 | @staticmethod 106 | def _aggregate_range_events(events): 107 | ''' 108 | Aggregates events to show the top N events that took the most time. 109 | It computes the duration between a start and end event. If the end event is not found then assume it is an ongoing event 110 | and compute duration. 111 | ''' 112 | 113 | range_events = (event for event in events if isinstance(event, RangeEvent)) 114 | start_events = {} 115 | event_durations = {} 116 | 117 | for event in range_events: 118 | if isinstance(event, event.start_event_type): 119 | start_events[event.start_event_type] = event 120 | if start_events.get(event.start_event_type) and isinstance(event, event.end_event_type): 121 | range_event = Efficient._base_range_event(event.end_event_type) 122 | end_event = event 123 | duration = event_durations.get(range_event) if event_durations.get(range_event) else timedelta() 124 | duration += end_event.client_time_utc - start_events[event.start_event_type].client_time_utc 125 | event_durations[range_event] = duration 126 | del(start_events[event.start_event_type]) 127 | # If start event does not have an end event compute duration with datetime.utcnow() as it might an ongoing event 128 | for start_event in start_events: 129 | range_event = Efficient._base_range_event(start_event) 130 | duration = event_durations.get(range_event) if event_durations.get(range_event) else timedelta() 131 | duration += datetime.utcnow() - start_events[start_event].client_time_utc 132 | event_durations[range_event] = duration 133 | 134 | return event_durations 135 | 136 | def _assert_timer_started(self): 137 | if not self._timer: 138 | raise EfficientException("Timer not started") 139 | 140 | @staticmethod 141 | def _base_range_event(event): 142 | return event.__bases__[0] 143 | 144 | @staticmethod 145 | def _pretty_format(event_durations): 146 | # send it back as a iterable of kv pairs. 147 | for event in event_durations: 148 | d,h,m,s = Efficient._days_hours_minutes_seconds(event_durations[event]) 149 | duration = d or h or m or s 150 | yield (event.__name__[:2], duration) 151 | 152 | @staticmethod 153 | def _days_hours_minutes_seconds(td): 154 | return td.days, td.seconds//3600, (td.seconds//60)%60, (td.seconds)%60 155 | 156 | @staticmethod 157 | def _parse(timedelta): 158 | hours, remainder = divmod(timedelta.total_seconds(), 3600) 159 | minutes, seconds = divmod(remainder, 60) 160 | 161 | return (int(hours), int(minutes), int(seconds)) 162 | 163 | class EfficientException(Exception): 164 | def __init__(self, message): 165 | self._message = message 166 | -------------------------------------------------------------------------------- /efficient/network_host.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import codecs 3 | import json 4 | import signal 5 | import sys 6 | 7 | from datetime import timedelta 8 | from io import BytesIO 9 | from socketserver import TCPServer 10 | from socketserver import StreamRequestHandler 11 | 12 | from efficient import Efficient,EfficientException 13 | from journald_logging import LogManager 14 | from led_display import LedDisplay 15 | from tracker import WorkDayTracker 16 | 17 | class EfficientHandler(StreamRequestHandler): 18 | def __init__(self, efficient_tracker, request, client_address, server, max_data_length = 2048): 19 | self._efficient = efficient_tracker 20 | self._max_data_length = max_data_length 21 | 22 | self._logger = LogManager.get_logger(__name__) 23 | 24 | super().__init__(request, client_address, server) 25 | 26 | def handle(self): 27 | message = self.request.recv(self._max_data_length) 28 | self._logger.debug("Handling message '{0}'".format(message)) 29 | 30 | success,command,data = self._parse_command(message) 31 | if not success: 32 | self._logger.info("Message parsing failed with '{0}'".format(data)) 33 | self._send_message(data) 34 | return 35 | 36 | response = self._handle_command(command, data) 37 | self._send_message(response) 38 | 39 | def _handle_command(self, name, data): 40 | self._logger.debug("Handling command '{0}'".format(name)) 41 | if name == "start": 42 | return self._handle_start(data['args']) 43 | elif name == "pause": 44 | return self._handle_pause() 45 | elif name == "resume": 46 | return self._handle_resume() 47 | elif name == "end": 48 | return self._handle_end() 49 | elif name == "event": 50 | return self._handle_event(data['args']) 51 | 52 | message = "Command '{0}' not supported".format(name) 53 | self._logger.info(message) 54 | return message 55 | 56 | def _handle_start(self, args): 57 | ''' 58 | Wrapper around the start() method of Efficient. 59 | Parses the arguments and calls the start() method 60 | ''' 61 | 62 | if not args: 63 | message = "Command 'start' does not have any arguments" 64 | self._logger.debug(message) 65 | return message 66 | if 'duration' not in args: 67 | message = "Command 'start' does not have any 'duration' specified" 68 | self._logger.debug(message) 69 | return message 70 | 71 | duration = args['duration'] 72 | hours = duration['hours'] 73 | minutes = duration['minutes'] 74 | seconds = duration['seconds'] 75 | 76 | try: 77 | self._efficient.start(timedelta(hours=hours, minutes=minutes, seconds=seconds), lambda: self._efficient.stop()) 78 | except EfficientException as e: 79 | return str(e) 80 | 81 | response = "Timer started for hours:{0} minutes:{1} seconds:{2}".format(hours, minutes, seconds) 82 | self._logger.info(response) 83 | return response 84 | 85 | def _handle_pause(self): 86 | ''' 87 | Wrapper around the pause() method of Efficient. 88 | Parses the arguments and calls the pause() method 89 | ''' 90 | 91 | try: 92 | self._efficient.pause() 93 | except EfficientException as e: 94 | return str(e) 95 | 96 | response = "Timer paused" 97 | self._logger.info(response) 98 | return response 99 | 100 | def _handle_resume(self): 101 | ''' 102 | Wrapper around the resume() method of Efficient. 103 | Parses the arguments and calls the resume() method 104 | ''' 105 | 106 | try: 107 | self._efficient.resume() 108 | except EfficientException as e: 109 | return str(e) 110 | 111 | response = "Timer resumed" 112 | self._logger.info(response) 113 | return response 114 | 115 | def _handle_end(self): 116 | ''' 117 | Wrapper around the stop() method of Efficient. 118 | Parses the arguments and calls the stop() method 119 | ''' 120 | 121 | try: 122 | self._efficient.stop() 123 | except EfficientException as e: 124 | return str(e) 125 | 126 | response = "Timer ended" 127 | self._logger.info(response) 128 | return response 129 | 130 | def _handle_event(self, args): 131 | if not args: 132 | message = "Command 'event' does not have any arguments" 133 | self._logger.debug(message) 134 | return message 135 | 136 | if 'name' not in args: 137 | message = "Event does not have any 'name' specified" 138 | self._logger.debug(message) 139 | return message 140 | 141 | name = args['name'] 142 | metadata = args['metadata'] if ('metadata' in args) else {} 143 | self._logger.event(name, metadata) 144 | 145 | return "Event '{0}' logged".format(name) 146 | 147 | def _parse_command(self, message): 148 | try: 149 | str_message = message.decode('utf-8') 150 | data = json.loads(str_message) 151 | except json.JSONDecodeError as e: 152 | return (False, None, "Malformed Json passed. {0}".format(str(e))) 153 | 154 | if 'command' not in data: 155 | return (False, None, "Json not of the correct type. Missing 'command' type") 156 | 157 | return (True, data['command'], data) 158 | 159 | def _send_message(self, message, ending='\n'): 160 | message_to_send = message + ending 161 | message_bytes = message_to_send.encode('utf-8') 162 | self.wfile.write(message_bytes) 163 | 164 | class EfficientServer(TCPServer): 165 | allow_reuse_address = True 166 | 167 | def __init__(self, server_address, RequestHandlerClass, efficient_tracker): 168 | self._efficient_tracker = efficient_tracker 169 | 170 | super().__init__(server_address, RequestHandlerClass) 171 | 172 | def finish_request(self, request, client_address): 173 | self.RequestHandlerClass(self._efficient_tracker, request, client_address, self) 174 | 175 | def terminate(signum, frame): 176 | if server: 177 | server.server_close() 178 | sys.exit(0) 179 | 180 | if __name__ == "__main__": 181 | signal.signal(signal.SIGINT, terminate) 182 | 183 | log = LogManager.get_logger('global') 184 | 185 | parser = argparse.ArgumentParser() 186 | # Server args 187 | parser.add_argument("--host", action="store", help="Host interface", default='', type=str) 188 | parser.add_argument("--port", action="store", help="Host port", default=8080, type=int) 189 | # Display args 190 | parser.add_argument("-r", "--led-rows", action="store", help="Display rows. 16 for 16x32, 32 for 32x32. Default: 32", default=32, type=int) 191 | parser.add_argument("--led-cols", action="store", help="Panel columns. Typically 32 or 64. (Default: 32)", default=32, type=int) 192 | parser.add_argument("-c", "--led-chain", action="store", help="Daisy-chained boards. Default: 1.", default=1, type=int) 193 | parser.add_argument("-P", "--led-parallel", action="store", help="For Plus-models or RPi2: parallel chains. 1..3. Default: 1", default=1, type=int) 194 | parser.add_argument("-p", "--led-pwm-bits", action="store", help="Bits used for PWM. Something between 1..11. Default: 11", default=11, type=int) 195 | parser.add_argument("-b", "--led-brightness", action="store", help="Sets brightness level. Default: 100. Range: 1..100", default=100, type=int) 196 | parser.add_argument("-m", "--led-gpio-mapping", help="Hardware Mapping: regular, adafruit-hat, adafruit-hat-pwm", default='adafruit-hat-pwm', choices=['regular', 'adafruit-hat', 'adafruit-hat-pwm'], type=str) 197 | parser.add_argument("--led-scan-mode", action="store", help="Progressive or interlaced scan. 0 Progressive, 1 Interlaced (default)", default=1, choices=range(2), type=int) 198 | parser.add_argument("--led-pwm-lsb-nanoseconds", action="store", help="Base time-unit for the on-time in the lowest significant bit in nanoseconds. Default: 130", default=130, type=int) 199 | parser.add_argument("--led-show-refresh", action="store_true", help="Shows the current refresh rate of the LED panel", default=0) 200 | parser.add_argument("--led-slowdown-gpio", action="store", help="Slow down writing to GPIO. Range: 1..100. Default: 1", default=1, choices=range(3), type=int) 201 | parser.add_argument("--led-no-hardware-pulse", action="store", help="Don't use hardware pin-pulse generation. Default: True", default=False, type=bool) 202 | parser.add_argument("--led-rgb-sequence", action="store", help="Switch if your matrix has led colors swapped. Default: RGB", default="RGB", type=str) 203 | parser.add_argument("--led-row-addr-type", action="store", help="0 = default; 1=AB-addressed panels", default=0, type=int, choices=[0,1]) 204 | parser.add_argument("--led-multiplexing", action="store", help="Multiplexing type: 0=direct; 1=strip; 2=checker; 3=spiral (Default: 0)", default=0, type=int, choices=[0,1,2,3]) 205 | args = parser.parse_args() 206 | 207 | display = LedDisplay(args) 208 | tracker = WorkDayTracker() # TODO: have to add the proper tracker 209 | efficient = Efficient(display,tracker) 210 | server = EfficientServer((args.host, args.port), EfficientHandler, efficient) 211 | log.info("Efficient server started at port {0}".format(args.port)) 212 | 213 | server.serve_forever() -------------------------------------------------------------------------------- /efficient/fonts/tom-thumb.bdf: -------------------------------------------------------------------------------- 1 | STARTFONT 2.1 2 | FONT -Raccoon-Fixed4x6-Medium-R-Normal--6-60-75-75-P-40-ISO10646-1 3 | SIZE 6 75 75 4 | FONTBOUNDINGBOX 3 6 0 -1 5 | STARTPROPERTIES 25 6 | FONT_NAME "Fixed4x6" 7 | FONT_ASCENT 5 8 | FONT_DESCENT 1 9 | QUAD_WIDTH 6 10 | X_HEIGHT 3 11 | CAP_HEIGHT 4 12 | FONTNAME_REGISTRY "" 13 | FAMILY_NAME "Fixed4x6" 14 | FOUNDRY "Raccoon" 15 | WEIGHT_NAME "Medium" 16 | SETWIDTH_NAME "Normal" 17 | SLANT "R" 18 | ADD_STYLE_NAME "" 19 | PIXEL_SIZE 6 20 | POINT_SIZE 60 21 | RESOLUTION_X 75 22 | RESOLUTION_Y 75 23 | RESOLUTION 75 24 | SPACING "P" 25 | AVERAGE_WIDTH 40 26 | CHARSET_REGISTRY "ISO10646" 27 | CHARSET_ENCODING "1" 28 | CHARSET_COLLECTIONS "ASCII ISOLatin1Encoding ISO10646-1" 29 | FULL_NAME "Fixed4x6" 30 | COPYRIGHT """""MIT""""" 31 | ENDPROPERTIES 32 | CHARS 203 33 | STARTCHAR space 34 | ENCODING 32 35 | SWIDTH 1000 0 36 | DWIDTH 4 0 37 | BBX 1 1 3 4 38 | BITMAP 39 | 00 40 | ENDCHAR 41 | STARTCHAR exclam 42 | ENCODING 33 43 | SWIDTH 1000 0 44 | DWIDTH 4 0 45 | BBX 1 5 1 0 46 | BITMAP 47 | 80 48 | 80 49 | 80 50 | 00 51 | 80 52 | ENDCHAR 53 | STARTCHAR quotedbl 54 | ENCODING 34 55 | SWIDTH 1000 0 56 | DWIDTH 4 0 57 | BBX 3 2 0 3 58 | BITMAP 59 | A0 60 | A0 61 | ENDCHAR 62 | STARTCHAR numbersign 63 | ENCODING 35 64 | SWIDTH 1000 0 65 | DWIDTH 4 0 66 | BBX 3 5 0 0 67 | BITMAP 68 | A0 69 | E0 70 | A0 71 | E0 72 | A0 73 | ENDCHAR 74 | STARTCHAR dollar 75 | ENCODING 36 76 | SWIDTH 1000 0 77 | DWIDTH 4 0 78 | BBX 3 5 0 0 79 | BITMAP 80 | 60 81 | C0 82 | 60 83 | C0 84 | 40 85 | ENDCHAR 86 | STARTCHAR percent 87 | ENCODING 37 88 | SWIDTH 1000 0 89 | DWIDTH 4 0 90 | BBX 3 5 0 0 91 | BITMAP 92 | 80 93 | 20 94 | 40 95 | 80 96 | 20 97 | ENDCHAR 98 | STARTCHAR ampersand 99 | ENCODING 38 100 | SWIDTH 1000 0 101 | DWIDTH 4 0 102 | BBX 3 5 0 0 103 | BITMAP 104 | C0 105 | C0 106 | E0 107 | A0 108 | 60 109 | ENDCHAR 110 | STARTCHAR quotesingle 111 | ENCODING 39 112 | SWIDTH 1000 0 113 | DWIDTH 4 0 114 | BBX 1 2 1 3 115 | BITMAP 116 | 80 117 | 80 118 | ENDCHAR 119 | STARTCHAR parenleft 120 | ENCODING 40 121 | SWIDTH 1000 0 122 | DWIDTH 4 0 123 | BBX 2 5 1 0 124 | BITMAP 125 | 40 126 | 80 127 | 80 128 | 80 129 | 40 130 | ENDCHAR 131 | STARTCHAR parenright 132 | ENCODING 41 133 | SWIDTH 1000 0 134 | DWIDTH 4 0 135 | BBX 2 5 0 0 136 | BITMAP 137 | 80 138 | 40 139 | 40 140 | 40 141 | 80 142 | ENDCHAR 143 | STARTCHAR asterisk 144 | ENCODING 42 145 | SWIDTH 1000 0 146 | DWIDTH 4 0 147 | BBX 3 3 0 2 148 | BITMAP 149 | A0 150 | 40 151 | A0 152 | ENDCHAR 153 | STARTCHAR plus 154 | ENCODING 43 155 | SWIDTH 1000 0 156 | DWIDTH 4 0 157 | BBX 3 3 0 1 158 | BITMAP 159 | 40 160 | E0 161 | 40 162 | ENDCHAR 163 | STARTCHAR comma 164 | ENCODING 44 165 | SWIDTH 1000 0 166 | DWIDTH 4 0 167 | BBX 2 2 0 0 168 | BITMAP 169 | 40 170 | 80 171 | ENDCHAR 172 | STARTCHAR hyphen 173 | ENCODING 45 174 | SWIDTH 1000 0 175 | DWIDTH 4 0 176 | BBX 3 1 0 2 177 | BITMAP 178 | E0 179 | ENDCHAR 180 | STARTCHAR period 181 | ENCODING 46 182 | SWIDTH 1000 0 183 | DWIDTH 4 0 184 | BBX 1 1 1 0 185 | BITMAP 186 | 80 187 | ENDCHAR 188 | STARTCHAR slash 189 | ENCODING 47 190 | SWIDTH 1000 0 191 | DWIDTH 4 0 192 | BBX 3 5 0 0 193 | BITMAP 194 | 20 195 | 20 196 | 40 197 | 80 198 | 80 199 | ENDCHAR 200 | STARTCHAR zero 201 | ENCODING 48 202 | SWIDTH 1000 0 203 | DWIDTH 4 0 204 | BBX 3 5 0 0 205 | BITMAP 206 | 60 207 | A0 208 | A0 209 | A0 210 | C0 211 | ENDCHAR 212 | STARTCHAR one 213 | ENCODING 49 214 | SWIDTH 1000 0 215 | DWIDTH 4 0 216 | BBX 2 5 0 0 217 | BITMAP 218 | 40 219 | C0 220 | 40 221 | 40 222 | 40 223 | ENDCHAR 224 | STARTCHAR two 225 | ENCODING 50 226 | SWIDTH 1000 0 227 | DWIDTH 4 0 228 | BBX 3 5 0 0 229 | BITMAP 230 | C0 231 | 20 232 | 40 233 | 80 234 | E0 235 | ENDCHAR 236 | STARTCHAR three 237 | ENCODING 51 238 | SWIDTH 1000 0 239 | DWIDTH 4 0 240 | BBX 3 5 0 0 241 | BITMAP 242 | C0 243 | 20 244 | 40 245 | 20 246 | C0 247 | ENDCHAR 248 | STARTCHAR four 249 | ENCODING 52 250 | SWIDTH 1000 0 251 | DWIDTH 4 0 252 | BBX 3 5 0 0 253 | BITMAP 254 | A0 255 | A0 256 | E0 257 | 20 258 | 20 259 | ENDCHAR 260 | STARTCHAR five 261 | ENCODING 53 262 | SWIDTH 1000 0 263 | DWIDTH 4 0 264 | BBX 3 5 0 0 265 | BITMAP 266 | E0 267 | 80 268 | C0 269 | 20 270 | C0 271 | ENDCHAR 272 | STARTCHAR six 273 | ENCODING 54 274 | SWIDTH 1000 0 275 | DWIDTH 4 0 276 | BBX 3 5 0 0 277 | BITMAP 278 | 60 279 | 80 280 | E0 281 | A0 282 | E0 283 | ENDCHAR 284 | STARTCHAR seven 285 | ENCODING 55 286 | SWIDTH 1000 0 287 | DWIDTH 4 0 288 | BBX 3 5 0 0 289 | BITMAP 290 | E0 291 | 20 292 | 40 293 | 80 294 | 80 295 | ENDCHAR 296 | STARTCHAR eight 297 | ENCODING 56 298 | SWIDTH 1000 0 299 | DWIDTH 4 0 300 | BBX 3 5 0 0 301 | BITMAP 302 | E0 303 | A0 304 | E0 305 | A0 306 | E0 307 | ENDCHAR 308 | STARTCHAR nine 309 | ENCODING 57 310 | SWIDTH 1000 0 311 | DWIDTH 4 0 312 | BBX 3 5 0 0 313 | BITMAP 314 | E0 315 | A0 316 | E0 317 | 20 318 | C0 319 | ENDCHAR 320 | STARTCHAR colon 321 | ENCODING 58 322 | SWIDTH 1000 0 323 | DWIDTH 4 0 324 | BBX 1 3 1 1 325 | BITMAP 326 | 80 327 | 00 328 | 80 329 | ENDCHAR 330 | STARTCHAR semicolon 331 | ENCODING 59 332 | SWIDTH 1000 0 333 | DWIDTH 4 0 334 | BBX 2 4 0 0 335 | BITMAP 336 | 40 337 | 00 338 | 40 339 | 80 340 | ENDCHAR 341 | STARTCHAR less 342 | ENCODING 60 343 | SWIDTH 1000 0 344 | DWIDTH 4 0 345 | BBX 3 5 0 0 346 | BITMAP 347 | 20 348 | 40 349 | 80 350 | 40 351 | 20 352 | ENDCHAR 353 | STARTCHAR equal 354 | ENCODING 61 355 | SWIDTH 1000 0 356 | DWIDTH 4 0 357 | BBX 3 3 0 1 358 | BITMAP 359 | E0 360 | 00 361 | E0 362 | ENDCHAR 363 | STARTCHAR greater 364 | ENCODING 62 365 | SWIDTH 1000 0 366 | DWIDTH 4 0 367 | BBX 3 5 0 0 368 | BITMAP 369 | 80 370 | 40 371 | 20 372 | 40 373 | 80 374 | ENDCHAR 375 | STARTCHAR question 376 | ENCODING 63 377 | SWIDTH 1000 0 378 | DWIDTH 4 0 379 | BBX 3 5 0 0 380 | BITMAP 381 | E0 382 | 20 383 | 40 384 | 00 385 | 40 386 | ENDCHAR 387 | STARTCHAR at 388 | ENCODING 64 389 | SWIDTH 1000 0 390 | DWIDTH 4 0 391 | BBX 3 5 0 0 392 | BITMAP 393 | 40 394 | A0 395 | E0 396 | 80 397 | 60 398 | ENDCHAR 399 | STARTCHAR A 400 | ENCODING 65 401 | SWIDTH 1000 0 402 | DWIDTH 4 0 403 | BBX 3 5 0 0 404 | BITMAP 405 | 40 406 | A0 407 | E0 408 | A0 409 | A0 410 | ENDCHAR 411 | STARTCHAR B 412 | ENCODING 66 413 | SWIDTH 1000 0 414 | DWIDTH 4 0 415 | BBX 3 5 0 0 416 | BITMAP 417 | C0 418 | A0 419 | C0 420 | A0 421 | C0 422 | ENDCHAR 423 | STARTCHAR C 424 | ENCODING 67 425 | SWIDTH 1000 0 426 | DWIDTH 4 0 427 | BBX 3 5 0 0 428 | BITMAP 429 | 60 430 | 80 431 | 80 432 | 80 433 | 60 434 | ENDCHAR 435 | STARTCHAR D 436 | ENCODING 68 437 | SWIDTH 1000 0 438 | DWIDTH 4 0 439 | BBX 3 5 0 0 440 | BITMAP 441 | C0 442 | A0 443 | A0 444 | A0 445 | C0 446 | ENDCHAR 447 | STARTCHAR E 448 | ENCODING 69 449 | SWIDTH 1000 0 450 | DWIDTH 4 0 451 | BBX 3 5 0 0 452 | BITMAP 453 | E0 454 | 80 455 | E0 456 | 80 457 | E0 458 | ENDCHAR 459 | STARTCHAR F 460 | ENCODING 70 461 | SWIDTH 1000 0 462 | DWIDTH 4 0 463 | BBX 3 5 0 0 464 | BITMAP 465 | E0 466 | 80 467 | E0 468 | 80 469 | 80 470 | ENDCHAR 471 | STARTCHAR G 472 | ENCODING 71 473 | SWIDTH 1000 0 474 | DWIDTH 4 0 475 | BBX 3 5 0 0 476 | BITMAP 477 | 60 478 | 80 479 | E0 480 | A0 481 | 60 482 | ENDCHAR 483 | STARTCHAR H 484 | ENCODING 72 485 | SWIDTH 1000 0 486 | DWIDTH 4 0 487 | BBX 3 5 0 0 488 | BITMAP 489 | A0 490 | A0 491 | E0 492 | A0 493 | A0 494 | ENDCHAR 495 | STARTCHAR I 496 | ENCODING 73 497 | SWIDTH 1000 0 498 | DWIDTH 4 0 499 | BBX 3 5 0 0 500 | BITMAP 501 | E0 502 | 40 503 | 40 504 | 40 505 | E0 506 | ENDCHAR 507 | STARTCHAR J 508 | ENCODING 74 509 | SWIDTH 1000 0 510 | DWIDTH 4 0 511 | BBX 3 5 0 0 512 | BITMAP 513 | 20 514 | 20 515 | 20 516 | A0 517 | 40 518 | ENDCHAR 519 | STARTCHAR K 520 | ENCODING 75 521 | SWIDTH 1000 0 522 | DWIDTH 4 0 523 | BBX 3 5 0 0 524 | BITMAP 525 | A0 526 | A0 527 | C0 528 | A0 529 | A0 530 | ENDCHAR 531 | STARTCHAR L 532 | ENCODING 76 533 | SWIDTH 1000 0 534 | DWIDTH 4 0 535 | BBX 3 5 0 0 536 | BITMAP 537 | 80 538 | 80 539 | 80 540 | 80 541 | E0 542 | ENDCHAR 543 | STARTCHAR M 544 | ENCODING 77 545 | SWIDTH 1000 0 546 | DWIDTH 4 0 547 | BBX 3 5 0 0 548 | BITMAP 549 | A0 550 | E0 551 | E0 552 | A0 553 | A0 554 | ENDCHAR 555 | STARTCHAR N 556 | ENCODING 78 557 | SWIDTH 1000 0 558 | DWIDTH 4 0 559 | BBX 3 5 0 0 560 | BITMAP 561 | A0 562 | E0 563 | E0 564 | E0 565 | A0 566 | ENDCHAR 567 | STARTCHAR O 568 | ENCODING 79 569 | SWIDTH 1000 0 570 | DWIDTH 4 0 571 | BBX 3 5 0 0 572 | BITMAP 573 | 40 574 | A0 575 | A0 576 | A0 577 | 40 578 | ENDCHAR 579 | STARTCHAR P 580 | ENCODING 80 581 | SWIDTH 1000 0 582 | DWIDTH 4 0 583 | BBX 3 5 0 0 584 | BITMAP 585 | C0 586 | A0 587 | C0 588 | 80 589 | 80 590 | ENDCHAR 591 | STARTCHAR Q 592 | ENCODING 81 593 | SWIDTH 1000 0 594 | DWIDTH 4 0 595 | BBX 3 5 0 0 596 | BITMAP 597 | 40 598 | A0 599 | A0 600 | E0 601 | 60 602 | ENDCHAR 603 | STARTCHAR R 604 | ENCODING 82 605 | SWIDTH 1000 0 606 | DWIDTH 4 0 607 | BBX 3 5 0 0 608 | BITMAP 609 | C0 610 | A0 611 | E0 612 | C0 613 | A0 614 | ENDCHAR 615 | STARTCHAR S 616 | ENCODING 83 617 | SWIDTH 1000 0 618 | DWIDTH 4 0 619 | BBX 3 5 0 0 620 | BITMAP 621 | 60 622 | 80 623 | 40 624 | 20 625 | C0 626 | ENDCHAR 627 | STARTCHAR T 628 | ENCODING 84 629 | SWIDTH 1000 0 630 | DWIDTH 4 0 631 | BBX 3 5 0 0 632 | BITMAP 633 | E0 634 | 40 635 | 40 636 | 40 637 | 40 638 | ENDCHAR 639 | STARTCHAR U 640 | ENCODING 85 641 | SWIDTH 1000 0 642 | DWIDTH 4 0 643 | BBX 3 5 0 0 644 | BITMAP 645 | A0 646 | A0 647 | A0 648 | A0 649 | 60 650 | ENDCHAR 651 | STARTCHAR V 652 | ENCODING 86 653 | SWIDTH 1000 0 654 | DWIDTH 4 0 655 | BBX 3 5 0 0 656 | BITMAP 657 | A0 658 | A0 659 | A0 660 | 40 661 | 40 662 | ENDCHAR 663 | STARTCHAR W 664 | ENCODING 87 665 | SWIDTH 1000 0 666 | DWIDTH 4 0 667 | BBX 3 5 0 0 668 | BITMAP 669 | A0 670 | A0 671 | E0 672 | E0 673 | A0 674 | ENDCHAR 675 | STARTCHAR X 676 | ENCODING 88 677 | SWIDTH 1000 0 678 | DWIDTH 4 0 679 | BBX 3 5 0 0 680 | BITMAP 681 | A0 682 | A0 683 | 40 684 | A0 685 | A0 686 | ENDCHAR 687 | STARTCHAR Y 688 | ENCODING 89 689 | SWIDTH 1000 0 690 | DWIDTH 4 0 691 | BBX 3 5 0 0 692 | BITMAP 693 | A0 694 | A0 695 | 40 696 | 40 697 | 40 698 | ENDCHAR 699 | STARTCHAR Z 700 | ENCODING 90 701 | SWIDTH 1000 0 702 | DWIDTH 4 0 703 | BBX 3 5 0 0 704 | BITMAP 705 | E0 706 | 20 707 | 40 708 | 80 709 | E0 710 | ENDCHAR 711 | STARTCHAR bracketleft 712 | ENCODING 91 713 | SWIDTH 1000 0 714 | DWIDTH 4 0 715 | BBX 3 5 0 0 716 | BITMAP 717 | E0 718 | 80 719 | 80 720 | 80 721 | E0 722 | ENDCHAR 723 | STARTCHAR backslash 724 | ENCODING 92 725 | SWIDTH 1000 0 726 | DWIDTH 4 0 727 | BBX 3 3 0 1 728 | BITMAP 729 | 80 730 | 40 731 | 20 732 | ENDCHAR 733 | STARTCHAR bracketright 734 | ENCODING 93 735 | SWIDTH 1000 0 736 | DWIDTH 4 0 737 | BBX 3 5 0 0 738 | BITMAP 739 | E0 740 | 20 741 | 20 742 | 20 743 | E0 744 | ENDCHAR 745 | STARTCHAR asciicircum 746 | ENCODING 94 747 | SWIDTH 1000 0 748 | DWIDTH 4 0 749 | BBX 3 2 0 3 750 | BITMAP 751 | 40 752 | A0 753 | ENDCHAR 754 | STARTCHAR underscore 755 | ENCODING 95 756 | SWIDTH 1000 0 757 | DWIDTH 4 0 758 | BBX 3 1 0 0 759 | BITMAP 760 | E0 761 | ENDCHAR 762 | STARTCHAR grave 763 | ENCODING 96 764 | SWIDTH 1000 0 765 | DWIDTH 4 0 766 | BBX 2 2 0 3 767 | BITMAP 768 | 80 769 | 40 770 | ENDCHAR 771 | STARTCHAR a 772 | ENCODING 97 773 | SWIDTH 1000 0 774 | DWIDTH 4 0 775 | BBX 3 4 0 0 776 | BITMAP 777 | C0 778 | 60 779 | A0 780 | E0 781 | ENDCHAR 782 | STARTCHAR b 783 | ENCODING 98 784 | SWIDTH 1000 0 785 | DWIDTH 4 0 786 | BBX 3 5 0 0 787 | BITMAP 788 | 80 789 | C0 790 | A0 791 | A0 792 | C0 793 | ENDCHAR 794 | STARTCHAR c 795 | ENCODING 99 796 | SWIDTH 1000 0 797 | DWIDTH 4 0 798 | BBX 3 4 0 0 799 | BITMAP 800 | 60 801 | 80 802 | 80 803 | 60 804 | ENDCHAR 805 | STARTCHAR d 806 | ENCODING 100 807 | SWIDTH 1000 0 808 | DWIDTH 4 0 809 | BBX 3 5 0 0 810 | BITMAP 811 | 20 812 | 60 813 | A0 814 | A0 815 | 60 816 | ENDCHAR 817 | STARTCHAR e 818 | ENCODING 101 819 | SWIDTH 1000 0 820 | DWIDTH 4 0 821 | BBX 3 4 0 0 822 | BITMAP 823 | 60 824 | A0 825 | C0 826 | 60 827 | ENDCHAR 828 | STARTCHAR f 829 | ENCODING 102 830 | SWIDTH 1000 0 831 | DWIDTH 4 0 832 | BBX 3 5 0 0 833 | BITMAP 834 | 20 835 | 40 836 | E0 837 | 40 838 | 40 839 | ENDCHAR 840 | STARTCHAR g 841 | ENCODING 103 842 | SWIDTH 1000 0 843 | DWIDTH 4 0 844 | BBX 3 5 0 -1 845 | BITMAP 846 | 60 847 | A0 848 | E0 849 | 20 850 | 40 851 | ENDCHAR 852 | STARTCHAR h 853 | ENCODING 104 854 | SWIDTH 1000 0 855 | DWIDTH 4 0 856 | BBX 3 5 0 0 857 | BITMAP 858 | 80 859 | C0 860 | A0 861 | A0 862 | A0 863 | ENDCHAR 864 | STARTCHAR i 865 | ENCODING 105 866 | SWIDTH 1000 0 867 | DWIDTH 4 0 868 | BBX 1 5 1 0 869 | BITMAP 870 | 80 871 | 00 872 | 80 873 | 80 874 | 80 875 | ENDCHAR 876 | STARTCHAR j 877 | ENCODING 106 878 | SWIDTH 1000 0 879 | DWIDTH 4 0 880 | BBX 3 6 0 -1 881 | BITMAP 882 | 20 883 | 00 884 | 20 885 | 20 886 | A0 887 | 40 888 | ENDCHAR 889 | STARTCHAR k 890 | ENCODING 107 891 | SWIDTH 1000 0 892 | DWIDTH 4 0 893 | BBX 3 5 0 0 894 | BITMAP 895 | 80 896 | A0 897 | C0 898 | C0 899 | A0 900 | ENDCHAR 901 | STARTCHAR l 902 | ENCODING 108 903 | SWIDTH 1000 0 904 | DWIDTH 4 0 905 | BBX 3 5 0 0 906 | BITMAP 907 | C0 908 | 40 909 | 40 910 | 40 911 | E0 912 | ENDCHAR 913 | STARTCHAR m 914 | ENCODING 109 915 | SWIDTH 1000 0 916 | DWIDTH 4 0 917 | BBX 3 4 0 0 918 | BITMAP 919 | E0 920 | E0 921 | E0 922 | A0 923 | ENDCHAR 924 | STARTCHAR n 925 | ENCODING 110 926 | SWIDTH 1000 0 927 | DWIDTH 4 0 928 | BBX 3 4 0 0 929 | BITMAP 930 | C0 931 | A0 932 | A0 933 | A0 934 | ENDCHAR 935 | STARTCHAR o 936 | ENCODING 111 937 | SWIDTH 1000 0 938 | DWIDTH 4 0 939 | BBX 3 4 0 0 940 | BITMAP 941 | 40 942 | A0 943 | A0 944 | 40 945 | ENDCHAR 946 | STARTCHAR p 947 | ENCODING 112 948 | SWIDTH 1000 0 949 | DWIDTH 4 0 950 | BBX 3 5 0 -1 951 | BITMAP 952 | C0 953 | A0 954 | A0 955 | C0 956 | 80 957 | ENDCHAR 958 | STARTCHAR q 959 | ENCODING 113 960 | SWIDTH 1000 0 961 | DWIDTH 4 0 962 | BBX 3 5 0 -1 963 | BITMAP 964 | 60 965 | A0 966 | A0 967 | 60 968 | 20 969 | ENDCHAR 970 | STARTCHAR r 971 | ENCODING 114 972 | SWIDTH 1000 0 973 | DWIDTH 4 0 974 | BBX 3 4 0 0 975 | BITMAP 976 | 60 977 | 80 978 | 80 979 | 80 980 | ENDCHAR 981 | STARTCHAR s 982 | ENCODING 115 983 | SWIDTH 1000 0 984 | DWIDTH 4 0 985 | BBX 3 4 0 0 986 | BITMAP 987 | 60 988 | C0 989 | 60 990 | C0 991 | ENDCHAR 992 | STARTCHAR t 993 | ENCODING 116 994 | SWIDTH 1000 0 995 | DWIDTH 4 0 996 | BBX 3 5 0 0 997 | BITMAP 998 | 40 999 | E0 1000 | 40 1001 | 40 1002 | 60 1003 | ENDCHAR 1004 | STARTCHAR u 1005 | ENCODING 117 1006 | SWIDTH 1000 0 1007 | DWIDTH 4 0 1008 | BBX 3 4 0 0 1009 | BITMAP 1010 | A0 1011 | A0 1012 | A0 1013 | 60 1014 | ENDCHAR 1015 | STARTCHAR v 1016 | ENCODING 118 1017 | SWIDTH 1000 0 1018 | DWIDTH 4 0 1019 | BBX 3 4 0 0 1020 | BITMAP 1021 | A0 1022 | A0 1023 | E0 1024 | 40 1025 | ENDCHAR 1026 | STARTCHAR w 1027 | ENCODING 119 1028 | SWIDTH 1000 0 1029 | DWIDTH 4 0 1030 | BBX 3 4 0 0 1031 | BITMAP 1032 | A0 1033 | E0 1034 | E0 1035 | E0 1036 | ENDCHAR 1037 | STARTCHAR x 1038 | ENCODING 120 1039 | SWIDTH 1000 0 1040 | DWIDTH 4 0 1041 | BBX 3 4 0 0 1042 | BITMAP 1043 | A0 1044 | 40 1045 | 40 1046 | A0 1047 | ENDCHAR 1048 | STARTCHAR y 1049 | ENCODING 121 1050 | SWIDTH 1000 0 1051 | DWIDTH 4 0 1052 | BBX 3 5 0 -1 1053 | BITMAP 1054 | A0 1055 | A0 1056 | 60 1057 | 20 1058 | 40 1059 | ENDCHAR 1060 | STARTCHAR z 1061 | ENCODING 122 1062 | SWIDTH 1000 0 1063 | DWIDTH 4 0 1064 | BBX 3 4 0 0 1065 | BITMAP 1066 | E0 1067 | 60 1068 | C0 1069 | E0 1070 | ENDCHAR 1071 | STARTCHAR braceleft 1072 | ENCODING 123 1073 | SWIDTH 1000 0 1074 | DWIDTH 4 0 1075 | BBX 3 5 0 0 1076 | BITMAP 1077 | 60 1078 | 40 1079 | 80 1080 | 40 1081 | 60 1082 | ENDCHAR 1083 | STARTCHAR bar 1084 | ENCODING 124 1085 | SWIDTH 1000 0 1086 | DWIDTH 4 0 1087 | BBX 1 5 1 0 1088 | BITMAP 1089 | 80 1090 | 80 1091 | 00 1092 | 80 1093 | 80 1094 | ENDCHAR 1095 | STARTCHAR braceright 1096 | ENCODING 125 1097 | SWIDTH 1000 0 1098 | DWIDTH 4 0 1099 | BBX 3 5 0 0 1100 | BITMAP 1101 | C0 1102 | 40 1103 | 20 1104 | 40 1105 | C0 1106 | ENDCHAR 1107 | STARTCHAR asciitilde 1108 | ENCODING 126 1109 | SWIDTH 1000 0 1110 | DWIDTH 4 0 1111 | BBX 3 2 0 3 1112 | BITMAP 1113 | 60 1114 | C0 1115 | ENDCHAR 1116 | STARTCHAR exclamdown 1117 | ENCODING 161 1118 | SWIDTH 1000 0 1119 | DWIDTH 4 0 1120 | BBX 1 5 1 0 1121 | BITMAP 1122 | 80 1123 | 00 1124 | 80 1125 | 80 1126 | 80 1127 | ENDCHAR 1128 | STARTCHAR cent 1129 | ENCODING 162 1130 | SWIDTH 1000 0 1131 | DWIDTH 4 0 1132 | BBX 3 5 0 0 1133 | BITMAP 1134 | 40 1135 | E0 1136 | 80 1137 | E0 1138 | 40 1139 | ENDCHAR 1140 | STARTCHAR sterling 1141 | ENCODING 163 1142 | SWIDTH 1000 0 1143 | DWIDTH 4 0 1144 | BBX 3 5 0 0 1145 | BITMAP 1146 | 60 1147 | 40 1148 | E0 1149 | 40 1150 | E0 1151 | ENDCHAR 1152 | STARTCHAR currency 1153 | ENCODING 164 1154 | SWIDTH 1000 0 1155 | DWIDTH 4 0 1156 | BBX 3 5 0 0 1157 | BITMAP 1158 | A0 1159 | 40 1160 | E0 1161 | 40 1162 | A0 1163 | ENDCHAR 1164 | STARTCHAR yen 1165 | ENCODING 165 1166 | SWIDTH 1000 0 1167 | DWIDTH 4 0 1168 | BBX 3 5 0 0 1169 | BITMAP 1170 | A0 1171 | A0 1172 | 40 1173 | E0 1174 | 40 1175 | ENDCHAR 1176 | STARTCHAR brokenbar 1177 | ENCODING 166 1178 | SWIDTH 1000 0 1179 | DWIDTH 4 0 1180 | BBX 1 5 1 0 1181 | BITMAP 1182 | 80 1183 | 80 1184 | 00 1185 | 80 1186 | 80 1187 | ENDCHAR 1188 | STARTCHAR section 1189 | ENCODING 167 1190 | SWIDTH 1000 0 1191 | DWIDTH 4 0 1192 | BBX 3 5 0 0 1193 | BITMAP 1194 | 60 1195 | 40 1196 | A0 1197 | 40 1198 | C0 1199 | ENDCHAR 1200 | STARTCHAR dieresis 1201 | ENCODING 168 1202 | SWIDTH 1000 0 1203 | DWIDTH 4 0 1204 | BBX 3 1 0 4 1205 | BITMAP 1206 | A0 1207 | ENDCHAR 1208 | STARTCHAR copyright 1209 | ENCODING 169 1210 | SWIDTH 1000 0 1211 | DWIDTH 4 0 1212 | BBX 3 3 0 2 1213 | BITMAP 1214 | 60 1215 | 80 1216 | 60 1217 | ENDCHAR 1218 | STARTCHAR ordfeminine 1219 | ENCODING 170 1220 | SWIDTH 1000 0 1221 | DWIDTH 4 0 1222 | BBX 3 5 0 0 1223 | BITMAP 1224 | 60 1225 | A0 1226 | E0 1227 | 00 1228 | E0 1229 | ENDCHAR 1230 | STARTCHAR guillemotleft 1231 | ENCODING 171 1232 | SWIDTH 1000 0 1233 | DWIDTH 4 0 1234 | BBX 2 3 0 2 1235 | BITMAP 1236 | 40 1237 | 80 1238 | 40 1239 | ENDCHAR 1240 | STARTCHAR logicalnot 1241 | ENCODING 172 1242 | SWIDTH 1000 0 1243 | DWIDTH 4 0 1244 | BBX 3 2 0 2 1245 | BITMAP 1246 | E0 1247 | 20 1248 | ENDCHAR 1249 | STARTCHAR softhyphen 1250 | ENCODING 173 1251 | SWIDTH 1000 0 1252 | DWIDTH 4 0 1253 | BBX 2 1 0 2 1254 | BITMAP 1255 | C0 1256 | ENDCHAR 1257 | STARTCHAR registered 1258 | ENCODING 174 1259 | SWIDTH 1000 0 1260 | DWIDTH 4 0 1261 | BBX 3 3 0 2 1262 | BITMAP 1263 | C0 1264 | C0 1265 | A0 1266 | ENDCHAR 1267 | STARTCHAR macron 1268 | ENCODING 175 1269 | SWIDTH 1000 0 1270 | DWIDTH 4 0 1271 | BBX 3 1 0 4 1272 | BITMAP 1273 | E0 1274 | ENDCHAR 1275 | STARTCHAR degree 1276 | ENCODING 176 1277 | SWIDTH 1000 0 1278 | DWIDTH 4 0 1279 | BBX 3 3 0 2 1280 | BITMAP 1281 | 40 1282 | A0 1283 | 40 1284 | ENDCHAR 1285 | STARTCHAR plusminus 1286 | ENCODING 177 1287 | SWIDTH 1000 0 1288 | DWIDTH 4 0 1289 | BBX 3 5 0 0 1290 | BITMAP 1291 | 40 1292 | E0 1293 | 40 1294 | 00 1295 | E0 1296 | ENDCHAR 1297 | STARTCHAR twosuperior 1298 | ENCODING 178 1299 | SWIDTH 1000 0 1300 | DWIDTH 4 0 1301 | BBX 3 3 0 2 1302 | BITMAP 1303 | C0 1304 | 40 1305 | 60 1306 | ENDCHAR 1307 | STARTCHAR threesuperior 1308 | ENCODING 179 1309 | SWIDTH 1000 0 1310 | DWIDTH 4 0 1311 | BBX 3 3 0 2 1312 | BITMAP 1313 | E0 1314 | 60 1315 | E0 1316 | ENDCHAR 1317 | STARTCHAR acute 1318 | ENCODING 180 1319 | SWIDTH 1000 0 1320 | DWIDTH 4 0 1321 | BBX 2 2 1 3 1322 | BITMAP 1323 | 40 1324 | 80 1325 | ENDCHAR 1326 | STARTCHAR mu 1327 | ENCODING 181 1328 | SWIDTH 1000 0 1329 | DWIDTH 4 0 1330 | BBX 3 5 0 0 1331 | BITMAP 1332 | A0 1333 | A0 1334 | A0 1335 | C0 1336 | 80 1337 | ENDCHAR 1338 | STARTCHAR paragraph 1339 | ENCODING 182 1340 | SWIDTH 1000 0 1341 | DWIDTH 4 0 1342 | BBX 3 5 0 0 1343 | BITMAP 1344 | 60 1345 | A0 1346 | 60 1347 | 60 1348 | 60 1349 | ENDCHAR 1350 | STARTCHAR periodcentered 1351 | ENCODING 183 1352 | SWIDTH 1000 0 1353 | DWIDTH 4 0 1354 | BBX 3 3 0 1 1355 | BITMAP 1356 | E0 1357 | E0 1358 | E0 1359 | ENDCHAR 1360 | STARTCHAR cedilla 1361 | ENCODING 184 1362 | SWIDTH 1000 0 1363 | DWIDTH 4 0 1364 | BBX 3 3 0 0 1365 | BITMAP 1366 | 40 1367 | 20 1368 | C0 1369 | ENDCHAR 1370 | STARTCHAR onesuperior 1371 | ENCODING 185 1372 | SWIDTH 1000 0 1373 | DWIDTH 4 0 1374 | BBX 1 3 1 2 1375 | BITMAP 1376 | 80 1377 | 80 1378 | 80 1379 | ENDCHAR 1380 | STARTCHAR ordmasculine 1381 | ENCODING 186 1382 | SWIDTH 1000 0 1383 | DWIDTH 4 0 1384 | BBX 3 5 0 0 1385 | BITMAP 1386 | 40 1387 | A0 1388 | 40 1389 | 00 1390 | E0 1391 | ENDCHAR 1392 | STARTCHAR guillemotright 1393 | ENCODING 187 1394 | SWIDTH 1000 0 1395 | DWIDTH 4 0 1396 | BBX 2 3 1 2 1397 | BITMAP 1398 | 80 1399 | 40 1400 | 80 1401 | ENDCHAR 1402 | STARTCHAR onequarter 1403 | ENCODING 188 1404 | SWIDTH 1000 0 1405 | DWIDTH 4 0 1406 | BBX 3 5 0 0 1407 | BITMAP 1408 | 80 1409 | 80 1410 | 00 1411 | 60 1412 | 20 1413 | ENDCHAR 1414 | STARTCHAR onehalf 1415 | ENCODING 189 1416 | SWIDTH 1000 0 1417 | DWIDTH 4 0 1418 | BBX 3 5 0 0 1419 | BITMAP 1420 | 80 1421 | 80 1422 | 00 1423 | C0 1424 | 60 1425 | ENDCHAR 1426 | STARTCHAR threequarters 1427 | ENCODING 190 1428 | SWIDTH 1000 0 1429 | DWIDTH 4 0 1430 | BBX 3 5 0 0 1431 | BITMAP 1432 | C0 1433 | C0 1434 | 00 1435 | 60 1436 | 20 1437 | ENDCHAR 1438 | STARTCHAR questiondown 1439 | ENCODING 191 1440 | SWIDTH 1000 0 1441 | DWIDTH 4 0 1442 | BBX 3 5 0 0 1443 | BITMAP 1444 | 40 1445 | 00 1446 | 40 1447 | 80 1448 | E0 1449 | ENDCHAR 1450 | STARTCHAR Agrave 1451 | ENCODING 192 1452 | SWIDTH 1000 0 1453 | DWIDTH 4 0 1454 | BBX 3 5 0 0 1455 | BITMAP 1456 | 40 1457 | 20 1458 | 40 1459 | E0 1460 | A0 1461 | ENDCHAR 1462 | STARTCHAR Aacute 1463 | ENCODING 193 1464 | SWIDTH 1000 0 1465 | DWIDTH 4 0 1466 | BBX 3 5 0 0 1467 | BITMAP 1468 | 40 1469 | 80 1470 | 40 1471 | E0 1472 | A0 1473 | ENDCHAR 1474 | STARTCHAR Acircumflex 1475 | ENCODING 194 1476 | SWIDTH 1000 0 1477 | DWIDTH 4 0 1478 | BBX 3 5 0 0 1479 | BITMAP 1480 | E0 1481 | 00 1482 | 40 1483 | E0 1484 | A0 1485 | ENDCHAR 1486 | STARTCHAR Atilde 1487 | ENCODING 195 1488 | SWIDTH 1000 0 1489 | DWIDTH 4 0 1490 | BBX 3 5 0 0 1491 | BITMAP 1492 | 60 1493 | C0 1494 | 40 1495 | E0 1496 | A0 1497 | ENDCHAR 1498 | STARTCHAR Adieresis 1499 | ENCODING 196 1500 | SWIDTH 1000 0 1501 | DWIDTH 4 0 1502 | BBX 3 5 0 0 1503 | BITMAP 1504 | A0 1505 | 40 1506 | A0 1507 | E0 1508 | A0 1509 | ENDCHAR 1510 | STARTCHAR Aring 1511 | ENCODING 197 1512 | SWIDTH 1000 0 1513 | DWIDTH 4 0 1514 | BBX 3 5 0 0 1515 | BITMAP 1516 | C0 1517 | C0 1518 | A0 1519 | E0 1520 | A0 1521 | ENDCHAR 1522 | STARTCHAR AE 1523 | ENCODING 198 1524 | SWIDTH 1000 0 1525 | DWIDTH 4 0 1526 | BBX 3 5 0 0 1527 | BITMAP 1528 | 60 1529 | C0 1530 | E0 1531 | C0 1532 | E0 1533 | ENDCHAR 1534 | STARTCHAR Ccedilla 1535 | ENCODING 199 1536 | SWIDTH 1000 0 1537 | DWIDTH 4 0 1538 | BBX 3 6 0 -1 1539 | BITMAP 1540 | 60 1541 | 80 1542 | 80 1543 | 60 1544 | 20 1545 | 40 1546 | ENDCHAR 1547 | STARTCHAR Egrave 1548 | ENCODING 200 1549 | SWIDTH 1000 0 1550 | DWIDTH 4 0 1551 | BBX 3 5 0 0 1552 | BITMAP 1553 | 40 1554 | 20 1555 | E0 1556 | C0 1557 | E0 1558 | ENDCHAR 1559 | STARTCHAR Eacute 1560 | ENCODING 201 1561 | SWIDTH 1000 0 1562 | DWIDTH 4 0 1563 | BBX 3 5 0 0 1564 | BITMAP 1565 | 40 1566 | 80 1567 | E0 1568 | C0 1569 | E0 1570 | ENDCHAR 1571 | STARTCHAR Ecircumflex 1572 | ENCODING 202 1573 | SWIDTH 1000 0 1574 | DWIDTH 4 0 1575 | BBX 3 5 0 0 1576 | BITMAP 1577 | E0 1578 | 00 1579 | E0 1580 | C0 1581 | E0 1582 | ENDCHAR 1583 | STARTCHAR Edieresis 1584 | ENCODING 203 1585 | SWIDTH 1000 0 1586 | DWIDTH 4 0 1587 | BBX 3 5 0 0 1588 | BITMAP 1589 | A0 1590 | 00 1591 | E0 1592 | C0 1593 | E0 1594 | ENDCHAR 1595 | STARTCHAR Igrave 1596 | ENCODING 204 1597 | SWIDTH 1000 0 1598 | DWIDTH 4 0 1599 | BBX 3 5 0 0 1600 | BITMAP 1601 | 40 1602 | 20 1603 | E0 1604 | 40 1605 | E0 1606 | ENDCHAR 1607 | STARTCHAR Iacute 1608 | ENCODING 205 1609 | SWIDTH 1000 0 1610 | DWIDTH 4 0 1611 | BBX 3 5 0 0 1612 | BITMAP 1613 | 40 1614 | 80 1615 | E0 1616 | 40 1617 | E0 1618 | ENDCHAR 1619 | STARTCHAR Icircumflex 1620 | ENCODING 206 1621 | SWIDTH 1000 0 1622 | DWIDTH 4 0 1623 | BBX 3 5 0 0 1624 | BITMAP 1625 | E0 1626 | 00 1627 | E0 1628 | 40 1629 | E0 1630 | ENDCHAR 1631 | STARTCHAR Idieresis 1632 | ENCODING 207 1633 | SWIDTH 1000 0 1634 | DWIDTH 4 0 1635 | BBX 3 5 0 0 1636 | BITMAP 1637 | A0 1638 | 00 1639 | E0 1640 | 40 1641 | E0 1642 | ENDCHAR 1643 | STARTCHAR Eth 1644 | ENCODING 208 1645 | SWIDTH 1000 0 1646 | DWIDTH 4 0 1647 | BBX 3 5 0 0 1648 | BITMAP 1649 | C0 1650 | A0 1651 | E0 1652 | A0 1653 | C0 1654 | ENDCHAR 1655 | STARTCHAR Ntilde 1656 | ENCODING 209 1657 | SWIDTH 1000 0 1658 | DWIDTH 4 0 1659 | BBX 3 5 0 0 1660 | BITMAP 1661 | C0 1662 | 60 1663 | A0 1664 | E0 1665 | A0 1666 | ENDCHAR 1667 | STARTCHAR Ograve 1668 | ENCODING 210 1669 | SWIDTH 1000 0 1670 | DWIDTH 4 0 1671 | BBX 3 5 0 0 1672 | BITMAP 1673 | 40 1674 | 20 1675 | E0 1676 | A0 1677 | E0 1678 | ENDCHAR 1679 | STARTCHAR Oacute 1680 | ENCODING 211 1681 | SWIDTH 1000 0 1682 | DWIDTH 4 0 1683 | BBX 3 5 0 0 1684 | BITMAP 1685 | 40 1686 | 80 1687 | E0 1688 | A0 1689 | E0 1690 | ENDCHAR 1691 | STARTCHAR Ocircumflex 1692 | ENCODING 212 1693 | SWIDTH 1000 0 1694 | DWIDTH 4 0 1695 | BBX 3 5 0 0 1696 | BITMAP 1697 | E0 1698 | 00 1699 | E0 1700 | A0 1701 | E0 1702 | ENDCHAR 1703 | STARTCHAR Otilde 1704 | ENCODING 213 1705 | SWIDTH 1000 0 1706 | DWIDTH 4 0 1707 | BBX 3 5 0 0 1708 | BITMAP 1709 | C0 1710 | 60 1711 | E0 1712 | A0 1713 | E0 1714 | ENDCHAR 1715 | STARTCHAR Odieresis 1716 | ENCODING 214 1717 | SWIDTH 1000 0 1718 | DWIDTH 4 0 1719 | BBX 3 5 0 0 1720 | BITMAP 1721 | A0 1722 | 00 1723 | E0 1724 | A0 1725 | E0 1726 | ENDCHAR 1727 | STARTCHAR multiply 1728 | ENCODING 215 1729 | SWIDTH 1000 0 1730 | DWIDTH 4 0 1731 | BBX 3 3 0 1 1732 | BITMAP 1733 | A0 1734 | 40 1735 | A0 1736 | ENDCHAR 1737 | STARTCHAR Oslash 1738 | ENCODING 216 1739 | SWIDTH 1000 0 1740 | DWIDTH 4 0 1741 | BBX 3 5 0 0 1742 | BITMAP 1743 | 60 1744 | A0 1745 | E0 1746 | A0 1747 | C0 1748 | ENDCHAR 1749 | STARTCHAR Ugrave 1750 | ENCODING 217 1751 | SWIDTH 1000 0 1752 | DWIDTH 4 0 1753 | BBX 3 5 0 0 1754 | BITMAP 1755 | 80 1756 | 40 1757 | A0 1758 | A0 1759 | E0 1760 | ENDCHAR 1761 | STARTCHAR Uacute 1762 | ENCODING 218 1763 | SWIDTH 1000 0 1764 | DWIDTH 4 0 1765 | BBX 3 5 0 0 1766 | BITMAP 1767 | 20 1768 | 40 1769 | A0 1770 | A0 1771 | E0 1772 | ENDCHAR 1773 | STARTCHAR Ucircumflex 1774 | ENCODING 219 1775 | SWIDTH 1000 0 1776 | DWIDTH 4 0 1777 | BBX 3 5 0 0 1778 | BITMAP 1779 | E0 1780 | 00 1781 | A0 1782 | A0 1783 | E0 1784 | ENDCHAR 1785 | STARTCHAR Udieresis 1786 | ENCODING 220 1787 | SWIDTH 1000 0 1788 | DWIDTH 4 0 1789 | BBX 3 5 0 0 1790 | BITMAP 1791 | A0 1792 | 00 1793 | A0 1794 | A0 1795 | E0 1796 | ENDCHAR 1797 | STARTCHAR Yacute 1798 | ENCODING 221 1799 | SWIDTH 1000 0 1800 | DWIDTH 4 0 1801 | BBX 3 5 0 0 1802 | BITMAP 1803 | 20 1804 | 40 1805 | A0 1806 | E0 1807 | 40 1808 | ENDCHAR 1809 | STARTCHAR Thorn 1810 | ENCODING 222 1811 | SWIDTH 1000 0 1812 | DWIDTH 4 0 1813 | BBX 3 5 0 0 1814 | BITMAP 1815 | 80 1816 | E0 1817 | A0 1818 | E0 1819 | 80 1820 | ENDCHAR 1821 | STARTCHAR germandbls 1822 | ENCODING 223 1823 | SWIDTH 1000 0 1824 | DWIDTH 4 0 1825 | BBX 3 6 0 -1 1826 | BITMAP 1827 | 60 1828 | A0 1829 | C0 1830 | A0 1831 | C0 1832 | 80 1833 | ENDCHAR 1834 | STARTCHAR agrave 1835 | ENCODING 224 1836 | SWIDTH 1000 0 1837 | DWIDTH 4 0 1838 | BBX 3 5 0 0 1839 | BITMAP 1840 | 40 1841 | 20 1842 | 60 1843 | A0 1844 | E0 1845 | ENDCHAR 1846 | STARTCHAR aacute 1847 | ENCODING 225 1848 | SWIDTH 1000 0 1849 | DWIDTH 4 0 1850 | BBX 3 5 0 0 1851 | BITMAP 1852 | 40 1853 | 80 1854 | 60 1855 | A0 1856 | E0 1857 | ENDCHAR 1858 | STARTCHAR acircumflex 1859 | ENCODING 226 1860 | SWIDTH 1000 0 1861 | DWIDTH 4 0 1862 | BBX 3 5 0 0 1863 | BITMAP 1864 | E0 1865 | 00 1866 | 60 1867 | A0 1868 | E0 1869 | ENDCHAR 1870 | STARTCHAR atilde 1871 | ENCODING 227 1872 | SWIDTH 1000 0 1873 | DWIDTH 4 0 1874 | BBX 3 5 0 0 1875 | BITMAP 1876 | 60 1877 | C0 1878 | 60 1879 | A0 1880 | E0 1881 | ENDCHAR 1882 | STARTCHAR adieresis 1883 | ENCODING 228 1884 | SWIDTH 1000 0 1885 | DWIDTH 4 0 1886 | BBX 3 5 0 0 1887 | BITMAP 1888 | A0 1889 | 00 1890 | 60 1891 | A0 1892 | E0 1893 | ENDCHAR 1894 | STARTCHAR aring 1895 | ENCODING 229 1896 | SWIDTH 1000 0 1897 | DWIDTH 4 0 1898 | BBX 3 5 0 0 1899 | BITMAP 1900 | 60 1901 | 60 1902 | 60 1903 | A0 1904 | E0 1905 | ENDCHAR 1906 | STARTCHAR ae 1907 | ENCODING 230 1908 | SWIDTH 1000 0 1909 | DWIDTH 4 0 1910 | BBX 3 4 0 0 1911 | BITMAP 1912 | 60 1913 | E0 1914 | E0 1915 | C0 1916 | ENDCHAR 1917 | STARTCHAR ccedilla 1918 | ENCODING 231 1919 | SWIDTH 1000 0 1920 | DWIDTH 4 0 1921 | BBX 3 5 0 -1 1922 | BITMAP 1923 | 60 1924 | 80 1925 | 60 1926 | 20 1927 | 40 1928 | ENDCHAR 1929 | STARTCHAR egrave 1930 | ENCODING 232 1931 | SWIDTH 1000 0 1932 | DWIDTH 4 0 1933 | BBX 3 5 0 0 1934 | BITMAP 1935 | 40 1936 | 20 1937 | 60 1938 | E0 1939 | 60 1940 | ENDCHAR 1941 | STARTCHAR eacute 1942 | ENCODING 233 1943 | SWIDTH 1000 0 1944 | DWIDTH 4 0 1945 | BBX 3 5 0 0 1946 | BITMAP 1947 | 40 1948 | 80 1949 | 60 1950 | E0 1951 | 60 1952 | ENDCHAR 1953 | STARTCHAR ecircumflex 1954 | ENCODING 234 1955 | SWIDTH 1000 0 1956 | DWIDTH 4 0 1957 | BBX 3 5 0 0 1958 | BITMAP 1959 | E0 1960 | 00 1961 | 60 1962 | E0 1963 | 60 1964 | ENDCHAR 1965 | STARTCHAR edieresis 1966 | ENCODING 235 1967 | SWIDTH 1000 0 1968 | DWIDTH 4 0 1969 | BBX 3 5 0 0 1970 | BITMAP 1971 | A0 1972 | 00 1973 | 60 1974 | E0 1975 | 60 1976 | ENDCHAR 1977 | STARTCHAR igrave 1978 | ENCODING 236 1979 | SWIDTH 1000 0 1980 | DWIDTH 4 0 1981 | BBX 2 5 1 0 1982 | BITMAP 1983 | 80 1984 | 40 1985 | 80 1986 | 80 1987 | 80 1988 | ENDCHAR 1989 | STARTCHAR iacute 1990 | ENCODING 237 1991 | SWIDTH 1000 0 1992 | DWIDTH 4 0 1993 | BBX 2 5 0 0 1994 | BITMAP 1995 | 40 1996 | 80 1997 | 40 1998 | 40 1999 | 40 2000 | ENDCHAR 2001 | STARTCHAR icircumflex 2002 | ENCODING 238 2003 | SWIDTH 1000 0 2004 | DWIDTH 4 0 2005 | BBX 3 5 0 0 2006 | BITMAP 2007 | E0 2008 | 00 2009 | 40 2010 | 40 2011 | 40 2012 | ENDCHAR 2013 | STARTCHAR idieresis 2014 | ENCODING 239 2015 | SWIDTH 1000 0 2016 | DWIDTH 4 0 2017 | BBX 3 5 0 0 2018 | BITMAP 2019 | A0 2020 | 00 2021 | 40 2022 | 40 2023 | 40 2024 | ENDCHAR 2025 | STARTCHAR eth 2026 | ENCODING 240 2027 | SWIDTH 1000 0 2028 | DWIDTH 4 0 2029 | BBX 3 5 0 0 2030 | BITMAP 2031 | 60 2032 | C0 2033 | 60 2034 | A0 2035 | 60 2036 | ENDCHAR 2037 | STARTCHAR ntilde 2038 | ENCODING 241 2039 | SWIDTH 1000 0 2040 | DWIDTH 4 0 2041 | BBX 3 5 0 0 2042 | BITMAP 2043 | C0 2044 | 60 2045 | C0 2046 | A0 2047 | A0 2048 | ENDCHAR 2049 | STARTCHAR ograve 2050 | ENCODING 242 2051 | SWIDTH 1000 0 2052 | DWIDTH 4 0 2053 | BBX 3 5 0 0 2054 | BITMAP 2055 | 40 2056 | 20 2057 | 40 2058 | A0 2059 | 40 2060 | ENDCHAR 2061 | STARTCHAR oacute 2062 | ENCODING 243 2063 | SWIDTH 1000 0 2064 | DWIDTH 4 0 2065 | BBX 3 5 0 0 2066 | BITMAP 2067 | 40 2068 | 80 2069 | 40 2070 | A0 2071 | 40 2072 | ENDCHAR 2073 | STARTCHAR ocircumflex 2074 | ENCODING 244 2075 | SWIDTH 1000 0 2076 | DWIDTH 4 0 2077 | BBX 3 5 0 0 2078 | BITMAP 2079 | E0 2080 | 00 2081 | 40 2082 | A0 2083 | 40 2084 | ENDCHAR 2085 | STARTCHAR otilde 2086 | ENCODING 245 2087 | SWIDTH 1000 0 2088 | DWIDTH 4 0 2089 | BBX 3 5 0 0 2090 | BITMAP 2091 | C0 2092 | 60 2093 | 40 2094 | A0 2095 | 40 2096 | ENDCHAR 2097 | STARTCHAR odieresis 2098 | ENCODING 246 2099 | SWIDTH 1000 0 2100 | DWIDTH 4 0 2101 | BBX 3 5 0 0 2102 | BITMAP 2103 | A0 2104 | 00 2105 | 40 2106 | A0 2107 | 40 2108 | ENDCHAR 2109 | STARTCHAR divide 2110 | ENCODING 247 2111 | SWIDTH 1000 0 2112 | DWIDTH 4 0 2113 | BBX 3 5 0 0 2114 | BITMAP 2115 | 40 2116 | 00 2117 | E0 2118 | 00 2119 | 40 2120 | ENDCHAR 2121 | STARTCHAR oslash 2122 | ENCODING 248 2123 | SWIDTH 1000 0 2124 | DWIDTH 4 0 2125 | BBX 3 4 0 0 2126 | BITMAP 2127 | 60 2128 | E0 2129 | A0 2130 | C0 2131 | ENDCHAR 2132 | STARTCHAR ugrave 2133 | ENCODING 249 2134 | SWIDTH 1000 0 2135 | DWIDTH 4 0 2136 | BBX 3 5 0 0 2137 | BITMAP 2138 | 80 2139 | 40 2140 | A0 2141 | A0 2142 | 60 2143 | ENDCHAR 2144 | STARTCHAR uacute 2145 | ENCODING 250 2146 | SWIDTH 1000 0 2147 | DWIDTH 4 0 2148 | BBX 3 5 0 0 2149 | BITMAP 2150 | 20 2151 | 40 2152 | A0 2153 | A0 2154 | 60 2155 | ENDCHAR 2156 | STARTCHAR ucircumflex 2157 | ENCODING 251 2158 | SWIDTH 1000 0 2159 | DWIDTH 4 0 2160 | BBX 3 5 0 0 2161 | BITMAP 2162 | E0 2163 | 00 2164 | A0 2165 | A0 2166 | 60 2167 | ENDCHAR 2168 | STARTCHAR udieresis 2169 | ENCODING 252 2170 | SWIDTH 1000 0 2171 | DWIDTH 4 0 2172 | BBX 3 5 0 0 2173 | BITMAP 2174 | A0 2175 | 00 2176 | A0 2177 | A0 2178 | 60 2179 | ENDCHAR 2180 | STARTCHAR yacute 2181 | ENCODING 253 2182 | SWIDTH 1000 0 2183 | DWIDTH 4 0 2184 | BBX 3 6 0 -1 2185 | BITMAP 2186 | 20 2187 | 40 2188 | A0 2189 | 60 2190 | 20 2191 | 40 2192 | ENDCHAR 2193 | STARTCHAR thorn 2194 | ENCODING 254 2195 | SWIDTH 1000 0 2196 | DWIDTH 4 0 2197 | BBX 3 5 0 -1 2198 | BITMAP 2199 | 80 2200 | C0 2201 | A0 2202 | C0 2203 | 80 2204 | ENDCHAR 2205 | STARTCHAR ydieresis 2206 | ENCODING 255 2207 | SWIDTH 1000 0 2208 | DWIDTH 4 0 2209 | BBX 3 6 0 -1 2210 | BITMAP 2211 | A0 2212 | 00 2213 | A0 2214 | 60 2215 | 20 2216 | 40 2217 | ENDCHAR 2218 | STARTCHAR gcircumflex 2219 | ENCODING 285 2220 | SWIDTH 1000 0 2221 | DWIDTH 6 0 2222 | BBX 1 1 0 0 2223 | BITMAP 2224 | 00 2225 | ENDCHAR 2226 | STARTCHAR OE 2227 | ENCODING 338 2228 | SWIDTH 666 0 2229 | DWIDTH 4 0 2230 | BBX 3 5 0 0 2231 | BITMAP 2232 | 60 2233 | C0 2234 | E0 2235 | C0 2236 | 60 2237 | ENDCHAR 2238 | STARTCHAR oe 2239 | ENCODING 339 2240 | SWIDTH 666 0 2241 | DWIDTH 4 0 2242 | BBX 3 4 0 0 2243 | BITMAP 2244 | 60 2245 | E0 2246 | C0 2247 | E0 2248 | ENDCHAR 2249 | STARTCHAR Scaron 2250 | ENCODING 352 2251 | SWIDTH 666 0 2252 | DWIDTH 4 0 2253 | BBX 3 5 0 0 2254 | BITMAP 2255 | A0 2256 | 60 2257 | C0 2258 | 60 2259 | C0 2260 | ENDCHAR 2261 | STARTCHAR scaron 2262 | ENCODING 353 2263 | SWIDTH 666 0 2264 | DWIDTH 4 0 2265 | BBX 3 5 0 0 2266 | BITMAP 2267 | A0 2268 | 60 2269 | C0 2270 | 60 2271 | C0 2272 | ENDCHAR 2273 | STARTCHAR Ydieresis 2274 | ENCODING 376 2275 | SWIDTH 666 0 2276 | DWIDTH 4 0 2277 | BBX 3 5 0 0 2278 | BITMAP 2279 | A0 2280 | 00 2281 | A0 2282 | 40 2283 | 40 2284 | ENDCHAR 2285 | STARTCHAR Zcaron 2286 | ENCODING 381 2287 | SWIDTH 666 0 2288 | DWIDTH 4 0 2289 | BBX 3 5 0 0 2290 | BITMAP 2291 | A0 2292 | E0 2293 | 60 2294 | C0 2295 | E0 2296 | ENDCHAR 2297 | STARTCHAR zcaron 2298 | ENCODING 382 2299 | SWIDTH 666 0 2300 | DWIDTH 4 0 2301 | BBX 3 5 0 0 2302 | BITMAP 2303 | A0 2304 | E0 2305 | 60 2306 | C0 2307 | E0 2308 | ENDCHAR 2309 | STARTCHAR uni0EA4 2310 | ENCODING 3748 2311 | SWIDTH 1000 0 2312 | DWIDTH 6 0 2313 | BBX 1 1 0 0 2314 | BITMAP 2315 | 00 2316 | ENDCHAR 2317 | STARTCHAR uni13A0 2318 | ENCODING 5024 2319 | SWIDTH 1000 0 2320 | DWIDTH 6 0 2321 | BBX 1 1 0 0 2322 | BITMAP 2323 | 00 2324 | ENDCHAR 2325 | STARTCHAR bullet 2326 | ENCODING 8226 2327 | SWIDTH 666 0 2328 | DWIDTH 4 0 2329 | BBX 1 1 1 2 2330 | BITMAP 2331 | 80 2332 | ENDCHAR 2333 | STARTCHAR ellipsis 2334 | ENCODING 8230 2335 | SWIDTH 666 0 2336 | DWIDTH 4 0 2337 | BBX 3 1 0 0 2338 | BITMAP 2339 | A0 2340 | ENDCHAR 2341 | STARTCHAR Euro 2342 | ENCODING 8364 2343 | SWIDTH 666 0 2344 | DWIDTH 4 0 2345 | BBX 3 5 0 0 2346 | BITMAP 2347 | 60 2348 | E0 2349 | E0 2350 | C0 2351 | 60 2352 | ENDCHAR 2353 | STARTCHAR uniFFFD 2354 | ENCODING 65533 2355 | SWIDTH 1000 0 2356 | DWIDTH 4 0 2357 | BBX 3 5 0 0 2358 | BITMAP 2359 | E0 2360 | A0 2361 | A0 2362 | A0 2363 | E0 2364 | ENDCHAR 2365 | ENDFONT 2366 | --------------------------------------------------------------------------------