new ip
This commit is contained in:
1 parent
aa14c170c8
commit
09b4990d6d
5404 files changed
+1184888
-3
No files matched your search
@@ -0,0 +1,583 @@
|
||||
import base64
|
||||
import json
|
||||
import tarfile
|
||||
from io import BytesIO
|
||||
from itertools import chain
|
||||
|
||||
try:
|
||||
from statistics import multimode as statistics_multimode
|
||||
from statistics import median as statistics_median
|
||||
except ImportError:
|
||||
# Python 2.7 mockup for statistics
|
||||
def statistics_multimode(data):
|
||||
return data
|
||||
|
||||
|
||||
def statistics_median(lst):
|
||||
n = len(lst)
|
||||
s = sorted(lst)
|
||||
return (sum(s[n // 2 - 1:n // 2 + 1]) / 2.0, s[n // 2])[n % 2] if n else None
|
||||
|
||||
try:
|
||||
from itertools import zip_longest
|
||||
except ImportError:
|
||||
from itertools import izip_longest as zip_longest
|
||||
|
||||
try:
|
||||
from string import split
|
||||
except ImportError:
|
||||
split = str.split
|
||||
|
||||
|
||||
class HersheyFonts(object):
|
||||
'''The Hershey Fonts:
|
||||
- are a set of more than 2000 glyph (symbol) descriptions in vector
|
||||
( <x,y> point-to-point ) format
|
||||
- can be grouped as almost 20 'occidental' (english, greek,
|
||||
cyrillic) fonts, 3 or more 'oriental' (Kanji, Hiragana,
|
||||
and Katakana) fonts, and a few hundred miscellaneous
|
||||
symbols (mathematical, musical, cartographic, etc etc)
|
||||
- are suitable for typographic quality output on a vector device
|
||||
(such as a plotter) when used at an appropriate scale.
|
||||
- were digitized by Dr. A. V. Hershey while working for the U.S.
|
||||
Government National Bureau of Standards (NBS).
|
||||
- are in the public domain, with a few caveats:
|
||||
- They are available from NTIS (National Technical Info.
|
||||
Service) in a computer-readable from which is *not*
|
||||
in the public domain. This format is described in
|
||||
a hardcopy publication "Tables of Coordinates for
|
||||
Hershey's Repertory of Occidental Type Fonts and
|
||||
Graphic Symbols" available from NTIS for less than
|
||||
$20 US (phone number +1 703 487 4763).
|
||||
- NTIS does not care about and doesn't want to know about
|
||||
what happens to Hershey Font data that is not
|
||||
distributed in their exact format.
|
||||
- This distribution is not in the NTIS format, and thus is
|
||||
only subject to the simple restriction described
|
||||
at the top of this file.
|
||||
|
||||
Hard Copy samples of the Hershey Fonts are best obtained by purchasing the
|
||||
book described above from NTIS. It contains a sample of all of the Occidental
|
||||
symbols (but none of the Oriental symbols).
|
||||
|
||||
This distribution:
|
||||
- contains
|
||||
* a complete copy of the Font data using the original
|
||||
glyph-numbering sequence
|
||||
* a set of translation tables that could be used to generate
|
||||
ASCII-sequence fonts in various typestyles
|
||||
* a couple of sample programs in C and Fortran that are
|
||||
capable of parsing the font data and displaying it
|
||||
on a graphic device (we recommend that if you
|
||||
wish to write programs using the fonts, you should
|
||||
hack up one of these until it works on your system)
|
||||
|
||||
- consists of the following files...
|
||||
hershey.doc - details of the font data format, typestyles and
|
||||
symbols included, etc.
|
||||
hersh.oc[1-4] - The Occidental font data (these files can
|
||||
be catenated into one large database)
|
||||
hersh.or[1-4] - The Oriental font data (likewise here)
|
||||
*.hmp - Occidental font map files. Each file is a translation
|
||||
table from Hershey glyph numbers to ASCII
|
||||
sequence for a particular typestyle.
|
||||
hershey.f77 - A fortran program that reads and displays all
|
||||
of the glyphs in a Hershey font file.
|
||||
hershey.c - The same, in C, using GKS, for MS-DOS and the
|
||||
PC-Color Graphics Adaptor.
|
||||
|
||||
Additional Work To Be Done (volunteers welcome!):
|
||||
|
||||
- Integrate this complete set of data with the hershey font typesetting
|
||||
program recently distributed to mod.sources
|
||||
- Come up with an integrated data structure and supporting routines
|
||||
that make use of the ASCII translation tables
|
||||
- Digitize additional characters for the few places where non-ideal
|
||||
symbol substitutions were made in the ASCII translation tables.
|
||||
- Make a version of the demo program (hershey.c or hershey.f77) that
|
||||
uses the standard Un*x plot routines.
|
||||
- Write a banner-style program using Hershey Fonts for input and
|
||||
non-graphic terminals or printers for output.
|
||||
- Anything else you'd like!
|
||||
|
||||
This file provides a brief description of the contents of the Occidental
|
||||
Hershey Font Files. For a complete listing of the fonts in hard copy, order
|
||||
NBS Special Publication 424, "A contribution to computer typesetting
|
||||
techniques: Tables of Coordinates for Hershey's Repertory of Occidental
|
||||
Type Fonts and Graphic Symbols". You can get it from NTIS (phone number is
|
||||
+1 703 487 4763) for less than twenty dollars US.
|
||||
|
||||
Basic Glyph (symbol) data:
|
||||
|
||||
hersh.oc1 - numbers 1 to 1199
|
||||
hersh.oc2 - numbers 1200 to 2499
|
||||
hersh.oc3 - numbers 2500 to 3199
|
||||
hersh.oc4 - numbers 3200 to 3999
|
||||
|
||||
These four files contain approximately 19 different fonts in
|
||||
the A-Z alphabet plus greek and cyrillic, along with hundreds of special
|
||||
symbols, described generically below.
|
||||
|
||||
There are also four files of Oriental fonts (hersh.or[1-4]). These
|
||||
files contain symbols from three Japanese alphabets (Kanji, Hiragana, and
|
||||
Katakana). It is unknown what other symbols may be contained therein, nor
|
||||
is it known what order the symbols are in (I don't know Japanese!).
|
||||
|
||||
Back to the Occidental files:
|
||||
|
||||
Fonts:
|
||||
Roman: Plain, Simplex, Duplex, Complex Small, Complex, Triplex
|
||||
Italic: Complex Small, Complex, Triplex
|
||||
Script: Simplex, Complex
|
||||
Gothic: German, English, Italian
|
||||
Greek: Plain, Simplex, Complex Small, Complex
|
||||
Cyrillic: Complex
|
||||
|
||||
Symbols:
|
||||
Mathematical (227-229,232,727-779,732,737-740,1227-1270,2227-2270,
|
||||
1294-1412,2294-2295,2401-2412)
|
||||
Daggers (for footnotes, etc) (1276-1279, 2276-2279)
|
||||
Astronomical (1281-1293,2281-2293)
|
||||
Astrological (2301-2312)
|
||||
Musical (2317-2382)
|
||||
Typesetting (ffl,fl,fi sorts of things) (miscellaneous places)
|
||||
Miscellaneous (mostly in 741-909, but also elsewhere):
|
||||
- Playing card suits
|
||||
- Meteorology
|
||||
- Graphics (lines, curves)
|
||||
- Electrical
|
||||
- Geometric (shapes)
|
||||
- Cartographic
|
||||
- Naval
|
||||
- Agricultural
|
||||
- Highways
|
||||
- Etc...
|
||||
|
||||
ASCII sequence translation files:
|
||||
|
||||
The Hershey glyphs, while in a particular order, are not in an
|
||||
ASCII sequence. I have provided translation files that give the
|
||||
sequence of glyph numbers that will most closely approximate the
|
||||
ASCII printing sequence (from space through ~, with the degree
|
||||
circle tacked on at the end) for each of the above fonts:
|
||||
|
||||
File names are made up of fffffftt.hmp,
|
||||
|
||||
where ffffff is the font style, one of:
|
||||
roman Roman
|
||||
greek Greek
|
||||
italic Italic
|
||||
script Script
|
||||
cyril Cyrillic (some characters not placed in
|
||||
the ASCII sequence)
|
||||
gothgr Gothic German
|
||||
gothgb Gothic English
|
||||
gothit Gothic Italian
|
||||
|
||||
and tt is the font type, one of:
|
||||
p Plain (very small, no lower case)
|
||||
s Simplex (plain, normal size, no serifs)
|
||||
d Duplex (normal size, no serifs, doubled lines)
|
||||
c Complex (normal size, serifs, doubled lines)
|
||||
t Triplex (normal size, serifs, tripled lines)
|
||||
cs Complex Small (Complex, smaller than normal size)
|
||||
|
||||
The three sizes are coded with particular base line (bottom of a capital
|
||||
letter) and cap line (top of a capital letter) values for 'y':
|
||||
|
||||
Size Base Line Cap Line
|
||||
|
||||
Very Small -5 +4
|
||||
Small -6 +7
|
||||
Normal -9 +12
|
||||
|
||||
(Note: some glyphs in the 'Very Small' fonts are actually 'Small')
|
||||
|
||||
The top line and bottom line, which are normally used to define vertical
|
||||
spacing, are not given. Maybe somebody can determine appropriate
|
||||
values for these!
|
||||
|
||||
The left line and right line, which are used to define horizontal spacing,
|
||||
are provided with each character in the database.
|
||||
|
||||
Format of Hershey glyphs:
|
||||
|
||||
5 bytes - glyphnumber
|
||||
3 bytes - length of data length in 16-bit words including left&right numbers
|
||||
1 byte - x value of left margin
|
||||
1 byte - x value of right margin
|
||||
(length*2)-2 bytes - stroke data
|
||||
|
||||
left&right margins and stroke data are biased by the value of the letter 'R'
|
||||
Subtract the letter 'R' to get the data.
|
||||
|
||||
e.g. if the data byte is 'R', the data is 0
|
||||
if the data byte is 'T', the data is +2
|
||||
if the data byte is 'J', the data is -8
|
||||
|
||||
and so on...
|
||||
|
||||
The coordinate system is x-y, with the origin (0,0) in the center of the
|
||||
glyph. X increases to the right and y increases *down*.
|
||||
|
||||
The stroke data is pairs of bytes, one byte for x followed by one byte for y.
|
||||
|
||||
A ' R' in the stroke data indicates a 'lift pen and move' instruction.'''
|
||||
__compressed_fonts_base64 = B'''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 truncated
|
||||
|
||||
class _objdict(dict):
|
||||
def __getattr__(self, name):
|
||||
if name in self:
|
||||
return self[name]
|
||||
else:
|
||||
raise AttributeError('No such attribute: ' + name)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if name in self:
|
||||
self[name] = value
|
||||
else:
|
||||
raise AttributeError('No such attribute: ' + name)
|
||||
|
||||
class _rednderopts(_objdict):
|
||||
@property
|
||||
def cap_line(self):
|
||||
return self['cap_line'] * self.scaley
|
||||
|
||||
@property
|
||||
def bottom_line(self):
|
||||
return self['bottom_line'] * self.scaley
|
||||
|
||||
@property
|
||||
def base_line(self):
|
||||
return self['base_line'] * self.scaley
|
||||
|
||||
class _HersheyRenderIterator(object):
|
||||
def __init__(self, glyphs, text=None):
|
||||
self.__text = text or ''
|
||||
if not isinstance(glyphs, dict):
|
||||
raise TypeError('glyphs parameter has to be a dictionary')
|
||||
self.__glyphs = glyphs
|
||||
|
||||
def text_glyphs(self, text=None):
|
||||
text = text or self.__text or ''
|
||||
for current_char in text:
|
||||
if current_char in self.__glyphs:
|
||||
the_glyph = self.__glyphs[current_char]
|
||||
if isinstance(the_glyph, _HersheyGlyph):
|
||||
yield the_glyph
|
||||
|
||||
def text_strokes(self, text=None, xofs=0, yofs=0, scalex=1, scaley=1, spacing=0, **kwargs):
|
||||
for glyph in self.text_glyphs(text=text):
|
||||
for stroke in glyph.strokes:
|
||||
yield [(xofs + (x - glyph.left_offset) * scalex, yofs + y * scaley) for x, y in stroke]
|
||||
xofs += spacing + scalex * glyph.char_width
|
||||
|
||||
def __init__(self, load_from_data_iterator='', load_default_font=None):
|
||||
self.__glyphs = {}
|
||||
self.__default_font_names_list = None
|
||||
self.__font_params = self._rednderopts({'xofs': 0, 'yofs': 0, 'scalex': 1, 'scaley': 1, 'spacing': 0, 'cap_line': -12, 'base_line': 9, 'bottom_line': 16})
|
||||
if load_default_font is not None:
|
||||
self.load_default_font(load_default_font)
|
||||
else:
|
||||
self.read_from_string_lines(data_iterator=load_from_data_iterator)
|
||||
|
||||
@property
|
||||
def render_options(self):
|
||||
'''xofs=0, yofs=0,
|
||||
scalex=1, scaley=1,
|
||||
spacing=0,
|
||||
cap_line=-12, base_line= 9, bottom_line= 16'''
|
||||
return self.__font_params
|
||||
|
||||
@property
|
||||
def all_glyphs(self):
|
||||
'''Get all Glyphs stored for currently loaded font. ={} if no font loaded'''
|
||||
return dict(self.__glyphs)
|
||||
|
||||
@render_options.setter
|
||||
def render_options(self, newdim):
|
||||
'''xofs=0, yofs=0,
|
||||
scalex=1, scaley=1,
|
||||
spacing=0,
|
||||
cap_line=-12, base_line= 9, bottom_line= 16'''
|
||||
if newdim.issubset(self.render_options.keys()):
|
||||
self.render_options.update(newdim)
|
||||
else:
|
||||
raise AttributeError('Unable to set unknown parameters')
|
||||
|
||||
@property
|
||||
def default_font_names(self):
|
||||
'''Get the list of built-in fonts'''
|
||||
if not self.__default_font_names_list:
|
||||
with BytesIO(self.__get_compressed_font_bytes()) as compressed_file_stream:
|
||||
with tarfile.open(fileobj=compressed_file_stream, mode='r', ) as ftar:
|
||||
self.__default_font_names_list = list(map(lambda tar_member: tar_member.name, ftar.getmembers()))
|
||||
del ftar
|
||||
del compressed_file_stream
|
||||
return list(self.__default_font_names_list)
|
||||
|
||||
def __get_compressed_font_bytes(self):
|
||||
for enc in ('64', '85', '32', '16'):
|
||||
if hasattr(self, '_HersheyFonts__compressed_fonts_base' + enc):
|
||||
if hasattr(base64, 'b' + enc + 'decode'):
|
||||
decoded = getattr(base64, 'b' + enc + 'decode')(getattr(self, '_HersheyFonts__compressed_fonts_base' + enc))
|
||||
return bytes(decoded)
|
||||
raise NotImplementedError('base' + enc + ' encoding not supported on this platform.')
|
||||
|
||||
def normalize_rendering(self, factor=1.0):
|
||||
'''Set rendering options to output text lines in upright direction, size set to "factor"'''
|
||||
scale_factor = float(factor) / (self.render_options['bottom_line'] - self.render_options['cap_line'])
|
||||
self.render_options.scaley = -scale_factor
|
||||
self.render_options.scalex = scale_factor
|
||||
self.render_options.yofs = self.render_options['bottom_line'] * scale_factor
|
||||
self.render_options.xofs = 0
|
||||
|
||||
def load_default_font(self, default_font_name=''):
|
||||
'''load built-in font by name. If default_font_name not specified, selects the predefined default font. The routine is returning the name of the loaded font.'''
|
||||
if not default_font_name:
|
||||
default_font_name = self.default_font_names[0]
|
||||
if default_font_name in self.default_font_names:
|
||||
with BytesIO(self.__get_compressed_font_bytes()) as compressed_file_stream:
|
||||
with tarfile.open(fileobj=compressed_file_stream, mode='r', ) as ftar:
|
||||
tarmember = ftar.extractfile(default_font_name)
|
||||
self.read_from_string_lines(tarmember)
|
||||
return default_font_name
|
||||
raise ValueError('"{0}" font not found.'.format(default_font_name))
|
||||
|
||||
def load_font_file(self, file_name):
|
||||
'''load font from external file'''
|
||||
with open(file_name, 'r') as fin:
|
||||
self.read_from_string_lines(fin)
|
||||
|
||||
def read_from_string_lines(self, data_iterator=None, first_glyph_ascii_code=32, use_charcode=False, merge_existing=False):
|
||||
'''Read font from iterable list of strings
|
||||
Parameters:
|
||||
- data_iterator : string list or empty to clear current font data
|
||||
- use_charcode : if True use the font embedded charcode parameter for glyph storage
|
||||
- first_glyph_ascii_code : if use_charcode is False, use this ASCII code for the first character in font line
|
||||
- merge_existing : if True merge the glyphs from data_iterator to the current font
|
||||
'''
|
||||
glyph_ascii_code = first_glyph_ascii_code
|
||||
cap = []
|
||||
base = []
|
||||
bottom = []
|
||||
cap_line = None
|
||||
base_line = None
|
||||
bottom_line = None
|
||||
aglyph = None
|
||||
if not merge_existing:
|
||||
self.__glyphs = {}
|
||||
if data_iterator:
|
||||
for line in data_iterator or '':
|
||||
if isinstance(line, str) and hasattr(line, 'decode'):
|
||||
line = line.decode()
|
||||
elif isinstance(line, bytes) and hasattr(line, 'decode'):
|
||||
line = line.decode('utf-8')
|
||||
if line[0] == '#':
|
||||
extraparams = json.loads(line[1:])
|
||||
if 'define_cap_line' in extraparams:
|
||||
cap_line = extraparams['define_cap_line']
|
||||
if 'define_base_line' in extraparams:
|
||||
base_line = extraparams['define_base_line']
|
||||
if 'define_bottom_line' in extraparams:
|
||||
bottom_line = extraparams['define_bottom_line']
|
||||
if aglyph:
|
||||
aglyph.parse_string_line(line)
|
||||
else:
|
||||
aglyph = _HersheyGlyph(data_line=line, default_base_line=base_line, default_bottom_line=bottom_line, default_cap_line=cap_line)
|
||||
if line[0] != '#':
|
||||
glyph_key = chr(aglyph.font_charcode if use_charcode else glyph_ascii_code)
|
||||
self.__glyphs[glyph_key] = aglyph
|
||||
cap.append(aglyph.cap_line)
|
||||
base.append(aglyph.base_line)
|
||||
bottom.append(aglyph.bottom_line)
|
||||
aglyph = None
|
||||
glyph_ascii_code += 1
|
||||
caps = statistics_multimode(cap)
|
||||
bases = statistics_multimode(base)
|
||||
bottoms = statistics_multimode(bottom)
|
||||
self.render_options.cap_line = statistics_median(caps) if cap_line is None else cap_line
|
||||
self.render_options.base_line = statistics_median(bases) if base_line is None else base_line
|
||||
self.render_options.bottom_line = statistics_median(bottoms) if bottom_line is None else bottom_line
|
||||
|
||||
def glyphs_for_text(self, text):
|
||||
'''Return iterable list of glyphs for the given text'''
|
||||
return self._HersheyRenderIterator(self.__glyphs).text_glyphs(text=text)
|
||||
|
||||
def strokes_for_text(self, text):
|
||||
'''Return iterable list of continuous strokes (polygons) for all characters with pre calculated offsets for the given text.
|
||||
Strokes (polygons) are list of (x,y) coordinates.
|
||||
'''
|
||||
return self._HersheyRenderIterator(self.__glyphs).text_strokes(text=text, **self.__font_params)
|
||||
|
||||
def lines_for_text(self, text):
|
||||
'''Return iterable list of individual lines for all characters with pre calculated offsets for the given text.
|
||||
Lines are a list of ((x0,y0),(x1,y1)) coordinates.
|
||||
'''
|
||||
return chain.from_iterable(zip(stroke[::], stroke[1::]) for stroke in self._HersheyRenderIterator(self.__glyphs).text_strokes(text=text, **self.__font_params))
|
||||
|
||||
|
||||
class _HersheyGlyph(object):
|
||||
def __init__(self, data_line='', default_cap_line=None, default_base_line=None, default_bottom_line=None):
|
||||
self.__capline = default_cap_line
|
||||
self.__baseline = default_base_line
|
||||
self.__bottomline = default_bottom_line
|
||||
self.__charcode = -1
|
||||
self.__left_side = 0
|
||||
self.__right_side = 0
|
||||
self.__strokes = []
|
||||
self.__xmin = self.__xmax = self.__ymin = self.__ymax = 0
|
||||
self.parse_string_line(data_line=data_line)
|
||||
|
||||
@property
|
||||
def base_line(self):
|
||||
'''Return the base line of the glyph. e.g. Horizontal leg of letter L.
|
||||
The parameter might be in or outside of the bounding box for the glyph
|
||||
'''
|
||||
return 9 if self.__baseline is None else self.__baseline
|
||||
|
||||
@property
|
||||
def cap_line(self):
|
||||
'''Return the cap line of the glyph. e.g. Horizontal hat of letter T.
|
||||
The parameter might be in or outside of the bounding box for the glyph
|
||||
'''
|
||||
return -12 if self.__capline is None else self.__capline
|
||||
|
||||
@property
|
||||
def bottom_line(self):
|
||||
'''Return the bottom line of the glyph. e.g. Lowest point of letter j.
|
||||
The parameter might be in or outside of the bounding box for the glyph
|
||||
'''
|
||||
return 16 if self.__bottomline is None else self.__bottomline
|
||||
|
||||
@property
|
||||
def font_charcode(self):
|
||||
'''Get the Hershey charcode of this glyph.'''
|
||||
return self.__charcode
|
||||
|
||||
@property
|
||||
def left_offset(self):
|
||||
'''Get left side of the glyph. Can be different to bounding box.'''
|
||||
return self.__left_side
|
||||
|
||||
@property
|
||||
def strokes(self):
|
||||
'''Return iterable list of continuous strokes (polygons) for this glyph.
|
||||
Strokes (polygons) are list of (x,y) coordinates.
|
||||
'''
|
||||
return self.__strokes
|
||||
|
||||
@property
|
||||
def char_width(self):
|
||||
'''Return the width of this glyph. May be different to bounding box.'''
|
||||
return self.__right_side - self.__left_side
|
||||
|
||||
@property
|
||||
def draw_box(self):
|
||||
'''Return the graphical bounding box for this Glyph in format ((xmin,ymin),(xmax,ymax))'''
|
||||
return (self.__xmin, self.__ymin), (self.__xmax, self.__ymax)
|
||||
|
||||
@property
|
||||
def char_box(self):
|
||||
'''Return the typographical bounding box for this Glyph in format ((xmin,ymin),(xmax,ymax)).
|
||||
Can be different to bounding box.
|
||||
See draw_box property for rendering bounding box
|
||||
'''
|
||||
return (self.__left_side, self.__bottomline), (self.__right_side, self.__capline)
|
||||
|
||||
def __char2val(self, c): # data is stored as signed bytes relative to ASCII R
|
||||
return ord(c) - ord('R')
|
||||
|
||||
@property
|
||||
def lines(self):
|
||||
'''Return iterable list of individual lines for this Glyph.
|
||||
Lines are a list of ((x0,y0),(x1,y1)) coordinates.
|
||||
'''
|
||||
return chain.from_iterable(zip(stroke[::], stroke[1::]) for stroke in self.__strokes)
|
||||
|
||||
def parse_string_line(self, data_line):
|
||||
"""Interprets a line of Hershey font text """
|
||||
if data_line:
|
||||
data_line = data_line.rstrip()
|
||||
if data_line:
|
||||
if data_line[0] == '#':
|
||||
extraparams = json.loads(data_line[1:])
|
||||
if 'glyph_cap_line' in extraparams:
|
||||
self.__capline = extraparams['glyph_cap_line']
|
||||
if 'glyph_base_line' in extraparams:
|
||||
self.__baseline = extraparams['glyph_base_line']
|
||||
if 'glyph_bottom_line' in extraparams:
|
||||
self.__bottomline = extraparams['glyph_bottom_line']
|
||||
elif len(data_line) > 9:
|
||||
strokes = []
|
||||
xmin = xmax = ymin = ymax = None
|
||||
# individual strokes are stored separated by <space>+R
|
||||
# starting at col 11
|
||||
for s in split(data_line[10:], ' R'):
|
||||
if len(s):
|
||||
stroke = list(zip(map(self.__char2val, s[::2]), map(self.__char2val, s[1::2])))
|
||||
xmin = min(stroke + ([xmin] if xmin else []), key=lambda t: t[0])
|
||||
ymin = min(stroke + ([ymin] if ymin else []), key=lambda t: t[1])
|
||||
xmax = max(stroke + ([xmax] if xmax else []), key=lambda t: t[0])
|
||||
ymax = max(stroke + ([ymax] if ymax else []), key=lambda t: t[1])
|
||||
strokes.append(stroke)
|
||||
self.__charcode = int(data_line[0:5])
|
||||
self.__left_side = self.__char2val(data_line[8])
|
||||
self.__right_side = self.__char2val(data_line[9])
|
||||
self.__strokes = strokes
|
||||
self.__xmin, self.__ymin, self.__xmax, self.__ymax = (xmin[0], ymin[1], xmax[0], ymax[1]) if strokes else (0, 0, 0, 0)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
thefont = HersheyFonts()
|
||||
main_script(thefont)
|
||||
main_gui(thefont)
|
||||
|
||||
|
||||
def main_script(thefont=HersheyFonts()):
|
||||
print('Built in fonts:')
|
||||
default_font_names = sorted(thefont.default_font_names)
|
||||
for fontname1, fontname2 in zip_longest(default_font_names[::2], default_font_names[1::2]):
|
||||
fontname2 = '' if fontname2 is None else '- "' + fontname2 + '"'
|
||||
fontname1 = '' if fontname1 is None else '"' + fontname1 + '"'
|
||||
print(' - {0:<25} {1}'.format(fontname1, fontname2))
|
||||
print('Default font: "{0}"'.format(thefont.load_default_font()))
|
||||
print('')
|
||||
print('Rendering options:')
|
||||
for optname, defval in thefont.render_options.items():
|
||||
print(' render_options.{0} = {1}'.format(optname, defval))
|
||||
|
||||
|
||||
def main_gui(thefont=HersheyFonts()):
|
||||
import turtle
|
||||
thefont.load_default_font()
|
||||
thefont.normalize_rendering(30)
|
||||
thefont.render_options.xofs = -367
|
||||
turtle.mode('logo')
|
||||
turtle.tracer(2, delay=3)
|
||||
for coord in range(4):
|
||||
turtle.forward(200)
|
||||
if coord < 2:
|
||||
turtle.stamp()
|
||||
turtle.back(200)
|
||||
turtle.right(90)
|
||||
turtle.color('blue')
|
||||
lineslist = thefont.lines_for_text('Pack my box with five dozen liquor jugs.')
|
||||
for pt1, pt2 in lineslist:
|
||||
turtle.penup()
|
||||
turtle.goto(pt1)
|
||||
turtle.setheading(turtle.towards(pt2))
|
||||
turtle.pendown()
|
||||
turtle.goto(pt2)
|
||||
turtle.penup()
|
||||
turtle.color('red')
|
||||
turtle.goto(0, 100)
|
||||
turtle.setheading(180)
|
||||
turtle.update()
|
||||
turtle.exitonclick()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,3 @@
|
||||
from .HersheyFonts import HersheyFonts
|
||||
|
||||
__version__ = "2.1.0"
|
||||
@@ -0,0 +1,5 @@
|
||||
from .HersheyFonts import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
print('Running demo')
|
||||
main()
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Reference in new issue
Block a user