bool:
- """Check if the file_data represents a PNG file by inspecting the header."""
- return file_data.startswith(b'\x89PNG\r\n\x1a\n')
-
- def is_jpeg(file_data: bytes) -> bool:
- """Check if the file_data represents a JPEG file by inspecting the header."""
- return file_data.startswith(b'\xFF\xD8\xFF')
- for index, file_data in enumerate(document.extract_files()):
- bn = Path(target_file).stem # Use stem to get filename without extension
- if is_emf(file_data):
- extension = '.emf'
- elif is_wmf(file_data):
- extension = '.wmf'
- elif is_png(file_data):
- extension = '.png'
- elif is_jpeg(file_data):
- extension = '.jpg'
- else:
- extension = '.bin' # Default extension for unknown types
- target_path = Path(outputdir) / f"{bn}_{index}{extension}"
- print(f"Writing extracted file to: {target_path}")
- with target_path.open("wb") as outf:
- outf.write(file_data)
-
- def run_on_file(self, contents):
- # Unzip the ppt file to the temp directory, allowing for multiple
- # attempts in case the file was not originally found (which can
- # happen in cloud drives)
- if self.fof.endswith(".pptx") or self.fof.endswith(".pptm"):
- ftype = "ppt"
- elif self.fof.endswith(".one"):
- ftype = "onenote"
- else:
- ftype = "word"
-
- print(f'Unzipping {self.fof}')
- if ftype == "onenote":
- media_dir = os.path.join(contents, ftype)
- os.mkdir(media_dir)
- self.get_images_onenote(self.fof,media_dir)
- else:
- media_dir = os.path.join(contents, ftype, "media")
- attempts = 0
- max_attempts = 3
- while attempts < max_attempts:
- try:
- with zipfile.ZipFile(self.fof, "r") as zip_ref:
- zip_ref.extractall(contents)
- break # Exit the loop if successful
- except zipfile.BadZipFile:
- attempts += 1
- if attempts == max_attempts:
- self.run_on_fof_done = True
- print(f'Could not unzip {self.fof} after {max_attempts} attempts.')
- return
- else:
- print(f'Attempt {attempts} to unzip {self.fof} failed. Retrying...')
-
- self.files = []
- if os.path.exists(media_dir):
- self.files += Processor.get_svgs(media_dir)
- trigger_refresh()
-
-
- if ftype == "ppt":
- image_slides = self.get_image_slidenums(contents)
-
- # Add linked images to self.files
- linked = [
- DisplayedFile(file_uri_to_path(k))
- for k in image_slides.keys()
- if "file:" in k
- ]
- self.files += linked
- slidenums = {
- os.path.join(contents, "ppt", "media", os.path.basename(k))
- if "file:" not in k
- else str(file_uri_to_path(k)): v
- for k, v in image_slides.items()
- }
-
- # Sort the files by slide number and make slidenums a corresponding list
- # Duplicates filenames if on multiple slides
- exp_files = []
- for file in self.files:
- slides = slidenums.get(file.name, [float("inf")])
- for slide in slides:
- exp_files.append((file,slide))
- if len(exp_files)>0: # Sort by slide and then name
- self.files, slidenums = map(list, zip(*sorted(exp_files, key=lambda x: (x[1], x[0]))))
- else:
- self.files, slidenums = [], []
- for i, v in enumerate(slidenums):
- self.files[i].slidenum = v if v != float("inf") else "?"
- self.files[i].islinked = self.files[i] in linked
- else:
- if ftype=='word':
- linked = [DisplayedFile(file_uri_to_path(k)) for k in self.get_linked_images_word(contents)]
- for f in linked:
- f.islinked = True
- self.files += linked
- trigger_refresh()
-
- subfiles = None
- for ii, fv in enumerate(self.files):
- ev = False
- if fv.name.endswith(".svg") and os.path.exists(fv.name):
- with OpenWithEncoding(fv.name) as f:
- file_content = f.read()
- if ORIG_KEY in file_content:
- key = ORIG_KEY + r":\s*(.+?)<"
- match = re.search(key, file_content)
- if match:
- orig_file = match.group(1)
- orig_hash = None
- if (
- ", hash: " in orig_file
- ): # introduced hashing later than ORIG_KEY
- orig_file, orig_hash = orig_file.split(
- ", hash: "
- )
- if os.path.exists(orig_file):
- ev = os.path.abspath(orig_file)
- else:
- # Check subdirectories of the file's location in case it was moved
-
- def list_all_files(directory):
- for dirpath, dirs, files in os.walk(
- directory
- ):
- for filename in files:
- yield os.path.join(
- dirpath, filename
- )
-
- fndir = os.path.split(self.fof)[0]
- subfiles = (
- list(list_all_files(fndir))
- if subfiles is None
- else subfiles
- )
-
- for tryfile in subfiles:
- if os.path.split(orig_file)[
- -1
- ] == os.path.split(tryfile)[-1] and (
- orig_hash is None
- or hash_file(tryfile) == orig_hash
- ):
- ev = os.path.abspath(tryfile)
- break
- self.files[ii].embed=ev
- self.files[ii].embed_uri = pathlib.Path(ev).as_uri() if ev else None
-
- def run_on_folder(self):
- self.files = Processor.get_svgs(self.fof)
- trigger_refresh()
-
- ii = 0
- while ii < len(self.files):
- fn = self.files[ii].name
- svg_pgs = []
-
- # Check if the file is an SVG file
- if fn.endswith(".svg"):
- with OpenWithEncoding(fn) as f:
- try:
- contents = f.read()
- except OSError:
- raise(f'Could not open {fn}')
- if re.search(r"<\s*inkscape:page[\s\S]*?>", contents):
- svg = dh.svg_from_file(fn)
- pgs = svg.cdocsize.pgs
- haspgs = inkex.installed_haspages
- # If the file has multiple pages, split it
- if haspgs and len(pgs) > 0:
- vbs = [svg.cdocsize.pxtouu(pg.bbpx) for pg in pgs]
- for vb in vbs:
- svg.set_viewbox(vb)
- tnsvg = os.path.join(self.tndir, str(self.numtns) + ".svg")
- self.numtns += 1
- dh.overwrite_svg(svg, tnsvg)
- svg_pgs.append(tnsvg)
-
- # If thumbnails were created (multiple pages), update files and thumbnails lists
- if len(svg_pgs) > 0:
- nfiles = [DisplayedFile(fn) for t in svg_pgs]
- for i, n in enumerate(nfiles):
- n.thumbnail = svg_pgs[i]
- n.pagenum = i+1
- self.files[ii:ii + 1] = nfiles
- trigger_refresh()
- ii += len(nfiles)
- else:
- ii += 1
-
- @staticmethod
- def get_svgs(dirin):
- svg_filenames = []
- for file in os.listdir(dirin):
- if should_display(file):
- svg_filenames.append(DisplayedFile(os.path.join(dirin, file)))
- svg_filenames.sort()
- return svg_filenames
-
- def run_on_fof(self):
- print("Running on file: " + self.fof)
-
- contents = os.path.join(
- temp_dir, f"{temp_head}_cont{self.no}"
- )
- if not (os.path.exists(contents)):
- os.mkdir(contents)
-
- self.tndir = os.path.join(contents, "thumbnails")
- if not os.path.exists(self.tndir):
- os.makedirs(self.tndir)
- self.numtns = len(os.listdir(self.tndir))
-
- if not self.isdir:
- self.run_on_file(contents)
- else:
- self.run_on_folder()
-
- for ii, f in enumerate(self.files):
- if f.islinked and f.thumbnail.endswith(".svg") and not os.path.exists(self.files[ii].name):
- f.thumbnail = os.path.join(dh.si_dir,'pngs','missing_svg.svg')
-
- self.convert_emfs() # start ConversionThreads
- self.run_on_fof_done = True
- trigger_refresh()
-
- def convert_emfs(self):
- for ii, f in enumerate(self.files):
- if f.name.endswith(".emf") or f.name.endswith(".wmf"):
- tnpng = os.path.join(self.tndir, str(self.numtns) + ".png")
- self.numtns += 1
- thread = ConversionThread(f, self, tnpng)
- self.cthreads.append(thread)
- thread.start()
-
- def run(self):
- with app_lock:
- if app is None:
- Make_Flask_App()
- time.sleep(1)
- # wait to see if check_for_refresh called
- global openedgallery
- if not (openedgallery):
- webbrowser.open("http://localhost:{}".format(str(PORTNUMBER)))
- openedgallery = True
- self.run_on_fof()
- watcher.add_watch(self)
-
-def should_display(file):
- """ Criteron for whether a file should be displayed in the gallery """
- valid_exts = ['svg','emf','wmf','png','gif','jpg','jpeg']
- return any(file.lower().endswith('.'+ext) for ext in valid_exts)
-
-
-warnings.filterwarnings(
- "ignore", message="Failed to import fsevents. Fall back to kqueue"
-)
-mydir = os.path.dirname(os.path.abspath(__file__))
-packages = os.path.join(mydir, "packages")
-if packages not in sys.path:
- sys.path.append(packages)
-from watchdog.observers import Observer
-from watchdog.events import FileSystemEventHandler
-from collections import defaultdict
-class Watcher(FileSystemEventHandler):
- def __init__(self):
- super().__init__()
- self.observer = Observer()
- self.dir_processors = defaultdict(list) # dirpath -> list of Processor
- self.dir_refs = defaultdict(int) # dirpath -> number of watchers
- self.dir_watches = {} # dirpath -> Watch object
- self.debounce_timers = {} # (watcher, path) -> timer
- self.file_mod_times = {} # full file path -> last mtime
- self.observer.start()
-
- def add_watch(self, fp):
- path = os.path.abspath(fp.fof)
- dir_path = path if fp.isdir else os.path.dirname(path)
-
- # Initialize mod times since watchdog sometimes does modifier events
- # that don't seem to be real
- if fp.isdir:
- cfiles = [os.path.join(fp.fof, f) for f in os.listdir(fp.fof)]
- else:
- cfiles = [fp.fof]
- for f in cfiles:
- if os.path.isfile(f) and self.is_target_file(f,fp):
- mtime = self.get_mod_time(f)
- if mtime:
- self.file_mod_times[os.path.abspath(f)] = mtime
-
- if self.dir_refs[dir_path] == 0:
- watch = self.observer.schedule(self, dir_path, recursive=False)
- self.dir_watches[dir_path] = watch
- print(f"Scheduled observer for {dir_path}")
-
- self.dir_processors[dir_path].append(fp)
- self.dir_refs[dir_path] += 1
-
- def remove_watch(self, fp):
- path = os.path.abspath(fp.fof)
- dir_path = path if fp.isdir else os.path.dirname(path)
-
- if fp in self.dir_processors[dir_path]:
- self.dir_processors[dir_path].remove(fp)
- self.dir_refs[dir_path] -= 1
-
- if self.dir_refs[dir_path] <= 0:
- print(f"Unscheduling observer for {dir_path}")
- watch = self.dir_watches.pop(dir_path, None)
- if watch:
- self.observer.unschedule(watch)
- self.dir_processors.pop(dir_path, None)
- self.dir_refs.pop(dir_path, None)
-
- def stop(self):
- self.observer.stop()
- self.observer.join()
-
- @staticmethod
- def get_mod_time(path):
- try:
- return os.path.getmtime(path)
- except FileNotFoundError:
- return None
-
- def is_target_file(self, file_path, watcher):
- file_name = os.path.basename(file_path)
- if not watcher.isdir:
- return os.path.abspath(file_path) == os.path.abspath(watcher.fof)
- return should_display(file_name)
-
- def handle_event(self, event):
- if event.is_directory:
- return
- dir_path = os.path.dirname(event.src_path)
- for watcher in self.dir_processors.get(dir_path, []):
- if not self.is_target_file(event.src_path, watcher):
- continue
- key = (watcher, event.src_path)
-
- if key in self.debounce_timers:
- self.debounce_timers[key].cancel()
- self.debounce_timers[key] = threading.Timer(
- 0.5, self.run_callback, [watcher, event]
- )
- self.debounce_timers[key].start()
-
-
- def run_callback(self, watcher, event):
- if event.event_type == "created" and watcher.create_fcn:
- watcher.create_fcn(event.src_path)
- elif event.event_type == "modified" and watcher.mod_fcn:
- watcher.mod_fcn(event.src_path)
- elif event.event_type == "deleted" and watcher.delete_fcn:
- watcher.delete_fcn(event.src_path)
-
- def on_created(self, event):
- self.handle_event(event)
-
- def on_deleted(self, event):
- self.file_mod_times.pop(event.src_path, None)
- self.handle_event(event)
-
- def on_modified(self, event):
- if event.is_directory:
- return
- new_mtime = self.get_mod_time(os.path.abspath(event.src_path))
- old_mtime = self.file_mod_times.get(os.path.abspath(event.src_path))
-
- if new_mtime and new_mtime != old_mtime:
- self.file_mod_times[event.src_path] = new_mtime
- self.handle_event(event)
-watcher = Watcher()
-
-converted_files = dict()
-lastupdate = time.time()
-processors = []
-openedgallery = False
-
-def process_selection(file, opened=True):
- if os.path.isdir(file) and os.path.isfile(os.path.join(file, "Gallery.cfg")):
- with open(os.path.join(file, "Gallery.cfg"), "r") as f:
- lines = f.readlines()
- lines = [line.strip() for line in lines]
- for ii, ln in enumerate(lines):
- process_selection(os.path.join(file, ln), True)
- return
-
- for fp in processors:
- if file == fp.fof:
- processors.remove(fp)
- watcher.remove_watch(fp)
- print("About to start")
- fp = Processor(file, opened=opened)
- fp.win = win
- processors.append(fp)
- fp.start()
-
-def quitnow():
- requests.get(
- "http://localhost:{}/stop".format(str(PORTNUMBER))
- ) # kill Flask app
-
-
- # remove temp files
- tmps = []
- for t in os.listdir(temp_dir):
- tmp = os.path.join(temp_dir, t)
- try:
- one_day_ago = time.time() - 24 * 60 * 60
- if os.path.getmtime(tmp) < one_day_ago:
- tmps.append(tmp)
- except FileNotFoundError: # already deleted
- pass
- if tmp.startswith(temp_base):
- tmps.append(tmp)
-
- for tmp in tmps:
- if os.path.exists(tmp):
- deleted = False
- nattempts = 0
- while not deleted and nattempts < MAXATTEMPTS:
- try:
- if os.path.isdir(tmp):
- shutil.rmtree(tmp)
- else:
- os.remove(tmp)
- deleted = True
- except PermissionError:
- time.sleep(1)
- nattempts += 1
-
- for fp in processors:
- watcher.remove_watch(fp)
-
- watcher.stop()
-
- pid = os.getpid()
- import signal
-
- os.kill(pid, signal.SIGINT) # or signal.SIGTERM
-
-if guitype == "gtk":
- import gi
-
- gi.require_version("Gtk", "3.0")
-
- class GalleryViewerServer(Gtk.Window):
- def __init__(self):
- Gtk.Window.__init__(self, title="Gallery Viewer")
- self.set_default_size(
- 400, -1
- ) # set width to 400 pixels, height can be automatic
- self.set_position(Gtk.WindowPosition.CENTER)
-
- self.containing_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
- self.containing_box.set_valign(Gtk.Align.CENTER)
- self.containing_box.set_margin_top(20)
- self.containing_box.set_margin_bottom(20)
-
- self.file_button = Gtk.Button(label="View contents of files (.pptx, .docx, .one)")
- self.file_button.connect("clicked", self.on_file_button_clicked)
- self.folder_button = Gtk.Button(label="View contents of folders")
- self.folder_button.connect("clicked", self.on_folder_button_clicked)
- self.clear_button = Gtk.Button(label="Clear selections")
- self.clear_button.connect("clicked", self.clear_clicked)
- self.gallery_button = Gtk.Button(label="Display gallery")
- self.gallery_button.connect("clicked", self.gallery_button_clicked)
- self.exit_button = Gtk.Button(label="Exit")
- self.exit_button.connect("clicked", self.on_button_clicked)
-
- # Create a list store to hold the file information
- self.liststore = Gtk.ListStore(str, str)
- self.treeview = Gtk.TreeView(model=self.liststore)
- renderer_text = Gtk.CellRendererText()
- column_text = Gtk.TreeViewColumn("Name", renderer_text, text=0)
- self.treeview.append_column(column_text)
- renderer_text = Gtk.CellRendererText()
- column_text = Gtk.TreeViewColumn("Location", renderer_text, text=1)
- self.treeview.append_column(column_text)
- self.scrolled_window_files = Gtk.ScrolledWindow()
- self.scrolled_window_files.set_policy(
- Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC
- )
- self.scrolled_window_files.set_size_request(600, 200)
- self.scrolled_window_files.set_vexpand(True)
- self.scrolled_window_files.add(self.treeview)
-
- self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
- # self.box.pack_start(self.containing_box, True, True, 0)
- self.box.pack_start(self.scrolled_window_files, True, True, 0)
- self.box.pack_start(self.file_button, False, False, 0)
- self.box.pack_start(self.folder_button, False, False, 0)
- self.box.pack_start(self.clear_button, False, False, 0)
- self.box.pack_start(self.gallery_button, False, False, 0)
- self.box.pack_start(self.exit_button, False, False, 0)
- self.add(self.box)
-
- def print_text(self, text):
- buffer = self.selected_file_label.get_buffer()
- start, end = buffer.get_bounds()
- if buffer.get_text(start, end, False) == "No file selected.":
- buffer.set_text("")
- buffer.insert(buffer.get_end_iter(), text + "\n")
- end_iter = buffer.get_end_iter()
- buffer.move_mark(buffer.get_insert(), end_iter)
- self.selected_file_label.scroll_to_mark(
- buffer.get_insert(), 0, True, 0, 0
- )
-
- def on_button_clicked(self, widget):
- self.destroy()
-
- def on_file_button_clicked(self, widget):
- native = Gtk.FileChooserNative.new(
- "Please choose one or more files", self, Gtk.FileChooserAction.OPEN, None, None
- )
- native.set_select_multiple(True)
- filter_ppt = Gtk.FileFilter()
- filter_ppt.set_name("Office files")
- filter_ppt.add_pattern("*.docx")
- filter_ppt.add_pattern("*.pptx")
- filter_ppt.add_pattern("*.pptm")
- filter_ppt.add_pattern("*.one")
- native.add_filter(filter_ppt)
- response = native.run()
- if response == Gtk.ResponseType.ACCEPT:
- selected_files = native.get_filenames()
- for selected_file in selected_files:
- file_name = os.path.basename(selected_file)
- file_dir = os.path.dirname(selected_file)
- self.liststore.append([file_name, file_dir])
- process_selection(selected_file)
- native.destroy()
-
- def on_folder_button_clicked(self, widget):
- native = Gtk.FileChooserNative.new(
- "Please choose one or more directories",
- self,
- Gtk.FileChooserAction.SELECT_FOLDER,
- None,
- None,
- )
- native.set_select_multiple(True)
- response = native.run()
- if response == Gtk.ResponseType.ACCEPT:
- selected_files = native.get_filenames()
- for selected_file in selected_files:
- file_name = os.path.basename(selected_file)
- file_dir = os.path.dirname(selected_file)
- self.liststore.append([file_name, file_dir])
- process_selection(selected_file)
-
- if os.path.exists(selected_file):
- for fnm in os.listdir(selected_file):
- if fnm.endswith('.docx') or fnm.endswith('.pptx') or fnm.endswith('.one'):
- if not fnm.startswith('~$'): # temp files
- file_name = fnm
- file_dir = selected_file
- self.liststore.append([file_name, file_dir])
- process_selection(os.path.join(file_dir,file_name))
- native.destroy()
-
- def gallery_button_clicked(self, widget):
- webbrowser.open("http://localhost:{}".format(str(PORTNUMBER)))
-
- def clear_clicked(self, widget):
- for fp in reversed(processors):
- processors.remove(fp)
- watcher.remove_watch(fp)
- self.liststore.clear()
-
- win = GalleryViewerServer()
- win.set_keep_above(True)
-
- # win.connect("destroy", quitnow)
- def quit_and_close(self):
- Gtk.main_quit()
- quitnow()
-
- win.connect("destroy", quit_and_close)
- win.show_all()
- win.set_keep_above(False)
- Gtk.main()
-elif guitype == "tkinter":
- root = tk.Tk()
- root.title("Gallery Viewer")
- root.attributes("-topmost", True)
- root.wm_minsize(width=350, height=-1)
-
- def open_file():
- file = filedialog.askopenfilename()
- file_label.config(text=file)
- process_selection(file)
-
- def end_program():
- print("Quitting")
- root.destroy()
- quitnow()
-
- file_label = tk.Label(root, text="No file selected.")
- file_label.pack()
- select_button = tk.Button(root, text="Select File", command=open_file)
- select_button.pack()
- end_button = tk.Button(root, text="End Program", command=end_program)
- end_button.pack()
- root.protocol("WM_DELETE_WINDOW", end_program)
- root.mainloop()
-
-print("Finishing")
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/gallery_viewer_template.html b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/gallery_viewer_template.html
deleted file mode 100644
index acc0a88..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/gallery_viewer_template.html
+++ /dev/null
@@ -1,382 +0,0 @@
-
-
-
-
-
- Gallery Viewer
-
-
-
-
-
- Gallery Viewer
-
-
-
- Show raster graphics in gallery
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/homogenizer.inx b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/homogenizer.inx
deleted file mode 100644
index 77cda49..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/homogenizer.inx
+++ /dev/null
@@ -1,68 +0,0 @@
-
-
- Homogenizer
- burghoff.homogenizer
-
-
-
- Sets the properties of all selected objects without changing objects' center position. For best results, imported PDFs should be Flattened before running.
-
-
- Text options
- true
- true
-
-
- true
-
- Fixed size (pt)
- Scale (%)
- Mean selected
- Median selected
- Min selected
- Max selected
- Scale max to (pt)
-
- 8
- false
-
-
- Stroke options
- true
- true
-
- Fixed size (px)
- Scale (%)
- Mean selected
- Median selected
- Min selected
- Max selected
-
- 1
-
- Other options
- false
-
-
-
- Scientific Inkscape v1.4.23
- https://github.com/burghoff/Scientific-Inkscape
- David Burghoff, University of Texas at Austin
-
-
-
- text
-
-
-
-
-
-
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/homogenizer.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/homogenizer.py
deleted file mode 100644
index c4928e4..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/homogenizer.py
+++ /dev/null
@@ -1,396 +0,0 @@
-#!/usr/bin/env python
-# coding=utf-8
-#
-# Copyright (c) 2023 David Burghoff
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-import dhelpers as dh
-import inkex
-from inkex import TextElement, FlowRoot, FlowPara, Tspan, Transform, Group, FlowSpan
-from inkex.text.cache import BaseElementCache
-otp_support_tags = BaseElementCache.otp_support_tags
-from inkex.text.utils import default_style_atts
-
-from applytransform_mod import fuseTransform
-import math
-
-badels = (
- inkex.NamedView,
- inkex.Defs,
- inkex.Metadata,
- inkex.ForeignObject,
- inkex.SVGfont,
- inkex.FontFace,
- inkex.MissingGlyph,
-)
-
-dispprofile = False
-
-
-class Homogenizer(inkex.EffectExtension):
- # def document_path(self):
- # return 'test'
-
- def add_arguments(self, pars):
- pars.add_argument("--tab", help="The selected UI-tab when OK was pressed")
- pars.add_argument(
- "--setfontsize", type=inkex.Boolean, default=False, help="Set font size?"
- )
- pars.add_argument("--fontsize", type=float, default=8, help="New font size")
- pars.add_argument(
- "--fixtextdistortion",
- type=inkex.Boolean,
- default=False,
- help="Fix distorted text?",
- )
- pars.add_argument("--fontmodes", type=int, default=1, help="Font size options")
-
- pars.add_argument(
- "--setfontfamily",
- type=inkex.Boolean,
- default=False,
- help="Set font family?",
- )
- pars.add_argument("--fontfamily", type=str, default="", help="New font family")
-
- # pars.add_argument("--setreplacement", type=inkex.Boolean, default=False,help="Replace missing fonts?")
- # pars.add_argument("--replacement", type=str, default='', help="Missing fon replacement");
-
- pars.add_argument(
- "--setstroke", type=inkex.Boolean, default=False, help="Set stroke width?"
- )
- pars.add_argument(
- "--setstrokew", type=float, default=1, help="New stroke width"
- )
- pars.add_argument(
- "--strokemodes", type=int, default=1, help="Stroke width options"
- )
- pars.add_argument(
- "--clearclipmasks", type=inkex.Boolean, default=False, help="Clear clips and masks"
- )
- pars.add_argument(
- "--fusetransforms",
- type=inkex.Boolean,
- default=False,
- help="Fuse transforms to paths?",
- )
- pars.add_argument(
- "--plotaware",
- type=inkex.Boolean,
- default=False,
- help="Plot-aware text scaling?",
- )
-
- def effect(self):
- if dispprofile:
- import cProfile, pstats, io
- from pstats import SortKey
-
- pr = cProfile.Profile()
- pr.enable()
-
- setfontsize = self.options.setfontsize
- # setfontsize = (self.options.fontmodes>1);
-
- fontsize = self.options.fontsize
- setfontfamily = self.options.setfontfamily
- fontfamily = self.options.fontfamily
- setstroke = self.options.setstroke
- setstrokew = self.options.setstrokew
- fixtextdistortion = self.options.fixtextdistortion
-
- sel0 = [self.svg.selection[ii] for ii in range(len(self.svg.selection))]
- # should work with both v1.0 and v1.1
- sel = [v for el in sel0 for v in el.descendants2()]
-
- itag = inkex.Image.ctag
- if all([el.tag == itag for el in sel]):
- inkex.utils.errormsg(
- """Thanks for using Scientific Inkscape!
-
-It appears that you're attempting to homogenize a raster Image object. Please note that Inkscape is mainly for working with vector images, not raster images. Vector images preserve all of the information used to generate them, whereas raster images do not. Read about the difference here:
-https://en.wikipedia.org/wiki/Vector_graphics
-
-Unfortunately, this means that there is not much the Homogenizer can do to edit raster images. If you want to edit a raster image, you will need to use a program like Photoshop or GIMP.
- """
- )
- quit()
- elif self.options.plotaware and any([not isinstance(k, Group) for k in sel0]):
- inkex.utils.errormsg(
- "Plot-aware scaling requires that every selected object be a grouped plot."
- )
- return
-
- sela = [el for el in sel if not (isinstance(el, badels))]
- sel = [
- el
- for el in sel
- if isinstance(el, (TextElement, Tspan, FlowRoot, FlowPara, FlowSpan))
- ]
-
- if setfontfamily or setfontsize or fixtextdistortion:
- tels = [d for d in sel if isinstance(d, (TextElement, FlowRoot))]
- if not self.options.plotaware:
- bbs = dh.BB2(self.svg, tels, False)
- else:
- aels = [d for el in sel0 for d in el.descendants2()]
- bbs = dh.BB2(self.svg, aels, False)
-
- if setfontsize:
- # Get all font sizes and scale factors
- onept = self.svg.cdocsize.unittouu("1pt")
- szs = dict()
- for el in tels:
- cszs = [c.tfs / onept for ln in el.parsed_text.lns for c in ln.chrs]
- if len(cszs) > 0:
- szs[el] = max(cszs)
-
- # Determine scale and/or size
- fixedscale = False
- try:
- if self.options.fontmodes == 3:
- fixedscale = True
- elif self.options.fontmodes == 4:
- fixedscale = True
- fontsize = fontsize / max(szs.values()) * 100
- elif self.options.fontmodes == 5:
- from statistics import mean
-
- fontsize = mean(szs.values())
- elif self.options.fontmodes == 6:
- from statistics import median
-
- fontsize = median(szs.values())
- elif self.options.fontmodes == 7:
- fontsize = min(szs.values())
- elif self.options.fontmodes == 8:
- fontsize = max(szs.values())
- except ValueError:
- fontsize = 12
-
- from inkex.text import parser
-
- for el in szs:
- for d in reversed(el.descendants2()):
- sty = d.cspecified_style
- if el == d or "font-size" in sty:
- dfs, sf, utdfs = dh.composed_width(d, "font-size")
- if dfs==0:
- continue
- bshift = parser.TChar.get_baseline(sty, d.getparent())
- if bshift != 0 or "%" in sty.get("font-size", ""):
- # Convert sub/superscripts into relative size
- pfs, sf, _ = dh.composed_width(d.getparent(), "font-size")
- d.cstyle["font-size"] = f"{dfs / pfs * 100:.2f}%"
- else:
- # Set absolute size
- scl = (
- fontsize * onept / dfs
- if not fixedscale
- else fontsize / 100
- )
- nfs = utdfs * scl
- nfs = f"{nfs:.2f}" if abs(nfs) > 1 else "{:.3g}".format(nfs)
- d.cstyle["font-size"] = nfs.rstrip("0").rstrip(".") + "px"
-
- if fixtextdistortion:
- # make a new transform that removes bad scaling and shearing (see General_affine_transformation.nb)
- for el in sel:
- ct = el.ccomposed_transform
- detv = ct.a * ct.d - ct.b * ct.c
- if detv!=0:
- signdet = -1 * (detv < 0) + (detv >= 0)
- sqrtdet = math.sqrt(abs(detv))
- magv = math.sqrt(ct.b**2 + ct.a**2)
- ctnew = Transform(
- [
- [ct.a * sqrtdet / magv, -ct.b * sqrtdet * signdet / magv, ct.e],
- [ct.b * sqrtdet / magv, ct.a * sqrtdet * signdet / magv, ct.f],
- ]
- )
- dh.global_transform(el, (ctnew @ (-ct)))
-
- if setfontfamily:
- from inkex.text.font_properties import inkscape_spec_to_css
- sty = inkscape_spec_to_css(fontfamily)
- if sty is None:
- dh.idebug('Font seems to be invalid—check its spelling.')
- import sys
- sys.exit()
- # If any type of Font Style is being set, reset the others to default
- if any(k in sty for k in ["font-weight", "font-style", "font-stretch"]):
- for k in ["font-weight", "font-style", "font-stretch"]:
- sty.setdefault(k, default_style_atts[k])
-
- for el in reversed(sel):
- for k,v in sty.items():
- el.cstyle[k] = v
- el.cstyle["-inkscape-font-specification"] = None
-
- from inkex.text import parser
-
- dh.character_fixer(tels)
-
- if setfontfamily or setfontsize or fixtextdistortion:
- bbs2 = dh.BB2(self.svg, tels, True)
- if not self.options.plotaware:
- for el in sel:
- myid = el.get_id()
- if (
- isinstance(el, (TextElement, FlowRoot))
- and myid in bbs
- and myid in bbs2
- ):
- bb = bbs[el.get_id()]
- bb2 = bbs2[el.get_id()]
- tx = (bb2[0] + bb2[2] / 2) - (bb[0] + bb[2] / 2)
- ty = (bb2[1] + bb2[3] / 2) - (bb[1] + bb[3] / 2)
- trl = Transform("translate({0}, {1})".format(-tx, -ty))
- dh.global_transform(el, trl)
-
- else:
- from scale_plots import (
- geometric_bbox,
- Find_Plot_Area,
- trtf,
- appendInt,
- )
-
- gbbs = {elid: geometric_bbox(el, fbb).sbb for elid, fbb in bbs.items()}
- for i0, g in enumerate(sel0):
- pels = [k for k in g if k.get_id() in bbs] # plot elements list
- vl, hl, lvel, lhel = Find_Plot_Area(pels, gbbs)
-
- if lvel is None or lhel is None:
- lvel = None
- lhel = None
- # Display warning and proceed
- numgroup = str(i0 + 1) + appendInt(i0 + 1)
- inkex.utils.errormsg(
- "A box-like plot area could not be automatically detected on the "
- + numgroup
- + " selected plot (group ID "
- + g.get_id()
- + ").\n\nDraw a box with a stroke to define the plot area."
- + "\nAdjustment will still be performed, but the results may not be ideal."
- )
-
- bbp = dh.bbox(None)
- # plot area
- for el in pels:
- if el.get_id() in [lvel, lhel]:
- bbp = bbp.union(gbbs[el.get_id()])
- for el in g.descendants2():
- if el in tels:
- bb1 = dh.bbox(bbs[el.get_id()])
- bb2 = dh.bbox(bbs2[el.get_id()])
- if bbp.isnull:
- dx = bb1.xc - bb2.xc
- dy = bb1.yc - bb2.yc
- else:
- # For elements outside the plot area, adjust position to maintain
- # the scaled distance to the plot area
- if bb1.xc < bbp.x1:
- dx = (bbp.x1 - bb2.x2) - (
- bbp.x1 - bb1.x2
- ) * bb2.w / bb1.w
- elif bb1.xc > bbp.x2:
- dx = (bb1.x1 - bbp.x2) * bb2.w / bb1.w - (
- bb2.x1 - bbp.x2
- )
- else:
- dx = bb1.xc - bb2.xc
- if bb1.yc < bbp.y1:
- dy = (bbp.y1 - bb2.y2) - (
- bbp.y1 - bb1.y2
- ) * bb2.h / bb1.h
- elif bb1.yc > bbp.y2:
- dy = (bb1.y1 - bbp.y2) * bb2.h / bb1.h - (
- bb2.y1 - bbp.y2
- )
- else:
- dy = bb1.yc - bb2.yc
- tr2 = trtf(dx, dy)
- dh.global_transform(el, tr2)
-
- if setstroke:
- szd = dict()
- sfd = dict()
- szs = []
- for el in sela:
- sw, sf, _ = dh.composed_width(el, "stroke-width")
-
- elid = el.get_id()
- szd[elid] = sw
- sfd[elid] = sf
- if sw is not None:
- szs.append(sw)
-
- fixedscale = False
- if self.options.strokemodes == 2:
- setstrokew = self.svg.cdocsize.unittouu(str(setstrokew) + "px")
- elif self.options.strokemodes == 3:
- fixedscale = True
- elif self.options.strokemodes == 5:
- from statistics import mean
-
- setstrokew = mean(szs)
- elif self.options.strokemodes == 6:
- from statistics import median
-
- setstrokew = median(szs)
- elif self.options.strokemodes == 7:
- setstrokew = min(szs)
- elif self.options.strokemodes == 8:
- setstrokew = max(szs)
-
- for el in sela:
- elid = el.get_id()
- if not (szd[elid] is None):
- if not (fixedscale):
- newsize = setstrokew
- else:
- newsize = szd[elid] * (setstrokew / 100)
- if sfd[elid]==0:
- continue
- el.cstyle["stroke-width"] = str(newsize / sfd[elid]) + "px"
-
- if self.options.fusetransforms:
- for el in sela:
- if el.tag in otp_support_tags:
- # Fuse the composed transform onto the path
- el.ctransform = el.ccomposed_transform
- fuseTransform(el)
- el.ctransform = -el.getparent().ccomposed_transform
-
-
- if self.options.clearclipmasks:
- for el in sela:
- el.cstyle['clip-path'] = 'none'
- el.cstyle['mask'] = 'none'
-
- if dispprofile:
- pr.disable()
- s = io.StringIO()
- sortby = SortKey.CUMULATIVE
- ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
- ps.print_stats()
- dh.debug(s.getvalue())
-
-
-if __name__ == "__main__":
- dh.Run_SI_Extension(Homogenizer(), "Homogenizer")
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/image_helpers.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/image_helpers.py
deleted file mode 100644
index 5ed5a83..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/image_helpers.py
+++ /dev/null
@@ -1,675 +0,0 @@
-# Stuff copied from image_extract and image_embed
-#!/usr/bin/env python
-# coding=utf-8
-#
-# Copyright (C) 2005,2007 Aaron Spike, aaron@ekips.org
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-"""
-Extract embedded images.
-"""
-
-from __future__ import unicode_literals
-
-import os
-import dhelpers # noqa
-import inkex
-from inkex import Image
-
-try:
- from base64 import decodebytes
- from base64 import b64encode
-except ImportError:
- from base64 import decodestring as decodebytes
- from base64 import b64encode
-
-
-def mime_to_ext(mime):
- """Return an extension based on the mime type"""
- # Most extensions are automatic (i.e. extension is same as minor part of mime type)
- part = mime.split("/", 1)[1].split("+")[0]
- return "." + {
- # These are the non-matching ones.
- "svg+xml": ".svg",
- "jpeg": ".jpg",
- "icon": ".ico",
- }.get(part, part)
-
-
-def extract_image(node, save_to):
- """Extract the node as if it were an image."""
- xlink = node.get("xlink:href")
- if not xlink.startswith("data:"):
- return # Not embedded image data
-
- # This call will raise AbortExtension if the document wasn't saved
- # and the user is trying to extract them to a relative directory.
- # save_to = self.absolute_href(self.options.filepath, default=None)
- # Make the target directory if it doesn't exist yet.
- if not os.path.isdir(save_to):
- os.makedirs(save_to)
-
- try:
- data = xlink[5:]
- (mimetype, data) = data.split(";", 1)
- (base, data) = data.split(",", 1)
- except ValueError:
- inkex.errormsg("Invalid image format found")
- return
-
- if base != "base64":
- inkex.errormsg("Can't decode encoding: {}".format(base))
- return
-
- file_ext = mime_to_ext(mimetype)
-
- pathwext = os.path.join(save_to, node.get("id") + file_ext)
- if os.path.isfile(pathwext):
- inkex.errormsg(
- "Can't extract image, filename already used: {}".format(pathwext)
- )
- return
-
- # self.msg('Image extracted to: {}'.format(pathwext))
-
- with open(pathwext, "wb") as fhl:
- fhl.write(decodebytes(data.encode("utf-8")))
-
- # absolute for making in-mem cycles work
- node.set("xlink:href", os.path.realpath(pathwext))
-
-
-"""
-Embed images so they are base64 encoded data inside the svg.
-"""
-
-from inkex.localization import inkex_gettext as _
-
-try:
- import urllib.request as urllib
- import urllib.parse as urlparse
- from base64 import encodebytes
-except ImportError:
- # python2 compatibility, remove when python3 only.
- import urllib
- import urlparse
- from base64 import encodestring as encodebytes
-
-
-def embed_image(node, svg_dir):
- """Embed the data of the selected Image Tag element"""
- xlink = node.get("xlink:href")
- if xlink is not None and xlink[:5] == "data:":
- # No need, data already embedded
- return
- if xlink is None:
- inkex.errormsg(
- _('Attribute "xlink:href" not set on node {}.'.format(node.get_id()))
- )
- return
-
- url = urlparse.urlparse(xlink)
- href = urllib.url2pathname(url.path)
-
- # Primary location always the filename itself, we allow this
- # call to search the user's home folder too.
- path = absolute_href2(href or "", svg_dir)
-
- # Backup directory where we can find the image
- if not os.path.isfile(path):
- path = node.get("sodipodi:absref", path)
-
- if not os.path.isfile(path):
- inkex.errormsg(_('File not found "{}". Unable to embed image.').format(path))
- return
-
- with open(path, "rb") as handle:
- # Don't read the whole file to check the header
- file_type = get_type(path, handle.read(10))
- handle.seek(0)
-
- if file_type:
- # Future: Change encodestring to encodebytes when python3 only
- node.set(
- "xlink:href",
- "data:{};base64,{}".format(
- file_type, encodebytes(handle.read()).decode("ascii")
- ),
- )
- node.pop("sodipodi:absref")
- else:
- inkex.errormsg(
- _(
- "%s is not of type image/png, image/jpeg, "
- "image/bmp, image/gif, image/tiff, or image/x-icon"
- )
- % path
- )
-
-
-def embed_external_image(el, filename):
- """Embed the data of the selected Image Tag element"""
- if filename is not None:
- with open(filename, "rb") as handle:
- # Don't read the whole file to check the header
- file_type = get_type(filename, handle.read(10))
- handle.seek(0)
-
- if file_type:
- # Future: Change encodestring to encodebytes when python3 only
- el.set(
- "xlink:href",
- "data:{};base64,{}".format(
- file_type, encodebytes(handle.read()).decode("ascii")
- ),
- )
- el.pop("sodipodi:absref")
- else:
- inkex.errormsg(
- _(
- "%s is not of type image/png, image/jpeg, "
- "image/bmp, image/gif, image/tiff, or image/x-icon"
- )
- % filename
- )
-
-
-# Check if image is linked or embedded. If linked, check if path is valid
-def check_linked(node, svg_dir):
- """Embed the data of the selected Image Tag element"""
- xlink = node.get("xlink:href")
- if xlink is not None and xlink[:5] == "data:":
- return False, None
- url = urlparse.urlparse(xlink)
- href = urllib.url2pathname(url.path)
- path = absolute_href2(href or "", svg_dir)
- if not os.path.isfile(path):
- path = node.get("sodipodi:absref", path)
- fileexists = os.path.isfile(path)
- if not (fileexists):
- return True, None
- else:
- return True, path
-
-
-# Gets the type of an image element
-def get_image_type(node, svg_dir):
- xlink = node.get("xlink:href")
-
- if not xlink.startswith("data:"):
- # Linked image
- xlink = node.get("xlink:href")
- if xlink is not None and xlink[:5] == "data:":
- # No need, data already embedded
- return None
- if xlink is None:
- print(_('Attribute "xlink:href" not set on node {}.'.format(node.get_id())))
- return None
-
- url = urlparse.urlparse(xlink)
- href = urllib.url2pathname(url.path)
-
- # Primary location always the filename itself, we allow this
- # call to search the user's home folder too.
- path = absolute_href2(href or "", svg_dir)
-
- # Backup directory where we can find the image
- if not os.path.isfile(path):
- path = node.get("sodipodi:absref", path)
-
- if not os.path.isfile(path):
- print(_('File not found "{}". Unable to embed image.').format(path))
- return None
-
- with open(path, "rb") as handle:
- # Don't read the whole file to check the header
- file_type = get_type(path, handle.read(10))
- return file_type
- else:
- try:
- data = xlink[5:]
- (mimetype, data) = data.split(";", 1)
- (base, data) = data.split(",", 1)
- except ValueError:
- print("Invalid image format found")
- return None
-
- if base != "base64":
- print("Can't decode encoding: {}".format(base))
- return None
- return mimetype
-
-
-def get_type(path, header):
- """Basic magic header checker, returns mime type"""
- for head, mime in (
- (b"\x89PNG", "image/png"),
- (b"\xff\xd8", "image/jpeg"),
- (b"BM", "image/bmp"),
- (b"GIF87a", "image/gif"),
- (b"GIF89a", "image/gif"),
- (b"MM\x00\x2a", "image/tiff"),
- (b"II\x2a\x00", "image/tiff"),
- ):
- if header.startswith(head):
- return mime
-
- # ico files lack any magic... therefore we check the filename instead
- for ext, mime in (
- # official IANA registered MIME is 'image/vnd.microsoft.icon' tho
- (".ico", "image/x-icon"),
- (".svg", "image/svg+xml"),
- ):
- if path.endswith(ext):
- return mime
- return None
-
-
-# if __name__ == '__main__':
-# EmbedImage().run()
-
-
-# Modification to the built-in absolute_href function that doesn't require an
-# extension class
-def absolute_href2(filename, svg_dir, default="~/"):
- """
- Process the filename such that it's turned into an absolute filename
- with the working directory being the directory of the loaded svg.
-
- User's home folder is also resolved. So '~/a.png` will be `/home/bob/a.png`
-
- Default is a fallback working directory to use if the svg's filename is not
- available, if you set default to None, then the user will be given errors if
- there's no working directory available from Inkscape.
- """
- filename = os.path.expanduser(filename)
- if not os.path.isabs(filename):
- filename = os.path.expanduser(filename)
- if not os.path.isabs(filename):
- filename = os.path.join(svg_dir, filename)
- return os.path.realpath(os.path.expanduser(filename))
-
-
-# Stuff by David Burghoff
-try:
- from PIL import Image as ImagePIL
-
- hasPIL = True
-except:
- hasPIL = False
-
-# def remove_alpha(imin):
-# background = ImagePIL.new('RGBA', imin.size, (255,255,255))
-# alpha_composite = ImagePIL.alpha_composite(background, imin)
-# alpha_composite_3 = alpha_composite.convert('RGB')
-# return alpha_composite_3
-
-
-# def remove_alpha(imin, background):
-# # background = ImagePIL.new('RGBA', imin.size, (255,255,255))
-# alpha_composite = ImagePIL.alpha_composite(background, imin)
-# alpha_composite_3 = alpha_composite.convert("RGB")
-# return alpha_composite_3
-
-# Convert and crop transparent image to JPG
-# Requires the transparent version (imin) as well as the opaque version (opaqueimin)
-# def to_jpeg(imin, opaqueimin, imout):
-# with ImagePIL.open(imin) as im:
-# with ImagePIL.open(opaqueimin) as oim:
-# bbox = im.getbbox()
-# # # Composite to remove transparent regions
-# # compim = remove_alpha(im, imb)
-
-# # Crop to non-transparent region only
-# # left,upper,right,lower (left & upper pixel is non-zero corner, right-1 & lower-1 is non-zero corner)
-# if bbox is not None:
-# oim = oim.crop(bbox)
-# bbox = [
-# bbox[0] / im.size[0],
-# bbox[1] / im.size[1],
-# bbox[2] / im.size[0],
-# bbox[3] / im.size[1],
-# ]
-# # normalize to original size
-# oim.convert('RGB').save(imout)
-# return imout, bbox
-
-
-def to_jpeg(imin, imout):
- with ImagePIL.open(imin) as im:
- im.convert("RGB").save(imout)
-
-
-def crop_image(imin):
- with ImagePIL.open(imin) as im:
- bbox = im.getbbox()
- # left,upper,right,lower (left & upper pixel is non-zero corner, right-1 & lower-1 is non-zero corner)
- if bbox is not None:
- cropim = im.crop(bbox)
- bbox = [
- bbox[0] / im.size[0],
- bbox[1] / im.size[1],
- bbox[2] / im.size[0],
- bbox[3] / im.size[1],
- ]
- # normalized to original size
- cropim.save(imin)
- return imin, bbox
-
-
-# Extract an embedded image
-def extract_image_simple(node, save_to_base):
- """Extract the node as if it were an image."""
- xlink = node.get("xlink:href")
- data = xlink[5:]
- try:
- data = xlink[5:]
- (mimetype, data) = data.split(";", 1)
- (base, data) = data.split(",", 1)
- except ValueError:
- return None
- file_ext = mime_to_ext(mimetype)
- pathwext = save_to_base + file_ext
- # inkex.utils.debug(pathwext)
- with open(pathwext, "wb") as fhl:
- fhl.write(decodebytes(data.encode("utf-8")))
- return pathwext
-
-
-# Get the size of an embedded image
-def embedded_size(node):
- xlink = node.get("xlink:href")
- try:
- data = xlink[5:]
- (mimetype, data) = data.split(";", 1)
- (base, data) = data.split(",", 1)
- return len(decodebytes(data.encode("utf-8")))
- except (ValueError, TypeError):
- return None
-
-
-# Get the data string of an embedded image with the alpha stripped out
-# This allows images to be identified after conversion to PDF
-def Stripped_Alpha_String(el):
- import tempfile
-
- tf = tempfile.NamedTemporaryFile().name
- fullpath = extract_image_simple(el, tf)
-
- with ImagePIL.open(fullpath) as im:
- newfile = strip_ext(fullpath) + ".png"
-
- return str(im.size)
-
- # im.convert('RGB').save(newfile)
-
- # with open(newfile, "rb") as handle:
- # # Don't read the whole file to check the header
- # file_type = get_type(newfile, handle.read(10))
- # handle.seek(0)
-
- # if file_type:
- # return "data:{};base64,{}".format(
- # file_type, encodebytes(handle.read()).decode("ascii"))
- # return None
-
-
-def Make_Data_Image(datastr):
- data = [ord(c) for c in datastr]
- # inkex.utils.debug(data)
- import numpy as np
-
- im = ImagePIL.fromarray(np.array([data], dtype="uint8"))
-
- import tempfile
-
- tf = tempfile.NamedTemporaryFile().name
- newfile = tf + ".png"
- im.save(newfile)
-
- with open(newfile, "rb") as handle:
- # Don't read the whole file to check the header
- file_type = get_type(newfile, handle.read(10))
- handle.seek(0)
-
- if file_type:
- return "data:{};base64,{}".format(
- file_type, encodebytes(handle.read()).decode("ascii")
- )
- return None
-
-
-def Read_Data_Image(imstr):
- data = imstr[5:]
- (mimetype, data) = data.split(";", 1)
- (base, data) = data.split(",", 1)
- import io
-
- im = ImagePIL.open(io.BytesIO(decodebytes(data.encode("utf-8"))))
-
- import numpy as np
-
- npa = np.asarray(im)
- if len(npa.shape) == 3:
- data = npa[:, :, 0]
- else:
- data = npa
- return "".join([chr(v) for v in list(data.ravel())])
-
-
-import io
-
-
-def str_to_ImagePIL(imstr):
- try:
- data = imstr[5:]
- (mimetype, data) = data.split(";", 1)
- (base, data) = data.split(",", 1)
- im = ImagePIL.open(io.BytesIO(decodebytes(data.encode("utf-8"))))
- return im
- except:
- return None
-
-
-def ImagePIL_to_str(im):
- try:
- img_byte_arr = io.BytesIO()
- im.save(img_byte_arr, format="png")
- vals = img_byte_arr.getvalue()
- file_type = get_type(None, vals[0:10])
- if file_type:
- return "data:{};base64,{}".format(
- file_type, encodebytes(vals).decode("ascii")
- )
- except:
- return None
-
-
-# Get just the alpha channel of an image, which can be turned into a mask
-# def make_alpha_mask(fin,maskout):
-# from PIL import Image, ImageOps
-# with Image.open(fin) as im:
-# (r,g,b,a)=im.split()
-# # (r,g,b)=Image.new('RGB',im.size,'black').split()
-# # nim = Image.merge('RGBA',(r,g,b,ImageOps.invert(a)))
-# # (r,g,b)=Image.new('RGB',im.size,'black').split()
-# nim = Image.merge('L',(a,))
-# nim.save(maskout)
-
-# # Get just the alpha channel of an image, which can be turned into a mask
-# def make_rgb(fin,rgbout):
-# from PIL import Image, ImageOps
-# with Image.open(fin) as im:
-# (r,g,b,a)=im.split()
-# # (r,g,b)=Image.new('RGB',im.size,'black').split()
-# # nim = Image.merge('RGBA',(r,g,b,ImageOps.invert(a)))
-# # (r,g,b)=Image.new('RGB',im.size,'black').split()
-# nim = Image.merge('RGB',(r,g,b))
-# nim.save(rgbout)
-
-
-# Strips image extensions
-def strip_ext(fnin):
- # strip existing extension
- if fnin[-4:].lower() in [".png", ".gif", "jpg", "tif"]:
- fnin = fnin[0:-4]
- if fnin[-5:].lower() in ["jpeg", "tiff"]:
- fnin = fnin[0:-5]
- return fnin
-
-
-# Extracts embedded images or returns the path of linked ones
-def extract_img_file(el, svg_dir, newpath):
- islinked, validpath = check_linked(el, svg_dir)
- if islinked and validpath is not None:
- impath = validpath
- madenew = False
- elif islinked and validpath is None:
- impath = None
- madenew = False
- else:
- # inkex.utils.debug(newpath)
- extract = extract_image_simple(el, strip_ext(newpath))
- if extract is not None:
- impath = extract
- madenew = True
- else:
- impath = None
- madenew = False
- return impath, islinked
-
-
-# For images with alpha=0 pixels, set the RGB of those pixels based on another
-# image. This is usually the same image with a background and with other objects.
-# For those pixels, alpha is then set to 1 (out of 255), which prevents the PDF
-# renderer from replacing those pixels with black. This avoids the 'gray ring'
-# issue that can happen on PDF exports.
-def Set_Alpha0_RGB(img, imgref):
- im1 = ImagePIL.open(img).convert("RGBA")
- im2 = ImagePIL.open(imgref).convert("RGBA")
- import numpy as np
-
- d1 = np.asarray(im1)
- d2 = np.asarray(im2)
- a = d1[:, :, 3]
- nd = np.stack(
- (
- np.where(a == 0, d2[:, :, 0], d1[:, :, 0]),
- np.where(a == 0, d2[:, :, 1], d1[:, :, 1]),
- np.where(a == 0, d2[:, :, 2], d1[:, :, 2]),
- np.where(a == 0, 1 * np.ones_like(a), a),
- ),
- 2,
- )
- ImagePIL.fromarray(nd).save(img)
- # inkex.utils.debug(img)
- anyalpha0 = np.where(a == 0, True, False).any()
- return anyalpha0
-
-
-# Crop a list of images based on the transparency of the first one
-# Returns the normalized bounding box, which we need later
-def crop_images(ims_in):
- bbox = None
- with ImagePIL.open(ims_in[0]) as ref_im:
- bbox = ref_im.getbbox()
- nsz = ref_im.size
-
- if bbox is not None:
- # left,upper,right,lower (left & upper pixel is non-zero corner, right-1 & lower-1 is non-zero corner)
- nbbox = [
- bbox[0] / nsz[0],
- bbox[1] / nsz[1],
- bbox[2] / nsz[0],
- bbox[3] / nsz[1],
- ] # normalize to original size
- for imf in ims_in:
- with ImagePIL.open(imf) as im:
- im.crop(bbox).save(imf)
- return nbbox
- else:
- return None
-
-
-# Get the absolute locations of all linked images when called by an extension
-# Needed because the temp file has a different location from the actual one
-def get_linked_locations(slf):
- llocations = dict()
- images = slf.svg.xpath("//svg:image")
- for node in images:
- xlink = node.get("xlink:href")
- if xlink is not None and xlink[:5] != "data:":
- try:
- import urllib.request as urllib
- import urllib.parse as urlparse
- except ImportError:
- # python2 compatibility, remove when python3 only.
- import urllib
- import urlparse
-
- url = urlparse.urlparse(xlink)
- href = urllib.url2pathname(url.path)
-
- # Look relative to the *temporary* filename instead of the original filename.
- try: # v1.2 forward
- path = slf.absolute_href(
- href or "", cwd=os.path.dirname(slf.options.input_file)
- )
- except: # pre-v1.2
- # Primary location always the filename itself, we allow this
- # call to search the user's home folder too.
- path = slf.absolute_href(href or "")
-
- # Backup directory where we can find the image
- if not os.path.isfile(path):
- path = node.get("sodipodi:absref", path)
-
- if os.path.isfile(path):
- llocations[node.get_id()] = path
- else:
- llocations[node.get_id()] = None
- return llocations
-
-
-# Get the absolute locations of all linked images when the absolute path is known
-def get_linked_locations_file(fin, svg):
- llocations = dict()
- images = svg.xpath("//svg:image")
- for node in images:
- xlink = node.get("xlink:href")
- if xlink is not None and xlink[:5] != "data:":
- try:
- import urllib.request as urllib
- import urllib.parse as urlparse
- except ImportError:
- # python2 compatibility, remove when python3 only.
- import urllib
- import urlparse
-
- url = urlparse.urlparse(xlink)
- href = urllib.url2pathname(url.path)
-
- path = absolute_href2(href or "", os.path.dirname(fin))
-
- # Backup directory where we can find the image
- if not os.path.isfile(path):
- path = node.get("sodipodi:absref", path)
-
- if os.path.isfile(path):
- llocations[node.get_id()] = path
- else:
- llocations[node.get_id()] = None
- return llocations
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/__init__.py
deleted file mode 100644
index fa049ea..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/__init__.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# coding=utf-8
-"""
-This describes the core API for the inkex core modules.
-
-This provides the basis from which you can develop your inkscape extension.
-"""
-
-# pylint: disable=wildcard-import
-import sys
-
-from .extensions import *
-from .utils import AbortExtension, DependencyError, Boolean, errormsg
-from .styles import *
-from .paths import Path, CubicSuperPath # Path commands are not exported
-from .colors import *
-from .transforms import *
-from .elements import *
-
-# legacy proxies
-from .deprecated import Effect
-from .deprecated import localize
-from .deprecated import debug
-
-# legacy functions
-from .deprecated import are_near_relative
-from .deprecated import unittouu
-
-MIN_VERSION = (3, 7)
-if sys.version_info < MIN_VERSION:
- sys.exit("Inkscape extensions require Python 3.7 or greater.")
-
-__version__ = "1.3.0" # Version number for inkex; may differ from Inkscape version.
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/base.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/base.py
deleted file mode 100644
index 14de8b8..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/base.py
+++ /dev/null
@@ -1,562 +0,0 @@
-# coding=utf-8
-#
-# Copyright (c) 2018 - Martin Owens
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-"""
-The ultimate base functionality for every Inkscape extension.
-"""
-
-import io
-import os
-import re
-import sys
-import copy
-
-from typing import (
- Dict,
- List,
- Tuple,
- Type,
- Optional,
- Callable,
- Any,
- Union,
- IO,
- TYPE_CHECKING,
- cast,
-)
-
-from argparse import ArgumentParser, Namespace
-from lxml import etree
-
-from .interfaces.IElement import IBaseElement, ISVGDocumentElement
-from .utils import filename_arg, AbortExtension, ABORT_STATUS, errormsg, do_nothing
-from .elements._parser import load_svg
-from .elements._utils import NSS
-from .localization import localize
-
-
-class InkscapeExtension:
- """
- The base class extension, provides argument parsing and basic
- variable handling features.
- """
-
- multi_inx = False # Set to true if this class is used by multiple inx files.
- extra_nss = {} # type: Dict[str, str]
-
- # Provide a unique value to allow detection of no argument specified
- # for `output` parameter of `run()`, not even `None`; this has to be an io
- # type for type checking purposes:
- output_unspecified = io.StringIO("")
-
- def __init__(self):
- # type: () -> None
- NSS.update(self.extra_nss)
- self.file_io = None # type: Optional[IO]
- self.options = Namespace()
- self.document = None # type: Union[None, bytes, str, etree.element]
- self.arg_parser = ArgumentParser(description=self.__doc__)
-
- self.arg_parser.add_argument(
- "input_file",
- nargs="?",
- metavar="INPUT_FILE",
- type=filename_arg,
- help="Filename of the input file (default is stdin)",
- default=None,
- )
-
- self.arg_parser.add_argument(
- "--output",
- type=str,
- default=None,
- help="Optional output filename for saving the result (default is stdout).",
- )
-
- self.add_arguments(self.arg_parser)
-
- localize()
-
- def add_arguments(self, pars):
- # type: (ArgumentParser) -> None
- """Add any extra arguments to your extension handle, use:
-
- def add_arguments(self, pars):
- pars.add_argument("--num-cool-things", type=int, default=3)
- pars.add_argument("--pos-in-doc", type=str, default="doobry")
- """
- # No extra arguments by default so super is not required
-
- def parse_arguments(self, args):
- # type: (List[str]) -> None
- """Parse the given arguments and set 'self.options'"""
- self.options = self.arg_parser.parse_args(args)
-
- def arg_method(self, prefix="method"):
- # type: (str) -> Callable[[str], Callable[[Any], Any]]
- """Used by add_argument to match a tab selection with an object method
-
- pars.add_argument("--tab", type=self.arg_method(), default="foo")
- ...
- self.options.tab(arguments)
- ...
- .. code-block:: python
- .. def method_foo(self, arguments):
- .. # do something
- """
-
- def _inner(value):
- name = f"""{prefix}_{value.strip('"').lower()}""".replace("-", "_")
- try:
- return getattr(self, name)
- except AttributeError as error:
- if name.startswith("_"):
- return do_nothing
- raise AbortExtension(f"Can not find method {name}") from error
-
- return _inner
-
- @staticmethod
- def arg_number_ranges():
- """Parses a number descriptor. e.g:
- ``1,2,4-5,7,9-`` is parsed to ``1, 2, 4, 5, 7, 9, 10, ..., lastvalue``
-
- .. versionadded:: 1.2
-
- Usage:
-
- .. code-block:: python
-
- # in add_arguments()
- pars.add_argument("--pages", type=self.arg_number_ranges(), default=1-)
- # later on, pages is then a list of ints
- pages = self.options.pages(lastvalue)
-
- """
-
- def _inner(value):
- def method(pages, lastvalue, startvalue=1):
- # replace ranges, such as -3, 10- with startvalue,2,3,10..lastvalue
- pages = re.sub(
- r"(\d+|)\s?-\s?(\d+|)",
- lambda m: ",".join(
- map(
- str,
- range(
- int(m.group(1) or startvalue),
- int(m.group(2) or lastvalue) + 1,
- ),
- )
- )
- if not (m.group(1) or m.group(2)) == ""
- else "",
- pages,
- )
-
- pages = map(int, re.findall(r"(\d+)", pages))
- pages = tuple({i for i in pages if i <= lastvalue})
- return pages
-
- return lambda lastvalue, startvalue=1: method(
- value, lastvalue, startvalue=startvalue
- )
-
- return _inner
-
- @staticmethod
- def arg_class(options: List[Type]) -> Callable[[str], Any]:
- """Used by add_argument to match an option with a class
-
- Types to choose from are given by the options list
-
- .. versionadded:: 1.2
-
- Usage:
-
- .. code-block:: python
-
- pars.add_argument("--class", type=self.arg_class([ClassA, ClassB]),
- default="ClassA")
- """
-
- def _inner(value: str):
- name = value.strip('"')
- for i in options:
- if name == i.__name__:
- return i
- raise AbortExtension(f"Can not find class {name}")
-
- return _inner
-
- def debug(self, msg):
- # type: (str) -> None
- """Write a debug message"""
- errormsg(f"DEBUG<{type(self).__name__}> {msg}\n")
-
- @staticmethod
- def msg(msg):
- # type: (str) -> None
- """Write a non-error message"""
- errormsg(msg)
-
- def run(self, args=None, output=output_unspecified):
- # type: (Optional[List[str]], Union[str, IO]) -> None
- """Main entrypoint for any Inkscape Extension"""
- try:
- if args is None:
- args = sys.argv[1:]
-
- self.parse_arguments(args)
- if self.options.input_file is None:
- self.options.input_file = sys.stdin
- elif "DOCUMENT_PATH" not in os.environ:
- os.environ["DOCUMENT_PATH"] = self.options.input_file
-
- self.bin_stdout = None
- if self.options.output is None:
- # If no output was specified, attempt to extract a binary
- # output from stdout, and if that doesn't seem possible,
- # punt and try whatever stream stdout is:
- if output is InkscapeExtension.output_unspecified:
- output = sys.stdout
- if "b" not in getattr(output, "mode", "") and not isinstance(
- output, (io.RawIOBase, io.BufferedIOBase)
- ):
- if hasattr(output, "buffer"):
- output = output.buffer # type: ignore
- elif hasattr(output, "fileno"):
- self.bin_stdout = os.fdopen(
- output.fileno(), "wb", closefd=False
- )
- output = self.bin_stdout
- self.options.output = output
-
- self.load_raw()
- self.save_raw(self.effect())
- except AbortExtension as err:
- errormsg(str(err))
- sys.exit(ABORT_STATUS)
- finally:
- self.clean_up()
-
- def load_raw(self):
- # type: () -> None
- """Load the input stream or filename, save everything to self"""
- if isinstance(self.options.input_file, str):
- # pylint: disable=consider-using-with
- self.file_io = open(self.options.input_file, "rb")
- document = self.load(self.file_io)
- else:
- document = self.load(self.options.input_file)
- self.document = document
-
- def save_raw(self, ret):
- # type: (Any) -> None
- """Save to the output stream, use everything from self"""
- if self.has_changed(ret):
- if isinstance(self.options.output, str):
- with open(self.options.output, "wb") as stream:
- self.save(stream)
- else:
- if sys.platform == "win32" and not "PYTEST_CURRENT_TEST" in os.environ:
- # When calling an extension from within Inkscape on Windows,
- # Python thinks that the output stream is seekable
- # (https://gitlab.com/inkscape/inkscape/-/issues/3273)
- self.options.output.seekable = lambda self: False
-
- def seek_replacement(offset: int, whence: int = 0):
- raise AttributeError(
- "We can't seek in the stream passed by Inkscape on Windows"
- )
-
- def tell_replacement():
- raise AttributeError(
- "We can't tell in the stream passed by Inkscape on Windows"
- )
-
- # Some libraries (e.g. ZipFile) don't query seekable, but check for an error
- # on seek
- self.options.output.seek = seek_replacement
- self.options.output.tell = tell_replacement
- self.save(self.options.output)
-
- def load(self, stream):
- # type: (IO) -> str
- """Takes the input stream and creates a document for parsing"""
- raise NotImplementedError(f"No input handle for {self.name}")
-
- def save(self, stream):
- # type: (IO) -> None
- """Save the given document to the output file"""
- raise NotImplementedError(f"No output handle for {self.name}")
-
- def effect(self):
- # type: () -> Any
- """Apply some effects on the document or local context"""
- raise NotImplementedError(f"No effect handle for {self.name}")
-
- def has_changed(self, ret): # pylint: disable=no-self-use
- # type: (Any) -> bool
- """Return true if the output should be saved"""
- return ret is not False
-
- def clean_up(self):
- # type: () -> None
- """Clean up any open handles and other items"""
- if hasattr(self, "bin_stdout"):
- if self.bin_stdout is not None:
- self.bin_stdout.close()
- if self.file_io is not None:
- self.file_io.close()
-
- @classmethod
- def svg_path(cls, default=None):
- # type: (Optional[str]) -> Optional[str]
- """
- Return the folder the svg is contained in.
- Returns None if there is no file.
-
- .. versionchanged:: 1.1
- A default path can be given which is returned in case no path to the
- SVG file can be determined.
- """
- path = cls.document_path()
- if path:
- return os.path.dirname(path)
- if default:
- return default
- return path # Return None or '' for context
-
- @classmethod
- def ext_path(cls):
- # type: () -> str
- """Return the folder the extension script is in"""
- return os.path.dirname(sys.modules[cls.__module__].__file__ or "")
-
- @classmethod
- def get_resource(cls, name, abort_on_fail=True):
- # type: (str, bool) -> str
- """Return the full filename of the resource in the extension's dir
-
- .. versionadded:: 1.1"""
- filename = cls.absolute_href(name, cwd=cls.ext_path())
- if abort_on_fail and not os.path.isfile(filename):
- raise AbortExtension(f"Could not find resource file: {filename}")
- return filename
-
- @classmethod
- def document_path(cls):
- # type: () -> Optional[str]
- """Returns the saved location of the document
-
- * Normal return is a string containing the saved location
- * Empty string means the document was never saved
- * 'None' means this version of Inkscape doesn't support DOCUMENT_PATH
-
- DO NOT READ OR WRITE TO THE DOCUMENT FILENAME!
-
- * Inkscape may have not written the latest changes, leaving you reading old
- data.
- * Inkscape will not respect anything you write to the file, causing data loss.
-
- .. versionadded:: 1.1
- """
- return os.environ.get("DOCUMENT_PATH", None)
-
- @classmethod
- def absolute_href(cls, filename, default="~/", cwd=None):
- # type: (str, str, Optional[str]) -> str
- """
- Process the filename such that it's turned into an absolute filename
- with the working directory being the directory of the loaded svg.
-
- User's home folder is also resolved. So '~/a.png` will be `/home/bob/a.png`
-
- Default is a fallback working directory to use if the svg's filename is not
- available.
-
- .. versionchanged:: 1.1
- If you set default to None, then the user will be given errors if
- there's no working directory available from Inkscape.
- """
- filename = os.path.expanduser(filename)
- if not os.path.isabs(filename):
- filename = os.path.expanduser(filename)
- if not os.path.isabs(filename):
- if cwd is None:
- cwd = cls.svg_path(default)
- if cwd is None:
- raise AbortExtension(
- "Can not use relative path, Inkscape isn't telling us the "
- "current working directory."
- )
- if cwd == "":
- raise AbortExtension(
- "The SVG must be saved before you can use relative paths."
- )
- filename = os.path.join(cwd, filename)
- return os.path.realpath(os.path.expanduser(filename))
-
- @property
- def name(self):
- # type: () -> str
- """Return a fixed name for this extension"""
- return type(self).__name__
-
-
-if TYPE_CHECKING:
- _Base = InkscapeExtension
-else:
- _Base = object
-
-
-class TempDirMixin(_Base): # pylint: disable=abstract-method
- """
- Provide a temporary directory for extensions to stash files.
- """
-
- dir_suffix = ""
- dir_prefix = "inktmp"
-
- def __init__(self, *args, **kwargs):
- self.tempdir = None
- self._tempdir = None
- super().__init__(*args, **kwargs)
-
- def load_raw(self):
- # type: () -> None
- """Create the temporary directory"""
- # pylint: disable=import-outside-toplevel
- from tempfile import TemporaryDirectory
-
- # Need to hold a reference to the Directory object or else it might get GC'd
- self._tempdir = TemporaryDirectory( # pylint: disable=consider-using-with
- prefix=self.dir_prefix, suffix=self.dir_suffix
- )
- self.tempdir = os.path.realpath(self._tempdir.name)
- super().load_raw()
-
- def clean_up(self):
- # type: () -> None
- """Delete the temporary directory"""
- self.tempdir = None
- # if the file does not exist, _tempdir is never set.
- if self._tempdir is not None:
- self._tempdir.cleanup()
- super().clean_up()
-
-
-class SvgInputMixin(_Base): # pylint: disable=too-few-public-methods, abstract-method
- """
- Expects the file input to be an svg document and will parse it.
- """
-
- # Select all objects if none are selected
- select_all: Tuple[Type[IBaseElement], ...] = ()
-
- def __init__(self):
- super().__init__()
-
- self.arg_parser.add_argument(
- "--id",
- action="append",
- type=str,
- dest="ids",
- default=[],
- help="id attribute of object to manipulate",
- )
-
- self.arg_parser.add_argument(
- "--selected-nodes",
- action="append",
- type=str,
- dest="selected_nodes",
- default=[],
- help="id:subpath:position of selected nodes, if any",
- )
-
- def load(self, stream):
- # type: (IO) -> etree
- """Load the stream as an svg xml etree and make a backup"""
- document = load_svg(stream)
- self.original_document = copy.deepcopy(document)
- self.svg: ISVGDocumentElement = document.getroot()
- self.svg.selection.set(*self.options.ids)
- if not self.svg.selection and self.select_all:
- self.svg.selection = self.svg.descendants().filter(*self.select_all)
- return document
-
-
-class SvgOutputMixin(_Base): # pylint: disable=too-few-public-methods, abstract-method
- """
- Expects the output document to be an svg document and will write an etree xml.
-
- A template can be specified to kick off the svg document building process.
- """
-
- template = """
- """
-
- @classmethod
- def get_template(cls, **kwargs):
- """
- Opens a template svg document for building, the kwargs
- MUST include all the replacement values in the template, the
- default template has 'width' and 'height' of the document.
- """
- kwargs.setdefault("unit", "")
- return load_svg(str(cls.template.format(**kwargs)))
-
- def save(self, stream):
- # type: (IO) -> None
- """Save the svg document to the given stream"""
- if isinstance(self.document, (bytes, str)):
- document = self.document
- elif "Element" in type(self.document).__name__:
- # isinstance can't be used here because etree is broken
- doc = cast(etree, self.document)
- document = doc.getroot().tostring()
- else:
- raise ValueError(
- f"Unknown type of document: {type(self.document).__name__} can not"
- + "save."
- )
-
- try:
- stream.write(document)
- except TypeError:
- # we hope that this happens only when document needs to be encoded
- stream.write(document.encode("utf-8")) # type: ignore
-
-
-class SvgThroughMixin(SvgInputMixin, SvgOutputMixin): # pylint: disable=abstract-method
- """
- Combine the input and output svg document handling (usually for effects).
- """
-
- def has_changed(self, ret): # pylint: disable=unused-argument
- # type: (Any) -> bool
- """Return true if the svg document has changed"""
- original = etree.tostring(self.original_document)
- result = etree.tostring(self.document)
- return original != result
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/bezier.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/bezier.py
deleted file mode 100644
index 976fdab..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/bezier.py
+++ /dev/null
@@ -1,488 +0,0 @@
-# coding=utf-8
-#
-# Copyright (C) 2010 Nick Drobchenko, nick@cnc-club.ru
-# Copyright (C) 2005 Aaron Spike, aaron@ekips.org
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# pylint: disable=invalid-name,too-many-locals
-#
-"""
-Bezier calculations
-"""
-
-import cmath
-import math
-
-import numpy
-
-from .transforms import DirectedLineSegment
-from .localization import inkex_gettext as _
-
-# bez = ((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3))
-
-
-def pointdistance(point_a, point_b):
- """The straight line distance between two points"""
- return math.sqrt(
- ((point_b[0] - point_a[0]) ** 2) + ((point_b[1] - point_a[1]) ** 2)
- )
-
-
-def between_point(point_a, point_b, time=0.5):
- """Returns the point between point a and point b"""
- return point_a[0] + time * (point_b[0] - point_a[0]), point_a[1] + time * (
- point_b[1] - point_a[1]
- )
-
-
-def percent_point(point_a, point_b, percent=50.0):
- """Returns between_point but takes percent instead of 0.0-1.0"""
- return between_point(point_a, point_b, percent / 100.0)
-
-
-def root_wrapper(root_a, root_b, root_c, root_d):
- """Get the Cubic function, moic formular of roots, simple root"""
- if root_a:
- # Monics formula, see
- # http://en.wikipedia.org/wiki/Cubic_function#Monic_formula_of_roots
- mono_a, mono_b, mono_c = (root_b / root_a, root_c / root_a, root_d / root_a)
- m = 2.0 * mono_a**3 - 9.0 * mono_a * mono_b + 27.0 * mono_c
- k = mono_a**2 - 3.0 * mono_b
- n = m**2 - 4.0 * k**3
- w1 = -0.5 + 0.5 * cmath.sqrt(-3.0)
- w2 = -0.5 - 0.5 * cmath.sqrt(-3.0)
- if n < 0:
- m1 = pow(complex((m + cmath.sqrt(n)) / 2), 1.0 / 3)
- n1 = pow(complex((m - cmath.sqrt(n)) / 2), 1.0 / 3)
- else:
- if m + math.sqrt(n) < 0:
- m1 = -pow(-(m + math.sqrt(n)) / 2, 1.0 / 3)
- else:
- m1 = pow((m + math.sqrt(n)) / 2, 1.0 / 3)
- if m - math.sqrt(n) < 0:
- n1 = -pow(-(m - math.sqrt(n)) / 2, 1.0 / 3)
- else:
- n1 = pow((m - math.sqrt(n)) / 2, 1.0 / 3)
- return (
- -1.0 / 3 * (mono_a + m1 + n1),
- -1.0 / 3 * (mono_a + w1 * m1 + w2 * n1),
- -1.0 / 3 * (mono_a + w2 * m1 + w1 * n1),
- )
- if root_b:
- det = root_c**2.0 - 4.0 * root_b * root_d
- if det:
- return (
- (-root_c + cmath.sqrt(det)) / (2.0 * root_b),
- (-root_c - cmath.sqrt(det)) / (2.0 * root_b),
- )
- return (-root_c / (2.0 * root_b),)
- if root_c:
- return (1.0 * (-root_d / root_c),)
- return ()
-
-
-def bezlenapprx(sp1, sp2):
- """Return the aproximate length between two beziers"""
- return (
- pointdistance(sp1[1], sp1[2])
- + pointdistance(sp1[2], sp2[0])
- + pointdistance(sp2[0], sp2[1])
- )
-
-
-def cspbezsplit(sp1, sp2, time=0.5):
- """Split a cubic bezier at the time period"""
- m1 = tpoint(sp1[1], sp1[2], time)
- m2 = tpoint(sp1[2], sp2[0], time)
- m3 = tpoint(sp2[0], sp2[1], time)
- m4 = tpoint(m1, m2, time)
- m5 = tpoint(m2, m3, time)
- m = tpoint(m4, m5, time)
- return [[sp1[0][:], sp1[1][:], m1], [m4, m, m5], [m3, sp2[1][:], sp2[2][:]]]
-
-
-def cspbezsplitatlength(sp1, sp2, length=0.5, tolerance=0.001):
- """Split a cubic bezier at length"""
- bez = (sp1[1][:], sp1[2][:], sp2[0][:], sp2[1][:])
- time = beziertatlength(bez, length, tolerance)
- return cspbezsplit(sp1, sp2, time)
-
-
-def cspseglength(sp1, sp2, tolerance=0.001):
- """Get cubic bezier segment length"""
- bez = (sp1[1][:], sp1[2][:], sp2[0][:], sp2[1][:])
- return bezierlength(bez, tolerance)
-
-
-def csplength(csp):
- """Get cubic bezier length"""
- total = 0
- lengths = []
- for sp in csp:
- lengths.append([])
- for i in range(1, len(sp)):
- l = cspseglength(sp[i - 1], sp[i])
- lengths[-1].append(l)
- total += l
- return lengths, total
-
-
-def bezierparameterize(bez):
- """Return the bezier parameter size
- Converts the bezier parametrisation from the default form
- P(t) = (1-t)³ P_1 + 3(1-t)²t P_2 + 3(1-t)t² P_3 + t³ x_4
- to the a form which can be differentiated more easily
- P(t) = a t³ + b t² + c t + P0
-
- Args:
- bez (List[Tuple[float, float]]): the Bezier curve. The elements of the list the
- coordinates of the points (in this order): Start point, Start control point,
- End control point, End point.
-
- Returns:
- Tuple[float, float, float, float, float, float, float, float]:
- the values ax, ay, bx, by, cx, cy, x0, y0
- """
- ((bx0, by0), (bx1, by1), (bx2, by2), (bx3, by3)) = bez
- # parametric bezier
- x0 = bx0
- y0 = by0
- cx = 3 * (bx1 - x0)
- bx = 3 * (bx2 - bx1) - cx
- ax = bx3 - x0 - cx - bx
- cy = 3 * (by1 - y0)
- by = 3 * (by2 - by1) - cy
- ay = by3 - y0 - cy - by
-
- return ax, ay, bx, by, cx, cy, x0, y0
-
-
-def linebezierintersect(arg_a, bez):
- """Where a line and bezier intersect"""
- ((lx1, ly1), (lx2, ly2)) = arg_a
- # parametric line
- dd = lx1
- cc = lx2 - lx1
- bb = ly1
- aa = ly2 - ly1
-
- if aa:
- coef1 = cc / aa
- coef2 = 1
- else:
- coef1 = 1
- coef2 = aa / cc
-
- ax, ay, bx, by, cx, cy, x0, y0 = bezierparameterize(bez)
- # cubic intersection coefficients
- a = coef1 * ay - coef2 * ax
- b = coef1 * by - coef2 * bx
- c = coef1 * cy - coef2 * cx
- d = coef1 * (y0 - bb) - coef2 * (x0 - dd)
-
- roots = root_wrapper(a, b, c, d)
- retval = []
- for i in roots:
- if isinstance(i, complex) and i.imag == 0:
- i = i.real
- if not isinstance(i, complex) and 0 <= i <= 1:
- retval.append(bezierpointatt(bez, i))
- return retval
-
-
-def bezierpointatt(bez, t):
- """Get coords at the given time point along a bezier curve"""
- ax, ay, bx, by, cx, cy, x0, y0 = bezierparameterize(bez)
- x = ax * (t**3) + bx * (t**2) + cx * t + x0
- y = ay * (t**3) + by * (t**2) + cy * t + y0
- return x, y
-
-
-def bezierslopeatt(bez, t):
- """Get slope at the given time point along a bezier curve
- The slope is computed as (dx, dy) where dx = df_x(t)/dt and dy = df_y(t)/dt.
- Note that for lines P1=P2 and P3=P4, so the slope at the end points is dx=dy=0
- (slope not defined).
-
- Args:
- bez (List[Tuple[float, float]]): the Bezier curve. The elements of the list the
- coordinates of the points (in this order): Start point, Start control point,
- End control point, End point.
- t (float): time in the interval [0, 1]
-
- Returns:
- Tuple[float, float]: x and y increment
- """
- ax, ay, bx, by, cx, cy, _, _ = bezierparameterize(bez)
- dx = 3 * ax * (t**2) + 2 * bx * t + cx
- dy = 3 * ay * (t**2) + 2 * by * t + cy
- return dx, dy
-
-
-def beziertatslope(bez, d):
- """Reverse; get time from slope along a bezier curve"""
- ax, ay, bx, by, cx, cy, _, _ = bezierparameterize(bez)
- (dy, dx) = d
- # quadratic coefficients of slope formula
- if dx:
- slope = 1.0 * (dy / dx)
- a = 3 * ay - 3 * ax * slope
- b = 2 * by - 2 * bx * slope
- c = cy - cx * slope
- elif dy:
- slope = 1.0 * (dx / dy)
- a = 3 * ax - 3 * ay * slope
- b = 2 * bx - 2 * by * slope
- c = cx - cy * slope
- else:
- return []
-
- roots = root_wrapper(0, a, b, c)
- retval = []
- for i in roots:
- if isinstance(i, complex) and i.imag == 0:
- i = i.real
- if not isinstance(i, complex) and 0 <= i <= 1:
- retval.append(i)
- return retval
-
-
-def tpoint(p1, p2, t):
- """Linearly interpolate between p1 and p2.
-
- t = 0.0 returns p1, t = 1.0 returns p2.
-
- :return: Interpolated point
- :rtype: tuple
-
- :param p1: First point as sequence of two floats
- :param p2: Second point as sequence of two floats
- :param t: Number between 0.0 and 1.0
- :type t: float
- """
- x1, y1 = p1
- x2, y2 = p2
- return x1 + t * (x2 - x1), y1 + t * (y2 - y1)
-
-
-def beziersplitatt(bez, t):
- """Split bezier at given time"""
- ((bx0, by0), (bx1, by1), (bx2, by2), (bx3, by3)) = bez
- m1 = tpoint((bx0, by0), (bx1, by1), t)
- m2 = tpoint((bx1, by1), (bx2, by2), t)
- m3 = tpoint((bx2, by2), (bx3, by3), t)
- m4 = tpoint(m1, m2, t)
- m5 = tpoint(m2, m3, t)
- m = tpoint(m4, m5, t)
-
- return ((bx0, by0), m1, m4, m), (m, m5, m3, (bx3, by3))
-
-
-def addifclose(bez, l, error=0.001):
- """Gravesen, Add if the line is closed, in-place addition to array l"""
- box = 0
- for i in range(1, 4):
- box += pointdistance(bez[i - 1], bez[i])
- chord = pointdistance(bez[0], bez[3])
- if (box - chord) > error:
- first, second = beziersplitatt(bez, 0.5)
- addifclose(first, l, error)
- addifclose(second, l, error)
- else:
- l[0] += (box / 2.0) + (chord / 2.0)
-
-
-# balfax, balfbx, balfcx, balfay, balfby, balfcy = 0, 0, 0, 0, 0, 0
-
-
-def balf(t, args):
- """Bezier Arc Length Function"""
- ax, bx, cx, ay, by, cy = args
- retval = (ax * (t**2) + bx * t + cx) ** 2 + (ay * (t**2) + by * t + cy) ** 2
- return math.sqrt(retval)
-
-
-def simpson(start, end, maxiter, tolerance, bezier_args):
- """Calculate the length of a bezier curve using Simpson's algorithm:
- http://steve.hollasch.net/cgindex/curves/cbezarclen.html
-
- Args:
- start (int): Start time (between 0 and 1)
- end (int): End time (between start time and 1)
- maxiter (int): Maximum number of iterations. If not a power of 2, the algorithm
- will behave like the value is set to the next power of 2.
- tolerance (float): maximum error ratio
- bezier_args (list): arguments as computed by bezierparametrize()
-
- Returns:
- float: the appoximate length of the bezier curve
- """
-
- n = 2
- multiplier = (end - start) / 6.0
- endsum = balf(start, bezier_args) + balf(end, bezier_args)
- interval = (end - start) / 2.0
- asum = 0.0
- bsum = balf(start + interval, bezier_args)
- est1 = multiplier * (endsum + (2.0 * asum) + (4.0 * bsum))
- est0 = 2.0 * est1
- # print(multiplier, endsum, interval, asum, bsum, est1, est0)
- while n < maxiter and abs(est1 - est0) > tolerance:
- n *= 2
- multiplier /= 2.0
- interval /= 2.0
- asum += bsum
- bsum = 0.0
- est0 = est1
- for i in range(1, n, 2):
- bsum += balf(start + (i * interval), bezier_args)
- est1 = multiplier * (endsum + (2.0 * asum) + (4.0 * bsum))
- # print(multiplier, endsum, interval, asum, bsum, est1, est0)
- return est1
-
-
-def bezierlength(bez, tolerance=0.001, time=1.0):
- """Get length of bezier curve"""
- ax, ay, bx, by, cx, cy, _, _ = bezierparameterize(bez)
- return simpson(0.0, time, 4096, tolerance, [3 * ax, 2 * bx, cx, 3 * ay, 2 * by, cy])
-
-
-def beziertatlength(bez, l=0.5, tolerance=0.001):
- """Get bezier curve time at the length specified"""
- curlen = bezierlength(bez, tolerance, 1.0)
- time = 1.0
- tdiv = time
- targetlen = l * curlen
- diff = curlen - targetlen
- while abs(diff) > tolerance:
- tdiv /= 2.0
- if diff < 0:
- time += tdiv
- else:
- time -= tdiv
- curlen = bezierlength(bez, tolerance, time)
- diff = curlen - targetlen
- return time
-
-
-def maxdist(bez):
- """Get maximum distance within bezier curve"""
- seg = DirectedLineSegment(bez[0], bez[3])
- return max(seg.distance_to_point(*bez[1]), seg.distance_to_point(*bez[2]))
-
-
-def cspsubdiv(csp, flat):
- """Sub-divide cubic sub-paths"""
- for sp in csp:
- subdiv(sp, flat)
-
-
-def subdiv(sp, flat, i=1):
- """sub divide bezier curve"""
- while i < len(sp):
- p0 = sp[i - 1][1]
- p1 = sp[i - 1][2]
- p2 = sp[i][0]
- p3 = sp[i][1]
-
- bez = (p0, p1, p2, p3)
- mdist = maxdist(bez)
- if mdist <= flat:
- i += 1
- else:
- one, two = beziersplitatt(bez, 0.5)
- sp[i - 1][2] = one[1]
- sp[i][0] = two[2]
- p = [one[2], one[3], two[1]]
- sp[i:1] = [p]
-
-
-def csparea(csp):
- """Get area in cubic sub-path"""
- MAT_AREA = numpy.array(
- [[0, 2, 1, -3], [-2, 0, 1, 1], [-1, -1, 0, 2], [3, -1, -2, 0]]
- )
- area = 0.0
- for sp in csp:
- if len(sp) < 2:
- continue
- for x, coord in enumerate(sp): # calculate polygon area
- area += 0.5 * sp[x - 1][1][0] * (coord[1][1] - sp[x - 2][1][1])
- for i in range(1, len(sp)): # add contribution from cubic Bezier
- vec_x = numpy.array(
- [sp[i - 1][1][0], sp[i - 1][2][0], sp[i][0][0], sp[i][1][0]]
- )
- vec_y = numpy.array(
- [sp[i - 1][1][1], sp[i - 1][2][1], sp[i][0][1], sp[i][1][1]]
- )
- vex = numpy.matmul(vec_x, MAT_AREA)
- area += 0.15 * numpy.matmul(vex, vec_y.T)
- return -area
-
-
-def cspcofm(csp):
- """Get cubic sub-path coefficient"""
- MAT_COFM_0 = numpy.array(
- [[0, 35, 10, -45], [-35, 0, 12, 23], [-10, -12, 0, 22], [45, -23, -22, 0]]
- )
-
- MAT_COFM_1 = numpy.array(
- [[0, 15, 3, -18], [-15, 0, 9, 6], [-3, -9, 0, 12], [18, -6, -12, 0]]
- )
-
- MAT_COFM_2 = numpy.array(
- [[0, 12, 6, -18], [-12, 0, 9, 3], [-6, -9, 0, 15], [18, -3, -15, 0]]
- )
-
- MAT_COFM_3 = numpy.array(
- [[0, 22, 23, -45], [-22, 0, 12, 10], [-23, -12, 0, 35], [45, -10, -35, 0]]
- )
- area = csparea(csp)
- xc = 0.0
- yc = 0.0
- if abs(area) < 1.0e-8:
- raise ValueError(_("Area is zero, cannot calculate Center of Mass"))
- for sp in csp:
- for x, coord in enumerate(sp): # calculate polygon moment
- xc += (
- sp[x - 1][1][1]
- * (sp[x - 2][1][0] - coord[1][0])
- * (sp[x - 2][1][0] + sp[x - 1][1][0] + coord[1][0])
- / 6
- )
- yc += (
- sp[x - 1][1][0]
- * (coord[1][1] - sp[x - 2][1][1])
- * (sp[x - 2][1][1] + sp[x - 1][1][1] + coord[1][1])
- / 6
- )
- for i in range(1, len(sp)): # add contribution from cubic Bezier
- vec_x = numpy.array(
- [sp[i - 1][1][0], sp[i - 1][2][0], sp[i][0][0], sp[i][1][0]]
- )
- vec_y = numpy.array(
- [sp[i - 1][1][1], sp[i - 1][2][1], sp[i][0][1], sp[i][1][1]]
- )
-
- def _mul(MAT, vec_x=vec_x, vec_y=vec_y):
- return numpy.matmul(numpy.matmul(vec_x, MAT), vec_y.T)
-
- vec_t = numpy.array(
- [_mul(MAT_COFM_0), _mul(MAT_COFM_1), _mul(MAT_COFM_2), _mul(MAT_COFM_3)]
- )
- xc += numpy.matmul(vec_x, vec_t.T) / 280
- yc += numpy.matmul(vec_y, vec_t.T) / 280
- return -xc / area, -yc / area
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/colors.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/colors.py
deleted file mode 100644
index 637522f..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/colors.py
+++ /dev/null
@@ -1,535 +0,0 @@
-# coding=utf-8
-#
-# Copyright (C) 2006 Jos Hirth, kaioa.com
-# Copyright (C) 2007 Aaron C. Spike
-# Copyright (C) 2009 Monash University
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-"""
-Basic color controls
-"""
-
-
-# All the names that get added to the inkex API itself.
-__all__ = ("Color", "ColorError", "ColorIdError")
-
-SVG_COLOR = {
- "aliceblue": "#f0f8ff",
- "antiquewhite": "#faebd7",
- "aqua": "#00ffff",
- "aquamarine": "#7fffd4",
- "azure": "#f0ffff",
- "beige": "#f5f5dc",
- "bisque": "#ffe4c4",
- "black": "#000000",
- "blanchedalmond": "#ffebcd",
- "blue": "#0000ff",
- "blueviolet": "#8a2be2",
- "brown": "#a52a2a",
- "burlywood": "#deb887",
- "cadetblue": "#5f9ea0",
- "chartreuse": "#7fff00",
- "chocolate": "#d2691e",
- "coral": "#ff7f50",
- "cornflowerblue": "#6495ed",
- "cornsilk": "#fff8dc",
- "crimson": "#dc143c",
- "cyan": "#00ffff",
- "darkblue": "#00008b",
- "darkcyan": "#008b8b",
- "darkgoldenrod": "#b8860b",
- "darkgray": "#a9a9a9",
- "darkgreen": "#006400",
- "darkgrey": "#a9a9a9",
- "darkkhaki": "#bdb76b",
- "darkmagenta": "#8b008b",
- "darkolivegreen": "#556b2f",
- "darkorange": "#ff8c00",
- "darkorchid": "#9932cc",
- "darkred": "#8b0000",
- "darksalmon": "#e9967a",
- "darkseagreen": "#8fbc8f",
- "darkslateblue": "#483d8b",
- "darkslategray": "#2f4f4f",
- "darkslategrey": "#2f4f4f",
- "darkturquoise": "#00ced1",
- "darkviolet": "#9400d3",
- "deeppink": "#ff1493",
- "deepskyblue": "#00bfff",
- "dimgray": "#696969",
- "dimgrey": "#696969",
- "dodgerblue": "#1e90ff",
- "firebrick": "#b22222",
- "floralwhite": "#fffaf0",
- "forestgreen": "#228b22",
- "fuchsia": "#ff00ff",
- "gainsboro": "#dcdcdc",
- "ghostwhite": "#f8f8ff",
- "gold": "#ffd700",
- "goldenrod": "#daa520",
- "gray": "#808080",
- "grey": "#808080",
- "green": "#008000",
- "greenyellow": "#adff2f",
- "honeydew": "#f0fff0",
- "hotpink": "#ff69b4",
- "indianred": "#cd5c5c",
- "indigo": "#4b0082",
- "ivory": "#fffff0",
- "khaki": "#f0e68c",
- "lavender": "#e6e6fa",
- "lavenderblush": "#fff0f5",
- "lawngreen": "#7cfc00",
- "lemonchiffon": "#fffacd",
- "lightblue": "#add8e6",
- "lightcoral": "#f08080",
- "lightcyan": "#e0ffff",
- "lightgoldenrodyellow": "#fafad2",
- "lightgray": "#d3d3d3",
- "lightgreen": "#90ee90",
- "lightgrey": "#d3d3d3",
- "lightpink": "#ffb6c1",
- "lightsalmon": "#ffa07a",
- "lightseagreen": "#20b2aa",
- "lightskyblue": "#87cefa",
- "lightslategray": "#778899",
- "lightslategrey": "#778899",
- "lightsteelblue": "#b0c4de",
- "lightyellow": "#ffffe0",
- "lime": "#00ff00",
- "limegreen": "#32cd32",
- "linen": "#faf0e6",
- "magenta": "#ff00ff",
- "maroon": "#800000",
- "mediumaquamarine": "#66cdaa",
- "mediumblue": "#0000cd",
- "mediumorchid": "#ba55d3",
- "mediumpurple": "#9370db",
- "mediumseagreen": "#3cb371",
- "mediumslateblue": "#7b68ee",
- "mediumspringgreen": "#00fa9a",
- "mediumturquoise": "#48d1cc",
- "mediumvioletred": "#c71585",
- "midnightblue": "#191970",
- "mintcream": "#f5fffa",
- "mistyrose": "#ffe4e1",
- "moccasin": "#ffe4b5",
- "navajowhite": "#ffdead",
- "navy": "#000080",
- "oldlace": "#fdf5e6",
- "olive": "#808000",
- "olivedrab": "#6b8e23",
- "orange": "#ffa500",
- "orangered": "#ff4500",
- "orchid": "#da70d6",
- "palegoldenrod": "#eee8aa",
- "palegreen": "#98fb98",
- "paleturquoise": "#afeeee",
- "palevioletred": "#db7093",
- "papayawhip": "#ffefd5",
- "peachpuff": "#ffdab9",
- "peru": "#cd853f",
- "pink": "#ffc0cb",
- "plum": "#dda0dd",
- "powderblue": "#b0e0e6",
- "purple": "#800080",
- "rebeccapurple": "#663399",
- "red": "#ff0000",
- "rosybrown": "#bc8f8f",
- "royalblue": "#4169e1",
- "saddlebrown": "#8b4513",
- "salmon": "#fa8072",
- "sandybrown": "#f4a460",
- "seagreen": "#2e8b57",
- "seashell": "#fff5ee",
- "sienna": "#a0522d",
- "silver": "#c0c0c0",
- "skyblue": "#87ceeb",
- "slateblue": "#6a5acd",
- "slategray": "#708090",
- "slategrey": "#708090",
- "snow": "#fffafa",
- "springgreen": "#00ff7f",
- "steelblue": "#4682b4",
- "tan": "#d2b48c",
- "teal": "#008080",
- "thistle": "#d8bfd8",
- "tomato": "#ff6347",
- "turquoise": "#40e0d0",
- "violet": "#ee82ee",
- "wheat": "#f5deb3",
- "white": "#ffffff",
- "whitesmoke": "#f5f5f5",
- "yellow": "#ffff00",
- "yellowgreen": "#9acd32",
- "none": None,
-}
-COLOR_SVG = {value: name for name, value in SVG_COLOR.items()}
-
-
-def is_color(color):
- """Determine if it is a color that we can use. If not, leave it unchanged."""
- try:
- return bool(Color(color))
- except ColorError:
- return False
-
-
-def constrain(minim, value, maxim, channel):
- """Returns the value so long as it is between min and max values"""
- if channel == "h": # Hue
- return value % maxim # Wrap around hue value
- return min([maxim, max([minim, value])])
-
-
-class ColorError(KeyError):
- """Specific color parsing error"""
-
-
-class ColorIdError(ColorError):
- """Special color error for gradient and color stop ids"""
-
-
-class Color(list):
- """An RGB array for the color
-
- Can be constructed from valid CSS color attributes, as well as
- tuple/list + color space. Percentage values are supported.
-
- .. versionchanged:: 1.2
- Clarification with respect to values denoting unity: For RGB color channels,
- "1.0", 1.0 and "100%" are treated as 255, while "1" and 1 are treated as 1.
-
- """
-
- red = property(
- lambda self: self.to_rgb()[0], lambda self, value: self._set(0, value)
- )
- green = property(
- lambda self: self.to_rgb()[1], lambda self, value: self._set(1, value)
- )
- blue = property(
- lambda self: self.to_rgb()[2], lambda self, value: self._set(2, value)
- )
- alpha = property(
- lambda self: self.to_rgba()[3],
- lambda self, value: self._set(3, value, ("rgba",)),
- )
- hue = property(
- lambda self: self.to_hsl()[0], lambda self, value: self._set(0, value, ("hsl",))
- )
- saturation = property(
- lambda self: self.to_hsl()[1], lambda self, value: self._set(1, value, ("hsl",))
- )
- lightness = property(
- lambda self: self.to_hsl()[2], lambda self, value: self._set(2, value, ("hsl",))
- )
-
- def __init__(self, color=None, space="rgb"):
- super().__init__()
- if isinstance(color, Color):
- space, color = color.space, list(color)
-
- if isinstance(color, str):
- # String from xml or css attributes
- space, color = self.parse_str(color.strip())
-
- if isinstance(color, int):
- # Number from arg parser colour value
- space, color = self.parse_int(color)
-
- # Empty list means 'none', or no color
- if color is None:
- color = []
-
- if not isinstance(color, (list, tuple)):
- raise ColorError("Not a known a color value")
-
- self.space = space
- try:
- for val in color:
- self.append(val)
- except ValueError as error:
- raise ColorError("Bad color list") from error
-
- def __hash__(self):
- """Allow colors to be hashable"""
- return tuple(self.to_rgba()).__hash__()
-
- def _set(self, index, value, spaces=("rgb", "rgba")):
- """Set the color value in place, limits setter to specific color space"""
- # Named colors are just rgb, so dump name memory
- if self.space == "named":
- self.space = "rgb"
- if not self.space in spaces:
- if index == 3 and self.space == "rgb":
- # Special, add alpha, don't convert back to rgb
- self.space = "rgba"
- self.append(constrain(0.0, float(value), 1.0, "a"))
- return
- # Set in other colour space and convert back and forth
- target = self.to(spaces[0])
- target[index] = constrain(0, int(value), 255, spaces[0][index])
- self[:] = target.to(self.space)
- return
- self[index] = constrain(0, int(value), 255, spaces[0][index])
-
- def append(self, val):
- """Append a value to the local list"""
- if len(self) == len(self.space):
- raise ValueError("Can't add any more values to color.")
-
- if isinstance(val, str):
- val = val.strip()
- if val.endswith("%"):
- val = float(val.strip("%")) / 100
- elif "." in val:
- val = float(val)
- else:
- val = int(val)
-
- end_type = int
- if len(self) == 3: # Alpha value
- val = min([1.0, val])
- end_type = float
- elif isinstance(val, float) and val <= 1.0:
- val *= 255
-
- if isinstance(val, (int, float)):
- super().append(max(end_type(val), 0))
-
- @staticmethod
- def parse_str(color):
- """Creates a rgb int array"""
- # Handle pre-defined svg color values
- if color and color.lower() in SVG_COLOR:
- return "named", Color.parse_str(SVG_COLOR[color.lower()])[1]
-
- if color is None:
- return "rgb", None
-
- if color.startswith("url("):
- raise ColorIdError("Color references other element id, e.g. a gradient")
-
- # Next handle short colors (css: #abc -> #aabbcc)
- if color.startswith("#"):
- # Remove any icc or ilab directives
- # FUTURE: We could use icc or ilab information
- col = color.split(" ")[0]
- if len(col) == 4:
- # pylint: disable=consider-using-f-string
- col = "#{1}{1}{2}{2}{3}{3}".format(*col)
-
- # Convert hex to integers
- try:
- return "rgb", (int(col[1:3], 16), int(col[3:5], 16), int(col[5:], 16))
- except ValueError as error:
- raise ColorError(f"Bad RGB hex color value {col}") from error
-
- # Handle other css color values
- elif "(" in color and ")" in color:
- space, values = color.lower().strip().strip(")").split("(")
- return space, values.split(",")
-
- try:
- return Color.parse_int(int(color))
- except ValueError:
- pass
-
- raise ColorError(f"Unknown color format: {color}")
-
- @staticmethod
- def parse_int(color):
- """Creates an rgb or rgba from a long int"""
- space = "rgb"
- color = [
- ((color >> 24) & 255), # red
- ((color >> 16) & 255), # green
- ((color >> 8) & 255), # blue
- ((color & 255) / 255.0), # opacity
- ]
- if color[-1] == 1.0:
- color.pop()
- else:
- space = "rgba"
- return space, color
-
- def __str__(self):
- """int array to #rrggbb"""
- # pylint: disable=consider-using-f-string
- if not self:
- return "none"
- if self.space == "named":
- rgbhex = "#{0:02x}{1:02x}{2:02x}".format(*self)
- if rgbhex in COLOR_SVG:
- return COLOR_SVG[rgbhex]
- self.space = "rgb"
- if self.space == "rgb":
- return "#{0:02x}{1:02x}{2:02x}".format(*self)
- if self.space == "rgba":
- if self[3] == 1.0:
- return "rgb({:g}, {:g}, {:g})".format(*self[:3])
- return "rgba({:g}, {:g}, {:g}, {:g})".format(*self)
- if self.space == "hsl":
- return "hsl({0:g}, {1:g}, {2:g})".format(*self)
- raise ColorError(f"Can't print colour space '{self.space}'")
-
- def __int__(self):
- """int array to large integer"""
- if not self:
- return -1
- color = self.to_rgba()
- return (
- (color[0] << 24)
- + (color[1] << 16)
- + (color[2] << 8)
- + (int(color[3] * 255))
- )
-
- def to(self, space): # pylint: disable=invalid-name
- """Dynamic caller for to_hsl, to_rgb, etc"""
- return getattr(self, "to_" + space)()
-
- def to_hsl(self):
- """Turn this color into a Hue/Saturation/Lightness colour space"""
- if not self and self.space in ("rgb", "named"):
- return self.to_rgb().to_hsl()
- if self.space == "hsl":
- return self
- if self.space in ("named"):
- return self.to_rgb().to_hsl()
- if self.space == "rgb":
- return Color(rgb_to_hsl(*self.to_floats()), space="hsl")
- raise ColorError(f"Unknown color conversion {self.space}->hsl")
-
- def to_rgb(self):
- """Turn this color into a Red/Green/Blue colour space"""
- if not self and self.space in ("rgb", "named"):
- return Color([0, 0, 0])
- if self.space == "rgb":
- return self
- if self.space in ("rgba", "named"):
- return Color(self[:3], space="rgb")
- if self.space == "hsl":
- return Color(hsl_to_rgb(*self.to_floats()), space="rgb")
- raise ColorError(f"Unknown color conversion {self.space}->rgb")
-
- def to_rgba(self, alpha=1.0):
- """Turn this color isn't an RGB with Alpha colour space"""
- if self.space == "rgba":
- return self
- return Color(self.to_rgb() + [alpha], "rgba")
-
- def to_floats(self):
- """Returns the colour values as percentage floats (0.0 - 1.0)"""
- return [val / 255.0 for val in self]
-
- def to_named(self):
- """Convert this color to a named color if possible"""
- if not self:
- return Color()
- return Color(COLOR_SVG.get(str(self), str(self)))
-
- def interpolate(self, other, fraction):
- """Interpolate two colours by the given fraction
-
- .. versionadded:: 1.1"""
- from .tween import ColorInterpolator # pylint: disable=import-outside-toplevel
-
- return ColorInterpolator(self, other).interpolate(fraction)
-
- @staticmethod
- def isnone(x):
- """Checks if a given color is none
-
- .. versionadded:: 1.2"""
-
- if x is None or (isinstance(x, str) and x.lower() == "none"):
- return True
- return False
-
- @staticmethod
- def iscolor(x, accept_none=False):
- """Checks if a given value can be parsed as a color
-
- .. versionadded:: 1.2"""
- if isinstance(x, str) and (accept_none or not (Color.isnone(x))):
- try:
- Color(x)
- return True
- except ColorError:
- pass
- if isinstance(x, Color):
- return True
- return False
-
-
-def rgb_to_hsl(red, green, blue):
- """RGB to HSL colour conversion"""
- rgb_max = max(red, green, blue)
- rgb_min = min(red, green, blue)
- delta = rgb_max - rgb_min
- hsl = [0.0, 0.0, (rgb_max + rgb_min) / 2.0]
- if delta != 0:
- if hsl[2] <= 0.5:
- hsl[1] = delta / (rgb_max + rgb_min)
- else:
- hsl[1] = delta / (2 - rgb_max - rgb_min)
-
- if red == rgb_max:
- hsl[0] = (green - blue) / delta
- elif green == rgb_max:
- hsl[0] = 2.0 + (blue - red) / delta
- elif blue == rgb_max:
- hsl[0] = 4.0 + (red - green) / delta
-
- hsl[0] /= 6.0
- if hsl[0] < 0:
- hsl[0] += 1
- if hsl[0] > 1:
- hsl[0] -= 1
- return hsl
-
-
-def hsl_to_rgb(hue, sat, light):
- """HSL to RGB Color Conversion"""
- if sat == 0:
- return [light, light, light] # Gray
-
- if light < 0.5:
- val2 = light * (1 + sat)
- else:
- val2 = light + sat - light * sat
- val1 = 2 * light - val2
- return [
- _hue_to_rgb(val1, val2, hue * 6 + 2.0),
- _hue_to_rgb(val1, val2, hue * 6),
- _hue_to_rgb(val1, val2, hue * 6 - 2.0),
- ]
-
-
-def _hue_to_rgb(val1, val2, hue):
- if hue < 0:
- hue += 6.0
- if hue > 6:
- hue -= 6.0
- if hue < 1:
- return val1 + (val2 - val1) * hue
- if hue < 3:
- return val2
- if hue < 4:
- return val1 + (val2 - val1) * (4 - hue)
- return val1
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/command.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/command.py
deleted file mode 100644
index 6bb7292..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/command.py
+++ /dev/null
@@ -1,347 +0,0 @@
-# coding=utf-8
-#
-# Copyright (C) 2019 Martin Owens
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA.
-#
-"""
-This API provides methods for calling Inkscape to execute a given
-Inkscape command. This may be needed for various compiling options
-(e.g., png), running other extensions or performing other options only
-available via the shell API.
-
-Best practice is to avoid using this API except when absolutely necessary,
-since it is resource-intensive to invoke a new Inkscape instance.
-
-However, in any circumstance when it is necessary to call Inkscape, it
-is strongly recommended that you do so through this API, rather than calling
-it yourself, to take advantage of the security settings and testing functions.
-
-"""
-
-import os
-import re
-import sys
-from shutil import which as warlock
-
-from subprocess import Popen, PIPE
-from tempfile import TemporaryDirectory
-from typing import List
-from lxml.etree import ElementTree
-
-from .elements import SvgDocumentElement
-
-INKSCAPE_EXECUTABLE_NAME = os.environ.get("INKSCAPE_COMMAND")
-if INKSCAPE_EXECUTABLE_NAME is None:
- if sys.platform == "win32":
- # prefer inkscape.exe over inkscape.com which spawns a command window
- INKSCAPE_EXECUTABLE_NAME = "inkscape.exe"
- else:
- INKSCAPE_EXECUTABLE_NAME = "inkscape"
-
-
-class CommandNotFound(IOError):
- """Command is not found"""
-
-
-class ProgramRunError(ValueError):
- """A specialized ValueError that is raised when a call to an external command fails.
- It stores additional information about a failed call to an external program.
-
- If only the ``program`` parameter is given, it is interpreted as the error message.
- Otherwise, the error message is compiled from all constructor parameters."""
-
- program: str
- """The absolute path to the called executable"""
-
- returncode: int
- """Return code of the program call"""
-
- stderr: str
- """stderr stream output of the call"""
-
- stdout: str
- """stdout stream output of the call"""
-
- arguments: List
- """Arguments of the call"""
-
- def __init__(self, program, returncode=None, stderr=None, stdout=None, args=None):
- self.program = program
- self.returncode = returncode
- self.stderr = stderr
- self.stdout = stdout
- self.arguments = args
- super().__init__(str(self))
-
- def __str__(self):
- if self.returncode is None:
- return self.program
- return (
- f"Return Code: {self.returncode}: {self.stderr}\n{self.stdout}"
- f"\nargs: {self.args}"
- )
-
-
-def which(program):
- """
- Attempt different methods of trying to find if the program exists.
- """
- if os.path.isabs(program) and os.path.isfile(program):
- return program
- # On Windows, shutil.which may give preference to .py files in the current directory
- # (such as pdflatex.py), e.g. if .PY is in pathext, because the current directory is
- # prepended to PATH. This can be suppressed by explicitly appending the current
- # directory.
-
- try:
- if sys.platform == "win32":
- prog = warlock(program, path=os.environ["PATH"] + ";" + os.curdir)
- if prog:
- return prog
- except ImportError:
- pass
-
- try:
- # Python3 only version of which
- prog = warlock(program)
- if prog:
- return prog
- except ImportError:
- pass # python2
-
- # There may be other methods for doing a `which` command for other
- # operating systems; These should go here as they are discovered.
-
- raise CommandNotFound(f"Can not find the command: '{program}'")
-
-
-def write_svg(svg, *filename):
- """Writes an svg to the given filename"""
- filename = os.path.join(*filename)
- if os.path.isfile(filename):
- return filename
- with open(filename, "wb") as fhl:
- if isinstance(svg, SvgDocumentElement):
- svg = ElementTree(svg)
- if hasattr(svg, "write"):
- # XML document
- svg.write(fhl)
- elif isinstance(svg, bytes):
- fhl.write(svg)
- else:
- raise ValueError("Not sure what type of SVG data this is.")
- return filename
-
-
-def to_arg(arg, oldie=False):
- """Convert a python argument to a command line argument"""
- if isinstance(arg, (tuple, list)):
- (arg, val) = arg
- arg = "-" + arg
- if len(arg) > 2 and not oldie:
- arg = "-" + arg
- if val is True:
- return arg
- if val is False:
- return None
- return f"{arg}={str(val)}"
- return str(arg)
-
-
-def to_args(prog, *positionals, **arguments):
- """Compile arguments and keyword arguments into a list of strings which Popen will
- understand.
-
- :param prog:
- Program executable prepended to the output.
- :type first: ``str``
-
- :Arguments:
- * (``str``) -- String added as given
- * (``tuple``) -- Ordered version of Keyword Arguments, see below
-
- :Keyword Arguments:
- * *name* (``str``) --
- Becomes ``--name="val"``
- * *name* (``bool``) --
- Becomes ``--name``
- * *name* (``list``) --
- Becomes ``--name="val1"`` ...
- * *n* (``str``) --
- Becomes ``-n=val``
- * *n* (``bool``) --
- Becomes ``-n``
-
- :return: Returns a list of compiled arguments ready for Popen.
- :rtype: ``list[str]``
- """
- args = [prog]
- oldie = arguments.pop("oldie", False)
- for arg, value in arguments.items():
- arg = arg.replace("_", "-").strip()
-
- if isinstance(value, tuple):
- value = list(value)
- elif not isinstance(value, list):
- value = [value]
-
- for val in value:
- args.append(to_arg((arg, val), oldie))
-
- args += [to_arg(pos, oldie) for pos in positionals if pos is not None]
- # Filter out empty non-arguments
- return [arg for arg in args if arg is not None]
-
-
-def to_args_sorted(prog, *positionals, **arguments):
- """same as :func:`to_args`, but keyword arguments are sorted beforehand
-
- .. versionadded:: 1.2"""
- return to_args(prog, *positionals, **dict(sorted(arguments.items())))
-
-
-def _call(program, *args, **kwargs):
- stdin = kwargs.pop("stdin", None)
- if isinstance(stdin, str):
- stdin = stdin.encode("utf-8")
- inpipe = PIPE if stdin else None
-
- args = to_args(which(program), *args, **kwargs)
-
- kwargs = {}
- if sys.platform == "win32":
- kwargs["creationflags"] = 0x08000000 # create no console window
-
- with Popen(
- args,
- shell=False, # Never have shell=True
- stdin=inpipe, # StdIn not used (yet)
- stdout=PIPE, # Grab any output (return it)
- stderr=PIPE, # Take all errors, just incase
- **kwargs,
- ) as process:
- (stdout, stderr) = process.communicate(input=stdin)
- if process.returncode == 0:
- return stdout
- raise ProgramRunError(program, process.returncode, stderr, stdout, args)
-
-
-def call(program, *args, **kwargs):
- """
- Generic caller to open any program and return its stdout::
-
- stdout = call('executable', arg1, arg2, dash_dash_arg='foo', d=True, ...)
-
- Will raise :class:`ProgramRunError` if return code is not 0.
-
- Keyword arguments:
- return_binary: Should stdout return raw bytes (default: False)
-
- .. versionadded:: 1.1
- stdin: The string or bytes containing the stdin (default: None)
-
- All other arguments converted using :func:`to_args` function.
- """
- # We use this long input because it's less likely to conflict with --binary=
- binary = kwargs.pop("return_binary", False)
- stdout = _call(program, *args, **kwargs)
- # Convert binary to string when we wish to have strings we do this here
- # so the mock tests will also run the conversion (always returns bytes)
- if not binary and isinstance(stdout, bytes):
- return stdout.decode(sys.stdout.encoding or "utf-8")
- return stdout
-
-
-def inkscape(svg_file, *args, **kwargs):
- """
- Call Inkscape with the given svg_file and the given arguments, see call().
-
- Returns the stdout of the call.
-
- .. versionchanged:: 1.3
- If the "actions" kwargs parameter is passed, it is checked whether the length of
- the action string might lead to issues with the Windows CLI call character
- limit. In this case, Inkscape is called in `--shell`
- mode and the actions are fed in via stdin. This avoids violating the character
- limit for command line arguments on Windows, which results in errors like this:
- `[WinError 206] The filename or extension is too long`.
- This workaround is also possible when calling Inkscape with long arguments
- to `--export-id` and `--query-id`, by converting the call to the appropriate
- action sequence. The stdout is cleaned to resemble non-interactive mode.
- """
- os.environ["SELF_CALL"] = "true"
- actions = kwargs.get("actions", None)
- strip_stdout = False
- # Keep some safe margin to the 8191 character limit.
- if actions is not None and len(actions) > 7000:
- args = args + ("--shell",)
- kwargs["stdin"] = actions
- kwargs.pop("actions")
- strip_stdout = True
- stdout = call(INKSCAPE_EXECUTABLE_NAME, svg_file, *args, **kwargs)
- if strip_stdout:
- split = re.split(r"\n> ", stdout)
- if len(split) > 1:
- if "\n" in split[1]:
- stdout = "\n".join(split[1].split("\n")[1:])
- else:
- stdout = ""
- return stdout
-
-
-def inkscape_command(svg, select=None, actions=None, *args, **kwargs):
- """
- Executes Inkscape batch actions with the given input and returns a new .
-
- inkscape_command('', [select=...], [actions=...], [...])
- """
- with TemporaryDirectory(prefix="inkscape-command") as tmpdir:
- svg_file = write_svg(svg, tmpdir, "input.svg")
- select = ("select", select) if select else None
- inkscape(
- svg_file,
- select,
- batch_process=True,
- export_overwrite=True,
- actions=actions,
- *args,
- **kwargs,
- )
- with open(svg_file, "rb") as fhl:
- return fhl.read()
-
-
-def take_snapshot(svg, dirname, name="snapshot", ext="png", dpi=96, **kwargs):
- """
- Take a snapshot of the given svg file.
-
- Resulting filename is yielded back, after generator finishes, the
- file is deleted so you must deal with the file inside the for loop.
- """
- svg_file = write_svg(svg, dirname, name + ".svg")
- ext_file = os.path.join(dirname, name + "." + str(ext).lower())
- inkscape(
- svg_file, export_dpi=dpi, export_filename=ext_file, export_type=ext, **kwargs
- )
- return ext_file
-
-
-def is_inkscape_available():
- """Return true if the Inkscape executable is available."""
- try:
- return bool(which(INKSCAPE_EXECUTABLE_NAME))
- except CommandNotFound:
- return False
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/css.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/css.py
deleted file mode 100644
index ebb9234..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/css.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# coding=utf-8
-#
-# Copyright (C) 2021 - Jonathan Neuhauser
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-"""Toplevel CSS utils that do not depend on other inkex functionality
-
-.. versionadded:: 1.2
- Previously a part of :py:mod:`inkex.styles`"""
-
-
-import re
-import cssselect
-
-
-class ConditionalRule:
- """A single css rule
-
- .. versionchanged:: 1.2
- The CSS rule is now processed using cssselect."""
-
- step_to_xpath = [
- # namespace addition
- (re.compile(r"(::|\/)([a-z]+)(?=\W)(?!-)"), r"\1svg:\2"),
- ]
-
- def __init__(self, rule):
- self.rule = rule.strip()
- self.selector = cssselect.parse(self.rule)[0]
-
- def __str__(self):
- return self.rule
-
- def to_xpath(self):
- """Attempt to convert the rule into a simplified xpath"""
- # the space in the end is needed for the negative lookbehind in the regex, will
- # be removed on return
- ret = cssselect.HTMLTranslator().selector_to_xpath(self.selector) + " "
- for matcher, replacer in self.step_to_xpath:
- ret = matcher.sub(replacer, ret)
- return ret.strip()
-
- def get_specificity(self):
- """gets the css specificity of this selector
-
- .. versionadded:: 1.2"""
- return self.selector.specificity()
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/README.rst b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/README.rst
deleted file mode 100644
index df3e2dc..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/README.rst
+++ /dev/null
@@ -1,4 +0,0 @@
-# coding=utf-8This directory contains compatibility layers for all the `simple` modules, such as `simplepath` and `simplestyle`
-
-This directory IS NOT a module path, to denote this we are using a dash in the name and there is no '__init__.py'
-
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/bezmisc.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/bezmisc.py
deleted file mode 100644
index 92b63f6..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/bezmisc.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# coding=utf-8
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# pylint: disable=invalid-name,unused-argument
-"""Deprecated bezmisc API"""
-
-from inkex.deprecated import deprecate
-from inkex import bezier
-
-bezierparameterize = deprecate(bezier.bezierparameterize)
-linebezierintersect = deprecate(bezier.linebezierintersect)
-bezierpointatt = deprecate(bezier.bezierpointatt)
-bezierslopeatt = deprecate(bezier.bezierslopeatt)
-beziertatslope = deprecate(bezier.beziertatslope)
-tpoint = deprecate(bezier.tpoint)
-beziersplitatt = deprecate(bezier.beziersplitatt)
-pointdistance = deprecate(bezier.pointdistance)
-Gravesen_addifclose = deprecate(bezier.addifclose)
-balf = deprecate(bezier.balf)
-bezierlengthSimpson = deprecate(bezier.bezierlength)
-beziertatlength = deprecate(bezier.beziertatlength)
-bezierlength = bezierlengthSimpson
-
-
-@deprecate
-def Simpson(func, a, b, n_limit, tolerance):
- """bezier.simpson(a, b, n_limit, tolerance, balf_arguments)"""
- raise AttributeError(
- """Because bezmisc.Simpson used global variables, it's not possible to
- call the replacement code automatically. In fact it's unlikely you were
- using the code or functionality you think you were since it's a highly
- broken way of writing python."""
- )
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/cspsubdiv.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/cspsubdiv.py
deleted file mode 100644
index 91b2237..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/cspsubdiv.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# coding=utf-8
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# pylint: disable=invalid-name
-"""Deprecated cspsubdiv API"""
-
-from inkex.deprecated import deprecate
-from inkex import bezier
-
-maxdist = deprecate(bezier.maxdist)
-cspsubdiv = deprecate(bezier.cspsubdiv)
-subdiv = deprecate(bezier.subdiv)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/cubicsuperpath.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/cubicsuperpath.py
deleted file mode 100644
index cb6f124..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/cubicsuperpath.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# coding=utf-8
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# pylint: disable=invalid-name
-"""Deprecated cubic super path API"""
-
-from inkex.deprecated import deprecate
-from inkex import paths
-
-
-@deprecate
-def ArcToPath(p1, params):
- return paths.arc_to_path(p1, params)
-
-
-@deprecate
-def CubicSuperPath(simplepath):
- return paths.Path(simplepath).to_superpath()
-
-
-@deprecate
-def unCubicSuperPath(csp):
- return paths.CubicSuperPath(csp).to_path().to_arrays()
-
-
-@deprecate
-def parsePath(d):
- return paths.CubicSuperPath(paths.Path(d))
-
-
-@deprecate
-def formatPath(p):
- return str(paths.Path(unCubicSuperPath(p)))
-
-
-matprod = deprecate(paths.matprod)
-rotmat = deprecate(paths.rotmat)
-applymat = deprecate(paths.applymat)
-norm = deprecate(paths.norm)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/ffgeom.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/ffgeom.py
deleted file mode 100644
index 2feb3bd..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/ffgeom.py
+++ /dev/null
@@ -1,92 +0,0 @@
-# coding=utf-8
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# pylint: disable=invalid-name,missing-docstring
-"""Deprecated ffgeom API"""
-
-from collections import namedtuple
-
-from inkex.deprecated import deprecate
-from inkex.transforms import DirectedLineSegment as NewSeg
-
-try:
- NaN = float("NaN")
-except ValueError:
- PosInf = 1e300000
- NaN = PosInf / PosInf
-
-
-class Point(namedtuple("Point", "x y")):
- __slots__ = ()
-
- def __getitem__(self, key):
- if isinstance(key, str):
- key = "xy".index(key)
- return super(Point, self).__getitem__(key)
-
-
-class Segment(NewSeg):
- @deprecate
- def __init__(self, e0, e1):
- """inkex.transforms.Segment(((x1, y1), (x2, y2)))"""
- if isinstance(e0, dict):
- e0 = (e0["x"], e0["y"])
- if isinstance(e1, dict):
- e1 = (e1["x"], e1["y"])
- super(Segment, self).__init__((e0, e1))
-
- def __getitem__(self, key):
- if key:
- return {"x": self.x.maximum, "y": self.y.maximum}
- return {"x": self.x.minimum, "y": self.y.minimum}
-
- delta_x = lambda self: self.width
- delta_y = lambda self: self.height
- run = delta_x
- rise = delta_y
-
- def distanceToPoint(self, p):
- return self.distance_to_point(p["x"], p["y"])
-
- def perpDistanceToPoint(self, p):
- return self.perp_distance(p["x"], p["y"])
-
- def angle(self):
- return super(Segment, self).angle
-
- def length(self):
- return super(Segment, self).length
-
- def pointAtLength(self, length):
- return self.point_at_length(length)
-
- def pointAtRatio(self, ratio):
- return self.point_at_ratio(ratio)
-
- def createParallel(self, p):
- self.parallel(p["x"], p["y"])
-
-
-@deprecate
-def intersectSegments(s1, s2):
- """transforms.Segment(s1).intersect(s2)"""
- return Point(*s1.intersect(s2))
-
-
-@deprecate
-def dot(s1, s2):
- """transforms.Segment(s1).dot(s2)"""
- return s1.dot(s2)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/run_command.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/run_command.py
deleted file mode 100644
index 84f4a4b..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/run_command.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# coding=utf-8
-#
-# Copyright (C) 2008 Stephen Silver
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-#
-"""
-Deprecated module for running SVG-generating commands in Inkscape extensions
-"""
-import os
-import sys
-import tempfile
-from subprocess import Popen, PIPE
-
-from inkex.deprecated import deprecate
-
-
-def run(command_format, prog_name):
- """inkex.commands.call(...)"""
- svgfile = tempfile.mktemp(".svg")
- command = command_format % svgfile
- msg = None
- # ps2pdf may attempt to write to the current directory, which may not
- # be writeable, so we switch to the temp directory first.
- try:
- os.chdir(tempfile.gettempdir())
- except IOError:
- pass
-
- try:
- proc = Popen(command, shell=True, stdout=PIPE, stderr=PIPE)
- return_code = proc.wait()
- out = proc.stdout.read()
- err = proc.stderr.read()
-
- if msg is None:
- if return_code:
- msg = "{} failed:\n{}\n{}\n".format(prog_name, out, err)
- elif err:
- sys.stderr.write(
- "{} executed but logged the following error:\n{}\n{}\n".format(
- prog_name, out, err
- )
- )
- except Exception as inst:
- msg = "Error attempting to run {}: {}".format(prog_name, str(inst))
-
- # If successful, copy the output file to stdout.
- if msg is None:
- if os.name == "nt": # make stdout work in binary on Windows
- import msvcrt
-
- msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
- try:
- with open(svgfile, "rb") as fhl:
- sys.stdout.write(fhl.read().decode(sys.stdout.encoding))
- except IOError as inst:
- msg = "Error reading temporary file: {}".format(str(inst))
-
- try:
- # Clean up.
- os.remove(svgfile)
- except (IOError, OSError):
- pass
-
- # Output error message (if any) and exit.
- return msg
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/simplepath.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/simplepath.py
deleted file mode 100644
index 6eaaf3b..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/simplepath.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# coding=utf-8
-# COPYRIGHT
-#
-# pylint: disable=invalid-name
-#
-"""
-Depreicated simplepath replacements with documentation
-"""
-
-import math
-from inkex.deprecated import deprecate, DeprecatedDict
-from inkex.transforms import Transform
-from inkex.paths import Path
-
-pathdefs = DeprecatedDict(
- {
- "M": ["L", 2, [float, float], ["x", "y"]],
- "L": ["L", 2, [float, float], ["x", "y"]],
- "H": ["H", 1, [float], ["x"]],
- "V": ["V", 1, [float], ["y"]],
- "C": [
- "C",
- 6,
- [float, float, float, float, float, float],
- ["x", "y", "x", "y", "x", "y"],
- ],
- "S": ["S", 4, [float, float, float, float], ["x", "y", "x", "y"]],
- "Q": ["Q", 4, [float, float, float, float], ["x", "y", "x", "y"]],
- "T": ["T", 2, [float, float], ["x", "y"]],
- "A": [
- "A",
- 7,
- [float, float, float, int, int, float, float],
- ["r", "r", "a", 0, "s", "x", "y"],
- ],
- "Z": ["L", 0, [], []],
- }
-)
-
-
-@deprecate
-def parsePath(d):
- """element.path.to_arrays()"""
- return Path(d).to_arrays()
-
-
-@deprecate
-def formatPath(a):
- """str(element.path) or str(Path(array))"""
- return str(Path(a))
-
-
-@deprecate
-def translatePath(p, x, y):
- """Path(array).translate(x, y)"""
- p[:] = Path(p).translate(x, y).to_arrays()
-
-
-@deprecate
-def scalePath(p, x, y):
- """Path(array).scale(x, y)"""
- p[:] = Path(p).scale(x, y).to_arrays()
-
-
-@deprecate
-def rotatePath(p, a, cx=0, cy=0):
- """Path(array).rotate(angle_degrees, (center_x, center_y))"""
- p[:] = Path(p).rotate(math.degrees(a), (cx, cy)).to_arrays()
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/simplestyle.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/simplestyle.py
deleted file mode 100644
index cab6d9d..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/simplestyle.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# coding=utf-8
-# COPYRIGHT
-"""DOCSTRING"""
-
-import inkex
-from inkex.colors import SVG_COLOR as svgcolors
-from inkex.deprecated import deprecate
-
-
-@deprecate
-def parseStyle(s):
- """dict(inkex.Style.parse_str(s))"""
- return dict(inkex.Style.parse_str(s))
-
-
-@deprecate
-def formatStyle(a):
- """str(inkex.Style(a))"""
- return str(inkex.Style(a))
-
-
-@deprecate
-def isColor(c):
- """inkex.colors.is_color(c)"""
- return inkex.colors.is_color(c)
-
-
-@deprecate
-def parseColor(c):
- """inkex.Color(c).to_rgb()"""
- return tuple(inkex.Color(c).to_rgb())
-
-
-@deprecate
-def formatColoria(a):
- """str(inkex.Color(a))"""
- return str(inkex.Color(a))
-
-
-@deprecate
-def formatColorfa(a):
- """str(inkex.Color(a))"""
- return str(inkex.Color(a))
-
-
-@deprecate
-def formatColor3i(r, g, b):
- """str(inkex.Color((r, g, b)))"""
- return str(inkex.Color((r, g, b)))
-
-
-@deprecate
-def formatColor3f(r, g, b):
- """str(inkex.Color((r, g, b)))"""
- return str(inkex.Color((r, g, b)))
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/simpletransform.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/simpletransform.py
deleted file mode 100644
index f408cd0..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated-simple/simpletransform.py
+++ /dev/null
@@ -1,122 +0,0 @@
-# coding=utf-8
-#
-# pylint: disable=invalid-name
-#
-"""
-Depreicated simpletransform replacements with documentation
-"""
-
-import warnings
-
-from inkex.deprecated import deprecate
-from inkex.transforms import Transform, BoundingBox, cubic_extrema
-from inkex.paths import Path
-
-import inkex, cubicsuperpath
-
-
-def _lists(mat):
- return [list(row) for row in mat]
-
-
-@deprecate
-def parseTransform(transf, mat=None):
- """Transform(str).matrix"""
- t = Transform(transf)
- if mat is not None:
- t = Transform(mat) @ t
- return _lists(t.matrix)
-
-
-@deprecate
-def formatTransform(mat):
- """str(Transform(mat))"""
- if len(mat) == 3:
- warnings.warn("3x3 matrices not suported")
- mat = mat[:2]
- return str(Transform(mat))
-
-
-@deprecate
-def invertTransform(mat):
- """-Transform(mat)"""
- return _lists((-Transform(mat)).matrix)
-
-
-@deprecate
-def composeTransform(mat1, mat2):
- """Transform(M1) * Transform(M2)"""
- return _lists((Transform(mat1) @ Transform(mat2)).matrix)
-
-
-@deprecate
-def composeParents(node, mat):
- """elem.composed_transform() or elem.transform * Transform(mat)"""
- return (node.transform @ Transform(mat)).matrix
-
-
-@deprecate
-def applyTransformToNode(mat, node):
- """elem.transform = Transform(mat) * elem.transform"""
- node.transform = Transform(mat) @ node.transform
-
-
-@deprecate
-def applyTransformToPoint(mat, pt):
- """Transform(mat).apply_to_point(pt)"""
- pt2 = Transform(mat).apply_to_point(pt)
- # Apply in place as original method was modifying arrays in place.
- # but don't do this in your code! This is not good code design.
- pt[0] = pt2[0]
- pt[1] = pt2[1]
-
-
-@deprecate
-def applyTransformToPath(mat, path):
- """Path(path).transform(mat)"""
- return Path(path).transform(Transform(mat)).to_arrays()
-
-
-@deprecate
-def fuseTransform(node):
- """node.apply_transform()"""
- return node.apply_transform()
-
-
-@deprecate
-def boxunion(b1, b2):
- """list(BoundingBox(b1) + BoundingBox(b2))"""
- bbox = BoundingBox(b1[:2], b1[2:]) + BoundingBox(b2[:2], b2[2:])
- return bbox.x.minimum, bbox.x.maximum, bbox.y.minimum, bbox.y.maximum
-
-
-@deprecate
-def roughBBox(path):
- """list(Path(path)).bounding_box())"""
- bbox = Path(path).bounding_box()
- return bbox.x.minimum, bbox.x.maximum, bbox.y.minimum, bbox.y.maximum
-
-
-@deprecate
-def refinedBBox(path):
- """list(Path(path)).bounding_box())"""
- bbox = Path(path).bounding_box()
- return bbox.x.minimum, bbox.x.maximum, bbox.y.minimum, bbox.y.maximum
-
-
-@deprecate
-def cubicExtrema(y0, y1, y2, y3):
- """from inkex.transforms import cubic_extrema"""
- return cubic_extrema(y0, y1, y2, y3)
-
-
-@deprecate
-def computeBBox(aList, mat=[[1, 0, 0], [0, 1, 0]]):
- """sum([node.bounding_box() for node in aList])"""
- return sum([node.bounding_box() for node in aList], None)
-
-
-@deprecate
-def computePointInNode(pt, node, mat=[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]):
- """(-Transform(node.transform * mat)).apply_to_point(pt)"""
- return (-Transform(node.transform * mat)).apply_to_point(pt)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated/__init__.py
deleted file mode 100644
index 180a1c2..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from .main import *
-from .meta import deprecate, _deprecated
-from .deprecatedeffect import DeprecatedEffect, Effect
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated/deprecatedeffect.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated/deprecatedeffect.py
deleted file mode 100644
index ee13670..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated/deprecatedeffect.py
+++ /dev/null
@@ -1,313 +0,0 @@
-# coding=utf-8
-#
-# Copyright (C) 2018 - Martin Owens
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-"""
-Deprecation functionality for the pre-1.0 Inkex main effect class.
-"""
-#
-# We ignore a lot of pylint warnings here:
-#
-# pylint: disable=invalid-name,unused-argument,missing-docstring,too-many-public-methods
-#
-
-import sys
-import argparse
-from argparse import ArgumentParser
-
-from .. import utils
-from .. import base
-from ..base import SvgThroughMixin, InkscapeExtension
-from ..localization import inkex_gettext as _
-from .meta import _deprecated
-
-
-class DeprecatedEffect:
- """An Inkscape effect, takes SVG in and outputs SVG, providing a deprecated layer"""
-
- options = argparse.Namespace()
-
- def __init__(self):
- super().__init__()
- self._doc_ids = None
- self._args = None
-
- # These are things we reference in the deprecated code, they are provided
- # by the new effects code, but we want to keep this as a Mixin so these
- # items will keep pylint happy and let use check our code as we write.
- if not hasattr(self, "svg"):
- from ..elements import SvgDocumentElement
-
- self.svg = SvgDocumentElement()
- if not hasattr(self, "arg_parser"):
- self.arg_parser = ArgumentParser()
- if not hasattr(self, "run"):
- self.run = self.affect
-
- @classmethod
- def _deprecated(
- cls, name, msg=_("{} is deprecated and should be removed"), stack=3
- ):
- """Give the user a warning about their extension using a deprecated API"""
- _deprecated(
- msg.format("Effect." + name, cls=cls.__module__ + "." + cls.__name__),
- stack=stack,
- )
-
- @property
- def OptionParser(self):
- self._deprecated(
- "OptionParser",
- _(
- "{} or `optparse` has been deprecated and replaced with `argparser`. "
- "You must change `self.OptionParser.add_option` to "
- "`self.arg_parser.add_argument`; the arguments are similar."
- ),
- )
- return self
-
- def add_option(self, *args, **kw):
- # Convert type string into type method as needed
- if "type" in kw:
- kw["type"] = {
- "string": str,
- "int": int,
- "float": float,
- "inkbool": utils.Boolean,
- }.get(kw["type"])
- if kw.get("action", None) == "store":
- # Default store action not required, removed.
- kw.pop("action")
- args = [arg for arg in args if arg != ""]
- self.arg_parser.add_argument(*args, **kw)
-
- def effect(self):
- self._deprecated(
- "effect",
- _(
- "{} method is now a required method. It should "
- "be created on {cls}, even if it does nothing."
- ),
- )
-
- @property
- def current_layer(self):
- self._deprecated(
- "current_layer",
- _(
- "{} is now a method in the SvgDocumentElement class. "
- "Use `self.svg.get_current_layer()` instead."
- ),
- )
- return self.svg.get_current_layer()
-
- @property
- def view_center(self):
- self._deprecated(
- "view_center",
- _(
- "{} is now a method in the SvgDocumentElement class. "
- "Use `self.svg.get_center_position()` instead."
- ),
- )
- return self.svg.namedview.center
-
- @property
- def selected(self):
- self._deprecated(
- "selected",
- _(
- "{} is now a dict in the SvgDocumentElement class. "
- "Use `self.svg.selected`."
- ),
- )
- return {elem.get("id"): elem for elem in self.svg.selected}
-
- @property
- def doc_ids(self):
- self._deprecated(
- "doc_ids",
- _(
- "{} is now a method in the SvgDocumentElement class. "
- "Use `self.svg.get_ids()` instead."
- ),
- )
- if self._doc_ids is None:
- self._doc_ids = dict.fromkeys(self.svg.get_ids())
- return self._doc_ids
-
- def getdocids(self):
- self._deprecated(
- "getdocids", _("Use `self.svg.get_ids()` instead of {} and `doc_ids`.")
- )
- self._doc_ids = None
- self.svg.ids.clear()
-
- def getselected(self):
- self._deprecated("getselected", _("{} has been removed"))
-
- def getElementById(self, eid):
- self._deprecated(
- "getElementById",
- _(
- "{} is now a method in the SvgDocumentElement class. "
- "Use `self.svg.getElementById(eid)` instead."
- ),
- )
- return self.svg.getElementById(eid)
-
- def xpathSingle(self, xpath):
- self._deprecated(
- "xpathSingle",
- _(
- "{} is now a new method in the SvgDocumentElement class. "
- "Use `self.svg.getElement(path)` instead."
- ),
- )
- return self.svg.getElement(xpath)
-
- def getParentNode(self, node):
- self._deprecated(
- "getParentNode",
- _("{} is no longer in use. Use the lxml `.getparent()` method instead."),
- )
- return node.getparent()
-
- def getNamedView(self):
- self._deprecated(
- "getNamedView",
- _(
- "{} is now a property of the SvgDocumentElement class. "
- "Use `self.svg.namedview` to access this element."
- ),
- )
- return self.svg.namedview
-
- def createGuide(self, posX, posY, angle):
- from ..elements import Guide
-
- self._deprecated(
- "createGuide",
- _(
- "{} is now a method of the namedview element object. "
- "Use `self.svg.namedview.add(Guide().move_to(x, y, a))` instead."
- ),
- )
- return self.svg.namedview.add(Guide().move_to(posX, posY, angle))
-
- def affect(
- self, args=sys.argv[1:], output=True
- ): # pylint: disable=dangerous-default-value
- # We need a list as the default value to preserve backwards compatibility
- self._deprecated(
- "affect", _("{} is now `Effect.run()`. The `output` argument has changed.")
- )
- self._args = args[-1:]
- return self.run(args=args)
-
- @property
- def args(self):
- self._deprecated("args", _("self.args[-1] is now self.options.input_file."))
- return self._args
-
- @property
- def svg_file(self):
- self._deprecated("svg_file", _("self.svg_file is now self.options.input_file."))
- return self.options.input_file
-
- def save_raw(self, ret):
- # Derived class may implement "output()"
- # Attention: 'cubify.py' implements __getattr__ -> hasattr(self, 'output')
- # returns True
- if hasattr(self.__class__, "output"):
- self._deprecated("output", "Use `save()` or `save_raw()` instead.", stack=5)
- return getattr(self, "output")()
- return base.InkscapeExtension.save_raw(self, ret)
-
- def uniqueId(self, old_id, make_new_id=True):
- self._deprecated(
- "uniqueId",
- _(
- "{} is now a method in the SvgDocumentElement class. "
- " Use `self.svg.get_unique_id(old_id)` instead."
- ),
- )
- return self.svg.get_unique_id(old_id)
-
- def getDocumentWidth(self):
- self._deprecated(
- "getDocumentWidth",
- _(
- "{} is now a property of the SvgDocumentElement class. "
- "Use `self.svg.width` instead."
- ),
- )
- return self.svg.get("width")
-
- def getDocumentHeight(self):
- self._deprecated(
- "getDocumentHeight",
- _(
- "{} is now a property of the SvgDocumentElement class. "
- "Use `self.svg.height` instead."
- ),
- )
- return self.svg.get("height")
-
- def getDocumentUnit(self):
- self._deprecated(
- "getDocumentUnit",
- _(
- "{} is now a property of the SvgDocumentElement class. "
- "Use `self.svg.unit` instead."
- ),
- )
- return self.svg.unit
-
- def unittouu(self, string):
- self._deprecated(
- "unittouu",
- _(
- "{} is now a method in the SvgDocumentElement class. "
- "Use `self.svg.unittouu(str)` instead."
- ),
- )
- return self.svg.unittouu(string)
-
- def uutounit(self, val, unit):
- self._deprecated(
- "uutounit",
- _(
- "{} is now a method in the SvgDocumentElement class. "
- "Use `self.svg.uutounit(value, unit)` instead."
- ),
- )
- return self.svg.uutounit(val, unit)
-
- def addDocumentUnit(self, value):
- self._deprecated(
- "addDocumentUnit",
- _(
- "{} is now a method in the SvgDocumentElement class. "
- "Use `self.svg.add_unit(value)` instead."
- ),
- )
- return self.svg.add_unit(value)
-
-
-class Effect(SvgThroughMixin, DeprecatedEffect, InkscapeExtension):
- """An Inkscape effect, takes SVG in and outputs SVG"""
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated/main.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated/main.py
deleted file mode 100644
index a747bda..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated/main.py
+++ /dev/null
@@ -1,178 +0,0 @@
-# coding=utf-8
-#
-# Copyright (C) 2018 - Martin Owens
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-"""
-Provide some documentation to existing extensions about why they're failing.
-"""
-#
-# We ignore a lot of pylint warnings here:
-#
-# pylint: disable=invalid-name,unused-argument,missing-docstring,too-many-public-methods
-#
-
-import os
-import sys
-import warnings
-import argparse
-
-from ..transforms import Transform
-from .. import utils
-from .. import units
-from ..elements._base import BaseElement, ShapeElement
-from ..elements._selected import ElementList
-from .meta import deprecate, _deprecated
-
-warnings.simplefilter("default")
-# To load each of the deprecated sub-modules (the ones without a namespace)
-# we will add the directory to our pythonpath so older scripts can find them
-
-INKEX_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
-SIMPLE_DIR = os.path.join(INKEX_DIR, "deprecated-simple")
-
-if os.path.isdir(SIMPLE_DIR):
- sys.path.append(SIMPLE_DIR)
-
-
-class DeprecatedDict(dict):
- @deprecate
- def __getitem__(self, key):
- return super().__getitem__(key)
-
- @deprecate
- def __iter__(self):
- return super().__iter__()
-
-
-# legacy inkex members
-
-
-class lazyproxy:
- """Proxy, use as decorator on a function with provides the wrapped object.
- The decorated function is called when a member is accessed on the proxy.
- """
-
- def __init__(self, getwrapped):
- """
- :param getwrapped: Callable which returns the wrapped object
- """
- self._getwrapped = getwrapped
-
- def __getattr__(self, name):
- return getattr(self._getwrapped(), name)
-
- def __call__(self, *args, **kwargs):
- return self._getwrapped()(*args, **kwargs)
-
-
-@lazyproxy
-def localize():
- _deprecated("inkex.localize was moved to inkex.localization.localize.", stack=3)
- from ..localization import localize as wrapped
-
- return wrapped
-
-
-def are_near_relative(a, b, eps):
- _deprecated(
- "inkex.are_near_relative was moved to " "inkex.units.are_near_relative", stack=2
- )
- return units.are_near_relative(a, b, eps)
-
-
-def debug(what):
- _deprecated("inkex.debug was moved to inkex.utils.debug.", stack=2)
- return utils.debug(what)
-
-
-# legacy inkex members <= 0.48.x
-
-
-def unittouu(string):
- _deprecated(
- "inkex.unittouu is now a method in the SvgDocumentElement class. "
- "Use `self.svg.unittouu(str)` instead.",
- stack=2,
- )
- return units.convert_unit(string, "px")
-
-
-# optparse.Values.ensure_value
-
-
-def ensure_value(self, attr, value):
- _deprecated("Effect().options.ensure_value was removed.", stack=2)
- if getattr(self, attr, None) is None:
- setattr(self, attr, value)
- return getattr(self, attr)
-
-
-argparse.Namespace.ensure_value = ensure_value # type: ignore
-
-
-@deprecate
-def zSort(inNode, idList):
- """self.svg.get_z_selected()"""
- sortedList = []
- theid = inNode.get("id")
- if theid in idList:
- sortedList.append(theid)
- for child in inNode:
- if len(sortedList) == len(idList):
- break
- sortedList += zSort(child, idList)
- return sortedList
-
-
-# This can't be handled as a mixin class because of circular importing.
-def description(self, value):
- """Use elem.desc = value"""
- self.desc = value
-
-
-BaseElement.description = deprecate(description, "1.1")
-
-
-def composed_style(element: ShapeElement):
- """Calculate the final styles applied to this element
- This function has been deprecated in favor of BaseElement.specified_style()"""
- return element.specified_style()
-
-
-ShapeElement.composed_style = deprecate(composed_style, "1.2")
-
-
-def paint_order(selection: ElementList):
- """Use :func:`rendering_order`"""
- return selection.rendering_order()
-
-
-ElementList.paint_order = deprecate(paint_order, "1.2") # type: ignore
-
-
-def transform_imul(self, matrix):
- """Use @= operator instead"""
- return self.__imatmul__(matrix)
-
-
-def transform_mul(self, matrix):
- """Use @ operator instead"""
- return self.__matmul__(matrix)
-
-
-Transform.__imul__ = deprecate(transform_imul, "1.2") # type: ignore
-Transform.__mul__ = deprecate(transform_mul, "1.2") # type: ignore
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated/meta.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated/meta.py
deleted file mode 100644
index cebd93c..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/deprecated/meta.py
+++ /dev/null
@@ -1,109 +0,0 @@
-# coding=utf-8
-#
-# Copyright (C) 2018 - Martin Owens
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-"""
-Deprecation functionality which does not require imports from Inkex.
-"""
-
-import os
-import traceback
-import warnings
-from typing import Optional
-
-try:
- DEPRECATION_LEVEL = int(os.environ.get("INKEX_DEPRECATION_LEVEL", 1))
-except ValueError:
- DEPRECATION_LEVEL = 1
-
-
-def _deprecated(msg, stack=2, level=DEPRECATION_LEVEL):
- """Internal method for raising a deprecation warning"""
- if level > 1:
- msg += " ; ".join(traceback.format_stack())
- if level:
- warnings.warn(msg, category=DeprecationWarning, stacklevel=stack + 1)
-
-
-def deprecate(func, version: Optional[str] = None):
- r"""Function decorator for deprecation functions which have a one-liner
- equivalent in the new API. The one-liner has to passed as a string
- to the decorator.
-
- >>> @deprecate
- >>> def someOldFunction(*args):
- >>> '''Example replacement code someNewFunction('foo', ...)'''
- >>> someNewFunction('foo', *args)
-
- Or if the args API is the same:
-
- >>> someOldFunction = deprecate(someNewFunction)
-
- """
-
- def _inner(*args, **kwargs):
- _deprecated(f"{func.__module__}.{func.__name__} -> {func.__doc__}", stack=2)
- return func(*args, **kwargs)
-
- _inner.__name__ = func.__name__
- if func.__doc__:
- if version is None:
- _inner.__doc__ = "Deprecated -> " + func.__doc__
- else:
- _inner.__doc__ = f"""{func.__doc__}\n\n.. deprecated:: {version}\n"""
- return _inner
-
-
-class DeprecatedSvgMixin:
- """Mixin which adds deprecated API elements to the SvgDocumentElement"""
-
- @property
- def selected(self):
- """svg.selection"""
- return self.selection
-
- @deprecate
- def set_selected(self, *ids):
- r"""svg.selection.set(\*ids)"""
- return self.selection.set(*ids)
-
- @deprecate
- def get_z_selected(self):
- """svg.selection.rendering_order()"""
- return self.selection.rendering_order()
-
- @deprecate
- def get_selected(self, *types):
- r"""svg.selection.filter(\*types).values()"""
- return self.selection.filter(*types).values()
-
- @deprecate
- def get_selected_or_all(self, *types):
- """Set select_all = True in extension class"""
- if not self.selection:
- self.selection.set_all()
- return self.selection.filter(*types)
-
- @deprecate
- def get_selected_bbox(self):
- """selection.bounding_box()"""
- return self.selection.bounding_box()
-
- @deprecate
- def get_first_selected(self, *types):
- r"""selection.filter(\*types).first() or [0] if you'd like an error"""
- return self.selection.filter(*types).first()
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/__init__.py
deleted file mode 100644
index d426ff6..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/__init__.py
+++ /dev/null
@@ -1,56 +0,0 @@
-"""
-Element based interface provides the bulk of features that allow you to
-interact directly with the SVG xml interface.
-
-See the documentation for each of the elements for details on how it works.
-"""
-
-from ._utils import addNS, NSS
-from ._parser import SVG_PARSER, load_svg
-from ._base import ShapeElement, BaseElement
-from ._svg import SvgDocumentElement
-from ._groups import Group, Layer, Anchor, Marker, ClipPath
-from ._polygons import PathElement, Polyline, Polygon, Line, Rectangle, Circle, Ellipse
-from ._text import (
- FlowRegion,
- FlowRoot,
- FlowPara,
- FlowDiv,
- FlowSpan,
- TextElement,
- TextPath,
- Tspan,
- SVGfont,
- FontFace,
- Glyph,
- MissingGlyph,
-)
-from ._use import Symbol, Use
-from ._meta import (
- Defs,
- StyleElement,
- Script,
- Desc,
- Title,
- NamedView,
- Guide,
- Metadata,
- ForeignObject,
- Switch,
- Grid,
- Page,
-)
-from ._filters import (
- Filter,
- Pattern,
- Mask,
- Gradient,
- LinearGradient,
- RadialGradient,
- PathEffect,
- Stop,
- MeshGradient,
- MeshRow,
- MeshPatch,
-)
-from ._image import Image
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_base.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_base.py
deleted file mode 100644
index 4f8048c..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_base.py
+++ /dev/null
@@ -1,811 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2020 Martin Owens
-# Sergei Izmailov
-# Thomas Holder
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# pylint: disable=arguments-differ
-"""
-Provide extra utility to each svg element type specific to its type.
-
-This is useful for having a common interface for each element which can
-give path, transform, and property access easily.
-"""
-from __future__ import annotations
-
-from copy import deepcopy
-from typing import Any, Tuple, Optional, overload, TypeVar, List
-from lxml import etree
-import re
-
-from ..interfaces.IElement import IBaseElement, ISVGDocumentElement
-
-from ..base import SvgOutputMixin
-from ..paths import Path
-from ..styles import Style, Classes
-from ..transforms import Transform, BoundingBox
-from ..utils import FragmentError
-from ..units import convert_unit, render_unit, parse_unit
-from ._utils import ChildToProperty, NSS, addNS, removeNS, splitNS
-from ..properties import BaseStyleValue, ShorthandValue, all_properties
-from ._selected import ElementList
-from ._parser import NodeBasedLookup, SVG_PARSER
-
-T = TypeVar("T", bound="BaseElement") # pylint: disable=invalid-name
-
-
-class BaseElement(IBaseElement):
- """Provide automatic namespaces to all calls"""
-
- # pylint: disable=too-many-public-methods
-
- def __init_subclass__(cls):
- if cls.tag_name:
- NodeBasedLookup.register_class(cls)
-
- @classmethod
- def is_class_element( # pylint: disable=unused-argument
- cls, elem: etree.Element
- ) -> bool:
- """Hook to do more restrictive check in addition to (ns,tag) match
-
- .. versionadded:: 1.2
- The function has been made public."""
- return True
-
- tag_name = ""
-
- @property
- def TAG(self): # pylint: disable=invalid-name
- """Return the tag_name without NS"""
- if not self.tag_name:
- return removeNS(super().tag)[-1]
- return removeNS(self.tag_name)[-1]
-
- @classmethod
- def new(cls, *children, **attrs):
- """Create a new element, converting attrs values to strings."""
- obj = cls(*children)
- obj.update(**attrs)
- return obj
-
- NAMESPACE = property(lambda self: splitNS(self.tag_name)[0])
- """Get namespace of element"""
-
- PARSER = SVG_PARSER
- """A reference to the :attr:`inkex.elements._parser.SVG_PARSER`"""
- WRAPPED_ATTRS = (
- # (prop_name, [optional: attr_name], cls)
- ("transform", Transform),
- ("style", Style),
- ("classes", "class", Classes),
- ) # type: Tuple[Tuple[Any, ...], ...]
- """A list of attributes that are automatically converted to objects."""
-
- # We do this because python2 and python3 have different ways
- # of combining two dictionaries that are incompatible.
- # This allows us to update these with inheritance.
- @property
- def wrapped_attrs(self):
- """Map attributes to property name and wrapper class"""
- return {row[-2]: (row[0], row[-1]) for row in self.WRAPPED_ATTRS}
-
- @property
- def wrapped_props(self):
- """Map properties to attribute name and wrapper class"""
- return {row[0]: (row[-2], row[-1]) for row in self.WRAPPED_ATTRS}
-
- typename = property(lambda self: type(self).__name__)
- """Type name of the element"""
- xml_path = property(lambda self: self.getroottree().getpath(self))
- """XPath representation of the element in its tree
-
- .. versionadded:: 1.1"""
- desc = ChildToProperty("svg:desc", prepend=True)
- """The element's long-form description (for accessibility purposes)
-
- .. versionadded:: 1.1"""
- title = ChildToProperty("svg:title", prepend=True)
- """The element's short-form description (for accessibility purposes)
-
- .. versionadded:: 1.1"""
-
- def __getattr__(self, name):
- """Get the attribute, but load it if it is not available yet"""
- if name in self.wrapped_props:
- (attr, cls) = self.wrapped_props[name]
-
- # The reason we do this here and not in _init is because lxml
- # is inconsistant about when elements are initialised.
- # So we make this a lazy property.
- def _set_attr(new_item):
- if new_item:
- self.set(attr, str(new_item))
- else:
- self.attrib.pop(attr, None) # pylint: disable=no-member
-
- # pylint: disable=no-member
- value = cls(self.attrib.get(attr, None), callback=_set_attr)
- if name == "style":
- value.element = self
- setattr(self, name, value)
- return value
- raise AttributeError(f"Can't find attribute {self.typename}.{name}")
-
- def __setattr__(self, name, value):
- """Set the attribute, update it if needed"""
- if name in self.wrapped_props:
- (attr, cls) = self.wrapped_props[name]
- # Don't call self.set or self.get (infinate loop)
- if value:
- if not isinstance(value, cls):
- value = cls(value)
- self.attrib[attr] = str(value)
- else:
- self.attrib.pop(attr, None) # pylint: disable=no-member
- else:
- super().__setattr__(name, value)
-
- def get(self, attr, default=None):
- """Get element attribute named, with addNS support."""
- if attr in self.wrapped_attrs:
- (prop, _) = self.wrapped_attrs[attr]
- value = getattr(self, prop, None)
- # We check the boolean nature of the value, because empty
- # transformations and style attributes are equiv to not-existing
- ret = str(value) if value else (default or None)
- return ret
- return super().get(addNS(attr), default)
-
- def set(self, attr, value):
- """Set element attribute named, with addNS support"""
- if attr in self.wrapped_attrs:
- # Always keep the local wrapped class up to date.
- (prop, cls) = self.wrapped_attrs[attr]
- setattr(self, prop, cls(value))
- value = getattr(self, prop)
- if not value:
- return
- if value is None:
- self.attrib.pop(addNS(attr), None) # pylint: disable=no-member
- else:
- value = str(value)
- super().set(addNS(attr), value)
-
- def update(self, **kwargs):
- """
- Update element attributes using keyword arguments
-
- Note: double underscore is used as namespace separator,
- i.e. "namespace__attr" argument name will be treated as "namespace:attr"
-
- :param kwargs: dict with name=value pairs
- :return: self
- """
- for name, value in kwargs.items():
- self.set(name, value)
- return self
-
- def pop(self, attr, default=None):
- """Delete/remove the element attribute named, with addNS support."""
- if attr in self.wrapped_attrs:
- # Always keep the local wrapped class up to date.
- (prop, cls) = self.wrapped_attrs[attr]
- value = getattr(self, prop)
- setattr(self, prop, cls(None))
- return value
- return self.attrib.pop(addNS(attr), default) # pylint: disable=no-member
-
- @overload
- def add(
- self, child1: BaseElement, child2: BaseElement, *children: BaseElement
- ) -> Tuple[BaseElement]:
- ...
-
- @overload
- def add(self, child: T) -> T:
- ...
-
- def add(self, *children):
- """
- Like append, but will do multiple children and will return
- children or only child
- """
- for child in children:
- self.append(child)
- return children if len(children) != 1 else children[0]
-
- def tostring(self):
- """Return this element as it would appear in an svg document"""
- # This kind of hack is pure maddness, but etree provides very little
- # in the way of fragment printing, prefering to always output valid xml
-
- svg = SvgOutputMixin.get_template(width=0, height=0).getroot()
- svg.append(self.copy())
- return svg.tostring().split(b">\n ", 1)[-1][:-6]
-
- def set_random_id(
- self,
- prefix: Optional[str] = None,
- size: Optional[int] = None,
- backlinks: bool = False,
- blacklist: Optional[List[str]] = None,
- ):
- """Sets the id attribute if it is not already set.
-
- The id consists of a prefix and an appended random integer of length size.
- Args:
- prefix (str, optional): the prefix of the new ID. Defaults to the tag name.
- size (Optional[int], optional): number of digits of the second part of the
- id. If None, the length is chosen based on the amount of existing
- objects. Defaults to None.
-
- .. versionchanged:: 1.2
- The default of this value has been changed from 4 to None.
- backlinks (bool, optional): Whether to update the links in existing objects
- that reference this element. Defaults to False.
- blacklist (List[str], optional): An additional list of ids that are not
- allowed to be used. This is useful when bulk inserting objects.
- Defaults to None.
-
- .. versionadded:: 1.2
- """
- prefix = str(self) if prefix is None else prefix
- self.set_id(
- self.root.get_unique_id(prefix, size=size, blacklist=blacklist),
- backlinks=backlinks,
- )
-
- def set_random_ids(
- self,
- prefix: Optional[str] = None,
- levels: int = -1,
- backlinks: bool = False,
- blacklist: Optional[List[str]] = None,
- ):
- """Same as set_random_id, but will apply also to children
-
- The id consists of a prefix and an appended random integer of length size.
- Args:
- prefix (str, optional): the prefix of the new ID. Defaults to the tag name.
- levels (int, optional): the depth of the tree traversion, if negative, no
- limit is imposed. Defaults to -1.
- backlinks (bool, optional): Whether to update the links in existing objects
- that reference this element. Defaults to False.
- blacklist (List[str], optional): An additional list of ids that are not
- allowed to be used. This is useful when bulk inserting objects.
- Defaults to None.
-
- .. versionadded:: 1.2
- """
- self.set_random_id(prefix=prefix, backlinks=backlinks, blacklist=blacklist)
- if levels != 0:
- for child in self:
- if hasattr(child, "set_random_ids"):
- child.set_random_ids(
- prefix=prefix, levels=levels - 1, backlinks=backlinks
- )
-
- eid = property(lambda self: self.get_id())
- """Property to access the element's id; will set a new unique id if not set."""
-
- def get_id(self, as_url=0) -> str:
- """Get the id for the element, will set a new unique id if not set.
-
- as_url - If set to 1, returns #{id} as a string
- If set to 2, returns url(#{id}) as a string
-
- Args:
- as_url (int, optional):
- - If set to 1, returns #{id} as a string
- - If set to 2, returns url(#{id}) as a string.
-
- Defaults to 0.
-
- .. versionadded:: 1.1
-
- Returns:
- str: formatted id
- """
- if "id" not in self.attrib:
- self.set_random_id(self.TAG)
- eid = self.get("id")
- if as_url > 0:
- eid = "#" + eid
- if as_url > 1:
- eid = f"url({eid})"
- return eid
-
- def set_id(self, new_id, backlinks=False):
- """Set the id and update backlinks to xlink and style urls if needed"""
- old_id = self.get("id", None)
- self.set("id", new_id)
- if backlinks and old_id:
- for elem in self.root.getElementsByHref(old_id):
- elem.href = self
- for attr in ["clip-path", "mask"]:
- for elem in self.root.getElementsByHref(old_id, attribute=attr):
- elem.set(attr, self.get_id(2))
- for elem in self.root.getElementsByStyleUrl(old_id):
- elem.style.update_urls(old_id, new_id)
-
- @property
- def root(self):
- """Get the root document element from any element descendent"""
- root, parent = self, self
- while parent is not None:
- root, parent = parent, parent.getparent()
-
- if not isinstance(root, ISVGDocumentElement):
- raise FragmentError("Element fragment does not have a document root!")
- return root
-
- def get_or_create(self, xpath, nodeclass=None, prepend=False):
- """Get or create the given xpath, pre/append new node if not found.
-
- .. versionchanged:: 1.1
- The ``nodeclass`` attribute is optional; if not given, it is looked up
- using :func:`~inkex.elements._parser.NodeBasedLookup.find_class`"""
- node = self.findone(xpath)
- if node is None:
- if nodeclass is None:
- nodeclass = NodeBasedLookup.find_class(xpath)
- node = nodeclass()
- if prepend:
- self.insert(0, node)
- else:
- self.append(node)
- return node
-
- def descendants(self):
- """Walks the element tree and yields all elements, parent first
-
- .. versionchanged:: 1.1
- The ``*types`` attribute was removed
-
- """
-
- return ElementList(
- self.root,
- [
- element
- for element in self.iter()
- if isinstance(element, (BaseElement, str))
- ],
- )
-
- def ancestors(self, elem=None, stop_at=()):
- """
- Walk the parents and yield all the ancestor elements, parent first
-
- Args:
- elem (BaseElement, optional): If provided, it will stop at the last common
- ancestor. Defaults to None.
-
- .. versionadded:: 1.1
-
- stop_at (tuple, optional): If provided, it will stop at the first parent
- that is in this list. Defaults to ().
-
- .. versionadded:: 1.1
-
- Returns:
- ElementList: list of ancestors
- """
-
- return ElementList(self.root, self._ancestors(elem=elem, stop_at=stop_at))
-
- def _ancestors(self, elem, stop_at):
- if isinstance(elem, BaseElement):
- stop_at = list(elem.ancestors())
- for parent in self.iterancestors():
- yield parent
- if parent in stop_at:
- break
-
- def backlinks(self, *types):
- """Get elements which link back to this element, like ancestors but via
- xlinks"""
- if not types or isinstance(self, types):
- yield self
- my_id = self.get("id")
- if my_id is not None:
- elems = list(self.root.getElementsByHref(my_id)) + list(
- self.root.getElementsByStyleUrl(my_id)
- )
- for elem in elems:
- if hasattr(elem, "backlinks"):
- for child in elem.backlinks(*types):
- yield child
-
- def xpath(self, pattern, namespaces=NSS): # pylint: disable=dangerous-default-value
- """Wrap xpath call and add svg namespaces"""
- return super().xpath(pattern, namespaces=namespaces)
-
- def findall(
- self, pattern, namespaces=NSS
- ): # pylint: disable=dangerous-default-value
- """Wrap findall call and add svg namespaces"""
- return super().findall(pattern, namespaces=namespaces)
-
- def findone(self, xpath):
- """Gets a single element from the given xpath or returns None"""
- el_list = self.xpath(xpath)
- return el_list[0] if el_list else None
-
- def delete(self):
- """Delete this node from it's parent node"""
- if self.getparent() is not None:
- self.getparent().remove(self)
-
- def remove_all(self, *types):
- """Remove all children or child types
-
- .. versionadded:: 1.1"""
- types = tuple(NodeBasedLookup.find_class(t) for t in types)
- for child in self:
- if not types or isinstance(child, types):
- self.remove(child)
-
- def replace_with(self, elem):
- """Replace this element with the given element"""
- self.addnext(elem)
- if not elem.get("id") and self.get("id"):
- elem.set("id", self.get("id"))
- if not elem.label and self.label:
- elem.label = self.label
- self.delete()
- return elem
-
- def copy(self):
- """Make a copy of the element and return it"""
- elem = deepcopy(self)
- elem.set("id", None)
- return elem
-
- def duplicate(self):
- """Like copy(), but the copy stays in the tree and sets a random id on the
- duplicate.
-
- .. versionchanged:: 1.2
- A random id is also set on all the duplicate's descendants"""
- elem = self.copy()
- self.addnext(elem)
- elem.set_random_ids()
- return elem
-
- def __str__(self):
- # We would do more here, but lxml is VERY unpleseant when it comes to
- # namespaces, basically over printing details and providing no
- # supression mechanisms to turn off xml's over engineering.
- return str(self.tag).split("}", maxsplit=1)[-1]
-
- @property
- def href(self):
- """Returns the referred-to element if available
-
- .. versionchanged:: 1.1
- A setter for href was added."""
- ref = self.get("href") or self.get("xlink:href")
- if not ref:
- return None
- return self.root.getElementById(ref.strip("#"))
-
- @href.setter
- def href(self, elem):
- """Set the href object"""
- if isinstance(elem, BaseElement):
- elem = elem.get_id()
- if self.get("href"):
- self.set("href", "#" + elem)
- else:
- self.set("xlink:href", "#" + elem)
-
- @property
- def label(self):
- """Returns the inkscape label"""
- return self.get("inkscape:label", None)
-
- @label.setter
- def label(self, value):
- """Sets the inkscape label"""
- self.set("inkscape:label", str(value))
-
- def is_sensitive(self):
- """Return true if this element is sensitive in inkscape
-
- .. versionadded:: 1.1"""
- return self.get("sodipodi:insensitive", None) != "true"
-
- def set_sensitive(self, sensitive=True):
- """Set the sensitivity of the element/layer
-
- .. versionadded:: 1.1"""
- # Sensitive requires None instead of 'false'
- self.set("sodipodi:insensitive", ["true", None][sensitive])
-
- @property
- def unit(self):
- """Return the unit being used by the owning document, cached
-
- .. versionadded:: 1.1"""
- try:
- return self.root.unit
- except FragmentError:
- return "px" # Don't cache.
-
- @staticmethod
- def to_dimensional(value, to_unit="px"):
- """Convert a value given in user units (px) the given unit type
-
- .. versionadded:: 1.2"""
- return convert_unit(value, to_unit)
-
- @staticmethod
- def to_dimensionless(value):
- """Convert a length value into user units (px)
-
- .. versionadded:: 1.2"""
- return convert_unit(value, "px")
-
- def uutounit(self, value, to_unit="px"):
- """Convert a unit value to a given unit. If the value does not have a unit,
- "Document" units are assumed. "Document units" are an Inkscape-specific concept.
- For most use-cases, :func:`to_dimensional` is more appropriate.
-
- .. versionadded:: 1.1"""
- return convert_unit(value, to_unit, default=self.unit)
-
- def unittouu(self, value):
- """Convert a unit value into document units. "Document unit" is an
- Inkscape-specific concept. For most use-cases, :func:`viewport_to_unit` (when
- the size of an object given in viewport units is needed) or
- :func:`to_dimensionless` (when the equivalent value without unit is needed) is
- more appropriate.
-
- .. versionadded:: 1.1"""
- return convert_unit(value, self.unit)
-
- def unit_to_viewport(self, value, unit="px"):
- """Converts a length value to viewport units, as defined by the width/height
- element on the root (i.e. applies the equivalent transform of the viewport)
-
- .. versionadded:: 1.2"""
- return self.to_dimensional(
- self.to_dimensionless(value) * self.root.equivalent_transform_scale, unit
- )
-
- def viewport_to_unit(self, value, unit="px"):
- """Converts a length given on the viewport to the specified unit in the user
- coordinate system
-
- .. versionadded:: 1.2"""
- return self.to_dimensional(
- self.to_dimensionless(value) / self.root.equivalent_transform_scale, unit
- )
-
- def add_unit(self, value):
- """Add document unit when no unit is specified in the string.
-
- .. versionadded:: 1.1"""
- return render_unit(value, self.unit)
-
- def cascaded_style(self):
- """Returns the cascaded style of an element (all rules that apply the element
- itself), based on the stylesheets, the presentation attributes and the inline
- style using the respective specificity of the style.
-
- see https://www.w3.org/TR/CSS22/cascade.html#cascading-order
-
- .. versionadded:: 1.2
-
- Returns:
- Style: the cascaded style
-
- """
- return Style.cascaded_style(self)
-
- def specified_style(self):
- """Returns the specified style of an element, i.e. the cascaded style +
- inheritance, see https://www.w3.org/TR/CSS22/cascade.html#specified-value.
-
- Returns:
- Style: the specified style
-
- .. versionadded:: 1.2
- """
- return Style.specified_style(self)
-
- def presentation_style(self):
- """Return presentation attributes of an element as style
-
- .. versionadded:: 1.2"""
- style = Style()
- for key in self.keys():
- if (
- key in all_properties
- and all_properties[key][2]
- and not issubclass(all_properties[key][0], ShorthandValue)
- ):
- # Shorthands cannot be set by presentation attributes
- result = BaseStyleValue.factory_errorhandled(
- key=key, value=self.attrib[key]
- )
- if result is not None: # parsing error
- style[key] = result[1]
- return style
-
- def composed_transform(self, other=None):
- """Calculate every transform down to the other element
- if none specified the transform is to the root document element
- """
- parent = self.getparent()
- if parent is not other and isinstance(parent, BaseElement):
- return parent.composed_transform(other) @ self.transform
- return self.transform
-
-
-NodeBasedLookup.default = BaseElement
-
-
-class ShapeElement(BaseElement):
- """Elements which have a visible representation on the canvas"""
-
- @property
- def path(self):
- """Gets the outline or path of the element, this may be a simple bounding box"""
- return Path(self.get_path())
-
- @path.setter
- def path(self, path):
- self.set_path(path)
-
- @property
- def clip(self):
- """Gets the clip path element (if any). May be set through CSS.
-
- .. versionadded:: 1.1"""
- ref = self.get("clip-path")
- if not ref:
- return self.specified_style()("clip-path")
- return self.root.getElementById(ref)
-
- @clip.setter
- def clip(self, elem):
- self.set("clip-path", elem.get_id(as_url=2))
-
- def get_path(self) -> Path:
- """Generate a path for this object which can inform the bounding box"""
- raise NotImplementedError(
- f"Path should be provided by svg elem {self.typename}."
- )
-
- def set_path(self, path):
- """Set the path for this object (if possible)"""
- raise AttributeError(
- f"Path can not be set on this element: {self.typename} <- {path}."
- )
-
- def to_path_element(self):
- """Replace this element with a path element"""
- from ._polygons import PathElement
-
- elem = PathElement()
- elem.path = self.path
- elem.style = self.effective_style()
- elem.transform = self.transform
- return elem
-
- def effective_style(self):
- """Without parent styles, what is the effective style is"""
- return self.style
-
- def bounding_box(self, transform=None):
- # type: (Optional[Transform]) -> Optional[BoundingBox]
- """BoundingBox of the shape
-
- .. versionchanged:: 1.1
- result adjusted for element's clip path if applicable."""
- shape_box = self.shape_box(transform)
- clip = self.clip
- if clip is None or shape_box is None:
- return shape_box
- return shape_box & clip.bounding_box(Transform(transform) @ self.transform)
-
- def shape_box(self, transform=None):
- # type: (Optional[Transform]) -> Optional[BoundingBox]
- """BoundingBox of the unclipped shape
-
- .. versionadded:: 1.1
- Previous :func:`bounding_box` function, returning the bounding box
- without computing the effect of a possible clip."""
- path = self.path.to_absolute()
- if transform is True:
- path = path.transform(self.composed_transform())
- else:
- path = path.transform(self.transform)
- if transform: # apply extra transformation
- path = path.transform(transform)
- return path.bounding_box()
-
- def is_visible(self):
- """Returns false if this object is invisible
-
- .. versionchanged:: 1.3
- rely on cascaded_style() to include CSS and presentation attributes
- include `visibility` attribute with check for inherit
- include ancestors
-
- .. versionadded:: 1.1"""
- return self._is_visible()
-
- def _is_visible(self, inherit_visibility=True):
- # iterate over self and ancestors
- for element in [self] + list(self.ancestors()):
- get_style = element.cascaded_style().get
- # case display:none
- if get_style("display", "inline") == "none":
- return False
- # case opacity:0
- if not float(get_style("opacity", 1.0)):
- return False
- # only check if childs visibility is inherited
- if inherit_visibility:
- # case visibility:hidden
- if get_style("visibility", "inherit") in (
- "hidden",
- "collapse",
- ):
- return False
- # case visibility: not inherit
- elif get_style("visibility", "inherit") != "inherit":
- inherit_visibility = False
-
- return True
-
- def get_line_height_uu(self):
- """Returns the specified value of line-height, in user units
-
- .. versionadded:: 1.1"""
- style = self.specified_style()
- font_size = style("font-size") # already in uu
- line_height = style("line-height")
- parsed = parse_unit(line_height)
- if parsed is None:
- return font_size * 1.2
- if parsed[1] == "%":
- return font_size * parsed[0] * 0.01
- return self.to_dimensionless(line_height)
-
-
-class ViewboxMixin:
- """Mixin for elements with viewboxes, such as , """
-
- def parse_viewbox(self, vbox: Optional[str]) -> Optional[List[float]]:
- """Parses a viewbox. If an error occurs during parsing,
- (0, 0, 0, 0) is returned. If the viewbox is None, None is returned.
-
- .. versionadded:: 1.3"""
- if vbox is not None and isinstance(vbox, str):
- try:
- result = [float(unit) for unit in re.split(r",\s*|\s+", vbox)]
- except ValueError:
- result = []
- if len(result) != 4:
- result = [0, 0, 0, 0]
- return result
- return None
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_filters.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_filters.py
deleted file mode 100644
index 8744f6d..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_filters.py
+++ /dev/null
@@ -1,516 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2020 Martin Owens
-# Sergei Izmailov
-# Thomas Holder
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# pylint: disable=arguments-differ
-"""
-Element interface for patterns, filters, gradients and path effects.
-"""
-from __future__ import annotations
-from typing import List, Tuple, TYPE_CHECKING, Optional
-
-from lxml import etree
-
-from ..utils import parse_percent
-
-from ..transforms import Transform
-
-from ..styles import Style
-
-from ._utils import addNS
-from ._base import BaseElement, ViewboxMixin
-from ._groups import GroupBase
-from ..units import convert_unit
-
-
-if TYPE_CHECKING:
- from ._svg import SvgDocumentElement
-
-
-class Filter(BaseElement):
- """A filter (usually in defs)"""
-
- tag_name = "filter"
-
- def add_primitive(self, fe_type, **args):
- """Create a filter primitive with the given arguments"""
- elem = etree.SubElement(self, addNS(fe_type, "svg"))
- elem.update(**args)
- return elem
-
- class Primitive(BaseElement):
- """Any filter primitive"""
-
- class Blend(Primitive):
- """Blend Filter element"""
-
- tag_name = "feBlend"
-
- class ColorMatrix(Primitive):
- """ColorMatrix Filter element"""
-
- tag_name = "feColorMatrix"
-
- class ComponentTransfer(Primitive):
- """ComponentTransfer Filter element"""
-
- tag_name = "feComponentTransfer"
-
- class Composite(Primitive):
- """Composite Filter element"""
-
- tag_name = "feComposite"
-
- class ConvolveMatrix(Primitive):
- """ConvolveMatrix Filter element"""
-
- tag_name = "feConvolveMatrix"
-
- class DiffuseLighting(Primitive):
- """DiffuseLightning Filter element"""
-
- tag_name = "feDiffuseLighting"
-
- class DisplacementMap(Primitive):
- """Flood Filter element"""
-
- tag_name = "feDisplacementMap"
-
- class Flood(Primitive):
- """DiffuseLightning Filter element"""
-
- tag_name = "feFlood"
-
- class GaussianBlur(Primitive):
- """GaussianBlur Filter element"""
-
- tag_name = "feGaussianBlur"
-
- class Image(Primitive):
- """Image Filter element"""
-
- tag_name = "feImage"
-
- class Merge(Primitive):
- """Merge Filter element"""
-
- tag_name = "feMerge"
-
- class Morphology(Primitive):
- """Morphology Filter element"""
-
- tag_name = "feMorphology"
-
- class Offset(Primitive):
- """Offset Filter element"""
-
- tag_name = "feOffset"
-
- class SpecularLighting(Primitive):
- """SpecularLighting Filter element"""
-
- tag_name = "feSpecularLighting"
-
- class Tile(Primitive):
- """Tile Filter element"""
-
- tag_name = "feTile"
-
- class Turbulence(Primitive):
- """Turbulence Filter element"""
-
- tag_name = "feTurbulence"
-
-
-class Stop(BaseElement):
- """Gradient stop
-
- .. versionadded:: 1.1"""
-
- tag_name = "stop"
-
- @property
- def offset(self) -> float:
- """The offset of the gradient stop"""
- value = self.get("offset", default="0")
- return parse_percent(value)
-
- @offset.setter
- def offset(self, number):
- self.set("offset", number)
-
- def interpolate(self, other, fraction):
- """Interpolate gradient stops"""
- from ..tween import StopInterpolator
-
- return StopInterpolator(self, other).interpolate(fraction)
-
-
-class Pattern(BaseElement, ViewboxMixin):
- """Pattern element which is used in the def to control repeating fills"""
-
- tag_name = "pattern"
- WRAPPED_ATTRS = BaseElement.WRAPPED_ATTRS + (("patternTransform", Transform),)
-
- def get_fallback(self, prop, default="0"):
- val = self.get(prop, None)
- if val is None:
- if isinstance(self.href, Pattern):
- return getattr(self.href, prop)
- val = default
- return val
-
- x = property(lambda self: self.get_fallback("x"))
- y = property(lambda self: self.get_fallback("y"))
- width = property(lambda self: self.get_fallback("width"))
- height = property(lambda self: self.get_fallback("height"))
- patternUnits = property(
- lambda self: self.get_fallback("patternUnits", "objectBoundingBox")
- )
-
- def get_viewbox(self) -> Optional[List[float]]:
- """Get the viewbox of the pattern, falling back to the href's viewbox
-
- .. versionadded:: 1.3"""
- vbox = self.get("viewBox", None)
- if vbox is None:
- if isinstance(self.href, Pattern):
- return self.href.get_viewbox()
- return self.parse_viewbox(vbox)
-
- def get_effective_parent(self, depth=0, maxDepth=10):
- """If a pattern has no children, but a href, it uses the children from the href.
- Avoids infinite recursion.
-
- .. versionadded:: 1.3"""
- if (
- len(self) == 0
- and self.href is not None
- and isinstance(self.href, Pattern)
- and depth < maxDepth
- ):
- return self.href.get_effective_parent(depth + 1, maxDepth)
- return self
-
-
-class Mask(GroupBase):
- """A structural object that serves as opacity mask
-
- .. versionadded:: 1.3"""
-
- tag_name = "mask"
-
- def get_fallback(self, prop, default="0"):
- return self.to_dimensionless(self.get(prop, default))
-
- x = property(lambda self: self.get_fallback("x"))
- y = property(lambda self: self.get_fallback("y"))
- width = property(lambda self: self.get_fallback("width"))
- height = property(lambda self: self.get_fallback("height"))
- maskUnits = property(lambda self: self.get("maskUnits", "objectBoundingBox"))
-
-
-class Gradient(BaseElement):
- """A gradient instruction usually in the defs."""
-
- WRAPPED_ATTRS = BaseElement.WRAPPED_ATTRS + (("gradientTransform", Transform),)
- """Additional to the :attr:`~inkex.elements._base.BaseElement.WRAPPED_ATTRS` of
- :class:`~inkex.elements._base.BaseElement`, ``gradientTransform`` is wrapped."""
-
- orientation_attributes = () # type: Tuple[str, ...]
- """
- .. versionadded:: 1.1
- """
-
- @property
- def stops(self):
- """Return an ordered list of own or linked stop nodes
-
- .. versionadded:: 1.1"""
- gradcolor = (
- self.href
- if isinstance(self.href, (LinearGradient, RadialGradient))
- else self
- )
- return [child for child in gradcolor if isinstance(child, Stop)]
-
- @property
- def stop_offsets(self):
- # type: () -> List[float]
- """Return a list of own or linked stop offsets
-
- .. versionadded:: 1.1"""
- return [child.offset for child in self.stops]
-
- @property
- def stop_styles(self): # type: () -> List[Style]
- """Return a list of own or linked offset styles
-
- .. versionadded:: 1.1"""
- return [child.style for child in self.stops]
-
- def remove_orientation(self):
- """Remove all orientation attributes from this element
-
- .. versionadded:: 1.1"""
- for attr in self.orientation_attributes:
- self.pop(attr)
-
- def interpolate(
- self,
- other: LinearGradient,
- fraction: float,
- svg: Optional[SvgDocumentElement] = None,
- ):
- """Interpolate with another gradient.
-
- .. versionadded:: 1.1"""
- from ..tween import GradientInterpolator
-
- return GradientInterpolator(self, other, svg).interpolate(fraction)
-
- def stops_and_orientation(self):
- """Return a copy of all the stops in this gradient
-
- .. versionadded:: 1.1"""
- stops = self.copy()
- stops.remove_orientation()
- orientation = self.copy()
- orientation.remove_all(Stop)
- return stops, orientation
-
- def get_percentage_parsed_unit(self, attribute, value, svg=None):
- """Parses an attribute of a gradient, respecting percentage values of
- "userSpaceOnUse" as percentages of document size. See
- https://www.w3.org/TR/SVG2/pservers.html#LinearGradientAttributes for details
-
- .. versionadded:: 1.3"""
- if isinstance(value, (float, int)):
- return value
- value = value.strip()
- if len(value) > 0 and value[-1] == "%":
- try:
- value = float(value.strip()[0:-1]) / 100.0
- gradientunits = self.get("gradientUnits", "objectBoundingBox")
- if gradientunits == "userSpaceOnUse":
- if svg is None:
- raise ValueError("Need root SVG to determine percentage value")
- bbox = svg.get_page_bbox()
- if attribute in ("cx", "fx", "x1", "x2"):
- return bbox.width * value
- if attribute in ("cy", "fy", "y1", "y2"):
- return bbox.height * value
- if attribute in ("r"):
- return bbox.diagonal_length * value
- if gradientunits == "objectBoundingBox":
- return value
- except ValueError:
- value = None
- return convert_unit(value, "px")
-
- def _get_or_href(self, attr, default, svg=None):
- val = self.get(attr)
- if val is None:
- if type(self.href) is type(self):
- return getattr(self.href, attr)()
- val = default
- return self.get_percentage_parsed_unit(attr, val, svg)
-
-
-class LinearGradient(Gradient):
- """LinearGradient element"""
-
- tag_name = "linearGradient"
- orientation_attributes = ("x1", "y1", "x2", "y2")
- """
- .. versionadded:: 1.1
- """
-
- def apply_transform(self): # type: () -> None
- """Apply transform to orientation points and set it to identity.
- .. versionadded:: 1.1
- """
- trans = self.pop("gradientTransform")
- pt1 = (
- self.to_dimensionless(self.get("x1")),
- self.to_dimensionless(self.get("y1")),
- )
- pt2 = (
- self.to_dimensionless(self.get("x2")),
- self.to_dimensionless(self.get("y2")),
- )
- p1t = trans.apply_to_point(pt1)
- p2t = trans.apply_to_point(pt2)
- self.update(
- x1=self.to_dimensionless(p1t[0]),
- y1=self.to_dimensionless(p1t[1]),
- x2=self.to_dimensionless(p2t[0]),
- y2=self.to_dimensionless(p2t[1]),
- )
-
- def x1(self, svg=None):
- """Get the x1 attribute
-
- .. versionadded:: 1.3"""
- return self._get_or_href("x1", "0%", svg)
-
- def x2(self, svg=None):
- """Get the x2 attribute
-
- .. versionadded:: 1.3"""
- return self._get_or_href("x2", "100%", svg)
-
- def y1(self, svg=None):
- """Get the y1 attribute
-
- .. versionadded:: 1.3"""
- return self._get_or_href("y1", "0%", svg)
-
- def y2(self, svg=None):
- """Get the y2 attribute
-
- .. versionadded:: 1.3"""
- return self._get_or_href("y2", "0%", svg)
-
-
-class RadialGradient(Gradient):
- """RadialGradient element"""
-
- tag_name = "radialGradient"
- orientation_attributes = ("cx", "cy", "fx", "fy", "r")
- """
- .. versionadded:: 1.1
- """
-
- def apply_transform(self): # type: () -> None
- """Apply transform to orientation points and set it to identity.
-
- .. versionadded:: 1.1
- """
- trans = self.pop("gradientTransform")
- pt1 = (
- self.to_dimensionless(self.get("cx")),
- self.to_dimensionless(self.get("cy")),
- )
- pt2 = (
- self.to_dimensionless(self.get("fx")),
- self.to_dimensionless(self.get("fy")),
- )
- p1t = trans.apply_to_point(pt1)
- p2t = trans.apply_to_point(pt2)
- self.update(
- cx=self.to_dimensionless(p1t[0]),
- cy=self.to_dimensionless(p1t[1]),
- fx=self.to_dimensionless(p2t[0]),
- fy=self.to_dimensionless(p2t[1]),
- )
-
- def cx(self, svg=None):
- """Get the effective cx (horizontal center) attribute in user units
-
- .. versionadded:: 1.3"""
- return self._get_or_href("cx", "50%", svg)
-
- def cy(self, svg=None):
- """Get the effective cy (vertical center) attribute in user units
-
- .. versionadded:: 1.3"""
- return self._get_or_href("cy", "50%", svg)
-
- def fx(self, svg=None):
- """Get the effective fx (horizontal focal point) attribute in user units
-
- .. versionadded:: 1.3"""
- return self._get_or_href("fx", self.cx(svg), svg)
-
- def fy(self, svg=None):
- """Get the effective fx (vertical focal point) attribute in user units
-
- .. versionadded:: 1.3"""
- return self._get_or_href("fy", self.cy(svg), svg)
-
- def r(self, svg=None):
- """Get the effective r (gradient radius) attribute in user units
-
- .. versionadded:: 1.3"""
- return self._get_or_href("r", "50%", svg)
-
-
-class PathEffect(BaseElement):
- """Inkscape LPE element"""
-
- tag_name = "inkscape:path-effect"
-
-
-class MeshGradient(Gradient):
- """Usable MeshGradient XML base class
-
- .. versionadded:: 1.1"""
-
- tag_name = "meshgradient"
-
- @classmethod
- def new_mesh(cls, pos=None, rows=1, cols=1, autocollect=True):
- """Return skeleton of 1x1 meshgradient definition."""
- # initial point
- if pos is None or len(pos) != 2:
- pos = [0.0, 0.0]
- # create nested elements for rows x cols mesh
- meshgradient = cls()
- for _ in range(rows):
- meshrow: BaseElement = meshgradient.add(MeshRow())
- for _ in range(cols):
- meshrow.append(MeshPatch())
- # set meshgradient attributes
- meshgradient.set("gradientUnits", "userSpaceOnUse")
- meshgradient.set("x", pos[0])
- meshgradient.set("y", pos[1])
- if autocollect:
- meshgradient.set("inkscape:collect", "always")
- return meshgradient
-
-
-class MeshRow(BaseElement):
- """Each row of a mesh gradient
-
- .. versionadded:: 1.1"""
-
- tag_name = "meshrow"
-
-
-class MeshPatch(BaseElement):
- """Each column or 'patch' in a mesh gradient
-
- .. versionadded:: 1.1"""
-
- tag_name = "meshpatch"
-
- def stops(self, edges, colors):
- """Add or edit meshpatch stops with path and stop-color."""
- # iterate stops based on number of edges (path data)
- for i, edge in enumerate(edges):
- if i < len(self):
- stop = self[i]
- else:
- stop = self.add(Stop())
-
- # set edge path data
- stop.set("path", str(edge))
- # set stop color
- stop.style["stop-color"] = str(colors[i % 2])
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_groups.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_groups.py
deleted file mode 100644
index e72620c..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_groups.py
+++ /dev/null
@@ -1,159 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2020 Martin Owens
-# Sergei Izmailov
-# Ryan Jarvis
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# pylint: disable=arguments-differ
-"""
-Interface for all group based elements such as Groups, Use, Markers etc.
-"""
-
-from lxml import etree # pylint: disable=unused-import
-
-from ..paths import Path
-from ..transforms import Transform
-
-from ._utils import addNS
-from ._base import ShapeElement, ViewboxMixin
-from ._polygons import PathElement
-
-try:
- from typing import Optional, List # pylint: disable=unused-import
-except ImportError:
- pass
-
-
-class GroupBase(ShapeElement):
- """Base Group element"""
-
- def get_path(self):
- ret = Path()
- for child in self:
- if isinstance(child, ShapeElement):
- ret += child.path.transform(child.transform)
- return ret
-
- def shape_box(self, transform=None):
- bbox = None
- effective_transform = Transform(transform) @ self.transform
- for child in self:
- if isinstance(child, ShapeElement):
- child_bbox = child.bounding_box(transform=effective_transform)
- if child_bbox is not None:
- bbox += child_bbox
- return bbox
-
- def bake_transforms_recursively(self, apply_to_paths=True):
- """Bake transforms, i.e. each leaf node has the effective transform (starting
- from this group) set, and parent transforms are removed.
-
- .. versionadded:: 1.4
-
- Args:
- apply_to_paths (bool, optional): For path elements, the
- path data is transformed with its effective transform. Nodes and handles
- will have the same position as before, but visual appearance of the
- stroke may change (stroke-width is not touched). Defaults to True.
- """
- # pylint: disable=attribute-defined-outside-init
- self.transform: Transform
- for element in self:
- if isinstance(element, PathElement) and apply_to_paths:
- element.path = element.path.transform(self.transform)
- else:
- element.transform = self.transform @ element.transform
- if isinstance(element, GroupBase):
- element.bake_transforms_recursively(apply_to_paths)
- self.transform = None
-
-
-class Group(GroupBase):
- """Any group element (layer or regular group)"""
-
- tag_name = "g"
-
- @classmethod
- def new(cls, label, *children, **attrs):
- attrs["inkscape:label"] = label
- return super().new(*children, **attrs)
-
- def effective_style(self):
- """A blend of each child's style mixed together (last child wins)"""
- style = self.style
- for child in self:
- style.update(child.effective_style())
- return style
-
- @property
- def groupmode(self):
- """Return the type of group this is"""
- return self.get("inkscape:groupmode", "group")
-
-
-class Layer(Group):
- """Inkscape extension of svg:g"""
-
- def _init(self):
- self.set("inkscape:groupmode", "layer")
-
- @classmethod
- def is_class_element(cls, elem):
- # type: (etree.Element) -> bool
- return elem.attrib.get(addNS("inkscape:groupmode"), None) == "layer"
-
-
-class Anchor(GroupBase):
- """An anchor or link tag"""
-
- tag_name = "a"
-
- @classmethod
- def new(cls, href, *children, **attrs):
- attrs["xlink:href"] = href
- return super().new(*children, **attrs)
-
-
-class ClipPath(GroupBase):
- """A path used to clip objects"""
-
- tag_name = "clipPath"
-
-
-class Marker(GroupBase, ViewboxMixin):
- """The element defines the graphic that is to be used for drawing
- arrowheads or polymarkers on a given , , or
- element."""
-
- tag_name = "marker"
-
- def get_viewbox(self) -> List[float]:
- """Returns the viewbox of the Marker, falling back to
- [0 0 markerWidth markerHeight]
-
- .. versionadded:: 1.3"""
- vbox = self.get("viewBox", None)
- result = self.parse_viewbox(vbox)
- if result is None:
- # use viewport, https://www.w3.org/TR/SVG11/painting.html#MarkerElement
- return [
- 0,
- 0,
- self.to_dimensionless(self.get("markerWidth")),
- self.to_dimensionless(self.get("markerHeight")),
- ]
- return result
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_image.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_image.py
deleted file mode 100644
index 9294d73..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_image.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2020 - Martin Owens
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-"""
-Image element interface.
-"""
-
-from ._polygons import RectangleBase
-
-
-class Image(RectangleBase):
- """Provide a useful extension for image elements"""
-
- tag_name = "image"
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_meta.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_meta.py
deleted file mode 100644
index eceb2c1..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_meta.py
+++ /dev/null
@@ -1,466 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2020 Martin Owens
-# Maren Hachmann
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# pylint: disable=arguments-differ
-"""
-Provide extra utility to each svg element type specific to its type.
-
-This is useful for having a common interface for each element which can
-give path, transform, and property access easily.
-"""
-
-from __future__ import annotations
-import math
-
-from typing import List, Optional
-
-from lxml import etree
-
-from inkex.deprecated.meta import deprecate
-
-from ..styles import StyleSheet
-from ..transforms import BoundingBox, Vector2d, VectorLike, DirectedLineSegment
-
-from ._base import BaseElement
-
-
-class Defs(BaseElement):
- """A header defs element, one per document"""
-
- tag_name = "defs"
-
-
-class StyleElement(BaseElement):
- """A CSS style element containing multiple style definitions"""
-
- tag_name = "style"
-
- def set_text(self, content):
- """Sets the style content text as a CDATA section"""
- self.text = etree.CDATA(str(content))
-
- def stylesheet(self):
- """Return the StyleSheet() object for the style tag"""
- return StyleSheet(self.text, callback=self.set_text)
-
-
-class Script(BaseElement):
- """A javascript tag in SVG"""
-
- tag_name = "script"
-
- def set_text(self, content):
- """Sets the style content text as a CDATA section"""
- self.text = etree.CDATA(str(content))
-
-
-class Desc(BaseElement):
- """Description element"""
-
- tag_name = "desc"
-
-
-class Title(BaseElement):
- """Title element"""
-
- tag_name = "title"
-
-
-class NamedView(BaseElement):
- """The NamedView element is Inkscape specific metadata about the file"""
-
- tag_name = "sodipodi:namedview"
-
- current_layer = property(lambda self: self.get("inkscape:current-layer"))
-
- @property
- def center(self):
- """Returns view_center in terms of document units"""
- return Vector2d(
- self.root.viewport_to_unit(self.get("inkscape:cx") or 0),
- self.root.viewport_to_unit(self.get("inkscape:cy") or 0),
- )
-
- def get_guides(self):
- """Returns a list of guides"""
- return self.findall("sodipodi:guide")
-
- def add_guide(self, position, orient=True, name=None) -> Guide:
- """Creates a new guide in this namedview
-
- .. versionadded:: 1.3
-
- Args:
- position: a float containing the y position for ``orient is True``, or
- the x position for ``orient is False``. The position is specified in the
- post-1.0 coordinate system, i.e. y=0 is at the top left of the viewbox,
- positive y axis pointing down.
-
- Alternatively, the position may be given as Tuple (or VectorLike)
- orient: True for horizontal, False for Vertical
-
- alternatively: Tuple / Vector specifying x and y coordinates of the
- normal vector of the guide, or the (clockwise) angle between the
- horizontal axis and the guide. Defaults to True (horizontal)
- name: label of the guide
-
- Returns:
- the created guide"""
- elem = self.add(Guide())
-
- if orient is True:
- elem.set_position(0, position, (0, -1))
- elif orient is False:
- elem.set_position(position, self.root.viewbox_height, (1, 0))
- else:
- pos = Vector2d(position)
- elem.set_position(pos.x, pos.y, orient)
- if name:
- elem.set("inkscape:label", str(name))
- return elem
-
- @deprecate
- def new_guide(self, position, orient=True, name=None):
- """
- .. deprecated:: 1.3
- Use :func:`add_guide` instead.
-
- Creates a new guide in this namedview
-
- Args:
- position: a float containing the y position for ``orient is True``, or
- the x position for ``orient is False``
-
- .. versionchanged:: 1.2
- Alternatively, the position may be given as Tuple (or VectorLike)
- orient: True for horizontal, False for Vertical
-
- .. versionchanged:: 1.2
- Tuple / Vector specifying x and y coordinates of the normal vector
- of the guide.
- name: label of the guide
-
- Returns:
- the created guide"""
- if orient is True:
- elem = Guide().move_to(0, position, (0, 1))
- elif orient is False:
- elem = Guide().move_to(position, 0, (1, 0))
- else:
- elem = Guide().move_to(*position, orient)
- if name:
- elem.set("inkscape:label", str(name))
- return self.add(elem)
-
- @deprecate
- def new_unique_guide(
- self, position: VectorLike, orientation: VectorLike
- ) -> Optional[Guide]:
- """
- .. deprecated:: 1.3
- Use :func:`add_unique_guide` instead.
-
- Add a guide iif there is no guide that looks the same.
-
- .. versionadded:: 1.2
-
- """
- elem = Guide().move_to(position[0], position[1], orientation)
- return self.add(elem) if self.get_similar_guide(elem) is None else None
-
- def add_unique_guide(
- self, position: VectorLike, orientation: VectorLike
- ) -> Optional[Guide]:
- """Add a guide iif there is no guide that looks the same.
-
- .. versionadded:: 1.3
-
- Args:
- position: Position as Tuple / Vector
- orientation: Tuple / Vector specifying x and y coordinates of the normal
- vector of the guide.
- name: label of the guide
- """
- elem = self.add(Guide()).set_position(position[0], position[1], orientation)
- if self.get_similar_guide(elem) is not None:
- self.remove(elem)
- return None
- return elem
-
- def get_similar_guide(self, other: Guide) -> Optional[Guide]:
- """Check if the namedview contains a guide that looks identical to one
- defined by (position, orientation) and is not identity (same element) as the
- first one. If such a guide exists, return it; otherwise, return None.
-
- .. versionadded:: 1.2"""
- for guide in self.get_guides():
- if Guide.guides_coincident(guide, other) and guide != other:
- return guide
- return None
-
- def _get_pages(self) -> List[Page]:
- """Returns all page elements"""
- return self.findall("inkscape:page")
-
- def _equivalent_page(self) -> Page:
- """Returns an unrooted page based on the viewbox dimensions"""
- return Page.new(self.root.viewbox_width, self.root.viewbox_height, 0, 0)
-
- def get_pages(self) -> List[Page]:
- """Returns a list of pages within the document. For single page documents,
- a detached page element with dimensions according to the viewbox will be
- returned.
-
- .. versionadded:: 1.2
-
- .. versionchanged:: 1.3
- For single-page documents, this function now returns the viewbox
- dimensions.
- """
- pages = self._get_pages()
- if len(pages) < 2:
- return [self._equivalent_page()]
- return pages
-
- def new_page(self, x, y, width, height, label=None):
- """Creates a new page in this namedview. Always add pages through this
- function to ensure that single-page documents are treated correctly.
-
- .. versionadded:: 1.2
-
- .. versionchanged:: 1.3
- If none exists, a page element with the viewbox dimensions will be
- inserted before the new page."""
- if len(self._get_pages()) == 0:
- self.add(self._equivalent_page())
- elem = Page(width=width, height=height, x=x, y=y)
- if label:
- elem.set("inkscape:label", str(label))
- return self.add(elem)
-
-
-class Guide(BaseElement):
- """An inkscape guide"""
-
- tag_name = "sodipodi:guide"
-
- @property
- def orientation(self) -> Vector2d:
- """Vector normal to the guide, in the pre-1.0 coordinate system (y axis upwards)
-
- .. versionadded:: 1.2"""
- return Vector2d(self.get("orientation"), fallback=(1, 0))
-
- @property
- def angle(self) -> float:
- """(Clockwise) angle between the guide and the horizontal axis in degrees
- (i.e. what Inkscape 1.2+ shows as "Angle" in the guide properties)
-
- .. versionadded:: 1.3"""
- return math.degrees(math.atan2(*self.orientation))
-
- is_horizontal = property(
- lambda self: self.orientation[0] == 0 and self.orientation[1] != 0
- )
- is_vertical = property(
- lambda self: self.orientation[0] != 0 and self.orientation[1] == 0
- )
-
- @property
- def raw_position(self) -> Vector2d:
- """Position of the guide handle. The y coordinate is flipped and relative
- to the bottom of the viewbox, this is a remnant of the pre-1.0 coordinate system
- """
- return Vector2d(self.get("position"), fallback=(0, 0))
-
- def point(self):
- """Use raw_position or position instead"""
- return self.raw_position
-
- point = property(deprecate(point)) # type: ignore
-
- @property
- def position(self) -> Vector2d:
- """Position of the guide handle in normal coordinates, i.e. (0,0) is at
- the top left corner of the viewbox, positive y axis pointing downwards.
-
- This function can only be used for guides which are attached to a root
- svg element."""
- pos = self.raw_position
- return Vector2d(pos.x, self.root.viewbox_height - pos.y)
-
- @classmethod
- def new(cls, pos_x, pos_y, angle, **attrs):
- guide = super().new(**attrs)
- guide.set_position(pos_x, pos_y, angle=angle)
- return guide
-
- def set_position(self, pos_x, pos_y, angle=None):
- """
- Move this guide to the given x,y position and optionally set its orientation.
- The coordinate system used is the post-1.0 coordinate system (origin in the
- top left corner, y axis pointing down), which also defines the sense of
- rotation.
-
- The guide must be rooted for this function to be used. Preferably, use
- :func:`inkex.elements._meta.add_guide` to create a new guide.
-
- .. versionadded:: 1.3
-
- Args:
- pos_x (Union[str, int, float]): x position of the guide's reference point
- pos_y (Union[str, int, float]): y position of the guide's reference point
- angle (Union[str, float, int, tuple, list], optional): Angle may be a
- string, float or integer, which will set the clockwise angle between the
- horizontal axis and the guide.
-
- Alternatively, it may be a pair of numbers (tuple) which will be set
- as normal vector.
-
- If not given at all, the orientation remains unchanged.
- Defaults to None.
-
- Returns:
- Guide: the modified guide
- """
- pos_y = self.root.viewbox_height - float(pos_y)
- self.set("position", f"{float(pos_x):g},{float(pos_y):g}")
- if isinstance(angle, str):
- if "," not in angle:
- angle = float(angle)
-
- if isinstance(angle, (float, int)):
- # Generate orientation from angle
- angle = (math.sin(math.radians(angle)), -math.cos(math.radians(angle)))
-
- if isinstance(angle, (tuple, list)) and len(angle) == 2:
- angle = ",".join(f"{i:g}" for i in [angle[0], -angle[1]])
-
- if angle is not None:
- self.set("orientation", angle)
- return self
-
- @deprecate
- def move_to(self, pos_x, pos_y, angle=None):
- """
- .. deprecated:: 1.3
- Use :func:`set_position` instead.
-
- Move this guide to the given x,y position,
-
- Angle may be a float or integer, which will change the orientation. Alternately,
- it may be a pair of numbers (tuple) which will set the orientation directly.
- If not given at all, the orientation remains unchanged.
- """
- self.set("position", f"{float(pos_x):g},{float(pos_y):g}")
- if isinstance(angle, str):
- if "," not in angle:
- angle = float(angle)
-
- if isinstance(angle, (float, int)):
- # Generate orientation from angle
- angle = (math.sin(math.radians(angle)), -math.cos(math.radians(angle)))
-
- if isinstance(angle, (tuple, list)) and len(angle) == 2:
- angle = ",".join(f"{i:g}" for i in angle)
-
- if angle is not None:
- self.set("orientation", angle)
- return self
-
- @staticmethod
- def guides_coincident(guide1, guide2):
- """Check if two guides defined by (position, orientation) and (opos, oor) look
- identical (i.e. the position lies on the other guide AND the guide is
- (anti)parallel to the other guide).
-
- .. versionadded:: 1.2"""
- # normalize orientations first
- orientation = guide1.orientation / guide1.orientation.length
- oor = guide2.orientation / guide2.orientation.length
-
- position = guide1.raw_position
- opos = guide2.raw_position
-
- return (
- DirectedLineSegment(
- position, position + Vector2d(orientation[1], -orientation[0])
- ).perp_distance(*opos)
- < 1e-6
- and abs(abs(orientation[1] * oor[0]) - abs(orientation[0] * oor[1])) < 1e-6
- )
-
-
-class Metadata(BaseElement):
- """Inkscape Metadata element"""
-
- tag_name = "metadata"
-
-
-class ForeignObject(BaseElement):
- """SVG foreignObject element"""
-
- tag_name = "foreignObject"
-
-
-class Switch(BaseElement):
- """A switch element"""
-
- tag_name = "switch"
-
-
-class Grid(BaseElement):
- """A namedview grid child"""
-
- tag_name = "inkscape:grid"
-
-
-class Page(BaseElement):
- """A namedview page child
-
- .. versionadded:: 1.2"""
-
- tag_name = "inkscape:page"
-
- width = property(lambda self: self.to_dimensionless(self.get("width") or 0))
- height = property(lambda self: self.to_dimensionless(self.get("height") or 0))
- x = property(lambda self: self.to_dimensionless(self.get("x") or 0))
- y = property(lambda self: self.to_dimensionless(self.get("y") or 0))
-
- @classmethod
- def new(cls, width, height, x, y):
- """Creates a new page element in the namedview"""
- page = super().new()
- page.move_to(x, y)
- page.set("width", width)
- page.set("height", height)
- return page
-
- def move_to(self, x, y):
- """Move this page to the given x,y position"""
- self.set("x", f"{float(x):g}")
- self.set("y", f"{float(y):g}")
- return self
-
- @property
- def bounding_box(self) -> BoundingBox:
- """Returns the bounding box of the page."""
- return BoundingBox(
- (self.x, self.x + self.width), (self.y, self.y + self.height)
- )
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_parser.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_parser.py
deleted file mode 100644
index ffe4e08..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_parser.py
+++ /dev/null
@@ -1,118 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2020 Martin Owens
-# Sergei Izmailov
-# Thomas Holder
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-"""Utilities for parsing SVG documents.
-
-.. versionadded:: 1.2
- Separated out from :py:mod:`inkex.elements._base`"""
-
-from collections import defaultdict
-from typing import DefaultDict, List, Any, Type
-
-from lxml import etree
-
-from ..interfaces.IElement import IBaseElement
-
-from ._utils import splitNS
-from ..utils import errormsg
-from ..localization import inkex_gettext as _
-
-
-class NodeBasedLookup(etree.PythonElementClassLookup):
- """
- We choose what kind of Elements we should return for each element, providing useful
- SVG based API to our extensions system.
- """
-
- default: Type[IBaseElement]
-
- # (ns,tag) -> list(cls) ; ascending priority
- lookup_table = defaultdict(list) # type: DefaultDict[str, List[Any]]
-
- @classmethod
- def register_class(cls, klass):
- """Register the given class using it's attached tag name"""
- cls.lookup_table[splitNS(klass.tag_name)].append(klass)
-
- @classmethod
- def find_class(cls, xpath):
- """Find the class for this type of element defined by an xpath
-
- .. versionadded:: 1.1"""
- if isinstance(xpath, type):
- return xpath
- for kls in cls.lookup_table[splitNS(xpath.split("/")[-1])]:
- # TODO: We could create a apply the xpath attrs to the test element
- # to narrow the search, but this does everything we need right now.
- test_element = kls()
- if kls.is_class_element(test_element):
- return kls
- raise KeyError(f"Could not find svg tag for '{xpath}'")
-
- def lookup(self, doc, element): # pylint: disable=unused-argument
- """Lookup called by lxml when assigning elements their object class"""
- try:
- for kls in reversed(self.lookup_table[splitNS(element.tag)]):
- if kls.is_class_element(element): # pylint: disable=protected-access
- return kls
- except TypeError:
- # Handle non-element proxies case
- # The documentation implies that it's not possible
- # Didn't found a reliable way to check whether proxy corresponds to element
- # or not
- # Look like lxml issue to me.
- # The troubling element is ""
- return None
- return NodeBasedLookup.default
-
-
-SVG_PARSER = etree.XMLParser(huge_tree=True, strip_cdata=False, recover=True)
-SVG_PARSER.set_element_class_lookup(NodeBasedLookup())
-
-
-def load_svg(stream):
- """Load SVG file using the SVG_PARSER"""
- if (isinstance(stream, str) and stream.lstrip().startswith("<")) or (
- isinstance(stream, bytes) and stream.lstrip().startswith(b"<")
- ):
- parsed = etree.ElementTree(etree.fromstring(stream, parser=SVG_PARSER))
- else:
- parsed = etree.parse(stream, parser=SVG_PARSER)
- if len(SVG_PARSER.error_log) > 0:
- errormsg(
- _(
- "A parsing error occurred, which means you are likely working with "
- "a non-conformant SVG file. The following errors were found:\n"
- )
- )
- for __, element in enumerate(SVG_PARSER.error_log):
- errormsg(
- _("{}. Line {}, column {}").format(
- element.message, element.line, element.column
- )
- )
- errormsg(
- _(
- "\nProcessing will continue; however we encourage you to fix your"
- " file manually."
- )
- )
- return parsed
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_polygons.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_polygons.py
deleted file mode 100644
index 04ea9aa..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_polygons.py
+++ /dev/null
@@ -1,509 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2020 Martin Owens
-# Sergei Izmailov
-# Thomas Holder
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# pylint: disable=arguments-differ
-"""
-Interface for all shapes/polygons such as lines, paths, rectangles, circles etc.
-"""
-
-from math import cos, pi, sin
-from typing import Optional, Tuple
-from ..paths import Arc, Curve, Move, Path, ZoneClose
-from ..paths import Line as PathLine
-from ..transforms import Transform, ImmutableVector2d, Vector2d
-from ..bezier import pointdistance
-
-from ._utils import addNS
-from ._base import ShapeElement
-
-
-class PathElementBase(ShapeElement):
- """Base element for path based shapes"""
-
- get_path = lambda self: Path(self.get("d"))
-
- @classmethod
- def new(cls, path, **attrs):
- return super().new(d=Path(path), **attrs)
-
- def set_path(self, path):
- """Set the given data as a path as the 'd' attribute"""
- self.set("d", str(Path(path)))
-
- def apply_transform(self):
- """Apply the internal transformation to this node and delete"""
- if "transform" in self.attrib:
- self.path = self.path.transform(self.transform)
- self.set("transform", Transform())
-
- @property
- def original_path(self):
- """Returns the original path if this is a LPE, or the path if not"""
- return Path(self.get("inkscape:original-d", self.path))
-
- @original_path.setter
- def original_path(self, path):
- if addNS("inkscape:original-d") in self.attrib:
- self.set("inkscape:original-d", str(Path(path)))
- else:
- self.path = path
-
-
-class PathElement(PathElementBase):
- """Provide a useful extension for path elements"""
-
- tag_name = "path"
-
- @staticmethod
- def _arcpath(
- cx: float,
- cy: float,
- rx: float,
- ry: float,
- start: float,
- end: float,
- arctype: str,
- ) -> Optional[Path]:
- """Compute the path for an arc defined by Inkscape-specific attributes.
-
- For details on arguments, see :func:`arc`.
-
- .. versionadded:: 1.2"""
- if abs(rx) < 1e-8 or abs(ry) < 1e-8:
- return None
- incr = end - start
- if incr < 0:
- incr += 2 * pi
- numsegs = min(1 + int(incr * 2.0 / pi), 4)
- incr = incr / numsegs
-
- computed = Path()
- computed.append(Move(cos(start), sin(start)))
- for seg in range(1, numsegs + 1):
- computed.append(
- Arc(1, 1, 0, 0, 1, cos(start + seg * incr), sin(start + seg * incr))
- )
- if abs(incr * numsegs - 2 * pi) > 1e-8 and (
- arctype in ("slice", "")
- ): # slice is default
- computed.append(PathLine(0, 0))
- if arctype != "arc":
- computed.append(ZoneClose())
- computed.transform(
- Transform().add_translate(cx, cy).add_scale(rx, ry), inplace=True
- )
- return computed.to_relative()
-
- @classmethod
- def arc(
- cls, center, rx, ry=None, arctype="", pathonly=False, **kw
- ): # pylint: disable=invalid-name
- """Generates a sodipodi elliptical arc (special type). Also computes the path
- that Inkscape uses under the hood.
- All data may be given as parseable strings or using numeric data types.
-
- Args:
- center (tuple-like): Coordinates of the star/polygon center as tuple or
- Vector2d
- rx (Union[float, str]): Radius in x direction
- ry (Union[float, str], optional): Radius in y direction. If not given,
- ry=rx. Defaults to None.
- arctype (str, optional): "arc", "chord" or "slice". Defaults to "", i.e.
- "slice".
-
- .. versionadded:: 1.2
- Previously set to "arc" as fixed value
- pathonly (bool, optional): Whether to create the path without
- Inkscape-specific attributes. Defaults to False.
-
- .. versionadded:: 1.2
- Keyword args:
- start (Union[float, str]): start angle in radians
- end (Union[float, str]): end angle in radians
- open (str): whether the path should be open (true/false). Not used in
- Inkscape > 1.1
-
- Returns:
- PathElement : the created star/polygon
- """
- others = [(name, kw.pop(name, None)) for name in ("start", "end", "open")]
- elem = cls(**kw)
- elem.set("sodipodi:cx", center[0])
- elem.set("sodipodi:cy", center[1])
- elem.set("sodipodi:rx", rx)
- elem.set("sodipodi:ry", ry or rx)
- elem.set("sodipodi:type", "arc")
- if arctype != "":
- elem.set("sodipodi:arc-type", arctype)
- for name, value in others:
- if value is not None:
- elem.set("sodipodi:" + name, str(value).lower())
-
- path = cls._arcpath(
- float(center[0]),
- float(center[1]),
- float(rx),
- float(ry or rx),
- float(elem.get("sodipodi:start", 0)),
- float(elem.get("sodipodi:end", 2 * pi)),
- arctype,
- )
- if pathonly:
- elem = cls(**kw)
- if path is not None:
- elem.path = path
- return elem
-
- @staticmethod
- def _starpath(
- c: Tuple[float, float],
- sides: int,
- r: Tuple[float, float], # pylint: disable=invalid-name
- arg: Tuple[float, float],
- rounded: float,
- flatsided: bool,
- ):
- """Helper method to generate the path for an Inkscape star/ polygon; randomized
- is ignored.
-
- For details on arguments, see :func:`star`.
-
- .. versionadded:: 1.2"""
-
- def _star_get_xy(point, index):
- cur_arg = arg[point] + 2 * pi / sides * (index % sides)
- return Vector2d(*c) + r[point] * Vector2d(cos(cur_arg), sin(cur_arg))
-
- def _rot90_rel(origin, other):
- """Returns a unit length vector at 90 deg from origin to other"""
- return (
- 1
- / pointdistance(other, origin)
- * Vector2d(other.y - origin.y, other.x - origin.x)
- )
-
- def _star_get_curvepoint(point, index, is_prev: bool):
- index = index % sides
- orig = _star_get_xy(point, index)
- previ = (index - 1 + sides) % sides
- nexti = (index + 1) % sides
- # neighbors of the current point depend on polygon or star
- prev = (
- _star_get_xy(point, previ)
- if flatsided
- else _star_get_xy(1 - point, index if point == 1 else previ)
- )
- nextp = (
- _star_get_xy(point, nexti)
- if flatsided
- else _star_get_xy(1 - point, index if point == 0 else nexti)
- )
- mid = 0.5 * (prev + nextp)
- # direction of bezier handles
- rot = _rot90_rel(orig, mid + 100000 * _rot90_rel(mid, nextp))
- ret = (
- rounded
- * rot
- * (
- -1 * pointdistance(prev, orig)
- if is_prev
- else pointdistance(nextp, orig)
- )
- )
- return orig + ret
-
- pointy = abs(rounded) < 1e-4
- result = Path()
- result.append(Move(*_star_get_xy(0, 0)))
- for i in range(0, sides):
- # draw to point type 1 for stars
- if not flatsided:
- if pointy:
- result.append(PathLine(*_star_get_xy(1, i)))
- else:
- result.append(
- Curve(
- *_star_get_curvepoint(0, i, False),
- *_star_get_curvepoint(1, i, True),
- *_star_get_xy(1, i),
- )
- )
- # draw to point type 0 for both stars and rectangles
- if pointy and i < sides - 1:
- result.append(PathLine(*_star_get_xy(0, i + 1)))
- if not pointy:
- if not flatsided:
- result.append(
- Curve(
- *_star_get_curvepoint(1, i, False),
- *_star_get_curvepoint(0, i + 1, True),
- *_star_get_xy(0, i + 1),
- )
- )
- else:
- result.append(
- Curve(
- *_star_get_curvepoint(0, i, False),
- *_star_get_curvepoint(0, i + 1, True),
- *_star_get_xy(0, i + 1),
- )
- )
-
- result.append(ZoneClose())
- return result.to_relative()
-
- @classmethod
- def star(
- cls,
- center,
- radii,
- sides=5,
- rounded=0,
- args=(0, 0),
- flatsided=False,
- pathonly=False,
- ):
- """Generate a sodipodi star / polygon. Also computes the path that Inkscape uses
- under the hood. The arguments for center, radii, sides, rounded and args can be
- given as strings or as numeric data.
-
- .. versionadded:: 1.1
-
- Args:
- center (Tuple-like): Coordinates of the star/polygon center as tuple or
- Vector2d
- radii (tuple): Radii of the control points, i.e. their distances from the
- center. The control points are specified in polar coordinates. Only the
- first control point is used for polygons.
- sides (int, optional): Number of sides / tips of the polygon / star.
- Defaults to 5.
- rounded (int, optional): Controls the rounding radius of the polygon / star.
- For `rounded=0`, only straight lines are used. Defaults to 0.
- args (tuple, optional): Angle between horizontal axis and control points.
- Defaults to (0,0).
-
- .. versionadded:: 1.2
- Previously fixed to (0.85, 1.3)
- flatsided (bool, optional): True for polygons, False for stars.
- Defaults to False.
-
- .. versionadded:: 1.2
- pathonly (bool, optional): Whether to create the path without
- Inkscape-specific attributes. Defaults to False.
-
- .. versionadded:: 1.2
-
- Returns:
- PathElement : the created star/polygon
- """
- elem = cls()
- elem.set("sodipodi:cx", center[0])
- elem.set("sodipodi:cy", center[1])
- elem.set("sodipodi:r1", radii[0])
- elem.set("sodipodi:r2", radii[1])
- elem.set("sodipodi:arg1", args[0])
- elem.set("sodipodi:arg2", args[1])
- elem.set("sodipodi:sides", max(sides, 3) if flatsided else max(sides, 2))
- elem.set("inkscape:rounded", rounded)
- elem.set("inkscape:flatsided", str(flatsided).lower())
- elem.set("sodipodi:type", "star")
-
- path = cls._starpath(
- (float(center[0]), float(center[1])),
- int(sides),
- (float(radii[0]), float(radii[1])),
- (float(args[0]), float(args[1])),
- float(rounded),
- flatsided,
- )
- if pathonly:
- elem = cls()
- # inkex.errormsg(path)
- if path is not None:
- elem.path = path
-
- return elem
-
-
-class Polyline(ShapeElement):
- """Like a path, but made up of straight line segments only"""
-
- tag_name = "polyline"
-
- def get_path(self):
- return Path("M" + self.get("points"))
-
- def set_path(self, path):
- points = [f"{x:g},{y:g}" for x, y in Path(path).end_points]
- self.set("points", " ".join(points))
-
-
-class Polygon(ShapeElement):
- """A closed polyline"""
-
- tag_name = "polygon"
- get_path = lambda self: Path("M" + self.get("points") + " Z")
-
-
-class Line(ShapeElement):
- """A line segment connecting two points"""
-
- tag_name = "line"
- x1 = property(lambda self: self.to_dimensionless(self.get("x1", 0)))
- y1 = property(lambda self: self.to_dimensionless(self.get("y1", 0)))
- x2 = property(lambda self: self.to_dimensionless(self.get("x2", 0)))
- y2 = property(lambda self: self.to_dimensionless(self.get("y2", 0)))
- get_path = lambda self: Path(f"M{self.x1},{self.y1} L{self.x2},{self.y2}")
-
- @classmethod
- def new(cls, start, end, **attrs):
- start = Vector2d(start)
- end = Vector2d(end)
- return super().new(x1=start.x, y1=start.y, x2=end.x, y2=end.y, **attrs)
-
-
-class RectangleBase(ShapeElement):
- """Provide a useful extension for rectangle elements"""
-
- left = property(lambda self: self.to_dimensionless(self.get("x", "0")))
- top = property(lambda self: self.to_dimensionless(self.get("y", "0")))
- right = property(lambda self: self.left + self.width)
- bottom = property(lambda self: self.top + self.height)
- width = property(lambda self: self.to_dimensionless(self.get("width", "0")))
- height = property(lambda self: self.to_dimensionless(self.get("height", "0")))
- rx = property(
- lambda self: self.to_dimensionless(self.get("rx", self.get("ry", 0.0)))
- )
- ry = property(
- lambda self: self.to_dimensionless(self.get("ry", self.get("rx", 0.0)))
- ) # pylint: disable=invalid-name
-
- def get_path(self):
- """Calculate the path as the box around the rect"""
- if self.rx or self.ry:
- # pylint: disable=invalid-name
- rx = min(self.rx if self.rx > 0 else self.ry, self.width / 2)
- ry = min(self.ry if self.ry > 0 else self.rx, self.height / 2)
- cpts = [self.left + rx, self.right - rx, self.top + ry, self.bottom - ry]
- return (
- f"M {cpts[0]},{self.top}"
- f"L {cpts[1]},{self.top} "
- f"A {rx},{ry} 0 0 1 {self.right},{cpts[2]}"
- f"L {self.right},{cpts[3]} "
- f"A {rx},{ry} 0 0 1 {cpts[1]},{self.bottom}"
- f"L {cpts[0]},{self.bottom} "
- f"A {rx},{ry} 0 0 1 {self.left},{cpts[3]}"
- f"L {self.left},{cpts[2]} "
- f"A {rx},{ry} 0 0 1 {cpts[0]},{self.top} z"
- )
-
- return f"M {self.left},{self.top} h{self.width}v{self.height}h{-self.width} z"
-
-
-class Rectangle(RectangleBase):
- """Provide a useful extension for rectangle elements"""
-
- tag_name = "rect"
-
- @classmethod
- def new(cls, left, top, width, height, **attrs):
- return super().new(x=left, y=top, width=width, height=height, **attrs)
-
-
-class EllipseBase(ShapeElement):
- """Absorbs common part of Circle and Ellipse classes"""
-
- def get_path(self):
- """Calculate the arc path of this circle"""
- rx, ry = self.rxry()
- cx, y = self.center.x, self.center.y - ry
- return (
- "M {cx},{y} "
- "a {rx},{ry} 0 1 0 {rx}, {ry} "
- "a {rx},{ry} 0 0 0 -{rx}, -{ry} z"
- ).format(cx=cx, y=y, rx=rx, ry=ry)
-
- @property
- def center(self):
- """Return center of circle/ellipse"""
- return ImmutableVector2d(
- self.to_dimensionless(self.get("cx", "0")),
- self.to_dimensionless(self.get("cy", "0")),
- )
-
- @center.setter
- def center(self, value):
- value = Vector2d(value)
- self.set("cx", value.x)
- self.set("cy", value.y)
-
- def rxry(self):
- # type: () -> Vector2d
- """Helper function"""
- raise NotImplementedError()
-
- @classmethod
- def new(cls, center, radius, **attrs):
- circle = super().new(**attrs)
- circle.center = center
- circle.radius = radius
- return circle
-
-
-class Circle(EllipseBase):
- """Provide a useful extension for circle elements"""
-
- tag_name = "circle"
-
- @property
- def radius(self) -> float:
- """Return radius of circle"""
- return self.to_dimensionless(self.get("r", "0"))
-
- @radius.setter
- def radius(self, value):
- self.set("r", self.to_dimensionless(value))
-
- def rxry(self):
- r = self.radius
- return Vector2d(r, r)
-
-
-class Ellipse(EllipseBase):
- """Provide a similar extension to the Circle interface for ellipses"""
-
- tag_name = "ellipse"
-
- @property
- def radius(self) -> ImmutableVector2d:
- """Return radii of ellipse"""
- return ImmutableVector2d(
- self.to_dimensionless(self.get("rx", "0")),
- self.to_dimensionless(self.get("ry", "0")),
- )
-
- @radius.setter
- def radius(self, value):
- value = Vector2d(value)
- self.set("rx", str(value.x))
- self.set("ry", str(value.y))
-
- def rxry(self):
- return self.radius
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_selected.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_selected.py
deleted file mode 100644
index 356486b..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_selected.py
+++ /dev/null
@@ -1,236 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2020 Martin Owens
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc.,Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-"""
-When elements are selected, these structures provide an advanced API.
-
-.. versionadded:: 1.1
-"""
-
-from collections import OrderedDict
-from typing import Any, overload, Union, Optional
-
-from ..interfaces.IElement import IBaseElement
-from ._utils import natural_sort_key
-from ..localization import inkex_gettext
-from ..utils import AbortExtension
-
-
-class ElementList(OrderedDict):
- """
- A list of elements, selected by id, iterator or xpath
-
- This may look like a dictionary, but it is really a list of elements.
- The default iterator is the element objects themselves (not keys) and it is
- possible to key elements by their numerical index.
-
- It is also possible to look up items by their id and the element object itself.
- """
-
- def __init__(self, svg, _iter=None):
- self.svg = svg
- self.ids = OrderedDict()
- super().__init__()
- if _iter is not None:
- self.set(*list(_iter))
-
- def __iter__(self):
- return self.values().__iter__()
-
- def __getitem__(self, key):
- return super().__getitem__(self._to_key(key))
-
- def __contains__(self, key):
- return super().__contains__(self._to_key(key))
-
- def __setitem__(self, orig_key, elem):
- if orig_key != elem and orig_key != elem.get("id"):
- raise ValueError(f"Refusing to set bad key in ElementList {orig_key}")
- if isinstance(elem, str):
- key = elem
- elem = self.svg.getElementById(elem, literal=True)
- if elem is None:
- return
- if isinstance(elem, IBaseElement):
- # Selection is a list of elements to select
- key = elem.xml_path
- element_id = elem.get("id")
- if element_id is not None:
- self.ids[element_id] = key
- super().__setitem__(key, elem)
- else:
- kind = type(elem).__name__
- raise ValueError(f"Unknown element type: {kind}")
-
- @overload
- def _to_key(self, key: None, default: Any) -> Any:
- ...
-
- @overload
- def _to_key(self, key: Union[int, IBaseElement, str], default: Any) -> str:
- ...
-
- def _to_key(self, key, default=None) -> str:
- """Takes a key (id, element, etc) and returns an xml_path key"""
-
- if self and key is None:
- key = default
- if isinstance(key, int):
- return list(self.keys())[key]
- if isinstance(key, IBaseElement):
- return key.xml_path
- if isinstance(key, str) and key[0] != "/":
- return self.ids.get(key, key)
- return key
-
- def clear(self):
- """Also clear ids"""
- self.ids.clear()
- super().clear()
-
- def set(self, *ids):
- """
- Sets the currently selected elements to these ids, any existing
- selection is cleared.
-
- Arguments a list of element ids, element objects or
- a single xpath expression starting with ``//``.
-
- All element objects must have an id to be correctly set.
-
- >>> selection.set("rect123", "path456", "text789")
- >>> selection.set(elem1, elem2, elem3)
- >>> selection.set("//rect")
- """
- self.clear()
- self.add(*ids)
-
- def pop(self, key=None):
- """Remove the key item or remove the last item selected"""
- item = super().pop(self._to_key(key, default=-1))
- self.ids.pop(item.get("id"))
- return item
-
- def add(self, *ids):
- """Like set() but does not clear first"""
- # Allow selecting of xpath elements directly
- if len(ids) == 1 and isinstance(ids[0], str) and ids[0].startswith("//"):
- ids = self.svg.xpath(ids[0])
-
- for elem in ids:
- self[elem] = elem # This doesn't matter
-
- def rendering_order(self):
- """Get the selected elements by z-order (stacking order), ordered from bottom to
- top
-
- .. versionadded:: 1.2
- :func:`paint_order` has been renamed to :func:`rendering_order`"""
- new_list = ElementList(self.svg)
- # the elements are stored with their xpath index, so a natural sort order
- # '3' < '20' < '100' has to be applied
- new_list.set(
- *[
- elem
- for _, elem in sorted(
- self.items(), key=lambda x: natural_sort_key(x[0])
- )
- ]
- )
- return new_list
-
- def filter(self, *types):
- """Filter selected elements of the given type, returns a new SelectedElements
- object"""
- return ElementList(
- self.svg, [e for e in self if not types or isinstance(e, types)]
- )
-
- def filter_nonzero(self, *types, error_msg: Optional[str] = None):
- """Filter selected elements of the given type, returns a new SelectedElements
- object. If the selection is empty, abort the extension.
-
- .. versionadded:: 1.2
-
- :param error_msg: e
- :type error_msg: str, optional
-
- Args:
- *types (Type) : type(s) to filter the selection by
- error_msg (str, optional): error message that is displayed if the selection
- is empty, defaults to
- ``_("Please select at least one element of type(s) {}")``.
- Defaults to None.
-
- Raises:
- AbortExtension: if the selection is empty
-
- Returns:
- ElementList: filtered selection
- """
- filtered = self.filter(*types)
- if not filtered:
- if error_msg is None:
- error_msg = inkex_gettext(
- "Please select at least one element of the following type(s): {}"
- ).format(", ".join([type.__name__ for type in types]))
- raise AbortExtension(error_msg)
- return filtered
-
- def get(self, *types):
- """Like filter, but will enter each element searching for any child of the given
- types"""
-
- def _recurse(elem):
- if not types or isinstance(elem, types):
- yield elem
- for child in elem:
- yield from _recurse(child)
-
- return ElementList(
- self.svg,
- [
- r
- for e in self
- for r in _recurse(e)
- if isinstance(r, (IBaseElement, str))
- ],
- )
-
- def id_dict(self):
- """For compatibility, return regular dictionary of id -> element pairs"""
- return {eid: self[xid] for eid, xid in self.ids.items()}
-
- def bounding_box(self):
- """
- Gets a :class:`inkex.transforms.BoundingBox` object for the selected items.
-
- Text objects have a bounding box without width or height that only
- reflects the coordinate of their anchor. If a text object is a part of
- the selection's boundary, the bounding box may be inaccurate.
-
- When no object is selected or when the object's location cannot be
- determined (e.g. empty group or layer), all coordinates will be None.
- """
- return sum([elem.bounding_box() for elem in self], None)
-
- def first(self):
- """Returns the first item in the selected list"""
- for elem in self:
- return elem
- return None
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_svg.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_svg.py
deleted file mode 100644
index 85d9604..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_svg.py
+++ /dev/null
@@ -1,415 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2020 Martin Owens
-# Thomas Holder
-# Sergei Izmailov
-# Windell Oskay
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# pylint: disable=attribute-defined-outside-init
-#
-"""
-Provide a way to load lxml attributes with an svg API on top.
-"""
-
-import random
-import math
-import re
-
-from lxml import etree
-
-from ..css import ConditionalRule
-from ..interfaces.IElement import ISVGDocumentElement
-
-from ..deprecated.meta import DeprecatedSvgMixin, deprecate
-from ..units import discover_unit, parse_unit
-from ._selected import ElementList
-from ..transforms import BoundingBox
-from ..styles import StyleSheets
-
-from ._base import BaseElement, ViewboxMixin
-from ._meta import StyleElement, NamedView
-from ._utils import registerNS
-
-from typing import Optional, List, Tuple
-
-if False: # pylint: disable=using-constant-test
- import typing # pylint: disable=unused-import
-
-
-class SvgDocumentElement(
- DeprecatedSvgMixin, ISVGDocumentElement, BaseElement, ViewboxMixin
-):
- """Provide access to the document level svg functionality"""
-
- # pylint: disable=too-many-public-methods
- tag_name = "svg"
-
- selection: ElementList
- """The selection as passed by Inkscape (readonly)"""
-
- def _init(self):
- self.current_layer = None
- self.view_center = (0.0, 0.0)
- self.selection = ElementList(self)
- self.ids = {}
-
- def tostring(self):
- """Convert document to string"""
- return etree.tostring(etree.ElementTree(self))
-
- def get_ids(self):
- """Returns a set of unique document ids"""
- if not self.ids:
- self.ids = set(self.xpath("//@id"))
- return self.ids
-
- def get_unique_id(
- self,
- prefix: str,
- size: Optional[int] = None,
- blacklist: Optional[List[str]] = None,
- ):
- """Generate a new id from an existing old_id
-
- The id consists of a prefix and an appended random integer with size digits.
-
- If size is not given, it is determined automatically from the length of
- existing ids, i.e. those in the document plus those in the blacklist.
-
- Args:
- prefix (str): the prefix of the new ID.
- size (Optional[int], optional): number of digits of the second part of the
- id. If None, the length is chosen based on the amount of existing
- objects. Defaults to None.
-
- .. versionchanged:: 1.1
- The default of this parameter has been changed from 4 to None.
- blacklist (Optional[Iterable[str]], optional): An additional iterable of ids
- that are not allowed to be used. This is useful when bulk inserting
- objects.
- Defaults to None.
-
- .. versionadded:: 1.2
-
- Returns:
- _type_: _description_
- """
- ids = self.get_ids()
- if size is None:
- size = max(math.ceil(math.log10(len(ids) or 1000)) + 1, 4)
- if blacklist is not None:
- ids.update(blacklist)
- new_id = None
- _from = 10**size - 1
- _to = 10**size
- while new_id is None or new_id in ids:
- # Do not use randint because py2/3 incompatibility
- new_id = prefix + str(int(random.random() * _from - _to) + _to)
- self.ids.add(new_id)
- return new_id
-
- def get_page_bbox(self, page=None) -> BoundingBox:
- """Gets the page dimensions as a bbox. For single-page documents, the viewbox
- dimensions are returned.
-
- Args:
- page (int, optional): Page number. Defaults to the first page.
-
- .. versionadded:: 1.3
-
- Raises:
- IndexError: if the page number provided does not exist in the document.
-
- Returns:
- BoundingBox: the bounding box of the page
- """
- if page is None:
- page = 0
- pages = self.namedview.get_pages()
- if 0 <= page < len(pages):
- return pages[page].bounding_box
- raise IndexError("Invalid page number")
-
- def get_current_layer(self):
- """Returns the currently selected layer"""
- layer = self.getElementById(self.namedview.current_layer, "svg:g")
- if layer is None:
- return self
- return layer
-
- def add_namespace(self, prefix, url):
- """Adds an xml namespace to the xml parser with the desired prefix.
-
- If the prefix or url are already in use with different values, this
- function will raise an error. Remove any attributes or elements using
- this namespace before calling this function in order to rename it.
-
- .. versionadded:: 1.3
- """
- if self.nsmap.get(prefix, None) == url:
- registerNS(prefix, url)
- return
-
- # Attempt to clean any existing namespaces
- if prefix in self.nsmap or url in self.nsmap.values():
- nskeep = [k for k, v in self.nsmap.items() if k != prefix and v != url]
- etree.cleanup_namespaces(self, keep_ns_prefixes=nskeep)
- if prefix in self.nsmap:
- raise KeyError("ns prefix already used with a different url")
- if url in self.nsmap.values():
- raise ValueError("ns url already used with a different prefix")
-
- # These are globals, but both will overwrite previous uses.
- registerNS(prefix, url)
- etree.register_namespace(prefix, url)
-
- # Set and unset an attribute to add the namespace to this root element.
- self.set(f"{prefix}:temp", "1")
- self.set(f"{prefix}:temp", None)
-
- def getElement(self, xpath): # pylint: disable=invalid-name
- """Gets a single element from the given xpath or returns None"""
- return self.findone(xpath)
-
- def getElementById(
- self, eid: str, elm="*", literal=False
- ): # pylint: disable=invalid-name
- """Get an element in this svg document by it's ID attribute.
-
- Args:
- eid (str): element id
- elm (str, optional): element type, including namespace, e.g. ``svg:path``.
- Defaults to "*".
- literal (bool, optional): If ``False``, ``#url()`` is stripped from ``eid``.
- Defaults to False.
-
- .. versionadded:: 1.1
-
- Returns:
- Union[BaseElement, None]: found element
- """
- if eid is not None and not literal:
- eid = eid.strip()[4:-1] if eid.startswith("url(") else eid
- eid = eid.lstrip("#")
- return self.getElement(f'//{elm}[@id="{eid}"]')
-
- def getElementByName(self, name, elm="*"): # pylint: disable=invalid-name
- """Get an element by it's inkscape:label (aka name)"""
- return self.getElement(f'//{elm}[@inkscape:label="{name}"]')
-
- def getElementsByClass(self, class_name): # pylint: disable=invalid-name
- """Get elements by it's class name"""
-
- return self.xpath(ConditionalRule(f".{class_name}").to_xpath())
-
- def getElementsByHref(
- self, eid: str, attribute="href"
- ): # pylint: disable=invalid-name
- """Get elements that reference the element with id eid.
-
- Args:
- eid (str): _description_
- attribute (str, optional): Attribute to look for.
- Valid choices: "href", "xlink:href", "mask", "clip-path".
- Defaults to "href".
-
- .. versionadded:: 1.2
-
- attribute set to "href" or "xlink:href" handles both cases.
- .. versionchanged:: 1.3
-
- Returns:
- Any: list of elements
- """
- if attribute == "href" or attribute == "xlink:href":
- return self.xpath(f'//*[@href|@xlink:href="#{eid}"]')
- elif attribute == "mask":
- return self.xpath(f'//*[@mask="url(#{eid})"]')
- elif attribute == "clip-path":
- return self.xpath(f'//*[@clip-path="url(#{eid})"]')
-
- def getElementsByStyleUrl(self, eid, style=None): # pylint: disable=invalid-name
- """Get elements by a style attribute url"""
- url = f"url(#{eid})"
- if style is not None:
- url = style + ":" + url
- return self.xpath(f'//*[contains(@style,"{url}")]')
-
- @property
- def name(self):
- """Returns the Document Name"""
- return self.get("sodipodi:docname", "")
-
- @property
- def namedview(self) -> NamedView:
- """Return the sp namedview meta information element"""
- return self.get_or_create("//sodipodi:namedview", prepend=True)
-
- @property
- def metadata(self):
- """Return the svg metadata meta element container"""
- return self.get_or_create("//svg:metadata", prepend=True)
-
- @property
- def defs(self):
- """Return the svg defs meta element container"""
- return self.get_or_create("//svg:defs", prepend=True)
-
- def get_viewbox(self) -> List[float]:
- """Parse and return the document's viewBox attribute"""
- return self.parse_viewbox(self.get("viewBox", "0")) or [0, 0, 0, 0]
-
- @property
- def viewbox_width(self) -> float: # getDocumentWidth(self):
- """Returns the width of the `user coordinate system
- `_ in user units, i.e.
- the width of the viewbox, as defined in the SVG file. If no viewbox is defined,
- the value of the width attribute is returned. If the height is not defined,
- returns 0.
-
- .. versionadded:: 1.2"""
- return self.get_viewbox()[2] or self.viewport_width
-
- @property
- def viewport_width(self) -> float:
- """Returns the width of the `viewport coordinate system
- `_ in user units, i.e. the
- width attribute of the svg element converted to px
-
- .. versionadded:: 1.2"""
- return self.to_dimensionless(self.get("width")) or self.get_viewbox()[2]
-
- @property
- def viewbox_height(self) -> float: # getDocumentHeight(self):
- """Returns the height of the `user coordinate system
- `_ in user units, i.e. the
- height of the viewbox, as defined in the SVG file. If no viewbox is defined, the
- value of the height attribute is returned. If the height is not defined,
- returns 0.
-
- .. versionadded:: 1.2"""
- return self.get_viewbox()[3] or self.viewport_height
-
- @property
- def viewport_height(self) -> float:
- """Returns the width of the `viewport coordinate system
- `_ in user units, i.e. the
- height attribute of the svg element converted to px
-
- .. versionadded:: 1.2"""
- return self.to_dimensionless(self.get("height")) or self.get_viewbox()[3]
-
- @property
- def scale(self):
- """Returns the ratio between the viewBox width and the page width.
-
- .. versionchanged:: 1.2
- Previously, the scale as shown by the document properties was computed,
- but the computation of this in core Inkscape changed in Inkscape 1.2, so
- this was moved to :attr:`inkscape_scale`."""
- return self._base_scale()
-
- @property
- def inkscape_scale(self):
- """Returns the ratio between the viewBox width (in width/height units) and the
- page width, which is displayed as "scale" in the Inkscape document
- properties.
-
- .. versionadded:: 1.2"""
-
- viewbox_unit = (
- parse_unit(self.get("width")) or parse_unit(self.get("height")) or (0, "px")
- )[1]
- return self._base_scale(viewbox_unit)
-
- def _base_scale(self, unit="px"):
- """Returns what Inkscape shows as "user units per `unit`"
-
- .. versionadded:: 1.2"""
- try:
- scale_x = (
- self.to_dimensional(self.viewport_width, unit) / self.viewbox_width
- )
- scale_y = (
- self.to_dimensional(self.viewport_height, unit) / self.viewbox_height
- )
- value = max([scale_x, scale_y])
- return 1.0 if value == 0 else value
- except (ValueError, ZeroDivisionError):
- return 1.0
-
- @property
- def equivalent_transform_scale(self) -> float:
- """Return the scale of the equivalent transform of the svg tag, as defined by
- https://www.w3.org/TR/SVG2/coords.html#ComputingAViewportsTransform
- (highly simplified)
-
- .. versionadded:: 1.2"""
- return self.scale
-
- @property
- def unit(self):
- """Returns the unit used for in the SVG document.
- In the case the SVG document lacks an attribute that explicitly
- defines what units are used for SVG coordinates, it tries to calculate
- the unit from the SVG width and viewBox attributes.
- Defaults to 'px' units."""
- if not hasattr(self, "_unit"):
- self._unit = "px" # Default is px
- viewbox = self.get_viewbox()
- if viewbox and set(viewbox) != {0}:
- self._unit = discover_unit(self.get("width"), viewbox[2], default="px")
- return self._unit
-
- @property
- def document_unit(self):
- """Returns the display unit (Inkscape-specific attribute) of the document
-
- .. versionadded:: 1.2"""
- return self.namedview.get("inkscape:document-units", "px")
-
- @property
- def stylesheets(self):
- """Get all the stylesheets, bound together to one, (for reading)"""
- sheets = StyleSheets(self)
- for node in self.xpath("//svg:style"):
- sheets.append(node.stylesheet())
- return sheets
-
- @property
- def stylesheet(self):
- """Return the first stylesheet or create one if needed (for writing)"""
- for sheet in self.stylesheets:
- return sheet
-
- style_node = StyleElement()
- self.defs.append(style_node)
- return style_node.stylesheet()
-
-
-def width(self):
- """Use :func:`viewport_width` instead"""
- return self.viewport_width
-
-
-def height(self):
- """Use :func:`viewport_height` instead"""
- return self.viewport_height
-
-
-SvgDocumentElement.width = property(deprecate(width, "1.2"))
-SvgDocumentElement.height = property(deprecate(height, "1.2"))
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_text.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_text.py
deleted file mode 100644
index 8fc1756..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_text.py
+++ /dev/null
@@ -1,202 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2020 Martin Owens
-# Thomas Holder
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# pylint: disable=arguments-differ
-"""
-Provide text based element classes interface.
-
-Because text is not rendered at all, no information about a text's path
-size or actual location can be generated yet.
-"""
-from __future__ import annotations
-
-from tempfile import TemporaryDirectory
-
-from ..interfaces.IElement import BaseElementProtocol
-from ..paths import Path
-from ..transforms import Transform, BoundingBox
-from ..command import inkscape, write_svg
-from ._base import BaseElement, ShapeElement
-from ._polygons import PathElementBase
-
-
-class TextBBMixin: # pylint: disable=too-few-public-methods
- """Mixin to query the bounding box from Inkscape
-
- .. versionadded:: 1.2"""
-
- def get_inkscape_bbox(self: BaseElementProtocol) -> BoundingBox:
- """Query the bbbox of a single object. This calls the Inkscape command,
- so it is rather slow to use in a loop."""
- with TemporaryDirectory(prefix="inkscape-command") as tmpdir:
- svg_file = write_svg(self.root, tmpdir, "input.svg")
- out = inkscape(svg_file, "-X", "-Y", "-W", "-H", query_id=self.get_id())
- out = list(map(self.root.viewport_to_unit, out.splitlines()))
- if len(out) != 4:
- raise ValueError("Error: Bounding box computation failed")
- return BoundingBox.new_xywh(*out)
-
-
-class FlowRegion(ShapeElement):
- """SVG Flow Region (SVG 2.0)"""
-
- tag_name = "flowRegion"
-
- def get_path(self):
- # This ignores flowRegionExcludes
- return sum([child.path for child in self], Path())
-
-
-class FlowRoot(ShapeElement, TextBBMixin):
- """SVG Flow Root (SVG 2.0)"""
-
- tag_name = "flowRoot"
-
- @property
- def region(self):
- """Return the first flowRegion in this flowRoot"""
- return self.findone("svg:flowRegion")
-
- def get_path(self):
- region = self.region
- return region.get_path() if region is not None else Path()
-
-
-class FlowPara(ShapeElement):
- """SVG Flow Paragraph (SVG 2.0)"""
-
- tag_name = "flowPara"
-
- def get_path(self):
- # XXX: These empty paths mean the bbox for text elements will be nothing.
- return Path()
-
-
-class FlowDiv(ShapeElement):
- """SVG Flow Div (SVG 2.0)"""
-
- tag_name = "flowDiv"
-
- def get_path(self):
- # XXX: These empty paths mean the bbox for text elements will be nothing.
- return Path()
-
-
-class FlowSpan(ShapeElement):
- """SVG Flow Span (SVG 2.0)"""
-
- tag_name = "flowSpan"
-
- def get_path(self):
- # XXX: These empty paths mean the bbox for text elements will be nothing.
- return Path()
-
-
-class TextElement(ShapeElement, TextBBMixin):
- """A Text element"""
-
- tag_name = "text"
- x = property(lambda self: self.to_dimensionless(self.get("x", 0)))
- y = property(lambda self: self.to_dimensionless(self.get("y", 0)))
-
- def get_path(self):
- return Path()
-
- def tspans(self):
- """Returns all children that are tspan elements"""
- return self.findall("svg:tspan")
-
- def get_text(self, sep="\n"):
- """Return the text content including tspans"""
- nodes = [self] + list(self.tspans())
- return sep.join([elem.text for elem in nodes if elem.text is not None])
-
- def shape_box(self, transform=None):
- """
- Returns a horrible bounding box that just contains the coord points
- of the text without width or height (which is impossible to calculate)
- """
- effective_transform = Transform(transform) @ self.transform
- x, y = effective_transform.apply_to_point((self.x, self.y))
- bbox = BoundingBox(x, y)
- for tspan in self.tspans():
- bbox += tspan.bounding_box(effective_transform)
- return bbox
-
-
-class TextPath(ShapeElement, TextBBMixin):
- """A textPath element"""
-
- tag_name = "textPath"
-
- def get_path(self):
- return Path()
-
-
-class Tspan(ShapeElement, TextBBMixin):
- """A tspan text element"""
-
- tag_name = "tspan"
- x = property(lambda self: self.to_dimensionless(self.get("x", 0)))
- y = property(lambda self: self.to_dimensionless(self.get("y", 0)))
-
- @classmethod
- def superscript(cls, text):
- """Adds a superscript tspan element"""
- return cls(text, style="font-size:65%;baseline-shift:super")
-
- def get_path(self):
- return Path()
-
- def shape_box(self, transform=None):
- """
- Returns a horrible bounding box that just contains the coord points
- of the text without width or height (which is impossible to calculate)
- """
- effective_transform = Transform(transform) @ self.transform
- x1, y1 = effective_transform.apply_to_point((self.x, self.y))
- fontsize = self.to_dimensionless(self.style.get("font-size", "12px"))
- x2 = self.x + 0 # XXX This is impossible to calculate!
- y2 = self.y + float(fontsize)
- x2, y2 = effective_transform.apply_to_point((x2, y2))
- return BoundingBox((x1, x2), (y1, y2))
-
-
-class SVGfont(BaseElement):
- """An svg font element"""
-
- tag_name = "font"
-
-
-class FontFace(BaseElement):
- """An svg font font-face element"""
-
- tag_name = "font-face"
-
-
-class Glyph(PathElementBase):
- """An svg font glyph element"""
-
- tag_name = "glyph"
-
-
-class MissingGlyph(BaseElement):
- """An svg font missing-glyph element"""
-
- tag_name = "missing-glyph"
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_use.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_use.py
deleted file mode 100644
index 52b7050..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_use.py
+++ /dev/null
@@ -1,89 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2020 Martin Owens
-# Thomas Holder
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-"""
-Interface for the Use and Symbol elements
-"""
-
-from ..transforms import Transform
-
-from ._groups import Group, GroupBase
-from ._base import ShapeElement
-
-
-class Symbol(GroupBase):
- """SVG symbol element"""
-
- tag_name = "symbol"
-
-
-class Use(ShapeElement):
- """A 'use' element that links to another in the document"""
-
- tag_name = "use"
-
- @classmethod
- def new(cls, elem, x, y, **attrs): # pylint: disable=arguments-differ
- ret = super().new(x=x, y=y, **attrs)
- ret.href = elem
- return ret
-
- def get_path(self):
- """Returns the path of the cloned href plus any transformation
-
- .. versionchanged:: 1.3
- include transform of the referenced element
- """
- path = self.href.path
- path = path.transform(self.href.transform)
- return path
-
- def effective_style(self):
- """Href's style plus this object's own styles"""
- style = self.href.effective_style()
- style.update(self.style)
- return style
-
- def unlink(self):
- """Unlink this clone, replacing it with a copy of the original"""
- copy = self.href.copy()
- if isinstance(copy, Symbol):
- group = Group(**copy.attrib)
- group.extend(copy)
- copy = group
- copy.transform = self.transform @ copy.transform
- copy.transform.add_translate(
- self.to_dimensionless(self.get("x", 0)),
- self.to_dimensionless(self.get("y", 0)),
- )
- copy.style = self.style + copy.style
- # Preserve the id of the clone to not break links that link the
- # As we replace exactly one element by exactly one, this should be safe.
- old_id = self.get_id()
- self.replace_with(copy)
- copy.set_random_ids()
- copy.set_id(old_id)
- return copy
-
- def shape_box(self, transform=None):
- """BoundingBox of the unclipped shape
-
- .. versionadded:: 1.1"""
- effective_transform = Transform(transform) @ self.transform
- return self.href.bounding_box(effective_transform)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_utils.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_utils.py
deleted file mode 100644
index bc72c6b..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/elements/_utils.py
+++ /dev/null
@@ -1,152 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) 2021 Martin Owens
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-"""
-Useful utilities specifically for elements (that aren't base classes)
-
-.. versionadded:: 1.1
- Most of the methods in this module were moved from inkex.utils.
-"""
-
-from collections import defaultdict
-import re
-
-# a dictionary of all of the xmlns prefixes in a standard inkscape doc
-NSS = {
- "sodipodi": "http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd",
- "cc": "http://creativecommons.org/ns#",
- "ccOLD": "http://web.resource.org/cc/",
- "svg": "http://www.w3.org/2000/svg",
- "dc": "http://purl.org/dc/elements/1.1/",
- "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
- "inkscape": "http://www.inkscape.org/namespaces/inkscape",
- "xlink": "http://www.w3.org/1999/xlink",
- "xml": "http://www.w3.org/XML/1998/namespace",
-}
-SSN = dict((b, a) for (a, b) in NSS.items())
-
-
-def registerNS(prefix, url):
- """Register the given prefix as a namespace url."""
- NSS[prefix] = url
- SSN[url] = prefix
-
-
-def addNS(tag, ns=None, namespaces=NSS): # pylint: disable=invalid-name
- """Add a known namespace to a name for use with lxml"""
- if tag.startswith("{") and ns:
- _, tag = removeNS(tag)
- if not tag.startswith("{"):
- tag = tag.replace("__", ":")
- if ":" in tag:
- (ns, tag) = tag.rsplit(":", 1)
- ns = namespaces.get(ns, None) or ns
- if ns is not None:
- return f"{{{ns}}}{tag}"
- return tag
-
-
-def removeNS(
- name, reverse_namespaces=SSN, default="svg"
-): # pylint: disable=invalid-name
- """The reverse of addNS, finds any namespace and returns tuple (ns, tag)"""
- if name[0] == "{":
- (url, tag) = name[1:].split("}", 1)
- return reverse_namespaces.get(url, default), tag
- if ":" in name:
- return name.rsplit(":", 1)
- return default, name
-
-
-def splitNS(name, namespaces=NSS): # pylint: disable=invalid-name
- """Like removeNS, but returns a url instead of a prefix"""
- (prefix, tag) = removeNS(name)
- return (namespaces[prefix], tag)
-
-
-def natural_sort_key(key, _nsre=re.compile("([0-9]+)")):
- """Helper for a natural sort, see
- https://stackoverflow.com/a/16090640/3298143"""
- return [int(text) if text.isdigit() else text.lower() for text in _nsre.split(key)]
-
-
-class ChildToProperty(property):
- """Use when you have a singleton child element who's text
- content is the canonical value for the property"""
-
- def __init__(self, tag, prepend=False):
- super().__init__()
- self.tag = tag
- self.prepend = prepend
-
- def __get__(self, obj, klass=None):
- elem = obj.findone(self.tag)
- return elem.text if elem is not None else None
-
- def __set__(self, obj, value):
- elem = obj.get_or_create(self.tag, prepend=self.prepend)
- elem.text = value
-
- def __delete__(self, obj):
- obj.remove_all(self.tag)
-
- @property
- def __doc__(self):
- return f"Get, set or delete the {self.tag} property."
-
-
-class CloningVat:
- """
- When modifying defs, sometimes we want to know if every backlink would have
- needed changing, or it was just some of them.
-
- This tracks the def elements, their promises and creates clones if needed.
- """
-
- def __init__(self, svg):
- self.svg = svg
- self.tracks = defaultdict(set)
- self.set_ids = defaultdict(list)
-
- def track(self, elem, parent, set_id=None, **kwargs):
- """Track the element and connected parent"""
- elem_id = elem.get("id")
- parent_id = parent.get("id")
- self.tracks[elem_id].add(parent_id)
- self.set_ids[elem_id].append((set_id, kwargs))
-
- def process(self, process, types=(), make_clones=True, **kwargs):
- """
- Process each tracked item if the backlinks match the parents
-
- Optionally make clones, process the clone and set the new id.
- """
- for elem_id in list(self.tracks):
- parents = self.tracks[elem_id]
- elem = self.svg.getElementById(elem_id)
- backlinks = {blk.get("id") for blk in elem.backlinks(*types)}
- if backlinks == parents:
- # No need to clone, we're processing on-behalf of all parents
- process(elem, **kwargs)
- elif make_clones:
- clone = elem.copy()
- elem.getparent().append(clone)
- clone.set_random_id()
- for update, upkw in self.set_ids.get(elem_id, ()):
- update(elem.get("id"), clone.get("id"), **upkw)
- process(clone, **kwargs)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/extensions.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/extensions.py
deleted file mode 100644
index 4c2752b..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/extensions.py
+++ /dev/null
@@ -1,534 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2018 Martin Owens
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-"""
-A helper module for creating Inkscape effect extensions
-
-This provides the basic generic types of extensions which most writers should
-use in their code. See below for the different types.
-"""
-
-import os
-import re
-import sys
-import types
-from abc import ABC
-
-from .utils import errormsg, Boolean
-from .colors import Color, ColorError
-from .elements import (
- load_svg,
- BaseElement,
- ShapeElement,
- Group,
- Layer,
- Grid,
- TextElement,
- FlowPara,
- FlowDiv,
- Pattern,
-)
-from .elements._utils import CloningVat
-from .base import (
- InkscapeExtension,
- SvgThroughMixin,
- SvgInputMixin,
- SvgOutputMixin,
- TempDirMixin,
-)
-from .transforms import Transform
-from .elements import LinearGradient, RadialGradient
-from .command import write_svg, inkscape, ProgramRunError
-from .utils import errormsg
-from .localization import inkex_gettext as _
-
-# All the names that get added to the inkex API itself.
-__all__ = (
- "EffectExtension",
- "GenerateExtension",
- "InputExtension",
- "OutputExtension",
- "RasterOutputExtension",
- "CallExtension",
- "TemplateExtension",
- "ColorExtension",
- "TextExtension",
-)
-
-stdout = sys.stdout
-
-
-class EffectExtension(SvgThroughMixin, InkscapeExtension, ABC):
- """
- Takes the SVG from Inkscape, modifies the selection or the document
- and returns an SVG to Inkscape.
- """
-
-
-class OutputExtension(SvgInputMixin, TempDirMixin, InkscapeExtension):
- """
- Takes the SVG from Inkscape and outputs it to something that's not an SVG.
-
- Used in functions for `Save As`
- """
-
- def effect(self):
- """Effect isn't needed for a lot of Output extensions"""
-
- def save(self, stream):
- """But save certainly is, we give a more exact message here"""
- raise NotImplementedError("Output extensions require a save(stream) method!")
-
- def preprocess(self, types_to_path=None, unlink_clones=True):
- """Preprocess the SVG into an export-friendly document by converting
- certain objects to path beforehand.
-
- Args:
- types_to_path (List[str], optional): List of element types to convert to
- path. Defaults to all text elements and all non-path shape elements.
- unlink_clones (bool, optional): If clones should be unlinked. Defaults to
- True.
-
- Returns:
- _type_: _description_
- """
- if types_to_path is None:
- types_to_path = [
- "flowRoot",
- "rect",
- "circle",
- "ellipse",
- "line",
- "polyline",
- "polygon",
- "text",
- ]
- actions = ["unlock-all"]
- if "flowRoot" in types_to_path:
- # Flow roots contain rectangles inside them, so they need to be
- # converted to paths separately from other shapes
- actions += [
- "select-by-element:flowRoot",
- "object-to-path",
- "select-clear",
- ]
- types_to_path.remove("flowRoot")
-
- # Now convert all non-paths to paths
- actions += ["select-by-element:" + i for i in types_to_path]
- actions += ["object-to-path", "select-clear"]
- # unlink clones
- if unlink_clones:
- actions += ["select-by-element:use", "object-unlink-clones"]
- # save and overwrite
- actions += ["export-overwrite", "export-do"]
-
- infile = os.path.join(self.tempdir, "input.svg")
- write_svg(self.document, infile)
- try:
- inkscape(infile, actions=";".join(actions))
- except ProgramRunError as err:
- errormsg(_("An error occurred during document preparation"))
- errormsg(err.stderr.decode("utf-8"))
-
- with open(infile, "r") as stream:
- self.document = load_svg(stream)
- self.svg = self.document.getroot()
-
-
-class RasterOutputExtension(InkscapeExtension):
- """
- Takes a PNG from Inkscape and outputs it to another raster format.
-
- .. versionadded:: 1.1
- """
-
- def __init__(self):
- super().__init__()
- self.img = None
-
- def load(self, stream):
- from PIL import Image
-
- # disable the PIL decompression bomb DOS attack check.
- Image.MAX_IMAGE_PIXELS = None
-
- self.img = Image.open(stream)
-
- def effect(self):
- """Not needed since image isn't being changed"""
-
- def save(self, stream):
- """Implement raster image saving here from PIL"""
- raise NotImplementedError("Raster Output extension requires a save method!")
-
-
-class InputExtension(SvgOutputMixin, InkscapeExtension):
- """
- Takes any type of file as input and outputs SVG which Inkscape can read.
-
- Used in functions for `Open`
- """
-
- def effect(self):
- """Effect isn't needed for a lot of Input extensions"""
-
- def load(self, stream):
- """But load certainly is, we give a more exact message here"""
- raise NotImplementedError("Input extensions require a load(stream) method!")
-
-
-class CallExtension(TempDirMixin, InputExtension):
- """Call an external program to get the output"""
-
- input_ext = "svg"
- output_ext = "svg"
-
- def load(self, stream):
- pass # Not called (load_raw instead)
-
- def load_raw(self):
- # Don't call InputExtension.load_raw
- TempDirMixin.load_raw(self)
- input_file = self.options.input_file
-
- if not isinstance(input_file, str):
- data = input_file.read()
- input_file = os.path.join(self.tempdir, "input." + self.input_ext)
- with open(input_file, "wb") as fhl:
- fhl.write(data)
-
- output_file = os.path.join(self.tempdir, "output." + self.output_ext)
- document = self.call(input_file, output_file) or output_file
- if isinstance(document, str):
- if not os.path.isfile(document):
- raise IOError(f"Can't find generated document: {document}")
-
- if self.output_ext == "svg":
- with open(document, "r", encoding="utf-8") as fhl:
- document = fhl.read()
- if "<" in document:
- document = load_svg(document.encode("utf-8"))
- else:
- with open(document, "rb") as fhl:
- document = fhl.read()
-
- self.document = document
-
- def call(self, input_file, output_file):
- """Call whatever programs are needed to get the desired result."""
- raise NotImplementedError("Call extensions require a call(in, out) method!")
-
-
-class GenerateExtension(EffectExtension):
- """
- Does not need any SVG, but instead just outputs an SVG fragment which is
- inserted into Inkscape, centered on the selection.
- """
-
- container_label = ""
- container_layer = False
-
- def generate(self):
- """
- Return an SVG fragment to be inserted into the selected layer of the document
- OR yield multiple elements which will be grouped into a container group
- element which will be given an automatic label and transformation.
- """
- raise NotImplementedError("Generate extensions must provide generate()")
-
- def container_transform(self):
- """
- Generate the transformation for the container group, the default is
- to return the center position of the svg document or view port.
- """
- (pos_x, pos_y) = self.svg.namedview.center
- if pos_x is None:
- pos_x = 0
- if pos_y is None:
- pos_y = 0
- return Transform(translate=(pos_x, pos_y))
-
- def create_container(self):
- """
- Return the container the generated elements will go into.
-
- Default is a new layer or current layer depending on the :attr:`container_layer`
- flag.
-
- .. versionadded:: 1.1
- """
- container = (Layer if self.container_layer else Group).new(self.container_label)
- if self.container_layer:
- self.svg.append(container)
- else:
- container.transform = self.container_transform()
- parent = self.svg.get_current_layer()
- try:
- parent_transform = parent.composed_transform()
- except AttributeError:
- pass
- else:
- container.transform = -parent_transform @ container.transform
- parent.append(container)
- return container
-
- def effect(self):
- layer = self.svg.get_current_layer()
- fragment = self.generate()
- if isinstance(fragment, types.GeneratorType):
- container = self.create_container()
- for child in fragment:
- if isinstance(child, BaseElement):
- container.append(child)
- elif isinstance(fragment, BaseElement):
- layer.append(fragment)
- else:
- errormsg("Nothing was generated\n")
-
-
-class TemplateExtension(EffectExtension):
- """
- Provide a standard way of creating templates.
- """
-
- size_rex = re.compile(r"([\d.]*)(\w\w)?x([\d.]*)(\w\w)?")
- template_id = "SVGRoot"
-
- def __init__(self):
- self.svg = None
- super().__init__()
- # Arguments added on after add_arguments so it can be overloaded cleanly.
- self.arg_parser.add_argument("--size", type=self.arg_size(), dest="size")
- self.arg_parser.add_argument("--width", type=int, default=800)
- self.arg_parser.add_argument("--height", type=int, default=600)
- self.arg_parser.add_argument("--orientation", default=None)
- self.arg_parser.add_argument("--unit", default="px")
- self.arg_parser.add_argument("--grid", type=Boolean)
- # self.svg = None
-
- def get_template(self, **kwargs):
- """Can be over-ridden with custom svg loading here"""
- return self.document
-
- def arg_size(self, unit="px"):
- """Argument is a string of the form X[unit]xY[unit], default units apply
- when missing"""
-
- def _inner(value):
- try:
- value = float(value)
- return (value, unit, value, unit)
- except ValueError:
- pass
- match = self.size_rex.match(str(value))
- if match is not None:
- size = match.groups()
- return (
- float(size[0]),
- size[1] or unit,
- float(size[2]),
- size[3] or unit,
- )
- return None
-
- return _inner
-
- def get_size(self):
- """Get the size of the new template (defaults to size options)"""
- size = self.options.size
- if self.options.size is None:
- size = (
- self.options.width,
- self.options.unit,
- self.options.height,
- self.options.unit,
- )
- if (
- self.options.orientation == "horizontal"
- and size[0] < size[2]
- or self.options.orientation == "vertical"
- and size[0] > size[2]
- ):
- size = size[2:4] + size[0:2]
- return size
-
- def effect(self):
- """Creates a template, do not over-ride"""
- (width, width_unit, height, height_unit) = self.get_size()
- width_px = int(self.svg.uutounit(width, "px"))
- height_px = int(self.svg.uutounit(height, "px"))
-
- self.document = self.get_template()
- self.svg = self.document.getroot()
- self.svg.set("id", self.template_id)
- self.svg.set("width", str(width) + width_unit)
- self.svg.set("height", str(height) + height_unit)
- self.svg.set("viewBox", f"0 0 {width} {height}")
- self.set_namedview(width_px, height_px, width_unit)
-
- def set_namedview(self, width, height, unit):
- """Setup the document namedview"""
- self.svg.namedview.set("inkscape:document-units", unit)
- self.svg.namedview.set("inkscape:zoom", "0.25")
- self.svg.namedview.set("inkscape:cx", str(width / 2.0))
- self.svg.namedview.set("inkscape:cy", str(height / 2.0))
- if self.options.grid:
- self.svg.namedview.set("showgrid", "true")
- self.svg.namedview.add(Grid(type="xygrid"))
-
-
-class ColorExtension(EffectExtension):
- """
- A standard way to modify colours in an svg document.
- """
-
- process_none = False # should we call modify_color for the "none" color.
- select_all = (ShapeElement,)
- pass_rgba = False
- """
- If true, color and opacity are processed together (as RGBA color)
- by :func:`modify_color`.
-
- If false (default), they are processed independently by `modify_color` and
- `modify_opacity`.
-
- .. versionadded:: 1.2
- """
-
- def __init__(self):
- super().__init__()
- self._renamed = {}
-
- def effect(self):
- # Limiting to shapes ignores Gradients (and other things) from the select_all
- # this prevents defs from being processed twice.
- self._renamed = {}
- gradients = CloningVat(self.svg)
- for elem in self.svg.selection.get(ShapeElement):
- self.process_element(elem, gradients)
- gradients.process(self.process_elements, types=(ShapeElement,))
-
- def process_elements(self, elem):
- """Process multiple elements (gradients)"""
- for child in elem.descendants():
- self.process_element(child)
-
- def process_element(self, elem, gradients=None):
- """Process one of the selected elements"""
- style = elem.specified_style()
- # Colours first
- for name in (
- elem.style.associated_props if self.pass_rgba else elem.style.color_props
- ):
- if name not in style:
- continue # we don't want to process default values
- try:
- value = style(name)
- except ColorError:
- continue # bad color value, don't touch.
- if isinstance(value, Color):
- col = Color(value)
- if self.pass_rgba:
- col = col.to_rgba(
- alpha=elem.style(elem.style.associated_props[name])
- )
- rgba_result = self._modify_color(name, col)
- elem.style.set_color(rgba_result, name)
-
- if isinstance(value, (LinearGradient, RadialGradient, Pattern)):
- gradients.track(value, elem, self._ref_cloned, style=style, name=name)
- if value.href is not None:
- gradients.track(value.href, elem, self._xlink_cloned, linker=value)
- # Then opacities (usually does nothing)
- if self.pass_rgba:
- return
- for name in elem.style.opacity_props:
- value = style(name)
- result = self.modify_opacity(name, value)
- if result not in (value, 1): # only modify if not equal to old or default
- elem.style[name] = result
-
- def _ref_cloned(self, old_id, new_id, style, name):
- self._renamed[old_id] = new_id
- style[name] = f"url(#{new_id})"
-
- def _xlink_cloned(self, old_id, new_id, linker): # pylint: disable=unused-argument
- lid = linker.get("id")
- linker = self.svg.getElementById(self._renamed.get(lid, lid))
- linker.href = new_id
-
- def _modify_color(self, name, color):
- """Pre-process color value to filter out bad colors"""
- if color or self.process_none:
- return self.modify_color(name, color)
- return color
-
- def modify_color(self, name, color):
- """Replace this method with your colour modifier method"""
- raise NotImplementedError("Provide a modify_color method.")
-
- def modify_opacity(
- self, name, opacity
- ): # pylint: disable=no-self-use, unused-argument
- """Optional opacity modification"""
- return opacity
-
-
-class TextExtension(EffectExtension):
- """
- A base effect for changing text in a document.
- """
-
- newline = True
- newpar = True
-
- def effect(self):
- nodes = self.svg.selection or {None: self.document.getroot()}
- for elem in nodes.values():
- self.process_element(elem)
-
- def process_element(self, node):
- """Reverse the node text"""
- if node.get("sodipodi:role") == "line":
- self.newline = True
- elif isinstance(node, (TextElement, FlowPara, FlowDiv)):
- self.newline = True
- self.newpar = True
-
- if node.text is not None:
- node.text = self.process_chardata(node.text)
- self.newline = False
- self.newpar = False
-
- for child in node:
- self.process_element(child)
-
- if node.tail is not None:
- node.tail = self.process_chardata(node.tail)
-
- def process_chardata(self, text):
- """Replaceable chardata method for processing the text"""
- return "".join(map(self.map_char, text))
-
- @staticmethod
- def map_char(char):
- """Replaceable map_char method for processing each letter"""
- raise NotImplementedError(
- "Please provide a process_chardata or map_char static method."
- )
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/gui/README.md b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/gui/README.md
deleted file mode 100644
index fede673..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/gui/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# What is inkex.gui
-
-This module is a Gtk based GUI creator. It helps extensions launch their own user interfaces and can help make sure those interfaces will work on all platforms that Inkscape ships with.
-
-# How do I use it
-
-You can create custom user interfaces by using the [Gnome Glade builder program](https://gitlab.gnome.org/GNOME/glade). Once you have a layout of all the widgets you want, you then make a GtkApp and Window classes inside your Python program, when the GtkApp is run, the windows will be shown to the user and all signals specified for the widgets will call functions on your window class.
-
-Please see the existing code for examples of how to do this.
-
-# This is a fork
-
-This code was originally part of the package `gtkme` which contained some parts we didn't want to ship—such as Ubuntu indicators and internet pixmaps. To avoid conflicts, our stripped down version of the `gtkme` module is renamed and placed inside of Inkscape's `inkex` module.
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/gui/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/gui/__init__.py
deleted file mode 100644
index 0f9ac69..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/gui/__init__.py
+++ /dev/null
@@ -1,58 +0,0 @@
-#
-# Copyright 2011-2022 Martin Owens
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see
-#
-# pylint: disable=wrong-import-position
-"""
-This is a wrapper layer to make interacting with Gtk a little less painful.
-The main issues with Gtk is that it expects an awful lot of the developer,
-code which is repeated over and over and patterns which every single developer
-will use are not given easy to use convenience functions.
-
-This makes Gtk programming WET, unattractive and error prone. This module steps
-inbetween and adds in all those missing bits. It's not meant to replace Gtk and
-certainly it's possible to use Gtk and threading directly.
-
-.. versionadded:: 1.2
-"""
-
-import os
-import sys
-import logging
-import threading
-
-from ..utils import DependencyError
-
-try:
- import gi
-
- gi.require_version("Gtk", "3.0")
-
- # Importing while covering stderr because pygobject has broken
- # warnings support and will force import warnings on our users.
- tmp, sys.stderr = sys.stderr, None # type: ignore
- from gi.repository import Gtk, GLib
-
- sys.stderr = tmp # type: ignore
-except ImportError: # pragma: no cover
- raise DependencyError(
- "You are missing the required libraries for Gtk."
- " Please report this problem to the Inkscape developers."
- )
-
-from .app import GtkApp
-from .window import Window, ChildWindow, FakeWidget
-from .listview import TreeView, IconView, ViewColumn, ViewSort, Separator
-from .pixmap import PixmapManager
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/gui/app.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/gui/app.py
deleted file mode 100644
index ba6fbf1..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/gui/app.py
+++ /dev/null
@@ -1,176 +0,0 @@
-# coding=utf-8
-#
-# Copyright 2011-2022 Martin Owens
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see
-#
-"""
-Gtk Application base classes, providing a way to load a GtkBuilder
-with a specific glade/ui file containing windows, and building
-a usable pythonic interface from them.
-"""
-import os
-import signal
-import logging
-
-from gi.repository import Gtk, GLib
-
-
-class GtkApp:
- """
- This wraps gtk builder and allows for some extra functionality with
- windows, especially the management of gtk main loops.
-
- Args:
- start_loop (bool, optional): If set to true will start a new gtk main loop.
- Defaults to False.
- start_gui (bool, optional): Used as local propertes if unset and passed to
- primary window when loaded. Defaults to True.
- """
-
- @property
- def prefix(self):
- """Folder prefix added to ui_dir"""
- return self.kwargs.get("prefix", "")
-
- @property
- def windows(self):
- """Returns a list of windows for this app"""
- return self.kwargs.get("windows", [])
-
- @property
- def ui_dir(self):
- """This is often the local directory"""
- return self.kwargs.get("ui_dir", "./")
-
- @property
- def ui_file(self):
- """If a single file is used for multiple windows"""
- return self.kwargs.get("ui_file", None)
-
- @property
- def app_name(self):
- """Set this variable in your class"""
- try:
- return self.kwargs["app_name"]
- except KeyError:
- raise NotImplementedError(
- "App name is not set, pass in or set 'app_name' in class."
- )
-
- @property
- def window(self):
- """Return the primary window"""
- return self._primary
-
- def __init__(self, start_loop=False, start_gui=True, **kwargs):
- """Creates a new GtkApp."""
- self.kwargs = kwargs
- self._loaded = {}
- self._initial = {}
- self._primary = None
-
- self.main_loop = GLib.main_depth()
-
- # Start with creating all the defined windows.
- if start_gui:
- self.init_gui()
- # Start up a gtk main loop when requested
- if start_loop:
- self.run()
-
- def run(self):
- """Run the gtk mainloop with ctrl+C and keyboard interrupt additions"""
- if not Gtk.init_check()[0]: # pragma: no cover
- raise RuntimeError(
- "Gtk failed to start." " Make sure $DISPLAY variable is set.\n"
- )
- try:
- # Add a signal to force quit on Ctrl+C (just like the old days)
- signal.signal(signal.SIGINT, signal.SIG_DFL)
- Gtk.main()
- except KeyboardInterrupt: # pragma: no cover
- logging.info("User Interrupted")
- logging.debug("Exiting %s", self.app_name)
-
- def get_ui_file(self, window):
- """Load any given gtk builder file from a standard location."""
- paths = [
- os.path.join(self.ui_dir, self.prefix, f"{window}.ui"),
- os.path.join(self.ui_dir, self.prefix, f"{self.ui_file}.ui"),
- ]
- for path in paths:
- if os.path.isfile(path):
- return path
- raise FileNotFoundError(f"Gtk Builder file is missing: {paths}")
-
- def init_gui(self):
- """Initialise all of our windows and load their signals"""
- if self.windows:
- for cls in self.windows:
- window = cls
- logging.debug("Adding window %s to GtkApp", window.name)
- self._initial[window.name] = window
- for window in self._initial.values():
- if window.primary:
- if not self._primary:
- self._primary = self.load_window(window.name)
- if not self.windows or not self._primary:
- raise KeyError(f"No primary window found for '{self.app_name}' app.")
-
- def load_window(self, name, *args, **kwargs):
- """Load a specific window from our group of windows"""
- window = self.proto_window(name)
- window.init(*args, **kwargs)
- return window
-
- def load_window_extract(self, name, **kwargs):
- """Load a child window as a widget container"""
- window = self.proto_window(name)
- window.load_widgets(**kwargs)
- return window.extract()
-
- def proto_window(self, name):
- """
- Loads a Glade window as a window without initialisation, used for
- extracting widgets from windows without loading them as windows.
- """
- logging.debug("Loading '%s' from %s", name, self._initial)
- if name in self._initial:
- # Create a new instance of this window
- window = self._initial[name](self)
- # Save the window object linked against the gtk window instance
- self._loaded[window.wid] = window
- return window
- raise KeyError(f"Can't load window '{name}', class not found.")
-
- def remove_window(self, window):
- """Remove the window from the list and exit if none remain"""
- if window.wid in self._loaded:
- self._loaded.pop(window.wid)
- else:
- logging.warning("Missing window '%s' on exit.", window.name)
- logging.debug("Loaded windows: %s", self._loaded)
- if not self._loaded:
- self.exit()
-
- def exit(self):
- """Exit our gtk application and kill gtk main if we have to"""
- if self.main_loop < GLib.main_depth():
- # Quit Gtk loop if we started one.
- tag = self._primary.name if self._primary else "program"
- logging.debug("Quit '%s' Main Loop.", tag)
- Gtk.main_quit()
- # You have to return in order for the loop to exit
- return 0
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/gui/asyncme.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/gui/asyncme.py
deleted file mode 100644
index d12ec5d..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/gui/asyncme.py
+++ /dev/null
@@ -1,329 +0,0 @@
-#
-# Copyright 2015 Ian Denhardt
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see
-#
-"""Convenience library for concurrency
-
-GUI apps frequently need concurrency, for example to avoid blocking UI while
-doing some long running computation. This module provides helpers for doing
-this kind of thing.
-
-The functions/methods here which spawn callables asynchronously
-don't supply a direct way to provide arguments. Instead, the user is
-expected to use a lambda, e.g::
-
- holding(lck, lambda: do_stuff(1,2,3, x='hello'))
-
-This is because the calling function may have additional arguments which
-could obscure the user's ability to pass arguments expected by the called
-function. For example, in the call::
-
- holding(lck, lambda: run_task(blocking=True), blocking=False)
-
-the blocking argument to holding might otherwise conflict with the
-blocking argument to run_task.
-"""
-import time
-import threading
-from datetime import datetime, timedelta
-
-from functools import wraps
-from typing import Any, Tuple
-from gi.repository import Gdk, GLib
-
-
-class Future:
- """A deferred result
-
- A `Future` is a result-to-be; it can be used to deliver a result
- asynchronously. Typical usage:
-
- >>> def background_task(task):
- ... ret = Future()
- ... def _task(x):
- ... return x - 4 + 2
- ... thread = threading.Thread(target=lambda: ret.run(lambda: _task(7)))
- ... thread.start()
- ... return ret
- >>> # Do other stuff
- >>> print(ret.wait())
- 5
-
- :func:`run` will also propagate exceptions; see its docstring for details.
- """
-
- def __init__(self):
- self._lock = threading.Lock()
- self._value = None
- self._exception = None
- self._lock.acquire()
-
- def is_ready(self):
- """Return whether the result is ready"""
- result = self._lock.acquire(False)
- if result:
- self._lock.release()
- return result
-
- def wait(self):
- """Wait for the result.
-
- `wait` blocks until the result is ready (either :func:`result` or
- :func:`exception` has been called), and then returns it (in the case
- of :func:`result`), or raises it (in the case of :func:`exception`).
- """
- with self._lock:
- if self._exception is None:
- return self._value
- else:
- raise self._exception # pylint: disable=raising-bad-type
-
- def result(self, value):
- """Supply the result as a return value.
-
- ``value`` is the result to supply; it will be returned when
- :func:`wait` is called.
- """
- self._value = value
- self._lock.release()
-
- def exception(self, err):
- """Supply an exception as the result.
-
- Args:
- err (Exception): an exception, which will be raised when :func:`wait`
- is called.
- """
- self._exception = err
- self._lock.release()
-
- def run(self, task):
- """Calls task(), and supplies the result.
-
- If ``task`` raises an exception, pass it to :func:`exception`.
- Otherwise, pass the return value to :func:`result`.
- """
- try:
- self.result(task())
- except Exception as err: # pylint: disable=broad-except
- self.exception(err)
-
-
-class DebouncedSyncVar:
- """A synchronized variable, which debounces its value
-
- :class:`DebouncedSyncVar` supports three operations: put, replace, and get.
- get will only retrieve a value once it has "settled," i.e. at least
- a certain amount of time has passed since the last time the value
- was modified.
- """
-
- def __init__(self, delay_seconds=0):
- """Create a new dsv with the supplied delay, and no initial value."""
- self._cv = threading.Condition()
- self._delay = timedelta(seconds=delay_seconds)
-
- self._deadline = None
- self._value = None
-
- self._have_value = False
-
- def set_delay(self, delay_seconds):
- """Set the delay in seconds of the debounce."""
- with self._cv:
- self._delay = timedelta(seconds=delay_seconds)
-
- def get(self, blocking=True, remove=True) -> Tuple[Any, bool]:
- """Retrieve a value.
-
- Args:
- blocking (bool, optional): if True, block until (1) the dsv has a value
- and (2) the value has been unchanged for an amount of time greater
- than or equal to the dsv's delay. Otherwise, if these conditions
- are not met, return ``(None, False)`` immediately. Defaults to True.
- remove (bool, optional): if True, remove the value when returning it.
- Otherwise, leave it where it is.. Defaults to True.
-
- Returns:
- Tuple[Any, bool]: Tuple (value, ok). ``value`` is the value of the variable
- (if successful, see above), and ok indicates whether or not a value was
- successfully retrieved.
- """
- while True:
- with self._cv:
- # If there's no value, either wait for one or return
- # failure.
- while not self._have_value:
- if blocking:
- self._cv.wait()
- else:
- return None, False # pragma: no cover
-
- now = datetime.now()
- deadline = self._deadline
- value = self._value
- if deadline <= now:
- # Okay, we're good. Remove the value if necessary, and
- # return it.
- if remove:
- self._have_value = False
- self._value = None
- self._cv.notify()
- return value, True
-
- # Deadline hasn't passed yet. Either wait or return failure.
- if blocking:
- time.sleep((deadline - now).total_seconds())
- else:
- return None, False # pragma: no cover
-
- def replace(self, value):
- """Replace the current value of the dsv (if any) with ``value``.
-
- replace never blocks (except briefly to acquire the lock). It does not
- wait for any unit of time to pass (though it does reset the timer on
- completion), nor does it wait for the dsv's value to appear or
- disappear.
- """
- with self._cv:
- self._replace(value)
-
- def put(self, value):
- """Set the dsv's value to ``value``.
-
- If the dsv already has a value, this blocks until the value is removed.
- Upon completion, this resets the timer.
- """
- with self._cv:
- while self._have_value:
- self._cv.wait()
- self._replace(value)
-
- def _replace(self, value):
- self._have_value = True
- self._value = value
- self._deadline = datetime.now() + self._delay
- self._cv.notify()
-
-
-def spawn_thread(func):
- """Call ``func()`` in a separate thread
-
- Returns the corresponding :class:`threading.Thread` object.
- """
- thread = threading.Thread(target=func)
- thread.start()
- return thread
-
-
-def in_mainloop(func):
- """Run f() in the gtk main loop
-
- Returns a :class:`Future` object which can be used to retrieve the return
- value of the function call.
-
- :func:`in_mainloop` exists because Gtk isn't threadsafe, and therefore cannot be
- manipulated except in the thread running the Gtk main loop. :func:`in_mainloop`
- can be used by other threads to manipulate Gtk safely.
- """
- future = Future()
-
- def handler(*_args, **_kwargs):
- """Function to be called in the future"""
- future.run(func)
-
- Gdk.threads_add_idle(0, handler, None)
- return future
-
-
-def mainloop_only(f):
- """A decorator which forces a function to only be run in Gtk's main loop.
-
- Invoking a decorated function as ``f(*args, **kwargs)`` is equivalent to
- using the undecorated function (from a thread other than the one running
- the Gtk main loop) as::
-
- in_mainloop(lambda: f(*args, **kwargs)).wait()
-
- :func:`mainloop_only` should be used to decorate functions which are unsafe
- to run outside of the Gtk main loop.
- """
-
- @wraps(f)
- def wrapper(*args, **kwargs):
- if GLib.main_depth():
- # Already in a mainloop, so just run it.
- return f(*args, **kwargs)
- return in_mainloop(lambda: f(*args, **kwargs)).wait()
-
- return wrapper
-
-
-def holding(lock, task, blocking=True):
- """Run task() while holding ``lock``.
-
- Args:
- blocking (bool, optional): if True, wait for the lock before running.
- Otherwise, if the lock is busy, return None immediately, and don't
- spawn `task`. Defaults to True.
-
- Returns:
- Union[Future, None]: The return value is a future which can be used to retrieve
- the result of running task (or None if the task was not run).
- """
- if not lock.acquire(False):
- return None
- ret = Future()
-
- def _target():
- ret.run(task)
- if ret._exception: # pragma: no cover
- ret.wait()
- lock.release()
-
- threading.Thread(target=_target).start()
- return ret
-
-
-def run_or_wait(func):
- """A decorator which runs the function using :func:`holding`
-
- This function creates a single lock for this function and
- waits for the lock to release before returning.
-
- See :func:`holding` above, with ``blocking=True``
- """
- lock = threading.Lock()
-
- def _inner(*args, **kwargs):
- return holding(lock, lambda: func(*args, **kwargs), blocking=True)
-
- return _inner
-
-
-def run_or_none(func):
- """A decorator which runs the function using :func:`holding`
-
- This function creates a single lock for this function and
- returns None if the process is already running (locked)
-
- See :func:`holding` above with ``blocking=True``
- """
- lock = threading.Lock()
-
- def _inner(*args, **kwargs):
- return holding(lock, lambda: func(*args, **kwargs), blocking=False)
-
- return _inner
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/gui/listview.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/gui/listview.py
deleted file mode 100644
index 56939e7..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/gui/listview.py
+++ /dev/null
@@ -1,562 +0,0 @@
-#
-# Copyright 2011-2022 Martin Owens
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see
-#
-"""
-Wraps the gtk treeview and iconview in something a little nicer.
-"""
-
-import logging
-
-from typing import Tuple, Type, Optional
-from gi.repository import Gtk, Gdk, GObject, GdkPixbuf, Pango
-
-from .pixmap import PixmapManager, SizeFilter
-
-GOBJ = GObject.TYPE_PYOBJECT
-
-
-def default(item, attr, d=None):
- """Python logic to choose an attribute, call it if required and return"""
- if hasattr(item, attr):
- prop = getattr(item, attr)
- if callable(prop):
- prop = prop()
- return prop
- return d
-
-
-def cmp(a, b):
- """Compare two objects"""
- return (a > b) - (a < b)
-
-
-def item_property(name, d=None):
- def inside(item):
- return default(item, name, d)
-
- return inside
-
-
-def label(obj):
- if isinstance(obj, tuple):
- return " or ".join([label(o) for o in obj])
- if not isinstance(obj, type):
- obj = type(obj)
- return obj.__name__
-
-
-class BaseView:
- """Controls for tree and icon views, a base class"""
-
- widget_type: Optional[Type[Gtk.Widget]] = None
-
- def __init__(self, widget, liststore=None, **kwargs):
- if not isinstance(widget, self.widget_type):
- lbl1 = label(self.widget_type)
- lbl2 = label(widget)
- raise TypeError(f"Wrong widget type: Expected {lbl1} got {lbl2}")
-
- self.selected_signal = kwargs.get("selected", None)
- self._iids = []
- self._list = widget
- self.args = kwargs
- self.selected = None
- self._data = None
- self.no_dupes = True
- self._model = self.create_model(liststore or widget.get_model())
- self._list.set_model(self._model)
- self.setup()
-
- self._list.connect(self.changed_signal, self.item_selected_signal)
-
- def get_model(self):
- """Returns the current data store model"""
- return self._model
-
- def create_model(self, liststore):
- """Setup the model and list"""
- if not isinstance(liststore, (Gtk.ListStore, Gtk.TreeStore)):
- lbl = label(liststore)
- raise TypeError(f"Expected List or TreeStore, got {lbl}")
- return liststore
-
- def refresh(self):
- """Attempt to refresh the listview"""
- self._list.queue_draw()
-
- def setup(self):
- """Setup columns, views, sorting etc"""
- pass
-
- def get_item_id(self, item):
- """
- Return an id set against this item.
-
- If item.get_id() is set then duplicates will be ignored.
- """
- if hasattr(item, "get_id"):
- return item.get_id()
- return None
-
- def replace(self, new_item, item_iter=None):
- """Replace all items, or a single item with object"""
- if item_iter:
- self.remove_item(item_iter)
- self.add_item(new_item)
- else:
- self.clear()
- self._data = new_item
- self.add_item(new_item)
-
- def item_selected(self, item=None, *others):
- """Base method result, called as an item is selected"""
- if self.selected != item:
- self.selected = item
- if self.selected_signal and item:
- self.selected_signal(item)
-
- def remove_item(self, item=None):
- """Remove an item from this view"""
- return self._model.remove(self.get_iter(item))
-
- def check_item_id(self, item):
- """Item id is recorded to guard against duplicates"""
- iid = self.get_item_id(item)
- if iid in self._iids and self.no_dupes:
- raise ValueError(f"Will not add duplicate row {iid}")
- if iid:
- self._iids.append(iid)
-
- def __iter__(self):
- ret = []
-
- def collect_all(store, treepath, treeiter):
- ret.append((self.get_item(treeiter), treepath, treeiter))
-
- self._model.foreach(collect_all)
- return ret.__iter__()
-
- def set_sensitive(self, sen=True):
- """Proxy the GTK property for sensitivity"""
- self._list.set_sensitive(sen)
-
- def clear(self):
- """Clear all items from this treeview"""
- self._iids = []
- self._model.clear()
-
- def item_double_clicked(self, *items):
- """What happens when you double click an item"""
- return items # Nothing
-
- def get_item(self, item_iter):
- """Return the object of attention from an iter"""
- return self._model[self.get_iter(item_iter)][0]
-
- def get_iter(self, item, path=False):
- """Return the iter given the item"""
- if isinstance(item, Gtk.TreePath):
- return item if path else self._model.get_iter(item)
- if isinstance(item, Gtk.TreeIter):
- return self._model.get_path(item) if path else item
- for src_item, src_path, src_iter in self:
- if item == src_item:
- return src_path if path else src_iter
- return None
-
-
-class TreeView(BaseView):
- """Controls and operates a tree view."""
-
- column_size = 16
- widget_type = Gtk.TreeView
- changed_signal = "cursor_changed"
-
- def setup(self):
- """Setup the treeview"""
- self._sel = self._list.get_selection()
- self._sel.set_mode(Gtk.SelectionMode.MULTIPLE)
- self._list.connect("button-press-event", self.item_selected_signal)
- # Separators should do something
- self._list.set_row_separator_func(TreeView.is_separator, None)
- super().setup()
-
- @staticmethod
- def is_separator(model, item_iter, data):
- """Internal function for seperator checking"""
- return isinstance(model.get_value(item_iter, 0), Separator)
-
- def get_selected_items(self):
- """Return a list of selected item objects"""
- return [self.get_item(row) for row in self._sel.get_selected_rows()[1]]
-
- def set_selected_items(self, *items):
- """Select the given items"""
- self._sel.unselect_all()
- for item in items:
- path_item = self.get_iter(item, path=True)
- if path_item is not None:
- self._sel.select_path(path_item)
-
- def is_selected(self, item):
- """Return true if the item is selected"""
- return self._sel.iter_is_selected(self.get_iter(item))
-
- def add(self, target, parent=None):
- """Add all items from the target to the treeview"""
- for item in target:
- self.add_item(item, parent=parent)
-
- def add_item(self, item, parent=None):
- """Add a single item image to the control, returns the TreePath"""
- if item is not None:
- self.check_item_id(item)
- return self._add_item([item], self.get_iter(parent))
- raise ValueError("Item can not be None.")
-
- def _add_item(self, item, parent):
- return self.get_iter(self._model.append(parent, item), path=True)
-
- def item_selected_signal(self, *args, **kwargs):
- """Signal for selecting an item"""
- return self.item_selected(*self.get_selected_items())
-
- def item_button_clicked(self, _, event):
- """Signal for mouse button click"""
- if event is None or event.type == Gdk.EventType._2BUTTON_PRESS:
- self.item_double_clicked(*self.get_selected_items())
-
- def expand_item(self, item, expand=True):
- """Expand one of our nodes"""
- self._list.expand_row(self.get_iter(item, path=True), expand)
-
- def create_model(self, liststore=None):
- """Set up an icon view for showing gallery images"""
- if liststore is None:
- liststore = Gtk.TreeStore(GOBJ)
- return super().create_model(liststore)
-
- def create_column(self, name, expand=True):
- """
- Create and pack a new column to this list.
-
- name - Label in the column header
- expand - Should the column expand
- """
- return ViewColumn(self._list, name, expand=expand)
-
- def create_sort(self, *args, **kwargs):
- """
- Create and attach a sorting view to this list.
-
- see ViewSort arguments for details.
- """
- return ViewSort(self._list, *args, **kwargs)
-
-
-class ComboBox(TreeView):
- """Controls and operates a combo box list."""
-
- widget_type = Gtk.ComboBox
- changed_signal = "changed"
-
- def setup(self):
- pass
-
- def get_selected_item(self):
- """Return the selected item of this combo box"""
- return self.get_item(self._list.get_active_iter())
-
- def set_selected_item(self, item):
- """Set the given item as the selected item"""
- self._list.set_active_iter(self.get_iter(item))
-
- def is_selected(self, item):
- """Returns true if this item is the selected item"""
- return self.get_selected_item() == item
-
- def get_selected_items(self):
- """Return a list of selected items (one)"""
- return [self.get_selected_item()]
-
-
-class IconView(BaseView):
- """Allows a simpler IconView for DBus List Objects"""
-
- widget_type = Gtk.IconView
- changed_signal = "selection-changed"
-
- def __init__(self, widget, pixmaps, *args, **kwargs):
- super().__init__(widget, *args, **kwargs)
- self.pixmaps = pixmaps
-
- def set_selected_item(self, item):
- """Sets the selected item to this item"""
- path = self.get_iter(item, path=True)
- if path:
- self._list.set_cursor(path, None, False)
-
- def get_selected_items(self):
- """Return the seleced item"""
- return [self.get_item(path) for path in self._list.get_selected_items()]
-
- def create_model(self, liststore):
- """Setup the icon view control and model"""
- if not liststore:
- liststore = Gtk.ListStore(GOBJ, str, GdkPixbuf.Pixbuf)
- return super().create_model(liststore)
-
- def setup(self):
- """Setup the columns for the iconview"""
- self._list.set_markup_column(1)
- self._list.set_pixbuf_column(2)
- super().setup()
-
- def add(self, target):
- """Add all items from the target to the iconview"""
- for item in target:
- self.add_item(item)
-
- def add_item(self, item):
- """Add a single item image to the control"""
- if item is not None:
- self.check_item_id(item)
- return self._add_item(item)
- raise ValueError("Item can not be None.")
-
- def get_markup(self, item):
- """Default text return for markup."""
- return default(item, "name", str(item))
-
- def get_icon(self, item):
- """Default icon return, pixbuf or gnome theme name"""
- return default(item, "icon", None)
-
- def _get_icon(self, item):
- return self.pixmaps.get(self.get_icon(item), item=item)
-
- def _add_item(self, item):
- """
- Each item's properties must be stuffed into the ListStore directly
- or the IconView won't see them, but only if on auto.
- """
- if not isinstance(item, (tuple, list)):
- item = [item, self.get_markup(item), self._get_icon(item)]
- return self._model.append(item)
-
- def item_selected_signal(self, *args, **kwargs):
- """Item has been selected"""
- return self.item_selected(*self.get_selected_items())
-
-
-class ViewSort(object):
- """
- A sorting function for use is ListViews
-
- ascending - Boolean which direction to sort
- contains - Contains this string
- data - A string or function to get data from each item.
- exact - Compare to this exact string instead.
- """
-
- def __init__(self, widget, data=None, ascending=False, exact=None, contains=None):
- self.tree = None
- self.data = data
- self.asc = ascending
- self.comp = exact.lower() if exact else None
- self.cont = contains
- self.tree = widget
- self.resort()
-
- def get_data(self, model, list_iter):
- """Generate sortable data from the item"""
- item = model.get_value(list_iter, 0)
- if isinstance(self.data, str):
- value = getattr(item, self.data)
- elif callable(self.data):
- value = self.data(item)
- return value
-
- def sort_func(self, model, iter1, iter2, data):
- """Called by Gtk to sort items"""
- value1 = self.get_data(model, iter1)
- value2 = self.get_data(model, iter2)
- if value1 == None or value2 == None:
- return 0
- if self.comp:
- if cmp(self.comp, value1.lower()) == 0:
- return 1
- elif cmp(self.comp, value2.lower()) == 0:
- return -1
- return 0
- elif self.cont:
- if self.cont in value1.lower():
- return 1
- elif self.cont in value2.lower():
- return -1
- return 0
- if value1 < value2:
- return 1
- if value2 < value1:
- return -1
- return 0
-
- def resort(self):
- model = self.tree.get_model()
- model.set_sort_func(0, self.sort_func, None)
- if self.asc:
- model.set_sort_column_id(0, Gtk.SortType.ASCENDING)
- else:
- model.set_sort_column_id(0, Gtk.SortType.DESCENDING)
-
-
-class ViewColumn(object):
- """
- Add a column to a gtk treeview.
-
- name - The column name used as a label.
- expand - Set column expansion.
- """
-
- def __init__(self, widget, name, expand=False):
- if isinstance(widget, Gtk.TreeView):
- column = Gtk.TreeViewColumn((name))
- column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
- column.set_expand(expand)
- self._column = column
- widget.append_column(self._column)
- else:
- # Deal with possible drop down lists
- self._column = widget
-
- def add_renderer(self, renderer, func, expand=True):
- """Set a custom renderer"""
- self._column.pack_start(renderer, expand)
- self._column.set_cell_data_func(renderer, func, None)
- return renderer
-
- def add_image_renderer(self, icon, pad=0, pixmaps=None, size=None):
- """
- Set the image renderer
-
- icon - The function that returns the image to be dsplayed.
- pad - The amount of padding around the image.
- pixmaps - The pixmap manager to use to get images.
- size - Restrict the images to this size.
- """
- # Manager where icons will be pulled from
- filters = [SizeFilter] if size else []
- pixmaps = pixmaps or PixmapManager(
- "", pixmap_dir="./", filters=filters, size=size
- )
-
- renderer = Gtk.CellRendererPixbuf()
- renderer.set_property("ypad", pad)
- renderer.set_property("xpad", pad)
- func = self.image_func(icon or self.default_icon, pixmaps)
- return self.add_renderer(renderer, func, expand=False)
-
- def add_text_renderer(self, text, wrap=None, template=None):
- """
- Set the text renderer.
-
- text - the function that returns the text to be displayed.
- wrap - The wrapping setting for this renderer.
- template - A standard template used for this text markup.
- """
-
- renderer = Gtk.CellRendererText()
- if wrap is not None:
- renderer.props.wrap_width = wrap
- renderer.props.wrap_mode = Pango.WrapMode.WORD
-
- renderer.props.background_set = True
- renderer.props.foreground_set = True
-
- func = self.text_func(text or self.default_text, template)
- return self.add_renderer(renderer, func, expand=True)
-
- @classmethod
- def clean(cls, text, markup=False):
- """Clean text of any pango markup confusing chars"""
- if text is None:
- text = ""
- if isinstance(text, (str, int, float)):
- if markup:
- text = str(text).replace("<", "<").replace(">", ">")
- return str(text).replace("&", "&")
- elif isinstance(text, dict):
- return dict([(k, cls.clean(v)) for k, v in text.items()])
- elif isinstance(text, (list, tuple)):
- return tuple([cls.clean(value) for value in text])
- raise TypeError("Unknown value type for text: %s" % str(type(text)))
-
- def get_callout(self, call, default=None):
- """Returns the right kind of method"""
- if isinstance(call, str):
- call = item_property(call, default)
- return call
-
- def text_func(self, call, template=None):
- """Wrap up our text functionality"""
- callout = self.get_callout(call)
-
- def internal(column, cell, model, item_iter, data):
- if TreeView.is_separator(model, item_iter, data):
- return
- item = model.get_value(item_iter, 0)
- markup = template is not None
- text = callout(item)
- if isinstance(template, str):
- text = template.format(self.clean(text, markup=True))
- else:
- text = self.clean(text)
- cell.set_property("markup", str(text))
-
- return internal
-
- def image_func(self, call, pixmaps=None):
- """Wrap, wrap wrap the func"""
- callout = self.get_callout(call)
-
- def internal(column, cell, model, item_iter, data):
- if TreeView.is_separator(model, item_iter, data):
- return
- item = model.get_value(item_iter, 0)
- icon = callout(item)
- # The or blank asks for the default icon from the pixmaps
- if isinstance(icon or "", str) and pixmaps:
- # Expect a Gnome theme icon
- icon = pixmaps.get(icon)
- elif icon:
- icon = pixmaps.apply_filters(icon)
-
- cell.set_property("pixbuf", icon)
- cell.set_property("visible", True)
-
- return internal
-
- def default_text(self, item):
- """Default text return for markup."""
- return default(item, "name", str(item))
-
- def default_icon(self, item):
- """Default icon return, pixbuf or gnome theme name"""
- return default(item, "icon", None)
-
-
-class Separator:
- """Reprisentation of a separator in a list"""
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/gui/pixmap.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/gui/pixmap.py
deleted file mode 100644
index 02f4ce8..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/gui/pixmap.py
+++ /dev/null
@@ -1,346 +0,0 @@
-#
-# Copyright 2011-2022 Martin Owens
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see
-#
-"""
-Provides wrappers for pixmap access.
-"""
-
-import os
-import logging
-
-from typing import List
-from collections.abc import Iterable
-from gi.repository import Gtk, GLib, GdkPixbuf
-
-ICON_THEME = Gtk.IconTheme.get_default()
-BILINEAR = GdkPixbuf.InterpType.BILINEAR
-HYPER = GdkPixbuf.InterpType.HYPER
-
-SIZE_ASPECT = 0
-SIZE_ASPECT_GROW = 1
-SIZE_ASPECT_CROP = 2
-SIZE_STRETCH = 3
-
-
-class PixmapLoadError(ValueError):
- """Failed to load a pixmap"""
-
-
-class PixmapFilter: # pylint: disable=too-few-public-methods
- """Base class for filtering the pixmaps in a manager's output.
-
- required - List of values required for this filter.
-
- Use:
-
- class Foo(PixmapManager):
- filters = [ PixmapFilterFoo ]
-
- """
-
- required: List[str] = []
- optional: List[str] = []
-
- def __init__(self, **kwargs):
- self.enabled = True
- for key in self.required:
- if key not in kwargs:
- self.enabled = False
- else:
- setattr(self, key, kwargs[key])
-
- for key in self.optional:
- if key in kwargs:
- setattr(self, key, kwargs[key])
-
- def filter(self, img, **kwargs):
- """Run filter, replace this methodwith your own"""
- raise NotImplementedError(
- "Please add 'filter' method to your PixmapFilter class %s."
- % type(self).__name__
- )
-
- @staticmethod
- def to_size(dat):
- """Tries to calculate a size that will work for the data"""
- if isinstance(dat, (int, float)):
- return (dat, dat)
- if isinstance(dat, Iterable) and len(dat) >= 2:
- return (dat[0], dat[1])
- return None
-
-
-class OverlayFilter(PixmapFilter):
- """Adds an overlay to output images, overlay can be any name that
- the owning pixmap manager can find.
-
- overlay : Name of overlay image
- position : Location of the image:
- 0 - Full size (1 to 1 overlay, default)
- (x,y) - Percentage from one end to the other position 0-1
- alpha : Blending alpha, 0 - 255
-
- """
-
- optional = ["position", "overlay", "alpha"]
-
- def __init__(self, *args, **kwargs):
- self.position = (0, 0)
- self.overlay = None
- self.alpha = 255
- super().__init__(*args, **kwargs)
- self.pad_x, self.pad_y = self.to_size(self.position)
-
- def get_overlay(self, **kwargs):
- if "manager" not in kwargs:
- raise ValueError("PixmapManager must be provided when adding an overlay.")
- return kwargs["manager"].get(
- kwargs.get("overlay", None) or self.overlay, no_overlay=True
- )
-
- def filter(self, img, no_overlay=False, **kwargs):
- # Recursion protection
- if no_overlay:
- return img
-
- overlay = self.get_overlay(**kwargs)
- if overlay:
- img = img.copy()
-
- (x, y, width, height) = self.set_position(overlay, img)
- overlay.composite(
- img, x, y, width, height, x, y, 1, 1, BILINEAR, self.alpha
- )
- return img
-
- def set_position(self, overlay, img):
- """Sets the position of img on the given width and height"""
- img_w, img_h = img.get_width(), img.get_height()
- ovl_w, ovl_h = overlay.get_width(), overlay.get_height()
- return (
- max([0, (img_w - ovl_w) * self.pad_x]),
- max([0, (img_h - ovl_h) * self.pad_y]),
- min([ovl_w, img_w]),
- min([ovl_h, img_h]),
- )
-
-
-class SizeFilter(PixmapFilter):
- """Resizes images to a certain size:
-
- resize_mode - Way in which the size is calculated
- 0 - Best Aspect, don't grow
- 1 - Best Aspect, grow
- 2 - Cropped Aspect
- 3 - Stretch
- """
-
- required = ["size"]
- optional = ["resize_mode"]
-
- def __init__(self, *args, **kwargs):
- self.size = None
- self.resize_mode = SIZE_ASPECT
- super().__init__(*args, **kwargs)
- self.img_w, self.img_h = self.to_size(self.size) or (0, 0)
-
- def aspect(self, img_w, img_h):
- """Get the aspect ratio of the image resized"""
- if self.resize_mode == SIZE_STRETCH:
- return (self.img_w, self.img_h)
-
- if (
- self.resize_mode == SIZE_ASPECT
- and img_w < self.img_w
- and img_h < self.img_h
- ):
- return (img_w, img_h)
- (pcw, pch) = (self.img_w / img_w, self.img_h / img_h)
- factor = (
- max(pcw, pch) if self.resize_mode == SIZE_ASPECT_CROP else min(pcw, pch)
- )
- return (int(img_w * factor), int(img_h * factor))
-
- def filter(self, img, **kwargs):
- if self.size is not None:
- (width, height) = self.aspect(img.get_width(), img.get_height())
- return img.scale_simple(width, height, HYPER)
- return img
-
-
-class PadFilter(SizeFilter):
- """Add padding to the image to make it a standard size"""
-
- optional = ["padding"]
-
- def __init__(self, *args, **kwargs):
- self.size = None
- self.padding = 0.5
- super().__init__(*args, **kwargs)
- self.pad_x, self.pad_y = self.to_size(self.padding)
-
- def filter(self, img, **kwargs):
- (width, height) = (img.get_width(), img.get_height())
- if width < self.img_w or height < self.img_h:
- target = GdkPixbuf.Pixbuf.new(
- img.get_colorspace(),
- True,
- img.get_bits_per_sample(),
- max([width, self.img_w]),
- max([height, self.img_h]),
- )
- target.fill(0x0) # Transparent black
-
- x = (target.get_width() - width) * self.pad_x
- y = (target.get_height() - height) * self.pad_y
-
- img.composite(target, x, y, width, height, x, y, 1, 1, BILINEAR, 255)
- return target
- return img
-
-
-class PixmapManager:
- """Manage a set of cached pixmaps, returns the default image
- if it can't find one or the missing image if that's available."""
-
- missing_image = "image-missing"
- default_image = "application-default-icon"
- icon_theme = ICON_THEME
- theme_size = 32
- filters: List[type] = []
- pixmap_dir = None
-
- def __init__(self, location="", **kwargs):
- self.location = location
- if self.pixmap_dir and not os.path.isabs(location):
- self.location = os.path.join(self.pixmap_dir, location)
-
- self.loader_size = PixmapFilter.to_size(kwargs.pop("load_size", None))
-
- # Add any instance specified filters first
- self._filters = []
- for item in kwargs.get("filters", []) + self.filters:
- if isinstance(item, PixmapFilter):
- self._filters.append(item)
- elif callable(item):
- # Now add any class specified filters with optional kwargs
- self._filters.append(item(**kwargs))
-
- self.cache = {}
- self.get_pixmap(self.default_image)
-
- def get(self, *args, **kwargs):
- """Get a pixmap of any kind"""
- return self.get_pixmap(*args, **kwargs)
-
- def get_missing_image(self):
- """Get a missing image when other images aren't found"""
- return self.get(self.missing_image)
-
- @staticmethod
- def data_is_file(data):
- """Test the file to see if it's a filename or not"""
- return isinstance(data, str) and "
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see
-#
-"""
-Structures for consistant testing of Gtk GUI programs.
-"""
-
-import sys
-from gi.repository import Gtk, GLib
-
-
-class MainLoopProtection:
- """
- This protection class provides a way to launch the Gtk mainloop in a test
- friendly way.
-
- Exception handling hooks provide a way to see errors that happen
- inside the main loop, raising them back to the caller.
- A full timeout in seconds stops the gtk mainloop from operating
- beyond a set time, acting as a kill switch in the event something
- has gone horribly wrong.
-
- Use:
- with MainLoopProtection(timeout=10s):
- app.run()
- """
-
- def __init__(self, timeout=10):
- self.timeout = timeout * 1000
- self._hooked = None
- self._old_excepthook = None
-
- def __enter__(self):
- # replace sys.excepthook with our own and remember hooked raised error
- self._old_excepthook = sys.excepthook
- sys.excepthook = self.excepthook
- # Remove mainloop by force if it doesn't die within 10 seconds
- self._timeout = GLib.timeout_add(self.timeout, self.idle_exit)
-
- def __exit__(self, exc, value, traceback): # pragma: no cover
- """Put the except handler back, cancel the timer and raise if needed"""
- if self._old_excepthook:
- sys.excepthook = self._old_excepthook
- # Remove the timeout, so we don't accidentally kill later mainloops
- if self._timeout:
- GLib.source_remove(self._timeout)
- # Raise an exception if one happened during the test run
- if self._hooked:
- exc, value, traceback = self._hooked
- if value and traceback:
- raise value.with_traceback(traceback)
-
- def idle_exit(self): # pragma: no cover
- """Try to going to kill any running mainloop."""
- GLib.idle_add(Gtk.main_quit)
-
- def excepthook(self, ex_type, ex_value, traceback): # pragma: no cover
- """Catch errors thrown by the Gtk mainloop"""
- self.idle_exit()
- # Remember the exception data for raising inside the test context
- if ex_value is not None:
- self._hooked = [ex_type, ex_value, traceback]
- # Fallback and double print the exception (remove if double printing is problematic)
- return self._old_excepthook(ex_type, ex_value, traceback)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/gui/window.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/gui/window.py
deleted file mode 100644
index a5c1ef6..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/gui/window.py
+++ /dev/null
@@ -1,201 +0,0 @@
-#
-# Copyright 2012-2022 Martin Owens
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see
-#
-# pylint: disable=too-many-instance-attributes
-"""
-Wraps the gtk windows with something a little nicer.
-"""
-import logging
-
-from gi.repository import Gtk
-
-PROPS = {
- "Box": ["expand", "fill", "padding", "pack-type"],
- "Grid": ["top-attach", "left-attach", "height", "width"],
- "Table": ["top-attach", "left-attach", "bottom-attach", "right-attach"],
-}
-
-
-def protect(cls, *methods):
- """Simple check for protecting an inherrited class from having
- certain methods over-ridden"""
- if not isinstance(cls, type):
- cls = type(cls)
- for method in methods:
- if method in cls.__dict__: # pragma: no cover
- raise RuntimeError(
- f"{cls.__name__} in {cls.__module__} has" f" protected def {method}()"
- )
-
-
-class Window:
- """
- This wraps gtk windows and allows for having parent windows
-
- name = 'name-of-the-window'
-
- Should the window be the first loaded and end gtk when closed:
-
- primary = True/False
- """
-
- primary = True
- name = None
-
- def __init__(self, gapp):
- self.gapp = gapp
- self.dead = False
- self.parent = None
- self.args = ()
- ui_file = gapp.get_ui_file(self.name)
-
- # Setup the gtk app connection
- self.w_tree = Gtk.Builder()
- self.widget = self.w_tree.get_object
- self.w_tree.set_translation_domain(gapp.app_name)
- self.w_tree.add_from_file(ui_file)
-
- # Setup the gtk builder window
- self.window = self.widget(self.name)
- if not self.window: # pragma: no cover
- raise KeyError(f"Missing window widget '{self.name}' from '{ui_file}'")
-
- # Give us a window id to track this window
- self.wid = str(hash(self.window))
-
- def extract(self):
- """Extract this window's container for use in other apps"""
- for child in self.window.get_children():
- self.window.remove(child)
- return child
-
- def init(self, parent=None, **kwargs):
- """Initialise the window within the GtkApp"""
- if "replace" not in kwargs:
- protect(self, "destroy", "exit", "load_window", "proto_window")
- self.args = kwargs
- # Set object defaults
- self.parent = parent
-
- self.w_tree.connect_signals(self)
-
- # These are some generic convience signals
- self.window.connect("destroy", self.exit)
-
- # If we have a parent window, then we expect not to quit
- if self.parent:
- self.window.set_transient_for(self.parent)
- self.parent.set_sensitive(False)
-
- # We may have some more gtk widgets to setup
- self.load_widgets(**self.args)
- self.window.show()
-
- def load_window(self, name, *args, **kwargs):
- """Load child window, automatically sets parent"""
- kwargs["parent"] = self.window
- return self.gapp.load_window(name, *args, **kwargs)
-
- def load_widgets(self):
- """Child class should use this to create widgets"""
-
- def destroy(self, widget=None): # pylint: disable=unused-argument
- """Destroy the window"""
- logging.debug("Destroying Window '%s'", self.name)
- self.window.destroy()
- # We don't need to call self.exit(), handeled by window event.
-
- def pre_exit(self):
- """Internal method for what to do when the window has died"""
-
- def exit(self, widget=None):
- """Called when the window needs to exit."""
- # Is the signal called by the window or by something else?
- if not widget or not isinstance(widget, Gtk.Window):
- self.destroy()
- # Clean up any required processes
- self.pre_exit()
- if self.parent:
- # We assume the parent didn't load another gtk loop
- self.parent.set_sensitive(True)
- # Exit our entire app if this is the primary window
- # Or just remove from parent window list, which may still exit.
- if self.primary:
- logging.debug("Exiting the application")
- self.gapp.exit()
- else:
- logging.debug("Removing Window %s from parent", self.name)
- self.gapp.remove_window(self)
- # Now finish up what ever is left to do now the window is dead.
- self.dead = True
- self.post_exit()
- return widget
-
- def post_exit(self):
- """Called after we've killed the window"""
-
- def if_widget(self, name):
- """
- Attempt to get the widget from gtk, but if not return a fake that won't
- cause any trouble if we don't further check if it's real.
- """
- return self.widget(name) or FakeWidget(name)
-
- def replace(self, old, new):
- """Replace the old widget with the new widget"""
- if isinstance(old, str):
- old = self.widget(old)
- if isinstance(new, str):
- new = self.widget(new)
- target = old.get_parent()
- source = new.get_parent()
- if target is not None:
- if source is not None:
- source.remove(new)
- target.remove(old)
- target.add(new)
-
- @staticmethod
- def get_widget_name(obj):
- """Return the widget's name in the builder file"""
- return Gtk.Buildable.get_name(obj)
-
-
-class ChildWindow(Window):
- """
- Base class for child window objects, these child windows are typically
- window objects in the same gtk builder file as their parents. If you just want
- to make a window that interacts with a parent window, use the normal
- Window class and call with the optional parent attribute.
- """
-
- primary = False
-
-
-class FakeWidget:
- """A fake widget class that can take calls"""
-
- def __init__(self, name):
- self._name = name
-
- def __getattr__(self, name):
- def _fake(*args, **kwargs):
- logging.info("Calling fake method: %s:%s", args, kwargs)
-
- return _fake
-
- def __bool__(self):
- return False
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/interfaces/IElement.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/interfaces/IElement.py
deleted file mode 100644
index 3e9df53..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/interfaces/IElement.py
+++ /dev/null
@@ -1,39 +0,0 @@
-"""Element abstractions for type comparisons without circular imports
-
-.. versionadded:: 1.2"""
-
-from __future__ import annotations
-
-from abc import ABC, abstractmethod
-
-import sys
-from lxml import etree
-
-if sys.version_info >= (3, 8):
- from typing import Protocol
-else:
- from typing_extensions import Protocol
-
-
-class IBaseElement(ABC, etree.ElementBase):
- """Abstraction for BaseElement to avoid circular imports"""
-
- @abstractmethod
- def get_id(self, as_url=0):
- """Returns the element ID. If not set, generates a unique ID."""
- raise NotImplementedError
-
-
-class BaseElementProtocol(Protocol):
- """Abstraction for BaseElement, to be used as typehint in mixin classes"""
-
- def get_id(self, as_url=0) -> str:
- """Returns the element ID. If not set, generates a unique ID."""
-
- @property
- def root(self) -> ISVGDocumentElement:
- """Returns the element's root."""
-
-
-class ISVGDocumentElement(IBaseElement):
- """Abstraction for SVGDocumentElement"""
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/interfaces/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/interfaces/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/inx.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/inx.py
deleted file mode 100644
index 2360bc6..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/inx.py
+++ /dev/null
@@ -1,244 +0,0 @@
-# coding=utf-8
-#
-# Copyright (C) 2018 Martin Owens
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-"""
-Parsing inx files for checking and generating.
-"""
-
-import os
-from inspect import isclass
-from importlib import util
-from lxml import etree
-
-from .base import InkscapeExtension
-from .utils import Boolean
-
-NSS = {
- "inx": "http://www.inkscape.org/namespace/inkscape/extension",
- "inkscape": "http://www.inkscape.org/namespaces/inkscape",
-}
-SSN = {b: a for (a, b) in NSS.items()}
-
-
-class InxLookup(etree.CustomElementClassLookup):
- """Custom inx xml file lookup"""
-
- def lookup(
- self, node_type, document, namespace, name
- ): # pylint: disable=unused-argument
- if name == "param":
- return ParamElement
- return InxElement
-
-
-INX_PARSER = etree.XMLParser()
-INX_PARSER.set_element_class_lookup(InxLookup())
-
-
-class InxFile:
- """Open an INX file and provide useful functions"""
-
- name = property(lambda self: self.xml.get_text("name"))
- ident = property(lambda self: self.xml.get_text("id"))
- slug = property(lambda self: self.ident.split(".")[-1].title().replace("_", ""))
- kind = property(lambda self: self.metadata["type"])
- warnings = property(lambda self: sorted(list(set(self.xml.warnings))))
-
- def __init__(self, filename):
- if isinstance(filename, str) and "<" in filename:
- filename = filename.encode("utf8")
- if isinstance(filename, bytes) and b"<" in filename:
- self.filename = None
- self.doc = etree.ElementTree(etree.fromstring(filename, parser=INX_PARSER))
- else:
- self.filename = os.path.basename(filename)
- self.doc = etree.parse(filename, parser=INX_PARSER)
- self.xml = self.doc.getroot()
- self.xml.warnings = []
-
- def __repr__(self):
- return f""
-
- @property
- def script(self):
- """Returns information about the called script"""
- command = self.xml.find_one("script/command")
- if command is None:
- return {}
- return {
- "interpreter": command.get("interpreter", None),
- "location": command.get("location", None),
- "script": command.text,
- }
-
- @property
- def extension_class(self):
- """Attempt to get the extension class"""
- script = self.script.get("script", None)
- if script is not None:
- name = script[:-3].replace("/", ".")
- spec = util.spec_from_file_location(name, script)
- mod = util.module_from_spec(spec)
- spec.loader.exec_module(mod)
- for value in mod.__dict__.values():
- if (
- "Base" not in name
- and isclass(value)
- and value.__module__ == name
- and issubclass(value, InkscapeExtension)
- ):
- return value
- return None
-
- @property
- def metadata(self):
- """Returns information about what type of extension this is"""
- effect = self.xml.find_one("effect")
- output = self.xml.find_one("output")
- inputs = self.xml.find_one("input")
- data = {}
- if effect is not None:
- template = self.xml.find_one("inkscape:templateinfo")
- if template is not None:
- data["type"] = "template"
- data["desc"] = self.xml.get_text(
- "templateinfo/shortdesc", nss="inkscape"
- )
- data["author"] = self.xml.get_text(
- "templateinfo/author", nss="inkscape"
- )
- else:
- data["type"] = "effect"
- data["preview"] = Boolean(effect.get("needs-live-preview", "true"))
- data["objects"] = effect.get_text("object-type", "all")
- elif inputs is not None:
- data["type"] = "input"
- data["extension"] = inputs.get_text("extension")
- data["mimetype"] = inputs.get_text("mimetype")
- data["tooltip"] = inputs.get_text("filetypetooltip")
- data["name"] = inputs.get_text("filetypename")
- elif output is not None:
- data["type"] = "output"
- data["dataloss"] = Boolean(output.get_text("dataloss", "false"))
- data["extension"] = output.get_text("extension")
- data["mimetype"] = output.get_text("mimetype")
- data["tooltip"] = output.get_text("filetypetooltip")
- data["name"] = output.get_text("filetypename")
- return data
-
- @property
- def menu(self):
- """Return the menu this effect ends up in"""
-
- def _recurse_menu(parent):
- for child in parent.xpath("submenu"):
- yield child.get("name")
- for subchild in _recurse_menu(child):
- yield subchild
- break # Not more than one menu chain?
-
- menu = self.xml.find_one("effect/effects-menu")
- return list(_recurse_menu(menu)) + [self.name]
-
- @property
- def params(self):
- """Get all params at all levels"""
- # Returns any params at any levels
- return list(self.xml.xpath("//param"))
-
-
-class InxElement(etree.ElementBase):
- """Any element in an inx file
-
- .. versionadded:: 1.1"""
-
- def set_warning(self, msg):
- """Set a warning for slightly incorrect inx contents"""
- root = self.get_root()
- if hasattr(root, "warnings"):
- root.warnings.append(msg)
-
- def get_root(self):
- """Get the root document element from any element descendent"""
- if self.getparent() is not None:
- return self.getparent().get_root()
- return self
-
- def get_default_prefix(self):
- """Set default xml namespace prefix. If none is defined, set warning"""
- tag = self.get_root().tag
- if "}" in tag:
- (url, tag) = tag[1:].split("}", 1)
- return SSN.get(url, "inx")
- self.set_warning("No inx xml prefix.")
- return None # no default prefix
-
- def apply_nss(self, xpath, nss=None):
- """Add prefixes to any xpath string"""
- if nss is None:
- nss = self.get_default_prefix()
-
- def _process(seg):
- if ":" in seg or not seg or not nss:
- return seg
- return f"{nss}:{seg}"
-
- return "/".join([_process(seg) for seg in xpath.split("/")])
-
- def xpath(self, xpath, nss=None):
- """Namespace specific xpath searches
-
- .. versionadded:: 1.1"""
- return super().xpath(self.apply_nss(xpath, nss=nss), namespaces=NSS)
-
- def find_one(self, name, nss=None):
- """Return the first element matching the given name
-
- .. versionadded:: 1.1"""
- for elem in self.xpath(name, nss=nss):
- return elem
- return None
-
- def get_text(self, name, default=None, nss=None):
- """Get text content agnostically"""
- for pref in ("", "_"):
- elem = self.find_one(pref + name, nss=nss)
- if elem is not None and elem.text:
- if pref == "_":
- self.set_warning(f"Use of old translation scheme: <_{name}...>")
- return elem.text
- return default
-
-
-class ParamElement(InxElement):
- """
- A param in an inx file.
- """
-
- name = property(lambda self: self.get("name"))
- param_type = property(lambda self: self.get("type", "string"))
-
- @property
- def options(self):
- """Return a list of option values"""
- if self.param_type == "notebook":
- return [option.get("name") for option in self.xpath("page")]
- return [option.get("value") for option in self.xpath("option")]
-
- def __repr__(self):
- return f" "
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/localization.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/localization.py
deleted file mode 100644
index 7de6c57..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/localization.py
+++ /dev/null
@@ -1,117 +0,0 @@
-# coding=utf-8
-#
-# Copyright (C) 2010 Nick Drobchenko, nick@cnc-club.ru
-# Copyright (C) 2005 Aaron Spike, aaron@ekips.org
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-"""
-Allow extensions to translate messages.
-"""
-
-import gettext
-import os, sys
-
-# Get gettext domain and matching locale directory for translation of extensions strings
-# (both environment variables are set by Inkscape)
-GETTEXT_DOMAIN = os.environ.get("INKEX_GETTEXT_DOMAIN")
-GETTEXT_DIRECTORY = os.environ.get("INKEX_GETTEXT_DIRECTORY")
-
-# INKSCAPE_LOCALEDIR can be used to override the default locale directory Inkscape uses
-INKSCAPE_LOCALEDIR = os.environ.get("INKSCAPE_LOCALEDIR")
-
-
-def localize(domain=GETTEXT_DOMAIN, localedir=GETTEXT_DIRECTORY):
- """Configure gettext and install _() function into builtins namespace for easy
- access"""
-
- # Do not enable translation if GETTEXT_DOMAIN is unset.
- # This is the case when translationdomain="none", but also when no catalog was
- # found.
- # Install a NullTranslation just to be sure
- # (so we do not get errors about undefined '_')
- if domain is None:
- gettext.NullTranslations().install()
- return
-
- # Use the default system locale by default,
- # but prefer LANGUAGE environment variable
- # (which is set by Inkscape according to UI language)
- languages = None
-
- trans = gettext.translation(domain, localedir, languages, fallback=True)
- trans.install()
-
-
-def inkex_localize():
- """
- Return internal Translations instance for translation of the inkex module itself
- Those will always use the 'inkscape' domain and attempt to lookup the same catalog
- Inkscape uses
- """
-
- domain = "inkscape"
- localedir = INKSCAPE_LOCALEDIR
- languages = None
-
- return gettext.translation(domain, localedir, languages, fallback=True)
-
-
-inkex_gettext = inkex_localize().gettext # pylint: disable=invalid-name
-"""
-Shortcut for gettext. Import as::
-
- from inkex.localize import inkex_gettext as _
-
-"""
-
-inkex_ngettext = inkex_localize().ngettext
-"""
-Shortcut for ngettext
-
- .. versionadded:: 1.2
-"""
-
-
-def inkex_fgettext(message, *args, **kwargs):
- """
- Shortcut for gettext and subsequent formatting. Import as::
-
- from inkex.localize import inkex_fgettext as _f
-
- The positionals and keyword arguments are passed to ``str.format()``.
-
- The call to xgettext must contain::
-
- --keyword=_f
- """
- return inkex_gettext(message).format(*args, **kwargs)
-
-
-if sys.version_info >= (3, 8):
- inkex_pgettext = inkex_localize().pgettext
- """
- Gettext with context. Import as::
-
- from inkex.localize import inkex_pgettext as pgettext
-
- Both parameters **must** be string literals. The call to xgettext must contain::
-
- --keyword=pgettext:1c,2
-
- .. versionadded:: 1.2
- """
-else:
- inkex_pgettext = lambda context, message: message
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/paths.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/paths.py
deleted file mode 100644
index ba4a525..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/paths.py
+++ /dev/null
@@ -1,2035 +0,0 @@
-# coding=utf-8
-#
-# Copyright (C) 2018 Martin Owens
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-"""
-functions for digesting paths
-"""
-from __future__ import annotations
-
-import re
-import copy
-import abc
-
-from math import atan2, cos, pi, sin, sqrt, acos, tan
-from typing import (
- Any,
- Type,
- Dict,
- Optional,
- Union,
- Tuple,
- List,
- Generator,
- TypeVar,
-)
-from .transforms import (
- Transform,
- BoundingBox,
- Vector2d,
- cubic_extrema,
- quadratic_extrema,
-)
-from .utils import classproperty, strargs
-
-
-Pathlike = TypeVar("Pathlike", bound="PathCommand")
-AbsolutePathlike = TypeVar("AbsolutePathlike", bound="AbsolutePathCommand")
-
-# All the names that get added to the inkex API itself.
-__all__ = (
- "Path",
- "CubicSuperPath",
- "PathCommand",
- "AbsolutePathCommand",
- "RelativePathCommand",
- # Path commands:
- "Line",
- "line",
- "Move",
- "move",
- "ZoneClose",
- "zoneClose",
- "Horz",
- "horz",
- "Vert",
- "vert",
- "Curve",
- "curve",
- "Smooth",
- "smooth",
- "Quadratic",
- "quadratic",
- "TepidQuadratic",
- "tepidQuadratic",
- "Arc",
- "arc",
- # errors
- "InvalidPath",
-)
-
-LEX_REX = re.compile(r"([MLHVCSQTAZmlhvcsqtaz])([^MLHVCSQTAZmlhvcsqtaz]*)")
-NONE = lambda obj: obj is not None
-
-
-class InvalidPath(ValueError):
- """Raised when given an invalid path string"""
-
-
-class PathCommand(abc.ABC):
- """
- Base class of all path commands
- """
-
- # Number of arguments that follow this path commands letter
- nargs = -1
-
- @classproperty # From python 3.9 on, just combine @classmethod and @property
- def name(cls): # pylint: disable=no-self-argument
- """The full name of the segment (i.e. Line, Arc, etc)"""
- return cls.__name__ # pylint: disable=no-member
-
- @classproperty
- def letter(cls): # pylint: disable=no-self-argument
- """The single letter representation of this command (i.e. L, A, etc)"""
- return cls.name[0]
-
- @classproperty
- def next_command(self):
- """The implicit next command. This is for automatic chains where the next
- command isn't given, just a bunch on numbers which we automatically parse."""
- return self
-
- @property
- def is_relative(self): # type: () -> bool
- """Whether the command is defined in relative coordinates, i.e. relative to
- the previous endpoint (lower case path command letter)"""
- raise NotImplementedError
-
- @property
- def is_absolute(self): # type: () -> bool
- """Whether the command is defined in absolute coordinates (upper case path
- command letter)"""
- raise NotImplementedError
-
- def to_relative(self, prev): # type: (Vector2d) -> RelativePathCommand
- """Return absolute counterpart for absolute commands or copy for relative"""
- raise NotImplementedError
-
- def to_absolute(self, prev): # type: (Vector2d) -> AbsolutePathCommand
- """Return relative counterpart for relative commands or copy for absolute"""
- raise NotImplementedError
-
- def reverse(self, first, prev):
- """Reverse path command
-
- .. versionadded:: 1.1"""
-
- def to_non_shorthand(self, prev, prev_control): # pylint: disable=unused-argument
- # type: (Vector2d, Vector2d) -> AbsolutePathCommand
- """Return an absolute non-shorthand command
-
- .. versionadded:: 1.1"""
- return self.to_absolute(prev)
-
- # The precision of the numbers when converting to string
- number_template = "{:.6g}"
-
- # Maps single letter path command to corresponding class
- # (filled at the bottom of file, when all classes already defined)
- _letter_to_class = {} # type: Dict[str, Type[Any]]
-
- @staticmethod
- def letter_to_class(letter):
- """Returns class for given path command letter"""
- return PathCommand._letter_to_class[letter]
-
- @property
- def args(self): # type: () -> List[float]
- """Returns path command arguments as tuple of floats"""
- raise NotImplementedError()
-
- def control_points(
- self, first: Vector2d, prev: Vector2d, prev_prev: Vector2d
- ) -> Union[List[Vector2d], Generator[Vector2d, None, None]]:
- """Returns list of path command control points"""
- raise NotImplementedError
-
- @classmethod
- def _argt(cls, sep):
- return sep.join([cls.number_template] * cls.nargs)
-
- def __str__(self):
- return f"{self.letter} {self._argt(' ').format(*self.args)}".strip()
-
- def __repr__(self):
- # pylint: disable=consider-using-f-string
- return "{{}}({})".format(self._argt(", ")).format(self.name, *self.args)
-
- def __eq__(self, other):
- previous = Vector2d()
- if type(self) == type(other): # pylint: disable=unidiomatic-typecheck
- return self.args == other.args
- if isinstance(other, tuple):
- return self.args == other
- if not isinstance(other, PathCommand):
- raise ValueError("Can't compare types")
- try:
- if self.is_relative == other.is_relative:
- return self.to_curve(previous) == other.to_curve(previous)
- except ValueError:
- pass
- return False
-
- def end_point(self, first, prev): # type: (Vector2d, Vector2d) -> Vector2d
- """Returns last control point of path command"""
- raise NotImplementedError()
-
- def update_bounding_box(
- self, first: Vector2d, last_two_points: List[Vector2d], bbox: BoundingBox
- ):
- # pylint: disable=unused-argument
- """Enlarges given bbox to contain path element.
-
- Args:
- first (Vector2d): first point of path. Required to calculate Z segment
- last_two_points (List[Vector2d]): list with last two control points in abs
- coords.
- bbox (BoundingBox): bounding box to update
- """
-
- raise NotImplementedError(f"Bounding box is not implemented for {self.name}")
-
- def to_curve(self, prev, prev_prev=Vector2d()):
- # type: (Vector2d, Vector2d) -> Curve
- """Convert command to :py:class:`Curve`
-
- Curve().to_curve() returns a copy
- """
- raise NotImplementedError(f"To curve not supported for {self.name}")
-
- def to_curves(self, prev, prev_prev=Vector2d()):
- # type: (Vector2d, Vector2d) -> List[Curve]
- """Convert command to list of :py:class:`Curve` commands"""
- return [self.to_curve(prev, prev_prev)]
-
- def to_line(self, prev):
- # type: (Vector2d) -> Line
- """Converts this segment to a line (copies if already a line)"""
- return Line(*self.end_point(Vector2d(), prev))
-
-
-class RelativePathCommand(PathCommand):
- """
- Abstract base class for relative path commands.
-
- Implements most of methods of :py:class:`PathCommand` through
- conversion to :py:class:`AbsolutePathCommand`
- """
-
- @property
- def is_relative(self):
- return True
-
- @property
- def is_absolute(self):
- return False
-
- def control_points(
- self, first: Vector2d, prev: Vector2d, prev_prev: Vector2d
- ) -> Union[List[Vector2d], Generator[Vector2d, None, None]]:
- return self.to_absolute(prev).control_points(first, prev, prev_prev)
-
- def to_relative(self, prev):
- # type: (Pathlike, Vector2d) -> Pathlike
- return self.__class__(*self.args)
-
- def update_bounding_box(self, first, last_two_points, bbox):
- self.to_absolute(last_two_points[-1]).update_bounding_box(
- first, last_two_points, bbox
- )
-
- def end_point(self, first, prev):
- # type: (Vector2d, Vector2d) -> Vector2d
- return self.to_absolute(prev).end_point(first, prev)
-
- def to_curve(self, prev, prev_prev=Vector2d()):
- # type: (Vector2d, Vector2d) -> Curve
- return self.to_absolute(prev).to_curve(prev, prev_prev)
-
- def to_curves(self, prev, prev_prev=Vector2d()):
- # type: (Vector2d, Vector2d) -> List[Curve]
- return self.to_absolute(prev).to_curves(prev, prev_prev)
-
-
-class AbsolutePathCommand(PathCommand):
- """Absolute path command. Unlike :py:class:`RelativePathCommand` can be transformed
- directly."""
-
- @property
- def is_relative(self):
- return False
-
- @property
- def is_absolute(self):
- return True
-
- def to_absolute(
- self, prev
- ): # type: (AbsolutePathlike, Vector2d) -> AbsolutePathlike
- return self.__class__(*self.args)
-
- def transform(
- self, transform
- ): # type: (AbsolutePathlike, Transform) -> AbsolutePathlike
- """Returns new transformed segment
-
- :param transform: a transformation to apply
- """
- raise NotImplementedError()
-
- def rotate(
- self, degrees, center
- ): # type: (AbsolutePathlike, float, Vector2d) -> AbsolutePathlike
- """
- Returns new transformed segment
-
- :param degrees: rotation angle in degrees
- :param center: invariant point of rotation
- """
- return self.transform(Transform(rotate=(degrees, center[0], center[1])))
-
- def translate(self, dr): # type: (AbsolutePathlike, Vector2d) -> AbsolutePathlike
- """Translate or scale this path command by dr"""
- return self.transform(Transform(translate=dr))
-
- def scale(
- self, factor
- ): # type: (AbsolutePathlike, Union[float, Tuple[float,float]]) -> AbsolutePathlike
- """Returns new transformed segment
-
- :param factor: scale or (scale_x, scale_y)
- """
- return self.transform(Transform(scale=factor))
-
-
-class Line(AbsolutePathCommand):
- """Line segment"""
-
- nargs = 2
-
- @property
- def args(self):
- return self.x, self.y
-
- def __init__(self, x, y):
- self.x = x
- self.y = y
-
- def update_bounding_box(self, first, last_two_points, bbox):
- bbox += BoundingBox(
- (last_two_points[-1].x, self.x), (last_two_points[-1].y, self.y)
- )
-
- def control_points(self, first, prev, prev_prev):
- # type: (Vector2d, Vector2d, Vector2d) -> Generator[Vector2d, None, None]
- yield Vector2d(self.x, self.y)
-
- def to_relative(self, prev):
- # type: (Vector2d) -> line
- return line(self.x - prev.x, self.y - prev.y)
-
- def transform(self, transform):
- # type: (Line, Transform) -> Line
- return Line(*transform.apply_to_point((self.x, self.y)))
-
- def end_point(self, first, prev):
- # type: (Vector2d, Vector2d) -> Vector2d
- return Vector2d(self.x, self.y)
-
- def to_curve(self, prev, prev_prev=Vector2d()):
- # type: (Vector2d, Optional[Vector2d]) -> Curve
- return Curve(prev.x, prev.y, self.x, self.y, self.x, self.y)
-
- def reverse(self, first, prev):
- return Line(prev.x, prev.y)
-
-
-class line(RelativePathCommand): # pylint: disable=invalid-name
- """Relative line segment"""
-
- nargs = 2
-
- @property
- def args(self):
- return self.dx, self.dy
-
- def __init__(self, dx, dy):
- self.dx = dx
- self.dy = dy
-
- def to_absolute(self, prev): # type: (Vector2d) -> Line
- return Line(prev.x + self.dx, prev.y + self.dy)
-
- def reverse(self, first, prev):
- return line(-self.dx, -self.dy)
-
-
-class Move(AbsolutePathCommand):
- """Move pen segment without a line"""
-
- nargs = 2
- next_command = Line
-
- @property
- def args(self):
- return self.x, self.y
-
- def __init__(self, x, y):
- self.x = x
- self.y = y
-
- def update_bounding_box(self, first, last_two_points, bbox):
- bbox += BoundingBox(self.x, self.y)
-
- def control_points(self, first, prev, prev_prev):
- # type: (Vector2d, Vector2d, Vector2d) -> Generator[Vector2d, None, None]
- yield Vector2d(self.x, self.y)
-
- def to_relative(self, prev):
- # type: (Vector2d) -> move
- return move(self.x - prev.x, self.y - prev.y)
-
- def transform(self, transform):
- # type: (Transform) -> Move
- return Move(*transform.apply_to_point((self.x, self.y)))
-
- def end_point(self, first, prev):
- # type: (Vector2d, Vector2d) -> Vector2d
- return Vector2d(self.x, self.y)
-
- def to_curve(self, prev, prev_prev=Vector2d()):
- # type: (Vector2d, Optional[Vector2d]) -> Curve
- raise ValueError("Move segments can not be changed into curves.")
-
- def reverse(self, first, prev):
- return Move(prev.x, prev.y)
-
-
-class move(RelativePathCommand): # pylint: disable=invalid-name
- """Relative move segment"""
-
- nargs = 2
- next_command = line
-
- @property
- def args(self):
- return self.dx, self.dy
-
- def __init__(self, dx, dy):
- self.dx = dx
- self.dy = dy
-
- def to_absolute(self, prev): # type: (Vector2d) -> Move
- return Move(prev.x + self.dx, prev.y + self.dy)
-
- def reverse(self, first, prev):
- return move(prev.x - first.x, prev.y - first.y)
-
-
-class ZoneClose(AbsolutePathCommand):
- """Close segment to finish a path"""
-
- nargs = 0
- next_command = Move
-
- @property
- def args(self):
- return ()
-
- def update_bounding_box(self, first, last_two_points, bbox):
- pass
-
- def transform(self, transform):
- # type: (Transform) -> ZoneClose
- return ZoneClose()
-
- def control_points(self, first, prev, prev_prev):
- # type: (Vector2d, Vector2d, Vector2d) -> Generator[Vector2d, None, None]
- yield first
-
- def to_relative(self, prev):
- # type: (Vector2d) -> zoneClose
- return zoneClose()
-
- def end_point(self, first, prev):
- # type: (Vector2d, Vector2d) -> Vector2d
- return first
-
- def to_curve(self, prev, prev_prev=Vector2d()):
- # type: (Vector2d, Optional[Vector2d]) -> Curve
- raise ValueError("ZoneClose segments can not be changed into curves.")
-
- def reverse(self, first, prev):
- return Line(prev.x, prev.y)
-
-
-class zoneClose(RelativePathCommand): # pylint: disable=invalid-name
- """Same as above (svg says no difference)"""
-
- nargs = 0
- next_command = Move
-
- @property
- def args(self):
- return ()
-
- def to_absolute(self, prev):
- return ZoneClose()
-
- def reverse(self, first, prev):
- return line(prev.x - first.x, prev.y - first.y)
-
-
-class Horz(AbsolutePathCommand):
- """Horizontal Line segment"""
-
- nargs = 1
-
- @property
- def args(self):
- return (self.x,)
-
- def __init__(self, x):
- self.x = x
-
- def update_bounding_box(self, first, last_two_points, bbox):
- bbox += BoundingBox((last_two_points[-1].x, self.x), last_two_points[-1].y)
-
- def control_points(self, first, prev, prev_prev):
- # type: (Vector2d, Vector2d, Vector2d) -> Generator[Vector2d, None, None]
- yield Vector2d(self.x, prev.y)
-
- def to_relative(self, prev):
- # type: (Vector2d) -> horz
- return horz(self.x - prev.x)
-
- def to_non_shorthand(self, prev, prev_control):
- # type: (Vector2d, Vector2d) -> Line
- return self.to_line(prev)
-
- def transform(self, transform):
- # type: (Pathlike, Transform) -> Pathlike
- raise ValueError("Horizontal lines can't be transformed directly.")
-
- def end_point(self, first, prev):
- # type: (Vector2d, Vector2d) -> Vector2d
- return Vector2d(self.x, prev.y)
-
- def to_curve(self, prev, prev_prev=Vector2d()):
- # type: (Vector2d, Optional[Vector2d]) -> Curve
- """Convert a horizontal line into a curve"""
- return self.to_line(prev).to_curve(prev)
-
- def to_line(self, prev):
- # type: (Vector2d) -> Line
- """Return this path command as a Line instead"""
- return Line(self.x, prev.y)
-
- def reverse(self, first, prev):
- return Horz(prev.x)
-
-
-class horz(RelativePathCommand): # pylint: disable=invalid-name
- """Relative horz line segment"""
-
- nargs = 1
-
- @property
- def args(self):
- return (self.dx,)
-
- def __init__(self, dx):
- self.dx = dx
-
- def to_absolute(self, prev): # type: (Vector2d) -> Horz
- return Horz(prev.x + self.dx)
-
- def to_non_shorthand(self, prev, prev_control):
- # type: (Vector2d, Vector2d) -> Line
- return self.to_line(prev)
-
- def to_line(self, prev): # type: (Vector2d) -> Line
- """Return this path command as a Line instead"""
- return Line(prev.x + self.dx, prev.y)
-
- def reverse(self, first, prev):
- return horz(-self.dx)
-
-
-class Vert(AbsolutePathCommand):
- """Vertical Line segment"""
-
- nargs = 1
-
- @property
- def args(self):
- return (self.y,)
-
- def __init__(self, y):
- self.y = y
-
- def update_bounding_box(self, first, last_two_points, bbox):
- bbox += BoundingBox(last_two_points[-1].x, (last_two_points[-1].y, self.y))
-
- def transform(self, transform): # type: (Pathlike, Transform) -> Pathlike
- raise ValueError("Vertical lines can't be transformed directly.")
-
- def control_points(self, first, prev, prev_prev):
- # type: (Vector2d, Vector2d, Vector2d) -> Generator[Vector2d, None, None]
- yield Vector2d(prev.x, self.y)
-
- def to_non_shorthand(self, prev, prev_control):
- # type: (Vector2d, Vector2d) -> Line
- return self.to_line(prev)
-
- def to_relative(self, prev):
- # type: (Vector2d) -> vert
- return vert(self.y - prev.y)
-
- def end_point(self, first, prev):
- # type: (Vector2d, Vector2d) -> Vector2d
- return Vector2d(prev.x, self.y)
-
- def to_line(self, prev):
- # type: (Vector2d) -> Line
- """Return this path command as a line instead"""
- return Line(prev.x, self.y)
-
- def to_curve(
- self, prev, prev_prev=Vector2d()
- ): # type: (Vector2d, Optional[Vector2d]) -> Curve
- """Convert a horizontal line into a curve"""
- return self.to_line(prev).to_curve(prev)
-
- def reverse(self, first, prev):
- return Vert(prev.y)
-
-
-class vert(RelativePathCommand): # pylint: disable=invalid-name
- """Relative vertical line segment"""
-
- nargs = 1
-
- @property
- def args(self):
- return (self.dy,)
-
- def __init__(self, dy):
- self.dy = dy
-
- def to_absolute(self, prev): # type: (Vector2d) -> Vert
- return Vert(prev.y + self.dy)
-
- def to_non_shorthand(self, prev, prev_control):
- # type: (Vector2d, Vector2d) -> Line
- return self.to_line(prev)
-
- def to_line(self, prev): # type: (Vector2d) -> Line
- """Return this path command as a line instead"""
- return Line(prev.x, prev.y + self.dy)
-
- def reverse(self, first, prev):
- return vert(-self.dy)
-
-
-class Curve(AbsolutePathCommand):
- """Absolute Curved Line segment"""
-
- nargs = 6
-
- @property
- def args(self):
- return self.x2, self.y2, self.x3, self.y3, self.x4, self.y4
-
- def __init__(self, x2, y2, x3, y3, x4, y4): # pylint: disable=too-many-arguments
- self.x2 = x2
- self.y2 = y2
-
- self.x3 = x3
- self.y3 = y3
-
- self.x4 = x4
- self.y4 = y4
-
- def update_bounding_box(self, first, last_two_points, bbox):
- x1, x2, x3, x4 = last_two_points[-1].x, self.x2, self.x3, self.x4
- y1, y2, y3, y4 = last_two_points[-1].y, self.y2, self.y3, self.y4
-
- if not (x1 in bbox.x and x2 in bbox.x and x3 in bbox.x and x4 in bbox.x):
- bbox.x += cubic_extrema(x1, x2, x3, x4)
-
- if not (y1 in bbox.y and y2 in bbox.y and y3 in bbox.y and y4 in bbox.y):
- bbox.y += cubic_extrema(y1, y2, y3, y4)
-
- def transform(self, transform):
- # type: (Transform) -> Curve
- x2, y2 = transform.apply_to_point((self.x2, self.y2))
- x3, y3 = transform.apply_to_point((self.x3, self.y3))
- x4, y4 = transform.apply_to_point((self.x4, self.y4))
- return Curve(x2, y2, x3, y3, x4, y4)
-
- def control_points(self, first, prev, prev_prev):
- # type: (Vector2d, Vector2d, Vector2d) -> Generator[Vector2d, None, None]
- yield Vector2d(self.x2, self.y2)
- yield Vector2d(self.x3, self.y3)
- yield Vector2d(self.x4, self.y4)
-
- def to_relative(self, prev): # type: (Vector2d) -> curve
- return curve(
- self.x2 - prev.x,
- self.y2 - prev.y,
- self.x3 - prev.x,
- self.y3 - prev.y,
- self.x4 - prev.x,
- self.y4 - prev.y,
- )
-
- def end_point(self, first, prev):
- return Vector2d(self.x4, self.y4)
-
- def to_curve(
- self, prev, prev_prev=Vector2d()
- ): # type: (Vector2d, Optional[Vector2d]) -> Curve
- """No conversion needed, pass-through, returns self"""
- return Curve(*self.args)
-
- def to_bez(self):
- """Returns the list of coords for SuperPath"""
- return [list(self.args[:2]), list(self.args[2:4]), list(self.args[4:6])]
-
- def reverse(self, first, prev):
- return Curve(self.x3, self.y3, self.x2, self.y2, prev.x, prev.y)
-
-
-class curve(RelativePathCommand): # pylint: disable=invalid-name
- """Relative curved line segment"""
-
- nargs = 6
-
- @property
- def args(self):
- return self.dx2, self.dy2, self.dx3, self.dy3, self.dx4, self.dy4
-
- def __init__(
- self, dx2, dy2, dx3, dy3, dx4, dy4
- ): # pylint: disable=too-many-arguments
- self.dx2 = dx2
- self.dy2 = dy2
-
- self.dx3 = dx3
- self.dy3 = dy3
-
- self.dx4 = dx4
- self.dy4 = dy4
-
- def to_absolute(self, prev): # type: (Vector2d) -> Curve
- return Curve(
- self.dx2 + prev.x,
- self.dy2 + prev.y,
- self.dx3 + prev.x,
- self.dy3 + prev.y,
- self.dx4 + prev.x,
- self.dy4 + prev.y,
- )
-
- def reverse(self, first, prev):
- return curve(
- -self.dx4 + self.dx3,
- -self.dy4 + self.dy3,
- -self.dx4 + self.dx2,
- -self.dy4 + self.dy2,
- -self.dx4,
- -self.dy4,
- )
-
-
-class Smooth(AbsolutePathCommand):
- """Absolute Smoothed Curved Line segment"""
-
- nargs = 4
-
- @property
- def args(self):
- return self.x3, self.y3, self.x4, self.y4
-
- def __init__(self, x3, y3, x4, y4):
- self.x3 = x3
- self.y3 = y3
-
- self.x4 = x4
- self.y4 = y4
-
- def update_bounding_box(self, first, last_two_points, bbox):
- self.to_curve(last_two_points[-1], last_two_points[-2]).update_bounding_box(
- first, last_two_points, bbox
- )
-
- def control_points(self, first, prev, prev_prev):
- # type: (Vector2d, Vector2d, Vector2d) -> Generator[Vector2d, None, None]
-
- x1, x2, x3, x4 = prev_prev.x, prev.x, self.x3, self.x4
- y1, y2, y3, y4 = prev_prev.y, prev.y, self.y3, self.y4
-
- # infer reflected point
- x2 = 2 * x2 - x1
- y2 = 2 * y2 - y1
-
- yield Vector2d(x2, y2)
- yield Vector2d(x3, y3)
- yield Vector2d(x4, y4)
-
- def to_non_shorthand(self, prev, prev_control):
- # type: (Vector2d, Vector2d) -> Curve
- return self.to_curve(prev, prev_control)
-
- def to_relative(self, prev): # type: (Vector2d) -> smooth
- return smooth(
- self.x3 - prev.x, self.y3 - prev.y, self.x4 - prev.x, self.y4 - prev.y
- )
-
- def transform(self, transform):
- # type: (Transform) -> Smooth
- x3, y3 = transform.apply_to_point((self.x3, self.y3))
- x4, y4 = transform.apply_to_point((self.x4, self.y4))
- return Smooth(x3, y3, x4, y4)
-
- def end_point(self, first, prev):
- return Vector2d(self.x4, self.y4)
-
- def to_curve(self, prev, prev_prev=Vector2d()):
- # type: (Vector2d, Vector2d) -> Curve
- """
- Convert this Smooth curve to a regular curve by creating a mirror
- set of nodes based on the previous node. Previous should be a curve.
- """
- (x2, y2), (x3, y3), (x4, y4) = self.control_points(prev, prev, prev_prev)
- return Curve(x2, y2, x3, y3, x4, y4)
-
- def reverse(self, first, prev):
- return Smooth(self.x3, self.y3, prev.x, prev.y)
-
-
-class smooth(RelativePathCommand): # pylint: disable=invalid-name
- """Relative smoothed curved line segment"""
-
- nargs = 4
-
- @property
- def args(self):
- return self.dx3, self.dy3, self.dx4, self.dy4
-
- def __init__(self, dx3, dy3, dx4, dy4):
- self.dx3 = dx3
- self.dy3 = dy3
-
- self.dx4 = dx4
- self.dy4 = dy4
-
- def to_absolute(self, prev): # type: (Vector2d) -> Smooth
- return Smooth(
- self.dx3 + prev.x, self.dy3 + prev.y, self.dx4 + prev.x, self.dy4 + prev.y
- )
-
- def to_non_shorthand(self, prev, prev_control):
- # type: (Vector2d, Vector2d) -> Curve
- return self.to_absolute(prev).to_non_shorthand(prev, prev_control)
-
- def reverse(self, first, prev):
- return smooth(-self.dx4 + self.dx3, -self.dy4 + self.dy3, -self.dx4, -self.dy4)
-
-
-class Quadratic(AbsolutePathCommand):
- """Absolute Quadratic Curved Line segment"""
-
- nargs = 4
-
- @property
- def args(self):
- return self.x2, self.y2, self.x3, self.y3
-
- def __init__(self, x2, y2, x3, y3):
- self.x2 = x2
- self.y2 = y2
-
- self.x3 = x3
- self.y3 = y3
-
- def update_bounding_box(self, first, last_two_points, bbox):
- x1, x2, x3 = last_two_points[-1].x, self.x2, self.x3
- y1, y2, y3 = last_two_points[-1].y, self.y2, self.y3
-
- if not (x1 in bbox.x and x2 in bbox.x and x3 in bbox.x):
- bbox.x += quadratic_extrema(x1, x2, x3)
-
- if not (y1 in bbox.y and y2 in bbox.y and y3 in bbox.y):
- bbox.y += quadratic_extrema(y1, y2, y3)
-
- def control_points(self, first, prev, prev_prev):
- # type: (Vector2d, Vector2d, Vector2d) -> Generator[Vector2d, None, None]
- yield Vector2d(self.x2, self.y2)
- yield Vector2d(self.x3, self.y3)
-
- def to_relative(self, prev):
- # type: (Vector2d) -> quadratic
- return quadratic(
- self.x2 - prev.x, self.y2 - prev.y, self.x3 - prev.x, self.y3 - prev.y
- )
-
- def transform(self, transform):
- # type: (Transform) -> Quadratic
- x2, y2 = transform.apply_to_point((self.x2, self.y2))
- x3, y3 = transform.apply_to_point((self.x3, self.y3))
- return Quadratic(x2, y2, x3, y3)
-
- def end_point(self, first, prev):
- # type: (Vector2d, Vector2d) -> Vector2d
- return Vector2d(self.x3, self.y3)
-
- def to_curve(self, prev, prev_prev=Vector2d()):
- # type: (Vector2d, Vector2d) -> Curve
- """Attempt to convert a quadratic to a curve"""
- prev = Vector2d(prev)
- x1 = 1.0 / 3 * prev.x + 2.0 / 3 * self.x2
- x2 = 2.0 / 3 * self.x2 + 1.0 / 3 * self.x3
- y1 = 1.0 / 3 * prev.y + 2.0 / 3 * self.y2
- y2 = 2.0 / 3 * self.y2 + 1.0 / 3 * self.y3
- return Curve(x1, y1, x2, y2, self.x3, self.y3)
-
- def reverse(self, first, prev):
- return Quadratic(self.x2, self.y2, prev.x, prev.y)
-
-
-class quadratic(RelativePathCommand): # pylint: disable=invalid-name
- """Relative quadratic line segment"""
-
- nargs = 4
-
- @property
- def args(self):
- return self.dx2, self.dy2, self.dx3, self.dy3
-
- def __init__(self, dx2, dy2, dx3, dy3):
- self.dx2 = dx2
- self.dx3 = dx3
- self.dy2 = dy2
- self.dy3 = dy3
-
- def to_absolute(self, prev): # type: (Vector2d) -> Quadratic
- return Quadratic(
- self.dx2 + prev.x, self.dy2 + prev.y, self.dx3 + prev.x, self.dy3 + prev.y
- )
-
- def reverse(self, first, prev):
- return quadratic(
- -self.dx3 + self.dx2, -self.dy3 + self.dy2, -self.dx3, -self.dy3
- )
-
-
-class TepidQuadratic(AbsolutePathCommand):
- """Continued Quadratic Line segment"""
-
- nargs = 2
-
- @property
- def args(self):
- return self.x3, self.y3
-
- def __init__(self, x3, y3):
- self.x3 = x3
- self.y3 = y3
-
- def update_bounding_box(self, first, last_two_points, bbox):
- self.to_quadratic(last_two_points[-1], last_two_points[-2]).update_bounding_box(
- first, last_two_points, bbox
- )
-
- def control_points(self, first, prev, prev_prev):
- # type: (Vector2d, Vector2d, Vector2d) -> Generator[Vector2d, None, None]
-
- x1, x2, x3 = prev_prev.x, prev.x, self.x3
- y1, y2, y3 = prev_prev.y, prev.y, self.y3
-
- # infer reflected point
- x2 = 2 * x2 - x1
- y2 = 2 * y2 - y1
-
- yield Vector2d(x2, y2)
- yield Vector2d(x3, y3)
-
- def to_non_shorthand(self, prev, prev_control):
- # type: (Vector2d, Vector2d) -> AbsolutePathCommand
- return self.to_quadratic(prev, prev_control)
-
- def to_relative(self, prev): # type: (Vector2d) -> tepidQuadratic
- return tepidQuadratic(self.x3 - prev.x, self.y3 - prev.y)
-
- def transform(self, transform):
- # type: (Transform) -> TepidQuadratic
- x3, y3 = transform.apply_to_point((self.x3, self.y3))
- return TepidQuadratic(x3, y3)
-
- def end_point(self, first, prev):
- # type: (Vector2d, Vector2d) -> Vector2d
- return Vector2d(self.x3, self.y3)
-
- def to_curve(self, prev, prev_prev=Vector2d()):
- # type: (Vector2d, Vector2d) -> Curve
- return self.to_quadratic(prev, prev_prev).to_curve(prev)
-
- def to_quadratic(self, prev, prev_prev):
- # type: (Vector2d, Vector2d) -> Quadratic
- """
- Convert this continued quadratic into a full quadratic
- """
- (x2, y2), (x3, y3) = self.control_points(prev, prev, prev_prev)
- return Quadratic(x2, y2, x3, y3)
-
- def reverse(self, first, prev):
- return TepidQuadratic(prev.x, prev.y)
-
-
-class tepidQuadratic(RelativePathCommand): # pylint: disable=invalid-name
- """Relative continued quadratic line segment"""
-
- nargs = 2
-
- @property
- def args(self):
- return self.dx3, self.dy3
-
- def __init__(self, dx3, dy3):
- self.dx3 = dx3
- self.dy3 = dy3
-
- def to_absolute(self, prev):
- # type: (Vector2d) -> TepidQuadratic
- return TepidQuadratic(self.dx3 + prev.x, self.dy3 + prev.y)
-
- def to_non_shorthand(self, prev, prev_control):
- # type: (Vector2d, Vector2d) -> AbsolutePathCommand
- return self.to_absolute(prev).to_non_shorthand(prev, prev_control)
-
- def reverse(self, first, prev):
- return tepidQuadratic(-self.dx3, -self.dy3)
-
-
-class Arc(AbsolutePathCommand):
- """Special Arc segment"""
-
- nargs = 7
-
- @property
- def args(self):
- return (
- self.rx,
- self.ry,
- self.x_axis_rotation,
- self.large_arc,
- self.sweep,
- self.x,
- self.y,
- )
-
- def __init__(
- self, rx, ry, x_axis_rotation, large_arc, sweep, x, y
- ): # pylint: disable=too-many-arguments
- self.rx = rx
- self.ry = ry
- self.x_axis_rotation = x_axis_rotation
- self.large_arc = large_arc
- self.sweep = sweep
- self.x = x
- self.y = y
-
- def update_bounding_box(self, first, last_two_points, bbox):
- prev = last_two_points[-1]
- for seg in self.to_curves(prev=prev):
- seg.update_bounding_box(first, [None, prev], bbox)
- prev = seg.end_point(first, prev)
-
- def control_points(self, first, prev, prev_prev):
- # type: (Vector2d, Vector2d, Vector2d) -> Generator[Vector2d, None, None]
- yield Vector2d(self.x, self.y)
-
- def to_curves(self, prev, prev_prev=Vector2d()):
- # type: (Vector2d, Vector2d) -> List[Curve]
- """Convert this arc into bezier curves"""
- path = CubicSuperPath([arc_to_path(list(prev), self.args)]).to_path(
- curves_only=True
- )
- # Ignore the first move command from to_path()
- return list(path)[1:]
-
- def transform(self, transform):
- # type: (Transform) -> Arc
- # pylint: disable=invalid-name, too-many-locals
- x_, y_ = transform.apply_to_point((self.x, self.y))
-
- T = transform # type: Transform
- if self.x_axis_rotation != 0:
- T = T @ Transform(rotate=self.x_axis_rotation)
- a, c, b, d, _, _ = list(T.to_hexad())
- # T = | a b |
- # | c d |
-
- detT = a * d - b * c
- detT2 = detT**2
-
- rx = float(self.rx)
- ry = float(self.ry)
-
- if rx == 0.0 or ry == 0.0 or detT2 == 0.0:
- # invalid Arc parameters
- # transform only last point
- return Arc(
- self.rx,
- self.ry,
- self.x_axis_rotation,
- self.large_arc,
- self.sweep,
- x_,
- y_,
- )
-
- A = (d**2 / rx**2 + c**2 / ry**2) / detT2
- B = -(d * b / rx**2 + c * a / ry**2) / detT2
- D = (b**2 / rx**2 + a**2 / ry**2) / detT2
-
- theta = atan2(-2 * B, D - A) / 2
- theta_deg = theta * 180.0 / pi
- DA = D - A
- l2 = 4 * B**2 + DA**2
-
- if l2 == 0:
- delta = 0.0
- else:
- delta = 0.5 * (-(DA**2) - 4 * B**2) / sqrt(l2)
-
- half = (A + D) / 2
-
- rx_ = 1.0 / sqrt(half + delta)
- ry_ = 1.0 / sqrt(half - delta)
-
- x_, y_ = transform.apply_to_point((self.x, self.y))
-
- if detT > 0:
- sweep = self.sweep
- else:
- sweep = 0 if self.sweep > 0 else 1
-
- return Arc(rx_, ry_, theta_deg, self.large_arc, sweep, x_, y_)
-
- def to_relative(self, prev):
- # type: (Vector2d) -> arc
- return arc(
- self.rx,
- self.ry,
- self.x_axis_rotation,
- self.large_arc,
- self.sweep,
- self.x - prev.x,
- self.y - prev.y,
- )
-
- def end_point(self, first, prev):
- # type: (Vector2d, Vector2d) -> Vector2d
- return Vector2d(self.x, self.y)
-
- def reverse(self, first, prev):
- return Arc(
- self.rx,
- self.ry,
- self.x_axis_rotation,
- self.large_arc,
- 1 - self.sweep,
- prev.x,
- prev.y,
- )
-
-
-class arc(RelativePathCommand): # pylint: disable=invalid-name
- """Relative Arc line segment"""
-
- nargs = 7
-
- @property
- def args(self):
- return (
- self.rx,
- self.ry,
- self.x_axis_rotation,
- self.large_arc,
- self.sweep,
- self.dx,
- self.dy,
- )
-
- def __init__(
- self, rx, ry, x_axis_rotation, large_arc, sweep, dx, dy
- ): # pylint: disable=too-many-arguments
- self.rx = rx
- self.ry = ry
- self.x_axis_rotation = x_axis_rotation
- self.large_arc = large_arc
- self.sweep = sweep
- self.dx = dx
- self.dy = dy
-
- def to_absolute(self, prev): # type: (Vector2d) -> "Arc"
- x1, y1 = prev
- return Arc(
- self.rx,
- self.ry,
- self.x_axis_rotation,
- self.large_arc,
- self.sweep,
- self.dx + x1,
- self.dy + y1,
- )
-
- def reverse(self, first, prev):
- return arc(
- self.rx,
- self.ry,
- self.x_axis_rotation,
- self.large_arc,
- 1 - self.sweep,
- -self.dx,
- -self.dy,
- )
-
-
-PathCommand._letter_to_class = { # pylint: disable=protected-access
- "M": Move,
- "L": Line,
- "V": Vert,
- "H": Horz,
- "A": Arc,
- "C": Curve,
- "S": Smooth,
- "Z": ZoneClose,
- "Q": Quadratic,
- "T": TepidQuadratic,
- "m": move,
- "l": line,
- "v": vert,
- "h": horz,
- "a": arc,
- "c": curve,
- "s": smooth,
- "z": zoneClose,
- "q": quadratic,
- "t": tepidQuadratic,
-}
-
-
-class Path(list):
- """A list of segment commands which combine to draw a shape"""
-
- class PathCommandProxy:
- """
- A handy class for Path traverse and coordinate access
-
- Reduces number of arguments in user code compared to bare
- :class:`PathCommand` methods
- """
-
- def __init__(
- self, command, first_point, previous_end_point, prev2_control_point
- ):
- self.command = command # type: PathCommand
- self.first_point = first_point # type: Vector2d
- self.previous_end_point = previous_end_point # type: Vector2d
- self.prev2_control_point = prev2_control_point # type: Vector2d
-
- @property
- def name(self):
- """The full name of the segment (i.e. Line, Arc, etc)"""
- return self.command.name
-
- @property
- def letter(self):
- """The single letter representation of this command (i.e. L, A, etc)"""
- return self.command.letter
-
- @property
- def next_command(self):
- """The implicit next command."""
- return self.command.next_command
-
- @property
- def is_relative(self):
- """Whether the command is defined in relative coordinates, i.e. relative to
- the previous endpoint (lower case path command letter)"""
- return self.command.is_relative
-
- @property
- def is_absolute(self):
- """Whether the command is defined in absolute coordinates (upper case path
- command letter)"""
- return self.command.is_absolute
-
- @property
- def args(self):
- """Returns path command arguments as tuple of floats"""
- return self.command.args
-
- @property
- def control_points(self):
- """Returns list of path command control points"""
- return self.command.control_points(
- self.first_point, self.previous_end_point, self.prev2_control_point
- )
-
- @property
- def end_point(self):
- """Returns last control point of path command"""
- return self.command.end_point(self.first_point, self.previous_end_point)
-
- def reverse(self):
- """Reverse path command"""
- return self.command.reverse(self.end_point, self.previous_end_point)
-
- def to_curve(self):
- """Convert command to :py:class:`Curve`
- Curve().to_curve() returns a copy
- """
- return self.command.to_curve(
- self.previous_end_point, self.prev2_control_point
- )
-
- def to_curves(self):
- """Convert command to list of :py:class:`Curve` commands"""
- return self.command.to_curves(
- self.previous_end_point, self.prev2_control_point
- )
-
- def to_absolute(self):
- """Return relative counterpart for relative commands or copy for absolute"""
- return self.command.to_absolute(self.previous_end_point)
-
- def __str__(self):
- return str(self.command)
-
- def __repr__(self):
- return "<" + self.__class__.__name__ + ">" + repr(self.command)
-
- def __init__(self, path_d=None):
- super().__init__()
- if isinstance(path_d, str):
- # Returns a generator returning PathCommand objects
- path_d = self.parse_string(path_d)
- elif isinstance(path_d, CubicSuperPath):
- path_d = path_d.to_path()
-
- for item in path_d or ():
- if isinstance(item, PathCommand):
- self.append(item)
- elif isinstance(item, (list, tuple)) and len(item) == 2:
- if isinstance(item[1], (list, tuple)):
- self.append(PathCommand.letter_to_class(item[0])(*item[1]))
- else:
- self.append(Line(*item))
- else:
- raise TypeError(
- f"Bad path type: {type(path_d).__name__}"
- f"({type(item).__name__}, ...): {item}"
- )
-
- @classmethod
- def parse_string(cls, path_d):
- """Parse a path string and generate segment objects"""
- for cmd, numbers in LEX_REX.findall(path_d):
- args = list(strargs(numbers))
- cmd = PathCommand.letter_to_class(cmd)
- i = 0
- while i < len(args) or cmd.nargs == 0:
- if len(args[i : i + cmd.nargs]) != cmd.nargs:
- return
- seg = cmd(*args[i : i + cmd.nargs])
- i += cmd.nargs
- cmd = seg.next_command
- yield seg
-
- def bounding_box(self):
- # type: () -> Optional[BoundingBox]
- """Return bounding box of the Path"""
- if not self:
- return None
- iterator = self.proxy_iterator()
- proxy = next(iterator)
- bbox = BoundingBox(proxy.first_point.x, proxy.first_point.y)
- try:
- while True:
- proxy = next(iterator)
- proxy.command.update_bounding_box(
- proxy.first_point,
- [
- proxy.prev2_control_point,
- proxy.previous_end_point,
- ],
- bbox,
- )
- except StopIteration:
- return bbox
-
- def append(self, cmd):
- """Append a command to this path including any chained commands"""
- if isinstance(cmd, list):
- self.extend(cmd)
- elif isinstance(cmd, PathCommand):
- super().append(cmd)
-
- def translate(self, x, y, inplace=False): # pylint: disable=invalid-name
- """Move all coords in this path by the given amount"""
- return self.transform(Transform(translate=(x, y)), inplace=inplace)
-
- def scale(self, x, y, inplace=False): # pylint: disable=invalid-name
- """Scale all coords in this path by the given amounts"""
- return self.transform(Transform(scale=(x, y)), inplace=inplace)
-
- def rotate(self, deg, center=None, inplace=False):
- """Rotate the path around the given point"""
- if center is None:
- # Default center is center of bbox
- bbox = self.bounding_box()
- if bbox:
- center = bbox.center
- else:
- center = Vector2d()
- center = Vector2d(center)
- return self.transform(
- Transform(rotate=(deg, center.x, center.y)), inplace=inplace
- )
-
- @property
- def control_points(self):
- """Returns all control points of the Path"""
- prev = Vector2d()
- prev_prev = Vector2d()
- first = Vector2d()
-
- for seg in self: # type: PathCommand
- cpts = list(seg.control_points(first, prev, prev_prev))
- if isinstance(seg, (zoneClose, ZoneClose, move, Move)):
- first = cpts[-1]
- for cpt in cpts:
- prev_prev = prev
- prev = cpt
- yield cpt
-
- @property
- def end_points(self):
- """Returns all endpoints of all path commands (i.e. the nodes)"""
- prev = Vector2d()
- first = Vector2d()
-
- for seg in self: # type: PathCommand
- end_point = seg.end_point(first, prev)
- if isinstance(seg, (zoneClose, ZoneClose, move, Move)):
- first = end_point
- prev = end_point
- yield end_point
-
- def transform(self, transform, inplace=False):
- """Convert to new path"""
- result = Path()
- previous = Vector2d()
- previous_new = Vector2d()
- start_zone = True
- first = Vector2d()
- first_new = Vector2d()
-
- for i, seg in enumerate(self): # type: PathCommand
- if start_zone:
- first = seg.end_point(first, previous)
-
- if isinstance(seg, (horz, Horz, Vert, vert)):
- seg = seg.to_line(previous)
-
- if seg.is_relative:
- new_seg = (
- seg.to_absolute(previous)
- .transform(transform)
- .to_relative(previous_new)
- )
- else:
- new_seg = seg.transform(transform)
-
- if start_zone:
- first_new = new_seg.end_point(first_new, previous_new)
-
- if inplace:
- self[i] = new_seg
- else:
- result.append(new_seg)
- previous = seg.end_point(first, previous)
- previous_new = new_seg.end_point(first_new, previous_new)
- start_zone = isinstance(seg, (zoneClose, ZoneClose))
- if inplace:
- return self
- return result
-
- def reverse(self):
- """Returns a reversed path"""
- result = Path()
- *_, first = self.end_points
- closer = None
-
- # Go through the path in reverse order
- for index, prcom in reversed(list(enumerate(self.proxy_iterator()))):
- if isinstance(prcom.command, (Move, move, ZoneClose, zoneClose)):
- if closer is not None:
- if len(result) > 0 and isinstance(
- result[-1], (Line, line, Vert, vert, Horz, horz)
- ):
- result.pop() # We can replace simple lines with Z
- result.append(closer) # replace with same type (rel or abs)
- if isinstance(prcom.command, (ZoneClose, zoneClose)):
- closer = prcom.command
- else:
- closer = None
-
- if index == 0:
- if prcom.letter == "M":
- result.insert(0, Move(first.x, first.y))
- elif prcom.letter == "m":
- result.insert(0, move(first.x, first.y))
- else:
- result.append(prcom.reverse())
-
- return result
-
- def break_apart(self) -> List[Path]:
- """Breaks apart a path into its subpaths
-
- .. versionadded:: 1.3"""
- result = [Path()]
- current = result[0]
-
- for cmnd in self.proxy_iterator():
- if cmnd.letter.lower() == "m":
- current = Path()
- result.append(current)
- current.append(Move(*cmnd.end_point))
- else:
- current.append(cmnd.command)
- # Remove all subpaths that are empty or only contain move commands
- return [
- i
- for i in result
- if len(i) != 0 and not all(j.letter.lower() == "m" for j in i)
- ]
-
- def close(self):
- """Attempt to close the last path segment"""
- if self and not isinstance(self[-1], (zoneClose, ZoneClose)):
- self.append(ZoneClose())
-
- def proxy_iterator(self):
- """
- Yields :py:class:`AugmentedPathIterator`
-
- :rtype: Iterator[ Path.PathCommandProxy ]
- """
-
- previous = Vector2d()
- prev_prev = Vector2d()
- first = Vector2d()
-
- for seg in self: # type: PathCommand#
- if isinstance(seg, (zoneClose, ZoneClose, move, Move)):
- first = seg.end_point(first, previous)
- yield Path.PathCommandProxy(seg, first, previous, prev_prev)
- if isinstance(
- seg,
- (
- curve,
- tepidQuadratic,
- quadratic,
- smooth,
- Curve,
- TepidQuadratic,
- Quadratic,
- Smooth,
- ),
- ):
- prev_prev = list(seg.control_points(first, previous, prev_prev))[-2]
- previous = seg.end_point(first, previous)
-
- def to_absolute(self):
- """Convert this path to use only absolute coordinates"""
- return self._to_absolute(True)
-
- def to_non_shorthand(self):
- # type: () -> Path
- """Convert this path to use only absolute non-shorthand coordinates
-
- .. versionadded:: 1.1"""
- return self._to_absolute(False)
-
- def _to_absolute(self, shorthand: bool) -> Path:
- """Make entire Path absolute.
-
- Args:
- shorthand (bool): If false, then convert all shorthand commands to
- non-shorthand.
-
- Returns:
- Path: the input path, converted to absolute coordinates.
- """
-
- abspath = Path()
-
- previous = Vector2d()
- first = Vector2d()
-
- for seg in self: # type: PathCommand
- if isinstance(seg, (move, Move)):
- first = seg.end_point(first, previous)
-
- if shorthand:
- abspath.append(seg.to_absolute(previous))
- else:
- if abspath and isinstance(abspath[-1], (Curve, Quadratic)):
- prev_control = list(
- abspath[-1].control_points(Vector2d(), Vector2d(), Vector2d())
- )[-2]
- else:
- prev_control = previous
-
- abspath.append(seg.to_non_shorthand(previous, prev_control))
-
- previous = seg.end_point(first, previous)
-
- return abspath
-
- def to_relative(self):
- """Convert this path to use only relative coordinates"""
- abspath = Path()
-
- previous = Vector2d()
- first = Vector2d()
-
- for seg in self: # type: PathCommand
- if isinstance(seg, (move, Move)):
- first = seg.end_point(first, previous)
-
- abspath.append(seg.to_relative(previous))
- previous = seg.end_point(first, previous)
-
- return abspath
-
- def __str__(self):
- return " ".join([str(seg) for seg in self])
-
- def __add__(self, other):
- acopy = copy.deepcopy(self)
- if isinstance(other, str):
- other = Path(other)
- if isinstance(other, list):
- acopy.extend(other)
- return acopy
-
- def to_arrays(self):
- """Returns path in format of parsePath output, returning arrays of absolute
- command data
-
- .. deprecated:: 1.0
- This is compatibility function for older API. Should not be used in new code
-
- """
- return [[seg.letter, list(seg.args)] for seg in self.to_non_shorthand()]
-
- def to_superpath(self):
- """Convert this path into a cubic super path"""
- return CubicSuperPath(self)
-
- def copy(self):
- """Make a copy"""
- return copy.deepcopy(self)
-
-
-class CubicSuperPath(list):
- """
- A conversion of a path into a predictable list of cubic curves which
- can be operated on as a list of simplified instructions.
-
- When converting back into a path, all lines, arcs etc will be converted
- to curve instructions.
-
- Structure is held as [SubPath[(point_a, bezier, point_b), ...]], ...]
- """
-
- def __init__(self, items):
- super().__init__()
- self._closed = True
- self._prev = Vector2d()
- self._prev_prev = Vector2d()
-
- if isinstance(items, str):
- items = Path(items)
-
- if isinstance(items, Path):
- items = items.to_absolute()
-
- for item in items:
- self.append(item)
-
- def __str__(self):
- return str(self.to_path())
-
- def append(self, item, force_shift=False):
- """Accept multiple different formats for the data
-
- .. versionchanged:: 1.2
- ``force_shift`` parameter has been added
- """
- if isinstance(item, list) and len(item) == 2 and isinstance(item[0], str):
- item = PathCommand.letter_to_class(item[0])(*item[1])
- coordinate_shift = True
- if isinstance(item, list) and len(item) == 3 and not force_shift:
- coordinate_shift = False
- is_quadratic = False
- if isinstance(item, PathCommand):
- if isinstance(item, Move):
- if self._closed is False:
- super().append([])
- item = [list(item.args), list(item.args), list(item.args)]
- elif isinstance(item, ZoneClose) and self and self[-1]:
- # This duplicates the first segment to 'close' the path, it's appended
- # directly because we don't want to last coord to change for the final
- # segment.
- self[-1].append(
- [self[-1][0][0][:], self[-1][0][1][:], self[-1][0][2][:]]
- )
- # Then adds a new subpath for the next shape (if any)
- self._closed = True
- self._prev.assign(self._first)
- return
- elif isinstance(item, Arc):
- # Arcs are made up of three curves (approximated)
- for arc_curve in item.to_curves(self._prev, self._prev_prev):
- x2, y2, x3, y3, x4, y4 = arc_curve.args
- self.append([[x2, y2], [x3, y3], [x4, y4]], force_shift=True)
- self._prev_prev.assign(x3, y3)
- return
- else:
- is_quadratic = isinstance(
- item, (Quadratic, TepidQuadratic, quadratic, tepidQuadratic)
- )
- if isinstance(item, (Horz, Vert)):
- item = item.to_line(self._prev)
- prp = self._prev_prev
- if is_quadratic:
- self._prev_prev = list(
- item.control_points(self._first, self._prev, prp)
- )[-2:-1][0]
- item = item.to_curve(self._prev, prp)
-
- if isinstance(item, Curve):
- # Curves are cut into three tuples for the super path.
- item = item.to_bez()
-
- if not isinstance(item, list):
- raise ValueError(f"Unknown super curve item type: {item}")
-
- if len(item) != 3 or not all(len(bit) == 2 for bit in item):
- # The item is already a subpath (usually from some other process)
- if len(item[0]) == 3 and all(len(bit) == 2 for bit in item[0]):
- super().append(self._clean(item))
- self._prev_prev = Vector2d(self[-1][-1][0])
- self._prev = Vector2d(self[-1][-1][1])
- return
- raise ValueError(f"Unknown super curve list format: {item}")
-
- if self._closed:
- # Closed means that the previous segment is closed so we need a new one
- # We always append to the last open segment. CSP starts out closed.
- self._closed = False
- super().append([])
-
- if coordinate_shift:
- if self[-1]:
- # The last tuple is replaced, it's the coords of where the next segment
- # will land.
- self[-1][-1][-1] = item[0][:]
- # The last coord is duplicated, but is expected to be replaced
- self[-1].append(item[1:] + copy.deepcopy(item)[-1:])
- else:
- # Item is already a csp segment and has already been shifted.
- self[-1].append(copy.deepcopy(item))
-
- self._prev = Vector2d(self[-1][-1][1])
- if not is_quadratic:
- self._prev_prev = Vector2d(self[-1][-1][0])
-
- def _clean(self, lst):
- """Recursively clean lists so they have the same type"""
- if isinstance(lst, (tuple, list)):
- return [self._clean(child) for child in lst]
- return lst
-
- @property
- def _first(self):
- try:
- return Vector2d(self[-1][0][0])
- except IndexError:
- return Vector2d()
-
- def to_path(self, curves_only=False, rtol=1e-5, atol=1e-8):
- """Convert the super path back to an svg path
-
- Arguments: see :func:`to_segments` for parameters"""
- return Path(list(self.to_segments(curves_only, rtol, atol)))
-
- def to_segments(self, curves_only=False, rtol=1e-5, atol=1e-8):
- """Generate a set of segments for this cubic super path
-
- Arguments:
- curves_only (bool, optional): If False, curves that can be represented
- by Lineto / ZoneClose commands, will be. Defaults to False.
- rtol (float, optional): relative tolerance, passed to :func:`is_line` and
- :func:`inkex.transforms.ImmutableVector2d.is_close` for checking if a
- line can be replaced by a ZoneClose command. Defaults to 1e-5.
-
- .. versionadded:: 1.2
- atol: absolute tolerance, passed to :func:`is_line` and
- :func:`inkex.transforms.ImmutableVector2d.is_close`. Defaults to 1e-8.
-
- .. versionadded:: 1.2"""
- for subpath in self:
- previous = []
- for segment in subpath:
- if not previous:
- yield Move(*segment[1][:])
- elif self.is_line(previous, segment, rtol, atol) and not curves_only:
- if segment is subpath[-1] and Vector2d(segment[1]).is_close(
- subpath[0][1], rtol, atol
- ):
- yield ZoneClose()
- else:
- yield Line(*segment[1][:])
- else:
- yield Curve(*(previous[2][:] + segment[0][:] + segment[1][:]))
- previous = segment
-
- def transform(self, transform):
- """Apply a transformation matrix to this super path"""
- return self.to_path().transform(transform).to_superpath()
-
- @staticmethod
- def is_on(pt_a, pt_b, pt_c, tol=1e-8):
- """Checks if point pt_a is on the line between points pt_b and pt_c
-
- .. versionadded:: 1.2"""
- return CubicSuperPath.collinear(pt_a, pt_b, pt_c, tol) and (
- CubicSuperPath.within(pt_a[0], pt_b[0], pt_c[0])
- if pt_a[0] != pt_b[0]
- else CubicSuperPath.within(pt_a[1], pt_b[1], pt_c[1])
- )
-
- @staticmethod
- def collinear(pt_a, pt_b, pt_c, tol=1e-8):
- """Checks if points pt_a, pt_b, pt_c lie on the same line,
- i.e. that the cross product (b-a) x (c-a) < tol
-
- .. versionadded:: 1.2"""
- return (
- abs(
- (pt_b[0] - pt_a[0]) * (pt_c[1] - pt_a[1])
- - (pt_c[0] - pt_a[0]) * (pt_b[1] - pt_a[1])
- )
- < tol
- )
-
- @staticmethod
- def within(val_b, val_a, val_c):
- """Checks if float val_b is between val_a and val_c
-
- .. versionadded:: 1.2"""
- return val_a <= val_b <= val_c or val_c <= val_b <= val_a
-
- @staticmethod
- def is_line(previous, segment, rtol=1e-5, atol=1e-8):
- """Check whether csp segment (two points) can be expressed as a line has retracted handles or the handles
- can be retracted without loss of information (i.e. both handles lie on the
- line)
-
- .. versionchanged:: 1.2
- Previously, it was only checked if both control points have retracted
- handles. Now it is also checked if the handles can be retracted without
- (visible) loss of information (i.e. both handles lie on the line connecting
- the nodes).
-
- Arguments:
- previous: first node in superpath notation
- segment: second node in superpath notation
- rtol (float, optional): relative tolerance, passed to
- :func:`inkex.transforms.ImmutableVector2d.is_close` for checking handle
- retraction. Defaults to 1e-5.
-
- .. versionadded:: 1.2
- atol (float, optional): absolute tolerance, passed to
- :func:`inkex.transforms.ImmutableVector2d.is_close` for checking handle
- retraction and
- :func:`inkex.paths.CubicSuperPath.is_on` for checking if all points
- (nodes + handles) lie on a line. Defaults to 1e-8.
-
- .. versionadded:: 1.2
- """
-
- retracted = Vector2d(previous[1]).is_close(
- previous[2], rtol, atol
- ) and Vector2d(segment[0]).is_close(segment[1], rtol, atol)
-
- if retracted:
- return True
-
- # Can both handles be retracted without loss of information?
- # Definitely the case if the handles lie on the same line as the two nodes and
- # in the correct order
- # E.g. cspbezsplitatlength outputs non-retracted handles when splitting a
- # straight line
- return CubicSuperPath.is_on(
- segment[0], segment[1], previous[2], atol
- ) and CubicSuperPath.is_on(previous[2], previous[1], segment[0], atol)
-
-
-def arc_to_path(point, params):
- """Approximates an arc with cubic bezier segments.
-
- Arguments:
- point: Starting point (absolute coords)
- params: Arcs parameters as per
- https://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
-
- Returns a list of triplets of points :
- [control_point_before, node, control_point_after]
- (first and last returned triplets are [p1, p1, *] and [*, p2, p2])
- """
-
- # pylint: disable=invalid-name, too-many-locals
- A = point[:]
- rx, ry, teta, longflag, sweepflag, x2, y2 = params[:]
- teta = teta * pi / 180.0
- B = [x2, y2]
- # Degenerate ellipse
- if rx == 0 or ry == 0 or A == B:
- return [[A[:], A[:], A[:]], [B[:], B[:], B[:]]]
-
- # turn coordinates so that the ellipse morph into a *unit circle* (not 0-centered)
- mat = matprod((rotmat(teta), [[1.0 / rx, 0.0], [0.0, 1.0 / ry]], rotmat(-teta)))
- applymat(mat, A)
- applymat(mat, B)
-
- k = [-(B[1] - A[1]), B[0] - A[0]]
- d = k[0] * k[0] + k[1] * k[1]
- k[0] /= sqrt(d)
- k[1] /= sqrt(d)
- d = sqrt(max(0, 1 - d / 4.0))
- # k is the unit normal to AB vector, pointing to center O
- # d is distance from center to AB segment (distance from O to the midpoint of AB)
- # for the last line, remember this is a unit circle, and kd vector is ortogonal to
- # AB (Pythagorean thm)
-
- if longflag == sweepflag:
- # top-right ellipse in SVG example
- # https://www.w3.org/TR/SVG/images/paths/arcs02.svg
- d *= -1
-
- O = [(B[0] + A[0]) / 2.0 + d * k[0], (B[1] + A[1]) / 2.0 + d * k[1]]
- OA = [A[0] - O[0], A[1] - O[1]]
- OB = [B[0] - O[0], B[1] - O[1]]
- start = acos(OA[0] / norm(OA))
- if OA[1] < 0:
- start *= -1
- end = acos(OB[0] / norm(OB))
- if OB[1] < 0:
- end *= -1
- # start and end are the angles from center of the circle to A and to B respectively
-
- if sweepflag and start > end:
- end += 2 * pi
- if (not sweepflag) and start < end:
- end -= 2 * pi
-
- NbSectors = int(abs(start - end) * 2 / pi) + 1
- dTeta = (end - start) / NbSectors
- v = 4 * tan(dTeta / 4.0) / 3.0
- # I would use v = tan(dTeta/2)*4*(sqrt(2)-1)/3 ?
- p = []
- for i in range(0, NbSectors + 1, 1):
- angle = start + i * dTeta
- v1 = [
- O[0] + cos(angle) - (-v) * sin(angle),
- O[1] + sin(angle) + (-v) * cos(angle),
- ]
- pt = [O[0] + cos(angle), O[1] + sin(angle)]
- v2 = [O[0] + cos(angle) - v * sin(angle), O[1] + sin(angle) + v * cos(angle)]
- p.append([v1, pt, v2])
- p[0][0] = p[0][1][:]
- p[-1][2] = p[-1][1][:]
-
- # go back to the original coordinate system
- mat = matprod((rotmat(teta), [[rx, 0], [0, ry]], rotmat(-teta)))
- for pts in p:
- applymat(mat, pts[0])
- applymat(mat, pts[1])
- applymat(mat, pts[2])
- return p
-
-
-def matprod(mlist):
- """Get the product of the mat"""
- prod = mlist[0]
- for mat in mlist[1:]:
- a00 = prod[0][0] * mat[0][0] + prod[0][1] * mat[1][0]
- a01 = prod[0][0] * mat[0][1] + prod[0][1] * mat[1][1]
- a10 = prod[1][0] * mat[0][0] + prod[1][1] * mat[1][0]
- a11 = prod[1][0] * mat[0][1] + prod[1][1] * mat[1][1]
- prod = [[a00, a01], [a10, a11]]
- return prod
-
-
-def rotmat(teta):
- """Rotate the mat"""
- return [[cos(teta), -sin(teta)], [sin(teta), cos(teta)]]
-
-
-def applymat(mat, point):
- """Apply the given mat"""
- x = mat[0][0] * point[0] + mat[0][1] * point[1]
- y = mat[1][0] * point[0] + mat[1][1] * point[1]
- point[0] = x
- point[1] = y
-
-
-def norm(point):
- """Normalise"""
- return sqrt(point[0] * point[0] + point[1] * point[1])
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/ports.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/ports.py
deleted file mode 100644
index 7595231..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/ports.py
+++ /dev/null
@@ -1,105 +0,0 @@
-# coding=utf-8
-#
-# Copyright (C) 2019 Martin Owens
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-"""
-Common access to serial and other computer ports.
-"""
-
-import os
-import sys
-import time
-from .utils import DependencyError, AbortExtension
-
-try:
- import serial
- from serial.tools import list_ports
-except ImportError:
- serial = None
-
-
-class Serial:
- """
- Attempt to get access to the computer's serial port.
-
- with Serial(port_name, ...) as com:
- com.write(...)
-
- Provides access to the debug/testing ports which are pretend ports
- able to accept the same input but allow for debugging.
- """
-
- def __init__(self, port, baud=9600, timeout=0.1, **options):
- self.test = port == "[test]"
- if self.test:
- import pty # This does not work on windows #pylint: disable=import-outside-toplevel
-
- self.controller, self.peripheral = pty.openpty()
- port = os.ttyname(self.peripheral)
-
- self.has_serial()
- self.com = serial.Serial()
- self.com.port = port
- self.com.baudrate = int(baud)
- self.com.timeout = timeout
- self.set_options(**options)
-
- def set_options(self, stop=1, size=8, flow=None, parity=None):
- """Set further options on the serial port"""
- size = {5: "five", 6: "six", 7: "seven", 8: "eight"}.get(size, size)
- stop = {"onepointfive": 1.5}.get(stop.lower(), stop)
- stop = {1: "one", 1.5: "one_point_five", 2: "two"}.get(stop, stop)
- self.com.bytesize = getattr(serial, str(str(size).upper()) + "BITS")
- self.com.stopbits = getattr(serial, "STOPBITS_" + str(stop).upper())
- self.com.parity = getattr(serial, "PARITY_" + str(parity).upper())
- # set flow control
- self.com.xonxoff = flow == "xonxoff"
- self.com.rtscts = flow in ("rtscts", "dsrdtrrtscts")
- self.com.dsrdtr = flow == "dsrdtrrtscts"
-
- def __enter__(self):
- try:
- # try to establish connection
- self.com.open()
- except serial.SerialException as error:
- raise AbortExtension(
- "Could not open serial port. Please check your device"
- " is running, connected and the settings are correct"
- ) from error
- return self.com
-
- def __exit__(self, exc, value, traceback):
- if not traceback and self.test:
- output = " " * 1024
- while len(output) == 1024:
- time.sleep(0.01)
- output = os.read(self.controller, 1024)
- sys.stderr.write(output.decode("utf8"))
- # self.com.read(2)
- self.com.close()
-
- @staticmethod
- def has_serial():
- """Late importing of pySerial module"""
- if serial is None:
- raise DependencyError("pySerial is required to open serial ports.")
-
- @staticmethod
- def list_ports():
- """Return a list of available serial ports"""
- Serial.has_serial() # Cause DependencyError error
- return [hw.name for hw in list_ports.comports(True)]
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/properties.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/properties.py
deleted file mode 100644
index 0ec0ab6..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/properties.py
+++ /dev/null
@@ -1,850 +0,0 @@
-# coding=utf-8
-#
-# Copyright (C) 2021 Jonathan Neuhauser, jonathan.neuhauser@outlook.com
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-"""
-Property management and parsing, CSS cascading, default value storage
-
-.. versionadded:: 1.2
-
-.. data:: all_properties
-
- A list of all properties, their parser class, and additional information
- such as whether they are inheritable or can be given as presentation attributes
-"""
-
-from abc import ABC, abstractmethod
-
-import re
-from typing import Tuple, Dict, Type, Union, List, Optional
-from .interfaces.IElement import IBaseElement, ISVGDocumentElement
-
-from .units import parse_unit, convert_unit
-
-from .colors import Color, ColorError
-
-
-class BaseStyleValue:
- """A class encapsuling a single CSS declaration / presentation_attribute"""
-
- def __init__(self, declaration=None, attr_name=None, value=None, important=False):
- self.attr_name: str
- self.value: str
- self.important: bool
- if declaration is not None and ":" in declaration:
- (
- self.attr_name,
- self.value,
- self.important,
- ) = BaseStyleValue.parse_declaration(declaration)
- elif attr_name is not None:
- self.attr_name = attr_name.strip().lower()
- if isinstance(value, str):
- self.value = value.strip()
- else:
- # maybe its already parsed? then set it
- self.value = self.unparse_value(value)
- self.important = important
- _ = self.parse_value() # check that we can parse this value
-
- @classmethod
- def parse_declaration(cls, declaration: str) -> Tuple[str, str, bool]:
- """Parse a single css declaration
-
- Args:
- declaration (str): a css declaration such as:
- ``fill: #000 !important;``. The trailing semicolon may be ommitted.
-
- Raises:
- ValueError: Unable to parse the declaration
-
- Returns:
- Tuple[str, str, bool]: a tuple with key, value and importance
- """
- if declaration != "" and ":" in declaration:
- declaration = declaration.replace(";", "")
- (name, value) = declaration.split(":", 1)
- # check whether this is an important declaration
- important = False
- if "!important" in value:
- value = value.replace("!important", "")
- important = True
- return (name.strip().lower(), value.strip(), important)
- raise ValueError("Invalid declaration")
-
- def parse_value(self, element=None):
- """Get parsed property value with resolved urls, color, etc.
-
- Args:
- element (BaseElement): the SVG element to which this style is applied to
- currently used for resolving gradients / masks, could be used for
- computing percentage attributes or calc() attributes [optional]
-
- Returns:
- object: parsed property value
- """
- if self.value == "inherit":
- if self.attr_name in all_properties:
- return self._parse_value(all_properties[self.attr_name][1], element)
- return None
- return self._parse_value(self.value, element)
-
- def _parse_value( # pylint: disable=unused-argument, no-self-use
- self, value: str, element=None
- ) -> object:
- """internal parse method, to be overwritten by derived classes
-
- Args:
- value (str): unparsed value
- element (BaseElement): the SVG element to which this style is applied to
- [optional]
-
- Returns:
- object: the parsed value
- """
- return value
-
- def unparse_value(self, value: object) -> str:
- """ "Unparses" an object, i.e. converts an object back to an attribute.
- If the result is invalid, i.e. can't be parsed, an exception is raised.
-
- Args:
- value (object): the object to be unparsed
-
- Returns:
- str: the attribute value
- """
- result = self._unparse_value(value)
- self._parse_value(result) # check if value can be parsed (value is valid)
- return result
-
- def _unparse_value(self, value: object) -> str: # pylint: disable=no-self-use
- return str(value)
-
- @property
- def declaration(self) -> str:
- """The css declaration corresponding to the StyleValue object
-
- Returns:
- str: the css declaration, such as "fill: #000 !important;"
- """
- return (
- self.attr_name
- + ":"
- + self.value
- + (" !important" if self.important else "")
- )
-
- @classmethod
- def factory(
- cls,
- declaration: Optional[str] = None,
- attr_name: Optional[str] = None,
- value: Optional[object] = None,
- important: Optional[bool] = False,
- ):
- """Create an attribute
-
- Args:
- declaration (str, optional): the CSS declaration to parse. Defaults to None.
- attr_name (str, optional): the attribute name. Defaults to None.
- value (object, optional): the attribute value. Defaults to None.
- important (bool, optional): whether the attribute is marked !important.
- Defaults to False.
-
- Raises:
- Errors may also be raised on parsing, so make sure to handle them
-
- Returns:
- BaseStyleValue: the parsed style
- """
- if declaration is not None and ":" in declaration:
- attr_name, value, important = BaseStyleValue.parse_declaration(declaration)
- elif attr_name is not None and value is not None:
- attr_name = attr_name.strip().lower()
- if isinstance(value, str):
- value = value.strip()
-
- if attr_name in all_properties:
- valuetype = all_properties[attr_name][0]
- return valuetype(declaration, attr_name, value, important)
- return BaseStyleValue(declaration, attr_name, value, important)
-
- def __eq__(self, other):
- if not (isinstance(other, BaseStyleValue)):
- return False
- if self.declaration != other.declaration:
- return False
- return True
-
- @staticmethod
- def factory_errorhandled(element=None, declaration="", key="", value=""):
- """Error handling for the factory method: if something goes wrong during
- parsing, ignore the attribute
-
- Args:
- element (BaseElement, optional): The element this declaration is affecting,
- for finding gradients ect. Defaults to None.
- declaration (str, optional): the CSS declaration to parse. Defaults to "".
- key (str, optional): the attribute name. Defaults to "".
- value (str, optional): the attribute value. Defaults to "".
-
- Returns:
- BaseStyleValue: The parsed style
- """
- try:
- value = BaseStyleValue.factory(
- declaration=declaration, attr_name=key, value=value
- )
- key = value.attr_name
- # Try to parse the attribute
- _ = value.parse_value(element)
- return (key, value)
- except ValueError:
- # something went wrong during parsing, e.g. a bad attribute format
- # or an attribute referencing a missing gradient
- # -> ignore this declaration
- pass
- except ColorError:
- # The color parsing methods have their own error type
- pass
-
-
-class AlphaValue(BaseStyleValue):
- """Stores an alpha value (such as opacity), which may be specified as
- as percentage or absolute value.
-
- Reference: https://www.w3.org/TR/css-color/#typedef-alpha-value"""
-
- def _parse_value(self, value: str, element=None):
- if value[-1] == "%": # percentage
- parsed_value = float(value[:-1]) * 0.01
- else:
- parsed_value = float(value)
- if parsed_value < 0:
- return 0
- if parsed_value > 1:
- return 1
- return parsed_value
-
- def _unparse_value(self, value: object) -> str:
- if isinstance(value, (float, int)):
- if value < 0:
- return "0"
- if value > 1:
- return "1"
- return str(value)
- raise ValueError("Value must be number")
-
-
-class ColorValue(BaseStyleValue):
- """Stores a color value
-
- Reference: https://drafts.csswg.org/css-color-3/#valuea-def-color"""
-
- def _parse_value(self, value: str, element=None):
- if value == "currentColor":
- if element is not None:
- style = element.specified_style()
- return style("color")
- return None
- return Color(value)
-
-
-# https://www.w3.org/TR/css3-values/#url-value
-# matches anything inside url() enclosed with single/double quotes
-# (technically a fragment url) or no quotes at all
-URLREGEX = r"url\(\s*('.*?'|\".*?\"|[^\"'].*?)\s*\)"
-
-
-def match_url_and_return_element(string: str, svg):
- """Parses a string containing an url, e.g. "url(#rect1234)",
- looks up the element in the svg document and returns it.
-
- Args:
- string (str): the string to parse
- svg (SvgDocumentElement): document referenced in the URL
-
- Raises:
- ValueError: if the string has invalid format
- ValueError: if the referenced element is not found
-
- Returns:
- BaseElement: the referenced element
- """
- regex = re.compile(URLREGEX)
- match = regex.match(string)
- if match:
- url = match.group(1)
- paint_server = svg.getElementById(url)
- return paint_server
- raise ValueError("invalid URL format")
-
-
-class URLNoneValue(BaseStyleValue):
- """Stores a value that is either none or an url, such as markers or masks.
-
- Reference: https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties"""
-
- def _parse_value(self, value: str, element=None):
- if value == "none":
- return None
- if value[0:4] == "url(":
- if element is not None and self.element_has_root(element):
- return match_url_and_return_element(value, element.root)
- return None
- raise ValueError("Invalid property value")
-
- def _unparse_value(self, value: object):
- if isinstance(value, IBaseElement):
- return f"url(#{value.get_id()})"
- return super()._unparse_value(value)
-
- @staticmethod
- def element_has_root(element) -> bool:
- "Checks if an element has a root, i.e. if element.root will fail"
-
- return not (
- element.getparent() is None and not isinstance(element, ISVGDocumentElement)
- )
-
-
-class PaintValue(ColorValue, URLNoneValue):
- """Stores a paint value (such as fill and stroke), which may be specified
- as color, or url.
-
- Reference: https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint"""
-
- def _parse_value(self, value: str, element=None):
- if value == "none":
- return None
- if value in ["context-fill", "context-stroke"]:
- return value
- if value == "currentColor":
- return super()._parse_value(value, element)
- if value[0:4] == "url(":
- # First part: fragment url
- # second part: a fallback color if the url is not found. Colors start with
- # a letter or a #
- regex = re.compile(URLREGEX + r"\s*([#\w].*?)?$")
- match = regex.match(value)
- if match:
- url = match.group(1)
- if element is not None and self.element_has_root(element):
- paint_server = element.root.getElementById(url)
- else:
- return None
- if paint_server is not None:
- return paint_server
- if match.group(2) is None:
- raise ValueError("Paint server not found")
- return Color(match.group(2))
- return Color(value)
-
- def _unparse_value(self, value: object):
- if value is None:
- return "none"
- return super()._unparse_value(value)
-
-
-class EnumValue(BaseStyleValue):
- """Stores a value that can only have a finite set of options"""
-
- def __init__(self, declaration=None, attr_name=None, value=None, important=False):
- self.valueset = all_properties[attr_name][4]
- super().__init__(declaration, attr_name, value, important)
-
- def _parse_value(self, value: str, element=None):
- if value in self.valueset:
- return value
- raise ValueError(
- f"Value '{value}' is invalid for the property {self.attr_name}. "
- + f"Allowed values are: {self.valueset + ['inherit']}"
- )
-
-
-class ShorthandValue(BaseStyleValue, ABC):
- """Stores a value that sets other values (e.g. the font shorthand)"""
-
- def apply_shorthand(self, style):
- """Applies a shorthand attribute to its style, respecting the
- importance state of the individual attributes.
-
- Args:
- style (Style): the style that the shorthand attribute is contained in,
- and that the shorthand attribute will be applied on
- """
- if self.attr_name not in style:
- return
-
- dct = self.get_shorthand_changes()
- importance = self.important
-
- # they are ordered in the order of adding the style elements.
- current_keys = list(style.keys())
- for curkey in dct:
- perform = False
- if curkey not in current_keys:
- # this is the easiest case, just set the element and the importance
- perform = True
- else:
- if importance != style.get_importance(curkey):
- # different importance, result independent of position
- perform = importance
- else:
- # only apply if style overwrites previous with same importance
- perform = current_keys.index(curkey) < current_keys.index(
- self.attr_name
- )
-
- if perform:
- style[curkey] = dct[curkey]
- style.set_importance(curkey, importance)
- style.pop(self.attr_name)
-
- @abstractmethod
- def get_shorthand_changes(self) -> Dict[str, str]:
- """calculates the value of affected attributes for this shorthand
-
- Returns:
- Dict[str, str]: a dictionary containing the new values of the
- affected attributes
- """
-
-
-class FontValue(ShorthandValue):
- """Logic for the shorthand font property"""
-
- def get_shorthand_changes(self):
- keys = [
- "font-style",
- "font-variant",
- "font-weight",
- "font-stretch",
- "font-size",
- "line-height",
- "font-family",
- ]
- options = {
- key: all_properties[key][4]
- for key in keys
- if isinstance(all_properties[key][4], list)
- }
- # Font stretch can be specified in percent, but for the
- # shorthand, only a keyword value is allowed
- options["font-stretch"] = (
- "normal",
- "ultra-condensed",
- "extra-condensed",
- "condensed",
- "semi-condensed",
- "semi-expanded",
- "expanded",
- "extra-expanded",
- "ultra-expanded",
- )
- result = {key: all_properties[key][1] for key in keys}
-
- tokens = [i for i in self.value.split() if i != ""]
-
- if len(tokens) == 0:
- return {} # shorthand not set, nothing to do
-
- while not (len(tokens) == 0):
- cur = tokens[0]
- if cur in options["font-style"]:
- result["font-style"] = cur
- elif cur in options["font-variant"]:
- result["font-variant"] = cur
- elif cur in options["font-weight"]:
- result["font-weight"] = cur
- elif cur in options["font-stretch"]:
- result["font-stretch"] = cur
- else:
- if "/" in cur:
- result["font-size"], result["line-height"] = cur.split("/")
- else:
- result["font-size"] = cur
- result["font-family"] = " ".join(tokens[1:])
- break
- tokens = tokens[1:] # remove first element
- return result
-
-
-class TextDecorationValue(ShorthandValue):
- """Logic for the shorthand font property
-
- .. versionadded:: 1.3"""
-
- def get_shorthand_changes(self):
- options = {
- "text-decoration-" + key: all_properties["text-decoration-" + key][4]
- for key in ("line", "style", "color")
- if isinstance(all_properties["text-decoration-" + key][4], list)
- }
- result = {
- "text-decoration-style": all_properties["text-decoration-style"][1],
- "text-decoration-color": "currentcolor",
- "text-decoration-line": [],
- }
-
- tokens = [i for i in self.value.split() if i != ""]
-
- if len(tokens) == 0:
- return {} # shorthand not set, nothing to do
-
- for cur in tokens:
- if cur in ["underline", "overline", "line-through", "blink"]:
- result["text-decoration-line"] += [cur]
- elif cur in options["text-decoration-style"]:
- result["text-decoration-style"] = cur
- else:
- result["text-decoration-color"] = cur
-
- if len(result["text-decoration-line"]) == 0:
- result["text-decoration-line"] = all_properties["text-decoration-line"][4]
- else:
- # Text-decoration-line can have multiple values.
- result["text-decoration-line"] = " ".join(result["text-decoration-line"])
-
- return result
-
-
-class MarkerShorthandValue(ShorthandValue, URLNoneValue):
- """Logic for the marker shorthand property"""
-
- def get_shorthand_changes(self):
- if self.value == "":
- return {} # shorthand not set, nothing to do
- return {k: self.value for k in ["marker-start", "marker-end", "marker-mid"]}
-
- def _parse_value(self, value: str, element=None):
- # Make sure the parsing routine doesn't choke on an empty shorthand
- if value == "":
- return ""
- return super()._parse_value(value, element)
-
-
-class FontSizeValue(BaseStyleValue):
- """Logic for the font-size property"""
-
- def _parse_value(self, value: str, element=None):
- if element is None:
- return value # no additional logic in this case
- try:
- return element.to_dimensionless(value)
- except ValueError: # unable to parse font size, e.g. font-size:normal
- return element.to_dimensionless("12")
-
-
-class StrokeDasharrayValue(BaseStyleValue):
- """Logic for the stroke-dasharray property"""
-
- def _parse_value(self, value: str, element=None):
- dashes = re.findall(r"[^,\s]+", value)
- if len(dashes) == 0 or value == "none":
- return None # no dasharray applied
- if not any(parse_unit(i) is None for i in dashes):
- dashes = [convert_unit(i, "px") for i in dashes]
- else:
- return None
- if any(i < 0 for i in dashes):
- return None # one negative value makes the dasharray invalid
- if len(dashes) % 2 == 1:
- dashes = 2 * dashes
- return dashes
-
- def _unparse_value(self, value: object) -> str:
- if value is None:
- return "none"
- if isinstance(value, list):
- return " ".join(map(str, value))
- return str(value)
-
-
-# keys: attributes, right side:
-# - Subclass of BaseStyleValue used for instantiating
-# - default value
-# - is presentation attribute
-# - inherited
-# - additional information, such as valid enum values
-# For properties which have no special implementation yet:
-# "(BaseStyleValue, , , None)"
-
-# Source for this list: https://www.w3.org/TR/SVG2/styling.html#PresentationAttributes
-
-
-all_properties: Dict[
- str, Tuple[Type[BaseStyleValue], str, bool, bool, Union[List[str], None]]
-] = {
- "alignment-baseline": (
- EnumValue,
- "baseline",
- True,
- False,
- [
- "baseline",
- "text-bottom",
- "alphabetic",
- "ideographic",
- "middle",
- "central",
- "mathematical",
- "text-top",
- ],
- ),
- "baseline-shift": (BaseStyleValue, "0", True, False, None),
- "clip": (BaseStyleValue, "auto", True, False, None),
- "clip-path": (URLNoneValue, "none", True, False, None),
- "clip-rule": (EnumValue, "nonzero", True, True, ["nonzero", "evenodd"]),
- # only used for currentColor, which is not yet implemented
- "color": (PaintValue, "black", True, True, None),
- "color-interpolation": (
- EnumValue,
- "sRGB",
- True,
- True,
- ["sRGB", "auto", "linearRGB"],
- ),
- "color-interpolation-filters": (
- EnumValue,
- "linearRGB",
- True,
- True,
- ["auto", "sRGB", "linearRGB"],
- ),
- "color-rendering": (
- EnumValue,
- "auto",
- True,
- True,
- ["auto", "optimizeSpeed", "optimizeQuality"],
- ),
- "cursor": (BaseStyleValue, "auto", True, True, None),
- "direction": (EnumValue, "ltr", True, True, ["ltr", "rtl"]),
- "display": (
- EnumValue,
- "inline",
- True,
- False,
- [
- "inline",
- "block",
- "list-item",
- "inline-block",
- "table",
- "inline-table",
- "table-row-group",
- "table-header-group",
- "table-footer-group",
- "table-row",
- "table-column-group",
- "table-column",
- "table-cell",
- "table-caption",
- "none",
- ],
- ), # every value except none is rendered normally
- "dominant-baseline": (
- EnumValue,
- "auto",
- True,
- True,
- [
- "auto",
- "text-bottom",
- "alphabetic",
- "ideographic",
- "middle",
- "central",
- "mathematical",
- "hanging",
- "text-top",
- ],
- ),
- "fill": (
- PaintValue,
- "black",
- True,
- True,
- None,
- ), # the normal fill, not the one
- "fill-opacity": (AlphaValue, "1", True, True, None),
- "fill-rule": (EnumValue, "nonzero", True, True, ["nonzero", "evenodd"]),
- "filter": (URLNoneValue, "none", True, False, None),
- "flood-color": (PaintValue, "black", True, False, None),
- "flood-opacity": (AlphaValue, "1", True, False, None),
- "font": (FontValue, "", True, False, None),
- "font-family": (BaseStyleValue, "sans-serif", True, True, None),
- "font-size": (FontSizeValue, "medium", True, True, None),
- "font-size-adjust": (BaseStyleValue, "none", True, True, None),
- "font-stretch": (BaseStyleValue, "normal", True, True, None),
- "font-style": (EnumValue, "normal", True, True, ["normal", "italic", "oblique"]),
- # a lot more values and subproperties in SVG2 / CSS-Fonts3
- "font-variant": (EnumValue, "normal", True, True, ["normal", "small-caps"]),
- "font-weight": (
- EnumValue,
- "normal",
- True,
- True,
- ["normal", "bold"] + [str(i) for i in range(100, 901, 100)],
- ),
- "glyph-orientation-horizontal": (BaseStyleValue, "0deg", True, True, None),
- "glyph-orientation-vertical": (BaseStyleValue, "auto", True, True, None),
- "inline-size": (BaseStyleValue, "0", False, False, None),
- "image-rendering": (
- EnumValue,
- "auto",
- True,
- True,
- ["auto", "optimizeQuality", "optimizeSpeed"],
- ),
- "letter-spacing": (BaseStyleValue, "normal", True, True, None),
- "lighting-color": (ColorValue, "normal", True, False, None),
- "line-height": (BaseStyleValue, "normal", False, True, None),
- "marker": (MarkerShorthandValue, "", True, True, None),
- "marker-end": (URLNoneValue, "none", True, True, None),
- "marker-mid": (URLNoneValue, "none", True, True, None),
- "marker-start": (URLNoneValue, "none", True, True, None),
- # is a shorthand for a lot of mask-related properties which Inkscape doesn't support
- "mask": (URLNoneValue, "none", True, False, None),
- "opacity": (AlphaValue, "1", True, False, None),
- "overflow": (
- EnumValue,
- "visible",
- True,
- False,
- ["visible", "hidden", "scroll", "auto"],
- ),
- "paint-order": (BaseStyleValue, "normal", True, False, None),
- "pointer-events": (
- EnumValue,
- "visiblePainted",
- True,
- True,
- [
- "bounding-box",
- "visiblePainted",
- "visibleFill",
- "visibleStroke",
- "visible",
- "painted",
- "fill",
- "stroke",
- "all",
- "none",
- ],
- ),
- "shape-inside": (URLNoneValue, "none", False, False, None),
- "shape-rendering": (
- EnumValue,
- "visiblePainted",
- True,
- True,
- ["auto", "optimizeSpeed", "crispEdges", "geometricPrecision"],
- ),
- "stop-color": (ColorValue, "black", True, False, None),
- "stop-opacity": (AlphaValue, "1", True, False, None),
- "stroke": (PaintValue, "none", True, True, None),
- "stroke-dasharray": (StrokeDasharrayValue, "none", True, True, None),
- "stroke-dashoffset": (BaseStyleValue, "0", True, True, None),
- "stroke-linecap": (EnumValue, "butt", True, True, ["butt", "round", "square"]),
- "stroke-linejoin": (
- EnumValue,
- "miter",
- True,
- True,
- ["miter", "miter-clip", "round", "bevel", "arcs"],
- ),
- "stroke-miterlimit": (BaseStyleValue, "4", True, True, None),
- "stroke-opacity": (AlphaValue, "1", True, True, None),
- "stroke-width": (BaseStyleValue, "1", True, True, None),
- "text-align": (
- BaseStyleValue,
- "start",
- True,
- True,
- None,
- ), # only HTML property, but used by some unit tests
- "text-anchor": (EnumValue, "start", True, True, ["start", "middle", "end"]),
- # shorthand for text-decoration-line, *-style, *-color
- "text-decoration": (TextDecorationValue, "", True, True, None),
- # multiple enum values are allowed
- "text-decoration-line": (BaseStyleValue, "none", False, False, None),
- "text-decoration-style": (
- EnumValue,
- "solid",
- False,
- False,
- ["solid", "double", "dotted", "dashed", "wavy"],
- ),
- # This currently cannot be a ColorValue because currentcolor and other special
- # colors are not supported by the Color class
- "text-decoration-color": (BaseStyleValue, "currentcolor", False, False, None),
- "text-overflow": (EnumValue, "clip", True, False, ["clip", "ellipsis"]),
- "text-rendering": (
- EnumValue,
- "auto",
- True,
- True,
- ["auto", "optimizeSpeed", "optimizeLegibility", "geometricPrecision"],
- ),
- "unicode-bidi": (
- EnumValue,
- "normal",
- True,
- False,
- [
- "normal",
- "embed",
- "isolate",
- "bidi-override",
- "isolate-override",
- "plaintext",
- ],
- ),
- "vector-effect": (BaseStyleValue, "none", True, False, None),
- "vertical-align": (BaseStyleValue, "baseline", False, False, None),
- "visibility": (EnumValue, "visible", True, True, ["visible", "hidden", "collapse"]),
- "white-space": (
- EnumValue,
- "normal",
- True,
- True,
- ["normal", "pre", "nowrap", "pre-wrap", "break-spaces", "pre-line"],
- ),
- "word-spacing": (BaseStyleValue, "normal", True, True, None),
- # including obsolete SVG 1.1 values
- "writing-mode": (
- EnumValue,
- "horizontal-tb",
- True,
- True,
- [
- "horizontal-tb",
- "vertical-rl",
- "vertical-lr",
- "lr",
- "lr-tb",
- "rl",
- "rl-tb",
- "tb",
- "tb-rl",
- ],
- ),
- "-inkscape-font-specification": (BaseStyleValue, "sans-serif", False, True, None),
-}
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/styles.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/styles.py
deleted file mode 100644
index 8f6b04b..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/styles.py
+++ /dev/null
@@ -1,636 +0,0 @@
-# coding=utf-8
-#
-# Copyright (C) 2005 Aaron Spike, aaron@ekips.org
-# 2019-2020 Martin Owens
-# 2021 Jonathan Neuhauser, jonathan.neuhauser@outlook.com
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-"""
-Functions for handling styles and embedded css
-"""
-
-import re
-import sys
-from collections import OrderedDict
-from typing import MutableMapping, Union, Iterable, TYPE_CHECKING
-
-from .interfaces.IElement import IBaseElement
-
-from .colors import Color
-from .properties import BaseStyleValue, all_properties, ShorthandValue
-from .css import ConditionalRule
-
-from .utils import FragmentError
-
-if TYPE_CHECKING:
- from .elements._svg import SvgDocumentElement
-
-
-class Classes(list):
- """A list of classes applied to an element (used in css and js)"""
-
- def __init__(self, classes=None, callback=None):
- self.callback = None
- if isinstance(classes, str):
- classes = classes.split()
- super().__init__(classes or ())
- self.callback = callback
-
- def __str__(self):
- return " ".join(self)
-
- def _callback(self):
- if self.callback is not None:
- self.callback(self)
-
- def __setitem__(self, index, value):
- super().__setitem__(index, value)
- self._callback()
-
- def append(self, value):
- value = str(value)
- if value not in self:
- super().append(value)
- self._callback()
-
- def remove(self, value):
- value = str(value)
- if value in self:
- super().remove(value)
- self._callback()
-
- def toggle(self, value):
- """If exists, remove it, if not, add it"""
- value = str(value)
- if value in self:
- return self.remove(value)
- return self.append(value)
-
-
-class Style(OrderedDict, MutableMapping[str, Union[str, BaseStyleValue]]):
- """A list of style directives
-
- .. versionchanged:: 1.2
- The Style API now allows for access to parsed / processed styles via the
- :func:`call` method.
-
- .. automethod:: __call__
- .. automethod:: __getitem__
- .. automethod:: __setitem__
- """
-
- color_props = ("stroke", "fill", "stop-color", "flood-color", "lighting-color")
- opacity_props = ("stroke-opacity", "fill-opacity", "opacity", "stop-opacity")
- unit_props = "stroke-width"
- """Dictionary of attributes with units.
-
- ..versionadded:: 1.2
- """
- associated_props = {
- "fill": "fill-opacity",
- "stroke": "stroke-opacity",
- "stop-color": "stop-opacity",
- }
- """Dictionary of association between color and opacity attributes.
-
- .. versionadded:: 1.2
- """
-
- def __init__(self, style=None, callback=None, element=None, **kw):
- self.element = element
- # This callback is set twice because this is 'pre-initial' data (no callback)
- self.callback = None
- # Either a string style or kwargs (with dashes as underscores).
- style = style or [(k.replace("_", "-"), v) for k, v in kw.items()]
- if isinstance(style, str):
- style = self._parse_str(style)
- # Order raw dictionaries so tests can be made reliable
- if isinstance(style, dict) and not isinstance(style, OrderedDict):
- style = [(name, style[name]) for name in sorted(style)]
- # Should accept dict, Style, parsed string, list etc.
- super().__init__(style)
- # Now after the initial data, the callback makes sense.
- self.callback = callback
-
- @staticmethod
- def _parse_str(style: str, element=None) -> Iterable[BaseStyleValue]:
- """Create a dictionary from the value of a CSS rule (such as an inline style or
- from an embedded style sheet), including its !important state, parsing the value
- if possible.
-
- Args:
- style: the content of a CSS rule to parse
- element: the element this style is working on (can be the root SVG, is used
- for parsing gradients etc.)
-
- Yields:
- :class:`~inkex.properties.BaseStyleValue`: the parsed attribute
- """
- for declaration in style.split(";"):
- if ":" in declaration:
- result = BaseStyleValue.factory_errorhandled(
- element, declaration=declaration.strip()
- )
- if result is not None:
- yield result
-
- @staticmethod
- def parse_str(style: str, element=None):
- """Parse a style passed as string"""
- return Style(style, element=element)
-
- def __str__(self):
- """Format an inline style attribute from a dictionary"""
- return self.to_str()
-
- def to_str(self, sep=";"):
- """Convert to string using a custom delimiter"""
- return sep.join([self.get_store(key).declaration for key in self])
-
- def __add__(self, other):
- """Add two styles together to get a third, composing them"""
- ret = self.copy()
- ret.update(Style(other))
- return ret
-
- def __iadd__(self, other):
- """Add style to this style, the same as ``style.update(dict)``"""
- self.update(other)
- return self
-
- def __sub__(self, other):
- """Remove keys and return copy"""
- ret = self.copy()
- ret.__isub__(other)
- return ret
-
- def __isub__(self, other):
- """Remove keys from this style, list of keys or other style dictionary"""
- for key in other:
- self.pop(key, None)
- return self
-
- def __ne__(self, other):
- return not self.__eq__(other)
-
- def copy(self):
- """Create a copy of the style.
-
- .. versionadded:: 1.2"""
- ret = Style({}, element=self.element)
- for key, value in super().items():
- ret[key] = value
- return ret
-
- def update(self, other):
- """Update, while respecting ``!important`` declarations."""
- if not isinstance(other, Style):
- other = Style(other)
- # only update
- if isinstance(other, Style):
- for key in other.keys():
- if not (self.get_importance(key) and not other.get_importance(key)):
- self[key] = other.get_store(key)
-
- if self.callback is not None:
- self.callback(self)
-
- def add_inherited(self, parent):
- """Creates a new Style containing all parent styles with importance "!important"
- and current styles with importance "!important"
-
- .. versionadded:: 1.2
-
- Args:
- parent: the parent style that will be merged into this one (will not be
- altered)
-
- Returns:
- Style: the merged Style object
- """
- ret = self.copy()
- ret.apply_shorthands() # parent should already have its shortcuts applied
-
- if not (isinstance(parent, Style)):
- return ret
-
- for key in parent.keys():
- apply = False
- if key in all_properties and all_properties[key][3]:
- # only set parent value if value is not set or parent importance is
- # higher
- if key not in ret:
- apply = True
- elif self.get_importance(key) != parent.get_importance(key):
- apply = parent.get_importance(key)
- if key in ret and ret[key] == "inherit":
- apply = True
- if apply:
- ret[key] = parent[key]
- return ret
-
- def apply_shorthands(self):
- """Apply all shorthands in this style."""
- for element in list(self.values()):
- if isinstance(element, ShorthandValue):
- element.apply_shorthand(self)
-
- def __delitem__(self, key):
- super().__delitem__(key)
- if self.callback is not None:
- self.callback(self)
-
- def pop(self, key, default=None):
- super().pop(key, default)
- # On Python < 3.11, pop internally calls __delitem__.
- # This does not happen in 3.11. To avoid
- # calling the callback twice, we need to check the Python version.
- if sys.version_info >= (3, 11):
- if self.callback is not None:
- self.callback(self)
-
- def __setitem__(self, key, value):
- """Sets a style value.
-
- .. versionchanged:: 1.2
- ``value`` can now also be non-string objects such as a Gradient.
-
- Args:
- key (str): the attribute name
- value (Any):
-
- - a :class:`BaseStyleValue`
- - a string with the value
- - any other object. The :class:`~inkex.properties.BaseStyleValue`
- subclass of the provided key will attempt to create a string out of
- the passed value.
- Raises:
- ValueError: when ``value`` is a :class:`~inkex.properties.BaseStyleValue`
- for a different attribute than `key`
- Error: Other exceptions may be raised when converting non-string objects."""
- if not isinstance(value, BaseStyleValue) or value is None:
- # try to convert the value using the factory
- value = BaseStyleValue.factory(attr_name=key, value=value)
- # check if the set attribute is valid
- _ = value.parse_value(self.element)
- elif key != value.attr_name:
- raise ValueError(
- """You're trying to save a value into a style attribute, but the
- provided key is different from the attribute name given in the value"""
- )
- super().__setitem__(key, value)
- if self.callback is not None:
- self.callback(self)
-
- def __getitem__(self, key):
- """Returns the unparsed value of the element (minus a possible ``!important``)
-
- .. versionchanged:: 1.2
- ``!important`` is removed from the value.
- """
- return self.get_store(key).value
-
- def get(self, key, default=None):
- if key in self:
- return self.__getitem__(key)
- return default
-
- def get_store(self, key):
- """Gets the :class:`~inkex.properties.BaseStyleValue` of this key, since the
- other interfaces - :func:`__getitem__` and :func:`__call__` - return the
- original and parsed value, respectively.
-
- .. versionadded:: 1.2
-
- Args:
- key (str): the attribute name
-
- Returns:
- BaseStyleValue: the BaseStyleValue struct of this attribute
- """
- return super().__getitem__(key)
-
- def __call__(self, key, element=None, default=None):
- """Return the parsed value of a style. Optionally, an element can be passed
- that will be used to find gradient definitions etc.
-
- .. versionadded:: 1.2"""
- # check if there are shorthand properties defined. If so, apply them to a copy
- copy = self
- for value in super().values():
- if isinstance(value, ShorthandValue):
- copy = self.copy()
- copy.apply_shorthands()
- if key in copy:
- return copy.get_store(key).parse_value(element or self.element)
- # style is not set, return the default value
- if key in all_properties or default is not None:
- defvalue = BaseStyleValue.factory(
- attr_name=key, value=default or all_properties[key][1]
- )
- return (
- defvalue.parse_value()
- ) # default values are independent of the element
- raise KeyError("Unknown attribute")
-
- def __eq__(self, other):
- if not isinstance(other, Style):
- other = Style(other)
- if self.keys() != other.keys():
- return False
- for arg in set(self) | set(other):
- if self.get_store(arg) != other.get_store(arg):
- return False
- return True
-
- def items(self):
- """The styles's parsed items
-
- .. versionadded:: 1.2"""
- for key, value in super().items():
- yield key, value.value
-
- def get_importance(self, key, default=False):
- """Returns whether the declaration with ``key`` is marked as ``!important``
-
- .. versionadded:: 1.2"""
- if key in self:
- return super().__getitem__(key).important
- return default
-
- def set_importance(self, key, importance):
- """Sets the ``!important`` state of a declaration with key ``key``
-
- .. versionadded:: 1.2"""
- if key in self:
- super().__getitem__(key).important = importance
- else:
- raise KeyError()
- if self.callback is not None:
- self.callback(self)
-
- def get_color(self, name="fill"):
- """Get the color AND opacity as one Color object"""
- color = Color(self.get(name, "none"))
- return color.to_rgba(self.get(name + "-opacity", 1.0))
-
- def set_color(self, color, name="fill"):
- """Sets the given color AND opacity as rgba to the fill or stroke style
- properties."""
- color = Color(color)
- if color.space == "rgba" and name in Style.associated_props:
- self[Style.associated_props[name]] = color.alpha
- self[name] = color.to_rgb()
- else:
- self[name] = color
-
- def update_urls(self, old_id, new_id):
- """Find urls in this style and replace them with the new id"""
- for name, value in self.items():
- if value == f"url(#{old_id})":
- self[name] = f"url(#{new_id})"
-
- def interpolate(self, other, fraction):
- # type: (Style, Style, float) -> Style
- """Interpolate all properties.
-
- .. versionadded:: 1.1"""
- from .tween import StyleInterpolator
- from inkex.elements import PathElement
-
- if self.element is None:
- self.element = PathElement(style=str(self))
- if other.element is None:
- other.element = PathElement(style=str(other))
- return StyleInterpolator(self.element, other.element).interpolate(fraction)
-
- @classmethod
- def cascaded_style(cls, element):
- """Returns the cascaded style of an element (all rules that apply the element
- itself), based on the stylesheets, the presentation attributes and the inline
- style using the respective specificity of the style
-
- see https://www.w3.org/TR/CSS22/cascade.html#cascading-order
-
- .. versionadded:: 1.2
-
- Args:
- element (BaseElement): the element that the cascaded style will be
- computed for
-
- Returns:
- Style: the cascaded style
- """
- try:
- styles = list(element.root.stylesheets.lookup_specificity(element.get_id()))
- except FragmentError:
- styles = []
-
- # presentation attributes have specificity 0,
- # see https://www.w3.org/TR/SVG/styling.html#PresentationAttributes
- styles.append([element.presentation_style(), (0, 0, 0)])
-
- # would be (1, 0, 0, 0), but then we'd have to extend every entry
- styles.append([element.style, (float("inf"), 0, 0)])
-
- # sort styles by specificity (ascending, so when overwriting it's correct)
- styles = sorted(styles, key=lambda item: item[1])
-
- result = styles[0][0].copy()
- for style, _ in styles[1:]:
- result.update(style)
- result.element = element
- return result
-
- @classmethod
- def specified_style(cls, element):
- """Returns the specified style of an element, i.e. the cascaded style +
- inheritance, see https://www.w3.org/TR/CSS22/cascade.html#specified-value
-
- .. versionadded:: 1.2
-
- Args:
- element (BaseElement): the element that the specified style will be computed
- for
-
- Returns:
- Style: the specified style
- """
-
- # We currently dont treat the case where parent=absolute value and
- # element=relative value, i.e. specified = relative * absolute.
- cascaded = Style.cascaded_style(element)
-
- parent = element.getparent()
-
- if parent is not None and isinstance(parent, IBaseElement):
- cascaded = Style.add_inherited(cascaded, parent.specified_style())
- cascaded.element = element
- return cascaded # doesn't have a parent
-
-
-class StyleSheets(list):
- """
- Special mechanism which contains all the stylesheets for an svg document
- while also caching lookups for specific elements.
-
- This caching is needed because data can't be attached to elements as they are
- re-created on the fly by lxml so lookups have to be centralised.
- """
-
- def __init__(self, svg=None):
- super().__init__()
- self.svg = svg
-
- def lookup(self, element_id, svg=None):
- """
- Find all styles for this element.
- """
- # This is aweful, but required because we can't know for sure
- # what might have changed in the xml tree.
- if svg is None:
- svg = self.svg
- for sheet in self:
- for style in sheet.lookup(element_id, svg=svg):
- yield style
-
- def lookup_specificity(self, element_id, svg=None):
- """
- Find all styles for this element and return the specificity of the match.
-
- .. versionadded:: 1.2
- """
- # This is aweful, but required because we can't know for sure
- # what might have changed in the xml tree.
- if svg is None:
- svg = self.svg
- for sheet in self:
- for style in sheet.lookup_specificity(element_id, svg=svg):
- yield style
-
-
-class StyleSheet(list):
- """
- A style sheet, usually the CDATA contents of a style tag, but also
- a css file used with a css. Will yield multiple Style() classes.
- """
-
- comment_strip = re.compile(r"(\/\/.*?\n)|(\/\*.*?\*\/|@import .*;)")
-
- def __init__(self, content=None, callback=None):
- super().__init__()
- self.callback = None
- # Remove comments
- content = self.comment_strip.sub("", (content or ""))
- # Parse rules
- for block in content.split("}"):
- if block:
- self.append(block)
- self.callback = callback
-
- def __str__(self):
- return "\n" + "\n".join([str(style) for style in self]) + "\n"
-
- def _callback(self, style=None): # pylint: disable=unused-argument
- if self.callback is not None:
- self.callback(self)
-
- def add(self, rule, style):
- """Append a rule and style combo to this stylesheet"""
- self.append(
- ConditionalStyle(rules=rule, style=str(style), callback=self._callback)
- )
-
- def append(self, other):
- """Make sure callback is called when updating"""
- if isinstance(other, str):
- if "{" not in other:
- return # Warning?
- rules, style = other.strip("}").split("{", 1)
- if rules.strip().startswith("@"): # ignore @font-face and @import
- return
- other = ConditionalStyle(
- rules=rules, style=style.strip(), callback=self._callback
- )
- super().append(other)
- self._callback()
-
- def lookup(self, element_id, svg):
- """Lookup the element_id against all the styles in this sheet"""
- for style in self:
- for elem in svg.xpath(style.to_xpath()):
- if elem.get("id", None) == element_id:
- yield style
-
- def lookup_specificity(self, element_id, svg):
- """Lookup the element_id against all the styles in this sheet
- and return the specificity of the match
-
- Args:
- element_id (str): the id of the element that styles are being queried for
- svg (SvgDocumentElement): The document that contains both element and the
- styles
-
- Yields:
- Tuple[ConditionalStyle, Tuple[int, int, int]]: all matched styles and the
- specificity of the match
- """
- for style in self:
- for rule, spec in zip(style.to_xpaths(), style.get_specificities()):
- for elem in svg.xpath(rule):
- if elem.get("id", None) == element_id:
- yield (style, spec)
-
-
-class ConditionalStyle(Style):
- """
- Just like a Style object, but includes one or more
- conditional rules which places this style in a stylesheet
- rather than being an attribute style.
- """
-
- def __init__(self, rules="*", style=None, callback=None, **kwargs):
- super().__init__(style=style, callback=callback, **kwargs)
- self.rules = [ConditionalRule(rule) for rule in rules.split(",")]
-
- def __str__(self):
- """Return this style as a css entry with class"""
- content = self.to_str(";\n ")
- rules = ",\n".join(str(rule) for rule in self.rules)
- if content:
- return f"{rules} {{\n {content};\n}}"
- return f"{rules} {{}}"
-
- def to_xpath(self):
- """Convert all rules to an xpath"""
- # This can be converted to cssselect.CSSSelector (lxml.cssselect) later if we
- # have coverage problems. The main reason we're not is that cssselect is doing
- # exactly this xpath transform and provides no extra functionality for reverse
- # lookups.
- return "|".join(self.to_xpaths())
-
- def to_xpaths(self):
- """Gets a list of xpaths for all rules of this ConditionalStyle
-
- .. versionadded:: 1.2"""
- return [rule.to_xpath() for rule in self.rules]
-
- def get_specificities(self):
- """Gets an iterator of the specificity of all rules in this ConditionalStyle
-
- .. versionadded:: 1.2"""
- for rule in self.rules:
- yield rule.get_specificity()
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/__init__.py
deleted file mode 100644
index 4c3d4fa..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/__init__.py
+++ /dev/null
@@ -1,454 +0,0 @@
-# coding=utf-8
-#
-# Copyright (C) 2018-2019 Martin Owens
-# 2019 Thomas Holder
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA.
-#
-"""
-Testing module. See :ref:`unittests` for details.
-"""
-
-import os
-import re
-import sys
-import shutil
-import tempfile
-import hashlib
-import random
-import uuid
-import io
-from typing import List, Union, Tuple, Type, TYPE_CHECKING
-
-from io import BytesIO, StringIO
-import xml.etree.ElementTree as xml
-
-from unittest import TestCase as BaseCase
-from inkex.base import InkscapeExtension
-
-from .. import Transform, load_svg, SvgDocumentElement
-from ..utils import to_bytes
-from .xmldiff import xmldiff
-from .mock import MockCommandMixin, Capture
-
-if TYPE_CHECKING:
- from .filters import Compare
-
-COMPARE_DELETE, COMPARE_CHECK, COMPARE_WRITE, COMPARE_OVERWRITE = range(4)
-
-
-class NoExtension(InkscapeExtension): # pylint: disable=too-few-public-methods
- """Test case must specify 'self.effect_class' to assertEffect."""
-
- def __init__(self, *args, **kwargs): # pylint: disable=super-init-not-called
- raise NotImplementedError(self.__doc__)
-
- def run(self, args=None, output=None):
- """Fake run"""
-
-
-class TestCase(MockCommandMixin, BaseCase):
- """
- Base class for all effects tests, provides access to data_files and
- test_without_parameters
- """
-
- effect_class = NoExtension # type: Type[InkscapeExtension]
- effect_name = property(lambda self: self.effect_class.__module__)
-
- # If set to true, the output is not expected to be the stdout SVG document, but
- # rather text or a message sent to the stderr, this is highly weird. But sometimes
- # happens.
- stderr_output = False
- stdout_protect = True
- stderr_protect = True
-
- def __init__(self, *args, **kw):
- super().__init__(*args, **kw)
- self._temp_dir = None
- self._effect = None
-
- def setUp(self): # pylint: disable=invalid-name
- """Make sure every test is seeded the same way"""
- self._effect = None
- super().setUp()
- random.seed(0x35F)
-
- def tearDown(self):
- super().tearDown()
- if self._temp_dir and os.path.isdir(self._temp_dir):
- shutil.rmtree(self._temp_dir)
-
- @classmethod
- def __file__(cls):
- """Create a __file__ property which acts much like the module version"""
- return os.path.abspath(sys.modules[cls.__module__].__file__)
-
- @classmethod
- def _testdir(cls):
- """Get's the folder where the test exists (so data can be found)"""
- return os.path.dirname(cls.__file__())
-
- @classmethod
- def rootdir(cls):
- """Return the full path to the extensions directory"""
- return os.path.dirname(cls._testdir())
-
- @classmethod
- def datadir(cls):
- """Get the data directory (can be over-ridden if needed)"""
- return os.path.join(cls._testdir(), "data")
-
- @property
- def tempdir(self):
- """Generate a temporary location to store files"""
- if self._temp_dir is None:
- self._temp_dir = os.path.realpath(tempfile.mkdtemp(prefix="inkex-tests-"))
- if not os.path.isdir(self._temp_dir):
- raise IOError("The temporary directory has disappeared!")
- return self._temp_dir
-
- def temp_file(
- self, prefix="file-", template="{prefix}{name}{suffix}", suffix=".tmp"
- ):
- """Generate the filename of a temporary file"""
- filename = template.format(prefix=prefix, suffix=suffix, name=uuid.uuid4().hex)
- return os.path.join(self.tempdir, filename)
-
- @classmethod
- def data_file(cls, filename, *parts, check_exists=True):
- """Provide a data file from a filename, can accept directories as arguments.
-
- .. versionchanged:: 1.2
- ``check_exists`` parameter added"""
- if os.path.isabs(filename):
- # Absolute root was passed in, so we trust that (it might be a tempdir)
- full_path = os.path.join(filename, *parts)
- else:
- # Otherwise we assume it's relative to the test data dir.
- full_path = os.path.join(cls.datadir(), filename, *parts)
-
- if not os.path.isfile(full_path) and check_exists:
- raise IOError(f"Can't find test data file: {full_path}")
- return full_path
-
- @property
- def empty_svg(self):
- """Returns a common minimal svg file"""
- return self.data_file("svg", "default-inkscape-SVG.svg")
-
- def assertAlmostTuple(
- self, found, expected, precision=8, msg=""
- ): # pylint: disable=invalid-name
- """
- Floating point results may vary with computer architecture; use
- assertAlmostEqual to allow a tolerance in the result.
- """
- self.assertEqual(len(found), len(expected), msg)
- for fon, exp in zip(found, expected):
- self.assertAlmostEqual(fon, exp, precision, msg)
-
- def assertEffectEmpty(self, effect, **kwargs): # pylint: disable=invalid-name
- """Assert calling effect without any arguments"""
- self.assertEffect(effect=effect, **kwargs)
-
- def assertEffect(self, *filename, **kwargs): # pylint: disable=invalid-name
- """Assert an effect, capturing the output to stdout.
-
- filename should point to a starting svg document, default is empty_svg
- """
- if filename:
- data_file = self.data_file(*filename)
- else:
- data_file = self.empty_svg
-
- os.environ["DOCUMENT_PATH"] = data_file
- args = [data_file] + list(kwargs.pop("args", []))
- args += [f"--{kw[0]}={kw[1]}" for kw in kwargs.items()]
-
- effect = kwargs.pop("effect", self.effect_class)()
-
- # Output is redirected to this string io buffer
- if self.stderr_output:
- with Capture("stderr") as stderr:
- effect.run(args, output=BytesIO())
- effect.test_output = stderr
- else:
- output = BytesIO()
- with Capture(
- "stdout", kwargs.get("stdout_protect", self.stdout_protect)
- ) as stdout:
- with Capture(
- "stderr", kwargs.get("stderr_protect", self.stderr_protect)
- ) as stderr:
- effect.run(args, output=output)
- self.assertEqual(
- "", stdout.getvalue(), "Extra print statements detected"
- )
- self.assertEqual(
- "", stderr.getvalue(), "Extra error or warnings detected"
- )
- effect.test_output = output
-
- if os.environ.get("FAIL_ON_DEPRECATION", False):
- warnings = getattr(effect, "warned_about", set())
- effect.warned_about = set() # reset for next test
- self.assertFalse(warnings, "Deprecated API is still being used!")
-
- return effect
-
- # pylint: disable=invalid-name
- def assertDeepAlmostEqual(self, first, second, places=None, msg=None, delta=None):
- """Asserts that two objects, possible nested lists, are almost equal."""
- if delta is None and places is None:
- places = 7
- if isinstance(first, (list, tuple)):
- assert len(first) == len(second)
- for f, s in zip(first, second):
- self.assertDeepAlmostEqual(f, s, places, msg, delta)
- else:
- self.assertAlmostEqual(first, second, places, msg, delta)
-
- def assertTransformEqual(self, lhs, rhs, places=7):
- """Assert that two transform expressions evaluate to the same
- transformation matrix.
-
- .. versionadded:: 1.1
- """
- self.assertAlmostTuple(
- tuple(Transform(lhs).to_hexad()), tuple(Transform(rhs).to_hexad()), places
- )
-
- # pylint: enable=invalid-name
-
- @property
- def effect(self):
- """Generate an effect object"""
- if self._effect is None:
- self._effect = self.effect_class()
- return self._effect
-
- def import_string(self, string, *args) -> SvgDocumentElement:
- """Runs a string through an import extension, with optional arguments
- provided as "--arg=value" arguments"""
- stream = io.BytesIO(string.encode())
- reader = self.effect_class()
- out = io.BytesIO()
- reader.parse_arguments([*args])
- reader.options.input_file = stream
- reader.options.output = out
- reader.load_raw()
- reader.save_raw(reader.effect())
- out.seek(0)
- decoded = out.read().decode("utf-8")
- document = load_svg(decoded)
- return document
-
-
-class InkscapeExtensionTestMixin:
- """Automatically setup self.effect for each test and test with an empty svg"""
-
- def setUp(self): # pylint: disable=invalid-name
- """Check if there is an effect_class set and create self.effect if it is"""
- super().setUp()
- if self.effect_class is None:
- self.skipTest("self.effect_class is not defined for this this test")
-
- def test_default_settings(self):
- """Extension works with empty svg file"""
- self.effect.run([self.empty_svg])
-
-
-class ComparisonMixin:
- """
- Add comparison tests to any existing test suite.
- """
-
- compare_file: Union[List[str], Tuple[str], str] = "svg/shapes.svg"
- """This input svg file sent to the extension (if any)"""
-
- compare_filters = [] # type: List[Compare]
- """The ways in which the output is filtered for comparision (see filters.py)"""
-
- compare_filter_save = False
- """If true, the filtered output will be saved and only applied to the
- extension output (and not to the reference file)"""
-
- comparisons = [
- (),
- ("--id=p1", "--id=r3"),
- ]
- """A list of comparison runs, each entry will cause the extension to be run."""
-
- compare_file_extension = "svg"
-
- @property
- def _compare_file_extension(self):
- """The default extension to use when outputting check files in COMPARE_CHECK
- mode."""
- if self.stderr_output:
- return "txt"
- return self.compare_file_extension
-
- def test_all_comparisons(self):
- """Testing all comparisons"""
- if not isinstance(self.compare_file, (list, tuple)):
- self._test_comparisons(self.compare_file)
- else:
- for compare_file in self.compare_file:
- self._test_comparisons(
- compare_file, addout=os.path.basename(compare_file)
- )
-
- def _test_comparisons(self, compare_file, addout=None):
- for args in self.comparisons:
- self.assertCompare(
- compare_file,
- self.get_compare_cmpfile(args, addout),
- args,
- )
-
- def assertCompare(
- self, infile, cmpfile, args, outfile=None
- ): # pylint: disable=invalid-name
- """
- Compare the output of a previous run against this one.
-
- Args:
- infile: The filename of the pre-processed svg (or other type of file)
- cmpfile: The filename of the data we expect to get, if not set
- the filename will be generated from the effect name and kwargs.
- args: All the arguments to be passed to the effect run
- outfile: Optional, instead of returning a regular output, this extension
- dumps it's output to this filename instead.
-
- """
- compare_mode = int(os.environ.get("EXPORT_COMPARE", COMPARE_DELETE))
-
- effect = self.assertEffect(infile, args=args)
-
- if cmpfile is None:
- cmpfile = self.get_compare_cmpfile(args)
-
- if not os.path.isfile(cmpfile) and compare_mode == COMPARE_DELETE:
- raise IOError(
- f"Comparison file {cmpfile} not found, set EXPORT_COMPARE=1 to create "
- "it."
- )
-
- if outfile:
- if not os.path.isabs(outfile):
- outfile = os.path.join(self.tempdir, outfile)
- self.assertTrue(
- os.path.isfile(outfile), f"No output file created! {outfile}"
- )
- with open(outfile, "rb") as fhl:
- data_a = fhl.read()
- else:
- data_a = effect.test_output.getvalue()
-
- write_output = None
- if compare_mode == COMPARE_CHECK:
- _file = cmpfile[:-4] if cmpfile.endswith(".out") else cmpfile
- write_output = f"{_file}.{self._compare_file_extension}"
- elif (
- compare_mode == COMPARE_WRITE and not os.path.isfile(cmpfile)
- ) or compare_mode == COMPARE_OVERWRITE:
- write_output = cmpfile
-
- try:
- if write_output and not os.path.isfile(cmpfile):
- raise AssertionError(f"Check the output: {write_output}")
- with open(cmpfile, "rb") as fhl:
- data_b = self._apply_compare_filters(fhl.read(), False)
- self._base_compare(data_a, data_b, compare_mode)
- except AssertionError:
- if write_output:
- if isinstance(data_a, str):
- data_a = data_a.encode("utf-8")
- with open(write_output, "wb") as fhl:
- fhl.write(self._apply_compare_filters(data_a, True))
- print(f"Written output: {write_output}")
- # This only reruns if the original test failed.
- # The idea here is to make sure the new output file is "stable"
- # Because some tests can produce random changes and we don't
- # want test authors to be too reassured by a simple write.
- if write_output == cmpfile:
- effect = self.assertEffect(infile, args=args)
- self._base_compare(data_a, cmpfile, COMPARE_CHECK)
- if not write_output == cmpfile:
- raise
-
- def _base_compare(self, data_a, data_b, compare_mode):
- data_a = self._apply_compare_filters(data_a)
-
- if (
- isinstance(data_a, bytes)
- and isinstance(data_b, bytes)
- and data_a.startswith(b"<")
- and data_b.startswith(b"<")
- ):
- # Late importing
- diff_xml, delta = xmldiff(data_a, data_b)
- if not delta and compare_mode == COMPARE_DELETE:
- print(
- "The XML is different, you can save the output using the "
- "EXPORT_COMPARE envionment variable. Set it to 1 to save a file "
- "you can check, set it to 3 to overwrite this comparison, setting "
- "the new data as the correct one.\n"
- )
- diff = "SVG Differences\n\n"
- if os.environ.get("XML_DIFF", False):
- diff = "<- " + diff_xml
- else:
- for x, (value_a, value_b) in enumerate(delta):
- try:
- # Take advantage of better text diff in testcase's own asserts.
- self.assertEqual(value_a, value_b)
- except AssertionError as err:
- diff += f" {x}. {str(err)}\n"
- self.assertTrue(delta, diff)
- else:
- # compare any content (non svg)
- self.assertEqual(data_a, data_b)
-
- def _apply_compare_filters(self, data, is_saving=None):
- data = to_bytes(data)
- # Applying filters flips depending if we are saving the filtered content
- # to disk, or filtering during the test run. This is because some filters
- # are destructive others are useful for diagnostics.
- if is_saving is self.compare_filter_save or is_saving is None:
- for cfilter in self.compare_filters:
- data = cfilter(data)
- return data
-
- def get_compare_cmpfile(self, args, addout=None):
- """Generate an output file for the arguments given"""
- if addout is not None:
- args = list(args) + [str(addout)]
- opstr = (
- "__".join(args)
- .replace(self.tempdir, "TMP_DIR")
- .replace(self.datadir(), "DAT_DIR")
- )
- opstr = re.sub(r"[^\w-]", "__", opstr)
- if opstr:
- if len(opstr) > 127:
- # avoid filename-too-long error
- opstr = hashlib.md5(opstr.encode("latin1")).hexdigest()
- opstr = "__" + opstr
- return self.data_file(
- "refs", f"{self.effect_name}{opstr}.out", check_exists=False
- )
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/decorators.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/decorators.py
deleted file mode 100644
index f27e638..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/decorators.py
+++ /dev/null
@@ -1,9 +0,0 @@
-"""
-Useful decorators for tests.
-"""
-import pytest
-from inkex.command import is_inkscape_available
-
-requires_inkscape = pytest.mark.skipif( # pylint: disable=invalid-name
- not is_inkscape_available(), reason="Test requires inkscape, but it's not available"
-)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/filters.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/filters.py
deleted file mode 100644
index 281adfc..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/filters.py
+++ /dev/null
@@ -1,180 +0,0 @@
-#
-# Copyright (C) 2019 Thomas Holder
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# pylint: disable=too-few-public-methods
-#
-"""
-Comparison filters for use with the ComparisonMixin.
-
-Each filter should be initialised in the list of
-filters that are being used.
-
-.. code-block:: python
-
- compare_filters = [
- CompareNumericFuzzy(),
- CompareOrderIndependentLines(option=yes),
- ]
-
-"""
-
-import re
-from ..utils import to_bytes
-
-
-class Compare:
- """
- Comparison base class, this acts as a passthrough unless
- the filter staticmethod is overwritten.
- """
-
- def __init__(self, **options):
- self.options = options
-
- def __call__(self, content):
- return self.filter(content)
-
- @staticmethod
- def filter(contents):
- """Replace this filter method with your own filtering"""
- return contents
-
-
-class CompareNumericFuzzy(Compare):
- """
- Turn all numbers into shorter standard formats
-
- 1.2345678 -> 1.2346
- 1.2300 -> 1.23, 50.0000 -> 50.0
- 50.0 -> 50
- """
-
- @staticmethod
- def filter(contents):
- func = lambda m: b"%.3f" % (float(m.group(0)))
- contents = re.sub(rb"\d+\.\d+(e[+-]\d+)?", func, contents)
- contents = re.sub(rb"(\d\.\d+?)0+\b", rb"\1", contents)
- contents = re.sub(rb"(\d)\.0+(?=\D|\b)", rb"\1", contents)
- return contents
-
-
-class CompareWithoutIds(Compare):
- """Remove all ids from the svg"""
-
- @staticmethod
- def filter(contents):
- return re.sub(rb' id="([^"]*)"', b"", contents)
-
-
-class CompareWithPathSpace(Compare):
- """Make sure that path segment commands have spaces around them"""
-
- @staticmethod
- def filter(contents):
- def func(match):
- """We've found a path command, process it"""
- new = re.sub(rb"\s*([LZMHVCSQTAatqscvhmzl])\s*", rb" \1 ", match.group(1))
- return b' d="' + new.replace(b",", b" ") + b'"'
-
- return re.sub(rb' d="([^"]*)"', func, contents)
-
-
-class CompareSize(Compare):
- """Compare the length of the contents instead of the contents"""
-
- @staticmethod
- def filter(contents):
- return len(contents)
-
-
-class CompareOrderIndependentBytes(Compare):
- """Take all the bytes and sort them"""
-
- @staticmethod
- def filter(contents):
- return b"".join([bytes(i) for i in sorted(contents)])
-
-
-class CompareOrderIndependentLines(Compare):
- """Take all the lines and sort them"""
-
- @staticmethod
- def filter(contents):
- return b"\n".join(sorted(contents.splitlines()))
-
-
-class CompareOrderIndependentStyle(Compare):
- """Take all styles and sort the results"""
-
- @staticmethod
- def filter(contents):
- contents = CompareNumericFuzzy.filter(contents)
-
- def func(match):
- """Search and replace function for sorting"""
- sty = b";".join(sorted(match.group(1).split(b";")))
- return b'style="%s"' % (sty,)
-
- return re.sub(rb'style="([^"]*)"', func, contents)
-
-
-class CompareOrderIndependentStyleAndPath(Compare):
- """Take all styles and paths and sort them both"""
-
- @staticmethod
- def filter(contents):
- contents = CompareOrderIndependentStyle.filter(contents)
-
- def func(match):
- """Search and replace function for sorting"""
- path = b"X".join(sorted(re.split(rb"[A-Z]", match.group(1))))
- return b'd="%s"' % (path,)
-
- return re.sub(rb'\bd="([^"]*)"', func, contents)
-
-
-class CompareOrderIndependentTags(Compare):
- """Sorts all the XML tags"""
-
- @staticmethod
- def filter(contents):
- return b"\n".join(sorted(re.split(rb">\s*<", contents)))
-
-
-class CompareReplacement(Compare):
- """Replace pieces to make output more comparable
-
- .. versionadded:: 1.1"""
-
- def __init__(self, *replacements):
- self.deltas = replacements
- super().__init__()
-
- def filter(self, contents):
- contents = to_bytes(contents)
- for _from, _to in self.deltas:
- contents = contents.replace(to_bytes(_from), to_bytes(_to))
- return contents
-
-
-class WindowsTextCompat(CompareReplacement):
- """Normalize newlines so tests comparing plain text work
-
- .. versionadded:: 1.2"""
-
- def __init__(self):
- super().__init__(("\r\n", "\n"))
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/inkscape.extension.rng b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/inkscape.extension.rng
deleted file mode 100644
index 1cb77de..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/inkscape.extension.rng
+++ /dev/null
@@ -1,577 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- file
- executable
- extension
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- python
- perl
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- path
- extensions
- inx
- absolute
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- all
- g
- path
- rect
- text
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- header
- url
-
-
-
-
-
-
- default
- preserve
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- expand
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- int
-
-
-
-
-
-
-
-
-
-
-
-
-
- full
-
-
-
-
-
-
-
-
-
- float
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- full
-
-
-
-
-
-
- bool
-
-
-
-
-
- color
-
-
-
-
- colorbutton
-
-
-
-
-
-
-
-
-
-
-
- string
-
-
-
-
-
-
-
-
-
- multiline
-
-
-
-
-
-
-
-
-
-
- path
-
-
-
-
- file
- files
- folder
- folders
- file_new
- folder_new
-
-
-
-
-
-
-
-
-
-
-
-
- optiongroup
-
-
-
-
- combo
- radio
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- notebook
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 0
- 1
-
-
-
-
-
-
- yes
- no
-
-
-
-
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/inkscape.extension.schema b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/inkscape.extension.schema
deleted file mode 100644
index 63cb97b..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/inkscape.extension.schema
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
- Warning: @value values should be unique for a given option.
-
-
-
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/inx.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/inx.py
deleted file mode 100644
index 20cc345..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/inx.py
+++ /dev/null
@@ -1,162 +0,0 @@
-#!/usr/bin/env python3
-# coding=utf-8
-"""
-Test elements extra logic from svg xml lxml custom classes.
-"""
-import os
-from importlib import resources
-
-from lxml import etree
-
-from ..utils import PY3
-from ..inx import InxFile
-
-INTERNAL_ARGS = ("help", "output", "id", "selected-nodes")
-ARG_TYPES = {
- "Boolean": "bool",
- "Color": "color",
- "str": "string",
- "int": "int",
- "float": "float",
-}
-
-
-class InxMixin:
- """Tools for Testing INX files, use as a mixin class:
-
- class MyTests(InxMixin, TestCase):
- def test_inx_file(self):
- self.assertInxIsGood("some_inx_file.inx")
- """
-
- def assertInxIsGood(self, inx_file): # pylint: disable=invalid-name
- """Test the inx file for consistancy and correctness"""
- self.assertTrue(PY3, "INX files can only be tested in python3")
-
- inx = InxFile(inx_file)
- if "help" in inx.ident or inx.script.get("interpreter", None) != "python":
- return
- cls = inx.extension_class
- # Check class can be matched in python file
- self.assertTrue(cls, f"Can not find class for {inx.filename}")
- # Check name is reasonable for the class
- if not cls.multi_inx:
- self.assertEqual(
- cls.__name__,
- inx.slug,
- f"Name of extension class {cls.__module__}.{cls.__name__} "
- f"is different from ident {inx.slug}",
- )
- self.assertParams(inx, cls)
-
- def assertParams(self, inx, cls): # pylint: disable=invalid-name
- """Confirm the params in the inx match the python script
-
- .. versionchanged:: 1.2
- Also checks that the default values are identical"""
- params = {param.name: self.parse_param(param) for param in inx.params}
- args = dict(self.introspect_arg_parser(cls().arg_parser))
- mismatch_a = list(set(params) ^ set(args) & set(params))
- mismatch_b = list(set(args) ^ set(params) & set(args))
- self.assertFalse(
- mismatch_a, f"{inx.filename}: Inx params missing from arg parser"
- )
- self.assertFalse(
- mismatch_b, f"{inx.filename}: Script args missing from inx xml"
- )
-
- for param in args:
- if params[param]["type"] and args[param]["type"]:
- self.assertEqual(
- params[param]["type"],
- args[param]["type"],
- f"Type is not the same for {inx.filename}:param:{param}",
- )
- inxdefault = params[param]["default"]
- argsdefault = args[param]["default"]
- if inxdefault and argsdefault:
- # for booleans, the inx is lowercase and the param is uppercase
- if params[param]["type"] == "bool":
- argsdefault = str(argsdefault).lower()
- elif params[param]["type"] not in ["string", None, "color"] or args[
- param
- ]["type"] in ["int", "float"]:
- # try to parse the inx value to compare numbers to numbers
- inxdefault = float(inxdefault)
- if args[param]["type"] == "color" or callable(args[param]["default"]):
- # skip color, method types
- continue
- self.assertEqual(
- argsdefault,
- inxdefault,
- f"Default value is not the same for {inx.filename}:param:{param}",
- )
-
- def introspect_arg_parser(self, arg_parser):
- """Pull apart the arg parser to find out what we have in it"""
- for (
- action
- ) in arg_parser._optionals._actions: # pylint: disable=protected-access
- for opt in action.option_strings:
- # Ignore params internal to inkscape (thus not in the inx)
- if opt.startswith("--") and opt[2:] not in INTERNAL_ARGS:
- yield (opt[2:], self.introspect_action(action))
-
- @staticmethod
- def introspect_action(action):
- """Pull apart a single action to get at the juicy insides"""
- return {
- "type": ARG_TYPES.get((action.type or str).__name__, "string"),
- "default": action.default,
- "choices": action.choices,
- "help": action.help,
- }
-
- @staticmethod
- def parse_param(param):
- """Pull apart the param element in the inx file"""
- if param.param_type in ("optiongroup", "notebook"):
- options = param.options
- return {
- "type": None,
- "choices": options,
- "default": options and options[0] or None,
- }
- param_type = param.param_type
- if param.param_type in ("path",):
- param_type = "string"
- return {
- "type": param_type,
- "default": param.text,
- "choices": None,
- }
-
- def assertInxSchemaValid(self, inx_file): # pylint: disable=invalid-name
- """Validate inx file schema."""
- self.assertTrue(INX_SCHEMAS, "no schema files found")
- with open(inx_file, "rb") as fp:
- inx_doc = etree.parse(fp)
-
- for schema_name, schema in INX_SCHEMAS.items():
- with self.subTest(schema_file=schema_name):
- schema.assert_(inx_doc)
-
-
-def _load_inx_schemas():
- _SCHEMA_CLASSES = {
- ".rng": etree.RelaxNG,
- ".schema": etree.Schematron, # "pre-ISO-Schematron"
- }
-
- for name in resources.contents(__package__):
- _, ext = os.path.splitext(name)
- schema_class = _SCHEMA_CLASSES.get(ext)
- if schema_class is None:
- continue
-
- with resources.open_binary(__package__, name) as fp:
- schema_doc = etree.parse(fp)
- yield name, schema_class(schema_doc)
-
-
-INX_SCHEMAS = dict(_load_inx_schemas())
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/mock.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/mock.py
deleted file mode 100644
index de5e569..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/mock.py
+++ /dev/null
@@ -1,463 +0,0 @@
-# coding=utf-8
-#
-# Copyright (C) 2018 Martin Owens
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA.
-#
-# pylint: disable=protected-access,too-few-public-methods
-"""
-Any mocking utilities required by testing. Mocking is when you need the test
-to exercise a piece of code, but that code may or does call on something
-outside of the target code that either takes too long to run, isn't available
-during the test running process or simply shouldn't be running at all.
-"""
-
-import io
-import os
-import sys
-import logging
-import hashlib
-import tempfile
-from typing import List, Tuple, Any
-
-from email.mime.application import MIMEApplication
-from email.mime.multipart import MIMEMultipart
-from email.mime.text import MIMEText
-from email.parser import Parser as EmailParser
-
-import inkex.command
-
-
-FIXED_BOUNDARY = "--CALLDATA--//--CALLDATA--"
-
-
-class Capture:
- """Capture stdout or stderr. Used as `with Capture('stdout') as stream:`"""
-
- def __init__(self, io_name="stdout", swap=True):
- self.io_name = io_name
- self.original = getattr(sys, io_name)
- self.stream = io.StringIO()
- self.swap = swap
-
- def __enter__(self):
- if self.swap:
- setattr(sys, self.io_name, self.stream)
- return self.stream
-
- def __exit__(self, exc, value, traceback):
- if exc is not None and self.swap:
- # Dump content back to original if there was an error.
- self.original.write(self.stream.getvalue())
- setattr(sys, self.io_name, self.original)
-
-
-class ManualVerbosity:
- """Change the verbosity of the test suite manually"""
-
- result = property(lambda self: self.test._current_result)
-
- def __init__(self, test, okay=True, dots=False):
- self.test = test
- self.okay = okay
- self.dots = dots
-
- def flip(
- self, exc_type=None, exc_val=None, exc_tb=None
- ): # pylint: disable=unused-argument
- """Swap the stored verbosity with the original"""
- self.okay, self.result.showAll = self.result.showAll, self.okay
- self.dots, self.result.dots = self.result.dots, self.okay
-
- __enter__ = flip
- __exit__ = flip
-
-
-class MockMixin:
- """
- Add mocking ability to any test base class, will set up mock on setUp
- and remove it on tearDown.
-
- Mocks are stored in an array attached to the test class (not instance!) which
- ensures that mocks can only ever be setUp once and can never be reset over
- themselves. (just in case this looks weird at first glance)
-
- class SomeTest(MockingMixin, TestBase):
- mocks = [(sys, 'exit', NoSystemExit("Nope!")]
- """
-
- mocks = [] # type: List[Tuple[Any, str, Any]]
-
- def setUpMock(self, owner, name, new): # pylint: disable=invalid-name
- """Setup the mock here, taking name and function and returning (name, old)"""
- old = getattr(owner, name)
- if isinstance(new, str):
- if hasattr(self, new):
- new = getattr(self, new)
- if isinstance(new, Exception):
-
- def _error_function(*args2, **kw2): # pylint: disable=unused-argument
- raise type(new)(str(new))
-
- setattr(owner, name, _error_function)
- elif new is None or isinstance(new, (str, int, float, list, tuple)):
-
- def _value_function(*args, **kw): # pylint: disable=unused-argument
- return new
-
- setattr(owner, name, _value_function)
- else:
- setattr(owner, name, new)
- # When we start, mocks contains length 3 tuples, when we're finished, it
- # contains length 4, this stops remocking and reunmocking from taking place.
- return (owner, name, old, False)
-
- def setUp(self): # pylint: disable=invalid-name
- """For each mock instruction, set it up and store the return"""
- super().setUp()
- for x, mock in enumerate(self.mocks):
- if len(mock) == 4:
- logging.error(
- "Mock was already set up, so it wasn't cleared previously!"
- )
- continue
- self.mocks[x] = self.setUpMock(*mock)
-
- def tearDown(self): # pylint: disable=invalid-name
- """For each returned stored, tear it down and restore mock instruction"""
- super().tearDown()
- try:
- for x, (owner, name, old, _) in enumerate(self.mocks):
- self.mocks[x] = (owner, name, getattr(owner, name))
- setattr(owner, name, old)
- except ValueError:
- logging.warning("Was never mocked, did something go wrong?")
-
- def old_call(self, name):
- """Get the original caller"""
- for arg in self.mocks:
- if arg[1] == name:
- return arg[2]
- return lambda: None
-
-
-class MockCommandMixin(MockMixin):
- """
- Replace all the command functions with testable replacements.
-
- This stops the pipeline and people without the programs, running into problems.
- """
-
- mocks = [
- (inkex.command, "_call", "mock_call"),
- (tempfile, "mkdtemp", "record_tempdir"),
- ]
- recorded_tempdirs = [] # type:List[str]
-
- def setUp(self): # pylint: disable=invalid-name
- super().setUp()
- # This is a the daftest thing I've ever seen, when in the middle
- # of a mock, the 'self' variable magically turns from a FooTest
- # into a TestCase, this makes it impossible to find the datadir.
- from . import TestCase
-
- TestCase._mockdatadir = self.datadir()
-
- @classmethod
- def cmddir(cls):
- """Returns the location of all the mocked command results"""
- from . import TestCase
-
- return os.path.join(TestCase._mockdatadir, "cmd")
-
- def record_tempdir(self, *args, **kwargs):
- """Record any attempts to make tempdirs"""
- newdir = self.old_call("mkdtemp")(*args, **kwargs)
- self.recorded_tempdirs.append(os.path.realpath(newdir))
- return newdir
-
- def clean_paths(self, data, files):
- """Clean a string of any files or tempdirs"""
-
- def replace(indata, replaced, replacement):
- if isinstance(indata, str):
- indata = indata.replace(replaced, replacement)
- else:
- indata = [i.replace(replaced, replacement) for i in indata]
- return indata
-
- try:
- for fdir in self.recorded_tempdirs:
- data = replace(data, fdir + os.sep, "./")
- data = replace(data, fdir, ".")
- files = replace(files, fdir + os.sep, "./")
- files = replace(files, fdir, ".")
- for fname in files:
- data = replace(data, fname, os.path.basename(fname))
- except (UnicodeDecodeError, TypeError):
- pass
- return data
-
- def get_all_tempfiles(self):
- """Returns a set() of all files currently in any of the tempdirs"""
- ret = set([])
- for fdir in self.recorded_tempdirs:
- if not os.path.isdir(fdir):
- continue
- for fname in os.listdir(fdir):
- if fname in (".", ".."):
- continue
- path = os.path.join(fdir, fname)
- # We store the modified time so if a program modifies
- # the input file in-place, it will look different.
- ret.add(path + f";{os.path.getmtime(path)}")
-
- return ret
-
- def ignore_command_mock(self, program, arglst, path):
- """Return true if the mock is ignored"""
- if self and program and arglst:
- env = os.environ.get("NO_MOCK_COMMANDS", 0)
- if (not os.path.exists(path) and int(env) == 1) or int(env) == 2:
- return True
- return False
-
- def mock_call(self, program, *args, **kwargs):
- """
- Replacement for the inkex.command.call() function, instead of calling
- an external program, will compile all arguments into a hash and use the
- hash to find a command result.
- """
- # Remove stdin first because it needs to NOT be in the Arguments list.
- stdin = kwargs.pop("stdin", None)
- args = list(args)
-
- # We use email
- msg = MIMEMultipart(boundary=FIXED_BOUNDARY)
- msg["Program"] = MockCommandMixin.get_program_name(program)
-
- # Gather any output files and add any input files to msg, args and kwargs
- # may be modified to strip out filename directories (which change)
- inputs, outputs = self.add_call_files(msg, args, kwargs)
-
- arglst = inkex.command.to_args_sorted(program, *args, **kwargs)[1:]
- arglst = self.clean_paths(arglst, inputs + outputs)
- argstr = " ".join(arglst)
- msg["Arguments"] = argstr.strip()
-
- if stdin is not None:
- # The stdin is counted as the msg body
- cleanin = (
- self.clean_paths(stdin, inputs + outputs)
- .replace("\r\n", "\n")
- .replace(".\\", "./")
- )
- msg.attach(MIMEText(cleanin, "plain", "utf-8"))
-
- keystr = msg.as_string()
- # On Windows, output is separated by CRLF
- keystr = keystr.replace("\r\n", "\n")
- # There is a difference between python2 and python3 output
- keystr = keystr.replace("\n\n", "\n")
- keystr = keystr.replace("\n ", " ")
- if "verb" in keystr:
- # Verbs seperated by colons cause diff in py2/3
- keystr = keystr.replace("; ", ";")
- # Generate a unique key for this call based on _all_ it's inputs
- key = hashlib.md5(keystr.encode("utf-8")).hexdigest()
-
- if self.ignore_command_mock(
- program, arglst, self.get_call_filename(program, key, create=True)
- ):
- # Call original code. This is so programmers can run the test suite
- # against the external programs too, to see how their fair.
- if stdin is not None:
- kwargs["stdin"] = stdin
-
- before = self.get_all_tempfiles()
- stdout = self.old_call("_call")(program, *args, **kwargs)
- outputs += list(self.get_all_tempfiles() - before)
- # Remove the modified time from the call
- outputs = [out.rsplit(";", 1)[0] for out in outputs]
-
- # After the program has run, we collect any file outputs and store
- # them, then store any stdout or stderr created during the run.
- # A developer can then use this to build new test cases.
- reply = MIMEMultipart(boundary=FIXED_BOUNDARY)
- reply["Program"] = MockCommandMixin.get_program_name(program)
- reply["Arguments"] = argstr
- self.save_call(program, key, stdout, outputs, reply)
- self.save_key(program, key, keystr, "key")
- return stdout
-
- try:
- return self.load_call(program, key, outputs)
- except IOError as err:
- self.save_key(program, key, keystr, "bad-key")
- raise IOError(
- f"Problem loading call: {program}/{key} use the environment variable "
- "NO_MOCK_COMMANDS=1 to call out to the external program and generate "
- f"the mock call file for call {program} {argstr}."
- ) from err
-
- def add_call_files(self, msg, args, kwargs):
- """
- Gather all files, adding input files to the msg (for hashing) and
- output files to the returned files list (for outputting in debug)
- """
- # Gather all possible string arguments together.
- loargs = sorted(kwargs.items(), key=lambda i: i[0])
- values = []
- for arg in args:
- if isinstance(arg, (tuple, list)):
- loargs.append(arg)
- else:
- values.append(str(arg))
-
- for _, value in loargs:
- if isinstance(value, (tuple, list)):
- for val in value:
- if val is not True:
- values.append(str(val))
- elif value is not True:
- values.append(str(value))
-
- # See if any of the strings could be filenames, either going to be
- # or are existing files on the disk.
- files = [[], []]
- for value in values:
- if os.path.isfile(value): # Input file
- files[0].append(value)
- self.add_call_file(msg, value)
- elif os.path.isdir(os.path.dirname(value)): # Output file
- files[1].append(value)
- return files
-
- def add_call_file(self, msg, filename):
- """Add a single file to the given mime message"""
- fname = os.path.basename(filename)
- with open(filename, "rb") as fhl:
- if filename.endswith(".svg"):
- value = self.clean_paths(fhl.read().decode("utf8"), [])
- else:
- value = fhl.read()
- try:
- value = value.decode()
- except UnicodeDecodeError:
- pass # do not attempt to process binary files further
- if isinstance(value, str):
- value = value.replace("\r\n", "\n").replace(".\\", "./")
- part = MIMEApplication(value, Name=fname)
- # After the file is closed
- part["Content-Disposition"] = "attachment"
- part["Filename"] = fname
- msg.attach(part)
-
- def get_call_filename(self, program, key, create=False):
- """
- Get the filename for the call testing information.
- """
- path = self.get_call_path(program, create=create)
- fname = os.path.join(path, key + ".msg")
- if not create and not os.path.isfile(fname):
- raise IOError(f"Attempted to find call test data {key}")
- return fname
-
- @staticmethod
- def get_program_name(program):
- """Takes a program and returns a program name"""
- if program == inkex.command.INKSCAPE_EXECUTABLE_NAME:
- return "inkscape"
- return program
-
- def get_call_path(self, program, create=True):
- """Get where this program would store it's test data"""
- command_dir = os.path.join(
- self.cmddir(), MockCommandMixin.get_program_name(program)
- )
- if not os.path.isdir(command_dir):
- if create:
- os.makedirs(command_dir)
- else:
- raise IOError(
- "A test is attempting to use an external program in a test:"
- f" {program}; but there is not a command data directory which "
- f"should contain the results of the command here: {command_dir}"
- )
- return command_dir
-
- def load_call(self, program, key, files):
- """
- Load the given call
- """
- fname = self.get_call_filename(program, key, create=False)
- with open(fname, "rb") as fhl:
- msg = EmailParser().parsestr(fhl.read().decode("utf-8"))
-
- stdout = None
- for part in msg.walk():
- if "attachment" in part.get("Content-Disposition", ""):
- base_name = part["Filename"]
- for out_file in files:
- if out_file.endswith(base_name):
- with open(out_file, "wb") as fhl:
- fhl.write(part.get_payload(decode=True))
- part = None
- if part is not None:
- # Was not caught by any normal outputs, so we will
- # save the file to EVERY tempdir in the hopes of
- # hitting on of them.
- for fdir in self.recorded_tempdirs:
- if os.path.isdir(fdir):
- with open(os.path.join(fdir, base_name), "wb") as fhl:
- fhl.write(part.get_payload(decode=True))
- elif part.get_content_type() == "text/plain":
- stdout = part.get_payload(decode=True)
-
- return stdout
-
- def save_call(
- self, program, key, stdout, files, msg, ext="output"
- ): # pylint: disable=too-many-arguments
- """
- Saves the results from the call into a debug output file, the resulting files
- should be a Mime msg file format with each attachment being one of the input
- files as well as any stdin and arguments used in the call.
- """
- if stdout is not None and stdout.strip():
- # The stdout is counted as the msg body here
- msg.attach(MIMEText(stdout.decode("utf-8"), "plain", "utf-8"))
-
- for fname in set(files):
- if os.path.isfile(fname):
- # print("SAVING FILE INTO MSG: {}".format(fname))
- self.add_call_file(msg, fname)
- else:
- part = MIMEText("Missing File", "plain", "utf-8")
- part.add_header("Filename", os.path.basename(fname))
- msg.attach(part)
- fname = self.get_call_filename(program, key, create=True) + "." + ext
-
- with open(fname, "wb") as fhl:
- fhl.write(msg.as_string().encode("utf-8"))
- if int(os.environ.get("NO_MOCK_COMMANDS", 0)) == 1:
- print(f"Saved mock call as {fname}, remove .{ext}")
-
- def save_key(self, program, key, keystr, ext="key"):
- """Save the key file if we are debugging the key data"""
- if os.environ.get("DEBUG_KEY"):
- fname = self.get_call_filename(program, key, create=True) + "." + ext
- with open(fname, "wb") as fhl:
- fhl.write(keystr.encode("utf-8"))
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/svg.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/svg.py
deleted file mode 100644
index c601c81..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/svg.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# coding=utf-8
-#
-# Copyright (C) 2018 Martin Owens
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA.
-#
-"""
-SVG specific utilities for tests.
-"""
-
-from lxml import etree
-
-from inkex import SVG_PARSER
-
-
-def svg(svg_attrs=""):
- """Returns xml etree based on a simple SVG element.
-
- svg_attrs: A string containing attributes to add to the
- root element of a minimal SVG document.
- """
- return etree.fromstring(
- str.encode(
- ''
- f" "
- ),
- parser=SVG_PARSER,
- )
-
-
-def svg_unit_scaled(width_unit):
- """Same as svg, but takes a width unit (top-level transform) for the new document.
-
- The transform is the ratio between the SVG width and the viewBox width.
- """
- return svg(f'width="1{width_unit}" viewBox="0 0 1 1"')
-
-
-def svg_file(filename):
- """Parse an svg file and return it's document root"""
- with open(filename, "r", encoding="utf-8") as fhl:
- doc = etree.parse(fhl, parser=SVG_PARSER)
- return doc.getroot()
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/test_inx_file.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/test_inx_file.py
deleted file mode 100644
index bce8e20..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/test_inx_file.py
+++ /dev/null
@@ -1,63 +0,0 @@
-"""Check .inx file(s).
-
-This is meant to be run an executable module, e.g.
-
- python -m inkex.tester.test_inx_file *.inx
-
-"""
-import argparse
-import sys
-import unittest
-from typing import List
-
-from .inx import InxMixin
-
-
-class InxTestCase(InxMixin, unittest.TestCase):
- inx_files: List[str] = []
-
- def test_inx_file_parameters(self):
- for inx_file in self.inx_files:
- with self.subTest(inx_file=inx_file):
- self.assertInxIsGood(inx_file)
-
- def test_inx_file_schema(self):
- for inx_file in self.inx_files:
- with self.subTest(inx_file=inx_file):
- self.assertInxSchemaValid(inx_file)
-
-
-def main(args=None):
- """Check .inx file(s)"""
- parser = argparse.ArgumentParser(description=__doc__)
- parser.add_argument(
- "-v",
- "--verbose",
- dest="verbosity",
- action="store_const",
- const=2,
- help="Verbose output",
- )
- parser.add_argument(
- "-q",
- "--quiet",
- dest="verbosity",
- action="store_const",
- const=0,
- help="Quiet output",
- )
- parser.add_argument(
- "inx_files", metavar="INX-FILE", nargs="+", help="An .inx file to check"
- )
- args = parser.parse_args(args, namespace=argparse.Namespace(verbosity=1))
-
- InxTestCase.inx_files = args.inx_files
- runner = unittest.TextTestRunner(verbosity=args.verbosity)
-
- suite = unittest.defaultTestLoader.loadTestsFromTestCase(InxTestCase)
- result = runner.run(suite)
- sys.exit(not result.wasSuccessful())
-
-
-if __name__ == "__main__":
- main()
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/word.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/word.py
deleted file mode 100644
index bf395d7..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/word.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# coding=utf-8
-#
-# Unknown author
-#
-"""
-Generate words for testing.
-"""
-
-import string
-import random
-
-
-def word_generator(text_length):
- """
- Generate a word of text_length size
- """
- word = ""
-
- for _ in range(0, text_length):
- word += random.choice(
- string.ascii_lowercase
- + string.ascii_uppercase
- + string.digits
- + string.punctuation
- )
-
- return word
-
-
-def sentencecase(word):
- """Make a word standace case"""
- word_new = ""
- lower_letters = list(string.ascii_lowercase)
- first = True
- for letter in word:
- if letter in lower_letters and first is True:
- word_new += letter.upper()
- first = False
- else:
- word_new += letter
-
- return word_new
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/xmldiff.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/xmldiff.py
deleted file mode 100644
index 1864530..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tester/xmldiff.py
+++ /dev/null
@@ -1,124 +0,0 @@
-#
-# Copyright 2011 (c) Ian Bicking
-# 2019 (c) Martin Owens
-#
-# Taken from http://formencode.org under the GPL compatible PSF License.
-# Modified to produce more output as a diff.
-#
-"""
-Allow two xml files/lxml etrees to be compared, returning their differences.
-"""
-import xml.etree.ElementTree as xml
-from io import BytesIO
-
-from inkex.paths import Path
-
-
-def text_compare(test1, test2):
- """
- Compare two text strings while allowing for '*' to match
- anything on either lhs or rhs.
- """
- if not test1 and not test2:
- return True
- if test1 == "*" or test2 == "*":
- return True
- return (test1 or "").strip() == (test2 or "").strip()
-
-
-class DeltaLogger(list):
- """A record keeper of the delta between two svg files"""
-
- def append_tag(self, tag_a, tag_b):
- """Record a tag difference"""
- if tag_a:
- tag_a = f"<{tag_a}.../>"
- if tag_b:
- tag_b = f"<{tag_b}.../>"
- self.append((tag_a, tag_b))
-
- def append_attr(self, attr, value_a, value_b):
- """Record an attribute difference"""
-
- def _prep(val):
- if val:
- if attr == "d":
- return [attr] + Path(val).to_arrays()
- return (attr, val)
- return val
-
- # Only append a difference if the preprocessed values are different.
- # This solves the issue that -0 != 0 in path data.
- prep_a = _prep(value_a)
- prep_b = _prep(value_b)
- if prep_a != prep_b:
- self.append((prep_a, prep_b))
-
- def append_text(self, text_a, text_b):
- """Record a text difference"""
- self.append((text_a, text_b))
-
- def __bool__(self):
- """Returns True if there's no log, i.e. the delta is clean"""
- return not self.__len__()
-
- __nonzero__ = __bool__
-
- def __repr__(self):
- if self:
- return "No differences detected"
- return f"{len(self)} xml differences"
-
-
-def to_xml(data):
- """Convert string or bytes to xml parsed root node"""
- if isinstance(data, str):
- data = data.encode("utf8")
- if isinstance(data, bytes):
- return xml.parse(BytesIO(data)).getroot()
- return data
-
-
-def xmldiff(data1, data2):
- """Create an xml difference, will modify the first xml structure with a diff"""
- xml1, xml2 = to_xml(data1), to_xml(data2)
- delta = DeltaLogger()
- _xmldiff(xml1, xml2, delta)
- return xml.tostring(xml1).decode("utf-8"), delta
-
-
-def _xmldiff(xml1, xml2, delta):
- if xml1.tag != xml2.tag:
- xml1.tag = f"{xml1.tag}XXX{xml2.tag}"
- delta.append_tag(xml1.tag, xml2.tag)
- for name, value in xml1.attrib.items():
- if name not in xml2.attrib:
- delta.append_attr(name, xml1.attrib[name], None)
- xml1.attrib[name] += "XXX"
- elif xml2.attrib.get(name) != value:
- delta.append_attr(name, xml1.attrib.get(name), xml2.attrib.get(name))
- xml1.attrib[name] = f"{xml1.attrib.get(name)}XXX{xml2.attrib.get(name)}"
- for name, value in xml2.attrib.items():
- if name not in xml1.attrib:
- delta.append_attr(name, None, value)
- xml1.attrib[name] = "XXX" + value
- if not text_compare(xml1.text, xml2.text):
- delta.append_text(xml1.text, xml2.text)
- xml1.text = f"{xml1.text}XXX{xml2.text}"
- if not text_compare(xml1.tail, xml2.tail):
- delta.append_text(xml1.tail, xml2.tail)
- xml1.tail = f"{xml1.tail}XXX{xml2.tail}"
-
- # Get children and pad with nulls
- children_a = list(xml1)
- children_b = list(xml2)
- children_a += [None] * (len(children_b) - len(children_a))
- children_b += [None] * (len(children_a) - len(children_b))
-
- for child_a, child_b in zip(children_a, children_b):
- if child_a is None: # child_b exists
- delta.append_tag(child_b.tag, None)
- elif child_b is None: # child_a exists
- delta.append_tag(None, child_a.tag)
- else:
- _xmldiff(child_a, child_b, delta)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/__init__.py
deleted file mode 100644
index 3520752..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/__init__.py
+++ /dev/null
@@ -1,81 +0,0 @@
-#!/usr/bin/env python
-# coding=utf-8
-#
-# Copyright (c) 2023 David Burghoff
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-
-"""
-This submodule gives all TextElements and FlowRoots the parsed_text property,
-which is a cached instance of ParsedText. To use, simply run
-
- import text
-
-Following this, text properties will be available at el.parsed_text. For more
-details, see parser.py. To check if it is working properly, run the
-following on some text:
-
- el.parsed_text.Make_Highlights('char')
-
-Rectangles should appear surrounding the logical extents of each character.
-
-Before parsing is done, a character table must be generated to determine the properties
-of all the characters present. This is done automatically by the first invocation of
-.parsed_text, which automatically analyzes the whole document and adds it to the SVG.
-If you are only parsing a few text elements, this can be sped up by calling
-svg.make_char_table(els). On the rare occasions this fails, a command call may
-be performed as a fallback.
-"""
-
-# Give inkex an inkex.text submodule that refers to this directory
-import sys
-import os
-import inspect
-import inkex
-
-if not hasattr(inkex, "text"):
- import importlib
-
- mydir = os.path.dirname(os.path.abspath(__file__))
- myloc, myname = os.path.split(mydir)
- oldpath = sys.path
- sys.path.append(myloc)
- inkex.text = importlib.import_module(myname)
- sys.modules["inkex.text"] = inkex.text
- sys.path = oldpath
-
-# Gives inkex elements some additional cached attributes, for further speedups
-import inkex.text.cache # pylint: disable=wrong-import-position
-
-
-def add_cache(base, derived):
- """
- Adds the cache capabilities from a derived class to a base class by dynamically
- attaching methods, properties, and data descriptors that are unique to the derived
- class to the base class.
- """
- predfn = (
- lambda x: isinstance(x, property)
- or inspect.isfunction(x)
- or inspect.isdatadescriptor(x)
- )
- base_m = dict(inspect.getmembers(base, predicate=predfn))
- for name, memb in inspect.getmembers(derived, predicate=predfn):
- if memb not in base_m.values():
- setattr(base, name, memb)
-
-
-add_cache(inkex.BaseElement, inkex.text.cache.BaseElementCache)
-add_cache(inkex.SvgDocumentElement, inkex.text.cache.SvgDocumentElementCache)
-add_cache(inkex.Style, inkex.text.cache.StyleCache)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/cache.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/cache.py
deleted file mode 100644
index 48f0fce..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/cache.py
+++ /dev/null
@@ -1,1302 +0,0 @@
-# coding=utf-8
-
-# Copyright (c) 2023 David Burghoff
-# Martin Owens
-# Sergei Izmailov
-# Thomas Holder
-# Jonathan Neuhauser
-
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-"""
-A collection of functions that provide caching of certain Inkex properties.
-Most provide the same functionality as the regular properties,
-but with a 'c' in front of the name. For example,
- Style: cstyle, cspecified_style, ccascaded_style
- Transform: ctransform, ccomposed_transform
- Miscellaneous: croot, cdefs, ctag
-Most are invalidated by setting to None (except ctransform, which is set to identity).
-xpath calls are avoided at all costs.
-
-Also gives SvgDocumentElements some dictionaries that are used to speed up
-various lookups:
- svg.iddict: elements by their ID
- svg.cssdict: CSS styles by ID
-
-Lastly, several core Inkex functions are overwritten with versions that
-use the cache. For example, getElementById uses svg.iddict to avoid xpath
-calls.
-"""
-
-import re
-from typing import Optional, List
-import inkex
-from inkex import Style
-from inkex import BaseElement, SvgDocumentElement
-from inkex.text.parser import ParsedText, CharacterTable
-from text.utils import shapetags, tags, ipx, list2 # pylint: disable=import-error
-import lxml
-
-EBget = lxml.etree.ElementBase.get
-EBset = lxml.etree.ElementBase.set
-
-
-def get_link_fcn(elem, typestr, svg=None, llget=False):
- """
- Function that references URLs, returning the referenced element
- Returns None if it does not exist or is invalid
- Accepts both elements and styles as inputs
- """
- if llget:
- urlv = EBget(elem, typestr)
- # fine for 'clip-path' & 'mask'
- else:
- urlv = elem.get(typestr)
- if urlv is not None:
- if svg is None:
- svg = elem.croot # need to specify svg for Styles but not BaseElements
- if svg is None:
- return None
- if typestr == "xlink:href":
- urlel = svg.getElementById(urlv[1:])
- elif urlv.startswith("url"):
- urlel = svg.getElementById(urlv[5:-1])
- else:
- urlel = None
- return urlel
- return None
-
-
-BE_set_id = BaseElement.set_id
-
-# Fast empty Style initialization
-if len(Style().__dict__) == 0:
-
- def empty_style():
- """Instantiates an empty Style object."""
- return Style.__new__(Style)
-else:
- bstyle_dict = Style().__dict__
-
- def empty_style():
- """Instantiates an empty Style object."""
- ret = Style.__new__(Style)
- ret.__dict__ = bstyle_dict
- return ret
-
-
-# pylint:disable=attribute-defined-outside-init
-class BaseElementCache(BaseElement):
- """Adds caching of style and transformation properties of base elements."""
-
- get_link = get_link_fcn
-
- class CStyle(Style):
- """
- Cached style attribute that invalidates the cached cascaded / specified
- style whenever the style is changed. Always use this when setting styles.
- """
-
- # Delete key when value set to None
- def __init__(self, val, elem):
- self.elem = elem
- self.init = True
- super().__init__(val)
- self.init = False
-
- def __setitem__(self, *args):
- # Allows for multiple inputs using
- # __setitem__(key1, value1, key2, value2, ...)
- if self.init:
- # OrderedDict sets items during initialization, use super()
- super().__setitem__(args[0], args[1])
- else:
- changedvalue = False
- for i in range(0, len(args), 2):
- key = args[i]
- value = args[i + 1]
- if value is None:
- if key in self:
- del self[key]
- changedvalue = True
- else:
- if key not in self or self[key] != value:
- super().__setitem__(key, value)
- changedvalue = True
- if changedvalue:
- self.elem.cstyle = self
-
-
- class CStyleDescriptor:
- """Descriptor for caching and managing style changes."""
-
- def __get__(self, elem, owner):
- if not hasattr(elem, "_cstyle") and elem is not None:
- elem._cstyle = BaseElementCache.CStyle(EBget(elem, "style"), elem)
- return elem._cstyle
-
- def __set__(self, elem, value):
- if value:
- vstr = str(value)
- EBset(elem, "style", vstr)
- else:
- if "style" in elem.attrib:
- del elem.attrib["style"]
-
- if not isinstance(value, BaseElementCache.CStyle):
- if isinstance(value, Style):
- # Cast to CStyle without reinitializing
- value.__class__ = BaseElementCache.CStyle
- value.elem = elem
- value.init = False
- else:
- value = BaseElementCache.CStyle(value, elem)
- elem._cstyle = value
- elem.ccascaded_style = None
- elem.cspecified_style = None
-
- cstyle = CStyleDescriptor()
-
- # Cached specified style property
- cstytags = shapetags | {SvgDocumentElement.ctag}
-
- def get_cspecified_style(self):
- """Returns the cached specified style, calculating it if not cached."""
- if not (hasattr(self, "_cspecified_style")):
- parent = self.getparent()
- if parent is not None and parent.tag in BaseElementCache.cstytags:
- ret = parent.cspecified_style + self.ccascaded_style
- else:
- ret = self.ccascaded_style
- if "font" in ret:
- ret = ret + BaseElementCache.font_shorthand(ret["font"])
- self._cspecified_style = ret
- return self._cspecified_style
-
- def set_cspecified_style(self, svi):
- """Invalidates the cached specified style."""
- if svi is None:
- try:
- delattr(self, "_cspecified_style")
- for k in list(self):
- k.cspecified_style = None # invalidate children
- except AttributeError:
- pass
-
- cspecified_style = property(get_cspecified_style, set_cspecified_style)
-
- shorthand_font_pattern = re.compile(
- r'^(?:(italic|oblique|normal)\s+)?' # font-style
- r'(?:(small-caps)\s+)?' # font-variant
- r'(?:(bold|bolder|lighter|\d{3})\s+)?' # font-weight
- r'(?:(ultra-condensed|extra-condensed|condensed|semi-condensed|normal|semi-expanded|expanded|extra-expanded|ultra-expanded)\s+)?' # font-stretch
- r'([0-9]+(?:\.[0-9]+)?(?:px|pt|em|rem|%)?)' # font-size (with optional decimal)
- r'(?:/([0-9]+(?:\.[0-9]+)?(?:px|pt|em|rem|%)?))?' # optional line-height
- r'\s+' # one or more spaces
- r'(.+)$' # the rest is font-family
- )
-
- @staticmethod
- def font_shorthand(fontstr):
- """ Parses a font shorthand into relevant properties """
- match = BaseElementCache.shorthand_font_pattern.match(fontstr)
- if match:
- font_style, font_variant, font_weight, font_stretch, font_size, line_height, font_family = match.groups()
- font_data = {
- "font-style": font_style,
- "font-variant": font_variant,
- "font-weight": font_weight,
- "font-stretch": font_stretch,
- "font-size": font_size,
- "line-height": line_height,
- "font-family": font_family.strip() if font_family else None,
- }
- ret = {k: v for k, v in font_data.items() if v is not None}
- else:
- ret = dict() # Invalid or unsupported font string
- return ret
-
- # Cached cascaded style property
- svgpres = {
- "alignment-baseline",
- "baseline-shift",
- "clip",
- "clip-path",
- "clip-rule",
- "color",
- "color-interpolation",
- "color-interpolation-filters",
- "color-profile",
- "color-rendering",
- "cursor",
- "direction",
- "display",
- "dominant-baseline",
- "enable-background",
- "fill",
- "fill-opacity",
- "fill-rule",
- "filter",
- "flood-color",
- "flood-opacity",
- "font-family",
- "font-size",
- "font-size-adjust",
- "font-stretch",
- "font-style",
- "font-variant",
- "font-weight",
- "glyph-orientation-horizontal",
- "glyph-orientation-vertical",
- "image-rendering",
- "kerning",
- "letter-spacing",
- "lighting-color",
- "marker-end",
- "marker-mid",
- "marker-start",
- "mask",
- "opacity",
- "overflow",
- "pointer-events",
- "shape-rendering",
- "stop-color",
- "stop-opacity",
- "stroke",
- "stroke-dasharray",
- "stroke-dashoffset",
- "stroke-linecap",
- "stroke-linejoin",
- "stroke-miterlimit",
- "stroke-opacity",
- "stroke-width",
- "text-anchor",
- "text-decoration",
- "text-rendering",
- "transform",
- "transform-origin",
- "unicode-bidi",
- "vector-effect",
- "visibility",
- "word-spacing",
- "writing-mode",
- }
- excludes = {"clip", "clip-path", "mask", "transform", "transform-origin"}
- style_atts = svgpres - excludes
-
- # raw dict update
- dict_update = Style.__bases__[0].update # type: ignore
-
- def get_cascaded_style(self):
- """Returns the cached cascaded (CSS) style, calculating it if not cached."""
- if not (hasattr(self, "_ccascaded_style")):
- # Local style (in "style" attribute)
- locsty = self.cstyle
- # CSS style (from stylesheet)
- csssty = getattr(self.croot, "cssdict", dict()).get(self.get_id())
- # Attribute style (from attributes other than "style")
- attsty = empty_style()
- BaseElementCache.dict_update(
- attsty,
- (
- {
- a: EBget(self, a)
- for a in self.attrib
- if a in BaseElementCache.style_atts
- }
- ),
- )
-
- if csssty is None:
- ret = attsty + locsty
- else:
- # Any style specified locally takes priority, followed by CSS,
- # followed by any attributes that the element has
- ret = attsty.add3(csssty, locsty)
- self._ccascaded_style = ret
- return self._ccascaded_style
-
- def set_ccascaded_style(self, svi):
- """Invalidates the cached cascaded style."""
- if svi is None:
- try:
- delattr(self, "_ccascaded_style")
- except AttributeError:
- pass
-
- ccascaded_style = property(get_cascaded_style, set_ccascaded_style)
-
- def get_ccomposed_transform(self):
- """
- Cached composed_transform, which can be invalidated by changes to
- transform of any ancestor.
- """
- if not (hasattr(self, "_ccomposed_transform")):
- myp = self.getparent()
- if myp is None:
- self._ccomposed_transform = self.ctransform
- else:
- self._ccomposed_transform = myp.ccomposed_transform @ self.ctransform
- return self._ccomposed_transform
-
- def set_ccomposed_transform(self, svi):
- """Invalidates the cached composed transform."""
- if svi is None and hasattr(self, "_ccomposed_transform"):
- delattr(self, "_ccomposed_transform") # invalidate
- for k in list2(self):
- k.ccomposed_transform = None # invalidate descendants
-
- ccomposed_transform = property(get_ccomposed_transform, set_ccomposed_transform)
-
- def get_ctransform(self):
- """
- Cached transform property
- Note: Can be None
- """
- if not (hasattr(self, "_ctransform")):
- self._ctransform = self.transform
- return self._ctransform
-
- def set_ctransform(self, newt):
- """Sets and caches a new transform, invalidating composed transforms."""
- self.transform = newt
- self._ctransform = newt
- self.ccomposed_transform = None
-
- ctransform = property(get_ctransform, set_ctransform)
-
- rect_tag = inkex.Rectangle.ctag
- round_tags = tags((inkex.Circle, inkex.Ellipse))
- line_tag = inkex.Line.ctag
- path_tag = inkex.PathElement.ctag
-
- def get_path2(self):
- """
- Cached get_path, modified to correctly calculate path for rectangles
- and ellipses. Caches Path of an object (set elem.cpath to None to reset)
- """
- if not hasattr(self, "_cpath"):
- # mostly from inkex.elements._polygons
- if self.tag == BaseElementCache.path_tag:
- ret = inkex.Path(self.get("d"))
- elif self.tag == BaseElementCache.rect_tag:
- left = ipx(self.get("x", "0"))
- top = ipx(self.get("y", "0"))
- width = ipx(self.get("width", "0"))
- height = ipx(self.get("height", "0"))
- rx = ipx(self.get("rx", self.get("ry", "0")))
- ry = ipx(self.get("ry", self.get("rx", "0")))
- right = left + width
- bottom = top + height
-
- # Calculate the path as the box around the rect
- if rx or ry:
- rx = min(rx if rx > 0 else ry, width / 2)
- ry = min(ry if ry > 0 else rx, height / 2)
- cpts = [left + rx, right - rx, top + ry, bottom - ry]
- return inkex.Path(
- f"M {cpts[0]},{top}"
- f"L {cpts[1]},{top} "
- f"A {rx},{ry} 0 0 1 {right},{cpts[2]}"
- f"L {right},{cpts[3]} "
- f"A {rx},{ry} 0 0 1 {cpts[1]},{bottom}"
- f"L {cpts[0]},{bottom} "
- f"A {rx},{ry} 0 0 1 {left},{cpts[3]}"
- f"L {left},{cpts[2]} "
- f"A {rx},{ry} 0 0 1 {cpts[0]},{top} z"
- )
-
- return inkex.Path(f"M {left},{top} h{width}v{height}h{-width} z")
-
- elif self.tag in BaseElementCache.round_tags:
- cx = ipx(self.get("cx", "0"))
- cy = ipx(self.get("cy", "0"))
- if isinstance(self, (inkex.Ellipse)):
- rx = ipx(self.get("rx", "0"))
- ry = ipx(self.get("ry", "0"))
- else: # circle
- rx = ipx(self.get("r", "0"))
- ry = ipx(self.get("r", "0"))
- ret = inkex.Path(
- (
- "M {cx},{y} "
- "a {rx},{ry} 0 1 0 {rx}, {ry} "
- "a {rx},{ry} 0 0 0 -{rx}, -{ry} z"
- ).format(cx=cx, y=cy - ry, rx=rx, ry=ry)
- )
-
- elif self.tag == BaseElementCache.line_tag: # updated in v1.2
- x1 = ipx(self.get("x1", "0"))
- y1 = ipx(self.get("y1", "0"))
- x2 = ipx(self.get("x2", "0"))
- y2 = ipx(self.get("y2", "0"))
- ret = inkex.Path(f"M{x1},{y1} L{x2},{y2}")
- else:
- ret = self.get_path()
- if isinstance(ret, str):
- ret = inkex.Path(ret)
- self._cpath = ret
-
- return self._cpath
-
- def set_cpath_fcn(self, svi):
- """Invalidates the cached path."""
- if svi is None and hasattr(self, "_cpath"):
- delattr(self, "_cpath") # invalidate
-
- cpath = property(get_path2, set_cpath_fcn)
- cpath_support = (
- inkex.Rectangle,
- inkex.Ellipse,
- inkex.Circle,
- inkex.Polygon,
- inkex.Polyline,
- inkex.Line,
- inkex.PathElement,
- )
-
- otp_support = cpath_support
- otp_support_tags = tags(cpath_support)
- otp_support_prop = property(lambda x: BaseElementCache.otp_support_tags)
- ptag = inkex.PathElement.ctag
-
- def object_to_path(self):
- """
- Converts specified elements to paths if supported. Adjusts the 'd'
- attribute and changes the element tag to a path.
- Only operates on elements within the `otp_support_tags` list.
- """
- if (
- self.tag in BaseElementCache.otp_support_tags
- and not self.tag == BaseElementCache.ptag
- ):
- self.set("d", str(self.cpath)) # do this first so cpath is correct
- self.tag = BaseElementCache.ptag
-
- # Cached root property
- svgtag = SvgDocumentElement.ctag
-
- def get_croot(self):
- """Returns the cached root of the SVG document."""
- try:
- return self._croot
- except AttributeError:
- if self.getparent() is not None:
- self._croot = self.getparent().croot
- elif self.tag == BaseElementCache.svgtag:
- self._croot = self
- else:
- self._croot = None
- return self._croot
-
- def set_croot(self, svi):
- """Sets the cached root of the SVG document."""
- self._croot = svi
-
- croot = property(get_croot, set_croot)
-
- # Version of set_random_id that uses cached root
- def set_random_id(
- self,
- prefix: Optional[str] = None,
- size: Optional[int] = None,
- backlinks: bool = False,
- blacklist: Optional[List[str]] = None,
- ):
- """Assigns a unique ID to the element, using the cached root."""
- prefix = str(self) if prefix is None else prefix
- self.set_id(
- self.croot.get_unique_id(prefix, size=size, blacklist=blacklist),
- backlinks=backlinks,
- )
-
- def get_id(self, as_url=0):
- """
- Version of get_id that uses the low-level get
- """
- if "id" not in self.attrib:
- self.set_random_id(self.TAG)
- eid = EBget(self, "id")
- if as_url > 0:
- eid = "#" + eid
- if as_url > 1:
- eid = f"url({eid})"
- return eid
-
- def set_id(self, new_id, backlinks=False):
- """Set the id and update backlinks to xlink and style urls if needed"""
- if self.croot is not None:
- self.croot.iddict[new_id] = self
- BE_set_id(self, new_id, backlinks=backlinks)
-
- comment_tag = lxml.etree.Comment("").tag
-
- def descendants2(self, return_tails=False):
- """
- A version of descendants that also returns a list of elements whose tails
- precede each element. (This is helpful for parsing text.)
- """
- if not return_tails:
- return [self] + [
- d
- for d in self.iterdescendants()
- if d.tag != BaseElementCache.comment_tag
- ]
- descendants = [self]
- precedingtails = [[]]
- endsat = [(self, None)]
- for ddv in self.iterdescendants():
- if not (ddv.tag == BaseElementCache.comment_tag):
- precedingtails.append([])
- while endsat[-1][1] == ddv:
- precedingtails[-1].append(endsat.pop()[0])
- nsib = ddv.getnext()
- if nsib is None:
- endsat.append((ddv, endsat[-1][1]))
- else:
- endsat.append((ddv, nsib))
- descendants.append(ddv)
-
- precedingtails.append([])
- while len(endsat) > 0:
- precedingtails[-1].append(endsat.pop()[0])
- return descendants, precedingtails
-
- def ancestors2(self, includeme=False, stopbefore=None, stopafter=None):
- """Version of ancestors that can stop before/after encountering an element"""
- anc = []
- cel = self if includeme else self.getparent()
- while cel is not None and cel != stopbefore:
- anc.append(cel)
- if cel == stopafter:
- break
- cel = cel.getparent()
- return anc
-
- ## Bookkeeping functions
- # When BaseElements are deleted, created, or moved, the caches need to be
- # updated or invalidated. These functions do that while preserving the original
- # base functionality
-
- # Deletion
- BE_delete = inkex.BaseElement.delete
-
- def delete(self, deleteup=False):
- """Deletes the element and optionally cleans up empty parent groups."""
- svg = self.croot
- for ddv in reversed(self.descendants2()):
- did = ddv.get_id()
- if svg is not None:
- try:
- svg.ids.remove(did)
- except (KeyError, AttributeError):
- pass
- svg.iddict.remove(ddv)
- ddv.croot = None
- if hasattr(svg, "_cd2"):
- svg.cdescendants2.delel(self)
-
- if deleteup:
- # If set, remove empty ancestor groups
- myp = self.getparent()
- BaseElementCache.BE_delete(self)
- if myp is not None and len(myp) == 0:
- BaseElementCache.delete(myp, True)
- else:
- BaseElementCache.BE_delete(self)
-
- # Insertion
- # BE_insert = inkex.BaseElement.insert
- BE_insert = lxml.etree.ElementBase.insert
-
- def insert(self, index, elem):
- """Inserts an element at a specified index, managing caching."""
- oldroot = elem.croot
- newroot = self.croot
-
- BaseElementCache.BE_insert(self, index, elem)
- elem.ccascaded_style = None
- elem.cspecified_style = None
- elem.ccomposed_transform = None
-
- if EBget(elem, "id") is None:
- # Make sure all elements have an ID (most assigned here)
- elem.croot = newroot
- if newroot is not None:
- newroot.iddict.add(elem) # generates an ID if needed
- for k in list2(elem):
- elem.append(k) # update children
- elif oldroot != newroot:
- # When the root is changing, remove from old dicts and add to new
- css = None
- if oldroot is not None:
- oldroot.iddict.remove(elem)
- css = oldroot.cssdict.pop(elem.get_id(), None)
- elem.croot = newroot
- if newroot is not None:
- newroot.iddict.add(elem) # generates an ID if needed
- if css is not None:
- newroot.cssdict[elem.get_id()] = css
- for k in list2(elem):
- elem.append(k) # update children
-
- # Appending
- # BE_append = inkex.BaseElement.append
- BE_append = lxml.etree.ElementBase.append
-
- def append(self, elem):
- """Appends an element, managing caching and ID conflicts."""
- oldroot = elem.croot
- newroot = self.croot
-
- BaseElementCache.BE_append(self, elem)
- elem.ccascaded_style = None
- elem.cspecified_style = None
- elem.ccomposed_transform = None
-
- if EBget(elem, "id") is None:
- # Make sure all elements have an ID (most assigned here)
- elem.croot = newroot
- if newroot is not None:
- newroot.iddict.add(elem) # generates an ID if needed
- for k in list2(elem):
- elem.append(k) # update children
- elif oldroot != newroot:
- # When the root is changing, remove from old dicts and add to new
- css = None
- if oldroot is not None:
- oldroot.iddict.remove(elem)
- css = oldroot.cssdict.pop(elem.get_id(), None)
- elem.croot = newroot
- if newroot is not None:
- newroot.iddict.add(elem) # generates an ID if needed
- if css is not None:
- newroot.cssdict[elem.get_id()] = css
- for k in list2(elem):
- elem.append(k) # update children
-
- # addnext
- # BE_addnext = inkex.BaseElement.addnext
- BE_addnext = lxml.etree.ElementBase.addnext
-
- def addnext(self, elem):
- """Adds an element next to the specified element, managing caching."""
- oldroot = elem.croot
- newroot = self.croot
-
- BaseElementCache.BE_addnext(self, elem)
- elem.ccascaded_style = None
- elem.cspecified_style = None
- elem.ccomposed_transform = None
-
- if EBget(elem, "id") is None:
- # Make sure all elements have an ID (most assigned here)
- elem.croot = newroot
- if newroot is not None:
- newroot.iddict.add(elem) # generates an ID if needed
- for k in list2(elem):
- elem.append(k) # update children
- elif oldroot != newroot:
- # When the root is changing, remove from old dicts and add to new
- css = None
- if oldroot is not None:
- oldroot.iddict.remove(elem)
- css = oldroot.cssdict.pop(elem.get_id(), None)
- elem.croot = newroot
- if newroot is not None:
- newroot.iddict.add(elem) # generates an ID if needed
- if css is not None:
- newroot.cssdict[elem.get_id()] = css
- for k in list2(elem):
- elem.append(k) # update children
-
- # Duplication
- clipmasktags = {inkex.addNS("mask", "svg"), inkex.ClipPath.ctag}
-
- def duplicate(self):
- """Creates a duplicate of the element, managing ID and caching."""
- eltail = self.tail
- if eltail is not None:
- self.tail = None
- dup = self.copy()
-
- # After copying, duplicates have the original ID. Remove prior to insertion
- origids = dict()
- for dupd in dup.descendants2():
- origids[dupd] = dupd.pop("id", None)
- dupd.croot = self.croot # set now for speed
-
- self.addnext(dup)
- if eltail is not None:
- self.tail = eltail
- # Fix tail bug: https://gitlab.com/inkscape/extensions/-/issues/480
-
- css = self.croot.cssdict
- newids = {
- EBget(k, "id"): css.get(origids[k])
- for k in dup.descendants2()
- if origids[k] in css
- }
- dup.croot.cssdict.update(newids)
-
- if dup.tag in BaseElementCache.clipmasktags:
- # Clip duplications can cause weird issues if they are not appended
- # to the end of Defs
- dup.croot.cdefs.append(dup)
- return dup
-
- def get_parsed_text(self):
- """Add parsed_text property to text, which is used to get the
- properties of text"""
- if not (hasattr(self, "_parsed_text")):
- self._parsed_text = ParsedText(self, self.croot.char_table)
- return self._parsed_text
-
- def set_parsed_text(self, svi):
- """Invalidates the cached parsed text."""
- if hasattr(self, "_parsed_text") and svi is None:
- delattr(self, "_parsed_text")
-
- parsed_text = property(get_parsed_text, set_parsed_text)
-
-
-class SvgDocumentElementCache(SvgDocumentElement):
- """Adds caching for SVG document elements."""
-
- def cdefs_func(self):
- """Cached def directly under root"""
- if not (hasattr(self, "_cdefs")):
- for k in list(self):
- if isinstance(k, (inkex.Defs)):
- self._cdefs = k
- return self._cdefs
- defel = inkex.Defs()
- self.insert(0, defel)
- self._cdefs = defel
- return self._cdefs
-
- cdefs = property(cdefs_func)
-
- styletag = inkex.addNS("style", "svg")
-
- def crootsty_func(self):
- """Cached style element directly under root"""
- if not hasattr(self, "_crootsty"):
- rootstys = [
- sty for sty in list(self) if sty.tag == SvgDocumentElementCache.styletag
- ]
- if len(rootstys) == 0:
- self._crootsty = inkex.StyleElement()
- self.insert(0, self._crootsty)
- else:
- self._crootsty = rootstys[0]
- return self._crootsty
-
- crootsty = property(crootsty_func)
-
- def get_ids(self):
- """Version of get_ids that uses iddict"""
- return set(self.iddict.keys())
-
- def get_unique_id(
- self,
- prefix: str,
- size: Optional[int] = None,
- blacklist: Optional[List[str]] = None,
- ):
- """Version of get_unique_id that removes randomness by keeping a
- running count"""
- new_id = None
- cnt = self.iddict.prefixcounter.get(prefix, 0)
- if blacklist is None:
- while new_id is None or new_id in self.iddict:
- new_id = prefix + str(cnt)
- cnt += 1
- else:
- while new_id is None or new_id in self.iddict or new_id in blacklist:
- new_id = prefix + str(cnt)
- cnt += 1
- self.iddict.prefixcounter[prefix] = cnt
- return new_id
-
- # Repeated getElementById lookups can be slow, so instead create a cached
- # iddict property. When an element is created that may be needed later,
- # it should be added using self.iddict.add.
- urlpat = re.compile(r"^url\(#(.*)\)$|^#")
-
- def getElementById(self, eid: str, elm="*", literal=False):
- """Returns an element by ID using cached data for efficiency."""
- if eid is not None and not literal:
- eid = SvgDocumentElementCache.urlpat.sub(r"\1", eid.strip())
- return self.iddict.get(eid)
-
- class IDDict(dict):
- """Keeps track of the IDs in a document"""
-
- def __init__(self, svg):
- super().__init__()
- self.svg = svg
- self.prefixcounter = dict()
- toassign = []
- for elem in svg.descendants2():
- if "id" in elem.attrib:
- self[EBget(elem, "id")] = elem
- else:
- toassign.append(elem)
- elem.croot = svg # do now to speed up later
- for elem in toassign:
- # Reduced version of get_unique_id_fcn
- # Cannot call it since it relies on iddict
- prefix = elem.TAG
- new_id = None
- cnt = self.prefixcounter.get(prefix, 0)
- while new_id is None or new_id in self:
- new_id = prefix + str(cnt)
- cnt += 1
- self.prefixcounter[prefix] = cnt
- # Reduced version of set_id
- self[new_id] = elem
- BE_set_id(elem, new_id)
-
- def add(self, elem):
- """Add an element to the ID dict"""
- elid = (
- elem.get_id()
- ) # fine since elem should have a croot to get called here
- if elid in self and not self[elid] == elem:
- # Make a new id when there's a conflict
- elem.set_random_id(elem.TAG)
- elid = elem.get_id()
- self[elid] = elem
-
- @property
- def descendants(self):
- """all svg descendants, not necessarily in order"""
- return list(self.values())
-
- def remove(self, elem):
- """Remove from ID dict"""
- elid = elem.get_id()
- if elid in self:
- del self[elid]
-
- @property
- def iddict(self):
- """Returns the ID dictionary that caches all elements by ID."""
- if not (hasattr(self, "_iddict")):
- self._iddict = SvgDocumentElementCache.IDDict(self)
- return self._iddict
-
- estyle = Style() # keep separate in case Style was overridden
-
- # Check if v1.4 or later
- hasmatches = hasattr(inkex.styles.ConditionalStyle, "matches")
-
- class CSSDict(dict):
- """A dict that keeps track of the CSS style for each element"""
-
- def __init__(self, svg):
- self.svg = svg
-
- # For certain xpaths such as classes, we can avoid xpath calls
- # by checking the class attributes on a document's descendants directly.
- # This is much faster for large documents.
- hasall = False
- simpleclasses = dict()
- simpleids = dict()
- patcls = re.compile(r"\.([-\w]+)")
- patid = re.compile(r"#(\w+)")
-
- for sheet in svg.stylesheets:
- for style in sheet:
- rules = tuple(str(r) for r in style.rules)
- # inkex.utils.debug(rules)
- if rules == ("*",):
- hasall = True
- elif all(patcls.match(r) for r in rules):
- # all rules are classes
- simpleclasses[rules] = [patcls.sub(r"\1", r) for r in rules]
- elif all(patid.match(r) for r in rules):
- # all rules are ids
- simpleids[rules] = [patid.sub(r"\1", r) for r in rules]
-
- # Now, we make a dictionary of rules / xpaths we can do easily
- knownrules = dict()
- if hasall or len(simpleclasses) > 0:
- dds = svg.iddict.descendants
- if hasall:
- knownrules[("*",)] = dds
- cvs = [EBget(d, "class") for d in dds]
- c2s = [(i, c) for (i, c) in enumerate(cvs) if c is not None]
- cmatches = dict()
- for i, clsval in c2s:
- for c in clsval.split(" "):
- cmatches[c] = cmatches.get(c, []) + [dds[i]]
- kxp2 = {
- rules: list(
- dict.fromkeys(
- [d for c in clslist if c in cmatches for d in cmatches[c]]
- )
- )
- for rules, clslist in simpleclasses.items()
- }
- knownrules.update(kxp2)
-
- for rules, ids in simpleids.items():
- knownrules[rules] = []
- for idv in ids:
- idel = svg.getElementById(idv)
- if idel is not None:
- knownrules[rules].append(idel)
-
- # Now run any necessary xpaths and get the element styles
- super().__init__()
- for sheet in svg.croot.stylesheets:
- for style in sheet:
- try:
- # els = svg.xpath(style.to_xpath()) # original code
- rules = tuple(str(r) for r in style.rules)
- stylev = Style(style)
- if rules in knownrules:
- els = knownrules[rules]
- else:
- if SvgDocumentElementCache.hasmatches:
- els = style.all_matches(svg)
- else:
- els = svg.xpath(style.to_xpath())
-
- if len(stylev) > 0:
- idvs = [
- EBget(elem, "id", None)
- for elem in els
- if "id" in elem.attrib
- ]
- newstys = {
- elid: stylev
- if elid not in self
- else self[elid] + stylev
- for elid in idvs
- }
- # Technically we should copy stylev, but as long as
- # styles in cssdict are only used in
- # get_cascaded_style, this is fine since that
- # function adds (creating copies)
- self.update(newstys)
- except (lxml.etree.XPathEvalError,):
- pass
-
- def dupe_entry(self, oldid, newid):
- """Duplicate entry in cssdict"""
- csssty = self.get(oldid)
- if csssty is not None:
- self[newid] = csssty
-
- def get_cssdict(self):
- """Returns the CSS dictionary that caches styles by element ID."""
- try:
- return self._cssdict
- except AttributeError:
- self._cssdict = SvgDocumentElementCache.CSSDict(self)
- return self._cssdict
-
- cssdict = property(get_cssdict)
-
- def document_size(self):
- """Calculates and caches the size of the SVG document in various units."""
- if not (hasattr(self, "_cdocsize")):
- rvb = self.get_viewbox()
- wstr = self.get("width")
- hstr = self.get("height")
-
- if rvb == [0, 0, 0, 0]:
- wval = ipx(wstr) if '%' not in wstr else 300
- hval = ipx(hstr) if '%' not in hstr else 150
- vbx = [0, 0, wval, hval]
- else:
- vbx = [float(v) for v in rvb] # just in case
-
- # Get document width and height in pixels
- wvl, wun = (
- inkex.units.parse_unit(wstr) if wstr is not None else (vbx[2], "px")
- )
- hvl, hun = (
- inkex.units.parse_unit(hstr) if hstr is not None else (vbx[3], "px")
- )
-
- def parse_preserve_aspect_ratio(par_str):
- align, meet_or_slice = "xMidYMid", "meet" # defaults
- valigns = [
- "xMinYMin",
- "xMidYMin",
- "xMaxYMin",
- "xMinYMid",
- "xMidYMid",
- "xMaxYMid",
- "xMinYMax",
- "xMidYMax",
- "xMaxYMax",
- "none",
- ]
- vmoss = ["meet", "slice"]
- if par_str:
- values = par_str.split(" ")
- if len(values) == 1:
- if values[0] in valigns:
- align = values[0]
- elif values[0] in vmoss:
- meet_or_slice = values[0]
- elif len(values) == 2:
- if values[0] in valigns and values[1] in vmoss:
- align = values[0]
- meet_or_slice = values[1]
- return align, meet_or_slice
-
- align, meet_or_slice = parse_preserve_aspect_ratio(
- self.get("preserveAspectRatio")
- )
-
- xfr = (
- inkex.units.convert_unit(str(wvl) + " " + wun, "px") / vbx[2]
- if wun != "%"
- else wvl / 100
- ) # width of uu in px pre-stretch
- yfr = (
- inkex.units.convert_unit(str(hvl) + " " + hun, "px") / vbx[3]
- if hun != "%"
- else hvl / 100
- ) # height of uu in px pre-stretch
- if align != "none":
- mfr = min(xfr, yfr) if meet_or_slice == "meet" else max(xfr, yfr)
- xmf = {"xMin": 0, "xMid": 0.5, "xMax": 1}[align[0:4]]
- ymf = {"YMin": 0, "YMid": 0.5, "YMax": 1}[align[4:]]
- vbx[0], vbx[2] = (
- vbx[0] + vbx[2] * (1 - xfr / mfr) * xmf,
- vbx[2] / mfr * (xfr if wun != "%" else 1),
- )
- vbx[1], vbx[3] = (
- vbx[1] + vbx[3] * (1 - yfr / mfr) * ymf,
- vbx[3] / mfr * (yfr if hun != "%" else 1),
- )
- if wun == "%":
- wvl, wun = vbx[2] * mfr, "px"
- if hun == "%":
- hvl, hun = vbx[3] * mfr, "px"
- else:
- if wun == "%":
- wvl, wun, vbx[2] = vbx[2], "px", vbx[2] / xfr
- if hun == "%":
- hvl, hun, vbx[3] = vbx[3], "px", vbx[3] / yfr
-
- wpx = inkex.units.convert_unit(
- str(wvl) + " " + wun, "px"
- ) # document width in px
- hpx = inkex.units.convert_unit(
- str(hvl) + " " + hun, "px"
- ) # document height in px
- uuw = wpx / vbx[2] # uu width in px (px/uu)
- uuh = hpx / vbx[3] # uu height in px (px/uu)
- uupx = uuw if abs(uuw - uuh) < 0.001 else None # uu size in px (px/uu)
- # should match Scale in Document Properties
-
- # Get Pages
- nvs = [elem for elem in list(self) if isinstance(elem, inkex.NamedView)]
- pgs = [
- elem
- for nv in nvs
- for elem in list(nv)
- if elem.tag == inkex.addNS("page", "inkscape")
- ]
- for pgv in pgs:
- pgv.bbuu = [
- ipx(pgv.get("x")),
- ipx(pgv.get("y")),
- ipx(pgv.get("width")),
- ipx(pgv.get("height")),
- ]
- pgv.bbpx = [
- pgv.bbuu[0] * xfr,
- pgv.bbuu[1] * yfr,
- pgv.bbuu[2] * xfr,
- pgv.bbuu[3] * yfr,
- ]
-
- class DocSize:
- """Contains the properties relating to document size"""
-
- def __init__(
- self,
- effvb,
- uuw,
- uuh,
- uupx,
- wunit,
- hunit,
- wpx,
- hpx,
- xfr,
- yfr,
- pgs,
- ):
- self.effvb = effvb
- self.uuw = uuw
- self.uuh = uuh
- self.uupx = uupx
- self.wunit = wunit
- self.hunit = hunit
- self.wpx = wpx
- self.hpx = hpx
- self.rawxf = xfr
- self.rawyf = yfr
- self.pgs = pgs
-
- def uutopx(self, x):
- """Converts a bounding box specified in uu to pixels"""
- ret = [
- (x[0] - self.effvb[0]) * self.uuw,
- (x[1] - self.effvb[1]) * self.uuh,
- x[2] * self.uuw,
- x[3] * self.uuh,
- ]
- return ret
-
- def pxtouu(self, x):
- """Converts a bounding box specified in pixels to uu"""
- ret = [
- x[0] / self.uuw + self.effvb[0],
- x[1] / self.uuh + self.effvb[1],
- x[2] / self.uuw,
- x[3] / self.uuh,
- ]
- return ret
-
- def unittouu(self, x):
- """Converts any unit into uu"""
- return (
- inkex.units.convert_unit(x, "px") / self.uupx
- if self.uupx is not None
- else None
- )
-
- def uutopxpgs(self, x):
- """Version that applies to Pages"""
- return [
- x[0] * self.rawxf,
- x[1] * self.rawyf,
- x[2] * self.rawxf,
- x[3] * self.rawyf,
- ]
-
- def pxtouupgs(self, x):
- """Version that applies to Pages"""
- return [
- x[0] / self.rawxf,
- x[1] / self.rawyf,
- x[2] / self.rawxf,
- x[3] / self.rawyf,
- ]
-
- self._cdocsize = DocSize(
- vbx, uuw, uuh, uupx, wun, hun, wpx, hpx, xfr, yfr, pgs
- )
- return self._cdocsize
-
- def set_cdocsize(self, svi):
- """Invalidates the cached document size."""
- if svi is None and hasattr(self, "_cdocsize"): # invalidate
- delattr(self, "_cdocsize")
-
- cdocsize = property(document_size, set_cdocsize)
-
- def set_viewbox(self, newvb):
- """Sets a new viewbox for the SVG, updating dimensions appropriately."""
- uuw, uuh, wunit, hunit = (
- self.cdocsize.uuw,
- self.cdocsize.uuh,
- self.cdocsize.wunit,
- self.cdocsize.hunit,
- )
- self.set(
- "width",
- str(inkex.units.convert_unit(str(newvb[2] * uuw) + "px", wunit)) + wunit,
- )
- self.set(
- "height",
- str(inkex.units.convert_unit(str(newvb[3] * uuh) + "px", hunit)) + hunit,
- )
- self.set("viewBox", " ".join([str(v) for v in newvb]))
- self.cdocsize = None
-
- def standardize_viewbox(self):
- """Converts viewbox to pixels, removing any non-uniform scaling appropriately"""
- pgbbs = [pgv.bbpx for pgv in self.cdocsize.pgs]
- self.set("viewBox", " ".join([str(v) for v in self.cdocsize.effvb]))
- self.set("width", str(self.cdocsize.wpx))
- self.set("height", str(self.cdocsize.hpx))
-
- # Update Pages appropriately
- self.cdocsize = None
- for i, pgv in enumerate(self.cdocsize.pgs):
- newbbuu = self.cdocsize.pxtouupgs(pgbbs[i])
- pgv.set("x", str(newbbuu[0]))
- pgv.set("y", str(newbbuu[1]))
- pgv.set("width", str(newbbuu[2]))
- pgv.set("height", str(newbbuu[3]))
-
- # Add char_table property to SVGs, which are used to collect all of the
- # properties of fonts in the document. Alternatively, calling make_char_table
- # on a subset of elements will cause only those elements to be included.
-
- def make_char_table(self, els=None):
- """
- Can be called with els argument to examine list of elements only
- (otherwise use entire SVG)
- """
- ttags = tags((inkex.TextElement, inkex.FlowRoot))
- if els is None:
- tels = [d for d in self.iddict.descendants if d.tag in ttags]
- else:
- tels = [d for d in els if d.tag in ttags]
- if not (hasattr(self, "_char_table")) or any(
- t not in getattr(self, "_char_table").els for t in tels
- ):
- self._char_table = CharacterTable(tels)
-
- def get_char_table(self):
- """Returns the cached character table, creating it if necessary."""
- if not (hasattr(self, "_char_table")):
- self.make_char_table()
- return self._char_table
-
- def set_char_table(self, svi):
- """Invalidates the cached character table."""
- if svi is None and hasattr(self, "_char_table"):
- delattr(self, "_char_table")
-
- char_table = property(get_char_table, set_char_table)
-
-
-class StyleCache(Style):
- """Caches and manages style data with enhanced functionality."""
-
- get_link = get_link_fcn
-
- def __hash__(self):
- """Generates a hash based on the style items."""
- return hash(tuple(self.items())) # type: ignore
-
- # Adds three styles
- def add3_fcn(self, y, z):
- """Adds three style objects together."""
- return self + y + z
-
- add3 = add3_fcn if not hasattr(Style, "add3") else getattr(Style, "add3")
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/font_properties.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/font_properties.py
deleted file mode 100644
index 10f2a66..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/font_properties.py
+++ /dev/null
@@ -1,1317 +0,0 @@
-# coding=utf-8
-#
-# Copyright (c) 2023 David Burghoff
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-"""
-Some functions for getting the properties of fonts and characters.
-Three libraries are used:
- fontconfig: Used for discovering fonts based on the CSS (SVG) style. This
- uses Inkscape's libfontconfig, so it always matches what Inkscape
- does.
- fonttools: Gets font properties once discovered (from font's filename)
- Pango: Used to render test characters (included with GTK)
- Sets up a blank GTK window and renders Pango text, reusing
- the same layout for all rendering.
-"""
-
-import os
-import warnings
-import sys
-import re
-import ctypes
-from unittest.mock import patch
-from functools import lru_cache
-import inkex
-from inkex.text.utils import default_style_atts
-from inkex import Style
-
-# The fontconfig library is used to select a font given its CSS specs
-# This library should work starting with v1.0
-
-# Due to the way fontconfig is structured, we have to patch
-# ctypes's LoadLibrary to help it find libfontconfig
-original_load_library = ctypes.cdll.LoadLibrary
-
-
-def custom_load_library(name):
- """
- Custom function to load a library based on the platform.
- """
- if name in ["libfontconfig.so.1", "libfreetype.so.6"]:
- libname = {
- "linux": {
- "libfreetype.so.6": "libfreetype.so.6",
- "libfontconfig.so.1": "libfontconfig.so.1",
- },
- "openbsd6": {
- "libfreetype.so.6": "libfreetype.so.28",
- "libfontconfig.so.1": "libfontconfig.so.11",
- },
- "darwin": {
- "libfreetype.so.6": "libfreetype.6.dylib",
- "libfontconfig.so.1": "libfontconfig.1.dylib",
- },
- "win32": {
- "libfreetype.so.6": "libfreetype-6.dll",
- "libfontconfig.so.1": "libfontconfig-1.dll",
- },
- }[sys.platform][name]
-
- if "SI_FC_DIR" in os.environ:
- fpath = os.path.abspath(os.path.join(os.environ["SI_FC_DIR"], libname))
- ret = original_load_library(fpath)
- else:
- try:
- ret = original_load_library(libname)
- except FileNotFoundError:
- biloc = inkex.inkscape_system_info.binary_location # type: ignore
- blocdir = os.path.dirname(biloc)
- fpath = os.path.abspath(os.path.join(blocdir, libname))
- ret = original_load_library(fpath)
- return ret
- if name == "libc.so.6":
- # Do not need to load, return a blank class consistent with fontconfig
- libc = type("libc", (object,), {"free": staticmethod(lambda ptr: None)})()
- libc.free.argtypes = (ctypes.c_void_p,)
- return libc
- return original_load_library(name)
-
-
-with patch("ctypes.cdll.LoadLibrary", side_effect=custom_load_library):
- import fontconfig as fc # pylint: disable=import-error
-
- FC = fc.FC
-
-
-def isnumeric(strv):
- """
- Check if a string can be converted to a number
- """
- try:
- float(strv)
- return True
- except ValueError:
- return False
-
-
-def interpolate_dict(dictv, x, defaultval):
- """
- Interpolate an input over the numerical values of a dict
- Supports both string and int inputs and outputs
- If the dict's values are enums, pick the nearest value
- """
- # Check for exact match first
- if x in dictv:
- return dictv[x]
-
- # Interpolate numerical values
- if isnumeric(x):
- input_value = int(x)
- numerical_keys = sorted([int(k) for k in dictv.keys() if isnumeric(k)])
- for k, val in dictv.items():
- if isnumeric(k):
- ktype = type(k) # int or str
- vtype = type(val) # int or str
-
- if vtype not in [int, float, str]: # enums
- # Pick the nearest entry
- intd = {int(k): v for k, v in dictv.items() if isnumeric(k)}
- return intd[min(intd.keys(), key=lambda x: abs(x - int(x)))]
- break
- if input_value <= numerical_keys[0]:
- return dictv[ktype(numerical_keys[0])]
- if input_value >= numerical_keys[-1]:
- return dictv[ktype(numerical_keys[-1])]
- for i in range(len(numerical_keys) - 1):
- if numerical_keys[i] <= input_value <= numerical_keys[i + 1]:
- lower_key = numerical_keys[i]
- upper_key = numerical_keys[i + 1]
- lower_value = float(dictv[ktype(lower_key)])
- upper_value = float(dictv[ktype(upper_key)])
- ratio = (input_value - lower_key) / (upper_key - lower_key)
- interpolated_value = lower_value + ratio * (upper_value - lower_value)
- if vtype in [int, float]:
- return interpolated_value
- return f"{interpolated_value:g}"
- return defaultval
-
-
-# Windows doesn't have the XDG_DATA_HOME directory set, which is
-# needed for /etc/fonts.conf to find the user fonts directory:
-# fonts
-# Needed for fontconfig
-# Set it based on the location of preferences.xml
-if sys.platform == "win32":
- if os.environ.get("XDG_DATA_HOME") is None:
- os.environ["XDG_DATA_HOME"] = os.path.dirname(
- inkex.inkscape_system_info.preferences # type: ignore
- )
-
-
-class FontConfig:
- """
- Class to handle FontConfig functionalities.
- """
-
- def __init__(self):
- self.truefonts = dict() # css
- self.truefontsfc = dict() # fontconfig
- self.truefontsfn = dict() # fullnames
- self.truefontsft = dict() # fonttools
- self.fontcharsets = dict()
- self.disable_lcctype()
- self.conf = fc.Config.get_current()
- self._font_list = None
- self._font_list_css = None
-
- def disable_lcctype(self):
- """
- Disables LC_CTYPE to suppress Mac warnings.
- """
- self.lcctype = os.environ.get("LC_CTYPE")
- if self.lcctype is not None and sys.platform == "darwin":
- del os.environ["LC_CTYPE"] # suppress Mac warning
-
- def enable_lcctype(self):
- """
- Enables LC_CTYPE if it was previously disabled.
- """
- if self.lcctype is not None and sys.platform == "darwin":
- os.environ["LC_CTYPE"] = self.lcctype
-
- def get_true_font(self, fontsty):
- """Use fontconfig to get the true font that most text will be rendered as"""
- if fontsty not in self.truefonts:
- found = self.font_match(fontsty)
- truefont = FontConfig.fcfont_to_css(found)
- self.truefonts[fontsty] = truefont
- self.truefontsfc[fontsty] = found
- self.fontcharsets[truefont] = found.get(fc.PROP.CHARSET, 0)[0]
- return self.truefonts[fontsty]
-
- def get_true_font_fullname(self, fontsty):
- """Use fontconfig to get the Face name for a font style"""
- if fontsty not in self.truefontsfn:
- if fontsty not in self.truefontsfc:
- self.get_true_font(fontsty)
- self.truefontsfn[fontsty] = self.truefontsfc[fontsty].get(
- fc.PROP.FULLNAME, 0
- )[0]
- return self.truefontsfn[fontsty]
-
- def get_true_font_by_char(self, fontsty, chars):
- """
- Sometimes, a font will not have every character and a different one is
- substituted. (For example, many fonts do not have the ⎣ character.)
- Gets the true font by character
- """
- if fontsty not in self.truefonts:
- # font_match is more reliable at matching Inkscape than font_sort
- self.get_true_font(fontsty)
- truefont = self.truefonts[fontsty]
- cd1 = {k: truefont for k in chars if ord(k) in self.fontcharsets[truefont]}
-
- if len(cd1) < len(chars):
- found = self.font_sort(fontsty)
- for fnt in found:
- truefont = FontConfig.fcfont_to_css(fnt)
- if truefont not in self.fontcharsets:
- self.fontcharsets[truefont] = fnt.get(fc.PROP.CHARSET, 0)[0]
- cset = self.fontcharsets[truefont]
- cd2 = {k: truefont for k in chars if ord(k) in cset and k not in cd1}
- cd1.update(cd2)
- if len(cd1) == len(chars):
- break
- if len(cd1) < len(chars):
- cd1.update({c: None for c in chars if c not in cd1})
- return cd1
-
- @lru_cache(maxsize=None)
- def font_match(self, fontsty):
- """
- fc fonts matching a given reduced font style
- """
- pat = FontConfig.css_to_fcpattern(fontsty)
- self.conf.substitute(pat, FC.MatchPattern)
- pat.default_substitute()
- ret, _ = self.conf.font_match(pat)
- return ret
-
- @lru_cache(maxsize=None)
- def font_sort(self, fontsty):
- """
- List of fc fonts closest to a given reduced font style
- """
- pat = FontConfig.css_to_fcpattern(fontsty)
- self.conf.substitute(pat, FC.MatchPattern)
- pat.default_substitute()
- ret, _, _ = self.conf.font_sort(pat, trim=True, want_coverage=False)
- return ret
-
- def get_fonttools_font(self, fontsty):
- """
- Get a FontTools font instance based on the reduced style.
- """
- if fontsty not in self.truefontsft:
- if fontsty not in self.truefontsfc:
- self.get_true_font(fontsty)
- found = self.truefontsfc[fontsty]
- self.truefontsft[fontsty] = FontToolsFontInstance(found)
- return self.truefontsft[fontsty]
-
- @staticmethod
- def css_to_fcpattern(sty):
- """Convert a style dictionary to an fc search pattern"""
- pat = fc.Pattern.name_parse(
- re.escape(sty["font-family"].replace("'", "").replace('"', ""))
- )
- pat.add(
- fc.PROP.WIDTH,
- C.CSSSTR_FCWDT.get(sty.get("font-stretch"), FC.WIDTH_NORMAL),
- )
- pat.add(
- fc.PROP.WEIGHT,
- interpolate_dict(C.CSSWGT_FCWGT, sty.get("font-weight"), FC.WEIGHT_NORMAL),
- )
- pat.add(
- fc.PROP.SLANT, C.CSSSTY_FCSLN.get(sty.get("font-style"), FC.SLANT_ROMAN)
- )
- return pat
-
- @staticmethod
- def fcfont_to_css(fnt):
- """Convert an fc font to a Style"""
- # For CSS, enclose font family in single quotes
- # Needed for fonts like Modern No. 20 with periods in the family
- fcfam = fnt.get(fc.PROP.FAMILY, 0)[0]
- fcwgt = fnt.get(fc.PROP.WEIGHT, 0)[0]
- fcsln = fnt.get(fc.PROP.SLANT, 0)[0]
- fcwdt = fnt.get(fc.PROP.WIDTH, 0)[0]
- if any(isinstance(v, tuple) for v in [fcfam, fcwgt, fcsln, fcwdt]):
- return None
- return Style(
- [
- ("font-family", "'" + fcfam.strip("'") + "'"),
- ("font-weight", interpolate_dict(C.FCWGT_CSSWGT, fcwgt, None)),
- ("font-style", C.FCSLN_CSSSTY[fcsln]),
- ("font-stretch", nearest_val(C.FCWDT_CSSSTR, fcwdt)),
- ]
- )
-
- @property
- def font_list(self):
- """
- Finds all fonts known to FontConfig
- Note that this does not appear to be completely comprehensive currently
- A few fonts are missing, such as HoloLens MDL2 Assets Bold on Windows
- These fonts can still be found using get_true_font
- """
- if self._font_list is None:
- pattern = fc.Pattern.create() # blank pattern
- properties = ["family", "weight", "slant", "width", "style", "file"]
- # style is a nice name for the weight/slant/width combo
- # e.g., Arial Narrow Bold
- self._font_list = self.conf.font_list(pattern, properties)
- self._font_list = sorted(
- self._font_list, key=lambda x: x.get("family", 0)[0]
- ) # Sort by family
- return self._font_list
-
- @property
- def font_list_css(self):
- """Finds all fonts known to FontConfig in css form"""
- if self._font_list_css is None:
- self._font_list_css = [FontConfig.fcfont_to_css(f) for f in self.font_list]
- return self._font_list_css
-
-
-fcfg = FontConfig()
-fontatt = ["font-family", "font-weight", "font-style", "font-stretch"]
-dfltatt = [(k, default_style_atts[k]) for k in fontatt]
-
-
-@lru_cache(maxsize=None)
-def font_style(sty):
- """
- Given a CSS style, return a style that has the four attributes
- that matter for font selection
- Note that Inkscape may not draw this font if it is not present on the system.
- The family can have multiple comma-separated values, used for fallback
- """
- sty2 = Style(dfltatt)
- sty2.update({k: v for k, v in sty.items() if k in fontatt})
- sty2["font-family"] = ",".join(
- ["'" + v.strip('"').strip("'") + "'" for v in sty2["font-family"].split(",")]
- )
- return sty2
-
-
-@lru_cache(maxsize=None)
-def true_style(sty):
- """
- Given a CSS style, return a style with the actual font that
- fontconfig selected. This is the actual font that Inkscape will draw.
- """
- sty2 = font_style(sty)
- tfnt = fcfg.get_true_font(sty2)
- return tfnt
-
-
-def nearest_val(dictv, inputval):
- """Return the value of a dict whose key is closest to the input value"""
- return dictv[min(dictv.keys(), key=lambda x: abs(x - inputval))]
-
-
-# Attempt to import the Pango bindings for the gi repository
-with warnings.catch_warnings():
- # Ignore ImportWarning for Gtk/Pango
- warnings.simplefilter("ignore")
-
- HASPANGO = False
- HASPANGOFT2 = False
- pangoenv = os.environ.get("USEPANGO", "")
- if pangoenv != "False":
- try:
- if sys.platform == "win32":
- # Windows may not have all of the typelibs needed for PangoFT2
- # Add the typelibs subdirectory as a fallback option
- bloc = inkex.inkscape_system_info.binary_location # type: ignore
- girepo = os.path.join(
- os.path.dirname(os.path.dirname(bloc)),
- "lib",
- "girepository-1.0",
- ) # Inkscape's GI repository
- if os.path.isdir(girepo):
- tlibs = [
- "fontconfig-2.0.typelib",
- "PangoFc-1.0.typelib",
- "PangoFT2-1.0.typelib",
- "freetype2-2.0.typelib",
- ]
- # If any typelibs are missing, try adding the typelibs subdirectory
- if any(
- not (os.path.exists(os.path.join(girepo, t))) for t in tlibs
- ):
- tlibsub = os.path.join(
- os.path.dirname(os.path.abspath(__file__)), "typelibs"
- )
- for newpath in [girepo, tlibsub]:
- # gi looks in the order specified in GI_TYPELIB_PATH
- cval = os.environ.get("GI_TYPELIB_PATH", "")
- if cval == "":
- os.environ["GI_TYPELIB_PATH"] = newpath
- elif newpath not in cval:
- os.environ["GI_TYPELIB_PATH"] = (
- cval + os.pathsep + newpath
- )
-
- import gi
-
- gi.require_version("Gtk", "3.0")
- from gi.repository import Pango
-
- try:
- HASPANGO = hasattr(Pango, "Variant") and hasattr(
- Pango.Variant, "NORMAL"
- )
- except ValueError:
- HASPANGO = False
- except ImportError:
- HASPANGO = False
-
- if HASPANGO:
- try:
- # May require some typelibs we do not have
- gi.require_version("PangoFT2", "1.0")
- from gi.repository import PangoFT2
-
- HASPANGOFT2 = True
- except ValueError:
- HASPANGOFT2 = False
- from gi.repository import Gdk
-
-if pangoenv in ["True", "False"]:
- os.environ["HASPANGO"] = str(HASPANGO)
- os.environ["HASPANGOFT2"] = str(HASPANGOFT2)
- with open("env_vars.txt", "w") as f:
- f.write(f"HASPANGO={os.environ['HASPANGO']}")
- f.write(f"\nHASPANGOFT2={os.environ['HASPANGOFT2']}")
-
-
-class PangoRenderer:
- """
- Class to handle Pango rendering functionalities.
- """
-
- def __init__(self):
- self.pangosize = 1024 * 4
- # size of text to render. 1024 is good
-
- with warnings.catch_warnings():
- # Ignore ImportWarning
- warnings.filterwarnings("ignore", category=ImportWarning)
- if HASPANGOFT2:
- self.ctx = Pango.Context.new()
- self.ctx.set_font_map(PangoFT2.FontMap.new())
- else:
- self.ctx = Gdk.pango_context_get()
- self.pangolayout = Pango.Layout(self.ctx)
- self.pufd = Pango.units_from_double
- self.putd = Pango.units_to_double
- self.scale = Pango.SCALE
- self._families = None
- self._faces = None
- self._face_descriptions = None
- self._face_strings = None
- self._face_css = None
-
- @staticmethod
- def css_attribute_to_pango(sty, key):
- """
- Convert CSS style attributes to Pango attributes.
- """
- val = sty.get(key)
- if key == "font-weight":
- return C.CSSWGT_PWGT.get(val, Pango.Weight.NORMAL)
- if key == "font-style":
- return C.CSSSTY_PSTY.get(val, Pango.Style.NORMAL)
- if key == "font-stretch":
- return C.CSSSTR_PSTR.get(val, Pango.Stretch.NORMAL)
- if key == "font-variant":
- return C.CSSVAR_PVAR.get(val, Pango.Variant.NORMAL)
- return None
-
- @staticmethod
- def css_to_pango_description(sty):
- """
- Convert CSS style to Pango FontDescription.
- """
- fdesc = Pango.FontDescription(sty["font-family"].strip("'").strip('"') + ",")
- # The comma above is very important for font-families like Rockwell Condensed.
- # Without it, Pango will interpret it as the Condensed font-stretch of the
- # Rockwell font-family, rather than the Rockwell Condensed font-family.
- fdesc.set_weight(
- interpolate_dict(C.CSSWGT_PWGT, sty.get("font-weight"), Pango.Weight.NORMAL)
- )
- fdesc.set_variant(
- C.CSSVAR_PVAR.get(sty.get("font-variant"), Pango.Variant.NORMAL)
- )
- fdesc.set_style(C.CSSSTY_PSTY.get(sty.get("font-style"), Pango.Style.NORMAL))
- fdesc.set_stretch(
- C.CSSSTR_PSTR.get(sty.get("font-stretch"), Pango.Stretch.NORMAL)
- )
- return fdesc
-
- @staticmethod
- def pango_to_fc(pstretch, pweight, pstyle):
- """
- Convert Pango attributes to FontConfig attributes.
- """
- fcwidth = C.PSTR_FCWDT[pstretch]
- fcweight = C.PWGT_FCWGT[pweight]
- fcslant = C.PSTY_FCSLN[pstyle]
- return fcwidth, fcweight, fcslant
-
- @staticmethod
- def pango_to_css(pdescription):
- """
- Convert Pango FontDescription to CSS Style.
- """
- fdesc = pdescription
- cstr = [k for k, v in C.CSSSTR_PSTR.items() if v == fdesc.get_stretch()]
- fwgt = fdesc.get_weight()
- if fwgt not in C.CSSWGT_PWGT.values():
- cwgt = [str(int(fwgt))]
- else:
- cwgt = [
- k
- for k, v in C.CSSWGT_PWGT.items()
- if v == fdesc.get_weight() and isnumeric(k)
- ]
- csty = [k for k, v in C.CSSSTY_PSTY.items() if v == fdesc.get_style()]
-
- sty = (("font-family", fdesc.get_family()),)
- if len(cwgt) > 0:
- sty += (("font-weight", cwgt[0]),)
- if len(csty) > 0:
- sty += (("font-style", csty[0]),)
- if len(cstr) > 0:
- sty += (("font-stretch", cstr[0]),)
- return Style(sty)
-
- @property
- def families(self):
- """List all font families available in Pango context."""
- if self._families is None:
- families = self.ctx.get_font_map().list_families()
- self._families = sorted(
- families, key=lambda x: x.get_name()
- ) # Sort families alphabetically
- return self._families
-
- @property
- def faces(self):
- """List all font faces available in Pango context."""
- if self._faces is None:
- self._faces = [fc for fm in self.families for fc in fm.list_faces()]
- return self._faces
-
- @property
- def face_descriptions(self):
- """List all font face descriptions available in Pango context."""
- if self._face_descriptions is None:
- self._face_descriptions = [fc.describe() for fc in self.faces]
- return self._face_descriptions
-
- @property
- def face_strings(self):
- """List all font face strings available in Pango context."""
- if self._face_strings is None:
- self._face_strings = [fd.to_string() for fd in self.face_descriptions]
- return self._face_strings
-
- @property
- def face_css(self):
- """List all font face CSS styles available in Pango context."""
- if self._face_css is None:
- self._face_css = [
- PangoRenderer.pango_to_css(fd) for fd in self.face_descriptions
- ]
- return self._face_css
-
- @staticmethod
- def fc_match_pango(family, pstretch, pweight, pstyle):
- """Look up a font by its Pango properties"""
- pat = fc.Pattern.name_parse(re.escape(family.replace("'", "").replace('"', "")))
- fcwidth, fcweight, fcslant = PangoRenderer.pango_to_fc(
- pstretch, pweight, pstyle
- )
- pat.add(fc.PROP.WIDTH, fcwidth)
- pat.add(fc.PROP.WEIGHT, fcweight)
- pat.add(fc.PROP.SLANT, fcslant)
-
- fcfg.conf.substitute(pat, FC.MatchPattern)
- pat.default_substitute()
- found, _ = fcfg.conf.font_match(pat)
- return found
-
- def set_text_style(self, sty):
- """Set the text style for rendering based on the provided style"""
- fdesc = PangoRenderer.css_to_pango_description(sty)
- fdesc.set_absolute_size(self.pufd(self.pangosize))
- fnt = self.ctx.get_font_map().load_font(self.ctx, fdesc)
-
- success = fnt is not None
- if success:
- self.pangolayout.set_font_description(fdesc)
- metrics = fnt.get_metrics()
- metrics = [
- self.putd(v) / self.pangosize
- for v in [
- metrics.get_height(),
- metrics.get_ascent(),
- metrics.get_descent(),
- ]
- ]
- return success, metrics
- return success, None
-
- def render_text(self, texttorender):
- """Render text using Pango layout."""
- self.pangolayout.set_text(texttorender, -1)
-
- def process_extents(self, ext, ascent):
- """
- Scale extents and return extents as standard bboxes
- (0:logical, 1:ink, 2: ink relative to anchor/baseline)
- """
- logr = ext.logical_rect
- logr = [
- self.putd(v) / self.pangosize
- for v in [logr.x, logr.y, logr.width, logr.height]
- ]
- inkr = ext.ink_rect
- inkr = [
- self.putd(v) / self.pangosize
- for v in [inkr.x, inkr.y, inkr.width, inkr.height]
- ]
- ir_rel = [inkr[0] - logr[0], inkr[1] - logr[1] - ascent, inkr[2], inkr[3]]
- return logr, inkr, ir_rel
-
- def get_character_extents(self, ascent, needexts):
- """
- Iterate through the layout to get the logical width of each character
- If there is differential kerning applied, it is applied to the
- width of the first character. For example, the 'V' in 'Voltage'
- will be thinner due to the 'o' that follows.
- Units: relative to font size
- """
- loi = self.pangolayout.get_iter()
- wds = []
- i = -1
- lastpos = True
- unwrapper = 0
- moved = True
- while moved:
- cext = loi.get_cluster_extents()
- i += 1
- if needexts[i] == "1":
- ext = self.process_extents(cext, ascent)
- if ext[0][0] < 0 and lastpos:
- unwrapper += 2**32 / (self.scale * self.pangosize)
- lastpos = ext[0][0] >= 0
- ext[0][0] += unwrapper # account for 32-bit overflow
- ext[1][0] += unwrapper
- wds.append(ext)
- else:
- wds.append(None)
- moved = loi.next_char()
-
- numunknown = self.pangolayout.get_unknown_glyphs_count()
- return wds, numunknown
-
-
-# pylint:disable=import-outside-toplevel
-class FontToolsFontInstance:
- """
- Class to handle FontTools font instances.
- Note that this is rarely used (only if Pango fails, or for getting the
- ascent for text flows). The imports are fairly time-consuming, so
- they are only performed conditionally.
- """
-
- def __init__(self, fcfont):
- self.font = FontToolsFontInstance.font_from_fc(fcfont)
- self.head = self.font["head"]
- self.os2 = self.font["OS/2"] if "OS/2" in self.font else None
- self.find_font_metrics()
- self.cmap = None
- self.hmtx = None
- self.kern = None
- self.gsub = None
- self.glyf = None
- self.glyph_names = None
- self.ligatures = None
-
- @staticmethod
- def font_from_fc(found):
- """Find a FontTools font from a FontConfig font."""
- fname = found.get(fc.PROP.FILE, 0)[0]
-
- from fontTools.ttLib import TTFont, TTLibFileIsCollectionError # pylint: disable=no-name-in-module
- import logging
-
- logging.getLogger("fontTools").setLevel(logging.ERROR)
- try:
- font = TTFont(fname)
-
- # If font has variants, get them
- if "fvar" in font:
- fcwgt = found.get(fc.PROP.WEIGHT, 0)[0]
- fcsln = found.get(fc.PROP.SLANT, 0)[0]
- fcwdt = found.get(fc.PROP.WIDTH, 0)[0]
- location = dict()
- for axis in font["fvar"].axes:
- if axis.axisTag == "wght":
- location["wght"] = nearest_val(C.FCWGT_OS2WGT, fcwgt)
- elif axis.axisTag == "wdth":
- location["wdth"] = fcwdt
- if len(location) > 0:
- from fontTools.varLib import mutator
-
- font = mutator.instantiateVariableFont(font, location)
-
- except TTLibFileIsCollectionError:
- # is TT collection
- fcfam = found.get(fc.PROP.FAMILY, 0)[0]
- fcwgt = found.get(fc.PROP.WEIGHT, 0)[0]
- fcsln = found.get(fc.PROP.SLANT, 0)[0]
- fcwdt = found.get(fc.PROP.WIDTH, 0)[0]
-
- from fontTools.ttLib import TTCollection
-
- collection = TTCollection(fname)
- num_fonts = len(collection)
- collection.close()
- num_match = []
- for i in range(num_fonts):
- tfont = TTFont(fname, fontNumber=i)
- font_weight = tfont["OS/2"].usWeightClass
- font_width = tfont["OS/2"].usWidthClass
-
- subfamily = tfont["name"].getName(2, 3, 1, 1033)
- subfamily = (
- subfamily.toUnicode() if subfamily is not None else "Unknown"
- )
- font_italic = (
- (tfont["OS/2"].fsSelection & 1) != 0
- or "italic" in subfamily.lower()
- or "oblique" in subfamily.lower()
- )
-
- # nameID=1: font family name
- familymatch = any(
- fcfam in n.toUnicode() for n in tfont["name"].names if n.nameID == 1
- )
- widthmatch = C.OS2WDT_FCWDT[font_width] == fcwdt
- weightmatch = (
- interpolate_dict(C.OS2WGT_FCWGT, font_weight, None) == fcwgt
- )
- slantmatch = (
- font_italic and fcsln in [FC.SLANT_ITALIC, FC.SLANT_OBLIQUE]
- ) or (not font_italic and fcsln == FC.SLANT_ROMAN)
- num_match.append(
- sum([weightmatch, widthmatch, slantmatch, familymatch])
- )
- if num_match[-1] == 4:
- font = tfont
- break
- if max(num_match) < 4:
- # Did not find a perfect match
- font = [
- TTFont(fname, fontNumber=i)
- for i in range(num_fonts)
- if num_match[i] == max(num_match)
- ][0]
- return font
-
- def find_font_metrics(self):
- """
- A modified version of Inkscape's find_font_metrics
- https://gitlab.com/inkscape/inkscape/-/blob/master/src/libnrtype/font-instance.cpp#L267
- Uses FontTools, which is Pythonic
- """
- font = self.font
- units_per_em = self.head.unitsPerEm
- os2 = self.os2
- if os2:
- self._ascent = abs(os2.sTypoAscender / units_per_em)
- self._descent = abs(os2.sTypoDescender / units_per_em)
- else:
- self._ascent = abs(font["hhea"].ascent / units_per_em)
- self._descent = abs(font["hhea"].descent / units_per_em)
- self._ascent_max = abs(font["hhea"].ascent / units_per_em)
- self._descent_max = abs(font["hhea"].descent / units_per_em)
- self._design_units = units_per_em
- emv = self._ascent + self._descent
- if emv > 0.0:
- self._ascent /= emv
- self._descent /= emv
-
- if os2 and os2.version >= 0x0002 and os2.version != 0xFFFF:
- self._xheight = abs(os2.sxHeight / units_per_em)
- else:
- glyph_set = font.getGlyphSet()
- self._xheight = (
- abs(glyph_set["x"].height / units_per_em)
- if "x" in glyph_set and glyph_set["x"].height is not None
- else 0.5
- )
- self._baselines = [0] * 8
- self.sp_css_baseline_ideographic = 0
- self.sp_css_baseline_hanging = 1
- self.sp_css_baseline_mathematical = 2
- self.sp_css_baseline_central = 3
- self.sp_css_baseline_middle = 4
- self.sp_css_baseline_text_beforeedge = 5
- self.sp_css_baseline_text_after_edge = 6
- self.sp_css_baseline_alphabetic = 7
- self._baselines[self.sp_css_baseline_ideographic] = -self._descent
- self._baselines[self.sp_css_baseline_hanging] = 0.8 * self._ascent
- self._baselines[self.sp_css_baseline_mathematical] = 0.8 * self._xheight
- self._baselines[self.sp_css_baseline_central] = 0.5 - self._descent
- self._baselines[self.sp_css_baseline_middle] = 0.5 * self._xheight
- self._baselines[self.sp_css_baseline_text_beforeedge] = self._ascent
- self._baselines[self.sp_css_baseline_text_after_edge] = -self._descent
-
- # Get capital height
- if os2 and hasattr(os2, "sCapHeight") and os2.sCapHeight not in [0, None]:
- self.cap_height = os2.sCapHeight / units_per_em
- elif "glyf" in font and "I" in font.getGlyphNames():
- glyf_table = font["glyf"]
- i_glyph = glyf_table["I"]
- self.cap_height = (i_glyph.yMax - 0 * i_glyph.yMin) / units_per_em
- else:
- self.cap_height = 1
-
- def get_char_advances(self, chars, pchars):
- """Get the advance width and bounding boxes of characters."""
- units_per_em = self.head.unitsPerEm
- if self.cmap is None:
- self.cmap = self.font.getBestCmap()
- if self.hmtx is None:
- self.hmtx = self.font["hmtx"]
- if self.kern is None:
- self.kern = self.font["kern"] if "kern" in self.font else None
- if self.gsub is None:
- self.gsub = self.font["GSUB"] if "GSUB" in self.font else None
- if self.glyf is None:
- self.glyf = self.font["glyf"] if "glyf" in self.font else None
- if self.glyph_names is None:
- self.glyph_names = self.font.getGlyphNames()
-
- if self.cmap is None:
- # Certain symbol fonts don't have a cmap table
- self.cmap = dict()
- for table in self.font["cmap"].tables:
- if table.isUnicode():
- for codepoint, name in table.cmap.items():
- self.cmap[codepoint] = name
- self.cmap[codepoint - 15 * 4096] = name
-
- advs = dict()
- bbs = dict()
- for c in chars:
- glyph1 = self.cmap.get(ord(c))
- if glyph1 is not None:
- if glyph1 in self.hmtx.metrics:
- advance_width, _ = self.hmtx.metrics[glyph1]
- advs[c] = advance_width / units_per_em
-
- try:
- glyph = self.glyf[glyph1]
- bbx = [
- glyph.xMin,
- -glyph.yMax,
- glyph.xMax - glyph.xMin,
- glyph.yMax - glyph.yMin,
- ]
- bbs[c] = [v / units_per_em for v in bbx]
- except (AttributeError, TypeError):
- bbs[c] = [0, 0, 0, 0]
- else:
- advs[c] = None
-
- # Get ligature table (made with LLM help)
- # Reference: https://learn.microsoft.com/en-us/typography/opentype/spec/gsub
- if self.ligatures is None:
- if self.gsub:
- gsub_table = self.gsub.table
- self.ligatures = dict()
-
- # Helper function to check if a feature tag is in the list
- def has_feature_tag(feature_tag, lookup_index):
- for feature in gsub_table.FeatureList.FeatureRecord:
- if feature.FeatureTag == feature_tag:
- if lookup_index in feature.Feature.LookupListIndex:
- return True
- return False
-
- for lookup_index, lookup in enumerate(gsub_table.LookupList.Lookup):
- for subtable in lookup.SubTable:
- if lookup.LookupType == 7: # Extension substitutions
- ext_subtable = subtable.ExtSubTable
- lookup_type = ext_subtable.LookupType
- else:
- ext_subtable = subtable
- lookup_type = lookup.LookupType
-
- if lookup_type == 4: # Ligature substitutions
- # Check if the lookup is for discretionary ligatures
- if has_feature_tag("liga", lookup_index):
- for (
- first_glyph,
- ligature_set,
- ) in ext_subtable.ligatures.items():
- for ligature in ligature_set:
- component_glyphs = [
- first_glyph
- ] + ligature.Component
- ligature_glyph = ligature.LigGlyph
- self.ligatures[tuple(component_glyphs)] = (
- ligature_glyph
- )
-
- else:
- self.ligatures = dict()
-
- dadvs = dict()
- for c in pchars:
- glyph2 = self.cmap.get(ord(c))
- for pchar in pchars[c]:
- glyph1 = self.cmap.get(ord(pchar))
- kerning_value = None
- if (glyph1, glyph2) in self.ligatures:
- ligglyph = self.ligatures[(glyph1, glyph2)]
- awlig, _ = self.hmtx.metrics[ligglyph]
- aw1, _ = self.hmtx.metrics[glyph1]
- aw2, _ = self.hmtx.metrics[glyph2]
- kerning_value = awlig - aw1 - aw2
- else:
- if self.kern is not None:
- for subtable in self.kern.kernTables:
- kerning_value = subtable.kernTable.get((glyph1, glyph2))
- if kerning_value is not None:
- break
- if kerning_value is None:
- kerning_value = 0
- dadvs[(pchar, c)] = kerning_value / units_per_em
- return advs, dadvs, bbs
-
-
-# pylint:enable=import-outside-toplevel
-
-
-class Conversions:
- """
- Conversions between CSS, FontConfig, Pango, and OS2 font attributes
- CSS: font-weight, font-style, font-stretch
- FontConfig: weight, slant, width
- Pango: weight, style, stretch
- OS2: weight, width,
-
- Inkscape conventions in libnrtype/font-factory.cpp
- https://gitlab.com/inkscape/inkscape/-/blob/master/src/libnrtype/font-factory.cpp
- """
-
- # CSS to fontconfig
- # For weights, Inkscape ignores anything commented out below
- # See ink_font_description_from_style in libnrtype/font-factory.cpp
- # And yet Semi-Light seems to work anyway
- CSSWGT_FCWGT = {
- # 'thin' : FC.WEIGHT_THIN,
- # 'ultralight': FC.WEIGHT_EXTRALIGHT,
- # 'light' : FC.WEIGHT_LIGHT,
- # 'semilight' : FC.WEIGHT_SEMILIGHT,
- # 'book' : FC.WEIGHT_BOOK,
- "normal": FC.WEIGHT_NORMAL,
- # 'medium' : FC.WEIGHT_MEDIUM,
- # 'semibold' : FC.WEIGHT_SEMIBOLD,
- "bold": FC.WEIGHT_BOLD,
- # 'ultrabold' : FC.WEIGHT_ULTRABOLD,
- # 'heavy' : FC.WEIGHT_HEAVY,
- # 'ultraheavy': FC.WEIGHT_ULTRABLACK,
- "100": FC.WEIGHT_THIN,
- "200": FC.WEIGHT_EXTRALIGHT,
- "300": FC.WEIGHT_LIGHT,
- "350": FC.WEIGHT_SEMILIGHT,
- # '380' : FC.WEIGHT_BOOK,
- "400": FC.WEIGHT_NORMAL,
- "500": FC.WEIGHT_MEDIUM,
- "600": FC.WEIGHT_SEMIBOLD,
- "700": FC.WEIGHT_BOLD,
- "800": FC.WEIGHT_ULTRABOLD,
- "900": FC.WEIGHT_HEAVY,
- # '1000' : FC.WEIGHT_ULTRABLACK
- }
-
- CSSSTY_FCSLN = {
- "normal": FC.SLANT_ROMAN,
- "italic": FC.SLANT_ITALIC,
- "oblique": FC.SLANT_OBLIQUE,
- }
-
- CSSSTR_FCWDT = {
- "ultra-condensed": FC.WIDTH_ULTRACONDENSED,
- "extra-condensed": FC.WIDTH_EXTRACONDENSED,
- "condensed": FC.WIDTH_CONDENSED,
- "semi-condensed": FC.WIDTH_SEMICONDENSED,
- "normal": FC.WIDTH_NORMAL,
- "semi-expanded": FC.WIDTH_SEMIEXPANDED,
- "expanded": FC.WIDTH_EXPANDED,
- "extra-expanded": FC.WIDTH_EXTRAEXPANDED,
- "ultra-expanded": FC.WIDTH_ULTRAEXPANDED,
- }
-
- # Fontconfig to CSS
- # Semi-Light, Book, and Ultra-Black are mapped to Light, Normal, Heavy
- # See FontFactory::GetUIStyles in libnrtype/font-factory.cpp
- # Despite this, Semi-Light seems to be valid
- FCWGT_CSSWGT = {
- FC.WEIGHT_THIN: "100",
- FC.WEIGHT_EXTRALIGHT: "200",
- FC.WEIGHT_LIGHT: "300",
- FC.WEIGHT_SEMILIGHT: "350",
- # FC.WEIGHT_BOOK : '380',
- FC.WEIGHT_BOOK: "400",
- FC.WEIGHT_NORMAL: "400",
- FC.WEIGHT_MEDIUM: "500",
- FC.WEIGHT_SEMIBOLD: "600",
- FC.WEIGHT_BOLD: "700",
- FC.WEIGHT_ULTRABOLD: "800",
- FC.WEIGHT_HEAVY: "900",
- # FC.WEIGHT_ULTRABLACK : '1000',
- FC.WEIGHT_ULTRABLACK: "900",
- }
- FCSLN_CSSSTY = {
- FC.SLANT_ROMAN: "normal",
- FC.SLANT_ITALIC: "italic",
- FC.SLANT_OBLIQUE: "oblique",
- }
- FCWDT_CSSSTR = {
- FC.WIDTH_ULTRACONDENSED: "ultra-condensed",
- FC.WIDTH_EXTRACONDENSED: "extra-condensed",
- FC.WIDTH_CONDENSED: "condensed",
- FC.WIDTH_SEMICONDENSED: "semi-condensed",
- FC.WIDTH_NORMAL: "normal",
- FC.WIDTH_SEMIEXPANDED: "semi-expanded",
- FC.WIDTH_EXPANDED: "expanded",
- FC.WIDTH_EXTRAEXPANDED: "extra-expanded",
- FC.WIDTH_ULTRAEXPANDED: "ultra-expanded",
- }
-
- # Pango style string description to CSS
- # Needed for matching the name of styles shown in Inkscape
- PWGTSTR_CSSWGT = {
- "Ultra-Light": "200",
- "Light": "300",
- "Semi-Light": "350",
- "Medium": "500",
- "Semi-Bold": "600",
- "Bold": "bold",
- "Ultra-Bold": "800",
- "Heavy": "900",
- "Normal": "normal",
- "Book": "380",
- "Thin": "100",
- "Ultra-Heavy": "1000",
- }
- PSTRSTR_CSSSTR = {
- "Ultra-Condensed": "ultra-condensed",
- "Extra-Condensed": "extra-condensed",
- "Condensed": "condensed",
- "Semi-Condensed": "semi-condensed",
- "Normal": "normal",
- "Semi-Expanded": "semi-expanded",
- "Expanded": "expanded",
- "Extra-Expanded": "extra-expanded",
- "Ultra-Expanded": "ultra-expanded",
- }
- PSTYSTR_CSSSTY = {
- "Italic": "italic",
- "Oblique": "oblique",
- "Normal": "normal",
- }
-
- # FC to OS2 and OS2 to FC
- FCWGT_OS2WGT = {
- FC.WEIGHT_THIN: 100,
- FC.WEIGHT_EXTRALIGHT: 200,
- FC.WEIGHT_LIGHT: 300,
- FC.WEIGHT_SEMILIGHT: 350,
- FC.WEIGHT_BOOK: 380,
- FC.WEIGHT_NORMAL: 400,
- FC.WEIGHT_MEDIUM: 500,
- FC.WEIGHT_SEMIBOLD: 600,
- FC.WEIGHT_BOLD: 700,
- FC.WEIGHT_ULTRABOLD: 800,
- FC.WEIGHT_HEAVY: 900,
- FC.WEIGHT_ULTRABLACK: 1000,
- }
- OS2WDT_FCWDT = {
- 1: FC.WIDTH_ULTRACONDENSED,
- 2: FC.WIDTH_EXTRACONDENSED,
- 3: FC.WIDTH_CONDENSED,
- 4: FC.WIDTH_SEMICONDENSED,
- 5: FC.WIDTH_NORMAL,
- 6: FC.WIDTH_SEMIEXPANDED,
- 7: FC.WIDTH_EXPANDED,
- 8: FC.WIDTH_EXTRAEXPANDED,
- 9: FC.WIDTH_ULTRAEXPANDED,
- }
- OS2WGT_FCWGT = {
- 100: FC.WEIGHT_THIN,
- 200: FC.WEIGHT_EXTRALIGHT,
- 300: FC.WEIGHT_LIGHT,
- 350: FC.WEIGHT_SEMILIGHT,
- 380: FC.WEIGHT_BOOK,
- 400: FC.WEIGHT_NORMAL,
- 500: FC.WEIGHT_MEDIUM,
- 600: FC.WEIGHT_SEMIBOLD,
- 700: FC.WEIGHT_BOLD,
- 800: FC.WEIGHT_ULTRABOLD,
- 900: FC.WEIGHT_HEAVY,
- 1000: FC.WEIGHT_ULTRABLACK,
- }
-
- if HASPANGO:
- # Pango to fontconfig
- PWGT_FCWGT = {
- Pango.Weight.THIN: FC.WEIGHT_THIN,
- Pango.Weight.ULTRALIGHT: FC.WEIGHT_ULTRALIGHT,
- Pango.Weight.ULTRALIGHT: FC.WEIGHT_EXTRALIGHT,
- Pango.Weight.LIGHT: FC.WEIGHT_LIGHT,
- Pango.Weight.SEMILIGHT: FC.WEIGHT_DEMILIGHT,
- Pango.Weight.SEMILIGHT: FC.WEIGHT_SEMILIGHT,
- Pango.Weight.BOOK: FC.WEIGHT_BOOK,
- Pango.Weight.NORMAL: FC.WEIGHT_REGULAR,
- Pango.Weight.NORMAL: FC.WEIGHT_NORMAL,
- Pango.Weight.MEDIUM: FC.WEIGHT_MEDIUM,
- Pango.Weight.SEMIBOLD: FC.WEIGHT_DEMIBOLD,
- Pango.Weight.SEMIBOLD: FC.WEIGHT_SEMIBOLD,
- Pango.Weight.BOLD: FC.WEIGHT_BOLD,
- Pango.Weight.ULTRABOLD: FC.WEIGHT_EXTRABOLD,
- Pango.Weight.ULTRABOLD: FC.WEIGHT_ULTRABOLD,
- Pango.Weight.HEAVY: FC.WEIGHT_BLACK,
- Pango.Weight.HEAVY: FC.WEIGHT_HEAVY,
- Pango.Weight.ULTRAHEAVY: FC.WEIGHT_EXTRABLACK,
- Pango.Weight.ULTRAHEAVY: FC.WEIGHT_ULTRABLACK,
- }
-
- PSTY_FCSLN = {
- Pango.Style.NORMAL: FC.SLANT_ROMAN,
- Pango.Style.ITALIC: FC.SLANT_ITALIC,
- Pango.Style.OBLIQUE: FC.SLANT_OBLIQUE,
- }
-
- PSTR_FCWDT = {
- Pango.Stretch.ULTRA_CONDENSED: FC.WIDTH_ULTRACONDENSED,
- Pango.Stretch.EXTRA_CONDENSED: FC.WIDTH_EXTRACONDENSED,
- Pango.Stretch.CONDENSED: FC.WIDTH_CONDENSED,
- Pango.Stretch.SEMI_CONDENSED: FC.WIDTH_SEMICONDENSED,
- Pango.Stretch.NORMAL: FC.WIDTH_NORMAL,
- Pango.Stretch.SEMI_EXPANDED: FC.WIDTH_SEMIEXPANDED,
- Pango.Stretch.EXPANDED: FC.WIDTH_EXPANDED,
- Pango.Stretch.EXTRA_EXPANDED: FC.WIDTH_EXTRAEXPANDED,
- Pango.Stretch.ULTRA_EXPANDED: FC.WIDTH_ULTRAEXPANDED,
- }
-
- # CSS to Pango
- CSSVAR_PVAR = {
- "normal": Pango.Variant.NORMAL,
- "small-caps": Pango.Variant.SMALL_CAPS,
- }
-
- CSSSTY_PSTY = {
- "normal": Pango.Style.NORMAL,
- "italic": Pango.Style.ITALIC,
- "oblique": Pango.Style.OBLIQUE,
- }
- # For weights, Inkscape ignores anything commented out below
- # See ink_font_description_from_style in libnrtype/font-factory.cpp
- CSSWGT_PWGT = {
- # 'thin' : Pango.Weight.THIN,
- # 'ultralight' : Pango.Weight.ULTRALIGHT,
- # 'light' : Pango.Weight.LIGHT,
- "semilight": Pango.Weight.SEMILIGHT,
- # 'book' : Pango.Weight.BOOK,
- "normal": Pango.Weight.NORMAL,
- # 'medium' : Pango.Weight.MEDIUM,
- # 'semibold' : Pango.Weight.SEMIBOLD,
- "bold": Pango.Weight.BOLD,
- # 'ultrabold' : Pango.Weight.ULTRABOLD,
- # 'heavy' : Pango.Weight.HEAVY,
- # 'ultraheavy' : Pango.Weight.ULTRAHEAVY,
- "100": Pango.Weight.THIN,
- "200": Pango.Weight.ULTRALIGHT,
- "300": Pango.Weight.LIGHT,
- "350": Pango.Weight.SEMILIGHT,
- # '380' : Pango.Weight.BOOK,
- "400": Pango.Weight.NORMAL,
- "500": Pango.Weight.MEDIUM,
- "600": Pango.Weight.SEMIBOLD,
- "700": Pango.Weight.BOLD,
- "800": Pango.Weight.ULTRABOLD,
- "900": Pango.Weight.HEAVY,
- # '1000' : Pango.Weight.ULTRAHEAVY
- }
- CSSSTR_PSTR = {
- "ultra-condensed": Pango.Stretch.ULTRA_CONDENSED,
- "extra-condensed": Pango.Stretch.EXTRA_CONDENSED,
- "condensed": Pango.Stretch.CONDENSED,
- "semi-condensed": Pango.Stretch.SEMI_CONDENSED,
- "normal": Pango.Stretch.NORMAL,
- "semi-expanded": Pango.Stretch.SEMI_EXPANDED,
- "expanded": Pango.Stretch.EXPANDED,
- "extra-expanded": Pango.Stretch.EXTRA_EXPANDED,
- "ultra-expanded": Pango.Stretch.ULTRA_EXPANDED,
- }
-
-
-C = Conversions # pylint: disable=invalid-name
-
-
-def inkscape_spec_to_css(fstr, usepango=False):
- """
- Look up a CSS style based on its Inkscape font specification, which is
- similar (identical?) to the Pango description's string representation.
- This function is meant to be forgiving, allowing a user to vary punctuation
- and capitalization, and tries to find a match in the system's families.
- It does NOT require Pango.
- """
-
- def clean_str(strin):
- ret = re.sub(r"[^\w\s]", "", strin)
- ret = re.sub(r"\s+", " ", ret)
- return ret.strip().lower()
-
- cstr = clean_str(fstr)
- if usepango and HASPANGO:
- fullfams = [fm.get_name() for fm in PangoRenderer().families]
- else:
- fullfams = [f.get("family", 0)[0] for f in fcfg.font_list]
- fullfams += ["Serif", "Sans", "System-ui", "Monospace"] # from Inkscape
-
- # Split the input into words and find the longest match to an installed
- # family at the beginning or end of the string
- fmnames = [clean_str(f) for f in fullfams]
- words = cstr.split()
- longest_match = ""
- match_length = 0
- match_type = None # 'prefix' or 'suffix'
- # Check for the longest prefix match
- for i in range(1, len(words) + 1):
- current_match = " ".join(words[:i])
- if current_match in fmnames and len(current_match) > len(longest_match):
- longest_match = current_match
- match_length = i
- match_type = "prefix"
- # Check for the longest suffix match
- for i in range(1, len(words) + 1):
- current_match = " ".join(words[-i:])
- if current_match in fmnames and len(current_match) > len(longest_match):
- longest_match = current_match
- match_length = i
- match_type = "suffix"
- if longest_match:
- fam = fullfams[fmnames.index(longest_match)]
- if match_type == "prefix":
- stylews = words[match_length:]
- elif match_type == "suffix":
- stylews = words[:-match_length]
- if not stylews:
- stylews = None
- else:
- fam = None
- stylews = words
-
- sty = {}
- if fam:
- sty["font-family"] = fam
- if stylews:
- pwgtstrs = {clean_str(k): v for k, v in C.PWGTSTR_CSSWGT.items()}
- pstrstrs = {clean_str(k): v for k, v in C.PSTRSTR_CSSSTR.items()}
- pstystrs = {clean_str(k): v for k, v in C.PSTYSTR_CSSSTY.items()}
- for wrd in stylews:
- understood = False
- if wrd in pwgtstrs:
- sty["font-weight"] = pwgtstrs[wrd]
- understood = True
- elif "weight" in wrd:
- sty["font-weight"] = re.search(r"weight(\d+)", wrd).group(1)
- understood = True
- if wrd in pstystrs:
- sty["font-style"] = pstystrs[wrd]
- understood = True
- if wrd in pstrstrs:
- sty["font-stretch"] = pstrstrs[wrd]
- understood = True
- if not understood:
- return None
- sty = {key: sty[key] for key in fontatt if key in sty} # order
- return sty
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/parser.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/parser.py
deleted file mode 100644
index 2ad362e..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/parser.py
+++ /dev/null
@@ -1,4097 +0,0 @@
-# coding=utf-8
-#
-# Copyright (c) 2023 David Burghoff
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-
-"""
-text.parser module parses text in a document according to the way Inkscape
-handles it. In short, every TextElement or FlowRoot is parsed into a ParsedText.
-Each ParsedText contains a collection of tlines, representing one line of text.
-Each TLine contains a collection of tchars, representing a single character.
-Characters are also grouped into tchunks, which represent chunks of characters
-sharing an anchor (usually from manually-kerned text).
-
-These functions allow text metrics and bounding boxes to be calculated without
-binary calls to Inkscape. It can calculate both the ink bounding box (i.e., where
-characters' ink is) as well as the extent bounding box (i.e, its logical location).
-The extent of a character is defined as extending between cursor positions in the
-x-direction and between the baseline and capital height in the y-direction.
-
-Some examples:
-- el.parsed_text.get_full_inkbbox(): gets the untransformed bounding box of the
- whole element
-- el.parsed_text.get_char_extents(): gets all characters' untransformed extents
-- el.parse_text.lns[0].chrs[0].pts_ut: the untransformed points of the extent of the
- first character of the first line
-- el.parse_text.lns[0].chrs[0].pts_t : the transformed points of the extent of the
- first character of the first line
-
-To check if things are working properly, you can run the make_highlights() function,
-which draws rectangles that tell you where the extents / bboxes are. For example,
-- el.parsed_text.make_highlights('char') : shows the extent of each character
-- el.parsed_text.make_highlights('fullink') : shows the bbox of the whole element
-"""
-
-import itertools
-import math
-from copy import copy
-import threading
-import lxml
-import numpy as np
-import inkex
-from inkex import (
- TextElement,
- Tspan,
- Transform,
- Style,
- FlowRoot,
- FlowRegion,
- FlowPara,
- FlowSpan,
-)
-from inkex.text.utils import (
- uniquetol,
- composed_width,
- composed_lineheight,
- default_style_atts,
- isrectangle,
- ipx,
- bbox,
-)
-from inkex.text.font_properties import (
- PangoRenderer,
- HASPANGO,
- fcfg,
- font_style,
- true_style,
-)
-from inkex.utils import debug
-
-DIFF_ADVANCES = True # generate a differential advances table for each font?
-TEXTSIZE = 100 # size of rendered text
-DEPATHOLOGIZE = True # clean up pathological atts not normally made by Inkscape
-
-EBget = lxml.etree.ElementBase.get
-EBset = lxml.etree.ElementBase.set
-
-TEtag, TStag, FRtag = TextElement.ctag, Tspan.ctag, FlowRoot.ctag
-TEFRtags = {TEtag, FRtag}
-TEtags = {TEtag, TStag}
-FRtags = {FRtag, FlowRegion.ctag, FlowPara.ctag, FlowSpan.ctag}
-
-LOCK = threading.Lock()
-
-
-class ParsedTextList(list):
- """
- A list of parsed text whose coordinates are computed
- vectorially using numpy. Normally they are calculated as they are needed,
- but this is inefficient when dealing with thousands of manually-kerned
- characters (e.g., from PDFs).
- """
-
- def __init__(self, els):
- """Initializes ParsedTextList with a list of elements."""
- super().__init__([el.parsed_text for el in els])
-
- def precalcs(self):
- """
- Calculate parsed_bb, parsed_pts_ut, parsed_pts_t for all chunks
- simultaneously
- """
- tws = [chk for ptxt in self for line in ptxt.lns for chk in line.chks]
- nchrs = sum(chk.ncs for chk in tws)
-
- # Preallocate arrays
- dadv, cwd, dxeff, dy, bshft, caph, unsp, anfr = np.zeros(
- (8, nchrs), dtype=float
- )
- fidx, lidx, widx = np.zeros((3, nchrs)).astype(int)
-
- unsp = np.array([chk.unrenderedspace for chk in tws], dtype=float)
- anfr = np.array([chk.line.anchfrac for chk in tws], dtype=float)
- chkx = np.array([chk.x for chk in tws], dtype=float)
- chky = np.array([chk.y for chk in tws], dtype=float)
- m00, m01, m02, m10, m11, m12 = (
- np.array([chk.transform.matrix[i][j] for chk in tws], dtype=float)
- for i in (0, 1)
- for j in (0, 1, 2)
- )
- lx2, rx2, by2, ty2 = np.zeros((4, len(tws))).astype(float)
-
- # Collect values for dadv, cwd, dxeff, dy, bshft, and caph
- idx = 0
- for j, chk in enumerate(tws):
- if DIFF_ADVANCES:
- for i in range(1, chk.ncs):
- dadv[idx + i] = chk.chrs[i].dadvs(chk.chrs[i - 1].c, chk.chrs[i].c)*(chk.dxeff[i]==0)
-
- for i in range(chk.ncs):
- cwd[idx + i] = chk.cwd[i]
- dxeff[idx + i] = chk.dxeff[i]
- dy[idx + i] = chk.dy[i]
- bshft[idx + i] = chk.bshft[i]
- caph[idx + i] = chk.caph[i]
- fidx[idx : idx + chk.ncs] = idx
- lidx[idx : idx + chk.ncs] = idx + chk.ncs - 1
- widx[idx : idx + chk.ncs] = j
- idx += chk.ncs
-
- # Calculate wds, cstop, and cstrt
- wds = cwd + dxeff + dadv
- cstop = np.array(list(itertools.accumulate(wds)), dtype=float)
- cstop += wds[fidx] - cstop[fidx]
- cstrt = cstop - cwd
-
- # # Calculate adyl
- adyl = np.array(list(itertools.accumulate(dy)), dtype=float)
- adyl += dy[fidx] - adyl[fidx]
-
- # Calculate chkw, offx, lftx, and rgtx
- offx = -anfr[widx] * (cstop[lidx] - unsp[widx] * cwd[lidx])
- lftx = chkx[widx] + cstrt + offx
- rgtx = chkx[widx] + cstop + offx
- btmy = chky[widx] + adyl - bshft
- topy = btmy - caph
-
- idx_ncs = np.cumsum([0] + [chk.ncs for chk in tws])
- starts = idx_ncs[:-1]
- subtract_ufunc = np.frompyfunc(lambda a, b: a - b, 2, 1)
- lx_minus_dxeff = subtract_ufunc(lftx, dxeff)
- lx2 = np.minimum.reduceat(lx_minus_dxeff, starts)
- rx2 = lx2 + cstop[idx_ncs[1:] - 1]
- by2 = np.maximum.reduceat(btmy, starts)
- ty2 = np.minimum.reduceat(topy, starts)
-
- cpts_ut = [
- np.hstack((lftx[:, np.newaxis], btmy[:, np.newaxis])),
- np.hstack((lftx[:, np.newaxis], topy[:, np.newaxis])),
- np.hstack((rgtx[:, np.newaxis], topy[:, np.newaxis])),
- np.hstack((rgtx[:, np.newaxis], btmy[:, np.newaxis])),
- ]
- mat = ((m00[widx], m01[widx], m02[widx]), (m10[widx], m11[widx], m12[widx]))
- cpts_t = [
- np.vstack(vmult(mat, lftx, btmy)).T,
- np.vstack(vmult(mat, lftx, topy)).T,
- np.vstack(vmult(mat, rgtx, topy)).T,
- np.vstack(vmult(mat, rgtx, btmy)).T,
- ]
- pts_ut = [
- np.vstack((lx2, by2)),
- np.vstack((lx2, ty2)),
- np.vstack((rx2, ty2)),
- np.vstack((rx2, by2)),
- ]
- mat = ((m00, m01, m02), (m10, m11, m12))
- pts_t = [
- np.vstack(vmult(mat, lx2, by2)),
- np.vstack(vmult(mat, lx2, ty2)),
- np.vstack(vmult(mat, rx2, ty2)),
- np.vstack(vmult(mat, rx2, by2)),
- ]
-
- # pylint:disable=protected-access
- # Split outputs into lists of column vectors
- # maxerr = float('-inf')
- for i, chk in enumerate(tws):
- # Extract the slices of the relevant arrays for this TChunk
- lxw = lftx[idx_ncs[i] : idx_ncs[i + 1], np.newaxis]
- rxw = rgtx[idx_ncs[i] : idx_ncs[i + 1], np.newaxis]
- byw = btmy[idx_ncs[i] : idx_ncs[i + 1], np.newaxis]
- tyw = topy[idx_ncs[i] : idx_ncs[i + 1], np.newaxis]
-
- chk._charpos = (lxw, rxw, byw, tyw, lx2[i], rx2[i], by2[i], ty2[i])
- chk._cpts_ut = [cpv[idx_ncs[i] : idx_ncs[i + 1], :] for cpv in cpts_ut]
- chk._cpts_t = [cpv[idx_ncs[i] : idx_ncs[i + 1], :] for cpv in cpts_t]
- chk._pts_ut = [
- (float(pts_ut[0][0][i]), float(pts_ut[0][1][i])),
- (float(pts_ut[1][0][i]), float(pts_ut[1][1][i])),
- (float(pts_ut[2][0][i]), float(pts_ut[2][1][i])),
- (float(pts_ut[3][0][i]), float(pts_ut[3][1][i])),
- ]
- chk._pts_t = [
- (float(pts_t[0][0][i]), float(pts_t[0][1][i])),
- (float(pts_t[1][0][i]), float(pts_t[1][1][i])),
- (float(pts_t[2][0][i]), float(pts_t[2][1][i])),
- (float(pts_t[3][0][i]), float(pts_t[3][1][i])),
- ]
-
- for j, c in enumerate(chk.chrs):
- c.parsed_pts_ut = [
- (float(chk._cpts_ut[0][j][0]), float(chk._cpts_ut[0][j][1])),
- (float(chk._cpts_ut[1][j][0]), float(chk._cpts_ut[1][j][1])),
- (float(chk._cpts_ut[2][j][0]), float(chk._cpts_ut[2][j][1])),
- (float(chk._cpts_ut[3][j][0]), float(chk._cpts_ut[3][j][1])),
- ]
- c.parsed_pts_t = [
- (float(chk._cpts_t[0][j][0]), float(chk._cpts_t[0][j][1])),
- (float(chk._cpts_t[1][j][0]), float(chk._cpts_t[1][j][1])),
- (float(chk._cpts_t[2][j][0]), float(chk._cpts_t[2][j][1])),
- (float(chk._cpts_t[3][j][0]), float(chk._cpts_t[3][j][1])),
- ]
- # pylint:enable=protected-access
-
- def make_next_chain(self):
- for pt in self:
- pt.make_next_chain()
-
-
-def vmult(mat, x, y):
- """Multiplies mat times (x;y) in a way compatible with vectorization"""
- return (
- mat[0][0] * x + mat[0][1] * y + mat[0][2],
- mat[1][0] * x + mat[1][1] * y + mat[1][2],
- )
-
-
-def vmultinv(mat, x, y):
- """Performs inverse matrix multiplication with vectors."""
- det = mat[0][0] * mat[1][1] - mat[0][1] * mat[1][0]
- inv_det = 1 / det
- sxv = x - mat[0][2]
- syv = y - mat[1][2]
- return (
- (mat[1][1] * sxv - mat[0][1] * syv) * inv_det,
- (mat[0][0] * syv - mat[1][0] * sxv) * inv_det,
- )
-
-
-class ParsedText:
- """A text element that has been parsed into a list of lines"""
-
- def __init__(self, elem, ctable):
- """Initializes ParsedText with an element and a character table."""
- self.ctable = ctable
- self.textel = elem
-
- sty = elem.cspecified_style
- self.isflow = (
- elem.tag == FRtag
- or (elem.croot is not None and sty.get_link("shape-inside", elem.croot) is not None)
- or ipx(sty.get("inline-size"))
- )
- self._tree = None
- self.dchange, self.writtendx, self.writtendy = [None] * 3
- self.achange = False
- if DEPATHOLOGIZE:
- remove_position_overflows(elem)
- if self.isflow:
- self.parse_lines_flow()
- else:
- self.parse_lines()
- self.finish_lines()
-
- tlvllns = [
- line for line in self.lns if line.tlvlno is not None and line.tlvlno > 0
- ]
- # top-level lines after 1st
- self.isinkscape = (
- all(line.sprl for line in tlvllns)
- and len(tlvllns) > 0
- and all(
- line.style.get("-inkscape-font-specification") is not None
- for line in self.lns
- )
- )
- # probably made in Inkscape
- self.ismlinkscape = self.isinkscape and len(self.lns) > 1
- # multi-line Inkscape
-
- def duplicate(self):
- """Duplicates a PT and its text without reparsing"""
- ret = copy(self)
- ret.textel = self.textel.duplicate()
- ret.tree = None
- cmemo = dict(zip(self.tree.dds, ret.tree.dds))
-
- ret.lns = []
- for line in self.lns:
- if len(line.chrs) == 0:
- continue
- ret_ln = line.copy(cmemo)
- ret_ln.ptxt = ret
- ret.lns.append(ret_ln)
-
- # nextw et al. could be from any line, update after copying
- for ret_ln in ret.lns:
- for ret_w in ret_ln.chks:
- ret_w.nextw = cmemo.get(ret_w.nextw)
- ret_w.prevw = cmemo.get(ret_w.prevw)
- ret_w.prevsametspan = cmemo.get(ret_w.prevsametspan)
- return ret
-
- def txt(self):
- """Returns the text content of the parsed lines."""
- return [line.txt() for line in self.lns]
-
- def reparse(self):
- """Reparses the text element."""
- self.__init__(self.textel, self.ctable)
-
- @property
- def chrs(self):
- """Returns a list of characters in the parsed lines."""
- return [c for line in self.lns for c in line.chrs]
-
- def parse_lines(self, srcsonly=False):
- """
- Every text element in an SVG can be thought of as a group of lines.
- A line is a collection of text that gets its position from a single
- source element. This position may be directly set, continued from a
- previous line, or inherited from a previous line.
- """
- elem = self.textel
- # First we get the tree structure of the text and do all our gets
- dds, pts, pdict = self.tree.dds, self.tree.ptails, self.tree.pdict
-
- numd = len(dds)
- kids = list(elem)
- text = [ddv.text for ddv in dds]
- ptail = [[tel.tail for tel in ptv] for ptv in pts] # preceding tails
-
- # Next we find the top-level sodipodi:role lines
- xvs = [ParsedText.get_xy(ddv, "x") for ddv in dds]
- yvs = [ParsedText.get_xy(ddv, "y") for ddv in dds]
- dxvs = [ParsedText.get_xy(ddv, "dx") for ddv in dds]
- dyvs = [ParsedText.get_xy(ddv, "dy") for ddv in dds]
- nsprl = {ddv:ddv.get("sodipodi:role")=='line' for ddv in dds}
-
- # Find effective sprls (ones that are not disabled)
- esprl = [False]*len(dds)
- for i, ddv in enumerate(dds):
- esprl[i] = nsprl[ddv] and len(xvs[i]) == 1 and len(yvs[i]) == 1 and dds[i] in kids
-
- # If I don't have text and any descendants have position, disables spr:l
- if esprl[i] and (text[i] == "" or text[i] is None):
- dstop = [j for j in range(len(pts)) if dds[i] in pts[j]][0]
- # should exist
- for ddi in range(i + 1, dstop):
- if xvs[ddi][0] is not None or yvs[ddi][0] is not None:
- if text[ddi] is not None and text[ddi] != "":
- esprl[i] = False
-
- if DEPATHOLOGIZE and not srcsonly:
- # Prune any sodipodi:roles that are inactive
- for ii, d in enumerate(dds):
- if not esprl[ii] and nsprl[d]:
- d.set('sodipodi:role',None)
- nsprl[d] = False
-
- # Figure out which effective sprls are top-level
- types = [None] * len(dds)
- for i,ddv in enumerate(dds):
- if not esprl[i]:
- types[i] = NORMAL
- elif len(ptail[i]) > 0 and ptail[i][-1] is not None:
- types[i] = PRECEDEDSPRL
- elif dds[i] == kids[0] and text[0] is not None: # and len(text[0])>0:
- # 2022.08.17: I am not sure if the len(text[0])==0 condition
- # should be included. Inkscape prunes text='', so not relevant
- # most of the time. It does seem to make a difference though.
- types[i] = PRECEDEDSPRL
- else:
- types[i] = TLVLSPRL
-
- # Position has a property of bidirectional inheritance. A tspan can inherit
- # position from its parent or its descendant unless there is text in between.
- # Down-inheritance requires that text be present
- # Aborts if a sprl is encountered
- def inherits_from(iin):
- jmax = iin
- while (
- jmax < numd - 1
- and text[jmax] in ["", None]
- and pdict[dds[jmax + 1]] == dds[jmax]
- and not (esprl[jmax + 1])
- ):
- jmax += 1
- if jmax < numd - 1 and text[jmax] in ["", None]:
- jmax = iin
-
- jmin = iin
- while (
- jmin > 0
- and text[jmin - 1] in ["", None]
- and dds[jmin - 1] == pdict[dds[jmin]]
- and not (esprl[jmin - 1])
- ):
- jmin -= 1
- return jmin, jmax # includes endpoints
-
- def inherit_none(iin, xyt):
- if xyt[iin][0] is None:
- imin, imax = inherits_from(iin)
- vld = [i for i in range(imin, imax + 1) if xyt[i][0] is not None]
- if len(vld) > 0:
- if any(i <= iin for i in vld):
- vld = [i for i in vld if i <= iin]
- # inherit up if possible
- dist = [abs(i - iin) for i in vld]
- j = [vld[i] for i in range(len(vld)) if dist[i] == min(dist)][0]
- return xyt[j], dds[j]
- return xyt[iin], dds[iin]
-
- # For positions that are None, inherit from ancestor/descendants if possible
- ixs, iys = xvs[:], yvs[:]
- xsrcs, ysrcs = dds[:], dds[:]
-
- for i in [i for i in range(0, len(dds)) if ixs[i][0] is None]:
- ixs[i], xsrcs[i] = inherit_none(i, xvs)
- for i in [i for i in range(0, len(dds)) if iys[i][0] is None]:
- iys[i], ysrcs[i] = inherit_none(i, yvs)
-
- if ixs[0][0] is None:
- ixs[0] = [0] # at least the parent needs a position
- if iys[0][0] is None:
- iys[0] = [0]
-
- # Finally, walk the text tree generating lines
- lns = []
- if not srcsonly:
- self.lns = []
- sprl_inherits = None
- for ddi, typ, tel, sel, txt in self.tree.dgenerator():
- newsprl = typ == TYP_TEXT and types[ddi] == TLVLSPRL
-
- if (txt is not None and len(txt) > 0) or newsprl:
- sty = sel.cspecified_style
- ctf = sel.ccomposed_transform
- fsz, scf, utfs = composed_width(sel, "font-size")
-
- if newsprl:
- lht = max(
- composed_lineheight(sel), composed_lineheight(sel.getparent())
- )
-
- # Make a new line if we're sprl or if we have a new x or y
- makeline = len(lns) == 0
- makeline |= typ == TYP_TEXT and (
- newsprl
- or (
- types[ddi] == NORMAL
- and (ixs[ddi][0] is not None or iys[ddi][0] is not None)
- )
- )
- if makeline:
- edi = ddi
- if typ == TYP_TAIL:
- edi = dds.index(sel)
- xvl = ixs[edi]
- xsrc = xsrcs[edi]
- yvl = iys[edi]
- ysrc = ysrcs[edi]
- if newsprl:
- if len(lns) == 0:
- xvl = [ixs[0][0]]
- xsrc = xsrcs[0]
- yvl = [iys[0][0]]
- ysrc = ysrcs[0]
- else:
- xvl = [sprl_inherits.x[0]]
- xsrc = sprl_inherits.xsrc
- yvl = [sprl_inherits.y[0] + lht / scf]
- ysrc = sprl_inherits.ysrc
- issprl = True
- continuex = False
- continuey = False
- else:
- continuex = False
- issprl = False
- if xvl[0] is None:
- if len(lns) > 0:
- xvl = lns[-1].x[:]
- xsrc = lns[-1].xsrc
- else:
- xvl = ixs[0][:]
- xsrc = xsrcs[0]
- continuex = True
- continuey = False
- if yvl[0] is None:
- if len(lns) > 0:
- yvl = lns[-1].y[:]
- ysrc = lns[-1].ysrc
- else:
- yvl = iys[0][:]
- ysrc = ysrcs[0]
- continuey = True
-
- if srcsonly: # quit and return srcs of first line
- return xsrc, ysrc
-
- tlvlno = None
- if ddi < numd and dds[ddi] in kids:
- tlvlno = kids.index(dds[ddi])
- elif edi == 0:
- tlvlno = 0
-
- anch = sty.get("text-anchor")
- if len(lns) > 0 and not nsprl[sel] and edi>0:
- if lns[-1].anchor is not None:
- anch = lns[
- -1
- ].anchor # non-spr lines inherit the previous line's anchor
- if anch is None:
- anch = "start"
- txtdir = sty.get("direction")
- if txtdir is not None:
- if txtdir == "rtl":
- if anch == "start":
- anch = "end"
- elif anch == "end":
- anch = "start"
-
- sprlabove = []
- cel = dds[edi]
- while cel != elem:
- if nsprl[cel]:
- sprlabove.append(cel)
- cel = pdict[cel]
-
- lns.append(
- TLine(
- self,
- xvl,
- yvl,
- xsrc,
- ysrc,
- issprl,
- sprlabove,
- anch,
- ctf,
- tlvlno,
- sty,
- continuex,
- continuey,
- )
- )
-
- if newsprl or len(lns) == 1:
- sprl_inherits = lns[-1]
-
- fsty = font_style(sty)
- tsty = true_style(fsty)
- if txt is not None:
- if typ==TYP_TEXT:
- dxv = dxvs[ddi]
- dyv = dyvs[ddi]
- if dxv[0] is None:
- dxv = [0]*len(txt)
- else:
- dxv = dxv + [0]*(len(txt)-len(dxv))
- if dyv[0] is None:
- dyv = [0]*len(txt)
- else:
- dyv = dyv + [0]*(len(txt)-len(dyv))
- else:
- dxv = [0]*len(txt)
- dyv = [0]*len(txt)
-
-
- for j, c in enumerate(txt):
- csty = self.font_picker(txt, j, fsty, tsty)
- prop = self.ctable.get_prop(c, csty)
- _ = TChar(
- c,
- fsz,
- utfs,
- prop,
- sty,
- csty,
- CLoc(tel, typ, j),
- lns[-1], dxv[j], dyv[j]
- )
-
- if j == 0:
- lsp0 = lns[-1].chrs[-1].lsp
- bshft0 = lns[-1].chrs[-1].bshft
- else:
- lns[-1].chrs[-1].lsp = lsp0
- lns[-1].chrs[-1].bshft = bshft0
-
- self.lns = lns
-
- def finish_lines(self):
- """Finalizes the parsed lines by calculating deltas and chunks."""
- if self.lns is not None:
- self.writtendx = any(c.dx != 0 for c in self.chrs)
- self.writtendy = any(c.dy != 0 for c in self.chrs)
-
- for line in self.lns:
- line.ptxt = self
- line.parse_chunks()
-
- for line in reversed(self.lns):
- if len(line.chrs) == 0:
- self.lns.remove(line)
- # prune empty lines
-
- def make_next_chain(self):
- """ For manual kerning removal, assign next and previous chunks """
- yvs = [
- line.y[0] for line in self.lns if line.y is not None and len(line.y) > 0
- ]
- tol = 0.001
- for unqy in uniquetol(yvs, tol):
- samey = [
- self.lns[i]
- for i in range(len(self.lns))
- if abs(yvs[i] - unqy) < tol
- ]
- sameyws = [chk for line in samey for chk in line.chks]
- xvs = [0.5*(chk.pts_ut[0][0] + chk.pts_ut[3][0]) for line in samey for chk in line.chks]
- sws = [
- x for _, x in sorted(zip(xvs, sameyws), key=lambda pair: pair[0])
- ] # chunks sorted in ascending x
- for i in range(1, len(sws)):
- # Account for space import bug where space has same position as prior char
- if sws[i - 1].txt==' ' and abs(sws[i - 1].pts_ut[0][0]-sws[i].pts_ut[0][0])<.01*sws[i - 1].spw:
- sws[i-1:i+1] = [sws[i],sws[i-1]]
- for i in range(1, len(sws)):
- sws[i - 1].nextw = sws[i]
- sws[i].prevw = sws[i - 1]
- sws[i].prevsametspan = (
- sws[i - 1].chrs[-1].loc.sel == sws[i].chrs[0].loc.sel
- )
-
- def font_picker(self, txt, j, fsty, tsty):
- """Determine what font Inkscape will render a character as"""
- if txt[j] != " ":
- # Most of the time we want the true style of the text. When this is
- # determined by Pango measurement, this practically always matches
- # Inkscape's font selection
- return tsty
- # If the character is a space and the surrounding characters are
- # missing from the true style, Pango will replace the space with a
- # space from the missing characters' font
- lbc = next((txt[i] for i in range(j - 1, -1, -1) if txt[i].strip()), None)
- fac = next((txt[i] for i in range(j + 1, len(txt)) if txt[i].strip()), None)
- if (
- lbc is not None
- and fac is not None
- and self.ctable.cstys[fsty][lbc] == self.ctable.cstys[fsty][fac]
- ):
- return self.ctable.cstys[fsty][lbc]
- if lbc is None and fac is not None:
- return self.ctable.cstys[fsty][fac]
- if fac is None and lbc is not None:
- return self.ctable.cstys[fsty][lbc]
- return tsty
- # Good fonts to check:
- # MS Outlook : missing most characters, but has a space
- # Marlett : charset doesn't have a space character, but somehow Pango
- # is finding one different from the one provided by
- # fontconfig's font_sort fallback
-
- def strip_sodipodi_role_line(self):
- """Strip every sodipodi:role line from an element without changing
- positions"""
- if any(ddv.get("sodipodi:role") == "line" for ddv in self.tree.dds):
- # Store old positions
- oxs = [c.pts_ut[0][0] for line in self.lns for c in line.chrs]
- oys = [c.pts_ut[0][1] for line in self.lns for c in line.chrs]
- odxs = [c.dx for line in self.lns for c in line.chrs]
- odys = [c.dy for line in self.lns for c in line.chrs]
- _ = [ddv.set("sodipodi:role", None) for ddv in self.tree.dds]
-
- pput = [c.parsed_pts_ut for line in self.lns for c in line.chrs]
- ppt = [c.parsed_pts_t for line in self.lns for c in line.chrs]
-
- deleteempty(self.textel)
- self.reparse()
- ii = 0;
- for line in self.lns:
- for c in line.chrs:
- c.parsed_pts_ut = pput[ii]
- c.parsed_pts_t = ppt[ii]
- ii += 1
-
- # Correct the position of the first character
- chrs = [c for line in self.lns for c in line.chrs]
- for i, line in enumerate(self.lns):
- myi = chrs.index(line.chrs[0])
- dx = oxs[myi] - chrs[myi].pts_ut[0][0]
- dy = oys[myi] - chrs[myi].pts_ut[0][1]
- if abs(dx) > 0.001 or abs(dy) > 0.001:
- newxv = [x + dx for x in line.x]
- newyv = [y + dy for y in line.y]
- if line.continuex or line.continuey:
- if line.chrs[0].loc.typ == TYP_TAIL:
- # wrap in a trivial Tspan so we can set x and y
- line.chrs[0].add_style(
- {"baseline-shift": "0%"}, setdefault=False
- )
- line.xsrc = line.chrs[0].loc.elem
- line.ysrc = line.chrs[0].loc.elem
- line.continuex = False
- line.continuey = False
- line.write_xy(newx=newxv, newy=newyv)
-
- # Fix non-first dxs
- ndxs = [c.dx for line in self.lns for c in line.chrs]
- ndys = [c.dy for line in self.lns for c in line.chrs]
- for i, c in enumerate(chrs):
- if c.lnindex > 0:
- if abs(odxs[i] - ndxs[i]) > 0.001 or abs(odys[i] - ndys[i]) > 0.001:
- c.dx = odxs[i]
- c.dy = odys[i]
- self.write_dxdy()
- for line in self.lns:
- for chk in line.chks:
- chk.charpos = None
-
- def strip_text_baseline_shift(self):
- """Remove baseline-shift if applied to the whole element"""
- if "baseline-shift" in self.textel.cspecified_style:
- if len(self.lns) > 0 and len(self.lns[0].chrs) > 0:
- lny = self.lns[0].y[:]
- bsv = self.lns[0].chrs[0].bshft
- self.textel.cstyle["baseline-shift"] = None
- self.reparse()
- newy = [y - bsv for y in lny]
- self.lns[0].write_xy(newy=newy)
-
- @staticmethod
- def get_xy(elem, xyt):
- """Gets the x or y coordinate values from an element."""
- val = EBget(elem, xyt) # fine for 'x','y','dx','dy'
- if not val:
- return [None] # None forces inheritance
- return [None if x == "none" else ipx(x) for x in val.split()]
-
- def write_dxdy(self):
- """
- After dx/dy has changed, call this to write them to the text element
- For simplicity, this is best done at the ParsedText level all at once
- """
- if self.dchange:
- # Group characters by location
- cs_loc = {(d,typ):[] for d in self.textel.descendants2() for typ in [TYP_TEXT,TYP_TAIL]}
- for ln in self.lns:
- for c in ln.chrs:
- cs_loc[c.loc.elem,c.loc.typ].append(c)
-
- for (d, typ), cd in cs_loc.items():
- dx = [c.dx for c in cd]
- dy = [c.dy for c in cd]
-
- dxset = trim_list(dx,0)
- dyset = trim_list(dy,0)
-
- if typ==TYP_TAIL and (dxset is not None or dyset is not None):
- d = wrap_string(d,typ)
- self.tree = None # invalidate
- typ = TYP_TEXT
- for c in cd:
- c.loc = CLoc(d,TYP_TEXT,c.loc.ind)
-
- if typ==TYP_TEXT:
- xyset(d,"dx",dxset)
- xyset(d,"dy",dyset)
-
- for chk in [chk for line in self.lns for chk in line.chks]:
- chk.charpos = None
-
- self.writtendx = any(c.dx != 0 for c in self.chrs)
- self.writtendy = any(c.dy != 0 for c in self.chrs)
- self.dchange = False
-
- def write_axay(self):
- """
- After dx/dy has changed, call this to write them to the text element
- For simplicity, this is best done at the ParsedText level all at once
- """
- if self.achange:
- # Group characters by location
- cs_loc = {(d,typ):[] for d in self.textel.descendants2() for typ in [TYP_TEXT,TYP_TAIL]}
- for ln in self.lns:
- for c in ln.chrs:
- cs_loc[c.loc.elem,c.loc.typ].append(c)
-
- for (d, typ), cd in cs_loc.items():
- ax = [c.ax for c in cd]
- ay = [c.ay for c in cd]
-
- axset = trim_list(ax,None)
- ayset = trim_list(ay,None)
- if ayset is not None and all(ayset[0]==ayv for ayv in ayset):
- ayset = ayset[0:1]
-
- if typ==TYP_TAIL and (axset is not None or ayset is not None):
- d = wrap_string(d,typ)
- self.tree = None # invalidate
- typ = TYP_TEXT
- for c in cd:
- c.loc = CLoc(d,TYP_TEXT,c.loc.ind)
-
- if typ==TYP_TEXT:
- # When an x/y ends in Nones they can be trimmed off, but when
- # they have internal Nones the string must be split
- if (axset is not None and None in axset) or (ayset is not None and None in ayset):
- span = inkex.Tspan if d.tag in TEtags else inkex.FlowSpan
- sx = [i+1 for i,v in enumerate(axset[:-1]) if v is None and axset[i+1] is not None]
- sy = [i+1 for i,v in enumerate(ayset[:-1]) if v is None and ayset[i+1] is not None]
- sidx = sorted(sx+sy)
- ranges = [(sidx[i-1] if i > 0 else 0, sidx[i] if i < len(sidx) else len(cd)) for i in range(len(sidx) + 1)]
- txt = d.text
- d.text = None
- for k,(r1,r2) in enumerate(reversed(ranges)):
- t = span() if k 0:
- cel = line.chrs[0].loc.elem
- while cel != elem and cel.getparent() != elem:
- cel = cel.getparent()
- tlvl = cel
-
- xyset(tlvl.getparent(), "x", line.x)
- xyset(tlvl.getparent(), "y", line.y)
-
- tlvl.set("sodipodi:role", "line")
- # reenable sodipodi so we can insert returns
-
- if self.writtendx or self.writtendy:
- for i, chrv in enumerate(self.lns[0].chrs):
- chrv.dx = olddx[i]
- chrv.dy = olddy[i]
- self.dchange = True
- # may have deleted spr lines
-
- # For single lines, reset line-height to default
- changed_styles = dict()
- if len(self.lns) == 1:
- for ddv in elem.descendants2():
- if "line-height" in ddv.cstyle:
- changed_styles[ddv] = ddv.cstyle
- del changed_styles[ddv]["line-height"]
-
- if len(self.chrs) > 0:
- # Clear all fonts and only apply to relevant Tspans
-
- for ddv in elem.descendants2():
- sty = changed_styles.get(ddv, ddv.cstyle)
- for key in [
- "font-family",
- "font-stretch",
- "font-weight",
- "font-style",
- "-inkscape-font-specification",
- ]:
- if key in sty:
- del sty[key]
- changed_styles[ddv] = sty
-
- for c in self.chrs:
- sty = changed_styles.get(c.loc.sel, c.loc.sel.cstyle)
- sty.update(c.fsty)
- changed_styles[c.loc.sel] = sty
-
- # Put the first char's font at top since that's what Inkscape displays
- # sty = Style(tuple(elem.cstyle.items()))
- sty = changed_styles.get(elem, elem.cstyle)
- sty.update(self.chrs[0].fsty)
- changed_styles[elem] = sty
-
- # Try to set nominal font size to max value
- # (value used by line-height, what Inkscape reports, etc.)
- fs_origins = set()
- for c in self.chrs:
- cel = c.loc.sel
- fs_origins.add(cel)
- celstyle = changed_styles.get(cel, cel.cstyle)
- while (
- ("font-size" in celstyle and "%" in str(celstyle["font-size"]))
- or ("font-size" not in celstyle)
- ) and cel is not elem:
- cel = cel.getparent()
- fs_origins.add(cel)
- celstyle = changed_styles.get(cel, cel.cstyle)
- if elem not in fs_origins and (
- len(self.lns) == 1 or all(not line.sprl for line in self.lns[1:])
- ):
- sty = changed_styles.get(elem, elem.cstyle)
- maxsize = max([c.utfs for c in self.chrs])
- if "font-size" not in sty or sty["font-size"] != maxsize:
- sty["font-size"] = str(max([c.utfs for c in self.chrs]))
- changed_styles[elem] = sty
- for k, val in changed_styles.items():
- k.cstyle = val
-
- def split_off_chunks(self, chks):
- """Splits off specified chunks of text into a new element."""
- return self.split_off_characters([c for chk in chks for c in chk.chrs])
-
- def split_off_characters(self, chrs):
- """Splits off specified characters into a new element."""
- npt = self.duplicate()
- newtxt = npt.textel
-
- # Record position
- cs1 = [c for ln in self.lns for c in ln.chrs]
- cs2 = [c for ln in npt.lns for c in ln.chrs]
- dmemo = dict(zip(cs1,cs2))
-
- ps1 = {c:c.pts_ut[0] for c in cs1}
- ps2 = {c:c.pts_ut[0] for c in cs2}
- ds = {c:(c.dx,c.dy) for c in cs2}
- fc = dmemo[chrs[0]]
-
-
- # chars' line index
- iln = self.lns.index(chrs[0].line)
- ciis = [c.lnindex for c in chrs] # indexs of charsin line
-
- fusex = self.lns[iln].continuex or ciis[0] > 0 or ds[fc][0] != 0
- fusey = self.lns[iln].continuey or ciis[0] > 0 or ds[fc][1] != 0
- if fusex:
- anfr = self.lns[iln].anchfrac
- oldx = chrs[0].pts_ut[0][0] * (1 - anfr) + chrs[-1].pts_ut[3][0] * anfr
- if fusey:
- oldy = chrs[0].chk.y + ds[fc][1]
-
- for c in reversed(chrs):
- c.delc(updatedelta=False)
-
- # Delete the other lines/chars in the copy
- for il2 in reversed(range(len(npt.lns))):
- if il2 != iln:
- npt.lns[il2].dell()
- else:
- nln = npt.lns[il2]
- for j in reversed(range(len(nln.chrs))):
- if j not in ciis:
- nln.chrs[j].delc(updatedelta=False)
-
- # Deletion of text can cause the srcs to be wrong.
- # Reparse to find whereit is now
- nln.xsrc, nln.ysrc = npt.parse_lines(srcsonly=True)
- nln.write_xy(newx=nln.x, newy=nln.y)
- nln.disablesodipodi(force=True)
- if len(self.lns) > 0:
- self.lns[0].xsrc, self.lns[0].ysrc = self.parse_lines(srcsonly=True)
- self.lns[0].write_xy(newx=self.lns[0].x, newy=self.lns[0].y)
-
- if fusex:
- nln.continuex = False
- nln.write_xy(newx=[oldx])
- ds[fc] = (0,ds[fc][1])
- if fusey:
- nln.continuey = False
- nln.write_xy(newy=[oldy])
- ds[fc] = (ds[fc][0],0)
-
-
- for ln in npt.lns:
- for c in ln.chrs:
- c.dx = ds[c][0]
- c.dy = ds[c][1]
- npt.write_dxdy()
-
- # In case there are some errors, correct with deltas
- for pt, ps in [(self,ps1), (npt,ps2)]:
- for ln in pt.lns:
- for chk in ln.chks:
- Deltax = [c.pts_ut[0][0] - ps[c][0] for c in chk.chrs]
- Deltay = [c.pts_ut[0][1] - ps[c][1] for c in chk.chrs]
-
- if any(abs(d) > 0.001 for d in Deltax + Deltay):
- for i in range(len(Deltax)):
- if i == 0:
- if ln.anchfrac == 0:
- dx = Deltax[0]
- dy = Deltay[0]
- elif ln.anchfrac == 0.5:
- dx = Deltax[0] + Deltax[-1]
- dy = Deltay[0] + Deltay[-1]
- else:
- dx = 0
- dy = 0
- # right aligned: would not be able to compute
- else:
- dx = Deltax[i] - Deltax[i - 1]
- dy = Deltay[i] - Deltay[i - 1]
- chk.chrs[i].dx -= dx
- chk.chrs[i].dy -= dy
- pt.write_dxdy()
-
- newtxt._parsed_text = npt
- return newtxt
-
- def delete_empty(self):
- """Deletes empty elements from the doc. Generally this is done last"""
- ocs_pts = [c.pts_t for c in self.chrs]
- ocs_dx = [c.dx for c in self.chrs]
- ocs_dy = [c.dy for c in self.chrs]
- dltd = deleteempty(self.textel)
- if dltd:
- self.tree = None
- self.reparse()
- # Correct the position of the first character
- chrs = [c for line in self.lns for c in line.chrs]
- for i, line in enumerate(self.lns):
- myi = chrs.index(line.chrs[0])
- dxt = ocs_pts[myi][0][0] - chrs[myi].pts_t[0][0]
- dyt = ocs_pts[myi][0][1] - chrs[myi].pts_t[0][1]
-
- if abs(dxt) > 0.001 or abs(dyt) > 0.001:
- oldpos = vmultinv(chrs[myi].line.transform.matrix, ocs_pts[myi][0][0], ocs_pts[myi][0][1])
- dltax = oldpos[0] - chrs[myi].pts_ut[0][0]
- dltay = oldpos[1] - chrs[myi].pts_ut[0][1]
- newxv = [x + dltax for x in line.x]
- newyv = [y + dltay for y in line.y]
-
- if line.continuex or line.continuey:
- if line.chrs[0].loc.typ == TYP_TAIL:
- # wrap in a trivial Tspan so we can set x and y
- line.chrs[0].add_style(
- {"baseline-shift": "0%"}, setdefault=False
- )
- line.xsrc = line.chrs[0].loc.elem
- line.ysrc = line.chrs[0].loc.elem
- line.continuex = False
- line.continuey = False
- line.write_xy(newx=newxv, newy=newyv)
-
- # Fix non-first dxs
- ndxs = [c.dx for line in self.lns for c in line.chrs]
- ndys = [c.dy for line in self.lns for c in line.chrs]
- for i, c in enumerate(chrs):
- if c.lnindex > 0:
- if abs(ocs_dx[i] - ndxs[i]) > 0.001 or abs(ocs_dy[i] - ndys[i]) > 0.001:
- c.dx = ocs_dx[i]
- c.dy = ocs_dy[i]
- self.write_dxdy()
- for line in self.lns:
- for chk in line.chks:
- chk.charpos = None
-
- def fuse_fonts(self):
- """
- For exporting, one usually wants to replace fonts with the actual font
- that each character is rendered as.
-
- Other SVG renderers may only be able to use the font face name (i.e.,
- the fullname in fontconfig, which is the same name reported as Face in
- Text and Font), so we make this the family name and add the true
- family as a backup (with appropriate weight, width, and slant).
-
- For example, the ultra-bold variation of Arial has a face name of
- Arial Black, while in Inkscape its CSS name is Arial Heavy. Therefore,
- we would change its font-family to 'Arial Black','Arial'.
- """
- # Collect all the fonts and how they are rendered
- cstys = self.ctable.cstys
- newfams = []
- for line in self.lns:
- for c in line.chrs:
- newfam = (c.fsty, c, c.loc.sel)
- if c.c in cstys[c.fsty] and cstys[c.fsty][c.c] is not None:
- csty = cstys[c.fsty][c.c]
- fullname = fcfg.get_true_font_fullname(csty)
- fontfam = csty["font-family"].strip("'")
- fusefam = "'" + fullname + "','" + fontfam + "'"
- fusesty = Style(
- {
- k: v if k != "font-family" else fusefam
- for k, v in csty.items()
- }
- )
- if not c.fsty == fusesty:
- newfam = (fusesty, c, c.loc.sel)
-
- newfams.append(newfam)
-
- # Make a dictionary whose keys are the elements whose styles we
- # want to modify and whose values are (the new family, a list of characters)
- # In descending order of the number of characters
- torepl = {}
- for csty, c, sel in newfams:
- torepl.setdefault(sel, []).append((csty, c))
- for sel, value in torepl.items():
- count_dict = {}
- for csty, c in value:
- count_dict.setdefault(csty, []).append(c)
- new_value = []
- for k, val in count_dict.items():
- new_tup = (k, val)
- new_value.append(new_tup)
- new_value.sort(key=lambda x: len(x[1]), reverse=True)
- torepl[sel] = new_value
-
- # Replace fonts
- for elem, rlst in torepl.items():
- # For the most common family, set the element style itself
- if not font_style(elem.ccascaded_style) == rlst[0][0]:
- elem.cstyle = elem.ccascaded_style
- for k, val in rlst[0][0].items():
- elem.cstyle[k] = val
- elem.cstyle["-inkscape-font-specification"] = None
- for c in rlst[0][1]:
- c.sty = elem.cstyle
- # For less common, need to wrap in a new Tspan
- for r in rlst[1:]:
- for c in r[1]:
- c.add_style(
- {
- "font-family": r[0]["font-family"],
- "font-weight": r[0]["font-weight"],
- "font-style": r[0]["font-style"],
- "font-stretch": r[0]["font-stretch"],
- "baseline-shift": "0%",
- },
- setdefault=False,
- )
-
- def flow_to_text(self):
- """Converts flowed text into normal text, returning the text els."""
- if self.isflow:
- newtxts = []
- for line in reversed(self.lns):
- nany = any(math.isnan(yvl) for yvl in line.y)
-
- anch = line.anchor
- algn = {"start": "start", "middle": "center", "end": "end"}[anch]
-
- origx = None
- if len(line.chrs) > 0:
- origx = line.chrs[0].pts_ut[0][0]
-
- newtxt = self.split_off_characters(line.chrs)
- if newtxt.tag == FRtag:
- for ddv in newtxt.descendants2():
- if ddv.tag == FRtag:
- ddv.tag = TextElement.ctag
- elif isinstance(ddv, (FlowPara, FlowSpan)):
- ddv.tag = TStag
- elif isinstance(ddv, inkex.FlowRegion):
- ddv.delete()
- else:
- newtxt.cstyle["shape-inside"] = None
- newtxt.cstyle["inline-size"] = None
- for k in list(newtxt):
- k.cstyle["text-align"] = algn
- k.cstyle["text-anchor"] = anch
-
- if nany:
- newtxt.delete()
- else:
- deleteempty(newtxt)
- npt = newtxt.parsed_text
- if (
- origx is not None
- and len(npt.lns) > 0
- and len(npt.lns[0].chrs) > 0
- ):
- npt.reparse()
- newx = [
- xvl + origx - npt.lns[0].chrs[0].pts_ut[0][0]
- for xvl in npt.lns[0].x
- ]
- npt.lns[0].write_xy(newx)
- newtxts.append(newtxt)
-
- self.textel.delete()
- return newtxts
- return []
-
- # For debugging: make a rectange at all of the line's chunks' nominal extents
- HIGHLIGHT_STYLE = "fill:#007575;fill-opacity:0.4675" # mimic selection
-
- def make_highlights(self, htype):
- """Creates rectangles to highlight extents or bounding boxes."""
- if htype == "char":
- exts = self.get_char_extents()
- elif htype == "charink":
- exts = self.get_char_inkbbox()
- elif htype == "fullink":
- exts = [self.get_full_inkbbox()]
- elif htype == "chunk":
- exts = self.get_chunk_extents()
- elif htype == "line":
- exts = self.get_line_extents()
- elif htype == "chunkink":
- exts = self.get_chunk_ink()
- elif htype == "lineink":
- exts = self.get_line_ink()
- else: # 'all'
- exts = [self.get_full_extent()]
- for i, ext in enumerate(exts):
- r = inkex.Rectangle()
- r.set("x", ext.x1)
- r.set("y", ext.y1)
- r.set("height", ext.h)
- r.set("width", ext.w)
- r.set("transform", self.textel.ccomposed_transform)
- sty = (
- ParsedText.HIGHLIGHT_STYLE
- if i % 2 == 0
- else ParsedText.HIGHLIGHT_STYLE.replace("0.4675", "0.5675")
- )
- r.set("style", sty)
- self.textel.croot.append(r)
-
- def differential_to_absolute_kerning(self):
- """ Converts any differential kerning to absolute kerning """
- if not self.isflow and any(c.dx!=0 for c in self.chrs):
- for ln in self.lns:
- ln.disablesodipodi()
- for i,w in enumerate(ln.chks):
- pts = [c.pts_ut for c in w.chrs]
- for j, c in enumerate(w.chrs):
- if c.dx != 0:
- # Get last char in chunk without a dx
- lc = next((j + idx for idx, c in enumerate(w.chrs[j+1:]) if c.dx != 0), len(w.chrs) - 1)
- c.ax = pts[j][0][0] * (1 - ln.anchfrac) + pts[lc][3][0] * ln.anchfrac
- c.dx = 0
- else:
- c.ax = w.x if j==0 else None
-
- if c.dy !=0:
- c.ay = pts[j][0][1]
- c.dy = 0
- else:
- c.ay = w.y if j==0 else None
- self.write_dxdy()
- self.write_axay()
-
- ptsut = [c.parsed_pts_ut for c in self.chrs]
- ptst = [c.parsed_pts_t for c in self.chrs]
- self.reparse()
- for i, c in enumerate(self.chrs):
- c.parsed_pts_ut = ptsut[i]
- c.parsed_pts_t = ptst[i]
-
- # Bounding box functions
- def get_char_inkbbox(self):
- """Gets the untransformed bounding boxes of all characters' ink."""
- exts = []
- if self.lns is not None and len(self.lns) > 0 and self.lns[0].xsrc is not None:
- for line in self.lns:
- for chk in line.chks:
- for c in chk.chrs:
- pt1 = c.pts_ut_ink[0]
- pt2 = c.pts_ut_ink[2]
- if not math.isnan(pt1[1]):
- exts.append(bbox((pt1, pt2)))
- return exts
-
- def get_full_inkbbox(self):
- """Gets the untransformed bounding box of the whole element."""
- ext = bbox(None)
- if self.lns is not None and len(self.lns) > 0 and self.lns[0].xsrc is not None:
- for line in self.lns:
- for chk in line.chks:
- for c in chk.chrs:
- pt1 = c.pts_ut_ink[0]
- pt2 = c.pts_ut_ink[2]
- ext = ext.union(bbox((pt1, pt2)))
- return ext
-
- # Extent functions
- def get_char_extents(self):
- """Gets the untransformed extent of each character."""
- exts = []
- if self.lns is not None and len(self.lns) > 0 and self.lns[0].xsrc is not None:
- for line in self.lns:
- for chk in line.chks:
- for c in chk.chrs:
- pt1 = c.pts_ut[0]
- pt2 = c.pts_ut[2]
- if not math.isnan(pt1[1]):
- exts.append(bbox((pt1, pt2)))
- return exts
-
- def get_chunk_extents(self):
- """Gets the untransformed extent of each chunk."""
- exts = []
- if self.lns is not None and len(self.lns) > 0 and self.lns[0].xsrc is not None:
- for line in self.lns:
- for chk in line.chks:
- pt1 = chk.pts_ut[0]
- pt2 = chk.pts_ut[2]
- if not math.isnan(pt1[1]):
- exts.append(bbox((pt1, pt2)))
- return exts
-
- def get_line_extents(self):
- """Gets the untransformed extent of each line."""
- exts = []
- if self.lns is not None and len(self.lns) > 0 and self.lns[0].xsrc is not None:
- for line in self.lns:
- extln = bbox(None)
- for chk in line.chks:
- pt1 = chk.pts_ut[0]
- pt2 = chk.pts_ut[2]
- if not math.isnan(pt1[1]):
- extln = extln.union(bbox((pt1, pt2)))
- if not (extln.isnull):
- exts.append(extln)
- return exts
-
- def get_chunk_ink(self):
- """Gets the untransformed extent of each chunk's ink."""
- exts = []
- if self.lns is not None and len(self.lns) > 0 and self.lns[0].xsrc is not None:
- for line in self.lns:
- for chk in line.chks:
- ext = bbox(None)
- for c in chk.chrs:
- pt1 = c.pts_ut_ink[0]
- pt2 = c.pts_ut_ink[2]
- ext = ext.union(bbox((pt1, pt2)))
- exts.append(ext)
- return exts
-
- def get_line_ink(self):
- """Gets the untransformed extent of each line's ink."""
- exts = []
- if self.lns is not None and len(self.lns) > 0 and self.lns[0].xsrc is not None:
- for line in self.lns:
- ext = bbox(None)
- for c in line.chrs:
- pt1 = c.pts_ut_ink[0]
- pt2 = c.pts_ut_ink[2]
- ext = ext.union(bbox((pt1, pt2)))
- exts.append(ext)
- return exts
-
- def get_full_extent(self, parsed=False):
- """
- Gets the untransformed extent of the whole element.
- parsed=True gets original prior to any mods
- """
- ext = bbox(None)
- if self.lns is not None and len(self.lns) > 0 and self.lns[0].xsrc is not None:
- for line in self.lns:
- for chk in line.chks:
- for c in chk.chrs:
- pts = (
- c.parsed_pts_ut
- if parsed and c.parsed_pts_ut is not None
- else c.pts_ut
- )
- pt1 = pts[0]
- pt2 = pts[2]
- if not math.isnan(pt1[1]):
- ext = ext.union(bbox((pt1, pt2)))
- return ext
-
- @property
- def tree(self):
- """Returns the text tree of the element."""
- if self._tree is None:
- self._tree = TextTree(self.textel)
- return self._tree
-
- @tree.setter
- def tree(self, val):
- """Sets the text tree of the element."""
- if val is None:
- self._tree = None
-
- def parse_lines_flow(self):
- """
- Parses lines of flowed text.
- For non-rectangular flow regions, uses svgpathtools to find where
- lines are drawn (this is imported conditionally).
- """
- self.lns = []
- sty = self.textel.cspecified_style
- isflowroot = self.textel.tag == FRtag
- isshapeins = (
- self.textel.tag == TEtag
- and sty.get_link("shape-inside", self.textel.croot) is not None
- )
- isz = ipx(sty.get("inline-size"))
- isinlinesz = self.textel.tag == TEtag and isz
- # Inkscape ignores 0 and invalid inline-size
-
- # Determine the flow region
- otp_support = self.textel.otp_support_prop
- if isshapeins:
- frgn = sty.get_link("shape-inside", self.textel.croot)
- dfr = frgn.duplicate()
-
- # Fuse transform to path
- dfr.object_to_path()
- dfr.set("d", str(dfr.cpath.transform(dfr.ctransform)))
- dfr.cpath = None
- dfr.ctransform = None
-
- # shape transform fused on path (not composed transform though)
- self.textel.append(dfr)
- region = dfr
- # Find the bbox of the FlowRegion
- elif isflowroot:
- for ddv in self.textel.descendants2():
- if isinstance(ddv, FlowRegion):
- pths = [p for p in ddv.descendants2() if p.tag in otp_support]
- if len(pths) > 0:
- region = pths[0]
- elif isinlinesz:
- # Make a Rectangle and treat it as the flow region
- r = inkex.Rectangle()
- _, ysrc = self.parse_lines(srcsonly=True)
- iszx = self.textel.get("x")
- iszy = self.textel.get("y", ysrc.get("y"))
-
- afr = get_anchorfrac(sty.get("text-anchor"))
- r.set("x", -isz * afr) # pylint:disable=invalid-unary-operand-type
- r.set("y", 0)
- r.set("height", isz)
- r.set("width", isz)
- self.textel.croot.append(r)
- region = r
-
- padding = ipx(self.textel.cspecified_style.get("shape-padding", "0"))
- isrect = isrectangle(region, includingtransform=False)
-
- usesvt = not isrect and region.tag in otp_support and padding == 0
- if not isrect and not usesvt:
- # Flow region we cannot yet handle, parse as normal text
- # This works as long as the SVG1.1 fallback is being used
- self.isflow = False
- return self.parse_lines()
- if usesvt:
- region.object_to_path()
- import svgpathtools as spt # pylint: disable=import-error, import-outside-toplevel
-
- sptregion = spt.parse_path(region.get("d"))
- if not sptregion.isclosed():
- end = sptregion[-1].end
- start = sptregion[0].start
- sptregion.append(spt.Line(end, start))
- sptregion.closed = True
-
- bbx = region.cpath.bounding_box()
- bbx = [bbx.left, bbx.top, bbx.width, bbx.height]
- if not padding == 0:
- bbx = [
- bbx[0] + padding,
- bbx[1] + padding,
- bbx[2] - 2 * padding,
- bbx[3] - 2 * padding,
- ]
-
- # Delete duplicate
- if isshapeins:
- dfr.delete()
- elif isinlinesz:
- r.delete()
-
- def height_above_below_baseline(elem):
- lht = composed_lineheight(elem)
- lsty = true_style(elem.cspecified_style)
- fsz, scf, _ = composed_width(elem, "font-size")
-
- absp = (0.5000 * (lht / fsz - 1) + CharacterTable.flowy(lsty)) * (
- fsz / scf
- ) # spacing above baseline
- bbsp = (0.5000 * (lht / fsz - 1) + 1 - CharacterTable.flowy(lsty)) * (
- fsz / scf
- ) # spacing below baseline
- rawfs = fsz / scf
- return absp, bbsp, rawfs
-
- # Get the properties of the FlowRoot
- rabsp, rbbsp, rfs = height_above_below_baseline(self.textel)
- rpct = (
- "line-height" in self.textel.cspecified_style
- and "%" in self.textel.cspecified_style["line-height"]
- )
- if rpct:
- pctage = float(self.textel.cspecified_style["line-height"].strip("%"))
-
- # Group characters into lines
- lns = []
- fparas = [
- k for k in list(self.textel) if isinstance(k, FlowPara)
- ] # top-level FlowParas
- for ddi, typ, tel, sel, txt in self.tree.dgenerator():
- if txt is not None and len(txt) > 0:
- if isflowroot:
- lnno = [
- i
- for i, fpv in enumerate(fparas)
- if fpv in tel.ancestors2(includeme=True)
- ]
- if len(lnno) == 0:
- lnno = 0
- else:
- lnno = lnno[0]
- # tails of a FlowPara belong to the next line
- if tel == fparas[lnno] and typ == TYP_TAIL:
- lnno += 1
- else:
- # Note: Tspans don't do anything in SVG2 flows
- lnno = 0
-
- # Determine above- and below-baseline lineheight
- sty = sel.cspecified_style
- ctf = sel.ccomposed_transform
- fsz, scf, utfs = composed_width(sel, "font-size")
- absp, bbsp, mrfs = height_above_below_baseline(sel)
- lsty = true_style(sel.cspecified_style)
- fabsp = max(rabsp, absp)
- fbbsp = max(rbbsp, bbsp)
-
- # Inkscape has a bug when the FlowRoot has a line-height
- # specified as a percentage and the FlowPara doesn't have one
- # specified. Account for this
- anc = sel.ancestors2(includeme=True, stopbefore=self.textel)
- if rpct and all("line-height" not in a.cstyle for a in anc):
- if rbbsp > bbsp:
- fbbsp += (CharacterTable.flowy(lsty) - 0.5) * (rfs - mrfs)
- if absp > rabsp:
- fabsp -= (0.5 * (pctage / 100)) * (mrfs - rfs)
- fbbsp -= (
- 0.5 * (pctage / 100) - (CharacterTable.flowy(lsty) - 0.5)
- ) * (mrfs - rfs)
-
- if lnno >= len(lns):
- algn = sty.get("text-align", "start")
- anch = sty.get("text-anchor", "start")
- if not algn == "start" and anch == "start":
- anch = {"start": "start", "center": "middle", "end": "end"}[
- algn
- ]
- cln = TLine(
- self,
- [0],
- [0],
- self.textel,
- self.textel,
- False,
- [],
- anch,
- ctf,
- None,
- sty,
- False,
- False,
- )
- lns.append(cln)
- cln.broken = False
- cln.effabsp = fabsp
- cln.effbbsp = fbbsp
- else:
- cln = lns[lnno]
-
- if typ==TYP_TEXT:
- dxv = ParsedText.get_xy(self.tree.dds[ddi],'dx')
- dyv = ParsedText.get_xy(self.tree.dds[ddi],'dy')
- if dxv[0] is None:
- dxv = [0]*len(txt)
- else:
- dxv = dxv + [0]*(len(txt)-len(dxv))
- if dyv[0] is None:
- dyv = [0]*len(txt)
- else:
- dyv = dyv + [0]*(len(txt)-len(dyv))
- else:
- dxv = [0]*len(txt)
- dyv = [0]*len(txt)
-
- fsty = font_style(sty)
- tsty = true_style(fsty)
- for j, c in enumerate(txt):
- csty = self.font_picker(txt, j, fsty, tsty)
- prop = self.ctable.get_prop(c, csty)
- tchr = TChar(
- c, fsz, utfs, prop, sty, csty, CLoc(tel, typ, j), cln, dxv[j], dyv[j]
- )
- tchr.lhs = (fabsp, fbbsp)
- if j == 0:
- lsp0 = tchr.lsp
- bshft0 = tchr.bshft
- else:
- tchr.lsp = lsp0
- tchr.bshft = bshft0
- self.lns = lns
- self.finish_lines()
- self.fparaafter = False
-
- # Figure out where to break lines
- # Currently only works for rectangular flows
- i = 0
- breakcs = " -—!}|/?"
- lncs = [line.chrs for line in lns]
- blns = []
- lchr = None
- y = 0
- while i < len(lncs):
- if len(lncs[i]) > 0:
- fchr = lncs[i][0]
-
- lht = None
- getxintervals = True
- while getxintervals:
- getxintervals = False
- if lht is None:
- lht = fchr.line.effabsp + fchr.line.effbbsp
-
- # Determine x intervals where we can draw text
- if not usesvt:
- # For rectangles this is just the bounding box
- xlims = [(bbx[0], bbx[2])]
- else:
- # For other flows this is where at least 90% of the line
- # is unobstructed by the path
- # Start by finding intervals where the y=10% and 90% points
- # of the baseline intersect with the flow region
- ln10 = spt.Line(
- bbx[0] + (bbx[1] + y + lht * 0.1) * 1j,
- bbx[0] + bbx[2] + (bbx[1] + y + lht * 0.1) * 1j,
- )
- ln90 = spt.Line(
- bbx[0] + (bbx[1] + y + lht * 0.9) * 1j,
- bbx[0] + bbx[2] + (bbx[1] + y + lht * 0.9) * 1j,
- )
- isc10 = sptregion.intersect(ln10)
- isc90 = sptregion.intersect(ln90)
- pts10 = sorted(
- [
- sptregion.point(T1).real
- for (T1, seg1, t1), (T2, seg2, t2) in isc10
- ]
- )
- pts90 = sorted(
- [
- sptregion.point(T1).real
- for (T1, seg1, t1), (T2, seg2, t2) in isc90
- ]
- )
- intervals = []
- for j in range(int(len(pts10) / 2)):
- int10 = (pts10[2 * j], pts10[2 * j + 1])
- for k in range(int(len(pts90) / 2)):
- int90 = (pts90[2 * k], pts90[2 * k + 1])
- intrsc = (
- max(int10[0], int90[0]),
- min(int10[1], int90[1]),
- )
- if intrsc[1] > intrsc[0]:
- intervals.append(intrsc)
-
- # Use the tangent line at the 10 and 90% points to find the
- # points where the line is at least 90% unobstructed
- tol = lht * 1e-6
-
- def tangent_line(pos):
- pnt = sptregion.point(pos)
- drv = sptregion.derivative(pos)
- if not drv.real == 0:
- slp = drv.imag / drv.real
- intcp = pnt.imag - drv.imag / drv.real * pnt.real
- else:
- slp = drv.imag / tol
- intcp = pnt.imag - drv.imag / tol * pnt.real
- return slp, intcp
-
- def intersection_belowabove(pos, below=True):
- pnt = sptregion.point(pos)
- vrtl = spt.Line(
- pnt.real + (bbx[1]) * 1j,
- pnt.real + (bbx[1] + bbx[3]) * 1j,
- )
- try:
- intrsct = sptregion.intersect(vrtl)
- except AssertionError:
- return None, None
- yvs = sorted(
- [
- sptregion.point(T1b).imag
- for (T1b, seg1b, t1b), (T2b, seg2b, t2b) in intrsct
- ]
- )
- if below:
- yvs = [yvl for yvl in yvs if yvl > pnt.imag + tol]
- ret = yvs[0] if len(yvs) > 0 else None
- else:
- yvs = [yvl for yvl in yvs if yvl < pnt.imag - tol]
- ret = yvs[-1] if len(yvs) > 0 else None
- rett = (
- [
- T1b
- for (T1b, seg1b, t1b), (T2b, seg2b, t2b) in intrsct
- if sptregion.point(T1b).imag == ret
- ][0]
- if ret is not None
- else None
- )
- return ret, rett
-
- def bounding_lines(p_top, p_btm):
- if len(p_top) > 0:
- p_top = p_top[0]
- (mtop, btop) = tangent_line(p_top)
- ry, r_pos = intersection_belowabove(p_top, below=True)
- if ry is None or ry > bbx[1] + y + lht:
- (mbtm, bbtm) = (0, bbx[1] + y + lht)
- else:
- (mbtm, bbtm) = tangent_line(r_pos)
- else:
- p_btm = p_btm[0]
- (mbtm, bbtm) = tangent_line(p_btm)
- ry, r_pos = intersection_belowabove(p_btm, below=False)
- if ry is None or ry < bbx[1] + y:
- (mtop, btop) = (0, bbx[1] + y)
- else:
- (mtop, btop) = tangent_line(r_pos)
- return mtop, btop, mbtm, bbtm
-
- ints2 = []
- for inta, intb in intervals:
- t10a = [
- T1
- for (T1, seg1, t1), (T2, seg2, t2) in isc10
- if sptregion.point(T1).real == inta
- ]
- t90a = [
- T1
- for (T1, seg1, t1), (T2, seg2, t2) in isc90
- if sptregion.point(T1).real == inta
- ]
- t10b = [
- T1
- for (T1, seg1, t1), (T2, seg2, t2) in isc10
- if sptregion.point(T1).real == intb
- ]
- t90b = [
- T1
- for (T1, seg1, t1), (T2, seg2, t2) in isc90
- if sptregion.point(T1).real == intb
- ]
-
- mtopa, btopa, mbtma, bbtma = bounding_lines(t10a, t90a)
- mtopb, btopb, mbtmb, bbtmb = bounding_lines(t10b, t90b)
-
- dya = (mbtma * inta + bbtma) - (mtopa * inta + btopa)
- if dya < 0.9 * lht - tol:
- xp9a = (
- (0.9 * lht - (bbtma - btopa)) / (mbtma - mtopa)
- if mbtma != mtopa
- else None
- )
- xp9a = xp9a if xp9a >= inta else None
- else:
- xp9a = inta
-
- dyb = (mbtmb * intb + bbtmb) - (mtopb * intb + btopb)
- if dyb < 0.9 * lht - tol:
- xp9b = (
- (0.9 * lht - (bbtmb - btopb)) / (mbtmb - mtopb)
- if mbtmb != mtopb
- else None
- )
- xp9b = xp9b if xp9b <= intb else None
- else:
- xp9b = intb
-
- if xp9a is not None and xp9b is not None and xp9b >= xp9a:
- ints2.append((xp9a, xp9b))
- xlims = [(intv[0], intv[1] - intv[0]) for intv in ints2]
-
- # Intersection diagnostics
- # for p in pts10:
- # c = dh.new_element(inkex.Circle, self.textel)
- # c.set('cx',str(p))
- # c.set('cy',str(bbx[1]+y+lht*0.1))
- # c.set('r',str(fchr.utfs/10))
- # c.set('style','fill:#000000;stroke:none')
- # self.textel.croot.append(c)
- # c.ctransform = self.textel.ccomposed_transform
- # for p in pts90:
- # c = dh.new_element(inkex.Circle, self.textel)
- # c.set('cx',str(p))
- # c.set('cy',str(bbx[1]+y+lht*0.9))
- # c.set('r',str(fchr.utfs/10))
- # c.set('style','fill:#000000;stroke:none')
- # self.textel.croot.append(c)
- # c.ctransform = self.textel.ccomposed_transform
-
- # For every interval, find what text we can insert and where
- # we should make breaks
- breaks = []
- for xlim in xlims:
- breakaft = None
- hardbreak = False
- strt = 0 if len(breaks) == 0 else breaks[-1] + 1
- csleft = lncs[i][strt:]
- for j, c in enumerate(csleft):
- if j == 0:
- fcrun = c
- if not isflowroot and c.c == "\n":
- breakaft = j
- hardbreak = True
- break
- if c.pts_ut[3][0] - fcrun.pts_ut[0][0] > xlim[1]:
- spcs = [cv for cv in csleft[:j] if cv.c in breakcs]
- # inkex.utils.debug('Break on '+str((c.c,j)))
- if c.c == " ":
- breakaft = j
- # inkex.utils.debug('Break overflow space')
- elif len(spcs) > 0:
- breakaft = [
- k
- for k, cv in enumerate(csleft)
- if spcs[-1] == cv
- ][0]
- # inkex.utils.debug('Break chunk')
- elif (
- xlim[1] > 4 * (c.line.effabsp + c.line.effbbsp)
- and j > 0
- ):
- # When the flowregion width is > 4*line height,
- # allow intraword break
- # https://gitlab.com/inkscape/inkscape/-/blob/master/src/libnrtype/Layout-TNG-Compute.cpp#L1989
- breakaft = j - 1
- # inkex.utils.debug('Break intraword')
- else:
- # Break whole line and hope that the next
- # line is wider
- breakaft = -1
- # inkex.utils.debug('Break whole line')
- break
- if breakaft is not None:
- c.line.broken = True
- breaks.append(breakaft + strt)
- if hardbreak:
- break
- else:
- break
- if len(xlims) == 0:
- breaks = [-1]
-
- # Use break points to split the line, pushing text after the
- # last break to the next line
- if not breaks:
- splitcs = [lncs[i]]
- else:
- splitcs = []
- prev_index = -1
- for index in breaks:
- splitcs.append(lncs[i][prev_index + 1 : index + 1])
- prev_index = index
- splitcs.append(lncs[i][prev_index + 1 :])
- nextcs = None
- if len(splitcs) > 1:
- nextcs = splitcs.pop(-1) # push to next line
- mycs = [item for sublist in splitcs[:-1] for item in sublist]
-
- allcs = [c for s in splitcs for c in s]
- maxabsp = (
- max([c.lhs[0] for c in allcs])
- if len(allcs) > 0
- else fchr.line.effabsp
- )
- maxbbsp = (
- max([c.lhs[1] for c in allcs])
- if len(allcs) > 0
- else fchr.line.effbbsp
- )
- if not lht == maxabsp + maxbbsp:
- lht = maxabsp + maxbbsp
- getxintervals = True
-
- if nextcs is not None:
- lncs = lncs[0:i] + [mycs, nextcs] + lncs[i + 1 :]
- y += maxabsp
- for j, chrs in enumerate(splitcs):
- line = fchr.line
- cln = TLine(
- self,
- [0],
- [0],
- self.textel,
- self.textel,
- False,
- [],
- line.anchor,
- line.transform,
- None,
- line.style,
- False,
- False,
- )
- cln.broken = line.broken
- cln.effabsp = maxabsp
- cln.effbbsp = maxbbsp
- blns.append(cln)
- for c in chrs:
- lchr = TChar(
- c.c,
- c.tfs,
- c.utfs,
- c.prop,
- c._sty,
- c.tsty,
- c.loc,
- cln, c._dx, c._dy
- )
-
- if len(chrs) > 0:
- anfr = cln.anchfrac
- x = (
- xlims[j][0] * (1 - anfr)
- + (xlims[j][0] + xlims[j][1]) * anfr
- )
- if i == 0 and isinlinesz and iszy is not None:
- y += ipx(iszy) - cln.effabsp
- if isinlinesz and iszx is not None:
- x += ipx(iszx)
- cln.x = [x]
- cln.y = [bbx[1] + y]
- y += maxbbsp
-
- if y - 0.1 * (maxabsp + maxbbsp) > bbx[3] and not isinlinesz:
- # When we reached the end of the flow region, add the remaining
- # characters to a line that is given a NaN y position
- cln.chrs = [c for lnc in lncs[i:] for c in lnc]
- for j, c in enumerate(cln.chrs):
- c.line = cln
- c.lnindex = j
- cln.y = [float("nan")]
- break
-
- i += 1
- if i > 1000:
- # inkex.utils.debug('Infinite loop')
- break
-
- # Determine if any FlowParas follow the last character
- # (Needed for unrenderedspace determination)
- if lchr is not None:
- dds = self.textel.descendants2()
- j = dds.index(lchr.loc.elem)
- if j < len(dds) - 1:
- self.fparaafter = any(isinstance(ddv, FlowPara) for ddv in dds[j + 1 :])
- self.lns = blns
-
-
-TYP_TEXT = 1
-TYP_TAIL = 0
-
-NORMAL = 0
-PRECEDEDSPRL = 1
-TLVLSPRL = 2
-
-class TextTree:
- """
- Descendant tree class for text, with a generator for iterating through blocks of
- text. Generator returns the current descendant index, typ (TYP_TAIL or TYP_TEXT),
- descendant element, element from which it gets its style, and text string.
- When starting at subel, only gets the tree subset corresponding to that element.
- Note: If there is no text/tail, returns None for that block.
- """
-
- def __init__(self, elem):
- """Initializes the text tree of an element."""
- dds, pts = elem.descendants2(True)
- self.dds = dds
- self.ptails = pts
- self.pdict = {ddv: ddv.getparent() for ddv in dds}
-
- def dgenerator(self, subel=None):
- """Yields descendants and their text in the tree."""
- if subel is None:
- starti = 0
- stopi = len(self.dds)
- subel = self.dds[0]
- else:
- starti = self.dds.index(subel)
- stopi = [i for i, ptail in enumerate(self.ptails) if subel in ptail][0]
-
- for ddi, typ in TextTree.ttgenerator(len(self.dds), starti, stopi):
- srcs = self.ptails[ddi] if typ == TYP_TAIL else [self.dds[ddi]]
- for src in srcs:
- if (
- typ == TYP_TAIL and src == subel
- ): # finish at my own tail (do not yield it)
- return
- txt = src.tail if typ == TYP_TAIL else src.text
- sel = (
- self.pdict[src] if typ == TYP_TAIL else src
- ) # tails get style from parent
- yield ddi, typ, src, sel, txt
-
- @staticmethod
- def ttgenerator(numd, starti=0, stopi=None):
- """
- A generator for crawling through a general text descendant tree
- Returns the current descendant index and typ (TYP_TAIL for tail,
- TYP_TEXT for text)
- """
- ddi = starti
- if stopi is None:
- stopi = numd
- typ = TYP_TAIL
- while True:
- if typ == TYP_TEXT:
- ddi += 1
- typ = TYP_TAIL
- else:
- typ = TYP_TEXT
- if ddi == stopi and typ == TYP_TEXT:
- return
- yield ddi, typ
-
-
-def get_anchorfrac(anch):
- """Gets the anchor fraction based on the text anchor."""
- anch_frac = {"start": 0, "middle": 0.5, "end": 1}
- return anch_frac.get(anch, 0)
-
-
-class TLine:
- """
- A single line, which represents a list of characters. Typically a top-level
- Tspan or TextElement. This is further subdivided into a list of chunks.
- """
-
- __slots__ = (
- "_xv",
- "_yv",
- "sprl",
- "sprlabove",
- "anchor",
- "anchfrac",
- "chrs",
- "chks",
- "transform",
- "xsrc",
- "ysrc",
- "tlvlno",
- "style",
- "continuex",
- "continuey",
- "ptxt",
- "splits",
- "sws",
- "effabsp",
- "effbbsp",
- "broken",
- )
-
- def __init__(
- self,
- ptxt,
- x,
- y,
- xsrc,
- ysrc,
- sprl,
- sprlabove,
- anch,
- xform,
- tlvlno,
- sty,
- continuex,
- continuey,
- ):
- """Initializes TLine with given parameters."""
- self._xv = x
- self._yv = y
- self.sprl = sprl
- # is this line truly a sodipodi:role line
- self.sprlabove = sprlabove
- # nominal value of spr (sprl may actually be disabled)
- self.anchor = anch
- self.anchfrac = get_anchorfrac(anch)
- self.chrs = []
- self.chks = []
- if xform is None:
- self.transform = Transform([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]])
- else:
- self.transform = xform
- self.xsrc = xsrc
- # element from which we derive our x value
- self.ysrc = ysrc
- # element from which we derive our x value
- self.tlvlno = tlvlno
- # which number Tspan I am if I'm top-level (otherwise None)
- self.style = sty
- # self.elem = elem;
- self.continuex = continuex
- # when enabled, x of a line is the endpoint of the previous line
- self.continuey = continuey
- # when enabled, y of a line is the endpoint of the previous line
- self.ptxt = ptxt
- # Flow-specific
- self.broken = None # text is broken
- self.effabsp = None # above-baseline space
- self.effbbsp = None # below-baseline space
- ptxt.lns.append(self)
-
- def copy(self, memo):
- """Creates a copy of the TLine instance."""
- ret = TLine.__new__(TLine)
- memo[self] = ret
-
- ret._xv = self._xv[:]
- ret._yv = self._yv[:]
- ret.sprl = self.sprl
- ret.sprlabove = [memo[sa] for sa in self.sprlabove]
- ret.anchor = self.anchor
- ret.anchfrac = self.anchfrac
- ret.chrs = [c.copy(memo) for c in self.chrs]
- ret.chks = [chk.copy(memo) for chk in self.chks]
- ret.transform = self.transform
- ret.xsrc = memo[self.xsrc]
- ret.ysrc = memo[self.ysrc]
- ret.tlvlno = self.tlvlno
- ret.style = self.style
- ret.continuex = self.continuex
- ret.continuey = self.continuey
- ret.ptxt = self.ptxt
- ret.broken = self.broken
- ret.effabsp = self.effabsp # above-baseline space
- ret.effbbsp = self.effbbsp # below-baseline space
-
- return ret
-
- @property
- def angle(self):
- """Returns the transform angle in degrees."""
- return math.atan2(self.transform.c, self.transform.d) * 180 / math.pi
-
- def get_x(self):
- """Returns the x property value."""
- if not self.continuex:
- return self._xv
- if self.ptxt is None:
- return [0]
- i = self.ptxt.lns.index(self)
- if i > 0 and len(self.ptxt.lns[i - 1].chks) > 0:
- anfr = self.anchfrac
- xanch = (1 + anfr) * self.ptxt.lns[i - 1].chks[-1].pts_ut[3][
- 0
- ] - anfr * self.ptxt.lns[i - 1].chks[-1].pts_ut[0][0]
- return [xanch]
- return [0]
-
- def set_x(self, xvi):
- """Sets the x property value."""
- self._xv = xvi
-
- x = property(get_x, set_x)
-
- def get_y(self):
- """Returns the y property value."""
- if not self.continuey:
- return self._yv
- if self.ptxt is None:
- return [0]
- i = self.ptxt.lns.index(self)
- if i > 0 and len(self.ptxt.lns[i - 1].chks) > 0:
- anfr = self.anchfrac
- yanch = (1 + anfr) * self.ptxt.lns[i - 1].chks[-1].pts_ut[3][
- 1
- ] - anfr * self.ptxt.lns[i - 1].chks[-1].pts_ut[0][1]
- return [yanch]
- return [0]
-
- def set_y(self, yvi):
- """Sets the y property value."""
- self._yv = yvi
-
- y = property(get_y, set_y)
- # anchorfrac = property(lambda self: get_anchorfrac(self.anchor))
-
- def insertc(self, c, i):
- """Inserts a character below a specified position."""
- for ch2 in self.chrs[i:]:
- ch2.lnindex += 1
- c.lnindex = i
- self.chrs = self.chrs[0:i] + [c] + self.chrs[i:]
- c.line = self
-
- def addw(self, chk):
- """Adds a complete chunk to the line."""
- self.chks.append(chk)
-
- def parse_chunks(self):
- """
- Parses the line into chunks based on characters.
- Parsing a line into chunks is the final step that should be called once
- the line parser has gotten all the characters.
- """
- chk = None
- self.chks = []
- for i in range(len(self.chrs)):
- if i == 0:
- chk = TChunk(i, self.x[0], self.y[0], self)
- # open new chunk
- elif (i < len(self.x) and self.x[i] is not None) or (i < len(self.y) and self.y[i] is not None): # None means keep the same chunk
- self.addw(chk) # close previous chunk
- chk = TChunk(i, self.x[min(i,len(self.x)-1)], self.y[min(i,len(self.y)-1)], self)
- # open new chunk
- else:
- chk.addc(i)
- # add to existing chunk
- if chk is not None:
- self.addw(chk)
-
- def dell(self):
- """Deletes the whole line."""
- self.write_xy(self.x[:1])
- for _, c in enumerate(reversed(self.chrs)):
- if c.loc.typ == TYP_TEXT:
- c.loc.elem.text = (
- c.loc.elem.text[: c.loc.ind] + c.loc.elem.text[c.loc.ind + 1 :]
- )
- else:
- c.loc.elem.tail = (
- c.loc.elem.tail[: c.loc.ind] + c.loc.elem.tail[c.loc.ind + 1 :]
- )
- self.ptxt.write_dxdy()
-
- if self in self.ptxt.lns:
- self.ptxt.lns.remove(self)
-
- def txt(self):
- """Returns the concatenated text of all characters."""
- return "".join([c.c for c in self.chrs])
-
- def change_alignment(self, newanch):
- """Changes the alignment of the line without affecting character position."""
- if newanch != self.anchor:
- sibsrc = [
- line
- for line in self.ptxt.lns
- if line.xsrc == self.xsrc or line.ysrc == self.ysrc
- ]
- for line in reversed(sibsrc):
- line.disablesodipodi()
- # Disable sprl for all lines sharing our src, including us
- # Note that it's impossible to change one line without affecting
- # the others
-
- for chk in self.chks:
- minx = min([chk.pts_ut[i][0] for i in range(4)])
- maxx = max([chk.pts_ut[i][0] for i in range(4)])
-
- if chk.unrenderedspace and self.chrs[-1] in chk.chrs:
- maxx -= chk.chrs[-1].cwd
-
- anfr = get_anchorfrac(newanch)
- newx = (1 - anfr) * minx + anfr * maxx
-
- if len(chk.chrs) > 0:
- newxv = self.x
- newxv[chk.chrs[0].lnindex] = newx
- self.write_xy(newxv)
- alignd = {"start": "start", "middle": "center", "end": "end"}
- chk.chrs[0].loc.elem.cstyle.__setitem__(
- "text-anchor", newanch, "text-align", alignd[newanch]
- )
- self.continuex = False
-
- self.anchor = newanch
- self.anchfrac = anfr
- chk.charpos = None # invalidate chunk positions
-
- def disablesodipodi(self, force=False):
- """Disables sodipodi:role=line."""
- if len(self.sprlabove) > 0 or force:
- if len(self.chrs) > 0:
- newsrc = self.chrs[0].loc.elem # disabling snaps src to first char
- if self.chrs[0].loc.typ == TYP_TAIL:
- newsrc = self.chrs[0].loc.elem.getparent()
- newsrc.set("sodipodi:role", None)
-
- self.sprlabove = []
- self.xsrc = newsrc
- self.ysrc = newsrc
- xyset(self.xsrc, "x", self.x) # fuse position to new source
- xyset(self.ysrc, "y", self.y)
- self.sprl = False
-
- def write_xy(self, newx=None, newy=None):
- """
- Changes the position of the line in the document.
- Update the line's position in the document, accounting for inheritance
- Never change x/y directly, always call this function
- """
- if newx is not None:
- sibsrc = [line for line in self.ptxt.lns if line.xsrc == self.xsrc]
- if len(sibsrc) > 1:
- for line in reversed(sibsrc):
- line.disablesodipodi() # Disable sprl when lines share an xsrc
-
- while len(newx) > 1 and newx[-1] is None:
- newx.pop()
- oldx = self._xv if not self.continuex else self.x
- self._xv = newx
- xyset(self.xsrc, "x", newx)
-
- if (
- len(oldx) > 1 and len(newx) == 1 and len(self.sprlabove) > 0
- ): # would re-enable sprl
- self.disablesodipodi()
-
- if newy is not None:
- sibsrc = [line for line in self.ptxt.lns if line.ysrc == self.ysrc]
- if len(sibsrc) > 1:
- for line in reversed(sibsrc):
- line.disablesodipodi() # Disable sprl when lines share a ysrc
-
- while len(newy) > 1 and newy[-1] is None:
- newx.pop()
- oldy = self._yv if not self.continuey else self.y
- self._yv = newy
- xyset(self.ysrc, "y", newy)
-
- if (
- len(oldy) > 1 and len(newy) == 1 and len(self.sprlabove) > 0
- ): # would re-enable sprl
- self.disablesodipodi()
-
- def split(self,i):
- if i>=len(self.chrs):
- return
- self.ptxt.split_off_characters(self.chrs[i:])
-
-class TChunk:
- """Represents a chunk, a group of characters with the same assigned anchor."""
-
- __slots__ = (
- "chrs",
- "windex",
- "iis",
- "ncs",
- "_xv",
- "_yv",
- # "scf",
- "line",
- "transform",
- "nextw",
- "prevw",
- "prevsametspan",
- "_pts_ut",
- "_pts_t",
- "_bb",
- "_dxeff",
- "_charpos",
- "_cpts_ut",
- "_cpts_t",
- "txt",
- "lsp",
- "dxeff",
- "cwd",
- "dy",
- "caph",
- "bshft",
- "mw",
- "merges",
- "mergetypes",
- "merged",
- "wtypes",
- "bb_big",
- )
-
- def __init__(self, i, x, y, line):
- """Initializes TChunk with given parameters."""
- c = line.chrs[i]
- self.chrs = [c]
- c.windex = 0
- self.iis = [i]
- # character index in chunk
- self.ncs = len(self.iis)
- self._xv = x
- self._yv = y
- # all letters have the same scale
- self.line = line
- self.transform = line.transform
- c.chk = self
- self.nextw = self.prevw = self.prevsametspan = None
- self._pts_ut = self._pts_t = self._bb = None
- self._dxeff = self._charpos = None
- self._cpts_ut = None
- self._cpts_t = None
-
- # Character attribute lists
- self.txt = c.c
- self.lsp = [c.lsp]
- self.dxeff = [c.dx, c.lsp]
- # Effective dx (with letter-spacing). Note that letter-spacing adds space
- # after the char, so dxeff ends up being longer than the number of chars by 1
- self.cwd = [c.cwd]
- self.dy = [c.dy]
- self.caph = [c.caph]
- self.bshft = [c.bshft]
-
- def copy(self, memo):
- """Creates a copy of the TChunk instance."""
- ret = TChunk.__new__(TChunk)
- memo[self] = ret
-
- # pylint:disable=protected-access
- ret.ncs = self.ncs
- ret._xv = self._xv
- ret._yv = self._yv
- ret.transform = self.transform
- ret.nextw = self.nextw
- ret.prevw = self.prevw
- ret.prevsametspan = self.prevsametspan
- ret._pts_ut = self._pts_ut
- ret._pts_t = self._pts_t
- ret._bb = self._bb
- ret._dxeff = self._dxeff
- ret._charpos = self._charpos
- ret._cpts_ut = self._cpts_ut
- ret._cpts_t = self._cpts_t
- ret.txt = self.txt
- ret.lsp = self.lsp[:]
- ret.dxeff = self.dxeff[:]
- ret.cwd = self.cwd[:]
- ret.dy = self.dy[:]
- ret.caph = self.caph[:]
- ret.bshft = self.bshft[:]
- # pylint:enable=protected-access
-
- ret.chrs = list(
- map(memo.get, self.chrs, self.chrs)
- ) # faster than [memo.get(c) for c in self.chrs]
- for ret_c in ret.chrs:
- ret_c.chk = ret
- ret.iis = self.iis[:]
- ret.line = memo[self.line]
- return ret
-
- def addc(self, i):
- """Adds an existing character to a chunk based on line index."""
- c = self.line.chrs[i]
- c.chk = None # avoid problems in character properties
- self.chrs.append(c)
- self.cchange()
- self.iis.append(i)
- self.ncs = len(self.iis)
- c.windex = self.ncs - 1
-
- self.txt += c.c
- self.lsp.append(c.lsp)
- self.dxeff[-1] += c.dx
- self.dxeff.append(c.lsp)
- self.cwd.append(c.cwd)
- self.dy.append(c.dy)
- self.caph.append(c.caph)
- self.bshft.append(c.bshft)
- c.chk = self
-
- def removec(self, c):
- """Removes a character from a chunk based on chunk index."""
- i = c.windex
- for ch2 in self.chrs[i + 1 :]:
- ch2.windex -= 1
- c.windex = None
- c.chk = None
- self.chrs = del2(self.chrs, i)
- self.iis.pop(i)
- self.ncs = len(self.iis)
- self.cchange()
-
- self.txt = del2(self.txt, i)
- self.lsp.pop(i)
- self.dxeff.pop(i)
- self.dxeff[i] = (self.chrs[i].dx if i < self.ncs else 0) + (
- self.chrs[i - 1].lsp if i > 0 else 0
- )
- self.cwd.pop(i)
- self.dy.pop(i)
- self.caph.pop(i)
- self.bshft.pop(i)
-
- if len(self.chrs) == 0: # chunk now empty
- self.del_chk()
-
- def cchange(self):
- """
- Callback for character addition/deletion. Used to invalidate cached
- properties
- """
- self.charpos = None
-
- if self.line.ptxt.writtendx or self.line.ptxt.writtendy:
- self.line.ptxt.dchange = True
-
- @property
- def x(self):
- """Get effective x value"""
- if self.line and self.ncs > 0:
- lnx = self.line._xv if not self.line.continuex else self.line.x
- # checking for continuex early eliminates most unnecessary calls
- i = self.iis[0] # first char
- return lnx[i] if i < len(lnx) else lnx[-1]
- return 0
-
- @property
- def y(self):
- """Get effective y value"""
- if self.line and self.ncs > 0:
- lny = self.line._yv if not self.line.continuey else self.line.y
- # checking for continuex early eliminates most unnecessary calls
- i = self.iis[0] # first char
- return lny[i] if i < len(lny) else lny[-1]
- return 0
-
- @property
- def scf(self):
- """Get scale of first character"""
- return self.chrs[0].scf if self.ncs>0 else None
-
- def del_chk(self,updatedelta=True):
- """Deletes the chunk from the line."""
- for c in reversed(self.chrs):
- c.delc(updatedelta=updatedelta)
- if self in self.line.chks:
- self.line.chks.remove(self)
-
- def appendc(self, ncv, ncprop, ndx, ndy, totail=None):
- """Generates a new character and adds it to the end of the chunk."""
- # Add to document
- lchr = self.chrs[-1]
- # last character
- if totail is None:
- myi = lchr.loc.ind + 1
- # insert after last character
- if lchr.loc.typ == TYP_TEXT:
- lchr.loc.elem.text = (
- lchr.loc.elem.text[0:myi] + ncv + lchr.loc.elem.text[myi:]
- )
- else:
- lchr.loc.elem.tail = (
- lchr.loc.elem.tail[0:myi] + ncv + lchr.loc.elem.tail[myi:]
- )
- else:
- if totail.tail is None:
- totail.tail = ncv
- else:
- totail.tail = ncv + totail.tail
-
- # Make new character as a copy of the last one of the current chunk
- c = copy(lchr)
- c.chk = None # so we don't have problems during style setting
- c.c = ncv
- c.prop = ncprop
- c.cwd = ncprop.charw * c.utfs
- c.dx = ndx
- c.dy = ndy
-
- if totail is None:
- c.loc = CLoc(c.loc.elem, c.loc.typ, c.loc.ind + 1) # updated location
- else:
- c.loc = CLoc(totail, TYP_TAIL, 0)
- c.sty = c.loc.sel.cspecified_style
-
- # Add to line
- myi = lchr.lnindex + 1 # insert after last character
- if len(self.line.x) > 0:
- newx = self.line.x[0:myi] + [None] + self.line.x[myi:]
- newx = newx[0 : len(self.line.chrs) + 1]
- self.line.write_xy(newx)
-
- self.line.insertc(c, myi)
- for i in range(myi + 1, len(self.line.chrs)):
- # need to increment index of subsequent objects with the same parent
- cha = self.line.chrs[i]
- if cha.loc.typ == c.loc.typ and cha.loc.elem == c.loc.elem:
- cha.loc.ind += 1
- if cha.chk is not None:
- cha.chk.iis[cha.windex] += 1
- # Add to chunk, recalculate properties
- self.addc(myi)
-
- # Adding a character causes the chunk to move if it's center- or right-justified
- # Need to fix this by adjusting position
- deltax = -self.line.anchfrac * self.line.chrs[myi].cwd
- if deltax != 0:
- newx = self.line.x
- newx[self.chrs[0].lnindex] -= deltax
- self.line.write_xy(newx)
-
- self.line.ptxt.write_dxdy()
-
- def append_chk(self, nchk, stype, maxspaces=None):
- """
- Adds a new chunk (possibly from another line) into the current one.
- Equivalent to typing it in
- """
- if len(nchk.chrs) > 0:
- # Calculate the number of spaces we need to keep the position constant
- # (still need to adjust for anchors)
- _, br1, _, bl2 = self.get_ut_pts(nchk)
- lchr = self.chrs[-1]
- # last character
- numsp = (bl2[0] - br1[0]) / (lchr.spw)
- numsp = max(0, round(numsp))
- if maxspaces is not None:
- numsp = min(numsp, maxspaces)
-
- for _ in range(numsp):
- self.appendc(
- " ", lchr.line.ptxt.ctable.get_prop(" ", lchr.tsty), -lchr.lsp, 0
- )
-
- fchr = nchk.chrs[0]
- prevc = self.chrs[-1]
- for c in nchk.chrs:
- mydx = (c != fchr) * c.dx - prevc.lsp * (c == fchr)
- # use dx to remove lsp from the previous c
- c.delc()
-
- ntype = copy(stype)
- otype = c.sty.get("baseline-shift")
- if otype in ["super", "sub"] and stype == "normal":
- ntype = otype
-
- # Nested tspans should be appended to the end of tails
- totail = None
- if self.chrs[-1].loc.sel != self.chrs[0].loc.sel:
- cel = self.chrs[-1].loc.sel
- while (
- cel is not None
- and cel.getparent() != self.chrs[0].loc.sel
- and cel.getparent() != self.line.ptxt.textel
- ):
- cel = cel.getparent()
- totail = cel
-
- self.appendc(c.c, c.prop, mydx, c.dy, totail=totail)
- newc = self.chrs[-1]
-
- # Make sure items on tails have right font size
- if totail is not None:
- fsz, scf, utfs = composed_width(totail.getparent(), "font-size")
- newc.utfs = utfs
- newc.tfs = fsz
- newc.cwd = newc.prop.charw * utfs
- newc.caph = newc.prop.caph * utfs
- newc.spw = newc.prop.spacew * utfs
- newc.chk.cwd[newc.windex] = newc.cwd
- newc.chk.caph[newc.windex] = newc.caph
-
- newc.parsed_pts_ut = [
- vmultinv(self.transform.matrix, p[0], p[1]) for p in c.parsed_pts_t
- ]
- newc.parsed_pts_t = c.parsed_pts_t
-
- # Update the style
- newsty = None
- if c.sty != newc.sty or ntype in ["super", "sub"] or abs(c.scf - newc.scf)>1e-4:
- newsty = c.sty
- if ntype in ["super", "sub"]:
- # newsty = c.sty
- # Nativize super/subscripts
- newsty["baseline-shift"] = (
- "super" if ntype == "super" else "sub"
- )
- newsty["font-size"] = "65%"
- # Leave size unchanged
- # nsz = round((c.spw*c.scf)/(newc.spw*newc.scf)*100)
- # newsty['font-size'] = str(nsz)+'%';
-
- # Leave baseline unchanged (works)
- # shft = round(-(bl2.y-br1.y)/self.tfs*100*self.scf);
- # newsty['baseline-shift']= str(shft)+'%';
- elif abs(c.scf - newc.scf)>1e-4:
- # Prevent accidental font size changes when differently
- # transformed
- nsz = round((c.spw * c.scf) / (newc.spw * newc.scf) * 100)
- # newsty['font-size'] = str(nsz)+'%';
- newsty["font-size"] = (
- f"{format(nsz, '.2f').rstrip('0').rstrip('.')}%"
- )
-
- if newsty is not None:
- newc.add_style(newsty, newfs=True)
- prevc = newc
-
- def get_ut_pts(self, chk2, current_pts=False):
- """Gets the coordinates of another chunk in my coordinate system."""
- if current_pts:
- c1uts = [c.pts_ut for c in self.chrs]
- c2uts = [c.pts_ut for c in chk2.chrs]
- c2ts = [c.pts_t for c in chk2.chrs]
- else:
- c1uts = [c.parsed_pts_ut for c in self.chrs]
- c2uts = [c.parsed_pts_ut for c in chk2.chrs]
- c2ts = [c.parsed_pts_t for c in chk2.chrs]
-
- minv = float("inf")
- chri = None
-
- for i in range(len(chk2.chrs)):
- if c2uts[i] is not None:
- if c2uts[i][0][0] < minv:
- minv = c2uts[i][0][0]
- chri = i
-
- bl2 = vmultinv(self.transform.matrix, c2ts[chri][0][0], c2ts[chri][0][1])
- tl2 = vmultinv(self.transform.matrix, c2ts[chri][1][0], c2ts[chri][1][1])
-
- maxv = float("-inf")
- chri = None
- for i in range(len(self.chrs)):
- if c1uts[i] is not None:
- if c1uts[i][3][0] > maxv:
- maxv = c1uts[i][3][0]
- chri = i
-
- tr1 = c1uts[chri][2]
- br1 = c1uts[chri][3]
- return tr1, br1, tl2, bl2
-
- def fix_merged_position(self):
- """
- Adjusts the position of merged text to account for small changes in
- chunk position that occur. This depends on alignment, so it is
- generally done after the final justification is set.
- """
- gcs = [c for c in self.chrs if c.c != " "]
- if len(gcs) > 0:
- omaxx = max([c.parsed_pts_ut[3][0] for c in gcs])
- ominx = min([c.parsed_pts_ut[0][0] for c in gcs])
- newptsut = [c.pts_ut for c in gcs]
- nmaxx = max([p[3][0] for p in newptsut])
- nminx = min([p[0][0] for p in newptsut])
-
- anfr = self.line.anchfrac
- deltaanch = (nminx * (1 - anfr) + nmaxx * anfr) - (
- ominx * (1 - anfr) + omaxx * anfr
- )
- # how much the final anchor moved
- if deltaanch != 0:
- newx = self.line.x
- newx[self.chrs[0].lnindex] -= deltaanch
- self.line.write_xy(newx)
- self.charpos = None
- self.line.ptxt.write_dxdy()
-
- def get_fs(self):
- """Returns the font size of the chunk."""
- return maxnone([c.tfs for c in self.chrs])
-
- tfs = property(get_fs)
-
- @property
- def spw(self):
- """Returns the space width of the chunk."""
- return maxnone([c.spw for c in self.chrs])
-
- def get_mch(self):
- """Returns the maximum character height of the chunk."""
- return maxnone(self.caph)
-
- mch = property(get_mch)
-
- @property
- def angle(self):
- """Returns the angle of the chunk."""
- return self.line.angle
-
- @property
- def unrenderedspace(self):
- """
- Returns True if the last character of a multichar line is a space
- and is not rendered.
- """
- lastspc = (
- self.ncs > 1
- and self.chrs[-1] == self.line.chrs[-1]
- and self.chrs[-1].c in [" ", "\u00a0"]
- )
- if not self.line.ptxt.isflow:
- return lastspc
- if lastspc:
- # For flows, is rendered when not the last line and line isn't broken
- lastln = (
- self.line.ptxt.lns[-1] == self.line and not self.line.ptxt.fparaafter
- )
- return lastln or self.line.broken
- return False
-
- @property
- def charpos(self):
- """
- Returns the character positions relative to the left side of the chunk.
- """
- if self._charpos is None:
- dadv = [0] * self.ncs
- if DIFF_ADVANCES:
- for i in range(1, self.ncs):
- dadv[i] = self.chrs[i].dadvs(self.chrs[i - 1].c, self.chrs[i].c)*(self.dxeff[i]==0)
- # default to 0 for chars of different style
- # any dx value overrides differential advances
-
- chks = [self.cwd[i] + self.dxeff[i] + dadv[i] for i in range(self.ncs)]
- cstop = list(itertools.accumulate(chks))
- # cumulative width up to and including the ith char
- cstrt = [cstop[i] - self.cwd[i] for i in range(self.ncs)]
-
- cstrt = np.array(cstrt, dtype=float)
- cstop = np.array(cstop, dtype=float)
-
- chkw = cstop[-1]
- offx = -self.line.anchfrac * (
- chkw - self.unrenderedspace * self.chrs[-1].cwd
- )
- # offset of the left side of the chunk from the anchor
- chkx = self.x
- chky = self.y
-
- lftx = (chkx + cstrt + offx)[:, np.newaxis]
- rgtx = (chkx + cstop + offx)[:, np.newaxis]
-
- adyl = list(itertools.accumulate(self.dy))
- btmy = np.array(
- [chky + dy - bs for dy, bs in zip(adyl, self.bshft)], dtype=float
- )[:, np.newaxis]
- topy = np.array(
- [
- chky + dy - bs - caph
- for dy, caph, bs in zip(adyl, self.caph, self.bshft)
- ],
- dtype=float,
- )[:, np.newaxis]
-
- lx2 = float(min(lftx - self.dxeff[0]).squeeze())
- rx2 = float(lx2 + chkw)
- by2 = float(max(btmy).squeeze())
- ty2 = float(min(topy).squeeze())
-
- self._charpos = (lftx, rgtx, btmy, topy, lx2, rx2, by2, ty2)
-
- return self._charpos
-
- @charpos.setter
- def charpos(self, svi):
- """Sets the character positions and invalidates dependent properties."""
- if svi is None: # invalidate self and dependees
- self._charpos = None
- self.pts_ut = None
- self.cpts_ut = None
- self.cpts_t = None
-
- @property
- def pts_ut(self):
- """Returns the untransformed bounding box of the chunk."""
- if self._pts_ut is None:
- (_, _, _, _, lx2, rx2, by2, ty2) = self.charpos
- self._pts_ut = [
- (lx2, by2),
- (lx2, ty2),
- (rx2, ty2),
- (rx2, by2),
- ]
- return self._pts_ut
-
- @pts_ut.setter
- def pts_ut(self, _):
- """Sets the untransformed bounding box and invalidates dependent properties."""
- if _ is None and self._pts_ut is not None: # invalidate self and dependees
- self._pts_ut = None
- self.pts_t = None
- # self.cpts_ut = None
-
- @property
- def pts_t(self):
- """Returns the transformed bounding box of the chunk."""
- if self._pts_t is None:
- self._pts_t = [
- vmult(self.transform.matrix, p[0], p[1]) for p in self.pts_ut
- ]
- return self._pts_t
-
- @pts_t.setter
- def pts_t(self, _):
- """Sets the transformed bounding box and invalidates dependent properties."""
- if _ is None and self._pts_t is not None: # invalidate self and dependees
- self._pts_t = None
- self.bbx = None
- # self.cpts_t = None
-
- @property
- def bbx(self):
- """Returns the bounding box of the chunk."""
- if self._bb is None:
- ptt = self.pts_t
- min_x = min(ptt[0][0], ptt[1][0], ptt[2][0], ptt[3][0])
- min_y = min(ptt[0][1], ptt[1][1], ptt[2][1], ptt[3][1])
- max_x = max(ptt[0][0], ptt[1][0], ptt[2][0], ptt[3][0])
- max_y = max(ptt[0][1], ptt[1][1], ptt[2][1], ptt[3][1])
- self._bb = bbox([min_x, min_y, max_x - min_x, max_y - min_y])
- return self._bb
-
- @bbx.setter
- def bbx(self, bbi):
- """Sets the bounding box and invalidates dependent properties."""
- if bbi is None: # invalidate
- self._bb = None
-
- @property
- def cpts_ut(self):
- """Returns the untransformed bounding box of characters."""
- if self._cpts_ut is None:
- (lftx, rgtx, btmy, topy, _, _, _, _) = self.charpos
- n_rows = lftx.shape[0]
- self._cpts_ut = [
- tuple((coord[i][0], val[i][0]) for i in range(n_rows))
- for coord, val in zip(
- (lftx, lftx, rgtx, rgtx), (btmy, topy, topy, btmy)
- )
- ]
-
- return self._cpts_ut
-
- @cpts_ut.setter
- def cpts_ut(self, svi):
- """Sets the untransformed bounding box of characters and invalidates
- dependent properties."""
- if svi is None:
- self._cpts_ut = None
-
- @property
- def cpts_t(self):
- """Returns the transformed bounding box of characters."""
- if self._cpts_t is None:
- (lftx, rgtx, btmy, topy, _, _, _, _) = self.charpos
- nch = len(lftx)
- pts = np.array(
- [
- (coord[i][0], val[i][0], 1)
- for coord, val in zip(
- (lftx, lftx, rgtx, rgtx), (btmy, topy, topy, btmy)
- )
- for i in range(nch)
- ],
- dtype=float,
- )
- mat = np.array(self.transform.matrix + ((0, 0, 1),), dtype=float)
-
- tps = np.dot(mat, pts.T).T
- self._cpts_t = [
- tps[0:nch, 0:2],
- tps[nch : 2 * nch, 0:2],
- tps[2 * nch : 3 * nch, 0:2],
- tps[3 * nch : 4 * nch, 0:2],
- ]
-
- return self._cpts_t
-
- @cpts_t.setter
- def cpts_t(self, svi):
- """Sets the transformed bounding box of characters and invalidates
- dependent properties."""
- if svi is None:
- self._cpts_t = None
-
-
-class TChar:
- """Represents a single character and its style."""
-
- def __init__(self, c, tfs, utfs, prop, sty, tsty, loc, line, dx, dy):
- """Initializes TChar with given parameters."""
- self.c = c
- self.tfs = tfs
- # transformed font size (uu)
- self.utfs = utfs
- # untransformed font size
- self.prop = prop
- # properties of a 1 uu character
- self.cwd = prop.charw * utfs
- # ut character width
- self._sty = sty
- # actual style
- self.tsty = tsty
- # true font style
- self.fsty = font_style(sty)
- # font style
- self.loc = loc
- # true location: [parent, TYP_TEXT or TYP_TAIL, index]
- self.caph = prop.caph * utfs
- # cap height (height of flat capitals like T)
- self.spw = prop.spacew * utfs
- # space width for style
- self.line = line
- line.chrs.append(self)
- self.lnindex = len(line.chrs) - 1
- # my line
- self.chk = None
- # my chunk (to be assigned)
- self.windex = None # index in chunk
- # 'normal','super', or 'sub' (to be assigned)
- self._dx = dx
- self._dy = dy
- self._ax = None
- self._ay = None
- self.dadvs = lambda cL, cR: prop.dadvs.get((cL, cR), 0) * utfs
- self.parsed_pts_t = None
- self.parsed_pts_ut = None
- # for merging later
- self._lsp = TChar.lspfunc(self.sty)
- self._bshft = TChar.bshftfunc(self.sty, self.loc.sel)
- # letter spacing
- self.lhs = None
- # flow line-heights
-
- def copy(self, memo=None):
- """Creates a copy of the TChar instance."""
- if memo is None:
- memo = dict()
- ret = TChar.__new__(TChar)
- memo[self] = ret
- ret.__dict__.update(self.__dict__)
- ret.loc = CLoc(
- memo.get(self.loc.elem, self.loc.elem), self.loc.typ, self.loc.ind
- )
- ret.line = memo.get(self.line, self.line)
- return ret
-
- @property
- def dx(self):
- """Returns the dx property."""
- return self._dx
-
- @dx.setter
- def dx(self, dxi):
- """Sets the dx property and invalidates dependent properties."""
- if self._dx != dxi:
- self._dx = dxi
- if self.chk is not None:
- self.chk.charpos = None # invalidate
- i = self.windex
- chk = self.chk
- chk.dxeff[i] = (chk.chrs[i].dx if i < chk.ncs else 0) + (
- chk.chrs[i - 1].lsp if i > 0 else 0
- )
- self.line.ptxt.dchange = True
-
- @property
- def dy(self):
- """Returns the dy property."""
- return self._dy
-
- @dy.setter
- def dy(self, dxi):
- """Sets the dy property and invalidates dependent properties."""
- if self._dy != dxi:
- self._dy = dxi
- if self.chk is not None:
- self.chk.dy[self.windex] = dxi
- self.line.ptxt.dchange = True
-
- @property
- def ax(self):
- """Returns the ax property."""
- return self._ax
-
- @ax.setter
- def ax(self, axi):
- """Sets the ax property and invalidates dependent properties."""
- if self._ax != axi:
- self._ax = axi
- self.line.ptxt.achange = True
-
- @property
- def ay(self):
- """Returns the ay property."""
- return self._ay
-
- @ay.setter
- def ay(self, axi):
- """Sets the dy property and invalidates dependent properties."""
- if self._ay != axi:
- self._ay = axi
- self.line.ptxt.achange = True
-
- @property
- def sty(self):
- """Returns the style of the character."""
- # Character style
- return self._sty
-
- @sty.setter
- def sty(self, styi):
- """Sets the style of the character and updates dependent properties."""
- self._sty = styi
- self.lsp = TChar.lspfunc(self._sty)
- self.bshft = TChar.bshftfunc(self._sty, self.loc.sel)
-
- self.fsty = font_style(styi)
- self.tsty = true_style(styi)
-
- @staticmethod
- def lspfunc(styv):
- """Returns the letter spacing for the given style."""
- if "letter-spacing" in styv:
- lspv = styv.get("letter-spacing")
- if "em" in lspv: # em is basically the font size
- fs2 = styv.get("font-size")
- if fs2 is None:
- fs2 = "12px"
- lspv = float(lspv.strip("em")) * ipx(fs2)
- else:
- lspv = ipx(lspv) or 0
- else:
- lspv = 0
- return lspv
-
- @property
- def lsp(self):
- """Returns the letter spacing of the character."""
- return self._lsp
-
- @lsp.setter
- def lsp(self, sval):
- """Sets the letter spacing and invalidates dependent properties."""
- if sval != self._lsp:
- self._lsp = sval
- if self.chk is not None:
- self.chk.charpos = None
- i = self.windex
- chk = self.chk
- chk.lsp[i] = sval
- chk.dxeff[i + 1] = (chk.chrs[i + 1].dx if i < chk.ncs - 1 else 0) + (
- chk.chrs[i].lsp if i < chk.ncs else 0
- )
-
- @staticmethod
- def bshftfunc(styv, strtel):
- """Calculates the baseline shift for the given style."""
- if "baseline-shift" in styv:
- # Find all ancestors with baseline-shift
- bsancs = []
- cel = strtel
- while cel is not None and "baseline-shift" in cel.cspecified_style:
- bsancs.append(cel)
- cel = cel.getparent()
-
- # Starting from the most distant ancestor, calculate relative
- # baseline shifts (i.e., how much each element is raised/lowered
- # from the last block of text)
- relbs = []
- for att in reversed(bsancs):
- if "baseline-shift" in att.cstyle:
- relbs.append(TChar.get_baseline(att.cstyle, att.getparent()))
- else:
- # When an element has a baseline-shift from inheritance
- # but no baseline-shift is specified, implicitly gets the
- # sum of the previous values
- relbs.append(sum(relbs))
- bshft = sum(relbs)
- else:
- bshft = 0
- return bshft
-
- # Character baseline shift
- @property
- def bshft(self):
- """Returns the baseline shift of the character."""
- return self._bshft
-
- @bshft.setter
- def bshft(self, sval):
- """Sets the baseline shift and invalidates dependent properties."""
- if sval != self._bshft:
- self._bshft = sval
- if self.chk is not None:
- self.chk.charpos = None
- self.chk.bshft[self.windex] = sval
-
- @staticmethod
- def get_baseline(styin, fsel):
- """Gets the baseline shift value based on style and font size."""
- bshft = styin.get("baseline-shift", "0")
- if bshft == "super":
- bshft = "40%"
- elif bshft == "sub":
- bshft = "-20%"
- if "%" in bshft: # relative to parent
- fs2, sf2, _ = composed_width(fsel, "font-size")
- bshft = fs2 / sf2 * float(bshft.strip("%")) / 100
- else:
- bshft = ipx(bshft) or 0
- return bshft
-
- @property
- def scf(self):
- """Returns the scale from the utfs to tfs."""
- return self.tfs / self.utfs
-
- def __str__(self):
- return str((self.c,self.loc.elem.get_id(), self.loc.typ, self.loc.ind))
-
- def delc(self,updatedelta=True):
- """Deletes the character from the document and its chunk/line."""
- # Deleting a character causes the chunk to move if it's center- or
- # right-justified. Adjust position to fix
- lncs = self.line.chrs
- myi = self.lnindex # index in line
-
- # Differential kerning affect on character width
- dko1 = dko2 = dkn = 0
- if myi < len(lncs) - 1:
- dko2 = self.dadvs(lncs[myi].c, lncs[myi + 1].c) # old from right
- if myi > 0:
- dkn = self.dadvs(lncs[myi - 1].c, lncs[myi + 1].c) # new
- if myi > 0:
- dko1 = self.dadvs(lncs[myi - 1].c, lncs[myi].c) # old from left
- tdk = dko1 + dko2 - dkn
-
- cwo = self.cwd + tdk + self._dx + self.lsp * (self.windex != 0)
- if self.chk.unrenderedspace and self.chk.chrs[-1] == self:
- if len(self.chk.chrs) > 1 and self.chk.chrs[-2].c != " ":
- cwo = tdk
- # Deletion will not affect position
- # Weirdly dkerning from unrendered spaces still counts
-
- if self == self.chk.chrs[0]: # from beginning of line
- deltax = (self.line.anchfrac - 1) * cwo
- else: # assume end of line
- deltax = self.line.anchfrac * cwo
-
- lnx = self.line.x
- changedx = False
- if deltax != 0:
- chkidx = self.line.chks.index(self.chk)
- if chkidx < len(lnx) and lnx[chkidx] is not None:
- lnx[chkidx] -= deltax
- changedx = True
-
- # Delete from document
- if self.loc.typ == TYP_TEXT:
- self.loc.elem.text = (
- self.loc.elem.text[: self.loc.ind]
- + self.loc.elem.text[self.loc.ind + 1 :]
- )
- else:
- self.loc.elem.tail = (
- self.loc.elem.tail[: self.loc.ind]
- + self.loc.elem.tail[self.loc.ind + 1 :]
- )
-
- if len(lnx) > 1 and myi < len(lnx):
- if myi < len(lnx) - 1 and lnx[myi] is not None and lnx[myi + 1] is None:
- newx = (
- lnx[: myi + 1] + lnx[myi + 2 :]
- ) # next x is None, delete that instead
- elif myi == len(lnx) - 1 and len(lncs) > len(lnx):
- newx = lnx # last x, characters still follow
- else:
- newx = lnx[:myi] + lnx[myi + 1 :]
-
- lnx = newx[: len(lncs) - 1]
- # we haven't deleted the char yet, so make it len-1 long
- changedx = True
- if changedx:
- self.line.write_xy(lnx)
-
- # Delete from line
- _ = [
- setattr(ca.loc, "ind", ca.loc.ind - 1)
- for ca in lncs[myi + 1 :]
- if ca.loc.typ == self.loc.typ and ca.loc.elem == self.loc.elem
- ]
- _ = [
- ca.chk.iis.__setitem__(ca.windex, ca.chk.iis[ca.windex] - 1)
- for ca in lncs[myi + 1 :]
- if ca.chk is not None
- ]
- _ = [setattr(c, "lnindex", c.lnindex - 1) for c in self.line.chrs[myi + 1 :]]
-
- lncs[myi].lnindex = None
- self.line.chrs = lncs[:myi] + lncs[myi + 1 :]
-
- if len(self.line.chrs) == 0: # line now empty, can delete
- self.line.dell()
-
- # Remove from chunk
- self.chk.removec(self)
-
- # Update the dx/dy value in the ParsedText
- if updatedelta:
- self.line.ptxt.write_dxdy()
-
- def add_style(self, sty, setdefault=True, newfs=False):
- """Adds a style to the character by wrapping it in a new Tspan."""
- span = Tspan if self.line.ptxt.textel.tag == TEtag else inkex.FlowSpan
- t = span()
- t.text = self.c
-
- prt = self.loc.elem
- if self.loc.typ == TYP_TEXT:
- tbefore = prt.text[0 : self.loc.ind]
- tafter = prt.text[self.loc.ind + 1 :]
- prt.text = tbefore
- prt.insert(0, t)
- t.tail = tafter
- else:
- tbefore = prt.tail[0 : self.loc.ind]
- tafter = prt.tail[self.loc.ind + 1 :]
- prt.tail = tbefore
- grp = prt.getparent()
- # parent is a Tspan, so insert it into the grandparent
- grp.insert(grp.index(prt) + 1, t)
- # after the parent
-
- t.tail = tafter
-
- self.line.ptxt.tree = None # invalidate
-
- myi = self.lnindex
- for i in range(
- myi + 1, len(self.line.chrs)
- ): # for characters after, update location
- cha = self.line.chrs[i]
- if cha.loc.elem == self.loc.elem and cha.loc.typ == self.loc.typ:
- cha.loc = CLoc(t, TYP_TAIL, i - myi - 1)
- self.loc = CLoc(t, TYP_TEXT, 0) # update my own location
-
- # When the specified style has something new span doesn't, are inheriting and
- # need to explicitly assign the default value
- styset = Style(sty)
- newspfd = t.cspecified_style
- for att in newspfd:
- if att not in styset and setdefault:
- styset[att] = default_style_atts.get(att)
-
- t.cstyle = styset
- self.sty = styset
- if newfs:
- fsz, scf, _ = composed_width(self.loc.sel, "font-size")
- self.utfs = fsz / scf
- self.tfs = fsz
- self.cwd = self.prop.charw * self.utfs
- self.chk.cwd[self.windex] = self.cwd
- self.caph = self.prop.caph * self.utfs
- self.chk.caph[self.windex] = self.caph
- self.chk.charpos = None
-
- @property
- def pts_ut(self):
- """Returns the untransformed bounding box of the character."""
- myi = self.windex
- cput = self.chk.cpts_ut
- ret_pts_ut = [
- (cput[0][myi][0], cput[0][myi][1]),
- (cput[1][myi][0], cput[1][myi][1]),
- (cput[2][myi][0], cput[2][myi][1]),
- (cput[3][myi][0], cput[3][myi][1]),
- ]
- return ret_pts_ut
-
- @property
- def pts_t(self):
- """Returns the transformed bounding box of the character."""
- myi = self.windex
- cpt = self.chk.cpts_t
- ret_pts_t = [
- (cpt[0][myi][0], cpt[0][myi][1]),
- (cpt[1][myi][0], cpt[1][myi][1]),
- (cpt[2][myi][0], cpt[2][myi][1]),
- (cpt[3][myi][0], cpt[3][myi][1]),
- ]
- return ret_pts_t
-
- @property
- def pts_ut_ink(self):
- """Returns the untransformed ink bounding box of the character."""
- put = self.pts_ut
- nwd = self.prop.inkbb[2] * self.utfs
- nht = self.prop.inkbb[3] * self.utfs
- x = put[0][0] + self.prop.inkbb[0] * self.utfs
- y = put[0][1] + self.prop.inkbb[1] * self.utfs + nht
- return [(x, y), (x, y - nht), (x + nwd, y - nht), (x + nwd, y)]
-
-
-def del2(x, ind):
- """deletes an index from a list"""
- return x[:ind] + x[ind + 1 :]
-
-
-def extendind(x, ind, val, default=None):
- """indexes a matrix, extending it if necessary"""
- if ind >= len(x):
- x += [default] * (ind + 1 - len(x))
- x[ind] = val
- return x
-
-
-def sortnone(x):
- """sorts an x skipping Nones"""
- rem = list(range(len(x)))
- minxrem = min([x[r] for r in rem if x[r] is not None])
- i = min([r for r in rem if x[r] == minxrem])
- sord = [i]
- rem.remove(i)
- while len(rem) > 0:
- if i == len(x) - 1 or x[i + 1] is not None:
- minxrem = min([x[r] for r in rem if x[r] is not None])
- i = min([r for r in rem if x[r] == minxrem])
- sord += [i]
- else:
- i += 1
- rem.remove(i)
- return sord
-
-
-class CProp:
- """
- A class representing the properties of a single character
- It is meant to be immutable...do not modify attributes
- """
-
- __slots__ = ("char", "charw", "spacew", "caph", "dadvs", "inkbb")
-
- def __init__(self, char, cwd, spw, caph, dadvs, inkbb):
- """Initializes CProp with given parameters."""
- self.char = char
- self.charw = cwd
- # character width
- self.spacew = spw
- # space width
- self.caph = caph
- # cap height
- self.dadvs = dadvs
- # table of how much extra width a preceding character adds to me
- self.inkbb = inkbb
-
- def __mul__(self, scl):
- """Scales the character properties by a given factor."""
- dadv2 = {k: val * scl for k, val in self.dadvs.items()}
- inkbb2 = [val * scl for val in self.inkbb]
- return CProp(
- self.char,
- self.charw * scl,
- self.spacew * scl,
- self.caph * scl,
- dadv2,
- inkbb2,
- )
-
- @property
- def __dict__(self):
- """Returns a dictionary of character properties."""
- return {
- "char": self.char,
- "charw": self.charw,
- "spacew": self.spacew,
- "caph": self.caph,
- "inkbb": self.inkbb,
- }
-
-
-class CLoc:
- """Represents the location of a single character in the SVG."""
-
- __slots__ = ("elem", "typ", "ind", "sel")
-
- def __init__(self, elem, typ, ind):
- """Initializes CLoc with given parameters."""
- self.elem = elem
- # the element it belongs to
- self.typ = typ
- # TYP_TEXT or TYP_TAIL
- self.ind = ind
- # its index
- self.sel = elem if typ == TYP_TEXT else elem.getparent()
- # where style comes from
-
- def copy(self, memo):
- """Creates a copy of the CLoc instance."""
- ret = CLoc.__new__(CLoc)
- memo[self] = ret
-
- ret.elem = memo[self.elem]
- ret.typ = self.typ
- ret.ind = self.ind
- ret.sel = memo[self.sel]
- return ret
-
- def __eq__(self, other):
- """Checks equality with another CLoc instance."""
- return (
- self.elem == other.elem and self.typ == other.typ and self.ind == other.ind
- )
-
- def __hash__(self):
- """Returns the hash value of the CLoc instance."""
- return hash((self.elem, self.typ, self.ind))
-
-
-class CharacterTable:
- """Represents the properties of a collection of characters."""
-
- def __init__(self, els):
- """Initializes CharacterTable with a list of elements."""
- self.els = els
- self.root = els[0].croot if len(els) > 0 else None
- self.tstyset, self.pchrset, self.fstyset, self.cstys = (
- CharacterTable.collect_characters(els)
- )
-
- # HASPANGO = False; os.environ["HASPANGO"]='False'
- if HASPANGO:
- # Prefer to measure with Pango if we have it (faster, more accurate)
- self.ctable = self.measure_characters()
- else:
- # Can also extract directly using fonttools, which is pure Python
- self.ctable = self.extract_characters()
-
- self.mults = dict()
- self._ftable = None
-
- @staticmethod
- def collect_characters(els):
- """Finds all the characters in a list of elements."""
- fstyset = dict() # set of characters in a given font style
- txtfsty = []
- for elem in els:
- tree = TextTree(elem)
- for _, _, _, sel, txt in tree.dgenerator():
- if txt is not None and len(txt) > 0:
- sty = sel.cspecified_style
- fsty = font_style(sty)
- fstyset.setdefault(fsty, set()).update(txt + " ")
- txtfsty.append((txt, fsty))
-
- cstys = dict() # cstys[fsty][c] looks up a character's true style
- tstyset = dict() # set of characters in a given true style
- for fsty, chrs in fstyset.items():
- tfbc = fcfg.get_true_font_by_char(fsty, chrs)
- cstys[fsty] = tfbc
- tstyset.setdefault(true_style(fsty), set()).update(chrs)
- for c, csty in tfbc.items():
- cstys.setdefault(csty, dict()).__setitem__(c, csty)
- tstyset.setdefault(csty, set()).update({c, " "})
-
- pchrset = dict()
- # set of characters that precede a char in a true style
- # used to apply differential kerning
- for txt, fsty in txtfsty:
- for j in range(1, len(txt)):
- csty = cstys[fsty][txt[j]]
- pchrset.setdefault(csty, dict())
- if csty == cstys[fsty][txt[j - 1]]:
- pchrset[csty].setdefault(txt[j], set()).update({txt[j - 1], " "})
-
- return tstyset, pchrset, fstyset, cstys
-
- def extract_characters(self):
- """
- Direct extraction of character metrics from the font file using fonttools
- fonttools is pure Python, so this usually works
- """
- badchars = {"\n", "\r"}
- ret = dict()
- for sty, chrs in self.tstyset.items():
- if sty is not None:
- bdcs = {c for c in chrs if c in badchars} # unusual chars
- gcs = {c for c in chrs if c not in badchars}
- chrfs = fcfg.get_true_font_by_char(sty, gcs)
- fntcs = dict() # font: corresponding characters
- for k, val in chrfs.items():
- fntcs.setdefault(val, []).append(k)
-
- if None in fntcs: # unrendered characters are bad
- bdcs.update(fntcs[None])
- gcs = gcs - set(fntcs[None])
- del fntcs[None]
-
- ret[sty] = dict()
- for fnt, chs in fntcs.items():
- ftfnt = fcfg.get_fonttools_font(fnt)
- if sty in self.pchrset:
- pct2 = {
- k: val for k, val in self.pchrset[sty].items() if k in chs
- }
- else:
- pct2 = dict()
- advs, dadv, inkbbs = ftfnt.get_char_advances(chs, pct2)
- for c in chs:
- cwd = advs[c]
- caph = ftfnt.cap_height
- # dr = 0
- inkbb = inkbbs[c]
- ret[sty][c] = CProp(c, cwd, None, caph, dadv, inkbb)
- spc = ret[sty][" "]
- for c in ret[sty]:
- ret[sty][c].spacew = spc.charw
- ret[sty][c].caph = spc.caph
- for bdc in bdcs:
- ret[sty][bdc] = CProp(
- bdc, 0, spc.charw, spc.caph, dict(), [0, 0, 0, 0]
- )
- else:
- ret[sty] = dict()
- for c in self.tstyset[None]:
- ret[sty][c] = CProp(c, 0, 0, 0, dict(), [0, 0, 0, 0])
- return ret
-
- def measure_characters(self):
- """
- Uses Pango to measure character properties by rendering them on an unseen
- context. Requires GTK Python bindings, generally present in Inkscape 1.1 and
- later. If Pango is absent, extract_characters will be called instead.
-
- Generates prefixed, suffixed copies of each string, compares them to a blank
- version without any character. This measures the logical advance, i.e., the
- width including intercharacter space. Width corresponds to a character with
- a composed font size of 1 uu.
- """
- cnt = 0
- pstrings = dict()
-
- def make_string(c, sty):
- nonlocal cnt
- cnt += 1
- pstrings["text" + str(cnt)] = (c, sty)
- return "text" + str(cnt)
-
- class StringInfo:
- """Stores metadata on strings"""
-
- def __init__(self, strval, strid, dadv, bareid=None):
- self.strval = strval
- self.strid = strid
- self.dadv = dadv
- self.bareid = bareid
-
- badchars = {"\n": " ", "\r": " "}
-
- def effc(c):
- return badchars.get(c, c)
-
- ixes = dict()
- validtstyset = {
- sty: chrs for sty, chrs in self.tstyset.items() if sty is not None
- }
- for sty in validtstyset:
- chrs = [chr(c) for c in fcfg.fontcharsets[sty]]
- if "=" in chrs:
- bufc = "="
- elif "M" in chrs:
- bufc = "M"
- else:
- bufc = min([ch for ch in chrs if ord(ch) >= ord("A")], key=ord)
- prefix = "I" + bufc
- suffix = bufc + "I"
- # Use an equals sign to eliminate differential kerning effects
- # (characters like '=' rarely have differential kerning), then I for
- # capital height.
-
- empty = prefix + suffix
- pistr = prefix + "pI" + suffix
- # Add 'pI' as test characters. 'p' provides the font's descender
- # (how much the tail descends), and 'I' gives cap height
- # (how tall capital letters are).
- ixes[sty] = (prefix, suffix, empty, pistr)
-
- ctbl = dict()
- bareids = []
- for sty, chrs in validtstyset.items():
- prefix, suffix, empty, pistr = ixes[sty]
- ctbl[sty] = dict()
- for myc in chrs:
- t = make_string(prefix + effc(myc) + suffix, sty)
- tbare = make_string(effc(myc), sty)
- bareids.append(tbare)
- dadv = dict()
- if DIFF_ADVANCES:
- for pchr in chrs:
- if (
- sty in self.pchrset
- and myc in self.pchrset[sty]
- and pchr in self.pchrset[sty][myc]
- ):
- tpc = make_string(
- prefix + effc(pchr) + effc(myc) + suffix, sty
- )
- # precede by all chars of the same style
- dadv[pchr] = tpc
- ctbl[sty][myc] = StringInfo(myc, t, dadv, tbare)
-
- ctbl[sty][pistr] = StringInfo(pistr, make_string(pistr, sty), dict())
- ctbl[sty][empty] = StringInfo(empty, make_string(empty, sty), dict())
-
- # Pango querying doesn't multithread well
- lock = threading.Lock()
- lock.acquire()
- try:
- pngr = PangoRenderer()
- nbb = dict()
- for sty in ctbl:
- joinch = " "
- mystrs = [val[0] for k, val in pstrings.items() if val[1] == sty]
- myids = [k for k, val in pstrings.items() if val[1] == sty]
-
- success, metrics = pngr.set_text_style(sty)
- if not (success):
- lock.release()
- return self.extract_characters()
- joinedstr = joinch.join(mystrs) + joinch + prefix
-
- # We need to render all the characters, but we don't
- # need all of their extents. For most of them we just
- # need the first character, unless the following string
- # has length 1 (and may be differently differentially kerned)
- modw = [
- any(
- len(mystrs[i]) == 1
- for i in range(i, i + 2)
- if 0 <= i < len(mystrs)
- )
- for i in range(len(mystrs))
- ]
- needexts = [
- "1"
- if len(s) == 1
- else "1" + "0" * (len(s) - 2) + ("1" if modw[i] else "0")
- for i, s in enumerate(mystrs)
- ]
- needexts2 = "0".join(needexts) + "1" + "1" * len(prefix)
- pngr.render_text(joinedstr)
- exts, _ = pngr.get_character_extents(metrics[1], needexts2)
-
- spw = exts[-len(prefix) - 1][0][2]
- cnt = 0
- x = 0
- for i, mystr in enumerate(mystrs):
- if modw[i]:
- altw = (
- exts[cnt + len(mystr) - 1][0][0]
- + exts[cnt + len(mystr) - 1][0][2]
- - exts[cnt][0][0]
- )
- else:
- altw = (
- exts[cnt + len(mystr) + 1][0][0] - exts[cnt][0][0] - spw
- )
- wdt = altw
-
- firstch = exts[cnt]
- (xbr, ybr, wbr, hbr) = tuple(firstch[2])
- if myids[i] not in bareids:
- xbr = x
- wbr = wdt
- # use logical width
-
- nbb[myids[i]] = [val * TEXTSIZE for val in [xbr, ybr, wbr, hbr]]
- cnt += len(mystr) + len(joinch)
- x += wdt
- finally:
- lock.release()
-
- dadv = dict()
- for sty, chd in ctbl.items():
- prefix, suffix, empty, pistr = ixes[sty]
- for i in chd:
- chd[i].bbx = bbox(nbb[chd[i].strid])
- if DIFF_ADVANCES:
- precwidth = dict()
- for j in chd[i].dadv:
- precwidth[j] = bbox(nbb[chd[i].dadv[j]]).w
- # width including the preceding character and extra kerning
- chd[i].precwidth = precwidth
-
- if DIFF_ADVANCES:
- dadv[sty] = dict()
- for i in chd:
- mcw = chd[i].bbx.w - chd[empty].bbx.w # my character width
- for j in chd[i].precwidth:
- pcw = chd[j].bbx.w - chd[empty].bbx.w # preceding char width
- bcw = chd[i].precwidth[j] - chd[empty].bbx.w # both char widths
- dadv[sty][j, chd[i].strval] = bcw - pcw - mcw
- # preceding char, then next char
-
- for sty, chd in ctbl.items():
- prefix, suffix, empty, pistr = ixes[sty]
- blnkwd = chd[empty].bbx.w
- spw = chd[" "].bbx.w - blnkwd # space width
- caph = -chd[empty].bbx.y1
- # cap height is the top of I (relative to baseline)
-
- dadvscl = dict()
- if DIFF_ADVANCES:
- for k in dadv[sty]:
- dadvscl[k] = dadv[sty][k] / TEXTSIZE
-
- for i in chd:
- cwd = chd[i].bbx.w - blnkwd
- # character width (full, including extra space on each side)
- if chd[i].bareid in nbb:
- inkbb = nbb[chd[i].bareid]
- else:
- # whitespace: make zero-width
- inkbb = [chd[i].bbx.x1, chd[i].bbx.y1, 0, 0]
-
- if chd[i].strval in badchars:
- cwd = 0
- chd[i] = CProp(
- chd[i].strval,
- cwd / TEXTSIZE,
- spw / TEXTSIZE,
- caph / TEXTSIZE,
- dadvscl,
- [val / TEXTSIZE for val in inkbb],
- )
- if None in self.tstyset:
- ctbl[None] = dict()
- for c in self.tstyset[None]:
- ctbl[None][c] = CProp(c, 0, 0, 0, dict(), [0, 0, 0, 0])
- return ctbl
-
- def __str__(self):
- ret = ""
- for sty in self.ctable:
- ret += str(sty) + "\n"
- val = None
- for c, val in self.ctable[sty].items():
- ret += " " + c + " : " + str(vars(val)) + "\n"
- if val is not None:
- ret += " " + str(val.dadvs)
- return ret
-
- @staticmethod
- def flowy(sty):
- """Returns the font's ascent value for the given style."""
- return fcfg.get_fonttools_font(sty)._ascent
-
- def get_prop(self, char, sty):
- """Returns the properties of a character for a given style."""
- try:
- return self.ctable[sty][char]
- except KeyError as excp:
- reset_msg = (
- "This probably means that new text was generated and the SVG's "
- "character table is outdated. Reset it by setting"
- )
- reset_action = "self.svg.char_table = None"
- frequency_msg = (
- "(This can take a long time, so best practice is to do this as few "
- "times as is possible.)"
- )
- if sty not in self.ctable:
- debug("No style matches found!")
- debug(reset_msg)
- debug(" " + reset_action)
- debug(frequency_msg)
- debug("\nCharacter: " + char)
- debug("Style: " + str(sty))
- debug("Existing styles: " + str(list(self.ctable.keys())))
- else:
- debug("No character matches!")
- debug(reset_msg)
- debug(" " + reset_action)
- debug(frequency_msg)
- debug("\nCharacter: " + char)
- debug("Style: " + str(sty))
- debug("Existing chars: " + str(list(self.ctable[sty].keys())))
- raise KeyError from excp
-
- def get_prop_mult(self, char, sty, scl):
- """Returns the scaled properties of a character for a given style."""
- try:
- return self.mults[(char, sty, scl)]
- except KeyError:
- self.mults[(char, sty, scl)] = self.get_prop(char, sty) * scl
- return self.mults[(char, sty, scl)]
-
-
-def wstrip(txt):
- """strip whitespaces"""
- return txt.translate({ord(c): None for c in " \n\t\r"})
-
-
-def deleteempty(elem):
- """
- Recursively delete empty elements
- Tspans are deleted if they're totally empty, TextElements are deleted
- if they contain only whitespace
- """
- anydeleted = False
- for k in list(elem):
- dcsndt = deleteempty(k)
- anydeleted |= dcsndt
- txt = elem.text
- tail = elem.tail
- if (
- (txt is None or len(txt) == 0)
- and (tail is None or len(tail) == 0)
- and len(elem) == 0
- ):
- elem.delete()
- anydeleted = True
- # delete anything empty
- elif elem.tag == TEtag:
- if all(
- (dcsndt.text is None or len(wstrip(dcsndt.text)) == 0)
- and (dcsndt.tail is None or len(wstrip(dcsndt.tail)) == 0)
- for dcsndt in elem.descendants2()
- ):
- elem.delete()
- anydeleted = True
- # delete any text elements that are just white space
- return anydeleted
-
-
-def maxnone(x):
- """Returns the maximum value of a list, or None if the list is empty."""
- if len(x) > 0:
- return max(x)
- return None
-
-
-def xyset(elem, xyt, val):
- """
- A fast setter for 'x', 'y', 'dx', and 'dy' that uses lxml's set directly and
- converts arrays to a string
- """
- if not (val):
- elem.attrib.pop(xyt, None) # pylint: disable=no-member
- else:
- EBset(elem, xyt, ' '.join('%s' % v for v in val))
-
-def remove_position_overflows(el):
- """
- Normally Inkscape only produces multiple position attributes (x,y,dx,dy) on a
- tspan corresponding to the number of characters. It does support more than this,
- but that is a somewhat pathological case that makes editing difficult.
- Removing this prior to parsing simplifies the logic needed.
- """
- xyvs = {(d, patt) : ParsedText.get_xy(d, patt) for d in el.descendants2() for patt in ['x','y','dx','dy']}
- anyoverflow = False
- ttree = [v for v in TextTree(el).dgenerator()]
- for ddi0, typ0, src0, sel0, txt0 in ttree:
- if typ0==TYP_TEXT:
- for patt in ['x','y','dx','dy']:
- anyoverflow |= (len(xyvs[src0,patt])>1 and
- ((txt0 is not None and len(xyvs[src0,patt])>len(txt0))
- or (txt0 is None)))
- # inkex.utils.debug((anyoverflow, patt,src0.get_id(),typ0))
-
-
- if anyoverflow:
- toplevels = list(el)
- xvs, yvs, dxs, dys = [[] for _ in range(4)]
- pos = {'x':xvs,'y':yvs,'dx':dxs,'dy':dys}
- diffs = ['dx','dy']
- topidx = 0
- for ddi0, typ0, src0, sel0, txt0 in ttree:
- if typ0==TYP_TEXT:
- for patt in ['x','y','dx','dy']:
- xyv = xyvs[src0, patt]
- # Objects lower in the descendant list override ancestors
- # dx and dy don't affect flows
- if len(xyv) > 0 and xyv[0] is not None:
- if patt in diffs or len(xyv)>1:
- src0.set(patt,None) # leave individual x,y alone
- cntd = 0
- cntl = 0
- for _, typ, src, _, txt in TextTree(el).dgenerator(subel=src0):
- if (
- typ == TYP_TAIL
- and src.get("sodipodi:role") == "line"
- and src in toplevels
- ):
- cntd += 1
- # top-level Tspans have an implicit CR at
- # the beginning of the tail
-
- if txt is not None:
- srtd = cntd
- stpd = min(len(xyv),cntd+len(txt))
- srtl = topidx+cntl
- stpl = topidx+cntl+stpd-srtd
-
- if stpl>=len(pos[patt]):
- pos[patt] += [None] * (stpl-len(pos[patt])+1)
- pos[patt][srtl:stpl] = xyv[srtd:stpd]
- cntd += len(txt)
- cntl += len(txt)
- if cntd >= len(xyv):
- break
- if txt0 is not None:
- topidx += len(txt0)
-
- topidx = 0
- for ddi, typ, src, sel, txt in ttree:
- if txt is not None:
- wrapped_tail = False
- for patt in ['x','y','dx','dy']:
- vals = [v for v in pos[patt][topidx:topidx+len(txt)] if v is not None]
- if len(vals)>0:
- if typ==TYP_TAIL and not wrapped_tail:
- # Tails need to be wrapped in a new Tspan
- src = wrap_string(src,typ)
- wrapped_tail = True
-
- if not(len(vals)==1 and patt not in diffs and wrapped_tail):
- # wrapping a tail could mess with sprl if single x/y specified
- xyset(src, patt, vals)
- topidx += len(txt)
-
-def wrap_string(src,typ):
- ''' Wrap a string in a new Tspan/FlowSpan'''
- if typ==TYP_TAIL:
- span = inkex.Tspan if src.tag in TEtags else inkex.FlowSpan
- t = span()
- t.text = src.tail
- src.tail = None
- src.getparent().insert(src.getparent().index(src) + 1, t)
- else:
- span = inkex.Tspan if src.tag in TEtags else inkex.FlowSpan
- t = span()
- t.text = src.text
- src.text = None
- src.insert(0, t)
- return t
-
-def trim_list(lst,val):
- """ Trims any values from the end of a list equal to val """
- trim = lst[:len(lst) - next((i for i, x in enumerate(reversed(lst)) if x != val), len(lst))]
- return trim if len(trim)>0 else None
\ No newline at end of file
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/typelibs/PangoFT2-1.0.typelib b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/typelibs/PangoFT2-1.0.typelib
deleted file mode 100644
index e505499..0000000
Binary files a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/typelibs/PangoFT2-1.0.typelib and /dev/null differ
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/typelibs/PangoFc-1.0.typelib b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/typelibs/PangoFc-1.0.typelib
deleted file mode 100644
index 652ac77..0000000
Binary files a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/typelibs/PangoFc-1.0.typelib and /dev/null differ
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/typelibs/fontconfig-2.0.typelib b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/typelibs/fontconfig-2.0.typelib
deleted file mode 100644
index acbd826..0000000
Binary files a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/typelibs/fontconfig-2.0.typelib and /dev/null differ
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/typelibs/freetype2-2.0.typelib b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/typelibs/freetype2-2.0.typelib
deleted file mode 100644
index 46dab5d..0000000
Binary files a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/typelibs/freetype2-2.0.typelib and /dev/null differ
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/utils.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/utils.py
deleted file mode 100644
index dd7cb0a..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/text/utils.py
+++ /dev/null
@@ -1,639 +0,0 @@
-# coding=utf-8
-#
-# Copyright (c) 2023 David Burghoff
-# Martin Owens
-# Sergei Izmailov
-# Thomas Holder
-# Jonathan Neuhauser
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-"""
-Utilities for text parsing
-"""
-
-import math
-import re
-import sys
-import os
-from functools import lru_cache
-import lxml
-from lxml import etree
-import inkex
-import inkex.command
-from inkex.properties import all_properties
-from inkex.units import CONVERSIONS, BOTH_MATCH
-
-# For style components that represent a size (stroke-width, font-size, etc),
-# calculate the true size reported by Inkscape in user units, inheriting
-# any styles/transforms/document scaling
-flookup = {"small": "10px", "medium": "12px", "large": "14px"}
-
-
-def composed_width(elem, comp):
- """
- Gets the transformed size of a style component and the scale factor representing
- the scale of the composed transform, accounting for relative sizes.
-
- Parameters:
- elem (Element): The element whose style to compute.
- comp (str): The component of the style to compute, such as 'stroke-width'
- or 'font-size'.
-
- Returns:
- tuple: A tuple containing the true size in user units, the scale factor,
- and the untransformed size
- """
- sty = elem.cspecified_style
- ctf = elem.ccomposed_transform
- satt = sty.get(comp)
-
- # Get default attribute if empty
- if satt is None:
- satt = default_style_atts[comp]
-
- if "%" in satt: # relative width, get ancestor width
- cel = elem
- while satt != cel.cstyle.get(comp) and satt != cel.get(comp):
- cel = cel.getparent()
- # figure out ancestor where % is coming from
-
- satt = float(satt.strip("%")) / 100
- tsz, scf, utsz = composed_width(cel.getparent(), comp)
-
- # Since relative widths have no untransformed width, we assign
- # it to be a scaled version of the ancestor's ut width
- return tsz * satt, scf, utsz * satt
- utsz = ipx(satt)
- if utsz is None:
- utsz = (
- utsz
- or ipx(flookup.get(satt) if comp == "font-size" else None)
- or ipx(default_style_atts[comp])
- )
- scf = math.sqrt(abs(ctf.a * ctf.d - ctf.b * ctf.c)) # scale factor
- return utsz * scf, scf, utsz
-
-
-def composed_lineheight(elem):
- """
- Get absolute line-height in user units based on an element's specified style.
-
- Parameters:
- elem (Element): The element whose line-height to compute.
-
- Returns:
- float: The computed line-height in user units.
- """
- sty = elem.cspecified_style
- satt = sty.get("line-height", default_style_atts["line-height"])
- if satt == "normal":
- satt = 1.25
- elif "%" in satt: # relative width, get parent width
- satt = float(satt.strip("%")) / 100
- else:
- try:
- # Lines have no unit, em treated the same
- satt = float(satt.strip("em"))
- except ValueError:
- fsz, scf, _ = composed_width(elem, "font-size")
- satt = ipx(satt) / (fsz / scf)
- fsz, _, _ = composed_width(elem, "font-size")
- return satt * fsz
-
-
-def unique(lst):
- """
- Returns a list of unique items from the given list while preserving order.
-
- Parameters:
- lst (list): The list from which to remove duplicates.
-
- Returns:
- list: A list of unique items in the order they appeared in the input list.
- """
- return list(dict.fromkeys(lst))
-
-
-def uniquetol(x, tol):
- """
- Like unique, but for numeric values and accepts a tolerance.
-
- Parameters:
- x (list of float): List of numeric values from which to remove near-duplicates.
- tol (float): The tolerance within which two numbers are considered the same.
-
- Returns:
- list of float: A list of unique numbers within the specified tolerance.
- """
- if not x: # Check if the input list is empty
- return []
- x_sorted = sorted((y for y in x if y is not None)) # Sort, ignoring None values
- ret = (
- [x_sorted[0]] if x_sorted else []
- ) # Start with the first value if there are any non-None values
- for i in range(1, len(x_sorted)):
- if abs(x_sorted[i] - ret[-1]) > tol:
- ret.append(x_sorted[i])
- # If there were any None values in the original list, append None to the result list
- if None in x:
- ret.append(None)
- return ret
-
-
-# Adds ctag to the inkex classes, which holds each class's corresponding tag
-# Checking the tag is usually much faster than instance checking, which can
-# substantially speed up low-level functions.
-# pylint:disable=protected-access
-lt = dict(inkex.elements._parser.NodeBasedLookup.lookup_table)
-shapetags = set()
-for key, v in lt.items():
- for v2 in v:
- v2.ctag = inkex.addNS(key[1], key[0]) if isinstance(key, tuple) else key
- if issubclass(v2, inkex.ShapeElement):
- shapetags.add(v2.ctag)
-tags = lambda x: {v.ctag for v in x} # converts class tuple to set of tags
-# pylint:enable=protected-access
-
-rectlike_tags = tags((inkex.PathElement, inkex.Rectangle, inkex.Line, inkex.Polyline))
-rect_tag = inkex.Rectangle.ctag
-pel_tag = inkex.PathElement.ctag
-usetag = inkex.Use.ctag
-
-PTH_CMDS = "".join(list(inkex.paths.PathCommand._letter_to_class.keys()))
-pth_cmd_pat = re.compile("[" + re.escape(PTH_CMDS) + "]")
-cnt_pth_cmds = lambda d: len(pth_cmd_pat.findall(d)) # count path commands
-
-
-def isrectangle(elem, includingtransform=True):
- """
- Determines if an element is rectangle-like, considering transformations if
- specified.
-
- Parameters:
- elem (Element): The element to check.
- includingtransform (bool): Whether to consider transformations in the
- determination.
-
- Returns:
- tuple: A tuple containing a boolean indicating if the element is a rectangle and
- the path if it is.
- """
-
- ret = True
- if not includingtransform and elem.tag == rect_tag:
- pth = elem.cpath
- elif elem.tag in rectlike_tags:
- if elem.tag == pel_tag and not (1 <= cnt_pth_cmds(elem.get("d", "")) <= 6):
- # Allow up to 6 (initial M + 4 lines + redundant close path)
- return False
- pth = elem.cpath
-
- if includingtransform:
- tmat = elem.ctransform.matrix
- x, y = list(
- zip(
- *[
- (
- tmat[0][0] * pt.x + tmat[0][1] * pt.y + tmat[0][2],
- tmat[1][0] * pt.x + tmat[1][1] * pt.y + tmat[1][2],
- )
- for pt in pth.end_points
- ]
- )
- )
- else:
- x, y = list(zip(*[(pt.x, pt.y) for pt in pth.end_points]))
-
- maxsz = max(max(x) - min(x), max(y) - min(y))
- tol = 1e-3 * maxsz
- if len(uniquetol(x, tol)) != 2 or len(uniquetol(y, tol)) != 2:
- ret = False
- elif elem.tag == usetag:
- useel = elem.get_link("xlink:href")
- if useel is not None:
- return isrectangle(useel)
- ret = True
- else:
- ret = False
-
- if ret:
- if (
- elem.get_link("mask", llget=True) is not None
- or elem.cspecified_style.get_link("filter", elem.croot) is not None
- ):
- ret = False
- elif elem.get_link("clip-path", llget=True) is not None and any(
- not isrectangle(k) for k in list(elem.get_link("clip-path", llget=True))
- ):
- ret = False
- return ret
-
-class InkscapeSystemInfo:
- """
- Discovers and caches Inkscape System info.
- """
-
- _UNSET = object() # Sentinel value for unset attributes
-
- def __init__(self):
- """Initialize InkscapeSystemInfo."""
- self._language = self._UNSET
- self._preferences = self._UNSET
- self._binary_location = self._UNSET
- self._binary_version = self._UNSET
-
- @property
- def language(self):
- """Get the language used by Inkscape."""
- if self._language is self._UNSET:
- self._language = self.determine_language()
- return self._language
-
- @property
- def preferences(self):
- """Get the preferences file location."""
- if self._preferences is self._UNSET:
- self._preferences = self.find_preferences()
- return self._preferences
-
- @property
- def binary_location(self):
- """Get the Inkscape binary location."""
- if self._binary_location is self._UNSET:
- self._binary_location = self.get_binary_location()
- return self._binary_location
-
- @property
- def binary_version(self):
- """Get the Inkscape binary version."""
- if self._binary_version is self._UNSET:
- self._binary_version = self.get_binary_version()
- return self._binary_version
-
- def get_binary_version(self):
- """Gets the binary location by calling with --version (slow)"""
- proc = subprocess_repeat([self.binary_location, "--version"])
-
- match = re.search(r"Inkscape\s+(\S+)\s+\(", str(proc.stdout))
- return match.group(1) if match else None
-
- @staticmethod
- def get_binary_location():
- """
- Gets the location of the Inkscape binary, checking the system
- path if necessary.
- """
-
- program = inkex.command.INKSCAPE_EXECUTABLE_NAME
- try:
- return inkex.command.which(program)
- except inkex.command.CommandNotFound as excp:
- # Search the path as a backup (primarily for testing)
- try:
- from shutil import which as warlock
-
- for sysp in sys.path:
- if sys.platform == "win32":
- prog = warlock(program, path=os.environ["PATH"] + ";" + sysp)
- if prog:
- return prog
- except ImportError as impe:
- raise ImportError("Failed to import the 'which' function.") from impe
- raise inkex.command.CommandNotFound(
- f"Can not find the command: '{program}'"
- ) from excp
-
- @staticmethod
- def find_preferences():
- """Attempt to discover preferences.xml"""
- prefspaths = []
-
- # First check the location of the user extensions directory
- mydir = os.path.dirname(os.path.abspath(__file__))
- file_path = mydir
- while "extensions" in file_path and os.path.basename(file_path) != "extensions":
- file_path = os.path.dirname(file_path)
- prefspaths.append(os.path.join(os.path.dirname(file_path), "preferences.xml"))
-
- # Try some common default locations based on the home directory
- home = os.path.expanduser("~")
- if sys.platform == "win32":
- appdata = os.getenv("APPDATA")
- if appdata is not None:
- # https://wiki.inkscape.org/wiki/Preferences_subsystem
- prefspaths.append(
- os.path.join(
- os.path.abspath(appdata), "inkscape", "preferences.xml"
- )
- )
- # https://en.wikipedia.org/wiki/Environment_variable#Default_Values_on_Microsoft_Windows
- prefspaths.append(
- os.path.join(home, "AppData", "Roaming", "inkscape", "preferences.xml")
- )
- # https://en.wikipedia.org/wiki/Environment_variable#Default_Values_on_Microsoft_Windows
- # http://tavmjong.free.fr/INKSCAPE/MANUAL/html/Customize-Files.html
- prefspaths.append(
- os.path.join(home, "Application Data", "Inkscape", "preferences.xml")
- )
- else:
- if sys.platform == "darwin":
- # test Mac
- prefspaths.append(
- os.path.join(
- home,
- "Library",
- "Application Support",
- "org.inkscape.Inkscape",
- "config",
- "inkscape",
- "preferences.xml",
- )
- )
- # test Linux
- prefspaths.append(
- os.path.join(home, ".config", "inkscape", "preferences.xml")
- )
- # https://wiki.inkscape.org/wiki/Preferences_subsystem#Where_preferences_are_stored
- prefspaths.append(
- os.path.join(home, ".config", "Inkscape", "preferences.xml")
- )
- # https://wiki.inkscape.org/wiki/Preferences_subsystem#Where_preferences_are_stored
- # https://alpha.inkscape.org/vectors/www.inkscapeforum.com/viewtopicc8ae.html?t=1712
- prefspaths.append(os.path.join(home, ".inkscape", "preferences.xml"))
-
- # Try finding from snap location
- file_path = mydir
- while "snap" in file_path and os.path.basename(file_path) != "snap":
- file_path = os.path.dirname(file_path)
- prefspaths.append(
- os.path.join(
- os.path.dirname(file_path), ".config", "inkscape", "preferences.xml"
- )
- )
-
- # Iterate over potential paths and return the first existing one
- for path in prefspaths:
- if os.path.exists(path):
- return path
-
- return None # failed
-
- @staticmethod
- def determine_language(verbose=False):
- """Try to find the language Inkscape is using"""
-
- def get_ui_language(prefspath):
- proot = etree.parse(prefspath).getroot()
- for k in proot:
- if k.get("id") == "ui" and k.get("language") is not None:
- return k.get("language")
- return None
-
- def getlocale_mod():
- # pylint:disable=import-outside-toplevel
- import warnings
- import locale
- # pylint:enable=import-outside-toplevel
-
- with warnings.catch_warnings():
- # temporary work-around for
- # https://github.com/python/cpython/issues/82986
- # by continuing to use getdefaultlocale() even though it has been
- # deprecated.
- if sys.version_info.minor >= 13:
- warnings.warn(
- "This function may not behave as expected in"
- " Python versions beyond 3.12",
- FutureWarning,
- )
- warnings.simplefilter("ignore", category=DeprecationWarning)
- language_code = locale.getdefaultlocale()[0]
- if language_code:
- return language_code
- return "en-US"
-
- # First, try to get the language from preferences.xml
- pxml = InkscapeSystemInfo().find_preferences()
- if verbose:
- inkex.utils.debug("Found preferences.xml: " + str(pxml))
- if pxml is not None:
- prefslang = get_ui_language(pxml)
- if verbose:
- inkex.utils.debug("preferences.xml language: " + str(prefslang))
- # If it can't be found or is set to use the system lang, use locale
- if pxml is None or prefslang in ["", None]:
- lcle = getlocale_mod()
- prefslang = lcle.split("_")[0]
- if verbose:
- inkex.utils.debug("locale language: " + str(prefslang))
- return prefslang
-
-
-inkex.inkscape_system_info = InkscapeSystemInfo() # type: ignore
-
-
-def subprocess_repeat(argin,cwd=None):
- """
- In the event of a timeout, repeats a subprocess call several times.
-
- Parameters:
- argin (list): The command and arguments to run in the subprocess.
-
- Returns:
- CompletedProcess: The result from the subprocess call.
- """
- base_timeout = 60
- nattempts = 6
- import subprocess
-
- nfails = 0
- ntime = 0
- for i in range(nattempts):
- timeout = base_timeout * 2**i
- try:
- os.environ["SELF_CALL"] = "true" # seems to be needed for 1.3
- proc = subprocess.run(
- argin,
- shell=False,
- timeout=timeout,
- stdout=subprocess.PIPE,
- stderr=subprocess.DEVNULL,
- check=True, cwd=cwd
- )
- break
- except subprocess.TimeoutExpired:
- nfails += 1
- ntime += timeout
- if nfails == nattempts:
- raise TimeoutError(
- "\nThe call to the Inkscape binary timed out "
- + str(nattempts)
- + " times in "
- + str(ntime)
- + " seconds.\n\n"
- + "This may be a temporary issue; try running the extension again."
- )
- else:
- return proc
-
-
-# Get default style attributes
-try:
- default_style_atts = {a: v[1] for a, v in all_properties.items()} # type: ignore
-except TypeError:
- default_style_atts = {
- a: "".join([str(t.value) for t in v.default_value])
- for a, v in all_properties.items()
- } # type: ignore
-default_style_atts["font-variant-ligatures"] = "normal" # missing
-
-
-comment_tag = lxml.etree.Comment("").tag
-
-
-def list2(elem):
- """Returns non-comment children of an element."""
- return [k for k in list(elem) if not (k.tag == comment_tag)]
-
-
-conv2 = {k: v / CONVERSIONS["px"] for k, v in CONVERSIONS.items()}
-
-
-@lru_cache(maxsize=None)
-def ipx(strin):
- """
- Implicit pixel function
- For many properties, a size specification of '1px' actually means '1uu'
- Even if the size explicitly says '1mm' and the user units are mm, this will be
- first converted to px and then interpreted to mean user units. (So '1mm' would
- up being bigger than 1 mm). This returns the size as Inkscape will interpret it
- (in uu).
- No unit: Assumes 'px'
- Invalid unit: Returns None
- """
- try:
- ret = BOTH_MATCH.match(strin)
- value = float(ret.groups()[0])
- from_unit = ret.groups()[-1] or "px"
- return value * conv2[from_unit]
- except (AttributeError, TypeError):
- return None
-
-
-# pylint:disable=invalid-name
-class bbox:
- """
- A modified bounding box class.
- """
-
- __slots__ = ("isnull", "x1", "x2", "y1", "y2", "xc", "yc", "w", "h", "sbb")
-
- def __init__(self, bb):
- """Initialize bbox."""
- if bb is not None:
- self.isnull = False
- if len(bb) == 2: # allow tuple of two points ((x1,y1),(x2,y2))
- self.sbb = [
- min(bb[0][0], bb[1][0]),
- min(bb[0][1], bb[1][1]),
- abs(bb[0][0] - bb[1][0]),
- abs(bb[0][1] - bb[1][1]),
- ]
- else:
- self.sbb = bb[:] # standard bbox
- self.x1, self.y1, self.w, self.h = self.sbb
- self.x2 = self.x1 + self.w
- self.y2 = self.y1 + self.h
- self.xc = (self.x1 + self.x2) / 2
- self.yc = (self.y1 + self.y2) / 2
- else:
- self.isnull = True
-
- def copy(self):
- """Copy the bounding box."""
- ret = bbox.__new__(bbox)
- ret.isnull = self.isnull
- if not self.isnull:
- ret.x1 = self.x1
- ret.x2 = self.x2
- ret.y1 = self.y1
- ret.y2 = self.y2
- ret.xc = self.xc
- ret.yc = self.yc
- ret.w = self.w
- ret.h = self.h
- ret.sbb = self.sbb[:]
- return ret
-
- def transform(self, xform):
- """Transform the bounding box."""
- if not (self.isnull) and xform is not None:
- tr1 = xform.apply_to_point([self.x1, self.y1])
- tr2 = xform.apply_to_point([self.x2, self.y2])
- tr3 = xform.apply_to_point([self.x1, self.y2])
- tr4 = xform.apply_to_point([self.x2, self.y1])
- return bbox(
- [
- min(tr1[0], tr2[0], tr3[0], tr4[0]),
- min(tr1[1], tr2[1], tr3[1], tr4[1]),
- max(tr1[0], tr2[0], tr3[0], tr4[0])
- - min(tr1[0], tr2[0], tr3[0], tr4[0]),
- max(tr1[1], tr2[1], tr3[1], tr4[1])
- - min(tr1[1], tr2[1], tr3[1], tr4[1]),
- ]
- )
- return bbox(None)
-
- def intersect(self, bb2):
- """Check if bounding boxes intersect."""
- return (abs(self.xc - bb2.xc) * 2 < (self.w + bb2.w)) and (
- abs(self.yc - bb2.yc) * 2 < (self.h + bb2.h)
- )
-
- def union(self, bb2):
- """Get the union of two bounding boxes."""
- if isinstance(bb2, list):
- bb2 = bbox(bb2)
- if not (self.isnull) and not bb2.isnull:
- minx = min((self.x1, self.x2, bb2.x1, bb2.x2))
- maxx = max((self.x1, self.x2, bb2.x1, bb2.x2))
- miny = min((self.y1, self.y2, bb2.y1, bb2.y2))
- maxy = max((self.y1, self.y2, bb2.y1, bb2.y2))
- return bbox([minx, miny, maxx - minx, maxy - miny])
- if self.isnull and not bb2.isnull:
- return bb2
- return self # bb2 is empty
-
- def intersection(self, bb2):
- """Get the intersection of two bounding boxes."""
- if isinstance(bb2, list):
- bb2 = bbox(bb2)
- if not (self.isnull):
- minx = max([self.x1, bb2.x1])
- maxx = min([self.x2, bb2.x2])
- miny = max([self.y1, bb2.y1])
- maxy = min([self.y2, bb2.y2])
- if maxx < minx or maxy < miny:
- return bbox(None)
- return bbox([minx, miny, maxx - minx, maxy - miny])
- return bbox(bb2.sbb)
-
- def __mul__(self, scl):
- """Scale the bounding box."""
- return bbox([self.x1 * scl, self.y1 * scl, self.w * scl, self.h * scl])
-
-
-# pylint:enable=invalid-name
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/transforms.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/transforms.py
deleted file mode 100644
index 846dff7..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/transforms.py
+++ /dev/null
@@ -1,1253 +0,0 @@
-# coding=utf-8
-#
-# Copyright (C) 2006 Jean-Francois Barraud, barraud@math.univ-lille1.fr
-# Copyright (C) 2010 Alvin Penner, penner@vaxxine.com
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-# barraud@math.univ-lille1.fr
-#
-# This code defines several functions to make handling of transform
-# attribute easier.
-#
-"""
-Provide transformation parsing to extensions
-"""
-from __future__ import annotations
-import re
-from decimal import Decimal
-from math import cos, radians, sin, sqrt, tan, fabs, atan2, hypot, pi, isfinite
-from typing import (
- overload,
- cast,
- Callable,
- Generator,
- Iterator,
- Tuple,
- Union,
- Optional,
- List,
-)
-
-
-from .utils import strargs, KeyDict
-
-
-VectorLike = Union[
- "ImmutableVector2d", Tuple[float, float]
-] # pylint: disable=invalid-name
-MatrixLike = Union[
- str,
- Tuple[Tuple[float, float, float], Tuple[float, float, float]],
- Tuple[float, float, float, float, float, float],
- "Transform",
-]
-BoundingIntervalArgs = Union[
- "BoundingInterval", Tuple[float, float], float
-] # pylint: disable=invalid-name
-
-# All the names that get added to the inkex API itself.
-__all__ = (
- "BoundingBox",
- "DirectedLineSegment",
- "ImmutableVector2d",
- "Transform",
- "Vector2d",
-)
-
-
-# Old settings, supported because users click 'ok' without looking.
-XAN = KeyDict({"l": "left", "r": "right", "m": "center_x"})
-YAN = KeyDict({"t": "top", "b": "bottom", "m": "center_y"})
-# Anchoring objects with given directions (see inx options)
-CUSTOM_DIRECTION = {270: "tb", 90: "bt", 0: "lr", 360: "lr", 180: "rl"}
-DIRECTION = ["tb", "bt", "lr", "rl", "ro", "ri"]
-
-
-class ImmutableVector2d:
- """Represents an immutable element of 2-dimensional Euclidean space"""
-
- _x = 0.0
- _y = 0.0
-
- x = property(lambda self: self._x)
- y = property(lambda self: self._y)
-
- @overload
- def __init__(self):
- # type: () -> None
- pass
-
- @overload
- def __init__(self, v, fallback=None):
- # type: (Union[VectorLike, str], Optional[Union[VectorLike, str]]) -> None
- pass
-
- @overload
- def __init__(self, x, y):
- # type: (float, float) -> None
- pass
-
- def __init__(self, *args, fallback=None):
- try:
- if len(args) == 0:
- x, y = 0.0, 0.0
- elif len(args) == 1:
- x, y = self._parse(args[0])
- elif len(args) == 2:
- x, y = map(float, args)
- else:
- raise ValueError("too many arguments")
- except (ValueError, TypeError) as error:
- if fallback is None:
- raise ValueError("Cannot parse vector and no fallback given") from error
- x, y = ImmutableVector2d(fallback)
- self._x, self._y = float(x), float(y)
-
- @staticmethod
- def _parse(point):
- # type: (Union[VectorLike, str]) -> Tuple[float, float]
- if isinstance(point, ImmutableVector2d):
- x, y = point.x, point.y
- elif isinstance(point, (tuple, list)) and len(point) == 2:
- x, y = map(float, point)
- elif isinstance(point, str) and point.count(",") == 1:
- x, y = map(float, point.split(","))
- else:
- raise ValueError(f"Can't parse {repr(point)}")
- return x, y
-
- def __add__(self, other):
- # type: (VectorLike) -> Vector2d
- other = Vector2d(other)
- return Vector2d(self.x + other.x, self.y + other.y)
-
- def __radd__(self, other):
- # type: (VectorLike) -> Vector2d
- other = Vector2d(other)
- return Vector2d(self.x + other.x, self.y + other.y)
-
- def __sub__(self, other):
- # type: (VectorLike) -> Vector2d
- other = Vector2d(other)
- return Vector2d(self.x - other.x, self.y - other.y)
-
- def __rsub__(self, other):
- # type: (VectorLike) -> Vector2d
- other = Vector2d(other)
- return Vector2d(-self.x + other.x, -self.y + other.y)
-
- def __neg__(self):
- # type: () -> Vector2d
- return Vector2d(-self.x, -self.y)
-
- def __pos__(self):
- # type: () -> Vector2d
- return Vector2d(self.x, self.y)
-
- def __floordiv__(self, factor):
- # type: (float) -> Vector2d
- return Vector2d(self.x / float(factor), self.y / float(factor))
-
- def __truediv__(self, factor):
- # type: (float) -> Vector2d
- return Vector2d(self.x / float(factor), self.y / float(factor))
-
- def __div__(self, factor):
- # type: (float) -> Vector2d
- return Vector2d(self.x / float(factor), self.y / float(factor))
-
- def __mul__(self, factor):
- # type: (float) -> Vector2d
- return Vector2d(self.x * factor, self.y * factor)
-
- def __abs__(self):
- # type: () -> float
- return self.length
-
- def __rmul__(self, factor):
- # type: (float) -> VectorLike
- return Vector2d(self.x * factor, self.y * factor)
-
- def __repr__(self):
- # type: () -> str
- return f"Vector2d({self.x:.6g}, {self.y:.6g})"
-
- def __str__(self):
- # type: () -> str
- return f"{self.x:.6g}, {self.y:.6g}"
-
- def __iter__(self) -> Generator[float, None, None]:
- yield self.x
- yield self.y
-
- def __len__(self):
- # type: () -> int
- return 2
-
- def __getitem__(self, item):
- # type: (int) -> float
- return (self.x, self.y)[item]
-
- def to_tuple(self) -> Tuple[float, float]:
- """A tuple of the vector's components"""
- return cast(Tuple[float, float], tuple(self))
-
- def to_polar_tuple(self):
- # type: () -> Tuple[float, Optional[float]]
- """A tuple of the vector's magnitude and direction
-
- .. versionadded:: 1.1"""
- return self.length, self.angle
-
- def dot(self, other: VectorLike) -> float:
- """Multiply Vectors component-wise"""
- other = Vector2d(other)
- return self.x * other.x + self.y * other.y
-
- def cross(self, other):
- # type: (VectorLike) -> float
- """Z component of the cross product of the vectors extended into 3D
-
- .. versionadded:: 1.1"""
- other = Vector2d(other)
- return self.x * other.y - self.y * other.x
-
- def is_close(
- self,
- other: Union[VectorLike, str, Tuple[float, float]],
- rtol: float = 1e-5,
- atol: float = 1e-8,
- ) -> float:
- """Checks if two vectors are (almost) identical, up to both absolute and
- relative tolerance."""
- other = Vector2d(other)
- delta = (self - other).length
- return delta < (atol + rtol * other.length)
-
- @property
- def length(self) -> float:
- """Returns the length of the vector"""
- return sqrt(self.dot(self))
-
- @property
- def angle(self):
- # type: () -> Optional[float]
- """The angle of the vector when represented in polar coordinates
-
- .. versionadded:: 1.1"""
- if self.x == 0 and self.y == 0:
- return None
- return atan2(self.y, self.x)
-
-
-class Vector2d(ImmutableVector2d):
- """Represents an element of 2-dimensional Euclidean space"""
-
- @staticmethod
- def from_polar(radius, theta):
- # type: (float, Optional[float]) -> Optional[Vector2d]
- """Creates a Vector2d from polar coordinates
-
- None is returned when theta is None and radius is not zero.
-
- .. versionadded:: 1.1
- """
- if radius == 0.0:
- return Vector2d(0.0, 0.0)
- if theta is not None:
- return Vector2d(radius * cos(theta), radius * sin(theta))
- # A vector with a radius but no direction is invalid
- return None
-
- @ImmutableVector2d.x.setter
- def x(self, value):
- # type: (Union[float, int, str]) -> None
- self._x = float(value)
-
- @ImmutableVector2d.y.setter
- def y(self, value):
- # type: (Union[float, int, str]) -> None
- self._y = float(value)
-
- def __iadd__(self, other):
- # type: (VectorLike) -> Vector2d
- other = Vector2d(other)
- self.x += other.x
- self.y += other.y
- return self
-
- def __isub__(self, other):
- # type: (VectorLike) -> Vector2d
- other = Vector2d(other)
- self.x -= other.x
- self.y -= other.y
- return self
-
- def __imul__(self, factor):
- # type: (float) -> Vector2d
- self.x *= factor
- self.y *= factor
- return self
-
- def __idiv__(self, factor):
- # type: (float) -> Vector2d
- self.x /= factor
- self.y /= factor
- return self
-
- def __itruediv__(self, factor):
- # type: (float) -> Vector2d
- self.x /= factor
- self.y /= factor
- return self
-
- def __ifloordiv__(self, factor):
- # type: (float) -> Vector2d
- self.x /= factor
- self.y /= factor
- return self
-
- @overload
- def assign(self, x, y):
- # type: (float, float) -> VectorLike
- pass
-
- @overload
- def assign(self, other):
- # type: (VectorLike, str) -> VectorLike
- pass
-
- def assign(self, *args):
- """Assigns a different vector in place"""
- self.x, self.y = Vector2d(*args)
- return self
-
-
-class Transform:
- """A transformation object which will always reduce to a matrix and can
- then be used in combination with other transformations for reducing
- finding a point and printing svg ready output.
-
- Use with svg transform attribute input:
-
- tr = Transform("scale(45, 32)")
-
- Use with triad matrix input (internal representation):
-
- tr = Transform(((1.0, 0.0, 0.0), (0.0, 1.0, 0.0)))
-
- Use with hexad matrix input (i.e. svg matrix(...)):
-
- tr = Transform((1.0, 0.0, 0.0, 1.0, 0.0, 0.0))
-
- Once you have a transformation you can operate tr * tr to compose,
- any of the above inputs are also valid operators for composing.
- """
-
- TRM = re.compile(r"(translate|scale|rotate|skewX|skewY|matrix)\s*\(([^)]*)\)\s*,?")
- absolute_tolerance = 1e-5 # type: float
-
- def __init__(
- self,
- matrix=None, # type: Optional[MatrixLike]
- callback=None, # type: Optional[Callable[[Transform], Transform]]
- **extra,
- ):
- # type: (...) -> None
- self.callback = None
- self.matrix = ((1.0, 0.0, 0.0), (0.0, 1.0, 0.0))
- if matrix is not None:
- self._set_matrix(matrix)
-
- self.add_kwargs(**extra)
- # Set callback last, so it doesn't kick off just setting up the internal value
- self.callback = callback
-
- def _set_matrix(self, matrix):
- # type: (MatrixLike) -> None
- """Parse a given string as an svg transformation instruction.
-
- .. versionadded:: 1.1"""
- if isinstance(matrix, str):
- for func, values in self.TRM.findall(matrix.strip()):
- getattr(self, "add_" + func.lower())(*strargs(values))
- elif isinstance(matrix, Transform):
- self.matrix = matrix.matrix
- elif isinstance(matrix, (tuple, list)) and len(matrix) == 2:
- row1 = matrix[0]
- row2 = matrix[1]
- if isinstance(row1, (tuple, list)) and isinstance(row2, (tuple, list)):
- if len(row1) == 3 and len(row2) == 3:
- row1 = cast(Tuple[float, float, float], tuple(map(float, row1)))
- row2 = cast(Tuple[float, float, float], tuple(map(float, row2)))
- self.matrix = row1, row2
- else:
- raise ValueError(
- f"Matrix '{matrix}' is not a valid transformation matrix"
- )
- else:
- raise ValueError(
- f"Matrix '{matrix}' is not a valid transformation matrix"
- )
- elif isinstance(matrix, (list, tuple)) and len(matrix) == 6:
- tmatrix = cast(
- Union[List[float], Tuple[float, float, float, float, float, float]],
- matrix,
- )
- row1 = (float(tmatrix[0]), float(tmatrix[2]), float(tmatrix[4]))
- row2 = (float(tmatrix[1]), float(tmatrix[3]), float(tmatrix[5]))
- self.matrix = row1, row2
- elif not isinstance(matrix, (list, tuple)):
- raise ValueError(f"Invalid transform type: {type(matrix).__name__}")
- else:
- raise ValueError(f"Matrix '{matrix}' is not a valid transformation matrix")
-
- # These provide quick access to the svg matrix:
- #
- # [ a, c, e ]
- # [ b, d, f ]
- #
- a = property(lambda self: self.matrix[0][0]) # pylint: disable=invalid-name
- b = property(lambda self: self.matrix[1][0]) # pylint: disable=invalid-name
- c = property(lambda self: self.matrix[0][1]) # pylint: disable=invalid-name
- d = property(lambda self: self.matrix[1][1]) # pylint: disable=invalid-name
- e = property(lambda self: self.matrix[0][2]) # pylint: disable=invalid-name
- f = property(lambda self: self.matrix[1][2]) # pylint: disable=invalid-name
-
- def __bool__(self):
- # type: () -> bool
- return not self.__eq__(Transform())
-
- __nonzero__ = __bool__
-
- @overload
- def add_matrix(self, a):
- # type: (MatrixLike) -> Transform
- pass
-
- @overload
- def add_matrix( # pylint: disable=too-many-arguments
- self, a: float, b: float, c: float, d: float, e: float, f: float
- ) -> Transform:
- pass
-
- @overload
- def add_matrix(self, a, b):
- # type: (Tuple[float, float, float], Tuple[float, float, float]) -> Transform
- pass
-
- def add_matrix(self, *args):
- """Add matrix in order they appear in the svg hexad"""
- if len(args) == 1:
- self.__imatmul__(Transform(args[0]))
- elif len(args) == 2 or len(args) == 6:
- self.__imatmul__(Transform(args))
- else:
- raise ValueError(f"Invalid number of arguments {args}")
- return self
-
- def add_kwargs(self, **kwargs):
- """Add translations, scales, rotations etc using key word arguments"""
- for key, value in reversed(list(kwargs.items())):
- func = getattr(self, "add_" + key)
- if isinstance(value, tuple):
- func(*value)
- elif value is not None:
- func(value)
- return self
-
- @overload
- def add_translate(self, dr):
- # type: (VectorLike) -> Transform
- pass
-
- @overload
- def add_translate(self, tr_x, tr_y=0.0):
- # type: (float, Optional[float]) -> Transform
- pass
-
- def add_translate(self, *args):
- """Add translate to this transformation"""
- if len(args) == 1 and isinstance(args[0], (int, float)):
- tr_x, tr_y = args[0], 0.0
- else:
- tr_x, tr_y = Vector2d(*args)
- self.__imatmul__(((1.0, 0.0, tr_x), (0.0, 1.0, tr_y)))
- return self
-
- def add_scale(self, sc_x, sc_y=None):
- """Add scale to this transformation"""
- sc_y = sc_x if sc_y is None else sc_y
- self.__imatmul__(((sc_x, 0.0, 0.0), (0.0, sc_y, 0.0)))
- return self
-
- @overload
- def add_rotate(self, deg, center):
- # type: (float, VectorLike) -> Transform
- pass
-
- @overload
- def add_rotate(self, deg, center_x, center_y):
- # type: (float, float, float) -> Transform
- pass
-
- @overload
- def add_rotate(self, deg):
- # type: (float) -> Transform
- pass
-
- @overload
- def add_rotate(self, deg, a):
- # type: (float, Union[VectorLike, str]) -> Transform
- pass
-
- @overload
- def add_rotate(self, deg, a, b):
- # type: (float, float, float) -> Transform
- pass
-
- def add_rotate(self, deg, *args):
- """Add rotation to this transformation"""
- center_x, center_y = Vector2d(*args)
- _cos, _sin = cos(radians(deg)), sin(radians(deg))
- self.__imatmul__(((_cos, -_sin, center_x), (_sin, _cos, center_y)))
- self.__imatmul__(((1.0, 0.0, -center_x), (0.0, 1.0, -center_y)))
- return self
-
- def add_skewx(self, deg):
- # type: (float) -> Transform
- """Add skew x to this transformation"""
- self.__imatmul__(((1.0, tan(radians(deg)), 0.0), (0.0, 1.0, 0.0)))
- return self
-
- def add_skewy(self, deg):
- # type: (float) -> Transform
- """Add skew y to this transformation"""
- self.__imatmul__(((1.0, 0.0, 0.0), (tan(radians(deg)), 1.0, 0.0)))
- return self
-
- def to_hexad(self):
- # type: () -> Iterator[float]
- """Returns the transform as a hexad matrix (used in svg)"""
- return (val for lst in zip(*self.matrix) for val in lst)
-
- def is_translate(self, exactly=False):
- # type: (bool) -> bool
- """Returns True if this transformation is ONLY translate"""
- tol = self.absolute_tolerance if not exactly else 0.0
- return (
- fabs(self.a - 1) <= tol
- and abs(self.d - 1) <= tol
- and fabs(self.b) <= tol
- and fabs(self.c) <= tol
- )
-
- def is_scale(self, exactly=False):
- # type: (bool) -> bool
- """Returns True if this transformation is ONLY scale"""
- tol = self.absolute_tolerance if not exactly else 0.0
- return (
- fabs(self.e) <= tol
- and fabs(self.f) <= tol
- and fabs(self.b) <= tol
- and fabs(self.c) <= tol
- )
-
- def is_rotate(self, exactly=False):
- # type: (bool) -> bool
- """Returns True if this transformation is ONLY rotate"""
- tol = self.absolute_tolerance if not exactly else 0.0
- return (
- self._is_URT(exactly=exactly)
- and fabs(self.e) <= tol
- and fabs(self.f) <= tol
- and fabs(self.a**2 + self.b**2 - 1) <= tol
- )
-
- def rotation_degrees(self):
- # type: () -> float
- """Return the amount of rotation in this transform"""
- if not self._is_URT(exactly=False):
- raise ValueError(
- "Rotation angle is undefined for non-uniformly scaled or skewed "
- "matrices"
- )
- return atan2(self.b, self.a) * 180 / pi
-
- def __str__(self):
- # type: () -> str
- """Format the given matrix into a string representation for svg"""
- hexad = tuple(self.to_hexad())
- if self.is_translate():
- if not self:
- return ""
- return f"translate({self.e:.6g}, {self.f:.6g})"
- if self.is_scale():
- return f"scale({self.a:.6g}, {self.d:.6g})"
- if self.is_rotate():
- return f"rotate({self.rotation_degrees():.6g})"
- return f"matrix({' '.join(f'{var:.6g}' for var in hexad)})"
-
- def __repr__(self) -> str:
- """String representation of this object"""
- return (
- f"{type(self).__name__}(("
- f"({', '.join(f'{var:.6g}' for var in self.matrix[0])}), "
- f"({', '.join(f'{var:.6g}' for var in self.matrix[1])})))"
- )
-
- def __eq__(self, matrix):
- # typing this requires writing a proof for mypy that matrix is really
- # MatrixLike
- """Test if this transformation is equal to the given matrix"""
- if isinstance(matrix, (str, tuple, list, Transform)):
- val = all(
- fabs(l - r) <= self.absolute_tolerance
- for l, r in zip(self.to_hexad(), Transform(matrix).to_hexad())
- )
- else:
- val = False
- return val
-
- def __matmul__(self, matrix):
- # type: (MatrixLike) -> Transform
- """Combine this transform's internal matrix with the given matrix"""
- # Conform the input to a known quantity (and convert if needed)
- other = Transform(matrix)
- # Return a transformation as the combined result
- return Transform(
- (
- self.a * other.a + self.c * other.b,
- self.b * other.a + self.d * other.b,
- self.a * other.c + self.c * other.d,
- self.b * other.c + self.d * other.d,
- self.a * other.e + self.c * other.f + self.e,
- self.b * other.e + self.d * other.f + self.f,
- )
- )
-
- def __imatmul__(self, matrix):
- # type: (MatrixLike) -> Transform
- """In place multiplication of transform matrices"""
- self.matrix = (self @ matrix).matrix
- if self.callback is not None:
- self.callback(self)
- return self
-
- def __neg__(self):
- # type: () -> Transform
- """Returns an inverted transformation"""
- det = (self.a * self.d) - (self.c * self.b)
- # invert the rotation/scaling part
- new_a = self.d / det
- new_d = self.a / det
- new_c = -self.c / det
- new_b = -self.b / det
- # invert the translational part
- new_e = -(new_a * self.e + new_c * self.f)
- new_f = -(new_b * self.e + new_d * self.f)
- return Transform((new_a, new_b, new_c, new_d, new_e, new_f))
-
- def apply_to_point(self, point):
- # type: (VectorLike) -> Vector2d
- """Transform a tuple (X, Y)"""
- if isinstance(point, str):
- raise ValueError(f"Will not transform string '{point}'")
- point = Vector2d(point)
- return Vector2d(
- self.a * point.x + self.c * point.y + self.e,
- self.b * point.x + self.d * point.y + self.f,
- )
-
- def _is_URT(self, exactly=False):
- # type: (bool) -> bool
- """
- Checks that transformation can be decomposed into product of
- Uniform scale (U), Rotation around origin (R) and translation (T)
-
- :return: decomposition as U*R*T is possible
- """
- tol = self.absolute_tolerance if not exactly else 0.0
- return (fabs(self.a - self.d) <= tol) and (fabs(self.b + self.c) <= tol)
-
- def interpolate(self, other, fraction):
- # type: (Transform, float) -> Transform
- """Interpolate with another Transform.
-
- .. versionadded:: 1.1
- """
- from .tween import TransformInterpolator
-
- return TransformInterpolator(self, other).interpolate(fraction)
-
-
-class BoundingInterval: # pylint: disable=too-few-public-methods
- """A pair of numbers that represent the minimum and maximum values."""
-
- @overload
- def __init__(self, other=None):
- # type: (Optional[BoundingInterval]) -> None
- pass
-
- @overload
- def __init__(self, pair):
- # type: (Tuple[float, float]) -> None
- pass
-
- @overload
- def __init__(self, value):
- # type: (float) -> None
- pass
-
- @overload
- def __init__(self, x, y):
- # type: (float, float) -> None
- pass
-
- def __init__(self, x=None, y=None):
- self.x: Union[int, float, Decimal]
- self.y: Union[int, float, Decimal]
- self.minimum: float
- self.maximum: float
- if y is not None:
- if isinstance(x, (int, float, Decimal)) and isinstance(
- y, (int, float, Decimal)
- ):
- self.minimum = float(x)
- self.maximum = float(y)
- else:
- raise ValueError(
- f"Not a number for scaling: {str((x, y))} "
- f"({type(x).__name__},{type(y).__name__})"
- )
-
- else:
- value = x
- if value is None:
- # identity for addition, zero for intersection
- self.minimum, self.maximum = float("+inf"), float("-inf")
- elif isinstance(value, BoundingInterval):
- self.minimum = value.minimum
- self.maximum = value.maximum
- elif isinstance(value, (tuple, list)) and len(value) == 2:
- self.minimum, self.maximum = min(value), max(value)
- elif isinstance(value, (int, float, Decimal)):
- self.minimum = self.maximum = float(value)
- else:
- raise ValueError(
- f"Not a number for scaling: {str(value)} ({type(value).__name__})"
- )
-
- def __bool__(self):
- # type: () -> bool
- return isfinite(self.minimum) and isfinite(self.maximum)
-
- __nonzero__ = __bool__
-
- def __neg__(self):
- # type: () -> BoundingInterval
- return BoundingInterval((-1 * self.maximum, -1 * self.minimum))
-
- def __add__(self, other):
- # type: (BoundingInterval) -> BoundingInterval
- """Calculate the bounding interval that covers both given bounding intervals"""
- new = BoundingInterval(self)
- if other is not None:
- new += other
- return new
-
- def __iadd__(self, other):
- # type: (BoundingInterval) -> BoundingInterval
- other = BoundingInterval(other)
- self.minimum = min((self.minimum, other.minimum))
- self.maximum = max((self.maximum, other.maximum))
- return self
-
- def __radd__(self, other):
- # type: (BoundingInterval) -> BoundingInterval
- if other is None:
- return BoundingInterval(self)
- return self + other
-
- def __and__(self, other: BoundingInterval) -> BoundingInterval:
- """Calculate the bounding interval where both given bounding intervals
- overlap"""
- new = BoundingInterval(self)
- if other is not None:
- new &= other
- return new
-
- def __iand__(self, other):
- # type: (BoundingInterval) -> BoundingInterval
- other = BoundingInterval(other)
- self.minimum = max((self.minimum, other.minimum))
- self.maximum = min((self.maximum, other.maximum))
- if self.minimum > self.maximum:
- self.minimum, self.maximum = float("+inf"), float("-inf")
- return self
-
- def __rand__(self, other):
- # type: (BoundingInterval) -> BoundingInterval
- if other is None:
- return BoundingInterval(self)
- return self & other
-
- def __mul__(self, other: float) -> BoundingInterval:
- new = BoundingInterval(self)
- if other is not None:
- new *= other
- return new
-
- def __imul__(self, other: float) -> BoundingInterval:
- self.minimum *= other
- self.maximum *= other
- return self
-
- def __iter__(self) -> Generator[float, None, None]:
- yield self.minimum
- yield self.maximum
-
- def __eq__(self, other) -> bool:
- return tuple(self) == tuple(BoundingInterval(other))
-
- def __contains__(self, value: float) -> bool:
- return self.minimum <= value <= self.maximum
-
- def __repr__(self) -> str:
- return f"BoundingInterval({self.minimum}, {self.maximum})"
-
- @property
- def center(self):
- # type: () -> float
- """Pick the middle of the line"""
- return self.minimum + ((self.maximum - self.minimum) / 2)
-
- @property
- def size(self):
- # type: () -> float
- """Return the size difference minimum and maximum"""
- return self.maximum - self.minimum
-
-
-class BoundingBox: # pylint: disable=too-few-public-methods
- """
- Some functions to compute a rough bbox of a given list of objects.
-
- BoundingBox(other)
- BoundingBox(x, y)
- BoundingBox((x1, x2), (y1, y2))
- """
-
- width = property(lambda self: self.x.size)
- height = property(lambda self: self.y.size)
- top = property(lambda self: self.y.minimum)
- left = property(lambda self: self.x.minimum)
- bottom = property(lambda self: self.y.maximum)
- right = property(lambda self: self.x.maximum)
- center_x = property(lambda self: self.x.center)
- center_y = property(lambda self: self.y.center)
- diagonal_length = property(
- lambda self: (self.width**2 + self.height**2) ** (0.5)
- )
-
- @overload
- def __init__(self, other=None):
- # type: (Optional[BoundingBox]) -> None
- pass
-
- @overload
- def __init__(self, x, y):
- # type: (BoundingIntervalArgs, BoundingIntervalArgs) -> None
- pass
-
- def __init__(self, x=None, y=None):
- if y is None:
- if x is None:
- # identity for addition, zero for intersection
- pass
- elif isinstance(x, BoundingBox):
- x, y = x.x, x.y
- else:
- raise ValueError(
- f"Not a number for scaling: {str(x)} ({type(x).__name__})"
- )
- self.x = BoundingInterval(x)
- self.y = BoundingInterval(y)
-
- @staticmethod
- def new_xywh(x: float, y: float, width: float, height: float) -> BoundingBox:
- """Create a bounding box using x, y, width and height
-
- .. versionadded:: 1.2"""
- return BoundingBox((x, x + width), (y, y + height))
-
- def __bool__(self):
- # type: () -> bool
- return bool(self.x) and bool(self.y)
-
- __nonzero__ = __bool__
-
- def __neg__(self):
- # type: () -> BoundingBox
- return BoundingBox(-self.x, -self.y)
-
- def __add__(self, other):
- # type: (Optional[BoundingBox]) -> BoundingBox
- """Calculate the bounding box that covers both given bounding boxes"""
- new = BoundingBox(self)
- new += BoundingBox(other)
- return new
-
- def __iadd__(self, other):
- # type: (Optional[BoundingBox]) -> BoundingBox
- other = BoundingBox(other)
- self.x += other.x
- self.y += other.y
- return self
-
- def __radd__(self, other):
- # type: (Optional[BoundingBox]) -> BoundingBox
- return self + other
-
- def __and__(self, other):
- # type: (Optional[BoundingBox]) -> BoundingBox
- """Calculate the bounding box where both given bounding boxes overlap"""
- new = BoundingBox(self)
- new &= BoundingBox(other)
- return new
-
- def __iand__(self, other: Optional[BoundingBox]) -> BoundingBox:
- other = BoundingBox(other)
- self.x = self.x & other.x
- self.y = self.y & other.y
- if not self.x or not self.y:
- self.x, self.y = BoundingInterval(), BoundingInterval()
- return self
-
- def __rand__(self, other):
- # type: (Optional[BoundingBox]) -> BoundingBox
- return self & other
-
- def __mul__(self, factor):
- # type: (float) -> BoundingBox
- new = BoundingBox(self)
- new *= factor
- return new
-
- def __imul__(self, factor):
- # type: (float) -> BoundingBox
- self.x *= factor
- self.y *= factor
- return self
-
- def __eq__(self, other):
- # type (object) -> bool
- if isinstance(other, BoundingBox):
- return tuple(self) == tuple(other)
- return False
-
- def __iter__(self) -> Generator[BoundingBox, None, None]:
- yield self.x
- yield self.y
-
- @property
- def area(self):
- """Return area of the bounding box
-
- .. versionadded:: 1.2"""
- return self.width * self.height
-
- @property
- def minimum(self):
- # type: () -> Vector2d
- """Return the minimum x,y coords"""
- return Vector2d(self.x.minimum, self.y.minimum)
-
- @property
- def maximum(self):
- # type: () -> Vector2d
- """Return the maximum x,y coords"""
- return Vector2d(self.x.maximum, self.y.maximum)
-
- def __repr__(self):
- # type: () -> str
- return f"BoundingBox({tuple(self.x)},{tuple(self.y)})"
-
- @property
- def center(self):
- # type: () -> Vector2d
- """Returns the middle of the bounding box"""
- return Vector2d(self.x.center, self.y.center)
-
- @property
- def size(self):
- """Returns a vector containing width and height of the bounding box
-
- .. versionadded:: 1.2"""
- return Vector2d(self.x.size, self.y.size)
-
- def get_anchor(self, xanchor, yanchor, direction=0, selbox=None):
- # type: (str, str, Union[int, str], Optional[BoundingBox]) -> float
- """Calls get_distance with the given anchor options"""
- return self.anchor_distance(
- getattr(self, XAN[xanchor]),
- getattr(self, YAN[yanchor]),
- direction=direction,
- selbox=selbox,
- )
-
- @staticmethod
- def anchor_distance(
- x: float,
- y: float,
- direction: Union[int, str] = 0,
- selbox: Optional[BoundingBox] = None,
- ) -> float:
- """Using the x,y returns a single sortable value based on direction and angle
-
- Args:
- x (float): input x coordinate
- y (float): input y coordinate
- direction (Union[int, str], optional): int/float (custom angle),
- tb/bt (top/bottom), lr/rl (left/right), ri/ro (radial). Defaults to 0.
- selbox (Optional[BoundingBox], optional): The bounding box of the whole
- selection for radial anchors. Defaults to None.
-
- Raises:
- ValueError: if radial distance is requested without the optional selbox
- parameter.
-
- Returns:
- float: the anchor distance with respect to the direction.
- """
-
- rot = 0.0
- if isinstance(direction, (int, float)): # Angle
- if direction not in CUSTOM_DIRECTION:
- return hypot(x, y) * (cos(radians(-direction) - atan2(y, x)))
- direction = CUSTOM_DIRECTION[direction]
-
- if direction in ("ro", "ri"):
- if selbox is None:
- raise ValueError(
- "Radial distance not available without selection bounding box"
- )
- rot = hypot(selbox.x.center - x, selbox.y.center - y)
-
- return [y, -y, x, -x, rot, -rot][DIRECTION.index(direction)]
-
- def resize(self, delta_x: float, delta_y: Optional[float] = None) -> BoundingBox:
- """Enlarges / shrinks a bounding box by a constant value. If only delta_x
- is given, each side is moved by the same amount; if delta_y is given,
- different deltas are applied to horizontal and vertical intervals.
-
- .. versionadded:: 1.2"""
- delta_y = delta_y or delta_x
- return BoundingBox(
- (self.x.minimum - delta_x, self.x.maximum + delta_x),
- (self.y.minimum - delta_y, self.y.maximum + delta_y),
- )
-
-
-class DirectedLineSegment:
- """
- A directed line segment
-
- DirectedLineSegment(((x0, y0), (x1, y1)))
- """
-
- start = Vector2d() # start point of segment
- end = Vector2d() # end point of segment
-
- x0 = property(lambda self: self.start.x) # pylint: disable=invalid-name
- y0 = property(lambda self: self.start.y) # pylint: disable=invalid-name
- x1 = property(lambda self: self.end.x)
- y1 = property(lambda self: self.end.y)
- dx = property(lambda self: self.vector.x) # pylint: disable=invalid-name
- dy = property(lambda self: self.vector.y) # pylint: disable=invalid-name
-
- @overload
- def __init__(self):
- # type: () -> None
- pass
-
- @overload
- def __init__(self, other):
- # type: (DirectedLineSegment) -> None
- pass
-
- @overload
- def __init__(self, start, end):
- # type: (VectorLike, VectorLike) -> None
- pass
-
- def __init__(self, *args):
- if not args: # overload 0
- start, end = Vector2d(), Vector2d()
- elif len(args) == 1: # overload 1
- (other,) = args
- start, end = other.start, other.end
- elif len(args) == 2: # overload 2
- start, end = args
- else:
- raise ValueError(f"DirectedLineSegment() can't be constructed from {args}")
-
- self.start = Vector2d(start)
- self.end = Vector2d(end)
-
- def __eq__(self, other):
- # type: (object) -> bool
- if isinstance(other, (tuple, DirectedLineSegment)):
- return tuple(self) == tuple(other)
- return False
-
- def __iter__(self):
- # type: () -> Generator[DirectedLineSegment, None, None]
- yield self.x0
- yield self.x1
- yield self.y0
- yield self.y1
-
- @property
- def vector(self):
- # type: () -> Vector2d
- """The vector of the directed line segment.
-
- The vector of the directed line segment represents the length
- and direction of segment, but not the starting point.
-
- .. versionadded:: 1.1
- """
- return self.end - self.start
-
- @property
- def length(self):
- # type: () -> float
- """Get the length of the line segment"""
- return self.vector.length
-
- @property
- def angle(self):
- # type: () -> float
- """Get the angle of the line created by this segment"""
- return atan2(self.dy, self.dx)
-
- def distance_to_point(self, x, y):
- # type: (float, float) -> Union[DirectedLineSegment, Optional[float]]
- """Get the distance to the given point (x, y)"""
- segment2 = DirectedLineSegment(self.start, (x, y))
- dot2 = segment2.dot(self)
- if dot2 <= 0:
- return DirectedLineSegment((x, y), self.start).length
- if self.dot(self) <= dot2:
- return DirectedLineSegment((x, y), self.end).length
- return self.perp_distance(x, y)
-
- def perp_distance(self, x, y):
- # type: (float, float) -> Optional[float]
- """Perpendicular distance to the given point"""
- if self.length == 0:
- return None
- return fabs((self.dx * (self.y0 - y)) - ((self.x0 - x) * self.dy)) / self.length
-
- def dot(self, other):
- # type: (DirectedLineSegment) -> float
- """Get the dot product with the segment with another"""
- return self.vector.dot(other.vector)
-
- def point_at_ratio(self, ratio):
- # type: (float) -> Tuple[float, float]
- """Get the point at the given ratio along the line"""
- return self.x0 + ratio * self.dx, self.y0 + ratio * self.dy
-
- def point_at_length(self, length):
- # type: (float) -> Tuple[float, float]
- """Get the point as the length along the line"""
- return self.point_at_ratio(length / self.length)
-
- def parallel(self, x, y):
- # type: (float, float) -> DirectedLineSegment
- """Create parallel Segment"""
- return DirectedLineSegment((x + self.dx, y + self.dy), (x, y))
-
- def intersect(self, other):
- # type: (DirectedLineSegment) -> Optional[Vector2d]
- """Get the intersection between two segments"""
- other = DirectedLineSegment(other)
- denom = self.vector.cross(other.vector)
- num = other.vector.cross(self.start - other.start)
-
- if denom != 0:
- return Vector2d(self.point_at_ratio(num / denom))
- return None
-
- def __repr__(self):
- # type: () -> str
- return f"DirectedLineSegment(({self.start}), ({self.end}))"
-
-
-def cubic_extrema(py0, py1, py2, py3):
- # type: (float, float, float, float) -> Tuple[float, float]
- """Returns the extreme value, given a set of bezier coordinates"""
-
- atol = 1e-9
- cmin, cmax = min(py0, py3), max(py0, py3)
- pd1 = py1 - py0
- pd2 = py2 - py1
- pd3 = py3 - py2
-
- def _is_bigger(point):
- if 0 < point < 1:
- pyx = (
- py0 * (1 - point) * (1 - point) * (1 - point)
- + 3 * py1 * point * (1 - point) * (1 - point)
- + 3 * py2 * point * point * (1 - point)
- + py3 * point * point * point
- )
- return min(cmin, pyx), max(cmax, pyx)
- return cmin, cmax
-
- if fabs(pd1 - 2 * pd2 + pd3) > atol:
- if pd2 * pd2 > pd1 * pd3:
- pds = sqrt(pd2 * pd2 - pd1 * pd3)
- cmin, cmax = _is_bigger((pd1 - pd2 + pds) / (pd1 - 2 * pd2 + pd3))
- cmin, cmax = _is_bigger((pd1 - pd2 - pds) / (pd1 - 2 * pd2 + pd3))
-
- elif fabs(pd2 - pd1) > atol:
- cmin, cmax = _is_bigger(-pd1 / (2 * (pd2 - pd1)))
-
- return cmin, cmax
-
-
-def quadratic_extrema(py0, py1, py2):
- # type: (float, float, float) -> Tuple[float, float]
- """Returns the extreme value, given a set of quadratic bezier coordinates"""
- atol = 1e-9
- cmin, cmax = min(py0, py2), max(py0, py2)
-
- def _is_bigger(point):
- if 0 < point < 1:
- pyx = (
- py0 * (1 - point) * (1 - point)
- + 2 * py1 * point * (1 - point)
- + py2 * point * point
- )
- return min(cmin, pyx), max(cmax, pyx)
- return cmin, cmax
-
- if fabs(py0 + py2 - 2 * py1) > atol:
- cmin, cmax = _is_bigger((py0 - py1) / (py0 + py2 - 2 * py1))
-
- return cmin, cmax
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/turtle.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/turtle.py
deleted file mode 100644
index 40c9b60..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/turtle.py
+++ /dev/null
@@ -1,255 +0,0 @@
-# coding=utf-8
-#
-# Copyright (C) 2005 Aaron Spike, aaron@ekips.org
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-"""A Python path turtle for Inkscape extensions"""
-
-import math
-import random
-from typing import List, Union
-
-from .paths import Line, Move, Path, PathCommand
-from .elements import PathElement, Group, BaseElement
-from .styles import Style
-
-
-class PathTurtle:
- """A Python path turtle
-
- .. versionchanged:: 1.2
- pTurtle has been renamed to PathTurtle."""
-
- def __init__(self, home=(0, 0)):
- self.__home = [home[0], home[1]]
- self.__pos = self.__home[:]
- self.__heading = -90
- self.__path = ""
- self.__draw = True
- self.__new = True
-
- def forward(self, mag: float):
- """Move turtle forward by mag in the current direction."""
- self.setpos(
- (
- self.__pos[0] + math.cos(math.radians(self.__heading)) * mag,
- self.__pos[1] + math.sin(math.radians(self.__heading)) * mag,
- )
- )
-
- def backward(self, mag):
- """Move turtle backward by mag in the current direction."""
- self.setpos(
- (
- self.__pos[0] - math.cos(math.radians(self.__heading)) * mag,
- self.__pos[1] - math.sin(math.radians(self.__heading)) * mag,
- )
- )
-
- def right(self, deg):
- """Rotate turtle right by deg degrees.
-
- Changed in inkex 1.2: The turtle now rotates right (previously left) when
- calling this method."""
- self.__heading += deg
-
- def left(self, deg):
- """Rotate turtle left by deg degrees.
-
- Changed in inkex 1.2: The turtle now rotates left (previously right) when
- calling this method."""
- self.__heading -= deg
-
- def penup(self):
- """Enable non-drawing / moving mode"""
- self.__draw = False
- self.__new = False
-
- def pendown(self):
- """Enable drawing mode"""
- if not self.__draw:
- self.__new = True
- self.__draw = True
-
- def pentoggle(self):
- """Switch between drawing and moving mode"""
- if self.__draw:
- self.penup()
- else:
- self.pendown()
-
- def home(self):
- """Move to home position"""
- self.setpos(self.__home)
-
- def clean(self):
- """Delete current path"""
- self.__path = ""
-
- def clear(self):
- """Delete current path and move to home"""
- self.clean()
- self.home()
-
- def setpos(self, arg):
- """Move/draw to position, depending on the current state"""
- if self.__new:
- self.__path += "M" + ",".join([str(i) for i in self.__pos])
- self.__new = False
- self.__pos = arg
- if self.__draw:
- self.__path += "L" + ",".join([str(i) for i in self.__pos])
-
- def getpos(self):
- """Returns the current position"""
- return self.__pos[:]
-
- def setheading(self, deg):
- """Set the heading to deg degrees"""
- self.__heading = deg
-
- def getheading(self):
- """Returns the heading in degrees"""
- return self.__heading
-
- def sethome(self, arg):
- """Set home position"""
- self.__home = list(arg)
-
- def getPath(self):
- """Returns the current path"""
- return self.__path
-
- def rtree(self, size, minimum, pt=False):
- """Generates a random tree"""
- if size < minimum:
- return
- self.fd(size)
- turn = random.uniform(20, 40)
- self.rt(turn)
- self.rtree(size * random.uniform(0.5, 0.9), minimum, pt)
- self.lt(turn)
- turn = random.uniform(20, 40)
- self.lt(turn)
- self.rtree(size * random.uniform(0.5, 0.9), minimum, pt)
- self.rt(turn)
- if pt:
- self.pu()
- self.bk(size)
- if pt:
- self.pd()
-
- # pylint: disable=invalid-name
- fd = forward
- bk = backward
- rt = right
- lt = left
- pu = penup
- pd = pendown
-
-
-pTurtle = PathTurtle # should be deprecated
-
-
-class PathBuilder:
- """This helper class can be used to construct a path and insert it into a
- document.
-
- .. versionadded:: 1.2"""
-
- def __init__(self, style: Style):
- """Initializes a PathDrawHelper object
-
- Args:
- style (Style): Style of the path.
- """
- self.current = Path()
- self.style = style
-
- def add(self, command: Union[PathCommand, List[PathCommand]]):
- """Add a Path command to the Helper
-
- Args:
- command (Union[PathCommand, List[PathCommand]]): A (list of) PathCommand(s)
- to be appended.
- """
- self.current.append(command)
-
- def terminate(self):
- """Terminates current subpath. This method does nothing by default and is
- supposed to be overridden in subclasses."""
-
- def append_next(self, sibling_before: BaseElement):
- """Insert the resulting Path as :class:`inkex.elements._polygons.PathElement`
- into the document tree.
-
- Args:
- sibling_before (BaseElement): The element the resulting path will be
- appended after.
- """
- pth = PathElement()
- pth.path = self.current
- pth.style = self.style
- sibling_before.addnext(pth)
-
- def Move_to(self, x, y): # pylint: disable=invalid-name
- """Shorthand to insert an absolute move command: `M x y`.
-
- Args:
- x (Float): x coordinate to move to
- y (Float): y coordinate to move to
- """
- self.add(Move(x, y))
-
- def Line_to(self, x, y): # pylint: disable=invalid-name
- """Shorthand to insert an absolute lineto command: `L x y`.
-
- Args:
- x (Float): x coordinate to draw a line to
- y (Float): y coordinate to draw a line to
- """
- self.add(Line(x, y))
-
-
-class PathGroupBuilder(PathBuilder):
- """This helper class can be used to construct a group of paths that all have the
- same style.
-
- .. versionadded:: 1.2"""
-
- def __init__(self, style):
- super().__init__(style)
- self.result = Group()
-
- def terminate(self):
- """Terminates the current Path, and appends it to the group if it is not
- empty."""
- if len(self.current) > 1:
- pth = PathElement()
- pth.path = self.current.to_absolute()
- pth.style = self.style
- self.result.append(pth)
- self.current = Path()
-
- def append_next(self, sibling_before: BaseElement):
- """Insert the resulting Path as :class:`inkex.elements._groups.Group` into the
- document tree.
-
- Args:
- sibling_before (BaseElement): The element the resulting group will be
- appended after.
- """
- sibling_before.addnext(self.result)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tween.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tween.py
deleted file mode 100644
index c5dca76..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/tween.py
+++ /dev/null
@@ -1,847 +0,0 @@
-# coding=utf-8
-#
-# Copyright (C) 2005 Aaron Spike, aaron@ekips.org
-# 2020 Jonathan Neuhauser, jonathan.neuhauser@outlook.com
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-"""Module for interpolating attributes and styles
-
-.. versionchanged:: 1.2
- Rewritten in inkex 1.2 in an object-oriented structure to support more attributes.
-"""
-from bisect import bisect_left
-import abc
-import copy
-
-from .styles import Style
-from .elements._filters import LinearGradient, RadialGradient, Stop
-from .transforms import Transform
-from .colors import Color
-from .units import convert_unit, parse_unit, render_unit
-from .bezier import bezlenapprx, cspbezsplit, cspbezsplitatlength, csplength
-from .paths import Path, CubicSuperPath
-from .elements import SvgDocumentElement
-from .utils import FragmentError
-
-
-try:
- from typing import Tuple, TypeVar
-
- Value = TypeVar("Value")
- Number = TypeVar("Number", int, float)
-except ImportError:
- pass
-
-
-def interpcoord(coord_a: Number, coord_b: Number, time: float):
- """Interpolate single coordinate by the amount of time"""
- return ValueInterpolator(coord_a, coord_b).interpolate(time)
-
-
-def interppoints(point1, point2, time):
- # type: (Tuple[float, float], Tuple[float, float], float) -> Tuple[float, float]
- """Interpolate coordinate points by amount of time"""
- return ArrayInterpolator(point1, point2).interpolate(time)
-
-
-class AttributeInterpolator(abc.ABC):
- """Interpolate between attributes"""
-
- def __init__(self, start_value, end_value):
- self.start_value = start_value
- self.end_value = end_value
-
- @staticmethod
- def best_style(node):
- """Gets the best possible approximation to a node's style. For nodes inside the
- element tree of an SVG file, stylesheets defined in the defs of that file can be
- taken into account. This should be the case for input elements, but is not
- required - in that case, only the local inline style is used.
-
- During the interpolation process, some nodes are created temporarily, such as
- plain gradients of a single color to allow solid<->gradient interpolation. These
- are not attached to the document tree and therefore have no root. Since the only
- style relevant for them is the inline style, it is acceptable to fallback to it.
-
- Args:
- node (BaseElement): The node to get the best approximated style of
-
- Returns:
- Style: If the node is rooted, the CSS specified style. Else, the inline
- style."""
- try:
- return node.specified_style()
- except FragmentError:
- return node.style
-
- @staticmethod
- def create_from_attribute(snode, enode, attribute, method=None):
- """Creates an interpolator for an attribute. Currently, only path, transform and
- style attributes are supported
-
- Args:
- snode (BaseElement): start element
- enode (BaseElement): end element
- attribute (str): attribute name (for styles, starting with "style/")
- method (AttributeInterpolator, optional): (currently only used for paths).
- Specifies a method used to interpolate the attribute. Defaults to None.
-
- Raises:
- ValueError: if an attribute is passed that is not a style, path or transform
- attribute
-
- Returns:
- AttributeInterpolator: an interpolator whose type depends on attribute.
- """
- if attribute in Style.color_props:
- return StyleInterpolator.create_from_fill_stroke(snode, enode, attribute)
- if attribute == "d":
- if method is None:
- method = FirstNodesInterpolator
- return method(snode.path, enode.path)
- if attribute == "style":
- return StyleInterpolator(snode, enode)
- if attribute.startswith("style/"):
- return StyleInterpolator.create(snode, enode, attribute[6:])
- if attribute == "transform":
- return TransformInterpolator(snode.transform, enode.transform)
- if method is not None:
- return method(snode.get(attribute), enode.get(attribute))
- raise ValueError("only path and style attributes are supported")
-
- @abc.abstractmethod
- def interpolate(self, time=0):
- """Interpolation method, needs to be implemented by subclasses"""
- return
-
-
-class StyleInterpolator(AttributeInterpolator):
- """Class to interpolate styles"""
-
- def __init__(self, start_value, end_value):
- super().__init__(start_value, end_value)
- self.interpolators = {}
- # some keys are always processed in a certain order, these provide alternative
- # interpolation routes if e.g. Color<->none is interpolated
- all_keys = list(
- dict.fromkeys(
- ["fill", "stroke", "fill-opacity", "stroke-opacity", "stroke-width"]
- + list(self.best_style(start_value).keys())
- + list(self.best_style(end_value).keys())
- )
- )
- for attr in all_keys:
- sstyle = self.best_style(start_value)
- estyle = self.best_style(end_value)
- if attr not in sstyle and attr not in estyle:
- continue
- try:
- interp = StyleInterpolator.create(
- self.start_value, self.end_value, attr
- )
- self.interpolators[attr] = interp
- except ValueError:
- # no interpolation method known for this attribute
- pass
-
- @staticmethod
- def create(snode, enode, attribute):
- """Creates an Interpolator for a given style attribute, depending on its type:
-
- - Color properties (such as fill, stroke) -> :class:`ColorInterpolator`,
- :class:`GradientInterpolator` ect.
- - Unit properties -> :class:`UnitValueInterpolator`
- - other properties -> :class:`ValueInterpolator`
-
- Args:
- snode (BaseElement): start element
- enode (BaseElement): end element
- attribute (str): attribute to interpolate
-
- Raises:
- ValueError: if the attribute is not in any of the lists
-
- Returns:
- AttributeInterpolator: an interpolator object whose type depends on the
- attribute.
- """
- if attribute in Style.color_props:
- return StyleInterpolator.create_from_fill_stroke(snode, enode, attribute)
-
- if attribute in Style.unit_props:
- return UnitValueInterpolator(
- AttributeInterpolator.best_style(snode)(attribute),
- AttributeInterpolator.best_style(enode)(attribute),
- )
-
- if attribute in Style.opacity_props:
- return ValueInterpolator(
- AttributeInterpolator.best_style(snode)(attribute),
- AttributeInterpolator.best_style(enode)(attribute),
- )
-
- raise ValueError("Unknown attribute")
-
- @staticmethod
- def create_from_fill_stroke(snode, enode, attribute):
- """Creates an Interpolator for a given color-like attribute
-
- Args:
- snode (BaseElement): start element
- enode (BaseElement): end element
- attribute (str): attribute to interpolate
-
- Raises:
- ValueError: if the attribute is not color-like
- ValueError: if the attribute is unset on both start and end style
-
- Returns:
- AttributeInterpolator: an interpolator object whose type depends on the
- attribute.
- """
- if attribute not in Style.color_props:
- raise ValueError("attribute must be a color property")
-
- sstyle = AttributeInterpolator.best_style(snode)
- estyle = AttributeInterpolator.best_style(enode)
-
- styles = [[snode, sstyle], [enode, estyle]]
- for cur, curstyle in styles:
- if curstyle(attribute) is None:
- cur.style[attribute + "-opacity"] = 0.0
- if attribute == "stroke":
- cur.style["stroke-width"] = 0.0
-
- # check if style is none, unset or a color
- if isinstance(
- sstyle(attribute), (LinearGradient, RadialGradient)
- ) or isinstance(estyle(attribute), (LinearGradient, RadialGradient)):
- # if one of the two styles is a gradient, use gradient interpolation.
- try:
- return GradientInterpolator.create(snode, enode, attribute)
- except ValueError:
- # different gradient types, just duplicate the first
- return TrivialInterpolator(sstyle(attribute))
- if sstyle(attribute) is None and estyle(attribute) is None:
- return TrivialInterpolator("none")
- return ColorInterpolator.create(sstyle, estyle, attribute)
-
- def interpolate(self, time=0):
- """Interpolates a style using the interpolators set in self.interpolators
-
- Args:
- time (int, optional): Interpolation position. If 0, start_value is returned,
- if 1, end_value is returned. Defaults to 0.
-
- Returns:
- inkex.Style: interpolated style
- """
- style = Style()
- for prop, interp in self.interpolators.items():
- style[prop] = interp.interpolate(time)
- return style
-
-
-class TrivialInterpolator(AttributeInterpolator):
- """Trivial interpolator, returns value for every time"""
-
- def __init__(self, value):
- super().__init__(value, value)
-
- def interpolate(self, time=0):
- return self.start_value
-
-
-class ValueInterpolator(AttributeInterpolator):
- """Class for interpolation of a single value"""
-
- def __init__(self, start_value=0, end_value=0):
- super().__init__(float(start_value), float(end_value))
-
- def interpolate(self, time=0):
- """(Linearly) interpolates a value
-
- Args:
- time (int, optional): Interpolation position. If 0, start_value is returned,
- if 1, end_value is returned. Defaults to 0.
-
- Returns:
- int: interpolated value
- """
- return self.start_value + ((self.end_value - self.start_value) * time)
-
-
-class UnitValueInterpolator(ValueInterpolator):
- """Class for interpolation of a value with unit"""
-
- def __init__(self, start_value=0, end_value=0):
- start_val, start_unit = parse_unit(start_value)
- end_val = convert_unit(end_value, start_unit)
- super().__init__(start_val, end_val)
- self.unit = start_unit
-
- def interpolate(self, time=0):
- return render_unit(super().interpolate(time), self.unit)
-
-
-class ArrayInterpolator(AttributeInterpolator):
- """Interpolates array-like objects element-wise, e.g. color, transform,
- coordinate"""
-
- def __init__(self, start_value, end_value):
- super().__init__(start_value, end_value)
- self.interpolators = [
- ValueInterpolator(cur, other)
- for (cur, other) in zip(start_value, end_value)
- ]
-
- def interpolate(self, time=0):
- """Interpolates an array element-wise
-
- Args:
- time (int, optional): [description]. Defaults to 0.
-
- Returns:
- List: interpolated array
- """
- return [interp.interpolate(time) for interp in self.interpolators]
-
-
-class TransformInterpolator(ArrayInterpolator):
- """Class for interpolation of transforms"""
-
- def __init__(self, start_value=Transform(), end_value=Transform()):
- """Creates a transform interpolator.
-
- Args:
- start_value (inkex.Transform, optional): start transform. Defaults to
- inkex.Transform().
- end_value (inkex.Transform, optional): end transform. Defaults to
- inkex.Transform().
- """
- super().__init__(start_value.to_hexad(), end_value.to_hexad())
-
- def interpolate(self, time=0):
- """Interpolates a transform by interpolating each item in the transform hexad
- separately.
-
- Args:
- time (int, optional): Interpolation position. If 0, start_value is returned,
- if 1, end_value is returned. Defaults to 0.
-
- Returns:
- Transform: interpolated transform
- """
- return Transform(super().interpolate(time))
-
-
-class ColorInterpolator(ArrayInterpolator):
- """Class for color interpolation"""
-
- @staticmethod
- def create(sst, est, attribute):
- """Creates a ColorInterpolator for either Fill or stroke, depending on the
- attribute.
-
- Args:
- sst (Style): Start style
- est (Style): End style
- attribute (string): either fill or stroke
-
- Raises:
- ValueError: if none of the start or end style is a color.
-
- Returns:
- ColorInterpolator: A ColorInterpolator object
- """
- styles = [sst, est]
- for cur, other in zip(styles, reversed(styles)):
- if not isinstance(cur(attribute), Color) or cur(attribute) is None:
- cur[attribute] = other(attribute)
- this = ColorInterpolator(
- Color(styles[0](attribute)), Color(styles[1](attribute))
- )
- if this is None:
- raise ValueError("One of the two attribute needs to be a plain color")
- return this
-
- def __init__(self, start_value=Color("#000000"), end_value=Color("#000000")):
- super().__init__(start_value, end_value)
-
- def interpolate(self, time=0):
- """Interpolates a color by interpolating its r, g, b, a channels separately.
-
- Args:
- time (int, optional): Interpolation position. If 0, start_value is returned,
- if 1, end_value is returned. Defaults to 0.
-
- Returns:
- Color: interpolated color
- """
- return Color(list(map(int, super().interpolate(time))))
-
-
-class GradientInterpolator(AttributeInterpolator):
- """Base class for Gradient Interpolation"""
-
- def __init__(self, start_value, end_value, svg=None):
- super().__init__(start_value, end_value)
- self.svg = svg
- # If one of the styles is empty, set it to the gradient of the other
- if start_value is None:
- self.start_value = end_value
- if end_value is None:
- self.end_value = start_value
- self.transform_interpolator = TransformInterpolator(
- self.start_value.gradientTransform, self.end_value.gradientTransform
- )
- self.orientation_interpolator = {
- attr: UnitValueInterpolator(
- self.start_value.get(attr), self.end_value.get(attr)
- )
- for attr in self.start_value.orientation_attributes
- if self.start_value.get(attr) is not None
- and self.end_value.get(attr) is not None
- }
- if not (
- self.start_value.href is not None
- and self.start_value.href is self.end_value.href
- ):
- # the gradient link to different stops, interpolate between them
- # add both start and end offsets, then take distict
- newoffsets = sorted(
- list(set(self.start_value.stop_offsets + self.end_value.stop_offsets))
- )
-
- def func(start, end, time):
- return StopInterpolator(start, end).interpolate(time)
-
- sstops = GradientInterpolator.interpolate_linear_list(
- self.start_value.stop_offsets,
- list(self.start_value.stops),
- newoffsets,
- func,
- )
- ostops = GradientInterpolator.interpolate_linear_list(
- self.end_value.stop_offsets,
- list(self.end_value.stops),
- newoffsets,
- func,
- )
- self.newstop_interpolator = [
- StopInterpolator(s1, s2) for s1, s2 in zip(sstops, ostops)
- ]
- else:
- self.newstop_interpolator = None
-
- @staticmethod
- def create(snode, enode, attribute):
- """Creates a `GradientInterpolator` for either fill or stroke, depending on
- attribute.
-
- Cases: (A, B) -> Interpolator
-
- - Linear Gradient, Linear Gradient -> LinearGradientInterpolator
- - Color or None, Linear Gradient -> LinearGradientInterpolator
- - Radial Gradient, Radial Gradient -> RadialGradientInterpolator
- - Color or None, Radial Gradient -> RadialGradientInterpolator
- - Radial Gradient, Linear Gradient -> ValueError
- - Color or None, Color or None -> ValueError
-
- Args:
- snode (BaseElement): start element
- enode (BaseElement): end element
- attribute (string): either fill or stroke
-
- Raises:
- ValueError: if none of the styles are a gradient or if they are gradients
- of different types
-
- Returns:
- GradientInterpolator: an Interpolator object
- """
- interpolator = None
- gradienttype = None
- # first find out which type of interpolator we need
- sstyle = AttributeInterpolator.best_style(snode)
- estyle = AttributeInterpolator.best_style(enode)
- for cur in [sstyle, estyle]:
- curgrad = None
- if isinstance(cur(attribute), (LinearGradient, RadialGradient)):
- curgrad = cur(attribute)
- for gradtype, interp in [
- [LinearGradient, LinearGradientInterpolator],
- [RadialGradient, RadialGradientInterpolator],
- ]:
- if curgrad is not None and isinstance(curgrad, gradtype):
- if interpolator is None:
- interpolator = interp
- gradienttype = gradtype
- if not (interp == interpolator):
- raise ValueError("Gradient types don't match")
- # If one of the styles is empty, set it to the gradient of the other, but with
- # zero opacity (and stroke-width for strokes)
- # If one of the styles is a plain color, replace it by a gradient with a single
- # stop
- iterator = [[snode, gradienttype(), enode], [enode, gradienttype(), snode]]
- for index in [0, 1]:
- curstyle = AttributeInterpolator.best_style(iterator[index][0])
- value = curstyle(attribute)
- if value is None:
- # if the attribute of one of the two ends is unset, set the opacity to
- # zero.
- iterator[index][0].style[attribute + "-opacity"] = 0.0
- if attribute == "stroke":
- iterator[index][0].style["stroke-width"] = 0.0
- if isinstance(value, Color):
- # if the attribute of one of the two ends is a color, convert it to a
- # one-stop gradient. Type depends on the type of the other gradient.
- interpolator.initialize_position(
- iterator[index][1], iterator[index][0].bounding_box()
- )
- stop = Stop()
- stop.style = Style()
- stop.style["stop-color"] = value
- stop.offset = 0
- iterator[index][1].add(stop)
- stop = Stop()
- stop.style = Style()
- stop.style["stop-color"] = value
- stop.offset = 1
- iterator[index][1].add(stop)
- else:
- iterator[index][1] = value # is a gradient
- if interpolator is None:
- raise ValueError("None of the two styles is a gradient")
- if interpolator in [LinearGradientInterpolator, RadialGradientInterpolator]:
- return interpolator(iterator[0][1], iterator[1][1], snode)
- return interpolator(iterator[0][1], iterator[1][1])
-
- @staticmethod
- def interpolate_linear_list(positions, values, newpositions, func):
- """Interpolates a list of values given at n positions to the best approximation
- at m newpositions.
-
- >>>
- |
- | x
- | x
- _________________
- pq q p q
- (x denotes function values, p: positions, q: newpositions)
- A function may be given to interpolate between given values.
-
- Args:
- positions (list[number-like]): position of current function values
- values (list[Type]): list of arbitrary type,
- ``len(values) == len(positions)``
- newpositions (list[number-like]): position of interpolated values
- func (Callable[[Type, Type, float], Type]): Function to interpolate between
- values
-
- Returns:
- list[Type]: interpolated function values at positions
- """
- newvalues = []
- positions = list(map(float, positions))
- newpositions = list(map(float, newpositions))
- for pos in newpositions:
- if len(positions) == 1:
- newvalues.append(values[0])
- else:
- # current run:
- # idxl pos idxr
- # p p | p
- # q q
- idxl = max(0, bisect_left(positions, pos) - 1)
- idxr = min(len(positions) - 1, idxl + 1)
- fraction = (pos - positions[idxl]) / (positions[idxr] - positions[idxl])
- vall = values[idxl]
- valr = values[idxr]
- newval = func(vall, valr, fraction)
- newvalues.append(newval)
- return newvalues
-
- @staticmethod
- def append_to_doc(element, gradient):
- """Splits a gradient into stops and orientation, appends it to the document's
- defs and returns the href to the orientation gradient.
-
- Args:
- element (BaseElement): an element inside the SVG that the gradient should be
- added to
- gradient (Gradient): the gradient to append to the document
-
- Returns:
- Gradient: the orientation gradient, or the gradient object if
- element has no root or is None
- """
- stops, orientation = gradient.stops_and_orientation()
- if element is None or (
- element.getparent() is None and not isinstance(element, SvgDocumentElement)
- ):
- return gradient
- element.root.defs.add(orientation)
- if len(stops) > 0:
- element.root.defs.add(stops, orientation)
- orientation.href = stops.get_id()
- return orientation
-
- def interpolate(self, time=0):
- """Interpolate with another gradient."""
- newgrad = self.start_value.copy()
- # interpolate transforms
- newgrad.gradientTransform = self.transform_interpolator.interpolate(time)
-
- # interpolate orientation
- for attr in self.orientation_interpolator.keys():
- newgrad.set(attr, self.orientation_interpolator[attr].interpolate(time))
-
- # interpolate stops
- if self.newstop_interpolator is not None:
- newgrad.remove_all(Stop)
- newgrad.add(
- *[interp.interpolate(time) for interp in self.newstop_interpolator]
- )
- if self.svg is None:
- return newgrad
- return GradientInterpolator.append_to_doc(self.svg, newgrad)
-
-
-class LinearGradientInterpolator(GradientInterpolator):
- """Class for interpolation of linear gradients"""
-
- def __init__(
- self, start_value=LinearGradient(), end_value=LinearGradient(), svg=None
- ):
- super().__init__(start_value, end_value, svg)
-
- @staticmethod
- def initialize_position(grad, bbox):
- """Initializes a linear gradient's position"""
- grad.set("x1", bbox.left)
- grad.set("x2", bbox.right)
- grad.set("y1", bbox.center.y)
- grad.set("y2", bbox.center.y)
-
-
-class RadialGradientInterpolator(GradientInterpolator):
- """Class to interpolate radial gradients"""
-
- def __init__(
- self, start_value=RadialGradient(), end_value=RadialGradient(), svg=None
- ):
- super().__init__(start_value, end_value, svg)
-
- @staticmethod
- def initialize_position(grad, bbox):
- """Initializes a radial gradient's position"""
- x, y = bbox.center
- grad.set("cx", x)
- grad.set("cy", y)
- grad.set("fx", x)
- grad.set("fy", y)
- grad.set("r", bbox.right - bbox.center.x)
-
-
-class StopInterpolator(AttributeInterpolator):
- """Class to interpolate gradient stops"""
-
- def __init__(self, start_value, end_value):
- super().__init__(start_value, end_value)
- self.style_interpolator = StyleInterpolator(start_value, end_value)
- self.position_interpolator = ValueInterpolator(
- float(start_value.offset), float(end_value.offset)
- )
-
- def interpolate(self, time=0):
- """Interpolates a gradient stop by interpolating style and offset separately
-
- Args:
- time (int, optional): Interpolation position. If 0, start_value is returned,
- if 1, end_value is returned. Defaults to 0.
-
- Returns:
- Stop: interpolated gradient stop
- """
- newstop = Stop()
- newstop.style = self.style_interpolator.interpolate(time)
- newstop.offset = self.position_interpolator.interpolate(time)
- return newstop
-
-
-class PathInterpolator(AttributeInterpolator):
- """Base class for Path interpolation"""
-
- def __init__(self, start_value=Path(), end_value=Path()):
- super().__init__(start_value.to_superpath(), end_value.to_superpath())
- self.processed_end_path = None
- self.processed_start_path = None
-
- def truncate_subpaths(self):
- """Truncates the longer path so that all subpaths in both paths have an equal
- number of bezier commands"""
- s = [[]]
- e = [[]]
- # loop through all subpaths as long as there are remaining ones
- while self.start_value and self.end_value:
- # if both subpaths contain a bezier command, append it to s and e
- if self.start_value[0] and self.end_value[0]:
- s[-1].append(self.start_value[0].pop(0))
- e[-1].append(self.end_value[0].pop(0))
- # if the subpath of start_value is empty, add the remaining empty list as
- # new subpath of s and one more item of end_value as new subpath of e.
- # Afterwards, the loop terminates
- elif self.end_value[0]:
- s.append(self.start_value.pop(0))
- e[-1].append(self.end_value[0][0])
- e.append([self.end_value[0].pop(0)])
- elif self.start_value[0]:
- e.append(self.end_value.pop(0))
- s[-1].append(self.start_value[0][0])
- s.append([self.start_value[0].pop(0)])
- # if there are no commands left in both start_value or end_value, add empty
- # list to both start_value and end_value
- else:
- s.append(self.start_value.pop(0))
- e.append(self.end_value.pop(0))
- self.processed_start_path = s
- self.processed_end_path = e
-
- def interpolate(self, time=0):
- # create an interpolated path for each interval
- interp = []
- # process subpaths
- for ssubpath, esubpath in zip(
- self.processed_start_path, self.processed_end_path
- ):
- if not (ssubpath or esubpath):
- break
- # add a new subpath to the interpolated path
- interp.append([])
- # process each bezier command in the subpaths (which now have equal length)
- for sbezier, ebezier in zip(ssubpath, esubpath):
- if not (sbezier or ebezier):
- break
- # add a new bezier command to the last subpath
- interp[-1].append([])
- # process points
- for point1, point2 in zip(sbezier, ebezier):
- if not (point1 or point2):
- break
- # add a new point to the last bezier command
- interp[-1][-1].append(
- ArrayInterpolator(point1, point2).interpolate(time)
- )
- # remove final subpath if empty.
- if not interp[-1]:
- del interp[-1]
- return CubicSuperPath(interp)
-
-
-class EqualSubsegmentsInterpolator(PathInterpolator):
- """Interpolates the path by rediscretizing the subpaths first."""
-
- @staticmethod
- def get_subpath_lenghts(path):
- """prepare lengths for interpolation"""
- sp_lenghts, total = csplength(path)
- t = 0
- lenghts = []
- for sp in sp_lenghts:
- for l in sp:
- t += l / total
- lenghts.append(t)
- lenghts.sort()
- return sp_lenghts, total, lenghts
-
- @staticmethod
- def process_path(path, other):
- """Rediscretize path so that all subpaths have an equal number of segments,
- so that there is a node at the path "times" where path or other have a node
-
- Args:
- path (Path): the first path
- other (Path): the second path
-
- Returns:
- Array: the prepared path description for the intermediate path"""
- sp_lenghts, total, _ = EqualSubsegmentsInterpolator.get_subpath_lenghts(path)
- _, _, lenghts = EqualSubsegmentsInterpolator.get_subpath_lenghts(other)
- t = 0
- s = [[]]
- for sp in sp_lenghts:
- if not path[0]:
- s.append(path.pop(0))
- s[-1].append(path[0].pop(0))
- for l in sp:
- pt = t
- t += l / total
- if lenghts and t > lenghts[0]:
- while lenghts and lenghts[0] < t:
- nt = (lenghts[0] - pt) / (t - pt)
- bezes = cspbezsplitatlength(s[-1][-1][:], path[0][0][:], nt)
- s[-1][-1:] = bezes[:2]
- path[0][0] = bezes[2]
- pt = lenghts.pop(0)
- s[-1].append(path[0].pop(0))
- return s
-
- def __init__(self, start_path=Path(), end_path=Path()):
- super().__init__(start_path, end_path)
- # rediscretisize both paths
- start_copy = copy.deepcopy(self.start_value)
- # TODO find out why self.start_value.copy() doesn't work
- self.start_value = EqualSubsegmentsInterpolator.process_path(
- self.start_value, self.end_value
- )
- self.end_value = EqualSubsegmentsInterpolator.process_path(
- self.end_value, start_copy
- )
-
- self.truncate_subpaths()
-
-
-class FirstNodesInterpolator(PathInterpolator):
- """Interpolates a path by discarding the trailing nodes of the longer subpath"""
-
- def __init__(self, start_path=Path(), end_path=Path()):
- super().__init__(start_path, end_path)
- # which path has fewer segments?
- lengthdiff = len(self.start_value) - len(self.end_value)
- # swap shortest first
- if lengthdiff > 0:
- self.start_value, self.end_value = self.end_value, self.start_value
- # subdivide the shorter path
- for _ in range(abs(lengthdiff)):
- maxlen = 0
- subpath = 0
- segment = 0
- for y, _ in enumerate(self.start_value):
- for z in range(1, len(self.start_value[y])):
- leng = bezlenapprx(
- self.start_value[y][z - 1], self.start_value[y][z]
- )
- if leng > maxlen:
- maxlen = leng
- subpath = y
- segment = z
- sp1, sp2 = self.start_value[subpath][segment - 1 : segment + 1]
- self.start_value[subpath][segment - 1 : segment + 1] = cspbezsplit(sp1, sp2)
- # if swapped, swap them back
- if lengthdiff > 0:
- self.start_value, self.end_value = self.end_value, self.start_value
- self.truncate_subpaths()
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/units.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/units.py
deleted file mode 100644
index fec0e87..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/units.py
+++ /dev/null
@@ -1,150 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (c) Aaron Spike
-# Aurélio A. Heckert
-# Bulia Byak
-# Nicolas Dufour, nicoduf@yahoo.fr
-# Peter J. R. Moulder
-# Martin Owens
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-"""
-Convert to and from various units and find the closest matching unit.
-"""
-
-import re
-
-# a dictionary of unit to user unit conversion factors
-CONVERSIONS = {
- "in": 96.0,
- "pt": 1.3333333333333333,
- "px": 1.0,
- "mm": 3.779527559055118,
- "cm": 37.79527559055118,
- "m": 3779.527559055118,
- "km": 3779527.559055118,
- "Q": 0.94488188976378,
- "pc": 16.0,
- "yd": 3456.0,
- "ft": 1152.0,
- "": 1.0, # Default px
-}
-
-# allowed unit types, including percentages, relative units, and others
-# that are not suitable for direct conversion to a length.
-# Note that this is _not_ an exhaustive list of allowed unit types.
-UNITS = [
- "in",
- "pt",
- "px",
- "mm",
- "cm",
- "m",
- "km",
- "Q",
- "pc",
- "yd",
- "ft",
- "",
- "%",
- "em",
- "ex",
- "ch",
- "rem",
- "vw",
- "vh",
- "vmin",
- "vmax",
- "deg",
- "grad",
- "rad",
- "turn",
- "s",
- "ms",
- "Hz",
- "kHz",
- "dpi",
- "dpcm",
- "dppx",
-]
-
-UNIT_MATCH = re.compile(rf"({'|'.join(UNITS)})")
-NUMBER_MATCH = re.compile(r"(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)")
-BOTH_MATCH = re.compile(rf"^\s*{NUMBER_MATCH.pattern}\s*{UNIT_MATCH.pattern}\s*$")
-
-
-def parse_unit(value, default_unit="px", default_value=None):
- """
- Takes a value such as 55.32px and returns (55.32, 'px')
- Returns default (None) if no match can be found
- """
- ret = BOTH_MATCH.match(str(value))
- if ret:
- return float(ret.groups()[0]), ret.groups()[-1] or default_unit
- return (default_value, default_unit) if default_value is not None else None
-
-
-def are_near_relative(point_a, point_b, eps=0.01):
- """Return true if the points are near to eps"""
- return (point_a - point_b <= point_a * eps) and (
- point_a - point_b >= -point_a * eps
- )
-
-
-def discover_unit(value, viewbox, default="px"):
- """Attempt to detect the unit being used based on the viewbox"""
- # Default 100px when width can't be parsed
- (value, unit) = parse_unit(value, default_value=100.0)
- if unit not in CONVERSIONS:
- return default
- this_factor = CONVERSIONS[unit] * value / viewbox
-
- # try to find the svgunitfactor in the list of units known. If we don't find
- # something, ...
- for unit, unit_factor in CONVERSIONS.items():
- if unit != "":
- # allow 1% error in factor
- if are_near_relative(this_factor, unit_factor, eps=0.01):
- return unit
- return default
-
-
-def convert_unit(value, to_unit, default="px"):
- """Returns userunits given a string representation of units in another system
-
- Args:
- value: string
- to_unit: unit to convert to
- default: if ``value`` contains no unit, what unit should be assumed.
-
- .. versionadded:: 1.1
- """
- value, from_unit = parse_unit(value, default_unit=default, default_value=0.0)
- if from_unit in CONVERSIONS and to_unit in CONVERSIONS:
- return (
- value * CONVERSIONS[from_unit] / CONVERSIONS.get(to_unit, CONVERSIONS["px"])
- )
- return 0.0
-
-
-def render_unit(value, unit):
- """Checks and then renders a number with its unit"""
- try:
- if isinstance(value, str):
- (value, unit) = parse_unit(value, default_unit=unit)
- return f"{value:.6g}{ unit:s}"
- except TypeError:
- return ""
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/utils.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/utils.py
deleted file mode 100644
index 2d1dd94..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/inkex/utils.py
+++ /dev/null
@@ -1,271 +0,0 @@
-# coding=utf-8
-#
-# Copyright (C) 2010 Nick Drobchenko, nick@cnc-club.ru
-# Copyright (C) 2005 Aaron Spike, aaron@ekips.org
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-"""
-Basic common utility functions for calculated things
-"""
-import os
-import sys
-import random
-import re
-import math
-from argparse import ArgumentTypeError
-from itertools import tee
-
-ABORT_STATUS = -5
-
-(X, Y) = range(2)
-PY3 = sys.version_info[0] == 3
-
-# pylint: disable=line-too-long
-# Taken from https://www.w3.org/Graphics/SVG/1.1/paths.html#PathDataBNF
-DIGIT_REX_PART = r"[0-9]"
-DIGIT_SEQUENCE_REX_PART = rf"(?:{DIGIT_REX_PART}+)"
-INTEGER_CONSTANT_REX_PART = DIGIT_SEQUENCE_REX_PART
-SIGN_REX_PART = r"[+-]"
-EXPONENT_REX_PART = rf"(?:[eE]{SIGN_REX_PART}?{DIGIT_SEQUENCE_REX_PART})"
-FRACTIONAL_CONSTANT_REX_PART = rf"(?:{DIGIT_SEQUENCE_REX_PART}?\.{DIGIT_SEQUENCE_REX_PART}|{DIGIT_SEQUENCE_REX_PART}\.)"
-FLOATING_POINT_CONSTANT_REX_PART = rf"(?:{FRACTIONAL_CONSTANT_REX_PART}{EXPONENT_REX_PART}?|{DIGIT_SEQUENCE_REX_PART}{EXPONENT_REX_PART})"
-NUMBER_REX = re.compile(
- rf"(?:{SIGN_REX_PART}?{FLOATING_POINT_CONSTANT_REX_PART}|{SIGN_REX_PART}?{INTEGER_CONSTANT_REX_PART})"
-)
-# pylint: enable=line-too-long
-
-
-def _pythonpath():
- for pth in os.environ.get("PYTHONPATH", "").split(":"):
- if os.path.isdir(pth):
- yield pth
-
-
-def get_user_directory():
- """Return the user directory where extensions are stored.
-
- .. versionadded:: 1.1"""
- if "INKSCAPE_PROFILE_DIR" in os.environ:
- return os.path.abspath(
- os.path.expanduser(
- os.path.join(os.environ["INKSCAPE_PROFILE_DIR"], "extensions")
- )
- )
-
- home = os.path.expanduser("~")
- for pth in _pythonpath():
- if pth.startswith(home):
- return pth
- return None
-
-
-def get_inkscape_directory():
- """Return the system directory where inkscape's core is.
-
- .. versionadded:: 1.1"""
- for pth in _pythonpath():
- if os.path.isdir(os.path.join(pth, "inkex")):
- return pth
- raise ValueError("Unable to determine the location of Inkscape")
-
-
-class KeyDict(dict):
- """
- A normal dictionary, except asking for anything not in the dictionary
- always returns the key itself. This is used for translation dictionaries.
- """
-
- def __getitem__(self, key):
- try:
- return super().__getitem__(key)
- except KeyError:
- return key
-
-
-def parse_percent(val: str):
- """Parse strings that are either values (i.e., '3.14159') or percentages
- (i.e. '75%') to a float.
-
- .. versionadded:: 1.2"""
- val = val.strip()
- if val.endswith("%"):
- return float(val[:-1]) / 100
- return float(val)
-
-
-def Boolean(value):
- """ArgParser function to turn a boolean string into a python boolean"""
- if value.upper() == "TRUE":
- return True
- if value.upper() == "FALSE":
- return False
- return None
-
-
-def to_bytes(content):
- """Ensures the content is bytes
-
- .. versionadded:: 1.1"""
- if isinstance(content, bytes):
- return content
- return str(content).encode("utf8")
-
-
-def debug(what):
- """Print debug message if debugging is switched on"""
- errormsg(what)
- return what
-
-
-def do_nothing(*args, **kwargs): # pylint: disable=unused-argument
- """A blank function to do nothing
-
- .. versionadded:: 1.1"""
-
-
-def errormsg(msg):
- """Intended for end-user-visible error messages.
-
- (Currently just writes to stderr with an appended newline, but could do
- something better in future: e.g. could add markup to distinguish error
- messages from status messages or debugging output.)
-
- Note that this should always be combined with translation::
-
- import inkex
- ...
- inkex.errormsg(_("This extension requires two selected paths."))
- """
- try:
- sys.stderr.write(msg)
- except TypeError:
- sys.stderr.write(str(msg))
- except UnicodeEncodeError:
- # Python 2:
- # Fallback for cases where sys.stderr.encoding is not Unicode.
- # Python 3:
- # This will not work as write() does not accept byte strings, but AFAIK
- # we should never reach this point as the default error handler is
- # 'backslashreplace'.
-
- # This will be None by default if stderr is piped, so use ASCII as a
- # last resort.
- encoding = sys.stderr.encoding or "ascii"
- sys.stderr.write(msg.encode(encoding, "backslashreplace"))
-
- # Write '\n' separately to avoid dealing with different string types.
- sys.stderr.write("\n")
-
-
-class AbortExtension(Exception):
- """Raised to print a message to the user without backtrace"""
-
-
-class DependencyError(NotImplementedError):
- """Raised when we need an external python module that isn't available"""
-
-
-class FragmentError(Exception):
- """Raised when trying to do rooty things on an xml fragment"""
-
-
-def to(kind): # pylint: disable=invalid-name
- """
- Decorator which will turn a generator into a list, tuple or other object type.
- """
-
- def _inner(call):
- def _outer(*args, **kw):
- return kind(call(*args, **kw))
-
- return _outer
-
- return _inner
-
-
-def strargs(string, kind=float):
- """Returns a list of floats from a string
-
- .. versionchanged:: 1.1
- also splits at -(minus) signs by adding a space in front of the - sign
-
- .. versionchanged:: 1.2
- Full support for the `SVG Path data BNF
- `_
- """
- return [kind(val) for val in NUMBER_REX.findall(string)]
-
-
-class classproperty: # pylint: disable=invalid-name, too-few-public-methods
- """Combine classmethod and property decorators"""
-
- def __init__(self, func):
- self.func = func
-
- def __get__(self, obj, owner):
- return self.func(owner)
-
-
-def filename_arg(name):
- """Existing file to read or option used in script arguments"""
- filename = os.path.abspath(os.path.expanduser(name))
- if not os.path.isfile(filename):
- raise ArgumentTypeError(f"File not found: {name}")
- return filename
-
-
-def pairwise(iterable, start=True):
- "Iterate over a list with overlapping pairs (see itertools recipes)"
- first, then = tee(iterable)
- starter = [(None, next(then, None))]
- if not start:
- starter = []
- return starter + list(zip(first, then))
-
-
-EVAL_GLOBALS = {}
-EVAL_GLOBALS.update(random.__dict__)
-EVAL_GLOBALS.update(math.__dict__)
-
-
-def math_eval(function, variable="x"):
- """Interpret a function string. All functions from math and random may be used.
-
- .. versionadded:: 1.1
-
- Returns:
- a lambda expression if sucessful; otherwise None.
- """
- try:
- if function != "":
- return eval(
- f"lambda {variable}: " + (function.strip('"') or "t"), EVAL_GLOBALS, {}
- )
- # handle incomplete/invalid function gracefully
- except SyntaxError:
- pass
- return None
-
-
-def is_number(string):
- """Checks if a value is a number
-
- .. versionadded:: 1.2"""
- try:
- float(string)
- return True
- except ValueError:
- return False
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/README.md b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/README.md
deleted file mode 100644
index 1f9e9fe..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/README.md
+++ /dev/null
@@ -1 +0,0 @@
-This directory is for contents of the site-packages associated with the packaged version of Inkex.
\ No newline at end of file
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/__init__.py
deleted file mode 100644
index fe58162..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/__init__.py
+++ /dev/null
@@ -1,115 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-from typing import List, Union
-
-from .charsetgroupprober import CharSetGroupProber
-from .charsetprober import CharSetProber
-from .enums import InputState
-from .resultdict import ResultDict
-from .universaldetector import UniversalDetector
-from .version import VERSION, __version__
-
-__all__ = ["UniversalDetector", "detect", "detect_all", "__version__", "VERSION"]
-
-
-def detect(
- byte_str: Union[bytes, bytearray], should_rename_legacy: bool = False
-) -> ResultDict:
- """
- Detect the encoding of the given byte string.
-
- :param byte_str: The byte sequence to examine.
- :type byte_str: ``bytes`` or ``bytearray``
- :param should_rename_legacy: Should we rename legacy encodings
- to their more modern equivalents?
- :type should_rename_legacy: ``bool``
- """
- if not isinstance(byte_str, bytearray):
- if not isinstance(byte_str, bytes):
- raise TypeError(
- f"Expected object of type bytes or bytearray, got: {type(byte_str)}"
- )
- byte_str = bytearray(byte_str)
- detector = UniversalDetector(should_rename_legacy=should_rename_legacy)
- detector.feed(byte_str)
- return detector.close()
-
-
-def detect_all(
- byte_str: Union[bytes, bytearray],
- ignore_threshold: bool = False,
- should_rename_legacy: bool = False,
-) -> List[ResultDict]:
- """
- Detect all the possible encodings of the given byte string.
-
- :param byte_str: The byte sequence to examine.
- :type byte_str: ``bytes`` or ``bytearray``
- :param ignore_threshold: Include encodings that are below
- ``UniversalDetector.MINIMUM_THRESHOLD``
- in results.
- :type ignore_threshold: ``bool``
- :param should_rename_legacy: Should we rename legacy encodings
- to their more modern equivalents?
- :type should_rename_legacy: ``bool``
- """
- if not isinstance(byte_str, bytearray):
- if not isinstance(byte_str, bytes):
- raise TypeError(
- f"Expected object of type bytes or bytearray, got: {type(byte_str)}"
- )
- byte_str = bytearray(byte_str)
-
- detector = UniversalDetector(should_rename_legacy=should_rename_legacy)
- detector.feed(byte_str)
- detector.close()
-
- if detector.input_state == InputState.HIGH_BYTE:
- results: List[ResultDict] = []
- probers: List[CharSetProber] = []
- for prober in detector.charset_probers:
- if isinstance(prober, CharSetGroupProber):
- probers.extend(p for p in prober.probers)
- else:
- probers.append(prober)
- for prober in probers:
- if ignore_threshold or prober.get_confidence() > detector.MINIMUM_THRESHOLD:
- charset_name = prober.charset_name or ""
- lower_charset_name = charset_name.lower()
- # Use Windows encoding name instead of ISO-8859 if we saw any
- # extra Windows-specific bytes
- if lower_charset_name.startswith("iso-8859") and detector.has_win_bytes:
- charset_name = detector.ISO_WIN_MAP.get(
- lower_charset_name, charset_name
- )
- # Rename legacy encodings with superset encodings if asked
- if should_rename_legacy:
- charset_name = detector.LEGACY_MAP.get(
- charset_name.lower(), charset_name
- )
- results.append(
- {
- "encoding": charset_name,
- "confidence": prober.get_confidence(),
- "language": prober.language,
- }
- )
- if len(results) > 0:
- return sorted(results, key=lambda result: -result["confidence"])
-
- return [detector.result]
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/__main__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/__main__.py
deleted file mode 100644
index c19b0d2..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/__main__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-"""Wrapper so people can run python -m chardet"""
-
-from .cli.chardetect import main
-
-if __name__ == "__main__":
- main()
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/big5freq.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/big5freq.py
deleted file mode 100644
index 87d9f97..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/big5freq.py
+++ /dev/null
@@ -1,386 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is Mozilla Communicator client code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-# Big5 frequency table
-# by Taiwan's Mandarin Promotion Council
-#
-#
-# 128 --> 0.42261
-# 256 --> 0.57851
-# 512 --> 0.74851
-# 1024 --> 0.89384
-# 2048 --> 0.97583
-#
-# Ideal Distribution Ratio = 0.74851/(1-0.74851) =2.98
-# Random Distribution Ration = 512/(5401-512)=0.105
-#
-# Typical Distribution Ratio about 25% of Ideal one, still much higher than RDR
-
-BIG5_TYPICAL_DISTRIBUTION_RATIO = 0.75
-
-# Char to FreqOrder table
-BIG5_TABLE_SIZE = 5376
-# fmt: off
-BIG5_CHAR_TO_FREQ_ORDER = (
- 1,1801,1506, 255,1431, 198, 9, 82, 6,5008, 177, 202,3681,1256,2821, 110, # 16
-3814, 33,3274, 261, 76, 44,2114, 16,2946,2187,1176, 659,3971, 26,3451,2653, # 32
-1198,3972,3350,4202, 410,2215, 302, 590, 361,1964, 8, 204, 58,4510,5009,1932, # 48
- 63,5010,5011, 317,1614, 75, 222, 159,4203,2417,1480,5012,3555,3091, 224,2822, # 64
-3682, 3, 10,3973,1471, 29,2787,1135,2866,1940, 873, 130,3275,1123, 312,5013, # 80
-4511,2052, 507, 252, 682,5014, 142,1915, 124, 206,2947, 34,3556,3204, 64, 604, # 96
-5015,2501,1977,1978, 155,1991, 645, 641,1606,5016,3452, 337, 72, 406,5017, 80, # 112
- 630, 238,3205,1509, 263, 939,1092,2654, 756,1440,1094,3453, 449, 69,2987, 591, # 128
- 179,2096, 471, 115,2035,1844, 60, 50,2988, 134, 806,1869, 734,2036,3454, 180, # 144
- 995,1607, 156, 537,2907, 688,5018, 319,1305, 779,2145, 514,2379, 298,4512, 359, # 160
-2502, 90,2716,1338, 663, 11, 906,1099,2553, 20,2441, 182, 532,1716,5019, 732, # 176
-1376,4204,1311,1420,3206, 25,2317,1056, 113, 399, 382,1950, 242,3455,2474, 529, # 192
-3276, 475,1447,3683,5020, 117, 21, 656, 810,1297,2300,2334,3557,5021, 126,4205, # 208
- 706, 456, 150, 613,4513, 71,1118,2037,4206, 145,3092, 85, 835, 486,2115,1246, # 224
-1426, 428, 727,1285,1015, 800, 106, 623, 303,1281,5022,2128,2359, 347,3815, 221, # 240
-3558,3135,5023,1956,1153,4207, 83, 296,1199,3093, 192, 624, 93,5024, 822,1898, # 256
-2823,3136, 795,2065, 991,1554,1542,1592, 27, 43,2867, 859, 139,1456, 860,4514, # 272
- 437, 712,3974, 164,2397,3137, 695, 211,3037,2097, 195,3975,1608,3559,3560,3684, # 288
-3976, 234, 811,2989,2098,3977,2233,1441,3561,1615,2380, 668,2077,1638, 305, 228, # 304
-1664,4515, 467, 415,5025, 262,2099,1593, 239, 108, 300, 200,1033, 512,1247,2078, # 320
-5026,5027,2176,3207,3685,2682, 593, 845,1062,3277, 88,1723,2038,3978,1951, 212, # 336
- 266, 152, 149, 468,1899,4208,4516, 77, 187,5028,3038, 37, 5,2990,5029,3979, # 352
-5030,5031, 39,2524,4517,2908,3208,2079, 55, 148, 74,4518, 545, 483,1474,1029, # 368
-1665, 217,1870,1531,3138,1104,2655,4209, 24, 172,3562, 900,3980,3563,3564,4519, # 384
- 32,1408,2824,1312, 329, 487,2360,2251,2717, 784,2683, 4,3039,3351,1427,1789, # 400
- 188, 109, 499,5032,3686,1717,1790, 888,1217,3040,4520,5033,3565,5034,3352,1520, # 416
-3687,3981, 196,1034, 775,5035,5036, 929,1816, 249, 439, 38,5037,1063,5038, 794, # 432
-3982,1435,2301, 46, 178,3278,2066,5039,2381,5040, 214,1709,4521, 804, 35, 707, # 448
- 324,3688,1601,2554, 140, 459,4210,5041,5042,1365, 839, 272, 978,2262,2580,3456, # 464
-2129,1363,3689,1423, 697, 100,3094, 48, 70,1231, 495,3139,2196,5043,1294,5044, # 480
-2080, 462, 586,1042,3279, 853, 256, 988, 185,2382,3457,1698, 434,1084,5045,3458, # 496
- 314,2625,2788,4522,2335,2336, 569,2285, 637,1817,2525, 757,1162,1879,1616,3459, # 512
- 287,1577,2116, 768,4523,1671,2868,3566,2526,1321,3816, 909,2418,5046,4211, 933, # 528
-3817,4212,2053,2361,1222,4524, 765,2419,1322, 786,4525,5047,1920,1462,1677,2909, # 544
-1699,5048,4526,1424,2442,3140,3690,2600,3353,1775,1941,3460,3983,4213, 309,1369, # 560
-1130,2825, 364,2234,1653,1299,3984,3567,3985,3986,2656, 525,1085,3041, 902,2001, # 576
-1475, 964,4527, 421,1845,1415,1057,2286, 940,1364,3141, 376,4528,4529,1381, 7, # 592
-2527, 983,2383, 336,1710,2684,1846, 321,3461, 559,1131,3042,2752,1809,1132,1313, # 608
- 265,1481,1858,5049, 352,1203,2826,3280, 167,1089, 420,2827, 776, 792,1724,3568, # 624
-4214,2443,3281,5050,4215,5051, 446, 229, 333,2753, 901,3818,1200,1557,4530,2657, # 640
-1921, 395,2754,2685,3819,4216,1836, 125, 916,3209,2626,4531,5052,5053,3820,5054, # 656
-5055,5056,4532,3142,3691,1133,2555,1757,3462,1510,2318,1409,3569,5057,2146, 438, # 672
-2601,2910,2384,3354,1068, 958,3043, 461, 311,2869,2686,4217,1916,3210,4218,1979, # 688
- 383, 750,2755,2627,4219, 274, 539, 385,1278,1442,5058,1154,1965, 384, 561, 210, # 704
- 98,1295,2556,3570,5059,1711,2420,1482,3463,3987,2911,1257, 129,5060,3821, 642, # 720
- 523,2789,2790,2658,5061, 141,2235,1333, 68, 176, 441, 876, 907,4220, 603,2602, # 736
- 710, 171,3464, 404, 549, 18,3143,2398,1410,3692,1666,5062,3571,4533,2912,4534, # 752
-5063,2991, 368,5064, 146, 366, 99, 871,3693,1543, 748, 807,1586,1185, 22,2263, # 768
- 379,3822,3211,5065,3212, 505,1942,2628,1992,1382,2319,5066, 380,2362, 218, 702, # 784
-1818,1248,3465,3044,3572,3355,3282,5067,2992,3694, 930,3283,3823,5068, 59,5069, # 800
- 585, 601,4221, 497,3466,1112,1314,4535,1802,5070,1223,1472,2177,5071, 749,1837, # 816
- 690,1900,3824,1773,3988,1476, 429,1043,1791,2236,2117, 917,4222, 447,1086,1629, # 832
-5072, 556,5073,5074,2021,1654, 844,1090, 105, 550, 966,1758,2828,1008,1783, 686, # 848
-1095,5075,2287, 793,1602,5076,3573,2603,4536,4223,2948,2302,4537,3825, 980,2503, # 864
- 544, 353, 527,4538, 908,2687,2913,5077, 381,2629,1943,1348,5078,1341,1252, 560, # 880
-3095,5079,3467,2870,5080,2054, 973, 886,2081, 143,4539,5081,5082, 157,3989, 496, # 896
-4224, 57, 840, 540,2039,4540,4541,3468,2118,1445, 970,2264,1748,1966,2082,4225, # 912
-3144,1234,1776,3284,2829,3695, 773,1206,2130,1066,2040,1326,3990,1738,1725,4226, # 928
- 279,3145, 51,1544,2604, 423,1578,2131,2067, 173,4542,1880,5083,5084,1583, 264, # 944
- 610,3696,4543,2444, 280, 154,5085,5086,5087,1739, 338,1282,3096, 693,2871,1411, # 960
-1074,3826,2445,5088,4544,5089,5090,1240, 952,2399,5091,2914,1538,2688, 685,1483, # 976
-4227,2475,1436, 953,4228,2055,4545, 671,2400, 79,4229,2446,3285, 608, 567,2689, # 992
-3469,4230,4231,1691, 393,1261,1792,2401,5092,4546,5093,5094,5095,5096,1383,1672, # 1008
-3827,3213,1464, 522,1119, 661,1150, 216, 675,4547,3991,1432,3574, 609,4548,2690, # 1024
-2402,5097,5098,5099,4232,3045, 0,5100,2476, 315, 231,2447, 301,3356,4549,2385, # 1040
-5101, 233,4233,3697,1819,4550,4551,5102, 96,1777,1315,2083,5103, 257,5104,1810, # 1056
-3698,2718,1139,1820,4234,2022,1124,2164,2791,1778,2659,5105,3097, 363,1655,3214, # 1072
-5106,2993,5107,5108,5109,3992,1567,3993, 718, 103,3215, 849,1443, 341,3357,2949, # 1088
-1484,5110,1712, 127, 67, 339,4235,2403, 679,1412, 821,5111,5112, 834, 738, 351, # 1104
-2994,2147, 846, 235,1497,1881, 418,1993,3828,2719, 186,1100,2148,2756,3575,1545, # 1120
-1355,2950,2872,1377, 583,3994,4236,2581,2995,5113,1298,3699,1078,2557,3700,2363, # 1136
- 78,3829,3830, 267,1289,2100,2002,1594,4237, 348, 369,1274,2197,2178,1838,4552, # 1152
-1821,2830,3701,2757,2288,2003,4553,2951,2758, 144,3358, 882,4554,3995,2759,3470, # 1168
-4555,2915,5114,4238,1726, 320,5115,3996,3046, 788,2996,5116,2831,1774,1327,2873, # 1184
-3997,2832,5117,1306,4556,2004,1700,3831,3576,2364,2660, 787,2023, 506, 824,3702, # 1200
- 534, 323,4557,1044,3359,2024,1901, 946,3471,5118,1779,1500,1678,5119,1882,4558, # 1216
- 165, 243,4559,3703,2528, 123, 683,4239, 764,4560, 36,3998,1793, 589,2916, 816, # 1232
- 626,1667,3047,2237,1639,1555,1622,3832,3999,5120,4000,2874,1370,1228,1933, 891, # 1248
-2084,2917, 304,4240,5121, 292,2997,2720,3577, 691,2101,4241,1115,4561, 118, 662, # 1264
-5122, 611,1156, 854,2386,1316,2875, 2, 386, 515,2918,5123,5124,3286, 868,2238, # 1280
-1486, 855,2661, 785,2216,3048,5125,1040,3216,3578,5126,3146, 448,5127,1525,5128, # 1296
-2165,4562,5129,3833,5130,4242,2833,3579,3147, 503, 818,4001,3148,1568, 814, 676, # 1312
-1444, 306,1749,5131,3834,1416,1030, 197,1428, 805,2834,1501,4563,5132,5133,5134, # 1328
-1994,5135,4564,5136,5137,2198, 13,2792,3704,2998,3149,1229,1917,5138,3835,2132, # 1344
-5139,4243,4565,2404,3580,5140,2217,1511,1727,1120,5141,5142, 646,3836,2448, 307, # 1360
-5143,5144,1595,3217,5145,5146,5147,3705,1113,1356,4002,1465,2529,2530,5148, 519, # 1376
-5149, 128,2133, 92,2289,1980,5150,4003,1512, 342,3150,2199,5151,2793,2218,1981, # 1392
-3360,4244, 290,1656,1317, 789, 827,2365,5152,3837,4566, 562, 581,4004,5153, 401, # 1408
-4567,2252, 94,4568,5154,1399,2794,5155,1463,2025,4569,3218,1944,5156, 828,1105, # 1424
-4245,1262,1394,5157,4246, 605,4570,5158,1784,2876,5159,2835, 819,2102, 578,2200, # 1440
-2952,5160,1502, 436,3287,4247,3288,2836,4005,2919,3472,3473,5161,2721,2320,5162, # 1456
-5163,2337,2068, 23,4571, 193, 826,3838,2103, 699,1630,4248,3098, 390,1794,1064, # 1472
-3581,5164,1579,3099,3100,1400,5165,4249,1839,1640,2877,5166,4572,4573, 137,4250, # 1488
- 598,3101,1967, 780, 104, 974,2953,5167, 278, 899, 253, 402, 572, 504, 493,1339, # 1504
-5168,4006,1275,4574,2582,2558,5169,3706,3049,3102,2253, 565,1334,2722, 863, 41, # 1520
-5170,5171,4575,5172,1657,2338, 19, 463,2760,4251, 606,5173,2999,3289,1087,2085, # 1536
-1323,2662,3000,5174,1631,1623,1750,4252,2691,5175,2878, 791,2723,2663,2339, 232, # 1552
-2421,5176,3001,1498,5177,2664,2630, 755,1366,3707,3290,3151,2026,1609, 119,1918, # 1568
-3474, 862,1026,4253,5178,4007,3839,4576,4008,4577,2265,1952,2477,5179,1125, 817, # 1584
-4254,4255,4009,1513,1766,2041,1487,4256,3050,3291,2837,3840,3152,5180,5181,1507, # 1600
-5182,2692, 733, 40,1632,1106,2879, 345,4257, 841,2531, 230,4578,3002,1847,3292, # 1616
-3475,5183,1263, 986,3476,5184, 735, 879, 254,1137, 857, 622,1300,1180,1388,1562, # 1632
-4010,4011,2954, 967,2761,2665,1349, 592,2134,1692,3361,3003,1995,4258,1679,4012, # 1648
-1902,2188,5185, 739,3708,2724,1296,1290,5186,4259,2201,2202,1922,1563,2605,2559, # 1664
-1871,2762,3004,5187, 435,5188, 343,1108, 596, 17,1751,4579,2239,3477,3709,5189, # 1680
-4580, 294,3582,2955,1693, 477, 979, 281,2042,3583, 643,2043,3710,2631,2795,2266, # 1696
-1031,2340,2135,2303,3584,4581, 367,1249,2560,5190,3585,5191,4582,1283,3362,2005, # 1712
- 240,1762,3363,4583,4584, 836,1069,3153, 474,5192,2149,2532, 268,3586,5193,3219, # 1728
-1521,1284,5194,1658,1546,4260,5195,3587,3588,5196,4261,3364,2693,1685,4262, 961, # 1744
-1673,2632, 190,2006,2203,3841,4585,4586,5197, 570,2504,3711,1490,5198,4587,2633, # 1760
-3293,1957,4588, 584,1514, 396,1045,1945,5199,4589,1968,2449,5200,5201,4590,4013, # 1776
- 619,5202,3154,3294, 215,2007,2796,2561,3220,4591,3221,4592, 763,4263,3842,4593, # 1792
-5203,5204,1958,1767,2956,3365,3712,1174, 452,1477,4594,3366,3155,5205,2838,1253, # 1808
-2387,2189,1091,2290,4264, 492,5206, 638,1169,1825,2136,1752,4014, 648, 926,1021, # 1824
-1324,4595, 520,4596, 997, 847,1007, 892,4597,3843,2267,1872,3713,2405,1785,4598, # 1840
-1953,2957,3103,3222,1728,4265,2044,3714,4599,2008,1701,3156,1551, 30,2268,4266, # 1856
-5207,2027,4600,3589,5208, 501,5209,4267, 594,3478,2166,1822,3590,3479,3591,3223, # 1872
- 829,2839,4268,5210,1680,3157,1225,4269,5211,3295,4601,4270,3158,2341,5212,4602, # 1888
-4271,5213,4015,4016,5214,1848,2388,2606,3367,5215,4603, 374,4017, 652,4272,4273, # 1904
- 375,1140, 798,5216,5217,5218,2366,4604,2269, 546,1659, 138,3051,2450,4605,5219, # 1920
-2254, 612,1849, 910, 796,3844,1740,1371, 825,3845,3846,5220,2920,2562,5221, 692, # 1936
- 444,3052,2634, 801,4606,4274,5222,1491, 244,1053,3053,4275,4276, 340,5223,4018, # 1952
-1041,3005, 293,1168, 87,1357,5224,1539, 959,5225,2240, 721, 694,4277,3847, 219, # 1968
-1478, 644,1417,3368,2666,1413,1401,1335,1389,4019,5226,5227,3006,2367,3159,1826, # 1984
- 730,1515, 184,2840, 66,4607,5228,1660,2958, 246,3369, 378,1457, 226,3480, 975, # 2000
-4020,2959,1264,3592, 674, 696,5229, 163,5230,1141,2422,2167, 713,3593,3370,4608, # 2016
-4021,5231,5232,1186, 15,5233,1079,1070,5234,1522,3224,3594, 276,1050,2725, 758, # 2032
-1126, 653,2960,3296,5235,2342, 889,3595,4022,3104,3007, 903,1250,4609,4023,3481, # 2048
-3596,1342,1681,1718, 766,3297, 286, 89,2961,3715,5236,1713,5237,2607,3371,3008, # 2064
-5238,2962,2219,3225,2880,5239,4610,2505,2533, 181, 387,1075,4024, 731,2190,3372, # 2080
-5240,3298, 310, 313,3482,2304, 770,4278, 54,3054, 189,4611,3105,3848,4025,5241, # 2096
-1230,1617,1850, 355,3597,4279,4612,3373, 111,4280,3716,1350,3160,3483,3055,4281, # 2112
-2150,3299,3598,5242,2797,4026,4027,3009, 722,2009,5243,1071, 247,1207,2343,2478, # 2128
-1378,4613,2010, 864,1437,1214,4614, 373,3849,1142,2220, 667,4615, 442,2763,2563, # 2144
-3850,4028,1969,4282,3300,1840, 837, 170,1107, 934,1336,1883,5244,5245,2119,4283, # 2160
-2841, 743,1569,5246,4616,4284, 582,2389,1418,3484,5247,1803,5248, 357,1395,1729, # 2176
-3717,3301,2423,1564,2241,5249,3106,3851,1633,4617,1114,2086,4285,1532,5250, 482, # 2192
-2451,4618,5251,5252,1492, 833,1466,5253,2726,3599,1641,2842,5254,1526,1272,3718, # 2208
-4286,1686,1795, 416,2564,1903,1954,1804,5255,3852,2798,3853,1159,2321,5256,2881, # 2224
-4619,1610,1584,3056,2424,2764, 443,3302,1163,3161,5257,5258,4029,5259,4287,2506, # 2240
-3057,4620,4030,3162,2104,1647,3600,2011,1873,4288,5260,4289, 431,3485,5261, 250, # 2256
- 97, 81,4290,5262,1648,1851,1558, 160, 848,5263, 866, 740,1694,5264,2204,2843, # 2272
-3226,4291,4621,3719,1687, 950,2479, 426, 469,3227,3720,3721,4031,5265,5266,1188, # 2288
- 424,1996, 861,3601,4292,3854,2205,2694, 168,1235,3602,4293,5267,2087,1674,4622, # 2304
-3374,3303, 220,2565,1009,5268,3855, 670,3010, 332,1208, 717,5269,5270,3603,2452, # 2320
-4032,3375,5271, 513,5272,1209,2882,3376,3163,4623,1080,5273,5274,5275,5276,2534, # 2336
-3722,3604, 815,1587,4033,4034,5277,3605,3486,3856,1254,4624,1328,3058,1390,4035, # 2352
-1741,4036,3857,4037,5278, 236,3858,2453,3304,5279,5280,3723,3859,1273,3860,4625, # 2368
-5281, 308,5282,4626, 245,4627,1852,2480,1307,2583, 430, 715,2137,2454,5283, 270, # 2384
- 199,2883,4038,5284,3606,2727,1753, 761,1754, 725,1661,1841,4628,3487,3724,5285, # 2400
-5286, 587, 14,3305, 227,2608, 326, 480,2270, 943,2765,3607, 291, 650,1884,5287, # 2416
-1702,1226, 102,1547, 62,3488, 904,4629,3489,1164,4294,5288,5289,1224,1548,2766, # 2432
- 391, 498,1493,5290,1386,1419,5291,2056,1177,4630, 813, 880,1081,2368, 566,1145, # 2448
-4631,2291,1001,1035,2566,2609,2242, 394,1286,5292,5293,2069,5294, 86,1494,1730, # 2464
-4039, 491,1588, 745, 897,2963, 843,3377,4040,2767,2884,3306,1768, 998,2221,2070, # 2480
- 397,1827,1195,1970,3725,3011,3378, 284,5295,3861,2507,2138,2120,1904,5296,4041, # 2496
-2151,4042,4295,1036,3490,1905, 114,2567,4296, 209,1527,5297,5298,2964,2844,2635, # 2512
-2390,2728,3164, 812,2568,5299,3307,5300,1559, 737,1885,3726,1210, 885, 28,2695, # 2528
-3608,3862,5301,4297,1004,1780,4632,5302, 346,1982,2222,2696,4633,3863,1742, 797, # 2544
-1642,4043,1934,1072,1384,2152, 896,4044,3308,3727,3228,2885,3609,5303,2569,1959, # 2560
-4634,2455,1786,5304,5305,5306,4045,4298,1005,1308,3728,4299,2729,4635,4636,1528, # 2576
-2610, 161,1178,4300,1983, 987,4637,1101,4301, 631,4046,1157,3229,2425,1343,1241, # 2592
-1016,2243,2570, 372, 877,2344,2508,1160, 555,1935, 911,4047,5307, 466,1170, 169, # 2608
-1051,2921,2697,3729,2481,3012,1182,2012,2571,1251,2636,5308, 992,2345,3491,1540, # 2624
-2730,1201,2071,2406,1997,2482,5309,4638, 528,1923,2191,1503,1874,1570,2369,3379, # 2640
-3309,5310, 557,1073,5311,1828,3492,2088,2271,3165,3059,3107, 767,3108,2799,4639, # 2656
-1006,4302,4640,2346,1267,2179,3730,3230, 778,4048,3231,2731,1597,2667,5312,4641, # 2672
-5313,3493,5314,5315,5316,3310,2698,1433,3311, 131, 95,1504,4049, 723,4303,3166, # 2688
-1842,3610,2768,2192,4050,2028,2105,3731,5317,3013,4051,1218,5318,3380,3232,4052, # 2704
-4304,2584, 248,1634,3864, 912,5319,2845,3732,3060,3865, 654, 53,5320,3014,5321, # 2720
-1688,4642, 777,3494,1032,4053,1425,5322, 191, 820,2121,2846, 971,4643, 931,3233, # 2736
- 135, 664, 783,3866,1998, 772,2922,1936,4054,3867,4644,2923,3234, 282,2732, 640, # 2752
-1372,3495,1127, 922, 325,3381,5323,5324, 711,2045,5325,5326,4055,2223,2800,1937, # 2768
-4056,3382,2224,2255,3868,2305,5327,4645,3869,1258,3312,4057,3235,2139,2965,4058, # 2784
-4059,5328,2225, 258,3236,4646, 101,1227,5329,3313,1755,5330,1391,3314,5331,2924, # 2800
-2057, 893,5332,5333,5334,1402,4305,2347,5335,5336,3237,3611,5337,5338, 878,1325, # 2816
-1781,2801,4647, 259,1385,2585, 744,1183,2272,4648,5339,4060,2509,5340, 684,1024, # 2832
-4306,5341, 472,3612,3496,1165,3315,4061,4062, 322,2153, 881, 455,1695,1152,1340, # 2848
- 660, 554,2154,4649,1058,4650,4307, 830,1065,3383,4063,4651,1924,5342,1703,1919, # 2864
-5343, 932,2273, 122,5344,4652, 947, 677,5345,3870,2637, 297,1906,1925,2274,4653, # 2880
-2322,3316,5346,5347,4308,5348,4309, 84,4310, 112, 989,5349, 547,1059,4064, 701, # 2896
-3613,1019,5350,4311,5351,3497, 942, 639, 457,2306,2456, 993,2966, 407, 851, 494, # 2912
-4654,3384, 927,5352,1237,5353,2426,3385, 573,4312, 680, 921,2925,1279,1875, 285, # 2928
- 790,1448,1984, 719,2168,5354,5355,4655,4065,4066,1649,5356,1541, 563,5357,1077, # 2944
-5358,3386,3061,3498, 511,3015,4067,4068,3733,4069,1268,2572,3387,3238,4656,4657, # 2960
-5359, 535,1048,1276,1189,2926,2029,3167,1438,1373,2847,2967,1134,2013,5360,4313, # 2976
-1238,2586,3109,1259,5361, 700,5362,2968,3168,3734,4314,5363,4315,1146,1876,1907, # 2992
-4658,2611,4070, 781,2427, 132,1589, 203, 147, 273,2802,2407, 898,1787,2155,4071, # 3008
-4072,5364,3871,2803,5365,5366,4659,4660,5367,3239,5368,1635,3872, 965,5369,1805, # 3024
-2699,1516,3614,1121,1082,1329,3317,4073,1449,3873, 65,1128,2848,2927,2769,1590, # 3040
-3874,5370,5371, 12,2668, 45, 976,2587,3169,4661, 517,2535,1013,1037,3240,5372, # 3056
-3875,2849,5373,3876,5374,3499,5375,2612, 614,1999,2323,3877,3110,2733,2638,5376, # 3072
-2588,4316, 599,1269,5377,1811,3735,5378,2700,3111, 759,1060, 489,1806,3388,3318, # 3088
-1358,5379,5380,2391,1387,1215,2639,2256, 490,5381,5382,4317,1759,2392,2348,5383, # 3104
-4662,3878,1908,4074,2640,1807,3241,4663,3500,3319,2770,2349, 874,5384,5385,3501, # 3120
-3736,1859, 91,2928,3737,3062,3879,4664,5386,3170,4075,2669,5387,3502,1202,1403, # 3136
-3880,2969,2536,1517,2510,4665,3503,2511,5388,4666,5389,2701,1886,1495,1731,4076, # 3152
-2370,4667,5390,2030,5391,5392,4077,2702,1216, 237,2589,4318,2324,4078,3881,4668, # 3168
-4669,2703,3615,3504, 445,4670,5393,5394,5395,5396,2771, 61,4079,3738,1823,4080, # 3184
-5397, 687,2046, 935, 925, 405,2670, 703,1096,1860,2734,4671,4081,1877,1367,2704, # 3200
-3389, 918,2106,1782,2483, 334,3320,1611,1093,4672, 564,3171,3505,3739,3390, 945, # 3216
-2641,2058,4673,5398,1926, 872,4319,5399,3506,2705,3112, 349,4320,3740,4082,4674, # 3232
-3882,4321,3741,2156,4083,4675,4676,4322,4677,2408,2047, 782,4084, 400, 251,4323, # 3248
-1624,5400,5401, 277,3742, 299,1265, 476,1191,3883,2122,4324,4325,1109, 205,5402, # 3264
-2590,1000,2157,3616,1861,5403,5404,5405,4678,5406,4679,2573, 107,2484,2158,4085, # 3280
-3507,3172,5407,1533, 541,1301, 158, 753,4326,2886,3617,5408,1696, 370,1088,4327, # 3296
-4680,3618, 579, 327, 440, 162,2244, 269,1938,1374,3508, 968,3063, 56,1396,3113, # 3312
-2107,3321,3391,5409,1927,2159,4681,3016,5410,3619,5411,5412,3743,4682,2485,5413, # 3328
-2804,5414,1650,4683,5415,2613,5416,5417,4086,2671,3392,1149,3393,4087,3884,4088, # 3344
-5418,1076, 49,5419, 951,3242,3322,3323, 450,2850, 920,5420,1812,2805,2371,4328, # 3360
-1909,1138,2372,3885,3509,5421,3243,4684,1910,1147,1518,2428,4685,3886,5422,4686, # 3376
-2393,2614, 260,1796,3244,5423,5424,3887,3324, 708,5425,3620,1704,5426,3621,1351, # 3392
-1618,3394,3017,1887, 944,4329,3395,4330,3064,3396,4331,5427,3744, 422, 413,1714, # 3408
-3325, 500,2059,2350,4332,2486,5428,1344,1911, 954,5429,1668,5430,5431,4089,2409, # 3424
-4333,3622,3888,4334,5432,2307,1318,2512,3114, 133,3115,2887,4687, 629, 31,2851, # 3440
-2706,3889,4688, 850, 949,4689,4090,2970,1732,2089,4335,1496,1853,5433,4091, 620, # 3456
-3245, 981,1242,3745,3397,1619,3746,1643,3326,2140,2457,1971,1719,3510,2169,5434, # 3472
-3246,5435,5436,3398,1829,5437,1277,4690,1565,2048,5438,1636,3623,3116,5439, 869, # 3488
-2852, 655,3890,3891,3117,4092,3018,3892,1310,3624,4691,5440,5441,5442,1733, 558, # 3504
-4692,3747, 335,1549,3065,1756,4336,3748,1946,3511,1830,1291,1192, 470,2735,2108, # 3520
-2806, 913,1054,4093,5443,1027,5444,3066,4094,4693, 982,2672,3399,3173,3512,3247, # 3536
-3248,1947,2807,5445, 571,4694,5446,1831,5447,3625,2591,1523,2429,5448,2090, 984, # 3552
-4695,3749,1960,5449,3750, 852, 923,2808,3513,3751, 969,1519, 999,2049,2325,1705, # 3568
-5450,3118, 615,1662, 151, 597,4095,2410,2326,1049, 275,4696,3752,4337, 568,3753, # 3584
-3626,2487,4338,3754,5451,2430,2275, 409,3249,5452,1566,2888,3514,1002, 769,2853, # 3600
- 194,2091,3174,3755,2226,3327,4339, 628,1505,5453,5454,1763,2180,3019,4096, 521, # 3616
-1161,2592,1788,2206,2411,4697,4097,1625,4340,4341, 412, 42,3119, 464,5455,2642, # 3632
-4698,3400,1760,1571,2889,3515,2537,1219,2207,3893,2643,2141,2373,4699,4700,3328, # 3648
-1651,3401,3627,5456,5457,3628,2488,3516,5458,3756,5459,5460,2276,2092, 460,5461, # 3664
-4701,5462,3020, 962, 588,3629, 289,3250,2644,1116, 52,5463,3067,1797,5464,5465, # 3680
-5466,1467,5467,1598,1143,3757,4342,1985,1734,1067,4702,1280,3402, 465,4703,1572, # 3696
- 510,5468,1928,2245,1813,1644,3630,5469,4704,3758,5470,5471,2673,1573,1534,5472, # 3712
-5473, 536,1808,1761,3517,3894,3175,2645,5474,5475,5476,4705,3518,2929,1912,2809, # 3728
-5477,3329,1122, 377,3251,5478, 360,5479,5480,4343,1529, 551,5481,2060,3759,1769, # 3744
-2431,5482,2930,4344,3330,3120,2327,2109,2031,4706,1404, 136,1468,1479, 672,1171, # 3760
-3252,2308, 271,3176,5483,2772,5484,2050, 678,2736, 865,1948,4707,5485,2014,4098, # 3776
-2971,5486,2737,2227,1397,3068,3760,4708,4709,1735,2931,3403,3631,5487,3895, 509, # 3792
-2854,2458,2890,3896,5488,5489,3177,3178,4710,4345,2538,4711,2309,1166,1010, 552, # 3808
- 681,1888,5490,5491,2972,2973,4099,1287,1596,1862,3179, 358, 453, 736, 175, 478, # 3824
-1117, 905,1167,1097,5492,1854,1530,5493,1706,5494,2181,3519,2292,3761,3520,3632, # 3840
-4346,2093,4347,5495,3404,1193,2489,4348,1458,2193,2208,1863,1889,1421,3331,2932, # 3856
-3069,2182,3521, 595,2123,5496,4100,5497,5498,4349,1707,2646, 223,3762,1359, 751, # 3872
-3121, 183,3522,5499,2810,3021, 419,2374, 633, 704,3897,2394, 241,5500,5501,5502, # 3888
- 838,3022,3763,2277,2773,2459,3898,1939,2051,4101,1309,3122,2246,1181,5503,1136, # 3904
-2209,3899,2375,1446,4350,2310,4712,5504,5505,4351,1055,2615, 484,3764,5506,4102, # 3920
- 625,4352,2278,3405,1499,4353,4103,5507,4104,4354,3253,2279,2280,3523,5508,5509, # 3936
-2774, 808,2616,3765,3406,4105,4355,3123,2539, 526,3407,3900,4356, 955,5510,1620, # 3952
-4357,2647,2432,5511,1429,3766,1669,1832, 994, 928,5512,3633,1260,5513,5514,5515, # 3968
-1949,2293, 741,2933,1626,4358,2738,2460, 867,1184, 362,3408,1392,5516,5517,4106, # 3984
-4359,1770,1736,3254,2934,4713,4714,1929,2707,1459,1158,5518,3070,3409,2891,1292, # 4000
-1930,2513,2855,3767,1986,1187,2072,2015,2617,4360,5519,2574,2514,2170,3768,2490, # 4016
-3332,5520,3769,4715,5521,5522, 666,1003,3023,1022,3634,4361,5523,4716,1814,2257, # 4032
- 574,3901,1603, 295,1535, 705,3902,4362, 283, 858, 417,5524,5525,3255,4717,4718, # 4048
-3071,1220,1890,1046,2281,2461,4107,1393,1599, 689,2575, 388,4363,5526,2491, 802, # 4064
-5527,2811,3903,2061,1405,2258,5528,4719,3904,2110,1052,1345,3256,1585,5529, 809, # 4080
-5530,5531,5532, 575,2739,3524, 956,1552,1469,1144,2328,5533,2329,1560,2462,3635, # 4096
-3257,4108, 616,2210,4364,3180,2183,2294,5534,1833,5535,3525,4720,5536,1319,3770, # 4112
-3771,1211,3636,1023,3258,1293,2812,5537,5538,5539,3905, 607,2311,3906, 762,2892, # 4128
-1439,4365,1360,4721,1485,3072,5540,4722,1038,4366,1450,2062,2648,4367,1379,4723, # 4144
-2593,5541,5542,4368,1352,1414,2330,2935,1172,5543,5544,3907,3908,4724,1798,1451, # 4160
-5545,5546,5547,5548,2936,4109,4110,2492,2351, 411,4111,4112,3637,3333,3124,4725, # 4176
-1561,2674,1452,4113,1375,5549,5550, 47,2974, 316,5551,1406,1591,2937,3181,5552, # 4192
-1025,2142,3125,3182, 354,2740, 884,2228,4369,2412, 508,3772, 726,3638, 996,2433, # 4208
-3639, 729,5553, 392,2194,1453,4114,4726,3773,5554,5555,2463,3640,2618,1675,2813, # 4224
- 919,2352,2975,2353,1270,4727,4115, 73,5556,5557, 647,5558,3259,2856,2259,1550, # 4240
-1346,3024,5559,1332, 883,3526,5560,5561,5562,5563,3334,2775,5564,1212, 831,1347, # 4256
-4370,4728,2331,3909,1864,3073, 720,3910,4729,4730,3911,5565,4371,5566,5567,4731, # 4272
-5568,5569,1799,4732,3774,2619,4733,3641,1645,2376,4734,5570,2938, 669,2211,2675, # 4288
-2434,5571,2893,5572,5573,1028,3260,5574,4372,2413,5575,2260,1353,5576,5577,4735, # 4304
-3183, 518,5578,4116,5579,4373,1961,5580,2143,4374,5581,5582,3025,2354,2355,3912, # 4320
- 516,1834,1454,4117,2708,4375,4736,2229,2620,1972,1129,3642,5583,2776,5584,2976, # 4336
-1422, 577,1470,3026,1524,3410,5585,5586, 432,4376,3074,3527,5587,2594,1455,2515, # 4352
-2230,1973,1175,5588,1020,2741,4118,3528,4737,5589,2742,5590,1743,1361,3075,3529, # 4368
-2649,4119,4377,4738,2295, 895, 924,4378,2171, 331,2247,3076, 166,1627,3077,1098, # 4384
-5591,1232,2894,2231,3411,4739, 657, 403,1196,2377, 542,3775,3412,1600,4379,3530, # 4400
-5592,4740,2777,3261, 576, 530,1362,4741,4742,2540,2676,3776,4120,5593, 842,3913, # 4416
-5594,2814,2032,1014,4121, 213,2709,3413, 665, 621,4380,5595,3777,2939,2435,5596, # 4432
-2436,3335,3643,3414,4743,4381,2541,4382,4744,3644,1682,4383,3531,1380,5597, 724, # 4448
-2282, 600,1670,5598,1337,1233,4745,3126,2248,5599,1621,4746,5600, 651,4384,5601, # 4464
-1612,4385,2621,5602,2857,5603,2743,2312,3078,5604, 716,2464,3079, 174,1255,2710, # 4480
-4122,3645, 548,1320,1398, 728,4123,1574,5605,1891,1197,3080,4124,5606,3081,3082, # 4496
-3778,3646,3779, 747,5607, 635,4386,4747,5608,5609,5610,4387,5611,5612,4748,5613, # 4512
-3415,4749,2437, 451,5614,3780,2542,2073,4388,2744,4389,4125,5615,1764,4750,5616, # 4528
-4390, 350,4751,2283,2395,2493,5617,4391,4126,2249,1434,4127, 488,4752, 458,4392, # 4544
-4128,3781, 771,1330,2396,3914,2576,3184,2160,2414,1553,2677,3185,4393,5618,2494, # 4560
-2895,2622,1720,2711,4394,3416,4753,5619,2543,4395,5620,3262,4396,2778,5621,2016, # 4576
-2745,5622,1155,1017,3782,3915,5623,3336,2313, 201,1865,4397,1430,5624,4129,5625, # 4592
-5626,5627,5628,5629,4398,1604,5630, 414,1866, 371,2595,4754,4755,3532,2017,3127, # 4608
-4756,1708, 960,4399, 887, 389,2172,1536,1663,1721,5631,2232,4130,2356,2940,1580, # 4624
-5632,5633,1744,4757,2544,4758,4759,5634,4760,5635,2074,5636,4761,3647,3417,2896, # 4640
-4400,5637,4401,2650,3418,2815, 673,2712,2465, 709,3533,4131,3648,4402,5638,1148, # 4656
- 502, 634,5639,5640,1204,4762,3649,1575,4763,2623,3783,5641,3784,3128, 948,3263, # 4672
- 121,1745,3916,1110,5642,4403,3083,2516,3027,4132,3785,1151,1771,3917,1488,4133, # 4688
-1987,5643,2438,3534,5644,5645,2094,5646,4404,3918,1213,1407,2816, 531,2746,2545, # 4704
-3264,1011,1537,4764,2779,4405,3129,1061,5647,3786,3787,1867,2897,5648,2018, 120, # 4720
-4406,4407,2063,3650,3265,2314,3919,2678,3419,1955,4765,4134,5649,3535,1047,2713, # 4736
-1266,5650,1368,4766,2858, 649,3420,3920,2546,2747,1102,2859,2679,5651,5652,2000, # 4752
-5653,1111,3651,2977,5654,2495,3921,3652,2817,1855,3421,3788,5655,5656,3422,2415, # 4768
-2898,3337,3266,3653,5657,2577,5658,3654,2818,4135,1460, 856,5659,3655,5660,2899, # 4784
-2978,5661,2900,3922,5662,4408, 632,2517, 875,3923,1697,3924,2296,5663,5664,4767, # 4800
-3028,1239, 580,4768,4409,5665, 914, 936,2075,1190,4136,1039,2124,5666,5667,5668, # 4816
-5669,3423,1473,5670,1354,4410,3925,4769,2173,3084,4137, 915,3338,4411,4412,3339, # 4832
-1605,1835,5671,2748, 398,3656,4413,3926,4138, 328,1913,2860,4139,3927,1331,4414, # 4848
-3029, 937,4415,5672,3657,4140,4141,3424,2161,4770,3425, 524, 742, 538,3085,1012, # 4864
-5673,5674,3928,2466,5675, 658,1103, 225,3929,5676,5677,4771,5678,4772,5679,3267, # 4880
-1243,5680,4142, 963,2250,4773,5681,2714,3658,3186,5682,5683,2596,2332,5684,4774, # 4896
-5685,5686,5687,3536, 957,3426,2547,2033,1931,2941,2467, 870,2019,3659,1746,2780, # 4912
-2781,2439,2468,5688,3930,5689,3789,3130,3790,3537,3427,3791,5690,1179,3086,5691, # 4928
-3187,2378,4416,3792,2548,3188,3131,2749,4143,5692,3428,1556,2549,2297, 977,2901, # 4944
-2034,4144,1205,3429,5693,1765,3430,3189,2125,1271, 714,1689,4775,3538,5694,2333, # 4960
-3931, 533,4417,3660,2184, 617,5695,2469,3340,3539,2315,5696,5697,3190,5698,5699, # 4976
-3932,1988, 618, 427,2651,3540,3431,5700,5701,1244,1690,5702,2819,4418,4776,5703, # 4992
-3541,4777,5704,2284,1576, 473,3661,4419,3432, 972,5705,3662,5706,3087,5707,5708, # 5008
-4778,4779,5709,3793,4145,4146,5710, 153,4780, 356,5711,1892,2902,4420,2144, 408, # 5024
- 803,2357,5712,3933,5713,4421,1646,2578,2518,4781,4782,3934,5714,3935,4422,5715, # 5040
-2416,3433, 752,5716,5717,1962,3341,2979,5718, 746,3030,2470,4783,4423,3794, 698, # 5056
-4784,1893,4424,3663,2550,4785,3664,3936,5719,3191,3434,5720,1824,1302,4147,2715, # 5072
-3937,1974,4425,5721,4426,3192, 823,1303,1288,1236,2861,3542,4148,3435, 774,3938, # 5088
-5722,1581,4786,1304,2862,3939,4787,5723,2440,2162,1083,3268,4427,4149,4428, 344, # 5104
-1173, 288,2316, 454,1683,5724,5725,1461,4788,4150,2597,5726,5727,4789, 985, 894, # 5120
-5728,3436,3193,5729,1914,2942,3795,1989,5730,2111,1975,5731,4151,5732,2579,1194, # 5136
- 425,5733,4790,3194,1245,3796,4429,5734,5735,2863,5736, 636,4791,1856,3940, 760, # 5152
-1800,5737,4430,2212,1508,4792,4152,1894,1684,2298,5738,5739,4793,4431,4432,2213, # 5168
- 479,5740,5741, 832,5742,4153,2496,5743,2980,2497,3797, 990,3132, 627,1815,2652, # 5184
-4433,1582,4434,2126,2112,3543,4794,5744, 799,4435,3195,5745,4795,2113,1737,3031, # 5200
-1018, 543, 754,4436,3342,1676,4796,4797,4154,4798,1489,5746,3544,5747,2624,2903, # 5216
-4155,5748,5749,2981,5750,5751,5752,5753,3196,4799,4800,2185,1722,5754,3269,3270, # 5232
-1843,3665,1715, 481, 365,1976,1857,5755,5756,1963,2498,4801,5757,2127,3666,3271, # 5248
- 433,1895,2064,2076,5758, 602,2750,5759,5760,5761,5762,5763,3032,1628,3437,5764, # 5264
-3197,4802,4156,2904,4803,2519,5765,2551,2782,5766,5767,5768,3343,4804,2905,5769, # 5280
-4805,5770,2864,4806,4807,1221,2982,4157,2520,5771,5772,5773,1868,1990,5774,5775, # 5296
-5776,1896,5777,5778,4808,1897,4158, 318,5779,2095,4159,4437,5780,5781, 485,5782, # 5312
- 938,3941, 553,2680, 116,5783,3942,3667,5784,3545,2681,2783,3438,3344,2820,5785, # 5328
-3668,2943,4160,1747,2944,2983,5786,5787, 207,5788,4809,5789,4810,2521,5790,3033, # 5344
- 890,3669,3943,5791,1878,3798,3439,5792,2186,2358,3440,1652,5793,5794,5795, 941, # 5360
-2299, 208,3546,4161,2020, 330,4438,3944,2906,2499,3799,4439,4811,5796,5797,5798, # 5376
-)
-# fmt: on
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/big5prober.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/big5prober.py
deleted file mode 100644
index ef09c60..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/big5prober.py
+++ /dev/null
@@ -1,47 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is Mozilla Communicator client code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-from .chardistribution import Big5DistributionAnalysis
-from .codingstatemachine import CodingStateMachine
-from .mbcharsetprober import MultiByteCharSetProber
-from .mbcssm import BIG5_SM_MODEL
-
-
-class Big5Prober(MultiByteCharSetProber):
- def __init__(self) -> None:
- super().__init__()
- self.coding_sm = CodingStateMachine(BIG5_SM_MODEL)
- self.distribution_analyzer = Big5DistributionAnalysis()
- self.reset()
-
- @property
- def charset_name(self) -> str:
- return "Big5"
-
- @property
- def language(self) -> str:
- return "Chinese"
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/chardistribution.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/chardistribution.py
deleted file mode 100644
index 176cb99..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/chardistribution.py
+++ /dev/null
@@ -1,261 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is Mozilla Communicator client code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-from typing import Tuple, Union
-
-from .big5freq import (
- BIG5_CHAR_TO_FREQ_ORDER,
- BIG5_TABLE_SIZE,
- BIG5_TYPICAL_DISTRIBUTION_RATIO,
-)
-from .euckrfreq import (
- EUCKR_CHAR_TO_FREQ_ORDER,
- EUCKR_TABLE_SIZE,
- EUCKR_TYPICAL_DISTRIBUTION_RATIO,
-)
-from .euctwfreq import (
- EUCTW_CHAR_TO_FREQ_ORDER,
- EUCTW_TABLE_SIZE,
- EUCTW_TYPICAL_DISTRIBUTION_RATIO,
-)
-from .gb2312freq import (
- GB2312_CHAR_TO_FREQ_ORDER,
- GB2312_TABLE_SIZE,
- GB2312_TYPICAL_DISTRIBUTION_RATIO,
-)
-from .jisfreq import (
- JIS_CHAR_TO_FREQ_ORDER,
- JIS_TABLE_SIZE,
- JIS_TYPICAL_DISTRIBUTION_RATIO,
-)
-from .johabfreq import JOHAB_TO_EUCKR_ORDER_TABLE
-
-
-class CharDistributionAnalysis:
- ENOUGH_DATA_THRESHOLD = 1024
- SURE_YES = 0.99
- SURE_NO = 0.01
- MINIMUM_DATA_THRESHOLD = 3
-
- def __init__(self) -> None:
- # Mapping table to get frequency order from char order (get from
- # GetOrder())
- self._char_to_freq_order: Tuple[int, ...] = tuple()
- self._table_size = 0 # Size of above table
- # This is a constant value which varies from language to language,
- # used in calculating confidence. See
- # http://www.mozilla.org/projects/intl/UniversalCharsetDetection.html
- # for further detail.
- self.typical_distribution_ratio = 0.0
- self._done = False
- self._total_chars = 0
- self._freq_chars = 0
- self.reset()
-
- def reset(self) -> None:
- """reset analyser, clear any state"""
- # If this flag is set to True, detection is done and conclusion has
- # been made
- self._done = False
- self._total_chars = 0 # Total characters encountered
- # The number of characters whose frequency order is less than 512
- self._freq_chars = 0
-
- def feed(self, char: Union[bytes, bytearray], char_len: int) -> None:
- """feed a character with known length"""
- if char_len == 2:
- # we only care about 2-bytes character in our distribution analysis
- order = self.get_order(char)
- else:
- order = -1
- if order >= 0:
- self._total_chars += 1
- # order is valid
- if order < self._table_size:
- if 512 > self._char_to_freq_order[order]:
- self._freq_chars += 1
-
- def get_confidence(self) -> float:
- """return confidence based on existing data"""
- # if we didn't receive any character in our consideration range,
- # return negative answer
- if self._total_chars <= 0 or self._freq_chars <= self.MINIMUM_DATA_THRESHOLD:
- return self.SURE_NO
-
- if self._total_chars != self._freq_chars:
- r = self._freq_chars / (
- (self._total_chars - self._freq_chars) * self.typical_distribution_ratio
- )
- if r < self.SURE_YES:
- return r
-
- # normalize confidence (we don't want to be 100% sure)
- return self.SURE_YES
-
- def got_enough_data(self) -> bool:
- # It is not necessary to receive all data to draw conclusion.
- # For charset detection, certain amount of data is enough
- return self._total_chars > self.ENOUGH_DATA_THRESHOLD
-
- def get_order(self, _: Union[bytes, bytearray]) -> int:
- # We do not handle characters based on the original encoding string,
- # but convert this encoding string to a number, here called order.
- # This allows multiple encodings of a language to share one frequency
- # table.
- return -1
-
-
-class EUCTWDistributionAnalysis(CharDistributionAnalysis):
- def __init__(self) -> None:
- super().__init__()
- self._char_to_freq_order = EUCTW_CHAR_TO_FREQ_ORDER
- self._table_size = EUCTW_TABLE_SIZE
- self.typical_distribution_ratio = EUCTW_TYPICAL_DISTRIBUTION_RATIO
-
- def get_order(self, byte_str: Union[bytes, bytearray]) -> int:
- # for euc-TW encoding, we are interested
- # first byte range: 0xc4 -- 0xfe
- # second byte range: 0xa1 -- 0xfe
- # no validation needed here. State machine has done that
- first_char = byte_str[0]
- if first_char >= 0xC4:
- return 94 * (first_char - 0xC4) + byte_str[1] - 0xA1
- return -1
-
-
-class EUCKRDistributionAnalysis(CharDistributionAnalysis):
- def __init__(self) -> None:
- super().__init__()
- self._char_to_freq_order = EUCKR_CHAR_TO_FREQ_ORDER
- self._table_size = EUCKR_TABLE_SIZE
- self.typical_distribution_ratio = EUCKR_TYPICAL_DISTRIBUTION_RATIO
-
- def get_order(self, byte_str: Union[bytes, bytearray]) -> int:
- # for euc-KR encoding, we are interested
- # first byte range: 0xb0 -- 0xfe
- # second byte range: 0xa1 -- 0xfe
- # no validation needed here. State machine has done that
- first_char = byte_str[0]
- if first_char >= 0xB0:
- return 94 * (first_char - 0xB0) + byte_str[1] - 0xA1
- return -1
-
-
-class JOHABDistributionAnalysis(CharDistributionAnalysis):
- def __init__(self) -> None:
- super().__init__()
- self._char_to_freq_order = EUCKR_CHAR_TO_FREQ_ORDER
- self._table_size = EUCKR_TABLE_SIZE
- self.typical_distribution_ratio = EUCKR_TYPICAL_DISTRIBUTION_RATIO
-
- def get_order(self, byte_str: Union[bytes, bytearray]) -> int:
- first_char = byte_str[0]
- if 0x88 <= first_char < 0xD4:
- code = first_char * 256 + byte_str[1]
- return JOHAB_TO_EUCKR_ORDER_TABLE.get(code, -1)
- return -1
-
-
-class GB2312DistributionAnalysis(CharDistributionAnalysis):
- def __init__(self) -> None:
- super().__init__()
- self._char_to_freq_order = GB2312_CHAR_TO_FREQ_ORDER
- self._table_size = GB2312_TABLE_SIZE
- self.typical_distribution_ratio = GB2312_TYPICAL_DISTRIBUTION_RATIO
-
- def get_order(self, byte_str: Union[bytes, bytearray]) -> int:
- # for GB2312 encoding, we are interested
- # first byte range: 0xb0 -- 0xfe
- # second byte range: 0xa1 -- 0xfe
- # no validation needed here. State machine has done that
- first_char, second_char = byte_str[0], byte_str[1]
- if (first_char >= 0xB0) and (second_char >= 0xA1):
- return 94 * (first_char - 0xB0) + second_char - 0xA1
- return -1
-
-
-class Big5DistributionAnalysis(CharDistributionAnalysis):
- def __init__(self) -> None:
- super().__init__()
- self._char_to_freq_order = BIG5_CHAR_TO_FREQ_ORDER
- self._table_size = BIG5_TABLE_SIZE
- self.typical_distribution_ratio = BIG5_TYPICAL_DISTRIBUTION_RATIO
-
- def get_order(self, byte_str: Union[bytes, bytearray]) -> int:
- # for big5 encoding, we are interested
- # first byte range: 0xa4 -- 0xfe
- # second byte range: 0x40 -- 0x7e , 0xa1 -- 0xfe
- # no validation needed here. State machine has done that
- first_char, second_char = byte_str[0], byte_str[1]
- if first_char >= 0xA4:
- if second_char >= 0xA1:
- return 157 * (first_char - 0xA4) + second_char - 0xA1 + 63
- return 157 * (first_char - 0xA4) + second_char - 0x40
- return -1
-
-
-class SJISDistributionAnalysis(CharDistributionAnalysis):
- def __init__(self) -> None:
- super().__init__()
- self._char_to_freq_order = JIS_CHAR_TO_FREQ_ORDER
- self._table_size = JIS_TABLE_SIZE
- self.typical_distribution_ratio = JIS_TYPICAL_DISTRIBUTION_RATIO
-
- def get_order(self, byte_str: Union[bytes, bytearray]) -> int:
- # for sjis encoding, we are interested
- # first byte range: 0x81 -- 0x9f , 0xe0 -- 0xfe
- # second byte range: 0x40 -- 0x7e, 0x81 -- oxfe
- # no validation needed here. State machine has done that
- first_char, second_char = byte_str[0], byte_str[1]
- if 0x81 <= first_char <= 0x9F:
- order = 188 * (first_char - 0x81)
- elif 0xE0 <= first_char <= 0xEF:
- order = 188 * (first_char - 0xE0 + 31)
- else:
- return -1
- order = order + second_char - 0x40
- if second_char > 0x7F:
- order = -1
- return order
-
-
-class EUCJPDistributionAnalysis(CharDistributionAnalysis):
- def __init__(self) -> None:
- super().__init__()
- self._char_to_freq_order = JIS_CHAR_TO_FREQ_ORDER
- self._table_size = JIS_TABLE_SIZE
- self.typical_distribution_ratio = JIS_TYPICAL_DISTRIBUTION_RATIO
-
- def get_order(self, byte_str: Union[bytes, bytearray]) -> int:
- # for euc-JP encoding, we are interested
- # first byte range: 0xa0 -- 0xfe
- # second byte range: 0xa1 -- 0xfe
- # no validation needed here. State machine has done that
- char = byte_str[0]
- if char >= 0xA0:
- return 94 * (char - 0xA1) + byte_str[1] - 0xA1
- return -1
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/charsetgroupprober.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/charsetgroupprober.py
deleted file mode 100644
index 6def56b..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/charsetgroupprober.py
+++ /dev/null
@@ -1,106 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is Mozilla Communicator client code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-from typing import List, Optional, Union
-
-from .charsetprober import CharSetProber
-from .enums import LanguageFilter, ProbingState
-
-
-class CharSetGroupProber(CharSetProber):
- def __init__(self, lang_filter: LanguageFilter = LanguageFilter.NONE) -> None:
- super().__init__(lang_filter=lang_filter)
- self._active_num = 0
- self.probers: List[CharSetProber] = []
- self._best_guess_prober: Optional[CharSetProber] = None
-
- def reset(self) -> None:
- super().reset()
- self._active_num = 0
- for prober in self.probers:
- prober.reset()
- prober.active = True
- self._active_num += 1
- self._best_guess_prober = None
-
- @property
- def charset_name(self) -> Optional[str]:
- if not self._best_guess_prober:
- self.get_confidence()
- if not self._best_guess_prober:
- return None
- return self._best_guess_prober.charset_name
-
- @property
- def language(self) -> Optional[str]:
- if not self._best_guess_prober:
- self.get_confidence()
- if not self._best_guess_prober:
- return None
- return self._best_guess_prober.language
-
- def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState:
- for prober in self.probers:
- if not prober.active:
- continue
- state = prober.feed(byte_str)
- if not state:
- continue
- if state == ProbingState.FOUND_IT:
- self._best_guess_prober = prober
- self._state = ProbingState.FOUND_IT
- return self.state
- if state == ProbingState.NOT_ME:
- prober.active = False
- self._active_num -= 1
- if self._active_num <= 0:
- self._state = ProbingState.NOT_ME
- return self.state
- return self.state
-
- def get_confidence(self) -> float:
- state = self.state
- if state == ProbingState.FOUND_IT:
- return 0.99
- if state == ProbingState.NOT_ME:
- return 0.01
- best_conf = 0.0
- self._best_guess_prober = None
- for prober in self.probers:
- if not prober.active:
- self.logger.debug("%s not active", prober.charset_name)
- continue
- conf = prober.get_confidence()
- self.logger.debug(
- "%s %s confidence = %s", prober.charset_name, prober.language, conf
- )
- if best_conf < conf:
- best_conf = conf
- self._best_guess_prober = prober
- if not self._best_guess_prober:
- return 0.0
- return best_conf
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/charsetprober.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/charsetprober.py
deleted file mode 100644
index a103ca1..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/charsetprober.py
+++ /dev/null
@@ -1,147 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is Mozilla Universal charset detector code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 2001
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-# Shy Shalom - original C code
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-import logging
-import re
-from typing import Optional, Union
-
-from .enums import LanguageFilter, ProbingState
-
-INTERNATIONAL_WORDS_PATTERN = re.compile(
- b"[a-zA-Z]*[\x80-\xFF]+[a-zA-Z]*[^a-zA-Z\x80-\xFF]?"
-)
-
-
-class CharSetProber:
-
- SHORTCUT_THRESHOLD = 0.95
-
- def __init__(self, lang_filter: LanguageFilter = LanguageFilter.NONE) -> None:
- self._state = ProbingState.DETECTING
- self.active = True
- self.lang_filter = lang_filter
- self.logger = logging.getLogger(__name__)
-
- def reset(self) -> None:
- self._state = ProbingState.DETECTING
-
- @property
- def charset_name(self) -> Optional[str]:
- return None
-
- @property
- def language(self) -> Optional[str]:
- raise NotImplementedError
-
- def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState:
- raise NotImplementedError
-
- @property
- def state(self) -> ProbingState:
- return self._state
-
- def get_confidence(self) -> float:
- return 0.0
-
- @staticmethod
- def filter_high_byte_only(buf: Union[bytes, bytearray]) -> bytes:
- buf = re.sub(b"([\x00-\x7F])+", b" ", buf)
- return buf
-
- @staticmethod
- def filter_international_words(buf: Union[bytes, bytearray]) -> bytearray:
- """
- We define three types of bytes:
- alphabet: english alphabets [a-zA-Z]
- international: international characters [\x80-\xFF]
- marker: everything else [^a-zA-Z\x80-\xFF]
- The input buffer can be thought to contain a series of words delimited
- by markers. This function works to filter all words that contain at
- least one international character. All contiguous sequences of markers
- are replaced by a single space ascii character.
- This filter applies to all scripts which do not use English characters.
- """
- filtered = bytearray()
-
- # This regex expression filters out only words that have at-least one
- # international character. The word may include one marker character at
- # the end.
- words = INTERNATIONAL_WORDS_PATTERN.findall(buf)
-
- for word in words:
- filtered.extend(word[:-1])
-
- # If the last character in the word is a marker, replace it with a
- # space as markers shouldn't affect our analysis (they are used
- # similarly across all languages and may thus have similar
- # frequencies).
- last_char = word[-1:]
- if not last_char.isalpha() and last_char < b"\x80":
- last_char = b" "
- filtered.extend(last_char)
-
- return filtered
-
- @staticmethod
- def remove_xml_tags(buf: Union[bytes, bytearray]) -> bytes:
- """
- Returns a copy of ``buf`` that retains only the sequences of English
- alphabet and high byte characters that are not between <> characters.
- This filter can be applied to all scripts which contain both English
- characters and extended ASCII characters, but is currently only used by
- ``Latin1Prober``.
- """
- filtered = bytearray()
- in_tag = False
- prev = 0
- buf = memoryview(buf).cast("c")
-
- for curr, buf_char in enumerate(buf):
- # Check if we're coming out of or entering an XML tag
-
- # https://github.com/python/typeshed/issues/8182
- if buf_char == b">": # type: ignore[comparison-overlap]
- prev = curr + 1
- in_tag = False
- # https://github.com/python/typeshed/issues/8182
- elif buf_char == b"<": # type: ignore[comparison-overlap]
- if curr > prev and not in_tag:
- # Keep everything after last non-extended-ASCII,
- # non-alphabetic character
- filtered.extend(buf[prev:curr])
- # Output a space to delimit stretch we kept
- filtered.extend(b" ")
- in_tag = True
-
- # If we're not in a tag...
- if not in_tag:
- # Keep everything after last non-extended-ASCII, non-alphabetic
- # character
- filtered.extend(buf[prev:])
-
- return filtered
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/cli/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/cli/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/cli/chardetect.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/cli/chardetect.py
deleted file mode 100644
index 43f6e14..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/cli/chardetect.py
+++ /dev/null
@@ -1,112 +0,0 @@
-"""
-Script which takes one or more file paths and reports on their detected
-encodings
-
-Example::
-
- % chardetect somefile someotherfile
- somefile: windows-1252 with confidence 0.5
- someotherfile: ascii with confidence 1.0
-
-If no paths are provided, it takes its input from stdin.
-
-"""
-
-
-import argparse
-import sys
-from typing import Iterable, List, Optional
-
-from .. import __version__
-from ..universaldetector import UniversalDetector
-
-
-def description_of(
- lines: Iterable[bytes],
- name: str = "stdin",
- minimal: bool = False,
- should_rename_legacy: bool = False,
-) -> Optional[str]:
- """
- Return a string describing the probable encoding of a file or
- list of strings.
-
- :param lines: The lines to get the encoding of.
- :type lines: Iterable of bytes
- :param name: Name of file or collection of lines
- :type name: str
- :param should_rename_legacy: Should we rename legacy encodings to
- their more modern equivalents?
- :type should_rename_legacy: ``bool``
- """
- u = UniversalDetector(should_rename_legacy=should_rename_legacy)
- for line in lines:
- line = bytearray(line)
- u.feed(line)
- # shortcut out of the loop to save reading further - particularly useful if we read a BOM.
- if u.done:
- break
- u.close()
- result = u.result
- if minimal:
- return result["encoding"]
- if result["encoding"]:
- return f'{name}: {result["encoding"]} with confidence {result["confidence"]}'
- return f"{name}: no result"
-
-
-def main(argv: Optional[List[str]] = None) -> None:
- """
- Handles command line arguments and gets things started.
-
- :param argv: List of arguments, as if specified on the command-line.
- If None, ``sys.argv[1:]`` is used instead.
- :type argv: list of str
- """
- # Get command line arguments
- parser = argparse.ArgumentParser(
- description=(
- "Takes one or more file paths and reports their detected encodings"
- )
- )
- parser.add_argument(
- "input",
- help="File whose encoding we would like to determine. (default: stdin)",
- type=argparse.FileType("rb"),
- nargs="*",
- default=[sys.stdin.buffer],
- )
- parser.add_argument(
- "--minimal",
- help="Print only the encoding to standard output",
- action="store_true",
- )
- parser.add_argument(
- "-l",
- "--legacy",
- help="Rename legacy encodings to more modern ones.",
- action="store_true",
- )
- parser.add_argument(
- "--version", action="version", version=f"%(prog)s {__version__}"
- )
- args = parser.parse_args(argv)
-
- for f in args.input:
- if f.isatty():
- print(
- "You are running chardetect interactively. Press "
- "CTRL-D twice at the start of a blank line to signal the "
- "end of your input. If you want help, run chardetect "
- "--help\n",
- file=sys.stderr,
- )
- print(
- description_of(
- f, f.name, minimal=args.minimal, should_rename_legacy=args.legacy
- )
- )
-
-
-if __name__ == "__main__":
- main()
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/codingstatemachine.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/codingstatemachine.py
deleted file mode 100644
index 8ed4a87..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/codingstatemachine.py
+++ /dev/null
@@ -1,90 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is mozilla.org code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-import logging
-
-from .codingstatemachinedict import CodingStateMachineDict
-from .enums import MachineState
-
-
-class CodingStateMachine:
- """
- A state machine to verify a byte sequence for a particular encoding. For
- each byte the detector receives, it will feed that byte to every active
- state machine available, one byte at a time. The state machine changes its
- state based on its previous state and the byte it receives. There are 3
- states in a state machine that are of interest to an auto-detector:
-
- START state: This is the state to start with, or a legal byte sequence
- (i.e. a valid code point) for character has been identified.
-
- ME state: This indicates that the state machine identified a byte sequence
- that is specific to the charset it is designed for and that
- there is no other possible encoding which can contain this byte
- sequence. This will to lead to an immediate positive answer for
- the detector.
-
- ERROR state: This indicates the state machine identified an illegal byte
- sequence for that encoding. This will lead to an immediate
- negative answer for this encoding. Detector will exclude this
- encoding from consideration from here on.
- """
-
- def __init__(self, sm: CodingStateMachineDict) -> None:
- self._model = sm
- self._curr_byte_pos = 0
- self._curr_char_len = 0
- self._curr_state = MachineState.START
- self.active = True
- self.logger = logging.getLogger(__name__)
- self.reset()
-
- def reset(self) -> None:
- self._curr_state = MachineState.START
-
- def next_state(self, c: int) -> int:
- # for each byte we get its class
- # if it is first byte, we also get byte length
- byte_class = self._model["class_table"][c]
- if self._curr_state == MachineState.START:
- self._curr_byte_pos = 0
- self._curr_char_len = self._model["char_len_table"][byte_class]
- # from byte's class and state_table, we get its next state
- curr_state = self._curr_state * self._model["class_factor"] + byte_class
- self._curr_state = self._model["state_table"][curr_state]
- self._curr_byte_pos += 1
- return self._curr_state
-
- def get_current_charlen(self) -> int:
- return self._curr_char_len
-
- def get_coding_state_machine(self) -> str:
- return self._model["name"]
-
- @property
- def language(self) -> str:
- return self._model["language"]
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/codingstatemachinedict.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/codingstatemachinedict.py
deleted file mode 100644
index 7a3c4c7..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/codingstatemachinedict.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from typing import TYPE_CHECKING, Tuple
-
-if TYPE_CHECKING:
- # TypedDict was introduced in Python 3.8.
- #
- # TODO: Remove the else block and TYPE_CHECKING check when dropping support
- # for Python 3.7.
- from typing import TypedDict
-
- class CodingStateMachineDict(TypedDict, total=False):
- class_table: Tuple[int, ...]
- class_factor: int
- state_table: Tuple[int, ...]
- char_len_table: Tuple[int, ...]
- name: str
- language: str # Optional key
-
-else:
- CodingStateMachineDict = dict
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/cp949prober.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/cp949prober.py
deleted file mode 100644
index fa7307e..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/cp949prober.py
+++ /dev/null
@@ -1,49 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is mozilla.org code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-from .chardistribution import EUCKRDistributionAnalysis
-from .codingstatemachine import CodingStateMachine
-from .mbcharsetprober import MultiByteCharSetProber
-from .mbcssm import CP949_SM_MODEL
-
-
-class CP949Prober(MultiByteCharSetProber):
- def __init__(self) -> None:
- super().__init__()
- self.coding_sm = CodingStateMachine(CP949_SM_MODEL)
- # NOTE: CP949 is a superset of EUC-KR, so the distribution should be
- # not different.
- self.distribution_analyzer = EUCKRDistributionAnalysis()
- self.reset()
-
- @property
- def charset_name(self) -> str:
- return "CP949"
-
- @property
- def language(self) -> str:
- return "Korean"
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/enums.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/enums.py
deleted file mode 100644
index 5e3e198..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/enums.py
+++ /dev/null
@@ -1,85 +0,0 @@
-"""
-All of the Enums that are used throughout the chardet package.
-
-:author: Dan Blanchard (dan.blanchard@gmail.com)
-"""
-
-from enum import Enum, Flag
-
-
-class InputState:
- """
- This enum represents the different states a universal detector can be in.
- """
-
- PURE_ASCII = 0
- ESC_ASCII = 1
- HIGH_BYTE = 2
-
-
-class LanguageFilter(Flag):
- """
- This enum represents the different language filters we can apply to a
- ``UniversalDetector``.
- """
-
- NONE = 0x00
- CHINESE_SIMPLIFIED = 0x01
- CHINESE_TRADITIONAL = 0x02
- JAPANESE = 0x04
- KOREAN = 0x08
- NON_CJK = 0x10
- ALL = 0x1F
- CHINESE = CHINESE_SIMPLIFIED | CHINESE_TRADITIONAL
- CJK = CHINESE | JAPANESE | KOREAN
-
-
-class ProbingState(Enum):
- """
- This enum represents the different states a prober can be in.
- """
-
- DETECTING = 0
- FOUND_IT = 1
- NOT_ME = 2
-
-
-class MachineState:
- """
- This enum represents the different states a state machine can be in.
- """
-
- START = 0
- ERROR = 1
- ITS_ME = 2
-
-
-class SequenceLikelihood:
- """
- This enum represents the likelihood of a character following the previous one.
- """
-
- NEGATIVE = 0
- UNLIKELY = 1
- LIKELY = 2
- POSITIVE = 3
-
- @classmethod
- def get_num_categories(cls) -> int:
- """:returns: The number of likelihood categories in the enum."""
- return 4
-
-
-class CharacterCategory:
- """
- This enum represents the different categories language models for
- ``SingleByteCharsetProber`` put characters into.
-
- Anything less than CONTROL is considered a letter.
- """
-
- UNDEFINED = 255
- LINE_BREAK = 254
- SYMBOL = 253
- DIGIT = 252
- CONTROL = 251
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/escprober.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/escprober.py
deleted file mode 100644
index fd71383..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/escprober.py
+++ /dev/null
@@ -1,102 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is mozilla.org code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-from typing import Optional, Union
-
-from .charsetprober import CharSetProber
-from .codingstatemachine import CodingStateMachine
-from .enums import LanguageFilter, MachineState, ProbingState
-from .escsm import (
- HZ_SM_MODEL,
- ISO2022CN_SM_MODEL,
- ISO2022JP_SM_MODEL,
- ISO2022KR_SM_MODEL,
-)
-
-
-class EscCharSetProber(CharSetProber):
- """
- This CharSetProber uses a "code scheme" approach for detecting encodings,
- whereby easily recognizable escape or shift sequences are relied on to
- identify these encodings.
- """
-
- def __init__(self, lang_filter: LanguageFilter = LanguageFilter.NONE) -> None:
- super().__init__(lang_filter=lang_filter)
- self.coding_sm = []
- if self.lang_filter & LanguageFilter.CHINESE_SIMPLIFIED:
- self.coding_sm.append(CodingStateMachine(HZ_SM_MODEL))
- self.coding_sm.append(CodingStateMachine(ISO2022CN_SM_MODEL))
- if self.lang_filter & LanguageFilter.JAPANESE:
- self.coding_sm.append(CodingStateMachine(ISO2022JP_SM_MODEL))
- if self.lang_filter & LanguageFilter.KOREAN:
- self.coding_sm.append(CodingStateMachine(ISO2022KR_SM_MODEL))
- self.active_sm_count = 0
- self._detected_charset: Optional[str] = None
- self._detected_language: Optional[str] = None
- self._state = ProbingState.DETECTING
- self.reset()
-
- def reset(self) -> None:
- super().reset()
- for coding_sm in self.coding_sm:
- coding_sm.active = True
- coding_sm.reset()
- self.active_sm_count = len(self.coding_sm)
- self._detected_charset = None
- self._detected_language = None
-
- @property
- def charset_name(self) -> Optional[str]:
- return self._detected_charset
-
- @property
- def language(self) -> Optional[str]:
- return self._detected_language
-
- def get_confidence(self) -> float:
- return 0.99 if self._detected_charset else 0.00
-
- def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState:
- for c in byte_str:
- for coding_sm in self.coding_sm:
- if not coding_sm.active:
- continue
- coding_state = coding_sm.next_state(c)
- if coding_state == MachineState.ERROR:
- coding_sm.active = False
- self.active_sm_count -= 1
- if self.active_sm_count <= 0:
- self._state = ProbingState.NOT_ME
- return self.state
- elif coding_state == MachineState.ITS_ME:
- self._state = ProbingState.FOUND_IT
- self._detected_charset = coding_sm.get_coding_state_machine()
- self._detected_language = coding_sm.language
- return self.state
-
- return self.state
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/escsm.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/escsm.py
deleted file mode 100644
index 11d4adf..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/escsm.py
+++ /dev/null
@@ -1,261 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is mozilla.org code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-from .codingstatemachinedict import CodingStateMachineDict
-from .enums import MachineState
-
-# fmt: off
-HZ_CLS = (
- 1, 0, 0, 0, 0, 0, 0, 0, # 00 - 07
- 0, 0, 0, 0, 0, 0, 0, 0, # 08 - 0f
- 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17
- 0, 0, 0, 1, 0, 0, 0, 0, # 18 - 1f
- 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 27
- 0, 0, 0, 0, 0, 0, 0, 0, # 28 - 2f
- 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37
- 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f
- 0, 0, 0, 0, 0, 0, 0, 0, # 40 - 47
- 0, 0, 0, 0, 0, 0, 0, 0, # 48 - 4f
- 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57
- 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f
- 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67
- 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f
- 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77
- 0, 0, 0, 4, 0, 5, 2, 0, # 78 - 7f
- 1, 1, 1, 1, 1, 1, 1, 1, # 80 - 87
- 1, 1, 1, 1, 1, 1, 1, 1, # 88 - 8f
- 1, 1, 1, 1, 1, 1, 1, 1, # 90 - 97
- 1, 1, 1, 1, 1, 1, 1, 1, # 98 - 9f
- 1, 1, 1, 1, 1, 1, 1, 1, # a0 - a7
- 1, 1, 1, 1, 1, 1, 1, 1, # a8 - af
- 1, 1, 1, 1, 1, 1, 1, 1, # b0 - b7
- 1, 1, 1, 1, 1, 1, 1, 1, # b8 - bf
- 1, 1, 1, 1, 1, 1, 1, 1, # c0 - c7
- 1, 1, 1, 1, 1, 1, 1, 1, # c8 - cf
- 1, 1, 1, 1, 1, 1, 1, 1, # d0 - d7
- 1, 1, 1, 1, 1, 1, 1, 1, # d8 - df
- 1, 1, 1, 1, 1, 1, 1, 1, # e0 - e7
- 1, 1, 1, 1, 1, 1, 1, 1, # e8 - ef
- 1, 1, 1, 1, 1, 1, 1, 1, # f0 - f7
- 1, 1, 1, 1, 1, 1, 1, 1, # f8 - ff
-)
-
-HZ_ST = (
-MachineState.START, MachineState.ERROR, 3, MachineState.START, MachineState.START, MachineState.START, MachineState.ERROR, MachineState.ERROR, # 00-07
-MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, # 08-0f
-MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.START, MachineState.START, 4, MachineState.ERROR, # 10-17
- 5, MachineState.ERROR, 6, MachineState.ERROR, 5, 5, 4, MachineState.ERROR, # 18-1f
- 4, MachineState.ERROR, 4, 4, 4, MachineState.ERROR, 4, MachineState.ERROR, # 20-27
- 4, MachineState.ITS_ME, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, # 28-2f
-)
-# fmt: on
-
-HZ_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0)
-
-HZ_SM_MODEL: CodingStateMachineDict = {
- "class_table": HZ_CLS,
- "class_factor": 6,
- "state_table": HZ_ST,
- "char_len_table": HZ_CHAR_LEN_TABLE,
- "name": "HZ-GB-2312",
- "language": "Chinese",
-}
-
-# fmt: off
-ISO2022CN_CLS = (
- 2, 0, 0, 0, 0, 0, 0, 0, # 00 - 07
- 0, 0, 0, 0, 0, 0, 0, 0, # 08 - 0f
- 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17
- 0, 0, 0, 1, 0, 0, 0, 0, # 18 - 1f
- 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 27
- 0, 3, 0, 0, 0, 0, 0, 0, # 28 - 2f
- 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37
- 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f
- 0, 0, 0, 4, 0, 0, 0, 0, # 40 - 47
- 0, 0, 0, 0, 0, 0, 0, 0, # 48 - 4f
- 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57
- 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f
- 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67
- 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f
- 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77
- 0, 0, 0, 0, 0, 0, 0, 0, # 78 - 7f
- 2, 2, 2, 2, 2, 2, 2, 2, # 80 - 87
- 2, 2, 2, 2, 2, 2, 2, 2, # 88 - 8f
- 2, 2, 2, 2, 2, 2, 2, 2, # 90 - 97
- 2, 2, 2, 2, 2, 2, 2, 2, # 98 - 9f
- 2, 2, 2, 2, 2, 2, 2, 2, # a0 - a7
- 2, 2, 2, 2, 2, 2, 2, 2, # a8 - af
- 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7
- 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf
- 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7
- 2, 2, 2, 2, 2, 2, 2, 2, # c8 - cf
- 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7
- 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df
- 2, 2, 2, 2, 2, 2, 2, 2, # e0 - e7
- 2, 2, 2, 2, 2, 2, 2, 2, # e8 - ef
- 2, 2, 2, 2, 2, 2, 2, 2, # f0 - f7
- 2, 2, 2, 2, 2, 2, 2, 2, # f8 - ff
-)
-
-ISO2022CN_ST = (
- MachineState.START, 3, MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, # 00-07
- MachineState.START, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 08-0f
- MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, # 10-17
- MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 4, MachineState.ERROR, # 18-1f
- MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 20-27
- 5, 6, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 28-2f
- MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 30-37
- MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, MachineState.START, # 38-3f
-)
-# fmt: on
-
-ISO2022CN_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0, 0, 0, 0)
-
-ISO2022CN_SM_MODEL: CodingStateMachineDict = {
- "class_table": ISO2022CN_CLS,
- "class_factor": 9,
- "state_table": ISO2022CN_ST,
- "char_len_table": ISO2022CN_CHAR_LEN_TABLE,
- "name": "ISO-2022-CN",
- "language": "Chinese",
-}
-
-# fmt: off
-ISO2022JP_CLS = (
- 2, 0, 0, 0, 0, 0, 0, 0, # 00 - 07
- 0, 0, 0, 0, 0, 0, 2, 2, # 08 - 0f
- 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17
- 0, 0, 0, 1, 0, 0, 0, 0, # 18 - 1f
- 0, 0, 0, 0, 7, 0, 0, 0, # 20 - 27
- 3, 0, 0, 0, 0, 0, 0, 0, # 28 - 2f
- 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37
- 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f
- 6, 0, 4, 0, 8, 0, 0, 0, # 40 - 47
- 0, 9, 5, 0, 0, 0, 0, 0, # 48 - 4f
- 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57
- 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f
- 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67
- 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f
- 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77
- 0, 0, 0, 0, 0, 0, 0, 0, # 78 - 7f
- 2, 2, 2, 2, 2, 2, 2, 2, # 80 - 87
- 2, 2, 2, 2, 2, 2, 2, 2, # 88 - 8f
- 2, 2, 2, 2, 2, 2, 2, 2, # 90 - 97
- 2, 2, 2, 2, 2, 2, 2, 2, # 98 - 9f
- 2, 2, 2, 2, 2, 2, 2, 2, # a0 - a7
- 2, 2, 2, 2, 2, 2, 2, 2, # a8 - af
- 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7
- 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf
- 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7
- 2, 2, 2, 2, 2, 2, 2, 2, # c8 - cf
- 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7
- 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df
- 2, 2, 2, 2, 2, 2, 2, 2, # e0 - e7
- 2, 2, 2, 2, 2, 2, 2, 2, # e8 - ef
- 2, 2, 2, 2, 2, 2, 2, 2, # f0 - f7
- 2, 2, 2, 2, 2, 2, 2, 2, # f8 - ff
-)
-
-ISO2022JP_ST = (
- MachineState.START, 3, MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, # 00-07
- MachineState.START, MachineState.START, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 08-0f
- MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, # 10-17
- MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, # 18-1f
- MachineState.ERROR, 5, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 4, MachineState.ERROR, MachineState.ERROR, # 20-27
- MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 6, MachineState.ITS_ME, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, # 28-2f
- MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, # 30-37
- MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 38-3f
- MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, MachineState.START, MachineState.START, # 40-47
-)
-# fmt: on
-
-ISO2022JP_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
-
-ISO2022JP_SM_MODEL: CodingStateMachineDict = {
- "class_table": ISO2022JP_CLS,
- "class_factor": 10,
- "state_table": ISO2022JP_ST,
- "char_len_table": ISO2022JP_CHAR_LEN_TABLE,
- "name": "ISO-2022-JP",
- "language": "Japanese",
-}
-
-# fmt: off
-ISO2022KR_CLS = (
- 2, 0, 0, 0, 0, 0, 0, 0, # 00 - 07
- 0, 0, 0, 0, 0, 0, 0, 0, # 08 - 0f
- 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17
- 0, 0, 0, 1, 0, 0, 0, 0, # 18 - 1f
- 0, 0, 0, 0, 3, 0, 0, 0, # 20 - 27
- 0, 4, 0, 0, 0, 0, 0, 0, # 28 - 2f
- 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37
- 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f
- 0, 0, 0, 5, 0, 0, 0, 0, # 40 - 47
- 0, 0, 0, 0, 0, 0, 0, 0, # 48 - 4f
- 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57
- 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f
- 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67
- 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f
- 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77
- 0, 0, 0, 0, 0, 0, 0, 0, # 78 - 7f
- 2, 2, 2, 2, 2, 2, 2, 2, # 80 - 87
- 2, 2, 2, 2, 2, 2, 2, 2, # 88 - 8f
- 2, 2, 2, 2, 2, 2, 2, 2, # 90 - 97
- 2, 2, 2, 2, 2, 2, 2, 2, # 98 - 9f
- 2, 2, 2, 2, 2, 2, 2, 2, # a0 - a7
- 2, 2, 2, 2, 2, 2, 2, 2, # a8 - af
- 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7
- 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf
- 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7
- 2, 2, 2, 2, 2, 2, 2, 2, # c8 - cf
- 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7
- 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df
- 2, 2, 2, 2, 2, 2, 2, 2, # e0 - e7
- 2, 2, 2, 2, 2, 2, 2, 2, # e8 - ef
- 2, 2, 2, 2, 2, 2, 2, 2, # f0 - f7
- 2, 2, 2, 2, 2, 2, 2, 2, # f8 - ff
-)
-
-ISO2022KR_ST = (
- MachineState.START, 3, MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, MachineState.ERROR, MachineState.ERROR, # 00-07
- MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, # 08-0f
- MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 4, MachineState.ERROR, MachineState.ERROR, # 10-17
- MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 5, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 18-1f
- MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.START, MachineState.START, MachineState.START, MachineState.START, # 20-27
-)
-# fmt: on
-
-ISO2022KR_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0)
-
-ISO2022KR_SM_MODEL: CodingStateMachineDict = {
- "class_table": ISO2022KR_CLS,
- "class_factor": 6,
- "state_table": ISO2022KR_ST,
- "char_len_table": ISO2022KR_CHAR_LEN_TABLE,
- "name": "ISO-2022-KR",
- "language": "Korean",
-}
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/eucjpprober.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/eucjpprober.py
deleted file mode 100644
index 39487f4..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/eucjpprober.py
+++ /dev/null
@@ -1,102 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is mozilla.org code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-from typing import Union
-
-from .chardistribution import EUCJPDistributionAnalysis
-from .codingstatemachine import CodingStateMachine
-from .enums import MachineState, ProbingState
-from .jpcntx import EUCJPContextAnalysis
-from .mbcharsetprober import MultiByteCharSetProber
-from .mbcssm import EUCJP_SM_MODEL
-
-
-class EUCJPProber(MultiByteCharSetProber):
- def __init__(self) -> None:
- super().__init__()
- self.coding_sm = CodingStateMachine(EUCJP_SM_MODEL)
- self.distribution_analyzer = EUCJPDistributionAnalysis()
- self.context_analyzer = EUCJPContextAnalysis()
- self.reset()
-
- def reset(self) -> None:
- super().reset()
- self.context_analyzer.reset()
-
- @property
- def charset_name(self) -> str:
- return "EUC-JP"
-
- @property
- def language(self) -> str:
- return "Japanese"
-
- def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState:
- assert self.coding_sm is not None
- assert self.distribution_analyzer is not None
-
- for i, byte in enumerate(byte_str):
- # PY3K: byte_str is a byte array, so byte is an int, not a byte
- coding_state = self.coding_sm.next_state(byte)
- if coding_state == MachineState.ERROR:
- self.logger.debug(
- "%s %s prober hit error at byte %s",
- self.charset_name,
- self.language,
- i,
- )
- self._state = ProbingState.NOT_ME
- break
- if coding_state == MachineState.ITS_ME:
- self._state = ProbingState.FOUND_IT
- break
- if coding_state == MachineState.START:
- char_len = self.coding_sm.get_current_charlen()
- if i == 0:
- self._last_char[1] = byte
- self.context_analyzer.feed(self._last_char, char_len)
- self.distribution_analyzer.feed(self._last_char, char_len)
- else:
- self.context_analyzer.feed(byte_str[i - 1 : i + 1], char_len)
- self.distribution_analyzer.feed(byte_str[i - 1 : i + 1], char_len)
-
- self._last_char[0] = byte_str[-1]
-
- if self.state == ProbingState.DETECTING:
- if self.context_analyzer.got_enough_data() and (
- self.get_confidence() > self.SHORTCUT_THRESHOLD
- ):
- self._state = ProbingState.FOUND_IT
-
- return self.state
-
- def get_confidence(self) -> float:
- assert self.distribution_analyzer is not None
-
- context_conf = self.context_analyzer.get_confidence()
- distrib_conf = self.distribution_analyzer.get_confidence()
- return max(context_conf, distrib_conf)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/euckrfreq.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/euckrfreq.py
deleted file mode 100644
index 7dc3b10..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/euckrfreq.py
+++ /dev/null
@@ -1,196 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is Mozilla Communicator client code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-# Sampling from about 20M text materials include literature and computer technology
-
-# 128 --> 0.79
-# 256 --> 0.92
-# 512 --> 0.986
-# 1024 --> 0.99944
-# 2048 --> 0.99999
-#
-# Idea Distribution Ratio = 0.98653 / (1-0.98653) = 73.24
-# Random Distribution Ration = 512 / (2350-512) = 0.279.
-#
-# Typical Distribution Ratio
-
-EUCKR_TYPICAL_DISTRIBUTION_RATIO = 6.0
-
-EUCKR_TABLE_SIZE = 2352
-
-# Char to FreqOrder table ,
-# fmt: off
-EUCKR_CHAR_TO_FREQ_ORDER = (
- 13, 130, 120,1396, 481,1719,1720, 328, 609, 212,1721, 707, 400, 299,1722, 87,
-1397,1723, 104, 536,1117,1203,1724,1267, 685,1268, 508,1725,1726,1727,1728,1398,
-1399,1729,1730,1731, 141, 621, 326,1057, 368,1732, 267, 488, 20,1733,1269,1734,
- 945,1400,1735, 47, 904,1270,1736,1737, 773, 248,1738, 409, 313, 786, 429,1739,
- 116, 987, 813,1401, 683, 75,1204, 145,1740,1741,1742,1743, 16, 847, 667, 622,
- 708,1744,1745,1746, 966, 787, 304, 129,1747, 60, 820, 123, 676,1748,1749,1750,
-1751, 617,1752, 626,1753,1754,1755,1756, 653,1757,1758,1759,1760,1761,1762, 856,
- 344,1763,1764,1765,1766, 89, 401, 418, 806, 905, 848,1767,1768,1769, 946,1205,
- 709,1770,1118,1771, 241,1772,1773,1774,1271,1775, 569,1776, 999,1777,1778,1779,
-1780, 337, 751,1058, 28, 628, 254,1781, 177, 906, 270, 349, 891,1079,1782, 19,
-1783, 379,1784, 315,1785, 629, 754,1402, 559,1786, 636, 203,1206,1787, 710, 567,
-1788, 935, 814,1789,1790,1207, 766, 528,1791,1792,1208,1793,1794,1795,1796,1797,
-1403,1798,1799, 533,1059,1404,1405,1156,1406, 936, 884,1080,1800, 351,1801,1802,
-1803,1804,1805, 801,1806,1807,1808,1119,1809,1157, 714, 474,1407,1810, 298, 899,
- 885,1811,1120, 802,1158,1812, 892,1813,1814,1408, 659,1815,1816,1121,1817,1818,
-1819,1820,1821,1822, 319,1823, 594, 545,1824, 815, 937,1209,1825,1826, 573,1409,
-1022,1827,1210,1828,1829,1830,1831,1832,1833, 556, 722, 807,1122,1060,1834, 697,
-1835, 900, 557, 715,1836,1410, 540,1411, 752,1159, 294, 597,1211, 976, 803, 770,
-1412,1837,1838, 39, 794,1413, 358,1839, 371, 925,1840, 453, 661, 788, 531, 723,
- 544,1023,1081, 869, 91,1841, 392, 430, 790, 602,1414, 677,1082, 457,1415,1416,
-1842,1843, 475, 327,1024,1417, 795, 121,1844, 733, 403,1418,1845,1846,1847, 300,
- 119, 711,1212, 627,1848,1272, 207,1849,1850, 796,1213, 382,1851, 519,1852,1083,
- 893,1853,1854,1855, 367, 809, 487, 671,1856, 663,1857,1858, 956, 471, 306, 857,
-1859,1860,1160,1084,1861,1862,1863,1864,1865,1061,1866,1867,1868,1869,1870,1871,
- 282, 96, 574,1872, 502,1085,1873,1214,1874, 907,1875,1876, 827, 977,1419,1420,
-1421, 268,1877,1422,1878,1879,1880, 308,1881, 2, 537,1882,1883,1215,1884,1885,
- 127, 791,1886,1273,1423,1887, 34, 336, 404, 643,1888, 571, 654, 894, 840,1889,
- 0, 886,1274, 122, 575, 260, 908, 938,1890,1275, 410, 316,1891,1892, 100,1893,
-1894,1123, 48,1161,1124,1025,1895, 633, 901,1276,1896,1897, 115, 816,1898, 317,
-1899, 694,1900, 909, 734,1424, 572, 866,1425, 691, 85, 524,1010, 543, 394, 841,
-1901,1902,1903,1026,1904,1905,1906,1907,1908,1909, 30, 451, 651, 988, 310,1910,
-1911,1426, 810,1216, 93,1912,1913,1277,1217,1914, 858, 759, 45, 58, 181, 610,
- 269,1915,1916, 131,1062, 551, 443,1000, 821,1427, 957, 895,1086,1917,1918, 375,
-1919, 359,1920, 687,1921, 822,1922, 293,1923,1924, 40, 662, 118, 692, 29, 939,
- 887, 640, 482, 174,1925, 69,1162, 728,1428, 910,1926,1278,1218,1279, 386, 870,
- 217, 854,1163, 823,1927,1928,1929,1930, 834,1931, 78,1932, 859,1933,1063,1934,
-1935,1936,1937, 438,1164, 208, 595,1938,1939,1940,1941,1219,1125,1942, 280, 888,
-1429,1430,1220,1431,1943,1944,1945,1946,1947,1280, 150, 510,1432,1948,1949,1950,
-1951,1952,1953,1954,1011,1087,1955,1433,1043,1956, 881,1957, 614, 958,1064,1065,
-1221,1958, 638,1001, 860, 967, 896,1434, 989, 492, 553,1281,1165,1959,1282,1002,
-1283,1222,1960,1961,1962,1963, 36, 383, 228, 753, 247, 454,1964, 876, 678,1965,
-1966,1284, 126, 464, 490, 835, 136, 672, 529, 940,1088,1435, 473,1967,1968, 467,
- 50, 390, 227, 587, 279, 378, 598, 792, 968, 240, 151, 160, 849, 882,1126,1285,
- 639,1044, 133, 140, 288, 360, 811, 563,1027, 561, 142, 523,1969,1970,1971, 7,
- 103, 296, 439, 407, 506, 634, 990,1972,1973,1974,1975, 645,1976,1977,1978,1979,
-1980,1981, 236,1982,1436,1983,1984,1089, 192, 828, 618, 518,1166, 333,1127,1985,
- 818,1223,1986,1987,1988,1989,1990,1991,1992,1993, 342,1128,1286, 746, 842,1994,
-1995, 560, 223,1287, 98, 8, 189, 650, 978,1288,1996,1437,1997, 17, 345, 250,
- 423, 277, 234, 512, 226, 97, 289, 42, 167,1998, 201,1999,2000, 843, 836, 824,
- 532, 338, 783,1090, 182, 576, 436,1438,1439, 527, 500,2001, 947, 889,2002,2003,
-2004,2005, 262, 600, 314, 447,2006, 547,2007, 693, 738,1129,2008, 71,1440, 745,
- 619, 688,2009, 829,2010,2011, 147,2012, 33, 948,2013,2014, 74, 224,2015, 61,
- 191, 918, 399, 637,2016,1028,1130, 257, 902,2017,2018,2019,2020,2021,2022,2023,
-2024,2025,2026, 837,2027,2028,2029,2030, 179, 874, 591, 52, 724, 246,2031,2032,
-2033,2034,1167, 969,2035,1289, 630, 605, 911,1091,1168,2036,2037,2038,1441, 912,
-2039, 623,2040,2041, 253,1169,1290,2042,1442, 146, 620, 611, 577, 433,2043,1224,
- 719,1170, 959, 440, 437, 534, 84, 388, 480,1131, 159, 220, 198, 679,2044,1012,
- 819,1066,1443, 113,1225, 194, 318,1003,1029,2045,2046,2047,2048,1067,2049,2050,
-2051,2052,2053, 59, 913, 112,2054, 632,2055, 455, 144, 739,1291,2056, 273, 681,
- 499,2057, 448,2058,2059, 760,2060,2061, 970, 384, 169, 245,1132,2062,2063, 414,
-1444,2064,2065, 41, 235,2066, 157, 252, 877, 568, 919, 789, 580,2067, 725,2068,
-2069,1292,2070,2071,1445,2072,1446,2073,2074, 55, 588, 66,1447, 271,1092,2075,
-1226,2076, 960,1013, 372,2077,2078,2079,2080,2081,1293,2082,2083,2084,2085, 850,
-2086,2087,2088,2089,2090, 186,2091,1068, 180,2092,2093,2094, 109,1227, 522, 606,
-2095, 867,1448,1093, 991,1171, 926, 353,1133,2096, 581,2097,2098,2099,1294,1449,
-1450,2100, 596,1172,1014,1228,2101,1451,1295,1173,1229,2102,2103,1296,1134,1452,
- 949,1135,2104,2105,1094,1453,1454,1455,2106,1095,2107,2108,2109,2110,2111,2112,
-2113,2114,2115,2116,2117, 804,2118,2119,1230,1231, 805,1456, 405,1136,2120,2121,
-2122,2123,2124, 720, 701,1297, 992,1457, 927,1004,2125,2126,2127,2128,2129,2130,
- 22, 417,2131, 303,2132, 385,2133, 971, 520, 513,2134,1174, 73,1096, 231, 274,
- 962,1458, 673,2135,1459,2136, 152,1137,2137,2138,2139,2140,1005,1138,1460,1139,
-2141,2142,2143,2144, 11, 374, 844,2145, 154,1232, 46,1461,2146, 838, 830, 721,
-1233, 106,2147, 90, 428, 462, 578, 566,1175, 352,2148,2149, 538,1234, 124,1298,
-2150,1462, 761, 565,2151, 686,2152, 649,2153, 72, 173,2154, 460, 415,2155,1463,
-2156,1235, 305,2157,2158,2159,2160,2161,2162, 579,2163,2164,2165,2166,2167, 747,
-2168,2169,2170,2171,1464, 669,2172,2173,2174,2175,2176,1465,2177, 23, 530, 285,
-2178, 335, 729,2179, 397,2180,2181,2182,1030,2183,2184, 698,2185,2186, 325,2187,
-2188, 369,2189, 799,1097,1015, 348,2190,1069, 680,2191, 851,1466,2192,2193, 10,
-2194, 613, 424,2195, 979, 108, 449, 589, 27, 172, 81,1031, 80, 774, 281, 350,
-1032, 525, 301, 582,1176,2196, 674,1045,2197,2198,1467, 730, 762,2199,2200,2201,
-2202,1468,2203, 993,2204,2205, 266,1070, 963,1140,2206,2207,2208, 664,1098, 972,
-2209,2210,2211,1177,1469,1470, 871,2212,2213,2214,2215,2216,1471,2217,2218,2219,
-2220,2221,2222,2223,2224,2225,2226,2227,1472,1236,2228,2229,2230,2231,2232,2233,
-2234,2235,1299,2236,2237, 200,2238, 477, 373,2239,2240, 731, 825, 777,2241,2242,
-2243, 521, 486, 548,2244,2245,2246,1473,1300, 53, 549, 137, 875, 76, 158,2247,
-1301,1474, 469, 396,1016, 278, 712,2248, 321, 442, 503, 767, 744, 941,1237,1178,
-1475,2249, 82, 178,1141,1179, 973,2250,1302,2251, 297,2252,2253, 570,2254,2255,
-2256, 18, 450, 206,2257, 290, 292,1142,2258, 511, 162, 99, 346, 164, 735,2259,
-1476,1477, 4, 554, 343, 798,1099,2260,1100,2261, 43, 171,1303, 139, 215,2262,
-2263, 717, 775,2264,1033, 322, 216,2265, 831,2266, 149,2267,1304,2268,2269, 702,
-1238, 135, 845, 347, 309,2270, 484,2271, 878, 655, 238,1006,1478,2272, 67,2273,
- 295,2274,2275, 461,2276, 478, 942, 412,2277,1034,2278,2279,2280, 265,2281, 541,
-2282,2283,2284,2285,2286, 70, 852,1071,2287,2288,2289,2290, 21, 56, 509, 117,
- 432,2291,2292, 331, 980, 552,1101, 148, 284, 105, 393,1180,1239, 755,2293, 187,
-2294,1046,1479,2295, 340,2296, 63,1047, 230,2297,2298,1305, 763,1306, 101, 800,
- 808, 494,2299,2300,2301, 903,2302, 37,1072, 14, 5,2303, 79, 675,2304, 312,
-2305,2306,2307,2308,2309,1480, 6,1307,2310,2311,2312, 1, 470, 35, 24, 229,
-2313, 695, 210, 86, 778, 15, 784, 592, 779, 32, 77, 855, 964,2314, 259,2315,
- 501, 380,2316,2317, 83, 981, 153, 689,1308,1481,1482,1483,2318,2319, 716,1484,
-2320,2321,2322,2323,2324,2325,1485,2326,2327, 128, 57, 68, 261,1048, 211, 170,
-1240, 31,2328, 51, 435, 742,2329,2330,2331, 635,2332, 264, 456,2333,2334,2335,
- 425,2336,1486, 143, 507, 263, 943,2337, 363, 920,1487, 256,1488,1102, 243, 601,
-1489,2338,2339,2340,2341,2342,2343,2344, 861,2345,2346,2347,2348,2349,2350, 395,
-2351,1490,1491, 62, 535, 166, 225,2352,2353, 668, 419,1241, 138, 604, 928,2354,
-1181,2355,1492,1493,2356,2357,2358,1143,2359, 696,2360, 387, 307,1309, 682, 476,
-2361,2362, 332, 12, 222, 156,2363, 232,2364, 641, 276, 656, 517,1494,1495,1035,
- 416, 736,1496,2365,1017, 586,2366,2367,2368,1497,2369, 242,2370,2371,2372,1498,
-2373, 965, 713,2374,2375,2376,2377, 740, 982,1499, 944,1500,1007,2378,2379,1310,
-1501,2380,2381,2382, 785, 329,2383,2384,1502,2385,2386,2387, 932,2388,1503,2389,
-2390,2391,2392,1242,2393,2394,2395,2396,2397, 994, 950,2398,2399,2400,2401,1504,
-1311,2402,2403,2404,2405,1049, 749,2406,2407, 853, 718,1144,1312,2408,1182,1505,
-2409,2410, 255, 516, 479, 564, 550, 214,1506,1507,1313, 413, 239, 444, 339,1145,
-1036,1508,1509,1314,1037,1510,1315,2411,1511,2412,2413,2414, 176, 703, 497, 624,
- 593, 921, 302,2415, 341, 165,1103,1512,2416,1513,2417,2418,2419, 376,2420, 700,
-2421,2422,2423, 258, 768,1316,2424,1183,2425, 995, 608,2426,2427,2428,2429, 221,
-2430,2431,2432,2433,2434,2435,2436,2437, 195, 323, 726, 188, 897, 983,1317, 377,
- 644,1050, 879,2438, 452,2439,2440,2441,2442,2443,2444, 914,2445,2446,2447,2448,
- 915, 489,2449,1514,1184,2450,2451, 515, 64, 427, 495,2452, 583,2453, 483, 485,
-1038, 562, 213,1515, 748, 666,2454,2455,2456,2457, 334,2458, 780, 996,1008, 705,
-1243,2459,2460,2461,2462,2463, 114,2464, 493,1146, 366, 163,1516, 961,1104,2465,
- 291,2466,1318,1105,2467,1517, 365,2468, 355, 951,1244,2469,1319,2470, 631,2471,
-2472, 218,1320, 364, 320, 756,1518,1519,1321,1520,1322,2473,2474,2475,2476, 997,
-2477,2478,2479,2480, 665,1185,2481, 916,1521,2482,2483,2484, 584, 684,2485,2486,
- 797,2487,1051,1186,2488,2489,2490,1522,2491,2492, 370,2493,1039,1187, 65,2494,
- 434, 205, 463,1188,2495, 125, 812, 391, 402, 826, 699, 286, 398, 155, 781, 771,
- 585,2496, 590, 505,1073,2497, 599, 244, 219, 917,1018, 952, 646,1523,2498,1323,
-2499,2500, 49, 984, 354, 741,2501, 625,2502,1324,2503,1019, 190, 357, 757, 491,
- 95, 782, 868,2504,2505,2506,2507,2508,2509, 134,1524,1074, 422,1525, 898,2510,
- 161,2511,2512,2513,2514, 769,2515,1526,2516,2517, 411,1325,2518, 472,1527,2519,
-2520,2521,2522,2523,2524, 985,2525,2526,2527,2528,2529,2530, 764,2531,1245,2532,
-2533, 25, 204, 311,2534, 496,2535,1052,2536,2537,2538,2539,2540,2541,2542, 199,
- 704, 504, 468, 758, 657,1528, 196, 44, 839,1246, 272, 750,2543, 765, 862,2544,
-2545,1326,2546, 132, 615, 933,2547, 732,2548,2549,2550,1189,1529,2551, 283,1247,
-1053, 607, 929,2552,2553,2554, 930, 183, 872, 616,1040,1147,2555,1148,1020, 441,
- 249,1075,2556,2557,2558, 466, 743,2559,2560,2561, 92, 514, 426, 420, 526,2562,
-2563,2564,2565,2566,2567,2568, 185,2569,2570,2571,2572, 776,1530, 658,2573, 362,
-2574, 361, 922,1076, 793,2575,2576,2577,2578,2579,2580,1531, 251,2581,2582,2583,
-2584,1532, 54, 612, 237,1327,2585,2586, 275, 408, 647, 111,2587,1533,1106, 465,
- 3, 458, 9, 38,2588, 107, 110, 890, 209, 26, 737, 498,2589,1534,2590, 431,
- 202, 88,1535, 356, 287,1107, 660,1149,2591, 381,1536, 986,1150, 445,1248,1151,
- 974,2592,2593, 846,2594, 446, 953, 184,1249,1250, 727,2595, 923, 193, 883,2596,
-2597,2598, 102, 324, 539, 817,2599, 421,1041,2600, 832,2601, 94, 175, 197, 406,
-2602, 459,2603,2604,2605,2606,2607, 330, 555,2608,2609,2610, 706,1108, 389,2611,
-2612,2613,2614, 233,2615, 833, 558, 931, 954,1251,2616,2617,1537, 546,2618,2619,
-1009,2620,2621,2622,1538, 690,1328,2623, 955,2624,1539,2625,2626, 772,2627,2628,
-2629,2630,2631, 924, 648, 863, 603,2632,2633, 934,1540, 864, 865,2634, 642,1042,
- 670,1190,2635,2636,2637,2638, 168,2639, 652, 873, 542,1054,1541,2640,2641,2642, # 512, 256
-)
-# fmt: on
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/euckrprober.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/euckrprober.py
deleted file mode 100644
index 1fc5de0..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/euckrprober.py
+++ /dev/null
@@ -1,47 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is mozilla.org code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-from .chardistribution import EUCKRDistributionAnalysis
-from .codingstatemachine import CodingStateMachine
-from .mbcharsetprober import MultiByteCharSetProber
-from .mbcssm import EUCKR_SM_MODEL
-
-
-class EUCKRProber(MultiByteCharSetProber):
- def __init__(self) -> None:
- super().__init__()
- self.coding_sm = CodingStateMachine(EUCKR_SM_MODEL)
- self.distribution_analyzer = EUCKRDistributionAnalysis()
- self.reset()
-
- @property
- def charset_name(self) -> str:
- return "EUC-KR"
-
- @property
- def language(self) -> str:
- return "Korean"
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/euctwfreq.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/euctwfreq.py
deleted file mode 100644
index 4900ccc..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/euctwfreq.py
+++ /dev/null
@@ -1,388 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is Mozilla Communicator client code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-# EUCTW frequency table
-# Converted from big5 work
-# by Taiwan's Mandarin Promotion Council
-#
-
-# 128 --> 0.42261
-# 256 --> 0.57851
-# 512 --> 0.74851
-# 1024 --> 0.89384
-# 2048 --> 0.97583
-#
-# Idea Distribution Ratio = 0.74851/(1-0.74851) =2.98
-# Random Distribution Ration = 512/(5401-512)=0.105
-#
-# Typical Distribution Ratio about 25% of Ideal one, still much higher than RDR
-
-EUCTW_TYPICAL_DISTRIBUTION_RATIO = 0.75
-
-# Char to FreqOrder table
-EUCTW_TABLE_SIZE = 5376
-
-# fmt: off
-EUCTW_CHAR_TO_FREQ_ORDER = (
- 1, 1800, 1506, 255, 1431, 198, 9, 82, 6, 7310, 177, 202, 3615, 1256, 2808, 110, # 2742
- 3735, 33, 3241, 261, 76, 44, 2113, 16, 2931, 2184, 1176, 659, 3868, 26, 3404, 2643, # 2758
- 1198, 3869, 3313, 4060, 410, 2211, 302, 590, 361, 1963, 8, 204, 58, 4296, 7311, 1931, # 2774
- 63, 7312, 7313, 317, 1614, 75, 222, 159, 4061, 2412, 1480, 7314, 3500, 3068, 224, 2809, # 2790
- 3616, 3, 10, 3870, 1471, 29, 2774, 1135, 2852, 1939, 873, 130, 3242, 1123, 312, 7315, # 2806
- 4297, 2051, 507, 252, 682, 7316, 142, 1914, 124, 206, 2932, 34, 3501, 3173, 64, 604, # 2822
- 7317, 2494, 1976, 1977, 155, 1990, 645, 641, 1606, 7318, 3405, 337, 72, 406, 7319, 80, # 2838
- 630, 238, 3174, 1509, 263, 939, 1092, 2644, 756, 1440, 1094, 3406, 449, 69, 2969, 591, # 2854
- 179, 2095, 471, 115, 2034, 1843, 60, 50, 2970, 134, 806, 1868, 734, 2035, 3407, 180, # 2870
- 995, 1607, 156, 537, 2893, 688, 7320, 319, 1305, 779, 2144, 514, 2374, 298, 4298, 359, # 2886
- 2495, 90, 2707, 1338, 663, 11, 906, 1099, 2545, 20, 2436, 182, 532, 1716, 7321, 732, # 2902
- 1376, 4062, 1311, 1420, 3175, 25, 2312, 1056, 113, 399, 382, 1949, 242, 3408, 2467, 529, # 2918
- 3243, 475, 1447, 3617, 7322, 117, 21, 656, 810, 1297, 2295, 2329, 3502, 7323, 126, 4063, # 2934
- 706, 456, 150, 613, 4299, 71, 1118, 2036, 4064, 145, 3069, 85, 835, 486, 2114, 1246, # 2950
- 1426, 428, 727, 1285, 1015, 800, 106, 623, 303, 1281, 7324, 2127, 2354, 347, 3736, 221, # 2966
- 3503, 3110, 7325, 1955, 1153, 4065, 83, 296, 1199, 3070, 192, 624, 93, 7326, 822, 1897, # 2982
- 2810, 3111, 795, 2064, 991, 1554, 1542, 1592, 27, 43, 2853, 859, 139, 1456, 860, 4300, # 2998
- 437, 712, 3871, 164, 2392, 3112, 695, 211, 3017, 2096, 195, 3872, 1608, 3504, 3505, 3618, # 3014
- 3873, 234, 811, 2971, 2097, 3874, 2229, 1441, 3506, 1615, 2375, 668, 2076, 1638, 305, 228, # 3030
- 1664, 4301, 467, 415, 7327, 262, 2098, 1593, 239, 108, 300, 200, 1033, 512, 1247, 2077, # 3046
- 7328, 7329, 2173, 3176, 3619, 2673, 593, 845, 1062, 3244, 88, 1723, 2037, 3875, 1950, 212, # 3062
- 266, 152, 149, 468, 1898, 4066, 4302, 77, 187, 7330, 3018, 37, 5, 2972, 7331, 3876, # 3078
- 7332, 7333, 39, 2517, 4303, 2894, 3177, 2078, 55, 148, 74, 4304, 545, 483, 1474, 1029, # 3094
- 1665, 217, 1869, 1531, 3113, 1104, 2645, 4067, 24, 172, 3507, 900, 3877, 3508, 3509, 4305, # 3110
- 32, 1408, 2811, 1312, 329, 487, 2355, 2247, 2708, 784, 2674, 4, 3019, 3314, 1427, 1788, # 3126
- 188, 109, 499, 7334, 3620, 1717, 1789, 888, 1217, 3020, 4306, 7335, 3510, 7336, 3315, 1520, # 3142
- 3621, 3878, 196, 1034, 775, 7337, 7338, 929, 1815, 249, 439, 38, 7339, 1063, 7340, 794, # 3158
- 3879, 1435, 2296, 46, 178, 3245, 2065, 7341, 2376, 7342, 214, 1709, 4307, 804, 35, 707, # 3174
- 324, 3622, 1601, 2546, 140, 459, 4068, 7343, 7344, 1365, 839, 272, 978, 2257, 2572, 3409, # 3190
- 2128, 1363, 3623, 1423, 697, 100, 3071, 48, 70, 1231, 495, 3114, 2193, 7345, 1294, 7346, # 3206
- 2079, 462, 586, 1042, 3246, 853, 256, 988, 185, 2377, 3410, 1698, 434, 1084, 7347, 3411, # 3222
- 314, 2615, 2775, 4308, 2330, 2331, 569, 2280, 637, 1816, 2518, 757, 1162, 1878, 1616, 3412, # 3238
- 287, 1577, 2115, 768, 4309, 1671, 2854, 3511, 2519, 1321, 3737, 909, 2413, 7348, 4069, 933, # 3254
- 3738, 7349, 2052, 2356, 1222, 4310, 765, 2414, 1322, 786, 4311, 7350, 1919, 1462, 1677, 2895, # 3270
- 1699, 7351, 4312, 1424, 2437, 3115, 3624, 2590, 3316, 1774, 1940, 3413, 3880, 4070, 309, 1369, # 3286
- 1130, 2812, 364, 2230, 1653, 1299, 3881, 3512, 3882, 3883, 2646, 525, 1085, 3021, 902, 2000, # 3302
- 1475, 964, 4313, 421, 1844, 1415, 1057, 2281, 940, 1364, 3116, 376, 4314, 4315, 1381, 7, # 3318
- 2520, 983, 2378, 336, 1710, 2675, 1845, 321, 3414, 559, 1131, 3022, 2742, 1808, 1132, 1313, # 3334
- 265, 1481, 1857, 7352, 352, 1203, 2813, 3247, 167, 1089, 420, 2814, 776, 792, 1724, 3513, # 3350
- 4071, 2438, 3248, 7353, 4072, 7354, 446, 229, 333, 2743, 901, 3739, 1200, 1557, 4316, 2647, # 3366
- 1920, 395, 2744, 2676, 3740, 4073, 1835, 125, 916, 3178, 2616, 4317, 7355, 7356, 3741, 7357, # 3382
- 7358, 7359, 4318, 3117, 3625, 1133, 2547, 1757, 3415, 1510, 2313, 1409, 3514, 7360, 2145, 438, # 3398
- 2591, 2896, 2379, 3317, 1068, 958, 3023, 461, 311, 2855, 2677, 4074, 1915, 3179, 4075, 1978, # 3414
- 383, 750, 2745, 2617, 4076, 274, 539, 385, 1278, 1442, 7361, 1154, 1964, 384, 561, 210, # 3430
- 98, 1295, 2548, 3515, 7362, 1711, 2415, 1482, 3416, 3884, 2897, 1257, 129, 7363, 3742, 642, # 3446
- 523, 2776, 2777, 2648, 7364, 141, 2231, 1333, 68, 176, 441, 876, 907, 4077, 603, 2592, # 3462
- 710, 171, 3417, 404, 549, 18, 3118, 2393, 1410, 3626, 1666, 7365, 3516, 4319, 2898, 4320, # 3478
- 7366, 2973, 368, 7367, 146, 366, 99, 871, 3627, 1543, 748, 807, 1586, 1185, 22, 2258, # 3494
- 379, 3743, 3180, 7368, 3181, 505, 1941, 2618, 1991, 1382, 2314, 7369, 380, 2357, 218, 702, # 3510
- 1817, 1248, 3418, 3024, 3517, 3318, 3249, 7370, 2974, 3628, 930, 3250, 3744, 7371, 59, 7372, # 3526
- 585, 601, 4078, 497, 3419, 1112, 1314, 4321, 1801, 7373, 1223, 1472, 2174, 7374, 749, 1836, # 3542
- 690, 1899, 3745, 1772, 3885, 1476, 429, 1043, 1790, 2232, 2116, 917, 4079, 447, 1086, 1629, # 3558
- 7375, 556, 7376, 7377, 2020, 1654, 844, 1090, 105, 550, 966, 1758, 2815, 1008, 1782, 686, # 3574
- 1095, 7378, 2282, 793, 1602, 7379, 3518, 2593, 4322, 4080, 2933, 2297, 4323, 3746, 980, 2496, # 3590
- 544, 353, 527, 4324, 908, 2678, 2899, 7380, 381, 2619, 1942, 1348, 7381, 1341, 1252, 560, # 3606
- 3072, 7382, 3420, 2856, 7383, 2053, 973, 886, 2080, 143, 4325, 7384, 7385, 157, 3886, 496, # 3622
- 4081, 57, 840, 540, 2038, 4326, 4327, 3421, 2117, 1445, 970, 2259, 1748, 1965, 2081, 4082, # 3638
- 3119, 1234, 1775, 3251, 2816, 3629, 773, 1206, 2129, 1066, 2039, 1326, 3887, 1738, 1725, 4083, # 3654
- 279, 3120, 51, 1544, 2594, 423, 1578, 2130, 2066, 173, 4328, 1879, 7386, 7387, 1583, 264, # 3670
- 610, 3630, 4329, 2439, 280, 154, 7388, 7389, 7390, 1739, 338, 1282, 3073, 693, 2857, 1411, # 3686
- 1074, 3747, 2440, 7391, 4330, 7392, 7393, 1240, 952, 2394, 7394, 2900, 1538, 2679, 685, 1483, # 3702
- 4084, 2468, 1436, 953, 4085, 2054, 4331, 671, 2395, 79, 4086, 2441, 3252, 608, 567, 2680, # 3718
- 3422, 4087, 4088, 1691, 393, 1261, 1791, 2396, 7395, 4332, 7396, 7397, 7398, 7399, 1383, 1672, # 3734
- 3748, 3182, 1464, 522, 1119, 661, 1150, 216, 675, 4333, 3888, 1432, 3519, 609, 4334, 2681, # 3750
- 2397, 7400, 7401, 7402, 4089, 3025, 0, 7403, 2469, 315, 231, 2442, 301, 3319, 4335, 2380, # 3766
- 7404, 233, 4090, 3631, 1818, 4336, 4337, 7405, 96, 1776, 1315, 2082, 7406, 257, 7407, 1809, # 3782
- 3632, 2709, 1139, 1819, 4091, 2021, 1124, 2163, 2778, 1777, 2649, 7408, 3074, 363, 1655, 3183, # 3798
- 7409, 2975, 7410, 7411, 7412, 3889, 1567, 3890, 718, 103, 3184, 849, 1443, 341, 3320, 2934, # 3814
- 1484, 7413, 1712, 127, 67, 339, 4092, 2398, 679, 1412, 821, 7414, 7415, 834, 738, 351, # 3830
- 2976, 2146, 846, 235, 1497, 1880, 418, 1992, 3749, 2710, 186, 1100, 2147, 2746, 3520, 1545, # 3846
- 1355, 2935, 2858, 1377, 583, 3891, 4093, 2573, 2977, 7416, 1298, 3633, 1078, 2549, 3634, 2358, # 3862
- 78, 3750, 3751, 267, 1289, 2099, 2001, 1594, 4094, 348, 369, 1274, 2194, 2175, 1837, 4338, # 3878
- 1820, 2817, 3635, 2747, 2283, 2002, 4339, 2936, 2748, 144, 3321, 882, 4340, 3892, 2749, 3423, # 3894
- 4341, 2901, 7417, 4095, 1726, 320, 7418, 3893, 3026, 788, 2978, 7419, 2818, 1773, 1327, 2859, # 3910
- 3894, 2819, 7420, 1306, 4342, 2003, 1700, 3752, 3521, 2359, 2650, 787, 2022, 506, 824, 3636, # 3926
- 534, 323, 4343, 1044, 3322, 2023, 1900, 946, 3424, 7421, 1778, 1500, 1678, 7422, 1881, 4344, # 3942
- 165, 243, 4345, 3637, 2521, 123, 683, 4096, 764, 4346, 36, 3895, 1792, 589, 2902, 816, # 3958
- 626, 1667, 3027, 2233, 1639, 1555, 1622, 3753, 3896, 7423, 3897, 2860, 1370, 1228, 1932, 891, # 3974
- 2083, 2903, 304, 4097, 7424, 292, 2979, 2711, 3522, 691, 2100, 4098, 1115, 4347, 118, 662, # 3990
- 7425, 611, 1156, 854, 2381, 1316, 2861, 2, 386, 515, 2904, 7426, 7427, 3253, 868, 2234, # 4006
- 1486, 855, 2651, 785, 2212, 3028, 7428, 1040, 3185, 3523, 7429, 3121, 448, 7430, 1525, 7431, # 4022
- 2164, 4348, 7432, 3754, 7433, 4099, 2820, 3524, 3122, 503, 818, 3898, 3123, 1568, 814, 676, # 4038
- 1444, 306, 1749, 7434, 3755, 1416, 1030, 197, 1428, 805, 2821, 1501, 4349, 7435, 7436, 7437, # 4054
- 1993, 7438, 4350, 7439, 7440, 2195, 13, 2779, 3638, 2980, 3124, 1229, 1916, 7441, 3756, 2131, # 4070
- 7442, 4100, 4351, 2399, 3525, 7443, 2213, 1511, 1727, 1120, 7444, 7445, 646, 3757, 2443, 307, # 4086
- 7446, 7447, 1595, 3186, 7448, 7449, 7450, 3639, 1113, 1356, 3899, 1465, 2522, 2523, 7451, 519, # 4102
- 7452, 128, 2132, 92, 2284, 1979, 7453, 3900, 1512, 342, 3125, 2196, 7454, 2780, 2214, 1980, # 4118
- 3323, 7455, 290, 1656, 1317, 789, 827, 2360, 7456, 3758, 4352, 562, 581, 3901, 7457, 401, # 4134
- 4353, 2248, 94, 4354, 1399, 2781, 7458, 1463, 2024, 4355, 3187, 1943, 7459, 828, 1105, 4101, # 4150
- 1262, 1394, 7460, 4102, 605, 4356, 7461, 1783, 2862, 7462, 2822, 819, 2101, 578, 2197, 2937, # 4166
- 7463, 1502, 436, 3254, 4103, 3255, 2823, 3902, 2905, 3425, 3426, 7464, 2712, 2315, 7465, 7466, # 4182
- 2332, 2067, 23, 4357, 193, 826, 3759, 2102, 699, 1630, 4104, 3075, 390, 1793, 1064, 3526, # 4198
- 7467, 1579, 3076, 3077, 1400, 7468, 4105, 1838, 1640, 2863, 7469, 4358, 4359, 137, 4106, 598, # 4214
- 3078, 1966, 780, 104, 974, 2938, 7470, 278, 899, 253, 402, 572, 504, 493, 1339, 7471, # 4230
- 3903, 1275, 4360, 2574, 2550, 7472, 3640, 3029, 3079, 2249, 565, 1334, 2713, 863, 41, 7473, # 4246
- 7474, 4361, 7475, 1657, 2333, 19, 463, 2750, 4107, 606, 7476, 2981, 3256, 1087, 2084, 1323, # 4262
- 2652, 2982, 7477, 1631, 1623, 1750, 4108, 2682, 7478, 2864, 791, 2714, 2653, 2334, 232, 2416, # 4278
- 7479, 2983, 1498, 7480, 2654, 2620, 755, 1366, 3641, 3257, 3126, 2025, 1609, 119, 1917, 3427, # 4294
- 862, 1026, 4109, 7481, 3904, 3760, 4362, 3905, 4363, 2260, 1951, 2470, 7482, 1125, 817, 4110, # 4310
- 4111, 3906, 1513, 1766, 2040, 1487, 4112, 3030, 3258, 2824, 3761, 3127, 7483, 7484, 1507, 7485, # 4326
- 2683, 733, 40, 1632, 1106, 2865, 345, 4113, 841, 2524, 230, 4364, 2984, 1846, 3259, 3428, # 4342
- 7486, 1263, 986, 3429, 7487, 735, 879, 254, 1137, 857, 622, 1300, 1180, 1388, 1562, 3907, # 4358
- 3908, 2939, 967, 2751, 2655, 1349, 592, 2133, 1692, 3324, 2985, 1994, 4114, 1679, 3909, 1901, # 4374
- 2185, 7488, 739, 3642, 2715, 1296, 1290, 7489, 4115, 2198, 2199, 1921, 1563, 2595, 2551, 1870, # 4390
- 2752, 2986, 7490, 435, 7491, 343, 1108, 596, 17, 1751, 4365, 2235, 3430, 3643, 7492, 4366, # 4406
- 294, 3527, 2940, 1693, 477, 979, 281, 2041, 3528, 643, 2042, 3644, 2621, 2782, 2261, 1031, # 4422
- 2335, 2134, 2298, 3529, 4367, 367, 1249, 2552, 7493, 3530, 7494, 4368, 1283, 3325, 2004, 240, # 4438
- 1762, 3326, 4369, 4370, 836, 1069, 3128, 474, 7495, 2148, 2525, 268, 3531, 7496, 3188, 1521, # 4454
- 1284, 7497, 1658, 1546, 4116, 7498, 3532, 3533, 7499, 4117, 3327, 2684, 1685, 4118, 961, 1673, # 4470
- 2622, 190, 2005, 2200, 3762, 4371, 4372, 7500, 570, 2497, 3645, 1490, 7501, 4373, 2623, 3260, # 4486
- 1956, 4374, 584, 1514, 396, 1045, 1944, 7502, 4375, 1967, 2444, 7503, 7504, 4376, 3910, 619, # 4502
- 7505, 3129, 3261, 215, 2006, 2783, 2553, 3189, 4377, 3190, 4378, 763, 4119, 3763, 4379, 7506, # 4518
- 7507, 1957, 1767, 2941, 3328, 3646, 1174, 452, 1477, 4380, 3329, 3130, 7508, 2825, 1253, 2382, # 4534
- 2186, 1091, 2285, 4120, 492, 7509, 638, 1169, 1824, 2135, 1752, 3911, 648, 926, 1021, 1324, # 4550
- 4381, 520, 4382, 997, 847, 1007, 892, 4383, 3764, 2262, 1871, 3647, 7510, 2400, 1784, 4384, # 4566
- 1952, 2942, 3080, 3191, 1728, 4121, 2043, 3648, 4385, 2007, 1701, 3131, 1551, 30, 2263, 4122, # 4582
- 7511, 2026, 4386, 3534, 7512, 501, 7513, 4123, 594, 3431, 2165, 1821, 3535, 3432, 3536, 3192, # 4598
- 829, 2826, 4124, 7514, 1680, 3132, 1225, 4125, 7515, 3262, 4387, 4126, 3133, 2336, 7516, 4388, # 4614
- 4127, 7517, 3912, 3913, 7518, 1847, 2383, 2596, 3330, 7519, 4389, 374, 3914, 652, 4128, 4129, # 4630
- 375, 1140, 798, 7520, 7521, 7522, 2361, 4390, 2264, 546, 1659, 138, 3031, 2445, 4391, 7523, # 4646
- 2250, 612, 1848, 910, 796, 3765, 1740, 1371, 825, 3766, 3767, 7524, 2906, 2554, 7525, 692, # 4662
- 444, 3032, 2624, 801, 4392, 4130, 7526, 1491, 244, 1053, 3033, 4131, 4132, 340, 7527, 3915, # 4678
- 1041, 2987, 293, 1168, 87, 1357, 7528, 1539, 959, 7529, 2236, 721, 694, 4133, 3768, 219, # 4694
- 1478, 644, 1417, 3331, 2656, 1413, 1401, 1335, 1389, 3916, 7530, 7531, 2988, 2362, 3134, 1825, # 4710
- 730, 1515, 184, 2827, 66, 4393, 7532, 1660, 2943, 246, 3332, 378, 1457, 226, 3433, 975, # 4726
- 3917, 2944, 1264, 3537, 674, 696, 7533, 163, 7534, 1141, 2417, 2166, 713, 3538, 3333, 4394, # 4742
- 3918, 7535, 7536, 1186, 15, 7537, 1079, 1070, 7538, 1522, 3193, 3539, 276, 1050, 2716, 758, # 4758
- 1126, 653, 2945, 3263, 7539, 2337, 889, 3540, 3919, 3081, 2989, 903, 1250, 4395, 3920, 3434, # 4774
- 3541, 1342, 1681, 1718, 766, 3264, 286, 89, 2946, 3649, 7540, 1713, 7541, 2597, 3334, 2990, # 4790
- 7542, 2947, 2215, 3194, 2866, 7543, 4396, 2498, 2526, 181, 387, 1075, 3921, 731, 2187, 3335, # 4806
- 7544, 3265, 310, 313, 3435, 2299, 770, 4134, 54, 3034, 189, 4397, 3082, 3769, 3922, 7545, # 4822
- 1230, 1617, 1849, 355, 3542, 4135, 4398, 3336, 111, 4136, 3650, 1350, 3135, 3436, 3035, 4137, # 4838
- 2149, 3266, 3543, 7546, 2784, 3923, 3924, 2991, 722, 2008, 7547, 1071, 247, 1207, 2338, 2471, # 4854
- 1378, 4399, 2009, 864, 1437, 1214, 4400, 373, 3770, 1142, 2216, 667, 4401, 442, 2753, 2555, # 4870
- 3771, 3925, 1968, 4138, 3267, 1839, 837, 170, 1107, 934, 1336, 1882, 7548, 7549, 2118, 4139, # 4886
- 2828, 743, 1569, 7550, 4402, 4140, 582, 2384, 1418, 3437, 7551, 1802, 7552, 357, 1395, 1729, # 4902
- 3651, 3268, 2418, 1564, 2237, 7553, 3083, 3772, 1633, 4403, 1114, 2085, 4141, 1532, 7554, 482, # 4918
- 2446, 4404, 7555, 7556, 1492, 833, 1466, 7557, 2717, 3544, 1641, 2829, 7558, 1526, 1272, 3652, # 4934
- 4142, 1686, 1794, 416, 2556, 1902, 1953, 1803, 7559, 3773, 2785, 3774, 1159, 2316, 7560, 2867, # 4950
- 4405, 1610, 1584, 3036, 2419, 2754, 443, 3269, 1163, 3136, 7561, 7562, 3926, 7563, 4143, 2499, # 4966
- 3037, 4406, 3927, 3137, 2103, 1647, 3545, 2010, 1872, 4144, 7564, 4145, 431, 3438, 7565, 250, # 4982
- 97, 81, 4146, 7566, 1648, 1850, 1558, 160, 848, 7567, 866, 740, 1694, 7568, 2201, 2830, # 4998
- 3195, 4147, 4407, 3653, 1687, 950, 2472, 426, 469, 3196, 3654, 3655, 3928, 7569, 7570, 1188, # 5014
- 424, 1995, 861, 3546, 4148, 3775, 2202, 2685, 168, 1235, 3547, 4149, 7571, 2086, 1674, 4408, # 5030
- 3337, 3270, 220, 2557, 1009, 7572, 3776, 670, 2992, 332, 1208, 717, 7573, 7574, 3548, 2447, # 5046
- 3929, 3338, 7575, 513, 7576, 1209, 2868, 3339, 3138, 4409, 1080, 7577, 7578, 7579, 7580, 2527, # 5062
- 3656, 3549, 815, 1587, 3930, 3931, 7581, 3550, 3439, 3777, 1254, 4410, 1328, 3038, 1390, 3932, # 5078
- 1741, 3933, 3778, 3934, 7582, 236, 3779, 2448, 3271, 7583, 7584, 3657, 3780, 1273, 3781, 4411, # 5094
- 7585, 308, 7586, 4412, 245, 4413, 1851, 2473, 1307, 2575, 430, 715, 2136, 2449, 7587, 270, # 5110
- 199, 2869, 3935, 7588, 3551, 2718, 1753, 761, 1754, 725, 1661, 1840, 4414, 3440, 3658, 7589, # 5126
- 7590, 587, 14, 3272, 227, 2598, 326, 480, 2265, 943, 2755, 3552, 291, 650, 1883, 7591, # 5142
- 1702, 1226, 102, 1547, 62, 3441, 904, 4415, 3442, 1164, 4150, 7592, 7593, 1224, 1548, 2756, # 5158
- 391, 498, 1493, 7594, 1386, 1419, 7595, 2055, 1177, 4416, 813, 880, 1081, 2363, 566, 1145, # 5174
- 4417, 2286, 1001, 1035, 2558, 2599, 2238, 394, 1286, 7596, 7597, 2068, 7598, 86, 1494, 1730, # 5190
- 3936, 491, 1588, 745, 897, 2948, 843, 3340, 3937, 2757, 2870, 3273, 1768, 998, 2217, 2069, # 5206
- 397, 1826, 1195, 1969, 3659, 2993, 3341, 284, 7599, 3782, 2500, 2137, 2119, 1903, 7600, 3938, # 5222
- 2150, 3939, 4151, 1036, 3443, 1904, 114, 2559, 4152, 209, 1527, 7601, 7602, 2949, 2831, 2625, # 5238
- 2385, 2719, 3139, 812, 2560, 7603, 3274, 7604, 1559, 737, 1884, 3660, 1210, 885, 28, 2686, # 5254
- 3553, 3783, 7605, 4153, 1004, 1779, 4418, 7606, 346, 1981, 2218, 2687, 4419, 3784, 1742, 797, # 5270
- 1642, 3940, 1933, 1072, 1384, 2151, 896, 3941, 3275, 3661, 3197, 2871, 3554, 7607, 2561, 1958, # 5286
- 4420, 2450, 1785, 7608, 7609, 7610, 3942, 4154, 1005, 1308, 3662, 4155, 2720, 4421, 4422, 1528, # 5302
- 2600, 161, 1178, 4156, 1982, 987, 4423, 1101, 4157, 631, 3943, 1157, 3198, 2420, 1343, 1241, # 5318
- 1016, 2239, 2562, 372, 877, 2339, 2501, 1160, 555, 1934, 911, 3944, 7611, 466, 1170, 169, # 5334
- 1051, 2907, 2688, 3663, 2474, 2994, 1182, 2011, 2563, 1251, 2626, 7612, 992, 2340, 3444, 1540, # 5350
- 2721, 1201, 2070, 2401, 1996, 2475, 7613, 4424, 528, 1922, 2188, 1503, 1873, 1570, 2364, 3342, # 5366
- 3276, 7614, 557, 1073, 7615, 1827, 3445, 2087, 2266, 3140, 3039, 3084, 767, 3085, 2786, 4425, # 5382
- 1006, 4158, 4426, 2341, 1267, 2176, 3664, 3199, 778, 3945, 3200, 2722, 1597, 2657, 7616, 4427, # 5398
- 7617, 3446, 7618, 7619, 7620, 3277, 2689, 1433, 3278, 131, 95, 1504, 3946, 723, 4159, 3141, # 5414
- 1841, 3555, 2758, 2189, 3947, 2027, 2104, 3665, 7621, 2995, 3948, 1218, 7622, 3343, 3201, 3949, # 5430
- 4160, 2576, 248, 1634, 3785, 912, 7623, 2832, 3666, 3040, 3786, 654, 53, 7624, 2996, 7625, # 5446
- 1688, 4428, 777, 3447, 1032, 3950, 1425, 7626, 191, 820, 2120, 2833, 971, 4429, 931, 3202, # 5462
- 135, 664, 783, 3787, 1997, 772, 2908, 1935, 3951, 3788, 4430, 2909, 3203, 282, 2723, 640, # 5478
- 1372, 3448, 1127, 922, 325, 3344, 7627, 7628, 711, 2044, 7629, 7630, 3952, 2219, 2787, 1936, # 5494
- 3953, 3345, 2220, 2251, 3789, 2300, 7631, 4431, 3790, 1258, 3279, 3954, 3204, 2138, 2950, 3955, # 5510
- 3956, 7632, 2221, 258, 3205, 4432, 101, 1227, 7633, 3280, 1755, 7634, 1391, 3281, 7635, 2910, # 5526
- 2056, 893, 7636, 7637, 7638, 1402, 4161, 2342, 7639, 7640, 3206, 3556, 7641, 7642, 878, 1325, # 5542
- 1780, 2788, 4433, 259, 1385, 2577, 744, 1183, 2267, 4434, 7643, 3957, 2502, 7644, 684, 1024, # 5558
- 4162, 7645, 472, 3557, 3449, 1165, 3282, 3958, 3959, 322, 2152, 881, 455, 1695, 1152, 1340, # 5574
- 660, 554, 2153, 4435, 1058, 4436, 4163, 830, 1065, 3346, 3960, 4437, 1923, 7646, 1703, 1918, # 5590
- 7647, 932, 2268, 122, 7648, 4438, 947, 677, 7649, 3791, 2627, 297, 1905, 1924, 2269, 4439, # 5606
- 2317, 3283, 7650, 7651, 4164, 7652, 4165, 84, 4166, 112, 989, 7653, 547, 1059, 3961, 701, # 5622
- 3558, 1019, 7654, 4167, 7655, 3450, 942, 639, 457, 2301, 2451, 993, 2951, 407, 851, 494, # 5638
- 4440, 3347, 927, 7656, 1237, 7657, 2421, 3348, 573, 4168, 680, 921, 2911, 1279, 1874, 285, # 5654
- 790, 1448, 1983, 719, 2167, 7658, 7659, 4441, 3962, 3963, 1649, 7660, 1541, 563, 7661, 1077, # 5670
- 7662, 3349, 3041, 3451, 511, 2997, 3964, 3965, 3667, 3966, 1268, 2564, 3350, 3207, 4442, 4443, # 5686
- 7663, 535, 1048, 1276, 1189, 2912, 2028, 3142, 1438, 1373, 2834, 2952, 1134, 2012, 7664, 4169, # 5702
- 1238, 2578, 3086, 1259, 7665, 700, 7666, 2953, 3143, 3668, 4170, 7667, 4171, 1146, 1875, 1906, # 5718
- 4444, 2601, 3967, 781, 2422, 132, 1589, 203, 147, 273, 2789, 2402, 898, 1786, 2154, 3968, # 5734
- 3969, 7668, 3792, 2790, 7669, 7670, 4445, 4446, 7671, 3208, 7672, 1635, 3793, 965, 7673, 1804, # 5750
- 2690, 1516, 3559, 1121, 1082, 1329, 3284, 3970, 1449, 3794, 65, 1128, 2835, 2913, 2759, 1590, # 5766
- 3795, 7674, 7675, 12, 2658, 45, 976, 2579, 3144, 4447, 517, 2528, 1013, 1037, 3209, 7676, # 5782
- 3796, 2836, 7677, 3797, 7678, 3452, 7679, 2602, 614, 1998, 2318, 3798, 3087, 2724, 2628, 7680, # 5798
- 2580, 4172, 599, 1269, 7681, 1810, 3669, 7682, 2691, 3088, 759, 1060, 489, 1805, 3351, 3285, # 5814
- 1358, 7683, 7684, 2386, 1387, 1215, 2629, 2252, 490, 7685, 7686, 4173, 1759, 2387, 2343, 7687, # 5830
- 4448, 3799, 1907, 3971, 2630, 1806, 3210, 4449, 3453, 3286, 2760, 2344, 874, 7688, 7689, 3454, # 5846
- 3670, 1858, 91, 2914, 3671, 3042, 3800, 4450, 7690, 3145, 3972, 2659, 7691, 3455, 1202, 1403, # 5862
- 3801, 2954, 2529, 1517, 2503, 4451, 3456, 2504, 7692, 4452, 7693, 2692, 1885, 1495, 1731, 3973, # 5878
- 2365, 4453, 7694, 2029, 7695, 7696, 3974, 2693, 1216, 237, 2581, 4174, 2319, 3975, 3802, 4454, # 5894
- 4455, 2694, 3560, 3457, 445, 4456, 7697, 7698, 7699, 7700, 2761, 61, 3976, 3672, 1822, 3977, # 5910
- 7701, 687, 2045, 935, 925, 405, 2660, 703, 1096, 1859, 2725, 4457, 3978, 1876, 1367, 2695, # 5926
- 3352, 918, 2105, 1781, 2476, 334, 3287, 1611, 1093, 4458, 564, 3146, 3458, 3673, 3353, 945, # 5942
- 2631, 2057, 4459, 7702, 1925, 872, 4175, 7703, 3459, 2696, 3089, 349, 4176, 3674, 3979, 4460, # 5958
- 3803, 4177, 3675, 2155, 3980, 4461, 4462, 4178, 4463, 2403, 2046, 782, 3981, 400, 251, 4179, # 5974
- 1624, 7704, 7705, 277, 3676, 299, 1265, 476, 1191, 3804, 2121, 4180, 4181, 1109, 205, 7706, # 5990
- 2582, 1000, 2156, 3561, 1860, 7707, 7708, 7709, 4464, 7710, 4465, 2565, 107, 2477, 2157, 3982, # 6006
- 3460, 3147, 7711, 1533, 541, 1301, 158, 753, 4182, 2872, 3562, 7712, 1696, 370, 1088, 4183, # 6022
- 4466, 3563, 579, 327, 440, 162, 2240, 269, 1937, 1374, 3461, 968, 3043, 56, 1396, 3090, # 6038
- 2106, 3288, 3354, 7713, 1926, 2158, 4467, 2998, 7714, 3564, 7715, 7716, 3677, 4468, 2478, 7717, # 6054
- 2791, 7718, 1650, 4469, 7719, 2603, 7720, 7721, 3983, 2661, 3355, 1149, 3356, 3984, 3805, 3985, # 6070
- 7722, 1076, 49, 7723, 951, 3211, 3289, 3290, 450, 2837, 920, 7724, 1811, 2792, 2366, 4184, # 6086
- 1908, 1138, 2367, 3806, 3462, 7725, 3212, 4470, 1909, 1147, 1518, 2423, 4471, 3807, 7726, 4472, # 6102
- 2388, 2604, 260, 1795, 3213, 7727, 7728, 3808, 3291, 708, 7729, 3565, 1704, 7730, 3566, 1351, # 6118
- 1618, 3357, 2999, 1886, 944, 4185, 3358, 4186, 3044, 3359, 4187, 7731, 3678, 422, 413, 1714, # 6134
- 3292, 500, 2058, 2345, 4188, 2479, 7732, 1344, 1910, 954, 7733, 1668, 7734, 7735, 3986, 2404, # 6150
- 4189, 3567, 3809, 4190, 7736, 2302, 1318, 2505, 3091, 133, 3092, 2873, 4473, 629, 31, 2838, # 6166
- 2697, 3810, 4474, 850, 949, 4475, 3987, 2955, 1732, 2088, 4191, 1496, 1852, 7737, 3988, 620, # 6182
- 3214, 981, 1242, 3679, 3360, 1619, 3680, 1643, 3293, 2139, 2452, 1970, 1719, 3463, 2168, 7738, # 6198
- 3215, 7739, 7740, 3361, 1828, 7741, 1277, 4476, 1565, 2047, 7742, 1636, 3568, 3093, 7743, 869, # 6214
- 2839, 655, 3811, 3812, 3094, 3989, 3000, 3813, 1310, 3569, 4477, 7744, 7745, 7746, 1733, 558, # 6230
- 4478, 3681, 335, 1549, 3045, 1756, 4192, 3682, 1945, 3464, 1829, 1291, 1192, 470, 2726, 2107, # 6246
- 2793, 913, 1054, 3990, 7747, 1027, 7748, 3046, 3991, 4479, 982, 2662, 3362, 3148, 3465, 3216, # 6262
- 3217, 1946, 2794, 7749, 571, 4480, 7750, 1830, 7751, 3570, 2583, 1523, 2424, 7752, 2089, 984, # 6278
- 4481, 3683, 1959, 7753, 3684, 852, 923, 2795, 3466, 3685, 969, 1519, 999, 2048, 2320, 1705, # 6294
- 7754, 3095, 615, 1662, 151, 597, 3992, 2405, 2321, 1049, 275, 4482, 3686, 4193, 568, 3687, # 6310
- 3571, 2480, 4194, 3688, 7755, 2425, 2270, 409, 3218, 7756, 1566, 2874, 3467, 1002, 769, 2840, # 6326
- 194, 2090, 3149, 3689, 2222, 3294, 4195, 628, 1505, 7757, 7758, 1763, 2177, 3001, 3993, 521, # 6342
- 1161, 2584, 1787, 2203, 2406, 4483, 3994, 1625, 4196, 4197, 412, 42, 3096, 464, 7759, 2632, # 6358
- 4484, 3363, 1760, 1571, 2875, 3468, 2530, 1219, 2204, 3814, 2633, 2140, 2368, 4485, 4486, 3295, # 6374
- 1651, 3364, 3572, 7760, 7761, 3573, 2481, 3469, 7762, 3690, 7763, 7764, 2271, 2091, 460, 7765, # 6390
- 4487, 7766, 3002, 962, 588, 3574, 289, 3219, 2634, 1116, 52, 7767, 3047, 1796, 7768, 7769, # 6406
- 7770, 1467, 7771, 1598, 1143, 3691, 4198, 1984, 1734, 1067, 4488, 1280, 3365, 465, 4489, 1572, # 6422
- 510, 7772, 1927, 2241, 1812, 1644, 3575, 7773, 4490, 3692, 7774, 7775, 2663, 1573, 1534, 7776, # 6438
- 7777, 4199, 536, 1807, 1761, 3470, 3815, 3150, 2635, 7778, 7779, 7780, 4491, 3471, 2915, 1911, # 6454
- 2796, 7781, 3296, 1122, 377, 3220, 7782, 360, 7783, 7784, 4200, 1529, 551, 7785, 2059, 3693, # 6470
- 1769, 2426, 7786, 2916, 4201, 3297, 3097, 2322, 2108, 2030, 4492, 1404, 136, 1468, 1479, 672, # 6486
- 1171, 3221, 2303, 271, 3151, 7787, 2762, 7788, 2049, 678, 2727, 865, 1947, 4493, 7789, 2013, # 6502
- 3995, 2956, 7790, 2728, 2223, 1397, 3048, 3694, 4494, 4495, 1735, 2917, 3366, 3576, 7791, 3816, # 6518
- 509, 2841, 2453, 2876, 3817, 7792, 7793, 3152, 3153, 4496, 4202, 2531, 4497, 2304, 1166, 1010, # 6534
- 552, 681, 1887, 7794, 7795, 2957, 2958, 3996, 1287, 1596, 1861, 3154, 358, 453, 736, 175, # 6550
- 478, 1117, 905, 1167, 1097, 7796, 1853, 1530, 7797, 1706, 7798, 2178, 3472, 2287, 3695, 3473, # 6566
- 3577, 4203, 2092, 4204, 7799, 3367, 1193, 2482, 4205, 1458, 2190, 2205, 1862, 1888, 1421, 3298, # 6582
- 2918, 3049, 2179, 3474, 595, 2122, 7800, 3997, 7801, 7802, 4206, 1707, 2636, 223, 3696, 1359, # 6598
- 751, 3098, 183, 3475, 7803, 2797, 3003, 419, 2369, 633, 704, 3818, 2389, 241, 7804, 7805, # 6614
- 7806, 838, 3004, 3697, 2272, 2763, 2454, 3819, 1938, 2050, 3998, 1309, 3099, 2242, 1181, 7807, # 6630
- 1136, 2206, 3820, 2370, 1446, 4207, 2305, 4498, 7808, 7809, 4208, 1055, 2605, 484, 3698, 7810, # 6646
- 3999, 625, 4209, 2273, 3368, 1499, 4210, 4000, 7811, 4001, 4211, 3222, 2274, 2275, 3476, 7812, # 6662
- 7813, 2764, 808, 2606, 3699, 3369, 4002, 4212, 3100, 2532, 526, 3370, 3821, 4213, 955, 7814, # 6678
- 1620, 4214, 2637, 2427, 7815, 1429, 3700, 1669, 1831, 994, 928, 7816, 3578, 1260, 7817, 7818, # 6694
- 7819, 1948, 2288, 741, 2919, 1626, 4215, 2729, 2455, 867, 1184, 362, 3371, 1392, 7820, 7821, # 6710
- 4003, 4216, 1770, 1736, 3223, 2920, 4499, 4500, 1928, 2698, 1459, 1158, 7822, 3050, 3372, 2877, # 6726
- 1292, 1929, 2506, 2842, 3701, 1985, 1187, 2071, 2014, 2607, 4217, 7823, 2566, 2507, 2169, 3702, # 6742
- 2483, 3299, 7824, 3703, 4501, 7825, 7826, 666, 1003, 3005, 1022, 3579, 4218, 7827, 4502, 1813, # 6758
- 2253, 574, 3822, 1603, 295, 1535, 705, 3823, 4219, 283, 858, 417, 7828, 7829, 3224, 4503, # 6774
- 4504, 3051, 1220, 1889, 1046, 2276, 2456, 4004, 1393, 1599, 689, 2567, 388, 4220, 7830, 2484, # 6790
- 802, 7831, 2798, 3824, 2060, 1405, 2254, 7832, 4505, 3825, 2109, 1052, 1345, 3225, 1585, 7833, # 6806
- 809, 7834, 7835, 7836, 575, 2730, 3477, 956, 1552, 1469, 1144, 2323, 7837, 2324, 1560, 2457, # 6822
- 3580, 3226, 4005, 616, 2207, 3155, 2180, 2289, 7838, 1832, 7839, 3478, 4506, 7840, 1319, 3704, # 6838
- 3705, 1211, 3581, 1023, 3227, 1293, 2799, 7841, 7842, 7843, 3826, 607, 2306, 3827, 762, 2878, # 6854
- 1439, 4221, 1360, 7844, 1485, 3052, 7845, 4507, 1038, 4222, 1450, 2061, 2638, 4223, 1379, 4508, # 6870
- 2585, 7846, 7847, 4224, 1352, 1414, 2325, 2921, 1172, 7848, 7849, 3828, 3829, 7850, 1797, 1451, # 6886
- 7851, 7852, 7853, 7854, 2922, 4006, 4007, 2485, 2346, 411, 4008, 4009, 3582, 3300, 3101, 4509, # 6902
- 1561, 2664, 1452, 4010, 1375, 7855, 7856, 47, 2959, 316, 7857, 1406, 1591, 2923, 3156, 7858, # 6918
- 1025, 2141, 3102, 3157, 354, 2731, 884, 2224, 4225, 2407, 508, 3706, 726, 3583, 996, 2428, # 6934
- 3584, 729, 7859, 392, 2191, 1453, 4011, 4510, 3707, 7860, 7861, 2458, 3585, 2608, 1675, 2800, # 6950
- 919, 2347, 2960, 2348, 1270, 4511, 4012, 73, 7862, 7863, 647, 7864, 3228, 2843, 2255, 1550, # 6966
- 1346, 3006, 7865, 1332, 883, 3479, 7866, 7867, 7868, 7869, 3301, 2765, 7870, 1212, 831, 1347, # 6982
- 4226, 4512, 2326, 3830, 1863, 3053, 720, 3831, 4513, 4514, 3832, 7871, 4227, 7872, 7873, 4515, # 6998
- 7874, 7875, 1798, 4516, 3708, 2609, 4517, 3586, 1645, 2371, 7876, 7877, 2924, 669, 2208, 2665, # 7014
- 2429, 7878, 2879, 7879, 7880, 1028, 3229, 7881, 4228, 2408, 7882, 2256, 1353, 7883, 7884, 4518, # 7030
- 3158, 518, 7885, 4013, 7886, 4229, 1960, 7887, 2142, 4230, 7888, 7889, 3007, 2349, 2350, 3833, # 7046
- 516, 1833, 1454, 4014, 2699, 4231, 4519, 2225, 2610, 1971, 1129, 3587, 7890, 2766, 7891, 2961, # 7062
- 1422, 577, 1470, 3008, 1524, 3373, 7892, 7893, 432, 4232, 3054, 3480, 7894, 2586, 1455, 2508, # 7078
- 2226, 1972, 1175, 7895, 1020, 2732, 4015, 3481, 4520, 7896, 2733, 7897, 1743, 1361, 3055, 3482, # 7094
- 2639, 4016, 4233, 4521, 2290, 895, 924, 4234, 2170, 331, 2243, 3056, 166, 1627, 3057, 1098, # 7110
- 7898, 1232, 2880, 2227, 3374, 4522, 657, 403, 1196, 2372, 542, 3709, 3375, 1600, 4235, 3483, # 7126
- 7899, 4523, 2767, 3230, 576, 530, 1362, 7900, 4524, 2533, 2666, 3710, 4017, 7901, 842, 3834, # 7142
- 7902, 2801, 2031, 1014, 4018, 213, 2700, 3376, 665, 621, 4236, 7903, 3711, 2925, 2430, 7904, # 7158
- 2431, 3302, 3588, 3377, 7905, 4237, 2534, 4238, 4525, 3589, 1682, 4239, 3484, 1380, 7906, 724, # 7174
- 2277, 600, 1670, 7907, 1337, 1233, 4526, 3103, 2244, 7908, 1621, 4527, 7909, 651, 4240, 7910, # 7190
- 1612, 4241, 2611, 7911, 2844, 7912, 2734, 2307, 3058, 7913, 716, 2459, 3059, 174, 1255, 2701, # 7206
- 4019, 3590, 548, 1320, 1398, 728, 4020, 1574, 7914, 1890, 1197, 3060, 4021, 7915, 3061, 3062, # 7222
- 3712, 3591, 3713, 747, 7916, 635, 4242, 4528, 7917, 7918, 7919, 4243, 7920, 7921, 4529, 7922, # 7238
- 3378, 4530, 2432, 451, 7923, 3714, 2535, 2072, 4244, 2735, 4245, 4022, 7924, 1764, 4531, 7925, # 7254
- 4246, 350, 7926, 2278, 2390, 2486, 7927, 4247, 4023, 2245, 1434, 4024, 488, 4532, 458, 4248, # 7270
- 4025, 3715, 771, 1330, 2391, 3835, 2568, 3159, 2159, 2409, 1553, 2667, 3160, 4249, 7928, 2487, # 7286
- 2881, 2612, 1720, 2702, 4250, 3379, 4533, 7929, 2536, 4251, 7930, 3231, 4252, 2768, 7931, 2015, # 7302
- 2736, 7932, 1155, 1017, 3716, 3836, 7933, 3303, 2308, 201, 1864, 4253, 1430, 7934, 4026, 7935, # 7318
- 7936, 7937, 7938, 7939, 4254, 1604, 7940, 414, 1865, 371, 2587, 4534, 4535, 3485, 2016, 3104, # 7334
- 4536, 1708, 960, 4255, 887, 389, 2171, 1536, 1663, 1721, 7941, 2228, 4027, 2351, 2926, 1580, # 7350
- 7942, 7943, 7944, 1744, 7945, 2537, 4537, 4538, 7946, 4539, 7947, 2073, 7948, 7949, 3592, 3380, # 7366
- 2882, 4256, 7950, 4257, 2640, 3381, 2802, 673, 2703, 2460, 709, 3486, 4028, 3593, 4258, 7951, # 7382
- 1148, 502, 634, 7952, 7953, 1204, 4540, 3594, 1575, 4541, 2613, 3717, 7954, 3718, 3105, 948, # 7398
- 3232, 121, 1745, 3837, 1110, 7955, 4259, 3063, 2509, 3009, 4029, 3719, 1151, 1771, 3838, 1488, # 7414
- 4030, 1986, 7956, 2433, 3487, 7957, 7958, 2093, 7959, 4260, 3839, 1213, 1407, 2803, 531, 2737, # 7430
- 2538, 3233, 1011, 1537, 7960, 2769, 4261, 3106, 1061, 7961, 3720, 3721, 1866, 2883, 7962, 2017, # 7446
- 120, 4262, 4263, 2062, 3595, 3234, 2309, 3840, 2668, 3382, 1954, 4542, 7963, 7964, 3488, 1047, # 7462
- 2704, 1266, 7965, 1368, 4543, 2845, 649, 3383, 3841, 2539, 2738, 1102, 2846, 2669, 7966, 7967, # 7478
- 1999, 7968, 1111, 3596, 2962, 7969, 2488, 3842, 3597, 2804, 1854, 3384, 3722, 7970, 7971, 3385, # 7494
- 2410, 2884, 3304, 3235, 3598, 7972, 2569, 7973, 3599, 2805, 4031, 1460, 856, 7974, 3600, 7975, # 7510
- 2885, 2963, 7976, 2886, 3843, 7977, 4264, 632, 2510, 875, 3844, 1697, 3845, 2291, 7978, 7979, # 7526
- 4544, 3010, 1239, 580, 4545, 4265, 7980, 914, 936, 2074, 1190, 4032, 1039, 2123, 7981, 7982, # 7542
- 7983, 3386, 1473, 7984, 1354, 4266, 3846, 7985, 2172, 3064, 4033, 915, 3305, 4267, 4268, 3306, # 7558
- 1605, 1834, 7986, 2739, 398, 3601, 4269, 3847, 4034, 328, 1912, 2847, 4035, 3848, 1331, 4270, # 7574
- 3011, 937, 4271, 7987, 3602, 4036, 4037, 3387, 2160, 4546, 3388, 524, 742, 538, 3065, 1012, # 7590
- 7988, 7989, 3849, 2461, 7990, 658, 1103, 225, 3850, 7991, 7992, 4547, 7993, 4548, 7994, 3236, # 7606
- 1243, 7995, 4038, 963, 2246, 4549, 7996, 2705, 3603, 3161, 7997, 7998, 2588, 2327, 7999, 4550, # 7622
- 8000, 8001, 8002, 3489, 3307, 957, 3389, 2540, 2032, 1930, 2927, 2462, 870, 2018, 3604, 1746, # 7638
- 2770, 2771, 2434, 2463, 8003, 3851, 8004, 3723, 3107, 3724, 3490, 3390, 3725, 8005, 1179, 3066, # 7654
- 8006, 3162, 2373, 4272, 3726, 2541, 3163, 3108, 2740, 4039, 8007, 3391, 1556, 2542, 2292, 977, # 7670
- 2887, 2033, 4040, 1205, 3392, 8008, 1765, 3393, 3164, 2124, 1271, 1689, 714, 4551, 3491, 8009, # 7686
- 2328, 3852, 533, 4273, 3605, 2181, 617, 8010, 2464, 3308, 3492, 2310, 8011, 8012, 3165, 8013, # 7702
- 8014, 3853, 1987, 618, 427, 2641, 3493, 3394, 8015, 8016, 1244, 1690, 8017, 2806, 4274, 4552, # 7718
- 8018, 3494, 8019, 8020, 2279, 1576, 473, 3606, 4275, 3395, 972, 8021, 3607, 8022, 3067, 8023, # 7734
- 8024, 4553, 4554, 8025, 3727, 4041, 4042, 8026, 153, 4555, 356, 8027, 1891, 2888, 4276, 2143, # 7750
- 408, 803, 2352, 8028, 3854, 8029, 4277, 1646, 2570, 2511, 4556, 4557, 3855, 8030, 3856, 4278, # 7766
- 8031, 2411, 3396, 752, 8032, 8033, 1961, 2964, 8034, 746, 3012, 2465, 8035, 4279, 3728, 698, # 7782
- 4558, 1892, 4280, 3608, 2543, 4559, 3609, 3857, 8036, 3166, 3397, 8037, 1823, 1302, 4043, 2706, # 7798
- 3858, 1973, 4281, 8038, 4282, 3167, 823, 1303, 1288, 1236, 2848, 3495, 4044, 3398, 774, 3859, # 7814
- 8039, 1581, 4560, 1304, 2849, 3860, 4561, 8040, 2435, 2161, 1083, 3237, 4283, 4045, 4284, 344, # 7830
- 1173, 288, 2311, 454, 1683, 8041, 8042, 1461, 4562, 4046, 2589, 8043, 8044, 4563, 985, 894, # 7846
- 8045, 3399, 3168, 8046, 1913, 2928, 3729, 1988, 8047, 2110, 1974, 8048, 4047, 8049, 2571, 1194, # 7862
- 425, 8050, 4564, 3169, 1245, 3730, 4285, 8051, 8052, 2850, 8053, 636, 4565, 1855, 3861, 760, # 7878
- 1799, 8054, 4286, 2209, 1508, 4566, 4048, 1893, 1684, 2293, 8055, 8056, 8057, 4287, 4288, 2210, # 7894
- 479, 8058, 8059, 832, 8060, 4049, 2489, 8061, 2965, 2490, 3731, 990, 3109, 627, 1814, 2642, # 7910
- 4289, 1582, 4290, 2125, 2111, 3496, 4567, 8062, 799, 4291, 3170, 8063, 4568, 2112, 1737, 3013, # 7926
- 1018, 543, 754, 4292, 3309, 1676, 4569, 4570, 4050, 8064, 1489, 8065, 3497, 8066, 2614, 2889, # 7942
- 4051, 8067, 8068, 2966, 8069, 8070, 8071, 8072, 3171, 4571, 4572, 2182, 1722, 8073, 3238, 3239, # 7958
- 1842, 3610, 1715, 481, 365, 1975, 1856, 8074, 8075, 1962, 2491, 4573, 8076, 2126, 3611, 3240, # 7974
- 433, 1894, 2063, 2075, 8077, 602, 2741, 8078, 8079, 8080, 8081, 8082, 3014, 1628, 3400, 8083, # 7990
- 3172, 4574, 4052, 2890, 4575, 2512, 8084, 2544, 2772, 8085, 8086, 8087, 3310, 4576, 2891, 8088, # 8006
- 4577, 8089, 2851, 4578, 4579, 1221, 2967, 4053, 2513, 8090, 8091, 8092, 1867, 1989, 8093, 8094, # 8022
- 8095, 1895, 8096, 8097, 4580, 1896, 4054, 318, 8098, 2094, 4055, 4293, 8099, 8100, 485, 8101, # 8038
- 938, 3862, 553, 2670, 116, 8102, 3863, 3612, 8103, 3498, 2671, 2773, 3401, 3311, 2807, 8104, # 8054
- 3613, 2929, 4056, 1747, 2930, 2968, 8105, 8106, 207, 8107, 8108, 2672, 4581, 2514, 8109, 3015, # 8070
- 890, 3614, 3864, 8110, 1877, 3732, 3402, 8111, 2183, 2353, 3403, 1652, 8112, 8113, 8114, 941, # 8086
- 2294, 208, 3499, 4057, 2019, 330, 4294, 3865, 2892, 2492, 3733, 4295, 8115, 8116, 8117, 8118, # 8102
-)
-# fmt: on
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/euctwprober.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/euctwprober.py
deleted file mode 100644
index a37ab18..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/euctwprober.py
+++ /dev/null
@@ -1,47 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is mozilla.org code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-from .chardistribution import EUCTWDistributionAnalysis
-from .codingstatemachine import CodingStateMachine
-from .mbcharsetprober import MultiByteCharSetProber
-from .mbcssm import EUCTW_SM_MODEL
-
-
-class EUCTWProber(MultiByteCharSetProber):
- def __init__(self) -> None:
- super().__init__()
- self.coding_sm = CodingStateMachine(EUCTW_SM_MODEL)
- self.distribution_analyzer = EUCTWDistributionAnalysis()
- self.reset()
-
- @property
- def charset_name(self) -> str:
- return "EUC-TW"
-
- @property
- def language(self) -> str:
- return "Taiwan"
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/gb2312freq.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/gb2312freq.py
deleted file mode 100644
index b32bfc7..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/gb2312freq.py
+++ /dev/null
@@ -1,284 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is Mozilla Communicator client code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-# GB2312 most frequently used character table
-#
-# Char to FreqOrder table , from hz6763
-
-# 512 --> 0.79 -- 0.79
-# 1024 --> 0.92 -- 0.13
-# 2048 --> 0.98 -- 0.06
-# 6768 --> 1.00 -- 0.02
-#
-# Ideal Distribution Ratio = 0.79135/(1-0.79135) = 3.79
-# Random Distribution Ration = 512 / (3755 - 512) = 0.157
-#
-# Typical Distribution Ratio about 25% of Ideal one, still much higher that RDR
-
-GB2312_TYPICAL_DISTRIBUTION_RATIO = 0.9
-
-GB2312_TABLE_SIZE = 3760
-
-# fmt: off
-GB2312_CHAR_TO_FREQ_ORDER = (
-1671, 749,1443,2364,3924,3807,2330,3921,1704,3463,2691,1511,1515, 572,3191,2205,
-2361, 224,2558, 479,1711, 963,3162, 440,4060,1905,2966,2947,3580,2647,3961,3842,
-2204, 869,4207, 970,2678,5626,2944,2956,1479,4048, 514,3595, 588,1346,2820,3409,
- 249,4088,1746,1873,2047,1774, 581,1813, 358,1174,3590,1014,1561,4844,2245, 670,
-1636,3112, 889,1286, 953, 556,2327,3060,1290,3141, 613, 185,3477,1367, 850,3820,
-1715,2428,2642,2303,2732,3041,2562,2648,3566,3946,1349, 388,3098,2091,1360,3585,
- 152,1687,1539, 738,1559, 59,1232,2925,2267,1388,1249,1741,1679,2960, 151,1566,
-1125,1352,4271, 924,4296, 385,3166,4459, 310,1245,2850, 70,3285,2729,3534,3575,
-2398,3298,3466,1960,2265, 217,3647, 864,1909,2084,4401,2773,1010,3269,5152, 853,
-3051,3121,1244,4251,1895, 364,1499,1540,2313,1180,3655,2268, 562, 715,2417,3061,
- 544, 336,3768,2380,1752,4075, 950, 280,2425,4382, 183,2759,3272, 333,4297,2155,
-1688,2356,1444,1039,4540, 736,1177,3349,2443,2368,2144,2225, 565, 196,1482,3406,
- 927,1335,4147, 692, 878,1311,1653,3911,3622,1378,4200,1840,2969,3149,2126,1816,
-2534,1546,2393,2760, 737,2494, 13, 447, 245,2747, 38,2765,2129,2589,1079, 606,
- 360, 471,3755,2890, 404, 848, 699,1785,1236, 370,2221,1023,3746,2074,2026,2023,
-2388,1581,2119, 812,1141,3091,2536,1519, 804,2053, 406,1596,1090, 784, 548,4414,
-1806,2264,2936,1100, 343,4114,5096, 622,3358, 743,3668,1510,1626,5020,3567,2513,
-3195,4115,5627,2489,2991, 24,2065,2697,1087,2719, 48,1634, 315, 68, 985,2052,
- 198,2239,1347,1107,1439, 597,2366,2172, 871,3307, 919,2487,2790,1867, 236,2570,
-1413,3794, 906,3365,3381,1701,1982,1818,1524,2924,1205, 616,2586,2072,2004, 575,
- 253,3099, 32,1365,1182, 197,1714,2454,1201, 554,3388,3224,2748, 756,2587, 250,
-2567,1507,1517,3529,1922,2761,2337,3416,1961,1677,2452,2238,3153, 615, 911,1506,
-1474,2495,1265,1906,2749,3756,3280,2161, 898,2714,1759,3450,2243,2444, 563, 26,
-3286,2266,3769,3344,2707,3677, 611,1402, 531,1028,2871,4548,1375, 261,2948, 835,
-1190,4134, 353, 840,2684,1900,3082,1435,2109,1207,1674, 329,1872,2781,4055,2686,
-2104, 608,3318,2423,2957,2768,1108,3739,3512,3271,3985,2203,1771,3520,1418,2054,
-1681,1153, 225,1627,2929, 162,2050,2511,3687,1954, 124,1859,2431,1684,3032,2894,
- 585,4805,3969,2869,2704,2088,2032,2095,3656,2635,4362,2209, 256, 518,2042,2105,
-3777,3657, 643,2298,1148,1779, 190, 989,3544, 414, 11,2135,2063,2979,1471, 403,
-3678, 126, 770,1563, 671,2499,3216,2877, 600,1179, 307,2805,4937,1268,1297,2694,
- 252,4032,1448,1494,1331,1394, 127,2256, 222,1647,1035,1481,3056,1915,1048, 873,
-3651, 210, 33,1608,2516, 200,1520, 415, 102, 0,3389,1287, 817, 91,3299,2940,
- 836,1814, 549,2197,1396,1669,2987,3582,2297,2848,4528,1070, 687, 20,1819, 121,
-1552,1364,1461,1968,2617,3540,2824,2083, 177, 948,4938,2291, 110,4549,2066, 648,
-3359,1755,2110,2114,4642,4845,1693,3937,3308,1257,1869,2123, 208,1804,3159,2992,
-2531,2549,3361,2418,1350,2347,2800,2568,1291,2036,2680, 72, 842,1990, 212,1233,
-1154,1586, 75,2027,3410,4900,1823,1337,2710,2676, 728,2810,1522,3026,4995, 157,
- 755,1050,4022, 710, 785,1936,2194,2085,1406,2777,2400, 150,1250,4049,1206, 807,
-1910, 534, 529,3309,1721,1660, 274, 39,2827, 661,2670,1578, 925,3248,3815,1094,
-4278,4901,4252, 41,1150,3747,2572,2227,4501,3658,4902,3813,3357,3617,2884,2258,
- 887, 538,4187,3199,1294,2439,3042,2329,2343,2497,1255, 107, 543,1527, 521,3478,
-3568, 194,5062, 15, 961,3870,1241,1192,2664, 66,5215,3260,2111,1295,1127,2152,
-3805,4135, 901,1164,1976, 398,1278, 530,1460, 748, 904,1054,1966,1426, 53,2909,
- 509, 523,2279,1534, 536,1019, 239,1685, 460,2353, 673,1065,2401,3600,4298,2272,
-1272,2363, 284,1753,3679,4064,1695, 81, 815,2677,2757,2731,1386, 859, 500,4221,
-2190,2566, 757,1006,2519,2068,1166,1455, 337,2654,3203,1863,1682,1914,3025,1252,
-1409,1366, 847, 714,2834,2038,3209, 964,2970,1901, 885,2553,1078,1756,3049, 301,
-1572,3326, 688,2130,1996,2429,1805,1648,2930,3421,2750,3652,3088, 262,1158,1254,
- 389,1641,1812, 526,1719, 923,2073,1073,1902, 468, 489,4625,1140, 857,2375,3070,
-3319,2863, 380, 116,1328,2693,1161,2244, 273,1212,1884,2769,3011,1775,1142, 461,
-3066,1200,2147,2212, 790, 702,2695,4222,1601,1058, 434,2338,5153,3640, 67,2360,
-4099,2502, 618,3472,1329, 416,1132, 830,2782,1807,2653,3211,3510,1662, 192,2124,
- 296,3979,1739,1611,3684, 23, 118, 324, 446,1239,1225, 293,2520,3814,3795,2535,
-3116, 17,1074, 467,2692,2201, 387,2922, 45,1326,3055,1645,3659,2817, 958, 243,
-1903,2320,1339,2825,1784,3289, 356, 576, 865,2315,2381,3377,3916,1088,3122,1713,
-1655, 935, 628,4689,1034,1327, 441, 800, 720, 894,1979,2183,1528,5289,2702,1071,
-4046,3572,2399,1571,3281, 79, 761,1103, 327, 134, 758,1899,1371,1615, 879, 442,
- 215,2605,2579, 173,2048,2485,1057,2975,3317,1097,2253,3801,4263,1403,1650,2946,
- 814,4968,3487,1548,2644,1567,1285, 2, 295,2636, 97, 946,3576, 832, 141,4257,
-3273, 760,3821,3521,3156,2607, 949,1024,1733,1516,1803,1920,2125,2283,2665,3180,
-1501,2064,3560,2171,1592, 803,3518,1416, 732,3897,4258,1363,1362,2458, 119,1427,
- 602,1525,2608,1605,1639,3175, 694,3064, 10, 465, 76,2000,4846,4208, 444,3781,
-1619,3353,2206,1273,3796, 740,2483, 320,1723,2377,3660,2619,1359,1137,1762,1724,
-2345,2842,1850,1862, 912, 821,1866, 612,2625,1735,2573,3369,1093, 844, 89, 937,
- 930,1424,3564,2413,2972,1004,3046,3019,2011, 711,3171,1452,4178, 428, 801,1943,
- 432, 445,2811, 206,4136,1472, 730, 349, 73, 397,2802,2547, 998,1637,1167, 789,
- 396,3217, 154,1218, 716,1120,1780,2819,4826,1931,3334,3762,2139,1215,2627, 552,
-3664,3628,3232,1405,2383,3111,1356,2652,3577,3320,3101,1703, 640,1045,1370,1246,
-4996, 371,1575,2436,1621,2210, 984,4033,1734,2638, 16,4529, 663,2755,3255,1451,
-3917,2257,1253,1955,2234,1263,2951, 214,1229, 617, 485, 359,1831,1969, 473,2310,
- 750,2058, 165, 80,2864,2419, 361,4344,2416,2479,1134, 796,3726,1266,2943, 860,
-2715, 938, 390,2734,1313,1384, 248, 202, 877,1064,2854, 522,3907, 279,1602, 297,
-2357, 395,3740, 137,2075, 944,4089,2584,1267,3802, 62,1533,2285, 178, 176, 780,
-2440, 201,3707, 590, 478,1560,4354,2117,1075, 30, 74,4643,4004,1635,1441,2745,
- 776,2596, 238,1077,1692,1912,2844, 605, 499,1742,3947, 241,3053, 980,1749, 936,
-2640,4511,2582, 515,1543,2162,5322,2892,2993, 890,2148,1924, 665,1827,3581,1032,
- 968,3163, 339,1044,1896, 270, 583,1791,1720,4367,1194,3488,3669, 43,2523,1657,
- 163,2167, 290,1209,1622,3378, 550, 634,2508,2510, 695,2634,2384,2512,1476,1414,
- 220,1469,2341,2138,2852,3183,2900,4939,2865,3502,1211,3680, 854,3227,1299,2976,
-3172, 186,2998,1459, 443,1067,3251,1495, 321,1932,3054, 909, 753,1410,1828, 436,
-2441,1119,1587,3164,2186,1258, 227, 231,1425,1890,3200,3942, 247, 959, 725,5254,
-2741, 577,2158,2079, 929, 120, 174, 838,2813, 591,1115, 417,2024, 40,3240,1536,
-1037, 291,4151,2354, 632,1298,2406,2500,3535,1825,1846,3451, 205,1171, 345,4238,
- 18,1163, 811, 685,2208,1217, 425,1312,1508,1175,4308,2552,1033, 587,1381,3059,
-2984,3482, 340,1316,4023,3972, 792,3176, 519, 777,4690, 918, 933,4130,2981,3741,
- 90,3360,2911,2200,5184,4550, 609,3079,2030, 272,3379,2736, 363,3881,1130,1447,
- 286, 779, 357,1169,3350,3137,1630,1220,2687,2391, 747,1277,3688,2618,2682,2601,
-1156,3196,5290,4034,3102,1689,3596,3128, 874, 219,2783, 798, 508,1843,2461, 269,
-1658,1776,1392,1913,2983,3287,2866,2159,2372, 829,4076, 46,4253,2873,1889,1894,
- 915,1834,1631,2181,2318, 298, 664,2818,3555,2735, 954,3228,3117, 527,3511,2173,
- 681,2712,3033,2247,2346,3467,1652, 155,2164,3382, 113,1994, 450, 899, 494, 994,
-1237,2958,1875,2336,1926,3727, 545,1577,1550, 633,3473, 204,1305,3072,2410,1956,
-2471, 707,2134, 841,2195,2196,2663,3843,1026,4940, 990,3252,4997, 368,1092, 437,
-3212,3258,1933,1829, 675,2977,2893, 412, 943,3723,4644,3294,3283,2230,2373,5154,
-2389,2241,2661,2323,1404,2524, 593, 787, 677,3008,1275,2059, 438,2709,2609,2240,
-2269,2246,1446, 36,1568,1373,3892,1574,2301,1456,3962, 693,2276,5216,2035,1143,
-2720,1919,1797,1811,2763,4137,2597,1830,1699,1488,1198,2090, 424,1694, 312,3634,
-3390,4179,3335,2252,1214, 561,1059,3243,2295,2561, 975,5155,2321,2751,3772, 472,
-1537,3282,3398,1047,2077,2348,2878,1323,3340,3076, 690,2906, 51, 369, 170,3541,
-1060,2187,2688,3670,2541,1083,1683, 928,3918, 459, 109,4427, 599,3744,4286, 143,
-2101,2730,2490, 82,1588,3036,2121, 281,1860, 477,4035,1238,2812,3020,2716,3312,
-1530,2188,2055,1317, 843, 636,1808,1173,3495, 649, 181,1002, 147,3641,1159,2414,
-3750,2289,2795, 813,3123,2610,1136,4368, 5,3391,4541,2174, 420, 429,1728, 754,
-1228,2115,2219, 347,2223,2733, 735,1518,3003,2355,3134,1764,3948,3329,1888,2424,
-1001,1234,1972,3321,3363,1672,1021,1450,1584, 226, 765, 655,2526,3404,3244,2302,
-3665, 731, 594,2184, 319,1576, 621, 658,2656,4299,2099,3864,1279,2071,2598,2739,
- 795,3086,3699,3908,1707,2352,2402,1382,3136,2475,1465,4847,3496,3865,1085,3004,
-2591,1084, 213,2287,1963,3565,2250, 822, 793,4574,3187,1772,1789,3050, 595,1484,
-1959,2770,1080,2650, 456, 422,2996, 940,3322,4328,4345,3092,2742, 965,2784, 739,
-4124, 952,1358,2498,2949,2565, 332,2698,2378, 660,2260,2473,4194,3856,2919, 535,
-1260,2651,1208,1428,1300,1949,1303,2942, 433,2455,2450,1251,1946, 614,1269, 641,
-1306,1810,2737,3078,2912, 564,2365,1419,1415,1497,4460,2367,2185,1379,3005,1307,
-3218,2175,1897,3063, 682,1157,4040,4005,1712,1160,1941,1399, 394, 402,2952,1573,
-1151,2986,2404, 862, 299,2033,1489,3006, 346, 171,2886,3401,1726,2932, 168,2533,
- 47,2507,1030,3735,1145,3370,1395,1318,1579,3609,4560,2857,4116,1457,2529,1965,
- 504,1036,2690,2988,2405, 745,5871, 849,2397,2056,3081, 863,2359,3857,2096, 99,
-1397,1769,2300,4428,1643,3455,1978,1757,3718,1440, 35,4879,3742,1296,4228,2280,
- 160,5063,1599,2013, 166, 520,3479,1646,3345,3012, 490,1937,1545,1264,2182,2505,
-1096,1188,1369,1436,2421,1667,2792,2460,1270,2122, 727,3167,2143, 806,1706,1012,
-1800,3037, 960,2218,1882, 805, 139,2456,1139,1521, 851,1052,3093,3089, 342,2039,
- 744,5097,1468,1502,1585,2087, 223, 939, 326,2140,2577, 892,2481,1623,4077, 982,
-3708, 135,2131, 87,2503,3114,2326,1106, 876,1616, 547,2997,2831,2093,3441,4530,
-4314, 9,3256,4229,4148, 659,1462,1986,1710,2046,2913,2231,4090,4880,5255,3392,
-3274,1368,3689,4645,1477, 705,3384,3635,1068,1529,2941,1458,3782,1509, 100,1656,
-2548, 718,2339, 408,1590,2780,3548,1838,4117,3719,1345,3530, 717,3442,2778,3220,
-2898,1892,4590,3614,3371,2043,1998,1224,3483, 891, 635, 584,2559,3355, 733,1766,
-1729,1172,3789,1891,2307, 781,2982,2271,1957,1580,5773,2633,2005,4195,3097,1535,
-3213,1189,1934,5693,3262, 586,3118,1324,1598, 517,1564,2217,1868,1893,4445,3728,
-2703,3139,1526,1787,1992,3882,2875,1549,1199,1056,2224,1904,2711,5098,4287, 338,
-1993,3129,3489,2689,1809,2815,1997, 957,1855,3898,2550,3275,3057,1105,1319, 627,
-1505,1911,1883,3526, 698,3629,3456,1833,1431, 746, 77,1261,2017,2296,1977,1885,
- 125,1334,1600, 525,1798,1109,2222,1470,1945, 559,2236,1186,3443,2476,1929,1411,
-2411,3135,1777,3372,2621,1841,1613,3229, 668,1430,1839,2643,2916, 195,1989,2671,
-2358,1387, 629,3205,2293,5256,4439, 123,1310, 888,1879,4300,3021,3605,1003,1162,
-3192,2910,2010, 140,2395,2859, 55,1082,2012,2901, 662, 419,2081,1438, 680,2774,
-4654,3912,1620,1731,1625,5035,4065,2328, 512,1344, 802,5443,2163,2311,2537, 524,
-3399, 98,1155,2103,1918,2606,3925,2816,1393,2465,1504,3773,2177,3963,1478,4346,
- 180,1113,4655,3461,2028,1698, 833,2696,1235,1322,1594,4408,3623,3013,3225,2040,
-3022, 541,2881, 607,3632,2029,1665,1219, 639,1385,1686,1099,2803,3231,1938,3188,
-2858, 427, 676,2772,1168,2025, 454,3253,2486,3556, 230,1950, 580, 791,1991,1280,
-1086,1974,2034, 630, 257,3338,2788,4903,1017, 86,4790, 966,2789,1995,1696,1131,
- 259,3095,4188,1308, 179,1463,5257, 289,4107,1248, 42,3413,1725,2288, 896,1947,
- 774,4474,4254, 604,3430,4264, 392,2514,2588, 452, 237,1408,3018, 988,4531,1970,
-3034,3310, 540,2370,1562,1288,2990, 502,4765,1147, 4,1853,2708, 207, 294,2814,
-4078,2902,2509, 684, 34,3105,3532,2551, 644, 709,2801,2344, 573,1727,3573,3557,
-2021,1081,3100,4315,2100,3681, 199,2263,1837,2385, 146,3484,1195,2776,3949, 997,
-1939,3973,1008,1091,1202,1962,1847,1149,4209,5444,1076, 493, 117,5400,2521, 972,
-1490,2934,1796,4542,2374,1512,2933,2657, 413,2888,1135,2762,2314,2156,1355,2369,
- 766,2007,2527,2170,3124,2491,2593,2632,4757,2437, 234,3125,3591,1898,1750,1376,
-1942,3468,3138, 570,2127,2145,3276,4131, 962, 132,1445,4196, 19, 941,3624,3480,
-3366,1973,1374,4461,3431,2629, 283,2415,2275, 808,2887,3620,2112,2563,1353,3610,
- 955,1089,3103,1053, 96, 88,4097, 823,3808,1583, 399, 292,4091,3313, 421,1128,
- 642,4006, 903,2539,1877,2082, 596, 29,4066,1790, 722,2157, 130, 995,1569, 769,
-1485, 464, 513,2213, 288,1923,1101,2453,4316, 133, 486,2445, 50, 625, 487,2207,
- 57, 423, 481,2962, 159,3729,1558, 491, 303, 482, 501, 240,2837, 112,3648,2392,
-1783, 362, 8,3433,3422, 610,2793,3277,1390,1284,1654, 21,3823, 734, 367, 623,
- 193, 287, 374,1009,1483, 816, 476, 313,2255,2340,1262,2150,2899,1146,2581, 782,
-2116,1659,2018,1880, 255,3586,3314,1110,2867,2137,2564, 986,2767,5185,2006, 650,
- 158, 926, 762, 881,3157,2717,2362,3587, 306,3690,3245,1542,3077,2427,1691,2478,
-2118,2985,3490,2438, 539,2305, 983, 129,1754, 355,4201,2386, 827,2923, 104,1773,
-2838,2771, 411,2905,3919, 376, 767, 122,1114, 828,2422,1817,3506, 266,3460,1007,
-1609,4998, 945,2612,4429,2274, 726,1247,1964,2914,2199,2070,4002,4108, 657,3323,
-1422, 579, 455,2764,4737,1222,2895,1670, 824,1223,1487,2525, 558, 861,3080, 598,
-2659,2515,1967, 752,2583,2376,2214,4180, 977, 704,2464,4999,2622,4109,1210,2961,
- 819,1541, 142,2284, 44, 418, 457,1126,3730,4347,4626,1644,1876,3671,1864, 302,
-1063,5694, 624, 723,1984,3745,1314,1676,2488,1610,1449,3558,3569,2166,2098, 409,
-1011,2325,3704,2306, 818,1732,1383,1824,1844,3757, 999,2705,3497,1216,1423,2683,
-2426,2954,2501,2726,2229,1475,2554,5064,1971,1794,1666,2014,1343, 783, 724, 191,
-2434,1354,2220,5065,1763,2752,2472,4152, 131, 175,2885,3434, 92,1466,4920,2616,
-3871,3872,3866, 128,1551,1632, 669,1854,3682,4691,4125,1230, 188,2973,3290,1302,
-1213, 560,3266, 917, 763,3909,3249,1760, 868,1958, 764,1782,2097, 145,2277,3774,
-4462, 64,1491,3062, 971,2132,3606,2442, 221,1226,1617, 218, 323,1185,3207,3147,
- 571, 619,1473,1005,1744,2281, 449,1887,2396,3685, 275, 375,3816,1743,3844,3731,
- 845,1983,2350,4210,1377, 773, 967,3499,3052,3743,2725,4007,1697,1022,3943,1464,
-3264,2855,2722,1952,1029,2839,2467, 84,4383,2215, 820,1391,2015,2448,3672, 377,
-1948,2168, 797,2545,3536,2578,2645, 94,2874,1678, 405,1259,3071, 771, 546,1315,
- 470,1243,3083, 895,2468, 981, 969,2037, 846,4181, 653,1276,2928, 14,2594, 557,
-3007,2474, 156, 902,1338,1740,2574, 537,2518, 973,2282,2216,2433,1928, 138,2903,
-1293,2631,1612, 646,3457, 839,2935, 111, 496,2191,2847, 589,3186, 149,3994,2060,
-4031,2641,4067,3145,1870, 37,3597,2136,1025,2051,3009,3383,3549,1121,1016,3261,
-1301, 251,2446,2599,2153, 872,3246, 637, 334,3705, 831, 884, 921,3065,3140,4092,
-2198,1944, 246,2964, 108,2045,1152,1921,2308,1031, 203,3173,4170,1907,3890, 810,
-1401,2003,1690, 506, 647,1242,2828,1761,1649,3208,2249,1589,3709,2931,5156,1708,
- 498, 666,2613, 834,3817,1231, 184,2851,1124, 883,3197,2261,3710,1765,1553,2658,
-1178,2639,2351, 93,1193, 942,2538,2141,4402, 235,1821, 870,1591,2192,1709,1871,
-3341,1618,4126,2595,2334, 603, 651, 69, 701, 268,2662,3411,2555,1380,1606, 503,
- 448, 254,2371,2646, 574,1187,2309,1770, 322,2235,1292,1801, 305, 566,1133, 229,
-2067,2057, 706, 167, 483,2002,2672,3295,1820,3561,3067, 316, 378,2746,3452,1112,
- 136,1981, 507,1651,2917,1117, 285,4591, 182,2580,3522,1304, 335,3303,1835,2504,
-1795,1792,2248, 674,1018,2106,2449,1857,2292,2845, 976,3047,1781,2600,2727,1389,
-1281, 52,3152, 153, 265,3950, 672,3485,3951,4463, 430,1183, 365, 278,2169, 27,
-1407,1336,2304, 209,1340,1730,2202,1852,2403,2883, 979,1737,1062, 631,2829,2542,
-3876,2592, 825,2086,2226,3048,3625, 352,1417,3724, 542, 991, 431,1351,3938,1861,
-2294, 826,1361,2927,3142,3503,1738, 463,2462,2723, 582,1916,1595,2808, 400,3845,
-3891,2868,3621,2254, 58,2492,1123, 910,2160,2614,1372,1603,1196,1072,3385,1700,
-3267,1980, 696, 480,2430, 920, 799,1570,2920,1951,2041,4047,2540,1321,4223,2469,
-3562,2228,1271,2602, 401,2833,3351,2575,5157, 907,2312,1256, 410, 263,3507,1582,
- 996, 678,1849,2316,1480, 908,3545,2237, 703,2322, 667,1826,2849,1531,2604,2999,
-2407,3146,2151,2630,1786,3711, 469,3542, 497,3899,2409, 858, 837,4446,3393,1274,
- 786, 620,1845,2001,3311, 484, 308,3367,1204,1815,3691,2332,1532,2557,1842,2020,
-2724,1927,2333,4440, 567, 22,1673,2728,4475,1987,1858,1144,1597, 101,1832,3601,
- 12, 974,3783,4391, 951,1412, 1,3720, 453,4608,4041, 528,1041,1027,3230,2628,
-1129, 875,1051,3291,1203,2262,1069,2860,2799,2149,2615,3278, 144,1758,3040, 31,
- 475,1680, 366,2685,3184, 311,1642,4008,2466,5036,1593,1493,2809, 216,1420,1668,
- 233, 304,2128,3284, 232,1429,1768,1040,2008,3407,2740,2967,2543, 242,2133, 778,
-1565,2022,2620, 505,2189,2756,1098,2273, 372,1614, 708, 553,2846,2094,2278, 169,
-3626,2835,4161, 228,2674,3165, 809,1454,1309, 466,1705,1095, 900,3423, 880,2667,
-3751,5258,2317,3109,2571,4317,2766,1503,1342, 866,4447,1118, 63,2076, 314,1881,
-1348,1061, 172, 978,3515,1747, 532, 511,3970, 6, 601, 905,2699,3300,1751, 276,
-1467,3725,2668, 65,4239,2544,2779,2556,1604, 578,2451,1802, 992,2331,2624,1320,
-3446, 713,1513,1013, 103,2786,2447,1661, 886,1702, 916, 654,3574,2031,1556, 751,
-2178,2821,2179,1498,1538,2176, 271, 914,2251,2080,1325, 638,1953,2937,3877,2432,
-2754, 95,3265,1716, 260,1227,4083, 775, 106,1357,3254, 426,1607, 555,2480, 772,
-1985, 244,2546, 474, 495,1046,2611,1851,2061, 71,2089,1675,2590, 742,3758,2843,
-3222,1433, 267,2180,2576,2826,2233,2092,3913,2435, 956,1745,3075, 856,2113,1116,
- 451, 3,1988,2896,1398, 993,2463,1878,2049,1341,2718,2721,2870,2108, 712,2904,
-4363,2753,2324, 277,2872,2349,2649, 384, 987, 435, 691,3000, 922, 164,3939, 652,
-1500,1184,4153,2482,3373,2165,4848,2335,3775,3508,3154,2806,2830,1554,2102,1664,
-2530,1434,2408, 893,1547,2623,3447,2832,2242,2532,3169,2856,3223,2078, 49,3770,
-3469, 462, 318, 656,2259,3250,3069, 679,1629,2758, 344,1138,1104,3120,1836,1283,
-3115,2154,1437,4448, 934, 759,1999, 794,2862,1038, 533,2560,1722,2342, 855,2626,
-1197,1663,4476,3127, 85,4240,2528, 25,1111,1181,3673, 407,3470,4561,2679,2713,
- 768,1925,2841,3986,1544,1165, 932, 373,1240,2146,1930,2673, 721,4766, 354,4333,
- 391,2963, 187, 61,3364,1442,1102, 330,1940,1767, 341,3809,4118, 393,2496,2062,
-2211, 105, 331, 300, 439, 913,1332, 626, 379,3304,1557, 328, 689,3952, 309,1555,
- 931, 317,2517,3027, 325, 569, 686,2107,3084, 60,1042,1333,2794, 264,3177,4014,
-1628, 258,3712, 7,4464,1176,1043,1778, 683, 114,1975, 78,1492, 383,1886, 510,
- 386, 645,5291,2891,2069,3305,4138,3867,2939,2603,2493,1935,1066,1848,3588,1015,
-1282,1289,4609, 697,1453,3044,2666,3611,1856,2412, 54, 719,1330, 568,3778,2459,
-1748, 788, 492, 551,1191,1000, 488,3394,3763, 282,1799, 348,2016,1523,3155,2390,
-1049, 382,2019,1788,1170, 729,2968,3523, 897,3926,2785,2938,3292, 350,2319,3238,
-1718,1717,2655,3453,3143,4465, 161,2889,2980,2009,1421, 56,1908,1640,2387,2232,
-1917,1874,2477,4921, 148, 83,3438, 592,4245,2882,1822,1055, 741, 115,1496,1624,
- 381,1638,4592,1020, 516,3214, 458, 947,4575,1432, 211,1514,2926,1865,2142, 189,
- 852,1221,1400,1486, 882,2299,4036, 351, 28,1122, 700,6479,6480,6481,6482,6483, #last 512
-)
-# fmt: on
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/gb2312prober.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/gb2312prober.py
deleted file mode 100644
index d423e73..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/gb2312prober.py
+++ /dev/null
@@ -1,47 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is mozilla.org code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-from .chardistribution import GB2312DistributionAnalysis
-from .codingstatemachine import CodingStateMachine
-from .mbcharsetprober import MultiByteCharSetProber
-from .mbcssm import GB2312_SM_MODEL
-
-
-class GB2312Prober(MultiByteCharSetProber):
- def __init__(self) -> None:
- super().__init__()
- self.coding_sm = CodingStateMachine(GB2312_SM_MODEL)
- self.distribution_analyzer = GB2312DistributionAnalysis()
- self.reset()
-
- @property
- def charset_name(self) -> str:
- return "GB2312"
-
- @property
- def language(self) -> str:
- return "Chinese"
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/hebrewprober.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/hebrewprober.py
deleted file mode 100644
index 785d005..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/hebrewprober.py
+++ /dev/null
@@ -1,316 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is Mozilla Universal charset detector code.
-#
-# The Initial Developer of the Original Code is
-# Shy Shalom
-# Portions created by the Initial Developer are Copyright (C) 2005
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-from typing import Optional, Union
-
-from .charsetprober import CharSetProber
-from .enums import ProbingState
-from .sbcharsetprober import SingleByteCharSetProber
-
-# This prober doesn't actually recognize a language or a charset.
-# It is a helper prober for the use of the Hebrew model probers
-
-### General ideas of the Hebrew charset recognition ###
-#
-# Four main charsets exist in Hebrew:
-# "ISO-8859-8" - Visual Hebrew
-# "windows-1255" - Logical Hebrew
-# "ISO-8859-8-I" - Logical Hebrew
-# "x-mac-hebrew" - ?? Logical Hebrew ??
-#
-# Both "ISO" charsets use a completely identical set of code points, whereas
-# "windows-1255" and "x-mac-hebrew" are two different proper supersets of
-# these code points. windows-1255 defines additional characters in the range
-# 0x80-0x9F as some misc punctuation marks as well as some Hebrew-specific
-# diacritics and additional 'Yiddish' ligature letters in the range 0xc0-0xd6.
-# x-mac-hebrew defines similar additional code points but with a different
-# mapping.
-#
-# As far as an average Hebrew text with no diacritics is concerned, all four
-# charsets are identical with respect to code points. Meaning that for the
-# main Hebrew alphabet, all four map the same values to all 27 Hebrew letters
-# (including final letters).
-#
-# The dominant difference between these charsets is their directionality.
-# "Visual" directionality means that the text is ordered as if the renderer is
-# not aware of a BIDI rendering algorithm. The renderer sees the text and
-# draws it from left to right. The text itself when ordered naturally is read
-# backwards. A buffer of Visual Hebrew generally looks like so:
-# "[last word of first line spelled backwards] [whole line ordered backwards
-# and spelled backwards] [first word of first line spelled backwards]
-# [end of line] [last word of second line] ... etc' "
-# adding punctuation marks, numbers and English text to visual text is
-# naturally also "visual" and from left to right.
-#
-# "Logical" directionality means the text is ordered "naturally" according to
-# the order it is read. It is the responsibility of the renderer to display
-# the text from right to left. A BIDI algorithm is used to place general
-# punctuation marks, numbers and English text in the text.
-#
-# Texts in x-mac-hebrew are almost impossible to find on the Internet. From
-# what little evidence I could find, it seems that its general directionality
-# is Logical.
-#
-# To sum up all of the above, the Hebrew probing mechanism knows about two
-# charsets:
-# Visual Hebrew - "ISO-8859-8" - backwards text - Words and sentences are
-# backwards while line order is natural. For charset recognition purposes
-# the line order is unimportant (In fact, for this implementation, even
-# word order is unimportant).
-# Logical Hebrew - "windows-1255" - normal, naturally ordered text.
-#
-# "ISO-8859-8-I" is a subset of windows-1255 and doesn't need to be
-# specifically identified.
-# "x-mac-hebrew" is also identified as windows-1255. A text in x-mac-hebrew
-# that contain special punctuation marks or diacritics is displayed with
-# some unconverted characters showing as question marks. This problem might
-# be corrected using another model prober for x-mac-hebrew. Due to the fact
-# that x-mac-hebrew texts are so rare, writing another model prober isn't
-# worth the effort and performance hit.
-#
-#### The Prober ####
-#
-# The prober is divided between two SBCharSetProbers and a HebrewProber,
-# all of which are managed, created, fed data, inquired and deleted by the
-# SBCSGroupProber. The two SBCharSetProbers identify that the text is in
-# fact some kind of Hebrew, Logical or Visual. The final decision about which
-# one is it is made by the HebrewProber by combining final-letter scores
-# with the scores of the two SBCharSetProbers to produce a final answer.
-#
-# The SBCSGroupProber is responsible for stripping the original text of HTML
-# tags, English characters, numbers, low-ASCII punctuation characters, spaces
-# and new lines. It reduces any sequence of such characters to a single space.
-# The buffer fed to each prober in the SBCS group prober is pure text in
-# high-ASCII.
-# The two SBCharSetProbers (model probers) share the same language model:
-# Win1255Model.
-# The first SBCharSetProber uses the model normally as any other
-# SBCharSetProber does, to recognize windows-1255, upon which this model was
-# built. The second SBCharSetProber is told to make the pair-of-letter
-# lookup in the language model backwards. This in practice exactly simulates
-# a visual Hebrew model using the windows-1255 logical Hebrew model.
-#
-# The HebrewProber is not using any language model. All it does is look for
-# final-letter evidence suggesting the text is either logical Hebrew or visual
-# Hebrew. Disjointed from the model probers, the results of the HebrewProber
-# alone are meaningless. HebrewProber always returns 0.00 as confidence
-# since it never identifies a charset by itself. Instead, the pointer to the
-# HebrewProber is passed to the model probers as a helper "Name Prober".
-# When the Group prober receives a positive identification from any prober,
-# it asks for the name of the charset identified. If the prober queried is a
-# Hebrew model prober, the model prober forwards the call to the
-# HebrewProber to make the final decision. In the HebrewProber, the
-# decision is made according to the final-letters scores maintained and Both
-# model probers scores. The answer is returned in the form of the name of the
-# charset identified, either "windows-1255" or "ISO-8859-8".
-
-
-class HebrewProber(CharSetProber):
- SPACE = 0x20
- # windows-1255 / ISO-8859-8 code points of interest
- FINAL_KAF = 0xEA
- NORMAL_KAF = 0xEB
- FINAL_MEM = 0xED
- NORMAL_MEM = 0xEE
- FINAL_NUN = 0xEF
- NORMAL_NUN = 0xF0
- FINAL_PE = 0xF3
- NORMAL_PE = 0xF4
- FINAL_TSADI = 0xF5
- NORMAL_TSADI = 0xF6
-
- # Minimum Visual vs Logical final letter score difference.
- # If the difference is below this, don't rely solely on the final letter score
- # distance.
- MIN_FINAL_CHAR_DISTANCE = 5
-
- # Minimum Visual vs Logical model score difference.
- # If the difference is below this, don't rely at all on the model score
- # distance.
- MIN_MODEL_DISTANCE = 0.01
-
- VISUAL_HEBREW_NAME = "ISO-8859-8"
- LOGICAL_HEBREW_NAME = "windows-1255"
-
- def __init__(self) -> None:
- super().__init__()
- self._final_char_logical_score = 0
- self._final_char_visual_score = 0
- self._prev = self.SPACE
- self._before_prev = self.SPACE
- self._logical_prober: Optional[SingleByteCharSetProber] = None
- self._visual_prober: Optional[SingleByteCharSetProber] = None
- self.reset()
-
- def reset(self) -> None:
- self._final_char_logical_score = 0
- self._final_char_visual_score = 0
- # The two last characters seen in the previous buffer,
- # mPrev and mBeforePrev are initialized to space in order to simulate
- # a word delimiter at the beginning of the data
- self._prev = self.SPACE
- self._before_prev = self.SPACE
- # These probers are owned by the group prober.
-
- def set_model_probers(
- self,
- logical_prober: SingleByteCharSetProber,
- visual_prober: SingleByteCharSetProber,
- ) -> None:
- self._logical_prober = logical_prober
- self._visual_prober = visual_prober
-
- def is_final(self, c: int) -> bool:
- return c in [
- self.FINAL_KAF,
- self.FINAL_MEM,
- self.FINAL_NUN,
- self.FINAL_PE,
- self.FINAL_TSADI,
- ]
-
- def is_non_final(self, c: int) -> bool:
- # The normal Tsadi is not a good Non-Final letter due to words like
- # 'lechotet' (to chat) containing an apostrophe after the tsadi. This
- # apostrophe is converted to a space in FilterWithoutEnglishLetters
- # causing the Non-Final tsadi to appear at an end of a word even
- # though this is not the case in the original text.
- # The letters Pe and Kaf rarely display a related behavior of not being
- # a good Non-Final letter. Words like 'Pop', 'Winamp' and 'Mubarak'
- # for example legally end with a Non-Final Pe or Kaf. However, the
- # benefit of these letters as Non-Final letters outweighs the damage
- # since these words are quite rare.
- return c in [self.NORMAL_KAF, self.NORMAL_MEM, self.NORMAL_NUN, self.NORMAL_PE]
-
- def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState:
- # Final letter analysis for logical-visual decision.
- # Look for evidence that the received buffer is either logical Hebrew
- # or visual Hebrew.
- # The following cases are checked:
- # 1) A word longer than 1 letter, ending with a final letter. This is
- # an indication that the text is laid out "naturally" since the
- # final letter really appears at the end. +1 for logical score.
- # 2) A word longer than 1 letter, ending with a Non-Final letter. In
- # normal Hebrew, words ending with Kaf, Mem, Nun, Pe or Tsadi,
- # should not end with the Non-Final form of that letter. Exceptions
- # to this rule are mentioned above in isNonFinal(). This is an
- # indication that the text is laid out backwards. +1 for visual
- # score
- # 3) A word longer than 1 letter, starting with a final letter. Final
- # letters should not appear at the beginning of a word. This is an
- # indication that the text is laid out backwards. +1 for visual
- # score.
- #
- # The visual score and logical score are accumulated throughout the
- # text and are finally checked against each other in GetCharSetName().
- # No checking for final letters in the middle of words is done since
- # that case is not an indication for either Logical or Visual text.
- #
- # We automatically filter out all 7-bit characters (replace them with
- # spaces) so the word boundary detection works properly. [MAP]
-
- if self.state == ProbingState.NOT_ME:
- # Both model probers say it's not them. No reason to continue.
- return ProbingState.NOT_ME
-
- byte_str = self.filter_high_byte_only(byte_str)
-
- for cur in byte_str:
- if cur == self.SPACE:
- # We stand on a space - a word just ended
- if self._before_prev != self.SPACE:
- # next-to-last char was not a space so self._prev is not a
- # 1 letter word
- if self.is_final(self._prev):
- # case (1) [-2:not space][-1:final letter][cur:space]
- self._final_char_logical_score += 1
- elif self.is_non_final(self._prev):
- # case (2) [-2:not space][-1:Non-Final letter][
- # cur:space]
- self._final_char_visual_score += 1
- else:
- # Not standing on a space
- if (
- (self._before_prev == self.SPACE)
- and (self.is_final(self._prev))
- and (cur != self.SPACE)
- ):
- # case (3) [-2:space][-1:final letter][cur:not space]
- self._final_char_visual_score += 1
- self._before_prev = self._prev
- self._prev = cur
-
- # Forever detecting, till the end or until both model probers return
- # ProbingState.NOT_ME (handled above)
- return ProbingState.DETECTING
-
- @property
- def charset_name(self) -> str:
- assert self._logical_prober is not None
- assert self._visual_prober is not None
-
- # Make the decision: is it Logical or Visual?
- # If the final letter score distance is dominant enough, rely on it.
- finalsub = self._final_char_logical_score - self._final_char_visual_score
- if finalsub >= self.MIN_FINAL_CHAR_DISTANCE:
- return self.LOGICAL_HEBREW_NAME
- if finalsub <= -self.MIN_FINAL_CHAR_DISTANCE:
- return self.VISUAL_HEBREW_NAME
-
- # It's not dominant enough, try to rely on the model scores instead.
- modelsub = (
- self._logical_prober.get_confidence() - self._visual_prober.get_confidence()
- )
- if modelsub > self.MIN_MODEL_DISTANCE:
- return self.LOGICAL_HEBREW_NAME
- if modelsub < -self.MIN_MODEL_DISTANCE:
- return self.VISUAL_HEBREW_NAME
-
- # Still no good, back to final letter distance, maybe it'll save the
- # day.
- if finalsub < 0.0:
- return self.VISUAL_HEBREW_NAME
-
- # (finalsub > 0 - Logical) or (don't know what to do) default to
- # Logical.
- return self.LOGICAL_HEBREW_NAME
-
- @property
- def language(self) -> str:
- return "Hebrew"
-
- @property
- def state(self) -> ProbingState:
- assert self._logical_prober is not None
- assert self._visual_prober is not None
-
- # Remain active as long as any of the model probers are active.
- if (self._logical_prober.state == ProbingState.NOT_ME) and (
- self._visual_prober.state == ProbingState.NOT_ME
- ):
- return ProbingState.NOT_ME
- return ProbingState.DETECTING
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/jisfreq.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/jisfreq.py
deleted file mode 100644
index 3293576..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/jisfreq.py
+++ /dev/null
@@ -1,325 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is Mozilla Communicator client code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-# Sampling from about 20M text materials include literature and computer technology
-#
-# Japanese frequency table, applied to both S-JIS and EUC-JP
-# They are sorted in order.
-
-# 128 --> 0.77094
-# 256 --> 0.85710
-# 512 --> 0.92635
-# 1024 --> 0.97130
-# 2048 --> 0.99431
-#
-# Ideal Distribution Ratio = 0.92635 / (1-0.92635) = 12.58
-# Random Distribution Ration = 512 / (2965+62+83+86-512) = 0.191
-#
-# Typical Distribution Ratio, 25% of IDR
-
-JIS_TYPICAL_DISTRIBUTION_RATIO = 3.0
-
-# Char to FreqOrder table ,
-JIS_TABLE_SIZE = 4368
-
-# fmt: off
-JIS_CHAR_TO_FREQ_ORDER = (
- 40, 1, 6, 182, 152, 180, 295,2127, 285, 381,3295,4304,3068,4606,3165,3510, # 16
-3511,1822,2785,4607,1193,2226,5070,4608, 171,2996,1247, 18, 179,5071, 856,1661, # 32
-1262,5072, 619, 127,3431,3512,3230,1899,1700, 232, 228,1294,1298, 284, 283,2041, # 48
-2042,1061,1062, 48, 49, 44, 45, 433, 434,1040,1041, 996, 787,2997,1255,4305, # 64
-2108,4609,1684,1648,5073,5074,5075,5076,5077,5078,3687,5079,4610,5080,3927,3928, # 80
-5081,3296,3432, 290,2285,1471,2187,5082,2580,2825,1303,2140,1739,1445,2691,3375, # 96
-1691,3297,4306,4307,4611, 452,3376,1182,2713,3688,3069,4308,5083,5084,5085,5086, # 112
-5087,5088,5089,5090,5091,5092,5093,5094,5095,5096,5097,5098,5099,5100,5101,5102, # 128
-5103,5104,5105,5106,5107,5108,5109,5110,5111,5112,4097,5113,5114,5115,5116,5117, # 144
-5118,5119,5120,5121,5122,5123,5124,5125,5126,5127,5128,5129,5130,5131,5132,5133, # 160
-5134,5135,5136,5137,5138,5139,5140,5141,5142,5143,5144,5145,5146,5147,5148,5149, # 176
-5150,5151,5152,4612,5153,5154,5155,5156,5157,5158,5159,5160,5161,5162,5163,5164, # 192
-5165,5166,5167,5168,5169,5170,5171,5172,5173,5174,5175,1472, 598, 618, 820,1205, # 208
-1309,1412,1858,1307,1692,5176,5177,5178,5179,5180,5181,5182,1142,1452,1234,1172, # 224
-1875,2043,2149,1793,1382,2973, 925,2404,1067,1241, 960,1377,2935,1491, 919,1217, # 240
-1865,2030,1406,1499,2749,4098,5183,5184,5185,5186,5187,5188,2561,4099,3117,1804, # 256
-2049,3689,4309,3513,1663,5189,3166,3118,3298,1587,1561,3433,5190,3119,1625,2998, # 272
-3299,4613,1766,3690,2786,4614,5191,5192,5193,5194,2161, 26,3377, 2,3929, 20, # 288
-3691, 47,4100, 50, 17, 16, 35, 268, 27, 243, 42, 155, 24, 154, 29, 184, # 304
- 4, 91, 14, 92, 53, 396, 33, 289, 9, 37, 64, 620, 21, 39, 321, 5, # 320
- 12, 11, 52, 13, 3, 208, 138, 0, 7, 60, 526, 141, 151,1069, 181, 275, # 336
-1591, 83, 132,1475, 126, 331, 829, 15, 69, 160, 59, 22, 157, 55,1079, 312, # 352
- 109, 38, 23, 25, 10, 19, 79,5195, 61, 382,1124, 8, 30,5196,5197,5198, # 368
-5199,5200,5201,5202,5203,5204,5205,5206, 89, 62, 74, 34,2416, 112, 139, 196, # 384
- 271, 149, 84, 607, 131, 765, 46, 88, 153, 683, 76, 874, 101, 258, 57, 80, # 400
- 32, 364, 121,1508, 169,1547, 68, 235, 145,2999, 41, 360,3027, 70, 63, 31, # 416
- 43, 259, 262,1383, 99, 533, 194, 66, 93, 846, 217, 192, 56, 106, 58, 565, # 432
- 280, 272, 311, 256, 146, 82, 308, 71, 100, 128, 214, 655, 110, 261, 104,1140, # 448
- 54, 51, 36, 87, 67,3070, 185,2618,2936,2020, 28,1066,2390,2059,5207,5208, # 464
-5209,5210,5211,5212,5213,5214,5215,5216,4615,5217,5218,5219,5220,5221,5222,5223, # 480
-5224,5225,5226,5227,5228,5229,5230,5231,5232,5233,5234,5235,5236,3514,5237,5238, # 496
-5239,5240,5241,5242,5243,5244,2297,2031,4616,4310,3692,5245,3071,5246,3598,5247, # 512
-4617,3231,3515,5248,4101,4311,4618,3808,4312,4102,5249,4103,4104,3599,5250,5251, # 528
-5252,5253,5254,5255,5256,5257,5258,5259,5260,5261,5262,5263,5264,5265,5266,5267, # 544
-5268,5269,5270,5271,5272,5273,5274,5275,5276,5277,5278,5279,5280,5281,5282,5283, # 560
-5284,5285,5286,5287,5288,5289,5290,5291,5292,5293,5294,5295,5296,5297,5298,5299, # 576
-5300,5301,5302,5303,5304,5305,5306,5307,5308,5309,5310,5311,5312,5313,5314,5315, # 592
-5316,5317,5318,5319,5320,5321,5322,5323,5324,5325,5326,5327,5328,5329,5330,5331, # 608
-5332,5333,5334,5335,5336,5337,5338,5339,5340,5341,5342,5343,5344,5345,5346,5347, # 624
-5348,5349,5350,5351,5352,5353,5354,5355,5356,5357,5358,5359,5360,5361,5362,5363, # 640
-5364,5365,5366,5367,5368,5369,5370,5371,5372,5373,5374,5375,5376,5377,5378,5379, # 656
-5380,5381, 363, 642,2787,2878,2788,2789,2316,3232,2317,3434,2011, 165,1942,3930, # 672
-3931,3932,3933,5382,4619,5383,4620,5384,5385,5386,5387,5388,5389,5390,5391,5392, # 688
-5393,5394,5395,5396,5397,5398,5399,5400,5401,5402,5403,5404,5405,5406,5407,5408, # 704
-5409,5410,5411,5412,5413,5414,5415,5416,5417,5418,5419,5420,5421,5422,5423,5424, # 720
-5425,5426,5427,5428,5429,5430,5431,5432,5433,5434,5435,5436,5437,5438,5439,5440, # 736
-5441,5442,5443,5444,5445,5446,5447,5448,5449,5450,5451,5452,5453,5454,5455,5456, # 752
-5457,5458,5459,5460,5461,5462,5463,5464,5465,5466,5467,5468,5469,5470,5471,5472, # 768
-5473,5474,5475,5476,5477,5478,5479,5480,5481,5482,5483,5484,5485,5486,5487,5488, # 784
-5489,5490,5491,5492,5493,5494,5495,5496,5497,5498,5499,5500,5501,5502,5503,5504, # 800
-5505,5506,5507,5508,5509,5510,5511,5512,5513,5514,5515,5516,5517,5518,5519,5520, # 816
-5521,5522,5523,5524,5525,5526,5527,5528,5529,5530,5531,5532,5533,5534,5535,5536, # 832
-5537,5538,5539,5540,5541,5542,5543,5544,5545,5546,5547,5548,5549,5550,5551,5552, # 848
-5553,5554,5555,5556,5557,5558,5559,5560,5561,5562,5563,5564,5565,5566,5567,5568, # 864
-5569,5570,5571,5572,5573,5574,5575,5576,5577,5578,5579,5580,5581,5582,5583,5584, # 880
-5585,5586,5587,5588,5589,5590,5591,5592,5593,5594,5595,5596,5597,5598,5599,5600, # 896
-5601,5602,5603,5604,5605,5606,5607,5608,5609,5610,5611,5612,5613,5614,5615,5616, # 912
-5617,5618,5619,5620,5621,5622,5623,5624,5625,5626,5627,5628,5629,5630,5631,5632, # 928
-5633,5634,5635,5636,5637,5638,5639,5640,5641,5642,5643,5644,5645,5646,5647,5648, # 944
-5649,5650,5651,5652,5653,5654,5655,5656,5657,5658,5659,5660,5661,5662,5663,5664, # 960
-5665,5666,5667,5668,5669,5670,5671,5672,5673,5674,5675,5676,5677,5678,5679,5680, # 976
-5681,5682,5683,5684,5685,5686,5687,5688,5689,5690,5691,5692,5693,5694,5695,5696, # 992
-5697,5698,5699,5700,5701,5702,5703,5704,5705,5706,5707,5708,5709,5710,5711,5712, # 1008
-5713,5714,5715,5716,5717,5718,5719,5720,5721,5722,5723,5724,5725,5726,5727,5728, # 1024
-5729,5730,5731,5732,5733,5734,5735,5736,5737,5738,5739,5740,5741,5742,5743,5744, # 1040
-5745,5746,5747,5748,5749,5750,5751,5752,5753,5754,5755,5756,5757,5758,5759,5760, # 1056
-5761,5762,5763,5764,5765,5766,5767,5768,5769,5770,5771,5772,5773,5774,5775,5776, # 1072
-5777,5778,5779,5780,5781,5782,5783,5784,5785,5786,5787,5788,5789,5790,5791,5792, # 1088
-5793,5794,5795,5796,5797,5798,5799,5800,5801,5802,5803,5804,5805,5806,5807,5808, # 1104
-5809,5810,5811,5812,5813,5814,5815,5816,5817,5818,5819,5820,5821,5822,5823,5824, # 1120
-5825,5826,5827,5828,5829,5830,5831,5832,5833,5834,5835,5836,5837,5838,5839,5840, # 1136
-5841,5842,5843,5844,5845,5846,5847,5848,5849,5850,5851,5852,5853,5854,5855,5856, # 1152
-5857,5858,5859,5860,5861,5862,5863,5864,5865,5866,5867,5868,5869,5870,5871,5872, # 1168
-5873,5874,5875,5876,5877,5878,5879,5880,5881,5882,5883,5884,5885,5886,5887,5888, # 1184
-5889,5890,5891,5892,5893,5894,5895,5896,5897,5898,5899,5900,5901,5902,5903,5904, # 1200
-5905,5906,5907,5908,5909,5910,5911,5912,5913,5914,5915,5916,5917,5918,5919,5920, # 1216
-5921,5922,5923,5924,5925,5926,5927,5928,5929,5930,5931,5932,5933,5934,5935,5936, # 1232
-5937,5938,5939,5940,5941,5942,5943,5944,5945,5946,5947,5948,5949,5950,5951,5952, # 1248
-5953,5954,5955,5956,5957,5958,5959,5960,5961,5962,5963,5964,5965,5966,5967,5968, # 1264
-5969,5970,5971,5972,5973,5974,5975,5976,5977,5978,5979,5980,5981,5982,5983,5984, # 1280
-5985,5986,5987,5988,5989,5990,5991,5992,5993,5994,5995,5996,5997,5998,5999,6000, # 1296
-6001,6002,6003,6004,6005,6006,6007,6008,6009,6010,6011,6012,6013,6014,6015,6016, # 1312
-6017,6018,6019,6020,6021,6022,6023,6024,6025,6026,6027,6028,6029,6030,6031,6032, # 1328
-6033,6034,6035,6036,6037,6038,6039,6040,6041,6042,6043,6044,6045,6046,6047,6048, # 1344
-6049,6050,6051,6052,6053,6054,6055,6056,6057,6058,6059,6060,6061,6062,6063,6064, # 1360
-6065,6066,6067,6068,6069,6070,6071,6072,6073,6074,6075,6076,6077,6078,6079,6080, # 1376
-6081,6082,6083,6084,6085,6086,6087,6088,6089,6090,6091,6092,6093,6094,6095,6096, # 1392
-6097,6098,6099,6100,6101,6102,6103,6104,6105,6106,6107,6108,6109,6110,6111,6112, # 1408
-6113,6114,2044,2060,4621, 997,1235, 473,1186,4622, 920,3378,6115,6116, 379,1108, # 1424
-4313,2657,2735,3934,6117,3809, 636,3233, 573,1026,3693,3435,2974,3300,2298,4105, # 1440
- 854,2937,2463, 393,2581,2417, 539, 752,1280,2750,2480, 140,1161, 440, 708,1569, # 1456
- 665,2497,1746,1291,1523,3000, 164,1603, 847,1331, 537,1997, 486, 508,1693,2418, # 1472
-1970,2227, 878,1220, 299,1030, 969, 652,2751, 624,1137,3301,2619, 65,3302,2045, # 1488
-1761,1859,3120,1930,3694,3516, 663,1767, 852, 835,3695, 269, 767,2826,2339,1305, # 1504
- 896,1150, 770,1616,6118, 506,1502,2075,1012,2519, 775,2520,2975,2340,2938,4314, # 1520
-3028,2086,1224,1943,2286,6119,3072,4315,2240,1273,1987,3935,1557, 175, 597, 985, # 1536
-3517,2419,2521,1416,3029, 585, 938,1931,1007,1052,1932,1685,6120,3379,4316,4623, # 1552
- 804, 599,3121,1333,2128,2539,1159,1554,2032,3810, 687,2033,2904, 952, 675,1467, # 1568
-3436,6121,2241,1096,1786,2440,1543,1924, 980,1813,2228, 781,2692,1879, 728,1918, # 1584
-3696,4624, 548,1950,4625,1809,1088,1356,3303,2522,1944, 502, 972, 373, 513,2827, # 1600
- 586,2377,2391,1003,1976,1631,6122,2464,1084, 648,1776,4626,2141, 324, 962,2012, # 1616
-2177,2076,1384, 742,2178,1448,1173,1810, 222, 102, 301, 445, 125,2420, 662,2498, # 1632
- 277, 200,1476,1165,1068, 224,2562,1378,1446, 450,1880, 659, 791, 582,4627,2939, # 1648
-3936,1516,1274, 555,2099,3697,1020,1389,1526,3380,1762,1723,1787,2229, 412,2114, # 1664
-1900,2392,3518, 512,2597, 427,1925,2341,3122,1653,1686,2465,2499, 697, 330, 273, # 1680
- 380,2162, 951, 832, 780, 991,1301,3073, 965,2270,3519, 668,2523,2636,1286, 535, # 1696
-1407, 518, 671, 957,2658,2378, 267, 611,2197,3030,6123, 248,2299, 967,1799,2356, # 1712
- 850,1418,3437,1876,1256,1480,2828,1718,6124,6125,1755,1664,2405,6126,4628,2879, # 1728
-2829, 499,2179, 676,4629, 557,2329,2214,2090, 325,3234, 464, 811,3001, 992,2342, # 1744
-2481,1232,1469, 303,2242, 466,1070,2163, 603,1777,2091,4630,2752,4631,2714, 322, # 1760
-2659,1964,1768, 481,2188,1463,2330,2857,3600,2092,3031,2421,4632,2318,2070,1849, # 1776
-2598,4633,1302,2254,1668,1701,2422,3811,2905,3032,3123,2046,4106,1763,1694,4634, # 1792
-1604, 943,1724,1454, 917, 868,2215,1169,2940, 552,1145,1800,1228,1823,1955, 316, # 1808
-1080,2510, 361,1807,2830,4107,2660,3381,1346,1423,1134,4108,6127, 541,1263,1229, # 1824
-1148,2540, 545, 465,1833,2880,3438,1901,3074,2482, 816,3937, 713,1788,2500, 122, # 1840
-1575, 195,1451,2501,1111,6128, 859, 374,1225,2243,2483,4317, 390,1033,3439,3075, # 1856
-2524,1687, 266, 793,1440,2599, 946, 779, 802, 507, 897,1081, 528,2189,1292, 711, # 1872
-1866,1725,1167,1640, 753, 398,2661,1053, 246, 348,4318, 137,1024,3440,1600,2077, # 1888
-2129, 825,4319, 698, 238, 521, 187,2300,1157,2423,1641,1605,1464,1610,1097,2541, # 1904
-1260,1436, 759,2255,1814,2150, 705,3235, 409,2563,3304, 561,3033,2005,2564, 726, # 1920
-1956,2343,3698,4109, 949,3812,3813,3520,1669, 653,1379,2525, 881,2198, 632,2256, # 1936
-1027, 778,1074, 733,1957, 514,1481,2466, 554,2180, 702,3938,1606,1017,1398,6129, # 1952
-1380,3521, 921, 993,1313, 594, 449,1489,1617,1166, 768,1426,1360, 495,1794,3601, # 1968
-1177,3602,1170,4320,2344, 476, 425,3167,4635,3168,1424, 401,2662,1171,3382,1998, # 1984
-1089,4110, 477,3169, 474,6130,1909, 596,2831,1842, 494, 693,1051,1028,1207,3076, # 2000
- 606,2115, 727,2790,1473,1115, 743,3522, 630, 805,1532,4321,2021, 366,1057, 838, # 2016
- 684,1114,2142,4322,2050,1492,1892,1808,2271,3814,2424,1971,1447,1373,3305,1090, # 2032
-1536,3939,3523,3306,1455,2199, 336, 369,2331,1035, 584,2393, 902, 718,2600,6131, # 2048
-2753, 463,2151,1149,1611,2467, 715,1308,3124,1268, 343,1413,3236,1517,1347,2663, # 2064
-2093,3940,2022,1131,1553,2100,2941,1427,3441,2942,1323,2484,6132,1980, 872,2368, # 2080
-2441,2943, 320,2369,2116,1082, 679,1933,3941,2791,3815, 625,1143,2023, 422,2200, # 2096
-3816,6133, 730,1695, 356,2257,1626,2301,2858,2637,1627,1778, 937, 883,2906,2693, # 2112
-3002,1769,1086, 400,1063,1325,3307,2792,4111,3077, 456,2345,1046, 747,6134,1524, # 2128
- 884,1094,3383,1474,2164,1059, 974,1688,2181,2258,1047, 345,1665,1187, 358, 875, # 2144
-3170, 305, 660,3524,2190,1334,1135,3171,1540,1649,2542,1527, 927, 968,2793, 885, # 2160
-1972,1850, 482, 500,2638,1218,1109,1085,2543,1654,2034, 876, 78,2287,1482,1277, # 2176
- 861,1675,1083,1779, 724,2754, 454, 397,1132,1612,2332, 893, 672,1237, 257,2259, # 2192
-2370, 135,3384, 337,2244, 547, 352, 340, 709,2485,1400, 788,1138,2511, 540, 772, # 2208
-1682,2260,2272,2544,2013,1843,1902,4636,1999,1562,2288,4637,2201,1403,1533, 407, # 2224
- 576,3308,1254,2071, 978,3385, 170, 136,1201,3125,2664,3172,2394, 213, 912, 873, # 2240
-3603,1713,2202, 699,3604,3699, 813,3442, 493, 531,1054, 468,2907,1483, 304, 281, # 2256
-4112,1726,1252,2094, 339,2319,2130,2639, 756,1563,2944, 748, 571,2976,1588,2425, # 2272
-2715,1851,1460,2426,1528,1392,1973,3237, 288,3309, 685,3386, 296, 892,2716,2216, # 2288
-1570,2245, 722,1747,2217, 905,3238,1103,6135,1893,1441,1965, 251,1805,2371,3700, # 2304
-2601,1919,1078, 75,2182,1509,1592,1270,2640,4638,2152,6136,3310,3817, 524, 706, # 2320
-1075, 292,3818,1756,2602, 317, 98,3173,3605,3525,1844,2218,3819,2502, 814, 567, # 2336
- 385,2908,1534,6137, 534,1642,3239, 797,6138,1670,1529, 953,4323, 188,1071, 538, # 2352
- 178, 729,3240,2109,1226,1374,2000,2357,2977, 731,2468,1116,2014,2051,6139,1261, # 2368
-1593, 803,2859,2736,3443, 556, 682, 823,1541,6140,1369,2289,1706,2794, 845, 462, # 2384
-2603,2665,1361, 387, 162,2358,1740, 739,1770,1720,1304,1401,3241,1049, 627,1571, # 2400
-2427,3526,1877,3942,1852,1500, 431,1910,1503, 677, 297,2795, 286,1433,1038,1198, # 2416
-2290,1133,1596,4113,4639,2469,1510,1484,3943,6141,2442, 108, 712,4640,2372, 866, # 2432
-3701,2755,3242,1348, 834,1945,1408,3527,2395,3243,1811, 824, 994,1179,2110,1548, # 2448
-1453, 790,3003, 690,4324,4325,2832,2909,3820,1860,3821, 225,1748, 310, 346,1780, # 2464
-2470, 821,1993,2717,2796, 828, 877,3528,2860,2471,1702,2165,2910,2486,1789, 453, # 2480
- 359,2291,1676, 73,1164,1461,1127,3311, 421, 604, 314,1037, 589, 116,2487, 737, # 2496
- 837,1180, 111, 244, 735,6142,2261,1861,1362, 986, 523, 418, 581,2666,3822, 103, # 2512
- 855, 503,1414,1867,2488,1091, 657,1597, 979, 605,1316,4641,1021,2443,2078,2001, # 2528
-1209, 96, 587,2166,1032, 260,1072,2153, 173, 94, 226,3244, 819,2006,4642,4114, # 2544
-2203, 231,1744, 782, 97,2667, 786,3387, 887, 391, 442,2219,4326,1425,6143,2694, # 2560
- 633,1544,1202, 483,2015, 592,2052,1958,2472,1655, 419, 129,4327,3444,3312,1714, # 2576
-1257,3078,4328,1518,1098, 865,1310,1019,1885,1512,1734, 469,2444, 148, 773, 436, # 2592
-1815,1868,1128,1055,4329,1245,2756,3445,2154,1934,1039,4643, 579,1238, 932,2320, # 2608
- 353, 205, 801, 115,2428, 944,2321,1881, 399,2565,1211, 678, 766,3944, 335,2101, # 2624
-1459,1781,1402,3945,2737,2131,1010, 844, 981,1326,1013, 550,1816,1545,2620,1335, # 2640
-1008, 371,2881, 936,1419,1613,3529,1456,1395,2273,1834,2604,1317,2738,2503, 416, # 2656
-1643,4330, 806,1126, 229, 591,3946,1314,1981,1576,1837,1666, 347,1790, 977,3313, # 2672
- 764,2861,1853, 688,2429,1920,1462, 77, 595, 415,2002,3034, 798,1192,4115,6144, # 2688
-2978,4331,3035,2695,2582,2072,2566, 430,2430,1727, 842,1396,3947,3702, 613, 377, # 2704
- 278, 236,1417,3388,3314,3174, 757,1869, 107,3530,6145,1194, 623,2262, 207,1253, # 2720
-2167,3446,3948, 492,1117,1935, 536,1838,2757,1246,4332, 696,2095,2406,1393,1572, # 2736
-3175,1782, 583, 190, 253,1390,2230, 830,3126,3389, 934,3245,1703,1749,2979,1870, # 2752
-2545,1656,2204, 869,2346,4116,3176,1817, 496,1764,4644, 942,1504, 404,1903,1122, # 2768
-1580,3606,2945,1022, 515, 372,1735, 955,2431,3036,6146,2797,1110,2302,2798, 617, # 2784
-6147, 441, 762,1771,3447,3607,3608,1904, 840,3037, 86, 939,1385, 572,1370,2445, # 2800
-1336, 114,3703, 898, 294, 203,3315, 703,1583,2274, 429, 961,4333,1854,1951,3390, # 2816
-2373,3704,4334,1318,1381, 966,1911,2322,1006,1155, 309, 989, 458,2718,1795,1372, # 2832
-1203, 252,1689,1363,3177, 517,1936, 168,1490, 562, 193,3823,1042,4117,1835, 551, # 2848
- 470,4645, 395, 489,3448,1871,1465,2583,2641, 417,1493, 279,1295, 511,1236,1119, # 2864
- 72,1231,1982,1812,3004, 871,1564, 984,3449,1667,2696,2096,4646,2347,2833,1673, # 2880
-3609, 695,3246,2668, 807,1183,4647, 890, 388,2333,1801,1457,2911,1765,1477,1031, # 2896
-3316,3317,1278,3391,2799,2292,2526, 163,3450,4335,2669,1404,1802,6148,2323,2407, # 2912
-1584,1728,1494,1824,1269, 298, 909,3318,1034,1632, 375, 776,1683,2061, 291, 210, # 2928
-1123, 809,1249,1002,2642,3038, 206,1011,2132, 144, 975, 882,1565, 342, 667, 754, # 2944
-1442,2143,1299,2303,2062, 447, 626,2205,1221,2739,2912,1144,1214,2206,2584, 760, # 2960
-1715, 614, 950,1281,2670,2621, 810, 577,1287,2546,4648, 242,2168, 250,2643, 691, # 2976
- 123,2644, 647, 313,1029, 689,1357,2946,1650, 216, 771,1339,1306, 808,2063, 549, # 2992
- 913,1371,2913,2914,6149,1466,1092,1174,1196,1311,2605,2396,1783,1796,3079, 406, # 3008
-2671,2117,3949,4649, 487,1825,2220,6150,2915, 448,2348,1073,6151,2397,1707, 130, # 3024
- 900,1598, 329, 176,1959,2527,1620,6152,2275,4336,3319,1983,2191,3705,3610,2155, # 3040
-3706,1912,1513,1614,6153,1988, 646, 392,2304,1589,3320,3039,1826,1239,1352,1340, # 3056
-2916, 505,2567,1709,1437,2408,2547, 906,6154,2672, 384,1458,1594,1100,1329, 710, # 3072
- 423,3531,2064,2231,2622,1989,2673,1087,1882, 333, 841,3005,1296,2882,2379, 580, # 3088
-1937,1827,1293,2585, 601, 574, 249,1772,4118,2079,1120, 645, 901,1176,1690, 795, # 3104
-2207, 478,1434, 516,1190,1530, 761,2080, 930,1264, 355, 435,1552, 644,1791, 987, # 3120
- 220,1364,1163,1121,1538, 306,2169,1327,1222, 546,2645, 218, 241, 610,1704,3321, # 3136
-1984,1839,1966,2528, 451,6155,2586,3707,2568, 907,3178, 254,2947, 186,1845,4650, # 3152
- 745, 432,1757, 428,1633, 888,2246,2221,2489,3611,2118,1258,1265, 956,3127,1784, # 3168
-4337,2490, 319, 510, 119, 457,3612, 274,2035,2007,4651,1409,3128, 970,2758, 590, # 3184
-2800, 661,2247,4652,2008,3950,1420,1549,3080,3322,3951,1651,1375,2111, 485,2491, # 3200
-1429,1156,6156,2548,2183,1495, 831,1840,2529,2446, 501,1657, 307,1894,3247,1341, # 3216
- 666, 899,2156,1539,2549,1559, 886, 349,2208,3081,2305,1736,3824,2170,2759,1014, # 3232
-1913,1386, 542,1397,2948, 490, 368, 716, 362, 159, 282,2569,1129,1658,1288,1750, # 3248
-2674, 276, 649,2016, 751,1496, 658,1818,1284,1862,2209,2087,2512,3451, 622,2834, # 3264
- 376, 117,1060,2053,1208,1721,1101,1443, 247,1250,3179,1792,3952,2760,2398,3953, # 3280
-6157,2144,3708, 446,2432,1151,2570,3452,2447,2761,2835,1210,2448,3082, 424,2222, # 3296
-1251,2449,2119,2836, 504,1581,4338, 602, 817, 857,3825,2349,2306, 357,3826,1470, # 3312
-1883,2883, 255, 958, 929,2917,3248, 302,4653,1050,1271,1751,2307,1952,1430,2697, # 3328
-2719,2359, 354,3180, 777, 158,2036,4339,1659,4340,4654,2308,2949,2248,1146,2232, # 3344
-3532,2720,1696,2623,3827,6158,3129,1550,2698,1485,1297,1428, 637, 931,2721,2145, # 3360
- 914,2550,2587, 81,2450, 612, 827,2646,1242,4655,1118,2884, 472,1855,3181,3533, # 3376
-3534, 569,1353,2699,1244,1758,2588,4119,2009,2762,2171,3709,1312,1531,6159,1152, # 3392
-1938, 134,1830, 471,3710,2276,1112,1535,3323,3453,3535, 982,1337,2950, 488, 826, # 3408
- 674,1058,1628,4120,2017, 522,2399, 211, 568,1367,3454, 350, 293,1872,1139,3249, # 3424
-1399,1946,3006,1300,2360,3324, 588, 736,6160,2606, 744, 669,3536,3828,6161,1358, # 3440
- 199, 723, 848, 933, 851,1939,1505,1514,1338,1618,1831,4656,1634,3613, 443,2740, # 3456
-3829, 717,1947, 491,1914,6162,2551,1542,4121,1025,6163,1099,1223, 198,3040,2722, # 3472
- 370, 410,1905,2589, 998,1248,3182,2380, 519,1449,4122,1710, 947, 928,1153,4341, # 3488
-2277, 344,2624,1511, 615, 105, 161,1212,1076,1960,3130,2054,1926,1175,1906,2473, # 3504
- 414,1873,2801,6164,2309, 315,1319,3325, 318,2018,2146,2157, 963, 631, 223,4342, # 3520
-4343,2675, 479,3711,1197,2625,3712,2676,2361,6165,4344,4123,6166,2451,3183,1886, # 3536
-2184,1674,1330,1711,1635,1506, 799, 219,3250,3083,3954,1677,3713,3326,2081,3614, # 3552
-1652,2073,4657,1147,3041,1752, 643,1961, 147,1974,3955,6167,1716,2037, 918,3007, # 3568
-1994, 120,1537, 118, 609,3184,4345, 740,3455,1219, 332,1615,3830,6168,1621,2980, # 3584
-1582, 783, 212, 553,2350,3714,1349,2433,2082,4124, 889,6169,2310,1275,1410, 973, # 3600
- 166,1320,3456,1797,1215,3185,2885,1846,2590,2763,4658, 629, 822,3008, 763, 940, # 3616
-1990,2862, 439,2409,1566,1240,1622, 926,1282,1907,2764, 654,2210,1607, 327,1130, # 3632
-3956,1678,1623,6170,2434,2192, 686, 608,3831,3715, 903,3957,3042,6171,2741,1522, # 3648
-1915,1105,1555,2552,1359, 323,3251,4346,3457, 738,1354,2553,2311,2334,1828,2003, # 3664
-3832,1753,2351,1227,6172,1887,4125,1478,6173,2410,1874,1712,1847, 520,1204,2607, # 3680
- 264,4659, 836,2677,2102, 600,4660,3833,2278,3084,6174,4347,3615,1342, 640, 532, # 3696
- 543,2608,1888,2400,2591,1009,4348,1497, 341,1737,3616,2723,1394, 529,3252,1321, # 3712
- 983,4661,1515,2120, 971,2592, 924, 287,1662,3186,4349,2700,4350,1519, 908,1948, # 3728
-2452, 156, 796,1629,1486,2223,2055, 694,4126,1259,1036,3392,1213,2249,2742,1889, # 3744
-1230,3958,1015, 910, 408, 559,3617,4662, 746, 725, 935,4663,3959,3009,1289, 563, # 3760
- 867,4664,3960,1567,2981,2038,2626, 988,2263,2381,4351, 143,2374, 704,1895,6175, # 3776
-1188,3716,2088, 673,3085,2362,4352, 484,1608,1921,2765,2918, 215, 904,3618,3537, # 3792
- 894, 509, 976,3043,2701,3961,4353,2837,2982, 498,6176,6177,1102,3538,1332,3393, # 3808
-1487,1636,1637, 233, 245,3962, 383, 650, 995,3044, 460,1520,1206,2352, 749,3327, # 3824
- 530, 700, 389,1438,1560,1773,3963,2264, 719,2951,2724,3834, 870,1832,1644,1000, # 3840
- 839,2474,3717, 197,1630,3394, 365,2886,3964,1285,2133, 734, 922, 818,1106, 732, # 3856
- 480,2083,1774,3458, 923,2279,1350, 221,3086, 85,2233,2234,3835,1585,3010,2147, # 3872
-1387,1705,2382,1619,2475, 133, 239,2802,1991,1016,2084,2383, 411,2838,1113, 651, # 3888
-1985,1160,3328, 990,1863,3087,1048,1276,2647, 265,2627,1599,3253,2056, 150, 638, # 3904
-2019, 656, 853, 326,1479, 680,1439,4354,1001,1759, 413,3459,3395,2492,1431, 459, # 3920
-4355,1125,3329,2265,1953,1450,2065,2863, 849, 351,2678,3131,3254,3255,1104,1577, # 3936
- 227,1351,1645,2453,2193,1421,2887, 812,2121, 634, 95,2435, 201,2312,4665,1646, # 3952
-1671,2743,1601,2554,2702,2648,2280,1315,1366,2089,3132,1573,3718,3965,1729,1189, # 3968
- 328,2679,1077,1940,1136, 558,1283, 964,1195, 621,2074,1199,1743,3460,3619,1896, # 3984
-1916,1890,3836,2952,1154,2112,1064, 862, 378,3011,2066,2113,2803,1568,2839,6178, # 4000
-3088,2919,1941,1660,2004,1992,2194, 142, 707,1590,1708,1624,1922,1023,1836,1233, # 4016
-1004,2313, 789, 741,3620,6179,1609,2411,1200,4127,3719,3720,4666,2057,3721, 593, # 4032
-2840, 367,2920,1878,6180,3461,1521, 628,1168, 692,2211,2649, 300, 720,2067,2571, # 4048
-2953,3396, 959,2504,3966,3539,3462,1977, 701,6181, 954,1043, 800, 681, 183,3722, # 4064
-1803,1730,3540,4128,2103, 815,2314, 174, 467, 230,2454,1093,2134, 755,3541,3397, # 4080
-1141,1162,6182,1738,2039, 270,3256,2513,1005,1647,2185,3837, 858,1679,1897,1719, # 4096
-2954,2324,1806, 402, 670, 167,4129,1498,2158,2104, 750,6183, 915, 189,1680,1551, # 4112
- 455,4356,1501,2455, 405,1095,2955, 338,1586,1266,1819, 570, 641,1324, 237,1556, # 4128
-2650,1388,3723,6184,1368,2384,1343,1978,3089,2436, 879,3724, 792,1191, 758,3012, # 4144
-1411,2135,1322,4357, 240,4667,1848,3725,1574,6185, 420,3045,1546,1391, 714,4358, # 4160
-1967, 941,1864, 863, 664, 426, 560,1731,2680,1785,2864,1949,2363, 403,3330,1415, # 4176
-1279,2136,1697,2335, 204, 721,2097,3838, 90,6186,2085,2505, 191,3967, 124,2148, # 4192
-1376,1798,1178,1107,1898,1405, 860,4359,1243,1272,2375,2983,1558,2456,1638, 113, # 4208
-3621, 578,1923,2609, 880, 386,4130, 784,2186,2266,1422,2956,2172,1722, 497, 263, # 4224
-2514,1267,2412,2610, 177,2703,3542, 774,1927,1344, 616,1432,1595,1018, 172,4360, # 4240
-2325, 911,4361, 438,1468,3622, 794,3968,2024,2173,1681,1829,2957, 945, 895,3090, # 4256
- 575,2212,2476, 475,2401,2681, 785,2744,1745,2293,2555,1975,3133,2865, 394,4668, # 4272
-3839, 635,4131, 639, 202,1507,2195,2766,1345,1435,2572,3726,1908,1184,1181,2457, # 4288
-3727,3134,4362, 843,2611, 437, 916,4669, 234, 769,1884,3046,3047,3623, 833,6187, # 4304
-1639,2250,2402,1355,1185,2010,2047, 999, 525,1732,1290,1488,2612, 948,1578,3728, # 4320
-2413,2477,1216,2725,2159, 334,3840,1328,3624,2921,1525,4132, 564,1056, 891,4363, # 4336
-1444,1698,2385,2251,3729,1365,2281,2235,1717,6188, 864,3841,2515, 444, 527,2767, # 4352
-2922,3625, 544, 461,6189, 566, 209,2437,3398,2098,1065,2068,3331,3626,3257,2137, # 4368 #last 512
-)
-# fmt: on
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/johabfreq.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/johabfreq.py
deleted file mode 100644
index c129699..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/johabfreq.py
+++ /dev/null
@@ -1,2382 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is Mozilla Communicator client code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-# The frequency data itself is the same as euc-kr.
-# This is just a mapping table to euc-kr.
-
-JOHAB_TO_EUCKR_ORDER_TABLE = {
- 0x8861: 0,
- 0x8862: 1,
- 0x8865: 2,
- 0x8868: 3,
- 0x8869: 4,
- 0x886A: 5,
- 0x886B: 6,
- 0x8871: 7,
- 0x8873: 8,
- 0x8874: 9,
- 0x8875: 10,
- 0x8876: 11,
- 0x8877: 12,
- 0x8878: 13,
- 0x8879: 14,
- 0x887B: 15,
- 0x887C: 16,
- 0x887D: 17,
- 0x8881: 18,
- 0x8882: 19,
- 0x8885: 20,
- 0x8889: 21,
- 0x8891: 22,
- 0x8893: 23,
- 0x8895: 24,
- 0x8896: 25,
- 0x8897: 26,
- 0x88A1: 27,
- 0x88A2: 28,
- 0x88A5: 29,
- 0x88A9: 30,
- 0x88B5: 31,
- 0x88B7: 32,
- 0x88C1: 33,
- 0x88C5: 34,
- 0x88C9: 35,
- 0x88E1: 36,
- 0x88E2: 37,
- 0x88E5: 38,
- 0x88E8: 39,
- 0x88E9: 40,
- 0x88EB: 41,
- 0x88F1: 42,
- 0x88F3: 43,
- 0x88F5: 44,
- 0x88F6: 45,
- 0x88F7: 46,
- 0x88F8: 47,
- 0x88FB: 48,
- 0x88FC: 49,
- 0x88FD: 50,
- 0x8941: 51,
- 0x8945: 52,
- 0x8949: 53,
- 0x8951: 54,
- 0x8953: 55,
- 0x8955: 56,
- 0x8956: 57,
- 0x8957: 58,
- 0x8961: 59,
- 0x8962: 60,
- 0x8963: 61,
- 0x8965: 62,
- 0x8968: 63,
- 0x8969: 64,
- 0x8971: 65,
- 0x8973: 66,
- 0x8975: 67,
- 0x8976: 68,
- 0x8977: 69,
- 0x897B: 70,
- 0x8981: 71,
- 0x8985: 72,
- 0x8989: 73,
- 0x8993: 74,
- 0x8995: 75,
- 0x89A1: 76,
- 0x89A2: 77,
- 0x89A5: 78,
- 0x89A8: 79,
- 0x89A9: 80,
- 0x89AB: 81,
- 0x89AD: 82,
- 0x89B0: 83,
- 0x89B1: 84,
- 0x89B3: 85,
- 0x89B5: 86,
- 0x89B7: 87,
- 0x89B8: 88,
- 0x89C1: 89,
- 0x89C2: 90,
- 0x89C5: 91,
- 0x89C9: 92,
- 0x89CB: 93,
- 0x89D1: 94,
- 0x89D3: 95,
- 0x89D5: 96,
- 0x89D7: 97,
- 0x89E1: 98,
- 0x89E5: 99,
- 0x89E9: 100,
- 0x89F3: 101,
- 0x89F6: 102,
- 0x89F7: 103,
- 0x8A41: 104,
- 0x8A42: 105,
- 0x8A45: 106,
- 0x8A49: 107,
- 0x8A51: 108,
- 0x8A53: 109,
- 0x8A55: 110,
- 0x8A57: 111,
- 0x8A61: 112,
- 0x8A65: 113,
- 0x8A69: 114,
- 0x8A73: 115,
- 0x8A75: 116,
- 0x8A81: 117,
- 0x8A82: 118,
- 0x8A85: 119,
- 0x8A88: 120,
- 0x8A89: 121,
- 0x8A8A: 122,
- 0x8A8B: 123,
- 0x8A90: 124,
- 0x8A91: 125,
- 0x8A93: 126,
- 0x8A95: 127,
- 0x8A97: 128,
- 0x8A98: 129,
- 0x8AA1: 130,
- 0x8AA2: 131,
- 0x8AA5: 132,
- 0x8AA9: 133,
- 0x8AB6: 134,
- 0x8AB7: 135,
- 0x8AC1: 136,
- 0x8AD5: 137,
- 0x8AE1: 138,
- 0x8AE2: 139,
- 0x8AE5: 140,
- 0x8AE9: 141,
- 0x8AF1: 142,
- 0x8AF3: 143,
- 0x8AF5: 144,
- 0x8B41: 145,
- 0x8B45: 146,
- 0x8B49: 147,
- 0x8B61: 148,
- 0x8B62: 149,
- 0x8B65: 150,
- 0x8B68: 151,
- 0x8B69: 152,
- 0x8B6A: 153,
- 0x8B71: 154,
- 0x8B73: 155,
- 0x8B75: 156,
- 0x8B77: 157,
- 0x8B81: 158,
- 0x8BA1: 159,
- 0x8BA2: 160,
- 0x8BA5: 161,
- 0x8BA8: 162,
- 0x8BA9: 163,
- 0x8BAB: 164,
- 0x8BB1: 165,
- 0x8BB3: 166,
- 0x8BB5: 167,
- 0x8BB7: 168,
- 0x8BB8: 169,
- 0x8BBC: 170,
- 0x8C61: 171,
- 0x8C62: 172,
- 0x8C63: 173,
- 0x8C65: 174,
- 0x8C69: 175,
- 0x8C6B: 176,
- 0x8C71: 177,
- 0x8C73: 178,
- 0x8C75: 179,
- 0x8C76: 180,
- 0x8C77: 181,
- 0x8C7B: 182,
- 0x8C81: 183,
- 0x8C82: 184,
- 0x8C85: 185,
- 0x8C89: 186,
- 0x8C91: 187,
- 0x8C93: 188,
- 0x8C95: 189,
- 0x8C96: 190,
- 0x8C97: 191,
- 0x8CA1: 192,
- 0x8CA2: 193,
- 0x8CA9: 194,
- 0x8CE1: 195,
- 0x8CE2: 196,
- 0x8CE3: 197,
- 0x8CE5: 198,
- 0x8CE9: 199,
- 0x8CF1: 200,
- 0x8CF3: 201,
- 0x8CF5: 202,
- 0x8CF6: 203,
- 0x8CF7: 204,
- 0x8D41: 205,
- 0x8D42: 206,
- 0x8D45: 207,
- 0x8D51: 208,
- 0x8D55: 209,
- 0x8D57: 210,
- 0x8D61: 211,
- 0x8D65: 212,
- 0x8D69: 213,
- 0x8D75: 214,
- 0x8D76: 215,
- 0x8D7B: 216,
- 0x8D81: 217,
- 0x8DA1: 218,
- 0x8DA2: 219,
- 0x8DA5: 220,
- 0x8DA7: 221,
- 0x8DA9: 222,
- 0x8DB1: 223,
- 0x8DB3: 224,
- 0x8DB5: 225,
- 0x8DB7: 226,
- 0x8DB8: 227,
- 0x8DB9: 228,
- 0x8DC1: 229,
- 0x8DC2: 230,
- 0x8DC9: 231,
- 0x8DD6: 232,
- 0x8DD7: 233,
- 0x8DE1: 234,
- 0x8DE2: 235,
- 0x8DF7: 236,
- 0x8E41: 237,
- 0x8E45: 238,
- 0x8E49: 239,
- 0x8E51: 240,
- 0x8E53: 241,
- 0x8E57: 242,
- 0x8E61: 243,
- 0x8E81: 244,
- 0x8E82: 245,
- 0x8E85: 246,
- 0x8E89: 247,
- 0x8E90: 248,
- 0x8E91: 249,
- 0x8E93: 250,
- 0x8E95: 251,
- 0x8E97: 252,
- 0x8E98: 253,
- 0x8EA1: 254,
- 0x8EA9: 255,
- 0x8EB6: 256,
- 0x8EB7: 257,
- 0x8EC1: 258,
- 0x8EC2: 259,
- 0x8EC5: 260,
- 0x8EC9: 261,
- 0x8ED1: 262,
- 0x8ED3: 263,
- 0x8ED6: 264,
- 0x8EE1: 265,
- 0x8EE5: 266,
- 0x8EE9: 267,
- 0x8EF1: 268,
- 0x8EF3: 269,
- 0x8F41: 270,
- 0x8F61: 271,
- 0x8F62: 272,
- 0x8F65: 273,
- 0x8F67: 274,
- 0x8F69: 275,
- 0x8F6B: 276,
- 0x8F70: 277,
- 0x8F71: 278,
- 0x8F73: 279,
- 0x8F75: 280,
- 0x8F77: 281,
- 0x8F7B: 282,
- 0x8FA1: 283,
- 0x8FA2: 284,
- 0x8FA5: 285,
- 0x8FA9: 286,
- 0x8FB1: 287,
- 0x8FB3: 288,
- 0x8FB5: 289,
- 0x8FB7: 290,
- 0x9061: 291,
- 0x9062: 292,
- 0x9063: 293,
- 0x9065: 294,
- 0x9068: 295,
- 0x9069: 296,
- 0x906A: 297,
- 0x906B: 298,
- 0x9071: 299,
- 0x9073: 300,
- 0x9075: 301,
- 0x9076: 302,
- 0x9077: 303,
- 0x9078: 304,
- 0x9079: 305,
- 0x907B: 306,
- 0x907D: 307,
- 0x9081: 308,
- 0x9082: 309,
- 0x9085: 310,
- 0x9089: 311,
- 0x9091: 312,
- 0x9093: 313,
- 0x9095: 314,
- 0x9096: 315,
- 0x9097: 316,
- 0x90A1: 317,
- 0x90A2: 318,
- 0x90A5: 319,
- 0x90A9: 320,
- 0x90B1: 321,
- 0x90B7: 322,
- 0x90E1: 323,
- 0x90E2: 324,
- 0x90E4: 325,
- 0x90E5: 326,
- 0x90E9: 327,
- 0x90EB: 328,
- 0x90EC: 329,
- 0x90F1: 330,
- 0x90F3: 331,
- 0x90F5: 332,
- 0x90F6: 333,
- 0x90F7: 334,
- 0x90FD: 335,
- 0x9141: 336,
- 0x9142: 337,
- 0x9145: 338,
- 0x9149: 339,
- 0x9151: 340,
- 0x9153: 341,
- 0x9155: 342,
- 0x9156: 343,
- 0x9157: 344,
- 0x9161: 345,
- 0x9162: 346,
- 0x9165: 347,
- 0x9169: 348,
- 0x9171: 349,
- 0x9173: 350,
- 0x9176: 351,
- 0x9177: 352,
- 0x917A: 353,
- 0x9181: 354,
- 0x9185: 355,
- 0x91A1: 356,
- 0x91A2: 357,
- 0x91A5: 358,
- 0x91A9: 359,
- 0x91AB: 360,
- 0x91B1: 361,
- 0x91B3: 362,
- 0x91B5: 363,
- 0x91B7: 364,
- 0x91BC: 365,
- 0x91BD: 366,
- 0x91C1: 367,
- 0x91C5: 368,
- 0x91C9: 369,
- 0x91D6: 370,
- 0x9241: 371,
- 0x9245: 372,
- 0x9249: 373,
- 0x9251: 374,
- 0x9253: 375,
- 0x9255: 376,
- 0x9261: 377,
- 0x9262: 378,
- 0x9265: 379,
- 0x9269: 380,
- 0x9273: 381,
- 0x9275: 382,
- 0x9277: 383,
- 0x9281: 384,
- 0x9282: 385,
- 0x9285: 386,
- 0x9288: 387,
- 0x9289: 388,
- 0x9291: 389,
- 0x9293: 390,
- 0x9295: 391,
- 0x9297: 392,
- 0x92A1: 393,
- 0x92B6: 394,
- 0x92C1: 395,
- 0x92E1: 396,
- 0x92E5: 397,
- 0x92E9: 398,
- 0x92F1: 399,
- 0x92F3: 400,
- 0x9341: 401,
- 0x9342: 402,
- 0x9349: 403,
- 0x9351: 404,
- 0x9353: 405,
- 0x9357: 406,
- 0x9361: 407,
- 0x9362: 408,
- 0x9365: 409,
- 0x9369: 410,
- 0x936A: 411,
- 0x936B: 412,
- 0x9371: 413,
- 0x9373: 414,
- 0x9375: 415,
- 0x9377: 416,
- 0x9378: 417,
- 0x937C: 418,
- 0x9381: 419,
- 0x9385: 420,
- 0x9389: 421,
- 0x93A1: 422,
- 0x93A2: 423,
- 0x93A5: 424,
- 0x93A9: 425,
- 0x93AB: 426,
- 0x93B1: 427,
- 0x93B3: 428,
- 0x93B5: 429,
- 0x93B7: 430,
- 0x93BC: 431,
- 0x9461: 432,
- 0x9462: 433,
- 0x9463: 434,
- 0x9465: 435,
- 0x9468: 436,
- 0x9469: 437,
- 0x946A: 438,
- 0x946B: 439,
- 0x946C: 440,
- 0x9470: 441,
- 0x9471: 442,
- 0x9473: 443,
- 0x9475: 444,
- 0x9476: 445,
- 0x9477: 446,
- 0x9478: 447,
- 0x9479: 448,
- 0x947D: 449,
- 0x9481: 450,
- 0x9482: 451,
- 0x9485: 452,
- 0x9489: 453,
- 0x9491: 454,
- 0x9493: 455,
- 0x9495: 456,
- 0x9496: 457,
- 0x9497: 458,
- 0x94A1: 459,
- 0x94E1: 460,
- 0x94E2: 461,
- 0x94E3: 462,
- 0x94E5: 463,
- 0x94E8: 464,
- 0x94E9: 465,
- 0x94EB: 466,
- 0x94EC: 467,
- 0x94F1: 468,
- 0x94F3: 469,
- 0x94F5: 470,
- 0x94F7: 471,
- 0x94F9: 472,
- 0x94FC: 473,
- 0x9541: 474,
- 0x9542: 475,
- 0x9545: 476,
- 0x9549: 477,
- 0x9551: 478,
- 0x9553: 479,
- 0x9555: 480,
- 0x9556: 481,
- 0x9557: 482,
- 0x9561: 483,
- 0x9565: 484,
- 0x9569: 485,
- 0x9576: 486,
- 0x9577: 487,
- 0x9581: 488,
- 0x9585: 489,
- 0x95A1: 490,
- 0x95A2: 491,
- 0x95A5: 492,
- 0x95A8: 493,
- 0x95A9: 494,
- 0x95AB: 495,
- 0x95AD: 496,
- 0x95B1: 497,
- 0x95B3: 498,
- 0x95B5: 499,
- 0x95B7: 500,
- 0x95B9: 501,
- 0x95BB: 502,
- 0x95C1: 503,
- 0x95C5: 504,
- 0x95C9: 505,
- 0x95E1: 506,
- 0x95F6: 507,
- 0x9641: 508,
- 0x9645: 509,
- 0x9649: 510,
- 0x9651: 511,
- 0x9653: 512,
- 0x9655: 513,
- 0x9661: 514,
- 0x9681: 515,
- 0x9682: 516,
- 0x9685: 517,
- 0x9689: 518,
- 0x9691: 519,
- 0x9693: 520,
- 0x9695: 521,
- 0x9697: 522,
- 0x96A1: 523,
- 0x96B6: 524,
- 0x96C1: 525,
- 0x96D7: 526,
- 0x96E1: 527,
- 0x96E5: 528,
- 0x96E9: 529,
- 0x96F3: 530,
- 0x96F5: 531,
- 0x96F7: 532,
- 0x9741: 533,
- 0x9745: 534,
- 0x9749: 535,
- 0x9751: 536,
- 0x9757: 537,
- 0x9761: 538,
- 0x9762: 539,
- 0x9765: 540,
- 0x9768: 541,
- 0x9769: 542,
- 0x976B: 543,
- 0x9771: 544,
- 0x9773: 545,
- 0x9775: 546,
- 0x9777: 547,
- 0x9781: 548,
- 0x97A1: 549,
- 0x97A2: 550,
- 0x97A5: 551,
- 0x97A8: 552,
- 0x97A9: 553,
- 0x97B1: 554,
- 0x97B3: 555,
- 0x97B5: 556,
- 0x97B6: 557,
- 0x97B7: 558,
- 0x97B8: 559,
- 0x9861: 560,
- 0x9862: 561,
- 0x9865: 562,
- 0x9869: 563,
- 0x9871: 564,
- 0x9873: 565,
- 0x9875: 566,
- 0x9876: 567,
- 0x9877: 568,
- 0x987D: 569,
- 0x9881: 570,
- 0x9882: 571,
- 0x9885: 572,
- 0x9889: 573,
- 0x9891: 574,
- 0x9893: 575,
- 0x9895: 576,
- 0x9896: 577,
- 0x9897: 578,
- 0x98E1: 579,
- 0x98E2: 580,
- 0x98E5: 581,
- 0x98E9: 582,
- 0x98EB: 583,
- 0x98EC: 584,
- 0x98F1: 585,
- 0x98F3: 586,
- 0x98F5: 587,
- 0x98F6: 588,
- 0x98F7: 589,
- 0x98FD: 590,
- 0x9941: 591,
- 0x9942: 592,
- 0x9945: 593,
- 0x9949: 594,
- 0x9951: 595,
- 0x9953: 596,
- 0x9955: 597,
- 0x9956: 598,
- 0x9957: 599,
- 0x9961: 600,
- 0x9976: 601,
- 0x99A1: 602,
- 0x99A2: 603,
- 0x99A5: 604,
- 0x99A9: 605,
- 0x99B7: 606,
- 0x99C1: 607,
- 0x99C9: 608,
- 0x99E1: 609,
- 0x9A41: 610,
- 0x9A45: 611,
- 0x9A81: 612,
- 0x9A82: 613,
- 0x9A85: 614,
- 0x9A89: 615,
- 0x9A90: 616,
- 0x9A91: 617,
- 0x9A97: 618,
- 0x9AC1: 619,
- 0x9AE1: 620,
- 0x9AE5: 621,
- 0x9AE9: 622,
- 0x9AF1: 623,
- 0x9AF3: 624,
- 0x9AF7: 625,
- 0x9B61: 626,
- 0x9B62: 627,
- 0x9B65: 628,
- 0x9B68: 629,
- 0x9B69: 630,
- 0x9B71: 631,
- 0x9B73: 632,
- 0x9B75: 633,
- 0x9B81: 634,
- 0x9B85: 635,
- 0x9B89: 636,
- 0x9B91: 637,
- 0x9B93: 638,
- 0x9BA1: 639,
- 0x9BA5: 640,
- 0x9BA9: 641,
- 0x9BB1: 642,
- 0x9BB3: 643,
- 0x9BB5: 644,
- 0x9BB7: 645,
- 0x9C61: 646,
- 0x9C62: 647,
- 0x9C65: 648,
- 0x9C69: 649,
- 0x9C71: 650,
- 0x9C73: 651,
- 0x9C75: 652,
- 0x9C76: 653,
- 0x9C77: 654,
- 0x9C78: 655,
- 0x9C7C: 656,
- 0x9C7D: 657,
- 0x9C81: 658,
- 0x9C82: 659,
- 0x9C85: 660,
- 0x9C89: 661,
- 0x9C91: 662,
- 0x9C93: 663,
- 0x9C95: 664,
- 0x9C96: 665,
- 0x9C97: 666,
- 0x9CA1: 667,
- 0x9CA2: 668,
- 0x9CA5: 669,
- 0x9CB5: 670,
- 0x9CB7: 671,
- 0x9CE1: 672,
- 0x9CE2: 673,
- 0x9CE5: 674,
- 0x9CE9: 675,
- 0x9CF1: 676,
- 0x9CF3: 677,
- 0x9CF5: 678,
- 0x9CF6: 679,
- 0x9CF7: 680,
- 0x9CFD: 681,
- 0x9D41: 682,
- 0x9D42: 683,
- 0x9D45: 684,
- 0x9D49: 685,
- 0x9D51: 686,
- 0x9D53: 687,
- 0x9D55: 688,
- 0x9D57: 689,
- 0x9D61: 690,
- 0x9D62: 691,
- 0x9D65: 692,
- 0x9D69: 693,
- 0x9D71: 694,
- 0x9D73: 695,
- 0x9D75: 696,
- 0x9D76: 697,
- 0x9D77: 698,
- 0x9D81: 699,
- 0x9D85: 700,
- 0x9D93: 701,
- 0x9D95: 702,
- 0x9DA1: 703,
- 0x9DA2: 704,
- 0x9DA5: 705,
- 0x9DA9: 706,
- 0x9DB1: 707,
- 0x9DB3: 708,
- 0x9DB5: 709,
- 0x9DB7: 710,
- 0x9DC1: 711,
- 0x9DC5: 712,
- 0x9DD7: 713,
- 0x9DF6: 714,
- 0x9E41: 715,
- 0x9E45: 716,
- 0x9E49: 717,
- 0x9E51: 718,
- 0x9E53: 719,
- 0x9E55: 720,
- 0x9E57: 721,
- 0x9E61: 722,
- 0x9E65: 723,
- 0x9E69: 724,
- 0x9E73: 725,
- 0x9E75: 726,
- 0x9E77: 727,
- 0x9E81: 728,
- 0x9E82: 729,
- 0x9E85: 730,
- 0x9E89: 731,
- 0x9E91: 732,
- 0x9E93: 733,
- 0x9E95: 734,
- 0x9E97: 735,
- 0x9EA1: 736,
- 0x9EB6: 737,
- 0x9EC1: 738,
- 0x9EE1: 739,
- 0x9EE2: 740,
- 0x9EE5: 741,
- 0x9EE9: 742,
- 0x9EF1: 743,
- 0x9EF5: 744,
- 0x9EF7: 745,
- 0x9F41: 746,
- 0x9F42: 747,
- 0x9F45: 748,
- 0x9F49: 749,
- 0x9F51: 750,
- 0x9F53: 751,
- 0x9F55: 752,
- 0x9F57: 753,
- 0x9F61: 754,
- 0x9F62: 755,
- 0x9F65: 756,
- 0x9F69: 757,
- 0x9F71: 758,
- 0x9F73: 759,
- 0x9F75: 760,
- 0x9F77: 761,
- 0x9F78: 762,
- 0x9F7B: 763,
- 0x9F7C: 764,
- 0x9FA1: 765,
- 0x9FA2: 766,
- 0x9FA5: 767,
- 0x9FA9: 768,
- 0x9FB1: 769,
- 0x9FB3: 770,
- 0x9FB5: 771,
- 0x9FB7: 772,
- 0xA061: 773,
- 0xA062: 774,
- 0xA065: 775,
- 0xA067: 776,
- 0xA068: 777,
- 0xA069: 778,
- 0xA06A: 779,
- 0xA06B: 780,
- 0xA071: 781,
- 0xA073: 782,
- 0xA075: 783,
- 0xA077: 784,
- 0xA078: 785,
- 0xA07B: 786,
- 0xA07D: 787,
- 0xA081: 788,
- 0xA082: 789,
- 0xA085: 790,
- 0xA089: 791,
- 0xA091: 792,
- 0xA093: 793,
- 0xA095: 794,
- 0xA096: 795,
- 0xA097: 796,
- 0xA098: 797,
- 0xA0A1: 798,
- 0xA0A2: 799,
- 0xA0A9: 800,
- 0xA0B7: 801,
- 0xA0E1: 802,
- 0xA0E2: 803,
- 0xA0E5: 804,
- 0xA0E9: 805,
- 0xA0EB: 806,
- 0xA0F1: 807,
- 0xA0F3: 808,
- 0xA0F5: 809,
- 0xA0F7: 810,
- 0xA0F8: 811,
- 0xA0FD: 812,
- 0xA141: 813,
- 0xA142: 814,
- 0xA145: 815,
- 0xA149: 816,
- 0xA151: 817,
- 0xA153: 818,
- 0xA155: 819,
- 0xA156: 820,
- 0xA157: 821,
- 0xA161: 822,
- 0xA162: 823,
- 0xA165: 824,
- 0xA169: 825,
- 0xA175: 826,
- 0xA176: 827,
- 0xA177: 828,
- 0xA179: 829,
- 0xA181: 830,
- 0xA1A1: 831,
- 0xA1A2: 832,
- 0xA1A4: 833,
- 0xA1A5: 834,
- 0xA1A9: 835,
- 0xA1AB: 836,
- 0xA1B1: 837,
- 0xA1B3: 838,
- 0xA1B5: 839,
- 0xA1B7: 840,
- 0xA1C1: 841,
- 0xA1C5: 842,
- 0xA1D6: 843,
- 0xA1D7: 844,
- 0xA241: 845,
- 0xA245: 846,
- 0xA249: 847,
- 0xA253: 848,
- 0xA255: 849,
- 0xA257: 850,
- 0xA261: 851,
- 0xA265: 852,
- 0xA269: 853,
- 0xA273: 854,
- 0xA275: 855,
- 0xA281: 856,
- 0xA282: 857,
- 0xA283: 858,
- 0xA285: 859,
- 0xA288: 860,
- 0xA289: 861,
- 0xA28A: 862,
- 0xA28B: 863,
- 0xA291: 864,
- 0xA293: 865,
- 0xA295: 866,
- 0xA297: 867,
- 0xA29B: 868,
- 0xA29D: 869,
- 0xA2A1: 870,
- 0xA2A5: 871,
- 0xA2A9: 872,
- 0xA2B3: 873,
- 0xA2B5: 874,
- 0xA2C1: 875,
- 0xA2E1: 876,
- 0xA2E5: 877,
- 0xA2E9: 878,
- 0xA341: 879,
- 0xA345: 880,
- 0xA349: 881,
- 0xA351: 882,
- 0xA355: 883,
- 0xA361: 884,
- 0xA365: 885,
- 0xA369: 886,
- 0xA371: 887,
- 0xA375: 888,
- 0xA3A1: 889,
- 0xA3A2: 890,
- 0xA3A5: 891,
- 0xA3A8: 892,
- 0xA3A9: 893,
- 0xA3AB: 894,
- 0xA3B1: 895,
- 0xA3B3: 896,
- 0xA3B5: 897,
- 0xA3B6: 898,
- 0xA3B7: 899,
- 0xA3B9: 900,
- 0xA3BB: 901,
- 0xA461: 902,
- 0xA462: 903,
- 0xA463: 904,
- 0xA464: 905,
- 0xA465: 906,
- 0xA468: 907,
- 0xA469: 908,
- 0xA46A: 909,
- 0xA46B: 910,
- 0xA46C: 911,
- 0xA471: 912,
- 0xA473: 913,
- 0xA475: 914,
- 0xA477: 915,
- 0xA47B: 916,
- 0xA481: 917,
- 0xA482: 918,
- 0xA485: 919,
- 0xA489: 920,
- 0xA491: 921,
- 0xA493: 922,
- 0xA495: 923,
- 0xA496: 924,
- 0xA497: 925,
- 0xA49B: 926,
- 0xA4A1: 927,
- 0xA4A2: 928,
- 0xA4A5: 929,
- 0xA4B3: 930,
- 0xA4E1: 931,
- 0xA4E2: 932,
- 0xA4E5: 933,
- 0xA4E8: 934,
- 0xA4E9: 935,
- 0xA4EB: 936,
- 0xA4F1: 937,
- 0xA4F3: 938,
- 0xA4F5: 939,
- 0xA4F7: 940,
- 0xA4F8: 941,
- 0xA541: 942,
- 0xA542: 943,
- 0xA545: 944,
- 0xA548: 945,
- 0xA549: 946,
- 0xA551: 947,
- 0xA553: 948,
- 0xA555: 949,
- 0xA556: 950,
- 0xA557: 951,
- 0xA561: 952,
- 0xA562: 953,
- 0xA565: 954,
- 0xA569: 955,
- 0xA573: 956,
- 0xA575: 957,
- 0xA576: 958,
- 0xA577: 959,
- 0xA57B: 960,
- 0xA581: 961,
- 0xA585: 962,
- 0xA5A1: 963,
- 0xA5A2: 964,
- 0xA5A3: 965,
- 0xA5A5: 966,
- 0xA5A9: 967,
- 0xA5B1: 968,
- 0xA5B3: 969,
- 0xA5B5: 970,
- 0xA5B7: 971,
- 0xA5C1: 972,
- 0xA5C5: 973,
- 0xA5D6: 974,
- 0xA5E1: 975,
- 0xA5F6: 976,
- 0xA641: 977,
- 0xA642: 978,
- 0xA645: 979,
- 0xA649: 980,
- 0xA651: 981,
- 0xA653: 982,
- 0xA661: 983,
- 0xA665: 984,
- 0xA681: 985,
- 0xA682: 986,
- 0xA685: 987,
- 0xA688: 988,
- 0xA689: 989,
- 0xA68A: 990,
- 0xA68B: 991,
- 0xA691: 992,
- 0xA693: 993,
- 0xA695: 994,
- 0xA697: 995,
- 0xA69B: 996,
- 0xA69C: 997,
- 0xA6A1: 998,
- 0xA6A9: 999,
- 0xA6B6: 1000,
- 0xA6C1: 1001,
- 0xA6E1: 1002,
- 0xA6E2: 1003,
- 0xA6E5: 1004,
- 0xA6E9: 1005,
- 0xA6F7: 1006,
- 0xA741: 1007,
- 0xA745: 1008,
- 0xA749: 1009,
- 0xA751: 1010,
- 0xA755: 1011,
- 0xA757: 1012,
- 0xA761: 1013,
- 0xA762: 1014,
- 0xA765: 1015,
- 0xA769: 1016,
- 0xA771: 1017,
- 0xA773: 1018,
- 0xA775: 1019,
- 0xA7A1: 1020,
- 0xA7A2: 1021,
- 0xA7A5: 1022,
- 0xA7A9: 1023,
- 0xA7AB: 1024,
- 0xA7B1: 1025,
- 0xA7B3: 1026,
- 0xA7B5: 1027,
- 0xA7B7: 1028,
- 0xA7B8: 1029,
- 0xA7B9: 1030,
- 0xA861: 1031,
- 0xA862: 1032,
- 0xA865: 1033,
- 0xA869: 1034,
- 0xA86B: 1035,
- 0xA871: 1036,
- 0xA873: 1037,
- 0xA875: 1038,
- 0xA876: 1039,
- 0xA877: 1040,
- 0xA87D: 1041,
- 0xA881: 1042,
- 0xA882: 1043,
- 0xA885: 1044,
- 0xA889: 1045,
- 0xA891: 1046,
- 0xA893: 1047,
- 0xA895: 1048,
- 0xA896: 1049,
- 0xA897: 1050,
- 0xA8A1: 1051,
- 0xA8A2: 1052,
- 0xA8B1: 1053,
- 0xA8E1: 1054,
- 0xA8E2: 1055,
- 0xA8E5: 1056,
- 0xA8E8: 1057,
- 0xA8E9: 1058,
- 0xA8F1: 1059,
- 0xA8F5: 1060,
- 0xA8F6: 1061,
- 0xA8F7: 1062,
- 0xA941: 1063,
- 0xA957: 1064,
- 0xA961: 1065,
- 0xA962: 1066,
- 0xA971: 1067,
- 0xA973: 1068,
- 0xA975: 1069,
- 0xA976: 1070,
- 0xA977: 1071,
- 0xA9A1: 1072,
- 0xA9A2: 1073,
- 0xA9A5: 1074,
- 0xA9A9: 1075,
- 0xA9B1: 1076,
- 0xA9B3: 1077,
- 0xA9B7: 1078,
- 0xAA41: 1079,
- 0xAA61: 1080,
- 0xAA77: 1081,
- 0xAA81: 1082,
- 0xAA82: 1083,
- 0xAA85: 1084,
- 0xAA89: 1085,
- 0xAA91: 1086,
- 0xAA95: 1087,
- 0xAA97: 1088,
- 0xAB41: 1089,
- 0xAB57: 1090,
- 0xAB61: 1091,
- 0xAB65: 1092,
- 0xAB69: 1093,
- 0xAB71: 1094,
- 0xAB73: 1095,
- 0xABA1: 1096,
- 0xABA2: 1097,
- 0xABA5: 1098,
- 0xABA9: 1099,
- 0xABB1: 1100,
- 0xABB3: 1101,
- 0xABB5: 1102,
- 0xABB7: 1103,
- 0xAC61: 1104,
- 0xAC62: 1105,
- 0xAC64: 1106,
- 0xAC65: 1107,
- 0xAC68: 1108,
- 0xAC69: 1109,
- 0xAC6A: 1110,
- 0xAC6B: 1111,
- 0xAC71: 1112,
- 0xAC73: 1113,
- 0xAC75: 1114,
- 0xAC76: 1115,
- 0xAC77: 1116,
- 0xAC7B: 1117,
- 0xAC81: 1118,
- 0xAC82: 1119,
- 0xAC85: 1120,
- 0xAC89: 1121,
- 0xAC91: 1122,
- 0xAC93: 1123,
- 0xAC95: 1124,
- 0xAC96: 1125,
- 0xAC97: 1126,
- 0xACA1: 1127,
- 0xACA2: 1128,
- 0xACA5: 1129,
- 0xACA9: 1130,
- 0xACB1: 1131,
- 0xACB3: 1132,
- 0xACB5: 1133,
- 0xACB7: 1134,
- 0xACC1: 1135,
- 0xACC5: 1136,
- 0xACC9: 1137,
- 0xACD1: 1138,
- 0xACD7: 1139,
- 0xACE1: 1140,
- 0xACE2: 1141,
- 0xACE3: 1142,
- 0xACE4: 1143,
- 0xACE5: 1144,
- 0xACE8: 1145,
- 0xACE9: 1146,
- 0xACEB: 1147,
- 0xACEC: 1148,
- 0xACF1: 1149,
- 0xACF3: 1150,
- 0xACF5: 1151,
- 0xACF6: 1152,
- 0xACF7: 1153,
- 0xACFC: 1154,
- 0xAD41: 1155,
- 0xAD42: 1156,
- 0xAD45: 1157,
- 0xAD49: 1158,
- 0xAD51: 1159,
- 0xAD53: 1160,
- 0xAD55: 1161,
- 0xAD56: 1162,
- 0xAD57: 1163,
- 0xAD61: 1164,
- 0xAD62: 1165,
- 0xAD65: 1166,
- 0xAD69: 1167,
- 0xAD71: 1168,
- 0xAD73: 1169,
- 0xAD75: 1170,
- 0xAD76: 1171,
- 0xAD77: 1172,
- 0xAD81: 1173,
- 0xAD85: 1174,
- 0xAD89: 1175,
- 0xAD97: 1176,
- 0xADA1: 1177,
- 0xADA2: 1178,
- 0xADA3: 1179,
- 0xADA5: 1180,
- 0xADA9: 1181,
- 0xADAB: 1182,
- 0xADB1: 1183,
- 0xADB3: 1184,
- 0xADB5: 1185,
- 0xADB7: 1186,
- 0xADBB: 1187,
- 0xADC1: 1188,
- 0xADC2: 1189,
- 0xADC5: 1190,
- 0xADC9: 1191,
- 0xADD7: 1192,
- 0xADE1: 1193,
- 0xADE5: 1194,
- 0xADE9: 1195,
- 0xADF1: 1196,
- 0xADF5: 1197,
- 0xADF6: 1198,
- 0xAE41: 1199,
- 0xAE45: 1200,
- 0xAE49: 1201,
- 0xAE51: 1202,
- 0xAE53: 1203,
- 0xAE55: 1204,
- 0xAE61: 1205,
- 0xAE62: 1206,
- 0xAE65: 1207,
- 0xAE69: 1208,
- 0xAE71: 1209,
- 0xAE73: 1210,
- 0xAE75: 1211,
- 0xAE77: 1212,
- 0xAE81: 1213,
- 0xAE82: 1214,
- 0xAE85: 1215,
- 0xAE88: 1216,
- 0xAE89: 1217,
- 0xAE91: 1218,
- 0xAE93: 1219,
- 0xAE95: 1220,
- 0xAE97: 1221,
- 0xAE99: 1222,
- 0xAE9B: 1223,
- 0xAE9C: 1224,
- 0xAEA1: 1225,
- 0xAEB6: 1226,
- 0xAEC1: 1227,
- 0xAEC2: 1228,
- 0xAEC5: 1229,
- 0xAEC9: 1230,
- 0xAED1: 1231,
- 0xAED7: 1232,
- 0xAEE1: 1233,
- 0xAEE2: 1234,
- 0xAEE5: 1235,
- 0xAEE9: 1236,
- 0xAEF1: 1237,
- 0xAEF3: 1238,
- 0xAEF5: 1239,
- 0xAEF7: 1240,
- 0xAF41: 1241,
- 0xAF42: 1242,
- 0xAF49: 1243,
- 0xAF51: 1244,
- 0xAF55: 1245,
- 0xAF57: 1246,
- 0xAF61: 1247,
- 0xAF62: 1248,
- 0xAF65: 1249,
- 0xAF69: 1250,
- 0xAF6A: 1251,
- 0xAF71: 1252,
- 0xAF73: 1253,
- 0xAF75: 1254,
- 0xAF77: 1255,
- 0xAFA1: 1256,
- 0xAFA2: 1257,
- 0xAFA5: 1258,
- 0xAFA8: 1259,
- 0xAFA9: 1260,
- 0xAFB0: 1261,
- 0xAFB1: 1262,
- 0xAFB3: 1263,
- 0xAFB5: 1264,
- 0xAFB7: 1265,
- 0xAFBC: 1266,
- 0xB061: 1267,
- 0xB062: 1268,
- 0xB064: 1269,
- 0xB065: 1270,
- 0xB069: 1271,
- 0xB071: 1272,
- 0xB073: 1273,
- 0xB076: 1274,
- 0xB077: 1275,
- 0xB07D: 1276,
- 0xB081: 1277,
- 0xB082: 1278,
- 0xB085: 1279,
- 0xB089: 1280,
- 0xB091: 1281,
- 0xB093: 1282,
- 0xB096: 1283,
- 0xB097: 1284,
- 0xB0B7: 1285,
- 0xB0E1: 1286,
- 0xB0E2: 1287,
- 0xB0E5: 1288,
- 0xB0E9: 1289,
- 0xB0EB: 1290,
- 0xB0F1: 1291,
- 0xB0F3: 1292,
- 0xB0F6: 1293,
- 0xB0F7: 1294,
- 0xB141: 1295,
- 0xB145: 1296,
- 0xB149: 1297,
- 0xB185: 1298,
- 0xB1A1: 1299,
- 0xB1A2: 1300,
- 0xB1A5: 1301,
- 0xB1A8: 1302,
- 0xB1A9: 1303,
- 0xB1AB: 1304,
- 0xB1B1: 1305,
- 0xB1B3: 1306,
- 0xB1B7: 1307,
- 0xB1C1: 1308,
- 0xB1C2: 1309,
- 0xB1C5: 1310,
- 0xB1D6: 1311,
- 0xB1E1: 1312,
- 0xB1F6: 1313,
- 0xB241: 1314,
- 0xB245: 1315,
- 0xB249: 1316,
- 0xB251: 1317,
- 0xB253: 1318,
- 0xB261: 1319,
- 0xB281: 1320,
- 0xB282: 1321,
- 0xB285: 1322,
- 0xB289: 1323,
- 0xB291: 1324,
- 0xB293: 1325,
- 0xB297: 1326,
- 0xB2A1: 1327,
- 0xB2B6: 1328,
- 0xB2C1: 1329,
- 0xB2E1: 1330,
- 0xB2E5: 1331,
- 0xB357: 1332,
- 0xB361: 1333,
- 0xB362: 1334,
- 0xB365: 1335,
- 0xB369: 1336,
- 0xB36B: 1337,
- 0xB370: 1338,
- 0xB371: 1339,
- 0xB373: 1340,
- 0xB381: 1341,
- 0xB385: 1342,
- 0xB389: 1343,
- 0xB391: 1344,
- 0xB3A1: 1345,
- 0xB3A2: 1346,
- 0xB3A5: 1347,
- 0xB3A9: 1348,
- 0xB3B1: 1349,
- 0xB3B3: 1350,
- 0xB3B5: 1351,
- 0xB3B7: 1352,
- 0xB461: 1353,
- 0xB462: 1354,
- 0xB465: 1355,
- 0xB466: 1356,
- 0xB467: 1357,
- 0xB469: 1358,
- 0xB46A: 1359,
- 0xB46B: 1360,
- 0xB470: 1361,
- 0xB471: 1362,
- 0xB473: 1363,
- 0xB475: 1364,
- 0xB476: 1365,
- 0xB477: 1366,
- 0xB47B: 1367,
- 0xB47C: 1368,
- 0xB481: 1369,
- 0xB482: 1370,
- 0xB485: 1371,
- 0xB489: 1372,
- 0xB491: 1373,
- 0xB493: 1374,
- 0xB495: 1375,
- 0xB496: 1376,
- 0xB497: 1377,
- 0xB4A1: 1378,
- 0xB4A2: 1379,
- 0xB4A5: 1380,
- 0xB4A9: 1381,
- 0xB4AC: 1382,
- 0xB4B1: 1383,
- 0xB4B3: 1384,
- 0xB4B5: 1385,
- 0xB4B7: 1386,
- 0xB4BB: 1387,
- 0xB4BD: 1388,
- 0xB4C1: 1389,
- 0xB4C5: 1390,
- 0xB4C9: 1391,
- 0xB4D3: 1392,
- 0xB4E1: 1393,
- 0xB4E2: 1394,
- 0xB4E5: 1395,
- 0xB4E6: 1396,
- 0xB4E8: 1397,
- 0xB4E9: 1398,
- 0xB4EA: 1399,
- 0xB4EB: 1400,
- 0xB4F1: 1401,
- 0xB4F3: 1402,
- 0xB4F4: 1403,
- 0xB4F5: 1404,
- 0xB4F6: 1405,
- 0xB4F7: 1406,
- 0xB4F8: 1407,
- 0xB4FA: 1408,
- 0xB4FC: 1409,
- 0xB541: 1410,
- 0xB542: 1411,
- 0xB545: 1412,
- 0xB549: 1413,
- 0xB551: 1414,
- 0xB553: 1415,
- 0xB555: 1416,
- 0xB557: 1417,
- 0xB561: 1418,
- 0xB562: 1419,
- 0xB563: 1420,
- 0xB565: 1421,
- 0xB569: 1422,
- 0xB56B: 1423,
- 0xB56C: 1424,
- 0xB571: 1425,
- 0xB573: 1426,
- 0xB574: 1427,
- 0xB575: 1428,
- 0xB576: 1429,
- 0xB577: 1430,
- 0xB57B: 1431,
- 0xB57C: 1432,
- 0xB57D: 1433,
- 0xB581: 1434,
- 0xB585: 1435,
- 0xB589: 1436,
- 0xB591: 1437,
- 0xB593: 1438,
- 0xB595: 1439,
- 0xB596: 1440,
- 0xB5A1: 1441,
- 0xB5A2: 1442,
- 0xB5A5: 1443,
- 0xB5A9: 1444,
- 0xB5AA: 1445,
- 0xB5AB: 1446,
- 0xB5AD: 1447,
- 0xB5B0: 1448,
- 0xB5B1: 1449,
- 0xB5B3: 1450,
- 0xB5B5: 1451,
- 0xB5B7: 1452,
- 0xB5B9: 1453,
- 0xB5C1: 1454,
- 0xB5C2: 1455,
- 0xB5C5: 1456,
- 0xB5C9: 1457,
- 0xB5D1: 1458,
- 0xB5D3: 1459,
- 0xB5D5: 1460,
- 0xB5D6: 1461,
- 0xB5D7: 1462,
- 0xB5E1: 1463,
- 0xB5E2: 1464,
- 0xB5E5: 1465,
- 0xB5F1: 1466,
- 0xB5F5: 1467,
- 0xB5F7: 1468,
- 0xB641: 1469,
- 0xB642: 1470,
- 0xB645: 1471,
- 0xB649: 1472,
- 0xB651: 1473,
- 0xB653: 1474,
- 0xB655: 1475,
- 0xB657: 1476,
- 0xB661: 1477,
- 0xB662: 1478,
- 0xB665: 1479,
- 0xB669: 1480,
- 0xB671: 1481,
- 0xB673: 1482,
- 0xB675: 1483,
- 0xB677: 1484,
- 0xB681: 1485,
- 0xB682: 1486,
- 0xB685: 1487,
- 0xB689: 1488,
- 0xB68A: 1489,
- 0xB68B: 1490,
- 0xB691: 1491,
- 0xB693: 1492,
- 0xB695: 1493,
- 0xB697: 1494,
- 0xB6A1: 1495,
- 0xB6A2: 1496,
- 0xB6A5: 1497,
- 0xB6A9: 1498,
- 0xB6B1: 1499,
- 0xB6B3: 1500,
- 0xB6B6: 1501,
- 0xB6B7: 1502,
- 0xB6C1: 1503,
- 0xB6C2: 1504,
- 0xB6C5: 1505,
- 0xB6C9: 1506,
- 0xB6D1: 1507,
- 0xB6D3: 1508,
- 0xB6D7: 1509,
- 0xB6E1: 1510,
- 0xB6E2: 1511,
- 0xB6E5: 1512,
- 0xB6E9: 1513,
- 0xB6F1: 1514,
- 0xB6F3: 1515,
- 0xB6F5: 1516,
- 0xB6F7: 1517,
- 0xB741: 1518,
- 0xB742: 1519,
- 0xB745: 1520,
- 0xB749: 1521,
- 0xB751: 1522,
- 0xB753: 1523,
- 0xB755: 1524,
- 0xB757: 1525,
- 0xB759: 1526,
- 0xB761: 1527,
- 0xB762: 1528,
- 0xB765: 1529,
- 0xB769: 1530,
- 0xB76F: 1531,
- 0xB771: 1532,
- 0xB773: 1533,
- 0xB775: 1534,
- 0xB777: 1535,
- 0xB778: 1536,
- 0xB779: 1537,
- 0xB77A: 1538,
- 0xB77B: 1539,
- 0xB77C: 1540,
- 0xB77D: 1541,
- 0xB781: 1542,
- 0xB785: 1543,
- 0xB789: 1544,
- 0xB791: 1545,
- 0xB795: 1546,
- 0xB7A1: 1547,
- 0xB7A2: 1548,
- 0xB7A5: 1549,
- 0xB7A9: 1550,
- 0xB7AA: 1551,
- 0xB7AB: 1552,
- 0xB7B0: 1553,
- 0xB7B1: 1554,
- 0xB7B3: 1555,
- 0xB7B5: 1556,
- 0xB7B6: 1557,
- 0xB7B7: 1558,
- 0xB7B8: 1559,
- 0xB7BC: 1560,
- 0xB861: 1561,
- 0xB862: 1562,
- 0xB865: 1563,
- 0xB867: 1564,
- 0xB868: 1565,
- 0xB869: 1566,
- 0xB86B: 1567,
- 0xB871: 1568,
- 0xB873: 1569,
- 0xB875: 1570,
- 0xB876: 1571,
- 0xB877: 1572,
- 0xB878: 1573,
- 0xB881: 1574,
- 0xB882: 1575,
- 0xB885: 1576,
- 0xB889: 1577,
- 0xB891: 1578,
- 0xB893: 1579,
- 0xB895: 1580,
- 0xB896: 1581,
- 0xB897: 1582,
- 0xB8A1: 1583,
- 0xB8A2: 1584,
- 0xB8A5: 1585,
- 0xB8A7: 1586,
- 0xB8A9: 1587,
- 0xB8B1: 1588,
- 0xB8B7: 1589,
- 0xB8C1: 1590,
- 0xB8C5: 1591,
- 0xB8C9: 1592,
- 0xB8E1: 1593,
- 0xB8E2: 1594,
- 0xB8E5: 1595,
- 0xB8E9: 1596,
- 0xB8EB: 1597,
- 0xB8F1: 1598,
- 0xB8F3: 1599,
- 0xB8F5: 1600,
- 0xB8F7: 1601,
- 0xB8F8: 1602,
- 0xB941: 1603,
- 0xB942: 1604,
- 0xB945: 1605,
- 0xB949: 1606,
- 0xB951: 1607,
- 0xB953: 1608,
- 0xB955: 1609,
- 0xB957: 1610,
- 0xB961: 1611,
- 0xB965: 1612,
- 0xB969: 1613,
- 0xB971: 1614,
- 0xB973: 1615,
- 0xB976: 1616,
- 0xB977: 1617,
- 0xB981: 1618,
- 0xB9A1: 1619,
- 0xB9A2: 1620,
- 0xB9A5: 1621,
- 0xB9A9: 1622,
- 0xB9AB: 1623,
- 0xB9B1: 1624,
- 0xB9B3: 1625,
- 0xB9B5: 1626,
- 0xB9B7: 1627,
- 0xB9B8: 1628,
- 0xB9B9: 1629,
- 0xB9BD: 1630,
- 0xB9C1: 1631,
- 0xB9C2: 1632,
- 0xB9C9: 1633,
- 0xB9D3: 1634,
- 0xB9D5: 1635,
- 0xB9D7: 1636,
- 0xB9E1: 1637,
- 0xB9F6: 1638,
- 0xB9F7: 1639,
- 0xBA41: 1640,
- 0xBA45: 1641,
- 0xBA49: 1642,
- 0xBA51: 1643,
- 0xBA53: 1644,
- 0xBA55: 1645,
- 0xBA57: 1646,
- 0xBA61: 1647,
- 0xBA62: 1648,
- 0xBA65: 1649,
- 0xBA77: 1650,
- 0xBA81: 1651,
- 0xBA82: 1652,
- 0xBA85: 1653,
- 0xBA89: 1654,
- 0xBA8A: 1655,
- 0xBA8B: 1656,
- 0xBA91: 1657,
- 0xBA93: 1658,
- 0xBA95: 1659,
- 0xBA97: 1660,
- 0xBAA1: 1661,
- 0xBAB6: 1662,
- 0xBAC1: 1663,
- 0xBAE1: 1664,
- 0xBAE2: 1665,
- 0xBAE5: 1666,
- 0xBAE9: 1667,
- 0xBAF1: 1668,
- 0xBAF3: 1669,
- 0xBAF5: 1670,
- 0xBB41: 1671,
- 0xBB45: 1672,
- 0xBB49: 1673,
- 0xBB51: 1674,
- 0xBB61: 1675,
- 0xBB62: 1676,
- 0xBB65: 1677,
- 0xBB69: 1678,
- 0xBB71: 1679,
- 0xBB73: 1680,
- 0xBB75: 1681,
- 0xBB77: 1682,
- 0xBBA1: 1683,
- 0xBBA2: 1684,
- 0xBBA5: 1685,
- 0xBBA8: 1686,
- 0xBBA9: 1687,
- 0xBBAB: 1688,
- 0xBBB1: 1689,
- 0xBBB3: 1690,
- 0xBBB5: 1691,
- 0xBBB7: 1692,
- 0xBBB8: 1693,
- 0xBBBB: 1694,
- 0xBBBC: 1695,
- 0xBC61: 1696,
- 0xBC62: 1697,
- 0xBC65: 1698,
- 0xBC67: 1699,
- 0xBC69: 1700,
- 0xBC6C: 1701,
- 0xBC71: 1702,
- 0xBC73: 1703,
- 0xBC75: 1704,
- 0xBC76: 1705,
- 0xBC77: 1706,
- 0xBC81: 1707,
- 0xBC82: 1708,
- 0xBC85: 1709,
- 0xBC89: 1710,
- 0xBC91: 1711,
- 0xBC93: 1712,
- 0xBC95: 1713,
- 0xBC96: 1714,
- 0xBC97: 1715,
- 0xBCA1: 1716,
- 0xBCA5: 1717,
- 0xBCB7: 1718,
- 0xBCE1: 1719,
- 0xBCE2: 1720,
- 0xBCE5: 1721,
- 0xBCE9: 1722,
- 0xBCF1: 1723,
- 0xBCF3: 1724,
- 0xBCF5: 1725,
- 0xBCF6: 1726,
- 0xBCF7: 1727,
- 0xBD41: 1728,
- 0xBD57: 1729,
- 0xBD61: 1730,
- 0xBD76: 1731,
- 0xBDA1: 1732,
- 0xBDA2: 1733,
- 0xBDA5: 1734,
- 0xBDA9: 1735,
- 0xBDB1: 1736,
- 0xBDB3: 1737,
- 0xBDB5: 1738,
- 0xBDB7: 1739,
- 0xBDB9: 1740,
- 0xBDC1: 1741,
- 0xBDC2: 1742,
- 0xBDC9: 1743,
- 0xBDD6: 1744,
- 0xBDE1: 1745,
- 0xBDF6: 1746,
- 0xBE41: 1747,
- 0xBE45: 1748,
- 0xBE49: 1749,
- 0xBE51: 1750,
- 0xBE53: 1751,
- 0xBE77: 1752,
- 0xBE81: 1753,
- 0xBE82: 1754,
- 0xBE85: 1755,
- 0xBE89: 1756,
- 0xBE91: 1757,
- 0xBE93: 1758,
- 0xBE97: 1759,
- 0xBEA1: 1760,
- 0xBEB6: 1761,
- 0xBEB7: 1762,
- 0xBEE1: 1763,
- 0xBF41: 1764,
- 0xBF61: 1765,
- 0xBF71: 1766,
- 0xBF75: 1767,
- 0xBF77: 1768,
- 0xBFA1: 1769,
- 0xBFA2: 1770,
- 0xBFA5: 1771,
- 0xBFA9: 1772,
- 0xBFB1: 1773,
- 0xBFB3: 1774,
- 0xBFB7: 1775,
- 0xBFB8: 1776,
- 0xBFBD: 1777,
- 0xC061: 1778,
- 0xC062: 1779,
- 0xC065: 1780,
- 0xC067: 1781,
- 0xC069: 1782,
- 0xC071: 1783,
- 0xC073: 1784,
- 0xC075: 1785,
- 0xC076: 1786,
- 0xC077: 1787,
- 0xC078: 1788,
- 0xC081: 1789,
- 0xC082: 1790,
- 0xC085: 1791,
- 0xC089: 1792,
- 0xC091: 1793,
- 0xC093: 1794,
- 0xC095: 1795,
- 0xC096: 1796,
- 0xC097: 1797,
- 0xC0A1: 1798,
- 0xC0A5: 1799,
- 0xC0A7: 1800,
- 0xC0A9: 1801,
- 0xC0B1: 1802,
- 0xC0B7: 1803,
- 0xC0E1: 1804,
- 0xC0E2: 1805,
- 0xC0E5: 1806,
- 0xC0E9: 1807,
- 0xC0F1: 1808,
- 0xC0F3: 1809,
- 0xC0F5: 1810,
- 0xC0F6: 1811,
- 0xC0F7: 1812,
- 0xC141: 1813,
- 0xC142: 1814,
- 0xC145: 1815,
- 0xC149: 1816,
- 0xC151: 1817,
- 0xC153: 1818,
- 0xC155: 1819,
- 0xC157: 1820,
- 0xC161: 1821,
- 0xC165: 1822,
- 0xC176: 1823,
- 0xC181: 1824,
- 0xC185: 1825,
- 0xC197: 1826,
- 0xC1A1: 1827,
- 0xC1A2: 1828,
- 0xC1A5: 1829,
- 0xC1A9: 1830,
- 0xC1B1: 1831,
- 0xC1B3: 1832,
- 0xC1B5: 1833,
- 0xC1B7: 1834,
- 0xC1C1: 1835,
- 0xC1C5: 1836,
- 0xC1C9: 1837,
- 0xC1D7: 1838,
- 0xC241: 1839,
- 0xC245: 1840,
- 0xC249: 1841,
- 0xC251: 1842,
- 0xC253: 1843,
- 0xC255: 1844,
- 0xC257: 1845,
- 0xC261: 1846,
- 0xC271: 1847,
- 0xC281: 1848,
- 0xC282: 1849,
- 0xC285: 1850,
- 0xC289: 1851,
- 0xC291: 1852,
- 0xC293: 1853,
- 0xC295: 1854,
- 0xC297: 1855,
- 0xC2A1: 1856,
- 0xC2B6: 1857,
- 0xC2C1: 1858,
- 0xC2C5: 1859,
- 0xC2E1: 1860,
- 0xC2E5: 1861,
- 0xC2E9: 1862,
- 0xC2F1: 1863,
- 0xC2F3: 1864,
- 0xC2F5: 1865,
- 0xC2F7: 1866,
- 0xC341: 1867,
- 0xC345: 1868,
- 0xC349: 1869,
- 0xC351: 1870,
- 0xC357: 1871,
- 0xC361: 1872,
- 0xC362: 1873,
- 0xC365: 1874,
- 0xC369: 1875,
- 0xC371: 1876,
- 0xC373: 1877,
- 0xC375: 1878,
- 0xC377: 1879,
- 0xC3A1: 1880,
- 0xC3A2: 1881,
- 0xC3A5: 1882,
- 0xC3A8: 1883,
- 0xC3A9: 1884,
- 0xC3AA: 1885,
- 0xC3B1: 1886,
- 0xC3B3: 1887,
- 0xC3B5: 1888,
- 0xC3B7: 1889,
- 0xC461: 1890,
- 0xC462: 1891,
- 0xC465: 1892,
- 0xC469: 1893,
- 0xC471: 1894,
- 0xC473: 1895,
- 0xC475: 1896,
- 0xC477: 1897,
- 0xC481: 1898,
- 0xC482: 1899,
- 0xC485: 1900,
- 0xC489: 1901,
- 0xC491: 1902,
- 0xC493: 1903,
- 0xC495: 1904,
- 0xC496: 1905,
- 0xC497: 1906,
- 0xC4A1: 1907,
- 0xC4A2: 1908,
- 0xC4B7: 1909,
- 0xC4E1: 1910,
- 0xC4E2: 1911,
- 0xC4E5: 1912,
- 0xC4E8: 1913,
- 0xC4E9: 1914,
- 0xC4F1: 1915,
- 0xC4F3: 1916,
- 0xC4F5: 1917,
- 0xC4F6: 1918,
- 0xC4F7: 1919,
- 0xC541: 1920,
- 0xC542: 1921,
- 0xC545: 1922,
- 0xC549: 1923,
- 0xC551: 1924,
- 0xC553: 1925,
- 0xC555: 1926,
- 0xC557: 1927,
- 0xC561: 1928,
- 0xC565: 1929,
- 0xC569: 1930,
- 0xC571: 1931,
- 0xC573: 1932,
- 0xC575: 1933,
- 0xC576: 1934,
- 0xC577: 1935,
- 0xC581: 1936,
- 0xC5A1: 1937,
- 0xC5A2: 1938,
- 0xC5A5: 1939,
- 0xC5A9: 1940,
- 0xC5B1: 1941,
- 0xC5B3: 1942,
- 0xC5B5: 1943,
- 0xC5B7: 1944,
- 0xC5C1: 1945,
- 0xC5C2: 1946,
- 0xC5C5: 1947,
- 0xC5C9: 1948,
- 0xC5D1: 1949,
- 0xC5D7: 1950,
- 0xC5E1: 1951,
- 0xC5F7: 1952,
- 0xC641: 1953,
- 0xC649: 1954,
- 0xC661: 1955,
- 0xC681: 1956,
- 0xC682: 1957,
- 0xC685: 1958,
- 0xC689: 1959,
- 0xC691: 1960,
- 0xC693: 1961,
- 0xC695: 1962,
- 0xC697: 1963,
- 0xC6A1: 1964,
- 0xC6A5: 1965,
- 0xC6A9: 1966,
- 0xC6B7: 1967,
- 0xC6C1: 1968,
- 0xC6D7: 1969,
- 0xC6E1: 1970,
- 0xC6E2: 1971,
- 0xC6E5: 1972,
- 0xC6E9: 1973,
- 0xC6F1: 1974,
- 0xC6F3: 1975,
- 0xC6F5: 1976,
- 0xC6F7: 1977,
- 0xC741: 1978,
- 0xC745: 1979,
- 0xC749: 1980,
- 0xC751: 1981,
- 0xC761: 1982,
- 0xC762: 1983,
- 0xC765: 1984,
- 0xC769: 1985,
- 0xC771: 1986,
- 0xC773: 1987,
- 0xC777: 1988,
- 0xC7A1: 1989,
- 0xC7A2: 1990,
- 0xC7A5: 1991,
- 0xC7A9: 1992,
- 0xC7B1: 1993,
- 0xC7B3: 1994,
- 0xC7B5: 1995,
- 0xC7B7: 1996,
- 0xC861: 1997,
- 0xC862: 1998,
- 0xC865: 1999,
- 0xC869: 2000,
- 0xC86A: 2001,
- 0xC871: 2002,
- 0xC873: 2003,
- 0xC875: 2004,
- 0xC876: 2005,
- 0xC877: 2006,
- 0xC881: 2007,
- 0xC882: 2008,
- 0xC885: 2009,
- 0xC889: 2010,
- 0xC891: 2011,
- 0xC893: 2012,
- 0xC895: 2013,
- 0xC896: 2014,
- 0xC897: 2015,
- 0xC8A1: 2016,
- 0xC8B7: 2017,
- 0xC8E1: 2018,
- 0xC8E2: 2019,
- 0xC8E5: 2020,
- 0xC8E9: 2021,
- 0xC8EB: 2022,
- 0xC8F1: 2023,
- 0xC8F3: 2024,
- 0xC8F5: 2025,
- 0xC8F6: 2026,
- 0xC8F7: 2027,
- 0xC941: 2028,
- 0xC942: 2029,
- 0xC945: 2030,
- 0xC949: 2031,
- 0xC951: 2032,
- 0xC953: 2033,
- 0xC955: 2034,
- 0xC957: 2035,
- 0xC961: 2036,
- 0xC965: 2037,
- 0xC976: 2038,
- 0xC981: 2039,
- 0xC985: 2040,
- 0xC9A1: 2041,
- 0xC9A2: 2042,
- 0xC9A5: 2043,
- 0xC9A9: 2044,
- 0xC9B1: 2045,
- 0xC9B3: 2046,
- 0xC9B5: 2047,
- 0xC9B7: 2048,
- 0xC9BC: 2049,
- 0xC9C1: 2050,
- 0xC9C5: 2051,
- 0xC9E1: 2052,
- 0xCA41: 2053,
- 0xCA45: 2054,
- 0xCA55: 2055,
- 0xCA57: 2056,
- 0xCA61: 2057,
- 0xCA81: 2058,
- 0xCA82: 2059,
- 0xCA85: 2060,
- 0xCA89: 2061,
- 0xCA91: 2062,
- 0xCA93: 2063,
- 0xCA95: 2064,
- 0xCA97: 2065,
- 0xCAA1: 2066,
- 0xCAB6: 2067,
- 0xCAC1: 2068,
- 0xCAE1: 2069,
- 0xCAE2: 2070,
- 0xCAE5: 2071,
- 0xCAE9: 2072,
- 0xCAF1: 2073,
- 0xCAF3: 2074,
- 0xCAF7: 2075,
- 0xCB41: 2076,
- 0xCB45: 2077,
- 0xCB49: 2078,
- 0xCB51: 2079,
- 0xCB57: 2080,
- 0xCB61: 2081,
- 0xCB62: 2082,
- 0xCB65: 2083,
- 0xCB68: 2084,
- 0xCB69: 2085,
- 0xCB6B: 2086,
- 0xCB71: 2087,
- 0xCB73: 2088,
- 0xCB75: 2089,
- 0xCB81: 2090,
- 0xCB85: 2091,
- 0xCB89: 2092,
- 0xCB91: 2093,
- 0xCB93: 2094,
- 0xCBA1: 2095,
- 0xCBA2: 2096,
- 0xCBA5: 2097,
- 0xCBA9: 2098,
- 0xCBB1: 2099,
- 0xCBB3: 2100,
- 0xCBB5: 2101,
- 0xCBB7: 2102,
- 0xCC61: 2103,
- 0xCC62: 2104,
- 0xCC63: 2105,
- 0xCC65: 2106,
- 0xCC69: 2107,
- 0xCC6B: 2108,
- 0xCC71: 2109,
- 0xCC73: 2110,
- 0xCC75: 2111,
- 0xCC76: 2112,
- 0xCC77: 2113,
- 0xCC7B: 2114,
- 0xCC81: 2115,
- 0xCC82: 2116,
- 0xCC85: 2117,
- 0xCC89: 2118,
- 0xCC91: 2119,
- 0xCC93: 2120,
- 0xCC95: 2121,
- 0xCC96: 2122,
- 0xCC97: 2123,
- 0xCCA1: 2124,
- 0xCCA2: 2125,
- 0xCCE1: 2126,
- 0xCCE2: 2127,
- 0xCCE5: 2128,
- 0xCCE9: 2129,
- 0xCCF1: 2130,
- 0xCCF3: 2131,
- 0xCCF5: 2132,
- 0xCCF6: 2133,
- 0xCCF7: 2134,
- 0xCD41: 2135,
- 0xCD42: 2136,
- 0xCD45: 2137,
- 0xCD49: 2138,
- 0xCD51: 2139,
- 0xCD53: 2140,
- 0xCD55: 2141,
- 0xCD57: 2142,
- 0xCD61: 2143,
- 0xCD65: 2144,
- 0xCD69: 2145,
- 0xCD71: 2146,
- 0xCD73: 2147,
- 0xCD76: 2148,
- 0xCD77: 2149,
- 0xCD81: 2150,
- 0xCD89: 2151,
- 0xCD93: 2152,
- 0xCD95: 2153,
- 0xCDA1: 2154,
- 0xCDA2: 2155,
- 0xCDA5: 2156,
- 0xCDA9: 2157,
- 0xCDB1: 2158,
- 0xCDB3: 2159,
- 0xCDB5: 2160,
- 0xCDB7: 2161,
- 0xCDC1: 2162,
- 0xCDD7: 2163,
- 0xCE41: 2164,
- 0xCE45: 2165,
- 0xCE61: 2166,
- 0xCE65: 2167,
- 0xCE69: 2168,
- 0xCE73: 2169,
- 0xCE75: 2170,
- 0xCE81: 2171,
- 0xCE82: 2172,
- 0xCE85: 2173,
- 0xCE88: 2174,
- 0xCE89: 2175,
- 0xCE8B: 2176,
- 0xCE91: 2177,
- 0xCE93: 2178,
- 0xCE95: 2179,
- 0xCE97: 2180,
- 0xCEA1: 2181,
- 0xCEB7: 2182,
- 0xCEE1: 2183,
- 0xCEE5: 2184,
- 0xCEE9: 2185,
- 0xCEF1: 2186,
- 0xCEF5: 2187,
- 0xCF41: 2188,
- 0xCF45: 2189,
- 0xCF49: 2190,
- 0xCF51: 2191,
- 0xCF55: 2192,
- 0xCF57: 2193,
- 0xCF61: 2194,
- 0xCF65: 2195,
- 0xCF69: 2196,
- 0xCF71: 2197,
- 0xCF73: 2198,
- 0xCF75: 2199,
- 0xCFA1: 2200,
- 0xCFA2: 2201,
- 0xCFA5: 2202,
- 0xCFA9: 2203,
- 0xCFB1: 2204,
- 0xCFB3: 2205,
- 0xCFB5: 2206,
- 0xCFB7: 2207,
- 0xD061: 2208,
- 0xD062: 2209,
- 0xD065: 2210,
- 0xD069: 2211,
- 0xD06E: 2212,
- 0xD071: 2213,
- 0xD073: 2214,
- 0xD075: 2215,
- 0xD077: 2216,
- 0xD081: 2217,
- 0xD082: 2218,
- 0xD085: 2219,
- 0xD089: 2220,
- 0xD091: 2221,
- 0xD093: 2222,
- 0xD095: 2223,
- 0xD096: 2224,
- 0xD097: 2225,
- 0xD0A1: 2226,
- 0xD0B7: 2227,
- 0xD0E1: 2228,
- 0xD0E2: 2229,
- 0xD0E5: 2230,
- 0xD0E9: 2231,
- 0xD0EB: 2232,
- 0xD0F1: 2233,
- 0xD0F3: 2234,
- 0xD0F5: 2235,
- 0xD0F7: 2236,
- 0xD141: 2237,
- 0xD142: 2238,
- 0xD145: 2239,
- 0xD149: 2240,
- 0xD151: 2241,
- 0xD153: 2242,
- 0xD155: 2243,
- 0xD157: 2244,
- 0xD161: 2245,
- 0xD162: 2246,
- 0xD165: 2247,
- 0xD169: 2248,
- 0xD171: 2249,
- 0xD173: 2250,
- 0xD175: 2251,
- 0xD176: 2252,
- 0xD177: 2253,
- 0xD181: 2254,
- 0xD185: 2255,
- 0xD189: 2256,
- 0xD193: 2257,
- 0xD1A1: 2258,
- 0xD1A2: 2259,
- 0xD1A5: 2260,
- 0xD1A9: 2261,
- 0xD1AE: 2262,
- 0xD1B1: 2263,
- 0xD1B3: 2264,
- 0xD1B5: 2265,
- 0xD1B7: 2266,
- 0xD1BB: 2267,
- 0xD1C1: 2268,
- 0xD1C2: 2269,
- 0xD1C5: 2270,
- 0xD1C9: 2271,
- 0xD1D5: 2272,
- 0xD1D7: 2273,
- 0xD1E1: 2274,
- 0xD1E2: 2275,
- 0xD1E5: 2276,
- 0xD1F5: 2277,
- 0xD1F7: 2278,
- 0xD241: 2279,
- 0xD242: 2280,
- 0xD245: 2281,
- 0xD249: 2282,
- 0xD253: 2283,
- 0xD255: 2284,
- 0xD257: 2285,
- 0xD261: 2286,
- 0xD265: 2287,
- 0xD269: 2288,
- 0xD273: 2289,
- 0xD275: 2290,
- 0xD281: 2291,
- 0xD282: 2292,
- 0xD285: 2293,
- 0xD289: 2294,
- 0xD28E: 2295,
- 0xD291: 2296,
- 0xD295: 2297,
- 0xD297: 2298,
- 0xD2A1: 2299,
- 0xD2A5: 2300,
- 0xD2A9: 2301,
- 0xD2B1: 2302,
- 0xD2B7: 2303,
- 0xD2C1: 2304,
- 0xD2C2: 2305,
- 0xD2C5: 2306,
- 0xD2C9: 2307,
- 0xD2D7: 2308,
- 0xD2E1: 2309,
- 0xD2E2: 2310,
- 0xD2E5: 2311,
- 0xD2E9: 2312,
- 0xD2F1: 2313,
- 0xD2F3: 2314,
- 0xD2F5: 2315,
- 0xD2F7: 2316,
- 0xD341: 2317,
- 0xD342: 2318,
- 0xD345: 2319,
- 0xD349: 2320,
- 0xD351: 2321,
- 0xD355: 2322,
- 0xD357: 2323,
- 0xD361: 2324,
- 0xD362: 2325,
- 0xD365: 2326,
- 0xD367: 2327,
- 0xD368: 2328,
- 0xD369: 2329,
- 0xD36A: 2330,
- 0xD371: 2331,
- 0xD373: 2332,
- 0xD375: 2333,
- 0xD377: 2334,
- 0xD37B: 2335,
- 0xD381: 2336,
- 0xD385: 2337,
- 0xD389: 2338,
- 0xD391: 2339,
- 0xD393: 2340,
- 0xD397: 2341,
- 0xD3A1: 2342,
- 0xD3A2: 2343,
- 0xD3A5: 2344,
- 0xD3A9: 2345,
- 0xD3B1: 2346,
- 0xD3B3: 2347,
- 0xD3B5: 2348,
- 0xD3B7: 2349,
-}
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/johabprober.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/johabprober.py
deleted file mode 100644
index d7364ba..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/johabprober.py
+++ /dev/null
@@ -1,47 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is mozilla.org code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-from .chardistribution import JOHABDistributionAnalysis
-from .codingstatemachine import CodingStateMachine
-from .mbcharsetprober import MultiByteCharSetProber
-from .mbcssm import JOHAB_SM_MODEL
-
-
-class JOHABProber(MultiByteCharSetProber):
- def __init__(self) -> None:
- super().__init__()
- self.coding_sm = CodingStateMachine(JOHAB_SM_MODEL)
- self.distribution_analyzer = JOHABDistributionAnalysis()
- self.reset()
-
- @property
- def charset_name(self) -> str:
- return "Johab"
-
- @property
- def language(self) -> str:
- return "Korean"
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/jpcntx.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/jpcntx.py
deleted file mode 100644
index 2f53bdd..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/jpcntx.py
+++ /dev/null
@@ -1,238 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is Mozilla Communicator client code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-from typing import List, Tuple, Union
-
-# This is hiragana 2-char sequence table, the number in each cell represents its frequency category
-# fmt: off
-jp2_char_context = (
- (0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1),
- (2, 4, 0, 4, 0, 3, 0, 4, 0, 3, 4, 4, 4, 2, 4, 3, 3, 4, 3, 2, 3, 3, 4, 2, 3, 3, 3, 2, 4, 1, 4, 3, 3, 1, 5, 4, 3, 4, 3, 4, 3, 5, 3, 0, 3, 5, 4, 2, 0, 3, 1, 0, 3, 3, 0, 3, 3, 0, 1, 1, 0, 4, 3, 0, 3, 3, 0, 4, 0, 2, 0, 3, 5, 5, 5, 5, 4, 0, 4, 1, 0, 3, 4),
- (0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2),
- (0, 4, 0, 5, 0, 5, 0, 4, 0, 4, 5, 4, 4, 3, 5, 3, 5, 1, 5, 3, 4, 3, 4, 4, 3, 4, 3, 3, 4, 3, 5, 4, 4, 3, 5, 5, 3, 5, 5, 5, 3, 5, 5, 3, 4, 5, 5, 3, 1, 3, 2, 0, 3, 4, 0, 4, 2, 0, 4, 2, 1, 5, 3, 2, 3, 5, 0, 4, 0, 2, 0, 5, 4, 4, 5, 4, 5, 0, 4, 0, 0, 4, 4),
- (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
- (0, 3, 0, 4, 0, 3, 0, 3, 0, 4, 5, 4, 3, 3, 3, 3, 4, 3, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 4, 4, 4, 4, 5, 3, 4, 4, 3, 4, 5, 5, 4, 5, 5, 1, 4, 5, 4, 3, 0, 3, 3, 1, 3, 3, 0, 4, 4, 0, 3, 3, 1, 5, 3, 3, 3, 5, 0, 4, 0, 3, 0, 4, 4, 3, 4, 3, 3, 0, 4, 1, 1, 3, 4),
- (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
- (0, 4, 0, 3, 0, 3, 0, 4, 0, 3, 4, 4, 3, 2, 2, 1, 2, 1, 3, 1, 3, 3, 3, 3, 3, 4, 3, 1, 3, 3, 5, 3, 3, 0, 4, 3, 0, 5, 4, 3, 3, 5, 4, 4, 3, 4, 4, 5, 0, 1, 2, 0, 1, 2, 0, 2, 2, 0, 1, 0, 0, 5, 2, 2, 1, 4, 0, 3, 0, 1, 0, 4, 4, 3, 5, 4, 3, 0, 2, 1, 0, 4, 3),
- (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
- (0, 3, 0, 5, 0, 4, 0, 2, 1, 4, 4, 2, 4, 1, 4, 2, 4, 2, 4, 3, 3, 3, 4, 3, 3, 3, 3, 1, 4, 2, 3, 3, 3, 1, 4, 4, 1, 1, 1, 4, 3, 3, 2, 0, 2, 4, 3, 2, 0, 3, 3, 0, 3, 1, 1, 0, 0, 0, 3, 3, 0, 4, 2, 2, 3, 4, 0, 4, 0, 3, 0, 4, 4, 5, 3, 4, 4, 0, 3, 0, 0, 1, 4),
- (1, 4, 0, 4, 0, 4, 0, 4, 0, 3, 5, 4, 4, 3, 4, 3, 5, 4, 3, 3, 4, 3, 5, 4, 4, 4, 4, 3, 4, 2, 4, 3, 3, 1, 5, 4, 3, 2, 4, 5, 4, 5, 5, 4, 4, 5, 4, 4, 0, 3, 2, 2, 3, 3, 0, 4, 3, 1, 3, 2, 1, 4, 3, 3, 4, 5, 0, 3, 0, 2, 0, 4, 5, 5, 4, 5, 4, 0, 4, 0, 0, 5, 4),
- (0, 5, 0, 5, 0, 4, 0, 3, 0, 4, 4, 3, 4, 3, 3, 3, 4, 0, 4, 4, 4, 3, 4, 3, 4, 3, 3, 1, 4, 2, 4, 3, 4, 0, 5, 4, 1, 4, 5, 4, 4, 5, 3, 2, 4, 3, 4, 3, 2, 4, 1, 3, 3, 3, 2, 3, 2, 0, 4, 3, 3, 4, 3, 3, 3, 4, 0, 4, 0, 3, 0, 4, 5, 4, 4, 4, 3, 0, 4, 1, 0, 1, 3),
- (0, 3, 1, 4, 0, 3, 0, 2, 0, 3, 4, 4, 3, 1, 4, 2, 3, 3, 4, 3, 4, 3, 4, 3, 4, 4, 3, 2, 3, 1, 5, 4, 4, 1, 4, 4, 3, 5, 4, 4, 3, 5, 5, 4, 3, 4, 4, 3, 1, 2, 3, 1, 2, 2, 0, 3, 2, 0, 3, 1, 0, 5, 3, 3, 3, 4, 3, 3, 3, 3, 4, 4, 4, 4, 5, 4, 2, 0, 3, 3, 2, 4, 3),
- (0, 2, 0, 3, 0, 1, 0, 1, 0, 0, 3, 2, 0, 0, 2, 0, 1, 0, 2, 1, 3, 3, 3, 1, 2, 3, 1, 0, 1, 0, 4, 2, 1, 1, 3, 3, 0, 4, 3, 3, 1, 4, 3, 3, 0, 3, 3, 2, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 4, 1, 0, 2, 3, 2, 2, 2, 1, 3, 3, 3, 4, 4, 3, 2, 0, 3, 1, 0, 3, 3),
- (0, 4, 0, 4, 0, 3, 0, 3, 0, 4, 4, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 2, 4, 3, 4, 3, 3, 2, 4, 3, 4, 5, 4, 1, 4, 5, 3, 5, 4, 5, 3, 5, 4, 0, 3, 5, 5, 3, 1, 3, 3, 2, 2, 3, 0, 3, 4, 1, 3, 3, 2, 4, 3, 3, 3, 4, 0, 4, 0, 3, 0, 4, 5, 4, 4, 5, 3, 0, 4, 1, 0, 3, 4),
- (0, 2, 0, 3, 0, 3, 0, 0, 0, 2, 2, 2, 1, 0, 1, 0, 0, 0, 3, 0, 3, 0, 3, 0, 1, 3, 1, 0, 3, 1, 3, 3, 3, 1, 3, 3, 3, 0, 1, 3, 1, 3, 4, 0, 0, 3, 1, 1, 0, 3, 2, 0, 0, 0, 0, 1, 3, 0, 1, 0, 0, 3, 3, 2, 0, 3, 0, 0, 0, 0, 0, 3, 4, 3, 4, 3, 3, 0, 3, 0, 0, 2, 3),
- (2, 3, 0, 3, 0, 2, 0, 1, 0, 3, 3, 4, 3, 1, 3, 1, 1, 1, 3, 1, 4, 3, 4, 3, 3, 3, 0, 0, 3, 1, 5, 4, 3, 1, 4, 3, 2, 5, 5, 4, 4, 4, 4, 3, 3, 4, 4, 4, 0, 2, 1, 1, 3, 2, 0, 1, 2, 0, 0, 1, 0, 4, 1, 3, 3, 3, 0, 3, 0, 1, 0, 4, 4, 4, 5, 5, 3, 0, 2, 0, 0, 4, 4),
- (0, 2, 0, 1, 0, 3, 1, 3, 0, 2, 3, 3, 3, 0, 3, 1, 0, 0, 3, 0, 3, 2, 3, 1, 3, 2, 1, 1, 0, 0, 4, 2, 1, 0, 2, 3, 1, 4, 3, 2, 0, 4, 4, 3, 1, 3, 1, 3, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 4, 1, 1, 1, 2, 0, 3, 0, 0, 0, 3, 4, 2, 4, 3, 2, 0, 1, 0, 0, 3, 3),
- (0, 1, 0, 4, 0, 5, 0, 4, 0, 2, 4, 4, 2, 3, 3, 2, 3, 3, 5, 3, 3, 3, 4, 3, 4, 2, 3, 0, 4, 3, 3, 3, 4, 1, 4, 3, 2, 1, 5, 5, 3, 4, 5, 1, 3, 5, 4, 2, 0, 3, 3, 0, 1, 3, 0, 4, 2, 0, 1, 3, 1, 4, 3, 3, 3, 3, 0, 3, 0, 1, 0, 3, 4, 4, 4, 5, 5, 0, 3, 0, 1, 4, 5),
- (0, 2, 0, 3, 0, 3, 0, 0, 0, 2, 3, 1, 3, 0, 4, 0, 1, 1, 3, 0, 3, 4, 3, 2, 3, 1, 0, 3, 3, 2, 3, 1, 3, 0, 2, 3, 0, 2, 1, 4, 1, 2, 2, 0, 0, 3, 3, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 2, 2, 0, 3, 2, 1, 3, 3, 0, 2, 0, 2, 0, 0, 3, 3, 1, 2, 4, 0, 3, 0, 2, 2, 3),
- (2, 4, 0, 5, 0, 4, 0, 4, 0, 2, 4, 4, 4, 3, 4, 3, 3, 3, 1, 2, 4, 3, 4, 3, 4, 4, 5, 0, 3, 3, 3, 3, 2, 0, 4, 3, 1, 4, 3, 4, 1, 4, 4, 3, 3, 4, 4, 3, 1, 2, 3, 0, 4, 2, 0, 4, 1, 0, 3, 3, 0, 4, 3, 3, 3, 4, 0, 4, 0, 2, 0, 3, 5, 3, 4, 5, 2, 0, 3, 0, 0, 4, 5),
- (0, 3, 0, 4, 0, 1, 0, 1, 0, 1, 3, 2, 2, 1, 3, 0, 3, 0, 2, 0, 2, 0, 3, 0, 2, 0, 0, 0, 1, 0, 1, 1, 0, 0, 3, 1, 0, 0, 0, 4, 0, 3, 1, 0, 2, 1, 3, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 2, 2, 3, 1, 0, 3, 0, 0, 0, 1, 4, 4, 4, 3, 0, 0, 4, 0, 0, 1, 4),
- (1, 4, 1, 5, 0, 3, 0, 3, 0, 4, 5, 4, 4, 3, 5, 3, 3, 4, 4, 3, 4, 1, 3, 3, 3, 3, 2, 1, 4, 1, 5, 4, 3, 1, 4, 4, 3, 5, 4, 4, 3, 5, 4, 3, 3, 4, 4, 4, 0, 3, 3, 1, 2, 3, 0, 3, 1, 0, 3, 3, 0, 5, 4, 4, 4, 4, 4, 4, 3, 3, 5, 4, 4, 3, 3, 5, 4, 0, 3, 2, 0, 4, 4),
- (0, 2, 0, 3, 0, 1, 0, 0, 0, 1, 3, 3, 3, 2, 4, 1, 3, 0, 3, 1, 3, 0, 2, 2, 1, 1, 0, 0, 2, 0, 4, 3, 1, 0, 4, 3, 0, 4, 4, 4, 1, 4, 3, 1, 1, 3, 3, 1, 0, 2, 0, 0, 1, 3, 0, 0, 0, 0, 2, 0, 0, 4, 3, 2, 4, 3, 5, 4, 3, 3, 3, 4, 3, 3, 4, 3, 3, 0, 2, 1, 0, 3, 3),
- (0, 2, 0, 4, 0, 3, 0, 2, 0, 2, 5, 5, 3, 4, 4, 4, 4, 1, 4, 3, 3, 0, 4, 3, 4, 3, 1, 3, 3, 2, 4, 3, 0, 3, 4, 3, 0, 3, 4, 4, 2, 4, 4, 0, 4, 5, 3, 3, 2, 2, 1, 1, 1, 2, 0, 1, 5, 0, 3, 3, 2, 4, 3, 3, 3, 4, 0, 3, 0, 2, 0, 4, 4, 3, 5, 5, 0, 0, 3, 0, 2, 3, 3),
- (0, 3, 0, 4, 0, 3, 0, 1, 0, 3, 4, 3, 3, 1, 3, 3, 3, 0, 3, 1, 3, 0, 4, 3, 3, 1, 1, 0, 3, 0, 3, 3, 0, 0, 4, 4, 0, 1, 5, 4, 3, 3, 5, 0, 3, 3, 4, 3, 0, 2, 0, 1, 1, 1, 0, 1, 3, 0, 1, 2, 1, 3, 3, 2, 3, 3, 0, 3, 0, 1, 0, 1, 3, 3, 4, 4, 1, 0, 1, 2, 2, 1, 3),
- (0, 1, 0, 4, 0, 4, 0, 3, 0, 1, 3, 3, 3, 2, 3, 1, 1, 0, 3, 0, 3, 3, 4, 3, 2, 4, 2, 0, 1, 0, 4, 3, 2, 0, 4, 3, 0, 5, 3, 3, 2, 4, 4, 4, 3, 3, 3, 4, 0, 1, 3, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 4, 2, 3, 3, 3, 0, 3, 0, 0, 0, 4, 4, 4, 5, 3, 2, 0, 3, 3, 0, 3, 5),
- (0, 2, 0, 3, 0, 0, 0, 3, 0, 1, 3, 0, 2, 0, 0, 0, 1, 0, 3, 1, 1, 3, 3, 0, 0, 3, 0, 0, 3, 0, 2, 3, 1, 0, 3, 1, 0, 3, 3, 2, 0, 4, 2, 2, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 0, 1, 0, 1, 0, 0, 0, 1, 3, 1, 2, 0, 0, 0, 1, 0, 0, 1, 4),
- (0, 3, 0, 3, 0, 5, 0, 1, 0, 2, 4, 3, 1, 3, 3, 2, 1, 1, 5, 2, 1, 0, 5, 1, 2, 0, 0, 0, 3, 3, 2, 2, 3, 2, 4, 3, 0, 0, 3, 3, 1, 3, 3, 0, 2, 5, 3, 4, 0, 3, 3, 0, 1, 2, 0, 2, 2, 0, 3, 2, 0, 2, 2, 3, 3, 3, 0, 2, 0, 1, 0, 3, 4, 4, 2, 5, 4, 0, 3, 0, 0, 3, 5),
- (0, 3, 0, 3, 0, 3, 0, 1, 0, 3, 3, 3, 3, 0, 3, 0, 2, 0, 2, 1, 1, 0, 2, 0, 1, 0, 0, 0, 2, 1, 0, 0, 1, 0, 3, 2, 0, 0, 3, 3, 1, 2, 3, 1, 0, 3, 3, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 3, 1, 2, 3, 0, 3, 0, 1, 0, 3, 2, 1, 0, 4, 3, 0, 1, 1, 0, 3, 3),
- (0, 4, 0, 5, 0, 3, 0, 3, 0, 4, 5, 5, 4, 3, 5, 3, 4, 3, 5, 3, 3, 2, 5, 3, 4, 4, 4, 3, 4, 3, 4, 5, 5, 3, 4, 4, 3, 4, 4, 5, 4, 4, 4, 3, 4, 5, 5, 4, 2, 3, 4, 2, 3, 4, 0, 3, 3, 1, 4, 3, 2, 4, 3, 3, 5, 5, 0, 3, 0, 3, 0, 5, 5, 5, 5, 4, 4, 0, 4, 0, 1, 4, 4),
- (0, 4, 0, 4, 0, 3, 0, 3, 0, 3, 5, 4, 4, 2, 3, 2, 5, 1, 3, 2, 5, 1, 4, 2, 3, 2, 3, 3, 4, 3, 3, 3, 3, 2, 5, 4, 1, 3, 3, 5, 3, 4, 4, 0, 4, 4, 3, 1, 1, 3, 1, 0, 2, 3, 0, 2, 3, 0, 3, 0, 0, 4, 3, 1, 3, 4, 0, 3, 0, 2, 0, 4, 4, 4, 3, 4, 5, 0, 4, 0, 0, 3, 4),
- (0, 3, 0, 3, 0, 3, 1, 2, 0, 3, 4, 4, 3, 3, 3, 0, 2, 2, 4, 3, 3, 1, 3, 3, 3, 1, 1, 0, 3, 1, 4, 3, 2, 3, 4, 4, 2, 4, 4, 4, 3, 4, 4, 3, 2, 4, 4, 3, 1, 3, 3, 1, 3, 3, 0, 4, 1, 0, 2, 2, 1, 4, 3, 2, 3, 3, 5, 4, 3, 3, 5, 4, 4, 3, 3, 0, 4, 0, 3, 2, 2, 4, 4),
- (0, 2, 0, 1, 0, 0, 0, 0, 0, 1, 2, 1, 3, 0, 0, 0, 0, 0, 2, 0, 1, 2, 1, 0, 0, 1, 0, 0, 0, 0, 3, 0, 0, 1, 0, 1, 1, 3, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 0, 3, 4, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1),
- (0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 4, 0, 4, 1, 4, 0, 3, 0, 4, 0, 3, 0, 4, 0, 3, 0, 3, 0, 4, 1, 5, 1, 4, 0, 0, 3, 0, 5, 0, 5, 2, 0, 1, 0, 0, 0, 2, 1, 4, 0, 1, 3, 0, 0, 3, 0, 0, 3, 1, 1, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0),
- (1, 4, 0, 5, 0, 3, 0, 2, 0, 3, 5, 4, 4, 3, 4, 3, 5, 3, 4, 3, 3, 0, 4, 3, 3, 3, 3, 3, 3, 2, 4, 4, 3, 1, 3, 4, 4, 5, 4, 4, 3, 4, 4, 1, 3, 5, 4, 3, 3, 3, 1, 2, 2, 3, 3, 1, 3, 1, 3, 3, 3, 5, 3, 3, 4, 5, 0, 3, 0, 3, 0, 3, 4, 3, 4, 4, 3, 0, 3, 0, 2, 4, 3),
- (0, 1, 0, 4, 0, 0, 0, 0, 0, 1, 4, 0, 4, 1, 4, 2, 4, 0, 3, 0, 1, 0, 1, 0, 0, 0, 0, 0, 2, 0, 3, 1, 1, 1, 0, 3, 0, 0, 0, 1, 2, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 3, 0, 0, 0, 0, 3, 2, 0, 2, 2, 0, 1, 0, 0, 0, 2, 3, 2, 3, 3, 0, 0, 0, 0, 2, 1, 0),
- (0, 5, 1, 5, 0, 3, 0, 3, 0, 5, 4, 4, 5, 1, 5, 3, 3, 0, 4, 3, 4, 3, 5, 3, 4, 3, 3, 2, 4, 3, 4, 3, 3, 0, 3, 3, 1, 4, 4, 3, 4, 4, 4, 3, 4, 5, 5, 3, 2, 3, 1, 1, 3, 3, 1, 3, 1, 1, 3, 3, 2, 4, 5, 3, 3, 5, 0, 4, 0, 3, 0, 4, 4, 3, 5, 3, 3, 0, 3, 4, 0, 4, 3),
- (0, 5, 0, 5, 0, 3, 0, 2, 0, 4, 4, 3, 5, 2, 4, 3, 3, 3, 4, 4, 4, 3, 5, 3, 5, 3, 3, 1, 4, 0, 4, 3, 3, 0, 3, 3, 0, 4, 4, 4, 4, 5, 4, 3, 3, 5, 5, 3, 2, 3, 1, 2, 3, 2, 0, 1, 0, 0, 3, 2, 2, 4, 4, 3, 1, 5, 0, 4, 0, 3, 0, 4, 3, 1, 3, 2, 1, 0, 3, 3, 0, 3, 3),
- (0, 4, 0, 5, 0, 5, 0, 4, 0, 4, 5, 5, 5, 3, 4, 3, 3, 2, 5, 4, 4, 3, 5, 3, 5, 3, 4, 0, 4, 3, 4, 4, 3, 2, 4, 4, 3, 4, 5, 4, 4, 5, 5, 0, 3, 5, 5, 4, 1, 3, 3, 2, 3, 3, 1, 3, 1, 0, 4, 3, 1, 4, 4, 3, 4, 5, 0, 4, 0, 2, 0, 4, 3, 4, 4, 3, 3, 0, 4, 0, 0, 5, 5),
- (0, 4, 0, 4, 0, 5, 0, 1, 1, 3, 3, 4, 4, 3, 4, 1, 3, 0, 5, 1, 3, 0, 3, 1, 3, 1, 1, 0, 3, 0, 3, 3, 4, 0, 4, 3, 0, 4, 4, 4, 3, 4, 4, 0, 3, 5, 4, 1, 0, 3, 0, 0, 2, 3, 0, 3, 1, 0, 3, 1, 0, 3, 2, 1, 3, 5, 0, 3, 0, 1, 0, 3, 2, 3, 3, 4, 4, 0, 2, 2, 0, 4, 4),
- (2, 4, 0, 5, 0, 4, 0, 3, 0, 4, 5, 5, 4, 3, 5, 3, 5, 3, 5, 3, 5, 2, 5, 3, 4, 3, 3, 4, 3, 4, 5, 3, 2, 1, 5, 4, 3, 2, 3, 4, 5, 3, 4, 1, 2, 5, 4, 3, 0, 3, 3, 0, 3, 2, 0, 2, 3, 0, 4, 1, 0, 3, 4, 3, 3, 5, 0, 3, 0, 1, 0, 4, 5, 5, 5, 4, 3, 0, 4, 2, 0, 3, 5),
- (0, 5, 0, 4, 0, 4, 0, 2, 0, 5, 4, 3, 4, 3, 4, 3, 3, 3, 4, 3, 4, 2, 5, 3, 5, 3, 4, 1, 4, 3, 4, 4, 4, 0, 3, 5, 0, 4, 4, 4, 4, 5, 3, 1, 3, 4, 5, 3, 3, 3, 3, 3, 3, 3, 0, 2, 2, 0, 3, 3, 2, 4, 3, 3, 3, 5, 3, 4, 1, 3, 3, 5, 3, 2, 0, 0, 0, 0, 4, 3, 1, 3, 3),
- (0, 1, 0, 3, 0, 3, 0, 1, 0, 1, 3, 3, 3, 2, 3, 3, 3, 0, 3, 0, 0, 0, 3, 1, 3, 0, 0, 0, 2, 2, 2, 3, 0, 0, 3, 2, 0, 1, 2, 4, 1, 3, 3, 0, 0, 3, 3, 3, 0, 1, 0, 0, 2, 1, 0, 0, 3, 0, 3, 1, 0, 3, 0, 0, 1, 3, 0, 2, 0, 1, 0, 3, 3, 1, 3, 3, 0, 0, 1, 1, 0, 3, 3),
- (0, 2, 0, 3, 0, 2, 1, 4, 0, 2, 2, 3, 1, 1, 3, 1, 1, 0, 2, 0, 3, 1, 2, 3, 1, 3, 0, 0, 1, 0, 4, 3, 2, 3, 3, 3, 1, 4, 2, 3, 3, 3, 3, 1, 0, 3, 1, 4, 0, 1, 1, 0, 1, 2, 0, 1, 1, 0, 1, 1, 0, 3, 1, 3, 2, 2, 0, 1, 0, 0, 0, 2, 3, 3, 3, 1, 0, 0, 0, 0, 0, 2, 3),
- (0, 5, 0, 4, 0, 5, 0, 2, 0, 4, 5, 5, 3, 3, 4, 3, 3, 1, 5, 4, 4, 2, 4, 4, 4, 3, 4, 2, 4, 3, 5, 5, 4, 3, 3, 4, 3, 3, 5, 5, 4, 5, 5, 1, 3, 4, 5, 3, 1, 4, 3, 1, 3, 3, 0, 3, 3, 1, 4, 3, 1, 4, 5, 3, 3, 5, 0, 4, 0, 3, 0, 5, 3, 3, 1, 4, 3, 0, 4, 0, 1, 5, 3),
- (0, 5, 0, 5, 0, 4, 0, 2, 0, 4, 4, 3, 4, 3, 3, 3, 3, 3, 5, 4, 4, 4, 4, 4, 4, 5, 3, 3, 5, 2, 4, 4, 4, 3, 4, 4, 3, 3, 4, 4, 5, 5, 3, 3, 4, 3, 4, 3, 3, 4, 3, 3, 3, 3, 1, 2, 2, 1, 4, 3, 3, 5, 4, 4, 3, 4, 0, 4, 0, 3, 0, 4, 4, 4, 4, 4, 1, 0, 4, 2, 0, 2, 4),
- (0, 4, 0, 4, 0, 3, 0, 1, 0, 3, 5, 2, 3, 0, 3, 0, 2, 1, 4, 2, 3, 3, 4, 1, 4, 3, 3, 2, 4, 1, 3, 3, 3, 0, 3, 3, 0, 0, 3, 3, 3, 5, 3, 3, 3, 3, 3, 2, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 1, 0, 0, 3, 1, 2, 2, 3, 0, 3, 0, 2, 0, 4, 4, 3, 3, 4, 1, 0, 3, 0, 0, 2, 4),
- (0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 2, 0, 0, 0, 0, 0, 1, 0, 2, 0, 1, 0, 0, 0, 0, 0, 3, 1, 3, 0, 3, 2, 0, 0, 0, 1, 0, 3, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 0, 2, 0, 0, 0, 0, 0, 0, 2),
- (0, 2, 1, 3, 0, 2, 0, 2, 0, 3, 3, 3, 3, 1, 3, 1, 3, 3, 3, 3, 3, 3, 4, 2, 2, 1, 2, 1, 4, 0, 4, 3, 1, 3, 3, 3, 2, 4, 3, 5, 4, 3, 3, 3, 3, 3, 3, 3, 0, 1, 3, 0, 2, 0, 0, 1, 0, 0, 1, 0, 0, 4, 2, 0, 2, 3, 0, 3, 3, 0, 3, 3, 4, 2, 3, 1, 4, 0, 1, 2, 0, 2, 3),
- (0, 3, 0, 3, 0, 1, 0, 3, 0, 2, 3, 3, 3, 0, 3, 1, 2, 0, 3, 3, 2, 3, 3, 2, 3, 2, 3, 1, 3, 0, 4, 3, 2, 0, 3, 3, 1, 4, 3, 3, 2, 3, 4, 3, 1, 3, 3, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 4, 1, 1, 0, 3, 0, 3, 1, 0, 2, 3, 3, 3, 3, 3, 1, 0, 0, 2, 0, 3, 3),
- (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 3, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 2, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 3),
- (0, 2, 0, 3, 1, 3, 0, 3, 0, 2, 3, 3, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 1, 3, 0, 2, 3, 1, 1, 4, 3, 3, 2, 3, 3, 1, 2, 2, 4, 1, 3, 3, 0, 1, 4, 2, 3, 0, 1, 3, 0, 3, 0, 0, 1, 3, 0, 2, 0, 0, 3, 3, 2, 1, 3, 0, 3, 0, 2, 0, 3, 4, 4, 4, 3, 1, 0, 3, 0, 0, 3, 3),
- (0, 2, 0, 1, 0, 2, 0, 0, 0, 1, 3, 2, 2, 1, 3, 0, 1, 1, 3, 0, 3, 2, 3, 1, 2, 0, 2, 0, 1, 1, 3, 3, 3, 0, 3, 3, 1, 1, 2, 3, 2, 3, 3, 1, 2, 3, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 0, 1, 0, 0, 2, 1, 2, 1, 3, 0, 3, 0, 0, 0, 3, 4, 4, 4, 3, 2, 0, 2, 0, 0, 2, 4),
- (0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 3, 1, 0, 0, 0, 0, 0, 0, 0, 3),
- (0, 3, 0, 3, 0, 2, 0, 3, 0, 3, 3, 3, 2, 3, 2, 2, 2, 0, 3, 1, 3, 3, 3, 2, 3, 3, 0, 0, 3, 0, 3, 2, 2, 0, 2, 3, 1, 4, 3, 4, 3, 3, 2, 3, 1, 5, 4, 4, 0, 3, 1, 2, 1, 3, 0, 3, 1, 1, 2, 0, 2, 3, 1, 3, 1, 3, 0, 3, 0, 1, 0, 3, 3, 4, 4, 2, 1, 0, 2, 1, 0, 2, 4),
- (0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 4, 2, 5, 1, 4, 0, 2, 0, 2, 1, 3, 1, 4, 0, 2, 1, 0, 0, 2, 1, 4, 1, 1, 0, 3, 3, 0, 5, 1, 3, 2, 3, 3, 1, 0, 3, 2, 3, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 4, 0, 1, 0, 3, 0, 2, 0, 1, 0, 3, 3, 3, 4, 3, 3, 0, 0, 0, 0, 2, 3),
- (0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 1, 0, 0, 0, 0, 0, 3),
- (0, 1, 0, 3, 0, 4, 0, 3, 0, 2, 4, 3, 1, 0, 3, 2, 2, 1, 3, 1, 2, 2, 3, 1, 1, 1, 2, 1, 3, 0, 1, 2, 0, 1, 3, 2, 1, 3, 0, 5, 5, 1, 0, 0, 1, 3, 2, 1, 0, 3, 0, 0, 1, 0, 0, 0, 0, 0, 3, 4, 0, 1, 1, 1, 3, 2, 0, 2, 0, 1, 0, 2, 3, 3, 1, 2, 3, 0, 1, 0, 1, 0, 4),
- (0, 0, 0, 1, 0, 3, 0, 3, 0, 2, 2, 1, 0, 0, 4, 0, 3, 0, 3, 1, 3, 0, 3, 0, 3, 0, 1, 0, 3, 0, 3, 1, 3, 0, 3, 3, 0, 0, 1, 2, 1, 1, 1, 0, 1, 2, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 1, 2, 0, 0, 2, 0, 0, 0, 0, 2, 3, 3, 3, 3, 0, 0, 0, 0, 1, 4),
- (0, 0, 0, 3, 0, 3, 0, 0, 0, 0, 3, 1, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3, 0, 2, 0, 2, 3, 0, 0, 2, 2, 3, 1, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 2, 0, 0, 0, 0, 2, 3),
- (2, 4, 0, 5, 0, 5, 0, 4, 0, 3, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 4, 5, 4, 5, 5, 5, 2, 3, 0, 5, 5, 4, 1, 5, 4, 3, 1, 5, 4, 3, 4, 4, 3, 3, 4, 3, 3, 0, 3, 2, 0, 2, 3, 0, 3, 0, 0, 3, 3, 0, 5, 3, 2, 3, 3, 0, 3, 0, 3, 0, 3, 4, 5, 4, 5, 3, 0, 4, 3, 0, 3, 4),
- (0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 3, 4, 3, 2, 3, 2, 3, 0, 4, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 2, 4, 3, 3, 1, 3, 4, 3, 4, 4, 4, 3, 4, 4, 3, 2, 4, 4, 1, 0, 2, 0, 0, 1, 1, 0, 2, 0, 0, 3, 1, 0, 5, 3, 2, 1, 3, 0, 3, 0, 1, 2, 4, 3, 2, 4, 3, 3, 0, 3, 2, 0, 4, 4),
- (0, 3, 0, 3, 0, 1, 0, 0, 0, 1, 4, 3, 3, 2, 3, 1, 3, 1, 4, 2, 3, 2, 4, 2, 3, 4, 3, 0, 2, 2, 3, 3, 3, 0, 3, 3, 3, 0, 3, 4, 1, 3, 3, 0, 3, 4, 3, 3, 0, 1, 1, 0, 1, 0, 0, 0, 4, 0, 3, 0, 0, 3, 1, 2, 1, 3, 0, 4, 0, 1, 0, 4, 3, 3, 4, 3, 3, 0, 2, 0, 0, 3, 3),
- (0, 3, 0, 4, 0, 1, 0, 3, 0, 3, 4, 3, 3, 0, 3, 3, 3, 1, 3, 1, 3, 3, 4, 3, 3, 3, 0, 0, 3, 1, 5, 3, 3, 1, 3, 3, 2, 5, 4, 3, 3, 4, 5, 3, 2, 5, 3, 4, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 1, 1, 0, 4, 2, 2, 1, 3, 0, 3, 0, 2, 0, 4, 4, 3, 5, 3, 2, 0, 1, 1, 0, 3, 4),
- (0, 5, 0, 4, 0, 5, 0, 2, 0, 4, 4, 3, 3, 2, 3, 3, 3, 1, 4, 3, 4, 1, 5, 3, 4, 3, 4, 0, 4, 2, 4, 3, 4, 1, 5, 4, 0, 4, 4, 4, 4, 5, 4, 1, 3, 5, 4, 2, 1, 4, 1, 1, 3, 2, 0, 3, 1, 0, 3, 2, 1, 4, 3, 3, 3, 4, 0, 4, 0, 3, 0, 4, 4, 4, 3, 3, 3, 0, 4, 2, 0, 3, 4),
- (1, 4, 0, 4, 0, 3, 0, 1, 0, 3, 3, 3, 1, 1, 3, 3, 2, 2, 3, 3, 1, 0, 3, 2, 2, 1, 2, 0, 3, 1, 2, 1, 2, 0, 3, 2, 0, 2, 2, 3, 3, 4, 3, 0, 3, 3, 1, 2, 0, 1, 1, 3, 1, 2, 0, 0, 3, 0, 1, 1, 0, 3, 2, 2, 3, 3, 0, 3, 0, 0, 0, 2, 3, 3, 4, 3, 3, 0, 1, 0, 0, 1, 4),
- (0, 4, 0, 4, 0, 4, 0, 0, 0, 3, 4, 4, 3, 1, 4, 2, 3, 2, 3, 3, 3, 1, 4, 3, 4, 0, 3, 0, 4, 2, 3, 3, 2, 2, 5, 4, 2, 1, 3, 4, 3, 4, 3, 1, 3, 3, 4, 2, 0, 2, 1, 0, 3, 3, 0, 0, 2, 0, 3, 1, 0, 4, 4, 3, 4, 3, 0, 4, 0, 1, 0, 2, 4, 4, 4, 4, 4, 0, 3, 2, 0, 3, 3),
- (0, 0, 0, 1, 0, 4, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3, 2, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2),
- (0, 2, 0, 3, 0, 4, 0, 4, 0, 1, 3, 3, 3, 0, 4, 0, 2, 1, 2, 1, 1, 1, 2, 0, 3, 1, 1, 0, 1, 0, 3, 1, 0, 0, 3, 3, 2, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 2, 2, 0, 3, 1, 0, 0, 1, 0, 1, 1, 0, 1, 2, 0, 3, 0, 0, 0, 0, 1, 0, 0, 3, 3, 4, 3, 1, 0, 1, 0, 3, 0, 2),
- (0, 0, 0, 3, 0, 5, 0, 0, 0, 0, 1, 0, 2, 0, 3, 1, 0, 1, 3, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 4, 0, 0, 0, 2, 3, 0, 1, 4, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 3, 0, 0, 0, 0, 0, 3),
- (0, 2, 0, 5, 0, 5, 0, 1, 0, 2, 4, 3, 3, 2, 5, 1, 3, 2, 3, 3, 3, 0, 4, 1, 2, 0, 3, 0, 4, 0, 2, 2, 1, 1, 5, 3, 0, 0, 1, 4, 2, 3, 2, 0, 3, 3, 3, 2, 0, 2, 4, 1, 1, 2, 0, 1, 1, 0, 3, 1, 0, 1, 3, 1, 2, 3, 0, 2, 0, 0, 0, 1, 3, 5, 4, 4, 4, 0, 3, 0, 0, 1, 3),
- (0, 4, 0, 5, 0, 4, 0, 4, 0, 4, 5, 4, 3, 3, 4, 3, 3, 3, 4, 3, 4, 4, 5, 3, 4, 5, 4, 2, 4, 2, 3, 4, 3, 1, 4, 4, 1, 3, 5, 4, 4, 5, 5, 4, 4, 5, 5, 5, 2, 3, 3, 1, 4, 3, 1, 3, 3, 0, 3, 3, 1, 4, 3, 4, 4, 4, 0, 3, 0, 4, 0, 3, 3, 4, 4, 5, 0, 0, 4, 3, 0, 4, 5),
- (0, 4, 0, 4, 0, 3, 0, 3, 0, 3, 4, 4, 4, 3, 3, 2, 4, 3, 4, 3, 4, 3, 5, 3, 4, 3, 2, 1, 4, 2, 4, 4, 3, 1, 3, 4, 2, 4, 5, 5, 3, 4, 5, 4, 1, 5, 4, 3, 0, 3, 2, 2, 3, 2, 1, 3, 1, 0, 3, 3, 3, 5, 3, 3, 3, 5, 4, 4, 2, 3, 3, 4, 3, 3, 3, 2, 1, 0, 3, 2, 1, 4, 3),
- (0, 4, 0, 5, 0, 4, 0, 3, 0, 3, 5, 5, 3, 2, 4, 3, 4, 0, 5, 4, 4, 1, 4, 4, 4, 3, 3, 3, 4, 3, 5, 5, 2, 3, 3, 4, 1, 2, 5, 5, 3, 5, 5, 2, 3, 5, 5, 4, 0, 3, 2, 0, 3, 3, 1, 1, 5, 1, 4, 1, 0, 4, 3, 2, 3, 5, 0, 4, 0, 3, 0, 5, 4, 3, 4, 3, 0, 0, 4, 1, 0, 4, 4),
- (1, 3, 0, 4, 0, 2, 0, 2, 0, 2, 5, 5, 3, 3, 3, 3, 3, 0, 4, 2, 3, 4, 4, 4, 3, 4, 0, 0, 3, 4, 5, 4, 3, 3, 3, 3, 2, 5, 5, 4, 5, 5, 5, 4, 3, 5, 5, 5, 1, 3, 1, 0, 1, 0, 0, 3, 2, 0, 4, 2, 0, 5, 2, 3, 2, 4, 1, 3, 0, 3, 0, 4, 5, 4, 5, 4, 3, 0, 4, 2, 0, 5, 4),
- (0, 3, 0, 4, 0, 5, 0, 3, 0, 3, 4, 4, 3, 2, 3, 2, 3, 3, 3, 3, 3, 2, 4, 3, 3, 2, 2, 0, 3, 3, 3, 3, 3, 1, 3, 3, 3, 0, 4, 4, 3, 4, 4, 1, 1, 4, 4, 2, 0, 3, 1, 0, 1, 1, 0, 4, 1, 0, 2, 3, 1, 3, 3, 1, 3, 4, 0, 3, 0, 1, 0, 3, 1, 3, 0, 0, 1, 0, 2, 0, 0, 4, 4),
- (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
- (0, 3, 0, 3, 0, 2, 0, 3, 0, 1, 5, 4, 3, 3, 3, 1, 4, 2, 1, 2, 3, 4, 4, 2, 4, 4, 5, 0, 3, 1, 4, 3, 4, 0, 4, 3, 3, 3, 2, 3, 2, 5, 3, 4, 3, 2, 2, 3, 0, 0, 3, 0, 2, 1, 0, 1, 2, 0, 0, 0, 0, 2, 1, 1, 3, 1, 0, 2, 0, 4, 0, 3, 4, 4, 4, 5, 2, 0, 2, 0, 0, 1, 3),
- (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 4, 2, 1, 1, 0, 1, 0, 3, 2, 0, 0, 3, 1, 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1, 4, 0, 4, 2, 1, 0, 0, 0, 0, 0, 1),
- (0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 2, 0, 2, 1, 0, 0, 1, 2, 1, 0, 1, 1, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 1, 0, 0, 0, 0, 0, 1, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2),
- (0, 4, 0, 4, 0, 4, 0, 3, 0, 4, 4, 3, 4, 2, 4, 3, 2, 0, 4, 4, 4, 3, 5, 3, 5, 3, 3, 2, 4, 2, 4, 3, 4, 3, 1, 4, 0, 2, 3, 4, 4, 4, 3, 3, 3, 4, 4, 4, 3, 4, 1, 3, 4, 3, 2, 1, 2, 1, 3, 3, 3, 4, 4, 3, 3, 5, 0, 4, 0, 3, 0, 4, 3, 3, 3, 2, 1, 0, 3, 0, 0, 3, 3),
- (0, 4, 0, 3, 0, 3, 0, 3, 0, 3, 5, 5, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 4, 4, 3, 3, 3, 3, 4, 3, 5, 3, 3, 1, 3, 2, 4, 5, 5, 5, 5, 4, 3, 4, 5, 5, 3, 2, 2, 3, 3, 3, 3, 2, 3, 3, 1, 2, 3, 2, 4, 3, 3, 3, 4, 0, 4, 0, 2, 0, 4, 3, 2, 2, 1, 2, 0, 3, 0, 0, 4, 1),
-)
-# fmt: on
-
-
-class JapaneseContextAnalysis:
- NUM_OF_CATEGORY = 6
- DONT_KNOW = -1
- ENOUGH_REL_THRESHOLD = 100
- MAX_REL_THRESHOLD = 1000
- MINIMUM_DATA_THRESHOLD = 4
-
- def __init__(self) -> None:
- self._total_rel = 0
- self._rel_sample: List[int] = []
- self._need_to_skip_char_num = 0
- self._last_char_order = -1
- self._done = False
- self.reset()
-
- def reset(self) -> None:
- self._total_rel = 0 # total sequence received
- # category counters, each integer counts sequence in its category
- self._rel_sample = [0] * self.NUM_OF_CATEGORY
- # if last byte in current buffer is not the last byte of a character,
- # we need to know how many bytes to skip in next buffer
- self._need_to_skip_char_num = 0
- self._last_char_order = -1 # The order of previous char
- # If this flag is set to True, detection is done and conclusion has
- # been made
- self._done = False
-
- def feed(self, byte_str: Union[bytes, bytearray], num_bytes: int) -> None:
- if self._done:
- return
-
- # The buffer we got is byte oriented, and a character may span in more than one
- # buffers. In case the last one or two byte in last buffer is not
- # complete, we record how many byte needed to complete that character
- # and skip these bytes here. We can choose to record those bytes as
- # well and analyse the character once it is complete, but since a
- # character will not make much difference, by simply skipping
- # this character will simply our logic and improve performance.
- i = self._need_to_skip_char_num
- while i < num_bytes:
- order, char_len = self.get_order(byte_str[i : i + 2])
- i += char_len
- if i > num_bytes:
- self._need_to_skip_char_num = i - num_bytes
- self._last_char_order = -1
- else:
- if (order != -1) and (self._last_char_order != -1):
- self._total_rel += 1
- if self._total_rel > self.MAX_REL_THRESHOLD:
- self._done = True
- break
- self._rel_sample[
- jp2_char_context[self._last_char_order][order]
- ] += 1
- self._last_char_order = order
-
- def got_enough_data(self) -> bool:
- return self._total_rel > self.ENOUGH_REL_THRESHOLD
-
- def get_confidence(self) -> float:
- # This is just one way to calculate confidence. It works well for me.
- if self._total_rel > self.MINIMUM_DATA_THRESHOLD:
- return (self._total_rel - self._rel_sample[0]) / self._total_rel
- return self.DONT_KNOW
-
- def get_order(self, _: Union[bytes, bytearray]) -> Tuple[int, int]:
- return -1, 1
-
-
-class SJISContextAnalysis(JapaneseContextAnalysis):
- def __init__(self) -> None:
- super().__init__()
- self._charset_name = "SHIFT_JIS"
-
- @property
- def charset_name(self) -> str:
- return self._charset_name
-
- def get_order(self, byte_str: Union[bytes, bytearray]) -> Tuple[int, int]:
- if not byte_str:
- return -1, 1
- # find out current char's byte length
- first_char = byte_str[0]
- if (0x81 <= first_char <= 0x9F) or (0xE0 <= first_char <= 0xFC):
- char_len = 2
- if (first_char == 0x87) or (0xFA <= first_char <= 0xFC):
- self._charset_name = "CP932"
- else:
- char_len = 1
-
- # return its order if it is hiragana
- if len(byte_str) > 1:
- second_char = byte_str[1]
- if (first_char == 202) and (0x9F <= second_char <= 0xF1):
- return second_char - 0x9F, char_len
-
- return -1, char_len
-
-
-class EUCJPContextAnalysis(JapaneseContextAnalysis):
- def get_order(self, byte_str: Union[bytes, bytearray]) -> Tuple[int, int]:
- if not byte_str:
- return -1, 1
- # find out current char's byte length
- first_char = byte_str[0]
- if (first_char == 0x8E) or (0xA1 <= first_char <= 0xFE):
- char_len = 2
- elif first_char == 0x8F:
- char_len = 3
- else:
- char_len = 1
-
- # return its order if it is hiragana
- if len(byte_str) > 1:
- second_char = byte_str[1]
- if (first_char == 0xA4) and (0xA1 <= second_char <= 0xF3):
- return second_char - 0xA1, char_len
-
- return -1, char_len
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/langbulgarianmodel.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/langbulgarianmodel.py
deleted file mode 100644
index 2f771bb..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/langbulgarianmodel.py
+++ /dev/null
@@ -1,4649 +0,0 @@
-from chardet.sbcharsetprober import SingleByteCharSetModel
-
-# 3: Positive
-# 2: Likely
-# 1: Unlikely
-# 0: Negative
-
-BULGARIAN_LANG_MODEL = {
- 63: { # 'e'
- 63: 1, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 0, # 'а'
- 18: 1, # 'б'
- 9: 1, # 'в'
- 20: 1, # 'г'
- 11: 1, # 'д'
- 3: 1, # 'е'
- 23: 1, # 'ж'
- 15: 1, # 'з'
- 2: 0, # 'и'
- 26: 1, # 'й'
- 12: 1, # 'к'
- 10: 1, # 'л'
- 14: 1, # 'м'
- 6: 1, # 'н'
- 4: 1, # 'о'
- 13: 1, # 'п'
- 7: 1, # 'р'
- 8: 1, # 'с'
- 5: 1, # 'т'
- 19: 0, # 'у'
- 29: 1, # 'ф'
- 25: 1, # 'х'
- 22: 0, # 'ц'
- 21: 1, # 'ч'
- 27: 1, # 'ш'
- 24: 1, # 'щ'
- 17: 0, # 'ъ'
- 52: 0, # 'ь'
- 42: 0, # 'ю'
- 16: 1, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 45: { # '\xad'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 1, # 'Б'
- 35: 1, # 'В'
- 43: 0, # 'Г'
- 37: 1, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 1, # 'И'
- 59: 0, # 'Й'
- 33: 1, # 'К'
- 46: 0, # 'Л'
- 38: 1, # 'М'
- 36: 0, # 'Н'
- 41: 1, # 'О'
- 30: 1, # 'П'
- 39: 1, # 'Р'
- 28: 1, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 1, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 0, # 'а'
- 18: 0, # 'б'
- 9: 0, # 'в'
- 20: 0, # 'г'
- 11: 0, # 'д'
- 3: 0, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 0, # 'и'
- 26: 0, # 'й'
- 12: 0, # 'к'
- 10: 0, # 'л'
- 14: 0, # 'м'
- 6: 0, # 'н'
- 4: 0, # 'о'
- 13: 0, # 'п'
- 7: 0, # 'р'
- 8: 0, # 'с'
- 5: 0, # 'т'
- 19: 0, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 0, # 'ъ'
- 52: 0, # 'ь'
- 42: 0, # 'ю'
- 16: 0, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 31: { # 'А'
- 63: 0, # 'e'
- 45: 1, # '\xad'
- 31: 1, # 'А'
- 32: 1, # 'Б'
- 35: 2, # 'В'
- 43: 1, # 'Г'
- 37: 2, # 'Д'
- 44: 2, # 'Е'
- 55: 1, # 'Ж'
- 47: 2, # 'З'
- 40: 1, # 'И'
- 59: 1, # 'Й'
- 33: 1, # 'К'
- 46: 2, # 'Л'
- 38: 1, # 'М'
- 36: 2, # 'Н'
- 41: 1, # 'О'
- 30: 2, # 'П'
- 39: 2, # 'Р'
- 28: 2, # 'С'
- 34: 2, # 'Т'
- 51: 1, # 'У'
- 48: 2, # 'Ф'
- 49: 1, # 'Х'
- 53: 1, # 'Ц'
- 50: 1, # 'Ч'
- 54: 1, # 'Ш'
- 57: 2, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 1, # 'Я'
- 1: 1, # 'а'
- 18: 2, # 'б'
- 9: 2, # 'в'
- 20: 2, # 'г'
- 11: 2, # 'д'
- 3: 1, # 'е'
- 23: 1, # 'ж'
- 15: 2, # 'з'
- 2: 0, # 'и'
- 26: 2, # 'й'
- 12: 2, # 'к'
- 10: 3, # 'л'
- 14: 2, # 'м'
- 6: 3, # 'н'
- 4: 0, # 'о'
- 13: 2, # 'п'
- 7: 2, # 'р'
- 8: 2, # 'с'
- 5: 2, # 'т'
- 19: 1, # 'у'
- 29: 2, # 'ф'
- 25: 1, # 'х'
- 22: 1, # 'ц'
- 21: 1, # 'ч'
- 27: 1, # 'ш'
- 24: 0, # 'щ'
- 17: 0, # 'ъ'
- 52: 0, # 'ь'
- 42: 0, # 'ю'
- 16: 1, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 32: { # 'Б'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 2, # 'А'
- 32: 2, # 'Б'
- 35: 1, # 'В'
- 43: 1, # 'Г'
- 37: 2, # 'Д'
- 44: 1, # 'Е'
- 55: 1, # 'Ж'
- 47: 2, # 'З'
- 40: 1, # 'И'
- 59: 0, # 'Й'
- 33: 1, # 'К'
- 46: 1, # 'Л'
- 38: 1, # 'М'
- 36: 2, # 'Н'
- 41: 2, # 'О'
- 30: 1, # 'П'
- 39: 1, # 'Р'
- 28: 2, # 'С'
- 34: 2, # 'Т'
- 51: 1, # 'У'
- 48: 2, # 'Ф'
- 49: 1, # 'Х'
- 53: 1, # 'Ц'
- 50: 1, # 'Ч'
- 54: 0, # 'Ш'
- 57: 1, # 'Щ'
- 61: 2, # 'Ъ'
- 60: 1, # 'Ю'
- 56: 1, # 'Я'
- 1: 3, # 'а'
- 18: 0, # 'б'
- 9: 0, # 'в'
- 20: 0, # 'г'
- 11: 1, # 'д'
- 3: 3, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 2, # 'и'
- 26: 0, # 'й'
- 12: 0, # 'к'
- 10: 2, # 'л'
- 14: 0, # 'м'
- 6: 0, # 'н'
- 4: 3, # 'о'
- 13: 0, # 'п'
- 7: 2, # 'р'
- 8: 1, # 'с'
- 5: 0, # 'т'
- 19: 2, # 'у'
- 29: 0, # 'ф'
- 25: 1, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 3, # 'ъ'
- 52: 1, # 'ь'
- 42: 1, # 'ю'
- 16: 2, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 35: { # 'В'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 2, # 'А'
- 32: 1, # 'Б'
- 35: 1, # 'В'
- 43: 0, # 'Г'
- 37: 1, # 'Д'
- 44: 2, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 2, # 'И'
- 59: 0, # 'Й'
- 33: 1, # 'К'
- 46: 1, # 'Л'
- 38: 1, # 'М'
- 36: 1, # 'Н'
- 41: 1, # 'О'
- 30: 1, # 'П'
- 39: 2, # 'Р'
- 28: 2, # 'С'
- 34: 1, # 'Т'
- 51: 1, # 'У'
- 48: 2, # 'Ф'
- 49: 0, # 'Х'
- 53: 1, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 1, # 'Ъ'
- 60: 1, # 'Ю'
- 56: 2, # 'Я'
- 1: 3, # 'а'
- 18: 1, # 'б'
- 9: 0, # 'в'
- 20: 0, # 'г'
- 11: 1, # 'д'
- 3: 3, # 'е'
- 23: 1, # 'ж'
- 15: 2, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 1, # 'к'
- 10: 2, # 'л'
- 14: 1, # 'м'
- 6: 2, # 'н'
- 4: 2, # 'о'
- 13: 1, # 'п'
- 7: 2, # 'р'
- 8: 2, # 'с'
- 5: 2, # 'т'
- 19: 1, # 'у'
- 29: 0, # 'ф'
- 25: 1, # 'х'
- 22: 0, # 'ц'
- 21: 2, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 2, # 'ъ'
- 52: 1, # 'ь'
- 42: 1, # 'ю'
- 16: 1, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 43: { # 'Г'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 2, # 'А'
- 32: 1, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 1, # 'Д'
- 44: 2, # 'Е'
- 55: 0, # 'Ж'
- 47: 1, # 'З'
- 40: 1, # 'И'
- 59: 0, # 'Й'
- 33: 1, # 'К'
- 46: 1, # 'Л'
- 38: 0, # 'М'
- 36: 1, # 'Н'
- 41: 1, # 'О'
- 30: 0, # 'П'
- 39: 1, # 'Р'
- 28: 1, # 'С'
- 34: 0, # 'Т'
- 51: 1, # 'У'
- 48: 1, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 1, # 'Щ'
- 61: 1, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 2, # 'а'
- 18: 1, # 'б'
- 9: 1, # 'в'
- 20: 0, # 'г'
- 11: 1, # 'д'
- 3: 3, # 'е'
- 23: 1, # 'ж'
- 15: 0, # 'з'
- 2: 2, # 'и'
- 26: 0, # 'й'
- 12: 1, # 'к'
- 10: 2, # 'л'
- 14: 1, # 'м'
- 6: 1, # 'н'
- 4: 2, # 'о'
- 13: 0, # 'п'
- 7: 2, # 'р'
- 8: 0, # 'с'
- 5: 0, # 'т'
- 19: 2, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 0, # 'ш'
- 24: 1, # 'щ'
- 17: 2, # 'ъ'
- 52: 1, # 'ь'
- 42: 1, # 'ю'
- 16: 1, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 37: { # 'Д'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 2, # 'А'
- 32: 1, # 'Б'
- 35: 2, # 'В'
- 43: 1, # 'Г'
- 37: 2, # 'Д'
- 44: 2, # 'Е'
- 55: 2, # 'Ж'
- 47: 1, # 'З'
- 40: 2, # 'И'
- 59: 0, # 'Й'
- 33: 1, # 'К'
- 46: 1, # 'Л'
- 38: 1, # 'М'
- 36: 1, # 'Н'
- 41: 2, # 'О'
- 30: 2, # 'П'
- 39: 1, # 'Р'
- 28: 2, # 'С'
- 34: 1, # 'Т'
- 51: 1, # 'У'
- 48: 1, # 'Ф'
- 49: 0, # 'Х'
- 53: 1, # 'Ц'
- 50: 1, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 1, # 'Ъ'
- 60: 1, # 'Ю'
- 56: 1, # 'Я'
- 1: 3, # 'а'
- 18: 0, # 'б'
- 9: 2, # 'в'
- 20: 0, # 'г'
- 11: 0, # 'д'
- 3: 3, # 'е'
- 23: 3, # 'ж'
- 15: 1, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 0, # 'к'
- 10: 1, # 'л'
- 14: 1, # 'м'
- 6: 2, # 'н'
- 4: 3, # 'о'
- 13: 0, # 'п'
- 7: 2, # 'р'
- 8: 0, # 'с'
- 5: 0, # 'т'
- 19: 2, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 2, # 'ъ'
- 52: 1, # 'ь'
- 42: 2, # 'ю'
- 16: 1, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 44: { # 'Е'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 1, # 'А'
- 32: 1, # 'Б'
- 35: 2, # 'В'
- 43: 1, # 'Г'
- 37: 1, # 'Д'
- 44: 1, # 'Е'
- 55: 1, # 'Ж'
- 47: 1, # 'З'
- 40: 1, # 'И'
- 59: 1, # 'Й'
- 33: 2, # 'К'
- 46: 2, # 'Л'
- 38: 1, # 'М'
- 36: 2, # 'Н'
- 41: 2, # 'О'
- 30: 1, # 'П'
- 39: 2, # 'Р'
- 28: 2, # 'С'
- 34: 2, # 'Т'
- 51: 1, # 'У'
- 48: 2, # 'Ф'
- 49: 1, # 'Х'
- 53: 2, # 'Ц'
- 50: 1, # 'Ч'
- 54: 1, # 'Ш'
- 57: 1, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 1, # 'Я'
- 1: 0, # 'а'
- 18: 1, # 'б'
- 9: 2, # 'в'
- 20: 1, # 'г'
- 11: 2, # 'д'
- 3: 0, # 'е'
- 23: 1, # 'ж'
- 15: 1, # 'з'
- 2: 0, # 'и'
- 26: 1, # 'й'
- 12: 2, # 'к'
- 10: 2, # 'л'
- 14: 2, # 'м'
- 6: 2, # 'н'
- 4: 0, # 'о'
- 13: 1, # 'п'
- 7: 2, # 'р'
- 8: 2, # 'с'
- 5: 1, # 'т'
- 19: 1, # 'у'
- 29: 1, # 'ф'
- 25: 1, # 'х'
- 22: 0, # 'ц'
- 21: 1, # 'ч'
- 27: 1, # 'ш'
- 24: 1, # 'щ'
- 17: 1, # 'ъ'
- 52: 0, # 'ь'
- 42: 1, # 'ю'
- 16: 1, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 55: { # 'Ж'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 1, # 'А'
- 32: 0, # 'Б'
- 35: 1, # 'В'
- 43: 0, # 'Г'
- 37: 1, # 'Д'
- 44: 1, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 1, # 'И'
- 59: 0, # 'Й'
- 33: 1, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 1, # 'Н'
- 41: 1, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 1, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 2, # 'а'
- 18: 0, # 'б'
- 9: 0, # 'в'
- 20: 0, # 'г'
- 11: 1, # 'д'
- 3: 2, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 2, # 'и'
- 26: 0, # 'й'
- 12: 0, # 'к'
- 10: 0, # 'л'
- 14: 0, # 'м'
- 6: 0, # 'н'
- 4: 2, # 'о'
- 13: 1, # 'п'
- 7: 1, # 'р'
- 8: 0, # 'с'
- 5: 0, # 'т'
- 19: 1, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 1, # 'ъ'
- 52: 1, # 'ь'
- 42: 1, # 'ю'
- 16: 0, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 47: { # 'З'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 2, # 'А'
- 32: 1, # 'Б'
- 35: 1, # 'В'
- 43: 1, # 'Г'
- 37: 1, # 'Д'
- 44: 1, # 'Е'
- 55: 0, # 'Ж'
- 47: 1, # 'З'
- 40: 1, # 'И'
- 59: 0, # 'Й'
- 33: 1, # 'К'
- 46: 1, # 'Л'
- 38: 1, # 'М'
- 36: 2, # 'Н'
- 41: 1, # 'О'
- 30: 1, # 'П'
- 39: 1, # 'Р'
- 28: 1, # 'С'
- 34: 1, # 'Т'
- 51: 1, # 'У'
- 48: 0, # 'Ф'
- 49: 1, # 'Х'
- 53: 1, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 1, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 1, # 'Я'
- 1: 3, # 'а'
- 18: 1, # 'б'
- 9: 2, # 'в'
- 20: 1, # 'г'
- 11: 2, # 'д'
- 3: 2, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 1, # 'и'
- 26: 0, # 'й'
- 12: 0, # 'к'
- 10: 2, # 'л'
- 14: 1, # 'м'
- 6: 1, # 'н'
- 4: 1, # 'о'
- 13: 0, # 'п'
- 7: 1, # 'р'
- 8: 0, # 'с'
- 5: 0, # 'т'
- 19: 1, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 1, # 'ъ'
- 52: 0, # 'ь'
- 42: 1, # 'ю'
- 16: 0, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 40: { # 'И'
- 63: 0, # 'e'
- 45: 1, # '\xad'
- 31: 1, # 'А'
- 32: 1, # 'Б'
- 35: 1, # 'В'
- 43: 1, # 'Г'
- 37: 1, # 'Д'
- 44: 2, # 'Е'
- 55: 1, # 'Ж'
- 47: 2, # 'З'
- 40: 1, # 'И'
- 59: 1, # 'Й'
- 33: 2, # 'К'
- 46: 2, # 'Л'
- 38: 2, # 'М'
- 36: 2, # 'Н'
- 41: 1, # 'О'
- 30: 1, # 'П'
- 39: 2, # 'Р'
- 28: 2, # 'С'
- 34: 2, # 'Т'
- 51: 0, # 'У'
- 48: 1, # 'Ф'
- 49: 1, # 'Х'
- 53: 1, # 'Ц'
- 50: 1, # 'Ч'
- 54: 1, # 'Ш'
- 57: 1, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 2, # 'Я'
- 1: 1, # 'а'
- 18: 1, # 'б'
- 9: 3, # 'в'
- 20: 2, # 'г'
- 11: 1, # 'д'
- 3: 1, # 'е'
- 23: 0, # 'ж'
- 15: 3, # 'з'
- 2: 0, # 'и'
- 26: 1, # 'й'
- 12: 1, # 'к'
- 10: 2, # 'л'
- 14: 2, # 'м'
- 6: 2, # 'н'
- 4: 0, # 'о'
- 13: 1, # 'п'
- 7: 2, # 'р'
- 8: 2, # 'с'
- 5: 2, # 'т'
- 19: 0, # 'у'
- 29: 1, # 'ф'
- 25: 1, # 'х'
- 22: 1, # 'ц'
- 21: 1, # 'ч'
- 27: 1, # 'ш'
- 24: 1, # 'щ'
- 17: 0, # 'ъ'
- 52: 0, # 'ь'
- 42: 0, # 'ю'
- 16: 0, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 59: { # 'Й'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 1, # 'Д'
- 44: 1, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 1, # 'К'
- 46: 1, # 'Л'
- 38: 1, # 'М'
- 36: 1, # 'Н'
- 41: 1, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 1, # 'С'
- 34: 1, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 1, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 1, # 'Я'
- 1: 0, # 'а'
- 18: 0, # 'б'
- 9: 0, # 'в'
- 20: 0, # 'г'
- 11: 0, # 'д'
- 3: 1, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 0, # 'и'
- 26: 0, # 'й'
- 12: 0, # 'к'
- 10: 0, # 'л'
- 14: 0, # 'м'
- 6: 0, # 'н'
- 4: 2, # 'о'
- 13: 0, # 'п'
- 7: 0, # 'р'
- 8: 0, # 'с'
- 5: 0, # 'т'
- 19: 0, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 1, # 'ъ'
- 52: 0, # 'ь'
- 42: 0, # 'ю'
- 16: 0, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 33: { # 'К'
- 63: 0, # 'e'
- 45: 1, # '\xad'
- 31: 2, # 'А'
- 32: 1, # 'Б'
- 35: 1, # 'В'
- 43: 1, # 'Г'
- 37: 1, # 'Д'
- 44: 1, # 'Е'
- 55: 0, # 'Ж'
- 47: 1, # 'З'
- 40: 2, # 'И'
- 59: 0, # 'Й'
- 33: 1, # 'К'
- 46: 1, # 'Л'
- 38: 0, # 'М'
- 36: 2, # 'Н'
- 41: 2, # 'О'
- 30: 2, # 'П'
- 39: 1, # 'Р'
- 28: 2, # 'С'
- 34: 1, # 'Т'
- 51: 1, # 'У'
- 48: 1, # 'Ф'
- 49: 1, # 'Х'
- 53: 1, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 1, # 'Ъ'
- 60: 1, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 0, # 'б'
- 9: 1, # 'в'
- 20: 0, # 'г'
- 11: 0, # 'д'
- 3: 2, # 'е'
- 23: 1, # 'ж'
- 15: 0, # 'з'
- 2: 2, # 'и'
- 26: 0, # 'й'
- 12: 0, # 'к'
- 10: 2, # 'л'
- 14: 1, # 'м'
- 6: 2, # 'н'
- 4: 3, # 'о'
- 13: 0, # 'п'
- 7: 3, # 'р'
- 8: 1, # 'с'
- 5: 0, # 'т'
- 19: 2, # 'у'
- 29: 0, # 'ф'
- 25: 1, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 1, # 'ш'
- 24: 0, # 'щ'
- 17: 2, # 'ъ'
- 52: 1, # 'ь'
- 42: 2, # 'ю'
- 16: 0, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 46: { # 'Л'
- 63: 1, # 'e'
- 45: 0, # '\xad'
- 31: 2, # 'А'
- 32: 1, # 'Б'
- 35: 1, # 'В'
- 43: 2, # 'Г'
- 37: 1, # 'Д'
- 44: 2, # 'Е'
- 55: 0, # 'Ж'
- 47: 1, # 'З'
- 40: 2, # 'И'
- 59: 0, # 'Й'
- 33: 1, # 'К'
- 46: 1, # 'Л'
- 38: 0, # 'М'
- 36: 1, # 'Н'
- 41: 2, # 'О'
- 30: 1, # 'П'
- 39: 0, # 'Р'
- 28: 1, # 'С'
- 34: 1, # 'Т'
- 51: 1, # 'У'
- 48: 0, # 'Ф'
- 49: 1, # 'Х'
- 53: 1, # 'Ц'
- 50: 1, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 1, # 'Ъ'
- 60: 1, # 'Ю'
- 56: 1, # 'Я'
- 1: 2, # 'а'
- 18: 0, # 'б'
- 9: 1, # 'в'
- 20: 0, # 'г'
- 11: 0, # 'д'
- 3: 3, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 2, # 'и'
- 26: 0, # 'й'
- 12: 0, # 'к'
- 10: 0, # 'л'
- 14: 0, # 'м'
- 6: 0, # 'н'
- 4: 2, # 'о'
- 13: 0, # 'п'
- 7: 0, # 'р'
- 8: 0, # 'с'
- 5: 0, # 'т'
- 19: 2, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 1, # 'ъ'
- 52: 1, # 'ь'
- 42: 2, # 'ю'
- 16: 1, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 38: { # 'М'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 2, # 'А'
- 32: 1, # 'Б'
- 35: 2, # 'В'
- 43: 0, # 'Г'
- 37: 1, # 'Д'
- 44: 1, # 'Е'
- 55: 0, # 'Ж'
- 47: 1, # 'З'
- 40: 2, # 'И'
- 59: 0, # 'Й'
- 33: 1, # 'К'
- 46: 1, # 'Л'
- 38: 1, # 'М'
- 36: 1, # 'Н'
- 41: 2, # 'О'
- 30: 1, # 'П'
- 39: 1, # 'Р'
- 28: 2, # 'С'
- 34: 1, # 'Т'
- 51: 1, # 'У'
- 48: 1, # 'Ф'
- 49: 0, # 'Х'
- 53: 1, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 1, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 1, # 'Я'
- 1: 3, # 'а'
- 18: 0, # 'б'
- 9: 0, # 'в'
- 20: 0, # 'г'
- 11: 0, # 'д'
- 3: 3, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 0, # 'к'
- 10: 2, # 'л'
- 14: 0, # 'м'
- 6: 2, # 'н'
- 4: 3, # 'о'
- 13: 0, # 'п'
- 7: 1, # 'р'
- 8: 0, # 'с'
- 5: 0, # 'т'
- 19: 2, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 2, # 'ъ'
- 52: 1, # 'ь'
- 42: 2, # 'ю'
- 16: 1, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 36: { # 'Н'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 2, # 'А'
- 32: 2, # 'Б'
- 35: 1, # 'В'
- 43: 1, # 'Г'
- 37: 2, # 'Д'
- 44: 2, # 'Е'
- 55: 1, # 'Ж'
- 47: 1, # 'З'
- 40: 2, # 'И'
- 59: 1, # 'Й'
- 33: 2, # 'К'
- 46: 1, # 'Л'
- 38: 1, # 'М'
- 36: 1, # 'Н'
- 41: 2, # 'О'
- 30: 1, # 'П'
- 39: 1, # 'Р'
- 28: 2, # 'С'
- 34: 2, # 'Т'
- 51: 1, # 'У'
- 48: 1, # 'Ф'
- 49: 1, # 'Х'
- 53: 1, # 'Ц'
- 50: 1, # 'Ч'
- 54: 1, # 'Ш'
- 57: 0, # 'Щ'
- 61: 1, # 'Ъ'
- 60: 1, # 'Ю'
- 56: 1, # 'Я'
- 1: 3, # 'а'
- 18: 0, # 'б'
- 9: 0, # 'в'
- 20: 1, # 'г'
- 11: 0, # 'д'
- 3: 3, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 0, # 'к'
- 10: 0, # 'л'
- 14: 0, # 'м'
- 6: 0, # 'н'
- 4: 3, # 'о'
- 13: 0, # 'п'
- 7: 0, # 'р'
- 8: 0, # 'с'
- 5: 1, # 'т'
- 19: 1, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 1, # 'ш'
- 24: 0, # 'щ'
- 17: 0, # 'ъ'
- 52: 0, # 'ь'
- 42: 2, # 'ю'
- 16: 2, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 41: { # 'О'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 1, # 'А'
- 32: 1, # 'Б'
- 35: 2, # 'В'
- 43: 1, # 'Г'
- 37: 2, # 'Д'
- 44: 1, # 'Е'
- 55: 1, # 'Ж'
- 47: 1, # 'З'
- 40: 1, # 'И'
- 59: 1, # 'Й'
- 33: 2, # 'К'
- 46: 2, # 'Л'
- 38: 2, # 'М'
- 36: 2, # 'Н'
- 41: 2, # 'О'
- 30: 1, # 'П'
- 39: 2, # 'Р'
- 28: 2, # 'С'
- 34: 2, # 'Т'
- 51: 1, # 'У'
- 48: 1, # 'Ф'
- 49: 1, # 'Х'
- 53: 0, # 'Ц'
- 50: 1, # 'Ч'
- 54: 1, # 'Ш'
- 57: 1, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 1, # 'Я'
- 1: 1, # 'а'
- 18: 2, # 'б'
- 9: 2, # 'в'
- 20: 2, # 'г'
- 11: 1, # 'д'
- 3: 1, # 'е'
- 23: 1, # 'ж'
- 15: 1, # 'з'
- 2: 0, # 'и'
- 26: 1, # 'й'
- 12: 2, # 'к'
- 10: 2, # 'л'
- 14: 1, # 'м'
- 6: 1, # 'н'
- 4: 0, # 'о'
- 13: 2, # 'п'
- 7: 2, # 'р'
- 8: 2, # 'с'
- 5: 3, # 'т'
- 19: 1, # 'у'
- 29: 1, # 'ф'
- 25: 1, # 'х'
- 22: 1, # 'ц'
- 21: 2, # 'ч'
- 27: 0, # 'ш'
- 24: 2, # 'щ'
- 17: 0, # 'ъ'
- 52: 0, # 'ь'
- 42: 0, # 'ю'
- 16: 1, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 30: { # 'П'
- 63: 0, # 'e'
- 45: 1, # '\xad'
- 31: 2, # 'А'
- 32: 1, # 'Б'
- 35: 1, # 'В'
- 43: 1, # 'Г'
- 37: 1, # 'Д'
- 44: 1, # 'Е'
- 55: 0, # 'Ж'
- 47: 1, # 'З'
- 40: 2, # 'И'
- 59: 0, # 'Й'
- 33: 1, # 'К'
- 46: 1, # 'Л'
- 38: 1, # 'М'
- 36: 1, # 'Н'
- 41: 2, # 'О'
- 30: 2, # 'П'
- 39: 2, # 'Р'
- 28: 2, # 'С'
- 34: 1, # 'Т'
- 51: 2, # 'У'
- 48: 1, # 'Ф'
- 49: 0, # 'Х'
- 53: 1, # 'Ц'
- 50: 1, # 'Ч'
- 54: 1, # 'Ш'
- 57: 0, # 'Щ'
- 61: 1, # 'Ъ'
- 60: 1, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 0, # 'б'
- 9: 0, # 'в'
- 20: 0, # 'г'
- 11: 2, # 'д'
- 3: 3, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 2, # 'и'
- 26: 0, # 'й'
- 12: 1, # 'к'
- 10: 3, # 'л'
- 14: 0, # 'м'
- 6: 1, # 'н'
- 4: 3, # 'о'
- 13: 0, # 'п'
- 7: 3, # 'р'
- 8: 1, # 'с'
- 5: 1, # 'т'
- 19: 2, # 'у'
- 29: 1, # 'ф'
- 25: 1, # 'х'
- 22: 0, # 'ц'
- 21: 1, # 'ч'
- 27: 1, # 'ш'
- 24: 0, # 'щ'
- 17: 2, # 'ъ'
- 52: 1, # 'ь'
- 42: 1, # 'ю'
- 16: 1, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 39: { # 'Р'
- 63: 0, # 'e'
- 45: 1, # '\xad'
- 31: 2, # 'А'
- 32: 1, # 'Б'
- 35: 1, # 'В'
- 43: 2, # 'Г'
- 37: 2, # 'Д'
- 44: 2, # 'Е'
- 55: 0, # 'Ж'
- 47: 1, # 'З'
- 40: 2, # 'И'
- 59: 0, # 'Й'
- 33: 1, # 'К'
- 46: 0, # 'Л'
- 38: 1, # 'М'
- 36: 1, # 'Н'
- 41: 2, # 'О'
- 30: 2, # 'П'
- 39: 1, # 'Р'
- 28: 1, # 'С'
- 34: 1, # 'Т'
- 51: 1, # 'У'
- 48: 1, # 'Ф'
- 49: 1, # 'Х'
- 53: 1, # 'Ц'
- 50: 1, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 1, # 'Ъ'
- 60: 1, # 'Ю'
- 56: 1, # 'Я'
- 1: 3, # 'а'
- 18: 0, # 'б'
- 9: 0, # 'в'
- 20: 0, # 'г'
- 11: 0, # 'д'
- 3: 2, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 2, # 'и'
- 26: 0, # 'й'
- 12: 0, # 'к'
- 10: 0, # 'л'
- 14: 0, # 'м'
- 6: 1, # 'н'
- 4: 3, # 'о'
- 13: 0, # 'п'
- 7: 0, # 'р'
- 8: 1, # 'с'
- 5: 0, # 'т'
- 19: 3, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 1, # 'ъ'
- 52: 0, # 'ь'
- 42: 1, # 'ю'
- 16: 1, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 28: { # 'С'
- 63: 1, # 'e'
- 45: 0, # '\xad'
- 31: 3, # 'А'
- 32: 2, # 'Б'
- 35: 2, # 'В'
- 43: 1, # 'Г'
- 37: 2, # 'Д'
- 44: 2, # 'Е'
- 55: 1, # 'Ж'
- 47: 1, # 'З'
- 40: 2, # 'И'
- 59: 0, # 'Й'
- 33: 2, # 'К'
- 46: 1, # 'Л'
- 38: 1, # 'М'
- 36: 1, # 'Н'
- 41: 2, # 'О'
- 30: 2, # 'П'
- 39: 1, # 'Р'
- 28: 2, # 'С'
- 34: 2, # 'Т'
- 51: 1, # 'У'
- 48: 1, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 1, # 'Ъ'
- 60: 1, # 'Ю'
- 56: 1, # 'Я'
- 1: 3, # 'а'
- 18: 1, # 'б'
- 9: 2, # 'в'
- 20: 1, # 'г'
- 11: 1, # 'д'
- 3: 3, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 2, # 'к'
- 10: 3, # 'л'
- 14: 2, # 'м'
- 6: 1, # 'н'
- 4: 3, # 'о'
- 13: 3, # 'п'
- 7: 2, # 'р'
- 8: 0, # 'с'
- 5: 3, # 'т'
- 19: 2, # 'у'
- 29: 2, # 'ф'
- 25: 1, # 'х'
- 22: 1, # 'ц'
- 21: 1, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 3, # 'ъ'
- 52: 1, # 'ь'
- 42: 1, # 'ю'
- 16: 1, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 34: { # 'Т'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 2, # 'А'
- 32: 2, # 'Б'
- 35: 1, # 'В'
- 43: 0, # 'Г'
- 37: 1, # 'Д'
- 44: 2, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 2, # 'И'
- 59: 0, # 'Й'
- 33: 2, # 'К'
- 46: 1, # 'Л'
- 38: 1, # 'М'
- 36: 1, # 'Н'
- 41: 2, # 'О'
- 30: 1, # 'П'
- 39: 2, # 'Р'
- 28: 2, # 'С'
- 34: 1, # 'Т'
- 51: 1, # 'У'
- 48: 1, # 'Ф'
- 49: 0, # 'Х'
- 53: 1, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 1, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 1, # 'Я'
- 1: 3, # 'а'
- 18: 1, # 'б'
- 9: 1, # 'в'
- 20: 0, # 'г'
- 11: 0, # 'д'
- 3: 3, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 2, # 'и'
- 26: 0, # 'й'
- 12: 1, # 'к'
- 10: 1, # 'л'
- 14: 0, # 'м'
- 6: 0, # 'н'
- 4: 3, # 'о'
- 13: 0, # 'п'
- 7: 3, # 'р'
- 8: 0, # 'с'
- 5: 0, # 'т'
- 19: 2, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 2, # 'ъ'
- 52: 0, # 'ь'
- 42: 1, # 'ю'
- 16: 2, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 51: { # 'У'
- 63: 0, # 'e'
- 45: 1, # '\xad'
- 31: 1, # 'А'
- 32: 1, # 'Б'
- 35: 1, # 'В'
- 43: 1, # 'Г'
- 37: 1, # 'Д'
- 44: 2, # 'Е'
- 55: 1, # 'Ж'
- 47: 1, # 'З'
- 40: 1, # 'И'
- 59: 0, # 'Й'
- 33: 1, # 'К'
- 46: 1, # 'Л'
- 38: 1, # 'М'
- 36: 1, # 'Н'
- 41: 0, # 'О'
- 30: 1, # 'П'
- 39: 1, # 'Р'
- 28: 1, # 'С'
- 34: 2, # 'Т'
- 51: 0, # 'У'
- 48: 1, # 'Ф'
- 49: 1, # 'Х'
- 53: 1, # 'Ц'
- 50: 1, # 'Ч'
- 54: 1, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 1, # 'а'
- 18: 1, # 'б'
- 9: 2, # 'в'
- 20: 1, # 'г'
- 11: 1, # 'д'
- 3: 2, # 'е'
- 23: 1, # 'ж'
- 15: 1, # 'з'
- 2: 2, # 'и'
- 26: 1, # 'й'
- 12: 2, # 'к'
- 10: 1, # 'л'
- 14: 1, # 'м'
- 6: 2, # 'н'
- 4: 2, # 'о'
- 13: 1, # 'п'
- 7: 1, # 'р'
- 8: 2, # 'с'
- 5: 1, # 'т'
- 19: 1, # 'у'
- 29: 0, # 'ф'
- 25: 1, # 'х'
- 22: 0, # 'ц'
- 21: 2, # 'ч'
- 27: 1, # 'ш'
- 24: 0, # 'щ'
- 17: 1, # 'ъ'
- 52: 0, # 'ь'
- 42: 0, # 'ю'
- 16: 0, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 48: { # 'Ф'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 2, # 'А'
- 32: 1, # 'Б'
- 35: 1, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 1, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 2, # 'И'
- 59: 0, # 'Й'
- 33: 1, # 'К'
- 46: 1, # 'Л'
- 38: 0, # 'М'
- 36: 1, # 'Н'
- 41: 1, # 'О'
- 30: 2, # 'П'
- 39: 1, # 'Р'
- 28: 2, # 'С'
- 34: 1, # 'Т'
- 51: 1, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 2, # 'а'
- 18: 0, # 'б'
- 9: 0, # 'в'
- 20: 0, # 'г'
- 11: 0, # 'д'
- 3: 2, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 2, # 'и'
- 26: 0, # 'й'
- 12: 0, # 'к'
- 10: 2, # 'л'
- 14: 0, # 'м'
- 6: 0, # 'н'
- 4: 2, # 'о'
- 13: 0, # 'п'
- 7: 2, # 'р'
- 8: 0, # 'с'
- 5: 0, # 'т'
- 19: 1, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 1, # 'ъ'
- 52: 1, # 'ь'
- 42: 1, # 'ю'
- 16: 0, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 49: { # 'Х'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 1, # 'А'
- 32: 0, # 'Б'
- 35: 1, # 'В'
- 43: 1, # 'Г'
- 37: 1, # 'Д'
- 44: 1, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 1, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 1, # 'Л'
- 38: 1, # 'М'
- 36: 1, # 'Н'
- 41: 1, # 'О'
- 30: 1, # 'П'
- 39: 1, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 1, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 2, # 'а'
- 18: 0, # 'б'
- 9: 1, # 'в'
- 20: 0, # 'г'
- 11: 0, # 'д'
- 3: 2, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 2, # 'и'
- 26: 0, # 'й'
- 12: 0, # 'к'
- 10: 1, # 'л'
- 14: 1, # 'м'
- 6: 0, # 'н'
- 4: 2, # 'о'
- 13: 0, # 'п'
- 7: 2, # 'р'
- 8: 0, # 'с'
- 5: 0, # 'т'
- 19: 2, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 2, # 'ъ'
- 52: 1, # 'ь'
- 42: 1, # 'ю'
- 16: 0, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 53: { # 'Ц'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 1, # 'А'
- 32: 0, # 'Б'
- 35: 1, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 1, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 2, # 'И'
- 59: 0, # 'Й'
- 33: 2, # 'К'
- 46: 1, # 'Л'
- 38: 1, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 1, # 'Р'
- 28: 2, # 'С'
- 34: 0, # 'Т'
- 51: 1, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 2, # 'а'
- 18: 0, # 'б'
- 9: 2, # 'в'
- 20: 0, # 'г'
- 11: 0, # 'д'
- 3: 2, # 'е'
- 23: 0, # 'ж'
- 15: 1, # 'з'
- 2: 2, # 'и'
- 26: 0, # 'й'
- 12: 0, # 'к'
- 10: 0, # 'л'
- 14: 0, # 'м'
- 6: 0, # 'н'
- 4: 1, # 'о'
- 13: 0, # 'п'
- 7: 1, # 'р'
- 8: 0, # 'с'
- 5: 0, # 'т'
- 19: 1, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 1, # 'ъ'
- 52: 0, # 'ь'
- 42: 1, # 'ю'
- 16: 1, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 50: { # 'Ч'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 2, # 'А'
- 32: 1, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 1, # 'Е'
- 55: 0, # 'Ж'
- 47: 1, # 'З'
- 40: 1, # 'И'
- 59: 0, # 'Й'
- 33: 1, # 'К'
- 46: 1, # 'Л'
- 38: 0, # 'М'
- 36: 1, # 'Н'
- 41: 1, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 1, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 2, # 'а'
- 18: 0, # 'б'
- 9: 0, # 'в'
- 20: 0, # 'г'
- 11: 0, # 'д'
- 3: 3, # 'е'
- 23: 1, # 'ж'
- 15: 0, # 'з'
- 2: 2, # 'и'
- 26: 0, # 'й'
- 12: 0, # 'к'
- 10: 1, # 'л'
- 14: 0, # 'м'
- 6: 0, # 'н'
- 4: 2, # 'о'
- 13: 0, # 'п'
- 7: 1, # 'р'
- 8: 0, # 'с'
- 5: 0, # 'т'
- 19: 2, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 1, # 'ъ'
- 52: 1, # 'ь'
- 42: 0, # 'ю'
- 16: 0, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 54: { # 'Ш'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 1, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 1, # 'Е'
- 55: 0, # 'Ж'
- 47: 1, # 'З'
- 40: 1, # 'И'
- 59: 0, # 'Й'
- 33: 1, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 1, # 'Н'
- 41: 1, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 1, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 2, # 'а'
- 18: 0, # 'б'
- 9: 2, # 'в'
- 20: 0, # 'г'
- 11: 0, # 'д'
- 3: 2, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 2, # 'и'
- 26: 0, # 'й'
- 12: 1, # 'к'
- 10: 1, # 'л'
- 14: 1, # 'м'
- 6: 1, # 'н'
- 4: 2, # 'о'
- 13: 1, # 'п'
- 7: 1, # 'р'
- 8: 0, # 'с'
- 5: 0, # 'т'
- 19: 2, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 0, # 'ц'
- 21: 1, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 1, # 'ъ'
- 52: 1, # 'ь'
- 42: 0, # 'ю'
- 16: 0, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 57: { # 'Щ'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 1, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 1, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 1, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 1, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 2, # 'а'
- 18: 0, # 'б'
- 9: 0, # 'в'
- 20: 0, # 'г'
- 11: 0, # 'д'
- 3: 2, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 1, # 'и'
- 26: 0, # 'й'
- 12: 0, # 'к'
- 10: 0, # 'л'
- 14: 0, # 'м'
- 6: 0, # 'н'
- 4: 1, # 'о'
- 13: 0, # 'п'
- 7: 1, # 'р'
- 8: 0, # 'с'
- 5: 0, # 'т'
- 19: 1, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 1, # 'ъ'
- 52: 0, # 'ь'
- 42: 0, # 'ю'
- 16: 1, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 61: { # 'Ъ'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 1, # 'Б'
- 35: 1, # 'В'
- 43: 0, # 'Г'
- 37: 1, # 'Д'
- 44: 0, # 'Е'
- 55: 1, # 'Ж'
- 47: 1, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 1, # 'К'
- 46: 2, # 'Л'
- 38: 1, # 'М'
- 36: 1, # 'Н'
- 41: 0, # 'О'
- 30: 1, # 'П'
- 39: 2, # 'Р'
- 28: 1, # 'С'
- 34: 1, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 1, # 'Х'
- 53: 1, # 'Ц'
- 50: 1, # 'Ч'
- 54: 1, # 'Ш'
- 57: 1, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 0, # 'а'
- 18: 0, # 'б'
- 9: 0, # 'в'
- 20: 0, # 'г'
- 11: 0, # 'д'
- 3: 0, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 0, # 'и'
- 26: 0, # 'й'
- 12: 0, # 'к'
- 10: 1, # 'л'
- 14: 0, # 'м'
- 6: 1, # 'н'
- 4: 0, # 'о'
- 13: 0, # 'п'
- 7: 1, # 'р'
- 8: 0, # 'с'
- 5: 0, # 'т'
- 19: 0, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 0, # 'ъ'
- 52: 0, # 'ь'
- 42: 0, # 'ю'
- 16: 0, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 60: { # 'Ю'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 1, # 'А'
- 32: 1, # 'Б'
- 35: 0, # 'В'
- 43: 1, # 'Г'
- 37: 1, # 'Д'
- 44: 0, # 'Е'
- 55: 1, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 1, # 'К'
- 46: 1, # 'Л'
- 38: 0, # 'М'
- 36: 1, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 1, # 'Р'
- 28: 1, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 0, # 'а'
- 18: 1, # 'б'
- 9: 1, # 'в'
- 20: 2, # 'г'
- 11: 1, # 'д'
- 3: 0, # 'е'
- 23: 2, # 'ж'
- 15: 1, # 'з'
- 2: 1, # 'и'
- 26: 0, # 'й'
- 12: 1, # 'к'
- 10: 1, # 'л'
- 14: 1, # 'м'
- 6: 1, # 'н'
- 4: 0, # 'о'
- 13: 1, # 'п'
- 7: 1, # 'р'
- 8: 1, # 'с'
- 5: 1, # 'т'
- 19: 0, # 'у'
- 29: 0, # 'ф'
- 25: 1, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 0, # 'ъ'
- 52: 0, # 'ь'
- 42: 0, # 'ю'
- 16: 0, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 56: { # 'Я'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 1, # 'Б'
- 35: 1, # 'В'
- 43: 1, # 'Г'
- 37: 1, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 1, # 'К'
- 46: 1, # 'Л'
- 38: 1, # 'М'
- 36: 1, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 1, # 'С'
- 34: 2, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 0, # 'а'
- 18: 1, # 'б'
- 9: 1, # 'в'
- 20: 1, # 'г'
- 11: 1, # 'д'
- 3: 0, # 'е'
- 23: 0, # 'ж'
- 15: 1, # 'з'
- 2: 1, # 'и'
- 26: 1, # 'й'
- 12: 1, # 'к'
- 10: 1, # 'л'
- 14: 2, # 'м'
- 6: 2, # 'н'
- 4: 0, # 'о'
- 13: 2, # 'п'
- 7: 1, # 'р'
- 8: 1, # 'с'
- 5: 1, # 'т'
- 19: 0, # 'у'
- 29: 0, # 'ф'
- 25: 1, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 1, # 'ш'
- 24: 0, # 'щ'
- 17: 0, # 'ъ'
- 52: 0, # 'ь'
- 42: 1, # 'ю'
- 16: 0, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 1: { # 'а'
- 63: 1, # 'e'
- 45: 1, # '\xad'
- 31: 1, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 1, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 1, # 'а'
- 18: 3, # 'б'
- 9: 3, # 'в'
- 20: 3, # 'г'
- 11: 3, # 'д'
- 3: 3, # 'е'
- 23: 3, # 'ж'
- 15: 3, # 'з'
- 2: 3, # 'и'
- 26: 3, # 'й'
- 12: 3, # 'к'
- 10: 3, # 'л'
- 14: 3, # 'м'
- 6: 3, # 'н'
- 4: 2, # 'о'
- 13: 3, # 'п'
- 7: 3, # 'р'
- 8: 3, # 'с'
- 5: 3, # 'т'
- 19: 3, # 'у'
- 29: 3, # 'ф'
- 25: 3, # 'х'
- 22: 3, # 'ц'
- 21: 3, # 'ч'
- 27: 3, # 'ш'
- 24: 3, # 'щ'
- 17: 0, # 'ъ'
- 52: 0, # 'ь'
- 42: 1, # 'ю'
- 16: 3, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 18: { # 'б'
- 63: 1, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 0, # 'б'
- 9: 3, # 'в'
- 20: 1, # 'г'
- 11: 2, # 'д'
- 3: 3, # 'е'
- 23: 1, # 'ж'
- 15: 1, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 1, # 'к'
- 10: 3, # 'л'
- 14: 2, # 'м'
- 6: 3, # 'н'
- 4: 3, # 'о'
- 13: 1, # 'п'
- 7: 3, # 'р'
- 8: 3, # 'с'
- 5: 0, # 'т'
- 19: 3, # 'у'
- 29: 0, # 'ф'
- 25: 2, # 'х'
- 22: 1, # 'ц'
- 21: 1, # 'ч'
- 27: 1, # 'ш'
- 24: 3, # 'щ'
- 17: 3, # 'ъ'
- 52: 1, # 'ь'
- 42: 2, # 'ю'
- 16: 3, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 9: { # 'в'
- 63: 1, # 'e'
- 45: 1, # '\xad'
- 31: 0, # 'А'
- 32: 1, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 1, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 1, # 'б'
- 9: 0, # 'в'
- 20: 2, # 'г'
- 11: 3, # 'д'
- 3: 3, # 'е'
- 23: 1, # 'ж'
- 15: 3, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 3, # 'к'
- 10: 3, # 'л'
- 14: 2, # 'м'
- 6: 3, # 'н'
- 4: 3, # 'о'
- 13: 2, # 'п'
- 7: 3, # 'р'
- 8: 3, # 'с'
- 5: 3, # 'т'
- 19: 2, # 'у'
- 29: 0, # 'ф'
- 25: 2, # 'х'
- 22: 2, # 'ц'
- 21: 3, # 'ч'
- 27: 2, # 'ш'
- 24: 1, # 'щ'
- 17: 3, # 'ъ'
- 52: 1, # 'ь'
- 42: 2, # 'ю'
- 16: 3, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 20: { # 'г'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 1, # 'б'
- 9: 2, # 'в'
- 20: 1, # 'г'
- 11: 2, # 'д'
- 3: 3, # 'е'
- 23: 0, # 'ж'
- 15: 1, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 1, # 'к'
- 10: 3, # 'л'
- 14: 1, # 'м'
- 6: 3, # 'н'
- 4: 3, # 'о'
- 13: 1, # 'п'
- 7: 3, # 'р'
- 8: 2, # 'с'
- 5: 2, # 'т'
- 19: 3, # 'у'
- 29: 1, # 'ф'
- 25: 1, # 'х'
- 22: 0, # 'ц'
- 21: 1, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 3, # 'ъ'
- 52: 1, # 'ь'
- 42: 1, # 'ю'
- 16: 1, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 11: { # 'д'
- 63: 1, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 2, # 'б'
- 9: 3, # 'в'
- 20: 2, # 'г'
- 11: 2, # 'д'
- 3: 3, # 'е'
- 23: 3, # 'ж'
- 15: 2, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 3, # 'к'
- 10: 3, # 'л'
- 14: 3, # 'м'
- 6: 3, # 'н'
- 4: 3, # 'о'
- 13: 3, # 'п'
- 7: 3, # 'р'
- 8: 3, # 'с'
- 5: 1, # 'т'
- 19: 3, # 'у'
- 29: 1, # 'ф'
- 25: 2, # 'х'
- 22: 2, # 'ц'
- 21: 2, # 'ч'
- 27: 1, # 'ш'
- 24: 1, # 'щ'
- 17: 3, # 'ъ'
- 52: 1, # 'ь'
- 42: 1, # 'ю'
- 16: 3, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 3: { # 'е'
- 63: 0, # 'e'
- 45: 1, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 2, # 'а'
- 18: 3, # 'б'
- 9: 3, # 'в'
- 20: 3, # 'г'
- 11: 3, # 'д'
- 3: 2, # 'е'
- 23: 3, # 'ж'
- 15: 3, # 'з'
- 2: 2, # 'и'
- 26: 3, # 'й'
- 12: 3, # 'к'
- 10: 3, # 'л'
- 14: 3, # 'м'
- 6: 3, # 'н'
- 4: 3, # 'о'
- 13: 3, # 'п'
- 7: 3, # 'р'
- 8: 3, # 'с'
- 5: 3, # 'т'
- 19: 2, # 'у'
- 29: 3, # 'ф'
- 25: 3, # 'х'
- 22: 3, # 'ц'
- 21: 3, # 'ч'
- 27: 3, # 'ш'
- 24: 3, # 'щ'
- 17: 1, # 'ъ'
- 52: 0, # 'ь'
- 42: 1, # 'ю'
- 16: 3, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 23: { # 'ж'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 3, # 'б'
- 9: 2, # 'в'
- 20: 1, # 'г'
- 11: 3, # 'д'
- 3: 3, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 2, # 'к'
- 10: 1, # 'л'
- 14: 1, # 'м'
- 6: 3, # 'н'
- 4: 2, # 'о'
- 13: 1, # 'п'
- 7: 1, # 'р'
- 8: 1, # 'с'
- 5: 1, # 'т'
- 19: 2, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 1, # 'ц'
- 21: 1, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 2, # 'ъ'
- 52: 0, # 'ь'
- 42: 0, # 'ю'
- 16: 1, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 15: { # 'з'
- 63: 1, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 3, # 'б'
- 9: 3, # 'в'
- 20: 3, # 'г'
- 11: 3, # 'д'
- 3: 3, # 'е'
- 23: 1, # 'ж'
- 15: 1, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 3, # 'к'
- 10: 3, # 'л'
- 14: 3, # 'м'
- 6: 3, # 'н'
- 4: 3, # 'о'
- 13: 3, # 'п'
- 7: 3, # 'р'
- 8: 3, # 'с'
- 5: 3, # 'т'
- 19: 3, # 'у'
- 29: 1, # 'ф'
- 25: 2, # 'х'
- 22: 2, # 'ц'
- 21: 2, # 'ч'
- 27: 2, # 'ш'
- 24: 1, # 'щ'
- 17: 2, # 'ъ'
- 52: 1, # 'ь'
- 42: 1, # 'ю'
- 16: 2, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 2: { # 'и'
- 63: 1, # 'e'
- 45: 1, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 1, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 1, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 1, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 1, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 3, # 'б'
- 9: 3, # 'в'
- 20: 3, # 'г'
- 11: 3, # 'д'
- 3: 3, # 'е'
- 23: 3, # 'ж'
- 15: 3, # 'з'
- 2: 3, # 'и'
- 26: 3, # 'й'
- 12: 3, # 'к'
- 10: 3, # 'л'
- 14: 3, # 'м'
- 6: 3, # 'н'
- 4: 3, # 'о'
- 13: 3, # 'п'
- 7: 3, # 'р'
- 8: 3, # 'с'
- 5: 3, # 'т'
- 19: 2, # 'у'
- 29: 3, # 'ф'
- 25: 3, # 'х'
- 22: 3, # 'ц'
- 21: 3, # 'ч'
- 27: 3, # 'ш'
- 24: 3, # 'щ'
- 17: 2, # 'ъ'
- 52: 0, # 'ь'
- 42: 1, # 'ю'
- 16: 3, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 26: { # 'й'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 1, # 'а'
- 18: 2, # 'б'
- 9: 2, # 'в'
- 20: 1, # 'г'
- 11: 2, # 'д'
- 3: 2, # 'е'
- 23: 0, # 'ж'
- 15: 2, # 'з'
- 2: 1, # 'и'
- 26: 0, # 'й'
- 12: 3, # 'к'
- 10: 2, # 'л'
- 14: 2, # 'м'
- 6: 3, # 'н'
- 4: 2, # 'о'
- 13: 1, # 'п'
- 7: 2, # 'р'
- 8: 3, # 'с'
- 5: 3, # 'т'
- 19: 1, # 'у'
- 29: 2, # 'ф'
- 25: 1, # 'х'
- 22: 2, # 'ц'
- 21: 2, # 'ч'
- 27: 1, # 'ш'
- 24: 1, # 'щ'
- 17: 1, # 'ъ'
- 52: 0, # 'ь'
- 42: 0, # 'ю'
- 16: 1, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 12: { # 'к'
- 63: 1, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 1, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 1, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 1, # 'б'
- 9: 3, # 'в'
- 20: 2, # 'г'
- 11: 1, # 'д'
- 3: 3, # 'е'
- 23: 0, # 'ж'
- 15: 2, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 1, # 'к'
- 10: 3, # 'л'
- 14: 2, # 'м'
- 6: 3, # 'н'
- 4: 3, # 'о'
- 13: 1, # 'п'
- 7: 3, # 'р'
- 8: 3, # 'с'
- 5: 3, # 'т'
- 19: 3, # 'у'
- 29: 1, # 'ф'
- 25: 1, # 'х'
- 22: 3, # 'ц'
- 21: 2, # 'ч'
- 27: 1, # 'ш'
- 24: 0, # 'щ'
- 17: 3, # 'ъ'
- 52: 1, # 'ь'
- 42: 2, # 'ю'
- 16: 1, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 10: { # 'л'
- 63: 1, # 'e'
- 45: 1, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 1, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 3, # 'б'
- 9: 3, # 'в'
- 20: 3, # 'г'
- 11: 2, # 'д'
- 3: 3, # 'е'
- 23: 3, # 'ж'
- 15: 2, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 3, # 'к'
- 10: 1, # 'л'
- 14: 2, # 'м'
- 6: 3, # 'н'
- 4: 3, # 'о'
- 13: 2, # 'п'
- 7: 2, # 'р'
- 8: 3, # 'с'
- 5: 3, # 'т'
- 19: 3, # 'у'
- 29: 2, # 'ф'
- 25: 2, # 'х'
- 22: 2, # 'ц'
- 21: 2, # 'ч'
- 27: 2, # 'ш'
- 24: 1, # 'щ'
- 17: 3, # 'ъ'
- 52: 2, # 'ь'
- 42: 3, # 'ю'
- 16: 3, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 14: { # 'м'
- 63: 1, # 'e'
- 45: 0, # '\xad'
- 31: 1, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 3, # 'б'
- 9: 3, # 'в'
- 20: 1, # 'г'
- 11: 1, # 'д'
- 3: 3, # 'е'
- 23: 1, # 'ж'
- 15: 1, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 2, # 'к'
- 10: 3, # 'л'
- 14: 1, # 'м'
- 6: 3, # 'н'
- 4: 3, # 'о'
- 13: 3, # 'п'
- 7: 2, # 'р'
- 8: 2, # 'с'
- 5: 1, # 'т'
- 19: 3, # 'у'
- 29: 2, # 'ф'
- 25: 1, # 'х'
- 22: 2, # 'ц'
- 21: 2, # 'ч'
- 27: 2, # 'ш'
- 24: 1, # 'щ'
- 17: 3, # 'ъ'
- 52: 1, # 'ь'
- 42: 2, # 'ю'
- 16: 3, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 6: { # 'н'
- 63: 1, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 1, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 2, # 'б'
- 9: 2, # 'в'
- 20: 3, # 'г'
- 11: 3, # 'д'
- 3: 3, # 'е'
- 23: 2, # 'ж'
- 15: 2, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 3, # 'к'
- 10: 2, # 'л'
- 14: 1, # 'м'
- 6: 3, # 'н'
- 4: 3, # 'о'
- 13: 1, # 'п'
- 7: 2, # 'р'
- 8: 3, # 'с'
- 5: 3, # 'т'
- 19: 3, # 'у'
- 29: 3, # 'ф'
- 25: 2, # 'х'
- 22: 3, # 'ц'
- 21: 3, # 'ч'
- 27: 2, # 'ш'
- 24: 1, # 'щ'
- 17: 3, # 'ъ'
- 52: 2, # 'ь'
- 42: 2, # 'ю'
- 16: 3, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 4: { # 'о'
- 63: 0, # 'e'
- 45: 1, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 2, # 'а'
- 18: 3, # 'б'
- 9: 3, # 'в'
- 20: 3, # 'г'
- 11: 3, # 'д'
- 3: 3, # 'е'
- 23: 3, # 'ж'
- 15: 3, # 'з'
- 2: 3, # 'и'
- 26: 3, # 'й'
- 12: 3, # 'к'
- 10: 3, # 'л'
- 14: 3, # 'м'
- 6: 3, # 'н'
- 4: 2, # 'о'
- 13: 3, # 'п'
- 7: 3, # 'р'
- 8: 3, # 'с'
- 5: 3, # 'т'
- 19: 2, # 'у'
- 29: 3, # 'ф'
- 25: 3, # 'х'
- 22: 3, # 'ц'
- 21: 3, # 'ч'
- 27: 3, # 'ш'
- 24: 3, # 'щ'
- 17: 1, # 'ъ'
- 52: 0, # 'ь'
- 42: 1, # 'ю'
- 16: 3, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 13: { # 'п'
- 63: 1, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 1, # 'б'
- 9: 2, # 'в'
- 20: 1, # 'г'
- 11: 1, # 'д'
- 3: 3, # 'е'
- 23: 0, # 'ж'
- 15: 1, # 'з'
- 2: 3, # 'и'
- 26: 1, # 'й'
- 12: 2, # 'к'
- 10: 3, # 'л'
- 14: 1, # 'м'
- 6: 2, # 'н'
- 4: 3, # 'о'
- 13: 1, # 'п'
- 7: 3, # 'р'
- 8: 2, # 'с'
- 5: 2, # 'т'
- 19: 3, # 'у'
- 29: 1, # 'ф'
- 25: 1, # 'х'
- 22: 2, # 'ц'
- 21: 2, # 'ч'
- 27: 1, # 'ш'
- 24: 1, # 'щ'
- 17: 3, # 'ъ'
- 52: 1, # 'ь'
- 42: 2, # 'ю'
- 16: 2, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 7: { # 'р'
- 63: 1, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 3, # 'б'
- 9: 3, # 'в'
- 20: 3, # 'г'
- 11: 3, # 'д'
- 3: 3, # 'е'
- 23: 3, # 'ж'
- 15: 2, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 3, # 'к'
- 10: 3, # 'л'
- 14: 3, # 'м'
- 6: 3, # 'н'
- 4: 3, # 'о'
- 13: 2, # 'п'
- 7: 1, # 'р'
- 8: 3, # 'с'
- 5: 3, # 'т'
- 19: 3, # 'у'
- 29: 2, # 'ф'
- 25: 3, # 'х'
- 22: 3, # 'ц'
- 21: 2, # 'ч'
- 27: 3, # 'ш'
- 24: 1, # 'щ'
- 17: 3, # 'ъ'
- 52: 1, # 'ь'
- 42: 2, # 'ю'
- 16: 3, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 8: { # 'с'
- 63: 1, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 2, # 'б'
- 9: 3, # 'в'
- 20: 2, # 'г'
- 11: 2, # 'д'
- 3: 3, # 'е'
- 23: 0, # 'ж'
- 15: 1, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 3, # 'к'
- 10: 3, # 'л'
- 14: 3, # 'м'
- 6: 3, # 'н'
- 4: 3, # 'о'
- 13: 3, # 'п'
- 7: 3, # 'р'
- 8: 1, # 'с'
- 5: 3, # 'т'
- 19: 3, # 'у'
- 29: 2, # 'ф'
- 25: 2, # 'х'
- 22: 2, # 'ц'
- 21: 2, # 'ч'
- 27: 2, # 'ш'
- 24: 0, # 'щ'
- 17: 3, # 'ъ'
- 52: 2, # 'ь'
- 42: 2, # 'ю'
- 16: 3, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 5: { # 'т'
- 63: 1, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 3, # 'б'
- 9: 3, # 'в'
- 20: 2, # 'г'
- 11: 2, # 'д'
- 3: 3, # 'е'
- 23: 1, # 'ж'
- 15: 1, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 3, # 'к'
- 10: 3, # 'л'
- 14: 2, # 'м'
- 6: 3, # 'н'
- 4: 3, # 'о'
- 13: 2, # 'п'
- 7: 3, # 'р'
- 8: 3, # 'с'
- 5: 3, # 'т'
- 19: 3, # 'у'
- 29: 1, # 'ф'
- 25: 2, # 'х'
- 22: 2, # 'ц'
- 21: 2, # 'ч'
- 27: 1, # 'ш'
- 24: 1, # 'щ'
- 17: 3, # 'ъ'
- 52: 2, # 'ь'
- 42: 2, # 'ю'
- 16: 3, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 19: { # 'у'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 3, # 'б'
- 9: 3, # 'в'
- 20: 3, # 'г'
- 11: 3, # 'д'
- 3: 2, # 'е'
- 23: 3, # 'ж'
- 15: 3, # 'з'
- 2: 2, # 'и'
- 26: 2, # 'й'
- 12: 3, # 'к'
- 10: 3, # 'л'
- 14: 3, # 'м'
- 6: 3, # 'н'
- 4: 2, # 'о'
- 13: 3, # 'п'
- 7: 3, # 'р'
- 8: 3, # 'с'
- 5: 3, # 'т'
- 19: 1, # 'у'
- 29: 2, # 'ф'
- 25: 2, # 'х'
- 22: 2, # 'ц'
- 21: 3, # 'ч'
- 27: 3, # 'ш'
- 24: 2, # 'щ'
- 17: 1, # 'ъ'
- 52: 0, # 'ь'
- 42: 1, # 'ю'
- 16: 1, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 29: { # 'ф'
- 63: 1, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 1, # 'б'
- 9: 1, # 'в'
- 20: 1, # 'г'
- 11: 0, # 'д'
- 3: 3, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 2, # 'к'
- 10: 2, # 'л'
- 14: 1, # 'м'
- 6: 1, # 'н'
- 4: 3, # 'о'
- 13: 0, # 'п'
- 7: 2, # 'р'
- 8: 2, # 'с'
- 5: 2, # 'т'
- 19: 2, # 'у'
- 29: 0, # 'ф'
- 25: 1, # 'х'
- 22: 0, # 'ц'
- 21: 1, # 'ч'
- 27: 1, # 'ш'
- 24: 0, # 'щ'
- 17: 2, # 'ъ'
- 52: 2, # 'ь'
- 42: 1, # 'ю'
- 16: 1, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 25: { # 'х'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 1, # 'б'
- 9: 3, # 'в'
- 20: 0, # 'г'
- 11: 1, # 'д'
- 3: 2, # 'е'
- 23: 0, # 'ж'
- 15: 1, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 1, # 'к'
- 10: 2, # 'л'
- 14: 2, # 'м'
- 6: 3, # 'н'
- 4: 3, # 'о'
- 13: 1, # 'п'
- 7: 3, # 'р'
- 8: 1, # 'с'
- 5: 2, # 'т'
- 19: 3, # 'у'
- 29: 0, # 'ф'
- 25: 1, # 'х'
- 22: 0, # 'ц'
- 21: 1, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 2, # 'ъ'
- 52: 0, # 'ь'
- 42: 1, # 'ю'
- 16: 1, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 22: { # 'ц'
- 63: 1, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 1, # 'б'
- 9: 2, # 'в'
- 20: 1, # 'г'
- 11: 1, # 'д'
- 3: 3, # 'е'
- 23: 0, # 'ж'
- 15: 1, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 2, # 'к'
- 10: 1, # 'л'
- 14: 1, # 'м'
- 6: 1, # 'н'
- 4: 2, # 'о'
- 13: 1, # 'п'
- 7: 1, # 'р'
- 8: 1, # 'с'
- 5: 1, # 'т'
- 19: 2, # 'у'
- 29: 1, # 'ф'
- 25: 1, # 'х'
- 22: 1, # 'ц'
- 21: 1, # 'ч'
- 27: 1, # 'ш'
- 24: 1, # 'щ'
- 17: 2, # 'ъ'
- 52: 1, # 'ь'
- 42: 0, # 'ю'
- 16: 2, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 21: { # 'ч'
- 63: 1, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 1, # 'б'
- 9: 3, # 'в'
- 20: 1, # 'г'
- 11: 0, # 'д'
- 3: 3, # 'е'
- 23: 1, # 'ж'
- 15: 0, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 3, # 'к'
- 10: 2, # 'л'
- 14: 2, # 'м'
- 6: 3, # 'н'
- 4: 3, # 'о'
- 13: 0, # 'п'
- 7: 2, # 'р'
- 8: 0, # 'с'
- 5: 2, # 'т'
- 19: 3, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 1, # 'ш'
- 24: 0, # 'щ'
- 17: 2, # 'ъ'
- 52: 0, # 'ь'
- 42: 1, # 'ю'
- 16: 0, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 27: { # 'ш'
- 63: 1, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 0, # 'б'
- 9: 2, # 'в'
- 20: 0, # 'г'
- 11: 1, # 'д'
- 3: 3, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 3, # 'к'
- 10: 2, # 'л'
- 14: 1, # 'м'
- 6: 3, # 'н'
- 4: 2, # 'о'
- 13: 2, # 'п'
- 7: 1, # 'р'
- 8: 0, # 'с'
- 5: 1, # 'т'
- 19: 2, # 'у'
- 29: 1, # 'ф'
- 25: 0, # 'х'
- 22: 0, # 'ц'
- 21: 1, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 2, # 'ъ'
- 52: 1, # 'ь'
- 42: 1, # 'ю'
- 16: 0, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 24: { # 'щ'
- 63: 1, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 3, # 'а'
- 18: 0, # 'б'
- 9: 1, # 'в'
- 20: 0, # 'г'
- 11: 0, # 'д'
- 3: 3, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 3, # 'и'
- 26: 0, # 'й'
- 12: 1, # 'к'
- 10: 0, # 'л'
- 14: 0, # 'м'
- 6: 2, # 'н'
- 4: 3, # 'о'
- 13: 0, # 'п'
- 7: 1, # 'р'
- 8: 0, # 'с'
- 5: 2, # 'т'
- 19: 3, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 1, # 'ц'
- 21: 0, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 1, # 'ъ'
- 52: 0, # 'ь'
- 42: 0, # 'ю'
- 16: 2, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 17: { # 'ъ'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 1, # 'а'
- 18: 3, # 'б'
- 9: 3, # 'в'
- 20: 3, # 'г'
- 11: 3, # 'д'
- 3: 2, # 'е'
- 23: 3, # 'ж'
- 15: 3, # 'з'
- 2: 1, # 'и'
- 26: 2, # 'й'
- 12: 3, # 'к'
- 10: 3, # 'л'
- 14: 3, # 'м'
- 6: 3, # 'н'
- 4: 3, # 'о'
- 13: 3, # 'п'
- 7: 3, # 'р'
- 8: 3, # 'с'
- 5: 3, # 'т'
- 19: 1, # 'у'
- 29: 1, # 'ф'
- 25: 2, # 'х'
- 22: 2, # 'ц'
- 21: 3, # 'ч'
- 27: 2, # 'ш'
- 24: 3, # 'щ'
- 17: 0, # 'ъ'
- 52: 0, # 'ь'
- 42: 2, # 'ю'
- 16: 0, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 52: { # 'ь'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 0, # 'а'
- 18: 0, # 'б'
- 9: 0, # 'в'
- 20: 0, # 'г'
- 11: 0, # 'д'
- 3: 1, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 0, # 'и'
- 26: 0, # 'й'
- 12: 1, # 'к'
- 10: 0, # 'л'
- 14: 0, # 'м'
- 6: 1, # 'н'
- 4: 3, # 'о'
- 13: 0, # 'п'
- 7: 0, # 'р'
- 8: 0, # 'с'
- 5: 1, # 'т'
- 19: 0, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 1, # 'ц'
- 21: 0, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 0, # 'ъ'
- 52: 0, # 'ь'
- 42: 1, # 'ю'
- 16: 0, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 42: { # 'ю'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 1, # 'а'
- 18: 2, # 'б'
- 9: 1, # 'в'
- 20: 2, # 'г'
- 11: 2, # 'д'
- 3: 1, # 'е'
- 23: 2, # 'ж'
- 15: 2, # 'з'
- 2: 1, # 'и'
- 26: 1, # 'й'
- 12: 2, # 'к'
- 10: 2, # 'л'
- 14: 2, # 'м'
- 6: 2, # 'н'
- 4: 1, # 'о'
- 13: 1, # 'п'
- 7: 2, # 'р'
- 8: 2, # 'с'
- 5: 2, # 'т'
- 19: 1, # 'у'
- 29: 1, # 'ф'
- 25: 1, # 'х'
- 22: 2, # 'ц'
- 21: 3, # 'ч'
- 27: 1, # 'ш'
- 24: 1, # 'щ'
- 17: 1, # 'ъ'
- 52: 0, # 'ь'
- 42: 0, # 'ю'
- 16: 1, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 16: { # 'я'
- 63: 0, # 'e'
- 45: 1, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 0, # 'а'
- 18: 3, # 'б'
- 9: 3, # 'в'
- 20: 2, # 'г'
- 11: 3, # 'д'
- 3: 2, # 'е'
- 23: 1, # 'ж'
- 15: 2, # 'з'
- 2: 1, # 'и'
- 26: 2, # 'й'
- 12: 3, # 'к'
- 10: 3, # 'л'
- 14: 3, # 'м'
- 6: 3, # 'н'
- 4: 1, # 'о'
- 13: 2, # 'п'
- 7: 2, # 'р'
- 8: 3, # 'с'
- 5: 3, # 'т'
- 19: 1, # 'у'
- 29: 1, # 'ф'
- 25: 3, # 'х'
- 22: 2, # 'ц'
- 21: 1, # 'ч'
- 27: 1, # 'ш'
- 24: 2, # 'щ'
- 17: 0, # 'ъ'
- 52: 0, # 'ь'
- 42: 0, # 'ю'
- 16: 1, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 58: { # 'є'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 0, # 'а'
- 18: 0, # 'б'
- 9: 0, # 'в'
- 20: 0, # 'г'
- 11: 0, # 'д'
- 3: 0, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 0, # 'и'
- 26: 0, # 'й'
- 12: 0, # 'к'
- 10: 0, # 'л'
- 14: 0, # 'м'
- 6: 0, # 'н'
- 4: 0, # 'о'
- 13: 0, # 'п'
- 7: 0, # 'р'
- 8: 0, # 'с'
- 5: 0, # 'т'
- 19: 0, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 0, # 'ъ'
- 52: 0, # 'ь'
- 42: 0, # 'ю'
- 16: 0, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
- 62: { # '№'
- 63: 0, # 'e'
- 45: 0, # '\xad'
- 31: 0, # 'А'
- 32: 0, # 'Б'
- 35: 0, # 'В'
- 43: 0, # 'Г'
- 37: 0, # 'Д'
- 44: 0, # 'Е'
- 55: 0, # 'Ж'
- 47: 0, # 'З'
- 40: 0, # 'И'
- 59: 0, # 'Й'
- 33: 0, # 'К'
- 46: 0, # 'Л'
- 38: 0, # 'М'
- 36: 0, # 'Н'
- 41: 0, # 'О'
- 30: 0, # 'П'
- 39: 0, # 'Р'
- 28: 0, # 'С'
- 34: 0, # 'Т'
- 51: 0, # 'У'
- 48: 0, # 'Ф'
- 49: 0, # 'Х'
- 53: 0, # 'Ц'
- 50: 0, # 'Ч'
- 54: 0, # 'Ш'
- 57: 0, # 'Щ'
- 61: 0, # 'Ъ'
- 60: 0, # 'Ю'
- 56: 0, # 'Я'
- 1: 0, # 'а'
- 18: 0, # 'б'
- 9: 0, # 'в'
- 20: 0, # 'г'
- 11: 0, # 'д'
- 3: 0, # 'е'
- 23: 0, # 'ж'
- 15: 0, # 'з'
- 2: 0, # 'и'
- 26: 0, # 'й'
- 12: 0, # 'к'
- 10: 0, # 'л'
- 14: 0, # 'м'
- 6: 0, # 'н'
- 4: 0, # 'о'
- 13: 0, # 'п'
- 7: 0, # 'р'
- 8: 0, # 'с'
- 5: 0, # 'т'
- 19: 0, # 'у'
- 29: 0, # 'ф'
- 25: 0, # 'х'
- 22: 0, # 'ц'
- 21: 0, # 'ч'
- 27: 0, # 'ш'
- 24: 0, # 'щ'
- 17: 0, # 'ъ'
- 52: 0, # 'ь'
- 42: 0, # 'ю'
- 16: 0, # 'я'
- 58: 0, # 'є'
- 62: 0, # '№'
- },
-}
-
-# 255: Undefined characters that did not exist in training text
-# 254: Carriage/Return
-# 253: symbol (punctuation) that does not belong to word
-# 252: 0 - 9
-# 251: Control characters
-
-# Character Mapping Table(s):
-ISO_8859_5_BULGARIAN_CHAR_TO_ORDER = {
- 0: 255, # '\x00'
- 1: 255, # '\x01'
- 2: 255, # '\x02'
- 3: 255, # '\x03'
- 4: 255, # '\x04'
- 5: 255, # '\x05'
- 6: 255, # '\x06'
- 7: 255, # '\x07'
- 8: 255, # '\x08'
- 9: 255, # '\t'
- 10: 254, # '\n'
- 11: 255, # '\x0b'
- 12: 255, # '\x0c'
- 13: 254, # '\r'
- 14: 255, # '\x0e'
- 15: 255, # '\x0f'
- 16: 255, # '\x10'
- 17: 255, # '\x11'
- 18: 255, # '\x12'
- 19: 255, # '\x13'
- 20: 255, # '\x14'
- 21: 255, # '\x15'
- 22: 255, # '\x16'
- 23: 255, # '\x17'
- 24: 255, # '\x18'
- 25: 255, # '\x19'
- 26: 255, # '\x1a'
- 27: 255, # '\x1b'
- 28: 255, # '\x1c'
- 29: 255, # '\x1d'
- 30: 255, # '\x1e'
- 31: 255, # '\x1f'
- 32: 253, # ' '
- 33: 253, # '!'
- 34: 253, # '"'
- 35: 253, # '#'
- 36: 253, # '$'
- 37: 253, # '%'
- 38: 253, # '&'
- 39: 253, # "'"
- 40: 253, # '('
- 41: 253, # ')'
- 42: 253, # '*'
- 43: 253, # '+'
- 44: 253, # ','
- 45: 253, # '-'
- 46: 253, # '.'
- 47: 253, # '/'
- 48: 252, # '0'
- 49: 252, # '1'
- 50: 252, # '2'
- 51: 252, # '3'
- 52: 252, # '4'
- 53: 252, # '5'
- 54: 252, # '6'
- 55: 252, # '7'
- 56: 252, # '8'
- 57: 252, # '9'
- 58: 253, # ':'
- 59: 253, # ';'
- 60: 253, # '<'
- 61: 253, # '='
- 62: 253, # '>'
- 63: 253, # '?'
- 64: 253, # '@'
- 65: 77, # 'A'
- 66: 90, # 'B'
- 67: 99, # 'C'
- 68: 100, # 'D'
- 69: 72, # 'E'
- 70: 109, # 'F'
- 71: 107, # 'G'
- 72: 101, # 'H'
- 73: 79, # 'I'
- 74: 185, # 'J'
- 75: 81, # 'K'
- 76: 102, # 'L'
- 77: 76, # 'M'
- 78: 94, # 'N'
- 79: 82, # 'O'
- 80: 110, # 'P'
- 81: 186, # 'Q'
- 82: 108, # 'R'
- 83: 91, # 'S'
- 84: 74, # 'T'
- 85: 119, # 'U'
- 86: 84, # 'V'
- 87: 96, # 'W'
- 88: 111, # 'X'
- 89: 187, # 'Y'
- 90: 115, # 'Z'
- 91: 253, # '['
- 92: 253, # '\\'
- 93: 253, # ']'
- 94: 253, # '^'
- 95: 253, # '_'
- 96: 253, # '`'
- 97: 65, # 'a'
- 98: 69, # 'b'
- 99: 70, # 'c'
- 100: 66, # 'd'
- 101: 63, # 'e'
- 102: 68, # 'f'
- 103: 112, # 'g'
- 104: 103, # 'h'
- 105: 92, # 'i'
- 106: 194, # 'j'
- 107: 104, # 'k'
- 108: 95, # 'l'
- 109: 86, # 'm'
- 110: 87, # 'n'
- 111: 71, # 'o'
- 112: 116, # 'p'
- 113: 195, # 'q'
- 114: 85, # 'r'
- 115: 93, # 's'
- 116: 97, # 't'
- 117: 113, # 'u'
- 118: 196, # 'v'
- 119: 197, # 'w'
- 120: 198, # 'x'
- 121: 199, # 'y'
- 122: 200, # 'z'
- 123: 253, # '{'
- 124: 253, # '|'
- 125: 253, # '}'
- 126: 253, # '~'
- 127: 253, # '\x7f'
- 128: 194, # '\x80'
- 129: 195, # '\x81'
- 130: 196, # '\x82'
- 131: 197, # '\x83'
- 132: 198, # '\x84'
- 133: 199, # '\x85'
- 134: 200, # '\x86'
- 135: 201, # '\x87'
- 136: 202, # '\x88'
- 137: 203, # '\x89'
- 138: 204, # '\x8a'
- 139: 205, # '\x8b'
- 140: 206, # '\x8c'
- 141: 207, # '\x8d'
- 142: 208, # '\x8e'
- 143: 209, # '\x8f'
- 144: 210, # '\x90'
- 145: 211, # '\x91'
- 146: 212, # '\x92'
- 147: 213, # '\x93'
- 148: 214, # '\x94'
- 149: 215, # '\x95'
- 150: 216, # '\x96'
- 151: 217, # '\x97'
- 152: 218, # '\x98'
- 153: 219, # '\x99'
- 154: 220, # '\x9a'
- 155: 221, # '\x9b'
- 156: 222, # '\x9c'
- 157: 223, # '\x9d'
- 158: 224, # '\x9e'
- 159: 225, # '\x9f'
- 160: 81, # '\xa0'
- 161: 226, # 'Ё'
- 162: 227, # 'Ђ'
- 163: 228, # 'Ѓ'
- 164: 229, # 'Є'
- 165: 230, # 'Ѕ'
- 166: 105, # 'І'
- 167: 231, # 'Ї'
- 168: 232, # 'Ј'
- 169: 233, # 'Љ'
- 170: 234, # 'Њ'
- 171: 235, # 'Ћ'
- 172: 236, # 'Ќ'
- 173: 45, # '\xad'
- 174: 237, # 'Ў'
- 175: 238, # 'Џ'
- 176: 31, # 'А'
- 177: 32, # 'Б'
- 178: 35, # 'В'
- 179: 43, # 'Г'
- 180: 37, # 'Д'
- 181: 44, # 'Е'
- 182: 55, # 'Ж'
- 183: 47, # 'З'
- 184: 40, # 'И'
- 185: 59, # 'Й'
- 186: 33, # 'К'
- 187: 46, # 'Л'
- 188: 38, # 'М'
- 189: 36, # 'Н'
- 190: 41, # 'О'
- 191: 30, # 'П'
- 192: 39, # 'Р'
- 193: 28, # 'С'
- 194: 34, # 'Т'
- 195: 51, # 'У'
- 196: 48, # 'Ф'
- 197: 49, # 'Х'
- 198: 53, # 'Ц'
- 199: 50, # 'Ч'
- 200: 54, # 'Ш'
- 201: 57, # 'Щ'
- 202: 61, # 'Ъ'
- 203: 239, # 'Ы'
- 204: 67, # 'Ь'
- 205: 240, # 'Э'
- 206: 60, # 'Ю'
- 207: 56, # 'Я'
- 208: 1, # 'а'
- 209: 18, # 'б'
- 210: 9, # 'в'
- 211: 20, # 'г'
- 212: 11, # 'д'
- 213: 3, # 'е'
- 214: 23, # 'ж'
- 215: 15, # 'з'
- 216: 2, # 'и'
- 217: 26, # 'й'
- 218: 12, # 'к'
- 219: 10, # 'л'
- 220: 14, # 'м'
- 221: 6, # 'н'
- 222: 4, # 'о'
- 223: 13, # 'п'
- 224: 7, # 'р'
- 225: 8, # 'с'
- 226: 5, # 'т'
- 227: 19, # 'у'
- 228: 29, # 'ф'
- 229: 25, # 'х'
- 230: 22, # 'ц'
- 231: 21, # 'ч'
- 232: 27, # 'ш'
- 233: 24, # 'щ'
- 234: 17, # 'ъ'
- 235: 75, # 'ы'
- 236: 52, # 'ь'
- 237: 241, # 'э'
- 238: 42, # 'ю'
- 239: 16, # 'я'
- 240: 62, # '№'
- 241: 242, # 'ё'
- 242: 243, # 'ђ'
- 243: 244, # 'ѓ'
- 244: 58, # 'є'
- 245: 245, # 'ѕ'
- 246: 98, # 'і'
- 247: 246, # 'ї'
- 248: 247, # 'ј'
- 249: 248, # 'љ'
- 250: 249, # 'њ'
- 251: 250, # 'ћ'
- 252: 251, # 'ќ'
- 253: 91, # '§'
- 254: 252, # 'ў'
- 255: 253, # 'џ'
-}
-
-ISO_8859_5_BULGARIAN_MODEL = SingleByteCharSetModel(
- charset_name="ISO-8859-5",
- language="Bulgarian",
- char_to_order_map=ISO_8859_5_BULGARIAN_CHAR_TO_ORDER,
- language_model=BULGARIAN_LANG_MODEL,
- typical_positive_ratio=0.969392,
- keep_ascii_letters=False,
- alphabet="АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯабвгдежзийклмнопрстуфхцчшщъьюя",
-)
-
-WINDOWS_1251_BULGARIAN_CHAR_TO_ORDER = {
- 0: 255, # '\x00'
- 1: 255, # '\x01'
- 2: 255, # '\x02'
- 3: 255, # '\x03'
- 4: 255, # '\x04'
- 5: 255, # '\x05'
- 6: 255, # '\x06'
- 7: 255, # '\x07'
- 8: 255, # '\x08'
- 9: 255, # '\t'
- 10: 254, # '\n'
- 11: 255, # '\x0b'
- 12: 255, # '\x0c'
- 13: 254, # '\r'
- 14: 255, # '\x0e'
- 15: 255, # '\x0f'
- 16: 255, # '\x10'
- 17: 255, # '\x11'
- 18: 255, # '\x12'
- 19: 255, # '\x13'
- 20: 255, # '\x14'
- 21: 255, # '\x15'
- 22: 255, # '\x16'
- 23: 255, # '\x17'
- 24: 255, # '\x18'
- 25: 255, # '\x19'
- 26: 255, # '\x1a'
- 27: 255, # '\x1b'
- 28: 255, # '\x1c'
- 29: 255, # '\x1d'
- 30: 255, # '\x1e'
- 31: 255, # '\x1f'
- 32: 253, # ' '
- 33: 253, # '!'
- 34: 253, # '"'
- 35: 253, # '#'
- 36: 253, # '$'
- 37: 253, # '%'
- 38: 253, # '&'
- 39: 253, # "'"
- 40: 253, # '('
- 41: 253, # ')'
- 42: 253, # '*'
- 43: 253, # '+'
- 44: 253, # ','
- 45: 253, # '-'
- 46: 253, # '.'
- 47: 253, # '/'
- 48: 252, # '0'
- 49: 252, # '1'
- 50: 252, # '2'
- 51: 252, # '3'
- 52: 252, # '4'
- 53: 252, # '5'
- 54: 252, # '6'
- 55: 252, # '7'
- 56: 252, # '8'
- 57: 252, # '9'
- 58: 253, # ':'
- 59: 253, # ';'
- 60: 253, # '<'
- 61: 253, # '='
- 62: 253, # '>'
- 63: 253, # '?'
- 64: 253, # '@'
- 65: 77, # 'A'
- 66: 90, # 'B'
- 67: 99, # 'C'
- 68: 100, # 'D'
- 69: 72, # 'E'
- 70: 109, # 'F'
- 71: 107, # 'G'
- 72: 101, # 'H'
- 73: 79, # 'I'
- 74: 185, # 'J'
- 75: 81, # 'K'
- 76: 102, # 'L'
- 77: 76, # 'M'
- 78: 94, # 'N'
- 79: 82, # 'O'
- 80: 110, # 'P'
- 81: 186, # 'Q'
- 82: 108, # 'R'
- 83: 91, # 'S'
- 84: 74, # 'T'
- 85: 119, # 'U'
- 86: 84, # 'V'
- 87: 96, # 'W'
- 88: 111, # 'X'
- 89: 187, # 'Y'
- 90: 115, # 'Z'
- 91: 253, # '['
- 92: 253, # '\\'
- 93: 253, # ']'
- 94: 253, # '^'
- 95: 253, # '_'
- 96: 253, # '`'
- 97: 65, # 'a'
- 98: 69, # 'b'
- 99: 70, # 'c'
- 100: 66, # 'd'
- 101: 63, # 'e'
- 102: 68, # 'f'
- 103: 112, # 'g'
- 104: 103, # 'h'
- 105: 92, # 'i'
- 106: 194, # 'j'
- 107: 104, # 'k'
- 108: 95, # 'l'
- 109: 86, # 'm'
- 110: 87, # 'n'
- 111: 71, # 'o'
- 112: 116, # 'p'
- 113: 195, # 'q'
- 114: 85, # 'r'
- 115: 93, # 's'
- 116: 97, # 't'
- 117: 113, # 'u'
- 118: 196, # 'v'
- 119: 197, # 'w'
- 120: 198, # 'x'
- 121: 199, # 'y'
- 122: 200, # 'z'
- 123: 253, # '{'
- 124: 253, # '|'
- 125: 253, # '}'
- 126: 253, # '~'
- 127: 253, # '\x7f'
- 128: 206, # 'Ђ'
- 129: 207, # 'Ѓ'
- 130: 208, # '‚'
- 131: 209, # 'ѓ'
- 132: 210, # '„'
- 133: 211, # '…'
- 134: 212, # '†'
- 135: 213, # '‡'
- 136: 120, # '€'
- 137: 214, # '‰'
- 138: 215, # 'Љ'
- 139: 216, # '‹'
- 140: 217, # 'Њ'
- 141: 218, # 'Ќ'
- 142: 219, # 'Ћ'
- 143: 220, # 'Џ'
- 144: 221, # 'ђ'
- 145: 78, # '‘'
- 146: 64, # '’'
- 147: 83, # '“'
- 148: 121, # '”'
- 149: 98, # '•'
- 150: 117, # '–'
- 151: 105, # '—'
- 152: 222, # None
- 153: 223, # '™'
- 154: 224, # 'љ'
- 155: 225, # '›'
- 156: 226, # 'њ'
- 157: 227, # 'ќ'
- 158: 228, # 'ћ'
- 159: 229, # 'џ'
- 160: 88, # '\xa0'
- 161: 230, # 'Ў'
- 162: 231, # 'ў'
- 163: 232, # 'Ј'
- 164: 233, # '¤'
- 165: 122, # 'Ґ'
- 166: 89, # '¦'
- 167: 106, # '§'
- 168: 234, # 'Ё'
- 169: 235, # '©'
- 170: 236, # 'Є'
- 171: 237, # '«'
- 172: 238, # '¬'
- 173: 45, # '\xad'
- 174: 239, # '®'
- 175: 240, # 'Ї'
- 176: 73, # '°'
- 177: 80, # '±'
- 178: 118, # 'І'
- 179: 114, # 'і'
- 180: 241, # 'ґ'
- 181: 242, # 'µ'
- 182: 243, # '¶'
- 183: 244, # '·'
- 184: 245, # 'ё'
- 185: 62, # '№'
- 186: 58, # 'є'
- 187: 246, # '»'
- 188: 247, # 'ј'
- 189: 248, # 'Ѕ'
- 190: 249, # 'ѕ'
- 191: 250, # 'ї'
- 192: 31, # 'А'
- 193: 32, # 'Б'
- 194: 35, # 'В'
- 195: 43, # 'Г'
- 196: 37, # 'Д'
- 197: 44, # 'Е'
- 198: 55, # 'Ж'
- 199: 47, # 'З'
- 200: 40, # 'И'
- 201: 59, # 'Й'
- 202: 33, # 'К'
- 203: 46, # 'Л'
- 204: 38, # 'М'
- 205: 36, # 'Н'
- 206: 41, # 'О'
- 207: 30, # 'П'
- 208: 39, # 'Р'
- 209: 28, # 'С'
- 210: 34, # 'Т'
- 211: 51, # 'У'
- 212: 48, # 'Ф'
- 213: 49, # 'Х'
- 214: 53, # 'Ц'
- 215: 50, # 'Ч'
- 216: 54, # 'Ш'
- 217: 57, # 'Щ'
- 218: 61, # 'Ъ'
- 219: 251, # 'Ы'
- 220: 67, # 'Ь'
- 221: 252, # 'Э'
- 222: 60, # 'Ю'
- 223: 56, # 'Я'
- 224: 1, # 'а'
- 225: 18, # 'б'
- 226: 9, # 'в'
- 227: 20, # 'г'
- 228: 11, # 'д'
- 229: 3, # 'е'
- 230: 23, # 'ж'
- 231: 15, # 'з'
- 232: 2, # 'и'
- 233: 26, # 'й'
- 234: 12, # 'к'
- 235: 10, # 'л'
- 236: 14, # 'м'
- 237: 6, # 'н'
- 238: 4, # 'о'
- 239: 13, # 'п'
- 240: 7, # 'р'
- 241: 8, # 'с'
- 242: 5, # 'т'
- 243: 19, # 'у'
- 244: 29, # 'ф'
- 245: 25, # 'х'
- 246: 22, # 'ц'
- 247: 21, # 'ч'
- 248: 27, # 'ш'
- 249: 24, # 'щ'
- 250: 17, # 'ъ'
- 251: 75, # 'ы'
- 252: 52, # 'ь'
- 253: 253, # 'э'
- 254: 42, # 'ю'
- 255: 16, # 'я'
-}
-
-WINDOWS_1251_BULGARIAN_MODEL = SingleByteCharSetModel(
- charset_name="windows-1251",
- language="Bulgarian",
- char_to_order_map=WINDOWS_1251_BULGARIAN_CHAR_TO_ORDER,
- language_model=BULGARIAN_LANG_MODEL,
- typical_positive_ratio=0.969392,
- keep_ascii_letters=False,
- alphabet="АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯабвгдежзийклмнопрстуфхцчшщъьюя",
-)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/langgreekmodel.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/langgreekmodel.py
deleted file mode 100644
index 0471d8b..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/langgreekmodel.py
+++ /dev/null
@@ -1,4397 +0,0 @@
-from chardet.sbcharsetprober import SingleByteCharSetModel
-
-# 3: Positive
-# 2: Likely
-# 1: Unlikely
-# 0: Negative
-
-GREEK_LANG_MODEL = {
- 60: { # 'e'
- 60: 2, # 'e'
- 55: 1, # 'o'
- 58: 2, # 't'
- 36: 1, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 1, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 0, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 0, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 0, # 'θ'
- 5: 0, # 'ι'
- 11: 0, # 'κ'
- 16: 0, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 0, # 'ο'
- 9: 0, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 0, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 55: { # 'o'
- 60: 0, # 'e'
- 55: 2, # 'o'
- 58: 2, # 't'
- 36: 1, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 0, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 0, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 0, # 'θ'
- 5: 0, # 'ι'
- 11: 0, # 'κ'
- 16: 0, # 'λ'
- 10: 0, # 'μ'
- 6: 1, # 'ν'
- 30: 0, # 'ξ'
- 4: 0, # 'ο'
- 9: 0, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 1, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 58: { # 't'
- 60: 2, # 'e'
- 55: 1, # 'o'
- 58: 1, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 2, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 0, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 0, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 0, # 'θ'
- 5: 0, # 'ι'
- 11: 0, # 'κ'
- 16: 0, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 1, # 'ο'
- 9: 0, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 0, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 36: { # '·'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 0, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 0, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 0, # 'θ'
- 5: 0, # 'ι'
- 11: 0, # 'κ'
- 16: 0, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 0, # 'ο'
- 9: 0, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 0, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 61: { # 'Ά'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 0, # 'α'
- 29: 0, # 'β'
- 20: 1, # 'γ'
- 21: 2, # 'δ'
- 3: 0, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 0, # 'θ'
- 5: 0, # 'ι'
- 11: 0, # 'κ'
- 16: 2, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 0, # 'ο'
- 9: 1, # 'π'
- 8: 2, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 0, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 46: { # 'Έ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 0, # 'α'
- 29: 2, # 'β'
- 20: 2, # 'γ'
- 21: 0, # 'δ'
- 3: 0, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 0, # 'θ'
- 5: 0, # 'ι'
- 11: 2, # 'κ'
- 16: 2, # 'λ'
- 10: 0, # 'μ'
- 6: 3, # 'ν'
- 30: 2, # 'ξ'
- 4: 0, # 'ο'
- 9: 2, # 'π'
- 8: 2, # 'ρ'
- 14: 0, # 'ς'
- 7: 1, # 'σ'
- 2: 2, # 'τ'
- 12: 0, # 'υ'
- 28: 2, # 'φ'
- 23: 3, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 54: { # 'Ό'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 0, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 0, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 0, # 'θ'
- 5: 0, # 'ι'
- 11: 0, # 'κ'
- 16: 2, # 'λ'
- 10: 2, # 'μ'
- 6: 2, # 'ν'
- 30: 0, # 'ξ'
- 4: 0, # 'ο'
- 9: 2, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 2, # 'σ'
- 2: 3, # 'τ'
- 12: 0, # 'υ'
- 28: 0, # 'φ'
- 23: 2, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 31: { # 'Α'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 2, # 'Β'
- 43: 2, # 'Γ'
- 41: 1, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 2, # 'Θ'
- 47: 2, # 'Ι'
- 44: 2, # 'Κ'
- 53: 2, # 'Λ'
- 38: 2, # 'Μ'
- 49: 2, # 'Ν'
- 59: 1, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 2, # 'Π'
- 48: 2, # 'Ρ'
- 37: 2, # 'Σ'
- 33: 2, # 'Τ'
- 45: 2, # 'Υ'
- 56: 2, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 0, # 'α'
- 29: 0, # 'β'
- 20: 2, # 'γ'
- 21: 0, # 'δ'
- 3: 0, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 1, # 'θ'
- 5: 0, # 'ι'
- 11: 2, # 'κ'
- 16: 3, # 'λ'
- 10: 2, # 'μ'
- 6: 3, # 'ν'
- 30: 2, # 'ξ'
- 4: 0, # 'ο'
- 9: 3, # 'π'
- 8: 3, # 'ρ'
- 14: 2, # 'ς'
- 7: 2, # 'σ'
- 2: 0, # 'τ'
- 12: 3, # 'υ'
- 28: 2, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 2, # 'ύ'
- 27: 0, # 'ώ'
- },
- 51: { # 'Β'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 2, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 1, # 'Ε'
- 40: 1, # 'Η'
- 52: 0, # 'Θ'
- 47: 1, # 'Ι'
- 44: 0, # 'Κ'
- 53: 1, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 2, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 2, # 'ά'
- 18: 2, # 'έ'
- 22: 2, # 'ή'
- 15: 0, # 'ί'
- 1: 2, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 2, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 0, # 'θ'
- 5: 2, # 'ι'
- 11: 0, # 'κ'
- 16: 2, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 2, # 'ο'
- 9: 0, # 'π'
- 8: 2, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 0, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 43: { # 'Γ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 1, # 'Α'
- 51: 0, # 'Β'
- 43: 2, # 'Γ'
- 41: 0, # 'Δ'
- 34: 2, # 'Ε'
- 40: 1, # 'Η'
- 52: 0, # 'Θ'
- 47: 2, # 'Ι'
- 44: 1, # 'Κ'
- 53: 1, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 1, # 'Ο'
- 35: 0, # 'Π'
- 48: 2, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 2, # 'Υ'
- 56: 0, # 'Φ'
- 50: 1, # 'Χ'
- 57: 2, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 2, # 'ί'
- 1: 2, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 2, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 0, # 'θ'
- 5: 3, # 'ι'
- 11: 0, # 'κ'
- 16: 2, # 'λ'
- 10: 0, # 'μ'
- 6: 2, # 'ν'
- 30: 0, # 'ξ'
- 4: 0, # 'ο'
- 9: 0, # 'π'
- 8: 2, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 0, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 41: { # 'Δ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 2, # 'Ε'
- 40: 2, # 'Η'
- 52: 0, # 'Θ'
- 47: 2, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 2, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 2, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 2, # 'ή'
- 15: 2, # 'ί'
- 1: 0, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 3, # 'ε'
- 32: 0, # 'ζ'
- 13: 2, # 'η'
- 25: 0, # 'θ'
- 5: 3, # 'ι'
- 11: 0, # 'κ'
- 16: 0, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 2, # 'ο'
- 9: 0, # 'π'
- 8: 2, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 2, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 2, # 'ω'
- 19: 1, # 'ό'
- 26: 2, # 'ύ'
- 27: 2, # 'ώ'
- },
- 34: { # 'Ε'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 2, # 'Α'
- 51: 0, # 'Β'
- 43: 2, # 'Γ'
- 41: 2, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 2, # 'Ι'
- 44: 2, # 'Κ'
- 53: 2, # 'Λ'
- 38: 2, # 'Μ'
- 49: 2, # 'Ν'
- 59: 1, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 2, # 'Π'
- 48: 2, # 'Ρ'
- 37: 2, # 'Σ'
- 33: 2, # 'Τ'
- 45: 2, # 'Υ'
- 56: 0, # 'Φ'
- 50: 2, # 'Χ'
- 57: 2, # 'Ω'
- 17: 3, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 3, # 'ί'
- 1: 0, # 'α'
- 29: 0, # 'β'
- 20: 3, # 'γ'
- 21: 2, # 'δ'
- 3: 1, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 1, # 'θ'
- 5: 2, # 'ι'
- 11: 3, # 'κ'
- 16: 3, # 'λ'
- 10: 2, # 'μ'
- 6: 3, # 'ν'
- 30: 2, # 'ξ'
- 4: 0, # 'ο'
- 9: 3, # 'π'
- 8: 2, # 'ρ'
- 14: 0, # 'ς'
- 7: 2, # 'σ'
- 2: 2, # 'τ'
- 12: 2, # 'υ'
- 28: 2, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 1, # 'ύ'
- 27: 0, # 'ώ'
- },
- 40: { # 'Η'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 1, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 2, # 'Θ'
- 47: 0, # 'Ι'
- 44: 2, # 'Κ'
- 53: 0, # 'Λ'
- 38: 2, # 'Μ'
- 49: 2, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 2, # 'Π'
- 48: 2, # 'Ρ'
- 37: 2, # 'Σ'
- 33: 2, # 'Τ'
- 45: 1, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 0, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 0, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 0, # 'θ'
- 5: 0, # 'ι'
- 11: 0, # 'κ'
- 16: 2, # 'λ'
- 10: 0, # 'μ'
- 6: 1, # 'ν'
- 30: 0, # 'ξ'
- 4: 0, # 'ο'
- 9: 0, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 0, # 'υ'
- 28: 0, # 'φ'
- 23: 1, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 52: { # 'Θ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 2, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 2, # 'Ε'
- 40: 2, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 2, # 'Ο'
- 35: 0, # 'Π'
- 48: 1, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 1, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 2, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 3, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 2, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 0, # 'θ'
- 5: 0, # 'ι'
- 11: 0, # 'κ'
- 16: 0, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 0, # 'ο'
- 9: 0, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 2, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 2, # 'ύ'
- 27: 0, # 'ώ'
- },
- 47: { # 'Ι'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 2, # 'Α'
- 51: 1, # 'Β'
- 43: 1, # 'Γ'
- 41: 2, # 'Δ'
- 34: 2, # 'Ε'
- 40: 2, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 2, # 'Κ'
- 53: 2, # 'Λ'
- 38: 2, # 'Μ'
- 49: 2, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 2, # 'Ο'
- 35: 0, # 'Π'
- 48: 2, # 'Ρ'
- 37: 2, # 'Σ'
- 33: 2, # 'Τ'
- 45: 0, # 'Υ'
- 56: 2, # 'Φ'
- 50: 0, # 'Χ'
- 57: 2, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 2, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 2, # 'δ'
- 3: 0, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 0, # 'θ'
- 5: 0, # 'ι'
- 11: 0, # 'κ'
- 16: 0, # 'λ'
- 10: 0, # 'μ'
- 6: 1, # 'ν'
- 30: 0, # 'ξ'
- 4: 2, # 'ο'
- 9: 0, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 2, # 'σ'
- 2: 1, # 'τ'
- 12: 0, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 1, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 44: { # 'Κ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 2, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 1, # 'Δ'
- 34: 2, # 'Ε'
- 40: 2, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 1, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 2, # 'Ο'
- 35: 0, # 'Π'
- 48: 2, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 1, # 'Τ'
- 45: 2, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 1, # 'Ω'
- 17: 3, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 3, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 2, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 0, # 'θ'
- 5: 2, # 'ι'
- 11: 0, # 'κ'
- 16: 2, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 2, # 'ο'
- 9: 0, # 'π'
- 8: 2, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 2, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 2, # 'ό'
- 26: 2, # 'ύ'
- 27: 2, # 'ώ'
- },
- 53: { # 'Λ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 2, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 2, # 'Ε'
- 40: 2, # 'Η'
- 52: 0, # 'Θ'
- 47: 2, # 'Ι'
- 44: 0, # 'Κ'
- 53: 2, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 2, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 2, # 'Σ'
- 33: 0, # 'Τ'
- 45: 2, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 2, # 'Ω'
- 17: 2, # 'ά'
- 18: 2, # 'έ'
- 22: 0, # 'ή'
- 15: 2, # 'ί'
- 1: 2, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 2, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 0, # 'θ'
- 5: 1, # 'ι'
- 11: 0, # 'κ'
- 16: 0, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 2, # 'ο'
- 9: 0, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 2, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 2, # 'ό'
- 26: 2, # 'ύ'
- 27: 0, # 'ώ'
- },
- 38: { # 'Μ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 2, # 'Α'
- 51: 2, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 2, # 'Ε'
- 40: 2, # 'Η'
- 52: 0, # 'Θ'
- 47: 2, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 2, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 2, # 'Ο'
- 35: 2, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 2, # 'ά'
- 18: 2, # 'έ'
- 22: 2, # 'ή'
- 15: 2, # 'ί'
- 1: 2, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 3, # 'ε'
- 32: 0, # 'ζ'
- 13: 2, # 'η'
- 25: 0, # 'θ'
- 5: 3, # 'ι'
- 11: 0, # 'κ'
- 16: 0, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 2, # 'ο'
- 9: 3, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 2, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 2, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 49: { # 'Ν'
- 60: 2, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 2, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 2, # 'Ε'
- 40: 2, # 'Η'
- 52: 0, # 'Θ'
- 47: 2, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 2, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 2, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 2, # 'Ω'
- 17: 0, # 'ά'
- 18: 2, # 'έ'
- 22: 0, # 'ή'
- 15: 2, # 'ί'
- 1: 2, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 1, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 0, # 'θ'
- 5: 0, # 'ι'
- 11: 0, # 'κ'
- 16: 0, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 2, # 'ο'
- 9: 0, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 0, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 1, # 'ω'
- 19: 2, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 59: { # 'Ξ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 1, # 'Ε'
- 40: 1, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 1, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 2, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 2, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 2, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 0, # 'θ'
- 5: 0, # 'ι'
- 11: 0, # 'κ'
- 16: 0, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 0, # 'ο'
- 9: 0, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 0, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 39: { # 'Ο'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 1, # 'Β'
- 43: 2, # 'Γ'
- 41: 2, # 'Δ'
- 34: 2, # 'Ε'
- 40: 1, # 'Η'
- 52: 2, # 'Θ'
- 47: 2, # 'Ι'
- 44: 2, # 'Κ'
- 53: 2, # 'Λ'
- 38: 2, # 'Μ'
- 49: 2, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 2, # 'Π'
- 48: 2, # 'Ρ'
- 37: 2, # 'Σ'
- 33: 2, # 'Τ'
- 45: 2, # 'Υ'
- 56: 2, # 'Φ'
- 50: 2, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 0, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 2, # 'δ'
- 3: 0, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 0, # 'θ'
- 5: 3, # 'ι'
- 11: 2, # 'κ'
- 16: 2, # 'λ'
- 10: 2, # 'μ'
- 6: 2, # 'ν'
- 30: 0, # 'ξ'
- 4: 0, # 'ο'
- 9: 2, # 'π'
- 8: 2, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 2, # 'τ'
- 12: 2, # 'υ'
- 28: 1, # 'φ'
- 23: 1, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 2, # 'ύ'
- 27: 0, # 'ώ'
- },
- 35: { # 'Π'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 2, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 2, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 2, # 'Ι'
- 44: 0, # 'Κ'
- 53: 2, # 'Λ'
- 38: 1, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 2, # 'Ο'
- 35: 0, # 'Π'
- 48: 2, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 1, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 1, # 'Χ'
- 57: 2, # 'Ω'
- 17: 2, # 'ά'
- 18: 1, # 'έ'
- 22: 1, # 'ή'
- 15: 2, # 'ί'
- 1: 3, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 3, # 'ε'
- 32: 0, # 'ζ'
- 13: 2, # 'η'
- 25: 0, # 'θ'
- 5: 2, # 'ι'
- 11: 0, # 'κ'
- 16: 2, # 'λ'
- 10: 0, # 'μ'
- 6: 2, # 'ν'
- 30: 0, # 'ξ'
- 4: 3, # 'ο'
- 9: 0, # 'π'
- 8: 3, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 2, # 'υ'
- 28: 0, # 'φ'
- 23: 2, # 'χ'
- 42: 0, # 'ψ'
- 24: 2, # 'ω'
- 19: 2, # 'ό'
- 26: 0, # 'ύ'
- 27: 3, # 'ώ'
- },
- 48: { # 'Ρ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 2, # 'Α'
- 51: 0, # 'Β'
- 43: 1, # 'Γ'
- 41: 1, # 'Δ'
- 34: 2, # 'Ε'
- 40: 2, # 'Η'
- 52: 0, # 'Θ'
- 47: 2, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 2, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 2, # 'Ο'
- 35: 0, # 'Π'
- 48: 2, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 1, # 'Τ'
- 45: 1, # 'Υ'
- 56: 0, # 'Φ'
- 50: 1, # 'Χ'
- 57: 1, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 2, # 'ί'
- 1: 0, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 0, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 0, # 'θ'
- 5: 0, # 'ι'
- 11: 0, # 'κ'
- 16: 0, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 1, # 'ο'
- 9: 0, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 3, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 2, # 'ω'
- 19: 0, # 'ό'
- 26: 2, # 'ύ'
- 27: 0, # 'ώ'
- },
- 37: { # 'Σ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 2, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 1, # 'Δ'
- 34: 2, # 'Ε'
- 40: 2, # 'Η'
- 52: 0, # 'Θ'
- 47: 2, # 'Ι'
- 44: 2, # 'Κ'
- 53: 0, # 'Λ'
- 38: 2, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 2, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 2, # 'Σ'
- 33: 2, # 'Τ'
- 45: 2, # 'Υ'
- 56: 0, # 'Φ'
- 50: 2, # 'Χ'
- 57: 2, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 2, # 'ή'
- 15: 2, # 'ί'
- 1: 2, # 'α'
- 29: 2, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 3, # 'ε'
- 32: 0, # 'ζ'
- 13: 3, # 'η'
- 25: 0, # 'θ'
- 5: 2, # 'ι'
- 11: 2, # 'κ'
- 16: 0, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 2, # 'ο'
- 9: 2, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 3, # 'τ'
- 12: 3, # 'υ'
- 28: 0, # 'φ'
- 23: 2, # 'χ'
- 42: 0, # 'ψ'
- 24: 2, # 'ω'
- 19: 0, # 'ό'
- 26: 2, # 'ύ'
- 27: 2, # 'ώ'
- },
- 33: { # 'Τ'
- 60: 0, # 'e'
- 55: 1, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 2, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 2, # 'Ε'
- 40: 2, # 'Η'
- 52: 0, # 'Θ'
- 47: 2, # 'Ι'
- 44: 2, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 2, # 'Ο'
- 35: 0, # 'Π'
- 48: 2, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 1, # 'Τ'
- 45: 1, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 2, # 'Ω'
- 17: 2, # 'ά'
- 18: 2, # 'έ'
- 22: 0, # 'ή'
- 15: 2, # 'ί'
- 1: 3, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 2, # 'ε'
- 32: 0, # 'ζ'
- 13: 2, # 'η'
- 25: 0, # 'θ'
- 5: 2, # 'ι'
- 11: 0, # 'κ'
- 16: 0, # 'λ'
- 10: 2, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 3, # 'ο'
- 9: 0, # 'π'
- 8: 2, # 'ρ'
- 14: 0, # 'ς'
- 7: 2, # 'σ'
- 2: 0, # 'τ'
- 12: 2, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 2, # 'ό'
- 26: 2, # 'ύ'
- 27: 3, # 'ώ'
- },
- 45: { # 'Υ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 2, # 'Γ'
- 41: 0, # 'Δ'
- 34: 1, # 'Ε'
- 40: 2, # 'Η'
- 52: 2, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 1, # 'Λ'
- 38: 2, # 'Μ'
- 49: 2, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 2, # 'Π'
- 48: 1, # 'Ρ'
- 37: 2, # 'Σ'
- 33: 2, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 1, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 0, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 0, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 0, # 'θ'
- 5: 0, # 'ι'
- 11: 0, # 'κ'
- 16: 2, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 0, # 'ο'
- 9: 3, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 0, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 56: { # 'Φ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 1, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 1, # 'Η'
- 52: 0, # 'Θ'
- 47: 2, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 2, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 2, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 2, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 0, # 'θ'
- 5: 2, # 'ι'
- 11: 0, # 'κ'
- 16: 0, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 2, # 'ο'
- 9: 0, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 2, # 'τ'
- 12: 2, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 1, # 'ύ'
- 27: 1, # 'ώ'
- },
- 50: { # 'Χ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 1, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 2, # 'Ε'
- 40: 2, # 'Η'
- 52: 0, # 'Θ'
- 47: 2, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 1, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 1, # 'Ο'
- 35: 0, # 'Π'
- 48: 2, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 1, # 'Χ'
- 57: 1, # 'Ω'
- 17: 2, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 2, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 2, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 0, # 'θ'
- 5: 0, # 'ι'
- 11: 0, # 'κ'
- 16: 0, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 2, # 'ο'
- 9: 0, # 'π'
- 8: 3, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 2, # 'τ'
- 12: 0, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 2, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 57: { # 'Ω'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 1, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 1, # 'Λ'
- 38: 0, # 'Μ'
- 49: 2, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 2, # 'Ρ'
- 37: 2, # 'Σ'
- 33: 2, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 0, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 0, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 0, # 'θ'
- 5: 0, # 'ι'
- 11: 0, # 'κ'
- 16: 0, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 0, # 'ο'
- 9: 0, # 'π'
- 8: 2, # 'ρ'
- 14: 2, # 'ς'
- 7: 2, # 'σ'
- 2: 0, # 'τ'
- 12: 0, # 'υ'
- 28: 0, # 'φ'
- 23: 1, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 17: { # 'ά'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 2, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 0, # 'α'
- 29: 3, # 'β'
- 20: 3, # 'γ'
- 21: 3, # 'δ'
- 3: 3, # 'ε'
- 32: 3, # 'ζ'
- 13: 0, # 'η'
- 25: 3, # 'θ'
- 5: 2, # 'ι'
- 11: 3, # 'κ'
- 16: 3, # 'λ'
- 10: 3, # 'μ'
- 6: 3, # 'ν'
- 30: 3, # 'ξ'
- 4: 0, # 'ο'
- 9: 3, # 'π'
- 8: 3, # 'ρ'
- 14: 3, # 'ς'
- 7: 3, # 'σ'
- 2: 3, # 'τ'
- 12: 0, # 'υ'
- 28: 3, # 'φ'
- 23: 3, # 'χ'
- 42: 3, # 'ψ'
- 24: 2, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 18: { # 'έ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 3, # 'α'
- 29: 2, # 'β'
- 20: 3, # 'γ'
- 21: 2, # 'δ'
- 3: 3, # 'ε'
- 32: 2, # 'ζ'
- 13: 0, # 'η'
- 25: 3, # 'θ'
- 5: 0, # 'ι'
- 11: 3, # 'κ'
- 16: 3, # 'λ'
- 10: 3, # 'μ'
- 6: 3, # 'ν'
- 30: 3, # 'ξ'
- 4: 3, # 'ο'
- 9: 3, # 'π'
- 8: 3, # 'ρ'
- 14: 3, # 'ς'
- 7: 3, # 'σ'
- 2: 3, # 'τ'
- 12: 0, # 'υ'
- 28: 3, # 'φ'
- 23: 3, # 'χ'
- 42: 3, # 'ψ'
- 24: 2, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 22: { # 'ή'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 1, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 0, # 'α'
- 29: 0, # 'β'
- 20: 3, # 'γ'
- 21: 3, # 'δ'
- 3: 0, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 3, # 'θ'
- 5: 0, # 'ι'
- 11: 3, # 'κ'
- 16: 2, # 'λ'
- 10: 3, # 'μ'
- 6: 3, # 'ν'
- 30: 2, # 'ξ'
- 4: 0, # 'ο'
- 9: 3, # 'π'
- 8: 3, # 'ρ'
- 14: 3, # 'ς'
- 7: 3, # 'σ'
- 2: 3, # 'τ'
- 12: 0, # 'υ'
- 28: 2, # 'φ'
- 23: 3, # 'χ'
- 42: 2, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 15: { # 'ί'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 3, # 'α'
- 29: 2, # 'β'
- 20: 3, # 'γ'
- 21: 3, # 'δ'
- 3: 3, # 'ε'
- 32: 3, # 'ζ'
- 13: 3, # 'η'
- 25: 3, # 'θ'
- 5: 0, # 'ι'
- 11: 3, # 'κ'
- 16: 3, # 'λ'
- 10: 3, # 'μ'
- 6: 3, # 'ν'
- 30: 3, # 'ξ'
- 4: 3, # 'ο'
- 9: 3, # 'π'
- 8: 3, # 'ρ'
- 14: 3, # 'ς'
- 7: 3, # 'σ'
- 2: 3, # 'τ'
- 12: 0, # 'υ'
- 28: 1, # 'φ'
- 23: 3, # 'χ'
- 42: 2, # 'ψ'
- 24: 3, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 1: { # 'α'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 2, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 2, # 'έ'
- 22: 0, # 'ή'
- 15: 3, # 'ί'
- 1: 0, # 'α'
- 29: 3, # 'β'
- 20: 3, # 'γ'
- 21: 3, # 'δ'
- 3: 2, # 'ε'
- 32: 3, # 'ζ'
- 13: 1, # 'η'
- 25: 3, # 'θ'
- 5: 3, # 'ι'
- 11: 3, # 'κ'
- 16: 3, # 'λ'
- 10: 3, # 'μ'
- 6: 3, # 'ν'
- 30: 3, # 'ξ'
- 4: 2, # 'ο'
- 9: 3, # 'π'
- 8: 3, # 'ρ'
- 14: 3, # 'ς'
- 7: 3, # 'σ'
- 2: 3, # 'τ'
- 12: 3, # 'υ'
- 28: 3, # 'φ'
- 23: 3, # 'χ'
- 42: 2, # 'ψ'
- 24: 0, # 'ω'
- 19: 2, # 'ό'
- 26: 2, # 'ύ'
- 27: 0, # 'ώ'
- },
- 29: { # 'β'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 3, # 'ά'
- 18: 2, # 'έ'
- 22: 3, # 'ή'
- 15: 2, # 'ί'
- 1: 3, # 'α'
- 29: 0, # 'β'
- 20: 2, # 'γ'
- 21: 2, # 'δ'
- 3: 3, # 'ε'
- 32: 0, # 'ζ'
- 13: 2, # 'η'
- 25: 0, # 'θ'
- 5: 3, # 'ι'
- 11: 0, # 'κ'
- 16: 3, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 3, # 'ο'
- 9: 0, # 'π'
- 8: 3, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 0, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 2, # 'ω'
- 19: 2, # 'ό'
- 26: 2, # 'ύ'
- 27: 2, # 'ώ'
- },
- 20: { # 'γ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 3, # 'ά'
- 18: 3, # 'έ'
- 22: 3, # 'ή'
- 15: 3, # 'ί'
- 1: 3, # 'α'
- 29: 0, # 'β'
- 20: 3, # 'γ'
- 21: 0, # 'δ'
- 3: 3, # 'ε'
- 32: 0, # 'ζ'
- 13: 3, # 'η'
- 25: 0, # 'θ'
- 5: 3, # 'ι'
- 11: 3, # 'κ'
- 16: 3, # 'λ'
- 10: 3, # 'μ'
- 6: 3, # 'ν'
- 30: 3, # 'ξ'
- 4: 3, # 'ο'
- 9: 0, # 'π'
- 8: 3, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 2, # 'υ'
- 28: 0, # 'φ'
- 23: 3, # 'χ'
- 42: 0, # 'ψ'
- 24: 3, # 'ω'
- 19: 3, # 'ό'
- 26: 2, # 'ύ'
- 27: 3, # 'ώ'
- },
- 21: { # 'δ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 2, # 'ά'
- 18: 3, # 'έ'
- 22: 3, # 'ή'
- 15: 3, # 'ί'
- 1: 3, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 3, # 'ε'
- 32: 0, # 'ζ'
- 13: 3, # 'η'
- 25: 0, # 'θ'
- 5: 3, # 'ι'
- 11: 0, # 'κ'
- 16: 0, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 3, # 'ο'
- 9: 0, # 'π'
- 8: 3, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 3, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 3, # 'ω'
- 19: 3, # 'ό'
- 26: 3, # 'ύ'
- 27: 3, # 'ώ'
- },
- 3: { # 'ε'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 2, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 3, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 3, # 'ί'
- 1: 2, # 'α'
- 29: 3, # 'β'
- 20: 3, # 'γ'
- 21: 3, # 'δ'
- 3: 2, # 'ε'
- 32: 2, # 'ζ'
- 13: 0, # 'η'
- 25: 3, # 'θ'
- 5: 3, # 'ι'
- 11: 3, # 'κ'
- 16: 3, # 'λ'
- 10: 3, # 'μ'
- 6: 3, # 'ν'
- 30: 3, # 'ξ'
- 4: 2, # 'ο'
- 9: 3, # 'π'
- 8: 3, # 'ρ'
- 14: 3, # 'ς'
- 7: 3, # 'σ'
- 2: 3, # 'τ'
- 12: 3, # 'υ'
- 28: 3, # 'φ'
- 23: 3, # 'χ'
- 42: 2, # 'ψ'
- 24: 3, # 'ω'
- 19: 2, # 'ό'
- 26: 3, # 'ύ'
- 27: 2, # 'ώ'
- },
- 32: { # 'ζ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 2, # 'ά'
- 18: 2, # 'έ'
- 22: 2, # 'ή'
- 15: 2, # 'ί'
- 1: 2, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 3, # 'ε'
- 32: 0, # 'ζ'
- 13: 3, # 'η'
- 25: 0, # 'θ'
- 5: 2, # 'ι'
- 11: 0, # 'κ'
- 16: 0, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 3, # 'ο'
- 9: 0, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 1, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 3, # 'ω'
- 19: 2, # 'ό'
- 26: 0, # 'ύ'
- 27: 2, # 'ώ'
- },
- 13: { # 'η'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 2, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 0, # 'α'
- 29: 0, # 'β'
- 20: 3, # 'γ'
- 21: 2, # 'δ'
- 3: 0, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 3, # 'θ'
- 5: 0, # 'ι'
- 11: 3, # 'κ'
- 16: 3, # 'λ'
- 10: 3, # 'μ'
- 6: 3, # 'ν'
- 30: 2, # 'ξ'
- 4: 0, # 'ο'
- 9: 2, # 'π'
- 8: 3, # 'ρ'
- 14: 3, # 'ς'
- 7: 3, # 'σ'
- 2: 3, # 'τ'
- 12: 0, # 'υ'
- 28: 2, # 'φ'
- 23: 3, # 'χ'
- 42: 2, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 25: { # 'θ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 2, # 'ά'
- 18: 3, # 'έ'
- 22: 3, # 'ή'
- 15: 2, # 'ί'
- 1: 3, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 3, # 'ε'
- 32: 0, # 'ζ'
- 13: 3, # 'η'
- 25: 0, # 'θ'
- 5: 3, # 'ι'
- 11: 0, # 'κ'
- 16: 1, # 'λ'
- 10: 3, # 'μ'
- 6: 2, # 'ν'
- 30: 0, # 'ξ'
- 4: 3, # 'ο'
- 9: 0, # 'π'
- 8: 3, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 3, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 3, # 'ω'
- 19: 3, # 'ό'
- 26: 3, # 'ύ'
- 27: 3, # 'ώ'
- },
- 5: { # 'ι'
- 60: 0, # 'e'
- 55: 1, # 'o'
- 58: 0, # 't'
- 36: 2, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 1, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 3, # 'ά'
- 18: 3, # 'έ'
- 22: 3, # 'ή'
- 15: 0, # 'ί'
- 1: 3, # 'α'
- 29: 3, # 'β'
- 20: 3, # 'γ'
- 21: 3, # 'δ'
- 3: 3, # 'ε'
- 32: 2, # 'ζ'
- 13: 3, # 'η'
- 25: 3, # 'θ'
- 5: 0, # 'ι'
- 11: 3, # 'κ'
- 16: 3, # 'λ'
- 10: 3, # 'μ'
- 6: 3, # 'ν'
- 30: 3, # 'ξ'
- 4: 3, # 'ο'
- 9: 3, # 'π'
- 8: 3, # 'ρ'
- 14: 3, # 'ς'
- 7: 3, # 'σ'
- 2: 3, # 'τ'
- 12: 0, # 'υ'
- 28: 2, # 'φ'
- 23: 3, # 'χ'
- 42: 2, # 'ψ'
- 24: 3, # 'ω'
- 19: 3, # 'ό'
- 26: 0, # 'ύ'
- 27: 3, # 'ώ'
- },
- 11: { # 'κ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 3, # 'ά'
- 18: 3, # 'έ'
- 22: 3, # 'ή'
- 15: 3, # 'ί'
- 1: 3, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 3, # 'δ'
- 3: 3, # 'ε'
- 32: 0, # 'ζ'
- 13: 3, # 'η'
- 25: 2, # 'θ'
- 5: 3, # 'ι'
- 11: 3, # 'κ'
- 16: 3, # 'λ'
- 10: 3, # 'μ'
- 6: 2, # 'ν'
- 30: 0, # 'ξ'
- 4: 3, # 'ο'
- 9: 2, # 'π'
- 8: 3, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 3, # 'τ'
- 12: 3, # 'υ'
- 28: 2, # 'φ'
- 23: 2, # 'χ'
- 42: 0, # 'ψ'
- 24: 3, # 'ω'
- 19: 3, # 'ό'
- 26: 3, # 'ύ'
- 27: 3, # 'ώ'
- },
- 16: { # 'λ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 3, # 'ά'
- 18: 3, # 'έ'
- 22: 3, # 'ή'
- 15: 3, # 'ί'
- 1: 3, # 'α'
- 29: 1, # 'β'
- 20: 2, # 'γ'
- 21: 1, # 'δ'
- 3: 3, # 'ε'
- 32: 0, # 'ζ'
- 13: 3, # 'η'
- 25: 2, # 'θ'
- 5: 3, # 'ι'
- 11: 2, # 'κ'
- 16: 3, # 'λ'
- 10: 2, # 'μ'
- 6: 2, # 'ν'
- 30: 0, # 'ξ'
- 4: 3, # 'ο'
- 9: 3, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 3, # 'τ'
- 12: 3, # 'υ'
- 28: 2, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 3, # 'ω'
- 19: 3, # 'ό'
- 26: 3, # 'ύ'
- 27: 3, # 'ώ'
- },
- 10: { # 'μ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 1, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 3, # 'ά'
- 18: 3, # 'έ'
- 22: 3, # 'ή'
- 15: 3, # 'ί'
- 1: 3, # 'α'
- 29: 3, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 3, # 'ε'
- 32: 0, # 'ζ'
- 13: 3, # 'η'
- 25: 0, # 'θ'
- 5: 3, # 'ι'
- 11: 0, # 'κ'
- 16: 0, # 'λ'
- 10: 3, # 'μ'
- 6: 3, # 'ν'
- 30: 0, # 'ξ'
- 4: 3, # 'ο'
- 9: 3, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 2, # 'υ'
- 28: 3, # 'φ'
- 23: 0, # 'χ'
- 42: 2, # 'ψ'
- 24: 3, # 'ω'
- 19: 3, # 'ό'
- 26: 2, # 'ύ'
- 27: 2, # 'ώ'
- },
- 6: { # 'ν'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 2, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 3, # 'ά'
- 18: 3, # 'έ'
- 22: 3, # 'ή'
- 15: 3, # 'ί'
- 1: 3, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 3, # 'δ'
- 3: 3, # 'ε'
- 32: 2, # 'ζ'
- 13: 3, # 'η'
- 25: 3, # 'θ'
- 5: 3, # 'ι'
- 11: 0, # 'κ'
- 16: 1, # 'λ'
- 10: 0, # 'μ'
- 6: 2, # 'ν'
- 30: 0, # 'ξ'
- 4: 3, # 'ο'
- 9: 0, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 3, # 'σ'
- 2: 3, # 'τ'
- 12: 3, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 3, # 'ω'
- 19: 3, # 'ό'
- 26: 3, # 'ύ'
- 27: 3, # 'ώ'
- },
- 30: { # 'ξ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 2, # 'ά'
- 18: 3, # 'έ'
- 22: 3, # 'ή'
- 15: 2, # 'ί'
- 1: 3, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 3, # 'ε'
- 32: 0, # 'ζ'
- 13: 3, # 'η'
- 25: 0, # 'θ'
- 5: 2, # 'ι'
- 11: 0, # 'κ'
- 16: 0, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 3, # 'ο'
- 9: 0, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 3, # 'τ'
- 12: 2, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 3, # 'ω'
- 19: 2, # 'ό'
- 26: 3, # 'ύ'
- 27: 1, # 'ώ'
- },
- 4: { # 'ο'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 2, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 2, # 'έ'
- 22: 3, # 'ή'
- 15: 3, # 'ί'
- 1: 2, # 'α'
- 29: 3, # 'β'
- 20: 3, # 'γ'
- 21: 3, # 'δ'
- 3: 3, # 'ε'
- 32: 0, # 'ζ'
- 13: 3, # 'η'
- 25: 3, # 'θ'
- 5: 3, # 'ι'
- 11: 3, # 'κ'
- 16: 3, # 'λ'
- 10: 3, # 'μ'
- 6: 3, # 'ν'
- 30: 2, # 'ξ'
- 4: 2, # 'ο'
- 9: 3, # 'π'
- 8: 3, # 'ρ'
- 14: 3, # 'ς'
- 7: 3, # 'σ'
- 2: 3, # 'τ'
- 12: 3, # 'υ'
- 28: 3, # 'φ'
- 23: 3, # 'χ'
- 42: 2, # 'ψ'
- 24: 2, # 'ω'
- 19: 1, # 'ό'
- 26: 3, # 'ύ'
- 27: 2, # 'ώ'
- },
- 9: { # 'π'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 3, # 'ά'
- 18: 3, # 'έ'
- 22: 3, # 'ή'
- 15: 3, # 'ί'
- 1: 3, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 3, # 'ε'
- 32: 0, # 'ζ'
- 13: 3, # 'η'
- 25: 0, # 'θ'
- 5: 3, # 'ι'
- 11: 0, # 'κ'
- 16: 3, # 'λ'
- 10: 0, # 'μ'
- 6: 2, # 'ν'
- 30: 0, # 'ξ'
- 4: 3, # 'ο'
- 9: 0, # 'π'
- 8: 3, # 'ρ'
- 14: 2, # 'ς'
- 7: 0, # 'σ'
- 2: 3, # 'τ'
- 12: 3, # 'υ'
- 28: 0, # 'φ'
- 23: 2, # 'χ'
- 42: 0, # 'ψ'
- 24: 3, # 'ω'
- 19: 3, # 'ό'
- 26: 2, # 'ύ'
- 27: 3, # 'ώ'
- },
- 8: { # 'ρ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 3, # 'ά'
- 18: 3, # 'έ'
- 22: 3, # 'ή'
- 15: 3, # 'ί'
- 1: 3, # 'α'
- 29: 2, # 'β'
- 20: 3, # 'γ'
- 21: 2, # 'δ'
- 3: 3, # 'ε'
- 32: 0, # 'ζ'
- 13: 3, # 'η'
- 25: 3, # 'θ'
- 5: 3, # 'ι'
- 11: 3, # 'κ'
- 16: 1, # 'λ'
- 10: 3, # 'μ'
- 6: 3, # 'ν'
- 30: 2, # 'ξ'
- 4: 3, # 'ο'
- 9: 2, # 'π'
- 8: 2, # 'ρ'
- 14: 0, # 'ς'
- 7: 2, # 'σ'
- 2: 3, # 'τ'
- 12: 3, # 'υ'
- 28: 3, # 'φ'
- 23: 3, # 'χ'
- 42: 0, # 'ψ'
- 24: 3, # 'ω'
- 19: 3, # 'ό'
- 26: 3, # 'ύ'
- 27: 3, # 'ώ'
- },
- 14: { # 'ς'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 2, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 0, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 0, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 0, # 'θ'
- 5: 0, # 'ι'
- 11: 0, # 'κ'
- 16: 0, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 0, # 'ο'
- 9: 0, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 0, # 'τ'
- 12: 0, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 7: { # 'σ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 2, # 'ά'
- 18: 2, # 'έ'
- 22: 3, # 'ή'
- 15: 3, # 'ί'
- 1: 3, # 'α'
- 29: 3, # 'β'
- 20: 0, # 'γ'
- 21: 2, # 'δ'
- 3: 3, # 'ε'
- 32: 0, # 'ζ'
- 13: 3, # 'η'
- 25: 3, # 'θ'
- 5: 3, # 'ι'
- 11: 3, # 'κ'
- 16: 2, # 'λ'
- 10: 3, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 3, # 'ο'
- 9: 3, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 3, # 'σ'
- 2: 3, # 'τ'
- 12: 3, # 'υ'
- 28: 3, # 'φ'
- 23: 3, # 'χ'
- 42: 0, # 'ψ'
- 24: 3, # 'ω'
- 19: 3, # 'ό'
- 26: 3, # 'ύ'
- 27: 2, # 'ώ'
- },
- 2: { # 'τ'
- 60: 0, # 'e'
- 55: 2, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 3, # 'ά'
- 18: 3, # 'έ'
- 22: 3, # 'ή'
- 15: 3, # 'ί'
- 1: 3, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 3, # 'ε'
- 32: 2, # 'ζ'
- 13: 3, # 'η'
- 25: 0, # 'θ'
- 5: 3, # 'ι'
- 11: 2, # 'κ'
- 16: 2, # 'λ'
- 10: 3, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 3, # 'ο'
- 9: 0, # 'π'
- 8: 3, # 'ρ'
- 14: 0, # 'ς'
- 7: 3, # 'σ'
- 2: 3, # 'τ'
- 12: 3, # 'υ'
- 28: 2, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 3, # 'ω'
- 19: 3, # 'ό'
- 26: 3, # 'ύ'
- 27: 3, # 'ώ'
- },
- 12: { # 'υ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 2, # 'ά'
- 18: 2, # 'έ'
- 22: 3, # 'ή'
- 15: 2, # 'ί'
- 1: 3, # 'α'
- 29: 2, # 'β'
- 20: 3, # 'γ'
- 21: 2, # 'δ'
- 3: 2, # 'ε'
- 32: 2, # 'ζ'
- 13: 2, # 'η'
- 25: 3, # 'θ'
- 5: 2, # 'ι'
- 11: 3, # 'κ'
- 16: 3, # 'λ'
- 10: 3, # 'μ'
- 6: 3, # 'ν'
- 30: 3, # 'ξ'
- 4: 3, # 'ο'
- 9: 3, # 'π'
- 8: 3, # 'ρ'
- 14: 3, # 'ς'
- 7: 3, # 'σ'
- 2: 3, # 'τ'
- 12: 0, # 'υ'
- 28: 2, # 'φ'
- 23: 3, # 'χ'
- 42: 2, # 'ψ'
- 24: 2, # 'ω'
- 19: 2, # 'ό'
- 26: 0, # 'ύ'
- 27: 2, # 'ώ'
- },
- 28: { # 'φ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 3, # 'ά'
- 18: 3, # 'έ'
- 22: 3, # 'ή'
- 15: 3, # 'ί'
- 1: 3, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 3, # 'ε'
- 32: 0, # 'ζ'
- 13: 2, # 'η'
- 25: 2, # 'θ'
- 5: 3, # 'ι'
- 11: 0, # 'κ'
- 16: 2, # 'λ'
- 10: 0, # 'μ'
- 6: 1, # 'ν'
- 30: 0, # 'ξ'
- 4: 3, # 'ο'
- 9: 0, # 'π'
- 8: 3, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 3, # 'τ'
- 12: 3, # 'υ'
- 28: 1, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 3, # 'ω'
- 19: 3, # 'ό'
- 26: 2, # 'ύ'
- 27: 2, # 'ώ'
- },
- 23: { # 'χ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 3, # 'ά'
- 18: 2, # 'έ'
- 22: 3, # 'ή'
- 15: 3, # 'ί'
- 1: 3, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 3, # 'ε'
- 32: 0, # 'ζ'
- 13: 2, # 'η'
- 25: 2, # 'θ'
- 5: 3, # 'ι'
- 11: 0, # 'κ'
- 16: 2, # 'λ'
- 10: 2, # 'μ'
- 6: 3, # 'ν'
- 30: 0, # 'ξ'
- 4: 3, # 'ο'
- 9: 0, # 'π'
- 8: 3, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 3, # 'τ'
- 12: 3, # 'υ'
- 28: 0, # 'φ'
- 23: 2, # 'χ'
- 42: 0, # 'ψ'
- 24: 3, # 'ω'
- 19: 3, # 'ό'
- 26: 3, # 'ύ'
- 27: 3, # 'ώ'
- },
- 42: { # 'ψ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 2, # 'ά'
- 18: 2, # 'έ'
- 22: 1, # 'ή'
- 15: 2, # 'ί'
- 1: 2, # 'α'
- 29: 0, # 'β'
- 20: 0, # 'γ'
- 21: 0, # 'δ'
- 3: 3, # 'ε'
- 32: 0, # 'ζ'
- 13: 3, # 'η'
- 25: 0, # 'θ'
- 5: 2, # 'ι'
- 11: 0, # 'κ'
- 16: 0, # 'λ'
- 10: 0, # 'μ'
- 6: 0, # 'ν'
- 30: 0, # 'ξ'
- 4: 2, # 'ο'
- 9: 0, # 'π'
- 8: 0, # 'ρ'
- 14: 0, # 'ς'
- 7: 0, # 'σ'
- 2: 2, # 'τ'
- 12: 1, # 'υ'
- 28: 0, # 'φ'
- 23: 0, # 'χ'
- 42: 0, # 'ψ'
- 24: 2, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 24: { # 'ω'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 1, # 'ά'
- 18: 0, # 'έ'
- 22: 2, # 'ή'
- 15: 0, # 'ί'
- 1: 0, # 'α'
- 29: 2, # 'β'
- 20: 3, # 'γ'
- 21: 2, # 'δ'
- 3: 0, # 'ε'
- 32: 0, # 'ζ'
- 13: 0, # 'η'
- 25: 3, # 'θ'
- 5: 2, # 'ι'
- 11: 0, # 'κ'
- 16: 2, # 'λ'
- 10: 3, # 'μ'
- 6: 3, # 'ν'
- 30: 0, # 'ξ'
- 4: 0, # 'ο'
- 9: 3, # 'π'
- 8: 3, # 'ρ'
- 14: 3, # 'ς'
- 7: 3, # 'σ'
- 2: 3, # 'τ'
- 12: 0, # 'υ'
- 28: 2, # 'φ'
- 23: 2, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 19: { # 'ό'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 0, # 'α'
- 29: 3, # 'β'
- 20: 3, # 'γ'
- 21: 3, # 'δ'
- 3: 1, # 'ε'
- 32: 2, # 'ζ'
- 13: 2, # 'η'
- 25: 2, # 'θ'
- 5: 2, # 'ι'
- 11: 3, # 'κ'
- 16: 3, # 'λ'
- 10: 3, # 'μ'
- 6: 3, # 'ν'
- 30: 1, # 'ξ'
- 4: 2, # 'ο'
- 9: 3, # 'π'
- 8: 3, # 'ρ'
- 14: 3, # 'ς'
- 7: 3, # 'σ'
- 2: 3, # 'τ'
- 12: 0, # 'υ'
- 28: 2, # 'φ'
- 23: 3, # 'χ'
- 42: 2, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 26: { # 'ύ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 2, # 'α'
- 29: 2, # 'β'
- 20: 2, # 'γ'
- 21: 1, # 'δ'
- 3: 3, # 'ε'
- 32: 0, # 'ζ'
- 13: 2, # 'η'
- 25: 3, # 'θ'
- 5: 0, # 'ι'
- 11: 3, # 'κ'
- 16: 3, # 'λ'
- 10: 3, # 'μ'
- 6: 3, # 'ν'
- 30: 2, # 'ξ'
- 4: 3, # 'ο'
- 9: 3, # 'π'
- 8: 3, # 'ρ'
- 14: 3, # 'ς'
- 7: 3, # 'σ'
- 2: 3, # 'τ'
- 12: 0, # 'υ'
- 28: 2, # 'φ'
- 23: 2, # 'χ'
- 42: 2, # 'ψ'
- 24: 2, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
- 27: { # 'ώ'
- 60: 0, # 'e'
- 55: 0, # 'o'
- 58: 0, # 't'
- 36: 0, # '·'
- 61: 0, # 'Ά'
- 46: 0, # 'Έ'
- 54: 0, # 'Ό'
- 31: 0, # 'Α'
- 51: 0, # 'Β'
- 43: 0, # 'Γ'
- 41: 0, # 'Δ'
- 34: 0, # 'Ε'
- 40: 0, # 'Η'
- 52: 0, # 'Θ'
- 47: 0, # 'Ι'
- 44: 0, # 'Κ'
- 53: 0, # 'Λ'
- 38: 0, # 'Μ'
- 49: 0, # 'Ν'
- 59: 0, # 'Ξ'
- 39: 0, # 'Ο'
- 35: 0, # 'Π'
- 48: 0, # 'Ρ'
- 37: 0, # 'Σ'
- 33: 0, # 'Τ'
- 45: 0, # 'Υ'
- 56: 0, # 'Φ'
- 50: 0, # 'Χ'
- 57: 0, # 'Ω'
- 17: 0, # 'ά'
- 18: 0, # 'έ'
- 22: 0, # 'ή'
- 15: 0, # 'ί'
- 1: 0, # 'α'
- 29: 1, # 'β'
- 20: 0, # 'γ'
- 21: 3, # 'δ'
- 3: 0, # 'ε'
- 32: 0, # 'ζ'
- 13: 1, # 'η'
- 25: 2, # 'θ'
- 5: 2, # 'ι'
- 11: 0, # 'κ'
- 16: 2, # 'λ'
- 10: 3, # 'μ'
- 6: 3, # 'ν'
- 30: 1, # 'ξ'
- 4: 0, # 'ο'
- 9: 2, # 'π'
- 8: 3, # 'ρ'
- 14: 3, # 'ς'
- 7: 3, # 'σ'
- 2: 3, # 'τ'
- 12: 0, # 'υ'
- 28: 1, # 'φ'
- 23: 1, # 'χ'
- 42: 0, # 'ψ'
- 24: 0, # 'ω'
- 19: 0, # 'ό'
- 26: 0, # 'ύ'
- 27: 0, # 'ώ'
- },
-}
-
-# 255: Undefined characters that did not exist in training text
-# 254: Carriage/Return
-# 253: symbol (punctuation) that does not belong to word
-# 252: 0 - 9
-# 251: Control characters
-
-# Character Mapping Table(s):
-WINDOWS_1253_GREEK_CHAR_TO_ORDER = {
- 0: 255, # '\x00'
- 1: 255, # '\x01'
- 2: 255, # '\x02'
- 3: 255, # '\x03'
- 4: 255, # '\x04'
- 5: 255, # '\x05'
- 6: 255, # '\x06'
- 7: 255, # '\x07'
- 8: 255, # '\x08'
- 9: 255, # '\t'
- 10: 254, # '\n'
- 11: 255, # '\x0b'
- 12: 255, # '\x0c'
- 13: 254, # '\r'
- 14: 255, # '\x0e'
- 15: 255, # '\x0f'
- 16: 255, # '\x10'
- 17: 255, # '\x11'
- 18: 255, # '\x12'
- 19: 255, # '\x13'
- 20: 255, # '\x14'
- 21: 255, # '\x15'
- 22: 255, # '\x16'
- 23: 255, # '\x17'
- 24: 255, # '\x18'
- 25: 255, # '\x19'
- 26: 255, # '\x1a'
- 27: 255, # '\x1b'
- 28: 255, # '\x1c'
- 29: 255, # '\x1d'
- 30: 255, # '\x1e'
- 31: 255, # '\x1f'
- 32: 253, # ' '
- 33: 253, # '!'
- 34: 253, # '"'
- 35: 253, # '#'
- 36: 253, # '$'
- 37: 253, # '%'
- 38: 253, # '&'
- 39: 253, # "'"
- 40: 253, # '('
- 41: 253, # ')'
- 42: 253, # '*'
- 43: 253, # '+'
- 44: 253, # ','
- 45: 253, # '-'
- 46: 253, # '.'
- 47: 253, # '/'
- 48: 252, # '0'
- 49: 252, # '1'
- 50: 252, # '2'
- 51: 252, # '3'
- 52: 252, # '4'
- 53: 252, # '5'
- 54: 252, # '6'
- 55: 252, # '7'
- 56: 252, # '8'
- 57: 252, # '9'
- 58: 253, # ':'
- 59: 253, # ';'
- 60: 253, # '<'
- 61: 253, # '='
- 62: 253, # '>'
- 63: 253, # '?'
- 64: 253, # '@'
- 65: 82, # 'A'
- 66: 100, # 'B'
- 67: 104, # 'C'
- 68: 94, # 'D'
- 69: 98, # 'E'
- 70: 101, # 'F'
- 71: 116, # 'G'
- 72: 102, # 'H'
- 73: 111, # 'I'
- 74: 187, # 'J'
- 75: 117, # 'K'
- 76: 92, # 'L'
- 77: 88, # 'M'
- 78: 113, # 'N'
- 79: 85, # 'O'
- 80: 79, # 'P'
- 81: 118, # 'Q'
- 82: 105, # 'R'
- 83: 83, # 'S'
- 84: 67, # 'T'
- 85: 114, # 'U'
- 86: 119, # 'V'
- 87: 95, # 'W'
- 88: 99, # 'X'
- 89: 109, # 'Y'
- 90: 188, # 'Z'
- 91: 253, # '['
- 92: 253, # '\\'
- 93: 253, # ']'
- 94: 253, # '^'
- 95: 253, # '_'
- 96: 253, # '`'
- 97: 72, # 'a'
- 98: 70, # 'b'
- 99: 80, # 'c'
- 100: 81, # 'd'
- 101: 60, # 'e'
- 102: 96, # 'f'
- 103: 93, # 'g'
- 104: 89, # 'h'
- 105: 68, # 'i'
- 106: 120, # 'j'
- 107: 97, # 'k'
- 108: 77, # 'l'
- 109: 86, # 'm'
- 110: 69, # 'n'
- 111: 55, # 'o'
- 112: 78, # 'p'
- 113: 115, # 'q'
- 114: 65, # 'r'
- 115: 66, # 's'
- 116: 58, # 't'
- 117: 76, # 'u'
- 118: 106, # 'v'
- 119: 103, # 'w'
- 120: 87, # 'x'
- 121: 107, # 'y'
- 122: 112, # 'z'
- 123: 253, # '{'
- 124: 253, # '|'
- 125: 253, # '}'
- 126: 253, # '~'
- 127: 253, # '\x7f'
- 128: 255, # '€'
- 129: 255, # None
- 130: 255, # '‚'
- 131: 255, # 'ƒ'
- 132: 255, # '„'
- 133: 255, # '…'
- 134: 255, # '†'
- 135: 255, # '‡'
- 136: 255, # None
- 137: 255, # '‰'
- 138: 255, # None
- 139: 255, # '‹'
- 140: 255, # None
- 141: 255, # None
- 142: 255, # None
- 143: 255, # None
- 144: 255, # None
- 145: 255, # '‘'
- 146: 255, # '’'
- 147: 255, # '“'
- 148: 255, # '”'
- 149: 255, # '•'
- 150: 255, # '–'
- 151: 255, # '—'
- 152: 255, # None
- 153: 255, # '™'
- 154: 255, # None
- 155: 255, # '›'
- 156: 255, # None
- 157: 255, # None
- 158: 255, # None
- 159: 255, # None
- 160: 253, # '\xa0'
- 161: 233, # '΅'
- 162: 61, # 'Ά'
- 163: 253, # '£'
- 164: 253, # '¤'
- 165: 253, # '¥'
- 166: 253, # '¦'
- 167: 253, # '§'
- 168: 253, # '¨'
- 169: 253, # '©'
- 170: 253, # None
- 171: 253, # '«'
- 172: 253, # '¬'
- 173: 74, # '\xad'
- 174: 253, # '®'
- 175: 253, # '―'
- 176: 253, # '°'
- 177: 253, # '±'
- 178: 253, # '²'
- 179: 253, # '³'
- 180: 247, # '΄'
- 181: 253, # 'µ'
- 182: 253, # '¶'
- 183: 36, # '·'
- 184: 46, # 'Έ'
- 185: 71, # 'Ή'
- 186: 73, # 'Ί'
- 187: 253, # '»'
- 188: 54, # 'Ό'
- 189: 253, # '½'
- 190: 108, # 'Ύ'
- 191: 123, # 'Ώ'
- 192: 110, # 'ΐ'
- 193: 31, # 'Α'
- 194: 51, # 'Β'
- 195: 43, # 'Γ'
- 196: 41, # 'Δ'
- 197: 34, # 'Ε'
- 198: 91, # 'Ζ'
- 199: 40, # 'Η'
- 200: 52, # 'Θ'
- 201: 47, # 'Ι'
- 202: 44, # 'Κ'
- 203: 53, # 'Λ'
- 204: 38, # 'Μ'
- 205: 49, # 'Ν'
- 206: 59, # 'Ξ'
- 207: 39, # 'Ο'
- 208: 35, # 'Π'
- 209: 48, # 'Ρ'
- 210: 250, # None
- 211: 37, # 'Σ'
- 212: 33, # 'Τ'
- 213: 45, # 'Υ'
- 214: 56, # 'Φ'
- 215: 50, # 'Χ'
- 216: 84, # 'Ψ'
- 217: 57, # 'Ω'
- 218: 120, # 'Ϊ'
- 219: 121, # 'Ϋ'
- 220: 17, # 'ά'
- 221: 18, # 'έ'
- 222: 22, # 'ή'
- 223: 15, # 'ί'
- 224: 124, # 'ΰ'
- 225: 1, # 'α'
- 226: 29, # 'β'
- 227: 20, # 'γ'
- 228: 21, # 'δ'
- 229: 3, # 'ε'
- 230: 32, # 'ζ'
- 231: 13, # 'η'
- 232: 25, # 'θ'
- 233: 5, # 'ι'
- 234: 11, # 'κ'
- 235: 16, # 'λ'
- 236: 10, # 'μ'
- 237: 6, # 'ν'
- 238: 30, # 'ξ'
- 239: 4, # 'ο'
- 240: 9, # 'π'
- 241: 8, # 'ρ'
- 242: 14, # 'ς'
- 243: 7, # 'σ'
- 244: 2, # 'τ'
- 245: 12, # 'υ'
- 246: 28, # 'φ'
- 247: 23, # 'χ'
- 248: 42, # 'ψ'
- 249: 24, # 'ω'
- 250: 64, # 'ϊ'
- 251: 75, # 'ϋ'
- 252: 19, # 'ό'
- 253: 26, # 'ύ'
- 254: 27, # 'ώ'
- 255: 253, # None
-}
-
-WINDOWS_1253_GREEK_MODEL = SingleByteCharSetModel(
- charset_name="windows-1253",
- language="Greek",
- char_to_order_map=WINDOWS_1253_GREEK_CHAR_TO_ORDER,
- language_model=GREEK_LANG_MODEL,
- typical_positive_ratio=0.982851,
- keep_ascii_letters=False,
- alphabet="ΆΈΉΊΌΎΏΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩάέήίαβγδεζηθικλμνξοπρςστυφχψωόύώ",
-)
-
-ISO_8859_7_GREEK_CHAR_TO_ORDER = {
- 0: 255, # '\x00'
- 1: 255, # '\x01'
- 2: 255, # '\x02'
- 3: 255, # '\x03'
- 4: 255, # '\x04'
- 5: 255, # '\x05'
- 6: 255, # '\x06'
- 7: 255, # '\x07'
- 8: 255, # '\x08'
- 9: 255, # '\t'
- 10: 254, # '\n'
- 11: 255, # '\x0b'
- 12: 255, # '\x0c'
- 13: 254, # '\r'
- 14: 255, # '\x0e'
- 15: 255, # '\x0f'
- 16: 255, # '\x10'
- 17: 255, # '\x11'
- 18: 255, # '\x12'
- 19: 255, # '\x13'
- 20: 255, # '\x14'
- 21: 255, # '\x15'
- 22: 255, # '\x16'
- 23: 255, # '\x17'
- 24: 255, # '\x18'
- 25: 255, # '\x19'
- 26: 255, # '\x1a'
- 27: 255, # '\x1b'
- 28: 255, # '\x1c'
- 29: 255, # '\x1d'
- 30: 255, # '\x1e'
- 31: 255, # '\x1f'
- 32: 253, # ' '
- 33: 253, # '!'
- 34: 253, # '"'
- 35: 253, # '#'
- 36: 253, # '$'
- 37: 253, # '%'
- 38: 253, # '&'
- 39: 253, # "'"
- 40: 253, # '('
- 41: 253, # ')'
- 42: 253, # '*'
- 43: 253, # '+'
- 44: 253, # ','
- 45: 253, # '-'
- 46: 253, # '.'
- 47: 253, # '/'
- 48: 252, # '0'
- 49: 252, # '1'
- 50: 252, # '2'
- 51: 252, # '3'
- 52: 252, # '4'
- 53: 252, # '5'
- 54: 252, # '6'
- 55: 252, # '7'
- 56: 252, # '8'
- 57: 252, # '9'
- 58: 253, # ':'
- 59: 253, # ';'
- 60: 253, # '<'
- 61: 253, # '='
- 62: 253, # '>'
- 63: 253, # '?'
- 64: 253, # '@'
- 65: 82, # 'A'
- 66: 100, # 'B'
- 67: 104, # 'C'
- 68: 94, # 'D'
- 69: 98, # 'E'
- 70: 101, # 'F'
- 71: 116, # 'G'
- 72: 102, # 'H'
- 73: 111, # 'I'
- 74: 187, # 'J'
- 75: 117, # 'K'
- 76: 92, # 'L'
- 77: 88, # 'M'
- 78: 113, # 'N'
- 79: 85, # 'O'
- 80: 79, # 'P'
- 81: 118, # 'Q'
- 82: 105, # 'R'
- 83: 83, # 'S'
- 84: 67, # 'T'
- 85: 114, # 'U'
- 86: 119, # 'V'
- 87: 95, # 'W'
- 88: 99, # 'X'
- 89: 109, # 'Y'
- 90: 188, # 'Z'
- 91: 253, # '['
- 92: 253, # '\\'
- 93: 253, # ']'
- 94: 253, # '^'
- 95: 253, # '_'
- 96: 253, # '`'
- 97: 72, # 'a'
- 98: 70, # 'b'
- 99: 80, # 'c'
- 100: 81, # 'd'
- 101: 60, # 'e'
- 102: 96, # 'f'
- 103: 93, # 'g'
- 104: 89, # 'h'
- 105: 68, # 'i'
- 106: 120, # 'j'
- 107: 97, # 'k'
- 108: 77, # 'l'
- 109: 86, # 'm'
- 110: 69, # 'n'
- 111: 55, # 'o'
- 112: 78, # 'p'
- 113: 115, # 'q'
- 114: 65, # 'r'
- 115: 66, # 's'
- 116: 58, # 't'
- 117: 76, # 'u'
- 118: 106, # 'v'
- 119: 103, # 'w'
- 120: 87, # 'x'
- 121: 107, # 'y'
- 122: 112, # 'z'
- 123: 253, # '{'
- 124: 253, # '|'
- 125: 253, # '}'
- 126: 253, # '~'
- 127: 253, # '\x7f'
- 128: 255, # '\x80'
- 129: 255, # '\x81'
- 130: 255, # '\x82'
- 131: 255, # '\x83'
- 132: 255, # '\x84'
- 133: 255, # '\x85'
- 134: 255, # '\x86'
- 135: 255, # '\x87'
- 136: 255, # '\x88'
- 137: 255, # '\x89'
- 138: 255, # '\x8a'
- 139: 255, # '\x8b'
- 140: 255, # '\x8c'
- 141: 255, # '\x8d'
- 142: 255, # '\x8e'
- 143: 255, # '\x8f'
- 144: 255, # '\x90'
- 145: 255, # '\x91'
- 146: 255, # '\x92'
- 147: 255, # '\x93'
- 148: 255, # '\x94'
- 149: 255, # '\x95'
- 150: 255, # '\x96'
- 151: 255, # '\x97'
- 152: 255, # '\x98'
- 153: 255, # '\x99'
- 154: 255, # '\x9a'
- 155: 255, # '\x9b'
- 156: 255, # '\x9c'
- 157: 255, # '\x9d'
- 158: 255, # '\x9e'
- 159: 255, # '\x9f'
- 160: 253, # '\xa0'
- 161: 233, # '‘'
- 162: 90, # '’'
- 163: 253, # '£'
- 164: 253, # '€'
- 165: 253, # '₯'
- 166: 253, # '¦'
- 167: 253, # '§'
- 168: 253, # '¨'
- 169: 253, # '©'
- 170: 253, # 'ͺ'
- 171: 253, # '«'
- 172: 253, # '¬'
- 173: 74, # '\xad'
- 174: 253, # None
- 175: 253, # '―'
- 176: 253, # '°'
- 177: 253, # '±'
- 178: 253, # '²'
- 179: 253, # '³'
- 180: 247, # '΄'
- 181: 248, # '΅'
- 182: 61, # 'Ά'
- 183: 36, # '·'
- 184: 46, # 'Έ'
- 185: 71, # 'Ή'
- 186: 73, # 'Ί'
- 187: 253, # '»'
- 188: 54, # 'Ό'
- 189: 253, # '½'
- 190: 108, # 'Ύ'
- 191: 123, # 'Ώ'
- 192: 110, # 'ΐ'
- 193: 31, # 'Α'
- 194: 51, # 'Β'
- 195: 43, # 'Γ'
- 196: 41, # 'Δ'
- 197: 34, # 'Ε'
- 198: 91, # 'Ζ'
- 199: 40, # 'Η'
- 200: 52, # 'Θ'
- 201: 47, # 'Ι'
- 202: 44, # 'Κ'
- 203: 53, # 'Λ'
- 204: 38, # 'Μ'
- 205: 49, # 'Ν'
- 206: 59, # 'Ξ'
- 207: 39, # 'Ο'
- 208: 35, # 'Π'
- 209: 48, # 'Ρ'
- 210: 250, # None
- 211: 37, # 'Σ'
- 212: 33, # 'Τ'
- 213: 45, # 'Υ'
- 214: 56, # 'Φ'
- 215: 50, # 'Χ'
- 216: 84, # 'Ψ'
- 217: 57, # 'Ω'
- 218: 120, # 'Ϊ'
- 219: 121, # 'Ϋ'
- 220: 17, # 'ά'
- 221: 18, # 'έ'
- 222: 22, # 'ή'
- 223: 15, # 'ί'
- 224: 124, # 'ΰ'
- 225: 1, # 'α'
- 226: 29, # 'β'
- 227: 20, # 'γ'
- 228: 21, # 'δ'
- 229: 3, # 'ε'
- 230: 32, # 'ζ'
- 231: 13, # 'η'
- 232: 25, # 'θ'
- 233: 5, # 'ι'
- 234: 11, # 'κ'
- 235: 16, # 'λ'
- 236: 10, # 'μ'
- 237: 6, # 'ν'
- 238: 30, # 'ξ'
- 239: 4, # 'ο'
- 240: 9, # 'π'
- 241: 8, # 'ρ'
- 242: 14, # 'ς'
- 243: 7, # 'σ'
- 244: 2, # 'τ'
- 245: 12, # 'υ'
- 246: 28, # 'φ'
- 247: 23, # 'χ'
- 248: 42, # 'ψ'
- 249: 24, # 'ω'
- 250: 64, # 'ϊ'
- 251: 75, # 'ϋ'
- 252: 19, # 'ό'
- 253: 26, # 'ύ'
- 254: 27, # 'ώ'
- 255: 253, # None
-}
-
-ISO_8859_7_GREEK_MODEL = SingleByteCharSetModel(
- charset_name="ISO-8859-7",
- language="Greek",
- char_to_order_map=ISO_8859_7_GREEK_CHAR_TO_ORDER,
- language_model=GREEK_LANG_MODEL,
- typical_positive_ratio=0.982851,
- keep_ascii_letters=False,
- alphabet="ΆΈΉΊΌΎΏΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩάέήίαβγδεζηθικλμνξοπρςστυφχψωόύώ",
-)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/langhebrewmodel.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/langhebrewmodel.py
deleted file mode 100644
index 86b3c5e..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/langhebrewmodel.py
+++ /dev/null
@@ -1,4380 +0,0 @@
-from chardet.sbcharsetprober import SingleByteCharSetModel
-
-# 3: Positive
-# 2: Likely
-# 1: Unlikely
-# 0: Negative
-
-HEBREW_LANG_MODEL = {
- 50: { # 'a'
- 50: 0, # 'a'
- 60: 1, # 'c'
- 61: 1, # 'd'
- 42: 1, # 'e'
- 53: 1, # 'i'
- 56: 2, # 'l'
- 54: 2, # 'n'
- 49: 0, # 'o'
- 51: 2, # 'r'
- 43: 1, # 's'
- 44: 2, # 't'
- 63: 1, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 0, # 'א'
- 8: 0, # 'ב'
- 20: 0, # 'ג'
- 16: 0, # 'ד'
- 3: 1, # 'ה'
- 2: 0, # 'ו'
- 24: 0, # 'ז'
- 14: 0, # 'ח'
- 22: 0, # 'ט'
- 1: 0, # 'י'
- 25: 0, # 'ך'
- 15: 0, # 'כ'
- 4: 0, # 'ל'
- 11: 0, # 'ם'
- 6: 1, # 'מ'
- 23: 0, # 'ן'
- 12: 0, # 'נ'
- 19: 0, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 0, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 1, # 'ק'
- 7: 0, # 'ר'
- 10: 1, # 'ש'
- 5: 0, # 'ת'
- 32: 0, # '–'
- 52: 1, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 60: { # 'c'
- 50: 1, # 'a'
- 60: 1, # 'c'
- 61: 0, # 'd'
- 42: 1, # 'e'
- 53: 1, # 'i'
- 56: 1, # 'l'
- 54: 0, # 'n'
- 49: 1, # 'o'
- 51: 1, # 'r'
- 43: 1, # 's'
- 44: 2, # 't'
- 63: 1, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 1, # 'א'
- 8: 0, # 'ב'
- 20: 0, # 'ג'
- 16: 0, # 'ד'
- 3: 1, # 'ה'
- 2: 0, # 'ו'
- 24: 0, # 'ז'
- 14: 0, # 'ח'
- 22: 0, # 'ט'
- 1: 0, # 'י'
- 25: 0, # 'ך'
- 15: 0, # 'כ'
- 4: 0, # 'ל'
- 11: 0, # 'ם'
- 6: 1, # 'מ'
- 23: 0, # 'ן'
- 12: 1, # 'נ'
- 19: 0, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 0, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 0, # 'ק'
- 7: 0, # 'ר'
- 10: 0, # 'ש'
- 5: 0, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 61: { # 'd'
- 50: 1, # 'a'
- 60: 0, # 'c'
- 61: 1, # 'd'
- 42: 1, # 'e'
- 53: 1, # 'i'
- 56: 1, # 'l'
- 54: 1, # 'n'
- 49: 2, # 'o'
- 51: 1, # 'r'
- 43: 1, # 's'
- 44: 0, # 't'
- 63: 1, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 0, # 'א'
- 8: 0, # 'ב'
- 20: 0, # 'ג'
- 16: 0, # 'ד'
- 3: 1, # 'ה'
- 2: 0, # 'ו'
- 24: 0, # 'ז'
- 14: 0, # 'ח'
- 22: 0, # 'ט'
- 1: 0, # 'י'
- 25: 0, # 'ך'
- 15: 0, # 'כ'
- 4: 0, # 'ל'
- 11: 0, # 'ם'
- 6: 0, # 'מ'
- 23: 0, # 'ן'
- 12: 0, # 'נ'
- 19: 0, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 0, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 0, # 'ק'
- 7: 0, # 'ר'
- 10: 0, # 'ש'
- 5: 0, # 'ת'
- 32: 1, # '–'
- 52: 1, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 42: { # 'e'
- 50: 1, # 'a'
- 60: 1, # 'c'
- 61: 2, # 'd'
- 42: 1, # 'e'
- 53: 1, # 'i'
- 56: 2, # 'l'
- 54: 2, # 'n'
- 49: 1, # 'o'
- 51: 2, # 'r'
- 43: 2, # 's'
- 44: 2, # 't'
- 63: 1, # 'u'
- 34: 1, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 0, # 'א'
- 8: 0, # 'ב'
- 20: 0, # 'ג'
- 16: 0, # 'ד'
- 3: 0, # 'ה'
- 2: 0, # 'ו'
- 24: 0, # 'ז'
- 14: 0, # 'ח'
- 22: 0, # 'ט'
- 1: 0, # 'י'
- 25: 0, # 'ך'
- 15: 0, # 'כ'
- 4: 0, # 'ל'
- 11: 0, # 'ם'
- 6: 0, # 'מ'
- 23: 0, # 'ן'
- 12: 0, # 'נ'
- 19: 0, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 1, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 0, # 'ק'
- 7: 0, # 'ר'
- 10: 0, # 'ש'
- 5: 0, # 'ת'
- 32: 1, # '–'
- 52: 2, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 53: { # 'i'
- 50: 1, # 'a'
- 60: 2, # 'c'
- 61: 1, # 'd'
- 42: 1, # 'e'
- 53: 0, # 'i'
- 56: 1, # 'l'
- 54: 2, # 'n'
- 49: 2, # 'o'
- 51: 1, # 'r'
- 43: 2, # 's'
- 44: 2, # 't'
- 63: 1, # 'u'
- 34: 0, # '\xa0'
- 55: 1, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 0, # 'א'
- 8: 0, # 'ב'
- 20: 0, # 'ג'
- 16: 0, # 'ד'
- 3: 0, # 'ה'
- 2: 0, # 'ו'
- 24: 0, # 'ז'
- 14: 0, # 'ח'
- 22: 0, # 'ט'
- 1: 0, # 'י'
- 25: 0, # 'ך'
- 15: 0, # 'כ'
- 4: 0, # 'ל'
- 11: 0, # 'ם'
- 6: 0, # 'מ'
- 23: 0, # 'ן'
- 12: 0, # 'נ'
- 19: 0, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 0, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 0, # 'ק'
- 7: 0, # 'ר'
- 10: 0, # 'ש'
- 5: 0, # 'ת'
- 32: 0, # '–'
- 52: 1, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 56: { # 'l'
- 50: 1, # 'a'
- 60: 1, # 'c'
- 61: 1, # 'd'
- 42: 2, # 'e'
- 53: 2, # 'i'
- 56: 2, # 'l'
- 54: 1, # 'n'
- 49: 1, # 'o'
- 51: 0, # 'r'
- 43: 1, # 's'
- 44: 1, # 't'
- 63: 1, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 0, # 'א'
- 8: 0, # 'ב'
- 20: 0, # 'ג'
- 16: 0, # 'ד'
- 3: 0, # 'ה'
- 2: 0, # 'ו'
- 24: 0, # 'ז'
- 14: 0, # 'ח'
- 22: 0, # 'ט'
- 1: 0, # 'י'
- 25: 0, # 'ך'
- 15: 0, # 'כ'
- 4: 0, # 'ל'
- 11: 0, # 'ם'
- 6: 0, # 'מ'
- 23: 0, # 'ן'
- 12: 0, # 'נ'
- 19: 0, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 0, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 0, # 'ק'
- 7: 0, # 'ר'
- 10: 0, # 'ש'
- 5: 0, # 'ת'
- 32: 0, # '–'
- 52: 1, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 54: { # 'n'
- 50: 1, # 'a'
- 60: 1, # 'c'
- 61: 1, # 'd'
- 42: 1, # 'e'
- 53: 1, # 'i'
- 56: 1, # 'l'
- 54: 1, # 'n'
- 49: 1, # 'o'
- 51: 0, # 'r'
- 43: 1, # 's'
- 44: 2, # 't'
- 63: 1, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 0, # 'א'
- 8: 0, # 'ב'
- 20: 0, # 'ג'
- 16: 0, # 'ד'
- 3: 1, # 'ה'
- 2: 0, # 'ו'
- 24: 0, # 'ז'
- 14: 0, # 'ח'
- 22: 0, # 'ט'
- 1: 0, # 'י'
- 25: 0, # 'ך'
- 15: 0, # 'כ'
- 4: 0, # 'ל'
- 11: 0, # 'ם'
- 6: 0, # 'מ'
- 23: 0, # 'ן'
- 12: 0, # 'נ'
- 19: 0, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 0, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 0, # 'ק'
- 7: 0, # 'ר'
- 10: 0, # 'ש'
- 5: 0, # 'ת'
- 32: 0, # '–'
- 52: 2, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 49: { # 'o'
- 50: 1, # 'a'
- 60: 1, # 'c'
- 61: 1, # 'd'
- 42: 1, # 'e'
- 53: 1, # 'i'
- 56: 1, # 'l'
- 54: 2, # 'n'
- 49: 1, # 'o'
- 51: 2, # 'r'
- 43: 1, # 's'
- 44: 1, # 't'
- 63: 1, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 0, # 'א'
- 8: 0, # 'ב'
- 20: 0, # 'ג'
- 16: 0, # 'ד'
- 3: 0, # 'ה'
- 2: 0, # 'ו'
- 24: 0, # 'ז'
- 14: 0, # 'ח'
- 22: 0, # 'ט'
- 1: 0, # 'י'
- 25: 0, # 'ך'
- 15: 0, # 'כ'
- 4: 0, # 'ל'
- 11: 0, # 'ם'
- 6: 0, # 'מ'
- 23: 0, # 'ן'
- 12: 0, # 'נ'
- 19: 0, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 0, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 0, # 'ק'
- 7: 0, # 'ר'
- 10: 0, # 'ש'
- 5: 0, # 'ת'
- 32: 0, # '–'
- 52: 1, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 51: { # 'r'
- 50: 2, # 'a'
- 60: 1, # 'c'
- 61: 1, # 'd'
- 42: 2, # 'e'
- 53: 1, # 'i'
- 56: 1, # 'l'
- 54: 1, # 'n'
- 49: 2, # 'o'
- 51: 1, # 'r'
- 43: 1, # 's'
- 44: 1, # 't'
- 63: 1, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 0, # 'א'
- 8: 0, # 'ב'
- 20: 0, # 'ג'
- 16: 0, # 'ד'
- 3: 0, # 'ה'
- 2: 0, # 'ו'
- 24: 0, # 'ז'
- 14: 0, # 'ח'
- 22: 0, # 'ט'
- 1: 0, # 'י'
- 25: 0, # 'ך'
- 15: 0, # 'כ'
- 4: 0, # 'ל'
- 11: 0, # 'ם'
- 6: 0, # 'מ'
- 23: 0, # 'ן'
- 12: 0, # 'נ'
- 19: 0, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 0, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 0, # 'ק'
- 7: 0, # 'ר'
- 10: 0, # 'ש'
- 5: 0, # 'ת'
- 32: 0, # '–'
- 52: 2, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 43: { # 's'
- 50: 1, # 'a'
- 60: 1, # 'c'
- 61: 0, # 'd'
- 42: 2, # 'e'
- 53: 1, # 'i'
- 56: 1, # 'l'
- 54: 1, # 'n'
- 49: 1, # 'o'
- 51: 1, # 'r'
- 43: 1, # 's'
- 44: 2, # 't'
- 63: 1, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 0, # 'א'
- 8: 0, # 'ב'
- 20: 0, # 'ג'
- 16: 0, # 'ד'
- 3: 0, # 'ה'
- 2: 0, # 'ו'
- 24: 0, # 'ז'
- 14: 0, # 'ח'
- 22: 0, # 'ט'
- 1: 0, # 'י'
- 25: 0, # 'ך'
- 15: 0, # 'כ'
- 4: 0, # 'ל'
- 11: 0, # 'ם'
- 6: 0, # 'מ'
- 23: 0, # 'ן'
- 12: 0, # 'נ'
- 19: 0, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 0, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 0, # 'ק'
- 7: 0, # 'ר'
- 10: 0, # 'ש'
- 5: 0, # 'ת'
- 32: 0, # '–'
- 52: 1, # '’'
- 47: 0, # '“'
- 46: 2, # '”'
- 58: 0, # '†'
- 40: 2, # '…'
- },
- 44: { # 't'
- 50: 1, # 'a'
- 60: 1, # 'c'
- 61: 0, # 'd'
- 42: 2, # 'e'
- 53: 2, # 'i'
- 56: 1, # 'l'
- 54: 0, # 'n'
- 49: 1, # 'o'
- 51: 1, # 'r'
- 43: 1, # 's'
- 44: 1, # 't'
- 63: 1, # 'u'
- 34: 1, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 0, # 'א'
- 8: 0, # 'ב'
- 20: 0, # 'ג'
- 16: 0, # 'ד'
- 3: 0, # 'ה'
- 2: 0, # 'ו'
- 24: 0, # 'ז'
- 14: 0, # 'ח'
- 22: 0, # 'ט'
- 1: 0, # 'י'
- 25: 0, # 'ך'
- 15: 0, # 'כ'
- 4: 0, # 'ל'
- 11: 0, # 'ם'
- 6: 0, # 'מ'
- 23: 0, # 'ן'
- 12: 0, # 'נ'
- 19: 0, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 0, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 0, # 'ק'
- 7: 0, # 'ר'
- 10: 0, # 'ש'
- 5: 0, # 'ת'
- 32: 0, # '–'
- 52: 2, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 63: { # 'u'
- 50: 1, # 'a'
- 60: 1, # 'c'
- 61: 1, # 'd'
- 42: 1, # 'e'
- 53: 1, # 'i'
- 56: 1, # 'l'
- 54: 1, # 'n'
- 49: 0, # 'o'
- 51: 1, # 'r'
- 43: 2, # 's'
- 44: 1, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 0, # 'א'
- 8: 0, # 'ב'
- 20: 0, # 'ג'
- 16: 0, # 'ד'
- 3: 0, # 'ה'
- 2: 0, # 'ו'
- 24: 0, # 'ז'
- 14: 0, # 'ח'
- 22: 0, # 'ט'
- 1: 0, # 'י'
- 25: 0, # 'ך'
- 15: 0, # 'כ'
- 4: 0, # 'ל'
- 11: 0, # 'ם'
- 6: 0, # 'מ'
- 23: 0, # 'ן'
- 12: 0, # 'נ'
- 19: 0, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 0, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 0, # 'ק'
- 7: 0, # 'ר'
- 10: 0, # 'ש'
- 5: 0, # 'ת'
- 32: 0, # '–'
- 52: 1, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 34: { # '\xa0'
- 50: 1, # 'a'
- 60: 0, # 'c'
- 61: 1, # 'd'
- 42: 0, # 'e'
- 53: 1, # 'i'
- 56: 0, # 'l'
- 54: 1, # 'n'
- 49: 1, # 'o'
- 51: 0, # 'r'
- 43: 1, # 's'
- 44: 1, # 't'
- 63: 0, # 'u'
- 34: 2, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 2, # 'א'
- 8: 1, # 'ב'
- 20: 1, # 'ג'
- 16: 1, # 'ד'
- 3: 1, # 'ה'
- 2: 1, # 'ו'
- 24: 1, # 'ז'
- 14: 1, # 'ח'
- 22: 1, # 'ט'
- 1: 2, # 'י'
- 25: 0, # 'ך'
- 15: 1, # 'כ'
- 4: 1, # 'ל'
- 11: 0, # 'ם'
- 6: 2, # 'מ'
- 23: 0, # 'ן'
- 12: 1, # 'נ'
- 19: 1, # 'ס'
- 13: 1, # 'ע'
- 26: 0, # 'ף'
- 18: 1, # 'פ'
- 27: 0, # 'ץ'
- 21: 1, # 'צ'
- 17: 1, # 'ק'
- 7: 1, # 'ר'
- 10: 1, # 'ש'
- 5: 1, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 55: { # '´'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 1, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 1, # 'א'
- 8: 0, # 'ב'
- 20: 0, # 'ג'
- 16: 0, # 'ד'
- 3: 1, # 'ה'
- 2: 1, # 'ו'
- 24: 0, # 'ז'
- 14: 0, # 'ח'
- 22: 0, # 'ט'
- 1: 2, # 'י'
- 25: 0, # 'ך'
- 15: 0, # 'כ'
- 4: 1, # 'ל'
- 11: 0, # 'ם'
- 6: 1, # 'מ'
- 23: 1, # 'ן'
- 12: 1, # 'נ'
- 19: 1, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 0, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 0, # 'ק'
- 7: 1, # 'ר'
- 10: 1, # 'ש'
- 5: 0, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 48: { # '¼'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 1, # 'א'
- 8: 0, # 'ב'
- 20: 0, # 'ג'
- 16: 0, # 'ד'
- 3: 0, # 'ה'
- 2: 1, # 'ו'
- 24: 0, # 'ז'
- 14: 0, # 'ח'
- 22: 0, # 'ט'
- 1: 0, # 'י'
- 25: 0, # 'ך'
- 15: 1, # 'כ'
- 4: 1, # 'ל'
- 11: 0, # 'ם'
- 6: 1, # 'מ'
- 23: 0, # 'ן'
- 12: 0, # 'נ'
- 19: 0, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 0, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 0, # 'ק'
- 7: 0, # 'ר'
- 10: 0, # 'ש'
- 5: 0, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 39: { # '½'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 0, # 'א'
- 8: 0, # 'ב'
- 20: 0, # 'ג'
- 16: 0, # 'ד'
- 3: 0, # 'ה'
- 2: 0, # 'ו'
- 24: 0, # 'ז'
- 14: 0, # 'ח'
- 22: 0, # 'ט'
- 1: 0, # 'י'
- 25: 0, # 'ך'
- 15: 1, # 'כ'
- 4: 1, # 'ל'
- 11: 0, # 'ם'
- 6: 0, # 'מ'
- 23: 0, # 'ן'
- 12: 0, # 'נ'
- 19: 0, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 0, # 'פ'
- 27: 0, # 'ץ'
- 21: 1, # 'צ'
- 17: 1, # 'ק'
- 7: 0, # 'ר'
- 10: 0, # 'ש'
- 5: 0, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 57: { # '¾'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 0, # 'א'
- 8: 0, # 'ב'
- 20: 0, # 'ג'
- 16: 0, # 'ד'
- 3: 0, # 'ה'
- 2: 0, # 'ו'
- 24: 0, # 'ז'
- 14: 0, # 'ח'
- 22: 0, # 'ט'
- 1: 0, # 'י'
- 25: 0, # 'ך'
- 15: 0, # 'כ'
- 4: 0, # 'ל'
- 11: 0, # 'ם'
- 6: 0, # 'מ'
- 23: 0, # 'ן'
- 12: 0, # 'נ'
- 19: 0, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 0, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 0, # 'ק'
- 7: 0, # 'ר'
- 10: 0, # 'ש'
- 5: 0, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 30: { # 'ְ'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 1, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 1, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 2, # 'א'
- 8: 2, # 'ב'
- 20: 2, # 'ג'
- 16: 2, # 'ד'
- 3: 2, # 'ה'
- 2: 2, # 'ו'
- 24: 2, # 'ז'
- 14: 2, # 'ח'
- 22: 2, # 'ט'
- 1: 2, # 'י'
- 25: 2, # 'ך'
- 15: 2, # 'כ'
- 4: 2, # 'ל'
- 11: 1, # 'ם'
- 6: 2, # 'מ'
- 23: 0, # 'ן'
- 12: 2, # 'נ'
- 19: 2, # 'ס'
- 13: 2, # 'ע'
- 26: 0, # 'ף'
- 18: 2, # 'פ'
- 27: 0, # 'ץ'
- 21: 2, # 'צ'
- 17: 2, # 'ק'
- 7: 2, # 'ר'
- 10: 2, # 'ש'
- 5: 2, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 59: { # 'ֱ'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 1, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 0, # 'א'
- 8: 1, # 'ב'
- 20: 1, # 'ג'
- 16: 0, # 'ד'
- 3: 0, # 'ה'
- 2: 0, # 'ו'
- 24: 1, # 'ז'
- 14: 0, # 'ח'
- 22: 0, # 'ט'
- 1: 1, # 'י'
- 25: 0, # 'ך'
- 15: 1, # 'כ'
- 4: 2, # 'ל'
- 11: 0, # 'ם'
- 6: 2, # 'מ'
- 23: 0, # 'ן'
- 12: 1, # 'נ'
- 19: 0, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 0, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 0, # 'ק'
- 7: 1, # 'ר'
- 10: 1, # 'ש'
- 5: 0, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 41: { # 'ֲ'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 0, # 'א'
- 8: 2, # 'ב'
- 20: 1, # 'ג'
- 16: 2, # 'ד'
- 3: 1, # 'ה'
- 2: 1, # 'ו'
- 24: 1, # 'ז'
- 14: 1, # 'ח'
- 22: 1, # 'ט'
- 1: 1, # 'י'
- 25: 1, # 'ך'
- 15: 1, # 'כ'
- 4: 2, # 'ל'
- 11: 0, # 'ם'
- 6: 2, # 'מ'
- 23: 0, # 'ן'
- 12: 2, # 'נ'
- 19: 1, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 1, # 'פ'
- 27: 0, # 'ץ'
- 21: 2, # 'צ'
- 17: 1, # 'ק'
- 7: 2, # 'ר'
- 10: 2, # 'ש'
- 5: 1, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 33: { # 'ִ'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 1, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 1, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 1, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 1, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 1, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 1, # 'א'
- 8: 2, # 'ב'
- 20: 2, # 'ג'
- 16: 2, # 'ד'
- 3: 1, # 'ה'
- 2: 1, # 'ו'
- 24: 2, # 'ז'
- 14: 1, # 'ח'
- 22: 1, # 'ט'
- 1: 3, # 'י'
- 25: 1, # 'ך'
- 15: 2, # 'כ'
- 4: 2, # 'ל'
- 11: 2, # 'ם'
- 6: 2, # 'מ'
- 23: 2, # 'ן'
- 12: 2, # 'נ'
- 19: 2, # 'ס'
- 13: 1, # 'ע'
- 26: 0, # 'ף'
- 18: 2, # 'פ'
- 27: 1, # 'ץ'
- 21: 2, # 'צ'
- 17: 2, # 'ק'
- 7: 2, # 'ר'
- 10: 2, # 'ש'
- 5: 2, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 37: { # 'ֵ'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 1, # 'ֶ'
- 31: 1, # 'ַ'
- 29: 1, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 2, # 'א'
- 8: 2, # 'ב'
- 20: 1, # 'ג'
- 16: 2, # 'ד'
- 3: 2, # 'ה'
- 2: 1, # 'ו'
- 24: 1, # 'ז'
- 14: 2, # 'ח'
- 22: 1, # 'ט'
- 1: 3, # 'י'
- 25: 2, # 'ך'
- 15: 1, # 'כ'
- 4: 2, # 'ל'
- 11: 2, # 'ם'
- 6: 1, # 'מ'
- 23: 2, # 'ן'
- 12: 2, # 'נ'
- 19: 1, # 'ס'
- 13: 2, # 'ע'
- 26: 1, # 'ף'
- 18: 1, # 'פ'
- 27: 1, # 'ץ'
- 21: 1, # 'צ'
- 17: 1, # 'ק'
- 7: 2, # 'ר'
- 10: 2, # 'ש'
- 5: 2, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 36: { # 'ֶ'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 1, # 'ֶ'
- 31: 1, # 'ַ'
- 29: 1, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 2, # 'א'
- 8: 2, # 'ב'
- 20: 1, # 'ג'
- 16: 2, # 'ד'
- 3: 2, # 'ה'
- 2: 1, # 'ו'
- 24: 1, # 'ז'
- 14: 2, # 'ח'
- 22: 1, # 'ט'
- 1: 2, # 'י'
- 25: 2, # 'ך'
- 15: 1, # 'כ'
- 4: 2, # 'ל'
- 11: 2, # 'ם'
- 6: 2, # 'מ'
- 23: 2, # 'ן'
- 12: 2, # 'נ'
- 19: 2, # 'ס'
- 13: 1, # 'ע'
- 26: 1, # 'ף'
- 18: 1, # 'פ'
- 27: 2, # 'ץ'
- 21: 1, # 'צ'
- 17: 1, # 'ק'
- 7: 2, # 'ר'
- 10: 2, # 'ש'
- 5: 2, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 31: { # 'ַ'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 1, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 1, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 2, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 2, # 'א'
- 8: 2, # 'ב'
- 20: 2, # 'ג'
- 16: 2, # 'ד'
- 3: 2, # 'ה'
- 2: 1, # 'ו'
- 24: 2, # 'ז'
- 14: 2, # 'ח'
- 22: 2, # 'ט'
- 1: 3, # 'י'
- 25: 1, # 'ך'
- 15: 2, # 'כ'
- 4: 2, # 'ל'
- 11: 2, # 'ם'
- 6: 2, # 'מ'
- 23: 2, # 'ן'
- 12: 2, # 'נ'
- 19: 2, # 'ס'
- 13: 2, # 'ע'
- 26: 2, # 'ף'
- 18: 2, # 'פ'
- 27: 1, # 'ץ'
- 21: 2, # 'צ'
- 17: 2, # 'ק'
- 7: 2, # 'ר'
- 10: 2, # 'ש'
- 5: 2, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 29: { # 'ָ'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 1, # 'ַ'
- 29: 2, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 1, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 2, # 'א'
- 8: 2, # 'ב'
- 20: 2, # 'ג'
- 16: 2, # 'ד'
- 3: 3, # 'ה'
- 2: 2, # 'ו'
- 24: 2, # 'ז'
- 14: 2, # 'ח'
- 22: 1, # 'ט'
- 1: 2, # 'י'
- 25: 2, # 'ך'
- 15: 2, # 'כ'
- 4: 2, # 'ל'
- 11: 2, # 'ם'
- 6: 2, # 'מ'
- 23: 2, # 'ן'
- 12: 2, # 'נ'
- 19: 1, # 'ס'
- 13: 2, # 'ע'
- 26: 1, # 'ף'
- 18: 2, # 'פ'
- 27: 1, # 'ץ'
- 21: 2, # 'צ'
- 17: 2, # 'ק'
- 7: 2, # 'ר'
- 10: 2, # 'ש'
- 5: 2, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 35: { # 'ֹ'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 1, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 2, # 'א'
- 8: 2, # 'ב'
- 20: 1, # 'ג'
- 16: 2, # 'ד'
- 3: 2, # 'ה'
- 2: 1, # 'ו'
- 24: 1, # 'ז'
- 14: 1, # 'ח'
- 22: 1, # 'ט'
- 1: 1, # 'י'
- 25: 1, # 'ך'
- 15: 2, # 'כ'
- 4: 2, # 'ל'
- 11: 2, # 'ם'
- 6: 2, # 'מ'
- 23: 2, # 'ן'
- 12: 2, # 'נ'
- 19: 2, # 'ס'
- 13: 2, # 'ע'
- 26: 1, # 'ף'
- 18: 2, # 'פ'
- 27: 1, # 'ץ'
- 21: 2, # 'צ'
- 17: 2, # 'ק'
- 7: 2, # 'ר'
- 10: 2, # 'ש'
- 5: 2, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 62: { # 'ֻ'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 0, # 'א'
- 8: 1, # 'ב'
- 20: 1, # 'ג'
- 16: 1, # 'ד'
- 3: 1, # 'ה'
- 2: 1, # 'ו'
- 24: 1, # 'ז'
- 14: 1, # 'ח'
- 22: 0, # 'ט'
- 1: 1, # 'י'
- 25: 0, # 'ך'
- 15: 1, # 'כ'
- 4: 2, # 'ל'
- 11: 1, # 'ם'
- 6: 1, # 'מ'
- 23: 1, # 'ן'
- 12: 1, # 'נ'
- 19: 1, # 'ס'
- 13: 1, # 'ע'
- 26: 0, # 'ף'
- 18: 1, # 'פ'
- 27: 0, # 'ץ'
- 21: 1, # 'צ'
- 17: 1, # 'ק'
- 7: 1, # 'ר'
- 10: 1, # 'ש'
- 5: 1, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 28: { # 'ּ'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 3, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 1, # 'ֲ'
- 33: 3, # 'ִ'
- 37: 2, # 'ֵ'
- 36: 2, # 'ֶ'
- 31: 3, # 'ַ'
- 29: 3, # 'ָ'
- 35: 2, # 'ֹ'
- 62: 1, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 2, # 'ׁ'
- 45: 1, # 'ׂ'
- 9: 2, # 'א'
- 8: 2, # 'ב'
- 20: 1, # 'ג'
- 16: 2, # 'ד'
- 3: 1, # 'ה'
- 2: 2, # 'ו'
- 24: 1, # 'ז'
- 14: 1, # 'ח'
- 22: 1, # 'ט'
- 1: 2, # 'י'
- 25: 2, # 'ך'
- 15: 2, # 'כ'
- 4: 2, # 'ל'
- 11: 1, # 'ם'
- 6: 2, # 'מ'
- 23: 1, # 'ן'
- 12: 2, # 'נ'
- 19: 1, # 'ס'
- 13: 2, # 'ע'
- 26: 1, # 'ף'
- 18: 1, # 'פ'
- 27: 1, # 'ץ'
- 21: 1, # 'צ'
- 17: 1, # 'ק'
- 7: 2, # 'ר'
- 10: 2, # 'ש'
- 5: 2, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 38: { # 'ׁ'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 2, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 2, # 'ִ'
- 37: 2, # 'ֵ'
- 36: 2, # 'ֶ'
- 31: 2, # 'ַ'
- 29: 2, # 'ָ'
- 35: 1, # 'ֹ'
- 62: 1, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 0, # 'א'
- 8: 0, # 'ב'
- 20: 0, # 'ג'
- 16: 0, # 'ד'
- 3: 0, # 'ה'
- 2: 2, # 'ו'
- 24: 0, # 'ז'
- 14: 0, # 'ח'
- 22: 0, # 'ט'
- 1: 1, # 'י'
- 25: 0, # 'ך'
- 15: 0, # 'כ'
- 4: 0, # 'ל'
- 11: 0, # 'ם'
- 6: 0, # 'מ'
- 23: 0, # 'ן'
- 12: 0, # 'נ'
- 19: 0, # 'ס'
- 13: 1, # 'ע'
- 26: 0, # 'ף'
- 18: 0, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 0, # 'ק'
- 7: 0, # 'ר'
- 10: 0, # 'ש'
- 5: 0, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 45: { # 'ׂ'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 2, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 2, # 'ִ'
- 37: 1, # 'ֵ'
- 36: 2, # 'ֶ'
- 31: 1, # 'ַ'
- 29: 2, # 'ָ'
- 35: 1, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 1, # 'א'
- 8: 0, # 'ב'
- 20: 1, # 'ג'
- 16: 0, # 'ד'
- 3: 1, # 'ה'
- 2: 2, # 'ו'
- 24: 0, # 'ז'
- 14: 1, # 'ח'
- 22: 0, # 'ט'
- 1: 1, # 'י'
- 25: 0, # 'ך'
- 15: 0, # 'כ'
- 4: 0, # 'ל'
- 11: 1, # 'ם'
- 6: 1, # 'מ'
- 23: 0, # 'ן'
- 12: 1, # 'נ'
- 19: 0, # 'ס'
- 13: 1, # 'ע'
- 26: 0, # 'ף'
- 18: 1, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 0, # 'ק'
- 7: 1, # 'ר'
- 10: 0, # 'ש'
- 5: 1, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 9: { # 'א'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 1, # '\xa0'
- 55: 1, # '´'
- 48: 1, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 2, # 'ֱ'
- 41: 2, # 'ֲ'
- 33: 2, # 'ִ'
- 37: 2, # 'ֵ'
- 36: 2, # 'ֶ'
- 31: 2, # 'ַ'
- 29: 2, # 'ָ'
- 35: 2, # 'ֹ'
- 62: 1, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 2, # 'א'
- 8: 3, # 'ב'
- 20: 3, # 'ג'
- 16: 3, # 'ד'
- 3: 3, # 'ה'
- 2: 3, # 'ו'
- 24: 3, # 'ז'
- 14: 3, # 'ח'
- 22: 3, # 'ט'
- 1: 3, # 'י'
- 25: 3, # 'ך'
- 15: 3, # 'כ'
- 4: 3, # 'ל'
- 11: 3, # 'ם'
- 6: 3, # 'מ'
- 23: 3, # 'ן'
- 12: 3, # 'נ'
- 19: 3, # 'ס'
- 13: 2, # 'ע'
- 26: 3, # 'ף'
- 18: 3, # 'פ'
- 27: 1, # 'ץ'
- 21: 3, # 'צ'
- 17: 3, # 'ק'
- 7: 3, # 'ר'
- 10: 3, # 'ש'
- 5: 3, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 8: { # 'ב'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 1, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 1, # '\xa0'
- 55: 1, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 2, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 2, # 'ִ'
- 37: 2, # 'ֵ'
- 36: 2, # 'ֶ'
- 31: 2, # 'ַ'
- 29: 2, # 'ָ'
- 35: 2, # 'ֹ'
- 62: 1, # 'ֻ'
- 28: 3, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 3, # 'א'
- 8: 3, # 'ב'
- 20: 3, # 'ג'
- 16: 3, # 'ד'
- 3: 3, # 'ה'
- 2: 3, # 'ו'
- 24: 3, # 'ז'
- 14: 3, # 'ח'
- 22: 3, # 'ט'
- 1: 3, # 'י'
- 25: 2, # 'ך'
- 15: 3, # 'כ'
- 4: 3, # 'ל'
- 11: 2, # 'ם'
- 6: 3, # 'מ'
- 23: 3, # 'ן'
- 12: 3, # 'נ'
- 19: 3, # 'ס'
- 13: 3, # 'ע'
- 26: 1, # 'ף'
- 18: 3, # 'פ'
- 27: 2, # 'ץ'
- 21: 3, # 'צ'
- 17: 3, # 'ק'
- 7: 3, # 'ר'
- 10: 3, # 'ש'
- 5: 3, # 'ת'
- 32: 1, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 20: { # 'ג'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 1, # '\xa0'
- 55: 2, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 2, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 1, # 'ִ'
- 37: 1, # 'ֵ'
- 36: 1, # 'ֶ'
- 31: 2, # 'ַ'
- 29: 2, # 'ָ'
- 35: 1, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 2, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 2, # 'א'
- 8: 3, # 'ב'
- 20: 2, # 'ג'
- 16: 3, # 'ד'
- 3: 3, # 'ה'
- 2: 3, # 'ו'
- 24: 3, # 'ז'
- 14: 2, # 'ח'
- 22: 2, # 'ט'
- 1: 3, # 'י'
- 25: 1, # 'ך'
- 15: 1, # 'כ'
- 4: 3, # 'ל'
- 11: 3, # 'ם'
- 6: 3, # 'מ'
- 23: 3, # 'ן'
- 12: 3, # 'נ'
- 19: 2, # 'ס'
- 13: 3, # 'ע'
- 26: 2, # 'ף'
- 18: 2, # 'פ'
- 27: 1, # 'ץ'
- 21: 1, # 'צ'
- 17: 1, # 'ק'
- 7: 3, # 'ר'
- 10: 3, # 'ש'
- 5: 3, # 'ת'
- 32: 0, # '–'
- 52: 1, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 16: { # 'ד'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 2, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 2, # 'ִ'
- 37: 2, # 'ֵ'
- 36: 2, # 'ֶ'
- 31: 2, # 'ַ'
- 29: 2, # 'ָ'
- 35: 2, # 'ֹ'
- 62: 1, # 'ֻ'
- 28: 2, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 3, # 'א'
- 8: 3, # 'ב'
- 20: 3, # 'ג'
- 16: 3, # 'ד'
- 3: 3, # 'ה'
- 2: 3, # 'ו'
- 24: 1, # 'ז'
- 14: 2, # 'ח'
- 22: 2, # 'ט'
- 1: 3, # 'י'
- 25: 2, # 'ך'
- 15: 2, # 'כ'
- 4: 3, # 'ל'
- 11: 3, # 'ם'
- 6: 3, # 'מ'
- 23: 2, # 'ן'
- 12: 3, # 'נ'
- 19: 2, # 'ס'
- 13: 3, # 'ע'
- 26: 2, # 'ף'
- 18: 3, # 'פ'
- 27: 0, # 'ץ'
- 21: 2, # 'צ'
- 17: 3, # 'ק'
- 7: 3, # 'ר'
- 10: 3, # 'ש'
- 5: 3, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 3: { # 'ה'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 1, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 1, # '\xa0'
- 55: 0, # '´'
- 48: 1, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 1, # 'ְ'
- 59: 1, # 'ֱ'
- 41: 2, # 'ֲ'
- 33: 2, # 'ִ'
- 37: 2, # 'ֵ'
- 36: 2, # 'ֶ'
- 31: 3, # 'ַ'
- 29: 2, # 'ָ'
- 35: 1, # 'ֹ'
- 62: 1, # 'ֻ'
- 28: 2, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 3, # 'א'
- 8: 3, # 'ב'
- 20: 3, # 'ג'
- 16: 3, # 'ד'
- 3: 3, # 'ה'
- 2: 3, # 'ו'
- 24: 3, # 'ז'
- 14: 3, # 'ח'
- 22: 3, # 'ט'
- 1: 3, # 'י'
- 25: 1, # 'ך'
- 15: 3, # 'כ'
- 4: 3, # 'ל'
- 11: 3, # 'ם'
- 6: 3, # 'מ'
- 23: 3, # 'ן'
- 12: 3, # 'נ'
- 19: 3, # 'ס'
- 13: 3, # 'ע'
- 26: 0, # 'ף'
- 18: 3, # 'פ'
- 27: 1, # 'ץ'
- 21: 3, # 'צ'
- 17: 3, # 'ק'
- 7: 3, # 'ר'
- 10: 3, # 'ש'
- 5: 3, # 'ת'
- 32: 1, # '–'
- 52: 1, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 2, # '…'
- },
- 2: { # 'ו'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 1, # 't'
- 63: 0, # 'u'
- 34: 1, # '\xa0'
- 55: 1, # '´'
- 48: 1, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 2, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 2, # 'ִ'
- 37: 1, # 'ֵ'
- 36: 1, # 'ֶ'
- 31: 2, # 'ַ'
- 29: 2, # 'ָ'
- 35: 3, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 3, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 3, # 'א'
- 8: 3, # 'ב'
- 20: 3, # 'ג'
- 16: 3, # 'ד'
- 3: 3, # 'ה'
- 2: 3, # 'ו'
- 24: 3, # 'ז'
- 14: 3, # 'ח'
- 22: 3, # 'ט'
- 1: 3, # 'י'
- 25: 3, # 'ך'
- 15: 3, # 'כ'
- 4: 3, # 'ל'
- 11: 3, # 'ם'
- 6: 3, # 'מ'
- 23: 3, # 'ן'
- 12: 3, # 'נ'
- 19: 3, # 'ס'
- 13: 3, # 'ע'
- 26: 3, # 'ף'
- 18: 3, # 'פ'
- 27: 3, # 'ץ'
- 21: 3, # 'צ'
- 17: 3, # 'ק'
- 7: 3, # 'ר'
- 10: 3, # 'ש'
- 5: 3, # 'ת'
- 32: 1, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 2, # '…'
- },
- 24: { # 'ז'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 1, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 2, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 1, # 'ֲ'
- 33: 1, # 'ִ'
- 37: 2, # 'ֵ'
- 36: 2, # 'ֶ'
- 31: 2, # 'ַ'
- 29: 2, # 'ָ'
- 35: 1, # 'ֹ'
- 62: 1, # 'ֻ'
- 28: 2, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 3, # 'א'
- 8: 2, # 'ב'
- 20: 2, # 'ג'
- 16: 2, # 'ד'
- 3: 3, # 'ה'
- 2: 3, # 'ו'
- 24: 2, # 'ז'
- 14: 2, # 'ח'
- 22: 1, # 'ט'
- 1: 3, # 'י'
- 25: 1, # 'ך'
- 15: 3, # 'כ'
- 4: 3, # 'ל'
- 11: 2, # 'ם'
- 6: 3, # 'מ'
- 23: 2, # 'ן'
- 12: 2, # 'נ'
- 19: 1, # 'ס'
- 13: 2, # 'ע'
- 26: 1, # 'ף'
- 18: 1, # 'פ'
- 27: 0, # 'ץ'
- 21: 2, # 'צ'
- 17: 3, # 'ק'
- 7: 3, # 'ר'
- 10: 1, # 'ש'
- 5: 2, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 14: { # 'ח'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 1, # '\xa0'
- 55: 1, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 2, # 'ְ'
- 59: 1, # 'ֱ'
- 41: 2, # 'ֲ'
- 33: 2, # 'ִ'
- 37: 2, # 'ֵ'
- 36: 2, # 'ֶ'
- 31: 2, # 'ַ'
- 29: 2, # 'ָ'
- 35: 2, # 'ֹ'
- 62: 1, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 2, # 'א'
- 8: 3, # 'ב'
- 20: 2, # 'ג'
- 16: 3, # 'ד'
- 3: 3, # 'ה'
- 2: 3, # 'ו'
- 24: 3, # 'ז'
- 14: 2, # 'ח'
- 22: 2, # 'ט'
- 1: 3, # 'י'
- 25: 1, # 'ך'
- 15: 2, # 'כ'
- 4: 3, # 'ל'
- 11: 3, # 'ם'
- 6: 3, # 'מ'
- 23: 2, # 'ן'
- 12: 3, # 'נ'
- 19: 3, # 'ס'
- 13: 1, # 'ע'
- 26: 2, # 'ף'
- 18: 2, # 'פ'
- 27: 2, # 'ץ'
- 21: 3, # 'צ'
- 17: 3, # 'ק'
- 7: 3, # 'ר'
- 10: 3, # 'ש'
- 5: 3, # 'ת'
- 32: 0, # '–'
- 52: 1, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 22: { # 'ט'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 1, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 2, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 2, # 'ִ'
- 37: 1, # 'ֵ'
- 36: 1, # 'ֶ'
- 31: 2, # 'ַ'
- 29: 1, # 'ָ'
- 35: 1, # 'ֹ'
- 62: 1, # 'ֻ'
- 28: 1, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 3, # 'א'
- 8: 3, # 'ב'
- 20: 3, # 'ג'
- 16: 1, # 'ד'
- 3: 3, # 'ה'
- 2: 3, # 'ו'
- 24: 2, # 'ז'
- 14: 3, # 'ח'
- 22: 2, # 'ט'
- 1: 3, # 'י'
- 25: 1, # 'ך'
- 15: 2, # 'כ'
- 4: 3, # 'ל'
- 11: 2, # 'ם'
- 6: 2, # 'מ'
- 23: 2, # 'ן'
- 12: 3, # 'נ'
- 19: 2, # 'ס'
- 13: 3, # 'ע'
- 26: 2, # 'ף'
- 18: 3, # 'פ'
- 27: 1, # 'ץ'
- 21: 2, # 'צ'
- 17: 2, # 'ק'
- 7: 3, # 'ר'
- 10: 2, # 'ש'
- 5: 3, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 1: { # 'י'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 1, # '\xa0'
- 55: 1, # '´'
- 48: 1, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 2, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 2, # 'ִ'
- 37: 2, # 'ֵ'
- 36: 1, # 'ֶ'
- 31: 2, # 'ַ'
- 29: 2, # 'ָ'
- 35: 2, # 'ֹ'
- 62: 1, # 'ֻ'
- 28: 2, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 3, # 'א'
- 8: 3, # 'ב'
- 20: 3, # 'ג'
- 16: 3, # 'ד'
- 3: 3, # 'ה'
- 2: 3, # 'ו'
- 24: 3, # 'ז'
- 14: 3, # 'ח'
- 22: 3, # 'ט'
- 1: 3, # 'י'
- 25: 3, # 'ך'
- 15: 3, # 'כ'
- 4: 3, # 'ל'
- 11: 3, # 'ם'
- 6: 3, # 'מ'
- 23: 3, # 'ן'
- 12: 3, # 'נ'
- 19: 3, # 'ס'
- 13: 3, # 'ע'
- 26: 3, # 'ף'
- 18: 3, # 'פ'
- 27: 3, # 'ץ'
- 21: 3, # 'צ'
- 17: 3, # 'ק'
- 7: 3, # 'ר'
- 10: 3, # 'ש'
- 5: 3, # 'ת'
- 32: 1, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 2, # '…'
- },
- 25: { # 'ך'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 2, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 2, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 1, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 1, # 'א'
- 8: 0, # 'ב'
- 20: 0, # 'ג'
- 16: 0, # 'ד'
- 3: 1, # 'ה'
- 2: 0, # 'ו'
- 24: 0, # 'ז'
- 14: 1, # 'ח'
- 22: 0, # 'ט'
- 1: 0, # 'י'
- 25: 0, # 'ך'
- 15: 0, # 'כ'
- 4: 1, # 'ל'
- 11: 0, # 'ם'
- 6: 1, # 'מ'
- 23: 0, # 'ן'
- 12: 0, # 'נ'
- 19: 0, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 0, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 0, # 'ק'
- 7: 0, # 'ר'
- 10: 1, # 'ש'
- 5: 0, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 15: { # 'כ'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 2, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 2, # 'ִ'
- 37: 2, # 'ֵ'
- 36: 2, # 'ֶ'
- 31: 2, # 'ַ'
- 29: 2, # 'ָ'
- 35: 1, # 'ֹ'
- 62: 1, # 'ֻ'
- 28: 3, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 3, # 'א'
- 8: 3, # 'ב'
- 20: 2, # 'ג'
- 16: 3, # 'ד'
- 3: 3, # 'ה'
- 2: 3, # 'ו'
- 24: 3, # 'ז'
- 14: 3, # 'ח'
- 22: 2, # 'ט'
- 1: 3, # 'י'
- 25: 3, # 'ך'
- 15: 3, # 'כ'
- 4: 3, # 'ל'
- 11: 3, # 'ם'
- 6: 3, # 'מ'
- 23: 3, # 'ן'
- 12: 3, # 'נ'
- 19: 3, # 'ס'
- 13: 2, # 'ע'
- 26: 3, # 'ף'
- 18: 3, # 'פ'
- 27: 1, # 'ץ'
- 21: 2, # 'צ'
- 17: 2, # 'ק'
- 7: 3, # 'ר'
- 10: 3, # 'ש'
- 5: 3, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 4: { # 'ל'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 1, # '\xa0'
- 55: 1, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 3, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 2, # 'ִ'
- 37: 2, # 'ֵ'
- 36: 2, # 'ֶ'
- 31: 2, # 'ַ'
- 29: 2, # 'ָ'
- 35: 2, # 'ֹ'
- 62: 1, # 'ֻ'
- 28: 2, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 3, # 'א'
- 8: 3, # 'ב'
- 20: 3, # 'ג'
- 16: 3, # 'ד'
- 3: 3, # 'ה'
- 2: 3, # 'ו'
- 24: 3, # 'ז'
- 14: 3, # 'ח'
- 22: 3, # 'ט'
- 1: 3, # 'י'
- 25: 3, # 'ך'
- 15: 3, # 'כ'
- 4: 3, # 'ל'
- 11: 3, # 'ם'
- 6: 3, # 'מ'
- 23: 2, # 'ן'
- 12: 3, # 'נ'
- 19: 3, # 'ס'
- 13: 3, # 'ע'
- 26: 2, # 'ף'
- 18: 3, # 'פ'
- 27: 2, # 'ץ'
- 21: 3, # 'צ'
- 17: 3, # 'ק'
- 7: 3, # 'ר'
- 10: 3, # 'ש'
- 5: 3, # 'ת'
- 32: 1, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 11: { # 'ם'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 1, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 1, # 'א'
- 8: 1, # 'ב'
- 20: 1, # 'ג'
- 16: 0, # 'ד'
- 3: 1, # 'ה'
- 2: 1, # 'ו'
- 24: 1, # 'ז'
- 14: 1, # 'ח'
- 22: 0, # 'ט'
- 1: 1, # 'י'
- 25: 0, # 'ך'
- 15: 1, # 'כ'
- 4: 1, # 'ל'
- 11: 1, # 'ם'
- 6: 1, # 'מ'
- 23: 0, # 'ן'
- 12: 1, # 'נ'
- 19: 0, # 'ס'
- 13: 1, # 'ע'
- 26: 0, # 'ף'
- 18: 1, # 'פ'
- 27: 1, # 'ץ'
- 21: 1, # 'צ'
- 17: 1, # 'ק'
- 7: 1, # 'ר'
- 10: 1, # 'ש'
- 5: 1, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 2, # '…'
- },
- 6: { # 'מ'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 1, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 2, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 2, # 'ִ'
- 37: 2, # 'ֵ'
- 36: 2, # 'ֶ'
- 31: 2, # 'ַ'
- 29: 2, # 'ָ'
- 35: 2, # 'ֹ'
- 62: 1, # 'ֻ'
- 28: 2, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 3, # 'א'
- 8: 3, # 'ב'
- 20: 3, # 'ג'
- 16: 3, # 'ד'
- 3: 3, # 'ה'
- 2: 3, # 'ו'
- 24: 3, # 'ז'
- 14: 3, # 'ח'
- 22: 3, # 'ט'
- 1: 3, # 'י'
- 25: 2, # 'ך'
- 15: 3, # 'כ'
- 4: 3, # 'ל'
- 11: 3, # 'ם'
- 6: 3, # 'מ'
- 23: 3, # 'ן'
- 12: 3, # 'נ'
- 19: 3, # 'ס'
- 13: 3, # 'ע'
- 26: 0, # 'ף'
- 18: 3, # 'פ'
- 27: 2, # 'ץ'
- 21: 3, # 'צ'
- 17: 3, # 'ק'
- 7: 3, # 'ר'
- 10: 3, # 'ש'
- 5: 3, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 23: { # 'ן'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 1, # '\xa0'
- 55: 0, # '´'
- 48: 1, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 1, # 'א'
- 8: 1, # 'ב'
- 20: 1, # 'ג'
- 16: 1, # 'ד'
- 3: 1, # 'ה'
- 2: 1, # 'ו'
- 24: 0, # 'ז'
- 14: 1, # 'ח'
- 22: 1, # 'ט'
- 1: 1, # 'י'
- 25: 0, # 'ך'
- 15: 1, # 'כ'
- 4: 1, # 'ל'
- 11: 1, # 'ם'
- 6: 1, # 'מ'
- 23: 0, # 'ן'
- 12: 1, # 'נ'
- 19: 1, # 'ס'
- 13: 1, # 'ע'
- 26: 1, # 'ף'
- 18: 1, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 1, # 'ק'
- 7: 1, # 'ר'
- 10: 1, # 'ש'
- 5: 1, # 'ת'
- 32: 1, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 2, # '…'
- },
- 12: { # 'נ'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 2, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 2, # 'ִ'
- 37: 2, # 'ֵ'
- 36: 2, # 'ֶ'
- 31: 2, # 'ַ'
- 29: 2, # 'ָ'
- 35: 1, # 'ֹ'
- 62: 1, # 'ֻ'
- 28: 2, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 3, # 'א'
- 8: 3, # 'ב'
- 20: 3, # 'ג'
- 16: 3, # 'ד'
- 3: 3, # 'ה'
- 2: 3, # 'ו'
- 24: 3, # 'ז'
- 14: 3, # 'ח'
- 22: 3, # 'ט'
- 1: 3, # 'י'
- 25: 2, # 'ך'
- 15: 3, # 'כ'
- 4: 3, # 'ל'
- 11: 3, # 'ם'
- 6: 3, # 'מ'
- 23: 3, # 'ן'
- 12: 3, # 'נ'
- 19: 3, # 'ס'
- 13: 3, # 'ע'
- 26: 2, # 'ף'
- 18: 3, # 'פ'
- 27: 2, # 'ץ'
- 21: 3, # 'צ'
- 17: 3, # 'ק'
- 7: 3, # 'ר'
- 10: 3, # 'ש'
- 5: 3, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 19: { # 'ס'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 1, # '\xa0'
- 55: 1, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 2, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 2, # 'ִ'
- 37: 1, # 'ֵ'
- 36: 2, # 'ֶ'
- 31: 2, # 'ַ'
- 29: 1, # 'ָ'
- 35: 1, # 'ֹ'
- 62: 2, # 'ֻ'
- 28: 2, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 2, # 'א'
- 8: 3, # 'ב'
- 20: 3, # 'ג'
- 16: 3, # 'ד'
- 3: 3, # 'ה'
- 2: 3, # 'ו'
- 24: 1, # 'ז'
- 14: 3, # 'ח'
- 22: 3, # 'ט'
- 1: 3, # 'י'
- 25: 2, # 'ך'
- 15: 3, # 'כ'
- 4: 3, # 'ל'
- 11: 2, # 'ם'
- 6: 3, # 'מ'
- 23: 2, # 'ן'
- 12: 3, # 'נ'
- 19: 2, # 'ס'
- 13: 3, # 'ע'
- 26: 3, # 'ף'
- 18: 3, # 'פ'
- 27: 0, # 'ץ'
- 21: 2, # 'צ'
- 17: 3, # 'ק'
- 7: 3, # 'ר'
- 10: 1, # 'ש'
- 5: 3, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 13: { # 'ע'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 1, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 1, # 'ְ'
- 59: 1, # 'ֱ'
- 41: 2, # 'ֲ'
- 33: 2, # 'ִ'
- 37: 2, # 'ֵ'
- 36: 2, # 'ֶ'
- 31: 2, # 'ַ'
- 29: 2, # 'ָ'
- 35: 2, # 'ֹ'
- 62: 1, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 2, # 'א'
- 8: 3, # 'ב'
- 20: 3, # 'ג'
- 16: 3, # 'ד'
- 3: 3, # 'ה'
- 2: 3, # 'ו'
- 24: 3, # 'ז'
- 14: 1, # 'ח'
- 22: 3, # 'ט'
- 1: 3, # 'י'
- 25: 2, # 'ך'
- 15: 2, # 'כ'
- 4: 3, # 'ל'
- 11: 3, # 'ם'
- 6: 3, # 'מ'
- 23: 2, # 'ן'
- 12: 3, # 'נ'
- 19: 3, # 'ס'
- 13: 2, # 'ע'
- 26: 1, # 'ף'
- 18: 2, # 'פ'
- 27: 2, # 'ץ'
- 21: 3, # 'צ'
- 17: 3, # 'ק'
- 7: 3, # 'ר'
- 10: 3, # 'ש'
- 5: 3, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 26: { # 'ף'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 1, # 'א'
- 8: 0, # 'ב'
- 20: 0, # 'ג'
- 16: 0, # 'ד'
- 3: 0, # 'ה'
- 2: 1, # 'ו'
- 24: 0, # 'ז'
- 14: 1, # 'ח'
- 22: 0, # 'ט'
- 1: 0, # 'י'
- 25: 0, # 'ך'
- 15: 1, # 'כ'
- 4: 1, # 'ל'
- 11: 0, # 'ם'
- 6: 1, # 'מ'
- 23: 0, # 'ן'
- 12: 0, # 'נ'
- 19: 1, # 'ס'
- 13: 0, # 'ע'
- 26: 1, # 'ף'
- 18: 1, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 1, # 'ק'
- 7: 1, # 'ר'
- 10: 1, # 'ש'
- 5: 0, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 18: { # 'פ'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 1, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 2, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 2, # 'ִ'
- 37: 1, # 'ֵ'
- 36: 2, # 'ֶ'
- 31: 1, # 'ַ'
- 29: 2, # 'ָ'
- 35: 1, # 'ֹ'
- 62: 1, # 'ֻ'
- 28: 2, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 3, # 'א'
- 8: 2, # 'ב'
- 20: 3, # 'ג'
- 16: 2, # 'ד'
- 3: 3, # 'ה'
- 2: 3, # 'ו'
- 24: 2, # 'ז'
- 14: 3, # 'ח'
- 22: 3, # 'ט'
- 1: 3, # 'י'
- 25: 2, # 'ך'
- 15: 3, # 'כ'
- 4: 3, # 'ל'
- 11: 2, # 'ם'
- 6: 2, # 'מ'
- 23: 3, # 'ן'
- 12: 3, # 'נ'
- 19: 3, # 'ס'
- 13: 3, # 'ע'
- 26: 2, # 'ף'
- 18: 2, # 'פ'
- 27: 2, # 'ץ'
- 21: 3, # 'צ'
- 17: 3, # 'ק'
- 7: 3, # 'ר'
- 10: 3, # 'ש'
- 5: 3, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 27: { # 'ץ'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 1, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 1, # 'א'
- 8: 0, # 'ב'
- 20: 0, # 'ג'
- 16: 0, # 'ד'
- 3: 0, # 'ה'
- 2: 0, # 'ו'
- 24: 0, # 'ז'
- 14: 0, # 'ח'
- 22: 0, # 'ט'
- 1: 0, # 'י'
- 25: 0, # 'ך'
- 15: 0, # 'כ'
- 4: 1, # 'ל'
- 11: 0, # 'ם'
- 6: 0, # 'מ'
- 23: 0, # 'ן'
- 12: 0, # 'נ'
- 19: 1, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 0, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 0, # 'ק'
- 7: 1, # 'ר'
- 10: 0, # 'ש'
- 5: 1, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 21: { # 'צ'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 1, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 2, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 2, # 'ִ'
- 37: 2, # 'ֵ'
- 36: 1, # 'ֶ'
- 31: 2, # 'ַ'
- 29: 2, # 'ָ'
- 35: 1, # 'ֹ'
- 62: 1, # 'ֻ'
- 28: 2, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 3, # 'א'
- 8: 3, # 'ב'
- 20: 2, # 'ג'
- 16: 3, # 'ד'
- 3: 3, # 'ה'
- 2: 3, # 'ו'
- 24: 1, # 'ז'
- 14: 3, # 'ח'
- 22: 2, # 'ט'
- 1: 3, # 'י'
- 25: 1, # 'ך'
- 15: 1, # 'כ'
- 4: 3, # 'ל'
- 11: 2, # 'ם'
- 6: 3, # 'מ'
- 23: 2, # 'ן'
- 12: 3, # 'נ'
- 19: 1, # 'ס'
- 13: 3, # 'ע'
- 26: 2, # 'ף'
- 18: 3, # 'פ'
- 27: 2, # 'ץ'
- 21: 2, # 'צ'
- 17: 3, # 'ק'
- 7: 3, # 'ר'
- 10: 0, # 'ש'
- 5: 3, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 17: { # 'ק'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 1, # '\xa0'
- 55: 1, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 2, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 2, # 'ִ'
- 37: 2, # 'ֵ'
- 36: 1, # 'ֶ'
- 31: 2, # 'ַ'
- 29: 2, # 'ָ'
- 35: 2, # 'ֹ'
- 62: 1, # 'ֻ'
- 28: 2, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 3, # 'א'
- 8: 3, # 'ב'
- 20: 2, # 'ג'
- 16: 3, # 'ד'
- 3: 3, # 'ה'
- 2: 3, # 'ו'
- 24: 2, # 'ז'
- 14: 3, # 'ח'
- 22: 3, # 'ט'
- 1: 3, # 'י'
- 25: 1, # 'ך'
- 15: 1, # 'כ'
- 4: 3, # 'ל'
- 11: 2, # 'ם'
- 6: 3, # 'מ'
- 23: 2, # 'ן'
- 12: 3, # 'נ'
- 19: 3, # 'ס'
- 13: 3, # 'ע'
- 26: 2, # 'ף'
- 18: 3, # 'פ'
- 27: 2, # 'ץ'
- 21: 3, # 'צ'
- 17: 2, # 'ק'
- 7: 3, # 'ר'
- 10: 3, # 'ש'
- 5: 3, # 'ת'
- 32: 0, # '–'
- 52: 1, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 7: { # 'ר'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 1, # '\xa0'
- 55: 2, # '´'
- 48: 1, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 2, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 1, # 'ֲ'
- 33: 2, # 'ִ'
- 37: 2, # 'ֵ'
- 36: 2, # 'ֶ'
- 31: 2, # 'ַ'
- 29: 2, # 'ָ'
- 35: 2, # 'ֹ'
- 62: 1, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 3, # 'א'
- 8: 3, # 'ב'
- 20: 3, # 'ג'
- 16: 3, # 'ד'
- 3: 3, # 'ה'
- 2: 3, # 'ו'
- 24: 3, # 'ז'
- 14: 3, # 'ח'
- 22: 3, # 'ט'
- 1: 3, # 'י'
- 25: 3, # 'ך'
- 15: 3, # 'כ'
- 4: 3, # 'ל'
- 11: 3, # 'ם'
- 6: 3, # 'מ'
- 23: 3, # 'ן'
- 12: 3, # 'נ'
- 19: 3, # 'ס'
- 13: 3, # 'ע'
- 26: 2, # 'ף'
- 18: 3, # 'פ'
- 27: 3, # 'ץ'
- 21: 3, # 'צ'
- 17: 3, # 'ק'
- 7: 3, # 'ר'
- 10: 3, # 'ש'
- 5: 3, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 2, # '…'
- },
- 10: { # 'ש'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 1, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 1, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 1, # 'ִ'
- 37: 1, # 'ֵ'
- 36: 1, # 'ֶ'
- 31: 1, # 'ַ'
- 29: 1, # 'ָ'
- 35: 1, # 'ֹ'
- 62: 1, # 'ֻ'
- 28: 2, # 'ּ'
- 38: 3, # 'ׁ'
- 45: 2, # 'ׂ'
- 9: 3, # 'א'
- 8: 3, # 'ב'
- 20: 3, # 'ג'
- 16: 3, # 'ד'
- 3: 3, # 'ה'
- 2: 3, # 'ו'
- 24: 2, # 'ז'
- 14: 3, # 'ח'
- 22: 3, # 'ט'
- 1: 3, # 'י'
- 25: 3, # 'ך'
- 15: 3, # 'כ'
- 4: 3, # 'ל'
- 11: 3, # 'ם'
- 6: 3, # 'מ'
- 23: 2, # 'ן'
- 12: 3, # 'נ'
- 19: 2, # 'ס'
- 13: 3, # 'ע'
- 26: 2, # 'ף'
- 18: 3, # 'פ'
- 27: 1, # 'ץ'
- 21: 2, # 'צ'
- 17: 3, # 'ק'
- 7: 3, # 'ר'
- 10: 3, # 'ש'
- 5: 3, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 1, # '…'
- },
- 5: { # 'ת'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 1, # '\xa0'
- 55: 0, # '´'
- 48: 1, # '¼'
- 39: 1, # '½'
- 57: 0, # '¾'
- 30: 2, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 2, # 'ִ'
- 37: 2, # 'ֵ'
- 36: 2, # 'ֶ'
- 31: 2, # 'ַ'
- 29: 2, # 'ָ'
- 35: 1, # 'ֹ'
- 62: 1, # 'ֻ'
- 28: 2, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 3, # 'א'
- 8: 3, # 'ב'
- 20: 3, # 'ג'
- 16: 2, # 'ד'
- 3: 3, # 'ה'
- 2: 3, # 'ו'
- 24: 2, # 'ז'
- 14: 3, # 'ח'
- 22: 2, # 'ט'
- 1: 3, # 'י'
- 25: 2, # 'ך'
- 15: 3, # 'כ'
- 4: 3, # 'ל'
- 11: 3, # 'ם'
- 6: 3, # 'מ'
- 23: 3, # 'ן'
- 12: 3, # 'נ'
- 19: 2, # 'ס'
- 13: 3, # 'ע'
- 26: 2, # 'ף'
- 18: 3, # 'פ'
- 27: 1, # 'ץ'
- 21: 2, # 'צ'
- 17: 3, # 'ק'
- 7: 3, # 'ר'
- 10: 3, # 'ש'
- 5: 3, # 'ת'
- 32: 1, # '–'
- 52: 1, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 2, # '…'
- },
- 32: { # '–'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 1, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 1, # 'א'
- 8: 1, # 'ב'
- 20: 1, # 'ג'
- 16: 1, # 'ד'
- 3: 1, # 'ה'
- 2: 1, # 'ו'
- 24: 0, # 'ז'
- 14: 1, # 'ח'
- 22: 0, # 'ט'
- 1: 1, # 'י'
- 25: 0, # 'ך'
- 15: 1, # 'כ'
- 4: 1, # 'ל'
- 11: 0, # 'ם'
- 6: 1, # 'מ'
- 23: 0, # 'ן'
- 12: 0, # 'נ'
- 19: 1, # 'ס'
- 13: 1, # 'ע'
- 26: 0, # 'ף'
- 18: 1, # 'פ'
- 27: 0, # 'ץ'
- 21: 1, # 'צ'
- 17: 0, # 'ק'
- 7: 1, # 'ר'
- 10: 1, # 'ש'
- 5: 1, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 52: { # '’'
- 50: 1, # 'a'
- 60: 0, # 'c'
- 61: 1, # 'd'
- 42: 1, # 'e'
- 53: 1, # 'i'
- 56: 1, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 1, # 'r'
- 43: 2, # 's'
- 44: 2, # 't'
- 63: 1, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 0, # 'א'
- 8: 0, # 'ב'
- 20: 0, # 'ג'
- 16: 0, # 'ד'
- 3: 0, # 'ה'
- 2: 1, # 'ו'
- 24: 0, # 'ז'
- 14: 0, # 'ח'
- 22: 0, # 'ט'
- 1: 0, # 'י'
- 25: 0, # 'ך'
- 15: 0, # 'כ'
- 4: 0, # 'ל'
- 11: 0, # 'ם'
- 6: 1, # 'מ'
- 23: 0, # 'ן'
- 12: 0, # 'נ'
- 19: 0, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 0, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 0, # 'ק'
- 7: 0, # 'ר'
- 10: 0, # 'ש'
- 5: 1, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 47: { # '“'
- 50: 1, # 'a'
- 60: 1, # 'c'
- 61: 1, # 'd'
- 42: 1, # 'e'
- 53: 1, # 'i'
- 56: 1, # 'l'
- 54: 1, # 'n'
- 49: 1, # 'o'
- 51: 1, # 'r'
- 43: 1, # 's'
- 44: 1, # 't'
- 63: 1, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 2, # 'א'
- 8: 1, # 'ב'
- 20: 1, # 'ג'
- 16: 1, # 'ד'
- 3: 1, # 'ה'
- 2: 1, # 'ו'
- 24: 1, # 'ז'
- 14: 1, # 'ח'
- 22: 1, # 'ט'
- 1: 1, # 'י'
- 25: 0, # 'ך'
- 15: 1, # 'כ'
- 4: 1, # 'ל'
- 11: 0, # 'ם'
- 6: 1, # 'מ'
- 23: 0, # 'ן'
- 12: 1, # 'נ'
- 19: 1, # 'ס'
- 13: 1, # 'ע'
- 26: 0, # 'ף'
- 18: 1, # 'פ'
- 27: 0, # 'ץ'
- 21: 1, # 'צ'
- 17: 1, # 'ק'
- 7: 1, # 'ר'
- 10: 1, # 'ש'
- 5: 1, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 46: { # '”'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 1, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 1, # 'א'
- 8: 1, # 'ב'
- 20: 1, # 'ג'
- 16: 0, # 'ד'
- 3: 0, # 'ה'
- 2: 0, # 'ו'
- 24: 0, # 'ז'
- 14: 0, # 'ח'
- 22: 0, # 'ט'
- 1: 1, # 'י'
- 25: 0, # 'ך'
- 15: 1, # 'כ'
- 4: 1, # 'ל'
- 11: 0, # 'ם'
- 6: 1, # 'מ'
- 23: 0, # 'ן'
- 12: 0, # 'נ'
- 19: 0, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 0, # 'פ'
- 27: 0, # 'ץ'
- 21: 1, # 'צ'
- 17: 0, # 'ק'
- 7: 1, # 'ר'
- 10: 0, # 'ש'
- 5: 0, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 0, # '†'
- 40: 0, # '…'
- },
- 58: { # '†'
- 50: 0, # 'a'
- 60: 0, # 'c'
- 61: 0, # 'd'
- 42: 0, # 'e'
- 53: 0, # 'i'
- 56: 0, # 'l'
- 54: 0, # 'n'
- 49: 0, # 'o'
- 51: 0, # 'r'
- 43: 0, # 's'
- 44: 0, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 0, # 'א'
- 8: 0, # 'ב'
- 20: 0, # 'ג'
- 16: 0, # 'ד'
- 3: 0, # 'ה'
- 2: 0, # 'ו'
- 24: 0, # 'ז'
- 14: 0, # 'ח'
- 22: 0, # 'ט'
- 1: 0, # 'י'
- 25: 0, # 'ך'
- 15: 0, # 'כ'
- 4: 0, # 'ל'
- 11: 0, # 'ם'
- 6: 0, # 'מ'
- 23: 0, # 'ן'
- 12: 0, # 'נ'
- 19: 0, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 0, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 0, # 'ק'
- 7: 0, # 'ר'
- 10: 0, # 'ש'
- 5: 0, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 0, # '”'
- 58: 2, # '†'
- 40: 0, # '…'
- },
- 40: { # '…'
- 50: 1, # 'a'
- 60: 1, # 'c'
- 61: 1, # 'd'
- 42: 1, # 'e'
- 53: 1, # 'i'
- 56: 0, # 'l'
- 54: 1, # 'n'
- 49: 0, # 'o'
- 51: 1, # 'r'
- 43: 1, # 's'
- 44: 1, # 't'
- 63: 0, # 'u'
- 34: 0, # '\xa0'
- 55: 0, # '´'
- 48: 0, # '¼'
- 39: 0, # '½'
- 57: 0, # '¾'
- 30: 0, # 'ְ'
- 59: 0, # 'ֱ'
- 41: 0, # 'ֲ'
- 33: 0, # 'ִ'
- 37: 0, # 'ֵ'
- 36: 0, # 'ֶ'
- 31: 0, # 'ַ'
- 29: 0, # 'ָ'
- 35: 0, # 'ֹ'
- 62: 0, # 'ֻ'
- 28: 0, # 'ּ'
- 38: 0, # 'ׁ'
- 45: 0, # 'ׂ'
- 9: 1, # 'א'
- 8: 0, # 'ב'
- 20: 0, # 'ג'
- 16: 0, # 'ד'
- 3: 1, # 'ה'
- 2: 1, # 'ו'
- 24: 1, # 'ז'
- 14: 0, # 'ח'
- 22: 0, # 'ט'
- 1: 1, # 'י'
- 25: 0, # 'ך'
- 15: 1, # 'כ'
- 4: 1, # 'ל'
- 11: 0, # 'ם'
- 6: 1, # 'מ'
- 23: 0, # 'ן'
- 12: 1, # 'נ'
- 19: 0, # 'ס'
- 13: 0, # 'ע'
- 26: 0, # 'ף'
- 18: 1, # 'פ'
- 27: 0, # 'ץ'
- 21: 0, # 'צ'
- 17: 0, # 'ק'
- 7: 1, # 'ר'
- 10: 1, # 'ש'
- 5: 1, # 'ת'
- 32: 0, # '–'
- 52: 0, # '’'
- 47: 0, # '“'
- 46: 1, # '”'
- 58: 0, # '†'
- 40: 2, # '…'
- },
-}
-
-# 255: Undefined characters that did not exist in training text
-# 254: Carriage/Return
-# 253: symbol (punctuation) that does not belong to word
-# 252: 0 - 9
-# 251: Control characters
-
-# Character Mapping Table(s):
-WINDOWS_1255_HEBREW_CHAR_TO_ORDER = {
- 0: 255, # '\x00'
- 1: 255, # '\x01'
- 2: 255, # '\x02'
- 3: 255, # '\x03'
- 4: 255, # '\x04'
- 5: 255, # '\x05'
- 6: 255, # '\x06'
- 7: 255, # '\x07'
- 8: 255, # '\x08'
- 9: 255, # '\t'
- 10: 254, # '\n'
- 11: 255, # '\x0b'
- 12: 255, # '\x0c'
- 13: 254, # '\r'
- 14: 255, # '\x0e'
- 15: 255, # '\x0f'
- 16: 255, # '\x10'
- 17: 255, # '\x11'
- 18: 255, # '\x12'
- 19: 255, # '\x13'
- 20: 255, # '\x14'
- 21: 255, # '\x15'
- 22: 255, # '\x16'
- 23: 255, # '\x17'
- 24: 255, # '\x18'
- 25: 255, # '\x19'
- 26: 255, # '\x1a'
- 27: 255, # '\x1b'
- 28: 255, # '\x1c'
- 29: 255, # '\x1d'
- 30: 255, # '\x1e'
- 31: 255, # '\x1f'
- 32: 253, # ' '
- 33: 253, # '!'
- 34: 253, # '"'
- 35: 253, # '#'
- 36: 253, # '$'
- 37: 253, # '%'
- 38: 253, # '&'
- 39: 253, # "'"
- 40: 253, # '('
- 41: 253, # ')'
- 42: 253, # '*'
- 43: 253, # '+'
- 44: 253, # ','
- 45: 253, # '-'
- 46: 253, # '.'
- 47: 253, # '/'
- 48: 252, # '0'
- 49: 252, # '1'
- 50: 252, # '2'
- 51: 252, # '3'
- 52: 252, # '4'
- 53: 252, # '5'
- 54: 252, # '6'
- 55: 252, # '7'
- 56: 252, # '8'
- 57: 252, # '9'
- 58: 253, # ':'
- 59: 253, # ';'
- 60: 253, # '<'
- 61: 253, # '='
- 62: 253, # '>'
- 63: 253, # '?'
- 64: 253, # '@'
- 65: 69, # 'A'
- 66: 91, # 'B'
- 67: 79, # 'C'
- 68: 80, # 'D'
- 69: 92, # 'E'
- 70: 89, # 'F'
- 71: 97, # 'G'
- 72: 90, # 'H'
- 73: 68, # 'I'
- 74: 111, # 'J'
- 75: 112, # 'K'
- 76: 82, # 'L'
- 77: 73, # 'M'
- 78: 95, # 'N'
- 79: 85, # 'O'
- 80: 78, # 'P'
- 81: 121, # 'Q'
- 82: 86, # 'R'
- 83: 71, # 'S'
- 84: 67, # 'T'
- 85: 102, # 'U'
- 86: 107, # 'V'
- 87: 84, # 'W'
- 88: 114, # 'X'
- 89: 103, # 'Y'
- 90: 115, # 'Z'
- 91: 253, # '['
- 92: 253, # '\\'
- 93: 253, # ']'
- 94: 253, # '^'
- 95: 253, # '_'
- 96: 253, # '`'
- 97: 50, # 'a'
- 98: 74, # 'b'
- 99: 60, # 'c'
- 100: 61, # 'd'
- 101: 42, # 'e'
- 102: 76, # 'f'
- 103: 70, # 'g'
- 104: 64, # 'h'
- 105: 53, # 'i'
- 106: 105, # 'j'
- 107: 93, # 'k'
- 108: 56, # 'l'
- 109: 65, # 'm'
- 110: 54, # 'n'
- 111: 49, # 'o'
- 112: 66, # 'p'
- 113: 110, # 'q'
- 114: 51, # 'r'
- 115: 43, # 's'
- 116: 44, # 't'
- 117: 63, # 'u'
- 118: 81, # 'v'
- 119: 77, # 'w'
- 120: 98, # 'x'
- 121: 75, # 'y'
- 122: 108, # 'z'
- 123: 253, # '{'
- 124: 253, # '|'
- 125: 253, # '}'
- 126: 253, # '~'
- 127: 253, # '\x7f'
- 128: 124, # '€'
- 129: 202, # None
- 130: 203, # '‚'
- 131: 204, # 'ƒ'
- 132: 205, # '„'
- 133: 40, # '…'
- 134: 58, # '†'
- 135: 206, # '‡'
- 136: 207, # 'ˆ'
- 137: 208, # '‰'
- 138: 209, # None
- 139: 210, # '‹'
- 140: 211, # None
- 141: 212, # None
- 142: 213, # None
- 143: 214, # None
- 144: 215, # None
- 145: 83, # '‘'
- 146: 52, # '’'
- 147: 47, # '“'
- 148: 46, # '”'
- 149: 72, # '•'
- 150: 32, # '–'
- 151: 94, # '—'
- 152: 216, # '˜'
- 153: 113, # '™'
- 154: 217, # None
- 155: 109, # '›'
- 156: 218, # None
- 157: 219, # None
- 158: 220, # None
- 159: 221, # None
- 160: 34, # '\xa0'
- 161: 116, # '¡'
- 162: 222, # '¢'
- 163: 118, # '£'
- 164: 100, # '₪'
- 165: 223, # '¥'
- 166: 224, # '¦'
- 167: 117, # '§'
- 168: 119, # '¨'
- 169: 104, # '©'
- 170: 125, # '×'
- 171: 225, # '«'
- 172: 226, # '¬'
- 173: 87, # '\xad'
- 174: 99, # '®'
- 175: 227, # '¯'
- 176: 106, # '°'
- 177: 122, # '±'
- 178: 123, # '²'
- 179: 228, # '³'
- 180: 55, # '´'
- 181: 229, # 'µ'
- 182: 230, # '¶'
- 183: 101, # '·'
- 184: 231, # '¸'
- 185: 232, # '¹'
- 186: 120, # '÷'
- 187: 233, # '»'
- 188: 48, # '¼'
- 189: 39, # '½'
- 190: 57, # '¾'
- 191: 234, # '¿'
- 192: 30, # 'ְ'
- 193: 59, # 'ֱ'
- 194: 41, # 'ֲ'
- 195: 88, # 'ֳ'
- 196: 33, # 'ִ'
- 197: 37, # 'ֵ'
- 198: 36, # 'ֶ'
- 199: 31, # 'ַ'
- 200: 29, # 'ָ'
- 201: 35, # 'ֹ'
- 202: 235, # None
- 203: 62, # 'ֻ'
- 204: 28, # 'ּ'
- 205: 236, # 'ֽ'
- 206: 126, # '־'
- 207: 237, # 'ֿ'
- 208: 238, # '׀'
- 209: 38, # 'ׁ'
- 210: 45, # 'ׂ'
- 211: 239, # '׃'
- 212: 240, # 'װ'
- 213: 241, # 'ױ'
- 214: 242, # 'ײ'
- 215: 243, # '׳'
- 216: 127, # '״'
- 217: 244, # None
- 218: 245, # None
- 219: 246, # None
- 220: 247, # None
- 221: 248, # None
- 222: 249, # None
- 223: 250, # None
- 224: 9, # 'א'
- 225: 8, # 'ב'
- 226: 20, # 'ג'
- 227: 16, # 'ד'
- 228: 3, # 'ה'
- 229: 2, # 'ו'
- 230: 24, # 'ז'
- 231: 14, # 'ח'
- 232: 22, # 'ט'
- 233: 1, # 'י'
- 234: 25, # 'ך'
- 235: 15, # 'כ'
- 236: 4, # 'ל'
- 237: 11, # 'ם'
- 238: 6, # 'מ'
- 239: 23, # 'ן'
- 240: 12, # 'נ'
- 241: 19, # 'ס'
- 242: 13, # 'ע'
- 243: 26, # 'ף'
- 244: 18, # 'פ'
- 245: 27, # 'ץ'
- 246: 21, # 'צ'
- 247: 17, # 'ק'
- 248: 7, # 'ר'
- 249: 10, # 'ש'
- 250: 5, # 'ת'
- 251: 251, # None
- 252: 252, # None
- 253: 128, # '\u200e'
- 254: 96, # '\u200f'
- 255: 253, # None
-}
-
-WINDOWS_1255_HEBREW_MODEL = SingleByteCharSetModel(
- charset_name="windows-1255",
- language="Hebrew",
- char_to_order_map=WINDOWS_1255_HEBREW_CHAR_TO_ORDER,
- language_model=HEBREW_LANG_MODEL,
- typical_positive_ratio=0.984004,
- keep_ascii_letters=False,
- alphabet="אבגדהוזחטיךכלםמןנסעףפץצקרשתװױײ",
-)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/langhungarianmodel.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/langhungarianmodel.py
deleted file mode 100644
index bd6630a..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/langhungarianmodel.py
+++ /dev/null
@@ -1,4649 +0,0 @@
-from chardet.sbcharsetprober import SingleByteCharSetModel
-
-# 3: Positive
-# 2: Likely
-# 1: Unlikely
-# 0: Negative
-
-HUNGARIAN_LANG_MODEL = {
- 28: { # 'A'
- 28: 0, # 'A'
- 40: 1, # 'B'
- 54: 1, # 'C'
- 45: 2, # 'D'
- 32: 1, # 'E'
- 50: 1, # 'F'
- 49: 2, # 'G'
- 38: 1, # 'H'
- 39: 2, # 'I'
- 53: 1, # 'J'
- 36: 2, # 'K'
- 41: 2, # 'L'
- 34: 1, # 'M'
- 35: 2, # 'N'
- 47: 1, # 'O'
- 46: 2, # 'P'
- 43: 2, # 'R'
- 33: 2, # 'S'
- 37: 2, # 'T'
- 57: 1, # 'U'
- 48: 1, # 'V'
- 55: 1, # 'Y'
- 52: 2, # 'Z'
- 2: 0, # 'a'
- 18: 1, # 'b'
- 26: 1, # 'c'
- 17: 2, # 'd'
- 1: 1, # 'e'
- 27: 1, # 'f'
- 12: 1, # 'g'
- 20: 1, # 'h'
- 9: 1, # 'i'
- 22: 1, # 'j'
- 7: 2, # 'k'
- 6: 2, # 'l'
- 13: 2, # 'm'
- 4: 2, # 'n'
- 8: 0, # 'o'
- 23: 2, # 'p'
- 10: 2, # 'r'
- 5: 1, # 's'
- 3: 1, # 't'
- 21: 1, # 'u'
- 19: 1, # 'v'
- 62: 1, # 'x'
- 16: 0, # 'y'
- 11: 3, # 'z'
- 51: 1, # 'Á'
- 44: 0, # 'É'
- 61: 1, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 0, # 'á'
- 15: 0, # 'é'
- 30: 0, # 'í'
- 25: 0, # 'ó'
- 24: 0, # 'ö'
- 31: 0, # 'ú'
- 29: 0, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 40: { # 'B'
- 28: 2, # 'A'
- 40: 1, # 'B'
- 54: 1, # 'C'
- 45: 1, # 'D'
- 32: 2, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 1, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 1, # 'L'
- 34: 0, # 'M'
- 35: 1, # 'N'
- 47: 2, # 'O'
- 46: 0, # 'P'
- 43: 1, # 'R'
- 33: 1, # 'S'
- 37: 1, # 'T'
- 57: 1, # 'U'
- 48: 1, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 2, # 'a'
- 18: 0, # 'b'
- 26: 0, # 'c'
- 17: 0, # 'd'
- 1: 3, # 'e'
- 27: 0, # 'f'
- 12: 0, # 'g'
- 20: 0, # 'h'
- 9: 2, # 'i'
- 22: 1, # 'j'
- 7: 0, # 'k'
- 6: 1, # 'l'
- 13: 0, # 'm'
- 4: 0, # 'n'
- 8: 2, # 'o'
- 23: 1, # 'p'
- 10: 2, # 'r'
- 5: 0, # 's'
- 3: 0, # 't'
- 21: 3, # 'u'
- 19: 0, # 'v'
- 62: 0, # 'x'
- 16: 1, # 'y'
- 11: 0, # 'z'
- 51: 1, # 'Á'
- 44: 1, # 'É'
- 61: 1, # 'Í'
- 58: 1, # 'Ó'
- 59: 1, # 'Ö'
- 60: 1, # 'Ú'
- 63: 1, # 'Ü'
- 14: 2, # 'á'
- 15: 2, # 'é'
- 30: 1, # 'í'
- 25: 1, # 'ó'
- 24: 1, # 'ö'
- 31: 1, # 'ú'
- 29: 1, # 'ü'
- 42: 1, # 'ő'
- 56: 1, # 'ű'
- },
- 54: { # 'C'
- 28: 1, # 'A'
- 40: 1, # 'B'
- 54: 1, # 'C'
- 45: 1, # 'D'
- 32: 1, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 1, # 'H'
- 39: 2, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 1, # 'L'
- 34: 1, # 'M'
- 35: 0, # 'N'
- 47: 1, # 'O'
- 46: 1, # 'P'
- 43: 1, # 'R'
- 33: 2, # 'S'
- 37: 1, # 'T'
- 57: 1, # 'U'
- 48: 0, # 'V'
- 55: 1, # 'Y'
- 52: 1, # 'Z'
- 2: 2, # 'a'
- 18: 0, # 'b'
- 26: 0, # 'c'
- 17: 0, # 'd'
- 1: 1, # 'e'
- 27: 0, # 'f'
- 12: 0, # 'g'
- 20: 1, # 'h'
- 9: 1, # 'i'
- 22: 0, # 'j'
- 7: 0, # 'k'
- 6: 1, # 'l'
- 13: 0, # 'm'
- 4: 0, # 'n'
- 8: 2, # 'o'
- 23: 0, # 'p'
- 10: 1, # 'r'
- 5: 3, # 's'
- 3: 0, # 't'
- 21: 1, # 'u'
- 19: 0, # 'v'
- 62: 0, # 'x'
- 16: 1, # 'y'
- 11: 1, # 'z'
- 51: 1, # 'Á'
- 44: 1, # 'É'
- 61: 1, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 1, # 'á'
- 15: 1, # 'é'
- 30: 1, # 'í'
- 25: 1, # 'ó'
- 24: 0, # 'ö'
- 31: 0, # 'ú'
- 29: 0, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 45: { # 'D'
- 28: 2, # 'A'
- 40: 1, # 'B'
- 54: 0, # 'C'
- 45: 1, # 'D'
- 32: 2, # 'E'
- 50: 1, # 'F'
- 49: 1, # 'G'
- 38: 1, # 'H'
- 39: 2, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 0, # 'L'
- 34: 1, # 'M'
- 35: 1, # 'N'
- 47: 2, # 'O'
- 46: 0, # 'P'
- 43: 1, # 'R'
- 33: 1, # 'S'
- 37: 1, # 'T'
- 57: 1, # 'U'
- 48: 1, # 'V'
- 55: 1, # 'Y'
- 52: 1, # 'Z'
- 2: 2, # 'a'
- 18: 0, # 'b'
- 26: 0, # 'c'
- 17: 0, # 'd'
- 1: 3, # 'e'
- 27: 0, # 'f'
- 12: 0, # 'g'
- 20: 0, # 'h'
- 9: 1, # 'i'
- 22: 0, # 'j'
- 7: 0, # 'k'
- 6: 0, # 'l'
- 13: 0, # 'm'
- 4: 0, # 'n'
- 8: 1, # 'o'
- 23: 0, # 'p'
- 10: 2, # 'r'
- 5: 0, # 's'
- 3: 0, # 't'
- 21: 2, # 'u'
- 19: 0, # 'v'
- 62: 0, # 'x'
- 16: 1, # 'y'
- 11: 1, # 'z'
- 51: 1, # 'Á'
- 44: 1, # 'É'
- 61: 1, # 'Í'
- 58: 1, # 'Ó'
- 59: 1, # 'Ö'
- 60: 1, # 'Ú'
- 63: 1, # 'Ü'
- 14: 1, # 'á'
- 15: 1, # 'é'
- 30: 1, # 'í'
- 25: 1, # 'ó'
- 24: 1, # 'ö'
- 31: 1, # 'ú'
- 29: 1, # 'ü'
- 42: 1, # 'ő'
- 56: 0, # 'ű'
- },
- 32: { # 'E'
- 28: 1, # 'A'
- 40: 1, # 'B'
- 54: 1, # 'C'
- 45: 1, # 'D'
- 32: 1, # 'E'
- 50: 1, # 'F'
- 49: 2, # 'G'
- 38: 1, # 'H'
- 39: 1, # 'I'
- 53: 1, # 'J'
- 36: 2, # 'K'
- 41: 2, # 'L'
- 34: 2, # 'M'
- 35: 2, # 'N'
- 47: 1, # 'O'
- 46: 1, # 'P'
- 43: 2, # 'R'
- 33: 2, # 'S'
- 37: 2, # 'T'
- 57: 1, # 'U'
- 48: 1, # 'V'
- 55: 1, # 'Y'
- 52: 1, # 'Z'
- 2: 1, # 'a'
- 18: 1, # 'b'
- 26: 1, # 'c'
- 17: 2, # 'd'
- 1: 1, # 'e'
- 27: 1, # 'f'
- 12: 3, # 'g'
- 20: 1, # 'h'
- 9: 1, # 'i'
- 22: 1, # 'j'
- 7: 1, # 'k'
- 6: 2, # 'l'
- 13: 2, # 'm'
- 4: 2, # 'n'
- 8: 0, # 'o'
- 23: 1, # 'p'
- 10: 2, # 'r'
- 5: 2, # 's'
- 3: 1, # 't'
- 21: 2, # 'u'
- 19: 1, # 'v'
- 62: 1, # 'x'
- 16: 0, # 'y'
- 11: 3, # 'z'
- 51: 1, # 'Á'
- 44: 1, # 'É'
- 61: 0, # 'Í'
- 58: 1, # 'Ó'
- 59: 1, # 'Ö'
- 60: 0, # 'Ú'
- 63: 1, # 'Ü'
- 14: 0, # 'á'
- 15: 0, # 'é'
- 30: 0, # 'í'
- 25: 0, # 'ó'
- 24: 1, # 'ö'
- 31: 0, # 'ú'
- 29: 0, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 50: { # 'F'
- 28: 1, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 1, # 'E'
- 50: 1, # 'F'
- 49: 0, # 'G'
- 38: 1, # 'H'
- 39: 1, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 1, # 'L'
- 34: 1, # 'M'
- 35: 1, # 'N'
- 47: 1, # 'O'
- 46: 0, # 'P'
- 43: 1, # 'R'
- 33: 0, # 'S'
- 37: 1, # 'T'
- 57: 1, # 'U'
- 48: 0, # 'V'
- 55: 1, # 'Y'
- 52: 0, # 'Z'
- 2: 2, # 'a'
- 18: 0, # 'b'
- 26: 0, # 'c'
- 17: 0, # 'd'
- 1: 2, # 'e'
- 27: 1, # 'f'
- 12: 0, # 'g'
- 20: 0, # 'h'
- 9: 2, # 'i'
- 22: 1, # 'j'
- 7: 0, # 'k'
- 6: 1, # 'l'
- 13: 0, # 'm'
- 4: 0, # 'n'
- 8: 2, # 'o'
- 23: 0, # 'p'
- 10: 2, # 'r'
- 5: 0, # 's'
- 3: 0, # 't'
- 21: 1, # 'u'
- 19: 0, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 0, # 'z'
- 51: 1, # 'Á'
- 44: 1, # 'É'
- 61: 0, # 'Í'
- 58: 1, # 'Ó'
- 59: 1, # 'Ö'
- 60: 0, # 'Ú'
- 63: 1, # 'Ü'
- 14: 1, # 'á'
- 15: 1, # 'é'
- 30: 0, # 'í'
- 25: 0, # 'ó'
- 24: 2, # 'ö'
- 31: 1, # 'ú'
- 29: 1, # 'ü'
- 42: 1, # 'ő'
- 56: 1, # 'ű'
- },
- 49: { # 'G'
- 28: 2, # 'A'
- 40: 1, # 'B'
- 54: 1, # 'C'
- 45: 1, # 'D'
- 32: 2, # 'E'
- 50: 1, # 'F'
- 49: 1, # 'G'
- 38: 1, # 'H'
- 39: 1, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 1, # 'L'
- 34: 1, # 'M'
- 35: 1, # 'N'
- 47: 1, # 'O'
- 46: 1, # 'P'
- 43: 1, # 'R'
- 33: 1, # 'S'
- 37: 1, # 'T'
- 57: 1, # 'U'
- 48: 1, # 'V'
- 55: 2, # 'Y'
- 52: 1, # 'Z'
- 2: 2, # 'a'
- 18: 0, # 'b'
- 26: 0, # 'c'
- 17: 0, # 'd'
- 1: 2, # 'e'
- 27: 0, # 'f'
- 12: 0, # 'g'
- 20: 0, # 'h'
- 9: 1, # 'i'
- 22: 0, # 'j'
- 7: 0, # 'k'
- 6: 1, # 'l'
- 13: 0, # 'm'
- 4: 0, # 'n'
- 8: 2, # 'o'
- 23: 0, # 'p'
- 10: 2, # 'r'
- 5: 0, # 's'
- 3: 0, # 't'
- 21: 1, # 'u'
- 19: 0, # 'v'
- 62: 0, # 'x'
- 16: 2, # 'y'
- 11: 0, # 'z'
- 51: 1, # 'Á'
- 44: 1, # 'É'
- 61: 1, # 'Í'
- 58: 1, # 'Ó'
- 59: 1, # 'Ö'
- 60: 1, # 'Ú'
- 63: 1, # 'Ü'
- 14: 1, # 'á'
- 15: 1, # 'é'
- 30: 0, # 'í'
- 25: 1, # 'ó'
- 24: 1, # 'ö'
- 31: 1, # 'ú'
- 29: 1, # 'ü'
- 42: 1, # 'ő'
- 56: 0, # 'ű'
- },
- 38: { # 'H'
- 28: 2, # 'A'
- 40: 1, # 'B'
- 54: 1, # 'C'
- 45: 0, # 'D'
- 32: 1, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 1, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 1, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 1, # 'O'
- 46: 0, # 'P'
- 43: 1, # 'R'
- 33: 1, # 'S'
- 37: 1, # 'T'
- 57: 1, # 'U'
- 48: 0, # 'V'
- 55: 1, # 'Y'
- 52: 0, # 'Z'
- 2: 3, # 'a'
- 18: 0, # 'b'
- 26: 0, # 'c'
- 17: 0, # 'd'
- 1: 2, # 'e'
- 27: 0, # 'f'
- 12: 0, # 'g'
- 20: 0, # 'h'
- 9: 2, # 'i'
- 22: 1, # 'j'
- 7: 0, # 'k'
- 6: 1, # 'l'
- 13: 1, # 'm'
- 4: 0, # 'n'
- 8: 3, # 'o'
- 23: 0, # 'p'
- 10: 1, # 'r'
- 5: 0, # 's'
- 3: 0, # 't'
- 21: 2, # 'u'
- 19: 0, # 'v'
- 62: 0, # 'x'
- 16: 1, # 'y'
- 11: 0, # 'z'
- 51: 2, # 'Á'
- 44: 2, # 'É'
- 61: 1, # 'Í'
- 58: 1, # 'Ó'
- 59: 1, # 'Ö'
- 60: 1, # 'Ú'
- 63: 1, # 'Ü'
- 14: 2, # 'á'
- 15: 1, # 'é'
- 30: 2, # 'í'
- 25: 1, # 'ó'
- 24: 1, # 'ö'
- 31: 1, # 'ú'
- 29: 1, # 'ü'
- 42: 1, # 'ő'
- 56: 1, # 'ű'
- },
- 39: { # 'I'
- 28: 2, # 'A'
- 40: 1, # 'B'
- 54: 1, # 'C'
- 45: 1, # 'D'
- 32: 1, # 'E'
- 50: 1, # 'F'
- 49: 1, # 'G'
- 38: 1, # 'H'
- 39: 2, # 'I'
- 53: 1, # 'J'
- 36: 2, # 'K'
- 41: 2, # 'L'
- 34: 1, # 'M'
- 35: 2, # 'N'
- 47: 1, # 'O'
- 46: 1, # 'P'
- 43: 1, # 'R'
- 33: 2, # 'S'
- 37: 1, # 'T'
- 57: 1, # 'U'
- 48: 1, # 'V'
- 55: 0, # 'Y'
- 52: 2, # 'Z'
- 2: 0, # 'a'
- 18: 1, # 'b'
- 26: 1, # 'c'
- 17: 2, # 'd'
- 1: 0, # 'e'
- 27: 1, # 'f'
- 12: 2, # 'g'
- 20: 1, # 'h'
- 9: 0, # 'i'
- 22: 1, # 'j'
- 7: 1, # 'k'
- 6: 2, # 'l'
- 13: 2, # 'm'
- 4: 1, # 'n'
- 8: 0, # 'o'
- 23: 1, # 'p'
- 10: 2, # 'r'
- 5: 2, # 's'
- 3: 2, # 't'
- 21: 0, # 'u'
- 19: 1, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 1, # 'z'
- 51: 1, # 'Á'
- 44: 1, # 'É'
- 61: 0, # 'Í'
- 58: 1, # 'Ó'
- 59: 1, # 'Ö'
- 60: 1, # 'Ú'
- 63: 1, # 'Ü'
- 14: 0, # 'á'
- 15: 0, # 'é'
- 30: 0, # 'í'
- 25: 0, # 'ó'
- 24: 0, # 'ö'
- 31: 0, # 'ú'
- 29: 0, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 53: { # 'J'
- 28: 2, # 'A'
- 40: 0, # 'B'
- 54: 1, # 'C'
- 45: 1, # 'D'
- 32: 2, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 1, # 'H'
- 39: 1, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 1, # 'L'
- 34: 1, # 'M'
- 35: 1, # 'N'
- 47: 1, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 1, # 'S'
- 37: 1, # 'T'
- 57: 1, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 1, # 'Z'
- 2: 2, # 'a'
- 18: 0, # 'b'
- 26: 0, # 'c'
- 17: 0, # 'd'
- 1: 2, # 'e'
- 27: 0, # 'f'
- 12: 0, # 'g'
- 20: 0, # 'h'
- 9: 1, # 'i'
- 22: 0, # 'j'
- 7: 0, # 'k'
- 6: 0, # 'l'
- 13: 0, # 'm'
- 4: 0, # 'n'
- 8: 1, # 'o'
- 23: 0, # 'p'
- 10: 0, # 'r'
- 5: 0, # 's'
- 3: 0, # 't'
- 21: 2, # 'u'
- 19: 0, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 0, # 'z'
- 51: 1, # 'Á'
- 44: 1, # 'É'
- 61: 0, # 'Í'
- 58: 1, # 'Ó'
- 59: 1, # 'Ö'
- 60: 1, # 'Ú'
- 63: 1, # 'Ü'
- 14: 2, # 'á'
- 15: 1, # 'é'
- 30: 0, # 'í'
- 25: 2, # 'ó'
- 24: 2, # 'ö'
- 31: 1, # 'ú'
- 29: 0, # 'ü'
- 42: 1, # 'ő'
- 56: 0, # 'ű'
- },
- 36: { # 'K'
- 28: 2, # 'A'
- 40: 1, # 'B'
- 54: 1, # 'C'
- 45: 1, # 'D'
- 32: 2, # 'E'
- 50: 1, # 'F'
- 49: 0, # 'G'
- 38: 1, # 'H'
- 39: 2, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 1, # 'L'
- 34: 1, # 'M'
- 35: 1, # 'N'
- 47: 2, # 'O'
- 46: 0, # 'P'
- 43: 1, # 'R'
- 33: 1, # 'S'
- 37: 1, # 'T'
- 57: 1, # 'U'
- 48: 1, # 'V'
- 55: 1, # 'Y'
- 52: 0, # 'Z'
- 2: 2, # 'a'
- 18: 0, # 'b'
- 26: 0, # 'c'
- 17: 0, # 'd'
- 1: 2, # 'e'
- 27: 1, # 'f'
- 12: 0, # 'g'
- 20: 1, # 'h'
- 9: 3, # 'i'
- 22: 0, # 'j'
- 7: 0, # 'k'
- 6: 1, # 'l'
- 13: 1, # 'm'
- 4: 1, # 'n'
- 8: 2, # 'o'
- 23: 0, # 'p'
- 10: 2, # 'r'
- 5: 0, # 's'
- 3: 0, # 't'
- 21: 1, # 'u'
- 19: 1, # 'v'
- 62: 0, # 'x'
- 16: 1, # 'y'
- 11: 0, # 'z'
- 51: 1, # 'Á'
- 44: 1, # 'É'
- 61: 1, # 'Í'
- 58: 1, # 'Ó'
- 59: 2, # 'Ö'
- 60: 1, # 'Ú'
- 63: 1, # 'Ü'
- 14: 2, # 'á'
- 15: 2, # 'é'
- 30: 1, # 'í'
- 25: 1, # 'ó'
- 24: 2, # 'ö'
- 31: 1, # 'ú'
- 29: 2, # 'ü'
- 42: 1, # 'ő'
- 56: 0, # 'ű'
- },
- 41: { # 'L'
- 28: 2, # 'A'
- 40: 1, # 'B'
- 54: 1, # 'C'
- 45: 1, # 'D'
- 32: 2, # 'E'
- 50: 1, # 'F'
- 49: 1, # 'G'
- 38: 1, # 'H'
- 39: 2, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 2, # 'L'
- 34: 1, # 'M'
- 35: 1, # 'N'
- 47: 2, # 'O'
- 46: 0, # 'P'
- 43: 1, # 'R'
- 33: 1, # 'S'
- 37: 2, # 'T'
- 57: 1, # 'U'
- 48: 1, # 'V'
- 55: 1, # 'Y'
- 52: 1, # 'Z'
- 2: 2, # 'a'
- 18: 0, # 'b'
- 26: 0, # 'c'
- 17: 0, # 'd'
- 1: 3, # 'e'
- 27: 0, # 'f'
- 12: 0, # 'g'
- 20: 0, # 'h'
- 9: 2, # 'i'
- 22: 1, # 'j'
- 7: 0, # 'k'
- 6: 1, # 'l'
- 13: 0, # 'm'
- 4: 0, # 'n'
- 8: 2, # 'o'
- 23: 0, # 'p'
- 10: 0, # 'r'
- 5: 0, # 's'
- 3: 0, # 't'
- 21: 2, # 'u'
- 19: 0, # 'v'
- 62: 0, # 'x'
- 16: 1, # 'y'
- 11: 0, # 'z'
- 51: 2, # 'Á'
- 44: 1, # 'É'
- 61: 1, # 'Í'
- 58: 1, # 'Ó'
- 59: 1, # 'Ö'
- 60: 1, # 'Ú'
- 63: 1, # 'Ü'
- 14: 2, # 'á'
- 15: 1, # 'é'
- 30: 1, # 'í'
- 25: 1, # 'ó'
- 24: 1, # 'ö'
- 31: 0, # 'ú'
- 29: 1, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 34: { # 'M'
- 28: 2, # 'A'
- 40: 1, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 2, # 'E'
- 50: 1, # 'F'
- 49: 0, # 'G'
- 38: 1, # 'H'
- 39: 2, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 1, # 'L'
- 34: 1, # 'M'
- 35: 1, # 'N'
- 47: 1, # 'O'
- 46: 1, # 'P'
- 43: 1, # 'R'
- 33: 1, # 'S'
- 37: 1, # 'T'
- 57: 1, # 'U'
- 48: 1, # 'V'
- 55: 1, # 'Y'
- 52: 1, # 'Z'
- 2: 3, # 'a'
- 18: 0, # 'b'
- 26: 1, # 'c'
- 17: 0, # 'd'
- 1: 3, # 'e'
- 27: 0, # 'f'
- 12: 0, # 'g'
- 20: 0, # 'h'
- 9: 3, # 'i'
- 22: 0, # 'j'
- 7: 0, # 'k'
- 6: 0, # 'l'
- 13: 1, # 'm'
- 4: 1, # 'n'
- 8: 3, # 'o'
- 23: 0, # 'p'
- 10: 1, # 'r'
- 5: 0, # 's'
- 3: 0, # 't'
- 21: 2, # 'u'
- 19: 0, # 'v'
- 62: 0, # 'x'
- 16: 1, # 'y'
- 11: 0, # 'z'
- 51: 2, # 'Á'
- 44: 1, # 'É'
- 61: 1, # 'Í'
- 58: 1, # 'Ó'
- 59: 1, # 'Ö'
- 60: 1, # 'Ú'
- 63: 1, # 'Ü'
- 14: 2, # 'á'
- 15: 2, # 'é'
- 30: 1, # 'í'
- 25: 1, # 'ó'
- 24: 1, # 'ö'
- 31: 1, # 'ú'
- 29: 1, # 'ü'
- 42: 0, # 'ő'
- 56: 1, # 'ű'
- },
- 35: { # 'N'
- 28: 2, # 'A'
- 40: 1, # 'B'
- 54: 1, # 'C'
- 45: 2, # 'D'
- 32: 2, # 'E'
- 50: 1, # 'F'
- 49: 1, # 'G'
- 38: 1, # 'H'
- 39: 1, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 1, # 'L'
- 34: 1, # 'M'
- 35: 1, # 'N'
- 47: 1, # 'O'
- 46: 1, # 'P'
- 43: 1, # 'R'
- 33: 1, # 'S'
- 37: 2, # 'T'
- 57: 1, # 'U'
- 48: 1, # 'V'
- 55: 2, # 'Y'
- 52: 1, # 'Z'
- 2: 3, # 'a'
- 18: 0, # 'b'
- 26: 0, # 'c'
- 17: 0, # 'd'
- 1: 3, # 'e'
- 27: 0, # 'f'
- 12: 0, # 'g'
- 20: 0, # 'h'
- 9: 2, # 'i'
- 22: 0, # 'j'
- 7: 0, # 'k'
- 6: 0, # 'l'
- 13: 0, # 'm'
- 4: 1, # 'n'
- 8: 2, # 'o'
- 23: 0, # 'p'
- 10: 0, # 'r'
- 5: 0, # 's'
- 3: 0, # 't'
- 21: 1, # 'u'
- 19: 0, # 'v'
- 62: 0, # 'x'
- 16: 2, # 'y'
- 11: 0, # 'z'
- 51: 1, # 'Á'
- 44: 1, # 'É'
- 61: 1, # 'Í'
- 58: 1, # 'Ó'
- 59: 1, # 'Ö'
- 60: 1, # 'Ú'
- 63: 1, # 'Ü'
- 14: 1, # 'á'
- 15: 2, # 'é'
- 30: 1, # 'í'
- 25: 1, # 'ó'
- 24: 1, # 'ö'
- 31: 0, # 'ú'
- 29: 0, # 'ü'
- 42: 1, # 'ő'
- 56: 0, # 'ű'
- },
- 47: { # 'O'
- 28: 1, # 'A'
- 40: 1, # 'B'
- 54: 1, # 'C'
- 45: 1, # 'D'
- 32: 1, # 'E'
- 50: 1, # 'F'
- 49: 1, # 'G'
- 38: 1, # 'H'
- 39: 1, # 'I'
- 53: 1, # 'J'
- 36: 2, # 'K'
- 41: 2, # 'L'
- 34: 2, # 'M'
- 35: 2, # 'N'
- 47: 1, # 'O'
- 46: 1, # 'P'
- 43: 2, # 'R'
- 33: 2, # 'S'
- 37: 2, # 'T'
- 57: 1, # 'U'
- 48: 1, # 'V'
- 55: 1, # 'Y'
- 52: 1, # 'Z'
- 2: 0, # 'a'
- 18: 1, # 'b'
- 26: 1, # 'c'
- 17: 1, # 'd'
- 1: 1, # 'e'
- 27: 1, # 'f'
- 12: 1, # 'g'
- 20: 1, # 'h'
- 9: 1, # 'i'
- 22: 1, # 'j'
- 7: 2, # 'k'
- 6: 2, # 'l'
- 13: 1, # 'm'
- 4: 1, # 'n'
- 8: 1, # 'o'
- 23: 1, # 'p'
- 10: 2, # 'r'
- 5: 1, # 's'
- 3: 2, # 't'
- 21: 1, # 'u'
- 19: 0, # 'v'
- 62: 1, # 'x'
- 16: 0, # 'y'
- 11: 1, # 'z'
- 51: 1, # 'Á'
- 44: 1, # 'É'
- 61: 0, # 'Í'
- 58: 1, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 0, # 'á'
- 15: 0, # 'é'
- 30: 0, # 'í'
- 25: 0, # 'ó'
- 24: 0, # 'ö'
- 31: 0, # 'ú'
- 29: 0, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 46: { # 'P'
- 28: 1, # 'A'
- 40: 1, # 'B'
- 54: 1, # 'C'
- 45: 1, # 'D'
- 32: 1, # 'E'
- 50: 1, # 'F'
- 49: 1, # 'G'
- 38: 1, # 'H'
- 39: 1, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 1, # 'L'
- 34: 0, # 'M'
- 35: 1, # 'N'
- 47: 1, # 'O'
- 46: 1, # 'P'
- 43: 2, # 'R'
- 33: 1, # 'S'
- 37: 1, # 'T'
- 57: 1, # 'U'
- 48: 1, # 'V'
- 55: 0, # 'Y'
- 52: 1, # 'Z'
- 2: 2, # 'a'
- 18: 0, # 'b'
- 26: 0, # 'c'
- 17: 0, # 'd'
- 1: 2, # 'e'
- 27: 1, # 'f'
- 12: 0, # 'g'
- 20: 1, # 'h'
- 9: 2, # 'i'
- 22: 0, # 'j'
- 7: 0, # 'k'
- 6: 1, # 'l'
- 13: 0, # 'm'
- 4: 1, # 'n'
- 8: 2, # 'o'
- 23: 0, # 'p'
- 10: 2, # 'r'
- 5: 1, # 's'
- 3: 0, # 't'
- 21: 1, # 'u'
- 19: 0, # 'v'
- 62: 0, # 'x'
- 16: 1, # 'y'
- 11: 0, # 'z'
- 51: 2, # 'Á'
- 44: 1, # 'É'
- 61: 1, # 'Í'
- 58: 1, # 'Ó'
- 59: 1, # 'Ö'
- 60: 0, # 'Ú'
- 63: 1, # 'Ü'
- 14: 3, # 'á'
- 15: 2, # 'é'
- 30: 0, # 'í'
- 25: 1, # 'ó'
- 24: 1, # 'ö'
- 31: 0, # 'ú'
- 29: 1, # 'ü'
- 42: 1, # 'ő'
- 56: 0, # 'ű'
- },
- 43: { # 'R'
- 28: 2, # 'A'
- 40: 1, # 'B'
- 54: 1, # 'C'
- 45: 1, # 'D'
- 32: 2, # 'E'
- 50: 1, # 'F'
- 49: 1, # 'G'
- 38: 1, # 'H'
- 39: 2, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 1, # 'L'
- 34: 1, # 'M'
- 35: 1, # 'N'
- 47: 2, # 'O'
- 46: 1, # 'P'
- 43: 1, # 'R'
- 33: 2, # 'S'
- 37: 2, # 'T'
- 57: 1, # 'U'
- 48: 1, # 'V'
- 55: 1, # 'Y'
- 52: 1, # 'Z'
- 2: 2, # 'a'
- 18: 0, # 'b'
- 26: 0, # 'c'
- 17: 0, # 'd'
- 1: 2, # 'e'
- 27: 0, # 'f'
- 12: 0, # 'g'
- 20: 1, # 'h'
- 9: 2, # 'i'
- 22: 0, # 'j'
- 7: 0, # 'k'
- 6: 0, # 'l'
- 13: 0, # 'm'
- 4: 0, # 'n'
- 8: 2, # 'o'
- 23: 0, # 'p'
- 10: 0, # 'r'
- 5: 0, # 's'
- 3: 0, # 't'
- 21: 1, # 'u'
- 19: 0, # 'v'
- 62: 0, # 'x'
- 16: 1, # 'y'
- 11: 0, # 'z'
- 51: 2, # 'Á'
- 44: 1, # 'É'
- 61: 1, # 'Í'
- 58: 2, # 'Ó'
- 59: 1, # 'Ö'
- 60: 1, # 'Ú'
- 63: 1, # 'Ü'
- 14: 2, # 'á'
- 15: 2, # 'é'
- 30: 1, # 'í'
- 25: 2, # 'ó'
- 24: 1, # 'ö'
- 31: 1, # 'ú'
- 29: 1, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 33: { # 'S'
- 28: 2, # 'A'
- 40: 1, # 'B'
- 54: 1, # 'C'
- 45: 1, # 'D'
- 32: 2, # 'E'
- 50: 1, # 'F'
- 49: 1, # 'G'
- 38: 1, # 'H'
- 39: 2, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 1, # 'L'
- 34: 1, # 'M'
- 35: 1, # 'N'
- 47: 2, # 'O'
- 46: 1, # 'P'
- 43: 1, # 'R'
- 33: 2, # 'S'
- 37: 2, # 'T'
- 57: 1, # 'U'
- 48: 1, # 'V'
- 55: 1, # 'Y'
- 52: 3, # 'Z'
- 2: 2, # 'a'
- 18: 0, # 'b'
- 26: 1, # 'c'
- 17: 0, # 'd'
- 1: 2, # 'e'
- 27: 0, # 'f'
- 12: 0, # 'g'
- 20: 1, # 'h'
- 9: 2, # 'i'
- 22: 0, # 'j'
- 7: 1, # 'k'
- 6: 1, # 'l'
- 13: 1, # 'm'
- 4: 0, # 'n'
- 8: 2, # 'o'
- 23: 1, # 'p'
- 10: 0, # 'r'
- 5: 0, # 's'
- 3: 1, # 't'
- 21: 1, # 'u'
- 19: 1, # 'v'
- 62: 0, # 'x'
- 16: 1, # 'y'
- 11: 3, # 'z'
- 51: 2, # 'Á'
- 44: 1, # 'É'
- 61: 1, # 'Í'
- 58: 1, # 'Ó'
- 59: 1, # 'Ö'
- 60: 1, # 'Ú'
- 63: 1, # 'Ü'
- 14: 2, # 'á'
- 15: 1, # 'é'
- 30: 1, # 'í'
- 25: 1, # 'ó'
- 24: 1, # 'ö'
- 31: 1, # 'ú'
- 29: 1, # 'ü'
- 42: 1, # 'ő'
- 56: 1, # 'ű'
- },
- 37: { # 'T'
- 28: 2, # 'A'
- 40: 1, # 'B'
- 54: 1, # 'C'
- 45: 1, # 'D'
- 32: 2, # 'E'
- 50: 1, # 'F'
- 49: 1, # 'G'
- 38: 1, # 'H'
- 39: 2, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 1, # 'L'
- 34: 1, # 'M'
- 35: 1, # 'N'
- 47: 2, # 'O'
- 46: 1, # 'P'
- 43: 2, # 'R'
- 33: 1, # 'S'
- 37: 2, # 'T'
- 57: 1, # 'U'
- 48: 1, # 'V'
- 55: 1, # 'Y'
- 52: 1, # 'Z'
- 2: 2, # 'a'
- 18: 0, # 'b'
- 26: 0, # 'c'
- 17: 0, # 'd'
- 1: 2, # 'e'
- 27: 0, # 'f'
- 12: 0, # 'g'
- 20: 1, # 'h'
- 9: 2, # 'i'
- 22: 0, # 'j'
- 7: 0, # 'k'
- 6: 0, # 'l'
- 13: 0, # 'm'
- 4: 0, # 'n'
- 8: 2, # 'o'
- 23: 0, # 'p'
- 10: 1, # 'r'
- 5: 1, # 's'
- 3: 0, # 't'
- 21: 2, # 'u'
- 19: 0, # 'v'
- 62: 0, # 'x'
- 16: 1, # 'y'
- 11: 1, # 'z'
- 51: 2, # 'Á'
- 44: 2, # 'É'
- 61: 1, # 'Í'
- 58: 1, # 'Ó'
- 59: 1, # 'Ö'
- 60: 1, # 'Ú'
- 63: 1, # 'Ü'
- 14: 2, # 'á'
- 15: 1, # 'é'
- 30: 1, # 'í'
- 25: 1, # 'ó'
- 24: 2, # 'ö'
- 31: 1, # 'ú'
- 29: 1, # 'ü'
- 42: 1, # 'ő'
- 56: 1, # 'ű'
- },
- 57: { # 'U'
- 28: 1, # 'A'
- 40: 1, # 'B'
- 54: 1, # 'C'
- 45: 1, # 'D'
- 32: 1, # 'E'
- 50: 1, # 'F'
- 49: 1, # 'G'
- 38: 1, # 'H'
- 39: 1, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 1, # 'L'
- 34: 1, # 'M'
- 35: 1, # 'N'
- 47: 1, # 'O'
- 46: 1, # 'P'
- 43: 1, # 'R'
- 33: 2, # 'S'
- 37: 1, # 'T'
- 57: 0, # 'U'
- 48: 1, # 'V'
- 55: 0, # 'Y'
- 52: 1, # 'Z'
- 2: 0, # 'a'
- 18: 1, # 'b'
- 26: 1, # 'c'
- 17: 1, # 'd'
- 1: 1, # 'e'
- 27: 0, # 'f'
- 12: 2, # 'g'
- 20: 0, # 'h'
- 9: 0, # 'i'
- 22: 1, # 'j'
- 7: 1, # 'k'
- 6: 1, # 'l'
- 13: 1, # 'm'
- 4: 1, # 'n'
- 8: 0, # 'o'
- 23: 1, # 'p'
- 10: 1, # 'r'
- 5: 1, # 's'
- 3: 1, # 't'
- 21: 0, # 'u'
- 19: 0, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 1, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 1, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 0, # 'á'
- 15: 0, # 'é'
- 30: 0, # 'í'
- 25: 0, # 'ó'
- 24: 0, # 'ö'
- 31: 0, # 'ú'
- 29: 0, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 48: { # 'V'
- 28: 2, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 1, # 'D'
- 32: 2, # 'E'
- 50: 1, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 2, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 0, # 'L'
- 34: 1, # 'M'
- 35: 1, # 'N'
- 47: 1, # 'O'
- 46: 1, # 'P'
- 43: 1, # 'R'
- 33: 1, # 'S'
- 37: 1, # 'T'
- 57: 1, # 'U'
- 48: 1, # 'V'
- 55: 1, # 'Y'
- 52: 0, # 'Z'
- 2: 3, # 'a'
- 18: 0, # 'b'
- 26: 0, # 'c'
- 17: 0, # 'd'
- 1: 2, # 'e'
- 27: 0, # 'f'
- 12: 0, # 'g'
- 20: 0, # 'h'
- 9: 2, # 'i'
- 22: 0, # 'j'
- 7: 0, # 'k'
- 6: 1, # 'l'
- 13: 0, # 'm'
- 4: 0, # 'n'
- 8: 2, # 'o'
- 23: 0, # 'p'
- 10: 0, # 'r'
- 5: 0, # 's'
- 3: 0, # 't'
- 21: 1, # 'u'
- 19: 0, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 0, # 'z'
- 51: 2, # 'Á'
- 44: 2, # 'É'
- 61: 1, # 'Í'
- 58: 1, # 'Ó'
- 59: 1, # 'Ö'
- 60: 0, # 'Ú'
- 63: 1, # 'Ü'
- 14: 2, # 'á'
- 15: 2, # 'é'
- 30: 1, # 'í'
- 25: 0, # 'ó'
- 24: 1, # 'ö'
- 31: 0, # 'ú'
- 29: 0, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 55: { # 'Y'
- 28: 2, # 'A'
- 40: 1, # 'B'
- 54: 1, # 'C'
- 45: 1, # 'D'
- 32: 2, # 'E'
- 50: 1, # 'F'
- 49: 1, # 'G'
- 38: 1, # 'H'
- 39: 1, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 1, # 'L'
- 34: 1, # 'M'
- 35: 1, # 'N'
- 47: 1, # 'O'
- 46: 1, # 'P'
- 43: 1, # 'R'
- 33: 1, # 'S'
- 37: 1, # 'T'
- 57: 1, # 'U'
- 48: 1, # 'V'
- 55: 0, # 'Y'
- 52: 2, # 'Z'
- 2: 1, # 'a'
- 18: 0, # 'b'
- 26: 0, # 'c'
- 17: 1, # 'd'
- 1: 1, # 'e'
- 27: 0, # 'f'
- 12: 0, # 'g'
- 20: 0, # 'h'
- 9: 0, # 'i'
- 22: 0, # 'j'
- 7: 0, # 'k'
- 6: 0, # 'l'
- 13: 0, # 'm'
- 4: 0, # 'n'
- 8: 1, # 'o'
- 23: 1, # 'p'
- 10: 0, # 'r'
- 5: 0, # 's'
- 3: 0, # 't'
- 21: 0, # 'u'
- 19: 1, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 0, # 'z'
- 51: 1, # 'Á'
- 44: 1, # 'É'
- 61: 1, # 'Í'
- 58: 1, # 'Ó'
- 59: 1, # 'Ö'
- 60: 1, # 'Ú'
- 63: 1, # 'Ü'
- 14: 0, # 'á'
- 15: 0, # 'é'
- 30: 0, # 'í'
- 25: 0, # 'ó'
- 24: 0, # 'ö'
- 31: 0, # 'ú'
- 29: 0, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 52: { # 'Z'
- 28: 2, # 'A'
- 40: 1, # 'B'
- 54: 0, # 'C'
- 45: 1, # 'D'
- 32: 2, # 'E'
- 50: 1, # 'F'
- 49: 1, # 'G'
- 38: 1, # 'H'
- 39: 2, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 1, # 'L'
- 34: 1, # 'M'
- 35: 1, # 'N'
- 47: 2, # 'O'
- 46: 1, # 'P'
- 43: 1, # 'R'
- 33: 2, # 'S'
- 37: 1, # 'T'
- 57: 1, # 'U'
- 48: 1, # 'V'
- 55: 1, # 'Y'
- 52: 1, # 'Z'
- 2: 1, # 'a'
- 18: 0, # 'b'
- 26: 0, # 'c'
- 17: 0, # 'd'
- 1: 1, # 'e'
- 27: 0, # 'f'
- 12: 0, # 'g'
- 20: 0, # 'h'
- 9: 1, # 'i'
- 22: 0, # 'j'
- 7: 0, # 'k'
- 6: 0, # 'l'
- 13: 0, # 'm'
- 4: 1, # 'n'
- 8: 1, # 'o'
- 23: 0, # 'p'
- 10: 1, # 'r'
- 5: 2, # 's'
- 3: 0, # 't'
- 21: 1, # 'u'
- 19: 0, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 0, # 'z'
- 51: 2, # 'Á'
- 44: 1, # 'É'
- 61: 1, # 'Í'
- 58: 1, # 'Ó'
- 59: 1, # 'Ö'
- 60: 1, # 'Ú'
- 63: 1, # 'Ü'
- 14: 1, # 'á'
- 15: 1, # 'é'
- 30: 0, # 'í'
- 25: 0, # 'ó'
- 24: 1, # 'ö'
- 31: 1, # 'ú'
- 29: 1, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 2: { # 'a'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 1, # 'a'
- 18: 3, # 'b'
- 26: 3, # 'c'
- 17: 3, # 'd'
- 1: 2, # 'e'
- 27: 2, # 'f'
- 12: 3, # 'g'
- 20: 3, # 'h'
- 9: 3, # 'i'
- 22: 3, # 'j'
- 7: 3, # 'k'
- 6: 3, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 8: 2, # 'o'
- 23: 3, # 'p'
- 10: 3, # 'r'
- 5: 3, # 's'
- 3: 3, # 't'
- 21: 3, # 'u'
- 19: 3, # 'v'
- 62: 1, # 'x'
- 16: 2, # 'y'
- 11: 3, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 1, # 'á'
- 15: 1, # 'é'
- 30: 1, # 'í'
- 25: 1, # 'ó'
- 24: 1, # 'ö'
- 31: 1, # 'ú'
- 29: 1, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 18: { # 'b'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 3, # 'a'
- 18: 3, # 'b'
- 26: 1, # 'c'
- 17: 1, # 'd'
- 1: 3, # 'e'
- 27: 1, # 'f'
- 12: 1, # 'g'
- 20: 1, # 'h'
- 9: 3, # 'i'
- 22: 2, # 'j'
- 7: 2, # 'k'
- 6: 2, # 'l'
- 13: 1, # 'm'
- 4: 2, # 'n'
- 8: 3, # 'o'
- 23: 1, # 'p'
- 10: 3, # 'r'
- 5: 2, # 's'
- 3: 1, # 't'
- 21: 3, # 'u'
- 19: 1, # 'v'
- 62: 0, # 'x'
- 16: 1, # 'y'
- 11: 1, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 3, # 'á'
- 15: 3, # 'é'
- 30: 2, # 'í'
- 25: 3, # 'ó'
- 24: 2, # 'ö'
- 31: 2, # 'ú'
- 29: 2, # 'ü'
- 42: 2, # 'ő'
- 56: 1, # 'ű'
- },
- 26: { # 'c'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 1, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 1, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 2, # 'a'
- 18: 1, # 'b'
- 26: 2, # 'c'
- 17: 1, # 'd'
- 1: 3, # 'e'
- 27: 1, # 'f'
- 12: 1, # 'g'
- 20: 3, # 'h'
- 9: 3, # 'i'
- 22: 1, # 'j'
- 7: 2, # 'k'
- 6: 1, # 'l'
- 13: 1, # 'm'
- 4: 1, # 'n'
- 8: 3, # 'o'
- 23: 1, # 'p'
- 10: 2, # 'r'
- 5: 3, # 's'
- 3: 2, # 't'
- 21: 2, # 'u'
- 19: 1, # 'v'
- 62: 0, # 'x'
- 16: 1, # 'y'
- 11: 2, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 2, # 'á'
- 15: 2, # 'é'
- 30: 2, # 'í'
- 25: 1, # 'ó'
- 24: 1, # 'ö'
- 31: 1, # 'ú'
- 29: 1, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 17: { # 'd'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 3, # 'a'
- 18: 2, # 'b'
- 26: 1, # 'c'
- 17: 2, # 'd'
- 1: 3, # 'e'
- 27: 1, # 'f'
- 12: 1, # 'g'
- 20: 2, # 'h'
- 9: 3, # 'i'
- 22: 3, # 'j'
- 7: 2, # 'k'
- 6: 1, # 'l'
- 13: 2, # 'm'
- 4: 3, # 'n'
- 8: 3, # 'o'
- 23: 1, # 'p'
- 10: 3, # 'r'
- 5: 3, # 's'
- 3: 3, # 't'
- 21: 3, # 'u'
- 19: 3, # 'v'
- 62: 0, # 'x'
- 16: 2, # 'y'
- 11: 2, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 3, # 'á'
- 15: 3, # 'é'
- 30: 3, # 'í'
- 25: 3, # 'ó'
- 24: 3, # 'ö'
- 31: 2, # 'ú'
- 29: 2, # 'ü'
- 42: 2, # 'ő'
- 56: 1, # 'ű'
- },
- 1: { # 'e'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 2, # 'a'
- 18: 3, # 'b'
- 26: 3, # 'c'
- 17: 3, # 'd'
- 1: 2, # 'e'
- 27: 3, # 'f'
- 12: 3, # 'g'
- 20: 3, # 'h'
- 9: 3, # 'i'
- 22: 3, # 'j'
- 7: 3, # 'k'
- 6: 3, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 8: 2, # 'o'
- 23: 3, # 'p'
- 10: 3, # 'r'
- 5: 3, # 's'
- 3: 3, # 't'
- 21: 2, # 'u'
- 19: 3, # 'v'
- 62: 2, # 'x'
- 16: 2, # 'y'
- 11: 3, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 3, # 'á'
- 15: 1, # 'é'
- 30: 1, # 'í'
- 25: 1, # 'ó'
- 24: 1, # 'ö'
- 31: 1, # 'ú'
- 29: 1, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 27: { # 'f'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 3, # 'a'
- 18: 1, # 'b'
- 26: 1, # 'c'
- 17: 1, # 'd'
- 1: 3, # 'e'
- 27: 2, # 'f'
- 12: 1, # 'g'
- 20: 1, # 'h'
- 9: 3, # 'i'
- 22: 2, # 'j'
- 7: 1, # 'k'
- 6: 1, # 'l'
- 13: 1, # 'm'
- 4: 1, # 'n'
- 8: 3, # 'o'
- 23: 0, # 'p'
- 10: 3, # 'r'
- 5: 1, # 's'
- 3: 1, # 't'
- 21: 2, # 'u'
- 19: 1, # 'v'
- 62: 0, # 'x'
- 16: 1, # 'y'
- 11: 0, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 3, # 'á'
- 15: 3, # 'é'
- 30: 1, # 'í'
- 25: 1, # 'ó'
- 24: 3, # 'ö'
- 31: 1, # 'ú'
- 29: 2, # 'ü'
- 42: 1, # 'ő'
- 56: 1, # 'ű'
- },
- 12: { # 'g'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 3, # 'a'
- 18: 3, # 'b'
- 26: 2, # 'c'
- 17: 2, # 'd'
- 1: 3, # 'e'
- 27: 2, # 'f'
- 12: 3, # 'g'
- 20: 3, # 'h'
- 9: 3, # 'i'
- 22: 3, # 'j'
- 7: 2, # 'k'
- 6: 3, # 'l'
- 13: 2, # 'm'
- 4: 3, # 'n'
- 8: 3, # 'o'
- 23: 1, # 'p'
- 10: 3, # 'r'
- 5: 3, # 's'
- 3: 3, # 't'
- 21: 3, # 'u'
- 19: 3, # 'v'
- 62: 0, # 'x'
- 16: 3, # 'y'
- 11: 2, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 3, # 'á'
- 15: 3, # 'é'
- 30: 2, # 'í'
- 25: 3, # 'ó'
- 24: 2, # 'ö'
- 31: 2, # 'ú'
- 29: 2, # 'ü'
- 42: 2, # 'ő'
- 56: 1, # 'ű'
- },
- 20: { # 'h'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 3, # 'a'
- 18: 1, # 'b'
- 26: 1, # 'c'
- 17: 0, # 'd'
- 1: 3, # 'e'
- 27: 0, # 'f'
- 12: 1, # 'g'
- 20: 2, # 'h'
- 9: 3, # 'i'
- 22: 1, # 'j'
- 7: 1, # 'k'
- 6: 1, # 'l'
- 13: 1, # 'm'
- 4: 1, # 'n'
- 8: 3, # 'o'
- 23: 0, # 'p'
- 10: 1, # 'r'
- 5: 2, # 's'
- 3: 1, # 't'
- 21: 3, # 'u'
- 19: 1, # 'v'
- 62: 0, # 'x'
- 16: 2, # 'y'
- 11: 0, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 3, # 'á'
- 15: 3, # 'é'
- 30: 3, # 'í'
- 25: 2, # 'ó'
- 24: 2, # 'ö'
- 31: 2, # 'ú'
- 29: 1, # 'ü'
- 42: 1, # 'ő'
- 56: 1, # 'ű'
- },
- 9: { # 'i'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 3, # 'a'
- 18: 3, # 'b'
- 26: 3, # 'c'
- 17: 3, # 'd'
- 1: 3, # 'e'
- 27: 3, # 'f'
- 12: 3, # 'g'
- 20: 3, # 'h'
- 9: 2, # 'i'
- 22: 2, # 'j'
- 7: 3, # 'k'
- 6: 3, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 8: 2, # 'o'
- 23: 2, # 'p'
- 10: 3, # 'r'
- 5: 3, # 's'
- 3: 3, # 't'
- 21: 3, # 'u'
- 19: 3, # 'v'
- 62: 1, # 'x'
- 16: 1, # 'y'
- 11: 3, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 3, # 'á'
- 15: 2, # 'é'
- 30: 1, # 'í'
- 25: 3, # 'ó'
- 24: 1, # 'ö'
- 31: 2, # 'ú'
- 29: 1, # 'ü'
- 42: 0, # 'ő'
- 56: 1, # 'ű'
- },
- 22: { # 'j'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 3, # 'a'
- 18: 2, # 'b'
- 26: 1, # 'c'
- 17: 3, # 'd'
- 1: 3, # 'e'
- 27: 1, # 'f'
- 12: 1, # 'g'
- 20: 2, # 'h'
- 9: 1, # 'i'
- 22: 2, # 'j'
- 7: 2, # 'k'
- 6: 2, # 'l'
- 13: 1, # 'm'
- 4: 2, # 'n'
- 8: 3, # 'o'
- 23: 1, # 'p'
- 10: 2, # 'r'
- 5: 2, # 's'
- 3: 3, # 't'
- 21: 3, # 'u'
- 19: 1, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 2, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 3, # 'á'
- 15: 3, # 'é'
- 30: 1, # 'í'
- 25: 3, # 'ó'
- 24: 3, # 'ö'
- 31: 3, # 'ú'
- 29: 2, # 'ü'
- 42: 1, # 'ő'
- 56: 1, # 'ű'
- },
- 7: { # 'k'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 3, # 'a'
- 18: 3, # 'b'
- 26: 2, # 'c'
- 17: 1, # 'd'
- 1: 3, # 'e'
- 27: 1, # 'f'
- 12: 1, # 'g'
- 20: 2, # 'h'
- 9: 3, # 'i'
- 22: 2, # 'j'
- 7: 3, # 'k'
- 6: 3, # 'l'
- 13: 1, # 'm'
- 4: 3, # 'n'
- 8: 3, # 'o'
- 23: 1, # 'p'
- 10: 3, # 'r'
- 5: 3, # 's'
- 3: 3, # 't'
- 21: 3, # 'u'
- 19: 2, # 'v'
- 62: 0, # 'x'
- 16: 2, # 'y'
- 11: 1, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 3, # 'á'
- 15: 3, # 'é'
- 30: 3, # 'í'
- 25: 2, # 'ó'
- 24: 3, # 'ö'
- 31: 1, # 'ú'
- 29: 3, # 'ü'
- 42: 1, # 'ő'
- 56: 1, # 'ű'
- },
- 6: { # 'l'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 1, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 1, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 3, # 'a'
- 18: 2, # 'b'
- 26: 3, # 'c'
- 17: 3, # 'd'
- 1: 3, # 'e'
- 27: 3, # 'f'
- 12: 3, # 'g'
- 20: 3, # 'h'
- 9: 3, # 'i'
- 22: 3, # 'j'
- 7: 3, # 'k'
- 6: 3, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 8: 3, # 'o'
- 23: 2, # 'p'
- 10: 2, # 'r'
- 5: 3, # 's'
- 3: 3, # 't'
- 21: 3, # 'u'
- 19: 3, # 'v'
- 62: 0, # 'x'
- 16: 3, # 'y'
- 11: 2, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 3, # 'á'
- 15: 3, # 'é'
- 30: 3, # 'í'
- 25: 3, # 'ó'
- 24: 3, # 'ö'
- 31: 2, # 'ú'
- 29: 2, # 'ü'
- 42: 3, # 'ő'
- 56: 1, # 'ű'
- },
- 13: { # 'm'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 3, # 'a'
- 18: 3, # 'b'
- 26: 2, # 'c'
- 17: 1, # 'd'
- 1: 3, # 'e'
- 27: 1, # 'f'
- 12: 1, # 'g'
- 20: 2, # 'h'
- 9: 3, # 'i'
- 22: 2, # 'j'
- 7: 1, # 'k'
- 6: 3, # 'l'
- 13: 3, # 'm'
- 4: 2, # 'n'
- 8: 3, # 'o'
- 23: 3, # 'p'
- 10: 2, # 'r'
- 5: 2, # 's'
- 3: 2, # 't'
- 21: 3, # 'u'
- 19: 1, # 'v'
- 62: 0, # 'x'
- 16: 1, # 'y'
- 11: 2, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 3, # 'á'
- 15: 3, # 'é'
- 30: 2, # 'í'
- 25: 2, # 'ó'
- 24: 2, # 'ö'
- 31: 2, # 'ú'
- 29: 2, # 'ü'
- 42: 1, # 'ő'
- 56: 2, # 'ű'
- },
- 4: { # 'n'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 3, # 'a'
- 18: 3, # 'b'
- 26: 3, # 'c'
- 17: 3, # 'd'
- 1: 3, # 'e'
- 27: 2, # 'f'
- 12: 3, # 'g'
- 20: 3, # 'h'
- 9: 3, # 'i'
- 22: 2, # 'j'
- 7: 3, # 'k'
- 6: 2, # 'l'
- 13: 2, # 'm'
- 4: 3, # 'n'
- 8: 3, # 'o'
- 23: 2, # 'p'
- 10: 2, # 'r'
- 5: 3, # 's'
- 3: 3, # 't'
- 21: 3, # 'u'
- 19: 2, # 'v'
- 62: 1, # 'x'
- 16: 3, # 'y'
- 11: 3, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 3, # 'á'
- 15: 3, # 'é'
- 30: 2, # 'í'
- 25: 2, # 'ó'
- 24: 3, # 'ö'
- 31: 2, # 'ú'
- 29: 3, # 'ü'
- 42: 2, # 'ő'
- 56: 1, # 'ű'
- },
- 8: { # 'o'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 1, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 2, # 'a'
- 18: 3, # 'b'
- 26: 3, # 'c'
- 17: 3, # 'd'
- 1: 2, # 'e'
- 27: 2, # 'f'
- 12: 3, # 'g'
- 20: 3, # 'h'
- 9: 2, # 'i'
- 22: 2, # 'j'
- 7: 3, # 'k'
- 6: 3, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 8: 1, # 'o'
- 23: 3, # 'p'
- 10: 3, # 'r'
- 5: 3, # 's'
- 3: 3, # 't'
- 21: 2, # 'u'
- 19: 3, # 'v'
- 62: 1, # 'x'
- 16: 1, # 'y'
- 11: 3, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 1, # 'á'
- 15: 2, # 'é'
- 30: 1, # 'í'
- 25: 1, # 'ó'
- 24: 1, # 'ö'
- 31: 1, # 'ú'
- 29: 1, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 23: { # 'p'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 3, # 'a'
- 18: 1, # 'b'
- 26: 2, # 'c'
- 17: 1, # 'd'
- 1: 3, # 'e'
- 27: 1, # 'f'
- 12: 1, # 'g'
- 20: 2, # 'h'
- 9: 3, # 'i'
- 22: 2, # 'j'
- 7: 2, # 'k'
- 6: 3, # 'l'
- 13: 1, # 'm'
- 4: 2, # 'n'
- 8: 3, # 'o'
- 23: 3, # 'p'
- 10: 3, # 'r'
- 5: 2, # 's'
- 3: 2, # 't'
- 21: 3, # 'u'
- 19: 2, # 'v'
- 62: 0, # 'x'
- 16: 1, # 'y'
- 11: 2, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 3, # 'á'
- 15: 3, # 'é'
- 30: 2, # 'í'
- 25: 2, # 'ó'
- 24: 2, # 'ö'
- 31: 1, # 'ú'
- 29: 2, # 'ü'
- 42: 1, # 'ő'
- 56: 1, # 'ű'
- },
- 10: { # 'r'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 3, # 'a'
- 18: 3, # 'b'
- 26: 3, # 'c'
- 17: 3, # 'd'
- 1: 3, # 'e'
- 27: 2, # 'f'
- 12: 3, # 'g'
- 20: 2, # 'h'
- 9: 3, # 'i'
- 22: 3, # 'j'
- 7: 3, # 'k'
- 6: 3, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 8: 3, # 'o'
- 23: 2, # 'p'
- 10: 3, # 'r'
- 5: 3, # 's'
- 3: 3, # 't'
- 21: 3, # 'u'
- 19: 3, # 'v'
- 62: 1, # 'x'
- 16: 2, # 'y'
- 11: 3, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 3, # 'á'
- 15: 3, # 'é'
- 30: 2, # 'í'
- 25: 3, # 'ó'
- 24: 3, # 'ö'
- 31: 3, # 'ú'
- 29: 3, # 'ü'
- 42: 2, # 'ő'
- 56: 2, # 'ű'
- },
- 5: { # 's'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 3, # 'a'
- 18: 3, # 'b'
- 26: 2, # 'c'
- 17: 2, # 'd'
- 1: 3, # 'e'
- 27: 2, # 'f'
- 12: 2, # 'g'
- 20: 2, # 'h'
- 9: 3, # 'i'
- 22: 1, # 'j'
- 7: 3, # 'k'
- 6: 2, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 8: 3, # 'o'
- 23: 2, # 'p'
- 10: 3, # 'r'
- 5: 3, # 's'
- 3: 3, # 't'
- 21: 3, # 'u'
- 19: 2, # 'v'
- 62: 0, # 'x'
- 16: 1, # 'y'
- 11: 3, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 3, # 'á'
- 15: 3, # 'é'
- 30: 3, # 'í'
- 25: 3, # 'ó'
- 24: 3, # 'ö'
- 31: 3, # 'ú'
- 29: 3, # 'ü'
- 42: 2, # 'ő'
- 56: 1, # 'ű'
- },
- 3: { # 't'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 3, # 'a'
- 18: 3, # 'b'
- 26: 2, # 'c'
- 17: 1, # 'd'
- 1: 3, # 'e'
- 27: 2, # 'f'
- 12: 1, # 'g'
- 20: 3, # 'h'
- 9: 3, # 'i'
- 22: 3, # 'j'
- 7: 3, # 'k'
- 6: 3, # 'l'
- 13: 2, # 'm'
- 4: 3, # 'n'
- 8: 3, # 'o'
- 23: 1, # 'p'
- 10: 3, # 'r'
- 5: 3, # 's'
- 3: 3, # 't'
- 21: 3, # 'u'
- 19: 3, # 'v'
- 62: 0, # 'x'
- 16: 3, # 'y'
- 11: 1, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 3, # 'á'
- 15: 3, # 'é'
- 30: 2, # 'í'
- 25: 3, # 'ó'
- 24: 3, # 'ö'
- 31: 3, # 'ú'
- 29: 3, # 'ü'
- 42: 3, # 'ő'
- 56: 2, # 'ű'
- },
- 21: { # 'u'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 1, # 'a'
- 18: 2, # 'b'
- 26: 2, # 'c'
- 17: 3, # 'd'
- 1: 2, # 'e'
- 27: 1, # 'f'
- 12: 3, # 'g'
- 20: 2, # 'h'
- 9: 2, # 'i'
- 22: 2, # 'j'
- 7: 3, # 'k'
- 6: 3, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 8: 1, # 'o'
- 23: 2, # 'p'
- 10: 3, # 'r'
- 5: 3, # 's'
- 3: 3, # 't'
- 21: 1, # 'u'
- 19: 3, # 'v'
- 62: 1, # 'x'
- 16: 1, # 'y'
- 11: 2, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 2, # 'á'
- 15: 1, # 'é'
- 30: 1, # 'í'
- 25: 1, # 'ó'
- 24: 0, # 'ö'
- 31: 1, # 'ú'
- 29: 0, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 19: { # 'v'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 3, # 'a'
- 18: 2, # 'b'
- 26: 1, # 'c'
- 17: 1, # 'd'
- 1: 3, # 'e'
- 27: 1, # 'f'
- 12: 1, # 'g'
- 20: 1, # 'h'
- 9: 3, # 'i'
- 22: 1, # 'j'
- 7: 1, # 'k'
- 6: 1, # 'l'
- 13: 1, # 'm'
- 4: 1, # 'n'
- 8: 3, # 'o'
- 23: 1, # 'p'
- 10: 1, # 'r'
- 5: 2, # 's'
- 3: 2, # 't'
- 21: 2, # 'u'
- 19: 2, # 'v'
- 62: 0, # 'x'
- 16: 1, # 'y'
- 11: 1, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 3, # 'á'
- 15: 3, # 'é'
- 30: 2, # 'í'
- 25: 2, # 'ó'
- 24: 2, # 'ö'
- 31: 1, # 'ú'
- 29: 2, # 'ü'
- 42: 1, # 'ő'
- 56: 1, # 'ű'
- },
- 62: { # 'x'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 1, # 'a'
- 18: 1, # 'b'
- 26: 1, # 'c'
- 17: 0, # 'd'
- 1: 1, # 'e'
- 27: 1, # 'f'
- 12: 0, # 'g'
- 20: 0, # 'h'
- 9: 1, # 'i'
- 22: 0, # 'j'
- 7: 1, # 'k'
- 6: 1, # 'l'
- 13: 1, # 'm'
- 4: 1, # 'n'
- 8: 1, # 'o'
- 23: 1, # 'p'
- 10: 1, # 'r'
- 5: 1, # 's'
- 3: 1, # 't'
- 21: 1, # 'u'
- 19: 0, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 0, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 1, # 'á'
- 15: 1, # 'é'
- 30: 1, # 'í'
- 25: 1, # 'ó'
- 24: 0, # 'ö'
- 31: 0, # 'ú'
- 29: 0, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 16: { # 'y'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 3, # 'a'
- 18: 2, # 'b'
- 26: 1, # 'c'
- 17: 1, # 'd'
- 1: 3, # 'e'
- 27: 2, # 'f'
- 12: 2, # 'g'
- 20: 2, # 'h'
- 9: 3, # 'i'
- 22: 2, # 'j'
- 7: 2, # 'k'
- 6: 2, # 'l'
- 13: 2, # 'm'
- 4: 3, # 'n'
- 8: 3, # 'o'
- 23: 2, # 'p'
- 10: 2, # 'r'
- 5: 3, # 's'
- 3: 3, # 't'
- 21: 3, # 'u'
- 19: 3, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 2, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 3, # 'á'
- 15: 3, # 'é'
- 30: 2, # 'í'
- 25: 2, # 'ó'
- 24: 3, # 'ö'
- 31: 2, # 'ú'
- 29: 2, # 'ü'
- 42: 1, # 'ő'
- 56: 2, # 'ű'
- },
- 11: { # 'z'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 3, # 'a'
- 18: 2, # 'b'
- 26: 1, # 'c'
- 17: 3, # 'd'
- 1: 3, # 'e'
- 27: 1, # 'f'
- 12: 2, # 'g'
- 20: 2, # 'h'
- 9: 3, # 'i'
- 22: 1, # 'j'
- 7: 3, # 'k'
- 6: 2, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 8: 3, # 'o'
- 23: 1, # 'p'
- 10: 2, # 'r'
- 5: 3, # 's'
- 3: 3, # 't'
- 21: 3, # 'u'
- 19: 2, # 'v'
- 62: 0, # 'x'
- 16: 1, # 'y'
- 11: 3, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 3, # 'á'
- 15: 3, # 'é'
- 30: 3, # 'í'
- 25: 3, # 'ó'
- 24: 3, # 'ö'
- 31: 2, # 'ú'
- 29: 3, # 'ü'
- 42: 2, # 'ő'
- 56: 1, # 'ű'
- },
- 51: { # 'Á'
- 28: 0, # 'A'
- 40: 1, # 'B'
- 54: 1, # 'C'
- 45: 1, # 'D'
- 32: 0, # 'E'
- 50: 1, # 'F'
- 49: 2, # 'G'
- 38: 1, # 'H'
- 39: 1, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 2, # 'L'
- 34: 1, # 'M'
- 35: 2, # 'N'
- 47: 0, # 'O'
- 46: 1, # 'P'
- 43: 2, # 'R'
- 33: 2, # 'S'
- 37: 1, # 'T'
- 57: 0, # 'U'
- 48: 1, # 'V'
- 55: 0, # 'Y'
- 52: 1, # 'Z'
- 2: 0, # 'a'
- 18: 1, # 'b'
- 26: 1, # 'c'
- 17: 1, # 'd'
- 1: 0, # 'e'
- 27: 0, # 'f'
- 12: 1, # 'g'
- 20: 1, # 'h'
- 9: 0, # 'i'
- 22: 1, # 'j'
- 7: 1, # 'k'
- 6: 2, # 'l'
- 13: 2, # 'm'
- 4: 0, # 'n'
- 8: 0, # 'o'
- 23: 1, # 'p'
- 10: 1, # 'r'
- 5: 1, # 's'
- 3: 1, # 't'
- 21: 0, # 'u'
- 19: 0, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 1, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 1, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 0, # 'á'
- 15: 0, # 'é'
- 30: 0, # 'í'
- 25: 0, # 'ó'
- 24: 0, # 'ö'
- 31: 0, # 'ú'
- 29: 0, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 44: { # 'É'
- 28: 0, # 'A'
- 40: 1, # 'B'
- 54: 1, # 'C'
- 45: 1, # 'D'
- 32: 1, # 'E'
- 50: 0, # 'F'
- 49: 2, # 'G'
- 38: 1, # 'H'
- 39: 1, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 2, # 'L'
- 34: 1, # 'M'
- 35: 2, # 'N'
- 47: 0, # 'O'
- 46: 1, # 'P'
- 43: 2, # 'R'
- 33: 2, # 'S'
- 37: 2, # 'T'
- 57: 0, # 'U'
- 48: 1, # 'V'
- 55: 0, # 'Y'
- 52: 1, # 'Z'
- 2: 0, # 'a'
- 18: 1, # 'b'
- 26: 1, # 'c'
- 17: 1, # 'd'
- 1: 0, # 'e'
- 27: 0, # 'f'
- 12: 1, # 'g'
- 20: 1, # 'h'
- 9: 0, # 'i'
- 22: 1, # 'j'
- 7: 1, # 'k'
- 6: 2, # 'l'
- 13: 1, # 'm'
- 4: 2, # 'n'
- 8: 0, # 'o'
- 23: 1, # 'p'
- 10: 2, # 'r'
- 5: 3, # 's'
- 3: 1, # 't'
- 21: 0, # 'u'
- 19: 1, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 0, # 'z'
- 51: 0, # 'Á'
- 44: 1, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 0, # 'á'
- 15: 0, # 'é'
- 30: 0, # 'í'
- 25: 0, # 'ó'
- 24: 0, # 'ö'
- 31: 0, # 'ú'
- 29: 0, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 61: { # 'Í'
- 28: 0, # 'A'
- 40: 1, # 'B'
- 54: 1, # 'C'
- 45: 1, # 'D'
- 32: 0, # 'E'
- 50: 1, # 'F'
- 49: 1, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 1, # 'J'
- 36: 0, # 'K'
- 41: 1, # 'L'
- 34: 1, # 'M'
- 35: 1, # 'N'
- 47: 0, # 'O'
- 46: 1, # 'P'
- 43: 1, # 'R'
- 33: 1, # 'S'
- 37: 1, # 'T'
- 57: 0, # 'U'
- 48: 1, # 'V'
- 55: 0, # 'Y'
- 52: 1, # 'Z'
- 2: 0, # 'a'
- 18: 0, # 'b'
- 26: 0, # 'c'
- 17: 0, # 'd'
- 1: 0, # 'e'
- 27: 0, # 'f'
- 12: 2, # 'g'
- 20: 0, # 'h'
- 9: 0, # 'i'
- 22: 0, # 'j'
- 7: 0, # 'k'
- 6: 0, # 'l'
- 13: 1, # 'm'
- 4: 0, # 'n'
- 8: 0, # 'o'
- 23: 0, # 'p'
- 10: 1, # 'r'
- 5: 0, # 's'
- 3: 1, # 't'
- 21: 0, # 'u'
- 19: 0, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 1, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 0, # 'á'
- 15: 0, # 'é'
- 30: 0, # 'í'
- 25: 0, # 'ó'
- 24: 0, # 'ö'
- 31: 0, # 'ú'
- 29: 0, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 58: { # 'Ó'
- 28: 1, # 'A'
- 40: 1, # 'B'
- 54: 1, # 'C'
- 45: 1, # 'D'
- 32: 0, # 'E'
- 50: 1, # 'F'
- 49: 1, # 'G'
- 38: 1, # 'H'
- 39: 1, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 2, # 'L'
- 34: 1, # 'M'
- 35: 1, # 'N'
- 47: 0, # 'O'
- 46: 1, # 'P'
- 43: 1, # 'R'
- 33: 1, # 'S'
- 37: 1, # 'T'
- 57: 0, # 'U'
- 48: 1, # 'V'
- 55: 0, # 'Y'
- 52: 1, # 'Z'
- 2: 0, # 'a'
- 18: 1, # 'b'
- 26: 1, # 'c'
- 17: 1, # 'd'
- 1: 0, # 'e'
- 27: 0, # 'f'
- 12: 0, # 'g'
- 20: 2, # 'h'
- 9: 0, # 'i'
- 22: 0, # 'j'
- 7: 1, # 'k'
- 6: 1, # 'l'
- 13: 0, # 'm'
- 4: 1, # 'n'
- 8: 0, # 'o'
- 23: 1, # 'p'
- 10: 1, # 'r'
- 5: 1, # 's'
- 3: 0, # 't'
- 21: 0, # 'u'
- 19: 1, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 1, # 'z'
- 51: 0, # 'Á'
- 44: 1, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 0, # 'á'
- 15: 0, # 'é'
- 30: 0, # 'í'
- 25: 0, # 'ó'
- 24: 0, # 'ö'
- 31: 0, # 'ú'
- 29: 0, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 59: { # 'Ö'
- 28: 0, # 'A'
- 40: 1, # 'B'
- 54: 1, # 'C'
- 45: 1, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 1, # 'G'
- 38: 1, # 'H'
- 39: 0, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 1, # 'L'
- 34: 1, # 'M'
- 35: 1, # 'N'
- 47: 0, # 'O'
- 46: 1, # 'P'
- 43: 1, # 'R'
- 33: 1, # 'S'
- 37: 1, # 'T'
- 57: 0, # 'U'
- 48: 1, # 'V'
- 55: 0, # 'Y'
- 52: 1, # 'Z'
- 2: 0, # 'a'
- 18: 0, # 'b'
- 26: 1, # 'c'
- 17: 1, # 'd'
- 1: 0, # 'e'
- 27: 0, # 'f'
- 12: 0, # 'g'
- 20: 0, # 'h'
- 9: 0, # 'i'
- 22: 0, # 'j'
- 7: 1, # 'k'
- 6: 1, # 'l'
- 13: 1, # 'm'
- 4: 1, # 'n'
- 8: 0, # 'o'
- 23: 0, # 'p'
- 10: 2, # 'r'
- 5: 1, # 's'
- 3: 1, # 't'
- 21: 0, # 'u'
- 19: 1, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 1, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 0, # 'á'
- 15: 0, # 'é'
- 30: 0, # 'í'
- 25: 0, # 'ó'
- 24: 0, # 'ö'
- 31: 0, # 'ú'
- 29: 0, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 60: { # 'Ú'
- 28: 0, # 'A'
- 40: 1, # 'B'
- 54: 1, # 'C'
- 45: 1, # 'D'
- 32: 0, # 'E'
- 50: 1, # 'F'
- 49: 1, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 1, # 'L'
- 34: 1, # 'M'
- 35: 1, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 1, # 'R'
- 33: 1, # 'S'
- 37: 1, # 'T'
- 57: 0, # 'U'
- 48: 1, # 'V'
- 55: 0, # 'Y'
- 52: 1, # 'Z'
- 2: 0, # 'a'
- 18: 0, # 'b'
- 26: 0, # 'c'
- 17: 0, # 'd'
- 1: 0, # 'e'
- 27: 0, # 'f'
- 12: 2, # 'g'
- 20: 0, # 'h'
- 9: 0, # 'i'
- 22: 2, # 'j'
- 7: 0, # 'k'
- 6: 0, # 'l'
- 13: 0, # 'm'
- 4: 1, # 'n'
- 8: 0, # 'o'
- 23: 0, # 'p'
- 10: 1, # 'r'
- 5: 1, # 's'
- 3: 1, # 't'
- 21: 0, # 'u'
- 19: 0, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 0, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 0, # 'á'
- 15: 0, # 'é'
- 30: 0, # 'í'
- 25: 0, # 'ó'
- 24: 0, # 'ö'
- 31: 0, # 'ú'
- 29: 0, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 63: { # 'Ü'
- 28: 0, # 'A'
- 40: 1, # 'B'
- 54: 0, # 'C'
- 45: 1, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 1, # 'G'
- 38: 1, # 'H'
- 39: 0, # 'I'
- 53: 1, # 'J'
- 36: 1, # 'K'
- 41: 1, # 'L'
- 34: 1, # 'M'
- 35: 1, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 1, # 'R'
- 33: 1, # 'S'
- 37: 1, # 'T'
- 57: 0, # 'U'
- 48: 1, # 'V'
- 55: 0, # 'Y'
- 52: 1, # 'Z'
- 2: 0, # 'a'
- 18: 1, # 'b'
- 26: 0, # 'c'
- 17: 1, # 'd'
- 1: 0, # 'e'
- 27: 0, # 'f'
- 12: 1, # 'g'
- 20: 0, # 'h'
- 9: 0, # 'i'
- 22: 0, # 'j'
- 7: 0, # 'k'
- 6: 1, # 'l'
- 13: 0, # 'm'
- 4: 1, # 'n'
- 8: 0, # 'o'
- 23: 0, # 'p'
- 10: 1, # 'r'
- 5: 1, # 's'
- 3: 1, # 't'
- 21: 0, # 'u'
- 19: 1, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 1, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 0, # 'á'
- 15: 0, # 'é'
- 30: 0, # 'í'
- 25: 0, # 'ó'
- 24: 0, # 'ö'
- 31: 0, # 'ú'
- 29: 0, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 14: { # 'á'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 1, # 'a'
- 18: 3, # 'b'
- 26: 3, # 'c'
- 17: 3, # 'd'
- 1: 1, # 'e'
- 27: 2, # 'f'
- 12: 3, # 'g'
- 20: 2, # 'h'
- 9: 2, # 'i'
- 22: 3, # 'j'
- 7: 3, # 'k'
- 6: 3, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 8: 1, # 'o'
- 23: 2, # 'p'
- 10: 3, # 'r'
- 5: 3, # 's'
- 3: 3, # 't'
- 21: 2, # 'u'
- 19: 3, # 'v'
- 62: 0, # 'x'
- 16: 1, # 'y'
- 11: 3, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 1, # 'á'
- 15: 2, # 'é'
- 30: 1, # 'í'
- 25: 0, # 'ó'
- 24: 1, # 'ö'
- 31: 0, # 'ú'
- 29: 1, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 15: { # 'é'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 1, # 'a'
- 18: 3, # 'b'
- 26: 2, # 'c'
- 17: 3, # 'd'
- 1: 1, # 'e'
- 27: 1, # 'f'
- 12: 3, # 'g'
- 20: 3, # 'h'
- 9: 2, # 'i'
- 22: 2, # 'j'
- 7: 3, # 'k'
- 6: 3, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 8: 1, # 'o'
- 23: 3, # 'p'
- 10: 3, # 'r'
- 5: 3, # 's'
- 3: 3, # 't'
- 21: 0, # 'u'
- 19: 3, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 3, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 1, # 'á'
- 15: 1, # 'é'
- 30: 0, # 'í'
- 25: 0, # 'ó'
- 24: 0, # 'ö'
- 31: 0, # 'ú'
- 29: 1, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 30: { # 'í'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 0, # 'a'
- 18: 1, # 'b'
- 26: 2, # 'c'
- 17: 1, # 'd'
- 1: 0, # 'e'
- 27: 1, # 'f'
- 12: 3, # 'g'
- 20: 0, # 'h'
- 9: 0, # 'i'
- 22: 1, # 'j'
- 7: 1, # 'k'
- 6: 2, # 'l'
- 13: 2, # 'm'
- 4: 3, # 'n'
- 8: 0, # 'o'
- 23: 1, # 'p'
- 10: 3, # 'r'
- 5: 2, # 's'
- 3: 3, # 't'
- 21: 0, # 'u'
- 19: 3, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 2, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 0, # 'á'
- 15: 0, # 'é'
- 30: 0, # 'í'
- 25: 0, # 'ó'
- 24: 0, # 'ö'
- 31: 0, # 'ú'
- 29: 0, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 25: { # 'ó'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 2, # 'a'
- 18: 3, # 'b'
- 26: 2, # 'c'
- 17: 3, # 'd'
- 1: 1, # 'e'
- 27: 2, # 'f'
- 12: 2, # 'g'
- 20: 2, # 'h'
- 9: 2, # 'i'
- 22: 2, # 'j'
- 7: 3, # 'k'
- 6: 3, # 'l'
- 13: 2, # 'm'
- 4: 3, # 'n'
- 8: 1, # 'o'
- 23: 2, # 'p'
- 10: 3, # 'r'
- 5: 3, # 's'
- 3: 3, # 't'
- 21: 1, # 'u'
- 19: 2, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 3, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 1, # 'á'
- 15: 1, # 'é'
- 30: 1, # 'í'
- 25: 0, # 'ó'
- 24: 1, # 'ö'
- 31: 1, # 'ú'
- 29: 1, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 24: { # 'ö'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 0, # 'a'
- 18: 3, # 'b'
- 26: 1, # 'c'
- 17: 2, # 'd'
- 1: 0, # 'e'
- 27: 1, # 'f'
- 12: 2, # 'g'
- 20: 1, # 'h'
- 9: 0, # 'i'
- 22: 1, # 'j'
- 7: 3, # 'k'
- 6: 3, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 8: 0, # 'o'
- 23: 2, # 'p'
- 10: 3, # 'r'
- 5: 3, # 's'
- 3: 3, # 't'
- 21: 0, # 'u'
- 19: 3, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 3, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 0, # 'á'
- 15: 0, # 'é'
- 30: 0, # 'í'
- 25: 0, # 'ó'
- 24: 0, # 'ö'
- 31: 0, # 'ú'
- 29: 0, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 31: { # 'ú'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 1, # 'a'
- 18: 1, # 'b'
- 26: 2, # 'c'
- 17: 1, # 'd'
- 1: 1, # 'e'
- 27: 2, # 'f'
- 12: 3, # 'g'
- 20: 1, # 'h'
- 9: 1, # 'i'
- 22: 3, # 'j'
- 7: 1, # 'k'
- 6: 3, # 'l'
- 13: 1, # 'm'
- 4: 2, # 'n'
- 8: 0, # 'o'
- 23: 1, # 'p'
- 10: 3, # 'r'
- 5: 3, # 's'
- 3: 2, # 't'
- 21: 1, # 'u'
- 19: 1, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 2, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 1, # 'á'
- 15: 1, # 'é'
- 30: 0, # 'í'
- 25: 0, # 'ó'
- 24: 0, # 'ö'
- 31: 0, # 'ú'
- 29: 0, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 29: { # 'ü'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 1, # 'a'
- 18: 1, # 'b'
- 26: 1, # 'c'
- 17: 2, # 'd'
- 1: 1, # 'e'
- 27: 1, # 'f'
- 12: 3, # 'g'
- 20: 2, # 'h'
- 9: 1, # 'i'
- 22: 1, # 'j'
- 7: 3, # 'k'
- 6: 3, # 'l'
- 13: 1, # 'm'
- 4: 3, # 'n'
- 8: 0, # 'o'
- 23: 1, # 'p'
- 10: 2, # 'r'
- 5: 2, # 's'
- 3: 2, # 't'
- 21: 0, # 'u'
- 19: 2, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 2, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 0, # 'á'
- 15: 1, # 'é'
- 30: 0, # 'í'
- 25: 0, # 'ó'
- 24: 0, # 'ö'
- 31: 0, # 'ú'
- 29: 0, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 42: { # 'ő'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 1, # 'a'
- 18: 2, # 'b'
- 26: 1, # 'c'
- 17: 2, # 'd'
- 1: 1, # 'e'
- 27: 1, # 'f'
- 12: 1, # 'g'
- 20: 1, # 'h'
- 9: 1, # 'i'
- 22: 1, # 'j'
- 7: 2, # 'k'
- 6: 3, # 'l'
- 13: 1, # 'm'
- 4: 2, # 'n'
- 8: 1, # 'o'
- 23: 1, # 'p'
- 10: 2, # 'r'
- 5: 2, # 's'
- 3: 2, # 't'
- 21: 1, # 'u'
- 19: 1, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 2, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 0, # 'á'
- 15: 1, # 'é'
- 30: 1, # 'í'
- 25: 0, # 'ó'
- 24: 0, # 'ö'
- 31: 0, # 'ú'
- 29: 1, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
- 56: { # 'ű'
- 28: 0, # 'A'
- 40: 0, # 'B'
- 54: 0, # 'C'
- 45: 0, # 'D'
- 32: 0, # 'E'
- 50: 0, # 'F'
- 49: 0, # 'G'
- 38: 0, # 'H'
- 39: 0, # 'I'
- 53: 0, # 'J'
- 36: 0, # 'K'
- 41: 0, # 'L'
- 34: 0, # 'M'
- 35: 0, # 'N'
- 47: 0, # 'O'
- 46: 0, # 'P'
- 43: 0, # 'R'
- 33: 0, # 'S'
- 37: 0, # 'T'
- 57: 0, # 'U'
- 48: 0, # 'V'
- 55: 0, # 'Y'
- 52: 0, # 'Z'
- 2: 1, # 'a'
- 18: 1, # 'b'
- 26: 0, # 'c'
- 17: 1, # 'd'
- 1: 1, # 'e'
- 27: 1, # 'f'
- 12: 1, # 'g'
- 20: 1, # 'h'
- 9: 1, # 'i'
- 22: 1, # 'j'
- 7: 1, # 'k'
- 6: 1, # 'l'
- 13: 0, # 'm'
- 4: 2, # 'n'
- 8: 0, # 'o'
- 23: 0, # 'p'
- 10: 1, # 'r'
- 5: 1, # 's'
- 3: 1, # 't'
- 21: 0, # 'u'
- 19: 1, # 'v'
- 62: 0, # 'x'
- 16: 0, # 'y'
- 11: 2, # 'z'
- 51: 0, # 'Á'
- 44: 0, # 'É'
- 61: 0, # 'Í'
- 58: 0, # 'Ó'
- 59: 0, # 'Ö'
- 60: 0, # 'Ú'
- 63: 0, # 'Ü'
- 14: 0, # 'á'
- 15: 0, # 'é'
- 30: 0, # 'í'
- 25: 0, # 'ó'
- 24: 0, # 'ö'
- 31: 0, # 'ú'
- 29: 0, # 'ü'
- 42: 0, # 'ő'
- 56: 0, # 'ű'
- },
-}
-
-# 255: Undefined characters that did not exist in training text
-# 254: Carriage/Return
-# 253: symbol (punctuation) that does not belong to word
-# 252: 0 - 9
-# 251: Control characters
-
-# Character Mapping Table(s):
-WINDOWS_1250_HUNGARIAN_CHAR_TO_ORDER = {
- 0: 255, # '\x00'
- 1: 255, # '\x01'
- 2: 255, # '\x02'
- 3: 255, # '\x03'
- 4: 255, # '\x04'
- 5: 255, # '\x05'
- 6: 255, # '\x06'
- 7: 255, # '\x07'
- 8: 255, # '\x08'
- 9: 255, # '\t'
- 10: 254, # '\n'
- 11: 255, # '\x0b'
- 12: 255, # '\x0c'
- 13: 254, # '\r'
- 14: 255, # '\x0e'
- 15: 255, # '\x0f'
- 16: 255, # '\x10'
- 17: 255, # '\x11'
- 18: 255, # '\x12'
- 19: 255, # '\x13'
- 20: 255, # '\x14'
- 21: 255, # '\x15'
- 22: 255, # '\x16'
- 23: 255, # '\x17'
- 24: 255, # '\x18'
- 25: 255, # '\x19'
- 26: 255, # '\x1a'
- 27: 255, # '\x1b'
- 28: 255, # '\x1c'
- 29: 255, # '\x1d'
- 30: 255, # '\x1e'
- 31: 255, # '\x1f'
- 32: 253, # ' '
- 33: 253, # '!'
- 34: 253, # '"'
- 35: 253, # '#'
- 36: 253, # '$'
- 37: 253, # '%'
- 38: 253, # '&'
- 39: 253, # "'"
- 40: 253, # '('
- 41: 253, # ')'
- 42: 253, # '*'
- 43: 253, # '+'
- 44: 253, # ','
- 45: 253, # '-'
- 46: 253, # '.'
- 47: 253, # '/'
- 48: 252, # '0'
- 49: 252, # '1'
- 50: 252, # '2'
- 51: 252, # '3'
- 52: 252, # '4'
- 53: 252, # '5'
- 54: 252, # '6'
- 55: 252, # '7'
- 56: 252, # '8'
- 57: 252, # '9'
- 58: 253, # ':'
- 59: 253, # ';'
- 60: 253, # '<'
- 61: 253, # '='
- 62: 253, # '>'
- 63: 253, # '?'
- 64: 253, # '@'
- 65: 28, # 'A'
- 66: 40, # 'B'
- 67: 54, # 'C'
- 68: 45, # 'D'
- 69: 32, # 'E'
- 70: 50, # 'F'
- 71: 49, # 'G'
- 72: 38, # 'H'
- 73: 39, # 'I'
- 74: 53, # 'J'
- 75: 36, # 'K'
- 76: 41, # 'L'
- 77: 34, # 'M'
- 78: 35, # 'N'
- 79: 47, # 'O'
- 80: 46, # 'P'
- 81: 72, # 'Q'
- 82: 43, # 'R'
- 83: 33, # 'S'
- 84: 37, # 'T'
- 85: 57, # 'U'
- 86: 48, # 'V'
- 87: 64, # 'W'
- 88: 68, # 'X'
- 89: 55, # 'Y'
- 90: 52, # 'Z'
- 91: 253, # '['
- 92: 253, # '\\'
- 93: 253, # ']'
- 94: 253, # '^'
- 95: 253, # '_'
- 96: 253, # '`'
- 97: 2, # 'a'
- 98: 18, # 'b'
- 99: 26, # 'c'
- 100: 17, # 'd'
- 101: 1, # 'e'
- 102: 27, # 'f'
- 103: 12, # 'g'
- 104: 20, # 'h'
- 105: 9, # 'i'
- 106: 22, # 'j'
- 107: 7, # 'k'
- 108: 6, # 'l'
- 109: 13, # 'm'
- 110: 4, # 'n'
- 111: 8, # 'o'
- 112: 23, # 'p'
- 113: 67, # 'q'
- 114: 10, # 'r'
- 115: 5, # 's'
- 116: 3, # 't'
- 117: 21, # 'u'
- 118: 19, # 'v'
- 119: 65, # 'w'
- 120: 62, # 'x'
- 121: 16, # 'y'
- 122: 11, # 'z'
- 123: 253, # '{'
- 124: 253, # '|'
- 125: 253, # '}'
- 126: 253, # '~'
- 127: 253, # '\x7f'
- 128: 161, # '€'
- 129: 162, # None
- 130: 163, # '‚'
- 131: 164, # None
- 132: 165, # '„'
- 133: 166, # '…'
- 134: 167, # '†'
- 135: 168, # '‡'
- 136: 169, # None
- 137: 170, # '‰'
- 138: 171, # 'Š'
- 139: 172, # '‹'
- 140: 173, # 'Ś'
- 141: 174, # 'Ť'
- 142: 175, # 'Ž'
- 143: 176, # 'Ź'
- 144: 177, # None
- 145: 178, # '‘'
- 146: 179, # '’'
- 147: 180, # '“'
- 148: 78, # '”'
- 149: 181, # '•'
- 150: 69, # '–'
- 151: 182, # '—'
- 152: 183, # None
- 153: 184, # '™'
- 154: 185, # 'š'
- 155: 186, # '›'
- 156: 187, # 'ś'
- 157: 188, # 'ť'
- 158: 189, # 'ž'
- 159: 190, # 'ź'
- 160: 191, # '\xa0'
- 161: 192, # 'ˇ'
- 162: 193, # '˘'
- 163: 194, # 'Ł'
- 164: 195, # '¤'
- 165: 196, # 'Ą'
- 166: 197, # '¦'
- 167: 76, # '§'
- 168: 198, # '¨'
- 169: 199, # '©'
- 170: 200, # 'Ş'
- 171: 201, # '«'
- 172: 202, # '¬'
- 173: 203, # '\xad'
- 174: 204, # '®'
- 175: 205, # 'Ż'
- 176: 81, # '°'
- 177: 206, # '±'
- 178: 207, # '˛'
- 179: 208, # 'ł'
- 180: 209, # '´'
- 181: 210, # 'µ'
- 182: 211, # '¶'
- 183: 212, # '·'
- 184: 213, # '¸'
- 185: 214, # 'ą'
- 186: 215, # 'ş'
- 187: 216, # '»'
- 188: 217, # 'Ľ'
- 189: 218, # '˝'
- 190: 219, # 'ľ'
- 191: 220, # 'ż'
- 192: 221, # 'Ŕ'
- 193: 51, # 'Á'
- 194: 83, # 'Â'
- 195: 222, # 'Ă'
- 196: 80, # 'Ä'
- 197: 223, # 'Ĺ'
- 198: 224, # 'Ć'
- 199: 225, # 'Ç'
- 200: 226, # 'Č'
- 201: 44, # 'É'
- 202: 227, # 'Ę'
- 203: 228, # 'Ë'
- 204: 229, # 'Ě'
- 205: 61, # 'Í'
- 206: 230, # 'Î'
- 207: 231, # 'Ď'
- 208: 232, # 'Đ'
- 209: 233, # 'Ń'
- 210: 234, # 'Ň'
- 211: 58, # 'Ó'
- 212: 235, # 'Ô'
- 213: 66, # 'Ő'
- 214: 59, # 'Ö'
- 215: 236, # '×'
- 216: 237, # 'Ř'
- 217: 238, # 'Ů'
- 218: 60, # 'Ú'
- 219: 70, # 'Ű'
- 220: 63, # 'Ü'
- 221: 239, # 'Ý'
- 222: 240, # 'Ţ'
- 223: 241, # 'ß'
- 224: 84, # 'ŕ'
- 225: 14, # 'á'
- 226: 75, # 'â'
- 227: 242, # 'ă'
- 228: 71, # 'ä'
- 229: 82, # 'ĺ'
- 230: 243, # 'ć'
- 231: 73, # 'ç'
- 232: 244, # 'č'
- 233: 15, # 'é'
- 234: 85, # 'ę'
- 235: 79, # 'ë'
- 236: 86, # 'ě'
- 237: 30, # 'í'
- 238: 77, # 'î'
- 239: 87, # 'ď'
- 240: 245, # 'đ'
- 241: 246, # 'ń'
- 242: 247, # 'ň'
- 243: 25, # 'ó'
- 244: 74, # 'ô'
- 245: 42, # 'ő'
- 246: 24, # 'ö'
- 247: 248, # '÷'
- 248: 249, # 'ř'
- 249: 250, # 'ů'
- 250: 31, # 'ú'
- 251: 56, # 'ű'
- 252: 29, # 'ü'
- 253: 251, # 'ý'
- 254: 252, # 'ţ'
- 255: 253, # '˙'
-}
-
-WINDOWS_1250_HUNGARIAN_MODEL = SingleByteCharSetModel(
- charset_name="windows-1250",
- language="Hungarian",
- char_to_order_map=WINDOWS_1250_HUNGARIAN_CHAR_TO_ORDER,
- language_model=HUNGARIAN_LANG_MODEL,
- typical_positive_ratio=0.947368,
- keep_ascii_letters=True,
- alphabet="ABCDEFGHIJKLMNOPRSTUVZabcdefghijklmnoprstuvzÁÉÍÓÖÚÜáéíóöúüŐőŰű",
-)
-
-ISO_8859_2_HUNGARIAN_CHAR_TO_ORDER = {
- 0: 255, # '\x00'
- 1: 255, # '\x01'
- 2: 255, # '\x02'
- 3: 255, # '\x03'
- 4: 255, # '\x04'
- 5: 255, # '\x05'
- 6: 255, # '\x06'
- 7: 255, # '\x07'
- 8: 255, # '\x08'
- 9: 255, # '\t'
- 10: 254, # '\n'
- 11: 255, # '\x0b'
- 12: 255, # '\x0c'
- 13: 254, # '\r'
- 14: 255, # '\x0e'
- 15: 255, # '\x0f'
- 16: 255, # '\x10'
- 17: 255, # '\x11'
- 18: 255, # '\x12'
- 19: 255, # '\x13'
- 20: 255, # '\x14'
- 21: 255, # '\x15'
- 22: 255, # '\x16'
- 23: 255, # '\x17'
- 24: 255, # '\x18'
- 25: 255, # '\x19'
- 26: 255, # '\x1a'
- 27: 255, # '\x1b'
- 28: 255, # '\x1c'
- 29: 255, # '\x1d'
- 30: 255, # '\x1e'
- 31: 255, # '\x1f'
- 32: 253, # ' '
- 33: 253, # '!'
- 34: 253, # '"'
- 35: 253, # '#'
- 36: 253, # '$'
- 37: 253, # '%'
- 38: 253, # '&'
- 39: 253, # "'"
- 40: 253, # '('
- 41: 253, # ')'
- 42: 253, # '*'
- 43: 253, # '+'
- 44: 253, # ','
- 45: 253, # '-'
- 46: 253, # '.'
- 47: 253, # '/'
- 48: 252, # '0'
- 49: 252, # '1'
- 50: 252, # '2'
- 51: 252, # '3'
- 52: 252, # '4'
- 53: 252, # '5'
- 54: 252, # '6'
- 55: 252, # '7'
- 56: 252, # '8'
- 57: 252, # '9'
- 58: 253, # ':'
- 59: 253, # ';'
- 60: 253, # '<'
- 61: 253, # '='
- 62: 253, # '>'
- 63: 253, # '?'
- 64: 253, # '@'
- 65: 28, # 'A'
- 66: 40, # 'B'
- 67: 54, # 'C'
- 68: 45, # 'D'
- 69: 32, # 'E'
- 70: 50, # 'F'
- 71: 49, # 'G'
- 72: 38, # 'H'
- 73: 39, # 'I'
- 74: 53, # 'J'
- 75: 36, # 'K'
- 76: 41, # 'L'
- 77: 34, # 'M'
- 78: 35, # 'N'
- 79: 47, # 'O'
- 80: 46, # 'P'
- 81: 71, # 'Q'
- 82: 43, # 'R'
- 83: 33, # 'S'
- 84: 37, # 'T'
- 85: 57, # 'U'
- 86: 48, # 'V'
- 87: 64, # 'W'
- 88: 68, # 'X'
- 89: 55, # 'Y'
- 90: 52, # 'Z'
- 91: 253, # '['
- 92: 253, # '\\'
- 93: 253, # ']'
- 94: 253, # '^'
- 95: 253, # '_'
- 96: 253, # '`'
- 97: 2, # 'a'
- 98: 18, # 'b'
- 99: 26, # 'c'
- 100: 17, # 'd'
- 101: 1, # 'e'
- 102: 27, # 'f'
- 103: 12, # 'g'
- 104: 20, # 'h'
- 105: 9, # 'i'
- 106: 22, # 'j'
- 107: 7, # 'k'
- 108: 6, # 'l'
- 109: 13, # 'm'
- 110: 4, # 'n'
- 111: 8, # 'o'
- 112: 23, # 'p'
- 113: 67, # 'q'
- 114: 10, # 'r'
- 115: 5, # 's'
- 116: 3, # 't'
- 117: 21, # 'u'
- 118: 19, # 'v'
- 119: 65, # 'w'
- 120: 62, # 'x'
- 121: 16, # 'y'
- 122: 11, # 'z'
- 123: 253, # '{'
- 124: 253, # '|'
- 125: 253, # '}'
- 126: 253, # '~'
- 127: 253, # '\x7f'
- 128: 159, # '\x80'
- 129: 160, # '\x81'
- 130: 161, # '\x82'
- 131: 162, # '\x83'
- 132: 163, # '\x84'
- 133: 164, # '\x85'
- 134: 165, # '\x86'
- 135: 166, # '\x87'
- 136: 167, # '\x88'
- 137: 168, # '\x89'
- 138: 169, # '\x8a'
- 139: 170, # '\x8b'
- 140: 171, # '\x8c'
- 141: 172, # '\x8d'
- 142: 173, # '\x8e'
- 143: 174, # '\x8f'
- 144: 175, # '\x90'
- 145: 176, # '\x91'
- 146: 177, # '\x92'
- 147: 178, # '\x93'
- 148: 179, # '\x94'
- 149: 180, # '\x95'
- 150: 181, # '\x96'
- 151: 182, # '\x97'
- 152: 183, # '\x98'
- 153: 184, # '\x99'
- 154: 185, # '\x9a'
- 155: 186, # '\x9b'
- 156: 187, # '\x9c'
- 157: 188, # '\x9d'
- 158: 189, # '\x9e'
- 159: 190, # '\x9f'
- 160: 191, # '\xa0'
- 161: 192, # 'Ą'
- 162: 193, # '˘'
- 163: 194, # 'Ł'
- 164: 195, # '¤'
- 165: 196, # 'Ľ'
- 166: 197, # 'Ś'
- 167: 75, # '§'
- 168: 198, # '¨'
- 169: 199, # 'Š'
- 170: 200, # 'Ş'
- 171: 201, # 'Ť'
- 172: 202, # 'Ź'
- 173: 203, # '\xad'
- 174: 204, # 'Ž'
- 175: 205, # 'Ż'
- 176: 79, # '°'
- 177: 206, # 'ą'
- 178: 207, # '˛'
- 179: 208, # 'ł'
- 180: 209, # '´'
- 181: 210, # 'ľ'
- 182: 211, # 'ś'
- 183: 212, # 'ˇ'
- 184: 213, # '¸'
- 185: 214, # 'š'
- 186: 215, # 'ş'
- 187: 216, # 'ť'
- 188: 217, # 'ź'
- 189: 218, # '˝'
- 190: 219, # 'ž'
- 191: 220, # 'ż'
- 192: 221, # 'Ŕ'
- 193: 51, # 'Á'
- 194: 81, # 'Â'
- 195: 222, # 'Ă'
- 196: 78, # 'Ä'
- 197: 223, # 'Ĺ'
- 198: 224, # 'Ć'
- 199: 225, # 'Ç'
- 200: 226, # 'Č'
- 201: 44, # 'É'
- 202: 227, # 'Ę'
- 203: 228, # 'Ë'
- 204: 229, # 'Ě'
- 205: 61, # 'Í'
- 206: 230, # 'Î'
- 207: 231, # 'Ď'
- 208: 232, # 'Đ'
- 209: 233, # 'Ń'
- 210: 234, # 'Ň'
- 211: 58, # 'Ó'
- 212: 235, # 'Ô'
- 213: 66, # 'Ő'
- 214: 59, # 'Ö'
- 215: 236, # '×'
- 216: 237, # 'Ř'
- 217: 238, # 'Ů'
- 218: 60, # 'Ú'
- 219: 69, # 'Ű'
- 220: 63, # 'Ü'
- 221: 239, # 'Ý'
- 222: 240, # 'Ţ'
- 223: 241, # 'ß'
- 224: 82, # 'ŕ'
- 225: 14, # 'á'
- 226: 74, # 'â'
- 227: 242, # 'ă'
- 228: 70, # 'ä'
- 229: 80, # 'ĺ'
- 230: 243, # 'ć'
- 231: 72, # 'ç'
- 232: 244, # 'č'
- 233: 15, # 'é'
- 234: 83, # 'ę'
- 235: 77, # 'ë'
- 236: 84, # 'ě'
- 237: 30, # 'í'
- 238: 76, # 'î'
- 239: 85, # 'ď'
- 240: 245, # 'đ'
- 241: 246, # 'ń'
- 242: 247, # 'ň'
- 243: 25, # 'ó'
- 244: 73, # 'ô'
- 245: 42, # 'ő'
- 246: 24, # 'ö'
- 247: 248, # '÷'
- 248: 249, # 'ř'
- 249: 250, # 'ů'
- 250: 31, # 'ú'
- 251: 56, # 'ű'
- 252: 29, # 'ü'
- 253: 251, # 'ý'
- 254: 252, # 'ţ'
- 255: 253, # '˙'
-}
-
-ISO_8859_2_HUNGARIAN_MODEL = SingleByteCharSetModel(
- charset_name="ISO-8859-2",
- language="Hungarian",
- char_to_order_map=ISO_8859_2_HUNGARIAN_CHAR_TO_ORDER,
- language_model=HUNGARIAN_LANG_MODEL,
- typical_positive_ratio=0.947368,
- keep_ascii_letters=True,
- alphabet="ABCDEFGHIJKLMNOPRSTUVZabcdefghijklmnoprstuvzÁÉÍÓÖÚÜáéíóöúüŐőŰű",
-)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/langrussianmodel.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/langrussianmodel.py
deleted file mode 100644
index 0d5b178..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/langrussianmodel.py
+++ /dev/null
@@ -1,5725 +0,0 @@
-from chardet.sbcharsetprober import SingleByteCharSetModel
-
-# 3: Positive
-# 2: Likely
-# 1: Unlikely
-# 0: Negative
-
-RUSSIAN_LANG_MODEL = {
- 37: { # 'А'
- 37: 0, # 'А'
- 44: 1, # 'Б'
- 33: 1, # 'В'
- 46: 1, # 'Г'
- 41: 1, # 'Д'
- 48: 1, # 'Е'
- 56: 1, # 'Ж'
- 51: 1, # 'З'
- 42: 1, # 'И'
- 60: 1, # 'Й'
- 36: 1, # 'К'
- 49: 1, # 'Л'
- 38: 1, # 'М'
- 31: 2, # 'Н'
- 34: 1, # 'О'
- 35: 1, # 'П'
- 45: 1, # 'Р'
- 32: 1, # 'С'
- 40: 1, # 'Т'
- 52: 1, # 'У'
- 53: 1, # 'Ф'
- 55: 1, # 'Х'
- 58: 1, # 'Ц'
- 50: 1, # 'Ч'
- 57: 1, # 'Ш'
- 63: 1, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 1, # 'Ю'
- 43: 1, # 'Я'
- 3: 1, # 'а'
- 21: 2, # 'б'
- 10: 2, # 'в'
- 19: 2, # 'г'
- 13: 2, # 'д'
- 2: 0, # 'е'
- 24: 1, # 'ж'
- 20: 1, # 'з'
- 4: 0, # 'и'
- 23: 1, # 'й'
- 11: 2, # 'к'
- 8: 3, # 'л'
- 12: 2, # 'м'
- 5: 2, # 'н'
- 1: 0, # 'о'
- 15: 2, # 'п'
- 9: 2, # 'р'
- 7: 2, # 'с'
- 6: 2, # 'т'
- 14: 2, # 'у'
- 39: 2, # 'ф'
- 26: 2, # 'х'
- 28: 0, # 'ц'
- 22: 1, # 'ч'
- 25: 2, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 1, # 'э'
- 27: 0, # 'ю'
- 16: 0, # 'я'
- },
- 44: { # 'Б'
- 37: 1, # 'А'
- 44: 0, # 'Б'
- 33: 1, # 'В'
- 46: 1, # 'Г'
- 41: 0, # 'Д'
- 48: 1, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 1, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 1, # 'Л'
- 38: 1, # 'М'
- 31: 1, # 'Н'
- 34: 1, # 'О'
- 35: 0, # 'П'
- 45: 1, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 1, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 1, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 1, # 'Я'
- 3: 2, # 'а'
- 21: 0, # 'б'
- 10: 0, # 'в'
- 19: 0, # 'г'
- 13: 1, # 'д'
- 2: 3, # 'е'
- 24: 0, # 'ж'
- 20: 0, # 'з'
- 4: 2, # 'и'
- 23: 0, # 'й'
- 11: 0, # 'к'
- 8: 2, # 'л'
- 12: 0, # 'м'
- 5: 0, # 'н'
- 1: 3, # 'о'
- 15: 0, # 'п'
- 9: 2, # 'р'
- 7: 0, # 'с'
- 6: 0, # 'т'
- 14: 2, # 'у'
- 39: 0, # 'ф'
- 26: 0, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 0, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 2, # 'ы'
- 17: 1, # 'ь'
- 30: 2, # 'э'
- 27: 1, # 'ю'
- 16: 1, # 'я'
- },
- 33: { # 'В'
- 37: 2, # 'А'
- 44: 0, # 'Б'
- 33: 1, # 'В'
- 46: 0, # 'Г'
- 41: 1, # 'Д'
- 48: 1, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 1, # 'И'
- 60: 0, # 'Й'
- 36: 1, # 'К'
- 49: 1, # 'Л'
- 38: 1, # 'М'
- 31: 1, # 'Н'
- 34: 1, # 'О'
- 35: 1, # 'П'
- 45: 1, # 'Р'
- 32: 1, # 'С'
- 40: 1, # 'Т'
- 52: 1, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 1, # 'Ш'
- 63: 0, # 'Щ'
- 62: 1, # 'Ы'
- 61: 1, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 1, # 'Я'
- 3: 2, # 'а'
- 21: 1, # 'б'
- 10: 1, # 'в'
- 19: 1, # 'г'
- 13: 2, # 'д'
- 2: 3, # 'е'
- 24: 0, # 'ж'
- 20: 2, # 'з'
- 4: 2, # 'и'
- 23: 0, # 'й'
- 11: 1, # 'к'
- 8: 2, # 'л'
- 12: 2, # 'м'
- 5: 2, # 'н'
- 1: 3, # 'о'
- 15: 2, # 'п'
- 9: 2, # 'р'
- 7: 3, # 'с'
- 6: 2, # 'т'
- 14: 2, # 'у'
- 39: 0, # 'ф'
- 26: 1, # 'х'
- 28: 1, # 'ц'
- 22: 2, # 'ч'
- 25: 1, # 'ш'
- 29: 0, # 'щ'
- 54: 1, # 'ъ'
- 18: 3, # 'ы'
- 17: 1, # 'ь'
- 30: 2, # 'э'
- 27: 0, # 'ю'
- 16: 1, # 'я'
- },
- 46: { # 'Г'
- 37: 1, # 'А'
- 44: 1, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 1, # 'Д'
- 48: 1, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 1, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 1, # 'Л'
- 38: 1, # 'М'
- 31: 1, # 'Н'
- 34: 1, # 'О'
- 35: 1, # 'П'
- 45: 1, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 1, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 2, # 'а'
- 21: 0, # 'б'
- 10: 1, # 'в'
- 19: 0, # 'г'
- 13: 2, # 'д'
- 2: 2, # 'е'
- 24: 0, # 'ж'
- 20: 0, # 'з'
- 4: 2, # 'и'
- 23: 0, # 'й'
- 11: 0, # 'к'
- 8: 2, # 'л'
- 12: 1, # 'м'
- 5: 1, # 'н'
- 1: 3, # 'о'
- 15: 0, # 'п'
- 9: 2, # 'р'
- 7: 0, # 'с'
- 6: 0, # 'т'
- 14: 2, # 'у'
- 39: 0, # 'ф'
- 26: 0, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 0, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 1, # 'ь'
- 30: 1, # 'э'
- 27: 1, # 'ю'
- 16: 0, # 'я'
- },
- 41: { # 'Д'
- 37: 1, # 'А'
- 44: 0, # 'Б'
- 33: 1, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 2, # 'Е'
- 56: 1, # 'Ж'
- 51: 0, # 'З'
- 42: 1, # 'И'
- 60: 0, # 'Й'
- 36: 1, # 'К'
- 49: 1, # 'Л'
- 38: 0, # 'М'
- 31: 1, # 'Н'
- 34: 1, # 'О'
- 35: 0, # 'П'
- 45: 1, # 'Р'
- 32: 1, # 'С'
- 40: 0, # 'Т'
- 52: 1, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 1, # 'Ц'
- 50: 1, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 1, # 'Ы'
- 61: 1, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 1, # 'Я'
- 3: 3, # 'а'
- 21: 0, # 'б'
- 10: 2, # 'в'
- 19: 0, # 'г'
- 13: 0, # 'д'
- 2: 2, # 'е'
- 24: 3, # 'ж'
- 20: 1, # 'з'
- 4: 2, # 'и'
- 23: 0, # 'й'
- 11: 0, # 'к'
- 8: 2, # 'л'
- 12: 1, # 'м'
- 5: 1, # 'н'
- 1: 3, # 'о'
- 15: 0, # 'п'
- 9: 2, # 'р'
- 7: 0, # 'с'
- 6: 0, # 'т'
- 14: 2, # 'у'
- 39: 0, # 'ф'
- 26: 1, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 0, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 1, # 'ы'
- 17: 1, # 'ь'
- 30: 2, # 'э'
- 27: 1, # 'ю'
- 16: 1, # 'я'
- },
- 48: { # 'Е'
- 37: 1, # 'А'
- 44: 1, # 'Б'
- 33: 1, # 'В'
- 46: 1, # 'Г'
- 41: 1, # 'Д'
- 48: 1, # 'Е'
- 56: 1, # 'Ж'
- 51: 1, # 'З'
- 42: 1, # 'И'
- 60: 1, # 'Й'
- 36: 1, # 'К'
- 49: 1, # 'Л'
- 38: 1, # 'М'
- 31: 2, # 'Н'
- 34: 1, # 'О'
- 35: 1, # 'П'
- 45: 2, # 'Р'
- 32: 2, # 'С'
- 40: 1, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 1, # 'Х'
- 58: 1, # 'Ц'
- 50: 1, # 'Ч'
- 57: 1, # 'Ш'
- 63: 1, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 1, # 'Я'
- 3: 0, # 'а'
- 21: 0, # 'б'
- 10: 2, # 'в'
- 19: 2, # 'г'
- 13: 2, # 'д'
- 2: 2, # 'е'
- 24: 1, # 'ж'
- 20: 1, # 'з'
- 4: 0, # 'и'
- 23: 2, # 'й'
- 11: 1, # 'к'
- 8: 2, # 'л'
- 12: 2, # 'м'
- 5: 1, # 'н'
- 1: 0, # 'о'
- 15: 1, # 'п'
- 9: 1, # 'р'
- 7: 3, # 'с'
- 6: 0, # 'т'
- 14: 0, # 'у'
- 39: 1, # 'ф'
- 26: 1, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 1, # 'ш'
- 29: 2, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 0, # 'э'
- 27: 1, # 'ю'
- 16: 0, # 'я'
- },
- 56: { # 'Ж'
- 37: 1, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 1, # 'Д'
- 48: 1, # 'Е'
- 56: 0, # 'Ж'
- 51: 1, # 'З'
- 42: 1, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 1, # 'Н'
- 34: 1, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 1, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 2, # 'а'
- 21: 1, # 'б'
- 10: 0, # 'в'
- 19: 1, # 'г'
- 13: 1, # 'д'
- 2: 2, # 'е'
- 24: 1, # 'ж'
- 20: 0, # 'з'
- 4: 2, # 'и'
- 23: 0, # 'й'
- 11: 0, # 'к'
- 8: 0, # 'л'
- 12: 1, # 'м'
- 5: 0, # 'н'
- 1: 2, # 'о'
- 15: 0, # 'п'
- 9: 1, # 'р'
- 7: 0, # 'с'
- 6: 0, # 'т'
- 14: 2, # 'у'
- 39: 0, # 'ф'
- 26: 0, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 0, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 0, # 'э'
- 27: 2, # 'ю'
- 16: 0, # 'я'
- },
- 51: { # 'З'
- 37: 1, # 'А'
- 44: 0, # 'Б'
- 33: 1, # 'В'
- 46: 1, # 'Г'
- 41: 1, # 'Д'
- 48: 1, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 1, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 1, # 'Л'
- 38: 1, # 'М'
- 31: 1, # 'Н'
- 34: 1, # 'О'
- 35: 0, # 'П'
- 45: 1, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 1, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 1, # 'Ы'
- 61: 1, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 3, # 'а'
- 21: 1, # 'б'
- 10: 2, # 'в'
- 19: 0, # 'г'
- 13: 2, # 'д'
- 2: 2, # 'е'
- 24: 0, # 'ж'
- 20: 0, # 'з'
- 4: 2, # 'и'
- 23: 0, # 'й'
- 11: 0, # 'к'
- 8: 1, # 'л'
- 12: 1, # 'м'
- 5: 2, # 'н'
- 1: 2, # 'о'
- 15: 0, # 'п'
- 9: 1, # 'р'
- 7: 0, # 'с'
- 6: 0, # 'т'
- 14: 1, # 'у'
- 39: 0, # 'ф'
- 26: 0, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 0, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 1, # 'ы'
- 17: 0, # 'ь'
- 30: 0, # 'э'
- 27: 0, # 'ю'
- 16: 1, # 'я'
- },
- 42: { # 'И'
- 37: 1, # 'А'
- 44: 1, # 'Б'
- 33: 1, # 'В'
- 46: 1, # 'Г'
- 41: 1, # 'Д'
- 48: 2, # 'Е'
- 56: 1, # 'Ж'
- 51: 1, # 'З'
- 42: 1, # 'И'
- 60: 1, # 'Й'
- 36: 1, # 'К'
- 49: 1, # 'Л'
- 38: 1, # 'М'
- 31: 1, # 'Н'
- 34: 1, # 'О'
- 35: 1, # 'П'
- 45: 1, # 'Р'
- 32: 2, # 'С'
- 40: 1, # 'Т'
- 52: 0, # 'У'
- 53: 1, # 'Ф'
- 55: 1, # 'Х'
- 58: 1, # 'Ц'
- 50: 1, # 'Ч'
- 57: 0, # 'Ш'
- 63: 1, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 1, # 'Ю'
- 43: 1, # 'Я'
- 3: 1, # 'а'
- 21: 2, # 'б'
- 10: 2, # 'в'
- 19: 2, # 'г'
- 13: 2, # 'д'
- 2: 2, # 'е'
- 24: 0, # 'ж'
- 20: 2, # 'з'
- 4: 1, # 'и'
- 23: 0, # 'й'
- 11: 1, # 'к'
- 8: 2, # 'л'
- 12: 2, # 'м'
- 5: 2, # 'н'
- 1: 1, # 'о'
- 15: 1, # 'п'
- 9: 2, # 'р'
- 7: 2, # 'с'
- 6: 2, # 'т'
- 14: 1, # 'у'
- 39: 1, # 'ф'
- 26: 2, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 1, # 'ш'
- 29: 1, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 0, # 'э'
- 27: 1, # 'ю'
- 16: 0, # 'я'
- },
- 60: { # 'Й'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 1, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 1, # 'К'
- 49: 1, # 'Л'
- 38: 0, # 'М'
- 31: 1, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 1, # 'С'
- 40: 1, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 1, # 'Х'
- 58: 1, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 0, # 'а'
- 21: 0, # 'б'
- 10: 0, # 'в'
- 19: 0, # 'г'
- 13: 0, # 'д'
- 2: 1, # 'е'
- 24: 0, # 'ж'
- 20: 0, # 'з'
- 4: 0, # 'и'
- 23: 0, # 'й'
- 11: 0, # 'к'
- 8: 0, # 'л'
- 12: 0, # 'м'
- 5: 0, # 'н'
- 1: 2, # 'о'
- 15: 0, # 'п'
- 9: 0, # 'р'
- 7: 0, # 'с'
- 6: 0, # 'т'
- 14: 0, # 'у'
- 39: 0, # 'ф'
- 26: 0, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 0, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 0, # 'э'
- 27: 0, # 'ю'
- 16: 0, # 'я'
- },
- 36: { # 'К'
- 37: 2, # 'А'
- 44: 0, # 'Б'
- 33: 1, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 1, # 'Е'
- 56: 0, # 'Ж'
- 51: 1, # 'З'
- 42: 1, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 1, # 'Л'
- 38: 0, # 'М'
- 31: 1, # 'Н'
- 34: 2, # 'О'
- 35: 1, # 'П'
- 45: 1, # 'Р'
- 32: 1, # 'С'
- 40: 1, # 'Т'
- 52: 1, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 1, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 3, # 'а'
- 21: 0, # 'б'
- 10: 1, # 'в'
- 19: 0, # 'г'
- 13: 0, # 'д'
- 2: 2, # 'е'
- 24: 0, # 'ж'
- 20: 0, # 'з'
- 4: 2, # 'и'
- 23: 0, # 'й'
- 11: 0, # 'к'
- 8: 2, # 'л'
- 12: 0, # 'м'
- 5: 1, # 'н'
- 1: 3, # 'о'
- 15: 0, # 'п'
- 9: 2, # 'р'
- 7: 2, # 'с'
- 6: 2, # 'т'
- 14: 2, # 'у'
- 39: 0, # 'ф'
- 26: 1, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 0, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 1, # 'ы'
- 17: 1, # 'ь'
- 30: 2, # 'э'
- 27: 1, # 'ю'
- 16: 0, # 'я'
- },
- 49: { # 'Л'
- 37: 2, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 1, # 'Г'
- 41: 0, # 'Д'
- 48: 1, # 'Е'
- 56: 1, # 'Ж'
- 51: 0, # 'З'
- 42: 1, # 'И'
- 60: 0, # 'Й'
- 36: 1, # 'К'
- 49: 1, # 'Л'
- 38: 1, # 'М'
- 31: 0, # 'Н'
- 34: 1, # 'О'
- 35: 1, # 'П'
- 45: 0, # 'Р'
- 32: 1, # 'С'
- 40: 1, # 'Т'
- 52: 1, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 1, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 1, # 'Ы'
- 61: 1, # 'Ь'
- 47: 0, # 'Э'
- 59: 1, # 'Ю'
- 43: 1, # 'Я'
- 3: 2, # 'а'
- 21: 0, # 'б'
- 10: 0, # 'в'
- 19: 1, # 'г'
- 13: 0, # 'д'
- 2: 2, # 'е'
- 24: 1, # 'ж'
- 20: 0, # 'з'
- 4: 2, # 'и'
- 23: 0, # 'й'
- 11: 0, # 'к'
- 8: 1, # 'л'
- 12: 0, # 'м'
- 5: 1, # 'н'
- 1: 2, # 'о'
- 15: 0, # 'п'
- 9: 0, # 'р'
- 7: 0, # 'с'
- 6: 0, # 'т'
- 14: 2, # 'у'
- 39: 0, # 'ф'
- 26: 1, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 0, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 1, # 'ы'
- 17: 1, # 'ь'
- 30: 2, # 'э'
- 27: 2, # 'ю'
- 16: 1, # 'я'
- },
- 38: { # 'М'
- 37: 1, # 'А'
- 44: 1, # 'Б'
- 33: 1, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 1, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 1, # 'И'
- 60: 0, # 'Й'
- 36: 1, # 'К'
- 49: 1, # 'Л'
- 38: 1, # 'М'
- 31: 1, # 'Н'
- 34: 1, # 'О'
- 35: 1, # 'П'
- 45: 1, # 'Р'
- 32: 1, # 'С'
- 40: 1, # 'Т'
- 52: 1, # 'У'
- 53: 1, # 'Ф'
- 55: 1, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 1, # 'Ы'
- 61: 0, # 'Ь'
- 47: 1, # 'Э'
- 59: 0, # 'Ю'
- 43: 1, # 'Я'
- 3: 3, # 'а'
- 21: 0, # 'б'
- 10: 0, # 'в'
- 19: 1, # 'г'
- 13: 0, # 'д'
- 2: 2, # 'е'
- 24: 0, # 'ж'
- 20: 0, # 'з'
- 4: 3, # 'и'
- 23: 0, # 'й'
- 11: 0, # 'к'
- 8: 1, # 'л'
- 12: 1, # 'м'
- 5: 2, # 'н'
- 1: 3, # 'о'
- 15: 0, # 'п'
- 9: 1, # 'р'
- 7: 1, # 'с'
- 6: 0, # 'т'
- 14: 2, # 'у'
- 39: 0, # 'ф'
- 26: 0, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 0, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 3, # 'ы'
- 17: 1, # 'ь'
- 30: 2, # 'э'
- 27: 1, # 'ю'
- 16: 1, # 'я'
- },
- 31: { # 'Н'
- 37: 2, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 1, # 'Г'
- 41: 1, # 'Д'
- 48: 1, # 'Е'
- 56: 0, # 'Ж'
- 51: 1, # 'З'
- 42: 2, # 'И'
- 60: 0, # 'Й'
- 36: 1, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 1, # 'Н'
- 34: 1, # 'О'
- 35: 0, # 'П'
- 45: 1, # 'Р'
- 32: 1, # 'С'
- 40: 1, # 'Т'
- 52: 1, # 'У'
- 53: 1, # 'Ф'
- 55: 1, # 'Х'
- 58: 1, # 'Ц'
- 50: 1, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 1, # 'Ы'
- 61: 1, # 'Ь'
- 47: 1, # 'Э'
- 59: 0, # 'Ю'
- 43: 1, # 'Я'
- 3: 3, # 'а'
- 21: 0, # 'б'
- 10: 0, # 'в'
- 19: 0, # 'г'
- 13: 0, # 'д'
- 2: 3, # 'е'
- 24: 0, # 'ж'
- 20: 0, # 'з'
- 4: 3, # 'и'
- 23: 0, # 'й'
- 11: 0, # 'к'
- 8: 0, # 'л'
- 12: 0, # 'м'
- 5: 0, # 'н'
- 1: 3, # 'о'
- 15: 0, # 'п'
- 9: 1, # 'р'
- 7: 0, # 'с'
- 6: 0, # 'т'
- 14: 3, # 'у'
- 39: 0, # 'ф'
- 26: 1, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 0, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 1, # 'ы'
- 17: 2, # 'ь'
- 30: 1, # 'э'
- 27: 1, # 'ю'
- 16: 1, # 'я'
- },
- 34: { # 'О'
- 37: 0, # 'А'
- 44: 1, # 'Б'
- 33: 1, # 'В'
- 46: 1, # 'Г'
- 41: 2, # 'Д'
- 48: 1, # 'Е'
- 56: 1, # 'Ж'
- 51: 1, # 'З'
- 42: 1, # 'И'
- 60: 1, # 'Й'
- 36: 1, # 'К'
- 49: 2, # 'Л'
- 38: 1, # 'М'
- 31: 2, # 'Н'
- 34: 1, # 'О'
- 35: 1, # 'П'
- 45: 2, # 'Р'
- 32: 1, # 'С'
- 40: 1, # 'Т'
- 52: 1, # 'У'
- 53: 1, # 'Ф'
- 55: 1, # 'Х'
- 58: 0, # 'Ц'
- 50: 1, # 'Ч'
- 57: 1, # 'Ш'
- 63: 1, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 1, # 'Я'
- 3: 1, # 'а'
- 21: 2, # 'б'
- 10: 1, # 'в'
- 19: 2, # 'г'
- 13: 2, # 'д'
- 2: 0, # 'е'
- 24: 1, # 'ж'
- 20: 1, # 'з'
- 4: 0, # 'и'
- 23: 1, # 'й'
- 11: 2, # 'к'
- 8: 2, # 'л'
- 12: 1, # 'м'
- 5: 3, # 'н'
- 1: 0, # 'о'
- 15: 2, # 'п'
- 9: 2, # 'р'
- 7: 2, # 'с'
- 6: 2, # 'т'
- 14: 1, # 'у'
- 39: 1, # 'ф'
- 26: 2, # 'х'
- 28: 1, # 'ц'
- 22: 2, # 'ч'
- 25: 2, # 'ш'
- 29: 1, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 0, # 'э'
- 27: 0, # 'ю'
- 16: 0, # 'я'
- },
- 35: { # 'П'
- 37: 1, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 1, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 1, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 1, # 'Л'
- 38: 0, # 'М'
- 31: 1, # 'Н'
- 34: 1, # 'О'
- 35: 1, # 'П'
- 45: 2, # 'Р'
- 32: 1, # 'С'
- 40: 1, # 'Т'
- 52: 1, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 1, # 'Ы'
- 61: 1, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 1, # 'Я'
- 3: 2, # 'а'
- 21: 0, # 'б'
- 10: 0, # 'в'
- 19: 0, # 'г'
- 13: 0, # 'д'
- 2: 2, # 'е'
- 24: 0, # 'ж'
- 20: 0, # 'з'
- 4: 2, # 'и'
- 23: 0, # 'й'
- 11: 0, # 'к'
- 8: 2, # 'л'
- 12: 0, # 'м'
- 5: 1, # 'н'
- 1: 3, # 'о'
- 15: 0, # 'п'
- 9: 3, # 'р'
- 7: 1, # 'с'
- 6: 1, # 'т'
- 14: 2, # 'у'
- 39: 1, # 'ф'
- 26: 0, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 1, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 1, # 'ы'
- 17: 2, # 'ь'
- 30: 1, # 'э'
- 27: 0, # 'ю'
- 16: 2, # 'я'
- },
- 45: { # 'Р'
- 37: 2, # 'А'
- 44: 1, # 'Б'
- 33: 1, # 'В'
- 46: 1, # 'Г'
- 41: 1, # 'Д'
- 48: 2, # 'Е'
- 56: 1, # 'Ж'
- 51: 0, # 'З'
- 42: 2, # 'И'
- 60: 0, # 'Й'
- 36: 1, # 'К'
- 49: 1, # 'Л'
- 38: 1, # 'М'
- 31: 1, # 'Н'
- 34: 2, # 'О'
- 35: 0, # 'П'
- 45: 1, # 'Р'
- 32: 1, # 'С'
- 40: 1, # 'Т'
- 52: 1, # 'У'
- 53: 0, # 'Ф'
- 55: 1, # 'Х'
- 58: 1, # 'Ц'
- 50: 1, # 'Ч'
- 57: 1, # 'Ш'
- 63: 0, # 'Щ'
- 62: 1, # 'Ы'
- 61: 1, # 'Ь'
- 47: 1, # 'Э'
- 59: 1, # 'Ю'
- 43: 1, # 'Я'
- 3: 3, # 'а'
- 21: 0, # 'б'
- 10: 1, # 'в'
- 19: 0, # 'г'
- 13: 0, # 'д'
- 2: 2, # 'е'
- 24: 1, # 'ж'
- 20: 0, # 'з'
- 4: 2, # 'и'
- 23: 0, # 'й'
- 11: 0, # 'к'
- 8: 0, # 'л'
- 12: 0, # 'м'
- 5: 0, # 'н'
- 1: 3, # 'о'
- 15: 0, # 'п'
- 9: 1, # 'р'
- 7: 0, # 'с'
- 6: 0, # 'т'
- 14: 2, # 'у'
- 39: 0, # 'ф'
- 26: 0, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 0, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 2, # 'ы'
- 17: 0, # 'ь'
- 30: 1, # 'э'
- 27: 1, # 'ю'
- 16: 2, # 'я'
- },
- 32: { # 'С'
- 37: 1, # 'А'
- 44: 1, # 'Б'
- 33: 1, # 'В'
- 46: 1, # 'Г'
- 41: 1, # 'Д'
- 48: 1, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 1, # 'И'
- 60: 0, # 'Й'
- 36: 1, # 'К'
- 49: 1, # 'Л'
- 38: 1, # 'М'
- 31: 1, # 'Н'
- 34: 1, # 'О'
- 35: 1, # 'П'
- 45: 1, # 'Р'
- 32: 1, # 'С'
- 40: 2, # 'Т'
- 52: 1, # 'У'
- 53: 0, # 'Ф'
- 55: 1, # 'Х'
- 58: 1, # 'Ц'
- 50: 1, # 'Ч'
- 57: 1, # 'Ш'
- 63: 0, # 'Щ'
- 62: 1, # 'Ы'
- 61: 1, # 'Ь'
- 47: 1, # 'Э'
- 59: 1, # 'Ю'
- 43: 1, # 'Я'
- 3: 2, # 'а'
- 21: 1, # 'б'
- 10: 2, # 'в'
- 19: 1, # 'г'
- 13: 2, # 'д'
- 2: 3, # 'е'
- 24: 1, # 'ж'
- 20: 1, # 'з'
- 4: 2, # 'и'
- 23: 0, # 'й'
- 11: 2, # 'к'
- 8: 2, # 'л'
- 12: 2, # 'м'
- 5: 2, # 'н'
- 1: 2, # 'о'
- 15: 2, # 'п'
- 9: 2, # 'р'
- 7: 1, # 'с'
- 6: 3, # 'т'
- 14: 2, # 'у'
- 39: 1, # 'ф'
- 26: 1, # 'х'
- 28: 1, # 'ц'
- 22: 1, # 'ч'
- 25: 0, # 'ш'
- 29: 0, # 'щ'
- 54: 1, # 'ъ'
- 18: 1, # 'ы'
- 17: 1, # 'ь'
- 30: 2, # 'э'
- 27: 1, # 'ю'
- 16: 1, # 'я'
- },
- 40: { # 'Т'
- 37: 1, # 'А'
- 44: 0, # 'Б'
- 33: 1, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 1, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 1, # 'И'
- 60: 0, # 'Й'
- 36: 1, # 'К'
- 49: 1, # 'Л'
- 38: 1, # 'М'
- 31: 1, # 'Н'
- 34: 2, # 'О'
- 35: 0, # 'П'
- 45: 1, # 'Р'
- 32: 1, # 'С'
- 40: 1, # 'Т'
- 52: 1, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 1, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 1, # 'Ы'
- 61: 1, # 'Ь'
- 47: 1, # 'Э'
- 59: 1, # 'Ю'
- 43: 1, # 'Я'
- 3: 3, # 'а'
- 21: 1, # 'б'
- 10: 2, # 'в'
- 19: 0, # 'г'
- 13: 0, # 'д'
- 2: 3, # 'е'
- 24: 0, # 'ж'
- 20: 0, # 'з'
- 4: 2, # 'и'
- 23: 0, # 'й'
- 11: 1, # 'к'
- 8: 1, # 'л'
- 12: 0, # 'м'
- 5: 0, # 'н'
- 1: 3, # 'о'
- 15: 0, # 'п'
- 9: 2, # 'р'
- 7: 1, # 'с'
- 6: 0, # 'т'
- 14: 2, # 'у'
- 39: 0, # 'ф'
- 26: 0, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 0, # 'ш'
- 29: 1, # 'щ'
- 54: 0, # 'ъ'
- 18: 3, # 'ы'
- 17: 1, # 'ь'
- 30: 2, # 'э'
- 27: 1, # 'ю'
- 16: 1, # 'я'
- },
- 52: { # 'У'
- 37: 1, # 'А'
- 44: 1, # 'Б'
- 33: 1, # 'В'
- 46: 1, # 'Г'
- 41: 1, # 'Д'
- 48: 1, # 'Е'
- 56: 1, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 1, # 'Й'
- 36: 1, # 'К'
- 49: 1, # 'Л'
- 38: 1, # 'М'
- 31: 1, # 'Н'
- 34: 1, # 'О'
- 35: 1, # 'П'
- 45: 1, # 'Р'
- 32: 1, # 'С'
- 40: 1, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 1, # 'Х'
- 58: 0, # 'Ц'
- 50: 1, # 'Ч'
- 57: 1, # 'Ш'
- 63: 1, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 1, # 'Ю'
- 43: 0, # 'Я'
- 3: 1, # 'а'
- 21: 2, # 'б'
- 10: 2, # 'в'
- 19: 1, # 'г'
- 13: 2, # 'д'
- 2: 1, # 'е'
- 24: 2, # 'ж'
- 20: 2, # 'з'
- 4: 2, # 'и'
- 23: 1, # 'й'
- 11: 1, # 'к'
- 8: 2, # 'л'
- 12: 2, # 'м'
- 5: 1, # 'н'
- 1: 2, # 'о'
- 15: 1, # 'п'
- 9: 2, # 'р'
- 7: 2, # 'с'
- 6: 2, # 'т'
- 14: 0, # 'у'
- 39: 1, # 'ф'
- 26: 1, # 'х'
- 28: 1, # 'ц'
- 22: 2, # 'ч'
- 25: 1, # 'ш'
- 29: 1, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 2, # 'э'
- 27: 1, # 'ю'
- 16: 0, # 'я'
- },
- 53: { # 'Ф'
- 37: 1, # 'А'
- 44: 1, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 1, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 1, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 1, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 1, # 'О'
- 35: 0, # 'П'
- 45: 1, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 1, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 2, # 'а'
- 21: 0, # 'б'
- 10: 0, # 'в'
- 19: 0, # 'г'
- 13: 0, # 'д'
- 2: 2, # 'е'
- 24: 0, # 'ж'
- 20: 0, # 'з'
- 4: 2, # 'и'
- 23: 0, # 'й'
- 11: 0, # 'к'
- 8: 2, # 'л'
- 12: 0, # 'м'
- 5: 0, # 'н'
- 1: 2, # 'о'
- 15: 0, # 'п'
- 9: 2, # 'р'
- 7: 0, # 'с'
- 6: 1, # 'т'
- 14: 2, # 'у'
- 39: 0, # 'ф'
- 26: 0, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 0, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 1, # 'ь'
- 30: 2, # 'э'
- 27: 0, # 'ю'
- 16: 0, # 'я'
- },
- 55: { # 'Х'
- 37: 1, # 'А'
- 44: 0, # 'Б'
- 33: 1, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 1, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 1, # 'Л'
- 38: 1, # 'М'
- 31: 1, # 'Н'
- 34: 1, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 2, # 'а'
- 21: 0, # 'б'
- 10: 2, # 'в'
- 19: 0, # 'г'
- 13: 0, # 'д'
- 2: 2, # 'е'
- 24: 0, # 'ж'
- 20: 0, # 'з'
- 4: 2, # 'и'
- 23: 0, # 'й'
- 11: 0, # 'к'
- 8: 2, # 'л'
- 12: 1, # 'м'
- 5: 0, # 'н'
- 1: 2, # 'о'
- 15: 0, # 'п'
- 9: 2, # 'р'
- 7: 0, # 'с'
- 6: 0, # 'т'
- 14: 1, # 'у'
- 39: 0, # 'ф'
- 26: 0, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 0, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 1, # 'ь'
- 30: 1, # 'э'
- 27: 0, # 'ю'
- 16: 0, # 'я'
- },
- 58: { # 'Ц'
- 37: 1, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 1, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 1, # 'И'
- 60: 0, # 'Й'
- 36: 1, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 1, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 1, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 1, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 1, # 'а'
- 21: 0, # 'б'
- 10: 1, # 'в'
- 19: 0, # 'г'
- 13: 0, # 'д'
- 2: 2, # 'е'
- 24: 0, # 'ж'
- 20: 0, # 'з'
- 4: 2, # 'и'
- 23: 0, # 'й'
- 11: 0, # 'к'
- 8: 0, # 'л'
- 12: 0, # 'м'
- 5: 0, # 'н'
- 1: 0, # 'о'
- 15: 0, # 'п'
- 9: 0, # 'р'
- 7: 0, # 'с'
- 6: 0, # 'т'
- 14: 1, # 'у'
- 39: 0, # 'ф'
- 26: 0, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 0, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 1, # 'ы'
- 17: 0, # 'ь'
- 30: 0, # 'э'
- 27: 1, # 'ю'
- 16: 0, # 'я'
- },
- 50: { # 'Ч'
- 37: 1, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 1, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 1, # 'И'
- 60: 0, # 'Й'
- 36: 1, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 1, # 'Н'
- 34: 0, # 'О'
- 35: 1, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 1, # 'Т'
- 52: 1, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 1, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 2, # 'а'
- 21: 0, # 'б'
- 10: 0, # 'в'
- 19: 0, # 'г'
- 13: 0, # 'д'
- 2: 2, # 'е'
- 24: 0, # 'ж'
- 20: 0, # 'з'
- 4: 2, # 'и'
- 23: 0, # 'й'
- 11: 0, # 'к'
- 8: 1, # 'л'
- 12: 0, # 'м'
- 5: 0, # 'н'
- 1: 1, # 'о'
- 15: 0, # 'п'
- 9: 1, # 'р'
- 7: 0, # 'с'
- 6: 3, # 'т'
- 14: 2, # 'у'
- 39: 0, # 'ф'
- 26: 0, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 0, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 1, # 'ь'
- 30: 0, # 'э'
- 27: 0, # 'ю'
- 16: 0, # 'я'
- },
- 57: { # 'Ш'
- 37: 1, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 1, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 1, # 'И'
- 60: 0, # 'Й'
- 36: 1, # 'К'
- 49: 1, # 'Л'
- 38: 0, # 'М'
- 31: 1, # 'Н'
- 34: 1, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 1, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 2, # 'а'
- 21: 0, # 'б'
- 10: 1, # 'в'
- 19: 0, # 'г'
- 13: 0, # 'д'
- 2: 2, # 'е'
- 24: 0, # 'ж'
- 20: 0, # 'з'
- 4: 1, # 'и'
- 23: 0, # 'й'
- 11: 1, # 'к'
- 8: 2, # 'л'
- 12: 1, # 'м'
- 5: 1, # 'н'
- 1: 2, # 'о'
- 15: 2, # 'п'
- 9: 1, # 'р'
- 7: 0, # 'с'
- 6: 2, # 'т'
- 14: 2, # 'у'
- 39: 0, # 'ф'
- 26: 1, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 1, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 1, # 'э'
- 27: 0, # 'ю'
- 16: 0, # 'я'
- },
- 63: { # 'Щ'
- 37: 1, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 1, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 1, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 1, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 1, # 'а'
- 21: 0, # 'б'
- 10: 0, # 'в'
- 19: 0, # 'г'
- 13: 0, # 'д'
- 2: 1, # 'е'
- 24: 0, # 'ж'
- 20: 0, # 'з'
- 4: 1, # 'и'
- 23: 0, # 'й'
- 11: 0, # 'к'
- 8: 0, # 'л'
- 12: 0, # 'м'
- 5: 0, # 'н'
- 1: 1, # 'о'
- 15: 0, # 'п'
- 9: 0, # 'р'
- 7: 0, # 'с'
- 6: 0, # 'т'
- 14: 1, # 'у'
- 39: 0, # 'ф'
- 26: 0, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 0, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 0, # 'э'
- 27: 0, # 'ю'
- 16: 0, # 'я'
- },
- 62: { # 'Ы'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 1, # 'В'
- 46: 1, # 'Г'
- 41: 0, # 'Д'
- 48: 1, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 1, # 'Й'
- 36: 1, # 'К'
- 49: 1, # 'Л'
- 38: 1, # 'М'
- 31: 1, # 'Н'
- 34: 0, # 'О'
- 35: 1, # 'П'
- 45: 1, # 'Р'
- 32: 1, # 'С'
- 40: 1, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 1, # 'Х'
- 58: 1, # 'Ц'
- 50: 0, # 'Ч'
- 57: 1, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 0, # 'а'
- 21: 0, # 'б'
- 10: 0, # 'в'
- 19: 0, # 'г'
- 13: 0, # 'д'
- 2: 0, # 'е'
- 24: 0, # 'ж'
- 20: 0, # 'з'
- 4: 0, # 'и'
- 23: 0, # 'й'
- 11: 0, # 'к'
- 8: 0, # 'л'
- 12: 0, # 'м'
- 5: 0, # 'н'
- 1: 0, # 'о'
- 15: 0, # 'п'
- 9: 0, # 'р'
- 7: 0, # 'с'
- 6: 0, # 'т'
- 14: 0, # 'у'
- 39: 0, # 'ф'
- 26: 0, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 0, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 0, # 'э'
- 27: 0, # 'ю'
- 16: 0, # 'я'
- },
- 61: { # 'Ь'
- 37: 0, # 'А'
- 44: 1, # 'Б'
- 33: 1, # 'В'
- 46: 0, # 'Г'
- 41: 1, # 'Д'
- 48: 1, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 1, # 'И'
- 60: 0, # 'Й'
- 36: 1, # 'К'
- 49: 0, # 'Л'
- 38: 1, # 'М'
- 31: 1, # 'Н'
- 34: 1, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 1, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 1, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 1, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 1, # 'Ю'
- 43: 1, # 'Я'
- 3: 0, # 'а'
- 21: 0, # 'б'
- 10: 0, # 'в'
- 19: 0, # 'г'
- 13: 0, # 'д'
- 2: 0, # 'е'
- 24: 0, # 'ж'
- 20: 0, # 'з'
- 4: 0, # 'и'
- 23: 0, # 'й'
- 11: 0, # 'к'
- 8: 0, # 'л'
- 12: 0, # 'м'
- 5: 0, # 'н'
- 1: 0, # 'о'
- 15: 0, # 'п'
- 9: 0, # 'р'
- 7: 0, # 'с'
- 6: 0, # 'т'
- 14: 0, # 'у'
- 39: 0, # 'ф'
- 26: 0, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 0, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 0, # 'э'
- 27: 0, # 'ю'
- 16: 0, # 'я'
- },
- 47: { # 'Э'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 1, # 'В'
- 46: 0, # 'Г'
- 41: 1, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 1, # 'Й'
- 36: 1, # 'К'
- 49: 1, # 'Л'
- 38: 1, # 'М'
- 31: 1, # 'Н'
- 34: 0, # 'О'
- 35: 1, # 'П'
- 45: 1, # 'Р'
- 32: 1, # 'С'
- 40: 1, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 1, # 'а'
- 21: 1, # 'б'
- 10: 2, # 'в'
- 19: 1, # 'г'
- 13: 2, # 'д'
- 2: 0, # 'е'
- 24: 1, # 'ж'
- 20: 0, # 'з'
- 4: 0, # 'и'
- 23: 2, # 'й'
- 11: 2, # 'к'
- 8: 2, # 'л'
- 12: 2, # 'м'
- 5: 2, # 'н'
- 1: 0, # 'о'
- 15: 1, # 'п'
- 9: 2, # 'р'
- 7: 1, # 'с'
- 6: 3, # 'т'
- 14: 1, # 'у'
- 39: 1, # 'ф'
- 26: 1, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 1, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 0, # 'э'
- 27: 0, # 'ю'
- 16: 0, # 'я'
- },
- 59: { # 'Ю'
- 37: 1, # 'А'
- 44: 1, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 1, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 1, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 1, # 'Р'
- 32: 0, # 'С'
- 40: 1, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 1, # 'Ч'
- 57: 0, # 'Ш'
- 63: 1, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 0, # 'а'
- 21: 1, # 'б'
- 10: 0, # 'в'
- 19: 1, # 'г'
- 13: 1, # 'д'
- 2: 0, # 'е'
- 24: 1, # 'ж'
- 20: 0, # 'з'
- 4: 0, # 'и'
- 23: 0, # 'й'
- 11: 1, # 'к'
- 8: 2, # 'л'
- 12: 1, # 'м'
- 5: 2, # 'н'
- 1: 0, # 'о'
- 15: 1, # 'п'
- 9: 1, # 'р'
- 7: 1, # 'с'
- 6: 0, # 'т'
- 14: 0, # 'у'
- 39: 0, # 'ф'
- 26: 1, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 0, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 0, # 'э'
- 27: 0, # 'ю'
- 16: 0, # 'я'
- },
- 43: { # 'Я'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 1, # 'В'
- 46: 1, # 'Г'
- 41: 0, # 'Д'
- 48: 1, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 1, # 'И'
- 60: 0, # 'Й'
- 36: 1, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 1, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 1, # 'С'
- 40: 1, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 1, # 'Х'
- 58: 0, # 'Ц'
- 50: 1, # 'Ч'
- 57: 0, # 'Ш'
- 63: 1, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 1, # 'Ю'
- 43: 1, # 'Я'
- 3: 0, # 'а'
- 21: 1, # 'б'
- 10: 1, # 'в'
- 19: 1, # 'г'
- 13: 1, # 'д'
- 2: 0, # 'е'
- 24: 0, # 'ж'
- 20: 1, # 'з'
- 4: 0, # 'и'
- 23: 1, # 'й'
- 11: 1, # 'к'
- 8: 1, # 'л'
- 12: 1, # 'м'
- 5: 2, # 'н'
- 1: 0, # 'о'
- 15: 1, # 'п'
- 9: 1, # 'р'
- 7: 1, # 'с'
- 6: 0, # 'т'
- 14: 0, # 'у'
- 39: 0, # 'ф'
- 26: 1, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 1, # 'ш'
- 29: 1, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 0, # 'э'
- 27: 0, # 'ю'
- 16: 0, # 'я'
- },
- 3: { # 'а'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 1, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 1, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 2, # 'а'
- 21: 3, # 'б'
- 10: 3, # 'в'
- 19: 3, # 'г'
- 13: 3, # 'д'
- 2: 3, # 'е'
- 24: 3, # 'ж'
- 20: 3, # 'з'
- 4: 3, # 'и'
- 23: 3, # 'й'
- 11: 3, # 'к'
- 8: 3, # 'л'
- 12: 3, # 'м'
- 5: 3, # 'н'
- 1: 2, # 'о'
- 15: 3, # 'п'
- 9: 3, # 'р'
- 7: 3, # 'с'
- 6: 3, # 'т'
- 14: 3, # 'у'
- 39: 2, # 'ф'
- 26: 3, # 'х'
- 28: 3, # 'ц'
- 22: 3, # 'ч'
- 25: 3, # 'ш'
- 29: 3, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 2, # 'э'
- 27: 3, # 'ю'
- 16: 3, # 'я'
- },
- 21: { # 'б'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 1, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 3, # 'а'
- 21: 2, # 'б'
- 10: 2, # 'в'
- 19: 1, # 'г'
- 13: 2, # 'д'
- 2: 3, # 'е'
- 24: 2, # 'ж'
- 20: 1, # 'з'
- 4: 3, # 'и'
- 23: 0, # 'й'
- 11: 2, # 'к'
- 8: 3, # 'л'
- 12: 2, # 'м'
- 5: 3, # 'н'
- 1: 3, # 'о'
- 15: 1, # 'п'
- 9: 3, # 'р'
- 7: 3, # 'с'
- 6: 2, # 'т'
- 14: 3, # 'у'
- 39: 0, # 'ф'
- 26: 2, # 'х'
- 28: 1, # 'ц'
- 22: 1, # 'ч'
- 25: 2, # 'ш'
- 29: 3, # 'щ'
- 54: 2, # 'ъ'
- 18: 3, # 'ы'
- 17: 2, # 'ь'
- 30: 1, # 'э'
- 27: 2, # 'ю'
- 16: 3, # 'я'
- },
- 10: { # 'в'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 3, # 'а'
- 21: 2, # 'б'
- 10: 2, # 'в'
- 19: 2, # 'г'
- 13: 3, # 'д'
- 2: 3, # 'е'
- 24: 1, # 'ж'
- 20: 3, # 'з'
- 4: 3, # 'и'
- 23: 0, # 'й'
- 11: 3, # 'к'
- 8: 3, # 'л'
- 12: 2, # 'м'
- 5: 3, # 'н'
- 1: 3, # 'о'
- 15: 3, # 'п'
- 9: 3, # 'р'
- 7: 3, # 'с'
- 6: 3, # 'т'
- 14: 3, # 'у'
- 39: 1, # 'ф'
- 26: 2, # 'х'
- 28: 2, # 'ц'
- 22: 2, # 'ч'
- 25: 3, # 'ш'
- 29: 2, # 'щ'
- 54: 2, # 'ъ'
- 18: 3, # 'ы'
- 17: 3, # 'ь'
- 30: 1, # 'э'
- 27: 1, # 'ю'
- 16: 3, # 'я'
- },
- 19: { # 'г'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 3, # 'а'
- 21: 1, # 'б'
- 10: 2, # 'в'
- 19: 1, # 'г'
- 13: 3, # 'д'
- 2: 3, # 'е'
- 24: 0, # 'ж'
- 20: 1, # 'з'
- 4: 3, # 'и'
- 23: 0, # 'й'
- 11: 2, # 'к'
- 8: 3, # 'л'
- 12: 2, # 'м'
- 5: 3, # 'н'
- 1: 3, # 'о'
- 15: 0, # 'п'
- 9: 3, # 'р'
- 7: 2, # 'с'
- 6: 2, # 'т'
- 14: 3, # 'у'
- 39: 1, # 'ф'
- 26: 1, # 'х'
- 28: 1, # 'ц'
- 22: 2, # 'ч'
- 25: 1, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 1, # 'ы'
- 17: 1, # 'ь'
- 30: 1, # 'э'
- 27: 1, # 'ю'
- 16: 0, # 'я'
- },
- 13: { # 'д'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 3, # 'а'
- 21: 2, # 'б'
- 10: 3, # 'в'
- 19: 2, # 'г'
- 13: 2, # 'д'
- 2: 3, # 'е'
- 24: 2, # 'ж'
- 20: 2, # 'з'
- 4: 3, # 'и'
- 23: 0, # 'й'
- 11: 3, # 'к'
- 8: 3, # 'л'
- 12: 2, # 'м'
- 5: 3, # 'н'
- 1: 3, # 'о'
- 15: 2, # 'п'
- 9: 3, # 'р'
- 7: 3, # 'с'
- 6: 3, # 'т'
- 14: 3, # 'у'
- 39: 1, # 'ф'
- 26: 2, # 'х'
- 28: 3, # 'ц'
- 22: 2, # 'ч'
- 25: 2, # 'ш'
- 29: 1, # 'щ'
- 54: 2, # 'ъ'
- 18: 3, # 'ы'
- 17: 3, # 'ь'
- 30: 1, # 'э'
- 27: 2, # 'ю'
- 16: 3, # 'я'
- },
- 2: { # 'е'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 2, # 'а'
- 21: 3, # 'б'
- 10: 3, # 'в'
- 19: 3, # 'г'
- 13: 3, # 'д'
- 2: 3, # 'е'
- 24: 3, # 'ж'
- 20: 3, # 'з'
- 4: 2, # 'и'
- 23: 3, # 'й'
- 11: 3, # 'к'
- 8: 3, # 'л'
- 12: 3, # 'м'
- 5: 3, # 'н'
- 1: 3, # 'о'
- 15: 3, # 'п'
- 9: 3, # 'р'
- 7: 3, # 'с'
- 6: 3, # 'т'
- 14: 2, # 'у'
- 39: 2, # 'ф'
- 26: 3, # 'х'
- 28: 3, # 'ц'
- 22: 3, # 'ч'
- 25: 3, # 'ш'
- 29: 3, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 1, # 'э'
- 27: 2, # 'ю'
- 16: 3, # 'я'
- },
- 24: { # 'ж'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 3, # 'а'
- 21: 2, # 'б'
- 10: 1, # 'в'
- 19: 2, # 'г'
- 13: 3, # 'д'
- 2: 3, # 'е'
- 24: 2, # 'ж'
- 20: 1, # 'з'
- 4: 3, # 'и'
- 23: 0, # 'й'
- 11: 2, # 'к'
- 8: 2, # 'л'
- 12: 1, # 'м'
- 5: 3, # 'н'
- 1: 2, # 'о'
- 15: 1, # 'п'
- 9: 2, # 'р'
- 7: 2, # 'с'
- 6: 1, # 'т'
- 14: 3, # 'у'
- 39: 1, # 'ф'
- 26: 0, # 'х'
- 28: 1, # 'ц'
- 22: 2, # 'ч'
- 25: 0, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 1, # 'ы'
- 17: 2, # 'ь'
- 30: 1, # 'э'
- 27: 1, # 'ю'
- 16: 1, # 'я'
- },
- 20: { # 'з'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 3, # 'а'
- 21: 3, # 'б'
- 10: 3, # 'в'
- 19: 3, # 'г'
- 13: 3, # 'д'
- 2: 3, # 'е'
- 24: 2, # 'ж'
- 20: 2, # 'з'
- 4: 3, # 'и'
- 23: 0, # 'й'
- 11: 3, # 'к'
- 8: 3, # 'л'
- 12: 3, # 'м'
- 5: 3, # 'н'
- 1: 3, # 'о'
- 15: 0, # 'п'
- 9: 3, # 'р'
- 7: 2, # 'с'
- 6: 2, # 'т'
- 14: 3, # 'у'
- 39: 0, # 'ф'
- 26: 0, # 'х'
- 28: 1, # 'ц'
- 22: 2, # 'ч'
- 25: 1, # 'ш'
- 29: 0, # 'щ'
- 54: 2, # 'ъ'
- 18: 3, # 'ы'
- 17: 2, # 'ь'
- 30: 1, # 'э'
- 27: 1, # 'ю'
- 16: 3, # 'я'
- },
- 4: { # 'и'
- 37: 1, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 1, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 3, # 'а'
- 21: 3, # 'б'
- 10: 3, # 'в'
- 19: 3, # 'г'
- 13: 3, # 'д'
- 2: 3, # 'е'
- 24: 3, # 'ж'
- 20: 3, # 'з'
- 4: 3, # 'и'
- 23: 3, # 'й'
- 11: 3, # 'к'
- 8: 3, # 'л'
- 12: 3, # 'м'
- 5: 3, # 'н'
- 1: 3, # 'о'
- 15: 3, # 'п'
- 9: 3, # 'р'
- 7: 3, # 'с'
- 6: 3, # 'т'
- 14: 2, # 'у'
- 39: 2, # 'ф'
- 26: 3, # 'х'
- 28: 3, # 'ц'
- 22: 3, # 'ч'
- 25: 3, # 'ш'
- 29: 3, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 2, # 'э'
- 27: 3, # 'ю'
- 16: 3, # 'я'
- },
- 23: { # 'й'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 1, # 'а'
- 21: 1, # 'б'
- 10: 1, # 'в'
- 19: 2, # 'г'
- 13: 3, # 'д'
- 2: 2, # 'е'
- 24: 0, # 'ж'
- 20: 2, # 'з'
- 4: 1, # 'и'
- 23: 0, # 'й'
- 11: 2, # 'к'
- 8: 2, # 'л'
- 12: 2, # 'м'
- 5: 3, # 'н'
- 1: 2, # 'о'
- 15: 1, # 'п'
- 9: 2, # 'р'
- 7: 3, # 'с'
- 6: 3, # 'т'
- 14: 1, # 'у'
- 39: 2, # 'ф'
- 26: 1, # 'х'
- 28: 2, # 'ц'
- 22: 3, # 'ч'
- 25: 2, # 'ш'
- 29: 1, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 1, # 'э'
- 27: 1, # 'ю'
- 16: 2, # 'я'
- },
- 11: { # 'к'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 3, # 'а'
- 21: 1, # 'б'
- 10: 3, # 'в'
- 19: 1, # 'г'
- 13: 1, # 'д'
- 2: 3, # 'е'
- 24: 2, # 'ж'
- 20: 2, # 'з'
- 4: 3, # 'и'
- 23: 0, # 'й'
- 11: 2, # 'к'
- 8: 3, # 'л'
- 12: 1, # 'м'
- 5: 3, # 'н'
- 1: 3, # 'о'
- 15: 0, # 'п'
- 9: 3, # 'р'
- 7: 3, # 'с'
- 6: 3, # 'т'
- 14: 3, # 'у'
- 39: 1, # 'ф'
- 26: 2, # 'х'
- 28: 2, # 'ц'
- 22: 1, # 'ч'
- 25: 2, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 1, # 'ы'
- 17: 1, # 'ь'
- 30: 1, # 'э'
- 27: 1, # 'ю'
- 16: 1, # 'я'
- },
- 8: { # 'л'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 3, # 'а'
- 21: 2, # 'б'
- 10: 2, # 'в'
- 19: 3, # 'г'
- 13: 2, # 'д'
- 2: 3, # 'е'
- 24: 3, # 'ж'
- 20: 2, # 'з'
- 4: 3, # 'и'
- 23: 0, # 'й'
- 11: 3, # 'к'
- 8: 3, # 'л'
- 12: 2, # 'м'
- 5: 3, # 'н'
- 1: 3, # 'о'
- 15: 2, # 'п'
- 9: 1, # 'р'
- 7: 3, # 'с'
- 6: 2, # 'т'
- 14: 3, # 'у'
- 39: 2, # 'ф'
- 26: 2, # 'х'
- 28: 1, # 'ц'
- 22: 3, # 'ч'
- 25: 2, # 'ш'
- 29: 1, # 'щ'
- 54: 0, # 'ъ'
- 18: 3, # 'ы'
- 17: 3, # 'ь'
- 30: 1, # 'э'
- 27: 3, # 'ю'
- 16: 3, # 'я'
- },
- 12: { # 'м'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 3, # 'а'
- 21: 2, # 'б'
- 10: 2, # 'в'
- 19: 2, # 'г'
- 13: 1, # 'д'
- 2: 3, # 'е'
- 24: 1, # 'ж'
- 20: 1, # 'з'
- 4: 3, # 'и'
- 23: 0, # 'й'
- 11: 2, # 'к'
- 8: 3, # 'л'
- 12: 2, # 'м'
- 5: 3, # 'н'
- 1: 3, # 'о'
- 15: 2, # 'п'
- 9: 2, # 'р'
- 7: 3, # 'с'
- 6: 2, # 'т'
- 14: 3, # 'у'
- 39: 2, # 'ф'
- 26: 2, # 'х'
- 28: 2, # 'ц'
- 22: 2, # 'ч'
- 25: 1, # 'ш'
- 29: 1, # 'щ'
- 54: 0, # 'ъ'
- 18: 3, # 'ы'
- 17: 2, # 'ь'
- 30: 2, # 'э'
- 27: 1, # 'ю'
- 16: 3, # 'я'
- },
- 5: { # 'н'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 3, # 'а'
- 21: 2, # 'б'
- 10: 2, # 'в'
- 19: 3, # 'г'
- 13: 3, # 'д'
- 2: 3, # 'е'
- 24: 2, # 'ж'
- 20: 2, # 'з'
- 4: 3, # 'и'
- 23: 0, # 'й'
- 11: 3, # 'к'
- 8: 2, # 'л'
- 12: 1, # 'м'
- 5: 3, # 'н'
- 1: 3, # 'о'
- 15: 1, # 'п'
- 9: 2, # 'р'
- 7: 3, # 'с'
- 6: 3, # 'т'
- 14: 3, # 'у'
- 39: 2, # 'ф'
- 26: 2, # 'х'
- 28: 3, # 'ц'
- 22: 3, # 'ч'
- 25: 2, # 'ш'
- 29: 2, # 'щ'
- 54: 1, # 'ъ'
- 18: 3, # 'ы'
- 17: 3, # 'ь'
- 30: 1, # 'э'
- 27: 3, # 'ю'
- 16: 3, # 'я'
- },
- 1: { # 'о'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 2, # 'а'
- 21: 3, # 'б'
- 10: 3, # 'в'
- 19: 3, # 'г'
- 13: 3, # 'д'
- 2: 3, # 'е'
- 24: 3, # 'ж'
- 20: 3, # 'з'
- 4: 3, # 'и'
- 23: 3, # 'й'
- 11: 3, # 'к'
- 8: 3, # 'л'
- 12: 3, # 'м'
- 5: 3, # 'н'
- 1: 3, # 'о'
- 15: 3, # 'п'
- 9: 3, # 'р'
- 7: 3, # 'с'
- 6: 3, # 'т'
- 14: 2, # 'у'
- 39: 2, # 'ф'
- 26: 3, # 'х'
- 28: 2, # 'ц'
- 22: 3, # 'ч'
- 25: 3, # 'ш'
- 29: 3, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 2, # 'э'
- 27: 3, # 'ю'
- 16: 3, # 'я'
- },
- 15: { # 'п'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 3, # 'а'
- 21: 1, # 'б'
- 10: 0, # 'в'
- 19: 0, # 'г'
- 13: 0, # 'д'
- 2: 3, # 'е'
- 24: 0, # 'ж'
- 20: 0, # 'з'
- 4: 3, # 'и'
- 23: 0, # 'й'
- 11: 2, # 'к'
- 8: 3, # 'л'
- 12: 1, # 'м'
- 5: 3, # 'н'
- 1: 3, # 'о'
- 15: 2, # 'п'
- 9: 3, # 'р'
- 7: 2, # 'с'
- 6: 2, # 'т'
- 14: 3, # 'у'
- 39: 1, # 'ф'
- 26: 0, # 'х'
- 28: 2, # 'ц'
- 22: 2, # 'ч'
- 25: 1, # 'ш'
- 29: 1, # 'щ'
- 54: 0, # 'ъ'
- 18: 3, # 'ы'
- 17: 2, # 'ь'
- 30: 1, # 'э'
- 27: 1, # 'ю'
- 16: 3, # 'я'
- },
- 9: { # 'р'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 3, # 'а'
- 21: 2, # 'б'
- 10: 3, # 'в'
- 19: 3, # 'г'
- 13: 3, # 'д'
- 2: 3, # 'е'
- 24: 3, # 'ж'
- 20: 2, # 'з'
- 4: 3, # 'и'
- 23: 0, # 'й'
- 11: 3, # 'к'
- 8: 2, # 'л'
- 12: 3, # 'м'
- 5: 3, # 'н'
- 1: 3, # 'о'
- 15: 2, # 'п'
- 9: 2, # 'р'
- 7: 3, # 'с'
- 6: 3, # 'т'
- 14: 3, # 'у'
- 39: 2, # 'ф'
- 26: 3, # 'х'
- 28: 2, # 'ц'
- 22: 2, # 'ч'
- 25: 3, # 'ш'
- 29: 2, # 'щ'
- 54: 0, # 'ъ'
- 18: 3, # 'ы'
- 17: 3, # 'ь'
- 30: 2, # 'э'
- 27: 2, # 'ю'
- 16: 3, # 'я'
- },
- 7: { # 'с'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 1, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 3, # 'а'
- 21: 2, # 'б'
- 10: 3, # 'в'
- 19: 2, # 'г'
- 13: 3, # 'д'
- 2: 3, # 'е'
- 24: 2, # 'ж'
- 20: 2, # 'з'
- 4: 3, # 'и'
- 23: 0, # 'й'
- 11: 3, # 'к'
- 8: 3, # 'л'
- 12: 3, # 'м'
- 5: 3, # 'н'
- 1: 3, # 'о'
- 15: 3, # 'п'
- 9: 3, # 'р'
- 7: 3, # 'с'
- 6: 3, # 'т'
- 14: 3, # 'у'
- 39: 2, # 'ф'
- 26: 3, # 'х'
- 28: 2, # 'ц'
- 22: 3, # 'ч'
- 25: 2, # 'ш'
- 29: 1, # 'щ'
- 54: 2, # 'ъ'
- 18: 3, # 'ы'
- 17: 3, # 'ь'
- 30: 2, # 'э'
- 27: 3, # 'ю'
- 16: 3, # 'я'
- },
- 6: { # 'т'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 3, # 'а'
- 21: 2, # 'б'
- 10: 3, # 'в'
- 19: 2, # 'г'
- 13: 2, # 'д'
- 2: 3, # 'е'
- 24: 1, # 'ж'
- 20: 1, # 'з'
- 4: 3, # 'и'
- 23: 0, # 'й'
- 11: 3, # 'к'
- 8: 3, # 'л'
- 12: 2, # 'м'
- 5: 3, # 'н'
- 1: 3, # 'о'
- 15: 2, # 'п'
- 9: 3, # 'р'
- 7: 3, # 'с'
- 6: 2, # 'т'
- 14: 3, # 'у'
- 39: 2, # 'ф'
- 26: 2, # 'х'
- 28: 2, # 'ц'
- 22: 2, # 'ч'
- 25: 2, # 'ш'
- 29: 2, # 'щ'
- 54: 2, # 'ъ'
- 18: 3, # 'ы'
- 17: 3, # 'ь'
- 30: 2, # 'э'
- 27: 2, # 'ю'
- 16: 3, # 'я'
- },
- 14: { # 'у'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 2, # 'а'
- 21: 3, # 'б'
- 10: 3, # 'в'
- 19: 3, # 'г'
- 13: 3, # 'д'
- 2: 3, # 'е'
- 24: 3, # 'ж'
- 20: 3, # 'з'
- 4: 2, # 'и'
- 23: 2, # 'й'
- 11: 3, # 'к'
- 8: 3, # 'л'
- 12: 3, # 'м'
- 5: 3, # 'н'
- 1: 2, # 'о'
- 15: 3, # 'п'
- 9: 3, # 'р'
- 7: 3, # 'с'
- 6: 3, # 'т'
- 14: 1, # 'у'
- 39: 2, # 'ф'
- 26: 3, # 'х'
- 28: 2, # 'ц'
- 22: 3, # 'ч'
- 25: 3, # 'ш'
- 29: 3, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 2, # 'э'
- 27: 3, # 'ю'
- 16: 2, # 'я'
- },
- 39: { # 'ф'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 3, # 'а'
- 21: 1, # 'б'
- 10: 0, # 'в'
- 19: 1, # 'г'
- 13: 0, # 'д'
- 2: 3, # 'е'
- 24: 0, # 'ж'
- 20: 0, # 'з'
- 4: 3, # 'и'
- 23: 0, # 'й'
- 11: 1, # 'к'
- 8: 2, # 'л'
- 12: 1, # 'м'
- 5: 1, # 'н'
- 1: 3, # 'о'
- 15: 1, # 'п'
- 9: 2, # 'р'
- 7: 2, # 'с'
- 6: 2, # 'т'
- 14: 2, # 'у'
- 39: 2, # 'ф'
- 26: 0, # 'х'
- 28: 0, # 'ц'
- 22: 1, # 'ч'
- 25: 1, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 2, # 'ы'
- 17: 1, # 'ь'
- 30: 2, # 'э'
- 27: 1, # 'ю'
- 16: 1, # 'я'
- },
- 26: { # 'х'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 3, # 'а'
- 21: 0, # 'б'
- 10: 3, # 'в'
- 19: 1, # 'г'
- 13: 1, # 'д'
- 2: 2, # 'е'
- 24: 0, # 'ж'
- 20: 1, # 'з'
- 4: 3, # 'и'
- 23: 0, # 'й'
- 11: 1, # 'к'
- 8: 2, # 'л'
- 12: 2, # 'м'
- 5: 3, # 'н'
- 1: 3, # 'о'
- 15: 1, # 'п'
- 9: 3, # 'р'
- 7: 2, # 'с'
- 6: 2, # 'т'
- 14: 2, # 'у'
- 39: 1, # 'ф'
- 26: 1, # 'х'
- 28: 1, # 'ц'
- 22: 1, # 'ч'
- 25: 2, # 'ш'
- 29: 0, # 'щ'
- 54: 1, # 'ъ'
- 18: 0, # 'ы'
- 17: 1, # 'ь'
- 30: 1, # 'э'
- 27: 1, # 'ю'
- 16: 0, # 'я'
- },
- 28: { # 'ц'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 3, # 'а'
- 21: 1, # 'б'
- 10: 2, # 'в'
- 19: 1, # 'г'
- 13: 1, # 'д'
- 2: 3, # 'е'
- 24: 0, # 'ж'
- 20: 1, # 'з'
- 4: 3, # 'и'
- 23: 0, # 'й'
- 11: 2, # 'к'
- 8: 1, # 'л'
- 12: 1, # 'м'
- 5: 1, # 'н'
- 1: 3, # 'о'
- 15: 0, # 'п'
- 9: 1, # 'р'
- 7: 0, # 'с'
- 6: 1, # 'т'
- 14: 3, # 'у'
- 39: 0, # 'ф'
- 26: 0, # 'х'
- 28: 1, # 'ц'
- 22: 0, # 'ч'
- 25: 1, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 3, # 'ы'
- 17: 1, # 'ь'
- 30: 0, # 'э'
- 27: 1, # 'ю'
- 16: 0, # 'я'
- },
- 22: { # 'ч'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 3, # 'а'
- 21: 1, # 'б'
- 10: 1, # 'в'
- 19: 0, # 'г'
- 13: 0, # 'д'
- 2: 3, # 'е'
- 24: 1, # 'ж'
- 20: 0, # 'з'
- 4: 3, # 'и'
- 23: 0, # 'й'
- 11: 3, # 'к'
- 8: 2, # 'л'
- 12: 1, # 'м'
- 5: 3, # 'н'
- 1: 2, # 'о'
- 15: 0, # 'п'
- 9: 2, # 'р'
- 7: 1, # 'с'
- 6: 3, # 'т'
- 14: 3, # 'у'
- 39: 1, # 'ф'
- 26: 1, # 'х'
- 28: 0, # 'ц'
- 22: 1, # 'ч'
- 25: 2, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 3, # 'ь'
- 30: 0, # 'э'
- 27: 0, # 'ю'
- 16: 0, # 'я'
- },
- 25: { # 'ш'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 3, # 'а'
- 21: 1, # 'б'
- 10: 2, # 'в'
- 19: 1, # 'г'
- 13: 0, # 'д'
- 2: 3, # 'е'
- 24: 0, # 'ж'
- 20: 0, # 'з'
- 4: 3, # 'и'
- 23: 0, # 'й'
- 11: 3, # 'к'
- 8: 3, # 'л'
- 12: 2, # 'м'
- 5: 3, # 'н'
- 1: 3, # 'о'
- 15: 2, # 'п'
- 9: 2, # 'р'
- 7: 1, # 'с'
- 6: 2, # 'т'
- 14: 3, # 'у'
- 39: 2, # 'ф'
- 26: 1, # 'х'
- 28: 1, # 'ц'
- 22: 1, # 'ч'
- 25: 1, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 3, # 'ь'
- 30: 1, # 'э'
- 27: 1, # 'ю'
- 16: 0, # 'я'
- },
- 29: { # 'щ'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 3, # 'а'
- 21: 0, # 'б'
- 10: 1, # 'в'
- 19: 0, # 'г'
- 13: 0, # 'д'
- 2: 3, # 'е'
- 24: 0, # 'ж'
- 20: 0, # 'з'
- 4: 3, # 'и'
- 23: 0, # 'й'
- 11: 0, # 'к'
- 8: 0, # 'л'
- 12: 1, # 'м'
- 5: 2, # 'н'
- 1: 1, # 'о'
- 15: 0, # 'п'
- 9: 2, # 'р'
- 7: 0, # 'с'
- 6: 0, # 'т'
- 14: 2, # 'у'
- 39: 0, # 'ф'
- 26: 0, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 0, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 2, # 'ь'
- 30: 0, # 'э'
- 27: 0, # 'ю'
- 16: 0, # 'я'
- },
- 54: { # 'ъ'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 0, # 'а'
- 21: 0, # 'б'
- 10: 0, # 'в'
- 19: 0, # 'г'
- 13: 0, # 'д'
- 2: 2, # 'е'
- 24: 0, # 'ж'
- 20: 0, # 'з'
- 4: 0, # 'и'
- 23: 0, # 'й'
- 11: 0, # 'к'
- 8: 0, # 'л'
- 12: 0, # 'м'
- 5: 0, # 'н'
- 1: 0, # 'о'
- 15: 0, # 'п'
- 9: 0, # 'р'
- 7: 0, # 'с'
- 6: 0, # 'т'
- 14: 0, # 'у'
- 39: 0, # 'ф'
- 26: 0, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 0, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 0, # 'э'
- 27: 1, # 'ю'
- 16: 2, # 'я'
- },
- 18: { # 'ы'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 0, # 'а'
- 21: 3, # 'б'
- 10: 3, # 'в'
- 19: 2, # 'г'
- 13: 2, # 'д'
- 2: 3, # 'е'
- 24: 2, # 'ж'
- 20: 2, # 'з'
- 4: 2, # 'и'
- 23: 3, # 'й'
- 11: 3, # 'к'
- 8: 3, # 'л'
- 12: 3, # 'м'
- 5: 3, # 'н'
- 1: 1, # 'о'
- 15: 3, # 'п'
- 9: 3, # 'р'
- 7: 3, # 'с'
- 6: 3, # 'т'
- 14: 1, # 'у'
- 39: 0, # 'ф'
- 26: 3, # 'х'
- 28: 2, # 'ц'
- 22: 3, # 'ч'
- 25: 3, # 'ш'
- 29: 2, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 0, # 'э'
- 27: 0, # 'ю'
- 16: 2, # 'я'
- },
- 17: { # 'ь'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 0, # 'а'
- 21: 2, # 'б'
- 10: 2, # 'в'
- 19: 2, # 'г'
- 13: 2, # 'д'
- 2: 3, # 'е'
- 24: 1, # 'ж'
- 20: 3, # 'з'
- 4: 2, # 'и'
- 23: 0, # 'й'
- 11: 3, # 'к'
- 8: 0, # 'л'
- 12: 3, # 'м'
- 5: 3, # 'н'
- 1: 2, # 'о'
- 15: 2, # 'п'
- 9: 1, # 'р'
- 7: 3, # 'с'
- 6: 2, # 'т'
- 14: 0, # 'у'
- 39: 2, # 'ф'
- 26: 1, # 'х'
- 28: 2, # 'ц'
- 22: 2, # 'ч'
- 25: 3, # 'ш'
- 29: 2, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 1, # 'э'
- 27: 3, # 'ю'
- 16: 3, # 'я'
- },
- 30: { # 'э'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 1, # 'М'
- 31: 1, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 1, # 'Р'
- 32: 1, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 1, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 0, # 'а'
- 21: 1, # 'б'
- 10: 1, # 'в'
- 19: 1, # 'г'
- 13: 2, # 'д'
- 2: 1, # 'е'
- 24: 0, # 'ж'
- 20: 1, # 'з'
- 4: 0, # 'и'
- 23: 2, # 'й'
- 11: 2, # 'к'
- 8: 2, # 'л'
- 12: 2, # 'м'
- 5: 2, # 'н'
- 1: 0, # 'о'
- 15: 2, # 'п'
- 9: 2, # 'р'
- 7: 2, # 'с'
- 6: 3, # 'т'
- 14: 1, # 'у'
- 39: 2, # 'ф'
- 26: 1, # 'х'
- 28: 0, # 'ц'
- 22: 0, # 'ч'
- 25: 1, # 'ш'
- 29: 0, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 1, # 'э'
- 27: 1, # 'ю'
- 16: 1, # 'я'
- },
- 27: { # 'ю'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 2, # 'а'
- 21: 3, # 'б'
- 10: 1, # 'в'
- 19: 2, # 'г'
- 13: 3, # 'д'
- 2: 1, # 'е'
- 24: 2, # 'ж'
- 20: 2, # 'з'
- 4: 1, # 'и'
- 23: 1, # 'й'
- 11: 2, # 'к'
- 8: 2, # 'л'
- 12: 2, # 'м'
- 5: 2, # 'н'
- 1: 1, # 'о'
- 15: 2, # 'п'
- 9: 2, # 'р'
- 7: 3, # 'с'
- 6: 3, # 'т'
- 14: 0, # 'у'
- 39: 1, # 'ф'
- 26: 2, # 'х'
- 28: 2, # 'ц'
- 22: 2, # 'ч'
- 25: 2, # 'ш'
- 29: 3, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 1, # 'э'
- 27: 2, # 'ю'
- 16: 1, # 'я'
- },
- 16: { # 'я'
- 37: 0, # 'А'
- 44: 0, # 'Б'
- 33: 0, # 'В'
- 46: 0, # 'Г'
- 41: 0, # 'Д'
- 48: 0, # 'Е'
- 56: 0, # 'Ж'
- 51: 0, # 'З'
- 42: 0, # 'И'
- 60: 0, # 'Й'
- 36: 0, # 'К'
- 49: 0, # 'Л'
- 38: 0, # 'М'
- 31: 0, # 'Н'
- 34: 0, # 'О'
- 35: 0, # 'П'
- 45: 0, # 'Р'
- 32: 0, # 'С'
- 40: 0, # 'Т'
- 52: 0, # 'У'
- 53: 0, # 'Ф'
- 55: 0, # 'Х'
- 58: 0, # 'Ц'
- 50: 0, # 'Ч'
- 57: 0, # 'Ш'
- 63: 0, # 'Щ'
- 62: 0, # 'Ы'
- 61: 0, # 'Ь'
- 47: 0, # 'Э'
- 59: 0, # 'Ю'
- 43: 0, # 'Я'
- 3: 0, # 'а'
- 21: 2, # 'б'
- 10: 3, # 'в'
- 19: 2, # 'г'
- 13: 3, # 'д'
- 2: 3, # 'е'
- 24: 3, # 'ж'
- 20: 3, # 'з'
- 4: 2, # 'и'
- 23: 2, # 'й'
- 11: 3, # 'к'
- 8: 3, # 'л'
- 12: 3, # 'м'
- 5: 3, # 'н'
- 1: 0, # 'о'
- 15: 2, # 'п'
- 9: 2, # 'р'
- 7: 3, # 'с'
- 6: 3, # 'т'
- 14: 1, # 'у'
- 39: 1, # 'ф'
- 26: 3, # 'х'
- 28: 2, # 'ц'
- 22: 2, # 'ч'
- 25: 2, # 'ш'
- 29: 3, # 'щ'
- 54: 0, # 'ъ'
- 18: 0, # 'ы'
- 17: 0, # 'ь'
- 30: 0, # 'э'
- 27: 2, # 'ю'
- 16: 2, # 'я'
- },
-}
-
-# 255: Undefined characters that did not exist in training text
-# 254: Carriage/Return
-# 253: symbol (punctuation) that does not belong to word
-# 252: 0 - 9
-# 251: Control characters
-
-# Character Mapping Table(s):
-IBM866_RUSSIAN_CHAR_TO_ORDER = {
- 0: 255, # '\x00'
- 1: 255, # '\x01'
- 2: 255, # '\x02'
- 3: 255, # '\x03'
- 4: 255, # '\x04'
- 5: 255, # '\x05'
- 6: 255, # '\x06'
- 7: 255, # '\x07'
- 8: 255, # '\x08'
- 9: 255, # '\t'
- 10: 254, # '\n'
- 11: 255, # '\x0b'
- 12: 255, # '\x0c'
- 13: 254, # '\r'
- 14: 255, # '\x0e'
- 15: 255, # '\x0f'
- 16: 255, # '\x10'
- 17: 255, # '\x11'
- 18: 255, # '\x12'
- 19: 255, # '\x13'
- 20: 255, # '\x14'
- 21: 255, # '\x15'
- 22: 255, # '\x16'
- 23: 255, # '\x17'
- 24: 255, # '\x18'
- 25: 255, # '\x19'
- 26: 255, # '\x1a'
- 27: 255, # '\x1b'
- 28: 255, # '\x1c'
- 29: 255, # '\x1d'
- 30: 255, # '\x1e'
- 31: 255, # '\x1f'
- 32: 253, # ' '
- 33: 253, # '!'
- 34: 253, # '"'
- 35: 253, # '#'
- 36: 253, # '$'
- 37: 253, # '%'
- 38: 253, # '&'
- 39: 253, # "'"
- 40: 253, # '('
- 41: 253, # ')'
- 42: 253, # '*'
- 43: 253, # '+'
- 44: 253, # ','
- 45: 253, # '-'
- 46: 253, # '.'
- 47: 253, # '/'
- 48: 252, # '0'
- 49: 252, # '1'
- 50: 252, # '2'
- 51: 252, # '3'
- 52: 252, # '4'
- 53: 252, # '5'
- 54: 252, # '6'
- 55: 252, # '7'
- 56: 252, # '8'
- 57: 252, # '9'
- 58: 253, # ':'
- 59: 253, # ';'
- 60: 253, # '<'
- 61: 253, # '='
- 62: 253, # '>'
- 63: 253, # '?'
- 64: 253, # '@'
- 65: 142, # 'A'
- 66: 143, # 'B'
- 67: 144, # 'C'
- 68: 145, # 'D'
- 69: 146, # 'E'
- 70: 147, # 'F'
- 71: 148, # 'G'
- 72: 149, # 'H'
- 73: 150, # 'I'
- 74: 151, # 'J'
- 75: 152, # 'K'
- 76: 74, # 'L'
- 77: 153, # 'M'
- 78: 75, # 'N'
- 79: 154, # 'O'
- 80: 155, # 'P'
- 81: 156, # 'Q'
- 82: 157, # 'R'
- 83: 158, # 'S'
- 84: 159, # 'T'
- 85: 160, # 'U'
- 86: 161, # 'V'
- 87: 162, # 'W'
- 88: 163, # 'X'
- 89: 164, # 'Y'
- 90: 165, # 'Z'
- 91: 253, # '['
- 92: 253, # '\\'
- 93: 253, # ']'
- 94: 253, # '^'
- 95: 253, # '_'
- 96: 253, # '`'
- 97: 71, # 'a'
- 98: 172, # 'b'
- 99: 66, # 'c'
- 100: 173, # 'd'
- 101: 65, # 'e'
- 102: 174, # 'f'
- 103: 76, # 'g'
- 104: 175, # 'h'
- 105: 64, # 'i'
- 106: 176, # 'j'
- 107: 177, # 'k'
- 108: 77, # 'l'
- 109: 72, # 'm'
- 110: 178, # 'n'
- 111: 69, # 'o'
- 112: 67, # 'p'
- 113: 179, # 'q'
- 114: 78, # 'r'
- 115: 73, # 's'
- 116: 180, # 't'
- 117: 181, # 'u'
- 118: 79, # 'v'
- 119: 182, # 'w'
- 120: 183, # 'x'
- 121: 184, # 'y'
- 122: 185, # 'z'
- 123: 253, # '{'
- 124: 253, # '|'
- 125: 253, # '}'
- 126: 253, # '~'
- 127: 253, # '\x7f'
- 128: 37, # 'А'
- 129: 44, # 'Б'
- 130: 33, # 'В'
- 131: 46, # 'Г'
- 132: 41, # 'Д'
- 133: 48, # 'Е'
- 134: 56, # 'Ж'
- 135: 51, # 'З'
- 136: 42, # 'И'
- 137: 60, # 'Й'
- 138: 36, # 'К'
- 139: 49, # 'Л'
- 140: 38, # 'М'
- 141: 31, # 'Н'
- 142: 34, # 'О'
- 143: 35, # 'П'
- 144: 45, # 'Р'
- 145: 32, # 'С'
- 146: 40, # 'Т'
- 147: 52, # 'У'
- 148: 53, # 'Ф'
- 149: 55, # 'Х'
- 150: 58, # 'Ц'
- 151: 50, # 'Ч'
- 152: 57, # 'Ш'
- 153: 63, # 'Щ'
- 154: 70, # 'Ъ'
- 155: 62, # 'Ы'
- 156: 61, # 'Ь'
- 157: 47, # 'Э'
- 158: 59, # 'Ю'
- 159: 43, # 'Я'
- 160: 3, # 'а'
- 161: 21, # 'б'
- 162: 10, # 'в'
- 163: 19, # 'г'
- 164: 13, # 'д'
- 165: 2, # 'е'
- 166: 24, # 'ж'
- 167: 20, # 'з'
- 168: 4, # 'и'
- 169: 23, # 'й'
- 170: 11, # 'к'
- 171: 8, # 'л'
- 172: 12, # 'м'
- 173: 5, # 'н'
- 174: 1, # 'о'
- 175: 15, # 'п'
- 176: 191, # '░'
- 177: 192, # '▒'
- 178: 193, # '▓'
- 179: 194, # '│'
- 180: 195, # '┤'
- 181: 196, # '╡'
- 182: 197, # '╢'
- 183: 198, # '╖'
- 184: 199, # '╕'
- 185: 200, # '╣'
- 186: 201, # '║'
- 187: 202, # '╗'
- 188: 203, # '╝'
- 189: 204, # '╜'
- 190: 205, # '╛'
- 191: 206, # '┐'
- 192: 207, # '└'
- 193: 208, # '┴'
- 194: 209, # '┬'
- 195: 210, # '├'
- 196: 211, # '─'
- 197: 212, # '┼'
- 198: 213, # '╞'
- 199: 214, # '╟'
- 200: 215, # '╚'
- 201: 216, # '╔'
- 202: 217, # '╩'
- 203: 218, # '╦'
- 204: 219, # '╠'
- 205: 220, # '═'
- 206: 221, # '╬'
- 207: 222, # '╧'
- 208: 223, # '╨'
- 209: 224, # '╤'
- 210: 225, # '╥'
- 211: 226, # '╙'
- 212: 227, # '╘'
- 213: 228, # '╒'
- 214: 229, # '╓'
- 215: 230, # '╫'
- 216: 231, # '╪'
- 217: 232, # '┘'
- 218: 233, # '┌'
- 219: 234, # '█'
- 220: 235, # '▄'
- 221: 236, # '▌'
- 222: 237, # '▐'
- 223: 238, # '▀'
- 224: 9, # 'р'
- 225: 7, # 'с'
- 226: 6, # 'т'
- 227: 14, # 'у'
- 228: 39, # 'ф'
- 229: 26, # 'х'
- 230: 28, # 'ц'
- 231: 22, # 'ч'
- 232: 25, # 'ш'
- 233: 29, # 'щ'
- 234: 54, # 'ъ'
- 235: 18, # 'ы'
- 236: 17, # 'ь'
- 237: 30, # 'э'
- 238: 27, # 'ю'
- 239: 16, # 'я'
- 240: 239, # 'Ё'
- 241: 68, # 'ё'
- 242: 240, # 'Є'
- 243: 241, # 'є'
- 244: 242, # 'Ї'
- 245: 243, # 'ї'
- 246: 244, # 'Ў'
- 247: 245, # 'ў'
- 248: 246, # '°'
- 249: 247, # '∙'
- 250: 248, # '·'
- 251: 249, # '√'
- 252: 250, # '№'
- 253: 251, # '¤'
- 254: 252, # '■'
- 255: 255, # '\xa0'
-}
-
-IBM866_RUSSIAN_MODEL = SingleByteCharSetModel(
- charset_name="IBM866",
- language="Russian",
- char_to_order_map=IBM866_RUSSIAN_CHAR_TO_ORDER,
- language_model=RUSSIAN_LANG_MODEL,
- typical_positive_ratio=0.976601,
- keep_ascii_letters=False,
- alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё",
-)
-
-WINDOWS_1251_RUSSIAN_CHAR_TO_ORDER = {
- 0: 255, # '\x00'
- 1: 255, # '\x01'
- 2: 255, # '\x02'
- 3: 255, # '\x03'
- 4: 255, # '\x04'
- 5: 255, # '\x05'
- 6: 255, # '\x06'
- 7: 255, # '\x07'
- 8: 255, # '\x08'
- 9: 255, # '\t'
- 10: 254, # '\n'
- 11: 255, # '\x0b'
- 12: 255, # '\x0c'
- 13: 254, # '\r'
- 14: 255, # '\x0e'
- 15: 255, # '\x0f'
- 16: 255, # '\x10'
- 17: 255, # '\x11'
- 18: 255, # '\x12'
- 19: 255, # '\x13'
- 20: 255, # '\x14'
- 21: 255, # '\x15'
- 22: 255, # '\x16'
- 23: 255, # '\x17'
- 24: 255, # '\x18'
- 25: 255, # '\x19'
- 26: 255, # '\x1a'
- 27: 255, # '\x1b'
- 28: 255, # '\x1c'
- 29: 255, # '\x1d'
- 30: 255, # '\x1e'
- 31: 255, # '\x1f'
- 32: 253, # ' '
- 33: 253, # '!'
- 34: 253, # '"'
- 35: 253, # '#'
- 36: 253, # '$'
- 37: 253, # '%'
- 38: 253, # '&'
- 39: 253, # "'"
- 40: 253, # '('
- 41: 253, # ')'
- 42: 253, # '*'
- 43: 253, # '+'
- 44: 253, # ','
- 45: 253, # '-'
- 46: 253, # '.'
- 47: 253, # '/'
- 48: 252, # '0'
- 49: 252, # '1'
- 50: 252, # '2'
- 51: 252, # '3'
- 52: 252, # '4'
- 53: 252, # '5'
- 54: 252, # '6'
- 55: 252, # '7'
- 56: 252, # '8'
- 57: 252, # '9'
- 58: 253, # ':'
- 59: 253, # ';'
- 60: 253, # '<'
- 61: 253, # '='
- 62: 253, # '>'
- 63: 253, # '?'
- 64: 253, # '@'
- 65: 142, # 'A'
- 66: 143, # 'B'
- 67: 144, # 'C'
- 68: 145, # 'D'
- 69: 146, # 'E'
- 70: 147, # 'F'
- 71: 148, # 'G'
- 72: 149, # 'H'
- 73: 150, # 'I'
- 74: 151, # 'J'
- 75: 152, # 'K'
- 76: 74, # 'L'
- 77: 153, # 'M'
- 78: 75, # 'N'
- 79: 154, # 'O'
- 80: 155, # 'P'
- 81: 156, # 'Q'
- 82: 157, # 'R'
- 83: 158, # 'S'
- 84: 159, # 'T'
- 85: 160, # 'U'
- 86: 161, # 'V'
- 87: 162, # 'W'
- 88: 163, # 'X'
- 89: 164, # 'Y'
- 90: 165, # 'Z'
- 91: 253, # '['
- 92: 253, # '\\'
- 93: 253, # ']'
- 94: 253, # '^'
- 95: 253, # '_'
- 96: 253, # '`'
- 97: 71, # 'a'
- 98: 172, # 'b'
- 99: 66, # 'c'
- 100: 173, # 'd'
- 101: 65, # 'e'
- 102: 174, # 'f'
- 103: 76, # 'g'
- 104: 175, # 'h'
- 105: 64, # 'i'
- 106: 176, # 'j'
- 107: 177, # 'k'
- 108: 77, # 'l'
- 109: 72, # 'm'
- 110: 178, # 'n'
- 111: 69, # 'o'
- 112: 67, # 'p'
- 113: 179, # 'q'
- 114: 78, # 'r'
- 115: 73, # 's'
- 116: 180, # 't'
- 117: 181, # 'u'
- 118: 79, # 'v'
- 119: 182, # 'w'
- 120: 183, # 'x'
- 121: 184, # 'y'
- 122: 185, # 'z'
- 123: 253, # '{'
- 124: 253, # '|'
- 125: 253, # '}'
- 126: 253, # '~'
- 127: 253, # '\x7f'
- 128: 191, # 'Ђ'
- 129: 192, # 'Ѓ'
- 130: 193, # '‚'
- 131: 194, # 'ѓ'
- 132: 195, # '„'
- 133: 196, # '…'
- 134: 197, # '†'
- 135: 198, # '‡'
- 136: 199, # '€'
- 137: 200, # '‰'
- 138: 201, # 'Љ'
- 139: 202, # '‹'
- 140: 203, # 'Њ'
- 141: 204, # 'Ќ'
- 142: 205, # 'Ћ'
- 143: 206, # 'Џ'
- 144: 207, # 'ђ'
- 145: 208, # '‘'
- 146: 209, # '’'
- 147: 210, # '“'
- 148: 211, # '”'
- 149: 212, # '•'
- 150: 213, # '–'
- 151: 214, # '—'
- 152: 215, # None
- 153: 216, # '™'
- 154: 217, # 'љ'
- 155: 218, # '›'
- 156: 219, # 'њ'
- 157: 220, # 'ќ'
- 158: 221, # 'ћ'
- 159: 222, # 'џ'
- 160: 223, # '\xa0'
- 161: 224, # 'Ў'
- 162: 225, # 'ў'
- 163: 226, # 'Ј'
- 164: 227, # '¤'
- 165: 228, # 'Ґ'
- 166: 229, # '¦'
- 167: 230, # '§'
- 168: 231, # 'Ё'
- 169: 232, # '©'
- 170: 233, # 'Є'
- 171: 234, # '«'
- 172: 235, # '¬'
- 173: 236, # '\xad'
- 174: 237, # '®'
- 175: 238, # 'Ї'
- 176: 239, # '°'
- 177: 240, # '±'
- 178: 241, # 'І'
- 179: 242, # 'і'
- 180: 243, # 'ґ'
- 181: 244, # 'µ'
- 182: 245, # '¶'
- 183: 246, # '·'
- 184: 68, # 'ё'
- 185: 247, # '№'
- 186: 248, # 'є'
- 187: 249, # '»'
- 188: 250, # 'ј'
- 189: 251, # 'Ѕ'
- 190: 252, # 'ѕ'
- 191: 253, # 'ї'
- 192: 37, # 'А'
- 193: 44, # 'Б'
- 194: 33, # 'В'
- 195: 46, # 'Г'
- 196: 41, # 'Д'
- 197: 48, # 'Е'
- 198: 56, # 'Ж'
- 199: 51, # 'З'
- 200: 42, # 'И'
- 201: 60, # 'Й'
- 202: 36, # 'К'
- 203: 49, # 'Л'
- 204: 38, # 'М'
- 205: 31, # 'Н'
- 206: 34, # 'О'
- 207: 35, # 'П'
- 208: 45, # 'Р'
- 209: 32, # 'С'
- 210: 40, # 'Т'
- 211: 52, # 'У'
- 212: 53, # 'Ф'
- 213: 55, # 'Х'
- 214: 58, # 'Ц'
- 215: 50, # 'Ч'
- 216: 57, # 'Ш'
- 217: 63, # 'Щ'
- 218: 70, # 'Ъ'
- 219: 62, # 'Ы'
- 220: 61, # 'Ь'
- 221: 47, # 'Э'
- 222: 59, # 'Ю'
- 223: 43, # 'Я'
- 224: 3, # 'а'
- 225: 21, # 'б'
- 226: 10, # 'в'
- 227: 19, # 'г'
- 228: 13, # 'д'
- 229: 2, # 'е'
- 230: 24, # 'ж'
- 231: 20, # 'з'
- 232: 4, # 'и'
- 233: 23, # 'й'
- 234: 11, # 'к'
- 235: 8, # 'л'
- 236: 12, # 'м'
- 237: 5, # 'н'
- 238: 1, # 'о'
- 239: 15, # 'п'
- 240: 9, # 'р'
- 241: 7, # 'с'
- 242: 6, # 'т'
- 243: 14, # 'у'
- 244: 39, # 'ф'
- 245: 26, # 'х'
- 246: 28, # 'ц'
- 247: 22, # 'ч'
- 248: 25, # 'ш'
- 249: 29, # 'щ'
- 250: 54, # 'ъ'
- 251: 18, # 'ы'
- 252: 17, # 'ь'
- 253: 30, # 'э'
- 254: 27, # 'ю'
- 255: 16, # 'я'
-}
-
-WINDOWS_1251_RUSSIAN_MODEL = SingleByteCharSetModel(
- charset_name="windows-1251",
- language="Russian",
- char_to_order_map=WINDOWS_1251_RUSSIAN_CHAR_TO_ORDER,
- language_model=RUSSIAN_LANG_MODEL,
- typical_positive_ratio=0.976601,
- keep_ascii_letters=False,
- alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё",
-)
-
-IBM855_RUSSIAN_CHAR_TO_ORDER = {
- 0: 255, # '\x00'
- 1: 255, # '\x01'
- 2: 255, # '\x02'
- 3: 255, # '\x03'
- 4: 255, # '\x04'
- 5: 255, # '\x05'
- 6: 255, # '\x06'
- 7: 255, # '\x07'
- 8: 255, # '\x08'
- 9: 255, # '\t'
- 10: 254, # '\n'
- 11: 255, # '\x0b'
- 12: 255, # '\x0c'
- 13: 254, # '\r'
- 14: 255, # '\x0e'
- 15: 255, # '\x0f'
- 16: 255, # '\x10'
- 17: 255, # '\x11'
- 18: 255, # '\x12'
- 19: 255, # '\x13'
- 20: 255, # '\x14'
- 21: 255, # '\x15'
- 22: 255, # '\x16'
- 23: 255, # '\x17'
- 24: 255, # '\x18'
- 25: 255, # '\x19'
- 26: 255, # '\x1a'
- 27: 255, # '\x1b'
- 28: 255, # '\x1c'
- 29: 255, # '\x1d'
- 30: 255, # '\x1e'
- 31: 255, # '\x1f'
- 32: 253, # ' '
- 33: 253, # '!'
- 34: 253, # '"'
- 35: 253, # '#'
- 36: 253, # '$'
- 37: 253, # '%'
- 38: 253, # '&'
- 39: 253, # "'"
- 40: 253, # '('
- 41: 253, # ')'
- 42: 253, # '*'
- 43: 253, # '+'
- 44: 253, # ','
- 45: 253, # '-'
- 46: 253, # '.'
- 47: 253, # '/'
- 48: 252, # '0'
- 49: 252, # '1'
- 50: 252, # '2'
- 51: 252, # '3'
- 52: 252, # '4'
- 53: 252, # '5'
- 54: 252, # '6'
- 55: 252, # '7'
- 56: 252, # '8'
- 57: 252, # '9'
- 58: 253, # ':'
- 59: 253, # ';'
- 60: 253, # '<'
- 61: 253, # '='
- 62: 253, # '>'
- 63: 253, # '?'
- 64: 253, # '@'
- 65: 142, # 'A'
- 66: 143, # 'B'
- 67: 144, # 'C'
- 68: 145, # 'D'
- 69: 146, # 'E'
- 70: 147, # 'F'
- 71: 148, # 'G'
- 72: 149, # 'H'
- 73: 150, # 'I'
- 74: 151, # 'J'
- 75: 152, # 'K'
- 76: 74, # 'L'
- 77: 153, # 'M'
- 78: 75, # 'N'
- 79: 154, # 'O'
- 80: 155, # 'P'
- 81: 156, # 'Q'
- 82: 157, # 'R'
- 83: 158, # 'S'
- 84: 159, # 'T'
- 85: 160, # 'U'
- 86: 161, # 'V'
- 87: 162, # 'W'
- 88: 163, # 'X'
- 89: 164, # 'Y'
- 90: 165, # 'Z'
- 91: 253, # '['
- 92: 253, # '\\'
- 93: 253, # ']'
- 94: 253, # '^'
- 95: 253, # '_'
- 96: 253, # '`'
- 97: 71, # 'a'
- 98: 172, # 'b'
- 99: 66, # 'c'
- 100: 173, # 'd'
- 101: 65, # 'e'
- 102: 174, # 'f'
- 103: 76, # 'g'
- 104: 175, # 'h'
- 105: 64, # 'i'
- 106: 176, # 'j'
- 107: 177, # 'k'
- 108: 77, # 'l'
- 109: 72, # 'm'
- 110: 178, # 'n'
- 111: 69, # 'o'
- 112: 67, # 'p'
- 113: 179, # 'q'
- 114: 78, # 'r'
- 115: 73, # 's'
- 116: 180, # 't'
- 117: 181, # 'u'
- 118: 79, # 'v'
- 119: 182, # 'w'
- 120: 183, # 'x'
- 121: 184, # 'y'
- 122: 185, # 'z'
- 123: 253, # '{'
- 124: 253, # '|'
- 125: 253, # '}'
- 126: 253, # '~'
- 127: 253, # '\x7f'
- 128: 191, # 'ђ'
- 129: 192, # 'Ђ'
- 130: 193, # 'ѓ'
- 131: 194, # 'Ѓ'
- 132: 68, # 'ё'
- 133: 195, # 'Ё'
- 134: 196, # 'є'
- 135: 197, # 'Є'
- 136: 198, # 'ѕ'
- 137: 199, # 'Ѕ'
- 138: 200, # 'і'
- 139: 201, # 'І'
- 140: 202, # 'ї'
- 141: 203, # 'Ї'
- 142: 204, # 'ј'
- 143: 205, # 'Ј'
- 144: 206, # 'љ'
- 145: 207, # 'Љ'
- 146: 208, # 'њ'
- 147: 209, # 'Њ'
- 148: 210, # 'ћ'
- 149: 211, # 'Ћ'
- 150: 212, # 'ќ'
- 151: 213, # 'Ќ'
- 152: 214, # 'ў'
- 153: 215, # 'Ў'
- 154: 216, # 'џ'
- 155: 217, # 'Џ'
- 156: 27, # 'ю'
- 157: 59, # 'Ю'
- 158: 54, # 'ъ'
- 159: 70, # 'Ъ'
- 160: 3, # 'а'
- 161: 37, # 'А'
- 162: 21, # 'б'
- 163: 44, # 'Б'
- 164: 28, # 'ц'
- 165: 58, # 'Ц'
- 166: 13, # 'д'
- 167: 41, # 'Д'
- 168: 2, # 'е'
- 169: 48, # 'Е'
- 170: 39, # 'ф'
- 171: 53, # 'Ф'
- 172: 19, # 'г'
- 173: 46, # 'Г'
- 174: 218, # '«'
- 175: 219, # '»'
- 176: 220, # '░'
- 177: 221, # '▒'
- 178: 222, # '▓'
- 179: 223, # '│'
- 180: 224, # '┤'
- 181: 26, # 'х'
- 182: 55, # 'Х'
- 183: 4, # 'и'
- 184: 42, # 'И'
- 185: 225, # '╣'
- 186: 226, # '║'
- 187: 227, # '╗'
- 188: 228, # '╝'
- 189: 23, # 'й'
- 190: 60, # 'Й'
- 191: 229, # '┐'
- 192: 230, # '└'
- 193: 231, # '┴'
- 194: 232, # '┬'
- 195: 233, # '├'
- 196: 234, # '─'
- 197: 235, # '┼'
- 198: 11, # 'к'
- 199: 36, # 'К'
- 200: 236, # '╚'
- 201: 237, # '╔'
- 202: 238, # '╩'
- 203: 239, # '╦'
- 204: 240, # '╠'
- 205: 241, # '═'
- 206: 242, # '╬'
- 207: 243, # '¤'
- 208: 8, # 'л'
- 209: 49, # 'Л'
- 210: 12, # 'м'
- 211: 38, # 'М'
- 212: 5, # 'н'
- 213: 31, # 'Н'
- 214: 1, # 'о'
- 215: 34, # 'О'
- 216: 15, # 'п'
- 217: 244, # '┘'
- 218: 245, # '┌'
- 219: 246, # '█'
- 220: 247, # '▄'
- 221: 35, # 'П'
- 222: 16, # 'я'
- 223: 248, # '▀'
- 224: 43, # 'Я'
- 225: 9, # 'р'
- 226: 45, # 'Р'
- 227: 7, # 'с'
- 228: 32, # 'С'
- 229: 6, # 'т'
- 230: 40, # 'Т'
- 231: 14, # 'у'
- 232: 52, # 'У'
- 233: 24, # 'ж'
- 234: 56, # 'Ж'
- 235: 10, # 'в'
- 236: 33, # 'В'
- 237: 17, # 'ь'
- 238: 61, # 'Ь'
- 239: 249, # '№'
- 240: 250, # '\xad'
- 241: 18, # 'ы'
- 242: 62, # 'Ы'
- 243: 20, # 'з'
- 244: 51, # 'З'
- 245: 25, # 'ш'
- 246: 57, # 'Ш'
- 247: 30, # 'э'
- 248: 47, # 'Э'
- 249: 29, # 'щ'
- 250: 63, # 'Щ'
- 251: 22, # 'ч'
- 252: 50, # 'Ч'
- 253: 251, # '§'
- 254: 252, # '■'
- 255: 255, # '\xa0'
-}
-
-IBM855_RUSSIAN_MODEL = SingleByteCharSetModel(
- charset_name="IBM855",
- language="Russian",
- char_to_order_map=IBM855_RUSSIAN_CHAR_TO_ORDER,
- language_model=RUSSIAN_LANG_MODEL,
- typical_positive_ratio=0.976601,
- keep_ascii_letters=False,
- alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё",
-)
-
-KOI8_R_RUSSIAN_CHAR_TO_ORDER = {
- 0: 255, # '\x00'
- 1: 255, # '\x01'
- 2: 255, # '\x02'
- 3: 255, # '\x03'
- 4: 255, # '\x04'
- 5: 255, # '\x05'
- 6: 255, # '\x06'
- 7: 255, # '\x07'
- 8: 255, # '\x08'
- 9: 255, # '\t'
- 10: 254, # '\n'
- 11: 255, # '\x0b'
- 12: 255, # '\x0c'
- 13: 254, # '\r'
- 14: 255, # '\x0e'
- 15: 255, # '\x0f'
- 16: 255, # '\x10'
- 17: 255, # '\x11'
- 18: 255, # '\x12'
- 19: 255, # '\x13'
- 20: 255, # '\x14'
- 21: 255, # '\x15'
- 22: 255, # '\x16'
- 23: 255, # '\x17'
- 24: 255, # '\x18'
- 25: 255, # '\x19'
- 26: 255, # '\x1a'
- 27: 255, # '\x1b'
- 28: 255, # '\x1c'
- 29: 255, # '\x1d'
- 30: 255, # '\x1e'
- 31: 255, # '\x1f'
- 32: 253, # ' '
- 33: 253, # '!'
- 34: 253, # '"'
- 35: 253, # '#'
- 36: 253, # '$'
- 37: 253, # '%'
- 38: 253, # '&'
- 39: 253, # "'"
- 40: 253, # '('
- 41: 253, # ')'
- 42: 253, # '*'
- 43: 253, # '+'
- 44: 253, # ','
- 45: 253, # '-'
- 46: 253, # '.'
- 47: 253, # '/'
- 48: 252, # '0'
- 49: 252, # '1'
- 50: 252, # '2'
- 51: 252, # '3'
- 52: 252, # '4'
- 53: 252, # '5'
- 54: 252, # '6'
- 55: 252, # '7'
- 56: 252, # '8'
- 57: 252, # '9'
- 58: 253, # ':'
- 59: 253, # ';'
- 60: 253, # '<'
- 61: 253, # '='
- 62: 253, # '>'
- 63: 253, # '?'
- 64: 253, # '@'
- 65: 142, # 'A'
- 66: 143, # 'B'
- 67: 144, # 'C'
- 68: 145, # 'D'
- 69: 146, # 'E'
- 70: 147, # 'F'
- 71: 148, # 'G'
- 72: 149, # 'H'
- 73: 150, # 'I'
- 74: 151, # 'J'
- 75: 152, # 'K'
- 76: 74, # 'L'
- 77: 153, # 'M'
- 78: 75, # 'N'
- 79: 154, # 'O'
- 80: 155, # 'P'
- 81: 156, # 'Q'
- 82: 157, # 'R'
- 83: 158, # 'S'
- 84: 159, # 'T'
- 85: 160, # 'U'
- 86: 161, # 'V'
- 87: 162, # 'W'
- 88: 163, # 'X'
- 89: 164, # 'Y'
- 90: 165, # 'Z'
- 91: 253, # '['
- 92: 253, # '\\'
- 93: 253, # ']'
- 94: 253, # '^'
- 95: 253, # '_'
- 96: 253, # '`'
- 97: 71, # 'a'
- 98: 172, # 'b'
- 99: 66, # 'c'
- 100: 173, # 'd'
- 101: 65, # 'e'
- 102: 174, # 'f'
- 103: 76, # 'g'
- 104: 175, # 'h'
- 105: 64, # 'i'
- 106: 176, # 'j'
- 107: 177, # 'k'
- 108: 77, # 'l'
- 109: 72, # 'm'
- 110: 178, # 'n'
- 111: 69, # 'o'
- 112: 67, # 'p'
- 113: 179, # 'q'
- 114: 78, # 'r'
- 115: 73, # 's'
- 116: 180, # 't'
- 117: 181, # 'u'
- 118: 79, # 'v'
- 119: 182, # 'w'
- 120: 183, # 'x'
- 121: 184, # 'y'
- 122: 185, # 'z'
- 123: 253, # '{'
- 124: 253, # '|'
- 125: 253, # '}'
- 126: 253, # '~'
- 127: 253, # '\x7f'
- 128: 191, # '─'
- 129: 192, # '│'
- 130: 193, # '┌'
- 131: 194, # '┐'
- 132: 195, # '└'
- 133: 196, # '┘'
- 134: 197, # '├'
- 135: 198, # '┤'
- 136: 199, # '┬'
- 137: 200, # '┴'
- 138: 201, # '┼'
- 139: 202, # '▀'
- 140: 203, # '▄'
- 141: 204, # '█'
- 142: 205, # '▌'
- 143: 206, # '▐'
- 144: 207, # '░'
- 145: 208, # '▒'
- 146: 209, # '▓'
- 147: 210, # '⌠'
- 148: 211, # '■'
- 149: 212, # '∙'
- 150: 213, # '√'
- 151: 214, # '≈'
- 152: 215, # '≤'
- 153: 216, # '≥'
- 154: 217, # '\xa0'
- 155: 218, # '⌡'
- 156: 219, # '°'
- 157: 220, # '²'
- 158: 221, # '·'
- 159: 222, # '÷'
- 160: 223, # '═'
- 161: 224, # '║'
- 162: 225, # '╒'
- 163: 68, # 'ё'
- 164: 226, # '╓'
- 165: 227, # '╔'
- 166: 228, # '╕'
- 167: 229, # '╖'
- 168: 230, # '╗'
- 169: 231, # '╘'
- 170: 232, # '╙'
- 171: 233, # '╚'
- 172: 234, # '╛'
- 173: 235, # '╜'
- 174: 236, # '╝'
- 175: 237, # '╞'
- 176: 238, # '╟'
- 177: 239, # '╠'
- 178: 240, # '╡'
- 179: 241, # 'Ё'
- 180: 242, # '╢'
- 181: 243, # '╣'
- 182: 244, # '╤'
- 183: 245, # '╥'
- 184: 246, # '╦'
- 185: 247, # '╧'
- 186: 248, # '╨'
- 187: 249, # '╩'
- 188: 250, # '╪'
- 189: 251, # '╫'
- 190: 252, # '╬'
- 191: 253, # '©'
- 192: 27, # 'ю'
- 193: 3, # 'а'
- 194: 21, # 'б'
- 195: 28, # 'ц'
- 196: 13, # 'д'
- 197: 2, # 'е'
- 198: 39, # 'ф'
- 199: 19, # 'г'
- 200: 26, # 'х'
- 201: 4, # 'и'
- 202: 23, # 'й'
- 203: 11, # 'к'
- 204: 8, # 'л'
- 205: 12, # 'м'
- 206: 5, # 'н'
- 207: 1, # 'о'
- 208: 15, # 'п'
- 209: 16, # 'я'
- 210: 9, # 'р'
- 211: 7, # 'с'
- 212: 6, # 'т'
- 213: 14, # 'у'
- 214: 24, # 'ж'
- 215: 10, # 'в'
- 216: 17, # 'ь'
- 217: 18, # 'ы'
- 218: 20, # 'з'
- 219: 25, # 'ш'
- 220: 30, # 'э'
- 221: 29, # 'щ'
- 222: 22, # 'ч'
- 223: 54, # 'ъ'
- 224: 59, # 'Ю'
- 225: 37, # 'А'
- 226: 44, # 'Б'
- 227: 58, # 'Ц'
- 228: 41, # 'Д'
- 229: 48, # 'Е'
- 230: 53, # 'Ф'
- 231: 46, # 'Г'
- 232: 55, # 'Х'
- 233: 42, # 'И'
- 234: 60, # 'Й'
- 235: 36, # 'К'
- 236: 49, # 'Л'
- 237: 38, # 'М'
- 238: 31, # 'Н'
- 239: 34, # 'О'
- 240: 35, # 'П'
- 241: 43, # 'Я'
- 242: 45, # 'Р'
- 243: 32, # 'С'
- 244: 40, # 'Т'
- 245: 52, # 'У'
- 246: 56, # 'Ж'
- 247: 33, # 'В'
- 248: 61, # 'Ь'
- 249: 62, # 'Ы'
- 250: 51, # 'З'
- 251: 57, # 'Ш'
- 252: 47, # 'Э'
- 253: 63, # 'Щ'
- 254: 50, # 'Ч'
- 255: 70, # 'Ъ'
-}
-
-KOI8_R_RUSSIAN_MODEL = SingleByteCharSetModel(
- charset_name="KOI8-R",
- language="Russian",
- char_to_order_map=KOI8_R_RUSSIAN_CHAR_TO_ORDER,
- language_model=RUSSIAN_LANG_MODEL,
- typical_positive_ratio=0.976601,
- keep_ascii_letters=False,
- alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё",
-)
-
-MACCYRILLIC_RUSSIAN_CHAR_TO_ORDER = {
- 0: 255, # '\x00'
- 1: 255, # '\x01'
- 2: 255, # '\x02'
- 3: 255, # '\x03'
- 4: 255, # '\x04'
- 5: 255, # '\x05'
- 6: 255, # '\x06'
- 7: 255, # '\x07'
- 8: 255, # '\x08'
- 9: 255, # '\t'
- 10: 254, # '\n'
- 11: 255, # '\x0b'
- 12: 255, # '\x0c'
- 13: 254, # '\r'
- 14: 255, # '\x0e'
- 15: 255, # '\x0f'
- 16: 255, # '\x10'
- 17: 255, # '\x11'
- 18: 255, # '\x12'
- 19: 255, # '\x13'
- 20: 255, # '\x14'
- 21: 255, # '\x15'
- 22: 255, # '\x16'
- 23: 255, # '\x17'
- 24: 255, # '\x18'
- 25: 255, # '\x19'
- 26: 255, # '\x1a'
- 27: 255, # '\x1b'
- 28: 255, # '\x1c'
- 29: 255, # '\x1d'
- 30: 255, # '\x1e'
- 31: 255, # '\x1f'
- 32: 253, # ' '
- 33: 253, # '!'
- 34: 253, # '"'
- 35: 253, # '#'
- 36: 253, # '$'
- 37: 253, # '%'
- 38: 253, # '&'
- 39: 253, # "'"
- 40: 253, # '('
- 41: 253, # ')'
- 42: 253, # '*'
- 43: 253, # '+'
- 44: 253, # ','
- 45: 253, # '-'
- 46: 253, # '.'
- 47: 253, # '/'
- 48: 252, # '0'
- 49: 252, # '1'
- 50: 252, # '2'
- 51: 252, # '3'
- 52: 252, # '4'
- 53: 252, # '5'
- 54: 252, # '6'
- 55: 252, # '7'
- 56: 252, # '8'
- 57: 252, # '9'
- 58: 253, # ':'
- 59: 253, # ';'
- 60: 253, # '<'
- 61: 253, # '='
- 62: 253, # '>'
- 63: 253, # '?'
- 64: 253, # '@'
- 65: 142, # 'A'
- 66: 143, # 'B'
- 67: 144, # 'C'
- 68: 145, # 'D'
- 69: 146, # 'E'
- 70: 147, # 'F'
- 71: 148, # 'G'
- 72: 149, # 'H'
- 73: 150, # 'I'
- 74: 151, # 'J'
- 75: 152, # 'K'
- 76: 74, # 'L'
- 77: 153, # 'M'
- 78: 75, # 'N'
- 79: 154, # 'O'
- 80: 155, # 'P'
- 81: 156, # 'Q'
- 82: 157, # 'R'
- 83: 158, # 'S'
- 84: 159, # 'T'
- 85: 160, # 'U'
- 86: 161, # 'V'
- 87: 162, # 'W'
- 88: 163, # 'X'
- 89: 164, # 'Y'
- 90: 165, # 'Z'
- 91: 253, # '['
- 92: 253, # '\\'
- 93: 253, # ']'
- 94: 253, # '^'
- 95: 253, # '_'
- 96: 253, # '`'
- 97: 71, # 'a'
- 98: 172, # 'b'
- 99: 66, # 'c'
- 100: 173, # 'd'
- 101: 65, # 'e'
- 102: 174, # 'f'
- 103: 76, # 'g'
- 104: 175, # 'h'
- 105: 64, # 'i'
- 106: 176, # 'j'
- 107: 177, # 'k'
- 108: 77, # 'l'
- 109: 72, # 'm'
- 110: 178, # 'n'
- 111: 69, # 'o'
- 112: 67, # 'p'
- 113: 179, # 'q'
- 114: 78, # 'r'
- 115: 73, # 's'
- 116: 180, # 't'
- 117: 181, # 'u'
- 118: 79, # 'v'
- 119: 182, # 'w'
- 120: 183, # 'x'
- 121: 184, # 'y'
- 122: 185, # 'z'
- 123: 253, # '{'
- 124: 253, # '|'
- 125: 253, # '}'
- 126: 253, # '~'
- 127: 253, # '\x7f'
- 128: 37, # 'А'
- 129: 44, # 'Б'
- 130: 33, # 'В'
- 131: 46, # 'Г'
- 132: 41, # 'Д'
- 133: 48, # 'Е'
- 134: 56, # 'Ж'
- 135: 51, # 'З'
- 136: 42, # 'И'
- 137: 60, # 'Й'
- 138: 36, # 'К'
- 139: 49, # 'Л'
- 140: 38, # 'М'
- 141: 31, # 'Н'
- 142: 34, # 'О'
- 143: 35, # 'П'
- 144: 45, # 'Р'
- 145: 32, # 'С'
- 146: 40, # 'Т'
- 147: 52, # 'У'
- 148: 53, # 'Ф'
- 149: 55, # 'Х'
- 150: 58, # 'Ц'
- 151: 50, # 'Ч'
- 152: 57, # 'Ш'
- 153: 63, # 'Щ'
- 154: 70, # 'Ъ'
- 155: 62, # 'Ы'
- 156: 61, # 'Ь'
- 157: 47, # 'Э'
- 158: 59, # 'Ю'
- 159: 43, # 'Я'
- 160: 191, # '†'
- 161: 192, # '°'
- 162: 193, # 'Ґ'
- 163: 194, # '£'
- 164: 195, # '§'
- 165: 196, # '•'
- 166: 197, # '¶'
- 167: 198, # 'І'
- 168: 199, # '®'
- 169: 200, # '©'
- 170: 201, # '™'
- 171: 202, # 'Ђ'
- 172: 203, # 'ђ'
- 173: 204, # '≠'
- 174: 205, # 'Ѓ'
- 175: 206, # 'ѓ'
- 176: 207, # '∞'
- 177: 208, # '±'
- 178: 209, # '≤'
- 179: 210, # '≥'
- 180: 211, # 'і'
- 181: 212, # 'µ'
- 182: 213, # 'ґ'
- 183: 214, # 'Ј'
- 184: 215, # 'Є'
- 185: 216, # 'є'
- 186: 217, # 'Ї'
- 187: 218, # 'ї'
- 188: 219, # 'Љ'
- 189: 220, # 'љ'
- 190: 221, # 'Њ'
- 191: 222, # 'њ'
- 192: 223, # 'ј'
- 193: 224, # 'Ѕ'
- 194: 225, # '¬'
- 195: 226, # '√'
- 196: 227, # 'ƒ'
- 197: 228, # '≈'
- 198: 229, # '∆'
- 199: 230, # '«'
- 200: 231, # '»'
- 201: 232, # '…'
- 202: 233, # '\xa0'
- 203: 234, # 'Ћ'
- 204: 235, # 'ћ'
- 205: 236, # 'Ќ'
- 206: 237, # 'ќ'
- 207: 238, # 'ѕ'
- 208: 239, # '–'
- 209: 240, # '—'
- 210: 241, # '“'
- 211: 242, # '”'
- 212: 243, # '‘'
- 213: 244, # '’'
- 214: 245, # '÷'
- 215: 246, # '„'
- 216: 247, # 'Ў'
- 217: 248, # 'ў'
- 218: 249, # 'Џ'
- 219: 250, # 'џ'
- 220: 251, # '№'
- 221: 252, # 'Ё'
- 222: 68, # 'ё'
- 223: 16, # 'я'
- 224: 3, # 'а'
- 225: 21, # 'б'
- 226: 10, # 'в'
- 227: 19, # 'г'
- 228: 13, # 'д'
- 229: 2, # 'е'
- 230: 24, # 'ж'
- 231: 20, # 'з'
- 232: 4, # 'и'
- 233: 23, # 'й'
- 234: 11, # 'к'
- 235: 8, # 'л'
- 236: 12, # 'м'
- 237: 5, # 'н'
- 238: 1, # 'о'
- 239: 15, # 'п'
- 240: 9, # 'р'
- 241: 7, # 'с'
- 242: 6, # 'т'
- 243: 14, # 'у'
- 244: 39, # 'ф'
- 245: 26, # 'х'
- 246: 28, # 'ц'
- 247: 22, # 'ч'
- 248: 25, # 'ш'
- 249: 29, # 'щ'
- 250: 54, # 'ъ'
- 251: 18, # 'ы'
- 252: 17, # 'ь'
- 253: 30, # 'э'
- 254: 27, # 'ю'
- 255: 255, # '€'
-}
-
-MACCYRILLIC_RUSSIAN_MODEL = SingleByteCharSetModel(
- charset_name="MacCyrillic",
- language="Russian",
- char_to_order_map=MACCYRILLIC_RUSSIAN_CHAR_TO_ORDER,
- language_model=RUSSIAN_LANG_MODEL,
- typical_positive_ratio=0.976601,
- keep_ascii_letters=False,
- alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё",
-)
-
-ISO_8859_5_RUSSIAN_CHAR_TO_ORDER = {
- 0: 255, # '\x00'
- 1: 255, # '\x01'
- 2: 255, # '\x02'
- 3: 255, # '\x03'
- 4: 255, # '\x04'
- 5: 255, # '\x05'
- 6: 255, # '\x06'
- 7: 255, # '\x07'
- 8: 255, # '\x08'
- 9: 255, # '\t'
- 10: 254, # '\n'
- 11: 255, # '\x0b'
- 12: 255, # '\x0c'
- 13: 254, # '\r'
- 14: 255, # '\x0e'
- 15: 255, # '\x0f'
- 16: 255, # '\x10'
- 17: 255, # '\x11'
- 18: 255, # '\x12'
- 19: 255, # '\x13'
- 20: 255, # '\x14'
- 21: 255, # '\x15'
- 22: 255, # '\x16'
- 23: 255, # '\x17'
- 24: 255, # '\x18'
- 25: 255, # '\x19'
- 26: 255, # '\x1a'
- 27: 255, # '\x1b'
- 28: 255, # '\x1c'
- 29: 255, # '\x1d'
- 30: 255, # '\x1e'
- 31: 255, # '\x1f'
- 32: 253, # ' '
- 33: 253, # '!'
- 34: 253, # '"'
- 35: 253, # '#'
- 36: 253, # '$'
- 37: 253, # '%'
- 38: 253, # '&'
- 39: 253, # "'"
- 40: 253, # '('
- 41: 253, # ')'
- 42: 253, # '*'
- 43: 253, # '+'
- 44: 253, # ','
- 45: 253, # '-'
- 46: 253, # '.'
- 47: 253, # '/'
- 48: 252, # '0'
- 49: 252, # '1'
- 50: 252, # '2'
- 51: 252, # '3'
- 52: 252, # '4'
- 53: 252, # '5'
- 54: 252, # '6'
- 55: 252, # '7'
- 56: 252, # '8'
- 57: 252, # '9'
- 58: 253, # ':'
- 59: 253, # ';'
- 60: 253, # '<'
- 61: 253, # '='
- 62: 253, # '>'
- 63: 253, # '?'
- 64: 253, # '@'
- 65: 142, # 'A'
- 66: 143, # 'B'
- 67: 144, # 'C'
- 68: 145, # 'D'
- 69: 146, # 'E'
- 70: 147, # 'F'
- 71: 148, # 'G'
- 72: 149, # 'H'
- 73: 150, # 'I'
- 74: 151, # 'J'
- 75: 152, # 'K'
- 76: 74, # 'L'
- 77: 153, # 'M'
- 78: 75, # 'N'
- 79: 154, # 'O'
- 80: 155, # 'P'
- 81: 156, # 'Q'
- 82: 157, # 'R'
- 83: 158, # 'S'
- 84: 159, # 'T'
- 85: 160, # 'U'
- 86: 161, # 'V'
- 87: 162, # 'W'
- 88: 163, # 'X'
- 89: 164, # 'Y'
- 90: 165, # 'Z'
- 91: 253, # '['
- 92: 253, # '\\'
- 93: 253, # ']'
- 94: 253, # '^'
- 95: 253, # '_'
- 96: 253, # '`'
- 97: 71, # 'a'
- 98: 172, # 'b'
- 99: 66, # 'c'
- 100: 173, # 'd'
- 101: 65, # 'e'
- 102: 174, # 'f'
- 103: 76, # 'g'
- 104: 175, # 'h'
- 105: 64, # 'i'
- 106: 176, # 'j'
- 107: 177, # 'k'
- 108: 77, # 'l'
- 109: 72, # 'm'
- 110: 178, # 'n'
- 111: 69, # 'o'
- 112: 67, # 'p'
- 113: 179, # 'q'
- 114: 78, # 'r'
- 115: 73, # 's'
- 116: 180, # 't'
- 117: 181, # 'u'
- 118: 79, # 'v'
- 119: 182, # 'w'
- 120: 183, # 'x'
- 121: 184, # 'y'
- 122: 185, # 'z'
- 123: 253, # '{'
- 124: 253, # '|'
- 125: 253, # '}'
- 126: 253, # '~'
- 127: 253, # '\x7f'
- 128: 191, # '\x80'
- 129: 192, # '\x81'
- 130: 193, # '\x82'
- 131: 194, # '\x83'
- 132: 195, # '\x84'
- 133: 196, # '\x85'
- 134: 197, # '\x86'
- 135: 198, # '\x87'
- 136: 199, # '\x88'
- 137: 200, # '\x89'
- 138: 201, # '\x8a'
- 139: 202, # '\x8b'
- 140: 203, # '\x8c'
- 141: 204, # '\x8d'
- 142: 205, # '\x8e'
- 143: 206, # '\x8f'
- 144: 207, # '\x90'
- 145: 208, # '\x91'
- 146: 209, # '\x92'
- 147: 210, # '\x93'
- 148: 211, # '\x94'
- 149: 212, # '\x95'
- 150: 213, # '\x96'
- 151: 214, # '\x97'
- 152: 215, # '\x98'
- 153: 216, # '\x99'
- 154: 217, # '\x9a'
- 155: 218, # '\x9b'
- 156: 219, # '\x9c'
- 157: 220, # '\x9d'
- 158: 221, # '\x9e'
- 159: 222, # '\x9f'
- 160: 223, # '\xa0'
- 161: 224, # 'Ё'
- 162: 225, # 'Ђ'
- 163: 226, # 'Ѓ'
- 164: 227, # 'Є'
- 165: 228, # 'Ѕ'
- 166: 229, # 'І'
- 167: 230, # 'Ї'
- 168: 231, # 'Ј'
- 169: 232, # 'Љ'
- 170: 233, # 'Њ'
- 171: 234, # 'Ћ'
- 172: 235, # 'Ќ'
- 173: 236, # '\xad'
- 174: 237, # 'Ў'
- 175: 238, # 'Џ'
- 176: 37, # 'А'
- 177: 44, # 'Б'
- 178: 33, # 'В'
- 179: 46, # 'Г'
- 180: 41, # 'Д'
- 181: 48, # 'Е'
- 182: 56, # 'Ж'
- 183: 51, # 'З'
- 184: 42, # 'И'
- 185: 60, # 'Й'
- 186: 36, # 'К'
- 187: 49, # 'Л'
- 188: 38, # 'М'
- 189: 31, # 'Н'
- 190: 34, # 'О'
- 191: 35, # 'П'
- 192: 45, # 'Р'
- 193: 32, # 'С'
- 194: 40, # 'Т'
- 195: 52, # 'У'
- 196: 53, # 'Ф'
- 197: 55, # 'Х'
- 198: 58, # 'Ц'
- 199: 50, # 'Ч'
- 200: 57, # 'Ш'
- 201: 63, # 'Щ'
- 202: 70, # 'Ъ'
- 203: 62, # 'Ы'
- 204: 61, # 'Ь'
- 205: 47, # 'Э'
- 206: 59, # 'Ю'
- 207: 43, # 'Я'
- 208: 3, # 'а'
- 209: 21, # 'б'
- 210: 10, # 'в'
- 211: 19, # 'г'
- 212: 13, # 'д'
- 213: 2, # 'е'
- 214: 24, # 'ж'
- 215: 20, # 'з'
- 216: 4, # 'и'
- 217: 23, # 'й'
- 218: 11, # 'к'
- 219: 8, # 'л'
- 220: 12, # 'м'
- 221: 5, # 'н'
- 222: 1, # 'о'
- 223: 15, # 'п'
- 224: 9, # 'р'
- 225: 7, # 'с'
- 226: 6, # 'т'
- 227: 14, # 'у'
- 228: 39, # 'ф'
- 229: 26, # 'х'
- 230: 28, # 'ц'
- 231: 22, # 'ч'
- 232: 25, # 'ш'
- 233: 29, # 'щ'
- 234: 54, # 'ъ'
- 235: 18, # 'ы'
- 236: 17, # 'ь'
- 237: 30, # 'э'
- 238: 27, # 'ю'
- 239: 16, # 'я'
- 240: 239, # '№'
- 241: 68, # 'ё'
- 242: 240, # 'ђ'
- 243: 241, # 'ѓ'
- 244: 242, # 'є'
- 245: 243, # 'ѕ'
- 246: 244, # 'і'
- 247: 245, # 'ї'
- 248: 246, # 'ј'
- 249: 247, # 'љ'
- 250: 248, # 'њ'
- 251: 249, # 'ћ'
- 252: 250, # 'ќ'
- 253: 251, # '§'
- 254: 252, # 'ў'
- 255: 255, # 'џ'
-}
-
-ISO_8859_5_RUSSIAN_MODEL = SingleByteCharSetModel(
- charset_name="ISO-8859-5",
- language="Russian",
- char_to_order_map=ISO_8859_5_RUSSIAN_CHAR_TO_ORDER,
- language_model=RUSSIAN_LANG_MODEL,
- typical_positive_ratio=0.976601,
- keep_ascii_letters=False,
- alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё",
-)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/langthaimodel.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/langthaimodel.py
deleted file mode 100644
index 883fdb1..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/langthaimodel.py
+++ /dev/null
@@ -1,4380 +0,0 @@
-from chardet.sbcharsetprober import SingleByteCharSetModel
-
-# 3: Positive
-# 2: Likely
-# 1: Unlikely
-# 0: Negative
-
-THAI_LANG_MODEL = {
- 5: { # 'ก'
- 5: 2, # 'ก'
- 30: 2, # 'ข'
- 24: 2, # 'ค'
- 8: 2, # 'ง'
- 26: 2, # 'จ'
- 52: 0, # 'ฉ'
- 34: 1, # 'ช'
- 51: 1, # 'ซ'
- 47: 0, # 'ญ'
- 58: 3, # 'ฎ'
- 57: 2, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 2, # 'ณ'
- 20: 2, # 'ด'
- 19: 3, # 'ต'
- 44: 0, # 'ถ'
- 14: 2, # 'ท'
- 48: 0, # 'ธ'
- 3: 2, # 'น'
- 17: 1, # 'บ'
- 25: 2, # 'ป'
- 39: 1, # 'ผ'
- 62: 1, # 'ฝ'
- 31: 1, # 'พ'
- 54: 0, # 'ฟ'
- 45: 1, # 'ภ'
- 9: 2, # 'ม'
- 16: 1, # 'ย'
- 2: 3, # 'ร'
- 61: 2, # 'ฤ'
- 15: 3, # 'ล'
- 12: 3, # 'ว'
- 42: 2, # 'ศ'
- 46: 3, # 'ษ'
- 18: 2, # 'ส'
- 21: 2, # 'ห'
- 4: 3, # 'อ'
- 63: 1, # 'ฯ'
- 22: 2, # 'ะ'
- 10: 3, # 'ั'
- 1: 3, # 'า'
- 36: 3, # 'ำ'
- 23: 3, # 'ิ'
- 13: 3, # 'ี'
- 40: 0, # 'ึ'
- 27: 2, # 'ื'
- 32: 2, # 'ุ'
- 35: 1, # 'ู'
- 11: 2, # 'เ'
- 28: 2, # 'แ'
- 41: 1, # 'โ'
- 29: 1, # 'ใ'
- 33: 2, # 'ไ'
- 50: 1, # 'ๆ'
- 37: 3, # '็'
- 6: 3, # '่'
- 7: 3, # '้'
- 38: 2, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 30: { # 'ข'
- 5: 1, # 'ก'
- 30: 0, # 'ข'
- 24: 1, # 'ค'
- 8: 1, # 'ง'
- 26: 1, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 2, # 'ณ'
- 20: 0, # 'ด'
- 19: 2, # 'ต'
- 44: 0, # 'ถ'
- 14: 1, # 'ท'
- 48: 0, # 'ธ'
- 3: 2, # 'น'
- 17: 1, # 'บ'
- 25: 1, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 0, # 'ม'
- 16: 2, # 'ย'
- 2: 1, # 'ร'
- 61: 0, # 'ฤ'
- 15: 0, # 'ล'
- 12: 2, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 1, # 'ส'
- 21: 1, # 'ห'
- 4: 3, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 3, # 'ั'
- 1: 3, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 2, # 'ี'
- 40: 3, # 'ึ'
- 27: 1, # 'ื'
- 32: 1, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 1, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 1, # '็'
- 6: 2, # '่'
- 7: 3, # '้'
- 38: 1, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 24: { # 'ค'
- 5: 0, # 'ก'
- 30: 0, # 'ข'
- 24: 2, # 'ค'
- 8: 2, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 2, # 'ณ'
- 20: 2, # 'ด'
- 19: 2, # 'ต'
- 44: 0, # 'ถ'
- 14: 1, # 'ท'
- 48: 0, # 'ธ'
- 3: 3, # 'น'
- 17: 0, # 'บ'
- 25: 1, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 2, # 'ม'
- 16: 2, # 'ย'
- 2: 3, # 'ร'
- 61: 0, # 'ฤ'
- 15: 3, # 'ล'
- 12: 3, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 1, # 'ส'
- 21: 0, # 'ห'
- 4: 2, # 'อ'
- 63: 0, # 'ฯ'
- 22: 2, # 'ะ'
- 10: 3, # 'ั'
- 1: 2, # 'า'
- 36: 3, # 'ำ'
- 23: 3, # 'ิ'
- 13: 2, # 'ี'
- 40: 0, # 'ึ'
- 27: 3, # 'ื'
- 32: 3, # 'ุ'
- 35: 2, # 'ู'
- 11: 1, # 'เ'
- 28: 0, # 'แ'
- 41: 3, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 1, # '็'
- 6: 3, # '่'
- 7: 3, # '้'
- 38: 3, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 8: { # 'ง'
- 5: 3, # 'ก'
- 30: 2, # 'ข'
- 24: 3, # 'ค'
- 8: 2, # 'ง'
- 26: 2, # 'จ'
- 52: 1, # 'ฉ'
- 34: 2, # 'ช'
- 51: 1, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 2, # 'ด'
- 19: 2, # 'ต'
- 44: 1, # 'ถ'
- 14: 3, # 'ท'
- 48: 1, # 'ธ'
- 3: 3, # 'น'
- 17: 2, # 'บ'
- 25: 2, # 'ป'
- 39: 2, # 'ผ'
- 62: 1, # 'ฝ'
- 31: 2, # 'พ'
- 54: 0, # 'ฟ'
- 45: 1, # 'ภ'
- 9: 2, # 'ม'
- 16: 1, # 'ย'
- 2: 2, # 'ร'
- 61: 0, # 'ฤ'
- 15: 2, # 'ล'
- 12: 2, # 'ว'
- 42: 2, # 'ศ'
- 46: 1, # 'ษ'
- 18: 3, # 'ส'
- 21: 3, # 'ห'
- 4: 2, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 1, # 'ั'
- 1: 3, # 'า'
- 36: 0, # 'ำ'
- 23: 2, # 'ิ'
- 13: 1, # 'ี'
- 40: 0, # 'ึ'
- 27: 1, # 'ื'
- 32: 1, # 'ุ'
- 35: 0, # 'ู'
- 11: 3, # 'เ'
- 28: 2, # 'แ'
- 41: 1, # 'โ'
- 29: 2, # 'ใ'
- 33: 2, # 'ไ'
- 50: 3, # 'ๆ'
- 37: 0, # '็'
- 6: 2, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 26: { # 'จ'
- 5: 2, # 'ก'
- 30: 1, # 'ข'
- 24: 0, # 'ค'
- 8: 2, # 'ง'
- 26: 3, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 2, # 'ด'
- 19: 1, # 'ต'
- 44: 1, # 'ถ'
- 14: 2, # 'ท'
- 48: 0, # 'ธ'
- 3: 3, # 'น'
- 17: 1, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 1, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 1, # 'ม'
- 16: 1, # 'ย'
- 2: 3, # 'ร'
- 61: 0, # 'ฤ'
- 15: 0, # 'ล'
- 12: 1, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 2, # 'ส'
- 21: 1, # 'ห'
- 4: 2, # 'อ'
- 63: 0, # 'ฯ'
- 22: 3, # 'ะ'
- 10: 3, # 'ั'
- 1: 3, # 'า'
- 36: 3, # 'ำ'
- 23: 2, # 'ิ'
- 13: 1, # 'ี'
- 40: 3, # 'ึ'
- 27: 1, # 'ื'
- 32: 3, # 'ุ'
- 35: 2, # 'ู'
- 11: 1, # 'เ'
- 28: 1, # 'แ'
- 41: 0, # 'โ'
- 29: 1, # 'ใ'
- 33: 1, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 2, # '่'
- 7: 2, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 52: { # 'ฉ'
- 5: 0, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 0, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 0, # 'ด'
- 19: 0, # 'ต'
- 44: 0, # 'ถ'
- 14: 0, # 'ท'
- 48: 0, # 'ธ'
- 3: 0, # 'น'
- 17: 3, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 3, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 1, # 'ม'
- 16: 1, # 'ย'
- 2: 0, # 'ร'
- 61: 0, # 'ฤ'
- 15: 2, # 'ล'
- 12: 1, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 0, # 'ส'
- 21: 0, # 'ห'
- 4: 0, # 'อ'
- 63: 0, # 'ฯ'
- 22: 1, # 'ะ'
- 10: 1, # 'ั'
- 1: 1, # 'า'
- 36: 0, # 'ำ'
- 23: 1, # 'ิ'
- 13: 1, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 1, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 34: { # 'ช'
- 5: 1, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 1, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 1, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 0, # 'ด'
- 19: 0, # 'ต'
- 44: 0, # 'ถ'
- 14: 1, # 'ท'
- 48: 0, # 'ธ'
- 3: 3, # 'น'
- 17: 2, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 2, # 'ม'
- 16: 1, # 'ย'
- 2: 1, # 'ร'
- 61: 0, # 'ฤ'
- 15: 0, # 'ล'
- 12: 1, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 0, # 'ส'
- 21: 0, # 'ห'
- 4: 2, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 2, # 'ั'
- 1: 3, # 'า'
- 36: 1, # 'ำ'
- 23: 3, # 'ิ'
- 13: 2, # 'ี'
- 40: 0, # 'ึ'
- 27: 3, # 'ื'
- 32: 3, # 'ุ'
- 35: 1, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 1, # '็'
- 6: 3, # '่'
- 7: 3, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 51: { # 'ซ'
- 5: 0, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 0, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 0, # 'ด'
- 19: 0, # 'ต'
- 44: 0, # 'ถ'
- 14: 0, # 'ท'
- 48: 0, # 'ธ'
- 3: 1, # 'น'
- 17: 0, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 0, # 'ม'
- 16: 0, # 'ย'
- 2: 0, # 'ร'
- 61: 0, # 'ฤ'
- 15: 1, # 'ล'
- 12: 0, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 1, # 'ส'
- 21: 0, # 'ห'
- 4: 2, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 1, # 'ั'
- 1: 1, # 'า'
- 36: 0, # 'ำ'
- 23: 1, # 'ิ'
- 13: 2, # 'ี'
- 40: 3, # 'ึ'
- 27: 2, # 'ื'
- 32: 1, # 'ุ'
- 35: 1, # 'ู'
- 11: 1, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 1, # '็'
- 6: 1, # '่'
- 7: 2, # '้'
- 38: 1, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 47: { # 'ญ'
- 5: 1, # 'ก'
- 30: 1, # 'ข'
- 24: 0, # 'ค'
- 8: 0, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 1, # 'ช'
- 51: 0, # 'ซ'
- 47: 3, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 0, # 'ด'
- 19: 0, # 'ต'
- 44: 0, # 'ถ'
- 14: 1, # 'ท'
- 48: 0, # 'ธ'
- 3: 0, # 'น'
- 17: 1, # 'บ'
- 25: 1, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 1, # 'ม'
- 16: 0, # 'ย'
- 2: 0, # 'ร'
- 61: 0, # 'ฤ'
- 15: 1, # 'ล'
- 12: 0, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 1, # 'ส'
- 21: 2, # 'ห'
- 4: 1, # 'อ'
- 63: 0, # 'ฯ'
- 22: 1, # 'ะ'
- 10: 2, # 'ั'
- 1: 3, # 'า'
- 36: 0, # 'ำ'
- 23: 1, # 'ิ'
- 13: 1, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 1, # 'เ'
- 28: 1, # 'แ'
- 41: 0, # 'โ'
- 29: 1, # 'ใ'
- 33: 0, # 'ไ'
- 50: 1, # 'ๆ'
- 37: 0, # '็'
- 6: 2, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 58: { # 'ฎ'
- 5: 2, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 0, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 0, # 'ด'
- 19: 0, # 'ต'
- 44: 0, # 'ถ'
- 14: 0, # 'ท'
- 48: 0, # 'ธ'
- 3: 0, # 'น'
- 17: 0, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 0, # 'ม'
- 16: 0, # 'ย'
- 2: 0, # 'ร'
- 61: 0, # 'ฤ'
- 15: 0, # 'ล'
- 12: 0, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 0, # 'ส'
- 21: 1, # 'ห'
- 4: 0, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 1, # 'ิ'
- 13: 2, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 57: { # 'ฏ'
- 5: 0, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 0, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 0, # 'ด'
- 19: 0, # 'ต'
- 44: 0, # 'ถ'
- 14: 0, # 'ท'
- 48: 0, # 'ธ'
- 3: 0, # 'น'
- 17: 0, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 0, # 'ม'
- 16: 0, # 'ย'
- 2: 0, # 'ร'
- 61: 0, # 'ฤ'
- 15: 0, # 'ล'
- 12: 0, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 0, # 'ส'
- 21: 0, # 'ห'
- 4: 0, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 3, # 'ิ'
- 13: 1, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 49: { # 'ฐ'
- 5: 1, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 0, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 0, # 'ด'
- 19: 0, # 'ต'
- 44: 0, # 'ถ'
- 14: 0, # 'ท'
- 48: 0, # 'ธ'
- 3: 0, # 'น'
- 17: 2, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 2, # 'ม'
- 16: 0, # 'ย'
- 2: 0, # 'ร'
- 61: 0, # 'ฤ'
- 15: 0, # 'ล'
- 12: 0, # 'ว'
- 42: 1, # 'ศ'
- 46: 0, # 'ษ'
- 18: 0, # 'ส'
- 21: 0, # 'ห'
- 4: 1, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 3, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 1, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 53: { # 'ฑ'
- 5: 0, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 0, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 0, # 'ด'
- 19: 0, # 'ต'
- 44: 0, # 'ถ'
- 14: 0, # 'ท'
- 48: 0, # 'ธ'
- 3: 0, # 'น'
- 17: 0, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 0, # 'ม'
- 16: 0, # 'ย'
- 2: 0, # 'ร'
- 61: 0, # 'ฤ'
- 15: 0, # 'ล'
- 12: 0, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 0, # 'ส'
- 21: 0, # 'ห'
- 4: 0, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 2, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 3, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 55: { # 'ฒ'
- 5: 0, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 0, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 0, # 'ด'
- 19: 0, # 'ต'
- 44: 0, # 'ถ'
- 14: 0, # 'ท'
- 48: 0, # 'ธ'
- 3: 3, # 'น'
- 17: 0, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 1, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 0, # 'ม'
- 16: 0, # 'ย'
- 2: 0, # 'ร'
- 61: 0, # 'ฤ'
- 15: 0, # 'ล'
- 12: 0, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 0, # 'ส'
- 21: 0, # 'ห'
- 4: 0, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 1, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 43: { # 'ณ'
- 5: 1, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 0, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 3, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 0, # 'ด'
- 19: 0, # 'ต'
- 44: 0, # 'ถ'
- 14: 0, # 'ท'
- 48: 0, # 'ธ'
- 3: 0, # 'น'
- 17: 0, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 3, # 'ภ'
- 9: 0, # 'ม'
- 16: 0, # 'ย'
- 2: 1, # 'ร'
- 61: 0, # 'ฤ'
- 15: 0, # 'ล'
- 12: 1, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 1, # 'ส'
- 21: 1, # 'ห'
- 4: 0, # 'อ'
- 63: 0, # 'ฯ'
- 22: 3, # 'ะ'
- 10: 0, # 'ั'
- 1: 3, # 'า'
- 36: 0, # 'ำ'
- 23: 1, # 'ิ'
- 13: 2, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 1, # 'เ'
- 28: 1, # 'แ'
- 41: 0, # 'โ'
- 29: 1, # 'ใ'
- 33: 1, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 3, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 20: { # 'ด'
- 5: 2, # 'ก'
- 30: 2, # 'ข'
- 24: 2, # 'ค'
- 8: 3, # 'ง'
- 26: 2, # 'จ'
- 52: 0, # 'ฉ'
- 34: 1, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 1, # 'ด'
- 19: 2, # 'ต'
- 44: 1, # 'ถ'
- 14: 2, # 'ท'
- 48: 0, # 'ธ'
- 3: 1, # 'น'
- 17: 1, # 'บ'
- 25: 1, # 'ป'
- 39: 1, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 1, # 'พ'
- 54: 0, # 'ฟ'
- 45: 1, # 'ภ'
- 9: 2, # 'ม'
- 16: 3, # 'ย'
- 2: 2, # 'ร'
- 61: 0, # 'ฤ'
- 15: 2, # 'ล'
- 12: 2, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 2, # 'ส'
- 21: 2, # 'ห'
- 4: 1, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 3, # 'ั'
- 1: 2, # 'า'
- 36: 2, # 'ำ'
- 23: 3, # 'ิ'
- 13: 3, # 'ี'
- 40: 1, # 'ึ'
- 27: 2, # 'ื'
- 32: 3, # 'ุ'
- 35: 2, # 'ู'
- 11: 2, # 'เ'
- 28: 2, # 'แ'
- 41: 1, # 'โ'
- 29: 2, # 'ใ'
- 33: 2, # 'ไ'
- 50: 2, # 'ๆ'
- 37: 2, # '็'
- 6: 1, # '่'
- 7: 3, # '้'
- 38: 1, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 19: { # 'ต'
- 5: 2, # 'ก'
- 30: 1, # 'ข'
- 24: 1, # 'ค'
- 8: 0, # 'ง'
- 26: 1, # 'จ'
- 52: 0, # 'ฉ'
- 34: 1, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 1, # 'ด'
- 19: 1, # 'ต'
- 44: 2, # 'ถ'
- 14: 1, # 'ท'
- 48: 0, # 'ธ'
- 3: 2, # 'น'
- 17: 1, # 'บ'
- 25: 1, # 'ป'
- 39: 1, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 1, # 'พ'
- 54: 0, # 'ฟ'
- 45: 2, # 'ภ'
- 9: 1, # 'ม'
- 16: 1, # 'ย'
- 2: 3, # 'ร'
- 61: 0, # 'ฤ'
- 15: 2, # 'ล'
- 12: 1, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 3, # 'ส'
- 21: 0, # 'ห'
- 4: 3, # 'อ'
- 63: 1, # 'ฯ'
- 22: 2, # 'ะ'
- 10: 3, # 'ั'
- 1: 3, # 'า'
- 36: 2, # 'ำ'
- 23: 3, # 'ิ'
- 13: 2, # 'ี'
- 40: 1, # 'ึ'
- 27: 1, # 'ื'
- 32: 3, # 'ุ'
- 35: 2, # 'ู'
- 11: 1, # 'เ'
- 28: 1, # 'แ'
- 41: 1, # 'โ'
- 29: 1, # 'ใ'
- 33: 1, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 2, # '็'
- 6: 3, # '่'
- 7: 3, # '้'
- 38: 2, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 44: { # 'ถ'
- 5: 1, # 'ก'
- 30: 0, # 'ข'
- 24: 1, # 'ค'
- 8: 0, # 'ง'
- 26: 1, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 0, # 'ด'
- 19: 1, # 'ต'
- 44: 0, # 'ถ'
- 14: 1, # 'ท'
- 48: 0, # 'ธ'
- 3: 1, # 'น'
- 17: 2, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 1, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 0, # 'ม'
- 16: 0, # 'ย'
- 2: 1, # 'ร'
- 61: 0, # 'ฤ'
- 15: 1, # 'ล'
- 12: 1, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 1, # 'ส'
- 21: 0, # 'ห'
- 4: 1, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 2, # 'ั'
- 1: 3, # 'า'
- 36: 0, # 'ำ'
- 23: 2, # 'ิ'
- 13: 1, # 'ี'
- 40: 3, # 'ึ'
- 27: 2, # 'ื'
- 32: 2, # 'ุ'
- 35: 3, # 'ู'
- 11: 1, # 'เ'
- 28: 1, # 'แ'
- 41: 0, # 'โ'
- 29: 1, # 'ใ'
- 33: 1, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 2, # '่'
- 7: 3, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 14: { # 'ท'
- 5: 1, # 'ก'
- 30: 1, # 'ข'
- 24: 3, # 'ค'
- 8: 1, # 'ง'
- 26: 1, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 2, # 'ด'
- 19: 1, # 'ต'
- 44: 0, # 'ถ'
- 14: 1, # 'ท'
- 48: 3, # 'ธ'
- 3: 3, # 'น'
- 17: 2, # 'บ'
- 25: 2, # 'ป'
- 39: 1, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 2, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 1, # 'ม'
- 16: 3, # 'ย'
- 2: 3, # 'ร'
- 61: 1, # 'ฤ'
- 15: 1, # 'ล'
- 12: 2, # 'ว'
- 42: 3, # 'ศ'
- 46: 1, # 'ษ'
- 18: 1, # 'ส'
- 21: 0, # 'ห'
- 4: 2, # 'อ'
- 63: 0, # 'ฯ'
- 22: 2, # 'ะ'
- 10: 3, # 'ั'
- 1: 3, # 'า'
- 36: 3, # 'ำ'
- 23: 2, # 'ิ'
- 13: 3, # 'ี'
- 40: 2, # 'ึ'
- 27: 1, # 'ื'
- 32: 3, # 'ุ'
- 35: 1, # 'ู'
- 11: 0, # 'เ'
- 28: 1, # 'แ'
- 41: 0, # 'โ'
- 29: 1, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 1, # '็'
- 6: 3, # '่'
- 7: 3, # '้'
- 38: 2, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 48: { # 'ธ'
- 5: 0, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 1, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 0, # 'ด'
- 19: 0, # 'ต'
- 44: 0, # 'ถ'
- 14: 0, # 'ท'
- 48: 0, # 'ธ'
- 3: 1, # 'น'
- 17: 0, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 0, # 'ม'
- 16: 0, # 'ย'
- 2: 2, # 'ร'
- 61: 0, # 'ฤ'
- 15: 0, # 'ล'
- 12: 0, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 0, # 'ส'
- 21: 0, # 'ห'
- 4: 0, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 2, # 'า'
- 36: 0, # 'ำ'
- 23: 3, # 'ิ'
- 13: 3, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 2, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 3, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 3: { # 'น'
- 5: 3, # 'ก'
- 30: 2, # 'ข'
- 24: 3, # 'ค'
- 8: 1, # 'ง'
- 26: 2, # 'จ'
- 52: 0, # 'ฉ'
- 34: 1, # 'ช'
- 51: 1, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 1, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 3, # 'ด'
- 19: 3, # 'ต'
- 44: 2, # 'ถ'
- 14: 3, # 'ท'
- 48: 3, # 'ธ'
- 3: 2, # 'น'
- 17: 2, # 'บ'
- 25: 2, # 'ป'
- 39: 2, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 2, # 'พ'
- 54: 1, # 'ฟ'
- 45: 1, # 'ภ'
- 9: 2, # 'ม'
- 16: 2, # 'ย'
- 2: 2, # 'ร'
- 61: 1, # 'ฤ'
- 15: 2, # 'ล'
- 12: 3, # 'ว'
- 42: 1, # 'ศ'
- 46: 0, # 'ษ'
- 18: 2, # 'ส'
- 21: 2, # 'ห'
- 4: 3, # 'อ'
- 63: 1, # 'ฯ'
- 22: 2, # 'ะ'
- 10: 3, # 'ั'
- 1: 3, # 'า'
- 36: 3, # 'ำ'
- 23: 3, # 'ิ'
- 13: 3, # 'ี'
- 40: 3, # 'ึ'
- 27: 3, # 'ื'
- 32: 3, # 'ุ'
- 35: 2, # 'ู'
- 11: 3, # 'เ'
- 28: 2, # 'แ'
- 41: 3, # 'โ'
- 29: 3, # 'ใ'
- 33: 3, # 'ไ'
- 50: 2, # 'ๆ'
- 37: 1, # '็'
- 6: 3, # '่'
- 7: 3, # '้'
- 38: 2, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 17: { # 'บ'
- 5: 3, # 'ก'
- 30: 2, # 'ข'
- 24: 2, # 'ค'
- 8: 1, # 'ง'
- 26: 1, # 'จ'
- 52: 1, # 'ฉ'
- 34: 1, # 'ช'
- 51: 1, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 1, # 'ด'
- 19: 2, # 'ต'
- 44: 1, # 'ถ'
- 14: 3, # 'ท'
- 48: 0, # 'ธ'
- 3: 3, # 'น'
- 17: 3, # 'บ'
- 25: 2, # 'ป'
- 39: 2, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 1, # 'พ'
- 54: 1, # 'ฟ'
- 45: 1, # 'ภ'
- 9: 1, # 'ม'
- 16: 0, # 'ย'
- 2: 3, # 'ร'
- 61: 0, # 'ฤ'
- 15: 2, # 'ล'
- 12: 3, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 2, # 'ส'
- 21: 2, # 'ห'
- 4: 2, # 'อ'
- 63: 1, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 3, # 'ั'
- 1: 3, # 'า'
- 36: 2, # 'ำ'
- 23: 2, # 'ิ'
- 13: 2, # 'ี'
- 40: 0, # 'ึ'
- 27: 2, # 'ื'
- 32: 3, # 'ุ'
- 35: 2, # 'ู'
- 11: 2, # 'เ'
- 28: 2, # 'แ'
- 41: 1, # 'โ'
- 29: 2, # 'ใ'
- 33: 2, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 1, # '็'
- 6: 2, # '่'
- 7: 2, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 25: { # 'ป'
- 5: 2, # 'ก'
- 30: 0, # 'ข'
- 24: 1, # 'ค'
- 8: 0, # 'ง'
- 26: 1, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 1, # 'ซ'
- 47: 0, # 'ญ'
- 58: 1, # 'ฎ'
- 57: 3, # 'ฏ'
- 49: 1, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 1, # 'ด'
- 19: 1, # 'ต'
- 44: 1, # 'ถ'
- 14: 1, # 'ท'
- 48: 0, # 'ธ'
- 3: 2, # 'น'
- 17: 0, # 'บ'
- 25: 1, # 'ป'
- 39: 1, # 'ผ'
- 62: 1, # 'ฝ'
- 31: 1, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 1, # 'ม'
- 16: 0, # 'ย'
- 2: 3, # 'ร'
- 61: 0, # 'ฤ'
- 15: 3, # 'ล'
- 12: 1, # 'ว'
- 42: 0, # 'ศ'
- 46: 1, # 'ษ'
- 18: 2, # 'ส'
- 21: 1, # 'ห'
- 4: 2, # 'อ'
- 63: 0, # 'ฯ'
- 22: 1, # 'ะ'
- 10: 3, # 'ั'
- 1: 1, # 'า'
- 36: 0, # 'ำ'
- 23: 2, # 'ิ'
- 13: 3, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 1, # 'ุ'
- 35: 0, # 'ู'
- 11: 1, # 'เ'
- 28: 2, # 'แ'
- 41: 0, # 'โ'
- 29: 1, # 'ใ'
- 33: 2, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 3, # '็'
- 6: 1, # '่'
- 7: 2, # '้'
- 38: 1, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 39: { # 'ผ'
- 5: 1, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 1, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 0, # 'ด'
- 19: 0, # 'ต'
- 44: 0, # 'ถ'
- 14: 0, # 'ท'
- 48: 0, # 'ธ'
- 3: 2, # 'น'
- 17: 0, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 1, # 'ม'
- 16: 2, # 'ย'
- 2: 0, # 'ร'
- 61: 0, # 'ฤ'
- 15: 3, # 'ล'
- 12: 0, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 1, # 'ส'
- 21: 0, # 'ห'
- 4: 0, # 'อ'
- 63: 0, # 'ฯ'
- 22: 1, # 'ะ'
- 10: 1, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 2, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 1, # 'ื'
- 32: 0, # 'ุ'
- 35: 3, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 3, # '่'
- 7: 1, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 62: { # 'ฝ'
- 5: 0, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 0, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 0, # 'ด'
- 19: 0, # 'ต'
- 44: 0, # 'ถ'
- 14: 0, # 'ท'
- 48: 0, # 'ธ'
- 3: 1, # 'น'
- 17: 0, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 0, # 'ม'
- 16: 0, # 'ย'
- 2: 1, # 'ร'
- 61: 0, # 'ฤ'
- 15: 0, # 'ล'
- 12: 0, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 0, # 'ส'
- 21: 0, # 'ห'
- 4: 0, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 1, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 1, # 'ี'
- 40: 2, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 2, # '่'
- 7: 1, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 31: { # 'พ'
- 5: 1, # 'ก'
- 30: 1, # 'ข'
- 24: 1, # 'ค'
- 8: 1, # 'ง'
- 26: 1, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 1, # 'ณ'
- 20: 1, # 'ด'
- 19: 1, # 'ต'
- 44: 0, # 'ถ'
- 14: 2, # 'ท'
- 48: 1, # 'ธ'
- 3: 3, # 'น'
- 17: 2, # 'บ'
- 25: 0, # 'ป'
- 39: 1, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 1, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 1, # 'ม'
- 16: 2, # 'ย'
- 2: 3, # 'ร'
- 61: 2, # 'ฤ'
- 15: 2, # 'ล'
- 12: 2, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 1, # 'ส'
- 21: 1, # 'ห'
- 4: 2, # 'อ'
- 63: 1, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 3, # 'ั'
- 1: 3, # 'า'
- 36: 0, # 'ำ'
- 23: 3, # 'ิ'
- 13: 2, # 'ี'
- 40: 1, # 'ึ'
- 27: 3, # 'ื'
- 32: 1, # 'ุ'
- 35: 2, # 'ู'
- 11: 1, # 'เ'
- 28: 1, # 'แ'
- 41: 0, # 'โ'
- 29: 1, # 'ใ'
- 33: 1, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 1, # '็'
- 6: 0, # '่'
- 7: 1, # '้'
- 38: 3, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 54: { # 'ฟ'
- 5: 0, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 0, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 1, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 0, # 'ด'
- 19: 1, # 'ต'
- 44: 0, # 'ถ'
- 14: 1, # 'ท'
- 48: 0, # 'ธ'
- 3: 0, # 'น'
- 17: 0, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 2, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 0, # 'ม'
- 16: 0, # 'ย'
- 2: 1, # 'ร'
- 61: 0, # 'ฤ'
- 15: 2, # 'ล'
- 12: 0, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 1, # 'ส'
- 21: 0, # 'ห'
- 4: 1, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 2, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 1, # 'ิ'
- 13: 1, # 'ี'
- 40: 0, # 'ึ'
- 27: 1, # 'ื'
- 32: 1, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 1, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 2, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 45: { # 'ภ'
- 5: 0, # 'ก'
- 30: 0, # 'ข'
- 24: 1, # 'ค'
- 8: 0, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 0, # 'ด'
- 19: 0, # 'ต'
- 44: 0, # 'ถ'
- 14: 3, # 'ท'
- 48: 0, # 'ธ'
- 3: 0, # 'น'
- 17: 0, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 1, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 0, # 'ม'
- 16: 0, # 'ย'
- 2: 1, # 'ร'
- 61: 0, # 'ฤ'
- 15: 0, # 'ล'
- 12: 0, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 0, # 'ส'
- 21: 0, # 'ห'
- 4: 0, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 3, # 'ั'
- 1: 3, # 'า'
- 36: 0, # 'ำ'
- 23: 1, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 2, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 1, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 9: { # 'ม'
- 5: 2, # 'ก'
- 30: 2, # 'ข'
- 24: 2, # 'ค'
- 8: 2, # 'ง'
- 26: 2, # 'จ'
- 52: 0, # 'ฉ'
- 34: 1, # 'ช'
- 51: 1, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 1, # 'ณ'
- 20: 2, # 'ด'
- 19: 2, # 'ต'
- 44: 1, # 'ถ'
- 14: 2, # 'ท'
- 48: 1, # 'ธ'
- 3: 3, # 'น'
- 17: 2, # 'บ'
- 25: 2, # 'ป'
- 39: 1, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 3, # 'พ'
- 54: 0, # 'ฟ'
- 45: 1, # 'ภ'
- 9: 2, # 'ม'
- 16: 1, # 'ย'
- 2: 2, # 'ร'
- 61: 2, # 'ฤ'
- 15: 2, # 'ล'
- 12: 2, # 'ว'
- 42: 1, # 'ศ'
- 46: 1, # 'ษ'
- 18: 3, # 'ส'
- 21: 3, # 'ห'
- 4: 3, # 'อ'
- 63: 0, # 'ฯ'
- 22: 1, # 'ะ'
- 10: 3, # 'ั'
- 1: 3, # 'า'
- 36: 0, # 'ำ'
- 23: 3, # 'ิ'
- 13: 3, # 'ี'
- 40: 0, # 'ึ'
- 27: 3, # 'ื'
- 32: 3, # 'ุ'
- 35: 3, # 'ู'
- 11: 2, # 'เ'
- 28: 2, # 'แ'
- 41: 2, # 'โ'
- 29: 2, # 'ใ'
- 33: 2, # 'ไ'
- 50: 1, # 'ๆ'
- 37: 1, # '็'
- 6: 3, # '่'
- 7: 2, # '้'
- 38: 1, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 16: { # 'ย'
- 5: 3, # 'ก'
- 30: 1, # 'ข'
- 24: 2, # 'ค'
- 8: 3, # 'ง'
- 26: 2, # 'จ'
- 52: 0, # 'ฉ'
- 34: 2, # 'ช'
- 51: 0, # 'ซ'
- 47: 2, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 2, # 'ด'
- 19: 2, # 'ต'
- 44: 1, # 'ถ'
- 14: 2, # 'ท'
- 48: 1, # 'ธ'
- 3: 3, # 'น'
- 17: 3, # 'บ'
- 25: 1, # 'ป'
- 39: 1, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 1, # 'พ'
- 54: 0, # 'ฟ'
- 45: 1, # 'ภ'
- 9: 2, # 'ม'
- 16: 0, # 'ย'
- 2: 2, # 'ร'
- 61: 0, # 'ฤ'
- 15: 1, # 'ล'
- 12: 3, # 'ว'
- 42: 1, # 'ศ'
- 46: 0, # 'ษ'
- 18: 2, # 'ส'
- 21: 1, # 'ห'
- 4: 2, # 'อ'
- 63: 0, # 'ฯ'
- 22: 2, # 'ะ'
- 10: 3, # 'ั'
- 1: 3, # 'า'
- 36: 0, # 'ำ'
- 23: 2, # 'ิ'
- 13: 3, # 'ี'
- 40: 1, # 'ึ'
- 27: 2, # 'ื'
- 32: 2, # 'ุ'
- 35: 3, # 'ู'
- 11: 2, # 'เ'
- 28: 1, # 'แ'
- 41: 1, # 'โ'
- 29: 2, # 'ใ'
- 33: 2, # 'ไ'
- 50: 2, # 'ๆ'
- 37: 1, # '็'
- 6: 3, # '่'
- 7: 2, # '้'
- 38: 3, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 2: { # 'ร'
- 5: 3, # 'ก'
- 30: 2, # 'ข'
- 24: 2, # 'ค'
- 8: 3, # 'ง'
- 26: 2, # 'จ'
- 52: 0, # 'ฉ'
- 34: 2, # 'ช'
- 51: 1, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 3, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 3, # 'ณ'
- 20: 2, # 'ด'
- 19: 2, # 'ต'
- 44: 3, # 'ถ'
- 14: 3, # 'ท'
- 48: 1, # 'ธ'
- 3: 2, # 'น'
- 17: 2, # 'บ'
- 25: 3, # 'ป'
- 39: 2, # 'ผ'
- 62: 1, # 'ฝ'
- 31: 2, # 'พ'
- 54: 1, # 'ฟ'
- 45: 1, # 'ภ'
- 9: 3, # 'ม'
- 16: 2, # 'ย'
- 2: 3, # 'ร'
- 61: 0, # 'ฤ'
- 15: 2, # 'ล'
- 12: 3, # 'ว'
- 42: 2, # 'ศ'
- 46: 2, # 'ษ'
- 18: 2, # 'ส'
- 21: 2, # 'ห'
- 4: 3, # 'อ'
- 63: 1, # 'ฯ'
- 22: 3, # 'ะ'
- 10: 3, # 'ั'
- 1: 3, # 'า'
- 36: 0, # 'ำ'
- 23: 3, # 'ิ'
- 13: 3, # 'ี'
- 40: 2, # 'ึ'
- 27: 3, # 'ื'
- 32: 3, # 'ุ'
- 35: 3, # 'ู'
- 11: 3, # 'เ'
- 28: 3, # 'แ'
- 41: 1, # 'โ'
- 29: 2, # 'ใ'
- 33: 1, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 3, # '็'
- 6: 3, # '่'
- 7: 3, # '้'
- 38: 3, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 61: { # 'ฤ'
- 5: 0, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 0, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 0, # 'ด'
- 19: 2, # 'ต'
- 44: 0, # 'ถ'
- 14: 2, # 'ท'
- 48: 0, # 'ธ'
- 3: 0, # 'น'
- 17: 0, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 1, # 'ม'
- 16: 0, # 'ย'
- 2: 0, # 'ร'
- 61: 0, # 'ฤ'
- 15: 0, # 'ล'
- 12: 0, # 'ว'
- 42: 0, # 'ศ'
- 46: 2, # 'ษ'
- 18: 0, # 'ส'
- 21: 0, # 'ห'
- 4: 0, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 15: { # 'ล'
- 5: 2, # 'ก'
- 30: 3, # 'ข'
- 24: 1, # 'ค'
- 8: 3, # 'ง'
- 26: 1, # 'จ'
- 52: 0, # 'ฉ'
- 34: 1, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 2, # 'ด'
- 19: 2, # 'ต'
- 44: 1, # 'ถ'
- 14: 2, # 'ท'
- 48: 0, # 'ธ'
- 3: 1, # 'น'
- 17: 2, # 'บ'
- 25: 2, # 'ป'
- 39: 1, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 1, # 'ภ'
- 9: 1, # 'ม'
- 16: 3, # 'ย'
- 2: 1, # 'ร'
- 61: 0, # 'ฤ'
- 15: 1, # 'ล'
- 12: 1, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 2, # 'ส'
- 21: 1, # 'ห'
- 4: 3, # 'อ'
- 63: 2, # 'ฯ'
- 22: 3, # 'ะ'
- 10: 3, # 'ั'
- 1: 3, # 'า'
- 36: 2, # 'ำ'
- 23: 3, # 'ิ'
- 13: 3, # 'ี'
- 40: 2, # 'ึ'
- 27: 3, # 'ื'
- 32: 2, # 'ุ'
- 35: 3, # 'ู'
- 11: 2, # 'เ'
- 28: 1, # 'แ'
- 41: 1, # 'โ'
- 29: 2, # 'ใ'
- 33: 1, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 2, # '็'
- 6: 3, # '่'
- 7: 3, # '้'
- 38: 2, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 12: { # 'ว'
- 5: 3, # 'ก'
- 30: 2, # 'ข'
- 24: 1, # 'ค'
- 8: 3, # 'ง'
- 26: 2, # 'จ'
- 52: 0, # 'ฉ'
- 34: 1, # 'ช'
- 51: 1, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 1, # 'ณ'
- 20: 2, # 'ด'
- 19: 1, # 'ต'
- 44: 1, # 'ถ'
- 14: 1, # 'ท'
- 48: 0, # 'ธ'
- 3: 3, # 'น'
- 17: 2, # 'บ'
- 25: 1, # 'ป'
- 39: 1, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 1, # 'พ'
- 54: 1, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 3, # 'ม'
- 16: 3, # 'ย'
- 2: 3, # 'ร'
- 61: 0, # 'ฤ'
- 15: 3, # 'ล'
- 12: 1, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 2, # 'ส'
- 21: 2, # 'ห'
- 4: 2, # 'อ'
- 63: 0, # 'ฯ'
- 22: 2, # 'ะ'
- 10: 3, # 'ั'
- 1: 3, # 'า'
- 36: 0, # 'ำ'
- 23: 3, # 'ิ'
- 13: 2, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 2, # 'ุ'
- 35: 0, # 'ู'
- 11: 3, # 'เ'
- 28: 2, # 'แ'
- 41: 1, # 'โ'
- 29: 1, # 'ใ'
- 33: 2, # 'ไ'
- 50: 1, # 'ๆ'
- 37: 0, # '็'
- 6: 3, # '่'
- 7: 3, # '้'
- 38: 1, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 42: { # 'ศ'
- 5: 1, # 'ก'
- 30: 0, # 'ข'
- 24: 1, # 'ค'
- 8: 0, # 'ง'
- 26: 1, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 1, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 0, # 'ด'
- 19: 1, # 'ต'
- 44: 0, # 'ถ'
- 14: 1, # 'ท'
- 48: 0, # 'ธ'
- 3: 2, # 'น'
- 17: 0, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 0, # 'ม'
- 16: 0, # 'ย'
- 2: 2, # 'ร'
- 61: 0, # 'ฤ'
- 15: 0, # 'ล'
- 12: 2, # 'ว'
- 42: 1, # 'ศ'
- 46: 2, # 'ษ'
- 18: 1, # 'ส'
- 21: 0, # 'ห'
- 4: 0, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 2, # 'ั'
- 1: 3, # 'า'
- 36: 0, # 'ำ'
- 23: 2, # 'ิ'
- 13: 0, # 'ี'
- 40: 3, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 2, # 'ู'
- 11: 0, # 'เ'
- 28: 1, # 'แ'
- 41: 0, # 'โ'
- 29: 1, # 'ใ'
- 33: 1, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 1, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 46: { # 'ษ'
- 5: 0, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 0, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 2, # 'ฎ'
- 57: 1, # 'ฏ'
- 49: 2, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 3, # 'ณ'
- 20: 0, # 'ด'
- 19: 1, # 'ต'
- 44: 0, # 'ถ'
- 14: 1, # 'ท'
- 48: 0, # 'ธ'
- 3: 0, # 'น'
- 17: 0, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 1, # 'ภ'
- 9: 1, # 'ม'
- 16: 2, # 'ย'
- 2: 2, # 'ร'
- 61: 0, # 'ฤ'
- 15: 0, # 'ล'
- 12: 0, # 'ว'
- 42: 1, # 'ศ'
- 46: 0, # 'ษ'
- 18: 0, # 'ส'
- 21: 0, # 'ห'
- 4: 0, # 'อ'
- 63: 0, # 'ฯ'
- 22: 2, # 'ะ'
- 10: 2, # 'ั'
- 1: 3, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 1, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 1, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 2, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 18: { # 'ส'
- 5: 2, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 2, # 'ง'
- 26: 1, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 3, # 'ด'
- 19: 3, # 'ต'
- 44: 3, # 'ถ'
- 14: 0, # 'ท'
- 48: 0, # 'ธ'
- 3: 3, # 'น'
- 17: 2, # 'บ'
- 25: 1, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 2, # 'ภ'
- 9: 3, # 'ม'
- 16: 1, # 'ย'
- 2: 3, # 'ร'
- 61: 0, # 'ฤ'
- 15: 1, # 'ล'
- 12: 2, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 0, # 'ส'
- 21: 2, # 'ห'
- 4: 3, # 'อ'
- 63: 0, # 'ฯ'
- 22: 2, # 'ะ'
- 10: 3, # 'ั'
- 1: 3, # 'า'
- 36: 3, # 'ำ'
- 23: 3, # 'ิ'
- 13: 3, # 'ี'
- 40: 2, # 'ึ'
- 27: 3, # 'ื'
- 32: 3, # 'ุ'
- 35: 3, # 'ู'
- 11: 2, # 'เ'
- 28: 0, # 'แ'
- 41: 1, # 'โ'
- 29: 0, # 'ใ'
- 33: 1, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 3, # '่'
- 7: 1, # '้'
- 38: 2, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 21: { # 'ห'
- 5: 3, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 1, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 2, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 1, # 'ด'
- 19: 3, # 'ต'
- 44: 0, # 'ถ'
- 14: 0, # 'ท'
- 48: 0, # 'ธ'
- 3: 3, # 'น'
- 17: 0, # 'บ'
- 25: 1, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 1, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 3, # 'ม'
- 16: 2, # 'ย'
- 2: 3, # 'ร'
- 61: 0, # 'ฤ'
- 15: 3, # 'ล'
- 12: 2, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 0, # 'ส'
- 21: 0, # 'ห'
- 4: 3, # 'อ'
- 63: 0, # 'ฯ'
- 22: 1, # 'ะ'
- 10: 3, # 'ั'
- 1: 3, # 'า'
- 36: 0, # 'ำ'
- 23: 1, # 'ิ'
- 13: 1, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 1, # 'ุ'
- 35: 1, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 3, # '็'
- 6: 3, # '่'
- 7: 3, # '้'
- 38: 2, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 4: { # 'อ'
- 5: 3, # 'ก'
- 30: 1, # 'ข'
- 24: 2, # 'ค'
- 8: 3, # 'ง'
- 26: 1, # 'จ'
- 52: 0, # 'ฉ'
- 34: 1, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 3, # 'ด'
- 19: 2, # 'ต'
- 44: 1, # 'ถ'
- 14: 2, # 'ท'
- 48: 1, # 'ธ'
- 3: 3, # 'น'
- 17: 3, # 'บ'
- 25: 1, # 'ป'
- 39: 1, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 1, # 'พ'
- 54: 1, # 'ฟ'
- 45: 1, # 'ภ'
- 9: 3, # 'ม'
- 16: 3, # 'ย'
- 2: 3, # 'ร'
- 61: 0, # 'ฤ'
- 15: 2, # 'ล'
- 12: 2, # 'ว'
- 42: 1, # 'ศ'
- 46: 0, # 'ษ'
- 18: 2, # 'ส'
- 21: 2, # 'ห'
- 4: 3, # 'อ'
- 63: 0, # 'ฯ'
- 22: 2, # 'ะ'
- 10: 3, # 'ั'
- 1: 3, # 'า'
- 36: 2, # 'ำ'
- 23: 2, # 'ิ'
- 13: 3, # 'ี'
- 40: 0, # 'ึ'
- 27: 3, # 'ื'
- 32: 3, # 'ุ'
- 35: 0, # 'ู'
- 11: 3, # 'เ'
- 28: 1, # 'แ'
- 41: 1, # 'โ'
- 29: 2, # 'ใ'
- 33: 2, # 'ไ'
- 50: 1, # 'ๆ'
- 37: 1, # '็'
- 6: 2, # '่'
- 7: 2, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 63: { # 'ฯ'
- 5: 0, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 0, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 0, # 'ด'
- 19: 0, # 'ต'
- 44: 0, # 'ถ'
- 14: 0, # 'ท'
- 48: 0, # 'ธ'
- 3: 0, # 'น'
- 17: 0, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 0, # 'ม'
- 16: 0, # 'ย'
- 2: 0, # 'ร'
- 61: 0, # 'ฤ'
- 15: 2, # 'ล'
- 12: 0, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 0, # 'ส'
- 21: 0, # 'ห'
- 4: 0, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 22: { # 'ะ'
- 5: 3, # 'ก'
- 30: 1, # 'ข'
- 24: 2, # 'ค'
- 8: 1, # 'ง'
- 26: 2, # 'จ'
- 52: 0, # 'ฉ'
- 34: 3, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 3, # 'ด'
- 19: 3, # 'ต'
- 44: 1, # 'ถ'
- 14: 3, # 'ท'
- 48: 1, # 'ธ'
- 3: 2, # 'น'
- 17: 3, # 'บ'
- 25: 2, # 'ป'
- 39: 1, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 2, # 'พ'
- 54: 0, # 'ฟ'
- 45: 1, # 'ภ'
- 9: 3, # 'ม'
- 16: 2, # 'ย'
- 2: 2, # 'ร'
- 61: 0, # 'ฤ'
- 15: 2, # 'ล'
- 12: 2, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 3, # 'ส'
- 21: 3, # 'ห'
- 4: 2, # 'อ'
- 63: 1, # 'ฯ'
- 22: 1, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 3, # 'เ'
- 28: 2, # 'แ'
- 41: 1, # 'โ'
- 29: 2, # 'ใ'
- 33: 2, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 10: { # 'ั'
- 5: 3, # 'ก'
- 30: 0, # 'ข'
- 24: 1, # 'ค'
- 8: 3, # 'ง'
- 26: 3, # 'จ'
- 52: 0, # 'ฉ'
- 34: 1, # 'ช'
- 51: 0, # 'ซ'
- 47: 3, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 2, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 3, # 'ฒ'
- 43: 3, # 'ณ'
- 20: 3, # 'ด'
- 19: 3, # 'ต'
- 44: 0, # 'ถ'
- 14: 2, # 'ท'
- 48: 0, # 'ธ'
- 3: 3, # 'น'
- 17: 3, # 'บ'
- 25: 1, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 2, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 3, # 'ม'
- 16: 3, # 'ย'
- 2: 0, # 'ร'
- 61: 0, # 'ฤ'
- 15: 2, # 'ล'
- 12: 3, # 'ว'
- 42: 2, # 'ศ'
- 46: 0, # 'ษ'
- 18: 3, # 'ส'
- 21: 0, # 'ห'
- 4: 0, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 3, # '่'
- 7: 3, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 1: { # 'า'
- 5: 3, # 'ก'
- 30: 2, # 'ข'
- 24: 3, # 'ค'
- 8: 3, # 'ง'
- 26: 3, # 'จ'
- 52: 0, # 'ฉ'
- 34: 3, # 'ช'
- 51: 1, # 'ซ'
- 47: 2, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 3, # 'ณ'
- 20: 3, # 'ด'
- 19: 3, # 'ต'
- 44: 1, # 'ถ'
- 14: 3, # 'ท'
- 48: 2, # 'ธ'
- 3: 3, # 'น'
- 17: 3, # 'บ'
- 25: 2, # 'ป'
- 39: 1, # 'ผ'
- 62: 1, # 'ฝ'
- 31: 3, # 'พ'
- 54: 1, # 'ฟ'
- 45: 1, # 'ภ'
- 9: 3, # 'ม'
- 16: 3, # 'ย'
- 2: 3, # 'ร'
- 61: 0, # 'ฤ'
- 15: 3, # 'ล'
- 12: 3, # 'ว'
- 42: 2, # 'ศ'
- 46: 3, # 'ษ'
- 18: 3, # 'ส'
- 21: 3, # 'ห'
- 4: 2, # 'อ'
- 63: 1, # 'ฯ'
- 22: 3, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 3, # 'เ'
- 28: 2, # 'แ'
- 41: 1, # 'โ'
- 29: 2, # 'ใ'
- 33: 2, # 'ไ'
- 50: 1, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 36: { # 'ำ'
- 5: 2, # 'ก'
- 30: 1, # 'ข'
- 24: 3, # 'ค'
- 8: 2, # 'ง'
- 26: 1, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 1, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 1, # 'ด'
- 19: 1, # 'ต'
- 44: 1, # 'ถ'
- 14: 1, # 'ท'
- 48: 0, # 'ธ'
- 3: 3, # 'น'
- 17: 1, # 'บ'
- 25: 1, # 'ป'
- 39: 1, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 1, # 'พ'
- 54: 0, # 'ฟ'
- 45: 1, # 'ภ'
- 9: 1, # 'ม'
- 16: 0, # 'ย'
- 2: 2, # 'ร'
- 61: 0, # 'ฤ'
- 15: 2, # 'ล'
- 12: 1, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 1, # 'ส'
- 21: 3, # 'ห'
- 4: 1, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 3, # 'เ'
- 28: 2, # 'แ'
- 41: 1, # 'โ'
- 29: 2, # 'ใ'
- 33: 2, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 23: { # 'ิ'
- 5: 3, # 'ก'
- 30: 1, # 'ข'
- 24: 2, # 'ค'
- 8: 3, # 'ง'
- 26: 3, # 'จ'
- 52: 0, # 'ฉ'
- 34: 3, # 'ช'
- 51: 0, # 'ซ'
- 47: 2, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 3, # 'ด'
- 19: 3, # 'ต'
- 44: 1, # 'ถ'
- 14: 3, # 'ท'
- 48: 3, # 'ธ'
- 3: 3, # 'น'
- 17: 3, # 'บ'
- 25: 2, # 'ป'
- 39: 2, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 3, # 'พ'
- 54: 1, # 'ฟ'
- 45: 2, # 'ภ'
- 9: 3, # 'ม'
- 16: 2, # 'ย'
- 2: 2, # 'ร'
- 61: 0, # 'ฤ'
- 15: 2, # 'ล'
- 12: 3, # 'ว'
- 42: 3, # 'ศ'
- 46: 2, # 'ษ'
- 18: 2, # 'ส'
- 21: 3, # 'ห'
- 4: 1, # 'อ'
- 63: 1, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 3, # 'เ'
- 28: 1, # 'แ'
- 41: 1, # 'โ'
- 29: 1, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 3, # '่'
- 7: 2, # '้'
- 38: 2, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 13: { # 'ี'
- 5: 3, # 'ก'
- 30: 2, # 'ข'
- 24: 2, # 'ค'
- 8: 0, # 'ง'
- 26: 1, # 'จ'
- 52: 0, # 'ฉ'
- 34: 1, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 2, # 'ด'
- 19: 1, # 'ต'
- 44: 0, # 'ถ'
- 14: 2, # 'ท'
- 48: 0, # 'ธ'
- 3: 1, # 'น'
- 17: 2, # 'บ'
- 25: 2, # 'ป'
- 39: 1, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 2, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 2, # 'ม'
- 16: 3, # 'ย'
- 2: 2, # 'ร'
- 61: 0, # 'ฤ'
- 15: 1, # 'ล'
- 12: 2, # 'ว'
- 42: 1, # 'ศ'
- 46: 0, # 'ษ'
- 18: 2, # 'ส'
- 21: 1, # 'ห'
- 4: 2, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 2, # 'เ'
- 28: 2, # 'แ'
- 41: 1, # 'โ'
- 29: 1, # 'ใ'
- 33: 1, # 'ไ'
- 50: 1, # 'ๆ'
- 37: 0, # '็'
- 6: 3, # '่'
- 7: 3, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 40: { # 'ึ'
- 5: 3, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 3, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 1, # 'ด'
- 19: 0, # 'ต'
- 44: 0, # 'ถ'
- 14: 0, # 'ท'
- 48: 0, # 'ธ'
- 3: 0, # 'น'
- 17: 0, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 1, # 'ม'
- 16: 0, # 'ย'
- 2: 0, # 'ร'
- 61: 0, # 'ฤ'
- 15: 0, # 'ล'
- 12: 0, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 0, # 'ส'
- 21: 0, # 'ห'
- 4: 0, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 3, # '่'
- 7: 3, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 27: { # 'ื'
- 5: 0, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 0, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 1, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 1, # 'ด'
- 19: 0, # 'ต'
- 44: 0, # 'ถ'
- 14: 0, # 'ท'
- 48: 0, # 'ธ'
- 3: 2, # 'น'
- 17: 3, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 2, # 'ม'
- 16: 0, # 'ย'
- 2: 0, # 'ร'
- 61: 0, # 'ฤ'
- 15: 0, # 'ล'
- 12: 0, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 0, # 'ส'
- 21: 0, # 'ห'
- 4: 3, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 3, # '่'
- 7: 3, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 32: { # 'ุ'
- 5: 3, # 'ก'
- 30: 2, # 'ข'
- 24: 3, # 'ค'
- 8: 3, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 2, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 1, # 'ฒ'
- 43: 3, # 'ณ'
- 20: 3, # 'ด'
- 19: 3, # 'ต'
- 44: 1, # 'ถ'
- 14: 2, # 'ท'
- 48: 1, # 'ธ'
- 3: 2, # 'น'
- 17: 2, # 'บ'
- 25: 2, # 'ป'
- 39: 2, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 1, # 'พ'
- 54: 0, # 'ฟ'
- 45: 1, # 'ภ'
- 9: 3, # 'ม'
- 16: 1, # 'ย'
- 2: 2, # 'ร'
- 61: 0, # 'ฤ'
- 15: 2, # 'ล'
- 12: 1, # 'ว'
- 42: 1, # 'ศ'
- 46: 2, # 'ษ'
- 18: 1, # 'ส'
- 21: 1, # 'ห'
- 4: 1, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 1, # 'เ'
- 28: 0, # 'แ'
- 41: 1, # 'โ'
- 29: 0, # 'ใ'
- 33: 1, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 3, # '่'
- 7: 2, # '้'
- 38: 1, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 35: { # 'ู'
- 5: 3, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 2, # 'ง'
- 26: 1, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 2, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 1, # 'ณ'
- 20: 2, # 'ด'
- 19: 2, # 'ต'
- 44: 0, # 'ถ'
- 14: 1, # 'ท'
- 48: 0, # 'ธ'
- 3: 2, # 'น'
- 17: 0, # 'บ'
- 25: 3, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 2, # 'ม'
- 16: 0, # 'ย'
- 2: 1, # 'ร'
- 61: 0, # 'ฤ'
- 15: 3, # 'ล'
- 12: 1, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 0, # 'ส'
- 21: 0, # 'ห'
- 4: 0, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 1, # 'เ'
- 28: 1, # 'แ'
- 41: 1, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 3, # '่'
- 7: 3, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 11: { # 'เ'
- 5: 3, # 'ก'
- 30: 3, # 'ข'
- 24: 3, # 'ค'
- 8: 2, # 'ง'
- 26: 3, # 'จ'
- 52: 3, # 'ฉ'
- 34: 3, # 'ช'
- 51: 2, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 1, # 'ณ'
- 20: 3, # 'ด'
- 19: 3, # 'ต'
- 44: 1, # 'ถ'
- 14: 3, # 'ท'
- 48: 1, # 'ธ'
- 3: 3, # 'น'
- 17: 3, # 'บ'
- 25: 3, # 'ป'
- 39: 2, # 'ผ'
- 62: 1, # 'ฝ'
- 31: 3, # 'พ'
- 54: 1, # 'ฟ'
- 45: 3, # 'ภ'
- 9: 3, # 'ม'
- 16: 2, # 'ย'
- 2: 3, # 'ร'
- 61: 0, # 'ฤ'
- 15: 3, # 'ล'
- 12: 3, # 'ว'
- 42: 2, # 'ศ'
- 46: 0, # 'ษ'
- 18: 3, # 'ส'
- 21: 3, # 'ห'
- 4: 3, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 28: { # 'แ'
- 5: 3, # 'ก'
- 30: 2, # 'ข'
- 24: 2, # 'ค'
- 8: 1, # 'ง'
- 26: 2, # 'จ'
- 52: 0, # 'ฉ'
- 34: 1, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 2, # 'ด'
- 19: 3, # 'ต'
- 44: 2, # 'ถ'
- 14: 3, # 'ท'
- 48: 0, # 'ธ'
- 3: 3, # 'น'
- 17: 3, # 'บ'
- 25: 2, # 'ป'
- 39: 3, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 2, # 'พ'
- 54: 2, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 2, # 'ม'
- 16: 2, # 'ย'
- 2: 2, # 'ร'
- 61: 0, # 'ฤ'
- 15: 3, # 'ล'
- 12: 2, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 3, # 'ส'
- 21: 3, # 'ห'
- 4: 1, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 41: { # 'โ'
- 5: 2, # 'ก'
- 30: 1, # 'ข'
- 24: 2, # 'ค'
- 8: 0, # 'ง'
- 26: 1, # 'จ'
- 52: 1, # 'ฉ'
- 34: 1, # 'ช'
- 51: 1, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 3, # 'ด'
- 19: 2, # 'ต'
- 44: 0, # 'ถ'
- 14: 2, # 'ท'
- 48: 0, # 'ธ'
- 3: 3, # 'น'
- 17: 1, # 'บ'
- 25: 3, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 1, # 'พ'
- 54: 1, # 'ฟ'
- 45: 1, # 'ภ'
- 9: 1, # 'ม'
- 16: 2, # 'ย'
- 2: 2, # 'ร'
- 61: 0, # 'ฤ'
- 15: 3, # 'ล'
- 12: 0, # 'ว'
- 42: 1, # 'ศ'
- 46: 0, # 'ษ'
- 18: 2, # 'ส'
- 21: 0, # 'ห'
- 4: 2, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 29: { # 'ใ'
- 5: 2, # 'ก'
- 30: 0, # 'ข'
- 24: 1, # 'ค'
- 8: 0, # 'ง'
- 26: 3, # 'จ'
- 52: 0, # 'ฉ'
- 34: 3, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 3, # 'ด'
- 19: 1, # 'ต'
- 44: 0, # 'ถ'
- 14: 0, # 'ท'
- 48: 0, # 'ธ'
- 3: 3, # 'น'
- 17: 2, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 0, # 'ม'
- 16: 1, # 'ย'
- 2: 0, # 'ร'
- 61: 0, # 'ฤ'
- 15: 0, # 'ล'
- 12: 0, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 3, # 'ส'
- 21: 3, # 'ห'
- 4: 0, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 33: { # 'ไ'
- 5: 1, # 'ก'
- 30: 2, # 'ข'
- 24: 0, # 'ค'
- 8: 0, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 1, # 'ช'
- 51: 1, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 3, # 'ด'
- 19: 1, # 'ต'
- 44: 0, # 'ถ'
- 14: 3, # 'ท'
- 48: 0, # 'ธ'
- 3: 0, # 'น'
- 17: 1, # 'บ'
- 25: 3, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 2, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 3, # 'ม'
- 16: 0, # 'ย'
- 2: 3, # 'ร'
- 61: 0, # 'ฤ'
- 15: 1, # 'ล'
- 12: 3, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 1, # 'ส'
- 21: 2, # 'ห'
- 4: 0, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 50: { # 'ๆ'
- 5: 0, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 0, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 0, # 'ด'
- 19: 0, # 'ต'
- 44: 0, # 'ถ'
- 14: 0, # 'ท'
- 48: 0, # 'ธ'
- 3: 0, # 'น'
- 17: 0, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 0, # 'ม'
- 16: 0, # 'ย'
- 2: 0, # 'ร'
- 61: 0, # 'ฤ'
- 15: 0, # 'ล'
- 12: 0, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 0, # 'ส'
- 21: 0, # 'ห'
- 4: 0, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 37: { # '็'
- 5: 2, # 'ก'
- 30: 1, # 'ข'
- 24: 2, # 'ค'
- 8: 2, # 'ง'
- 26: 3, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 1, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 1, # 'ด'
- 19: 2, # 'ต'
- 44: 0, # 'ถ'
- 14: 1, # 'ท'
- 48: 0, # 'ธ'
- 3: 3, # 'น'
- 17: 3, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 2, # 'ม'
- 16: 1, # 'ย'
- 2: 0, # 'ร'
- 61: 0, # 'ฤ'
- 15: 0, # 'ล'
- 12: 2, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 1, # 'ส'
- 21: 0, # 'ห'
- 4: 1, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 1, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 1, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 6: { # '่'
- 5: 2, # 'ก'
- 30: 1, # 'ข'
- 24: 2, # 'ค'
- 8: 3, # 'ง'
- 26: 2, # 'จ'
- 52: 0, # 'ฉ'
- 34: 1, # 'ช'
- 51: 1, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 1, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 1, # 'ด'
- 19: 2, # 'ต'
- 44: 1, # 'ถ'
- 14: 2, # 'ท'
- 48: 1, # 'ธ'
- 3: 3, # 'น'
- 17: 1, # 'บ'
- 25: 2, # 'ป'
- 39: 2, # 'ผ'
- 62: 1, # 'ฝ'
- 31: 1, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 3, # 'ม'
- 16: 3, # 'ย'
- 2: 2, # 'ร'
- 61: 0, # 'ฤ'
- 15: 2, # 'ล'
- 12: 3, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 2, # 'ส'
- 21: 1, # 'ห'
- 4: 3, # 'อ'
- 63: 0, # 'ฯ'
- 22: 1, # 'ะ'
- 10: 0, # 'ั'
- 1: 3, # 'า'
- 36: 2, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 3, # 'เ'
- 28: 2, # 'แ'
- 41: 1, # 'โ'
- 29: 2, # 'ใ'
- 33: 2, # 'ไ'
- 50: 1, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 7: { # '้'
- 5: 2, # 'ก'
- 30: 1, # 'ข'
- 24: 2, # 'ค'
- 8: 3, # 'ง'
- 26: 2, # 'จ'
- 52: 0, # 'ฉ'
- 34: 1, # 'ช'
- 51: 1, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 1, # 'ด'
- 19: 2, # 'ต'
- 44: 1, # 'ถ'
- 14: 2, # 'ท'
- 48: 0, # 'ธ'
- 3: 3, # 'น'
- 17: 2, # 'บ'
- 25: 2, # 'ป'
- 39: 2, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 1, # 'พ'
- 54: 1, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 3, # 'ม'
- 16: 2, # 'ย'
- 2: 2, # 'ร'
- 61: 0, # 'ฤ'
- 15: 1, # 'ล'
- 12: 3, # 'ว'
- 42: 1, # 'ศ'
- 46: 0, # 'ษ'
- 18: 2, # 'ส'
- 21: 2, # 'ห'
- 4: 3, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 3, # 'า'
- 36: 2, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 2, # 'เ'
- 28: 2, # 'แ'
- 41: 1, # 'โ'
- 29: 2, # 'ใ'
- 33: 2, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 38: { # '์'
- 5: 2, # 'ก'
- 30: 1, # 'ข'
- 24: 1, # 'ค'
- 8: 0, # 'ง'
- 26: 1, # 'จ'
- 52: 0, # 'ฉ'
- 34: 1, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 2, # 'ด'
- 19: 1, # 'ต'
- 44: 1, # 'ถ'
- 14: 1, # 'ท'
- 48: 0, # 'ธ'
- 3: 1, # 'น'
- 17: 1, # 'บ'
- 25: 1, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 1, # 'พ'
- 54: 1, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 2, # 'ม'
- 16: 0, # 'ย'
- 2: 1, # 'ร'
- 61: 1, # 'ฤ'
- 15: 1, # 'ล'
- 12: 1, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 1, # 'ส'
- 21: 1, # 'ห'
- 4: 2, # 'อ'
- 63: 1, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 2, # 'เ'
- 28: 2, # 'แ'
- 41: 1, # 'โ'
- 29: 1, # 'ใ'
- 33: 1, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 0, # '๑'
- 59: 0, # '๒'
- 60: 0, # '๕'
- },
- 56: { # '๑'
- 5: 0, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 0, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 0, # 'ด'
- 19: 0, # 'ต'
- 44: 0, # 'ถ'
- 14: 0, # 'ท'
- 48: 0, # 'ธ'
- 3: 0, # 'น'
- 17: 0, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 0, # 'ม'
- 16: 0, # 'ย'
- 2: 0, # 'ร'
- 61: 0, # 'ฤ'
- 15: 0, # 'ล'
- 12: 0, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 0, # 'ส'
- 21: 0, # 'ห'
- 4: 0, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 2, # '๑'
- 59: 1, # '๒'
- 60: 1, # '๕'
- },
- 59: { # '๒'
- 5: 0, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 0, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 0, # 'ด'
- 19: 0, # 'ต'
- 44: 0, # 'ถ'
- 14: 0, # 'ท'
- 48: 0, # 'ธ'
- 3: 0, # 'น'
- 17: 0, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 0, # 'ม'
- 16: 0, # 'ย'
- 2: 0, # 'ร'
- 61: 0, # 'ฤ'
- 15: 0, # 'ล'
- 12: 0, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 0, # 'ส'
- 21: 0, # 'ห'
- 4: 0, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 1, # '๑'
- 59: 1, # '๒'
- 60: 3, # '๕'
- },
- 60: { # '๕'
- 5: 0, # 'ก'
- 30: 0, # 'ข'
- 24: 0, # 'ค'
- 8: 0, # 'ง'
- 26: 0, # 'จ'
- 52: 0, # 'ฉ'
- 34: 0, # 'ช'
- 51: 0, # 'ซ'
- 47: 0, # 'ญ'
- 58: 0, # 'ฎ'
- 57: 0, # 'ฏ'
- 49: 0, # 'ฐ'
- 53: 0, # 'ฑ'
- 55: 0, # 'ฒ'
- 43: 0, # 'ณ'
- 20: 0, # 'ด'
- 19: 0, # 'ต'
- 44: 0, # 'ถ'
- 14: 0, # 'ท'
- 48: 0, # 'ธ'
- 3: 0, # 'น'
- 17: 0, # 'บ'
- 25: 0, # 'ป'
- 39: 0, # 'ผ'
- 62: 0, # 'ฝ'
- 31: 0, # 'พ'
- 54: 0, # 'ฟ'
- 45: 0, # 'ภ'
- 9: 0, # 'ม'
- 16: 0, # 'ย'
- 2: 0, # 'ร'
- 61: 0, # 'ฤ'
- 15: 0, # 'ล'
- 12: 0, # 'ว'
- 42: 0, # 'ศ'
- 46: 0, # 'ษ'
- 18: 0, # 'ส'
- 21: 0, # 'ห'
- 4: 0, # 'อ'
- 63: 0, # 'ฯ'
- 22: 0, # 'ะ'
- 10: 0, # 'ั'
- 1: 0, # 'า'
- 36: 0, # 'ำ'
- 23: 0, # 'ิ'
- 13: 0, # 'ี'
- 40: 0, # 'ึ'
- 27: 0, # 'ื'
- 32: 0, # 'ุ'
- 35: 0, # 'ู'
- 11: 0, # 'เ'
- 28: 0, # 'แ'
- 41: 0, # 'โ'
- 29: 0, # 'ใ'
- 33: 0, # 'ไ'
- 50: 0, # 'ๆ'
- 37: 0, # '็'
- 6: 0, # '่'
- 7: 0, # '้'
- 38: 0, # '์'
- 56: 2, # '๑'
- 59: 1, # '๒'
- 60: 0, # '๕'
- },
-}
-
-# 255: Undefined characters that did not exist in training text
-# 254: Carriage/Return
-# 253: symbol (punctuation) that does not belong to word
-# 252: 0 - 9
-# 251: Control characters
-
-# Character Mapping Table(s):
-TIS_620_THAI_CHAR_TO_ORDER = {
- 0: 255, # '\x00'
- 1: 255, # '\x01'
- 2: 255, # '\x02'
- 3: 255, # '\x03'
- 4: 255, # '\x04'
- 5: 255, # '\x05'
- 6: 255, # '\x06'
- 7: 255, # '\x07'
- 8: 255, # '\x08'
- 9: 255, # '\t'
- 10: 254, # '\n'
- 11: 255, # '\x0b'
- 12: 255, # '\x0c'
- 13: 254, # '\r'
- 14: 255, # '\x0e'
- 15: 255, # '\x0f'
- 16: 255, # '\x10'
- 17: 255, # '\x11'
- 18: 255, # '\x12'
- 19: 255, # '\x13'
- 20: 255, # '\x14'
- 21: 255, # '\x15'
- 22: 255, # '\x16'
- 23: 255, # '\x17'
- 24: 255, # '\x18'
- 25: 255, # '\x19'
- 26: 255, # '\x1a'
- 27: 255, # '\x1b'
- 28: 255, # '\x1c'
- 29: 255, # '\x1d'
- 30: 255, # '\x1e'
- 31: 255, # '\x1f'
- 32: 253, # ' '
- 33: 253, # '!'
- 34: 253, # '"'
- 35: 253, # '#'
- 36: 253, # '$'
- 37: 253, # '%'
- 38: 253, # '&'
- 39: 253, # "'"
- 40: 253, # '('
- 41: 253, # ')'
- 42: 253, # '*'
- 43: 253, # '+'
- 44: 253, # ','
- 45: 253, # '-'
- 46: 253, # '.'
- 47: 253, # '/'
- 48: 252, # '0'
- 49: 252, # '1'
- 50: 252, # '2'
- 51: 252, # '3'
- 52: 252, # '4'
- 53: 252, # '5'
- 54: 252, # '6'
- 55: 252, # '7'
- 56: 252, # '8'
- 57: 252, # '9'
- 58: 253, # ':'
- 59: 253, # ';'
- 60: 253, # '<'
- 61: 253, # '='
- 62: 253, # '>'
- 63: 253, # '?'
- 64: 253, # '@'
- 65: 182, # 'A'
- 66: 106, # 'B'
- 67: 107, # 'C'
- 68: 100, # 'D'
- 69: 183, # 'E'
- 70: 184, # 'F'
- 71: 185, # 'G'
- 72: 101, # 'H'
- 73: 94, # 'I'
- 74: 186, # 'J'
- 75: 187, # 'K'
- 76: 108, # 'L'
- 77: 109, # 'M'
- 78: 110, # 'N'
- 79: 111, # 'O'
- 80: 188, # 'P'
- 81: 189, # 'Q'
- 82: 190, # 'R'
- 83: 89, # 'S'
- 84: 95, # 'T'
- 85: 112, # 'U'
- 86: 113, # 'V'
- 87: 191, # 'W'
- 88: 192, # 'X'
- 89: 193, # 'Y'
- 90: 194, # 'Z'
- 91: 253, # '['
- 92: 253, # '\\'
- 93: 253, # ']'
- 94: 253, # '^'
- 95: 253, # '_'
- 96: 253, # '`'
- 97: 64, # 'a'
- 98: 72, # 'b'
- 99: 73, # 'c'
- 100: 114, # 'd'
- 101: 74, # 'e'
- 102: 115, # 'f'
- 103: 116, # 'g'
- 104: 102, # 'h'
- 105: 81, # 'i'
- 106: 201, # 'j'
- 107: 117, # 'k'
- 108: 90, # 'l'
- 109: 103, # 'm'
- 110: 78, # 'n'
- 111: 82, # 'o'
- 112: 96, # 'p'
- 113: 202, # 'q'
- 114: 91, # 'r'
- 115: 79, # 's'
- 116: 84, # 't'
- 117: 104, # 'u'
- 118: 105, # 'v'
- 119: 97, # 'w'
- 120: 98, # 'x'
- 121: 92, # 'y'
- 122: 203, # 'z'
- 123: 253, # '{'
- 124: 253, # '|'
- 125: 253, # '}'
- 126: 253, # '~'
- 127: 253, # '\x7f'
- 128: 209, # '\x80'
- 129: 210, # '\x81'
- 130: 211, # '\x82'
- 131: 212, # '\x83'
- 132: 213, # '\x84'
- 133: 88, # '\x85'
- 134: 214, # '\x86'
- 135: 215, # '\x87'
- 136: 216, # '\x88'
- 137: 217, # '\x89'
- 138: 218, # '\x8a'
- 139: 219, # '\x8b'
- 140: 220, # '\x8c'
- 141: 118, # '\x8d'
- 142: 221, # '\x8e'
- 143: 222, # '\x8f'
- 144: 223, # '\x90'
- 145: 224, # '\x91'
- 146: 99, # '\x92'
- 147: 85, # '\x93'
- 148: 83, # '\x94'
- 149: 225, # '\x95'
- 150: 226, # '\x96'
- 151: 227, # '\x97'
- 152: 228, # '\x98'
- 153: 229, # '\x99'
- 154: 230, # '\x9a'
- 155: 231, # '\x9b'
- 156: 232, # '\x9c'
- 157: 233, # '\x9d'
- 158: 234, # '\x9e'
- 159: 235, # '\x9f'
- 160: 236, # None
- 161: 5, # 'ก'
- 162: 30, # 'ข'
- 163: 237, # 'ฃ'
- 164: 24, # 'ค'
- 165: 238, # 'ฅ'
- 166: 75, # 'ฆ'
- 167: 8, # 'ง'
- 168: 26, # 'จ'
- 169: 52, # 'ฉ'
- 170: 34, # 'ช'
- 171: 51, # 'ซ'
- 172: 119, # 'ฌ'
- 173: 47, # 'ญ'
- 174: 58, # 'ฎ'
- 175: 57, # 'ฏ'
- 176: 49, # 'ฐ'
- 177: 53, # 'ฑ'
- 178: 55, # 'ฒ'
- 179: 43, # 'ณ'
- 180: 20, # 'ด'
- 181: 19, # 'ต'
- 182: 44, # 'ถ'
- 183: 14, # 'ท'
- 184: 48, # 'ธ'
- 185: 3, # 'น'
- 186: 17, # 'บ'
- 187: 25, # 'ป'
- 188: 39, # 'ผ'
- 189: 62, # 'ฝ'
- 190: 31, # 'พ'
- 191: 54, # 'ฟ'
- 192: 45, # 'ภ'
- 193: 9, # 'ม'
- 194: 16, # 'ย'
- 195: 2, # 'ร'
- 196: 61, # 'ฤ'
- 197: 15, # 'ล'
- 198: 239, # 'ฦ'
- 199: 12, # 'ว'
- 200: 42, # 'ศ'
- 201: 46, # 'ษ'
- 202: 18, # 'ส'
- 203: 21, # 'ห'
- 204: 76, # 'ฬ'
- 205: 4, # 'อ'
- 206: 66, # 'ฮ'
- 207: 63, # 'ฯ'
- 208: 22, # 'ะ'
- 209: 10, # 'ั'
- 210: 1, # 'า'
- 211: 36, # 'ำ'
- 212: 23, # 'ิ'
- 213: 13, # 'ี'
- 214: 40, # 'ึ'
- 215: 27, # 'ื'
- 216: 32, # 'ุ'
- 217: 35, # 'ู'
- 218: 86, # 'ฺ'
- 219: 240, # None
- 220: 241, # None
- 221: 242, # None
- 222: 243, # None
- 223: 244, # '฿'
- 224: 11, # 'เ'
- 225: 28, # 'แ'
- 226: 41, # 'โ'
- 227: 29, # 'ใ'
- 228: 33, # 'ไ'
- 229: 245, # 'ๅ'
- 230: 50, # 'ๆ'
- 231: 37, # '็'
- 232: 6, # '่'
- 233: 7, # '้'
- 234: 67, # '๊'
- 235: 77, # '๋'
- 236: 38, # '์'
- 237: 93, # 'ํ'
- 238: 246, # '๎'
- 239: 247, # '๏'
- 240: 68, # '๐'
- 241: 56, # '๑'
- 242: 59, # '๒'
- 243: 65, # '๓'
- 244: 69, # '๔'
- 245: 60, # '๕'
- 246: 70, # '๖'
- 247: 80, # '๗'
- 248: 71, # '๘'
- 249: 87, # '๙'
- 250: 248, # '๚'
- 251: 249, # '๛'
- 252: 250, # None
- 253: 251, # None
- 254: 252, # None
- 255: 253, # None
-}
-
-TIS_620_THAI_MODEL = SingleByteCharSetModel(
- charset_name="TIS-620",
- language="Thai",
- char_to_order_map=TIS_620_THAI_CHAR_TO_ORDER,
- language_model=THAI_LANG_MODEL,
- typical_positive_ratio=0.926386,
- keep_ascii_letters=False,
- alphabet="กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำิีึืฺุู฿เแโใไๅๆ็่้๊๋์ํ๎๏๐๑๒๓๔๕๖๗๘๙๚๛",
-)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/langturkishmodel.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/langturkishmodel.py
deleted file mode 100644
index 64c9433..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/langturkishmodel.py
+++ /dev/null
@@ -1,4380 +0,0 @@
-from chardet.sbcharsetprober import SingleByteCharSetModel
-
-# 3: Positive
-# 2: Likely
-# 1: Unlikely
-# 0: Negative
-
-TURKISH_LANG_MODEL = {
- 23: { # 'A'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 0, # 'K'
- 49: 0, # 'L'
- 20: 0, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 0, # 'b'
- 28: 0, # 'c'
- 12: 2, # 'd'
- 2: 3, # 'e'
- 18: 0, # 'f'
- 27: 1, # 'g'
- 25: 1, # 'h'
- 3: 1, # 'i'
- 24: 0, # 'j'
- 10: 2, # 'k'
- 5: 1, # 'l'
- 13: 1, # 'm'
- 4: 1, # 'n'
- 15: 0, # 'o'
- 26: 0, # 'p'
- 7: 1, # 'r'
- 8: 1, # 's'
- 9: 1, # 't'
- 14: 1, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 3, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 1, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 0, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 0, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 37: { # 'B'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 2, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 2, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 1, # 'K'
- 49: 0, # 'L'
- 20: 0, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 1, # 'P'
- 44: 0, # 'R'
- 35: 1, # 'S'
- 31: 0, # 'T'
- 51: 0, # 'U'
- 38: 1, # 'V'
- 62: 0, # 'W'
- 43: 1, # 'Y'
- 56: 0, # 'Z'
- 1: 2, # 'a'
- 21: 0, # 'b'
- 28: 2, # 'c'
- 12: 0, # 'd'
- 2: 3, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 0, # 'i'
- 24: 0, # 'j'
- 10: 0, # 'k'
- 5: 0, # 'l'
- 13: 1, # 'm'
- 4: 1, # 'n'
- 15: 0, # 'o'
- 26: 0, # 'p'
- 7: 0, # 'r'
- 8: 0, # 's'
- 9: 0, # 't'
- 14: 2, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 0, # 'y'
- 22: 1, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 1, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 1, # 'ö'
- 17: 0, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 0, # 'ı'
- 40: 1, # 'Ş'
- 19: 1, # 'ş'
- },
- 47: { # 'C'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 1, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 0, # 'K'
- 49: 1, # 'L'
- 20: 0, # 'M'
- 46: 1, # 'N'
- 42: 0, # 'O'
- 48: 1, # 'P'
- 44: 1, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 0, # 'U'
- 38: 1, # 'V'
- 62: 0, # 'W'
- 43: 1, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 0, # 'b'
- 28: 2, # 'c'
- 12: 0, # 'd'
- 2: 3, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 0, # 'i'
- 24: 2, # 'j'
- 10: 1, # 'k'
- 5: 2, # 'l'
- 13: 2, # 'm'
- 4: 2, # 'n'
- 15: 1, # 'o'
- 26: 0, # 'p'
- 7: 2, # 'r'
- 8: 0, # 's'
- 9: 0, # 't'
- 14: 3, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 0, # 'y'
- 22: 2, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 1, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 1, # 'ç'
- 61: 0, # 'î'
- 34: 1, # 'ö'
- 17: 0, # 'ü'
- 30: 0, # 'ğ'
- 41: 1, # 'İ'
- 6: 3, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 39: { # 'D'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 1, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 1, # 'K'
- 49: 0, # 'L'
- 20: 0, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 1, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 2, # 'a'
- 21: 0, # 'b'
- 28: 2, # 'c'
- 12: 0, # 'd'
- 2: 2, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 0, # 'i'
- 24: 0, # 'j'
- 10: 0, # 'k'
- 5: 1, # 'l'
- 13: 3, # 'm'
- 4: 0, # 'n'
- 15: 1, # 'o'
- 26: 0, # 'p'
- 7: 0, # 'r'
- 8: 0, # 's'
- 9: 0, # 't'
- 14: 1, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 0, # 'y'
- 22: 1, # 'z'
- 63: 0, # '·'
- 54: 1, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 1, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 0, # 'ü'
- 30: 1, # 'ğ'
- 41: 0, # 'İ'
- 6: 1, # 'ı'
- 40: 1, # 'Ş'
- 19: 0, # 'ş'
- },
- 29: { # 'E'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 1, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 3, # 'K'
- 49: 0, # 'L'
- 20: 1, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 0, # 'b'
- 28: 0, # 'c'
- 12: 2, # 'd'
- 2: 3, # 'e'
- 18: 0, # 'f'
- 27: 1, # 'g'
- 25: 0, # 'h'
- 3: 1, # 'i'
- 24: 1, # 'j'
- 10: 0, # 'k'
- 5: 3, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 15: 0, # 'o'
- 26: 0, # 'p'
- 7: 0, # 'r'
- 8: 1, # 's'
- 9: 1, # 't'
- 14: 1, # 'u'
- 32: 1, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 2, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 0, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 3, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 52: { # 'F'
- 23: 0, # 'A'
- 37: 1, # 'B'
- 47: 1, # 'C'
- 39: 1, # 'D'
- 29: 1, # 'E'
- 52: 2, # 'F'
- 36: 0, # 'G'
- 45: 2, # 'H'
- 53: 1, # 'I'
- 60: 0, # 'J'
- 16: 0, # 'K'
- 49: 0, # 'L'
- 20: 1, # 'M'
- 46: 1, # 'N'
- 42: 1, # 'O'
- 48: 2, # 'P'
- 44: 1, # 'R'
- 35: 1, # 'S'
- 31: 1, # 'T'
- 51: 1, # 'U'
- 38: 1, # 'V'
- 62: 0, # 'W'
- 43: 2, # 'Y'
- 56: 0, # 'Z'
- 1: 0, # 'a'
- 21: 1, # 'b'
- 28: 1, # 'c'
- 12: 1, # 'd'
- 2: 0, # 'e'
- 18: 1, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 2, # 'i'
- 24: 1, # 'j'
- 10: 0, # 'k'
- 5: 0, # 'l'
- 13: 1, # 'm'
- 4: 2, # 'n'
- 15: 1, # 'o'
- 26: 0, # 'p'
- 7: 2, # 'r'
- 8: 1, # 's'
- 9: 1, # 't'
- 14: 1, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 1, # 'y'
- 22: 1, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 1, # 'Ö'
- 55: 2, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 2, # 'ö'
- 17: 0, # 'ü'
- 30: 1, # 'ğ'
- 41: 1, # 'İ'
- 6: 2, # 'ı'
- 40: 0, # 'Ş'
- 19: 2, # 'ş'
- },
- 36: { # 'G'
- 23: 1, # 'A'
- 37: 0, # 'B'
- 47: 1, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 1, # 'F'
- 36: 2, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 2, # 'K'
- 49: 0, # 'L'
- 20: 0, # 'M'
- 46: 2, # 'N'
- 42: 1, # 'O'
- 48: 1, # 'P'
- 44: 1, # 'R'
- 35: 1, # 'S'
- 31: 0, # 'T'
- 51: 1, # 'U'
- 38: 2, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 0, # 'b'
- 28: 1, # 'c'
- 12: 0, # 'd'
- 2: 3, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 0, # 'i'
- 24: 1, # 'j'
- 10: 1, # 'k'
- 5: 0, # 'l'
- 13: 3, # 'm'
- 4: 2, # 'n'
- 15: 0, # 'o'
- 26: 1, # 'p'
- 7: 0, # 'r'
- 8: 1, # 's'
- 9: 1, # 't'
- 14: 3, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 1, # 'x'
- 11: 0, # 'y'
- 22: 2, # 'z'
- 63: 0, # '·'
- 54: 1, # 'Ç'
- 50: 2, # 'Ö'
- 55: 0, # 'Ü'
- 59: 1, # 'â'
- 33: 2, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 0, # 'ü'
- 30: 1, # 'ğ'
- 41: 1, # 'İ'
- 6: 2, # 'ı'
- 40: 2, # 'Ş'
- 19: 1, # 'ş'
- },
- 45: { # 'H'
- 23: 0, # 'A'
- 37: 1, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 2, # 'F'
- 36: 2, # 'G'
- 45: 1, # 'H'
- 53: 1, # 'I'
- 60: 0, # 'J'
- 16: 2, # 'K'
- 49: 1, # 'L'
- 20: 0, # 'M'
- 46: 1, # 'N'
- 42: 1, # 'O'
- 48: 1, # 'P'
- 44: 0, # 'R'
- 35: 2, # 'S'
- 31: 0, # 'T'
- 51: 1, # 'U'
- 38: 2, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 0, # 'b'
- 28: 2, # 'c'
- 12: 0, # 'd'
- 2: 3, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 2, # 'i'
- 24: 0, # 'j'
- 10: 1, # 'k'
- 5: 0, # 'l'
- 13: 2, # 'm'
- 4: 0, # 'n'
- 15: 1, # 'o'
- 26: 1, # 'p'
- 7: 1, # 'r'
- 8: 0, # 's'
- 9: 0, # 't'
- 14: 3, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 0, # 'y'
- 22: 2, # 'z'
- 63: 0, # '·'
- 54: 1, # 'Ç'
- 50: 1, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 1, # 'ç'
- 61: 0, # 'î'
- 34: 1, # 'ö'
- 17: 0, # 'ü'
- 30: 2, # 'ğ'
- 41: 1, # 'İ'
- 6: 0, # 'ı'
- 40: 2, # 'Ş'
- 19: 1, # 'ş'
- },
- 53: { # 'I'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 1, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 2, # 'K'
- 49: 0, # 'L'
- 20: 0, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 1, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 2, # 'a'
- 21: 0, # 'b'
- 28: 2, # 'c'
- 12: 0, # 'd'
- 2: 2, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 0, # 'i'
- 24: 0, # 'j'
- 10: 0, # 'k'
- 5: 2, # 'l'
- 13: 2, # 'm'
- 4: 0, # 'n'
- 15: 0, # 'o'
- 26: 0, # 'p'
- 7: 0, # 'r'
- 8: 0, # 's'
- 9: 0, # 't'
- 14: 2, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 0, # 'y'
- 22: 2, # 'z'
- 63: 0, # '·'
- 54: 1, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 2, # 'ç'
- 61: 0, # 'î'
- 34: 1, # 'ö'
- 17: 0, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 0, # 'ı'
- 40: 1, # 'Ş'
- 19: 1, # 'ş'
- },
- 60: { # 'J'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 0, # 'K'
- 49: 0, # 'L'
- 20: 1, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 0, # 'a'
- 21: 1, # 'b'
- 28: 0, # 'c'
- 12: 1, # 'd'
- 2: 0, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 1, # 'i'
- 24: 0, # 'j'
- 10: 0, # 'k'
- 5: 0, # 'l'
- 13: 0, # 'm'
- 4: 1, # 'n'
- 15: 0, # 'o'
- 26: 0, # 'p'
- 7: 0, # 'r'
- 8: 1, # 's'
- 9: 0, # 't'
- 14: 0, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 0, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 0, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 0, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 16: { # 'K'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 3, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 0, # 'K'
- 49: 0, # 'L'
- 20: 2, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 2, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 2, # 'a'
- 21: 3, # 'b'
- 28: 0, # 'c'
- 12: 3, # 'd'
- 2: 1, # 'e'
- 18: 3, # 'f'
- 27: 3, # 'g'
- 25: 3, # 'h'
- 3: 3, # 'i'
- 24: 2, # 'j'
- 10: 3, # 'k'
- 5: 0, # 'l'
- 13: 0, # 'm'
- 4: 3, # 'n'
- 15: 0, # 'o'
- 26: 1, # 'p'
- 7: 3, # 'r'
- 8: 3, # 's'
- 9: 3, # 't'
- 14: 0, # 'u'
- 32: 3, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 2, # 'y'
- 22: 1, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 2, # 'ü'
- 30: 0, # 'ğ'
- 41: 1, # 'İ'
- 6: 3, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 49: { # 'L'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 2, # 'E'
- 52: 0, # 'F'
- 36: 1, # 'G'
- 45: 1, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 0, # 'K'
- 49: 0, # 'L'
- 20: 1, # 'M'
- 46: 0, # 'N'
- 42: 2, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 1, # 'Y'
- 56: 0, # 'Z'
- 1: 0, # 'a'
- 21: 3, # 'b'
- 28: 0, # 'c'
- 12: 2, # 'd'
- 2: 0, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 2, # 'i'
- 24: 0, # 'j'
- 10: 1, # 'k'
- 5: 0, # 'l'
- 13: 0, # 'm'
- 4: 2, # 'n'
- 15: 1, # 'o'
- 26: 1, # 'p'
- 7: 1, # 'r'
- 8: 1, # 's'
- 9: 1, # 't'
- 14: 0, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 2, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 2, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 1, # 'ö'
- 17: 1, # 'ü'
- 30: 1, # 'ğ'
- 41: 0, # 'İ'
- 6: 2, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 20: { # 'M'
- 23: 1, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 1, # 'J'
- 16: 3, # 'K'
- 49: 0, # 'L'
- 20: 2, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 1, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 2, # 'b'
- 28: 0, # 'c'
- 12: 3, # 'd'
- 2: 3, # 'e'
- 18: 0, # 'f'
- 27: 1, # 'g'
- 25: 1, # 'h'
- 3: 2, # 'i'
- 24: 2, # 'j'
- 10: 2, # 'k'
- 5: 2, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 15: 0, # 'o'
- 26: 1, # 'p'
- 7: 3, # 'r'
- 8: 0, # 's'
- 9: 2, # 't'
- 14: 3, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 2, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 3, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 0, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 3, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 46: { # 'N'
- 23: 0, # 'A'
- 37: 1, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 1, # 'F'
- 36: 1, # 'G'
- 45: 1, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 2, # 'K'
- 49: 0, # 'L'
- 20: 0, # 'M'
- 46: 1, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 1, # 'R'
- 35: 1, # 'S'
- 31: 0, # 'T'
- 51: 1, # 'U'
- 38: 2, # 'V'
- 62: 0, # 'W'
- 43: 1, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 0, # 'b'
- 28: 2, # 'c'
- 12: 0, # 'd'
- 2: 3, # 'e'
- 18: 0, # 'f'
- 27: 1, # 'g'
- 25: 0, # 'h'
- 3: 0, # 'i'
- 24: 2, # 'j'
- 10: 1, # 'k'
- 5: 1, # 'l'
- 13: 3, # 'm'
- 4: 2, # 'n'
- 15: 1, # 'o'
- 26: 1, # 'p'
- 7: 1, # 'r'
- 8: 0, # 's'
- 9: 0, # 't'
- 14: 3, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 1, # 'x'
- 11: 1, # 'y'
- 22: 2, # 'z'
- 63: 0, # '·'
- 54: 1, # 'Ç'
- 50: 1, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 1, # 'ö'
- 17: 0, # 'ü'
- 30: 0, # 'ğ'
- 41: 1, # 'İ'
- 6: 2, # 'ı'
- 40: 1, # 'Ş'
- 19: 1, # 'ş'
- },
- 42: { # 'O'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 1, # 'F'
- 36: 0, # 'G'
- 45: 1, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 2, # 'K'
- 49: 1, # 'L'
- 20: 0, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 2, # 'P'
- 44: 1, # 'R'
- 35: 1, # 'S'
- 31: 0, # 'T'
- 51: 1, # 'U'
- 38: 1, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 0, # 'b'
- 28: 2, # 'c'
- 12: 0, # 'd'
- 2: 2, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 0, # 'i'
- 24: 0, # 'j'
- 10: 0, # 'k'
- 5: 3, # 'l'
- 13: 3, # 'm'
- 4: 0, # 'n'
- 15: 1, # 'o'
- 26: 0, # 'p'
- 7: 0, # 'r'
- 8: 0, # 's'
- 9: 0, # 't'
- 14: 2, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 0, # 'y'
- 22: 2, # 'z'
- 63: 0, # '·'
- 54: 2, # 'Ç'
- 50: 1, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 2, # 'ç'
- 61: 0, # 'î'
- 34: 1, # 'ö'
- 17: 0, # 'ü'
- 30: 1, # 'ğ'
- 41: 2, # 'İ'
- 6: 1, # 'ı'
- 40: 1, # 'Ş'
- 19: 1, # 'ş'
- },
- 48: { # 'P'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 2, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 2, # 'F'
- 36: 1, # 'G'
- 45: 1, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 2, # 'K'
- 49: 0, # 'L'
- 20: 0, # 'M'
- 46: 1, # 'N'
- 42: 1, # 'O'
- 48: 1, # 'P'
- 44: 0, # 'R'
- 35: 1, # 'S'
- 31: 0, # 'T'
- 51: 0, # 'U'
- 38: 1, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 2, # 'a'
- 21: 0, # 'b'
- 28: 2, # 'c'
- 12: 0, # 'd'
- 2: 3, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 0, # 'i'
- 24: 0, # 'j'
- 10: 1, # 'k'
- 5: 0, # 'l'
- 13: 2, # 'm'
- 4: 0, # 'n'
- 15: 2, # 'o'
- 26: 0, # 'p'
- 7: 0, # 'r'
- 8: 0, # 's'
- 9: 0, # 't'
- 14: 2, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 2, # 'x'
- 11: 0, # 'y'
- 22: 2, # 'z'
- 63: 0, # '·'
- 54: 1, # 'Ç'
- 50: 2, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 2, # 'ö'
- 17: 0, # 'ü'
- 30: 1, # 'ğ'
- 41: 1, # 'İ'
- 6: 0, # 'ı'
- 40: 2, # 'Ş'
- 19: 1, # 'ş'
- },
- 44: { # 'R'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 1, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 1, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 3, # 'K'
- 49: 0, # 'L'
- 20: 0, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 1, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 1, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 1, # 'b'
- 28: 1, # 'c'
- 12: 0, # 'd'
- 2: 2, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 0, # 'i'
- 24: 0, # 'j'
- 10: 1, # 'k'
- 5: 2, # 'l'
- 13: 2, # 'm'
- 4: 0, # 'n'
- 15: 1, # 'o'
- 26: 0, # 'p'
- 7: 0, # 'r'
- 8: 0, # 's'
- 9: 0, # 't'
- 14: 2, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 1, # 'y'
- 22: 2, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 1, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 1, # 'ç'
- 61: 0, # 'î'
- 34: 1, # 'ö'
- 17: 1, # 'ü'
- 30: 1, # 'ğ'
- 41: 0, # 'İ'
- 6: 2, # 'ı'
- 40: 1, # 'Ş'
- 19: 1, # 'ş'
- },
- 35: { # 'S'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 1, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 1, # 'F'
- 36: 1, # 'G'
- 45: 1, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 3, # 'K'
- 49: 1, # 'L'
- 20: 1, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 1, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 1, # 'U'
- 38: 1, # 'V'
- 62: 0, # 'W'
- 43: 1, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 0, # 'b'
- 28: 2, # 'c'
- 12: 0, # 'd'
- 2: 3, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 0, # 'i'
- 24: 0, # 'j'
- 10: 1, # 'k'
- 5: 1, # 'l'
- 13: 2, # 'm'
- 4: 1, # 'n'
- 15: 0, # 'o'
- 26: 0, # 'p'
- 7: 0, # 'r'
- 8: 0, # 's'
- 9: 1, # 't'
- 14: 2, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 0, # 'y'
- 22: 1, # 'z'
- 63: 0, # '·'
- 54: 2, # 'Ç'
- 50: 2, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 3, # 'ç'
- 61: 0, # 'î'
- 34: 1, # 'ö'
- 17: 0, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 3, # 'ı'
- 40: 2, # 'Ş'
- 19: 1, # 'ş'
- },
- 31: { # 'T'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 1, # 'J'
- 16: 2, # 'K'
- 49: 0, # 'L'
- 20: 1, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 2, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 2, # 'b'
- 28: 0, # 'c'
- 12: 1, # 'd'
- 2: 3, # 'e'
- 18: 2, # 'f'
- 27: 2, # 'g'
- 25: 0, # 'h'
- 3: 1, # 'i'
- 24: 1, # 'j'
- 10: 2, # 'k'
- 5: 2, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 15: 0, # 'o'
- 26: 2, # 'p'
- 7: 2, # 'r'
- 8: 0, # 's'
- 9: 2, # 't'
- 14: 2, # 'u'
- 32: 1, # 'v'
- 57: 1, # 'w'
- 58: 1, # 'x'
- 11: 2, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 1, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 3, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 51: { # 'U'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 1, # 'F'
- 36: 1, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 1, # 'K'
- 49: 0, # 'L'
- 20: 0, # 'M'
- 46: 1, # 'N'
- 42: 0, # 'O'
- 48: 1, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 1, # 'U'
- 38: 1, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 0, # 'b'
- 28: 1, # 'c'
- 12: 0, # 'd'
- 2: 3, # 'e'
- 18: 0, # 'f'
- 27: 2, # 'g'
- 25: 0, # 'h'
- 3: 0, # 'i'
- 24: 0, # 'j'
- 10: 1, # 'k'
- 5: 1, # 'l'
- 13: 3, # 'm'
- 4: 2, # 'n'
- 15: 0, # 'o'
- 26: 1, # 'p'
- 7: 0, # 'r'
- 8: 0, # 's'
- 9: 0, # 't'
- 14: 2, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 0, # 'y'
- 22: 2, # 'z'
- 63: 0, # '·'
- 54: 1, # 'Ç'
- 50: 1, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 0, # 'ü'
- 30: 1, # 'ğ'
- 41: 1, # 'İ'
- 6: 2, # 'ı'
- 40: 0, # 'Ş'
- 19: 1, # 'ş'
- },
- 38: { # 'V'
- 23: 1, # 'A'
- 37: 1, # 'B'
- 47: 1, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 2, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 3, # 'K'
- 49: 0, # 'L'
- 20: 3, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 1, # 'P'
- 44: 1, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 1, # 'U'
- 38: 1, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 0, # 'b'
- 28: 2, # 'c'
- 12: 0, # 'd'
- 2: 3, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 0, # 'i'
- 24: 0, # 'j'
- 10: 0, # 'k'
- 5: 2, # 'l'
- 13: 2, # 'm'
- 4: 0, # 'n'
- 15: 2, # 'o'
- 26: 0, # 'p'
- 7: 0, # 'r'
- 8: 0, # 's'
- 9: 1, # 't'
- 14: 3, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 1, # 'y'
- 22: 2, # 'z'
- 63: 0, # '·'
- 54: 1, # 'Ç'
- 50: 1, # 'Ö'
- 55: 0, # 'Ü'
- 59: 1, # 'â'
- 33: 2, # 'ç'
- 61: 0, # 'î'
- 34: 1, # 'ö'
- 17: 0, # 'ü'
- 30: 1, # 'ğ'
- 41: 1, # 'İ'
- 6: 3, # 'ı'
- 40: 2, # 'Ş'
- 19: 1, # 'ş'
- },
- 62: { # 'W'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 0, # 'K'
- 49: 0, # 'L'
- 20: 0, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 0, # 'a'
- 21: 0, # 'b'
- 28: 0, # 'c'
- 12: 0, # 'd'
- 2: 0, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 0, # 'i'
- 24: 0, # 'j'
- 10: 0, # 'k'
- 5: 0, # 'l'
- 13: 0, # 'm'
- 4: 0, # 'n'
- 15: 0, # 'o'
- 26: 0, # 'p'
- 7: 0, # 'r'
- 8: 0, # 's'
- 9: 0, # 't'
- 14: 0, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 0, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 0, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 0, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 43: { # 'Y'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 1, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 2, # 'F'
- 36: 0, # 'G'
- 45: 1, # 'H'
- 53: 1, # 'I'
- 60: 0, # 'J'
- 16: 2, # 'K'
- 49: 0, # 'L'
- 20: 0, # 'M'
- 46: 2, # 'N'
- 42: 0, # 'O'
- 48: 2, # 'P'
- 44: 1, # 'R'
- 35: 1, # 'S'
- 31: 0, # 'T'
- 51: 1, # 'U'
- 38: 2, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 0, # 'b'
- 28: 2, # 'c'
- 12: 0, # 'd'
- 2: 2, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 0, # 'i'
- 24: 1, # 'j'
- 10: 1, # 'k'
- 5: 1, # 'l'
- 13: 3, # 'm'
- 4: 0, # 'n'
- 15: 2, # 'o'
- 26: 0, # 'p'
- 7: 0, # 'r'
- 8: 0, # 's'
- 9: 0, # 't'
- 14: 3, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 1, # 'x'
- 11: 0, # 'y'
- 22: 2, # 'z'
- 63: 0, # '·'
- 54: 1, # 'Ç'
- 50: 2, # 'Ö'
- 55: 1, # 'Ü'
- 59: 1, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 1, # 'ö'
- 17: 0, # 'ü'
- 30: 1, # 'ğ'
- 41: 1, # 'İ'
- 6: 0, # 'ı'
- 40: 2, # 'Ş'
- 19: 1, # 'ş'
- },
- 56: { # 'Z'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 0, # 'K'
- 49: 0, # 'L'
- 20: 0, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 2, # 'Z'
- 1: 2, # 'a'
- 21: 1, # 'b'
- 28: 0, # 'c'
- 12: 0, # 'd'
- 2: 2, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 2, # 'i'
- 24: 1, # 'j'
- 10: 0, # 'k'
- 5: 0, # 'l'
- 13: 1, # 'm'
- 4: 1, # 'n'
- 15: 0, # 'o'
- 26: 0, # 'p'
- 7: 1, # 'r'
- 8: 1, # 's'
- 9: 0, # 't'
- 14: 2, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 0, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 1, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 1, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 1: { # 'a'
- 23: 3, # 'A'
- 37: 0, # 'B'
- 47: 1, # 'C'
- 39: 0, # 'D'
- 29: 3, # 'E'
- 52: 0, # 'F'
- 36: 1, # 'G'
- 45: 1, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 0, # 'K'
- 49: 0, # 'L'
- 20: 3, # 'M'
- 46: 1, # 'N'
- 42: 0, # 'O'
- 48: 1, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 3, # 'T'
- 51: 0, # 'U'
- 38: 1, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 2, # 'Z'
- 1: 2, # 'a'
- 21: 3, # 'b'
- 28: 0, # 'c'
- 12: 3, # 'd'
- 2: 2, # 'e'
- 18: 3, # 'f'
- 27: 3, # 'g'
- 25: 3, # 'h'
- 3: 3, # 'i'
- 24: 3, # 'j'
- 10: 3, # 'k'
- 5: 0, # 'l'
- 13: 2, # 'm'
- 4: 3, # 'n'
- 15: 1, # 'o'
- 26: 3, # 'p'
- 7: 3, # 'r'
- 8: 3, # 's'
- 9: 3, # 't'
- 14: 3, # 'u'
- 32: 3, # 'v'
- 57: 2, # 'w'
- 58: 0, # 'x'
- 11: 3, # 'y'
- 22: 0, # 'z'
- 63: 1, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 1, # 'ç'
- 61: 1, # 'î'
- 34: 1, # 'ö'
- 17: 3, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 3, # 'ı'
- 40: 0, # 'Ş'
- 19: 1, # 'ş'
- },
- 21: { # 'b'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 1, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 1, # 'J'
- 16: 2, # 'K'
- 49: 0, # 'L'
- 20: 2, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 1, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 1, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 2, # 'b'
- 28: 0, # 'c'
- 12: 3, # 'd'
- 2: 3, # 'e'
- 18: 0, # 'f'
- 27: 3, # 'g'
- 25: 1, # 'h'
- 3: 3, # 'i'
- 24: 2, # 'j'
- 10: 3, # 'k'
- 5: 3, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 15: 0, # 'o'
- 26: 3, # 'p'
- 7: 1, # 'r'
- 8: 2, # 's'
- 9: 2, # 't'
- 14: 2, # 'u'
- 32: 1, # 'v'
- 57: 0, # 'w'
- 58: 1, # 'x'
- 11: 3, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 1, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 0, # 'ü'
- 30: 1, # 'ğ'
- 41: 0, # 'İ'
- 6: 2, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 28: { # 'c'
- 23: 0, # 'A'
- 37: 1, # 'B'
- 47: 1, # 'C'
- 39: 1, # 'D'
- 29: 2, # 'E'
- 52: 0, # 'F'
- 36: 2, # 'G'
- 45: 2, # 'H'
- 53: 1, # 'I'
- 60: 0, # 'J'
- 16: 0, # 'K'
- 49: 0, # 'L'
- 20: 2, # 'M'
- 46: 1, # 'N'
- 42: 1, # 'O'
- 48: 2, # 'P'
- 44: 1, # 'R'
- 35: 1, # 'S'
- 31: 2, # 'T'
- 51: 2, # 'U'
- 38: 2, # 'V'
- 62: 0, # 'W'
- 43: 3, # 'Y'
- 56: 0, # 'Z'
- 1: 1, # 'a'
- 21: 1, # 'b'
- 28: 2, # 'c'
- 12: 2, # 'd'
- 2: 1, # 'e'
- 18: 1, # 'f'
- 27: 2, # 'g'
- 25: 2, # 'h'
- 3: 3, # 'i'
- 24: 1, # 'j'
- 10: 3, # 'k'
- 5: 0, # 'l'
- 13: 2, # 'm'
- 4: 3, # 'n'
- 15: 2, # 'o'
- 26: 2, # 'p'
- 7: 3, # 'r'
- 8: 3, # 's'
- 9: 3, # 't'
- 14: 1, # 'u'
- 32: 0, # 'v'
- 57: 1, # 'w'
- 58: 0, # 'x'
- 11: 2, # 'y'
- 22: 1, # 'z'
- 63: 1, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 1, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 1, # 'î'
- 34: 2, # 'ö'
- 17: 2, # 'ü'
- 30: 2, # 'ğ'
- 41: 1, # 'İ'
- 6: 3, # 'ı'
- 40: 0, # 'Ş'
- 19: 2, # 'ş'
- },
- 12: { # 'd'
- 23: 1, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 2, # 'J'
- 16: 3, # 'K'
- 49: 0, # 'L'
- 20: 3, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 1, # 'S'
- 31: 1, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 2, # 'b'
- 28: 1, # 'c'
- 12: 3, # 'd'
- 2: 3, # 'e'
- 18: 1, # 'f'
- 27: 3, # 'g'
- 25: 3, # 'h'
- 3: 2, # 'i'
- 24: 3, # 'j'
- 10: 2, # 'k'
- 5: 3, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 15: 1, # 'o'
- 26: 2, # 'p'
- 7: 3, # 'r'
- 8: 2, # 's'
- 9: 2, # 't'
- 14: 3, # 'u'
- 32: 1, # 'v'
- 57: 0, # 'w'
- 58: 1, # 'x'
- 11: 3, # 'y'
- 22: 1, # 'z'
- 63: 1, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 1, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 2, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 2: { # 'e'
- 23: 2, # 'A'
- 37: 0, # 'B'
- 47: 2, # 'C'
- 39: 0, # 'D'
- 29: 3, # 'E'
- 52: 1, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 1, # 'K'
- 49: 0, # 'L'
- 20: 3, # 'M'
- 46: 1, # 'N'
- 42: 0, # 'O'
- 48: 1, # 'P'
- 44: 1, # 'R'
- 35: 0, # 'S'
- 31: 3, # 'T'
- 51: 0, # 'U'
- 38: 1, # 'V'
- 62: 0, # 'W'
- 43: 1, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 3, # 'b'
- 28: 0, # 'c'
- 12: 3, # 'd'
- 2: 2, # 'e'
- 18: 3, # 'f'
- 27: 3, # 'g'
- 25: 3, # 'h'
- 3: 3, # 'i'
- 24: 3, # 'j'
- 10: 3, # 'k'
- 5: 0, # 'l'
- 13: 2, # 'm'
- 4: 3, # 'n'
- 15: 1, # 'o'
- 26: 3, # 'p'
- 7: 3, # 'r'
- 8: 3, # 's'
- 9: 3, # 't'
- 14: 3, # 'u'
- 32: 3, # 'v'
- 57: 2, # 'w'
- 58: 0, # 'x'
- 11: 3, # 'y'
- 22: 1, # 'z'
- 63: 1, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 1, # 'ç'
- 61: 0, # 'î'
- 34: 1, # 'ö'
- 17: 3, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 3, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 18: { # 'f'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 2, # 'K'
- 49: 0, # 'L'
- 20: 2, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 2, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 1, # 'b'
- 28: 0, # 'c'
- 12: 3, # 'd'
- 2: 3, # 'e'
- 18: 2, # 'f'
- 27: 1, # 'g'
- 25: 1, # 'h'
- 3: 1, # 'i'
- 24: 1, # 'j'
- 10: 1, # 'k'
- 5: 3, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 15: 0, # 'o'
- 26: 2, # 'p'
- 7: 1, # 'r'
- 8: 3, # 's'
- 9: 3, # 't'
- 14: 1, # 'u'
- 32: 2, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 1, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 1, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 1, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 1, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 27: { # 'g'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 3, # 'K'
- 49: 0, # 'L'
- 20: 0, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 1, # 'S'
- 31: 1, # 'T'
- 51: 0, # 'U'
- 38: 2, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 1, # 'b'
- 28: 0, # 'c'
- 12: 1, # 'd'
- 2: 3, # 'e'
- 18: 0, # 'f'
- 27: 2, # 'g'
- 25: 1, # 'h'
- 3: 2, # 'i'
- 24: 3, # 'j'
- 10: 2, # 'k'
- 5: 3, # 'l'
- 13: 3, # 'm'
- 4: 2, # 'n'
- 15: 0, # 'o'
- 26: 1, # 'p'
- 7: 2, # 'r'
- 8: 2, # 's'
- 9: 3, # 't'
- 14: 3, # 'u'
- 32: 1, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 1, # 'y'
- 22: 0, # 'z'
- 63: 1, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 0, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 2, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 25: { # 'h'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 2, # 'K'
- 49: 0, # 'L'
- 20: 0, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 0, # 'b'
- 28: 0, # 'c'
- 12: 2, # 'd'
- 2: 3, # 'e'
- 18: 0, # 'f'
- 27: 1, # 'g'
- 25: 2, # 'h'
- 3: 2, # 'i'
- 24: 3, # 'j'
- 10: 3, # 'k'
- 5: 3, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 15: 1, # 'o'
- 26: 1, # 'p'
- 7: 3, # 'r'
- 8: 3, # 's'
- 9: 2, # 't'
- 14: 3, # 'u'
- 32: 2, # 'v'
- 57: 1, # 'w'
- 58: 0, # 'x'
- 11: 1, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 0, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 3, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 3: { # 'i'
- 23: 2, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 1, # 'J'
- 16: 3, # 'K'
- 49: 0, # 'L'
- 20: 3, # 'M'
- 46: 0, # 'N'
- 42: 1, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 1, # 'S'
- 31: 2, # 'T'
- 51: 0, # 'U'
- 38: 1, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 2, # 'b'
- 28: 0, # 'c'
- 12: 3, # 'd'
- 2: 3, # 'e'
- 18: 2, # 'f'
- 27: 3, # 'g'
- 25: 1, # 'h'
- 3: 3, # 'i'
- 24: 2, # 'j'
- 10: 3, # 'k'
- 5: 3, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 15: 1, # 'o'
- 26: 3, # 'p'
- 7: 3, # 'r'
- 8: 3, # 's'
- 9: 3, # 't'
- 14: 3, # 'u'
- 32: 2, # 'v'
- 57: 1, # 'w'
- 58: 1, # 'x'
- 11: 3, # 'y'
- 22: 1, # 'z'
- 63: 1, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 1, # 'Ü'
- 59: 0, # 'â'
- 33: 2, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 3, # 'ü'
- 30: 0, # 'ğ'
- 41: 1, # 'İ'
- 6: 2, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 24: { # 'j'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 1, # 'J'
- 16: 2, # 'K'
- 49: 0, # 'L'
- 20: 2, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 1, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 1, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 1, # 'Z'
- 1: 3, # 'a'
- 21: 1, # 'b'
- 28: 1, # 'c'
- 12: 3, # 'd'
- 2: 3, # 'e'
- 18: 2, # 'f'
- 27: 1, # 'g'
- 25: 1, # 'h'
- 3: 2, # 'i'
- 24: 1, # 'j'
- 10: 2, # 'k'
- 5: 2, # 'l'
- 13: 3, # 'm'
- 4: 2, # 'n'
- 15: 0, # 'o'
- 26: 1, # 'p'
- 7: 2, # 'r'
- 8: 3, # 's'
- 9: 2, # 't'
- 14: 3, # 'u'
- 32: 2, # 'v'
- 57: 0, # 'w'
- 58: 2, # 'x'
- 11: 1, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 1, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 1, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 3, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 10: { # 'k'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 3, # 'K'
- 49: 0, # 'L'
- 20: 2, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 3, # 'T'
- 51: 0, # 'U'
- 38: 1, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 1, # 'Z'
- 1: 3, # 'a'
- 21: 2, # 'b'
- 28: 0, # 'c'
- 12: 2, # 'd'
- 2: 3, # 'e'
- 18: 1, # 'f'
- 27: 2, # 'g'
- 25: 2, # 'h'
- 3: 3, # 'i'
- 24: 2, # 'j'
- 10: 2, # 'k'
- 5: 3, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 15: 0, # 'o'
- 26: 3, # 'p'
- 7: 2, # 'r'
- 8: 2, # 's'
- 9: 2, # 't'
- 14: 3, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 1, # 'x'
- 11: 3, # 'y'
- 22: 0, # 'z'
- 63: 1, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 3, # 'ç'
- 61: 0, # 'î'
- 34: 1, # 'ö'
- 17: 3, # 'ü'
- 30: 1, # 'ğ'
- 41: 0, # 'İ'
- 6: 3, # 'ı'
- 40: 0, # 'Ş'
- 19: 1, # 'ş'
- },
- 5: { # 'l'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 3, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 0, # 'K'
- 49: 0, # 'L'
- 20: 2, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 1, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 0, # 'a'
- 21: 3, # 'b'
- 28: 0, # 'c'
- 12: 3, # 'd'
- 2: 1, # 'e'
- 18: 3, # 'f'
- 27: 3, # 'g'
- 25: 2, # 'h'
- 3: 3, # 'i'
- 24: 2, # 'j'
- 10: 3, # 'k'
- 5: 1, # 'l'
- 13: 1, # 'm'
- 4: 3, # 'n'
- 15: 0, # 'o'
- 26: 2, # 'p'
- 7: 3, # 'r'
- 8: 3, # 's'
- 9: 3, # 't'
- 14: 2, # 'u'
- 32: 2, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 3, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 1, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 2, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 3, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 13: { # 'm'
- 23: 1, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 3, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 0, # 'K'
- 49: 0, # 'L'
- 20: 3, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 3, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 1, # 'Y'
- 56: 0, # 'Z'
- 1: 2, # 'a'
- 21: 3, # 'b'
- 28: 0, # 'c'
- 12: 3, # 'd'
- 2: 2, # 'e'
- 18: 3, # 'f'
- 27: 3, # 'g'
- 25: 3, # 'h'
- 3: 3, # 'i'
- 24: 3, # 'j'
- 10: 3, # 'k'
- 5: 0, # 'l'
- 13: 2, # 'm'
- 4: 3, # 'n'
- 15: 1, # 'o'
- 26: 2, # 'p'
- 7: 3, # 'r'
- 8: 3, # 's'
- 9: 3, # 't'
- 14: 2, # 'u'
- 32: 2, # 'v'
- 57: 1, # 'w'
- 58: 0, # 'x'
- 11: 3, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 3, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 3, # 'ı'
- 40: 0, # 'Ş'
- 19: 1, # 'ş'
- },
- 4: { # 'n'
- 23: 1, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 1, # 'H'
- 53: 0, # 'I'
- 60: 2, # 'J'
- 16: 3, # 'K'
- 49: 0, # 'L'
- 20: 3, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 2, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 2, # 'b'
- 28: 1, # 'c'
- 12: 3, # 'd'
- 2: 3, # 'e'
- 18: 1, # 'f'
- 27: 2, # 'g'
- 25: 3, # 'h'
- 3: 2, # 'i'
- 24: 2, # 'j'
- 10: 3, # 'k'
- 5: 3, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 15: 1, # 'o'
- 26: 3, # 'p'
- 7: 2, # 'r'
- 8: 3, # 's'
- 9: 3, # 't'
- 14: 3, # 'u'
- 32: 2, # 'v'
- 57: 0, # 'w'
- 58: 2, # 'x'
- 11: 3, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 1, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 2, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 1, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 15: { # 'o'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 1, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 2, # 'F'
- 36: 1, # 'G'
- 45: 1, # 'H'
- 53: 1, # 'I'
- 60: 0, # 'J'
- 16: 3, # 'K'
- 49: 2, # 'L'
- 20: 0, # 'M'
- 46: 2, # 'N'
- 42: 1, # 'O'
- 48: 2, # 'P'
- 44: 1, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 0, # 'b'
- 28: 2, # 'c'
- 12: 0, # 'd'
- 2: 3, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 1, # 'i'
- 24: 2, # 'j'
- 10: 1, # 'k'
- 5: 3, # 'l'
- 13: 3, # 'm'
- 4: 2, # 'n'
- 15: 2, # 'o'
- 26: 0, # 'p'
- 7: 1, # 'r'
- 8: 0, # 's'
- 9: 0, # 't'
- 14: 3, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 2, # 'x'
- 11: 0, # 'y'
- 22: 2, # 'z'
- 63: 0, # '·'
- 54: 1, # 'Ç'
- 50: 2, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 3, # 'ç'
- 61: 0, # 'î'
- 34: 1, # 'ö'
- 17: 0, # 'ü'
- 30: 2, # 'ğ'
- 41: 2, # 'İ'
- 6: 3, # 'ı'
- 40: 2, # 'Ş'
- 19: 2, # 'ş'
- },
- 26: { # 'p'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 3, # 'K'
- 49: 0, # 'L'
- 20: 1, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 1, # 'b'
- 28: 0, # 'c'
- 12: 1, # 'd'
- 2: 3, # 'e'
- 18: 0, # 'f'
- 27: 1, # 'g'
- 25: 1, # 'h'
- 3: 2, # 'i'
- 24: 3, # 'j'
- 10: 1, # 'k'
- 5: 3, # 'l'
- 13: 3, # 'm'
- 4: 2, # 'n'
- 15: 0, # 'o'
- 26: 2, # 'p'
- 7: 2, # 'r'
- 8: 1, # 's'
- 9: 1, # 't'
- 14: 3, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 1, # 'x'
- 11: 1, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 3, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 1, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 3, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 7: { # 'r'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 1, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 2, # 'J'
- 16: 3, # 'K'
- 49: 0, # 'L'
- 20: 2, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 2, # 'T'
- 51: 1, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 1, # 'Z'
- 1: 3, # 'a'
- 21: 1, # 'b'
- 28: 0, # 'c'
- 12: 3, # 'd'
- 2: 3, # 'e'
- 18: 0, # 'f'
- 27: 2, # 'g'
- 25: 3, # 'h'
- 3: 2, # 'i'
- 24: 2, # 'j'
- 10: 3, # 'k'
- 5: 3, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 15: 0, # 'o'
- 26: 2, # 'p'
- 7: 3, # 'r'
- 8: 3, # 's'
- 9: 3, # 't'
- 14: 3, # 'u'
- 32: 2, # 'v'
- 57: 0, # 'w'
- 58: 1, # 'x'
- 11: 2, # 'y'
- 22: 0, # 'z'
- 63: 1, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 2, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 3, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 2, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 8: { # 's'
- 23: 1, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 1, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 3, # 'K'
- 49: 0, # 'L'
- 20: 3, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 2, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 1, # 'Z'
- 1: 3, # 'a'
- 21: 2, # 'b'
- 28: 1, # 'c'
- 12: 3, # 'd'
- 2: 3, # 'e'
- 18: 0, # 'f'
- 27: 2, # 'g'
- 25: 2, # 'h'
- 3: 2, # 'i'
- 24: 3, # 'j'
- 10: 3, # 'k'
- 5: 3, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 15: 0, # 'o'
- 26: 3, # 'p'
- 7: 3, # 'r'
- 8: 3, # 's'
- 9: 3, # 't'
- 14: 3, # 'u'
- 32: 2, # 'v'
- 57: 0, # 'w'
- 58: 1, # 'x'
- 11: 2, # 'y'
- 22: 1, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 2, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 2, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 3, # 'ı'
- 40: 0, # 'Ş'
- 19: 1, # 'ş'
- },
- 9: { # 't'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 1, # 'J'
- 16: 3, # 'K'
- 49: 0, # 'L'
- 20: 2, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 2, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 1, # 'Z'
- 1: 3, # 'a'
- 21: 3, # 'b'
- 28: 0, # 'c'
- 12: 3, # 'd'
- 2: 3, # 'e'
- 18: 2, # 'f'
- 27: 2, # 'g'
- 25: 2, # 'h'
- 3: 2, # 'i'
- 24: 2, # 'j'
- 10: 3, # 'k'
- 5: 3, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 15: 0, # 'o'
- 26: 2, # 'p'
- 7: 3, # 'r'
- 8: 3, # 's'
- 9: 3, # 't'
- 14: 3, # 'u'
- 32: 3, # 'v'
- 57: 0, # 'w'
- 58: 2, # 'x'
- 11: 2, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 3, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 2, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 3, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 14: { # 'u'
- 23: 3, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 3, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 1, # 'H'
- 53: 0, # 'I'
- 60: 1, # 'J'
- 16: 0, # 'K'
- 49: 0, # 'L'
- 20: 3, # 'M'
- 46: 2, # 'N'
- 42: 0, # 'O'
- 48: 1, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 3, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 1, # 'Y'
- 56: 2, # 'Z'
- 1: 2, # 'a'
- 21: 3, # 'b'
- 28: 0, # 'c'
- 12: 3, # 'd'
- 2: 2, # 'e'
- 18: 2, # 'f'
- 27: 3, # 'g'
- 25: 3, # 'h'
- 3: 3, # 'i'
- 24: 2, # 'j'
- 10: 3, # 'k'
- 5: 0, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 15: 0, # 'o'
- 26: 3, # 'p'
- 7: 3, # 'r'
- 8: 3, # 's'
- 9: 3, # 't'
- 14: 3, # 'u'
- 32: 2, # 'v'
- 57: 2, # 'w'
- 58: 0, # 'x'
- 11: 3, # 'y'
- 22: 0, # 'z'
- 63: 1, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 3, # 'ü'
- 30: 1, # 'ğ'
- 41: 0, # 'İ'
- 6: 3, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 32: { # 'v'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 3, # 'K'
- 49: 0, # 'L'
- 20: 1, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 0, # 'b'
- 28: 0, # 'c'
- 12: 3, # 'd'
- 2: 3, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 0, # 'i'
- 24: 1, # 'j'
- 10: 1, # 'k'
- 5: 3, # 'l'
- 13: 2, # 'm'
- 4: 3, # 'n'
- 15: 0, # 'o'
- 26: 1, # 'p'
- 7: 1, # 'r'
- 8: 2, # 's'
- 9: 3, # 't'
- 14: 3, # 'u'
- 32: 1, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 0, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 2, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 0, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 1, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 57: { # 'w'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 0, # 'K'
- 49: 0, # 'L'
- 20: 0, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 1, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 1, # 'a'
- 21: 0, # 'b'
- 28: 0, # 'c'
- 12: 0, # 'd'
- 2: 2, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 1, # 'h'
- 3: 0, # 'i'
- 24: 0, # 'j'
- 10: 1, # 'k'
- 5: 0, # 'l'
- 13: 0, # 'm'
- 4: 1, # 'n'
- 15: 0, # 'o'
- 26: 0, # 'p'
- 7: 0, # 'r'
- 8: 1, # 's'
- 9: 0, # 't'
- 14: 1, # 'u'
- 32: 0, # 'v'
- 57: 2, # 'w'
- 58: 0, # 'x'
- 11: 0, # 'y'
- 22: 0, # 'z'
- 63: 1, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 1, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 0, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 58: { # 'x'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 1, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 1, # 'J'
- 16: 0, # 'K'
- 49: 0, # 'L'
- 20: 1, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 0, # 'a'
- 21: 1, # 'b'
- 28: 0, # 'c'
- 12: 2, # 'd'
- 2: 1, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 2, # 'i'
- 24: 2, # 'j'
- 10: 1, # 'k'
- 5: 0, # 'l'
- 13: 0, # 'm'
- 4: 2, # 'n'
- 15: 0, # 'o'
- 26: 0, # 'p'
- 7: 1, # 'r'
- 8: 2, # 's'
- 9: 1, # 't'
- 14: 0, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 2, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 1, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 2, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 11: { # 'y'
- 23: 1, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 1, # 'J'
- 16: 3, # 'K'
- 49: 0, # 'L'
- 20: 1, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 1, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 1, # 'Y'
- 56: 1, # 'Z'
- 1: 3, # 'a'
- 21: 1, # 'b'
- 28: 0, # 'c'
- 12: 2, # 'd'
- 2: 3, # 'e'
- 18: 0, # 'f'
- 27: 2, # 'g'
- 25: 2, # 'h'
- 3: 2, # 'i'
- 24: 1, # 'j'
- 10: 2, # 'k'
- 5: 3, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 15: 0, # 'o'
- 26: 1, # 'p'
- 7: 2, # 'r'
- 8: 1, # 's'
- 9: 2, # 't'
- 14: 3, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 1, # 'x'
- 11: 3, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 3, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 2, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 3, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 22: { # 'z'
- 23: 2, # 'A'
- 37: 2, # 'B'
- 47: 1, # 'C'
- 39: 2, # 'D'
- 29: 3, # 'E'
- 52: 1, # 'F'
- 36: 2, # 'G'
- 45: 2, # 'H'
- 53: 1, # 'I'
- 60: 0, # 'J'
- 16: 0, # 'K'
- 49: 0, # 'L'
- 20: 3, # 'M'
- 46: 2, # 'N'
- 42: 2, # 'O'
- 48: 2, # 'P'
- 44: 1, # 'R'
- 35: 1, # 'S'
- 31: 3, # 'T'
- 51: 2, # 'U'
- 38: 2, # 'V'
- 62: 0, # 'W'
- 43: 2, # 'Y'
- 56: 1, # 'Z'
- 1: 1, # 'a'
- 21: 2, # 'b'
- 28: 1, # 'c'
- 12: 2, # 'd'
- 2: 2, # 'e'
- 18: 3, # 'f'
- 27: 2, # 'g'
- 25: 2, # 'h'
- 3: 3, # 'i'
- 24: 2, # 'j'
- 10: 3, # 'k'
- 5: 0, # 'l'
- 13: 2, # 'm'
- 4: 3, # 'n'
- 15: 2, # 'o'
- 26: 2, # 'p'
- 7: 3, # 'r'
- 8: 3, # 's'
- 9: 3, # 't'
- 14: 0, # 'u'
- 32: 2, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 3, # 'y'
- 22: 2, # 'z'
- 63: 1, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 2, # 'Ü'
- 59: 1, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 2, # 'ö'
- 17: 2, # 'ü'
- 30: 2, # 'ğ'
- 41: 1, # 'İ'
- 6: 3, # 'ı'
- 40: 1, # 'Ş'
- 19: 2, # 'ş'
- },
- 63: { # '·'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 0, # 'K'
- 49: 0, # 'L'
- 20: 0, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 0, # 'a'
- 21: 0, # 'b'
- 28: 0, # 'c'
- 12: 0, # 'd'
- 2: 1, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 0, # 'i'
- 24: 0, # 'j'
- 10: 0, # 'k'
- 5: 0, # 'l'
- 13: 2, # 'm'
- 4: 0, # 'n'
- 15: 0, # 'o'
- 26: 0, # 'p'
- 7: 0, # 'r'
- 8: 0, # 's'
- 9: 0, # 't'
- 14: 2, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 0, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 0, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 0, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 54: { # 'Ç'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 1, # 'C'
- 39: 1, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 1, # 'G'
- 45: 1, # 'H'
- 53: 1, # 'I'
- 60: 0, # 'J'
- 16: 0, # 'K'
- 49: 0, # 'L'
- 20: 0, # 'M'
- 46: 0, # 'N'
- 42: 1, # 'O'
- 48: 1, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 1, # 'U'
- 38: 1, # 'V'
- 62: 0, # 'W'
- 43: 2, # 'Y'
- 56: 0, # 'Z'
- 1: 0, # 'a'
- 21: 1, # 'b'
- 28: 0, # 'c'
- 12: 1, # 'd'
- 2: 0, # 'e'
- 18: 0, # 'f'
- 27: 1, # 'g'
- 25: 0, # 'h'
- 3: 3, # 'i'
- 24: 0, # 'j'
- 10: 1, # 'k'
- 5: 0, # 'l'
- 13: 0, # 'm'
- 4: 2, # 'n'
- 15: 1, # 'o'
- 26: 0, # 'p'
- 7: 2, # 'r'
- 8: 0, # 's'
- 9: 1, # 't'
- 14: 0, # 'u'
- 32: 2, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 0, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 2, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 1, # 'ö'
- 17: 0, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 2, # 'ı'
- 40: 0, # 'Ş'
- 19: 1, # 'ş'
- },
- 50: { # 'Ö'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 1, # 'C'
- 39: 1, # 'D'
- 29: 2, # 'E'
- 52: 0, # 'F'
- 36: 1, # 'G'
- 45: 2, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 0, # 'K'
- 49: 0, # 'L'
- 20: 1, # 'M'
- 46: 1, # 'N'
- 42: 2, # 'O'
- 48: 2, # 'P'
- 44: 1, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 1, # 'U'
- 38: 1, # 'V'
- 62: 0, # 'W'
- 43: 2, # 'Y'
- 56: 0, # 'Z'
- 1: 0, # 'a'
- 21: 2, # 'b'
- 28: 1, # 'c'
- 12: 2, # 'd'
- 2: 0, # 'e'
- 18: 1, # 'f'
- 27: 1, # 'g'
- 25: 1, # 'h'
- 3: 2, # 'i'
- 24: 0, # 'j'
- 10: 2, # 'k'
- 5: 0, # 'l'
- 13: 0, # 'm'
- 4: 3, # 'n'
- 15: 2, # 'o'
- 26: 2, # 'p'
- 7: 3, # 'r'
- 8: 1, # 's'
- 9: 2, # 't'
- 14: 0, # 'u'
- 32: 1, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 0, # 'y'
- 22: 1, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 2, # 'ö'
- 17: 2, # 'ü'
- 30: 1, # 'ğ'
- 41: 0, # 'İ'
- 6: 2, # 'ı'
- 40: 0, # 'Ş'
- 19: 1, # 'ş'
- },
- 55: { # 'Ü'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 2, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 1, # 'K'
- 49: 0, # 'L'
- 20: 0, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 1, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 0, # 'U'
- 38: 1, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 2, # 'a'
- 21: 0, # 'b'
- 28: 2, # 'c'
- 12: 0, # 'd'
- 2: 2, # 'e'
- 18: 0, # 'f'
- 27: 1, # 'g'
- 25: 0, # 'h'
- 3: 0, # 'i'
- 24: 0, # 'j'
- 10: 0, # 'k'
- 5: 1, # 'l'
- 13: 1, # 'm'
- 4: 1, # 'n'
- 15: 0, # 'o'
- 26: 0, # 'p'
- 7: 0, # 'r'
- 8: 0, # 's'
- 9: 1, # 't'
- 14: 2, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 0, # 'y'
- 22: 1, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 1, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 1, # 'ö'
- 17: 0, # 'ü'
- 30: 1, # 'ğ'
- 41: 1, # 'İ'
- 6: 0, # 'ı'
- 40: 0, # 'Ş'
- 19: 1, # 'ş'
- },
- 59: { # 'â'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 1, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 1, # 'K'
- 49: 0, # 'L'
- 20: 0, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 2, # 'a'
- 21: 0, # 'b'
- 28: 0, # 'c'
- 12: 0, # 'd'
- 2: 2, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 0, # 'i'
- 24: 0, # 'j'
- 10: 0, # 'k'
- 5: 0, # 'l'
- 13: 2, # 'm'
- 4: 0, # 'n'
- 15: 1, # 'o'
- 26: 0, # 'p'
- 7: 0, # 'r'
- 8: 0, # 's'
- 9: 0, # 't'
- 14: 2, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 0, # 'y'
- 22: 1, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 0, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 1, # 'ı'
- 40: 1, # 'Ş'
- 19: 0, # 'ş'
- },
- 33: { # 'ç'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 3, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 0, # 'K'
- 49: 0, # 'L'
- 20: 1, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 2, # 'T'
- 51: 0, # 'U'
- 38: 1, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 0, # 'Z'
- 1: 0, # 'a'
- 21: 3, # 'b'
- 28: 0, # 'c'
- 12: 2, # 'd'
- 2: 0, # 'e'
- 18: 2, # 'f'
- 27: 1, # 'g'
- 25: 3, # 'h'
- 3: 3, # 'i'
- 24: 0, # 'j'
- 10: 3, # 'k'
- 5: 0, # 'l'
- 13: 0, # 'm'
- 4: 3, # 'n'
- 15: 0, # 'o'
- 26: 1, # 'p'
- 7: 3, # 'r'
- 8: 2, # 's'
- 9: 3, # 't'
- 14: 0, # 'u'
- 32: 2, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 2, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 1, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 3, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 61: { # 'î'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 0, # 'K'
- 49: 0, # 'L'
- 20: 0, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 0, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 1, # 'Z'
- 1: 2, # 'a'
- 21: 0, # 'b'
- 28: 0, # 'c'
- 12: 0, # 'd'
- 2: 2, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 0, # 'i'
- 24: 1, # 'j'
- 10: 0, # 'k'
- 5: 0, # 'l'
- 13: 1, # 'm'
- 4: 1, # 'n'
- 15: 0, # 'o'
- 26: 0, # 'p'
- 7: 0, # 'r'
- 8: 0, # 's'
- 9: 0, # 't'
- 14: 1, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 0, # 'y'
- 22: 1, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 1, # 'î'
- 34: 0, # 'ö'
- 17: 0, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 1, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 34: { # 'ö'
- 23: 0, # 'A'
- 37: 1, # 'B'
- 47: 1, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 2, # 'F'
- 36: 1, # 'G'
- 45: 1, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 3, # 'K'
- 49: 1, # 'L'
- 20: 0, # 'M'
- 46: 1, # 'N'
- 42: 1, # 'O'
- 48: 2, # 'P'
- 44: 1, # 'R'
- 35: 1, # 'S'
- 31: 1, # 'T'
- 51: 1, # 'U'
- 38: 1, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 1, # 'Z'
- 1: 3, # 'a'
- 21: 1, # 'b'
- 28: 2, # 'c'
- 12: 1, # 'd'
- 2: 3, # 'e'
- 18: 0, # 'f'
- 27: 2, # 'g'
- 25: 2, # 'h'
- 3: 1, # 'i'
- 24: 2, # 'j'
- 10: 1, # 'k'
- 5: 2, # 'l'
- 13: 3, # 'm'
- 4: 2, # 'n'
- 15: 2, # 'o'
- 26: 0, # 'p'
- 7: 0, # 'r'
- 8: 3, # 's'
- 9: 1, # 't'
- 14: 3, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 1, # 'y'
- 22: 2, # 'z'
- 63: 0, # '·'
- 54: 1, # 'Ç'
- 50: 2, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 2, # 'ç'
- 61: 0, # 'î'
- 34: 2, # 'ö'
- 17: 0, # 'ü'
- 30: 2, # 'ğ'
- 41: 1, # 'İ'
- 6: 1, # 'ı'
- 40: 2, # 'Ş'
- 19: 1, # 'ş'
- },
- 17: { # 'ü'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 1, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 0, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 1, # 'J'
- 16: 1, # 'K'
- 49: 0, # 'L'
- 20: 1, # 'M'
- 46: 0, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 1, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 0, # 'Y'
- 56: 1, # 'Z'
- 1: 3, # 'a'
- 21: 0, # 'b'
- 28: 0, # 'c'
- 12: 1, # 'd'
- 2: 3, # 'e'
- 18: 1, # 'f'
- 27: 2, # 'g'
- 25: 0, # 'h'
- 3: 1, # 'i'
- 24: 1, # 'j'
- 10: 2, # 'k'
- 5: 3, # 'l'
- 13: 2, # 'm'
- 4: 3, # 'n'
- 15: 0, # 'o'
- 26: 2, # 'p'
- 7: 2, # 'r'
- 8: 3, # 's'
- 9: 2, # 't'
- 14: 3, # 'u'
- 32: 1, # 'v'
- 57: 1, # 'w'
- 58: 0, # 'x'
- 11: 0, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 1, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 2, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 2, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 30: { # 'ğ'
- 23: 0, # 'A'
- 37: 2, # 'B'
- 47: 1, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 2, # 'F'
- 36: 1, # 'G'
- 45: 0, # 'H'
- 53: 1, # 'I'
- 60: 0, # 'J'
- 16: 3, # 'K'
- 49: 0, # 'L'
- 20: 1, # 'M'
- 46: 2, # 'N'
- 42: 2, # 'O'
- 48: 1, # 'P'
- 44: 1, # 'R'
- 35: 0, # 'S'
- 31: 1, # 'T'
- 51: 0, # 'U'
- 38: 2, # 'V'
- 62: 0, # 'W'
- 43: 2, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 0, # 'b'
- 28: 2, # 'c'
- 12: 0, # 'd'
- 2: 2, # 'e'
- 18: 0, # 'f'
- 27: 0, # 'g'
- 25: 0, # 'h'
- 3: 0, # 'i'
- 24: 3, # 'j'
- 10: 1, # 'k'
- 5: 2, # 'l'
- 13: 3, # 'm'
- 4: 0, # 'n'
- 15: 1, # 'o'
- 26: 0, # 'p'
- 7: 1, # 'r'
- 8: 0, # 's'
- 9: 0, # 't'
- 14: 3, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 0, # 'y'
- 22: 2, # 'z'
- 63: 0, # '·'
- 54: 2, # 'Ç'
- 50: 2, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 1, # 'ç'
- 61: 0, # 'î'
- 34: 2, # 'ö'
- 17: 0, # 'ü'
- 30: 1, # 'ğ'
- 41: 2, # 'İ'
- 6: 2, # 'ı'
- 40: 2, # 'Ş'
- 19: 1, # 'ş'
- },
- 41: { # 'İ'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 1, # 'C'
- 39: 1, # 'D'
- 29: 1, # 'E'
- 52: 0, # 'F'
- 36: 2, # 'G'
- 45: 2, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 0, # 'K'
- 49: 0, # 'L'
- 20: 2, # 'M'
- 46: 1, # 'N'
- 42: 1, # 'O'
- 48: 2, # 'P'
- 44: 0, # 'R'
- 35: 1, # 'S'
- 31: 1, # 'T'
- 51: 1, # 'U'
- 38: 1, # 'V'
- 62: 0, # 'W'
- 43: 2, # 'Y'
- 56: 0, # 'Z'
- 1: 1, # 'a'
- 21: 2, # 'b'
- 28: 1, # 'c'
- 12: 2, # 'd'
- 2: 1, # 'e'
- 18: 0, # 'f'
- 27: 3, # 'g'
- 25: 2, # 'h'
- 3: 2, # 'i'
- 24: 2, # 'j'
- 10: 2, # 'k'
- 5: 0, # 'l'
- 13: 1, # 'm'
- 4: 3, # 'n'
- 15: 1, # 'o'
- 26: 1, # 'p'
- 7: 3, # 'r'
- 8: 3, # 's'
- 9: 2, # 't'
- 14: 0, # 'u'
- 32: 0, # 'v'
- 57: 1, # 'w'
- 58: 0, # 'x'
- 11: 2, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 1, # 'Ü'
- 59: 1, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 1, # 'ö'
- 17: 1, # 'ü'
- 30: 2, # 'ğ'
- 41: 0, # 'İ'
- 6: 3, # 'ı'
- 40: 0, # 'Ş'
- 19: 1, # 'ş'
- },
- 6: { # 'ı'
- 23: 2, # 'A'
- 37: 0, # 'B'
- 47: 0, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 0, # 'F'
- 36: 1, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 2, # 'J'
- 16: 3, # 'K'
- 49: 0, # 'L'
- 20: 3, # 'M'
- 46: 1, # 'N'
- 42: 0, # 'O'
- 48: 0, # 'P'
- 44: 0, # 'R'
- 35: 0, # 'S'
- 31: 2, # 'T'
- 51: 0, # 'U'
- 38: 0, # 'V'
- 62: 0, # 'W'
- 43: 2, # 'Y'
- 56: 1, # 'Z'
- 1: 3, # 'a'
- 21: 2, # 'b'
- 28: 1, # 'c'
- 12: 3, # 'd'
- 2: 3, # 'e'
- 18: 3, # 'f'
- 27: 3, # 'g'
- 25: 2, # 'h'
- 3: 3, # 'i'
- 24: 3, # 'j'
- 10: 3, # 'k'
- 5: 3, # 'l'
- 13: 3, # 'm'
- 4: 3, # 'n'
- 15: 0, # 'o'
- 26: 3, # 'p'
- 7: 3, # 'r'
- 8: 3, # 's'
- 9: 3, # 't'
- 14: 3, # 'u'
- 32: 3, # 'v'
- 57: 1, # 'w'
- 58: 1, # 'x'
- 11: 3, # 'y'
- 22: 0, # 'z'
- 63: 1, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 2, # 'ç'
- 61: 0, # 'î'
- 34: 0, # 'ö'
- 17: 3, # 'ü'
- 30: 0, # 'ğ'
- 41: 0, # 'İ'
- 6: 3, # 'ı'
- 40: 0, # 'Ş'
- 19: 0, # 'ş'
- },
- 40: { # 'Ş'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 1, # 'C'
- 39: 1, # 'D'
- 29: 1, # 'E'
- 52: 0, # 'F'
- 36: 1, # 'G'
- 45: 2, # 'H'
- 53: 1, # 'I'
- 60: 0, # 'J'
- 16: 0, # 'K'
- 49: 0, # 'L'
- 20: 2, # 'M'
- 46: 1, # 'N'
- 42: 1, # 'O'
- 48: 2, # 'P'
- 44: 2, # 'R'
- 35: 1, # 'S'
- 31: 1, # 'T'
- 51: 0, # 'U'
- 38: 1, # 'V'
- 62: 0, # 'W'
- 43: 2, # 'Y'
- 56: 1, # 'Z'
- 1: 0, # 'a'
- 21: 2, # 'b'
- 28: 0, # 'c'
- 12: 2, # 'd'
- 2: 0, # 'e'
- 18: 3, # 'f'
- 27: 0, # 'g'
- 25: 2, # 'h'
- 3: 3, # 'i'
- 24: 2, # 'j'
- 10: 1, # 'k'
- 5: 0, # 'l'
- 13: 1, # 'm'
- 4: 3, # 'n'
- 15: 2, # 'o'
- 26: 0, # 'p'
- 7: 3, # 'r'
- 8: 2, # 's'
- 9: 2, # 't'
- 14: 1, # 'u'
- 32: 3, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 2, # 'y'
- 22: 0, # 'z'
- 63: 0, # '·'
- 54: 0, # 'Ç'
- 50: 0, # 'Ö'
- 55: 1, # 'Ü'
- 59: 0, # 'â'
- 33: 0, # 'ç'
- 61: 0, # 'î'
- 34: 2, # 'ö'
- 17: 1, # 'ü'
- 30: 2, # 'ğ'
- 41: 0, # 'İ'
- 6: 2, # 'ı'
- 40: 1, # 'Ş'
- 19: 2, # 'ş'
- },
- 19: { # 'ş'
- 23: 0, # 'A'
- 37: 0, # 'B'
- 47: 1, # 'C'
- 39: 0, # 'D'
- 29: 0, # 'E'
- 52: 2, # 'F'
- 36: 1, # 'G'
- 45: 0, # 'H'
- 53: 0, # 'I'
- 60: 0, # 'J'
- 16: 3, # 'K'
- 49: 2, # 'L'
- 20: 0, # 'M'
- 46: 1, # 'N'
- 42: 1, # 'O'
- 48: 1, # 'P'
- 44: 1, # 'R'
- 35: 1, # 'S'
- 31: 0, # 'T'
- 51: 1, # 'U'
- 38: 1, # 'V'
- 62: 0, # 'W'
- 43: 1, # 'Y'
- 56: 0, # 'Z'
- 1: 3, # 'a'
- 21: 1, # 'b'
- 28: 2, # 'c'
- 12: 0, # 'd'
- 2: 3, # 'e'
- 18: 0, # 'f'
- 27: 2, # 'g'
- 25: 1, # 'h'
- 3: 1, # 'i'
- 24: 0, # 'j'
- 10: 2, # 'k'
- 5: 2, # 'l'
- 13: 3, # 'm'
- 4: 0, # 'n'
- 15: 0, # 'o'
- 26: 1, # 'p'
- 7: 3, # 'r'
- 8: 0, # 's'
- 9: 0, # 't'
- 14: 3, # 'u'
- 32: 0, # 'v'
- 57: 0, # 'w'
- 58: 0, # 'x'
- 11: 0, # 'y'
- 22: 2, # 'z'
- 63: 0, # '·'
- 54: 1, # 'Ç'
- 50: 2, # 'Ö'
- 55: 0, # 'Ü'
- 59: 0, # 'â'
- 33: 1, # 'ç'
- 61: 1, # 'î'
- 34: 2, # 'ö'
- 17: 0, # 'ü'
- 30: 1, # 'ğ'
- 41: 1, # 'İ'
- 6: 1, # 'ı'
- 40: 1, # 'Ş'
- 19: 1, # 'ş'
- },
-}
-
-# 255: Undefined characters that did not exist in training text
-# 254: Carriage/Return
-# 253: symbol (punctuation) that does not belong to word
-# 252: 0 - 9
-# 251: Control characters
-
-# Character Mapping Table(s):
-ISO_8859_9_TURKISH_CHAR_TO_ORDER = {
- 0: 255, # '\x00'
- 1: 255, # '\x01'
- 2: 255, # '\x02'
- 3: 255, # '\x03'
- 4: 255, # '\x04'
- 5: 255, # '\x05'
- 6: 255, # '\x06'
- 7: 255, # '\x07'
- 8: 255, # '\x08'
- 9: 255, # '\t'
- 10: 255, # '\n'
- 11: 255, # '\x0b'
- 12: 255, # '\x0c'
- 13: 255, # '\r'
- 14: 255, # '\x0e'
- 15: 255, # '\x0f'
- 16: 255, # '\x10'
- 17: 255, # '\x11'
- 18: 255, # '\x12'
- 19: 255, # '\x13'
- 20: 255, # '\x14'
- 21: 255, # '\x15'
- 22: 255, # '\x16'
- 23: 255, # '\x17'
- 24: 255, # '\x18'
- 25: 255, # '\x19'
- 26: 255, # '\x1a'
- 27: 255, # '\x1b'
- 28: 255, # '\x1c'
- 29: 255, # '\x1d'
- 30: 255, # '\x1e'
- 31: 255, # '\x1f'
- 32: 255, # ' '
- 33: 255, # '!'
- 34: 255, # '"'
- 35: 255, # '#'
- 36: 255, # '$'
- 37: 255, # '%'
- 38: 255, # '&'
- 39: 255, # "'"
- 40: 255, # '('
- 41: 255, # ')'
- 42: 255, # '*'
- 43: 255, # '+'
- 44: 255, # ','
- 45: 255, # '-'
- 46: 255, # '.'
- 47: 255, # '/'
- 48: 255, # '0'
- 49: 255, # '1'
- 50: 255, # '2'
- 51: 255, # '3'
- 52: 255, # '4'
- 53: 255, # '5'
- 54: 255, # '6'
- 55: 255, # '7'
- 56: 255, # '8'
- 57: 255, # '9'
- 58: 255, # ':'
- 59: 255, # ';'
- 60: 255, # '<'
- 61: 255, # '='
- 62: 255, # '>'
- 63: 255, # '?'
- 64: 255, # '@'
- 65: 23, # 'A'
- 66: 37, # 'B'
- 67: 47, # 'C'
- 68: 39, # 'D'
- 69: 29, # 'E'
- 70: 52, # 'F'
- 71: 36, # 'G'
- 72: 45, # 'H'
- 73: 53, # 'I'
- 74: 60, # 'J'
- 75: 16, # 'K'
- 76: 49, # 'L'
- 77: 20, # 'M'
- 78: 46, # 'N'
- 79: 42, # 'O'
- 80: 48, # 'P'
- 81: 69, # 'Q'
- 82: 44, # 'R'
- 83: 35, # 'S'
- 84: 31, # 'T'
- 85: 51, # 'U'
- 86: 38, # 'V'
- 87: 62, # 'W'
- 88: 65, # 'X'
- 89: 43, # 'Y'
- 90: 56, # 'Z'
- 91: 255, # '['
- 92: 255, # '\\'
- 93: 255, # ']'
- 94: 255, # '^'
- 95: 255, # '_'
- 96: 255, # '`'
- 97: 1, # 'a'
- 98: 21, # 'b'
- 99: 28, # 'c'
- 100: 12, # 'd'
- 101: 2, # 'e'
- 102: 18, # 'f'
- 103: 27, # 'g'
- 104: 25, # 'h'
- 105: 3, # 'i'
- 106: 24, # 'j'
- 107: 10, # 'k'
- 108: 5, # 'l'
- 109: 13, # 'm'
- 110: 4, # 'n'
- 111: 15, # 'o'
- 112: 26, # 'p'
- 113: 64, # 'q'
- 114: 7, # 'r'
- 115: 8, # 's'
- 116: 9, # 't'
- 117: 14, # 'u'
- 118: 32, # 'v'
- 119: 57, # 'w'
- 120: 58, # 'x'
- 121: 11, # 'y'
- 122: 22, # 'z'
- 123: 255, # '{'
- 124: 255, # '|'
- 125: 255, # '}'
- 126: 255, # '~'
- 127: 255, # '\x7f'
- 128: 180, # '\x80'
- 129: 179, # '\x81'
- 130: 178, # '\x82'
- 131: 177, # '\x83'
- 132: 176, # '\x84'
- 133: 175, # '\x85'
- 134: 174, # '\x86'
- 135: 173, # '\x87'
- 136: 172, # '\x88'
- 137: 171, # '\x89'
- 138: 170, # '\x8a'
- 139: 169, # '\x8b'
- 140: 168, # '\x8c'
- 141: 167, # '\x8d'
- 142: 166, # '\x8e'
- 143: 165, # '\x8f'
- 144: 164, # '\x90'
- 145: 163, # '\x91'
- 146: 162, # '\x92'
- 147: 161, # '\x93'
- 148: 160, # '\x94'
- 149: 159, # '\x95'
- 150: 101, # '\x96'
- 151: 158, # '\x97'
- 152: 157, # '\x98'
- 153: 156, # '\x99'
- 154: 155, # '\x9a'
- 155: 154, # '\x9b'
- 156: 153, # '\x9c'
- 157: 152, # '\x9d'
- 158: 151, # '\x9e'
- 159: 106, # '\x9f'
- 160: 150, # '\xa0'
- 161: 149, # '¡'
- 162: 148, # '¢'
- 163: 147, # '£'
- 164: 146, # '¤'
- 165: 145, # '¥'
- 166: 144, # '¦'
- 167: 100, # '§'
- 168: 143, # '¨'
- 169: 142, # '©'
- 170: 141, # 'ª'
- 171: 140, # '«'
- 172: 139, # '¬'
- 173: 138, # '\xad'
- 174: 137, # '®'
- 175: 136, # '¯'
- 176: 94, # '°'
- 177: 80, # '±'
- 178: 93, # '²'
- 179: 135, # '³'
- 180: 105, # '´'
- 181: 134, # 'µ'
- 182: 133, # '¶'
- 183: 63, # '·'
- 184: 132, # '¸'
- 185: 131, # '¹'
- 186: 130, # 'º'
- 187: 129, # '»'
- 188: 128, # '¼'
- 189: 127, # '½'
- 190: 126, # '¾'
- 191: 125, # '¿'
- 192: 124, # 'À'
- 193: 104, # 'Á'
- 194: 73, # 'Â'
- 195: 99, # 'Ã'
- 196: 79, # 'Ä'
- 197: 85, # 'Å'
- 198: 123, # 'Æ'
- 199: 54, # 'Ç'
- 200: 122, # 'È'
- 201: 98, # 'É'
- 202: 92, # 'Ê'
- 203: 121, # 'Ë'
- 204: 120, # 'Ì'
- 205: 91, # 'Í'
- 206: 103, # 'Î'
- 207: 119, # 'Ï'
- 208: 68, # 'Ğ'
- 209: 118, # 'Ñ'
- 210: 117, # 'Ò'
- 211: 97, # 'Ó'
- 212: 116, # 'Ô'
- 213: 115, # 'Õ'
- 214: 50, # 'Ö'
- 215: 90, # '×'
- 216: 114, # 'Ø'
- 217: 113, # 'Ù'
- 218: 112, # 'Ú'
- 219: 111, # 'Û'
- 220: 55, # 'Ü'
- 221: 41, # 'İ'
- 222: 40, # 'Ş'
- 223: 86, # 'ß'
- 224: 89, # 'à'
- 225: 70, # 'á'
- 226: 59, # 'â'
- 227: 78, # 'ã'
- 228: 71, # 'ä'
- 229: 82, # 'å'
- 230: 88, # 'æ'
- 231: 33, # 'ç'
- 232: 77, # 'è'
- 233: 66, # 'é'
- 234: 84, # 'ê'
- 235: 83, # 'ë'
- 236: 110, # 'ì'
- 237: 75, # 'í'
- 238: 61, # 'î'
- 239: 96, # 'ï'
- 240: 30, # 'ğ'
- 241: 67, # 'ñ'
- 242: 109, # 'ò'
- 243: 74, # 'ó'
- 244: 87, # 'ô'
- 245: 102, # 'õ'
- 246: 34, # 'ö'
- 247: 95, # '÷'
- 248: 81, # 'ø'
- 249: 108, # 'ù'
- 250: 76, # 'ú'
- 251: 72, # 'û'
- 252: 17, # 'ü'
- 253: 6, # 'ı'
- 254: 19, # 'ş'
- 255: 107, # 'ÿ'
-}
-
-ISO_8859_9_TURKISH_MODEL = SingleByteCharSetModel(
- charset_name="ISO-8859-9",
- language="Turkish",
- char_to_order_map=ISO_8859_9_TURKISH_CHAR_TO_ORDER,
- language_model=TURKISH_LANG_MODEL,
- typical_positive_ratio=0.97029,
- keep_ascii_letters=True,
- alphabet="ABCDEFGHIJKLMNOPRSTUVYZabcdefghijklmnoprstuvyzÂÇÎÖÛÜâçîöûüĞğİıŞş",
-)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/latin1prober.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/latin1prober.py
deleted file mode 100644
index 59a01d9..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/latin1prober.py
+++ /dev/null
@@ -1,147 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is Mozilla Universal charset detector code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 2001
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-# Shy Shalom - original C code
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-from typing import List, Union
-
-from .charsetprober import CharSetProber
-from .enums import ProbingState
-
-FREQ_CAT_NUM = 4
-
-UDF = 0 # undefined
-OTH = 1 # other
-ASC = 2 # ascii capital letter
-ASS = 3 # ascii small letter
-ACV = 4 # accent capital vowel
-ACO = 5 # accent capital other
-ASV = 6 # accent small vowel
-ASO = 7 # accent small other
-CLASS_NUM = 8 # total classes
-
-# fmt: off
-Latin1_CharToClass = (
- OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 00 - 07
- OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 08 - 0F
- OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 10 - 17
- OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 18 - 1F
- OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 20 - 27
- OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 28 - 2F
- OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 30 - 37
- OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 38 - 3F
- OTH, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 40 - 47
- ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 48 - 4F
- ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 50 - 57
- ASC, ASC, ASC, OTH, OTH, OTH, OTH, OTH, # 58 - 5F
- OTH, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 60 - 67
- ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 68 - 6F
- ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 70 - 77
- ASS, ASS, ASS, OTH, OTH, OTH, OTH, OTH, # 78 - 7F
- OTH, UDF, OTH, ASO, OTH, OTH, OTH, OTH, # 80 - 87
- OTH, OTH, ACO, OTH, ACO, UDF, ACO, UDF, # 88 - 8F
- UDF, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 90 - 97
- OTH, OTH, ASO, OTH, ASO, UDF, ASO, ACO, # 98 - 9F
- OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # A0 - A7
- OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # A8 - AF
- OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # B0 - B7
- OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # B8 - BF
- ACV, ACV, ACV, ACV, ACV, ACV, ACO, ACO, # C0 - C7
- ACV, ACV, ACV, ACV, ACV, ACV, ACV, ACV, # C8 - CF
- ACO, ACO, ACV, ACV, ACV, ACV, ACV, OTH, # D0 - D7
- ACV, ACV, ACV, ACV, ACV, ACO, ACO, ACO, # D8 - DF
- ASV, ASV, ASV, ASV, ASV, ASV, ASO, ASO, # E0 - E7
- ASV, ASV, ASV, ASV, ASV, ASV, ASV, ASV, # E8 - EF
- ASO, ASO, ASV, ASV, ASV, ASV, ASV, OTH, # F0 - F7
- ASV, ASV, ASV, ASV, ASV, ASO, ASO, ASO, # F8 - FF
-)
-
-# 0 : illegal
-# 1 : very unlikely
-# 2 : normal
-# 3 : very likely
-Latin1ClassModel = (
-# UDF OTH ASC ASS ACV ACO ASV ASO
- 0, 0, 0, 0, 0, 0, 0, 0, # UDF
- 0, 3, 3, 3, 3, 3, 3, 3, # OTH
- 0, 3, 3, 3, 3, 3, 3, 3, # ASC
- 0, 3, 3, 3, 1, 1, 3, 3, # ASS
- 0, 3, 3, 3, 1, 2, 1, 2, # ACV
- 0, 3, 3, 3, 3, 3, 3, 3, # ACO
- 0, 3, 1, 3, 1, 1, 1, 3, # ASV
- 0, 3, 1, 3, 1, 1, 3, 3, # ASO
-)
-# fmt: on
-
-
-class Latin1Prober(CharSetProber):
- def __init__(self) -> None:
- super().__init__()
- self._last_char_class = OTH
- self._freq_counter: List[int] = []
- self.reset()
-
- def reset(self) -> None:
- self._last_char_class = OTH
- self._freq_counter = [0] * FREQ_CAT_NUM
- super().reset()
-
- @property
- def charset_name(self) -> str:
- return "ISO-8859-1"
-
- @property
- def language(self) -> str:
- return ""
-
- def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState:
- byte_str = self.remove_xml_tags(byte_str)
- for c in byte_str:
- char_class = Latin1_CharToClass[c]
- freq = Latin1ClassModel[(self._last_char_class * CLASS_NUM) + char_class]
- if freq == 0:
- self._state = ProbingState.NOT_ME
- break
- self._freq_counter[freq] += 1
- self._last_char_class = char_class
-
- return self.state
-
- def get_confidence(self) -> float:
- if self.state == ProbingState.NOT_ME:
- return 0.01
-
- total = sum(self._freq_counter)
- confidence = (
- 0.0
- if total < 0.01
- else (self._freq_counter[3] - self._freq_counter[1] * 20.0) / total
- )
- confidence = max(confidence, 0.0)
- # lower the confidence of latin1 so that other more accurate
- # detector can take priority.
- confidence *= 0.73
- return confidence
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/macromanprober.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/macromanprober.py
deleted file mode 100644
index 1425d10..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/macromanprober.py
+++ /dev/null
@@ -1,162 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# This code was modified from latin1prober.py by Rob Speer .
-# The Original Code is Mozilla Universal charset detector code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 2001
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Rob Speer - adapt to MacRoman encoding
-# Mark Pilgrim - port to Python
-# Shy Shalom - original C code
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-from typing import List, Union
-
-from .charsetprober import CharSetProber
-from .enums import ProbingState
-
-FREQ_CAT_NUM = 4
-
-UDF = 0 # undefined
-OTH = 1 # other
-ASC = 2 # ascii capital letter
-ASS = 3 # ascii small letter
-ACV = 4 # accent capital vowel
-ACO = 5 # accent capital other
-ASV = 6 # accent small vowel
-ASO = 7 # accent small other
-ODD = 8 # character that is unlikely to appear
-CLASS_NUM = 9 # total classes
-
-# The change from Latin1 is that we explicitly look for extended characters
-# that are infrequently-occurring symbols, and consider them to always be
-# improbable. This should let MacRoman get out of the way of more likely
-# encodings in most situations.
-
-# fmt: off
-MacRoman_CharToClass = (
- OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 00 - 07
- OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 08 - 0F
- OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 10 - 17
- OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 18 - 1F
- OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 20 - 27
- OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 28 - 2F
- OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 30 - 37
- OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 38 - 3F
- OTH, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 40 - 47
- ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 48 - 4F
- ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 50 - 57
- ASC, ASC, ASC, OTH, OTH, OTH, OTH, OTH, # 58 - 5F
- OTH, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 60 - 67
- ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 68 - 6F
- ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 70 - 77
- ASS, ASS, ASS, OTH, OTH, OTH, OTH, OTH, # 78 - 7F
- ACV, ACV, ACO, ACV, ACO, ACV, ACV, ASV, # 80 - 87
- ASV, ASV, ASV, ASV, ASV, ASO, ASV, ASV, # 88 - 8F
- ASV, ASV, ASV, ASV, ASV, ASV, ASO, ASV, # 90 - 97
- ASV, ASV, ASV, ASV, ASV, ASV, ASV, ASV, # 98 - 9F
- OTH, OTH, OTH, OTH, OTH, OTH, OTH, ASO, # A0 - A7
- OTH, OTH, ODD, ODD, OTH, OTH, ACV, ACV, # A8 - AF
- OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # B0 - B7
- OTH, OTH, OTH, OTH, OTH, OTH, ASV, ASV, # B8 - BF
- OTH, OTH, ODD, OTH, ODD, OTH, OTH, OTH, # C0 - C7
- OTH, OTH, OTH, ACV, ACV, ACV, ACV, ASV, # C8 - CF
- OTH, OTH, OTH, OTH, OTH, OTH, OTH, ODD, # D0 - D7
- ASV, ACV, ODD, OTH, OTH, OTH, OTH, OTH, # D8 - DF
- OTH, OTH, OTH, OTH, OTH, ACV, ACV, ACV, # E0 - E7
- ACV, ACV, ACV, ACV, ACV, ACV, ACV, ACV, # E8 - EF
- ODD, ACV, ACV, ACV, ACV, ASV, ODD, ODD, # F0 - F7
- ODD, ODD, ODD, ODD, ODD, ODD, ODD, ODD, # F8 - FF
-)
-
-# 0 : illegal
-# 1 : very unlikely
-# 2 : normal
-# 3 : very likely
-MacRomanClassModel = (
-# UDF OTH ASC ASS ACV ACO ASV ASO ODD
- 0, 0, 0, 0, 0, 0, 0, 0, 0, # UDF
- 0, 3, 3, 3, 3, 3, 3, 3, 1, # OTH
- 0, 3, 3, 3, 3, 3, 3, 3, 1, # ASC
- 0, 3, 3, 3, 1, 1, 3, 3, 1, # ASS
- 0, 3, 3, 3, 1, 2, 1, 2, 1, # ACV
- 0, 3, 3, 3, 3, 3, 3, 3, 1, # ACO
- 0, 3, 1, 3, 1, 1, 1, 3, 1, # ASV
- 0, 3, 1, 3, 1, 1, 3, 3, 1, # ASO
- 0, 1, 1, 1, 1, 1, 1, 1, 1, # ODD
-)
-# fmt: on
-
-
-class MacRomanProber(CharSetProber):
- def __init__(self) -> None:
- super().__init__()
- self._last_char_class = OTH
- self._freq_counter: List[int] = []
- self.reset()
-
- def reset(self) -> None:
- self._last_char_class = OTH
- self._freq_counter = [0] * FREQ_CAT_NUM
-
- # express the prior that MacRoman is a somewhat rare encoding;
- # this can be done by starting out in a slightly improbable state
- # that must be overcome
- self._freq_counter[2] = 10
-
- super().reset()
-
- @property
- def charset_name(self) -> str:
- return "MacRoman"
-
- @property
- def language(self) -> str:
- return ""
-
- def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState:
- byte_str = self.remove_xml_tags(byte_str)
- for c in byte_str:
- char_class = MacRoman_CharToClass[c]
- freq = MacRomanClassModel[(self._last_char_class * CLASS_NUM) + char_class]
- if freq == 0:
- self._state = ProbingState.NOT_ME
- break
- self._freq_counter[freq] += 1
- self._last_char_class = char_class
-
- return self.state
-
- def get_confidence(self) -> float:
- if self.state == ProbingState.NOT_ME:
- return 0.01
-
- total = sum(self._freq_counter)
- confidence = (
- 0.0
- if total < 0.01
- else (self._freq_counter[3] - self._freq_counter[1] * 20.0) / total
- )
- confidence = max(confidence, 0.0)
- # lower the confidence of MacRoman so that other more accurate
- # detector can take priority.
- confidence *= 0.73
- return confidence
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/mbcharsetprober.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/mbcharsetprober.py
deleted file mode 100644
index 666307e..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/mbcharsetprober.py
+++ /dev/null
@@ -1,95 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is Mozilla Universal charset detector code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 2001
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-# Shy Shalom - original C code
-# Proofpoint, Inc.
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-from typing import Optional, Union
-
-from .chardistribution import CharDistributionAnalysis
-from .charsetprober import CharSetProber
-from .codingstatemachine import CodingStateMachine
-from .enums import LanguageFilter, MachineState, ProbingState
-
-
-class MultiByteCharSetProber(CharSetProber):
- """
- MultiByteCharSetProber
- """
-
- def __init__(self, lang_filter: LanguageFilter = LanguageFilter.NONE) -> None:
- super().__init__(lang_filter=lang_filter)
- self.distribution_analyzer: Optional[CharDistributionAnalysis] = None
- self.coding_sm: Optional[CodingStateMachine] = None
- self._last_char = bytearray(b"\0\0")
-
- def reset(self) -> None:
- super().reset()
- if self.coding_sm:
- self.coding_sm.reset()
- if self.distribution_analyzer:
- self.distribution_analyzer.reset()
- self._last_char = bytearray(b"\0\0")
-
- def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState:
- assert self.coding_sm is not None
- assert self.distribution_analyzer is not None
-
- for i, byte in enumerate(byte_str):
- coding_state = self.coding_sm.next_state(byte)
- if coding_state == MachineState.ERROR:
- self.logger.debug(
- "%s %s prober hit error at byte %s",
- self.charset_name,
- self.language,
- i,
- )
- self._state = ProbingState.NOT_ME
- break
- if coding_state == MachineState.ITS_ME:
- self._state = ProbingState.FOUND_IT
- break
- if coding_state == MachineState.START:
- char_len = self.coding_sm.get_current_charlen()
- if i == 0:
- self._last_char[1] = byte
- self.distribution_analyzer.feed(self._last_char, char_len)
- else:
- self.distribution_analyzer.feed(byte_str[i - 1 : i + 1], char_len)
-
- self._last_char[0] = byte_str[-1]
-
- if self.state == ProbingState.DETECTING:
- if self.distribution_analyzer.got_enough_data() and (
- self.get_confidence() > self.SHORTCUT_THRESHOLD
- ):
- self._state = ProbingState.FOUND_IT
-
- return self.state
-
- def get_confidence(self) -> float:
- assert self.distribution_analyzer is not None
- return self.distribution_analyzer.get_confidence()
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/mbcsgroupprober.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/mbcsgroupprober.py
deleted file mode 100644
index 6cb9cc7..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/mbcsgroupprober.py
+++ /dev/null
@@ -1,57 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is Mozilla Universal charset detector code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 2001
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-# Shy Shalom - original C code
-# Proofpoint, Inc.
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-from .big5prober import Big5Prober
-from .charsetgroupprober import CharSetGroupProber
-from .cp949prober import CP949Prober
-from .enums import LanguageFilter
-from .eucjpprober import EUCJPProber
-from .euckrprober import EUCKRProber
-from .euctwprober import EUCTWProber
-from .gb2312prober import GB2312Prober
-from .johabprober import JOHABProber
-from .sjisprober import SJISProber
-from .utf8prober import UTF8Prober
-
-
-class MBCSGroupProber(CharSetGroupProber):
- def __init__(self, lang_filter: LanguageFilter = LanguageFilter.NONE) -> None:
- super().__init__(lang_filter=lang_filter)
- self.probers = [
- UTF8Prober(),
- SJISProber(),
- EUCJPProber(),
- GB2312Prober(),
- EUCKRProber(),
- CP949Prober(),
- Big5Prober(),
- EUCTWProber(),
- JOHABProber(),
- ]
- self.reset()
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/mbcssm.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/mbcssm.py
deleted file mode 100644
index 7bbe97e..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/mbcssm.py
+++ /dev/null
@@ -1,661 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is mozilla.org code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-from .codingstatemachinedict import CodingStateMachineDict
-from .enums import MachineState
-
-# BIG5
-
-# fmt: off
-BIG5_CLS = (
- 1, 1, 1, 1, 1, 1, 1, 1, # 00 - 07 #allow 0x00 as legal value
- 1, 1, 1, 1, 1, 1, 0, 0, # 08 - 0f
- 1, 1, 1, 1, 1, 1, 1, 1, # 10 - 17
- 1, 1, 1, 0, 1, 1, 1, 1, # 18 - 1f
- 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 27
- 1, 1, 1, 1, 1, 1, 1, 1, # 28 - 2f
- 1, 1, 1, 1, 1, 1, 1, 1, # 30 - 37
- 1, 1, 1, 1, 1, 1, 1, 1, # 38 - 3f
- 2, 2, 2, 2, 2, 2, 2, 2, # 40 - 47
- 2, 2, 2, 2, 2, 2, 2, 2, # 48 - 4f
- 2, 2, 2, 2, 2, 2, 2, 2, # 50 - 57
- 2, 2, 2, 2, 2, 2, 2, 2, # 58 - 5f
- 2, 2, 2, 2, 2, 2, 2, 2, # 60 - 67
- 2, 2, 2, 2, 2, 2, 2, 2, # 68 - 6f
- 2, 2, 2, 2, 2, 2, 2, 2, # 70 - 77
- 2, 2, 2, 2, 2, 2, 2, 1, # 78 - 7f
- 4, 4, 4, 4, 4, 4, 4, 4, # 80 - 87
- 4, 4, 4, 4, 4, 4, 4, 4, # 88 - 8f
- 4, 4, 4, 4, 4, 4, 4, 4, # 90 - 97
- 4, 4, 4, 4, 4, 4, 4, 4, # 98 - 9f
- 4, 3, 3, 3, 3, 3, 3, 3, # a0 - a7
- 3, 3, 3, 3, 3, 3, 3, 3, # a8 - af
- 3, 3, 3, 3, 3, 3, 3, 3, # b0 - b7
- 3, 3, 3, 3, 3, 3, 3, 3, # b8 - bf
- 3, 3, 3, 3, 3, 3, 3, 3, # c0 - c7
- 3, 3, 3, 3, 3, 3, 3, 3, # c8 - cf
- 3, 3, 3, 3, 3, 3, 3, 3, # d0 - d7
- 3, 3, 3, 3, 3, 3, 3, 3, # d8 - df
- 3, 3, 3, 3, 3, 3, 3, 3, # e0 - e7
- 3, 3, 3, 3, 3, 3, 3, 3, # e8 - ef
- 3, 3, 3, 3, 3, 3, 3, 3, # f0 - f7
- 3, 3, 3, 3, 3, 3, 3, 0 # f8 - ff
-)
-
-BIG5_ST = (
- MachineState.ERROR,MachineState.START,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07
- MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,#08-0f
- MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START#10-17
-)
-# fmt: on
-
-BIG5_CHAR_LEN_TABLE = (0, 1, 1, 2, 0)
-
-BIG5_SM_MODEL: CodingStateMachineDict = {
- "class_table": BIG5_CLS,
- "class_factor": 5,
- "state_table": BIG5_ST,
- "char_len_table": BIG5_CHAR_LEN_TABLE,
- "name": "Big5",
-}
-
-# CP949
-# fmt: off
-CP949_CLS = (
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, # 00 - 0f
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, # 10 - 1f
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 2f
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 30 - 3f
- 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, # 40 - 4f
- 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 1, 1, 1, # 50 - 5f
- 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, # 60 - 6f
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 1, 1, 1, # 70 - 7f
- 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, # 80 - 8f
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, # 90 - 9f
- 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, # a0 - af
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, # b0 - bf
- 7, 7, 7, 7, 7, 7, 9, 2, 2, 3, 2, 2, 2, 2, 2, 2, # c0 - cf
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, # d0 - df
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, # e0 - ef
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, # f0 - ff
-)
-
-CP949_ST = (
-#cls= 0 1 2 3 4 5 6 7 8 9 # previous state =
- MachineState.ERROR,MachineState.START, 3,MachineState.ERROR,MachineState.START,MachineState.START, 4, 5,MachineState.ERROR, 6, # MachineState.START
- MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, # MachineState.ERROR
- MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME, # MachineState.ITS_ME
- MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START, # 3
- MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, # 4
- MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, # 5
- MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START, # 6
-)
-# fmt: on
-
-CP949_CHAR_LEN_TABLE = (0, 1, 2, 0, 1, 1, 2, 2, 0, 2)
-
-CP949_SM_MODEL: CodingStateMachineDict = {
- "class_table": CP949_CLS,
- "class_factor": 10,
- "state_table": CP949_ST,
- "char_len_table": CP949_CHAR_LEN_TABLE,
- "name": "CP949",
-}
-
-# EUC-JP
-# fmt: off
-EUCJP_CLS = (
- 4, 4, 4, 4, 4, 4, 4, 4, # 00 - 07
- 4, 4, 4, 4, 4, 4, 5, 5, # 08 - 0f
- 4, 4, 4, 4, 4, 4, 4, 4, # 10 - 17
- 4, 4, 4, 5, 4, 4, 4, 4, # 18 - 1f
- 4, 4, 4, 4, 4, 4, 4, 4, # 20 - 27
- 4, 4, 4, 4, 4, 4, 4, 4, # 28 - 2f
- 4, 4, 4, 4, 4, 4, 4, 4, # 30 - 37
- 4, 4, 4, 4, 4, 4, 4, 4, # 38 - 3f
- 4, 4, 4, 4, 4, 4, 4, 4, # 40 - 47
- 4, 4, 4, 4, 4, 4, 4, 4, # 48 - 4f
- 4, 4, 4, 4, 4, 4, 4, 4, # 50 - 57
- 4, 4, 4, 4, 4, 4, 4, 4, # 58 - 5f
- 4, 4, 4, 4, 4, 4, 4, 4, # 60 - 67
- 4, 4, 4, 4, 4, 4, 4, 4, # 68 - 6f
- 4, 4, 4, 4, 4, 4, 4, 4, # 70 - 77
- 4, 4, 4, 4, 4, 4, 4, 4, # 78 - 7f
- 5, 5, 5, 5, 5, 5, 5, 5, # 80 - 87
- 5, 5, 5, 5, 5, 5, 1, 3, # 88 - 8f
- 5, 5, 5, 5, 5, 5, 5, 5, # 90 - 97
- 5, 5, 5, 5, 5, 5, 5, 5, # 98 - 9f
- 5, 2, 2, 2, 2, 2, 2, 2, # a0 - a7
- 2, 2, 2, 2, 2, 2, 2, 2, # a8 - af
- 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7
- 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf
- 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7
- 2, 2, 2, 2, 2, 2, 2, 2, # c8 - cf
- 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7
- 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df
- 0, 0, 0, 0, 0, 0, 0, 0, # e0 - e7
- 0, 0, 0, 0, 0, 0, 0, 0, # e8 - ef
- 0, 0, 0, 0, 0, 0, 0, 0, # f0 - f7
- 0, 0, 0, 0, 0, 0, 0, 5 # f8 - ff
-)
-
-EUCJP_ST = (
- 3, 4, 3, 5,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07
- MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f
- MachineState.ITS_ME,MachineState.ITS_ME,MachineState.START,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#10-17
- MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 3,MachineState.ERROR,#18-1f
- 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START#20-27
-)
-# fmt: on
-
-EUCJP_CHAR_LEN_TABLE = (2, 2, 2, 3, 1, 0)
-
-EUCJP_SM_MODEL: CodingStateMachineDict = {
- "class_table": EUCJP_CLS,
- "class_factor": 6,
- "state_table": EUCJP_ST,
- "char_len_table": EUCJP_CHAR_LEN_TABLE,
- "name": "EUC-JP",
-}
-
-# EUC-KR
-# fmt: off
-EUCKR_CLS = (
- 1, 1, 1, 1, 1, 1, 1, 1, # 00 - 07
- 1, 1, 1, 1, 1, 1, 0, 0, # 08 - 0f
- 1, 1, 1, 1, 1, 1, 1, 1, # 10 - 17
- 1, 1, 1, 0, 1, 1, 1, 1, # 18 - 1f
- 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 27
- 1, 1, 1, 1, 1, 1, 1, 1, # 28 - 2f
- 1, 1, 1, 1, 1, 1, 1, 1, # 30 - 37
- 1, 1, 1, 1, 1, 1, 1, 1, # 38 - 3f
- 1, 1, 1, 1, 1, 1, 1, 1, # 40 - 47
- 1, 1, 1, 1, 1, 1, 1, 1, # 48 - 4f
- 1, 1, 1, 1, 1, 1, 1, 1, # 50 - 57
- 1, 1, 1, 1, 1, 1, 1, 1, # 58 - 5f
- 1, 1, 1, 1, 1, 1, 1, 1, # 60 - 67
- 1, 1, 1, 1, 1, 1, 1, 1, # 68 - 6f
- 1, 1, 1, 1, 1, 1, 1, 1, # 70 - 77
- 1, 1, 1, 1, 1, 1, 1, 1, # 78 - 7f
- 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 87
- 0, 0, 0, 0, 0, 0, 0, 0, # 88 - 8f
- 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 97
- 0, 0, 0, 0, 0, 0, 0, 0, # 98 - 9f
- 0, 2, 2, 2, 2, 2, 2, 2, # a0 - a7
- 2, 2, 2, 2, 2, 3, 3, 3, # a8 - af
- 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7
- 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf
- 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7
- 2, 3, 2, 2, 2, 2, 2, 2, # c8 - cf
- 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7
- 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df
- 2, 2, 2, 2, 2, 2, 2, 2, # e0 - e7
- 2, 2, 2, 2, 2, 2, 2, 2, # e8 - ef
- 2, 2, 2, 2, 2, 2, 2, 2, # f0 - f7
- 2, 2, 2, 2, 2, 2, 2, 0 # f8 - ff
-)
-
-EUCKR_ST = (
- MachineState.ERROR,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07
- MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START #08-0f
-)
-# fmt: on
-
-EUCKR_CHAR_LEN_TABLE = (0, 1, 2, 0)
-
-EUCKR_SM_MODEL: CodingStateMachineDict = {
- "class_table": EUCKR_CLS,
- "class_factor": 4,
- "state_table": EUCKR_ST,
- "char_len_table": EUCKR_CHAR_LEN_TABLE,
- "name": "EUC-KR",
-}
-
-# JOHAB
-# fmt: off
-JOHAB_CLS = (
- 4,4,4,4,4,4,4,4, # 00 - 07
- 4,4,4,4,4,4,0,0, # 08 - 0f
- 4,4,4,4,4,4,4,4, # 10 - 17
- 4,4,4,0,4,4,4,4, # 18 - 1f
- 4,4,4,4,4,4,4,4, # 20 - 27
- 4,4,4,4,4,4,4,4, # 28 - 2f
- 4,3,3,3,3,3,3,3, # 30 - 37
- 3,3,3,3,3,3,3,3, # 38 - 3f
- 3,1,1,1,1,1,1,1, # 40 - 47
- 1,1,1,1,1,1,1,1, # 48 - 4f
- 1,1,1,1,1,1,1,1, # 50 - 57
- 1,1,1,1,1,1,1,1, # 58 - 5f
- 1,1,1,1,1,1,1,1, # 60 - 67
- 1,1,1,1,1,1,1,1, # 68 - 6f
- 1,1,1,1,1,1,1,1, # 70 - 77
- 1,1,1,1,1,1,1,2, # 78 - 7f
- 6,6,6,6,8,8,8,8, # 80 - 87
- 8,8,8,8,8,8,8,8, # 88 - 8f
- 8,7,7,7,7,7,7,7, # 90 - 97
- 7,7,7,7,7,7,7,7, # 98 - 9f
- 7,7,7,7,7,7,7,7, # a0 - a7
- 7,7,7,7,7,7,7,7, # a8 - af
- 7,7,7,7,7,7,7,7, # b0 - b7
- 7,7,7,7,7,7,7,7, # b8 - bf
- 7,7,7,7,7,7,7,7, # c0 - c7
- 7,7,7,7,7,7,7,7, # c8 - cf
- 7,7,7,7,5,5,5,5, # d0 - d7
- 5,9,9,9,9,9,9,5, # d8 - df
- 9,9,9,9,9,9,9,9, # e0 - e7
- 9,9,9,9,9,9,9,9, # e8 - ef
- 9,9,9,9,9,9,9,9, # f0 - f7
- 9,9,5,5,5,5,5,0 # f8 - ff
-)
-
-JOHAB_ST = (
-# cls = 0 1 2 3 4 5 6 7 8 9
- MachineState.ERROR ,MachineState.START ,MachineState.START ,MachineState.START ,MachineState.START ,MachineState.ERROR ,MachineState.ERROR ,3 ,3 ,4 , # MachineState.START
- MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME, # MachineState.ITS_ME
- MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR , # MachineState.ERROR
- MachineState.ERROR ,MachineState.START ,MachineState.START ,MachineState.ERROR ,MachineState.ERROR ,MachineState.START ,MachineState.START ,MachineState.START ,MachineState.START ,MachineState.START , # 3
- MachineState.ERROR ,MachineState.START ,MachineState.ERROR ,MachineState.START ,MachineState.ERROR ,MachineState.START ,MachineState.ERROR ,MachineState.START ,MachineState.ERROR ,MachineState.START , # 4
-)
-# fmt: on
-
-JOHAB_CHAR_LEN_TABLE = (0, 1, 1, 1, 1, 0, 0, 2, 2, 2)
-
-JOHAB_SM_MODEL: CodingStateMachineDict = {
- "class_table": JOHAB_CLS,
- "class_factor": 10,
- "state_table": JOHAB_ST,
- "char_len_table": JOHAB_CHAR_LEN_TABLE,
- "name": "Johab",
-}
-
-# EUC-TW
-# fmt: off
-EUCTW_CLS = (
- 2, 2, 2, 2, 2, 2, 2, 2, # 00 - 07
- 2, 2, 2, 2, 2, 2, 0, 0, # 08 - 0f
- 2, 2, 2, 2, 2, 2, 2, 2, # 10 - 17
- 2, 2, 2, 0, 2, 2, 2, 2, # 18 - 1f
- 2, 2, 2, 2, 2, 2, 2, 2, # 20 - 27
- 2, 2, 2, 2, 2, 2, 2, 2, # 28 - 2f
- 2, 2, 2, 2, 2, 2, 2, 2, # 30 - 37
- 2, 2, 2, 2, 2, 2, 2, 2, # 38 - 3f
- 2, 2, 2, 2, 2, 2, 2, 2, # 40 - 47
- 2, 2, 2, 2, 2, 2, 2, 2, # 48 - 4f
- 2, 2, 2, 2, 2, 2, 2, 2, # 50 - 57
- 2, 2, 2, 2, 2, 2, 2, 2, # 58 - 5f
- 2, 2, 2, 2, 2, 2, 2, 2, # 60 - 67
- 2, 2, 2, 2, 2, 2, 2, 2, # 68 - 6f
- 2, 2, 2, 2, 2, 2, 2, 2, # 70 - 77
- 2, 2, 2, 2, 2, 2, 2, 2, # 78 - 7f
- 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 87
- 0, 0, 0, 0, 0, 0, 6, 0, # 88 - 8f
- 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 97
- 0, 0, 0, 0, 0, 0, 0, 0, # 98 - 9f
- 0, 3, 4, 4, 4, 4, 4, 4, # a0 - a7
- 5, 5, 1, 1, 1, 1, 1, 1, # a8 - af
- 1, 1, 1, 1, 1, 1, 1, 1, # b0 - b7
- 1, 1, 1, 1, 1, 1, 1, 1, # b8 - bf
- 1, 1, 3, 1, 3, 3, 3, 3, # c0 - c7
- 3, 3, 3, 3, 3, 3, 3, 3, # c8 - cf
- 3, 3, 3, 3, 3, 3, 3, 3, # d0 - d7
- 3, 3, 3, 3, 3, 3, 3, 3, # d8 - df
- 3, 3, 3, 3, 3, 3, 3, 3, # e0 - e7
- 3, 3, 3, 3, 3, 3, 3, 3, # e8 - ef
- 3, 3, 3, 3, 3, 3, 3, 3, # f0 - f7
- 3, 3, 3, 3, 3, 3, 3, 0 # f8 - ff
-)
-
-EUCTW_ST = (
- MachineState.ERROR,MachineState.ERROR,MachineState.START, 3, 3, 3, 4,MachineState.ERROR,#00-07
- MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f
- MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.START,MachineState.ERROR,#10-17
- MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f
- 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.START,MachineState.START,#20-27
- MachineState.START,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START #28-2f
-)
-# fmt: on
-
-EUCTW_CHAR_LEN_TABLE = (0, 0, 1, 2, 2, 2, 3)
-
-EUCTW_SM_MODEL: CodingStateMachineDict = {
- "class_table": EUCTW_CLS,
- "class_factor": 7,
- "state_table": EUCTW_ST,
- "char_len_table": EUCTW_CHAR_LEN_TABLE,
- "name": "x-euc-tw",
-}
-
-# GB2312
-# fmt: off
-GB2312_CLS = (
- 1, 1, 1, 1, 1, 1, 1, 1, # 00 - 07
- 1, 1, 1, 1, 1, 1, 0, 0, # 08 - 0f
- 1, 1, 1, 1, 1, 1, 1, 1, # 10 - 17
- 1, 1, 1, 0, 1, 1, 1, 1, # 18 - 1f
- 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 27
- 1, 1, 1, 1, 1, 1, 1, 1, # 28 - 2f
- 3, 3, 3, 3, 3, 3, 3, 3, # 30 - 37
- 3, 3, 1, 1, 1, 1, 1, 1, # 38 - 3f
- 2, 2, 2, 2, 2, 2, 2, 2, # 40 - 47
- 2, 2, 2, 2, 2, 2, 2, 2, # 48 - 4f
- 2, 2, 2, 2, 2, 2, 2, 2, # 50 - 57
- 2, 2, 2, 2, 2, 2, 2, 2, # 58 - 5f
- 2, 2, 2, 2, 2, 2, 2, 2, # 60 - 67
- 2, 2, 2, 2, 2, 2, 2, 2, # 68 - 6f
- 2, 2, 2, 2, 2, 2, 2, 2, # 70 - 77
- 2, 2, 2, 2, 2, 2, 2, 4, # 78 - 7f
- 5, 6, 6, 6, 6, 6, 6, 6, # 80 - 87
- 6, 6, 6, 6, 6, 6, 6, 6, # 88 - 8f
- 6, 6, 6, 6, 6, 6, 6, 6, # 90 - 97
- 6, 6, 6, 6, 6, 6, 6, 6, # 98 - 9f
- 6, 6, 6, 6, 6, 6, 6, 6, # a0 - a7
- 6, 6, 6, 6, 6, 6, 6, 6, # a8 - af
- 6, 6, 6, 6, 6, 6, 6, 6, # b0 - b7
- 6, 6, 6, 6, 6, 6, 6, 6, # b8 - bf
- 6, 6, 6, 6, 6, 6, 6, 6, # c0 - c7
- 6, 6, 6, 6, 6, 6, 6, 6, # c8 - cf
- 6, 6, 6, 6, 6, 6, 6, 6, # d0 - d7
- 6, 6, 6, 6, 6, 6, 6, 6, # d8 - df
- 6, 6, 6, 6, 6, 6, 6, 6, # e0 - e7
- 6, 6, 6, 6, 6, 6, 6, 6, # e8 - ef
- 6, 6, 6, 6, 6, 6, 6, 6, # f0 - f7
- 6, 6, 6, 6, 6, 6, 6, 0 # f8 - ff
-)
-
-GB2312_ST = (
- MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, 3,MachineState.ERROR,#00-07
- MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f
- MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,#10-17
- 4,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f
- MachineState.ERROR,MachineState.ERROR, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,#20-27
- MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START #28-2f
-)
-# fmt: on
-
-# To be accurate, the length of class 6 can be either 2 or 4.
-# But it is not necessary to discriminate between the two since
-# it is used for frequency analysis only, and we are validating
-# each code range there as well. So it is safe to set it to be
-# 2 here.
-GB2312_CHAR_LEN_TABLE = (0, 1, 1, 1, 1, 1, 2)
-
-GB2312_SM_MODEL: CodingStateMachineDict = {
- "class_table": GB2312_CLS,
- "class_factor": 7,
- "state_table": GB2312_ST,
- "char_len_table": GB2312_CHAR_LEN_TABLE,
- "name": "GB2312",
-}
-
-# Shift_JIS
-# fmt: off
-SJIS_CLS = (
- 1, 1, 1, 1, 1, 1, 1, 1, # 00 - 07
- 1, 1, 1, 1, 1, 1, 0, 0, # 08 - 0f
- 1, 1, 1, 1, 1, 1, 1, 1, # 10 - 17
- 1, 1, 1, 0, 1, 1, 1, 1, # 18 - 1f
- 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 27
- 1, 1, 1, 1, 1, 1, 1, 1, # 28 - 2f
- 1, 1, 1, 1, 1, 1, 1, 1, # 30 - 37
- 1, 1, 1, 1, 1, 1, 1, 1, # 38 - 3f
- 2, 2, 2, 2, 2, 2, 2, 2, # 40 - 47
- 2, 2, 2, 2, 2, 2, 2, 2, # 48 - 4f
- 2, 2, 2, 2, 2, 2, 2, 2, # 50 - 57
- 2, 2, 2, 2, 2, 2, 2, 2, # 58 - 5f
- 2, 2, 2, 2, 2, 2, 2, 2, # 60 - 67
- 2, 2, 2, 2, 2, 2, 2, 2, # 68 - 6f
- 2, 2, 2, 2, 2, 2, 2, 2, # 70 - 77
- 2, 2, 2, 2, 2, 2, 2, 1, # 78 - 7f
- 3, 3, 3, 3, 3, 2, 2, 3, # 80 - 87
- 3, 3, 3, 3, 3, 3, 3, 3, # 88 - 8f
- 3, 3, 3, 3, 3, 3, 3, 3, # 90 - 97
- 3, 3, 3, 3, 3, 3, 3, 3, # 98 - 9f
- #0xa0 is illegal in sjis encoding, but some pages does
- #contain such byte. We need to be more error forgiven.
- 2, 2, 2, 2, 2, 2, 2, 2, # a0 - a7
- 2, 2, 2, 2, 2, 2, 2, 2, # a8 - af
- 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7
- 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf
- 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7
- 2, 2, 2, 2, 2, 2, 2, 2, # c8 - cf
- 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7
- 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df
- 3, 3, 3, 3, 3, 3, 3, 3, # e0 - e7
- 3, 3, 3, 3, 3, 4, 4, 4, # e8 - ef
- 3, 3, 3, 3, 3, 3, 3, 3, # f0 - f7
- 3, 3, 3, 3, 3, 0, 0, 0, # f8 - ff
-)
-
-SJIS_ST = (
- MachineState.ERROR,MachineState.START,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07
- MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f
- MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START #10-17
-)
-# fmt: on
-
-SJIS_CHAR_LEN_TABLE = (0, 1, 1, 2, 0, 0)
-
-SJIS_SM_MODEL: CodingStateMachineDict = {
- "class_table": SJIS_CLS,
- "class_factor": 6,
- "state_table": SJIS_ST,
- "char_len_table": SJIS_CHAR_LEN_TABLE,
- "name": "Shift_JIS",
-}
-
-# UCS2-BE
-# fmt: off
-UCS2BE_CLS = (
- 0, 0, 0, 0, 0, 0, 0, 0, # 00 - 07
- 0, 0, 1, 0, 0, 2, 0, 0, # 08 - 0f
- 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17
- 0, 0, 0, 3, 0, 0, 0, 0, # 18 - 1f
- 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 27
- 0, 3, 3, 3, 3, 3, 0, 0, # 28 - 2f
- 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37
- 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f
- 0, 0, 0, 0, 0, 0, 0, 0, # 40 - 47
- 0, 0, 0, 0, 0, 0, 0, 0, # 48 - 4f
- 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57
- 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f
- 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67
- 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f
- 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77
- 0, 0, 0, 0, 0, 0, 0, 0, # 78 - 7f
- 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 87
- 0, 0, 0, 0, 0, 0, 0, 0, # 88 - 8f
- 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 97
- 0, 0, 0, 0, 0, 0, 0, 0, # 98 - 9f
- 0, 0, 0, 0, 0, 0, 0, 0, # a0 - a7
- 0, 0, 0, 0, 0, 0, 0, 0, # a8 - af
- 0, 0, 0, 0, 0, 0, 0, 0, # b0 - b7
- 0, 0, 0, 0, 0, 0, 0, 0, # b8 - bf
- 0, 0, 0, 0, 0, 0, 0, 0, # c0 - c7
- 0, 0, 0, 0, 0, 0, 0, 0, # c8 - cf
- 0, 0, 0, 0, 0, 0, 0, 0, # d0 - d7
- 0, 0, 0, 0, 0, 0, 0, 0, # d8 - df
- 0, 0, 0, 0, 0, 0, 0, 0, # e0 - e7
- 0, 0, 0, 0, 0, 0, 0, 0, # e8 - ef
- 0, 0, 0, 0, 0, 0, 0, 0, # f0 - f7
- 0, 0, 0, 0, 0, 0, 4, 5 # f8 - ff
-)
-
-UCS2BE_ST = (
- 5, 7, 7,MachineState.ERROR, 4, 3,MachineState.ERROR,MachineState.ERROR,#00-07
- MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f
- MachineState.ITS_ME,MachineState.ITS_ME, 6, 6, 6, 6,MachineState.ERROR,MachineState.ERROR,#10-17
- 6, 6, 6, 6, 6,MachineState.ITS_ME, 6, 6,#18-1f
- 6, 6, 6, 6, 5, 7, 7,MachineState.ERROR,#20-27
- 5, 8, 6, 6,MachineState.ERROR, 6, 6, 6,#28-2f
- 6, 6, 6, 6,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START #30-37
-)
-# fmt: on
-
-UCS2BE_CHAR_LEN_TABLE = (2, 2, 2, 0, 2, 2)
-
-UCS2BE_SM_MODEL: CodingStateMachineDict = {
- "class_table": UCS2BE_CLS,
- "class_factor": 6,
- "state_table": UCS2BE_ST,
- "char_len_table": UCS2BE_CHAR_LEN_TABLE,
- "name": "UTF-16BE",
-}
-
-# UCS2-LE
-# fmt: off
-UCS2LE_CLS = (
- 0, 0, 0, 0, 0, 0, 0, 0, # 00 - 07
- 0, 0, 1, 0, 0, 2, 0, 0, # 08 - 0f
- 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17
- 0, 0, 0, 3, 0, 0, 0, 0, # 18 - 1f
- 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 27
- 0, 3, 3, 3, 3, 3, 0, 0, # 28 - 2f
- 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37
- 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f
- 0, 0, 0, 0, 0, 0, 0, 0, # 40 - 47
- 0, 0, 0, 0, 0, 0, 0, 0, # 48 - 4f
- 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57
- 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f
- 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67
- 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f
- 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77
- 0, 0, 0, 0, 0, 0, 0, 0, # 78 - 7f
- 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 87
- 0, 0, 0, 0, 0, 0, 0, 0, # 88 - 8f
- 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 97
- 0, 0, 0, 0, 0, 0, 0, 0, # 98 - 9f
- 0, 0, 0, 0, 0, 0, 0, 0, # a0 - a7
- 0, 0, 0, 0, 0, 0, 0, 0, # a8 - af
- 0, 0, 0, 0, 0, 0, 0, 0, # b0 - b7
- 0, 0, 0, 0, 0, 0, 0, 0, # b8 - bf
- 0, 0, 0, 0, 0, 0, 0, 0, # c0 - c7
- 0, 0, 0, 0, 0, 0, 0, 0, # c8 - cf
- 0, 0, 0, 0, 0, 0, 0, 0, # d0 - d7
- 0, 0, 0, 0, 0, 0, 0, 0, # d8 - df
- 0, 0, 0, 0, 0, 0, 0, 0, # e0 - e7
- 0, 0, 0, 0, 0, 0, 0, 0, # e8 - ef
- 0, 0, 0, 0, 0, 0, 0, 0, # f0 - f7
- 0, 0, 0, 0, 0, 0, 4, 5 # f8 - ff
-)
-
-UCS2LE_ST = (
- 6, 6, 7, 6, 4, 3,MachineState.ERROR,MachineState.ERROR,#00-07
- MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f
- MachineState.ITS_ME,MachineState.ITS_ME, 5, 5, 5,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,#10-17
- 5, 5, 5,MachineState.ERROR, 5,MachineState.ERROR, 6, 6,#18-1f
- 7, 6, 8, 8, 5, 5, 5,MachineState.ERROR,#20-27
- 5, 5, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 5, 5,#28-2f
- 5, 5, 5,MachineState.ERROR, 5,MachineState.ERROR,MachineState.START,MachineState.START #30-37
-)
-# fmt: on
-
-UCS2LE_CHAR_LEN_TABLE = (2, 2, 2, 2, 2, 2)
-
-UCS2LE_SM_MODEL: CodingStateMachineDict = {
- "class_table": UCS2LE_CLS,
- "class_factor": 6,
- "state_table": UCS2LE_ST,
- "char_len_table": UCS2LE_CHAR_LEN_TABLE,
- "name": "UTF-16LE",
-}
-
-# UTF-8
-# fmt: off
-UTF8_CLS = (
- 1, 1, 1, 1, 1, 1, 1, 1, # 00 - 07 #allow 0x00 as a legal value
- 1, 1, 1, 1, 1, 1, 0, 0, # 08 - 0f
- 1, 1, 1, 1, 1, 1, 1, 1, # 10 - 17
- 1, 1, 1, 0, 1, 1, 1, 1, # 18 - 1f
- 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 27
- 1, 1, 1, 1, 1, 1, 1, 1, # 28 - 2f
- 1, 1, 1, 1, 1, 1, 1, 1, # 30 - 37
- 1, 1, 1, 1, 1, 1, 1, 1, # 38 - 3f
- 1, 1, 1, 1, 1, 1, 1, 1, # 40 - 47
- 1, 1, 1, 1, 1, 1, 1, 1, # 48 - 4f
- 1, 1, 1, 1, 1, 1, 1, 1, # 50 - 57
- 1, 1, 1, 1, 1, 1, 1, 1, # 58 - 5f
- 1, 1, 1, 1, 1, 1, 1, 1, # 60 - 67
- 1, 1, 1, 1, 1, 1, 1, 1, # 68 - 6f
- 1, 1, 1, 1, 1, 1, 1, 1, # 70 - 77
- 1, 1, 1, 1, 1, 1, 1, 1, # 78 - 7f
- 2, 2, 2, 2, 3, 3, 3, 3, # 80 - 87
- 4, 4, 4, 4, 4, 4, 4, 4, # 88 - 8f
- 4, 4, 4, 4, 4, 4, 4, 4, # 90 - 97
- 4, 4, 4, 4, 4, 4, 4, 4, # 98 - 9f
- 5, 5, 5, 5, 5, 5, 5, 5, # a0 - a7
- 5, 5, 5, 5, 5, 5, 5, 5, # a8 - af
- 5, 5, 5, 5, 5, 5, 5, 5, # b0 - b7
- 5, 5, 5, 5, 5, 5, 5, 5, # b8 - bf
- 0, 0, 6, 6, 6, 6, 6, 6, # c0 - c7
- 6, 6, 6, 6, 6, 6, 6, 6, # c8 - cf
- 6, 6, 6, 6, 6, 6, 6, 6, # d0 - d7
- 6, 6, 6, 6, 6, 6, 6, 6, # d8 - df
- 7, 8, 8, 8, 8, 8, 8, 8, # e0 - e7
- 8, 8, 8, 8, 8, 9, 8, 8, # e8 - ef
- 10, 11, 11, 11, 11, 11, 11, 11, # f0 - f7
- 12, 13, 13, 13, 14, 15, 0, 0 # f8 - ff
-)
-
-UTF8_ST = (
- MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 12, 10,#00-07
- 9, 11, 8, 7, 6, 5, 4, 3,#08-0f
- MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#10-17
- MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f
- MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#20-27
- MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#28-2f
- MachineState.ERROR,MachineState.ERROR, 5, 5, 5, 5,MachineState.ERROR,MachineState.ERROR,#30-37
- MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#38-3f
- MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 5, 5, 5,MachineState.ERROR,MachineState.ERROR,#40-47
- MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#48-4f
- MachineState.ERROR,MachineState.ERROR, 7, 7, 7, 7,MachineState.ERROR,MachineState.ERROR,#50-57
- MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#58-5f
- MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 7, 7,MachineState.ERROR,MachineState.ERROR,#60-67
- MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#68-6f
- MachineState.ERROR,MachineState.ERROR, 9, 9, 9, 9,MachineState.ERROR,MachineState.ERROR,#70-77
- MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#78-7f
- MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 9,MachineState.ERROR,MachineState.ERROR,#80-87
- MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#88-8f
- MachineState.ERROR,MachineState.ERROR, 12, 12, 12, 12,MachineState.ERROR,MachineState.ERROR,#90-97
- MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#98-9f
- MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 12,MachineState.ERROR,MachineState.ERROR,#a0-a7
- MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#a8-af
- MachineState.ERROR,MachineState.ERROR, 12, 12, 12,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#b0-b7
- MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#b8-bf
- MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,#c0-c7
- MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR #c8-cf
-)
-# fmt: on
-
-UTF8_CHAR_LEN_TABLE = (0, 1, 0, 0, 0, 0, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6)
-
-UTF8_SM_MODEL: CodingStateMachineDict = {
- "class_table": UTF8_CLS,
- "class_factor": 16,
- "state_table": UTF8_ST,
- "char_len_table": UTF8_CHAR_LEN_TABLE,
- "name": "UTF-8",
-}
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/metadata/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/metadata/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/metadata/languages.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/metadata/languages.py
deleted file mode 100644
index eb40c5f..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/metadata/languages.py
+++ /dev/null
@@ -1,352 +0,0 @@
-"""
-Metadata about languages used by our model training code for our
-SingleByteCharSetProbers. Could be used for other things in the future.
-
-This code is based on the language metadata from the uchardet project.
-"""
-
-from string import ascii_letters
-from typing import List, Optional
-
-# TODO: Add Ukrainian (KOI8-U)
-
-
-class Language:
- """Metadata about a language useful for training models
-
- :ivar name: The human name for the language, in English.
- :type name: str
- :ivar iso_code: 2-letter ISO 639-1 if possible, 3-letter ISO code otherwise,
- or use another catalog as a last resort.
- :type iso_code: str
- :ivar use_ascii: Whether or not ASCII letters should be included in trained
- models.
- :type use_ascii: bool
- :ivar charsets: The charsets we want to support and create data for.
- :type charsets: list of str
- :ivar alphabet: The characters in the language's alphabet. If `use_ascii` is
- `True`, you only need to add those not in the ASCII set.
- :type alphabet: str
- :ivar wiki_start_pages: The Wikipedia pages to start from if we're crawling
- Wikipedia for training data.
- :type wiki_start_pages: list of str
- """
-
- def __init__(
- self,
- name: Optional[str] = None,
- iso_code: Optional[str] = None,
- use_ascii: bool = True,
- charsets: Optional[List[str]] = None,
- alphabet: Optional[str] = None,
- wiki_start_pages: Optional[List[str]] = None,
- ) -> None:
- super().__init__()
- self.name = name
- self.iso_code = iso_code
- self.use_ascii = use_ascii
- self.charsets = charsets
- if self.use_ascii:
- if alphabet:
- alphabet += ascii_letters
- else:
- alphabet = ascii_letters
- elif not alphabet:
- raise ValueError("Must supply alphabet if use_ascii is False")
- self.alphabet = "".join(sorted(set(alphabet))) if alphabet else None
- self.wiki_start_pages = wiki_start_pages
-
- def __repr__(self) -> str:
- param_str = ", ".join(
- f"{k}={v!r}" for k, v in self.__dict__.items() if not k.startswith("_")
- )
- return f"{self.__class__.__name__}({param_str})"
-
-
-LANGUAGES = {
- "Arabic": Language(
- name="Arabic",
- iso_code="ar",
- use_ascii=False,
- # We only support encodings that use isolated
- # forms, because the current recommendation is
- # that the rendering system handles presentation
- # forms. This means we purposefully skip IBM864.
- charsets=["ISO-8859-6", "WINDOWS-1256", "CP720", "CP864"],
- alphabet="ءآأؤإئابةتثجحخدذرزسشصضطظعغػؼؽؾؿـفقكلمنهوىيًٌٍَُِّ",
- wiki_start_pages=["الصفحة_الرئيسية"],
- ),
- "Belarusian": Language(
- name="Belarusian",
- iso_code="be",
- use_ascii=False,
- charsets=["ISO-8859-5", "WINDOWS-1251", "IBM866", "MacCyrillic"],
- alphabet="АБВГДЕЁЖЗІЙКЛМНОПРСТУЎФХЦЧШЫЬЭЮЯабвгдеёжзійклмнопрстуўфхцчшыьэюяʼ",
- wiki_start_pages=["Галоўная_старонка"],
- ),
- "Bulgarian": Language(
- name="Bulgarian",
- iso_code="bg",
- use_ascii=False,
- charsets=["ISO-8859-5", "WINDOWS-1251", "IBM855"],
- alphabet="АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯабвгдежзийклмнопрстуфхцчшщъьюя",
- wiki_start_pages=["Начална_страница"],
- ),
- "Czech": Language(
- name="Czech",
- iso_code="cz",
- use_ascii=True,
- charsets=["ISO-8859-2", "WINDOWS-1250"],
- alphabet="áčďéěíňóřšťúůýžÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ",
- wiki_start_pages=["Hlavní_strana"],
- ),
- "Danish": Language(
- name="Danish",
- iso_code="da",
- use_ascii=True,
- charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"],
- alphabet="æøåÆØÅ",
- wiki_start_pages=["Forside"],
- ),
- "German": Language(
- name="German",
- iso_code="de",
- use_ascii=True,
- charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"],
- alphabet="äöüßẞÄÖÜ",
- wiki_start_pages=["Wikipedia:Hauptseite"],
- ),
- "Greek": Language(
- name="Greek",
- iso_code="el",
- use_ascii=False,
- charsets=["ISO-8859-7", "WINDOWS-1253"],
- alphabet="αβγδεζηθικλμνξοπρσςτυφχψωάέήίόύώΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΣΤΥΦΧΨΩΆΈΉΊΌΎΏ",
- wiki_start_pages=["Πύλη:Κύρια"],
- ),
- "English": Language(
- name="English",
- iso_code="en",
- use_ascii=True,
- charsets=["ISO-8859-1", "WINDOWS-1252", "MacRoman"],
- wiki_start_pages=["Main_Page"],
- ),
- "Esperanto": Language(
- name="Esperanto",
- iso_code="eo",
- # Q, W, X, and Y not used at all
- use_ascii=False,
- charsets=["ISO-8859-3"],
- alphabet="abcĉdefgĝhĥijĵklmnoprsŝtuŭvzABCĈDEFGĜHĤIJĴKLMNOPRSŜTUŬVZ",
- wiki_start_pages=["Vikipedio:Ĉefpaĝo"],
- ),
- "Spanish": Language(
- name="Spanish",
- iso_code="es",
- use_ascii=True,
- charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"],
- alphabet="ñáéíóúüÑÁÉÍÓÚÜ",
- wiki_start_pages=["Wikipedia:Portada"],
- ),
- "Estonian": Language(
- name="Estonian",
- iso_code="et",
- use_ascii=False,
- charsets=["ISO-8859-4", "ISO-8859-13", "WINDOWS-1257"],
- # C, F, Š, Q, W, X, Y, Z, Ž are only for
- # loanwords
- alphabet="ABDEGHIJKLMNOPRSTUVÕÄÖÜabdeghijklmnoprstuvõäöü",
- wiki_start_pages=["Esileht"],
- ),
- "Finnish": Language(
- name="Finnish",
- iso_code="fi",
- use_ascii=True,
- charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"],
- alphabet="ÅÄÖŠŽåäöšž",
- wiki_start_pages=["Wikipedia:Etusivu"],
- ),
- "French": Language(
- name="French",
- iso_code="fr",
- use_ascii=True,
- charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"],
- alphabet="œàâçèéîïùûêŒÀÂÇÈÉÎÏÙÛÊ",
- wiki_start_pages=["Wikipédia:Accueil_principal", "Bœuf (animal)"],
- ),
- "Hebrew": Language(
- name="Hebrew",
- iso_code="he",
- use_ascii=False,
- charsets=["ISO-8859-8", "WINDOWS-1255"],
- alphabet="אבגדהוזחטיךכלםמןנסעףפץצקרשתװױײ",
- wiki_start_pages=["עמוד_ראשי"],
- ),
- "Croatian": Language(
- name="Croatian",
- iso_code="hr",
- # Q, W, X, Y are only used for foreign words.
- use_ascii=False,
- charsets=["ISO-8859-2", "WINDOWS-1250"],
- alphabet="abcčćdđefghijklmnoprsštuvzžABCČĆDĐEFGHIJKLMNOPRSŠTUVZŽ",
- wiki_start_pages=["Glavna_stranica"],
- ),
- "Hungarian": Language(
- name="Hungarian",
- iso_code="hu",
- # Q, W, X, Y are only used for foreign words.
- use_ascii=False,
- charsets=["ISO-8859-2", "WINDOWS-1250"],
- alphabet="abcdefghijklmnoprstuvzáéíóöőúüűABCDEFGHIJKLMNOPRSTUVZÁÉÍÓÖŐÚÜŰ",
- wiki_start_pages=["Kezdőlap"],
- ),
- "Italian": Language(
- name="Italian",
- iso_code="it",
- use_ascii=True,
- charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"],
- alphabet="ÀÈÉÌÒÓÙàèéìòóù",
- wiki_start_pages=["Pagina_principale"],
- ),
- "Lithuanian": Language(
- name="Lithuanian",
- iso_code="lt",
- use_ascii=False,
- charsets=["ISO-8859-13", "WINDOWS-1257", "ISO-8859-4"],
- # Q, W, and X not used at all
- alphabet="AĄBCČDEĘĖFGHIĮYJKLMNOPRSŠTUŲŪVZŽaąbcčdeęėfghiįyjklmnoprsštuųūvzž",
- wiki_start_pages=["Pagrindinis_puslapis"],
- ),
- "Latvian": Language(
- name="Latvian",
- iso_code="lv",
- use_ascii=False,
- charsets=["ISO-8859-13", "WINDOWS-1257", "ISO-8859-4"],
- # Q, W, X, Y are only for loanwords
- alphabet="AĀBCČDEĒFGĢHIĪJKĶLĻMNŅOPRSŠTUŪVZŽaābcčdeēfgģhiījkķlļmnņoprsštuūvzž",
- wiki_start_pages=["Sākumlapa"],
- ),
- "Macedonian": Language(
- name="Macedonian",
- iso_code="mk",
- use_ascii=False,
- charsets=["ISO-8859-5", "WINDOWS-1251", "MacCyrillic", "IBM855"],
- alphabet="АБВГДЃЕЖЗЅИЈКЛЉМНЊОПРСТЌУФХЦЧЏШабвгдѓежзѕијклљмнњопрстќуфхцчџш",
- wiki_start_pages=["Главна_страница"],
- ),
- "Dutch": Language(
- name="Dutch",
- iso_code="nl",
- use_ascii=True,
- charsets=["ISO-8859-1", "WINDOWS-1252", "MacRoman"],
- wiki_start_pages=["Hoofdpagina"],
- ),
- "Polish": Language(
- name="Polish",
- iso_code="pl",
- # Q and X are only used for foreign words.
- use_ascii=False,
- charsets=["ISO-8859-2", "WINDOWS-1250"],
- alphabet="AĄBCĆDEĘFGHIJKLŁMNŃOÓPRSŚTUWYZŹŻaąbcćdeęfghijklłmnńoóprsśtuwyzźż",
- wiki_start_pages=["Wikipedia:Strona_główna"],
- ),
- "Portuguese": Language(
- name="Portuguese",
- iso_code="pt",
- use_ascii=True,
- charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"],
- alphabet="ÁÂÃÀÇÉÊÍÓÔÕÚáâãàçéêíóôõú",
- wiki_start_pages=["Wikipédia:Página_principal"],
- ),
- "Romanian": Language(
- name="Romanian",
- iso_code="ro",
- use_ascii=True,
- charsets=["ISO-8859-2", "WINDOWS-1250"],
- alphabet="ăâîșțĂÂÎȘȚ",
- wiki_start_pages=["Pagina_principală"],
- ),
- "Russian": Language(
- name="Russian",
- iso_code="ru",
- use_ascii=False,
- charsets=[
- "ISO-8859-5",
- "WINDOWS-1251",
- "KOI8-R",
- "MacCyrillic",
- "IBM866",
- "IBM855",
- ],
- alphabet="абвгдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ",
- wiki_start_pages=["Заглавная_страница"],
- ),
- "Slovak": Language(
- name="Slovak",
- iso_code="sk",
- use_ascii=True,
- charsets=["ISO-8859-2", "WINDOWS-1250"],
- alphabet="áäčďéíĺľňóôŕšťúýžÁÄČĎÉÍĹĽŇÓÔŔŠŤÚÝŽ",
- wiki_start_pages=["Hlavná_stránka"],
- ),
- "Slovene": Language(
- name="Slovene",
- iso_code="sl",
- # Q, W, X, Y are only used for foreign words.
- use_ascii=False,
- charsets=["ISO-8859-2", "WINDOWS-1250"],
- alphabet="abcčdefghijklmnoprsštuvzžABCČDEFGHIJKLMNOPRSŠTUVZŽ",
- wiki_start_pages=["Glavna_stran"],
- ),
- # Serbian can be written in both Latin and Cyrillic, but there's no
- # simple way to get the Latin alphabet pages from Wikipedia through
- # the API, so for now we just support Cyrillic.
- "Serbian": Language(
- name="Serbian",
- iso_code="sr",
- alphabet="АБВГДЂЕЖЗИЈКЛЉМНЊОПРСТЋУФХЦЧЏШабвгдђежзијклљмнњопрстћуфхцчџш",
- charsets=["ISO-8859-5", "WINDOWS-1251", "MacCyrillic", "IBM855"],
- wiki_start_pages=["Главна_страна"],
- ),
- "Thai": Language(
- name="Thai",
- iso_code="th",
- use_ascii=False,
- charsets=["ISO-8859-11", "TIS-620", "CP874"],
- alphabet="กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำิีึืฺุู฿เแโใไๅๆ็่้๊๋์ํ๎๏๐๑๒๓๔๕๖๗๘๙๚๛",
- wiki_start_pages=["หน้าหลัก"],
- ),
- "Turkish": Language(
- name="Turkish",
- iso_code="tr",
- # Q, W, and X are not used by Turkish
- use_ascii=False,
- charsets=["ISO-8859-3", "ISO-8859-9", "WINDOWS-1254"],
- alphabet="abcçdefgğhıijklmnoöprsştuüvyzâîûABCÇDEFGĞHIİJKLMNOÖPRSŞTUÜVYZÂÎÛ",
- wiki_start_pages=["Ana_Sayfa"],
- ),
- "Vietnamese": Language(
- name="Vietnamese",
- iso_code="vi",
- use_ascii=False,
- # Windows-1258 is the only common 8-bit
- # Vietnamese encoding supported by Python.
- # From Wikipedia:
- # For systems that lack support for Unicode,
- # dozens of 8-bit Vietnamese code pages are
- # available.[1] The most common are VISCII
- # (TCVN 5712:1993), VPS, and Windows-1258.[3]
- # Where ASCII is required, such as when
- # ensuring readability in plain text e-mail,
- # Vietnamese letters are often encoded
- # according to Vietnamese Quoted-Readable
- # (VIQR) or VSCII Mnemonic (VSCII-MNEM),[4]
- # though usage of either variable-width
- # scheme has declined dramatically following
- # the adoption of Unicode on the World Wide
- # Web.
- charsets=["WINDOWS-1258"],
- alphabet="aăâbcdđeêghiklmnoôơpqrstuưvxyAĂÂBCDĐEÊGHIKLMNOÔƠPQRSTUƯVXY",
- wiki_start_pages=["Chữ_Quốc_ngữ"],
- ),
-}
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/py.typed b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/py.typed
deleted file mode 100644
index e69de29..0000000
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/resultdict.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/resultdict.py
deleted file mode 100644
index 7d36e64..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/resultdict.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from typing import TYPE_CHECKING, Optional
-
-if TYPE_CHECKING:
- # TypedDict was introduced in Python 3.8.
- #
- # TODO: Remove the else block and TYPE_CHECKING check when dropping support
- # for Python 3.7.
- from typing import TypedDict
-
- class ResultDict(TypedDict):
- encoding: Optional[str]
- confidence: float
- language: Optional[str]
-
-else:
- ResultDict = dict
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/sbcharsetprober.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/sbcharsetprober.py
deleted file mode 100644
index 0ffbcdd..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/sbcharsetprober.py
+++ /dev/null
@@ -1,162 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is Mozilla Universal charset detector code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 2001
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-# Shy Shalom - original C code
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-from typing import Dict, List, NamedTuple, Optional, Union
-
-from .charsetprober import CharSetProber
-from .enums import CharacterCategory, ProbingState, SequenceLikelihood
-
-
-class SingleByteCharSetModel(NamedTuple):
- charset_name: str
- language: str
- char_to_order_map: Dict[int, int]
- language_model: Dict[int, Dict[int, int]]
- typical_positive_ratio: float
- keep_ascii_letters: bool
- alphabet: str
-
-
-class SingleByteCharSetProber(CharSetProber):
- SAMPLE_SIZE = 64
- SB_ENOUGH_REL_THRESHOLD = 1024 # 0.25 * SAMPLE_SIZE^2
- POSITIVE_SHORTCUT_THRESHOLD = 0.95
- NEGATIVE_SHORTCUT_THRESHOLD = 0.05
-
- def __init__(
- self,
- model: SingleByteCharSetModel,
- is_reversed: bool = False,
- name_prober: Optional[CharSetProber] = None,
- ) -> None:
- super().__init__()
- self._model = model
- # TRUE if we need to reverse every pair in the model lookup
- self._reversed = is_reversed
- # Optional auxiliary prober for name decision
- self._name_prober = name_prober
- self._last_order = 255
- self._seq_counters: List[int] = []
- self._total_seqs = 0
- self._total_char = 0
- self._control_char = 0
- self._freq_char = 0
- self.reset()
-
- def reset(self) -> None:
- super().reset()
- # char order of last character
- self._last_order = 255
- self._seq_counters = [0] * SequenceLikelihood.get_num_categories()
- self._total_seqs = 0
- self._total_char = 0
- self._control_char = 0
- # characters that fall in our sampling range
- self._freq_char = 0
-
- @property
- def charset_name(self) -> Optional[str]:
- if self._name_prober:
- return self._name_prober.charset_name
- return self._model.charset_name
-
- @property
- def language(self) -> Optional[str]:
- if self._name_prober:
- return self._name_prober.language
- return self._model.language
-
- def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState:
- # TODO: Make filter_international_words keep things in self.alphabet
- if not self._model.keep_ascii_letters:
- byte_str = self.filter_international_words(byte_str)
- else:
- byte_str = self.remove_xml_tags(byte_str)
- if not byte_str:
- return self.state
- char_to_order_map = self._model.char_to_order_map
- language_model = self._model.language_model
- for char in byte_str:
- order = char_to_order_map.get(char, CharacterCategory.UNDEFINED)
- # XXX: This was SYMBOL_CAT_ORDER before, with a value of 250, but
- # CharacterCategory.SYMBOL is actually 253, so we use CONTROL
- # to make it closer to the original intent. The only difference
- # is whether or not we count digits and control characters for
- # _total_char purposes.
- if order < CharacterCategory.CONTROL:
- self._total_char += 1
- if order < self.SAMPLE_SIZE:
- self._freq_char += 1
- if self._last_order < self.SAMPLE_SIZE:
- self._total_seqs += 1
- if not self._reversed:
- lm_cat = language_model[self._last_order][order]
- else:
- lm_cat = language_model[order][self._last_order]
- self._seq_counters[lm_cat] += 1
- self._last_order = order
-
- charset_name = self._model.charset_name
- if self.state == ProbingState.DETECTING:
- if self._total_seqs > self.SB_ENOUGH_REL_THRESHOLD:
- confidence = self.get_confidence()
- if confidence > self.POSITIVE_SHORTCUT_THRESHOLD:
- self.logger.debug(
- "%s confidence = %s, we have a winner", charset_name, confidence
- )
- self._state = ProbingState.FOUND_IT
- elif confidence < self.NEGATIVE_SHORTCUT_THRESHOLD:
- self.logger.debug(
- "%s confidence = %s, below negative shortcut threshold %s",
- charset_name,
- confidence,
- self.NEGATIVE_SHORTCUT_THRESHOLD,
- )
- self._state = ProbingState.NOT_ME
-
- return self.state
-
- def get_confidence(self) -> float:
- r = 0.01
- if self._total_seqs > 0:
- r = (
- (
- self._seq_counters[SequenceLikelihood.POSITIVE]
- + 0.25 * self._seq_counters[SequenceLikelihood.LIKELY]
- )
- / self._total_seqs
- / self._model.typical_positive_ratio
- )
- # The more control characters (proportionnaly to the size
- # of the text), the less confident we become in the current
- # charset.
- r = r * (self._total_char - self._control_char) / self._total_char
- r = r * self._freq_char / self._total_char
- if r >= 1.0:
- r = 0.99
- return r
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/sbcsgroupprober.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/sbcsgroupprober.py
deleted file mode 100644
index 890ae84..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/sbcsgroupprober.py
+++ /dev/null
@@ -1,88 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is Mozilla Universal charset detector code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 2001
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-# Shy Shalom - original C code
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-from .charsetgroupprober import CharSetGroupProber
-from .hebrewprober import HebrewProber
-from .langbulgarianmodel import ISO_8859_5_BULGARIAN_MODEL, WINDOWS_1251_BULGARIAN_MODEL
-from .langgreekmodel import ISO_8859_7_GREEK_MODEL, WINDOWS_1253_GREEK_MODEL
-from .langhebrewmodel import WINDOWS_1255_HEBREW_MODEL
-
-# from .langhungarianmodel import (ISO_8859_2_HUNGARIAN_MODEL,
-# WINDOWS_1250_HUNGARIAN_MODEL)
-from .langrussianmodel import (
- IBM855_RUSSIAN_MODEL,
- IBM866_RUSSIAN_MODEL,
- ISO_8859_5_RUSSIAN_MODEL,
- KOI8_R_RUSSIAN_MODEL,
- MACCYRILLIC_RUSSIAN_MODEL,
- WINDOWS_1251_RUSSIAN_MODEL,
-)
-from .langthaimodel import TIS_620_THAI_MODEL
-from .langturkishmodel import ISO_8859_9_TURKISH_MODEL
-from .sbcharsetprober import SingleByteCharSetProber
-
-
-class SBCSGroupProber(CharSetGroupProber):
- def __init__(self) -> None:
- super().__init__()
- hebrew_prober = HebrewProber()
- logical_hebrew_prober = SingleByteCharSetProber(
- WINDOWS_1255_HEBREW_MODEL, is_reversed=False, name_prober=hebrew_prober
- )
- # TODO: See if using ISO-8859-8 Hebrew model works better here, since
- # it's actually the visual one
- visual_hebrew_prober = SingleByteCharSetProber(
- WINDOWS_1255_HEBREW_MODEL, is_reversed=True, name_prober=hebrew_prober
- )
- hebrew_prober.set_model_probers(logical_hebrew_prober, visual_hebrew_prober)
- # TODO: ORDER MATTERS HERE. I changed the order vs what was in master
- # and several tests failed that did not before. Some thought
- # should be put into the ordering, and we should consider making
- # order not matter here, because that is very counter-intuitive.
- self.probers = [
- SingleByteCharSetProber(WINDOWS_1251_RUSSIAN_MODEL),
- SingleByteCharSetProber(KOI8_R_RUSSIAN_MODEL),
- SingleByteCharSetProber(ISO_8859_5_RUSSIAN_MODEL),
- SingleByteCharSetProber(MACCYRILLIC_RUSSIAN_MODEL),
- SingleByteCharSetProber(IBM866_RUSSIAN_MODEL),
- SingleByteCharSetProber(IBM855_RUSSIAN_MODEL),
- SingleByteCharSetProber(ISO_8859_7_GREEK_MODEL),
- SingleByteCharSetProber(WINDOWS_1253_GREEK_MODEL),
- SingleByteCharSetProber(ISO_8859_5_BULGARIAN_MODEL),
- SingleByteCharSetProber(WINDOWS_1251_BULGARIAN_MODEL),
- # TODO: Restore Hungarian encodings (iso-8859-2 and windows-1250)
- # after we retrain model.
- # SingleByteCharSetProber(ISO_8859_2_HUNGARIAN_MODEL),
- # SingleByteCharSetProber(WINDOWS_1250_HUNGARIAN_MODEL),
- SingleByteCharSetProber(TIS_620_THAI_MODEL),
- SingleByteCharSetProber(ISO_8859_9_TURKISH_MODEL),
- hebrew_prober,
- logical_hebrew_prober,
- visual_hebrew_prober,
- ]
- self.reset()
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/sjisprober.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/sjisprober.py
deleted file mode 100644
index 91df077..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/sjisprober.py
+++ /dev/null
@@ -1,105 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is mozilla.org code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-from typing import Union
-
-from .chardistribution import SJISDistributionAnalysis
-from .codingstatemachine import CodingStateMachine
-from .enums import MachineState, ProbingState
-from .jpcntx import SJISContextAnalysis
-from .mbcharsetprober import MultiByteCharSetProber
-from .mbcssm import SJIS_SM_MODEL
-
-
-class SJISProber(MultiByteCharSetProber):
- def __init__(self) -> None:
- super().__init__()
- self.coding_sm = CodingStateMachine(SJIS_SM_MODEL)
- self.distribution_analyzer = SJISDistributionAnalysis()
- self.context_analyzer = SJISContextAnalysis()
- self.reset()
-
- def reset(self) -> None:
- super().reset()
- self.context_analyzer.reset()
-
- @property
- def charset_name(self) -> str:
- return self.context_analyzer.charset_name
-
- @property
- def language(self) -> str:
- return "Japanese"
-
- def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState:
- assert self.coding_sm is not None
- assert self.distribution_analyzer is not None
-
- for i, byte in enumerate(byte_str):
- coding_state = self.coding_sm.next_state(byte)
- if coding_state == MachineState.ERROR:
- self.logger.debug(
- "%s %s prober hit error at byte %s",
- self.charset_name,
- self.language,
- i,
- )
- self._state = ProbingState.NOT_ME
- break
- if coding_state == MachineState.ITS_ME:
- self._state = ProbingState.FOUND_IT
- break
- if coding_state == MachineState.START:
- char_len = self.coding_sm.get_current_charlen()
- if i == 0:
- self._last_char[1] = byte
- self.context_analyzer.feed(
- self._last_char[2 - char_len :], char_len
- )
- self.distribution_analyzer.feed(self._last_char, char_len)
- else:
- self.context_analyzer.feed(
- byte_str[i + 1 - char_len : i + 3 - char_len], char_len
- )
- self.distribution_analyzer.feed(byte_str[i - 1 : i + 1], char_len)
-
- self._last_char[0] = byte_str[-1]
-
- if self.state == ProbingState.DETECTING:
- if self.context_analyzer.got_enough_data() and (
- self.get_confidence() > self.SHORTCUT_THRESHOLD
- ):
- self._state = ProbingState.FOUND_IT
-
- return self.state
-
- def get_confidence(self) -> float:
- assert self.distribution_analyzer is not None
-
- context_conf = self.context_analyzer.get_confidence()
- distrib_conf = self.distribution_analyzer.get_confidence()
- return max(context_conf, distrib_conf)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/universaldetector.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/universaldetector.py
deleted file mode 100644
index 30c441d..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/universaldetector.py
+++ /dev/null
@@ -1,362 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is Mozilla Universal charset detector code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 2001
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-# Shy Shalom - original C code
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-"""
-Module containing the UniversalDetector detector class, which is the primary
-class a user of ``chardet`` should use.
-
-:author: Mark Pilgrim (initial port to Python)
-:author: Shy Shalom (original C code)
-:author: Dan Blanchard (major refactoring for 3.0)
-:author: Ian Cordasco
-"""
-
-
-import codecs
-import logging
-import re
-from typing import List, Optional, Union
-
-from .charsetgroupprober import CharSetGroupProber
-from .charsetprober import CharSetProber
-from .enums import InputState, LanguageFilter, ProbingState
-from .escprober import EscCharSetProber
-from .latin1prober import Latin1Prober
-from .macromanprober import MacRomanProber
-from .mbcsgroupprober import MBCSGroupProber
-from .resultdict import ResultDict
-from .sbcsgroupprober import SBCSGroupProber
-from .utf1632prober import UTF1632Prober
-
-
-class UniversalDetector:
- """
- The ``UniversalDetector`` class underlies the ``chardet.detect`` function
- and coordinates all of the different charset probers.
-
- To get a ``dict`` containing an encoding and its confidence, you can simply
- run:
-
- .. code::
-
- u = UniversalDetector()
- u.feed(some_bytes)
- u.close()
- detected = u.result
-
- """
-
- MINIMUM_THRESHOLD = 0.20
- HIGH_BYTE_DETECTOR = re.compile(b"[\x80-\xFF]")
- ESC_DETECTOR = re.compile(b"(\033|~{)")
- WIN_BYTE_DETECTOR = re.compile(b"[\x80-\x9F]")
- ISO_WIN_MAP = {
- "iso-8859-1": "Windows-1252",
- "iso-8859-2": "Windows-1250",
- "iso-8859-5": "Windows-1251",
- "iso-8859-6": "Windows-1256",
- "iso-8859-7": "Windows-1253",
- "iso-8859-8": "Windows-1255",
- "iso-8859-9": "Windows-1254",
- "iso-8859-13": "Windows-1257",
- }
- # Based on https://encoding.spec.whatwg.org/#names-and-labels
- # but altered to match Python names for encodings and remove mappings
- # that break tests.
- LEGACY_MAP = {
- "ascii": "Windows-1252",
- "iso-8859-1": "Windows-1252",
- "tis-620": "ISO-8859-11",
- "iso-8859-9": "Windows-1254",
- "gb2312": "GB18030",
- "euc-kr": "CP949",
- "utf-16le": "UTF-16",
- }
-
- def __init__(
- self,
- lang_filter: LanguageFilter = LanguageFilter.ALL,
- should_rename_legacy: bool = False,
- ) -> None:
- self._esc_charset_prober: Optional[EscCharSetProber] = None
- self._utf1632_prober: Optional[UTF1632Prober] = None
- self._charset_probers: List[CharSetProber] = []
- self.result: ResultDict = {
- "encoding": None,
- "confidence": 0.0,
- "language": None,
- }
- self.done = False
- self._got_data = False
- self._input_state = InputState.PURE_ASCII
- self._last_char = b""
- self.lang_filter = lang_filter
- self.logger = logging.getLogger(__name__)
- self._has_win_bytes = False
- self.should_rename_legacy = should_rename_legacy
- self.reset()
-
- @property
- def input_state(self) -> int:
- return self._input_state
-
- @property
- def has_win_bytes(self) -> bool:
- return self._has_win_bytes
-
- @property
- def charset_probers(self) -> List[CharSetProber]:
- return self._charset_probers
-
- def reset(self) -> None:
- """
- Reset the UniversalDetector and all of its probers back to their
- initial states. This is called by ``__init__``, so you only need to
- call this directly in between analyses of different documents.
- """
- self.result = {"encoding": None, "confidence": 0.0, "language": None}
- self.done = False
- self._got_data = False
- self._has_win_bytes = False
- self._input_state = InputState.PURE_ASCII
- self._last_char = b""
- if self._esc_charset_prober:
- self._esc_charset_prober.reset()
- if self._utf1632_prober:
- self._utf1632_prober.reset()
- for prober in self._charset_probers:
- prober.reset()
-
- def feed(self, byte_str: Union[bytes, bytearray]) -> None:
- """
- Takes a chunk of a document and feeds it through all of the relevant
- charset probers.
-
- After calling ``feed``, you can check the value of the ``done``
- attribute to see if you need to continue feeding the
- ``UniversalDetector`` more data, or if it has made a prediction
- (in the ``result`` attribute).
-
- .. note::
- You should always call ``close`` when you're done feeding in your
- document if ``done`` is not already ``True``.
- """
- if self.done:
- return
-
- if not byte_str:
- return
-
- if not isinstance(byte_str, bytearray):
- byte_str = bytearray(byte_str)
-
- # First check for known BOMs, since these are guaranteed to be correct
- if not self._got_data:
- # If the data starts with BOM, we know it is UTF
- if byte_str.startswith(codecs.BOM_UTF8):
- # EF BB BF UTF-8 with BOM
- self.result = {
- "encoding": "UTF-8-SIG",
- "confidence": 1.0,
- "language": "",
- }
- elif byte_str.startswith((codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE)):
- # FF FE 00 00 UTF-32, little-endian BOM
- # 00 00 FE FF UTF-32, big-endian BOM
- self.result = {"encoding": "UTF-32", "confidence": 1.0, "language": ""}
- elif byte_str.startswith(b"\xFE\xFF\x00\x00"):
- # FE FF 00 00 UCS-4, unusual octet order BOM (3412)
- self.result = {
- # TODO: This encoding is not supported by Python. Should remove?
- "encoding": "X-ISO-10646-UCS-4-3412",
- "confidence": 1.0,
- "language": "",
- }
- elif byte_str.startswith(b"\x00\x00\xFF\xFE"):
- # 00 00 FF FE UCS-4, unusual octet order BOM (2143)
- self.result = {
- # TODO: This encoding is not supported by Python. Should remove?
- "encoding": "X-ISO-10646-UCS-4-2143",
- "confidence": 1.0,
- "language": "",
- }
- elif byte_str.startswith((codecs.BOM_LE, codecs.BOM_BE)):
- # FF FE UTF-16, little endian BOM
- # FE FF UTF-16, big endian BOM
- self.result = {"encoding": "UTF-16", "confidence": 1.0, "language": ""}
-
- self._got_data = True
- if self.result["encoding"] is not None:
- self.done = True
- return
-
- # If none of those matched and we've only see ASCII so far, check
- # for high bytes and escape sequences
- if self._input_state == InputState.PURE_ASCII:
- if self.HIGH_BYTE_DETECTOR.search(byte_str):
- self._input_state = InputState.HIGH_BYTE
- elif (
- self._input_state == InputState.PURE_ASCII
- and self.ESC_DETECTOR.search(self._last_char + byte_str)
- ):
- self._input_state = InputState.ESC_ASCII
-
- self._last_char = byte_str[-1:]
-
- # next we will look to see if it is appears to be either a UTF-16 or
- # UTF-32 encoding
- if not self._utf1632_prober:
- self._utf1632_prober = UTF1632Prober()
-
- if self._utf1632_prober.state == ProbingState.DETECTING:
- if self._utf1632_prober.feed(byte_str) == ProbingState.FOUND_IT:
- self.result = {
- "encoding": self._utf1632_prober.charset_name,
- "confidence": self._utf1632_prober.get_confidence(),
- "language": "",
- }
- self.done = True
- return
-
- # If we've seen escape sequences, use the EscCharSetProber, which
- # uses a simple state machine to check for known escape sequences in
- # HZ and ISO-2022 encodings, since those are the only encodings that
- # use such sequences.
- if self._input_state == InputState.ESC_ASCII:
- if not self._esc_charset_prober:
- self._esc_charset_prober = EscCharSetProber(self.lang_filter)
- if self._esc_charset_prober.feed(byte_str) == ProbingState.FOUND_IT:
- self.result = {
- "encoding": self._esc_charset_prober.charset_name,
- "confidence": self._esc_charset_prober.get_confidence(),
- "language": self._esc_charset_prober.language,
- }
- self.done = True
- # If we've seen high bytes (i.e., those with values greater than 127),
- # we need to do more complicated checks using all our multi-byte and
- # single-byte probers that are left. The single-byte probers
- # use character bigram distributions to determine the encoding, whereas
- # the multi-byte probers use a combination of character unigram and
- # bigram distributions.
- elif self._input_state == InputState.HIGH_BYTE:
- if not self._charset_probers:
- self._charset_probers = [MBCSGroupProber(self.lang_filter)]
- # If we're checking non-CJK encodings, use single-byte prober
- if self.lang_filter & LanguageFilter.NON_CJK:
- self._charset_probers.append(SBCSGroupProber())
- self._charset_probers.append(Latin1Prober())
- self._charset_probers.append(MacRomanProber())
- for prober in self._charset_probers:
- if prober.feed(byte_str) == ProbingState.FOUND_IT:
- self.result = {
- "encoding": prober.charset_name,
- "confidence": prober.get_confidence(),
- "language": prober.language,
- }
- self.done = True
- break
- if self.WIN_BYTE_DETECTOR.search(byte_str):
- self._has_win_bytes = True
-
- def close(self) -> ResultDict:
- """
- Stop analyzing the current document and come up with a final
- prediction.
-
- :returns: The ``result`` attribute, a ``dict`` with the keys
- `encoding`, `confidence`, and `language`.
- """
- # Don't bother with checks if we're already done
- if self.done:
- return self.result
- self.done = True
-
- if not self._got_data:
- self.logger.debug("no data received!")
-
- # Default to ASCII if it is all we've seen so far
- elif self._input_state == InputState.PURE_ASCII:
- self.result = {"encoding": "ascii", "confidence": 1.0, "language": ""}
-
- # If we have seen non-ASCII, return the best that met MINIMUM_THRESHOLD
- elif self._input_state == InputState.HIGH_BYTE:
- prober_confidence = None
- max_prober_confidence = 0.0
- max_prober = None
- for prober in self._charset_probers:
- if not prober:
- continue
- prober_confidence = prober.get_confidence()
- if prober_confidence > max_prober_confidence:
- max_prober_confidence = prober_confidence
- max_prober = prober
- if max_prober and (max_prober_confidence > self.MINIMUM_THRESHOLD):
- charset_name = max_prober.charset_name
- assert charset_name is not None
- lower_charset_name = charset_name.lower()
- confidence = max_prober.get_confidence()
- # Use Windows encoding name instead of ISO-8859 if we saw any
- # extra Windows-specific bytes
- if lower_charset_name.startswith("iso-8859"):
- if self._has_win_bytes:
- charset_name = self.ISO_WIN_MAP.get(
- lower_charset_name, charset_name
- )
- # Rename legacy encodings with superset encodings if asked
- if self.should_rename_legacy:
- charset_name = self.LEGACY_MAP.get(
- (charset_name or "").lower(), charset_name
- )
- self.result = {
- "encoding": charset_name,
- "confidence": confidence,
- "language": max_prober.language,
- }
-
- # Log all prober confidences if none met MINIMUM_THRESHOLD
- if self.logger.getEffectiveLevel() <= logging.DEBUG:
- if self.result["encoding"] is None:
- self.logger.debug("no probers hit minimum threshold")
- for group_prober in self._charset_probers:
- if not group_prober:
- continue
- if isinstance(group_prober, CharSetGroupProber):
- for prober in group_prober.probers:
- self.logger.debug(
- "%s %s confidence = %s",
- prober.charset_name,
- prober.language,
- prober.get_confidence(),
- )
- else:
- self.logger.debug(
- "%s %s confidence = %s",
- group_prober.charset_name,
- group_prober.language,
- group_prober.get_confidence(),
- )
- return self.result
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/utf1632prober.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/utf1632prober.py
deleted file mode 100644
index 6bdec63..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/utf1632prober.py
+++ /dev/null
@@ -1,225 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-#
-# Contributor(s):
-# Jason Zavaglia
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-from typing import List, Union
-
-from .charsetprober import CharSetProber
-from .enums import ProbingState
-
-
-class UTF1632Prober(CharSetProber):
- """
- This class simply looks for occurrences of zero bytes, and infers
- whether the file is UTF16 or UTF32 (low-endian or big-endian)
- For instance, files looking like ( \0 \0 \0 [nonzero] )+
- have a good probability to be UTF32BE. Files looking like ( \0 [nonzero] )+
- may be guessed to be UTF16BE, and inversely for little-endian varieties.
- """
-
- # how many logical characters to scan before feeling confident of prediction
- MIN_CHARS_FOR_DETECTION = 20
- # a fixed constant ratio of expected zeros or non-zeros in modulo-position.
- EXPECTED_RATIO = 0.94
-
- def __init__(self) -> None:
- super().__init__()
- self.position = 0
- self.zeros_at_mod = [0] * 4
- self.nonzeros_at_mod = [0] * 4
- self._state = ProbingState.DETECTING
- self.quad = [0, 0, 0, 0]
- self.invalid_utf16be = False
- self.invalid_utf16le = False
- self.invalid_utf32be = False
- self.invalid_utf32le = False
- self.first_half_surrogate_pair_detected_16be = False
- self.first_half_surrogate_pair_detected_16le = False
- self.reset()
-
- def reset(self) -> None:
- super().reset()
- self.position = 0
- self.zeros_at_mod = [0] * 4
- self.nonzeros_at_mod = [0] * 4
- self._state = ProbingState.DETECTING
- self.invalid_utf16be = False
- self.invalid_utf16le = False
- self.invalid_utf32be = False
- self.invalid_utf32le = False
- self.first_half_surrogate_pair_detected_16be = False
- self.first_half_surrogate_pair_detected_16le = False
- self.quad = [0, 0, 0, 0]
-
- @property
- def charset_name(self) -> str:
- if self.is_likely_utf32be():
- return "utf-32be"
- if self.is_likely_utf32le():
- return "utf-32le"
- if self.is_likely_utf16be():
- return "utf-16be"
- if self.is_likely_utf16le():
- return "utf-16le"
- # default to something valid
- return "utf-16"
-
- @property
- def language(self) -> str:
- return ""
-
- def approx_32bit_chars(self) -> float:
- return max(1.0, self.position / 4.0)
-
- def approx_16bit_chars(self) -> float:
- return max(1.0, self.position / 2.0)
-
- def is_likely_utf32be(self) -> bool:
- approx_chars = self.approx_32bit_chars()
- return approx_chars >= self.MIN_CHARS_FOR_DETECTION and (
- self.zeros_at_mod[0] / approx_chars > self.EXPECTED_RATIO
- and self.zeros_at_mod[1] / approx_chars > self.EXPECTED_RATIO
- and self.zeros_at_mod[2] / approx_chars > self.EXPECTED_RATIO
- and self.nonzeros_at_mod[3] / approx_chars > self.EXPECTED_RATIO
- and not self.invalid_utf32be
- )
-
- def is_likely_utf32le(self) -> bool:
- approx_chars = self.approx_32bit_chars()
- return approx_chars >= self.MIN_CHARS_FOR_DETECTION and (
- self.nonzeros_at_mod[0] / approx_chars > self.EXPECTED_RATIO
- and self.zeros_at_mod[1] / approx_chars > self.EXPECTED_RATIO
- and self.zeros_at_mod[2] / approx_chars > self.EXPECTED_RATIO
- and self.zeros_at_mod[3] / approx_chars > self.EXPECTED_RATIO
- and not self.invalid_utf32le
- )
-
- def is_likely_utf16be(self) -> bool:
- approx_chars = self.approx_16bit_chars()
- return approx_chars >= self.MIN_CHARS_FOR_DETECTION and (
- (self.nonzeros_at_mod[1] + self.nonzeros_at_mod[3]) / approx_chars
- > self.EXPECTED_RATIO
- and (self.zeros_at_mod[0] + self.zeros_at_mod[2]) / approx_chars
- > self.EXPECTED_RATIO
- and not self.invalid_utf16be
- )
-
- def is_likely_utf16le(self) -> bool:
- approx_chars = self.approx_16bit_chars()
- return approx_chars >= self.MIN_CHARS_FOR_DETECTION and (
- (self.nonzeros_at_mod[0] + self.nonzeros_at_mod[2]) / approx_chars
- > self.EXPECTED_RATIO
- and (self.zeros_at_mod[1] + self.zeros_at_mod[3]) / approx_chars
- > self.EXPECTED_RATIO
- and not self.invalid_utf16le
- )
-
- def validate_utf32_characters(self, quad: List[int]) -> None:
- """
- Validate if the quad of bytes is valid UTF-32.
-
- UTF-32 is valid in the range 0x00000000 - 0x0010FFFF
- excluding 0x0000D800 - 0x0000DFFF
-
- https://en.wikipedia.org/wiki/UTF-32
- """
- if (
- quad[0] != 0
- or quad[1] > 0x10
- or (quad[0] == 0 and quad[1] == 0 and 0xD8 <= quad[2] <= 0xDF)
- ):
- self.invalid_utf32be = True
- if (
- quad[3] != 0
- or quad[2] > 0x10
- or (quad[3] == 0 and quad[2] == 0 and 0xD8 <= quad[1] <= 0xDF)
- ):
- self.invalid_utf32le = True
-
- def validate_utf16_characters(self, pair: List[int]) -> None:
- """
- Validate if the pair of bytes is valid UTF-16.
-
- UTF-16 is valid in the range 0x0000 - 0xFFFF excluding 0xD800 - 0xFFFF
- with an exception for surrogate pairs, which must be in the range
- 0xD800-0xDBFF followed by 0xDC00-0xDFFF
-
- https://en.wikipedia.org/wiki/UTF-16
- """
- if not self.first_half_surrogate_pair_detected_16be:
- if 0xD8 <= pair[0] <= 0xDB:
- self.first_half_surrogate_pair_detected_16be = True
- elif 0xDC <= pair[0] <= 0xDF:
- self.invalid_utf16be = True
- else:
- if 0xDC <= pair[0] <= 0xDF:
- self.first_half_surrogate_pair_detected_16be = False
- else:
- self.invalid_utf16be = True
-
- if not self.first_half_surrogate_pair_detected_16le:
- if 0xD8 <= pair[1] <= 0xDB:
- self.first_half_surrogate_pair_detected_16le = True
- elif 0xDC <= pair[1] <= 0xDF:
- self.invalid_utf16le = True
- else:
- if 0xDC <= pair[1] <= 0xDF:
- self.first_half_surrogate_pair_detected_16le = False
- else:
- self.invalid_utf16le = True
-
- def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState:
- for c in byte_str:
- mod4 = self.position % 4
- self.quad[mod4] = c
- if mod4 == 3:
- self.validate_utf32_characters(self.quad)
- self.validate_utf16_characters(self.quad[0:2])
- self.validate_utf16_characters(self.quad[2:4])
- if c == 0:
- self.zeros_at_mod[mod4] += 1
- else:
- self.nonzeros_at_mod[mod4] += 1
- self.position += 1
- return self.state
-
- @property
- def state(self) -> ProbingState:
- if self._state in {ProbingState.NOT_ME, ProbingState.FOUND_IT}:
- # terminal, decided states
- return self._state
- if self.get_confidence() > 0.80:
- self._state = ProbingState.FOUND_IT
- elif self.position > 4 * 1024:
- # if we get to 4kb into the file, and we can't conclude it's UTF,
- # let's give up
- self._state = ProbingState.NOT_ME
- return self._state
-
- def get_confidence(self) -> float:
- return (
- 0.85
- if (
- self.is_likely_utf16le()
- or self.is_likely_utf16be()
- or self.is_likely_utf32le()
- or self.is_likely_utf32be()
- )
- else 0.00
- )
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/utf8prober.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/utf8prober.py
deleted file mode 100644
index d96354d..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/utf8prober.py
+++ /dev/null
@@ -1,82 +0,0 @@
-######################## BEGIN LICENSE BLOCK ########################
-# The Original Code is mozilla.org code.
-#
-# The Initial Developer of the Original Code is
-# Netscape Communications Corporation.
-# Portions created by the Initial Developer are Copyright (C) 1998
-# the Initial Developer. All Rights Reserved.
-#
-# Contributor(s):
-# Mark Pilgrim - port to Python
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
-# 02110-1301 USA
-######################### END LICENSE BLOCK #########################
-
-from typing import Union
-
-from .charsetprober import CharSetProber
-from .codingstatemachine import CodingStateMachine
-from .enums import MachineState, ProbingState
-from .mbcssm import UTF8_SM_MODEL
-
-
-class UTF8Prober(CharSetProber):
- ONE_CHAR_PROB = 0.5
-
- def __init__(self) -> None:
- super().__init__()
- self.coding_sm = CodingStateMachine(UTF8_SM_MODEL)
- self._num_mb_chars = 0
- self.reset()
-
- def reset(self) -> None:
- super().reset()
- self.coding_sm.reset()
- self._num_mb_chars = 0
-
- @property
- def charset_name(self) -> str:
- return "utf-8"
-
- @property
- def language(self) -> str:
- return ""
-
- def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState:
- for c in byte_str:
- coding_state = self.coding_sm.next_state(c)
- if coding_state == MachineState.ERROR:
- self._state = ProbingState.NOT_ME
- break
- if coding_state == MachineState.ITS_ME:
- self._state = ProbingState.FOUND_IT
- break
- if coding_state == MachineState.START:
- if self.coding_sm.get_current_charlen() >= 2:
- self._num_mb_chars += 1
-
- if self.state == ProbingState.DETECTING:
- if self.get_confidence() > self.SHORTCUT_THRESHOLD:
- self._state = ProbingState.FOUND_IT
-
- return self.state
-
- def get_confidence(self) -> float:
- unlike = 0.99
- if self._num_mb_chars < 6:
- unlike *= self.ONE_CHAR_PROB**self._num_mb_chars
- return 1.0 - unlike
- return unlike
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/version.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/version.py
deleted file mode 100644
index 19dd01e..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/chardet/version.py
+++ /dev/null
@@ -1,9 +0,0 @@
-"""
-This module exists only to simplify retrieving the version number of chardet
-from within setuptools and from chardet subpackages.
-
-:author: Dan Blanchard (dan.blanchard@gmail.com)
-"""
-
-__version__ = "5.2.0"
-VERSION = __version__.split(".")
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/cssselect/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/cssselect/__init__.py
deleted file mode 100644
index 77f028b..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/cssselect/__init__.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- CSS Selectors based on XPath
- ============================
-
- This module supports selecting XML/HTML elements based on CSS selectors.
- See the `CSSSelector` class for details.
-
-
- :copyright: (c) 2007-2012 Ian Bicking and contributors.
- See AUTHORS for more details.
- :license: BSD, see LICENSE for more details.
-
-"""
-
-from cssselect.parser import (
- parse,
- Selector,
- FunctionalPseudoElement,
- SelectorError,
- SelectorSyntaxError,
-)
-from cssselect.xpath import GenericTranslator, HTMLTranslator, ExpressionError
-
-__all__ = (
- "ExpressionError",
- "FunctionalPseudoElement",
- "GenericTranslator",
- "HTMLTranslator",
- "parse",
- "Selector",
- "SelectorError",
- "SelectorSyntaxError",
-)
-
-VERSION = "1.2.0"
-__version__ = VERSION
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/cssselect/parser.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/cssselect/parser.py
deleted file mode 100644
index 25a650c..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/cssselect/parser.py
+++ /dev/null
@@ -1,1062 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- cssselect.parser
- ================
-
- Tokenizer, parser and parsed objects for CSS selectors.
-
-
- :copyright: (c) 2007-2012 Ian Bicking and contributors.
- See AUTHORS for more details.
- :license: BSD, see LICENSE for more details.
-
-"""
-
-import sys
-import re
-import operator
-import typing
-from typing import Iterable, Iterator, List, Optional, Sequence, Tuple, Union
-
-
-def ascii_lower(string: str) -> str:
- """Lower-case, but only in the ASCII range."""
- return string.encode("utf8").lower().decode("utf8")
-
-
-class SelectorError(Exception):
- """Common parent for :class:`SelectorSyntaxError` and
- :class:`ExpressionError`.
-
- You can just use ``except SelectorError:`` when calling
- :meth:`~GenericTranslator.css_to_xpath` and handle both exceptions types.
-
- """
-
-
-class SelectorSyntaxError(SelectorError, SyntaxError):
- """Parsing a selector that does not match the grammar."""
-
-
-#### Parsed objects
-
-Tree = Union[
- "Element",
- "Hash",
- "Class",
- "Function",
- "Pseudo",
- "Attrib",
- "Negation",
- "Relation",
- "Matching",
- "SpecificityAdjustment",
- "CombinedSelector",
-]
-PseudoElement = Union["FunctionalPseudoElement", str]
-
-
-class Selector:
- """
- Represents a parsed selector.
-
- :meth:`~GenericTranslator.selector_to_xpath` accepts this object,
- but ignores :attr:`pseudo_element`. It is the user’s responsibility
- to account for pseudo-elements and reject selectors with unknown
- or unsupported pseudo-elements.
-
- """
-
- def __init__(self, tree: Tree, pseudo_element: Optional[PseudoElement] = None) -> None:
- self.parsed_tree = tree
- if pseudo_element is not None and not isinstance(pseudo_element, FunctionalPseudoElement):
- pseudo_element = ascii_lower(pseudo_element)
- #: A :class:`FunctionalPseudoElement`,
- #: or the identifier for the pseudo-element as a string,
- # or ``None``.
- #:
- #: +-------------------------+----------------+--------------------------------+
- #: | | Selector | Pseudo-element |
- #: +=========================+================+================================+
- #: | CSS3 syntax | ``a::before`` | ``'before'`` |
- #: +-------------------------+----------------+--------------------------------+
- #: | Older syntax | ``a:before`` | ``'before'`` |
- #: +-------------------------+----------------+--------------------------------+
- #: | From the Lists3_ draft, | ``li::marker`` | ``'marker'`` |
- #: | not in Selectors3 | | |
- #: +-------------------------+----------------+--------------------------------+
- #: | Invalid pseudo-class | ``li:marker`` | ``None`` |
- #: +-------------------------+----------------+--------------------------------+
- #: | Functional | ``a::foo(2)`` | ``FunctionalPseudoElement(…)`` |
- #: +-------------------------+----------------+--------------------------------+
- #:
- #: .. _Lists3: http://www.w3.org/TR/2011/WD-css3-lists-20110524/#marker-pseudoelement
- self.pseudo_element = pseudo_element
-
- def __repr__(self) -> str:
- if isinstance(self.pseudo_element, FunctionalPseudoElement):
- pseudo_element = repr(self.pseudo_element)
- elif self.pseudo_element:
- pseudo_element = "::%s" % self.pseudo_element
- else:
- pseudo_element = ""
- return "%s[%r%s]" % (self.__class__.__name__, self.parsed_tree, pseudo_element)
-
- def canonical(self) -> str:
- """Return a CSS representation for this selector (a string)"""
- if isinstance(self.pseudo_element, FunctionalPseudoElement):
- pseudo_element = "::%s" % self.pseudo_element.canonical()
- elif self.pseudo_element:
- pseudo_element = "::%s" % self.pseudo_element
- else:
- pseudo_element = ""
- res = "%s%s" % (self.parsed_tree.canonical(), pseudo_element)
- if len(res) > 1:
- res = res.lstrip("*")
- return res
-
- def specificity(self) -> Tuple[int, int, int]:
- """Return the specificity_ of this selector as a tuple of 3 integers.
-
- .. _specificity: http://www.w3.org/TR/selectors/#specificity
-
- """
- a, b, c = self.parsed_tree.specificity()
- if self.pseudo_element:
- c += 1
- return a, b, c
-
-
-class Class:
- """
- Represents selector.class_name
- """
-
- def __init__(self, selector: Tree, class_name: str) -> None:
- self.selector = selector
- self.class_name = class_name
-
- def __repr__(self) -> str:
- return "%s[%r.%s]" % (self.__class__.__name__, self.selector, self.class_name)
-
- def canonical(self) -> str:
- return "%s.%s" % (self.selector.canonical(), self.class_name)
-
- def specificity(self) -> Tuple[int, int, int]:
- a, b, c = self.selector.specificity()
- b += 1
- return a, b, c
-
-
-class FunctionalPseudoElement:
- """
- Represents selector::name(arguments)
-
- .. attribute:: name
-
- The name (identifier) of the pseudo-element, as a string.
-
- .. attribute:: arguments
-
- The arguments of the pseudo-element, as a list of tokens.
-
- **Note:** tokens are not part of the public API,
- and may change between cssselect versions.
- Use at your own risks.
-
- """
-
- def __init__(self, name: str, arguments: Sequence["Token"]):
- self.name = ascii_lower(name)
- self.arguments = arguments
-
- def __repr__(self) -> str:
- return "%s[::%s(%r)]" % (
- self.__class__.__name__,
- self.name,
- [token.value for token in self.arguments],
- )
-
- def argument_types(self) -> List[str]:
- return [token.type for token in self.arguments]
-
- def canonical(self) -> str:
- args = "".join(token.css() for token in self.arguments)
- return "%s(%s)" % (self.name, args)
-
-
-class Function:
- """
- Represents selector:name(expr)
- """
-
- def __init__(self, selector: Tree, name: str, arguments: Sequence["Token"]) -> None:
- self.selector = selector
- self.name = ascii_lower(name)
- self.arguments = arguments
-
- def __repr__(self) -> str:
- return "%s[%r:%s(%r)]" % (
- self.__class__.__name__,
- self.selector,
- self.name,
- [token.value for token in self.arguments],
- )
-
- def argument_types(self) -> List[str]:
- return [token.type for token in self.arguments]
-
- def canonical(self) -> str:
- args = "".join(token.css() for token in self.arguments)
- return "%s:%s(%s)" % (self.selector.canonical(), self.name, args)
-
- def specificity(self) -> Tuple[int, int, int]:
- a, b, c = self.selector.specificity()
- b += 1
- return a, b, c
-
-
-class Pseudo:
- """
- Represents selector:ident
- """
-
- def __init__(self, selector: Tree, ident: str) -> None:
- self.selector = selector
- self.ident = ascii_lower(ident)
-
- def __repr__(self) -> str:
- return "%s[%r:%s]" % (self.__class__.__name__, self.selector, self.ident)
-
- def canonical(self) -> str:
- return "%s:%s" % (self.selector.canonical(), self.ident)
-
- def specificity(self) -> Tuple[int, int, int]:
- a, b, c = self.selector.specificity()
- b += 1
- return a, b, c
-
-
-class Negation:
- """
- Represents selector:not(subselector)
- """
-
- def __init__(self, selector: Tree, subselector: Tree) -> None:
- self.selector = selector
- self.subselector = subselector
-
- def __repr__(self) -> str:
- return "%s[%r:not(%r)]" % (self.__class__.__name__, self.selector, self.subselector)
-
- def canonical(self) -> str:
- subsel = self.subselector.canonical()
- if len(subsel) > 1:
- subsel = subsel.lstrip("*")
- return "%s:not(%s)" % (self.selector.canonical(), subsel)
-
- def specificity(self) -> Tuple[int, int, int]:
- a1, b1, c1 = self.selector.specificity()
- a2, b2, c2 = self.subselector.specificity()
- return a1 + a2, b1 + b2, c1 + c2
-
-
-class Relation:
- """
- Represents selector:has(subselector)
- """
-
- def __init__(self, selector: Tree, combinator: "Token", subselector: Selector):
- self.selector = selector
- self.combinator = combinator
- self.subselector = subselector
-
- def __repr__(self) -> str:
- return "%s[%r:has(%r)]" % (
- self.__class__.__name__,
- self.selector,
- self.subselector,
- )
-
- def canonical(self) -> str:
- try:
- subsel = self.subselector[0].canonical() # type: ignore
- except TypeError:
- subsel = self.subselector.canonical()
- if len(subsel) > 1:
- subsel = subsel.lstrip("*")
- return "%s:has(%s)" % (self.selector.canonical(), subsel)
-
- def specificity(self) -> Tuple[int, int, int]:
- a1, b1, c1 = self.selector.specificity()
- try:
- a2, b2, c2 = self.subselector[-1].specificity() # type: ignore
- except TypeError:
- a2, b2, c2 = self.subselector.specificity()
- return a1 + a2, b1 + b2, c1 + c2
-
-
-class Matching:
- """
- Represents selector:is(selector_list)
- """
-
- def __init__(self, selector: Tree, selector_list: Iterable[Tree]):
- self.selector = selector
- self.selector_list = selector_list
-
- def __repr__(self) -> str:
- return "%s[%r:is(%s)]" % (
- self.__class__.__name__,
- self.selector,
- ", ".join(map(repr, self.selector_list)),
- )
-
- def canonical(self) -> str:
- selector_arguments = []
- for s in self.selector_list:
- selarg = s.canonical()
- selector_arguments.append(selarg.lstrip("*"))
- return "%s:is(%s)" % (self.selector.canonical(), ", ".join(map(str, selector_arguments)))
-
- def specificity(self) -> Tuple[int, int, int]:
- return max(x.specificity() for x in self.selector_list)
-
-
-class SpecificityAdjustment:
- """
- Represents selector:where(selector_list)
- Same as selector:is(selector_list), but its specificity is always 0
- """
-
- def __init__(self, selector: Tree, selector_list: List[Tree]):
- self.selector = selector
- self.selector_list = selector_list
-
- def __repr__(self) -> str:
- return "%s[%r:where(%s)]" % (
- self.__class__.__name__,
- self.selector,
- ", ".join(map(repr, self.selector_list)),
- )
-
- def canonical(self) -> str:
- selector_arguments = []
- for s in self.selector_list:
- selarg = s.canonical()
- selector_arguments.append(selarg.lstrip("*"))
- return "%s:where(%s)" % (
- self.selector.canonical(),
- ", ".join(map(str, selector_arguments)),
- )
-
- def specificity(self) -> Tuple[int, int, int]:
- return 0, 0, 0
-
-
-class Attrib:
- """
- Represents selector[namespace|attrib operator value]
- """
-
- @typing.overload
- def __init__(
- self,
- selector: Tree,
- namespace: Optional[str],
- attrib: str,
- operator: 'typing.Literal["exists"]',
- value: None,
- ) -> None:
- ...
-
- @typing.overload
- def __init__(
- self, selector: Tree, namespace: Optional[str], attrib: str, operator: str, value: "Token"
- ) -> None:
- ...
-
- def __init__(
- self,
- selector: Tree,
- namespace: Optional[str],
- attrib: str,
- operator: str,
- value: Optional["Token"],
- ) -> None:
- self.selector = selector
- self.namespace = namespace
- self.attrib = attrib
- self.operator = operator
- self.value = value
-
- def __repr__(self) -> str:
- if self.namespace:
- attrib = "%s|%s" % (self.namespace, self.attrib)
- else:
- attrib = self.attrib
- if self.operator == "exists":
- return "%s[%r[%s]]" % (self.__class__.__name__, self.selector, attrib)
- else:
- return "%s[%r[%s %s %r]]" % (
- self.__class__.__name__,
- self.selector,
- attrib,
- self.operator,
- typing.cast("Token", self.value).value,
- )
-
- def canonical(self) -> str:
- if self.namespace:
- attrib = "%s|%s" % (self.namespace, self.attrib)
- else:
- attrib = self.attrib
-
- if self.operator == "exists":
- op = attrib
- else:
- op = "%s%s%s" % (attrib, self.operator, typing.cast("Token", self.value).css())
-
- return "%s[%s]" % (self.selector.canonical(), op)
-
- def specificity(self) -> Tuple[int, int, int]:
- a, b, c = self.selector.specificity()
- b += 1
- return a, b, c
-
-
-class Element:
- """
- Represents namespace|element
-
- `None` is for the universal selector '*'
-
- """
-
- def __init__(self, namespace: Optional[str] = None, element: Optional[str] = None) -> None:
- self.namespace = namespace
- self.element = element
-
- def __repr__(self) -> str:
- return "%s[%s]" % (self.__class__.__name__, self.canonical())
-
- def canonical(self) -> str:
- element = self.element or "*"
- if self.namespace:
- element = "%s|%s" % (self.namespace, element)
- return element
-
- def specificity(self) -> Tuple[int, int, int]:
- if self.element:
- return 0, 0, 1
- else:
- return 0, 0, 0
-
-
-class Hash:
- """
- Represents selector#id
- """
-
- def __init__(self, selector: Tree, id: str) -> None:
- self.selector = selector
- self.id = id
-
- def __repr__(self) -> str:
- return "%s[%r#%s]" % (self.__class__.__name__, self.selector, self.id)
-
- def canonical(self) -> str:
- return "%s#%s" % (self.selector.canonical(), self.id)
-
- def specificity(self) -> Tuple[int, int, int]:
- a, b, c = self.selector.specificity()
- a += 1
- return a, b, c
-
-
-class CombinedSelector:
- def __init__(self, selector: Tree, combinator: str, subselector: Tree) -> None:
- assert selector is not None
- self.selector = selector
- self.combinator = combinator
- self.subselector = subselector
-
- def __repr__(self) -> str:
- if self.combinator == " ":
- comb = ""
- else:
- comb = self.combinator
- return "%s[%r %s %r]" % (self.__class__.__name__, self.selector, comb, self.subselector)
-
- def canonical(self) -> str:
- subsel = self.subselector.canonical()
- if len(subsel) > 1:
- subsel = subsel.lstrip("*")
- return "%s %s %s" % (self.selector.canonical(), self.combinator, subsel)
-
- def specificity(self) -> Tuple[int, int, int]:
- a1, b1, c1 = self.selector.specificity()
- a2, b2, c2 = self.subselector.specificity()
- return a1 + a2, b1 + b2, c1 + c2
-
-
-#### Parser
-
-# foo
-_el_re = re.compile(r"^[ \t\r\n\f]*([a-zA-Z]+)[ \t\r\n\f]*$")
-
-# foo#bar or #bar
-_id_re = re.compile(r"^[ \t\r\n\f]*([a-zA-Z]*)#([a-zA-Z0-9_-]+)[ \t\r\n\f]*$")
-
-# foo.bar or .bar
-_class_re = re.compile(r"^[ \t\r\n\f]*([a-zA-Z]*)\.([a-zA-Z][a-zA-Z0-9_-]*)[ \t\r\n\f]*$")
-
-
-def parse(css: str) -> List[Selector]:
- """Parse a CSS *group of selectors*.
-
- If you don't care about pseudo-elements or selector specificity,
- you can skip this and use :meth:`~GenericTranslator.css_to_xpath`.
-
- :param css:
- A *group of selectors* as a string.
- :raises:
- :class:`SelectorSyntaxError` on invalid selectors.
- :returns:
- A list of parsed :class:`Selector` objects, one for each
- selector in the comma-separated group.
-
- """
- # Fast path for simple cases
- match = _el_re.match(css)
- if match:
- return [Selector(Element(element=match.group(1)))]
- match = _id_re.match(css)
- if match is not None:
- return [Selector(Hash(Element(element=match.group(1) or None), match.group(2)))]
- match = _class_re.match(css)
- if match is not None:
- return [Selector(Class(Element(element=match.group(1) or None), match.group(2)))]
-
- stream = TokenStream(tokenize(css))
- stream.source = css
- return list(parse_selector_group(stream))
-
-
-# except SelectorSyntaxError:
-# e = sys.exc_info()[1]
-# message = "%s at %s -> %r" % (
-# e, stream.used, stream.peek())
-# e.msg = message
-# e.args = tuple([message])
-# raise
-
-
-def parse_selector_group(stream: "TokenStream") -> Iterator[Selector]:
- stream.skip_whitespace()
- while 1:
- yield Selector(*parse_selector(stream))
- if stream.peek() == ("DELIM", ","):
- stream.next()
- stream.skip_whitespace()
- else:
- break
-
-
-def parse_selector(stream: "TokenStream") -> Tuple[Tree, Optional[PseudoElement]]:
- result, pseudo_element = parse_simple_selector(stream)
- while 1:
- stream.skip_whitespace()
- peek = stream.peek()
- if peek in (("EOF", None), ("DELIM", ",")):
- break
- if pseudo_element:
- raise SelectorSyntaxError(
- "Got pseudo-element ::%s not at the end of a selector" % pseudo_element
- )
- if peek.is_delim("+", ">", "~"):
- # A combinator
- combinator = typing.cast(str, stream.next().value)
- stream.skip_whitespace()
- else:
- # By exclusion, the last parse_simple_selector() ended
- # at peek == ' '
- combinator = " "
- next_selector, pseudo_element = parse_simple_selector(stream)
- result = CombinedSelector(result, combinator, next_selector)
- return result, pseudo_element
-
-
-def parse_simple_selector(
- stream: "TokenStream", inside_negation: bool = False
-) -> Tuple[Tree, Optional[PseudoElement]]:
- stream.skip_whitespace()
- selector_start = len(stream.used)
- peek = stream.peek()
- if peek.type == "IDENT" or peek == ("DELIM", "*"):
- if peek.type == "IDENT":
- namespace = stream.next().value
- else:
- stream.next()
- namespace = None
- if stream.peek() == ("DELIM", "|"):
- stream.next()
- element = stream.next_ident_or_star()
- else:
- element = namespace
- namespace = None
- else:
- element = namespace = None
- result: Tree = Element(namespace, element)
- pseudo_element: Optional[PseudoElement] = None
- while 1:
- peek = stream.peek()
- if (
- peek.type in ("S", "EOF")
- or peek.is_delim(",", "+", ">", "~")
- or (inside_negation and peek == ("DELIM", ")"))
- ):
- break
- if pseudo_element:
- raise SelectorSyntaxError(
- "Got pseudo-element ::%s not at the end of a selector" % pseudo_element
- )
- if peek.type == "HASH":
- result = Hash(result, typing.cast(str, stream.next().value))
- elif peek == ("DELIM", "."):
- stream.next()
- result = Class(result, stream.next_ident())
- elif peek == ("DELIM", "|"):
- stream.next()
- result = Element(None, stream.next_ident())
- elif peek == ("DELIM", "["):
- stream.next()
- result = parse_attrib(result, stream)
- elif peek == ("DELIM", ":"):
- stream.next()
- if stream.peek() == ("DELIM", ":"):
- stream.next()
- pseudo_element = stream.next_ident()
- if stream.peek() == ("DELIM", "("):
- stream.next()
- pseudo_element = FunctionalPseudoElement(
- pseudo_element, parse_arguments(stream)
- )
- continue
- ident = stream.next_ident()
- if ident.lower() in ("first-line", "first-letter", "before", "after"):
- # Special case: CSS 2.1 pseudo-elements can have a single ':'
- # Any new pseudo-element must have two.
- pseudo_element = str(ident)
- continue
- if stream.peek() != ("DELIM", "("):
- result = Pseudo(result, ident)
- if repr(result) == "Pseudo[Element[*]:scope]":
- if not (
- len(stream.used) == 2
- or (len(stream.used) == 3 and stream.used[0].type == "S")
- or (len(stream.used) >= 3 and stream.used[-3].is_delim(","))
- or (
- len(stream.used) >= 4
- and stream.used[-3].type == "S"
- and stream.used[-4].is_delim(",")
- )
- ):
- raise SelectorSyntaxError(
- 'Got immediate child pseudo-element ":scope" '
- "not at the start of a selector"
- )
- continue
- stream.next()
- stream.skip_whitespace()
- if ident.lower() == "not":
- if inside_negation:
- raise SelectorSyntaxError("Got nested :not()")
- argument, argument_pseudo_element = parse_simple_selector(
- stream, inside_negation=True
- )
- next = stream.next()
- if argument_pseudo_element:
- raise SelectorSyntaxError(
- "Got pseudo-element ::%s inside :not() at %s"
- % (argument_pseudo_element, next.pos)
- )
- if next != ("DELIM", ")"):
- raise SelectorSyntaxError("Expected ')', got %s" % (next,))
- result = Negation(result, argument)
- elif ident.lower() == "has":
- combinator, arguments = parse_relative_selector(stream)
- result = Relation(result, combinator, arguments)
-
- elif ident.lower() in ("matches", "is"):
- selectors = parse_simple_selector_arguments(stream)
- result = Matching(result, selectors)
- elif ident.lower() == "where":
- selectors = parse_simple_selector_arguments(stream)
- result = SpecificityAdjustment(result, selectors)
- else:
- result = Function(result, ident, parse_arguments(stream))
- else:
- raise SelectorSyntaxError("Expected selector, got %s" % (peek,))
- if len(stream.used) == selector_start:
- raise SelectorSyntaxError("Expected selector, got %s" % (stream.peek(),))
- return result, pseudo_element
-
-
-def parse_arguments(stream: "TokenStream") -> List["Token"]:
- arguments: List["Token"] = []
- while 1:
- stream.skip_whitespace()
- next = stream.next()
- if next.type in ("IDENT", "STRING", "NUMBER") or next in [("DELIM", "+"), ("DELIM", "-")]:
- arguments.append(next)
- elif next == ("DELIM", ")"):
- return arguments
- else:
- raise SelectorSyntaxError("Expected an argument, got %s" % (next,))
-
-
-def parse_relative_selector(stream: "TokenStream") -> Tuple["Token", Selector]:
- stream.skip_whitespace()
- subselector = ""
- next = stream.next()
-
- if next in [("DELIM", "+"), ("DELIM", "-"), ("DELIM", ">"), ("DELIM", "~")]:
- combinator = next
- stream.skip_whitespace()
- next = stream.next()
- else:
- combinator = Token("DELIM", " ", pos=0)
-
- while 1:
- if next.type in ("IDENT", "STRING", "NUMBER") or next in [("DELIM", "."), ("DELIM", "*")]:
- subselector += typing.cast(str, next.value)
- elif next == ("DELIM", ")"):
- result = parse(subselector)
- return combinator, result[0]
- else:
- raise SelectorSyntaxError("Expected an argument, got %s" % (next,))
- next = stream.next()
-
-
-def parse_simple_selector_arguments(stream: "TokenStream") -> List[Tree]:
- arguments = []
- while 1:
- result, pseudo_element = parse_simple_selector(stream, True)
- if pseudo_element:
- raise SelectorSyntaxError(
- "Got pseudo-element ::%s inside function" % (pseudo_element,)
- )
- stream.skip_whitespace()
- next = stream.next()
- if next in (("EOF", None), ("DELIM", ",")):
- stream.next()
- stream.skip_whitespace()
- arguments.append(result)
- elif next == ("DELIM", ")"):
- arguments.append(result)
- break
- else:
- raise SelectorSyntaxError("Expected an argument, got %s" % (next,))
- return arguments
-
-
-def parse_attrib(selector: Tree, stream: "TokenStream") -> Attrib:
- stream.skip_whitespace()
- attrib = stream.next_ident_or_star()
- if attrib is None and stream.peek() != ("DELIM", "|"):
- raise SelectorSyntaxError("Expected '|', got %s" % (stream.peek(),))
- namespace: Optional[str]
- op: Optional[str]
- if stream.peek() == ("DELIM", "|"):
- stream.next()
- if stream.peek() == ("DELIM", "="):
- namespace = None
- stream.next()
- op = "|="
- else:
- namespace = attrib
- attrib = stream.next_ident()
- op = None
- else:
- namespace = op = None
- if op is None:
- stream.skip_whitespace()
- next = stream.next()
- if next == ("DELIM", "]"):
- return Attrib(selector, namespace, typing.cast(str, attrib), "exists", None)
- elif next == ("DELIM", "="):
- op = "="
- elif next.is_delim("^", "$", "*", "~", "|", "!") and (stream.peek() == ("DELIM", "=")):
- op = typing.cast(str, next.value) + "="
- stream.next()
- else:
- raise SelectorSyntaxError("Operator expected, got %s" % (next,))
- stream.skip_whitespace()
- value = stream.next()
- if value.type not in ("IDENT", "STRING"):
- raise SelectorSyntaxError("Expected string or ident, got %s" % (value,))
- stream.skip_whitespace()
- next = stream.next()
- if next != ("DELIM", "]"):
- raise SelectorSyntaxError("Expected ']', got %s" % (next,))
- return Attrib(selector, namespace, typing.cast(str, attrib), op, value)
-
-
-def parse_series(tokens: Iterable["Token"]) -> Tuple[int, int]:
- """
- Parses the arguments for :nth-child() and friends.
-
- :raises: A list of tokens
- :returns: :``(a, b)``
-
- """
- for token in tokens:
- if token.type == "STRING":
- raise ValueError("String tokens not allowed in series.")
- s = "".join(typing.cast(str, token.value) for token in tokens).strip()
- if s == "odd":
- return 2, 1
- elif s == "even":
- return 2, 0
- elif s == "n":
- return 1, 0
- if "n" not in s:
- # Just b
- return 0, int(s)
- a, b = s.split("n", 1)
- a_as_int: int
- if not a:
- a_as_int = 1
- elif a == "-" or a == "+":
- a_as_int = int(a + "1")
- else:
- a_as_int = int(a)
- b_as_int: int
- if not b:
- b_as_int = 0
- else:
- b_as_int = int(b)
- return a_as_int, b_as_int
-
-
-#### Token objects
-
-
-class Token(Tuple[str, Optional[str]]):
- @typing.overload
- def __new__(
- cls,
- type_: 'typing.Literal["IDENT", "HASH", "STRING", "S", "DELIM", "NUMBER"]',
- value: str,
- pos: int,
- ) -> "Token":
- ...
-
- @typing.overload
- def __new__(cls, type_: 'typing.Literal["EOF"]', value: None, pos: int) -> "Token":
- ...
-
- def __new__(cls, type_: str, value: Optional[str], pos: int) -> "Token":
- obj = tuple.__new__(cls, (type_, value))
- obj.pos = pos
- return obj
-
- def __repr__(self) -> str:
- return "<%s '%s' at %i>" % (self.type, self.value, self.pos)
-
- def is_delim(self, *values: str) -> bool:
- return self.type == "DELIM" and self.value in values
-
- pos: int
-
- @property
- def type(self) -> str:
- return self[0]
-
- @property
- def value(self) -> Optional[str]:
- return self[1]
-
- def css(self) -> str:
- if self.type == "STRING":
- return repr(self.value)
- else:
- return typing.cast(str, self.value)
-
-
-class EOFToken(Token):
- def __new__(cls, pos: int) -> "EOFToken":
- return typing.cast("EOFToken", Token.__new__(cls, "EOF", None, pos))
-
- def __repr__(self) -> str:
- return "<%s at %i>" % (self.type, self.pos)
-
-
-#### Tokenizer
-
-
-class TokenMacros:
- unicode_escape = r"\\([0-9a-f]{1,6})(?:\r\n|[ \n\r\t\f])?"
- escape = unicode_escape + r"|\\[^\n\r\f0-9a-f]"
- string_escape = r"\\(?:\n|\r\n|\r|\f)|" + escape
- nonascii = r"[^\0-\177]"
- nmchar = "[_a-z0-9-]|%s|%s" % (escape, nonascii)
- nmstart = "[_a-z]|%s|%s" % (escape, nonascii)
-
-
-if typing.TYPE_CHECKING:
-
- class MatchFunc(typing.Protocol):
- def __call__(
- self, string: str, pos: int = ..., endpos: int = ...
- ) -> Optional["re.Match[str]"]:
- ...
-
-
-def _compile(pattern: str) -> "MatchFunc":
- return re.compile(pattern % vars(TokenMacros), re.IGNORECASE).match
-
-
-_match_whitespace = _compile(r"[ \t\r\n\f]+")
-_match_number = _compile(r"[+-]?(?:[0-9]*\.[0-9]+|[0-9]+)")
-_match_hash = _compile("#(?:%(nmchar)s)+")
-_match_ident = _compile("-?(?:%(nmstart)s)(?:%(nmchar)s)*")
-_match_string_by_quote = {
- "'": _compile(r"([^\n\r\f\\']|%(string_escape)s)*"),
- '"': _compile(r'([^\n\r\f\\"]|%(string_escape)s)*'),
-}
-
-_sub_simple_escape = re.compile(r"\\(.)").sub
-_sub_unicode_escape = re.compile(TokenMacros.unicode_escape, re.I).sub
-_sub_newline_escape = re.compile(r"\\(?:\n|\r\n|\r|\f)").sub
-
-# Same as r'\1', but faster on CPython
-_replace_simple = operator.methodcaller("group", 1)
-
-
-def _replace_unicode(match: "re.Match[str]") -> str:
- codepoint = int(match.group(1), 16)
- if codepoint > sys.maxunicode:
- codepoint = 0xFFFD
- return chr(codepoint)
-
-
-def unescape_ident(value: str) -> str:
- value = _sub_unicode_escape(_replace_unicode, value)
- value = _sub_simple_escape(_replace_simple, value)
- return value
-
-
-def tokenize(s: str) -> Iterator[Token]:
- pos = 0
- len_s = len(s)
- while pos < len_s:
- match = _match_whitespace(s, pos=pos)
- if match:
- yield Token("S", " ", pos)
- pos = match.end()
- continue
-
- match = _match_ident(s, pos=pos)
- if match:
- value = _sub_simple_escape(
- _replace_simple, _sub_unicode_escape(_replace_unicode, match.group())
- )
- yield Token("IDENT", value, pos)
- pos = match.end()
- continue
-
- match = _match_hash(s, pos=pos)
- if match:
- value = _sub_simple_escape(
- _replace_simple, _sub_unicode_escape(_replace_unicode, match.group()[1:])
- )
- yield Token("HASH", value, pos)
- pos = match.end()
- continue
-
- quote = s[pos]
- if quote in _match_string_by_quote:
- match = _match_string_by_quote[quote](s, pos=pos + 1)
- assert match, "Should have found at least an empty match"
- end_pos = match.end()
- if end_pos == len_s:
- raise SelectorSyntaxError("Unclosed string at %s" % pos)
- if s[end_pos] != quote:
- raise SelectorSyntaxError("Invalid string at %s" % pos)
- value = _sub_simple_escape(
- _replace_simple,
- _sub_unicode_escape(_replace_unicode, _sub_newline_escape("", match.group())),
- )
- yield Token("STRING", value, pos)
- pos = end_pos + 1
- continue
-
- match = _match_number(s, pos=pos)
- if match:
- value = match.group()
- yield Token("NUMBER", value, pos)
- pos = match.end()
- continue
-
- pos2 = pos + 2
- if s[pos:pos2] == "/*":
- pos = s.find("*/", pos2)
- if pos == -1:
- pos = len_s
- else:
- pos += 2
- continue
-
- yield Token("DELIM", s[pos], pos)
- pos += 1
-
- assert pos == len_s
- yield EOFToken(pos)
-
-
-class TokenStream:
- def __init__(self, tokens: Iterable[Token], source: Optional[str] = None) -> None:
- self.used: List[Token] = []
- self.tokens = iter(tokens)
- self.source = source
- self.peeked: Optional[Token] = None
- self._peeking = False
- self.next_token = self.tokens.__next__
-
- def next(self) -> Token:
- if self._peeking:
- self._peeking = False
- self.used.append(typing.cast(Token, self.peeked))
- return typing.cast(Token, self.peeked)
- else:
- next = self.next_token()
- self.used.append(next)
- return next
-
- def peek(self) -> Token:
- if not self._peeking:
- self.peeked = self.next_token()
- self._peeking = True
- return typing.cast(Token, self.peeked)
-
- def next_ident(self) -> str:
- next = self.next()
- if next.type != "IDENT":
- raise SelectorSyntaxError("Expected ident, got %s" % (next,))
- return typing.cast(str, next.value)
-
- def next_ident_or_star(self) -> Optional[str]:
- next = self.next()
- if next.type == "IDENT":
- return next.value
- elif next == ("DELIM", "*"):
- return None
- else:
- raise SelectorSyntaxError("Expected ident or '*', got %s" % (next,))
-
- def skip_whitespace(self) -> None:
- peek = self.peek()
- if peek.type == "S":
- self.next()
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/cssselect/py.typed b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/cssselect/py.typed
deleted file mode 100644
index e69de29..0000000
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/cssselect/xpath.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/cssselect/xpath.py
deleted file mode 100644
index fd28c47..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/cssselect/xpath.py
+++ /dev/null
@@ -1,889 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- cssselect.xpath
- ===============
-
- Translation of parsed CSS selectors to XPath expressions.
-
-
- :copyright: (c) 2007-2012 Ian Bicking and contributors.
- See AUTHORS for more details.
- :license: BSD, see LICENSE for more details.
-
-"""
-
-import re
-import typing
-import warnings
-from typing import Optional
-
-from cssselect.parser import (
- parse,
- parse_series,
- PseudoElement,
- Selector,
- SelectorError,
- Tree,
- Element,
- Hash,
- Class,
- Function,
- Pseudo,
- Attrib,
- Negation,
- Relation,
- Matching,
- SpecificityAdjustment,
- CombinedSelector,
-)
-
-
-@typing.no_type_check
-def _unicode_safe_getattr(obj, name, default=None):
- warnings.warn(
- "_unicode_safe_getattr is deprecated and will be removed in the"
- " next release, use getattr() instead",
- DeprecationWarning,
- stacklevel=2,
- )
- return getattr(obj, name, default)
-
-
-class ExpressionError(SelectorError, RuntimeError):
- """Unknown or unsupported selector (eg. pseudo-class)."""
-
-
-#### XPath Helpers
-
-
-class XPathExpr:
- def __init__(
- self, path: str = "", element: str = "*", condition: str = "", star_prefix: bool = False
- ) -> None:
- self.path = path
- self.element = element
- self.condition = condition
-
- def __str__(self) -> str:
- path = str(self.path) + str(self.element)
- if self.condition:
- path += "[%s]" % self.condition
- return path
-
- def __repr__(self) -> str:
- return "%s[%s]" % (self.__class__.__name__, self)
-
- def add_condition(self, condition: str, conjuction: str = "and") -> "XPathExpr":
- if self.condition:
- self.condition = "(%s) %s (%s)" % (self.condition, conjuction, condition)
- else:
- self.condition = condition
- return self
-
- def add_name_test(self) -> None:
- if self.element == "*":
- # We weren't doing a test anyway
- return
- self.add_condition("name() = %s" % GenericTranslator.xpath_literal(self.element))
- self.element = "*"
-
- def add_star_prefix(self) -> None:
- """
- Append '*/' to the path to keep the context constrained
- to a single parent.
- """
- self.path += "*/"
-
- def join(
- self,
- combiner: str,
- other: "XPathExpr",
- closing_combiner: Optional[str] = None,
- has_inner_condition: bool = False,
- ) -> "XPathExpr":
- path = str(self) + combiner
- # Any "star prefix" is redundant when joining.
- if other.path != "*/":
- path += other.path
- self.path = path
- if not has_inner_condition:
- self.element = other.element + closing_combiner if closing_combiner else other.element
- self.condition = other.condition
- else:
- self.element = other.element
- if other.condition:
- self.element += "[" + other.condition + "]"
- if closing_combiner:
- self.element += closing_combiner
- return self
-
-
-split_at_single_quotes = re.compile("('+)").split
-
-# The spec is actually more permissive than that, but don’t bother.
-# This is just for the fast path.
-# http://www.w3.org/TR/REC-xml/#NT-NameStartChar
-is_safe_name = re.compile("^[a-zA-Z_][a-zA-Z0-9_.-]*$").match
-
-# Test that the string is not empty and does not contain whitespace
-is_non_whitespace = re.compile(r"^[^ \t\r\n\f]+$").match
-
-
-#### Translation
-
-
-class GenericTranslator:
- """
- Translator for "generic" XML documents.
-
- Everything is case-sensitive, no assumption is made on the meaning
- of element names and attribute names.
-
- """
-
- ####
- #### HERE BE DRAGONS
- ####
- #### You are welcome to hook into this to change some behavior,
- #### but do so at your own risks.
- #### Until it has received a lot more work and review,
- #### I reserve the right to change this API in backward-incompatible ways
- #### with any minor version of cssselect.
- #### See https://github.com/scrapy/cssselect/pull/22
- #### -- Simon Sapin.
- ####
-
- combinator_mapping = {
- " ": "descendant",
- ">": "child",
- "+": "direct_adjacent",
- "~": "indirect_adjacent",
- }
-
- attribute_operator_mapping = {
- "exists": "exists",
- "=": "equals",
- "~=": "includes",
- "|=": "dashmatch",
- "^=": "prefixmatch",
- "$=": "suffixmatch",
- "*=": "substringmatch",
- "!=": "different", # XXX Not in Level 3 but meh
- }
-
- #: The attribute used for ID selectors depends on the document language:
- #: http://www.w3.org/TR/selectors/#id-selectors
- id_attribute = "id"
-
- #: The attribute used for ``:lang()`` depends on the document language:
- #: http://www.w3.org/TR/selectors/#lang-pseudo
- lang_attribute = "xml:lang"
-
- #: The case sensitivity of document language element names,
- #: attribute names, and attribute values in selectors depends
- #: on the document language.
- #: http://www.w3.org/TR/selectors/#casesens
- #:
- #: When a document language defines one of these as case-insensitive,
- #: cssselect assumes that the document parser makes the parsed values
- #: lower-case. Making the selector lower-case too makes the comparaison
- #: case-insensitive.
- #:
- #: In HTML, element names and attributes names (but not attribute values)
- #: are case-insensitive. All of lxml.html, html5lib, BeautifulSoup4
- #: and HTMLParser make them lower-case in their parse result, so
- #: the assumption holds.
- lower_case_element_names = False
- lower_case_attribute_names = False
- lower_case_attribute_values = False
-
- # class used to represent and xpath expression
- xpathexpr_cls = XPathExpr
-
- def css_to_xpath(self, css: str, prefix: str = "descendant-or-self::") -> str:
- """Translate a *group of selectors* to XPath.
-
- Pseudo-elements are not supported here since XPath only knows
- about "real" elements.
-
- :param css:
- A *group of selectors* as a string.
- :param prefix:
- This string is prepended to the XPath expression for each selector.
- The default makes selectors scoped to the context node’s subtree.
- :raises:
- :class:`~cssselect.SelectorSyntaxError` on invalid selectors,
- :class:`ExpressionError` on unknown/unsupported selectors,
- including pseudo-elements.
- :returns:
- The equivalent XPath 1.0 expression as a string.
-
- """
- return " | ".join(
- self.selector_to_xpath(selector, prefix, translate_pseudo_elements=True)
- for selector in parse(css)
- )
-
- def selector_to_xpath(
- self,
- selector: Selector,
- prefix: str = "descendant-or-self::",
- translate_pseudo_elements: bool = False,
- ) -> str:
- """Translate a parsed selector to XPath.
-
-
- :param selector:
- A parsed :class:`Selector` object.
- :param prefix:
- This string is prepended to the resulting XPath expression.
- The default makes selectors scoped to the context node’s subtree.
- :param translate_pseudo_elements:
- Unless this is set to ``True`` (as :meth:`css_to_xpath` does),
- the :attr:`~Selector.pseudo_element` attribute of the selector
- is ignored.
- It is the caller's responsibility to reject selectors
- with pseudo-elements, or to account for them somehow.
- :raises:
- :class:`ExpressionError` on unknown/unsupported selectors.
- :returns:
- The equivalent XPath 1.0 expression as a string.
-
- """
- tree = getattr(selector, "parsed_tree", None)
- if not tree:
- raise TypeError("Expected a parsed selector, got %r" % (selector,))
- xpath = self.xpath(tree)
- assert isinstance(xpath, self.xpathexpr_cls) # help debug a missing 'return'
- if translate_pseudo_elements and selector.pseudo_element:
- xpath = self.xpath_pseudo_element(xpath, selector.pseudo_element)
- return (prefix or "") + str(xpath)
-
- def xpath_pseudo_element(self, xpath: XPathExpr, pseudo_element: PseudoElement) -> XPathExpr:
- """Translate a pseudo-element.
-
- Defaults to not supporting pseudo-elements at all,
- but can be overridden by sub-classes.
-
- """
- raise ExpressionError("Pseudo-elements are not supported.")
-
- @staticmethod
- def xpath_literal(s: str) -> str:
- s = str(s)
- if "'" not in s:
- s = "'%s'" % s
- elif '"' not in s:
- s = '"%s"' % s
- else:
- s = "concat(%s)" % ",".join(
- [
- (("'" in part) and '"%s"' or "'%s'") % part
- for part in split_at_single_quotes(s)
- if part
- ]
- )
- return s
-
- def xpath(self, parsed_selector: Tree) -> XPathExpr:
- """Translate any parsed selector object."""
- type_name = type(parsed_selector).__name__
- method = getattr(self, "xpath_%s" % type_name.lower(), None)
- if method is None:
- raise ExpressionError("%s is not supported." % type_name)
- return typing.cast(XPathExpr, method(parsed_selector))
-
- # Dispatched by parsed object type
-
- def xpath_combinedselector(self, combined: CombinedSelector) -> XPathExpr:
- """Translate a combined selector."""
- combinator = self.combinator_mapping[combined.combinator]
- method = getattr(self, "xpath_%s_combinator" % combinator)
- return typing.cast(
- XPathExpr, method(self.xpath(combined.selector), self.xpath(combined.subselector))
- )
-
- def xpath_negation(self, negation: Negation) -> XPathExpr:
- xpath = self.xpath(negation.selector)
- sub_xpath = self.xpath(negation.subselector)
- sub_xpath.add_name_test()
- if sub_xpath.condition:
- return xpath.add_condition("not(%s)" % sub_xpath.condition)
- else:
- return xpath.add_condition("0")
-
- def xpath_relation(self, relation: Relation) -> XPathExpr:
- xpath = self.xpath(relation.selector)
- combinator = relation.combinator
- subselector = relation.subselector
- right = self.xpath(subselector.parsed_tree)
- method = getattr(
- self,
- "xpath_relation_%s_combinator"
- % self.combinator_mapping[typing.cast(str, combinator.value)],
- )
- return typing.cast(XPathExpr, method(xpath, right))
-
- def xpath_matching(self, matching: Matching) -> XPathExpr:
- xpath = self.xpath(matching.selector)
- exprs = [self.xpath(selector) for selector in matching.selector_list]
- for e in exprs:
- e.add_name_test()
- if e.condition:
- xpath.add_condition(e.condition, "or")
- return xpath
-
- def xpath_specificityadjustment(self, matching: SpecificityAdjustment) -> XPathExpr:
- xpath = self.xpath(matching.selector)
- exprs = [self.xpath(selector) for selector in matching.selector_list]
- for e in exprs:
- e.add_name_test()
- if e.condition:
- xpath.add_condition(e.condition, "or")
- return xpath
-
- def xpath_function(self, function: Function) -> XPathExpr:
- """Translate a functional pseudo-class."""
- method_name = "xpath_%s_function" % function.name.replace("-", "_")
- method = getattr(self, method_name, None)
- if not method:
- raise ExpressionError("The pseudo-class :%s() is unknown" % function.name)
- return typing.cast(XPathExpr, method(self.xpath(function.selector), function))
-
- def xpath_pseudo(self, pseudo: Pseudo) -> XPathExpr:
- """Translate a pseudo-class."""
- method_name = "xpath_%s_pseudo" % pseudo.ident.replace("-", "_")
- method = getattr(self, method_name, None)
- if not method:
- # TODO: better error message for pseudo-elements?
- raise ExpressionError("The pseudo-class :%s is unknown" % pseudo.ident)
- return typing.cast(XPathExpr, method(self.xpath(pseudo.selector)))
-
- def xpath_attrib(self, selector: Attrib) -> XPathExpr:
- """Translate an attribute selector."""
- operator = self.attribute_operator_mapping[selector.operator]
- method = getattr(self, "xpath_attrib_%s" % operator)
- if self.lower_case_attribute_names:
- name = selector.attrib.lower()
- else:
- name = selector.attrib
- safe = is_safe_name(name)
- if selector.namespace:
- name = "%s:%s" % (selector.namespace, name)
- safe = safe and is_safe_name(selector.namespace)
- if safe:
- attrib = "@" + name
- else:
- attrib = "attribute::*[name() = %s]" % self.xpath_literal(name)
- if selector.value is None:
- value = None
- elif self.lower_case_attribute_values:
- value = typing.cast(str, selector.value.value).lower()
- else:
- value = selector.value.value
- return typing.cast(XPathExpr, method(self.xpath(selector.selector), attrib, value))
-
- def xpath_class(self, class_selector: Class) -> XPathExpr:
- """Translate a class selector."""
- # .foo is defined as [class~=foo] in the spec.
- xpath = self.xpath(class_selector.selector)
- return self.xpath_attrib_includes(xpath, "@class", class_selector.class_name)
-
- def xpath_hash(self, id_selector: Hash) -> XPathExpr:
- """Translate an ID selector."""
- xpath = self.xpath(id_selector.selector)
- return self.xpath_attrib_equals(xpath, "@id", id_selector.id)
-
- def xpath_element(self, selector: Element) -> XPathExpr:
- """Translate a type or universal selector."""
- element = selector.element
- if not element:
- element = "*"
- safe = True
- else:
- safe = bool(is_safe_name(element))
- if self.lower_case_element_names:
- element = element.lower()
- if selector.namespace:
- # Namespace prefixes are case-sensitive.
- # http://www.w3.org/TR/css3-namespace/#prefixes
- element = "%s:%s" % (selector.namespace, element)
- safe = safe and bool(is_safe_name(selector.namespace))
- xpath = self.xpathexpr_cls(element=element)
- if not safe:
- xpath.add_name_test()
- return xpath
-
- # CombinedSelector: dispatch by combinator
-
- def xpath_descendant_combinator(self, left: XPathExpr, right: XPathExpr) -> XPathExpr:
- """right is a child, grand-child or further descendant of left"""
- return left.join("/descendant-or-self::*/", right)
-
- def xpath_child_combinator(self, left: XPathExpr, right: XPathExpr) -> XPathExpr:
- """right is an immediate child of left"""
- return left.join("/", right)
-
- def xpath_direct_adjacent_combinator(self, left: XPathExpr, right: XPathExpr) -> XPathExpr:
- """right is a sibling immediately after left"""
- xpath = left.join("/following-sibling::", right)
- xpath.add_name_test()
- return xpath.add_condition("position() = 1")
-
- def xpath_indirect_adjacent_combinator(self, left: XPathExpr, right: XPathExpr) -> XPathExpr:
- """right is a sibling after left, immediately or not"""
- return left.join("/following-sibling::", right)
-
- def xpath_relation_descendant_combinator(self, left: XPathExpr, right: XPathExpr) -> XPathExpr:
- """right is a child, grand-child or further descendant of left; select left"""
- return left.join("[descendant::", right, closing_combiner="]", has_inner_condition=True)
-
- def xpath_relation_child_combinator(self, left: XPathExpr, right: XPathExpr) -> XPathExpr:
- """right is an immediate child of left; select left"""
- return left.join("[./", right, closing_combiner="]")
-
- def xpath_relation_direct_adjacent_combinator(
- self, left: XPathExpr, right: XPathExpr
- ) -> XPathExpr:
- """right is a sibling immediately after left; select left"""
- xpath = left.add_condition(
- "following-sibling::*[(name() = '{}') and (position() = 1)]".format(right.element)
- )
- return xpath
-
- def xpath_relation_indirect_adjacent_combinator(
- self, left: XPathExpr, right: XPathExpr
- ) -> XPathExpr:
- """right is a sibling after left, immediately or not; select left"""
- return left.join("[following-sibling::", right, closing_combiner="]")
-
- # Function: dispatch by function/pseudo-class name
-
- def xpath_nth_child_function(
- self, xpath: XPathExpr, function: Function, last: bool = False, add_name_test: bool = True
- ) -> XPathExpr:
- try:
- a, b = parse_series(function.arguments)
- except ValueError:
- raise ExpressionError("Invalid series: '%r'" % function.arguments)
-
- # From https://www.w3.org/TR/css3-selectors/#structural-pseudos:
- #
- # :nth-child(an+b)
- # an+b-1 siblings before
- #
- # :nth-last-child(an+b)
- # an+b-1 siblings after
- #
- # :nth-of-type(an+b)
- # an+b-1 siblings with the same expanded element name before
- #
- # :nth-last-of-type(an+b)
- # an+b-1 siblings with the same expanded element name after
- #
- # So,
- # for :nth-child and :nth-of-type
- #
- # count(preceding-sibling::) = an+b-1
- #
- # for :nth-last-child and :nth-last-of-type
- #
- # count(following-sibling::) = an+b-1
- #
- # therefore,
- # count(...) - (b-1) ≡ 0 (mod a)
- #
- # if a == 0:
- # ~~~~~~~~~~
- # count(...) = b-1
- #
- # if a < 0:
- # ~~~~~~~~~
- # count(...) - b +1 <= 0
- # -> count(...) <= b-1
- #
- # if a > 0:
- # ~~~~~~~~~
- # count(...) - b +1 >= 0
- # -> count(...) >= b-1
-
- # work with b-1 instead
- b_min_1 = b - 1
-
- # early-exit condition 1:
- # ~~~~~~~~~~~~~~~~~~~~~~~
- # for a == 1, nth-*(an+b) means n+b-1 siblings before/after,
- # and since n ∈ {0, 1, 2, ...}, if b-1<=0,
- # there is always an "n" matching any number of siblings (maybe none)
- if a == 1 and b_min_1 <= 0:
- return xpath
-
- # early-exit condition 2:
- # ~~~~~~~~~~~~~~~~~~~~~~~
- # an+b-1 siblings with a<0 and (b-1)<0 is not possible
- if a < 0 and b_min_1 < 0:
- return xpath.add_condition("0")
-
- # `add_name_test` boolean is inverted and somewhat counter-intuitive:
- #
- # nth_of_type() calls nth_child(add_name_test=False)
- if add_name_test:
- nodetest = "*"
- else:
- nodetest = "%s" % xpath.element
-
- # count siblings before or after the element
- if not last:
- siblings_count = "count(preceding-sibling::%s)" % nodetest
- else:
- siblings_count = "count(following-sibling::%s)" % nodetest
-
- # special case of fixed position: nth-*(0n+b)
- # if a == 0:
- # ~~~~~~~~~~
- # count(***-sibling::***) = b-1
- if a == 0:
- return xpath.add_condition("%s = %s" % (siblings_count, b_min_1))
-
- expressions = []
-
- if a > 0:
- # siblings count, an+b-1, is always >= 0,
- # so if a>0, and (b-1)<=0, an "n" exists to satisfy this,
- # therefore, the predicate is only interesting if (b-1)>0
- if b_min_1 > 0:
- expressions.append("%s >= %s" % (siblings_count, b_min_1))
- else:
- # if a<0, and (b-1)<0, no "n" satisfies this,
- # this is tested above as an early exist condition
- # otherwise,
- expressions.append("%s <= %s" % (siblings_count, b_min_1))
-
- # operations modulo 1 or -1 are simpler, one only needs to verify:
- #
- # - either:
- # count(***-sibling::***) - (b-1) = n = 0, 1, 2, 3, etc.,
- # i.e. count(***-sibling::***) >= (b-1)
- #
- # - or:
- # count(***-sibling::***) - (b-1) = -n = 0, -1, -2, -3, etc.,
- # i.e. count(***-sibling::***) <= (b-1)
- # we we just did above.
- #
- if abs(a) != 1:
- # count(***-sibling::***) - (b-1) ≡ 0 (mod a)
- left = siblings_count
-
- # apply "modulo a" on 2nd term, -(b-1),
- # to simplify things like "(... +6) % -3",
- # and also make it positive with |a|
- b_neg = (-b_min_1) % abs(a)
-
- if b_neg != 0:
- b_neg_as_str = "+%s" % b_neg
- left = "(%s %s)" % (left, b_neg_as_str)
-
- expressions.append("%s mod %s = 0" % (left, a))
-
- if len(expressions) > 1:
- template = "(%s)"
- else:
- template = "%s"
- xpath.add_condition(" and ".join(template % expression for expression in expressions))
- return xpath
-
- def xpath_nth_last_child_function(self, xpath: XPathExpr, function: Function) -> XPathExpr:
- return self.xpath_nth_child_function(xpath, function, last=True)
-
- def xpath_nth_of_type_function(self, xpath: XPathExpr, function: Function) -> XPathExpr:
- if xpath.element == "*":
- raise ExpressionError("*:nth-of-type() is not implemented")
- return self.xpath_nth_child_function(xpath, function, add_name_test=False)
-
- def xpath_nth_last_of_type_function(self, xpath: XPathExpr, function: Function) -> XPathExpr:
- if xpath.element == "*":
- raise ExpressionError("*:nth-of-type() is not implemented")
- return self.xpath_nth_child_function(xpath, function, last=True, add_name_test=False)
-
- def xpath_contains_function(self, xpath: XPathExpr, function: Function) -> XPathExpr:
- # Defined there, removed in later drafts:
- # http://www.w3.org/TR/2001/CR-css3-selectors-20011113/#content-selectors
- if function.argument_types() not in (["STRING"], ["IDENT"]):
- raise ExpressionError(
- "Expected a single string or ident for :contains(), got %r" % function.arguments
- )
- value = typing.cast(str, function.arguments[0].value)
- return xpath.add_condition("contains(., %s)" % self.xpath_literal(value))
-
- def xpath_lang_function(self, xpath: XPathExpr, function: Function) -> XPathExpr:
- if function.argument_types() not in (["STRING"], ["IDENT"]):
- raise ExpressionError(
- "Expected a single string or ident for :lang(), got %r" % function.arguments
- )
- value = typing.cast(str, function.arguments[0].value)
- return xpath.add_condition("lang(%s)" % (self.xpath_literal(value)))
-
- # Pseudo: dispatch by pseudo-class name
-
- def xpath_root_pseudo(self, xpath: XPathExpr) -> XPathExpr:
- return xpath.add_condition("not(parent::*)")
-
- # CSS immediate children (CSS ":scope > div" to XPath "child::div" or "./div")
- # Works only at the start of a selector
- # Needed to get immediate children of a processed selector in Scrapy
- # for product in response.css('.product'):
- # description = product.css(':scope > div::text').get()
- def xpath_scope_pseudo(self, xpath: XPathExpr) -> XPathExpr:
- return xpath.add_condition("1")
-
- def xpath_first_child_pseudo(self, xpath: XPathExpr) -> XPathExpr:
- return xpath.add_condition("count(preceding-sibling::*) = 0")
-
- def xpath_last_child_pseudo(self, xpath: XPathExpr) -> XPathExpr:
- return xpath.add_condition("count(following-sibling::*) = 0")
-
- def xpath_first_of_type_pseudo(self, xpath: XPathExpr) -> XPathExpr:
- if xpath.element == "*":
- raise ExpressionError("*:first-of-type is not implemented")
- return xpath.add_condition("count(preceding-sibling::%s) = 0" % xpath.element)
-
- def xpath_last_of_type_pseudo(self, xpath: XPathExpr) -> XPathExpr:
- if xpath.element == "*":
- raise ExpressionError("*:last-of-type is not implemented")
- return xpath.add_condition("count(following-sibling::%s) = 0" % xpath.element)
-
- def xpath_only_child_pseudo(self, xpath: XPathExpr) -> XPathExpr:
- return xpath.add_condition("count(parent::*/child::*) = 1")
-
- def xpath_only_of_type_pseudo(self, xpath: XPathExpr) -> XPathExpr:
- if xpath.element == "*":
- raise ExpressionError("*:only-of-type is not implemented")
- return xpath.add_condition("count(parent::*/child::%s) = 1" % xpath.element)
-
- def xpath_empty_pseudo(self, xpath: XPathExpr) -> XPathExpr:
- return xpath.add_condition("not(*) and not(string-length())")
-
- def pseudo_never_matches(self, xpath: XPathExpr) -> XPathExpr:
- """Common implementation for pseudo-classes that never match."""
- return xpath.add_condition("0")
-
- xpath_link_pseudo = pseudo_never_matches
- xpath_visited_pseudo = pseudo_never_matches
- xpath_hover_pseudo = pseudo_never_matches
- xpath_active_pseudo = pseudo_never_matches
- xpath_focus_pseudo = pseudo_never_matches
- xpath_target_pseudo = pseudo_never_matches
- xpath_enabled_pseudo = pseudo_never_matches
- xpath_disabled_pseudo = pseudo_never_matches
- xpath_checked_pseudo = pseudo_never_matches
-
- # Attrib: dispatch by attribute operator
-
- def xpath_attrib_exists(self, xpath: XPathExpr, name: str, value: Optional[str]) -> XPathExpr:
- assert not value
- xpath.add_condition(name)
- return xpath
-
- def xpath_attrib_equals(self, xpath: XPathExpr, name: str, value: Optional[str]) -> XPathExpr:
- assert value is not None
- xpath.add_condition("%s = %s" % (name, self.xpath_literal(value)))
- return xpath
-
- def xpath_attrib_different(
- self, xpath: XPathExpr, name: str, value: Optional[str]
- ) -> XPathExpr:
- assert value is not None
- # FIXME: this seems like a weird hack...
- if value:
- xpath.add_condition("not(%s) or %s != %s" % (name, name, self.xpath_literal(value)))
- else:
- xpath.add_condition("%s != %s" % (name, self.xpath_literal(value)))
- return xpath
-
- def xpath_attrib_includes(
- self, xpath: XPathExpr, name: str, value: Optional[str]
- ) -> XPathExpr:
- if value and is_non_whitespace(value):
- xpath.add_condition(
- "%s and contains(concat(' ', normalize-space(%s), ' '), %s)"
- % (name, name, self.xpath_literal(" " + value + " "))
- )
- else:
- xpath.add_condition("0")
- return xpath
-
- def xpath_attrib_dashmatch(
- self, xpath: XPathExpr, name: str, value: Optional[str]
- ) -> XPathExpr:
- assert value is not None
- # Weird, but true...
- xpath.add_condition(
- "%s and (%s = %s or starts-with(%s, %s))"
- % (name, name, self.xpath_literal(value), name, self.xpath_literal(value + "-"))
- )
- return xpath
-
- def xpath_attrib_prefixmatch(
- self, xpath: XPathExpr, name: str, value: Optional[str]
- ) -> XPathExpr:
- if value:
- xpath.add_condition(
- "%s and starts-with(%s, %s)" % (name, name, self.xpath_literal(value))
- )
- else:
- xpath.add_condition("0")
- return xpath
-
- def xpath_attrib_suffixmatch(
- self, xpath: XPathExpr, name: str, value: Optional[str]
- ) -> XPathExpr:
- if value:
- # Oddly there is a starts-with in XPath 1.0, but not ends-with
- xpath.add_condition(
- "%s and substring(%s, string-length(%s)-%s) = %s"
- % (name, name, name, len(value) - 1, self.xpath_literal(value))
- )
- else:
- xpath.add_condition("0")
- return xpath
-
- def xpath_attrib_substringmatch(
- self, xpath: XPathExpr, name: str, value: Optional[str]
- ) -> XPathExpr:
- if value:
- # Attribute selectors are case sensitive
- xpath.add_condition(
- "%s and contains(%s, %s)" % (name, name, self.xpath_literal(value))
- )
- else:
- xpath.add_condition("0")
- return xpath
-
-
-class HTMLTranslator(GenericTranslator):
- """
- Translator for (X)HTML documents.
-
- Has a more useful implementation of some pseudo-classes based on
- HTML-specific element names and attribute names, as described in
- the `HTML5 specification`_. It assumes no-quirks mode.
- The API is the same as :class:`GenericTranslator`.
-
- .. _HTML5 specification: http://www.w3.org/TR/html5/links.html#selectors
-
- :param xhtml:
- If false (the default), element names and attribute names
- are case-insensitive.
-
- """
-
- lang_attribute = "lang"
-
- def __init__(self, xhtml: bool = False) -> None:
- self.xhtml = xhtml # Might be useful for sub-classes?
- if not xhtml:
- # See their definition in GenericTranslator.
- self.lower_case_element_names = True
- self.lower_case_attribute_names = True
-
- def xpath_checked_pseudo(self, xpath: XPathExpr) -> XPathExpr: # type: ignore
- # FIXME: is this really all the elements?
- return xpath.add_condition(
- "(@selected and name(.) = 'option') or "
- "(@checked "
- "and (name(.) = 'input' or name(.) = 'command')"
- "and (@type = 'checkbox' or @type = 'radio'))"
- )
-
- def xpath_lang_function(self, xpath: XPathExpr, function: Function) -> XPathExpr:
- if function.argument_types() not in (["STRING"], ["IDENT"]):
- raise ExpressionError(
- "Expected a single string or ident for :lang(), got %r" % function.arguments
- )
- value = function.arguments[0].value
- assert value
- return xpath.add_condition(
- "ancestor-or-self::*[@lang][1][starts-with(concat("
- # XPath 1.0 has no lower-case function...
- "translate(@%s, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', "
- "'abcdefghijklmnopqrstuvwxyz'), "
- "'-'), %s)]" % (self.lang_attribute, self.xpath_literal(value.lower() + "-"))
- )
-
- def xpath_link_pseudo(self, xpath: XPathExpr) -> XPathExpr: # type: ignore
- return xpath.add_condition(
- "@href and (name(.) = 'a' or name(.) = 'link' or name(.) = 'area')"
- )
-
- # Links are never visited, the implementation for :visited is the same
- # as in GenericTranslator
-
- def xpath_disabled_pseudo(self, xpath: XPathExpr) -> XPathExpr: # type: ignore
- # http://www.w3.org/TR/html5/section-index.html#attributes-1
- return xpath.add_condition(
- """
- (
- @disabled and
- (
- (name(.) = 'input' and @type != 'hidden') or
- name(.) = 'button' or
- name(.) = 'select' or
- name(.) = 'textarea' or
- name(.) = 'command' or
- name(.) = 'fieldset' or
- name(.) = 'optgroup' or
- name(.) = 'option'
- )
- ) or (
- (
- (name(.) = 'input' and @type != 'hidden') or
- name(.) = 'button' or
- name(.) = 'select' or
- name(.) = 'textarea'
- )
- and ancestor::fieldset[@disabled]
- )
- """
- )
- # FIXME: in the second half, add "and is not a descendant of that
- # fieldset element's first legend element child, if any."
-
- def xpath_enabled_pseudo(self, xpath: XPathExpr) -> XPathExpr: # type: ignore
- # http://www.w3.org/TR/html5/section-index.html#attributes-1
- return xpath.add_condition(
- """
- (
- @href and (
- name(.) = 'a' or
- name(.) = 'link' or
- name(.) = 'area'
- )
- ) or (
- (
- name(.) = 'command' or
- name(.) = 'fieldset' or
- name(.) = 'optgroup'
- )
- and not(@disabled)
- ) or (
- (
- (name(.) = 'input' and @type != 'hidden') or
- name(.) = 'button' or
- name(.) = 'select' or
- name(.) = 'textarea' or
- name(.) = 'keygen'
- )
- and not (@disabled or ancestor::fieldset[@disabled])
- ) or (
- name(.) = 'option' and not(
- @disabled or ancestor::optgroup[@disabled]
- )
- )
- """
- )
- # FIXME: ... or "li elements that are children of menu elements,
- # and that have a child element that defines a command, if the first
- # such element's Disabled State facet is false (not disabled)".
- # FIXME: after ancestor::fieldset[@disabled], add "and is not a
- # descendant of that fieldset element's first legend element child,
- # if any."
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/__init__.py
deleted file mode 100644
index 499eb9c..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/__init__.py
+++ /dev/null
@@ -1,8 +0,0 @@
-import logging
-from fontTools.misc.loggingTools import configLogger
-
-log = logging.getLogger(__name__)
-
-version = __version__ = "4.40.1.dev0"
-
-__all__ = ["version", "log", "configLogger"]
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/__main__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/__main__.py
deleted file mode 100644
index 7c74ad3..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/__main__.py
+++ /dev/null
@@ -1,35 +0,0 @@
-import sys
-
-
-def main(args=None):
- if args is None:
- args = sys.argv[1:]
-
- # TODO Handle library-wide options. Eg.:
- # --unicodedata
- # --verbose / other logging stuff
-
- # TODO Allow a way to run arbitrary modules? Useful for setting
- # library-wide options and calling another library. Eg.:
- #
- # $ fonttools --unicodedata=... fontmake ...
- #
- # This allows for a git-like command where thirdparty commands
- # can be added. Should we just try importing the fonttools
- # module first and try without if it fails?
-
- if len(sys.argv) < 2:
- sys.argv.append("help")
- if sys.argv[1] == "-h" or sys.argv[1] == "--help":
- sys.argv[1] = "help"
- mod = "fontTools." + sys.argv[1]
- sys.argv[1] = sys.argv[0] + " " + sys.argv[1]
- del sys.argv[0]
-
- import runpy
-
- runpy.run_module(mod, run_name="__main__")
-
-
-if __name__ == "__main__":
- sys.exit(main())
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/afmLib.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/afmLib.py
deleted file mode 100644
index 198559d..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/afmLib.py
+++ /dev/null
@@ -1,440 +0,0 @@
-"""Module for reading and writing AFM (Adobe Font Metrics) files.
-
-Note that this has been designed to read in AFM files generated by Fontographer
-and has not been tested on many other files. In particular, it does not
-implement the whole Adobe AFM specification [#f1]_ but, it should read most
-"common" AFM files.
-
-Here is an example of using `afmLib` to read, modify and write an AFM file:
-
- >>> from fontTools.afmLib import AFM
- >>> f = AFM("Tests/afmLib/data/TestAFM.afm")
- >>>
- >>> # Accessing a pair gets you the kern value
- >>> f[("V","A")]
- -60
- >>>
- >>> # Accessing a glyph name gets you metrics
- >>> f["A"]
- (65, 668, (8, -25, 660, 666))
- >>> # (charnum, width, bounding box)
- >>>
- >>> # Accessing an attribute gets you metadata
- >>> f.FontName
- 'TestFont-Regular'
- >>> f.FamilyName
- 'TestFont'
- >>> f.Weight
- 'Regular'
- >>> f.XHeight
- 500
- >>> f.Ascender
- 750
- >>>
- >>> # Attributes and items can also be set
- >>> f[("A","V")] = -150 # Tighten kerning
- >>> f.FontName = "TestFont Squished"
- >>>
- >>> # And the font written out again (remove the # in front)
- >>> #f.write("testfont-squished.afm")
-
-.. rubric:: Footnotes
-
-.. [#f1] `Adobe Technote 5004 `_,
- Adobe Font Metrics File Format Specification.
-
-"""
-
-
-import re
-
-# every single line starts with a "word"
-identifierRE = re.compile(r"^([A-Za-z]+).*")
-
-# regular expression to parse char lines
-charRE = re.compile(
- r"(-?\d+)" # charnum
- r"\s*;\s*WX\s+" # ; WX
- r"(-?\d+)" # width
- r"\s*;\s*N\s+" # ; N
- r"([.A-Za-z0-9_]+)" # charname
- r"\s*;\s*B\s+" # ; B
- r"(-?\d+)" # left
- r"\s+"
- r"(-?\d+)" # bottom
- r"\s+"
- r"(-?\d+)" # right
- r"\s+"
- r"(-?\d+)" # top
- r"\s*;\s*" # ;
-)
-
-# regular expression to parse kerning lines
-kernRE = re.compile(
- r"([.A-Za-z0-9_]+)" # leftchar
- r"\s+"
- r"([.A-Za-z0-9_]+)" # rightchar
- r"\s+"
- r"(-?\d+)" # value
- r"\s*"
-)
-
-# regular expressions to parse composite info lines of the form:
-# Aacute 2 ; PCC A 0 0 ; PCC acute 182 211 ;
-compositeRE = re.compile(
- r"([.A-Za-z0-9_]+)"
- r"\s+"
- r"(\d+)"
- r"\s*;\s*" # char name # number of parts
-)
-componentRE = re.compile(
- r"PCC\s+" # PPC
- r"([.A-Za-z0-9_]+)" # base char name
- r"\s+"
- r"(-?\d+)" # x offset
- r"\s+"
- r"(-?\d+)" # y offset
- r"\s*;\s*"
-)
-
-preferredAttributeOrder = [
- "FontName",
- "FullName",
- "FamilyName",
- "Weight",
- "ItalicAngle",
- "IsFixedPitch",
- "FontBBox",
- "UnderlinePosition",
- "UnderlineThickness",
- "Version",
- "Notice",
- "EncodingScheme",
- "CapHeight",
- "XHeight",
- "Ascender",
- "Descender",
-]
-
-
-class error(Exception):
- pass
-
-
-class AFM(object):
- _attrs = None
-
- _keywords = [
- "StartFontMetrics",
- "EndFontMetrics",
- "StartCharMetrics",
- "EndCharMetrics",
- "StartKernData",
- "StartKernPairs",
- "EndKernPairs",
- "EndKernData",
- "StartComposites",
- "EndComposites",
- ]
-
- def __init__(self, path=None):
- """AFM file reader.
-
- Instantiating an object with a path name will cause the file to be opened,
- read, and parsed. Alternatively the path can be left unspecified, and a
- file can be parsed later with the :meth:`read` method."""
- self._attrs = {}
- self._chars = {}
- self._kerning = {}
- self._index = {}
- self._comments = []
- self._composites = {}
- if path is not None:
- self.read(path)
-
- def read(self, path):
- """Opens, reads and parses a file."""
- lines = readlines(path)
- for line in lines:
- if not line.strip():
- continue
- m = identifierRE.match(line)
- if m is None:
- raise error("syntax error in AFM file: " + repr(line))
-
- pos = m.regs[1][1]
- word = line[:pos]
- rest = line[pos:].strip()
- if word in self._keywords:
- continue
- if word == "C":
- self.parsechar(rest)
- elif word == "KPX":
- self.parsekernpair(rest)
- elif word == "CC":
- self.parsecomposite(rest)
- else:
- self.parseattr(word, rest)
-
- def parsechar(self, rest):
- m = charRE.match(rest)
- if m is None:
- raise error("syntax error in AFM file: " + repr(rest))
- things = []
- for fr, to in m.regs[1:]:
- things.append(rest[fr:to])
- charname = things[2]
- del things[2]
- charnum, width, l, b, r, t = (int(thing) for thing in things)
- self._chars[charname] = charnum, width, (l, b, r, t)
-
- def parsekernpair(self, rest):
- m = kernRE.match(rest)
- if m is None:
- raise error("syntax error in AFM file: " + repr(rest))
- things = []
- for fr, to in m.regs[1:]:
- things.append(rest[fr:to])
- leftchar, rightchar, value = things
- value = int(value)
- self._kerning[(leftchar, rightchar)] = value
-
- def parseattr(self, word, rest):
- if word == "FontBBox":
- l, b, r, t = [int(thing) for thing in rest.split()]
- self._attrs[word] = l, b, r, t
- elif word == "Comment":
- self._comments.append(rest)
- else:
- try:
- value = int(rest)
- except (ValueError, OverflowError):
- self._attrs[word] = rest
- else:
- self._attrs[word] = value
-
- def parsecomposite(self, rest):
- m = compositeRE.match(rest)
- if m is None:
- raise error("syntax error in AFM file: " + repr(rest))
- charname = m.group(1)
- ncomponents = int(m.group(2))
- rest = rest[m.regs[0][1] :]
- components = []
- while True:
- m = componentRE.match(rest)
- if m is None:
- raise error("syntax error in AFM file: " + repr(rest))
- basechar = m.group(1)
- xoffset = int(m.group(2))
- yoffset = int(m.group(3))
- components.append((basechar, xoffset, yoffset))
- rest = rest[m.regs[0][1] :]
- if not rest:
- break
- assert len(components) == ncomponents
- self._composites[charname] = components
-
- def write(self, path, sep="\r"):
- """Writes out an AFM font to the given path."""
- import time
-
- lines = [
- "StartFontMetrics 2.0",
- "Comment Generated by afmLib; at %s"
- % (time.strftime("%m/%d/%Y %H:%M:%S", time.localtime(time.time()))),
- ]
-
- # write comments, assuming (possibly wrongly!) they should
- # all appear at the top
- for comment in self._comments:
- lines.append("Comment " + comment)
-
- # write attributes, first the ones we know about, in
- # a preferred order
- attrs = self._attrs
- for attr in preferredAttributeOrder:
- if attr in attrs:
- value = attrs[attr]
- if attr == "FontBBox":
- value = "%s %s %s %s" % value
- lines.append(attr + " " + str(value))
- # then write the attributes we don't know about,
- # in alphabetical order
- items = sorted(attrs.items())
- for attr, value in items:
- if attr in preferredAttributeOrder:
- continue
- lines.append(attr + " " + str(value))
-
- # write char metrics
- lines.append("StartCharMetrics " + repr(len(self._chars)))
- items = [
- (charnum, (charname, width, box))
- for charname, (charnum, width, box) in self._chars.items()
- ]
-
- def myKey(a):
- """Custom key function to make sure unencoded chars (-1)
- end up at the end of the list after sorting."""
- if a[0] == -1:
- a = (0xFFFF,) + a[1:] # 0xffff is an arbitrary large number
- return a
-
- items.sort(key=myKey)
-
- for charnum, (charname, width, (l, b, r, t)) in items:
- lines.append(
- "C %d ; WX %d ; N %s ; B %d %d %d %d ;"
- % (charnum, width, charname, l, b, r, t)
- )
- lines.append("EndCharMetrics")
-
- # write kerning info
- lines.append("StartKernData")
- lines.append("StartKernPairs " + repr(len(self._kerning)))
- items = sorted(self._kerning.items())
- for (leftchar, rightchar), value in items:
- lines.append("KPX %s %s %d" % (leftchar, rightchar, value))
- lines.append("EndKernPairs")
- lines.append("EndKernData")
-
- if self._composites:
- composites = sorted(self._composites.items())
- lines.append("StartComposites %s" % len(self._composites))
- for charname, components in composites:
- line = "CC %s %s ;" % (charname, len(components))
- for basechar, xoffset, yoffset in components:
- line = line + " PCC %s %s %s ;" % (basechar, xoffset, yoffset)
- lines.append(line)
- lines.append("EndComposites")
-
- lines.append("EndFontMetrics")
-
- writelines(path, lines, sep)
-
- def has_kernpair(self, pair):
- """Returns `True` if the given glyph pair (specified as a tuple) exists
- in the kerning dictionary."""
- return pair in self._kerning
-
- def kernpairs(self):
- """Returns a list of all kern pairs in the kerning dictionary."""
- return list(self._kerning.keys())
-
- def has_char(self, char):
- """Returns `True` if the given glyph exists in the font."""
- return char in self._chars
-
- def chars(self):
- """Returns a list of all glyph names in the font."""
- return list(self._chars.keys())
-
- def comments(self):
- """Returns all comments from the file."""
- return self._comments
-
- def addComment(self, comment):
- """Adds a new comment to the file."""
- self._comments.append(comment)
-
- def addComposite(self, glyphName, components):
- """Specifies that the glyph `glyphName` is made up of the given components.
- The components list should be of the following form::
-
- [
- (glyphname, xOffset, yOffset),
- ...
- ]
-
- """
- self._composites[glyphName] = components
-
- def __getattr__(self, attr):
- if attr in self._attrs:
- return self._attrs[attr]
- else:
- raise AttributeError(attr)
-
- def __setattr__(self, attr, value):
- # all attrs *not* starting with "_" are consider to be AFM keywords
- if attr[:1] == "_":
- self.__dict__[attr] = value
- else:
- self._attrs[attr] = value
-
- def __delattr__(self, attr):
- # all attrs *not* starting with "_" are consider to be AFM keywords
- if attr[:1] == "_":
- try:
- del self.__dict__[attr]
- except KeyError:
- raise AttributeError(attr)
- else:
- try:
- del self._attrs[attr]
- except KeyError:
- raise AttributeError(attr)
-
- def __getitem__(self, key):
- if isinstance(key, tuple):
- # key is a tuple, return the kernpair
- return self._kerning[key]
- else:
- # return the metrics instead
- return self._chars[key]
-
- def __setitem__(self, key, value):
- if isinstance(key, tuple):
- # key is a tuple, set kernpair
- self._kerning[key] = value
- else:
- # set char metrics
- self._chars[key] = value
-
- def __delitem__(self, key):
- if isinstance(key, tuple):
- # key is a tuple, del kernpair
- del self._kerning[key]
- else:
- # del char metrics
- del self._chars[key]
-
- def __repr__(self):
- if hasattr(self, "FullName"):
- return "" % self.FullName
- else:
- return "" % id(self)
-
-
-def readlines(path):
- with open(path, "r", encoding="ascii") as f:
- data = f.read()
- return data.splitlines()
-
-
-def writelines(path, lines, sep="\r"):
- with open(path, "w", encoding="ascii", newline=sep) as f:
- f.write("\n".join(lines) + "\n")
-
-
-if __name__ == "__main__":
- import EasyDialogs
-
- path = EasyDialogs.AskFileForOpen()
- if path:
- afm = AFM(path)
- char = "A"
- if afm.has_char(char):
- print(afm[char]) # print charnum, width and boundingbox
- pair = ("A", "V")
- if afm.has_kernpair(pair):
- print(afm[pair]) # print kerning value for pair
- print(afm.Version) # various other afm entries have become attributes
- print(afm.Weight)
- # afm.comments() returns a list of all Comment lines found in the AFM
- print(afm.comments())
- # print afm.chars()
- # print afm.kernpairs()
- print(afm)
- afm.write(path + ".muck")
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/agl.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/agl.py
deleted file mode 100644
index d699462..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/agl.py
+++ /dev/null
@@ -1,5233 +0,0 @@
-# -*- coding: utf-8 -*-
-# The tables below are taken from
-# https://github.com/adobe-type-tools/agl-aglfn/raw/4036a9ca80a62f64f9de4f7321a9a045ad0ecfd6/glyphlist.txt
-# and
-# https://github.com/adobe-type-tools/agl-aglfn/raw/4036a9ca80a62f64f9de4f7321a9a045ad0ecfd6/aglfn.txt
-"""
-Interface to the Adobe Glyph List
-
-This module exists to convert glyph names from the Adobe Glyph List
-to their Unicode equivalents. Example usage:
-
- >>> from fontTools.agl import toUnicode
- >>> toUnicode("nahiragana")
- 'な'
-
-It also contains two dictionaries, ``UV2AGL`` and ``AGL2UV``, which map from
-Unicode codepoints to AGL names and vice versa:
-
- >>> import fontTools
- >>> fontTools.agl.UV2AGL[ord("?")]
- 'question'
- >>> fontTools.agl.AGL2UV["wcircumflex"]
- 373
-
-This is used by fontTools when it has to construct glyph names for a font which
-doesn't include any (e.g. format 3.0 post tables).
-"""
-
-from fontTools.misc.textTools import tostr
-import re
-
-
-_aglText = """\
-# -----------------------------------------------------------
-# Copyright 2002-2019 Adobe (http://www.adobe.com/).
-#
-# Redistribution and use in source and binary forms, with or
-# without modification, are permitted provided that the
-# following conditions are met:
-#
-# Redistributions of source code must retain the above
-# copyright notice, this list of conditions and the following
-# disclaimer.
-#
-# Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following
-# disclaimer in the documentation and/or other materials
-# provided with the distribution.
-#
-# Neither the name of Adobe nor the names of its contributors
-# may be used to endorse or promote products derived from this
-# software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
-# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
-# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
-# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-# -----------------------------------------------------------
-# Name: Adobe Glyph List
-# Table version: 2.0
-# Date: September 20, 2002
-# URL: https://github.com/adobe-type-tools/agl-aglfn
-#
-# Format: two semicolon-delimited fields:
-# (1) glyph name--upper/lowercase letters and digits
-# (2) Unicode scalar value--four uppercase hexadecimal digits
-#
-A;0041
-AE;00C6
-AEacute;01FC
-AEmacron;01E2
-AEsmall;F7E6
-Aacute;00C1
-Aacutesmall;F7E1
-Abreve;0102
-Abreveacute;1EAE
-Abrevecyrillic;04D0
-Abrevedotbelow;1EB6
-Abrevegrave;1EB0
-Abrevehookabove;1EB2
-Abrevetilde;1EB4
-Acaron;01CD
-Acircle;24B6
-Acircumflex;00C2
-Acircumflexacute;1EA4
-Acircumflexdotbelow;1EAC
-Acircumflexgrave;1EA6
-Acircumflexhookabove;1EA8
-Acircumflexsmall;F7E2
-Acircumflextilde;1EAA
-Acute;F6C9
-Acutesmall;F7B4
-Acyrillic;0410
-Adblgrave;0200
-Adieresis;00C4
-Adieresiscyrillic;04D2
-Adieresismacron;01DE
-Adieresissmall;F7E4
-Adotbelow;1EA0
-Adotmacron;01E0
-Agrave;00C0
-Agravesmall;F7E0
-Ahookabove;1EA2
-Aiecyrillic;04D4
-Ainvertedbreve;0202
-Alpha;0391
-Alphatonos;0386
-Amacron;0100
-Amonospace;FF21
-Aogonek;0104
-Aring;00C5
-Aringacute;01FA
-Aringbelow;1E00
-Aringsmall;F7E5
-Asmall;F761
-Atilde;00C3
-Atildesmall;F7E3
-Aybarmenian;0531
-B;0042
-Bcircle;24B7
-Bdotaccent;1E02
-Bdotbelow;1E04
-Becyrillic;0411
-Benarmenian;0532
-Beta;0392
-Bhook;0181
-Blinebelow;1E06
-Bmonospace;FF22
-Brevesmall;F6F4
-Bsmall;F762
-Btopbar;0182
-C;0043
-Caarmenian;053E
-Cacute;0106
-Caron;F6CA
-Caronsmall;F6F5
-Ccaron;010C
-Ccedilla;00C7
-Ccedillaacute;1E08
-Ccedillasmall;F7E7
-Ccircle;24B8
-Ccircumflex;0108
-Cdot;010A
-Cdotaccent;010A
-Cedillasmall;F7B8
-Chaarmenian;0549
-Cheabkhasiancyrillic;04BC
-Checyrillic;0427
-Chedescenderabkhasiancyrillic;04BE
-Chedescendercyrillic;04B6
-Chedieresiscyrillic;04F4
-Cheharmenian;0543
-Chekhakassiancyrillic;04CB
-Cheverticalstrokecyrillic;04B8
-Chi;03A7
-Chook;0187
-Circumflexsmall;F6F6
-Cmonospace;FF23
-Coarmenian;0551
-Csmall;F763
-D;0044
-DZ;01F1
-DZcaron;01C4
-Daarmenian;0534
-Dafrican;0189
-Dcaron;010E
-Dcedilla;1E10
-Dcircle;24B9
-Dcircumflexbelow;1E12
-Dcroat;0110
-Ddotaccent;1E0A
-Ddotbelow;1E0C
-Decyrillic;0414
-Deicoptic;03EE
-Delta;2206
-Deltagreek;0394
-Dhook;018A
-Dieresis;F6CB
-DieresisAcute;F6CC
-DieresisGrave;F6CD
-Dieresissmall;F7A8
-Digammagreek;03DC
-Djecyrillic;0402
-Dlinebelow;1E0E
-Dmonospace;FF24
-Dotaccentsmall;F6F7
-Dslash;0110
-Dsmall;F764
-Dtopbar;018B
-Dz;01F2
-Dzcaron;01C5
-Dzeabkhasiancyrillic;04E0
-Dzecyrillic;0405
-Dzhecyrillic;040F
-E;0045
-Eacute;00C9
-Eacutesmall;F7E9
-Ebreve;0114
-Ecaron;011A
-Ecedillabreve;1E1C
-Echarmenian;0535
-Ecircle;24BA
-Ecircumflex;00CA
-Ecircumflexacute;1EBE
-Ecircumflexbelow;1E18
-Ecircumflexdotbelow;1EC6
-Ecircumflexgrave;1EC0
-Ecircumflexhookabove;1EC2
-Ecircumflexsmall;F7EA
-Ecircumflextilde;1EC4
-Ecyrillic;0404
-Edblgrave;0204
-Edieresis;00CB
-Edieresissmall;F7EB
-Edot;0116
-Edotaccent;0116
-Edotbelow;1EB8
-Efcyrillic;0424
-Egrave;00C8
-Egravesmall;F7E8
-Eharmenian;0537
-Ehookabove;1EBA
-Eightroman;2167
-Einvertedbreve;0206
-Eiotifiedcyrillic;0464
-Elcyrillic;041B
-Elevenroman;216A
-Emacron;0112
-Emacronacute;1E16
-Emacrongrave;1E14
-Emcyrillic;041C
-Emonospace;FF25
-Encyrillic;041D
-Endescendercyrillic;04A2
-Eng;014A
-Enghecyrillic;04A4
-Enhookcyrillic;04C7
-Eogonek;0118
-Eopen;0190
-Epsilon;0395
-Epsilontonos;0388
-Ercyrillic;0420
-Ereversed;018E
-Ereversedcyrillic;042D
-Escyrillic;0421
-Esdescendercyrillic;04AA
-Esh;01A9
-Esmall;F765
-Eta;0397
-Etarmenian;0538
-Etatonos;0389
-Eth;00D0
-Ethsmall;F7F0
-Etilde;1EBC
-Etildebelow;1E1A
-Euro;20AC
-Ezh;01B7
-Ezhcaron;01EE
-Ezhreversed;01B8
-F;0046
-Fcircle;24BB
-Fdotaccent;1E1E
-Feharmenian;0556
-Feicoptic;03E4
-Fhook;0191
-Fitacyrillic;0472
-Fiveroman;2164
-Fmonospace;FF26
-Fourroman;2163
-Fsmall;F766
-G;0047
-GBsquare;3387
-Gacute;01F4
-Gamma;0393
-Gammaafrican;0194
-Gangiacoptic;03EA
-Gbreve;011E
-Gcaron;01E6
-Gcedilla;0122
-Gcircle;24BC
-Gcircumflex;011C
-Gcommaaccent;0122
-Gdot;0120
-Gdotaccent;0120
-Gecyrillic;0413
-Ghadarmenian;0542
-Ghemiddlehookcyrillic;0494
-Ghestrokecyrillic;0492
-Gheupturncyrillic;0490
-Ghook;0193
-Gimarmenian;0533
-Gjecyrillic;0403
-Gmacron;1E20
-Gmonospace;FF27
-Grave;F6CE
-Gravesmall;F760
-Gsmall;F767
-Gsmallhook;029B
-Gstroke;01E4
-H;0048
-H18533;25CF
-H18543;25AA
-H18551;25AB
-H22073;25A1
-HPsquare;33CB
-Haabkhasiancyrillic;04A8
-Hadescendercyrillic;04B2
-Hardsigncyrillic;042A
-Hbar;0126
-Hbrevebelow;1E2A
-Hcedilla;1E28
-Hcircle;24BD
-Hcircumflex;0124
-Hdieresis;1E26
-Hdotaccent;1E22
-Hdotbelow;1E24
-Hmonospace;FF28
-Hoarmenian;0540
-Horicoptic;03E8
-Hsmall;F768
-Hungarumlaut;F6CF
-Hungarumlautsmall;F6F8
-Hzsquare;3390
-I;0049
-IAcyrillic;042F
-IJ;0132
-IUcyrillic;042E
-Iacute;00CD
-Iacutesmall;F7ED
-Ibreve;012C
-Icaron;01CF
-Icircle;24BE
-Icircumflex;00CE
-Icircumflexsmall;F7EE
-Icyrillic;0406
-Idblgrave;0208
-Idieresis;00CF
-Idieresisacute;1E2E
-Idieresiscyrillic;04E4
-Idieresissmall;F7EF
-Idot;0130
-Idotaccent;0130
-Idotbelow;1ECA
-Iebrevecyrillic;04D6
-Iecyrillic;0415
-Ifraktur;2111
-Igrave;00CC
-Igravesmall;F7EC
-Ihookabove;1EC8
-Iicyrillic;0418
-Iinvertedbreve;020A
-Iishortcyrillic;0419
-Imacron;012A
-Imacroncyrillic;04E2
-Imonospace;FF29
-Iniarmenian;053B
-Iocyrillic;0401
-Iogonek;012E
-Iota;0399
-Iotaafrican;0196
-Iotadieresis;03AA
-Iotatonos;038A
-Ismall;F769
-Istroke;0197
-Itilde;0128
-Itildebelow;1E2C
-Izhitsacyrillic;0474
-Izhitsadblgravecyrillic;0476
-J;004A
-Jaarmenian;0541
-Jcircle;24BF
-Jcircumflex;0134
-Jecyrillic;0408
-Jheharmenian;054B
-Jmonospace;FF2A
-Jsmall;F76A
-K;004B
-KBsquare;3385
-KKsquare;33CD
-Kabashkircyrillic;04A0
-Kacute;1E30
-Kacyrillic;041A
-Kadescendercyrillic;049A
-Kahookcyrillic;04C3
-Kappa;039A
-Kastrokecyrillic;049E
-Kaverticalstrokecyrillic;049C
-Kcaron;01E8
-Kcedilla;0136
-Kcircle;24C0
-Kcommaaccent;0136
-Kdotbelow;1E32
-Keharmenian;0554
-Kenarmenian;053F
-Khacyrillic;0425
-Kheicoptic;03E6
-Khook;0198
-Kjecyrillic;040C
-Klinebelow;1E34
-Kmonospace;FF2B
-Koppacyrillic;0480
-Koppagreek;03DE
-Ksicyrillic;046E
-Ksmall;F76B
-L;004C
-LJ;01C7
-LL;F6BF
-Lacute;0139
-Lambda;039B
-Lcaron;013D
-Lcedilla;013B
-Lcircle;24C1
-Lcircumflexbelow;1E3C
-Lcommaaccent;013B
-Ldot;013F
-Ldotaccent;013F
-Ldotbelow;1E36
-Ldotbelowmacron;1E38
-Liwnarmenian;053C
-Lj;01C8
-Ljecyrillic;0409
-Llinebelow;1E3A
-Lmonospace;FF2C
-Lslash;0141
-Lslashsmall;F6F9
-Lsmall;F76C
-M;004D
-MBsquare;3386
-Macron;F6D0
-Macronsmall;F7AF
-Macute;1E3E
-Mcircle;24C2
-Mdotaccent;1E40
-Mdotbelow;1E42
-Menarmenian;0544
-Mmonospace;FF2D
-Msmall;F76D
-Mturned;019C
-Mu;039C
-N;004E
-NJ;01CA
-Nacute;0143
-Ncaron;0147
-Ncedilla;0145
-Ncircle;24C3
-Ncircumflexbelow;1E4A
-Ncommaaccent;0145
-Ndotaccent;1E44
-Ndotbelow;1E46
-Nhookleft;019D
-Nineroman;2168
-Nj;01CB
-Njecyrillic;040A
-Nlinebelow;1E48
-Nmonospace;FF2E
-Nowarmenian;0546
-Nsmall;F76E
-Ntilde;00D1
-Ntildesmall;F7F1
-Nu;039D
-O;004F
-OE;0152
-OEsmall;F6FA
-Oacute;00D3
-Oacutesmall;F7F3
-Obarredcyrillic;04E8
-Obarreddieresiscyrillic;04EA
-Obreve;014E
-Ocaron;01D1
-Ocenteredtilde;019F
-Ocircle;24C4
-Ocircumflex;00D4
-Ocircumflexacute;1ED0
-Ocircumflexdotbelow;1ED8
-Ocircumflexgrave;1ED2
-Ocircumflexhookabove;1ED4
-Ocircumflexsmall;F7F4
-Ocircumflextilde;1ED6
-Ocyrillic;041E
-Odblacute;0150
-Odblgrave;020C
-Odieresis;00D6
-Odieresiscyrillic;04E6
-Odieresissmall;F7F6
-Odotbelow;1ECC
-Ogoneksmall;F6FB
-Ograve;00D2
-Ogravesmall;F7F2
-Oharmenian;0555
-Ohm;2126
-Ohookabove;1ECE
-Ohorn;01A0
-Ohornacute;1EDA
-Ohorndotbelow;1EE2
-Ohorngrave;1EDC
-Ohornhookabove;1EDE
-Ohorntilde;1EE0
-Ohungarumlaut;0150
-Oi;01A2
-Oinvertedbreve;020E
-Omacron;014C
-Omacronacute;1E52
-Omacrongrave;1E50
-Omega;2126
-Omegacyrillic;0460
-Omegagreek;03A9
-Omegaroundcyrillic;047A
-Omegatitlocyrillic;047C
-Omegatonos;038F
-Omicron;039F
-Omicrontonos;038C
-Omonospace;FF2F
-Oneroman;2160
-Oogonek;01EA
-Oogonekmacron;01EC
-Oopen;0186
-Oslash;00D8
-Oslashacute;01FE
-Oslashsmall;F7F8
-Osmall;F76F
-Ostrokeacute;01FE
-Otcyrillic;047E
-Otilde;00D5
-Otildeacute;1E4C
-Otildedieresis;1E4E
-Otildesmall;F7F5
-P;0050
-Pacute;1E54
-Pcircle;24C5
-Pdotaccent;1E56
-Pecyrillic;041F
-Peharmenian;054A
-Pemiddlehookcyrillic;04A6
-Phi;03A6
-Phook;01A4
-Pi;03A0
-Piwrarmenian;0553
-Pmonospace;FF30
-Psi;03A8
-Psicyrillic;0470
-Psmall;F770
-Q;0051
-Qcircle;24C6
-Qmonospace;FF31
-Qsmall;F771
-R;0052
-Raarmenian;054C
-Racute;0154
-Rcaron;0158
-Rcedilla;0156
-Rcircle;24C7
-Rcommaaccent;0156
-Rdblgrave;0210
-Rdotaccent;1E58
-Rdotbelow;1E5A
-Rdotbelowmacron;1E5C
-Reharmenian;0550
-Rfraktur;211C
-Rho;03A1
-Ringsmall;F6FC
-Rinvertedbreve;0212
-Rlinebelow;1E5E
-Rmonospace;FF32
-Rsmall;F772
-Rsmallinverted;0281
-Rsmallinvertedsuperior;02B6
-S;0053
-SF010000;250C
-SF020000;2514
-SF030000;2510
-SF040000;2518
-SF050000;253C
-SF060000;252C
-SF070000;2534
-SF080000;251C
-SF090000;2524
-SF100000;2500
-SF110000;2502
-SF190000;2561
-SF200000;2562
-SF210000;2556
-SF220000;2555
-SF230000;2563
-SF240000;2551
-SF250000;2557
-SF260000;255D
-SF270000;255C
-SF280000;255B
-SF360000;255E
-SF370000;255F
-SF380000;255A
-SF390000;2554
-SF400000;2569
-SF410000;2566
-SF420000;2560
-SF430000;2550
-SF440000;256C
-SF450000;2567
-SF460000;2568
-SF470000;2564
-SF480000;2565
-SF490000;2559
-SF500000;2558
-SF510000;2552
-SF520000;2553
-SF530000;256B
-SF540000;256A
-Sacute;015A
-Sacutedotaccent;1E64
-Sampigreek;03E0
-Scaron;0160
-Scarondotaccent;1E66
-Scaronsmall;F6FD
-Scedilla;015E
-Schwa;018F
-Schwacyrillic;04D8
-Schwadieresiscyrillic;04DA
-Scircle;24C8
-Scircumflex;015C
-Scommaaccent;0218
-Sdotaccent;1E60
-Sdotbelow;1E62
-Sdotbelowdotaccent;1E68
-Seharmenian;054D
-Sevenroman;2166
-Shaarmenian;0547
-Shacyrillic;0428
-Shchacyrillic;0429
-Sheicoptic;03E2
-Shhacyrillic;04BA
-Shimacoptic;03EC
-Sigma;03A3
-Sixroman;2165
-Smonospace;FF33
-Softsigncyrillic;042C
-Ssmall;F773
-Stigmagreek;03DA
-T;0054
-Tau;03A4
-Tbar;0166
-Tcaron;0164
-Tcedilla;0162
-Tcircle;24C9
-Tcircumflexbelow;1E70
-Tcommaaccent;0162
-Tdotaccent;1E6A
-Tdotbelow;1E6C
-Tecyrillic;0422
-Tedescendercyrillic;04AC
-Tenroman;2169
-Tetsecyrillic;04B4
-Theta;0398
-Thook;01AC
-Thorn;00DE
-Thornsmall;F7FE
-Threeroman;2162
-Tildesmall;F6FE
-Tiwnarmenian;054F
-Tlinebelow;1E6E
-Tmonospace;FF34
-Toarmenian;0539
-Tonefive;01BC
-Tonesix;0184
-Tonetwo;01A7
-Tretroflexhook;01AE
-Tsecyrillic;0426
-Tshecyrillic;040B
-Tsmall;F774
-Twelveroman;216B
-Tworoman;2161
-U;0055
-Uacute;00DA
-Uacutesmall;F7FA
-Ubreve;016C
-Ucaron;01D3
-Ucircle;24CA
-Ucircumflex;00DB
-Ucircumflexbelow;1E76
-Ucircumflexsmall;F7FB
-Ucyrillic;0423
-Udblacute;0170
-Udblgrave;0214
-Udieresis;00DC
-Udieresisacute;01D7
-Udieresisbelow;1E72
-Udieresiscaron;01D9
-Udieresiscyrillic;04F0
-Udieresisgrave;01DB
-Udieresismacron;01D5
-Udieresissmall;F7FC
-Udotbelow;1EE4
-Ugrave;00D9
-Ugravesmall;F7F9
-Uhookabove;1EE6
-Uhorn;01AF
-Uhornacute;1EE8
-Uhorndotbelow;1EF0
-Uhorngrave;1EEA
-Uhornhookabove;1EEC
-Uhorntilde;1EEE
-Uhungarumlaut;0170
-Uhungarumlautcyrillic;04F2
-Uinvertedbreve;0216
-Ukcyrillic;0478
-Umacron;016A
-Umacroncyrillic;04EE
-Umacrondieresis;1E7A
-Umonospace;FF35
-Uogonek;0172
-Upsilon;03A5
-Upsilon1;03D2
-Upsilonacutehooksymbolgreek;03D3
-Upsilonafrican;01B1
-Upsilondieresis;03AB
-Upsilondieresishooksymbolgreek;03D4
-Upsilonhooksymbol;03D2
-Upsilontonos;038E
-Uring;016E
-Ushortcyrillic;040E
-Usmall;F775
-Ustraightcyrillic;04AE
-Ustraightstrokecyrillic;04B0
-Utilde;0168
-Utildeacute;1E78
-Utildebelow;1E74
-V;0056
-Vcircle;24CB
-Vdotbelow;1E7E
-Vecyrillic;0412
-Vewarmenian;054E
-Vhook;01B2
-Vmonospace;FF36
-Voarmenian;0548
-Vsmall;F776
-Vtilde;1E7C
-W;0057
-Wacute;1E82
-Wcircle;24CC
-Wcircumflex;0174
-Wdieresis;1E84
-Wdotaccent;1E86
-Wdotbelow;1E88
-Wgrave;1E80
-Wmonospace;FF37
-Wsmall;F777
-X;0058
-Xcircle;24CD
-Xdieresis;1E8C
-Xdotaccent;1E8A
-Xeharmenian;053D
-Xi;039E
-Xmonospace;FF38
-Xsmall;F778
-Y;0059
-Yacute;00DD
-Yacutesmall;F7FD
-Yatcyrillic;0462
-Ycircle;24CE
-Ycircumflex;0176
-Ydieresis;0178
-Ydieresissmall;F7FF
-Ydotaccent;1E8E
-Ydotbelow;1EF4
-Yericyrillic;042B
-Yerudieresiscyrillic;04F8
-Ygrave;1EF2
-Yhook;01B3
-Yhookabove;1EF6
-Yiarmenian;0545
-Yicyrillic;0407
-Yiwnarmenian;0552
-Ymonospace;FF39
-Ysmall;F779
-Ytilde;1EF8
-Yusbigcyrillic;046A
-Yusbigiotifiedcyrillic;046C
-Yuslittlecyrillic;0466
-Yuslittleiotifiedcyrillic;0468
-Z;005A
-Zaarmenian;0536
-Zacute;0179
-Zcaron;017D
-Zcaronsmall;F6FF
-Zcircle;24CF
-Zcircumflex;1E90
-Zdot;017B
-Zdotaccent;017B
-Zdotbelow;1E92
-Zecyrillic;0417
-Zedescendercyrillic;0498
-Zedieresiscyrillic;04DE
-Zeta;0396
-Zhearmenian;053A
-Zhebrevecyrillic;04C1
-Zhecyrillic;0416
-Zhedescendercyrillic;0496
-Zhedieresiscyrillic;04DC
-Zlinebelow;1E94
-Zmonospace;FF3A
-Zsmall;F77A
-Zstroke;01B5
-a;0061
-aabengali;0986
-aacute;00E1
-aadeva;0906
-aagujarati;0A86
-aagurmukhi;0A06
-aamatragurmukhi;0A3E
-aarusquare;3303
-aavowelsignbengali;09BE
-aavowelsigndeva;093E
-aavowelsigngujarati;0ABE
-abbreviationmarkarmenian;055F
-abbreviationsigndeva;0970
-abengali;0985
-abopomofo;311A
-abreve;0103
-abreveacute;1EAF
-abrevecyrillic;04D1
-abrevedotbelow;1EB7
-abrevegrave;1EB1
-abrevehookabove;1EB3
-abrevetilde;1EB5
-acaron;01CE
-acircle;24D0
-acircumflex;00E2
-acircumflexacute;1EA5
-acircumflexdotbelow;1EAD
-acircumflexgrave;1EA7
-acircumflexhookabove;1EA9
-acircumflextilde;1EAB
-acute;00B4
-acutebelowcmb;0317
-acutecmb;0301
-acutecomb;0301
-acutedeva;0954
-acutelowmod;02CF
-acutetonecmb;0341
-acyrillic;0430
-adblgrave;0201
-addakgurmukhi;0A71
-adeva;0905
-adieresis;00E4
-adieresiscyrillic;04D3
-adieresismacron;01DF
-adotbelow;1EA1
-adotmacron;01E1
-ae;00E6
-aeacute;01FD
-aekorean;3150
-aemacron;01E3
-afii00208;2015
-afii08941;20A4
-afii10017;0410
-afii10018;0411
-afii10019;0412
-afii10020;0413
-afii10021;0414
-afii10022;0415
-afii10023;0401
-afii10024;0416
-afii10025;0417
-afii10026;0418
-afii10027;0419
-afii10028;041A
-afii10029;041B
-afii10030;041C
-afii10031;041D
-afii10032;041E
-afii10033;041F
-afii10034;0420
-afii10035;0421
-afii10036;0422
-afii10037;0423
-afii10038;0424
-afii10039;0425
-afii10040;0426
-afii10041;0427
-afii10042;0428
-afii10043;0429
-afii10044;042A
-afii10045;042B
-afii10046;042C
-afii10047;042D
-afii10048;042E
-afii10049;042F
-afii10050;0490
-afii10051;0402
-afii10052;0403
-afii10053;0404
-afii10054;0405
-afii10055;0406
-afii10056;0407
-afii10057;0408
-afii10058;0409
-afii10059;040A
-afii10060;040B
-afii10061;040C
-afii10062;040E
-afii10063;F6C4
-afii10064;F6C5
-afii10065;0430
-afii10066;0431
-afii10067;0432
-afii10068;0433
-afii10069;0434
-afii10070;0435
-afii10071;0451
-afii10072;0436
-afii10073;0437
-afii10074;0438
-afii10075;0439
-afii10076;043A
-afii10077;043B
-afii10078;043C
-afii10079;043D
-afii10080;043E
-afii10081;043F
-afii10082;0440
-afii10083;0441
-afii10084;0442
-afii10085;0443
-afii10086;0444
-afii10087;0445
-afii10088;0446
-afii10089;0447
-afii10090;0448
-afii10091;0449
-afii10092;044A
-afii10093;044B
-afii10094;044C
-afii10095;044D
-afii10096;044E
-afii10097;044F
-afii10098;0491
-afii10099;0452
-afii10100;0453
-afii10101;0454
-afii10102;0455
-afii10103;0456
-afii10104;0457
-afii10105;0458
-afii10106;0459
-afii10107;045A
-afii10108;045B
-afii10109;045C
-afii10110;045E
-afii10145;040F
-afii10146;0462
-afii10147;0472
-afii10148;0474
-afii10192;F6C6
-afii10193;045F
-afii10194;0463
-afii10195;0473
-afii10196;0475
-afii10831;F6C7
-afii10832;F6C8
-afii10846;04D9
-afii299;200E
-afii300;200F
-afii301;200D
-afii57381;066A
-afii57388;060C
-afii57392;0660
-afii57393;0661
-afii57394;0662
-afii57395;0663
-afii57396;0664
-afii57397;0665
-afii57398;0666
-afii57399;0667
-afii57400;0668
-afii57401;0669
-afii57403;061B
-afii57407;061F
-afii57409;0621
-afii57410;0622
-afii57411;0623
-afii57412;0624
-afii57413;0625
-afii57414;0626
-afii57415;0627
-afii57416;0628
-afii57417;0629
-afii57418;062A
-afii57419;062B
-afii57420;062C
-afii57421;062D
-afii57422;062E
-afii57423;062F
-afii57424;0630
-afii57425;0631
-afii57426;0632
-afii57427;0633
-afii57428;0634
-afii57429;0635
-afii57430;0636
-afii57431;0637
-afii57432;0638
-afii57433;0639
-afii57434;063A
-afii57440;0640
-afii57441;0641
-afii57442;0642
-afii57443;0643
-afii57444;0644
-afii57445;0645
-afii57446;0646
-afii57448;0648
-afii57449;0649
-afii57450;064A
-afii57451;064B
-afii57452;064C
-afii57453;064D
-afii57454;064E
-afii57455;064F
-afii57456;0650
-afii57457;0651
-afii57458;0652
-afii57470;0647
-afii57505;06A4
-afii57506;067E
-afii57507;0686
-afii57508;0698
-afii57509;06AF
-afii57511;0679
-afii57512;0688
-afii57513;0691
-afii57514;06BA
-afii57519;06D2
-afii57534;06D5
-afii57636;20AA
-afii57645;05BE
-afii57658;05C3
-afii57664;05D0
-afii57665;05D1
-afii57666;05D2
-afii57667;05D3
-afii57668;05D4
-afii57669;05D5
-afii57670;05D6
-afii57671;05D7
-afii57672;05D8
-afii57673;05D9
-afii57674;05DA
-afii57675;05DB
-afii57676;05DC
-afii57677;05DD
-afii57678;05DE
-afii57679;05DF
-afii57680;05E0
-afii57681;05E1
-afii57682;05E2
-afii57683;05E3
-afii57684;05E4
-afii57685;05E5
-afii57686;05E6
-afii57687;05E7
-afii57688;05E8
-afii57689;05E9
-afii57690;05EA
-afii57694;FB2A
-afii57695;FB2B
-afii57700;FB4B
-afii57705;FB1F
-afii57716;05F0
-afii57717;05F1
-afii57718;05F2
-afii57723;FB35
-afii57793;05B4
-afii57794;05B5
-afii57795;05B6
-afii57796;05BB
-afii57797;05B8
-afii57798;05B7
-afii57799;05B0
-afii57800;05B2
-afii57801;05B1
-afii57802;05B3
-afii57803;05C2
-afii57804;05C1
-afii57806;05B9
-afii57807;05BC
-afii57839;05BD
-afii57841;05BF
-afii57842;05C0
-afii57929;02BC
-afii61248;2105
-afii61289;2113
-afii61352;2116
-afii61573;202C
-afii61574;202D
-afii61575;202E
-afii61664;200C
-afii63167;066D
-afii64937;02BD
-agrave;00E0
-agujarati;0A85
-agurmukhi;0A05
-ahiragana;3042
-ahookabove;1EA3
-aibengali;0990
-aibopomofo;311E
-aideva;0910
-aiecyrillic;04D5
-aigujarati;0A90
-aigurmukhi;0A10
-aimatragurmukhi;0A48
-ainarabic;0639
-ainfinalarabic;FECA
-aininitialarabic;FECB
-ainmedialarabic;FECC
-ainvertedbreve;0203
-aivowelsignbengali;09C8
-aivowelsigndeva;0948
-aivowelsigngujarati;0AC8
-akatakana;30A2
-akatakanahalfwidth;FF71
-akorean;314F
-alef;05D0
-alefarabic;0627
-alefdageshhebrew;FB30
-aleffinalarabic;FE8E
-alefhamzaabovearabic;0623
-alefhamzaabovefinalarabic;FE84
-alefhamzabelowarabic;0625
-alefhamzabelowfinalarabic;FE88
-alefhebrew;05D0
-aleflamedhebrew;FB4F
-alefmaddaabovearabic;0622
-alefmaddaabovefinalarabic;FE82
-alefmaksuraarabic;0649
-alefmaksurafinalarabic;FEF0
-alefmaksurainitialarabic;FEF3
-alefmaksuramedialarabic;FEF4
-alefpatahhebrew;FB2E
-alefqamatshebrew;FB2F
-aleph;2135
-allequal;224C
-alpha;03B1
-alphatonos;03AC
-amacron;0101
-amonospace;FF41
-ampersand;0026
-ampersandmonospace;FF06
-ampersandsmall;F726
-amsquare;33C2
-anbopomofo;3122
-angbopomofo;3124
-angkhankhuthai;0E5A
-angle;2220
-anglebracketleft;3008
-anglebracketleftvertical;FE3F
-anglebracketright;3009
-anglebracketrightvertical;FE40
-angleleft;2329
-angleright;232A
-angstrom;212B
-anoteleia;0387
-anudattadeva;0952
-anusvarabengali;0982
-anusvaradeva;0902
-anusvaragujarati;0A82
-aogonek;0105
-apaatosquare;3300
-aparen;249C
-apostrophearmenian;055A
-apostrophemod;02BC
-apple;F8FF
-approaches;2250
-approxequal;2248
-approxequalorimage;2252
-approximatelyequal;2245
-araeaekorean;318E
-araeakorean;318D
-arc;2312
-arighthalfring;1E9A
-aring;00E5
-aringacute;01FB
-aringbelow;1E01
-arrowboth;2194
-arrowdashdown;21E3
-arrowdashleft;21E0
-arrowdashright;21E2
-arrowdashup;21E1
-arrowdblboth;21D4
-arrowdbldown;21D3
-arrowdblleft;21D0
-arrowdblright;21D2
-arrowdblup;21D1
-arrowdown;2193
-arrowdownleft;2199
-arrowdownright;2198
-arrowdownwhite;21E9
-arrowheaddownmod;02C5
-arrowheadleftmod;02C2
-arrowheadrightmod;02C3
-arrowheadupmod;02C4
-arrowhorizex;F8E7
-arrowleft;2190
-arrowleftdbl;21D0
-arrowleftdblstroke;21CD
-arrowleftoverright;21C6
-arrowleftwhite;21E6
-arrowright;2192
-arrowrightdblstroke;21CF
-arrowrightheavy;279E
-arrowrightoverleft;21C4
-arrowrightwhite;21E8
-arrowtableft;21E4
-arrowtabright;21E5
-arrowup;2191
-arrowupdn;2195
-arrowupdnbse;21A8
-arrowupdownbase;21A8
-arrowupleft;2196
-arrowupleftofdown;21C5
-arrowupright;2197
-arrowupwhite;21E7
-arrowvertex;F8E6
-asciicircum;005E
-asciicircummonospace;FF3E
-asciitilde;007E
-asciitildemonospace;FF5E
-ascript;0251
-ascriptturned;0252
-asmallhiragana;3041
-asmallkatakana;30A1
-asmallkatakanahalfwidth;FF67
-asterisk;002A
-asteriskaltonearabic;066D
-asteriskarabic;066D
-asteriskmath;2217
-asteriskmonospace;FF0A
-asterisksmall;FE61
-asterism;2042
-asuperior;F6E9
-asymptoticallyequal;2243
-at;0040
-atilde;00E3
-atmonospace;FF20
-atsmall;FE6B
-aturned;0250
-aubengali;0994
-aubopomofo;3120
-audeva;0914
-augujarati;0A94
-augurmukhi;0A14
-aulengthmarkbengali;09D7
-aumatragurmukhi;0A4C
-auvowelsignbengali;09CC
-auvowelsigndeva;094C
-auvowelsigngujarati;0ACC
-avagrahadeva;093D
-aybarmenian;0561
-ayin;05E2
-ayinaltonehebrew;FB20
-ayinhebrew;05E2
-b;0062
-babengali;09AC
-backslash;005C
-backslashmonospace;FF3C
-badeva;092C
-bagujarati;0AAC
-bagurmukhi;0A2C
-bahiragana;3070
-bahtthai;0E3F
-bakatakana;30D0
-bar;007C
-barmonospace;FF5C
-bbopomofo;3105
-bcircle;24D1
-bdotaccent;1E03
-bdotbelow;1E05
-beamedsixteenthnotes;266C
-because;2235
-becyrillic;0431
-beharabic;0628
-behfinalarabic;FE90
-behinitialarabic;FE91
-behiragana;3079
-behmedialarabic;FE92
-behmeeminitialarabic;FC9F
-behmeemisolatedarabic;FC08
-behnoonfinalarabic;FC6D
-bekatakana;30D9
-benarmenian;0562
-bet;05D1
-beta;03B2
-betasymbolgreek;03D0
-betdagesh;FB31
-betdageshhebrew;FB31
-bethebrew;05D1
-betrafehebrew;FB4C
-bhabengali;09AD
-bhadeva;092D
-bhagujarati;0AAD
-bhagurmukhi;0A2D
-bhook;0253
-bihiragana;3073
-bikatakana;30D3
-bilabialclick;0298
-bindigurmukhi;0A02
-birusquare;3331
-blackcircle;25CF
-blackdiamond;25C6
-blackdownpointingtriangle;25BC
-blackleftpointingpointer;25C4
-blackleftpointingtriangle;25C0
-blacklenticularbracketleft;3010
-blacklenticularbracketleftvertical;FE3B
-blacklenticularbracketright;3011
-blacklenticularbracketrightvertical;FE3C
-blacklowerlefttriangle;25E3
-blacklowerrighttriangle;25E2
-blackrectangle;25AC
-blackrightpointingpointer;25BA
-blackrightpointingtriangle;25B6
-blacksmallsquare;25AA
-blacksmilingface;263B
-blacksquare;25A0
-blackstar;2605
-blackupperlefttriangle;25E4
-blackupperrighttriangle;25E5
-blackuppointingsmalltriangle;25B4
-blackuppointingtriangle;25B2
-blank;2423
-blinebelow;1E07
-block;2588
-bmonospace;FF42
-bobaimaithai;0E1A
-bohiragana;307C
-bokatakana;30DC
-bparen;249D
-bqsquare;33C3
-braceex;F8F4
-braceleft;007B
-braceleftbt;F8F3
-braceleftmid;F8F2
-braceleftmonospace;FF5B
-braceleftsmall;FE5B
-bracelefttp;F8F1
-braceleftvertical;FE37
-braceright;007D
-bracerightbt;F8FE
-bracerightmid;F8FD
-bracerightmonospace;FF5D
-bracerightsmall;FE5C
-bracerighttp;F8FC
-bracerightvertical;FE38
-bracketleft;005B
-bracketleftbt;F8F0
-bracketleftex;F8EF
-bracketleftmonospace;FF3B
-bracketlefttp;F8EE
-bracketright;005D
-bracketrightbt;F8FB
-bracketrightex;F8FA
-bracketrightmonospace;FF3D
-bracketrighttp;F8F9
-breve;02D8
-brevebelowcmb;032E
-brevecmb;0306
-breveinvertedbelowcmb;032F
-breveinvertedcmb;0311
-breveinverteddoublecmb;0361
-bridgebelowcmb;032A
-bridgeinvertedbelowcmb;033A
-brokenbar;00A6
-bstroke;0180
-bsuperior;F6EA
-btopbar;0183
-buhiragana;3076
-bukatakana;30D6
-bullet;2022
-bulletinverse;25D8
-bulletoperator;2219
-bullseye;25CE
-c;0063
-caarmenian;056E
-cabengali;099A
-cacute;0107
-cadeva;091A
-cagujarati;0A9A
-cagurmukhi;0A1A
-calsquare;3388
-candrabindubengali;0981
-candrabinducmb;0310
-candrabindudeva;0901
-candrabindugujarati;0A81
-capslock;21EA
-careof;2105
-caron;02C7
-caronbelowcmb;032C
-caroncmb;030C
-carriagereturn;21B5
-cbopomofo;3118
-ccaron;010D
-ccedilla;00E7
-ccedillaacute;1E09
-ccircle;24D2
-ccircumflex;0109
-ccurl;0255
-cdot;010B
-cdotaccent;010B
-cdsquare;33C5
-cedilla;00B8
-cedillacmb;0327
-cent;00A2
-centigrade;2103
-centinferior;F6DF
-centmonospace;FFE0
-centoldstyle;F7A2
-centsuperior;F6E0
-chaarmenian;0579
-chabengali;099B
-chadeva;091B
-chagujarati;0A9B
-chagurmukhi;0A1B
-chbopomofo;3114
-cheabkhasiancyrillic;04BD
-checkmark;2713
-checyrillic;0447
-chedescenderabkhasiancyrillic;04BF
-chedescendercyrillic;04B7
-chedieresiscyrillic;04F5
-cheharmenian;0573
-chekhakassiancyrillic;04CC
-cheverticalstrokecyrillic;04B9
-chi;03C7
-chieuchacirclekorean;3277
-chieuchaparenkorean;3217
-chieuchcirclekorean;3269
-chieuchkorean;314A
-chieuchparenkorean;3209
-chochangthai;0E0A
-chochanthai;0E08
-chochingthai;0E09
-chochoethai;0E0C
-chook;0188
-cieucacirclekorean;3276
-cieucaparenkorean;3216
-cieuccirclekorean;3268
-cieuckorean;3148
-cieucparenkorean;3208
-cieucuparenkorean;321C
-circle;25CB
-circlemultiply;2297
-circleot;2299
-circleplus;2295
-circlepostalmark;3036
-circlewithlefthalfblack;25D0
-circlewithrighthalfblack;25D1
-circumflex;02C6
-circumflexbelowcmb;032D
-circumflexcmb;0302
-clear;2327
-clickalveolar;01C2
-clickdental;01C0
-clicklateral;01C1
-clickretroflex;01C3
-club;2663
-clubsuitblack;2663
-clubsuitwhite;2667
-cmcubedsquare;33A4
-cmonospace;FF43
-cmsquaredsquare;33A0
-coarmenian;0581
-colon;003A
-colonmonetary;20A1
-colonmonospace;FF1A
-colonsign;20A1
-colonsmall;FE55
-colontriangularhalfmod;02D1
-colontriangularmod;02D0
-comma;002C
-commaabovecmb;0313
-commaaboverightcmb;0315
-commaaccent;F6C3
-commaarabic;060C
-commaarmenian;055D
-commainferior;F6E1
-commamonospace;FF0C
-commareversedabovecmb;0314
-commareversedmod;02BD
-commasmall;FE50
-commasuperior;F6E2
-commaturnedabovecmb;0312
-commaturnedmod;02BB
-compass;263C
-congruent;2245
-contourintegral;222E
-control;2303
-controlACK;0006
-controlBEL;0007
-controlBS;0008
-controlCAN;0018
-controlCR;000D
-controlDC1;0011
-controlDC2;0012
-controlDC3;0013
-controlDC4;0014
-controlDEL;007F
-controlDLE;0010
-controlEM;0019
-controlENQ;0005
-controlEOT;0004
-controlESC;001B
-controlETB;0017
-controlETX;0003
-controlFF;000C
-controlFS;001C
-controlGS;001D
-controlHT;0009
-controlLF;000A
-controlNAK;0015
-controlRS;001E
-controlSI;000F
-controlSO;000E
-controlSOT;0002
-controlSTX;0001
-controlSUB;001A
-controlSYN;0016
-controlUS;001F
-controlVT;000B
-copyright;00A9
-copyrightsans;F8E9
-copyrightserif;F6D9
-cornerbracketleft;300C
-cornerbracketlefthalfwidth;FF62
-cornerbracketleftvertical;FE41
-cornerbracketright;300D
-cornerbracketrighthalfwidth;FF63
-cornerbracketrightvertical;FE42
-corporationsquare;337F
-cosquare;33C7
-coverkgsquare;33C6
-cparen;249E
-cruzeiro;20A2
-cstretched;0297
-curlyand;22CF
-curlyor;22CE
-currency;00A4
-cyrBreve;F6D1
-cyrFlex;F6D2
-cyrbreve;F6D4
-cyrflex;F6D5
-d;0064
-daarmenian;0564
-dabengali;09A6
-dadarabic;0636
-dadeva;0926
-dadfinalarabic;FEBE
-dadinitialarabic;FEBF
-dadmedialarabic;FEC0
-dagesh;05BC
-dageshhebrew;05BC
-dagger;2020
-daggerdbl;2021
-dagujarati;0AA6
-dagurmukhi;0A26
-dahiragana;3060
-dakatakana;30C0
-dalarabic;062F
-dalet;05D3
-daletdagesh;FB33
-daletdageshhebrew;FB33
-dalethatafpatah;05D3 05B2
-dalethatafpatahhebrew;05D3 05B2
-dalethatafsegol;05D3 05B1
-dalethatafsegolhebrew;05D3 05B1
-dalethebrew;05D3
-dalethiriq;05D3 05B4
-dalethiriqhebrew;05D3 05B4
-daletholam;05D3 05B9
-daletholamhebrew;05D3 05B9
-daletpatah;05D3 05B7
-daletpatahhebrew;05D3 05B7
-daletqamats;05D3 05B8
-daletqamatshebrew;05D3 05B8
-daletqubuts;05D3 05BB
-daletqubutshebrew;05D3 05BB
-daletsegol;05D3 05B6
-daletsegolhebrew;05D3 05B6
-daletsheva;05D3 05B0
-daletshevahebrew;05D3 05B0
-dalettsere;05D3 05B5
-dalettserehebrew;05D3 05B5
-dalfinalarabic;FEAA
-dammaarabic;064F
-dammalowarabic;064F
-dammatanaltonearabic;064C
-dammatanarabic;064C
-danda;0964
-dargahebrew;05A7
-dargalefthebrew;05A7
-dasiapneumatacyrilliccmb;0485
-dblGrave;F6D3
-dblanglebracketleft;300A
-dblanglebracketleftvertical;FE3D
-dblanglebracketright;300B
-dblanglebracketrightvertical;FE3E
-dblarchinvertedbelowcmb;032B
-dblarrowleft;21D4
-dblarrowright;21D2
-dbldanda;0965
-dblgrave;F6D6
-dblgravecmb;030F
-dblintegral;222C
-dbllowline;2017
-dbllowlinecmb;0333
-dbloverlinecmb;033F
-dblprimemod;02BA
-dblverticalbar;2016
-dblverticallineabovecmb;030E
-dbopomofo;3109
-dbsquare;33C8
-dcaron;010F
-dcedilla;1E11
-dcircle;24D3
-dcircumflexbelow;1E13
-dcroat;0111
-ddabengali;09A1
-ddadeva;0921
-ddagujarati;0AA1
-ddagurmukhi;0A21
-ddalarabic;0688
-ddalfinalarabic;FB89
-dddhadeva;095C
-ddhabengali;09A2
-ddhadeva;0922
-ddhagujarati;0AA2
-ddhagurmukhi;0A22
-ddotaccent;1E0B
-ddotbelow;1E0D
-decimalseparatorarabic;066B
-decimalseparatorpersian;066B
-decyrillic;0434
-degree;00B0
-dehihebrew;05AD
-dehiragana;3067
-deicoptic;03EF
-dekatakana;30C7
-deleteleft;232B
-deleteright;2326
-delta;03B4
-deltaturned;018D
-denominatorminusonenumeratorbengali;09F8
-dezh;02A4
-dhabengali;09A7
-dhadeva;0927
-dhagujarati;0AA7
-dhagurmukhi;0A27
-dhook;0257
-dialytikatonos;0385
-dialytikatonoscmb;0344
-diamond;2666
-diamondsuitwhite;2662
-dieresis;00A8
-dieresisacute;F6D7
-dieresisbelowcmb;0324
-dieresiscmb;0308
-dieresisgrave;F6D8
-dieresistonos;0385
-dihiragana;3062
-dikatakana;30C2
-dittomark;3003
-divide;00F7
-divides;2223
-divisionslash;2215
-djecyrillic;0452
-dkshade;2593
-dlinebelow;1E0F
-dlsquare;3397
-dmacron;0111
-dmonospace;FF44
-dnblock;2584
-dochadathai;0E0E
-dodekthai;0E14
-dohiragana;3069
-dokatakana;30C9
-dollar;0024
-dollarinferior;F6E3
-dollarmonospace;FF04
-dollaroldstyle;F724
-dollarsmall;FE69
-dollarsuperior;F6E4
-dong;20AB
-dorusquare;3326
-dotaccent;02D9
-dotaccentcmb;0307
-dotbelowcmb;0323
-dotbelowcomb;0323
-dotkatakana;30FB
-dotlessi;0131
-dotlessj;F6BE
-dotlessjstrokehook;0284
-dotmath;22C5
-dottedcircle;25CC
-doubleyodpatah;FB1F
-doubleyodpatahhebrew;FB1F
-downtackbelowcmb;031E
-downtackmod;02D5
-dparen;249F
-dsuperior;F6EB
-dtail;0256
-dtopbar;018C
-duhiragana;3065
-dukatakana;30C5
-dz;01F3
-dzaltone;02A3
-dzcaron;01C6
-dzcurl;02A5
-dzeabkhasiancyrillic;04E1
-dzecyrillic;0455
-dzhecyrillic;045F
-e;0065
-eacute;00E9
-earth;2641
-ebengali;098F
-ebopomofo;311C
-ebreve;0115
-ecandradeva;090D
-ecandragujarati;0A8D
-ecandravowelsigndeva;0945
-ecandravowelsigngujarati;0AC5
-ecaron;011B
-ecedillabreve;1E1D
-echarmenian;0565
-echyiwnarmenian;0587
-ecircle;24D4
-ecircumflex;00EA
-ecircumflexacute;1EBF
-ecircumflexbelow;1E19
-ecircumflexdotbelow;1EC7
-ecircumflexgrave;1EC1
-ecircumflexhookabove;1EC3
-ecircumflextilde;1EC5
-ecyrillic;0454
-edblgrave;0205
-edeva;090F
-edieresis;00EB
-edot;0117
-edotaccent;0117
-edotbelow;1EB9
-eegurmukhi;0A0F
-eematragurmukhi;0A47
-efcyrillic;0444
-egrave;00E8
-egujarati;0A8F
-eharmenian;0567
-ehbopomofo;311D
-ehiragana;3048
-ehookabove;1EBB
-eibopomofo;311F
-eight;0038
-eightarabic;0668
-eightbengali;09EE
-eightcircle;2467
-eightcircleinversesansserif;2791
-eightdeva;096E
-eighteencircle;2471
-eighteenparen;2485
-eighteenperiod;2499
-eightgujarati;0AEE
-eightgurmukhi;0A6E
-eighthackarabic;0668
-eighthangzhou;3028
-eighthnotebeamed;266B
-eightideographicparen;3227
-eightinferior;2088
-eightmonospace;FF18
-eightoldstyle;F738
-eightparen;247B
-eightperiod;248F
-eightpersian;06F8
-eightroman;2177
-eightsuperior;2078
-eightthai;0E58
-einvertedbreve;0207
-eiotifiedcyrillic;0465
-ekatakana;30A8
-ekatakanahalfwidth;FF74
-ekonkargurmukhi;0A74
-ekorean;3154
-elcyrillic;043B
-element;2208
-elevencircle;246A
-elevenparen;247E
-elevenperiod;2492
-elevenroman;217A
-ellipsis;2026
-ellipsisvertical;22EE
-emacron;0113
-emacronacute;1E17
-emacrongrave;1E15
-emcyrillic;043C
-emdash;2014
-emdashvertical;FE31
-emonospace;FF45
-emphasismarkarmenian;055B
-emptyset;2205
-enbopomofo;3123
-encyrillic;043D
-endash;2013
-endashvertical;FE32
-endescendercyrillic;04A3
-eng;014B
-engbopomofo;3125
-enghecyrillic;04A5
-enhookcyrillic;04C8
-enspace;2002
-eogonek;0119
-eokorean;3153
-eopen;025B
-eopenclosed;029A
-eopenreversed;025C
-eopenreversedclosed;025E
-eopenreversedhook;025D
-eparen;24A0
-epsilon;03B5
-epsilontonos;03AD
-equal;003D
-equalmonospace;FF1D
-equalsmall;FE66
-equalsuperior;207C
-equivalence;2261
-erbopomofo;3126
-ercyrillic;0440
-ereversed;0258
-ereversedcyrillic;044D
-escyrillic;0441
-esdescendercyrillic;04AB
-esh;0283
-eshcurl;0286
-eshortdeva;090E
-eshortvowelsigndeva;0946
-eshreversedloop;01AA
-eshsquatreversed;0285
-esmallhiragana;3047
-esmallkatakana;30A7
-esmallkatakanahalfwidth;FF6A
-estimated;212E
-esuperior;F6EC
-eta;03B7
-etarmenian;0568
-etatonos;03AE
-eth;00F0
-etilde;1EBD
-etildebelow;1E1B
-etnahtafoukhhebrew;0591
-etnahtafoukhlefthebrew;0591
-etnahtahebrew;0591
-etnahtalefthebrew;0591
-eturned;01DD
-eukorean;3161
-euro;20AC
-evowelsignbengali;09C7
-evowelsigndeva;0947
-evowelsigngujarati;0AC7
-exclam;0021
-exclamarmenian;055C
-exclamdbl;203C
-exclamdown;00A1
-exclamdownsmall;F7A1
-exclammonospace;FF01
-exclamsmall;F721
-existential;2203
-ezh;0292
-ezhcaron;01EF
-ezhcurl;0293
-ezhreversed;01B9
-ezhtail;01BA
-f;0066
-fadeva;095E
-fagurmukhi;0A5E
-fahrenheit;2109
-fathaarabic;064E
-fathalowarabic;064E
-fathatanarabic;064B
-fbopomofo;3108
-fcircle;24D5
-fdotaccent;1E1F
-feharabic;0641
-feharmenian;0586
-fehfinalarabic;FED2
-fehinitialarabic;FED3
-fehmedialarabic;FED4
-feicoptic;03E5
-female;2640
-ff;FB00
-ffi;FB03
-ffl;FB04
-fi;FB01
-fifteencircle;246E
-fifteenparen;2482
-fifteenperiod;2496
-figuredash;2012
-filledbox;25A0
-filledrect;25AC
-finalkaf;05DA
-finalkafdagesh;FB3A
-finalkafdageshhebrew;FB3A
-finalkafhebrew;05DA
-finalkafqamats;05DA 05B8
-finalkafqamatshebrew;05DA 05B8
-finalkafsheva;05DA 05B0
-finalkafshevahebrew;05DA 05B0
-finalmem;05DD
-finalmemhebrew;05DD
-finalnun;05DF
-finalnunhebrew;05DF
-finalpe;05E3
-finalpehebrew;05E3
-finaltsadi;05E5
-finaltsadihebrew;05E5
-firsttonechinese;02C9
-fisheye;25C9
-fitacyrillic;0473
-five;0035
-fivearabic;0665
-fivebengali;09EB
-fivecircle;2464
-fivecircleinversesansserif;278E
-fivedeva;096B
-fiveeighths;215D
-fivegujarati;0AEB
-fivegurmukhi;0A6B
-fivehackarabic;0665
-fivehangzhou;3025
-fiveideographicparen;3224
-fiveinferior;2085
-fivemonospace;FF15
-fiveoldstyle;F735
-fiveparen;2478
-fiveperiod;248C
-fivepersian;06F5
-fiveroman;2174
-fivesuperior;2075
-fivethai;0E55
-fl;FB02
-florin;0192
-fmonospace;FF46
-fmsquare;3399
-fofanthai;0E1F
-fofathai;0E1D
-fongmanthai;0E4F
-forall;2200
-four;0034
-fourarabic;0664
-fourbengali;09EA
-fourcircle;2463
-fourcircleinversesansserif;278D
-fourdeva;096A
-fourgujarati;0AEA
-fourgurmukhi;0A6A
-fourhackarabic;0664
-fourhangzhou;3024
-fourideographicparen;3223
-fourinferior;2084
-fourmonospace;FF14
-fournumeratorbengali;09F7
-fouroldstyle;F734
-fourparen;2477
-fourperiod;248B
-fourpersian;06F4
-fourroman;2173
-foursuperior;2074
-fourteencircle;246D
-fourteenparen;2481
-fourteenperiod;2495
-fourthai;0E54
-fourthtonechinese;02CB
-fparen;24A1
-fraction;2044
-franc;20A3
-g;0067
-gabengali;0997
-gacute;01F5
-gadeva;0917
-gafarabic;06AF
-gaffinalarabic;FB93
-gafinitialarabic;FB94
-gafmedialarabic;FB95
-gagujarati;0A97
-gagurmukhi;0A17
-gahiragana;304C
-gakatakana;30AC
-gamma;03B3
-gammalatinsmall;0263
-gammasuperior;02E0
-gangiacoptic;03EB
-gbopomofo;310D
-gbreve;011F
-gcaron;01E7
-gcedilla;0123
-gcircle;24D6
-gcircumflex;011D
-gcommaaccent;0123
-gdot;0121
-gdotaccent;0121
-gecyrillic;0433
-gehiragana;3052
-gekatakana;30B2
-geometricallyequal;2251
-gereshaccenthebrew;059C
-gereshhebrew;05F3
-gereshmuqdamhebrew;059D
-germandbls;00DF
-gershayimaccenthebrew;059E
-gershayimhebrew;05F4
-getamark;3013
-ghabengali;0998
-ghadarmenian;0572
-ghadeva;0918
-ghagujarati;0A98
-ghagurmukhi;0A18
-ghainarabic;063A
-ghainfinalarabic;FECE
-ghaininitialarabic;FECF
-ghainmedialarabic;FED0
-ghemiddlehookcyrillic;0495
-ghestrokecyrillic;0493
-gheupturncyrillic;0491
-ghhadeva;095A
-ghhagurmukhi;0A5A
-ghook;0260
-ghzsquare;3393
-gihiragana;304E
-gikatakana;30AE
-gimarmenian;0563
-gimel;05D2
-gimeldagesh;FB32
-gimeldageshhebrew;FB32
-gimelhebrew;05D2
-gjecyrillic;0453
-glottalinvertedstroke;01BE
-glottalstop;0294
-glottalstopinverted;0296
-glottalstopmod;02C0
-glottalstopreversed;0295
-glottalstopreversedmod;02C1
-glottalstopreversedsuperior;02E4
-glottalstopstroke;02A1
-glottalstopstrokereversed;02A2
-gmacron;1E21
-gmonospace;FF47
-gohiragana;3054
-gokatakana;30B4
-gparen;24A2
-gpasquare;33AC
-gradient;2207
-grave;0060
-gravebelowcmb;0316
-gravecmb;0300
-gravecomb;0300
-gravedeva;0953
-gravelowmod;02CE
-gravemonospace;FF40
-gravetonecmb;0340
-greater;003E
-greaterequal;2265
-greaterequalorless;22DB
-greatermonospace;FF1E
-greaterorequivalent;2273
-greaterorless;2277
-greateroverequal;2267
-greatersmall;FE65
-gscript;0261
-gstroke;01E5
-guhiragana;3050
-guillemotleft;00AB
-guillemotright;00BB
-guilsinglleft;2039
-guilsinglright;203A
-gukatakana;30B0
-guramusquare;3318
-gysquare;33C9
-h;0068
-haabkhasiancyrillic;04A9
-haaltonearabic;06C1
-habengali;09B9
-hadescendercyrillic;04B3
-hadeva;0939
-hagujarati;0AB9
-hagurmukhi;0A39
-haharabic;062D
-hahfinalarabic;FEA2
-hahinitialarabic;FEA3
-hahiragana;306F
-hahmedialarabic;FEA4
-haitusquare;332A
-hakatakana;30CF
-hakatakanahalfwidth;FF8A
-halantgurmukhi;0A4D
-hamzaarabic;0621
-hamzadammaarabic;0621 064F
-hamzadammatanarabic;0621 064C
-hamzafathaarabic;0621 064E
-hamzafathatanarabic;0621 064B
-hamzalowarabic;0621
-hamzalowkasraarabic;0621 0650
-hamzalowkasratanarabic;0621 064D
-hamzasukunarabic;0621 0652
-hangulfiller;3164
-hardsigncyrillic;044A
-harpoonleftbarbup;21BC
-harpoonrightbarbup;21C0
-hasquare;33CA
-hatafpatah;05B2
-hatafpatah16;05B2
-hatafpatah23;05B2
-hatafpatah2f;05B2
-hatafpatahhebrew;05B2
-hatafpatahnarrowhebrew;05B2
-hatafpatahquarterhebrew;05B2
-hatafpatahwidehebrew;05B2
-hatafqamats;05B3
-hatafqamats1b;05B3
-hatafqamats28;05B3
-hatafqamats34;05B3
-hatafqamatshebrew;05B3
-hatafqamatsnarrowhebrew;05B3
-hatafqamatsquarterhebrew;05B3
-hatafqamatswidehebrew;05B3
-hatafsegol;05B1
-hatafsegol17;05B1
-hatafsegol24;05B1
-hatafsegol30;05B1
-hatafsegolhebrew;05B1
-hatafsegolnarrowhebrew;05B1
-hatafsegolquarterhebrew;05B1
-hatafsegolwidehebrew;05B1
-hbar;0127
-hbopomofo;310F
-hbrevebelow;1E2B
-hcedilla;1E29
-hcircle;24D7
-hcircumflex;0125
-hdieresis;1E27
-hdotaccent;1E23
-hdotbelow;1E25
-he;05D4
-heart;2665
-heartsuitblack;2665
-heartsuitwhite;2661
-hedagesh;FB34
-hedageshhebrew;FB34
-hehaltonearabic;06C1
-heharabic;0647
-hehebrew;05D4
-hehfinalaltonearabic;FBA7
-hehfinalalttwoarabic;FEEA
-hehfinalarabic;FEEA
-hehhamzaabovefinalarabic;FBA5
-hehhamzaaboveisolatedarabic;FBA4
-hehinitialaltonearabic;FBA8
-hehinitialarabic;FEEB
-hehiragana;3078
-hehmedialaltonearabic;FBA9
-hehmedialarabic;FEEC
-heiseierasquare;337B
-hekatakana;30D8
-hekatakanahalfwidth;FF8D
-hekutaarusquare;3336
-henghook;0267
-herutusquare;3339
-het;05D7
-hethebrew;05D7
-hhook;0266
-hhooksuperior;02B1
-hieuhacirclekorean;327B
-hieuhaparenkorean;321B
-hieuhcirclekorean;326D
-hieuhkorean;314E
-hieuhparenkorean;320D
-hihiragana;3072
-hikatakana;30D2
-hikatakanahalfwidth;FF8B
-hiriq;05B4
-hiriq14;05B4
-hiriq21;05B4
-hiriq2d;05B4
-hiriqhebrew;05B4
-hiriqnarrowhebrew;05B4
-hiriqquarterhebrew;05B4
-hiriqwidehebrew;05B4
-hlinebelow;1E96
-hmonospace;FF48
-hoarmenian;0570
-hohipthai;0E2B
-hohiragana;307B
-hokatakana;30DB
-hokatakanahalfwidth;FF8E
-holam;05B9
-holam19;05B9
-holam26;05B9
-holam32;05B9
-holamhebrew;05B9
-holamnarrowhebrew;05B9
-holamquarterhebrew;05B9
-holamwidehebrew;05B9
-honokhukthai;0E2E
-hookabovecomb;0309
-hookcmb;0309
-hookpalatalizedbelowcmb;0321
-hookretroflexbelowcmb;0322
-hoonsquare;3342
-horicoptic;03E9
-horizontalbar;2015
-horncmb;031B
-hotsprings;2668
-house;2302
-hparen;24A3
-hsuperior;02B0
-hturned;0265
-huhiragana;3075
-huiitosquare;3333
-hukatakana;30D5
-hukatakanahalfwidth;FF8C
-hungarumlaut;02DD
-hungarumlautcmb;030B
-hv;0195
-hyphen;002D
-hypheninferior;F6E5
-hyphenmonospace;FF0D
-hyphensmall;FE63
-hyphensuperior;F6E6
-hyphentwo;2010
-i;0069
-iacute;00ED
-iacyrillic;044F
-ibengali;0987
-ibopomofo;3127
-ibreve;012D
-icaron;01D0
-icircle;24D8
-icircumflex;00EE
-icyrillic;0456
-idblgrave;0209
-ideographearthcircle;328F
-ideographfirecircle;328B
-ideographicallianceparen;323F
-ideographiccallparen;323A
-ideographiccentrecircle;32A5
-ideographicclose;3006
-ideographiccomma;3001
-ideographiccommaleft;FF64
-ideographiccongratulationparen;3237
-ideographiccorrectcircle;32A3
-ideographicearthparen;322F
-ideographicenterpriseparen;323D
-ideographicexcellentcircle;329D
-ideographicfestivalparen;3240
-ideographicfinancialcircle;3296
-ideographicfinancialparen;3236
-ideographicfireparen;322B
-ideographichaveparen;3232
-ideographichighcircle;32A4
-ideographiciterationmark;3005
-ideographiclaborcircle;3298
-ideographiclaborparen;3238
-ideographicleftcircle;32A7
-ideographiclowcircle;32A6
-ideographicmedicinecircle;32A9
-ideographicmetalparen;322E
-ideographicmoonparen;322A
-ideographicnameparen;3234
-ideographicperiod;3002
-ideographicprintcircle;329E
-ideographicreachparen;3243
-ideographicrepresentparen;3239
-ideographicresourceparen;323E
-ideographicrightcircle;32A8
-ideographicsecretcircle;3299
-ideographicselfparen;3242
-ideographicsocietyparen;3233
-ideographicspace;3000
-ideographicspecialparen;3235
-ideographicstockparen;3231
-ideographicstudyparen;323B
-ideographicsunparen;3230
-ideographicsuperviseparen;323C
-ideographicwaterparen;322C
-ideographicwoodparen;322D
-ideographiczero;3007
-ideographmetalcircle;328E
-ideographmooncircle;328A
-ideographnamecircle;3294
-ideographsuncircle;3290
-ideographwatercircle;328C
-ideographwoodcircle;328D
-ideva;0907
-idieresis;00EF
-idieresisacute;1E2F
-idieresiscyrillic;04E5
-idotbelow;1ECB
-iebrevecyrillic;04D7
-iecyrillic;0435
-ieungacirclekorean;3275
-ieungaparenkorean;3215
-ieungcirclekorean;3267
-ieungkorean;3147
-ieungparenkorean;3207
-igrave;00EC
-igujarati;0A87
-igurmukhi;0A07
-ihiragana;3044
-ihookabove;1EC9
-iibengali;0988
-iicyrillic;0438
-iideva;0908
-iigujarati;0A88
-iigurmukhi;0A08
-iimatragurmukhi;0A40
-iinvertedbreve;020B
-iishortcyrillic;0439
-iivowelsignbengali;09C0
-iivowelsigndeva;0940
-iivowelsigngujarati;0AC0
-ij;0133
-ikatakana;30A4
-ikatakanahalfwidth;FF72
-ikorean;3163
-ilde;02DC
-iluyhebrew;05AC
-imacron;012B
-imacroncyrillic;04E3
-imageorapproximatelyequal;2253
-imatragurmukhi;0A3F
-imonospace;FF49
-increment;2206
-infinity;221E
-iniarmenian;056B
-integral;222B
-integralbottom;2321
-integralbt;2321
-integralex;F8F5
-integraltop;2320
-integraltp;2320
-intersection;2229
-intisquare;3305
-invbullet;25D8
-invcircle;25D9
-invsmileface;263B
-iocyrillic;0451
-iogonek;012F
-iota;03B9
-iotadieresis;03CA
-iotadieresistonos;0390
-iotalatin;0269
-iotatonos;03AF
-iparen;24A4
-irigurmukhi;0A72
-ismallhiragana;3043
-ismallkatakana;30A3
-ismallkatakanahalfwidth;FF68
-issharbengali;09FA
-istroke;0268
-isuperior;F6ED
-iterationhiragana;309D
-iterationkatakana;30FD
-itilde;0129
-itildebelow;1E2D
-iubopomofo;3129
-iucyrillic;044E
-ivowelsignbengali;09BF
-ivowelsigndeva;093F
-ivowelsigngujarati;0ABF
-izhitsacyrillic;0475
-izhitsadblgravecyrillic;0477
-j;006A
-jaarmenian;0571
-jabengali;099C
-jadeva;091C
-jagujarati;0A9C
-jagurmukhi;0A1C
-jbopomofo;3110
-jcaron;01F0
-jcircle;24D9
-jcircumflex;0135
-jcrossedtail;029D
-jdotlessstroke;025F
-jecyrillic;0458
-jeemarabic;062C
-jeemfinalarabic;FE9E
-jeeminitialarabic;FE9F
-jeemmedialarabic;FEA0
-jeharabic;0698
-jehfinalarabic;FB8B
-jhabengali;099D
-jhadeva;091D
-jhagujarati;0A9D
-jhagurmukhi;0A1D
-jheharmenian;057B
-jis;3004
-jmonospace;FF4A
-jparen;24A5
-jsuperior;02B2
-k;006B
-kabashkircyrillic;04A1
-kabengali;0995
-kacute;1E31
-kacyrillic;043A
-kadescendercyrillic;049B
-kadeva;0915
-kaf;05DB
-kafarabic;0643
-kafdagesh;FB3B
-kafdageshhebrew;FB3B
-kaffinalarabic;FEDA
-kafhebrew;05DB
-kafinitialarabic;FEDB
-kafmedialarabic;FEDC
-kafrafehebrew;FB4D
-kagujarati;0A95
-kagurmukhi;0A15
-kahiragana;304B
-kahookcyrillic;04C4
-kakatakana;30AB
-kakatakanahalfwidth;FF76
-kappa;03BA
-kappasymbolgreek;03F0
-kapyeounmieumkorean;3171
-kapyeounphieuphkorean;3184
-kapyeounpieupkorean;3178
-kapyeounssangpieupkorean;3179
-karoriisquare;330D
-kashidaautoarabic;0640
-kashidaautonosidebearingarabic;0640
-kasmallkatakana;30F5
-kasquare;3384
-kasraarabic;0650
-kasratanarabic;064D
-kastrokecyrillic;049F
-katahiraprolongmarkhalfwidth;FF70
-kaverticalstrokecyrillic;049D
-kbopomofo;310E
-kcalsquare;3389
-kcaron;01E9
-kcedilla;0137
-kcircle;24DA
-kcommaaccent;0137
-kdotbelow;1E33
-keharmenian;0584
-kehiragana;3051
-kekatakana;30B1
-kekatakanahalfwidth;FF79
-kenarmenian;056F
-kesmallkatakana;30F6
-kgreenlandic;0138
-khabengali;0996
-khacyrillic;0445
-khadeva;0916
-khagujarati;0A96
-khagurmukhi;0A16
-khaharabic;062E
-khahfinalarabic;FEA6
-khahinitialarabic;FEA7
-khahmedialarabic;FEA8
-kheicoptic;03E7
-khhadeva;0959
-khhagurmukhi;0A59
-khieukhacirclekorean;3278
-khieukhaparenkorean;3218
-khieukhcirclekorean;326A
-khieukhkorean;314B
-khieukhparenkorean;320A
-khokhaithai;0E02
-khokhonthai;0E05
-khokhuatthai;0E03
-khokhwaithai;0E04
-khomutthai;0E5B
-khook;0199
-khorakhangthai;0E06
-khzsquare;3391
-kihiragana;304D
-kikatakana;30AD
-kikatakanahalfwidth;FF77
-kiroguramusquare;3315
-kiromeetorusquare;3316
-kirosquare;3314
-kiyeokacirclekorean;326E
-kiyeokaparenkorean;320E
-kiyeokcirclekorean;3260
-kiyeokkorean;3131
-kiyeokparenkorean;3200
-kiyeoksioskorean;3133
-kjecyrillic;045C
-klinebelow;1E35
-klsquare;3398
-kmcubedsquare;33A6
-kmonospace;FF4B
-kmsquaredsquare;33A2
-kohiragana;3053
-kohmsquare;33C0
-kokaithai;0E01
-kokatakana;30B3
-kokatakanahalfwidth;FF7A
-kooposquare;331E
-koppacyrillic;0481
-koreanstandardsymbol;327F
-koroniscmb;0343
-kparen;24A6
-kpasquare;33AA
-ksicyrillic;046F
-ktsquare;33CF
-kturned;029E
-kuhiragana;304F
-kukatakana;30AF
-kukatakanahalfwidth;FF78
-kvsquare;33B8
-kwsquare;33BE
-l;006C
-labengali;09B2
-lacute;013A
-ladeva;0932
-lagujarati;0AB2
-lagurmukhi;0A32
-lakkhangyaothai;0E45
-lamaleffinalarabic;FEFC
-lamalefhamzaabovefinalarabic;FEF8
-lamalefhamzaaboveisolatedarabic;FEF7
-lamalefhamzabelowfinalarabic;FEFA
-lamalefhamzabelowisolatedarabic;FEF9
-lamalefisolatedarabic;FEFB
-lamalefmaddaabovefinalarabic;FEF6
-lamalefmaddaaboveisolatedarabic;FEF5
-lamarabic;0644
-lambda;03BB
-lambdastroke;019B
-lamed;05DC
-lameddagesh;FB3C
-lameddageshhebrew;FB3C
-lamedhebrew;05DC
-lamedholam;05DC 05B9
-lamedholamdagesh;05DC 05B9 05BC
-lamedholamdageshhebrew;05DC 05B9 05BC
-lamedholamhebrew;05DC 05B9
-lamfinalarabic;FEDE
-lamhahinitialarabic;FCCA
-laminitialarabic;FEDF
-lamjeeminitialarabic;FCC9
-lamkhahinitialarabic;FCCB
-lamlamhehisolatedarabic;FDF2
-lammedialarabic;FEE0
-lammeemhahinitialarabic;FD88
-lammeeminitialarabic;FCCC
-lammeemjeeminitialarabic;FEDF FEE4 FEA0
-lammeemkhahinitialarabic;FEDF FEE4 FEA8
-largecircle;25EF
-lbar;019A
-lbelt;026C
-lbopomofo;310C
-lcaron;013E
-lcedilla;013C
-lcircle;24DB
-lcircumflexbelow;1E3D
-lcommaaccent;013C
-ldot;0140
-ldotaccent;0140
-ldotbelow;1E37
-ldotbelowmacron;1E39
-leftangleabovecmb;031A
-lefttackbelowcmb;0318
-less;003C
-lessequal;2264
-lessequalorgreater;22DA
-lessmonospace;FF1C
-lessorequivalent;2272
-lessorgreater;2276
-lessoverequal;2266
-lesssmall;FE64
-lezh;026E
-lfblock;258C
-lhookretroflex;026D
-lira;20A4
-liwnarmenian;056C
-lj;01C9
-ljecyrillic;0459
-ll;F6C0
-lladeva;0933
-llagujarati;0AB3
-llinebelow;1E3B
-llladeva;0934
-llvocalicbengali;09E1
-llvocalicdeva;0961
-llvocalicvowelsignbengali;09E3
-llvocalicvowelsigndeva;0963
-lmiddletilde;026B
-lmonospace;FF4C
-lmsquare;33D0
-lochulathai;0E2C
-logicaland;2227
-logicalnot;00AC
-logicalnotreversed;2310
-logicalor;2228
-lolingthai;0E25
-longs;017F
-lowlinecenterline;FE4E
-lowlinecmb;0332
-lowlinedashed;FE4D
-lozenge;25CA
-lparen;24A7
-lslash;0142
-lsquare;2113
-lsuperior;F6EE
-ltshade;2591
-luthai;0E26
-lvocalicbengali;098C
-lvocalicdeva;090C
-lvocalicvowelsignbengali;09E2
-lvocalicvowelsigndeva;0962
-lxsquare;33D3
-m;006D
-mabengali;09AE
-macron;00AF
-macronbelowcmb;0331
-macroncmb;0304
-macronlowmod;02CD
-macronmonospace;FFE3
-macute;1E3F
-madeva;092E
-magujarati;0AAE
-magurmukhi;0A2E
-mahapakhhebrew;05A4
-mahapakhlefthebrew;05A4
-mahiragana;307E
-maichattawalowleftthai;F895
-maichattawalowrightthai;F894
-maichattawathai;0E4B
-maichattawaupperleftthai;F893
-maieklowleftthai;F88C
-maieklowrightthai;F88B
-maiekthai;0E48
-maiekupperleftthai;F88A
-maihanakatleftthai;F884
-maihanakatthai;0E31
-maitaikhuleftthai;F889
-maitaikhuthai;0E47
-maitholowleftthai;F88F
-maitholowrightthai;F88E
-maithothai;0E49
-maithoupperleftthai;F88D
-maitrilowleftthai;F892
-maitrilowrightthai;F891
-maitrithai;0E4A
-maitriupperleftthai;F890
-maiyamokthai;0E46
-makatakana;30DE
-makatakanahalfwidth;FF8F
-male;2642
-mansyonsquare;3347
-maqafhebrew;05BE
-mars;2642
-masoracirclehebrew;05AF
-masquare;3383
-mbopomofo;3107
-mbsquare;33D4
-mcircle;24DC
-mcubedsquare;33A5
-mdotaccent;1E41
-mdotbelow;1E43
-meemarabic;0645
-meemfinalarabic;FEE2
-meeminitialarabic;FEE3
-meemmedialarabic;FEE4
-meemmeeminitialarabic;FCD1
-meemmeemisolatedarabic;FC48
-meetorusquare;334D
-mehiragana;3081
-meizierasquare;337E
-mekatakana;30E1
-mekatakanahalfwidth;FF92
-mem;05DE
-memdagesh;FB3E
-memdageshhebrew;FB3E
-memhebrew;05DE
-menarmenian;0574
-merkhahebrew;05A5
-merkhakefulahebrew;05A6
-merkhakefulalefthebrew;05A6
-merkhalefthebrew;05A5
-mhook;0271
-mhzsquare;3392
-middledotkatakanahalfwidth;FF65
-middot;00B7
-mieumacirclekorean;3272
-mieumaparenkorean;3212
-mieumcirclekorean;3264
-mieumkorean;3141
-mieumpansioskorean;3170
-mieumparenkorean;3204
-mieumpieupkorean;316E
-mieumsioskorean;316F
-mihiragana;307F
-mikatakana;30DF
-mikatakanahalfwidth;FF90
-minus;2212
-minusbelowcmb;0320
-minuscircle;2296
-minusmod;02D7
-minusplus;2213
-minute;2032
-miribaarusquare;334A
-mirisquare;3349
-mlonglegturned;0270
-mlsquare;3396
-mmcubedsquare;33A3
-mmonospace;FF4D
-mmsquaredsquare;339F
-mohiragana;3082
-mohmsquare;33C1
-mokatakana;30E2
-mokatakanahalfwidth;FF93
-molsquare;33D6
-momathai;0E21
-moverssquare;33A7
-moverssquaredsquare;33A8
-mparen;24A8
-mpasquare;33AB
-mssquare;33B3
-msuperior;F6EF
-mturned;026F
-mu;00B5
-mu1;00B5
-muasquare;3382
-muchgreater;226B
-muchless;226A
-mufsquare;338C
-mugreek;03BC
-mugsquare;338D
-muhiragana;3080
-mukatakana;30E0
-mukatakanahalfwidth;FF91
-mulsquare;3395
-multiply;00D7
-mumsquare;339B
-munahhebrew;05A3
-munahlefthebrew;05A3
-musicalnote;266A
-musicalnotedbl;266B
-musicflatsign;266D
-musicsharpsign;266F
-mussquare;33B2
-muvsquare;33B6
-muwsquare;33BC
-mvmegasquare;33B9
-mvsquare;33B7
-mwmegasquare;33BF
-mwsquare;33BD
-n;006E
-nabengali;09A8
-nabla;2207
-nacute;0144
-nadeva;0928
-nagujarati;0AA8
-nagurmukhi;0A28
-nahiragana;306A
-nakatakana;30CA
-nakatakanahalfwidth;FF85
-napostrophe;0149
-nasquare;3381
-nbopomofo;310B
-nbspace;00A0
-ncaron;0148
-ncedilla;0146
-ncircle;24DD
-ncircumflexbelow;1E4B
-ncommaaccent;0146
-ndotaccent;1E45
-ndotbelow;1E47
-nehiragana;306D
-nekatakana;30CD
-nekatakanahalfwidth;FF88
-newsheqelsign;20AA
-nfsquare;338B
-ngabengali;0999
-ngadeva;0919
-ngagujarati;0A99
-ngagurmukhi;0A19
-ngonguthai;0E07
-nhiragana;3093
-nhookleft;0272
-nhookretroflex;0273
-nieunacirclekorean;326F
-nieunaparenkorean;320F
-nieuncieuckorean;3135
-nieuncirclekorean;3261
-nieunhieuhkorean;3136
-nieunkorean;3134
-nieunpansioskorean;3168
-nieunparenkorean;3201
-nieunsioskorean;3167
-nieuntikeutkorean;3166
-nihiragana;306B
-nikatakana;30CB
-nikatakanahalfwidth;FF86
-nikhahitleftthai;F899
-nikhahitthai;0E4D
-nine;0039
-ninearabic;0669
-ninebengali;09EF
-ninecircle;2468
-ninecircleinversesansserif;2792
-ninedeva;096F
-ninegujarati;0AEF
-ninegurmukhi;0A6F
-ninehackarabic;0669
-ninehangzhou;3029
-nineideographicparen;3228
-nineinferior;2089
-ninemonospace;FF19
-nineoldstyle;F739
-nineparen;247C
-nineperiod;2490
-ninepersian;06F9
-nineroman;2178
-ninesuperior;2079
-nineteencircle;2472
-nineteenparen;2486
-nineteenperiod;249A
-ninethai;0E59
-nj;01CC
-njecyrillic;045A
-nkatakana;30F3
-nkatakanahalfwidth;FF9D
-nlegrightlong;019E
-nlinebelow;1E49
-nmonospace;FF4E
-nmsquare;339A
-nnabengali;09A3
-nnadeva;0923
-nnagujarati;0AA3
-nnagurmukhi;0A23
-nnnadeva;0929
-nohiragana;306E
-nokatakana;30CE
-nokatakanahalfwidth;FF89
-nonbreakingspace;00A0
-nonenthai;0E13
-nonuthai;0E19
-noonarabic;0646
-noonfinalarabic;FEE6
-noonghunnaarabic;06BA
-noonghunnafinalarabic;FB9F
-noonhehinitialarabic;FEE7 FEEC
-nooninitialarabic;FEE7
-noonjeeminitialarabic;FCD2
-noonjeemisolatedarabic;FC4B
-noonmedialarabic;FEE8
-noonmeeminitialarabic;FCD5
-noonmeemisolatedarabic;FC4E
-noonnoonfinalarabic;FC8D
-notcontains;220C
-notelement;2209
-notelementof;2209
-notequal;2260
-notgreater;226F
-notgreaternorequal;2271
-notgreaternorless;2279
-notidentical;2262
-notless;226E
-notlessnorequal;2270
-notparallel;2226
-notprecedes;2280
-notsubset;2284
-notsucceeds;2281
-notsuperset;2285
-nowarmenian;0576
-nparen;24A9
-nssquare;33B1
-nsuperior;207F
-ntilde;00F1
-nu;03BD
-nuhiragana;306C
-nukatakana;30CC
-nukatakanahalfwidth;FF87
-nuktabengali;09BC
-nuktadeva;093C
-nuktagujarati;0ABC
-nuktagurmukhi;0A3C
-numbersign;0023
-numbersignmonospace;FF03
-numbersignsmall;FE5F
-numeralsigngreek;0374
-numeralsignlowergreek;0375
-numero;2116
-nun;05E0
-nundagesh;FB40
-nundageshhebrew;FB40
-nunhebrew;05E0
-nvsquare;33B5
-nwsquare;33BB
-nyabengali;099E
-nyadeva;091E
-nyagujarati;0A9E
-nyagurmukhi;0A1E
-o;006F
-oacute;00F3
-oangthai;0E2D
-obarred;0275
-obarredcyrillic;04E9
-obarreddieresiscyrillic;04EB
-obengali;0993
-obopomofo;311B
-obreve;014F
-ocandradeva;0911
-ocandragujarati;0A91
-ocandravowelsigndeva;0949
-ocandravowelsigngujarati;0AC9
-ocaron;01D2
-ocircle;24DE
-ocircumflex;00F4
-ocircumflexacute;1ED1
-ocircumflexdotbelow;1ED9
-ocircumflexgrave;1ED3
-ocircumflexhookabove;1ED5
-ocircumflextilde;1ED7
-ocyrillic;043E
-odblacute;0151
-odblgrave;020D
-odeva;0913
-odieresis;00F6
-odieresiscyrillic;04E7
-odotbelow;1ECD
-oe;0153
-oekorean;315A
-ogonek;02DB
-ogonekcmb;0328
-ograve;00F2
-ogujarati;0A93
-oharmenian;0585
-ohiragana;304A
-ohookabove;1ECF
-ohorn;01A1
-ohornacute;1EDB
-ohorndotbelow;1EE3
-ohorngrave;1EDD
-ohornhookabove;1EDF
-ohorntilde;1EE1
-ohungarumlaut;0151
-oi;01A3
-oinvertedbreve;020F
-okatakana;30AA
-okatakanahalfwidth;FF75
-okorean;3157
-olehebrew;05AB
-omacron;014D
-omacronacute;1E53
-omacrongrave;1E51
-omdeva;0950
-omega;03C9
-omega1;03D6
-omegacyrillic;0461
-omegalatinclosed;0277
-omegaroundcyrillic;047B
-omegatitlocyrillic;047D
-omegatonos;03CE
-omgujarati;0AD0
-omicron;03BF
-omicrontonos;03CC
-omonospace;FF4F
-one;0031
-onearabic;0661
-onebengali;09E7
-onecircle;2460
-onecircleinversesansserif;278A
-onedeva;0967
-onedotenleader;2024
-oneeighth;215B
-onefitted;F6DC
-onegujarati;0AE7
-onegurmukhi;0A67
-onehackarabic;0661
-onehalf;00BD
-onehangzhou;3021
-oneideographicparen;3220
-oneinferior;2081
-onemonospace;FF11
-onenumeratorbengali;09F4
-oneoldstyle;F731
-oneparen;2474
-oneperiod;2488
-onepersian;06F1
-onequarter;00BC
-oneroman;2170
-onesuperior;00B9
-onethai;0E51
-onethird;2153
-oogonek;01EB
-oogonekmacron;01ED
-oogurmukhi;0A13
-oomatragurmukhi;0A4B
-oopen;0254
-oparen;24AA
-openbullet;25E6
-option;2325
-ordfeminine;00AA
-ordmasculine;00BA
-orthogonal;221F
-oshortdeva;0912
-oshortvowelsigndeva;094A
-oslash;00F8
-oslashacute;01FF
-osmallhiragana;3049
-osmallkatakana;30A9
-osmallkatakanahalfwidth;FF6B
-ostrokeacute;01FF
-osuperior;F6F0
-otcyrillic;047F
-otilde;00F5
-otildeacute;1E4D
-otildedieresis;1E4F
-oubopomofo;3121
-overline;203E
-overlinecenterline;FE4A
-overlinecmb;0305
-overlinedashed;FE49
-overlinedblwavy;FE4C
-overlinewavy;FE4B
-overscore;00AF
-ovowelsignbengali;09CB
-ovowelsigndeva;094B
-ovowelsigngujarati;0ACB
-p;0070
-paampssquare;3380
-paasentosquare;332B
-pabengali;09AA
-pacute;1E55
-padeva;092A
-pagedown;21DF
-pageup;21DE
-pagujarati;0AAA
-pagurmukhi;0A2A
-pahiragana;3071
-paiyannoithai;0E2F
-pakatakana;30D1
-palatalizationcyrilliccmb;0484
-palochkacyrillic;04C0
-pansioskorean;317F
-paragraph;00B6
-parallel;2225
-parenleft;0028
-parenleftaltonearabic;FD3E
-parenleftbt;F8ED
-parenleftex;F8EC
-parenleftinferior;208D
-parenleftmonospace;FF08
-parenleftsmall;FE59
-parenleftsuperior;207D
-parenlefttp;F8EB
-parenleftvertical;FE35
-parenright;0029
-parenrightaltonearabic;FD3F
-parenrightbt;F8F8
-parenrightex;F8F7
-parenrightinferior;208E
-parenrightmonospace;FF09
-parenrightsmall;FE5A
-parenrightsuperior;207E
-parenrighttp;F8F6
-parenrightvertical;FE36
-partialdiff;2202
-paseqhebrew;05C0
-pashtahebrew;0599
-pasquare;33A9
-patah;05B7
-patah11;05B7
-patah1d;05B7
-patah2a;05B7
-patahhebrew;05B7
-patahnarrowhebrew;05B7
-patahquarterhebrew;05B7
-patahwidehebrew;05B7
-pazerhebrew;05A1
-pbopomofo;3106
-pcircle;24DF
-pdotaccent;1E57
-pe;05E4
-pecyrillic;043F
-pedagesh;FB44
-pedageshhebrew;FB44
-peezisquare;333B
-pefinaldageshhebrew;FB43
-peharabic;067E
-peharmenian;057A
-pehebrew;05E4
-pehfinalarabic;FB57
-pehinitialarabic;FB58
-pehiragana;307A
-pehmedialarabic;FB59
-pekatakana;30DA
-pemiddlehookcyrillic;04A7
-perafehebrew;FB4E
-percent;0025
-percentarabic;066A
-percentmonospace;FF05
-percentsmall;FE6A
-period;002E
-periodarmenian;0589
-periodcentered;00B7
-periodhalfwidth;FF61
-periodinferior;F6E7
-periodmonospace;FF0E
-periodsmall;FE52
-periodsuperior;F6E8
-perispomenigreekcmb;0342
-perpendicular;22A5
-perthousand;2030
-peseta;20A7
-pfsquare;338A
-phabengali;09AB
-phadeva;092B
-phagujarati;0AAB
-phagurmukhi;0A2B
-phi;03C6
-phi1;03D5
-phieuphacirclekorean;327A
-phieuphaparenkorean;321A
-phieuphcirclekorean;326C
-phieuphkorean;314D
-phieuphparenkorean;320C
-philatin;0278
-phinthuthai;0E3A
-phisymbolgreek;03D5
-phook;01A5
-phophanthai;0E1E
-phophungthai;0E1C
-phosamphaothai;0E20
-pi;03C0
-pieupacirclekorean;3273
-pieupaparenkorean;3213
-pieupcieuckorean;3176
-pieupcirclekorean;3265
-pieupkiyeokkorean;3172
-pieupkorean;3142
-pieupparenkorean;3205
-pieupsioskiyeokkorean;3174
-pieupsioskorean;3144
-pieupsiostikeutkorean;3175
-pieupthieuthkorean;3177
-pieuptikeutkorean;3173
-pihiragana;3074
-pikatakana;30D4
-pisymbolgreek;03D6
-piwrarmenian;0583
-plus;002B
-plusbelowcmb;031F
-pluscircle;2295
-plusminus;00B1
-plusmod;02D6
-plusmonospace;FF0B
-plussmall;FE62
-plussuperior;207A
-pmonospace;FF50
-pmsquare;33D8
-pohiragana;307D
-pointingindexdownwhite;261F
-pointingindexleftwhite;261C
-pointingindexrightwhite;261E
-pointingindexupwhite;261D
-pokatakana;30DD
-poplathai;0E1B
-postalmark;3012
-postalmarkface;3020
-pparen;24AB
-precedes;227A
-prescription;211E
-primemod;02B9
-primereversed;2035
-product;220F
-projective;2305
-prolongedkana;30FC
-propellor;2318
-propersubset;2282
-propersuperset;2283
-proportion;2237
-proportional;221D
-psi;03C8
-psicyrillic;0471
-psilipneumatacyrilliccmb;0486
-pssquare;33B0
-puhiragana;3077
-pukatakana;30D7
-pvsquare;33B4
-pwsquare;33BA
-q;0071
-qadeva;0958
-qadmahebrew;05A8
-qafarabic;0642
-qaffinalarabic;FED6
-qafinitialarabic;FED7
-qafmedialarabic;FED8
-qamats;05B8
-qamats10;05B8
-qamats1a;05B8
-qamats1c;05B8
-qamats27;05B8
-qamats29;05B8
-qamats33;05B8
-qamatsde;05B8
-qamatshebrew;05B8
-qamatsnarrowhebrew;05B8
-qamatsqatanhebrew;05B8
-qamatsqatannarrowhebrew;05B8
-qamatsqatanquarterhebrew;05B8
-qamatsqatanwidehebrew;05B8
-qamatsquarterhebrew;05B8
-qamatswidehebrew;05B8
-qarneyparahebrew;059F
-qbopomofo;3111
-qcircle;24E0
-qhook;02A0
-qmonospace;FF51
-qof;05E7
-qofdagesh;FB47
-qofdageshhebrew;FB47
-qofhatafpatah;05E7 05B2
-qofhatafpatahhebrew;05E7 05B2
-qofhatafsegol;05E7 05B1
-qofhatafsegolhebrew;05E7 05B1
-qofhebrew;05E7
-qofhiriq;05E7 05B4
-qofhiriqhebrew;05E7 05B4
-qofholam;05E7 05B9
-qofholamhebrew;05E7 05B9
-qofpatah;05E7 05B7
-qofpatahhebrew;05E7 05B7
-qofqamats;05E7 05B8
-qofqamatshebrew;05E7 05B8
-qofqubuts;05E7 05BB
-qofqubutshebrew;05E7 05BB
-qofsegol;05E7 05B6
-qofsegolhebrew;05E7 05B6
-qofsheva;05E7 05B0
-qofshevahebrew;05E7 05B0
-qoftsere;05E7 05B5
-qoftserehebrew;05E7 05B5
-qparen;24AC
-quarternote;2669
-qubuts;05BB
-qubuts18;05BB
-qubuts25;05BB
-qubuts31;05BB
-qubutshebrew;05BB
-qubutsnarrowhebrew;05BB
-qubutsquarterhebrew;05BB
-qubutswidehebrew;05BB
-question;003F
-questionarabic;061F
-questionarmenian;055E
-questiondown;00BF
-questiondownsmall;F7BF
-questiongreek;037E
-questionmonospace;FF1F
-questionsmall;F73F
-quotedbl;0022
-quotedblbase;201E
-quotedblleft;201C
-quotedblmonospace;FF02
-quotedblprime;301E
-quotedblprimereversed;301D
-quotedblright;201D
-quoteleft;2018
-quoteleftreversed;201B
-quotereversed;201B
-quoteright;2019
-quoterightn;0149
-quotesinglbase;201A
-quotesingle;0027
-quotesinglemonospace;FF07
-r;0072
-raarmenian;057C
-rabengali;09B0
-racute;0155
-radeva;0930
-radical;221A
-radicalex;F8E5
-radoverssquare;33AE
-radoverssquaredsquare;33AF
-radsquare;33AD
-rafe;05BF
-rafehebrew;05BF
-ragujarati;0AB0
-ragurmukhi;0A30
-rahiragana;3089
-rakatakana;30E9
-rakatakanahalfwidth;FF97
-ralowerdiagonalbengali;09F1
-ramiddlediagonalbengali;09F0
-ramshorn;0264
-ratio;2236
-rbopomofo;3116
-rcaron;0159
-rcedilla;0157
-rcircle;24E1
-rcommaaccent;0157
-rdblgrave;0211
-rdotaccent;1E59
-rdotbelow;1E5B
-rdotbelowmacron;1E5D
-referencemark;203B
-reflexsubset;2286
-reflexsuperset;2287
-registered;00AE
-registersans;F8E8
-registerserif;F6DA
-reharabic;0631
-reharmenian;0580
-rehfinalarabic;FEAE
-rehiragana;308C
-rehyehaleflamarabic;0631 FEF3 FE8E 0644
-rekatakana;30EC
-rekatakanahalfwidth;FF9A
-resh;05E8
-reshdageshhebrew;FB48
-reshhatafpatah;05E8 05B2
-reshhatafpatahhebrew;05E8 05B2
-reshhatafsegol;05E8 05B1
-reshhatafsegolhebrew;05E8 05B1
-reshhebrew;05E8
-reshhiriq;05E8 05B4
-reshhiriqhebrew;05E8 05B4
-reshholam;05E8 05B9
-reshholamhebrew;05E8 05B9
-reshpatah;05E8 05B7
-reshpatahhebrew;05E8 05B7
-reshqamats;05E8 05B8
-reshqamatshebrew;05E8 05B8
-reshqubuts;05E8 05BB
-reshqubutshebrew;05E8 05BB
-reshsegol;05E8 05B6
-reshsegolhebrew;05E8 05B6
-reshsheva;05E8 05B0
-reshshevahebrew;05E8 05B0
-reshtsere;05E8 05B5
-reshtserehebrew;05E8 05B5
-reversedtilde;223D
-reviahebrew;0597
-reviamugrashhebrew;0597
-revlogicalnot;2310
-rfishhook;027E
-rfishhookreversed;027F
-rhabengali;09DD
-rhadeva;095D
-rho;03C1
-rhook;027D
-rhookturned;027B
-rhookturnedsuperior;02B5
-rhosymbolgreek;03F1
-rhotichookmod;02DE
-rieulacirclekorean;3271
-rieulaparenkorean;3211
-rieulcirclekorean;3263
-rieulhieuhkorean;3140
-rieulkiyeokkorean;313A
-rieulkiyeoksioskorean;3169
-rieulkorean;3139
-rieulmieumkorean;313B
-rieulpansioskorean;316C
-rieulparenkorean;3203
-rieulphieuphkorean;313F
-rieulpieupkorean;313C
-rieulpieupsioskorean;316B
-rieulsioskorean;313D
-rieulthieuthkorean;313E
-rieultikeutkorean;316A
-rieulyeorinhieuhkorean;316D
-rightangle;221F
-righttackbelowcmb;0319
-righttriangle;22BF
-rihiragana;308A
-rikatakana;30EA
-rikatakanahalfwidth;FF98
-ring;02DA
-ringbelowcmb;0325
-ringcmb;030A
-ringhalfleft;02BF
-ringhalfleftarmenian;0559
-ringhalfleftbelowcmb;031C
-ringhalfleftcentered;02D3
-ringhalfright;02BE
-ringhalfrightbelowcmb;0339
-ringhalfrightcentered;02D2
-rinvertedbreve;0213
-rittorusquare;3351
-rlinebelow;1E5F
-rlongleg;027C
-rlonglegturned;027A
-rmonospace;FF52
-rohiragana;308D
-rokatakana;30ED
-rokatakanahalfwidth;FF9B
-roruathai;0E23
-rparen;24AD
-rrabengali;09DC
-rradeva;0931
-rragurmukhi;0A5C
-rreharabic;0691
-rrehfinalarabic;FB8D
-rrvocalicbengali;09E0
-rrvocalicdeva;0960
-rrvocalicgujarati;0AE0
-rrvocalicvowelsignbengali;09C4
-rrvocalicvowelsigndeva;0944
-rrvocalicvowelsigngujarati;0AC4
-rsuperior;F6F1
-rtblock;2590
-rturned;0279
-rturnedsuperior;02B4
-ruhiragana;308B
-rukatakana;30EB
-rukatakanahalfwidth;FF99
-rupeemarkbengali;09F2
-rupeesignbengali;09F3
-rupiah;F6DD
-ruthai;0E24
-rvocalicbengali;098B
-rvocalicdeva;090B
-rvocalicgujarati;0A8B
-rvocalicvowelsignbengali;09C3
-rvocalicvowelsigndeva;0943
-rvocalicvowelsigngujarati;0AC3
-s;0073
-sabengali;09B8
-sacute;015B
-sacutedotaccent;1E65
-sadarabic;0635
-sadeva;0938
-sadfinalarabic;FEBA
-sadinitialarabic;FEBB
-sadmedialarabic;FEBC
-sagujarati;0AB8
-sagurmukhi;0A38
-sahiragana;3055
-sakatakana;30B5
-sakatakanahalfwidth;FF7B
-sallallahoualayhewasallamarabic;FDFA
-samekh;05E1
-samekhdagesh;FB41
-samekhdageshhebrew;FB41
-samekhhebrew;05E1
-saraaathai;0E32
-saraaethai;0E41
-saraaimaimalaithai;0E44
-saraaimaimuanthai;0E43
-saraamthai;0E33
-saraathai;0E30
-saraethai;0E40
-saraiileftthai;F886
-saraiithai;0E35
-saraileftthai;F885
-saraithai;0E34
-saraothai;0E42
-saraueeleftthai;F888
-saraueethai;0E37
-saraueleftthai;F887
-sarauethai;0E36
-sarauthai;0E38
-sarauuthai;0E39
-sbopomofo;3119
-scaron;0161
-scarondotaccent;1E67
-scedilla;015F
-schwa;0259
-schwacyrillic;04D9
-schwadieresiscyrillic;04DB
-schwahook;025A
-scircle;24E2
-scircumflex;015D
-scommaaccent;0219
-sdotaccent;1E61
-sdotbelow;1E63
-sdotbelowdotaccent;1E69
-seagullbelowcmb;033C
-second;2033
-secondtonechinese;02CA
-section;00A7
-seenarabic;0633
-seenfinalarabic;FEB2
-seeninitialarabic;FEB3
-seenmedialarabic;FEB4
-segol;05B6
-segol13;05B6
-segol1f;05B6
-segol2c;05B6
-segolhebrew;05B6
-segolnarrowhebrew;05B6
-segolquarterhebrew;05B6
-segoltahebrew;0592
-segolwidehebrew;05B6
-seharmenian;057D
-sehiragana;305B
-sekatakana;30BB
-sekatakanahalfwidth;FF7E
-semicolon;003B
-semicolonarabic;061B
-semicolonmonospace;FF1B
-semicolonsmall;FE54
-semivoicedmarkkana;309C
-semivoicedmarkkanahalfwidth;FF9F
-sentisquare;3322
-sentosquare;3323
-seven;0037
-sevenarabic;0667
-sevenbengali;09ED
-sevencircle;2466
-sevencircleinversesansserif;2790
-sevendeva;096D
-seveneighths;215E
-sevengujarati;0AED
-sevengurmukhi;0A6D
-sevenhackarabic;0667
-sevenhangzhou;3027
-sevenideographicparen;3226
-seveninferior;2087
-sevenmonospace;FF17
-sevenoldstyle;F737
-sevenparen;247A
-sevenperiod;248E
-sevenpersian;06F7
-sevenroman;2176
-sevensuperior;2077
-seventeencircle;2470
-seventeenparen;2484
-seventeenperiod;2498
-seventhai;0E57
-sfthyphen;00AD
-shaarmenian;0577
-shabengali;09B6
-shacyrillic;0448
-shaddaarabic;0651
-shaddadammaarabic;FC61
-shaddadammatanarabic;FC5E
-shaddafathaarabic;FC60
-shaddafathatanarabic;0651 064B
-shaddakasraarabic;FC62
-shaddakasratanarabic;FC5F
-shade;2592
-shadedark;2593
-shadelight;2591
-shademedium;2592
-shadeva;0936
-shagujarati;0AB6
-shagurmukhi;0A36
-shalshelethebrew;0593
-shbopomofo;3115
-shchacyrillic;0449
-sheenarabic;0634
-sheenfinalarabic;FEB6
-sheeninitialarabic;FEB7
-sheenmedialarabic;FEB8
-sheicoptic;03E3
-sheqel;20AA
-sheqelhebrew;20AA
-sheva;05B0
-sheva115;05B0
-sheva15;05B0
-sheva22;05B0
-sheva2e;05B0
-shevahebrew;05B0
-shevanarrowhebrew;05B0
-shevaquarterhebrew;05B0
-shevawidehebrew;05B0
-shhacyrillic;04BB
-shimacoptic;03ED
-shin;05E9
-shindagesh;FB49
-shindageshhebrew;FB49
-shindageshshindot;FB2C
-shindageshshindothebrew;FB2C
-shindageshsindot;FB2D
-shindageshsindothebrew;FB2D
-shindothebrew;05C1
-shinhebrew;05E9
-shinshindot;FB2A
-shinshindothebrew;FB2A
-shinsindot;FB2B
-shinsindothebrew;FB2B
-shook;0282
-sigma;03C3
-sigma1;03C2
-sigmafinal;03C2
-sigmalunatesymbolgreek;03F2
-sihiragana;3057
-sikatakana;30B7
-sikatakanahalfwidth;FF7C
-siluqhebrew;05BD
-siluqlefthebrew;05BD
-similar;223C
-sindothebrew;05C2
-siosacirclekorean;3274
-siosaparenkorean;3214
-sioscieuckorean;317E
-sioscirclekorean;3266
-sioskiyeokkorean;317A
-sioskorean;3145
-siosnieunkorean;317B
-siosparenkorean;3206
-siospieupkorean;317D
-siostikeutkorean;317C
-six;0036
-sixarabic;0666
-sixbengali;09EC
-sixcircle;2465
-sixcircleinversesansserif;278F
-sixdeva;096C
-sixgujarati;0AEC
-sixgurmukhi;0A6C
-sixhackarabic;0666
-sixhangzhou;3026
-sixideographicparen;3225
-sixinferior;2086
-sixmonospace;FF16
-sixoldstyle;F736
-sixparen;2479
-sixperiod;248D
-sixpersian;06F6
-sixroman;2175
-sixsuperior;2076
-sixteencircle;246F
-sixteencurrencydenominatorbengali;09F9
-sixteenparen;2483
-sixteenperiod;2497
-sixthai;0E56
-slash;002F
-slashmonospace;FF0F
-slong;017F
-slongdotaccent;1E9B
-smileface;263A
-smonospace;FF53
-sofpasuqhebrew;05C3
-softhyphen;00AD
-softsigncyrillic;044C
-sohiragana;305D
-sokatakana;30BD
-sokatakanahalfwidth;FF7F
-soliduslongoverlaycmb;0338
-solidusshortoverlaycmb;0337
-sorusithai;0E29
-sosalathai;0E28
-sosothai;0E0B
-sosuathai;0E2A
-space;0020
-spacehackarabic;0020
-spade;2660
-spadesuitblack;2660
-spadesuitwhite;2664
-sparen;24AE
-squarebelowcmb;033B
-squarecc;33C4
-squarecm;339D
-squarediagonalcrosshatchfill;25A9
-squarehorizontalfill;25A4
-squarekg;338F
-squarekm;339E
-squarekmcapital;33CE
-squareln;33D1
-squarelog;33D2
-squaremg;338E
-squaremil;33D5
-squaremm;339C
-squaremsquared;33A1
-squareorthogonalcrosshatchfill;25A6
-squareupperlefttolowerrightfill;25A7
-squareupperrighttolowerleftfill;25A8
-squareverticalfill;25A5
-squarewhitewithsmallblack;25A3
-srsquare;33DB
-ssabengali;09B7
-ssadeva;0937
-ssagujarati;0AB7
-ssangcieuckorean;3149
-ssanghieuhkorean;3185
-ssangieungkorean;3180
-ssangkiyeokkorean;3132
-ssangnieunkorean;3165
-ssangpieupkorean;3143
-ssangsioskorean;3146
-ssangtikeutkorean;3138
-ssuperior;F6F2
-sterling;00A3
-sterlingmonospace;FFE1
-strokelongoverlaycmb;0336
-strokeshortoverlaycmb;0335
-subset;2282
-subsetnotequal;228A
-subsetorequal;2286
-succeeds;227B
-suchthat;220B
-suhiragana;3059
-sukatakana;30B9
-sukatakanahalfwidth;FF7D
-sukunarabic;0652
-summation;2211
-sun;263C
-superset;2283
-supersetnotequal;228B
-supersetorequal;2287
-svsquare;33DC
-syouwaerasquare;337C
-t;0074
-tabengali;09A4
-tackdown;22A4
-tackleft;22A3
-tadeva;0924
-tagujarati;0AA4
-tagurmukhi;0A24
-taharabic;0637
-tahfinalarabic;FEC2
-tahinitialarabic;FEC3
-tahiragana;305F
-tahmedialarabic;FEC4
-taisyouerasquare;337D
-takatakana;30BF
-takatakanahalfwidth;FF80
-tatweelarabic;0640
-tau;03C4
-tav;05EA
-tavdages;FB4A
-tavdagesh;FB4A
-tavdageshhebrew;FB4A
-tavhebrew;05EA
-tbar;0167
-tbopomofo;310A
-tcaron;0165
-tccurl;02A8
-tcedilla;0163
-tcheharabic;0686
-tchehfinalarabic;FB7B
-tchehinitialarabic;FB7C
-tchehmedialarabic;FB7D
-tchehmeeminitialarabic;FB7C FEE4
-tcircle;24E3
-tcircumflexbelow;1E71
-tcommaaccent;0163
-tdieresis;1E97
-tdotaccent;1E6B
-tdotbelow;1E6D
-tecyrillic;0442
-tedescendercyrillic;04AD
-teharabic;062A
-tehfinalarabic;FE96
-tehhahinitialarabic;FCA2
-tehhahisolatedarabic;FC0C
-tehinitialarabic;FE97
-tehiragana;3066
-tehjeeminitialarabic;FCA1
-tehjeemisolatedarabic;FC0B
-tehmarbutaarabic;0629
-tehmarbutafinalarabic;FE94
-tehmedialarabic;FE98
-tehmeeminitialarabic;FCA4
-tehmeemisolatedarabic;FC0E
-tehnoonfinalarabic;FC73
-tekatakana;30C6
-tekatakanahalfwidth;FF83
-telephone;2121
-telephoneblack;260E
-telishagedolahebrew;05A0
-telishaqetanahebrew;05A9
-tencircle;2469
-tenideographicparen;3229
-tenparen;247D
-tenperiod;2491
-tenroman;2179
-tesh;02A7
-tet;05D8
-tetdagesh;FB38
-tetdageshhebrew;FB38
-tethebrew;05D8
-tetsecyrillic;04B5
-tevirhebrew;059B
-tevirlefthebrew;059B
-thabengali;09A5
-thadeva;0925
-thagujarati;0AA5
-thagurmukhi;0A25
-thalarabic;0630
-thalfinalarabic;FEAC
-thanthakhatlowleftthai;F898
-thanthakhatlowrightthai;F897
-thanthakhatthai;0E4C
-thanthakhatupperleftthai;F896
-theharabic;062B
-thehfinalarabic;FE9A
-thehinitialarabic;FE9B
-thehmedialarabic;FE9C
-thereexists;2203
-therefore;2234
-theta;03B8
-theta1;03D1
-thetasymbolgreek;03D1
-thieuthacirclekorean;3279
-thieuthaparenkorean;3219
-thieuthcirclekorean;326B
-thieuthkorean;314C
-thieuthparenkorean;320B
-thirteencircle;246C
-thirteenparen;2480
-thirteenperiod;2494
-thonangmonthothai;0E11
-thook;01AD
-thophuthaothai;0E12
-thorn;00FE
-thothahanthai;0E17
-thothanthai;0E10
-thothongthai;0E18
-thothungthai;0E16
-thousandcyrillic;0482
-thousandsseparatorarabic;066C
-thousandsseparatorpersian;066C
-three;0033
-threearabic;0663
-threebengali;09E9
-threecircle;2462
-threecircleinversesansserif;278C
-threedeva;0969
-threeeighths;215C
-threegujarati;0AE9
-threegurmukhi;0A69
-threehackarabic;0663
-threehangzhou;3023
-threeideographicparen;3222
-threeinferior;2083
-threemonospace;FF13
-threenumeratorbengali;09F6
-threeoldstyle;F733
-threeparen;2476
-threeperiod;248A
-threepersian;06F3
-threequarters;00BE
-threequartersemdash;F6DE
-threeroman;2172
-threesuperior;00B3
-threethai;0E53
-thzsquare;3394
-tihiragana;3061
-tikatakana;30C1
-tikatakanahalfwidth;FF81
-tikeutacirclekorean;3270
-tikeutaparenkorean;3210
-tikeutcirclekorean;3262
-tikeutkorean;3137
-tikeutparenkorean;3202
-tilde;02DC
-tildebelowcmb;0330
-tildecmb;0303
-tildecomb;0303
-tildedoublecmb;0360
-tildeoperator;223C
-tildeoverlaycmb;0334
-tildeverticalcmb;033E
-timescircle;2297
-tipehahebrew;0596
-tipehalefthebrew;0596
-tippigurmukhi;0A70
-titlocyrilliccmb;0483
-tiwnarmenian;057F
-tlinebelow;1E6F
-tmonospace;FF54
-toarmenian;0569
-tohiragana;3068
-tokatakana;30C8
-tokatakanahalfwidth;FF84
-tonebarextrahighmod;02E5
-tonebarextralowmod;02E9
-tonebarhighmod;02E6
-tonebarlowmod;02E8
-tonebarmidmod;02E7
-tonefive;01BD
-tonesix;0185
-tonetwo;01A8
-tonos;0384
-tonsquare;3327
-topatakthai;0E0F
-tortoiseshellbracketleft;3014
-tortoiseshellbracketleftsmall;FE5D
-tortoiseshellbracketleftvertical;FE39
-tortoiseshellbracketright;3015
-tortoiseshellbracketrightsmall;FE5E
-tortoiseshellbracketrightvertical;FE3A
-totaothai;0E15
-tpalatalhook;01AB
-tparen;24AF
-trademark;2122
-trademarksans;F8EA
-trademarkserif;F6DB
-tretroflexhook;0288
-triagdn;25BC
-triaglf;25C4
-triagrt;25BA
-triagup;25B2
-ts;02A6
-tsadi;05E6
-tsadidagesh;FB46
-tsadidageshhebrew;FB46
-tsadihebrew;05E6
-tsecyrillic;0446
-tsere;05B5
-tsere12;05B5
-tsere1e;05B5
-tsere2b;05B5
-tserehebrew;05B5
-tserenarrowhebrew;05B5
-tserequarterhebrew;05B5
-tserewidehebrew;05B5
-tshecyrillic;045B
-tsuperior;F6F3
-ttabengali;099F
-ttadeva;091F
-ttagujarati;0A9F
-ttagurmukhi;0A1F
-tteharabic;0679
-ttehfinalarabic;FB67
-ttehinitialarabic;FB68
-ttehmedialarabic;FB69
-tthabengali;09A0
-tthadeva;0920
-tthagujarati;0AA0
-tthagurmukhi;0A20
-tturned;0287
-tuhiragana;3064
-tukatakana;30C4
-tukatakanahalfwidth;FF82
-tusmallhiragana;3063
-tusmallkatakana;30C3
-tusmallkatakanahalfwidth;FF6F
-twelvecircle;246B
-twelveparen;247F
-twelveperiod;2493
-twelveroman;217B
-twentycircle;2473
-twentyhangzhou;5344
-twentyparen;2487
-twentyperiod;249B
-two;0032
-twoarabic;0662
-twobengali;09E8
-twocircle;2461
-twocircleinversesansserif;278B
-twodeva;0968
-twodotenleader;2025
-twodotleader;2025
-twodotleadervertical;FE30
-twogujarati;0AE8
-twogurmukhi;0A68
-twohackarabic;0662
-twohangzhou;3022
-twoideographicparen;3221
-twoinferior;2082
-twomonospace;FF12
-twonumeratorbengali;09F5
-twooldstyle;F732
-twoparen;2475
-twoperiod;2489
-twopersian;06F2
-tworoman;2171
-twostroke;01BB
-twosuperior;00B2
-twothai;0E52
-twothirds;2154
-u;0075
-uacute;00FA
-ubar;0289
-ubengali;0989
-ubopomofo;3128
-ubreve;016D
-ucaron;01D4
-ucircle;24E4
-ucircumflex;00FB
-ucircumflexbelow;1E77
-ucyrillic;0443
-udattadeva;0951
-udblacute;0171
-udblgrave;0215
-udeva;0909
-udieresis;00FC
-udieresisacute;01D8
-udieresisbelow;1E73
-udieresiscaron;01DA
-udieresiscyrillic;04F1
-udieresisgrave;01DC
-udieresismacron;01D6
-udotbelow;1EE5
-ugrave;00F9
-ugujarati;0A89
-ugurmukhi;0A09
-uhiragana;3046
-uhookabove;1EE7
-uhorn;01B0
-uhornacute;1EE9
-uhorndotbelow;1EF1
-uhorngrave;1EEB
-uhornhookabove;1EED
-uhorntilde;1EEF
-uhungarumlaut;0171
-uhungarumlautcyrillic;04F3
-uinvertedbreve;0217
-ukatakana;30A6
-ukatakanahalfwidth;FF73
-ukcyrillic;0479
-ukorean;315C
-umacron;016B
-umacroncyrillic;04EF
-umacrondieresis;1E7B
-umatragurmukhi;0A41
-umonospace;FF55
-underscore;005F
-underscoredbl;2017
-underscoremonospace;FF3F
-underscorevertical;FE33
-underscorewavy;FE4F
-union;222A
-universal;2200
-uogonek;0173
-uparen;24B0
-upblock;2580
-upperdothebrew;05C4
-upsilon;03C5
-upsilondieresis;03CB
-upsilondieresistonos;03B0
-upsilonlatin;028A
-upsilontonos;03CD
-uptackbelowcmb;031D
-uptackmod;02D4
-uragurmukhi;0A73
-uring;016F
-ushortcyrillic;045E
-usmallhiragana;3045
-usmallkatakana;30A5
-usmallkatakanahalfwidth;FF69
-ustraightcyrillic;04AF
-ustraightstrokecyrillic;04B1
-utilde;0169
-utildeacute;1E79
-utildebelow;1E75
-uubengali;098A
-uudeva;090A
-uugujarati;0A8A
-uugurmukhi;0A0A
-uumatragurmukhi;0A42
-uuvowelsignbengali;09C2
-uuvowelsigndeva;0942
-uuvowelsigngujarati;0AC2
-uvowelsignbengali;09C1
-uvowelsigndeva;0941
-uvowelsigngujarati;0AC1
-v;0076
-vadeva;0935
-vagujarati;0AB5
-vagurmukhi;0A35
-vakatakana;30F7
-vav;05D5
-vavdagesh;FB35
-vavdagesh65;FB35
-vavdageshhebrew;FB35
-vavhebrew;05D5
-vavholam;FB4B
-vavholamhebrew;FB4B
-vavvavhebrew;05F0
-vavyodhebrew;05F1
-vcircle;24E5
-vdotbelow;1E7F
-vecyrillic;0432
-veharabic;06A4
-vehfinalarabic;FB6B
-vehinitialarabic;FB6C
-vehmedialarabic;FB6D
-vekatakana;30F9
-venus;2640
-verticalbar;007C
-verticallineabovecmb;030D
-verticallinebelowcmb;0329
-verticallinelowmod;02CC
-verticallinemod;02C8
-vewarmenian;057E
-vhook;028B
-vikatakana;30F8
-viramabengali;09CD
-viramadeva;094D
-viramagujarati;0ACD
-visargabengali;0983
-visargadeva;0903
-visargagujarati;0A83
-vmonospace;FF56
-voarmenian;0578
-voicediterationhiragana;309E
-voicediterationkatakana;30FE
-voicedmarkkana;309B
-voicedmarkkanahalfwidth;FF9E
-vokatakana;30FA
-vparen;24B1
-vtilde;1E7D
-vturned;028C
-vuhiragana;3094
-vukatakana;30F4
-w;0077
-wacute;1E83
-waekorean;3159
-wahiragana;308F
-wakatakana;30EF
-wakatakanahalfwidth;FF9C
-wakorean;3158
-wasmallhiragana;308E
-wasmallkatakana;30EE
-wattosquare;3357
-wavedash;301C
-wavyunderscorevertical;FE34
-wawarabic;0648
-wawfinalarabic;FEEE
-wawhamzaabovearabic;0624
-wawhamzaabovefinalarabic;FE86
-wbsquare;33DD
-wcircle;24E6
-wcircumflex;0175
-wdieresis;1E85
-wdotaccent;1E87
-wdotbelow;1E89
-wehiragana;3091
-weierstrass;2118
-wekatakana;30F1
-wekorean;315E
-weokorean;315D
-wgrave;1E81
-whitebullet;25E6
-whitecircle;25CB
-whitecircleinverse;25D9
-whitecornerbracketleft;300E
-whitecornerbracketleftvertical;FE43
-whitecornerbracketright;300F
-whitecornerbracketrightvertical;FE44
-whitediamond;25C7
-whitediamondcontainingblacksmalldiamond;25C8
-whitedownpointingsmalltriangle;25BF
-whitedownpointingtriangle;25BD
-whiteleftpointingsmalltriangle;25C3
-whiteleftpointingtriangle;25C1
-whitelenticularbracketleft;3016
-whitelenticularbracketright;3017
-whiterightpointingsmalltriangle;25B9
-whiterightpointingtriangle;25B7
-whitesmallsquare;25AB
-whitesmilingface;263A
-whitesquare;25A1
-whitestar;2606
-whitetelephone;260F
-whitetortoiseshellbracketleft;3018
-whitetortoiseshellbracketright;3019
-whiteuppointingsmalltriangle;25B5
-whiteuppointingtriangle;25B3
-wihiragana;3090
-wikatakana;30F0
-wikorean;315F
-wmonospace;FF57
-wohiragana;3092
-wokatakana;30F2
-wokatakanahalfwidth;FF66
-won;20A9
-wonmonospace;FFE6
-wowaenthai;0E27
-wparen;24B2
-wring;1E98
-wsuperior;02B7
-wturned;028D
-wynn;01BF
-x;0078
-xabovecmb;033D
-xbopomofo;3112
-xcircle;24E7
-xdieresis;1E8D
-xdotaccent;1E8B
-xeharmenian;056D
-xi;03BE
-xmonospace;FF58
-xparen;24B3
-xsuperior;02E3
-y;0079
-yaadosquare;334E
-yabengali;09AF
-yacute;00FD
-yadeva;092F
-yaekorean;3152
-yagujarati;0AAF
-yagurmukhi;0A2F
-yahiragana;3084
-yakatakana;30E4
-yakatakanahalfwidth;FF94
-yakorean;3151
-yamakkanthai;0E4E
-yasmallhiragana;3083
-yasmallkatakana;30E3
-yasmallkatakanahalfwidth;FF6C
-yatcyrillic;0463
-ycircle;24E8
-ycircumflex;0177
-ydieresis;00FF
-ydotaccent;1E8F
-ydotbelow;1EF5
-yeharabic;064A
-yehbarreearabic;06D2
-yehbarreefinalarabic;FBAF
-yehfinalarabic;FEF2
-yehhamzaabovearabic;0626
-yehhamzaabovefinalarabic;FE8A
-yehhamzaaboveinitialarabic;FE8B
-yehhamzaabovemedialarabic;FE8C
-yehinitialarabic;FEF3
-yehmedialarabic;FEF4
-yehmeeminitialarabic;FCDD
-yehmeemisolatedarabic;FC58
-yehnoonfinalarabic;FC94
-yehthreedotsbelowarabic;06D1
-yekorean;3156
-yen;00A5
-yenmonospace;FFE5
-yeokorean;3155
-yeorinhieuhkorean;3186
-yerahbenyomohebrew;05AA
-yerahbenyomolefthebrew;05AA
-yericyrillic;044B
-yerudieresiscyrillic;04F9
-yesieungkorean;3181
-yesieungpansioskorean;3183
-yesieungsioskorean;3182
-yetivhebrew;059A
-ygrave;1EF3
-yhook;01B4
-yhookabove;1EF7
-yiarmenian;0575
-yicyrillic;0457
-yikorean;3162
-yinyang;262F
-yiwnarmenian;0582
-ymonospace;FF59
-yod;05D9
-yoddagesh;FB39
-yoddageshhebrew;FB39
-yodhebrew;05D9
-yodyodhebrew;05F2
-yodyodpatahhebrew;FB1F
-yohiragana;3088
-yoikorean;3189
-yokatakana;30E8
-yokatakanahalfwidth;FF96
-yokorean;315B
-yosmallhiragana;3087
-yosmallkatakana;30E7
-yosmallkatakanahalfwidth;FF6E
-yotgreek;03F3
-yoyaekorean;3188
-yoyakorean;3187
-yoyakthai;0E22
-yoyingthai;0E0D
-yparen;24B4
-ypogegrammeni;037A
-ypogegrammenigreekcmb;0345
-yr;01A6
-yring;1E99
-ysuperior;02B8
-ytilde;1EF9
-yturned;028E
-yuhiragana;3086
-yuikorean;318C
-yukatakana;30E6
-yukatakanahalfwidth;FF95
-yukorean;3160
-yusbigcyrillic;046B
-yusbigiotifiedcyrillic;046D
-yuslittlecyrillic;0467
-yuslittleiotifiedcyrillic;0469
-yusmallhiragana;3085
-yusmallkatakana;30E5
-yusmallkatakanahalfwidth;FF6D
-yuyekorean;318B
-yuyeokorean;318A
-yyabengali;09DF
-yyadeva;095F
-z;007A
-zaarmenian;0566
-zacute;017A
-zadeva;095B
-zagurmukhi;0A5B
-zaharabic;0638
-zahfinalarabic;FEC6
-zahinitialarabic;FEC7
-zahiragana;3056
-zahmedialarabic;FEC8
-zainarabic;0632
-zainfinalarabic;FEB0
-zakatakana;30B6
-zaqefgadolhebrew;0595
-zaqefqatanhebrew;0594
-zarqahebrew;0598
-zayin;05D6
-zayindagesh;FB36
-zayindageshhebrew;FB36
-zayinhebrew;05D6
-zbopomofo;3117
-zcaron;017E
-zcircle;24E9
-zcircumflex;1E91
-zcurl;0291
-zdot;017C
-zdotaccent;017C
-zdotbelow;1E93
-zecyrillic;0437
-zedescendercyrillic;0499
-zedieresiscyrillic;04DF
-zehiragana;305C
-zekatakana;30BC
-zero;0030
-zeroarabic;0660
-zerobengali;09E6
-zerodeva;0966
-zerogujarati;0AE6
-zerogurmukhi;0A66
-zerohackarabic;0660
-zeroinferior;2080
-zeromonospace;FF10
-zerooldstyle;F730
-zeropersian;06F0
-zerosuperior;2070
-zerothai;0E50
-zerowidthjoiner;FEFF
-zerowidthnonjoiner;200C
-zerowidthspace;200B
-zeta;03B6
-zhbopomofo;3113
-zhearmenian;056A
-zhebrevecyrillic;04C2
-zhecyrillic;0436
-zhedescendercyrillic;0497
-zhedieresiscyrillic;04DD
-zihiragana;3058
-zikatakana;30B8
-zinorhebrew;05AE
-zlinebelow;1E95
-zmonospace;FF5A
-zohiragana;305E
-zokatakana;30BE
-zparen;24B5
-zretroflexhook;0290
-zstroke;01B6
-zuhiragana;305A
-zukatakana;30BA
-# END
-"""
-
-
-_aglfnText = """\
-# -----------------------------------------------------------
-# Copyright 2002-2019 Adobe (http://www.adobe.com/).
-#
-# Redistribution and use in source and binary forms, with or
-# without modification, are permitted provided that the
-# following conditions are met:
-#
-# Redistributions of source code must retain the above
-# copyright notice, this list of conditions and the following
-# disclaimer.
-#
-# Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following
-# disclaimer in the documentation and/or other materials
-# provided with the distribution.
-#
-# Neither the name of Adobe nor the names of its contributors
-# may be used to endorse or promote products derived from this
-# software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
-# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
-# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
-# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-# -----------------------------------------------------------
-# Name: Adobe Glyph List For New Fonts
-# Table version: 1.7
-# Date: November 6, 2008
-# URL: https://github.com/adobe-type-tools/agl-aglfn
-#
-# Description:
-#
-# AGLFN (Adobe Glyph List For New Fonts) provides a list of base glyph
-# names that are recommended for new fonts, which are compatible with
-# the AGL (Adobe Glyph List) Specification, and which should be used
-# as described in Section 6 of that document. AGLFN comprises the set
-# of glyph names from AGL that map via the AGL Specification rules to
-# the semantically correct UV (Unicode Value). For example, "Asmall"
-# is omitted because AGL maps this glyph name to the PUA (Private Use
-# Area) value U+F761, rather than to the UV that maps from the glyph
-# name "A." Also omitted is "ffi," because AGL maps this to the
-# Alphabetic Presentation Forms value U+FB03, rather than decomposing
-# it into the following sequence of three UVs: U+0066, U+0066, and
-# U+0069. The name "arrowvertex" has been omitted because this glyph
-# now has a real UV, and AGL is now incorrect in mapping it to the PUA
-# value U+F8E6. If you do not find an appropriate name for your glyph
-# in this list, then please refer to Section 6 of the AGL
-# Specification.
-#
-# Format: three semicolon-delimited fields:
-# (1) Standard UV or CUS UV--four uppercase hexadecimal digits
-# (2) Glyph name--upper/lowercase letters and digits
-# (3) Character names: Unicode character names for standard UVs, and
-# descriptive names for CUS UVs--uppercase letters, hyphen, and
-# space
-#
-# The records are sorted by glyph name in increasing ASCII order,
-# entries with the same glyph name are sorted in decreasing priority
-# order, the UVs and Unicode character names are provided for
-# convenience, lines starting with "#" are comments, and blank lines
-# should be ignored.
-#
-# Revision History:
-#
-# 1.7 [6 November 2008]
-# - Reverted to the original 1.4 and earlier mappings for Delta,
-# Omega, and mu.
-# - Removed mappings for "afii" names. These should now be assigned
-# "uni" names.
-# - Removed mappings for "commaaccent" names. These should now be
-# assigned "uni" names.
-#
-# 1.6 [30 January 2006]
-# - Completed work intended in 1.5.
-#
-# 1.5 [23 November 2005]
-# - Removed duplicated block at end of file.
-# - Changed mappings:
-# 2206;Delta;INCREMENT changed to 0394;Delta;GREEK CAPITAL LETTER DELTA
-# 2126;Omega;OHM SIGN changed to 03A9;Omega;GREEK CAPITAL LETTER OMEGA
-# 03BC;mu;MICRO SIGN changed to 03BC;mu;GREEK SMALL LETTER MU
-# - Corrected statement above about why "ffi" is omitted.
-#
-# 1.4 [24 September 2003]
-# - Changed version to 1.4, to avoid confusion with the AGL 1.3.
-# - Fixed spelling errors in the header.
-# - Fully removed "arrowvertex," as it is mapped only to a PUA Unicode
-# value in some fonts.
-#
-# 1.1 [17 April 2003]
-# - Renamed [Tt]cedilla back to [Tt]commaaccent.
-#
-# 1.0 [31 January 2003]
-# - Original version.
-# - Derived from the AGLv1.2 by:
-# removing the PUA area codes;
-# removing duplicate Unicode mappings; and
-# renaming "tcommaaccent" to "tcedilla" and "Tcommaaccent" to "Tcedilla"
-#
-0041;A;LATIN CAPITAL LETTER A
-00C6;AE;LATIN CAPITAL LETTER AE
-01FC;AEacute;LATIN CAPITAL LETTER AE WITH ACUTE
-00C1;Aacute;LATIN CAPITAL LETTER A WITH ACUTE
-0102;Abreve;LATIN CAPITAL LETTER A WITH BREVE
-00C2;Acircumflex;LATIN CAPITAL LETTER A WITH CIRCUMFLEX
-00C4;Adieresis;LATIN CAPITAL LETTER A WITH DIAERESIS
-00C0;Agrave;LATIN CAPITAL LETTER A WITH GRAVE
-0391;Alpha;GREEK CAPITAL LETTER ALPHA
-0386;Alphatonos;GREEK CAPITAL LETTER ALPHA WITH TONOS
-0100;Amacron;LATIN CAPITAL LETTER A WITH MACRON
-0104;Aogonek;LATIN CAPITAL LETTER A WITH OGONEK
-00C5;Aring;LATIN CAPITAL LETTER A WITH RING ABOVE
-01FA;Aringacute;LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE
-00C3;Atilde;LATIN CAPITAL LETTER A WITH TILDE
-0042;B;LATIN CAPITAL LETTER B
-0392;Beta;GREEK CAPITAL LETTER BETA
-0043;C;LATIN CAPITAL LETTER C
-0106;Cacute;LATIN CAPITAL LETTER C WITH ACUTE
-010C;Ccaron;LATIN CAPITAL LETTER C WITH CARON
-00C7;Ccedilla;LATIN CAPITAL LETTER C WITH CEDILLA
-0108;Ccircumflex;LATIN CAPITAL LETTER C WITH CIRCUMFLEX
-010A;Cdotaccent;LATIN CAPITAL LETTER C WITH DOT ABOVE
-03A7;Chi;GREEK CAPITAL LETTER CHI
-0044;D;LATIN CAPITAL LETTER D
-010E;Dcaron;LATIN CAPITAL LETTER D WITH CARON
-0110;Dcroat;LATIN CAPITAL LETTER D WITH STROKE
-2206;Delta;INCREMENT
-0045;E;LATIN CAPITAL LETTER E
-00C9;Eacute;LATIN CAPITAL LETTER E WITH ACUTE
-0114;Ebreve;LATIN CAPITAL LETTER E WITH BREVE
-011A;Ecaron;LATIN CAPITAL LETTER E WITH CARON
-00CA;Ecircumflex;LATIN CAPITAL LETTER E WITH CIRCUMFLEX
-00CB;Edieresis;LATIN CAPITAL LETTER E WITH DIAERESIS
-0116;Edotaccent;LATIN CAPITAL LETTER E WITH DOT ABOVE
-00C8;Egrave;LATIN CAPITAL LETTER E WITH GRAVE
-0112;Emacron;LATIN CAPITAL LETTER E WITH MACRON
-014A;Eng;LATIN CAPITAL LETTER ENG
-0118;Eogonek;LATIN CAPITAL LETTER E WITH OGONEK
-0395;Epsilon;GREEK CAPITAL LETTER EPSILON
-0388;Epsilontonos;GREEK CAPITAL LETTER EPSILON WITH TONOS
-0397;Eta;GREEK CAPITAL LETTER ETA
-0389;Etatonos;GREEK CAPITAL LETTER ETA WITH TONOS
-00D0;Eth;LATIN CAPITAL LETTER ETH
-20AC;Euro;EURO SIGN
-0046;F;LATIN CAPITAL LETTER F
-0047;G;LATIN CAPITAL LETTER G
-0393;Gamma;GREEK CAPITAL LETTER GAMMA
-011E;Gbreve;LATIN CAPITAL LETTER G WITH BREVE
-01E6;Gcaron;LATIN CAPITAL LETTER G WITH CARON
-011C;Gcircumflex;LATIN CAPITAL LETTER G WITH CIRCUMFLEX
-0120;Gdotaccent;LATIN CAPITAL LETTER G WITH DOT ABOVE
-0048;H;LATIN CAPITAL LETTER H
-25CF;H18533;BLACK CIRCLE
-25AA;H18543;BLACK SMALL SQUARE
-25AB;H18551;WHITE SMALL SQUARE
-25A1;H22073;WHITE SQUARE
-0126;Hbar;LATIN CAPITAL LETTER H WITH STROKE
-0124;Hcircumflex;LATIN CAPITAL LETTER H WITH CIRCUMFLEX
-0049;I;LATIN CAPITAL LETTER I
-0132;IJ;LATIN CAPITAL LIGATURE IJ
-00CD;Iacute;LATIN CAPITAL LETTER I WITH ACUTE
-012C;Ibreve;LATIN CAPITAL LETTER I WITH BREVE
-00CE;Icircumflex;LATIN CAPITAL LETTER I WITH CIRCUMFLEX
-00CF;Idieresis;LATIN CAPITAL LETTER I WITH DIAERESIS
-0130;Idotaccent;LATIN CAPITAL LETTER I WITH DOT ABOVE
-2111;Ifraktur;BLACK-LETTER CAPITAL I
-00CC;Igrave;LATIN CAPITAL LETTER I WITH GRAVE
-012A;Imacron;LATIN CAPITAL LETTER I WITH MACRON
-012E;Iogonek;LATIN CAPITAL LETTER I WITH OGONEK
-0399;Iota;GREEK CAPITAL LETTER IOTA
-03AA;Iotadieresis;GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
-038A;Iotatonos;GREEK CAPITAL LETTER IOTA WITH TONOS
-0128;Itilde;LATIN CAPITAL LETTER I WITH TILDE
-004A;J;LATIN CAPITAL LETTER J
-0134;Jcircumflex;LATIN CAPITAL LETTER J WITH CIRCUMFLEX
-004B;K;LATIN CAPITAL LETTER K
-039A;Kappa;GREEK CAPITAL LETTER KAPPA
-004C;L;LATIN CAPITAL LETTER L
-0139;Lacute;LATIN CAPITAL LETTER L WITH ACUTE
-039B;Lambda;GREEK CAPITAL LETTER LAMDA
-013D;Lcaron;LATIN CAPITAL LETTER L WITH CARON
-013F;Ldot;LATIN CAPITAL LETTER L WITH MIDDLE DOT
-0141;Lslash;LATIN CAPITAL LETTER L WITH STROKE
-004D;M;LATIN CAPITAL LETTER M
-039C;Mu;GREEK CAPITAL LETTER MU
-004E;N;LATIN CAPITAL LETTER N
-0143;Nacute;LATIN CAPITAL LETTER N WITH ACUTE
-0147;Ncaron;LATIN CAPITAL LETTER N WITH CARON
-00D1;Ntilde;LATIN CAPITAL LETTER N WITH TILDE
-039D;Nu;GREEK CAPITAL LETTER NU
-004F;O;LATIN CAPITAL LETTER O
-0152;OE;LATIN CAPITAL LIGATURE OE
-00D3;Oacute;LATIN CAPITAL LETTER O WITH ACUTE
-014E;Obreve;LATIN CAPITAL LETTER O WITH BREVE
-00D4;Ocircumflex;LATIN CAPITAL LETTER O WITH CIRCUMFLEX
-00D6;Odieresis;LATIN CAPITAL LETTER O WITH DIAERESIS
-00D2;Ograve;LATIN CAPITAL LETTER O WITH GRAVE
-01A0;Ohorn;LATIN CAPITAL LETTER O WITH HORN
-0150;Ohungarumlaut;LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
-014C;Omacron;LATIN CAPITAL LETTER O WITH MACRON
-2126;Omega;OHM SIGN
-038F;Omegatonos;GREEK CAPITAL LETTER OMEGA WITH TONOS
-039F;Omicron;GREEK CAPITAL LETTER OMICRON
-038C;Omicrontonos;GREEK CAPITAL LETTER OMICRON WITH TONOS
-00D8;Oslash;LATIN CAPITAL LETTER O WITH STROKE
-01FE;Oslashacute;LATIN CAPITAL LETTER O WITH STROKE AND ACUTE
-00D5;Otilde;LATIN CAPITAL LETTER O WITH TILDE
-0050;P;LATIN CAPITAL LETTER P
-03A6;Phi;GREEK CAPITAL LETTER PHI
-03A0;Pi;GREEK CAPITAL LETTER PI
-03A8;Psi;GREEK CAPITAL LETTER PSI
-0051;Q;LATIN CAPITAL LETTER Q
-0052;R;LATIN CAPITAL LETTER R
-0154;Racute;LATIN CAPITAL LETTER R WITH ACUTE
-0158;Rcaron;LATIN CAPITAL LETTER R WITH CARON
-211C;Rfraktur;BLACK-LETTER CAPITAL R
-03A1;Rho;GREEK CAPITAL LETTER RHO
-0053;S;LATIN CAPITAL LETTER S
-250C;SF010000;BOX DRAWINGS LIGHT DOWN AND RIGHT
-2514;SF020000;BOX DRAWINGS LIGHT UP AND RIGHT
-2510;SF030000;BOX DRAWINGS LIGHT DOWN AND LEFT
-2518;SF040000;BOX DRAWINGS LIGHT UP AND LEFT
-253C;SF050000;BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
-252C;SF060000;BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
-2534;SF070000;BOX DRAWINGS LIGHT UP AND HORIZONTAL
-251C;SF080000;BOX DRAWINGS LIGHT VERTICAL AND RIGHT
-2524;SF090000;BOX DRAWINGS LIGHT VERTICAL AND LEFT
-2500;SF100000;BOX DRAWINGS LIGHT HORIZONTAL
-2502;SF110000;BOX DRAWINGS LIGHT VERTICAL
-2561;SF190000;BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
-2562;SF200000;BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
-2556;SF210000;BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
-2555;SF220000;BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
-2563;SF230000;BOX DRAWINGS DOUBLE VERTICAL AND LEFT
-2551;SF240000;BOX DRAWINGS DOUBLE VERTICAL
-2557;SF250000;BOX DRAWINGS DOUBLE DOWN AND LEFT
-255D;SF260000;BOX DRAWINGS DOUBLE UP AND LEFT
-255C;SF270000;BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
-255B;SF280000;BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
-255E;SF360000;BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
-255F;SF370000;BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
-255A;SF380000;BOX DRAWINGS DOUBLE UP AND RIGHT
-2554;SF390000;BOX DRAWINGS DOUBLE DOWN AND RIGHT
-2569;SF400000;BOX DRAWINGS DOUBLE UP AND HORIZONTAL
-2566;SF410000;BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
-2560;SF420000;BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
-2550;SF430000;BOX DRAWINGS DOUBLE HORIZONTAL
-256C;SF440000;BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
-2567;SF450000;BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
-2568;SF460000;BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
-2564;SF470000;BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
-2565;SF480000;BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
-2559;SF490000;BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
-2558;SF500000;BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
-2552;SF510000;BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
-2553;SF520000;BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
-256B;SF530000;BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
-256A;SF540000;BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
-015A;Sacute;LATIN CAPITAL LETTER S WITH ACUTE
-0160;Scaron;LATIN CAPITAL LETTER S WITH CARON
-015E;Scedilla;LATIN CAPITAL LETTER S WITH CEDILLA
-015C;Scircumflex;LATIN CAPITAL LETTER S WITH CIRCUMFLEX
-03A3;Sigma;GREEK CAPITAL LETTER SIGMA
-0054;T;LATIN CAPITAL LETTER T
-03A4;Tau;GREEK CAPITAL LETTER TAU
-0166;Tbar;LATIN CAPITAL LETTER T WITH STROKE
-0164;Tcaron;LATIN CAPITAL LETTER T WITH CARON
-0398;Theta;GREEK CAPITAL LETTER THETA
-00DE;Thorn;LATIN CAPITAL LETTER THORN
-0055;U;LATIN CAPITAL LETTER U
-00DA;Uacute;LATIN CAPITAL LETTER U WITH ACUTE
-016C;Ubreve;LATIN CAPITAL LETTER U WITH BREVE
-00DB;Ucircumflex;LATIN CAPITAL LETTER U WITH CIRCUMFLEX
-00DC;Udieresis;LATIN CAPITAL LETTER U WITH DIAERESIS
-00D9;Ugrave;LATIN CAPITAL LETTER U WITH GRAVE
-01AF;Uhorn;LATIN CAPITAL LETTER U WITH HORN
-0170;Uhungarumlaut;LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
-016A;Umacron;LATIN CAPITAL LETTER U WITH MACRON
-0172;Uogonek;LATIN CAPITAL LETTER U WITH OGONEK
-03A5;Upsilon;GREEK CAPITAL LETTER UPSILON
-03D2;Upsilon1;GREEK UPSILON WITH HOOK SYMBOL
-03AB;Upsilondieresis;GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
-038E;Upsilontonos;GREEK CAPITAL LETTER UPSILON WITH TONOS
-016E;Uring;LATIN CAPITAL LETTER U WITH RING ABOVE
-0168;Utilde;LATIN CAPITAL LETTER U WITH TILDE
-0056;V;LATIN CAPITAL LETTER V
-0057;W;LATIN CAPITAL LETTER W
-1E82;Wacute;LATIN CAPITAL LETTER W WITH ACUTE
-0174;Wcircumflex;LATIN CAPITAL LETTER W WITH CIRCUMFLEX
-1E84;Wdieresis;LATIN CAPITAL LETTER W WITH DIAERESIS
-1E80;Wgrave;LATIN CAPITAL LETTER W WITH GRAVE
-0058;X;LATIN CAPITAL LETTER X
-039E;Xi;GREEK CAPITAL LETTER XI
-0059;Y;LATIN CAPITAL LETTER Y
-00DD;Yacute;LATIN CAPITAL LETTER Y WITH ACUTE
-0176;Ycircumflex;LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
-0178;Ydieresis;LATIN CAPITAL LETTER Y WITH DIAERESIS
-1EF2;Ygrave;LATIN CAPITAL LETTER Y WITH GRAVE
-005A;Z;LATIN CAPITAL LETTER Z
-0179;Zacute;LATIN CAPITAL LETTER Z WITH ACUTE
-017D;Zcaron;LATIN CAPITAL LETTER Z WITH CARON
-017B;Zdotaccent;LATIN CAPITAL LETTER Z WITH DOT ABOVE
-0396;Zeta;GREEK CAPITAL LETTER ZETA
-0061;a;LATIN SMALL LETTER A
-00E1;aacute;LATIN SMALL LETTER A WITH ACUTE
-0103;abreve;LATIN SMALL LETTER A WITH BREVE
-00E2;acircumflex;LATIN SMALL LETTER A WITH CIRCUMFLEX
-00B4;acute;ACUTE ACCENT
-0301;acutecomb;COMBINING ACUTE ACCENT
-00E4;adieresis;LATIN SMALL LETTER A WITH DIAERESIS
-00E6;ae;LATIN SMALL LETTER AE
-01FD;aeacute;LATIN SMALL LETTER AE WITH ACUTE
-00E0;agrave;LATIN SMALL LETTER A WITH GRAVE
-2135;aleph;ALEF SYMBOL
-03B1;alpha;GREEK SMALL LETTER ALPHA
-03AC;alphatonos;GREEK SMALL LETTER ALPHA WITH TONOS
-0101;amacron;LATIN SMALL LETTER A WITH MACRON
-0026;ampersand;AMPERSAND
-2220;angle;ANGLE
-2329;angleleft;LEFT-POINTING ANGLE BRACKET
-232A;angleright;RIGHT-POINTING ANGLE BRACKET
-0387;anoteleia;GREEK ANO TELEIA
-0105;aogonek;LATIN SMALL LETTER A WITH OGONEK
-2248;approxequal;ALMOST EQUAL TO
-00E5;aring;LATIN SMALL LETTER A WITH RING ABOVE
-01FB;aringacute;LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE
-2194;arrowboth;LEFT RIGHT ARROW
-21D4;arrowdblboth;LEFT RIGHT DOUBLE ARROW
-21D3;arrowdbldown;DOWNWARDS DOUBLE ARROW
-21D0;arrowdblleft;LEFTWARDS DOUBLE ARROW
-21D2;arrowdblright;RIGHTWARDS DOUBLE ARROW
-21D1;arrowdblup;UPWARDS DOUBLE ARROW
-2193;arrowdown;DOWNWARDS ARROW
-2190;arrowleft;LEFTWARDS ARROW
-2192;arrowright;RIGHTWARDS ARROW
-2191;arrowup;UPWARDS ARROW
-2195;arrowupdn;UP DOWN ARROW
-21A8;arrowupdnbse;UP DOWN ARROW WITH BASE
-005E;asciicircum;CIRCUMFLEX ACCENT
-007E;asciitilde;TILDE
-002A;asterisk;ASTERISK
-2217;asteriskmath;ASTERISK OPERATOR
-0040;at;COMMERCIAL AT
-00E3;atilde;LATIN SMALL LETTER A WITH TILDE
-0062;b;LATIN SMALL LETTER B
-005C;backslash;REVERSE SOLIDUS
-007C;bar;VERTICAL LINE
-03B2;beta;GREEK SMALL LETTER BETA
-2588;block;FULL BLOCK
-007B;braceleft;LEFT CURLY BRACKET
-007D;braceright;RIGHT CURLY BRACKET
-005B;bracketleft;LEFT SQUARE BRACKET
-005D;bracketright;RIGHT SQUARE BRACKET
-02D8;breve;BREVE
-00A6;brokenbar;BROKEN BAR
-2022;bullet;BULLET
-0063;c;LATIN SMALL LETTER C
-0107;cacute;LATIN SMALL LETTER C WITH ACUTE
-02C7;caron;CARON
-21B5;carriagereturn;DOWNWARDS ARROW WITH CORNER LEFTWARDS
-010D;ccaron;LATIN SMALL LETTER C WITH CARON
-00E7;ccedilla;LATIN SMALL LETTER C WITH CEDILLA
-0109;ccircumflex;LATIN SMALL LETTER C WITH CIRCUMFLEX
-010B;cdotaccent;LATIN SMALL LETTER C WITH DOT ABOVE
-00B8;cedilla;CEDILLA
-00A2;cent;CENT SIGN
-03C7;chi;GREEK SMALL LETTER CHI
-25CB;circle;WHITE CIRCLE
-2297;circlemultiply;CIRCLED TIMES
-2295;circleplus;CIRCLED PLUS
-02C6;circumflex;MODIFIER LETTER CIRCUMFLEX ACCENT
-2663;club;BLACK CLUB SUIT
-003A;colon;COLON
-20A1;colonmonetary;COLON SIGN
-002C;comma;COMMA
-2245;congruent;APPROXIMATELY EQUAL TO
-00A9;copyright;COPYRIGHT SIGN
-00A4;currency;CURRENCY SIGN
-0064;d;LATIN SMALL LETTER D
-2020;dagger;DAGGER
-2021;daggerdbl;DOUBLE DAGGER
-010F;dcaron;LATIN SMALL LETTER D WITH CARON
-0111;dcroat;LATIN SMALL LETTER D WITH STROKE
-00B0;degree;DEGREE SIGN
-03B4;delta;GREEK SMALL LETTER DELTA
-2666;diamond;BLACK DIAMOND SUIT
-00A8;dieresis;DIAERESIS
-0385;dieresistonos;GREEK DIALYTIKA TONOS
-00F7;divide;DIVISION SIGN
-2593;dkshade;DARK SHADE
-2584;dnblock;LOWER HALF BLOCK
-0024;dollar;DOLLAR SIGN
-20AB;dong;DONG SIGN
-02D9;dotaccent;DOT ABOVE
-0323;dotbelowcomb;COMBINING DOT BELOW
-0131;dotlessi;LATIN SMALL LETTER DOTLESS I
-22C5;dotmath;DOT OPERATOR
-0065;e;LATIN SMALL LETTER E
-00E9;eacute;LATIN SMALL LETTER E WITH ACUTE
-0115;ebreve;LATIN SMALL LETTER E WITH BREVE
-011B;ecaron;LATIN SMALL LETTER E WITH CARON
-00EA;ecircumflex;LATIN SMALL LETTER E WITH CIRCUMFLEX
-00EB;edieresis;LATIN SMALL LETTER E WITH DIAERESIS
-0117;edotaccent;LATIN SMALL LETTER E WITH DOT ABOVE
-00E8;egrave;LATIN SMALL LETTER E WITH GRAVE
-0038;eight;DIGIT EIGHT
-2208;element;ELEMENT OF
-2026;ellipsis;HORIZONTAL ELLIPSIS
-0113;emacron;LATIN SMALL LETTER E WITH MACRON
-2014;emdash;EM DASH
-2205;emptyset;EMPTY SET
-2013;endash;EN DASH
-014B;eng;LATIN SMALL LETTER ENG
-0119;eogonek;LATIN SMALL LETTER E WITH OGONEK
-03B5;epsilon;GREEK SMALL LETTER EPSILON
-03AD;epsilontonos;GREEK SMALL LETTER EPSILON WITH TONOS
-003D;equal;EQUALS SIGN
-2261;equivalence;IDENTICAL TO
-212E;estimated;ESTIMATED SYMBOL
-03B7;eta;GREEK SMALL LETTER ETA
-03AE;etatonos;GREEK SMALL LETTER ETA WITH TONOS
-00F0;eth;LATIN SMALL LETTER ETH
-0021;exclam;EXCLAMATION MARK
-203C;exclamdbl;DOUBLE EXCLAMATION MARK
-00A1;exclamdown;INVERTED EXCLAMATION MARK
-2203;existential;THERE EXISTS
-0066;f;LATIN SMALL LETTER F
-2640;female;FEMALE SIGN
-2012;figuredash;FIGURE DASH
-25A0;filledbox;BLACK SQUARE
-25AC;filledrect;BLACK RECTANGLE
-0035;five;DIGIT FIVE
-215D;fiveeighths;VULGAR FRACTION FIVE EIGHTHS
-0192;florin;LATIN SMALL LETTER F WITH HOOK
-0034;four;DIGIT FOUR
-2044;fraction;FRACTION SLASH
-20A3;franc;FRENCH FRANC SIGN
-0067;g;LATIN SMALL LETTER G
-03B3;gamma;GREEK SMALL LETTER GAMMA
-011F;gbreve;LATIN SMALL LETTER G WITH BREVE
-01E7;gcaron;LATIN SMALL LETTER G WITH CARON
-011D;gcircumflex;LATIN SMALL LETTER G WITH CIRCUMFLEX
-0121;gdotaccent;LATIN SMALL LETTER G WITH DOT ABOVE
-00DF;germandbls;LATIN SMALL LETTER SHARP S
-2207;gradient;NABLA
-0060;grave;GRAVE ACCENT
-0300;gravecomb;COMBINING GRAVE ACCENT
-003E;greater;GREATER-THAN SIGN
-2265;greaterequal;GREATER-THAN OR EQUAL TO
-00AB;guillemotleft;LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
-00BB;guillemotright;RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
-2039;guilsinglleft;SINGLE LEFT-POINTING ANGLE QUOTATION MARK
-203A;guilsinglright;SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
-0068;h;LATIN SMALL LETTER H
-0127;hbar;LATIN SMALL LETTER H WITH STROKE
-0125;hcircumflex;LATIN SMALL LETTER H WITH CIRCUMFLEX
-2665;heart;BLACK HEART SUIT
-0309;hookabovecomb;COMBINING HOOK ABOVE
-2302;house;HOUSE
-02DD;hungarumlaut;DOUBLE ACUTE ACCENT
-002D;hyphen;HYPHEN-MINUS
-0069;i;LATIN SMALL LETTER I
-00ED;iacute;LATIN SMALL LETTER I WITH ACUTE
-012D;ibreve;LATIN SMALL LETTER I WITH BREVE
-00EE;icircumflex;LATIN SMALL LETTER I WITH CIRCUMFLEX
-00EF;idieresis;LATIN SMALL LETTER I WITH DIAERESIS
-00EC;igrave;LATIN SMALL LETTER I WITH GRAVE
-0133;ij;LATIN SMALL LIGATURE IJ
-012B;imacron;LATIN SMALL LETTER I WITH MACRON
-221E;infinity;INFINITY
-222B;integral;INTEGRAL
-2321;integralbt;BOTTOM HALF INTEGRAL
-2320;integraltp;TOP HALF INTEGRAL
-2229;intersection;INTERSECTION
-25D8;invbullet;INVERSE BULLET
-25D9;invcircle;INVERSE WHITE CIRCLE
-263B;invsmileface;BLACK SMILING FACE
-012F;iogonek;LATIN SMALL LETTER I WITH OGONEK
-03B9;iota;GREEK SMALL LETTER IOTA
-03CA;iotadieresis;GREEK SMALL LETTER IOTA WITH DIALYTIKA
-0390;iotadieresistonos;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
-03AF;iotatonos;GREEK SMALL LETTER IOTA WITH TONOS
-0129;itilde;LATIN SMALL LETTER I WITH TILDE
-006A;j;LATIN SMALL LETTER J
-0135;jcircumflex;LATIN SMALL LETTER J WITH CIRCUMFLEX
-006B;k;LATIN SMALL LETTER K
-03BA;kappa;GREEK SMALL LETTER KAPPA
-0138;kgreenlandic;LATIN SMALL LETTER KRA
-006C;l;LATIN SMALL LETTER L
-013A;lacute;LATIN SMALL LETTER L WITH ACUTE
-03BB;lambda;GREEK SMALL LETTER LAMDA
-013E;lcaron;LATIN SMALL LETTER L WITH CARON
-0140;ldot;LATIN SMALL LETTER L WITH MIDDLE DOT
-003C;less;LESS-THAN SIGN
-2264;lessequal;LESS-THAN OR EQUAL TO
-258C;lfblock;LEFT HALF BLOCK
-20A4;lira;LIRA SIGN
-2227;logicaland;LOGICAL AND
-00AC;logicalnot;NOT SIGN
-2228;logicalor;LOGICAL OR
-017F;longs;LATIN SMALL LETTER LONG S
-25CA;lozenge;LOZENGE
-0142;lslash;LATIN SMALL LETTER L WITH STROKE
-2591;ltshade;LIGHT SHADE
-006D;m;LATIN SMALL LETTER M
-00AF;macron;MACRON
-2642;male;MALE SIGN
-2212;minus;MINUS SIGN
-2032;minute;PRIME
-00B5;mu;MICRO SIGN
-00D7;multiply;MULTIPLICATION SIGN
-266A;musicalnote;EIGHTH NOTE
-266B;musicalnotedbl;BEAMED EIGHTH NOTES
-006E;n;LATIN SMALL LETTER N
-0144;nacute;LATIN SMALL LETTER N WITH ACUTE
-0149;napostrophe;LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
-0148;ncaron;LATIN SMALL LETTER N WITH CARON
-0039;nine;DIGIT NINE
-2209;notelement;NOT AN ELEMENT OF
-2260;notequal;NOT EQUAL TO
-2284;notsubset;NOT A SUBSET OF
-00F1;ntilde;LATIN SMALL LETTER N WITH TILDE
-03BD;nu;GREEK SMALL LETTER NU
-0023;numbersign;NUMBER SIGN
-006F;o;LATIN SMALL LETTER O
-00F3;oacute;LATIN SMALL LETTER O WITH ACUTE
-014F;obreve;LATIN SMALL LETTER O WITH BREVE
-00F4;ocircumflex;LATIN SMALL LETTER O WITH CIRCUMFLEX
-00F6;odieresis;LATIN SMALL LETTER O WITH DIAERESIS
-0153;oe;LATIN SMALL LIGATURE OE
-02DB;ogonek;OGONEK
-00F2;ograve;LATIN SMALL LETTER O WITH GRAVE
-01A1;ohorn;LATIN SMALL LETTER O WITH HORN
-0151;ohungarumlaut;LATIN SMALL LETTER O WITH DOUBLE ACUTE
-014D;omacron;LATIN SMALL LETTER O WITH MACRON
-03C9;omega;GREEK SMALL LETTER OMEGA
-03D6;omega1;GREEK PI SYMBOL
-03CE;omegatonos;GREEK SMALL LETTER OMEGA WITH TONOS
-03BF;omicron;GREEK SMALL LETTER OMICRON
-03CC;omicrontonos;GREEK SMALL LETTER OMICRON WITH TONOS
-0031;one;DIGIT ONE
-2024;onedotenleader;ONE DOT LEADER
-215B;oneeighth;VULGAR FRACTION ONE EIGHTH
-00BD;onehalf;VULGAR FRACTION ONE HALF
-00BC;onequarter;VULGAR FRACTION ONE QUARTER
-2153;onethird;VULGAR FRACTION ONE THIRD
-25E6;openbullet;WHITE BULLET
-00AA;ordfeminine;FEMININE ORDINAL INDICATOR
-00BA;ordmasculine;MASCULINE ORDINAL INDICATOR
-221F;orthogonal;RIGHT ANGLE
-00F8;oslash;LATIN SMALL LETTER O WITH STROKE
-01FF;oslashacute;LATIN SMALL LETTER O WITH STROKE AND ACUTE
-00F5;otilde;LATIN SMALL LETTER O WITH TILDE
-0070;p;LATIN SMALL LETTER P
-00B6;paragraph;PILCROW SIGN
-0028;parenleft;LEFT PARENTHESIS
-0029;parenright;RIGHT PARENTHESIS
-2202;partialdiff;PARTIAL DIFFERENTIAL
-0025;percent;PERCENT SIGN
-002E;period;FULL STOP
-00B7;periodcentered;MIDDLE DOT
-22A5;perpendicular;UP TACK
-2030;perthousand;PER MILLE SIGN
-20A7;peseta;PESETA SIGN
-03C6;phi;GREEK SMALL LETTER PHI
-03D5;phi1;GREEK PHI SYMBOL
-03C0;pi;GREEK SMALL LETTER PI
-002B;plus;PLUS SIGN
-00B1;plusminus;PLUS-MINUS SIGN
-211E;prescription;PRESCRIPTION TAKE
-220F;product;N-ARY PRODUCT
-2282;propersubset;SUBSET OF
-2283;propersuperset;SUPERSET OF
-221D;proportional;PROPORTIONAL TO
-03C8;psi;GREEK SMALL LETTER PSI
-0071;q;LATIN SMALL LETTER Q
-003F;question;QUESTION MARK
-00BF;questiondown;INVERTED QUESTION MARK
-0022;quotedbl;QUOTATION MARK
-201E;quotedblbase;DOUBLE LOW-9 QUOTATION MARK
-201C;quotedblleft;LEFT DOUBLE QUOTATION MARK
-201D;quotedblright;RIGHT DOUBLE QUOTATION MARK
-2018;quoteleft;LEFT SINGLE QUOTATION MARK
-201B;quotereversed;SINGLE HIGH-REVERSED-9 QUOTATION MARK
-2019;quoteright;RIGHT SINGLE QUOTATION MARK
-201A;quotesinglbase;SINGLE LOW-9 QUOTATION MARK
-0027;quotesingle;APOSTROPHE
-0072;r;LATIN SMALL LETTER R
-0155;racute;LATIN SMALL LETTER R WITH ACUTE
-221A;radical;SQUARE ROOT
-0159;rcaron;LATIN SMALL LETTER R WITH CARON
-2286;reflexsubset;SUBSET OF OR EQUAL TO
-2287;reflexsuperset;SUPERSET OF OR EQUAL TO
-00AE;registered;REGISTERED SIGN
-2310;revlogicalnot;REVERSED NOT SIGN
-03C1;rho;GREEK SMALL LETTER RHO
-02DA;ring;RING ABOVE
-2590;rtblock;RIGHT HALF BLOCK
-0073;s;LATIN SMALL LETTER S
-015B;sacute;LATIN SMALL LETTER S WITH ACUTE
-0161;scaron;LATIN SMALL LETTER S WITH CARON
-015F;scedilla;LATIN SMALL LETTER S WITH CEDILLA
-015D;scircumflex;LATIN SMALL LETTER S WITH CIRCUMFLEX
-2033;second;DOUBLE PRIME
-00A7;section;SECTION SIGN
-003B;semicolon;SEMICOLON
-0037;seven;DIGIT SEVEN
-215E;seveneighths;VULGAR FRACTION SEVEN EIGHTHS
-2592;shade;MEDIUM SHADE
-03C3;sigma;GREEK SMALL LETTER SIGMA
-03C2;sigma1;GREEK SMALL LETTER FINAL SIGMA
-223C;similar;TILDE OPERATOR
-0036;six;DIGIT SIX
-002F;slash;SOLIDUS
-263A;smileface;WHITE SMILING FACE
-0020;space;SPACE
-2660;spade;BLACK SPADE SUIT
-00A3;sterling;POUND SIGN
-220B;suchthat;CONTAINS AS MEMBER
-2211;summation;N-ARY SUMMATION
-263C;sun;WHITE SUN WITH RAYS
-0074;t;LATIN SMALL LETTER T
-03C4;tau;GREEK SMALL LETTER TAU
-0167;tbar;LATIN SMALL LETTER T WITH STROKE
-0165;tcaron;LATIN SMALL LETTER T WITH CARON
-2234;therefore;THEREFORE
-03B8;theta;GREEK SMALL LETTER THETA
-03D1;theta1;GREEK THETA SYMBOL
-00FE;thorn;LATIN SMALL LETTER THORN
-0033;three;DIGIT THREE
-215C;threeeighths;VULGAR FRACTION THREE EIGHTHS
-00BE;threequarters;VULGAR FRACTION THREE QUARTERS
-02DC;tilde;SMALL TILDE
-0303;tildecomb;COMBINING TILDE
-0384;tonos;GREEK TONOS
-2122;trademark;TRADE MARK SIGN
-25BC;triagdn;BLACK DOWN-POINTING TRIANGLE
-25C4;triaglf;BLACK LEFT-POINTING POINTER
-25BA;triagrt;BLACK RIGHT-POINTING POINTER
-25B2;triagup;BLACK UP-POINTING TRIANGLE
-0032;two;DIGIT TWO
-2025;twodotenleader;TWO DOT LEADER
-2154;twothirds;VULGAR FRACTION TWO THIRDS
-0075;u;LATIN SMALL LETTER U
-00FA;uacute;LATIN SMALL LETTER U WITH ACUTE
-016D;ubreve;LATIN SMALL LETTER U WITH BREVE
-00FB;ucircumflex;LATIN SMALL LETTER U WITH CIRCUMFLEX
-00FC;udieresis;LATIN SMALL LETTER U WITH DIAERESIS
-00F9;ugrave;LATIN SMALL LETTER U WITH GRAVE
-01B0;uhorn;LATIN SMALL LETTER U WITH HORN
-0171;uhungarumlaut;LATIN SMALL LETTER U WITH DOUBLE ACUTE
-016B;umacron;LATIN SMALL LETTER U WITH MACRON
-005F;underscore;LOW LINE
-2017;underscoredbl;DOUBLE LOW LINE
-222A;union;UNION
-2200;universal;FOR ALL
-0173;uogonek;LATIN SMALL LETTER U WITH OGONEK
-2580;upblock;UPPER HALF BLOCK
-03C5;upsilon;GREEK SMALL LETTER UPSILON
-03CB;upsilondieresis;GREEK SMALL LETTER UPSILON WITH DIALYTIKA
-03B0;upsilondieresistonos;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
-03CD;upsilontonos;GREEK SMALL LETTER UPSILON WITH TONOS
-016F;uring;LATIN SMALL LETTER U WITH RING ABOVE
-0169;utilde;LATIN SMALL LETTER U WITH TILDE
-0076;v;LATIN SMALL LETTER V
-0077;w;LATIN SMALL LETTER W
-1E83;wacute;LATIN SMALL LETTER W WITH ACUTE
-0175;wcircumflex;LATIN SMALL LETTER W WITH CIRCUMFLEX
-1E85;wdieresis;LATIN SMALL LETTER W WITH DIAERESIS
-2118;weierstrass;SCRIPT CAPITAL P
-1E81;wgrave;LATIN SMALL LETTER W WITH GRAVE
-0078;x;LATIN SMALL LETTER X
-03BE;xi;GREEK SMALL LETTER XI
-0079;y;LATIN SMALL LETTER Y
-00FD;yacute;LATIN SMALL LETTER Y WITH ACUTE
-0177;ycircumflex;LATIN SMALL LETTER Y WITH CIRCUMFLEX
-00FF;ydieresis;LATIN SMALL LETTER Y WITH DIAERESIS
-00A5;yen;YEN SIGN
-1EF3;ygrave;LATIN SMALL LETTER Y WITH GRAVE
-007A;z;LATIN SMALL LETTER Z
-017A;zacute;LATIN SMALL LETTER Z WITH ACUTE
-017E;zcaron;LATIN SMALL LETTER Z WITH CARON
-017C;zdotaccent;LATIN SMALL LETTER Z WITH DOT ABOVE
-0030;zero;DIGIT ZERO
-03B6;zeta;GREEK SMALL LETTER ZETA
-# END
-"""
-
-
-class AGLError(Exception):
- pass
-
-
-LEGACY_AGL2UV = {}
-AGL2UV = {}
-UV2AGL = {}
-
-
-def _builddicts():
- import re
-
- lines = _aglText.splitlines()
-
- parseAGL_RE = re.compile("([A-Za-z0-9]+);((?:[0-9A-F]{4})(?: (?:[0-9A-F]{4}))*)$")
-
- for line in lines:
- if not line or line[:1] == "#":
- continue
- m = parseAGL_RE.match(line)
- if not m:
- raise AGLError("syntax error in glyphlist.txt: %s" % repr(line[:20]))
- unicodes = m.group(2)
- assert len(unicodes) % 5 == 4
- unicodes = [int(unicode, 16) for unicode in unicodes.split()]
- glyphName = tostr(m.group(1))
- LEGACY_AGL2UV[glyphName] = unicodes
-
- lines = _aglfnText.splitlines()
-
- parseAGLFN_RE = re.compile("([0-9A-F]{4});([A-Za-z0-9]+);.*?$")
-
- for line in lines:
- if not line or line[:1] == "#":
- continue
- m = parseAGLFN_RE.match(line)
- if not m:
- raise AGLError("syntax error in aglfn.txt: %s" % repr(line[:20]))
- unicode = m.group(1)
- assert len(unicode) == 4
- unicode = int(unicode, 16)
- glyphName = tostr(m.group(2))
- AGL2UV[glyphName] = unicode
- UV2AGL[unicode] = glyphName
-
-
-_builddicts()
-
-
-def toUnicode(glyph, isZapfDingbats=False):
- """Convert glyph names to Unicode, such as ``'longs_t.oldstyle'`` --> ``u'ſt'``
-
- If ``isZapfDingbats`` is ``True``, the implementation recognizes additional
- glyph names (as required by the AGL specification).
- """
- # https://github.com/adobe-type-tools/agl-specification#2-the-mapping
- #
- # 1. Drop all the characters from the glyph name starting with
- # the first occurrence of a period (U+002E; FULL STOP), if any.
- glyph = glyph.split(".", 1)[0]
-
- # 2. Split the remaining string into a sequence of components,
- # using underscore (U+005F; LOW LINE) as the delimiter.
- components = glyph.split("_")
-
- # 3. Map each component to a character string according to the
- # procedure below, and concatenate those strings; the result
- # is the character string to which the glyph name is mapped.
- result = [_glyphComponentToUnicode(c, isZapfDingbats) for c in components]
- return "".join(result)
-
-
-def _glyphComponentToUnicode(component, isZapfDingbats):
- # If the font is Zapf Dingbats (PostScript FontName: ZapfDingbats),
- # and the component is in the ITC Zapf Dingbats Glyph List, then
- # map it to the corresponding character in that list.
- dingbat = _zapfDingbatsToUnicode(component) if isZapfDingbats else None
- if dingbat:
- return dingbat
-
- # Otherwise, if the component is in AGL, then map it
- # to the corresponding character in that list.
- uchars = LEGACY_AGL2UV.get(component)
- if uchars:
- return "".join(map(chr, uchars))
-
- # Otherwise, if the component is of the form "uni" (U+0075,
- # U+006E, and U+0069) followed by a sequence of uppercase
- # hexadecimal digits (0–9 and A–F, meaning U+0030 through
- # U+0039 and U+0041 through U+0046), if the length of that
- # sequence is a multiple of four, and if each group of four
- # digits represents a value in the ranges 0000 through D7FF
- # or E000 through FFFF, then interpret each as a Unicode scalar
- # value and map the component to the string made of those
- # scalar values. Note that the range and digit-length
- # restrictions mean that the "uni" glyph name prefix can be
- # used only with UVs in the Basic Multilingual Plane (BMP).
- uni = _uniToUnicode(component)
- if uni:
- return uni
-
- # Otherwise, if the component is of the form "u" (U+0075)
- # followed by a sequence of four to six uppercase hexadecimal
- # digits (0–9 and A–F, meaning U+0030 through U+0039 and
- # U+0041 through U+0046), and those digits represents a value
- # in the ranges 0000 through D7FF or E000 through 10FFFF, then
- # interpret it as a Unicode scalar value and map the component
- # to the string made of this scalar value.
- uni = _uToUnicode(component)
- if uni:
- return uni
-
- # Otherwise, map the component to an empty string.
- return ""
-
-
-# https://github.com/adobe-type-tools/agl-aglfn/blob/master/zapfdingbats.txt
-_AGL_ZAPF_DINGBATS = (
- " ✁✂✄☎✆✝✞✟✠✡☛☞✌✍✎✏✑✒✓✔✕✖✗✘✙✚✛✜✢✣✤✥✦✧★✩✪✫✬✭✮✯✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀"
- "❁❂❃❄❅❆❇❈❉❊❋●❍■❏❑▲▼◆❖ ◗❘❙❚❯❱❲❳❨❩❬❭❪❫❴❵❛❜❝❞❡❢❣❤✐❥❦❧♠♥♦♣ ✉✈✇"
- "①②③④⑤⑥⑦⑧⑨⑩❶❷❸❹❺❻❼❽❾❿➀➁➂➃➄➅➆➇➈➉➊➋➌➍➎➏➐➑➒➓➔→➣↔"
- "↕➙➛➜➝➞➟➠➡➢➤➥➦➧➨➩➫➭➯➲➳➵➸➺➻➼➽➾➚➪➶➹➘➴➷➬➮➱✃❐❒❮❰"
-)
-
-
-def _zapfDingbatsToUnicode(glyph):
- """Helper for toUnicode()."""
- if len(glyph) < 2 or glyph[0] != "a":
- return None
- try:
- gid = int(glyph[1:])
- except ValueError:
- return None
- if gid < 0 or gid >= len(_AGL_ZAPF_DINGBATS):
- return None
- uchar = _AGL_ZAPF_DINGBATS[gid]
- return uchar if uchar != " " else None
-
-
-_re_uni = re.compile("^uni([0-9A-F]+)$")
-
-
-def _uniToUnicode(component):
- """Helper for toUnicode() to handle "uniABCD" components."""
- match = _re_uni.match(component)
- if match is None:
- return None
- digits = match.group(1)
- if len(digits) % 4 != 0:
- return None
- chars = [int(digits[i : i + 4], 16) for i in range(0, len(digits), 4)]
- if any(c >= 0xD800 and c <= 0xDFFF for c in chars):
- # The AGL specification explicitly excluded surrogate pairs.
- return None
- return "".join([chr(c) for c in chars])
-
-
-_re_u = re.compile("^u([0-9A-F]{4,6})$")
-
-
-def _uToUnicode(component):
- """Helper for toUnicode() to handle "u1ABCD" components."""
- match = _re_u.match(component)
- if match is None:
- return None
- digits = match.group(1)
- try:
- value = int(digits, 16)
- except ValueError:
- return None
- if (value >= 0x0000 and value <= 0xD7FF) or (value >= 0xE000 and value <= 0x10FFFF):
- return chr(value)
- return None
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cffLib/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cffLib/__init__.py
deleted file mode 100644
index bb62673..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cffLib/__init__.py
+++ /dev/null
@@ -1,3830 +0,0 @@
-"""cffLib: read/write Adobe CFF fonts
-
-OpenType fonts with PostScript outlines contain a completely independent
-font file, Adobe's *Compact Font Format*. So dealing with OpenType fonts
-requires also dealing with CFF. This module allows you to read and write
-fonts written in the CFF format.
-
-In 2016, OpenType 1.8 introduced the `CFF2 `_
-format which, along with other changes, extended the CFF format to deal with
-the demands of variable fonts. This module parses both original CFF and CFF2.
-
-"""
-
-from fontTools.misc import sstruct
-from fontTools.misc import psCharStrings
-from fontTools.misc.arrayTools import unionRect, intRect
-from fontTools.misc.textTools import (
- bytechr,
- byteord,
- bytesjoin,
- tobytes,
- tostr,
- safeEval,
-)
-from fontTools.ttLib import TTFont
-from fontTools.ttLib.tables.otBase import OTTableWriter
-from fontTools.ttLib.tables.otBase import OTTableReader
-from fontTools.ttLib.tables import otTables as ot
-from io import BytesIO
-import struct
-import logging
-import re
-
-# mute cffLib debug messages when running ttx in verbose mode
-DEBUG = logging.DEBUG - 1
-log = logging.getLogger(__name__)
-
-cffHeaderFormat = """
- major: B
- minor: B
- hdrSize: B
-"""
-
-maxStackLimit = 513
-# maxstack operator has been deprecated. max stack is now always 513.
-
-
-class StopHintCountEvent(Exception):
- pass
-
-
-class _DesubroutinizingT2Decompiler(psCharStrings.SimpleT2Decompiler):
- stop_hintcount_ops = (
- "op_hintmask",
- "op_cntrmask",
- "op_rmoveto",
- "op_hmoveto",
- "op_vmoveto",
- )
-
- def __init__(self, localSubrs, globalSubrs, private=None):
- psCharStrings.SimpleT2Decompiler.__init__(
- self, localSubrs, globalSubrs, private
- )
-
- def execute(self, charString):
- self.need_hintcount = True # until proven otherwise
- for op_name in self.stop_hintcount_ops:
- setattr(self, op_name, self.stop_hint_count)
-
- if hasattr(charString, "_desubroutinized"):
- # If a charstring has already been desubroutinized, we will still
- # need to execute it if we need to count hints in order to
- # compute the byte length for mask arguments, and haven't finished
- # counting hints pairs.
- if self.need_hintcount and self.callingStack:
- try:
- psCharStrings.SimpleT2Decompiler.execute(self, charString)
- except StopHintCountEvent:
- del self.callingStack[-1]
- return
-
- charString._patches = []
- psCharStrings.SimpleT2Decompiler.execute(self, charString)
- desubroutinized = charString.program[:]
- for idx, expansion in reversed(charString._patches):
- assert idx >= 2
- assert desubroutinized[idx - 1] in [
- "callsubr",
- "callgsubr",
- ], desubroutinized[idx - 1]
- assert type(desubroutinized[idx - 2]) == int
- if expansion[-1] == "return":
- expansion = expansion[:-1]
- desubroutinized[idx - 2 : idx] = expansion
- if not self.private.in_cff2:
- if "endchar" in desubroutinized:
- # Cut off after first endchar
- desubroutinized = desubroutinized[
- : desubroutinized.index("endchar") + 1
- ]
- else:
- if not len(desubroutinized) or desubroutinized[-1] != "return":
- desubroutinized.append("return")
-
- charString._desubroutinized = desubroutinized
- del charString._patches
-
- def op_callsubr(self, index):
- subr = self.localSubrs[self.operandStack[-1] + self.localBias]
- psCharStrings.SimpleT2Decompiler.op_callsubr(self, index)
- self.processSubr(index, subr)
-
- def op_callgsubr(self, index):
- subr = self.globalSubrs[self.operandStack[-1] + self.globalBias]
- psCharStrings.SimpleT2Decompiler.op_callgsubr(self, index)
- self.processSubr(index, subr)
-
- def stop_hint_count(self, *args):
- self.need_hintcount = False
- for op_name in self.stop_hintcount_ops:
- setattr(self, op_name, None)
- cs = self.callingStack[-1]
- if hasattr(cs, "_desubroutinized"):
- raise StopHintCountEvent()
-
- def op_hintmask(self, index):
- psCharStrings.SimpleT2Decompiler.op_hintmask(self, index)
- if self.need_hintcount:
- self.stop_hint_count()
-
- def processSubr(self, index, subr):
- cs = self.callingStack[-1]
- if not hasattr(cs, "_desubroutinized"):
- cs._patches.append((index, subr._desubroutinized))
-
-
-class CFFFontSet(object):
- """A CFF font "file" can contain more than one font, although this is
- extremely rare (and not allowed within OpenType fonts).
-
- This class is the entry point for parsing a CFF table. To actually
- manipulate the data inside the CFF font, you will want to access the
- ``CFFFontSet``'s :class:`TopDict` object. To do this, a ``CFFFontSet``
- object can either be treated as a dictionary (with appropriate
- ``keys()`` and ``values()`` methods) mapping font names to :class:`TopDict`
- objects, or as a list.
-
- .. code:: python
-
- from fontTools import ttLib
- tt = ttLib.TTFont("Tests/cffLib/data/LinLibertine_RBI.otf")
- tt["CFF "].cff
- #
- tt["CFF "].cff[0] # Here's your actual font data
- #
-
- """
-
- def decompile(self, file, otFont, isCFF2=None):
- """Parse a binary CFF file into an internal representation. ``file``
- should be a file handle object. ``otFont`` is the top-level
- :py:class:`fontTools.ttLib.ttFont.TTFont` object containing this CFF file.
-
- If ``isCFF2`` is passed and set to ``True`` or ``False``, then the
- library makes an assertion that the CFF header is of the appropriate
- version.
- """
-
- self.otFont = otFont
- sstruct.unpack(cffHeaderFormat, file.read(3), self)
- if isCFF2 is not None:
- # called from ttLib: assert 'major' as read from file matches the
- # expected version
- expected_major = 2 if isCFF2 else 1
- if self.major != expected_major:
- raise ValueError(
- "Invalid CFF 'major' version: expected %d, found %d"
- % (expected_major, self.major)
- )
- else:
- # use 'major' version from file to determine if isCFF2
- assert self.major in (1, 2), "Unknown CFF format"
- isCFF2 = self.major == 2
- if not isCFF2:
- self.offSize = struct.unpack("B", file.read(1))[0]
- file.seek(self.hdrSize)
- self.fontNames = list(tostr(s) for s in Index(file, isCFF2=isCFF2))
- self.topDictIndex = TopDictIndex(file, isCFF2=isCFF2)
- self.strings = IndexedStrings(file)
- else: # isCFF2
- self.topDictSize = struct.unpack(">H", file.read(2))[0]
- file.seek(self.hdrSize)
- self.fontNames = ["CFF2Font"]
- cff2GetGlyphOrder = otFont.getGlyphOrder
- # in CFF2, offsetSize is the size of the TopDict data.
- self.topDictIndex = TopDictIndex(
- file, cff2GetGlyphOrder, self.topDictSize, isCFF2=isCFF2
- )
- self.strings = None
- self.GlobalSubrs = GlobalSubrsIndex(file, isCFF2=isCFF2)
- self.topDictIndex.strings = self.strings
- self.topDictIndex.GlobalSubrs = self.GlobalSubrs
-
- def __len__(self):
- return len(self.fontNames)
-
- def keys(self):
- return list(self.fontNames)
-
- def values(self):
- return self.topDictIndex
-
- def __getitem__(self, nameOrIndex):
- """Return TopDict instance identified by name (str) or index (int
- or any object that implements `__index__`).
- """
- if hasattr(nameOrIndex, "__index__"):
- index = nameOrIndex.__index__()
- elif isinstance(nameOrIndex, str):
- name = nameOrIndex
- try:
- index = self.fontNames.index(name)
- except ValueError:
- raise KeyError(nameOrIndex)
- else:
- raise TypeError(nameOrIndex)
- return self.topDictIndex[index]
-
- def compile(self, file, otFont, isCFF2=None):
- """Write the object back into binary representation onto the given file.
- ``file`` should be a file handle object. ``otFont`` is the top-level
- :py:class:`fontTools.ttLib.ttFont.TTFont` object containing this CFF file.
-
- If ``isCFF2`` is passed and set to ``True`` or ``False``, then the
- library makes an assertion that the CFF header is of the appropriate
- version.
- """
- self.otFont = otFont
- if isCFF2 is not None:
- # called from ttLib: assert 'major' value matches expected version
- expected_major = 2 if isCFF2 else 1
- if self.major != expected_major:
- raise ValueError(
- "Invalid CFF 'major' version: expected %d, found %d"
- % (expected_major, self.major)
- )
- else:
- # use current 'major' value to determine output format
- assert self.major in (1, 2), "Unknown CFF format"
- isCFF2 = self.major == 2
-
- if otFont.recalcBBoxes and not isCFF2:
- for topDict in self.topDictIndex:
- topDict.recalcFontBBox()
-
- if not isCFF2:
- strings = IndexedStrings()
- else:
- strings = None
- writer = CFFWriter(isCFF2)
- topCompiler = self.topDictIndex.getCompiler(strings, self, isCFF2=isCFF2)
- if isCFF2:
- self.hdrSize = 5
- writer.add(sstruct.pack(cffHeaderFormat, self))
- # Note: topDictSize will most likely change in CFFWriter.toFile().
- self.topDictSize = topCompiler.getDataLength()
- writer.add(struct.pack(">H", self.topDictSize))
- else:
- self.hdrSize = 4
- self.offSize = 4 # will most likely change in CFFWriter.toFile().
- writer.add(sstruct.pack(cffHeaderFormat, self))
- writer.add(struct.pack("B", self.offSize))
- if not isCFF2:
- fontNames = Index()
- for name in self.fontNames:
- fontNames.append(name)
- writer.add(fontNames.getCompiler(strings, self, isCFF2=isCFF2))
- writer.add(topCompiler)
- if not isCFF2:
- writer.add(strings.getCompiler())
- writer.add(self.GlobalSubrs.getCompiler(strings, self, isCFF2=isCFF2))
-
- for topDict in self.topDictIndex:
- if not hasattr(topDict, "charset") or topDict.charset is None:
- charset = otFont.getGlyphOrder()
- topDict.charset = charset
- children = topCompiler.getChildren(strings)
- for child in children:
- writer.add(child)
-
- writer.toFile(file)
-
- def toXML(self, xmlWriter):
- """Write the object into XML representation onto the given
- :class:`fontTools.misc.xmlWriter.XMLWriter`.
-
- .. code:: python
-
- writer = xmlWriter.XMLWriter(sys.stdout)
- tt["CFF "].cff.toXML(writer)
-
- """
-
- xmlWriter.simpletag("major", value=self.major)
- xmlWriter.newline()
- xmlWriter.simpletag("minor", value=self.minor)
- xmlWriter.newline()
- for fontName in self.fontNames:
- xmlWriter.begintag("CFFFont", name=tostr(fontName))
- xmlWriter.newline()
- font = self[fontName]
- font.toXML(xmlWriter)
- xmlWriter.endtag("CFFFont")
- xmlWriter.newline()
- xmlWriter.newline()
- xmlWriter.begintag("GlobalSubrs")
- xmlWriter.newline()
- self.GlobalSubrs.toXML(xmlWriter)
- xmlWriter.endtag("GlobalSubrs")
- xmlWriter.newline()
-
- def fromXML(self, name, attrs, content, otFont=None):
- """Reads data from the XML element into the ``CFFFontSet`` object."""
- self.otFont = otFont
-
- # set defaults. These will be replaced if there are entries for them
- # in the XML file.
- if not hasattr(self, "major"):
- self.major = 1
- if not hasattr(self, "minor"):
- self.minor = 0
-
- if name == "CFFFont":
- if self.major == 1:
- if not hasattr(self, "offSize"):
- # this will be recalculated when the cff is compiled.
- self.offSize = 4
- if not hasattr(self, "hdrSize"):
- self.hdrSize = 4
- if not hasattr(self, "GlobalSubrs"):
- self.GlobalSubrs = GlobalSubrsIndex()
- if not hasattr(self, "fontNames"):
- self.fontNames = []
- self.topDictIndex = TopDictIndex()
- fontName = attrs["name"]
- self.fontNames.append(fontName)
- topDict = TopDict(GlobalSubrs=self.GlobalSubrs)
- topDict.charset = None # gets filled in later
- elif self.major == 2:
- if not hasattr(self, "hdrSize"):
- self.hdrSize = 5
- if not hasattr(self, "GlobalSubrs"):
- self.GlobalSubrs = GlobalSubrsIndex()
- if not hasattr(self, "fontNames"):
- self.fontNames = ["CFF2Font"]
- cff2GetGlyphOrder = self.otFont.getGlyphOrder
- topDict = TopDict(
- GlobalSubrs=self.GlobalSubrs, cff2GetGlyphOrder=cff2GetGlyphOrder
- )
- self.topDictIndex = TopDictIndex(None, cff2GetGlyphOrder)
- self.topDictIndex.append(topDict)
- for element in content:
- if isinstance(element, str):
- continue
- name, attrs, content = element
- topDict.fromXML(name, attrs, content)
-
- if hasattr(topDict, "VarStore") and topDict.FDArray[0].vstore is None:
- fdArray = topDict.FDArray
- for fontDict in fdArray:
- if hasattr(fontDict, "Private"):
- fontDict.Private.vstore = topDict.VarStore
-
- elif name == "GlobalSubrs":
- subrCharStringClass = psCharStrings.T2CharString
- if not hasattr(self, "GlobalSubrs"):
- self.GlobalSubrs = GlobalSubrsIndex()
- for element in content:
- if isinstance(element, str):
- continue
- name, attrs, content = element
- subr = subrCharStringClass()
- subr.fromXML(name, attrs, content)
- self.GlobalSubrs.append(subr)
- elif name == "major":
- self.major = int(attrs["value"])
- elif name == "minor":
- self.minor = int(attrs["value"])
-
- def convertCFFToCFF2(self, otFont):
- """Converts this object from CFF format to CFF2 format. This conversion
- is done 'in-place'. The conversion cannot be reversed.
-
- This assumes a decompiled CFF table. (i.e. that the object has been
- filled via :meth:`decompile`.)"""
- self.major = 2
- cff2GetGlyphOrder = self.otFont.getGlyphOrder
- topDictData = TopDictIndex(None, cff2GetGlyphOrder)
- topDictData.items = self.topDictIndex.items
- self.topDictIndex = topDictData
- topDict = topDictData[0]
- if hasattr(topDict, "Private"):
- privateDict = topDict.Private
- else:
- privateDict = None
- opOrder = buildOrder(topDictOperators2)
- topDict.order = opOrder
- topDict.cff2GetGlyphOrder = cff2GetGlyphOrder
- for entry in topDictOperators:
- key = entry[1]
- if key not in opOrder:
- if key in topDict.rawDict:
- del topDict.rawDict[key]
- if hasattr(topDict, key):
- delattr(topDict, key)
-
- if not hasattr(topDict, "FDArray"):
- fdArray = topDict.FDArray = FDArrayIndex()
- fdArray.strings = None
- fdArray.GlobalSubrs = topDict.GlobalSubrs
- topDict.GlobalSubrs.fdArray = fdArray
- charStrings = topDict.CharStrings
- if charStrings.charStringsAreIndexed:
- charStrings.charStringsIndex.fdArray = fdArray
- else:
- charStrings.fdArray = fdArray
- fontDict = FontDict()
- fontDict.setCFF2(True)
- fdArray.append(fontDict)
- fontDict.Private = privateDict
- privateOpOrder = buildOrder(privateDictOperators2)
- for entry in privateDictOperators:
- key = entry[1]
- if key not in privateOpOrder:
- if key in privateDict.rawDict:
- # print "Removing private dict", key
- del privateDict.rawDict[key]
- if hasattr(privateDict, key):
- delattr(privateDict, key)
- # print "Removing privateDict attr", key
- else:
- # clean up the PrivateDicts in the fdArray
- fdArray = topDict.FDArray
- privateOpOrder = buildOrder(privateDictOperators2)
- for fontDict in fdArray:
- fontDict.setCFF2(True)
- for key in fontDict.rawDict.keys():
- if key not in fontDict.order:
- del fontDict.rawDict[key]
- if hasattr(fontDict, key):
- delattr(fontDict, key)
-
- privateDict = fontDict.Private
- for entry in privateDictOperators:
- key = entry[1]
- if key not in privateOpOrder:
- if key in privateDict.rawDict:
- # print "Removing private dict", key
- del privateDict.rawDict[key]
- if hasattr(privateDict, key):
- delattr(privateDict, key)
- # print "Removing privateDict attr", key
- # At this point, the Subrs and Charstrings are all still T2Charstring class
- # easiest to fix this by compiling, then decompiling again
- file = BytesIO()
- self.compile(file, otFont, isCFF2=True)
- file.seek(0)
- self.decompile(file, otFont, isCFF2=True)
-
- def desubroutinize(self):
- for fontName in self.fontNames:
- font = self[fontName]
- cs = font.CharStrings
- for g in font.charset:
- c, _ = cs.getItemAndSelector(g)
- c.decompile()
- subrs = getattr(c.private, "Subrs", [])
- decompiler = _DesubroutinizingT2Decompiler(
- subrs, c.globalSubrs, c.private
- )
- decompiler.execute(c)
- c.program = c._desubroutinized
- del c._desubroutinized
- # Delete all the local subrs
- if hasattr(font, "FDArray"):
- for fd in font.FDArray:
- pd = fd.Private
- if hasattr(pd, "Subrs"):
- del pd.Subrs
- if "Subrs" in pd.rawDict:
- del pd.rawDict["Subrs"]
- else:
- pd = font.Private
- if hasattr(pd, "Subrs"):
- del pd.Subrs
- if "Subrs" in pd.rawDict:
- del pd.rawDict["Subrs"]
- # as well as the global subrs
- self.GlobalSubrs.clear()
-
-
-class CFFWriter(object):
- """Helper class for serializing CFF data to binary. Used by
- :meth:`CFFFontSet.compile`."""
-
- def __init__(self, isCFF2):
- self.data = []
- self.isCFF2 = isCFF2
-
- def add(self, table):
- self.data.append(table)
-
- def toFile(self, file):
- lastPosList = None
- count = 1
- while True:
- log.log(DEBUG, "CFFWriter.toFile() iteration: %d", count)
- count = count + 1
- pos = 0
- posList = [pos]
- for item in self.data:
- if hasattr(item, "getDataLength"):
- endPos = pos + item.getDataLength()
- if isinstance(item, TopDictIndexCompiler) and item.isCFF2:
- self.topDictSize = item.getDataLength()
- else:
- endPos = pos + len(item)
- if hasattr(item, "setPos"):
- item.setPos(pos, endPos)
- pos = endPos
- posList.append(pos)
- if posList == lastPosList:
- break
- lastPosList = posList
- log.log(DEBUG, "CFFWriter.toFile() writing to file.")
- begin = file.tell()
- if self.isCFF2:
- self.data[1] = struct.pack(">H", self.topDictSize)
- else:
- self.offSize = calcOffSize(lastPosList[-1])
- self.data[1] = struct.pack("B", self.offSize)
- posList = [0]
- for item in self.data:
- if hasattr(item, "toFile"):
- item.toFile(file)
- else:
- file.write(item)
- posList.append(file.tell() - begin)
- assert posList == lastPosList
-
-
-def calcOffSize(largestOffset):
- if largestOffset < 0x100:
- offSize = 1
- elif largestOffset < 0x10000:
- offSize = 2
- elif largestOffset < 0x1000000:
- offSize = 3
- else:
- offSize = 4
- return offSize
-
-
-class IndexCompiler(object):
- """Base class for writing CFF `INDEX data `_
- to binary."""
-
- def __init__(self, items, strings, parent, isCFF2=None):
- if isCFF2 is None and hasattr(parent, "isCFF2"):
- isCFF2 = parent.isCFF2
- assert isCFF2 is not None
- self.isCFF2 = isCFF2
- self.items = self.getItems(items, strings)
- self.parent = parent
-
- def getItems(self, items, strings):
- return items
-
- def getOffsets(self):
- # An empty INDEX contains only the count field.
- if self.items:
- pos = 1
- offsets = [pos]
- for item in self.items:
- if hasattr(item, "getDataLength"):
- pos = pos + item.getDataLength()
- else:
- pos = pos + len(item)
- offsets.append(pos)
- else:
- offsets = []
- return offsets
-
- def getDataLength(self):
- if self.isCFF2:
- countSize = 4
- else:
- countSize = 2
-
- if self.items:
- lastOffset = self.getOffsets()[-1]
- offSize = calcOffSize(lastOffset)
- dataLength = (
- countSize
- + 1 # count
- + (len(self.items) + 1) * offSize # offSize
- + lastOffset # the offsets
- - 1 # size of object data
- )
- else:
- # count. For empty INDEX tables, this is the only entry.
- dataLength = countSize
-
- return dataLength
-
- def toFile(self, file):
- offsets = self.getOffsets()
- if self.isCFF2:
- writeCard32(file, len(self.items))
- else:
- writeCard16(file, len(self.items))
- # An empty INDEX contains only the count field.
- if self.items:
- offSize = calcOffSize(offsets[-1])
- writeCard8(file, offSize)
- offSize = -offSize
- pack = struct.pack
- for offset in offsets:
- binOffset = pack(">l", offset)[offSize:]
- assert len(binOffset) == -offSize
- file.write(binOffset)
- for item in self.items:
- if hasattr(item, "toFile"):
- item.toFile(file)
- else:
- data = tobytes(item, encoding="latin1")
- file.write(data)
-
-
-class IndexedStringsCompiler(IndexCompiler):
- def getItems(self, items, strings):
- return items.strings
-
-
-class TopDictIndexCompiler(IndexCompiler):
- """Helper class for writing the TopDict to binary."""
-
- def getItems(self, items, strings):
- out = []
- for item in items:
- out.append(item.getCompiler(strings, self))
- return out
-
- def getChildren(self, strings):
- children = []
- for topDict in self.items:
- children.extend(topDict.getChildren(strings))
- return children
-
- def getOffsets(self):
- if self.isCFF2:
- offsets = [0, self.items[0].getDataLength()]
- return offsets
- else:
- return super(TopDictIndexCompiler, self).getOffsets()
-
- def getDataLength(self):
- if self.isCFF2:
- dataLength = self.items[0].getDataLength()
- return dataLength
- else:
- return super(TopDictIndexCompiler, self).getDataLength()
-
- def toFile(self, file):
- if self.isCFF2:
- self.items[0].toFile(file)
- else:
- super(TopDictIndexCompiler, self).toFile(file)
-
-
-class FDArrayIndexCompiler(IndexCompiler):
- """Helper class for writing the
- `Font DICT INDEX `_
- to binary."""
-
- def getItems(self, items, strings):
- out = []
- for item in items:
- out.append(item.getCompiler(strings, self))
- return out
-
- def getChildren(self, strings):
- children = []
- for fontDict in self.items:
- children.extend(fontDict.getChildren(strings))
- return children
-
- def toFile(self, file):
- offsets = self.getOffsets()
- if self.isCFF2:
- writeCard32(file, len(self.items))
- else:
- writeCard16(file, len(self.items))
- offSize = calcOffSize(offsets[-1])
- writeCard8(file, offSize)
- offSize = -offSize
- pack = struct.pack
- for offset in offsets:
- binOffset = pack(">l", offset)[offSize:]
- assert len(binOffset) == -offSize
- file.write(binOffset)
- for item in self.items:
- if hasattr(item, "toFile"):
- item.toFile(file)
- else:
- file.write(item)
-
- def setPos(self, pos, endPos):
- self.parent.rawDict["FDArray"] = pos
-
-
-class GlobalSubrsCompiler(IndexCompiler):
- """Helper class for writing the `global subroutine INDEX `_
- to binary."""
-
- def getItems(self, items, strings):
- out = []
- for cs in items:
- cs.compile(self.isCFF2)
- out.append(cs.bytecode)
- return out
-
-
-class SubrsCompiler(GlobalSubrsCompiler):
- """Helper class for writing the `local subroutine INDEX `_
- to binary."""
-
- def setPos(self, pos, endPos):
- offset = pos - self.parent.pos
- self.parent.rawDict["Subrs"] = offset
-
-
-class CharStringsCompiler(GlobalSubrsCompiler):
- """Helper class for writing the `CharStrings INDEX `_
- to binary."""
-
- def getItems(self, items, strings):
- out = []
- for cs in items:
- cs.compile(self.isCFF2)
- out.append(cs.bytecode)
- return out
-
- def setPos(self, pos, endPos):
- self.parent.rawDict["CharStrings"] = pos
-
-
-class Index(object):
- """This class represents what the CFF spec calls an INDEX (an array of
- variable-sized objects). `Index` items can be addressed and set using
- Python list indexing."""
-
- compilerClass = IndexCompiler
-
- def __init__(self, file=None, isCFF2=None):
- assert (isCFF2 is None) == (file is None)
- self.items = []
- name = self.__class__.__name__
- if file is None:
- return
- self._isCFF2 = isCFF2
- log.log(DEBUG, "loading %s at %s", name, file.tell())
- self.file = file
- if isCFF2:
- count = readCard32(file)
- else:
- count = readCard16(file)
- if count == 0:
- return
- self.items = [None] * count
- offSize = readCard8(file)
- log.log(DEBUG, " index count: %s offSize: %s", count, offSize)
- assert offSize <= 4, "offSize too large: %s" % offSize
- self.offsets = offsets = []
- pad = b"\0" * (4 - offSize)
- for index in range(count + 1):
- chunk = file.read(offSize)
- chunk = pad + chunk
- (offset,) = struct.unpack(">L", chunk)
- offsets.append(int(offset))
- self.offsetBase = file.tell() - 1
- file.seek(self.offsetBase + offsets[-1]) # pretend we've read the whole lot
- log.log(DEBUG, " end of %s at %s", name, file.tell())
-
- def __len__(self):
- return len(self.items)
-
- def __getitem__(self, index):
- item = self.items[index]
- if item is not None:
- return item
- offset = self.offsets[index] + self.offsetBase
- size = self.offsets[index + 1] - self.offsets[index]
- file = self.file
- file.seek(offset)
- data = file.read(size)
- assert len(data) == size
- item = self.produceItem(index, data, file, offset)
- self.items[index] = item
- return item
-
- def __setitem__(self, index, item):
- self.items[index] = item
-
- def produceItem(self, index, data, file, offset):
- return data
-
- def append(self, item):
- """Add an item to an INDEX."""
- self.items.append(item)
-
- def getCompiler(self, strings, parent, isCFF2=None):
- return self.compilerClass(self, strings, parent, isCFF2=isCFF2)
-
- def clear(self):
- """Empty the INDEX."""
- del self.items[:]
-
-
-class GlobalSubrsIndex(Index):
- """This index contains all the global subroutines in the font. A global
- subroutine is a set of ``CharString`` data which is accessible to any
- glyph in the font, and are used to store repeated instructions - for
- example, components may be encoded as global subroutines, but so could
- hinting instructions.
-
- Remember that when interpreting a ``callgsubr`` instruction (or indeed
- a ``callsubr`` instruction) that you will need to add the "subroutine
- number bias" to number given:
-
- .. code:: python
-
- tt = ttLib.TTFont("Almendra-Bold.otf")
- u = tt["CFF "].cff[0].CharStrings["udieresis"]
- u.decompile()
-
- u.toXML(XMLWriter(sys.stdout))
- #
- # -64 callgsubr <-- Subroutine which implements the dieresis mark
- #
-
- tt["CFF "].cff[0].GlobalSubrs[-64] # <-- WRONG
- #
-
- tt["CFF "].cff[0].GlobalSubrs[-64 + 107] # <-- RIGHT
- #
-
- ("The bias applied depends on the number of subrs (gsubrs). If the number of
- subrs (gsubrs) is less than 1240, the bias is 107. Otherwise if it is less
- than 33900, it is 1131; otherwise it is 32768.",
- `Subroutine Operators `)
- """
-
- compilerClass = GlobalSubrsCompiler
- subrClass = psCharStrings.T2CharString
- charStringClass = psCharStrings.T2CharString
-
- def __init__(
- self,
- file=None,
- globalSubrs=None,
- private=None,
- fdSelect=None,
- fdArray=None,
- isCFF2=None,
- ):
- super(GlobalSubrsIndex, self).__init__(file, isCFF2=isCFF2)
- self.globalSubrs = globalSubrs
- self.private = private
- if fdSelect:
- self.fdSelect = fdSelect
- if fdArray:
- self.fdArray = fdArray
-
- def produceItem(self, index, data, file, offset):
- if self.private is not None:
- private = self.private
- elif hasattr(self, "fdArray") and self.fdArray is not None:
- if hasattr(self, "fdSelect") and self.fdSelect is not None:
- fdIndex = self.fdSelect[index]
- else:
- fdIndex = 0
- private = self.fdArray[fdIndex].Private
- else:
- private = None
- return self.subrClass(data, private=private, globalSubrs=self.globalSubrs)
-
- def toXML(self, xmlWriter):
- """Write the subroutines index into XML representation onto the given
- :class:`fontTools.misc.xmlWriter.XMLWriter`.
-
- .. code:: python
-
- writer = xmlWriter.XMLWriter(sys.stdout)
- tt["CFF "].cff[0].GlobalSubrs.toXML(writer)
-
- """
- xmlWriter.comment(
- "The 'index' attribute is only for humans; " "it is ignored when parsed."
- )
- xmlWriter.newline()
- for i in range(len(self)):
- subr = self[i]
- if subr.needsDecompilation():
- xmlWriter.begintag("CharString", index=i, raw=1)
- else:
- xmlWriter.begintag("CharString", index=i)
- xmlWriter.newline()
- subr.toXML(xmlWriter)
- xmlWriter.endtag("CharString")
- xmlWriter.newline()
-
- def fromXML(self, name, attrs, content):
- if name != "CharString":
- return
- subr = self.subrClass()
- subr.fromXML(name, attrs, content)
- self.append(subr)
-
- def getItemAndSelector(self, index):
- sel = None
- if hasattr(self, "fdSelect"):
- sel = self.fdSelect[index]
- return self[index], sel
-
-
-class SubrsIndex(GlobalSubrsIndex):
- """This index contains a glyph's local subroutines. A local subroutine is a
- private set of ``CharString`` data which is accessible only to the glyph to
- which the index is attached."""
-
- compilerClass = SubrsCompiler
-
-
-class TopDictIndex(Index):
- """This index represents the array of ``TopDict`` structures in the font
- (again, usually only one entry is present). Hence the following calls are
- equivalent:
-
- .. code:: python
-
- tt["CFF "].cff[0]
- #
- tt["CFF "].cff.topDictIndex[0]
- #
-
- """
-
- compilerClass = TopDictIndexCompiler
-
- def __init__(self, file=None, cff2GetGlyphOrder=None, topSize=0, isCFF2=None):
- assert (isCFF2 is None) == (file is None)
- self.cff2GetGlyphOrder = cff2GetGlyphOrder
- if file is not None and isCFF2:
- self._isCFF2 = isCFF2
- self.items = []
- name = self.__class__.__name__
- log.log(DEBUG, "loading %s at %s", name, file.tell())
- self.file = file
- count = 1
- self.items = [None] * count
- self.offsets = [0, topSize]
- self.offsetBase = file.tell()
- # pretend we've read the whole lot
- file.seek(self.offsetBase + topSize)
- log.log(DEBUG, " end of %s at %s", name, file.tell())
- else:
- super(TopDictIndex, self).__init__(file, isCFF2=isCFF2)
-
- def produceItem(self, index, data, file, offset):
- top = TopDict(
- self.strings,
- file,
- offset,
- self.GlobalSubrs,
- self.cff2GetGlyphOrder,
- isCFF2=self._isCFF2,
- )
- top.decompile(data)
- return top
-
- def toXML(self, xmlWriter):
- for i in range(len(self)):
- xmlWriter.begintag("FontDict", index=i)
- xmlWriter.newline()
- self[i].toXML(xmlWriter)
- xmlWriter.endtag("FontDict")
- xmlWriter.newline()
-
-
-class FDArrayIndex(Index):
- compilerClass = FDArrayIndexCompiler
-
- def toXML(self, xmlWriter):
- for i in range(len(self)):
- xmlWriter.begintag("FontDict", index=i)
- xmlWriter.newline()
- self[i].toXML(xmlWriter)
- xmlWriter.endtag("FontDict")
- xmlWriter.newline()
-
- def produceItem(self, index, data, file, offset):
- fontDict = FontDict(
- self.strings,
- file,
- offset,
- self.GlobalSubrs,
- isCFF2=self._isCFF2,
- vstore=self.vstore,
- )
- fontDict.decompile(data)
- return fontDict
-
- def fromXML(self, name, attrs, content):
- if name != "FontDict":
- return
- fontDict = FontDict()
- for element in content:
- if isinstance(element, str):
- continue
- name, attrs, content = element
- fontDict.fromXML(name, attrs, content)
- self.append(fontDict)
-
-
-class VarStoreData(object):
- def __init__(self, file=None, otVarStore=None):
- self.file = file
- self.data = None
- self.otVarStore = otVarStore
- self.font = TTFont() # dummy font for the decompile function.
-
- def decompile(self):
- if self.file:
- # read data in from file. Assume position is correct.
- length = readCard16(self.file)
- self.data = self.file.read(length)
- globalState = {}
- reader = OTTableReader(self.data, globalState)
- self.otVarStore = ot.VarStore()
- self.otVarStore.decompile(reader, self.font)
- return self
-
- def compile(self):
- writer = OTTableWriter()
- self.otVarStore.compile(writer, self.font)
- # Note that this omits the initial Card16 length from the CFF2
- # VarStore data block
- self.data = writer.getAllData()
-
- def writeXML(self, xmlWriter, name):
- self.otVarStore.toXML(xmlWriter, self.font)
-
- def xmlRead(self, name, attrs, content, parent):
- self.otVarStore = ot.VarStore()
- for element in content:
- if isinstance(element, tuple):
- name, attrs, content = element
- self.otVarStore.fromXML(name, attrs, content, self.font)
- else:
- pass
- return None
-
- def __len__(self):
- return len(self.data)
-
- def getNumRegions(self, vsIndex):
- if vsIndex is None:
- vsIndex = 0
- varData = self.otVarStore.VarData[vsIndex]
- numRegions = varData.VarRegionCount
- return numRegions
-
-
-class FDSelect(object):
- def __init__(self, file=None, numGlyphs=None, format=None):
- if file:
- # read data in from file
- self.format = readCard8(file)
- if self.format == 0:
- from array import array
-
- self.gidArray = array("B", file.read(numGlyphs)).tolist()
- elif self.format == 3:
- gidArray = [None] * numGlyphs
- nRanges = readCard16(file)
- fd = None
- prev = None
- for i in range(nRanges):
- first = readCard16(file)
- if prev is not None:
- for glyphID in range(prev, first):
- gidArray[glyphID] = fd
- prev = first
- fd = readCard8(file)
- if prev is not None:
- first = readCard16(file)
- for glyphID in range(prev, first):
- gidArray[glyphID] = fd
- self.gidArray = gidArray
- elif self.format == 4:
- gidArray = [None] * numGlyphs
- nRanges = readCard32(file)
- fd = None
- prev = None
- for i in range(nRanges):
- first = readCard32(file)
- if prev is not None:
- for glyphID in range(prev, first):
- gidArray[glyphID] = fd
- prev = first
- fd = readCard16(file)
- if prev is not None:
- first = readCard32(file)
- for glyphID in range(prev, first):
- gidArray[glyphID] = fd
- self.gidArray = gidArray
- else:
- assert False, "unsupported FDSelect format: %s" % format
- else:
- # reading from XML. Make empty gidArray, and leave format as passed in.
- # format is None will result in the smallest representation being used.
- self.format = format
- self.gidArray = []
-
- def __len__(self):
- return len(self.gidArray)
-
- def __getitem__(self, index):
- return self.gidArray[index]
-
- def __setitem__(self, index, fdSelectValue):
- self.gidArray[index] = fdSelectValue
-
- def append(self, fdSelectValue):
- self.gidArray.append(fdSelectValue)
-
-
-class CharStrings(object):
- """The ``CharStrings`` in the font represent the instructions for drawing
- each glyph. This object presents a dictionary interface to the font's
- CharStrings, indexed by glyph name:
-
- .. code:: python
-
- tt["CFF "].cff[0].CharStrings["a"]
- #
-
- See :class:`fontTools.misc.psCharStrings.T1CharString` and
- :class:`fontTools.misc.psCharStrings.T2CharString` for how to decompile,
- compile and interpret the glyph drawing instructions in the returned objects.
-
- """
-
- def __init__(
- self,
- file,
- charset,
- globalSubrs,
- private,
- fdSelect,
- fdArray,
- isCFF2=None,
- varStore=None,
- ):
- self.globalSubrs = globalSubrs
- self.varStore = varStore
- if file is not None:
- self.charStringsIndex = SubrsIndex(
- file, globalSubrs, private, fdSelect, fdArray, isCFF2=isCFF2
- )
- self.charStrings = charStrings = {}
- for i in range(len(charset)):
- charStrings[charset[i]] = i
- # read from OTF file: charStrings.values() are indices into
- # charStringsIndex.
- self.charStringsAreIndexed = 1
- else:
- self.charStrings = {}
- # read from ttx file: charStrings.values() are actual charstrings
- self.charStringsAreIndexed = 0
- self.private = private
- if fdSelect is not None:
- self.fdSelect = fdSelect
- if fdArray is not None:
- self.fdArray = fdArray
-
- def keys(self):
- return list(self.charStrings.keys())
-
- def values(self):
- if self.charStringsAreIndexed:
- return self.charStringsIndex
- else:
- return list(self.charStrings.values())
-
- def has_key(self, name):
- return name in self.charStrings
-
- __contains__ = has_key
-
- def __len__(self):
- return len(self.charStrings)
-
- def __getitem__(self, name):
- charString = self.charStrings[name]
- if self.charStringsAreIndexed:
- charString = self.charStringsIndex[charString]
- return charString
-
- def __setitem__(self, name, charString):
- if self.charStringsAreIndexed:
- index = self.charStrings[name]
- self.charStringsIndex[index] = charString
- else:
- self.charStrings[name] = charString
-
- def getItemAndSelector(self, name):
- if self.charStringsAreIndexed:
- index = self.charStrings[name]
- return self.charStringsIndex.getItemAndSelector(index)
- else:
- if hasattr(self, "fdArray"):
- if hasattr(self, "fdSelect"):
- sel = self.charStrings[name].fdSelectIndex
- else:
- sel = 0
- else:
- sel = None
- return self.charStrings[name], sel
-
- def toXML(self, xmlWriter):
- names = sorted(self.keys())
- for name in names:
- charStr, fdSelectIndex = self.getItemAndSelector(name)
- if charStr.needsDecompilation():
- raw = [("raw", 1)]
- else:
- raw = []
- if fdSelectIndex is None:
- xmlWriter.begintag("CharString", [("name", name)] + raw)
- else:
- xmlWriter.begintag(
- "CharString",
- [("name", name), ("fdSelectIndex", fdSelectIndex)] + raw,
- )
- xmlWriter.newline()
- charStr.toXML(xmlWriter)
- xmlWriter.endtag("CharString")
- xmlWriter.newline()
-
- def fromXML(self, name, attrs, content):
- for element in content:
- if isinstance(element, str):
- continue
- name, attrs, content = element
- if name != "CharString":
- continue
- fdID = -1
- if hasattr(self, "fdArray"):
- try:
- fdID = safeEval(attrs["fdSelectIndex"])
- except KeyError:
- fdID = 0
- private = self.fdArray[fdID].Private
- else:
- private = self.private
-
- glyphName = attrs["name"]
- charStringClass = psCharStrings.T2CharString
- charString = charStringClass(private=private, globalSubrs=self.globalSubrs)
- charString.fromXML(name, attrs, content)
- if fdID >= 0:
- charString.fdSelectIndex = fdID
- self[glyphName] = charString
-
-
-def readCard8(file):
- return byteord(file.read(1))
-
-
-def readCard16(file):
- (value,) = struct.unpack(">H", file.read(2))
- return value
-
-
-def readCard32(file):
- (value,) = struct.unpack(">L", file.read(4))
- return value
-
-
-def writeCard8(file, value):
- file.write(bytechr(value))
-
-
-def writeCard16(file, value):
- file.write(struct.pack(">H", value))
-
-
-def writeCard32(file, value):
- file.write(struct.pack(">L", value))
-
-
-def packCard8(value):
- return bytechr(value)
-
-
-def packCard16(value):
- return struct.pack(">H", value)
-
-
-def packCard32(value):
- return struct.pack(">L", value)
-
-
-def buildOperatorDict(table):
- d = {}
- for op, name, arg, default, conv in table:
- d[op] = (name, arg)
- return d
-
-
-def buildOpcodeDict(table):
- d = {}
- for op, name, arg, default, conv in table:
- if isinstance(op, tuple):
- op = bytechr(op[0]) + bytechr(op[1])
- else:
- op = bytechr(op)
- d[name] = (op, arg)
- return d
-
-
-def buildOrder(table):
- l = []
- for op, name, arg, default, conv in table:
- l.append(name)
- return l
-
-
-def buildDefaults(table):
- d = {}
- for op, name, arg, default, conv in table:
- if default is not None:
- d[name] = default
- return d
-
-
-def buildConverters(table):
- d = {}
- for op, name, arg, default, conv in table:
- d[name] = conv
- return d
-
-
-class SimpleConverter(object):
- def read(self, parent, value):
- if not hasattr(parent, "file"):
- return self._read(parent, value)
- file = parent.file
- pos = file.tell()
- try:
- return self._read(parent, value)
- finally:
- file.seek(pos)
-
- def _read(self, parent, value):
- return value
-
- def write(self, parent, value):
- return value
-
- def xmlWrite(self, xmlWriter, name, value):
- xmlWriter.simpletag(name, value=value)
- xmlWriter.newline()
-
- def xmlRead(self, name, attrs, content, parent):
- return attrs["value"]
-
-
-class ASCIIConverter(SimpleConverter):
- def _read(self, parent, value):
- return tostr(value, encoding="ascii")
-
- def write(self, parent, value):
- return tobytes(value, encoding="ascii")
-
- def xmlWrite(self, xmlWriter, name, value):
- xmlWriter.simpletag(name, value=tostr(value, encoding="ascii"))
- xmlWriter.newline()
-
- def xmlRead(self, name, attrs, content, parent):
- return tobytes(attrs["value"], encoding=("ascii"))
-
-
-class Latin1Converter(SimpleConverter):
- def _read(self, parent, value):
- return tostr(value, encoding="latin1")
-
- def write(self, parent, value):
- return tobytes(value, encoding="latin1")
-
- def xmlWrite(self, xmlWriter, name, value):
- value = tostr(value, encoding="latin1")
- if name in ["Notice", "Copyright"]:
- value = re.sub(r"[\r\n]\s+", " ", value)
- xmlWriter.simpletag(name, value=value)
- xmlWriter.newline()
-
- def xmlRead(self, name, attrs, content, parent):
- return tobytes(attrs["value"], encoding=("latin1"))
-
-
-def parseNum(s):
- try:
- value = int(s)
- except:
- value = float(s)
- return value
-
-
-def parseBlendList(s):
- valueList = []
- for element in s:
- if isinstance(element, str):
- continue
- name, attrs, content = element
- blendList = attrs["value"].split()
- blendList = [eval(val) for val in blendList]
- valueList.append(blendList)
- if len(valueList) == 1:
- valueList = valueList[0]
- return valueList
-
-
-class NumberConverter(SimpleConverter):
- def xmlWrite(self, xmlWriter, name, value):
- if isinstance(value, list):
- xmlWriter.begintag(name)
- xmlWriter.newline()
- xmlWriter.indent()
- blendValue = " ".join([str(val) for val in value])
- xmlWriter.simpletag(kBlendDictOpName, value=blendValue)
- xmlWriter.newline()
- xmlWriter.dedent()
- xmlWriter.endtag(name)
- xmlWriter.newline()
- else:
- xmlWriter.simpletag(name, value=value)
- xmlWriter.newline()
-
- def xmlRead(self, name, attrs, content, parent):
- valueString = attrs.get("value", None)
- if valueString is None:
- value = parseBlendList(content)
- else:
- value = parseNum(attrs["value"])
- return value
-
-
-class ArrayConverter(SimpleConverter):
- def xmlWrite(self, xmlWriter, name, value):
- if value and isinstance(value[0], list):
- xmlWriter.begintag(name)
- xmlWriter.newline()
- xmlWriter.indent()
- for valueList in value:
- blendValue = " ".join([str(val) for val in valueList])
- xmlWriter.simpletag(kBlendDictOpName, value=blendValue)
- xmlWriter.newline()
- xmlWriter.dedent()
- xmlWriter.endtag(name)
- xmlWriter.newline()
- else:
- value = " ".join([str(val) for val in value])
- xmlWriter.simpletag(name, value=value)
- xmlWriter.newline()
-
- def xmlRead(self, name, attrs, content, parent):
- valueString = attrs.get("value", None)
- if valueString is None:
- valueList = parseBlendList(content)
- else:
- values = valueString.split()
- valueList = [parseNum(value) for value in values]
- return valueList
-
-
-class TableConverter(SimpleConverter):
- def xmlWrite(self, xmlWriter, name, value):
- xmlWriter.begintag(name)
- xmlWriter.newline()
- value.toXML(xmlWriter)
- xmlWriter.endtag(name)
- xmlWriter.newline()
-
- def xmlRead(self, name, attrs, content, parent):
- ob = self.getClass()()
- for element in content:
- if isinstance(element, str):
- continue
- name, attrs, content = element
- ob.fromXML(name, attrs, content)
- return ob
-
-
-class PrivateDictConverter(TableConverter):
- def getClass(self):
- return PrivateDict
-
- def _read(self, parent, value):
- size, offset = value
- file = parent.file
- isCFF2 = parent._isCFF2
- try:
- vstore = parent.vstore
- except AttributeError:
- vstore = None
- priv = PrivateDict(parent.strings, file, offset, isCFF2=isCFF2, vstore=vstore)
- file.seek(offset)
- data = file.read(size)
- assert len(data) == size
- priv.decompile(data)
- return priv
-
- def write(self, parent, value):
- return (0, 0) # dummy value
-
-
-class SubrsConverter(TableConverter):
- def getClass(self):
- return SubrsIndex
-
- def _read(self, parent, value):
- file = parent.file
- isCFF2 = parent._isCFF2
- file.seek(parent.offset + value) # Offset(self)
- return SubrsIndex(file, isCFF2=isCFF2)
-
- def write(self, parent, value):
- return 0 # dummy value
-
-
-class CharStringsConverter(TableConverter):
- def _read(self, parent, value):
- file = parent.file
- isCFF2 = parent._isCFF2
- charset = parent.charset
- varStore = getattr(parent, "VarStore", None)
- globalSubrs = parent.GlobalSubrs
- if hasattr(parent, "FDArray"):
- fdArray = parent.FDArray
- if hasattr(parent, "FDSelect"):
- fdSelect = parent.FDSelect
- else:
- fdSelect = None
- private = None
- else:
- fdSelect, fdArray = None, None
- private = parent.Private
- file.seek(value) # Offset(0)
- charStrings = CharStrings(
- file,
- charset,
- globalSubrs,
- private,
- fdSelect,
- fdArray,
- isCFF2=isCFF2,
- varStore=varStore,
- )
- return charStrings
-
- def write(self, parent, value):
- return 0 # dummy value
-
- def xmlRead(self, name, attrs, content, parent):
- if hasattr(parent, "FDArray"):
- # if it is a CID-keyed font, then the private Dict is extracted from the
- # parent.FDArray
- fdArray = parent.FDArray
- if hasattr(parent, "FDSelect"):
- fdSelect = parent.FDSelect
- else:
- fdSelect = None
- private = None
- else:
- # if it is a name-keyed font, then the private dict is in the top dict,
- # and
- # there is no fdArray.
- private, fdSelect, fdArray = parent.Private, None, None
- charStrings = CharStrings(
- None,
- None,
- parent.GlobalSubrs,
- private,
- fdSelect,
- fdArray,
- varStore=getattr(parent, "VarStore", None),
- )
- charStrings.fromXML(name, attrs, content)
- return charStrings
-
-
-class CharsetConverter(SimpleConverter):
- def _read(self, parent, value):
- isCID = hasattr(parent, "ROS")
- if value > 2:
- numGlyphs = parent.numGlyphs
- file = parent.file
- file.seek(value)
- log.log(DEBUG, "loading charset at %s", value)
- format = readCard8(file)
- if format == 0:
- charset = parseCharset0(numGlyphs, file, parent.strings, isCID)
- elif format == 1 or format == 2:
- charset = parseCharset(numGlyphs, file, parent.strings, isCID, format)
- else:
- raise NotImplementedError
- assert len(charset) == numGlyphs
- log.log(DEBUG, " charset end at %s", file.tell())
- # make sure glyph names are unique
- allNames = {}
- newCharset = []
- for glyphName in charset:
- if glyphName in allNames:
- # make up a new glyphName that's unique
- n = allNames[glyphName]
- while (glyphName + "#" + str(n)) in allNames:
- n += 1
- allNames[glyphName] = n + 1
- glyphName = glyphName + "#" + str(n)
- allNames[glyphName] = 1
- newCharset.append(glyphName)
- charset = newCharset
- else: # offset == 0 -> no charset data.
- if isCID or "CharStrings" not in parent.rawDict:
- # We get here only when processing fontDicts from the FDArray of
- # CFF-CID fonts. Only the real topDict references the chrset.
- assert value == 0
- charset = None
- elif value == 0:
- charset = cffISOAdobeStrings
- elif value == 1:
- charset = cffIExpertStrings
- elif value == 2:
- charset = cffExpertSubsetStrings
- if charset and (len(charset) != parent.numGlyphs):
- charset = charset[: parent.numGlyphs]
- return charset
-
- def write(self, parent, value):
- return 0 # dummy value
-
- def xmlWrite(self, xmlWriter, name, value):
- # XXX only write charset when not in OT/TTX context, where we
- # dump charset as a separate "GlyphOrder" table.
- # # xmlWriter.simpletag("charset")
- xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element")
- xmlWriter.newline()
-
- def xmlRead(self, name, attrs, content, parent):
- pass
-
-
-class CharsetCompiler(object):
- def __init__(self, strings, charset, parent):
- assert charset[0] == ".notdef"
- isCID = hasattr(parent.dictObj, "ROS")
- data0 = packCharset0(charset, isCID, strings)
- data = packCharset(charset, isCID, strings)
- if len(data) < len(data0):
- self.data = data
- else:
- self.data = data0
- self.parent = parent
-
- def setPos(self, pos, endPos):
- self.parent.rawDict["charset"] = pos
-
- def getDataLength(self):
- return len(self.data)
-
- def toFile(self, file):
- file.write(self.data)
-
-
-def getStdCharSet(charset):
- # check to see if we can use a predefined charset value.
- predefinedCharSetVal = None
- predefinedCharSets = [
- (cffISOAdobeStringCount, cffISOAdobeStrings, 0),
- (cffExpertStringCount, cffIExpertStrings, 1),
- (cffExpertSubsetStringCount, cffExpertSubsetStrings, 2),
- ]
- lcs = len(charset)
- for cnt, pcs, csv in predefinedCharSets:
- if predefinedCharSetVal is not None:
- break
- if lcs > cnt:
- continue
- predefinedCharSetVal = csv
- for i in range(lcs):
- if charset[i] != pcs[i]:
- predefinedCharSetVal = None
- break
- return predefinedCharSetVal
-
-
-def getCIDfromName(name, strings):
- return int(name[3:])
-
-
-def getSIDfromName(name, strings):
- return strings.getSID(name)
-
-
-def packCharset0(charset, isCID, strings):
- fmt = 0
- data = [packCard8(fmt)]
- if isCID:
- getNameID = getCIDfromName
- else:
- getNameID = getSIDfromName
-
- for name in charset[1:]:
- data.append(packCard16(getNameID(name, strings)))
- return bytesjoin(data)
-
-
-def packCharset(charset, isCID, strings):
- fmt = 1
- ranges = []
- first = None
- end = 0
- if isCID:
- getNameID = getCIDfromName
- else:
- getNameID = getSIDfromName
-
- for name in charset[1:]:
- SID = getNameID(name, strings)
- if first is None:
- first = SID
- elif end + 1 != SID:
- nLeft = end - first
- if nLeft > 255:
- fmt = 2
- ranges.append((first, nLeft))
- first = SID
- end = SID
- if end:
- nLeft = end - first
- if nLeft > 255:
- fmt = 2
- ranges.append((first, nLeft))
-
- data = [packCard8(fmt)]
- if fmt == 1:
- nLeftFunc = packCard8
- else:
- nLeftFunc = packCard16
- for first, nLeft in ranges:
- data.append(packCard16(first) + nLeftFunc(nLeft))
- return bytesjoin(data)
-
-
-def parseCharset0(numGlyphs, file, strings, isCID):
- charset = [".notdef"]
- if isCID:
- for i in range(numGlyphs - 1):
- CID = readCard16(file)
- charset.append("cid" + str(CID).zfill(5))
- else:
- for i in range(numGlyphs - 1):
- SID = readCard16(file)
- charset.append(strings[SID])
- return charset
-
-
-def parseCharset(numGlyphs, file, strings, isCID, fmt):
- charset = [".notdef"]
- count = 1
- if fmt == 1:
- nLeftFunc = readCard8
- else:
- nLeftFunc = readCard16
- while count < numGlyphs:
- first = readCard16(file)
- nLeft = nLeftFunc(file)
- if isCID:
- for CID in range(first, first + nLeft + 1):
- charset.append("cid" + str(CID).zfill(5))
- else:
- for SID in range(first, first + nLeft + 1):
- charset.append(strings[SID])
- count = count + nLeft + 1
- return charset
-
-
-class EncodingCompiler(object):
- def __init__(self, strings, encoding, parent):
- assert not isinstance(encoding, str)
- data0 = packEncoding0(parent.dictObj.charset, encoding, parent.strings)
- data1 = packEncoding1(parent.dictObj.charset, encoding, parent.strings)
- if len(data0) < len(data1):
- self.data = data0
- else:
- self.data = data1
- self.parent = parent
-
- def setPos(self, pos, endPos):
- self.parent.rawDict["Encoding"] = pos
-
- def getDataLength(self):
- return len(self.data)
-
- def toFile(self, file):
- file.write(self.data)
-
-
-class EncodingConverter(SimpleConverter):
- def _read(self, parent, value):
- if value == 0:
- return "StandardEncoding"
- elif value == 1:
- return "ExpertEncoding"
- else:
- assert value > 1
- file = parent.file
- file.seek(value)
- log.log(DEBUG, "loading Encoding at %s", value)
- fmt = readCard8(file)
- haveSupplement = fmt & 0x80
- if haveSupplement:
- raise NotImplementedError("Encoding supplements are not yet supported")
- fmt = fmt & 0x7F
- if fmt == 0:
- encoding = parseEncoding0(
- parent.charset, file, haveSupplement, parent.strings
- )
- elif fmt == 1:
- encoding = parseEncoding1(
- parent.charset, file, haveSupplement, parent.strings
- )
- return encoding
-
- def write(self, parent, value):
- if value == "StandardEncoding":
- return 0
- elif value == "ExpertEncoding":
- return 1
- return 0 # dummy value
-
- def xmlWrite(self, xmlWriter, name, value):
- if value in ("StandardEncoding", "ExpertEncoding"):
- xmlWriter.simpletag(name, name=value)
- xmlWriter.newline()
- return
- xmlWriter.begintag(name)
- xmlWriter.newline()
- for code in range(len(value)):
- glyphName = value[code]
- if glyphName != ".notdef":
- xmlWriter.simpletag("map", code=hex(code), name=glyphName)
- xmlWriter.newline()
- xmlWriter.endtag(name)
- xmlWriter.newline()
-
- def xmlRead(self, name, attrs, content, parent):
- if "name" in attrs:
- return attrs["name"]
- encoding = [".notdef"] * 256
- for element in content:
- if isinstance(element, str):
- continue
- name, attrs, content = element
- code = safeEval(attrs["code"])
- glyphName = attrs["name"]
- encoding[code] = glyphName
- return encoding
-
-
-def parseEncoding0(charset, file, haveSupplement, strings):
- nCodes = readCard8(file)
- encoding = [".notdef"] * 256
- for glyphID in range(1, nCodes + 1):
- code = readCard8(file)
- if code != 0:
- encoding[code] = charset[glyphID]
- return encoding
-
-
-def parseEncoding1(charset, file, haveSupplement, strings):
- nRanges = readCard8(file)
- encoding = [".notdef"] * 256
- glyphID = 1
- for i in range(nRanges):
- code = readCard8(file)
- nLeft = readCard8(file)
- for glyphID in range(glyphID, glyphID + nLeft + 1):
- encoding[code] = charset[glyphID]
- code = code + 1
- glyphID = glyphID + 1
- return encoding
-
-
-def packEncoding0(charset, encoding, strings):
- fmt = 0
- m = {}
- for code in range(len(encoding)):
- name = encoding[code]
- if name != ".notdef":
- m[name] = code
- codes = []
- for name in charset[1:]:
- code = m.get(name)
- codes.append(code)
-
- while codes and codes[-1] is None:
- codes.pop()
-
- data = [packCard8(fmt), packCard8(len(codes))]
- for code in codes:
- if code is None:
- code = 0
- data.append(packCard8(code))
- return bytesjoin(data)
-
-
-def packEncoding1(charset, encoding, strings):
- fmt = 1
- m = {}
- for code in range(len(encoding)):
- name = encoding[code]
- if name != ".notdef":
- m[name] = code
- ranges = []
- first = None
- end = 0
- for name in charset[1:]:
- code = m.get(name, -1)
- if first is None:
- first = code
- elif end + 1 != code:
- nLeft = end - first
- ranges.append((first, nLeft))
- first = code
- end = code
- nLeft = end - first
- ranges.append((first, nLeft))
-
- # remove unencoded glyphs at the end.
- while ranges and ranges[-1][0] == -1:
- ranges.pop()
-
- data = [packCard8(fmt), packCard8(len(ranges))]
- for first, nLeft in ranges:
- if first == -1: # unencoded
- first = 0
- data.append(packCard8(first) + packCard8(nLeft))
- return bytesjoin(data)
-
-
-class FDArrayConverter(TableConverter):
- def _read(self, parent, value):
- try:
- vstore = parent.VarStore
- except AttributeError:
- vstore = None
- file = parent.file
- isCFF2 = parent._isCFF2
- file.seek(value)
- fdArray = FDArrayIndex(file, isCFF2=isCFF2)
- fdArray.vstore = vstore
- fdArray.strings = parent.strings
- fdArray.GlobalSubrs = parent.GlobalSubrs
- return fdArray
-
- def write(self, parent, value):
- return 0 # dummy value
-
- def xmlRead(self, name, attrs, content, parent):
- fdArray = FDArrayIndex()
- for element in content:
- if isinstance(element, str):
- continue
- name, attrs, content = element
- fdArray.fromXML(name, attrs, content)
- return fdArray
-
-
-class FDSelectConverter(SimpleConverter):
- def _read(self, parent, value):
- file = parent.file
- file.seek(value)
- fdSelect = FDSelect(file, parent.numGlyphs)
- return fdSelect
-
- def write(self, parent, value):
- return 0 # dummy value
-
- # The FDSelect glyph data is written out to XML in the charstring keys,
- # so we write out only the format selector
- def xmlWrite(self, xmlWriter, name, value):
- xmlWriter.simpletag(name, [("format", value.format)])
- xmlWriter.newline()
-
- def xmlRead(self, name, attrs, content, parent):
- fmt = safeEval(attrs["format"])
- file = None
- numGlyphs = None
- fdSelect = FDSelect(file, numGlyphs, fmt)
- return fdSelect
-
-
-class VarStoreConverter(SimpleConverter):
- def _read(self, parent, value):
- file = parent.file
- file.seek(value)
- varStore = VarStoreData(file)
- varStore.decompile()
- return varStore
-
- def write(self, parent, value):
- return 0 # dummy value
-
- def xmlWrite(self, xmlWriter, name, value):
- value.writeXML(xmlWriter, name)
-
- def xmlRead(self, name, attrs, content, parent):
- varStore = VarStoreData()
- varStore.xmlRead(name, attrs, content, parent)
- return varStore
-
-
-def packFDSelect0(fdSelectArray):
- fmt = 0
- data = [packCard8(fmt)]
- for index in fdSelectArray:
- data.append(packCard8(index))
- return bytesjoin(data)
-
-
-def packFDSelect3(fdSelectArray):
- fmt = 3
- fdRanges = []
- lenArray = len(fdSelectArray)
- lastFDIndex = -1
- for i in range(lenArray):
- fdIndex = fdSelectArray[i]
- if lastFDIndex != fdIndex:
- fdRanges.append([i, fdIndex])
- lastFDIndex = fdIndex
- sentinelGID = i + 1
-
- data = [packCard8(fmt)]
- data.append(packCard16(len(fdRanges)))
- for fdRange in fdRanges:
- data.append(packCard16(fdRange[0]))
- data.append(packCard8(fdRange[1]))
- data.append(packCard16(sentinelGID))
- return bytesjoin(data)
-
-
-def packFDSelect4(fdSelectArray):
- fmt = 4
- fdRanges = []
- lenArray = len(fdSelectArray)
- lastFDIndex = -1
- for i in range(lenArray):
- fdIndex = fdSelectArray[i]
- if lastFDIndex != fdIndex:
- fdRanges.append([i, fdIndex])
- lastFDIndex = fdIndex
- sentinelGID = i + 1
-
- data = [packCard8(fmt)]
- data.append(packCard32(len(fdRanges)))
- for fdRange in fdRanges:
- data.append(packCard32(fdRange[0]))
- data.append(packCard16(fdRange[1]))
- data.append(packCard32(sentinelGID))
- return bytesjoin(data)
-
-
-class FDSelectCompiler(object):
- def __init__(self, fdSelect, parent):
- fmt = fdSelect.format
- fdSelectArray = fdSelect.gidArray
- if fmt == 0:
- self.data = packFDSelect0(fdSelectArray)
- elif fmt == 3:
- self.data = packFDSelect3(fdSelectArray)
- elif fmt == 4:
- self.data = packFDSelect4(fdSelectArray)
- else:
- # choose smaller of the two formats
- data0 = packFDSelect0(fdSelectArray)
- data3 = packFDSelect3(fdSelectArray)
- if len(data0) < len(data3):
- self.data = data0
- fdSelect.format = 0
- else:
- self.data = data3
- fdSelect.format = 3
-
- self.parent = parent
-
- def setPos(self, pos, endPos):
- self.parent.rawDict["FDSelect"] = pos
-
- def getDataLength(self):
- return len(self.data)
-
- def toFile(self, file):
- file.write(self.data)
-
-
-class VarStoreCompiler(object):
- def __init__(self, varStoreData, parent):
- self.parent = parent
- if not varStoreData.data:
- varStoreData.compile()
- data = [packCard16(len(varStoreData.data)), varStoreData.data]
- self.data = bytesjoin(data)
-
- def setPos(self, pos, endPos):
- self.parent.rawDict["VarStore"] = pos
-
- def getDataLength(self):
- return len(self.data)
-
- def toFile(self, file):
- file.write(self.data)
-
-
-class ROSConverter(SimpleConverter):
- def xmlWrite(self, xmlWriter, name, value):
- registry, order, supplement = value
- xmlWriter.simpletag(
- name,
- [
- ("Registry", tostr(registry)),
- ("Order", tostr(order)),
- ("Supplement", supplement),
- ],
- )
- xmlWriter.newline()
-
- def xmlRead(self, name, attrs, content, parent):
- return (attrs["Registry"], attrs["Order"], safeEval(attrs["Supplement"]))
-
-
-topDictOperators = [
- # opcode name argument type default converter
- (25, "maxstack", "number", None, None),
- ((12, 30), "ROS", ("SID", "SID", "number"), None, ROSConverter()),
- ((12, 20), "SyntheticBase", "number", None, None),
- (0, "version", "SID", None, None),
- (1, "Notice", "SID", None, Latin1Converter()),
- ((12, 0), "Copyright", "SID", None, Latin1Converter()),
- (2, "FullName", "SID", None, None),
- ((12, 38), "FontName", "SID", None, None),
- (3, "FamilyName", "SID", None, None),
- (4, "Weight", "SID", None, None),
- ((12, 1), "isFixedPitch", "number", 0, None),
- ((12, 2), "ItalicAngle", "number", 0, None),
- ((12, 3), "UnderlinePosition", "number", -100, None),
- ((12, 4), "UnderlineThickness", "number", 50, None),
- ((12, 5), "PaintType", "number", 0, None),
- ((12, 6), "CharstringType", "number", 2, None),
- ((12, 7), "FontMatrix", "array", [0.001, 0, 0, 0.001, 0, 0], None),
- (13, "UniqueID", "number", None, None),
- (5, "FontBBox", "array", [0, 0, 0, 0], None),
- ((12, 8), "StrokeWidth", "number", 0, None),
- (14, "XUID", "array", None, None),
- ((12, 21), "PostScript", "SID", None, None),
- ((12, 22), "BaseFontName", "SID", None, None),
- ((12, 23), "BaseFontBlend", "delta", None, None),
- ((12, 31), "CIDFontVersion", "number", 0, None),
- ((12, 32), "CIDFontRevision", "number", 0, None),
- ((12, 33), "CIDFontType", "number", 0, None),
- ((12, 34), "CIDCount", "number", 8720, None),
- (15, "charset", "number", None, CharsetConverter()),
- ((12, 35), "UIDBase", "number", None, None),
- (16, "Encoding", "number", 0, EncodingConverter()),
- (18, "Private", ("number", "number"), None, PrivateDictConverter()),
- ((12, 37), "FDSelect", "number", None, FDSelectConverter()),
- ((12, 36), "FDArray", "number", None, FDArrayConverter()),
- (17, "CharStrings", "number", None, CharStringsConverter()),
- (24, "VarStore", "number", None, VarStoreConverter()),
-]
-
-topDictOperators2 = [
- # opcode name argument type default converter
- (25, "maxstack", "number", None, None),
- ((12, 7), "FontMatrix", "array", [0.001, 0, 0, 0.001, 0, 0], None),
- ((12, 37), "FDSelect", "number", None, FDSelectConverter()),
- ((12, 36), "FDArray", "number", None, FDArrayConverter()),
- (17, "CharStrings", "number", None, CharStringsConverter()),
- (24, "VarStore", "number", None, VarStoreConverter()),
-]
-
-# Note! FDSelect and FDArray must both preceed CharStrings in the output XML build order,
-# in order for the font to compile back from xml.
-
-kBlendDictOpName = "blend"
-blendOp = 23
-
-privateDictOperators = [
- # opcode name argument type default converter
- (22, "vsindex", "number", None, None),
- (
- blendOp,
- kBlendDictOpName,
- "blendList",
- None,
- None,
- ), # This is for reading to/from XML: it not written to CFF.
- (6, "BlueValues", "delta", None, None),
- (7, "OtherBlues", "delta", None, None),
- (8, "FamilyBlues", "delta", None, None),
- (9, "FamilyOtherBlues", "delta", None, None),
- ((12, 9), "BlueScale", "number", 0.039625, None),
- ((12, 10), "BlueShift", "number", 7, None),
- ((12, 11), "BlueFuzz", "number", 1, None),
- (10, "StdHW", "number", None, None),
- (11, "StdVW", "number", None, None),
- ((12, 12), "StemSnapH", "delta", None, None),
- ((12, 13), "StemSnapV", "delta", None, None),
- ((12, 14), "ForceBold", "number", 0, None),
- ((12, 15), "ForceBoldThreshold", "number", None, None), # deprecated
- ((12, 16), "lenIV", "number", None, None), # deprecated
- ((12, 17), "LanguageGroup", "number", 0, None),
- ((12, 18), "ExpansionFactor", "number", 0.06, None),
- ((12, 19), "initialRandomSeed", "number", 0, None),
- (20, "defaultWidthX", "number", 0, None),
- (21, "nominalWidthX", "number", 0, None),
- (19, "Subrs", "number", None, SubrsConverter()),
-]
-
-privateDictOperators2 = [
- # opcode name argument type default converter
- (22, "vsindex", "number", None, None),
- (
- blendOp,
- kBlendDictOpName,
- "blendList",
- None,
- None,
- ), # This is for reading to/from XML: it not written to CFF.
- (6, "BlueValues", "delta", None, None),
- (7, "OtherBlues", "delta", None, None),
- (8, "FamilyBlues", "delta", None, None),
- (9, "FamilyOtherBlues", "delta", None, None),
- ((12, 9), "BlueScale", "number", 0.039625, None),
- ((12, 10), "BlueShift", "number", 7, None),
- ((12, 11), "BlueFuzz", "number", 1, None),
- (10, "StdHW", "number", None, None),
- (11, "StdVW", "number", None, None),
- ((12, 12), "StemSnapH", "delta", None, None),
- ((12, 13), "StemSnapV", "delta", None, None),
- ((12, 17), "LanguageGroup", "number", 0, None),
- ((12, 18), "ExpansionFactor", "number", 0.06, None),
- (19, "Subrs", "number", None, SubrsConverter()),
-]
-
-
-def addConverters(table):
- for i in range(len(table)):
- op, name, arg, default, conv = table[i]
- if conv is not None:
- continue
- if arg in ("delta", "array"):
- conv = ArrayConverter()
- elif arg == "number":
- conv = NumberConverter()
- elif arg == "SID":
- conv = ASCIIConverter()
- elif arg == "blendList":
- conv = None
- else:
- assert False
- table[i] = op, name, arg, default, conv
-
-
-addConverters(privateDictOperators)
-addConverters(topDictOperators)
-
-
-class TopDictDecompiler(psCharStrings.DictDecompiler):
- operators = buildOperatorDict(topDictOperators)
-
-
-class PrivateDictDecompiler(psCharStrings.DictDecompiler):
- operators = buildOperatorDict(privateDictOperators)
-
-
-class DictCompiler(object):
- maxBlendStack = 0
-
- def __init__(self, dictObj, strings, parent, isCFF2=None):
- if strings:
- assert isinstance(strings, IndexedStrings)
- if isCFF2 is None and hasattr(parent, "isCFF2"):
- isCFF2 = parent.isCFF2
- assert isCFF2 is not None
- self.isCFF2 = isCFF2
- self.dictObj = dictObj
- self.strings = strings
- self.parent = parent
- rawDict = {}
- for name in dictObj.order:
- value = getattr(dictObj, name, None)
- if value is None:
- continue
- conv = dictObj.converters[name]
- value = conv.write(dictObj, value)
- if value == dictObj.defaults.get(name):
- continue
- rawDict[name] = value
- self.rawDict = rawDict
-
- def setPos(self, pos, endPos):
- pass
-
- def getDataLength(self):
- return len(self.compile("getDataLength"))
-
- def compile(self, reason):
- log.log(DEBUG, "-- compiling %s for %s", self.__class__.__name__, reason)
- rawDict = self.rawDict
- data = []
- for name in self.dictObj.order:
- value = rawDict.get(name)
- if value is None:
- continue
- op, argType = self.opcodes[name]
- if isinstance(argType, tuple):
- l = len(argType)
- assert len(value) == l, "value doesn't match arg type"
- for i in range(l):
- arg = argType[i]
- v = value[i]
- arghandler = getattr(self, "arg_" + arg)
- data.append(arghandler(v))
- else:
- arghandler = getattr(self, "arg_" + argType)
- data.append(arghandler(value))
- data.append(op)
- data = bytesjoin(data)
- return data
-
- def toFile(self, file):
- data = self.compile("toFile")
- file.write(data)
-
- def arg_number(self, num):
- if isinstance(num, list):
- data = [encodeNumber(val) for val in num]
- data.append(encodeNumber(1))
- data.append(bytechr(blendOp))
- datum = bytesjoin(data)
- else:
- datum = encodeNumber(num)
- return datum
-
- def arg_SID(self, s):
- return psCharStrings.encodeIntCFF(self.strings.getSID(s))
-
- def arg_array(self, value):
- data = []
- for num in value:
- data.append(self.arg_number(num))
- return bytesjoin(data)
-
- def arg_delta(self, value):
- if not value:
- return b""
- val0 = value[0]
- if isinstance(val0, list):
- data = self.arg_delta_blend(value)
- else:
- out = []
- last = 0
- for v in value:
- out.append(v - last)
- last = v
- data = []
- for num in out:
- data.append(encodeNumber(num))
- return bytesjoin(data)
-
- def arg_delta_blend(self, value):
- """A delta list with blend lists has to be *all* blend lists.
-
- The value is a list is arranged as follows::
-
- [
- [V0, d0..dn]
- [V1, d0..dn]
- ...
- [Vm, d0..dn]
- ]
-
- ``V`` is the absolute coordinate value from the default font, and ``d0-dn``
- are the delta values from the *n* regions. Each ``V`` is an absolute
- coordinate from the default font.
-
- We want to return a list::
-
- [
- [v0, v1..vm]
- [d0..dn]
- ...
- [d0..dn]
- numBlends
- blendOp
- ]
-
- where each ``v`` is relative to the previous default font value.
- """
- numMasters = len(value[0])
- numBlends = len(value)
- numStack = (numBlends * numMasters) + 1
- if numStack > self.maxBlendStack:
- # Figure out the max number of value we can blend
- # and divide this list up into chunks of that size.
-
- numBlendValues = int((self.maxBlendStack - 1) / numMasters)
- out = []
- while True:
- numVal = min(len(value), numBlendValues)
- if numVal == 0:
- break
- valList = value[0:numVal]
- out1 = self.arg_delta_blend(valList)
- out.extend(out1)
- value = value[numVal:]
- else:
- firstList = [0] * numBlends
- deltaList = [None] * numBlends
- i = 0
- prevVal = 0
- while i < numBlends:
- # For PrivateDict BlueValues, the default font
- # values are absolute, not relative.
- # Must convert these back to relative coordinates
- # befor writing to CFF2.
- defaultValue = value[i][0]
- firstList[i] = defaultValue - prevVal
- prevVal = defaultValue
- deltaList[i] = value[i][1:]
- i += 1
-
- relValueList = firstList
- for blendList in deltaList:
- relValueList.extend(blendList)
- out = [encodeNumber(val) for val in relValueList]
- out.append(encodeNumber(numBlends))
- out.append(bytechr(blendOp))
- return out
-
-
-def encodeNumber(num):
- if isinstance(num, float):
- return psCharStrings.encodeFloat(num)
- else:
- return psCharStrings.encodeIntCFF(num)
-
-
-class TopDictCompiler(DictCompiler):
- opcodes = buildOpcodeDict(topDictOperators)
-
- def getChildren(self, strings):
- isCFF2 = self.isCFF2
- children = []
- if self.dictObj.cff2GetGlyphOrder is None:
- if hasattr(self.dictObj, "charset") and self.dictObj.charset:
- if hasattr(self.dictObj, "ROS"): # aka isCID
- charsetCode = None
- else:
- charsetCode = getStdCharSet(self.dictObj.charset)
- if charsetCode is None:
- children.append(
- CharsetCompiler(strings, self.dictObj.charset, self)
- )
- else:
- self.rawDict["charset"] = charsetCode
- if hasattr(self.dictObj, "Encoding") and self.dictObj.Encoding:
- encoding = self.dictObj.Encoding
- if not isinstance(encoding, str):
- children.append(EncodingCompiler(strings, encoding, self))
- else:
- if hasattr(self.dictObj, "VarStore"):
- varStoreData = self.dictObj.VarStore
- varStoreComp = VarStoreCompiler(varStoreData, self)
- children.append(varStoreComp)
- if hasattr(self.dictObj, "FDSelect"):
- # I have not yet supported merging a ttx CFF-CID font, as there are
- # interesting issues about merging the FDArrays. Here I assume that
- # either the font was read from XML, and the FDSelect indices are all
- # in the charstring data, or the FDSelect array is already fully defined.
- fdSelect = self.dictObj.FDSelect
- # probably read in from XML; assume fdIndex in CharString data
- if len(fdSelect) == 0:
- charStrings = self.dictObj.CharStrings
- for name in self.dictObj.charset:
- fdSelect.append(charStrings[name].fdSelectIndex)
- fdSelectComp = FDSelectCompiler(fdSelect, self)
- children.append(fdSelectComp)
- if hasattr(self.dictObj, "CharStrings"):
- items = []
- charStrings = self.dictObj.CharStrings
- for name in self.dictObj.charset:
- items.append(charStrings[name])
- charStringsComp = CharStringsCompiler(items, strings, self, isCFF2=isCFF2)
- children.append(charStringsComp)
- if hasattr(self.dictObj, "FDArray"):
- # I have not yet supported merging a ttx CFF-CID font, as there are
- # interesting issues about merging the FDArrays. Here I assume that the
- # FDArray info is correct and complete.
- fdArrayIndexComp = self.dictObj.FDArray.getCompiler(strings, self)
- children.append(fdArrayIndexComp)
- children.extend(fdArrayIndexComp.getChildren(strings))
- if hasattr(self.dictObj, "Private"):
- privComp = self.dictObj.Private.getCompiler(strings, self)
- children.append(privComp)
- children.extend(privComp.getChildren(strings))
- return children
-
-
-class FontDictCompiler(DictCompiler):
- opcodes = buildOpcodeDict(topDictOperators)
-
- def __init__(self, dictObj, strings, parent, isCFF2=None):
- super(FontDictCompiler, self).__init__(dictObj, strings, parent, isCFF2=isCFF2)
- #
- # We now take some effort to detect if there were any key/value pairs
- # supplied that were ignored in the FontDict context, and issue a warning
- # for those cases.
- #
- ignoredNames = []
- dictObj = self.dictObj
- for name in sorted(set(dictObj.converters) - set(dictObj.order)):
- if name in dictObj.rawDict:
- # The font was directly read from binary. In this
- # case, we want to report *all* "useless" key/value
- # pairs that are in the font, not just the ones that
- # are different from the default.
- ignoredNames.append(name)
- else:
- # The font was probably read from a TTX file. We only
- # warn about keys whos value is not the default. The
- # ones that have the default value will not be written
- # to binary anyway.
- default = dictObj.defaults.get(name)
- if default is not None:
- conv = dictObj.converters[name]
- default = conv.read(dictObj, default)
- if getattr(dictObj, name, None) != default:
- ignoredNames.append(name)
- if ignoredNames:
- log.warning(
- "Some CFF FDArray/FontDict keys were ignored upon compile: "
- + " ".join(sorted(ignoredNames))
- )
-
- def getChildren(self, strings):
- children = []
- if hasattr(self.dictObj, "Private"):
- privComp = self.dictObj.Private.getCompiler(strings, self)
- children.append(privComp)
- children.extend(privComp.getChildren(strings))
- return children
-
-
-class PrivateDictCompiler(DictCompiler):
- maxBlendStack = maxStackLimit
- opcodes = buildOpcodeDict(privateDictOperators)
-
- def setPos(self, pos, endPos):
- size = endPos - pos
- self.parent.rawDict["Private"] = size, pos
- self.pos = pos
-
- def getChildren(self, strings):
- children = []
- if hasattr(self.dictObj, "Subrs"):
- children.append(self.dictObj.Subrs.getCompiler(strings, self))
- return children
-
-
-class BaseDict(object):
- def __init__(self, strings=None, file=None, offset=None, isCFF2=None):
- assert (isCFF2 is None) == (file is None)
- self.rawDict = {}
- self.skipNames = []
- self.strings = strings
- if file is None:
- return
- self._isCFF2 = isCFF2
- self.file = file
- if offset is not None:
- log.log(DEBUG, "loading %s at %s", self.__class__.__name__, offset)
- self.offset = offset
-
- def decompile(self, data):
- log.log(DEBUG, " length %s is %d", self.__class__.__name__, len(data))
- dec = self.decompilerClass(self.strings, self)
- dec.decompile(data)
- self.rawDict = dec.getDict()
- self.postDecompile()
-
- def postDecompile(self):
- pass
-
- def getCompiler(self, strings, parent, isCFF2=None):
- return self.compilerClass(self, strings, parent, isCFF2=isCFF2)
-
- def __getattr__(self, name):
- if name[:2] == name[-2:] == "__":
- # to make deepcopy() and pickle.load() work, we need to signal with
- # AttributeError that dunder methods like '__deepcopy__' or '__getstate__'
- # aren't implemented. For more details, see:
- # https://github.com/fonttools/fonttools/pull/1488
- raise AttributeError(name)
- value = self.rawDict.get(name, None)
- if value is None:
- value = self.defaults.get(name)
- if value is None:
- raise AttributeError(name)
- conv = self.converters[name]
- value = conv.read(self, value)
- setattr(self, name, value)
- return value
-
- def toXML(self, xmlWriter):
- for name in self.order:
- if name in self.skipNames:
- continue
- value = getattr(self, name, None)
- # XXX For "charset" we never skip calling xmlWrite even if the
- # value is None, so we always write the following XML comment:
- #
- #
- #
- # Charset is None when 'CFF ' table is imported from XML into an
- # empty TTFont(). By writing this comment all the time, we obtain
- # the same XML output whether roundtripping XML-to-XML or
- # dumping binary-to-XML
- if value is None and name != "charset":
- continue
- conv = self.converters[name]
- conv.xmlWrite(xmlWriter, name, value)
- ignoredNames = set(self.rawDict) - set(self.order)
- if ignoredNames:
- xmlWriter.comment(
- "some keys were ignored: %s" % " ".join(sorted(ignoredNames))
- )
- xmlWriter.newline()
-
- def fromXML(self, name, attrs, content):
- conv = self.converters[name]
- value = conv.xmlRead(name, attrs, content, self)
- setattr(self, name, value)
-
-
-class TopDict(BaseDict):
- """The ``TopDict`` represents the top-level dictionary holding font
- information. CFF2 tables contain a restricted set of top-level entries
- as described `here `_,
- but CFF tables may contain a wider range of information. This information
- can be accessed through attributes or through the dictionary returned
- through the ``rawDict`` property:
-
- .. code:: python
-
- font = tt["CFF "].cff[0]
- font.FamilyName
- # 'Linux Libertine O'
- font.rawDict["FamilyName"]
- # 'Linux Libertine O'
-
- More information is available in the CFF file's private dictionary, accessed
- via the ``Private`` property:
-
- .. code:: python
-
- tt["CFF "].cff[0].Private.BlueValues
- # [-15, 0, 515, 515, 666, 666]
-
- """
-
- defaults = buildDefaults(topDictOperators)
- converters = buildConverters(topDictOperators)
- compilerClass = TopDictCompiler
- order = buildOrder(topDictOperators)
- decompilerClass = TopDictDecompiler
-
- def __init__(
- self,
- strings=None,
- file=None,
- offset=None,
- GlobalSubrs=None,
- cff2GetGlyphOrder=None,
- isCFF2=None,
- ):
- super(TopDict, self).__init__(strings, file, offset, isCFF2=isCFF2)
- self.cff2GetGlyphOrder = cff2GetGlyphOrder
- self.GlobalSubrs = GlobalSubrs
- if isCFF2:
- self.defaults = buildDefaults(topDictOperators2)
- self.charset = cff2GetGlyphOrder()
- self.order = buildOrder(topDictOperators2)
- else:
- self.defaults = buildDefaults(topDictOperators)
- self.order = buildOrder(topDictOperators)
-
- def getGlyphOrder(self):
- """Returns a list of glyph names in the CFF font."""
- return self.charset
-
- def postDecompile(self):
- offset = self.rawDict.get("CharStrings")
- if offset is None:
- return
- # get the number of glyphs beforehand.
- self.file.seek(offset)
- if self._isCFF2:
- self.numGlyphs = readCard32(self.file)
- else:
- self.numGlyphs = readCard16(self.file)
-
- def toXML(self, xmlWriter):
- if hasattr(self, "CharStrings"):
- self.decompileAllCharStrings()
- if hasattr(self, "ROS"):
- self.skipNames = ["Encoding"]
- if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
- # these values have default values, but I only want them to show up
- # in CID fonts.
- self.skipNames = [
- "CIDFontVersion",
- "CIDFontRevision",
- "CIDFontType",
- "CIDCount",
- ]
- BaseDict.toXML(self, xmlWriter)
-
- def decompileAllCharStrings(self):
- # Make sure that all the Private Dicts have been instantiated.
- for i, charString in enumerate(self.CharStrings.values()):
- try:
- charString.decompile()
- except:
- log.error("Error in charstring %s", i)
- raise
-
- def recalcFontBBox(self):
- fontBBox = None
- for charString in self.CharStrings.values():
- bounds = charString.calcBounds(self.CharStrings)
- if bounds is not None:
- if fontBBox is not None:
- fontBBox = unionRect(fontBBox, bounds)
- else:
- fontBBox = bounds
-
- if fontBBox is None:
- self.FontBBox = self.defaults["FontBBox"][:]
- else:
- self.FontBBox = list(intRect(fontBBox))
-
-
-class FontDict(BaseDict):
- #
- # Since fonttools used to pass a lot of fields that are not relevant in the FDArray
- # FontDict, there are 'ttx' files in the wild that contain all these. These got in
- # the ttx files because fonttools writes explicit values for all the TopDict default
- # values. These are not actually illegal in the context of an FDArray FontDict - you
- # can legally, per spec, put any arbitrary key/value pair in a FontDict - but are
- # useless since current major company CFF interpreters ignore anything but the set
- # listed in this file. So, we just silently skip them. An exception is Weight: this
- # is not used by any interpreter, but some foundries have asked that this be
- # supported in FDArray FontDicts just to preserve information about the design when
- # the font is being inspected.
- #
- # On top of that, there are fonts out there that contain such useless FontDict values.
- #
- # By subclassing TopDict, we *allow* all key/values from TopDict, both when reading
- # from binary or when reading from XML, but by overriding `order` with a limited
- # list of names, we ensure that only the useful names ever get exported to XML and
- # ever get compiled into the binary font.
- #
- # We override compilerClass so we can warn about "useless" key/value pairs, either
- # from the original binary font or from TTX input.
- #
- # See:
- # - https://github.com/fonttools/fonttools/issues/740
- # - https://github.com/fonttools/fonttools/issues/601
- # - https://github.com/adobe-type-tools/afdko/issues/137
- #
- defaults = {}
- converters = buildConverters(topDictOperators)
- compilerClass = FontDictCompiler
- orderCFF = ["FontName", "FontMatrix", "Weight", "Private"]
- orderCFF2 = ["Private"]
- decompilerClass = TopDictDecompiler
-
- def __init__(
- self,
- strings=None,
- file=None,
- offset=None,
- GlobalSubrs=None,
- isCFF2=None,
- vstore=None,
- ):
- super(FontDict, self).__init__(strings, file, offset, isCFF2=isCFF2)
- self.vstore = vstore
- self.setCFF2(isCFF2)
-
- def setCFF2(self, isCFF2):
- # isCFF2 may be None.
- if isCFF2:
- self.order = self.orderCFF2
- self._isCFF2 = True
- else:
- self.order = self.orderCFF
- self._isCFF2 = False
-
-
-class PrivateDict(BaseDict):
- defaults = buildDefaults(privateDictOperators)
- converters = buildConverters(privateDictOperators)
- order = buildOrder(privateDictOperators)
- decompilerClass = PrivateDictDecompiler
- compilerClass = PrivateDictCompiler
-
- def __init__(self, strings=None, file=None, offset=None, isCFF2=None, vstore=None):
- super(PrivateDict, self).__init__(strings, file, offset, isCFF2=isCFF2)
- self.vstore = vstore
- if isCFF2:
- self.defaults = buildDefaults(privateDictOperators2)
- self.order = buildOrder(privateDictOperators2)
- # Provide dummy values. This avoids needing to provide
- # an isCFF2 state in a lot of places.
- self.nominalWidthX = self.defaultWidthX = None
- else:
- self.defaults = buildDefaults(privateDictOperators)
- self.order = buildOrder(privateDictOperators)
-
- @property
- def in_cff2(self):
- return self._isCFF2
-
- def getNumRegions(self, vi=None): # called from misc/psCharStrings.py
- # if getNumRegions is being called, we can assume that VarStore exists.
- if vi is None:
- if hasattr(self, "vsindex"):
- vi = self.vsindex
- else:
- vi = 0
- numRegions = self.vstore.getNumRegions(vi)
- return numRegions
-
-
-class IndexedStrings(object):
-
- """SID -> string mapping."""
-
- def __init__(self, file=None):
- if file is None:
- strings = []
- else:
- strings = [tostr(s, encoding="latin1") for s in Index(file, isCFF2=False)]
- self.strings = strings
-
- def getCompiler(self):
- return IndexedStringsCompiler(self, None, self, isCFF2=False)
-
- def __len__(self):
- return len(self.strings)
-
- def __getitem__(self, SID):
- if SID < cffStandardStringCount:
- return cffStandardStrings[SID]
- else:
- return self.strings[SID - cffStandardStringCount]
-
- def getSID(self, s):
- if not hasattr(self, "stringMapping"):
- self.buildStringMapping()
- s = tostr(s, encoding="latin1")
- if s in cffStandardStringMapping:
- SID = cffStandardStringMapping[s]
- elif s in self.stringMapping:
- SID = self.stringMapping[s]
- else:
- SID = len(self.strings) + cffStandardStringCount
- self.strings.append(s)
- self.stringMapping[s] = SID
- return SID
-
- def getStrings(self):
- return self.strings
-
- def buildStringMapping(self):
- self.stringMapping = {}
- for index in range(len(self.strings)):
- self.stringMapping[self.strings[index]] = index + cffStandardStringCount
-
-
-# The 391 Standard Strings as used in the CFF format.
-# from Adobe Technical None #5176, version 1.0, 18 March 1998
-
-cffStandardStrings = [
- ".notdef",
- "space",
- "exclam",
- "quotedbl",
- "numbersign",
- "dollar",
- "percent",
- "ampersand",
- "quoteright",
- "parenleft",
- "parenright",
- "asterisk",
- "plus",
- "comma",
- "hyphen",
- "period",
- "slash",
- "zero",
- "one",
- "two",
- "three",
- "four",
- "five",
- "six",
- "seven",
- "eight",
- "nine",
- "colon",
- "semicolon",
- "less",
- "equal",
- "greater",
- "question",
- "at",
- "A",
- "B",
- "C",
- "D",
- "E",
- "F",
- "G",
- "H",
- "I",
- "J",
- "K",
- "L",
- "M",
- "N",
- "O",
- "P",
- "Q",
- "R",
- "S",
- "T",
- "U",
- "V",
- "W",
- "X",
- "Y",
- "Z",
- "bracketleft",
- "backslash",
- "bracketright",
- "asciicircum",
- "underscore",
- "quoteleft",
- "a",
- "b",
- "c",
- "d",
- "e",
- "f",
- "g",
- "h",
- "i",
- "j",
- "k",
- "l",
- "m",
- "n",
- "o",
- "p",
- "q",
- "r",
- "s",
- "t",
- "u",
- "v",
- "w",
- "x",
- "y",
- "z",
- "braceleft",
- "bar",
- "braceright",
- "asciitilde",
- "exclamdown",
- "cent",
- "sterling",
- "fraction",
- "yen",
- "florin",
- "section",
- "currency",
- "quotesingle",
- "quotedblleft",
- "guillemotleft",
- "guilsinglleft",
- "guilsinglright",
- "fi",
- "fl",
- "endash",
- "dagger",
- "daggerdbl",
- "periodcentered",
- "paragraph",
- "bullet",
- "quotesinglbase",
- "quotedblbase",
- "quotedblright",
- "guillemotright",
- "ellipsis",
- "perthousand",
- "questiondown",
- "grave",
- "acute",
- "circumflex",
- "tilde",
- "macron",
- "breve",
- "dotaccent",
- "dieresis",
- "ring",
- "cedilla",
- "hungarumlaut",
- "ogonek",
- "caron",
- "emdash",
- "AE",
- "ordfeminine",
- "Lslash",
- "Oslash",
- "OE",
- "ordmasculine",
- "ae",
- "dotlessi",
- "lslash",
- "oslash",
- "oe",
- "germandbls",
- "onesuperior",
- "logicalnot",
- "mu",
- "trademark",
- "Eth",
- "onehalf",
- "plusminus",
- "Thorn",
- "onequarter",
- "divide",
- "brokenbar",
- "degree",
- "thorn",
- "threequarters",
- "twosuperior",
- "registered",
- "minus",
- "eth",
- "multiply",
- "threesuperior",
- "copyright",
- "Aacute",
- "Acircumflex",
- "Adieresis",
- "Agrave",
- "Aring",
- "Atilde",
- "Ccedilla",
- "Eacute",
- "Ecircumflex",
- "Edieresis",
- "Egrave",
- "Iacute",
- "Icircumflex",
- "Idieresis",
- "Igrave",
- "Ntilde",
- "Oacute",
- "Ocircumflex",
- "Odieresis",
- "Ograve",
- "Otilde",
- "Scaron",
- "Uacute",
- "Ucircumflex",
- "Udieresis",
- "Ugrave",
- "Yacute",
- "Ydieresis",
- "Zcaron",
- "aacute",
- "acircumflex",
- "adieresis",
- "agrave",
- "aring",
- "atilde",
- "ccedilla",
- "eacute",
- "ecircumflex",
- "edieresis",
- "egrave",
- "iacute",
- "icircumflex",
- "idieresis",
- "igrave",
- "ntilde",
- "oacute",
- "ocircumflex",
- "odieresis",
- "ograve",
- "otilde",
- "scaron",
- "uacute",
- "ucircumflex",
- "udieresis",
- "ugrave",
- "yacute",
- "ydieresis",
- "zcaron",
- "exclamsmall",
- "Hungarumlautsmall",
- "dollaroldstyle",
- "dollarsuperior",
- "ampersandsmall",
- "Acutesmall",
- "parenleftsuperior",
- "parenrightsuperior",
- "twodotenleader",
- "onedotenleader",
- "zerooldstyle",
- "oneoldstyle",
- "twooldstyle",
- "threeoldstyle",
- "fouroldstyle",
- "fiveoldstyle",
- "sixoldstyle",
- "sevenoldstyle",
- "eightoldstyle",
- "nineoldstyle",
- "commasuperior",
- "threequartersemdash",
- "periodsuperior",
- "questionsmall",
- "asuperior",
- "bsuperior",
- "centsuperior",
- "dsuperior",
- "esuperior",
- "isuperior",
- "lsuperior",
- "msuperior",
- "nsuperior",
- "osuperior",
- "rsuperior",
- "ssuperior",
- "tsuperior",
- "ff",
- "ffi",
- "ffl",
- "parenleftinferior",
- "parenrightinferior",
- "Circumflexsmall",
- "hyphensuperior",
- "Gravesmall",
- "Asmall",
- "Bsmall",
- "Csmall",
- "Dsmall",
- "Esmall",
- "Fsmall",
- "Gsmall",
- "Hsmall",
- "Ismall",
- "Jsmall",
- "Ksmall",
- "Lsmall",
- "Msmall",
- "Nsmall",
- "Osmall",
- "Psmall",
- "Qsmall",
- "Rsmall",
- "Ssmall",
- "Tsmall",
- "Usmall",
- "Vsmall",
- "Wsmall",
- "Xsmall",
- "Ysmall",
- "Zsmall",
- "colonmonetary",
- "onefitted",
- "rupiah",
- "Tildesmall",
- "exclamdownsmall",
- "centoldstyle",
- "Lslashsmall",
- "Scaronsmall",
- "Zcaronsmall",
- "Dieresissmall",
- "Brevesmall",
- "Caronsmall",
- "Dotaccentsmall",
- "Macronsmall",
- "figuredash",
- "hypheninferior",
- "Ogoneksmall",
- "Ringsmall",
- "Cedillasmall",
- "questiondownsmall",
- "oneeighth",
- "threeeighths",
- "fiveeighths",
- "seveneighths",
- "onethird",
- "twothirds",
- "zerosuperior",
- "foursuperior",
- "fivesuperior",
- "sixsuperior",
- "sevensuperior",
- "eightsuperior",
- "ninesuperior",
- "zeroinferior",
- "oneinferior",
- "twoinferior",
- "threeinferior",
- "fourinferior",
- "fiveinferior",
- "sixinferior",
- "seveninferior",
- "eightinferior",
- "nineinferior",
- "centinferior",
- "dollarinferior",
- "periodinferior",
- "commainferior",
- "Agravesmall",
- "Aacutesmall",
- "Acircumflexsmall",
- "Atildesmall",
- "Adieresissmall",
- "Aringsmall",
- "AEsmall",
- "Ccedillasmall",
- "Egravesmall",
- "Eacutesmall",
- "Ecircumflexsmall",
- "Edieresissmall",
- "Igravesmall",
- "Iacutesmall",
- "Icircumflexsmall",
- "Idieresissmall",
- "Ethsmall",
- "Ntildesmall",
- "Ogravesmall",
- "Oacutesmall",
- "Ocircumflexsmall",
- "Otildesmall",
- "Odieresissmall",
- "OEsmall",
- "Oslashsmall",
- "Ugravesmall",
- "Uacutesmall",
- "Ucircumflexsmall",
- "Udieresissmall",
- "Yacutesmall",
- "Thornsmall",
- "Ydieresissmall",
- "001.000",
- "001.001",
- "001.002",
- "001.003",
- "Black",
- "Bold",
- "Book",
- "Light",
- "Medium",
- "Regular",
- "Roman",
- "Semibold",
-]
-
-cffStandardStringCount = 391
-assert len(cffStandardStrings) == cffStandardStringCount
-# build reverse mapping
-cffStandardStringMapping = {}
-for _i in range(cffStandardStringCount):
- cffStandardStringMapping[cffStandardStrings[_i]] = _i
-
-cffISOAdobeStrings = [
- ".notdef",
- "space",
- "exclam",
- "quotedbl",
- "numbersign",
- "dollar",
- "percent",
- "ampersand",
- "quoteright",
- "parenleft",
- "parenright",
- "asterisk",
- "plus",
- "comma",
- "hyphen",
- "period",
- "slash",
- "zero",
- "one",
- "two",
- "three",
- "four",
- "five",
- "six",
- "seven",
- "eight",
- "nine",
- "colon",
- "semicolon",
- "less",
- "equal",
- "greater",
- "question",
- "at",
- "A",
- "B",
- "C",
- "D",
- "E",
- "F",
- "G",
- "H",
- "I",
- "J",
- "K",
- "L",
- "M",
- "N",
- "O",
- "P",
- "Q",
- "R",
- "S",
- "T",
- "U",
- "V",
- "W",
- "X",
- "Y",
- "Z",
- "bracketleft",
- "backslash",
- "bracketright",
- "asciicircum",
- "underscore",
- "quoteleft",
- "a",
- "b",
- "c",
- "d",
- "e",
- "f",
- "g",
- "h",
- "i",
- "j",
- "k",
- "l",
- "m",
- "n",
- "o",
- "p",
- "q",
- "r",
- "s",
- "t",
- "u",
- "v",
- "w",
- "x",
- "y",
- "z",
- "braceleft",
- "bar",
- "braceright",
- "asciitilde",
- "exclamdown",
- "cent",
- "sterling",
- "fraction",
- "yen",
- "florin",
- "section",
- "currency",
- "quotesingle",
- "quotedblleft",
- "guillemotleft",
- "guilsinglleft",
- "guilsinglright",
- "fi",
- "fl",
- "endash",
- "dagger",
- "daggerdbl",
- "periodcentered",
- "paragraph",
- "bullet",
- "quotesinglbase",
- "quotedblbase",
- "quotedblright",
- "guillemotright",
- "ellipsis",
- "perthousand",
- "questiondown",
- "grave",
- "acute",
- "circumflex",
- "tilde",
- "macron",
- "breve",
- "dotaccent",
- "dieresis",
- "ring",
- "cedilla",
- "hungarumlaut",
- "ogonek",
- "caron",
- "emdash",
- "AE",
- "ordfeminine",
- "Lslash",
- "Oslash",
- "OE",
- "ordmasculine",
- "ae",
- "dotlessi",
- "lslash",
- "oslash",
- "oe",
- "germandbls",
- "onesuperior",
- "logicalnot",
- "mu",
- "trademark",
- "Eth",
- "onehalf",
- "plusminus",
- "Thorn",
- "onequarter",
- "divide",
- "brokenbar",
- "degree",
- "thorn",
- "threequarters",
- "twosuperior",
- "registered",
- "minus",
- "eth",
- "multiply",
- "threesuperior",
- "copyright",
- "Aacute",
- "Acircumflex",
- "Adieresis",
- "Agrave",
- "Aring",
- "Atilde",
- "Ccedilla",
- "Eacute",
- "Ecircumflex",
- "Edieresis",
- "Egrave",
- "Iacute",
- "Icircumflex",
- "Idieresis",
- "Igrave",
- "Ntilde",
- "Oacute",
- "Ocircumflex",
- "Odieresis",
- "Ograve",
- "Otilde",
- "Scaron",
- "Uacute",
- "Ucircumflex",
- "Udieresis",
- "Ugrave",
- "Yacute",
- "Ydieresis",
- "Zcaron",
- "aacute",
- "acircumflex",
- "adieresis",
- "agrave",
- "aring",
- "atilde",
- "ccedilla",
- "eacute",
- "ecircumflex",
- "edieresis",
- "egrave",
- "iacute",
- "icircumflex",
- "idieresis",
- "igrave",
- "ntilde",
- "oacute",
- "ocircumflex",
- "odieresis",
- "ograve",
- "otilde",
- "scaron",
- "uacute",
- "ucircumflex",
- "udieresis",
- "ugrave",
- "yacute",
- "ydieresis",
- "zcaron",
-]
-
-cffISOAdobeStringCount = 229
-assert len(cffISOAdobeStrings) == cffISOAdobeStringCount
-
-cffIExpertStrings = [
- ".notdef",
- "space",
- "exclamsmall",
- "Hungarumlautsmall",
- "dollaroldstyle",
- "dollarsuperior",
- "ampersandsmall",
- "Acutesmall",
- "parenleftsuperior",
- "parenrightsuperior",
- "twodotenleader",
- "onedotenleader",
- "comma",
- "hyphen",
- "period",
- "fraction",
- "zerooldstyle",
- "oneoldstyle",
- "twooldstyle",
- "threeoldstyle",
- "fouroldstyle",
- "fiveoldstyle",
- "sixoldstyle",
- "sevenoldstyle",
- "eightoldstyle",
- "nineoldstyle",
- "colon",
- "semicolon",
- "commasuperior",
- "threequartersemdash",
- "periodsuperior",
- "questionsmall",
- "asuperior",
- "bsuperior",
- "centsuperior",
- "dsuperior",
- "esuperior",
- "isuperior",
- "lsuperior",
- "msuperior",
- "nsuperior",
- "osuperior",
- "rsuperior",
- "ssuperior",
- "tsuperior",
- "ff",
- "fi",
- "fl",
- "ffi",
- "ffl",
- "parenleftinferior",
- "parenrightinferior",
- "Circumflexsmall",
- "hyphensuperior",
- "Gravesmall",
- "Asmall",
- "Bsmall",
- "Csmall",
- "Dsmall",
- "Esmall",
- "Fsmall",
- "Gsmall",
- "Hsmall",
- "Ismall",
- "Jsmall",
- "Ksmall",
- "Lsmall",
- "Msmall",
- "Nsmall",
- "Osmall",
- "Psmall",
- "Qsmall",
- "Rsmall",
- "Ssmall",
- "Tsmall",
- "Usmall",
- "Vsmall",
- "Wsmall",
- "Xsmall",
- "Ysmall",
- "Zsmall",
- "colonmonetary",
- "onefitted",
- "rupiah",
- "Tildesmall",
- "exclamdownsmall",
- "centoldstyle",
- "Lslashsmall",
- "Scaronsmall",
- "Zcaronsmall",
- "Dieresissmall",
- "Brevesmall",
- "Caronsmall",
- "Dotaccentsmall",
- "Macronsmall",
- "figuredash",
- "hypheninferior",
- "Ogoneksmall",
- "Ringsmall",
- "Cedillasmall",
- "onequarter",
- "onehalf",
- "threequarters",
- "questiondownsmall",
- "oneeighth",
- "threeeighths",
- "fiveeighths",
- "seveneighths",
- "onethird",
- "twothirds",
- "zerosuperior",
- "onesuperior",
- "twosuperior",
- "threesuperior",
- "foursuperior",
- "fivesuperior",
- "sixsuperior",
- "sevensuperior",
- "eightsuperior",
- "ninesuperior",
- "zeroinferior",
- "oneinferior",
- "twoinferior",
- "threeinferior",
- "fourinferior",
- "fiveinferior",
- "sixinferior",
- "seveninferior",
- "eightinferior",
- "nineinferior",
- "centinferior",
- "dollarinferior",
- "periodinferior",
- "commainferior",
- "Agravesmall",
- "Aacutesmall",
- "Acircumflexsmall",
- "Atildesmall",
- "Adieresissmall",
- "Aringsmall",
- "AEsmall",
- "Ccedillasmall",
- "Egravesmall",
- "Eacutesmall",
- "Ecircumflexsmall",
- "Edieresissmall",
- "Igravesmall",
- "Iacutesmall",
- "Icircumflexsmall",
- "Idieresissmall",
- "Ethsmall",
- "Ntildesmall",
- "Ogravesmall",
- "Oacutesmall",
- "Ocircumflexsmall",
- "Otildesmall",
- "Odieresissmall",
- "OEsmall",
- "Oslashsmall",
- "Ugravesmall",
- "Uacutesmall",
- "Ucircumflexsmall",
- "Udieresissmall",
- "Yacutesmall",
- "Thornsmall",
- "Ydieresissmall",
-]
-
-cffExpertStringCount = 166
-assert len(cffIExpertStrings) == cffExpertStringCount
-
-cffExpertSubsetStrings = [
- ".notdef",
- "space",
- "dollaroldstyle",
- "dollarsuperior",
- "parenleftsuperior",
- "parenrightsuperior",
- "twodotenleader",
- "onedotenleader",
- "comma",
- "hyphen",
- "period",
- "fraction",
- "zerooldstyle",
- "oneoldstyle",
- "twooldstyle",
- "threeoldstyle",
- "fouroldstyle",
- "fiveoldstyle",
- "sixoldstyle",
- "sevenoldstyle",
- "eightoldstyle",
- "nineoldstyle",
- "colon",
- "semicolon",
- "commasuperior",
- "threequartersemdash",
- "periodsuperior",
- "asuperior",
- "bsuperior",
- "centsuperior",
- "dsuperior",
- "esuperior",
- "isuperior",
- "lsuperior",
- "msuperior",
- "nsuperior",
- "osuperior",
- "rsuperior",
- "ssuperior",
- "tsuperior",
- "ff",
- "fi",
- "fl",
- "ffi",
- "ffl",
- "parenleftinferior",
- "parenrightinferior",
- "hyphensuperior",
- "colonmonetary",
- "onefitted",
- "rupiah",
- "centoldstyle",
- "figuredash",
- "hypheninferior",
- "onequarter",
- "onehalf",
- "threequarters",
- "oneeighth",
- "threeeighths",
- "fiveeighths",
- "seveneighths",
- "onethird",
- "twothirds",
- "zerosuperior",
- "onesuperior",
- "twosuperior",
- "threesuperior",
- "foursuperior",
- "fivesuperior",
- "sixsuperior",
- "sevensuperior",
- "eightsuperior",
- "ninesuperior",
- "zeroinferior",
- "oneinferior",
- "twoinferior",
- "threeinferior",
- "fourinferior",
- "fiveinferior",
- "sixinferior",
- "seveninferior",
- "eightinferior",
- "nineinferior",
- "centinferior",
- "dollarinferior",
- "periodinferior",
- "commainferior",
-]
-
-cffExpertSubsetStringCount = 87
-assert len(cffExpertSubsetStrings) == cffExpertSubsetStringCount
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cffLib/specializer.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cffLib/specializer.py
deleted file mode 100644
index f3645bf..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cffLib/specializer.py
+++ /dev/null
@@ -1,849 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""T2CharString operator specializer and generalizer.
-
-PostScript glyph drawing operations can be expressed in multiple different
-ways. For example, as well as the ``lineto`` operator, there is also a
-``hlineto`` operator which draws a horizontal line, removing the need to
-specify a ``dx`` coordinate, and a ``vlineto`` operator which draws a
-vertical line, removing the need to specify a ``dy`` coordinate. As well
-as decompiling :class:`fontTools.misc.psCharStrings.T2CharString` objects
-into lists of operations, this module allows for conversion between general
-and specific forms of the operation.
-
-"""
-
-from fontTools.cffLib import maxStackLimit
-
-
-def stringToProgram(string):
- if isinstance(string, str):
- string = string.split()
- program = []
- for token in string:
- try:
- token = int(token)
- except ValueError:
- try:
- token = float(token)
- except ValueError:
- pass
- program.append(token)
- return program
-
-
-def programToString(program):
- return " ".join(str(x) for x in program)
-
-
-def programToCommands(program, getNumRegions=None):
- """Takes a T2CharString program list and returns list of commands.
- Each command is a two-tuple of commandname,arg-list. The commandname might
- be empty string if no commandname shall be emitted (used for glyph width,
- hintmask/cntrmask argument, as well as stray arguments at the end of the
- program (¯\_(ツ)_/¯).
- 'getNumRegions' may be None, or a callable object. It must return the
- number of regions. 'getNumRegions' takes a single argument, vsindex. If
- the vsindex argument is None, getNumRegions returns the default number
- of regions for the charstring, else it returns the numRegions for
- the vsindex.
- The Charstring may or may not start with a width value. If the first
- non-blend operator has an odd number of arguments, then the first argument is
- a width, and is popped off. This is complicated with blend operators, as
- there may be more than one before the first hint or moveto operator, and each
- one reduces several arguments to just one list argument. We have to sum the
- number of arguments that are not part of the blend arguments, and all the
- 'numBlends' values. We could instead have said that by definition, if there
- is a blend operator, there is no width value, since CFF2 Charstrings don't
- have width values. I discussed this with Behdad, and we are allowing for an
- initial width value in this case because developers may assemble a CFF2
- charstring from CFF Charstrings, which could have width values.
- """
-
- seenWidthOp = False
- vsIndex = None
- lenBlendStack = 0
- lastBlendIndex = 0
- commands = []
- stack = []
- it = iter(program)
-
- for token in it:
- if not isinstance(token, str):
- stack.append(token)
- continue
-
- if token == "blend":
- assert getNumRegions is not None
- numSourceFonts = 1 + getNumRegions(vsIndex)
- # replace the blend op args on the stack with a single list
- # containing all the blend op args.
- numBlends = stack[-1]
- numBlendArgs = numBlends * numSourceFonts + 1
- # replace first blend op by a list of the blend ops.
- stack[-numBlendArgs:] = [stack[-numBlendArgs:]]
- lenBlendStack += numBlends + len(stack) - 1
- lastBlendIndex = len(stack)
- # if a blend op exists, this is or will be a CFF2 charstring.
- continue
-
- elif token == "vsindex":
- vsIndex = stack[-1]
- assert type(vsIndex) is int
-
- elif (not seenWidthOp) and token in {
- "hstem",
- "hstemhm",
- "vstem",
- "vstemhm",
- "cntrmask",
- "hintmask",
- "hmoveto",
- "vmoveto",
- "rmoveto",
- "endchar",
- }:
- seenWidthOp = True
- parity = token in {"hmoveto", "vmoveto"}
- if lenBlendStack:
- # lenBlendStack has the number of args represented by the last blend
- # arg and all the preceding args. We need to now add the number of
- # args following the last blend arg.
- numArgs = lenBlendStack + len(stack[lastBlendIndex:])
- else:
- numArgs = len(stack)
- if numArgs and (numArgs % 2) ^ parity:
- width = stack.pop(0)
- commands.append(("", [width]))
-
- if token in {"hintmask", "cntrmask"}:
- if stack:
- commands.append(("", stack))
- commands.append((token, []))
- commands.append(("", [next(it)]))
- else:
- commands.append((token, stack))
- stack = []
- if stack:
- commands.append(("", stack))
- return commands
-
-
-def _flattenBlendArgs(args):
- token_list = []
- for arg in args:
- if isinstance(arg, list):
- token_list.extend(arg)
- token_list.append("blend")
- else:
- token_list.append(arg)
- return token_list
-
-
-def commandsToProgram(commands):
- """Takes a commands list as returned by programToCommands() and converts
- it back to a T2CharString program list."""
- program = []
- for op, args in commands:
- if any(isinstance(arg, list) for arg in args):
- args = _flattenBlendArgs(args)
- program.extend(args)
- if op:
- program.append(op)
- return program
-
-
-def _everyN(el, n):
- """Group the list el into groups of size n"""
- if len(el) % n != 0:
- raise ValueError(el)
- for i in range(0, len(el), n):
- yield el[i : i + n]
-
-
-class _GeneralizerDecombinerCommandsMap(object):
- @staticmethod
- def rmoveto(args):
- if len(args) != 2:
- raise ValueError(args)
- yield ("rmoveto", args)
-
- @staticmethod
- def hmoveto(args):
- if len(args) != 1:
- raise ValueError(args)
- yield ("rmoveto", [args[0], 0])
-
- @staticmethod
- def vmoveto(args):
- if len(args) != 1:
- raise ValueError(args)
- yield ("rmoveto", [0, args[0]])
-
- @staticmethod
- def rlineto(args):
- if not args:
- raise ValueError(args)
- for args in _everyN(args, 2):
- yield ("rlineto", args)
-
- @staticmethod
- def hlineto(args):
- if not args:
- raise ValueError(args)
- it = iter(args)
- try:
- while True:
- yield ("rlineto", [next(it), 0])
- yield ("rlineto", [0, next(it)])
- except StopIteration:
- pass
-
- @staticmethod
- def vlineto(args):
- if not args:
- raise ValueError(args)
- it = iter(args)
- try:
- while True:
- yield ("rlineto", [0, next(it)])
- yield ("rlineto", [next(it), 0])
- except StopIteration:
- pass
-
- @staticmethod
- def rrcurveto(args):
- if not args:
- raise ValueError(args)
- for args in _everyN(args, 6):
- yield ("rrcurveto", args)
-
- @staticmethod
- def hhcurveto(args):
- if len(args) < 4 or len(args) % 4 > 1:
- raise ValueError(args)
- if len(args) % 2 == 1:
- yield ("rrcurveto", [args[1], args[0], args[2], args[3], args[4], 0])
- args = args[5:]
- for args in _everyN(args, 4):
- yield ("rrcurveto", [args[0], 0, args[1], args[2], args[3], 0])
-
- @staticmethod
- def vvcurveto(args):
- if len(args) < 4 or len(args) % 4 > 1:
- raise ValueError(args)
- if len(args) % 2 == 1:
- yield ("rrcurveto", [args[0], args[1], args[2], args[3], 0, args[4]])
- args = args[5:]
- for args in _everyN(args, 4):
- yield ("rrcurveto", [0, args[0], args[1], args[2], 0, args[3]])
-
- @staticmethod
- def hvcurveto(args):
- if len(args) < 4 or len(args) % 8 not in {0, 1, 4, 5}:
- raise ValueError(args)
- last_args = None
- if len(args) % 2 == 1:
- lastStraight = len(args) % 8 == 5
- args, last_args = args[:-5], args[-5:]
- it = _everyN(args, 4)
- try:
- while True:
- args = next(it)
- yield ("rrcurveto", [args[0], 0, args[1], args[2], 0, args[3]])
- args = next(it)
- yield ("rrcurveto", [0, args[0], args[1], args[2], args[3], 0])
- except StopIteration:
- pass
- if last_args:
- args = last_args
- if lastStraight:
- yield ("rrcurveto", [args[0], 0, args[1], args[2], args[4], args[3]])
- else:
- yield ("rrcurveto", [0, args[0], args[1], args[2], args[3], args[4]])
-
- @staticmethod
- def vhcurveto(args):
- if len(args) < 4 or len(args) % 8 not in {0, 1, 4, 5}:
- raise ValueError(args)
- last_args = None
- if len(args) % 2 == 1:
- lastStraight = len(args) % 8 == 5
- args, last_args = args[:-5], args[-5:]
- it = _everyN(args, 4)
- try:
- while True:
- args = next(it)
- yield ("rrcurveto", [0, args[0], args[1], args[2], args[3], 0])
- args = next(it)
- yield ("rrcurveto", [args[0], 0, args[1], args[2], 0, args[3]])
- except StopIteration:
- pass
- if last_args:
- args = last_args
- if lastStraight:
- yield ("rrcurveto", [0, args[0], args[1], args[2], args[3], args[4]])
- else:
- yield ("rrcurveto", [args[0], 0, args[1], args[2], args[4], args[3]])
-
- @staticmethod
- def rcurveline(args):
- if len(args) < 8 or len(args) % 6 != 2:
- raise ValueError(args)
- args, last_args = args[:-2], args[-2:]
- for args in _everyN(args, 6):
- yield ("rrcurveto", args)
- yield ("rlineto", last_args)
-
- @staticmethod
- def rlinecurve(args):
- if len(args) < 8 or len(args) % 2 != 0:
- raise ValueError(args)
- args, last_args = args[:-6], args[-6:]
- for args in _everyN(args, 2):
- yield ("rlineto", args)
- yield ("rrcurveto", last_args)
-
-
-def _convertBlendOpToArgs(blendList):
- # args is list of blend op args. Since we are supporting
- # recursive blend op calls, some of these args may also
- # be a list of blend op args, and need to be converted before
- # we convert the current list.
- if any([isinstance(arg, list) for arg in blendList]):
- args = [
- i
- for e in blendList
- for i in (_convertBlendOpToArgs(e) if isinstance(e, list) else [e])
- ]
- else:
- args = blendList
-
- # We now know that blendList contains a blend op argument list, even if
- # some of the args are lists that each contain a blend op argument list.
- # Convert from:
- # [default font arg sequence x0,...,xn] + [delta tuple for x0] + ... + [delta tuple for xn]
- # to:
- # [ [x0] + [delta tuple for x0],
- # ...,
- # [xn] + [delta tuple for xn] ]
- numBlends = args[-1]
- # Can't use args.pop() when the args are being used in a nested list
- # comprehension. See calling context
- args = args[:-1]
-
- numRegions = len(args) // numBlends - 1
- if not (numBlends * (numRegions + 1) == len(args)):
- raise ValueError(blendList)
-
- defaultArgs = [[arg] for arg in args[:numBlends]]
- deltaArgs = args[numBlends:]
- numDeltaValues = len(deltaArgs)
- deltaList = [
- deltaArgs[i : i + numRegions] for i in range(0, numDeltaValues, numRegions)
- ]
- blend_args = [a + b + [1] for a, b in zip(defaultArgs, deltaList)]
- return blend_args
-
-
-def generalizeCommands(commands, ignoreErrors=False):
- result = []
- mapping = _GeneralizerDecombinerCommandsMap
- for op, args in commands:
- # First, generalize any blend args in the arg list.
- if any([isinstance(arg, list) for arg in args]):
- try:
- args = [
- n
- for arg in args
- for n in (
- _convertBlendOpToArgs(arg) if isinstance(arg, list) else [arg]
- )
- ]
- except ValueError:
- if ignoreErrors:
- # Store op as data, such that consumers of commands do not have to
- # deal with incorrect number of arguments.
- result.append(("", args))
- result.append(("", [op]))
- else:
- raise
-
- func = getattr(mapping, op, None)
- if not func:
- result.append((op, args))
- continue
- try:
- for command in func(args):
- result.append(command)
- except ValueError:
- if ignoreErrors:
- # Store op as data, such that consumers of commands do not have to
- # deal with incorrect number of arguments.
- result.append(("", args))
- result.append(("", [op]))
- else:
- raise
- return result
-
-
-def generalizeProgram(program, getNumRegions=None, **kwargs):
- return commandsToProgram(
- generalizeCommands(programToCommands(program, getNumRegions), **kwargs)
- )
-
-
-def _categorizeVector(v):
- """
- Takes X,Y vector v and returns one of r, h, v, or 0 depending on which
- of X and/or Y are zero, plus tuple of nonzero ones. If both are zero,
- it returns a single zero still.
-
- >>> _categorizeVector((0,0))
- ('0', (0,))
- >>> _categorizeVector((1,0))
- ('h', (1,))
- >>> _categorizeVector((0,2))
- ('v', (2,))
- >>> _categorizeVector((1,2))
- ('r', (1, 2))
- """
- if not v[0]:
- if not v[1]:
- return "0", v[:1]
- else:
- return "v", v[1:]
- else:
- if not v[1]:
- return "h", v[:1]
- else:
- return "r", v
-
-
-def _mergeCategories(a, b):
- if a == "0":
- return b
- if b == "0":
- return a
- if a == b:
- return a
- return None
-
-
-def _negateCategory(a):
- if a == "h":
- return "v"
- if a == "v":
- return "h"
- assert a in "0r"
- return a
-
-
-def _convertToBlendCmds(args):
- # return a list of blend commands, and
- # the remaining non-blended args, if any.
- num_args = len(args)
- stack_use = 0
- new_args = []
- i = 0
- while i < num_args:
- arg = args[i]
- if not isinstance(arg, list):
- new_args.append(arg)
- i += 1
- stack_use += 1
- else:
- prev_stack_use = stack_use
- # The arg is a tuple of blend values.
- # These are each (master 0,delta 1..delta n, 1)
- # Combine as many successive tuples as we can,
- # up to the max stack limit.
- num_sources = len(arg) - 1
- blendlist = [arg]
- i += 1
- stack_use += 1 + num_sources # 1 for the num_blends arg
- while (i < num_args) and isinstance(args[i], list):
- blendlist.append(args[i])
- i += 1
- stack_use += num_sources
- if stack_use + num_sources > maxStackLimit:
- # if we are here, max stack is the CFF2 max stack.
- # I use the CFF2 max stack limit here rather than
- # the 'maxstack' chosen by the client, as the default
- # maxstack may have been used unintentionally. For all
- # the other operators, this just produces a little less
- # optimization, but here it puts a hard (and low) limit
- # on the number of source fonts that can be used.
- break
- # blendList now contains as many single blend tuples as can be
- # combined without exceeding the CFF2 stack limit.
- num_blends = len(blendlist)
- # append the 'num_blends' default font values
- blend_args = []
- for arg in blendlist:
- blend_args.append(arg[0])
- for arg in blendlist:
- assert arg[-1] == 1
- blend_args.extend(arg[1:-1])
- blend_args.append(num_blends)
- new_args.append(blend_args)
- stack_use = prev_stack_use + num_blends
-
- return new_args
-
-
-def _addArgs(a, b):
- if isinstance(b, list):
- if isinstance(a, list):
- if len(a) != len(b) or a[-1] != b[-1]:
- raise ValueError()
- return [_addArgs(va, vb) for va, vb in zip(a[:-1], b[:-1])] + [a[-1]]
- else:
- a, b = b, a
- if isinstance(a, list):
- assert a[-1] == 1
- return [_addArgs(a[0], b)] + a[1:]
- return a + b
-
-
-def specializeCommands(
- commands,
- ignoreErrors=False,
- generalizeFirst=True,
- preserveTopology=False,
- maxstack=48,
-):
- # We perform several rounds of optimizations. They are carefully ordered and are:
- #
- # 0. Generalize commands.
- # This ensures that they are in our expected simple form, with each line/curve only
- # having arguments for one segment, and using the generic form (rlineto/rrcurveto).
- # If caller is sure the input is in this form, they can turn off generalization to
- # save time.
- #
- # 1. Combine successive rmoveto operations.
- #
- # 2. Specialize rmoveto/rlineto/rrcurveto operators into horizontal/vertical variants.
- # We specialize into some, made-up, variants as well, which simplifies following
- # passes.
- #
- # 3. Merge or delete redundant operations, to the extent requested.
- # OpenType spec declares point numbers in CFF undefined. As such, we happily
- # change topology. If client relies on point numbers (in GPOS anchors, or for
- # hinting purposes(what?)) they can turn this off.
- #
- # 4. Peephole optimization to revert back some of the h/v variants back into their
- # original "relative" operator (rline/rrcurveto) if that saves a byte.
- #
- # 5. Combine adjacent operators when possible, minding not to go over max stack size.
- #
- # 6. Resolve any remaining made-up operators into real operators.
- #
- # I have convinced myself that this produces optimal bytecode (except for, possibly
- # one byte each time maxstack size prohibits combining.) YMMV, but you'd be wrong. :-)
- # A dynamic-programming approach can do the same but would be significantly slower.
- #
- # 7. For any args which are blend lists, convert them to a blend command.
-
- # 0. Generalize commands.
- if generalizeFirst:
- commands = generalizeCommands(commands, ignoreErrors=ignoreErrors)
- else:
- commands = list(commands) # Make copy since we modify in-place later.
-
- # 1. Combine successive rmoveto operations.
- for i in range(len(commands) - 1, 0, -1):
- if "rmoveto" == commands[i][0] == commands[i - 1][0]:
- v1, v2 = commands[i - 1][1], commands[i][1]
- commands[i - 1] = ("rmoveto", [v1[0] + v2[0], v1[1] + v2[1]])
- del commands[i]
-
- # 2. Specialize rmoveto/rlineto/rrcurveto operators into horizontal/vertical variants.
- #
- # We, in fact, specialize into more, made-up, variants that special-case when both
- # X and Y components are zero. This simplifies the following optimization passes.
- # This case is rare, but OCD does not let me skip it.
- #
- # After this round, we will have four variants that use the following mnemonics:
- #
- # - 'r' for relative, ie. non-zero X and non-zero Y,
- # - 'h' for horizontal, ie. zero X and non-zero Y,
- # - 'v' for vertical, ie. non-zero X and zero Y,
- # - '0' for zeros, ie. zero X and zero Y.
- #
- # The '0' pseudo-operators are not part of the spec, but help simplify the following
- # optimization rounds. We resolve them at the end. So, after this, we will have four
- # moveto and four lineto variants:
- #
- # - 0moveto, 0lineto
- # - hmoveto, hlineto
- # - vmoveto, vlineto
- # - rmoveto, rlineto
- #
- # and sixteen curveto variants. For example, a '0hcurveto' operator means a curve
- # dx0,dy0,dx1,dy1,dx2,dy2,dx3,dy3 where dx0, dx1, and dy3 are zero but not dx3.
- # An 'rvcurveto' means dx3 is zero but not dx0,dy0,dy3.
- #
- # There are nine different variants of curves without the '0'. Those nine map exactly
- # to the existing curve variants in the spec: rrcurveto, and the four variants hhcurveto,
- # vvcurveto, hvcurveto, and vhcurveto each cover two cases, one with an odd number of
- # arguments and one without. Eg. an hhcurveto with an extra argument (odd number of
- # arguments) is in fact an rhcurveto. The operators in the spec are designed such that
- # all four of rhcurveto, rvcurveto, hrcurveto, and vrcurveto are encodable for one curve.
- #
- # Of the curve types with '0', the 00curveto is equivalent to a lineto variant. The rest
- # of the curve types with a 0 need to be encoded as a h or v variant. Ie. a '0' can be
- # thought of a "don't care" and can be used as either an 'h' or a 'v'. As such, we always
- # encode a number 0 as argument when we use a '0' variant. Later on, we can just substitute
- # the '0' with either 'h' or 'v' and it works.
- #
- # When we get to curve splines however, things become more complicated... XXX finish this.
- # There's one more complexity with splines. If one side of the spline is not horizontal or
- # vertical (or zero), ie. if it's 'r', then it limits which spline types we can encode.
- # Only hhcurveto and vvcurveto operators can encode a spline starting with 'r', and
- # only hvcurveto and vhcurveto operators can encode a spline ending with 'r'.
- # This limits our merge opportunities later.
- #
- for i in range(len(commands)):
- op, args = commands[i]
-
- if op in {"rmoveto", "rlineto"}:
- c, args = _categorizeVector(args)
- commands[i] = c + op[1:], args
- continue
-
- if op == "rrcurveto":
- c1, args1 = _categorizeVector(args[:2])
- c2, args2 = _categorizeVector(args[-2:])
- commands[i] = c1 + c2 + "curveto", args1 + args[2:4] + args2
- continue
-
- # 3. Merge or delete redundant operations, to the extent requested.
- #
- # TODO
- # A 0moveto that comes before all other path operations can be removed.
- # though I find conflicting evidence for this.
- #
- # TODO
- # "If hstem and vstem hints are both declared at the beginning of a
- # CharString, and this sequence is followed directly by the hintmask or
- # cntrmask operators, then the vstem hint operator (or, if applicable,
- # the vstemhm operator) need not be included."
- #
- # "The sequence and form of a CFF2 CharString program may be represented as:
- # {hs* vs* cm* hm* mt subpath}? {mt subpath}*"
- #
- # https://www.microsoft.com/typography/otspec/cff2charstr.htm#section3.1
- #
- # For Type2 CharStrings the sequence is:
- # w? {hs* vs* cm* hm* mt subpath}? {mt subpath}* endchar"
-
- # Some other redundancies change topology (point numbers).
- if not preserveTopology:
- for i in range(len(commands) - 1, -1, -1):
- op, args = commands[i]
-
- # A 00curveto is demoted to a (specialized) lineto.
- if op == "00curveto":
- assert len(args) == 4
- c, args = _categorizeVector(args[1:3])
- op = c + "lineto"
- commands[i] = op, args
- # and then...
-
- # A 0lineto can be deleted.
- if op == "0lineto":
- del commands[i]
- continue
-
- # Merge adjacent hlineto's and vlineto's.
- # In CFF2 charstrings from variable fonts, each
- # arg item may be a list of blendable values, one from
- # each source font.
- if i and op in {"hlineto", "vlineto"} and (op == commands[i - 1][0]):
- _, other_args = commands[i - 1]
- assert len(args) == 1 and len(other_args) == 1
- try:
- new_args = [_addArgs(args[0], other_args[0])]
- except ValueError:
- continue
- commands[i - 1] = (op, new_args)
- del commands[i]
- continue
-
- # 4. Peephole optimization to revert back some of the h/v variants back into their
- # original "relative" operator (rline/rrcurveto) if that saves a byte.
- for i in range(1, len(commands) - 1):
- op, args = commands[i]
- prv, nxt = commands[i - 1][0], commands[i + 1][0]
-
- if op in {"0lineto", "hlineto", "vlineto"} and prv == nxt == "rlineto":
- assert len(args) == 1
- args = [0, args[0]] if op[0] == "v" else [args[0], 0]
- commands[i] = ("rlineto", args)
- continue
-
- if op[2:] == "curveto" and len(args) == 5 and prv == nxt == "rrcurveto":
- assert (op[0] == "r") ^ (op[1] == "r")
- if op[0] == "v":
- pos = 0
- elif op[0] != "r":
- pos = 1
- elif op[1] == "v":
- pos = 4
- else:
- pos = 5
- # Insert, while maintaining the type of args (can be tuple or list).
- args = args[:pos] + type(args)((0,)) + args[pos:]
- commands[i] = ("rrcurveto", args)
- continue
-
- # 5. Combine adjacent operators when possible, minding not to go over max stack size.
- for i in range(len(commands) - 1, 0, -1):
- op1, args1 = commands[i - 1]
- op2, args2 = commands[i]
- new_op = None
-
- # Merge logic...
- if {op1, op2} <= {"rlineto", "rrcurveto"}:
- if op1 == op2:
- new_op = op1
- else:
- if op2 == "rrcurveto" and len(args2) == 6:
- new_op = "rlinecurve"
- elif len(args2) == 2:
- new_op = "rcurveline"
-
- elif (op1, op2) in {("rlineto", "rlinecurve"), ("rrcurveto", "rcurveline")}:
- new_op = op2
-
- elif {op1, op2} == {"vlineto", "hlineto"}:
- new_op = op1
-
- elif "curveto" == op1[2:] == op2[2:]:
- d0, d1 = op1[:2]
- d2, d3 = op2[:2]
-
- if d1 == "r" or d2 == "r" or d0 == d3 == "r":
- continue
-
- d = _mergeCategories(d1, d2)
- if d is None:
- continue
- if d0 == "r":
- d = _mergeCategories(d, d3)
- if d is None:
- continue
- new_op = "r" + d + "curveto"
- elif d3 == "r":
- d0 = _mergeCategories(d0, _negateCategory(d))
- if d0 is None:
- continue
- new_op = d0 + "r" + "curveto"
- else:
- d0 = _mergeCategories(d0, d3)
- if d0 is None:
- continue
- new_op = d0 + d + "curveto"
-
- # Make sure the stack depth does not exceed (maxstack - 1), so
- # that subroutinizer can insert subroutine calls at any point.
- if new_op and len(args1) + len(args2) < maxstack:
- commands[i - 1] = (new_op, args1 + args2)
- del commands[i]
-
- # 6. Resolve any remaining made-up operators into real operators.
- for i in range(len(commands)):
- op, args = commands[i]
-
- if op in {"0moveto", "0lineto"}:
- commands[i] = "h" + op[1:], args
- continue
-
- if op[2:] == "curveto" and op[:2] not in {"rr", "hh", "vv", "vh", "hv"}:
- op0, op1 = op[:2]
- if (op0 == "r") ^ (op1 == "r"):
- assert len(args) % 2 == 1
- if op0 == "0":
- op0 = "h"
- if op1 == "0":
- op1 = "h"
- if op0 == "r":
- op0 = op1
- if op1 == "r":
- op1 = _negateCategory(op0)
- assert {op0, op1} <= {"h", "v"}, (op0, op1)
-
- if len(args) % 2:
- if op0 != op1: # vhcurveto / hvcurveto
- if (op0 == "h") ^ (len(args) % 8 == 1):
- # Swap last two args order
- args = args[:-2] + args[-1:] + args[-2:-1]
- else: # hhcurveto / vvcurveto
- if op0 == "h": # hhcurveto
- # Swap first two args order
- args = args[1:2] + args[:1] + args[2:]
-
- commands[i] = op0 + op1 + "curveto", args
- continue
-
- # 7. For any series of args which are blend lists, convert the series to a single blend arg.
- for i in range(len(commands)):
- op, args = commands[i]
- if any(isinstance(arg, list) for arg in args):
- commands[i] = op, _convertToBlendCmds(args)
-
- return commands
-
-
-def specializeProgram(program, getNumRegions=None, **kwargs):
- return commandsToProgram(
- specializeCommands(programToCommands(program, getNumRegions), **kwargs)
- )
-
-
-if __name__ == "__main__":
- import sys
-
- if len(sys.argv) == 1:
- import doctest
-
- sys.exit(doctest.testmod().failed)
-
- import argparse
-
- parser = argparse.ArgumentParser(
- "fonttools cffLib.specialer",
- description="CFF CharString generalizer/specializer",
- )
- parser.add_argument("program", metavar="command", nargs="*", help="Commands.")
- parser.add_argument(
- "--num-regions",
- metavar="NumRegions",
- nargs="*",
- default=None,
- help="Number of variable-font regions for blend opertaions.",
- )
-
- options = parser.parse_args(sys.argv[1:])
-
- getNumRegions = (
- None
- if options.num_regions is None
- else lambda vsIndex: int(options.num_regions[0 if vsIndex is None else vsIndex])
- )
-
- program = stringToProgram(options.program)
- print("Program:")
- print(programToString(program))
- commands = programToCommands(program, getNumRegions)
- print("Commands:")
- print(commands)
- program2 = commandsToProgram(commands)
- print("Program from commands:")
- print(programToString(program2))
- assert program == program2
- print("Generalized program:")
- print(programToString(generalizeProgram(program, getNumRegions)))
- print("Specialized program:")
- print(programToString(specializeProgram(program, getNumRegions)))
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cffLib/width.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cffLib/width.py
deleted file mode 100644
index 0ba3ed3..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cffLib/width.py
+++ /dev/null
@@ -1,207 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""T2CharString glyph width optimizer.
-
-CFF glyphs whose width equals the CFF Private dictionary's ``defaultWidthX``
-value do not need to specify their width in their charstring, saving bytes.
-This module determines the optimum ``defaultWidthX`` and ``nominalWidthX``
-values for a font, when provided with a list of glyph widths."""
-
-from fontTools.ttLib import TTFont
-from collections import defaultdict
-from operator import add
-from functools import reduce
-
-
-class missingdict(dict):
- def __init__(self, missing_func):
- self.missing_func = missing_func
-
- def __missing__(self, v):
- return self.missing_func(v)
-
-
-def cumSum(f, op=add, start=0, decreasing=False):
- keys = sorted(f.keys())
- minx, maxx = keys[0], keys[-1]
-
- total = reduce(op, f.values(), start)
-
- if decreasing:
- missing = lambda x: start if x > maxx else total
- domain = range(maxx, minx - 1, -1)
- else:
- missing = lambda x: start if x < minx else total
- domain = range(minx, maxx + 1)
-
- out = missingdict(missing)
-
- v = start
- for x in domain:
- v = op(v, f[x])
- out[x] = v
-
- return out
-
-
-def byteCost(widths, default, nominal):
- if not hasattr(widths, "items"):
- d = defaultdict(int)
- for w in widths:
- d[w] += 1
- widths = d
-
- cost = 0
- for w, freq in widths.items():
- if w == default:
- continue
- diff = abs(w - nominal)
- if diff <= 107:
- cost += freq
- elif diff <= 1131:
- cost += freq * 2
- else:
- cost += freq * 5
- return cost
-
-
-def optimizeWidthsBruteforce(widths):
- """Bruteforce version. Veeeeeeeeeeeeeeeeery slow. Only works for smallests of fonts."""
-
- d = defaultdict(int)
- for w in widths:
- d[w] += 1
-
- # Maximum number of bytes using default can possibly save
- maxDefaultAdvantage = 5 * max(d.values())
-
- minw, maxw = min(widths), max(widths)
- domain = list(range(minw, maxw + 1))
-
- bestCostWithoutDefault = min(byteCost(widths, None, nominal) for nominal in domain)
-
- bestCost = len(widths) * 5 + 1
- for nominal in domain:
- if byteCost(widths, None, nominal) > bestCost + maxDefaultAdvantage:
- continue
- for default in domain:
- cost = byteCost(widths, default, nominal)
- if cost < bestCost:
- bestCost = cost
- bestDefault = default
- bestNominal = nominal
-
- return bestDefault, bestNominal
-
-
-def optimizeWidths(widths):
- """Given a list of glyph widths, or dictionary mapping glyph width to number of
- glyphs having that, returns a tuple of best CFF default and nominal glyph widths.
-
- This algorithm is linear in UPEM+numGlyphs."""
-
- if not hasattr(widths, "items"):
- d = defaultdict(int)
- for w in widths:
- d[w] += 1
- widths = d
-
- keys = sorted(widths.keys())
- minw, maxw = keys[0], keys[-1]
- domain = list(range(minw, maxw + 1))
-
- # Cumulative sum/max forward/backward.
- cumFrqU = cumSum(widths, op=add)
- cumMaxU = cumSum(widths, op=max)
- cumFrqD = cumSum(widths, op=add, decreasing=True)
- cumMaxD = cumSum(widths, op=max, decreasing=True)
-
- # Cost per nominal choice, without default consideration.
- nomnCostU = missingdict(
- lambda x: cumFrqU[x] + cumFrqU[x - 108] + cumFrqU[x - 1132] * 3
- )
- nomnCostD = missingdict(
- lambda x: cumFrqD[x] + cumFrqD[x + 108] + cumFrqD[x + 1132] * 3
- )
- nomnCost = missingdict(lambda x: nomnCostU[x] + nomnCostD[x] - widths[x])
-
- # Cost-saving per nominal choice, by best default choice.
- dfltCostU = missingdict(
- lambda x: max(cumMaxU[x], cumMaxU[x - 108] * 2, cumMaxU[x - 1132] * 5)
- )
- dfltCostD = missingdict(
- lambda x: max(cumMaxD[x], cumMaxD[x + 108] * 2, cumMaxD[x + 1132] * 5)
- )
- dfltCost = missingdict(lambda x: max(dfltCostU[x], dfltCostD[x]))
-
- # Combined cost per nominal choice.
- bestCost = missingdict(lambda x: nomnCost[x] - dfltCost[x])
-
- # Best nominal.
- nominal = min(domain, key=lambda x: bestCost[x])
-
- # Work back the best default.
- bestC = bestCost[nominal]
- dfltC = nomnCost[nominal] - bestCost[nominal]
- ends = []
- if dfltC == dfltCostU[nominal]:
- starts = [nominal, nominal - 108, nominal - 1132]
- for start in starts:
- while cumMaxU[start] and cumMaxU[start] == cumMaxU[start - 1]:
- start -= 1
- ends.append(start)
- else:
- starts = [nominal, nominal + 108, nominal + 1132]
- for start in starts:
- while cumMaxD[start] and cumMaxD[start] == cumMaxD[start + 1]:
- start += 1
- ends.append(start)
- default = min(ends, key=lambda default: byteCost(widths, default, nominal))
-
- return default, nominal
-
-
-def main(args=None):
- """Calculate optimum defaultWidthX/nominalWidthX values"""
-
- import argparse
-
- parser = argparse.ArgumentParser(
- "fonttools cffLib.width",
- description=main.__doc__,
- )
- parser.add_argument(
- "inputs", metavar="FILE", type=str, nargs="+", help="Input TTF files"
- )
- parser.add_argument(
- "-b",
- "--brute-force",
- dest="brute",
- action="store_true",
- help="Use brute-force approach (VERY slow)",
- )
-
- args = parser.parse_args(args)
-
- for fontfile in args.inputs:
- font = TTFont(fontfile)
- hmtx = font["hmtx"]
- widths = [m[0] for m in hmtx.metrics.values()]
- if args.brute:
- default, nominal = optimizeWidthsBruteforce(widths)
- else:
- default, nominal = optimizeWidths(widths)
- print(
- "glyphs=%d default=%d nominal=%d byteCost=%d"
- % (len(widths), default, nominal, byteCost(widths, default, nominal))
- )
-
-
-if __name__ == "__main__":
- import sys
-
- if len(sys.argv) == 1:
- import doctest
-
- sys.exit(doctest.testmod().failed)
- main()
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/colorLib/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/colorLib/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/colorLib/builder.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/colorLib/builder.py
deleted file mode 100644
index 442bc20..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/colorLib/builder.py
+++ /dev/null
@@ -1,659 +0,0 @@
-"""
-colorLib.builder: Build COLR/CPAL tables from scratch
-
-"""
-import collections
-import copy
-import enum
-from functools import partial
-from math import ceil, log
-from typing import (
- Any,
- Dict,
- Generator,
- Iterable,
- List,
- Mapping,
- Optional,
- Sequence,
- Tuple,
- Type,
- TypeVar,
- Union,
-)
-from fontTools.misc.arrayTools import intRect
-from fontTools.misc.fixedTools import fixedToFloat
-from fontTools.misc.treeTools import build_n_ary_tree
-from fontTools.ttLib.tables import C_O_L_R_
-from fontTools.ttLib.tables import C_P_A_L_
-from fontTools.ttLib.tables import _n_a_m_e
-from fontTools.ttLib.tables import otTables as ot
-from fontTools.ttLib.tables.otTables import ExtendMode, CompositeMode
-from .errors import ColorLibError
-from .geometry import round_start_circle_stable_containment
-from .table_builder import BuildCallback, TableBuilder
-
-
-# TODO move type aliases to colorLib.types?
-T = TypeVar("T")
-_Kwargs = Mapping[str, Any]
-_PaintInput = Union[int, _Kwargs, ot.Paint, Tuple[str, "_PaintInput"]]
-_PaintInputList = Sequence[_PaintInput]
-_ColorGlyphsDict = Dict[str, Union[_PaintInputList, _PaintInput]]
-_ColorGlyphsV0Dict = Dict[str, Sequence[Tuple[str, int]]]
-_ClipBoxInput = Union[
- Tuple[int, int, int, int, int], # format 1, variable
- Tuple[int, int, int, int], # format 0, non-variable
- ot.ClipBox,
-]
-
-
-MAX_PAINT_COLR_LAYER_COUNT = 255
-_DEFAULT_ALPHA = 1.0
-_MAX_REUSE_LEN = 32
-
-
-def _beforeBuildPaintRadialGradient(paint, source):
- x0 = source["x0"]
- y0 = source["y0"]
- r0 = source["r0"]
- x1 = source["x1"]
- y1 = source["y1"]
- r1 = source["r1"]
-
- # TODO apparently no builder_test confirms this works (?)
-
- # avoid abrupt change after rounding when c0 is near c1's perimeter
- c = round_start_circle_stable_containment((x0, y0), r0, (x1, y1), r1)
- x0, y0 = c.centre
- r0 = c.radius
-
- # update source to ensure paint is built with corrected values
- source["x0"] = x0
- source["y0"] = y0
- source["r0"] = r0
- source["x1"] = x1
- source["y1"] = y1
- source["r1"] = r1
-
- return paint, source
-
-
-def _defaultColorStop():
- colorStop = ot.ColorStop()
- colorStop.Alpha = _DEFAULT_ALPHA
- return colorStop
-
-
-def _defaultVarColorStop():
- colorStop = ot.VarColorStop()
- colorStop.Alpha = _DEFAULT_ALPHA
- return colorStop
-
-
-def _defaultColorLine():
- colorLine = ot.ColorLine()
- colorLine.Extend = ExtendMode.PAD
- return colorLine
-
-
-def _defaultVarColorLine():
- colorLine = ot.VarColorLine()
- colorLine.Extend = ExtendMode.PAD
- return colorLine
-
-
-def _defaultPaintSolid():
- paint = ot.Paint()
- paint.Alpha = _DEFAULT_ALPHA
- return paint
-
-
-def _buildPaintCallbacks():
- return {
- (
- BuildCallback.BEFORE_BUILD,
- ot.Paint,
- ot.PaintFormat.PaintRadialGradient,
- ): _beforeBuildPaintRadialGradient,
- (
- BuildCallback.BEFORE_BUILD,
- ot.Paint,
- ot.PaintFormat.PaintVarRadialGradient,
- ): _beforeBuildPaintRadialGradient,
- (BuildCallback.CREATE_DEFAULT, ot.ColorStop): _defaultColorStop,
- (BuildCallback.CREATE_DEFAULT, ot.VarColorStop): _defaultVarColorStop,
- (BuildCallback.CREATE_DEFAULT, ot.ColorLine): _defaultColorLine,
- (BuildCallback.CREATE_DEFAULT, ot.VarColorLine): _defaultVarColorLine,
- (
- BuildCallback.CREATE_DEFAULT,
- ot.Paint,
- ot.PaintFormat.PaintSolid,
- ): _defaultPaintSolid,
- (
- BuildCallback.CREATE_DEFAULT,
- ot.Paint,
- ot.PaintFormat.PaintVarSolid,
- ): _defaultPaintSolid,
- }
-
-
-def populateCOLRv0(
- table: ot.COLR,
- colorGlyphsV0: _ColorGlyphsV0Dict,
- glyphMap: Optional[Mapping[str, int]] = None,
-):
- """Build v0 color layers and add to existing COLR table.
-
- Args:
- table: a raw ``otTables.COLR()`` object (not ttLib's ``table_C_O_L_R_``).
- colorGlyphsV0: map of base glyph names to lists of (layer glyph names,
- color palette index) tuples. Can be empty.
- glyphMap: a map from glyph names to glyph indices, as returned from
- ``TTFont.getReverseGlyphMap()``, to optionally sort base records by GID.
- """
- if glyphMap is not None:
- colorGlyphItems = sorted(
- colorGlyphsV0.items(), key=lambda item: glyphMap[item[0]]
- )
- else:
- colorGlyphItems = colorGlyphsV0.items()
- baseGlyphRecords = []
- layerRecords = []
- for baseGlyph, layers in colorGlyphItems:
- baseRec = ot.BaseGlyphRecord()
- baseRec.BaseGlyph = baseGlyph
- baseRec.FirstLayerIndex = len(layerRecords)
- baseRec.NumLayers = len(layers)
- baseGlyphRecords.append(baseRec)
-
- for layerGlyph, paletteIndex in layers:
- layerRec = ot.LayerRecord()
- layerRec.LayerGlyph = layerGlyph
- layerRec.PaletteIndex = paletteIndex
- layerRecords.append(layerRec)
-
- table.BaseGlyphRecordArray = table.LayerRecordArray = None
- if baseGlyphRecords:
- table.BaseGlyphRecordArray = ot.BaseGlyphRecordArray()
- table.BaseGlyphRecordArray.BaseGlyphRecord = baseGlyphRecords
- if layerRecords:
- table.LayerRecordArray = ot.LayerRecordArray()
- table.LayerRecordArray.LayerRecord = layerRecords
- table.BaseGlyphRecordCount = len(baseGlyphRecords)
- table.LayerRecordCount = len(layerRecords)
-
-
-def buildCOLR(
- colorGlyphs: _ColorGlyphsDict,
- version: Optional[int] = None,
- *,
- glyphMap: Optional[Mapping[str, int]] = None,
- varStore: Optional[ot.VarStore] = None,
- varIndexMap: Optional[ot.DeltaSetIndexMap] = None,
- clipBoxes: Optional[Dict[str, _ClipBoxInput]] = None,
- allowLayerReuse: bool = True,
-) -> C_O_L_R_.table_C_O_L_R_:
- """Build COLR table from color layers mapping.
-
- Args:
-
- colorGlyphs: map of base glyph name to, either list of (layer glyph name,
- color palette index) tuples for COLRv0; or a single ``Paint`` (dict) or
- list of ``Paint`` for COLRv1.
- version: the version of COLR table. If None, the version is determined
- by the presence of COLRv1 paints or variation data (varStore), which
- require version 1; otherwise, if all base glyphs use only simple color
- layers, version 0 is used.
- glyphMap: a map from glyph names to glyph indices, as returned from
- TTFont.getReverseGlyphMap(), to optionally sort base records by GID.
- varStore: Optional ItemVarationStore for deltas associated with v1 layer.
- varIndexMap: Optional DeltaSetIndexMap for deltas associated with v1 layer.
- clipBoxes: Optional map of base glyph name to clip box 4- or 5-tuples:
- (xMin, yMin, xMax, yMax) or (xMin, yMin, xMax, yMax, varIndexBase).
-
- Returns:
- A new COLR table.
- """
- self = C_O_L_R_.table_C_O_L_R_()
-
- if varStore is not None and version == 0:
- raise ValueError("Can't add VarStore to COLRv0")
-
- if version in (None, 0) and not varStore:
- # split color glyphs into v0 and v1 and encode separately
- colorGlyphsV0, colorGlyphsV1 = _split_color_glyphs_by_version(colorGlyphs)
- if version == 0 and colorGlyphsV1:
- raise ValueError("Can't encode COLRv1 glyphs in COLRv0")
- else:
- # unless explicitly requested for v1 or have variations, in which case
- # we encode all color glyph as v1
- colorGlyphsV0, colorGlyphsV1 = {}, colorGlyphs
-
- colr = ot.COLR()
-
- populateCOLRv0(colr, colorGlyphsV0, glyphMap)
-
- colr.LayerList, colr.BaseGlyphList = buildColrV1(
- colorGlyphsV1,
- glyphMap,
- allowLayerReuse=allowLayerReuse,
- )
-
- if version is None:
- version = 1 if (varStore or colorGlyphsV1) else 0
- elif version not in (0, 1):
- raise NotImplementedError(version)
- self.version = colr.Version = version
-
- if version == 0:
- self.ColorLayers = self._decompileColorLayersV0(colr)
- else:
- colr.ClipList = buildClipList(clipBoxes) if clipBoxes else None
- colr.VarIndexMap = varIndexMap
- colr.VarStore = varStore
- self.table = colr
-
- return self
-
-
-def buildClipList(clipBoxes: Dict[str, _ClipBoxInput]) -> ot.ClipList:
- clipList = ot.ClipList()
- clipList.Format = 1
- clipList.clips = {name: buildClipBox(box) for name, box in clipBoxes.items()}
- return clipList
-
-
-def buildClipBox(clipBox: _ClipBoxInput) -> ot.ClipBox:
- if isinstance(clipBox, ot.ClipBox):
- return clipBox
- n = len(clipBox)
- clip = ot.ClipBox()
- if n not in (4, 5):
- raise ValueError(f"Invalid ClipBox: expected 4 or 5 values, found {n}")
- clip.xMin, clip.yMin, clip.xMax, clip.yMax = intRect(clipBox[:4])
- clip.Format = int(n == 5) + 1
- if n == 5:
- clip.VarIndexBase = int(clipBox[4])
- return clip
-
-
-class ColorPaletteType(enum.IntFlag):
- USABLE_WITH_LIGHT_BACKGROUND = 0x0001
- USABLE_WITH_DARK_BACKGROUND = 0x0002
-
- @classmethod
- def _missing_(cls, value):
- # enforce reserved bits
- if isinstance(value, int) and (value < 0 or value & 0xFFFC != 0):
- raise ValueError(f"{value} is not a valid {cls.__name__}")
- return super()._missing_(value)
-
-
-# None, 'abc' or {'en': 'abc', 'de': 'xyz'}
-_OptionalLocalizedString = Union[None, str, Dict[str, str]]
-
-
-def buildPaletteLabels(
- labels: Iterable[_OptionalLocalizedString], nameTable: _n_a_m_e.table__n_a_m_e
-) -> List[Optional[int]]:
- return [
- nameTable.addMultilingualName(l, mac=False)
- if isinstance(l, dict)
- else C_P_A_L_.table_C_P_A_L_.NO_NAME_ID
- if l is None
- else nameTable.addMultilingualName({"en": l}, mac=False)
- for l in labels
- ]
-
-
-def buildCPAL(
- palettes: Sequence[Sequence[Tuple[float, float, float, float]]],
- paletteTypes: Optional[Sequence[ColorPaletteType]] = None,
- paletteLabels: Optional[Sequence[_OptionalLocalizedString]] = None,
- paletteEntryLabels: Optional[Sequence[_OptionalLocalizedString]] = None,
- nameTable: Optional[_n_a_m_e.table__n_a_m_e] = None,
-) -> C_P_A_L_.table_C_P_A_L_:
- """Build CPAL table from list of color palettes.
-
- Args:
- palettes: list of lists of colors encoded as tuples of (R, G, B, A) floats
- in the range [0..1].
- paletteTypes: optional list of ColorPaletteType, one for each palette.
- paletteLabels: optional list of palette labels. Each lable can be either:
- None (no label), a string (for for default English labels), or a
- localized string (as a dict keyed with BCP47 language codes).
- paletteEntryLabels: optional list of palette entry labels, one for each
- palette entry (see paletteLabels).
- nameTable: optional name table where to store palette and palette entry
- labels. Required if either paletteLabels or paletteEntryLabels is set.
-
- Return:
- A new CPAL v0 or v1 table, if custom palette types or labels are specified.
- """
- if len({len(p) for p in palettes}) != 1:
- raise ColorLibError("color palettes have different lengths")
-
- if (paletteLabels or paletteEntryLabels) and not nameTable:
- raise TypeError(
- "nameTable is required if palette or palette entries have labels"
- )
-
- cpal = C_P_A_L_.table_C_P_A_L_()
- cpal.numPaletteEntries = len(palettes[0])
-
- cpal.palettes = []
- for i, palette in enumerate(palettes):
- colors = []
- for j, color in enumerate(palette):
- if not isinstance(color, tuple) or len(color) != 4:
- raise ColorLibError(
- f"In palette[{i}][{j}]: expected (R, G, B, A) tuple, got {color!r}"
- )
- if any(v > 1 or v < 0 for v in color):
- raise ColorLibError(
- f"palette[{i}][{j}] has invalid out-of-range [0..1] color: {color!r}"
- )
- # input colors are RGBA, CPAL encodes them as BGRA
- red, green, blue, alpha = color
- colors.append(
- C_P_A_L_.Color(*(round(v * 255) for v in (blue, green, red, alpha)))
- )
- cpal.palettes.append(colors)
-
- if any(v is not None for v in (paletteTypes, paletteLabels, paletteEntryLabels)):
- cpal.version = 1
-
- if paletteTypes is not None:
- if len(paletteTypes) != len(palettes):
- raise ColorLibError(
- f"Expected {len(palettes)} paletteTypes, got {len(paletteTypes)}"
- )
- cpal.paletteTypes = [ColorPaletteType(t).value for t in paletteTypes]
- else:
- cpal.paletteTypes = [C_P_A_L_.table_C_P_A_L_.DEFAULT_PALETTE_TYPE] * len(
- palettes
- )
-
- if paletteLabels is not None:
- if len(paletteLabels) != len(palettes):
- raise ColorLibError(
- f"Expected {len(palettes)} paletteLabels, got {len(paletteLabels)}"
- )
- cpal.paletteLabels = buildPaletteLabels(paletteLabels, nameTable)
- else:
- cpal.paletteLabels = [C_P_A_L_.table_C_P_A_L_.NO_NAME_ID] * len(palettes)
-
- if paletteEntryLabels is not None:
- if len(paletteEntryLabels) != cpal.numPaletteEntries:
- raise ColorLibError(
- f"Expected {cpal.numPaletteEntries} paletteEntryLabels, "
- f"got {len(paletteEntryLabels)}"
- )
- cpal.paletteEntryLabels = buildPaletteLabels(paletteEntryLabels, nameTable)
- else:
- cpal.paletteEntryLabels = [
- C_P_A_L_.table_C_P_A_L_.NO_NAME_ID
- ] * cpal.numPaletteEntries
- else:
- cpal.version = 0
-
- return cpal
-
-
-# COLR v1 tables
-# See draft proposal at: https://github.com/googlefonts/colr-gradients-spec
-
-
-def _is_colrv0_layer(layer: Any) -> bool:
- # Consider as COLRv0 layer any sequence of length 2 (be it tuple or list) in which
- # the first element is a str (the layerGlyph) and the second element is an int
- # (CPAL paletteIndex).
- # https://github.com/googlefonts/ufo2ft/issues/426
- try:
- layerGlyph, paletteIndex = layer
- except (TypeError, ValueError):
- return False
- else:
- return isinstance(layerGlyph, str) and isinstance(paletteIndex, int)
-
-
-def _split_color_glyphs_by_version(
- colorGlyphs: _ColorGlyphsDict,
-) -> Tuple[_ColorGlyphsV0Dict, _ColorGlyphsDict]:
- colorGlyphsV0 = {}
- colorGlyphsV1 = {}
- for baseGlyph, layers in colorGlyphs.items():
- if all(_is_colrv0_layer(l) for l in layers):
- colorGlyphsV0[baseGlyph] = layers
- else:
- colorGlyphsV1[baseGlyph] = layers
-
- # sanity check
- assert set(colorGlyphs) == (set(colorGlyphsV0) | set(colorGlyphsV1))
-
- return colorGlyphsV0, colorGlyphsV1
-
-
-def _reuse_ranges(num_layers: int) -> Generator[Tuple[int, int], None, None]:
- # TODO feels like something itertools might have already
- for lbound in range(num_layers):
- # Reuse of very large #s of layers is relatively unlikely
- # +2: we want sequences of at least 2
- # otData handles single-record duplication
- for ubound in range(
- lbound + 2, min(num_layers + 1, lbound + 2 + _MAX_REUSE_LEN)
- ):
- yield (lbound, ubound)
-
-
-class LayerReuseCache:
- reusePool: Mapping[Tuple[Any, ...], int]
- tuples: Mapping[int, Tuple[Any, ...]]
- keepAlive: List[ot.Paint] # we need id to remain valid
-
- def __init__(self):
- self.reusePool = {}
- self.tuples = {}
- self.keepAlive = []
-
- def _paint_tuple(self, paint: ot.Paint):
- # start simple, who even cares about cyclic graphs or interesting field types
- def _tuple_safe(value):
- if isinstance(value, enum.Enum):
- return value
- elif hasattr(value, "__dict__"):
- return tuple(
- (k, _tuple_safe(v)) for k, v in sorted(value.__dict__.items())
- )
- elif isinstance(value, collections.abc.MutableSequence):
- return tuple(_tuple_safe(e) for e in value)
- return value
-
- # Cache the tuples for individual Paint instead of the whole sequence
- # because the seq could be a transient slice
- result = self.tuples.get(id(paint), None)
- if result is None:
- result = _tuple_safe(paint)
- self.tuples[id(paint)] = result
- self.keepAlive.append(paint)
- return result
-
- def _as_tuple(self, paints: Sequence[ot.Paint]) -> Tuple[Any, ...]:
- return tuple(self._paint_tuple(p) for p in paints)
-
- def try_reuse(self, layers: List[ot.Paint]) -> List[ot.Paint]:
- found_reuse = True
- while found_reuse:
- found_reuse = False
-
- ranges = sorted(
- _reuse_ranges(len(layers)),
- key=lambda t: (t[1] - t[0], t[1], t[0]),
- reverse=True,
- )
- for lbound, ubound in ranges:
- reuse_lbound = self.reusePool.get(
- self._as_tuple(layers[lbound:ubound]), -1
- )
- if reuse_lbound == -1:
- continue
- new_slice = ot.Paint()
- new_slice.Format = int(ot.PaintFormat.PaintColrLayers)
- new_slice.NumLayers = ubound - lbound
- new_slice.FirstLayerIndex = reuse_lbound
- layers = layers[:lbound] + [new_slice] + layers[ubound:]
- found_reuse = True
- break
- return layers
-
- def add(self, layers: List[ot.Paint], first_layer_index: int):
- for lbound, ubound in _reuse_ranges(len(layers)):
- self.reusePool[self._as_tuple(layers[lbound:ubound])] = (
- lbound + first_layer_index
- )
-
-
-class LayerListBuilder:
- layers: List[ot.Paint]
- cache: LayerReuseCache
- allowLayerReuse: bool
-
- def __init__(self, *, allowLayerReuse=True):
- self.layers = []
- if allowLayerReuse:
- self.cache = LayerReuseCache()
- else:
- self.cache = None
-
- # We need to intercept construction of PaintColrLayers
- callbacks = _buildPaintCallbacks()
- callbacks[
- (
- BuildCallback.BEFORE_BUILD,
- ot.Paint,
- ot.PaintFormat.PaintColrLayers,
- )
- ] = self._beforeBuildPaintColrLayers
- self.tableBuilder = TableBuilder(callbacks)
-
- # COLR layers is unusual in that it modifies shared state
- # so we need a callback into an object
- def _beforeBuildPaintColrLayers(self, dest, source):
- # Sketchy gymnastics: a sequence input will have dropped it's layers
- # into NumLayers; get it back
- if isinstance(source.get("NumLayers", None), collections.abc.Sequence):
- layers = source["NumLayers"]
- else:
- layers = source["Layers"]
-
- # Convert maps seqs or whatever into typed objects
- layers = [self.buildPaint(l) for l in layers]
-
- # No reason to have a colr layers with just one entry
- if len(layers) == 1:
- return layers[0], {}
-
- if self.cache is not None:
- # Look for reuse, with preference to longer sequences
- # This may make the layer list smaller
- layers = self.cache.try_reuse(layers)
-
- # The layer list is now final; if it's too big we need to tree it
- is_tree = len(layers) > MAX_PAINT_COLR_LAYER_COUNT
- layers = build_n_ary_tree(layers, n=MAX_PAINT_COLR_LAYER_COUNT)
-
- # We now have a tree of sequences with Paint leaves.
- # Convert the sequences into PaintColrLayers.
- def listToColrLayers(layer):
- if isinstance(layer, collections.abc.Sequence):
- return self.buildPaint(
- {
- "Format": ot.PaintFormat.PaintColrLayers,
- "Layers": [listToColrLayers(l) for l in layer],
- }
- )
- return layer
-
- layers = [listToColrLayers(l) for l in layers]
-
- # No reason to have a colr layers with just one entry
- if len(layers) == 1:
- return layers[0], {}
-
- paint = ot.Paint()
- paint.Format = int(ot.PaintFormat.PaintColrLayers)
- paint.NumLayers = len(layers)
- paint.FirstLayerIndex = len(self.layers)
- self.layers.extend(layers)
-
- # Register our parts for reuse provided we aren't a tree
- # If we are a tree the leaves registered for reuse and that will suffice
- if self.cache is not None and not is_tree:
- self.cache.add(layers, paint.FirstLayerIndex)
-
- # we've fully built dest; empty source prevents generalized build from kicking in
- return paint, {}
-
- def buildPaint(self, paint: _PaintInput) -> ot.Paint:
- return self.tableBuilder.build(ot.Paint, paint)
-
- def build(self) -> Optional[ot.LayerList]:
- if not self.layers:
- return None
- layers = ot.LayerList()
- layers.LayerCount = len(self.layers)
- layers.Paint = self.layers
- return layers
-
-
-def buildBaseGlyphPaintRecord(
- baseGlyph: str, layerBuilder: LayerListBuilder, paint: _PaintInput
-) -> ot.BaseGlyphList:
- self = ot.BaseGlyphPaintRecord()
- self.BaseGlyph = baseGlyph
- self.Paint = layerBuilder.buildPaint(paint)
- return self
-
-
-def _format_glyph_errors(errors: Mapping[str, Exception]) -> str:
- lines = []
- for baseGlyph, error in sorted(errors.items()):
- lines.append(f" {baseGlyph} => {type(error).__name__}: {error}")
- return "\n".join(lines)
-
-
-def buildColrV1(
- colorGlyphs: _ColorGlyphsDict,
- glyphMap: Optional[Mapping[str, int]] = None,
- *,
- allowLayerReuse: bool = True,
-) -> Tuple[Optional[ot.LayerList], ot.BaseGlyphList]:
- if glyphMap is not None:
- colorGlyphItems = sorted(
- colorGlyphs.items(), key=lambda item: glyphMap[item[0]]
- )
- else:
- colorGlyphItems = colorGlyphs.items()
-
- errors = {}
- baseGlyphs = []
- layerBuilder = LayerListBuilder(allowLayerReuse=allowLayerReuse)
- for baseGlyph, paint in colorGlyphItems:
- try:
- baseGlyphs.append(buildBaseGlyphPaintRecord(baseGlyph, layerBuilder, paint))
-
- except (ColorLibError, OverflowError, ValueError, TypeError) as e:
- errors[baseGlyph] = e
-
- if errors:
- failed_glyphs = _format_glyph_errors(errors)
- exc = ColorLibError(f"Failed to build BaseGlyphList:\n{failed_glyphs}")
- exc.errors = errors
- raise exc from next(iter(errors.values()))
-
- layers = layerBuilder.build()
- glyphs = ot.BaseGlyphList()
- glyphs.BaseGlyphCount = len(baseGlyphs)
- glyphs.BaseGlyphPaintRecord = baseGlyphs
- return (layers, glyphs)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/colorLib/errors.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/colorLib/errors.py
deleted file mode 100644
index 18cbebb..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/colorLib/errors.py
+++ /dev/null
@@ -1,2 +0,0 @@
-class ColorLibError(Exception):
- pass
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/colorLib/geometry.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/colorLib/geometry.py
deleted file mode 100644
index 1ce161b..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/colorLib/geometry.py
+++ /dev/null
@@ -1,143 +0,0 @@
-"""Helpers for manipulating 2D points and vectors in COLR table."""
-
-from math import copysign, cos, hypot, isclose, pi
-from fontTools.misc.roundTools import otRound
-
-
-def _vector_between(origin, target):
- return (target[0] - origin[0], target[1] - origin[1])
-
-
-def _round_point(pt):
- return (otRound(pt[0]), otRound(pt[1]))
-
-
-def _unit_vector(vec):
- length = hypot(*vec)
- if length == 0:
- return None
- return (vec[0] / length, vec[1] / length)
-
-
-_CIRCLE_INSIDE_TOLERANCE = 1e-4
-
-
-# The unit vector's X and Y components are respectively
-# U = (cos(α), sin(α))
-# where α is the angle between the unit vector and the positive x axis.
-_UNIT_VECTOR_THRESHOLD = cos(3 / 8 * pi) # == sin(1/8 * pi) == 0.38268343236508984
-
-
-def _rounding_offset(direction):
- # Return 2-tuple of -/+ 1.0 or 0.0 approximately based on the direction vector.
- # We divide the unit circle in 8 equal slices oriented towards the cardinal
- # (N, E, S, W) and intermediate (NE, SE, SW, NW) directions. To each slice we
- # map one of the possible cases: -1, 0, +1 for either X and Y coordinate.
- # E.g. Return (+1.0, -1.0) if unit vector is oriented towards SE, or
- # (-1.0, 0.0) if it's pointing West, etc.
- uv = _unit_vector(direction)
- if not uv:
- return (0, 0)
-
- result = []
- for uv_component in uv:
- if -_UNIT_VECTOR_THRESHOLD <= uv_component < _UNIT_VECTOR_THRESHOLD:
- # unit vector component near 0: direction almost orthogonal to the
- # direction of the current axis, thus keep coordinate unchanged
- result.append(0)
- else:
- # nudge coord by +/- 1.0 in direction of unit vector
- result.append(copysign(1.0, uv_component))
- return tuple(result)
-
-
-class Circle:
- def __init__(self, centre, radius):
- self.centre = centre
- self.radius = radius
-
- def __repr__(self):
- return f"Circle(centre={self.centre}, radius={self.radius})"
-
- def round(self):
- return Circle(_round_point(self.centre), otRound(self.radius))
-
- def inside(self, outer_circle, tolerance=_CIRCLE_INSIDE_TOLERANCE):
- dist = self.radius + hypot(*_vector_between(self.centre, outer_circle.centre))
- return (
- isclose(outer_circle.radius, dist, rel_tol=_CIRCLE_INSIDE_TOLERANCE)
- or outer_circle.radius > dist
- )
-
- def concentric(self, other):
- return self.centre == other.centre
-
- def move(self, dx, dy):
- self.centre = (self.centre[0] + dx, self.centre[1] + dy)
-
-
-def round_start_circle_stable_containment(c0, r0, c1, r1):
- """Round start circle so that it stays inside/outside end circle after rounding.
-
- The rounding of circle coordinates to integers may cause an abrupt change
- if the start circle c0 is so close to the end circle c1's perimiter that
- it ends up falling outside (or inside) as a result of the rounding.
- To keep the gradient unchanged, we nudge it in the right direction.
-
- See:
- https://github.com/googlefonts/colr-gradients-spec/issues/204
- https://github.com/googlefonts/picosvg/issues/158
- """
- start, end = Circle(c0, r0), Circle(c1, r1)
-
- inside_before_round = start.inside(end)
-
- round_start = start.round()
- round_end = end.round()
- inside_after_round = round_start.inside(round_end)
-
- if inside_before_round == inside_after_round:
- return round_start
- elif inside_after_round:
- # start was outside before rounding: we need to push start away from end
- direction = _vector_between(round_end.centre, round_start.centre)
- radius_delta = +1.0
- else:
- # start was inside before rounding: we need to push start towards end
- direction = _vector_between(round_start.centre, round_end.centre)
- radius_delta = -1.0
- dx, dy = _rounding_offset(direction)
-
- # At most 2 iterations ought to be enough to converge. Before the loop, we
- # know the start circle didn't keep containment after normal rounding; thus
- # we continue adjusting by -/+ 1.0 until containment is restored.
- # Normal rounding can at most move each coordinates -/+0.5; in the worst case
- # both the start and end circle's centres and radii will be rounded in opposite
- # directions, e.g. when they move along a 45 degree diagonal:
- # c0 = (1.5, 1.5) ===> (2.0, 2.0)
- # r0 = 0.5 ===> 1.0
- # c1 = (0.499, 0.499) ===> (0.0, 0.0)
- # r1 = 2.499 ===> 2.0
- # In this example, the relative distance between the circles, calculated
- # as r1 - (r0 + distance(c0, c1)) is initially 0.57437 (c0 is inside c1), and
- # -1.82842 after rounding (c0 is now outside c1). Nudging c0 by -1.0 on both
- # x and y axes moves it towards c1 by hypot(-1.0, -1.0) = 1.41421. Two of these
- # moves cover twice that distance, which is enough to restore containment.
- max_attempts = 2
- for _ in range(max_attempts):
- if round_start.concentric(round_end):
- # can't move c0 towards c1 (they are the same), so we change the radius
- round_start.radius += radius_delta
- assert round_start.radius >= 0
- else:
- round_start.move(dx, dy)
- if inside_before_round == round_start.inside(round_end):
- break
- else: # likely a bug
- raise AssertionError(
- f"Rounding circle {start} "
- f"{'inside' if inside_before_round else 'outside'} "
- f"{end} failed after {max_attempts} attempts!"
- )
-
- return round_start
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/colorLib/table_builder.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/colorLib/table_builder.py
deleted file mode 100644
index f1e182c..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/colorLib/table_builder.py
+++ /dev/null
@@ -1,223 +0,0 @@
-"""
-colorLib.table_builder: Generic helper for filling in BaseTable derivatives from tuples and maps and such.
-
-"""
-
-import collections
-import enum
-from fontTools.ttLib.tables.otBase import (
- BaseTable,
- FormatSwitchingBaseTable,
- UInt8FormatSwitchingBaseTable,
-)
-from fontTools.ttLib.tables.otConverters import (
- ComputedInt,
- SimpleValue,
- Struct,
- Short,
- UInt8,
- UShort,
- IntValue,
- FloatValue,
- OptionalValue,
-)
-from fontTools.misc.roundTools import otRound
-
-
-class BuildCallback(enum.Enum):
- """Keyed on (BEFORE_BUILD, class[, Format if available]).
- Receives (dest, source).
- Should return (dest, source), which can be new objects.
- """
-
- BEFORE_BUILD = enum.auto()
-
- """Keyed on (AFTER_BUILD, class[, Format if available]).
- Receives (dest).
- Should return dest, which can be a new object.
- """
- AFTER_BUILD = enum.auto()
-
- """Keyed on (CREATE_DEFAULT, class[, Format if available]).
- Receives no arguments.
- Should return a new instance of class.
- """
- CREATE_DEFAULT = enum.auto()
-
-
-def _assignable(convertersByName):
- return {k: v for k, v in convertersByName.items() if not isinstance(v, ComputedInt)}
-
-
-def _isNonStrSequence(value):
- return isinstance(value, collections.abc.Sequence) and not isinstance(value, str)
-
-
-def _split_format(cls, source):
- if _isNonStrSequence(source):
- assert len(source) > 0, f"{cls} needs at least format from {source}"
- fmt, remainder = source[0], source[1:]
- elif isinstance(source, collections.abc.Mapping):
- assert "Format" in source, f"{cls} needs at least Format from {source}"
- remainder = source.copy()
- fmt = remainder.pop("Format")
- else:
- raise ValueError(f"Not sure how to populate {cls} from {source}")
-
- assert isinstance(
- fmt, collections.abc.Hashable
- ), f"{cls} Format is not hashable: {fmt!r}"
- assert fmt in cls.convertersByName, f"{cls} invalid Format: {fmt!r}"
-
- return fmt, remainder
-
-
-class TableBuilder:
- """
- Helps to populate things derived from BaseTable from maps, tuples, etc.
-
- A table of lifecycle callbacks may be provided to add logic beyond what is possible
- based on otData info for the target class. See BuildCallbacks.
- """
-
- def __init__(self, callbackTable=None):
- if callbackTable is None:
- callbackTable = {}
- self._callbackTable = callbackTable
-
- def _convert(self, dest, field, converter, value):
- enumClass = getattr(converter, "enumClass", None)
-
- if enumClass:
- if isinstance(value, enumClass):
- pass
- elif isinstance(value, str):
- try:
- value = getattr(enumClass, value.upper())
- except AttributeError:
- raise ValueError(f"{value} is not a valid {enumClass}")
- else:
- value = enumClass(value)
-
- elif isinstance(converter, IntValue):
- value = otRound(value)
- elif isinstance(converter, FloatValue):
- value = float(value)
-
- elif isinstance(converter, Struct):
- if converter.repeat:
- if _isNonStrSequence(value):
- value = [self.build(converter.tableClass, v) for v in value]
- else:
- value = [self.build(converter.tableClass, value)]
- setattr(dest, converter.repeat, len(value))
- else:
- value = self.build(converter.tableClass, value)
- elif callable(converter):
- value = converter(value)
-
- setattr(dest, field, value)
-
- def build(self, cls, source):
- assert issubclass(cls, BaseTable)
-
- if isinstance(source, cls):
- return source
-
- callbackKey = (cls,)
- fmt = None
- if issubclass(cls, FormatSwitchingBaseTable):
- fmt, source = _split_format(cls, source)
- callbackKey = (cls, fmt)
-
- dest = self._callbackTable.get(
- (BuildCallback.CREATE_DEFAULT,) + callbackKey, lambda: cls()
- )()
- assert isinstance(dest, cls)
-
- convByName = _assignable(cls.convertersByName)
- skippedFields = set()
-
- # For format switchers we need to resolve converters based on format
- if issubclass(cls, FormatSwitchingBaseTable):
- dest.Format = fmt
- convByName = _assignable(convByName[dest.Format])
- skippedFields.add("Format")
-
- # Convert sequence => mapping so before thunk only has to handle one format
- if _isNonStrSequence(source):
- # Sequence (typically list or tuple) assumed to match fields in declaration order
- assert len(source) <= len(
- convByName
- ), f"Sequence of {len(source)} too long for {cls}; expected <= {len(convByName)} values"
- source = dict(zip(convByName.keys(), source))
-
- dest, source = self._callbackTable.get(
- (BuildCallback.BEFORE_BUILD,) + callbackKey, lambda d, s: (d, s)
- )(dest, source)
-
- if isinstance(source, collections.abc.Mapping):
- for field, value in source.items():
- if field in skippedFields:
- continue
- converter = convByName.get(field, None)
- if not converter:
- raise ValueError(
- f"Unrecognized field {field} for {cls}; expected one of {sorted(convByName.keys())}"
- )
- self._convert(dest, field, converter, value)
- else:
- # let's try as a 1-tuple
- dest = self.build(cls, (source,))
-
- for field, conv in convByName.items():
- if not hasattr(dest, field) and isinstance(conv, OptionalValue):
- setattr(dest, field, conv.DEFAULT)
-
- dest = self._callbackTable.get(
- (BuildCallback.AFTER_BUILD,) + callbackKey, lambda d: d
- )(dest)
-
- return dest
-
-
-class TableUnbuilder:
- def __init__(self, callbackTable=None):
- if callbackTable is None:
- callbackTable = {}
- self._callbackTable = callbackTable
-
- def unbuild(self, table):
- assert isinstance(table, BaseTable)
-
- source = {}
-
- callbackKey = (type(table),)
- if isinstance(table, FormatSwitchingBaseTable):
- source["Format"] = int(table.Format)
- callbackKey += (table.Format,)
-
- for converter in table.getConverters():
- if isinstance(converter, ComputedInt):
- continue
- value = getattr(table, converter.name)
-
- enumClass = getattr(converter, "enumClass", None)
- if enumClass:
- source[converter.name] = value.name.lower()
- elif isinstance(converter, Struct):
- if converter.repeat:
- source[converter.name] = [self.unbuild(v) for v in value]
- else:
- source[converter.name] = self.unbuild(value)
- elif isinstance(converter, SimpleValue):
- # "simple" values (e.g. int, float, str) need no further un-building
- source[converter.name] = value
- else:
- raise NotImplementedError(
- "Don't know how unbuild {value!r} with {converter!r}"
- )
-
- source = self._callbackTable.get(callbackKey, lambda s: s)(source)
-
- return source
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/colorLib/unbuilder.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/colorLib/unbuilder.py
deleted file mode 100644
index ac24355..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/colorLib/unbuilder.py
+++ /dev/null
@@ -1,81 +0,0 @@
-from fontTools.ttLib.tables import otTables as ot
-from .table_builder import TableUnbuilder
-
-
-def unbuildColrV1(layerList, baseGlyphList):
- layers = []
- if layerList:
- layers = layerList.Paint
- unbuilder = LayerListUnbuilder(layers)
- return {
- rec.BaseGlyph: unbuilder.unbuildPaint(rec.Paint)
- for rec in baseGlyphList.BaseGlyphPaintRecord
- }
-
-
-def _flatten_layers(lst):
- for paint in lst:
- if paint["Format"] == ot.PaintFormat.PaintColrLayers:
- yield from _flatten_layers(paint["Layers"])
- else:
- yield paint
-
-
-class LayerListUnbuilder:
- def __init__(self, layers):
- self.layers = layers
-
- callbacks = {
- (
- ot.Paint,
- ot.PaintFormat.PaintColrLayers,
- ): self._unbuildPaintColrLayers,
- }
- self.tableUnbuilder = TableUnbuilder(callbacks)
-
- def unbuildPaint(self, paint):
- assert isinstance(paint, ot.Paint)
- return self.tableUnbuilder.unbuild(paint)
-
- def _unbuildPaintColrLayers(self, source):
- assert source["Format"] == ot.PaintFormat.PaintColrLayers
-
- layers = list(
- _flatten_layers(
- [
- self.unbuildPaint(childPaint)
- for childPaint in self.layers[
- source["FirstLayerIndex"] : source["FirstLayerIndex"]
- + source["NumLayers"]
- ]
- ]
- )
- )
-
- if len(layers) == 1:
- return layers[0]
-
- return {"Format": source["Format"], "Layers": layers}
-
-
-if __name__ == "__main__":
- from pprint import pprint
- import sys
- from fontTools.ttLib import TTFont
-
- try:
- fontfile = sys.argv[1]
- except IndexError:
- sys.exit("usage: fonttools colorLib.unbuilder FONTFILE")
-
- font = TTFont(fontfile)
- colr = font["COLR"]
- if colr.version < 1:
- sys.exit(f"error: No COLR table version=1 found in {fontfile}")
-
- colorGlyphs = unbuildColrV1(
- colr.table.LayerList,
- colr.table.BaseGlyphList,
- )
-
- pprint(colorGlyphs)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/config/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/config/__init__.py
deleted file mode 100644
index c106fe5..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/config/__init__.py
+++ /dev/null
@@ -1,74 +0,0 @@
-"""
-Define all configuration options that can affect the working of fontTools
-modules. E.g. optimization levels of varLib IUP, otlLib GPOS compression level,
-etc. If this file gets too big, split it into smaller files per-module.
-
-An instance of the Config class can be attached to a TTFont object, so that
-the various modules can access their configuration options from it.
-"""
-from textwrap import dedent
-
-from fontTools.misc.configTools import *
-
-
-class Config(AbstractConfig):
- options = Options()
-
-
-OPTIONS = Config.options
-
-
-Config.register_option(
- name="fontTools.otlLib.optimize.gpos:COMPRESSION_LEVEL",
- help=dedent(
- """\
- GPOS Lookup type 2 (PairPos) compression level:
- 0 = do not attempt to compact PairPos lookups;
- 1 to 8 = create at most 1 to 8 new subtables for each existing
- subtable, provided that it would yield a 50%% file size saving;
- 9 = create as many new subtables as needed to yield a file size saving.
- Default: 0.
-
- This compaction aims to save file size, by splitting large class
- kerning subtables (Format 2) that contain many zero values into
- smaller and denser subtables. It's a trade-off between the overhead
- of several subtables versus the sparseness of one big subtable.
-
- See the pull request: https://github.com/fonttools/fonttools/pull/2326
- """
- ),
- default=0,
- parse=int,
- validate=lambda v: v in range(10),
-)
-
-Config.register_option(
- name="fontTools.ttLib.tables.otBase:USE_HARFBUZZ_REPACKER",
- help=dedent(
- """\
- FontTools tries to use the HarfBuzz Repacker to serialize GPOS/GSUB tables
- if the uharfbuzz python bindings are importable, otherwise falls back to its
- slower, less efficient serializer. Set to False to always use the latter.
- Set to True to explicitly request the HarfBuzz Repacker (will raise an
- error if uharfbuzz cannot be imported).
- """
- ),
- default=None,
- parse=Option.parse_optional_bool,
- validate=Option.validate_optional_bool,
-)
-
-Config.register_option(
- name="fontTools.otlLib.builder:WRITE_GPOS7",
- help=dedent(
- """\
- macOS before 13.2 didn’t support GPOS LookupType 7 (non-chaining
- ContextPos lookups), so FontTools.otlLib.builder disables a file size
- optimisation that would use LookupType 7 instead of 8 when there is no
- chaining (no prefix or suffix). Set to True to enable the optimization.
- """
- ),
- default=False,
- parse=Option.parse_optional_bool,
- validate=Option.validate_optional_bool,
-)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cu2qu/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cu2qu/__init__.py
deleted file mode 100644
index 4ae6356..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cu2qu/__init__.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright 2016 Google Inc. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from .cu2qu import *
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cu2qu/__main__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cu2qu/__main__.py
deleted file mode 100644
index 084bf8f..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cu2qu/__main__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-import sys
-from .cli import main
-
-
-if __name__ == "__main__":
- sys.exit(main())
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cu2qu/benchmark.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cu2qu/benchmark.py
deleted file mode 100644
index 2ab1e96..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cu2qu/benchmark.py
+++ /dev/null
@@ -1,55 +0,0 @@
-"""Benchmark the cu2qu algorithm performance."""
-
-from .cu2qu import *
-import random
-import timeit
-
-MAX_ERR = 0.05
-
-
-def generate_curve():
- return [
- tuple(float(random.randint(0, 2048)) for coord in range(2))
- for point in range(4)
- ]
-
-
-def setup_curve_to_quadratic():
- return generate_curve(), MAX_ERR
-
-
-def setup_curves_to_quadratic():
- num_curves = 3
- return ([generate_curve() for curve in range(num_curves)], [MAX_ERR] * num_curves)
-
-
-def run_benchmark(module, function, setup_suffix="", repeat=5, number=1000):
- setup_func = "setup_" + function
- if setup_suffix:
- print("%s with %s:" % (function, setup_suffix), end="")
- setup_func += "_" + setup_suffix
- else:
- print("%s:" % function, end="")
-
- def wrapper(function, setup_func):
- function = globals()[function]
- setup_func = globals()[setup_func]
-
- def wrapped():
- return function(*setup_func())
-
- return wrapped
-
- results = timeit.repeat(wrapper(function, setup_func), repeat=repeat, number=number)
- print("\t%5.1fus" % (min(results) * 1000000.0 / number))
-
-
-def main():
- """Benchmark the cu2qu algorithm performance."""
- run_benchmark("cu2qu", "curve_to_quadratic")
- run_benchmark("cu2qu", "curves_to_quadratic")
-
-
-if __name__ == "__main__":
- random.seed(1)
- main()
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cu2qu/cli.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cu2qu/cli.py
deleted file mode 100644
index 9144043..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cu2qu/cli.py
+++ /dev/null
@@ -1,198 +0,0 @@
-import os
-import argparse
-import logging
-import shutil
-import multiprocessing as mp
-from contextlib import closing
-from functools import partial
-
-import fontTools
-from .ufo import font_to_quadratic, fonts_to_quadratic
-
-ufo_module = None
-try:
- import ufoLib2 as ufo_module
-except ImportError:
- try:
- import defcon as ufo_module
- except ImportError as e:
- pass
-
-
-logger = logging.getLogger("fontTools.cu2qu")
-
-
-def _cpu_count():
- try:
- return mp.cpu_count()
- except NotImplementedError: # pragma: no cover
- return 1
-
-
-def open_ufo(path):
- if hasattr(ufo_module.Font, "open"): # ufoLib2
- return ufo_module.Font.open(path)
- return ufo_module.Font(path) # defcon
-
-
-def _font_to_quadratic(input_path, output_path=None, **kwargs):
- ufo = open_ufo(input_path)
- logger.info("Converting curves for %s", input_path)
- if font_to_quadratic(ufo, **kwargs):
- logger.info("Saving %s", output_path)
- if output_path:
- ufo.save(output_path)
- else:
- ufo.save() # save in-place
- elif output_path:
- _copytree(input_path, output_path)
-
-
-def _samepath(path1, path2):
- # TODO on python3+, there's os.path.samefile
- path1 = os.path.normcase(os.path.abspath(os.path.realpath(path1)))
- path2 = os.path.normcase(os.path.abspath(os.path.realpath(path2)))
- return path1 == path2
-
-
-def _copytree(input_path, output_path):
- if _samepath(input_path, output_path):
- logger.debug("input and output paths are the same file; skipped copy")
- return
- if os.path.exists(output_path):
- shutil.rmtree(output_path)
- shutil.copytree(input_path, output_path)
-
-
-def main(args=None):
- """Convert a UFO font from cubic to quadratic curves"""
- parser = argparse.ArgumentParser(prog="cu2qu")
- parser.add_argument("--version", action="version", version=fontTools.__version__)
- parser.add_argument(
- "infiles",
- nargs="+",
- metavar="INPUT",
- help="one or more input UFO source file(s).",
- )
- parser.add_argument("-v", "--verbose", action="count", default=0)
- parser.add_argument(
- "-e",
- "--conversion-error",
- type=float,
- metavar="ERROR",
- default=None,
- help="maxiumum approximation error measured in EM (default: 0.001)",
- )
- parser.add_argument(
- "-m",
- "--mixed",
- default=False,
- action="store_true",
- help="whether to used mixed quadratic and cubic curves",
- )
- parser.add_argument(
- "--keep-direction",
- dest="reverse_direction",
- action="store_false",
- help="do not reverse the contour direction",
- )
-
- mode_parser = parser.add_mutually_exclusive_group()
- mode_parser.add_argument(
- "-i",
- "--interpolatable",
- action="store_true",
- help="whether curve conversion should keep interpolation compatibility",
- )
- mode_parser.add_argument(
- "-j",
- "--jobs",
- type=int,
- nargs="?",
- default=1,
- const=_cpu_count(),
- metavar="N",
- help="Convert using N multiple processes (default: %(default)s)",
- )
-
- output_parser = parser.add_mutually_exclusive_group()
- output_parser.add_argument(
- "-o",
- "--output-file",
- default=None,
- metavar="OUTPUT",
- help=(
- "output filename for the converted UFO. By default fonts are "
- "modified in place. This only works with a single input."
- ),
- )
- output_parser.add_argument(
- "-d",
- "--output-dir",
- default=None,
- metavar="DIRECTORY",
- help="output directory where to save converted UFOs",
- )
-
- options = parser.parse_args(args)
-
- if ufo_module is None:
- parser.error("Either ufoLib2 or defcon are required to run this script.")
-
- if not options.verbose:
- level = "WARNING"
- elif options.verbose == 1:
- level = "INFO"
- else:
- level = "DEBUG"
- logging.basicConfig(level=level)
-
- if len(options.infiles) > 1 and options.output_file:
- parser.error("-o/--output-file can't be used with multile inputs")
-
- if options.output_dir:
- output_dir = options.output_dir
- if not os.path.exists(output_dir):
- os.mkdir(output_dir)
- elif not os.path.isdir(output_dir):
- parser.error("'%s' is not a directory" % output_dir)
- output_paths = [
- os.path.join(output_dir, os.path.basename(p)) for p in options.infiles
- ]
- elif options.output_file:
- output_paths = [options.output_file]
- else:
- # save in-place
- output_paths = [None] * len(options.infiles)
-
- kwargs = dict(
- dump_stats=options.verbose > 0,
- max_err_em=options.conversion_error,
- reverse_direction=options.reverse_direction,
- all_quadratic=False if options.mixed else True,
- )
-
- if options.interpolatable:
- logger.info("Converting curves compatibly")
- ufos = [open_ufo(infile) for infile in options.infiles]
- if fonts_to_quadratic(ufos, **kwargs):
- for ufo, output_path in zip(ufos, output_paths):
- logger.info("Saving %s", output_path)
- if output_path:
- ufo.save(output_path)
- else:
- ufo.save()
- else:
- for input_path, output_path in zip(options.infiles, output_paths):
- if output_path:
- _copytree(input_path, output_path)
- else:
- jobs = min(len(options.infiles), options.jobs) if options.jobs > 1 else 1
- if jobs > 1:
- func = partial(_font_to_quadratic, **kwargs)
- logger.info("Running %d parallel processes", jobs)
- with closing(mp.Pool(jobs)) as pool:
- pool.starmap(func, zip(options.infiles, output_paths))
- else:
- for input_path, output_path in zip(options.infiles, output_paths):
- _font_to_quadratic(input_path, output_path, **kwargs)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cu2qu/cu2qu.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cu2qu/cu2qu.py
deleted file mode 100644
index e620b48..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cu2qu/cu2qu.py
+++ /dev/null
@@ -1,534 +0,0 @@
-# cython: language_level=3
-# distutils: define_macros=CYTHON_TRACE_NOGIL=1
-
-# Copyright 2015 Google Inc. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-try:
- import cython
-
- COMPILED = cython.compiled
-except (AttributeError, ImportError):
- # if cython not installed, use mock module with no-op decorators and types
- from fontTools.misc import cython
-
- COMPILED = False
-
-import math
-
-from .errors import Error as Cu2QuError, ApproxNotFoundError
-
-
-__all__ = ["curve_to_quadratic", "curves_to_quadratic"]
-
-MAX_N = 100
-
-NAN = float("NaN")
-
-
-@cython.cfunc
-@cython.inline
-@cython.returns(cython.double)
-@cython.locals(v1=cython.complex, v2=cython.complex)
-def dot(v1, v2):
- """Return the dot product of two vectors.
-
- Args:
- v1 (complex): First vector.
- v2 (complex): Second vector.
-
- Returns:
- double: Dot product.
- """
- return (v1 * v2.conjugate()).real
-
-
-@cython.cfunc
-@cython.inline
-@cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex)
-@cython.locals(
- _1=cython.complex, _2=cython.complex, _3=cython.complex, _4=cython.complex
-)
-def calc_cubic_points(a, b, c, d):
- _1 = d
- _2 = (c / 3.0) + d
- _3 = (b + c) / 3.0 + _2
- _4 = a + d + c + b
- return _1, _2, _3, _4
-
-
-@cython.cfunc
-@cython.inline
-@cython.locals(
- p0=cython.complex, p1=cython.complex, p2=cython.complex, p3=cython.complex
-)
-@cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex)
-def calc_cubic_parameters(p0, p1, p2, p3):
- c = (p1 - p0) * 3.0
- b = (p2 - p1) * 3.0 - c
- d = p0
- a = p3 - d - c - b
- return a, b, c, d
-
-
-@cython.cfunc
-@cython.inline
-@cython.locals(
- p0=cython.complex, p1=cython.complex, p2=cython.complex, p3=cython.complex
-)
-def split_cubic_into_n_iter(p0, p1, p2, p3, n):
- """Split a cubic Bezier into n equal parts.
-
- Splits the curve into `n` equal parts by curve time.
- (t=0..1/n, t=1/n..2/n, ...)
-
- Args:
- p0 (complex): Start point of curve.
- p1 (complex): First handle of curve.
- p2 (complex): Second handle of curve.
- p3 (complex): End point of curve.
-
- Returns:
- An iterator yielding the control points (four complex values) of the
- subcurves.
- """
- # Hand-coded special-cases
- if n == 2:
- return iter(split_cubic_into_two(p0, p1, p2, p3))
- if n == 3:
- return iter(split_cubic_into_three(p0, p1, p2, p3))
- if n == 4:
- a, b = split_cubic_into_two(p0, p1, p2, p3)
- return iter(
- split_cubic_into_two(a[0], a[1], a[2], a[3])
- + split_cubic_into_two(b[0], b[1], b[2], b[3])
- )
- if n == 6:
- a, b = split_cubic_into_two(p0, p1, p2, p3)
- return iter(
- split_cubic_into_three(a[0], a[1], a[2], a[3])
- + split_cubic_into_three(b[0], b[1], b[2], b[3])
- )
-
- return _split_cubic_into_n_gen(p0, p1, p2, p3, n)
-
-
-@cython.locals(
- p0=cython.complex,
- p1=cython.complex,
- p2=cython.complex,
- p3=cython.complex,
- n=cython.int,
-)
-@cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex)
-@cython.locals(
- dt=cython.double, delta_2=cython.double, delta_3=cython.double, i=cython.int
-)
-@cython.locals(
- a1=cython.complex, b1=cython.complex, c1=cython.complex, d1=cython.complex
-)
-def _split_cubic_into_n_gen(p0, p1, p2, p3, n):
- a, b, c, d = calc_cubic_parameters(p0, p1, p2, p3)
- dt = 1 / n
- delta_2 = dt * dt
- delta_3 = dt * delta_2
- for i in range(n):
- t1 = i * dt
- t1_2 = t1 * t1
- # calc new a, b, c and d
- a1 = a * delta_3
- b1 = (3 * a * t1 + b) * delta_2
- c1 = (2 * b * t1 + c + 3 * a * t1_2) * dt
- d1 = a * t1 * t1_2 + b * t1_2 + c * t1 + d
- yield calc_cubic_points(a1, b1, c1, d1)
-
-
-@cython.cfunc
-@cython.inline
-@cython.locals(
- p0=cython.complex, p1=cython.complex, p2=cython.complex, p3=cython.complex
-)
-@cython.locals(mid=cython.complex, deriv3=cython.complex)
-def split_cubic_into_two(p0, p1, p2, p3):
- """Split a cubic Bezier into two equal parts.
-
- Splits the curve into two equal parts at t = 0.5
-
- Args:
- p0 (complex): Start point of curve.
- p1 (complex): First handle of curve.
- p2 (complex): Second handle of curve.
- p3 (complex): End point of curve.
-
- Returns:
- tuple: Two cubic Beziers (each expressed as a tuple of four complex
- values).
- """
- mid = (p0 + 3 * (p1 + p2) + p3) * 0.125
- deriv3 = (p3 + p2 - p1 - p0) * 0.125
- return (
- (p0, (p0 + p1) * 0.5, mid - deriv3, mid),
- (mid, mid + deriv3, (p2 + p3) * 0.5, p3),
- )
-
-
-@cython.cfunc
-@cython.inline
-@cython.locals(
- p0=cython.complex,
- p1=cython.complex,
- p2=cython.complex,
- p3=cython.complex,
-)
-@cython.locals(
- mid1=cython.complex,
- deriv1=cython.complex,
- mid2=cython.complex,
- deriv2=cython.complex,
-)
-def split_cubic_into_three(p0, p1, p2, p3):
- """Split a cubic Bezier into three equal parts.
-
- Splits the curve into three equal parts at t = 1/3 and t = 2/3
-
- Args:
- p0 (complex): Start point of curve.
- p1 (complex): First handle of curve.
- p2 (complex): Second handle of curve.
- p3 (complex): End point of curve.
-
- Returns:
- tuple: Three cubic Beziers (each expressed as a tuple of four complex
- values).
- """
- mid1 = (8 * p0 + 12 * p1 + 6 * p2 + p3) * (1 / 27)
- deriv1 = (p3 + 3 * p2 - 4 * p0) * (1 / 27)
- mid2 = (p0 + 6 * p1 + 12 * p2 + 8 * p3) * (1 / 27)
- deriv2 = (4 * p3 - 3 * p1 - p0) * (1 / 27)
- return (
- (p0, (2 * p0 + p1) / 3.0, mid1 - deriv1, mid1),
- (mid1, mid1 + deriv1, mid2 - deriv2, mid2),
- (mid2, mid2 + deriv2, (p2 + 2 * p3) / 3.0, p3),
- )
-
-
-@cython.cfunc
-@cython.inline
-@cython.returns(cython.complex)
-@cython.locals(
- t=cython.double,
- p0=cython.complex,
- p1=cython.complex,
- p2=cython.complex,
- p3=cython.complex,
-)
-@cython.locals(_p1=cython.complex, _p2=cython.complex)
-def cubic_approx_control(t, p0, p1, p2, p3):
- """Approximate a cubic Bezier using a quadratic one.
-
- Args:
- t (double): Position of control point.
- p0 (complex): Start point of curve.
- p1 (complex): First handle of curve.
- p2 (complex): Second handle of curve.
- p3 (complex): End point of curve.
-
- Returns:
- complex: Location of candidate control point on quadratic curve.
- """
- _p1 = p0 + (p1 - p0) * 1.5
- _p2 = p3 + (p2 - p3) * 1.5
- return _p1 + (_p2 - _p1) * t
-
-
-@cython.cfunc
-@cython.inline
-@cython.returns(cython.complex)
-@cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex)
-@cython.locals(ab=cython.complex, cd=cython.complex, p=cython.complex, h=cython.double)
-def calc_intersect(a, b, c, d):
- """Calculate the intersection of two lines.
-
- Args:
- a (complex): Start point of first line.
- b (complex): End point of first line.
- c (complex): Start point of second line.
- d (complex): End point of second line.
-
- Returns:
- complex: Location of intersection if one present, ``complex(NaN,NaN)``
- if no intersection was found.
- """
- ab = b - a
- cd = d - c
- p = ab * 1j
- try:
- h = dot(p, a - c) / dot(p, cd)
- except ZeroDivisionError:
- return complex(NAN, NAN)
- return c + cd * h
-
-
-@cython.cfunc
-@cython.returns(cython.int)
-@cython.locals(
- tolerance=cython.double,
- p0=cython.complex,
- p1=cython.complex,
- p2=cython.complex,
- p3=cython.complex,
-)
-@cython.locals(mid=cython.complex, deriv3=cython.complex)
-def cubic_farthest_fit_inside(p0, p1, p2, p3, tolerance):
- """Check if a cubic Bezier lies within a given distance of the origin.
-
- "Origin" means *the* origin (0,0), not the start of the curve. Note that no
- checks are made on the start and end positions of the curve; this function
- only checks the inside of the curve.
-
- Args:
- p0 (complex): Start point of curve.
- p1 (complex): First handle of curve.
- p2 (complex): Second handle of curve.
- p3 (complex): End point of curve.
- tolerance (double): Distance from origin.
-
- Returns:
- bool: True if the cubic Bezier ``p`` entirely lies within a distance
- ``tolerance`` of the origin, False otherwise.
- """
- # First check p2 then p1, as p2 has higher error early on.
- if abs(p2) <= tolerance and abs(p1) <= tolerance:
- return True
-
- # Split.
- mid = (p0 + 3 * (p1 + p2) + p3) * 0.125
- if abs(mid) > tolerance:
- return False
- deriv3 = (p3 + p2 - p1 - p0) * 0.125
- return cubic_farthest_fit_inside(
- p0, (p0 + p1) * 0.5, mid - deriv3, mid, tolerance
- ) and cubic_farthest_fit_inside(mid, mid + deriv3, (p2 + p3) * 0.5, p3, tolerance)
-
-
-@cython.cfunc
-@cython.inline
-@cython.locals(tolerance=cython.double)
-@cython.locals(
- q1=cython.complex,
- c0=cython.complex,
- c1=cython.complex,
- c2=cython.complex,
- c3=cython.complex,
-)
-def cubic_approx_quadratic(cubic, tolerance):
- """Approximate a cubic Bezier with a single quadratic within a given tolerance.
-
- Args:
- cubic (sequence): Four complex numbers representing control points of
- the cubic Bezier curve.
- tolerance (double): Permitted deviation from the original curve.
-
- Returns:
- Three complex numbers representing control points of the quadratic
- curve if it fits within the given tolerance, or ``None`` if no suitable
- curve could be calculated.
- """
-
- q1 = calc_intersect(cubic[0], cubic[1], cubic[2], cubic[3])
- if math.isnan(q1.imag):
- return None
- c0 = cubic[0]
- c3 = cubic[3]
- c1 = c0 + (q1 - c0) * (2 / 3)
- c2 = c3 + (q1 - c3) * (2 / 3)
- if not cubic_farthest_fit_inside(0, c1 - cubic[1], c2 - cubic[2], 0, tolerance):
- return None
- return c0, q1, c3
-
-
-@cython.cfunc
-@cython.locals(n=cython.int, tolerance=cython.double)
-@cython.locals(i=cython.int)
-@cython.locals(all_quadratic=cython.int)
-@cython.locals(
- c0=cython.complex, c1=cython.complex, c2=cython.complex, c3=cython.complex
-)
-@cython.locals(
- q0=cython.complex,
- q1=cython.complex,
- next_q1=cython.complex,
- q2=cython.complex,
- d1=cython.complex,
-)
-def cubic_approx_spline(cubic, n, tolerance, all_quadratic):
- """Approximate a cubic Bezier curve with a spline of n quadratics.
-
- Args:
- cubic (sequence): Four complex numbers representing control points of
- the cubic Bezier curve.
- n (int): Number of quadratic Bezier curves in the spline.
- tolerance (double): Permitted deviation from the original curve.
-
- Returns:
- A list of ``n+2`` complex numbers, representing control points of the
- quadratic spline if it fits within the given tolerance, or ``None`` if
- no suitable spline could be calculated.
- """
-
- if n == 1:
- return cubic_approx_quadratic(cubic, tolerance)
- if n == 2 and all_quadratic == False:
- return cubic
-
- cubics = split_cubic_into_n_iter(cubic[0], cubic[1], cubic[2], cubic[3], n)
-
- # calculate the spline of quadratics and check errors at the same time.
- next_cubic = next(cubics)
- next_q1 = cubic_approx_control(
- 0, next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3]
- )
- q2 = cubic[0]
- d1 = 0j
- spline = [cubic[0], next_q1]
- for i in range(1, n + 1):
- # Current cubic to convert
- c0, c1, c2, c3 = next_cubic
-
- # Current quadratic approximation of current cubic
- q0 = q2
- q1 = next_q1
- if i < n:
- next_cubic = next(cubics)
- next_q1 = cubic_approx_control(
- i / (n - 1), next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3]
- )
- spline.append(next_q1)
- q2 = (q1 + next_q1) * 0.5
- else:
- q2 = c3
-
- # End-point deltas
- d0 = d1
- d1 = q2 - c3
-
- if abs(d1) > tolerance or not cubic_farthest_fit_inside(
- d0,
- q0 + (q1 - q0) * (2 / 3) - c1,
- q2 + (q1 - q2) * (2 / 3) - c2,
- d1,
- tolerance,
- ):
- return None
- spline.append(cubic[3])
-
- return spline
-
-
-@cython.locals(max_err=cython.double)
-@cython.locals(n=cython.int)
-@cython.locals(all_quadratic=cython.int)
-def curve_to_quadratic(curve, max_err, all_quadratic=True):
- """Approximate a cubic Bezier curve with a spline of n quadratics.
-
- Args:
- cubic (sequence): Four 2D tuples representing control points of
- the cubic Bezier curve.
- max_err (double): Permitted deviation from the original curve.
- all_quadratic (bool): If True (default) returned value is a
- quadratic spline. If False, it's either a single quadratic
- curve or a single cubic curve.
-
- Returns:
- If all_quadratic is True: A list of 2D tuples, representing
- control points of the quadratic spline if it fits within the
- given tolerance, or ``None`` if no suitable spline could be
- calculated.
-
- If all_quadratic is False: Either a quadratic curve (if length
- of output is 3), or a cubic curve (if length of output is 4).
- """
-
- curve = [complex(*p) for p in curve]
-
- for n in range(1, MAX_N + 1):
- spline = cubic_approx_spline(curve, n, max_err, all_quadratic)
- if spline is not None:
- # done. go home
- return [(s.real, s.imag) for s in spline]
-
- raise ApproxNotFoundError(curve)
-
-
-@cython.locals(l=cython.int, last_i=cython.int, i=cython.int)
-@cython.locals(all_quadratic=cython.int)
-def curves_to_quadratic(curves, max_errors, all_quadratic=True):
- """Return quadratic Bezier splines approximating the input cubic Beziers.
-
- Args:
- curves: A sequence of *n* curves, each curve being a sequence of four
- 2D tuples.
- max_errors: A sequence of *n* floats representing the maximum permissible
- deviation from each of the cubic Bezier curves.
- all_quadratic (bool): If True (default) returned values are a
- quadratic spline. If False, they are either a single quadratic
- curve or a single cubic curve.
-
- Example::
-
- >>> curves_to_quadratic( [
- ... [ (50,50), (100,100), (150,100), (200,50) ],
- ... [ (75,50), (120,100), (150,75), (200,60) ]
- ... ], [1,1] )
- [[(50.0, 50.0), (75.0, 75.0), (125.0, 91.66666666666666), (175.0, 75.0), (200.0, 50.0)], [(75.0, 50.0), (97.5, 75.0), (135.41666666666666, 82.08333333333333), (175.0, 67.5), (200.0, 60.0)]]
-
- The returned splines have "implied oncurve points" suitable for use in
- TrueType ``glif`` outlines - i.e. in the first spline returned above,
- the first quadratic segment runs from (50,50) to
- ( (75 + 125)/2 , (120 + 91.666..)/2 ) = (100, 83.333...).
-
- Returns:
- If all_quadratic is True, a list of splines, each spline being a list
- of 2D tuples.
-
- If all_quadratic is False, a list of curves, each curve being a quadratic
- (length 3), or cubic (length 4).
-
- Raises:
- fontTools.cu2qu.Errors.ApproxNotFoundError: if no suitable approximation
- can be found for all curves with the given parameters.
- """
-
- curves = [[complex(*p) for p in curve] for curve in curves]
- assert len(max_errors) == len(curves)
-
- l = len(curves)
- splines = [None] * l
- last_i = i = 0
- n = 1
- while True:
- spline = cubic_approx_spline(curves[i], n, max_errors[i], all_quadratic)
- if spline is None:
- if n == MAX_N:
- break
- n += 1
- last_i = i
- continue
- splines[i] = spline
- i = (i + 1) % l
- if i == last_i:
- # done. go home
- return [[(s.real, s.imag) for s in spline] for spline in splines]
-
- raise ApproxNotFoundError(curves)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cu2qu/errors.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cu2qu/errors.py
deleted file mode 100644
index fa3dc42..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cu2qu/errors.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# Copyright 2016 Google Inc. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-class Error(Exception):
- """Base Cu2Qu exception class for all other errors."""
-
-
-class ApproxNotFoundError(Error):
- def __init__(self, curve):
- message = "no approximation found: %s" % curve
- super().__init__(message)
- self.curve = curve
-
-
-class UnequalZipLengthsError(Error):
- pass
-
-
-class IncompatibleGlyphsError(Error):
- def __init__(self, glyphs):
- assert len(glyphs) > 1
- self.glyphs = glyphs
- names = set(repr(g.name) for g in glyphs)
- if len(names) > 1:
- self.combined_name = "{%s}" % ", ".join(sorted(names))
- else:
- self.combined_name = names.pop()
-
- def __repr__(self):
- return "<%s %s>" % (type(self).__name__, self.combined_name)
-
-
-class IncompatibleSegmentNumberError(IncompatibleGlyphsError):
- def __str__(self):
- return "Glyphs named %s have different number of segments" % (
- self.combined_name
- )
-
-
-class IncompatibleSegmentTypesError(IncompatibleGlyphsError):
- def __init__(self, glyphs, segments):
- IncompatibleGlyphsError.__init__(self, glyphs)
- self.segments = segments
-
- def __str__(self):
- lines = []
- ndigits = len(str(max(self.segments)))
- for i, tags in sorted(self.segments.items()):
- lines.append(
- "%s: (%s)" % (str(i).rjust(ndigits), ", ".join(repr(t) for t in tags))
- )
- return "Glyphs named %s have incompatible segment types:\n %s" % (
- self.combined_name,
- "\n ".join(lines),
- )
-
-
-class IncompatibleFontsError(Error):
- def __init__(self, glyph_errors):
- self.glyph_errors = glyph_errors
-
- def __str__(self):
- return "fonts contains incompatible glyphs: %s" % (
- ", ".join(repr(g) for g in sorted(self.glyph_errors.keys()))
- )
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cu2qu/ufo.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cu2qu/ufo.py
deleted file mode 100644
index 10367cf..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/cu2qu/ufo.py
+++ /dev/null
@@ -1,349 +0,0 @@
-# Copyright 2015 Google Inc. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-"""Converts cubic bezier curves to quadratic splines.
-
-Conversion is performed such that the quadratic splines keep the same end-curve
-tangents as the original cubics. The approach is iterative, increasing the
-number of segments for a spline until the error gets below a bound.
-
-Respective curves from multiple fonts will be converted at once to ensure that
-the resulting splines are interpolation-compatible.
-"""
-
-import logging
-from fontTools.pens.basePen import AbstractPen
-from fontTools.pens.pointPen import PointToSegmentPen
-from fontTools.pens.reverseContourPen import ReverseContourPen
-
-from . import curves_to_quadratic
-from .errors import (
- UnequalZipLengthsError,
- IncompatibleSegmentNumberError,
- IncompatibleSegmentTypesError,
- IncompatibleGlyphsError,
- IncompatibleFontsError,
-)
-
-
-__all__ = ["fonts_to_quadratic", "font_to_quadratic"]
-
-# The default approximation error below is a relative value (1/1000 of the EM square).
-# Later on, we convert it to absolute font units by multiplying it by a font's UPEM
-# (see fonts_to_quadratic).
-DEFAULT_MAX_ERR = 0.001
-CURVE_TYPE_LIB_KEY = "com.github.googlei18n.cu2qu.curve_type"
-
-logger = logging.getLogger(__name__)
-
-
-_zip = zip
-
-
-def zip(*args):
- """Ensure each argument to zip has the same length. Also make sure a list is
- returned for python 2/3 compatibility.
- """
-
- if len(set(len(a) for a in args)) != 1:
- raise UnequalZipLengthsError(*args)
- return list(_zip(*args))
-
-
-class GetSegmentsPen(AbstractPen):
- """Pen to collect segments into lists of points for conversion.
-
- Curves always include their initial on-curve point, so some points are
- duplicated between segments.
- """
-
- def __init__(self):
- self._last_pt = None
- self.segments = []
-
- def _add_segment(self, tag, *args):
- if tag in ["move", "line", "qcurve", "curve"]:
- self._last_pt = args[-1]
- self.segments.append((tag, args))
-
- def moveTo(self, pt):
- self._add_segment("move", pt)
-
- def lineTo(self, pt):
- self._add_segment("line", pt)
-
- def qCurveTo(self, *points):
- self._add_segment("qcurve", self._last_pt, *points)
-
- def curveTo(self, *points):
- self._add_segment("curve", self._last_pt, *points)
-
- def closePath(self):
- self._add_segment("close")
-
- def endPath(self):
- self._add_segment("end")
-
- def addComponent(self, glyphName, transformation):
- pass
-
-
-def _get_segments(glyph):
- """Get a glyph's segments as extracted by GetSegmentsPen."""
-
- pen = GetSegmentsPen()
- # glyph.draw(pen)
- # We can't simply draw the glyph with the pen, but we must initialize the
- # PointToSegmentPen explicitly with outputImpliedClosingLine=True.
- # By default PointToSegmentPen does not outputImpliedClosingLine -- unless
- # last and first point on closed contour are duplicated. Because we are
- # converting multiple glyphs at the same time, we want to make sure
- # this function returns the same number of segments, whether or not
- # the last and first point overlap.
- # https://github.com/googlefonts/fontmake/issues/572
- # https://github.com/fonttools/fonttools/pull/1720
- pointPen = PointToSegmentPen(pen, outputImpliedClosingLine=True)
- glyph.drawPoints(pointPen)
- return pen.segments
-
-
-def _set_segments(glyph, segments, reverse_direction):
- """Draw segments as extracted by GetSegmentsPen back to a glyph."""
-
- glyph.clearContours()
- pen = glyph.getPen()
- if reverse_direction:
- pen = ReverseContourPen(pen)
- for tag, args in segments:
- if tag == "move":
- pen.moveTo(*args)
- elif tag == "line":
- pen.lineTo(*args)
- elif tag == "curve":
- pen.curveTo(*args[1:])
- elif tag == "qcurve":
- pen.qCurveTo(*args[1:])
- elif tag == "close":
- pen.closePath()
- elif tag == "end":
- pen.endPath()
- else:
- raise AssertionError('Unhandled segment type "%s"' % tag)
-
-
-def _segments_to_quadratic(segments, max_err, stats, all_quadratic=True):
- """Return quadratic approximations of cubic segments."""
-
- assert all(s[0] == "curve" for s in segments), "Non-cubic given to convert"
-
- new_points = curves_to_quadratic([s[1] for s in segments], max_err, all_quadratic)
- n = len(new_points[0])
- assert all(len(s) == n for s in new_points[1:]), "Converted incompatibly"
-
- spline_length = str(n - 2)
- stats[spline_length] = stats.get(spline_length, 0) + 1
-
- if all_quadratic or n == 3:
- return [("qcurve", p) for p in new_points]
- else:
- return [("curve", p) for p in new_points]
-
-
-def _glyphs_to_quadratic(glyphs, max_err, reverse_direction, stats, all_quadratic=True):
- """Do the actual conversion of a set of compatible glyphs, after arguments
- have been set up.
-
- Return True if the glyphs were modified, else return False.
- """
-
- try:
- segments_by_location = zip(*[_get_segments(g) for g in glyphs])
- except UnequalZipLengthsError:
- raise IncompatibleSegmentNumberError(glyphs)
- if not any(segments_by_location):
- return False
-
- # always modify input glyphs if reverse_direction is True
- glyphs_modified = reverse_direction
-
- new_segments_by_location = []
- incompatible = {}
- for i, segments in enumerate(segments_by_location):
- tag = segments[0][0]
- if not all(s[0] == tag for s in segments[1:]):
- incompatible[i] = [s[0] for s in segments]
- elif tag == "curve":
- new_segments = _segments_to_quadratic(
- segments, max_err, stats, all_quadratic
- )
- if all_quadratic or new_segments != segments:
- glyphs_modified = True
- segments = new_segments
- new_segments_by_location.append(segments)
-
- if glyphs_modified:
- new_segments_by_glyph = zip(*new_segments_by_location)
- for glyph, new_segments in zip(glyphs, new_segments_by_glyph):
- _set_segments(glyph, new_segments, reverse_direction)
-
- if incompatible:
- raise IncompatibleSegmentTypesError(glyphs, segments=incompatible)
- return glyphs_modified
-
-
-def glyphs_to_quadratic(
- glyphs, max_err=None, reverse_direction=False, stats=None, all_quadratic=True
-):
- """Convert the curves of a set of compatible of glyphs to quadratic.
-
- All curves will be converted to quadratic at once, ensuring interpolation
- compatibility. If this is not required, calling glyphs_to_quadratic with one
- glyph at a time may yield slightly more optimized results.
-
- Return True if glyphs were modified, else return False.
-
- Raises IncompatibleGlyphsError if glyphs have non-interpolatable outlines.
- """
- if stats is None:
- stats = {}
-
- if not max_err:
- # assume 1000 is the default UPEM
- max_err = DEFAULT_MAX_ERR * 1000
-
- if isinstance(max_err, (list, tuple)):
- max_errors = max_err
- else:
- max_errors = [max_err] * len(glyphs)
- assert len(max_errors) == len(glyphs)
-
- return _glyphs_to_quadratic(
- glyphs, max_errors, reverse_direction, stats, all_quadratic
- )
-
-
-def fonts_to_quadratic(
- fonts,
- max_err_em=None,
- max_err=None,
- reverse_direction=False,
- stats=None,
- dump_stats=False,
- remember_curve_type=True,
- all_quadratic=True,
-):
- """Convert the curves of a collection of fonts to quadratic.
-
- All curves will be converted to quadratic at once, ensuring interpolation
- compatibility. If this is not required, calling fonts_to_quadratic with one
- font at a time may yield slightly more optimized results.
-
- Return True if fonts were modified, else return False.
-
- By default, cu2qu stores the curve type in the fonts' lib, under a private
- key "com.github.googlei18n.cu2qu.curve_type", and will not try to convert
- them again if the curve type is already set to "quadratic".
- Setting 'remember_curve_type' to False disables this optimization.
-
- Raises IncompatibleFontsError if same-named glyphs from different fonts
- have non-interpolatable outlines.
- """
-
- if remember_curve_type:
- curve_types = {f.lib.get(CURVE_TYPE_LIB_KEY, "cubic") for f in fonts}
- if len(curve_types) == 1:
- curve_type = next(iter(curve_types))
- if curve_type in ("quadratic", "mixed"):
- logger.info("Curves already converted to quadratic")
- return False
- elif curve_type == "cubic":
- pass # keep converting
- else:
- raise NotImplementedError(curve_type)
- elif len(curve_types) > 1:
- # going to crash later if they do differ
- logger.warning("fonts may contain different curve types")
-
- if stats is None:
- stats = {}
-
- if max_err_em and max_err:
- raise TypeError("Only one of max_err and max_err_em can be specified.")
- if not (max_err_em or max_err):
- max_err_em = DEFAULT_MAX_ERR
-
- if isinstance(max_err, (list, tuple)):
- assert len(max_err) == len(fonts)
- max_errors = max_err
- elif max_err:
- max_errors = [max_err] * len(fonts)
-
- if isinstance(max_err_em, (list, tuple)):
- assert len(fonts) == len(max_err_em)
- max_errors = [f.info.unitsPerEm * e for f, e in zip(fonts, max_err_em)]
- elif max_err_em:
- max_errors = [f.info.unitsPerEm * max_err_em for f in fonts]
-
- modified = False
- glyph_errors = {}
- for name in set().union(*(f.keys() for f in fonts)):
- glyphs = []
- cur_max_errors = []
- for font, error in zip(fonts, max_errors):
- if name in font:
- glyphs.append(font[name])
- cur_max_errors.append(error)
- try:
- modified |= _glyphs_to_quadratic(
- glyphs, cur_max_errors, reverse_direction, stats, all_quadratic
- )
- except IncompatibleGlyphsError as exc:
- logger.error(exc)
- glyph_errors[name] = exc
-
- if glyph_errors:
- raise IncompatibleFontsError(glyph_errors)
-
- if modified and dump_stats:
- spline_lengths = sorted(stats.keys())
- logger.info(
- "New spline lengths: %s"
- % (", ".join("%s: %d" % (l, stats[l]) for l in spline_lengths))
- )
-
- if remember_curve_type:
- for font in fonts:
- curve_type = font.lib.get(CURVE_TYPE_LIB_KEY, "cubic")
- new_curve_type = "quadratic" if all_quadratic else "mixed"
- if curve_type != new_curve_type:
- font.lib[CURVE_TYPE_LIB_KEY] = new_curve_type
- modified = True
- return modified
-
-
-def glyph_to_quadratic(glyph, **kwargs):
- """Convenience wrapper around glyphs_to_quadratic, for just one glyph.
- Return True if the glyph was modified, else return False.
- """
-
- return glyphs_to_quadratic([glyph], **kwargs)
-
-
-def font_to_quadratic(font, **kwargs):
- """Convenience wrapper around fonts_to_quadratic, for just one font.
- Return True if the font was modified, else return False.
- """
-
- return fonts_to_quadratic([font], **kwargs)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/designspaceLib/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/designspaceLib/__init__.py
deleted file mode 100644
index 1c71fd0..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/designspaceLib/__init__.py
+++ /dev/null
@@ -1,3283 +0,0 @@
-from __future__ import annotations
-
-import collections
-import copy
-import itertools
-import math
-import os
-import posixpath
-from io import BytesIO, StringIO
-from textwrap import indent
-from typing import Any, Dict, List, MutableMapping, Optional, Tuple, Union, cast
-
-from fontTools.misc import etree as ET
-from fontTools.misc import plistlib
-from fontTools.misc.loggingTools import LogMixin
-from fontTools.misc.textTools import tobytes, tostr
-
-"""
- designSpaceDocument
-
- - read and write designspace files
-"""
-
-__all__ = [
- "AxisDescriptor",
- "AxisLabelDescriptor",
- "AxisMappingDescriptor",
- "BaseDocReader",
- "BaseDocWriter",
- "DesignSpaceDocument",
- "DesignSpaceDocumentError",
- "DiscreteAxisDescriptor",
- "InstanceDescriptor",
- "LocationLabelDescriptor",
- "RangeAxisSubsetDescriptor",
- "RuleDescriptor",
- "SourceDescriptor",
- "ValueAxisSubsetDescriptor",
- "VariableFontDescriptor",
-]
-
-# ElementTree allows to find namespace-prefixed elements, but not attributes
-# so we have to do it ourselves for 'xml:lang'
-XML_NS = "{http://www.w3.org/XML/1998/namespace}"
-XML_LANG = XML_NS + "lang"
-
-
-def posix(path):
- """Normalize paths using forward slash to work also on Windows."""
- new_path = posixpath.join(*path.split(os.path.sep))
- if path.startswith("/"):
- # The above transformation loses absolute paths
- new_path = "/" + new_path
- elif path.startswith(r"\\"):
- # The above transformation loses leading slashes of UNC path mounts
- new_path = "//" + new_path
- return new_path
-
-
-def posixpath_property(private_name):
- """Generate a propery that holds a path always using forward slashes."""
-
- def getter(self):
- # Normal getter
- return getattr(self, private_name)
-
- def setter(self, value):
- # The setter rewrites paths using forward slashes
- if value is not None:
- value = posix(value)
- setattr(self, private_name, value)
-
- return property(getter, setter)
-
-
-class DesignSpaceDocumentError(Exception):
- def __init__(self, msg, obj=None):
- self.msg = msg
- self.obj = obj
-
- def __str__(self):
- return str(self.msg) + (": %r" % self.obj if self.obj is not None else "")
-
-
-class AsDictMixin(object):
- def asdict(self):
- d = {}
- for attr, value in self.__dict__.items():
- if attr.startswith("_"):
- continue
- if hasattr(value, "asdict"):
- value = value.asdict()
- elif isinstance(value, list):
- value = [v.asdict() if hasattr(v, "asdict") else v for v in value]
- d[attr] = value
- return d
-
-
-class SimpleDescriptor(AsDictMixin):
- """Containers for a bunch of attributes"""
-
- # XXX this is ugly. The 'print' is inappropriate here, and instead of
- # assert, it should simply return True/False
- def compare(self, other):
- # test if this object contains the same data as the other
- for attr in self._attrs:
- try:
- assert getattr(self, attr) == getattr(other, attr)
- except AssertionError:
- print(
- "failed attribute",
- attr,
- getattr(self, attr),
- "!=",
- getattr(other, attr),
- )
-
- def __repr__(self):
- attrs = [f"{a}={repr(getattr(self, a))}," for a in self._attrs]
- attrs = indent("\n".join(attrs), " ")
- return f"{self.__class__.__name__}(\n{attrs}\n)"
-
-
-class SourceDescriptor(SimpleDescriptor):
- """Simple container for data related to the source
-
- .. code:: python
-
- doc = DesignSpaceDocument()
- s1 = SourceDescriptor()
- s1.path = masterPath1
- s1.name = "master.ufo1"
- s1.font = defcon.Font("master.ufo1")
- s1.location = dict(weight=0)
- s1.familyName = "MasterFamilyName"
- s1.styleName = "MasterStyleNameOne"
- s1.localisedFamilyName = dict(fr="Caractère")
- s1.mutedGlyphNames.append("A")
- s1.mutedGlyphNames.append("Z")
- doc.addSource(s1)
-
- """
-
- flavor = "source"
- _attrs = [
- "filename",
- "path",
- "name",
- "layerName",
- "location",
- "copyLib",
- "copyGroups",
- "copyFeatures",
- "muteKerning",
- "muteInfo",
- "mutedGlyphNames",
- "familyName",
- "styleName",
- "localisedFamilyName",
- ]
-
- filename = posixpath_property("_filename")
- path = posixpath_property("_path")
-
- def __init__(
- self,
- *,
- filename=None,
- path=None,
- font=None,
- name=None,
- location=None,
- designLocation=None,
- layerName=None,
- familyName=None,
- styleName=None,
- localisedFamilyName=None,
- copyLib=False,
- copyInfo=False,
- copyGroups=False,
- copyFeatures=False,
- muteKerning=False,
- muteInfo=False,
- mutedGlyphNames=None,
- ):
- self.filename = filename
- """string. A relative path to the source file, **as it is in the document**.
-
- MutatorMath + VarLib.
- """
- self.path = path
- """The absolute path, calculated from filename."""
-
- self.font = font
- """Any Python object. Optional. Points to a representation of this
- source font that is loaded in memory, as a Python object (e.g. a
- ``defcon.Font`` or a ``fontTools.ttFont.TTFont``).
-
- The default document reader will not fill-in this attribute, and the
- default writer will not use this attribute. It is up to the user of
- ``designspaceLib`` to either load the resource identified by
- ``filename`` and store it in this field, or write the contents of
- this field to the disk and make ```filename`` point to that.
- """
-
- self.name = name
- """string. Optional. Unique identifier name for this source.
-
- MutatorMath + varLib.
- """
-
- self.designLocation = (
- designLocation if designLocation is not None else location or {}
- )
- """dict. Axis values for this source, in design space coordinates.
-
- MutatorMath + varLib.
-
- This may be only part of the full design location.
- See :meth:`getFullDesignLocation()`
-
- .. versionadded:: 5.0
- """
-
- self.layerName = layerName
- """string. The name of the layer in the source to look for
- outline data. Default ``None`` which means ``foreground``.
- """
- self.familyName = familyName
- """string. Family name of this source. Though this data
- can be extracted from the font, it can be efficient to have it right
- here.
-
- varLib.
- """
- self.styleName = styleName
- """string. Style name of this source. Though this data
- can be extracted from the font, it can be efficient to have it right
- here.
-
- varLib.
- """
- self.localisedFamilyName = localisedFamilyName or {}
- """dict. A dictionary of localised family name strings, keyed by
- language code.
-
- If present, will be used to build localized names for all instances.
-
- .. versionadded:: 5.0
- """
-
- self.copyLib = copyLib
- """bool. Indicates if the contents of the font.lib need to
- be copied to the instances.
-
- MutatorMath.
-
- .. deprecated:: 5.0
- """
- self.copyInfo = copyInfo
- """bool. Indicates if the non-interpolating font.info needs
- to be copied to the instances.
-
- MutatorMath.
-
- .. deprecated:: 5.0
- """
- self.copyGroups = copyGroups
- """bool. Indicates if the groups need to be copied to the
- instances.
-
- MutatorMath.
-
- .. deprecated:: 5.0
- """
- self.copyFeatures = copyFeatures
- """bool. Indicates if the feature text needs to be
- copied to the instances.
-
- MutatorMath.
-
- .. deprecated:: 5.0
- """
- self.muteKerning = muteKerning
- """bool. Indicates if the kerning data from this source
- needs to be muted (i.e. not be part of the calculations).
-
- MutatorMath only.
- """
- self.muteInfo = muteInfo
- """bool. Indicated if the interpolating font.info data for
- this source needs to be muted.
-
- MutatorMath only.
- """
- self.mutedGlyphNames = mutedGlyphNames or []
- """list. Glyphnames that need to be muted in the
- instances.
-
- MutatorMath only.
- """
-
- @property
- def location(self):
- """dict. Axis values for this source, in design space coordinates.
-
- MutatorMath + varLib.
-
- .. deprecated:: 5.0
- Use the more explicit alias for this property :attr:`designLocation`.
- """
- return self.designLocation
-
- @location.setter
- def location(self, location: Optional[AnisotropicLocationDict]):
- self.designLocation = location or {}
-
- def setFamilyName(self, familyName, languageCode="en"):
- """Setter for :attr:`localisedFamilyName`
-
- .. versionadded:: 5.0
- """
- self.localisedFamilyName[languageCode] = tostr(familyName)
-
- def getFamilyName(self, languageCode="en"):
- """Getter for :attr:`localisedFamilyName`
-
- .. versionadded:: 5.0
- """
- return self.localisedFamilyName.get(languageCode)
-
- def getFullDesignLocation(
- self, doc: "DesignSpaceDocument"
- ) -> AnisotropicLocationDict:
- """Get the complete design location of this source, from its
- :attr:`designLocation` and the document's axis defaults.
-
- .. versionadded:: 5.0
- """
- result: AnisotropicLocationDict = {}
- for axis in doc.axes:
- if axis.name in self.designLocation:
- result[axis.name] = self.designLocation[axis.name]
- else:
- result[axis.name] = axis.map_forward(axis.default)
- return result
-
-
-class RuleDescriptor(SimpleDescriptor):
- """Represents the rule descriptor element: a set of glyph substitutions to
- trigger conditionally in some parts of the designspace.
-
- .. code:: python
-
- r1 = RuleDescriptor()
- r1.name = "unique.rule.name"
- r1.conditionSets.append([dict(name="weight", minimum=-10, maximum=10), dict(...)])
- r1.conditionSets.append([dict(...), dict(...)])
- r1.subs.append(("a", "a.alt"))
-
- .. code:: xml
-
-
-
-
-
-
-
-
-
-
-
-
-
- """
-
- _attrs = ["name", "conditionSets", "subs"] # what do we need here
-
- def __init__(self, *, name=None, conditionSets=None, subs=None):
- self.name = name
- """string. Unique name for this rule. Can be used to reference this rule data."""
- # list of lists of dict(name='aaaa', minimum=0, maximum=1000)
- self.conditionSets = conditionSets or []
- """a list of conditionsets.
-
- - Each conditionset is a list of conditions.
- - Each condition is a dict with ``name``, ``minimum`` and ``maximum`` keys.
- """
- # list of substitutions stored as tuples of glyphnames ("a", "a.alt")
- self.subs = subs or []
- """list of substitutions.
-
- - Each substitution is stored as tuples of glyphnames, e.g. ("a", "a.alt").
- - Note: By default, rules are applied first, before other text
- shaping/OpenType layout, as they are part of the
- `Required Variation Alternates OpenType feature `_.
- See ref:`rules-element` § Attributes.
- """
-
-
-def evaluateRule(rule, location):
- """Return True if any of the rule's conditionsets matches the given location."""
- return any(evaluateConditions(c, location) for c in rule.conditionSets)
-
-
-def evaluateConditions(conditions, location):
- """Return True if all the conditions matches the given location.
-
- - If a condition has no minimum, check for < maximum.
- - If a condition has no maximum, check for > minimum.
- """
- for cd in conditions:
- value = location[cd["name"]]
- if cd.get("minimum") is None:
- if value > cd["maximum"]:
- return False
- elif cd.get("maximum") is None:
- if cd["minimum"] > value:
- return False
- elif not cd["minimum"] <= value <= cd["maximum"]:
- return False
- return True
-
-
-def processRules(rules, location, glyphNames):
- """Apply these rules at this location to these glyphnames.
-
- Return a new list of glyphNames with substitutions applied.
-
- - rule order matters
- """
- newNames = []
- for rule in rules:
- if evaluateRule(rule, location):
- for name in glyphNames:
- swap = False
- for a, b in rule.subs:
- if name == a:
- swap = True
- break
- if swap:
- newNames.append(b)
- else:
- newNames.append(name)
- glyphNames = newNames
- newNames = []
- return glyphNames
-
-
-AnisotropicLocationDict = Dict[str, Union[float, Tuple[float, float]]]
-SimpleLocationDict = Dict[str, float]
-
-
-class AxisMappingDescriptor(SimpleDescriptor):
- """Represents the axis mapping element: mapping an input location
- to an output location in the designspace.
-
- .. code:: python
-
- m1 = AxisMappingDescriptor()
- m1.inputLocation = {"weight": 900, "width": 150}
- m1.outputLocation = {"weight": 870}
-
- .. code:: xml
-
-
-
-
-
-
-
-
-
-
-
-
- """
-
- _attrs = ["inputLocation", "outputLocation"]
-
- def __init__(self, *, inputLocation=None, outputLocation=None):
- self.inputLocation: SimpleLocationDict = inputLocation or {}
- """dict. Axis values for the input of the mapping, in design space coordinates.
-
- varLib.
-
- .. versionadded:: 5.1
- """
- self.outputLocation: SimpleLocationDict = outputLocation or {}
- """dict. Axis values for the output of the mapping, in design space coordinates.
-
- varLib.
-
- .. versionadded:: 5.1
- """
-
-
-class InstanceDescriptor(SimpleDescriptor):
- """Simple container for data related to the instance
-
-
- .. code:: python
-
- i2 = InstanceDescriptor()
- i2.path = instancePath2
- i2.familyName = "InstanceFamilyName"
- i2.styleName = "InstanceStyleName"
- i2.name = "instance.ufo2"
- # anisotropic location
- i2.designLocation = dict(weight=500, width=(400,300))
- i2.postScriptFontName = "InstancePostscriptName"
- i2.styleMapFamilyName = "InstanceStyleMapFamilyName"
- i2.styleMapStyleName = "InstanceStyleMapStyleName"
- i2.lib['com.coolDesignspaceApp.specimenText'] = 'Hamburgerwhatever'
- doc.addInstance(i2)
- """
-
- flavor = "instance"
- _defaultLanguageCode = "en"
- _attrs = [
- "filename",
- "path",
- "name",
- "locationLabel",
- "designLocation",
- "userLocation",
- "familyName",
- "styleName",
- "postScriptFontName",
- "styleMapFamilyName",
- "styleMapStyleName",
- "localisedFamilyName",
- "localisedStyleName",
- "localisedStyleMapFamilyName",
- "localisedStyleMapStyleName",
- "glyphs",
- "kerning",
- "info",
- "lib",
- ]
-
- filename = posixpath_property("_filename")
- path = posixpath_property("_path")
-
- def __init__(
- self,
- *,
- filename=None,
- path=None,
- font=None,
- name=None,
- location=None,
- locationLabel=None,
- designLocation=None,
- userLocation=None,
- familyName=None,
- styleName=None,
- postScriptFontName=None,
- styleMapFamilyName=None,
- styleMapStyleName=None,
- localisedFamilyName=None,
- localisedStyleName=None,
- localisedStyleMapFamilyName=None,
- localisedStyleMapStyleName=None,
- glyphs=None,
- kerning=True,
- info=True,
- lib=None,
- ):
- self.filename = filename
- """string. Relative path to the instance file, **as it is
- in the document**. The file may or may not exist.
-
- MutatorMath + VarLib.
- """
- self.path = path
- """string. Absolute path to the instance file, calculated from
- the document path and the string in the filename attr. The file may
- or may not exist.
-
- MutatorMath.
- """
- self.font = font
- """Same as :attr:`SourceDescriptor.font`
-
- .. seealso:: :attr:`SourceDescriptor.font`
- """
- self.name = name
- """string. Unique identifier name of the instance, used to
- identify it if it needs to be referenced from elsewhere in the
- document.
- """
- self.locationLabel = locationLabel
- """Name of a :class:`LocationLabelDescriptor`. If
- provided, the instance should have the same location as the
- LocationLabel.
-
- .. seealso::
- :meth:`getFullDesignLocation`
- :meth:`getFullUserLocation`
-
- .. versionadded:: 5.0
- """
- self.designLocation: AnisotropicLocationDict = (
- designLocation if designLocation is not None else (location or {})
- )
- """dict. Axis values for this instance, in design space coordinates.
-
- MutatorMath + varLib.
-
- .. seealso:: This may be only part of the full location. See:
- :meth:`getFullDesignLocation`
- :meth:`getFullUserLocation`
-
- .. versionadded:: 5.0
- """
- self.userLocation: SimpleLocationDict = userLocation or {}
- """dict. Axis values for this instance, in user space coordinates.
-
- MutatorMath + varLib.
-
- .. seealso:: This may be only part of the full location. See:
- :meth:`getFullDesignLocation`
- :meth:`getFullUserLocation`
-
- .. versionadded:: 5.0
- """
- self.familyName = familyName
- """string. Family name of this instance.
-
- MutatorMath + varLib.
- """
- self.styleName = styleName
- """string. Style name of this instance.
-
- MutatorMath + varLib.
- """
- self.postScriptFontName = postScriptFontName
- """string. Postscript fontname for this instance.
-
- MutatorMath + varLib.
- """
- self.styleMapFamilyName = styleMapFamilyName
- """string. StyleMap familyname for this instance.
-
- MutatorMath + varLib.
- """
- self.styleMapStyleName = styleMapStyleName
- """string. StyleMap stylename for this instance.
-
- MutatorMath + varLib.
- """
- self.localisedFamilyName = localisedFamilyName or {}
- """dict. A dictionary of localised family name
- strings, keyed by language code.
- """
- self.localisedStyleName = localisedStyleName or {}
- """dict. A dictionary of localised stylename
- strings, keyed by language code.
- """
- self.localisedStyleMapFamilyName = localisedStyleMapFamilyName or {}
- """A dictionary of localised style map
- familyname strings, keyed by language code.
- """
- self.localisedStyleMapStyleName = localisedStyleMapStyleName or {}
- """A dictionary of localised style map
- stylename strings, keyed by language code.
- """
- self.glyphs = glyphs or {}
- """dict for special master definitions for glyphs. If glyphs
- need special masters (to record the results of executed rules for
- example).
-
- MutatorMath.
-
- .. deprecated:: 5.0
- Use rules or sparse sources instead.
- """
- self.kerning = kerning
- """ bool. Indicates if this instance needs its kerning
- calculated.
-
- MutatorMath.
-
- .. deprecated:: 5.0
- """
- self.info = info
- """bool. Indicated if this instance needs the interpolating
- font.info calculated.
-
- .. deprecated:: 5.0
- """
-
- self.lib = lib or {}
- """Custom data associated with this instance."""
-
- @property
- def location(self):
- """dict. Axis values for this instance.
-
- MutatorMath + varLib.
-
- .. deprecated:: 5.0
- Use the more explicit alias for this property :attr:`designLocation`.
- """
- return self.designLocation
-
- @location.setter
- def location(self, location: Optional[AnisotropicLocationDict]):
- self.designLocation = location or {}
-
- def setStyleName(self, styleName, languageCode="en"):
- """These methods give easier access to the localised names."""
- self.localisedStyleName[languageCode] = tostr(styleName)
-
- def getStyleName(self, languageCode="en"):
- return self.localisedStyleName.get(languageCode)
-
- def setFamilyName(self, familyName, languageCode="en"):
- self.localisedFamilyName[languageCode] = tostr(familyName)
-
- def getFamilyName(self, languageCode="en"):
- return self.localisedFamilyName.get(languageCode)
-
- def setStyleMapStyleName(self, styleMapStyleName, languageCode="en"):
- self.localisedStyleMapStyleName[languageCode] = tostr(styleMapStyleName)
-
- def getStyleMapStyleName(self, languageCode="en"):
- return self.localisedStyleMapStyleName.get(languageCode)
-
- def setStyleMapFamilyName(self, styleMapFamilyName, languageCode="en"):
- self.localisedStyleMapFamilyName[languageCode] = tostr(styleMapFamilyName)
-
- def getStyleMapFamilyName(self, languageCode="en"):
- return self.localisedStyleMapFamilyName.get(languageCode)
-
- def clearLocation(self, axisName: Optional[str] = None):
- """Clear all location-related fields. Ensures that
- :attr:``designLocation`` and :attr:``userLocation`` are dictionaries
- (possibly empty if clearing everything).
-
- In order to update the location of this instance wholesale, a user
- should first clear all the fields, then change the field(s) for which
- they have data.
-
- .. code:: python
-
- instance.clearLocation()
- instance.designLocation = {'Weight': (34, 36.5), 'Width': 100}
- instance.userLocation = {'Opsz': 16}
-
- In order to update a single axis location, the user should only clear
- that axis, then edit the values:
-
- .. code:: python
-
- instance.clearLocation('Weight')
- instance.designLocation['Weight'] = (34, 36.5)
-
- Args:
- axisName: if provided, only clear the location for that axis.
-
- .. versionadded:: 5.0
- """
- self.locationLabel = None
- if axisName is None:
- self.designLocation = {}
- self.userLocation = {}
- else:
- if self.designLocation is None:
- self.designLocation = {}
- if axisName in self.designLocation:
- del self.designLocation[axisName]
- if self.userLocation is None:
- self.userLocation = {}
- if axisName in self.userLocation:
- del self.userLocation[axisName]
-
- def getLocationLabelDescriptor(
- self, doc: "DesignSpaceDocument"
- ) -> Optional[LocationLabelDescriptor]:
- """Get the :class:`LocationLabelDescriptor` instance that matches
- this instances's :attr:`locationLabel`.
-
- Raises if the named label can't be found.
-
- .. versionadded:: 5.0
- """
- if self.locationLabel is None:
- return None
- label = doc.getLocationLabel(self.locationLabel)
- if label is None:
- raise DesignSpaceDocumentError(
- "InstanceDescriptor.getLocationLabelDescriptor(): "
- f"unknown location label `{self.locationLabel}` in instance `{self.name}`."
- )
- return label
-
- def getFullDesignLocation(
- self, doc: "DesignSpaceDocument"
- ) -> AnisotropicLocationDict:
- """Get the complete design location of this instance, by combining data
- from the various location fields, default axis values and mappings, and
- top-level location labels.
-
- The source of truth for this instance's location is determined for each
- axis independently by taking the first not-None field in this list:
-
- - ``locationLabel``: the location along this axis is the same as the
- matching STAT format 4 label. No anisotropy.
- - ``designLocation[axisName]``: the explicit design location along this
- axis, possibly anisotropic.
- - ``userLocation[axisName]``: the explicit user location along this
- axis. No anisotropy.
- - ``axis.default``: default axis value. No anisotropy.
-
- .. versionadded:: 5.0
- """
- label = self.getLocationLabelDescriptor(doc)
- if label is not None:
- return doc.map_forward(label.userLocation) # type: ignore
- result: AnisotropicLocationDict = {}
- for axis in doc.axes:
- if axis.name in self.designLocation:
- result[axis.name] = self.designLocation[axis.name]
- elif axis.name in self.userLocation:
- result[axis.name] = axis.map_forward(self.userLocation[axis.name])
- else:
- result[axis.name] = axis.map_forward(axis.default)
- return result
-
- def getFullUserLocation(self, doc: "DesignSpaceDocument") -> SimpleLocationDict:
- """Get the complete user location for this instance.
-
- .. seealso:: :meth:`getFullDesignLocation`
-
- .. versionadded:: 5.0
- """
- return doc.map_backward(self.getFullDesignLocation(doc))
-
-
-def tagForAxisName(name):
- # try to find or make a tag name for this axis name
- names = {
- "weight": ("wght", dict(en="Weight")),
- "width": ("wdth", dict(en="Width")),
- "optical": ("opsz", dict(en="Optical Size")),
- "slant": ("slnt", dict(en="Slant")),
- "italic": ("ital", dict(en="Italic")),
- }
- if name.lower() in names:
- return names[name.lower()]
- if len(name) < 4:
- tag = name + "*" * (4 - len(name))
- else:
- tag = name[:4]
- return tag, dict(en=name)
-
-
-class AbstractAxisDescriptor(SimpleDescriptor):
- flavor = "axis"
-
- def __init__(
- self,
- *,
- tag=None,
- name=None,
- labelNames=None,
- hidden=False,
- map=None,
- axisOrdering=None,
- axisLabels=None,
- ):
- # opentype tag for this axis
- self.tag = tag
- """string. Four letter tag for this axis. Some might be
- registered at the `OpenType
- specification `__.
- Privately-defined axis tags must begin with an uppercase letter and
- use only uppercase letters or digits.
- """
- # name of the axis used in locations
- self.name = name
- """string. Name of the axis as it is used in the location dicts.
-
- MutatorMath + varLib.
- """
- # names for UI purposes, if this is not a standard axis,
- self.labelNames = labelNames or {}
- """dict. When defining a non-registered axis, it will be
- necessary to define user-facing readable names for the axis. Keyed by
- xml:lang code. Values are required to be ``unicode`` strings, even if
- they only contain ASCII characters.
- """
- self.hidden = hidden
- """bool. Whether this axis should be hidden in user interfaces.
- """
- self.map = map or []
- """list of input / output values that can describe a warp of user space
- to design space coordinates. If no map values are present, it is assumed
- user space is the same as design space, as in [(minimum, minimum),
- (maximum, maximum)].
-
- varLib.
- """
- self.axisOrdering = axisOrdering
- """STAT table field ``axisOrdering``.
-
- See: `OTSpec STAT Axis Record `_
-
- .. versionadded:: 5.0
- """
- self.axisLabels: List[AxisLabelDescriptor] = axisLabels or []
- """STAT table entries for Axis Value Tables format 1, 2, 3.
-
- See: `OTSpec STAT Axis Value Tables `_
-
- .. versionadded:: 5.0
- """
-
-
-class AxisDescriptor(AbstractAxisDescriptor):
- """Simple container for the axis data.
-
- Add more localisations?
-
- .. code:: python
-
- a1 = AxisDescriptor()
- a1.minimum = 1
- a1.maximum = 1000
- a1.default = 400
- a1.name = "weight"
- a1.tag = "wght"
- a1.labelNames['fa-IR'] = "قطر"
- a1.labelNames['en'] = "Wéíght"
- a1.map = [(1.0, 10.0), (400.0, 66.0), (1000.0, 990.0)]
- a1.axisOrdering = 1
- a1.axisLabels = [
- AxisLabelDescriptor(name="Regular", userValue=400, elidable=True)
- ]
- doc.addAxis(a1)
- """
-
- _attrs = [
- "tag",
- "name",
- "maximum",
- "minimum",
- "default",
- "map",
- "axisOrdering",
- "axisLabels",
- ]
-
- def __init__(
- self,
- *,
- tag=None,
- name=None,
- labelNames=None,
- minimum=None,
- default=None,
- maximum=None,
- hidden=False,
- map=None,
- axisOrdering=None,
- axisLabels=None,
- ):
- super().__init__(
- tag=tag,
- name=name,
- labelNames=labelNames,
- hidden=hidden,
- map=map,
- axisOrdering=axisOrdering,
- axisLabels=axisLabels,
- )
- self.minimum = minimum
- """number. The minimum value for this axis in user space.
-
- MutatorMath + varLib.
- """
- self.maximum = maximum
- """number. The maximum value for this axis in user space.
-
- MutatorMath + varLib.
- """
- self.default = default
- """number. The default value for this axis, i.e. when a new location is
- created, this is the value this axis will get in user space.
-
- MutatorMath + varLib.
- """
-
- def serialize(self):
- # output to a dict, used in testing
- return dict(
- tag=self.tag,
- name=self.name,
- labelNames=self.labelNames,
- maximum=self.maximum,
- minimum=self.minimum,
- default=self.default,
- hidden=self.hidden,
- map=self.map,
- axisOrdering=self.axisOrdering,
- axisLabels=self.axisLabels,
- )
-
- def map_forward(self, v):
- """Maps value from axis mapping's input (user) to output (design)."""
- from fontTools.varLib.models import piecewiseLinearMap
-
- if not self.map:
- return v
- return piecewiseLinearMap(v, {k: v for k, v in self.map})
-
- def map_backward(self, v):
- """Maps value from axis mapping's output (design) to input (user)."""
- from fontTools.varLib.models import piecewiseLinearMap
-
- if isinstance(v, tuple):
- v = v[0]
- if not self.map:
- return v
- return piecewiseLinearMap(v, {v: k for k, v in self.map})
-
-
-class DiscreteAxisDescriptor(AbstractAxisDescriptor):
- """Container for discrete axis data.
-
- Use this for axes that do not interpolate. The main difference from a
- continuous axis is that a continuous axis has a ``minimum`` and ``maximum``,
- while a discrete axis has a list of ``values``.
-
- Example: an Italic axis with 2 stops, Roman and Italic, that are not
- compatible. The axis still allows to bind together the full font family,
- which is useful for the STAT table, however it can't become a variation
- axis in a VF.
-
- .. code:: python
-
- a2 = DiscreteAxisDescriptor()
- a2.values = [0, 1]
- a2.default = 0
- a2.name = "Italic"
- a2.tag = "ITAL"
- a2.labelNames['fr'] = "Italique"
- a2.map = [(0, 0), (1, -11)]
- a2.axisOrdering = 2
- a2.axisLabels = [
- AxisLabelDescriptor(name="Roman", userValue=0, elidable=True)
- ]
- doc.addAxis(a2)
-
- .. versionadded:: 5.0
- """
-
- flavor = "axis"
- _attrs = ("tag", "name", "values", "default", "map", "axisOrdering", "axisLabels")
-
- def __init__(
- self,
- *,
- tag=None,
- name=None,
- labelNames=None,
- values=None,
- default=None,
- hidden=False,
- map=None,
- axisOrdering=None,
- axisLabels=None,
- ):
- super().__init__(
- tag=tag,
- name=name,
- labelNames=labelNames,
- hidden=hidden,
- map=map,
- axisOrdering=axisOrdering,
- axisLabels=axisLabels,
- )
- self.default: float = default
- """The default value for this axis, i.e. when a new location is
- created, this is the value this axis will get in user space.
-
- However, this default value is less important than in continuous axes:
-
- - it doesn't define the "neutral" version of outlines from which
- deltas would apply, as this axis does not interpolate.
- - it doesn't provide the reference glyph set for the designspace, as
- fonts at each value can have different glyph sets.
- """
- self.values: List[float] = values or []
- """List of possible values for this axis. Contrary to continuous axes,
- only the values in this list can be taken by the axis, nothing in-between.
- """
-
- def map_forward(self, value):
- """Maps value from axis mapping's input to output.
-
- Returns value unchanged if no mapping entry is found.
-
- Note: for discrete axes, each value must have its mapping entry, if
- you intend that value to be mapped.
- """
- return next((v for k, v in self.map if k == value), value)
-
- def map_backward(self, value):
- """Maps value from axis mapping's output to input.
-
- Returns value unchanged if no mapping entry is found.
-
- Note: for discrete axes, each value must have its mapping entry, if
- you intend that value to be mapped.
- """
- if isinstance(value, tuple):
- value = value[0]
- return next((k for k, v in self.map if v == value), value)
-
-
-class AxisLabelDescriptor(SimpleDescriptor):
- """Container for axis label data.
-
- Analogue of OpenType's STAT data for a single axis (formats 1, 2 and 3).
- All values are user values.
- See: `OTSpec STAT Axis value table, format 1, 2, 3 `_
-
- The STAT format of the Axis value depends on which field are filled-in,
- see :meth:`getFormat`
-
- .. versionadded:: 5.0
- """
-
- flavor = "label"
- _attrs = (
- "userMinimum",
- "userValue",
- "userMaximum",
- "name",
- "elidable",
- "olderSibling",
- "linkedUserValue",
- "labelNames",
- )
-
- def __init__(
- self,
- *,
- name,
- userValue,
- userMinimum=None,
- userMaximum=None,
- elidable=False,
- olderSibling=False,
- linkedUserValue=None,
- labelNames=None,
- ):
- self.userMinimum: Optional[float] = userMinimum
- """STAT field ``rangeMinValue`` (format 2)."""
- self.userValue: float = userValue
- """STAT field ``value`` (format 1, 3) or ``nominalValue`` (format 2)."""
- self.userMaximum: Optional[float] = userMaximum
- """STAT field ``rangeMaxValue`` (format 2)."""
- self.name: str = name
- """Label for this axis location, STAT field ``valueNameID``."""
- self.elidable: bool = elidable
- """STAT flag ``ELIDABLE_AXIS_VALUE_NAME``.
-
- See: `OTSpec STAT Flags `_
- """
- self.olderSibling: bool = olderSibling
- """STAT flag ``OLDER_SIBLING_FONT_ATTRIBUTE``.
-
- See: `OTSpec STAT Flags `_
- """
- self.linkedUserValue: Optional[float] = linkedUserValue
- """STAT field ``linkedValue`` (format 3)."""
- self.labelNames: MutableMapping[str, str] = labelNames or {}
- """User-facing translations of this location's label. Keyed by
- ``xml:lang`` code.
- """
-
- def getFormat(self) -> int:
- """Determine which format of STAT Axis value to use to encode this label.
-
- =========== ========= =========== =========== ===============
- STAT Format userValue userMinimum userMaximum linkedUserValue
- =========== ========= =========== =========== ===============
- 1 ✅ ❌ ❌ ❌
- 2 ✅ ✅ ✅ ❌
- 3 ✅ ❌ ❌ ✅
- =========== ========= =========== =========== ===============
- """
- if self.linkedUserValue is not None:
- return 3
- if self.userMinimum is not None or self.userMaximum is not None:
- return 2
- return 1
-
- @property
- def defaultName(self) -> str:
- """Return the English name from :attr:`labelNames` or the :attr:`name`."""
- return self.labelNames.get("en") or self.name
-
-
-class LocationLabelDescriptor(SimpleDescriptor):
- """Container for location label data.
-
- Analogue of OpenType's STAT data for a free-floating location (format 4).
- All values are user values.
-
- See: `OTSpec STAT Axis value table, format 4 `_
-
- .. versionadded:: 5.0
- """
-
- flavor = "label"
- _attrs = ("name", "elidable", "olderSibling", "userLocation", "labelNames")
-
- def __init__(
- self,
- *,
- name,
- userLocation,
- elidable=False,
- olderSibling=False,
- labelNames=None,
- ):
- self.name: str = name
- """Label for this named location, STAT field ``valueNameID``."""
- self.userLocation: SimpleLocationDict = userLocation or {}
- """Location in user coordinates along each axis.
-
- If an axis is not mentioned, it is assumed to be at its default location.
-
- .. seealso:: This may be only part of the full location. See:
- :meth:`getFullUserLocation`
- """
- self.elidable: bool = elidable
- """STAT flag ``ELIDABLE_AXIS_VALUE_NAME``.
-
- See: `OTSpec STAT Flags `_
- """
- self.olderSibling: bool = olderSibling
- """STAT flag ``OLDER_SIBLING_FONT_ATTRIBUTE``.
-
- See: `OTSpec STAT Flags `_
- """
- self.labelNames: Dict[str, str] = labelNames or {}
- """User-facing translations of this location's label. Keyed by
- xml:lang code.
- """
-
- @property
- def defaultName(self) -> str:
- """Return the English name from :attr:`labelNames` or the :attr:`name`."""
- return self.labelNames.get("en") or self.name
-
- def getFullUserLocation(self, doc: "DesignSpaceDocument") -> SimpleLocationDict:
- """Get the complete user location of this label, by combining data
- from the explicit user location and default axis values.
-
- .. versionadded:: 5.0
- """
- return {
- axis.name: self.userLocation.get(axis.name, axis.default)
- for axis in doc.axes
- }
-
-
-class VariableFontDescriptor(SimpleDescriptor):
- """Container for variable fonts, sub-spaces of the Designspace.
-
- Use-cases:
-
- - From a single DesignSpace with discrete axes, define 1 variable font
- per value on the discrete axes. Before version 5, you would have needed
- 1 DesignSpace per such variable font, and a lot of data duplication.
- - From a big variable font with many axes, define subsets of that variable
- font that only include some axes and freeze other axes at a given location.
-
- .. versionadded:: 5.0
- """
-
- flavor = "variable-font"
- _attrs = ("filename", "axisSubsets", "lib")
-
- filename = posixpath_property("_filename")
-
- def __init__(self, *, name, filename=None, axisSubsets=None, lib=None):
- self.name: str = name
- """string, required. Name of this variable to identify it during the
- build process and from other parts of the document, and also as a
- filename in case the filename property is empty.
-
- VarLib.
- """
- self.filename: str = filename
- """string, optional. Relative path to the variable font file, **as it is
- in the document**. The file may or may not exist.
-
- If not specified, the :attr:`name` will be used as a basename for the file.
- """
- self.axisSubsets: List[
- Union[RangeAxisSubsetDescriptor, ValueAxisSubsetDescriptor]
- ] = (axisSubsets or [])
- """Axis subsets to include in this variable font.
-
- If an axis is not mentioned, assume that we only want the default
- location of that axis (same as a :class:`ValueAxisSubsetDescriptor`).
- """
- self.lib: MutableMapping[str, Any] = lib or {}
- """Custom data associated with this variable font."""
-
-
-class RangeAxisSubsetDescriptor(SimpleDescriptor):
- """Subset of a continuous axis to include in a variable font.
-
- .. versionadded:: 5.0
- """
-
- flavor = "axis-subset"
- _attrs = ("name", "userMinimum", "userDefault", "userMaximum")
-
- def __init__(
- self, *, name, userMinimum=-math.inf, userDefault=None, userMaximum=math.inf
- ):
- self.name: str = name
- """Name of the :class:`AxisDescriptor` to subset."""
- self.userMinimum: float = userMinimum
- """New minimum value of the axis in the target variable font.
- If not specified, assume the same minimum value as the full axis.
- (default = ``-math.inf``)
- """
- self.userDefault: Optional[float] = userDefault
- """New default value of the axis in the target variable font.
- If not specified, assume the same default value as the full axis.
- (default = ``None``)
- """
- self.userMaximum: float = userMaximum
- """New maximum value of the axis in the target variable font.
- If not specified, assume the same maximum value as the full axis.
- (default = ``math.inf``)
- """
-
-
-class ValueAxisSubsetDescriptor(SimpleDescriptor):
- """Single value of a discrete or continuous axis to use in a variable font.
-
- .. versionadded:: 5.0
- """
-
- flavor = "axis-subset"
- _attrs = ("name", "userValue")
-
- def __init__(self, *, name, userValue):
- self.name: str = name
- """Name of the :class:`AxisDescriptor` or :class:`DiscreteAxisDescriptor`
- to "snapshot" or "freeze".
- """
- self.userValue: float = userValue
- """Value in user coordinates at which to freeze the given axis."""
-
-
-class BaseDocWriter(object):
- _whiteSpace = " "
- axisDescriptorClass = AxisDescriptor
- discreteAxisDescriptorClass = DiscreteAxisDescriptor
- axisLabelDescriptorClass = AxisLabelDescriptor
- axisMappingDescriptorClass = AxisMappingDescriptor
- locationLabelDescriptorClass = LocationLabelDescriptor
- ruleDescriptorClass = RuleDescriptor
- sourceDescriptorClass = SourceDescriptor
- variableFontDescriptorClass = VariableFontDescriptor
- valueAxisSubsetDescriptorClass = ValueAxisSubsetDescriptor
- rangeAxisSubsetDescriptorClass = RangeAxisSubsetDescriptor
- instanceDescriptorClass = InstanceDescriptor
-
- @classmethod
- def getAxisDecriptor(cls):
- return cls.axisDescriptorClass()
-
- @classmethod
- def getAxisMappingDescriptor(cls):
- return cls.axisMappingDescriptorClass()
-
- @classmethod
- def getSourceDescriptor(cls):
- return cls.sourceDescriptorClass()
-
- @classmethod
- def getInstanceDescriptor(cls):
- return cls.instanceDescriptorClass()
-
- @classmethod
- def getRuleDescriptor(cls):
- return cls.ruleDescriptorClass()
-
- def __init__(self, documentPath, documentObject: DesignSpaceDocument):
- self.path = documentPath
- self.documentObject = documentObject
- self.effectiveFormatTuple = self._getEffectiveFormatTuple()
- self.root = ET.Element("designspace")
-
- def write(self, pretty=True, encoding="UTF-8", xml_declaration=True):
- self.root.attrib["format"] = ".".join(str(i) for i in self.effectiveFormatTuple)
-
- if (
- self.documentObject.axes
- or self.documentObject.axisMappings
- or self.documentObject.elidedFallbackName is not None
- ):
- axesElement = ET.Element("axes")
- if self.documentObject.elidedFallbackName is not None:
- axesElement.attrib[
- "elidedfallbackname"
- ] = self.documentObject.elidedFallbackName
- self.root.append(axesElement)
- for axisObject in self.documentObject.axes:
- self._addAxis(axisObject)
-
- if self.documentObject.axisMappings:
- mappingsElement = ET.Element("mappings")
- self.root.findall(".axes")[0].append(mappingsElement)
- for mappingObject in self.documentObject.axisMappings:
- self._addAxisMapping(mappingsElement, mappingObject)
-
- if self.documentObject.locationLabels:
- labelsElement = ET.Element("labels")
- for labelObject in self.documentObject.locationLabels:
- self._addLocationLabel(labelsElement, labelObject)
- self.root.append(labelsElement)
-
- if self.documentObject.rules:
- if getattr(self.documentObject, "rulesProcessingLast", False):
- attributes = {"processing": "last"}
- else:
- attributes = {}
- self.root.append(ET.Element("rules", attributes))
- for ruleObject in self.documentObject.rules:
- self._addRule(ruleObject)
-
- if self.documentObject.sources:
- self.root.append(ET.Element("sources"))
- for sourceObject in self.documentObject.sources:
- self._addSource(sourceObject)
-
- if self.documentObject.variableFonts:
- variableFontsElement = ET.Element("variable-fonts")
- for variableFont in self.documentObject.variableFonts:
- self._addVariableFont(variableFontsElement, variableFont)
- self.root.append(variableFontsElement)
-
- if self.documentObject.instances:
- self.root.append(ET.Element("instances"))
- for instanceObject in self.documentObject.instances:
- self._addInstance(instanceObject)
-
- if self.documentObject.lib:
- self._addLib(self.root, self.documentObject.lib, 2)
-
- tree = ET.ElementTree(self.root)
- tree.write(
- self.path,
- encoding=encoding,
- method="xml",
- xml_declaration=xml_declaration,
- pretty_print=pretty,
- )
-
- def _getEffectiveFormatTuple(self):
- """Try to use the version specified in the document, or a sufficiently
- recent version to be able to encode what the document contains.
- """
- minVersion = self.documentObject.formatTuple
- if (
- any(
- hasattr(axis, "values")
- or axis.axisOrdering is not None
- or axis.axisLabels
- for axis in self.documentObject.axes
- )
- or self.documentObject.locationLabels
- or any(source.localisedFamilyName for source in self.documentObject.sources)
- or self.documentObject.variableFonts
- or any(
- instance.locationLabel or instance.userLocation
- for instance in self.documentObject.instances
- )
- ):
- if minVersion < (5, 0):
- minVersion = (5, 0)
- if self.documentObject.axisMappings:
- if minVersion < (5, 1):
- minVersion = (5, 1)
- return minVersion
-
- def _makeLocationElement(self, locationObject, name=None):
- """Convert Location dict to a locationElement."""
- locElement = ET.Element("location")
- if name is not None:
- locElement.attrib["name"] = name
- validatedLocation = self.documentObject.newDefaultLocation()
- for axisName, axisValue in locationObject.items():
- if axisName in validatedLocation:
- # only accept values we know
- validatedLocation[axisName] = axisValue
- for dimensionName, dimensionValue in validatedLocation.items():
- dimElement = ET.Element("dimension")
- dimElement.attrib["name"] = dimensionName
- if type(dimensionValue) == tuple:
- dimElement.attrib["xvalue"] = self.intOrFloat(dimensionValue[0])
- dimElement.attrib["yvalue"] = self.intOrFloat(dimensionValue[1])
- else:
- dimElement.attrib["xvalue"] = self.intOrFloat(dimensionValue)
- locElement.append(dimElement)
- return locElement, validatedLocation
-
- def intOrFloat(self, num):
- if int(num) == num:
- return "%d" % num
- return ("%f" % num).rstrip("0").rstrip(".")
-
- def _addRule(self, ruleObject):
- # if none of the conditions have minimum or maximum values, do not add the rule.
- ruleElement = ET.Element("rule")
- if ruleObject.name is not None:
- ruleElement.attrib["name"] = ruleObject.name
- for conditions in ruleObject.conditionSets:
- conditionsetElement = ET.Element("conditionset")
- for cond in conditions:
- if cond.get("minimum") is None and cond.get("maximum") is None:
- # neither is defined, don't add this condition
- continue
- conditionElement = ET.Element("condition")
- conditionElement.attrib["name"] = cond.get("name")
- if cond.get("minimum") is not None:
- conditionElement.attrib["minimum"] = self.intOrFloat(
- cond.get("minimum")
- )
- if cond.get("maximum") is not None:
- conditionElement.attrib["maximum"] = self.intOrFloat(
- cond.get("maximum")
- )
- conditionsetElement.append(conditionElement)
- if len(conditionsetElement):
- ruleElement.append(conditionsetElement)
- for sub in ruleObject.subs:
- subElement = ET.Element("sub")
- subElement.attrib["name"] = sub[0]
- subElement.attrib["with"] = sub[1]
- ruleElement.append(subElement)
- if len(ruleElement):
- self.root.findall(".rules")[0].append(ruleElement)
-
- def _addAxis(self, axisObject):
- axisElement = ET.Element("axis")
- axisElement.attrib["tag"] = axisObject.tag
- axisElement.attrib["name"] = axisObject.name
- self._addLabelNames(axisElement, axisObject.labelNames)
- if axisObject.map:
- for inputValue, outputValue in axisObject.map:
- mapElement = ET.Element("map")
- mapElement.attrib["input"] = self.intOrFloat(inputValue)
- mapElement.attrib["output"] = self.intOrFloat(outputValue)
- axisElement.append(mapElement)
- if axisObject.axisOrdering or axisObject.axisLabels:
- labelsElement = ET.Element("labels")
- if axisObject.axisOrdering is not None:
- labelsElement.attrib["ordering"] = str(axisObject.axisOrdering)
- for label in axisObject.axisLabels:
- self._addAxisLabel(labelsElement, label)
- axisElement.append(labelsElement)
- if hasattr(axisObject, "minimum"):
- axisElement.attrib["minimum"] = self.intOrFloat(axisObject.minimum)
- axisElement.attrib["maximum"] = self.intOrFloat(axisObject.maximum)
- elif hasattr(axisObject, "values"):
- axisElement.attrib["values"] = " ".join(
- self.intOrFloat(v) for v in axisObject.values
- )
- axisElement.attrib["default"] = self.intOrFloat(axisObject.default)
- if axisObject.hidden:
- axisElement.attrib["hidden"] = "1"
- self.root.findall(".axes")[0].append(axisElement)
-
- def _addAxisMapping(self, mappingsElement, mappingObject):
- mappingElement = ET.Element("mapping")
- for what in ("inputLocation", "outputLocation"):
- whatObject = getattr(mappingObject, what, None)
- if whatObject is None:
- continue
- whatElement = ET.Element(what[:-8])
- mappingElement.append(whatElement)
-
- for name, value in whatObject.items():
- dimensionElement = ET.Element("dimension")
- dimensionElement.attrib["name"] = name
- dimensionElement.attrib["xvalue"] = self.intOrFloat(value)
- whatElement.append(dimensionElement)
-
- mappingsElement.append(mappingElement)
-
- def _addAxisLabel(
- self, axisElement: ET.Element, label: AxisLabelDescriptor
- ) -> None:
- labelElement = ET.Element("label")
- labelElement.attrib["uservalue"] = self.intOrFloat(label.userValue)
- if label.userMinimum is not None:
- labelElement.attrib["userminimum"] = self.intOrFloat(label.userMinimum)
- if label.userMaximum is not None:
- labelElement.attrib["usermaximum"] = self.intOrFloat(label.userMaximum)
- labelElement.attrib["name"] = label.name
- if label.elidable:
- labelElement.attrib["elidable"] = "true"
- if label.olderSibling:
- labelElement.attrib["oldersibling"] = "true"
- if label.linkedUserValue is not None:
- labelElement.attrib["linkeduservalue"] = self.intOrFloat(
- label.linkedUserValue
- )
- self._addLabelNames(labelElement, label.labelNames)
- axisElement.append(labelElement)
-
- def _addLabelNames(self, parentElement, labelNames):
- for languageCode, labelName in sorted(labelNames.items()):
- languageElement = ET.Element("labelname")
- languageElement.attrib[XML_LANG] = languageCode
- languageElement.text = labelName
- parentElement.append(languageElement)
-
- def _addLocationLabel(
- self, parentElement: ET.Element, label: LocationLabelDescriptor
- ) -> None:
- labelElement = ET.Element("label")
- labelElement.attrib["name"] = label.name
- if label.elidable:
- labelElement.attrib["elidable"] = "true"
- if label.olderSibling:
- labelElement.attrib["oldersibling"] = "true"
- self._addLabelNames(labelElement, label.labelNames)
- self._addLocationElement(labelElement, userLocation=label.userLocation)
- parentElement.append(labelElement)
-
- def _addLocationElement(
- self,
- parentElement,
- *,
- designLocation: AnisotropicLocationDict = None,
- userLocation: SimpleLocationDict = None,
- ):
- locElement = ET.Element("location")
- for axis in self.documentObject.axes:
- if designLocation is not None and axis.name in designLocation:
- dimElement = ET.Element("dimension")
- dimElement.attrib["name"] = axis.name
- value = designLocation[axis.name]
- if isinstance(value, tuple):
- dimElement.attrib["xvalue"] = self.intOrFloat(value[0])
- dimElement.attrib["yvalue"] = self.intOrFloat(value[1])
- else:
- dimElement.attrib["xvalue"] = self.intOrFloat(value)
- locElement.append(dimElement)
- elif userLocation is not None and axis.name in userLocation:
- dimElement = ET.Element("dimension")
- dimElement.attrib["name"] = axis.name
- value = userLocation[axis.name]
- dimElement.attrib["uservalue"] = self.intOrFloat(value)
- locElement.append(dimElement)
- if len(locElement) > 0:
- parentElement.append(locElement)
-
- def _addInstance(self, instanceObject):
- instanceElement = ET.Element("instance")
- if instanceObject.name is not None:
- instanceElement.attrib["name"] = instanceObject.name
- if instanceObject.locationLabel is not None:
- instanceElement.attrib["location"] = instanceObject.locationLabel
- if instanceObject.familyName is not None:
- instanceElement.attrib["familyname"] = instanceObject.familyName
- if instanceObject.styleName is not None:
- instanceElement.attrib["stylename"] = instanceObject.styleName
- # add localisations
- if instanceObject.localisedStyleName:
- languageCodes = list(instanceObject.localisedStyleName.keys())
- languageCodes.sort()
- for code in languageCodes:
- if code == "en":
- continue # already stored in the element attribute
- localisedStyleNameElement = ET.Element("stylename")
- localisedStyleNameElement.attrib[XML_LANG] = code
- localisedStyleNameElement.text = instanceObject.getStyleName(code)
- instanceElement.append(localisedStyleNameElement)
- if instanceObject.localisedFamilyName:
- languageCodes = list(instanceObject.localisedFamilyName.keys())
- languageCodes.sort()
- for code in languageCodes:
- if code == "en":
- continue # already stored in the element attribute
- localisedFamilyNameElement = ET.Element("familyname")
- localisedFamilyNameElement.attrib[XML_LANG] = code
- localisedFamilyNameElement.text = instanceObject.getFamilyName(code)
- instanceElement.append(localisedFamilyNameElement)
- if instanceObject.localisedStyleMapStyleName:
- languageCodes = list(instanceObject.localisedStyleMapStyleName.keys())
- languageCodes.sort()
- for code in languageCodes:
- if code == "en":
- continue
- localisedStyleMapStyleNameElement = ET.Element("stylemapstylename")
- localisedStyleMapStyleNameElement.attrib[XML_LANG] = code
- localisedStyleMapStyleNameElement.text = (
- instanceObject.getStyleMapStyleName(code)
- )
- instanceElement.append(localisedStyleMapStyleNameElement)
- if instanceObject.localisedStyleMapFamilyName:
- languageCodes = list(instanceObject.localisedStyleMapFamilyName.keys())
- languageCodes.sort()
- for code in languageCodes:
- if code == "en":
- continue
- localisedStyleMapFamilyNameElement = ET.Element("stylemapfamilyname")
- localisedStyleMapFamilyNameElement.attrib[XML_LANG] = code
- localisedStyleMapFamilyNameElement.text = (
- instanceObject.getStyleMapFamilyName(code)
- )
- instanceElement.append(localisedStyleMapFamilyNameElement)
-
- if self.effectiveFormatTuple >= (5, 0):
- if instanceObject.locationLabel is None:
- self._addLocationElement(
- instanceElement,
- designLocation=instanceObject.designLocation,
- userLocation=instanceObject.userLocation,
- )
- else:
- # Pre-version 5.0 code was validating and filling in the location
- # dict while writing it out, as preserved below.
- if instanceObject.location is not None:
- locationElement, instanceObject.location = self._makeLocationElement(
- instanceObject.location
- )
- instanceElement.append(locationElement)
- if instanceObject.filename is not None:
- instanceElement.attrib["filename"] = instanceObject.filename
- if instanceObject.postScriptFontName is not None:
- instanceElement.attrib[
- "postscriptfontname"
- ] = instanceObject.postScriptFontName
- if instanceObject.styleMapFamilyName is not None:
- instanceElement.attrib[
- "stylemapfamilyname"
- ] = instanceObject.styleMapFamilyName
- if instanceObject.styleMapStyleName is not None:
- instanceElement.attrib[
- "stylemapstylename"
- ] = instanceObject.styleMapStyleName
- if self.effectiveFormatTuple < (5, 0):
- # Deprecated members as of version 5.0
- if instanceObject.glyphs:
- if instanceElement.findall(".glyphs") == []:
- glyphsElement = ET.Element("glyphs")
- instanceElement.append(glyphsElement)
- glyphsElement = instanceElement.findall(".glyphs")[0]
- for glyphName, data in sorted(instanceObject.glyphs.items()):
- glyphElement = self._writeGlyphElement(
- instanceElement, instanceObject, glyphName, data
- )
- glyphsElement.append(glyphElement)
- if instanceObject.kerning:
- kerningElement = ET.Element("kerning")
- instanceElement.append(kerningElement)
- if instanceObject.info:
- infoElement = ET.Element("info")
- instanceElement.append(infoElement)
- self._addLib(instanceElement, instanceObject.lib, 4)
- self.root.findall(".instances")[0].append(instanceElement)
-
- def _addSource(self, sourceObject):
- sourceElement = ET.Element("source")
- if sourceObject.filename is not None:
- sourceElement.attrib["filename"] = sourceObject.filename
- if sourceObject.name is not None:
- if sourceObject.name.find("temp_master") != 0:
- # do not save temporary source names
- sourceElement.attrib["name"] = sourceObject.name
- if sourceObject.familyName is not None:
- sourceElement.attrib["familyname"] = sourceObject.familyName
- if sourceObject.styleName is not None:
- sourceElement.attrib["stylename"] = sourceObject.styleName
- if sourceObject.layerName is not None:
- sourceElement.attrib["layer"] = sourceObject.layerName
- if sourceObject.localisedFamilyName:
- languageCodes = list(sourceObject.localisedFamilyName.keys())
- languageCodes.sort()
- for code in languageCodes:
- if code == "en":
- continue # already stored in the element attribute
- localisedFamilyNameElement = ET.Element("familyname")
- localisedFamilyNameElement.attrib[XML_LANG] = code
- localisedFamilyNameElement.text = sourceObject.getFamilyName(code)
- sourceElement.append(localisedFamilyNameElement)
- if sourceObject.copyLib:
- libElement = ET.Element("lib")
- libElement.attrib["copy"] = "1"
- sourceElement.append(libElement)
- if sourceObject.copyGroups:
- groupsElement = ET.Element("groups")
- groupsElement.attrib["copy"] = "1"
- sourceElement.append(groupsElement)
- if sourceObject.copyFeatures:
- featuresElement = ET.Element("features")
- featuresElement.attrib["copy"] = "1"
- sourceElement.append(featuresElement)
- if sourceObject.copyInfo or sourceObject.muteInfo:
- infoElement = ET.Element("info")
- if sourceObject.copyInfo:
- infoElement.attrib["copy"] = "1"
- if sourceObject.muteInfo:
- infoElement.attrib["mute"] = "1"
- sourceElement.append(infoElement)
- if sourceObject.muteKerning:
- kerningElement = ET.Element("kerning")
- kerningElement.attrib["mute"] = "1"
- sourceElement.append(kerningElement)
- if sourceObject.mutedGlyphNames:
- for name in sourceObject.mutedGlyphNames:
- glyphElement = ET.Element("glyph")
- glyphElement.attrib["name"] = name
- glyphElement.attrib["mute"] = "1"
- sourceElement.append(glyphElement)
- if self.effectiveFormatTuple >= (5, 0):
- self._addLocationElement(
- sourceElement, designLocation=sourceObject.location
- )
- else:
- # Pre-version 5.0 code was validating and filling in the location
- # dict while writing it out, as preserved below.
- locationElement, sourceObject.location = self._makeLocationElement(
- sourceObject.location
- )
- sourceElement.append(locationElement)
- self.root.findall(".sources")[0].append(sourceElement)
-
- def _addVariableFont(
- self, parentElement: ET.Element, vf: VariableFontDescriptor
- ) -> None:
- vfElement = ET.Element("variable-font")
- vfElement.attrib["name"] = vf.name
- if vf.filename is not None:
- vfElement.attrib["filename"] = vf.filename
- if vf.axisSubsets:
- subsetsElement = ET.Element("axis-subsets")
- for subset in vf.axisSubsets:
- subsetElement = ET.Element("axis-subset")
- subsetElement.attrib["name"] = subset.name
- # Mypy doesn't support narrowing union types via hasattr()
- # https://mypy.readthedocs.io/en/stable/type_narrowing.html
- # TODO(Python 3.10): use TypeGuard
- if hasattr(subset, "userMinimum"):
- subset = cast(RangeAxisSubsetDescriptor, subset)
- if subset.userMinimum != -math.inf:
- subsetElement.attrib["userminimum"] = self.intOrFloat(
- subset.userMinimum
- )
- if subset.userMaximum != math.inf:
- subsetElement.attrib["usermaximum"] = self.intOrFloat(
- subset.userMaximum
- )
- if subset.userDefault is not None:
- subsetElement.attrib["userdefault"] = self.intOrFloat(
- subset.userDefault
- )
- elif hasattr(subset, "userValue"):
- subset = cast(ValueAxisSubsetDescriptor, subset)
- subsetElement.attrib["uservalue"] = self.intOrFloat(
- subset.userValue
- )
- subsetsElement.append(subsetElement)
- vfElement.append(subsetsElement)
- self._addLib(vfElement, vf.lib, 4)
- parentElement.append(vfElement)
-
- def _addLib(self, parentElement: ET.Element, data: Any, indent_level: int) -> None:
- if not data:
- return
- libElement = ET.Element("lib")
- libElement.append(plistlib.totree(data, indent_level=indent_level))
- parentElement.append(libElement)
-
- def _writeGlyphElement(self, instanceElement, instanceObject, glyphName, data):
- glyphElement = ET.Element("glyph")
- if data.get("mute"):
- glyphElement.attrib["mute"] = "1"
- if data.get("unicodes") is not None:
- glyphElement.attrib["unicode"] = " ".join(
- [hex(u) for u in data.get("unicodes")]
- )
- if data.get("instanceLocation") is not None:
- locationElement, data["instanceLocation"] = self._makeLocationElement(
- data.get("instanceLocation")
- )
- glyphElement.append(locationElement)
- if glyphName is not None:
- glyphElement.attrib["name"] = glyphName
- if data.get("note") is not None:
- noteElement = ET.Element("note")
- noteElement.text = data.get("note")
- glyphElement.append(noteElement)
- if data.get("masters") is not None:
- mastersElement = ET.Element("masters")
- for m in data.get("masters"):
- masterElement = ET.Element("master")
- if m.get("glyphName") is not None:
- masterElement.attrib["glyphname"] = m.get("glyphName")
- if m.get("font") is not None:
- masterElement.attrib["source"] = m.get("font")
- if m.get("location") is not None:
- locationElement, m["location"] = self._makeLocationElement(
- m.get("location")
- )
- masterElement.append(locationElement)
- mastersElement.append(masterElement)
- glyphElement.append(mastersElement)
- return glyphElement
-
-
-class BaseDocReader(LogMixin):
- axisDescriptorClass = AxisDescriptor
- discreteAxisDescriptorClass = DiscreteAxisDescriptor
- axisLabelDescriptorClass = AxisLabelDescriptor
- axisMappingDescriptorClass = AxisMappingDescriptor
- locationLabelDescriptorClass = LocationLabelDescriptor
- ruleDescriptorClass = RuleDescriptor
- sourceDescriptorClass = SourceDescriptor
- variableFontsDescriptorClass = VariableFontDescriptor
- valueAxisSubsetDescriptorClass = ValueAxisSubsetDescriptor
- rangeAxisSubsetDescriptorClass = RangeAxisSubsetDescriptor
- instanceDescriptorClass = InstanceDescriptor
-
- def __init__(self, documentPath, documentObject):
- self.path = documentPath
- self.documentObject = documentObject
- tree = ET.parse(self.path)
- self.root = tree.getroot()
- self.documentObject.formatVersion = self.root.attrib.get("format", "3.0")
- self._axes = []
- self.rules = []
- self.sources = []
- self.instances = []
- self.axisDefaults = {}
- self._strictAxisNames = True
-
- @classmethod
- def fromstring(cls, string, documentObject):
- f = BytesIO(tobytes(string, encoding="utf-8"))
- self = cls(f, documentObject)
- self.path = None
- return self
-
- def read(self):
- self.readAxes()
- self.readLabels()
- self.readRules()
- self.readVariableFonts()
- self.readSources()
- self.readInstances()
- self.readLib()
-
- def readRules(self):
- # we also need to read any conditions that are outside of a condition set.
- rules = []
- rulesElement = self.root.find(".rules")
- if rulesElement is not None:
- processingValue = rulesElement.attrib.get("processing", "first")
- if processingValue not in {"first", "last"}:
- raise DesignSpaceDocumentError(
- " processing attribute value is not valid: %r, "
- "expected 'first' or 'last'" % processingValue
- )
- self.documentObject.rulesProcessingLast = processingValue == "last"
- for ruleElement in self.root.findall(".rules/rule"):
- ruleObject = self.ruleDescriptorClass()
- ruleName = ruleObject.name = ruleElement.attrib.get("name")
- # read any stray conditions outside a condition set
- externalConditions = self._readConditionElements(
- ruleElement,
- ruleName,
- )
- if externalConditions:
- ruleObject.conditionSets.append(externalConditions)
- self.log.info(
- "Found stray rule conditions outside a conditionset. "
- "Wrapped them in a new conditionset."
- )
- # read the conditionsets
- for conditionSetElement in ruleElement.findall(".conditionset"):
- conditionSet = self._readConditionElements(
- conditionSetElement,
- ruleName,
- )
- if conditionSet is not None:
- ruleObject.conditionSets.append(conditionSet)
- for subElement in ruleElement.findall(".sub"):
- a = subElement.attrib["name"]
- b = subElement.attrib["with"]
- ruleObject.subs.append((a, b))
- rules.append(ruleObject)
- self.documentObject.rules = rules
-
- def _readConditionElements(self, parentElement, ruleName=None):
- cds = []
- for conditionElement in parentElement.findall(".condition"):
- cd = {}
- cdMin = conditionElement.attrib.get("minimum")
- if cdMin is not None:
- cd["minimum"] = float(cdMin)
- else:
- # will allow these to be None, assume axis.minimum
- cd["minimum"] = None
- cdMax = conditionElement.attrib.get("maximum")
- if cdMax is not None:
- cd["maximum"] = float(cdMax)
- else:
- # will allow these to be None, assume axis.maximum
- cd["maximum"] = None
- cd["name"] = conditionElement.attrib.get("name")
- # # test for things
- if cd.get("minimum") is None and cd.get("maximum") is None:
- raise DesignSpaceDocumentError(
- "condition missing required minimum or maximum in rule"
- + (" '%s'" % ruleName if ruleName is not None else "")
- )
- cds.append(cd)
- return cds
-
- def readAxes(self):
- # read the axes elements, including the warp map.
- axesElement = self.root.find(".axes")
- if axesElement is not None and "elidedfallbackname" in axesElement.attrib:
- self.documentObject.elidedFallbackName = axesElement.attrib[
- "elidedfallbackname"
- ]
- axisElements = self.root.findall(".axes/axis")
- if not axisElements:
- return
- for axisElement in axisElements:
- if (
- self.documentObject.formatTuple >= (5, 0)
- and "values" in axisElement.attrib
- ):
- axisObject = self.discreteAxisDescriptorClass()
- axisObject.values = [
- float(s) for s in axisElement.attrib["values"].split(" ")
- ]
- else:
- axisObject = self.axisDescriptorClass()
- axisObject.minimum = float(axisElement.attrib.get("minimum"))
- axisObject.maximum = float(axisElement.attrib.get("maximum"))
- axisObject.default = float(axisElement.attrib.get("default"))
- axisObject.name = axisElement.attrib.get("name")
- if axisElement.attrib.get("hidden", False):
- axisObject.hidden = True
- axisObject.tag = axisElement.attrib.get("tag")
- for mapElement in axisElement.findall("map"):
- a = float(mapElement.attrib["input"])
- b = float(mapElement.attrib["output"])
- axisObject.map.append((a, b))
- for labelNameElement in axisElement.findall("labelname"):
- # Note: elementtree reads the "xml:lang" attribute name as
- # '{http://www.w3.org/XML/1998/namespace}lang'
- for key, lang in labelNameElement.items():
- if key == XML_LANG:
- axisObject.labelNames[lang] = tostr(labelNameElement.text)
- labelElement = axisElement.find(".labels")
- if labelElement is not None:
- if "ordering" in labelElement.attrib:
- axisObject.axisOrdering = int(labelElement.attrib["ordering"])
- for label in labelElement.findall(".label"):
- axisObject.axisLabels.append(self.readAxisLabel(label))
- self.documentObject.axes.append(axisObject)
- self.axisDefaults[axisObject.name] = axisObject.default
-
- mappingsElement = self.root.find(".axes/mappings")
- self.documentObject.axisMappings = []
- if mappingsElement is not None:
- for mappingElement in mappingsElement.findall("mapping"):
- inputElement = mappingElement.find("input")
- outputElement = mappingElement.find("output")
- inputLoc = {}
- outputLoc = {}
- for dimElement in inputElement.findall(".dimension"):
- name = dimElement.attrib["name"]
- value = float(dimElement.attrib["xvalue"])
- inputLoc[name] = value
- for dimElement in outputElement.findall(".dimension"):
- name = dimElement.attrib["name"]
- value = float(dimElement.attrib["xvalue"])
- outputLoc[name] = value
- axisMappingObject = self.axisMappingDescriptorClass(
- inputLocation=inputLoc, outputLocation=outputLoc
- )
- self.documentObject.axisMappings.append(axisMappingObject)
-
- def readAxisLabel(self, element: ET.Element):
- xml_attrs = {
- "userminimum",
- "uservalue",
- "usermaximum",
- "name",
- "elidable",
- "oldersibling",
- "linkeduservalue",
- }
- unknown_attrs = set(element.attrib) - xml_attrs
- if unknown_attrs:
- raise DesignSpaceDocumentError(
- f"label element contains unknown attributes: {', '.join(unknown_attrs)}"
- )
-
- name = element.get("name")
- if name is None:
- raise DesignSpaceDocumentError("label element must have a name attribute.")
- valueStr = element.get("uservalue")
- if valueStr is None:
- raise DesignSpaceDocumentError(
- "label element must have a uservalue attribute."
- )
- value = float(valueStr)
- minimumStr = element.get("userminimum")
- minimum = float(minimumStr) if minimumStr is not None else None
- maximumStr = element.get("usermaximum")
- maximum = float(maximumStr) if maximumStr is not None else None
- linkedValueStr = element.get("linkeduservalue")
- linkedValue = float(linkedValueStr) if linkedValueStr is not None else None
- elidable = True if element.get("elidable") == "true" else False
- olderSibling = True if element.get("oldersibling") == "true" else False
- labelNames = {
- lang: label_name.text or ""
- for label_name in element.findall("labelname")
- for attr, lang in label_name.items()
- if attr == XML_LANG
- # Note: elementtree reads the "xml:lang" attribute name as
- # '{http://www.w3.org/XML/1998/namespace}lang'
- }
- return self.axisLabelDescriptorClass(
- name=name,
- userValue=value,
- userMinimum=minimum,
- userMaximum=maximum,
- elidable=elidable,
- olderSibling=olderSibling,
- linkedUserValue=linkedValue,
- labelNames=labelNames,
- )
-
- def readLabels(self):
- if self.documentObject.formatTuple < (5, 0):
- return
-
- xml_attrs = {"name", "elidable", "oldersibling"}
- for labelElement in self.root.findall(".labels/label"):
- unknown_attrs = set(labelElement.attrib) - xml_attrs
- if unknown_attrs:
- raise DesignSpaceDocumentError(
- f"Label element contains unknown attributes: {', '.join(unknown_attrs)}"
- )
-
- name = labelElement.get("name")
- if name is None:
- raise DesignSpaceDocumentError(
- "label element must have a name attribute."
- )
- designLocation, userLocation = self.locationFromElement(labelElement)
- if designLocation:
- raise DesignSpaceDocumentError(
- f' element "{name}" must only have user locations (using uservalue="").'
- )
- elidable = True if labelElement.get("elidable") == "true" else False
- olderSibling = True if labelElement.get("oldersibling") == "true" else False
- labelNames = {
- lang: label_name.text or ""
- for label_name in labelElement.findall("labelname")
- for attr, lang in label_name.items()
- if attr == XML_LANG
- # Note: elementtree reads the "xml:lang" attribute name as
- # '{http://www.w3.org/XML/1998/namespace}lang'
- }
- locationLabel = self.locationLabelDescriptorClass(
- name=name,
- userLocation=userLocation,
- elidable=elidable,
- olderSibling=olderSibling,
- labelNames=labelNames,
- )
- self.documentObject.locationLabels.append(locationLabel)
-
- def readVariableFonts(self):
- if self.documentObject.formatTuple < (5, 0):
- return
-
- xml_attrs = {"name", "filename"}
- for variableFontElement in self.root.findall(".variable-fonts/variable-font"):
- unknown_attrs = set(variableFontElement.attrib) - xml_attrs
- if unknown_attrs:
- raise DesignSpaceDocumentError(
- f"variable-font element contains unknown attributes: {', '.join(unknown_attrs)}"
- )
-
- name = variableFontElement.get("name")
- if name is None:
- raise DesignSpaceDocumentError(
- "variable-font element must have a name attribute."
- )
-
- filename = variableFontElement.get("filename")
-
- axisSubsetsElement = variableFontElement.find(".axis-subsets")
- if axisSubsetsElement is None:
- raise DesignSpaceDocumentError(
- "variable-font element must contain an axis-subsets element."
- )
- axisSubsets = []
- for axisSubset in axisSubsetsElement.iterfind(".axis-subset"):
- axisSubsets.append(self.readAxisSubset(axisSubset))
-
- lib = None
- libElement = variableFontElement.find(".lib")
- if libElement is not None:
- lib = plistlib.fromtree(libElement[0])
-
- variableFont = self.variableFontsDescriptorClass(
- name=name,
- filename=filename,
- axisSubsets=axisSubsets,
- lib=lib,
- )
- self.documentObject.variableFonts.append(variableFont)
-
- def readAxisSubset(self, element: ET.Element):
- if "uservalue" in element.attrib:
- xml_attrs = {"name", "uservalue"}
- unknown_attrs = set(element.attrib) - xml_attrs
- if unknown_attrs:
- raise DesignSpaceDocumentError(
- f"axis-subset element contains unknown attributes: {', '.join(unknown_attrs)}"
- )
-
- name = element.get("name")
- if name is None:
- raise DesignSpaceDocumentError(
- "axis-subset element must have a name attribute."
- )
- userValueStr = element.get("uservalue")
- if userValueStr is None:
- raise DesignSpaceDocumentError(
- "The axis-subset element for a discrete subset must have a uservalue attribute."
- )
- userValue = float(userValueStr)
-
- return self.valueAxisSubsetDescriptorClass(name=name, userValue=userValue)
- else:
- xml_attrs = {"name", "userminimum", "userdefault", "usermaximum"}
- unknown_attrs = set(element.attrib) - xml_attrs
- if unknown_attrs:
- raise DesignSpaceDocumentError(
- f"axis-subset element contains unknown attributes: {', '.join(unknown_attrs)}"
- )
-
- name = element.get("name")
- if name is None:
- raise DesignSpaceDocumentError(
- "axis-subset element must have a name attribute."
- )
-
- userMinimum = element.get("userminimum")
- userDefault = element.get("userdefault")
- userMaximum = element.get("usermaximum")
- if (
- userMinimum is not None
- and userDefault is not None
- and userMaximum is not None
- ):
- return self.rangeAxisSubsetDescriptorClass(
- name=name,
- userMinimum=float(userMinimum),
- userDefault=float(userDefault),
- userMaximum=float(userMaximum),
- )
- if all(v is None for v in (userMinimum, userDefault, userMaximum)):
- return self.rangeAxisSubsetDescriptorClass(name=name)
-
- raise DesignSpaceDocumentError(
- "axis-subset element must have min/max/default values or none at all."
- )
-
- def readSources(self):
- for sourceCount, sourceElement in enumerate(
- self.root.findall(".sources/source")
- ):
- filename = sourceElement.attrib.get("filename")
- if filename is not None and self.path is not None:
- sourcePath = os.path.abspath(
- os.path.join(os.path.dirname(self.path), filename)
- )
- else:
- sourcePath = None
- sourceName = sourceElement.attrib.get("name")
- if sourceName is None:
- # add a temporary source name
- sourceName = "temp_master.%d" % (sourceCount)
- sourceObject = self.sourceDescriptorClass()
- sourceObject.path = sourcePath # absolute path to the ufo source
- sourceObject.filename = filename # path as it is stored in the document
- sourceObject.name = sourceName
- familyName = sourceElement.attrib.get("familyname")
- if familyName is not None:
- sourceObject.familyName = familyName
- styleName = sourceElement.attrib.get("stylename")
- if styleName is not None:
- sourceObject.styleName = styleName
- for familyNameElement in sourceElement.findall("familyname"):
- for key, lang in familyNameElement.items():
- if key == XML_LANG:
- familyName = familyNameElement.text
- sourceObject.setFamilyName(familyName, lang)
- designLocation, userLocation = self.locationFromElement(sourceElement)
- if userLocation:
- raise DesignSpaceDocumentError(
- f' element "{sourceName}" must only have design locations (using xvalue="").'
- )
- sourceObject.location = designLocation
- layerName = sourceElement.attrib.get("layer")
- if layerName is not None:
- sourceObject.layerName = layerName
- for libElement in sourceElement.findall(".lib"):
- if libElement.attrib.get("copy") == "1":
- sourceObject.copyLib = True
- for groupsElement in sourceElement.findall(".groups"):
- if groupsElement.attrib.get("copy") == "1":
- sourceObject.copyGroups = True
- for infoElement in sourceElement.findall(".info"):
- if infoElement.attrib.get("copy") == "1":
- sourceObject.copyInfo = True
- if infoElement.attrib.get("mute") == "1":
- sourceObject.muteInfo = True
- for featuresElement in sourceElement.findall(".features"):
- if featuresElement.attrib.get("copy") == "1":
- sourceObject.copyFeatures = True
- for glyphElement in sourceElement.findall(".glyph"):
- glyphName = glyphElement.attrib.get("name")
- if glyphName is None:
- continue
- if glyphElement.attrib.get("mute") == "1":
- sourceObject.mutedGlyphNames.append(glyphName)
- for kerningElement in sourceElement.findall(".kerning"):
- if kerningElement.attrib.get("mute") == "1":
- sourceObject.muteKerning = True
- self.documentObject.sources.append(sourceObject)
-
- def locationFromElement(self, element):
- """Read a nested ```` element inside the given ``element``.
-
- .. versionchanged:: 5.0
- Return a tuple of (designLocation, userLocation)
- """
- elementLocation = (None, None)
- for locationElement in element.findall(".location"):
- elementLocation = self.readLocationElement(locationElement)
- break
- return elementLocation
-
- def readLocationElement(self, locationElement):
- """Read a ```` element.
-
- .. versionchanged:: 5.0
- Return a tuple of (designLocation, userLocation)
- """
- if self._strictAxisNames and not self.documentObject.axes:
- raise DesignSpaceDocumentError("No axes defined")
- userLoc = {}
- designLoc = {}
- for dimensionElement in locationElement.findall(".dimension"):
- dimName = dimensionElement.attrib.get("name")
- if self._strictAxisNames and dimName not in self.axisDefaults:
- # In case the document contains no axis definitions,
- self.log.warning('Location with undefined axis: "%s".', dimName)
- continue
- userValue = xValue = yValue = None
- try:
- userValue = dimensionElement.attrib.get("uservalue")
- if userValue is not None:
- userValue = float(userValue)
- except ValueError:
- self.log.warning(
- "ValueError in readLocation userValue %3.3f", userValue
- )
- try:
- xValue = dimensionElement.attrib.get("xvalue")
- if xValue is not None:
- xValue = float(xValue)
- except ValueError:
- self.log.warning("ValueError in readLocation xValue %3.3f", xValue)
- try:
- yValue = dimensionElement.attrib.get("yvalue")
- if yValue is not None:
- yValue = float(yValue)
- except ValueError:
- self.log.warning("ValueError in readLocation yValue %3.3f", yValue)
- if userValue is None == xValue is None:
- raise DesignSpaceDocumentError(
- f'Exactly one of uservalue="" or xvalue="" must be provided for location dimension "{dimName}"'
- )
- if yValue is not None:
- if xValue is None:
- raise DesignSpaceDocumentError(
- f'Missing xvalue="" for the location dimension "{dimName}"" with yvalue="{yValue}"'
- )
- designLoc[dimName] = (xValue, yValue)
- elif xValue is not None:
- designLoc[dimName] = xValue
- else:
- userLoc[dimName] = userValue
- return designLoc, userLoc
-
- def readInstances(self, makeGlyphs=True, makeKerning=True, makeInfo=True):
- instanceElements = self.root.findall(".instances/instance")
- for instanceElement in instanceElements:
- self._readSingleInstanceElement(
- instanceElement,
- makeGlyphs=makeGlyphs,
- makeKerning=makeKerning,
- makeInfo=makeInfo,
- )
-
- def _readSingleInstanceElement(
- self, instanceElement, makeGlyphs=True, makeKerning=True, makeInfo=True
- ):
- filename = instanceElement.attrib.get("filename")
- if filename is not None and self.documentObject.path is not None:
- instancePath = os.path.join(
- os.path.dirname(self.documentObject.path), filename
- )
- else:
- instancePath = None
- instanceObject = self.instanceDescriptorClass()
- instanceObject.path = instancePath # absolute path to the instance
- instanceObject.filename = filename # path as it is stored in the document
- name = instanceElement.attrib.get("name")
- if name is not None:
- instanceObject.name = name
- familyname = instanceElement.attrib.get("familyname")
- if familyname is not None:
- instanceObject.familyName = familyname
- stylename = instanceElement.attrib.get("stylename")
- if stylename is not None:
- instanceObject.styleName = stylename
- postScriptFontName = instanceElement.attrib.get("postscriptfontname")
- if postScriptFontName is not None:
- instanceObject.postScriptFontName = postScriptFontName
- styleMapFamilyName = instanceElement.attrib.get("stylemapfamilyname")
- if styleMapFamilyName is not None:
- instanceObject.styleMapFamilyName = styleMapFamilyName
- styleMapStyleName = instanceElement.attrib.get("stylemapstylename")
- if styleMapStyleName is not None:
- instanceObject.styleMapStyleName = styleMapStyleName
- # read localised names
- for styleNameElement in instanceElement.findall("stylename"):
- for key, lang in styleNameElement.items():
- if key == XML_LANG:
- styleName = styleNameElement.text
- instanceObject.setStyleName(styleName, lang)
- for familyNameElement in instanceElement.findall("familyname"):
- for key, lang in familyNameElement.items():
- if key == XML_LANG:
- familyName = familyNameElement.text
- instanceObject.setFamilyName(familyName, lang)
- for styleMapStyleNameElement in instanceElement.findall("stylemapstylename"):
- for key, lang in styleMapStyleNameElement.items():
- if key == XML_LANG:
- styleMapStyleName = styleMapStyleNameElement.text
- instanceObject.setStyleMapStyleName(styleMapStyleName, lang)
- for styleMapFamilyNameElement in instanceElement.findall("stylemapfamilyname"):
- for key, lang in styleMapFamilyNameElement.items():
- if key == XML_LANG:
- styleMapFamilyName = styleMapFamilyNameElement.text
- instanceObject.setStyleMapFamilyName(styleMapFamilyName, lang)
- designLocation, userLocation = self.locationFromElement(instanceElement)
- locationLabel = instanceElement.attrib.get("location")
- if (designLocation or userLocation) and locationLabel is not None:
- raise DesignSpaceDocumentError(
- 'instance element must have at most one of the location="..." attribute or the nested location element'
- )
- instanceObject.locationLabel = locationLabel
- instanceObject.userLocation = userLocation or {}
- instanceObject.designLocation = designLocation or {}
- for glyphElement in instanceElement.findall(".glyphs/glyph"):
- self.readGlyphElement(glyphElement, instanceObject)
- for infoElement in instanceElement.findall("info"):
- self.readInfoElement(infoElement, instanceObject)
- for libElement in instanceElement.findall("lib"):
- self.readLibElement(libElement, instanceObject)
- self.documentObject.instances.append(instanceObject)
-
- def readLibElement(self, libElement, instanceObject):
- """Read the lib element for the given instance."""
- instanceObject.lib = plistlib.fromtree(libElement[0])
-
- def readInfoElement(self, infoElement, instanceObject):
- """Read the info element."""
- instanceObject.info = True
-
- def readGlyphElement(self, glyphElement, instanceObject):
- """
- Read the glyph element, which could look like either one of these:
-
- .. code-block:: xml
-
-
-
-
-
-
-
-
-
- This is an instance from an anisotropic interpolation.
-
-
- """
- glyphData = {}
- glyphName = glyphElement.attrib.get("name")
- if glyphName is None:
- raise DesignSpaceDocumentError("Glyph object without name attribute")
- mute = glyphElement.attrib.get("mute")
- if mute == "1":
- glyphData["mute"] = True
- # unicode
- unicodes = glyphElement.attrib.get("unicode")
- if unicodes is not None:
- try:
- unicodes = [int(u, 16) for u in unicodes.split(" ")]
- glyphData["unicodes"] = unicodes
- except ValueError:
- raise DesignSpaceDocumentError(
- "unicode values %s are not integers" % unicodes
- )
-
- for noteElement in glyphElement.findall(".note"):
- glyphData["note"] = noteElement.text
- break
- designLocation, userLocation = self.locationFromElement(glyphElement)
- if userLocation:
- raise DesignSpaceDocumentError(
- f' element "{glyphName}" must only have design locations (using xvalue="").'
- )
- if designLocation is not None:
- glyphData["instanceLocation"] = designLocation
- glyphSources = None
- for masterElement in glyphElement.findall(".masters/master"):
- fontSourceName = masterElement.attrib.get("source")
- designLocation, userLocation = self.locationFromElement(masterElement)
- if userLocation:
- raise DesignSpaceDocumentError(
- f' element "{fontSourceName}" must only have design locations (using xvalue="").'
- )
- masterGlyphName = masterElement.attrib.get("glyphname")
- if masterGlyphName is None:
- # if we don't read a glyphname, use the one we have
- masterGlyphName = glyphName
- d = dict(
- font=fontSourceName, location=designLocation, glyphName=masterGlyphName
- )
- if glyphSources is None:
- glyphSources = []
- glyphSources.append(d)
- if glyphSources is not None:
- glyphData["masters"] = glyphSources
- instanceObject.glyphs[glyphName] = glyphData
-
- def readLib(self):
- """Read the lib element for the whole document."""
- for libElement in self.root.findall(".lib"):
- self.documentObject.lib = plistlib.fromtree(libElement[0])
-
-
-class DesignSpaceDocument(LogMixin, AsDictMixin):
- """The DesignSpaceDocument object can read and write ``.designspace`` data.
- It imports the axes, sources, variable fonts and instances to very basic
- **descriptor** objects that store the data in attributes. Data is added to
- the document by creating such descriptor objects, filling them with data
- and then adding them to the document. This makes it easy to integrate this
- object in different contexts.
-
- The **DesignSpaceDocument** object can be subclassed to work with
- different objects, as long as they have the same attributes. Reader and
- Writer objects can be subclassed as well.
-
- **Note:** Python attribute names are usually camelCased, the
- corresponding `XML `_ attributes are usually
- all lowercase.
-
- .. code:: python
-
- from fontTools.designspaceLib import DesignSpaceDocument
- doc = DesignSpaceDocument.fromfile("some/path/to/my.designspace")
- doc.formatVersion
- doc.elidedFallbackName
- doc.axes
- doc.axisMappings
- doc.locationLabels
- doc.rules
- doc.rulesProcessingLast
- doc.sources
- doc.variableFonts
- doc.instances
- doc.lib
-
- """
-
- def __init__(self, readerClass=None, writerClass=None):
- self.path = None
- """String, optional. When the document is read from the disk, this is
- the full path that was given to :meth:`read` or :meth:`fromfile`.
- """
- self.filename = None
- """String, optional. When the document is read from the disk, this is
- its original file name, i.e. the last part of its path.
-
- When the document is produced by a Python script and still only exists
- in memory, the producing script can write here an indication of a
- possible "good" filename, in case one wants to save the file somewhere.
- """
-
- self.formatVersion: Optional[str] = None
- """Format version for this document, as a string. E.g. "4.0" """
-
- self.elidedFallbackName: Optional[str] = None
- """STAT Style Attributes Header field ``elidedFallbackNameID``.
-
- See: `OTSpec STAT Style Attributes Header `_
-
- .. versionadded:: 5.0
- """
-
- self.axes: List[Union[AxisDescriptor, DiscreteAxisDescriptor]] = []
- """List of this document's axes."""
-
- self.axisMappings: List[AxisMappingDescriptor] = []
- """List of this document's axis mappings."""
-
- self.locationLabels: List[LocationLabelDescriptor] = []
- """List of this document's STAT format 4 labels.
-
- .. versionadded:: 5.0"""
- self.rules: List[RuleDescriptor] = []
- """List of this document's rules."""
- self.rulesProcessingLast: bool = False
- """This flag indicates whether the substitution rules should be applied
- before or after other glyph substitution features.
-
- - False: before
- - True: after.
-
- Default is False. For new projects, you probably want True. See
- the following issues for more information:
- `fontTools#1371 `__
- `fontTools#2050 `__
-
- If you want to use a different feature altogether, e.g. ``calt``,
- use the lib key ``com.github.fonttools.varLib.featureVarsFeatureTag``
-
- .. code:: xml
-
-
-
- com.github.fonttools.varLib.featureVarsFeatureTag
- calt
-
-
- """
- self.sources: List[SourceDescriptor] = []
- """List of this document's sources."""
- self.variableFonts: List[VariableFontDescriptor] = []
- """List of this document's variable fonts.
-
- .. versionadded:: 5.0"""
- self.instances: List[InstanceDescriptor] = []
- """List of this document's instances."""
- self.lib: Dict = {}
- """User defined, custom data associated with the whole document.
-
- Use reverse-DNS notation to identify your own data.
- Respect the data stored by others.
- """
-
- self.default: Optional[str] = None
- """Name of the default master.
-
- This attribute is updated by the :meth:`findDefault`
- """
-
- if readerClass is not None:
- self.readerClass = readerClass
- else:
- self.readerClass = BaseDocReader
- if writerClass is not None:
- self.writerClass = writerClass
- else:
- self.writerClass = BaseDocWriter
-
- @classmethod
- def fromfile(cls, path, readerClass=None, writerClass=None):
- """Read a designspace file from ``path`` and return a new instance of
- :class:.
- """
- self = cls(readerClass=readerClass, writerClass=writerClass)
- self.read(path)
- return self
-
- @classmethod
- def fromstring(cls, string, readerClass=None, writerClass=None):
- self = cls(readerClass=readerClass, writerClass=writerClass)
- reader = self.readerClass.fromstring(string, self)
- reader.read()
- if self.sources:
- self.findDefault()
- return self
-
- def tostring(self, encoding=None):
- """Returns the designspace as a string. Default encoding ``utf-8``."""
- if encoding is str or (encoding is not None and encoding.lower() == "unicode"):
- f = StringIO()
- xml_declaration = False
- elif encoding is None or encoding == "utf-8":
- f = BytesIO()
- encoding = "UTF-8"
- xml_declaration = True
- else:
- raise ValueError("unsupported encoding: '%s'" % encoding)
- writer = self.writerClass(f, self)
- writer.write(encoding=encoding, xml_declaration=xml_declaration)
- return f.getvalue()
-
- def read(self, path):
- """Read a designspace file from ``path`` and populates the fields of
- ``self`` with the data.
- """
- if hasattr(path, "__fspath__"): # support os.PathLike objects
- path = path.__fspath__()
- self.path = path
- self.filename = os.path.basename(path)
- reader = self.readerClass(path, self)
- reader.read()
- if self.sources:
- self.findDefault()
-
- def write(self, path):
- """Write this designspace to ``path``."""
- if hasattr(path, "__fspath__"): # support os.PathLike objects
- path = path.__fspath__()
- self.path = path
- self.filename = os.path.basename(path)
- self.updatePaths()
- writer = self.writerClass(path, self)
- writer.write()
-
- def _posixRelativePath(self, otherPath):
- relative = os.path.relpath(otherPath, os.path.dirname(self.path))
- return posix(relative)
-
- def updatePaths(self):
- """
- Right before we save we need to identify and respond to the following situations:
- In each descriptor, we have to do the right thing for the filename attribute.
-
- ::
-
- case 1.
- descriptor.filename == None
- descriptor.path == None
-
- -- action:
- write as is, descriptors will not have a filename attr.
- useless, but no reason to interfere.
-
-
- case 2.
- descriptor.filename == "../something"
- descriptor.path == None
-
- -- action:
- write as is. The filename attr should not be touched.
-
-
- case 3.
- descriptor.filename == None
- descriptor.path == "~/absolute/path/there"
-
- -- action:
- calculate the relative path for filename.
- We're not overwriting some other value for filename, it should be fine
-
-
- case 4.
- descriptor.filename == '../somewhere'
- descriptor.path == "~/absolute/path/there"
-
- -- action:
- there is a conflict between the given filename, and the path.
- So we know where the file is relative to the document.
- Can't guess why they're different, we just choose for path to be correct and update filename.
- """
- assert self.path is not None
- for descriptor in self.sources + self.instances:
- if descriptor.path is not None:
- # case 3 and 4: filename gets updated and relativized
- descriptor.filename = self._posixRelativePath(descriptor.path)
-
- def addSource(self, sourceDescriptor: SourceDescriptor):
- """Add the given ``sourceDescriptor`` to ``doc.sources``."""
- self.sources.append(sourceDescriptor)
-
- def addSourceDescriptor(self, **kwargs):
- """Instantiate a new :class:`SourceDescriptor` using the given
- ``kwargs`` and add it to ``doc.sources``.
- """
- source = self.writerClass.sourceDescriptorClass(**kwargs)
- self.addSource(source)
- return source
-
- def addInstance(self, instanceDescriptor: InstanceDescriptor):
- """Add the given ``instanceDescriptor`` to :attr:`instances`."""
- self.instances.append(instanceDescriptor)
-
- def addInstanceDescriptor(self, **kwargs):
- """Instantiate a new :class:`InstanceDescriptor` using the given
- ``kwargs`` and add it to :attr:`instances`.
- """
- instance = self.writerClass.instanceDescriptorClass(**kwargs)
- self.addInstance(instance)
- return instance
-
- def addAxis(self, axisDescriptor: Union[AxisDescriptor, DiscreteAxisDescriptor]):
- """Add the given ``axisDescriptor`` to :attr:`axes`."""
- self.axes.append(axisDescriptor)
-
- def addAxisDescriptor(self, **kwargs):
- """Instantiate a new :class:`AxisDescriptor` using the given
- ``kwargs`` and add it to :attr:`axes`.
-
- The axis will be and instance of :class:`DiscreteAxisDescriptor` if
- the ``kwargs`` provide a ``value``, or a :class:`AxisDescriptor` otherwise.
- """
- if "values" in kwargs:
- axis = self.writerClass.discreteAxisDescriptorClass(**kwargs)
- else:
- axis = self.writerClass.axisDescriptorClass(**kwargs)
- self.addAxis(axis)
- return axis
-
- def addAxisMapping(self, axisMappingDescriptor: AxisMappingDescriptor):
- """Add the given ``axisMappingDescriptor`` to :attr:`axisMappings`."""
- self.axisMappings.append(axisMappingDescriptor)
-
- def addAxisMappingDescriptor(self, **kwargs):
- """Instantiate a new :class:`AxisMappingDescriptor` using the given
- ``kwargs`` and add it to :attr:`rules`.
- """
- axisMapping = self.writerClass.axisMappingDescriptorClass(**kwargs)
- self.addAxisMapping(axisMapping)
- return axisMapping
-
- def addRule(self, ruleDescriptor: RuleDescriptor):
- """Add the given ``ruleDescriptor`` to :attr:`rules`."""
- self.rules.append(ruleDescriptor)
-
- def addRuleDescriptor(self, **kwargs):
- """Instantiate a new :class:`RuleDescriptor` using the given
- ``kwargs`` and add it to :attr:`rules`.
- """
- rule = self.writerClass.ruleDescriptorClass(**kwargs)
- self.addRule(rule)
- return rule
-
- def addVariableFont(self, variableFontDescriptor: VariableFontDescriptor):
- """Add the given ``variableFontDescriptor`` to :attr:`variableFonts`.
-
- .. versionadded:: 5.0
- """
- self.variableFonts.append(variableFontDescriptor)
-
- def addVariableFontDescriptor(self, **kwargs):
- """Instantiate a new :class:`VariableFontDescriptor` using the given
- ``kwargs`` and add it to :attr:`variableFonts`.
-
- .. versionadded:: 5.0
- """
- variableFont = self.writerClass.variableFontDescriptorClass(**kwargs)
- self.addVariableFont(variableFont)
- return variableFont
-
- def addLocationLabel(self, locationLabelDescriptor: LocationLabelDescriptor):
- """Add the given ``locationLabelDescriptor`` to :attr:`locationLabels`.
-
- .. versionadded:: 5.0
- """
- self.locationLabels.append(locationLabelDescriptor)
-
- def addLocationLabelDescriptor(self, **kwargs):
- """Instantiate a new :class:`LocationLabelDescriptor` using the given
- ``kwargs`` and add it to :attr:`locationLabels`.
-
- .. versionadded:: 5.0
- """
- locationLabel = self.writerClass.locationLabelDescriptorClass(**kwargs)
- self.addLocationLabel(locationLabel)
- return locationLabel
-
- def newDefaultLocation(self):
- """Return a dict with the default location in design space coordinates."""
- # Without OrderedDict, output XML would be non-deterministic.
- # https://github.com/LettError/designSpaceDocument/issues/10
- loc = collections.OrderedDict()
- for axisDescriptor in self.axes:
- loc[axisDescriptor.name] = axisDescriptor.map_forward(
- axisDescriptor.default
- )
- return loc
-
- def labelForUserLocation(
- self, userLocation: SimpleLocationDict
- ) -> Optional[LocationLabelDescriptor]:
- """Return the :class:`LocationLabel` that matches the given
- ``userLocation``, or ``None`` if no such label exists.
-
- .. versionadded:: 5.0
- """
- return next(
- (
- label
- for label in self.locationLabels
- if label.userLocation == userLocation
- ),
- None,
- )
-
- def updateFilenameFromPath(self, masters=True, instances=True, force=False):
- """Set a descriptor filename attr from the path and this document path.
-
- If the filename attribute is not None: skip it.
- """
- if masters:
- for descriptor in self.sources:
- if descriptor.filename is not None and not force:
- continue
- if self.path is not None:
- descriptor.filename = self._posixRelativePath(descriptor.path)
- if instances:
- for descriptor in self.instances:
- if descriptor.filename is not None and not force:
- continue
- if self.path is not None:
- descriptor.filename = self._posixRelativePath(descriptor.path)
-
- def newAxisDescriptor(self):
- """Ask the writer class to make us a new axisDescriptor."""
- return self.writerClass.getAxisDecriptor()
-
- def newSourceDescriptor(self):
- """Ask the writer class to make us a new sourceDescriptor."""
- return self.writerClass.getSourceDescriptor()
-
- def newInstanceDescriptor(self):
- """Ask the writer class to make us a new instanceDescriptor."""
- return self.writerClass.getInstanceDescriptor()
-
- def getAxisOrder(self):
- """Return a list of axis names, in the same order as defined in the document."""
- names = []
- for axisDescriptor in self.axes:
- names.append(axisDescriptor.name)
- return names
-
- def getAxis(self, name: str) -> AxisDescriptor | DiscreteAxisDescriptor | None:
- """Return the axis with the given ``name``, or ``None`` if no such axis exists."""
- return next((axis for axis in self.axes if axis.name == name), None)
-
- def getAxisByTag(self, tag: str) -> AxisDescriptor | DiscreteAxisDescriptor | None:
- """Return the axis with the given ``tag``, or ``None`` if no such axis exists."""
- return next((axis for axis in self.axes if axis.tag == tag), None)
-
- def getLocationLabel(self, name: str) -> Optional[LocationLabelDescriptor]:
- """Return the top-level location label with the given ``name``, or
- ``None`` if no such label exists.
-
- .. versionadded:: 5.0
- """
- for label in self.locationLabels:
- if label.name == name:
- return label
- return None
-
- def map_forward(self, userLocation: SimpleLocationDict) -> SimpleLocationDict:
- """Map a user location to a design location.
-
- Assume that missing coordinates are at the default location for that axis.
-
- Note: the output won't be anisotropic, only the xvalue is set.
-
- .. versionadded:: 5.0
- """
- return {
- axis.name: axis.map_forward(userLocation.get(axis.name, axis.default))
- for axis in self.axes
- }
-
- def map_backward(
- self, designLocation: AnisotropicLocationDict
- ) -> SimpleLocationDict:
- """Map a design location to a user location.
-
- Assume that missing coordinates are at the default location for that axis.
-
- When the input has anisotropic locations, only the xvalue is used.
-
- .. versionadded:: 5.0
- """
- return {
- axis.name: (
- axis.map_backward(designLocation[axis.name])
- if axis.name in designLocation
- else axis.default
- )
- for axis in self.axes
- }
-
- def findDefault(self):
- """Set and return SourceDescriptor at the default location or None.
-
- The default location is the set of all `default` values in user space
- of all axes.
-
- This function updates the document's :attr:`default` value.
-
- .. versionchanged:: 5.0
- Allow the default source to not specify some of the axis values, and
- they are assumed to be the default.
- See :meth:`SourceDescriptor.getFullDesignLocation()`
- """
- self.default = None
-
- # Convert the default location from user space to design space before comparing
- # it against the SourceDescriptor locations (always in design space).
- defaultDesignLocation = self.newDefaultLocation()
-
- for sourceDescriptor in self.sources:
- if sourceDescriptor.getFullDesignLocation(self) == defaultDesignLocation:
- self.default = sourceDescriptor
- return sourceDescriptor
-
- return None
-
- def normalizeLocation(self, location):
- """Return a dict with normalized axis values."""
- from fontTools.varLib.models import normalizeValue
-
- new = {}
- for axis in self.axes:
- if axis.name not in location:
- # skipping this dimension it seems
- continue
- value = location[axis.name]
- # 'anisotropic' location, take first coord only
- if isinstance(value, tuple):
- value = value[0]
- triple = [
- axis.map_forward(v) for v in (axis.minimum, axis.default, axis.maximum)
- ]
- new[axis.name] = normalizeValue(value, triple)
- return new
-
- def normalize(self):
- """
- Normalise the geometry of this designspace:
-
- - scale all the locations of all masters and instances to the -1 - 0 - 1 value.
- - we need the axis data to do the scaling, so we do those last.
- """
- # masters
- for item in self.sources:
- item.location = self.normalizeLocation(item.location)
- # instances
- for item in self.instances:
- # glyph masters for this instance
- for _, glyphData in item.glyphs.items():
- glyphData["instanceLocation"] = self.normalizeLocation(
- glyphData["instanceLocation"]
- )
- for glyphMaster in glyphData["masters"]:
- glyphMaster["location"] = self.normalizeLocation(
- glyphMaster["location"]
- )
- item.location = self.normalizeLocation(item.location)
- # the axes
- for axis in self.axes:
- # scale the map first
- newMap = []
- for inputValue, outputValue in axis.map:
- newOutputValue = self.normalizeLocation({axis.name: outputValue}).get(
- axis.name
- )
- newMap.append((inputValue, newOutputValue))
- if newMap:
- axis.map = newMap
- # finally the axis values
- minimum = self.normalizeLocation({axis.name: axis.minimum}).get(axis.name)
- maximum = self.normalizeLocation({axis.name: axis.maximum}).get(axis.name)
- default = self.normalizeLocation({axis.name: axis.default}).get(axis.name)
- # and set them in the axis.minimum
- axis.minimum = minimum
- axis.maximum = maximum
- axis.default = default
- # now the rules
- for rule in self.rules:
- newConditionSets = []
- for conditions in rule.conditionSets:
- newConditions = []
- for cond in conditions:
- if cond.get("minimum") is not None:
- minimum = self.normalizeLocation(
- {cond["name"]: cond["minimum"]}
- ).get(cond["name"])
- else:
- minimum = None
- if cond.get("maximum") is not None:
- maximum = self.normalizeLocation(
- {cond["name"]: cond["maximum"]}
- ).get(cond["name"])
- else:
- maximum = None
- newConditions.append(
- dict(name=cond["name"], minimum=minimum, maximum=maximum)
- )
- newConditionSets.append(newConditions)
- rule.conditionSets = newConditionSets
-
- def loadSourceFonts(self, opener, **kwargs):
- """Ensure SourceDescriptor.font attributes are loaded, and return list of fonts.
-
- Takes a callable which initializes a new font object (e.g. TTFont, or
- defcon.Font, etc.) from the SourceDescriptor.path, and sets the
- SourceDescriptor.font attribute.
- If the font attribute is already not None, it is not loaded again.
- Fonts with the same path are only loaded once and shared among SourceDescriptors.
-
- For example, to load UFO sources using defcon:
-
- designspace = DesignSpaceDocument.fromfile("path/to/my.designspace")
- designspace.loadSourceFonts(defcon.Font)
-
- Or to load masters as FontTools binary fonts, including extra options:
-
- designspace.loadSourceFonts(ttLib.TTFont, recalcBBoxes=False)
-
- Args:
- opener (Callable): takes one required positional argument, the source.path,
- and an optional list of keyword arguments, and returns a new font object
- loaded from the path.
- **kwargs: extra options passed on to the opener function.
-
- Returns:
- List of font objects in the order they appear in the sources list.
- """
- # we load fonts with the same source.path only once
- loaded = {}
- fonts = []
- for source in self.sources:
- if source.font is not None: # font already loaded
- fonts.append(source.font)
- continue
- if source.path in loaded:
- source.font = loaded[source.path]
- else:
- if source.path is None:
- raise DesignSpaceDocumentError(
- "Designspace source '%s' has no 'path' attribute"
- % (source.name or "")
- )
- source.font = opener(source.path, **kwargs)
- loaded[source.path] = source.font
- fonts.append(source.font)
- return fonts
-
- @property
- def formatTuple(self):
- """Return the formatVersion as a tuple of (major, minor).
-
- .. versionadded:: 5.0
- """
- if self.formatVersion is None:
- return (5, 0)
- numbers = (int(i) for i in self.formatVersion.split("."))
- major = next(numbers)
- minor = next(numbers, 0)
- return (major, minor)
-
- def getVariableFonts(self) -> List[VariableFontDescriptor]:
- """Return all variable fonts defined in this document, or implicit
- variable fonts that can be built from the document's continuous axes.
-
- In the case of Designspace documents before version 5, the whole
- document was implicitly describing a variable font that covers the
- whole space.
-
- In version 5 and above documents, there can be as many variable fonts
- as there are locations on discrete axes.
-
- .. seealso:: :func:`splitInterpolable`
-
- .. versionadded:: 5.0
- """
- if self.variableFonts:
- return self.variableFonts
-
- variableFonts = []
- discreteAxes = []
- rangeAxisSubsets: List[
- Union[RangeAxisSubsetDescriptor, ValueAxisSubsetDescriptor]
- ] = []
- for axis in self.axes:
- if hasattr(axis, "values"):
- # Mypy doesn't support narrowing union types via hasattr()
- # TODO(Python 3.10): use TypeGuard
- # https://mypy.readthedocs.io/en/stable/type_narrowing.html
- axis = cast(DiscreteAxisDescriptor, axis)
- discreteAxes.append(axis) # type: ignore
- else:
- rangeAxisSubsets.append(RangeAxisSubsetDescriptor(name=axis.name))
- valueCombinations = itertools.product(*[axis.values for axis in discreteAxes])
- for values in valueCombinations:
- basename = None
- if self.filename is not None:
- basename = os.path.splitext(self.filename)[0] + "-VF"
- if self.path is not None:
- basename = os.path.splitext(os.path.basename(self.path))[0] + "-VF"
- if basename is None:
- basename = "VF"
- axisNames = "".join(
- [f"-{axis.tag}{value}" for axis, value in zip(discreteAxes, values)]
- )
- variableFonts.append(
- VariableFontDescriptor(
- name=f"{basename}{axisNames}",
- axisSubsets=rangeAxisSubsets
- + [
- ValueAxisSubsetDescriptor(name=axis.name, userValue=value)
- for axis, value in zip(discreteAxes, values)
- ],
- )
- )
- return variableFonts
-
- def deepcopyExceptFonts(self):
- """Allow deep-copying a DesignSpace document without deep-copying
- attached UFO fonts or TTFont objects. The :attr:`font` attribute
- is shared by reference between the original and the copy.
-
- .. versionadded:: 5.0
- """
- fonts = [source.font for source in self.sources]
- try:
- for source in self.sources:
- source.font = None
- res = copy.deepcopy(self)
- for source, font in zip(res.sources, fonts):
- source.font = font
- return res
- finally:
- for source, font in zip(self.sources, fonts):
- source.font = font
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/designspaceLib/split.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/designspaceLib/split.py
deleted file mode 100644
index 0b7cdf4..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/designspaceLib/split.py
+++ /dev/null
@@ -1,475 +0,0 @@
-"""Allows building all the variable fonts of a DesignSpace version 5 by
-splitting the document into interpolable sub-space, then into each VF.
-"""
-
-from __future__ import annotations
-
-import itertools
-import logging
-import math
-from typing import Any, Callable, Dict, Iterator, List, Tuple, cast
-
-from fontTools.designspaceLib import (
- AxisDescriptor,
- AxisMappingDescriptor,
- DesignSpaceDocument,
- DiscreteAxisDescriptor,
- InstanceDescriptor,
- RuleDescriptor,
- SimpleLocationDict,
- SourceDescriptor,
- VariableFontDescriptor,
-)
-from fontTools.designspaceLib.statNames import StatNames, getStatNames
-from fontTools.designspaceLib.types import (
- ConditionSet,
- Range,
- Region,
- getVFUserRegion,
- locationInRegion,
- regionInRegion,
- userRegionToDesignRegion,
-)
-
-LOGGER = logging.getLogger(__name__)
-
-MakeInstanceFilenameCallable = Callable[
- [DesignSpaceDocument, InstanceDescriptor, StatNames], str
-]
-
-
-def defaultMakeInstanceFilename(
- doc: DesignSpaceDocument, instance: InstanceDescriptor, statNames: StatNames
-) -> str:
- """Default callable to synthesize an instance filename
- when makeNames=True, for instances that don't specify an instance name
- in the designspace. This part of the name generation can be overriden
- because it's not specified by the STAT table.
- """
- familyName = instance.familyName or statNames.familyNames.get("en")
- styleName = instance.styleName or statNames.styleNames.get("en")
- return f"{familyName}-{styleName}.ttf"
-
-
-def splitInterpolable(
- doc: DesignSpaceDocument,
- makeNames: bool = True,
- expandLocations: bool = True,
- makeInstanceFilename: MakeInstanceFilenameCallable = defaultMakeInstanceFilename,
-) -> Iterator[Tuple[SimpleLocationDict, DesignSpaceDocument]]:
- """Split the given DS5 into several interpolable sub-designspaces.
- There are as many interpolable sub-spaces as there are combinations of
- discrete axis values.
-
- E.g. with axes:
- - italic (discrete) Upright or Italic
- - style (discrete) Sans or Serif
- - weight (continuous) 100 to 900
-
- There are 4 sub-spaces in which the Weight axis should interpolate:
- (Upright, Sans), (Upright, Serif), (Italic, Sans) and (Italic, Serif).
-
- The sub-designspaces still include the full axis definitions and STAT data,
- but the rules, sources, variable fonts, instances are trimmed down to only
- keep what falls within the interpolable sub-space.
-
- Args:
- - ``makeNames``: Whether to compute the instance family and style
- names using the STAT data.
- - ``expandLocations``: Whether to turn all locations into "full"
- locations, including implicit default axis values where missing.
- - ``makeInstanceFilename``: Callable to synthesize an instance filename
- when makeNames=True, for instances that don't specify an instance name
- in the designspace. This part of the name generation can be overridden
- because it's not specified by the STAT table.
-
- .. versionadded:: 5.0
- """
- discreteAxes = []
- interpolableUserRegion: Region = {}
- for axis in doc.axes:
- if hasattr(axis, "values"):
- # Mypy doesn't support narrowing union types via hasattr()
- # TODO(Python 3.10): use TypeGuard
- # https://mypy.readthedocs.io/en/stable/type_narrowing.html
- axis = cast(DiscreteAxisDescriptor, axis)
- discreteAxes.append(axis)
- else:
- axis = cast(AxisDescriptor, axis)
- interpolableUserRegion[axis.name] = Range(
- axis.minimum,
- axis.maximum,
- axis.default,
- )
- valueCombinations = itertools.product(*[axis.values for axis in discreteAxes])
- for values in valueCombinations:
- discreteUserLocation = {
- discreteAxis.name: value
- for discreteAxis, value in zip(discreteAxes, values)
- }
- subDoc = _extractSubSpace(
- doc,
- {**interpolableUserRegion, **discreteUserLocation},
- keepVFs=True,
- makeNames=makeNames,
- expandLocations=expandLocations,
- makeInstanceFilename=makeInstanceFilename,
- )
- yield discreteUserLocation, subDoc
-
-
-def splitVariableFonts(
- doc: DesignSpaceDocument,
- makeNames: bool = False,
- expandLocations: bool = False,
- makeInstanceFilename: MakeInstanceFilenameCallable = defaultMakeInstanceFilename,
-) -> Iterator[Tuple[str, DesignSpaceDocument]]:
- """Convert each variable font listed in this document into a standalone
- designspace. This can be used to compile all the variable fonts from a
- format 5 designspace using tools that can only deal with 1 VF at a time.
-
- Args:
- - ``makeNames``: Whether to compute the instance family and style
- names using the STAT data.
- - ``expandLocations``: Whether to turn all locations into "full"
- locations, including implicit default axis values where missing.
- - ``makeInstanceFilename``: Callable to synthesize an instance filename
- when makeNames=True, for instances that don't specify an instance name
- in the designspace. This part of the name generation can be overridden
- because it's not specified by the STAT table.
-
- .. versionadded:: 5.0
- """
- # Make one DesignspaceDoc v5 for each variable font
- for vf in doc.getVariableFonts():
- vfUserRegion = getVFUserRegion(doc, vf)
- vfDoc = _extractSubSpace(
- doc,
- vfUserRegion,
- keepVFs=False,
- makeNames=makeNames,
- expandLocations=expandLocations,
- makeInstanceFilename=makeInstanceFilename,
- )
- vfDoc.lib = {**vfDoc.lib, **vf.lib}
- yield vf.name, vfDoc
-
-
-def convert5to4(
- doc: DesignSpaceDocument,
-) -> Dict[str, DesignSpaceDocument]:
- """Convert each variable font listed in this document into a standalone
- format 4 designspace. This can be used to compile all the variable fonts
- from a format 5 designspace using tools that only know about format 4.
-
- .. versionadded:: 5.0
- """
- vfs = {}
- for _location, subDoc in splitInterpolable(doc):
- for vfName, vfDoc in splitVariableFonts(subDoc):
- vfDoc.formatVersion = "4.1"
- vfs[vfName] = vfDoc
- return vfs
-
-
-def _extractSubSpace(
- doc: DesignSpaceDocument,
- userRegion: Region,
- *,
- keepVFs: bool,
- makeNames: bool,
- expandLocations: bool,
- makeInstanceFilename: MakeInstanceFilenameCallable,
-) -> DesignSpaceDocument:
- subDoc = DesignSpaceDocument()
- # Don't include STAT info
- # FIXME: (Jany) let's think about it. Not include = OK because the point of
- # the splitting is to build VFs and we'll use the STAT data of the full
- # document to generate the STAT of the VFs, so "no need" to have STAT data
- # in sub-docs. Counterpoint: what if someone wants to split this DS for
- # other purposes? Maybe for that it would be useful to also subset the STAT
- # data?
- # subDoc.elidedFallbackName = doc.elidedFallbackName
-
- def maybeExpandDesignLocation(object):
- if expandLocations:
- return object.getFullDesignLocation(doc)
- else:
- return object.designLocation
-
- for axis in doc.axes:
- range = userRegion[axis.name]
- if isinstance(range, Range) and hasattr(axis, "minimum"):
- # Mypy doesn't support narrowing union types via hasattr()
- # TODO(Python 3.10): use TypeGuard
- # https://mypy.readthedocs.io/en/stable/type_narrowing.html
- axis = cast(AxisDescriptor, axis)
- subDoc.addAxis(
- AxisDescriptor(
- # Same info
- tag=axis.tag,
- name=axis.name,
- labelNames=axis.labelNames,
- hidden=axis.hidden,
- # Subset range
- minimum=max(range.minimum, axis.minimum),
- default=range.default or axis.default,
- maximum=min(range.maximum, axis.maximum),
- map=[
- (user, design)
- for user, design in axis.map
- if range.minimum <= user <= range.maximum
- ],
- # Don't include STAT info
- axisOrdering=None,
- axisLabels=None,
- )
- )
-
- subDoc.axisMappings = mappings = []
- subDocAxes = {axis.name for axis in subDoc.axes}
- for mapping in doc.axisMappings:
- if not all(axis in subDocAxes for axis in mapping.inputLocation.keys()):
- continue
- if not all(axis in subDocAxes for axis in mapping.outputLocation.keys()):
- LOGGER.error(
- "In axis mapping from input %s, some output axes are not in the variable-font: %s",
- mapping.inputLocation,
- mapping.outputLocation,
- )
- continue
-
- mappingAxes = set()
- mappingAxes.update(mapping.inputLocation.keys())
- mappingAxes.update(mapping.outputLocation.keys())
- for axis in doc.axes:
- if axis.name not in mappingAxes:
- continue
- range = userRegion[axis.name]
- if (
- range.minimum != axis.minimum
- or (range.default is not None and range.default != axis.default)
- or range.maximum != axis.maximum
- ):
- LOGGER.error(
- "Limiting axis ranges used in elements not supported: %s",
- axis.name,
- )
- continue
-
- mappings.append(
- AxisMappingDescriptor(
- inputLocation=mapping.inputLocation,
- outputLocation=mapping.outputLocation,
- )
- )
-
- # Don't include STAT info
- # subDoc.locationLabels = doc.locationLabels
-
- # Rules: subset them based on conditions
- designRegion = userRegionToDesignRegion(doc, userRegion)
- subDoc.rules = _subsetRulesBasedOnConditions(doc.rules, designRegion)
- subDoc.rulesProcessingLast = doc.rulesProcessingLast
-
- # Sources: keep only the ones that fall within the kept axis ranges
- for source in doc.sources:
- if not locationInRegion(doc.map_backward(source.designLocation), userRegion):
- continue
-
- subDoc.addSource(
- SourceDescriptor(
- filename=source.filename,
- path=source.path,
- font=source.font,
- name=source.name,
- designLocation=_filterLocation(
- userRegion, maybeExpandDesignLocation(source)
- ),
- layerName=source.layerName,
- familyName=source.familyName,
- styleName=source.styleName,
- muteKerning=source.muteKerning,
- muteInfo=source.muteInfo,
- mutedGlyphNames=source.mutedGlyphNames,
- )
- )
-
- # Copy family name translations from the old default source to the new default
- vfDefault = subDoc.findDefault()
- oldDefault = doc.findDefault()
- if vfDefault is not None and oldDefault is not None:
- vfDefault.localisedFamilyName = oldDefault.localisedFamilyName
-
- # Variable fonts: keep only the ones that fall within the kept axis ranges
- if keepVFs:
- # Note: call getVariableFont() to make the implicit VFs explicit
- for vf in doc.getVariableFonts():
- vfUserRegion = getVFUserRegion(doc, vf)
- if regionInRegion(vfUserRegion, userRegion):
- subDoc.addVariableFont(
- VariableFontDescriptor(
- name=vf.name,
- filename=vf.filename,
- axisSubsets=[
- axisSubset
- for axisSubset in vf.axisSubsets
- if isinstance(userRegion[axisSubset.name], Range)
- ],
- lib=vf.lib,
- )
- )
-
- # Instances: same as Sources + compute missing names
- for instance in doc.instances:
- if not locationInRegion(instance.getFullUserLocation(doc), userRegion):
- continue
-
- if makeNames:
- statNames = getStatNames(doc, instance.getFullUserLocation(doc))
- familyName = instance.familyName or statNames.familyNames.get("en")
- styleName = instance.styleName or statNames.styleNames.get("en")
- subDoc.addInstance(
- InstanceDescriptor(
- filename=instance.filename
- or makeInstanceFilename(doc, instance, statNames),
- path=instance.path,
- font=instance.font,
- name=instance.name or f"{familyName} {styleName}",
- userLocation={} if expandLocations else instance.userLocation,
- designLocation=_filterLocation(
- userRegion, maybeExpandDesignLocation(instance)
- ),
- familyName=familyName,
- styleName=styleName,
- postScriptFontName=instance.postScriptFontName
- or statNames.postScriptFontName,
- styleMapFamilyName=instance.styleMapFamilyName
- or statNames.styleMapFamilyNames.get("en"),
- styleMapStyleName=instance.styleMapStyleName
- or statNames.styleMapStyleName,
- localisedFamilyName=instance.localisedFamilyName
- or statNames.familyNames,
- localisedStyleName=instance.localisedStyleName
- or statNames.styleNames,
- localisedStyleMapFamilyName=instance.localisedStyleMapFamilyName
- or statNames.styleMapFamilyNames,
- localisedStyleMapStyleName=instance.localisedStyleMapStyleName
- or {},
- lib=instance.lib,
- )
- )
- else:
- subDoc.addInstance(
- InstanceDescriptor(
- filename=instance.filename,
- path=instance.path,
- font=instance.font,
- name=instance.name,
- userLocation={} if expandLocations else instance.userLocation,
- designLocation=_filterLocation(
- userRegion, maybeExpandDesignLocation(instance)
- ),
- familyName=instance.familyName,
- styleName=instance.styleName,
- postScriptFontName=instance.postScriptFontName,
- styleMapFamilyName=instance.styleMapFamilyName,
- styleMapStyleName=instance.styleMapStyleName,
- localisedFamilyName=instance.localisedFamilyName,
- localisedStyleName=instance.localisedStyleName,
- localisedStyleMapFamilyName=instance.localisedStyleMapFamilyName,
- localisedStyleMapStyleName=instance.localisedStyleMapStyleName,
- lib=instance.lib,
- )
- )
-
- subDoc.lib = doc.lib
-
- return subDoc
-
-
-def _conditionSetFrom(conditionSet: List[Dict[str, Any]]) -> ConditionSet:
- c: Dict[str, Range] = {}
- for condition in conditionSet:
- minimum, maximum = condition.get("minimum"), condition.get("maximum")
- c[condition["name"]] = Range(
- minimum if minimum is not None else -math.inf,
- maximum if maximum is not None else math.inf,
- )
- return c
-
-
-def _subsetRulesBasedOnConditions(
- rules: List[RuleDescriptor], designRegion: Region
-) -> List[RuleDescriptor]:
- # What rules to keep:
- # - Keep the rule if any conditionset is relevant.
- # - A conditionset is relevant if all conditions are relevant or it is empty.
- # - A condition is relevant if
- # - axis is point (C-AP),
- # - and point in condition's range (C-AP-in)
- # (in this case remove the condition because it's always true)
- # - else (C-AP-out) whole conditionset can be discarded (condition false
- # => conditionset false)
- # - axis is range (C-AR),
- # - (C-AR-all) and axis range fully contained in condition range: we can
- # scrap the condition because it's always true
- # - (C-AR-inter) and intersection(axis range, condition range) not empty:
- # keep the condition with the smaller range (= intersection)
- # - (C-AR-none) else, whole conditionset can be discarded
- newRules: List[RuleDescriptor] = []
- for rule in rules:
- newRule: RuleDescriptor = RuleDescriptor(
- name=rule.name, conditionSets=[], subs=rule.subs
- )
- for conditionset in rule.conditionSets:
- cs = _conditionSetFrom(conditionset)
- newConditionset: List[Dict[str, Any]] = []
- discardConditionset = False
- for selectionName, selectionValue in designRegion.items():
- # TODO: Ensure that all(key in conditionset for key in region.keys())?
- if selectionName not in cs:
- # raise Exception("Selection has different axes than the rules")
- continue
- if isinstance(selectionValue, (float, int)): # is point
- # Case C-AP-in
- if selectionValue in cs[selectionName]:
- pass # always matches, conditionset can stay empty for this one.
- # Case C-AP-out
- else:
- discardConditionset = True
- else: # is range
- # Case C-AR-all
- if selectionValue in cs[selectionName]:
- pass # always matches, conditionset can stay empty for this one.
- else:
- intersection = cs[selectionName].intersection(selectionValue)
- # Case C-AR-inter
- if intersection is not None:
- newConditionset.append(
- {
- "name": selectionName,
- "minimum": intersection.minimum,
- "maximum": intersection.maximum,
- }
- )
- # Case C-AR-none
- else:
- discardConditionset = True
- if not discardConditionset:
- newRule.conditionSets.append(newConditionset)
- if newRule.conditionSets:
- newRules.append(newRule)
-
- return newRules
-
-
-def _filterLocation(
- userRegion: Region,
- location: Dict[str, float],
-) -> Dict[str, float]:
- return {
- name: value
- for name, value in location.items()
- if name in userRegion and isinstance(userRegion[name], Range)
- }
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/designspaceLib/statNames.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/designspaceLib/statNames.py
deleted file mode 100644
index 2facb89..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/designspaceLib/statNames.py
+++ /dev/null
@@ -1,255 +0,0 @@
-"""Compute name information for a given location in user-space coordinates
-using STAT data. This can be used to fill-in automatically the names of an
-instance:
-
-.. code:: python
-
- instance = doc.instances[0]
- names = getStatNames(doc, instance.getFullUserLocation(doc))
- print(names.styleNames)
-"""
-from __future__ import annotations
-
-from dataclasses import dataclass
-from typing import Dict, Optional, Tuple, Union
-import logging
-
-from fontTools.designspaceLib import (
- AxisDescriptor,
- AxisLabelDescriptor,
- DesignSpaceDocument,
- DesignSpaceDocumentError,
- DiscreteAxisDescriptor,
- SimpleLocationDict,
- SourceDescriptor,
-)
-
-LOGGER = logging.getLogger(__name__)
-
-# TODO(Python 3.8): use Literal
-# RibbiStyleName = Union[Literal["regular"], Literal["bold"], Literal["italic"], Literal["bold italic"]]
-RibbiStyle = str
-BOLD_ITALIC_TO_RIBBI_STYLE = {
- (False, False): "regular",
- (False, True): "italic",
- (True, False): "bold",
- (True, True): "bold italic",
-}
-
-
-@dataclass
-class StatNames:
- """Name data generated from the STAT table information."""
-
- familyNames: Dict[str, str]
- styleNames: Dict[str, str]
- postScriptFontName: Optional[str]
- styleMapFamilyNames: Dict[str, str]
- styleMapStyleName: Optional[RibbiStyle]
-
-
-def getStatNames(
- doc: DesignSpaceDocument, userLocation: SimpleLocationDict
-) -> StatNames:
- """Compute the family, style, PostScript names of the given ``userLocation``
- using the document's STAT information.
-
- Also computes localizations.
-
- If not enough STAT data is available for a given name, either its dict of
- localized names will be empty (family and style names), or the name will be
- None (PostScript name).
-
- .. versionadded:: 5.0
- """
- familyNames: Dict[str, str] = {}
- defaultSource: Optional[SourceDescriptor] = doc.findDefault()
- if defaultSource is None:
- LOGGER.warning("Cannot determine default source to look up family name.")
- elif defaultSource.familyName is None:
- LOGGER.warning(
- "Cannot look up family name, assign the 'familyname' attribute to the default source."
- )
- else:
- familyNames = {
- "en": defaultSource.familyName,
- **defaultSource.localisedFamilyName,
- }
-
- styleNames: Dict[str, str] = {}
- # If a free-standing label matches the location, use it for name generation.
- label = doc.labelForUserLocation(userLocation)
- if label is not None:
- styleNames = {"en": label.name, **label.labelNames}
- # Otherwise, scour the axis labels for matches.
- else:
- # Gather all languages in which at least one translation is provided
- # Then build names for all these languages, but fallback to English
- # whenever a translation is missing.
- labels = _getAxisLabelsForUserLocation(doc.axes, userLocation)
- if labels:
- languages = set(
- language for label in labels for language in label.labelNames
- )
- languages.add("en")
- for language in languages:
- styleName = " ".join(
- label.labelNames.get(language, label.defaultName)
- for label in labels
- if not label.elidable
- )
- if not styleName and doc.elidedFallbackName is not None:
- styleName = doc.elidedFallbackName
- styleNames[language] = styleName
-
- if "en" not in familyNames or "en" not in styleNames:
- # Not enough information to compute PS names of styleMap names
- return StatNames(
- familyNames=familyNames,
- styleNames=styleNames,
- postScriptFontName=None,
- styleMapFamilyNames={},
- styleMapStyleName=None,
- )
-
- postScriptFontName = f"{familyNames['en']}-{styleNames['en']}".replace(" ", "")
-
- styleMapStyleName, regularUserLocation = _getRibbiStyle(doc, userLocation)
-
- styleNamesForStyleMap = styleNames
- if regularUserLocation != userLocation:
- regularStatNames = getStatNames(doc, regularUserLocation)
- styleNamesForStyleMap = regularStatNames.styleNames
-
- styleMapFamilyNames = {}
- for language in set(familyNames).union(styleNames.keys()):
- familyName = familyNames.get(language, familyNames["en"])
- styleName = styleNamesForStyleMap.get(language, styleNamesForStyleMap["en"])
- styleMapFamilyNames[language] = (familyName + " " + styleName).strip()
-
- return StatNames(
- familyNames=familyNames,
- styleNames=styleNames,
- postScriptFontName=postScriptFontName,
- styleMapFamilyNames=styleMapFamilyNames,
- styleMapStyleName=styleMapStyleName,
- )
-
-
-def _getSortedAxisLabels(
- axes: list[Union[AxisDescriptor, DiscreteAxisDescriptor]],
-) -> Dict[str, list[AxisLabelDescriptor]]:
- """Returns axis labels sorted by their ordering, with unordered ones appended as
- they are listed."""
-
- # First, get the axis labels with explicit ordering...
- sortedAxes = sorted(
- (axis for axis in axes if axis.axisOrdering is not None),
- key=lambda a: a.axisOrdering,
- )
- sortedLabels: Dict[str, list[AxisLabelDescriptor]] = {
- axis.name: axis.axisLabels for axis in sortedAxes
- }
-
- # ... then append the others in the order they appear.
- # NOTE: This relies on Python 3.7+ dict's preserved insertion order.
- for axis in axes:
- if axis.axisOrdering is None:
- sortedLabels[axis.name] = axis.axisLabels
-
- return sortedLabels
-
-
-def _getAxisLabelsForUserLocation(
- axes: list[Union[AxisDescriptor, DiscreteAxisDescriptor]],
- userLocation: SimpleLocationDict,
-) -> list[AxisLabelDescriptor]:
- labels: list[AxisLabelDescriptor] = []
-
- allAxisLabels = _getSortedAxisLabels(axes)
- if allAxisLabels.keys() != userLocation.keys():
- LOGGER.warning(
- f"Mismatch between user location '{userLocation.keys()}' and available "
- f"labels for '{allAxisLabels.keys()}'."
- )
-
- for axisName, axisLabels in allAxisLabels.items():
- userValue = userLocation[axisName]
- label: Optional[AxisLabelDescriptor] = next(
- (
- l
- for l in axisLabels
- if l.userValue == userValue
- or (
- l.userMinimum is not None
- and l.userMaximum is not None
- and l.userMinimum <= userValue <= l.userMaximum
- )
- ),
- None,
- )
- if label is None:
- LOGGER.debug(
- f"Document needs a label for axis '{axisName}', user value '{userValue}'."
- )
- else:
- labels.append(label)
-
- return labels
-
-
-def _getRibbiStyle(
- self: DesignSpaceDocument, userLocation: SimpleLocationDict
-) -> Tuple[RibbiStyle, SimpleLocationDict]:
- """Compute the RIBBI style name of the given user location,
- return the location of the matching Regular in the RIBBI group.
-
- .. versionadded:: 5.0
- """
- regularUserLocation = {}
- axes_by_tag = {axis.tag: axis for axis in self.axes}
-
- bold: bool = False
- italic: bool = False
-
- axis = axes_by_tag.get("wght")
- if axis is not None:
- for regular_label in axis.axisLabels:
- if (
- regular_label.linkedUserValue == userLocation[axis.name]
- # In the "recursive" case where both the Regular has
- # linkedUserValue pointing the Bold, and the Bold has
- # linkedUserValue pointing to the Regular, only consider the
- # first case: Regular (e.g. 400) has linkedUserValue pointing to
- # Bold (e.g. 700, higher than Regular)
- and regular_label.userValue < regular_label.linkedUserValue
- ):
- regularUserLocation[axis.name] = regular_label.userValue
- bold = True
- break
-
- axis = axes_by_tag.get("ital") or axes_by_tag.get("slnt")
- if axis is not None:
- for upright_label in axis.axisLabels:
- if (
- upright_label.linkedUserValue == userLocation[axis.name]
- # In the "recursive" case where both the Upright has
- # linkedUserValue pointing the Italic, and the Italic has
- # linkedUserValue pointing to the Upright, only consider the
- # first case: Upright (e.g. ital=0, slant=0) has
- # linkedUserValue pointing to Italic (e.g ital=1, slant=-12 or
- # slant=12 for backwards italics, in any case higher than
- # Upright in absolute value, hence the abs() below.
- and abs(upright_label.userValue) < abs(upright_label.linkedUserValue)
- ):
- regularUserLocation[axis.name] = upright_label.userValue
- italic = True
- break
-
- return (
- BOLD_ITALIC_TO_RIBBI_STYLE[bold, italic],
- {
- **userLocation,
- **regularUserLocation,
- },
- )
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/designspaceLib/types.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/designspaceLib/types.py
deleted file mode 100644
index 80ba9d6..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/designspaceLib/types.py
+++ /dev/null
@@ -1,147 +0,0 @@
-from __future__ import annotations
-
-from dataclasses import dataclass
-from typing import Dict, List, Optional, Union, cast
-
-from fontTools.designspaceLib import (
- AxisDescriptor,
- DesignSpaceDocument,
- DesignSpaceDocumentError,
- RangeAxisSubsetDescriptor,
- SimpleLocationDict,
- ValueAxisSubsetDescriptor,
- VariableFontDescriptor,
-)
-
-
-def clamp(value, minimum, maximum):
- return min(max(value, minimum), maximum)
-
-
-@dataclass
-class Range:
- minimum: float
- """Inclusive minimum of the range."""
- maximum: float
- """Inclusive maximum of the range."""
- default: float = 0
- """Default value"""
-
- def __post_init__(self):
- self.minimum, self.maximum = sorted((self.minimum, self.maximum))
- self.default = clamp(self.default, self.minimum, self.maximum)
-
- def __contains__(self, value: Union[float, Range]) -> bool:
- if isinstance(value, Range):
- return self.minimum <= value.minimum and value.maximum <= self.maximum
- return self.minimum <= value <= self.maximum
-
- def intersection(self, other: Range) -> Optional[Range]:
- if self.maximum < other.minimum or self.minimum > other.maximum:
- return None
- else:
- return Range(
- max(self.minimum, other.minimum),
- min(self.maximum, other.maximum),
- self.default, # We don't care about the default in this use-case
- )
-
-
-# A region selection is either a range or a single value, as a Designspace v5
-# axis-subset element only allows a single discrete value or a range for a
-# variable-font element.
-Region = Dict[str, Union[Range, float]]
-
-# A conditionset is a set of named ranges.
-ConditionSet = Dict[str, Range]
-
-# A rule is a list of conditionsets where any has to be relevant for the whole rule to be relevant.
-Rule = List[ConditionSet]
-Rules = Dict[str, Rule]
-
-
-def locationInRegion(location: SimpleLocationDict, region: Region) -> bool:
- for name, value in location.items():
- if name not in region:
- return False
- regionValue = region[name]
- if isinstance(regionValue, (float, int)):
- if value != regionValue:
- return False
- else:
- if value not in regionValue:
- return False
- return True
-
-
-def regionInRegion(region: Region, superRegion: Region) -> bool:
- for name, value in region.items():
- if not name in superRegion:
- return False
- superValue = superRegion[name]
- if isinstance(superValue, (float, int)):
- if value != superValue:
- return False
- else:
- if value not in superValue:
- return False
- return True
-
-
-def userRegionToDesignRegion(doc: DesignSpaceDocument, userRegion: Region) -> Region:
- designRegion = {}
- for name, value in userRegion.items():
- axis = doc.getAxis(name)
- if axis is None:
- raise DesignSpaceDocumentError(
- f"Cannot find axis named '{name}' for region."
- )
- if isinstance(value, (float, int)):
- designRegion[name] = axis.map_forward(value)
- else:
- designRegion[name] = Range(
- axis.map_forward(value.minimum),
- axis.map_forward(value.maximum),
- axis.map_forward(value.default),
- )
- return designRegion
-
-
-def getVFUserRegion(doc: DesignSpaceDocument, vf: VariableFontDescriptor) -> Region:
- vfUserRegion: Region = {}
- # For each axis, 2 cases:
- # - it has a range = it's an axis in the VF DS
- # - it's a single location = use it to know which rules should apply in the VF
- for axisSubset in vf.axisSubsets:
- axis = doc.getAxis(axisSubset.name)
- if axis is None:
- raise DesignSpaceDocumentError(
- f"Cannot find axis named '{axisSubset.name}' for variable font '{vf.name}'."
- )
- if hasattr(axisSubset, "userMinimum"):
- # Mypy doesn't support narrowing union types via hasattr()
- # TODO(Python 3.10): use TypeGuard
- # https://mypy.readthedocs.io/en/stable/type_narrowing.html
- axisSubset = cast(RangeAxisSubsetDescriptor, axisSubset)
- if not hasattr(axis, "minimum"):
- raise DesignSpaceDocumentError(
- f"Cannot select a range over '{axis.name}' for variable font '{vf.name}' "
- "because it's a discrete axis, use only 'userValue' instead."
- )
- axis = cast(AxisDescriptor, axis)
- vfUserRegion[axis.name] = Range(
- max(axisSubset.userMinimum, axis.minimum),
- min(axisSubset.userMaximum, axis.maximum),
- axisSubset.userDefault or axis.default,
- )
- else:
- axisSubset = cast(ValueAxisSubsetDescriptor, axisSubset)
- vfUserRegion[axis.name] = axisSubset.userValue
- # Any axis not mentioned explicitly has a single location = default value
- for axis in doc.axes:
- if axis.name not in vfUserRegion:
- assert isinstance(
- axis.default, (int, float)
- ), f"Axis '{axis.name}' has no valid default value."
- vfUserRegion[axis.name] = axis.default
- return vfUserRegion
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/encodings/MacRoman.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/encodings/MacRoman.py
deleted file mode 100644
index ba8bf14..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/encodings/MacRoman.py
+++ /dev/null
@@ -1,258 +0,0 @@
-MacRoman = [
- "NUL",
- "Eth",
- "eth",
- "Lslash",
- "lslash",
- "Scaron",
- "scaron",
- "Yacute",
- "yacute",
- "HT",
- "LF",
- "Thorn",
- "thorn",
- "CR",
- "Zcaron",
- "zcaron",
- "DLE",
- "DC1",
- "DC2",
- "DC3",
- "DC4",
- "onehalf",
- "onequarter",
- "onesuperior",
- "threequarters",
- "threesuperior",
- "twosuperior",
- "brokenbar",
- "minus",
- "multiply",
- "RS",
- "US",
- "space",
- "exclam",
- "quotedbl",
- "numbersign",
- "dollar",
- "percent",
- "ampersand",
- "quotesingle",
- "parenleft",
- "parenright",
- "asterisk",
- "plus",
- "comma",
- "hyphen",
- "period",
- "slash",
- "zero",
- "one",
- "two",
- "three",
- "four",
- "five",
- "six",
- "seven",
- "eight",
- "nine",
- "colon",
- "semicolon",
- "less",
- "equal",
- "greater",
- "question",
- "at",
- "A",
- "B",
- "C",
- "D",
- "E",
- "F",
- "G",
- "H",
- "I",
- "J",
- "K",
- "L",
- "M",
- "N",
- "O",
- "P",
- "Q",
- "R",
- "S",
- "T",
- "U",
- "V",
- "W",
- "X",
- "Y",
- "Z",
- "bracketleft",
- "backslash",
- "bracketright",
- "asciicircum",
- "underscore",
- "grave",
- "a",
- "b",
- "c",
- "d",
- "e",
- "f",
- "g",
- "h",
- "i",
- "j",
- "k",
- "l",
- "m",
- "n",
- "o",
- "p",
- "q",
- "r",
- "s",
- "t",
- "u",
- "v",
- "w",
- "x",
- "y",
- "z",
- "braceleft",
- "bar",
- "braceright",
- "asciitilde",
- "DEL",
- "Adieresis",
- "Aring",
- "Ccedilla",
- "Eacute",
- "Ntilde",
- "Odieresis",
- "Udieresis",
- "aacute",
- "agrave",
- "acircumflex",
- "adieresis",
- "atilde",
- "aring",
- "ccedilla",
- "eacute",
- "egrave",
- "ecircumflex",
- "edieresis",
- "iacute",
- "igrave",
- "icircumflex",
- "idieresis",
- "ntilde",
- "oacute",
- "ograve",
- "ocircumflex",
- "odieresis",
- "otilde",
- "uacute",
- "ugrave",
- "ucircumflex",
- "udieresis",
- "dagger",
- "degree",
- "cent",
- "sterling",
- "section",
- "bullet",
- "paragraph",
- "germandbls",
- "registered",
- "copyright",
- "trademark",
- "acute",
- "dieresis",
- "notequal",
- "AE",
- "Oslash",
- "infinity",
- "plusminus",
- "lessequal",
- "greaterequal",
- "yen",
- "mu",
- "partialdiff",
- "summation",
- "product",
- "pi",
- "integral",
- "ordfeminine",
- "ordmasculine",
- "Omega",
- "ae",
- "oslash",
- "questiondown",
- "exclamdown",
- "logicalnot",
- "radical",
- "florin",
- "approxequal",
- "Delta",
- "guillemotleft",
- "guillemotright",
- "ellipsis",
- "nbspace",
- "Agrave",
- "Atilde",
- "Otilde",
- "OE",
- "oe",
- "endash",
- "emdash",
- "quotedblleft",
- "quotedblright",
- "quoteleft",
- "quoteright",
- "divide",
- "lozenge",
- "ydieresis",
- "Ydieresis",
- "fraction",
- "currency",
- "guilsinglleft",
- "guilsinglright",
- "fi",
- "fl",
- "daggerdbl",
- "periodcentered",
- "quotesinglbase",
- "quotedblbase",
- "perthousand",
- "Acircumflex",
- "Ecircumflex",
- "Aacute",
- "Edieresis",
- "Egrave",
- "Iacute",
- "Icircumflex",
- "Idieresis",
- "Igrave",
- "Oacute",
- "Ocircumflex",
- "apple",
- "Ograve",
- "Uacute",
- "Ucircumflex",
- "Ugrave",
- "dotlessi",
- "circumflex",
- "tilde",
- "macron",
- "breve",
- "dotaccent",
- "ring",
- "cedilla",
- "hungarumlaut",
- "ogonek",
- "caron",
-]
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/encodings/StandardEncoding.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/encodings/StandardEncoding.py
deleted file mode 100644
index bf13886..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/encodings/StandardEncoding.py
+++ /dev/null
@@ -1,258 +0,0 @@
-StandardEncoding = [
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- "space",
- "exclam",
- "quotedbl",
- "numbersign",
- "dollar",
- "percent",
- "ampersand",
- "quoteright",
- "parenleft",
- "parenright",
- "asterisk",
- "plus",
- "comma",
- "hyphen",
- "period",
- "slash",
- "zero",
- "one",
- "two",
- "three",
- "four",
- "five",
- "six",
- "seven",
- "eight",
- "nine",
- "colon",
- "semicolon",
- "less",
- "equal",
- "greater",
- "question",
- "at",
- "A",
- "B",
- "C",
- "D",
- "E",
- "F",
- "G",
- "H",
- "I",
- "J",
- "K",
- "L",
- "M",
- "N",
- "O",
- "P",
- "Q",
- "R",
- "S",
- "T",
- "U",
- "V",
- "W",
- "X",
- "Y",
- "Z",
- "bracketleft",
- "backslash",
- "bracketright",
- "asciicircum",
- "underscore",
- "quoteleft",
- "a",
- "b",
- "c",
- "d",
- "e",
- "f",
- "g",
- "h",
- "i",
- "j",
- "k",
- "l",
- "m",
- "n",
- "o",
- "p",
- "q",
- "r",
- "s",
- "t",
- "u",
- "v",
- "w",
- "x",
- "y",
- "z",
- "braceleft",
- "bar",
- "braceright",
- "asciitilde",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- "exclamdown",
- "cent",
- "sterling",
- "fraction",
- "yen",
- "florin",
- "section",
- "currency",
- "quotesingle",
- "quotedblleft",
- "guillemotleft",
- "guilsinglleft",
- "guilsinglright",
- "fi",
- "fl",
- ".notdef",
- "endash",
- "dagger",
- "daggerdbl",
- "periodcentered",
- ".notdef",
- "paragraph",
- "bullet",
- "quotesinglbase",
- "quotedblbase",
- "quotedblright",
- "guillemotright",
- "ellipsis",
- "perthousand",
- ".notdef",
- "questiondown",
- ".notdef",
- "grave",
- "acute",
- "circumflex",
- "tilde",
- "macron",
- "breve",
- "dotaccent",
- "dieresis",
- ".notdef",
- "ring",
- "cedilla",
- ".notdef",
- "hungarumlaut",
- "ogonek",
- "caron",
- "emdash",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- "AE",
- ".notdef",
- "ordfeminine",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- "Lslash",
- "Oslash",
- "OE",
- "ordmasculine",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
- "ae",
- ".notdef",
- ".notdef",
- ".notdef",
- "dotlessi",
- ".notdef",
- ".notdef",
- "lslash",
- "oslash",
- "oe",
- "germandbls",
- ".notdef",
- ".notdef",
- ".notdef",
- ".notdef",
-]
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/encodings/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/encodings/__init__.py
deleted file mode 100644
index 156cb23..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/encodings/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-"""Empty __init__.py file to signal Python this directory is a package."""
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/encodings/codecs.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/encodings/codecs.py
deleted file mode 100644
index 3ac0268..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/encodings/codecs.py
+++ /dev/null
@@ -1,135 +0,0 @@
-"""Extend the Python codecs module with a few encodings that are used in OpenType (name table)
-but missing from Python. See https://github.com/fonttools/fonttools/issues/236 for details."""
-
-import codecs
-import encodings
-
-
-class ExtendCodec(codecs.Codec):
- def __init__(self, name, base_encoding, mapping):
- self.name = name
- self.base_encoding = base_encoding
- self.mapping = mapping
- self.reverse = {v: k for k, v in mapping.items()}
- self.max_len = max(len(v) for v in mapping.values())
- self.info = codecs.CodecInfo(
- name=self.name, encode=self.encode, decode=self.decode
- )
- codecs.register_error(name, self.error)
-
- def _map(self, mapper, output_type, exc_type, input, errors):
- base_error_handler = codecs.lookup_error(errors)
- length = len(input)
- out = output_type()
- while input:
- # first try to use self.error as the error handler
- try:
- part = mapper(input, self.base_encoding, errors=self.name)
- out += part
- break # All converted
- except exc_type as e:
- # else convert the correct part, handle error as requested and continue
- out += mapper(input[: e.start], self.base_encoding, self.name)
- replacement, pos = base_error_handler(e)
- out += replacement
- input = input[pos:]
- return out, length
-
- def encode(self, input, errors="strict"):
- return self._map(codecs.encode, bytes, UnicodeEncodeError, input, errors)
-
- def decode(self, input, errors="strict"):
- return self._map(codecs.decode, str, UnicodeDecodeError, input, errors)
-
- def error(self, e):
- if isinstance(e, UnicodeDecodeError):
- for end in range(e.start + 1, e.end + 1):
- s = e.object[e.start : end]
- if s in self.mapping:
- return self.mapping[s], end
- elif isinstance(e, UnicodeEncodeError):
- for end in range(e.start + 1, e.start + self.max_len + 1):
- s = e.object[e.start : end]
- if s in self.reverse:
- return self.reverse[s], end
- e.encoding = self.name
- raise e
-
-
-_extended_encodings = {
- "x_mac_japanese_ttx": (
- "shift_jis",
- {
- b"\xFC": chr(0x007C),
- b"\x7E": chr(0x007E),
- b"\x80": chr(0x005C),
- b"\xA0": chr(0x00A0),
- b"\xFD": chr(0x00A9),
- b"\xFE": chr(0x2122),
- b"\xFF": chr(0x2026),
- },
- ),
- "x_mac_trad_chinese_ttx": (
- "big5",
- {
- b"\x80": chr(0x005C),
- b"\xA0": chr(0x00A0),
- b"\xFD": chr(0x00A9),
- b"\xFE": chr(0x2122),
- b"\xFF": chr(0x2026),
- },
- ),
- "x_mac_korean_ttx": (
- "euc_kr",
- {
- b"\x80": chr(0x00A0),
- b"\x81": chr(0x20A9),
- b"\x82": chr(0x2014),
- b"\x83": chr(0x00A9),
- b"\xFE": chr(0x2122),
- b"\xFF": chr(0x2026),
- },
- ),
- "x_mac_simp_chinese_ttx": (
- "gb2312",
- {
- b"\x80": chr(0x00FC),
- b"\xA0": chr(0x00A0),
- b"\xFD": chr(0x00A9),
- b"\xFE": chr(0x2122),
- b"\xFF": chr(0x2026),
- },
- ),
-}
-
-_cache = {}
-
-
-def search_function(name):
- name = encodings.normalize_encoding(name) # Rather undocumented...
- if name in _extended_encodings:
- if name not in _cache:
- base_encoding, mapping = _extended_encodings[name]
- assert name[-4:] == "_ttx"
- # Python 2 didn't have any of the encodings that we are implementing
- # in this file. Python 3 added aliases for the East Asian ones, mapping
- # them "temporarily" to the same base encoding as us, with a comment
- # suggesting that full implementation will appear some time later.
- # As such, try the Python version of the x_mac_... first, if that is found,
- # use *that* as our base encoding. This would make our encoding upgrade
- # to the full encoding when and if Python finally implements that.
- # http://bugs.python.org/issue24041
- base_encodings = [name[:-4], base_encoding]
- for base_encoding in base_encodings:
- try:
- codecs.lookup(base_encoding)
- except LookupError:
- continue
- _cache[name] = ExtendCodec(name, base_encoding, mapping)
- break
- return _cache[name].info
-
- return None
-
-
-codecs.register(search_function)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/__init__.py
deleted file mode 100644
index ae532cd..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-"""fontTools.feaLib -- a package for dealing with OpenType feature files."""
-
-# The structure of OpenType feature files is defined here:
-# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/__main__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/__main__.py
deleted file mode 100644
index a45230e..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/__main__.py
+++ /dev/null
@@ -1,78 +0,0 @@
-from fontTools.ttLib import TTFont
-from fontTools.feaLib.builder import addOpenTypeFeatures, Builder
-from fontTools.feaLib.error import FeatureLibError
-from fontTools import configLogger
-from fontTools.misc.cliTools import makeOutputFileName
-import sys
-import argparse
-import logging
-
-
-log = logging.getLogger("fontTools.feaLib")
-
-
-def main(args=None):
- """Add features from a feature file (.fea) into an OTF font"""
- parser = argparse.ArgumentParser(
- description="Use fontTools to compile OpenType feature files (*.fea)."
- )
- parser.add_argument(
- "input_fea", metavar="FEATURES", help="Path to the feature file"
- )
- parser.add_argument(
- "input_font", metavar="INPUT_FONT", help="Path to the input font"
- )
- parser.add_argument(
- "-o",
- "--output",
- dest="output_font",
- metavar="OUTPUT_FONT",
- help="Path to the output font.",
- )
- parser.add_argument(
- "-t",
- "--tables",
- metavar="TABLE_TAG",
- choices=Builder.supportedTables,
- nargs="+",
- help="Specify the table(s) to be built.",
- )
- parser.add_argument(
- "-d",
- "--debug",
- action="store_true",
- help="Add source-level debugging information to font.",
- )
- parser.add_argument(
- "-v",
- "--verbose",
- help="Increase the logger verbosity. Multiple -v " "options are allowed.",
- action="count",
- default=0,
- )
- parser.add_argument(
- "--traceback", help="show traceback for exceptions.", action="store_true"
- )
- options = parser.parse_args(args)
-
- levels = ["WARNING", "INFO", "DEBUG"]
- configLogger(level=levels[min(len(levels) - 1, options.verbose)])
-
- output_font = options.output_font or makeOutputFileName(options.input_font)
- log.info("Compiling features to '%s'" % (output_font))
-
- font = TTFont(options.input_font)
- try:
- addOpenTypeFeatures(
- font, options.input_fea, tables=options.tables, debug=options.debug
- )
- except FeatureLibError as e:
- if options.traceback:
- raise
- log.error(e)
- sys.exit(1)
- font.save(output_font)
-
-
-if __name__ == "__main__":
- sys.exit(main())
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/ast.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/ast.py
deleted file mode 100644
index 17c6cc3..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/ast.py
+++ /dev/null
@@ -1,2134 +0,0 @@
-from fontTools.feaLib.error import FeatureLibError
-from fontTools.feaLib.location import FeatureLibLocation
-from fontTools.misc.encodingTools import getEncoding
-from fontTools.misc.textTools import byteord, tobytes
-from collections import OrderedDict
-import itertools
-
-SHIFT = " " * 4
-
-__all__ = [
- "Element",
- "FeatureFile",
- "Comment",
- "GlyphName",
- "GlyphClass",
- "GlyphClassName",
- "MarkClassName",
- "AnonymousBlock",
- "Block",
- "FeatureBlock",
- "NestedBlock",
- "LookupBlock",
- "GlyphClassDefinition",
- "GlyphClassDefStatement",
- "MarkClass",
- "MarkClassDefinition",
- "AlternateSubstStatement",
- "Anchor",
- "AnchorDefinition",
- "AttachStatement",
- "AxisValueLocationStatement",
- "BaseAxis",
- "CVParametersNameStatement",
- "ChainContextPosStatement",
- "ChainContextSubstStatement",
- "CharacterStatement",
- "ConditionsetStatement",
- "CursivePosStatement",
- "ElidedFallbackName",
- "ElidedFallbackNameID",
- "Expression",
- "FeatureNameStatement",
- "FeatureReferenceStatement",
- "FontRevisionStatement",
- "HheaField",
- "IgnorePosStatement",
- "IgnoreSubstStatement",
- "IncludeStatement",
- "LanguageStatement",
- "LanguageSystemStatement",
- "LigatureCaretByIndexStatement",
- "LigatureCaretByPosStatement",
- "LigatureSubstStatement",
- "LookupFlagStatement",
- "LookupReferenceStatement",
- "MarkBasePosStatement",
- "MarkLigPosStatement",
- "MarkMarkPosStatement",
- "MultipleSubstStatement",
- "NameRecord",
- "OS2Field",
- "PairPosStatement",
- "ReverseChainSingleSubstStatement",
- "ScriptStatement",
- "SinglePosStatement",
- "SingleSubstStatement",
- "SizeParameters",
- "Statement",
- "STATAxisValueStatement",
- "STATDesignAxisStatement",
- "STATNameStatement",
- "SubtableStatement",
- "TableBlock",
- "ValueRecord",
- "ValueRecordDefinition",
- "VheaField",
-]
-
-
-def deviceToString(device):
- if device is None:
- return ""
- else:
- return "" % ", ".join("%d %d" % t for t in device)
-
-
-fea_keywords = set(
- [
- "anchor",
- "anchordef",
- "anon",
- "anonymous",
- "by",
- "contour",
- "cursive",
- "device",
- "enum",
- "enumerate",
- "excludedflt",
- "exclude_dflt",
- "feature",
- "from",
- "ignore",
- "ignorebaseglyphs",
- "ignoreligatures",
- "ignoremarks",
- "include",
- "includedflt",
- "include_dflt",
- "language",
- "languagesystem",
- "lookup",
- "lookupflag",
- "mark",
- "markattachmenttype",
- "markclass",
- "nameid",
- "null",
- "parameters",
- "pos",
- "position",
- "required",
- "righttoleft",
- "reversesub",
- "rsub",
- "script",
- "sub",
- "substitute",
- "subtable",
- "table",
- "usemarkfilteringset",
- "useextension",
- "valuerecorddef",
- "base",
- "gdef",
- "head",
- "hhea",
- "name",
- "vhea",
- "vmtx",
- ]
-)
-
-
-def asFea(g):
- if hasattr(g, "asFea"):
- return g.asFea()
- elif isinstance(g, tuple) and len(g) == 2:
- return asFea(g[0]) + " - " + asFea(g[1]) # a range
- elif g.lower() in fea_keywords:
- return "\\" + g
- else:
- return g
-
-
-class Element(object):
- """A base class representing "something" in a feature file."""
-
- def __init__(self, location=None):
- #: location of this element as a `FeatureLibLocation` object.
- if location and not isinstance(location, FeatureLibLocation):
- location = FeatureLibLocation(*location)
- self.location = location
-
- def build(self, builder):
- pass
-
- def asFea(self, indent=""):
- """Returns this element as a string of feature code. For block-type
- elements (such as :class:`FeatureBlock`), the `indent` string is
- added to the start of each line in the output."""
- raise NotImplementedError
-
- def __str__(self):
- return self.asFea()
-
-
-class Statement(Element):
- pass
-
-
-class Expression(Element):
- pass
-
-
-class Comment(Element):
- """A comment in a feature file."""
-
- def __init__(self, text, location=None):
- super(Comment, self).__init__(location)
- #: Text of the comment
- self.text = text
-
- def asFea(self, indent=""):
- return self.text
-
-
-class NullGlyph(Expression):
- """The NULL glyph, used in glyph deletion substitutions."""
-
- def __init__(self, location=None):
- Expression.__init__(self, location)
- #: The name itself as a string
-
- def glyphSet(self):
- """The glyphs in this class as a tuple of :class:`GlyphName` objects."""
- return ()
-
- def asFea(self, indent=""):
- return "NULL"
-
-
-class GlyphName(Expression):
- """A single glyph name, such as ``cedilla``."""
-
- def __init__(self, glyph, location=None):
- Expression.__init__(self, location)
- #: The name itself as a string
- self.glyph = glyph
-
- def glyphSet(self):
- """The glyphs in this class as a tuple of :class:`GlyphName` objects."""
- return (self.glyph,)
-
- def asFea(self, indent=""):
- return asFea(self.glyph)
-
-
-class GlyphClass(Expression):
- """A glyph class, such as ``[acute cedilla grave]``."""
-
- def __init__(self, glyphs=None, location=None):
- Expression.__init__(self, location)
- #: The list of glyphs in this class, as :class:`GlyphName` objects.
- self.glyphs = glyphs if glyphs is not None else []
- self.original = []
- self.curr = 0
-
- def glyphSet(self):
- """The glyphs in this class as a tuple of :class:`GlyphName` objects."""
- return tuple(self.glyphs)
-
- def asFea(self, indent=""):
- if len(self.original):
- if self.curr < len(self.glyphs):
- self.original.extend(self.glyphs[self.curr :])
- self.curr = len(self.glyphs)
- return "[" + " ".join(map(asFea, self.original)) + "]"
- else:
- return "[" + " ".join(map(asFea, self.glyphs)) + "]"
-
- def extend(self, glyphs):
- """Add a list of :class:`GlyphName` objects to the class."""
- self.glyphs.extend(glyphs)
-
- def append(self, glyph):
- """Add a single :class:`GlyphName` object to the class."""
- self.glyphs.append(glyph)
-
- def add_range(self, start, end, glyphs):
- """Add a range (e.g. ``A-Z``) to the class. ``start`` and ``end``
- are either :class:`GlyphName` objects or strings representing the
- start and end glyphs in the class, and ``glyphs`` is the full list of
- :class:`GlyphName` objects in the range."""
- if self.curr < len(self.glyphs):
- self.original.extend(self.glyphs[self.curr :])
- self.original.append((start, end))
- self.glyphs.extend(glyphs)
- self.curr = len(self.glyphs)
-
- def add_cid_range(self, start, end, glyphs):
- """Add a range to the class by glyph ID. ``start`` and ``end`` are the
- initial and final IDs, and ``glyphs`` is the full list of
- :class:`GlyphName` objects in the range."""
- if self.curr < len(self.glyphs):
- self.original.extend(self.glyphs[self.curr :])
- self.original.append(("\\{}".format(start), "\\{}".format(end)))
- self.glyphs.extend(glyphs)
- self.curr = len(self.glyphs)
-
- def add_class(self, gc):
- """Add glyphs from the given :class:`GlyphClassName` object to the
- class."""
- if self.curr < len(self.glyphs):
- self.original.extend(self.glyphs[self.curr :])
- self.original.append(gc)
- self.glyphs.extend(gc.glyphSet())
- self.curr = len(self.glyphs)
-
-
-class GlyphClassName(Expression):
- """A glyph class name, such as ``@FRENCH_MARKS``. This must be instantiated
- with a :class:`GlyphClassDefinition` object."""
-
- def __init__(self, glyphclass, location=None):
- Expression.__init__(self, location)
- assert isinstance(glyphclass, GlyphClassDefinition)
- self.glyphclass = glyphclass
-
- def glyphSet(self):
- """The glyphs in this class as a tuple of :class:`GlyphName` objects."""
- return tuple(self.glyphclass.glyphSet())
-
- def asFea(self, indent=""):
- return "@" + self.glyphclass.name
-
-
-class MarkClassName(Expression):
- """A mark class name, such as ``@FRENCH_MARKS`` defined with ``markClass``.
- This must be instantiated with a :class:`MarkClass` object."""
-
- def __init__(self, markClass, location=None):
- Expression.__init__(self, location)
- assert isinstance(markClass, MarkClass)
- self.markClass = markClass
-
- def glyphSet(self):
- """The glyphs in this class as a tuple of :class:`GlyphName` objects."""
- return self.markClass.glyphSet()
-
- def asFea(self, indent=""):
- return "@" + self.markClass.name
-
-
-class AnonymousBlock(Statement):
- """An anonymous data block."""
-
- def __init__(self, tag, content, location=None):
- Statement.__init__(self, location)
- self.tag = tag #: string containing the block's "tag"
- self.content = content #: block data as string
-
- def asFea(self, indent=""):
- res = "anon {} {{\n".format(self.tag)
- res += self.content
- res += "}} {};\n\n".format(self.tag)
- return res
-
-
-class Block(Statement):
- """A block of statements: feature, lookup, etc."""
-
- def __init__(self, location=None):
- Statement.__init__(self, location)
- self.statements = [] #: Statements contained in the block
-
- def build(self, builder):
- """When handed a 'builder' object of comparable interface to
- :class:`fontTools.feaLib.builder`, walks the statements in this
- block, calling the builder callbacks."""
- for s in self.statements:
- s.build(builder)
-
- def asFea(self, indent=""):
- indent += SHIFT
- return (
- indent
- + ("\n" + indent).join([s.asFea(indent=indent) for s in self.statements])
- + "\n"
- )
-
-
-class FeatureFile(Block):
- """The top-level element of the syntax tree, containing the whole feature
- file in its ``statements`` attribute."""
-
- def __init__(self):
- Block.__init__(self, location=None)
- self.markClasses = {} # name --> ast.MarkClass
-
- def asFea(self, indent=""):
- return "\n".join(s.asFea(indent=indent) for s in self.statements)
-
-
-class FeatureBlock(Block):
- """A named feature block."""
-
- def __init__(self, name, use_extension=False, location=None):
- Block.__init__(self, location)
- self.name, self.use_extension = name, use_extension
-
- def build(self, builder):
- """Call the ``start_feature`` callback on the builder object, visit
- all the statements in this feature, and then call ``end_feature``."""
- # TODO(sascha): Handle use_extension.
- builder.start_feature(self.location, self.name)
- # language exclude_dflt statements modify builder.features_
- # limit them to this block with temporary builder.features_
- features = builder.features_
- builder.features_ = {}
- Block.build(self, builder)
- for key, value in builder.features_.items():
- features.setdefault(key, []).extend(value)
- builder.features_ = features
- builder.end_feature()
-
- def asFea(self, indent=""):
- res = indent + "feature %s " % self.name.strip()
- if self.use_extension:
- res += "useExtension "
- res += "{\n"
- res += Block.asFea(self, indent=indent)
- res += indent + "} %s;\n" % self.name.strip()
- return res
-
-
-class NestedBlock(Block):
- """A block inside another block, for example when found inside a
- ``cvParameters`` block."""
-
- def __init__(self, tag, block_name, location=None):
- Block.__init__(self, location)
- self.tag = tag
- self.block_name = block_name
-
- def build(self, builder):
- Block.build(self, builder)
- if self.block_name == "ParamUILabelNameID":
- builder.add_to_cv_num_named_params(self.tag)
-
- def asFea(self, indent=""):
- res = "{}{} {{\n".format(indent, self.block_name)
- res += Block.asFea(self, indent=indent)
- res += "{}}};\n".format(indent)
- return res
-
-
-class LookupBlock(Block):
- """A named lookup, containing ``statements``."""
-
- def __init__(self, name, use_extension=False, location=None):
- Block.__init__(self, location)
- self.name, self.use_extension = name, use_extension
-
- def build(self, builder):
- # TODO(sascha): Handle use_extension.
- builder.start_lookup_block(self.location, self.name)
- Block.build(self, builder)
- builder.end_lookup_block()
-
- def asFea(self, indent=""):
- res = "lookup {} ".format(self.name)
- if self.use_extension:
- res += "useExtension "
- res += "{\n"
- res += Block.asFea(self, indent=indent)
- res += "{}}} {};\n".format(indent, self.name)
- return res
-
-
-class TableBlock(Block):
- """A ``table ... { }`` block."""
-
- def __init__(self, name, location=None):
- Block.__init__(self, location)
- self.name = name
-
- def asFea(self, indent=""):
- res = "table {} {{\n".format(self.name.strip())
- res += super(TableBlock, self).asFea(indent=indent)
- res += "}} {};\n".format(self.name.strip())
- return res
-
-
-class GlyphClassDefinition(Statement):
- """Example: ``@UPPERCASE = [A-Z];``."""
-
- def __init__(self, name, glyphs, location=None):
- Statement.__init__(self, location)
- self.name = name #: class name as a string, without initial ``@``
- self.glyphs = glyphs #: a :class:`GlyphClass` object
-
- def glyphSet(self):
- """The glyphs in this class as a tuple of :class:`GlyphName` objects."""
- return tuple(self.glyphs.glyphSet())
-
- def asFea(self, indent=""):
- return "@" + self.name + " = " + self.glyphs.asFea() + ";"
-
-
-class GlyphClassDefStatement(Statement):
- """Example: ``GlyphClassDef @UPPERCASE, [B], [C], [D];``. The parameters
- must be either :class:`GlyphClass` or :class:`GlyphClassName` objects, or
- ``None``."""
-
- def __init__(
- self, baseGlyphs, markGlyphs, ligatureGlyphs, componentGlyphs, location=None
- ):
- Statement.__init__(self, location)
- self.baseGlyphs, self.markGlyphs = (baseGlyphs, markGlyphs)
- self.ligatureGlyphs = ligatureGlyphs
- self.componentGlyphs = componentGlyphs
-
- def build(self, builder):
- """Calls the builder's ``add_glyphClassDef`` callback."""
- base = self.baseGlyphs.glyphSet() if self.baseGlyphs else tuple()
- liga = self.ligatureGlyphs.glyphSet() if self.ligatureGlyphs else tuple()
- mark = self.markGlyphs.glyphSet() if self.markGlyphs else tuple()
- comp = self.componentGlyphs.glyphSet() if self.componentGlyphs else tuple()
- builder.add_glyphClassDef(self.location, base, liga, mark, comp)
-
- def asFea(self, indent=""):
- return "GlyphClassDef {}, {}, {}, {};".format(
- self.baseGlyphs.asFea() if self.baseGlyphs else "",
- self.ligatureGlyphs.asFea() if self.ligatureGlyphs else "",
- self.markGlyphs.asFea() if self.markGlyphs else "",
- self.componentGlyphs.asFea() if self.componentGlyphs else "",
- )
-
-
-class MarkClass(object):
- """One `or more` ``markClass`` statements for the same mark class.
-
- While glyph classes can be defined only once, the feature file format
- allows expanding mark classes with multiple definitions, each using
- different glyphs and anchors. The following are two ``MarkClassDefinitions``
- for the same ``MarkClass``::
-
- markClass [acute grave] @FRENCH_ACCENTS;
- markClass [cedilla] @FRENCH_ACCENTS;
-
- The ``MarkClass`` object is therefore just a container for a list of
- :class:`MarkClassDefinition` statements.
- """
-
- def __init__(self, name):
- self.name = name
- self.definitions = []
- self.glyphs = OrderedDict() # glyph --> ast.MarkClassDefinitions
-
- def addDefinition(self, definition):
- """Add a :class:`MarkClassDefinition` statement to this mark class."""
- assert isinstance(definition, MarkClassDefinition)
- self.definitions.append(definition)
- for glyph in definition.glyphSet():
- if glyph in self.glyphs:
- otherLoc = self.glyphs[glyph].location
- if otherLoc is None:
- end = ""
- else:
- end = f" at {otherLoc}"
- raise FeatureLibError(
- "Glyph %s already defined%s" % (glyph, end), definition.location
- )
- self.glyphs[glyph] = definition
-
- def glyphSet(self):
- """The glyphs in this class as a tuple of :class:`GlyphName` objects."""
- return tuple(self.glyphs.keys())
-
- def asFea(self, indent=""):
- res = "\n".join(d.asFea() for d in self.definitions)
- return res
-
-
-class MarkClassDefinition(Statement):
- """A single ``markClass`` statement. The ``markClass`` should be a
- :class:`MarkClass` object, the ``anchor`` an :class:`Anchor` object,
- and the ``glyphs`` parameter should be a `glyph-containing object`_ .
-
- Example:
-
- .. code:: python
-
- mc = MarkClass("FRENCH_ACCENTS")
- mc.addDefinition( MarkClassDefinition(mc, Anchor(350, 800),
- GlyphClass([ GlyphName("acute"), GlyphName("grave") ])
- ) )
- mc.addDefinition( MarkClassDefinition(mc, Anchor(350, -200),
- GlyphClass([ GlyphName("cedilla") ])
- ) )
-
- mc.asFea()
- # markClass [acute grave] @FRENCH_ACCENTS;
- # markClass [cedilla] @FRENCH_ACCENTS;
-
- """
-
- def __init__(self, markClass, anchor, glyphs, location=None):
- Statement.__init__(self, location)
- assert isinstance(markClass, MarkClass)
- assert isinstance(anchor, Anchor) and isinstance(glyphs, Expression)
- self.markClass, self.anchor, self.glyphs = markClass, anchor, glyphs
-
- def glyphSet(self):
- """The glyphs in this class as a tuple of :class:`GlyphName` objects."""
- return self.glyphs.glyphSet()
-
- def asFea(self, indent=""):
- return "markClass {} {} @{};".format(
- self.glyphs.asFea(), self.anchor.asFea(), self.markClass.name
- )
-
-
-class AlternateSubstStatement(Statement):
- """A ``sub ... from ...`` statement.
-
- ``prefix``, ``glyph``, ``suffix`` and ``replacement`` should be lists of
- `glyph-containing objects`_. ``glyph`` should be a `one element list`."""
-
- def __init__(self, prefix, glyph, suffix, replacement, location=None):
- Statement.__init__(self, location)
- self.prefix, self.glyph, self.suffix = (prefix, glyph, suffix)
- self.replacement = replacement
-
- def build(self, builder):
- """Calls the builder's ``add_alternate_subst`` callback."""
- glyph = self.glyph.glyphSet()
- assert len(glyph) == 1, glyph
- glyph = list(glyph)[0]
- prefix = [p.glyphSet() for p in self.prefix]
- suffix = [s.glyphSet() for s in self.suffix]
- replacement = self.replacement.glyphSet()
- builder.add_alternate_subst(self.location, prefix, glyph, suffix, replacement)
-
- def asFea(self, indent=""):
- res = "sub "
- if len(self.prefix) or len(self.suffix):
- if len(self.prefix):
- res += " ".join(map(asFea, self.prefix)) + " "
- res += asFea(self.glyph) + "'" # even though we really only use 1
- if len(self.suffix):
- res += " " + " ".join(map(asFea, self.suffix))
- else:
- res += asFea(self.glyph)
- res += " from "
- res += asFea(self.replacement)
- res += ";"
- return res
-
-
-class Anchor(Expression):
- """An ``Anchor`` element, used inside a ``pos`` rule.
-
- If a ``name`` is given, this will be used in preference to the coordinates.
- Other values should be integer.
- """
-
- def __init__(
- self,
- x,
- y,
- name=None,
- contourpoint=None,
- xDeviceTable=None,
- yDeviceTable=None,
- location=None,
- ):
- Expression.__init__(self, location)
- self.name = name
- self.x, self.y, self.contourpoint = x, y, contourpoint
- self.xDeviceTable, self.yDeviceTable = xDeviceTable, yDeviceTable
-
- def asFea(self, indent=""):
- if self.name is not None:
- return "".format(self.name)
- res = ""
- return res
-
-
-class AnchorDefinition(Statement):
- """A named anchor definition. (2.e.viii). ``name`` should be a string."""
-
- def __init__(self, name, x, y, contourpoint=None, location=None):
- Statement.__init__(self, location)
- self.name, self.x, self.y, self.contourpoint = name, x, y, contourpoint
-
- def asFea(self, indent=""):
- res = "anchorDef {} {}".format(self.x, self.y)
- if self.contourpoint:
- res += " contourpoint {}".format(self.contourpoint)
- res += " {};".format(self.name)
- return res
-
-
-class AttachStatement(Statement):
- """A ``GDEF`` table ``Attach`` statement."""
-
- def __init__(self, glyphs, contourPoints, location=None):
- Statement.__init__(self, location)
- self.glyphs = glyphs #: A `glyph-containing object`_
- self.contourPoints = contourPoints #: A list of integer contour points
-
- def build(self, builder):
- """Calls the builder's ``add_attach_points`` callback."""
- glyphs = self.glyphs.glyphSet()
- builder.add_attach_points(self.location, glyphs, self.contourPoints)
-
- def asFea(self, indent=""):
- return "Attach {} {};".format(
- self.glyphs.asFea(), " ".join(str(c) for c in self.contourPoints)
- )
-
-
-class ChainContextPosStatement(Statement):
- r"""A chained contextual positioning statement.
-
- ``prefix``, ``glyphs``, and ``suffix`` should be lists of
- `glyph-containing objects`_ .
-
- ``lookups`` should be a list of elements representing what lookups
- to apply at each glyph position. Each element should be a
- :class:`LookupBlock` to apply a single chaining lookup at the given
- position, a list of :class:`LookupBlock`\ s to apply multiple
- lookups, or ``None`` to apply no lookup. The length of the outer
- list should equal the length of ``glyphs``; the inner lists can be
- of variable length."""
-
- def __init__(self, prefix, glyphs, suffix, lookups, location=None):
- Statement.__init__(self, location)
- self.prefix, self.glyphs, self.suffix = prefix, glyphs, suffix
- self.lookups = list(lookups)
- for i, lookup in enumerate(lookups):
- if lookup:
- try:
- (_ for _ in lookup)
- except TypeError:
- self.lookups[i] = [lookup]
-
- def build(self, builder):
- """Calls the builder's ``add_chain_context_pos`` callback."""
- prefix = [p.glyphSet() for p in self.prefix]
- glyphs = [g.glyphSet() for g in self.glyphs]
- suffix = [s.glyphSet() for s in self.suffix]
- builder.add_chain_context_pos(
- self.location, prefix, glyphs, suffix, self.lookups
- )
-
- def asFea(self, indent=""):
- res = "pos "
- if (
- len(self.prefix)
- or len(self.suffix)
- or any([x is not None for x in self.lookups])
- ):
- if len(self.prefix):
- res += " ".join(g.asFea() for g in self.prefix) + " "
- for i, g in enumerate(self.glyphs):
- res += g.asFea() + "'"
- if self.lookups[i]:
- for lu in self.lookups[i]:
- res += " lookup " + lu.name
- if i < len(self.glyphs) - 1:
- res += " "
- if len(self.suffix):
- res += " " + " ".join(map(asFea, self.suffix))
- else:
- res += " ".join(map(asFea, self.glyph))
- res += ";"
- return res
-
-
-class ChainContextSubstStatement(Statement):
- r"""A chained contextual substitution statement.
-
- ``prefix``, ``glyphs``, and ``suffix`` should be lists of
- `glyph-containing objects`_ .
-
- ``lookups`` should be a list of elements representing what lookups
- to apply at each glyph position. Each element should be a
- :class:`LookupBlock` to apply a single chaining lookup at the given
- position, a list of :class:`LookupBlock`\ s to apply multiple
- lookups, or ``None`` to apply no lookup. The length of the outer
- list should equal the length of ``glyphs``; the inner lists can be
- of variable length."""
-
- def __init__(self, prefix, glyphs, suffix, lookups, location=None):
- Statement.__init__(self, location)
- self.prefix, self.glyphs, self.suffix = prefix, glyphs, suffix
- self.lookups = list(lookups)
- for i, lookup in enumerate(lookups):
- if lookup:
- try:
- (_ for _ in lookup)
- except TypeError:
- self.lookups[i] = [lookup]
-
- def build(self, builder):
- """Calls the builder's ``add_chain_context_subst`` callback."""
- prefix = [p.glyphSet() for p in self.prefix]
- glyphs = [g.glyphSet() for g in self.glyphs]
- suffix = [s.glyphSet() for s in self.suffix]
- builder.add_chain_context_subst(
- self.location, prefix, glyphs, suffix, self.lookups
- )
-
- def asFea(self, indent=""):
- res = "sub "
- if (
- len(self.prefix)
- or len(self.suffix)
- or any([x is not None for x in self.lookups])
- ):
- if len(self.prefix):
- res += " ".join(g.asFea() for g in self.prefix) + " "
- for i, g in enumerate(self.glyphs):
- res += g.asFea() + "'"
- if self.lookups[i]:
- for lu in self.lookups[i]:
- res += " lookup " + lu.name
- if i < len(self.glyphs) - 1:
- res += " "
- if len(self.suffix):
- res += " " + " ".join(map(asFea, self.suffix))
- else:
- res += " ".join(map(asFea, self.glyph))
- res += ";"
- return res
-
-
-class CursivePosStatement(Statement):
- """A cursive positioning statement. Entry and exit anchors can either
- be :class:`Anchor` objects or ``None``."""
-
- def __init__(self, glyphclass, entryAnchor, exitAnchor, location=None):
- Statement.__init__(self, location)
- self.glyphclass = glyphclass
- self.entryAnchor, self.exitAnchor = entryAnchor, exitAnchor
-
- def build(self, builder):
- """Calls the builder object's ``add_cursive_pos`` callback."""
- builder.add_cursive_pos(
- self.location, self.glyphclass.glyphSet(), self.entryAnchor, self.exitAnchor
- )
-
- def asFea(self, indent=""):
- entry = self.entryAnchor.asFea() if self.entryAnchor else ""
- exit = self.exitAnchor.asFea() if self.exitAnchor else ""
- return "pos cursive {} {} {};".format(self.glyphclass.asFea(), entry, exit)
-
-
-class FeatureReferenceStatement(Statement):
- """Example: ``feature salt;``"""
-
- def __init__(self, featureName, location=None):
- Statement.__init__(self, location)
- self.location, self.featureName = (location, featureName)
-
- def build(self, builder):
- """Calls the builder object's ``add_feature_reference`` callback."""
- builder.add_feature_reference(self.location, self.featureName)
-
- def asFea(self, indent=""):
- return "feature {};".format(self.featureName)
-
-
-class IgnorePosStatement(Statement):
- """An ``ignore pos`` statement, containing `one or more` contexts to ignore.
-
- ``chainContexts`` should be a list of ``(prefix, glyphs, suffix)`` tuples,
- with each of ``prefix``, ``glyphs`` and ``suffix`` being
- `glyph-containing objects`_ ."""
-
- def __init__(self, chainContexts, location=None):
- Statement.__init__(self, location)
- self.chainContexts = chainContexts
-
- def build(self, builder):
- """Calls the builder object's ``add_chain_context_pos`` callback on each
- rule context."""
- for prefix, glyphs, suffix in self.chainContexts:
- prefix = [p.glyphSet() for p in prefix]
- glyphs = [g.glyphSet() for g in glyphs]
- suffix = [s.glyphSet() for s in suffix]
- builder.add_chain_context_pos(self.location, prefix, glyphs, suffix, [])
-
- def asFea(self, indent=""):
- contexts = []
- for prefix, glyphs, suffix in self.chainContexts:
- res = ""
- if len(prefix) or len(suffix):
- if len(prefix):
- res += " ".join(map(asFea, prefix)) + " "
- res += " ".join(g.asFea() + "'" for g in glyphs)
- if len(suffix):
- res += " " + " ".join(map(asFea, suffix))
- else:
- res += " ".join(map(asFea, glyphs))
- contexts.append(res)
- return "ignore pos " + ", ".join(contexts) + ";"
-
-
-class IgnoreSubstStatement(Statement):
- """An ``ignore sub`` statement, containing `one or more` contexts to ignore.
-
- ``chainContexts`` should be a list of ``(prefix, glyphs, suffix)`` tuples,
- with each of ``prefix``, ``glyphs`` and ``suffix`` being
- `glyph-containing objects`_ ."""
-
- def __init__(self, chainContexts, location=None):
- Statement.__init__(self, location)
- self.chainContexts = chainContexts
-
- def build(self, builder):
- """Calls the builder object's ``add_chain_context_subst`` callback on
- each rule context."""
- for prefix, glyphs, suffix in self.chainContexts:
- prefix = [p.glyphSet() for p in prefix]
- glyphs = [g.glyphSet() for g in glyphs]
- suffix = [s.glyphSet() for s in suffix]
- builder.add_chain_context_subst(self.location, prefix, glyphs, suffix, [])
-
- def asFea(self, indent=""):
- contexts = []
- for prefix, glyphs, suffix in self.chainContexts:
- res = ""
- if len(prefix):
- res += " ".join(map(asFea, prefix)) + " "
- res += " ".join(g.asFea() + "'" for g in glyphs)
- if len(suffix):
- res += " " + " ".join(map(asFea, suffix))
- contexts.append(res)
- return "ignore sub " + ", ".join(contexts) + ";"
-
-
-class IncludeStatement(Statement):
- """An ``include()`` statement."""
-
- def __init__(self, filename, location=None):
- super(IncludeStatement, self).__init__(location)
- self.filename = filename #: String containing name of file to include
-
- def build(self):
- # TODO: consider lazy-loading the including parser/lexer?
- raise FeatureLibError(
- "Building an include statement is not implemented yet. "
- "Instead, use Parser(..., followIncludes=True) for building.",
- self.location,
- )
-
- def asFea(self, indent=""):
- return indent + "include(%s);" % self.filename
-
-
-class LanguageStatement(Statement):
- """A ``language`` statement within a feature."""
-
- def __init__(self, language, include_default=True, required=False, location=None):
- Statement.__init__(self, location)
- assert len(language) == 4
- self.language = language #: A four-character language tag
- self.include_default = include_default #: If false, "exclude_dflt"
- self.required = required
-
- def build(self, builder):
- """Call the builder object's ``set_language`` callback."""
- builder.set_language(
- location=self.location,
- language=self.language,
- include_default=self.include_default,
- required=self.required,
- )
-
- def asFea(self, indent=""):
- res = "language {}".format(self.language.strip())
- if not self.include_default:
- res += " exclude_dflt"
- if self.required:
- res += " required"
- res += ";"
- return res
-
-
-class LanguageSystemStatement(Statement):
- """A top-level ``languagesystem`` statement."""
-
- def __init__(self, script, language, location=None):
- Statement.__init__(self, location)
- self.script, self.language = (script, language)
-
- def build(self, builder):
- """Calls the builder object's ``add_language_system`` callback."""
- builder.add_language_system(self.location, self.script, self.language)
-
- def asFea(self, indent=""):
- return "languagesystem {} {};".format(self.script, self.language.strip())
-
-
-class FontRevisionStatement(Statement):
- """A ``head`` table ``FontRevision`` statement. ``revision`` should be a
- number, and will be formatted to three significant decimal places."""
-
- def __init__(self, revision, location=None):
- Statement.__init__(self, location)
- self.revision = revision
-
- def build(self, builder):
- builder.set_font_revision(self.location, self.revision)
-
- def asFea(self, indent=""):
- return "FontRevision {:.3f};".format(self.revision)
-
-
-class LigatureCaretByIndexStatement(Statement):
- """A ``GDEF`` table ``LigatureCaretByIndex`` statement. ``glyphs`` should be
- a `glyph-containing object`_, and ``carets`` should be a list of integers."""
-
- def __init__(self, glyphs, carets, location=None):
- Statement.__init__(self, location)
- self.glyphs, self.carets = (glyphs, carets)
-
- def build(self, builder):
- """Calls the builder object's ``add_ligatureCaretByIndex_`` callback."""
- glyphs = self.glyphs.glyphSet()
- builder.add_ligatureCaretByIndex_(self.location, glyphs, set(self.carets))
-
- def asFea(self, indent=""):
- return "LigatureCaretByIndex {} {};".format(
- self.glyphs.asFea(), " ".join(str(x) for x in self.carets)
- )
-
-
-class LigatureCaretByPosStatement(Statement):
- """A ``GDEF`` table ``LigatureCaretByPos`` statement. ``glyphs`` should be
- a `glyph-containing object`_, and ``carets`` should be a list of integers."""
-
- def __init__(self, glyphs, carets, location=None):
- Statement.__init__(self, location)
- self.glyphs, self.carets = (glyphs, carets)
-
- def build(self, builder):
- """Calls the builder object's ``add_ligatureCaretByPos_`` callback."""
- glyphs = self.glyphs.glyphSet()
- builder.add_ligatureCaretByPos_(self.location, glyphs, set(self.carets))
-
- def asFea(self, indent=""):
- return "LigatureCaretByPos {} {};".format(
- self.glyphs.asFea(), " ".join(str(x) for x in self.carets)
- )
-
-
-class LigatureSubstStatement(Statement):
- """A chained contextual substitution statement.
-
- ``prefix``, ``glyphs``, and ``suffix`` should be lists of
- `glyph-containing objects`_; ``replacement`` should be a single
- `glyph-containing object`_.
-
- If ``forceChain`` is True, this is expressed as a chaining rule
- (e.g. ``sub f' i' by f_i``) even when no context is given."""
-
- def __init__(self, prefix, glyphs, suffix, replacement, forceChain, location=None):
- Statement.__init__(self, location)
- self.prefix, self.glyphs, self.suffix = (prefix, glyphs, suffix)
- self.replacement, self.forceChain = replacement, forceChain
-
- def build(self, builder):
- prefix = [p.glyphSet() for p in self.prefix]
- glyphs = [g.glyphSet() for g in self.glyphs]
- suffix = [s.glyphSet() for s in self.suffix]
- builder.add_ligature_subst(
- self.location, prefix, glyphs, suffix, self.replacement, self.forceChain
- )
-
- def asFea(self, indent=""):
- res = "sub "
- if len(self.prefix) or len(self.suffix) or self.forceChain:
- if len(self.prefix):
- res += " ".join(g.asFea() for g in self.prefix) + " "
- res += " ".join(g.asFea() + "'" for g in self.glyphs)
- if len(self.suffix):
- res += " " + " ".join(g.asFea() for g in self.suffix)
- else:
- res += " ".join(g.asFea() for g in self.glyphs)
- res += " by "
- res += asFea(self.replacement)
- res += ";"
- return res
-
-
-class LookupFlagStatement(Statement):
- """A ``lookupflag`` statement. The ``value`` should be an integer value
- representing the flags in use, but not including the ``markAttachment``
- class and ``markFilteringSet`` values, which must be specified as
- glyph-containing objects."""
-
- def __init__(
- self, value=0, markAttachment=None, markFilteringSet=None, location=None
- ):
- Statement.__init__(self, location)
- self.value = value
- self.markAttachment = markAttachment
- self.markFilteringSet = markFilteringSet
-
- def build(self, builder):
- """Calls the builder object's ``set_lookup_flag`` callback."""
- markAttach = None
- if self.markAttachment is not None:
- markAttach = self.markAttachment.glyphSet()
- markFilter = None
- if self.markFilteringSet is not None:
- markFilter = self.markFilteringSet.glyphSet()
- builder.set_lookup_flag(self.location, self.value, markAttach, markFilter)
-
- def asFea(self, indent=""):
- res = []
- flags = ["RightToLeft", "IgnoreBaseGlyphs", "IgnoreLigatures", "IgnoreMarks"]
- curr = 1
- for i in range(len(flags)):
- if self.value & curr != 0:
- res.append(flags[i])
- curr = curr << 1
- if self.markAttachment is not None:
- res.append("MarkAttachmentType {}".format(self.markAttachment.asFea()))
- if self.markFilteringSet is not None:
- res.append("UseMarkFilteringSet {}".format(self.markFilteringSet.asFea()))
- if not res:
- res = ["0"]
- return "lookupflag {};".format(" ".join(res))
-
-
-class LookupReferenceStatement(Statement):
- """Represents a ``lookup ...;`` statement to include a lookup in a feature.
-
- The ``lookup`` should be a :class:`LookupBlock` object."""
-
- def __init__(self, lookup, location=None):
- Statement.__init__(self, location)
- self.location, self.lookup = (location, lookup)
-
- def build(self, builder):
- """Calls the builder object's ``add_lookup_call`` callback."""
- builder.add_lookup_call(self.lookup.name)
-
- def asFea(self, indent=""):
- return "lookup {};".format(self.lookup.name)
-
-
-class MarkBasePosStatement(Statement):
- """A mark-to-base positioning rule. The ``base`` should be a
- `glyph-containing object`_. The ``marks`` should be a list of
- (:class:`Anchor`, :class:`MarkClass`) tuples."""
-
- def __init__(self, base, marks, location=None):
- Statement.__init__(self, location)
- self.base, self.marks = base, marks
-
- def build(self, builder):
- """Calls the builder object's ``add_mark_base_pos`` callback."""
- builder.add_mark_base_pos(self.location, self.base.glyphSet(), self.marks)
-
- def asFea(self, indent=""):
- res = "pos base {}".format(self.base.asFea())
- for a, m in self.marks:
- res += "\n" + indent + SHIFT + "{} mark @{}".format(a.asFea(), m.name)
- res += ";"
- return res
-
-
-class MarkLigPosStatement(Statement):
- """A mark-to-ligature positioning rule. The ``ligatures`` must be a
- `glyph-containing object`_. The ``marks`` should be a list of lists: each
- element in the top-level list represents a component glyph, and is made
- up of a list of (:class:`Anchor`, :class:`MarkClass`) tuples representing
- mark attachment points for that position.
-
- Example::
-
- m1 = MarkClass("TOP_MARKS")
- m2 = MarkClass("BOTTOM_MARKS")
- # ... add definitions to mark classes...
-
- glyph = GlyphName("lam_meem_jeem")
- marks = [
- [ (Anchor(625,1800), m1) ], # Attachments on 1st component (lam)
- [ (Anchor(376,-378), m2) ], # Attachments on 2nd component (meem)
- [ ] # No attachments on the jeem
- ]
- mlp = MarkLigPosStatement(glyph, marks)
-
- mlp.asFea()
- # pos ligature lam_meem_jeem mark @TOP_MARKS
- # ligComponent mark @BOTTOM_MARKS;
-
- """
-
- def __init__(self, ligatures, marks, location=None):
- Statement.__init__(self, location)
- self.ligatures, self.marks = ligatures, marks
-
- def build(self, builder):
- """Calls the builder object's ``add_mark_lig_pos`` callback."""
- builder.add_mark_lig_pos(self.location, self.ligatures.glyphSet(), self.marks)
-
- def asFea(self, indent=""):
- res = "pos ligature {}".format(self.ligatures.asFea())
- ligs = []
- for l in self.marks:
- temp = ""
- if l is None or not len(l):
- temp = "\n" + indent + SHIFT * 2 + ""
- else:
- for a, m in l:
- temp += (
- "\n"
- + indent
- + SHIFT * 2
- + "{} mark @{}".format(a.asFea(), m.name)
- )
- ligs.append(temp)
- res += ("\n" + indent + SHIFT + "ligComponent").join(ligs)
- res += ";"
- return res
-
-
-class MarkMarkPosStatement(Statement):
- """A mark-to-mark positioning rule. The ``baseMarks`` must be a
- `glyph-containing object`_. The ``marks`` should be a list of
- (:class:`Anchor`, :class:`MarkClass`) tuples."""
-
- def __init__(self, baseMarks, marks, location=None):
- Statement.__init__(self, location)
- self.baseMarks, self.marks = baseMarks, marks
-
- def build(self, builder):
- """Calls the builder object's ``add_mark_mark_pos`` callback."""
- builder.add_mark_mark_pos(self.location, self.baseMarks.glyphSet(), self.marks)
-
- def asFea(self, indent=""):
- res = "pos mark {}".format(self.baseMarks.asFea())
- for a, m in self.marks:
- res += "\n" + indent + SHIFT + "{} mark @{}".format(a.asFea(), m.name)
- res += ";"
- return res
-
-
-class MultipleSubstStatement(Statement):
- """A multiple substitution statement.
-
- Args:
- prefix: a list of `glyph-containing objects`_.
- glyph: a single glyph-containing object.
- suffix: a list of glyph-containing objects.
- replacement: a list of glyph-containing objects.
- forceChain: If true, the statement is expressed as a chaining rule
- (e.g. ``sub f' i' by f_i``) even when no context is given.
- """
-
- def __init__(
- self, prefix, glyph, suffix, replacement, forceChain=False, location=None
- ):
- Statement.__init__(self, location)
- self.prefix, self.glyph, self.suffix = prefix, glyph, suffix
- self.replacement = replacement
- self.forceChain = forceChain
-
- def build(self, builder):
- """Calls the builder object's ``add_multiple_subst`` callback."""
- prefix = [p.glyphSet() for p in self.prefix]
- suffix = [s.glyphSet() for s in self.suffix]
- if hasattr(self.glyph, "glyphSet"):
- originals = self.glyph.glyphSet()
- else:
- originals = [self.glyph]
- count = len(originals)
- replaces = []
- for r in self.replacement:
- if hasattr(r, "glyphSet"):
- replace = r.glyphSet()
- else:
- replace = [r]
- if len(replace) == 1 and len(replace) != count:
- replace = replace * count
- replaces.append(replace)
- replaces = list(zip(*replaces))
-
- seen_originals = set()
- for i, original in enumerate(originals):
- if original not in seen_originals:
- seen_originals.add(original)
- builder.add_multiple_subst(
- self.location,
- prefix,
- original,
- suffix,
- replaces and replaces[i] or (),
- self.forceChain,
- )
-
- def asFea(self, indent=""):
- res = "sub "
- if len(self.prefix) or len(self.suffix) or self.forceChain:
- if len(self.prefix):
- res += " ".join(map(asFea, self.prefix)) + " "
- res += asFea(self.glyph) + "'"
- if len(self.suffix):
- res += " " + " ".join(map(asFea, self.suffix))
- else:
- res += asFea(self.glyph)
- replacement = self.replacement or [NullGlyph()]
- res += " by "
- res += " ".join(map(asFea, replacement))
- res += ";"
- return res
-
-
-class PairPosStatement(Statement):
- """A pair positioning statement.
-
- ``glyphs1`` and ``glyphs2`` should be `glyph-containing objects`_.
- ``valuerecord1`` should be a :class:`ValueRecord` object;
- ``valuerecord2`` should be either a :class:`ValueRecord` object or ``None``.
- If ``enumerated`` is true, then this is expressed as an
- `enumerated pair `_.
- """
-
- def __init__(
- self,
- glyphs1,
- valuerecord1,
- glyphs2,
- valuerecord2,
- enumerated=False,
- location=None,
- ):
- Statement.__init__(self, location)
- self.enumerated = enumerated
- self.glyphs1, self.valuerecord1 = glyphs1, valuerecord1
- self.glyphs2, self.valuerecord2 = glyphs2, valuerecord2
-
- def build(self, builder):
- """Calls a callback on the builder object:
-
- * If the rule is enumerated, calls ``add_specific_pair_pos`` on each
- combination of first and second glyphs.
- * If the glyphs are both single :class:`GlyphName` objects, calls
- ``add_specific_pair_pos``.
- * Else, calls ``add_class_pair_pos``.
- """
- if self.enumerated:
- g = [self.glyphs1.glyphSet(), self.glyphs2.glyphSet()]
- seen_pair = False
- for glyph1, glyph2 in itertools.product(*g):
- seen_pair = True
- builder.add_specific_pair_pos(
- self.location, glyph1, self.valuerecord1, glyph2, self.valuerecord2
- )
- if not seen_pair:
- raise FeatureLibError(
- "Empty glyph class in positioning rule", self.location
- )
- return
-
- is_specific = isinstance(self.glyphs1, GlyphName) and isinstance(
- self.glyphs2, GlyphName
- )
- if is_specific:
- builder.add_specific_pair_pos(
- self.location,
- self.glyphs1.glyph,
- self.valuerecord1,
- self.glyphs2.glyph,
- self.valuerecord2,
- )
- else:
- builder.add_class_pair_pos(
- self.location,
- self.glyphs1.glyphSet(),
- self.valuerecord1,
- self.glyphs2.glyphSet(),
- self.valuerecord2,
- )
-
- def asFea(self, indent=""):
- res = "enum " if self.enumerated else ""
- if self.valuerecord2:
- res += "pos {} {} {} {};".format(
- self.glyphs1.asFea(),
- self.valuerecord1.asFea(),
- self.glyphs2.asFea(),
- self.valuerecord2.asFea(),
- )
- else:
- res += "pos {} {} {};".format(
- self.glyphs1.asFea(), self.glyphs2.asFea(), self.valuerecord1.asFea()
- )
- return res
-
-
-class ReverseChainSingleSubstStatement(Statement):
- """A reverse chaining substitution statement. You don't see those every day.
-
- Note the unusual argument order: ``suffix`` comes `before` ``glyphs``.
- ``old_prefix``, ``old_suffix``, ``glyphs`` and ``replacements`` should be
- lists of `glyph-containing objects`_. ``glyphs`` and ``replacements`` should
- be one-item lists.
- """
-
- def __init__(self, old_prefix, old_suffix, glyphs, replacements, location=None):
- Statement.__init__(self, location)
- self.old_prefix, self.old_suffix = old_prefix, old_suffix
- self.glyphs = glyphs
- self.replacements = replacements
-
- def build(self, builder):
- prefix = [p.glyphSet() for p in self.old_prefix]
- suffix = [s.glyphSet() for s in self.old_suffix]
- originals = self.glyphs[0].glyphSet()
- replaces = self.replacements[0].glyphSet()
- if len(replaces) == 1:
- replaces = replaces * len(originals)
- builder.add_reverse_chain_single_subst(
- self.location, prefix, suffix, dict(zip(originals, replaces))
- )
-
- def asFea(self, indent=""):
- res = "rsub "
- if len(self.old_prefix) or len(self.old_suffix):
- if len(self.old_prefix):
- res += " ".join(asFea(g) for g in self.old_prefix) + " "
- res += " ".join(asFea(g) + "'" for g in self.glyphs)
- if len(self.old_suffix):
- res += " " + " ".join(asFea(g) for g in self.old_suffix)
- else:
- res += " ".join(map(asFea, self.glyphs))
- res += " by {};".format(" ".join(asFea(g) for g in self.replacements))
- return res
-
-
-class SingleSubstStatement(Statement):
- """A single substitution statement.
-
- Note the unusual argument order: ``prefix`` and suffix come `after`
- the replacement ``glyphs``. ``prefix``, ``suffix``, ``glyphs`` and
- ``replace`` should be lists of `glyph-containing objects`_. ``glyphs`` and
- ``replace`` should be one-item lists.
- """
-
- def __init__(self, glyphs, replace, prefix, suffix, forceChain, location=None):
- Statement.__init__(self, location)
- self.prefix, self.suffix = prefix, suffix
- self.forceChain = forceChain
- self.glyphs = glyphs
- self.replacements = replace
-
- def build(self, builder):
- """Calls the builder object's ``add_single_subst`` callback."""
- prefix = [p.glyphSet() for p in self.prefix]
- suffix = [s.glyphSet() for s in self.suffix]
- originals = self.glyphs[0].glyphSet()
- replaces = self.replacements[0].glyphSet()
- if len(replaces) == 1:
- replaces = replaces * len(originals)
- builder.add_single_subst(
- self.location,
- prefix,
- suffix,
- OrderedDict(zip(originals, replaces)),
- self.forceChain,
- )
-
- def asFea(self, indent=""):
- res = "sub "
- if len(self.prefix) or len(self.suffix) or self.forceChain:
- if len(self.prefix):
- res += " ".join(asFea(g) for g in self.prefix) + " "
- res += " ".join(asFea(g) + "'" for g in self.glyphs)
- if len(self.suffix):
- res += " " + " ".join(asFea(g) for g in self.suffix)
- else:
- res += " ".join(asFea(g) for g in self.glyphs)
- res += " by {};".format(" ".join(asFea(g) for g in self.replacements))
- return res
-
-
-class ScriptStatement(Statement):
- """A ``script`` statement."""
-
- def __init__(self, script, location=None):
- Statement.__init__(self, location)
- self.script = script #: the script code
-
- def build(self, builder):
- """Calls the builder's ``set_script`` callback."""
- builder.set_script(self.location, self.script)
-
- def asFea(self, indent=""):
- return "script {};".format(self.script.strip())
-
-
-class SinglePosStatement(Statement):
- """A single position statement. ``prefix`` and ``suffix`` should be
- lists of `glyph-containing objects`_.
-
- ``pos`` should be a one-element list containing a (`glyph-containing object`_,
- :class:`ValueRecord`) tuple."""
-
- def __init__(self, pos, prefix, suffix, forceChain, location=None):
- Statement.__init__(self, location)
- self.pos, self.prefix, self.suffix = pos, prefix, suffix
- self.forceChain = forceChain
-
- def build(self, builder):
- """Calls the builder object's ``add_single_pos`` callback."""
- prefix = [p.glyphSet() for p in self.prefix]
- suffix = [s.glyphSet() for s in self.suffix]
- pos = [(g.glyphSet(), value) for g, value in self.pos]
- builder.add_single_pos(self.location, prefix, suffix, pos, self.forceChain)
-
- def asFea(self, indent=""):
- res = "pos "
- if len(self.prefix) or len(self.suffix) or self.forceChain:
- if len(self.prefix):
- res += " ".join(map(asFea, self.prefix)) + " "
- res += " ".join(
- [
- asFea(x[0]) + "'" + ((" " + x[1].asFea()) if x[1] else "")
- for x in self.pos
- ]
- )
- if len(self.suffix):
- res += " " + " ".join(map(asFea, self.suffix))
- else:
- res += " ".join(
- [asFea(x[0]) + " " + (x[1].asFea() if x[1] else "") for x in self.pos]
- )
- res += ";"
- return res
-
-
-class SubtableStatement(Statement):
- """Represents a subtable break."""
-
- def __init__(self, location=None):
- Statement.__init__(self, location)
-
- def build(self, builder):
- """Calls the builder objects's ``add_subtable_break`` callback."""
- builder.add_subtable_break(self.location)
-
- def asFea(self, indent=""):
- return "subtable;"
-
-
-class ValueRecord(Expression):
- """Represents a value record."""
-
- def __init__(
- self,
- xPlacement=None,
- yPlacement=None,
- xAdvance=None,
- yAdvance=None,
- xPlaDevice=None,
- yPlaDevice=None,
- xAdvDevice=None,
- yAdvDevice=None,
- vertical=False,
- location=None,
- ):
- Expression.__init__(self, location)
- self.xPlacement, self.yPlacement = (xPlacement, yPlacement)
- self.xAdvance, self.yAdvance = (xAdvance, yAdvance)
- self.xPlaDevice, self.yPlaDevice = (xPlaDevice, yPlaDevice)
- self.xAdvDevice, self.yAdvDevice = (xAdvDevice, yAdvDevice)
- self.vertical = vertical
-
- def __eq__(self, other):
- return (
- self.xPlacement == other.xPlacement
- and self.yPlacement == other.yPlacement
- and self.xAdvance == other.xAdvance
- and self.yAdvance == other.yAdvance
- and self.xPlaDevice == other.xPlaDevice
- and self.xAdvDevice == other.xAdvDevice
- )
-
- def __ne__(self, other):
- return not self.__eq__(other)
-
- def __hash__(self):
- return (
- hash(self.xPlacement)
- ^ hash(self.yPlacement)
- ^ hash(self.xAdvance)
- ^ hash(self.yAdvance)
- ^ hash(self.xPlaDevice)
- ^ hash(self.yPlaDevice)
- ^ hash(self.xAdvDevice)
- ^ hash(self.yAdvDevice)
- )
-
- def asFea(self, indent=""):
- if not self:
- return ""
-
- x, y = self.xPlacement, self.yPlacement
- xAdvance, yAdvance = self.xAdvance, self.yAdvance
- xPlaDevice, yPlaDevice = self.xPlaDevice, self.yPlaDevice
- xAdvDevice, yAdvDevice = self.xAdvDevice, self.yAdvDevice
- vertical = self.vertical
-
- # Try format A, if possible.
- if x is None and y is None:
- if xAdvance is None and vertical:
- return str(yAdvance)
- elif yAdvance is None and not vertical:
- return str(xAdvance)
-
- # Make any remaining None value 0 to avoid generating invalid records.
- x = x or 0
- y = y or 0
- xAdvance = xAdvance or 0
- yAdvance = yAdvance or 0
-
- # Try format B, if possible.
- if (
- xPlaDevice is None
- and yPlaDevice is None
- and xAdvDevice is None
- and yAdvDevice is None
- ):
- return "<%s %s %s %s>" % (x, y, xAdvance, yAdvance)
-
- # Last resort is format C.
- return "<%s %s %s %s %s %s %s %s>" % (
- x,
- y,
- xAdvance,
- yAdvance,
- deviceToString(xPlaDevice),
- deviceToString(yPlaDevice),
- deviceToString(xAdvDevice),
- deviceToString(yAdvDevice),
- )
-
- def __bool__(self):
- return any(
- getattr(self, v) is not None
- for v in [
- "xPlacement",
- "yPlacement",
- "xAdvance",
- "yAdvance",
- "xPlaDevice",
- "yPlaDevice",
- "xAdvDevice",
- "yAdvDevice",
- ]
- )
-
- __nonzero__ = __bool__
-
-
-class ValueRecordDefinition(Statement):
- """Represents a named value record definition."""
-
- def __init__(self, name, value, location=None):
- Statement.__init__(self, location)
- self.name = name #: Value record name as string
- self.value = value #: :class:`ValueRecord` object
-
- def asFea(self, indent=""):
- return "valueRecordDef {} {};".format(self.value.asFea(), self.name)
-
-
-def simplify_name_attributes(pid, eid, lid):
- if pid == 3 and eid == 1 and lid == 1033:
- return ""
- elif pid == 1 and eid == 0 and lid == 0:
- return "1"
- else:
- return "{} {} {}".format(pid, eid, lid)
-
-
-class NameRecord(Statement):
- """Represents a name record. (`Section 9.e. `_)"""
-
- def __init__(self, nameID, platformID, platEncID, langID, string, location=None):
- Statement.__init__(self, location)
- self.nameID = nameID #: Name ID as integer (e.g. 9 for designer's name)
- self.platformID = platformID #: Platform ID as integer
- self.platEncID = platEncID #: Platform encoding ID as integer
- self.langID = langID #: Language ID as integer
- self.string = string #: Name record value
-
- def build(self, builder):
- """Calls the builder object's ``add_name_record`` callback."""
- builder.add_name_record(
- self.location,
- self.nameID,
- self.platformID,
- self.platEncID,
- self.langID,
- self.string,
- )
-
- def asFea(self, indent=""):
- def escape(c, escape_pattern):
- # Also escape U+0022 QUOTATION MARK and U+005C REVERSE SOLIDUS
- if c >= 0x20 and c <= 0x7E and c not in (0x22, 0x5C):
- return chr(c)
- else:
- return escape_pattern % c
-
- encoding = getEncoding(self.platformID, self.platEncID, self.langID)
- if encoding is None:
- raise FeatureLibError("Unsupported encoding", self.location)
- s = tobytes(self.string, encoding=encoding)
- if encoding == "utf_16_be":
- escaped_string = "".join(
- [
- escape(byteord(s[i]) * 256 + byteord(s[i + 1]), r"\%04x")
- for i in range(0, len(s), 2)
- ]
- )
- else:
- escaped_string = "".join([escape(byteord(b), r"\%02x") for b in s])
- plat = simplify_name_attributes(self.platformID, self.platEncID, self.langID)
- if plat != "":
- plat += " "
- return 'nameid {} {}"{}";'.format(self.nameID, plat, escaped_string)
-
-
-class FeatureNameStatement(NameRecord):
- """Represents a ``sizemenuname`` or ``name`` statement."""
-
- def build(self, builder):
- """Calls the builder object's ``add_featureName`` callback."""
- NameRecord.build(self, builder)
- builder.add_featureName(self.nameID)
-
- def asFea(self, indent=""):
- if self.nameID == "size":
- tag = "sizemenuname"
- else:
- tag = "name"
- plat = simplify_name_attributes(self.platformID, self.platEncID, self.langID)
- if plat != "":
- plat += " "
- return '{} {}"{}";'.format(tag, plat, self.string)
-
-
-class STATNameStatement(NameRecord):
- """Represents a STAT table ``name`` statement."""
-
- def asFea(self, indent=""):
- plat = simplify_name_attributes(self.platformID, self.platEncID, self.langID)
- if plat != "":
- plat += " "
- return 'name {}"{}";'.format(plat, self.string)
-
-
-class SizeParameters(Statement):
- """A ``parameters`` statement."""
-
- def __init__(self, DesignSize, SubfamilyID, RangeStart, RangeEnd, location=None):
- Statement.__init__(self, location)
- self.DesignSize = DesignSize
- self.SubfamilyID = SubfamilyID
- self.RangeStart = RangeStart
- self.RangeEnd = RangeEnd
-
- def build(self, builder):
- """Calls the builder object's ``set_size_parameters`` callback."""
- builder.set_size_parameters(
- self.location,
- self.DesignSize,
- self.SubfamilyID,
- self.RangeStart,
- self.RangeEnd,
- )
-
- def asFea(self, indent=""):
- res = "parameters {:.1f} {}".format(self.DesignSize, self.SubfamilyID)
- if self.RangeStart != 0 or self.RangeEnd != 0:
- res += " {} {}".format(int(self.RangeStart * 10), int(self.RangeEnd * 10))
- return res + ";"
-
-
-class CVParametersNameStatement(NameRecord):
- """Represent a name statement inside a ``cvParameters`` block."""
-
- def __init__(
- self, nameID, platformID, platEncID, langID, string, block_name, location=None
- ):
- NameRecord.__init__(
- self, nameID, platformID, platEncID, langID, string, location=location
- )
- self.block_name = block_name
-
- def build(self, builder):
- """Calls the builder object's ``add_cv_parameter`` callback."""
- item = ""
- if self.block_name == "ParamUILabelNameID":
- item = "_{}".format(builder.cv_num_named_params_.get(self.nameID, 0))
- builder.add_cv_parameter(self.nameID)
- self.nameID = (self.nameID, self.block_name + item)
- NameRecord.build(self, builder)
-
- def asFea(self, indent=""):
- plat = simplify_name_attributes(self.platformID, self.platEncID, self.langID)
- if plat != "":
- plat += " "
- return 'name {}"{}";'.format(plat, self.string)
-
-
-class CharacterStatement(Statement):
- """
- Statement used in cvParameters blocks of Character Variant features (cvXX).
- The Unicode value may be written with either decimal or hexadecimal
- notation. The value must be preceded by '0x' if it is a hexadecimal value.
- The largest Unicode value allowed is 0xFFFFFF.
- """
-
- def __init__(self, character, tag, location=None):
- Statement.__init__(self, location)
- self.character = character
- self.tag = tag
-
- def build(self, builder):
- """Calls the builder object's ``add_cv_character`` callback."""
- builder.add_cv_character(self.character, self.tag)
-
- def asFea(self, indent=""):
- return "Character {:#x};".format(self.character)
-
-
-class BaseAxis(Statement):
- """An axis definition, being either a ``VertAxis.BaseTagList/BaseScriptList``
- pair or a ``HorizAxis.BaseTagList/BaseScriptList`` pair."""
-
- def __init__(self, bases, scripts, vertical, location=None):
- Statement.__init__(self, location)
- self.bases = bases #: A list of baseline tag names as strings
- self.scripts = scripts #: A list of script record tuplets (script tag, default baseline tag, base coordinate)
- self.vertical = vertical #: Boolean; VertAxis if True, HorizAxis if False
-
- def build(self, builder):
- """Calls the builder object's ``set_base_axis`` callback."""
- builder.set_base_axis(self.bases, self.scripts, self.vertical)
-
- def asFea(self, indent=""):
- direction = "Vert" if self.vertical else "Horiz"
- scripts = [
- "{} {} {}".format(a[0], a[1], " ".join(map(str, a[2])))
- for a in self.scripts
- ]
- return "{}Axis.BaseTagList {};\n{}{}Axis.BaseScriptList {};".format(
- direction, " ".join(self.bases), indent, direction, ", ".join(scripts)
- )
-
-
-class OS2Field(Statement):
- """An entry in the ``OS/2`` table. Most ``values`` should be numbers or
- strings, apart from when the key is ``UnicodeRange``, ``CodePageRange``
- or ``Panose``, in which case it should be an array of integers."""
-
- def __init__(self, key, value, location=None):
- Statement.__init__(self, location)
- self.key = key
- self.value = value
-
- def build(self, builder):
- """Calls the builder object's ``add_os2_field`` callback."""
- builder.add_os2_field(self.key, self.value)
-
- def asFea(self, indent=""):
- def intarr2str(x):
- return " ".join(map(str, x))
-
- numbers = (
- "FSType",
- "TypoAscender",
- "TypoDescender",
- "TypoLineGap",
- "winAscent",
- "winDescent",
- "XHeight",
- "CapHeight",
- "WeightClass",
- "WidthClass",
- "LowerOpSize",
- "UpperOpSize",
- )
- ranges = ("UnicodeRange", "CodePageRange")
- keywords = dict([(x.lower(), [x, str]) for x in numbers])
- keywords.update([(x.lower(), [x, intarr2str]) for x in ranges])
- keywords["panose"] = ["Panose", intarr2str]
- keywords["vendor"] = ["Vendor", lambda y: '"{}"'.format(y)]
- if self.key in keywords:
- return "{} {};".format(
- keywords[self.key][0], keywords[self.key][1](self.value)
- )
- return "" # should raise exception
-
-
-class HheaField(Statement):
- """An entry in the ``hhea`` table."""
-
- def __init__(self, key, value, location=None):
- Statement.__init__(self, location)
- self.key = key
- self.value = value
-
- def build(self, builder):
- """Calls the builder object's ``add_hhea_field`` callback."""
- builder.add_hhea_field(self.key, self.value)
-
- def asFea(self, indent=""):
- fields = ("CaretOffset", "Ascender", "Descender", "LineGap")
- keywords = dict([(x.lower(), x) for x in fields])
- return "{} {};".format(keywords[self.key], self.value)
-
-
-class VheaField(Statement):
- """An entry in the ``vhea`` table."""
-
- def __init__(self, key, value, location=None):
- Statement.__init__(self, location)
- self.key = key
- self.value = value
-
- def build(self, builder):
- """Calls the builder object's ``add_vhea_field`` callback."""
- builder.add_vhea_field(self.key, self.value)
-
- def asFea(self, indent=""):
- fields = ("VertTypoAscender", "VertTypoDescender", "VertTypoLineGap")
- keywords = dict([(x.lower(), x) for x in fields])
- return "{} {};".format(keywords[self.key], self.value)
-
-
-class STATDesignAxisStatement(Statement):
- """A STAT table Design Axis
-
- Args:
- tag (str): a 4 letter axis tag
- axisOrder (int): an int
- names (list): a list of :class:`STATNameStatement` objects
- """
-
- def __init__(self, tag, axisOrder, names, location=None):
- Statement.__init__(self, location)
- self.tag = tag
- self.axisOrder = axisOrder
- self.names = names
- self.location = location
-
- def build(self, builder):
- builder.addDesignAxis(self, self.location)
-
- def asFea(self, indent=""):
- indent += SHIFT
- res = f"DesignAxis {self.tag} {self.axisOrder} {{ \n"
- res += ("\n" + indent).join([s.asFea(indent=indent) for s in self.names]) + "\n"
- res += "};"
- return res
-
-
-class ElidedFallbackName(Statement):
- """STAT table ElidedFallbackName
-
- Args:
- names: a list of :class:`STATNameStatement` objects
- """
-
- def __init__(self, names, location=None):
- Statement.__init__(self, location)
- self.names = names
- self.location = location
-
- def build(self, builder):
- builder.setElidedFallbackName(self.names, self.location)
-
- def asFea(self, indent=""):
- indent += SHIFT
- res = "ElidedFallbackName { \n"
- res += ("\n" + indent).join([s.asFea(indent=indent) for s in self.names]) + "\n"
- res += "};"
- return res
-
-
-class ElidedFallbackNameID(Statement):
- """STAT table ElidedFallbackNameID
-
- Args:
- value: an int pointing to an existing name table name ID
- """
-
- def __init__(self, value, location=None):
- Statement.__init__(self, location)
- self.value = value
- self.location = location
-
- def build(self, builder):
- builder.setElidedFallbackName(self.value, self.location)
-
- def asFea(self, indent=""):
- return f"ElidedFallbackNameID {self.value};"
-
-
-class STATAxisValueStatement(Statement):
- """A STAT table Axis Value Record
-
- Args:
- names (list): a list of :class:`STATNameStatement` objects
- locations (list): a list of :class:`AxisValueLocationStatement` objects
- flags (int): an int
- """
-
- def __init__(self, names, locations, flags, location=None):
- Statement.__init__(self, location)
- self.names = names
- self.locations = locations
- self.flags = flags
-
- def build(self, builder):
- builder.addAxisValueRecord(self, self.location)
-
- def asFea(self, indent=""):
- res = "AxisValue {\n"
- for location in self.locations:
- res += location.asFea()
-
- for nameRecord in self.names:
- res += nameRecord.asFea()
- res += "\n"
-
- if self.flags:
- flags = ["OlderSiblingFontAttribute", "ElidableAxisValueName"]
- flagStrings = []
- curr = 1
- for i in range(len(flags)):
- if self.flags & curr != 0:
- flagStrings.append(flags[i])
- curr = curr << 1
- res += f"flag {' '.join(flagStrings)};\n"
- res += "};"
- return res
-
-
-class AxisValueLocationStatement(Statement):
- """
- A STAT table Axis Value Location
-
- Args:
- tag (str): a 4 letter axis tag
- values (list): a list of ints and/or floats
- """
-
- def __init__(self, tag, values, location=None):
- Statement.__init__(self, location)
- self.tag = tag
- self.values = values
-
- def asFea(self, res=""):
- res += f"location {self.tag} "
- res += f"{' '.join(str(i) for i in self.values)};\n"
- return res
-
-
-class ConditionsetStatement(Statement):
- """
- A variable layout conditionset
-
- Args:
- name (str): the name of this conditionset
- conditions (dict): a dictionary mapping axis tags to a
- tuple of (min,max) userspace coordinates.
- """
-
- def __init__(self, name, conditions, location=None):
- Statement.__init__(self, location)
- self.name = name
- self.conditions = conditions
-
- def build(self, builder):
- builder.add_conditionset(self.location, self.name, self.conditions)
-
- def asFea(self, res="", indent=""):
- res += indent + f"conditionset {self.name} " + "{\n"
- for tag, (minvalue, maxvalue) in self.conditions.items():
- res += indent + SHIFT + f"{tag} {minvalue} {maxvalue};\n"
- res += indent + "}" + f" {self.name};\n"
- return res
-
-
-class VariationBlock(Block):
- """A variation feature block, applicable in a given set of conditions."""
-
- def __init__(self, name, conditionset, use_extension=False, location=None):
- Block.__init__(self, location)
- self.name, self.conditionset, self.use_extension = (
- name,
- conditionset,
- use_extension,
- )
-
- def build(self, builder):
- """Call the ``start_feature`` callback on the builder object, visit
- all the statements in this feature, and then call ``end_feature``."""
- builder.start_feature(self.location, self.name)
- if (
- self.conditionset != "NULL"
- and self.conditionset not in builder.conditionsets_
- ):
- raise FeatureLibError(
- f"variation block used undefined conditionset {self.conditionset}",
- self.location,
- )
-
- # language exclude_dflt statements modify builder.features_
- # limit them to this block with temporary builder.features_
- features = builder.features_
- builder.features_ = {}
- Block.build(self, builder)
- for key, value in builder.features_.items():
- items = builder.feature_variations_.setdefault(key, {}).setdefault(
- self.conditionset, []
- )
- items.extend(value)
- if key not in features:
- features[key] = [] # Ensure we make a feature record
- builder.features_ = features
- builder.end_feature()
-
- def asFea(self, indent=""):
- res = indent + "variation %s " % self.name.strip()
- res += self.conditionset + " "
- if self.use_extension:
- res += "useExtension "
- res += "{\n"
- res += Block.asFea(self, indent=indent)
- res += indent + "} %s;\n" % self.name.strip()
- return res
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/builder.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/builder.py
deleted file mode 100644
index 42d1f8f..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/builder.py
+++ /dev/null
@@ -1,1706 +0,0 @@
-from fontTools.misc import sstruct
-from fontTools.misc.textTools import Tag, tostr, binary2num, safeEval
-from fontTools.feaLib.error import FeatureLibError
-from fontTools.feaLib.lookupDebugInfo import (
- LookupDebugInfo,
- LOOKUP_DEBUG_INFO_KEY,
- LOOKUP_DEBUG_ENV_VAR,
-)
-from fontTools.feaLib.parser import Parser
-from fontTools.feaLib.ast import FeatureFile
-from fontTools.feaLib.variableScalar import VariableScalar
-from fontTools.otlLib import builder as otl
-from fontTools.otlLib.maxContextCalc import maxCtxFont
-from fontTools.ttLib import newTable, getTableModule
-from fontTools.ttLib.tables import otBase, otTables
-from fontTools.otlLib.builder import (
- AlternateSubstBuilder,
- ChainContextPosBuilder,
- ChainContextSubstBuilder,
- LigatureSubstBuilder,
- MultipleSubstBuilder,
- CursivePosBuilder,
- MarkBasePosBuilder,
- MarkLigPosBuilder,
- MarkMarkPosBuilder,
- ReverseChainSingleSubstBuilder,
- SingleSubstBuilder,
- ClassPairPosSubtableBuilder,
- PairPosBuilder,
- SinglePosBuilder,
- ChainContextualRule,
-)
-from fontTools.otlLib.error import OpenTypeLibError
-from fontTools.varLib.varStore import OnlineVarStoreBuilder
-from fontTools.varLib.builder import buildVarDevTable
-from fontTools.varLib.featureVars import addFeatureVariationsRaw
-from fontTools.varLib.models import normalizeValue, piecewiseLinearMap
-from collections import defaultdict
-import itertools
-from io import StringIO
-import logging
-import warnings
-import os
-
-
-log = logging.getLogger(__name__)
-
-
-def addOpenTypeFeatures(font, featurefile, tables=None, debug=False):
- """Add features from a file to a font. Note that this replaces any features
- currently present.
-
- Args:
- font (feaLib.ttLib.TTFont): The font object.
- featurefile: Either a path or file object (in which case we
- parse it into an AST), or a pre-parsed AST instance.
- tables: If passed, restrict the set of affected tables to those in the
- list.
- debug: Whether to add source debugging information to the font in the
- ``Debg`` table
-
- """
- builder = Builder(font, featurefile)
- builder.build(tables=tables, debug=debug)
-
-
-def addOpenTypeFeaturesFromString(
- font, features, filename=None, tables=None, debug=False
-):
- """Add features from a string to a font. Note that this replaces any
- features currently present.
-
- Args:
- font (feaLib.ttLib.TTFont): The font object.
- features: A string containing feature code.
- filename: The directory containing ``filename`` is used as the root of
- relative ``include()`` paths; if ``None`` is provided, the current
- directory is assumed.
- tables: If passed, restrict the set of affected tables to those in the
- list.
- debug: Whether to add source debugging information to the font in the
- ``Debg`` table
-
- """
-
- featurefile = StringIO(tostr(features))
- if filename:
- featurefile.name = filename
- addOpenTypeFeatures(font, featurefile, tables=tables, debug=debug)
-
-
-class Builder(object):
- supportedTables = frozenset(
- Tag(tag)
- for tag in [
- "BASE",
- "GDEF",
- "GPOS",
- "GSUB",
- "OS/2",
- "head",
- "hhea",
- "name",
- "vhea",
- "STAT",
- ]
- )
-
- def __init__(self, font, featurefile):
- self.font = font
- # 'featurefile' can be either a path or file object (in which case we
- # parse it into an AST), or a pre-parsed AST instance
- if isinstance(featurefile, FeatureFile):
- self.parseTree, self.file = featurefile, None
- else:
- self.parseTree, self.file = None, featurefile
- self.glyphMap = font.getReverseGlyphMap()
- self.varstorebuilder = None
- if "fvar" in font:
- self.axes = font["fvar"].axes
- self.varstorebuilder = OnlineVarStoreBuilder(
- [ax.axisTag for ax in self.axes]
- )
- self.default_language_systems_ = set()
- self.script_ = None
- self.lookupflag_ = 0
- self.lookupflag_markFilterSet_ = None
- self.language_systems = set()
- self.seen_non_DFLT_script_ = False
- self.named_lookups_ = {}
- self.cur_lookup_ = None
- self.cur_lookup_name_ = None
- self.cur_feature_name_ = None
- self.lookups_ = []
- self.lookup_locations = {"GSUB": {}, "GPOS": {}}
- self.features_ = {} # ('latn', 'DEU ', 'smcp') --> [LookupBuilder*]
- self.required_features_ = {} # ('latn', 'DEU ') --> 'scmp'
- self.feature_variations_ = {}
- # for feature 'aalt'
- self.aalt_features_ = [] # [(location, featureName)*], for 'aalt'
- self.aalt_location_ = None
- self.aalt_alternates_ = {}
- # for 'featureNames'
- self.featureNames_ = set()
- self.featureNames_ids_ = {}
- # for 'cvParameters'
- self.cv_parameters_ = set()
- self.cv_parameters_ids_ = {}
- self.cv_num_named_params_ = {}
- self.cv_characters_ = defaultdict(list)
- # for feature 'size'
- self.size_parameters_ = None
- # for table 'head'
- self.fontRevision_ = None # 2.71
- # for table 'name'
- self.names_ = []
- # for table 'BASE'
- self.base_horiz_axis_ = None
- self.base_vert_axis_ = None
- # for table 'GDEF'
- self.attachPoints_ = {} # "a" --> {3, 7}
- self.ligCaretCoords_ = {} # "f_f_i" --> {300, 600}
- self.ligCaretPoints_ = {} # "f_f_i" --> {3, 7}
- self.glyphClassDefs_ = {} # "fi" --> (2, (file, line, column))
- self.markAttach_ = {} # "acute" --> (4, (file, line, column))
- self.markAttachClassID_ = {} # frozenset({"acute", "grave"}) --> 4
- self.markFilterSets_ = {} # frozenset({"acute", "grave"}) --> 4
- # for table 'OS/2'
- self.os2_ = {}
- # for table 'hhea'
- self.hhea_ = {}
- # for table 'vhea'
- self.vhea_ = {}
- # for table 'STAT'
- self.stat_ = {}
- # for conditionsets
- self.conditionsets_ = {}
- # We will often use exactly the same locations (i.e. the font's masters)
- # for a large number of variable scalars. Instead of creating a model
- # for each, let's share the models.
- self.model_cache = {}
-
- def build(self, tables=None, debug=False):
- if self.parseTree is None:
- self.parseTree = Parser(self.file, self.glyphMap).parse()
- self.parseTree.build(self)
- # by default, build all the supported tables
- if tables is None:
- tables = self.supportedTables
- else:
- tables = frozenset(tables)
- unsupported = tables - self.supportedTables
- if unsupported:
- unsupported_string = ", ".join(sorted(unsupported))
- raise NotImplementedError(
- "The following tables were requested but are unsupported: "
- f"{unsupported_string}."
- )
- if "GSUB" in tables:
- self.build_feature_aalt_()
- if "head" in tables:
- self.build_head()
- if "hhea" in tables:
- self.build_hhea()
- if "vhea" in tables:
- self.build_vhea()
- if "name" in tables:
- self.build_name()
- if "OS/2" in tables:
- self.build_OS_2()
- if "STAT" in tables:
- self.build_STAT()
- for tag in ("GPOS", "GSUB"):
- if tag not in tables:
- continue
- table = self.makeTable(tag)
- if self.feature_variations_:
- self.makeFeatureVariations(table, tag)
- if (
- table.ScriptList.ScriptCount > 0
- or table.FeatureList.FeatureCount > 0
- or table.LookupList.LookupCount > 0
- ):
- fontTable = self.font[tag] = newTable(tag)
- fontTable.table = table
- elif tag in self.font:
- del self.font[tag]
- if any(tag in self.font for tag in ("GPOS", "GSUB")) and "OS/2" in self.font:
- self.font["OS/2"].usMaxContext = maxCtxFont(self.font)
- if "GDEF" in tables:
- gdef = self.buildGDEF()
- if gdef:
- self.font["GDEF"] = gdef
- elif "GDEF" in self.font:
- del self.font["GDEF"]
- if "BASE" in tables:
- base = self.buildBASE()
- if base:
- self.font["BASE"] = base
- elif "BASE" in self.font:
- del self.font["BASE"]
- if debug or os.environ.get(LOOKUP_DEBUG_ENV_VAR):
- self.buildDebg()
-
- def get_chained_lookup_(self, location, builder_class):
- result = builder_class(self.font, location)
- result.lookupflag = self.lookupflag_
- result.markFilterSet = self.lookupflag_markFilterSet_
- self.lookups_.append(result)
- return result
-
- def add_lookup_to_feature_(self, lookup, feature_name):
- for script, lang in self.language_systems:
- key = (script, lang, feature_name)
- self.features_.setdefault(key, []).append(lookup)
-
- def get_lookup_(self, location, builder_class):
- if (
- self.cur_lookup_
- and type(self.cur_lookup_) == builder_class
- and self.cur_lookup_.lookupflag == self.lookupflag_
- and self.cur_lookup_.markFilterSet == self.lookupflag_markFilterSet_
- ):
- return self.cur_lookup_
- if self.cur_lookup_name_ and self.cur_lookup_:
- raise FeatureLibError(
- "Within a named lookup block, all rules must be of "
- "the same lookup type and flag",
- location,
- )
- self.cur_lookup_ = builder_class(self.font, location)
- self.cur_lookup_.lookupflag = self.lookupflag_
- self.cur_lookup_.markFilterSet = self.lookupflag_markFilterSet_
- self.lookups_.append(self.cur_lookup_)
- if self.cur_lookup_name_:
- # We are starting a lookup rule inside a named lookup block.
- self.named_lookups_[self.cur_lookup_name_] = self.cur_lookup_
- if self.cur_feature_name_:
- # We are starting a lookup rule inside a feature. This includes
- # lookup rules inside named lookups inside features.
- self.add_lookup_to_feature_(self.cur_lookup_, self.cur_feature_name_)
- return self.cur_lookup_
-
- def build_feature_aalt_(self):
- if not self.aalt_features_ and not self.aalt_alternates_:
- return
- alternates = {g: set(a) for g, a in self.aalt_alternates_.items()}
- for location, name in self.aalt_features_ + [(None, "aalt")]:
- feature = [
- (script, lang, feature, lookups)
- for (script, lang, feature), lookups in self.features_.items()
- if feature == name
- ]
- # "aalt" does not have to specify its own lookups, but it might.
- if not feature and name != "aalt":
- warnings.warn("%s: Feature %s has not been defined" % (location, name))
- continue
- for script, lang, feature, lookups in feature:
- for lookuplist in lookups:
- if not isinstance(lookuplist, list):
- lookuplist = [lookuplist]
- for lookup in lookuplist:
- for glyph, alts in lookup.getAlternateGlyphs().items():
- alternates.setdefault(glyph, set()).update(alts)
- single = {
- glyph: list(repl)[0] for glyph, repl in alternates.items() if len(repl) == 1
- }
- # TODO: Figure out the glyph alternate ordering used by makeotf.
- # https://github.com/fonttools/fonttools/issues/836
- multi = {
- glyph: sorted(repl, key=self.font.getGlyphID)
- for glyph, repl in alternates.items()
- if len(repl) > 1
- }
- if not single and not multi:
- return
- self.features_ = {
- (script, lang, feature): lookups
- for (script, lang, feature), lookups in self.features_.items()
- if feature != "aalt"
- }
- old_lookups = self.lookups_
- self.lookups_ = []
- self.start_feature(self.aalt_location_, "aalt")
- if single:
- single_lookup = self.get_lookup_(location, SingleSubstBuilder)
- single_lookup.mapping = single
- if multi:
- multi_lookup = self.get_lookup_(location, AlternateSubstBuilder)
- multi_lookup.alternates = multi
- self.end_feature()
- self.lookups_.extend(old_lookups)
-
- def build_head(self):
- if not self.fontRevision_:
- return
- table = self.font.get("head")
- if not table: # this only happens for unit tests
- table = self.font["head"] = newTable("head")
- table.decompile(b"\0" * 54, self.font)
- table.tableVersion = 1.0
- table.created = table.modified = 3406620153 # 2011-12-13 11:22:33
- table.fontRevision = self.fontRevision_
-
- def build_hhea(self):
- if not self.hhea_:
- return
- table = self.font.get("hhea")
- if not table: # this only happens for unit tests
- table = self.font["hhea"] = newTable("hhea")
- table.decompile(b"\0" * 36, self.font)
- table.tableVersion = 0x00010000
- if "caretoffset" in self.hhea_:
- table.caretOffset = self.hhea_["caretoffset"]
- if "ascender" in self.hhea_:
- table.ascent = self.hhea_["ascender"]
- if "descender" in self.hhea_:
- table.descent = self.hhea_["descender"]
- if "linegap" in self.hhea_:
- table.lineGap = self.hhea_["linegap"]
-
- def build_vhea(self):
- if not self.vhea_:
- return
- table = self.font.get("vhea")
- if not table: # this only happens for unit tests
- table = self.font["vhea"] = newTable("vhea")
- table.decompile(b"\0" * 36, self.font)
- table.tableVersion = 0x00011000
- if "verttypoascender" in self.vhea_:
- table.ascent = self.vhea_["verttypoascender"]
- if "verttypodescender" in self.vhea_:
- table.descent = self.vhea_["verttypodescender"]
- if "verttypolinegap" in self.vhea_:
- table.lineGap = self.vhea_["verttypolinegap"]
-
- def get_user_name_id(self, table):
- # Try to find first unused font-specific name id
- nameIDs = [name.nameID for name in table.names]
- for user_name_id in range(256, 32767):
- if user_name_id not in nameIDs:
- return user_name_id
-
- def buildFeatureParams(self, tag):
- params = None
- if tag == "size":
- params = otTables.FeatureParamsSize()
- (
- params.DesignSize,
- params.SubfamilyID,
- params.RangeStart,
- params.RangeEnd,
- ) = self.size_parameters_
- if tag in self.featureNames_ids_:
- params.SubfamilyNameID = self.featureNames_ids_[tag]
- else:
- params.SubfamilyNameID = 0
- elif tag in self.featureNames_:
- if not self.featureNames_ids_:
- # name table wasn't selected among the tables to build; skip
- pass
- else:
- assert tag in self.featureNames_ids_
- params = otTables.FeatureParamsStylisticSet()
- params.Version = 0
- params.UINameID = self.featureNames_ids_[tag]
- elif tag in self.cv_parameters_:
- params = otTables.FeatureParamsCharacterVariants()
- params.Format = 0
- params.FeatUILabelNameID = self.cv_parameters_ids_.get(
- (tag, "FeatUILabelNameID"), 0
- )
- params.FeatUITooltipTextNameID = self.cv_parameters_ids_.get(
- (tag, "FeatUITooltipTextNameID"), 0
- )
- params.SampleTextNameID = self.cv_parameters_ids_.get(
- (tag, "SampleTextNameID"), 0
- )
- params.NumNamedParameters = self.cv_num_named_params_.get(tag, 0)
- params.FirstParamUILabelNameID = self.cv_parameters_ids_.get(
- (tag, "ParamUILabelNameID_0"), 0
- )
- params.CharCount = len(self.cv_characters_[tag])
- params.Character = self.cv_characters_[tag]
- return params
-
- def build_name(self):
- if not self.names_:
- return
- table = self.font.get("name")
- if not table: # this only happens for unit tests
- table = self.font["name"] = newTable("name")
- table.names = []
- for name in self.names_:
- nameID, platformID, platEncID, langID, string = name
- # For featureNames block, nameID is 'feature tag'
- # For cvParameters blocks, nameID is ('feature tag', 'block name')
- if not isinstance(nameID, int):
- tag = nameID
- if tag in self.featureNames_:
- if tag not in self.featureNames_ids_:
- self.featureNames_ids_[tag] = self.get_user_name_id(table)
- assert self.featureNames_ids_[tag] is not None
- nameID = self.featureNames_ids_[tag]
- elif tag[0] in self.cv_parameters_:
- if tag not in self.cv_parameters_ids_:
- self.cv_parameters_ids_[tag] = self.get_user_name_id(table)
- assert self.cv_parameters_ids_[tag] is not None
- nameID = self.cv_parameters_ids_[tag]
- table.setName(string, nameID, platformID, platEncID, langID)
- table.names.sort()
-
- def build_OS_2(self):
- if not self.os2_:
- return
- table = self.font.get("OS/2")
- if not table: # this only happens for unit tests
- table = self.font["OS/2"] = newTable("OS/2")
- data = b"\0" * sstruct.calcsize(getTableModule("OS/2").OS2_format_0)
- table.decompile(data, self.font)
- version = 0
- if "fstype" in self.os2_:
- table.fsType = self.os2_["fstype"]
- if "panose" in self.os2_:
- panose = getTableModule("OS/2").Panose()
- (
- panose.bFamilyType,
- panose.bSerifStyle,
- panose.bWeight,
- panose.bProportion,
- panose.bContrast,
- panose.bStrokeVariation,
- panose.bArmStyle,
- panose.bLetterForm,
- panose.bMidline,
- panose.bXHeight,
- ) = self.os2_["panose"]
- table.panose = panose
- if "typoascender" in self.os2_:
- table.sTypoAscender = self.os2_["typoascender"]
- if "typodescender" in self.os2_:
- table.sTypoDescender = self.os2_["typodescender"]
- if "typolinegap" in self.os2_:
- table.sTypoLineGap = self.os2_["typolinegap"]
- if "winascent" in self.os2_:
- table.usWinAscent = self.os2_["winascent"]
- if "windescent" in self.os2_:
- table.usWinDescent = self.os2_["windescent"]
- if "vendor" in self.os2_:
- table.achVendID = safeEval("'''" + self.os2_["vendor"] + "'''")
- if "weightclass" in self.os2_:
- table.usWeightClass = self.os2_["weightclass"]
- if "widthclass" in self.os2_:
- table.usWidthClass = self.os2_["widthclass"]
- if "unicoderange" in self.os2_:
- table.setUnicodeRanges(self.os2_["unicoderange"])
- if "codepagerange" in self.os2_:
- pages = self.build_codepages_(self.os2_["codepagerange"])
- table.ulCodePageRange1, table.ulCodePageRange2 = pages
- version = 1
- if "xheight" in self.os2_:
- table.sxHeight = self.os2_["xheight"]
- version = 2
- if "capheight" in self.os2_:
- table.sCapHeight = self.os2_["capheight"]
- version = 2
- if "loweropsize" in self.os2_:
- table.usLowerOpticalPointSize = self.os2_["loweropsize"]
- version = 5
- if "upperopsize" in self.os2_:
- table.usUpperOpticalPointSize = self.os2_["upperopsize"]
- version = 5
-
- def checkattr(table, attrs):
- for attr in attrs:
- if not hasattr(table, attr):
- setattr(table, attr, 0)
-
- table.version = max(version, table.version)
- # this only happens for unit tests
- if version >= 1:
- checkattr(table, ("ulCodePageRange1", "ulCodePageRange2"))
- if version >= 2:
- checkattr(
- table,
- (
- "sxHeight",
- "sCapHeight",
- "usDefaultChar",
- "usBreakChar",
- "usMaxContext",
- ),
- )
- if version >= 5:
- checkattr(table, ("usLowerOpticalPointSize", "usUpperOpticalPointSize"))
-
- def setElidedFallbackName(self, value, location):
- # ElidedFallbackName is a convenience method for setting
- # ElidedFallbackNameID so only one can be allowed
- for token in ("ElidedFallbackName", "ElidedFallbackNameID"):
- if token in self.stat_:
- raise FeatureLibError(
- f"{token} is already set.",
- location,
- )
- if isinstance(value, int):
- self.stat_["ElidedFallbackNameID"] = value
- elif isinstance(value, list):
- self.stat_["ElidedFallbackName"] = value
- else:
- raise AssertionError(value)
-
- def addDesignAxis(self, designAxis, location):
- if "DesignAxes" not in self.stat_:
- self.stat_["DesignAxes"] = []
- if designAxis.tag in (r.tag for r in self.stat_["DesignAxes"]):
- raise FeatureLibError(
- f'DesignAxis already defined for tag "{designAxis.tag}".',
- location,
- )
- if designAxis.axisOrder in (r.axisOrder for r in self.stat_["DesignAxes"]):
- raise FeatureLibError(
- f"DesignAxis already defined for axis number {designAxis.axisOrder}.",
- location,
- )
- self.stat_["DesignAxes"].append(designAxis)
-
- def addAxisValueRecord(self, axisValueRecord, location):
- if "AxisValueRecords" not in self.stat_:
- self.stat_["AxisValueRecords"] = []
- # Check for duplicate AxisValueRecords
- for record_ in self.stat_["AxisValueRecords"]:
- if (
- {n.asFea() for n in record_.names}
- == {n.asFea() for n in axisValueRecord.names}
- and {n.asFea() for n in record_.locations}
- == {n.asFea() for n in axisValueRecord.locations}
- and record_.flags == axisValueRecord.flags
- ):
- raise FeatureLibError(
- "An AxisValueRecord with these values is already defined.",
- location,
- )
- self.stat_["AxisValueRecords"].append(axisValueRecord)
-
- def build_STAT(self):
- if not self.stat_:
- return
-
- axes = self.stat_.get("DesignAxes")
- if not axes:
- raise FeatureLibError("DesignAxes not defined", None)
- axisValueRecords = self.stat_.get("AxisValueRecords")
- axisValues = {}
- format4_locations = []
- for tag in axes:
- axisValues[tag.tag] = []
- if axisValueRecords is not None:
- for avr in axisValueRecords:
- valuesDict = {}
- if avr.flags > 0:
- valuesDict["flags"] = avr.flags
- if len(avr.locations) == 1:
- location = avr.locations[0]
- values = location.values
- if len(values) == 1: # format1
- valuesDict.update({"value": values[0], "name": avr.names})
- if len(values) == 2: # format3
- valuesDict.update(
- {
- "value": values[0],
- "linkedValue": values[1],
- "name": avr.names,
- }
- )
- if len(values) == 3: # format2
- nominal, minVal, maxVal = values
- valuesDict.update(
- {
- "nominalValue": nominal,
- "rangeMinValue": minVal,
- "rangeMaxValue": maxVal,
- "name": avr.names,
- }
- )
- axisValues[location.tag].append(valuesDict)
- else:
- valuesDict.update(
- {
- "location": {i.tag: i.values[0] for i in avr.locations},
- "name": avr.names,
- }
- )
- format4_locations.append(valuesDict)
-
- designAxes = [
- {
- "ordering": a.axisOrder,
- "tag": a.tag,
- "name": a.names,
- "values": axisValues[a.tag],
- }
- for a in axes
- ]
-
- nameTable = self.font.get("name")
- if not nameTable: # this only happens for unit tests
- nameTable = self.font["name"] = newTable("name")
- nameTable.names = []
-
- if "ElidedFallbackNameID" in self.stat_:
- nameID = self.stat_["ElidedFallbackNameID"]
- name = nameTable.getDebugName(nameID)
- if not name:
- raise FeatureLibError(
- f"ElidedFallbackNameID {nameID} points "
- "to a nameID that does not exist in the "
- '"name" table',
- None,
- )
- elif "ElidedFallbackName" in self.stat_:
- nameID = self.stat_["ElidedFallbackName"]
-
- otl.buildStatTable(
- self.font,
- designAxes,
- locations=format4_locations,
- elidedFallbackName=nameID,
- )
-
- def build_codepages_(self, pages):
- pages2bits = {
- 1252: 0,
- 1250: 1,
- 1251: 2,
- 1253: 3,
- 1254: 4,
- 1255: 5,
- 1256: 6,
- 1257: 7,
- 1258: 8,
- 874: 16,
- 932: 17,
- 936: 18,
- 949: 19,
- 950: 20,
- 1361: 21,
- 869: 48,
- 866: 49,
- 865: 50,
- 864: 51,
- 863: 52,
- 862: 53,
- 861: 54,
- 860: 55,
- 857: 56,
- 855: 57,
- 852: 58,
- 775: 59,
- 737: 60,
- 708: 61,
- 850: 62,
- 437: 63,
- }
- bits = [pages2bits[p] for p in pages if p in pages2bits]
- pages = []
- for i in range(2):
- pages.append("")
- for j in range(i * 32, (i + 1) * 32):
- if j in bits:
- pages[i] += "1"
- else:
- pages[i] += "0"
- return [binary2num(p[::-1]) for p in pages]
-
- def buildBASE(self):
- if not self.base_horiz_axis_ and not self.base_vert_axis_:
- return None
- base = otTables.BASE()
- base.Version = 0x00010000
- base.HorizAxis = self.buildBASEAxis(self.base_horiz_axis_)
- base.VertAxis = self.buildBASEAxis(self.base_vert_axis_)
-
- result = newTable("BASE")
- result.table = base
- return result
-
- def buildBASEAxis(self, axis):
- if not axis:
- return
- bases, scripts = axis
- axis = otTables.Axis()
- axis.BaseTagList = otTables.BaseTagList()
- axis.BaseTagList.BaselineTag = bases
- axis.BaseTagList.BaseTagCount = len(bases)
- axis.BaseScriptList = otTables.BaseScriptList()
- axis.BaseScriptList.BaseScriptRecord = []
- axis.BaseScriptList.BaseScriptCount = len(scripts)
- for script in sorted(scripts):
- record = otTables.BaseScriptRecord()
- record.BaseScriptTag = script[0]
- record.BaseScript = otTables.BaseScript()
- record.BaseScript.BaseLangSysCount = 0
- record.BaseScript.BaseValues = otTables.BaseValues()
- record.BaseScript.BaseValues.DefaultIndex = bases.index(script[1])
- record.BaseScript.BaseValues.BaseCoord = []
- record.BaseScript.BaseValues.BaseCoordCount = len(script[2])
- for c in script[2]:
- coord = otTables.BaseCoord()
- coord.Format = 1
- coord.Coordinate = c
- record.BaseScript.BaseValues.BaseCoord.append(coord)
- axis.BaseScriptList.BaseScriptRecord.append(record)
- return axis
-
- def buildGDEF(self):
- gdef = otTables.GDEF()
- gdef.GlyphClassDef = self.buildGDEFGlyphClassDef_()
- gdef.AttachList = otl.buildAttachList(self.attachPoints_, self.glyphMap)
- gdef.LigCaretList = otl.buildLigCaretList(
- self.ligCaretCoords_, self.ligCaretPoints_, self.glyphMap
- )
- gdef.MarkAttachClassDef = self.buildGDEFMarkAttachClassDef_()
- gdef.MarkGlyphSetsDef = self.buildGDEFMarkGlyphSetsDef_()
- gdef.Version = 0x00010002 if gdef.MarkGlyphSetsDef else 0x00010000
- if self.varstorebuilder:
- store = self.varstorebuilder.finish()
- if store:
- gdef.Version = 0x00010003
- gdef.VarStore = store
- varidx_map = store.optimize()
-
- gdef.remap_device_varidxes(varidx_map)
- if "GPOS" in self.font:
- self.font["GPOS"].table.remap_device_varidxes(varidx_map)
- self.model_cache.clear()
- if any(
- (
- gdef.GlyphClassDef,
- gdef.AttachList,
- gdef.LigCaretList,
- gdef.MarkAttachClassDef,
- gdef.MarkGlyphSetsDef,
- )
- ) or hasattr(gdef, "VarStore"):
- result = newTable("GDEF")
- result.table = gdef
- return result
- else:
- return None
-
- def buildGDEFGlyphClassDef_(self):
- if self.glyphClassDefs_:
- classes = {g: c for (g, (c, _)) in self.glyphClassDefs_.items()}
- else:
- classes = {}
- for lookup in self.lookups_:
- classes.update(lookup.inferGlyphClasses())
- for markClass in self.parseTree.markClasses.values():
- for markClassDef in markClass.definitions:
- for glyph in markClassDef.glyphSet():
- classes[glyph] = 3
- if classes:
- result = otTables.GlyphClassDef()
- result.classDefs = classes
- return result
- else:
- return None
-
- def buildGDEFMarkAttachClassDef_(self):
- classDefs = {g: c for g, (c, _) in self.markAttach_.items()}
- if not classDefs:
- return None
- result = otTables.MarkAttachClassDef()
- result.classDefs = classDefs
- return result
-
- def buildGDEFMarkGlyphSetsDef_(self):
- sets = []
- for glyphs, id_ in sorted(
- self.markFilterSets_.items(), key=lambda item: item[1]
- ):
- sets.append(glyphs)
- return otl.buildMarkGlyphSetsDef(sets, self.glyphMap)
-
- def buildDebg(self):
- if "Debg" not in self.font:
- self.font["Debg"] = newTable("Debg")
- self.font["Debg"].data = {}
- self.font["Debg"].data[LOOKUP_DEBUG_INFO_KEY] = self.lookup_locations
-
- def buildLookups_(self, tag):
- assert tag in ("GPOS", "GSUB"), tag
- for lookup in self.lookups_:
- lookup.lookup_index = None
- lookups = []
- for lookup in self.lookups_:
- if lookup.table != tag:
- continue
- lookup.lookup_index = len(lookups)
- self.lookup_locations[tag][str(lookup.lookup_index)] = LookupDebugInfo(
- location=str(lookup.location),
- name=self.get_lookup_name_(lookup),
- feature=None,
- )
- lookups.append(lookup)
- try:
- otLookups = [l.build() for l in lookups]
- except OpenTypeLibError as e:
- raise FeatureLibError(str(e), e.location) from e
- return otLookups
-
- def makeTable(self, tag):
- table = getattr(otTables, tag, None)()
- table.Version = 0x00010000
- table.ScriptList = otTables.ScriptList()
- table.ScriptList.ScriptRecord = []
- table.FeatureList = otTables.FeatureList()
- table.FeatureList.FeatureRecord = []
- table.LookupList = otTables.LookupList()
- table.LookupList.Lookup = self.buildLookups_(tag)
-
- # Build a table for mapping (tag, lookup_indices) to feature_index.
- # For example, ('liga', (2,3,7)) --> 23.
- feature_indices = {}
- required_feature_indices = {} # ('latn', 'DEU') --> 23
- scripts = {} # 'latn' --> {'DEU': [23, 24]} for feature #23,24
- # Sort the feature table by feature tag:
- # https://github.com/fonttools/fonttools/issues/568
- sortFeatureTag = lambda f: (f[0][2], f[0][1], f[0][0], f[1])
- for key, lookups in sorted(self.features_.items(), key=sortFeatureTag):
- script, lang, feature_tag = key
- # l.lookup_index will be None when a lookup is not needed
- # for the table under construction. For example, substitution
- # rules will have no lookup_index while building GPOS tables.
- lookup_indices = tuple(
- [l.lookup_index for l in lookups if l.lookup_index is not None]
- )
-
- size_feature = tag == "GPOS" and feature_tag == "size"
- force_feature = self.any_feature_variations(feature_tag, tag)
- if len(lookup_indices) == 0 and not size_feature and not force_feature:
- continue
-
- for ix in lookup_indices:
- try:
- self.lookup_locations[tag][str(ix)] = self.lookup_locations[tag][
- str(ix)
- ]._replace(feature=key)
- except KeyError:
- warnings.warn(
- "feaLib.Builder subclass needs upgrading to "
- "stash debug information. See fonttools#2065."
- )
-
- feature_key = (feature_tag, lookup_indices)
- feature_index = feature_indices.get(feature_key)
- if feature_index is None:
- feature_index = len(table.FeatureList.FeatureRecord)
- frec = otTables.FeatureRecord()
- frec.FeatureTag = feature_tag
- frec.Feature = otTables.Feature()
- frec.Feature.FeatureParams = self.buildFeatureParams(feature_tag)
- frec.Feature.LookupListIndex = list(lookup_indices)
- frec.Feature.LookupCount = len(lookup_indices)
- table.FeatureList.FeatureRecord.append(frec)
- feature_indices[feature_key] = feature_index
- scripts.setdefault(script, {}).setdefault(lang, []).append(feature_index)
- if self.required_features_.get((script, lang)) == feature_tag:
- required_feature_indices[(script, lang)] = feature_index
-
- # Build ScriptList.
- for script, lang_features in sorted(scripts.items()):
- srec = otTables.ScriptRecord()
- srec.ScriptTag = script
- srec.Script = otTables.Script()
- srec.Script.DefaultLangSys = None
- srec.Script.LangSysRecord = []
- for lang, feature_indices in sorted(lang_features.items()):
- langrec = otTables.LangSysRecord()
- langrec.LangSys = otTables.LangSys()
- langrec.LangSys.LookupOrder = None
-
- req_feature_index = required_feature_indices.get((script, lang))
- if req_feature_index is None:
- langrec.LangSys.ReqFeatureIndex = 0xFFFF
- else:
- langrec.LangSys.ReqFeatureIndex = req_feature_index
-
- langrec.LangSys.FeatureIndex = [
- i for i in feature_indices if i != req_feature_index
- ]
- langrec.LangSys.FeatureCount = len(langrec.LangSys.FeatureIndex)
-
- if lang == "dflt":
- srec.Script.DefaultLangSys = langrec.LangSys
- else:
- langrec.LangSysTag = lang
- srec.Script.LangSysRecord.append(langrec)
- srec.Script.LangSysCount = len(srec.Script.LangSysRecord)
- table.ScriptList.ScriptRecord.append(srec)
-
- table.ScriptList.ScriptCount = len(table.ScriptList.ScriptRecord)
- table.FeatureList.FeatureCount = len(table.FeatureList.FeatureRecord)
- table.LookupList.LookupCount = len(table.LookupList.Lookup)
- return table
-
- def makeFeatureVariations(self, table, table_tag):
- feature_vars = {}
- has_any_variations = False
- # Sort out which lookups to build, gather their indices
- for (_, _, feature_tag), variations in self.feature_variations_.items():
- feature_vars[feature_tag] = []
- for conditionset, builders in variations.items():
- raw_conditionset = self.conditionsets_[conditionset]
- indices = []
- for b in builders:
- if b.table != table_tag:
- continue
- assert b.lookup_index is not None
- indices.append(b.lookup_index)
- has_any_variations = True
- feature_vars[feature_tag].append((raw_conditionset, indices))
-
- if has_any_variations:
- for feature_tag, conditions_and_lookups in feature_vars.items():
- addFeatureVariationsRaw(
- self.font, table, conditions_and_lookups, feature_tag
- )
-
- def any_feature_variations(self, feature_tag, table_tag):
- for (_, _, feature), variations in self.feature_variations_.items():
- if feature != feature_tag:
- continue
- for conditionset, builders in variations.items():
- if any(b.table == table_tag for b in builders):
- return True
- return False
-
- def get_lookup_name_(self, lookup):
- rev = {v: k for k, v in self.named_lookups_.items()}
- if lookup in rev:
- return rev[lookup]
- return None
-
- def add_language_system(self, location, script, language):
- # OpenType Feature File Specification, section 4.b.i
- if script == "DFLT" and language == "dflt" and self.default_language_systems_:
- raise FeatureLibError(
- 'If "languagesystem DFLT dflt" is present, it must be '
- "the first of the languagesystem statements",
- location,
- )
- if script == "DFLT":
- if self.seen_non_DFLT_script_:
- raise FeatureLibError(
- 'languagesystems using the "DFLT" script tag must '
- "precede all other languagesystems",
- location,
- )
- else:
- self.seen_non_DFLT_script_ = True
- if (script, language) in self.default_language_systems_:
- raise FeatureLibError(
- '"languagesystem %s %s" has already been specified'
- % (script.strip(), language.strip()),
- location,
- )
- self.default_language_systems_.add((script, language))
-
- def get_default_language_systems_(self):
- # OpenType Feature File specification, 4.b.i. languagesystem:
- # If no "languagesystem" statement is present, then the
- # implementation must behave exactly as though the following
- # statement were present at the beginning of the feature file:
- # languagesystem DFLT dflt;
- if self.default_language_systems_:
- return frozenset(self.default_language_systems_)
- else:
- return frozenset({("DFLT", "dflt")})
-
- def start_feature(self, location, name):
- self.language_systems = self.get_default_language_systems_()
- self.script_ = "DFLT"
- self.cur_lookup_ = None
- self.cur_feature_name_ = name
- self.lookupflag_ = 0
- self.lookupflag_markFilterSet_ = None
- if name == "aalt":
- self.aalt_location_ = location
-
- def end_feature(self):
- assert self.cur_feature_name_ is not None
- self.cur_feature_name_ = None
- self.language_systems = None
- self.cur_lookup_ = None
- self.lookupflag_ = 0
- self.lookupflag_markFilterSet_ = None
-
- def start_lookup_block(self, location, name):
- if name in self.named_lookups_:
- raise FeatureLibError(
- 'Lookup "%s" has already been defined' % name, location
- )
- if self.cur_feature_name_ == "aalt":
- raise FeatureLibError(
- "Lookup blocks cannot be placed inside 'aalt' features; "
- "move it out, and then refer to it with a lookup statement",
- location,
- )
- self.cur_lookup_name_ = name
- self.named_lookups_[name] = None
- self.cur_lookup_ = None
- if self.cur_feature_name_ is None:
- self.lookupflag_ = 0
- self.lookupflag_markFilterSet_ = None
-
- def end_lookup_block(self):
- assert self.cur_lookup_name_ is not None
- self.cur_lookup_name_ = None
- self.cur_lookup_ = None
- if self.cur_feature_name_ is None:
- self.lookupflag_ = 0
- self.lookupflag_markFilterSet_ = None
-
- def add_lookup_call(self, lookup_name):
- assert lookup_name in self.named_lookups_, lookup_name
- self.cur_lookup_ = None
- lookup = self.named_lookups_[lookup_name]
- if lookup is not None: # skip empty named lookup
- self.add_lookup_to_feature_(lookup, self.cur_feature_name_)
-
- def set_font_revision(self, location, revision):
- self.fontRevision_ = revision
-
- def set_language(self, location, language, include_default, required):
- assert len(language) == 4
- if self.cur_feature_name_ in ("aalt", "size"):
- raise FeatureLibError(
- "Language statements are not allowed "
- 'within "feature %s"' % self.cur_feature_name_,
- location,
- )
- if self.cur_feature_name_ is None:
- raise FeatureLibError(
- "Language statements are not allowed "
- "within standalone lookup blocks",
- location,
- )
- self.cur_lookup_ = None
-
- key = (self.script_, language, self.cur_feature_name_)
- lookups = self.features_.get((key[0], "dflt", key[2]))
- if (language == "dflt" or include_default) and lookups:
- self.features_[key] = lookups[:]
- else:
- self.features_[key] = []
- self.language_systems = frozenset([(self.script_, language)])
-
- if required:
- key = (self.script_, language)
- if key in self.required_features_:
- raise FeatureLibError(
- "Language %s (script %s) has already "
- "specified feature %s as its required feature"
- % (
- language.strip(),
- self.script_.strip(),
- self.required_features_[key].strip(),
- ),
- location,
- )
- self.required_features_[key] = self.cur_feature_name_
-
- def getMarkAttachClass_(self, location, glyphs):
- glyphs = frozenset(glyphs)
- id_ = self.markAttachClassID_.get(glyphs)
- if id_ is not None:
- return id_
- id_ = len(self.markAttachClassID_) + 1
- self.markAttachClassID_[glyphs] = id_
- for glyph in glyphs:
- if glyph in self.markAttach_:
- _, loc = self.markAttach_[glyph]
- raise FeatureLibError(
- "Glyph %s already has been assigned "
- "a MarkAttachmentType at %s" % (glyph, loc),
- location,
- )
- self.markAttach_[glyph] = (id_, location)
- return id_
-
- def getMarkFilterSet_(self, location, glyphs):
- glyphs = frozenset(glyphs)
- id_ = self.markFilterSets_.get(glyphs)
- if id_ is not None:
- return id_
- id_ = len(self.markFilterSets_)
- self.markFilterSets_[glyphs] = id_
- return id_
-
- def set_lookup_flag(self, location, value, markAttach, markFilter):
- value = value & 0xFF
- if markAttach:
- markAttachClass = self.getMarkAttachClass_(location, markAttach)
- value = value | (markAttachClass << 8)
- if markFilter:
- markFilterSet = self.getMarkFilterSet_(location, markFilter)
- value = value | 0x10
- self.lookupflag_markFilterSet_ = markFilterSet
- else:
- self.lookupflag_markFilterSet_ = None
- self.lookupflag_ = value
-
- def set_script(self, location, script):
- if self.cur_feature_name_ in ("aalt", "size"):
- raise FeatureLibError(
- "Script statements are not allowed "
- 'within "feature %s"' % self.cur_feature_name_,
- location,
- )
- if self.cur_feature_name_ is None:
- raise FeatureLibError(
- "Script statements are not allowed " "within standalone lookup blocks",
- location,
- )
- if self.language_systems == {(script, "dflt")}:
- # Nothing to do.
- return
- self.cur_lookup_ = None
- self.script_ = script
- self.lookupflag_ = 0
- self.lookupflag_markFilterSet_ = None
- self.set_language(location, "dflt", include_default=True, required=False)
-
- def find_lookup_builders_(self, lookups):
- """Helper for building chain contextual substitutions
-
- Given a list of lookup names, finds the LookupBuilder for each name.
- If an input name is None, it gets mapped to a None LookupBuilder.
- """
- lookup_builders = []
- for lookuplist in lookups:
- if lookuplist is not None:
- lookup_builders.append(
- [self.named_lookups_.get(l.name) for l in lookuplist]
- )
- else:
- lookup_builders.append(None)
- return lookup_builders
-
- def add_attach_points(self, location, glyphs, contourPoints):
- for glyph in glyphs:
- self.attachPoints_.setdefault(glyph, set()).update(contourPoints)
-
- def add_feature_reference(self, location, featureName):
- if self.cur_feature_name_ != "aalt":
- raise FeatureLibError(
- 'Feature references are only allowed inside "feature aalt"', location
- )
- self.aalt_features_.append((location, featureName))
-
- def add_featureName(self, tag):
- self.featureNames_.add(tag)
-
- def add_cv_parameter(self, tag):
- self.cv_parameters_.add(tag)
-
- def add_to_cv_num_named_params(self, tag):
- """Adds new items to ``self.cv_num_named_params_``
- or increments the count of existing items."""
- if tag in self.cv_num_named_params_:
- self.cv_num_named_params_[tag] += 1
- else:
- self.cv_num_named_params_[tag] = 1
-
- def add_cv_character(self, character, tag):
- self.cv_characters_[tag].append(character)
-
- def set_base_axis(self, bases, scripts, vertical):
- if vertical:
- self.base_vert_axis_ = (bases, scripts)
- else:
- self.base_horiz_axis_ = (bases, scripts)
-
- def set_size_parameters(
- self, location, DesignSize, SubfamilyID, RangeStart, RangeEnd
- ):
- if self.cur_feature_name_ != "size":
- raise FeatureLibError(
- "Parameters statements are not allowed "
- 'within "feature %s"' % self.cur_feature_name_,
- location,
- )
- self.size_parameters_ = [DesignSize, SubfamilyID, RangeStart, RangeEnd]
- for script, lang in self.language_systems:
- key = (script, lang, self.cur_feature_name_)
- self.features_.setdefault(key, [])
-
- # GSUB rules
-
- # GSUB 1
- def add_single_subst(self, location, prefix, suffix, mapping, forceChain):
- if self.cur_feature_name_ == "aalt":
- for from_glyph, to_glyph in mapping.items():
- alts = self.aalt_alternates_.setdefault(from_glyph, set())
- alts.add(to_glyph)
- return
- if prefix or suffix or forceChain:
- self.add_single_subst_chained_(location, prefix, suffix, mapping)
- return
- lookup = self.get_lookup_(location, SingleSubstBuilder)
- for from_glyph, to_glyph in mapping.items():
- if from_glyph in lookup.mapping:
- if to_glyph == lookup.mapping[from_glyph]:
- log.info(
- "Removing duplicate single substitution from glyph"
- ' "%s" to "%s" at %s',
- from_glyph,
- to_glyph,
- location,
- )
- else:
- raise FeatureLibError(
- 'Already defined rule for replacing glyph "%s" by "%s"'
- % (from_glyph, lookup.mapping[from_glyph]),
- location,
- )
- lookup.mapping[from_glyph] = to_glyph
-
- # GSUB 2
- def add_multiple_subst(
- self, location, prefix, glyph, suffix, replacements, forceChain=False
- ):
- if prefix or suffix or forceChain:
- chain = self.get_lookup_(location, ChainContextSubstBuilder)
- sub = self.get_chained_lookup_(location, MultipleSubstBuilder)
- sub.mapping[glyph] = replacements
- chain.rules.append(ChainContextualRule(prefix, [{glyph}], suffix, [sub]))
- return
- lookup = self.get_lookup_(location, MultipleSubstBuilder)
- if glyph in lookup.mapping:
- if replacements == lookup.mapping[glyph]:
- log.info(
- "Removing duplicate multiple substitution from glyph"
- ' "%s" to %s%s',
- glyph,
- replacements,
- f" at {location}" if location else "",
- )
- else:
- raise FeatureLibError(
- 'Already defined substitution for glyph "%s"' % glyph, location
- )
- lookup.mapping[glyph] = replacements
-
- # GSUB 3
- def add_alternate_subst(self, location, prefix, glyph, suffix, replacement):
- if self.cur_feature_name_ == "aalt":
- alts = self.aalt_alternates_.setdefault(glyph, set())
- alts.update(replacement)
- return
- if prefix or suffix:
- chain = self.get_lookup_(location, ChainContextSubstBuilder)
- lookup = self.get_chained_lookup_(location, AlternateSubstBuilder)
- chain.rules.append(ChainContextualRule(prefix, [{glyph}], suffix, [lookup]))
- else:
- lookup = self.get_lookup_(location, AlternateSubstBuilder)
- if glyph in lookup.alternates:
- raise FeatureLibError(
- 'Already defined alternates for glyph "%s"' % glyph, location
- )
- # We allow empty replacement glyphs here.
- lookup.alternates[glyph] = replacement
-
- # GSUB 4
- def add_ligature_subst(
- self, location, prefix, glyphs, suffix, replacement, forceChain
- ):
- if prefix or suffix or forceChain:
- chain = self.get_lookup_(location, ChainContextSubstBuilder)
- lookup = self.get_chained_lookup_(location, LigatureSubstBuilder)
- chain.rules.append(ChainContextualRule(prefix, glyphs, suffix, [lookup]))
- else:
- lookup = self.get_lookup_(location, LigatureSubstBuilder)
-
- if not all(glyphs):
- raise FeatureLibError("Empty glyph class in substitution", location)
-
- # OpenType feature file syntax, section 5.d, "Ligature substitution":
- # "Since the OpenType specification does not allow ligature
- # substitutions to be specified on target sequences that contain
- # glyph classes, the implementation software will enumerate
- # all specific glyph sequences if glyph classes are detected"
- for g in sorted(itertools.product(*glyphs)):
- lookup.ligatures[g] = replacement
-
- # GSUB 5/6
- def add_chain_context_subst(self, location, prefix, glyphs, suffix, lookups):
- if not all(glyphs) or not all(prefix) or not all(suffix):
- raise FeatureLibError(
- "Empty glyph class in contextual substitution", location
- )
- lookup = self.get_lookup_(location, ChainContextSubstBuilder)
- lookup.rules.append(
- ChainContextualRule(
- prefix, glyphs, suffix, self.find_lookup_builders_(lookups)
- )
- )
-
- def add_single_subst_chained_(self, location, prefix, suffix, mapping):
- if not mapping or not all(prefix) or not all(suffix):
- raise FeatureLibError(
- "Empty glyph class in contextual substitution", location
- )
- # https://github.com/fonttools/fonttools/issues/512
- # https://github.com/fonttools/fonttools/issues/2150
- chain = self.get_lookup_(location, ChainContextSubstBuilder)
- sub = chain.find_chainable_single_subst(mapping)
- if sub is None:
- sub = self.get_chained_lookup_(location, SingleSubstBuilder)
- sub.mapping.update(mapping)
- chain.rules.append(
- ChainContextualRule(prefix, [list(mapping.keys())], suffix, [sub])
- )
-
- # GSUB 8
- def add_reverse_chain_single_subst(self, location, old_prefix, old_suffix, mapping):
- if not mapping:
- raise FeatureLibError("Empty glyph class in substitution", location)
- lookup = self.get_lookup_(location, ReverseChainSingleSubstBuilder)
- lookup.rules.append((old_prefix, old_suffix, mapping))
-
- # GPOS rules
-
- # GPOS 1
- def add_single_pos(self, location, prefix, suffix, pos, forceChain):
- if prefix or suffix or forceChain:
- self.add_single_pos_chained_(location, prefix, suffix, pos)
- else:
- lookup = self.get_lookup_(location, SinglePosBuilder)
- for glyphs, value in pos:
- if not glyphs:
- raise FeatureLibError(
- "Empty glyph class in positioning rule", location
- )
- otValueRecord = self.makeOpenTypeValueRecord(
- location, value, pairPosContext=False
- )
- for glyph in glyphs:
- try:
- lookup.add_pos(location, glyph, otValueRecord)
- except OpenTypeLibError as e:
- raise FeatureLibError(str(e), e.location) from e
-
- # GPOS 2
- def add_class_pair_pos(self, location, glyphclass1, value1, glyphclass2, value2):
- if not glyphclass1 or not glyphclass2:
- raise FeatureLibError("Empty glyph class in positioning rule", location)
- lookup = self.get_lookup_(location, PairPosBuilder)
- v1 = self.makeOpenTypeValueRecord(location, value1, pairPosContext=True)
- v2 = self.makeOpenTypeValueRecord(location, value2, pairPosContext=True)
- lookup.addClassPair(location, glyphclass1, v1, glyphclass2, v2)
-
- def add_specific_pair_pos(self, location, glyph1, value1, glyph2, value2):
- if not glyph1 or not glyph2:
- raise FeatureLibError("Empty glyph class in positioning rule", location)
- lookup = self.get_lookup_(location, PairPosBuilder)
- v1 = self.makeOpenTypeValueRecord(location, value1, pairPosContext=True)
- v2 = self.makeOpenTypeValueRecord(location, value2, pairPosContext=True)
- lookup.addGlyphPair(location, glyph1, v1, glyph2, v2)
-
- # GPOS 3
- def add_cursive_pos(self, location, glyphclass, entryAnchor, exitAnchor):
- if not glyphclass:
- raise FeatureLibError("Empty glyph class in positioning rule", location)
- lookup = self.get_lookup_(location, CursivePosBuilder)
- lookup.add_attachment(
- location,
- glyphclass,
- self.makeOpenTypeAnchor(location, entryAnchor),
- self.makeOpenTypeAnchor(location, exitAnchor),
- )
-
- # GPOS 4
- def add_mark_base_pos(self, location, bases, marks):
- builder = self.get_lookup_(location, MarkBasePosBuilder)
- self.add_marks_(location, builder, marks)
- if not bases:
- raise FeatureLibError("Empty glyph class in positioning rule", location)
- for baseAnchor, markClass in marks:
- otBaseAnchor = self.makeOpenTypeAnchor(location, baseAnchor)
- for base in bases:
- builder.bases.setdefault(base, {})[markClass.name] = otBaseAnchor
-
- # GPOS 5
- def add_mark_lig_pos(self, location, ligatures, components):
- builder = self.get_lookup_(location, MarkLigPosBuilder)
- componentAnchors = []
- if not ligatures:
- raise FeatureLibError("Empty glyph class in positioning rule", location)
- for marks in components:
- anchors = {}
- self.add_marks_(location, builder, marks)
- for ligAnchor, markClass in marks:
- anchors[markClass.name] = self.makeOpenTypeAnchor(location, ligAnchor)
- componentAnchors.append(anchors)
- for glyph in ligatures:
- builder.ligatures[glyph] = componentAnchors
-
- # GPOS 6
- def add_mark_mark_pos(self, location, baseMarks, marks):
- builder = self.get_lookup_(location, MarkMarkPosBuilder)
- self.add_marks_(location, builder, marks)
- if not baseMarks:
- raise FeatureLibError("Empty glyph class in positioning rule", location)
- for baseAnchor, markClass in marks:
- otBaseAnchor = self.makeOpenTypeAnchor(location, baseAnchor)
- for baseMark in baseMarks:
- builder.baseMarks.setdefault(baseMark, {})[
- markClass.name
- ] = otBaseAnchor
-
- # GPOS 7/8
- def add_chain_context_pos(self, location, prefix, glyphs, suffix, lookups):
- if not all(glyphs) or not all(prefix) or not all(suffix):
- raise FeatureLibError(
- "Empty glyph class in contextual positioning rule", location
- )
- lookup = self.get_lookup_(location, ChainContextPosBuilder)
- lookup.rules.append(
- ChainContextualRule(
- prefix, glyphs, suffix, self.find_lookup_builders_(lookups)
- )
- )
-
- def add_single_pos_chained_(self, location, prefix, suffix, pos):
- if not pos or not all(prefix) or not all(suffix):
- raise FeatureLibError(
- "Empty glyph class in contextual positioning rule", location
- )
- # https://github.com/fonttools/fonttools/issues/514
- chain = self.get_lookup_(location, ChainContextPosBuilder)
- targets = []
- for _, _, _, lookups in chain.rules:
- targets.extend(lookups)
- subs = []
- for glyphs, value in pos:
- if value is None:
- subs.append(None)
- continue
- otValue = self.makeOpenTypeValueRecord(
- location, value, pairPosContext=False
- )
- sub = chain.find_chainable_single_pos(targets, glyphs, otValue)
- if sub is None:
- sub = self.get_chained_lookup_(location, SinglePosBuilder)
- targets.append(sub)
- for glyph in glyphs:
- sub.add_pos(location, glyph, otValue)
- subs.append(sub)
- assert len(pos) == len(subs), (pos, subs)
- chain.rules.append(
- ChainContextualRule(prefix, [g for g, v in pos], suffix, subs)
- )
-
- def add_marks_(self, location, lookupBuilder, marks):
- """Helper for add_mark_{base,liga,mark}_pos."""
- for _, markClass in marks:
- for markClassDef in markClass.definitions:
- for mark in markClassDef.glyphs.glyphSet():
- if mark not in lookupBuilder.marks:
- otMarkAnchor = self.makeOpenTypeAnchor(
- location, markClassDef.anchor
- )
- lookupBuilder.marks[mark] = (markClass.name, otMarkAnchor)
- else:
- existingMarkClass = lookupBuilder.marks[mark][0]
- if markClass.name != existingMarkClass:
- raise FeatureLibError(
- "Glyph %s cannot be in both @%s and @%s"
- % (mark, existingMarkClass, markClass.name),
- location,
- )
-
- def add_subtable_break(self, location):
- self.cur_lookup_.add_subtable_break(location)
-
- def setGlyphClass_(self, location, glyph, glyphClass):
- oldClass, oldLocation = self.glyphClassDefs_.get(glyph, (None, None))
- if oldClass and oldClass != glyphClass:
- raise FeatureLibError(
- "Glyph %s was assigned to a different class at %s"
- % (glyph, oldLocation),
- location,
- )
- self.glyphClassDefs_[glyph] = (glyphClass, location)
-
- def add_glyphClassDef(
- self, location, baseGlyphs, ligatureGlyphs, markGlyphs, componentGlyphs
- ):
- for glyph in baseGlyphs:
- self.setGlyphClass_(location, glyph, 1)
- for glyph in ligatureGlyphs:
- self.setGlyphClass_(location, glyph, 2)
- for glyph in markGlyphs:
- self.setGlyphClass_(location, glyph, 3)
- for glyph in componentGlyphs:
- self.setGlyphClass_(location, glyph, 4)
-
- def add_ligatureCaretByIndex_(self, location, glyphs, carets):
- for glyph in glyphs:
- if glyph not in self.ligCaretPoints_:
- self.ligCaretPoints_[glyph] = carets
-
- def makeLigCaret(self, location, caret):
- if not isinstance(caret, VariableScalar):
- return caret
- default, device = self.makeVariablePos(location, caret)
- if device is not None:
- return (default, device)
- return default
-
- def add_ligatureCaretByPos_(self, location, glyphs, carets):
- carets = [self.makeLigCaret(location, caret) for caret in carets]
- for glyph in glyphs:
- if glyph not in self.ligCaretCoords_:
- self.ligCaretCoords_[glyph] = carets
-
- def add_name_record(self, location, nameID, platformID, platEncID, langID, string):
- self.names_.append([nameID, platformID, platEncID, langID, string])
-
- def add_os2_field(self, key, value):
- self.os2_[key] = value
-
- def add_hhea_field(self, key, value):
- self.hhea_[key] = value
-
- def add_vhea_field(self, key, value):
- self.vhea_[key] = value
-
- def add_conditionset(self, location, key, value):
- if "fvar" not in self.font:
- raise FeatureLibError(
- "Cannot add feature variations to a font without an 'fvar' table",
- location,
- )
-
- # Normalize
- axisMap = {
- axis.axisTag: (axis.minValue, axis.defaultValue, axis.maxValue)
- for axis in self.axes
- }
-
- value = {
- tag: (
- normalizeValue(bottom, axisMap[tag]),
- normalizeValue(top, axisMap[tag]),
- )
- for tag, (bottom, top) in value.items()
- }
-
- # NOTE: This might result in rounding errors (off-by-ones) compared to
- # rules in Designspace files, since we're working with what's in the
- # `avar` table rather than the original values.
- if "avar" in self.font:
- mapping = self.font["avar"].segments
- value = {
- axis: tuple(
- piecewiseLinearMap(v, mapping[axis]) if axis in mapping else v
- for v in condition_range
- )
- for axis, condition_range in value.items()
- }
-
- self.conditionsets_[key] = value
-
- def makeVariablePos(self, location, varscalar):
- if not self.varstorebuilder:
- raise FeatureLibError(
- "Can't define a variable scalar in a non-variable font", location
- )
-
- varscalar.axes = self.axes
- if not varscalar.does_vary:
- return varscalar.default, None
-
- default, index = varscalar.add_to_variation_store(
- self.varstorebuilder, self.model_cache, self.font.get("avar")
- )
-
- device = None
- if index is not None and index != 0xFFFFFFFF:
- device = buildVarDevTable(index)
-
- return default, device
-
- def makeOpenTypeAnchor(self, location, anchor):
- """ast.Anchor --> otTables.Anchor"""
- if anchor is None:
- return None
- variable = False
- deviceX, deviceY = None, None
- if anchor.xDeviceTable is not None:
- deviceX = otl.buildDevice(dict(anchor.xDeviceTable))
- if anchor.yDeviceTable is not None:
- deviceY = otl.buildDevice(dict(anchor.yDeviceTable))
- for dim in ("x", "y"):
- varscalar = getattr(anchor, dim)
- if not isinstance(varscalar, VariableScalar):
- continue
- if getattr(anchor, dim + "DeviceTable") is not None:
- raise FeatureLibError(
- "Can't define a device coordinate and variable scalar", location
- )
- default, device = self.makeVariablePos(location, varscalar)
- setattr(anchor, dim, default)
- if device is not None:
- if dim == "x":
- deviceX = device
- else:
- deviceY = device
- variable = True
-
- otlanchor = otl.buildAnchor(
- anchor.x, anchor.y, anchor.contourpoint, deviceX, deviceY
- )
- if variable:
- otlanchor.Format = 3
- return otlanchor
-
- _VALUEREC_ATTRS = {
- name[0].lower() + name[1:]: (name, isDevice)
- for _, name, isDevice, _ in otBase.valueRecordFormat
- if not name.startswith("Reserved")
- }
-
- def makeOpenTypeValueRecord(self, location, v, pairPosContext):
- """ast.ValueRecord --> otBase.ValueRecord"""
- if not v:
- return None
-
- vr = {}
- for astName, (otName, isDevice) in self._VALUEREC_ATTRS.items():
- val = getattr(v, astName, None)
- if not val:
- continue
- if isDevice:
- vr[otName] = otl.buildDevice(dict(val))
- elif isinstance(val, VariableScalar):
- otDeviceName = otName[0:4] + "Device"
- feaDeviceName = otDeviceName[0].lower() + otDeviceName[1:]
- if getattr(v, feaDeviceName):
- raise FeatureLibError(
- "Can't define a device coordinate and variable scalar", location
- )
- vr[otName], device = self.makeVariablePos(location, val)
- if device is not None:
- vr[otDeviceName] = device
- else:
- vr[otName] = val
-
- if pairPosContext and not vr:
- vr = {"YAdvance": 0} if v.vertical else {"XAdvance": 0}
- valRec = otl.buildValue(vr)
- return valRec
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/error.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/error.py
deleted file mode 100644
index a2c5f9d..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/error.py
+++ /dev/null
@@ -1,22 +0,0 @@
-class FeatureLibError(Exception):
- def __init__(self, message, location):
- Exception.__init__(self, message)
- self.location = location
-
- def __str__(self):
- message = Exception.__str__(self)
- if self.location:
- return f"{self.location}: {message}"
- else:
- return message
-
-
-class IncludedFeaNotFound(FeatureLibError):
- def __str__(self):
- assert self.location is not None
-
- message = (
- "The following feature file should be included but cannot be found: "
- f"{Exception.__str__(self)}"
- )
- return f"{self.location}: {message}"
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/lexer.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/lexer.py
deleted file mode 100644
index e0ae0ae..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/lexer.py
+++ /dev/null
@@ -1,291 +0,0 @@
-from fontTools.feaLib.error import FeatureLibError, IncludedFeaNotFound
-from fontTools.feaLib.location import FeatureLibLocation
-import re
-import os
-
-try:
- import cython
-except ImportError:
- # if cython not installed, use mock module with no-op decorators and types
- from fontTools.misc import cython
-
-
-class Lexer(object):
- NUMBER = "NUMBER"
- HEXADECIMAL = "HEXADECIMAL"
- OCTAL = "OCTAL"
- NUMBERS = (NUMBER, HEXADECIMAL, OCTAL)
- FLOAT = "FLOAT"
- STRING = "STRING"
- NAME = "NAME"
- FILENAME = "FILENAME"
- GLYPHCLASS = "GLYPHCLASS"
- CID = "CID"
- SYMBOL = "SYMBOL"
- COMMENT = "COMMENT"
- NEWLINE = "NEWLINE"
- ANONYMOUS_BLOCK = "ANONYMOUS_BLOCK"
-
- CHAR_WHITESPACE_ = " \t"
- CHAR_NEWLINE_ = "\r\n"
- CHAR_SYMBOL_ = ",;:-+'{}[]<>()="
- CHAR_DIGIT_ = "0123456789"
- CHAR_HEXDIGIT_ = "0123456789ABCDEFabcdef"
- CHAR_LETTER_ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
- CHAR_NAME_START_ = CHAR_LETTER_ + "_+*:.^~!\\"
- CHAR_NAME_CONTINUATION_ = CHAR_LETTER_ + CHAR_DIGIT_ + "_.+*:^~!/-"
-
- RE_GLYPHCLASS = re.compile(r"^[A-Za-z_0-9.\-]+$")
-
- MODE_NORMAL_ = "NORMAL"
- MODE_FILENAME_ = "FILENAME"
-
- def __init__(self, text, filename):
- self.filename_ = filename
- self.line_ = 1
- self.pos_ = 0
- self.line_start_ = 0
- self.text_ = text
- self.text_length_ = len(text)
- self.mode_ = Lexer.MODE_NORMAL_
-
- def __iter__(self):
- return self
-
- def next(self): # Python 2
- return self.__next__()
-
- def __next__(self): # Python 3
- while True:
- token_type, token, location = self.next_()
- if token_type != Lexer.NEWLINE:
- return (token_type, token, location)
-
- def location_(self):
- column = self.pos_ - self.line_start_ + 1
- return FeatureLibLocation(self.filename_ or "", self.line_, column)
-
- def next_(self):
- self.scan_over_(Lexer.CHAR_WHITESPACE_)
- location = self.location_()
- start = self.pos_
- text = self.text_
- limit = len(text)
- if start >= limit:
- raise StopIteration()
- cur_char = text[start]
- next_char = text[start + 1] if start + 1 < limit else None
-
- if cur_char == "\n":
- self.pos_ += 1
- self.line_ += 1
- self.line_start_ = self.pos_
- return (Lexer.NEWLINE, None, location)
- if cur_char == "\r":
- self.pos_ += 2 if next_char == "\n" else 1
- self.line_ += 1
- self.line_start_ = self.pos_
- return (Lexer.NEWLINE, None, location)
- if cur_char == "#":
- self.scan_until_(Lexer.CHAR_NEWLINE_)
- return (Lexer.COMMENT, text[start : self.pos_], location)
-
- if self.mode_ is Lexer.MODE_FILENAME_:
- if cur_char != "(":
- raise FeatureLibError("Expected '(' before file name", location)
- self.scan_until_(")")
- cur_char = text[self.pos_] if self.pos_ < limit else None
- if cur_char != ")":
- raise FeatureLibError("Expected ')' after file name", location)
- self.pos_ += 1
- self.mode_ = Lexer.MODE_NORMAL_
- return (Lexer.FILENAME, text[start + 1 : self.pos_ - 1], location)
-
- if cur_char == "\\" and next_char in Lexer.CHAR_DIGIT_:
- self.pos_ += 1
- self.scan_over_(Lexer.CHAR_DIGIT_)
- return (Lexer.CID, int(text[start + 1 : self.pos_], 10), location)
- if cur_char == "@":
- self.pos_ += 1
- self.scan_over_(Lexer.CHAR_NAME_CONTINUATION_)
- glyphclass = text[start + 1 : self.pos_]
- if len(glyphclass) < 1:
- raise FeatureLibError("Expected glyph class name", location)
- if len(glyphclass) > 63:
- raise FeatureLibError(
- "Glyph class names must not be longer than 63 characters", location
- )
- if not Lexer.RE_GLYPHCLASS.match(glyphclass):
- raise FeatureLibError(
- "Glyph class names must consist of letters, digits, "
- "underscore, period or hyphen",
- location,
- )
- return (Lexer.GLYPHCLASS, glyphclass, location)
- if cur_char in Lexer.CHAR_NAME_START_:
- self.pos_ += 1
- self.scan_over_(Lexer.CHAR_NAME_CONTINUATION_)
- token = text[start : self.pos_]
- if token == "include":
- self.mode_ = Lexer.MODE_FILENAME_
- return (Lexer.NAME, token, location)
- if cur_char == "0" and next_char in "xX":
- self.pos_ += 2
- self.scan_over_(Lexer.CHAR_HEXDIGIT_)
- return (Lexer.HEXADECIMAL, int(text[start : self.pos_], 16), location)
- if cur_char == "0" and next_char in Lexer.CHAR_DIGIT_:
- self.scan_over_(Lexer.CHAR_DIGIT_)
- return (Lexer.OCTAL, int(text[start : self.pos_], 8), location)
- if cur_char in Lexer.CHAR_DIGIT_:
- self.scan_over_(Lexer.CHAR_DIGIT_)
- if self.pos_ >= limit or text[self.pos_] != ".":
- return (Lexer.NUMBER, int(text[start : self.pos_], 10), location)
- self.scan_over_(".")
- self.scan_over_(Lexer.CHAR_DIGIT_)
- return (Lexer.FLOAT, float(text[start : self.pos_]), location)
- if cur_char == "-" and next_char in Lexer.CHAR_DIGIT_:
- self.pos_ += 1
- self.scan_over_(Lexer.CHAR_DIGIT_)
- if self.pos_ >= limit or text[self.pos_] != ".":
- return (Lexer.NUMBER, int(text[start : self.pos_], 10), location)
- self.scan_over_(".")
- self.scan_over_(Lexer.CHAR_DIGIT_)
- return (Lexer.FLOAT, float(text[start : self.pos_]), location)
- if cur_char in Lexer.CHAR_SYMBOL_:
- self.pos_ += 1
- return (Lexer.SYMBOL, cur_char, location)
- if cur_char == '"':
- self.pos_ += 1
- self.scan_until_('"')
- if self.pos_ < self.text_length_ and self.text_[self.pos_] == '"':
- self.pos_ += 1
- # strip newlines embedded within a string
- string = re.sub("[\r\n]", "", text[start + 1 : self.pos_ - 1])
- return (Lexer.STRING, string, location)
- else:
- raise FeatureLibError("Expected '\"' to terminate string", location)
- raise FeatureLibError("Unexpected character: %r" % cur_char, location)
-
- def scan_over_(self, valid):
- p = self.pos_
- while p < self.text_length_ and self.text_[p] in valid:
- p += 1
- self.pos_ = p
-
- def scan_until_(self, stop_at):
- p = self.pos_
- while p < self.text_length_ and self.text_[p] not in stop_at:
- p += 1
- self.pos_ = p
-
- def scan_anonymous_block(self, tag):
- location = self.location_()
- tag = tag.strip()
- self.scan_until_(Lexer.CHAR_NEWLINE_)
- self.scan_over_(Lexer.CHAR_NEWLINE_)
- regexp = r"}\s*" + tag + r"\s*;"
- split = re.split(regexp, self.text_[self.pos_ :], maxsplit=1)
- if len(split) != 2:
- raise FeatureLibError(
- "Expected '} %s;' to terminate anonymous block" % tag, location
- )
- self.pos_ += len(split[0])
- return (Lexer.ANONYMOUS_BLOCK, split[0], location)
-
-
-class IncludingLexer(object):
- """A Lexer that follows include statements.
-
- The OpenType feature file specification states that due to
- historical reasons, relative imports should be resolved in this
- order:
-
- 1. If the source font is UFO format, then relative to the UFO's
- font directory
- 2. relative to the top-level include file
- 3. relative to the parent include file
-
- We only support 1 (via includeDir) and 2.
- """
-
- def __init__(self, featurefile, *, includeDir=None):
- """Initializes an IncludingLexer.
-
- Behavior:
- If includeDir is passed, it will be used to determine the top-level
- include directory to use for all encountered include statements. If it is
- not passed, ``os.path.dirname(featurefile)`` will be considered the
- include directory.
- """
-
- self.lexers_ = [self.make_lexer_(featurefile)]
- self.featurefilepath = self.lexers_[0].filename_
- self.includeDir = includeDir
-
- def __iter__(self):
- return self
-
- def next(self): # Python 2
- return self.__next__()
-
- def __next__(self): # Python 3
- while self.lexers_:
- lexer = self.lexers_[-1]
- try:
- token_type, token, location = next(lexer)
- except StopIteration:
- self.lexers_.pop()
- continue
- if token_type is Lexer.NAME and token == "include":
- fname_type, fname_token, fname_location = lexer.next()
- if fname_type is not Lexer.FILENAME:
- raise FeatureLibError("Expected file name", fname_location)
- # semi_type, semi_token, semi_location = lexer.next()
- # if semi_type is not Lexer.SYMBOL or semi_token != ";":
- # raise FeatureLibError("Expected ';'", semi_location)
- if os.path.isabs(fname_token):
- path = fname_token
- else:
- if self.includeDir is not None:
- curpath = self.includeDir
- elif self.featurefilepath is not None:
- curpath = os.path.dirname(self.featurefilepath)
- else:
- # if the IncludingLexer was initialized from an in-memory
- # file-like stream, it doesn't have a 'name' pointing to
- # its filesystem path, therefore we fall back to using the
- # current working directory to resolve relative includes
- curpath = os.getcwd()
- path = os.path.join(curpath, fname_token)
- if len(self.lexers_) >= 5:
- raise FeatureLibError("Too many recursive includes", fname_location)
- try:
- self.lexers_.append(self.make_lexer_(path))
- except FileNotFoundError as err:
- raise IncludedFeaNotFound(fname_token, fname_location) from err
- else:
- return (token_type, token, location)
- raise StopIteration()
-
- @staticmethod
- def make_lexer_(file_or_path):
- if hasattr(file_or_path, "read"):
- fileobj, closing = file_or_path, False
- else:
- filename, closing = file_or_path, True
- fileobj = open(filename, "r", encoding="utf-8")
- data = fileobj.read()
- filename = getattr(fileobj, "name", None)
- if closing:
- fileobj.close()
- return Lexer(data, filename)
-
- def scan_anonymous_block(self, tag):
- return self.lexers_[-1].scan_anonymous_block(tag)
-
-
-class NonIncludingLexer(IncludingLexer):
- """Lexer that does not follow `include` statements, emits them as-is."""
-
- def __next__(self): # Python 3
- return next(self.lexers_[0])
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/location.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/location.py
deleted file mode 100644
index 50f761d..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/location.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import NamedTuple
-
-
-class FeatureLibLocation(NamedTuple):
- """A location in a feature file"""
-
- file: str
- line: int
- column: int
-
- def __str__(self):
- return f"{self.file}:{self.line}:{self.column}"
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/lookupDebugInfo.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/lookupDebugInfo.py
deleted file mode 100644
index d4da7de..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/lookupDebugInfo.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from typing import NamedTuple
-
-LOOKUP_DEBUG_INFO_KEY = "com.github.fonttools.feaLib"
-LOOKUP_DEBUG_ENV_VAR = "FONTTOOLS_LOOKUP_DEBUGGING"
-
-
-class LookupDebugInfo(NamedTuple):
- """Information about where a lookup came from, to be embedded in a font"""
-
- location: str
- name: str
- feature: list
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/parser.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/parser.py
deleted file mode 100644
index 8ffdf64..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/parser.py
+++ /dev/null
@@ -1,2365 +0,0 @@
-from fontTools.feaLib.error import FeatureLibError
-from fontTools.feaLib.lexer import Lexer, IncludingLexer, NonIncludingLexer
-from fontTools.feaLib.variableScalar import VariableScalar
-from fontTools.misc.encodingTools import getEncoding
-from fontTools.misc.textTools import bytechr, tobytes, tostr
-import fontTools.feaLib.ast as ast
-import logging
-import os
-import re
-
-
-log = logging.getLogger(__name__)
-
-
-class Parser(object):
- """Initializes a Parser object.
-
- Example:
-
- .. code:: python
-
- from fontTools.feaLib.parser import Parser
- parser = Parser(file, font.getReverseGlyphMap())
- parsetree = parser.parse()
-
- Note: the ``glyphNames`` iterable serves a double role to help distinguish
- glyph names from ranges in the presence of hyphens and to ensure that glyph
- names referenced in a feature file are actually part of a font's glyph set.
- If the iterable is left empty, no glyph name in glyph set checking takes
- place, and all glyph tokens containing hyphens are treated as literal glyph
- names, not as ranges. (Adding a space around the hyphen can, in any case,
- help to disambiguate ranges from glyph names containing hyphens.)
-
- By default, the parser will follow ``include()`` statements in the feature
- file. To turn this off, pass ``followIncludes=False``. Pass a directory string as
- ``includeDir`` to explicitly declare a directory to search included feature files
- in.
- """
-
- extensions = {}
- ast = ast
- SS_FEATURE_TAGS = {"ss%02d" % i for i in range(1, 20 + 1)}
- CV_FEATURE_TAGS = {"cv%02d" % i for i in range(1, 99 + 1)}
-
- def __init__(
- self, featurefile, glyphNames=(), followIncludes=True, includeDir=None, **kwargs
- ):
- if "glyphMap" in kwargs:
- from fontTools.misc.loggingTools import deprecateArgument
-
- deprecateArgument("glyphMap", "use 'glyphNames' (iterable) instead")
- if glyphNames:
- raise TypeError(
- "'glyphNames' and (deprecated) 'glyphMap' are " "mutually exclusive"
- )
- glyphNames = kwargs.pop("glyphMap")
- if kwargs:
- raise TypeError(
- "unsupported keyword argument%s: %s"
- % ("" if len(kwargs) == 1 else "s", ", ".join(repr(k) for k in kwargs))
- )
-
- self.glyphNames_ = set(glyphNames)
- self.doc_ = self.ast.FeatureFile()
- self.anchors_ = SymbolTable()
- self.glyphclasses_ = SymbolTable()
- self.lookups_ = SymbolTable()
- self.valuerecords_ = SymbolTable()
- self.symbol_tables_ = {self.anchors_, self.valuerecords_}
- self.next_token_type_, self.next_token_ = (None, None)
- self.cur_comments_ = []
- self.next_token_location_ = None
- lexerClass = IncludingLexer if followIncludes else NonIncludingLexer
- self.lexer_ = lexerClass(featurefile, includeDir=includeDir)
- self.missing = {}
- self.advance_lexer_(comments=True)
-
- def parse(self):
- """Parse the file, and return a :class:`fontTools.feaLib.ast.FeatureFile`
- object representing the root of the abstract syntax tree containing the
- parsed contents of the file."""
- statements = self.doc_.statements
- while self.next_token_type_ is not None or self.cur_comments_:
- self.advance_lexer_(comments=True)
- if self.cur_token_type_ is Lexer.COMMENT:
- statements.append(
- self.ast.Comment(self.cur_token_, location=self.cur_token_location_)
- )
- elif self.is_cur_keyword_("include"):
- statements.append(self.parse_include_())
- elif self.cur_token_type_ is Lexer.GLYPHCLASS:
- statements.append(self.parse_glyphclass_definition_())
- elif self.is_cur_keyword_(("anon", "anonymous")):
- statements.append(self.parse_anonymous_())
- elif self.is_cur_keyword_("anchorDef"):
- statements.append(self.parse_anchordef_())
- elif self.is_cur_keyword_("languagesystem"):
- statements.append(self.parse_languagesystem_())
- elif self.is_cur_keyword_("lookup"):
- statements.append(self.parse_lookup_(vertical=False))
- elif self.is_cur_keyword_("markClass"):
- statements.append(self.parse_markClass_())
- elif self.is_cur_keyword_("feature"):
- statements.append(self.parse_feature_block_())
- elif self.is_cur_keyword_("conditionset"):
- statements.append(self.parse_conditionset_())
- elif self.is_cur_keyword_("variation"):
- statements.append(self.parse_feature_block_(variation=True))
- elif self.is_cur_keyword_("table"):
- statements.append(self.parse_table_())
- elif self.is_cur_keyword_("valueRecordDef"):
- statements.append(self.parse_valuerecord_definition_(vertical=False))
- elif (
- self.cur_token_type_ is Lexer.NAME
- and self.cur_token_ in self.extensions
- ):
- statements.append(self.extensions[self.cur_token_](self))
- elif self.cur_token_type_ is Lexer.SYMBOL and self.cur_token_ == ";":
- continue
- else:
- raise FeatureLibError(
- "Expected feature, languagesystem, lookup, markClass, "
- 'table, or glyph class definition, got {} "{}"'.format(
- self.cur_token_type_, self.cur_token_
- ),
- self.cur_token_location_,
- )
- # Report any missing glyphs at the end of parsing
- if self.missing:
- error = [
- " %s (first found at %s)" % (name, loc)
- for name, loc in self.missing.items()
- ]
- raise FeatureLibError(
- "The following glyph names are referenced but are missing from the "
- "glyph set:\n" + ("\n".join(error)),
- None,
- )
- return self.doc_
-
- def parse_anchor_(self):
- # Parses an anchor in any of the four formats given in the feature
- # file specification (2.e.vii).
- self.expect_symbol_("<")
- self.expect_keyword_("anchor")
- location = self.cur_token_location_
-
- if self.next_token_ == "NULL": # Format D
- self.expect_keyword_("NULL")
- self.expect_symbol_(">")
- return None
-
- if self.next_token_type_ == Lexer.NAME: # Format E
- name = self.expect_name_()
- anchordef = self.anchors_.resolve(name)
- if anchordef is None:
- raise FeatureLibError(
- 'Unknown anchor "%s"' % name, self.cur_token_location_
- )
- self.expect_symbol_(">")
- return self.ast.Anchor(
- anchordef.x,
- anchordef.y,
- name=name,
- contourpoint=anchordef.contourpoint,
- xDeviceTable=None,
- yDeviceTable=None,
- location=location,
- )
-
- x, y = self.expect_number_(variable=True), self.expect_number_(variable=True)
-
- contourpoint = None
- if self.next_token_ == "contourpoint": # Format B
- self.expect_keyword_("contourpoint")
- contourpoint = self.expect_number_()
-
- if self.next_token_ == "<": # Format C
- xDeviceTable = self.parse_device_()
- yDeviceTable = self.parse_device_()
- else:
- xDeviceTable, yDeviceTable = None, None
-
- self.expect_symbol_(">")
- return self.ast.Anchor(
- x,
- y,
- name=None,
- contourpoint=contourpoint,
- xDeviceTable=xDeviceTable,
- yDeviceTable=yDeviceTable,
- location=location,
- )
-
- def parse_anchor_marks_(self):
- # Parses a sequence of ``[ mark @MARKCLASS]*.``
- anchorMarks = [] # [(self.ast.Anchor, markClassName)*]
- while self.next_token_ == "<":
- anchor = self.parse_anchor_()
- if anchor is None and self.next_token_ != "mark":
- continue # without mark, eg. in GPOS type 5
- self.expect_keyword_("mark")
- markClass = self.expect_markClass_reference_()
- anchorMarks.append((anchor, markClass))
- return anchorMarks
-
- def parse_anchordef_(self):
- # Parses a named anchor definition (`section 2.e.viii `_).
- assert self.is_cur_keyword_("anchorDef")
- location = self.cur_token_location_
- x, y = self.expect_number_(), self.expect_number_()
- contourpoint = None
- if self.next_token_ == "contourpoint":
- self.expect_keyword_("contourpoint")
- contourpoint = self.expect_number_()
- name = self.expect_name_()
- self.expect_symbol_(";")
- anchordef = self.ast.AnchorDefinition(
- name, x, y, contourpoint=contourpoint, location=location
- )
- self.anchors_.define(name, anchordef)
- return anchordef
-
- def parse_anonymous_(self):
- # Parses an anonymous data block (`section 10 `_).
- assert self.is_cur_keyword_(("anon", "anonymous"))
- tag = self.expect_tag_()
- _, content, location = self.lexer_.scan_anonymous_block(tag)
- self.advance_lexer_()
- self.expect_symbol_("}")
- end_tag = self.expect_tag_()
- assert tag == end_tag, "bad splitting in Lexer.scan_anonymous_block()"
- self.expect_symbol_(";")
- return self.ast.AnonymousBlock(tag, content, location=location)
-
- def parse_attach_(self):
- # Parses a GDEF Attach statement (`section 9.b `_)
- assert self.is_cur_keyword_("Attach")
- location = self.cur_token_location_
- glyphs = self.parse_glyphclass_(accept_glyphname=True)
- contourPoints = {self.expect_number_()}
- while self.next_token_ != ";":
- contourPoints.add(self.expect_number_())
- self.expect_symbol_(";")
- return self.ast.AttachStatement(glyphs, contourPoints, location=location)
-
- def parse_enumerate_(self, vertical):
- # Parse an enumerated pair positioning rule (`section 6.b.ii `_).
- assert self.cur_token_ in {"enumerate", "enum"}
- self.advance_lexer_()
- return self.parse_position_(enumerated=True, vertical=vertical)
-
- def parse_GlyphClassDef_(self):
- # Parses 'GlyphClassDef @BASE, @LIGATURES, @MARKS, @COMPONENTS;'
- assert self.is_cur_keyword_("GlyphClassDef")
- location = self.cur_token_location_
- if self.next_token_ != ",":
- baseGlyphs = self.parse_glyphclass_(accept_glyphname=False)
- else:
- baseGlyphs = None
- self.expect_symbol_(",")
- if self.next_token_ != ",":
- ligatureGlyphs = self.parse_glyphclass_(accept_glyphname=False)
- else:
- ligatureGlyphs = None
- self.expect_symbol_(",")
- if self.next_token_ != ",":
- markGlyphs = self.parse_glyphclass_(accept_glyphname=False)
- else:
- markGlyphs = None
- self.expect_symbol_(",")
- if self.next_token_ != ";":
- componentGlyphs = self.parse_glyphclass_(accept_glyphname=False)
- else:
- componentGlyphs = None
- self.expect_symbol_(";")
- return self.ast.GlyphClassDefStatement(
- baseGlyphs, markGlyphs, ligatureGlyphs, componentGlyphs, location=location
- )
-
- def parse_glyphclass_definition_(self):
- # Parses glyph class definitions such as '@UPPERCASE = [A-Z];'
- location, name = self.cur_token_location_, self.cur_token_
- self.expect_symbol_("=")
- glyphs = self.parse_glyphclass_(accept_glyphname=False)
- self.expect_symbol_(";")
- glyphclass = self.ast.GlyphClassDefinition(name, glyphs, location=location)
- self.glyphclasses_.define(name, glyphclass)
- return glyphclass
-
- def split_glyph_range_(self, name, location):
- # Since v1.20, the OpenType Feature File specification allows
- # for dashes in glyph names. A sequence like "a-b-c-d" could
- # therefore mean a single glyph whose name happens to be
- # "a-b-c-d", or it could mean a range from glyph "a" to glyph
- # "b-c-d", or a range from glyph "a-b" to glyph "c-d", or a
- # range from glyph "a-b-c" to glyph "d".Technically, this
- # example could be resolved because the (pretty complex)
- # definition of glyph ranges renders most of these splits
- # invalid. But the specification does not say that a compiler
- # should try to apply such fancy heuristics. To encourage
- # unambiguous feature files, we therefore try all possible
- # splits and reject the feature file if there are multiple
- # splits possible. It is intentional that we don't just emit a
- # warning; warnings tend to get ignored. To fix the problem,
- # font designers can trivially add spaces around the intended
- # split point, and we emit a compiler error that suggests
- # how exactly the source should be rewritten to make things
- # unambiguous.
- parts = name.split("-")
- solutions = []
- for i in range(len(parts)):
- start, limit = "-".join(parts[0:i]), "-".join(parts[i:])
- if start in self.glyphNames_ and limit in self.glyphNames_:
- solutions.append((start, limit))
- if len(solutions) == 1:
- start, limit = solutions[0]
- return start, limit
- elif len(solutions) == 0:
- raise FeatureLibError(
- '"%s" is not a glyph in the font, and it can not be split '
- "into a range of known glyphs" % name,
- location,
- )
- else:
- ranges = " or ".join(['"%s - %s"' % (s, l) for s, l in solutions])
- raise FeatureLibError(
- 'Ambiguous glyph range "%s"; '
- "please use %s to clarify what you mean" % (name, ranges),
- location,
- )
-
- def parse_glyphclass_(self, accept_glyphname, accept_null=False):
- # Parses a glyph class, either named or anonymous, or (if
- # ``bool(accept_glyphname)``) a glyph name. If ``bool(accept_null)`` then
- # also accept the special NULL glyph.
- if accept_glyphname and self.next_token_type_ in (Lexer.NAME, Lexer.CID):
- if accept_null and self.next_token_ == "NULL":
- # If you want a glyph called NULL, you should escape it.
- self.advance_lexer_()
- return self.ast.NullGlyph(location=self.cur_token_location_)
- glyph = self.expect_glyph_()
- self.check_glyph_name_in_glyph_set(glyph)
- return self.ast.GlyphName(glyph, location=self.cur_token_location_)
- if self.next_token_type_ is Lexer.GLYPHCLASS:
- self.advance_lexer_()
- gc = self.glyphclasses_.resolve(self.cur_token_)
- if gc is None:
- raise FeatureLibError(
- "Unknown glyph class @%s" % self.cur_token_,
- self.cur_token_location_,
- )
- if isinstance(gc, self.ast.MarkClass):
- return self.ast.MarkClassName(gc, location=self.cur_token_location_)
- else:
- return self.ast.GlyphClassName(gc, location=self.cur_token_location_)
-
- self.expect_symbol_("[")
- location = self.cur_token_location_
- glyphs = self.ast.GlyphClass(location=location)
- while self.next_token_ != "]":
- if self.next_token_type_ is Lexer.NAME:
- glyph = self.expect_glyph_()
- location = self.cur_token_location_
- if "-" in glyph and self.glyphNames_ and glyph not in self.glyphNames_:
- start, limit = self.split_glyph_range_(glyph, location)
- self.check_glyph_name_in_glyph_set(start, limit)
- glyphs.add_range(
- start, limit, self.make_glyph_range_(location, start, limit)
- )
- elif self.next_token_ == "-":
- start = glyph
- self.expect_symbol_("-")
- limit = self.expect_glyph_()
- self.check_glyph_name_in_glyph_set(start, limit)
- glyphs.add_range(
- start, limit, self.make_glyph_range_(location, start, limit)
- )
- else:
- if "-" in glyph and not self.glyphNames_:
- log.warning(
- str(
- FeatureLibError(
- f"Ambiguous glyph name that looks like a range: {glyph!r}",
- location,
- )
- )
- )
- self.check_glyph_name_in_glyph_set(glyph)
- glyphs.append(glyph)
- elif self.next_token_type_ is Lexer.CID:
- glyph = self.expect_glyph_()
- if self.next_token_ == "-":
- range_location = self.cur_token_location_
- range_start = self.cur_token_
- self.expect_symbol_("-")
- range_end = self.expect_cid_()
- self.check_glyph_name_in_glyph_set(
- f"cid{range_start:05d}",
- f"cid{range_end:05d}",
- )
- glyphs.add_cid_range(
- range_start,
- range_end,
- self.make_cid_range_(range_location, range_start, range_end),
- )
- else:
- glyph_name = f"cid{self.cur_token_:05d}"
- self.check_glyph_name_in_glyph_set(glyph_name)
- glyphs.append(glyph_name)
- elif self.next_token_type_ is Lexer.GLYPHCLASS:
- self.advance_lexer_()
- gc = self.glyphclasses_.resolve(self.cur_token_)
- if gc is None:
- raise FeatureLibError(
- "Unknown glyph class @%s" % self.cur_token_,
- self.cur_token_location_,
- )
- if isinstance(gc, self.ast.MarkClass):
- gc = self.ast.MarkClassName(gc, location=self.cur_token_location_)
- else:
- gc = self.ast.GlyphClassName(gc, location=self.cur_token_location_)
- glyphs.add_class(gc)
- else:
- raise FeatureLibError(
- "Expected glyph name, glyph range, "
- f"or glyph class reference, found {self.next_token_!r}",
- self.next_token_location_,
- )
- self.expect_symbol_("]")
- return glyphs
-
- def parse_glyph_pattern_(self, vertical):
- # Parses a glyph pattern, including lookups and context, e.g.::
- #
- # a b
- # a b c' d e
- # a b c' lookup ChangeC d e
- prefix, glyphs, lookups, values, suffix = ([], [], [], [], [])
- hasMarks = False
- while self.next_token_ not in {"by", "from", ";", ","}:
- gc = self.parse_glyphclass_(accept_glyphname=True)
- marked = False
- if self.next_token_ == "'":
- self.expect_symbol_("'")
- hasMarks = marked = True
- if marked:
- if suffix:
- # makeotf also reports this as an error, while FontForge
- # silently inserts ' in all the intervening glyphs.
- # https://github.com/fonttools/fonttools/pull/1096
- raise FeatureLibError(
- "Unsupported contextual target sequence: at most "
- "one run of marked (') glyph/class names allowed",
- self.cur_token_location_,
- )
- glyphs.append(gc)
- elif glyphs:
- suffix.append(gc)
- else:
- prefix.append(gc)
-
- if self.is_next_value_():
- values.append(self.parse_valuerecord_(vertical))
- else:
- values.append(None)
-
- lookuplist = None
- while self.next_token_ == "lookup":
- if lookuplist is None:
- lookuplist = []
- self.expect_keyword_("lookup")
- if not marked:
- raise FeatureLibError(
- "Lookups can only follow marked glyphs",
- self.cur_token_location_,
- )
- lookup_name = self.expect_name_()
- lookup = self.lookups_.resolve(lookup_name)
- if lookup is None:
- raise FeatureLibError(
- 'Unknown lookup "%s"' % lookup_name, self.cur_token_location_
- )
- lookuplist.append(lookup)
- if marked:
- lookups.append(lookuplist)
-
- if not glyphs and not suffix: # eg., "sub f f i by"
- assert lookups == []
- return ([], prefix, [None] * len(prefix), values, [], hasMarks)
- else:
- if any(values[: len(prefix)]):
- raise FeatureLibError(
- "Positioning cannot be applied in the bactrack glyph sequence, "
- "before the marked glyph sequence.",
- self.cur_token_location_,
- )
- marked_values = values[len(prefix) : len(prefix) + len(glyphs)]
- if any(marked_values):
- if any(values[len(prefix) + len(glyphs) :]):
- raise FeatureLibError(
- "Positioning values are allowed only in the marked glyph "
- "sequence, or after the final glyph node when only one glyph "
- "node is marked.",
- self.cur_token_location_,
- )
- values = marked_values
- elif values and values[-1]:
- if len(glyphs) > 1 or any(values[:-1]):
- raise FeatureLibError(
- "Positioning values are allowed only in the marked glyph "
- "sequence, or after the final glyph node when only one glyph "
- "node is marked.",
- self.cur_token_location_,
- )
- values = values[-1:]
- elif any(values):
- raise FeatureLibError(
- "Positioning values are allowed only in the marked glyph "
- "sequence, or after the final glyph node when only one glyph "
- "node is marked.",
- self.cur_token_location_,
- )
- return (prefix, glyphs, lookups, values, suffix, hasMarks)
-
- def parse_ignore_glyph_pattern_(self, sub):
- location = self.cur_token_location_
- prefix, glyphs, lookups, values, suffix, hasMarks = self.parse_glyph_pattern_(
- vertical=False
- )
- if any(lookups):
- raise FeatureLibError(
- f'No lookups can be specified for "ignore {sub}"', location
- )
- if not hasMarks:
- error = FeatureLibError(
- f'Ambiguous "ignore {sub}", there should be least one marked glyph',
- location,
- )
- log.warning(str(error))
- suffix, glyphs = glyphs[1:], glyphs[0:1]
- chainContext = (prefix, glyphs, suffix)
- return chainContext
-
- def parse_ignore_context_(self, sub):
- location = self.cur_token_location_
- chainContext = [self.parse_ignore_glyph_pattern_(sub)]
- while self.next_token_ == ",":
- self.expect_symbol_(",")
- chainContext.append(self.parse_ignore_glyph_pattern_(sub))
- self.expect_symbol_(";")
- return chainContext
-
- def parse_ignore_(self):
- # Parses an ignore sub/pos rule.
- assert self.is_cur_keyword_("ignore")
- location = self.cur_token_location_
- self.advance_lexer_()
- if self.cur_token_ in ["substitute", "sub"]:
- chainContext = self.parse_ignore_context_("sub")
- return self.ast.IgnoreSubstStatement(chainContext, location=location)
- if self.cur_token_ in ["position", "pos"]:
- chainContext = self.parse_ignore_context_("pos")
- return self.ast.IgnorePosStatement(chainContext, location=location)
- raise FeatureLibError(
- 'Expected "substitute" or "position"', self.cur_token_location_
- )
-
- def parse_include_(self):
- assert self.cur_token_ == "include"
- location = self.cur_token_location_
- filename = self.expect_filename_()
- # self.expect_symbol_(";")
- return ast.IncludeStatement(filename, location=location)
-
- def parse_language_(self):
- assert self.is_cur_keyword_("language")
- location = self.cur_token_location_
- language = self.expect_language_tag_()
- include_default, required = (True, False)
- if self.next_token_ in {"exclude_dflt", "include_dflt"}:
- include_default = self.expect_name_() == "include_dflt"
- if self.next_token_ == "required":
- self.expect_keyword_("required")
- required = True
- self.expect_symbol_(";")
- return self.ast.LanguageStatement(
- language, include_default, required, location=location
- )
-
- def parse_ligatureCaretByIndex_(self):
- assert self.is_cur_keyword_("LigatureCaretByIndex")
- location = self.cur_token_location_
- glyphs = self.parse_glyphclass_(accept_glyphname=True)
- carets = [self.expect_number_()]
- while self.next_token_ != ";":
- carets.append(self.expect_number_())
- self.expect_symbol_(";")
- return self.ast.LigatureCaretByIndexStatement(glyphs, carets, location=location)
-
- def parse_ligatureCaretByPos_(self):
- assert self.is_cur_keyword_("LigatureCaretByPos")
- location = self.cur_token_location_
- glyphs = self.parse_glyphclass_(accept_glyphname=True)
- carets = [self.expect_number_(variable=True)]
- while self.next_token_ != ";":
- carets.append(self.expect_number_(variable=True))
- self.expect_symbol_(";")
- return self.ast.LigatureCaretByPosStatement(glyphs, carets, location=location)
-
- def parse_lookup_(self, vertical):
- # Parses a ``lookup`` - either a lookup block, or a lookup reference
- # inside a feature.
- assert self.is_cur_keyword_("lookup")
- location, name = self.cur_token_location_, self.expect_name_()
-
- if self.next_token_ == ";":
- lookup = self.lookups_.resolve(name)
- if lookup is None:
- raise FeatureLibError(
- 'Unknown lookup "%s"' % name, self.cur_token_location_
- )
- self.expect_symbol_(";")
- return self.ast.LookupReferenceStatement(lookup, location=location)
-
- use_extension = False
- if self.next_token_ == "useExtension":
- self.expect_keyword_("useExtension")
- use_extension = True
-
- block = self.ast.LookupBlock(name, use_extension, location=location)
- self.parse_block_(block, vertical)
- self.lookups_.define(name, block)
- return block
-
- def parse_lookupflag_(self):
- # Parses a ``lookupflag`` statement, either specified by number or
- # in words.
- assert self.is_cur_keyword_("lookupflag")
- location = self.cur_token_location_
-
- # format B: "lookupflag 6;"
- if self.next_token_type_ == Lexer.NUMBER:
- value = self.expect_number_()
- self.expect_symbol_(";")
- return self.ast.LookupFlagStatement(value, location=location)
-
- # format A: "lookupflag RightToLeft MarkAttachmentType @M;"
- value_seen = False
- value, markAttachment, markFilteringSet = 0, None, None
- flags = {
- "RightToLeft": 1,
- "IgnoreBaseGlyphs": 2,
- "IgnoreLigatures": 4,
- "IgnoreMarks": 8,
- }
- seen = set()
- while self.next_token_ != ";":
- if self.next_token_ in seen:
- raise FeatureLibError(
- "%s can be specified only once" % self.next_token_,
- self.next_token_location_,
- )
- seen.add(self.next_token_)
- if self.next_token_ == "MarkAttachmentType":
- self.expect_keyword_("MarkAttachmentType")
- markAttachment = self.parse_glyphclass_(accept_glyphname=False)
- elif self.next_token_ == "UseMarkFilteringSet":
- self.expect_keyword_("UseMarkFilteringSet")
- markFilteringSet = self.parse_glyphclass_(accept_glyphname=False)
- elif self.next_token_ in flags:
- value_seen = True
- value = value | flags[self.expect_name_()]
- else:
- raise FeatureLibError(
- '"%s" is not a recognized lookupflag' % self.next_token_,
- self.next_token_location_,
- )
- self.expect_symbol_(";")
-
- if not any([value_seen, markAttachment, markFilteringSet]):
- raise FeatureLibError(
- "lookupflag must have a value", self.next_token_location_
- )
-
- return self.ast.LookupFlagStatement(
- value,
- markAttachment=markAttachment,
- markFilteringSet=markFilteringSet,
- location=location,
- )
-
- def parse_markClass_(self):
- assert self.is_cur_keyword_("markClass")
- location = self.cur_token_location_
- glyphs = self.parse_glyphclass_(accept_glyphname=True)
- if not glyphs.glyphSet():
- raise FeatureLibError(
- "Empty glyph class in mark class definition", location
- )
- anchor = self.parse_anchor_()
- name = self.expect_class_name_()
- self.expect_symbol_(";")
- markClass = self.doc_.markClasses.get(name)
- if markClass is None:
- markClass = self.ast.MarkClass(name)
- self.doc_.markClasses[name] = markClass
- self.glyphclasses_.define(name, markClass)
- mcdef = self.ast.MarkClassDefinition(
- markClass, anchor, glyphs, location=location
- )
- markClass.addDefinition(mcdef)
- return mcdef
-
- def parse_position_(self, enumerated, vertical):
- assert self.cur_token_ in {"position", "pos"}
- if self.next_token_ == "cursive": # GPOS type 3
- return self.parse_position_cursive_(enumerated, vertical)
- elif self.next_token_ == "base": # GPOS type 4
- return self.parse_position_base_(enumerated, vertical)
- elif self.next_token_ == "ligature": # GPOS type 5
- return self.parse_position_ligature_(enumerated, vertical)
- elif self.next_token_ == "mark": # GPOS type 6
- return self.parse_position_mark_(enumerated, vertical)
-
- location = self.cur_token_location_
- prefix, glyphs, lookups, values, suffix, hasMarks = self.parse_glyph_pattern_(
- vertical
- )
- self.expect_symbol_(";")
-
- if any(lookups):
- # GPOS type 8: Chaining contextual positioning; explicit lookups
- if any(values):
- raise FeatureLibError(
- 'If "lookup" is present, no values must be specified', location
- )
- return self.ast.ChainContextPosStatement(
- prefix, glyphs, suffix, lookups, location=location
- )
-
- # Pair positioning, format A: "pos V 10 A -10;"
- # Pair positioning, format B: "pos V A -20;"
- if not prefix and not suffix and len(glyphs) == 2 and not hasMarks:
- if values[0] is None: # Format B: "pos V A -20;"
- values.reverse()
- return self.ast.PairPosStatement(
- glyphs[0],
- values[0],
- glyphs[1],
- values[1],
- enumerated=enumerated,
- location=location,
- )
-
- if enumerated:
- raise FeatureLibError(
- '"enumerate" is only allowed with pair positionings', location
- )
- return self.ast.SinglePosStatement(
- list(zip(glyphs, values)),
- prefix,
- suffix,
- forceChain=hasMarks,
- location=location,
- )
-
- def parse_position_cursive_(self, enumerated, vertical):
- location = self.cur_token_location_
- self.expect_keyword_("cursive")
- if enumerated:
- raise FeatureLibError(
- '"enumerate" is not allowed with ' "cursive attachment positioning",
- location,
- )
- glyphclass = self.parse_glyphclass_(accept_glyphname=True)
- entryAnchor = self.parse_anchor_()
- exitAnchor = self.parse_anchor_()
- self.expect_symbol_(";")
- return self.ast.CursivePosStatement(
- glyphclass, entryAnchor, exitAnchor, location=location
- )
-
- def parse_position_base_(self, enumerated, vertical):
- location = self.cur_token_location_
- self.expect_keyword_("base")
- if enumerated:
- raise FeatureLibError(
- '"enumerate" is not allowed with '
- "mark-to-base attachment positioning",
- location,
- )
- base = self.parse_glyphclass_(accept_glyphname=True)
- marks = self.parse_anchor_marks_()
- self.expect_symbol_(";")
- return self.ast.MarkBasePosStatement(base, marks, location=location)
-
- def parse_position_ligature_(self, enumerated, vertical):
- location = self.cur_token_location_
- self.expect_keyword_("ligature")
- if enumerated:
- raise FeatureLibError(
- '"enumerate" is not allowed with '
- "mark-to-ligature attachment positioning",
- location,
- )
- ligatures = self.parse_glyphclass_(accept_glyphname=True)
- marks = [self.parse_anchor_marks_()]
- while self.next_token_ == "ligComponent":
- self.expect_keyword_("ligComponent")
- marks.append(self.parse_anchor_marks_())
- self.expect_symbol_(";")
- return self.ast.MarkLigPosStatement(ligatures, marks, location=location)
-
- def parse_position_mark_(self, enumerated, vertical):
- location = self.cur_token_location_
- self.expect_keyword_("mark")
- if enumerated:
- raise FeatureLibError(
- '"enumerate" is not allowed with '
- "mark-to-mark attachment positioning",
- location,
- )
- baseMarks = self.parse_glyphclass_(accept_glyphname=True)
- marks = self.parse_anchor_marks_()
- self.expect_symbol_(";")
- return self.ast.MarkMarkPosStatement(baseMarks, marks, location=location)
-
- def parse_script_(self):
- assert self.is_cur_keyword_("script")
- location, script = self.cur_token_location_, self.expect_script_tag_()
- self.expect_symbol_(";")
- return self.ast.ScriptStatement(script, location=location)
-
- def parse_substitute_(self):
- assert self.cur_token_ in {"substitute", "sub", "reversesub", "rsub"}
- location = self.cur_token_location_
- reverse = self.cur_token_ in {"reversesub", "rsub"}
- (
- old_prefix,
- old,
- lookups,
- values,
- old_suffix,
- hasMarks,
- ) = self.parse_glyph_pattern_(vertical=False)
- if any(values):
- raise FeatureLibError(
- "Substitution statements cannot contain values", location
- )
- new = []
- if self.next_token_ == "by":
- keyword = self.expect_keyword_("by")
- while self.next_token_ != ";":
- gc = self.parse_glyphclass_(accept_glyphname=True, accept_null=True)
- new.append(gc)
- elif self.next_token_ == "from":
- keyword = self.expect_keyword_("from")
- new = [self.parse_glyphclass_(accept_glyphname=False)]
- else:
- keyword = None
- self.expect_symbol_(";")
- if len(new) == 0 and not any(lookups):
- raise FeatureLibError(
- 'Expected "by", "from" or explicit lookup references',
- self.cur_token_location_,
- )
-
- # GSUB lookup type 3: Alternate substitution.
- # Format: "substitute a from [a.1 a.2 a.3];"
- if keyword == "from":
- if reverse:
- raise FeatureLibError(
- 'Reverse chaining substitutions do not support "from"', location
- )
- if len(old) != 1 or len(old[0].glyphSet()) != 1:
- raise FeatureLibError('Expected a single glyph before "from"', location)
- if len(new) != 1:
- raise FeatureLibError(
- 'Expected a single glyphclass after "from"', location
- )
- return self.ast.AlternateSubstStatement(
- old_prefix, old[0], old_suffix, new[0], location=location
- )
-
- num_lookups = len([l for l in lookups if l is not None])
-
- is_deletion = False
- if len(new) == 1 and isinstance(new[0], ast.NullGlyph):
- new = [] # Deletion
- is_deletion = True
-
- # GSUB lookup type 1: Single substitution.
- # Format A: "substitute a by a.sc;"
- # Format B: "substitute [one.fitted one.oldstyle] by one;"
- # Format C: "substitute [a-d] by [A.sc-D.sc];"
- if not reverse and len(old) == 1 and len(new) == 1 and num_lookups == 0:
- glyphs = list(old[0].glyphSet())
- replacements = list(new[0].glyphSet())
- if len(replacements) == 1:
- replacements = replacements * len(glyphs)
- if len(glyphs) != len(replacements):
- raise FeatureLibError(
- 'Expected a glyph class with %d elements after "by", '
- "but found a glyph class with %d elements"
- % (len(glyphs), len(replacements)),
- location,
- )
- return self.ast.SingleSubstStatement(
- old, new, old_prefix, old_suffix, forceChain=hasMarks, location=location
- )
-
- # Glyph deletion, built as GSUB lookup type 2: Multiple substitution
- # with empty replacement.
- if is_deletion and len(old) == 1 and num_lookups == 0:
- return self.ast.MultipleSubstStatement(
- old_prefix,
- old[0],
- old_suffix,
- (),
- forceChain=hasMarks,
- location=location,
- )
-
- # GSUB lookup type 2: Multiple substitution.
- # Format: "substitute f_f_i by f f i;"
- #
- # GlyphsApp introduces two additional formats:
- # Format 1: "substitute [f_i f_l] by [f f] [i l];"
- # Format 2: "substitute [f_i f_l] by f [i l];"
- # http://handbook.glyphsapp.com/en/layout/multiple-substitution-with-classes/
- if not reverse and len(old) == 1 and len(new) > 1 and num_lookups == 0:
- count = len(old[0].glyphSet())
- for n in new:
- if not list(n.glyphSet()):
- raise FeatureLibError("Empty class in replacement", location)
- if len(n.glyphSet()) != 1 and len(n.glyphSet()) != count:
- raise FeatureLibError(
- f'Expected a glyph class with 1 or {count} elements after "by", '
- f"but found a glyph class with {len(n.glyphSet())} elements",
- location,
- )
- return self.ast.MultipleSubstStatement(
- old_prefix,
- old[0],
- old_suffix,
- new,
- forceChain=hasMarks,
- location=location,
- )
-
- # GSUB lookup type 4: Ligature substitution.
- # Format: "substitute f f i by f_f_i;"
- if (
- not reverse
- and len(old) > 1
- and len(new) == 1
- and len(new[0].glyphSet()) == 1
- and num_lookups == 0
- ):
- return self.ast.LigatureSubstStatement(
- old_prefix,
- old,
- old_suffix,
- list(new[0].glyphSet())[0],
- forceChain=hasMarks,
- location=location,
- )
-
- # GSUB lookup type 8: Reverse chaining substitution.
- if reverse:
- if len(old) != 1:
- raise FeatureLibError(
- "In reverse chaining single substitutions, "
- "only a single glyph or glyph class can be replaced",
- location,
- )
- if len(new) != 1:
- raise FeatureLibError(
- "In reverse chaining single substitutions, "
- 'the replacement (after "by") must be a single glyph '
- "or glyph class",
- location,
- )
- if num_lookups != 0:
- raise FeatureLibError(
- "Reverse chaining substitutions cannot call named lookups", location
- )
- glyphs = sorted(list(old[0].glyphSet()))
- replacements = sorted(list(new[0].glyphSet()))
- if len(replacements) == 1:
- replacements = replacements * len(glyphs)
- if len(glyphs) != len(replacements):
- raise FeatureLibError(
- 'Expected a glyph class with %d elements after "by", '
- "but found a glyph class with %d elements"
- % (len(glyphs), len(replacements)),
- location,
- )
- return self.ast.ReverseChainSingleSubstStatement(
- old_prefix, old_suffix, old, new, location=location
- )
-
- if len(old) > 1 and len(new) > 1:
- raise FeatureLibError(
- "Direct substitution of multiple glyphs by multiple glyphs "
- "is not supported",
- location,
- )
-
- # If there are remaining glyphs to parse, this is an invalid GSUB statement
- if len(new) != 0 or is_deletion:
- raise FeatureLibError("Invalid substitution statement", location)
-
- # GSUB lookup type 6: Chaining contextual substitution.
- rule = self.ast.ChainContextSubstStatement(
- old_prefix, old, old_suffix, lookups, location=location
- )
- return rule
-
- def parse_subtable_(self):
- assert self.is_cur_keyword_("subtable")
- location = self.cur_token_location_
- self.expect_symbol_(";")
- return self.ast.SubtableStatement(location=location)
-
- def parse_size_parameters_(self):
- # Parses a ``parameters`` statement used in ``size`` features. See
- # `section 8.b `_.
- assert self.is_cur_keyword_("parameters")
- location = self.cur_token_location_
- DesignSize = self.expect_decipoint_()
- SubfamilyID = self.expect_number_()
- RangeStart = 0.0
- RangeEnd = 0.0
- if self.next_token_type_ in (Lexer.NUMBER, Lexer.FLOAT) or SubfamilyID != 0:
- RangeStart = self.expect_decipoint_()
- RangeEnd = self.expect_decipoint_()
-
- self.expect_symbol_(";")
- return self.ast.SizeParameters(
- DesignSize, SubfamilyID, RangeStart, RangeEnd, location=location
- )
-
- def parse_size_menuname_(self):
- assert self.is_cur_keyword_("sizemenuname")
- location = self.cur_token_location_
- platformID, platEncID, langID, string = self.parse_name_()
- return self.ast.FeatureNameStatement(
- "size", platformID, platEncID, langID, string, location=location
- )
-
- def parse_table_(self):
- assert self.is_cur_keyword_("table")
- location, name = self.cur_token_location_, self.expect_tag_()
- table = self.ast.TableBlock(name, location=location)
- self.expect_symbol_("{")
- handler = {
- "GDEF": self.parse_table_GDEF_,
- "head": self.parse_table_head_,
- "hhea": self.parse_table_hhea_,
- "vhea": self.parse_table_vhea_,
- "name": self.parse_table_name_,
- "BASE": self.parse_table_BASE_,
- "OS/2": self.parse_table_OS_2_,
- "STAT": self.parse_table_STAT_,
- }.get(name)
- if handler:
- handler(table)
- else:
- raise FeatureLibError(
- '"table %s" is not supported' % name.strip(), location
- )
- self.expect_symbol_("}")
- end_tag = self.expect_tag_()
- if end_tag != name:
- raise FeatureLibError(
- 'Expected "%s"' % name.strip(), self.cur_token_location_
- )
- self.expect_symbol_(";")
- return table
-
- def parse_table_GDEF_(self, table):
- statements = table.statements
- while self.next_token_ != "}" or self.cur_comments_:
- self.advance_lexer_(comments=True)
- if self.cur_token_type_ is Lexer.COMMENT:
- statements.append(
- self.ast.Comment(self.cur_token_, location=self.cur_token_location_)
- )
- elif self.is_cur_keyword_("Attach"):
- statements.append(self.parse_attach_())
- elif self.is_cur_keyword_("GlyphClassDef"):
- statements.append(self.parse_GlyphClassDef_())
- elif self.is_cur_keyword_("LigatureCaretByIndex"):
- statements.append(self.parse_ligatureCaretByIndex_())
- elif self.is_cur_keyword_("LigatureCaretByPos"):
- statements.append(self.parse_ligatureCaretByPos_())
- elif self.cur_token_ == ";":
- continue
- else:
- raise FeatureLibError(
- "Expected Attach, LigatureCaretByIndex, " "or LigatureCaretByPos",
- self.cur_token_location_,
- )
-
- def parse_table_head_(self, table):
- statements = table.statements
- while self.next_token_ != "}" or self.cur_comments_:
- self.advance_lexer_(comments=True)
- if self.cur_token_type_ is Lexer.COMMENT:
- statements.append(
- self.ast.Comment(self.cur_token_, location=self.cur_token_location_)
- )
- elif self.is_cur_keyword_("FontRevision"):
- statements.append(self.parse_FontRevision_())
- elif self.cur_token_ == ";":
- continue
- else:
- raise FeatureLibError("Expected FontRevision", self.cur_token_location_)
-
- def parse_table_hhea_(self, table):
- statements = table.statements
- fields = ("CaretOffset", "Ascender", "Descender", "LineGap")
- while self.next_token_ != "}" or self.cur_comments_:
- self.advance_lexer_(comments=True)
- if self.cur_token_type_ is Lexer.COMMENT:
- statements.append(
- self.ast.Comment(self.cur_token_, location=self.cur_token_location_)
- )
- elif self.cur_token_type_ is Lexer.NAME and self.cur_token_ in fields:
- key = self.cur_token_.lower()
- value = self.expect_number_()
- statements.append(
- self.ast.HheaField(key, value, location=self.cur_token_location_)
- )
- if self.next_token_ != ";":
- raise FeatureLibError(
- "Incomplete statement", self.next_token_location_
- )
- elif self.cur_token_ == ";":
- continue
- else:
- raise FeatureLibError(
- "Expected CaretOffset, Ascender, " "Descender or LineGap",
- self.cur_token_location_,
- )
-
- def parse_table_vhea_(self, table):
- statements = table.statements
- fields = ("VertTypoAscender", "VertTypoDescender", "VertTypoLineGap")
- while self.next_token_ != "}" or self.cur_comments_:
- self.advance_lexer_(comments=True)
- if self.cur_token_type_ is Lexer.COMMENT:
- statements.append(
- self.ast.Comment(self.cur_token_, location=self.cur_token_location_)
- )
- elif self.cur_token_type_ is Lexer.NAME and self.cur_token_ in fields:
- key = self.cur_token_.lower()
- value = self.expect_number_()
- statements.append(
- self.ast.VheaField(key, value, location=self.cur_token_location_)
- )
- if self.next_token_ != ";":
- raise FeatureLibError(
- "Incomplete statement", self.next_token_location_
- )
- elif self.cur_token_ == ";":
- continue
- else:
- raise FeatureLibError(
- "Expected VertTypoAscender, "
- "VertTypoDescender or VertTypoLineGap",
- self.cur_token_location_,
- )
-
- def parse_table_name_(self, table):
- statements = table.statements
- while self.next_token_ != "}" or self.cur_comments_:
- self.advance_lexer_(comments=True)
- if self.cur_token_type_ is Lexer.COMMENT:
- statements.append(
- self.ast.Comment(self.cur_token_, location=self.cur_token_location_)
- )
- elif self.is_cur_keyword_("nameid"):
- statement = self.parse_nameid_()
- if statement:
- statements.append(statement)
- elif self.cur_token_ == ";":
- continue
- else:
- raise FeatureLibError("Expected nameid", self.cur_token_location_)
-
- def parse_name_(self):
- """Parses a name record. See `section 9.e `_."""
- platEncID = None
- langID = None
- if self.next_token_type_ in Lexer.NUMBERS:
- platformID = self.expect_any_number_()
- location = self.cur_token_location_
- if platformID not in (1, 3):
- raise FeatureLibError("Expected platform id 1 or 3", location)
- if self.next_token_type_ in Lexer.NUMBERS:
- platEncID = self.expect_any_number_()
- langID = self.expect_any_number_()
- else:
- platformID = 3
- location = self.cur_token_location_
-
- if platformID == 1: # Macintosh
- platEncID = platEncID or 0 # Roman
- langID = langID or 0 # English
- else: # 3, Windows
- platEncID = platEncID or 1 # Unicode
- langID = langID or 0x0409 # English
-
- string = self.expect_string_()
- self.expect_symbol_(";")
-
- encoding = getEncoding(platformID, platEncID, langID)
- if encoding is None:
- raise FeatureLibError("Unsupported encoding", location)
- unescaped = self.unescape_string_(string, encoding)
- return platformID, platEncID, langID, unescaped
-
- def parse_stat_name_(self):
- platEncID = None
- langID = None
- if self.next_token_type_ in Lexer.NUMBERS:
- platformID = self.expect_any_number_()
- location = self.cur_token_location_
- if platformID not in (1, 3):
- raise FeatureLibError("Expected platform id 1 or 3", location)
- if self.next_token_type_ in Lexer.NUMBERS:
- platEncID = self.expect_any_number_()
- langID = self.expect_any_number_()
- else:
- platformID = 3
- location = self.cur_token_location_
-
- if platformID == 1: # Macintosh
- platEncID = platEncID or 0 # Roman
- langID = langID or 0 # English
- else: # 3, Windows
- platEncID = platEncID or 1 # Unicode
- langID = langID or 0x0409 # English
-
- string = self.expect_string_()
- encoding = getEncoding(platformID, platEncID, langID)
- if encoding is None:
- raise FeatureLibError("Unsupported encoding", location)
- unescaped = self.unescape_string_(string, encoding)
- return platformID, platEncID, langID, unescaped
-
- def parse_nameid_(self):
- assert self.cur_token_ == "nameid", self.cur_token_
- location, nameID = self.cur_token_location_, self.expect_any_number_()
- if nameID > 32767:
- raise FeatureLibError(
- "Name id value cannot be greater than 32767", self.cur_token_location_
- )
- platformID, platEncID, langID, string = self.parse_name_()
- return self.ast.NameRecord(
- nameID, platformID, platEncID, langID, string, location=location
- )
-
- def unescape_string_(self, string, encoding):
- if encoding == "utf_16_be":
- s = re.sub(r"\\[0-9a-fA-F]{4}", self.unescape_unichr_, string)
- else:
- unescape = lambda m: self.unescape_byte_(m, encoding)
- s = re.sub(r"\\[0-9a-fA-F]{2}", unescape, string)
- # We now have a Unicode string, but it might contain surrogate pairs.
- # We convert surrogates to actual Unicode by round-tripping through
- # Python's UTF-16 codec in a special mode.
- utf16 = tobytes(s, "utf_16_be", "surrogatepass")
- return tostr(utf16, "utf_16_be")
-
- @staticmethod
- def unescape_unichr_(match):
- n = match.group(0)[1:]
- return chr(int(n, 16))
-
- @staticmethod
- def unescape_byte_(match, encoding):
- n = match.group(0)[1:]
- return bytechr(int(n, 16)).decode(encoding)
-
- def parse_table_BASE_(self, table):
- statements = table.statements
- while self.next_token_ != "}" or self.cur_comments_:
- self.advance_lexer_(comments=True)
- if self.cur_token_type_ is Lexer.COMMENT:
- statements.append(
- self.ast.Comment(self.cur_token_, location=self.cur_token_location_)
- )
- elif self.is_cur_keyword_("HorizAxis.BaseTagList"):
- horiz_bases = self.parse_base_tag_list_()
- elif self.is_cur_keyword_("HorizAxis.BaseScriptList"):
- horiz_scripts = self.parse_base_script_list_(len(horiz_bases))
- statements.append(
- self.ast.BaseAxis(
- horiz_bases,
- horiz_scripts,
- False,
- location=self.cur_token_location_,
- )
- )
- elif self.is_cur_keyword_("VertAxis.BaseTagList"):
- vert_bases = self.parse_base_tag_list_()
- elif self.is_cur_keyword_("VertAxis.BaseScriptList"):
- vert_scripts = self.parse_base_script_list_(len(vert_bases))
- statements.append(
- self.ast.BaseAxis(
- vert_bases,
- vert_scripts,
- True,
- location=self.cur_token_location_,
- )
- )
- elif self.cur_token_ == ";":
- continue
-
- def parse_table_OS_2_(self, table):
- statements = table.statements
- numbers = (
- "FSType",
- "TypoAscender",
- "TypoDescender",
- "TypoLineGap",
- "winAscent",
- "winDescent",
- "XHeight",
- "CapHeight",
- "WeightClass",
- "WidthClass",
- "LowerOpSize",
- "UpperOpSize",
- )
- ranges = ("UnicodeRange", "CodePageRange")
- while self.next_token_ != "}" or self.cur_comments_:
- self.advance_lexer_(comments=True)
- if self.cur_token_type_ is Lexer.COMMENT:
- statements.append(
- self.ast.Comment(self.cur_token_, location=self.cur_token_location_)
- )
- elif self.cur_token_type_ is Lexer.NAME:
- key = self.cur_token_.lower()
- value = None
- if self.cur_token_ in numbers:
- value = self.expect_number_()
- elif self.is_cur_keyword_("Panose"):
- value = []
- for i in range(10):
- value.append(self.expect_number_())
- elif self.cur_token_ in ranges:
- value = []
- while self.next_token_ != ";":
- value.append(self.expect_number_())
- elif self.is_cur_keyword_("Vendor"):
- value = self.expect_string_()
- statements.append(
- self.ast.OS2Field(key, value, location=self.cur_token_location_)
- )
- elif self.cur_token_ == ";":
- continue
-
- def parse_STAT_ElidedFallbackName(self):
- assert self.is_cur_keyword_("ElidedFallbackName")
- self.expect_symbol_("{")
- names = []
- while self.next_token_ != "}" or self.cur_comments_:
- self.advance_lexer_()
- if self.is_cur_keyword_("name"):
- platformID, platEncID, langID, string = self.parse_stat_name_()
- nameRecord = self.ast.STATNameStatement(
- "stat",
- platformID,
- platEncID,
- langID,
- string,
- location=self.cur_token_location_,
- )
- names.append(nameRecord)
- else:
- if self.cur_token_ != ";":
- raise FeatureLibError(
- f"Unexpected token {self.cur_token_} " f"in ElidedFallbackName",
- self.cur_token_location_,
- )
- self.expect_symbol_("}")
- if not names:
- raise FeatureLibError('Expected "name"', self.cur_token_location_)
- return names
-
- def parse_STAT_design_axis(self):
- assert self.is_cur_keyword_("DesignAxis")
- names = []
- axisTag = self.expect_tag_()
- if (
- axisTag not in ("ital", "opsz", "slnt", "wdth", "wght")
- and not axisTag.isupper()
- ):
- log.warning(f"Unregistered axis tag {axisTag} should be uppercase.")
- axisOrder = self.expect_number_()
- self.expect_symbol_("{")
- while self.next_token_ != "}" or self.cur_comments_:
- self.advance_lexer_()
- if self.cur_token_type_ is Lexer.COMMENT:
- continue
- elif self.is_cur_keyword_("name"):
- location = self.cur_token_location_
- platformID, platEncID, langID, string = self.parse_stat_name_()
- name = self.ast.STATNameStatement(
- "stat", platformID, platEncID, langID, string, location=location
- )
- names.append(name)
- elif self.cur_token_ == ";":
- continue
- else:
- raise FeatureLibError(
- f'Expected "name", got {self.cur_token_}', self.cur_token_location_
- )
-
- self.expect_symbol_("}")
- return self.ast.STATDesignAxisStatement(
- axisTag, axisOrder, names, self.cur_token_location_
- )
-
- def parse_STAT_axis_value_(self):
- assert self.is_cur_keyword_("AxisValue")
- self.expect_symbol_("{")
- locations = []
- names = []
- flags = 0
- while self.next_token_ != "}" or self.cur_comments_:
- self.advance_lexer_(comments=True)
- if self.cur_token_type_ is Lexer.COMMENT:
- continue
- elif self.is_cur_keyword_("name"):
- location = self.cur_token_location_
- platformID, platEncID, langID, string = self.parse_stat_name_()
- name = self.ast.STATNameStatement(
- "stat", platformID, platEncID, langID, string, location=location
- )
- names.append(name)
- elif self.is_cur_keyword_("location"):
- location = self.parse_STAT_location()
- locations.append(location)
- elif self.is_cur_keyword_("flag"):
- flags = self.expect_stat_flags()
- elif self.cur_token_ == ";":
- continue
- else:
- raise FeatureLibError(
- f"Unexpected token {self.cur_token_} " f"in AxisValue",
- self.cur_token_location_,
- )
- self.expect_symbol_("}")
- if not names:
- raise FeatureLibError('Expected "Axis Name"', self.cur_token_location_)
- if not locations:
- raise FeatureLibError('Expected "Axis location"', self.cur_token_location_)
- if len(locations) > 1:
- for location in locations:
- if len(location.values) > 1:
- raise FeatureLibError(
- "Only one value is allowed in a "
- "Format 4 Axis Value Record, but "
- f"{len(location.values)} were found.",
- self.cur_token_location_,
- )
- format4_tags = []
- for location in locations:
- tag = location.tag
- if tag in format4_tags:
- raise FeatureLibError(
- f"Axis tag {tag} already " "defined.", self.cur_token_location_
- )
- format4_tags.append(tag)
-
- return self.ast.STATAxisValueStatement(
- names, locations, flags, self.cur_token_location_
- )
-
- def parse_STAT_location(self):
- values = []
- tag = self.expect_tag_()
- if len(tag.strip()) != 4:
- raise FeatureLibError(
- f"Axis tag {self.cur_token_} must be 4 " "characters",
- self.cur_token_location_,
- )
-
- while self.next_token_ != ";":
- if self.next_token_type_ is Lexer.FLOAT:
- value = self.expect_float_()
- values.append(value)
- elif self.next_token_type_ is Lexer.NUMBER:
- value = self.expect_number_()
- values.append(value)
- else:
- raise FeatureLibError(
- f'Unexpected value "{self.next_token_}". '
- "Expected integer or float.",
- self.next_token_location_,
- )
- if len(values) == 3:
- nominal, min_val, max_val = values
- if nominal < min_val or nominal > max_val:
- raise FeatureLibError(
- f"Default value {nominal} is outside "
- f"of specified range "
- f"{min_val}-{max_val}.",
- self.next_token_location_,
- )
- return self.ast.AxisValueLocationStatement(tag, values)
-
- def parse_table_STAT_(self, table):
- statements = table.statements
- design_axes = []
- while self.next_token_ != "}" or self.cur_comments_:
- self.advance_lexer_(comments=True)
- if self.cur_token_type_ is Lexer.COMMENT:
- statements.append(
- self.ast.Comment(self.cur_token_, location=self.cur_token_location_)
- )
- elif self.cur_token_type_ is Lexer.NAME:
- if self.is_cur_keyword_("ElidedFallbackName"):
- names = self.parse_STAT_ElidedFallbackName()
- statements.append(self.ast.ElidedFallbackName(names))
- elif self.is_cur_keyword_("ElidedFallbackNameID"):
- value = self.expect_number_()
- statements.append(self.ast.ElidedFallbackNameID(value))
- self.expect_symbol_(";")
- elif self.is_cur_keyword_("DesignAxis"):
- designAxis = self.parse_STAT_design_axis()
- design_axes.append(designAxis.tag)
- statements.append(designAxis)
- self.expect_symbol_(";")
- elif self.is_cur_keyword_("AxisValue"):
- axisValueRecord = self.parse_STAT_axis_value_()
- for location in axisValueRecord.locations:
- if location.tag not in design_axes:
- # Tag must be defined in a DesignAxis before it
- # can be referenced
- raise FeatureLibError(
- "DesignAxis not defined for " f"{location.tag}.",
- self.cur_token_location_,
- )
- statements.append(axisValueRecord)
- self.expect_symbol_(";")
- else:
- raise FeatureLibError(
- f"Unexpected token {self.cur_token_}", self.cur_token_location_
- )
- elif self.cur_token_ == ";":
- continue
-
- def parse_base_tag_list_(self):
- # Parses BASE table entries. (See `section 9.a `_)
- assert self.cur_token_ in (
- "HorizAxis.BaseTagList",
- "VertAxis.BaseTagList",
- ), self.cur_token_
- bases = []
- while self.next_token_ != ";":
- bases.append(self.expect_script_tag_())
- self.expect_symbol_(";")
- return bases
-
- def parse_base_script_list_(self, count):
- assert self.cur_token_ in (
- "HorizAxis.BaseScriptList",
- "VertAxis.BaseScriptList",
- ), self.cur_token_
- scripts = [(self.parse_base_script_record_(count))]
- while self.next_token_ == ",":
- self.expect_symbol_(",")
- scripts.append(self.parse_base_script_record_(count))
- self.expect_symbol_(";")
- return scripts
-
- def parse_base_script_record_(self, count):
- script_tag = self.expect_script_tag_()
- base_tag = self.expect_script_tag_()
- coords = [self.expect_number_() for i in range(count)]
- return script_tag, base_tag, coords
-
- def parse_device_(self):
- result = None
- self.expect_symbol_("<")
- self.expect_keyword_("device")
- if self.next_token_ == "NULL":
- self.expect_keyword_("NULL")
- else:
- result = [(self.expect_number_(), self.expect_number_())]
- while self.next_token_ == ",":
- self.expect_symbol_(",")
- result.append((self.expect_number_(), self.expect_number_()))
- result = tuple(result) # make it hashable
- self.expect_symbol_(">")
- return result
-
- def is_next_value_(self):
- return (
- self.next_token_type_ is Lexer.NUMBER
- or self.next_token_ == "<"
- or self.next_token_ == "("
- )
-
- def parse_valuerecord_(self, vertical):
- if (
- self.next_token_type_ is Lexer.SYMBOL and self.next_token_ == "("
- ) or self.next_token_type_ is Lexer.NUMBER:
- number, location = (
- self.expect_number_(variable=True),
- self.cur_token_location_,
- )
- if vertical:
- val = self.ast.ValueRecord(
- yAdvance=number, vertical=vertical, location=location
- )
- else:
- val = self.ast.ValueRecord(
- xAdvance=number, vertical=vertical, location=location
- )
- return val
- self.expect_symbol_("<")
- location = self.cur_token_location_
- if self.next_token_type_ is Lexer.NAME:
- name = self.expect_name_()
- if name == "NULL":
- self.expect_symbol_(">")
- return self.ast.ValueRecord()
- vrd = self.valuerecords_.resolve(name)
- if vrd is None:
- raise FeatureLibError(
- 'Unknown valueRecordDef "%s"' % name, self.cur_token_location_
- )
- value = vrd.value
- xPlacement, yPlacement = (value.xPlacement, value.yPlacement)
- xAdvance, yAdvance = (value.xAdvance, value.yAdvance)
- else:
- xPlacement, yPlacement, xAdvance, yAdvance = (
- self.expect_number_(variable=True),
- self.expect_number_(variable=True),
- self.expect_number_(variable=True),
- self.expect_number_(variable=True),
- )
-
- if self.next_token_ == "<":
- xPlaDevice, yPlaDevice, xAdvDevice, yAdvDevice = (
- self.parse_device_(),
- self.parse_device_(),
- self.parse_device_(),
- self.parse_device_(),
- )
- allDeltas = sorted(
- [
- delta
- for size, delta in (xPlaDevice if xPlaDevice else ())
- + (yPlaDevice if yPlaDevice else ())
- + (xAdvDevice if xAdvDevice else ())
- + (yAdvDevice if yAdvDevice else ())
- ]
- )
- if allDeltas[0] < -128 or allDeltas[-1] > 127:
- raise FeatureLibError(
- "Device value out of valid range (-128..127)",
- self.cur_token_location_,
- )
- else:
- xPlaDevice, yPlaDevice, xAdvDevice, yAdvDevice = (None, None, None, None)
-
- self.expect_symbol_(">")
- return self.ast.ValueRecord(
- xPlacement,
- yPlacement,
- xAdvance,
- yAdvance,
- xPlaDevice,
- yPlaDevice,
- xAdvDevice,
- yAdvDevice,
- vertical=vertical,
- location=location,
- )
-
- def parse_valuerecord_definition_(self, vertical):
- # Parses a named value record definition. (See section `2.e.v `_)
- assert self.is_cur_keyword_("valueRecordDef")
- location = self.cur_token_location_
- value = self.parse_valuerecord_(vertical)
- name = self.expect_name_()
- self.expect_symbol_(";")
- vrd = self.ast.ValueRecordDefinition(name, value, location=location)
- self.valuerecords_.define(name, vrd)
- return vrd
-
- def parse_languagesystem_(self):
- assert self.cur_token_ == "languagesystem"
- location = self.cur_token_location_
- script = self.expect_script_tag_()
- language = self.expect_language_tag_()
- self.expect_symbol_(";")
- return self.ast.LanguageSystemStatement(script, language, location=location)
-
- def parse_feature_block_(self, variation=False):
- if variation:
- assert self.cur_token_ == "variation"
- else:
- assert self.cur_token_ == "feature"
- location = self.cur_token_location_
- tag = self.expect_tag_()
- vertical = tag in {"vkrn", "vpal", "vhal", "valt"}
-
- stylisticset = None
- cv_feature = None
- size_feature = False
- if tag in self.SS_FEATURE_TAGS:
- stylisticset = tag
- elif tag in self.CV_FEATURE_TAGS:
- cv_feature = tag
- elif tag == "size":
- size_feature = True
-
- if variation:
- conditionset = self.expect_name_()
-
- use_extension = False
- if self.next_token_ == "useExtension":
- self.expect_keyword_("useExtension")
- use_extension = True
-
- if variation:
- block = self.ast.VariationBlock(
- tag, conditionset, use_extension=use_extension, location=location
- )
- else:
- block = self.ast.FeatureBlock(
- tag, use_extension=use_extension, location=location
- )
- self.parse_block_(block, vertical, stylisticset, size_feature, cv_feature)
- return block
-
- def parse_feature_reference_(self):
- assert self.cur_token_ == "feature", self.cur_token_
- location = self.cur_token_location_
- featureName = self.expect_tag_()
- self.expect_symbol_(";")
- return self.ast.FeatureReferenceStatement(featureName, location=location)
-
- def parse_featureNames_(self, tag):
- """Parses a ``featureNames`` statement found in stylistic set features.
- See section `8.c `_.
- """
- assert self.cur_token_ == "featureNames", self.cur_token_
- block = self.ast.NestedBlock(
- tag, self.cur_token_, location=self.cur_token_location_
- )
- self.expect_symbol_("{")
- for symtab in self.symbol_tables_:
- symtab.enter_scope()
- while self.next_token_ != "}" or self.cur_comments_:
- self.advance_lexer_(comments=True)
- if self.cur_token_type_ is Lexer.COMMENT:
- block.statements.append(
- self.ast.Comment(self.cur_token_, location=self.cur_token_location_)
- )
- elif self.is_cur_keyword_("name"):
- location = self.cur_token_location_
- platformID, platEncID, langID, string = self.parse_name_()
- block.statements.append(
- self.ast.FeatureNameStatement(
- tag, platformID, platEncID, langID, string, location=location
- )
- )
- elif self.cur_token_ == ";":
- continue
- else:
- raise FeatureLibError('Expected "name"', self.cur_token_location_)
- self.expect_symbol_("}")
- for symtab in self.symbol_tables_:
- symtab.exit_scope()
- self.expect_symbol_(";")
- return block
-
- def parse_cvParameters_(self, tag):
- # Parses a ``cvParameters`` block found in Character Variant features.
- # See section `8.d `_.
- assert self.cur_token_ == "cvParameters", self.cur_token_
- block = self.ast.NestedBlock(
- tag, self.cur_token_, location=self.cur_token_location_
- )
- self.expect_symbol_("{")
- for symtab in self.symbol_tables_:
- symtab.enter_scope()
-
- statements = block.statements
- while self.next_token_ != "}" or self.cur_comments_:
- self.advance_lexer_(comments=True)
- if self.cur_token_type_ is Lexer.COMMENT:
- statements.append(
- self.ast.Comment(self.cur_token_, location=self.cur_token_location_)
- )
- elif self.is_cur_keyword_(
- {
- "FeatUILabelNameID",
- "FeatUITooltipTextNameID",
- "SampleTextNameID",
- "ParamUILabelNameID",
- }
- ):
- statements.append(self.parse_cvNameIDs_(tag, self.cur_token_))
- elif self.is_cur_keyword_("Character"):
- statements.append(self.parse_cvCharacter_(tag))
- elif self.cur_token_ == ";":
- continue
- else:
- raise FeatureLibError(
- "Expected statement: got {} {}".format(
- self.cur_token_type_, self.cur_token_
- ),
- self.cur_token_location_,
- )
-
- self.expect_symbol_("}")
- for symtab in self.symbol_tables_:
- symtab.exit_scope()
- self.expect_symbol_(";")
- return block
-
- def parse_cvNameIDs_(self, tag, block_name):
- assert self.cur_token_ == block_name, self.cur_token_
- block = self.ast.NestedBlock(tag, block_name, location=self.cur_token_location_)
- self.expect_symbol_("{")
- for symtab in self.symbol_tables_:
- symtab.enter_scope()
- while self.next_token_ != "}" or self.cur_comments_:
- self.advance_lexer_(comments=True)
- if self.cur_token_type_ is Lexer.COMMENT:
- block.statements.append(
- self.ast.Comment(self.cur_token_, location=self.cur_token_location_)
- )
- elif self.is_cur_keyword_("name"):
- location = self.cur_token_location_
- platformID, platEncID, langID, string = self.parse_name_()
- block.statements.append(
- self.ast.CVParametersNameStatement(
- tag,
- platformID,
- platEncID,
- langID,
- string,
- block_name,
- location=location,
- )
- )
- elif self.cur_token_ == ";":
- continue
- else:
- raise FeatureLibError('Expected "name"', self.cur_token_location_)
- self.expect_symbol_("}")
- for symtab in self.symbol_tables_:
- symtab.exit_scope()
- self.expect_symbol_(";")
- return block
-
- def parse_cvCharacter_(self, tag):
- assert self.cur_token_ == "Character", self.cur_token_
- location, character = self.cur_token_location_, self.expect_any_number_()
- self.expect_symbol_(";")
- if not (0xFFFFFF >= character >= 0):
- raise FeatureLibError(
- "Character value must be between "
- "{:#x} and {:#x}".format(0, 0xFFFFFF),
- location,
- )
- return self.ast.CharacterStatement(character, tag, location=location)
-
- def parse_FontRevision_(self):
- # Parses a ``FontRevision`` statement found in the head table. See
- # `section 9.c `_.
- assert self.cur_token_ == "FontRevision", self.cur_token_
- location, version = self.cur_token_location_, self.expect_float_()
- self.expect_symbol_(";")
- if version <= 0:
- raise FeatureLibError("Font revision numbers must be positive", location)
- return self.ast.FontRevisionStatement(version, location=location)
-
- def parse_conditionset_(self):
- name = self.expect_name_()
-
- conditions = {}
- self.expect_symbol_("{")
-
- while self.next_token_ != "}":
- self.advance_lexer_()
- if self.cur_token_type_ is not Lexer.NAME:
- raise FeatureLibError("Expected an axis name", self.cur_token_location_)
-
- axis = self.cur_token_
- if axis in conditions:
- raise FeatureLibError(
- f"Repeated condition for axis {axis}", self.cur_token_location_
- )
-
- if self.next_token_type_ is Lexer.FLOAT:
- min_value = self.expect_float_()
- elif self.next_token_type_ is Lexer.NUMBER:
- min_value = self.expect_number_(variable=False)
-
- if self.next_token_type_ is Lexer.FLOAT:
- max_value = self.expect_float_()
- elif self.next_token_type_ is Lexer.NUMBER:
- max_value = self.expect_number_(variable=False)
- self.expect_symbol_(";")
-
- conditions[axis] = (min_value, max_value)
-
- self.expect_symbol_("}")
-
- finalname = self.expect_name_()
- if finalname != name:
- raise FeatureLibError('Expected "%s"' % name, self.cur_token_location_)
- return self.ast.ConditionsetStatement(name, conditions)
-
- def parse_block_(
- self, block, vertical, stylisticset=None, size_feature=False, cv_feature=None
- ):
- self.expect_symbol_("{")
- for symtab in self.symbol_tables_:
- symtab.enter_scope()
-
- statements = block.statements
- while self.next_token_ != "}" or self.cur_comments_:
- self.advance_lexer_(comments=True)
- if self.cur_token_type_ is Lexer.COMMENT:
- statements.append(
- self.ast.Comment(self.cur_token_, location=self.cur_token_location_)
- )
- elif self.cur_token_type_ is Lexer.GLYPHCLASS:
- statements.append(self.parse_glyphclass_definition_())
- elif self.is_cur_keyword_("anchorDef"):
- statements.append(self.parse_anchordef_())
- elif self.is_cur_keyword_({"enum", "enumerate"}):
- statements.append(self.parse_enumerate_(vertical=vertical))
- elif self.is_cur_keyword_("feature"):
- statements.append(self.parse_feature_reference_())
- elif self.is_cur_keyword_("ignore"):
- statements.append(self.parse_ignore_())
- elif self.is_cur_keyword_("language"):
- statements.append(self.parse_language_())
- elif self.is_cur_keyword_("lookup"):
- statements.append(self.parse_lookup_(vertical))
- elif self.is_cur_keyword_("lookupflag"):
- statements.append(self.parse_lookupflag_())
- elif self.is_cur_keyword_("markClass"):
- statements.append(self.parse_markClass_())
- elif self.is_cur_keyword_({"pos", "position"}):
- statements.append(
- self.parse_position_(enumerated=False, vertical=vertical)
- )
- elif self.is_cur_keyword_("script"):
- statements.append(self.parse_script_())
- elif self.is_cur_keyword_({"sub", "substitute", "rsub", "reversesub"}):
- statements.append(self.parse_substitute_())
- elif self.is_cur_keyword_("subtable"):
- statements.append(self.parse_subtable_())
- elif self.is_cur_keyword_("valueRecordDef"):
- statements.append(self.parse_valuerecord_definition_(vertical))
- elif stylisticset and self.is_cur_keyword_("featureNames"):
- statements.append(self.parse_featureNames_(stylisticset))
- elif cv_feature and self.is_cur_keyword_("cvParameters"):
- statements.append(self.parse_cvParameters_(cv_feature))
- elif size_feature and self.is_cur_keyword_("parameters"):
- statements.append(self.parse_size_parameters_())
- elif size_feature and self.is_cur_keyword_("sizemenuname"):
- statements.append(self.parse_size_menuname_())
- elif (
- self.cur_token_type_ is Lexer.NAME
- and self.cur_token_ in self.extensions
- ):
- statements.append(self.extensions[self.cur_token_](self))
- elif self.cur_token_ == ";":
- continue
- else:
- raise FeatureLibError(
- "Expected glyph class definition or statement: got {} {}".format(
- self.cur_token_type_, self.cur_token_
- ),
- self.cur_token_location_,
- )
-
- self.expect_symbol_("}")
- for symtab in self.symbol_tables_:
- symtab.exit_scope()
-
- name = self.expect_name_()
- if name != block.name.strip():
- raise FeatureLibError(
- 'Expected "%s"' % block.name.strip(), self.cur_token_location_
- )
- self.expect_symbol_(";")
-
- # A multiple substitution may have a single destination, in which case
- # it will look just like a single substitution. So if there are both
- # multiple and single substitutions, upgrade all the single ones to
- # multiple substitutions.
-
- # Check if we have a mix of non-contextual singles and multiples.
- has_single = False
- has_multiple = False
- for s in statements:
- if isinstance(s, self.ast.SingleSubstStatement):
- has_single = not any([s.prefix, s.suffix, s.forceChain])
- elif isinstance(s, self.ast.MultipleSubstStatement):
- has_multiple = not any([s.prefix, s.suffix, s.forceChain])
-
- # Upgrade all single substitutions to multiple substitutions.
- if has_single and has_multiple:
- statements = []
- for s in block.statements:
- if isinstance(s, self.ast.SingleSubstStatement):
- glyphs = s.glyphs[0].glyphSet()
- replacements = s.replacements[0].glyphSet()
- if len(replacements) == 1:
- replacements *= len(glyphs)
- for i, glyph in enumerate(glyphs):
- statements.append(
- self.ast.MultipleSubstStatement(
- s.prefix,
- glyph,
- s.suffix,
- [replacements[i]],
- s.forceChain,
- location=s.location,
- )
- )
- else:
- statements.append(s)
- block.statements = statements
-
- def is_cur_keyword_(self, k):
- if self.cur_token_type_ is Lexer.NAME:
- if isinstance(k, type("")): # basestring is gone in Python3
- return self.cur_token_ == k
- else:
- return self.cur_token_ in k
- return False
-
- def expect_class_name_(self):
- self.advance_lexer_()
- if self.cur_token_type_ is not Lexer.GLYPHCLASS:
- raise FeatureLibError("Expected @NAME", self.cur_token_location_)
- return self.cur_token_
-
- def expect_cid_(self):
- self.advance_lexer_()
- if self.cur_token_type_ is Lexer.CID:
- return self.cur_token_
- raise FeatureLibError("Expected a CID", self.cur_token_location_)
-
- def expect_filename_(self):
- self.advance_lexer_()
- if self.cur_token_type_ is not Lexer.FILENAME:
- raise FeatureLibError("Expected file name", self.cur_token_location_)
- return self.cur_token_
-
- def expect_glyph_(self):
- self.advance_lexer_()
- if self.cur_token_type_ is Lexer.NAME:
- self.cur_token_ = self.cur_token_.lstrip("\\")
- if len(self.cur_token_) > 63:
- raise FeatureLibError(
- "Glyph names must not be longer than 63 characters",
- self.cur_token_location_,
- )
- return self.cur_token_
- elif self.cur_token_type_ is Lexer.CID:
- return "cid%05d" % self.cur_token_
- raise FeatureLibError("Expected a glyph name or CID", self.cur_token_location_)
-
- def check_glyph_name_in_glyph_set(self, *names):
- """Adds a glyph name (just `start`) or glyph names of a
- range (`start` and `end`) which are not in the glyph set
- to the "missing list" for future error reporting.
-
- If no glyph set is present, does nothing.
- """
- if self.glyphNames_:
- for name in names:
- if name in self.glyphNames_:
- continue
- if name not in self.missing:
- self.missing[name] = self.cur_token_location_
-
- def expect_markClass_reference_(self):
- name = self.expect_class_name_()
- mc = self.glyphclasses_.resolve(name)
- if mc is None:
- raise FeatureLibError(
- "Unknown markClass @%s" % name, self.cur_token_location_
- )
- if not isinstance(mc, self.ast.MarkClass):
- raise FeatureLibError(
- "@%s is not a markClass" % name, self.cur_token_location_
- )
- return mc
-
- def expect_tag_(self):
- self.advance_lexer_()
- if self.cur_token_type_ is not Lexer.NAME:
- raise FeatureLibError("Expected a tag", self.cur_token_location_)
- if len(self.cur_token_) > 4:
- raise FeatureLibError(
- "Tags cannot be longer than 4 characters", self.cur_token_location_
- )
- return (self.cur_token_ + " ")[:4]
-
- def expect_script_tag_(self):
- tag = self.expect_tag_()
- if tag == "dflt":
- raise FeatureLibError(
- '"dflt" is not a valid script tag; use "DFLT" instead',
- self.cur_token_location_,
- )
- return tag
-
- def expect_language_tag_(self):
- tag = self.expect_tag_()
- if tag == "DFLT":
- raise FeatureLibError(
- '"DFLT" is not a valid language tag; use "dflt" instead',
- self.cur_token_location_,
- )
- return tag
-
- def expect_symbol_(self, symbol):
- self.advance_lexer_()
- if self.cur_token_type_ is Lexer.SYMBOL and self.cur_token_ == symbol:
- return symbol
- raise FeatureLibError("Expected '%s'" % symbol, self.cur_token_location_)
-
- def expect_keyword_(self, keyword):
- self.advance_lexer_()
- if self.cur_token_type_ is Lexer.NAME and self.cur_token_ == keyword:
- return self.cur_token_
- raise FeatureLibError('Expected "%s"' % keyword, self.cur_token_location_)
-
- def expect_name_(self):
- self.advance_lexer_()
- if self.cur_token_type_ is Lexer.NAME:
- return self.cur_token_
- raise FeatureLibError("Expected a name", self.cur_token_location_)
-
- def expect_number_(self, variable=False):
- self.advance_lexer_()
- if self.cur_token_type_ is Lexer.NUMBER:
- return self.cur_token_
- if variable and self.cur_token_type_ is Lexer.SYMBOL and self.cur_token_ == "(":
- return self.expect_variable_scalar_()
- raise FeatureLibError("Expected a number", self.cur_token_location_)
-
- def expect_variable_scalar_(self):
- self.advance_lexer_() # "("
- scalar = VariableScalar()
- while True:
- if self.cur_token_type_ == Lexer.SYMBOL and self.cur_token_ == ")":
- break
- location, value = self.expect_master_()
- scalar.add_value(location, value)
- return scalar
-
- def expect_master_(self):
- location = {}
- while True:
- if self.cur_token_type_ is not Lexer.NAME:
- raise FeatureLibError("Expected an axis name", self.cur_token_location_)
- axis = self.cur_token_
- self.advance_lexer_()
- if not (self.cur_token_type_ is Lexer.SYMBOL and self.cur_token_ == "="):
- raise FeatureLibError(
- "Expected an equals sign", self.cur_token_location_
- )
- value = self.expect_number_()
- location[axis] = value
- if self.next_token_type_ is Lexer.NAME and self.next_token_[0] == ":":
- # Lexer has just read the value as a glyph name. We'll correct it later
- break
- self.advance_lexer_()
- if not (self.cur_token_type_ is Lexer.SYMBOL and self.cur_token_ == ","):
- raise FeatureLibError(
- "Expected an comma or an equals sign", self.cur_token_location_
- )
- self.advance_lexer_()
- self.advance_lexer_()
- value = int(self.cur_token_[1:])
- self.advance_lexer_()
- return location, value
-
- def expect_any_number_(self):
- self.advance_lexer_()
- if self.cur_token_type_ in Lexer.NUMBERS:
- return self.cur_token_
- raise FeatureLibError(
- "Expected a decimal, hexadecimal or octal number", self.cur_token_location_
- )
-
- def expect_float_(self):
- self.advance_lexer_()
- if self.cur_token_type_ is Lexer.FLOAT:
- return self.cur_token_
- raise FeatureLibError(
- "Expected a floating-point number", self.cur_token_location_
- )
-
- def expect_decipoint_(self):
- if self.next_token_type_ == Lexer.FLOAT:
- return self.expect_float_()
- elif self.next_token_type_ is Lexer.NUMBER:
- return self.expect_number_() / 10
- else:
- raise FeatureLibError(
- "Expected an integer or floating-point number", self.cur_token_location_
- )
-
- def expect_stat_flags(self):
- value = 0
- flags = {
- "OlderSiblingFontAttribute": 1,
- "ElidableAxisValueName": 2,
- }
- while self.next_token_ != ";":
- if self.next_token_ in flags:
- name = self.expect_name_()
- value = value | flags[name]
- else:
- raise FeatureLibError(
- f"Unexpected STAT flag {self.cur_token_}", self.cur_token_location_
- )
- return value
-
- def expect_stat_values_(self):
- if self.next_token_type_ == Lexer.FLOAT:
- return self.expect_float_()
- elif self.next_token_type_ is Lexer.NUMBER:
- return self.expect_number_()
- else:
- raise FeatureLibError(
- "Expected an integer or floating-point number", self.cur_token_location_
- )
-
- def expect_string_(self):
- self.advance_lexer_()
- if self.cur_token_type_ is Lexer.STRING:
- return self.cur_token_
- raise FeatureLibError("Expected a string", self.cur_token_location_)
-
- def advance_lexer_(self, comments=False):
- if comments and self.cur_comments_:
- self.cur_token_type_ = Lexer.COMMENT
- self.cur_token_, self.cur_token_location_ = self.cur_comments_.pop(0)
- return
- else:
- self.cur_token_type_, self.cur_token_, self.cur_token_location_ = (
- self.next_token_type_,
- self.next_token_,
- self.next_token_location_,
- )
- while True:
- try:
- (
- self.next_token_type_,
- self.next_token_,
- self.next_token_location_,
- ) = next(self.lexer_)
- except StopIteration:
- self.next_token_type_, self.next_token_ = (None, None)
- if self.next_token_type_ != Lexer.COMMENT:
- break
- self.cur_comments_.append((self.next_token_, self.next_token_location_))
-
- @staticmethod
- def reverse_string_(s):
- """'abc' --> 'cba'"""
- return "".join(reversed(list(s)))
-
- def make_cid_range_(self, location, start, limit):
- """(location, 999, 1001) --> ["cid00999", "cid01000", "cid01001"]"""
- result = list()
- if start > limit:
- raise FeatureLibError(
- "Bad range: start should be less than limit", location
- )
- for cid in range(start, limit + 1):
- result.append("cid%05d" % cid)
- return result
-
- def make_glyph_range_(self, location, start, limit):
- """(location, "a.sc", "d.sc") --> ["a.sc", "b.sc", "c.sc", "d.sc"]"""
- result = list()
- if len(start) != len(limit):
- raise FeatureLibError(
- 'Bad range: "%s" and "%s" should have the same length' % (start, limit),
- location,
- )
-
- rev = self.reverse_string_
- prefix = os.path.commonprefix([start, limit])
- suffix = rev(os.path.commonprefix([rev(start), rev(limit)]))
- if len(suffix) > 0:
- start_range = start[len(prefix) : -len(suffix)]
- limit_range = limit[len(prefix) : -len(suffix)]
- else:
- start_range = start[len(prefix) :]
- limit_range = limit[len(prefix) :]
-
- if start_range >= limit_range:
- raise FeatureLibError(
- "Start of range must be smaller than its end", location
- )
-
- uppercase = re.compile(r"^[A-Z]$")
- if uppercase.match(start_range) and uppercase.match(limit_range):
- for c in range(ord(start_range), ord(limit_range) + 1):
- result.append("%s%c%s" % (prefix, c, suffix))
- return result
-
- lowercase = re.compile(r"^[a-z]$")
- if lowercase.match(start_range) and lowercase.match(limit_range):
- for c in range(ord(start_range), ord(limit_range) + 1):
- result.append("%s%c%s" % (prefix, c, suffix))
- return result
-
- digits = re.compile(r"^[0-9]{1,3}$")
- if digits.match(start_range) and digits.match(limit_range):
- for i in range(int(start_range, 10), int(limit_range, 10) + 1):
- number = ("000" + str(i))[-len(start_range) :]
- result.append("%s%s%s" % (prefix, number, suffix))
- return result
-
- raise FeatureLibError('Bad range: "%s-%s"' % (start, limit), location)
-
-
-class SymbolTable(object):
- def __init__(self):
- self.scopes_ = [{}]
-
- def enter_scope(self):
- self.scopes_.append({})
-
- def exit_scope(self):
- self.scopes_.pop()
-
- def define(self, name, item):
- self.scopes_[-1][name] = item
-
- def resolve(self, name):
- for scope in reversed(self.scopes_):
- item = scope.get(name)
- if item:
- return item
- return None
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/variableScalar.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/variableScalar.py
deleted file mode 100644
index c97b435..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/feaLib/variableScalar.py
+++ /dev/null
@@ -1,112 +0,0 @@
-from fontTools.varLib.models import VariationModel, normalizeValue, piecewiseLinearMap
-
-
-def Location(loc):
- return tuple(sorted(loc.items()))
-
-
-class VariableScalar:
- """A scalar with different values at different points in the designspace."""
-
- def __init__(self, location_value={}):
- self.values = {}
- self.axes = {}
- for location, value in location_value.items():
- self.add_value(location, value)
-
- def __repr__(self):
- items = []
- for location, value in self.values.items():
- loc = ",".join(["%s=%i" % (ax, loc) for ax, loc in location])
- items.append("%s:%i" % (loc, value))
- return "(" + (" ".join(items)) + ")"
-
- @property
- def does_vary(self):
- values = list(self.values.values())
- return any(v != values[0] for v in values[1:])
-
- @property
- def axes_dict(self):
- if not self.axes:
- raise ValueError(
- ".axes must be defined on variable scalar before interpolating"
- )
- return {ax.axisTag: ax for ax in self.axes}
-
- def _normalized_location(self, location):
- location = self.fix_location(location)
- normalized_location = {}
- for axtag in location.keys():
- if axtag not in self.axes_dict:
- raise ValueError("Unknown axis %s in %s" % (axtag, location))
- axis = self.axes_dict[axtag]
- normalized_location[axtag] = normalizeValue(
- location[axtag], (axis.minValue, axis.defaultValue, axis.maxValue)
- )
-
- return Location(normalized_location)
-
- def fix_location(self, location):
- location = dict(location)
- for tag, axis in self.axes_dict.items():
- if tag not in location:
- location[tag] = axis.defaultValue
- return location
-
- def add_value(self, location, value):
- if self.axes:
- location = self.fix_location(location)
-
- self.values[Location(location)] = value
-
- def fix_all_locations(self):
- self.values = {
- Location(self.fix_location(l)): v for l, v in self.values.items()
- }
-
- @property
- def default(self):
- self.fix_all_locations()
- key = Location({ax.axisTag: ax.defaultValue for ax in self.axes})
- if key not in self.values:
- raise ValueError("Default value could not be found")
- # I *guess* we could interpolate one, but I don't know how.
- return self.values[key]
-
- def value_at_location(self, location, model_cache=None, avar=None):
- loc = location
- if loc in self.values.keys():
- return self.values[loc]
- values = list(self.values.values())
- return self.model(model_cache, avar).interpolateFromMasters(loc, values)
-
- def model(self, model_cache=None, avar=None):
- if model_cache is not None:
- key = tuple(self.values.keys())
- if key in model_cache:
- return model_cache[key]
- locations = [dict(self._normalized_location(k)) for k in self.values.keys()]
- if avar is not None:
- mapping = avar.segments
- locations = [
- {
- k: piecewiseLinearMap(v, mapping[k]) if k in mapping else v
- for k, v in location.items()
- }
- for location in locations
- ]
- m = VariationModel(locations)
- if model_cache is not None:
- model_cache[key] = m
- return m
-
- def get_deltas_and_supports(self, model_cache=None, avar=None):
- values = list(self.values.values())
- return self.model(model_cache, avar).getDeltasAndSupports(values)
-
- def add_to_variation_store(self, store_builder, model_cache=None, avar=None):
- deltas, supports = self.get_deltas_and_supports(model_cache, avar)
- store_builder.setSupports(supports)
- index = store_builder.storeDeltas(deltas)
- return int(self.default), index
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/fontBuilder.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/fontBuilder.py
deleted file mode 100644
index 6a76740..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/fontBuilder.py
+++ /dev/null
@@ -1,996 +0,0 @@
-__all__ = ["FontBuilder"]
-
-"""
-This module is *experimental*, meaning it still may evolve and change.
-
-The `FontBuilder` class is a convenient helper to construct working TTF or
-OTF fonts from scratch.
-
-Note that the various setup methods cannot be called in arbitrary order,
-due to various interdependencies between OpenType tables. Here is an order
-that works:
-
- fb = FontBuilder(...)
- fb.setupGlyphOrder(...)
- fb.setupCharacterMap(...)
- fb.setupGlyf(...) --or-- fb.setupCFF(...)
- fb.setupHorizontalMetrics(...)
- fb.setupHorizontalHeader()
- fb.setupNameTable(...)
- fb.setupOS2()
- fb.addOpenTypeFeatures(...)
- fb.setupPost()
- fb.save(...)
-
-Here is how to build a minimal TTF:
-
-```python
-from fontTools.fontBuilder import FontBuilder
-from fontTools.pens.ttGlyphPen import TTGlyphPen
-
-
-def drawTestGlyph(pen):
- pen.moveTo((100, 100))
- pen.lineTo((100, 1000))
- pen.qCurveTo((200, 900), (400, 900), (500, 1000))
- pen.lineTo((500, 100))
- pen.closePath()
-
-
-fb = FontBuilder(1024, isTTF=True)
-fb.setupGlyphOrder([".notdef", ".null", "space", "A", "a"])
-fb.setupCharacterMap({32: "space", 65: "A", 97: "a"})
-advanceWidths = {".notdef": 600, "space": 500, "A": 600, "a": 600, ".null": 0}
-
-familyName = "HelloTestFont"
-styleName = "TotallyNormal"
-version = "0.1"
-
-nameStrings = dict(
- familyName=dict(en=familyName, nl="HalloTestFont"),
- styleName=dict(en=styleName, nl="TotaalNormaal"),
- uniqueFontIdentifier="fontBuilder: " + familyName + "." + styleName,
- fullName=familyName + "-" + styleName,
- psName=familyName + "-" + styleName,
- version="Version " + version,
-)
-
-pen = TTGlyphPen(None)
-drawTestGlyph(pen)
-glyph = pen.glyph()
-glyphs = {".notdef": glyph, "space": glyph, "A": glyph, "a": glyph, ".null": glyph}
-fb.setupGlyf(glyphs)
-metrics = {}
-glyphTable = fb.font["glyf"]
-for gn, advanceWidth in advanceWidths.items():
- metrics[gn] = (advanceWidth, glyphTable[gn].xMin)
-fb.setupHorizontalMetrics(metrics)
-fb.setupHorizontalHeader(ascent=824, descent=-200)
-fb.setupNameTable(nameStrings)
-fb.setupOS2(sTypoAscender=824, usWinAscent=824, usWinDescent=200)
-fb.setupPost()
-fb.save("test.ttf")
-```
-
-And here's how to build a minimal OTF:
-
-```python
-from fontTools.fontBuilder import FontBuilder
-from fontTools.pens.t2CharStringPen import T2CharStringPen
-
-
-def drawTestGlyph(pen):
- pen.moveTo((100, 100))
- pen.lineTo((100, 1000))
- pen.curveTo((200, 900), (400, 900), (500, 1000))
- pen.lineTo((500, 100))
- pen.closePath()
-
-
-fb = FontBuilder(1024, isTTF=False)
-fb.setupGlyphOrder([".notdef", ".null", "space", "A", "a"])
-fb.setupCharacterMap({32: "space", 65: "A", 97: "a"})
-advanceWidths = {".notdef": 600, "space": 500, "A": 600, "a": 600, ".null": 0}
-
-familyName = "HelloTestFont"
-styleName = "TotallyNormal"
-version = "0.1"
-
-nameStrings = dict(
- familyName=dict(en=familyName, nl="HalloTestFont"),
- styleName=dict(en=styleName, nl="TotaalNormaal"),
- uniqueFontIdentifier="fontBuilder: " + familyName + "." + styleName,
- fullName=familyName + "-" + styleName,
- psName=familyName + "-" + styleName,
- version="Version " + version,
-)
-
-pen = T2CharStringPen(600, None)
-drawTestGlyph(pen)
-charString = pen.getCharString()
-charStrings = {
- ".notdef": charString,
- "space": charString,
- "A": charString,
- "a": charString,
- ".null": charString,
-}
-fb.setupCFF(nameStrings["psName"], {"FullName": nameStrings["psName"]}, charStrings, {})
-lsb = {gn: cs.calcBounds(None)[0] for gn, cs in charStrings.items()}
-metrics = {}
-for gn, advanceWidth in advanceWidths.items():
- metrics[gn] = (advanceWidth, lsb[gn])
-fb.setupHorizontalMetrics(metrics)
-fb.setupHorizontalHeader(ascent=824, descent=200)
-fb.setupNameTable(nameStrings)
-fb.setupOS2(sTypoAscender=824, usWinAscent=824, usWinDescent=200)
-fb.setupPost()
-fb.save("test.otf")
-```
-"""
-
-from .ttLib import TTFont, newTable
-from .ttLib.tables._c_m_a_p import cmap_classes
-from .ttLib.tables._g_l_y_f import flagCubic
-from .misc.timeTools import timestampNow
-import struct
-from collections import OrderedDict
-
-
-_headDefaults = dict(
- tableVersion=1.0,
- fontRevision=1.0,
- checkSumAdjustment=0,
- magicNumber=0x5F0F3CF5,
- flags=0x0003,
- unitsPerEm=1000,
- created=0,
- modified=0,
- xMin=0,
- yMin=0,
- xMax=0,
- yMax=0,
- macStyle=0,
- lowestRecPPEM=3,
- fontDirectionHint=2,
- indexToLocFormat=0,
- glyphDataFormat=0,
-)
-
-_maxpDefaultsTTF = dict(
- tableVersion=0x00010000,
- numGlyphs=0,
- maxPoints=0,
- maxContours=0,
- maxCompositePoints=0,
- maxCompositeContours=0,
- maxZones=2,
- maxTwilightPoints=0,
- maxStorage=0,
- maxFunctionDefs=0,
- maxInstructionDefs=0,
- maxStackElements=0,
- maxSizeOfInstructions=0,
- maxComponentElements=0,
- maxComponentDepth=0,
-)
-_maxpDefaultsOTF = dict(
- tableVersion=0x00005000,
- numGlyphs=0,
-)
-
-_postDefaults = dict(
- formatType=3.0,
- italicAngle=0,
- underlinePosition=0,
- underlineThickness=0,
- isFixedPitch=0,
- minMemType42=0,
- maxMemType42=0,
- minMemType1=0,
- maxMemType1=0,
-)
-
-_hheaDefaults = dict(
- tableVersion=0x00010000,
- ascent=0,
- descent=0,
- lineGap=0,
- advanceWidthMax=0,
- minLeftSideBearing=0,
- minRightSideBearing=0,
- xMaxExtent=0,
- caretSlopeRise=1,
- caretSlopeRun=0,
- caretOffset=0,
- reserved0=0,
- reserved1=0,
- reserved2=0,
- reserved3=0,
- metricDataFormat=0,
- numberOfHMetrics=0,
-)
-
-_vheaDefaults = dict(
- tableVersion=0x00010000,
- ascent=0,
- descent=0,
- lineGap=0,
- advanceHeightMax=0,
- minTopSideBearing=0,
- minBottomSideBearing=0,
- yMaxExtent=0,
- caretSlopeRise=0,
- caretSlopeRun=0,
- reserved0=0,
- reserved1=0,
- reserved2=0,
- reserved3=0,
- reserved4=0,
- metricDataFormat=0,
- numberOfVMetrics=0,
-)
-
-_nameIDs = dict(
- copyright=0,
- familyName=1,
- styleName=2,
- uniqueFontIdentifier=3,
- fullName=4,
- version=5,
- psName=6,
- trademark=7,
- manufacturer=8,
- designer=9,
- description=10,
- vendorURL=11,
- designerURL=12,
- licenseDescription=13,
- licenseInfoURL=14,
- # reserved = 15,
- typographicFamily=16,
- typographicSubfamily=17,
- compatibleFullName=18,
- sampleText=19,
- postScriptCIDFindfontName=20,
- wwsFamilyName=21,
- wwsSubfamilyName=22,
- lightBackgroundPalette=23,
- darkBackgroundPalette=24,
- variationsPostScriptNamePrefix=25,
-)
-
-# to insert in setupNameTable doc string:
-# print("\n".join(("%s (nameID %s)" % (k, v)) for k, v in sorted(_nameIDs.items(), key=lambda x: x[1])))
-
-_panoseDefaults = dict(
- bFamilyType=0,
- bSerifStyle=0,
- bWeight=0,
- bProportion=0,
- bContrast=0,
- bStrokeVariation=0,
- bArmStyle=0,
- bLetterForm=0,
- bMidline=0,
- bXHeight=0,
-)
-
-_OS2Defaults = dict(
- version=3,
- xAvgCharWidth=0,
- usWeightClass=400,
- usWidthClass=5,
- fsType=0x0004, # default: Preview & Print embedding
- ySubscriptXSize=0,
- ySubscriptYSize=0,
- ySubscriptXOffset=0,
- ySubscriptYOffset=0,
- ySuperscriptXSize=0,
- ySuperscriptYSize=0,
- ySuperscriptXOffset=0,
- ySuperscriptYOffset=0,
- yStrikeoutSize=0,
- yStrikeoutPosition=0,
- sFamilyClass=0,
- panose=_panoseDefaults,
- ulUnicodeRange1=0,
- ulUnicodeRange2=0,
- ulUnicodeRange3=0,
- ulUnicodeRange4=0,
- achVendID="????",
- fsSelection=0,
- usFirstCharIndex=0,
- usLastCharIndex=0,
- sTypoAscender=0,
- sTypoDescender=0,
- sTypoLineGap=0,
- usWinAscent=0,
- usWinDescent=0,
- ulCodePageRange1=0,
- ulCodePageRange2=0,
- sxHeight=0,
- sCapHeight=0,
- usDefaultChar=0, # .notdef
- usBreakChar=32, # space
- usMaxContext=0,
- usLowerOpticalPointSize=0,
- usUpperOpticalPointSize=0,
-)
-
-
-class FontBuilder(object):
- def __init__(self, unitsPerEm=None, font=None, isTTF=True, glyphDataFormat=0):
- """Initialize a FontBuilder instance.
-
- If the `font` argument is not given, a new `TTFont` will be
- constructed, and `unitsPerEm` must be given. If `isTTF` is True,
- the font will be a glyf-based TTF; if `isTTF` is False it will be
- a CFF-based OTF.
-
- The `glyphDataFormat` argument corresponds to the `head` table field
- that defines the format of the TrueType `glyf` table (default=0).
- TrueType glyphs historically can only contain quadratic splines and static
- components, but there's a proposal to add support for cubic Bezier curves as well
- as variable composites/components at
- https://github.com/harfbuzz/boring-expansion-spec/blob/main/glyf1.md
- You can experiment with the new features by setting `glyphDataFormat` to 1.
- A ValueError is raised if `glyphDataFormat` is left at 0 but glyphs are added
- that contain cubic splines or varcomposites. This is to prevent accidentally
- creating fonts that are incompatible with existing TrueType implementations.
-
- If `font` is given, it must be a `TTFont` instance and `unitsPerEm`
- must _not_ be given. The `isTTF` and `glyphDataFormat` arguments will be ignored.
- """
- if font is None:
- self.font = TTFont(recalcTimestamp=False)
- self.isTTF = isTTF
- now = timestampNow()
- assert unitsPerEm is not None
- self.setupHead(
- unitsPerEm=unitsPerEm,
- create=now,
- modified=now,
- glyphDataFormat=glyphDataFormat,
- )
- self.setupMaxp()
- else:
- assert unitsPerEm is None
- self.font = font
- self.isTTF = "glyf" in font
-
- def save(self, file):
- """Save the font. The 'file' argument can be either a pathname or a
- writable file object.
- """
- self.font.save(file)
-
- def _initTableWithValues(self, tableTag, defaults, values):
- table = self.font[tableTag] = newTable(tableTag)
- for k, v in defaults.items():
- setattr(table, k, v)
- for k, v in values.items():
- setattr(table, k, v)
- return table
-
- def _updateTableWithValues(self, tableTag, values):
- table = self.font[tableTag]
- for k, v in values.items():
- setattr(table, k, v)
-
- def setupHead(self, **values):
- """Create a new `head` table and initialize it with default values,
- which can be overridden by keyword arguments.
- """
- self._initTableWithValues("head", _headDefaults, values)
-
- def updateHead(self, **values):
- """Update the head table with the fields and values passed as
- keyword arguments.
- """
- self._updateTableWithValues("head", values)
-
- def setupGlyphOrder(self, glyphOrder):
- """Set the glyph order for the font."""
- self.font.setGlyphOrder(glyphOrder)
-
- def setupCharacterMap(self, cmapping, uvs=None, allowFallback=False):
- """Build the `cmap` table for the font. The `cmapping` argument should
- be a dict mapping unicode code points as integers to glyph names.
-
- The `uvs` argument, when passed, must be a list of tuples, describing
- Unicode Variation Sequences. These tuples have three elements:
- (unicodeValue, variationSelector, glyphName)
- `unicodeValue` and `variationSelector` are integer code points.
- `glyphName` may be None, to indicate this is the default variation.
- Text processors will then use the cmap to find the glyph name.
- Each Unicode Variation Sequence should be an officially supported
- sequence, but this is not policed.
- """
- subTables = []
- highestUnicode = max(cmapping) if cmapping else 0
- if highestUnicode > 0xFFFF:
- cmapping_3_1 = dict((k, v) for k, v in cmapping.items() if k < 0x10000)
- subTable_3_10 = buildCmapSubTable(cmapping, 12, 3, 10)
- subTables.append(subTable_3_10)
- else:
- cmapping_3_1 = cmapping
- format = 4
- subTable_3_1 = buildCmapSubTable(cmapping_3_1, format, 3, 1)
- try:
- subTable_3_1.compile(self.font)
- except struct.error:
- # format 4 overflowed, fall back to format 12
- if not allowFallback:
- raise ValueError(
- "cmap format 4 subtable overflowed; sort glyph order by unicode to fix."
- )
- format = 12
- subTable_3_1 = buildCmapSubTable(cmapping_3_1, format, 3, 1)
- subTables.append(subTable_3_1)
- subTable_0_3 = buildCmapSubTable(cmapping_3_1, format, 0, 3)
- subTables.append(subTable_0_3)
-
- if uvs is not None:
- uvsDict = {}
- for unicodeValue, variationSelector, glyphName in uvs:
- if cmapping.get(unicodeValue) == glyphName:
- # this is a default variation
- glyphName = None
- if variationSelector not in uvsDict:
- uvsDict[variationSelector] = []
- uvsDict[variationSelector].append((unicodeValue, glyphName))
- uvsSubTable = buildCmapSubTable({}, 14, 0, 5)
- uvsSubTable.uvsDict = uvsDict
- subTables.append(uvsSubTable)
-
- self.font["cmap"] = newTable("cmap")
- self.font["cmap"].tableVersion = 0
- self.font["cmap"].tables = subTables
-
- def setupNameTable(self, nameStrings, windows=True, mac=True):
- """Create the `name` table for the font. The `nameStrings` argument must
- be a dict, mapping nameIDs or descriptive names for the nameIDs to name
- record values. A value is either a string, or a dict, mapping language codes
- to strings, to allow localized name table entries.
-
- By default, both Windows (platformID=3) and Macintosh (platformID=1) name
- records are added, unless any of `windows` or `mac` arguments is False.
-
- The following descriptive names are available for nameIDs:
-
- copyright (nameID 0)
- familyName (nameID 1)
- styleName (nameID 2)
- uniqueFontIdentifier (nameID 3)
- fullName (nameID 4)
- version (nameID 5)
- psName (nameID 6)
- trademark (nameID 7)
- manufacturer (nameID 8)
- designer (nameID 9)
- description (nameID 10)
- vendorURL (nameID 11)
- designerURL (nameID 12)
- licenseDescription (nameID 13)
- licenseInfoURL (nameID 14)
- typographicFamily (nameID 16)
- typographicSubfamily (nameID 17)
- compatibleFullName (nameID 18)
- sampleText (nameID 19)
- postScriptCIDFindfontName (nameID 20)
- wwsFamilyName (nameID 21)
- wwsSubfamilyName (nameID 22)
- lightBackgroundPalette (nameID 23)
- darkBackgroundPalette (nameID 24)
- variationsPostScriptNamePrefix (nameID 25)
- """
- nameTable = self.font["name"] = newTable("name")
- nameTable.names = []
-
- for nameName, nameValue in nameStrings.items():
- if isinstance(nameName, int):
- nameID = nameName
- else:
- nameID = _nameIDs[nameName]
- if isinstance(nameValue, str):
- nameValue = dict(en=nameValue)
- nameTable.addMultilingualName(
- nameValue, ttFont=self.font, nameID=nameID, windows=windows, mac=mac
- )
-
- def setupOS2(self, **values):
- """Create a new `OS/2` table and initialize it with default values,
- which can be overridden by keyword arguments.
- """
- self._initTableWithValues("OS/2", _OS2Defaults, values)
- if "xAvgCharWidth" not in values:
- assert (
- "hmtx" in self.font
- ), "the 'hmtx' table must be setup before the 'OS/2' table"
- self.font["OS/2"].recalcAvgCharWidth(self.font)
- if not (
- "ulUnicodeRange1" in values
- or "ulUnicodeRange2" in values
- or "ulUnicodeRange3" in values
- or "ulUnicodeRange3" in values
- ):
- assert (
- "cmap" in self.font
- ), "the 'cmap' table must be setup before the 'OS/2' table"
- self.font["OS/2"].recalcUnicodeRanges(self.font)
-
- def setupCFF(self, psName, fontInfo, charStringsDict, privateDict):
- from .cffLib import (
- CFFFontSet,
- TopDictIndex,
- TopDict,
- CharStrings,
- GlobalSubrsIndex,
- PrivateDict,
- )
-
- assert not self.isTTF
- self.font.sfntVersion = "OTTO"
- fontSet = CFFFontSet()
- fontSet.major = 1
- fontSet.minor = 0
- fontSet.otFont = self.font
- fontSet.fontNames = [psName]
- fontSet.topDictIndex = TopDictIndex()
-
- globalSubrs = GlobalSubrsIndex()
- fontSet.GlobalSubrs = globalSubrs
- private = PrivateDict()
- for key, value in privateDict.items():
- setattr(private, key, value)
- fdSelect = None
- fdArray = None
-
- topDict = TopDict()
- topDict.charset = self.font.getGlyphOrder()
- topDict.Private = private
- topDict.GlobalSubrs = fontSet.GlobalSubrs
- for key, value in fontInfo.items():
- setattr(topDict, key, value)
- if "FontMatrix" not in fontInfo:
- scale = 1 / self.font["head"].unitsPerEm
- topDict.FontMatrix = [scale, 0, 0, scale, 0, 0]
-
- charStrings = CharStrings(
- None, topDict.charset, globalSubrs, private, fdSelect, fdArray
- )
- for glyphName, charString in charStringsDict.items():
- charString.private = private
- charString.globalSubrs = globalSubrs
- charStrings[glyphName] = charString
- topDict.CharStrings = charStrings
-
- fontSet.topDictIndex.append(topDict)
-
- self.font["CFF "] = newTable("CFF ")
- self.font["CFF "].cff = fontSet
-
- def setupCFF2(self, charStringsDict, fdArrayList=None, regions=None):
- from .cffLib import (
- CFFFontSet,
- TopDictIndex,
- TopDict,
- CharStrings,
- GlobalSubrsIndex,
- PrivateDict,
- FDArrayIndex,
- FontDict,
- )
-
- assert not self.isTTF
- self.font.sfntVersion = "OTTO"
- fontSet = CFFFontSet()
- fontSet.major = 2
- fontSet.minor = 0
-
- cff2GetGlyphOrder = self.font.getGlyphOrder
- fontSet.topDictIndex = TopDictIndex(None, cff2GetGlyphOrder, None)
-
- globalSubrs = GlobalSubrsIndex()
- fontSet.GlobalSubrs = globalSubrs
-
- if fdArrayList is None:
- fdArrayList = [{}]
- fdSelect = None
- fdArray = FDArrayIndex()
- fdArray.strings = None
- fdArray.GlobalSubrs = globalSubrs
- for privateDict in fdArrayList:
- fontDict = FontDict()
- fontDict.setCFF2(True)
- private = PrivateDict()
- for key, value in privateDict.items():
- setattr(private, key, value)
- fontDict.Private = private
- fdArray.append(fontDict)
-
- topDict = TopDict()
- topDict.cff2GetGlyphOrder = cff2GetGlyphOrder
- topDict.FDArray = fdArray
- scale = 1 / self.font["head"].unitsPerEm
- topDict.FontMatrix = [scale, 0, 0, scale, 0, 0]
-
- private = fdArray[0].Private
- charStrings = CharStrings(None, None, globalSubrs, private, fdSelect, fdArray)
- for glyphName, charString in charStringsDict.items():
- charString.private = private
- charString.globalSubrs = globalSubrs
- charStrings[glyphName] = charString
- topDict.CharStrings = charStrings
-
- fontSet.topDictIndex.append(topDict)
-
- self.font["CFF2"] = newTable("CFF2")
- self.font["CFF2"].cff = fontSet
-
- if regions:
- self.setupCFF2Regions(regions)
-
- def setupCFF2Regions(self, regions):
- from .varLib.builder import buildVarRegionList, buildVarData, buildVarStore
- from .cffLib import VarStoreData
-
- assert "fvar" in self.font, "fvar must to be set up first"
- assert "CFF2" in self.font, "CFF2 must to be set up first"
- axisTags = [a.axisTag for a in self.font["fvar"].axes]
- varRegionList = buildVarRegionList(regions, axisTags)
- varData = buildVarData(list(range(len(regions))), None, optimize=False)
- varStore = buildVarStore(varRegionList, [varData])
- vstore = VarStoreData(otVarStore=varStore)
- topDict = self.font["CFF2"].cff.topDictIndex[0]
- topDict.VarStore = vstore
- for fontDict in topDict.FDArray:
- fontDict.Private.vstore = vstore
-
- def setupGlyf(self, glyphs, calcGlyphBounds=True, validateGlyphFormat=True):
- """Create the `glyf` table from a dict, that maps glyph names
- to `fontTools.ttLib.tables._g_l_y_f.Glyph` objects, for example
- as made by `fontTools.pens.ttGlyphPen.TTGlyphPen`.
-
- If `calcGlyphBounds` is True, the bounds of all glyphs will be
- calculated. Only pass False if your glyph objects already have
- their bounding box values set.
-
- If `validateGlyphFormat` is True, raise ValueError if any of the glyphs contains
- cubic curves or is a variable composite but head.glyphDataFormat=0.
- Set it to False to skip the check if you know in advance all the glyphs are
- compatible with the specified glyphDataFormat.
- """
- assert self.isTTF
-
- if validateGlyphFormat and self.font["head"].glyphDataFormat == 0:
- for name, g in glyphs.items():
- if g.isVarComposite():
- raise ValueError(
- f"Glyph {name!r} is a variable composite, but glyphDataFormat=0"
- )
- elif g.numberOfContours > 0 and any(f & flagCubic for f in g.flags):
- raise ValueError(
- f"Glyph {name!r} has cubic Bezier outlines, but glyphDataFormat=0; "
- "either convert to quadratics with cu2qu or set glyphDataFormat=1."
- )
-
- self.font["loca"] = newTable("loca")
- self.font["glyf"] = newTable("glyf")
- self.font["glyf"].glyphs = glyphs
- if hasattr(self.font, "glyphOrder"):
- self.font["glyf"].glyphOrder = self.font.glyphOrder
- if calcGlyphBounds:
- self.calcGlyphBounds()
-
- def setupFvar(self, axes, instances):
- """Adds an font variations table to the font.
-
- Args:
- axes (list): See below.
- instances (list): See below.
-
- ``axes`` should be a list of axes, with each axis either supplied as
- a py:class:`.designspaceLib.AxisDescriptor` object, or a tuple in the
- format ```tupletag, minValue, defaultValue, maxValue, name``.
- The ``name`` is either a string, or a dict, mapping language codes
- to strings, to allow localized name table entries.
-
- ```instances`` should be a list of instances, with each instance either
- supplied as a py:class:`.designspaceLib.InstanceDescriptor` object, or a
- dict with keys ``location`` (mapping of axis tags to float values),
- ``stylename`` and (optionally) ``postscriptfontname``.
- The ``stylename`` is either a string, or a dict, mapping language codes
- to strings, to allow localized name table entries.
- """
-
- addFvar(self.font, axes, instances)
-
- def setupAvar(self, axes):
- """Adds an axis variations table to the font.
-
- Args:
- axes (list): A list of py:class:`.designspaceLib.AxisDescriptor` objects.
- """
- from .varLib import _add_avar
-
- _add_avar(self.font, OrderedDict(enumerate(axes))) # Only values are used
-
- def setupGvar(self, variations):
- gvar = self.font["gvar"] = newTable("gvar")
- gvar.version = 1
- gvar.reserved = 0
- gvar.variations = variations
-
- def calcGlyphBounds(self):
- """Calculate the bounding boxes of all glyphs in the `glyf` table.
- This is usually not called explicitly by client code.
- """
- glyphTable = self.font["glyf"]
- for glyph in glyphTable.glyphs.values():
- glyph.recalcBounds(glyphTable)
-
- def setupHorizontalMetrics(self, metrics):
- """Create a new `hmtx` table, for horizontal metrics.
-
- The `metrics` argument must be a dict, mapping glyph names to
- `(width, leftSidebearing)` tuples.
- """
- self.setupMetrics("hmtx", metrics)
-
- def setupVerticalMetrics(self, metrics):
- """Create a new `vmtx` table, for horizontal metrics.
-
- The `metrics` argument must be a dict, mapping glyph names to
- `(height, topSidebearing)` tuples.
- """
- self.setupMetrics("vmtx", metrics)
-
- def setupMetrics(self, tableTag, metrics):
- """See `setupHorizontalMetrics()` and `setupVerticalMetrics()`."""
- assert tableTag in ("hmtx", "vmtx")
- mtxTable = self.font[tableTag] = newTable(tableTag)
- roundedMetrics = {}
- for gn in metrics:
- w, lsb = metrics[gn]
- roundedMetrics[gn] = int(round(w)), int(round(lsb))
- mtxTable.metrics = roundedMetrics
-
- def setupHorizontalHeader(self, **values):
- """Create a new `hhea` table initialize it with default values,
- which can be overridden by keyword arguments.
- """
- self._initTableWithValues("hhea", _hheaDefaults, values)
-
- def setupVerticalHeader(self, **values):
- """Create a new `vhea` table initialize it with default values,
- which can be overridden by keyword arguments.
- """
- self._initTableWithValues("vhea", _vheaDefaults, values)
-
- def setupVerticalOrigins(self, verticalOrigins, defaultVerticalOrigin=None):
- """Create a new `VORG` table. The `verticalOrigins` argument must be
- a dict, mapping glyph names to vertical origin values.
-
- The `defaultVerticalOrigin` argument should be the most common vertical
- origin value. If omitted, this value will be derived from the actual
- values in the `verticalOrigins` argument.
- """
- if defaultVerticalOrigin is None:
- # find the most frequent vorg value
- bag = {}
- for gn in verticalOrigins:
- vorg = verticalOrigins[gn]
- if vorg not in bag:
- bag[vorg] = 1
- else:
- bag[vorg] += 1
- defaultVerticalOrigin = sorted(
- bag, key=lambda vorg: bag[vorg], reverse=True
- )[0]
- self._initTableWithValues(
- "VORG",
- {},
- dict(VOriginRecords={}, defaultVertOriginY=defaultVerticalOrigin),
- )
- vorgTable = self.font["VORG"]
- vorgTable.majorVersion = 1
- vorgTable.minorVersion = 0
- for gn in verticalOrigins:
- vorgTable[gn] = verticalOrigins[gn]
-
- def setupPost(self, keepGlyphNames=True, **values):
- """Create a new `post` table and initialize it with default values,
- which can be overridden by keyword arguments.
- """
- isCFF2 = "CFF2" in self.font
- postTable = self._initTableWithValues("post", _postDefaults, values)
- if (self.isTTF or isCFF2) and keepGlyphNames:
- postTable.formatType = 2.0
- postTable.extraNames = []
- postTable.mapping = {}
- else:
- postTable.formatType = 3.0
-
- def setupMaxp(self):
- """Create a new `maxp` table. This is called implicitly by FontBuilder
- itself and is usually not called by client code.
- """
- if self.isTTF:
- defaults = _maxpDefaultsTTF
- else:
- defaults = _maxpDefaultsOTF
- self._initTableWithValues("maxp", defaults, {})
-
- def setupDummyDSIG(self):
- """This adds an empty DSIG table to the font to make some MS applications
- happy. This does not properly sign the font.
- """
- values = dict(
- ulVersion=1,
- usFlag=0,
- usNumSigs=0,
- signatureRecords=[],
- )
- self._initTableWithValues("DSIG", {}, values)
-
- def addOpenTypeFeatures(self, features, filename=None, tables=None, debug=False):
- """Add OpenType features to the font from a string containing
- Feature File syntax.
-
- The `filename` argument is used in error messages and to determine
- where to look for "include" files.
-
- The optional `tables` argument can be a list of OTL tables tags to
- build, allowing the caller to only build selected OTL tables. See
- `fontTools.feaLib` for details.
-
- The optional `debug` argument controls whether to add source debugging
- information to the font in the `Debg` table.
- """
- from .feaLib.builder import addOpenTypeFeaturesFromString
-
- addOpenTypeFeaturesFromString(
- self.font, features, filename=filename, tables=tables, debug=debug
- )
-
- def addFeatureVariations(self, conditionalSubstitutions, featureTag="rvrn"):
- """Add conditional substitutions to a Variable Font.
-
- See `fontTools.varLib.featureVars.addFeatureVariations`.
- """
- from .varLib import featureVars
-
- if "fvar" not in self.font:
- raise KeyError("'fvar' table is missing; can't add FeatureVariations.")
-
- featureVars.addFeatureVariations(
- self.font, conditionalSubstitutions, featureTag=featureTag
- )
-
- def setupCOLR(
- self,
- colorLayers,
- version=None,
- varStore=None,
- varIndexMap=None,
- clipBoxes=None,
- allowLayerReuse=True,
- ):
- """Build new COLR table using color layers dictionary.
-
- Cf. `fontTools.colorLib.builder.buildCOLR`.
- """
- from fontTools.colorLib.builder import buildCOLR
-
- glyphMap = self.font.getReverseGlyphMap()
- self.font["COLR"] = buildCOLR(
- colorLayers,
- version=version,
- glyphMap=glyphMap,
- varStore=varStore,
- varIndexMap=varIndexMap,
- clipBoxes=clipBoxes,
- allowLayerReuse=allowLayerReuse,
- )
-
- def setupCPAL(
- self,
- palettes,
- paletteTypes=None,
- paletteLabels=None,
- paletteEntryLabels=None,
- ):
- """Build new CPAL table using list of palettes.
-
- Optionally build CPAL v1 table using paletteTypes, paletteLabels and
- paletteEntryLabels.
-
- Cf. `fontTools.colorLib.builder.buildCPAL`.
- """
- from fontTools.colorLib.builder import buildCPAL
-
- self.font["CPAL"] = buildCPAL(
- palettes,
- paletteTypes=paletteTypes,
- paletteLabels=paletteLabels,
- paletteEntryLabels=paletteEntryLabels,
- nameTable=self.font.get("name"),
- )
-
- def setupStat(self, axes, locations=None, elidedFallbackName=2):
- """Build a new 'STAT' table.
-
- See `fontTools.otlLib.builder.buildStatTable` for details about
- the arguments.
- """
- from .otlLib.builder import buildStatTable
-
- buildStatTable(self.font, axes, locations, elidedFallbackName)
-
-
-def buildCmapSubTable(cmapping, format, platformID, platEncID):
- subTable = cmap_classes[format](format)
- subTable.cmap = cmapping
- subTable.platformID = platformID
- subTable.platEncID = platEncID
- subTable.language = 0
- return subTable
-
-
-def addFvar(font, axes, instances):
- from .ttLib.tables._f_v_a_r import Axis, NamedInstance
-
- assert axes
-
- fvar = newTable("fvar")
- nameTable = font["name"]
-
- for axis_def in axes:
- axis = Axis()
-
- if isinstance(axis_def, tuple):
- (
- axis.axisTag,
- axis.minValue,
- axis.defaultValue,
- axis.maxValue,
- name,
- ) = axis_def
- else:
- (axis.axisTag, axis.minValue, axis.defaultValue, axis.maxValue, name) = (
- axis_def.tag,
- axis_def.minimum,
- axis_def.default,
- axis_def.maximum,
- axis_def.name,
- )
-
- if isinstance(name, str):
- name = dict(en=name)
-
- axis.axisNameID = nameTable.addMultilingualName(name, ttFont=font)
- fvar.axes.append(axis)
-
- for instance in instances:
- if isinstance(instance, dict):
- coordinates = instance["location"]
- name = instance["stylename"]
- psname = instance.get("postscriptfontname")
- else:
- coordinates = instance.location
- name = instance.localisedStyleName or instance.styleName
- psname = instance.postScriptFontName
-
- if isinstance(name, str):
- name = dict(en=name)
-
- inst = NamedInstance()
- inst.subfamilyNameID = nameTable.addMultilingualName(name, ttFont=font)
- if psname is not None:
- inst.postscriptNameID = nameTable.addName(psname)
- inst.coordinates = coordinates
- fvar.instances.append(inst)
-
- font["fvar"] = fvar
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/help.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/help.py
deleted file mode 100644
index 4334e50..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/help.py
+++ /dev/null
@@ -1,35 +0,0 @@
-import pkgutil
-import sys
-import fontTools
-import importlib
-import os
-from pathlib import Path
-
-
-def main():
- """Show this help"""
- path = fontTools.__path__
- descriptions = {}
- for pkg in sorted(
- mod.name
- for mod in pkgutil.walk_packages([fontTools.__path__[0]], prefix="fontTools.")
- ):
- try:
- imports = __import__(pkg, globals(), locals(), ["main"])
- except ImportError as e:
- continue
- try:
- description = imports.main.__doc__
- if description:
- pkg = pkg.replace("fontTools.", "").replace(".__main__", "")
- # show the docstring's first line only
- descriptions[pkg] = description.splitlines()[0]
- except AttributeError as e:
- pass
- for pkg, description in descriptions.items():
- print("fonttools %-12s %s" % (pkg, description), file=sys.stderr)
-
-
-if __name__ == "__main__":
- print("fonttools v%s\n" % fontTools.__version__, file=sys.stderr)
- main()
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/__init__.py
deleted file mode 100644
index 8d8a521..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/__init__.py
+++ /dev/null
@@ -1,210 +0,0 @@
-# Copyright 2013 Google, Inc. All Rights Reserved.
-#
-# Google Author(s): Behdad Esfahbod, Roozbeh Pournader
-
-from fontTools import ttLib
-import fontTools.merge.base
-from fontTools.merge.cmap import (
- computeMegaGlyphOrder,
- computeMegaCmap,
- renameCFFCharStrings,
-)
-from fontTools.merge.layout import layoutPreMerge, layoutPostMerge
-from fontTools.merge.options import Options
-import fontTools.merge.tables
-from fontTools.misc.loggingTools import Timer
-from functools import reduce
-import sys
-import logging
-
-
-log = logging.getLogger("fontTools.merge")
-timer = Timer(logger=logging.getLogger(__name__ + ".timer"), level=logging.INFO)
-
-
-class Merger(object):
- """Font merger.
-
- This class merges multiple files into a single OpenType font, taking into
- account complexities such as OpenType layout (``GSUB``/``GPOS``) tables and
- cross-font metrics (e.g. ``hhea.ascent`` is set to the maximum value across
- all the fonts).
-
- If multiple glyphs map to the same Unicode value, and the glyphs are considered
- sufficiently different (that is, they differ in any of paths, widths, or
- height), then subsequent glyphs are renamed and a lookup in the ``locl``
- feature will be created to disambiguate them. For example, if the arguments
- are an Arabic font and a Latin font and both contain a set of parentheses,
- the Latin glyphs will be renamed to ``parenleft#1`` and ``parenright#1``,
- and a lookup will be inserted into the to ``locl`` feature (creating it if
- necessary) under the ``latn`` script to substitute ``parenleft`` with
- ``parenleft#1`` etc.
-
- Restrictions:
-
- - All fonts must have the same units per em.
- - If duplicate glyph disambiguation takes place as described above then the
- fonts must have a ``GSUB`` table.
-
- Attributes:
- options: Currently unused.
- """
-
- def __init__(self, options=None):
- if not options:
- options = Options()
-
- self.options = options
-
- def _openFonts(self, fontfiles):
- fonts = [ttLib.TTFont(fontfile) for fontfile in fontfiles]
- for font, fontfile in zip(fonts, fontfiles):
- font._merger__fontfile = fontfile
- font._merger__name = font["name"].getDebugName(4)
- return fonts
-
- def merge(self, fontfiles):
- """Merges fonts together.
-
- Args:
- fontfiles: A list of file names to be merged
-
- Returns:
- A :class:`fontTools.ttLib.TTFont` object. Call the ``save`` method on
- this to write it out to an OTF file.
- """
- #
- # Settle on a mega glyph order.
- #
- fonts = self._openFonts(fontfiles)
- glyphOrders = [list(font.getGlyphOrder()) for font in fonts]
- computeMegaGlyphOrder(self, glyphOrders)
-
- # Take first input file sfntVersion
- sfntVersion = fonts[0].sfntVersion
-
- # Reload fonts and set new glyph names on them.
- fonts = self._openFonts(fontfiles)
- for font, glyphOrder in zip(fonts, glyphOrders):
- font.setGlyphOrder(glyphOrder)
- if "CFF " in font:
- renameCFFCharStrings(self, glyphOrder, font["CFF "])
-
- cmaps = [font["cmap"] for font in fonts]
- self.duplicateGlyphsPerFont = [{} for _ in fonts]
- computeMegaCmap(self, cmaps)
-
- mega = ttLib.TTFont(sfntVersion=sfntVersion)
- mega.setGlyphOrder(self.glyphOrder)
-
- for font in fonts:
- self._preMerge(font)
-
- self.fonts = fonts
-
- allTags = reduce(set.union, (list(font.keys()) for font in fonts), set())
- allTags.remove("GlyphOrder")
-
- for tag in sorted(allTags):
- if tag in self.options.drop_tables:
- continue
-
- with timer("merge '%s'" % tag):
- tables = [font.get(tag, NotImplemented) for font in fonts]
-
- log.info("Merging '%s'.", tag)
- clazz = ttLib.getTableClass(tag)
- table = clazz(tag).merge(self, tables)
- # XXX Clean this up and use: table = mergeObjects(tables)
-
- if table is not NotImplemented and table is not False:
- mega[tag] = table
- log.info("Merged '%s'.", tag)
- else:
- log.info("Dropped '%s'.", tag)
-
- del self.duplicateGlyphsPerFont
- del self.fonts
-
- self._postMerge(mega)
-
- return mega
-
- def mergeObjects(self, returnTable, logic, tables):
- # Right now we don't use self at all. Will use in the future
- # for options and logging.
-
- allKeys = set.union(
- set(),
- *(vars(table).keys() for table in tables if table is not NotImplemented),
- )
- for key in allKeys:
- try:
- mergeLogic = logic[key]
- except KeyError:
- try:
- mergeLogic = logic["*"]
- except KeyError:
- raise Exception(
- "Don't know how to merge key %s of class %s"
- % (key, returnTable.__class__.__name__)
- )
- if mergeLogic is NotImplemented:
- continue
- value = mergeLogic(getattr(table, key, NotImplemented) for table in tables)
- if value is not NotImplemented:
- setattr(returnTable, key, value)
-
- return returnTable
-
- def _preMerge(self, font):
- layoutPreMerge(font)
-
- def _postMerge(self, font):
- layoutPostMerge(font)
-
- if "OS/2" in font:
- # https://github.com/fonttools/fonttools/issues/2538
- # TODO: Add an option to disable this?
- font["OS/2"].recalcAvgCharWidth(font)
-
-
-__all__ = ["Options", "Merger", "main"]
-
-
-@timer("make one with everything (TOTAL TIME)")
-def main(args=None):
- """Merge multiple fonts into one"""
- from fontTools import configLogger
-
- if args is None:
- args = sys.argv[1:]
-
- options = Options()
- args = options.parse_opts(args, ignore_unknown=["output-file"])
- outfile = "merged.ttf"
- fontfiles = []
- for g in args:
- if g.startswith("--output-file="):
- outfile = g[14:]
- continue
- fontfiles.append(g)
-
- if len(args) < 1:
- print("usage: pyftmerge font...", file=sys.stderr)
- return 1
-
- configLogger(level=logging.INFO if options.verbose else logging.WARNING)
- if options.timing:
- timer.logger.setLevel(logging.DEBUG)
- else:
- timer.logger.disabled = True
-
- merger = Merger(options=options)
- font = merger.merge(fontfiles)
- with timer("compile and save font"):
- font.save(outfile)
-
-
-if __name__ == "__main__":
- sys.exit(main())
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/__main__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/__main__.py
deleted file mode 100644
index ff632d4..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/__main__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-import sys
-from fontTools.merge import main
-
-
-if __name__ == "__main__":
- sys.exit(main())
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/base.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/base.py
deleted file mode 100644
index 37f9097..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/base.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# Copyright 2013 Google, Inc. All Rights Reserved.
-#
-# Google Author(s): Behdad Esfahbod, Roozbeh Pournader
-
-from fontTools.ttLib.tables.DefaultTable import DefaultTable
-import logging
-
-
-log = logging.getLogger("fontTools.merge")
-
-
-def add_method(*clazzes, **kwargs):
- """Returns a decorator function that adds a new method to one or
- more classes."""
- allowDefault = kwargs.get("allowDefaultTable", False)
-
- def wrapper(method):
- done = []
- for clazz in clazzes:
- if clazz in done:
- continue # Support multiple names of a clazz
- done.append(clazz)
- assert allowDefault or clazz != DefaultTable, "Oops, table class not found."
- assert (
- method.__name__ not in clazz.__dict__
- ), "Oops, class '%s' has method '%s'." % (clazz.__name__, method.__name__)
- setattr(clazz, method.__name__, method)
- return None
-
- return wrapper
-
-
-def mergeObjects(lst):
- lst = [item for item in lst if item is not NotImplemented]
- if not lst:
- return NotImplemented
- lst = [item for item in lst if item is not None]
- if not lst:
- return None
-
- clazz = lst[0].__class__
- assert all(type(item) == clazz for item in lst), lst
-
- logic = clazz.mergeMap
- returnTable = clazz()
- returnDict = {}
-
- allKeys = set.union(set(), *(vars(table).keys() for table in lst))
- for key in allKeys:
- try:
- mergeLogic = logic[key]
- except KeyError:
- try:
- mergeLogic = logic["*"]
- except KeyError:
- raise Exception(
- "Don't know how to merge key %s of class %s" % (key, clazz.__name__)
- )
- if mergeLogic is NotImplemented:
- continue
- value = mergeLogic(getattr(table, key, NotImplemented) for table in lst)
- if value is not NotImplemented:
- returnDict[key] = value
-
- returnTable.__dict__ = returnDict
-
- return returnTable
-
-
-@add_method(DefaultTable, allowDefaultTable=True)
-def merge(self, m, tables):
- if not hasattr(self, "mergeMap"):
- log.info("Don't know how to merge '%s'.", self.tableTag)
- return NotImplemented
-
- logic = self.mergeMap
-
- if isinstance(logic, dict):
- return m.mergeObjects(self, self.mergeMap, tables)
- else:
- return logic(tables)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/cmap.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/cmap.py
deleted file mode 100644
index 3209a5d..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/cmap.py
+++ /dev/null
@@ -1,141 +0,0 @@
-# Copyright 2013 Google, Inc. All Rights Reserved.
-#
-# Google Author(s): Behdad Esfahbod, Roozbeh Pournader
-
-from fontTools.merge.unicode import is_Default_Ignorable
-from fontTools.pens.recordingPen import DecomposingRecordingPen
-import logging
-
-
-log = logging.getLogger("fontTools.merge")
-
-
-def computeMegaGlyphOrder(merger, glyphOrders):
- """Modifies passed-in glyphOrders to reflect new glyph names.
- Stores merger.glyphOrder."""
- megaOrder = {}
- for glyphOrder in glyphOrders:
- for i, glyphName in enumerate(glyphOrder):
- if glyphName in megaOrder:
- n = megaOrder[glyphName]
- while (glyphName + "." + repr(n)) in megaOrder:
- n += 1
- megaOrder[glyphName] = n
- glyphName += "." + repr(n)
- glyphOrder[i] = glyphName
- megaOrder[glyphName] = 1
- merger.glyphOrder = megaOrder = list(megaOrder.keys())
-
-
-def _glyphsAreSame(
- glyphSet1,
- glyphSet2,
- glyph1,
- glyph2,
- advanceTolerance=0.05,
- advanceToleranceEmpty=0.20,
-):
- pen1 = DecomposingRecordingPen(glyphSet1)
- pen2 = DecomposingRecordingPen(glyphSet2)
- g1 = glyphSet1[glyph1]
- g2 = glyphSet2[glyph2]
- g1.draw(pen1)
- g2.draw(pen2)
- if pen1.value != pen2.value:
- return False
- # Allow more width tolerance for glyphs with no ink
- tolerance = advanceTolerance if pen1.value else advanceToleranceEmpty
- # TODO Warn if advances not the same but within tolerance.
- if abs(g1.width - g2.width) > g1.width * tolerance:
- return False
- if hasattr(g1, "height") and g1.height is not None:
- if abs(g1.height - g2.height) > g1.height * tolerance:
- return False
- return True
-
-
-# Valid (format, platformID, platEncID) triplets for cmap subtables containing
-# Unicode BMP-only and Unicode Full Repertoire semantics.
-# Cf. OpenType spec for "Platform specific encodings":
-# https://docs.microsoft.com/en-us/typography/opentype/spec/name
-class _CmapUnicodePlatEncodings:
- BMP = {(4, 3, 1), (4, 0, 3), (4, 0, 4), (4, 0, 6)}
- FullRepertoire = {(12, 3, 10), (12, 0, 4), (12, 0, 6)}
-
-
-def computeMegaCmap(merger, cmapTables):
- """Sets merger.cmap and merger.glyphOrder."""
-
- # TODO Handle format=14.
- # Only merge format 4 and 12 Unicode subtables, ignores all other subtables
- # If there is a format 12 table for a font, ignore the format 4 table of it
- chosenCmapTables = []
- for fontIdx, table in enumerate(cmapTables):
- format4 = None
- format12 = None
- for subtable in table.tables:
- properties = (subtable.format, subtable.platformID, subtable.platEncID)
- if properties in _CmapUnicodePlatEncodings.BMP:
- format4 = subtable
- elif properties in _CmapUnicodePlatEncodings.FullRepertoire:
- format12 = subtable
- else:
- log.warning(
- "Dropped cmap subtable from font '%s':\t"
- "format %2s, platformID %2s, platEncID %2s",
- fontIdx,
- subtable.format,
- subtable.platformID,
- subtable.platEncID,
- )
- if format12 is not None:
- chosenCmapTables.append((format12, fontIdx))
- elif format4 is not None:
- chosenCmapTables.append((format4, fontIdx))
-
- # Build the unicode mapping
- merger.cmap = cmap = {}
- fontIndexForGlyph = {}
- glyphSets = [None for f in merger.fonts] if hasattr(merger, "fonts") else None
-
- for table, fontIdx in chosenCmapTables:
- # handle duplicates
- for uni, gid in table.cmap.items():
- oldgid = cmap.get(uni, None)
- if oldgid is None:
- cmap[uni] = gid
- fontIndexForGlyph[gid] = fontIdx
- elif is_Default_Ignorable(uni) or uni in (0x25CC,): # U+25CC DOTTED CIRCLE
- continue
- elif oldgid != gid:
- # Char previously mapped to oldgid, now to gid.
- # Record, to fix up in GSUB 'locl' later.
- if merger.duplicateGlyphsPerFont[fontIdx].get(oldgid) is None:
- if glyphSets is not None:
- oldFontIdx = fontIndexForGlyph[oldgid]
- for idx in (fontIdx, oldFontIdx):
- if glyphSets[idx] is None:
- glyphSets[idx] = merger.fonts[idx].getGlyphSet()
- # if _glyphsAreSame(glyphSets[oldFontIdx], glyphSets[fontIdx], oldgid, gid):
- # continue
- merger.duplicateGlyphsPerFont[fontIdx][oldgid] = gid
- elif merger.duplicateGlyphsPerFont[fontIdx][oldgid] != gid:
- # Char previously mapped to oldgid but oldgid is already remapped to a different
- # gid, because of another Unicode character.
- # TODO: Try harder to do something about these.
- log.warning(
- "Dropped mapping from codepoint %#06X to glyphId '%s'", uni, gid
- )
-
-
-def renameCFFCharStrings(merger, glyphOrder, cffTable):
- """Rename topDictIndex charStrings based on glyphOrder."""
- td = cffTable.cff.topDictIndex[0]
-
- charStrings = {}
- for i, v in enumerate(td.CharStrings.charStrings.values()):
- glyphName = glyphOrder[i]
- charStrings[glyphName] = v
- td.CharStrings.charStrings = charStrings
-
- td.charset = list(glyphOrder)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/layout.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/layout.py
deleted file mode 100644
index 6b85cd5..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/layout.py
+++ /dev/null
@@ -1,530 +0,0 @@
-# Copyright 2013 Google, Inc. All Rights Reserved.
-#
-# Google Author(s): Behdad Esfahbod, Roozbeh Pournader
-
-from fontTools import ttLib
-from fontTools.ttLib.tables.DefaultTable import DefaultTable
-from fontTools.ttLib.tables import otTables
-from fontTools.merge.base import add_method, mergeObjects
-from fontTools.merge.util import *
-import logging
-
-
-log = logging.getLogger("fontTools.merge")
-
-
-def mergeLookupLists(lst):
- # TODO Do smarter merge.
- return sumLists(lst)
-
-
-def mergeFeatures(lst):
- assert lst
- self = otTables.Feature()
- self.FeatureParams = None
- self.LookupListIndex = mergeLookupLists(
- [l.LookupListIndex for l in lst if l.LookupListIndex]
- )
- self.LookupCount = len(self.LookupListIndex)
- return self
-
-
-def mergeFeatureLists(lst):
- d = {}
- for l in lst:
- for f in l:
- tag = f.FeatureTag
- if tag not in d:
- d[tag] = []
- d[tag].append(f.Feature)
- ret = []
- for tag in sorted(d.keys()):
- rec = otTables.FeatureRecord()
- rec.FeatureTag = tag
- rec.Feature = mergeFeatures(d[tag])
- ret.append(rec)
- return ret
-
-
-def mergeLangSyses(lst):
- assert lst
-
- # TODO Support merging ReqFeatureIndex
- assert all(l.ReqFeatureIndex == 0xFFFF for l in lst)
-
- self = otTables.LangSys()
- self.LookupOrder = None
- self.ReqFeatureIndex = 0xFFFF
- self.FeatureIndex = mergeFeatureLists(
- [l.FeatureIndex for l in lst if l.FeatureIndex]
- )
- self.FeatureCount = len(self.FeatureIndex)
- return self
-
-
-def mergeScripts(lst):
- assert lst
-
- if len(lst) == 1:
- return lst[0]
- langSyses = {}
- for sr in lst:
- for lsr in sr.LangSysRecord:
- if lsr.LangSysTag not in langSyses:
- langSyses[lsr.LangSysTag] = []
- langSyses[lsr.LangSysTag].append(lsr.LangSys)
- lsrecords = []
- for tag, langSys_list in sorted(langSyses.items()):
- lsr = otTables.LangSysRecord()
- lsr.LangSys = mergeLangSyses(langSys_list)
- lsr.LangSysTag = tag
- lsrecords.append(lsr)
-
- self = otTables.Script()
- self.LangSysRecord = lsrecords
- self.LangSysCount = len(lsrecords)
- dfltLangSyses = [s.DefaultLangSys for s in lst if s.DefaultLangSys]
- if dfltLangSyses:
- self.DefaultLangSys = mergeLangSyses(dfltLangSyses)
- else:
- self.DefaultLangSys = None
- return self
-
-
-def mergeScriptRecords(lst):
- d = {}
- for l in lst:
- for s in l:
- tag = s.ScriptTag
- if tag not in d:
- d[tag] = []
- d[tag].append(s.Script)
- ret = []
- for tag in sorted(d.keys()):
- rec = otTables.ScriptRecord()
- rec.ScriptTag = tag
- rec.Script = mergeScripts(d[tag])
- ret.append(rec)
- return ret
-
-
-otTables.ScriptList.mergeMap = {
- "ScriptCount": lambda lst: None, # TODO
- "ScriptRecord": mergeScriptRecords,
-}
-otTables.BaseScriptList.mergeMap = {
- "BaseScriptCount": lambda lst: None, # TODO
- # TODO: Merge duplicate entries
- "BaseScriptRecord": lambda lst: sorted(
- sumLists(lst), key=lambda s: s.BaseScriptTag
- ),
-}
-
-otTables.FeatureList.mergeMap = {
- "FeatureCount": sum,
- "FeatureRecord": lambda lst: sorted(sumLists(lst), key=lambda s: s.FeatureTag),
-}
-
-otTables.LookupList.mergeMap = {
- "LookupCount": sum,
- "Lookup": sumLists,
-}
-
-otTables.Coverage.mergeMap = {
- "Format": min,
- "glyphs": sumLists,
-}
-
-otTables.ClassDef.mergeMap = {
- "Format": min,
- "classDefs": sumDicts,
-}
-
-otTables.LigCaretList.mergeMap = {
- "Coverage": mergeObjects,
- "LigGlyphCount": sum,
- "LigGlyph": sumLists,
-}
-
-otTables.AttachList.mergeMap = {
- "Coverage": mergeObjects,
- "GlyphCount": sum,
- "AttachPoint": sumLists,
-}
-
-# XXX Renumber MarkFilterSets of lookups
-otTables.MarkGlyphSetsDef.mergeMap = {
- "MarkSetTableFormat": equal,
- "MarkSetCount": sum,
- "Coverage": sumLists,
-}
-
-otTables.Axis.mergeMap = {
- "*": mergeObjects,
-}
-
-# XXX Fix BASE table merging
-otTables.BaseTagList.mergeMap = {
- "BaseTagCount": sum,
- "BaselineTag": sumLists,
-}
-
-otTables.GDEF.mergeMap = (
- otTables.GSUB.mergeMap
-) = (
- otTables.GPOS.mergeMap
-) = otTables.BASE.mergeMap = otTables.JSTF.mergeMap = otTables.MATH.mergeMap = {
- "*": mergeObjects,
- "Version": max,
-}
-
-ttLib.getTableClass("GDEF").mergeMap = ttLib.getTableClass(
- "GSUB"
-).mergeMap = ttLib.getTableClass("GPOS").mergeMap = ttLib.getTableClass(
- "BASE"
-).mergeMap = ttLib.getTableClass(
- "JSTF"
-).mergeMap = ttLib.getTableClass(
- "MATH"
-).mergeMap = {
- "tableTag": onlyExisting(equal), # XXX clean me up
- "table": mergeObjects,
-}
-
-
-@add_method(ttLib.getTableClass("GSUB"))
-def merge(self, m, tables):
- assert len(tables) == len(m.duplicateGlyphsPerFont)
- for i, (table, dups) in enumerate(zip(tables, m.duplicateGlyphsPerFont)):
- if not dups:
- continue
- if table is None or table is NotImplemented:
- log.warning(
- "Have non-identical duplicates to resolve for '%s' but no GSUB. Are duplicates intended?: %s",
- m.fonts[i]._merger__name,
- dups,
- )
- continue
-
- synthFeature = None
- synthLookup = None
- for script in table.table.ScriptList.ScriptRecord:
- if script.ScriptTag == "DFLT":
- continue # XXX
- for langsys in [script.Script.DefaultLangSys] + [
- l.LangSys for l in script.Script.LangSysRecord
- ]:
- if langsys is None:
- continue # XXX Create!
- feature = [v for v in langsys.FeatureIndex if v.FeatureTag == "locl"]
- assert len(feature) <= 1
- if feature:
- feature = feature[0]
- else:
- if not synthFeature:
- synthFeature = otTables.FeatureRecord()
- synthFeature.FeatureTag = "locl"
- f = synthFeature.Feature = otTables.Feature()
- f.FeatureParams = None
- f.LookupCount = 0
- f.LookupListIndex = []
- table.table.FeatureList.FeatureRecord.append(synthFeature)
- table.table.FeatureList.FeatureCount += 1
- feature = synthFeature
- langsys.FeatureIndex.append(feature)
- langsys.FeatureIndex.sort(key=lambda v: v.FeatureTag)
-
- if not synthLookup:
- subtable = otTables.SingleSubst()
- subtable.mapping = dups
- synthLookup = otTables.Lookup()
- synthLookup.LookupFlag = 0
- synthLookup.LookupType = 1
- synthLookup.SubTableCount = 1
- synthLookup.SubTable = [subtable]
- if table.table.LookupList is None:
- # mtiLib uses None as default value for LookupList,
- # while feaLib points to an empty array with count 0
- # TODO: make them do the same
- table.table.LookupList = otTables.LookupList()
- table.table.LookupList.Lookup = []
- table.table.LookupList.LookupCount = 0
- table.table.LookupList.Lookup.append(synthLookup)
- table.table.LookupList.LookupCount += 1
-
- if feature.Feature.LookupListIndex[:1] != [synthLookup]:
- feature.Feature.LookupListIndex[:0] = [synthLookup]
- feature.Feature.LookupCount += 1
-
- DefaultTable.merge(self, m, tables)
- return self
-
-
-@add_method(
- otTables.SingleSubst,
- otTables.MultipleSubst,
- otTables.AlternateSubst,
- otTables.LigatureSubst,
- otTables.ReverseChainSingleSubst,
- otTables.SinglePos,
- otTables.PairPos,
- otTables.CursivePos,
- otTables.MarkBasePos,
- otTables.MarkLigPos,
- otTables.MarkMarkPos,
-)
-def mapLookups(self, lookupMap):
- pass
-
-
-# Copied and trimmed down from subset.py
-@add_method(
- otTables.ContextSubst,
- otTables.ChainContextSubst,
- otTables.ContextPos,
- otTables.ChainContextPos,
-)
-def __merge_classify_context(self):
- class ContextHelper(object):
- def __init__(self, klass, Format):
- if klass.__name__.endswith("Subst"):
- Typ = "Sub"
- Type = "Subst"
- else:
- Typ = "Pos"
- Type = "Pos"
- if klass.__name__.startswith("Chain"):
- Chain = "Chain"
- else:
- Chain = ""
- ChainTyp = Chain + Typ
-
- self.Typ = Typ
- self.Type = Type
- self.Chain = Chain
- self.ChainTyp = ChainTyp
-
- self.LookupRecord = Type + "LookupRecord"
-
- if Format == 1:
- self.Rule = ChainTyp + "Rule"
- self.RuleSet = ChainTyp + "RuleSet"
- elif Format == 2:
- self.Rule = ChainTyp + "ClassRule"
- self.RuleSet = ChainTyp + "ClassSet"
-
- if self.Format not in [1, 2, 3]:
- return None # Don't shoot the messenger; let it go
- if not hasattr(self.__class__, "_merge__ContextHelpers"):
- self.__class__._merge__ContextHelpers = {}
- if self.Format not in self.__class__._merge__ContextHelpers:
- helper = ContextHelper(self.__class__, self.Format)
- self.__class__._merge__ContextHelpers[self.Format] = helper
- return self.__class__._merge__ContextHelpers[self.Format]
-
-
-@add_method(
- otTables.ContextSubst,
- otTables.ChainContextSubst,
- otTables.ContextPos,
- otTables.ChainContextPos,
-)
-def mapLookups(self, lookupMap):
- c = self.__merge_classify_context()
-
- if self.Format in [1, 2]:
- for rs in getattr(self, c.RuleSet):
- if not rs:
- continue
- for r in getattr(rs, c.Rule):
- if not r:
- continue
- for ll in getattr(r, c.LookupRecord):
- if not ll:
- continue
- ll.LookupListIndex = lookupMap[ll.LookupListIndex]
- elif self.Format == 3:
- for ll in getattr(self, c.LookupRecord):
- if not ll:
- continue
- ll.LookupListIndex = lookupMap[ll.LookupListIndex]
- else:
- assert 0, "unknown format: %s" % self.Format
-
-
-@add_method(otTables.ExtensionSubst, otTables.ExtensionPos)
-def mapLookups(self, lookupMap):
- if self.Format == 1:
- self.ExtSubTable.mapLookups(lookupMap)
- else:
- assert 0, "unknown format: %s" % self.Format
-
-
-@add_method(otTables.Lookup)
-def mapLookups(self, lookupMap):
- for st in self.SubTable:
- if not st:
- continue
- st.mapLookups(lookupMap)
-
-
-@add_method(otTables.LookupList)
-def mapLookups(self, lookupMap):
- for l in self.Lookup:
- if not l:
- continue
- l.mapLookups(lookupMap)
-
-
-@add_method(otTables.Lookup)
-def mapMarkFilteringSets(self, markFilteringSetMap):
- if self.LookupFlag & 0x0010:
- self.MarkFilteringSet = markFilteringSetMap[self.MarkFilteringSet]
-
-
-@add_method(otTables.LookupList)
-def mapMarkFilteringSets(self, markFilteringSetMap):
- for l in self.Lookup:
- if not l:
- continue
- l.mapMarkFilteringSets(markFilteringSetMap)
-
-
-@add_method(otTables.Feature)
-def mapLookups(self, lookupMap):
- self.LookupListIndex = [lookupMap[i] for i in self.LookupListIndex]
-
-
-@add_method(otTables.FeatureList)
-def mapLookups(self, lookupMap):
- for f in self.FeatureRecord:
- if not f or not f.Feature:
- continue
- f.Feature.mapLookups(lookupMap)
-
-
-@add_method(otTables.DefaultLangSys, otTables.LangSys)
-def mapFeatures(self, featureMap):
- self.FeatureIndex = [featureMap[i] for i in self.FeatureIndex]
- if self.ReqFeatureIndex != 65535:
- self.ReqFeatureIndex = featureMap[self.ReqFeatureIndex]
-
-
-@add_method(otTables.Script)
-def mapFeatures(self, featureMap):
- if self.DefaultLangSys:
- self.DefaultLangSys.mapFeatures(featureMap)
- for l in self.LangSysRecord:
- if not l or not l.LangSys:
- continue
- l.LangSys.mapFeatures(featureMap)
-
-
-@add_method(otTables.ScriptList)
-def mapFeatures(self, featureMap):
- for s in self.ScriptRecord:
- if not s or not s.Script:
- continue
- s.Script.mapFeatures(featureMap)
-
-
-def layoutPreMerge(font):
- # Map indices to references
-
- GDEF = font.get("GDEF")
- GSUB = font.get("GSUB")
- GPOS = font.get("GPOS")
-
- for t in [GSUB, GPOS]:
- if not t:
- continue
-
- if t.table.LookupList:
- lookupMap = {i: v for i, v in enumerate(t.table.LookupList.Lookup)}
- t.table.LookupList.mapLookups(lookupMap)
- t.table.FeatureList.mapLookups(lookupMap)
-
- if (
- GDEF
- and GDEF.table.Version >= 0x00010002
- and GDEF.table.MarkGlyphSetsDef
- ):
- markFilteringSetMap = {
- i: v for i, v in enumerate(GDEF.table.MarkGlyphSetsDef.Coverage)
- }
- t.table.LookupList.mapMarkFilteringSets(markFilteringSetMap)
-
- if t.table.FeatureList and t.table.ScriptList:
- featureMap = {i: v for i, v in enumerate(t.table.FeatureList.FeatureRecord)}
- t.table.ScriptList.mapFeatures(featureMap)
-
- # TODO FeatureParams nameIDs
-
-
-def layoutPostMerge(font):
- # Map references back to indices
-
- GDEF = font.get("GDEF")
- GSUB = font.get("GSUB")
- GPOS = font.get("GPOS")
-
- for t in [GSUB, GPOS]:
- if not t:
- continue
-
- if t.table.FeatureList and t.table.ScriptList:
- # Collect unregistered (new) features.
- featureMap = GregariousIdentityDict(t.table.FeatureList.FeatureRecord)
- t.table.ScriptList.mapFeatures(featureMap)
-
- # Record used features.
- featureMap = AttendanceRecordingIdentityDict(
- t.table.FeatureList.FeatureRecord
- )
- t.table.ScriptList.mapFeatures(featureMap)
- usedIndices = featureMap.s
-
- # Remove unused features
- t.table.FeatureList.FeatureRecord = [
- f
- for i, f in enumerate(t.table.FeatureList.FeatureRecord)
- if i in usedIndices
- ]
-
- # Map back to indices.
- featureMap = NonhashableDict(t.table.FeatureList.FeatureRecord)
- t.table.ScriptList.mapFeatures(featureMap)
-
- t.table.FeatureList.FeatureCount = len(t.table.FeatureList.FeatureRecord)
-
- if t.table.LookupList:
- # Collect unregistered (new) lookups.
- lookupMap = GregariousIdentityDict(t.table.LookupList.Lookup)
- t.table.FeatureList.mapLookups(lookupMap)
- t.table.LookupList.mapLookups(lookupMap)
-
- # Record used lookups.
- lookupMap = AttendanceRecordingIdentityDict(t.table.LookupList.Lookup)
- t.table.FeatureList.mapLookups(lookupMap)
- t.table.LookupList.mapLookups(lookupMap)
- usedIndices = lookupMap.s
-
- # Remove unused lookups
- t.table.LookupList.Lookup = [
- l for i, l in enumerate(t.table.LookupList.Lookup) if i in usedIndices
- ]
-
- # Map back to indices.
- lookupMap = NonhashableDict(t.table.LookupList.Lookup)
- t.table.FeatureList.mapLookups(lookupMap)
- t.table.LookupList.mapLookups(lookupMap)
-
- t.table.LookupList.LookupCount = len(t.table.LookupList.Lookup)
-
- if GDEF and GDEF.table.Version >= 0x00010002:
- markFilteringSetMap = NonhashableDict(
- GDEF.table.MarkGlyphSetsDef.Coverage
- )
- t.table.LookupList.mapMarkFilteringSets(markFilteringSetMap)
-
- # TODO FeatureParams nameIDs
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/options.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/options.py
deleted file mode 100644
index f134009..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/options.py
+++ /dev/null
@@ -1,82 +0,0 @@
-# Copyright 2013 Google, Inc. All Rights Reserved.
-#
-# Google Author(s): Behdad Esfahbod, Roozbeh Pournader
-
-
-class Options(object):
- class UnknownOptionError(Exception):
- pass
-
- def __init__(self, **kwargs):
- self.verbose = False
- self.timing = False
- self.drop_tables = []
-
- self.set(**kwargs)
-
- def set(self, **kwargs):
- for k, v in kwargs.items():
- if not hasattr(self, k):
- raise self.UnknownOptionError("Unknown option '%s'" % k)
- setattr(self, k, v)
-
- def parse_opts(self, argv, ignore_unknown=[]):
- ret = []
- opts = {}
- for a in argv:
- orig_a = a
- if not a.startswith("--"):
- ret.append(a)
- continue
- a = a[2:]
- i = a.find("=")
- op = "="
- if i == -1:
- if a.startswith("no-"):
- k = a[3:]
- v = False
- else:
- k = a
- v = True
- else:
- k = a[:i]
- if k[-1] in "-+":
- op = k[-1] + "=" # Ops is '-=' or '+=' now.
- k = k[:-1]
- v = a[i + 1 :]
- ok = k
- k = k.replace("-", "_")
- if not hasattr(self, k):
- if ignore_unknown is True or ok in ignore_unknown:
- ret.append(orig_a)
- continue
- else:
- raise self.UnknownOptionError("Unknown option '%s'" % a)
-
- ov = getattr(self, k)
- if isinstance(ov, bool):
- v = bool(v)
- elif isinstance(ov, int):
- v = int(v)
- elif isinstance(ov, list):
- vv = v.split(",")
- if vv == [""]:
- vv = []
- vv = [int(x, 0) if len(x) and x[0] in "0123456789" else x for x in vv]
- if op == "=":
- v = vv
- elif op == "+=":
- v = ov
- v.extend(vv)
- elif op == "-=":
- v = ov
- for x in vv:
- if x in v:
- v.remove(x)
- else:
- assert 0
-
- opts[k] = v
- self.set(**opts)
-
- return ret
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/tables.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/tables.py
deleted file mode 100644
index b3c7dc3..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/tables.py
+++ /dev/null
@@ -1,338 +0,0 @@
-# Copyright 2013 Google, Inc. All Rights Reserved.
-#
-# Google Author(s): Behdad Esfahbod, Roozbeh Pournader
-
-from fontTools import ttLib, cffLib
-from fontTools.misc.psCharStrings import T2WidthExtractor
-from fontTools.ttLib.tables.DefaultTable import DefaultTable
-from fontTools.merge.base import add_method, mergeObjects
-from fontTools.merge.cmap import computeMegaCmap
-from fontTools.merge.util import *
-import logging
-
-
-log = logging.getLogger("fontTools.merge")
-
-
-ttLib.getTableClass("maxp").mergeMap = {
- "*": max,
- "tableTag": equal,
- "tableVersion": equal,
- "numGlyphs": sum,
- "maxStorage": first,
- "maxFunctionDefs": first,
- "maxInstructionDefs": first,
- # TODO When we correctly merge hinting data, update these values:
- # maxFunctionDefs, maxInstructionDefs, maxSizeOfInstructions
-}
-
-headFlagsMergeBitMap = {
- "size": 16,
- "*": bitwise_or,
- 1: bitwise_and, # Baseline at y = 0
- 2: bitwise_and, # lsb at x = 0
- 3: bitwise_and, # Force ppem to integer values. FIXME?
- 5: bitwise_and, # Font is vertical
- 6: lambda bit: 0, # Always set to zero
- 11: bitwise_and, # Font data is 'lossless'
- 13: bitwise_and, # Optimized for ClearType
- 14: bitwise_and, # Last resort font. FIXME? equal or first may be better
- 15: lambda bit: 0, # Always set to zero
-}
-
-ttLib.getTableClass("head").mergeMap = {
- "tableTag": equal,
- "tableVersion": max,
- "fontRevision": max,
- "checkSumAdjustment": lambda lst: 0, # We need *something* here
- "magicNumber": equal,
- "flags": mergeBits(headFlagsMergeBitMap),
- "unitsPerEm": equal,
- "created": current_time,
- "modified": current_time,
- "xMin": min,
- "yMin": min,
- "xMax": max,
- "yMax": max,
- "macStyle": first,
- "lowestRecPPEM": max,
- "fontDirectionHint": lambda lst: 2,
- "indexToLocFormat": first,
- "glyphDataFormat": equal,
-}
-
-ttLib.getTableClass("hhea").mergeMap = {
- "*": equal,
- "tableTag": equal,
- "tableVersion": max,
- "ascent": max,
- "descent": min,
- "lineGap": max,
- "advanceWidthMax": max,
- "minLeftSideBearing": min,
- "minRightSideBearing": min,
- "xMaxExtent": max,
- "caretSlopeRise": first,
- "caretSlopeRun": first,
- "caretOffset": first,
- "numberOfHMetrics": recalculate,
-}
-
-ttLib.getTableClass("vhea").mergeMap = {
- "*": equal,
- "tableTag": equal,
- "tableVersion": max,
- "ascent": max,
- "descent": min,
- "lineGap": max,
- "advanceHeightMax": max,
- "minTopSideBearing": min,
- "minBottomSideBearing": min,
- "yMaxExtent": max,
- "caretSlopeRise": first,
- "caretSlopeRun": first,
- "caretOffset": first,
- "numberOfVMetrics": recalculate,
-}
-
-os2FsTypeMergeBitMap = {
- "size": 16,
- "*": lambda bit: 0,
- 1: bitwise_or, # no embedding permitted
- 2: bitwise_and, # allow previewing and printing documents
- 3: bitwise_and, # allow editing documents
- 8: bitwise_or, # no subsetting permitted
- 9: bitwise_or, # no embedding of outlines permitted
-}
-
-
-def mergeOs2FsType(lst):
- lst = list(lst)
- if all(item == 0 for item in lst):
- return 0
-
- # Compute least restrictive logic for each fsType value
- for i in range(len(lst)):
- # unset bit 1 (no embedding permitted) if either bit 2 or 3 is set
- if lst[i] & 0x000C:
- lst[i] &= ~0x0002
- # set bit 2 (allow previewing) if bit 3 is set (allow editing)
- elif lst[i] & 0x0008:
- lst[i] |= 0x0004
- # set bits 2 and 3 if everything is allowed
- elif lst[i] == 0:
- lst[i] = 0x000C
-
- fsType = mergeBits(os2FsTypeMergeBitMap)(lst)
- # unset bits 2 and 3 if bit 1 is set (some font is "no embedding")
- if fsType & 0x0002:
- fsType &= ~0x000C
- return fsType
-
-
-ttLib.getTableClass("OS/2").mergeMap = {
- "*": first,
- "tableTag": equal,
- "version": max,
- "xAvgCharWidth": first, # Will be recalculated at the end on the merged font
- "fsType": mergeOs2FsType, # Will be overwritten
- "panose": first, # FIXME: should really be the first Latin font
- "ulUnicodeRange1": bitwise_or,
- "ulUnicodeRange2": bitwise_or,
- "ulUnicodeRange3": bitwise_or,
- "ulUnicodeRange4": bitwise_or,
- "fsFirstCharIndex": min,
- "fsLastCharIndex": max,
- "sTypoAscender": max,
- "sTypoDescender": min,
- "sTypoLineGap": max,
- "usWinAscent": max,
- "usWinDescent": max,
- # Version 1
- "ulCodePageRange1": onlyExisting(bitwise_or),
- "ulCodePageRange2": onlyExisting(bitwise_or),
- # Version 2, 3, 4
- "sxHeight": onlyExisting(max),
- "sCapHeight": onlyExisting(max),
- "usDefaultChar": onlyExisting(first),
- "usBreakChar": onlyExisting(first),
- "usMaxContext": onlyExisting(max),
- # version 5
- "usLowerOpticalPointSize": onlyExisting(min),
- "usUpperOpticalPointSize": onlyExisting(max),
-}
-
-
-@add_method(ttLib.getTableClass("OS/2"))
-def merge(self, m, tables):
- DefaultTable.merge(self, m, tables)
- if self.version < 2:
- # bits 8 and 9 are reserved and should be set to zero
- self.fsType &= ~0x0300
- if self.version >= 3:
- # Only one of bits 1, 2, and 3 may be set. We already take
- # care of bit 1 implications in mergeOs2FsType. So unset
- # bit 2 if bit 3 is already set.
- if self.fsType & 0x0008:
- self.fsType &= ~0x0004
- return self
-
-
-ttLib.getTableClass("post").mergeMap = {
- "*": first,
- "tableTag": equal,
- "formatType": max,
- "isFixedPitch": min,
- "minMemType42": max,
- "maxMemType42": lambda lst: 0,
- "minMemType1": max,
- "maxMemType1": lambda lst: 0,
- "mapping": onlyExisting(sumDicts),
- "extraNames": lambda lst: [],
-}
-
-ttLib.getTableClass("vmtx").mergeMap = ttLib.getTableClass("hmtx").mergeMap = {
- "tableTag": equal,
- "metrics": sumDicts,
-}
-
-ttLib.getTableClass("name").mergeMap = {
- "tableTag": equal,
- "names": first, # FIXME? Does mixing name records make sense?
-}
-
-ttLib.getTableClass("loca").mergeMap = {
- "*": recalculate,
- "tableTag": equal,
-}
-
-ttLib.getTableClass("glyf").mergeMap = {
- "tableTag": equal,
- "glyphs": sumDicts,
- "glyphOrder": sumLists,
- "axisTags": equal,
-}
-
-
-@add_method(ttLib.getTableClass("glyf"))
-def merge(self, m, tables):
- for i, table in enumerate(tables):
- for g in table.glyphs.values():
- if i:
- # Drop hints for all but first font, since
- # we don't map functions / CVT values.
- g.removeHinting()
- # Expand composite glyphs to load their
- # composite glyph names.
- if g.isComposite() or g.isVarComposite():
- g.expand(table)
- return DefaultTable.merge(self, m, tables)
-
-
-ttLib.getTableClass("prep").mergeMap = lambda self, lst: first(lst)
-ttLib.getTableClass("fpgm").mergeMap = lambda self, lst: first(lst)
-ttLib.getTableClass("cvt ").mergeMap = lambda self, lst: first(lst)
-ttLib.getTableClass("gasp").mergeMap = lambda self, lst: first(
- lst
-) # FIXME? Appears irreconcilable
-
-
-@add_method(ttLib.getTableClass("CFF "))
-def merge(self, m, tables):
- if any(hasattr(table, "FDSelect") for table in tables):
- raise NotImplementedError("Merging CID-keyed CFF tables is not supported yet")
-
- for table in tables:
- table.cff.desubroutinize()
-
- newcff = tables[0]
- newfont = newcff.cff[0]
- private = newfont.Private
- newDefaultWidthX, newNominalWidthX = private.defaultWidthX, private.nominalWidthX
- storedNamesStrings = []
- glyphOrderStrings = []
- glyphOrder = set(newfont.getGlyphOrder())
-
- for name in newfont.strings.strings:
- if name not in glyphOrder:
- storedNamesStrings.append(name)
- else:
- glyphOrderStrings.append(name)
-
- chrset = list(newfont.charset)
- newcs = newfont.CharStrings
- log.debug("FONT 0 CharStrings: %d.", len(newcs))
-
- for i, table in enumerate(tables[1:], start=1):
- font = table.cff[0]
- defaultWidthX, nominalWidthX = (
- font.Private.defaultWidthX,
- font.Private.nominalWidthX,
- )
- widthsDiffer = (
- defaultWidthX != newDefaultWidthX or nominalWidthX != newNominalWidthX
- )
- font.Private = private
- fontGlyphOrder = set(font.getGlyphOrder())
- for name in font.strings.strings:
- if name in fontGlyphOrder:
- glyphOrderStrings.append(name)
- cs = font.CharStrings
- gs = table.cff.GlobalSubrs
- log.debug("Font %d CharStrings: %d.", i, len(cs))
- chrset.extend(font.charset)
- if newcs.charStringsAreIndexed:
- for i, name in enumerate(cs.charStrings, start=len(newcs)):
- newcs.charStrings[name] = i
- newcs.charStringsIndex.items.append(None)
- for name in cs.charStrings:
- if widthsDiffer:
- c = cs[name]
- defaultWidthXToken = object()
- extractor = T2WidthExtractor([], [], nominalWidthX, defaultWidthXToken)
- extractor.execute(c)
- width = extractor.width
- if width is not defaultWidthXToken:
- c.program.pop(0)
- else:
- width = defaultWidthX
- if width != newDefaultWidthX:
- c.program.insert(0, width - newNominalWidthX)
- newcs[name] = cs[name]
-
- newfont.charset = chrset
- newfont.numGlyphs = len(chrset)
- newfont.strings.strings = glyphOrderStrings + storedNamesStrings
-
- return newcff
-
-
-@add_method(ttLib.getTableClass("cmap"))
-def merge(self, m, tables):
- # TODO Handle format=14.
- if not hasattr(m, "cmap"):
- computeMegaCmap(m, tables)
- cmap = m.cmap
-
- cmapBmpOnly = {uni: gid for uni, gid in cmap.items() if uni <= 0xFFFF}
- self.tables = []
- module = ttLib.getTableModule("cmap")
- if len(cmapBmpOnly) != len(cmap):
- # format-12 required.
- cmapTable = module.cmap_classes[12](12)
- cmapTable.platformID = 3
- cmapTable.platEncID = 10
- cmapTable.language = 0
- cmapTable.cmap = cmap
- self.tables.append(cmapTable)
- # always create format-4
- cmapTable = module.cmap_classes[4](4)
- cmapTable.platformID = 3
- cmapTable.platEncID = 1
- cmapTable.language = 0
- cmapTable.cmap = cmapBmpOnly
- # ordered by platform then encoding
- self.tables.insert(0, cmapTable)
- self.tableVersion = 0
- self.numSubTables = len(self.tables)
- return self
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/unicode.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/unicode.py
deleted file mode 100644
index 65ae6c4..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/unicode.py
+++ /dev/null
@@ -1,78 +0,0 @@
-# Copyright 2021 Behdad Esfahbod. All Rights Reserved.
-
-
-def is_Default_Ignorable(u):
- # http://www.unicode.org/reports/tr44/#Default_Ignorable_Code_Point
- #
- # TODO Move me to unicodedata module and autogenerate.
- #
- # Unicode 14.0:
- # $ grep '; Default_Ignorable_Code_Point ' DerivedCoreProperties.txt | sed 's/;.*#/#/'
- # 00AD # Cf SOFT HYPHEN
- # 034F # Mn COMBINING GRAPHEME JOINER
- # 061C # Cf ARABIC LETTER MARK
- # 115F..1160 # Lo [2] HANGUL CHOSEONG FILLER..HANGUL JUNGSEONG FILLER
- # 17B4..17B5 # Mn [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA
- # 180B..180D # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
- # 180E # Cf MONGOLIAN VOWEL SEPARATOR
- # 180F # Mn MONGOLIAN FREE VARIATION SELECTOR FOUR
- # 200B..200F # Cf [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK
- # 202A..202E # Cf [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE
- # 2060..2064 # Cf [5] WORD JOINER..INVISIBLE PLUS
- # 2065 # Cn
- # 2066..206F # Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES
- # 3164 # Lo HANGUL FILLER
- # FE00..FE0F # Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16
- # FEFF # Cf ZERO WIDTH NO-BREAK SPACE
- # FFA0 # Lo HALFWIDTH HANGUL FILLER
- # FFF0..FFF8 # Cn [9] ..
- # 1BCA0..1BCA3 # Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP
- # 1D173..1D17A # Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE
- # E0000 # Cn
- # E0001 # Cf LANGUAGE TAG
- # E0002..E001F # Cn [30] ..
- # E0020..E007F # Cf [96] TAG SPACE..CANCEL TAG
- # E0080..E00FF # Cn [128] ..
- # E0100..E01EF # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
- # E01F0..E0FFF # Cn [3600] ..
- return (
- u == 0x00AD
- or u == 0x034F # Cf SOFT HYPHEN
- or u == 0x061C # Mn COMBINING GRAPHEME JOINER
- or 0x115F <= u <= 0x1160 # Cf ARABIC LETTER MARK
- or 0x17B4 # Lo [2] HANGUL CHOSEONG FILLER..HANGUL JUNGSEONG FILLER
- <= u
- <= 0x17B5
- or 0x180B # Mn [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA
- <= u
- <= 0x180D
- or u # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
- == 0x180E
- or u == 0x180F # Cf MONGOLIAN VOWEL SEPARATOR
- or 0x200B <= u <= 0x200F # Mn MONGOLIAN FREE VARIATION SELECTOR FOUR
- or 0x202A <= u <= 0x202E # Cf [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK
- or 0x2060 # Cf [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE
- <= u
- <= 0x2064
- or u == 0x2065 # Cf [5] WORD JOINER..INVISIBLE PLUS
- or 0x2066 <= u <= 0x206F # Cn
- or u == 0x3164 # Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES
- or 0xFE00 <= u <= 0xFE0F # Lo HANGUL FILLER
- or u == 0xFEFF # Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16
- or u == 0xFFA0 # Cf ZERO WIDTH NO-BREAK SPACE
- or 0xFFF0 <= u <= 0xFFF8 # Lo HALFWIDTH HANGUL FILLER
- or 0x1BCA0 <= u <= 0x1BCA3 # Cn [9] ..
- or 0x1D173 # Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP
- <= u
- <= 0x1D17A
- or u == 0xE0000 # Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE
- or u == 0xE0001 # Cn
- or 0xE0002 <= u <= 0xE001F # Cf LANGUAGE TAG
- or 0xE0020 <= u <= 0xE007F # Cn [30] ..
- or 0xE0080 <= u <= 0xE00FF # Cf [96] TAG SPACE..CANCEL TAG
- or 0xE0100 <= u <= 0xE01EF # Cn [128] ..
- or 0xE01F0 # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
- <= u
- <= 0xE0FFF
- or False # Cn [3600] ..
- )
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/util.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/util.py
deleted file mode 100644
index 42fe39d..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/merge/util.py
+++ /dev/null
@@ -1,143 +0,0 @@
-# Copyright 2013 Google, Inc. All Rights Reserved.
-#
-# Google Author(s): Behdad Esfahbod, Roozbeh Pournader
-
-from fontTools.misc.timeTools import timestampNow
-from fontTools.ttLib.tables.DefaultTable import DefaultTable
-from functools import reduce
-import operator
-import logging
-
-
-log = logging.getLogger("fontTools.merge")
-
-
-# General utility functions for merging values from different fonts
-
-
-def equal(lst):
- lst = list(lst)
- t = iter(lst)
- first = next(t)
- assert all(item == first for item in t), "Expected all items to be equal: %s" % lst
- return first
-
-
-def first(lst):
- return next(iter(lst))
-
-
-def recalculate(lst):
- return NotImplemented
-
-
-def current_time(lst):
- return timestampNow()
-
-
-def bitwise_and(lst):
- return reduce(operator.and_, lst)
-
-
-def bitwise_or(lst):
- return reduce(operator.or_, lst)
-
-
-def avg_int(lst):
- lst = list(lst)
- return sum(lst) // len(lst)
-
-
-def onlyExisting(func):
- """Returns a filter func that when called with a list,
- only calls func on the non-NotImplemented items of the list,
- and only so if there's at least one item remaining.
- Otherwise returns NotImplemented."""
-
- def wrapper(lst):
- items = [item for item in lst if item is not NotImplemented]
- return func(items) if items else NotImplemented
-
- return wrapper
-
-
-def sumLists(lst):
- l = []
- for item in lst:
- l.extend(item)
- return l
-
-
-def sumDicts(lst):
- d = {}
- for item in lst:
- d.update(item)
- return d
-
-
-def mergeBits(bitmap):
- def wrapper(lst):
- lst = list(lst)
- returnValue = 0
- for bitNumber in range(bitmap["size"]):
- try:
- mergeLogic = bitmap[bitNumber]
- except KeyError:
- try:
- mergeLogic = bitmap["*"]
- except KeyError:
- raise Exception("Don't know how to merge bit %s" % bitNumber)
- shiftedBit = 1 << bitNumber
- mergedValue = mergeLogic(bool(item & shiftedBit) for item in lst)
- returnValue |= mergedValue << bitNumber
- return returnValue
-
- return wrapper
-
-
-class AttendanceRecordingIdentityDict(object):
- """A dictionary-like object that records indices of items actually accessed
- from a list."""
-
- def __init__(self, lst):
- self.l = lst
- self.d = {id(v): i for i, v in enumerate(lst)}
- self.s = set()
-
- def __getitem__(self, v):
- self.s.add(self.d[id(v)])
- return v
-
-
-class GregariousIdentityDict(object):
- """A dictionary-like object that welcomes guests without reservations and
- adds them to the end of the guest list."""
-
- def __init__(self, lst):
- self.l = lst
- self.s = set(id(v) for v in lst)
-
- def __getitem__(self, v):
- if id(v) not in self.s:
- self.s.add(id(v))
- self.l.append(v)
- return v
-
-
-class NonhashableDict(object):
- """A dictionary-like object mapping objects to values."""
-
- def __init__(self, keys, values=None):
- if values is None:
- self.d = {id(v): i for i, v in enumerate(keys)}
- else:
- self.d = {id(k): v for k, v in zip(keys, values)}
-
- def __getitem__(self, k):
- return self.d[id(k)]
-
- def __setitem__(self, k, v):
- self.d[id(k)] = v
-
- def __delitem__(self, k):
- del self.d[id(k)]
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/__init__.py
deleted file mode 100644
index 156cb23..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-"""Empty __init__.py file to signal Python this directory is a package."""
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/arrayTools.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/arrayTools.py
deleted file mode 100644
index 5fb01a8..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/arrayTools.py
+++ /dev/null
@@ -1,422 +0,0 @@
-"""Routines for calculating bounding boxes, point in rectangle calculations and
-so on.
-"""
-
-from fontTools.misc.roundTools import otRound
-from fontTools.misc.vector import Vector as _Vector
-import math
-import warnings
-
-
-def calcBounds(array):
- """Calculate the bounding rectangle of a 2D points array.
-
- Args:
- array: A sequence of 2D tuples.
-
- Returns:
- A four-item tuple representing the bounding rectangle ``(xMin, yMin, xMax, yMax)``.
- """
- if not array:
- return 0, 0, 0, 0
- xs = [x for x, y in array]
- ys = [y for x, y in array]
- return min(xs), min(ys), max(xs), max(ys)
-
-
-def calcIntBounds(array, round=otRound):
- """Calculate the integer bounding rectangle of a 2D points array.
-
- Values are rounded to closest integer towards ``+Infinity`` using the
- :func:`fontTools.misc.fixedTools.otRound` function by default, unless
- an optional ``round`` function is passed.
-
- Args:
- array: A sequence of 2D tuples.
- round: A rounding function of type ``f(x: float) -> int``.
-
- Returns:
- A four-item tuple of integers representing the bounding rectangle:
- ``(xMin, yMin, xMax, yMax)``.
- """
- return tuple(round(v) for v in calcBounds(array))
-
-
-def updateBounds(bounds, p, min=min, max=max):
- """Add a point to a bounding rectangle.
-
- Args:
- bounds: A bounding rectangle expressed as a tuple
- ``(xMin, yMin, xMax, yMax)``.
- p: A 2D tuple representing a point.
- min,max: functions to compute the minimum and maximum.
-
- Returns:
- The updated bounding rectangle ``(xMin, yMin, xMax, yMax)``.
- """
- (x, y) = p
- xMin, yMin, xMax, yMax = bounds
- return min(xMin, x), min(yMin, y), max(xMax, x), max(yMax, y)
-
-
-def pointInRect(p, rect):
- """Test if a point is inside a bounding rectangle.
-
- Args:
- p: A 2D tuple representing a point.
- rect: A bounding rectangle expressed as a tuple
- ``(xMin, yMin, xMax, yMax)``.
-
- Returns:
- ``True`` if the point is inside the rectangle, ``False`` otherwise.
- """
- (x, y) = p
- xMin, yMin, xMax, yMax = rect
- return (xMin <= x <= xMax) and (yMin <= y <= yMax)
-
-
-def pointsInRect(array, rect):
- """Determine which points are inside a bounding rectangle.
-
- Args:
- array: A sequence of 2D tuples.
- rect: A bounding rectangle expressed as a tuple
- ``(xMin, yMin, xMax, yMax)``.
-
- Returns:
- A list containing the points inside the rectangle.
- """
- if len(array) < 1:
- return []
- xMin, yMin, xMax, yMax = rect
- return [(xMin <= x <= xMax) and (yMin <= y <= yMax) for x, y in array]
-
-
-def vectorLength(vector):
- """Calculate the length of the given vector.
-
- Args:
- vector: A 2D tuple.
-
- Returns:
- The Euclidean length of the vector.
- """
- x, y = vector
- return math.sqrt(x**2 + y**2)
-
-
-def asInt16(array):
- """Round a list of floats to 16-bit signed integers.
-
- Args:
- array: List of float values.
-
- Returns:
- A list of rounded integers.
- """
- return [int(math.floor(i + 0.5)) for i in array]
-
-
-def normRect(rect):
- """Normalize a bounding box rectangle.
-
- This function "turns the rectangle the right way up", so that the following
- holds::
-
- xMin <= xMax and yMin <= yMax
-
- Args:
- rect: A bounding rectangle expressed as a tuple
- ``(xMin, yMin, xMax, yMax)``.
-
- Returns:
- A normalized bounding rectangle.
- """
- (xMin, yMin, xMax, yMax) = rect
- return min(xMin, xMax), min(yMin, yMax), max(xMin, xMax), max(yMin, yMax)
-
-
-def scaleRect(rect, x, y):
- """Scale a bounding box rectangle.
-
- Args:
- rect: A bounding rectangle expressed as a tuple
- ``(xMin, yMin, xMax, yMax)``.
- x: Factor to scale the rectangle along the X axis.
- Y: Factor to scale the rectangle along the Y axis.
-
- Returns:
- A scaled bounding rectangle.
- """
- (xMin, yMin, xMax, yMax) = rect
- return xMin * x, yMin * y, xMax * x, yMax * y
-
-
-def offsetRect(rect, dx, dy):
- """Offset a bounding box rectangle.
-
- Args:
- rect: A bounding rectangle expressed as a tuple
- ``(xMin, yMin, xMax, yMax)``.
- dx: Amount to offset the rectangle along the X axis.
- dY: Amount to offset the rectangle along the Y axis.
-
- Returns:
- An offset bounding rectangle.
- """
- (xMin, yMin, xMax, yMax) = rect
- return xMin + dx, yMin + dy, xMax + dx, yMax + dy
-
-
-def insetRect(rect, dx, dy):
- """Inset a bounding box rectangle on all sides.
-
- Args:
- rect: A bounding rectangle expressed as a tuple
- ``(xMin, yMin, xMax, yMax)``.
- dx: Amount to inset the rectangle along the X axis.
- dY: Amount to inset the rectangle along the Y axis.
-
- Returns:
- An inset bounding rectangle.
- """
- (xMin, yMin, xMax, yMax) = rect
- return xMin + dx, yMin + dy, xMax - dx, yMax - dy
-
-
-def sectRect(rect1, rect2):
- """Test for rectangle-rectangle intersection.
-
- Args:
- rect1: First bounding rectangle, expressed as tuples
- ``(xMin, yMin, xMax, yMax)``.
- rect2: Second bounding rectangle.
-
- Returns:
- A boolean and a rectangle.
- If the input rectangles intersect, returns ``True`` and the intersecting
- rectangle. Returns ``False`` and ``(0, 0, 0, 0)`` if the input
- rectangles don't intersect.
- """
- (xMin1, yMin1, xMax1, yMax1) = rect1
- (xMin2, yMin2, xMax2, yMax2) = rect2
- xMin, yMin, xMax, yMax = (
- max(xMin1, xMin2),
- max(yMin1, yMin2),
- min(xMax1, xMax2),
- min(yMax1, yMax2),
- )
- if xMin >= xMax or yMin >= yMax:
- return False, (0, 0, 0, 0)
- return True, (xMin, yMin, xMax, yMax)
-
-
-def unionRect(rect1, rect2):
- """Determine union of bounding rectangles.
-
- Args:
- rect1: First bounding rectangle, expressed as tuples
- ``(xMin, yMin, xMax, yMax)``.
- rect2: Second bounding rectangle.
-
- Returns:
- The smallest rectangle in which both input rectangles are fully
- enclosed.
- """
- (xMin1, yMin1, xMax1, yMax1) = rect1
- (xMin2, yMin2, xMax2, yMax2) = rect2
- xMin, yMin, xMax, yMax = (
- min(xMin1, xMin2),
- min(yMin1, yMin2),
- max(xMax1, xMax2),
- max(yMax1, yMax2),
- )
- return (xMin, yMin, xMax, yMax)
-
-
-def rectCenter(rect):
- """Determine rectangle center.
-
- Args:
- rect: Bounding rectangle, expressed as tuples
- ``(xMin, yMin, xMax, yMax)``.
-
- Returns:
- A 2D tuple representing the point at the center of the rectangle.
- """
- (xMin, yMin, xMax, yMax) = rect
- return (xMin + xMax) / 2, (yMin + yMax) / 2
-
-
-def rectArea(rect):
- """Determine rectangle area.
-
- Args:
- rect: Bounding rectangle, expressed as tuples
- ``(xMin, yMin, xMax, yMax)``.
-
- Returns:
- The area of the rectangle.
- """
- (xMin, yMin, xMax, yMax) = rect
- return (yMax - yMin) * (xMax - xMin)
-
-
-def intRect(rect):
- """Round a rectangle to integer values.
-
- Guarantees that the resulting rectangle is NOT smaller than the original.
-
- Args:
- rect: Bounding rectangle, expressed as tuples
- ``(xMin, yMin, xMax, yMax)``.
-
- Returns:
- A rounded bounding rectangle.
- """
- (xMin, yMin, xMax, yMax) = rect
- xMin = int(math.floor(xMin))
- yMin = int(math.floor(yMin))
- xMax = int(math.ceil(xMax))
- yMax = int(math.ceil(yMax))
- return (xMin, yMin, xMax, yMax)
-
-
-def quantizeRect(rect, factor=1):
- """
- >>> bounds = (72.3, -218.4, 1201.3, 919.1)
- >>> quantizeRect(bounds)
- (72, -219, 1202, 920)
- >>> quantizeRect(bounds, factor=10)
- (70, -220, 1210, 920)
- >>> quantizeRect(bounds, factor=100)
- (0, -300, 1300, 1000)
- """
- if factor < 1:
- raise ValueError(f"Expected quantization factor >= 1, found: {factor!r}")
- xMin, yMin, xMax, yMax = normRect(rect)
- return (
- int(math.floor(xMin / factor) * factor),
- int(math.floor(yMin / factor) * factor),
- int(math.ceil(xMax / factor) * factor),
- int(math.ceil(yMax / factor) * factor),
- )
-
-
-class Vector(_Vector):
- def __init__(self, *args, **kwargs):
- warnings.warn(
- "fontTools.misc.arrayTools.Vector has been deprecated, please use "
- "fontTools.misc.vector.Vector instead.",
- DeprecationWarning,
- )
-
-
-def pairwise(iterable, reverse=False):
- """Iterate over current and next items in iterable.
-
- Args:
- iterable: An iterable
- reverse: If true, iterate in reverse order.
-
- Returns:
- A iterable yielding two elements per iteration.
-
- Example:
-
- >>> tuple(pairwise([]))
- ()
- >>> tuple(pairwise([], reverse=True))
- ()
- >>> tuple(pairwise([0]))
- ((0, 0),)
- >>> tuple(pairwise([0], reverse=True))
- ((0, 0),)
- >>> tuple(pairwise([0, 1]))
- ((0, 1), (1, 0))
- >>> tuple(pairwise([0, 1], reverse=True))
- ((1, 0), (0, 1))
- >>> tuple(pairwise([0, 1, 2]))
- ((0, 1), (1, 2), (2, 0))
- >>> tuple(pairwise([0, 1, 2], reverse=True))
- ((2, 1), (1, 0), (0, 2))
- >>> tuple(pairwise(['a', 'b', 'c', 'd']))
- (('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a'))
- >>> tuple(pairwise(['a', 'b', 'c', 'd'], reverse=True))
- (('d', 'c'), ('c', 'b'), ('b', 'a'), ('a', 'd'))
- """
- if not iterable:
- return
- if reverse:
- it = reversed(iterable)
- else:
- it = iter(iterable)
- first = next(it, None)
- a = first
- for b in it:
- yield (a, b)
- a = b
- yield (a, first)
-
-
-def _test():
- """
- >>> import math
- >>> calcBounds([])
- (0, 0, 0, 0)
- >>> calcBounds([(0, 40), (0, 100), (50, 50), (80, 10)])
- (0, 10, 80, 100)
- >>> updateBounds((0, 0, 0, 0), (100, 100))
- (0, 0, 100, 100)
- >>> pointInRect((50, 50), (0, 0, 100, 100))
- True
- >>> pointInRect((0, 0), (0, 0, 100, 100))
- True
- >>> pointInRect((100, 100), (0, 0, 100, 100))
- True
- >>> not pointInRect((101, 100), (0, 0, 100, 100))
- True
- >>> list(pointsInRect([(50, 50), (0, 0), (100, 100), (101, 100)], (0, 0, 100, 100)))
- [True, True, True, False]
- >>> vectorLength((3, 4))
- 5.0
- >>> vectorLength((1, 1)) == math.sqrt(2)
- True
- >>> list(asInt16([0, 0.1, 0.5, 0.9]))
- [0, 0, 1, 1]
- >>> normRect((0, 10, 100, 200))
- (0, 10, 100, 200)
- >>> normRect((100, 200, 0, 10))
- (0, 10, 100, 200)
- >>> scaleRect((10, 20, 50, 150), 1.5, 2)
- (15.0, 40, 75.0, 300)
- >>> offsetRect((10, 20, 30, 40), 5, 6)
- (15, 26, 35, 46)
- >>> insetRect((10, 20, 50, 60), 5, 10)
- (15, 30, 45, 50)
- >>> insetRect((10, 20, 50, 60), -5, -10)
- (5, 10, 55, 70)
- >>> intersects, rect = sectRect((0, 10, 20, 30), (0, 40, 20, 50))
- >>> not intersects
- True
- >>> intersects, rect = sectRect((0, 10, 20, 30), (5, 20, 35, 50))
- >>> intersects
- 1
- >>> rect
- (5, 20, 20, 30)
- >>> unionRect((0, 10, 20, 30), (0, 40, 20, 50))
- (0, 10, 20, 50)
- >>> rectCenter((0, 0, 100, 200))
- (50.0, 100.0)
- >>> rectCenter((0, 0, 100, 199.0))
- (50.0, 99.5)
- >>> intRect((0.9, 2.9, 3.1, 4.1))
- (0, 2, 4, 5)
- """
-
-
-if __name__ == "__main__":
- import sys
- import doctest
-
- sys.exit(doctest.testmod().failed)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/bezierTools.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/bezierTools.py
deleted file mode 100644
index 7772a4b..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/bezierTools.py
+++ /dev/null
@@ -1,1474 +0,0 @@
-# -*- coding: utf-8 -*-
-"""fontTools.misc.bezierTools.py -- tools for working with Bezier path segments.
-"""
-
-from fontTools.misc.arrayTools import calcBounds, sectRect, rectArea
-from fontTools.misc.transform import Identity
-import math
-from collections import namedtuple
-
-try:
- import cython
-
- COMPILED = cython.compiled
-except (AttributeError, ImportError):
- # if cython not installed, use mock module with no-op decorators and types
- from fontTools.misc import cython
-
- COMPILED = False
-
-
-Intersection = namedtuple("Intersection", ["pt", "t1", "t2"])
-
-
-__all__ = [
- "approximateCubicArcLength",
- "approximateCubicArcLengthC",
- "approximateQuadraticArcLength",
- "approximateQuadraticArcLengthC",
- "calcCubicArcLength",
- "calcCubicArcLengthC",
- "calcQuadraticArcLength",
- "calcQuadraticArcLengthC",
- "calcCubicBounds",
- "calcQuadraticBounds",
- "splitLine",
- "splitQuadratic",
- "splitCubic",
- "splitQuadraticAtT",
- "splitCubicAtT",
- "splitCubicAtTC",
- "splitCubicIntoTwoAtTC",
- "solveQuadratic",
- "solveCubic",
- "quadraticPointAtT",
- "cubicPointAtT",
- "cubicPointAtTC",
- "linePointAtT",
- "segmentPointAtT",
- "lineLineIntersections",
- "curveLineIntersections",
- "curveCurveIntersections",
- "segmentSegmentIntersections",
-]
-
-
-def calcCubicArcLength(pt1, pt2, pt3, pt4, tolerance=0.005):
- """Calculates the arc length for a cubic Bezier segment.
-
- Whereas :func:`approximateCubicArcLength` approximates the length, this
- function calculates it by "measuring", recursively dividing the curve
- until the divided segments are shorter than ``tolerance``.
-
- Args:
- pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.
- tolerance: Controls the precision of the calcuation.
-
- Returns:
- Arc length value.
- """
- return calcCubicArcLengthC(
- complex(*pt1), complex(*pt2), complex(*pt3), complex(*pt4), tolerance
- )
-
-
-def _split_cubic_into_two(p0, p1, p2, p3):
- mid = (p0 + 3 * (p1 + p2) + p3) * 0.125
- deriv3 = (p3 + p2 - p1 - p0) * 0.125
- return (
- (p0, (p0 + p1) * 0.5, mid - deriv3, mid),
- (mid, mid + deriv3, (p2 + p3) * 0.5, p3),
- )
-
-
-@cython.returns(cython.double)
-@cython.locals(
- p0=cython.complex,
- p1=cython.complex,
- p2=cython.complex,
- p3=cython.complex,
-)
-@cython.locals(mult=cython.double, arch=cython.double, box=cython.double)
-def _calcCubicArcLengthCRecurse(mult, p0, p1, p2, p3):
- arch = abs(p0 - p3)
- box = abs(p0 - p1) + abs(p1 - p2) + abs(p2 - p3)
- if arch * mult >= box:
- return (arch + box) * 0.5
- else:
- one, two = _split_cubic_into_two(p0, p1, p2, p3)
- return _calcCubicArcLengthCRecurse(mult, *one) + _calcCubicArcLengthCRecurse(
- mult, *two
- )
-
-
-@cython.returns(cython.double)
-@cython.locals(
- pt1=cython.complex,
- pt2=cython.complex,
- pt3=cython.complex,
- pt4=cython.complex,
-)
-@cython.locals(
- tolerance=cython.double,
- mult=cython.double,
-)
-def calcCubicArcLengthC(pt1, pt2, pt3, pt4, tolerance=0.005):
- """Calculates the arc length for a cubic Bezier segment.
-
- Args:
- pt1,pt2,pt3,pt4: Control points of the Bezier as complex numbers.
- tolerance: Controls the precision of the calcuation.
-
- Returns:
- Arc length value.
- """
- mult = 1.0 + 1.5 * tolerance # The 1.5 is a empirical hack; no math
- return _calcCubicArcLengthCRecurse(mult, pt1, pt2, pt3, pt4)
-
-
-epsilonDigits = 6
-epsilon = 1e-10
-
-
-@cython.cfunc
-@cython.inline
-@cython.returns(cython.double)
-@cython.locals(v1=cython.complex, v2=cython.complex)
-def _dot(v1, v2):
- return (v1 * v2.conjugate()).real
-
-
-@cython.cfunc
-@cython.inline
-@cython.returns(cython.double)
-@cython.locals(x=cython.complex)
-def _intSecAtan(x):
- # In : sympy.integrate(sp.sec(sp.atan(x)))
- # Out: x*sqrt(x**2 + 1)/2 + asinh(x)/2
- return x * math.sqrt(x**2 + 1) / 2 + math.asinh(x) / 2
-
-
-def calcQuadraticArcLength(pt1, pt2, pt3):
- """Calculates the arc length for a quadratic Bezier segment.
-
- Args:
- pt1: Start point of the Bezier as 2D tuple.
- pt2: Handle point of the Bezier as 2D tuple.
- pt3: End point of the Bezier as 2D tuple.
-
- Returns:
- Arc length value.
-
- Example::
-
- >>> calcQuadraticArcLength((0, 0), (0, 0), (0, 0)) # empty segment
- 0.0
- >>> calcQuadraticArcLength((0, 0), (50, 0), (80, 0)) # collinear points
- 80.0
- >>> calcQuadraticArcLength((0, 0), (0, 50), (0, 80)) # collinear points vertical
- 80.0
- >>> calcQuadraticArcLength((0, 0), (50, 20), (100, 40)) # collinear points
- 107.70329614269008
- >>> calcQuadraticArcLength((0, 0), (0, 100), (100, 0))
- 154.02976155645263
- >>> calcQuadraticArcLength((0, 0), (0, 50), (100, 0))
- 120.21581243984076
- >>> calcQuadraticArcLength((0, 0), (50, -10), (80, 50))
- 102.53273816445825
- >>> calcQuadraticArcLength((0, 0), (40, 0), (-40, 0)) # collinear points, control point outside
- 66.66666666666667
- >>> calcQuadraticArcLength((0, 0), (40, 0), (0, 0)) # collinear points, looping back
- 40.0
- """
- return calcQuadraticArcLengthC(complex(*pt1), complex(*pt2), complex(*pt3))
-
-
-@cython.returns(cython.double)
-@cython.locals(
- pt1=cython.complex,
- pt2=cython.complex,
- pt3=cython.complex,
- d0=cython.complex,
- d1=cython.complex,
- d=cython.complex,
- n=cython.complex,
-)
-@cython.locals(
- scale=cython.double,
- origDist=cython.double,
- a=cython.double,
- b=cython.double,
- x0=cython.double,
- x1=cython.double,
- Len=cython.double,
-)
-def calcQuadraticArcLengthC(pt1, pt2, pt3):
- """Calculates the arc length for a quadratic Bezier segment.
-
- Args:
- pt1: Start point of the Bezier as a complex number.
- pt2: Handle point of the Bezier as a complex number.
- pt3: End point of the Bezier as a complex number.
-
- Returns:
- Arc length value.
- """
- # Analytical solution to the length of a quadratic bezier.
- # Documentation: https://github.com/fonttools/fonttools/issues/3055
- d0 = pt2 - pt1
- d1 = pt3 - pt2
- d = d1 - d0
- n = d * 1j
- scale = abs(n)
- if scale == 0.0:
- return abs(pt3 - pt1)
- origDist = _dot(n, d0)
- if abs(origDist) < epsilon:
- if _dot(d0, d1) >= 0:
- return abs(pt3 - pt1)
- a, b = abs(d0), abs(d1)
- return (a * a + b * b) / (a + b)
- x0 = _dot(d, d0) / origDist
- x1 = _dot(d, d1) / origDist
- Len = abs(2 * (_intSecAtan(x1) - _intSecAtan(x0)) * origDist / (scale * (x1 - x0)))
- return Len
-
-
-def approximateQuadraticArcLength(pt1, pt2, pt3):
- """Calculates the arc length for a quadratic Bezier segment.
-
- Uses Gauss-Legendre quadrature for a branch-free approximation.
- See :func:`calcQuadraticArcLength` for a slower but more accurate result.
-
- Args:
- pt1: Start point of the Bezier as 2D tuple.
- pt2: Handle point of the Bezier as 2D tuple.
- pt3: End point of the Bezier as 2D tuple.
-
- Returns:
- Approximate arc length value.
- """
- return approximateQuadraticArcLengthC(complex(*pt1), complex(*pt2), complex(*pt3))
-
-
-@cython.returns(cython.double)
-@cython.locals(
- pt1=cython.complex,
- pt2=cython.complex,
- pt3=cython.complex,
-)
-@cython.locals(
- v0=cython.double,
- v1=cython.double,
- v2=cython.double,
-)
-def approximateQuadraticArcLengthC(pt1, pt2, pt3):
- """Calculates the arc length for a quadratic Bezier segment.
-
- Uses Gauss-Legendre quadrature for a branch-free approximation.
- See :func:`calcQuadraticArcLength` for a slower but more accurate result.
-
- Args:
- pt1: Start point of the Bezier as a complex number.
- pt2: Handle point of the Bezier as a complex number.
- pt3: End point of the Bezier as a complex number.
-
- Returns:
- Approximate arc length value.
- """
- # This, essentially, approximates the length-of-derivative function
- # to be integrated with the best-matching fifth-degree polynomial
- # approximation of it.
- #
- # https://en.wikipedia.org/wiki/Gaussian_quadrature#Gauss.E2.80.93Legendre_quadrature
-
- # abs(BezierCurveC[2].diff(t).subs({t:T})) for T in sorted(.5, .5±sqrt(3/5)/2),
- # weighted 5/18, 8/18, 5/18 respectively.
- v0 = abs(
- -0.492943519233745 * pt1 + 0.430331482911935 * pt2 + 0.0626120363218102 * pt3
- )
- v1 = abs(pt3 - pt1) * 0.4444444444444444
- v2 = abs(
- -0.0626120363218102 * pt1 - 0.430331482911935 * pt2 + 0.492943519233745 * pt3
- )
-
- return v0 + v1 + v2
-
-
-def calcQuadraticBounds(pt1, pt2, pt3):
- """Calculates the bounding rectangle for a quadratic Bezier segment.
-
- Args:
- pt1: Start point of the Bezier as a 2D tuple.
- pt2: Handle point of the Bezier as a 2D tuple.
- pt3: End point of the Bezier as a 2D tuple.
-
- Returns:
- A four-item tuple representing the bounding rectangle ``(xMin, yMin, xMax, yMax)``.
-
- Example::
-
- >>> calcQuadraticBounds((0, 0), (50, 100), (100, 0))
- (0, 0, 100, 50.0)
- >>> calcQuadraticBounds((0, 0), (100, 0), (100, 100))
- (0.0, 0.0, 100, 100)
- """
- (ax, ay), (bx, by), (cx, cy) = calcQuadraticParameters(pt1, pt2, pt3)
- ax2 = ax * 2.0
- ay2 = ay * 2.0
- roots = []
- if ax2 != 0:
- roots.append(-bx / ax2)
- if ay2 != 0:
- roots.append(-by / ay2)
- points = [
- (ax * t * t + bx * t + cx, ay * t * t + by * t + cy)
- for t in roots
- if 0 <= t < 1
- ] + [pt1, pt3]
- return calcBounds(points)
-
-
-def approximateCubicArcLength(pt1, pt2, pt3, pt4):
- """Approximates the arc length for a cubic Bezier segment.
-
- Uses Gauss-Lobatto quadrature with n=5 points to approximate arc length.
- See :func:`calcCubicArcLength` for a slower but more accurate result.
-
- Args:
- pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.
-
- Returns:
- Arc length value.
-
- Example::
-
- >>> approximateCubicArcLength((0, 0), (25, 100), (75, 100), (100, 0))
- 190.04332968932817
- >>> approximateCubicArcLength((0, 0), (50, 0), (100, 50), (100, 100))
- 154.8852074945903
- >>> approximateCubicArcLength((0, 0), (50, 0), (100, 0), (150, 0)) # line; exact result should be 150.
- 149.99999999999991
- >>> approximateCubicArcLength((0, 0), (50, 0), (100, 0), (-50, 0)) # cusp; exact result should be 150.
- 136.9267662156362
- >>> approximateCubicArcLength((0, 0), (50, 0), (100, -50), (-50, 0)) # cusp
- 154.80848416537057
- """
- return approximateCubicArcLengthC(
- complex(*pt1), complex(*pt2), complex(*pt3), complex(*pt4)
- )
-
-
-@cython.returns(cython.double)
-@cython.locals(
- pt1=cython.complex,
- pt2=cython.complex,
- pt3=cython.complex,
- pt4=cython.complex,
-)
-@cython.locals(
- v0=cython.double,
- v1=cython.double,
- v2=cython.double,
- v3=cython.double,
- v4=cython.double,
-)
-def approximateCubicArcLengthC(pt1, pt2, pt3, pt4):
- """Approximates the arc length for a cubic Bezier segment.
-
- Args:
- pt1,pt2,pt3,pt4: Control points of the Bezier as complex numbers.
-
- Returns:
- Arc length value.
- """
- # This, essentially, approximates the length-of-derivative function
- # to be integrated with the best-matching seventh-degree polynomial
- # approximation of it.
- #
- # https://en.wikipedia.org/wiki/Gaussian_quadrature#Gauss.E2.80.93Lobatto_rules
-
- # abs(BezierCurveC[3].diff(t).subs({t:T})) for T in sorted(0, .5±(3/7)**.5/2, .5, 1),
- # weighted 1/20, 49/180, 32/90, 49/180, 1/20 respectively.
- v0 = abs(pt2 - pt1) * 0.15
- v1 = abs(
- -0.558983582205757 * pt1
- + 0.325650248872424 * pt2
- + 0.208983582205757 * pt3
- + 0.024349751127576 * pt4
- )
- v2 = abs(pt4 - pt1 + pt3 - pt2) * 0.26666666666666666
- v3 = abs(
- -0.024349751127576 * pt1
- - 0.208983582205757 * pt2
- - 0.325650248872424 * pt3
- + 0.558983582205757 * pt4
- )
- v4 = abs(pt4 - pt3) * 0.15
-
- return v0 + v1 + v2 + v3 + v4
-
-
-def calcCubicBounds(pt1, pt2, pt3, pt4):
- """Calculates the bounding rectangle for a quadratic Bezier segment.
-
- Args:
- pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.
-
- Returns:
- A four-item tuple representing the bounding rectangle ``(xMin, yMin, xMax, yMax)``.
-
- Example::
-
- >>> calcCubicBounds((0, 0), (25, 100), (75, 100), (100, 0))
- (0, 0, 100, 75.0)
- >>> calcCubicBounds((0, 0), (50, 0), (100, 50), (100, 100))
- (0.0, 0.0, 100, 100)
- >>> print("%f %f %f %f" % calcCubicBounds((50, 0), (0, 100), (100, 100), (50, 0)))
- 35.566243 0.000000 64.433757 75.000000
- """
- (ax, ay), (bx, by), (cx, cy), (dx, dy) = calcCubicParameters(pt1, pt2, pt3, pt4)
- # calc first derivative
- ax3 = ax * 3.0
- ay3 = ay * 3.0
- bx2 = bx * 2.0
- by2 = by * 2.0
- xRoots = [t for t in solveQuadratic(ax3, bx2, cx) if 0 <= t < 1]
- yRoots = [t for t in solveQuadratic(ay3, by2, cy) if 0 <= t < 1]
- roots = xRoots + yRoots
-
- points = [
- (
- ax * t * t * t + bx * t * t + cx * t + dx,
- ay * t * t * t + by * t * t + cy * t + dy,
- )
- for t in roots
- ] + [pt1, pt4]
- return calcBounds(points)
-
-
-def splitLine(pt1, pt2, where, isHorizontal):
- """Split a line at a given coordinate.
-
- Args:
- pt1: Start point of line as 2D tuple.
- pt2: End point of line as 2D tuple.
- where: Position at which to split the line.
- isHorizontal: Direction of the ray splitting the line. If true,
- ``where`` is interpreted as a Y coordinate; if false, then
- ``where`` is interpreted as an X coordinate.
-
- Returns:
- A list of two line segments (each line segment being two 2D tuples)
- if the line was successfully split, or a list containing the original
- line.
-
- Example::
-
- >>> printSegments(splitLine((0, 0), (100, 100), 50, True))
- ((0, 0), (50, 50))
- ((50, 50), (100, 100))
- >>> printSegments(splitLine((0, 0), (100, 100), 100, True))
- ((0, 0), (100, 100))
- >>> printSegments(splitLine((0, 0), (100, 100), 0, True))
- ((0, 0), (0, 0))
- ((0, 0), (100, 100))
- >>> printSegments(splitLine((0, 0), (100, 100), 0, False))
- ((0, 0), (0, 0))
- ((0, 0), (100, 100))
- >>> printSegments(splitLine((100, 0), (0, 0), 50, False))
- ((100, 0), (50, 0))
- ((50, 0), (0, 0))
- >>> printSegments(splitLine((0, 100), (0, 0), 50, True))
- ((0, 100), (0, 50))
- ((0, 50), (0, 0))
- """
- pt1x, pt1y = pt1
- pt2x, pt2y = pt2
-
- ax = pt2x - pt1x
- ay = pt2y - pt1y
-
- bx = pt1x
- by = pt1y
-
- a = (ax, ay)[isHorizontal]
-
- if a == 0:
- return [(pt1, pt2)]
- t = (where - (bx, by)[isHorizontal]) / a
- if 0 <= t < 1:
- midPt = ax * t + bx, ay * t + by
- return [(pt1, midPt), (midPt, pt2)]
- else:
- return [(pt1, pt2)]
-
-
-def splitQuadratic(pt1, pt2, pt3, where, isHorizontal):
- """Split a quadratic Bezier curve at a given coordinate.
-
- Args:
- pt1,pt2,pt3: Control points of the Bezier as 2D tuples.
- where: Position at which to split the curve.
- isHorizontal: Direction of the ray splitting the curve. If true,
- ``where`` is interpreted as a Y coordinate; if false, then
- ``where`` is interpreted as an X coordinate.
-
- Returns:
- A list of two curve segments (each curve segment being three 2D tuples)
- if the curve was successfully split, or a list containing the original
- curve.
-
- Example::
-
- >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 150, False))
- ((0, 0), (50, 100), (100, 0))
- >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 50, False))
- ((0, 0), (25, 50), (50, 50))
- ((50, 50), (75, 50), (100, 0))
- >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 25, False))
- ((0, 0), (12.5, 25), (25, 37.5))
- ((25, 37.5), (62.5, 75), (100, 0))
- >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 25, True))
- ((0, 0), (7.32233, 14.6447), (14.6447, 25))
- ((14.6447, 25), (50, 75), (85.3553, 25))
- ((85.3553, 25), (92.6777, 14.6447), (100, -7.10543e-15))
- >>> # XXX I'm not at all sure if the following behavior is desirable:
- >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 50, True))
- ((0, 0), (25, 50), (50, 50))
- ((50, 50), (50, 50), (50, 50))
- ((50, 50), (75, 50), (100, 0))
- """
- a, b, c = calcQuadraticParameters(pt1, pt2, pt3)
- solutions = solveQuadratic(
- a[isHorizontal], b[isHorizontal], c[isHorizontal] - where
- )
- solutions = sorted(t for t in solutions if 0 <= t < 1)
- if not solutions:
- return [(pt1, pt2, pt3)]
- return _splitQuadraticAtT(a, b, c, *solutions)
-
-
-def splitCubic(pt1, pt2, pt3, pt4, where, isHorizontal):
- """Split a cubic Bezier curve at a given coordinate.
-
- Args:
- pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.
- where: Position at which to split the curve.
- isHorizontal: Direction of the ray splitting the curve. If true,
- ``where`` is interpreted as a Y coordinate; if false, then
- ``where`` is interpreted as an X coordinate.
-
- Returns:
- A list of two curve segments (each curve segment being four 2D tuples)
- if the curve was successfully split, or a list containing the original
- curve.
-
- Example::
-
- >>> printSegments(splitCubic((0, 0), (25, 100), (75, 100), (100, 0), 150, False))
- ((0, 0), (25, 100), (75, 100), (100, 0))
- >>> printSegments(splitCubic((0, 0), (25, 100), (75, 100), (100, 0), 50, False))
- ((0, 0), (12.5, 50), (31.25, 75), (50, 75))
- ((50, 75), (68.75, 75), (87.5, 50), (100, 0))
- >>> printSegments(splitCubic((0, 0), (25, 100), (75, 100), (100, 0), 25, True))
- ((0, 0), (2.29379, 9.17517), (4.79804, 17.5085), (7.47414, 25))
- ((7.47414, 25), (31.2886, 91.6667), (68.7114, 91.6667), (92.5259, 25))
- ((92.5259, 25), (95.202, 17.5085), (97.7062, 9.17517), (100, 1.77636e-15))
- """
- a, b, c, d = calcCubicParameters(pt1, pt2, pt3, pt4)
- solutions = solveCubic(
- a[isHorizontal], b[isHorizontal], c[isHorizontal], d[isHorizontal] - where
- )
- solutions = sorted(t for t in solutions if 0 <= t < 1)
- if not solutions:
- return [(pt1, pt2, pt3, pt4)]
- return _splitCubicAtT(a, b, c, d, *solutions)
-
-
-def splitQuadraticAtT(pt1, pt2, pt3, *ts):
- """Split a quadratic Bezier curve at one or more values of t.
-
- Args:
- pt1,pt2,pt3: Control points of the Bezier as 2D tuples.
- *ts: Positions at which to split the curve.
-
- Returns:
- A list of curve segments (each curve segment being three 2D tuples).
-
- Examples::
-
- >>> printSegments(splitQuadraticAtT((0, 0), (50, 100), (100, 0), 0.5))
- ((0, 0), (25, 50), (50, 50))
- ((50, 50), (75, 50), (100, 0))
- >>> printSegments(splitQuadraticAtT((0, 0), (50, 100), (100, 0), 0.5, 0.75))
- ((0, 0), (25, 50), (50, 50))
- ((50, 50), (62.5, 50), (75, 37.5))
- ((75, 37.5), (87.5, 25), (100, 0))
- """
- a, b, c = calcQuadraticParameters(pt1, pt2, pt3)
- return _splitQuadraticAtT(a, b, c, *ts)
-
-
-def splitCubicAtT(pt1, pt2, pt3, pt4, *ts):
- """Split a cubic Bezier curve at one or more values of t.
-
- Args:
- pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.
- *ts: Positions at which to split the curve.
-
- Returns:
- A list of curve segments (each curve segment being four 2D tuples).
-
- Examples::
-
- >>> printSegments(splitCubicAtT((0, 0), (25, 100), (75, 100), (100, 0), 0.5))
- ((0, 0), (12.5, 50), (31.25, 75), (50, 75))
- ((50, 75), (68.75, 75), (87.5, 50), (100, 0))
- >>> printSegments(splitCubicAtT((0, 0), (25, 100), (75, 100), (100, 0), 0.5, 0.75))
- ((0, 0), (12.5, 50), (31.25, 75), (50, 75))
- ((50, 75), (59.375, 75), (68.75, 68.75), (77.3438, 56.25))
- ((77.3438, 56.25), (85.9375, 43.75), (93.75, 25), (100, 0))
- """
- a, b, c, d = calcCubicParameters(pt1, pt2, pt3, pt4)
- return _splitCubicAtT(a, b, c, d, *ts)
-
-
-@cython.locals(
- pt1=cython.complex,
- pt2=cython.complex,
- pt3=cython.complex,
- pt4=cython.complex,
- a=cython.complex,
- b=cython.complex,
- c=cython.complex,
- d=cython.complex,
-)
-def splitCubicAtTC(pt1, pt2, pt3, pt4, *ts):
- """Split a cubic Bezier curve at one or more values of t.
-
- Args:
- pt1,pt2,pt3,pt4: Control points of the Bezier as complex numbers..
- *ts: Positions at which to split the curve.
-
- Yields:
- Curve segments (each curve segment being four complex numbers).
- """
- a, b, c, d = calcCubicParametersC(pt1, pt2, pt3, pt4)
- yield from _splitCubicAtTC(a, b, c, d, *ts)
-
-
-@cython.returns(cython.complex)
-@cython.locals(
- t=cython.double,
- pt1=cython.complex,
- pt2=cython.complex,
- pt3=cython.complex,
- pt4=cython.complex,
- pointAtT=cython.complex,
- off1=cython.complex,
- off2=cython.complex,
-)
-@cython.locals(
- t2=cython.double, _1_t=cython.double, _1_t_2=cython.double, _2_t_1_t=cython.double
-)
-def splitCubicIntoTwoAtTC(pt1, pt2, pt3, pt4, t):
- """Split a cubic Bezier curve at t.
-
- Args:
- pt1,pt2,pt3,pt4: Control points of the Bezier as complex numbers.
- t: Position at which to split the curve.
-
- Returns:
- A tuple of two curve segments (each curve segment being four complex numbers).
- """
- t2 = t * t
- _1_t = 1 - t
- _1_t_2 = _1_t * _1_t
- _2_t_1_t = 2 * t * _1_t
- pointAtT = (
- _1_t_2 * _1_t * pt1 + 3 * (_1_t_2 * t * pt2 + _1_t * t2 * pt3) + t2 * t * pt4
- )
- off1 = _1_t_2 * pt1 + _2_t_1_t * pt2 + t2 * pt3
- off2 = _1_t_2 * pt2 + _2_t_1_t * pt3 + t2 * pt4
-
- pt2 = pt1 + (pt2 - pt1) * t
- pt3 = pt4 + (pt3 - pt4) * _1_t
-
- return ((pt1, pt2, off1, pointAtT), (pointAtT, off2, pt3, pt4))
-
-
-def _splitQuadraticAtT(a, b, c, *ts):
- ts = list(ts)
- segments = []
- ts.insert(0, 0.0)
- ts.append(1.0)
- ax, ay = a
- bx, by = b
- cx, cy = c
- for i in range(len(ts) - 1):
- t1 = ts[i]
- t2 = ts[i + 1]
- delta = t2 - t1
- # calc new a, b and c
- delta_2 = delta * delta
- a1x = ax * delta_2
- a1y = ay * delta_2
- b1x = (2 * ax * t1 + bx) * delta
- b1y = (2 * ay * t1 + by) * delta
- t1_2 = t1 * t1
- c1x = ax * t1_2 + bx * t1 + cx
- c1y = ay * t1_2 + by * t1 + cy
-
- pt1, pt2, pt3 = calcQuadraticPoints((a1x, a1y), (b1x, b1y), (c1x, c1y))
- segments.append((pt1, pt2, pt3))
- return segments
-
-
-def _splitCubicAtT(a, b, c, d, *ts):
- ts = list(ts)
- ts.insert(0, 0.0)
- ts.append(1.0)
- segments = []
- ax, ay = a
- bx, by = b
- cx, cy = c
- dx, dy = d
- for i in range(len(ts) - 1):
- t1 = ts[i]
- t2 = ts[i + 1]
- delta = t2 - t1
-
- delta_2 = delta * delta
- delta_3 = delta * delta_2
- t1_2 = t1 * t1
- t1_3 = t1 * t1_2
-
- # calc new a, b, c and d
- a1x = ax * delta_3
- a1y = ay * delta_3
- b1x = (3 * ax * t1 + bx) * delta_2
- b1y = (3 * ay * t1 + by) * delta_2
- c1x = (2 * bx * t1 + cx + 3 * ax * t1_2) * delta
- c1y = (2 * by * t1 + cy + 3 * ay * t1_2) * delta
- d1x = ax * t1_3 + bx * t1_2 + cx * t1 + dx
- d1y = ay * t1_3 + by * t1_2 + cy * t1 + dy
- pt1, pt2, pt3, pt4 = calcCubicPoints(
- (a1x, a1y), (b1x, b1y), (c1x, c1y), (d1x, d1y)
- )
- segments.append((pt1, pt2, pt3, pt4))
- return segments
-
-
-@cython.locals(
- a=cython.complex,
- b=cython.complex,
- c=cython.complex,
- d=cython.complex,
- t1=cython.double,
- t2=cython.double,
- delta=cython.double,
- delta_2=cython.double,
- delta_3=cython.double,
- a1=cython.complex,
- b1=cython.complex,
- c1=cython.complex,
- d1=cython.complex,
-)
-def _splitCubicAtTC(a, b, c, d, *ts):
- ts = list(ts)
- ts.insert(0, 0.0)
- ts.append(1.0)
- for i in range(len(ts) - 1):
- t1 = ts[i]
- t2 = ts[i + 1]
- delta = t2 - t1
-
- delta_2 = delta * delta
- delta_3 = delta * delta_2
- t1_2 = t1 * t1
- t1_3 = t1 * t1_2
-
- # calc new a, b, c and d
- a1 = a * delta_3
- b1 = (3 * a * t1 + b) * delta_2
- c1 = (2 * b * t1 + c + 3 * a * t1_2) * delta
- d1 = a * t1_3 + b * t1_2 + c * t1 + d
- pt1, pt2, pt3, pt4 = calcCubicPointsC(a1, b1, c1, d1)
- yield (pt1, pt2, pt3, pt4)
-
-
-#
-# Equation solvers.
-#
-
-from math import sqrt, acos, cos, pi
-
-
-def solveQuadratic(a, b, c, sqrt=sqrt):
- """Solve a quadratic equation.
-
- Solves *a*x*x + b*x + c = 0* where a, b and c are real.
-
- Args:
- a: coefficient of *x²*
- b: coefficient of *x*
- c: constant term
-
- Returns:
- A list of roots. Note that the returned list is neither guaranteed to
- be sorted nor to contain unique values!
- """
- if abs(a) < epsilon:
- if abs(b) < epsilon:
- # We have a non-equation; therefore, we have no valid solution
- roots = []
- else:
- # We have a linear equation with 1 root.
- roots = [-c / b]
- else:
- # We have a true quadratic equation. Apply the quadratic formula to find two roots.
- DD = b * b - 4.0 * a * c
- if DD >= 0.0:
- rDD = sqrt(DD)
- roots = [(-b + rDD) / 2.0 / a, (-b - rDD) / 2.0 / a]
- else:
- # complex roots, ignore
- roots = []
- return roots
-
-
-def solveCubic(a, b, c, d):
- """Solve a cubic equation.
-
- Solves *a*x*x*x + b*x*x + c*x + d = 0* where a, b, c and d are real.
-
- Args:
- a: coefficient of *x³*
- b: coefficient of *x²*
- c: coefficient of *x*
- d: constant term
-
- Returns:
- A list of roots. Note that the returned list is neither guaranteed to
- be sorted nor to contain unique values!
-
- Examples::
-
- >>> solveCubic(1, 1, -6, 0)
- [-3.0, -0.0, 2.0]
- >>> solveCubic(-10.0, -9.0, 48.0, -29.0)
- [-2.9, 1.0, 1.0]
- >>> solveCubic(-9.875, -9.0, 47.625, -28.75)
- [-2.911392, 1.0, 1.0]
- >>> solveCubic(1.0, -4.5, 6.75, -3.375)
- [1.5, 1.5, 1.5]
- >>> solveCubic(-12.0, 18.0, -9.0, 1.50023651123)
- [0.5, 0.5, 0.5]
- >>> solveCubic(
- ... 9.0, 0.0, 0.0, -7.62939453125e-05
- ... ) == [-0.0, -0.0, -0.0]
- True
- """
- #
- # adapted from:
- # CUBIC.C - Solve a cubic polynomial
- # public domain by Ross Cottrell
- # found at: http://www.strangecreations.com/library/snippets/Cubic.C
- #
- if abs(a) < epsilon:
- # don't just test for zero; for very small values of 'a' solveCubic()
- # returns unreliable results, so we fall back to quad.
- return solveQuadratic(b, c, d)
- a = float(a)
- a1 = b / a
- a2 = c / a
- a3 = d / a
-
- Q = (a1 * a1 - 3.0 * a2) / 9.0
- R = (2.0 * a1 * a1 * a1 - 9.0 * a1 * a2 + 27.0 * a3) / 54.0
-
- R2 = R * R
- Q3 = Q * Q * Q
- R2 = 0 if R2 < epsilon else R2
- Q3 = 0 if abs(Q3) < epsilon else Q3
-
- R2_Q3 = R2 - Q3
-
- if R2 == 0.0 and Q3 == 0.0:
- x = round(-a1 / 3.0, epsilonDigits)
- return [x, x, x]
- elif R2_Q3 <= epsilon * 0.5:
- # The epsilon * .5 above ensures that Q3 is not zero.
- theta = acos(max(min(R / sqrt(Q3), 1.0), -1.0))
- rQ2 = -2.0 * sqrt(Q)
- a1_3 = a1 / 3.0
- x0 = rQ2 * cos(theta / 3.0) - a1_3
- x1 = rQ2 * cos((theta + 2.0 * pi) / 3.0) - a1_3
- x2 = rQ2 * cos((theta + 4.0 * pi) / 3.0) - a1_3
- x0, x1, x2 = sorted([x0, x1, x2])
- # Merge roots that are close-enough
- if x1 - x0 < epsilon and x2 - x1 < epsilon:
- x0 = x1 = x2 = round((x0 + x1 + x2) / 3.0, epsilonDigits)
- elif x1 - x0 < epsilon:
- x0 = x1 = round((x0 + x1) / 2.0, epsilonDigits)
- x2 = round(x2, epsilonDigits)
- elif x2 - x1 < epsilon:
- x0 = round(x0, epsilonDigits)
- x1 = x2 = round((x1 + x2) / 2.0, epsilonDigits)
- else:
- x0 = round(x0, epsilonDigits)
- x1 = round(x1, epsilonDigits)
- x2 = round(x2, epsilonDigits)
- return [x0, x1, x2]
- else:
- x = pow(sqrt(R2_Q3) + abs(R), 1 / 3.0)
- x = x + Q / x
- if R >= 0.0:
- x = -x
- x = round(x - a1 / 3.0, epsilonDigits)
- return [x]
-
-
-#
-# Conversion routines for points to parameters and vice versa
-#
-
-
-def calcQuadraticParameters(pt1, pt2, pt3):
- x2, y2 = pt2
- x3, y3 = pt3
- cx, cy = pt1
- bx = (x2 - cx) * 2.0
- by = (y2 - cy) * 2.0
- ax = x3 - cx - bx
- ay = y3 - cy - by
- return (ax, ay), (bx, by), (cx, cy)
-
-
-def calcCubicParameters(pt1, pt2, pt3, pt4):
- x2, y2 = pt2
- x3, y3 = pt3
- x4, y4 = pt4
- dx, dy = pt1
- cx = (x2 - dx) * 3.0
- cy = (y2 - dy) * 3.0
- bx = (x3 - x2) * 3.0 - cx
- by = (y3 - y2) * 3.0 - cy
- ax = x4 - dx - cx - bx
- ay = y4 - dy - cy - by
- return (ax, ay), (bx, by), (cx, cy), (dx, dy)
-
-
-@cython.cfunc
-@cython.inline
-@cython.locals(
- pt1=cython.complex,
- pt2=cython.complex,
- pt3=cython.complex,
- pt4=cython.complex,
- a=cython.complex,
- b=cython.complex,
- c=cython.complex,
-)
-def calcCubicParametersC(pt1, pt2, pt3, pt4):
- c = (pt2 - pt1) * 3.0
- b = (pt3 - pt2) * 3.0 - c
- a = pt4 - pt1 - c - b
- return (a, b, c, pt1)
-
-
-def calcQuadraticPoints(a, b, c):
- ax, ay = a
- bx, by = b
- cx, cy = c
- x1 = cx
- y1 = cy
- x2 = (bx * 0.5) + cx
- y2 = (by * 0.5) + cy
- x3 = ax + bx + cx
- y3 = ay + by + cy
- return (x1, y1), (x2, y2), (x3, y3)
-
-
-def calcCubicPoints(a, b, c, d):
- ax, ay = a
- bx, by = b
- cx, cy = c
- dx, dy = d
- x1 = dx
- y1 = dy
- x2 = (cx / 3.0) + dx
- y2 = (cy / 3.0) + dy
- x3 = (bx + cx) / 3.0 + x2
- y3 = (by + cy) / 3.0 + y2
- x4 = ax + dx + cx + bx
- y4 = ay + dy + cy + by
- return (x1, y1), (x2, y2), (x3, y3), (x4, y4)
-
-
-@cython.cfunc
-@cython.inline
-@cython.locals(
- a=cython.complex,
- b=cython.complex,
- c=cython.complex,
- d=cython.complex,
- p2=cython.complex,
- p3=cython.complex,
- p4=cython.complex,
-)
-def calcCubicPointsC(a, b, c, d):
- p2 = c * (1 / 3) + d
- p3 = (b + c) * (1 / 3) + p2
- p4 = a + b + c + d
- return (d, p2, p3, p4)
-
-
-#
-# Point at time
-#
-
-
-def linePointAtT(pt1, pt2, t):
- """Finds the point at time `t` on a line.
-
- Args:
- pt1, pt2: Coordinates of the line as 2D tuples.
- t: The time along the line.
-
- Returns:
- A 2D tuple with the coordinates of the point.
- """
- return ((pt1[0] * (1 - t) + pt2[0] * t), (pt1[1] * (1 - t) + pt2[1] * t))
-
-
-def quadraticPointAtT(pt1, pt2, pt3, t):
- """Finds the point at time `t` on a quadratic curve.
-
- Args:
- pt1, pt2, pt3: Coordinates of the curve as 2D tuples.
- t: The time along the curve.
-
- Returns:
- A 2D tuple with the coordinates of the point.
- """
- x = (1 - t) * (1 - t) * pt1[0] + 2 * (1 - t) * t * pt2[0] + t * t * pt3[0]
- y = (1 - t) * (1 - t) * pt1[1] + 2 * (1 - t) * t * pt2[1] + t * t * pt3[1]
- return (x, y)
-
-
-def cubicPointAtT(pt1, pt2, pt3, pt4, t):
- """Finds the point at time `t` on a cubic curve.
-
- Args:
- pt1, pt2, pt3, pt4: Coordinates of the curve as 2D tuples.
- t: The time along the curve.
-
- Returns:
- A 2D tuple with the coordinates of the point.
- """
- t2 = t * t
- _1_t = 1 - t
- _1_t_2 = _1_t * _1_t
- x = (
- _1_t_2 * _1_t * pt1[0]
- + 3 * (_1_t_2 * t * pt2[0] + _1_t * t2 * pt3[0])
- + t2 * t * pt4[0]
- )
- y = (
- _1_t_2 * _1_t * pt1[1]
- + 3 * (_1_t_2 * t * pt2[1] + _1_t * t2 * pt3[1])
- + t2 * t * pt4[1]
- )
- return (x, y)
-
-
-@cython.returns(cython.complex)
-@cython.locals(
- t=cython.double,
- pt1=cython.complex,
- pt2=cython.complex,
- pt3=cython.complex,
- pt4=cython.complex,
-)
-@cython.locals(t2=cython.double, _1_t=cython.double, _1_t_2=cython.double)
-def cubicPointAtTC(pt1, pt2, pt3, pt4, t):
- """Finds the point at time `t` on a cubic curve.
-
- Args:
- pt1, pt2, pt3, pt4: Coordinates of the curve as complex numbers.
- t: The time along the curve.
-
- Returns:
- A complex number with the coordinates of the point.
- """
- t2 = t * t
- _1_t = 1 - t
- _1_t_2 = _1_t * _1_t
- return _1_t_2 * _1_t * pt1 + 3 * (_1_t_2 * t * pt2 + _1_t * t2 * pt3) + t2 * t * pt4
-
-
-def segmentPointAtT(seg, t):
- if len(seg) == 2:
- return linePointAtT(*seg, t)
- elif len(seg) == 3:
- return quadraticPointAtT(*seg, t)
- elif len(seg) == 4:
- return cubicPointAtT(*seg, t)
- raise ValueError("Unknown curve degree")
-
-
-#
-# Intersection finders
-#
-
-
-def _line_t_of_pt(s, e, pt):
- sx, sy = s
- ex, ey = e
- px, py = pt
- if abs(sx - ex) < epsilon and abs(sy - ey) < epsilon:
- # Line is a point!
- return -1
- # Use the largest
- if abs(sx - ex) > abs(sy - ey):
- return (px - sx) / (ex - sx)
- else:
- return (py - sy) / (ey - sy)
-
-
-def _both_points_are_on_same_side_of_origin(a, b, origin):
- xDiff = (a[0] - origin[0]) * (b[0] - origin[0])
- yDiff = (a[1] - origin[1]) * (b[1] - origin[1])
- return not (xDiff <= 0.0 and yDiff <= 0.0)
-
-
-def lineLineIntersections(s1, e1, s2, e2):
- """Finds intersections between two line segments.
-
- Args:
- s1, e1: Coordinates of the first line as 2D tuples.
- s2, e2: Coordinates of the second line as 2D tuples.
-
- Returns:
- A list of ``Intersection`` objects, each object having ``pt``, ``t1``
- and ``t2`` attributes containing the intersection point, time on first
- segment and time on second segment respectively.
-
- Examples::
-
- >>> a = lineLineIntersections( (310,389), (453, 222), (289, 251), (447, 367))
- >>> len(a)
- 1
- >>> intersection = a[0]
- >>> intersection.pt
- (374.44882952482897, 313.73458370177315)
- >>> (intersection.t1, intersection.t2)
- (0.45069111555824465, 0.5408153767394238)
- """
- s1x, s1y = s1
- e1x, e1y = e1
- s2x, s2y = s2
- e2x, e2y = e2
- if (
- math.isclose(s2x, e2x) and math.isclose(s1x, e1x) and not math.isclose(s1x, s2x)
- ): # Parallel vertical
- return []
- if (
- math.isclose(s2y, e2y) and math.isclose(s1y, e1y) and not math.isclose(s1y, s2y)
- ): # Parallel horizontal
- return []
- if math.isclose(s2x, e2x) and math.isclose(s2y, e2y): # Line segment is tiny
- return []
- if math.isclose(s1x, e1x) and math.isclose(s1y, e1y): # Line segment is tiny
- return []
- if math.isclose(e1x, s1x):
- x = s1x
- slope34 = (e2y - s2y) / (e2x - s2x)
- y = slope34 * (x - s2x) + s2y
- pt = (x, y)
- return [
- Intersection(
- pt=pt, t1=_line_t_of_pt(s1, e1, pt), t2=_line_t_of_pt(s2, e2, pt)
- )
- ]
- if math.isclose(s2x, e2x):
- x = s2x
- slope12 = (e1y - s1y) / (e1x - s1x)
- y = slope12 * (x - s1x) + s1y
- pt = (x, y)
- return [
- Intersection(
- pt=pt, t1=_line_t_of_pt(s1, e1, pt), t2=_line_t_of_pt(s2, e2, pt)
- )
- ]
-
- slope12 = (e1y - s1y) / (e1x - s1x)
- slope34 = (e2y - s2y) / (e2x - s2x)
- if math.isclose(slope12, slope34):
- return []
- x = (slope12 * s1x - s1y - slope34 * s2x + s2y) / (slope12 - slope34)
- y = slope12 * (x - s1x) + s1y
- pt = (x, y)
- if _both_points_are_on_same_side_of_origin(
- pt, e1, s1
- ) and _both_points_are_on_same_side_of_origin(pt, s2, e2):
- return [
- Intersection(
- pt=pt, t1=_line_t_of_pt(s1, e1, pt), t2=_line_t_of_pt(s2, e2, pt)
- )
- ]
- return []
-
-
-def _alignment_transformation(segment):
- # Returns a transformation which aligns a segment horizontally at the
- # origin. Apply this transformation to curves and root-find to find
- # intersections with the segment.
- start = segment[0]
- end = segment[-1]
- angle = math.atan2(end[1] - start[1], end[0] - start[0])
- return Identity.rotate(-angle).translate(-start[0], -start[1])
-
-
-def _curve_line_intersections_t(curve, line):
- aligned_curve = _alignment_transformation(line).transformPoints(curve)
- if len(curve) == 3:
- a, b, c = calcQuadraticParameters(*aligned_curve)
- intersections = solveQuadratic(a[1], b[1], c[1])
- elif len(curve) == 4:
- a, b, c, d = calcCubicParameters(*aligned_curve)
- intersections = solveCubic(a[1], b[1], c[1], d[1])
- else:
- raise ValueError("Unknown curve degree")
- return sorted(i for i in intersections if 0.0 <= i <= 1)
-
-
-def curveLineIntersections(curve, line):
- """Finds intersections between a curve and a line.
-
- Args:
- curve: List of coordinates of the curve segment as 2D tuples.
- line: List of coordinates of the line segment as 2D tuples.
-
- Returns:
- A list of ``Intersection`` objects, each object having ``pt``, ``t1``
- and ``t2`` attributes containing the intersection point, time on first
- segment and time on second segment respectively.
-
- Examples::
- >>> curve = [ (100, 240), (30, 60), (210, 230), (160, 30) ]
- >>> line = [ (25, 260), (230, 20) ]
- >>> intersections = curveLineIntersections(curve, line)
- >>> len(intersections)
- 3
- >>> intersections[0].pt
- (84.9000930760723, 189.87306176459828)
- """
- if len(curve) == 3:
- pointFinder = quadraticPointAtT
- elif len(curve) == 4:
- pointFinder = cubicPointAtT
- else:
- raise ValueError("Unknown curve degree")
- intersections = []
- for t in _curve_line_intersections_t(curve, line):
- pt = pointFinder(*curve, t)
- # Back-project the point onto the line, to avoid problems with
- # numerical accuracy in the case of vertical and horizontal lines
- line_t = _line_t_of_pt(*line, pt)
- pt = linePointAtT(*line, line_t)
- intersections.append(Intersection(pt=pt, t1=t, t2=line_t))
- return intersections
-
-
-def _curve_bounds(c):
- if len(c) == 3:
- return calcQuadraticBounds(*c)
- elif len(c) == 4:
- return calcCubicBounds(*c)
- raise ValueError("Unknown curve degree")
-
-
-def _split_segment_at_t(c, t):
- if len(c) == 2:
- s, e = c
- midpoint = linePointAtT(s, e, t)
- return [(s, midpoint), (midpoint, e)]
- if len(c) == 3:
- return splitQuadraticAtT(*c, t)
- elif len(c) == 4:
- return splitCubicAtT(*c, t)
- raise ValueError("Unknown curve degree")
-
-
-def _curve_curve_intersections_t(
- curve1, curve2, precision=1e-3, range1=None, range2=None
-):
- bounds1 = _curve_bounds(curve1)
- bounds2 = _curve_bounds(curve2)
-
- if not range1:
- range1 = (0.0, 1.0)
- if not range2:
- range2 = (0.0, 1.0)
-
- # If bounds don't intersect, go home
- intersects, _ = sectRect(bounds1, bounds2)
- if not intersects:
- return []
-
- def midpoint(r):
- return 0.5 * (r[0] + r[1])
-
- # If they do overlap but they're tiny, approximate
- if rectArea(bounds1) < precision and rectArea(bounds2) < precision:
- return [(midpoint(range1), midpoint(range2))]
-
- c11, c12 = _split_segment_at_t(curve1, 0.5)
- c11_range = (range1[0], midpoint(range1))
- c12_range = (midpoint(range1), range1[1])
-
- c21, c22 = _split_segment_at_t(curve2, 0.5)
- c21_range = (range2[0], midpoint(range2))
- c22_range = (midpoint(range2), range2[1])
-
- found = []
- found.extend(
- _curve_curve_intersections_t(
- c11, c21, precision, range1=c11_range, range2=c21_range
- )
- )
- found.extend(
- _curve_curve_intersections_t(
- c12, c21, precision, range1=c12_range, range2=c21_range
- )
- )
- found.extend(
- _curve_curve_intersections_t(
- c11, c22, precision, range1=c11_range, range2=c22_range
- )
- )
- found.extend(
- _curve_curve_intersections_t(
- c12, c22, precision, range1=c12_range, range2=c22_range
- )
- )
-
- unique_key = lambda ts: (int(ts[0] / precision), int(ts[1] / precision))
- seen = set()
- unique_values = []
-
- for ts in found:
- key = unique_key(ts)
- if key in seen:
- continue
- seen.add(key)
- unique_values.append(ts)
-
- return unique_values
-
-
-def curveCurveIntersections(curve1, curve2):
- """Finds intersections between a curve and a curve.
-
- Args:
- curve1: List of coordinates of the first curve segment as 2D tuples.
- curve2: List of coordinates of the second curve segment as 2D tuples.
-
- Returns:
- A list of ``Intersection`` objects, each object having ``pt``, ``t1``
- and ``t2`` attributes containing the intersection point, time on first
- segment and time on second segment respectively.
-
- Examples::
- >>> curve1 = [ (10,100), (90,30), (40,140), (220,220) ]
- >>> curve2 = [ (5,150), (180,20), (80,250), (210,190) ]
- >>> intersections = curveCurveIntersections(curve1, curve2)
- >>> len(intersections)
- 3
- >>> intersections[0].pt
- (81.7831487395506, 109.88904552375288)
- """
- intersection_ts = _curve_curve_intersections_t(curve1, curve2)
- return [
- Intersection(pt=segmentPointAtT(curve1, ts[0]), t1=ts[0], t2=ts[1])
- for ts in intersection_ts
- ]
-
-
-def segmentSegmentIntersections(seg1, seg2):
- """Finds intersections between two segments.
-
- Args:
- seg1: List of coordinates of the first segment as 2D tuples.
- seg2: List of coordinates of the second segment as 2D tuples.
-
- Returns:
- A list of ``Intersection`` objects, each object having ``pt``, ``t1``
- and ``t2`` attributes containing the intersection point, time on first
- segment and time on second segment respectively.
-
- Examples::
- >>> curve1 = [ (10,100), (90,30), (40,140), (220,220) ]
- >>> curve2 = [ (5,150), (180,20), (80,250), (210,190) ]
- >>> intersections = segmentSegmentIntersections(curve1, curve2)
- >>> len(intersections)
- 3
- >>> intersections[0].pt
- (81.7831487395506, 109.88904552375288)
- >>> curve3 = [ (100, 240), (30, 60), (210, 230), (160, 30) ]
- >>> line = [ (25, 260), (230, 20) ]
- >>> intersections = segmentSegmentIntersections(curve3, line)
- >>> len(intersections)
- 3
- >>> intersections[0].pt
- (84.9000930760723, 189.87306176459828)
-
- """
- # Arrange by degree
- swapped = False
- if len(seg2) > len(seg1):
- seg2, seg1 = seg1, seg2
- swapped = True
- if len(seg1) > 2:
- if len(seg2) > 2:
- intersections = curveCurveIntersections(seg1, seg2)
- else:
- intersections = curveLineIntersections(seg1, seg2)
- elif len(seg1) == 2 and len(seg2) == 2:
- intersections = lineLineIntersections(*seg1, *seg2)
- else:
- raise ValueError("Couldn't work out which intersection function to use")
- if not swapped:
- return intersections
- return [Intersection(pt=i.pt, t1=i.t2, t2=i.t1) for i in intersections]
-
-
-def _segmentrepr(obj):
- """
- >>> _segmentrepr([1, [2, 3], [], [[2, [3, 4], [0.1, 2.2]]]])
- '(1, (2, 3), (), ((2, (3, 4), (0.1, 2.2))))'
- """
- try:
- it = iter(obj)
- except TypeError:
- return "%g" % obj
- else:
- return "(%s)" % ", ".join(_segmentrepr(x) for x in it)
-
-
-def printSegments(segments):
- """Helper for the doctests, displaying each segment in a list of
- segments on a single line as a tuple.
- """
- for segment in segments:
- print(_segmentrepr(segment))
-
-
-if __name__ == "__main__":
- import sys
- import doctest
-
- sys.exit(doctest.testmod().failed)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/classifyTools.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/classifyTools.py
deleted file mode 100644
index 2235bbd..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/classifyTools.py
+++ /dev/null
@@ -1,171 +0,0 @@
-""" fontTools.misc.classifyTools.py -- tools for classifying things.
-"""
-
-
-class Classifier(object):
-
- """
- Main Classifier object, used to classify things into similar sets.
- """
-
- def __init__(self, sort=True):
- self._things = set() # set of all things known so far
- self._sets = [] # list of class sets produced so far
- self._mapping = {} # map from things to their class set
- self._dirty = False
- self._sort = sort
-
- def add(self, set_of_things):
- """
- Add a set to the classifier. Any iterable is accepted.
- """
- if not set_of_things:
- return
-
- self._dirty = True
-
- things, sets, mapping = self._things, self._sets, self._mapping
-
- s = set(set_of_things)
- intersection = s.intersection(things) # existing things
- s.difference_update(intersection) # new things
- difference = s
- del s
-
- # Add new class for new things
- if difference:
- things.update(difference)
- sets.append(difference)
- for thing in difference:
- mapping[thing] = difference
- del difference
-
- while intersection:
- # Take one item and process the old class it belongs to
- old_class = mapping[next(iter(intersection))]
- old_class_intersection = old_class.intersection(intersection)
-
- # Update old class to remove items from new set
- old_class.difference_update(old_class_intersection)
-
- # Remove processed items from todo list
- intersection.difference_update(old_class_intersection)
-
- # Add new class for the intersection with old class
- sets.append(old_class_intersection)
- for thing in old_class_intersection:
- mapping[thing] = old_class_intersection
- del old_class_intersection
-
- def update(self, list_of_sets):
- """
- Add a a list of sets to the classifier. Any iterable of iterables is accepted.
- """
- for s in list_of_sets:
- self.add(s)
-
- def _process(self):
- if not self._dirty:
- return
-
- # Do any deferred processing
- sets = self._sets
- self._sets = [s for s in sets if s]
-
- if self._sort:
- self._sets = sorted(self._sets, key=lambda s: (-len(s), sorted(s)))
-
- self._dirty = False
-
- # Output methods
-
- def getThings(self):
- """Returns the set of all things known so far.
-
- The return value belongs to the Classifier object and should NOT
- be modified while the classifier is still in use.
- """
- self._process()
- return self._things
-
- def getMapping(self):
- """Returns the mapping from things to their class set.
-
- The return value belongs to the Classifier object and should NOT
- be modified while the classifier is still in use.
- """
- self._process()
- return self._mapping
-
- def getClasses(self):
- """Returns the list of class sets.
-
- The return value belongs to the Classifier object and should NOT
- be modified while the classifier is still in use.
- """
- self._process()
- return self._sets
-
-
-def classify(list_of_sets, sort=True):
- """
- Takes a iterable of iterables (list of sets from here on; but any
- iterable works.), and returns the smallest list of sets such that
- each set, is either a subset, or is disjoint from, each of the input
- sets.
-
- In other words, this function classifies all the things present in
- any of the input sets, into similar classes, based on which sets
- things are a member of.
-
- If sort=True, return class sets are sorted by decreasing size and
- their natural sort order within each class size. Otherwise, class
- sets are returned in the order that they were identified, which is
- generally not significant.
-
- >>> classify([]) == ([], {})
- True
- >>> classify([[]]) == ([], {})
- True
- >>> classify([[], []]) == ([], {})
- True
- >>> classify([[1]]) == ([{1}], {1: {1}})
- True
- >>> classify([[1,2]]) == ([{1, 2}], {1: {1, 2}, 2: {1, 2}})
- True
- >>> classify([[1],[2]]) == ([{1}, {2}], {1: {1}, 2: {2}})
- True
- >>> classify([[1,2],[2]]) == ([{1}, {2}], {1: {1}, 2: {2}})
- True
- >>> classify([[1,2],[2,4]]) == ([{1}, {2}, {4}], {1: {1}, 2: {2}, 4: {4}})
- True
- >>> classify([[1,2],[2,4,5]]) == (
- ... [{4, 5}, {1}, {2}], {1: {1}, 2: {2}, 4: {4, 5}, 5: {4, 5}})
- True
- >>> classify([[1,2],[2,4,5]], sort=False) == (
- ... [{1}, {4, 5}, {2}], {1: {1}, 2: {2}, 4: {4, 5}, 5: {4, 5}})
- True
- >>> classify([[1,2,9],[2,4,5]], sort=False) == (
- ... [{1, 9}, {4, 5}, {2}], {1: {1, 9}, 2: {2}, 4: {4, 5}, 5: {4, 5},
- ... 9: {1, 9}})
- True
- >>> classify([[1,2,9,15],[2,4,5]], sort=False) == (
- ... [{1, 9, 15}, {4, 5}, {2}], {1: {1, 9, 15}, 2: {2}, 4: {4, 5},
- ... 5: {4, 5}, 9: {1, 9, 15}, 15: {1, 9, 15}})
- True
- >>> classes, mapping = classify([[1,2,9,15],[2,4,5],[15,5]], sort=False)
- >>> set([frozenset(c) for c in classes]) == set(
- ... [frozenset(s) for s in ({1, 9}, {4}, {2}, {5}, {15})])
- True
- >>> mapping == {1: {1, 9}, 2: {2}, 4: {4}, 5: {5}, 9: {1, 9}, 15: {15}}
- True
- """
- classifier = Classifier(sort=sort)
- classifier.update(list_of_sets)
- return classifier.getClasses(), classifier.getMapping()
-
-
-if __name__ == "__main__":
- import sys, doctest
-
- sys.exit(doctest.testmod(optionflags=doctest.ELLIPSIS).failed)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/cliTools.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/cliTools.py
deleted file mode 100644
index 8322ea9..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/cliTools.py
+++ /dev/null
@@ -1,52 +0,0 @@
-"""Collection of utilities for command-line interfaces and console scripts."""
-import os
-import re
-
-
-numberAddedRE = re.compile(r"#\d+$")
-
-
-def makeOutputFileName(
- input, outputDir=None, extension=None, overWrite=False, suffix=""
-):
- """Generates a suitable file name for writing output.
-
- Often tools will want to take a file, do some kind of transformation to it,
- and write it out again. This function determines an appropriate name for the
- output file, through one or more of the following steps:
-
- - changing the output directory
- - appending suffix before file extension
- - replacing the file extension
- - suffixing the filename with a number (``#1``, ``#2``, etc.) to avoid
- overwriting an existing file.
-
- Args:
- input: Name of input file.
- outputDir: Optionally, a new directory to write the file into.
- suffix: Optionally, a string suffix is appended to file name before
- the extension.
- extension: Optionally, a replacement for the current file extension.
- overWrite: Overwriting an existing file is permitted if true; if false
- and the proposed filename exists, a new name will be generated by
- adding an appropriate number suffix.
-
- Returns:
- str: Suitable output filename
- """
- dirName, fileName = os.path.split(input)
- fileName, ext = os.path.splitext(fileName)
- if outputDir:
- dirName = outputDir
- fileName = numberAddedRE.split(fileName)[0]
- if extension is None:
- extension = os.path.splitext(input)[1]
- output = os.path.join(dirName, fileName + suffix + extension)
- n = 1
- if not overWrite:
- while os.path.exists(output):
- output = os.path.join(
- dirName, fileName + suffix + "#" + repr(n) + extension
- )
- n += 1
- return output
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/configTools.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/configTools.py
deleted file mode 100644
index 38bbada..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/configTools.py
+++ /dev/null
@@ -1,348 +0,0 @@
-"""
-Code of the config system; not related to fontTools or fonts in particular.
-
-The options that are specific to fontTools are in :mod:`fontTools.config`.
-
-To create your own config system, you need to create an instance of
-:class:`Options`, and a subclass of :class:`AbstractConfig` with its
-``options`` class variable set to your instance of Options.
-
-"""
-from __future__ import annotations
-
-import logging
-from dataclasses import dataclass
-from typing import (
- Any,
- Callable,
- ClassVar,
- Dict,
- Iterable,
- Mapping,
- MutableMapping,
- Optional,
- Set,
- Union,
-)
-
-
-log = logging.getLogger(__name__)
-
-__all__ = [
- "AbstractConfig",
- "ConfigAlreadyRegisteredError",
- "ConfigError",
- "ConfigUnknownOptionError",
- "ConfigValueParsingError",
- "ConfigValueValidationError",
- "Option",
- "Options",
-]
-
-
-class ConfigError(Exception):
- """Base exception for the config module."""
-
-
-class ConfigAlreadyRegisteredError(ConfigError):
- """Raised when a module tries to register a configuration option that
- already exists.
-
- Should not be raised too much really, only when developing new fontTools
- modules.
- """
-
- def __init__(self, name):
- super().__init__(f"Config option {name} is already registered.")
-
-
-class ConfigValueParsingError(ConfigError):
- """Raised when a configuration value cannot be parsed."""
-
- def __init__(self, name, value):
- super().__init__(
- f"Config option {name}: value cannot be parsed (given {repr(value)})"
- )
-
-
-class ConfigValueValidationError(ConfigError):
- """Raised when a configuration value cannot be validated."""
-
- def __init__(self, name, value):
- super().__init__(
- f"Config option {name}: value is invalid (given {repr(value)})"
- )
-
-
-class ConfigUnknownOptionError(ConfigError):
- """Raised when a configuration option is unknown."""
-
- def __init__(self, option_or_name):
- name = (
- f"'{option_or_name.name}' (id={id(option_or_name)})>"
- if isinstance(option_or_name, Option)
- else f"'{option_or_name}'"
- )
- super().__init__(f"Config option {name} is unknown")
-
-
-# eq=False because Options are unique, not fungible objects
-@dataclass(frozen=True, eq=False)
-class Option:
- name: str
- """Unique name identifying the option (e.g. package.module:MY_OPTION)."""
- help: str
- """Help text for this option."""
- default: Any
- """Default value for this option."""
- parse: Callable[[str], Any]
- """Turn input (e.g. string) into proper type. Only when reading from file."""
- validate: Optional[Callable[[Any], bool]] = None
- """Return true if the given value is an acceptable value."""
-
- @staticmethod
- def parse_optional_bool(v: str) -> Optional[bool]:
- s = str(v).lower()
- if s in {"0", "no", "false"}:
- return False
- if s in {"1", "yes", "true"}:
- return True
- if s in {"auto", "none"}:
- return None
- raise ValueError("invalid optional bool: {v!r}")
-
- @staticmethod
- def validate_optional_bool(v: Any) -> bool:
- return v is None or isinstance(v, bool)
-
-
-class Options(Mapping):
- """Registry of available options for a given config system.
-
- Define new options using the :meth:`register()` method.
-
- Access existing options using the Mapping interface.
- """
-
- __options: Dict[str, Option]
-
- def __init__(self, other: "Options" = None) -> None:
- self.__options = {}
- if other is not None:
- for option in other.values():
- self.register_option(option)
-
- def register(
- self,
- name: str,
- help: str,
- default: Any,
- parse: Callable[[str], Any],
- validate: Optional[Callable[[Any], bool]] = None,
- ) -> Option:
- """Create and register a new option."""
- return self.register_option(Option(name, help, default, parse, validate))
-
- def register_option(self, option: Option) -> Option:
- """Register a new option."""
- name = option.name
- if name in self.__options:
- raise ConfigAlreadyRegisteredError(name)
- self.__options[name] = option
- return option
-
- def is_registered(self, option: Option) -> bool:
- """Return True if the same option object is already registered."""
- return self.__options.get(option.name) is option
-
- def __getitem__(self, key: str) -> Option:
- return self.__options.__getitem__(key)
-
- def __iter__(self) -> Iterator[str]:
- return self.__options.__iter__()
-
- def __len__(self) -> int:
- return self.__options.__len__()
-
- def __repr__(self) -> str:
- return (
- f"{self.__class__.__name__}({{\n"
- + "".join(
- f" {k!r}: Option(default={v.default!r}, ...),\n"
- for k, v in self.__options.items()
- )
- + "})"
- )
-
-
-_USE_GLOBAL_DEFAULT = object()
-
-
-class AbstractConfig(MutableMapping):
- """
- Create a set of config values, optionally pre-filled with values from
- the given dictionary or pre-existing config object.
-
- The class implements the MutableMapping protocol keyed by option name (`str`).
- For convenience its methods accept either Option or str as the key parameter.
-
- .. seealso:: :meth:`set()`
-
- This config class is abstract because it needs its ``options`` class
- var to be set to an instance of :class:`Options` before it can be
- instanciated and used.
-
- .. code:: python
-
- class MyConfig(AbstractConfig):
- options = Options()
-
- MyConfig.register_option( "test:option_name", "This is an option", 0, int, lambda v: isinstance(v, int))
-
- cfg = MyConfig({"test:option_name": 10})
-
- """
-
- options: ClassVar[Options]
-
- @classmethod
- def register_option(
- cls,
- name: str,
- help: str,
- default: Any,
- parse: Callable[[str], Any],
- validate: Optional[Callable[[Any], bool]] = None,
- ) -> Option:
- """Register an available option in this config system."""
- return cls.options.register(
- name, help=help, default=default, parse=parse, validate=validate
- )
-
- _values: Dict[str, Any]
-
- def __init__(
- self,
- values: Union[AbstractConfig, Dict[Union[Option, str], Any]] = {},
- parse_values: bool = False,
- skip_unknown: bool = False,
- ):
- self._values = {}
- values_dict = values._values if isinstance(values, AbstractConfig) else values
- for name, value in values_dict.items():
- self.set(name, value, parse_values, skip_unknown)
-
- def _resolve_option(self, option_or_name: Union[Option, str]) -> Option:
- if isinstance(option_or_name, Option):
- option = option_or_name
- if not self.options.is_registered(option):
- raise ConfigUnknownOptionError(option)
- return option
- elif isinstance(option_or_name, str):
- name = option_or_name
- try:
- return self.options[name]
- except KeyError:
- raise ConfigUnknownOptionError(name)
- else:
- raise TypeError(
- "expected Option or str, found "
- f"{type(option_or_name).__name__}: {option_or_name!r}"
- )
-
- def set(
- self,
- option_or_name: Union[Option, str],
- value: Any,
- parse_values: bool = False,
- skip_unknown: bool = False,
- ):
- """Set the value of an option.
-
- Args:
- * `option_or_name`: an `Option` object or its name (`str`).
- * `value`: the value to be assigned to given option.
- * `parse_values`: parse the configuration value from a string into
- its proper type, as per its `Option` object. The default
- behavior is to raise `ConfigValueValidationError` when the value
- is not of the right type. Useful when reading options from a
- file type that doesn't support as many types as Python.
- * `skip_unknown`: skip unknown configuration options. The default
- behaviour is to raise `ConfigUnknownOptionError`. Useful when
- reading options from a configuration file that has extra entries
- (e.g. for a later version of fontTools)
- """
- try:
- option = self._resolve_option(option_or_name)
- except ConfigUnknownOptionError as e:
- if skip_unknown:
- log.debug(str(e))
- return
- raise
-
- # Can be useful if the values come from a source that doesn't have
- # strict typing (.ini file? Terminal input?)
- if parse_values:
- try:
- value = option.parse(value)
- except Exception as e:
- raise ConfigValueParsingError(option.name, value) from e
-
- if option.validate is not None and not option.validate(value):
- raise ConfigValueValidationError(option.name, value)
-
- self._values[option.name] = value
-
- def get(
- self, option_or_name: Union[Option, str], default: Any = _USE_GLOBAL_DEFAULT
- ) -> Any:
- """
- Get the value of an option. The value which is returned is the first
- provided among:
-
- 1. a user-provided value in the options's ``self._values`` dict
- 2. a caller-provided default value to this method call
- 3. the global default for the option provided in ``fontTools.config``
-
- This is to provide the ability to migrate progressively from config
- options passed as arguments to fontTools APIs to config options read
- from the current TTFont, e.g.
-
- .. code:: python
-
- def fontToolsAPI(font, some_option):
- value = font.cfg.get("someLib.module:SOME_OPTION", some_option)
- # use value
-
- That way, the function will work the same for users of the API that
- still pass the option to the function call, but will favour the new
- config mechanism if the given font specifies a value for that option.
- """
- option = self._resolve_option(option_or_name)
- if option.name in self._values:
- return self._values[option.name]
- if default is not _USE_GLOBAL_DEFAULT:
- return default
- return option.default
-
- def copy(self):
- return self.__class__(self._values)
-
- def __getitem__(self, option_or_name: Union[Option, str]) -> Any:
- return self.get(option_or_name)
-
- def __setitem__(self, option_or_name: Union[Option, str], value: Any) -> None:
- return self.set(option_or_name, value)
-
- def __delitem__(self, option_or_name: Union[Option, str]) -> None:
- option = self._resolve_option(option_or_name)
- del self._values[option.name]
-
- def __iter__(self) -> Iterable[str]:
- return self._values.__iter__()
-
- def __len__(self) -> int:
- return len(self._values)
-
- def __repr__(self) -> str:
- return f"{self.__class__.__name__}({repr(self._values)})"
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/cython.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/cython.py
deleted file mode 100644
index 2a42d94..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/cython.py
+++ /dev/null
@@ -1,27 +0,0 @@
-""" Exports a no-op 'cython' namespace similar to
-https://github.com/cython/cython/blob/master/Cython/Shadow.py
-
-This allows to optionally compile @cython decorated functions
-(when cython is available at built time), or run the same code
-as pure-python, without runtime dependency on cython module.
-
-We only define the symbols that we use. E.g. see fontTools.cu2qu
-"""
-
-from types import SimpleNamespace
-
-
-def _empty_decorator(x):
- return x
-
-
-compiled = False
-
-for name in ("double", "complex", "int"):
- globals()[name] = None
-
-for name in ("cfunc", "inline"):
- globals()[name] = _empty_decorator
-
-locals = lambda **_: _empty_decorator
-returns = lambda _: _empty_decorator
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/dictTools.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/dictTools.py
deleted file mode 100644
index e3c0df7..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/dictTools.py
+++ /dev/null
@@ -1,84 +0,0 @@
-"""Misc dict tools."""
-
-
-__all__ = ["hashdict"]
-
-
-# https://stackoverflow.com/questions/1151658/python-hashable-dicts
-class hashdict(dict):
- """
- hashable dict implementation, suitable for use as a key into
- other dicts.
-
- >>> h1 = hashdict({"apples": 1, "bananas":2})
- >>> h2 = hashdict({"bananas": 3, "mangoes": 5})
- >>> h1+h2
- hashdict(apples=1, bananas=3, mangoes=5)
- >>> d1 = {}
- >>> d1[h1] = "salad"
- >>> d1[h1]
- 'salad'
- >>> d1[h2]
- Traceback (most recent call last):
- ...
- KeyError: hashdict(bananas=3, mangoes=5)
-
- based on answers from
- http://stackoverflow.com/questions/1151658/python-hashable-dicts
-
- """
-
- def __key(self):
- return tuple(sorted(self.items()))
-
- def __repr__(self):
- return "{0}({1})".format(
- self.__class__.__name__,
- ", ".join("{0}={1}".format(str(i[0]), repr(i[1])) for i in self.__key()),
- )
-
- def __hash__(self):
- return hash(self.__key())
-
- def __setitem__(self, key, value):
- raise TypeError(
- "{0} does not support item assignment".format(self.__class__.__name__)
- )
-
- def __delitem__(self, key):
- raise TypeError(
- "{0} does not support item assignment".format(self.__class__.__name__)
- )
-
- def clear(self):
- raise TypeError(
- "{0} does not support item assignment".format(self.__class__.__name__)
- )
-
- def pop(self, *args, **kwargs):
- raise TypeError(
- "{0} does not support item assignment".format(self.__class__.__name__)
- )
-
- def popitem(self, *args, **kwargs):
- raise TypeError(
- "{0} does not support item assignment".format(self.__class__.__name__)
- )
-
- def setdefault(self, *args, **kwargs):
- raise TypeError(
- "{0} does not support item assignment".format(self.__class__.__name__)
- )
-
- def update(self, *args, **kwargs):
- raise TypeError(
- "{0} does not support item assignment".format(self.__class__.__name__)
- )
-
- # update is not ok because it mutates the object
- # __add__ is ok because it creates a new object
- # while the new object is under construction, it's ok to mutate it
- def __add__(self, right):
- result = hashdict(self)
- dict.update(result, right)
- return result
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/eexec.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/eexec.py
deleted file mode 100644
index cafa312..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/eexec.py
+++ /dev/null
@@ -1,119 +0,0 @@
-"""
-PostScript Type 1 fonts make use of two types of encryption: charstring
-encryption and ``eexec`` encryption. Charstring encryption is used for
-the charstrings themselves, while ``eexec`` is used to encrypt larger
-sections of the font program, such as the ``Private`` and ``CharStrings``
-dictionaries. Despite the different names, the algorithm is the same,
-although ``eexec`` encryption uses a fixed initial key R=55665.
-
-The algorithm uses cipher feedback, meaning that the ciphertext is used
-to modify the key. Because of this, the routines in this module return
-the new key at the end of the operation.
-
-"""
-
-from fontTools.misc.textTools import bytechr, bytesjoin, byteord
-
-
-def _decryptChar(cipher, R):
- cipher = byteord(cipher)
- plain = ((cipher ^ (R >> 8))) & 0xFF
- R = ((cipher + R) * 52845 + 22719) & 0xFFFF
- return bytechr(plain), R
-
-
-def _encryptChar(plain, R):
- plain = byteord(plain)
- cipher = ((plain ^ (R >> 8))) & 0xFF
- R = ((cipher + R) * 52845 + 22719) & 0xFFFF
- return bytechr(cipher), R
-
-
-def decrypt(cipherstring, R):
- r"""
- Decrypts a string using the Type 1 encryption algorithm.
-
- Args:
- cipherstring: String of ciphertext.
- R: Initial key.
-
- Returns:
- decryptedStr: Plaintext string.
- R: Output key for subsequent decryptions.
-
- Examples::
-
- >>> testStr = b"\0\0asdadads asds\265"
- >>> decryptedStr, R = decrypt(testStr, 12321)
- >>> decryptedStr == b'0d\nh\x15\xe8\xc4\xb2\x15\x1d\x108\x1a<6\xa1'
- True
- >>> R == 36142
- True
- """
- plainList = []
- for cipher in cipherstring:
- plain, R = _decryptChar(cipher, R)
- plainList.append(plain)
- plainstring = bytesjoin(plainList)
- return plainstring, int(R)
-
-
-def encrypt(plainstring, R):
- r"""
- Encrypts a string using the Type 1 encryption algorithm.
-
- Note that the algorithm as described in the Type 1 specification requires the
- plaintext to be prefixed with a number of random bytes. (For ``eexec`` the
- number of random bytes is set to 4.) This routine does *not* add the random
- prefix to its input.
-
- Args:
- plainstring: String of plaintext.
- R: Initial key.
-
- Returns:
- cipherstring: Ciphertext string.
- R: Output key for subsequent encryptions.
-
- Examples::
-
- >>> testStr = b"\0\0asdadads asds\265"
- >>> decryptedStr, R = decrypt(testStr, 12321)
- >>> decryptedStr == b'0d\nh\x15\xe8\xc4\xb2\x15\x1d\x108\x1a<6\xa1'
- True
- >>> R == 36142
- True
-
- >>> testStr = b'0d\nh\x15\xe8\xc4\xb2\x15\x1d\x108\x1a<6\xa1'
- >>> encryptedStr, R = encrypt(testStr, 12321)
- >>> encryptedStr == b"\0\0asdadads asds\265"
- True
- >>> R == 36142
- True
- """
- cipherList = []
- for plain in plainstring:
- cipher, R = _encryptChar(plain, R)
- cipherList.append(cipher)
- cipherstring = bytesjoin(cipherList)
- return cipherstring, int(R)
-
-
-def hexString(s):
- import binascii
-
- return binascii.hexlify(s)
-
-
-def deHexString(h):
- import binascii
-
- h = bytesjoin(h.split())
- return binascii.unhexlify(h)
-
-
-if __name__ == "__main__":
- import sys
- import doctest
-
- sys.exit(doctest.testmod().failed)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/encodingTools.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/encodingTools.py
deleted file mode 100644
index 43bf96a..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/encodingTools.py
+++ /dev/null
@@ -1,72 +0,0 @@
-"""fontTools.misc.encodingTools.py -- tools for working with OpenType encodings.
-"""
-
-import fontTools.encodings.codecs
-
-# Map keyed by platformID, then platEncID, then possibly langID
-_encodingMap = {
- 0: { # Unicode
- 0: "utf_16_be",
- 1: "utf_16_be",
- 2: "utf_16_be",
- 3: "utf_16_be",
- 4: "utf_16_be",
- 5: "utf_16_be",
- 6: "utf_16_be",
- },
- 1: { # Macintosh
- # See
- # https://github.com/fonttools/fonttools/issues/236
- 0: { # Macintosh, platEncID==0, keyed by langID
- 15: "mac_iceland",
- 17: "mac_turkish",
- 18: "mac_croatian",
- 24: "mac_latin2",
- 25: "mac_latin2",
- 26: "mac_latin2",
- 27: "mac_latin2",
- 28: "mac_latin2",
- 36: "mac_latin2",
- 37: "mac_romanian",
- 38: "mac_latin2",
- 39: "mac_latin2",
- 40: "mac_latin2",
- Ellipsis: "mac_roman", # Other
- },
- 1: "x_mac_japanese_ttx",
- 2: "x_mac_trad_chinese_ttx",
- 3: "x_mac_korean_ttx",
- 6: "mac_greek",
- 7: "mac_cyrillic",
- 25: "x_mac_simp_chinese_ttx",
- 29: "mac_latin2",
- 35: "mac_turkish",
- 37: "mac_iceland",
- },
- 2: {
- 0: "ascii",
- 1: "utf_16_be",
- 2: "latin1",
- }, # ISO
- 3: { # Microsoft
- 0: "utf_16_be",
- 1: "utf_16_be",
- 2: "shift_jis",
- 3: "gb2312",
- 4: "big5",
- 5: "euc_kr",
- 6: "johab",
- 10: "utf_16_be",
- },
-}
-
-
-def getEncoding(platformID, platEncID, langID, default=None):
- """Returns the Python encoding name for OpenType platformID/encodingID/langID
- triplet. If encoding for these values is not known, by default None is
- returned. That can be overriden by passing a value to the default argument.
- """
- encoding = _encodingMap.get(platformID, {}).get(platEncID, default)
- if isinstance(encoding, dict):
- encoding = encoding.get(langID, encoding[Ellipsis])
- return encoding
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/etree.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/etree.py
deleted file mode 100644
index 9d4a65c..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/etree.py
+++ /dev/null
@@ -1,478 +0,0 @@
-"""Shim module exporting the same ElementTree API for lxml and
-xml.etree backends.
-
-When lxml is installed, it is automatically preferred over the built-in
-xml.etree module.
-On Python 2.7, the cElementTree module is preferred over the pure-python
-ElementTree module.
-
-Besides exporting a unified interface, this also defines extra functions
-or subclasses built-in ElementTree classes to add features that are
-only availble in lxml, like OrderedDict for attributes, pretty_print and
-iterwalk.
-"""
-from fontTools.misc.textTools import tostr
-
-
-XML_DECLARATION = """"""
-
-__all__ = [
- # public symbols
- "Comment",
- "dump",
- "Element",
- "ElementTree",
- "fromstring",
- "fromstringlist",
- "iselement",
- "iterparse",
- "parse",
- "ParseError",
- "PI",
- "ProcessingInstruction",
- "QName",
- "SubElement",
- "tostring",
- "tostringlist",
- "TreeBuilder",
- "XML",
- "XMLParser",
- "register_namespace",
-]
-
-try:
- from lxml.etree import *
-
- _have_lxml = True
-except ImportError:
- try:
- from xml.etree.cElementTree import *
-
- # the cElementTree version of XML function doesn't support
- # the optional 'parser' keyword argument
- from xml.etree.ElementTree import XML
- except ImportError: # pragma: no cover
- from xml.etree.ElementTree import *
- _have_lxml = False
-
- import sys
-
- # dict is always ordered in python >= 3.6 and on pypy
- PY36 = sys.version_info >= (3, 6)
- try:
- import __pypy__
- except ImportError:
- __pypy__ = None
- _dict_is_ordered = bool(PY36 or __pypy__)
- del PY36, __pypy__
-
- if _dict_is_ordered:
- _Attrib = dict
- else:
- from collections import OrderedDict as _Attrib
-
- if isinstance(Element, type):
- _Element = Element
- else:
- # in py27, cElementTree.Element cannot be subclassed, so
- # we need to import the pure-python class
- from xml.etree.ElementTree import Element as _Element
-
- class Element(_Element):
- """Element subclass that keeps the order of attributes."""
-
- def __init__(self, tag, attrib=_Attrib(), **extra):
- super(Element, self).__init__(tag)
- self.attrib = _Attrib()
- if attrib:
- self.attrib.update(attrib)
- if extra:
- self.attrib.update(extra)
-
- def SubElement(parent, tag, attrib=_Attrib(), **extra):
- """Must override SubElement as well otherwise _elementtree.SubElement
- fails if 'parent' is a subclass of Element object.
- """
- element = parent.__class__(tag, attrib, **extra)
- parent.append(element)
- return element
-
- def _iterwalk(element, events, tag):
- include = tag is None or element.tag == tag
- if include and "start" in events:
- yield ("start", element)
- for e in element:
- for item in _iterwalk(e, events, tag):
- yield item
- if include:
- yield ("end", element)
-
- def iterwalk(element_or_tree, events=("end",), tag=None):
- """A tree walker that generates events from an existing tree as
- if it was parsing XML data with iterparse().
- Drop-in replacement for lxml.etree.iterwalk.
- """
- if iselement(element_or_tree):
- element = element_or_tree
- else:
- element = element_or_tree.getroot()
- if tag == "*":
- tag = None
- for item in _iterwalk(element, events, tag):
- yield item
-
- _ElementTree = ElementTree
-
- class ElementTree(_ElementTree):
- """ElementTree subclass that adds 'pretty_print' and 'doctype'
- arguments to the 'write' method.
- Currently these are only supported for the default XML serialization
- 'method', and not also for "html" or "text", for these are delegated
- to the base class.
- """
-
- def write(
- self,
- file_or_filename,
- encoding=None,
- xml_declaration=False,
- method=None,
- doctype=None,
- pretty_print=False,
- ):
- if method and method != "xml":
- # delegate to super-class
- super(ElementTree, self).write(
- file_or_filename,
- encoding=encoding,
- xml_declaration=xml_declaration,
- method=method,
- )
- return
-
- if encoding is not None and encoding.lower() == "unicode":
- if xml_declaration:
- raise ValueError(
- "Serialisation to unicode must not request an XML declaration"
- )
- write_declaration = False
- encoding = "unicode"
- elif xml_declaration is None:
- # by default, write an XML declaration only for non-standard encodings
- write_declaration = encoding is not None and encoding.upper() not in (
- "ASCII",
- "UTF-8",
- "UTF8",
- "US-ASCII",
- )
- else:
- write_declaration = xml_declaration
-
- if encoding is None:
- encoding = "ASCII"
-
- if pretty_print:
- # NOTE this will modify the tree in-place
- _indent(self._root)
-
- with _get_writer(file_or_filename, encoding) as write:
- if write_declaration:
- write(XML_DECLARATION % encoding.upper())
- if pretty_print:
- write("\n")
- if doctype:
- write(_tounicode(doctype))
- if pretty_print:
- write("\n")
-
- qnames, namespaces = _namespaces(self._root)
- _serialize_xml(write, self._root, qnames, namespaces)
-
- import io
-
- def tostring(
- element,
- encoding=None,
- xml_declaration=None,
- method=None,
- doctype=None,
- pretty_print=False,
- ):
- """Custom 'tostring' function that uses our ElementTree subclass, with
- pretty_print support.
- """
- stream = io.StringIO() if encoding == "unicode" else io.BytesIO()
- ElementTree(element).write(
- stream,
- encoding=encoding,
- xml_declaration=xml_declaration,
- method=method,
- doctype=doctype,
- pretty_print=pretty_print,
- )
- return stream.getvalue()
-
- # serialization support
-
- import re
-
- # Valid XML strings can include any Unicode character, excluding control
- # characters, the surrogate blocks, FFFE, and FFFF:
- # Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
- # Here we reversed the pattern to match only the invalid characters.
- # For the 'narrow' python builds supporting only UCS-2, which represent
- # characters beyond BMP as UTF-16 surrogate pairs, we need to pass through
- # the surrogate block. I haven't found a more elegant solution...
- UCS2 = sys.maxunicode < 0x10FFFF
- if UCS2:
- _invalid_xml_string = re.compile(
- "[\u0000-\u0008\u000B-\u000C\u000E-\u001F\uFFFE-\uFFFF]"
- )
- else:
- _invalid_xml_string = re.compile(
- "[\u0000-\u0008\u000B-\u000C\u000E-\u001F\uD800-\uDFFF\uFFFE-\uFFFF]"
- )
-
- def _tounicode(s):
- """Test if a string is valid user input and decode it to unicode string
- using ASCII encoding if it's a bytes string.
- Reject all bytes/unicode input that contains non-XML characters.
- Reject all bytes input that contains non-ASCII characters.
- """
- try:
- s = tostr(s, encoding="ascii", errors="strict")
- except UnicodeDecodeError:
- raise ValueError(
- "Bytes strings can only contain ASCII characters. "
- "Use unicode strings for non-ASCII characters."
- )
- except AttributeError:
- _raise_serialization_error(s)
- if s and _invalid_xml_string.search(s):
- raise ValueError(
- "All strings must be XML compatible: Unicode or ASCII, "
- "no NULL bytes or control characters"
- )
- return s
-
- import contextlib
-
- @contextlib.contextmanager
- def _get_writer(file_or_filename, encoding):
- # returns text write method and release all resources after using
- try:
- write = file_or_filename.write
- except AttributeError:
- # file_or_filename is a file name
- f = open(
- file_or_filename,
- "w",
- encoding="utf-8" if encoding == "unicode" else encoding,
- errors="xmlcharrefreplace",
- )
- with f:
- yield f.write
- else:
- # file_or_filename is a file-like object
- # encoding determines if it is a text or binary writer
- if encoding == "unicode":
- # use a text writer as is
- yield write
- else:
- # wrap a binary writer with TextIOWrapper
- detach_buffer = False
- if isinstance(file_or_filename, io.BufferedIOBase):
- buf = file_or_filename
- elif isinstance(file_or_filename, io.RawIOBase):
- buf = io.BufferedWriter(file_or_filename)
- detach_buffer = True
- else:
- # This is to handle passed objects that aren't in the
- # IOBase hierarchy, but just have a write method
- buf = io.BufferedIOBase()
- buf.writable = lambda: True
- buf.write = write
- try:
- # TextIOWrapper uses this methods to determine
- # if BOM (for UTF-16, etc) should be added
- buf.seekable = file_or_filename.seekable
- buf.tell = file_or_filename.tell
- except AttributeError:
- pass
- wrapper = io.TextIOWrapper(
- buf,
- encoding=encoding,
- errors="xmlcharrefreplace",
- newline="\n",
- )
- try:
- yield wrapper.write
- finally:
- # Keep the original file open when the TextIOWrapper and
- # the BufferedWriter are destroyed
- wrapper.detach()
- if detach_buffer:
- buf.detach()
-
- from xml.etree.ElementTree import _namespace_map
-
- def _namespaces(elem):
- # identify namespaces used in this tree
-
- # maps qnames to *encoded* prefix:local names
- qnames = {None: None}
-
- # maps uri:s to prefixes
- namespaces = {}
-
- def add_qname(qname):
- # calculate serialized qname representation
- try:
- qname = _tounicode(qname)
- if qname[:1] == "{":
- uri, tag = qname[1:].rsplit("}", 1)
- prefix = namespaces.get(uri)
- if prefix is None:
- prefix = _namespace_map.get(uri)
- if prefix is None:
- prefix = "ns%d" % len(namespaces)
- else:
- prefix = _tounicode(prefix)
- if prefix != "xml":
- namespaces[uri] = prefix
- if prefix:
- qnames[qname] = "%s:%s" % (prefix, tag)
- else:
- qnames[qname] = tag # default element
- else:
- qnames[qname] = qname
- except TypeError:
- _raise_serialization_error(qname)
-
- # populate qname and namespaces table
- for elem in elem.iter():
- tag = elem.tag
- if isinstance(tag, QName):
- if tag.text not in qnames:
- add_qname(tag.text)
- elif isinstance(tag, str):
- if tag not in qnames:
- add_qname(tag)
- elif tag is not None and tag is not Comment and tag is not PI:
- _raise_serialization_error(tag)
- for key, value in elem.items():
- if isinstance(key, QName):
- key = key.text
- if key not in qnames:
- add_qname(key)
- if isinstance(value, QName) and value.text not in qnames:
- add_qname(value.text)
- text = elem.text
- if isinstance(text, QName) and text.text not in qnames:
- add_qname(text.text)
- return qnames, namespaces
-
- def _serialize_xml(write, elem, qnames, namespaces, **kwargs):
- tag = elem.tag
- text = elem.text
- if tag is Comment:
- write("" % _tounicode(text))
- elif tag is ProcessingInstruction:
- write("%s?>" % _tounicode(text))
- else:
- tag = qnames[_tounicode(tag) if tag is not None else None]
- if tag is None:
- if text:
- write(_escape_cdata(text))
- for e in elem:
- _serialize_xml(write, e, qnames, None)
- else:
- write("<" + tag)
- if namespaces:
- for uri, prefix in sorted(
- namespaces.items(), key=lambda x: x[1]
- ): # sort on prefix
- if prefix:
- prefix = ":" + prefix
- write(' xmlns%s="%s"' % (prefix, _escape_attrib(uri)))
- attrs = elem.attrib
- if attrs:
- # try to keep existing attrib order
- if len(attrs) <= 1 or type(attrs) is _Attrib:
- items = attrs.items()
- else:
- # if plain dict, use lexical order
- items = sorted(attrs.items())
- for k, v in items:
- if isinstance(k, QName):
- k = _tounicode(k.text)
- else:
- k = _tounicode(k)
- if isinstance(v, QName):
- v = qnames[_tounicode(v.text)]
- else:
- v = _escape_attrib(v)
- write(' %s="%s"' % (qnames[k], v))
- if text is not None or len(elem):
- write(">")
- if text:
- write(_escape_cdata(text))
- for e in elem:
- _serialize_xml(write, e, qnames, None)
- write("" + tag + ">")
- else:
- write("/>")
- if elem.tail:
- write(_escape_cdata(elem.tail))
-
- def _raise_serialization_error(text):
- raise TypeError("cannot serialize %r (type %s)" % (text, type(text).__name__))
-
- def _escape_cdata(text):
- # escape character data
- try:
- text = _tounicode(text)
- # it's worth avoiding do-nothing calls for short strings
- if "&" in text:
- text = text.replace("&", "&")
- if "<" in text:
- text = text.replace("<", "<")
- if ">" in text:
- text = text.replace(">", ">")
- return text
- except (TypeError, AttributeError):
- _raise_serialization_error(text)
-
- def _escape_attrib(text):
- # escape attribute value
- try:
- text = _tounicode(text)
- if "&" in text:
- text = text.replace("&", "&")
- if "<" in text:
- text = text.replace("<", "<")
- if ">" in text:
- text = text.replace(">", ">")
- if '"' in text:
- text = text.replace('"', """)
- if "\n" in text:
- text = text.replace("\n", "
")
- return text
- except (TypeError, AttributeError):
- _raise_serialization_error(text)
-
- def _indent(elem, level=0):
- # From http://effbot.org/zone/element-lib.htm#prettyprint
- i = "\n" + level * " "
- if len(elem):
- if not elem.text or not elem.text.strip():
- elem.text = i + " "
- if not elem.tail or not elem.tail.strip():
- elem.tail = i
- for elem in elem:
- _indent(elem, level + 1)
- if not elem.tail or not elem.tail.strip():
- elem.tail = i
- else:
- if level and (not elem.tail or not elem.tail.strip()):
- elem.tail = i
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/filenames.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/filenames.py
deleted file mode 100644
index d279f89..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/filenames.py
+++ /dev/null
@@ -1,246 +0,0 @@
-"""
-This module implements the algorithm for converting between a "user name" -
-something that a user can choose arbitrarily inside a font editor - and a file
-name suitable for use in a wide range of operating systems and filesystems.
-
-The `UFO 3 specification `_
-provides an example of an algorithm for such conversion, which avoids illegal
-characters, reserved file names, ambiguity between upper- and lower-case
-characters, and clashes with existing files.
-
-This code was originally copied from
-`ufoLib `_
-by Tal Leming and is copyright (c) 2005-2016, The RoboFab Developers:
-
-- Erik van Blokland
-- Tal Leming
-- Just van Rossum
-"""
-
-
-illegalCharacters = r"\" * + / : < > ? [ \ ] | \0".split(" ")
-illegalCharacters += [chr(i) for i in range(1, 32)]
-illegalCharacters += [chr(0x7F)]
-reservedFileNames = "CON PRN AUX CLOCK$ NUL A:-Z: COM1".lower().split(" ")
-reservedFileNames += "LPT1 LPT2 LPT3 COM2 COM3 COM4".lower().split(" ")
-maxFileNameLength = 255
-
-
-class NameTranslationError(Exception):
- pass
-
-
-def userNameToFileName(userName, existing=[], prefix="", suffix=""):
- """Converts from a user name to a file name.
-
- Takes care to avoid illegal characters, reserved file names, ambiguity between
- upper- and lower-case characters, and clashes with existing files.
-
- Args:
- userName (str): The input file name.
- existing: A case-insensitive list of all existing file names.
- prefix: Prefix to be prepended to the file name.
- suffix: Suffix to be appended to the file name.
-
- Returns:
- A suitable filename.
-
- Raises:
- NameTranslationError: If no suitable name could be generated.
-
- Examples::
-
- >>> userNameToFileName("a") == "a"
- True
- >>> userNameToFileName("A") == "A_"
- True
- >>> userNameToFileName("AE") == "A_E_"
- True
- >>> userNameToFileName("Ae") == "A_e"
- True
- >>> userNameToFileName("ae") == "ae"
- True
- >>> userNameToFileName("aE") == "aE_"
- True
- >>> userNameToFileName("a.alt") == "a.alt"
- True
- >>> userNameToFileName("A.alt") == "A_.alt"
- True
- >>> userNameToFileName("A.Alt") == "A_.A_lt"
- True
- >>> userNameToFileName("A.aLt") == "A_.aL_t"
- True
- >>> userNameToFileName(u"A.alT") == "A_.alT_"
- True
- >>> userNameToFileName("T_H") == "T__H_"
- True
- >>> userNameToFileName("T_h") == "T__h"
- True
- >>> userNameToFileName("t_h") == "t_h"
- True
- >>> userNameToFileName("F_F_I") == "F__F__I_"
- True
- >>> userNameToFileName("f_f_i") == "f_f_i"
- True
- >>> userNameToFileName("Aacute_V.swash") == "A_acute_V_.swash"
- True
- >>> userNameToFileName(".notdef") == "_notdef"
- True
- >>> userNameToFileName("con") == "_con"
- True
- >>> userNameToFileName("CON") == "C_O_N_"
- True
- >>> userNameToFileName("con.alt") == "_con.alt"
- True
- >>> userNameToFileName("alt.con") == "alt._con"
- True
- """
- # the incoming name must be a str
- if not isinstance(userName, str):
- raise ValueError("The value for userName must be a string.")
- # establish the prefix and suffix lengths
- prefixLength = len(prefix)
- suffixLength = len(suffix)
- # replace an initial period with an _
- # if no prefix is to be added
- if not prefix and userName[0] == ".":
- userName = "_" + userName[1:]
- # filter the user name
- filteredUserName = []
- for character in userName:
- # replace illegal characters with _
- if character in illegalCharacters:
- character = "_"
- # add _ to all non-lower characters
- elif character != character.lower():
- character += "_"
- filteredUserName.append(character)
- userName = "".join(filteredUserName)
- # clip to 255
- sliceLength = maxFileNameLength - prefixLength - suffixLength
- userName = userName[:sliceLength]
- # test for illegal files names
- parts = []
- for part in userName.split("."):
- if part.lower() in reservedFileNames:
- part = "_" + part
- parts.append(part)
- userName = ".".join(parts)
- # test for clash
- fullName = prefix + userName + suffix
- if fullName.lower() in existing:
- fullName = handleClash1(userName, existing, prefix, suffix)
- # finished
- return fullName
-
-
-def handleClash1(userName, existing=[], prefix="", suffix=""):
- """
- existing should be a case-insensitive list
- of all existing file names.
-
- >>> prefix = ("0" * 5) + "."
- >>> suffix = "." + ("0" * 10)
- >>> existing = ["a" * 5]
-
- >>> e = list(existing)
- >>> handleClash1(userName="A" * 5, existing=e,
- ... prefix=prefix, suffix=suffix) == (
- ... '00000.AAAAA000000000000001.0000000000')
- True
-
- >>> e = list(existing)
- >>> e.append(prefix + "aaaaa" + "1".zfill(15) + suffix)
- >>> handleClash1(userName="A" * 5, existing=e,
- ... prefix=prefix, suffix=suffix) == (
- ... '00000.AAAAA000000000000002.0000000000')
- True
-
- >>> e = list(existing)
- >>> e.append(prefix + "AAAAA" + "2".zfill(15) + suffix)
- >>> handleClash1(userName="A" * 5, existing=e,
- ... prefix=prefix, suffix=suffix) == (
- ... '00000.AAAAA000000000000001.0000000000')
- True
- """
- # if the prefix length + user name length + suffix length + 15 is at
- # or past the maximum length, silce 15 characters off of the user name
- prefixLength = len(prefix)
- suffixLength = len(suffix)
- if prefixLength + len(userName) + suffixLength + 15 > maxFileNameLength:
- l = prefixLength + len(userName) + suffixLength + 15
- sliceLength = maxFileNameLength - l
- userName = userName[:sliceLength]
- finalName = None
- # try to add numbers to create a unique name
- counter = 1
- while finalName is None:
- name = userName + str(counter).zfill(15)
- fullName = prefix + name + suffix
- if fullName.lower() not in existing:
- finalName = fullName
- break
- else:
- counter += 1
- if counter >= 999999999999999:
- break
- # if there is a clash, go to the next fallback
- if finalName is None:
- finalName = handleClash2(existing, prefix, suffix)
- # finished
- return finalName
-
-
-def handleClash2(existing=[], prefix="", suffix=""):
- """
- existing should be a case-insensitive list
- of all existing file names.
-
- >>> prefix = ("0" * 5) + "."
- >>> suffix = "." + ("0" * 10)
- >>> existing = [prefix + str(i) + suffix for i in range(100)]
-
- >>> e = list(existing)
- >>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
- ... '00000.100.0000000000')
- True
-
- >>> e = list(existing)
- >>> e.remove(prefix + "1" + suffix)
- >>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
- ... '00000.1.0000000000')
- True
-
- >>> e = list(existing)
- >>> e.remove(prefix + "2" + suffix)
- >>> handleClash2(existing=e, prefix=prefix, suffix=suffix) == (
- ... '00000.2.0000000000')
- True
- """
- # calculate the longest possible string
- maxLength = maxFileNameLength - len(prefix) - len(suffix)
- maxValue = int("9" * maxLength)
- # try to find a number
- finalName = None
- counter = 1
- while finalName is None:
- fullName = prefix + str(counter) + suffix
- if fullName.lower() not in existing:
- finalName = fullName
- break
- else:
- counter += 1
- if counter >= maxValue:
- break
- # raise an error if nothing has been found
- if finalName is None:
- raise NameTranslationError("No unique name could be found.")
- # finished
- return finalName
-
-
-if __name__ == "__main__":
- import doctest
- import sys
-
- sys.exit(doctest.testmod().failed)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/fixedTools.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/fixedTools.py
deleted file mode 100644
index 3300428..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/fixedTools.py
+++ /dev/null
@@ -1,253 +0,0 @@
-"""
-The `OpenType specification `_
-defines two fixed-point data types:
-
-``Fixed``
- A 32-bit signed fixed-point number with a 16 bit twos-complement
- magnitude component and 16 fractional bits.
-``F2DOT14``
- A 16-bit signed fixed-point number with a 2 bit twos-complement
- magnitude component and 14 fractional bits.
-
-To support reading and writing data with these data types, this module provides
-functions for converting between fixed-point, float and string representations.
-
-.. data:: MAX_F2DOT14
-
- The maximum value that can still fit in an F2Dot14. (1.99993896484375)
-"""
-
-from .roundTools import otRound, nearestMultipleShortestRepr
-import logging
-
-log = logging.getLogger(__name__)
-
-__all__ = [
- "MAX_F2DOT14",
- "fixedToFloat",
- "floatToFixed",
- "floatToFixedToFloat",
- "floatToFixedToStr",
- "fixedToStr",
- "strToFixed",
- "strToFixedToFloat",
- "ensureVersionIsLong",
- "versionToFixed",
-]
-
-
-MAX_F2DOT14 = 0x7FFF / (1 << 14)
-
-
-def fixedToFloat(value, precisionBits):
- """Converts a fixed-point number to a float given the number of
- precision bits.
-
- Args:
- value (int): Number in fixed-point format.
- precisionBits (int): Number of precision bits.
-
- Returns:
- Floating point value.
-
- Examples::
-
- >>> import math
- >>> f = fixedToFloat(-10139, precisionBits=14)
- >>> math.isclose(f, -0.61883544921875)
- True
- """
- return value / (1 << precisionBits)
-
-
-def floatToFixed(value, precisionBits):
- """Converts a float to a fixed-point number given the number of
- precision bits.
-
- Args:
- value (float): Floating point value.
- precisionBits (int): Number of precision bits.
-
- Returns:
- int: Fixed-point representation.
-
- Examples::
-
- >>> floatToFixed(-0.61883544921875, precisionBits=14)
- -10139
- >>> floatToFixed(-0.61884, precisionBits=14)
- -10139
- """
- return otRound(value * (1 << precisionBits))
-
-
-def floatToFixedToFloat(value, precisionBits):
- """Converts a float to a fixed-point number and back again.
-
- By converting the float to fixed, rounding it, and converting it back
- to float again, this returns a floating point values which is exactly
- representable in fixed-point format.
-
- Note: this **is** equivalent to ``fixedToFloat(floatToFixed(value))``.
-
- Args:
- value (float): The input floating point value.
- precisionBits (int): Number of precision bits.
-
- Returns:
- float: The transformed and rounded value.
-
- Examples::
- >>> import math
- >>> f1 = -0.61884
- >>> f2 = floatToFixedToFloat(-0.61884, precisionBits=14)
- >>> f1 != f2
- True
- >>> math.isclose(f2, -0.61883544921875)
- True
- """
- scale = 1 << precisionBits
- return otRound(value * scale) / scale
-
-
-def fixedToStr(value, precisionBits):
- """Converts a fixed-point number to a string representing a decimal float.
-
- This chooses the float that has the shortest decimal representation (the least
- number of fractional decimal digits).
-
- For example, to convert a fixed-point number in a 2.14 format, use
- ``precisionBits=14``::
-
- >>> fixedToStr(-10139, precisionBits=14)
- '-0.61884'
-
- This is pretty slow compared to the simple division used in ``fixedToFloat``.
- Use sporadically when you need to serialize or print the fixed-point number in
- a human-readable form.
- It uses nearestMultipleShortestRepr under the hood.
-
- Args:
- value (int): The fixed-point value to convert.
- precisionBits (int): Number of precision bits, *up to a maximum of 16*.
-
- Returns:
- str: A string representation of the value.
- """
- scale = 1 << precisionBits
- return nearestMultipleShortestRepr(value / scale, factor=1.0 / scale)
-
-
-def strToFixed(string, precisionBits):
- """Converts a string representing a decimal float to a fixed-point number.
-
- Args:
- string (str): A string representing a decimal float.
- precisionBits (int): Number of precision bits, *up to a maximum of 16*.
-
- Returns:
- int: Fixed-point representation.
-
- Examples::
-
- >>> ## to convert a float string to a 2.14 fixed-point number:
- >>> strToFixed('-0.61884', precisionBits=14)
- -10139
- """
- value = float(string)
- return otRound(value * (1 << precisionBits))
-
-
-def strToFixedToFloat(string, precisionBits):
- """Convert a string to a decimal float with fixed-point rounding.
-
- This first converts string to a float, then turns it into a fixed-point
- number with ``precisionBits`` fractional binary digits, then back to a
- float again.
-
- This is simply a shorthand for fixedToFloat(floatToFixed(float(s))).
-
- Args:
- string (str): A string representing a decimal float.
- precisionBits (int): Number of precision bits.
-
- Returns:
- float: The transformed and rounded value.
-
- Examples::
-
- >>> import math
- >>> s = '-0.61884'
- >>> bits = 14
- >>> f = strToFixedToFloat(s, precisionBits=bits)
- >>> math.isclose(f, -0.61883544921875)
- True
- >>> f == fixedToFloat(floatToFixed(float(s), precisionBits=bits), precisionBits=bits)
- True
- """
- value = float(string)
- scale = 1 << precisionBits
- return otRound(value * scale) / scale
-
-
-def floatToFixedToStr(value, precisionBits):
- """Convert float to string with fixed-point rounding.
-
- This uses the shortest decimal representation (ie. the least
- number of fractional decimal digits) to represent the equivalent
- fixed-point number with ``precisionBits`` fractional binary digits.
- It uses nearestMultipleShortestRepr under the hood.
-
- >>> floatToFixedToStr(-0.61883544921875, precisionBits=14)
- '-0.61884'
-
- Args:
- value (float): The float value to convert.
- precisionBits (int): Number of precision bits, *up to a maximum of 16*.
-
- Returns:
- str: A string representation of the value.
-
- """
- scale = 1 << precisionBits
- return nearestMultipleShortestRepr(value, factor=1.0 / scale)
-
-
-def ensureVersionIsLong(value):
- """Ensure a table version is an unsigned long.
-
- OpenType table version numbers are expressed as a single unsigned long
- comprising of an unsigned short major version and unsigned short minor
- version. This function detects if the value to be used as a version number
- looks too small (i.e. is less than ``0x10000``), and converts it to
- fixed-point using :func:`floatToFixed` if so.
-
- Args:
- value (Number): a candidate table version number.
-
- Returns:
- int: A table version number, possibly corrected to fixed-point.
- """
- if value < 0x10000:
- newValue = floatToFixed(value, 16)
- log.warning(
- "Table version value is a float: %.4f; " "fix to use hex instead: 0x%08x",
- value,
- newValue,
- )
- value = newValue
- return value
-
-
-def versionToFixed(value):
- """Ensure a table version number is fixed-point.
-
- Args:
- value (str): a candidate table version number.
-
- Returns:
- int: A table version number, possibly corrected to fixed-point.
- """
- value = int(value, 0) if value.startswith("0") else float(value)
- value = ensureVersionIsLong(value)
- return value
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/intTools.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/intTools.py
deleted file mode 100644
index 0ca2985..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/intTools.py
+++ /dev/null
@@ -1,25 +0,0 @@
-__all__ = ["popCount", "bit_count", "bit_indices"]
-
-
-try:
- bit_count = int.bit_count
-except AttributeError:
-
- def bit_count(v):
- return bin(v).count("1")
-
-
-"""Return number of 1 bits (population count) of the absolute value of an integer.
-
-See https://docs.python.org/3.10/library/stdtypes.html#int.bit_count
-"""
-popCount = bit_count # alias
-
-
-def bit_indices(v):
- """Return list of indices where bits are set, 0 being the index of the least significant bit.
-
- >>> bit_indices(0b101)
- [0, 2]
- """
- return [i for i, b in enumerate(bin(v)[::-1]) if b == "1"]
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/loggingTools.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/loggingTools.py
deleted file mode 100644
index 78704f5..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/loggingTools.py
+++ /dev/null
@@ -1,543 +0,0 @@
-import sys
-import logging
-import timeit
-from functools import wraps
-from collections.abc import Mapping, Callable
-import warnings
-from logging import PercentStyle
-
-
-# default logging level used by Timer class
-TIME_LEVEL = logging.DEBUG
-
-# per-level format strings used by the default formatter
-# (the level name is not printed for INFO and DEBUG messages)
-DEFAULT_FORMATS = {
- "*": "%(levelname)s: %(message)s",
- "INFO": "%(message)s",
- "DEBUG": "%(message)s",
-}
-
-
-class LevelFormatter(logging.Formatter):
- """Log formatter with level-specific formatting.
-
- Formatter class which optionally takes a dict of logging levels to
- format strings, allowing to customise the log records appearance for
- specific levels.
-
-
- Attributes:
- fmt: A dictionary mapping logging levels to format strings.
- The ``*`` key identifies the default format string.
- datefmt: As per py:class:`logging.Formatter`
- style: As per py:class:`logging.Formatter`
-
- >>> import sys
- >>> handler = logging.StreamHandler(sys.stdout)
- >>> formatter = LevelFormatter(
- ... fmt={
- ... '*': '[%(levelname)s] %(message)s',
- ... 'DEBUG': '%(name)s [%(levelname)s] %(message)s',
- ... 'INFO': '%(message)s',
- ... })
- >>> handler.setFormatter(formatter)
- >>> log = logging.getLogger('test')
- >>> log.setLevel(logging.DEBUG)
- >>> log.addHandler(handler)
- >>> log.debug('this uses a custom format string')
- test [DEBUG] this uses a custom format string
- >>> log.info('this also uses a custom format string')
- this also uses a custom format string
- >>> log.warning("this one uses the default format string")
- [WARNING] this one uses the default format string
- """
-
- def __init__(self, fmt=None, datefmt=None, style="%"):
- if style != "%":
- raise ValueError(
- "only '%' percent style is supported in both python 2 and 3"
- )
- if fmt is None:
- fmt = DEFAULT_FORMATS
- if isinstance(fmt, str):
- default_format = fmt
- custom_formats = {}
- elif isinstance(fmt, Mapping):
- custom_formats = dict(fmt)
- default_format = custom_formats.pop("*", None)
- else:
- raise TypeError("fmt must be a str or a dict of str: %r" % fmt)
- super(LevelFormatter, self).__init__(default_format, datefmt)
- self.default_format = self._fmt
- self.custom_formats = {}
- for level, fmt in custom_formats.items():
- level = logging._checkLevel(level)
- self.custom_formats[level] = fmt
-
- def format(self, record):
- if self.custom_formats:
- fmt = self.custom_formats.get(record.levelno, self.default_format)
- if self._fmt != fmt:
- self._fmt = fmt
- # for python >= 3.2, _style needs to be set if _fmt changes
- if PercentStyle:
- self._style = PercentStyle(fmt)
- return super(LevelFormatter, self).format(record)
-
-
-def configLogger(**kwargs):
- """A more sophisticated logging system configuation manager.
-
- This is more or less the same as :py:func:`logging.basicConfig`,
- with some additional options and defaults.
-
- The default behaviour is to create a ``StreamHandler`` which writes to
- sys.stderr, set a formatter using the ``DEFAULT_FORMATS`` strings, and add
- the handler to the top-level library logger ("fontTools").
-
- A number of optional keyword arguments may be specified, which can alter
- the default behaviour.
-
- Args:
-
- logger: Specifies the logger name or a Logger instance to be
- configured. (Defaults to "fontTools" logger). Unlike ``basicConfig``,
- this function can be called multiple times to reconfigure a logger.
- If the logger or any of its children already exists before the call is
- made, they will be reset before the new configuration is applied.
- filename: Specifies that a ``FileHandler`` be created, using the
- specified filename, rather than a ``StreamHandler``.
- filemode: Specifies the mode to open the file, if filename is
- specified. (If filemode is unspecified, it defaults to ``a``).
- format: Use the specified format string for the handler. This
- argument also accepts a dictionary of format strings keyed by
- level name, to allow customising the records appearance for
- specific levels. The special ``'*'`` key is for 'any other' level.
- datefmt: Use the specified date/time format.
- level: Set the logger level to the specified level.
- stream: Use the specified stream to initialize the StreamHandler. Note
- that this argument is incompatible with ``filename`` - if both
- are present, ``stream`` is ignored.
- handlers: If specified, this should be an iterable of already created
- handlers, which will be added to the logger. Any handler in the
- list which does not have a formatter assigned will be assigned the
- formatter created in this function.
- filters: If specified, this should be an iterable of already created
- filters. If the ``handlers`` do not already have filters assigned,
- these filters will be added to them.
- propagate: All loggers have a ``propagate`` attribute which determines
- whether to continue searching for handlers up the logging hierarchy.
- If not provided, the "propagate" attribute will be set to ``False``.
- """
- # using kwargs to enforce keyword-only arguments in py2.
- handlers = kwargs.pop("handlers", None)
- if handlers is None:
- if "stream" in kwargs and "filename" in kwargs:
- raise ValueError(
- "'stream' and 'filename' should not be " "specified together"
- )
- else:
- if "stream" in kwargs or "filename" in kwargs:
- raise ValueError(
- "'stream' or 'filename' should not be "
- "specified together with 'handlers'"
- )
- if handlers is None:
- filename = kwargs.pop("filename", None)
- mode = kwargs.pop("filemode", "a")
- if filename:
- h = logging.FileHandler(filename, mode)
- else:
- stream = kwargs.pop("stream", None)
- h = logging.StreamHandler(stream)
- handlers = [h]
- # By default, the top-level library logger is configured.
- logger = kwargs.pop("logger", "fontTools")
- if not logger or isinstance(logger, str):
- # empty "" or None means the 'root' logger
- logger = logging.getLogger(logger)
- # before (re)configuring, reset named logger and its children (if exist)
- _resetExistingLoggers(parent=logger.name)
- # use DEFAULT_FORMATS if 'format' is None
- fs = kwargs.pop("format", None)
- dfs = kwargs.pop("datefmt", None)
- # XXX: '%' is the only format style supported on both py2 and 3
- style = kwargs.pop("style", "%")
- fmt = LevelFormatter(fs, dfs, style)
- filters = kwargs.pop("filters", [])
- for h in handlers:
- if h.formatter is None:
- h.setFormatter(fmt)
- if not h.filters:
- for f in filters:
- h.addFilter(f)
- logger.addHandler(h)
- if logger.name != "root":
- # stop searching up the hierarchy for handlers
- logger.propagate = kwargs.pop("propagate", False)
- # set a custom severity level
- level = kwargs.pop("level", None)
- if level is not None:
- logger.setLevel(level)
- if kwargs:
- keys = ", ".join(kwargs.keys())
- raise ValueError("Unrecognised argument(s): %s" % keys)
-
-
-def _resetExistingLoggers(parent="root"):
- """Reset the logger named 'parent' and all its children to their initial
- state, if they already exist in the current configuration.
- """
- root = logging.root
- # get sorted list of all existing loggers
- existing = sorted(root.manager.loggerDict.keys())
- if parent == "root":
- # all the existing loggers are children of 'root'
- loggers_to_reset = [parent] + existing
- elif parent not in existing:
- # nothing to do
- return
- elif parent in existing:
- loggers_to_reset = [parent]
- # collect children, starting with the entry after parent name
- i = existing.index(parent) + 1
- prefixed = parent + "."
- pflen = len(prefixed)
- num_existing = len(existing)
- while i < num_existing:
- if existing[i][:pflen] == prefixed:
- loggers_to_reset.append(existing[i])
- i += 1
- for name in loggers_to_reset:
- if name == "root":
- root.setLevel(logging.WARNING)
- for h in root.handlers[:]:
- root.removeHandler(h)
- for f in root.filters[:]:
- root.removeFilters(f)
- root.disabled = False
- else:
- logger = root.manager.loggerDict[name]
- logger.level = logging.NOTSET
- logger.handlers = []
- logger.filters = []
- logger.propagate = True
- logger.disabled = False
-
-
-class Timer(object):
- """Keeps track of overall time and split/lap times.
-
- >>> import time
- >>> timer = Timer()
- >>> time.sleep(0.01)
- >>> print("First lap:", timer.split())
- First lap: ...
- >>> time.sleep(0.02)
- >>> print("Second lap:", timer.split())
- Second lap: ...
- >>> print("Overall time:", timer.time())
- Overall time: ...
-
- Can be used as a context manager inside with-statements.
-
- >>> with Timer() as t:
- ... time.sleep(0.01)
- >>> print("%0.3f seconds" % t.elapsed)
- 0... seconds
-
- If initialised with a logger, it can log the elapsed time automatically
- upon exiting the with-statement.
-
- >>> import logging
- >>> log = logging.getLogger("my-fancy-timer-logger")
- >>> configLogger(logger=log, level="DEBUG", format="%(message)s", stream=sys.stdout)
- >>> with Timer(log, 'do something'):
- ... time.sleep(0.01)
- Took ... to do something
-
- The same Timer instance, holding a reference to a logger, can be reused
- in multiple with-statements, optionally with different messages or levels.
-
- >>> timer = Timer(log)
- >>> with timer():
- ... time.sleep(0.01)
- elapsed time: ...s
- >>> with timer('redo it', level=logging.INFO):
- ... time.sleep(0.02)
- Took ... to redo it
-
- It can also be used as a function decorator to log the time elapsed to run
- the decorated function.
-
- >>> @timer()
- ... def test1():
- ... time.sleep(0.01)
- >>> @timer('run test 2', level=logging.INFO)
- ... def test2():
- ... time.sleep(0.02)
- >>> test1()
- Took ... to run 'test1'
- >>> test2()
- Took ... to run test 2
- """
-
- # timeit.default_timer choses the most accurate clock for each platform
- _time = timeit.default_timer
- default_msg = "elapsed time: %(time).3fs"
- default_format = "Took %(time).3fs to %(msg)s"
-
- def __init__(self, logger=None, msg=None, level=None, start=None):
- self.reset(start)
- if logger is None:
- for arg in ("msg", "level"):
- if locals().get(arg) is not None:
- raise ValueError("'%s' can't be specified without a 'logger'" % arg)
- self.logger = logger
- self.level = level if level is not None else TIME_LEVEL
- self.msg = msg
-
- def reset(self, start=None):
- """Reset timer to 'start_time' or the current time."""
- if start is None:
- self.start = self._time()
- else:
- self.start = start
- self.last = self.start
- self.elapsed = 0.0
-
- def time(self):
- """Return the overall time (in seconds) since the timer started."""
- return self._time() - self.start
-
- def split(self):
- """Split and return the lap time (in seconds) in between splits."""
- current = self._time()
- self.elapsed = current - self.last
- self.last = current
- return self.elapsed
-
- def formatTime(self, msg, time):
- """Format 'time' value in 'msg' and return formatted string.
- If 'msg' contains a '%(time)' format string, try to use that.
- Otherwise, use the predefined 'default_format'.
- If 'msg' is empty or None, fall back to 'default_msg'.
- """
- if not msg:
- msg = self.default_msg
- if msg.find("%(time)") < 0:
- msg = self.default_format % {"msg": msg, "time": time}
- else:
- try:
- msg = msg % {"time": time}
- except (KeyError, ValueError):
- pass # skip if the format string is malformed
- return msg
-
- def __enter__(self):
- """Start a new lap"""
- self.last = self._time()
- self.elapsed = 0.0
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- """End the current lap. If timer has a logger, log the time elapsed,
- using the format string in self.msg (or the default one).
- """
- time = self.split()
- if self.logger is None or exc_type:
- # if there's no logger attached, or if any exception occurred in
- # the with-statement, exit without logging the time
- return
- message = self.formatTime(self.msg, time)
- # Allow log handlers to see the individual parts to facilitate things
- # like a server accumulating aggregate stats.
- msg_parts = {"msg": self.msg, "time": time}
- self.logger.log(self.level, message, msg_parts)
-
- def __call__(self, func_or_msg=None, **kwargs):
- """If the first argument is a function, return a decorator which runs
- the wrapped function inside Timer's context manager.
- Otherwise, treat the first argument as a 'msg' string and return an updated
- Timer instance, referencing the same logger.
- A 'level' keyword can also be passed to override self.level.
- """
- if isinstance(func_or_msg, Callable):
- func = func_or_msg
- # use the function name when no explicit 'msg' is provided
- if not self.msg:
- self.msg = "run '%s'" % func.__name__
-
- @wraps(func)
- def wrapper(*args, **kwds):
- with self:
- return func(*args, **kwds)
-
- return wrapper
- else:
- msg = func_or_msg or kwargs.get("msg")
- level = kwargs.get("level", self.level)
- return self.__class__(self.logger, msg, level)
-
- def __float__(self):
- return self.elapsed
-
- def __int__(self):
- return int(self.elapsed)
-
- def __str__(self):
- return "%.3f" % self.elapsed
-
-
-class ChannelsFilter(logging.Filter):
- """Provides a hierarchical filter for log entries based on channel names.
-
- Filters out records emitted from a list of enabled channel names,
- including their children. It works the same as the ``logging.Filter``
- class, but allows the user to specify multiple channel names.
-
- >>> import sys
- >>> handler = logging.StreamHandler(sys.stdout)
- >>> handler.setFormatter(logging.Formatter("%(message)s"))
- >>> filter = ChannelsFilter("A.B", "C.D")
- >>> handler.addFilter(filter)
- >>> root = logging.getLogger()
- >>> root.addHandler(handler)
- >>> root.setLevel(level=logging.DEBUG)
- >>> logging.getLogger('A.B').debug('this record passes through')
- this record passes through
- >>> logging.getLogger('A.B.C').debug('records from children also pass')
- records from children also pass
- >>> logging.getLogger('C.D').debug('this one as well')
- this one as well
- >>> logging.getLogger('A.B.').debug('also this one')
- also this one
- >>> logging.getLogger('A.F').debug('but this one does not!')
- >>> logging.getLogger('C.DE').debug('neither this one!')
- """
-
- def __init__(self, *names):
- self.names = names
- self.num = len(names)
- self.lengths = {n: len(n) for n in names}
-
- def filter(self, record):
- if self.num == 0:
- return True
- for name in self.names:
- nlen = self.lengths[name]
- if name == record.name:
- return True
- elif record.name.find(name, 0, nlen) == 0 and record.name[nlen] == ".":
- return True
- return False
-
-
-class CapturingLogHandler(logging.Handler):
- def __init__(self, logger, level):
- super(CapturingLogHandler, self).__init__(level=level)
- self.records = []
- if isinstance(logger, str):
- self.logger = logging.getLogger(logger)
- else:
- self.logger = logger
-
- def __enter__(self):
- self.original_disabled = self.logger.disabled
- self.original_level = self.logger.level
- self.original_propagate = self.logger.propagate
-
- self.logger.addHandler(self)
- self.logger.setLevel(self.level)
- self.logger.disabled = False
- self.logger.propagate = False
-
- return self
-
- def __exit__(self, type, value, traceback):
- self.logger.removeHandler(self)
- self.logger.setLevel(self.original_level)
- self.logger.disabled = self.original_disabled
- self.logger.propagate = self.original_propagate
-
- return self
-
- def emit(self, record):
- self.records.append(record)
-
- def assertRegex(self, regexp, msg=None):
- import re
-
- pattern = re.compile(regexp)
- for r in self.records:
- if pattern.search(r.getMessage()):
- return True
- if msg is None:
- msg = "Pattern '%s' not found in logger records" % regexp
- assert 0, msg
-
-
-class LogMixin(object):
- """Mixin class that adds logging functionality to another class.
-
- You can define a new class that subclasses from ``LogMixin`` as well as
- other base classes through multiple inheritance.
- All instances of that class will have a ``log`` property that returns
- a ``logging.Logger`` named after their respective ``.``.
-
- For example:
-
- >>> class BaseClass(object):
- ... pass
- >>> class MyClass(LogMixin, BaseClass):
- ... pass
- >>> a = MyClass()
- >>> isinstance(a.log, logging.Logger)
- True
- >>> print(a.log.name)
- fontTools.misc.loggingTools.MyClass
- >>> class AnotherClass(MyClass):
- ... pass
- >>> b = AnotherClass()
- >>> isinstance(b.log, logging.Logger)
- True
- >>> print(b.log.name)
- fontTools.misc.loggingTools.AnotherClass
- """
-
- @property
- def log(self):
- if not hasattr(self, "_log"):
- name = ".".join((self.__class__.__module__, self.__class__.__name__))
- self._log = logging.getLogger(name)
- return self._log
-
-
-def deprecateArgument(name, msg, category=UserWarning):
- """Raise a warning about deprecated function argument 'name'."""
- warnings.warn("%r is deprecated; %s" % (name, msg), category=category, stacklevel=3)
-
-
-def deprecateFunction(msg, category=UserWarning):
- """Decorator to raise a warning when a deprecated function is called."""
-
- def decorator(func):
- @wraps(func)
- def wrapper(*args, **kwargs):
- warnings.warn(
- "%r is deprecated; %s" % (func.__name__, msg),
- category=category,
- stacklevel=2,
- )
- return func(*args, **kwargs)
-
- return wrapper
-
- return decorator
-
-
-if __name__ == "__main__":
- import doctest
-
- sys.exit(doctest.testmod(optionflags=doctest.ELLIPSIS).failed)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/macCreatorType.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/macCreatorType.py
deleted file mode 100644
index 36b15ac..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/macCreatorType.py
+++ /dev/null
@@ -1,56 +0,0 @@
-from fontTools.misc.textTools import Tag, bytesjoin, strjoin
-
-try:
- import xattr
-except ImportError:
- xattr = None
-
-
-def _reverseString(s):
- s = list(s)
- s.reverse()
- return strjoin(s)
-
-
-def getMacCreatorAndType(path):
- """Returns file creator and file type codes for a path.
-
- Args:
- path (str): A file path.
-
- Returns:
- A tuple of two :py:class:`fontTools.textTools.Tag` objects, the first
- representing the file creator and the second representing the
- file type.
- """
- if xattr is not None:
- try:
- finderInfo = xattr.getxattr(path, "com.apple.FinderInfo")
- except (KeyError, IOError):
- pass
- else:
- fileType = Tag(finderInfo[:4])
- fileCreator = Tag(finderInfo[4:8])
- return fileCreator, fileType
- return None, None
-
-
-def setMacCreatorAndType(path, fileCreator, fileType):
- """Set file creator and file type codes for a path.
-
- Note that if the ``xattr`` module is not installed, no action is
- taken but no error is raised.
-
- Args:
- path (str): A file path.
- fileCreator: A four-character file creator tag.
- fileType: A four-character file type tag.
-
- """
- if xattr is not None:
- from fontTools.misc.textTools import pad
-
- if not all(len(s) == 4 for s in (fileCreator, fileType)):
- raise TypeError("arg must be string of 4 chars")
- finderInfo = pad(bytesjoin([fileType, fileCreator]), 32)
- xattr.setxattr(path, "com.apple.FinderInfo", finderInfo)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/macRes.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/macRes.py
deleted file mode 100644
index f5a6cfe..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/macRes.py
+++ /dev/null
@@ -1,261 +0,0 @@
-from io import BytesIO
-import struct
-from fontTools.misc import sstruct
-from fontTools.misc.textTools import bytesjoin, tostr
-from collections import OrderedDict
-from collections.abc import MutableMapping
-
-
-class ResourceError(Exception):
- pass
-
-
-class ResourceReader(MutableMapping):
- """Reader for Mac OS resource forks.
-
- Parses a resource fork and returns resources according to their type.
- If run on OS X, this will open the resource fork in the filesystem.
- Otherwise, it will open the file itself and attempt to read it as
- though it were a resource fork.
-
- The returned object can be indexed by type and iterated over,
- returning in each case a list of py:class:`Resource` objects
- representing all the resources of a certain type.
-
- """
-
- def __init__(self, fileOrPath):
- """Open a file
-
- Args:
- fileOrPath: Either an object supporting a ``read`` method, an
- ``os.PathLike`` object, or a string.
- """
- self._resources = OrderedDict()
- if hasattr(fileOrPath, "read"):
- self.file = fileOrPath
- else:
- try:
- # try reading from the resource fork (only works on OS X)
- self.file = self.openResourceFork(fileOrPath)
- self._readFile()
- return
- except (ResourceError, IOError):
- # if it fails, use the data fork
- self.file = self.openDataFork(fileOrPath)
- self._readFile()
-
- @staticmethod
- def openResourceFork(path):
- if hasattr(path, "__fspath__"): # support os.PathLike objects
- path = path.__fspath__()
- with open(path + "/..namedfork/rsrc", "rb") as resfork:
- data = resfork.read()
- infile = BytesIO(data)
- infile.name = path
- return infile
-
- @staticmethod
- def openDataFork(path):
- with open(path, "rb") as datafork:
- data = datafork.read()
- infile = BytesIO(data)
- infile.name = path
- return infile
-
- def _readFile(self):
- self._readHeaderAndMap()
- self._readTypeList()
-
- def _read(self, numBytes, offset=None):
- if offset is not None:
- try:
- self.file.seek(offset)
- except OverflowError:
- raise ResourceError("Failed to seek offset ('offset' is too large)")
- if self.file.tell() != offset:
- raise ResourceError("Failed to seek offset (reached EOF)")
- try:
- data = self.file.read(numBytes)
- except OverflowError:
- raise ResourceError("Cannot read resource ('numBytes' is too large)")
- if len(data) != numBytes:
- raise ResourceError("Cannot read resource (not enough data)")
- return data
-
- def _readHeaderAndMap(self):
- self.file.seek(0)
- headerData = self._read(ResourceForkHeaderSize)
- sstruct.unpack(ResourceForkHeader, headerData, self)
- # seek to resource map, skip reserved
- mapOffset = self.mapOffset + 22
- resourceMapData = self._read(ResourceMapHeaderSize, mapOffset)
- sstruct.unpack(ResourceMapHeader, resourceMapData, self)
- self.absTypeListOffset = self.mapOffset + self.typeListOffset
- self.absNameListOffset = self.mapOffset + self.nameListOffset
-
- def _readTypeList(self):
- absTypeListOffset = self.absTypeListOffset
- numTypesData = self._read(2, absTypeListOffset)
- (self.numTypes,) = struct.unpack(">H", numTypesData)
- absTypeListOffset2 = absTypeListOffset + 2
- for i in range(self.numTypes + 1):
- resTypeItemOffset = absTypeListOffset2 + ResourceTypeItemSize * i
- resTypeItemData = self._read(ResourceTypeItemSize, resTypeItemOffset)
- item = sstruct.unpack(ResourceTypeItem, resTypeItemData)
- resType = tostr(item["type"], encoding="mac-roman")
- refListOffset = absTypeListOffset + item["refListOffset"]
- numRes = item["numRes"] + 1
- resources = self._readReferenceList(resType, refListOffset, numRes)
- self._resources[resType] = resources
-
- def _readReferenceList(self, resType, refListOffset, numRes):
- resources = []
- for i in range(numRes):
- refOffset = refListOffset + ResourceRefItemSize * i
- refData = self._read(ResourceRefItemSize, refOffset)
- res = Resource(resType)
- res.decompile(refData, self)
- resources.append(res)
- return resources
-
- def __getitem__(self, resType):
- return self._resources[resType]
-
- def __delitem__(self, resType):
- del self._resources[resType]
-
- def __setitem__(self, resType, resources):
- self._resources[resType] = resources
-
- def __len__(self):
- return len(self._resources)
-
- def __iter__(self):
- return iter(self._resources)
-
- def keys(self):
- return self._resources.keys()
-
- @property
- def types(self):
- """A list of the types of resources in the resource fork."""
- return list(self._resources.keys())
-
- def countResources(self, resType):
- """Return the number of resources of a given type."""
- try:
- return len(self[resType])
- except KeyError:
- return 0
-
- def getIndices(self, resType):
- """Returns a list of indices of resources of a given type."""
- numRes = self.countResources(resType)
- if numRes:
- return list(range(1, numRes + 1))
- else:
- return []
-
- def getNames(self, resType):
- """Return list of names of all resources of a given type."""
- return [res.name for res in self.get(resType, []) if res.name is not None]
-
- def getIndResource(self, resType, index):
- """Return resource of given type located at an index ranging from 1
- to the number of resources for that type, or None if not found.
- """
- if index < 1:
- return None
- try:
- res = self[resType][index - 1]
- except (KeyError, IndexError):
- return None
- return res
-
- def getNamedResource(self, resType, name):
- """Return the named resource of given type, else return None."""
- name = tostr(name, encoding="mac-roman")
- for res in self.get(resType, []):
- if res.name == name:
- return res
- return None
-
- def close(self):
- if not self.file.closed:
- self.file.close()
-
-
-class Resource(object):
- """Represents a resource stored within a resource fork.
-
- Attributes:
- type: resource type.
- data: resource data.
- id: ID.
- name: resource name.
- attr: attributes.
- """
-
- def __init__(
- self, resType=None, resData=None, resID=None, resName=None, resAttr=None
- ):
- self.type = resType
- self.data = resData
- self.id = resID
- self.name = resName
- self.attr = resAttr
-
- def decompile(self, refData, reader):
- sstruct.unpack(ResourceRefItem, refData, self)
- # interpret 3-byte dataOffset as (padded) ULONG to unpack it with struct
- (self.dataOffset,) = struct.unpack(">L", bytesjoin([b"\0", self.dataOffset]))
- absDataOffset = reader.dataOffset + self.dataOffset
- (dataLength,) = struct.unpack(">L", reader._read(4, absDataOffset))
- self.data = reader._read(dataLength)
- if self.nameOffset == -1:
- return
- absNameOffset = reader.absNameListOffset + self.nameOffset
- (nameLength,) = struct.unpack("B", reader._read(1, absNameOffset))
- (name,) = struct.unpack(">%ss" % nameLength, reader._read(nameLength))
- self.name = tostr(name, encoding="mac-roman")
-
-
-ResourceForkHeader = """
- > # big endian
- dataOffset: L
- mapOffset: L
- dataLen: L
- mapLen: L
-"""
-
-ResourceForkHeaderSize = sstruct.calcsize(ResourceForkHeader)
-
-ResourceMapHeader = """
- > # big endian
- attr: H
- typeListOffset: H
- nameListOffset: H
-"""
-
-ResourceMapHeaderSize = sstruct.calcsize(ResourceMapHeader)
-
-ResourceTypeItem = """
- > # big endian
- type: 4s
- numRes: H
- refListOffset: H
-"""
-
-ResourceTypeItemSize = sstruct.calcsize(ResourceTypeItem)
-
-ResourceRefItem = """
- > # big endian
- id: h
- nameOffset: h
- attr: B
- dataOffset: 3s
- reserved: L
-"""
-
-ResourceRefItemSize = sstruct.calcsize(ResourceRefItem)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/plistlib/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/plistlib/__init__.py
deleted file mode 100644
index 066eef3..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/plistlib/__init__.py
+++ /dev/null
@@ -1,681 +0,0 @@
-import collections.abc
-import re
-from typing import (
- Any,
- Callable,
- Dict,
- List,
- Mapping,
- MutableMapping,
- Optional,
- Sequence,
- Type,
- Union,
- IO,
-)
-import warnings
-from io import BytesIO
-from datetime import datetime
-from base64 import b64encode, b64decode
-from numbers import Integral
-from types import SimpleNamespace
-from functools import singledispatch
-
-from fontTools.misc import etree
-
-from fontTools.misc.textTools import tostr
-
-
-# By default, we
-# - deserialize elements as bytes and
-# - serialize bytes as elements.
-# Before, on Python 2, we
-# - deserialized elements as plistlib.Data objects, in order to
-# distinguish them from the built-in str type (which is bytes on python2)
-# - serialized bytes as elements (they must have only contained
-# ASCII characters in this case)
-# You can pass use_builtin_types=[True|False] to the load/dump etc. functions
-# to enforce a specific treatment.
-# NOTE that unicode type always maps to element, and plistlib.Data
-# always maps to element, regardless of use_builtin_types.
-USE_BUILTIN_TYPES = True
-
-XML_DECLARATION = b""""""
-
-PLIST_DOCTYPE = (
- b''
-)
-
-
-# Date should conform to a subset of ISO 8601:
-# YYYY '-' MM '-' DD 'T' HH ':' MM ':' SS 'Z'
-_date_parser = re.compile(
- r"(?P\d\d\d\d)"
- r"(?:-(?P\d\d)"
- r"(?:-(?P\d\d)"
- r"(?:T(?P\d\d)"
- r"(?::(?P\d\d)"
- r"(?::(?P\d\d))"
- r"?)?)?)?)?Z",
- re.ASCII,
-)
-
-
-def _date_from_string(s: str) -> datetime:
- order = ("year", "month", "day", "hour", "minute", "second")
- m = _date_parser.match(s)
- if m is None:
- raise ValueError(f"Expected ISO 8601 date string, but got '{s:r}'.")
- gd = m.groupdict()
- lst = []
- for key in order:
- val = gd[key]
- if val is None:
- break
- lst.append(int(val))
- # NOTE: mypy doesn't know that lst is 6 elements long.
- return datetime(*lst) # type:ignore
-
-
-def _date_to_string(d: datetime) -> str:
- return "%04d-%02d-%02dT%02d:%02d:%02dZ" % (
- d.year,
- d.month,
- d.day,
- d.hour,
- d.minute,
- d.second,
- )
-
-
-class Data:
- """Represents binary data when ``use_builtin_types=False.``
-
- This class wraps binary data loaded from a plist file when the
- ``use_builtin_types`` argument to the loading function (:py:func:`fromtree`,
- :py:func:`load`, :py:func:`loads`) is false.
-
- The actual binary data is retrieved using the ``data`` attribute.
- """
-
- def __init__(self, data: bytes) -> None:
- if not isinstance(data, bytes):
- raise TypeError("Expected bytes, found %s" % type(data).__name__)
- self.data = data
-
- @classmethod
- def fromBase64(cls, data: Union[bytes, str]) -> "Data":
- return cls(b64decode(data))
-
- def asBase64(self, maxlinelength: int = 76, indent_level: int = 1) -> bytes:
- return _encode_base64(
- self.data, maxlinelength=maxlinelength, indent_level=indent_level
- )
-
- def __eq__(self, other: Any) -> bool:
- if isinstance(other, self.__class__):
- return self.data == other.data
- elif isinstance(other, bytes):
- return self.data == other
- else:
- return NotImplemented
-
- def __repr__(self) -> str:
- return "%s(%s)" % (self.__class__.__name__, repr(self.data))
-
-
-def _encode_base64(
- data: bytes, maxlinelength: Optional[int] = 76, indent_level: int = 1
-) -> bytes:
- data = b64encode(data)
- if data and maxlinelength:
- # split into multiple lines right-justified to 'maxlinelength' chars
- indent = b"\n" + b" " * indent_level
- max_length = max(16, maxlinelength - len(indent))
- chunks = []
- for i in range(0, len(data), max_length):
- chunks.append(indent)
- chunks.append(data[i : i + max_length])
- chunks.append(indent)
- data = b"".join(chunks)
- return data
-
-
-# Mypy does not support recursive type aliases as of 0.782, Pylance does.
-# https://github.com/python/mypy/issues/731
-# https://devblogs.microsoft.com/python/pylance-introduces-five-new-features-that-enable-type-magic-for-python-developers/#1-support-for-recursive-type-aliases
-PlistEncodable = Union[
- bool,
- bytes,
- Data,
- datetime,
- float,
- Integral,
- Mapping[str, Any],
- Sequence[Any],
- str,
-]
-
-
-class PlistTarget:
- """Event handler using the ElementTree Target API that can be
- passed to a XMLParser to produce property list objects from XML.
- It is based on the CPython plistlib module's _PlistParser class,
- but does not use the expat parser.
-
- >>> from fontTools.misc import etree
- >>> parser = etree.XMLParser(target=PlistTarget())
- >>> result = etree.XML(
- ... ""
- ... " something "
- ... " blah "
- ... " ",
- ... parser=parser)
- >>> result == {"something": "blah"}
- True
-
- Links:
- https://github.com/python/cpython/blob/main/Lib/plistlib.py
- http://lxml.de/parsing.html#the-target-parser-interface
- """
-
- def __init__(
- self,
- use_builtin_types: Optional[bool] = None,
- dict_type: Type[MutableMapping[str, Any]] = dict,
- ) -> None:
- self.stack: List[PlistEncodable] = []
- self.current_key: Optional[str] = None
- self.root: Optional[PlistEncodable] = None
- if use_builtin_types is None:
- self._use_builtin_types = USE_BUILTIN_TYPES
- else:
- if use_builtin_types is False:
- warnings.warn(
- "Setting use_builtin_types to False is deprecated and will be "
- "removed soon.",
- DeprecationWarning,
- )
- self._use_builtin_types = use_builtin_types
- self._dict_type = dict_type
-
- def start(self, tag: str, attrib: Mapping[str, str]) -> None:
- self._data: List[str] = []
- handler = _TARGET_START_HANDLERS.get(tag)
- if handler is not None:
- handler(self)
-
- def end(self, tag: str) -> None:
- handler = _TARGET_END_HANDLERS.get(tag)
- if handler is not None:
- handler(self)
-
- def data(self, data: str) -> None:
- self._data.append(data)
-
- def close(self) -> PlistEncodable:
- if self.root is None:
- raise ValueError("No root set.")
- return self.root
-
- # helpers
-
- def add_object(self, value: PlistEncodable) -> None:
- if self.current_key is not None:
- stack_top = self.stack[-1]
- if not isinstance(stack_top, collections.abc.MutableMapping):
- raise ValueError("unexpected element: %r" % stack_top)
- stack_top[self.current_key] = value
- self.current_key = None
- elif not self.stack:
- # this is the root object
- self.root = value
- else:
- stack_top = self.stack[-1]
- if not isinstance(stack_top, list):
- raise ValueError("unexpected element: %r" % stack_top)
- stack_top.append(value)
-
- def get_data(self) -> str:
- data = "".join(self._data)
- self._data = []
- return data
-
-
-# event handlers
-
-
-def start_dict(self: PlistTarget) -> None:
- d = self._dict_type()
- self.add_object(d)
- self.stack.append(d)
-
-
-def end_dict(self: PlistTarget) -> None:
- if self.current_key:
- raise ValueError("missing value for key '%s'" % self.current_key)
- self.stack.pop()
-
-
-def end_key(self: PlistTarget) -> None:
- if self.current_key or not isinstance(self.stack[-1], collections.abc.Mapping):
- raise ValueError("unexpected key")
- self.current_key = self.get_data()
-
-
-def start_array(self: PlistTarget) -> None:
- a: List[PlistEncodable] = []
- self.add_object(a)
- self.stack.append(a)
-
-
-def end_array(self: PlistTarget) -> None:
- self.stack.pop()
-
-
-def end_true(self: PlistTarget) -> None:
- self.add_object(True)
-
-
-def end_false(self: PlistTarget) -> None:
- self.add_object(False)
-
-
-def end_integer(self: PlistTarget) -> None:
- self.add_object(int(self.get_data()))
-
-
-def end_real(self: PlistTarget) -> None:
- self.add_object(float(self.get_data()))
-
-
-def end_string(self: PlistTarget) -> None:
- self.add_object(self.get_data())
-
-
-def end_data(self: PlistTarget) -> None:
- if self._use_builtin_types:
- self.add_object(b64decode(self.get_data()))
- else:
- self.add_object(Data.fromBase64(self.get_data()))
-
-
-def end_date(self: PlistTarget) -> None:
- self.add_object(_date_from_string(self.get_data()))
-
-
-_TARGET_START_HANDLERS: Dict[str, Callable[[PlistTarget], None]] = {
- "dict": start_dict,
- "array": start_array,
-}
-
-_TARGET_END_HANDLERS: Dict[str, Callable[[PlistTarget], None]] = {
- "dict": end_dict,
- "array": end_array,
- "key": end_key,
- "true": end_true,
- "false": end_false,
- "integer": end_integer,
- "real": end_real,
- "string": end_string,
- "data": end_data,
- "date": end_date,
-}
-
-
-# functions to build element tree from plist data
-
-
-def _string_element(value: str, ctx: SimpleNamespace) -> etree.Element:
- el = etree.Element("string")
- el.text = value
- return el
-
-
-def _bool_element(value: bool, ctx: SimpleNamespace) -> etree.Element:
- if value:
- return etree.Element("true")
- return etree.Element("false")
-
-
-def _integer_element(value: int, ctx: SimpleNamespace) -> etree.Element:
- if -1 << 63 <= value < 1 << 64:
- el = etree.Element("integer")
- el.text = "%d" % value
- return el
- raise OverflowError(value)
-
-
-def _real_element(value: float, ctx: SimpleNamespace) -> etree.Element:
- el = etree.Element("real")
- el.text = repr(value)
- return el
-
-
-def _dict_element(
- d: Mapping[str, PlistEncodable], ctx: SimpleNamespace
-) -> etree.Element:
- el = etree.Element("dict")
- items = d.items()
- if ctx.sort_keys:
- items = sorted(items) # type: ignore
- ctx.indent_level += 1
- for key, value in items:
- if not isinstance(key, str):
- if ctx.skipkeys:
- continue
- raise TypeError("keys must be strings")
- k = etree.SubElement(el, "key")
- k.text = tostr(key, "utf-8")
- el.append(_make_element(value, ctx))
- ctx.indent_level -= 1
- return el
-
-
-def _array_element(
- array: Sequence[PlistEncodable], ctx: SimpleNamespace
-) -> etree.Element:
- el = etree.Element("array")
- if len(array) == 0:
- return el
- ctx.indent_level += 1
- for value in array:
- el.append(_make_element(value, ctx))
- ctx.indent_level -= 1
- return el
-
-
-def _date_element(date: datetime, ctx: SimpleNamespace) -> etree.Element:
- el = etree.Element("date")
- el.text = _date_to_string(date)
- return el
-
-
-def _data_element(data: bytes, ctx: SimpleNamespace) -> etree.Element:
- el = etree.Element("data")
- # NOTE: mypy is confused about whether el.text should be str or bytes.
- el.text = _encode_base64( # type: ignore
- data,
- maxlinelength=(76 if ctx.pretty_print else None),
- indent_level=ctx.indent_level,
- )
- return el
-
-
-def _string_or_data_element(raw_bytes: bytes, ctx: SimpleNamespace) -> etree.Element:
- if ctx.use_builtin_types:
- return _data_element(raw_bytes, ctx)
- else:
- try:
- string = raw_bytes.decode(encoding="ascii", errors="strict")
- except UnicodeDecodeError:
- raise ValueError(
- "invalid non-ASCII bytes; use unicode string instead: %r" % raw_bytes
- )
- return _string_element(string, ctx)
-
-
-# The following is probably not entirely correct. The signature should take `Any`
-# and return `NoReturn`. At the time of this writing, neither mypy nor Pyright
-# can deal with singledispatch properly and will apply the signature of the base
-# function to all others. Being slightly dishonest makes it type-check and return
-# usable typing information for the optimistic case.
-@singledispatch
-def _make_element(value: PlistEncodable, ctx: SimpleNamespace) -> etree.Element:
- raise TypeError("unsupported type: %s" % type(value))
-
-
-_make_element.register(str)(_string_element)
-_make_element.register(bool)(_bool_element)
-_make_element.register(Integral)(_integer_element)
-_make_element.register(float)(_real_element)
-_make_element.register(collections.abc.Mapping)(_dict_element)
-_make_element.register(list)(_array_element)
-_make_element.register(tuple)(_array_element)
-_make_element.register(datetime)(_date_element)
-_make_element.register(bytes)(_string_or_data_element)
-_make_element.register(bytearray)(_data_element)
-_make_element.register(Data)(lambda v, ctx: _data_element(v.data, ctx))
-
-
-# Public functions to create element tree from plist-compatible python
-# data structures and viceversa, for use when (de)serializing GLIF xml.
-
-
-def totree(
- value: PlistEncodable,
- sort_keys: bool = True,
- skipkeys: bool = False,
- use_builtin_types: Optional[bool] = None,
- pretty_print: bool = True,
- indent_level: int = 1,
-) -> etree.Element:
- """Convert a value derived from a plist into an XML tree.
-
- Args:
- value: Any kind of value to be serialized to XML.
- sort_keys: Whether keys of dictionaries should be sorted.
- skipkeys (bool): Whether to silently skip non-string dictionary
- keys.
- use_builtin_types (bool): If true, byte strings will be
- encoded in Base-64 and wrapped in a ``data`` tag; if
- false, they will be either stored as ASCII strings or an
- exception raised if they cannot be decoded as such. Defaults
- to ``True`` if not present. Deprecated.
- pretty_print (bool): Whether to indent the output.
- indent_level (int): Level of indentation when serializing.
-
- Returns: an ``etree`` ``Element`` object.
-
- Raises:
- ``TypeError``
- if non-string dictionary keys are serialized
- and ``skipkeys`` is false.
- ``ValueError``
- if non-ASCII binary data is present
- and `use_builtin_types` is false.
- """
- if use_builtin_types is None:
- use_builtin_types = USE_BUILTIN_TYPES
- else:
- use_builtin_types = use_builtin_types
- context = SimpleNamespace(
- sort_keys=sort_keys,
- skipkeys=skipkeys,
- use_builtin_types=use_builtin_types,
- pretty_print=pretty_print,
- indent_level=indent_level,
- )
- return _make_element(value, context)
-
-
-def fromtree(
- tree: etree.Element,
- use_builtin_types: Optional[bool] = None,
- dict_type: Type[MutableMapping[str, Any]] = dict,
-) -> Any:
- """Convert an XML tree to a plist structure.
-
- Args:
- tree: An ``etree`` ``Element``.
- use_builtin_types: If True, binary data is deserialized to
- bytes strings. If False, it is wrapped in :py:class:`Data`
- objects. Defaults to True if not provided. Deprecated.
- dict_type: What type to use for dictionaries.
-
- Returns: An object (usually a dictionary).
- """
- target = PlistTarget(use_builtin_types=use_builtin_types, dict_type=dict_type)
- for action, element in etree.iterwalk(tree, events=("start", "end")):
- if action == "start":
- target.start(element.tag, element.attrib)
- elif action == "end":
- # if there are no children, parse the leaf's data
- if not len(element):
- # always pass str, not None
- target.data(element.text or "")
- target.end(element.tag)
- return target.close()
-
-
-# python3 plistlib API
-
-
-def load(
- fp: IO[bytes],
- use_builtin_types: Optional[bool] = None,
- dict_type: Type[MutableMapping[str, Any]] = dict,
-) -> Any:
- """Load a plist file into an object.
-
- Args:
- fp: An opened file.
- use_builtin_types: If True, binary data is deserialized to
- bytes strings. If False, it is wrapped in :py:class:`Data`
- objects. Defaults to True if not provided. Deprecated.
- dict_type: What type to use for dictionaries.
-
- Returns:
- An object (usually a dictionary) representing the top level of
- the plist file.
- """
-
- if not hasattr(fp, "read"):
- raise AttributeError("'%s' object has no attribute 'read'" % type(fp).__name__)
- target = PlistTarget(use_builtin_types=use_builtin_types, dict_type=dict_type)
- parser = etree.XMLParser(target=target)
- result = etree.parse(fp, parser=parser)
- # lxml returns the target object directly, while ElementTree wraps
- # it as the root of an ElementTree object
- try:
- return result.getroot()
- except AttributeError:
- return result
-
-
-def loads(
- value: bytes,
- use_builtin_types: Optional[bool] = None,
- dict_type: Type[MutableMapping[str, Any]] = dict,
-) -> Any:
- """Load a plist file from a string into an object.
-
- Args:
- value: A bytes string containing a plist.
- use_builtin_types: If True, binary data is deserialized to
- bytes strings. If False, it is wrapped in :py:class:`Data`
- objects. Defaults to True if not provided. Deprecated.
- dict_type: What type to use for dictionaries.
-
- Returns:
- An object (usually a dictionary) representing the top level of
- the plist file.
- """
-
- fp = BytesIO(value)
- return load(fp, use_builtin_types=use_builtin_types, dict_type=dict_type)
-
-
-def dump(
- value: PlistEncodable,
- fp: IO[bytes],
- sort_keys: bool = True,
- skipkeys: bool = False,
- use_builtin_types: Optional[bool] = None,
- pretty_print: bool = True,
-) -> None:
- """Write a Python object to a plist file.
-
- Args:
- value: An object to write.
- fp: A file opened for writing.
- sort_keys (bool): Whether keys of dictionaries should be sorted.
- skipkeys (bool): Whether to silently skip non-string dictionary
- keys.
- use_builtin_types (bool): If true, byte strings will be
- encoded in Base-64 and wrapped in a ``data`` tag; if
- false, they will be either stored as ASCII strings or an
- exception raised if they cannot be represented. Defaults
- pretty_print (bool): Whether to indent the output.
- indent_level (int): Level of indentation when serializing.
-
- Raises:
- ``TypeError``
- if non-string dictionary keys are serialized
- and ``skipkeys`` is false.
- ``ValueError``
- if non-representable binary data is present
- and `use_builtin_types` is false.
- """
-
- if not hasattr(fp, "write"):
- raise AttributeError("'%s' object has no attribute 'write'" % type(fp).__name__)
- root = etree.Element("plist", version="1.0")
- el = totree(
- value,
- sort_keys=sort_keys,
- skipkeys=skipkeys,
- use_builtin_types=use_builtin_types,
- pretty_print=pretty_print,
- )
- root.append(el)
- tree = etree.ElementTree(root)
- # we write the doctype ourselves instead of using the 'doctype' argument
- # of 'write' method, becuse lxml will force adding a '\n' even when
- # pretty_print is False.
- if pretty_print:
- header = b"\n".join((XML_DECLARATION, PLIST_DOCTYPE, b""))
- else:
- header = XML_DECLARATION + PLIST_DOCTYPE
- fp.write(header)
- tree.write( # type: ignore
- fp,
- encoding="utf-8",
- pretty_print=pretty_print,
- xml_declaration=False,
- )
-
-
-def dumps(
- value: PlistEncodable,
- sort_keys: bool = True,
- skipkeys: bool = False,
- use_builtin_types: Optional[bool] = None,
- pretty_print: bool = True,
-) -> bytes:
- """Write a Python object to a string in plist format.
-
- Args:
- value: An object to write.
- sort_keys (bool): Whether keys of dictionaries should be sorted.
- skipkeys (bool): Whether to silently skip non-string dictionary
- keys.
- use_builtin_types (bool): If true, byte strings will be
- encoded in Base-64 and wrapped in a ``data`` tag; if
- false, they will be either stored as strings or an
- exception raised if they cannot be represented. Defaults
- pretty_print (bool): Whether to indent the output.
- indent_level (int): Level of indentation when serializing.
-
- Returns:
- string: A plist representation of the Python object.
-
- Raises:
- ``TypeError``
- if non-string dictionary keys are serialized
- and ``skipkeys`` is false.
- ``ValueError``
- if non-representable binary data is present
- and `use_builtin_types` is false.
- """
- fp = BytesIO()
- dump(
- value,
- fp,
- sort_keys=sort_keys,
- skipkeys=skipkeys,
- use_builtin_types=use_builtin_types,
- pretty_print=pretty_print,
- )
- return fp.getvalue()
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/plistlib/py.typed b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/plistlib/py.typed
deleted file mode 100644
index e69de29..0000000
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/psCharStrings.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/psCharStrings.py
deleted file mode 100644
index bd51ff4..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/psCharStrings.py
+++ /dev/null
@@ -1,1474 +0,0 @@
-"""psCharStrings.py -- module implementing various kinds of CharStrings:
-CFF dictionary data and Type1/Type2 CharStrings.
-"""
-
-from fontTools.misc.fixedTools import (
- fixedToFloat,
- floatToFixed,
- floatToFixedToStr,
- strToFixedToFloat,
-)
-from fontTools.misc.textTools import bytechr, byteord, bytesjoin, strjoin
-from fontTools.pens.boundsPen import BoundsPen
-import struct
-import logging
-
-
-log = logging.getLogger(__name__)
-
-
-def read_operator(self, b0, data, index):
- if b0 == 12:
- op = (b0, byteord(data[index]))
- index = index + 1
- else:
- op = b0
- try:
- operator = self.operators[op]
- except KeyError:
- return None, index
- value = self.handle_operator(operator)
- return value, index
-
-
-def read_byte(self, b0, data, index):
- return b0 - 139, index
-
-
-def read_smallInt1(self, b0, data, index):
- b1 = byteord(data[index])
- return (b0 - 247) * 256 + b1 + 108, index + 1
-
-
-def read_smallInt2(self, b0, data, index):
- b1 = byteord(data[index])
- return -(b0 - 251) * 256 - b1 - 108, index + 1
-
-
-def read_shortInt(self, b0, data, index):
- (value,) = struct.unpack(">h", data[index : index + 2])
- return value, index + 2
-
-
-def read_longInt(self, b0, data, index):
- (value,) = struct.unpack(">l", data[index : index + 4])
- return value, index + 4
-
-
-def read_fixed1616(self, b0, data, index):
- (value,) = struct.unpack(">l", data[index : index + 4])
- return fixedToFloat(value, precisionBits=16), index + 4
-
-
-def read_reserved(self, b0, data, index):
- assert NotImplementedError
- return NotImplemented, index
-
-
-def read_realNumber(self, b0, data, index):
- number = ""
- while True:
- b = byteord(data[index])
- index = index + 1
- nibble0 = (b & 0xF0) >> 4
- nibble1 = b & 0x0F
- if nibble0 == 0xF:
- break
- number = number + realNibbles[nibble0]
- if nibble1 == 0xF:
- break
- number = number + realNibbles[nibble1]
- return float(number), index
-
-
-t1OperandEncoding = [None] * 256
-t1OperandEncoding[0:32] = (32) * [read_operator]
-t1OperandEncoding[32:247] = (247 - 32) * [read_byte]
-t1OperandEncoding[247:251] = (251 - 247) * [read_smallInt1]
-t1OperandEncoding[251:255] = (255 - 251) * [read_smallInt2]
-t1OperandEncoding[255] = read_longInt
-assert len(t1OperandEncoding) == 256
-
-t2OperandEncoding = t1OperandEncoding[:]
-t2OperandEncoding[28] = read_shortInt
-t2OperandEncoding[255] = read_fixed1616
-
-cffDictOperandEncoding = t2OperandEncoding[:]
-cffDictOperandEncoding[29] = read_longInt
-cffDictOperandEncoding[30] = read_realNumber
-cffDictOperandEncoding[255] = read_reserved
-
-
-realNibbles = [
- "0",
- "1",
- "2",
- "3",
- "4",
- "5",
- "6",
- "7",
- "8",
- "9",
- ".",
- "E",
- "E-",
- None,
- "-",
-]
-realNibblesDict = {v: i for i, v in enumerate(realNibbles)}
-
-maxOpStack = 193
-
-
-def buildOperatorDict(operatorList):
- oper = {}
- opc = {}
- for item in operatorList:
- if len(item) == 2:
- oper[item[0]] = item[1]
- else:
- oper[item[0]] = item[1:]
- if isinstance(item[0], tuple):
- opc[item[1]] = item[0]
- else:
- opc[item[1]] = (item[0],)
- return oper, opc
-
-
-t2Operators = [
- # opcode name
- (1, "hstem"),
- (3, "vstem"),
- (4, "vmoveto"),
- (5, "rlineto"),
- (6, "hlineto"),
- (7, "vlineto"),
- (8, "rrcurveto"),
- (10, "callsubr"),
- (11, "return"),
- (14, "endchar"),
- (15, "vsindex"),
- (16, "blend"),
- (18, "hstemhm"),
- (19, "hintmask"),
- (20, "cntrmask"),
- (21, "rmoveto"),
- (22, "hmoveto"),
- (23, "vstemhm"),
- (24, "rcurveline"),
- (25, "rlinecurve"),
- (26, "vvcurveto"),
- (27, "hhcurveto"),
- # (28, 'shortint'), # not really an operator
- (29, "callgsubr"),
- (30, "vhcurveto"),
- (31, "hvcurveto"),
- ((12, 0), "ignore"), # dotsection. Yes, there a few very early OTF/CFF
- # fonts with this deprecated operator. Just ignore it.
- ((12, 3), "and"),
- ((12, 4), "or"),
- ((12, 5), "not"),
- ((12, 8), "store"),
- ((12, 9), "abs"),
- ((12, 10), "add"),
- ((12, 11), "sub"),
- ((12, 12), "div"),
- ((12, 13), "load"),
- ((12, 14), "neg"),
- ((12, 15), "eq"),
- ((12, 18), "drop"),
- ((12, 20), "put"),
- ((12, 21), "get"),
- ((12, 22), "ifelse"),
- ((12, 23), "random"),
- ((12, 24), "mul"),
- ((12, 26), "sqrt"),
- ((12, 27), "dup"),
- ((12, 28), "exch"),
- ((12, 29), "index"),
- ((12, 30), "roll"),
- ((12, 34), "hflex"),
- ((12, 35), "flex"),
- ((12, 36), "hflex1"),
- ((12, 37), "flex1"),
-]
-
-
-def getIntEncoder(format):
- if format == "cff":
- fourByteOp = bytechr(29)
- elif format == "t1":
- fourByteOp = bytechr(255)
- else:
- assert format == "t2"
- fourByteOp = None
-
- def encodeInt(
- value,
- fourByteOp=fourByteOp,
- bytechr=bytechr,
- pack=struct.pack,
- unpack=struct.unpack,
- ):
- if -107 <= value <= 107:
- code = bytechr(value + 139)
- elif 108 <= value <= 1131:
- value = value - 108
- code = bytechr((value >> 8) + 247) + bytechr(value & 0xFF)
- elif -1131 <= value <= -108:
- value = -value - 108
- code = bytechr((value >> 8) + 251) + bytechr(value & 0xFF)
- elif fourByteOp is None:
- # T2 only supports 2 byte ints
- if -32768 <= value <= 32767:
- code = bytechr(28) + pack(">h", value)
- else:
- # Backwards compatible hack: due to a previous bug in FontTools,
- # 16.16 fixed numbers were written out as 4-byte ints. When
- # these numbers were small, they were wrongly written back as
- # small ints instead of 4-byte ints, breaking round-tripping.
- # This here workaround doesn't do it any better, since we can't
- # distinguish anymore between small ints that were supposed to
- # be small fixed numbers and small ints that were just small
- # ints. Hence the warning.
- log.warning(
- "4-byte T2 number got passed to the "
- "IntType handler. This should happen only when reading in "
- "old XML files.\n"
- )
- code = bytechr(255) + pack(">l", value)
- else:
- code = fourByteOp + pack(">l", value)
- return code
-
- return encodeInt
-
-
-encodeIntCFF = getIntEncoder("cff")
-encodeIntT1 = getIntEncoder("t1")
-encodeIntT2 = getIntEncoder("t2")
-
-
-def encodeFixed(f, pack=struct.pack):
- """For T2 only"""
- value = floatToFixed(f, precisionBits=16)
- if value & 0xFFFF == 0: # check if the fractional part is zero
- return encodeIntT2(value >> 16) # encode only the integer part
- else:
- return b"\xff" + pack(">l", value) # encode the entire fixed point value
-
-
-realZeroBytes = bytechr(30) + bytechr(0xF)
-
-
-def encodeFloat(f):
- # For CFF only, used in cffLib
- if f == 0.0: # 0.0 == +0.0 == -0.0
- return realZeroBytes
- # Note: 14 decimal digits seems to be the limitation for CFF real numbers
- # in macOS. However, we use 8 here to match the implementation of AFDKO.
- s = "%.8G" % f
- if s[:2] == "0.":
- s = s[1:]
- elif s[:3] == "-0.":
- s = "-" + s[2:]
- nibbles = []
- while s:
- c = s[0]
- s = s[1:]
- if c == "E":
- c2 = s[:1]
- if c2 == "-":
- s = s[1:]
- c = "E-"
- elif c2 == "+":
- s = s[1:]
- nibbles.append(realNibblesDict[c])
- nibbles.append(0xF)
- if len(nibbles) % 2:
- nibbles.append(0xF)
- d = bytechr(30)
- for i in range(0, len(nibbles), 2):
- d = d + bytechr(nibbles[i] << 4 | nibbles[i + 1])
- return d
-
-
-class CharStringCompileError(Exception):
- pass
-
-
-class SimpleT2Decompiler(object):
- def __init__(self, localSubrs, globalSubrs, private=None, blender=None):
- self.localSubrs = localSubrs
- self.localBias = calcSubrBias(localSubrs)
- self.globalSubrs = globalSubrs
- self.globalBias = calcSubrBias(globalSubrs)
- self.private = private
- self.blender = blender
- self.reset()
-
- def reset(self):
- self.callingStack = []
- self.operandStack = []
- self.hintCount = 0
- self.hintMaskBytes = 0
- self.numRegions = 0
- self.vsIndex = 0
-
- def execute(self, charString):
- self.callingStack.append(charString)
- needsDecompilation = charString.needsDecompilation()
- if needsDecompilation:
- program = []
- pushToProgram = program.append
- else:
- pushToProgram = lambda x: None
- pushToStack = self.operandStack.append
- index = 0
- while True:
- token, isOperator, index = charString.getToken(index)
- if token is None:
- break # we're done!
- pushToProgram(token)
- if isOperator:
- handlerName = "op_" + token
- handler = getattr(self, handlerName, None)
- if handler is not None:
- rv = handler(index)
- if rv:
- hintMaskBytes, index = rv
- pushToProgram(hintMaskBytes)
- else:
- self.popall()
- else:
- pushToStack(token)
- if needsDecompilation:
- charString.setProgram(program)
- del self.callingStack[-1]
-
- def pop(self):
- value = self.operandStack[-1]
- del self.operandStack[-1]
- return value
-
- def popall(self):
- stack = self.operandStack[:]
- self.operandStack[:] = []
- return stack
-
- def push(self, value):
- self.operandStack.append(value)
-
- def op_return(self, index):
- if self.operandStack:
- pass
-
- def op_endchar(self, index):
- pass
-
- def op_ignore(self, index):
- pass
-
- def op_callsubr(self, index):
- subrIndex = self.pop()
- subr = self.localSubrs[subrIndex + self.localBias]
- self.execute(subr)
-
- def op_callgsubr(self, index):
- subrIndex = self.pop()
- subr = self.globalSubrs[subrIndex + self.globalBias]
- self.execute(subr)
-
- def op_hstem(self, index):
- self.countHints()
-
- def op_vstem(self, index):
- self.countHints()
-
- def op_hstemhm(self, index):
- self.countHints()
-
- def op_vstemhm(self, index):
- self.countHints()
-
- def op_hintmask(self, index):
- if not self.hintMaskBytes:
- self.countHints()
- self.hintMaskBytes = (self.hintCount + 7) // 8
- hintMaskBytes, index = self.callingStack[-1].getBytes(index, self.hintMaskBytes)
- return hintMaskBytes, index
-
- op_cntrmask = op_hintmask
-
- def countHints(self):
- args = self.popall()
- self.hintCount = self.hintCount + len(args) // 2
-
- # misc
- def op_and(self, index):
- raise NotImplementedError
-
- def op_or(self, index):
- raise NotImplementedError
-
- def op_not(self, index):
- raise NotImplementedError
-
- def op_store(self, index):
- raise NotImplementedError
-
- def op_abs(self, index):
- raise NotImplementedError
-
- def op_add(self, index):
- raise NotImplementedError
-
- def op_sub(self, index):
- raise NotImplementedError
-
- def op_div(self, index):
- raise NotImplementedError
-
- def op_load(self, index):
- raise NotImplementedError
-
- def op_neg(self, index):
- raise NotImplementedError
-
- def op_eq(self, index):
- raise NotImplementedError
-
- def op_drop(self, index):
- raise NotImplementedError
-
- def op_put(self, index):
- raise NotImplementedError
-
- def op_get(self, index):
- raise NotImplementedError
-
- def op_ifelse(self, index):
- raise NotImplementedError
-
- def op_random(self, index):
- raise NotImplementedError
-
- def op_mul(self, index):
- raise NotImplementedError
-
- def op_sqrt(self, index):
- raise NotImplementedError
-
- def op_dup(self, index):
- raise NotImplementedError
-
- def op_exch(self, index):
- raise NotImplementedError
-
- def op_index(self, index):
- raise NotImplementedError
-
- def op_roll(self, index):
- raise NotImplementedError
-
- def op_blend(self, index):
- if self.numRegions == 0:
- self.numRegions = self.private.getNumRegions()
- numBlends = self.pop()
- numOps = numBlends * (self.numRegions + 1)
- if self.blender is None:
- del self.operandStack[
- -(numOps - numBlends) :
- ] # Leave the default operands on the stack.
- else:
- argi = len(self.operandStack) - numOps
- end_args = tuplei = argi + numBlends
- while argi < end_args:
- next_ti = tuplei + self.numRegions
- deltas = self.operandStack[tuplei:next_ti]
- delta = self.blender(self.vsIndex, deltas)
- self.operandStack[argi] += delta
- tuplei = next_ti
- argi += 1
- self.operandStack[end_args:] = []
-
- def op_vsindex(self, index):
- vi = self.pop()
- self.vsIndex = vi
- self.numRegions = self.private.getNumRegions(vi)
-
-
-t1Operators = [
- # opcode name
- (1, "hstem"),
- (3, "vstem"),
- (4, "vmoveto"),
- (5, "rlineto"),
- (6, "hlineto"),
- (7, "vlineto"),
- (8, "rrcurveto"),
- (9, "closepath"),
- (10, "callsubr"),
- (11, "return"),
- (13, "hsbw"),
- (14, "endchar"),
- (21, "rmoveto"),
- (22, "hmoveto"),
- (30, "vhcurveto"),
- (31, "hvcurveto"),
- ((12, 0), "dotsection"),
- ((12, 1), "vstem3"),
- ((12, 2), "hstem3"),
- ((12, 6), "seac"),
- ((12, 7), "sbw"),
- ((12, 12), "div"),
- ((12, 16), "callothersubr"),
- ((12, 17), "pop"),
- ((12, 33), "setcurrentpoint"),
-]
-
-
-class T2WidthExtractor(SimpleT2Decompiler):
- def __init__(
- self,
- localSubrs,
- globalSubrs,
- nominalWidthX,
- defaultWidthX,
- private=None,
- blender=None,
- ):
- SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs, private, blender)
- self.nominalWidthX = nominalWidthX
- self.defaultWidthX = defaultWidthX
-
- def reset(self):
- SimpleT2Decompiler.reset(self)
- self.gotWidth = 0
- self.width = 0
-
- def popallWidth(self, evenOdd=0):
- args = self.popall()
- if not self.gotWidth:
- if evenOdd ^ (len(args) % 2):
- # For CFF2 charstrings, this should never happen
- assert (
- self.defaultWidthX is not None
- ), "CFF2 CharStrings must not have an initial width value"
- self.width = self.nominalWidthX + args[0]
- args = args[1:]
- else:
- self.width = self.defaultWidthX
- self.gotWidth = 1
- return args
-
- def countHints(self):
- args = self.popallWidth()
- self.hintCount = self.hintCount + len(args) // 2
-
- def op_rmoveto(self, index):
- self.popallWidth()
-
- def op_hmoveto(self, index):
- self.popallWidth(1)
-
- def op_vmoveto(self, index):
- self.popallWidth(1)
-
- def op_endchar(self, index):
- self.popallWidth()
-
-
-class T2OutlineExtractor(T2WidthExtractor):
- def __init__(
- self,
- pen,
- localSubrs,
- globalSubrs,
- nominalWidthX,
- defaultWidthX,
- private=None,
- blender=None,
- ):
- T2WidthExtractor.__init__(
- self,
- localSubrs,
- globalSubrs,
- nominalWidthX,
- defaultWidthX,
- private,
- blender,
- )
- self.pen = pen
- self.subrLevel = 0
-
- def reset(self):
- T2WidthExtractor.reset(self)
- self.currentPoint = (0, 0)
- self.sawMoveTo = 0
- self.subrLevel = 0
-
- def execute(self, charString):
- self.subrLevel += 1
- super().execute(charString)
- self.subrLevel -= 1
- if self.subrLevel == 0:
- self.endPath()
-
- def _nextPoint(self, point):
- x, y = self.currentPoint
- point = x + point[0], y + point[1]
- self.currentPoint = point
- return point
-
- def rMoveTo(self, point):
- self.pen.moveTo(self._nextPoint(point))
- self.sawMoveTo = 1
-
- def rLineTo(self, point):
- if not self.sawMoveTo:
- self.rMoveTo((0, 0))
- self.pen.lineTo(self._nextPoint(point))
-
- def rCurveTo(self, pt1, pt2, pt3):
- if not self.sawMoveTo:
- self.rMoveTo((0, 0))
- nextPoint = self._nextPoint
- self.pen.curveTo(nextPoint(pt1), nextPoint(pt2), nextPoint(pt3))
-
- def closePath(self):
- if self.sawMoveTo:
- self.pen.closePath()
- self.sawMoveTo = 0
-
- def endPath(self):
- # In T2 there are no open paths, so always do a closePath when
- # finishing a sub path. We avoid spurious calls to closePath()
- # because its a real T1 op we're emulating in T2 whereas
- # endPath() is just a means to that emulation
- if self.sawMoveTo:
- self.closePath()
-
- #
- # hint operators
- #
- # def op_hstem(self, index):
- # self.countHints()
- # def op_vstem(self, index):
- # self.countHints()
- # def op_hstemhm(self, index):
- # self.countHints()
- # def op_vstemhm(self, index):
- # self.countHints()
- # def op_hintmask(self, index):
- # self.countHints()
- # def op_cntrmask(self, index):
- # self.countHints()
-
- #
- # path constructors, moveto
- #
- def op_rmoveto(self, index):
- self.endPath()
- self.rMoveTo(self.popallWidth())
-
- def op_hmoveto(self, index):
- self.endPath()
- self.rMoveTo((self.popallWidth(1)[0], 0))
-
- def op_vmoveto(self, index):
- self.endPath()
- self.rMoveTo((0, self.popallWidth(1)[0]))
-
- def op_endchar(self, index):
- self.endPath()
- args = self.popallWidth()
- if args:
- from fontTools.encodings.StandardEncoding import StandardEncoding
-
- # endchar can do seac accent bulding; The T2 spec says it's deprecated,
- # but recent software that shall remain nameless does output it.
- adx, ady, bchar, achar = args
- baseGlyph = StandardEncoding[bchar]
- self.pen.addComponent(baseGlyph, (1, 0, 0, 1, 0, 0))
- accentGlyph = StandardEncoding[achar]
- self.pen.addComponent(accentGlyph, (1, 0, 0, 1, adx, ady))
-
- #
- # path constructors, lines
- #
- def op_rlineto(self, index):
- args = self.popall()
- for i in range(0, len(args), 2):
- point = args[i : i + 2]
- self.rLineTo(point)
-
- def op_hlineto(self, index):
- self.alternatingLineto(1)
-
- def op_vlineto(self, index):
- self.alternatingLineto(0)
-
- #
- # path constructors, curves
- #
- def op_rrcurveto(self, index):
- """{dxa dya dxb dyb dxc dyc}+ rrcurveto"""
- args = self.popall()
- for i in range(0, len(args), 6):
- (
- dxa,
- dya,
- dxb,
- dyb,
- dxc,
- dyc,
- ) = args[i : i + 6]
- self.rCurveTo((dxa, dya), (dxb, dyb), (dxc, dyc))
-
- def op_rcurveline(self, index):
- """{dxa dya dxb dyb dxc dyc}+ dxd dyd rcurveline"""
- args = self.popall()
- for i in range(0, len(args) - 2, 6):
- dxb, dyb, dxc, dyc, dxd, dyd = args[i : i + 6]
- self.rCurveTo((dxb, dyb), (dxc, dyc), (dxd, dyd))
- self.rLineTo(args[-2:])
-
- def op_rlinecurve(self, index):
- """{dxa dya}+ dxb dyb dxc dyc dxd dyd rlinecurve"""
- args = self.popall()
- lineArgs = args[:-6]
- for i in range(0, len(lineArgs), 2):
- self.rLineTo(lineArgs[i : i + 2])
- dxb, dyb, dxc, dyc, dxd, dyd = args[-6:]
- self.rCurveTo((dxb, dyb), (dxc, dyc), (dxd, dyd))
-
- def op_vvcurveto(self, index):
- "dx1? {dya dxb dyb dyc}+ vvcurveto"
- args = self.popall()
- if len(args) % 2:
- dx1 = args[0]
- args = args[1:]
- else:
- dx1 = 0
- for i in range(0, len(args), 4):
- dya, dxb, dyb, dyc = args[i : i + 4]
- self.rCurveTo((dx1, dya), (dxb, dyb), (0, dyc))
- dx1 = 0
-
- def op_hhcurveto(self, index):
- """dy1? {dxa dxb dyb dxc}+ hhcurveto"""
- args = self.popall()
- if len(args) % 2:
- dy1 = args[0]
- args = args[1:]
- else:
- dy1 = 0
- for i in range(0, len(args), 4):
- dxa, dxb, dyb, dxc = args[i : i + 4]
- self.rCurveTo((dxa, dy1), (dxb, dyb), (dxc, 0))
- dy1 = 0
-
- def op_vhcurveto(self, index):
- """dy1 dx2 dy2 dx3 {dxa dxb dyb dyc dyd dxe dye dxf}* dyf? vhcurveto (30)
- {dya dxb dyb dxc dxd dxe dye dyf}+ dxf? vhcurveto
- """
- args = self.popall()
- while args:
- args = self.vcurveto(args)
- if args:
- args = self.hcurveto(args)
-
- def op_hvcurveto(self, index):
- """dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf?
- {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf?
- """
- args = self.popall()
- while args:
- args = self.hcurveto(args)
- if args:
- args = self.vcurveto(args)
-
- #
- # path constructors, flex
- #
- def op_hflex(self, index):
- dx1, dx2, dy2, dx3, dx4, dx5, dx6 = self.popall()
- dy1 = dy3 = dy4 = dy6 = 0
- dy5 = -dy2
- self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3))
- self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6))
-
- def op_flex(self, index):
- dx1, dy1, dx2, dy2, dx3, dy3, dx4, dy4, dx5, dy5, dx6, dy6, fd = self.popall()
- self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3))
- self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6))
-
- def op_hflex1(self, index):
- dx1, dy1, dx2, dy2, dx3, dx4, dx5, dy5, dx6 = self.popall()
- dy3 = dy4 = 0
- dy6 = -(dy1 + dy2 + dy3 + dy4 + dy5)
-
- self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3))
- self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6))
-
- def op_flex1(self, index):
- dx1, dy1, dx2, dy2, dx3, dy3, dx4, dy4, dx5, dy5, d6 = self.popall()
- dx = dx1 + dx2 + dx3 + dx4 + dx5
- dy = dy1 + dy2 + dy3 + dy4 + dy5
- if abs(dx) > abs(dy):
- dx6 = d6
- dy6 = -dy
- else:
- dx6 = -dx
- dy6 = d6
- self.rCurveTo((dx1, dy1), (dx2, dy2), (dx3, dy3))
- self.rCurveTo((dx4, dy4), (dx5, dy5), (dx6, dy6))
-
- # misc
- def op_and(self, index):
- raise NotImplementedError
-
- def op_or(self, index):
- raise NotImplementedError
-
- def op_not(self, index):
- raise NotImplementedError
-
- def op_store(self, index):
- raise NotImplementedError
-
- def op_abs(self, index):
- raise NotImplementedError
-
- def op_add(self, index):
- raise NotImplementedError
-
- def op_sub(self, index):
- raise NotImplementedError
-
- def op_div(self, index):
- num2 = self.pop()
- num1 = self.pop()
- d1 = num1 // num2
- d2 = num1 / num2
- if d1 == d2:
- self.push(d1)
- else:
- self.push(d2)
-
- def op_load(self, index):
- raise NotImplementedError
-
- def op_neg(self, index):
- raise NotImplementedError
-
- def op_eq(self, index):
- raise NotImplementedError
-
- def op_drop(self, index):
- raise NotImplementedError
-
- def op_put(self, index):
- raise NotImplementedError
-
- def op_get(self, index):
- raise NotImplementedError
-
- def op_ifelse(self, index):
- raise NotImplementedError
-
- def op_random(self, index):
- raise NotImplementedError
-
- def op_mul(self, index):
- raise NotImplementedError
-
- def op_sqrt(self, index):
- raise NotImplementedError
-
- def op_dup(self, index):
- raise NotImplementedError
-
- def op_exch(self, index):
- raise NotImplementedError
-
- def op_index(self, index):
- raise NotImplementedError
-
- def op_roll(self, index):
- raise NotImplementedError
-
- #
- # miscellaneous helpers
- #
- def alternatingLineto(self, isHorizontal):
- args = self.popall()
- for arg in args:
- if isHorizontal:
- point = (arg, 0)
- else:
- point = (0, arg)
- self.rLineTo(point)
- isHorizontal = not isHorizontal
-
- def vcurveto(self, args):
- dya, dxb, dyb, dxc = args[:4]
- args = args[4:]
- if len(args) == 1:
- dyc = args[0]
- args = []
- else:
- dyc = 0
- self.rCurveTo((0, dya), (dxb, dyb), (dxc, dyc))
- return args
-
- def hcurveto(self, args):
- dxa, dxb, dyb, dyc = args[:4]
- args = args[4:]
- if len(args) == 1:
- dxc = args[0]
- args = []
- else:
- dxc = 0
- self.rCurveTo((dxa, 0), (dxb, dyb), (dxc, dyc))
- return args
-
-
-class T1OutlineExtractor(T2OutlineExtractor):
- def __init__(self, pen, subrs):
- self.pen = pen
- self.subrs = subrs
- self.reset()
-
- def reset(self):
- self.flexing = 0
- self.width = 0
- self.sbx = 0
- T2OutlineExtractor.reset(self)
-
- def endPath(self):
- if self.sawMoveTo:
- self.pen.endPath()
- self.sawMoveTo = 0
-
- def popallWidth(self, evenOdd=0):
- return self.popall()
-
- def exch(self):
- stack = self.operandStack
- stack[-1], stack[-2] = stack[-2], stack[-1]
-
- #
- # path constructors
- #
- def op_rmoveto(self, index):
- if self.flexing:
- return
- self.endPath()
- self.rMoveTo(self.popall())
-
- def op_hmoveto(self, index):
- if self.flexing:
- # We must add a parameter to the stack if we are flexing
- self.push(0)
- return
- self.endPath()
- self.rMoveTo((self.popall()[0], 0))
-
- def op_vmoveto(self, index):
- if self.flexing:
- # We must add a parameter to the stack if we are flexing
- self.push(0)
- self.exch()
- return
- self.endPath()
- self.rMoveTo((0, self.popall()[0]))
-
- def op_closepath(self, index):
- self.closePath()
-
- def op_setcurrentpoint(self, index):
- args = self.popall()
- x, y = args
- self.currentPoint = x, y
-
- def op_endchar(self, index):
- self.endPath()
-
- def op_hsbw(self, index):
- sbx, wx = self.popall()
- self.width = wx
- self.sbx = sbx
- self.currentPoint = sbx, self.currentPoint[1]
-
- def op_sbw(self, index):
- self.popall() # XXX
-
- #
- def op_callsubr(self, index):
- subrIndex = self.pop()
- subr = self.subrs[subrIndex]
- self.execute(subr)
-
- def op_callothersubr(self, index):
- subrIndex = self.pop()
- nArgs = self.pop()
- # print nArgs, subrIndex, "callothersubr"
- if subrIndex == 0 and nArgs == 3:
- self.doFlex()
- self.flexing = 0
- elif subrIndex == 1 and nArgs == 0:
- self.flexing = 1
- # ignore...
-
- def op_pop(self, index):
- pass # ignore...
-
- def doFlex(self):
- finaly = self.pop()
- finalx = self.pop()
- self.pop() # flex height is unused
-
- p3y = self.pop()
- p3x = self.pop()
- bcp4y = self.pop()
- bcp4x = self.pop()
- bcp3y = self.pop()
- bcp3x = self.pop()
- p2y = self.pop()
- p2x = self.pop()
- bcp2y = self.pop()
- bcp2x = self.pop()
- bcp1y = self.pop()
- bcp1x = self.pop()
- rpy = self.pop()
- rpx = self.pop()
-
- # call rrcurveto
- self.push(bcp1x + rpx)
- self.push(bcp1y + rpy)
- self.push(bcp2x)
- self.push(bcp2y)
- self.push(p2x)
- self.push(p2y)
- self.op_rrcurveto(None)
-
- # call rrcurveto
- self.push(bcp3x)
- self.push(bcp3y)
- self.push(bcp4x)
- self.push(bcp4y)
- self.push(p3x)
- self.push(p3y)
- self.op_rrcurveto(None)
-
- # Push back final coords so subr 0 can find them
- self.push(finalx)
- self.push(finaly)
-
- def op_dotsection(self, index):
- self.popall() # XXX
-
- def op_hstem3(self, index):
- self.popall() # XXX
-
- def op_seac(self, index):
- "asb adx ady bchar achar seac"
- from fontTools.encodings.StandardEncoding import StandardEncoding
-
- asb, adx, ady, bchar, achar = self.popall()
- baseGlyph = StandardEncoding[bchar]
- self.pen.addComponent(baseGlyph, (1, 0, 0, 1, 0, 0))
- accentGlyph = StandardEncoding[achar]
- adx = adx + self.sbx - asb # seac weirdness
- self.pen.addComponent(accentGlyph, (1, 0, 0, 1, adx, ady))
-
- def op_vstem3(self, index):
- self.popall() # XXX
-
-
-class T2CharString(object):
- operandEncoding = t2OperandEncoding
- operators, opcodes = buildOperatorDict(t2Operators)
- decompilerClass = SimpleT2Decompiler
- outlineExtractor = T2OutlineExtractor
-
- def __init__(self, bytecode=None, program=None, private=None, globalSubrs=None):
- if program is None:
- program = []
- self.bytecode = bytecode
- self.program = program
- self.private = private
- self.globalSubrs = globalSubrs if globalSubrs is not None else []
- self._cur_vsindex = None
-
- def getNumRegions(self, vsindex=None):
- pd = self.private
- assert pd is not None
- if vsindex is not None:
- self._cur_vsindex = vsindex
- elif self._cur_vsindex is None:
- self._cur_vsindex = pd.vsindex if hasattr(pd, "vsindex") else 0
- return pd.getNumRegions(self._cur_vsindex)
-
- def __repr__(self):
- if self.bytecode is None:
- return "<%s (source) at %x>" % (self.__class__.__name__, id(self))
- else:
- return "<%s (bytecode) at %x>" % (self.__class__.__name__, id(self))
-
- def getIntEncoder(self):
- return encodeIntT2
-
- def getFixedEncoder(self):
- return encodeFixed
-
- def decompile(self):
- if not self.needsDecompilation():
- return
- subrs = getattr(self.private, "Subrs", [])
- decompiler = self.decompilerClass(subrs, self.globalSubrs, self.private)
- decompiler.execute(self)
-
- def draw(self, pen, blender=None):
- subrs = getattr(self.private, "Subrs", [])
- extractor = self.outlineExtractor(
- pen,
- subrs,
- self.globalSubrs,
- self.private.nominalWidthX,
- self.private.defaultWidthX,
- self.private,
- blender,
- )
- extractor.execute(self)
- self.width = extractor.width
-
- def calcBounds(self, glyphSet):
- boundsPen = BoundsPen(glyphSet)
- self.draw(boundsPen)
- return boundsPen.bounds
-
- def compile(self, isCFF2=False):
- if self.bytecode is not None:
- return
- opcodes = self.opcodes
- program = self.program
-
- if isCFF2:
- # If present, remove return and endchar operators.
- if program and program[-1] in ("return", "endchar"):
- program = program[:-1]
- elif program and not isinstance(program[-1], str):
- raise CharStringCompileError(
- "T2CharString or Subr has items on the stack after last operator."
- )
-
- bytecode = []
- encodeInt = self.getIntEncoder()
- encodeFixed = self.getFixedEncoder()
- i = 0
- end = len(program)
- while i < end:
- token = program[i]
- i = i + 1
- if isinstance(token, str):
- try:
- bytecode.extend(bytechr(b) for b in opcodes[token])
- except KeyError:
- raise CharStringCompileError("illegal operator: %s" % token)
- if token in ("hintmask", "cntrmask"):
- bytecode.append(program[i]) # hint mask
- i = i + 1
- elif isinstance(token, int):
- bytecode.append(encodeInt(token))
- elif isinstance(token, float):
- bytecode.append(encodeFixed(token))
- else:
- assert 0, "unsupported type: %s" % type(token)
- try:
- bytecode = bytesjoin(bytecode)
- except TypeError:
- log.error(bytecode)
- raise
- self.setBytecode(bytecode)
-
- def needsDecompilation(self):
- return self.bytecode is not None
-
- def setProgram(self, program):
- self.program = program
- self.bytecode = None
-
- def setBytecode(self, bytecode):
- self.bytecode = bytecode
- self.program = None
-
- def getToken(self, index, len=len, byteord=byteord, isinstance=isinstance):
- if self.bytecode is not None:
- if index >= len(self.bytecode):
- return None, 0, 0
- b0 = byteord(self.bytecode[index])
- index = index + 1
- handler = self.operandEncoding[b0]
- token, index = handler(self, b0, self.bytecode, index)
- else:
- if index >= len(self.program):
- return None, 0, 0
- token = self.program[index]
- index = index + 1
- isOperator = isinstance(token, str)
- return token, isOperator, index
-
- def getBytes(self, index, nBytes):
- if self.bytecode is not None:
- newIndex = index + nBytes
- bytes = self.bytecode[index:newIndex]
- index = newIndex
- else:
- bytes = self.program[index]
- index = index + 1
- assert len(bytes) == nBytes
- return bytes, index
-
- def handle_operator(self, operator):
- return operator
-
- def toXML(self, xmlWriter, ttFont=None):
- from fontTools.misc.textTools import num2binary
-
- if self.bytecode is not None:
- xmlWriter.dumphex(self.bytecode)
- else:
- index = 0
- args = []
- while True:
- token, isOperator, index = self.getToken(index)
- if token is None:
- break
- if isOperator:
- if token in ("hintmask", "cntrmask"):
- hintMask, isOperator, index = self.getToken(index)
- bits = []
- for byte in hintMask:
- bits.append(num2binary(byteord(byte), 8))
- hintMask = strjoin(bits)
- line = " ".join(args + [token, hintMask])
- else:
- line = " ".join(args + [token])
- xmlWriter.write(line)
- xmlWriter.newline()
- args = []
- else:
- if isinstance(token, float):
- token = floatToFixedToStr(token, precisionBits=16)
- else:
- token = str(token)
- args.append(token)
- if args:
- # NOTE: only CFF2 charstrings/subrs can have numeric arguments on
- # the stack after the last operator. Compiling this would fail if
- # this is part of CFF 1.0 table.
- line = " ".join(args)
- xmlWriter.write(line)
-
- def fromXML(self, name, attrs, content):
- from fontTools.misc.textTools import binary2num, readHex
-
- if attrs.get("raw"):
- self.setBytecode(readHex(content))
- return
- content = strjoin(content)
- content = content.split()
- program = []
- end = len(content)
- i = 0
- while i < end:
- token = content[i]
- i = i + 1
- try:
- token = int(token)
- except ValueError:
- try:
- token = strToFixedToFloat(token, precisionBits=16)
- except ValueError:
- program.append(token)
- if token in ("hintmask", "cntrmask"):
- mask = content[i]
- maskBytes = b""
- for j in range(0, len(mask), 8):
- maskBytes = maskBytes + bytechr(binary2num(mask[j : j + 8]))
- program.append(maskBytes)
- i = i + 1
- else:
- program.append(token)
- else:
- program.append(token)
- self.setProgram(program)
-
-
-class T1CharString(T2CharString):
- operandEncoding = t1OperandEncoding
- operators, opcodes = buildOperatorDict(t1Operators)
-
- def __init__(self, bytecode=None, program=None, subrs=None):
- super().__init__(bytecode, program)
- self.subrs = subrs
-
- def getIntEncoder(self):
- return encodeIntT1
-
- def getFixedEncoder(self):
- def encodeFixed(value):
- raise TypeError("Type 1 charstrings don't support floating point operands")
-
- def decompile(self):
- if self.bytecode is None:
- return
- program = []
- index = 0
- while True:
- token, isOperator, index = self.getToken(index)
- if token is None:
- break
- program.append(token)
- self.setProgram(program)
-
- def draw(self, pen):
- extractor = T1OutlineExtractor(pen, self.subrs)
- extractor.execute(self)
- self.width = extractor.width
-
-
-class DictDecompiler(object):
- operandEncoding = cffDictOperandEncoding
-
- def __init__(self, strings, parent=None):
- self.stack = []
- self.strings = strings
- self.dict = {}
- self.parent = parent
-
- def getDict(self):
- assert len(self.stack) == 0, "non-empty stack"
- return self.dict
-
- def decompile(self, data):
- index = 0
- lenData = len(data)
- push = self.stack.append
- while index < lenData:
- b0 = byteord(data[index])
- index = index + 1
- handler = self.operandEncoding[b0]
- value, index = handler(self, b0, data, index)
- if value is not None:
- push(value)
-
- def pop(self):
- value = self.stack[-1]
- del self.stack[-1]
- return value
-
- def popall(self):
- args = self.stack[:]
- del self.stack[:]
- return args
-
- def handle_operator(self, operator):
- operator, argType = operator
- if isinstance(argType, tuple):
- value = ()
- for i in range(len(argType) - 1, -1, -1):
- arg = argType[i]
- arghandler = getattr(self, "arg_" + arg)
- value = (arghandler(operator),) + value
- else:
- arghandler = getattr(self, "arg_" + argType)
- value = arghandler(operator)
- if operator == "blend":
- self.stack.extend(value)
- else:
- self.dict[operator] = value
-
- def arg_number(self, name):
- if isinstance(self.stack[0], list):
- out = self.arg_blend_number(self.stack)
- else:
- out = self.pop()
- return out
-
- def arg_blend_number(self, name):
- out = []
- blendArgs = self.pop()
- numMasters = len(blendArgs)
- out.append(blendArgs)
- out.append("blend")
- dummy = self.popall()
- return blendArgs
-
- def arg_SID(self, name):
- return self.strings[self.pop()]
-
- def arg_array(self, name):
- return self.popall()
-
- def arg_blendList(self, name):
- """
- There may be non-blend args at the top of the stack. We first calculate
- where the blend args start in the stack. These are the last
- numMasters*numBlends) +1 args.
- The blend args starts with numMasters relative coordinate values, the BlueValues in the list from the default master font. This is followed by
- numBlends list of values. Each of value in one of these lists is the
- Variable Font delta for the matching region.
-
- We re-arrange this to be a list of numMaster entries. Each entry starts with the corresponding default font relative value, and is followed by
- the delta values. We then convert the default values, the first item in each entry, to an absolute value.
- """
- vsindex = self.dict.get("vsindex", 0)
- numMasters = (
- self.parent.getNumRegions(vsindex) + 1
- ) # only a PrivateDict has blended ops.
- numBlends = self.pop()
- args = self.popall()
- numArgs = len(args)
- # The spec says that there should be no non-blended Blue Values,.
- assert numArgs == numMasters * numBlends
- value = [None] * numBlends
- numDeltas = numMasters - 1
- i = 0
- prevVal = 0
- while i < numBlends:
- newVal = args[i] + prevVal
- prevVal = newVal
- masterOffset = numBlends + (i * numDeltas)
- blendList = [newVal] + args[masterOffset : masterOffset + numDeltas]
- value[i] = blendList
- i += 1
- return value
-
- def arg_delta(self, name):
- valueList = self.popall()
- out = []
- if valueList and isinstance(valueList[0], list):
- # arg_blendList() has already converted these to absolute values.
- out = valueList
- else:
- current = 0
- for v in valueList:
- current = current + v
- out.append(current)
- return out
-
-
-def calcSubrBias(subrs):
- nSubrs = len(subrs)
- if nSubrs < 1240:
- bias = 107
- elif nSubrs < 33900:
- bias = 1131
- else:
- bias = 32768
- return bias
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/psLib.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/psLib.py
deleted file mode 100644
index 3bfdb4a..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/psLib.py
+++ /dev/null
@@ -1,398 +0,0 @@
-from fontTools.misc.textTools import bytechr, byteord, bytesjoin, tobytes, tostr
-from fontTools.misc import eexec
-from .psOperators import (
- PSOperators,
- ps_StandardEncoding,
- ps_array,
- ps_boolean,
- ps_dict,
- ps_integer,
- ps_literal,
- ps_mark,
- ps_name,
- ps_operator,
- ps_procedure,
- ps_procmark,
- ps_real,
- ps_string,
-)
-import re
-from collections.abc import Callable
-from string import whitespace
-import logging
-
-
-log = logging.getLogger(__name__)
-
-ps_special = b"()<>[]{}%" # / is one too, but we take care of that one differently
-
-skipwhiteRE = re.compile(bytesjoin([b"[", whitespace, b"]*"]))
-endofthingPat = bytesjoin([b"[^][(){}<>/%", whitespace, b"]*"])
-endofthingRE = re.compile(endofthingPat)
-commentRE = re.compile(b"%[^\n\r]*")
-
-# XXX This not entirely correct as it doesn't allow *nested* embedded parens:
-stringPat = rb"""
- \(
- (
- (
- [^()]* \ [()]
- )
- |
- (
- [^()]* \( [^()]* \)
- )
- )*
- [^()]*
- \)
-"""
-stringPat = b"".join(stringPat.split())
-stringRE = re.compile(stringPat)
-
-hexstringRE = re.compile(bytesjoin([b"<[", whitespace, b"0-9A-Fa-f]*>"]))
-
-
-class PSTokenError(Exception):
- pass
-
-
-class PSError(Exception):
- pass
-
-
-class PSTokenizer(object):
- def __init__(self, buf=b"", encoding="ascii"):
- # Force self.buf to be a byte string
- buf = tobytes(buf)
- self.buf = buf
- self.len = len(buf)
- self.pos = 0
- self.closed = False
- self.encoding = encoding
-
- def read(self, n=-1):
- """Read at most 'n' bytes from the buffer, or less if the read
- hits EOF before obtaining 'n' bytes.
- If 'n' is negative or omitted, read all data until EOF is reached.
- """
- if self.closed:
- raise ValueError("I/O operation on closed file")
- if n is None or n < 0:
- newpos = self.len
- else:
- newpos = min(self.pos + n, self.len)
- r = self.buf[self.pos : newpos]
- self.pos = newpos
- return r
-
- def close(self):
- if not self.closed:
- self.closed = True
- del self.buf, self.pos
-
- def getnexttoken(
- self,
- # localize some stuff, for performance
- len=len,
- ps_special=ps_special,
- stringmatch=stringRE.match,
- hexstringmatch=hexstringRE.match,
- commentmatch=commentRE.match,
- endmatch=endofthingRE.match,
- ):
- self.skipwhite()
- if self.pos >= self.len:
- return None, None
- pos = self.pos
- buf = self.buf
- char = bytechr(byteord(buf[pos]))
- if char in ps_special:
- if char in b"{}[]":
- tokentype = "do_special"
- token = char
- elif char == b"%":
- tokentype = "do_comment"
- _, nextpos = commentmatch(buf, pos).span()
- token = buf[pos:nextpos]
- elif char == b"(":
- tokentype = "do_string"
- m = stringmatch(buf, pos)
- if m is None:
- raise PSTokenError("bad string at character %d" % pos)
- _, nextpos = m.span()
- token = buf[pos:nextpos]
- elif char == b"<":
- tokentype = "do_hexstring"
- m = hexstringmatch(buf, pos)
- if m is None:
- raise PSTokenError("bad hexstring at character %d" % pos)
- _, nextpos = m.span()
- token = buf[pos:nextpos]
- else:
- raise PSTokenError("bad token at character %d" % pos)
- else:
- if char == b"/":
- tokentype = "do_literal"
- m = endmatch(buf, pos + 1)
- else:
- tokentype = ""
- m = endmatch(buf, pos)
- if m is None:
- raise PSTokenError("bad token at character %d" % pos)
- _, nextpos = m.span()
- token = buf[pos:nextpos]
- self.pos = pos + len(token)
- token = tostr(token, encoding=self.encoding)
- return tokentype, token
-
- def skipwhite(self, whitematch=skipwhiteRE.match):
- _, nextpos = whitematch(self.buf, self.pos).span()
- self.pos = nextpos
-
- def starteexec(self):
- self.pos = self.pos + 1
- self.dirtybuf = self.buf[self.pos :]
- self.buf, R = eexec.decrypt(self.dirtybuf, 55665)
- self.len = len(self.buf)
- self.pos = 4
-
- def stopeexec(self):
- if not hasattr(self, "dirtybuf"):
- return
- self.buf = self.dirtybuf
- del self.dirtybuf
-
-
-class PSInterpreter(PSOperators):
- def __init__(self, encoding="ascii"):
- systemdict = {}
- userdict = {}
- self.encoding = encoding
- self.dictstack = [systemdict, userdict]
- self.stack = []
- self.proclevel = 0
- self.procmark = ps_procmark()
- self.fillsystemdict()
-
- def fillsystemdict(self):
- systemdict = self.dictstack[0]
- systemdict["["] = systemdict["mark"] = self.mark = ps_mark()
- systemdict["]"] = ps_operator("]", self.do_makearray)
- systemdict["true"] = ps_boolean(1)
- systemdict["false"] = ps_boolean(0)
- systemdict["StandardEncoding"] = ps_array(ps_StandardEncoding)
- systemdict["FontDirectory"] = ps_dict({})
- self.suckoperators(systemdict, self.__class__)
-
- def suckoperators(self, systemdict, klass):
- for name in dir(klass):
- attr = getattr(self, name)
- if isinstance(attr, Callable) and name[:3] == "ps_":
- name = name[3:]
- systemdict[name] = ps_operator(name, attr)
- for baseclass in klass.__bases__:
- self.suckoperators(systemdict, baseclass)
-
- def interpret(self, data, getattr=getattr):
- tokenizer = self.tokenizer = PSTokenizer(data, self.encoding)
- getnexttoken = tokenizer.getnexttoken
- do_token = self.do_token
- handle_object = self.handle_object
- try:
- while 1:
- tokentype, token = getnexttoken()
- if not token:
- break
- if tokentype:
- handler = getattr(self, tokentype)
- object = handler(token)
- else:
- object = do_token(token)
- if object is not None:
- handle_object(object)
- tokenizer.close()
- self.tokenizer = None
- except:
- if self.tokenizer is not None:
- log.debug(
- "ps error:\n"
- "- - - - - - -\n"
- "%s\n"
- ">>>\n"
- "%s\n"
- "- - - - - - -",
- self.tokenizer.buf[self.tokenizer.pos - 50 : self.tokenizer.pos],
- self.tokenizer.buf[self.tokenizer.pos : self.tokenizer.pos + 50],
- )
- raise
-
- def handle_object(self, object):
- if not (self.proclevel or object.literal or object.type == "proceduretype"):
- if object.type != "operatortype":
- object = self.resolve_name(object.value)
- if object.literal:
- self.push(object)
- else:
- if object.type == "proceduretype":
- self.call_procedure(object)
- else:
- object.function()
- else:
- self.push(object)
-
- def call_procedure(self, proc):
- handle_object = self.handle_object
- for item in proc.value:
- handle_object(item)
-
- def resolve_name(self, name):
- dictstack = self.dictstack
- for i in range(len(dictstack) - 1, -1, -1):
- if name in dictstack[i]:
- return dictstack[i][name]
- raise PSError("name error: " + str(name))
-
- def do_token(
- self,
- token,
- int=int,
- float=float,
- ps_name=ps_name,
- ps_integer=ps_integer,
- ps_real=ps_real,
- ):
- try:
- num = int(token)
- except (ValueError, OverflowError):
- try:
- num = float(token)
- except (ValueError, OverflowError):
- if "#" in token:
- hashpos = token.find("#")
- try:
- base = int(token[:hashpos])
- num = int(token[hashpos + 1 :], base)
- except (ValueError, OverflowError):
- return ps_name(token)
- else:
- return ps_integer(num)
- else:
- return ps_name(token)
- else:
- return ps_real(num)
- else:
- return ps_integer(num)
-
- def do_comment(self, token):
- pass
-
- def do_literal(self, token):
- return ps_literal(token[1:])
-
- def do_string(self, token):
- return ps_string(token[1:-1])
-
- def do_hexstring(self, token):
- hexStr = "".join(token[1:-1].split())
- if len(hexStr) % 2:
- hexStr = hexStr + "0"
- cleanstr = []
- for i in range(0, len(hexStr), 2):
- cleanstr.append(chr(int(hexStr[i : i + 2], 16)))
- cleanstr = "".join(cleanstr)
- return ps_string(cleanstr)
-
- def do_special(self, token):
- if token == "{":
- self.proclevel = self.proclevel + 1
- return self.procmark
- elif token == "}":
- proc = []
- while 1:
- topobject = self.pop()
- if topobject == self.procmark:
- break
- proc.append(topobject)
- self.proclevel = self.proclevel - 1
- proc.reverse()
- return ps_procedure(proc)
- elif token == "[":
- return self.mark
- elif token == "]":
- return ps_name("]")
- else:
- raise PSTokenError("huh?")
-
- def push(self, object):
- self.stack.append(object)
-
- def pop(self, *types):
- stack = self.stack
- if not stack:
- raise PSError("stack underflow")
- object = stack[-1]
- if types:
- if object.type not in types:
- raise PSError(
- "typecheck, expected %s, found %s" % (repr(types), object.type)
- )
- del stack[-1]
- return object
-
- def do_makearray(self):
- array = []
- while 1:
- topobject = self.pop()
- if topobject == self.mark:
- break
- array.append(topobject)
- array.reverse()
- self.push(ps_array(array))
-
- def close(self):
- """Remove circular references."""
- del self.stack
- del self.dictstack
-
-
-def unpack_item(item):
- tp = type(item.value)
- if tp == dict:
- newitem = {}
- for key, value in item.value.items():
- newitem[key] = unpack_item(value)
- elif tp == list:
- newitem = [None] * len(item.value)
- for i in range(len(item.value)):
- newitem[i] = unpack_item(item.value[i])
- if item.type == "proceduretype":
- newitem = tuple(newitem)
- else:
- newitem = item.value
- return newitem
-
-
-def suckfont(data, encoding="ascii"):
- m = re.search(rb"/FontName\s+/([^ \t\n\r]+)\s+def", data)
- if m:
- fontName = m.group(1)
- fontName = fontName.decode()
- else:
- fontName = None
- interpreter = PSInterpreter(encoding=encoding)
- interpreter.interpret(
- b"/Helvetica 4 dict dup /Encoding StandardEncoding put definefont pop"
- )
- interpreter.interpret(data)
- fontdir = interpreter.dictstack[0]["FontDirectory"].value
- if fontName in fontdir:
- rawfont = fontdir[fontName]
- else:
- # fall back, in case fontName wasn't found
- fontNames = list(fontdir.keys())
- if len(fontNames) > 1:
- fontNames.remove("Helvetica")
- fontNames.sort()
- rawfont = fontdir[fontNames[0]]
- interpreter.close()
- return unpack_item(rawfont)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/psOperators.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/psOperators.py
deleted file mode 100644
index d0b975e..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/psOperators.py
+++ /dev/null
@@ -1,572 +0,0 @@
-_accessstrings = {0: "", 1: "readonly", 2: "executeonly", 3: "noaccess"}
-
-
-class ps_object(object):
- literal = 1
- access = 0
- value = None
-
- def __init__(self, value):
- self.value = value
- self.type = self.__class__.__name__[3:] + "type"
-
- def __repr__(self):
- return "<%s %s>" % (self.__class__.__name__[3:], repr(self.value))
-
-
-class ps_operator(ps_object):
- literal = 0
-
- def __init__(self, name, function):
- self.name = name
- self.function = function
- self.type = self.__class__.__name__[3:] + "type"
-
- def __repr__(self):
- return "" % self.name
-
-
-class ps_procedure(ps_object):
- literal = 0
-
- def __repr__(self):
- return ""
-
- def __str__(self):
- psstring = "{"
- for i in range(len(self.value)):
- if i:
- psstring = psstring + " " + str(self.value[i])
- else:
- psstring = psstring + str(self.value[i])
- return psstring + "}"
-
-
-class ps_name(ps_object):
- literal = 0
-
- def __str__(self):
- if self.literal:
- return "/" + self.value
- else:
- return self.value
-
-
-class ps_literal(ps_object):
- def __str__(self):
- return "/" + self.value
-
-
-class ps_array(ps_object):
- def __str__(self):
- psstring = "["
- for i in range(len(self.value)):
- item = self.value[i]
- access = _accessstrings[item.access]
- if access:
- access = " " + access
- if i:
- psstring = psstring + " " + str(item) + access
- else:
- psstring = psstring + str(item) + access
- return psstring + "]"
-
- def __repr__(self):
- return ""
-
-
-_type1_pre_eexec_order = [
- "FontInfo",
- "FontName",
- "Encoding",
- "PaintType",
- "FontType",
- "FontMatrix",
- "FontBBox",
- "UniqueID",
- "Metrics",
- "StrokeWidth",
-]
-
-_type1_fontinfo_order = [
- "version",
- "Notice",
- "FullName",
- "FamilyName",
- "Weight",
- "ItalicAngle",
- "isFixedPitch",
- "UnderlinePosition",
- "UnderlineThickness",
-]
-
-_type1_post_eexec_order = ["Private", "CharStrings", "FID"]
-
-
-def _type1_item_repr(key, value):
- psstring = ""
- access = _accessstrings[value.access]
- if access:
- access = access + " "
- if key == "CharStrings":
- psstring = psstring + "/%s %s def\n" % (
- key,
- _type1_CharString_repr(value.value),
- )
- elif key == "Encoding":
- psstring = psstring + _type1_Encoding_repr(value, access)
- else:
- psstring = psstring + "/%s %s %sdef\n" % (str(key), str(value), access)
- return psstring
-
-
-def _type1_Encoding_repr(encoding, access):
- encoding = encoding.value
- psstring = "/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n"
- for i in range(256):
- name = encoding[i].value
- if name != ".notdef":
- psstring = psstring + "dup %d /%s put\n" % (i, name)
- return psstring + access + "def\n"
-
-
-def _type1_CharString_repr(charstrings):
- items = sorted(charstrings.items())
- return "xxx"
-
-
-class ps_font(ps_object):
- def __str__(self):
- psstring = "%d dict dup begin\n" % len(self.value)
- for key in _type1_pre_eexec_order:
- try:
- value = self.value[key]
- except KeyError:
- pass
- else:
- psstring = psstring + _type1_item_repr(key, value)
- items = sorted(self.value.items())
- for key, value in items:
- if key not in _type1_pre_eexec_order + _type1_post_eexec_order:
- psstring = psstring + _type1_item_repr(key, value)
- psstring = psstring + "currentdict end\ncurrentfile eexec\ndup "
- for key in _type1_post_eexec_order:
- try:
- value = self.value[key]
- except KeyError:
- pass
- else:
- psstring = psstring + _type1_item_repr(key, value)
- return (
- psstring
- + "dup/FontName get exch definefont pop\nmark currentfile closefile\n"
- + 8 * (64 * "0" + "\n")
- + "cleartomark"
- + "\n"
- )
-
- def __repr__(self):
- return ""
-
-
-class ps_file(ps_object):
- pass
-
-
-class ps_dict(ps_object):
- def __str__(self):
- psstring = "%d dict dup begin\n" % len(self.value)
- items = sorted(self.value.items())
- for key, value in items:
- access = _accessstrings[value.access]
- if access:
- access = access + " "
- psstring = psstring + "/%s %s %sdef\n" % (str(key), str(value), access)
- return psstring + "end "
-
- def __repr__(self):
- return ""
-
-
-class ps_mark(ps_object):
- def __init__(self):
- self.value = "mark"
- self.type = self.__class__.__name__[3:] + "type"
-
-
-class ps_procmark(ps_object):
- def __init__(self):
- self.value = "procmark"
- self.type = self.__class__.__name__[3:] + "type"
-
-
-class ps_null(ps_object):
- def __init__(self):
- self.type = self.__class__.__name__[3:] + "type"
-
-
-class ps_boolean(ps_object):
- def __str__(self):
- if self.value:
- return "true"
- else:
- return "false"
-
-
-class ps_string(ps_object):
- def __str__(self):
- return "(%s)" % repr(self.value)[1:-1]
-
-
-class ps_integer(ps_object):
- def __str__(self):
- return repr(self.value)
-
-
-class ps_real(ps_object):
- def __str__(self):
- return repr(self.value)
-
-
-class PSOperators(object):
- def ps_def(self):
- obj = self.pop()
- name = self.pop()
- self.dictstack[-1][name.value] = obj
-
- def ps_bind(self):
- proc = self.pop("proceduretype")
- self.proc_bind(proc)
- self.push(proc)
-
- def proc_bind(self, proc):
- for i in range(len(proc.value)):
- item = proc.value[i]
- if item.type == "proceduretype":
- self.proc_bind(item)
- else:
- if not item.literal:
- try:
- obj = self.resolve_name(item.value)
- except:
- pass
- else:
- if obj.type == "operatortype":
- proc.value[i] = obj
-
- def ps_exch(self):
- if len(self.stack) < 2:
- raise RuntimeError("stack underflow")
- obj1 = self.pop()
- obj2 = self.pop()
- self.push(obj1)
- self.push(obj2)
-
- def ps_dup(self):
- if not self.stack:
- raise RuntimeError("stack underflow")
- self.push(self.stack[-1])
-
- def ps_exec(self):
- obj = self.pop()
- if obj.type == "proceduretype":
- self.call_procedure(obj)
- else:
- self.handle_object(obj)
-
- def ps_count(self):
- self.push(ps_integer(len(self.stack)))
-
- def ps_eq(self):
- any1 = self.pop()
- any2 = self.pop()
- self.push(ps_boolean(any1.value == any2.value))
-
- def ps_ne(self):
- any1 = self.pop()
- any2 = self.pop()
- self.push(ps_boolean(any1.value != any2.value))
-
- def ps_cvx(self):
- obj = self.pop()
- obj.literal = 0
- self.push(obj)
-
- def ps_matrix(self):
- matrix = [
- ps_real(1.0),
- ps_integer(0),
- ps_integer(0),
- ps_real(1.0),
- ps_integer(0),
- ps_integer(0),
- ]
- self.push(ps_array(matrix))
-
- def ps_string(self):
- num = self.pop("integertype").value
- self.push(ps_string("\0" * num))
-
- def ps_type(self):
- obj = self.pop()
- self.push(ps_string(obj.type))
-
- def ps_store(self):
- value = self.pop()
- key = self.pop()
- name = key.value
- for i in range(len(self.dictstack) - 1, -1, -1):
- if name in self.dictstack[i]:
- self.dictstack[i][name] = value
- break
- self.dictstack[-1][name] = value
-
- def ps_where(self):
- name = self.pop()
- # XXX
- self.push(ps_boolean(0))
-
- def ps_systemdict(self):
- self.push(ps_dict(self.dictstack[0]))
-
- def ps_userdict(self):
- self.push(ps_dict(self.dictstack[1]))
-
- def ps_currentdict(self):
- self.push(ps_dict(self.dictstack[-1]))
-
- def ps_currentfile(self):
- self.push(ps_file(self.tokenizer))
-
- def ps_eexec(self):
- f = self.pop("filetype").value
- f.starteexec()
-
- def ps_closefile(self):
- f = self.pop("filetype").value
- f.skipwhite()
- f.stopeexec()
-
- def ps_cleartomark(self):
- obj = self.pop()
- while obj != self.mark:
- obj = self.pop()
-
- def ps_readstring(self, ps_boolean=ps_boolean, len=len):
- s = self.pop("stringtype")
- oldstr = s.value
- f = self.pop("filetype")
- # pad = file.value.read(1)
- # for StringIO, this is faster
- f.value.pos = f.value.pos + 1
- newstr = f.value.read(len(oldstr))
- s.value = newstr
- self.push(s)
- self.push(ps_boolean(len(oldstr) == len(newstr)))
-
- def ps_known(self):
- key = self.pop()
- d = self.pop("dicttype", "fonttype")
- self.push(ps_boolean(key.value in d.value))
-
- def ps_if(self):
- proc = self.pop("proceduretype")
- if self.pop("booleantype").value:
- self.call_procedure(proc)
-
- def ps_ifelse(self):
- proc2 = self.pop("proceduretype")
- proc1 = self.pop("proceduretype")
- if self.pop("booleantype").value:
- self.call_procedure(proc1)
- else:
- self.call_procedure(proc2)
-
- def ps_readonly(self):
- obj = self.pop()
- if obj.access < 1:
- obj.access = 1
- self.push(obj)
-
- def ps_executeonly(self):
- obj = self.pop()
- if obj.access < 2:
- obj.access = 2
- self.push(obj)
-
- def ps_noaccess(self):
- obj = self.pop()
- if obj.access < 3:
- obj.access = 3
- self.push(obj)
-
- def ps_not(self):
- obj = self.pop("booleantype", "integertype")
- if obj.type == "booleantype":
- self.push(ps_boolean(not obj.value))
- else:
- self.push(ps_integer(~obj.value))
-
- def ps_print(self):
- str = self.pop("stringtype")
- print("PS output --->", str.value)
-
- def ps_anchorsearch(self):
- seek = self.pop("stringtype")
- s = self.pop("stringtype")
- seeklen = len(seek.value)
- if s.value[:seeklen] == seek.value:
- self.push(ps_string(s.value[seeklen:]))
- self.push(seek)
- self.push(ps_boolean(1))
- else:
- self.push(s)
- self.push(ps_boolean(0))
-
- def ps_array(self):
- num = self.pop("integertype")
- array = ps_array([None] * num.value)
- self.push(array)
-
- def ps_astore(self):
- array = self.pop("arraytype")
- for i in range(len(array.value) - 1, -1, -1):
- array.value[i] = self.pop()
- self.push(array)
-
- def ps_load(self):
- name = self.pop()
- self.push(self.resolve_name(name.value))
-
- def ps_put(self):
- obj1 = self.pop()
- obj2 = self.pop()
- obj3 = self.pop("arraytype", "dicttype", "stringtype", "proceduretype")
- tp = obj3.type
- if tp == "arraytype" or tp == "proceduretype":
- obj3.value[obj2.value] = obj1
- elif tp == "dicttype":
- obj3.value[obj2.value] = obj1
- elif tp == "stringtype":
- index = obj2.value
- obj3.value = obj3.value[:index] + chr(obj1.value) + obj3.value[index + 1 :]
-
- def ps_get(self):
- obj1 = self.pop()
- if obj1.value == "Encoding":
- pass
- obj2 = self.pop(
- "arraytype", "dicttype", "stringtype", "proceduretype", "fonttype"
- )
- tp = obj2.type
- if tp in ("arraytype", "proceduretype"):
- self.push(obj2.value[obj1.value])
- elif tp in ("dicttype", "fonttype"):
- self.push(obj2.value[obj1.value])
- elif tp == "stringtype":
- self.push(ps_integer(ord(obj2.value[obj1.value])))
- else:
- assert False, "shouldn't get here"
-
- def ps_getinterval(self):
- obj1 = self.pop("integertype")
- obj2 = self.pop("integertype")
- obj3 = self.pop("arraytype", "stringtype")
- tp = obj3.type
- if tp == "arraytype":
- self.push(ps_array(obj3.value[obj2.value : obj2.value + obj1.value]))
- elif tp == "stringtype":
- self.push(ps_string(obj3.value[obj2.value : obj2.value + obj1.value]))
-
- def ps_putinterval(self):
- obj1 = self.pop("arraytype", "stringtype")
- obj2 = self.pop("integertype")
- obj3 = self.pop("arraytype", "stringtype")
- tp = obj3.type
- if tp == "arraytype":
- obj3.value[obj2.value : obj2.value + len(obj1.value)] = obj1.value
- elif tp == "stringtype":
- newstr = obj3.value[: obj2.value]
- newstr = newstr + obj1.value
- newstr = newstr + obj3.value[obj2.value + len(obj1.value) :]
- obj3.value = newstr
-
- def ps_cvn(self):
- self.push(ps_name(self.pop("stringtype").value))
-
- def ps_index(self):
- n = self.pop("integertype").value
- if n < 0:
- raise RuntimeError("index may not be negative")
- self.push(self.stack[-1 - n])
-
- def ps_for(self):
- proc = self.pop("proceduretype")
- limit = self.pop("integertype", "realtype").value
- increment = self.pop("integertype", "realtype").value
- i = self.pop("integertype", "realtype").value
- while 1:
- if increment > 0:
- if i > limit:
- break
- else:
- if i < limit:
- break
- if type(i) == type(0.0):
- self.push(ps_real(i))
- else:
- self.push(ps_integer(i))
- self.call_procedure(proc)
- i = i + increment
-
- def ps_forall(self):
- proc = self.pop("proceduretype")
- obj = self.pop("arraytype", "stringtype", "dicttype")
- tp = obj.type
- if tp == "arraytype":
- for item in obj.value:
- self.push(item)
- self.call_procedure(proc)
- elif tp == "stringtype":
- for item in obj.value:
- self.push(ps_integer(ord(item)))
- self.call_procedure(proc)
- elif tp == "dicttype":
- for key, value in obj.value.items():
- self.push(ps_name(key))
- self.push(value)
- self.call_procedure(proc)
-
- def ps_definefont(self):
- font = self.pop("dicttype")
- name = self.pop()
- font = ps_font(font.value)
- self.dictstack[0]["FontDirectory"].value[name.value] = font
- self.push(font)
-
- def ps_findfont(self):
- name = self.pop()
- font = self.dictstack[0]["FontDirectory"].value[name.value]
- self.push(font)
-
- def ps_pop(self):
- self.pop()
-
- def ps_dict(self):
- self.pop("integertype")
- self.push(ps_dict({}))
-
- def ps_begin(self):
- self.dictstack.append(self.pop("dicttype").value)
-
- def ps_end(self):
- if len(self.dictstack) > 2:
- del self.dictstack[-1]
- else:
- raise RuntimeError("dictstack underflow")
-
-
-notdef = ".notdef"
-from fontTools.encodings.StandardEncoding import StandardEncoding
-
-ps_StandardEncoding = list(map(ps_name, StandardEncoding))
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/py23.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/py23.py
deleted file mode 100644
index 29f634d..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/py23.py
+++ /dev/null
@@ -1,96 +0,0 @@
-"""Python 2/3 compat layer leftovers."""
-
-import decimal as _decimal
-import math as _math
-import warnings
-from contextlib import redirect_stderr, redirect_stdout
-from io import BytesIO
-from io import StringIO as UnicodeIO
-from types import SimpleNamespace
-
-from .textTools import Tag, bytechr, byteord, bytesjoin, strjoin, tobytes, tostr
-
-warnings.warn(
- "The py23 module has been deprecated and will be removed in a future release. "
- "Please update your code.",
- DeprecationWarning,
-)
-
-__all__ = [
- "basestring",
- "bytechr",
- "byteord",
- "BytesIO",
- "bytesjoin",
- "open",
- "Py23Error",
- "range",
- "RecursionError",
- "round",
- "SimpleNamespace",
- "StringIO",
- "strjoin",
- "Tag",
- "tobytes",
- "tostr",
- "tounicode",
- "unichr",
- "unicode",
- "UnicodeIO",
- "xrange",
- "zip",
-]
-
-
-class Py23Error(NotImplementedError):
- pass
-
-
-RecursionError = RecursionError
-StringIO = UnicodeIO
-
-basestring = str
-isclose = _math.isclose
-isfinite = _math.isfinite
-open = open
-range = range
-round = round3 = round
-unichr = chr
-unicode = str
-zip = zip
-
-tounicode = tostr
-
-
-def xrange(*args, **kwargs):
- raise Py23Error("'xrange' is not defined. Use 'range' instead.")
-
-
-def round2(number, ndigits=None):
- """
- Implementation of Python 2 built-in round() function.
- Rounds a number to a given precision in decimal digits (default
- 0 digits). The result is a floating point number. Values are rounded
- to the closest multiple of 10 to the power minus ndigits; if two
- multiples are equally close, rounding is done away from 0.
- ndigits may be negative.
- See Python 2 documentation:
- https://docs.python.org/2/library/functions.html?highlight=round#round
- """
- if ndigits is None:
- ndigits = 0
-
- if ndigits < 0:
- exponent = 10 ** (-ndigits)
- quotient, remainder = divmod(number, exponent)
- if remainder >= exponent // 2 and number >= 0:
- quotient += 1
- return float(quotient * exponent)
- else:
- exponent = _decimal.Decimal("10") ** (-ndigits)
-
- d = _decimal.Decimal.from_float(number).quantize(
- exponent, rounding=_decimal.ROUND_HALF_UP
- )
-
- return float(d)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/roundTools.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/roundTools.py
deleted file mode 100644
index 48a47c0..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/roundTools.py
+++ /dev/null
@@ -1,109 +0,0 @@
-"""
-Various round-to-integer helpers.
-"""
-
-import math
-import functools
-import logging
-
-log = logging.getLogger(__name__)
-
-__all__ = [
- "noRound",
- "otRound",
- "maybeRound",
- "roundFunc",
-]
-
-
-def noRound(value):
- return value
-
-
-def otRound(value):
- """Round float value to nearest integer towards ``+Infinity``.
-
- The OpenType spec (in the section on `"normalization" of OpenType Font Variations `_)
- defines the required method for converting floating point values to
- fixed-point. In particular it specifies the following rounding strategy:
-
- for fractional values of 0.5 and higher, take the next higher integer;
- for other fractional values, truncate.
-
- This function rounds the floating-point value according to this strategy
- in preparation for conversion to fixed-point.
-
- Args:
- value (float): The input floating-point value.
-
- Returns
- float: The rounded value.
- """
- # See this thread for how we ended up with this implementation:
- # https://github.com/fonttools/fonttools/issues/1248#issuecomment-383198166
- return int(math.floor(value + 0.5))
-
-
-def maybeRound(v, tolerance, round=otRound):
- rounded = round(v)
- return rounded if abs(rounded - v) <= tolerance else v
-
-
-def roundFunc(tolerance, round=otRound):
- if tolerance < 0:
- raise ValueError("Rounding tolerance must be positive")
-
- if tolerance == 0:
- return noRound
-
- if tolerance >= 0.5:
- return round
-
- return functools.partial(maybeRound, tolerance=tolerance, round=round)
-
-
-def nearestMultipleShortestRepr(value: float, factor: float) -> str:
- """Round to nearest multiple of factor and return shortest decimal representation.
-
- This chooses the float that is closer to a multiple of the given factor while
- having the shortest decimal representation (the least number of fractional decimal
- digits).
-
- For example, given the following:
-
- >>> nearestMultipleShortestRepr(-0.61883544921875, 1.0/(1<<14))
- '-0.61884'
-
- Useful when you need to serialize or print a fixed-point number (or multiples
- thereof, such as F2Dot14 fractions of 180 degrees in COLRv1 PaintRotate) in
- a human-readable form.
-
- Args:
- value (value): The value to be rounded and serialized.
- factor (float): The value which the result is a close multiple of.
-
- Returns:
- str: A compact string representation of the value.
- """
- if not value:
- return "0.0"
-
- value = otRound(value / factor) * factor
- eps = 0.5 * factor
- lo = value - eps
- hi = value + eps
- # If the range of valid choices spans an integer, return the integer.
- if int(lo) != int(hi):
- return str(float(round(value)))
-
- fmt = "%.8f"
- lo = fmt % lo
- hi = fmt % hi
- assert len(lo) == len(hi) and lo != hi
- for i in range(len(lo)):
- if lo[i] != hi[i]:
- break
- period = lo.find(".")
- assert period < i
- fmt = "%%.%df" % (i - period)
- return fmt % value
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/sstruct.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/sstruct.py
deleted file mode 100644
index d35bc9a..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/sstruct.py
+++ /dev/null
@@ -1,220 +0,0 @@
-"""sstruct.py -- SuperStruct
-
-Higher level layer on top of the struct module, enabling to
-bind names to struct elements. The interface is similar to
-struct, except the objects passed and returned are not tuples
-(or argument lists), but dictionaries or instances.
-
-Just like struct, we use fmt strings to describe a data
-structure, except we use one line per element. Lines are
-separated by newlines or semi-colons. Each line contains
-either one of the special struct characters ('@', '=', '<',
-'>' or '!') or a 'name:formatchar' combo (eg. 'myFloat:f').
-Repetitions, like the struct module offers them are not useful
-in this context, except for fixed length strings (eg. 'myInt:5h'
-is not allowed but 'myString:5s' is). The 'x' fmt character
-(pad byte) is treated as 'special', since it is by definition
-anonymous. Extra whitespace is allowed everywhere.
-
-The sstruct module offers one feature that the "normal" struct
-module doesn't: support for fixed point numbers. These are spelled
-as "n.mF", where n is the number of bits before the point, and m
-the number of bits after the point. Fixed point numbers get
-converted to floats.
-
-pack(fmt, object):
- 'object' is either a dictionary or an instance (or actually
- anything that has a __dict__ attribute). If it is a dictionary,
- its keys are used for names. If it is an instance, it's
- attributes are used to grab struct elements from. Returns
- a string containing the data.
-
-unpack(fmt, data, object=None)
- If 'object' is omitted (or None), a new dictionary will be
- returned. If 'object' is a dictionary, it will be used to add
- struct elements to. If it is an instance (or in fact anything
- that has a __dict__ attribute), an attribute will be added for
- each struct element. In the latter two cases, 'object' itself
- is returned.
-
-unpack2(fmt, data, object=None)
- Convenience function. Same as unpack, except data may be longer
- than needed. The returned value is a tuple: (object, leftoverdata).
-
-calcsize(fmt)
- like struct.calcsize(), but uses our own fmt strings:
- it returns the size of the data in bytes.
-"""
-
-from fontTools.misc.fixedTools import fixedToFloat as fi2fl, floatToFixed as fl2fi
-from fontTools.misc.textTools import tobytes, tostr
-import struct
-import re
-
-__version__ = "1.2"
-__copyright__ = "Copyright 1998, Just van Rossum "
-
-
-class Error(Exception):
- pass
-
-
-def pack(fmt, obj):
- formatstring, names, fixes = getformat(fmt, keep_pad_byte=True)
- elements = []
- if not isinstance(obj, dict):
- obj = obj.__dict__
- for name in names:
- value = obj[name]
- if name in fixes:
- # fixed point conversion
- value = fl2fi(value, fixes[name])
- elif isinstance(value, str):
- value = tobytes(value)
- elements.append(value)
- data = struct.pack(*(formatstring,) + tuple(elements))
- return data
-
-
-def unpack(fmt, data, obj=None):
- if obj is None:
- obj = {}
- data = tobytes(data)
- formatstring, names, fixes = getformat(fmt)
- if isinstance(obj, dict):
- d = obj
- else:
- d = obj.__dict__
- elements = struct.unpack(formatstring, data)
- for i in range(len(names)):
- name = names[i]
- value = elements[i]
- if name in fixes:
- # fixed point conversion
- value = fi2fl(value, fixes[name])
- elif isinstance(value, bytes):
- try:
- value = tostr(value)
- except UnicodeDecodeError:
- pass
- d[name] = value
- return obj
-
-
-def unpack2(fmt, data, obj=None):
- length = calcsize(fmt)
- return unpack(fmt, data[:length], obj), data[length:]
-
-
-def calcsize(fmt):
- formatstring, names, fixes = getformat(fmt)
- return struct.calcsize(formatstring)
-
-
-# matches "name:formatchar" (whitespace is allowed)
-_elementRE = re.compile(
- r"\s*" # whitespace
- r"([A-Za-z_][A-Za-z_0-9]*)" # name (python identifier)
- r"\s*:\s*" # whitespace : whitespace
- r"([xcbB?hHiIlLqQfd]|" # formatchar...
- r"[0-9]+[ps]|" # ...formatchar...
- r"([0-9]+)\.([0-9]+)(F))" # ...formatchar
- r"\s*" # whitespace
- r"(#.*)?$" # [comment] + end of string
-)
-
-# matches the special struct fmt chars and 'x' (pad byte)
-_extraRE = re.compile(r"\s*([x@=<>!])\s*(#.*)?$")
-
-# matches an "empty" string, possibly containing whitespace and/or a comment
-_emptyRE = re.compile(r"\s*(#.*)?$")
-
-_fixedpointmappings = {8: "b", 16: "h", 32: "l"}
-
-_formatcache = {}
-
-
-def getformat(fmt, keep_pad_byte=False):
- fmt = tostr(fmt, encoding="ascii")
- try:
- formatstring, names, fixes = _formatcache[fmt]
- except KeyError:
- lines = re.split("[\n;]", fmt)
- formatstring = ""
- names = []
- fixes = {}
- for line in lines:
- if _emptyRE.match(line):
- continue
- m = _extraRE.match(line)
- if m:
- formatchar = m.group(1)
- if formatchar != "x" and formatstring:
- raise Error("a special fmt char must be first")
- else:
- m = _elementRE.match(line)
- if not m:
- raise Error("syntax error in fmt: '%s'" % line)
- name = m.group(1)
- formatchar = m.group(2)
- if keep_pad_byte or formatchar != "x":
- names.append(name)
- if m.group(3):
- # fixed point
- before = int(m.group(3))
- after = int(m.group(4))
- bits = before + after
- if bits not in [8, 16, 32]:
- raise Error("fixed point must be 8, 16 or 32 bits long")
- formatchar = _fixedpointmappings[bits]
- assert m.group(5) == "F"
- fixes[name] = after
- formatstring = formatstring + formatchar
- _formatcache[fmt] = formatstring, names, fixes
- return formatstring, names, fixes
-
-
-def _test():
- fmt = """
- # comments are allowed
- > # big endian (see documentation for struct)
- # empty lines are allowed:
-
- ashort: h
- along: l
- abyte: b # a byte
- achar: c
- astr: 5s
- afloat: f; adouble: d # multiple "statements" are allowed
- afixed: 16.16F
- abool: ?
- apad: x
- """
-
- print("size:", calcsize(fmt))
-
- class foo(object):
- pass
-
- i = foo()
-
- i.ashort = 0x7FFF
- i.along = 0x7FFFFFFF
- i.abyte = 0x7F
- i.achar = "a"
- i.astr = "12345"
- i.afloat = 0.5
- i.adouble = 0.5
- i.afixed = 1.5
- i.abool = True
-
- data = pack(fmt, i)
- print("data:", repr(data))
- print(unpack(fmt, data))
- i2 = foo()
- unpack(fmt, data, i2)
- print(vars(i2))
-
-
-if __name__ == "__main__":
- _test()
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/symfont.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/symfont.py
deleted file mode 100644
index fb9e20a..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/symfont.py
+++ /dev/null
@@ -1,248 +0,0 @@
-from fontTools.pens.basePen import BasePen
-from functools import partial
-from itertools import count
-import sympy as sp
-import sys
-
-n = 3 # Max Bezier degree; 3 for cubic, 2 for quadratic
-
-t, x, y = sp.symbols("t x y", real=True)
-c = sp.symbols("c", real=False) # Complex representation instead of x/y
-
-X = tuple(sp.symbols("x:%d" % (n + 1), real=True))
-Y = tuple(sp.symbols("y:%d" % (n + 1), real=True))
-P = tuple(zip(*(sp.symbols("p:%d[%s]" % (n + 1, w), real=True) for w in "01")))
-C = tuple(sp.symbols("c:%d" % (n + 1), real=False))
-
-# Cubic Bernstein basis functions
-BinomialCoefficient = [(1, 0)]
-for i in range(1, n + 1):
- last = BinomialCoefficient[-1]
- this = tuple(last[j - 1] + last[j] for j in range(len(last))) + (0,)
- BinomialCoefficient.append(this)
-BinomialCoefficient = tuple(tuple(item[:-1]) for item in BinomialCoefficient)
-del last, this
-
-BernsteinPolynomial = tuple(
- tuple(c * t**i * (1 - t) ** (n - i) for i, c in enumerate(coeffs))
- for n, coeffs in enumerate(BinomialCoefficient)
-)
-
-BezierCurve = tuple(
- tuple(
- sum(P[i][j] * bernstein for i, bernstein in enumerate(bernsteins))
- for j in range(2)
- )
- for n, bernsteins in enumerate(BernsteinPolynomial)
-)
-BezierCurveC = tuple(
- sum(C[i] * bernstein for i, bernstein in enumerate(bernsteins))
- for n, bernsteins in enumerate(BernsteinPolynomial)
-)
-
-
-def green(f, curveXY):
- f = -sp.integrate(sp.sympify(f), y)
- f = f.subs({x: curveXY[0], y: curveXY[1]})
- f = sp.integrate(f * sp.diff(curveXY[0], t), (t, 0, 1))
- return f
-
-
-class _BezierFuncsLazy(dict):
- def __init__(self, symfunc):
- self._symfunc = symfunc
- self._bezfuncs = {}
-
- def __missing__(self, i):
- args = ["p%d" % d for d in range(i + 1)]
- f = green(self._symfunc, BezierCurve[i])
- f = sp.gcd_terms(f.collect(sum(P, ()))) # Optimize
- return sp.lambdify(args, f)
-
-
-class GreenPen(BasePen):
- _BezierFuncs = {}
-
- @classmethod
- def _getGreenBezierFuncs(celf, func):
- funcstr = str(func)
- if not funcstr in celf._BezierFuncs:
- celf._BezierFuncs[funcstr] = _BezierFuncsLazy(func)
- return celf._BezierFuncs[funcstr]
-
- def __init__(self, func, glyphset=None):
- BasePen.__init__(self, glyphset)
- self._funcs = self._getGreenBezierFuncs(func)
- self.value = 0
-
- def _moveTo(self, p0):
- self.__startPoint = p0
-
- def _closePath(self):
- p0 = self._getCurrentPoint()
- if p0 != self.__startPoint:
- self._lineTo(self.__startPoint)
-
- def _endPath(self):
- p0 = self._getCurrentPoint()
- if p0 != self.__startPoint:
- # Green theorem is not defined on open contours.
- raise NotImplementedError
-
- def _lineTo(self, p1):
- p0 = self._getCurrentPoint()
- self.value += self._funcs[1](p0, p1)
-
- def _qCurveToOne(self, p1, p2):
- p0 = self._getCurrentPoint()
- self.value += self._funcs[2](p0, p1, p2)
-
- def _curveToOne(self, p1, p2, p3):
- p0 = self._getCurrentPoint()
- self.value += self._funcs[3](p0, p1, p2, p3)
-
-
-# Sample pens.
-# Do not use this in real code.
-# Use fontTools.pens.momentsPen.MomentsPen instead.
-AreaPen = partial(GreenPen, func=1)
-MomentXPen = partial(GreenPen, func=x)
-MomentYPen = partial(GreenPen, func=y)
-MomentXXPen = partial(GreenPen, func=x * x)
-MomentYYPen = partial(GreenPen, func=y * y)
-MomentXYPen = partial(GreenPen, func=x * y)
-
-
-def printGreenPen(penName, funcs, file=sys.stdout, docstring=None):
- if docstring is not None:
- print('"""%s"""' % docstring)
-
- print(
- """from fontTools.pens.basePen import BasePen, OpenContourError
-try:
- import cython
-
- COMPILED = cython.compiled
-except (AttributeError, ImportError):
- # if cython not installed, use mock module with no-op decorators and types
- from fontTools.misc import cython
-
- COMPILED = False
-
-
-__all__ = ["%s"]
-
-class %s(BasePen):
-
- def __init__(self, glyphset=None):
- BasePen.__init__(self, glyphset)
-"""
- % (penName, penName),
- file=file,
- )
- for name, f in funcs:
- print(" self.%s = 0" % name, file=file)
- print(
- """
- def _moveTo(self, p0):
- self.__startPoint = p0
-
- def _closePath(self):
- p0 = self._getCurrentPoint()
- if p0 != self.__startPoint:
- self._lineTo(self.__startPoint)
-
- def _endPath(self):
- p0 = self._getCurrentPoint()
- if p0 != self.__startPoint:
- # Green theorem is not defined on open contours.
- raise OpenContourError(
- "Green theorem is not defined on open contours."
- )
-""",
- end="",
- file=file,
- )
-
- for n in (1, 2, 3):
- subs = {P[i][j]: [X, Y][j][i] for i in range(n + 1) for j in range(2)}
- greens = [green(f, BezierCurve[n]) for name, f in funcs]
- greens = [sp.gcd_terms(f.collect(sum(P, ()))) for f in greens] # Optimize
- greens = [f.subs(subs) for f in greens] # Convert to p to x/y
- defs, exprs = sp.cse(
- greens,
- optimizations="basic",
- symbols=(sp.Symbol("r%d" % i) for i in count()),
- )
-
- print()
- for name, value in defs:
- print(" @cython.locals(%s=cython.double)" % name, file=file)
- if n == 1:
- print(
- """\
- @cython.locals(x0=cython.double, y0=cython.double)
- @cython.locals(x1=cython.double, y1=cython.double)
- def _lineTo(self, p1):
- x0,y0 = self._getCurrentPoint()
- x1,y1 = p1
-""",
- file=file,
- )
- elif n == 2:
- print(
- """\
- @cython.locals(x0=cython.double, y0=cython.double)
- @cython.locals(x1=cython.double, y1=cython.double)
- @cython.locals(x2=cython.double, y2=cython.double)
- def _qCurveToOne(self, p1, p2):
- x0,y0 = self._getCurrentPoint()
- x1,y1 = p1
- x2,y2 = p2
-""",
- file=file,
- )
- elif n == 3:
- print(
- """\
- @cython.locals(x0=cython.double, y0=cython.double)
- @cython.locals(x1=cython.double, y1=cython.double)
- @cython.locals(x2=cython.double, y2=cython.double)
- @cython.locals(x3=cython.double, y3=cython.double)
- def _curveToOne(self, p1, p2, p3):
- x0,y0 = self._getCurrentPoint()
- x1,y1 = p1
- x2,y2 = p2
- x3,y3 = p3
-""",
- file=file,
- )
- for name, value in defs:
- print(" %s = %s" % (name, value), file=file)
-
- print(file=file)
- for name, value in zip([f[0] for f in funcs], exprs):
- print(" self.%s += %s" % (name, value), file=file)
-
- print(
- """
-if __name__ == '__main__':
- from fontTools.misc.symfont import x, y, printGreenPen
- printGreenPen('%s', ["""
- % penName,
- file=file,
- )
- for name, f in funcs:
- print(" ('%s', %s)," % (name, str(f)), file=file)
- print(" ])", file=file)
-
-
-if __name__ == "__main__":
- pen = AreaPen()
- pen.moveTo((100, 100))
- pen.lineTo((100, 200))
- pen.lineTo((200, 200))
- pen.curveTo((200, 250), (300, 300), (250, 350))
- pen.lineTo((200, 100))
- pen.closePath()
- print(pen.value)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/testTools.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/testTools.py
deleted file mode 100644
index be61161..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/testTools.py
+++ /dev/null
@@ -1,229 +0,0 @@
-"""Helpers for writing unit tests."""
-
-from collections.abc import Iterable
-from io import BytesIO
-import os
-import re
-import shutil
-import sys
-import tempfile
-from unittest import TestCase as _TestCase
-from fontTools.config import Config
-from fontTools.misc.textTools import tobytes
-from fontTools.misc.xmlWriter import XMLWriter
-
-
-def parseXML(xmlSnippet):
- """Parses a snippet of XML.
-
- Input can be either a single string (unicode or UTF-8 bytes), or a
- a sequence of strings.
-
- The result is in the same format that would be returned by
- XMLReader, but the parser imposes no constraints on the root
- element so it can be called on small snippets of TTX files.
- """
- # To support snippets with multiple elements, we add a fake root.
- reader = TestXMLReader_()
- xml = b""
- if isinstance(xmlSnippet, bytes):
- xml += xmlSnippet
- elif isinstance(xmlSnippet, str):
- xml += tobytes(xmlSnippet, "utf-8")
- elif isinstance(xmlSnippet, Iterable):
- xml += b"".join(tobytes(s, "utf-8") for s in xmlSnippet)
- else:
- raise TypeError(
- "expected string or sequence of strings; found %r"
- % type(xmlSnippet).__name__
- )
- xml += b" "
- reader.parser.Parse(xml, 0)
- return reader.root[2]
-
-
-def parseXmlInto(font, parseInto, xmlSnippet):
- parsed_xml = [e for e in parseXML(xmlSnippet.strip()) if not isinstance(e, str)]
- for name, attrs, content in parsed_xml:
- parseInto.fromXML(name, attrs, content, font)
- parseInto.populateDefaults()
- return parseInto
-
-
-class FakeFont:
- def __init__(self, glyphs):
- self.glyphOrder_ = glyphs
- self.reverseGlyphOrderDict_ = {g: i for i, g in enumerate(glyphs)}
- self.lazy = False
- self.tables = {}
- self.cfg = Config()
-
- def __getitem__(self, tag):
- return self.tables[tag]
-
- def __setitem__(self, tag, table):
- self.tables[tag] = table
-
- def get(self, tag, default=None):
- return self.tables.get(tag, default)
-
- def getGlyphID(self, name):
- return self.reverseGlyphOrderDict_[name]
-
- def getGlyphIDMany(self, lst):
- return [self.getGlyphID(gid) for gid in lst]
-
- def getGlyphName(self, glyphID):
- if glyphID < len(self.glyphOrder_):
- return self.glyphOrder_[glyphID]
- else:
- return "glyph%.5d" % glyphID
-
- def getGlyphNameMany(self, lst):
- return [self.getGlyphName(gid) for gid in lst]
-
- def getGlyphOrder(self):
- return self.glyphOrder_
-
- def getReverseGlyphMap(self):
- return self.reverseGlyphOrderDict_
-
- def getGlyphNames(self):
- return sorted(self.getGlyphOrder())
-
-
-class TestXMLReader_(object):
- def __init__(self):
- from xml.parsers.expat import ParserCreate
-
- self.parser = ParserCreate()
- self.parser.StartElementHandler = self.startElement_
- self.parser.EndElementHandler = self.endElement_
- self.parser.CharacterDataHandler = self.addCharacterData_
- self.root = None
- self.stack = []
-
- def startElement_(self, name, attrs):
- element = (name, attrs, [])
- if self.stack:
- self.stack[-1][2].append(element)
- else:
- self.root = element
- self.stack.append(element)
-
- def endElement_(self, name):
- self.stack.pop()
-
- def addCharacterData_(self, data):
- self.stack[-1][2].append(data)
-
-
-def makeXMLWriter(newlinestr="\n"):
- # don't write OS-specific new lines
- writer = XMLWriter(BytesIO(), newlinestr=newlinestr)
- # erase XML declaration
- writer.file.seek(0)
- writer.file.truncate()
- return writer
-
-
-def getXML(func, ttFont=None):
- """Call the passed toXML function and return the written content as a
- list of lines (unicode strings).
- Result is stripped of XML declaration and OS-specific newline characters.
- """
- writer = makeXMLWriter()
- func(writer, ttFont)
- xml = writer.file.getvalue().decode("utf-8")
- # toXML methods must always end with a writer.newline()
- assert xml.endswith("\n")
- return xml.splitlines()
-
-
-def stripVariableItemsFromTTX(
- string: str,
- ttLibVersion: bool = True,
- checkSumAdjustment: bool = True,
- modified: bool = True,
- created: bool = True,
- sfntVersion: bool = False, # opt-in only
-) -> str:
- """Strip stuff like ttLibVersion, checksums, timestamps, etc. from TTX dumps."""
- # ttlib changes with the fontTools version
- if ttLibVersion:
- string = re.sub(' ttLibVersion="[^"]+"', "", string)
- # sometimes (e.g. some subsetter tests) we don't care whether it's OTF or TTF
- if sfntVersion:
- string = re.sub(' sfntVersion="[^"]+"', "", string)
- # head table checksum and creation and mod date changes with each save.
- if checkSumAdjustment:
- string = re.sub(' ', "", string)
- if modified:
- string = re.sub(' ', "", string)
- if created:
- string = re.sub(' ', "", string)
- return string
-
-
-class MockFont(object):
- """A font-like object that automatically adds any looked up glyphname
- to its glyphOrder."""
-
- def __init__(self):
- self._glyphOrder = [".notdef"]
-
- class AllocatingDict(dict):
- def __missing__(reverseDict, key):
- self._glyphOrder.append(key)
- gid = len(reverseDict)
- reverseDict[key] = gid
- return gid
-
- self._reverseGlyphOrder = AllocatingDict({".notdef": 0})
- self.lazy = False
-
- def getGlyphID(self, glyph):
- gid = self._reverseGlyphOrder[glyph]
- return gid
-
- def getReverseGlyphMap(self):
- return self._reverseGlyphOrder
-
- def getGlyphName(self, gid):
- return self._glyphOrder[gid]
-
- def getGlyphOrder(self):
- return self._glyphOrder
-
-
-class TestCase(_TestCase):
- def __init__(self, methodName):
- _TestCase.__init__(self, methodName)
- # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
- # and fires deprecation warnings if a program uses the old name.
- if not hasattr(self, "assertRaisesRegex"):
- self.assertRaisesRegex = self.assertRaisesRegexp
-
-
-class DataFilesHandler(TestCase):
- def setUp(self):
- self.tempdir = None
- self.num_tempfiles = 0
-
- def tearDown(self):
- if self.tempdir:
- shutil.rmtree(self.tempdir)
-
- def getpath(self, testfile):
- folder = os.path.dirname(sys.modules[self.__module__].__file__)
- return os.path.join(folder, "data", testfile)
-
- def temp_dir(self):
- if not self.tempdir:
- self.tempdir = tempfile.mkdtemp()
-
- def temp_font(self, font_path, file_name):
- self.temp_dir()
- temppath = os.path.join(self.tempdir, file_name)
- shutil.copy2(font_path, temppath)
- return temppath
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/textTools.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/textTools.py
deleted file mode 100644
index f7ca1ac..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/textTools.py
+++ /dev/null
@@ -1,155 +0,0 @@
-"""fontTools.misc.textTools.py -- miscellaneous routines."""
-
-
-import ast
-import string
-
-
-# alias kept for backward compatibility
-safeEval = ast.literal_eval
-
-
-class Tag(str):
- @staticmethod
- def transcode(blob):
- if isinstance(blob, bytes):
- blob = blob.decode("latin-1")
- return blob
-
- def __new__(self, content):
- return str.__new__(self, self.transcode(content))
-
- def __ne__(self, other):
- return not self.__eq__(other)
-
- def __eq__(self, other):
- return str.__eq__(self, self.transcode(other))
-
- def __hash__(self):
- return str.__hash__(self)
-
- def tobytes(self):
- return self.encode("latin-1")
-
-
-def readHex(content):
- """Convert a list of hex strings to binary data."""
- return deHexStr(strjoin(chunk for chunk in content if isinstance(chunk, str)))
-
-
-def deHexStr(hexdata):
- """Convert a hex string to binary data."""
- hexdata = strjoin(hexdata.split())
- if len(hexdata) % 2:
- hexdata = hexdata + "0"
- data = []
- for i in range(0, len(hexdata), 2):
- data.append(bytechr(int(hexdata[i : i + 2], 16)))
- return bytesjoin(data)
-
-
-def hexStr(data):
- """Convert binary data to a hex string."""
- h = string.hexdigits
- r = ""
- for c in data:
- i = byteord(c)
- r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
- return r
-
-
-def num2binary(l, bits=32):
- items = []
- binary = ""
- for i in range(bits):
- if l & 0x1:
- binary = "1" + binary
- else:
- binary = "0" + binary
- l = l >> 1
- if not ((i + 1) % 8):
- items.append(binary)
- binary = ""
- if binary:
- items.append(binary)
- items.reverse()
- assert l in (0, -1), "number doesn't fit in number of bits"
- return " ".join(items)
-
-
-def binary2num(bin):
- bin = strjoin(bin.split())
- l = 0
- for digit in bin:
- l = l << 1
- if digit != "0":
- l = l | 0x1
- return l
-
-
-def caselessSort(alist):
- """Return a sorted copy of a list. If there are only strings
- in the list, it will not consider case.
- """
-
- try:
- return sorted(alist, key=lambda a: (a.lower(), a))
- except TypeError:
- return sorted(alist)
-
-
-def pad(data, size):
- r"""Pad byte string 'data' with null bytes until its length is a
- multiple of 'size'.
-
- >>> len(pad(b'abcd', 4))
- 4
- >>> len(pad(b'abcde', 2))
- 6
- >>> len(pad(b'abcde', 4))
- 8
- >>> pad(b'abcdef', 4) == b'abcdef\x00\x00'
- True
- """
- data = tobytes(data)
- if size > 1:
- remainder = len(data) % size
- if remainder:
- data += b"\0" * (size - remainder)
- return data
-
-
-def tostr(s, encoding="ascii", errors="strict"):
- if not isinstance(s, str):
- return s.decode(encoding, errors)
- else:
- return s
-
-
-def tobytes(s, encoding="ascii", errors="strict"):
- if isinstance(s, str):
- return s.encode(encoding, errors)
- else:
- return bytes(s)
-
-
-def bytechr(n):
- return bytes([n])
-
-
-def byteord(c):
- return c if isinstance(c, int) else ord(c)
-
-
-def strjoin(iterable, joiner=""):
- return tostr(joiner).join(iterable)
-
-
-def bytesjoin(iterable, joiner=b""):
- return tobytes(joiner).join(tobytes(item) for item in iterable)
-
-
-if __name__ == "__main__":
- import doctest, sys
-
- sys.exit(doctest.testmod().failed)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/timeTools.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/timeTools.py
deleted file mode 100644
index 175ce81..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/timeTools.py
+++ /dev/null
@@ -1,88 +0,0 @@
-"""fontTools.misc.timeTools.py -- tools for working with OpenType timestamps.
-"""
-
-import os
-import time
-from datetime import datetime, timezone
-import calendar
-
-
-epoch_diff = calendar.timegm((1904, 1, 1, 0, 0, 0, 0, 0, 0))
-
-DAYNAMES = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
-MONTHNAMES = [
- None,
- "Jan",
- "Feb",
- "Mar",
- "Apr",
- "May",
- "Jun",
- "Jul",
- "Aug",
- "Sep",
- "Oct",
- "Nov",
- "Dec",
-]
-
-
-def asctime(t=None):
- """
- Convert a tuple or struct_time representing a time as returned by gmtime()
- or localtime() to a 24-character string of the following form:
-
- >>> asctime(time.gmtime(0))
- 'Thu Jan 1 00:00:00 1970'
-
- If t is not provided, the current time as returned by localtime() is used.
- Locale information is not used by asctime().
-
- This is meant to normalise the output of the built-in time.asctime() across
- different platforms and Python versions.
- In Python 3.x, the day of the month is right-justified, whereas on Windows
- Python 2.7 it is padded with zeros.
-
- See https://github.com/fonttools/fonttools/issues/455
- """
- if t is None:
- t = time.localtime()
- s = "%s %s %2s %s" % (
- DAYNAMES[t.tm_wday],
- MONTHNAMES[t.tm_mon],
- t.tm_mday,
- time.strftime("%H:%M:%S %Y", t),
- )
- return s
-
-
-def timestampToString(value):
- return asctime(time.gmtime(max(0, value + epoch_diff)))
-
-
-def timestampFromString(value):
- wkday, mnth = value[:7].split()
- t = datetime.strptime(value[7:], " %d %H:%M:%S %Y")
- t = t.replace(month=MONTHNAMES.index(mnth), tzinfo=timezone.utc)
- wkday_idx = DAYNAMES.index(wkday)
- assert t.weekday() == wkday_idx, '"' + value + '" has inconsistent weekday'
- return int(t.timestamp()) - epoch_diff
-
-
-def timestampNow():
- # https://reproducible-builds.org/specs/source-date-epoch/
- source_date_epoch = os.environ.get("SOURCE_DATE_EPOCH")
- if source_date_epoch is not None:
- return int(source_date_epoch) - epoch_diff
- return int(time.time() - epoch_diff)
-
-
-def timestampSinceEpoch(value):
- return int(value - epoch_diff)
-
-
-if __name__ == "__main__":
- import sys
- import doctest
-
- sys.exit(doctest.testmod().failed)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/transform.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/transform.py
deleted file mode 100644
index f85b54b..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/transform.py
+++ /dev/null
@@ -1,495 +0,0 @@
-"""Affine 2D transformation matrix class.
-
-The Transform class implements various transformation matrix operations,
-both on the matrix itself, as well as on 2D coordinates.
-
-Transform instances are effectively immutable: all methods that operate on the
-transformation itself always return a new instance. This has as the
-interesting side effect that Transform instances are hashable, ie. they can be
-used as dictionary keys.
-
-This module exports the following symbols:
-
-Transform
- this is the main class
-Identity
- Transform instance set to the identity transformation
-Offset
- Convenience function that returns a translating transformation
-Scale
- Convenience function that returns a scaling transformation
-
-The DecomposedTransform class implements a transformation with separate
-translate, rotation, scale, skew, and transformation-center components.
-
-:Example:
-
- >>> t = Transform(2, 0, 0, 3, 0, 0)
- >>> t.transformPoint((100, 100))
- (200, 300)
- >>> t = Scale(2, 3)
- >>> t.transformPoint((100, 100))
- (200, 300)
- >>> t.transformPoint((0, 0))
- (0, 0)
- >>> t = Offset(2, 3)
- >>> t.transformPoint((100, 100))
- (102, 103)
- >>> t.transformPoint((0, 0))
- (2, 3)
- >>> t2 = t.scale(0.5)
- >>> t2.transformPoint((100, 100))
- (52.0, 53.0)
- >>> import math
- >>> t3 = t2.rotate(math.pi / 2)
- >>> t3.transformPoint((0, 0))
- (2.0, 3.0)
- >>> t3.transformPoint((100, 100))
- (-48.0, 53.0)
- >>> t = Identity.scale(0.5).translate(100, 200).skew(0.1, 0.2)
- >>> t.transformPoints([(0, 0), (1, 1), (100, 100)])
- [(50.0, 100.0), (50.550167336042726, 100.60135501775433), (105.01673360427253, 160.13550177543362)]
- >>>
-"""
-
-import math
-from typing import NamedTuple
-from dataclasses import dataclass
-
-
-__all__ = ["Transform", "Identity", "Offset", "Scale", "DecomposedTransform"]
-
-
-_EPSILON = 1e-15
-_ONE_EPSILON = 1 - _EPSILON
-_MINUS_ONE_EPSILON = -1 + _EPSILON
-
-
-def _normSinCos(v):
- if abs(v) < _EPSILON:
- v = 0
- elif v > _ONE_EPSILON:
- v = 1
- elif v < _MINUS_ONE_EPSILON:
- v = -1
- return v
-
-
-class Transform(NamedTuple):
-
- """2x2 transformation matrix plus offset, a.k.a. Affine transform.
- Transform instances are immutable: all transforming methods, eg.
- rotate(), return a new Transform instance.
-
- :Example:
-
- >>> t = Transform()
- >>> t
-
- >>> t.scale(2)
-
- >>> t.scale(2.5, 5.5)
-
- >>>
- >>> t.scale(2, 3).transformPoint((100, 100))
- (200, 300)
-
- Transform's constructor takes six arguments, all of which are
- optional, and can be used as keyword arguments::
-
- >>> Transform(12)
-
- >>> Transform(dx=12)
-
- >>> Transform(yx=12)
-
-
- Transform instances also behave like sequences of length 6::
-
- >>> len(Identity)
- 6
- >>> list(Identity)
- [1, 0, 0, 1, 0, 0]
- >>> tuple(Identity)
- (1, 0, 0, 1, 0, 0)
-
- Transform instances are comparable::
-
- >>> t1 = Identity.scale(2, 3).translate(4, 6)
- >>> t2 = Identity.translate(8, 18).scale(2, 3)
- >>> t1 == t2
- 1
-
- But beware of floating point rounding errors::
-
- >>> t1 = Identity.scale(0.2, 0.3).translate(0.4, 0.6)
- >>> t2 = Identity.translate(0.08, 0.18).scale(0.2, 0.3)
- >>> t1
-
- >>> t2
-
- >>> t1 == t2
- 0
-
- Transform instances are hashable, meaning you can use them as
- keys in dictionaries::
-
- >>> d = {Scale(12, 13): None}
- >>> d
- {: None}
-
- But again, beware of floating point rounding errors::
-
- >>> t1 = Identity.scale(0.2, 0.3).translate(0.4, 0.6)
- >>> t2 = Identity.translate(0.08, 0.18).scale(0.2, 0.3)
- >>> t1
-
- >>> t2
-
- >>> d = {t1: None}
- >>> d
- {: None}
- >>> d[t2]
- Traceback (most recent call last):
- File "", line 1, in ?
- KeyError:
- """
-
- xx: float = 1
- xy: float = 0
- yx: float = 0
- yy: float = 1
- dx: float = 0
- dy: float = 0
-
- def transformPoint(self, p):
- """Transform a point.
-
- :Example:
-
- >>> t = Transform()
- >>> t = t.scale(2.5, 5.5)
- >>> t.transformPoint((100, 100))
- (250.0, 550.0)
- """
- (x, y) = p
- xx, xy, yx, yy, dx, dy = self
- return (xx * x + yx * y + dx, xy * x + yy * y + dy)
-
- def transformPoints(self, points):
- """Transform a list of points.
-
- :Example:
-
- >>> t = Scale(2, 3)
- >>> t.transformPoints([(0, 0), (0, 100), (100, 100), (100, 0)])
- [(0, 0), (0, 300), (200, 300), (200, 0)]
- >>>
- """
- xx, xy, yx, yy, dx, dy = self
- return [(xx * x + yx * y + dx, xy * x + yy * y + dy) for x, y in points]
-
- def transformVector(self, v):
- """Transform an (dx, dy) vector, treating translation as zero.
-
- :Example:
-
- >>> t = Transform(2, 0, 0, 2, 10, 20)
- >>> t.transformVector((3, -4))
- (6, -8)
- >>>
- """
- (dx, dy) = v
- xx, xy, yx, yy = self[:4]
- return (xx * dx + yx * dy, xy * dx + yy * dy)
-
- def transformVectors(self, vectors):
- """Transform a list of (dx, dy) vector, treating translation as zero.
-
- :Example:
- >>> t = Transform(2, 0, 0, 2, 10, 20)
- >>> t.transformVectors([(3, -4), (5, -6)])
- [(6, -8), (10, -12)]
- >>>
- """
- xx, xy, yx, yy = self[:4]
- return [(xx * dx + yx * dy, xy * dx + yy * dy) for dx, dy in vectors]
-
- def translate(self, x=0, y=0):
- """Return a new transformation, translated (offset) by x, y.
-
- :Example:
- >>> t = Transform()
- >>> t.translate(20, 30)
-
- >>>
- """
- return self.transform((1, 0, 0, 1, x, y))
-
- def scale(self, x=1, y=None):
- """Return a new transformation, scaled by x, y. The 'y' argument
- may be None, which implies to use the x value for y as well.
-
- :Example:
- >>> t = Transform()
- >>> t.scale(5)
-
- >>> t.scale(5, 6)
-
- >>>
- """
- if y is None:
- y = x
- return self.transform((x, 0, 0, y, 0, 0))
-
- def rotate(self, angle):
- """Return a new transformation, rotated by 'angle' (radians).
-
- :Example:
- >>> import math
- >>> t = Transform()
- >>> t.rotate(math.pi / 2)
-
- >>>
- """
- import math
-
- c = _normSinCos(math.cos(angle))
- s = _normSinCos(math.sin(angle))
- return self.transform((c, s, -s, c, 0, 0))
-
- def skew(self, x=0, y=0):
- """Return a new transformation, skewed by x and y.
-
- :Example:
- >>> import math
- >>> t = Transform()
- >>> t.skew(math.pi / 4)
-
- >>>
- """
- import math
-
- return self.transform((1, math.tan(y), math.tan(x), 1, 0, 0))
-
- def transform(self, other):
- """Return a new transformation, transformed by another
- transformation.
-
- :Example:
- >>> t = Transform(2, 0, 0, 3, 1, 6)
- >>> t.transform((4, 3, 2, 1, 5, 6))
-
- >>>
- """
- xx1, xy1, yx1, yy1, dx1, dy1 = other
- xx2, xy2, yx2, yy2, dx2, dy2 = self
- return self.__class__(
- xx1 * xx2 + xy1 * yx2,
- xx1 * xy2 + xy1 * yy2,
- yx1 * xx2 + yy1 * yx2,
- yx1 * xy2 + yy1 * yy2,
- xx2 * dx1 + yx2 * dy1 + dx2,
- xy2 * dx1 + yy2 * dy1 + dy2,
- )
-
- def reverseTransform(self, other):
- """Return a new transformation, which is the other transformation
- transformed by self. self.reverseTransform(other) is equivalent to
- other.transform(self).
-
- :Example:
- >>> t = Transform(2, 0, 0, 3, 1, 6)
- >>> t.reverseTransform((4, 3, 2, 1, 5, 6))
-
- >>> Transform(4, 3, 2, 1, 5, 6).transform((2, 0, 0, 3, 1, 6))
-
- >>>
- """
- xx1, xy1, yx1, yy1, dx1, dy1 = self
- xx2, xy2, yx2, yy2, dx2, dy2 = other
- return self.__class__(
- xx1 * xx2 + xy1 * yx2,
- xx1 * xy2 + xy1 * yy2,
- yx1 * xx2 + yy1 * yx2,
- yx1 * xy2 + yy1 * yy2,
- xx2 * dx1 + yx2 * dy1 + dx2,
- xy2 * dx1 + yy2 * dy1 + dy2,
- )
-
- def inverse(self):
- """Return the inverse transformation.
-
- :Example:
- >>> t = Identity.translate(2, 3).scale(4, 5)
- >>> t.transformPoint((10, 20))
- (42, 103)
- >>> it = t.inverse()
- >>> it.transformPoint((42, 103))
- (10.0, 20.0)
- >>>
- """
- if self == Identity:
- return self
- xx, xy, yx, yy, dx, dy = self
- det = xx * yy - yx * xy
- xx, xy, yx, yy = yy / det, -xy / det, -yx / det, xx / det
- dx, dy = -xx * dx - yx * dy, -xy * dx - yy * dy
- return self.__class__(xx, xy, yx, yy, dx, dy)
-
- def toPS(self):
- """Return a PostScript representation
-
- :Example:
-
- >>> t = Identity.scale(2, 3).translate(4, 5)
- >>> t.toPS()
- '[2 0 0 3 8 15]'
- >>>
- """
- return "[%s %s %s %s %s %s]" % self
-
- def toDecomposed(self) -> "DecomposedTransform":
- """Decompose into a DecomposedTransform."""
- return DecomposedTransform.fromTransform(self)
-
- def __bool__(self):
- """Returns True if transform is not identity, False otherwise.
-
- :Example:
-
- >>> bool(Identity)
- False
- >>> bool(Transform())
- False
- >>> bool(Scale(1.))
- False
- >>> bool(Scale(2))
- True
- >>> bool(Offset())
- False
- >>> bool(Offset(0))
- False
- >>> bool(Offset(2))
- True
- """
- return self != Identity
-
- def __repr__(self):
- return "<%s [%g %g %g %g %g %g]>" % ((self.__class__.__name__,) + self)
-
-
-Identity = Transform()
-
-
-def Offset(x=0, y=0):
- """Return the identity transformation offset by x, y.
-
- :Example:
- >>> Offset(2, 3)
-
- >>>
- """
- return Transform(1, 0, 0, 1, x, y)
-
-
-def Scale(x, y=None):
- """Return the identity transformation scaled by x, y. The 'y' argument
- may be None, which implies to use the x value for y as well.
-
- :Example:
- >>> Scale(2, 3)
-
- >>>
- """
- if y is None:
- y = x
- return Transform(x, 0, 0, y, 0, 0)
-
-
-@dataclass
-class DecomposedTransform:
- """The DecomposedTransform class implements a transformation with separate
- translate, rotation, scale, skew, and transformation-center components.
- """
-
- translateX: float = 0
- translateY: float = 0
- rotation: float = 0 # in degrees, counter-clockwise
- scaleX: float = 1
- scaleY: float = 1
- skewX: float = 0 # in degrees, clockwise
- skewY: float = 0 # in degrees, counter-clockwise
- tCenterX: float = 0
- tCenterY: float = 0
-
- @classmethod
- def fromTransform(self, transform):
- # Adapted from an answer on
- # https://math.stackexchange.com/questions/13150/extracting-rotation-scale-values-from-2d-transformation-matrix
- a, b, c, d, x, y = transform
-
- sx = math.copysign(1, a)
- if sx < 0:
- a *= sx
- b *= sx
-
- delta = a * d - b * c
-
- rotation = 0
- scaleX = scaleY = 0
- skewX = skewY = 0
-
- # Apply the QR-like decomposition.
- if a != 0 or b != 0:
- r = math.sqrt(a * a + b * b)
- rotation = math.acos(a / r) if b >= 0 else -math.acos(a / r)
- scaleX, scaleY = (r, delta / r)
- skewX, skewY = (math.atan((a * c + b * d) / (r * r)), 0)
- elif c != 0 or d != 0:
- s = math.sqrt(c * c + d * d)
- rotation = math.pi / 2 - (
- math.acos(-c / s) if d >= 0 else -math.acos(c / s)
- )
- scaleX, scaleY = (delta / s, s)
- skewX, skewY = (0, math.atan((a * c + b * d) / (s * s)))
- else:
- # a = b = c = d = 0
- pass
-
- return DecomposedTransform(
- x,
- y,
- math.degrees(rotation),
- scaleX * sx,
- scaleY,
- math.degrees(skewX) * sx,
- math.degrees(skewY),
- 0,
- 0,
- )
-
- def toTransform(self):
- """Return the Transform() equivalent of this transformation.
-
- :Example:
- >>> DecomposedTransform(scaleX=2, scaleY=2).toTransform()
-
- >>>
- """
- t = Transform()
- t = t.translate(
- self.translateX + self.tCenterX, self.translateY + self.tCenterY
- )
- t = t.rotate(math.radians(self.rotation))
- t = t.scale(self.scaleX, self.scaleY)
- t = t.skew(math.radians(self.skewX), math.radians(self.skewY))
- t = t.translate(-self.tCenterX, -self.tCenterY)
- return t
-
-
-if __name__ == "__main__":
- import sys
- import doctest
-
- sys.exit(doctest.testmod().failed)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/treeTools.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/treeTools.py
deleted file mode 100644
index 24e10ba..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/treeTools.py
+++ /dev/null
@@ -1,45 +0,0 @@
-"""Generic tools for working with trees."""
-
-from math import ceil, log
-
-
-def build_n_ary_tree(leaves, n):
- """Build N-ary tree from sequence of leaf nodes.
-
- Return a list of lists where each non-leaf node is a list containing
- max n nodes.
- """
- if not leaves:
- return []
-
- assert n > 1
-
- depth = ceil(log(len(leaves), n))
-
- if depth <= 1:
- return list(leaves)
-
- # Fully populate complete subtrees of root until we have enough leaves left
- root = []
- unassigned = None
- full_step = n ** (depth - 1)
- for i in range(0, len(leaves), full_step):
- subtree = leaves[i : i + full_step]
- if len(subtree) < full_step:
- unassigned = subtree
- break
- while len(subtree) > n:
- subtree = [subtree[k : k + n] for k in range(0, len(subtree), n)]
- root.append(subtree)
-
- if unassigned:
- # Recurse to fill the last subtree, which is the only partially populated one
- subtree = build_n_ary_tree(unassigned, n)
- if len(subtree) <= n - len(root):
- # replace last subtree with its children if they can still fit
- root.extend(subtree)
- else:
- root.append(subtree)
- assert len(root) <= n
-
- return root
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/vector.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/vector.py
deleted file mode 100644
index 666ff15..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/vector.py
+++ /dev/null
@@ -1,148 +0,0 @@
-from numbers import Number
-import math
-import operator
-import warnings
-
-
-__all__ = ["Vector"]
-
-
-class Vector(tuple):
-
- """A math-like vector.
-
- Represents an n-dimensional numeric vector. ``Vector`` objects support
- vector addition and subtraction, scalar multiplication and division,
- negation, rounding, and comparison tests.
- """
-
- __slots__ = ()
-
- def __new__(cls, values, keep=False):
- if keep is not False:
- warnings.warn(
- "the 'keep' argument has been deprecated",
- DeprecationWarning,
- )
- if type(values) == Vector:
- # No need to create a new object
- return values
- return super().__new__(cls, values)
-
- def __repr__(self):
- return f"{self.__class__.__name__}({super().__repr__()})"
-
- def _vectorOp(self, other, op):
- if isinstance(other, Vector):
- assert len(self) == len(other)
- return self.__class__(op(a, b) for a, b in zip(self, other))
- if isinstance(other, Number):
- return self.__class__(op(v, other) for v in self)
- raise NotImplementedError()
-
- def _scalarOp(self, other, op):
- if isinstance(other, Number):
- return self.__class__(op(v, other) for v in self)
- raise NotImplementedError()
-
- def _unaryOp(self, op):
- return self.__class__(op(v) for v in self)
-
- def __add__(self, other):
- return self._vectorOp(other, operator.add)
-
- __radd__ = __add__
-
- def __sub__(self, other):
- return self._vectorOp(other, operator.sub)
-
- def __rsub__(self, other):
- return self._vectorOp(other, _operator_rsub)
-
- def __mul__(self, other):
- return self._scalarOp(other, operator.mul)
-
- __rmul__ = __mul__
-
- def __truediv__(self, other):
- return self._scalarOp(other, operator.truediv)
-
- def __rtruediv__(self, other):
- return self._scalarOp(other, _operator_rtruediv)
-
- def __pos__(self):
- return self._unaryOp(operator.pos)
-
- def __neg__(self):
- return self._unaryOp(operator.neg)
-
- def __round__(self, *, round=round):
- return self._unaryOp(round)
-
- def __eq__(self, other):
- if isinstance(other, list):
- # bw compat Vector([1, 2, 3]) == [1, 2, 3]
- other = tuple(other)
- return super().__eq__(other)
-
- def __ne__(self, other):
- return not self.__eq__(other)
-
- def __bool__(self):
- return any(self)
-
- __nonzero__ = __bool__
-
- def __abs__(self):
- return math.sqrt(sum(x * x for x in self))
-
- def length(self):
- """Return the length of the vector. Equivalent to abs(vector)."""
- return abs(self)
-
- def normalized(self):
- """Return the normalized vector of the vector."""
- return self / abs(self)
-
- def dot(self, other):
- """Performs vector dot product, returning the sum of
- ``a[0] * b[0], a[1] * b[1], ...``"""
- assert len(self) == len(other)
- return sum(a * b for a, b in zip(self, other))
-
- # Deprecated methods/properties
-
- def toInt(self):
- warnings.warn(
- "the 'toInt' method has been deprecated, use round(vector) instead",
- DeprecationWarning,
- )
- return self.__round__()
-
- @property
- def values(self):
- warnings.warn(
- "the 'values' attribute has been deprecated, use "
- "the vector object itself instead",
- DeprecationWarning,
- )
- return list(self)
-
- @values.setter
- def values(self, values):
- raise AttributeError(
- "can't set attribute, the 'values' attribute has been deprecated",
- )
-
- def isclose(self, other: "Vector", **kwargs) -> bool:
- """Return True if the vector is close to another Vector."""
- assert len(self) == len(other)
- return all(math.isclose(a, b, **kwargs) for a, b in zip(self, other))
-
-
-def _operator_rsub(a, b):
- return operator.sub(b, a)
-
-
-def _operator_rtruediv(a, b):
- return operator.truediv(b, a)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/visitor.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/visitor.py
deleted file mode 100644
index d289895..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/visitor.py
+++ /dev/null
@@ -1,141 +0,0 @@
-"""Generic visitor pattern implementation for Python objects."""
-
-import enum
-
-
-class Visitor(object):
- defaultStop = False
-
- @classmethod
- def _register(celf, clazzes_attrs):
- assert celf != Visitor, "Subclass Visitor instead."
- if "_visitors" not in celf.__dict__:
- celf._visitors = {}
-
- def wrapper(method):
- assert method.__name__ == "visit"
- for clazzes, attrs in clazzes_attrs:
- if type(clazzes) != tuple:
- clazzes = (clazzes,)
- if type(attrs) == str:
- attrs = (attrs,)
- for clazz in clazzes:
- _visitors = celf._visitors.setdefault(clazz, {})
- for attr in attrs:
- assert attr not in _visitors, (
- "Oops, class '%s' has visitor function for '%s' defined already."
- % (clazz.__name__, attr)
- )
- _visitors[attr] = method
- return None
-
- return wrapper
-
- @classmethod
- def register(celf, clazzes):
- if type(clazzes) != tuple:
- clazzes = (clazzes,)
- return celf._register([(clazzes, (None,))])
-
- @classmethod
- def register_attr(celf, clazzes, attrs):
- clazzes_attrs = []
- if type(clazzes) != tuple:
- clazzes = (clazzes,)
- if type(attrs) == str:
- attrs = (attrs,)
- for clazz in clazzes:
- clazzes_attrs.append((clazz, attrs))
- return celf._register(clazzes_attrs)
-
- @classmethod
- def register_attrs(celf, clazzes_attrs):
- return celf._register(clazzes_attrs)
-
- @classmethod
- def _visitorsFor(celf, thing, _default={}):
- typ = type(thing)
-
- for celf in celf.mro():
- _visitors = getattr(celf, "_visitors", None)
- if _visitors is None:
- break
-
- m = celf._visitors.get(typ, None)
- if m is not None:
- return m
-
- return _default
-
- def visitObject(self, obj, *args, **kwargs):
- """Called to visit an object. This function loops over all non-private
- attributes of the objects and calls any user-registered (via
- @register_attr() or @register_attrs()) visit() functions.
-
- If there is no user-registered visit function, of if there is and it
- returns True, or it returns None (or doesn't return anything) and
- visitor.defaultStop is False (default), then the visitor will proceed
- to call self.visitAttr()"""
-
- keys = sorted(vars(obj).keys())
- _visitors = self._visitorsFor(obj)
- defaultVisitor = _visitors.get("*", None)
- for key in keys:
- if key[0] == "_":
- continue
- value = getattr(obj, key)
- visitorFunc = _visitors.get(key, defaultVisitor)
- if visitorFunc is not None:
- ret = visitorFunc(self, obj, key, value, *args, **kwargs)
- if ret == False or (ret is None and self.defaultStop):
- continue
- self.visitAttr(obj, key, value, *args, **kwargs)
-
- def visitAttr(self, obj, attr, value, *args, **kwargs):
- """Called to visit an attribute of an object."""
- self.visit(value, *args, **kwargs)
-
- def visitList(self, obj, *args, **kwargs):
- """Called to visit any value that is a list."""
- for value in obj:
- self.visit(value, *args, **kwargs)
-
- def visitDict(self, obj, *args, **kwargs):
- """Called to visit any value that is a dictionary."""
- for value in obj.values():
- self.visit(value, *args, **kwargs)
-
- def visitLeaf(self, obj, *args, **kwargs):
- """Called to visit any value that is not an object, list,
- or dictionary."""
- pass
-
- def visit(self, obj, *args, **kwargs):
- """This is the main entry to the visitor. The visitor will visit object
- obj.
-
- The visitor will first determine if there is a registered (via
- @register()) visit function for the type of object. If there is, it
- will be called, and (visitor, obj, *args, **kwargs) will be passed to
- the user visit function.
-
- If there is no user-registered visit function, of if there is and it
- returns True, or it returns None (or doesn't return anything) and
- visitor.defaultStop is False (default), then the visitor will proceed
- to dispatch to one of self.visitObject(), self.visitList(),
- self.visitDict(), or self.visitLeaf() (any of which can be overriden in
- a subclass)."""
-
- visitorFunc = self._visitorsFor(obj).get(None, None)
- if visitorFunc is not None:
- ret = visitorFunc(self, obj, *args, **kwargs)
- if ret == False or (ret is None and self.defaultStop):
- return
- if hasattr(obj, "__dict__") and not isinstance(obj, enum.Enum):
- self.visitObject(obj, *args, **kwargs)
- elif isinstance(obj, list):
- self.visitList(obj, *args, **kwargs)
- elif isinstance(obj, dict):
- self.visitDict(obj, *args, **kwargs)
- else:
- self.visitLeaf(obj, *args, **kwargs)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/xmlReader.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/xmlReader.py
deleted file mode 100644
index d8e502f..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/xmlReader.py
+++ /dev/null
@@ -1,188 +0,0 @@
-from fontTools import ttLib
-from fontTools.misc.textTools import safeEval
-from fontTools.ttLib.tables.DefaultTable import DefaultTable
-import sys
-import os
-import logging
-
-
-log = logging.getLogger(__name__)
-
-
-class TTXParseError(Exception):
- pass
-
-
-BUFSIZE = 0x4000
-
-
-class XMLReader(object):
- def __init__(
- self, fileOrPath, ttFont, progress=None, quiet=None, contentOnly=False
- ):
- if fileOrPath == "-":
- fileOrPath = sys.stdin
- if not hasattr(fileOrPath, "read"):
- self.file = open(fileOrPath, "rb")
- self._closeStream = True
- else:
- # assume readable file object
- self.file = fileOrPath
- self._closeStream = False
- self.ttFont = ttFont
- self.progress = progress
- if quiet is not None:
- from fontTools.misc.loggingTools import deprecateArgument
-
- deprecateArgument("quiet", "configure logging instead")
- self.quiet = quiet
- self.root = None
- self.contentStack = []
- self.contentOnly = contentOnly
- self.stackSize = 0
-
- def read(self, rootless=False):
- if rootless:
- self.stackSize += 1
- if self.progress:
- self.file.seek(0, 2)
- fileSize = self.file.tell()
- self.progress.set(0, fileSize // 100 or 1)
- self.file.seek(0)
- self._parseFile(self.file)
- if self._closeStream:
- self.close()
- if rootless:
- self.stackSize -= 1
-
- def close(self):
- self.file.close()
-
- def _parseFile(self, file):
- from xml.parsers.expat import ParserCreate
-
- parser = ParserCreate()
- parser.StartElementHandler = self._startElementHandler
- parser.EndElementHandler = self._endElementHandler
- parser.CharacterDataHandler = self._characterDataHandler
-
- pos = 0
- while True:
- chunk = file.read(BUFSIZE)
- if not chunk:
- parser.Parse(chunk, 1)
- break
- pos = pos + len(chunk)
- if self.progress:
- self.progress.set(pos // 100)
- parser.Parse(chunk, 0)
-
- def _startElementHandler(self, name, attrs):
- if self.stackSize == 1 and self.contentOnly:
- # We already know the table we're parsing, skip
- # parsing the table tag and continue to
- # stack '2' which begins parsing content
- self.contentStack.append([])
- self.stackSize = 2
- return
- stackSize = self.stackSize
- self.stackSize = stackSize + 1
- subFile = attrs.get("src")
- if subFile is not None:
- if hasattr(self.file, "name"):
- # if file has a name, get its parent directory
- dirname = os.path.dirname(self.file.name)
- else:
- # else fall back to using the current working directory
- dirname = os.getcwd()
- subFile = os.path.join(dirname, subFile)
- if not stackSize:
- if name != "ttFont":
- raise TTXParseError("illegal root tag: %s" % name)
- if self.ttFont.reader is None and not self.ttFont.tables:
- sfntVersion = attrs.get("sfntVersion")
- if sfntVersion is not None:
- if len(sfntVersion) != 4:
- sfntVersion = safeEval('"' + sfntVersion + '"')
- self.ttFont.sfntVersion = sfntVersion
- self.contentStack.append([])
- elif stackSize == 1:
- if subFile is not None:
- subReader = XMLReader(subFile, self.ttFont, self.progress)
- subReader.read()
- self.contentStack.append([])
- return
- tag = ttLib.xmlToTag(name)
- msg = "Parsing '%s' table..." % tag
- if self.progress:
- self.progress.setLabel(msg)
- log.info(msg)
- if tag == "GlyphOrder":
- tableClass = ttLib.GlyphOrder
- elif "ERROR" in attrs or ("raw" in attrs and safeEval(attrs["raw"])):
- tableClass = DefaultTable
- else:
- tableClass = ttLib.getTableClass(tag)
- if tableClass is None:
- tableClass = DefaultTable
- if tag == "loca" and tag in self.ttFont:
- # Special-case the 'loca' table as we need the
- # original if the 'glyf' table isn't recompiled.
- self.currentTable = self.ttFont[tag]
- else:
- self.currentTable = tableClass(tag)
- self.ttFont[tag] = self.currentTable
- self.contentStack.append([])
- elif stackSize == 2 and subFile is not None:
- subReader = XMLReader(subFile, self.ttFont, self.progress, contentOnly=True)
- subReader.read()
- self.contentStack.append([])
- self.root = subReader.root
- elif stackSize == 2:
- self.contentStack.append([])
- self.root = (name, attrs, self.contentStack[-1])
- else:
- l = []
- self.contentStack[-1].append((name, attrs, l))
- self.contentStack.append(l)
-
- def _characterDataHandler(self, data):
- if self.stackSize > 1:
- # parser parses in chunks, so we may get multiple calls
- # for the same text node; thus we need to append the data
- # to the last item in the content stack:
- # https://github.com/fonttools/fonttools/issues/2614
- if (
- data != "\n"
- and self.contentStack[-1]
- and isinstance(self.contentStack[-1][-1], str)
- and self.contentStack[-1][-1] != "\n"
- ):
- self.contentStack[-1][-1] += data
- else:
- self.contentStack[-1].append(data)
-
- def _endElementHandler(self, name):
- self.stackSize = self.stackSize - 1
- del self.contentStack[-1]
- if not self.contentOnly:
- if self.stackSize == 1:
- self.root = None
- elif self.stackSize == 2:
- name, attrs, content = self.root
- self.currentTable.fromXML(name, attrs, content, self.ttFont)
- self.root = None
-
-
-class ProgressPrinter(object):
- def __init__(self, title, maxval=100):
- print(title)
-
- def set(self, val, maxval=None):
- pass
-
- def increment(self, val=1):
- pass
-
- def setLabel(self, text):
- print(text)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/xmlWriter.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/xmlWriter.py
deleted file mode 100644
index 9a8dc3e..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/misc/xmlWriter.py
+++ /dev/null
@@ -1,204 +0,0 @@
-"""xmlWriter.py -- Simple XML authoring class"""
-
-from fontTools.misc.textTools import byteord, strjoin, tobytes, tostr
-import sys
-import os
-import string
-
-INDENT = " "
-
-
-class XMLWriter(object):
- def __init__(
- self,
- fileOrPath,
- indentwhite=INDENT,
- idlefunc=None,
- encoding="utf_8",
- newlinestr="\n",
- ):
- if encoding.lower().replace("-", "").replace("_", "") != "utf8":
- raise Exception("Only UTF-8 encoding is supported.")
- if fileOrPath == "-":
- fileOrPath = sys.stdout
- if not hasattr(fileOrPath, "write"):
- self.filename = fileOrPath
- self.file = open(fileOrPath, "wb")
- self._closeStream = True
- else:
- self.filename = None
- # assume writable file object
- self.file = fileOrPath
- self._closeStream = False
-
- # Figure out if writer expects bytes or unicodes
- try:
- # The bytes check should be first. See:
- # https://github.com/fonttools/fonttools/pull/233
- self.file.write(b"")
- self.totype = tobytes
- except TypeError:
- # This better not fail.
- self.file.write("")
- self.totype = tostr
- self.indentwhite = self.totype(indentwhite)
- if newlinestr is None:
- self.newlinestr = self.totype(os.linesep)
- else:
- self.newlinestr = self.totype(newlinestr)
- self.indentlevel = 0
- self.stack = []
- self.needindent = 1
- self.idlefunc = idlefunc
- self.idlecounter = 0
- self._writeraw('')
- self.newline()
-
- def __enter__(self):
- return self
-
- def __exit__(self, exception_type, exception_value, traceback):
- self.close()
-
- def close(self):
- if self._closeStream:
- self.file.close()
-
- def write(self, string, indent=True):
- """Writes text."""
- self._writeraw(escape(string), indent=indent)
-
- def writecdata(self, string):
- """Writes text in a CDATA section."""
- self._writeraw("")
-
- def write8bit(self, data, strip=False):
- """Writes a bytes() sequence into the XML, escaping
- non-ASCII bytes. When this is read in xmlReader,
- the original bytes can be recovered by encoding to
- 'latin-1'."""
- self._writeraw(escape8bit(data), strip=strip)
-
- def write_noindent(self, string):
- """Writes text without indentation."""
- self._writeraw(escape(string), indent=False)
-
- def _writeraw(self, data, indent=True, strip=False):
- """Writes bytes, possibly indented."""
- if indent and self.needindent:
- self.file.write(self.indentlevel * self.indentwhite)
- self.needindent = 0
- s = self.totype(data, encoding="utf_8")
- if strip:
- s = s.strip()
- self.file.write(s)
-
- def newline(self):
- self.file.write(self.newlinestr)
- self.needindent = 1
- idlecounter = self.idlecounter
- if not idlecounter % 100 and self.idlefunc is not None:
- self.idlefunc()
- self.idlecounter = idlecounter + 1
-
- def comment(self, data):
- data = escape(data)
- lines = data.split("\n")
- self._writeraw("")
-
- def simpletag(self, _TAG_, *args, **kwargs):
- attrdata = self.stringifyattrs(*args, **kwargs)
- data = "<%s%s/>" % (_TAG_, attrdata)
- self._writeraw(data)
-
- def begintag(self, _TAG_, *args, **kwargs):
- attrdata = self.stringifyattrs(*args, **kwargs)
- data = "<%s%s>" % (_TAG_, attrdata)
- self._writeraw(data)
- self.stack.append(_TAG_)
- self.indent()
-
- def endtag(self, _TAG_):
- assert self.stack and self.stack[-1] == _TAG_, "nonmatching endtag"
- del self.stack[-1]
- self.dedent()
- data = "%s>" % _TAG_
- self._writeraw(data)
-
- def dumphex(self, data):
- linelength = 16
- hexlinelength = linelength * 2
- chunksize = 8
- for i in range(0, len(data), linelength):
- hexline = hexStr(data[i : i + linelength])
- line = ""
- white = ""
- for j in range(0, hexlinelength, chunksize):
- line = line + white + hexline[j : j + chunksize]
- white = " "
- self._writeraw(line)
- self.newline()
-
- def indent(self):
- self.indentlevel = self.indentlevel + 1
-
- def dedent(self):
- assert self.indentlevel > 0
- self.indentlevel = self.indentlevel - 1
-
- def stringifyattrs(self, *args, **kwargs):
- if kwargs:
- assert not args
- attributes = sorted(kwargs.items())
- elif args:
- assert len(args) == 1
- attributes = args[0]
- else:
- return ""
- data = ""
- for attr, value in attributes:
- if not isinstance(value, (bytes, str)):
- value = str(value)
- data = data + ' %s="%s"' % (attr, escapeattr(value))
- return data
-
-
-def escape(data):
- data = tostr(data, "utf_8")
- data = data.replace("&", "&")
- data = data.replace("<", "<")
- data = data.replace(">", ">")
- data = data.replace("\r", "
")
- return data
-
-
-def escapeattr(data):
- data = escape(data)
- data = data.replace('"', """)
- return data
-
-
-def escape8bit(data):
- """Input is Unicode string."""
-
- def escapechar(c):
- n = ord(c)
- if 32 <= n <= 127 and c not in "<&>":
- return c
- else:
- return "" + repr(n) + ";"
-
- return strjoin(map(escapechar, data.decode("latin-1")))
-
-
-def hexStr(s):
- h = string.hexdigits
- r = ""
- for c in s:
- i = byteord(c)
- r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
- return r
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/mtiLib/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/mtiLib/__init__.py
deleted file mode 100644
index 36d0649..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/mtiLib/__init__.py
+++ /dev/null
@@ -1,1410 +0,0 @@
-#!/usr/bin/python
-
-# FontDame-to-FontTools for OpenType Layout tables
-#
-# Source language spec is available at:
-# http://monotype.github.io/OpenType_Table_Source/otl_source.html
-# https://github.com/Monotype/OpenType_Table_Source/
-
-from fontTools import ttLib
-from fontTools.ttLib.tables._c_m_a_p import cmap_classes
-from fontTools.ttLib.tables import otTables as ot
-from fontTools.ttLib.tables.otBase import ValueRecord, valueRecordFormatDict
-from fontTools.otlLib import builder as otl
-from contextlib import contextmanager
-from fontTools.ttLib import newTable
-from fontTools.feaLib.lookupDebugInfo import LOOKUP_DEBUG_ENV_VAR, LOOKUP_DEBUG_INFO_KEY
-from operator import setitem
-import os
-import logging
-
-
-class MtiLibError(Exception):
- pass
-
-
-class ReferenceNotFoundError(MtiLibError):
- pass
-
-
-class FeatureNotFoundError(ReferenceNotFoundError):
- pass
-
-
-class LookupNotFoundError(ReferenceNotFoundError):
- pass
-
-
-log = logging.getLogger("fontTools.mtiLib")
-
-
-def makeGlyph(s):
- if s[:2] in ["U ", "u "]:
- return ttLib.TTFont._makeGlyphName(int(s[2:], 16))
- elif s[:2] == "# ":
- return "glyph%.5d" % int(s[2:])
- assert s.find(" ") < 0, "Space found in glyph name: %s" % s
- assert s, "Glyph name is empty"
- return s
-
-
-def makeGlyphs(l):
- return [makeGlyph(g) for g in l]
-
-
-def mapLookup(sym, mapping):
- # Lookups are addressed by name. So resolved them using a map if available.
- # Fallback to parsing as lookup index if a map isn't provided.
- if mapping is not None:
- try:
- idx = mapping[sym]
- except KeyError:
- raise LookupNotFoundError(sym)
- else:
- idx = int(sym)
- return idx
-
-
-def mapFeature(sym, mapping):
- # Features are referenced by index according the spec. So, if symbol is an
- # integer, use it directly. Otherwise look up in the map if provided.
- try:
- idx = int(sym)
- except ValueError:
- try:
- idx = mapping[sym]
- except KeyError:
- raise FeatureNotFoundError(sym)
- return idx
-
-
-def setReference(mapper, mapping, sym, setter, collection, key):
- try:
- mapped = mapper(sym, mapping)
- except ReferenceNotFoundError as e:
- try:
- if mapping is not None:
- mapping.addDeferredMapping(
- lambda ref: setter(collection, key, ref), sym, e
- )
- return
- except AttributeError:
- pass
- raise
- setter(collection, key, mapped)
-
-
-class DeferredMapping(dict):
- def __init__(self):
- self._deferredMappings = []
-
- def addDeferredMapping(self, setter, sym, e):
- log.debug("Adding deferred mapping for symbol '%s' %s", sym, type(e).__name__)
- self._deferredMappings.append((setter, sym, e))
-
- def applyDeferredMappings(self):
- for setter, sym, e in self._deferredMappings:
- log.debug(
- "Applying deferred mapping for symbol '%s' %s", sym, type(e).__name__
- )
- try:
- mapped = self[sym]
- except KeyError:
- raise e
- setter(mapped)
- log.debug("Set to %s", mapped)
- self._deferredMappings = []
-
-
-def parseScriptList(lines, featureMap=None):
- self = ot.ScriptList()
- records = []
- with lines.between("script table"):
- for line in lines:
- while len(line) < 4:
- line.append("")
- scriptTag, langSysTag, defaultFeature, features = line
- log.debug("Adding script %s language-system %s", scriptTag, langSysTag)
-
- langSys = ot.LangSys()
- langSys.LookupOrder = None
- if defaultFeature:
- setReference(
- mapFeature,
- featureMap,
- defaultFeature,
- setattr,
- langSys,
- "ReqFeatureIndex",
- )
- else:
- langSys.ReqFeatureIndex = 0xFFFF
- syms = stripSplitComma(features)
- langSys.FeatureIndex = theList = [3] * len(syms)
- for i, sym in enumerate(syms):
- setReference(mapFeature, featureMap, sym, setitem, theList, i)
- langSys.FeatureCount = len(langSys.FeatureIndex)
-
- script = [s for s in records if s.ScriptTag == scriptTag]
- if script:
- script = script[0].Script
- else:
- scriptRec = ot.ScriptRecord()
- scriptRec.ScriptTag = scriptTag + " " * (4 - len(scriptTag))
- scriptRec.Script = ot.Script()
- records.append(scriptRec)
- script = scriptRec.Script
- script.DefaultLangSys = None
- script.LangSysRecord = []
- script.LangSysCount = 0
-
- if langSysTag == "default":
- script.DefaultLangSys = langSys
- else:
- langSysRec = ot.LangSysRecord()
- langSysRec.LangSysTag = langSysTag + " " * (4 - len(langSysTag))
- langSysRec.LangSys = langSys
- script.LangSysRecord.append(langSysRec)
- script.LangSysCount = len(script.LangSysRecord)
-
- for script in records:
- script.Script.LangSysRecord = sorted(
- script.Script.LangSysRecord, key=lambda rec: rec.LangSysTag
- )
- self.ScriptRecord = sorted(records, key=lambda rec: rec.ScriptTag)
- self.ScriptCount = len(self.ScriptRecord)
- return self
-
-
-def parseFeatureList(lines, lookupMap=None, featureMap=None):
- self = ot.FeatureList()
- self.FeatureRecord = []
- with lines.between("feature table"):
- for line in lines:
- name, featureTag, lookups = line
- if featureMap is not None:
- assert name not in featureMap, "Duplicate feature name: %s" % name
- featureMap[name] = len(self.FeatureRecord)
- # If feature name is integer, make sure it matches its index.
- try:
- assert int(name) == len(self.FeatureRecord), "%d %d" % (
- name,
- len(self.FeatureRecord),
- )
- except ValueError:
- pass
- featureRec = ot.FeatureRecord()
- featureRec.FeatureTag = featureTag
- featureRec.Feature = ot.Feature()
- self.FeatureRecord.append(featureRec)
- feature = featureRec.Feature
- feature.FeatureParams = None
- syms = stripSplitComma(lookups)
- feature.LookupListIndex = theList = [None] * len(syms)
- for i, sym in enumerate(syms):
- setReference(mapLookup, lookupMap, sym, setitem, theList, i)
- feature.LookupCount = len(feature.LookupListIndex)
-
- self.FeatureCount = len(self.FeatureRecord)
- return self
-
-
-def parseLookupFlags(lines):
- flags = 0
- filterset = None
- allFlags = [
- "righttoleft",
- "ignorebaseglyphs",
- "ignoreligatures",
- "ignoremarks",
- "markattachmenttype",
- "markfiltertype",
- ]
- while lines.peeks()[0].lower() in allFlags:
- line = next(lines)
- flag = {
- "righttoleft": 0x0001,
- "ignorebaseglyphs": 0x0002,
- "ignoreligatures": 0x0004,
- "ignoremarks": 0x0008,
- }.get(line[0].lower())
- if flag:
- assert line[1].lower() in ["yes", "no"], line[1]
- if line[1].lower() == "yes":
- flags |= flag
- continue
- if line[0].lower() == "markattachmenttype":
- flags |= int(line[1]) << 8
- continue
- if line[0].lower() == "markfiltertype":
- flags |= 0x10
- filterset = int(line[1])
- return flags, filterset
-
-
-def parseSingleSubst(lines, font, _lookupMap=None):
- mapping = {}
- for line in lines:
- assert len(line) == 2, line
- line = makeGlyphs(line)
- mapping[line[0]] = line[1]
- return otl.buildSingleSubstSubtable(mapping)
-
-
-def parseMultiple(lines, font, _lookupMap=None):
- mapping = {}
- for line in lines:
- line = makeGlyphs(line)
- mapping[line[0]] = line[1:]
- return otl.buildMultipleSubstSubtable(mapping)
-
-
-def parseAlternate(lines, font, _lookupMap=None):
- mapping = {}
- for line in lines:
- line = makeGlyphs(line)
- mapping[line[0]] = line[1:]
- return otl.buildAlternateSubstSubtable(mapping)
-
-
-def parseLigature(lines, font, _lookupMap=None):
- mapping = {}
- for line in lines:
- assert len(line) >= 2, line
- line = makeGlyphs(line)
- mapping[tuple(line[1:])] = line[0]
- return otl.buildLigatureSubstSubtable(mapping)
-
-
-def parseSinglePos(lines, font, _lookupMap=None):
- values = {}
- for line in lines:
- assert len(line) == 3, line
- w = line[0].title().replace(" ", "")
- assert w in valueRecordFormatDict
- g = makeGlyph(line[1])
- v = int(line[2])
- if g not in values:
- values[g] = ValueRecord()
- assert not hasattr(values[g], w), (g, w)
- setattr(values[g], w, v)
- return otl.buildSinglePosSubtable(values, font.getReverseGlyphMap())
-
-
-def parsePair(lines, font, _lookupMap=None):
- self = ot.PairPos()
- self.ValueFormat1 = self.ValueFormat2 = 0
- typ = lines.peeks()[0].split()[0].lower()
- if typ in ("left", "right"):
- self.Format = 1
- values = {}
- for line in lines:
- assert len(line) == 4, line
- side = line[0].split()[0].lower()
- assert side in ("left", "right"), side
- what = line[0][len(side) :].title().replace(" ", "")
- mask = valueRecordFormatDict[what][0]
- glyph1, glyph2 = makeGlyphs(line[1:3])
- value = int(line[3])
- if not glyph1 in values:
- values[glyph1] = {}
- if not glyph2 in values[glyph1]:
- values[glyph1][glyph2] = (ValueRecord(), ValueRecord())
- rec2 = values[glyph1][glyph2]
- if side == "left":
- self.ValueFormat1 |= mask
- vr = rec2[0]
- else:
- self.ValueFormat2 |= mask
- vr = rec2[1]
- assert not hasattr(vr, what), (vr, what)
- setattr(vr, what, value)
- self.Coverage = makeCoverage(set(values.keys()), font)
- self.PairSet = []
- for glyph1 in self.Coverage.glyphs:
- values1 = values[glyph1]
- pairset = ot.PairSet()
- records = pairset.PairValueRecord = []
- for glyph2 in sorted(values1.keys(), key=font.getGlyphID):
- values2 = values1[glyph2]
- pair = ot.PairValueRecord()
- pair.SecondGlyph = glyph2
- pair.Value1 = values2[0]
- pair.Value2 = values2[1] if self.ValueFormat2 else None
- records.append(pair)
- pairset.PairValueCount = len(pairset.PairValueRecord)
- self.PairSet.append(pairset)
- self.PairSetCount = len(self.PairSet)
- elif typ.endswith("class"):
- self.Format = 2
- classDefs = [None, None]
- while lines.peeks()[0].endswith("class definition begin"):
- typ = lines.peek()[0][: -len("class definition begin")].lower()
- idx, klass = {
- "first": (0, ot.ClassDef1),
- "second": (1, ot.ClassDef2),
- }[typ]
- assert classDefs[idx] is None
- classDefs[idx] = parseClassDef(lines, font, klass=klass)
- self.ClassDef1, self.ClassDef2 = classDefs
- self.Class1Count, self.Class2Count = (
- 1 + max(c.classDefs.values()) for c in classDefs
- )
- self.Class1Record = [ot.Class1Record() for i in range(self.Class1Count)]
- for rec1 in self.Class1Record:
- rec1.Class2Record = [ot.Class2Record() for j in range(self.Class2Count)]
- for rec2 in rec1.Class2Record:
- rec2.Value1 = ValueRecord()
- rec2.Value2 = ValueRecord()
- for line in lines:
- assert len(line) == 4, line
- side = line[0].split()[0].lower()
- assert side in ("left", "right"), side
- what = line[0][len(side) :].title().replace(" ", "")
- mask = valueRecordFormatDict[what][0]
- class1, class2, value = (int(x) for x in line[1:4])
- rec2 = self.Class1Record[class1].Class2Record[class2]
- if side == "left":
- self.ValueFormat1 |= mask
- vr = rec2.Value1
- else:
- self.ValueFormat2 |= mask
- vr = rec2.Value2
- assert not hasattr(vr, what), (vr, what)
- setattr(vr, what, value)
- for rec1 in self.Class1Record:
- for rec2 in rec1.Class2Record:
- rec2.Value1 = ValueRecord(self.ValueFormat1, rec2.Value1)
- rec2.Value2 = (
- ValueRecord(self.ValueFormat2, rec2.Value2)
- if self.ValueFormat2
- else None
- )
-
- self.Coverage = makeCoverage(set(self.ClassDef1.classDefs.keys()), font)
- else:
- assert 0, typ
- return self
-
-
-def parseKernset(lines, font, _lookupMap=None):
- typ = lines.peeks()[0].split()[0].lower()
- if typ in ("left", "right"):
- with lines.until(
- ("firstclass definition begin", "secondclass definition begin")
- ):
- return parsePair(lines, font)
- return parsePair(lines, font)
-
-
-def makeAnchor(data, klass=ot.Anchor):
- assert len(data) <= 2
- anchor = klass()
- anchor.Format = 1
- anchor.XCoordinate, anchor.YCoordinate = intSplitComma(data[0])
- if len(data) > 1 and data[1] != "":
- anchor.Format = 2
- anchor.AnchorPoint = int(data[1])
- return anchor
-
-
-def parseCursive(lines, font, _lookupMap=None):
- records = {}
- for line in lines:
- assert len(line) in [3, 4], line
- idx, klass = {
- "entry": (0, ot.EntryAnchor),
- "exit": (1, ot.ExitAnchor),
- }[line[0]]
- glyph = makeGlyph(line[1])
- if glyph not in records:
- records[glyph] = [None, None]
- assert records[glyph][idx] is None, (glyph, idx)
- records[glyph][idx] = makeAnchor(line[2:], klass)
- return otl.buildCursivePosSubtable(records, font.getReverseGlyphMap())
-
-
-def makeMarkRecords(data, coverage, c):
- records = []
- for glyph in coverage.glyphs:
- klass, anchor = data[glyph]
- record = c.MarkRecordClass()
- record.Class = klass
- setattr(record, c.MarkAnchor, anchor)
- records.append(record)
- return records
-
-
-def makeBaseRecords(data, coverage, c, classCount):
- records = []
- idx = {}
- for glyph in coverage.glyphs:
- idx[glyph] = len(records)
- record = c.BaseRecordClass()
- anchors = [None] * classCount
- setattr(record, c.BaseAnchor, anchors)
- records.append(record)
- for (glyph, klass), anchor in data.items():
- record = records[idx[glyph]]
- anchors = getattr(record, c.BaseAnchor)
- assert anchors[klass] is None, (glyph, klass)
- anchors[klass] = anchor
- return records
-
-
-def makeLigatureRecords(data, coverage, c, classCount):
- records = [None] * len(coverage.glyphs)
- idx = {g: i for i, g in enumerate(coverage.glyphs)}
-
- for (glyph, klass, compIdx, compCount), anchor in data.items():
- record = records[idx[glyph]]
- if record is None:
- record = records[idx[glyph]] = ot.LigatureAttach()
- record.ComponentCount = compCount
- record.ComponentRecord = [ot.ComponentRecord() for i in range(compCount)]
- for compRec in record.ComponentRecord:
- compRec.LigatureAnchor = [None] * classCount
- assert record.ComponentCount == compCount, (
- glyph,
- record.ComponentCount,
- compCount,
- )
-
- anchors = record.ComponentRecord[compIdx - 1].LigatureAnchor
- assert anchors[klass] is None, (glyph, compIdx, klass)
- anchors[klass] = anchor
- return records
-
-
-def parseMarkToSomething(lines, font, c):
- self = c.Type()
- self.Format = 1
- markData = {}
- baseData = {}
- Data = {
- "mark": (markData, c.MarkAnchorClass),
- "base": (baseData, c.BaseAnchorClass),
- "ligature": (baseData, c.BaseAnchorClass),
- }
- maxKlass = 0
- for line in lines:
- typ = line[0]
- assert typ in ("mark", "base", "ligature")
- glyph = makeGlyph(line[1])
- data, anchorClass = Data[typ]
- extraItems = 2 if typ == "ligature" else 0
- extras = tuple(int(i) for i in line[2 : 2 + extraItems])
- klass = int(line[2 + extraItems])
- anchor = makeAnchor(line[3 + extraItems :], anchorClass)
- if typ == "mark":
- key, value = glyph, (klass, anchor)
- else:
- key, value = ((glyph, klass) + extras), anchor
- assert key not in data, key
- data[key] = value
- maxKlass = max(maxKlass, klass)
-
- # Mark
- markCoverage = makeCoverage(set(markData.keys()), font, c.MarkCoverageClass)
- markArray = c.MarkArrayClass()
- markRecords = makeMarkRecords(markData, markCoverage, c)
- setattr(markArray, c.MarkRecord, markRecords)
- setattr(markArray, c.MarkCount, len(markRecords))
- setattr(self, c.MarkCoverage, markCoverage)
- setattr(self, c.MarkArray, markArray)
- self.ClassCount = maxKlass + 1
-
- # Base
- self.classCount = 0 if not baseData else 1 + max(k[1] for k, v in baseData.items())
- baseCoverage = makeCoverage(
- set([k[0] for k in baseData.keys()]), font, c.BaseCoverageClass
- )
- baseArray = c.BaseArrayClass()
- if c.Base == "Ligature":
- baseRecords = makeLigatureRecords(baseData, baseCoverage, c, self.classCount)
- else:
- baseRecords = makeBaseRecords(baseData, baseCoverage, c, self.classCount)
- setattr(baseArray, c.BaseRecord, baseRecords)
- setattr(baseArray, c.BaseCount, len(baseRecords))
- setattr(self, c.BaseCoverage, baseCoverage)
- setattr(self, c.BaseArray, baseArray)
-
- return self
-
-
-class MarkHelper(object):
- def __init__(self):
- for Which in ("Mark", "Base"):
- for What in ("Coverage", "Array", "Count", "Record", "Anchor"):
- key = Which + What
- if Which == "Mark" and What in ("Count", "Record", "Anchor"):
- value = key
- else:
- value = getattr(self, Which) + What
- if value == "LigatureRecord":
- value = "LigatureAttach"
- setattr(self, key, value)
- if What != "Count":
- klass = getattr(ot, value)
- setattr(self, key + "Class", klass)
-
-
-class MarkToBaseHelper(MarkHelper):
- Mark = "Mark"
- Base = "Base"
- Type = ot.MarkBasePos
-
-
-class MarkToMarkHelper(MarkHelper):
- Mark = "Mark1"
- Base = "Mark2"
- Type = ot.MarkMarkPos
-
-
-class MarkToLigatureHelper(MarkHelper):
- Mark = "Mark"
- Base = "Ligature"
- Type = ot.MarkLigPos
-
-
-def parseMarkToBase(lines, font, _lookupMap=None):
- return parseMarkToSomething(lines, font, MarkToBaseHelper())
-
-
-def parseMarkToMark(lines, font, _lookupMap=None):
- return parseMarkToSomething(lines, font, MarkToMarkHelper())
-
-
-def parseMarkToLigature(lines, font, _lookupMap=None):
- return parseMarkToSomething(lines, font, MarkToLigatureHelper())
-
-
-def stripSplitComma(line):
- return [s.strip() for s in line.split(",")] if line else []
-
-
-def intSplitComma(line):
- return [int(i) for i in line.split(",")] if line else []
-
-
-# Copied from fontTools.subset
-class ContextHelper(object):
- def __init__(self, klassName, Format):
- if klassName.endswith("Subst"):
- Typ = "Sub"
- Type = "Subst"
- else:
- Typ = "Pos"
- Type = "Pos"
- if klassName.startswith("Chain"):
- Chain = "Chain"
- InputIdx = 1
- DataLen = 3
- else:
- Chain = ""
- InputIdx = 0
- DataLen = 1
- ChainTyp = Chain + Typ
-
- self.Typ = Typ
- self.Type = Type
- self.Chain = Chain
- self.ChainTyp = ChainTyp
- self.InputIdx = InputIdx
- self.DataLen = DataLen
-
- self.LookupRecord = Type + "LookupRecord"
-
- if Format == 1:
- Coverage = lambda r: r.Coverage
- ChainCoverage = lambda r: r.Coverage
- ContextData = lambda r: (None,)
- ChainContextData = lambda r: (None, None, None)
- SetContextData = None
- SetChainContextData = None
- RuleData = lambda r: (r.Input,)
- ChainRuleData = lambda r: (r.Backtrack, r.Input, r.LookAhead)
-
- def SetRuleData(r, d):
- (r.Input,) = d
- (r.GlyphCount,) = (len(x) + 1 for x in d)
-
- def ChainSetRuleData(r, d):
- (r.Backtrack, r.Input, r.LookAhead) = d
- (
- r.BacktrackGlyphCount,
- r.InputGlyphCount,
- r.LookAheadGlyphCount,
- ) = (
- len(d[0]),
- len(d[1]) + 1,
- len(d[2]),
- )
-
- elif Format == 2:
- Coverage = lambda r: r.Coverage
- ChainCoverage = lambda r: r.Coverage
- ContextData = lambda r: (r.ClassDef,)
- ChainContextData = lambda r: (
- r.BacktrackClassDef,
- r.InputClassDef,
- r.LookAheadClassDef,
- )
-
- def SetContextData(r, d):
- (r.ClassDef,) = d
-
- def SetChainContextData(r, d):
- (r.BacktrackClassDef, r.InputClassDef, r.LookAheadClassDef) = d
-
- RuleData = lambda r: (r.Class,)
- ChainRuleData = lambda r: (r.Backtrack, r.Input, r.LookAhead)
-
- def SetRuleData(r, d):
- (r.Class,) = d
- (r.GlyphCount,) = (len(x) + 1 for x in d)
-
- def ChainSetRuleData(r, d):
- (r.Backtrack, r.Input, r.LookAhead) = d
- (
- r.BacktrackGlyphCount,
- r.InputGlyphCount,
- r.LookAheadGlyphCount,
- ) = (
- len(d[0]),
- len(d[1]) + 1,
- len(d[2]),
- )
-
- elif Format == 3:
- Coverage = lambda r: r.Coverage[0]
- ChainCoverage = lambda r: r.InputCoverage[0]
- ContextData = None
- ChainContextData = None
- SetContextData = None
- SetChainContextData = None
- RuleData = lambda r: r.Coverage
- ChainRuleData = lambda r: (
- r.BacktrackCoverage + r.InputCoverage + r.LookAheadCoverage
- )
-
- def SetRuleData(r, d):
- (r.Coverage,) = d
- (r.GlyphCount,) = (len(x) for x in d)
-
- def ChainSetRuleData(r, d):
- (r.BacktrackCoverage, r.InputCoverage, r.LookAheadCoverage) = d
- (
- r.BacktrackGlyphCount,
- r.InputGlyphCount,
- r.LookAheadGlyphCount,
- ) = (len(x) for x in d)
-
- else:
- assert 0, "unknown format: %s" % Format
-
- if Chain:
- self.Coverage = ChainCoverage
- self.ContextData = ChainContextData
- self.SetContextData = SetChainContextData
- self.RuleData = ChainRuleData
- self.SetRuleData = ChainSetRuleData
- else:
- self.Coverage = Coverage
- self.ContextData = ContextData
- self.SetContextData = SetContextData
- self.RuleData = RuleData
- self.SetRuleData = SetRuleData
-
- if Format == 1:
- self.Rule = ChainTyp + "Rule"
- self.RuleCount = ChainTyp + "RuleCount"
- self.RuleSet = ChainTyp + "RuleSet"
- self.RuleSetCount = ChainTyp + "RuleSetCount"
- self.Intersect = lambda glyphs, c, r: [r] if r in glyphs else []
- elif Format == 2:
- self.Rule = ChainTyp + "ClassRule"
- self.RuleCount = ChainTyp + "ClassRuleCount"
- self.RuleSet = ChainTyp + "ClassSet"
- self.RuleSetCount = ChainTyp + "ClassSetCount"
- self.Intersect = lambda glyphs, c, r: (
- c.intersect_class(glyphs, r)
- if c
- else (set(glyphs) if r == 0 else set())
- )
-
- self.ClassDef = "InputClassDef" if Chain else "ClassDef"
- self.ClassDefIndex = 1 if Chain else 0
- self.Input = "Input" if Chain else "Class"
-
-
-def parseLookupRecords(items, klassName, lookupMap=None):
- klass = getattr(ot, klassName)
- lst = []
- for item in items:
- rec = klass()
- item = stripSplitComma(item)
- assert len(item) == 2, item
- idx = int(item[0])
- assert idx > 0, idx
- rec.SequenceIndex = idx - 1
- setReference(mapLookup, lookupMap, item[1], setattr, rec, "LookupListIndex")
- lst.append(rec)
- return lst
-
-
-def makeClassDef(classDefs, font, klass=ot.Coverage):
- if not classDefs:
- return None
- self = klass()
- self.classDefs = dict(classDefs)
- return self
-
-
-def parseClassDef(lines, font, klass=ot.ClassDef):
- classDefs = {}
- with lines.between("class definition"):
- for line in lines:
- glyph = makeGlyph(line[0])
- assert glyph not in classDefs, glyph
- classDefs[glyph] = int(line[1])
- return makeClassDef(classDefs, font, klass)
-
-
-def makeCoverage(glyphs, font, klass=ot.Coverage):
- if not glyphs:
- return None
- if isinstance(glyphs, set):
- glyphs = sorted(glyphs)
- coverage = klass()
- coverage.glyphs = sorted(set(glyphs), key=font.getGlyphID)
- return coverage
-
-
-def parseCoverage(lines, font, klass=ot.Coverage):
- glyphs = []
- with lines.between("coverage definition"):
- for line in lines:
- glyphs.append(makeGlyph(line[0]))
- return makeCoverage(glyphs, font, klass)
-
-
-def bucketizeRules(self, c, rules, bucketKeys):
- buckets = {}
- for seq, recs in rules:
- buckets.setdefault(seq[c.InputIdx][0], []).append(
- (tuple(s[1 if i == c.InputIdx else 0 :] for i, s in enumerate(seq)), recs)
- )
-
- rulesets = []
- for firstGlyph in bucketKeys:
- if firstGlyph not in buckets:
- rulesets.append(None)
- continue
- thisRules = []
- for seq, recs in buckets[firstGlyph]:
- rule = getattr(ot, c.Rule)()
- c.SetRuleData(rule, seq)
- setattr(rule, c.Type + "Count", len(recs))
- setattr(rule, c.LookupRecord, recs)
- thisRules.append(rule)
-
- ruleset = getattr(ot, c.RuleSet)()
- setattr(ruleset, c.Rule, thisRules)
- setattr(ruleset, c.RuleCount, len(thisRules))
- rulesets.append(ruleset)
-
- setattr(self, c.RuleSet, rulesets)
- setattr(self, c.RuleSetCount, len(rulesets))
-
-
-def parseContext(lines, font, Type, lookupMap=None):
- self = getattr(ot, Type)()
- typ = lines.peeks()[0].split()[0].lower()
- if typ == "glyph":
- self.Format = 1
- log.debug("Parsing %s format %s", Type, self.Format)
- c = ContextHelper(Type, self.Format)
- rules = []
- for line in lines:
- assert line[0].lower() == "glyph", line[0]
- while len(line) < 1 + c.DataLen:
- line.append("")
- seq = tuple(makeGlyphs(stripSplitComma(i)) for i in line[1 : 1 + c.DataLen])
- recs = parseLookupRecords(line[1 + c.DataLen :], c.LookupRecord, lookupMap)
- rules.append((seq, recs))
-
- firstGlyphs = set(seq[c.InputIdx][0] for seq, recs in rules)
- self.Coverage = makeCoverage(firstGlyphs, font)
- bucketizeRules(self, c, rules, self.Coverage.glyphs)
- elif typ.endswith("class"):
- self.Format = 2
- log.debug("Parsing %s format %s", Type, self.Format)
- c = ContextHelper(Type, self.Format)
- classDefs = [None] * c.DataLen
- while lines.peeks()[0].endswith("class definition begin"):
- typ = lines.peek()[0][: -len("class definition begin")].lower()
- idx, klass = {
- 1: {
- "": (0, ot.ClassDef),
- },
- 3: {
- "backtrack": (0, ot.BacktrackClassDef),
- "": (1, ot.InputClassDef),
- "lookahead": (2, ot.LookAheadClassDef),
- },
- }[c.DataLen][typ]
- assert classDefs[idx] is None, idx
- classDefs[idx] = parseClassDef(lines, font, klass=klass)
- c.SetContextData(self, classDefs)
- rules = []
- for line in lines:
- assert line[0].lower().startswith("class"), line[0]
- while len(line) < 1 + c.DataLen:
- line.append("")
- seq = tuple(intSplitComma(i) for i in line[1 : 1 + c.DataLen])
- recs = parseLookupRecords(line[1 + c.DataLen :], c.LookupRecord, lookupMap)
- rules.append((seq, recs))
- firstClasses = set(seq[c.InputIdx][0] for seq, recs in rules)
- firstGlyphs = set(
- g for g, c in classDefs[c.InputIdx].classDefs.items() if c in firstClasses
- )
- self.Coverage = makeCoverage(firstGlyphs, font)
- bucketizeRules(self, c, rules, range(max(firstClasses) + 1))
- elif typ.endswith("coverage"):
- self.Format = 3
- log.debug("Parsing %s format %s", Type, self.Format)
- c = ContextHelper(Type, self.Format)
- coverages = tuple([] for i in range(c.DataLen))
- while lines.peeks()[0].endswith("coverage definition begin"):
- typ = lines.peek()[0][: -len("coverage definition begin")].lower()
- idx, klass = {
- 1: {
- "": (0, ot.Coverage),
- },
- 3: {
- "backtrack": (0, ot.BacktrackCoverage),
- "input": (1, ot.InputCoverage),
- "lookahead": (2, ot.LookAheadCoverage),
- },
- }[c.DataLen][typ]
- coverages[idx].append(parseCoverage(lines, font, klass=klass))
- c.SetRuleData(self, coverages)
- lines = list(lines)
- assert len(lines) == 1
- line = lines[0]
- assert line[0].lower() == "coverage", line[0]
- recs = parseLookupRecords(line[1:], c.LookupRecord, lookupMap)
- setattr(self, c.Type + "Count", len(recs))
- setattr(self, c.LookupRecord, recs)
- else:
- assert 0, typ
- return self
-
-
-def parseContextSubst(lines, font, lookupMap=None):
- return parseContext(lines, font, "ContextSubst", lookupMap=lookupMap)
-
-
-def parseContextPos(lines, font, lookupMap=None):
- return parseContext(lines, font, "ContextPos", lookupMap=lookupMap)
-
-
-def parseChainedSubst(lines, font, lookupMap=None):
- return parseContext(lines, font, "ChainContextSubst", lookupMap=lookupMap)
-
-
-def parseChainedPos(lines, font, lookupMap=None):
- return parseContext(lines, font, "ChainContextPos", lookupMap=lookupMap)
-
-
-def parseReverseChainedSubst(lines, font, _lookupMap=None):
- self = ot.ReverseChainSingleSubst()
- self.Format = 1
- coverages = ([], [])
- while lines.peeks()[0].endswith("coverage definition begin"):
- typ = lines.peek()[0][: -len("coverage definition begin")].lower()
- idx, klass = {
- "backtrack": (0, ot.BacktrackCoverage),
- "lookahead": (1, ot.LookAheadCoverage),
- }[typ]
- coverages[idx].append(parseCoverage(lines, font, klass=klass))
- self.BacktrackCoverage = coverages[0]
- self.BacktrackGlyphCount = len(self.BacktrackCoverage)
- self.LookAheadCoverage = coverages[1]
- self.LookAheadGlyphCount = len(self.LookAheadCoverage)
- mapping = {}
- for line in lines:
- assert len(line) == 2, line
- line = makeGlyphs(line)
- mapping[line[0]] = line[1]
- self.Coverage = makeCoverage(set(mapping.keys()), font)
- self.Substitute = [mapping[k] for k in self.Coverage.glyphs]
- self.GlyphCount = len(self.Substitute)
- return self
-
-
-def parseLookup(lines, tableTag, font, lookupMap=None):
- line = lines.expect("lookup")
- _, name, typ = line
- log.debug("Parsing lookup type %s %s", typ, name)
- lookup = ot.Lookup()
- lookup.LookupFlag, filterset = parseLookupFlags(lines)
- if filterset is not None:
- lookup.MarkFilteringSet = filterset
- lookup.LookupType, parseLookupSubTable = {
- "GSUB": {
- "single": (1, parseSingleSubst),
- "multiple": (2, parseMultiple),
- "alternate": (3, parseAlternate),
- "ligature": (4, parseLigature),
- "context": (5, parseContextSubst),
- "chained": (6, parseChainedSubst),
- "reversechained": (8, parseReverseChainedSubst),
- },
- "GPOS": {
- "single": (1, parseSinglePos),
- "pair": (2, parsePair),
- "kernset": (2, parseKernset),
- "cursive": (3, parseCursive),
- "mark to base": (4, parseMarkToBase),
- "mark to ligature": (5, parseMarkToLigature),
- "mark to mark": (6, parseMarkToMark),
- "context": (7, parseContextPos),
- "chained": (8, parseChainedPos),
- },
- }[tableTag][typ]
-
- with lines.until("lookup end"):
- subtables = []
-
- while lines.peek():
- with lines.until(("% subtable", "subtable end")):
- while lines.peek():
- subtable = parseLookupSubTable(lines, font, lookupMap)
- assert lookup.LookupType == subtable.LookupType
- subtables.append(subtable)
- if lines.peeks()[0] in ("% subtable", "subtable end"):
- next(lines)
- lines.expect("lookup end")
-
- lookup.SubTable = subtables
- lookup.SubTableCount = len(lookup.SubTable)
- if lookup.SubTableCount == 0:
- # Remove this return when following is fixed:
- # https://github.com/fonttools/fonttools/issues/789
- return None
- return lookup
-
-
-def parseGSUBGPOS(lines, font, tableTag):
- container = ttLib.getTableClass(tableTag)()
- lookupMap = DeferredMapping()
- featureMap = DeferredMapping()
- assert tableTag in ("GSUB", "GPOS")
- log.debug("Parsing %s", tableTag)
- self = getattr(ot, tableTag)()
- self.Version = 0x00010000
- fields = {
- "script table begin": (
- "ScriptList",
- lambda lines: parseScriptList(lines, featureMap),
- ),
- "feature table begin": (
- "FeatureList",
- lambda lines: parseFeatureList(lines, lookupMap, featureMap),
- ),
- "lookup": ("LookupList", None),
- }
- for attr, parser in fields.values():
- setattr(self, attr, None)
- while lines.peek() is not None:
- typ = lines.peek()[0].lower()
- if typ not in fields:
- log.debug("Skipping %s", lines.peek())
- next(lines)
- continue
- attr, parser = fields[typ]
- if typ == "lookup":
- if self.LookupList is None:
- self.LookupList = ot.LookupList()
- self.LookupList.Lookup = []
- _, name, _ = lines.peek()
- lookup = parseLookup(lines, tableTag, font, lookupMap)
- if lookupMap is not None:
- assert name not in lookupMap, "Duplicate lookup name: %s" % name
- lookupMap[name] = len(self.LookupList.Lookup)
- else:
- assert int(name) == len(self.LookupList.Lookup), "%d %d" % (
- name,
- len(self.Lookup),
- )
- self.LookupList.Lookup.append(lookup)
- else:
- assert getattr(self, attr) is None, attr
- setattr(self, attr, parser(lines))
- if self.LookupList:
- self.LookupList.LookupCount = len(self.LookupList.Lookup)
- if lookupMap is not None:
- lookupMap.applyDeferredMappings()
- if os.environ.get(LOOKUP_DEBUG_ENV_VAR):
- if "Debg" not in font:
- font["Debg"] = newTable("Debg")
- font["Debg"].data = {}
- debug = (
- font["Debg"]
- .data.setdefault(LOOKUP_DEBUG_INFO_KEY, {})
- .setdefault(tableTag, {})
- )
- for name, lookup in lookupMap.items():
- debug[str(lookup)] = ["", name, ""]
-
- featureMap.applyDeferredMappings()
- container.table = self
- return container
-
-
-def parseGSUB(lines, font):
- return parseGSUBGPOS(lines, font, "GSUB")
-
-
-def parseGPOS(lines, font):
- return parseGSUBGPOS(lines, font, "GPOS")
-
-
-def parseAttachList(lines, font):
- points = {}
- with lines.between("attachment list"):
- for line in lines:
- glyph = makeGlyph(line[0])
- assert glyph not in points, glyph
- points[glyph] = [int(i) for i in line[1:]]
- return otl.buildAttachList(points, font.getReverseGlyphMap())
-
-
-def parseCaretList(lines, font):
- carets = {}
- with lines.between("carets"):
- for line in lines:
- glyph = makeGlyph(line[0])
- assert glyph not in carets, glyph
- num = int(line[1])
- thisCarets = [int(i) for i in line[2:]]
- assert num == len(thisCarets), line
- carets[glyph] = thisCarets
- return otl.buildLigCaretList(carets, {}, font.getReverseGlyphMap())
-
-
-def makeMarkFilteringSets(sets, font):
- self = ot.MarkGlyphSetsDef()
- self.MarkSetTableFormat = 1
- self.MarkSetCount = 1 + max(sets.keys())
- self.Coverage = [None] * self.MarkSetCount
- for k, v in sorted(sets.items()):
- self.Coverage[k] = makeCoverage(set(v), font)
- return self
-
-
-def parseMarkFilteringSets(lines, font):
- sets = {}
- with lines.between("set definition"):
- for line in lines:
- assert len(line) == 2, line
- glyph = makeGlyph(line[0])
- # TODO accept set names
- st = int(line[1])
- if st not in sets:
- sets[st] = []
- sets[st].append(glyph)
- return makeMarkFilteringSets(sets, font)
-
-
-def parseGDEF(lines, font):
- container = ttLib.getTableClass("GDEF")()
- log.debug("Parsing GDEF")
- self = ot.GDEF()
- fields = {
- "class definition begin": (
- "GlyphClassDef",
- lambda lines, font: parseClassDef(lines, font, klass=ot.GlyphClassDef),
- ),
- "attachment list begin": ("AttachList", parseAttachList),
- "carets begin": ("LigCaretList", parseCaretList),
- "mark attachment class definition begin": (
- "MarkAttachClassDef",
- lambda lines, font: parseClassDef(lines, font, klass=ot.MarkAttachClassDef),
- ),
- "markfilter set definition begin": ("MarkGlyphSetsDef", parseMarkFilteringSets),
- }
- for attr, parser in fields.values():
- setattr(self, attr, None)
- while lines.peek() is not None:
- typ = lines.peek()[0].lower()
- if typ not in fields:
- log.debug("Skipping %s", typ)
- next(lines)
- continue
- attr, parser = fields[typ]
- assert getattr(self, attr) is None, attr
- setattr(self, attr, parser(lines, font))
- self.Version = 0x00010000 if self.MarkGlyphSetsDef is None else 0x00010002
- container.table = self
- return container
-
-
-def parseCmap(lines, font):
- container = ttLib.getTableClass("cmap")()
- log.debug("Parsing cmap")
- tables = []
- while lines.peek() is not None:
- lines.expect("cmap subtable %d" % len(tables))
- platId, encId, fmt, lang = [
- parseCmapId(lines, field)
- for field in ("platformID", "encodingID", "format", "language")
- ]
- table = cmap_classes[fmt](fmt)
- table.platformID = platId
- table.platEncID = encId
- table.language = lang
- table.cmap = {}
- line = next(lines)
- while line[0] != "end subtable":
- table.cmap[int(line[0], 16)] = line[1]
- line = next(lines)
- tables.append(table)
- container.tableVersion = 0
- container.tables = tables
- return container
-
-
-def parseCmapId(lines, field):
- line = next(lines)
- assert field == line[0]
- return int(line[1])
-
-
-def parseTable(lines, font, tableTag=None):
- log.debug("Parsing table")
- line = lines.peeks()
- tag = None
- if line[0].split()[0] == "FontDame":
- tag = line[0].split()[1]
- elif " ".join(line[0].split()[:3]) == "Font Chef Table":
- tag = line[0].split()[3]
- if tag is not None:
- next(lines)
- tag = tag.ljust(4)
- if tableTag is None:
- tableTag = tag
- else:
- assert tableTag == tag, (tableTag, tag)
-
- assert (
- tableTag is not None
- ), "Don't know what table to parse and data doesn't specify"
-
- return {
- "GSUB": parseGSUB,
- "GPOS": parseGPOS,
- "GDEF": parseGDEF,
- "cmap": parseCmap,
- }[tableTag](lines, font)
-
-
-class Tokenizer(object):
- def __init__(self, f):
- # TODO BytesIO / StringIO as needed? also, figure out whether we work on bytes or unicode
- lines = iter(f)
- try:
- self.filename = f.name
- except:
- self.filename = None
- self.lines = iter(lines)
- self.line = ""
- self.lineno = 0
- self.stoppers = []
- self.buffer = None
-
- def __iter__(self):
- return self
-
- def _next_line(self):
- self.lineno += 1
- line = self.line = next(self.lines)
- line = [s.strip() for s in line.split("\t")]
- if len(line) == 1 and not line[0]:
- del line[0]
- if line and not line[-1]:
- log.warning("trailing tab found on line %d: %s" % (self.lineno, self.line))
- while line and not line[-1]:
- del line[-1]
- return line
-
- def _next_nonempty(self):
- while True:
- line = self._next_line()
- # Skip comments and empty lines
- if line and line[0] and (line[0][0] != "%" or line[0] == "% subtable"):
- return line
-
- def _next_buffered(self):
- if self.buffer:
- ret = self.buffer
- self.buffer = None
- return ret
- else:
- return self._next_nonempty()
-
- def __next__(self):
- line = self._next_buffered()
- if line[0].lower() in self.stoppers:
- self.buffer = line
- raise StopIteration
- return line
-
- def next(self):
- return self.__next__()
-
- def peek(self):
- if not self.buffer:
- try:
- self.buffer = self._next_nonempty()
- except StopIteration:
- return None
- if self.buffer[0].lower() in self.stoppers:
- return None
- return self.buffer
-
- def peeks(self):
- ret = self.peek()
- return ret if ret is not None else ("",)
-
- @contextmanager
- def between(self, tag):
- start = tag + " begin"
- end = tag + " end"
- self.expectendswith(start)
- self.stoppers.append(end)
- yield
- del self.stoppers[-1]
- self.expect(tag + " end")
-
- @contextmanager
- def until(self, tags):
- if type(tags) is not tuple:
- tags = (tags,)
- self.stoppers.extend(tags)
- yield
- del self.stoppers[-len(tags) :]
-
- def expect(self, s):
- line = next(self)
- tag = line[0].lower()
- assert tag == s, "Expected '%s', got '%s'" % (s, tag)
- return line
-
- def expectendswith(self, s):
- line = next(self)
- tag = line[0].lower()
- assert tag.endswith(s), "Expected '*%s', got '%s'" % (s, tag)
- return line
-
-
-def build(f, font, tableTag=None):
- """Convert a Monotype font layout file to an OpenType layout object
-
- A font object must be passed, but this may be a "dummy" font; it is only
- used for sorting glyph sets when making coverage tables and to hold the
- OpenType layout table while it is being built.
-
- Args:
- f: A file object.
- font (TTFont): A font object.
- tableTag (string): If provided, asserts that the file contains data for the
- given OpenType table.
-
- Returns:
- An object representing the table. (e.g. ``table_G_S_U_B_``)
- """
- lines = Tokenizer(f)
- return parseTable(lines, font, tableTag=tableTag)
-
-
-def main(args=None, font=None):
- """Convert a FontDame OTL file to TTX XML
-
- Writes XML output to stdout.
-
- Args:
- args: Command line arguments (``--font``, ``--table``, input files).
- """
- import sys
- from fontTools import configLogger
- from fontTools.misc.testTools import MockFont
-
- if args is None:
- args = sys.argv[1:]
-
- # configure the library logger (for >= WARNING)
- configLogger()
- # comment this out to enable debug messages from mtiLib's logger
- # log.setLevel(logging.DEBUG)
-
- import argparse
-
- parser = argparse.ArgumentParser(
- "fonttools mtiLib",
- description=main.__doc__,
- )
-
- parser.add_argument(
- "--font",
- "-f",
- metavar="FILE",
- dest="font",
- help="Input TTF files (used for glyph classes and sorting coverage tables)",
- )
- parser.add_argument(
- "--table",
- "-t",
- metavar="TABLE",
- dest="tableTag",
- help="Table to fill (sniffed from input file if not provided)",
- )
- parser.add_argument(
- "inputs", metavar="FILE", type=str, nargs="+", help="Input FontDame .txt files"
- )
-
- args = parser.parse_args(args)
-
- if font is None:
- if args.font:
- font = ttLib.TTFont(args.font)
- else:
- font = MockFont()
-
- for f in args.inputs:
- log.debug("Processing %s", f)
- with open(f, "rt", encoding="utf-8") as f:
- table = build(f, font, tableTag=args.tableTag)
- blob = table.compile(font) # Make sure it compiles
- decompiled = table.__class__()
- decompiled.decompile(blob, font) # Make sure it decompiles!
-
- # continue
- from fontTools.misc import xmlWriter
-
- tag = table.tableTag
- writer = xmlWriter.XMLWriter(sys.stdout)
- writer.begintag(tag)
- writer.newline()
- # table.toXML(writer, font)
- decompiled.toXML(writer, font)
- writer.endtag(tag)
- writer.newline()
-
-
-if __name__ == "__main__":
- import sys
-
- sys.exit(main())
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/mtiLib/__main__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/mtiLib/__main__.py
deleted file mode 100644
index 29c802b..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/mtiLib/__main__.py
+++ /dev/null
@@ -1,5 +0,0 @@
-import sys
-from fontTools.mtiLib import main
-
-if __name__ == "__main__":
- sys.exit(main())
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/__init__.py
deleted file mode 100644
index 12e414f..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-"""OpenType Layout-related functionality."""
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/builder.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/builder.py
deleted file mode 100644
index 2a02c20..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/builder.py
+++ /dev/null
@@ -1,2916 +0,0 @@
-from collections import namedtuple, OrderedDict
-import os
-from fontTools.misc.fixedTools import fixedToFloat
-from fontTools import ttLib
-from fontTools.ttLib.tables import otTables as ot
-from fontTools.ttLib.tables.otBase import (
- ValueRecord,
- valueRecordFormatDict,
- OTTableWriter,
- CountReference,
-)
-from fontTools.ttLib.tables import otBase
-from fontTools.feaLib.ast import STATNameStatement
-from fontTools.otlLib.optimize.gpos import (
- _compression_level_from_env,
- compact_lookup,
-)
-from fontTools.otlLib.error import OpenTypeLibError
-from functools import reduce
-import logging
-import copy
-
-
-log = logging.getLogger(__name__)
-
-
-def buildCoverage(glyphs, glyphMap):
- """Builds a coverage table.
-
- Coverage tables (as defined in the `OpenType spec `__)
- are used in all OpenType Layout lookups apart from the Extension type, and
- define the glyphs involved in a layout subtable. This allows shaping engines
- to compare the glyph stream with the coverage table and quickly determine
- whether a subtable should be involved in a shaping operation.
-
- This function takes a list of glyphs and a glyphname-to-ID map, and
- returns a ``Coverage`` object representing the coverage table.
-
- Example::
-
- glyphMap = font.getReverseGlyphMap()
- glyphs = [ "A", "B", "C" ]
- coverage = buildCoverage(glyphs, glyphMap)
-
- Args:
- glyphs: a sequence of glyph names.
- glyphMap: a glyph name to ID map, typically returned from
- ``font.getReverseGlyphMap()``.
-
- Returns:
- An ``otTables.Coverage`` object or ``None`` if there are no glyphs
- supplied.
- """
-
- if not glyphs:
- return None
- self = ot.Coverage()
- self.glyphs = sorted(set(glyphs), key=glyphMap.__getitem__)
- return self
-
-
-LOOKUP_FLAG_RIGHT_TO_LEFT = 0x0001
-LOOKUP_FLAG_IGNORE_BASE_GLYPHS = 0x0002
-LOOKUP_FLAG_IGNORE_LIGATURES = 0x0004
-LOOKUP_FLAG_IGNORE_MARKS = 0x0008
-LOOKUP_FLAG_USE_MARK_FILTERING_SET = 0x0010
-
-
-def buildLookup(subtables, flags=0, markFilterSet=None):
- """Turns a collection of rules into a lookup.
-
- A Lookup (as defined in the `OpenType Spec `__)
- wraps the individual rules in a layout operation (substitution or
- positioning) in a data structure expressing their overall lookup type -
- for example, single substitution, mark-to-base attachment, and so on -
- as well as the lookup flags and any mark filtering sets. You may import
- the following constants to express lookup flags:
-
- - ``LOOKUP_FLAG_RIGHT_TO_LEFT``
- - ``LOOKUP_FLAG_IGNORE_BASE_GLYPHS``
- - ``LOOKUP_FLAG_IGNORE_LIGATURES``
- - ``LOOKUP_FLAG_IGNORE_MARKS``
- - ``LOOKUP_FLAG_USE_MARK_FILTERING_SET``
-
- Args:
- subtables: A list of layout subtable objects (e.g.
- ``MultipleSubst``, ``PairPos``, etc.) or ``None``.
- flags (int): This lookup's flags.
- markFilterSet: Either ``None`` if no mark filtering set is used, or
- an integer representing the filtering set to be used for this
- lookup. If a mark filtering set is provided,
- `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
- flags.
-
- Returns:
- An ``otTables.Lookup`` object or ``None`` if there are no subtables
- supplied.
- """
- if subtables is None:
- return None
- subtables = [st for st in subtables if st is not None]
- if not subtables:
- return None
- assert all(
- t.LookupType == subtables[0].LookupType for t in subtables
- ), "all subtables must have the same LookupType; got %s" % repr(
- [t.LookupType for t in subtables]
- )
- self = ot.Lookup()
- self.LookupType = subtables[0].LookupType
- self.LookupFlag = flags
- self.SubTable = subtables
- self.SubTableCount = len(self.SubTable)
- if markFilterSet is not None:
- self.LookupFlag |= LOOKUP_FLAG_USE_MARK_FILTERING_SET
- assert isinstance(markFilterSet, int), markFilterSet
- self.MarkFilteringSet = markFilterSet
- else:
- assert (self.LookupFlag & LOOKUP_FLAG_USE_MARK_FILTERING_SET) == 0, (
- "if markFilterSet is None, flags must not set "
- "LOOKUP_FLAG_USE_MARK_FILTERING_SET; flags=0x%04x" % flags
- )
- return self
-
-
-class LookupBuilder(object):
- SUBTABLE_BREAK_ = "SUBTABLE_BREAK"
-
- def __init__(self, font, location, table, lookup_type):
- self.font = font
- self.glyphMap = font.getReverseGlyphMap()
- self.location = location
- self.table, self.lookup_type = table, lookup_type
- self.lookupflag = 0
- self.markFilterSet = None
- self.lookup_index = None # assigned when making final tables
- assert table in ("GPOS", "GSUB")
-
- def equals(self, other):
- return (
- isinstance(other, self.__class__)
- and self.table == other.table
- and self.lookupflag == other.lookupflag
- and self.markFilterSet == other.markFilterSet
- )
-
- def inferGlyphClasses(self):
- """Infers glyph glasses for the GDEF table, such as {"cedilla":3}."""
- return {}
-
- def getAlternateGlyphs(self):
- """Helper for building 'aalt' features."""
- return {}
-
- def buildLookup_(self, subtables):
- return buildLookup(subtables, self.lookupflag, self.markFilterSet)
-
- def buildMarkClasses_(self, marks):
- """{"cedilla": ("BOTTOM", ast.Anchor), ...} --> {"BOTTOM":0, "TOP":1}
-
- Helper for MarkBasePostBuilder, MarkLigPosBuilder, and
- MarkMarkPosBuilder. Seems to return the same numeric IDs
- for mark classes as the AFDKO makeotf tool.
- """
- ids = {}
- for mark in sorted(marks.keys(), key=self.font.getGlyphID):
- markClassName, _markAnchor = marks[mark]
- if markClassName not in ids:
- ids[markClassName] = len(ids)
- return ids
-
- def setBacktrackCoverage_(self, prefix, subtable):
- subtable.BacktrackGlyphCount = len(prefix)
- subtable.BacktrackCoverage = []
- for p in reversed(prefix):
- coverage = buildCoverage(p, self.glyphMap)
- subtable.BacktrackCoverage.append(coverage)
-
- def setLookAheadCoverage_(self, suffix, subtable):
- subtable.LookAheadGlyphCount = len(suffix)
- subtable.LookAheadCoverage = []
- for s in suffix:
- coverage = buildCoverage(s, self.glyphMap)
- subtable.LookAheadCoverage.append(coverage)
-
- def setInputCoverage_(self, glyphs, subtable):
- subtable.InputGlyphCount = len(glyphs)
- subtable.InputCoverage = []
- for g in glyphs:
- coverage = buildCoverage(g, self.glyphMap)
- subtable.InputCoverage.append(coverage)
-
- def setCoverage_(self, glyphs, subtable):
- subtable.GlyphCount = len(glyphs)
- subtable.Coverage = []
- for g in glyphs:
- coverage = buildCoverage(g, self.glyphMap)
- subtable.Coverage.append(coverage)
-
- def build_subst_subtables(self, mapping, klass):
- substitutions = [{}]
- for key in mapping:
- if key[0] == self.SUBTABLE_BREAK_:
- substitutions.append({})
- else:
- substitutions[-1][key] = mapping[key]
- subtables = [klass(s) for s in substitutions]
- return subtables
-
- def add_subtable_break(self, location):
- """Add an explicit subtable break.
-
- Args:
- location: A string or tuple representing the location in the
- original source which produced this break, or ``None`` if
- no location is provided.
- """
- log.warning(
- OpenTypeLibError(
- 'unsupported "subtable" statement for lookup type', location
- )
- )
-
-
-class AlternateSubstBuilder(LookupBuilder):
- """Builds an Alternate Substitution (GSUB3) lookup.
-
- Users are expected to manually add alternate glyph substitutions to
- the ``alternates`` attribute after the object has been initialized,
- e.g.::
-
- builder.alternates["A"] = ["A.alt1", "A.alt2"]
-
- Attributes:
- font (``fontTools.TTLib.TTFont``): A font object.
- location: A string or tuple representing the location in the original
- source which produced this lookup.
- alternates: An ordered dictionary of alternates, mapping glyph names
- to a list of names of alternates.
- lookupflag (int): The lookup's flag
- markFilterSet: Either ``None`` if no mark filtering set is used, or
- an integer representing the filtering set to be used for this
- lookup. If a mark filtering set is provided,
- `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
- flags.
- """
-
- def __init__(self, font, location):
- LookupBuilder.__init__(self, font, location, "GSUB", 3)
- self.alternates = OrderedDict()
-
- def equals(self, other):
- return LookupBuilder.equals(self, other) and self.alternates == other.alternates
-
- def build(self):
- """Build the lookup.
-
- Returns:
- An ``otTables.Lookup`` object representing the alternate
- substitution lookup.
- """
- subtables = self.build_subst_subtables(
- self.alternates, buildAlternateSubstSubtable
- )
- return self.buildLookup_(subtables)
-
- def getAlternateGlyphs(self):
- return self.alternates
-
- def add_subtable_break(self, location):
- self.alternates[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
-
-
-class ChainContextualRule(
- namedtuple("ChainContextualRule", ["prefix", "glyphs", "suffix", "lookups"])
-):
- @property
- def is_subtable_break(self):
- return self.prefix == LookupBuilder.SUBTABLE_BREAK_
-
-
-class ChainContextualRuleset:
- def __init__(self):
- self.rules = []
-
- def addRule(self, rule):
- self.rules.append(rule)
-
- @property
- def hasPrefixOrSuffix(self):
- # Do we have any prefixes/suffixes? If this is False for all
- # rulesets, we can express the whole lookup as GPOS5/GSUB7.
- for rule in self.rules:
- if len(rule.prefix) > 0 or len(rule.suffix) > 0:
- return True
- return False
-
- @property
- def hasAnyGlyphClasses(self):
- # Do we use glyph classes anywhere in the rules? If this is False
- # we can express this subtable as a Format 1.
- for rule in self.rules:
- for coverage in (rule.prefix, rule.glyphs, rule.suffix):
- if any(len(x) > 1 for x in coverage):
- return True
- return False
-
- def format2ClassDefs(self):
- PREFIX, GLYPHS, SUFFIX = 0, 1, 2
- classDefBuilders = []
- for ix in [PREFIX, GLYPHS, SUFFIX]:
- context = []
- for r in self.rules:
- context.append(r[ix])
- classes = self._classBuilderForContext(context)
- if not classes:
- return None
- classDefBuilders.append(classes)
- return classDefBuilders
-
- def _classBuilderForContext(self, context):
- classdefbuilder = ClassDefBuilder(useClass0=False)
- for position in context:
- for glyphset in position:
- glyphs = set(glyphset)
- if not classdefbuilder.canAdd(glyphs):
- return None
- classdefbuilder.add(glyphs)
- return classdefbuilder
-
-
-class ChainContextualBuilder(LookupBuilder):
- def equals(self, other):
- return LookupBuilder.equals(self, other) and self.rules == other.rules
-
- def rulesets(self):
- # Return a list of ChainContextRuleset objects, taking explicit
- # subtable breaks into account
- ruleset = [ChainContextualRuleset()]
- for rule in self.rules:
- if rule.is_subtable_break:
- ruleset.append(ChainContextualRuleset())
- continue
- ruleset[-1].addRule(rule)
- # Squish any empty subtables
- return [x for x in ruleset if len(x.rules) > 0]
-
- def getCompiledSize_(self, subtables):
- size = 0
- for st in subtables:
- w = OTTableWriter()
- w["LookupType"] = CountReference(
- {"LookupType": st.LookupType}, "LookupType"
- )
- # We need to make a copy here because compiling
- # modifies the subtable (finalizing formats etc.)
- copy.deepcopy(st).compile(w, self.font)
- size += len(w.getAllData())
- return size
-
- def build(self):
- """Build the lookup.
-
- Returns:
- An ``otTables.Lookup`` object representing the chained
- contextual positioning lookup.
- """
- subtables = []
-
- rulesets = self.rulesets()
- chaining = any(ruleset.hasPrefixOrSuffix for ruleset in rulesets)
-
- # https://github.com/fonttools/fonttools/issues/2539
- #
- # Unfortunately, as of 2022-03-07, Apple's CoreText renderer does not
- # correctly process GPOS7 lookups, so for now we force contextual
- # positioning lookups to be chaining (GPOS8).
- #
- # This seems to be fixed as of macOS 13.2, but we keep disabling this
- # for now until we are no longer concerned about old macOS versions.
- # But we allow people to opt-out of this with the config key below.
- write_gpos7 = self.font.cfg.get("fontTools.otlLib.builder:WRITE_GPOS7")
- # horrible separation of concerns breach
- if not write_gpos7 and self.subtable_type == "Pos":
- chaining = True
-
- for ruleset in rulesets:
- # Determine format strategy. We try to build formats 1, 2 and 3
- # subtables and then work out which is best. candidates list holds
- # the subtables in each format for this ruleset (including a dummy
- # "format 0" to make the addressing match the format numbers).
-
- # We can always build a format 3 lookup by accumulating each of
- # the rules into a list, so start with that.
- candidates = [None, None, None, []]
- for rule in ruleset.rules:
- candidates[3].append(self.buildFormat3Subtable(rule, chaining))
-
- # Can we express the whole ruleset as a format 2 subtable?
- classdefs = ruleset.format2ClassDefs()
- if classdefs:
- candidates[2] = [
- self.buildFormat2Subtable(ruleset, classdefs, chaining)
- ]
-
- if not ruleset.hasAnyGlyphClasses:
- candidates[1] = [self.buildFormat1Subtable(ruleset, chaining)]
-
- for i in [1, 2, 3]:
- if candidates[i]:
- try:
- self.getCompiledSize_(candidates[i])
- except Exception as e:
- log.warning(
- "Contextual format %i at %s overflowed (%s)"
- % (i, str(self.location), e)
- )
- candidates[i] = None
-
- candidates = [x for x in candidates if x is not None]
- if not candidates:
- raise OpenTypeLibError("All candidates overflowed", self.location)
-
- winner = min(candidates, key=self.getCompiledSize_)
- subtables.extend(winner)
-
- # If we are not chaining, lookup type will be automatically fixed by
- # buildLookup_
- return self.buildLookup_(subtables)
-
- def buildFormat1Subtable(self, ruleset, chaining=True):
- st = self.newSubtable_(chaining=chaining)
- st.Format = 1
- st.populateDefaults()
- coverage = set()
- rulesetsByFirstGlyph = {}
- ruleAttr = self.ruleAttr_(format=1, chaining=chaining)
-
- for rule in ruleset.rules:
- ruleAsSubtable = self.newRule_(format=1, chaining=chaining)
-
- if chaining:
- ruleAsSubtable.BacktrackGlyphCount = len(rule.prefix)
- ruleAsSubtable.LookAheadGlyphCount = len(rule.suffix)
- ruleAsSubtable.Backtrack = [list(x)[0] for x in reversed(rule.prefix)]
- ruleAsSubtable.LookAhead = [list(x)[0] for x in rule.suffix]
-
- ruleAsSubtable.InputGlyphCount = len(rule.glyphs)
- else:
- ruleAsSubtable.GlyphCount = len(rule.glyphs)
-
- ruleAsSubtable.Input = [list(x)[0] for x in rule.glyphs[1:]]
-
- self.buildLookupList(rule, ruleAsSubtable)
-
- firstGlyph = list(rule.glyphs[0])[0]
- if firstGlyph not in rulesetsByFirstGlyph:
- coverage.add(firstGlyph)
- rulesetsByFirstGlyph[firstGlyph] = []
- rulesetsByFirstGlyph[firstGlyph].append(ruleAsSubtable)
-
- st.Coverage = buildCoverage(coverage, self.glyphMap)
- ruleSets = []
- for g in st.Coverage.glyphs:
- ruleSet = self.newRuleSet_(format=1, chaining=chaining)
- setattr(ruleSet, ruleAttr, rulesetsByFirstGlyph[g])
- setattr(ruleSet, f"{ruleAttr}Count", len(rulesetsByFirstGlyph[g]))
- ruleSets.append(ruleSet)
-
- setattr(st, self.ruleSetAttr_(format=1, chaining=chaining), ruleSets)
- setattr(
- st, self.ruleSetAttr_(format=1, chaining=chaining) + "Count", len(ruleSets)
- )
-
- return st
-
- def buildFormat2Subtable(self, ruleset, classdefs, chaining=True):
- st = self.newSubtable_(chaining=chaining)
- st.Format = 2
- st.populateDefaults()
-
- if chaining:
- (
- st.BacktrackClassDef,
- st.InputClassDef,
- st.LookAheadClassDef,
- ) = [c.build() for c in classdefs]
- else:
- st.ClassDef = classdefs[1].build()
-
- inClasses = classdefs[1].classes()
-
- classSets = []
- for _ in inClasses:
- classSet = self.newRuleSet_(format=2, chaining=chaining)
- classSets.append(classSet)
-
- coverage = set()
- classRuleAttr = self.ruleAttr_(format=2, chaining=chaining)
-
- for rule in ruleset.rules:
- ruleAsSubtable = self.newRule_(format=2, chaining=chaining)
- if chaining:
- ruleAsSubtable.BacktrackGlyphCount = len(rule.prefix)
- ruleAsSubtable.LookAheadGlyphCount = len(rule.suffix)
- # The glyphs in the rule may be list, tuple, odict_keys...
- # Order is not important anyway because they are guaranteed
- # to be members of the same class.
- ruleAsSubtable.Backtrack = [
- st.BacktrackClassDef.classDefs[list(x)[0]]
- for x in reversed(rule.prefix)
- ]
- ruleAsSubtable.LookAhead = [
- st.LookAheadClassDef.classDefs[list(x)[0]] for x in rule.suffix
- ]
-
- ruleAsSubtable.InputGlyphCount = len(rule.glyphs)
- ruleAsSubtable.Input = [
- st.InputClassDef.classDefs[list(x)[0]] for x in rule.glyphs[1:]
- ]
- setForThisRule = classSets[
- st.InputClassDef.classDefs[list(rule.glyphs[0])[0]]
- ]
- else:
- ruleAsSubtable.GlyphCount = len(rule.glyphs)
- ruleAsSubtable.Class = [ # The spec calls this InputSequence
- st.ClassDef.classDefs[list(x)[0]] for x in rule.glyphs[1:]
- ]
- setForThisRule = classSets[
- st.ClassDef.classDefs[list(rule.glyphs[0])[0]]
- ]
-
- self.buildLookupList(rule, ruleAsSubtable)
- coverage |= set(rule.glyphs[0])
-
- getattr(setForThisRule, classRuleAttr).append(ruleAsSubtable)
- setattr(
- setForThisRule,
- f"{classRuleAttr}Count",
- getattr(setForThisRule, f"{classRuleAttr}Count") + 1,
- )
- setattr(st, self.ruleSetAttr_(format=2, chaining=chaining), classSets)
- setattr(
- st, self.ruleSetAttr_(format=2, chaining=chaining) + "Count", len(classSets)
- )
- st.Coverage = buildCoverage(coverage, self.glyphMap)
- return st
-
- def buildFormat3Subtable(self, rule, chaining=True):
- st = self.newSubtable_(chaining=chaining)
- st.Format = 3
- if chaining:
- self.setBacktrackCoverage_(rule.prefix, st)
- self.setLookAheadCoverage_(rule.suffix, st)
- self.setInputCoverage_(rule.glyphs, st)
- else:
- self.setCoverage_(rule.glyphs, st)
- self.buildLookupList(rule, st)
- return st
-
- def buildLookupList(self, rule, st):
- for sequenceIndex, lookupList in enumerate(rule.lookups):
- if lookupList is not None:
- if not isinstance(lookupList, list):
- # Can happen with synthesised lookups
- lookupList = [lookupList]
- for l in lookupList:
- if l.lookup_index is None:
- if isinstance(self, ChainContextPosBuilder):
- other = "substitution"
- else:
- other = "positioning"
- raise OpenTypeLibError(
- "Missing index of the specified "
- f"lookup, might be a {other} lookup",
- self.location,
- )
- rec = self.newLookupRecord_(st)
- rec.SequenceIndex = sequenceIndex
- rec.LookupListIndex = l.lookup_index
-
- def add_subtable_break(self, location):
- self.rules.append(
- ChainContextualRule(
- self.SUBTABLE_BREAK_,
- self.SUBTABLE_BREAK_,
- self.SUBTABLE_BREAK_,
- [self.SUBTABLE_BREAK_],
- )
- )
-
- def newSubtable_(self, chaining=True):
- subtablename = f"Context{self.subtable_type}"
- if chaining:
- subtablename = "Chain" + subtablename
- st = getattr(ot, subtablename)() # ot.ChainContextPos()/ot.ChainSubst()/etc.
- setattr(st, f"{self.subtable_type}Count", 0)
- setattr(st, f"{self.subtable_type}LookupRecord", [])
- return st
-
- # Format 1 and format 2 GSUB5/GSUB6/GPOS7/GPOS8 rulesets and rules form a family:
- #
- # format 1 ruleset format 1 rule format 2 ruleset format 2 rule
- # GSUB5 SubRuleSet SubRule SubClassSet SubClassRule
- # GSUB6 ChainSubRuleSet ChainSubRule ChainSubClassSet ChainSubClassRule
- # GPOS7 PosRuleSet PosRule PosClassSet PosClassRule
- # GPOS8 ChainPosRuleSet ChainPosRule ChainPosClassSet ChainPosClassRule
- #
- # The following functions generate the attribute names and subtables according
- # to this naming convention.
- def ruleSetAttr_(self, format=1, chaining=True):
- if format == 1:
- formatType = "Rule"
- elif format == 2:
- formatType = "Class"
- else:
- raise AssertionError(formatType)
- subtablename = f"{self.subtable_type[0:3]}{formatType}Set" # Sub, not Subst.
- if chaining:
- subtablename = "Chain" + subtablename
- return subtablename
-
- def ruleAttr_(self, format=1, chaining=True):
- if format == 1:
- formatType = ""
- elif format == 2:
- formatType = "Class"
- else:
- raise AssertionError(formatType)
- subtablename = f"{self.subtable_type[0:3]}{formatType}Rule" # Sub, not Subst.
- if chaining:
- subtablename = "Chain" + subtablename
- return subtablename
-
- def newRuleSet_(self, format=1, chaining=True):
- st = getattr(
- ot, self.ruleSetAttr_(format, chaining)
- )() # ot.ChainPosRuleSet()/ot.SubRuleSet()/etc.
- st.populateDefaults()
- return st
-
- def newRule_(self, format=1, chaining=True):
- st = getattr(
- ot, self.ruleAttr_(format, chaining)
- )() # ot.ChainPosClassRule()/ot.SubClassRule()/etc.
- st.populateDefaults()
- return st
-
- def attachSubtableWithCount_(
- self, st, subtable_name, count_name, existing=None, index=None, chaining=False
- ):
- if chaining:
- subtable_name = "Chain" + subtable_name
- count_name = "Chain" + count_name
-
- if not hasattr(st, count_name):
- setattr(st, count_name, 0)
- setattr(st, subtable_name, [])
-
- if existing:
- new_subtable = existing
- else:
- # Create a new, empty subtable from otTables
- new_subtable = getattr(ot, subtable_name)()
-
- setattr(st, count_name, getattr(st, count_name) + 1)
-
- if index:
- getattr(st, subtable_name).insert(index, new_subtable)
- else:
- getattr(st, subtable_name).append(new_subtable)
-
- return new_subtable
-
- def newLookupRecord_(self, st):
- return self.attachSubtableWithCount_(
- st,
- f"{self.subtable_type}LookupRecord",
- f"{self.subtable_type}Count",
- chaining=False,
- ) # Oddly, it isn't ChainSubstLookupRecord
-
-
-class ChainContextPosBuilder(ChainContextualBuilder):
- """Builds a Chained Contextual Positioning (GPOS8) lookup.
-
- Users are expected to manually add rules to the ``rules`` attribute after
- the object has been initialized, e.g.::
-
- # pos [A B] [C D] x' lookup lu1 y' z' lookup lu2 E;
-
- prefix = [ ["A", "B"], ["C", "D"] ]
- suffix = [ ["E"] ]
- glyphs = [ ["x"], ["y"], ["z"] ]
- lookups = [ [lu1], None, [lu2] ]
- builder.rules.append( (prefix, glyphs, suffix, lookups) )
-
- Attributes:
- font (``fontTools.TTLib.TTFont``): A font object.
- location: A string or tuple representing the location in the original
- source which produced this lookup.
- rules: A list of tuples representing the rules in this lookup.
- lookupflag (int): The lookup's flag
- markFilterSet: Either ``None`` if no mark filtering set is used, or
- an integer representing the filtering set to be used for this
- lookup. If a mark filtering set is provided,
- `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
- flags.
- """
-
- def __init__(self, font, location):
- LookupBuilder.__init__(self, font, location, "GPOS", 8)
- self.rules = []
- self.subtable_type = "Pos"
-
- def find_chainable_single_pos(self, lookups, glyphs, value):
- """Helper for add_single_pos_chained_()"""
- res = None
- for lookup in lookups[::-1]:
- if lookup == self.SUBTABLE_BREAK_:
- return res
- if isinstance(lookup, SinglePosBuilder) and all(
- lookup.can_add(glyph, value) for glyph in glyphs
- ):
- res = lookup
- return res
-
-
-class ChainContextSubstBuilder(ChainContextualBuilder):
- """Builds a Chained Contextual Substitution (GSUB6) lookup.
-
- Users are expected to manually add rules to the ``rules`` attribute after
- the object has been initialized, e.g.::
-
- # sub [A B] [C D] x' lookup lu1 y' z' lookup lu2 E;
-
- prefix = [ ["A", "B"], ["C", "D"] ]
- suffix = [ ["E"] ]
- glyphs = [ ["x"], ["y"], ["z"] ]
- lookups = [ [lu1], None, [lu2] ]
- builder.rules.append( (prefix, glyphs, suffix, lookups) )
-
- Attributes:
- font (``fontTools.TTLib.TTFont``): A font object.
- location: A string or tuple representing the location in the original
- source which produced this lookup.
- rules: A list of tuples representing the rules in this lookup.
- lookupflag (int): The lookup's flag
- markFilterSet: Either ``None`` if no mark filtering set is used, or
- an integer representing the filtering set to be used for this
- lookup. If a mark filtering set is provided,
- `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
- flags.
- """
-
- def __init__(self, font, location):
- LookupBuilder.__init__(self, font, location, "GSUB", 6)
- self.rules = [] # (prefix, input, suffix, lookups)
- self.subtable_type = "Subst"
-
- def getAlternateGlyphs(self):
- result = {}
- for rule in self.rules:
- if rule.is_subtable_break:
- continue
- for lookups in rule.lookups:
- if not isinstance(lookups, list):
- lookups = [lookups]
- for lookup in lookups:
- if lookup is not None:
- alts = lookup.getAlternateGlyphs()
- for glyph, replacements in alts.items():
- result.setdefault(glyph, set()).update(replacements)
- return result
-
- def find_chainable_single_subst(self, mapping):
- """Helper for add_single_subst_chained_()"""
- res = None
- for rule in self.rules[::-1]:
- if rule.is_subtable_break:
- return res
- for sub in rule.lookups:
- if isinstance(sub, SingleSubstBuilder) and not any(
- g in mapping and mapping[g] != sub.mapping[g] for g in sub.mapping
- ):
- res = sub
- return res
-
-
-class LigatureSubstBuilder(LookupBuilder):
- """Builds a Ligature Substitution (GSUB4) lookup.
-
- Users are expected to manually add ligatures to the ``ligatures``
- attribute after the object has been initialized, e.g.::
-
- # sub f i by f_i;
- builder.ligatures[("f","f","i")] = "f_f_i"
-
- Attributes:
- font (``fontTools.TTLib.TTFont``): A font object.
- location: A string or tuple representing the location in the original
- source which produced this lookup.
- ligatures: An ordered dictionary mapping a tuple of glyph names to the
- ligature glyphname.
- lookupflag (int): The lookup's flag
- markFilterSet: Either ``None`` if no mark filtering set is used, or
- an integer representing the filtering set to be used for this
- lookup. If a mark filtering set is provided,
- `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
- flags.
- """
-
- def __init__(self, font, location):
- LookupBuilder.__init__(self, font, location, "GSUB", 4)
- self.ligatures = OrderedDict() # {('f','f','i'): 'f_f_i'}
-
- def equals(self, other):
- return LookupBuilder.equals(self, other) and self.ligatures == other.ligatures
-
- def build(self):
- """Build the lookup.
-
- Returns:
- An ``otTables.Lookup`` object representing the ligature
- substitution lookup.
- """
- subtables = self.build_subst_subtables(
- self.ligatures, buildLigatureSubstSubtable
- )
- return self.buildLookup_(subtables)
-
- def add_subtable_break(self, location):
- self.ligatures[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
-
-
-class MultipleSubstBuilder(LookupBuilder):
- """Builds a Multiple Substitution (GSUB2) lookup.
-
- Users are expected to manually add substitutions to the ``mapping``
- attribute after the object has been initialized, e.g.::
-
- # sub uni06C0 by uni06D5.fina hamza.above;
- builder.mapping["uni06C0"] = [ "uni06D5.fina", "hamza.above"]
-
- Attributes:
- font (``fontTools.TTLib.TTFont``): A font object.
- location: A string or tuple representing the location in the original
- source which produced this lookup.
- mapping: An ordered dictionary mapping a glyph name to a list of
- substituted glyph names.
- lookupflag (int): The lookup's flag
- markFilterSet: Either ``None`` if no mark filtering set is used, or
- an integer representing the filtering set to be used for this
- lookup. If a mark filtering set is provided,
- `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
- flags.
- """
-
- def __init__(self, font, location):
- LookupBuilder.__init__(self, font, location, "GSUB", 2)
- self.mapping = OrderedDict()
-
- def equals(self, other):
- return LookupBuilder.equals(self, other) and self.mapping == other.mapping
-
- def build(self):
- subtables = self.build_subst_subtables(self.mapping, buildMultipleSubstSubtable)
- return self.buildLookup_(subtables)
-
- def add_subtable_break(self, location):
- self.mapping[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
-
-
-class CursivePosBuilder(LookupBuilder):
- """Builds a Cursive Positioning (GPOS3) lookup.
-
- Attributes:
- font (``fontTools.TTLib.TTFont``): A font object.
- location: A string or tuple representing the location in the original
- source which produced this lookup.
- attachments: An ordered dictionary mapping a glyph name to a two-element
- tuple of ``otTables.Anchor`` objects.
- lookupflag (int): The lookup's flag
- markFilterSet: Either ``None`` if no mark filtering set is used, or
- an integer representing the filtering set to be used for this
- lookup. If a mark filtering set is provided,
- `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
- flags.
- """
-
- def __init__(self, font, location):
- LookupBuilder.__init__(self, font, location, "GPOS", 3)
- self.attachments = {}
-
- def equals(self, other):
- return (
- LookupBuilder.equals(self, other) and self.attachments == other.attachments
- )
-
- def add_attachment(self, location, glyphs, entryAnchor, exitAnchor):
- """Adds attachment information to the cursive positioning lookup.
-
- Args:
- location: A string or tuple representing the location in the
- original source which produced this lookup. (Unused.)
- glyphs: A list of glyph names sharing these entry and exit
- anchor locations.
- entryAnchor: A ``otTables.Anchor`` object representing the
- entry anchor, or ``None`` if no entry anchor is present.
- exitAnchor: A ``otTables.Anchor`` object representing the
- exit anchor, or ``None`` if no exit anchor is present.
- """
- for glyph in glyphs:
- self.attachments[glyph] = (entryAnchor, exitAnchor)
-
- def build(self):
- """Build the lookup.
-
- Returns:
- An ``otTables.Lookup`` object representing the cursive
- positioning lookup.
- """
- st = buildCursivePosSubtable(self.attachments, self.glyphMap)
- return self.buildLookup_([st])
-
-
-class MarkBasePosBuilder(LookupBuilder):
- """Builds a Mark-To-Base Positioning (GPOS4) lookup.
-
- Users are expected to manually add marks and bases to the ``marks``
- and ``bases`` attributes after the object has been initialized, e.g.::
-
- builder.marks["acute"] = (0, a1)
- builder.marks["grave"] = (0, a1)
- builder.marks["cedilla"] = (1, a2)
- builder.bases["a"] = {0: a3, 1: a5}
- builder.bases["b"] = {0: a4, 1: a5}
-
- Attributes:
- font (``fontTools.TTLib.TTFont``): A font object.
- location: A string or tuple representing the location in the original
- source which produced this lookup.
- marks: An dictionary mapping a glyph name to a two-element
- tuple containing a mark class ID and ``otTables.Anchor`` object.
- bases: An dictionary mapping a glyph name to a dictionary of
- mark class IDs and ``otTables.Anchor`` object.
- lookupflag (int): The lookup's flag
- markFilterSet: Either ``None`` if no mark filtering set is used, or
- an integer representing the filtering set to be used for this
- lookup. If a mark filtering set is provided,
- `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
- flags.
- """
-
- def __init__(self, font, location):
- LookupBuilder.__init__(self, font, location, "GPOS", 4)
- self.marks = {} # glyphName -> (markClassName, anchor)
- self.bases = {} # glyphName -> {markClassName: anchor}
-
- def equals(self, other):
- return (
- LookupBuilder.equals(self, other)
- and self.marks == other.marks
- and self.bases == other.bases
- )
-
- def inferGlyphClasses(self):
- result = {glyph: 1 for glyph in self.bases}
- result.update({glyph: 3 for glyph in self.marks})
- return result
-
- def build(self):
- """Build the lookup.
-
- Returns:
- An ``otTables.Lookup`` object representing the mark-to-base
- positioning lookup.
- """
- markClasses = self.buildMarkClasses_(self.marks)
- marks = {}
- for mark, (mc, anchor) in self.marks.items():
- if mc not in markClasses:
- raise ValueError(
- "Mark class %s not found for mark glyph %s" % (mc, mark)
- )
- marks[mark] = (markClasses[mc], anchor)
- bases = {}
- for glyph, anchors in self.bases.items():
- bases[glyph] = {}
- for mc, anchor in anchors.items():
- if mc not in markClasses:
- raise ValueError(
- "Mark class %s not found for base glyph %s" % (mc, glyph)
- )
- bases[glyph][markClasses[mc]] = anchor
- subtables = buildMarkBasePos(marks, bases, self.glyphMap)
- return self.buildLookup_(subtables)
-
-
-class MarkLigPosBuilder(LookupBuilder):
- """Builds a Mark-To-Ligature Positioning (GPOS5) lookup.
-
- Users are expected to manually add marks and bases to the ``marks``
- and ``ligatures`` attributes after the object has been initialized, e.g.::
-
- builder.marks["acute"] = (0, a1)
- builder.marks["grave"] = (0, a1)
- builder.marks["cedilla"] = (1, a2)
- builder.ligatures["f_i"] = [
- { 0: a3, 1: a5 }, # f
- { 0: a4, 1: a5 } # i
- ]
-
- Attributes:
- font (``fontTools.TTLib.TTFont``): A font object.
- location: A string or tuple representing the location in the original
- source which produced this lookup.
- marks: An dictionary mapping a glyph name to a two-element
- tuple containing a mark class ID and ``otTables.Anchor`` object.
- ligatures: An dictionary mapping a glyph name to an array with one
- element for each ligature component. Each array element should be
- a dictionary mapping mark class IDs to ``otTables.Anchor`` objects.
- lookupflag (int): The lookup's flag
- markFilterSet: Either ``None`` if no mark filtering set is used, or
- an integer representing the filtering set to be used for this
- lookup. If a mark filtering set is provided,
- `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
- flags.
- """
-
- def __init__(self, font, location):
- LookupBuilder.__init__(self, font, location, "GPOS", 5)
- self.marks = {} # glyphName -> (markClassName, anchor)
- self.ligatures = {} # glyphName -> [{markClassName: anchor}, ...]
-
- def equals(self, other):
- return (
- LookupBuilder.equals(self, other)
- and self.marks == other.marks
- and self.ligatures == other.ligatures
- )
-
- def inferGlyphClasses(self):
- result = {glyph: 2 for glyph in self.ligatures}
- result.update({glyph: 3 for glyph in self.marks})
- return result
-
- def build(self):
- """Build the lookup.
-
- Returns:
- An ``otTables.Lookup`` object representing the mark-to-ligature
- positioning lookup.
- """
- markClasses = self.buildMarkClasses_(self.marks)
- marks = {
- mark: (markClasses[mc], anchor) for mark, (mc, anchor) in self.marks.items()
- }
- ligs = {}
- for lig, components in self.ligatures.items():
- ligs[lig] = []
- for c in components:
- ligs[lig].append({markClasses[mc]: a for mc, a in c.items()})
- subtables = buildMarkLigPos(marks, ligs, self.glyphMap)
- return self.buildLookup_(subtables)
-
-
-class MarkMarkPosBuilder(LookupBuilder):
- """Builds a Mark-To-Mark Positioning (GPOS6) lookup.
-
- Users are expected to manually add marks and bases to the ``marks``
- and ``baseMarks`` attributes after the object has been initialized, e.g.::
-
- builder.marks["acute"] = (0, a1)
- builder.marks["grave"] = (0, a1)
- builder.marks["cedilla"] = (1, a2)
- builder.baseMarks["acute"] = {0: a3}
-
- Attributes:
- font (``fontTools.TTLib.TTFont``): A font object.
- location: A string or tuple representing the location in the original
- source which produced this lookup.
- marks: An dictionary mapping a glyph name to a two-element
- tuple containing a mark class ID and ``otTables.Anchor`` object.
- baseMarks: An dictionary mapping a glyph name to a dictionary
- containing one item: a mark class ID and a ``otTables.Anchor`` object.
- lookupflag (int): The lookup's flag
- markFilterSet: Either ``None`` if no mark filtering set is used, or
- an integer representing the filtering set to be used for this
- lookup. If a mark filtering set is provided,
- `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
- flags.
- """
-
- def __init__(self, font, location):
- LookupBuilder.__init__(self, font, location, "GPOS", 6)
- self.marks = {} # glyphName -> (markClassName, anchor)
- self.baseMarks = {} # glyphName -> {markClassName: anchor}
-
- def equals(self, other):
- return (
- LookupBuilder.equals(self, other)
- and self.marks == other.marks
- and self.baseMarks == other.baseMarks
- )
-
- def inferGlyphClasses(self):
- result = {glyph: 3 for glyph in self.baseMarks}
- result.update({glyph: 3 for glyph in self.marks})
- return result
-
- def build(self):
- """Build the lookup.
-
- Returns:
- An ``otTables.Lookup`` object representing the mark-to-mark
- positioning lookup.
- """
- markClasses = self.buildMarkClasses_(self.marks)
- markClassList = sorted(markClasses.keys(), key=markClasses.get)
- marks = {
- mark: (markClasses[mc], anchor) for mark, (mc, anchor) in self.marks.items()
- }
-
- st = ot.MarkMarkPos()
- st.Format = 1
- st.ClassCount = len(markClasses)
- st.Mark1Coverage = buildCoverage(marks, self.glyphMap)
- st.Mark2Coverage = buildCoverage(self.baseMarks, self.glyphMap)
- st.Mark1Array = buildMarkArray(marks, self.glyphMap)
- st.Mark2Array = ot.Mark2Array()
- st.Mark2Array.Mark2Count = len(st.Mark2Coverage.glyphs)
- st.Mark2Array.Mark2Record = []
- for base in st.Mark2Coverage.glyphs:
- anchors = [self.baseMarks[base].get(mc) for mc in markClassList]
- st.Mark2Array.Mark2Record.append(buildMark2Record(anchors))
- return self.buildLookup_([st])
-
-
-class ReverseChainSingleSubstBuilder(LookupBuilder):
- """Builds a Reverse Chaining Contextual Single Substitution (GSUB8) lookup.
-
- Users are expected to manually add substitutions to the ``substitutions``
- attribute after the object has been initialized, e.g.::
-
- # reversesub [a e n] d' by d.alt;
- prefix = [ ["a", "e", "n"] ]
- suffix = []
- mapping = { "d": "d.alt" }
- builder.substitutions.append( (prefix, suffix, mapping) )
-
- Attributes:
- font (``fontTools.TTLib.TTFont``): A font object.
- location: A string or tuple representing the location in the original
- source which produced this lookup.
- substitutions: A three-element tuple consisting of a prefix sequence,
- a suffix sequence, and a dictionary of single substitutions.
- lookupflag (int): The lookup's flag
- markFilterSet: Either ``None`` if no mark filtering set is used, or
- an integer representing the filtering set to be used for this
- lookup. If a mark filtering set is provided,
- `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
- flags.
- """
-
- def __init__(self, font, location):
- LookupBuilder.__init__(self, font, location, "GSUB", 8)
- self.rules = [] # (prefix, suffix, mapping)
-
- def equals(self, other):
- return LookupBuilder.equals(self, other) and self.rules == other.rules
-
- def build(self):
- """Build the lookup.
-
- Returns:
- An ``otTables.Lookup`` object representing the chained
- contextual substitution lookup.
- """
- subtables = []
- for prefix, suffix, mapping in self.rules:
- st = ot.ReverseChainSingleSubst()
- st.Format = 1
- self.setBacktrackCoverage_(prefix, st)
- self.setLookAheadCoverage_(suffix, st)
- st.Coverage = buildCoverage(mapping.keys(), self.glyphMap)
- st.GlyphCount = len(mapping)
- st.Substitute = [mapping[g] for g in st.Coverage.glyphs]
- subtables.append(st)
- return self.buildLookup_(subtables)
-
- def add_subtable_break(self, location):
- # Nothing to do here, each substitution is in its own subtable.
- pass
-
-
-class SingleSubstBuilder(LookupBuilder):
- """Builds a Single Substitution (GSUB1) lookup.
-
- Users are expected to manually add substitutions to the ``mapping``
- attribute after the object has been initialized, e.g.::
-
- # sub x by y;
- builder.mapping["x"] = "y"
-
- Attributes:
- font (``fontTools.TTLib.TTFont``): A font object.
- location: A string or tuple representing the location in the original
- source which produced this lookup.
- mapping: A dictionary mapping a single glyph name to another glyph name.
- lookupflag (int): The lookup's flag
- markFilterSet: Either ``None`` if no mark filtering set is used, or
- an integer representing the filtering set to be used for this
- lookup. If a mark filtering set is provided,
- `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
- flags.
- """
-
- def __init__(self, font, location):
- LookupBuilder.__init__(self, font, location, "GSUB", 1)
- self.mapping = OrderedDict()
-
- def equals(self, other):
- return LookupBuilder.equals(self, other) and self.mapping == other.mapping
-
- def build(self):
- """Build the lookup.
-
- Returns:
- An ``otTables.Lookup`` object representing the multiple
- substitution lookup.
- """
- subtables = self.build_subst_subtables(self.mapping, buildSingleSubstSubtable)
- return self.buildLookup_(subtables)
-
- def getAlternateGlyphs(self):
- return {glyph: set([repl]) for glyph, repl in self.mapping.items()}
-
- def add_subtable_break(self, location):
- self.mapping[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
-
-
-class ClassPairPosSubtableBuilder(object):
- """Builds class-based Pair Positioning (GPOS2 format 2) subtables.
-
- Note that this does *not* build a GPOS2 ``otTables.Lookup`` directly,
- but builds a list of ``otTables.PairPos`` subtables. It is used by the
- :class:`PairPosBuilder` below.
-
- Attributes:
- builder (PairPosBuilder): A pair positioning lookup builder.
- """
-
- def __init__(self, builder):
- self.builder_ = builder
- self.classDef1_, self.classDef2_ = None, None
- self.values_ = {} # (glyphclass1, glyphclass2) --> (value1, value2)
- self.forceSubtableBreak_ = False
- self.subtables_ = []
-
- def addPair(self, gc1, value1, gc2, value2):
- """Add a pair positioning rule.
-
- Args:
- gc1: A set of glyph names for the "left" glyph
- value1: An ``otTables.ValueRecord`` object for the left glyph's
- positioning.
- gc2: A set of glyph names for the "right" glyph
- value2: An ``otTables.ValueRecord`` object for the right glyph's
- positioning.
- """
- mergeable = (
- not self.forceSubtableBreak_
- and self.classDef1_ is not None
- and self.classDef1_.canAdd(gc1)
- and self.classDef2_ is not None
- and self.classDef2_.canAdd(gc2)
- )
- if not mergeable:
- self.flush_()
- self.classDef1_ = ClassDefBuilder(useClass0=True)
- self.classDef2_ = ClassDefBuilder(useClass0=False)
- self.values_ = {}
- self.classDef1_.add(gc1)
- self.classDef2_.add(gc2)
- self.values_[(gc1, gc2)] = (value1, value2)
-
- def addSubtableBreak(self):
- """Add an explicit subtable break at this point."""
- self.forceSubtableBreak_ = True
-
- def subtables(self):
- """Return the list of ``otTables.PairPos`` subtables constructed."""
- self.flush_()
- return self.subtables_
-
- def flush_(self):
- if self.classDef1_ is None or self.classDef2_ is None:
- return
- st = buildPairPosClassesSubtable(self.values_, self.builder_.glyphMap)
- if st.Coverage is None:
- return
- self.subtables_.append(st)
- self.forceSubtableBreak_ = False
-
-
-class PairPosBuilder(LookupBuilder):
- """Builds a Pair Positioning (GPOS2) lookup.
-
- Attributes:
- font (``fontTools.TTLib.TTFont``): A font object.
- location: A string or tuple representing the location in the original
- source which produced this lookup.
- pairs: An array of class-based pair positioning tuples. Usually
- manipulated with the :meth:`addClassPair` method below.
- glyphPairs: A dictionary mapping a tuple of glyph names to a tuple
- of ``otTables.ValueRecord`` objects. Usually manipulated with the
- :meth:`addGlyphPair` method below.
- lookupflag (int): The lookup's flag
- markFilterSet: Either ``None`` if no mark filtering set is used, or
- an integer representing the filtering set to be used for this
- lookup. If a mark filtering set is provided,
- `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
- flags.
- """
-
- def __init__(self, font, location):
- LookupBuilder.__init__(self, font, location, "GPOS", 2)
- self.pairs = [] # [(gc1, value1, gc2, value2)*]
- self.glyphPairs = {} # (glyph1, glyph2) --> (value1, value2)
- self.locations = {} # (gc1, gc2) --> (filepath, line, column)
-
- def addClassPair(self, location, glyphclass1, value1, glyphclass2, value2):
- """Add a class pair positioning rule to the current lookup.
-
- Args:
- location: A string or tuple representing the location in the
- original source which produced this rule. Unused.
- glyphclass1: A set of glyph names for the "left" glyph in the pair.
- value1: A ``otTables.ValueRecord`` for positioning the left glyph.
- glyphclass2: A set of glyph names for the "right" glyph in the pair.
- value2: A ``otTables.ValueRecord`` for positioning the right glyph.
- """
- self.pairs.append((glyphclass1, value1, glyphclass2, value2))
-
- def addGlyphPair(self, location, glyph1, value1, glyph2, value2):
- """Add a glyph pair positioning rule to the current lookup.
-
- Args:
- location: A string or tuple representing the location in the
- original source which produced this rule.
- glyph1: A glyph name for the "left" glyph in the pair.
- value1: A ``otTables.ValueRecord`` for positioning the left glyph.
- glyph2: A glyph name for the "right" glyph in the pair.
- value2: A ``otTables.ValueRecord`` for positioning the right glyph.
- """
- key = (glyph1, glyph2)
- oldValue = self.glyphPairs.get(key, None)
- if oldValue is not None:
- # the Feature File spec explicitly allows specific pairs generated
- # by an 'enum' rule to be overridden by preceding single pairs
- otherLoc = self.locations[key]
- log.debug(
- "Already defined position for pair %s %s at %s; "
- "choosing the first value",
- glyph1,
- glyph2,
- otherLoc,
- )
- else:
- self.glyphPairs[key] = (value1, value2)
- self.locations[key] = location
-
- def add_subtable_break(self, location):
- self.pairs.append(
- (
- self.SUBTABLE_BREAK_,
- self.SUBTABLE_BREAK_,
- self.SUBTABLE_BREAK_,
- self.SUBTABLE_BREAK_,
- )
- )
-
- def equals(self, other):
- return (
- LookupBuilder.equals(self, other)
- and self.glyphPairs == other.glyphPairs
- and self.pairs == other.pairs
- )
-
- def build(self):
- """Build the lookup.
-
- Returns:
- An ``otTables.Lookup`` object representing the pair positioning
- lookup.
- """
- builders = {}
- builder = ClassPairPosSubtableBuilder(self)
- for glyphclass1, value1, glyphclass2, value2 in self.pairs:
- if glyphclass1 is self.SUBTABLE_BREAK_:
- builder.addSubtableBreak()
- continue
- builder.addPair(glyphclass1, value1, glyphclass2, value2)
- subtables = []
- if self.glyphPairs:
- subtables.extend(buildPairPosGlyphs(self.glyphPairs, self.glyphMap))
- subtables.extend(builder.subtables())
- lookup = self.buildLookup_(subtables)
-
- # Compact the lookup
- # This is a good moment to do it because the compaction should create
- # smaller subtables, which may prevent overflows from happening.
- # Keep reading the value from the ENV until ufo2ft switches to the config system
- level = self.font.cfg.get(
- "fontTools.otlLib.optimize.gpos:COMPRESSION_LEVEL",
- default=_compression_level_from_env(),
- )
- if level != 0:
- log.info("Compacting GPOS...")
- compact_lookup(self.font, level, lookup)
-
- return lookup
-
-
-class SinglePosBuilder(LookupBuilder):
- """Builds a Single Positioning (GPOS1) lookup.
-
- Attributes:
- font (``fontTools.TTLib.TTFont``): A font object.
- location: A string or tuple representing the location in the original
- source which produced this lookup.
- mapping: A dictionary mapping a glyph name to a ``otTables.ValueRecord``
- objects. Usually manipulated with the :meth:`add_pos` method below.
- lookupflag (int): The lookup's flag
- markFilterSet: Either ``None`` if no mark filtering set is used, or
- an integer representing the filtering set to be used for this
- lookup. If a mark filtering set is provided,
- `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
- flags.
- """
-
- def __init__(self, font, location):
- LookupBuilder.__init__(self, font, location, "GPOS", 1)
- self.locations = {} # glyph -> (filename, line, column)
- self.mapping = {} # glyph -> ot.ValueRecord
-
- def add_pos(self, location, glyph, otValueRecord):
- """Add a single positioning rule.
-
- Args:
- location: A string or tuple representing the location in the
- original source which produced this lookup.
- glyph: A glyph name.
- otValueRection: A ``otTables.ValueRecord`` used to position the
- glyph.
- """
- if not self.can_add(glyph, otValueRecord):
- otherLoc = self.locations[glyph]
- raise OpenTypeLibError(
- 'Already defined different position for glyph "%s" at %s'
- % (glyph, otherLoc),
- location,
- )
- if otValueRecord:
- self.mapping[glyph] = otValueRecord
- self.locations[glyph] = location
-
- def can_add(self, glyph, value):
- assert isinstance(value, ValueRecord)
- curValue = self.mapping.get(glyph)
- return curValue is None or curValue == value
-
- def equals(self, other):
- return LookupBuilder.equals(self, other) and self.mapping == other.mapping
-
- def build(self):
- """Build the lookup.
-
- Returns:
- An ``otTables.Lookup`` object representing the single positioning
- lookup.
- """
- subtables = buildSinglePos(self.mapping, self.glyphMap)
- return self.buildLookup_(subtables)
-
-
-# GSUB
-
-
-def buildSingleSubstSubtable(mapping):
- """Builds a single substitution (GSUB1) subtable.
-
- Note that if you are implementing a layout compiler, you may find it more
- flexible to use
- :py:class:`fontTools.otlLib.lookupBuilders.SingleSubstBuilder` instead.
-
- Args:
- mapping: A dictionary mapping input glyph names to output glyph names.
-
- Returns:
- An ``otTables.SingleSubst`` object, or ``None`` if the mapping dictionary
- is empty.
- """
- if not mapping:
- return None
- self = ot.SingleSubst()
- self.mapping = dict(mapping)
- return self
-
-
-def buildMultipleSubstSubtable(mapping):
- """Builds a multiple substitution (GSUB2) subtable.
-
- Note that if you are implementing a layout compiler, you may find it more
- flexible to use
- :py:class:`fontTools.otlLib.lookupBuilders.MultipleSubstBuilder` instead.
-
- Example::
-
- # sub uni06C0 by uni06D5.fina hamza.above
- # sub uni06C2 by uni06C1.fina hamza.above;
-
- subtable = buildMultipleSubstSubtable({
- "uni06C0": [ "uni06D5.fina", "hamza.above"],
- "uni06C2": [ "uni06D1.fina", "hamza.above"]
- })
-
- Args:
- mapping: A dictionary mapping input glyph names to a list of output
- glyph names.
-
- Returns:
- An ``otTables.MultipleSubst`` object or ``None`` if the mapping dictionary
- is empty.
- """
- if not mapping:
- return None
- self = ot.MultipleSubst()
- self.mapping = dict(mapping)
- return self
-
-
-def buildAlternateSubstSubtable(mapping):
- """Builds an alternate substitution (GSUB3) subtable.
-
- Note that if you are implementing a layout compiler, you may find it more
- flexible to use
- :py:class:`fontTools.otlLib.lookupBuilders.AlternateSubstBuilder` instead.
-
- Args:
- mapping: A dictionary mapping input glyph names to a list of output
- glyph names.
-
- Returns:
- An ``otTables.AlternateSubst`` object or ``None`` if the mapping dictionary
- is empty.
- """
- if not mapping:
- return None
- self = ot.AlternateSubst()
- self.alternates = dict(mapping)
- return self
-
-
-def _getLigatureKey(components):
- # Computes a key for ordering ligatures in a GSUB Type-4 lookup.
-
- # When building the OpenType lookup, we need to make sure that
- # the longest sequence of components is listed first, so we
- # use the negative length as the primary key for sorting.
- # To make buildLigatureSubstSubtable() deterministic, we use the
- # component sequence as the secondary key.
-
- # For example, this will sort (f,f,f) < (f,f,i) < (f,f) < (f,i) < (f,l).
- return (-len(components), components)
-
-
-def buildLigatureSubstSubtable(mapping):
- """Builds a ligature substitution (GSUB4) subtable.
-
- Note that if you are implementing a layout compiler, you may find it more
- flexible to use
- :py:class:`fontTools.otlLib.lookupBuilders.LigatureSubstBuilder` instead.
-
- Example::
-
- # sub f f i by f_f_i;
- # sub f i by f_i;
-
- subtable = buildLigatureSubstSubtable({
- ("f", "f", "i"): "f_f_i",
- ("f", "i"): "f_i",
- })
-
- Args:
- mapping: A dictionary mapping tuples of glyph names to output
- glyph names.
-
- Returns:
- An ``otTables.LigatureSubst`` object or ``None`` if the mapping dictionary
- is empty.
- """
-
- if not mapping:
- return None
- self = ot.LigatureSubst()
- # The following single line can replace the rest of this function
- # with fontTools >= 3.1:
- # self.ligatures = dict(mapping)
- self.ligatures = {}
- for components in sorted(mapping.keys(), key=_getLigatureKey):
- ligature = ot.Ligature()
- ligature.Component = components[1:]
- ligature.CompCount = len(ligature.Component) + 1
- ligature.LigGlyph = mapping[components]
- firstGlyph = components[0]
- self.ligatures.setdefault(firstGlyph, []).append(ligature)
- return self
-
-
-# GPOS
-
-
-def buildAnchor(x, y, point=None, deviceX=None, deviceY=None):
- """Builds an Anchor table.
-
- This determines the appropriate anchor format based on the passed parameters.
-
- Args:
- x (int): X coordinate.
- y (int): Y coordinate.
- point (int): Index of glyph contour point, if provided.
- deviceX (``otTables.Device``): X coordinate device table, if provided.
- deviceY (``otTables.Device``): Y coordinate device table, if provided.
-
- Returns:
- An ``otTables.Anchor`` object.
- """
- self = ot.Anchor()
- self.XCoordinate, self.YCoordinate = x, y
- self.Format = 1
- if point is not None:
- self.AnchorPoint = point
- self.Format = 2
- if deviceX is not None or deviceY is not None:
- assert (
- self.Format == 1
- ), "Either point, or both of deviceX/deviceY, must be None."
- self.XDeviceTable = deviceX
- self.YDeviceTable = deviceY
- self.Format = 3
- return self
-
-
-def buildBaseArray(bases, numMarkClasses, glyphMap):
- """Builds a base array record.
-
- As part of building mark-to-base positioning rules, you will need to define
- a ``BaseArray`` record, which "defines for each base glyph an array of
- anchors, one for each mark class." This function builds the base array
- subtable.
-
- Example::
-
- bases = {"a": {0: a3, 1: a5}, "b": {0: a4, 1: a5}}
- basearray = buildBaseArray(bases, 2, font.getReverseGlyphMap())
-
- Args:
- bases (dict): A dictionary mapping anchors to glyphs; the keys being
- glyph names, and the values being dictionaries mapping mark class ID
- to the appropriate ``otTables.Anchor`` object used for attaching marks
- of that class.
- numMarkClasses (int): The total number of mark classes for which anchors
- are defined.
- glyphMap: a glyph name to ID map, typically returned from
- ``font.getReverseGlyphMap()``.
-
- Returns:
- An ``otTables.BaseArray`` object.
- """
- self = ot.BaseArray()
- self.BaseRecord = []
- for base in sorted(bases, key=glyphMap.__getitem__):
- b = bases[base]
- anchors = [b.get(markClass) for markClass in range(numMarkClasses)]
- self.BaseRecord.append(buildBaseRecord(anchors))
- self.BaseCount = len(self.BaseRecord)
- return self
-
-
-def buildBaseRecord(anchors):
- # [otTables.Anchor, otTables.Anchor, ...] --> otTables.BaseRecord
- self = ot.BaseRecord()
- self.BaseAnchor = anchors
- return self
-
-
-def buildComponentRecord(anchors):
- """Builds a component record.
-
- As part of building mark-to-ligature positioning rules, you will need to
- define ``ComponentRecord`` objects, which contain "an array of offsets...
- to the Anchor tables that define all the attachment points used to attach
- marks to the component." This function builds the component record.
-
- Args:
- anchors: A list of ``otTables.Anchor`` objects or ``None``.
-
- Returns:
- A ``otTables.ComponentRecord`` object or ``None`` if no anchors are
- supplied.
- """
- if not anchors:
- return None
- self = ot.ComponentRecord()
- self.LigatureAnchor = anchors
- return self
-
-
-def buildCursivePosSubtable(attach, glyphMap):
- """Builds a cursive positioning (GPOS3) subtable.
-
- Cursive positioning lookups are made up of a coverage table of glyphs,
- and a set of ``EntryExitRecord`` records containing the anchors for
- each glyph. This function builds the cursive positioning subtable.
-
- Example::
-
- subtable = buildCursivePosSubtable({
- "AlifIni": (None, buildAnchor(0, 50)),
- "BehMed": (buildAnchor(500,250), buildAnchor(0,50)),
- # ...
- }, font.getReverseGlyphMap())
-
- Args:
- attach (dict): A mapping between glyph names and a tuple of two
- ``otTables.Anchor`` objects representing entry and exit anchors.
- glyphMap: a glyph name to ID map, typically returned from
- ``font.getReverseGlyphMap()``.
-
- Returns:
- An ``otTables.CursivePos`` object, or ``None`` if the attachment
- dictionary was empty.
- """
- if not attach:
- return None
- self = ot.CursivePos()
- self.Format = 1
- self.Coverage = buildCoverage(attach.keys(), glyphMap)
- self.EntryExitRecord = []
- for glyph in self.Coverage.glyphs:
- entryAnchor, exitAnchor = attach[glyph]
- rec = ot.EntryExitRecord()
- rec.EntryAnchor = entryAnchor
- rec.ExitAnchor = exitAnchor
- self.EntryExitRecord.append(rec)
- self.EntryExitCount = len(self.EntryExitRecord)
- return self
-
-
-def buildDevice(deltas):
- """Builds a Device record as part of a ValueRecord or Anchor.
-
- Device tables specify size-specific adjustments to value records
- and anchors to reflect changes based on the resolution of the output.
- For example, one could specify that an anchor's Y position should be
- increased by 1 pixel when displayed at 8 pixels per em. This routine
- builds device records.
-
- Args:
- deltas: A dictionary mapping pixels-per-em sizes to the delta
- adjustment in pixels when the font is displayed at that size.
-
- Returns:
- An ``otTables.Device`` object if any deltas were supplied, or
- ``None`` otherwise.
- """
- if not deltas:
- return None
- self = ot.Device()
- keys = deltas.keys()
- self.StartSize = startSize = min(keys)
- self.EndSize = endSize = max(keys)
- assert 0 <= startSize <= endSize
- self.DeltaValue = deltaValues = [
- deltas.get(size, 0) for size in range(startSize, endSize + 1)
- ]
- maxDelta = max(deltaValues)
- minDelta = min(deltaValues)
- assert minDelta > -129 and maxDelta < 128
- if minDelta > -3 and maxDelta < 2:
- self.DeltaFormat = 1
- elif minDelta > -9 and maxDelta < 8:
- self.DeltaFormat = 2
- else:
- self.DeltaFormat = 3
- return self
-
-
-def buildLigatureArray(ligs, numMarkClasses, glyphMap):
- """Builds a LigatureArray subtable.
-
- As part of building a mark-to-ligature lookup, you will need to define
- the set of anchors (for each mark class) on each component of the ligature
- where marks can be attached. For example, for an Arabic divine name ligature
- (lam lam heh), you may want to specify mark attachment positioning for
- superior marks (fatha, etc.) and inferior marks (kasra, etc.) on each glyph
- of the ligature. This routine builds the ligature array record.
-
- Example::
-
- buildLigatureArray({
- "lam-lam-heh": [
- { 0: superiorAnchor1, 1: inferiorAnchor1 }, # attach points for lam1
- { 0: superiorAnchor2, 1: inferiorAnchor2 }, # attach points for lam2
- { 0: superiorAnchor3, 1: inferiorAnchor3 }, # attach points for heh
- ]
- }, 2, font.getReverseGlyphMap())
-
- Args:
- ligs (dict): A mapping of ligature names to an array of dictionaries:
- for each component glyph in the ligature, an dictionary mapping
- mark class IDs to anchors.
- numMarkClasses (int): The number of mark classes.
- glyphMap: a glyph name to ID map, typically returned from
- ``font.getReverseGlyphMap()``.
-
- Returns:
- An ``otTables.LigatureArray`` object if deltas were supplied.
- """
- self = ot.LigatureArray()
- self.LigatureAttach = []
- for lig in sorted(ligs, key=glyphMap.__getitem__):
- anchors = []
- for component in ligs[lig]:
- anchors.append([component.get(mc) for mc in range(numMarkClasses)])
- self.LigatureAttach.append(buildLigatureAttach(anchors))
- self.LigatureCount = len(self.LigatureAttach)
- return self
-
-
-def buildLigatureAttach(components):
- # [[Anchor, Anchor], [Anchor, Anchor, Anchor]] --> LigatureAttach
- self = ot.LigatureAttach()
- self.ComponentRecord = [buildComponentRecord(c) for c in components]
- self.ComponentCount = len(self.ComponentRecord)
- return self
-
-
-def buildMarkArray(marks, glyphMap):
- """Builds a mark array subtable.
-
- As part of building mark-to-* positioning rules, you will need to define
- a MarkArray subtable, which "defines the class and the anchor point
- for a mark glyph." This function builds the mark array subtable.
-
- Example::
-
- mark = {
- "acute": (0, buildAnchor(300,712)),
- # ...
- }
- markarray = buildMarkArray(marks, font.getReverseGlyphMap())
-
- Args:
- marks (dict): A dictionary mapping anchors to glyphs; the keys being
- glyph names, and the values being a tuple of mark class number and
- an ``otTables.Anchor`` object representing the mark's attachment
- point.
- glyphMap: a glyph name to ID map, typically returned from
- ``font.getReverseGlyphMap()``.
-
- Returns:
- An ``otTables.MarkArray`` object.
- """
- self = ot.MarkArray()
- self.MarkRecord = []
- for mark in sorted(marks.keys(), key=glyphMap.__getitem__):
- markClass, anchor = marks[mark]
- markrec = buildMarkRecord(markClass, anchor)
- self.MarkRecord.append(markrec)
- self.MarkCount = len(self.MarkRecord)
- return self
-
-
-def buildMarkBasePos(marks, bases, glyphMap):
- """Build a list of MarkBasePos (GPOS4) subtables.
-
- This routine turns a set of marks and bases into a list of mark-to-base
- positioning subtables. Currently the list will contain a single subtable
- containing all marks and bases, although at a later date it may return the
- optimal list of subtables subsetting the marks and bases into groups which
- save space. See :func:`buildMarkBasePosSubtable` below.
-
- Note that if you are implementing a layout compiler, you may find it more
- flexible to use
- :py:class:`fontTools.otlLib.lookupBuilders.MarkBasePosBuilder` instead.
-
- Example::
-
- # a1, a2, a3, a4, a5 = buildAnchor(500, 100), ...
-
- marks = {"acute": (0, a1), "grave": (0, a1), "cedilla": (1, a2)}
- bases = {"a": {0: a3, 1: a5}, "b": {0: a4, 1: a5}}
- markbaseposes = buildMarkBasePos(marks, bases, font.getReverseGlyphMap())
-
- Args:
- marks (dict): A dictionary mapping anchors to glyphs; the keys being
- glyph names, and the values being a tuple of mark class number and
- an ``otTables.Anchor`` object representing the mark's attachment
- point. (See :func:`buildMarkArray`.)
- bases (dict): A dictionary mapping anchors to glyphs; the keys being
- glyph names, and the values being dictionaries mapping mark class ID
- to the appropriate ``otTables.Anchor`` object used for attaching marks
- of that class. (See :func:`buildBaseArray`.)
- glyphMap: a glyph name to ID map, typically returned from
- ``font.getReverseGlyphMap()``.
-
- Returns:
- A list of ``otTables.MarkBasePos`` objects.
- """
- # TODO: Consider emitting multiple subtables to save space.
- # Partition the marks and bases into disjoint subsets, so that
- # MarkBasePos rules would only access glyphs from a single
- # subset. This would likely lead to smaller mark/base
- # matrices, so we might be able to omit many of the empty
- # anchor tables that we currently produce. Of course, this
- # would only work if the MarkBasePos rules of real-world fonts
- # allow partitioning into multiple subsets. We should find out
- # whether this is the case; if so, implement the optimization.
- # On the other hand, a very large number of subtables could
- # slow down layout engines; so this would need profiling.
- return [buildMarkBasePosSubtable(marks, bases, glyphMap)]
-
-
-def buildMarkBasePosSubtable(marks, bases, glyphMap):
- """Build a single MarkBasePos (GPOS4) subtable.
-
- This builds a mark-to-base lookup subtable containing all of the referenced
- marks and bases. See :func:`buildMarkBasePos`.
-
- Args:
- marks (dict): A dictionary mapping anchors to glyphs; the keys being
- glyph names, and the values being a tuple of mark class number and
- an ``otTables.Anchor`` object representing the mark's attachment
- point. (See :func:`buildMarkArray`.)
- bases (dict): A dictionary mapping anchors to glyphs; the keys being
- glyph names, and the values being dictionaries mapping mark class ID
- to the appropriate ``otTables.Anchor`` object used for attaching marks
- of that class. (See :func:`buildBaseArray`.)
- glyphMap: a glyph name to ID map, typically returned from
- ``font.getReverseGlyphMap()``.
-
- Returns:
- A ``otTables.MarkBasePos`` object.
- """
- self = ot.MarkBasePos()
- self.Format = 1
- self.MarkCoverage = buildCoverage(marks, glyphMap)
- self.MarkArray = buildMarkArray(marks, glyphMap)
- self.ClassCount = max([mc for mc, _ in marks.values()]) + 1
- self.BaseCoverage = buildCoverage(bases, glyphMap)
- self.BaseArray = buildBaseArray(bases, self.ClassCount, glyphMap)
- return self
-
-
-def buildMarkLigPos(marks, ligs, glyphMap):
- """Build a list of MarkLigPos (GPOS5) subtables.
-
- This routine turns a set of marks and ligatures into a list of mark-to-ligature
- positioning subtables. Currently the list will contain a single subtable
- containing all marks and ligatures, although at a later date it may return
- the optimal list of subtables subsetting the marks and ligatures into groups
- which save space. See :func:`buildMarkLigPosSubtable` below.
-
- Note that if you are implementing a layout compiler, you may find it more
- flexible to use
- :py:class:`fontTools.otlLib.lookupBuilders.MarkLigPosBuilder` instead.
-
- Example::
-
- # a1, a2, a3, a4, a5 = buildAnchor(500, 100), ...
- marks = {
- "acute": (0, a1),
- "grave": (0, a1),
- "cedilla": (1, a2)
- }
- ligs = {
- "f_i": [
- { 0: a3, 1: a5 }, # f
- { 0: a4, 1: a5 } # i
- ],
- # "c_t": [{...}, {...}]
- }
- markligposes = buildMarkLigPos(marks, ligs,
- font.getReverseGlyphMap())
-
- Args:
- marks (dict): A dictionary mapping anchors to glyphs; the keys being
- glyph names, and the values being a tuple of mark class number and
- an ``otTables.Anchor`` object representing the mark's attachment
- point. (See :func:`buildMarkArray`.)
- ligs (dict): A mapping of ligature names to an array of dictionaries:
- for each component glyph in the ligature, an dictionary mapping
- mark class IDs to anchors. (See :func:`buildLigatureArray`.)
- glyphMap: a glyph name to ID map, typically returned from
- ``font.getReverseGlyphMap()``.
-
- Returns:
- A list of ``otTables.MarkLigPos`` objects.
-
- """
- # TODO: Consider splitting into multiple subtables to save space,
- # as with MarkBasePos, this would be a trade-off that would need
- # profiling. And, depending on how typical fonts are structured,
- # it might not be worth doing at all.
- return [buildMarkLigPosSubtable(marks, ligs, glyphMap)]
-
-
-def buildMarkLigPosSubtable(marks, ligs, glyphMap):
- """Build a single MarkLigPos (GPOS5) subtable.
-
- This builds a mark-to-base lookup subtable containing all of the referenced
- marks and bases. See :func:`buildMarkLigPos`.
-
- Args:
- marks (dict): A dictionary mapping anchors to glyphs; the keys being
- glyph names, and the values being a tuple of mark class number and
- an ``otTables.Anchor`` object representing the mark's attachment
- point. (See :func:`buildMarkArray`.)
- ligs (dict): A mapping of ligature names to an array of dictionaries:
- for each component glyph in the ligature, an dictionary mapping
- mark class IDs to anchors. (See :func:`buildLigatureArray`.)
- glyphMap: a glyph name to ID map, typically returned from
- ``font.getReverseGlyphMap()``.
-
- Returns:
- A ``otTables.MarkLigPos`` object.
- """
- self = ot.MarkLigPos()
- self.Format = 1
- self.MarkCoverage = buildCoverage(marks, glyphMap)
- self.MarkArray = buildMarkArray(marks, glyphMap)
- self.ClassCount = max([mc for mc, _ in marks.values()]) + 1
- self.LigatureCoverage = buildCoverage(ligs, glyphMap)
- self.LigatureArray = buildLigatureArray(ligs, self.ClassCount, glyphMap)
- return self
-
-
-def buildMarkRecord(classID, anchor):
- assert isinstance(classID, int)
- assert isinstance(anchor, ot.Anchor)
- self = ot.MarkRecord()
- self.Class = classID
- self.MarkAnchor = anchor
- return self
-
-
-def buildMark2Record(anchors):
- # [otTables.Anchor, otTables.Anchor, ...] --> otTables.Mark2Record
- self = ot.Mark2Record()
- self.Mark2Anchor = anchors
- return self
-
-
-def _getValueFormat(f, values, i):
- # Helper for buildPairPos{Glyphs|Classes}Subtable.
- if f is not None:
- return f
- mask = 0
- for value in values:
- if value is not None and value[i] is not None:
- mask |= value[i].getFormat()
- return mask
-
-
-def buildPairPosClassesSubtable(pairs, glyphMap, valueFormat1=None, valueFormat2=None):
- """Builds a class pair adjustment (GPOS2 format 2) subtable.
-
- Kerning tables are generally expressed as pair positioning tables using
- class-based pair adjustments. This routine builds format 2 PairPos
- subtables.
-
- Note that if you are implementing a layout compiler, you may find it more
- flexible to use
- :py:class:`fontTools.otlLib.lookupBuilders.ClassPairPosSubtableBuilder`
- instead, as this takes care of ensuring that the supplied pairs can be
- formed into non-overlapping classes and emitting individual subtables
- whenever the non-overlapping requirement means that a new subtable is
- required.
-
- Example::
-
- pairs = {}
-
- pairs[(
- [ "K", "X" ],
- [ "W", "V" ]
- )] = ( buildValue(xAdvance=+5), buildValue() )
- # pairs[(... , ...)] = (..., ...)
-
- pairpos = buildPairPosClassesSubtable(pairs, font.getReverseGlyphMap())
-
- Args:
- pairs (dict): Pair positioning data; the keys being a two-element
- tuple of lists of glyphnames, and the values being a two-element
- tuple of ``otTables.ValueRecord`` objects.
- glyphMap: a glyph name to ID map, typically returned from
- ``font.getReverseGlyphMap()``.
- valueFormat1: Force the "left" value records to the given format.
- valueFormat2: Force the "right" value records to the given format.
-
- Returns:
- A ``otTables.PairPos`` object.
- """
- coverage = set()
- classDef1 = ClassDefBuilder(useClass0=True)
- classDef2 = ClassDefBuilder(useClass0=False)
- for gc1, gc2 in sorted(pairs):
- coverage.update(gc1)
- classDef1.add(gc1)
- classDef2.add(gc2)
- self = ot.PairPos()
- self.Format = 2
- valueFormat1 = self.ValueFormat1 = _getValueFormat(valueFormat1, pairs.values(), 0)
- valueFormat2 = self.ValueFormat2 = _getValueFormat(valueFormat2, pairs.values(), 1)
- self.Coverage = buildCoverage(coverage, glyphMap)
- self.ClassDef1 = classDef1.build()
- self.ClassDef2 = classDef2.build()
- classes1 = classDef1.classes()
- classes2 = classDef2.classes()
- self.Class1Record = []
- for c1 in classes1:
- rec1 = ot.Class1Record()
- rec1.Class2Record = []
- self.Class1Record.append(rec1)
- for c2 in classes2:
- rec2 = ot.Class2Record()
- val1, val2 = pairs.get((c1, c2), (None, None))
- rec2.Value1 = (
- ValueRecord(src=val1, valueFormat=valueFormat1)
- if valueFormat1
- else None
- )
- rec2.Value2 = (
- ValueRecord(src=val2, valueFormat=valueFormat2)
- if valueFormat2
- else None
- )
- rec1.Class2Record.append(rec2)
- self.Class1Count = len(self.Class1Record)
- self.Class2Count = len(classes2)
- return self
-
-
-def buildPairPosGlyphs(pairs, glyphMap):
- """Builds a list of glyph-based pair adjustment (GPOS2 format 1) subtables.
-
- This organises a list of pair positioning adjustments into subtables based
- on common value record formats.
-
- Note that if you are implementing a layout compiler, you may find it more
- flexible to use
- :py:class:`fontTools.otlLib.lookupBuilders.PairPosBuilder`
- instead.
-
- Example::
-
- pairs = {
- ("K", "W"): ( buildValue(xAdvance=+5), buildValue() ),
- ("K", "V"): ( buildValue(xAdvance=+5), buildValue() ),
- # ...
- }
-
- subtables = buildPairPosGlyphs(pairs, font.getReverseGlyphMap())
-
- Args:
- pairs (dict): Pair positioning data; the keys being a two-element
- tuple of glyphnames, and the values being a two-element
- tuple of ``otTables.ValueRecord`` objects.
- glyphMap: a glyph name to ID map, typically returned from
- ``font.getReverseGlyphMap()``.
-
- Returns:
- A list of ``otTables.PairPos`` objects.
- """
-
- p = {} # (formatA, formatB) --> {(glyphA, glyphB): (valA, valB)}
- for (glyphA, glyphB), (valA, valB) in pairs.items():
- formatA = valA.getFormat() if valA is not None else 0
- formatB = valB.getFormat() if valB is not None else 0
- pos = p.setdefault((formatA, formatB), {})
- pos[(glyphA, glyphB)] = (valA, valB)
- return [
- buildPairPosGlyphsSubtable(pos, glyphMap, formatA, formatB)
- for ((formatA, formatB), pos) in sorted(p.items())
- ]
-
-
-def buildPairPosGlyphsSubtable(pairs, glyphMap, valueFormat1=None, valueFormat2=None):
- """Builds a single glyph-based pair adjustment (GPOS2 format 1) subtable.
-
- This builds a PairPos subtable from a dictionary of glyph pairs and
- their positioning adjustments. See also :func:`buildPairPosGlyphs`.
-
- Note that if you are implementing a layout compiler, you may find it more
- flexible to use
- :py:class:`fontTools.otlLib.lookupBuilders.PairPosBuilder` instead.
-
- Example::
-
- pairs = {
- ("K", "W"): ( buildValue(xAdvance=+5), buildValue() ),
- ("K", "V"): ( buildValue(xAdvance=+5), buildValue() ),
- # ...
- }
-
- pairpos = buildPairPosGlyphsSubtable(pairs, font.getReverseGlyphMap())
-
- Args:
- pairs (dict): Pair positioning data; the keys being a two-element
- tuple of glyphnames, and the values being a two-element
- tuple of ``otTables.ValueRecord`` objects.
- glyphMap: a glyph name to ID map, typically returned from
- ``font.getReverseGlyphMap()``.
- valueFormat1: Force the "left" value records to the given format.
- valueFormat2: Force the "right" value records to the given format.
-
- Returns:
- A ``otTables.PairPos`` object.
- """
- self = ot.PairPos()
- self.Format = 1
- valueFormat1 = self.ValueFormat1 = _getValueFormat(valueFormat1, pairs.values(), 0)
- valueFormat2 = self.ValueFormat2 = _getValueFormat(valueFormat2, pairs.values(), 1)
- p = {}
- for (glyphA, glyphB), (valA, valB) in pairs.items():
- p.setdefault(glyphA, []).append((glyphB, valA, valB))
- self.Coverage = buildCoverage({g for g, _ in pairs.keys()}, glyphMap)
- self.PairSet = []
- for glyph in self.Coverage.glyphs:
- ps = ot.PairSet()
- ps.PairValueRecord = []
- self.PairSet.append(ps)
- for glyph2, val1, val2 in sorted(p[glyph], key=lambda x: glyphMap[x[0]]):
- pvr = ot.PairValueRecord()
- pvr.SecondGlyph = glyph2
- pvr.Value1 = (
- ValueRecord(src=val1, valueFormat=valueFormat1)
- if valueFormat1
- else None
- )
- pvr.Value2 = (
- ValueRecord(src=val2, valueFormat=valueFormat2)
- if valueFormat2
- else None
- )
- ps.PairValueRecord.append(pvr)
- ps.PairValueCount = len(ps.PairValueRecord)
- self.PairSetCount = len(self.PairSet)
- return self
-
-
-def buildSinglePos(mapping, glyphMap):
- """Builds a list of single adjustment (GPOS1) subtables.
-
- This builds a list of SinglePos subtables from a dictionary of glyph
- names and their positioning adjustments. The format of the subtables are
- determined to optimize the size of the resulting subtables.
- See also :func:`buildSinglePosSubtable`.
-
- Note that if you are implementing a layout compiler, you may find it more
- flexible to use
- :py:class:`fontTools.otlLib.lookupBuilders.SinglePosBuilder` instead.
-
- Example::
-
- mapping = {
- "V": buildValue({ "xAdvance" : +5 }),
- # ...
- }
-
- subtables = buildSinglePos(pairs, font.getReverseGlyphMap())
-
- Args:
- mapping (dict): A mapping between glyphnames and
- ``otTables.ValueRecord`` objects.
- glyphMap: a glyph name to ID map, typically returned from
- ``font.getReverseGlyphMap()``.
-
- Returns:
- A list of ``otTables.SinglePos`` objects.
- """
- result, handled = [], set()
- # In SinglePos format 1, the covered glyphs all share the same ValueRecord.
- # In format 2, each glyph has its own ValueRecord, but these records
- # all have the same properties (eg., all have an X but no Y placement).
- coverages, masks, values = {}, {}, {}
- for glyph, value in mapping.items():
- key = _getSinglePosValueKey(value)
- coverages.setdefault(key, []).append(glyph)
- masks.setdefault(key[0], []).append(key)
- values[key] = value
-
- # If a ValueRecord is shared between multiple glyphs, we generate
- # a SinglePos format 1 subtable; that is the most compact form.
- for key, glyphs in coverages.items():
- # 5 ushorts is the length of introducing another sublookup
- if len(glyphs) * _getSinglePosValueSize(key) > 5:
- format1Mapping = {g: values[key] for g in glyphs}
- result.append(buildSinglePosSubtable(format1Mapping, glyphMap))
- handled.add(key)
-
- # In the remaining ValueRecords, look for those whose valueFormat
- # (the set of used properties) is shared between multiple records.
- # These will get encoded in format 2.
- for valueFormat, keys in masks.items():
- f2 = [k for k in keys if k not in handled]
- if len(f2) > 1:
- format2Mapping = {}
- for k in f2:
- format2Mapping.update((g, values[k]) for g in coverages[k])
- result.append(buildSinglePosSubtable(format2Mapping, glyphMap))
- handled.update(f2)
-
- # The remaining ValueRecords are only used by a few glyphs, normally
- # one. We encode these in format 1 again.
- for key, glyphs in coverages.items():
- if key not in handled:
- for g in glyphs:
- st = buildSinglePosSubtable({g: values[key]}, glyphMap)
- result.append(st)
-
- # When the OpenType layout engine traverses the subtables, it will
- # stop after the first matching subtable. Therefore, we sort the
- # resulting subtables by decreasing coverage size; this increases
- # the chance that the layout engine can do an early exit. (Of course,
- # this would only be true if all glyphs were equally frequent, which
- # is not really the case; but we do not know their distribution).
- # If two subtables cover the same number of glyphs, we sort them
- # by glyph ID so that our output is deterministic.
- result.sort(key=lambda t: _getSinglePosTableKey(t, glyphMap))
- return result
-
-
-def buildSinglePosSubtable(values, glyphMap):
- """Builds a single adjustment (GPOS1) subtable.
-
- This builds a list of SinglePos subtables from a dictionary of glyph
- names and their positioning adjustments. The format of the subtable is
- determined to optimize the size of the output.
- See also :func:`buildSinglePos`.
-
- Note that if you are implementing a layout compiler, you may find it more
- flexible to use
- :py:class:`fontTools.otlLib.lookupBuilders.SinglePosBuilder` instead.
-
- Example::
-
- mapping = {
- "V": buildValue({ "xAdvance" : +5 }),
- # ...
- }
-
- subtable = buildSinglePos(pairs, font.getReverseGlyphMap())
-
- Args:
- mapping (dict): A mapping between glyphnames and
- ``otTables.ValueRecord`` objects.
- glyphMap: a glyph name to ID map, typically returned from
- ``font.getReverseGlyphMap()``.
-
- Returns:
- A ``otTables.SinglePos`` object.
- """
- self = ot.SinglePos()
- self.Coverage = buildCoverage(values.keys(), glyphMap)
- valueFormat = self.ValueFormat = reduce(
- int.__or__, [v.getFormat() for v in values.values()], 0
- )
- valueRecords = [
- ValueRecord(src=values[g], valueFormat=valueFormat)
- for g in self.Coverage.glyphs
- ]
- if all(v == valueRecords[0] for v in valueRecords):
- self.Format = 1
- if self.ValueFormat != 0:
- self.Value = valueRecords[0]
- else:
- self.Value = None
- else:
- self.Format = 2
- self.Value = valueRecords
- self.ValueCount = len(self.Value)
- return self
-
-
-def _getSinglePosTableKey(subtable, glyphMap):
- assert isinstance(subtable, ot.SinglePos), subtable
- glyphs = subtable.Coverage.glyphs
- return (-len(glyphs), glyphMap[glyphs[0]])
-
-
-def _getSinglePosValueKey(valueRecord):
- # otBase.ValueRecord --> (2, ("YPlacement": 12))
- assert isinstance(valueRecord, ValueRecord), valueRecord
- valueFormat, result = 0, []
- for name, value in valueRecord.__dict__.items():
- if isinstance(value, ot.Device):
- result.append((name, _makeDeviceTuple(value)))
- else:
- result.append((name, value))
- valueFormat |= valueRecordFormatDict[name][0]
- result.sort()
- result.insert(0, valueFormat)
- return tuple(result)
-
-
-_DeviceTuple = namedtuple("_DeviceTuple", "DeltaFormat StartSize EndSize DeltaValue")
-
-
-def _makeDeviceTuple(device):
- # otTables.Device --> tuple, for making device tables unique
- return _DeviceTuple(
- device.DeltaFormat,
- device.StartSize,
- device.EndSize,
- () if device.DeltaFormat & 0x8000 else tuple(device.DeltaValue),
- )
-
-
-def _getSinglePosValueSize(valueKey):
- # Returns how many ushorts this valueKey (short form of ValueRecord) takes up
- count = 0
- for _, v in valueKey[1:]:
- if isinstance(v, _DeviceTuple):
- count += len(v.DeltaValue) + 3
- else:
- count += 1
- return count
-
-
-def buildValue(value):
- """Builds a positioning value record.
-
- Value records are used to specify coordinates and adjustments for
- positioning and attaching glyphs. Many of the positioning functions
- in this library take ``otTables.ValueRecord`` objects as arguments.
- This function builds value records from dictionaries.
-
- Args:
- value (dict): A dictionary with zero or more of the following keys:
- - ``xPlacement``
- - ``yPlacement``
- - ``xAdvance``
- - ``yAdvance``
- - ``xPlaDevice``
- - ``yPlaDevice``
- - ``xAdvDevice``
- - ``yAdvDevice``
-
- Returns:
- An ``otTables.ValueRecord`` object.
- """
- self = ValueRecord()
- for k, v in value.items():
- setattr(self, k, v)
- return self
-
-
-# GDEF
-
-
-def buildAttachList(attachPoints, glyphMap):
- """Builds an AttachList subtable.
-
- A GDEF table may contain an Attachment Point List table (AttachList)
- which stores the contour indices of attachment points for glyphs with
- attachment points. This routine builds AttachList subtables.
-
- Args:
- attachPoints (dict): A mapping between glyph names and a list of
- contour indices.
-
- Returns:
- An ``otTables.AttachList`` object if attachment points are supplied,
- or ``None`` otherwise.
- """
- if not attachPoints:
- return None
- self = ot.AttachList()
- self.Coverage = buildCoverage(attachPoints.keys(), glyphMap)
- self.AttachPoint = [buildAttachPoint(attachPoints[g]) for g in self.Coverage.glyphs]
- self.GlyphCount = len(self.AttachPoint)
- return self
-
-
-def buildAttachPoint(points):
- # [4, 23, 41] --> otTables.AttachPoint
- # Only used by above.
- if not points:
- return None
- self = ot.AttachPoint()
- self.PointIndex = sorted(set(points))
- self.PointCount = len(self.PointIndex)
- return self
-
-
-def buildCaretValueForCoord(coord):
- # 500 --> otTables.CaretValue, format 1
- # (500, DeviceTable) --> otTables.CaretValue, format 3
- self = ot.CaretValue()
- if isinstance(coord, tuple):
- self.Format = 3
- self.Coordinate, self.DeviceTable = coord
- else:
- self.Format = 1
- self.Coordinate = coord
- return self
-
-
-def buildCaretValueForPoint(point):
- # 4 --> otTables.CaretValue, format 2
- self = ot.CaretValue()
- self.Format = 2
- self.CaretValuePoint = point
- return self
-
-
-def buildLigCaretList(coords, points, glyphMap):
- """Builds a ligature caret list table.
-
- Ligatures appear as a single glyph representing multiple characters; however
- when, for example, editing text containing a ``f_i`` ligature, the user may
- want to place the cursor between the ``f`` and the ``i``. The ligature caret
- list in the GDEF table specifies the position to display the "caret" (the
- character insertion indicator, typically a flashing vertical bar) "inside"
- the ligature to represent an insertion point. The insertion positions may
- be specified either by coordinate or by contour point.
-
- Example::
-
- coords = {
- "f_f_i": [300, 600] # f|fi cursor at 300 units, ff|i cursor at 600.
- }
- points = {
- "c_t": [28] # c|t cursor appears at coordinate of contour point 28.
- }
- ligcaretlist = buildLigCaretList(coords, points, font.getReverseGlyphMap())
-
- Args:
- coords: A mapping between glyph names and a list of coordinates for
- the insertion point of each ligature component after the first one.
- points: A mapping between glyph names and a list of contour points for
- the insertion point of each ligature component after the first one.
- glyphMap: a glyph name to ID map, typically returned from
- ``font.getReverseGlyphMap()``.
-
- Returns:
- A ``otTables.LigCaretList`` object if any carets are present, or
- ``None`` otherwise."""
- glyphs = set(coords.keys()) if coords else set()
- if points:
- glyphs.update(points.keys())
- carets = {g: buildLigGlyph(coords.get(g), points.get(g)) for g in glyphs}
- carets = {g: c for g, c in carets.items() if c is not None}
- if not carets:
- return None
- self = ot.LigCaretList()
- self.Coverage = buildCoverage(carets.keys(), glyphMap)
- self.LigGlyph = [carets[g] for g in self.Coverage.glyphs]
- self.LigGlyphCount = len(self.LigGlyph)
- return self
-
-
-def buildLigGlyph(coords, points):
- # ([500], [4]) --> otTables.LigGlyph; None for empty coords/points
- carets = []
- if coords:
- coords = sorted(coords, key=lambda c: c[0] if isinstance(c, tuple) else c)
- carets.extend([buildCaretValueForCoord(c) for c in coords])
- if points:
- carets.extend([buildCaretValueForPoint(p) for p in sorted(points)])
- if not carets:
- return None
- self = ot.LigGlyph()
- self.CaretValue = carets
- self.CaretCount = len(self.CaretValue)
- return self
-
-
-def buildMarkGlyphSetsDef(markSets, glyphMap):
- """Builds a mark glyph sets definition table.
-
- OpenType Layout lookups may choose to use mark filtering sets to consider
- or ignore particular combinations of marks. These sets are specified by
- setting a flag on the lookup, but the mark filtering sets are defined in
- the ``GDEF`` table. This routine builds the subtable containing the mark
- glyph set definitions.
-
- Example::
-
- set0 = set("acute", "grave")
- set1 = set("caron", "grave")
-
- markglyphsets = buildMarkGlyphSetsDef([set0, set1], font.getReverseGlyphMap())
-
- Args:
-
- markSets: A list of sets of glyphnames.
- glyphMap: a glyph name to ID map, typically returned from
- ``font.getReverseGlyphMap()``.
-
- Returns
- An ``otTables.MarkGlyphSetsDef`` object.
- """
- if not markSets:
- return None
- self = ot.MarkGlyphSetsDef()
- self.MarkSetTableFormat = 1
- self.Coverage = [buildCoverage(m, glyphMap) for m in markSets]
- self.MarkSetCount = len(self.Coverage)
- return self
-
-
-class ClassDefBuilder(object):
- """Helper for building ClassDef tables."""
-
- def __init__(self, useClass0):
- self.classes_ = set()
- self.glyphs_ = {}
- self.useClass0_ = useClass0
-
- def canAdd(self, glyphs):
- if isinstance(glyphs, (set, frozenset)):
- glyphs = sorted(glyphs)
- glyphs = tuple(glyphs)
- if glyphs in self.classes_:
- return True
- for glyph in glyphs:
- if glyph in self.glyphs_:
- return False
- return True
-
- def add(self, glyphs):
- if isinstance(glyphs, (set, frozenset)):
- glyphs = sorted(glyphs)
- glyphs = tuple(glyphs)
- if glyphs in self.classes_:
- return
- self.classes_.add(glyphs)
- for glyph in glyphs:
- if glyph in self.glyphs_:
- raise OpenTypeLibError(
- f"Glyph {glyph} is already present in class.", None
- )
- self.glyphs_[glyph] = glyphs
-
- def classes(self):
- # In ClassDef1 tables, class id #0 does not need to be encoded
- # because zero is the default. Therefore, we use id #0 for the
- # glyph class that has the largest number of members. However,
- # in other tables than ClassDef1, 0 means "every other glyph"
- # so we should not use that ID for any real glyph classes;
- # we implement this by inserting an empty set at position 0.
- #
- # TODO: Instead of counting the number of glyphs in each class,
- # we should determine the encoded size. If the glyphs in a large
- # class form a contiguous range, the encoding is actually quite
- # compact, whereas a non-contiguous set might need a lot of bytes
- # in the output file. We don't get this right with the key below.
- result = sorted(self.classes_, key=lambda s: (len(s), s), reverse=True)
- if not self.useClass0_:
- result.insert(0, frozenset())
- return result
-
- def build(self):
- glyphClasses = {}
- for classID, glyphs in enumerate(self.classes()):
- if classID == 0:
- continue
- for glyph in glyphs:
- glyphClasses[glyph] = classID
- classDef = ot.ClassDef()
- classDef.classDefs = glyphClasses
- return classDef
-
-
-AXIS_VALUE_NEGATIVE_INFINITY = fixedToFloat(-0x80000000, 16)
-AXIS_VALUE_POSITIVE_INFINITY = fixedToFloat(0x7FFFFFFF, 16)
-
-
-def buildStatTable(
- ttFont, axes, locations=None, elidedFallbackName=2, windowsNames=True, macNames=True
-):
- """Add a 'STAT' table to 'ttFont'.
-
- 'axes' is a list of dictionaries describing axes and their
- values.
-
- Example::
-
- axes = [
- dict(
- tag="wght",
- name="Weight",
- ordering=0, # optional
- values=[
- dict(value=100, name='Thin'),
- dict(value=300, name='Light'),
- dict(value=400, name='Regular', flags=0x2),
- dict(value=900, name='Black'),
- ],
- )
- ]
-
- Each axis dict must have 'tag' and 'name' items. 'tag' maps
- to the 'AxisTag' field. 'name' can be a name ID (int), a string,
- or a dictionary containing multilingual names (see the
- addMultilingualName() name table method), and will translate to
- the AxisNameID field.
-
- An axis dict may contain an 'ordering' item that maps to the
- AxisOrdering field. If omitted, the order of the axes list is
- used to calculate AxisOrdering fields.
-
- The axis dict may contain a 'values' item, which is a list of
- dictionaries describing AxisValue records belonging to this axis.
-
- Each value dict must have a 'name' item, which can be a name ID
- (int), a string, or a dictionary containing multilingual names,
- like the axis name. It translates to the ValueNameID field.
-
- Optionally the value dict can contain a 'flags' item. It maps to
- the AxisValue Flags field, and will be 0 when omitted.
-
- The format of the AxisValue is determined by the remaining contents
- of the value dictionary:
-
- If the value dict contains a 'value' item, an AxisValue record
- Format 1 is created. If in addition to the 'value' item it contains
- a 'linkedValue' item, an AxisValue record Format 3 is built.
-
- If the value dict contains a 'nominalValue' item, an AxisValue
- record Format 2 is built. Optionally it may contain 'rangeMinValue'
- and 'rangeMaxValue' items. These map to -Infinity and +Infinity
- respectively if omitted.
-
- You cannot specify Format 4 AxisValue tables this way, as they are
- not tied to a single axis, and specify a name for a location that
- is defined by multiple axes values. Instead, you need to supply the
- 'locations' argument.
-
- The optional 'locations' argument specifies AxisValue Format 4
- tables. It should be a list of dicts, where each dict has a 'name'
- item, which works just like the value dicts above, an optional
- 'flags' item (defaulting to 0x0), and a 'location' dict. A
- location dict key is an axis tag, and the associated value is the
- location on the specified axis. They map to the AxisIndex and Value
- fields of the AxisValueRecord.
-
- Example::
-
- locations = [
- dict(name='Regular ABCD', location=dict(wght=300, ABCD=100)),
- dict(name='Bold ABCD XYZ', location=dict(wght=600, ABCD=200)),
- ]
-
- The optional 'elidedFallbackName' argument can be a name ID (int),
- a string, a dictionary containing multilingual names, or a list of
- STATNameStatements. It translates to the ElidedFallbackNameID field.
-
- The 'ttFont' argument must be a TTFont instance that already has a
- 'name' table. If a 'STAT' table already exists, it will be
- overwritten by the newly created one.
- """
- ttFont["STAT"] = ttLib.newTable("STAT")
- statTable = ttFont["STAT"].table = ot.STAT()
- nameTable = ttFont["name"]
- statTable.ElidedFallbackNameID = _addName(
- nameTable, elidedFallbackName, windows=windowsNames, mac=macNames
- )
-
- # 'locations' contains data for AxisValue Format 4
- axisRecords, axisValues = _buildAxisRecords(
- axes, nameTable, windowsNames=windowsNames, macNames=macNames
- )
- if not locations:
- statTable.Version = 0x00010001
- else:
- # We'll be adding Format 4 AxisValue records, which
- # requires a higher table version
- statTable.Version = 0x00010002
- multiAxisValues = _buildAxisValuesFormat4(
- locations, axes, nameTable, windowsNames=windowsNames, macNames=macNames
- )
- axisValues = multiAxisValues + axisValues
- nameTable.names.sort()
-
- # Store AxisRecords
- axisRecordArray = ot.AxisRecordArray()
- axisRecordArray.Axis = axisRecords
- # XXX these should not be hard-coded but computed automatically
- statTable.DesignAxisRecordSize = 8
- statTable.DesignAxisRecord = axisRecordArray
- statTable.DesignAxisCount = len(axisRecords)
-
- statTable.AxisValueCount = 0
- statTable.AxisValueArray = None
- if axisValues:
- # Store AxisValueRecords
- axisValueArray = ot.AxisValueArray()
- axisValueArray.AxisValue = axisValues
- statTable.AxisValueArray = axisValueArray
- statTable.AxisValueCount = len(axisValues)
-
-
-def _buildAxisRecords(axes, nameTable, windowsNames=True, macNames=True):
- axisRecords = []
- axisValues = []
- for axisRecordIndex, axisDict in enumerate(axes):
- axis = ot.AxisRecord()
- axis.AxisTag = axisDict["tag"]
- axis.AxisNameID = _addName(
- nameTable, axisDict["name"], 256, windows=windowsNames, mac=macNames
- )
- axis.AxisOrdering = axisDict.get("ordering", axisRecordIndex)
- axisRecords.append(axis)
-
- for axisVal in axisDict.get("values", ()):
- axisValRec = ot.AxisValue()
- axisValRec.AxisIndex = axisRecordIndex
- axisValRec.Flags = axisVal.get("flags", 0)
- axisValRec.ValueNameID = _addName(
- nameTable, axisVal["name"], windows=windowsNames, mac=macNames
- )
-
- if "value" in axisVal:
- axisValRec.Value = axisVal["value"]
- if "linkedValue" in axisVal:
- axisValRec.Format = 3
- axisValRec.LinkedValue = axisVal["linkedValue"]
- else:
- axisValRec.Format = 1
- elif "nominalValue" in axisVal:
- axisValRec.Format = 2
- axisValRec.NominalValue = axisVal["nominalValue"]
- axisValRec.RangeMinValue = axisVal.get(
- "rangeMinValue", AXIS_VALUE_NEGATIVE_INFINITY
- )
- axisValRec.RangeMaxValue = axisVal.get(
- "rangeMaxValue", AXIS_VALUE_POSITIVE_INFINITY
- )
- else:
- raise ValueError("Can't determine format for AxisValue")
-
- axisValues.append(axisValRec)
- return axisRecords, axisValues
-
-
-def _buildAxisValuesFormat4(
- locations, axes, nameTable, windowsNames=True, macNames=True
-):
- axisTagToIndex = {}
- for axisRecordIndex, axisDict in enumerate(axes):
- axisTagToIndex[axisDict["tag"]] = axisRecordIndex
-
- axisValues = []
- for axisLocationDict in locations:
- axisValRec = ot.AxisValue()
- axisValRec.Format = 4
- axisValRec.ValueNameID = _addName(
- nameTable, axisLocationDict["name"], windows=windowsNames, mac=macNames
- )
- axisValRec.Flags = axisLocationDict.get("flags", 0)
- axisValueRecords = []
- for tag, value in axisLocationDict["location"].items():
- avr = ot.AxisValueRecord()
- avr.AxisIndex = axisTagToIndex[tag]
- avr.Value = value
- axisValueRecords.append(avr)
- axisValueRecords.sort(key=lambda avr: avr.AxisIndex)
- axisValRec.AxisCount = len(axisValueRecords)
- axisValRec.AxisValueRecord = axisValueRecords
- axisValues.append(axisValRec)
- return axisValues
-
-
-def _addName(nameTable, value, minNameID=0, windows=True, mac=True):
- if isinstance(value, int):
- # Already a nameID
- return value
- if isinstance(value, str):
- names = dict(en=value)
- elif isinstance(value, dict):
- names = value
- elif isinstance(value, list):
- nameID = nameTable._findUnusedNameID()
- for nameRecord in value:
- if isinstance(nameRecord, STATNameStatement):
- nameTable.setName(
- nameRecord.string,
- nameID,
- nameRecord.platformID,
- nameRecord.platEncID,
- nameRecord.langID,
- )
- else:
- raise TypeError("value must be a list of STATNameStatements")
- return nameID
- else:
- raise TypeError("value must be int, str, dict or list")
- return nameTable.addMultilingualName(
- names, windows=windows, mac=mac, minNameID=minNameID
- )
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/builder.py.sketch b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/builder.py.sketch
deleted file mode 100644
index 9addf8c..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/builder.py.sketch
+++ /dev/null
@@ -1,105 +0,0 @@
-
-from fontTools.otlLib import builder as builder
-
-GDEF::mark filtering sets
-name::
-
-lookup_flags = builder.LOOKUP_FLAG_IGNORE_MARKS | builder.LOOKUP_FLAG_RTL
-smcp_subtable = builder.buildSingleSubstitute({'a':'a.scmp'})
-smcp_lookup = builder.buildLookup([smcp_subtable], lookup_flags=lookup_flags, mark_filter_set=int)
-
-lookups = [smcp_lookup, ...]
-
-scmp_feature = builder.buildFeature('smcp', [scmp_lookup], lookup_list=lookups)
-scmp_feature = builder.buildFeature('smcp', [0])
-
-features = [smcp_feature]
-
-default_langsys = builder.buildLangSys(set([scmp_feature]), requiredFeature=None, featureOrder=features)
-default_langsys = builder.buildLangSys(set([0]), requiredFeature=None)
-
-script =
-
-
-#GSUB:
-
-builder.buildSingleSubst({'a':'a.scmp'})
-builder.buildLigatureSubst({('f','i'):'fi'})
-builder.buildMultipleSubst({'a':('a0','a1')})
-builder.buildAlternateSubst({'a':('a.0','a.1')})
-
-
-class ChainSequence : namedtuple(['backtrack', 'input', 'lookahead')])
- pass
-
-ChainSequence(backtrack=..., input=..., lookahead=...)
-
-klass0 = frozenset()
-
-builder.buildChainContextGlyphs(
- [
- ( (None, ('f','f','i'), (,)), ( (1,lookup_fi), (1,lookup_2) ) ),
- ],
- glyphMap
-)
-builder.buildChainContextClass(
- [
- ( (None, (2,0,1), (,)), ( (1,lookup_fi), (1,lookup_2) ) ),
- ],
- klasses = ( backtrackClass, ... ),
- glyphMap
-)
-builder.buildChainContextCoverage(
- ( (None, (frozenset('f'),frozenset('f'),frozenset('i')), (,)), ( (1,lookup_fi), (1,lookup_2) ) ),
- glyphMap
-)
-builder.buildExtension(...)
-
-#GPOS:
-device = builder.buildDevice()
-builder.buildAnchor(100, -200) or (100,-200)
-builder.buildAnchor(100, -200, device=device)
-builder.buildAnchor(100, -200, point=2)
-
-valueRecord = builder.buildValue({'XAdvance':-200, ...})
-
-builder.buildSinglePos({'a':valueRecord})
-builder.buildPairPosGlyphs(
- {
- ('a','b'): (valueRecord1,valueRecord2),
- },
- glyphMap,
- , valueFormat1=None, valueFormat2=None
-)
-builder.buildPairPosClasses(
- {
- (frozenset(['a']),frozenset(['b'])): (valueRecord1,valueRecord2),
- },
- glyphMap,
- , valueFormat1=None, valueFormat2=None
-)
-
-builder.buildCursivePos(
- {
- 'alef': (entry,exit),
- }
- glyphMap
-)
-builder.buildMarkBasePos(
- marks = {
- 'mark1': (klass, anchor),
- },
- bases = {
- 'base0': [anchor0, anchor1, anchor2],
- },
- glyphMap
-)
-builder.buildMarkBasePos(
- marks = {
- 'mark1': (name, anchor),
- },
- bases = {
- 'base0': {'top':anchor0, 'left':anchor1},
- },
- glyphMap
-)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/error.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/error.py
deleted file mode 100644
index 1cbef57..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/error.py
+++ /dev/null
@@ -1,11 +0,0 @@
-class OpenTypeLibError(Exception):
- def __init__(self, message, location):
- Exception.__init__(self, message)
- self.location = location
-
- def __str__(self):
- message = Exception.__str__(self)
- if self.location:
- return f"{self.location}: {message}"
- else:
- return message
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/maxContextCalc.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/maxContextCalc.py
deleted file mode 100644
index 03e7561..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/maxContextCalc.py
+++ /dev/null
@@ -1,96 +0,0 @@
-__all__ = ["maxCtxFont"]
-
-
-def maxCtxFont(font):
- """Calculate the usMaxContext value for an entire font."""
-
- maxCtx = 0
- for tag in ("GSUB", "GPOS"):
- if tag not in font:
- continue
- table = font[tag].table
- if not table.LookupList:
- continue
- for lookup in table.LookupList.Lookup:
- for st in lookup.SubTable:
- maxCtx = maxCtxSubtable(maxCtx, tag, lookup.LookupType, st)
- return maxCtx
-
-
-def maxCtxSubtable(maxCtx, tag, lookupType, st):
- """Calculate usMaxContext based on a single lookup table (and an existing
- max value).
- """
-
- # single positioning, single / multiple substitution
- if (tag == "GPOS" and lookupType == 1) or (
- tag == "GSUB" and lookupType in (1, 2, 3)
- ):
- maxCtx = max(maxCtx, 1)
-
- # pair positioning
- elif tag == "GPOS" and lookupType == 2:
- maxCtx = max(maxCtx, 2)
-
- # ligatures
- elif tag == "GSUB" and lookupType == 4:
- for ligatures in st.ligatures.values():
- for ligature in ligatures:
- maxCtx = max(maxCtx, ligature.CompCount)
-
- # context
- elif (tag == "GPOS" and lookupType == 7) or (tag == "GSUB" and lookupType == 5):
- maxCtx = maxCtxContextualSubtable(maxCtx, st, "Pos" if tag == "GPOS" else "Sub")
-
- # chained context
- elif (tag == "GPOS" and lookupType == 8) or (tag == "GSUB" and lookupType == 6):
- maxCtx = maxCtxContextualSubtable(
- maxCtx, st, "Pos" if tag == "GPOS" else "Sub", "Chain"
- )
-
- # extensions
- elif (tag == "GPOS" and lookupType == 9) or (tag == "GSUB" and lookupType == 7):
- maxCtx = maxCtxSubtable(maxCtx, tag, st.ExtensionLookupType, st.ExtSubTable)
-
- # reverse-chained context
- elif tag == "GSUB" and lookupType == 8:
- maxCtx = maxCtxContextualRule(maxCtx, st, "Reverse")
-
- return maxCtx
-
-
-def maxCtxContextualSubtable(maxCtx, st, ruleType, chain=""):
- """Calculate usMaxContext based on a contextual feature subtable."""
-
- if st.Format == 1:
- for ruleset in getattr(st, "%s%sRuleSet" % (chain, ruleType)):
- if ruleset is None:
- continue
- for rule in getattr(ruleset, "%s%sRule" % (chain, ruleType)):
- if rule is None:
- continue
- maxCtx = maxCtxContextualRule(maxCtx, rule, chain)
-
- elif st.Format == 2:
- for ruleset in getattr(st, "%s%sClassSet" % (chain, ruleType)):
- if ruleset is None:
- continue
- for rule in getattr(ruleset, "%s%sClassRule" % (chain, ruleType)):
- if rule is None:
- continue
- maxCtx = maxCtxContextualRule(maxCtx, rule, chain)
-
- elif st.Format == 3:
- maxCtx = maxCtxContextualRule(maxCtx, st, chain)
-
- return maxCtx
-
-
-def maxCtxContextualRule(maxCtx, st, chain):
- """Calculate usMaxContext based on a contextual feature rule."""
-
- if not chain:
- return max(maxCtx, st.GlyphCount)
- elif chain == "Reverse":
- return max(maxCtx, st.GlyphCount + st.LookAheadGlyphCount)
- return max(maxCtx, st.InputGlyphCount + st.LookAheadGlyphCount)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/optimize/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/optimize/__init__.py
deleted file mode 100644
index 25bce9c..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/optimize/__init__.py
+++ /dev/null
@@ -1,53 +0,0 @@
-from argparse import RawTextHelpFormatter
-from fontTools.otlLib.optimize.gpos import COMPRESSION_LEVEL, compact
-from fontTools.ttLib import TTFont
-
-
-def main(args=None):
- """Optimize the layout tables of an existing font"""
- from argparse import ArgumentParser
-
- from fontTools import configLogger
-
- parser = ArgumentParser(
- prog="otlLib.optimize",
- description=main.__doc__,
- formatter_class=RawTextHelpFormatter,
- )
- parser.add_argument("font")
- parser.add_argument(
- "-o", metavar="OUTPUTFILE", dest="outfile", default=None, help="output file"
- )
- parser.add_argument(
- "--gpos-compression-level",
- help=COMPRESSION_LEVEL.help,
- default=COMPRESSION_LEVEL.default,
- choices=list(range(10)),
- type=int,
- )
- logging_group = parser.add_mutually_exclusive_group(required=False)
- logging_group.add_argument(
- "-v", "--verbose", action="store_true", help="Run more verbosely."
- )
- logging_group.add_argument(
- "-q", "--quiet", action="store_true", help="Turn verbosity off."
- )
- options = parser.parse_args(args)
-
- configLogger(
- level=("DEBUG" if options.verbose else "ERROR" if options.quiet else "INFO")
- )
-
- font = TTFont(options.font)
- compact(font, options.gpos_compression_level)
- font.save(options.outfile or options.font)
-
-
-if __name__ == "__main__":
- import sys
-
- if len(sys.argv) > 1:
- sys.exit(main())
- import doctest
-
- sys.exit(doctest.testmod().failed)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/optimize/__main__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/optimize/__main__.py
deleted file mode 100644
index b0ae908..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/optimize/__main__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-import sys
-from fontTools.otlLib.optimize import main
-
-
-if __name__ == "__main__":
- sys.exit(main())
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/optimize/gpos.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/optimize/gpos.py
deleted file mode 100644
index 01c2257..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/otlLib/optimize/gpos.py
+++ /dev/null
@@ -1,453 +0,0 @@
-import logging
-import os
-from collections import defaultdict, namedtuple
-from functools import reduce
-from itertools import chain
-from math import log2
-from typing import DefaultDict, Dict, Iterable, List, Sequence, Tuple
-
-from fontTools.config import OPTIONS
-from fontTools.misc.intTools import bit_count, bit_indices
-from fontTools.ttLib import TTFont
-from fontTools.ttLib.tables import otBase, otTables
-
-log = logging.getLogger(__name__)
-
-COMPRESSION_LEVEL = OPTIONS[f"{__name__}:COMPRESSION_LEVEL"]
-
-# Kept because ufo2ft depends on it, to be removed once ufo2ft uses the config instead
-# https://github.com/fonttools/fonttools/issues/2592
-GPOS_COMPACT_MODE_ENV_KEY = "FONTTOOLS_GPOS_COMPACT_MODE"
-GPOS_COMPACT_MODE_DEFAULT = str(COMPRESSION_LEVEL.default)
-
-
-def _compression_level_from_env() -> int:
- env_level = GPOS_COMPACT_MODE_DEFAULT
- if GPOS_COMPACT_MODE_ENV_KEY in os.environ:
- import warnings
-
- warnings.warn(
- f"'{GPOS_COMPACT_MODE_ENV_KEY}' environment variable is deprecated. "
- "Please set the 'fontTools.otlLib.optimize.gpos:COMPRESSION_LEVEL' option "
- "in TTFont.cfg.",
- DeprecationWarning,
- )
-
- env_level = os.environ[GPOS_COMPACT_MODE_ENV_KEY]
- if len(env_level) == 1 and env_level in "0123456789":
- return int(env_level)
- raise ValueError(f"Bad {GPOS_COMPACT_MODE_ENV_KEY}={env_level}")
-
-
-def compact(font: TTFont, level: int) -> TTFont:
- # Ideal plan:
- # 1. Find lookups of Lookup Type 2: Pair Adjustment Positioning Subtable
- # https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#lookup-type-2-pair-adjustment-positioning-subtable
- # 2. Extract glyph-glyph kerning and class-kerning from all present subtables
- # 3. Regroup into different subtable arrangements
- # 4. Put back into the lookup
- #
- # Actual implementation:
- # 2. Only class kerning is optimized currently
- # 3. If the input kerning is already in several subtables, the subtables
- # are not grouped together first; instead each subtable is treated
- # independently, so currently this step is:
- # Split existing subtables into more smaller subtables
- gpos = font["GPOS"]
- for lookup in gpos.table.LookupList.Lookup:
- if lookup.LookupType == 2:
- compact_lookup(font, level, lookup)
- elif lookup.LookupType == 9 and lookup.SubTable[0].ExtensionLookupType == 2:
- compact_ext_lookup(font, level, lookup)
- return font
-
-
-def compact_lookup(font: TTFont, level: int, lookup: otTables.Lookup) -> None:
- new_subtables = compact_pair_pos(font, level, lookup.SubTable)
- lookup.SubTable = new_subtables
- lookup.SubTableCount = len(new_subtables)
-
-
-def compact_ext_lookup(font: TTFont, level: int, lookup: otTables.Lookup) -> None:
- new_subtables = compact_pair_pos(
- font, level, [ext_subtable.ExtSubTable for ext_subtable in lookup.SubTable]
- )
- new_ext_subtables = []
- for subtable in new_subtables:
- ext_subtable = otTables.ExtensionPos()
- ext_subtable.Format = 1
- ext_subtable.ExtSubTable = subtable
- new_ext_subtables.append(ext_subtable)
- lookup.SubTable = new_ext_subtables
- lookup.SubTableCount = len(new_ext_subtables)
-
-
-def compact_pair_pos(
- font: TTFont, level: int, subtables: Sequence[otTables.PairPos]
-) -> Sequence[otTables.PairPos]:
- new_subtables = []
- for subtable in subtables:
- if subtable.Format == 1:
- # Not doing anything to Format 1 (yet?)
- new_subtables.append(subtable)
- elif subtable.Format == 2:
- new_subtables.extend(compact_class_pairs(font, level, subtable))
- return new_subtables
-
-
-def compact_class_pairs(
- font: TTFont, level: int, subtable: otTables.PairPos
-) -> List[otTables.PairPos]:
- from fontTools.otlLib.builder import buildPairPosClassesSubtable
-
- subtables = []
- classes1: DefaultDict[int, List[str]] = defaultdict(list)
- for g in subtable.Coverage.glyphs:
- classes1[subtable.ClassDef1.classDefs.get(g, 0)].append(g)
- classes2: DefaultDict[int, List[str]] = defaultdict(list)
- for g, i in subtable.ClassDef2.classDefs.items():
- classes2[i].append(g)
- all_pairs = {}
- for i, class1 in enumerate(subtable.Class1Record):
- for j, class2 in enumerate(class1.Class2Record):
- if is_really_zero(class2):
- continue
- all_pairs[(tuple(sorted(classes1[i])), tuple(sorted(classes2[j])))] = (
- getattr(class2, "Value1", None),
- getattr(class2, "Value2", None),
- )
- grouped_pairs = cluster_pairs_by_class2_coverage_custom_cost(font, all_pairs, level)
- for pairs in grouped_pairs:
- subtables.append(buildPairPosClassesSubtable(pairs, font.getReverseGlyphMap()))
- return subtables
-
-
-def is_really_zero(class2: otTables.Class2Record) -> bool:
- v1 = getattr(class2, "Value1", None)
- v2 = getattr(class2, "Value2", None)
- return (v1 is None or v1.getEffectiveFormat() == 0) and (
- v2 is None or v2.getEffectiveFormat() == 0
- )
-
-
-Pairs = Dict[
- Tuple[Tuple[str, ...], Tuple[str, ...]],
- Tuple[otBase.ValueRecord, otBase.ValueRecord],
-]
-
-
-# Adapted from https://github.com/fonttools/fonttools/blob/f64f0b42f2d1163b2d85194e0979def539f5dca3/Lib/fontTools/ttLib/tables/otTables.py#L935-L958
-def _getClassRanges(glyphIDs: Iterable[int]):
- glyphIDs = sorted(glyphIDs)
- last = glyphIDs[0]
- ranges = [[last]]
- for glyphID in glyphIDs[1:]:
- if glyphID != last + 1:
- ranges[-1].append(last)
- ranges.append([glyphID])
- last = glyphID
- ranges[-1].append(last)
- return ranges, glyphIDs[0], glyphIDs[-1]
-
-
-# Adapted from https://github.com/fonttools/fonttools/blob/f64f0b42f2d1163b2d85194e0979def539f5dca3/Lib/fontTools/ttLib/tables/otTables.py#L960-L989
-def _classDef_bytes(
- class_data: List[Tuple[List[Tuple[int, int]], int, int]],
- class_ids: List[int],
- coverage=False,
-):
- if not class_ids:
- return 0
- first_ranges, min_glyph_id, max_glyph_id = class_data[class_ids[0]]
- range_count = len(first_ranges)
- for i in class_ids[1:]:
- data = class_data[i]
- range_count += len(data[0])
- min_glyph_id = min(min_glyph_id, data[1])
- max_glyph_id = max(max_glyph_id, data[2])
- glyphCount = max_glyph_id - min_glyph_id + 1
- # https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-1
- format1_bytes = 6 + glyphCount * 2
- # https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-2
- format2_bytes = 4 + range_count * 6
- return min(format1_bytes, format2_bytes)
-
-
-ClusteringContext = namedtuple(
- "ClusteringContext",
- [
- "lines",
- "all_class1",
- "all_class1_data",
- "all_class2_data",
- "valueFormat1_bytes",
- "valueFormat2_bytes",
- ],
-)
-
-
-class Cluster:
- # TODO(Python 3.7): Turn this into a dataclass
- # ctx: ClusteringContext
- # indices: int
- # Caches
- # TODO(Python 3.8): use functools.cached_property instead of the
- # manually cached properties, and remove the cache fields listed below.
- # _indices: Optional[List[int]] = None
- # _column_indices: Optional[List[int]] = None
- # _cost: Optional[int] = None
-
- __slots__ = "ctx", "indices_bitmask", "_indices", "_column_indices", "_cost"
-
- def __init__(self, ctx: ClusteringContext, indices_bitmask: int):
- self.ctx = ctx
- self.indices_bitmask = indices_bitmask
- self._indices = None
- self._column_indices = None
- self._cost = None
-
- @property
- def indices(self):
- if self._indices is None:
- self._indices = bit_indices(self.indices_bitmask)
- return self._indices
-
- @property
- def column_indices(self):
- if self._column_indices is None:
- # Indices of columns that have a 1 in at least 1 line
- # => binary OR all the lines
- bitmask = reduce(int.__or__, (self.ctx.lines[i] for i in self.indices))
- self._column_indices = bit_indices(bitmask)
- return self._column_indices
-
- @property
- def width(self):
- # Add 1 because Class2=0 cannot be used but needs to be encoded.
- return len(self.column_indices) + 1
-
- @property
- def cost(self):
- if self._cost is None:
- self._cost = (
- # 2 bytes to store the offset to this subtable in the Lookup table above
- 2
- # Contents of the subtable
- # From: https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#pair-adjustment-positioning-format-2-class-pair-adjustment
- # uint16 posFormat Format identifier: format = 2
- + 2
- # Offset16 coverageOffset Offset to Coverage table, from beginning of PairPos subtable.
- + 2
- + self.coverage_bytes
- # uint16 valueFormat1 ValueRecord definition — for the first glyph of the pair (may be zero).
- + 2
- # uint16 valueFormat2 ValueRecord definition — for the second glyph of the pair (may be zero).
- + 2
- # Offset16 classDef1Offset Offset to ClassDef table, from beginning of PairPos subtable — for the first glyph of the pair.
- + 2
- + self.classDef1_bytes
- # Offset16 classDef2Offset Offset to ClassDef table, from beginning of PairPos subtable — for the second glyph of the pair.
- + 2
- + self.classDef2_bytes
- # uint16 class1Count Number of classes in classDef1 table — includes Class 0.
- + 2
- # uint16 class2Count Number of classes in classDef2 table — includes Class 0.
- + 2
- # Class1Record class1Records[class1Count] Array of Class1 records, ordered by classes in classDef1.
- + (self.ctx.valueFormat1_bytes + self.ctx.valueFormat2_bytes)
- * len(self.indices)
- * self.width
- )
- return self._cost
-
- @property
- def coverage_bytes(self):
- format1_bytes = (
- # From https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-format-1
- # uint16 coverageFormat Format identifier — format = 1
- # uint16 glyphCount Number of glyphs in the glyph array
- 4
- # uint16 glyphArray[glyphCount] Array of glyph IDs — in numerical order
- + sum(len(self.ctx.all_class1[i]) for i in self.indices) * 2
- )
- ranges = sorted(
- chain.from_iterable(self.ctx.all_class1_data[i][0] for i in self.indices)
- )
- merged_range_count = 0
- last = None
- for start, end in ranges:
- if last is not None and start != last + 1:
- merged_range_count += 1
- last = end
- format2_bytes = (
- # From https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-format-2
- # uint16 coverageFormat Format identifier — format = 2
- # uint16 rangeCount Number of RangeRecords
- 4
- # RangeRecord rangeRecords[rangeCount] Array of glyph ranges — ordered by startGlyphID.
- # uint16 startGlyphID First glyph ID in the range
- # uint16 endGlyphID Last glyph ID in the range
- # uint16 startCoverageIndex Coverage Index of first glyph ID in range
- + merged_range_count * 6
- )
- return min(format1_bytes, format2_bytes)
-
- @property
- def classDef1_bytes(self):
- # We can skip encoding one of the Class1 definitions, and use
- # Class1=0 to represent it instead, because Class1 is gated by the
- # Coverage definition. Use Class1=0 for the highest byte savings.
- # Going through all options takes too long, pick the biggest class
- # = what happens in otlLib.builder.ClassDefBuilder.classes()
- biggest_index = max(self.indices, key=lambda i: len(self.ctx.all_class1[i]))
- return _classDef_bytes(
- self.ctx.all_class1_data, [i for i in self.indices if i != biggest_index]
- )
-
- @property
- def classDef2_bytes(self):
- # All Class2 need to be encoded because we can't use Class2=0
- return _classDef_bytes(self.ctx.all_class2_data, self.column_indices)
-
-
-def cluster_pairs_by_class2_coverage_custom_cost(
- font: TTFont,
- pairs: Pairs,
- compression: int = 5,
-) -> List[Pairs]:
- if not pairs:
- # The subtable was actually empty?
- return [pairs]
-
- # Sorted for reproducibility/determinism
- all_class1 = sorted(set(pair[0] for pair in pairs))
- all_class2 = sorted(set(pair[1] for pair in pairs))
-
- # Use Python's big ints for binary vectors representing each line
- lines = [
- sum(
- 1 << i if (class1, class2) in pairs else 0
- for i, class2 in enumerate(all_class2)
- )
- for class1 in all_class1
- ]
-
- # Map glyph names to ids and work with ints throughout for ClassDef formats
- name_to_id = font.getReverseGlyphMap()
- # Each entry in the arrays below is (range_count, min_glyph_id, max_glyph_id)
- all_class1_data = [
- _getClassRanges(name_to_id[name] for name in cls) for cls in all_class1
- ]
- all_class2_data = [
- _getClassRanges(name_to_id[name] for name in cls) for cls in all_class2
- ]
-
- format1 = 0
- format2 = 0
- for pair, value in pairs.items():
- format1 |= value[0].getEffectiveFormat() if value[0] else 0
- format2 |= value[1].getEffectiveFormat() if value[1] else 0
- valueFormat1_bytes = bit_count(format1) * 2
- valueFormat2_bytes = bit_count(format2) * 2
-
- ctx = ClusteringContext(
- lines,
- all_class1,
- all_class1_data,
- all_class2_data,
- valueFormat1_bytes,
- valueFormat2_bytes,
- )
-
- cluster_cache: Dict[int, Cluster] = {}
-
- def make_cluster(indices: int) -> Cluster:
- cluster = cluster_cache.get(indices, None)
- if cluster is not None:
- return cluster
- cluster = Cluster(ctx, indices)
- cluster_cache[indices] = cluster
- return cluster
-
- def merge(cluster: Cluster, other: Cluster) -> Cluster:
- return make_cluster(cluster.indices_bitmask | other.indices_bitmask)
-
- # Agglomerative clustering by hand, checking the cost gain of the new
- # cluster against the previously separate clusters
- # Start with 1 cluster per line
- # cluster = set of lines = new subtable
- clusters = [make_cluster(1 << i) for i in range(len(lines))]
-
- # Cost of 1 cluster with everything
- # `(1 << len) - 1` gives a bitmask full of 1's of length `len`
- cost_before_splitting = make_cluster((1 << len(lines)) - 1).cost
- log.debug(f" len(clusters) = {len(clusters)}")
-
- while len(clusters) > 1:
- lowest_cost_change = None
- best_cluster_index = None
- best_other_index = None
- best_merged = None
- for i, cluster in enumerate(clusters):
- for j, other in enumerate(clusters[i + 1 :]):
- merged = merge(cluster, other)
- cost_change = merged.cost - cluster.cost - other.cost
- if lowest_cost_change is None or cost_change < lowest_cost_change:
- lowest_cost_change = cost_change
- best_cluster_index = i
- best_other_index = i + 1 + j
- best_merged = merged
- assert lowest_cost_change is not None
- assert best_cluster_index is not None
- assert best_other_index is not None
- assert best_merged is not None
-
- # If the best merge we found is still taking down the file size, then
- # there's no question: we must do it, because it's beneficial in both
- # ways (lower file size and lower number of subtables). However, if the
- # best merge we found is not reducing file size anymore, then we need to
- # look at the other stop criteria = the compression factor.
- if lowest_cost_change > 0:
- # Stop critera: check whether we should keep merging.
- # Compute size reduction brought by splitting
- cost_after_splitting = sum(c.cost for c in clusters)
- # size_reduction so that after = before * (1 - size_reduction)
- # E.g. before = 1000, after = 800, 1 - 800/1000 = 0.2
- size_reduction = 1 - cost_after_splitting / cost_before_splitting
-
- # Force more merging by taking into account the compression number.
- # Target behaviour: compression number = 1 to 9, default 5 like gzip
- # - 1 = accept to add 1 subtable to reduce size by 50%
- # - 5 = accept to add 5 subtables to reduce size by 50%
- # See https://github.com/harfbuzz/packtab/blob/master/Lib/packTab/__init__.py#L690-L691
- # Given the size reduction we have achieved so far, compute how many
- # new subtables are acceptable.
- max_new_subtables = -log2(1 - size_reduction) * compression
- log.debug(
- f" len(clusters) = {len(clusters):3d} size_reduction={size_reduction:5.2f} max_new_subtables={max_new_subtables}",
- )
- if compression == 9:
- # Override level 9 to mean: create any number of subtables
- max_new_subtables = len(clusters)
-
- # If we have managed to take the number of new subtables below the
- # threshold, then we can stop.
- if len(clusters) <= max_new_subtables + 1:
- break
-
- # No reason to stop yet, do the merge and move on to the next.
- del clusters[best_other_index]
- clusters[best_cluster_index] = best_merged
-
- # All clusters are final; turn bitmasks back into the "Pairs" format
- pairs_by_class1: Dict[Tuple[str, ...], Pairs] = defaultdict(dict)
- for pair, values in pairs.items():
- pairs_by_class1[pair[0]][pair] = values
- pairs_groups: List[Pairs] = []
- for cluster in clusters:
- pairs_group: Pairs = dict()
- for i in cluster.indices:
- class1 = all_class1[i]
- pairs_group.update(pairs_by_class1[class1])
- pairs_groups.append(pairs_group)
- return pairs_groups
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/__init__.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/__init__.py
deleted file mode 100644
index 156cb23..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-"""Empty __init__.py file to signal Python this directory is a package."""
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/areaPen.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/areaPen.py
deleted file mode 100644
index 004bb06..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/areaPen.py
+++ /dev/null
@@ -1,52 +0,0 @@
-"""Calculate the area of a glyph."""
-
-from fontTools.pens.basePen import BasePen
-
-
-__all__ = ["AreaPen"]
-
-
-class AreaPen(BasePen):
- def __init__(self, glyphset=None):
- BasePen.__init__(self, glyphset)
- self.value = 0
-
- def _moveTo(self, p0):
- self._p0 = self._startPoint = p0
-
- def _lineTo(self, p1):
- x0, y0 = self._p0
- x1, y1 = p1
- self.value -= (x1 - x0) * (y1 + y0) * 0.5
- self._p0 = p1
-
- def _qCurveToOne(self, p1, p2):
- # https://github.com/Pomax/bezierinfo/issues/44
- p0 = self._p0
- x0, y0 = p0[0], p0[1]
- x1, y1 = p1[0] - x0, p1[1] - y0
- x2, y2 = p2[0] - x0, p2[1] - y0
- self.value -= (x2 * y1 - x1 * y2) / 3
- self._lineTo(p2)
- self._p0 = p2
-
- def _curveToOne(self, p1, p2, p3):
- # https://github.com/Pomax/bezierinfo/issues/44
- p0 = self._p0
- x0, y0 = p0[0], p0[1]
- x1, y1 = p1[0] - x0, p1[1] - y0
- x2, y2 = p2[0] - x0, p2[1] - y0
- x3, y3 = p3[0] - x0, p3[1] - y0
- self.value -= (x1 * (-y2 - y3) + x2 * (y1 - 2 * y3) + x3 * (y1 + 2 * y2)) * 0.15
- self._lineTo(p3)
- self._p0 = p3
-
- def _closePath(self):
- self._lineTo(self._startPoint)
- del self._p0, self._startPoint
-
- def _endPath(self):
- if self._p0 != self._startPoint:
- # Area is not defined for open contours.
- raise NotImplementedError
- del self._p0, self._startPoint
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/basePen.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/basePen.py
deleted file mode 100644
index ac8abd4..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/basePen.py
+++ /dev/null
@@ -1,444 +0,0 @@
-"""fontTools.pens.basePen.py -- Tools and base classes to build pen objects.
-
-The Pen Protocol
-
-A Pen is a kind of object that standardizes the way how to "draw" outlines:
-it is a middle man between an outline and a drawing. In other words:
-it is an abstraction for drawing outlines, making sure that outline objects
-don't need to know the details about how and where they're being drawn, and
-that drawings don't need to know the details of how outlines are stored.
-
-The most basic pattern is this::
-
- outline.draw(pen) # 'outline' draws itself onto 'pen'
-
-Pens can be used to render outlines to the screen, but also to construct
-new outlines. Eg. an outline object can be both a drawable object (it has a
-draw() method) as well as a pen itself: you *build* an outline using pen
-methods.
-
-The AbstractPen class defines the Pen protocol. It implements almost
-nothing (only no-op closePath() and endPath() methods), but is useful
-for documentation purposes. Subclassing it basically tells the reader:
-"this class implements the Pen protocol.". An examples of an AbstractPen
-subclass is :py:class:`fontTools.pens.transformPen.TransformPen`.
-
-The BasePen class is a base implementation useful for pens that actually
-draw (for example a pen renders outlines using a native graphics engine).
-BasePen contains a lot of base functionality, making it very easy to build
-a pen that fully conforms to the pen protocol. Note that if you subclass
-BasePen, you *don't* override moveTo(), lineTo(), etc., but _moveTo(),
-_lineTo(), etc. See the BasePen doc string for details. Examples of
-BasePen subclasses are fontTools.pens.boundsPen.BoundsPen and
-fontTools.pens.cocoaPen.CocoaPen.
-
-Coordinates are usually expressed as (x, y) tuples, but generally any
-sequence of length 2 will do.
-"""
-
-from typing import Tuple, Dict
-
-from fontTools.misc.loggingTools import LogMixin
-from fontTools.misc.transform import DecomposedTransform
-
-__all__ = [
- "AbstractPen",
- "NullPen",
- "BasePen",
- "PenError",
- "decomposeSuperBezierSegment",
- "decomposeQuadraticSegment",
-]
-
-
-class PenError(Exception):
- """Represents an error during penning."""
-
-
-class OpenContourError(PenError):
- pass
-
-
-class AbstractPen:
- def moveTo(self, pt: Tuple[float, float]) -> None:
- """Begin a new sub path, set the current point to 'pt'. You must
- end each sub path with a call to pen.closePath() or pen.endPath().
- """
- raise NotImplementedError
-
- def lineTo(self, pt: Tuple[float, float]) -> None:
- """Draw a straight line from the current point to 'pt'."""
- raise NotImplementedError
-
- def curveTo(self, *points: Tuple[float, float]) -> None:
- """Draw a cubic bezier with an arbitrary number of control points.
-
- The last point specified is on-curve, all others are off-curve
- (control) points. If the number of control points is > 2, the
- segment is split into multiple bezier segments. This works
- like this:
-
- Let n be the number of control points (which is the number of
- arguments to this call minus 1). If n==2, a plain vanilla cubic
- bezier is drawn. If n==1, we fall back to a quadratic segment and
- if n==0 we draw a straight line. It gets interesting when n>2:
- n-1 PostScript-style cubic segments will be drawn as if it were
- one curve. See decomposeSuperBezierSegment().
-
- The conversion algorithm used for n>2 is inspired by NURB
- splines, and is conceptually equivalent to the TrueType "implied
- points" principle. See also decomposeQuadraticSegment().
- """
- raise NotImplementedError
-
- def qCurveTo(self, *points: Tuple[float, float]) -> None:
- """Draw a whole string of quadratic curve segments.
-
- The last point specified is on-curve, all others are off-curve
- points.
-
- This method implements TrueType-style curves, breaking up curves
- using 'implied points': between each two consequtive off-curve points,
- there is one implied point exactly in the middle between them. See
- also decomposeQuadraticSegment().
-
- The last argument (normally the on-curve point) may be None.
- This is to support contours that have NO on-curve points (a rarely
- seen feature of TrueType outlines).
- """
- raise NotImplementedError
-
- def closePath(self) -> None:
- """Close the current sub path. You must call either pen.closePath()
- or pen.endPath() after each sub path.
- """
- pass
-
- def endPath(self) -> None:
- """End the current sub path, but don't close it. You must call
- either pen.closePath() or pen.endPath() after each sub path.
- """
- pass
-
- def addComponent(
- self,
- glyphName: str,
- transformation: Tuple[float, float, float, float, float, float],
- ) -> None:
- """Add a sub glyph. The 'transformation' argument must be a 6-tuple
- containing an affine transformation, or a Transform object from the
- fontTools.misc.transform module. More precisely: it should be a
- sequence containing 6 numbers.
- """
- raise NotImplementedError
-
- def addVarComponent(
- self,
- glyphName: str,
- transformation: DecomposedTransform,
- location: Dict[str, float],
- ) -> None:
- """Add a VarComponent sub glyph. The 'transformation' argument
- must be a DecomposedTransform from the fontTools.misc.transform module,
- and the 'location' argument must be a dictionary mapping axis tags
- to their locations.
- """
- # GlyphSet decomposes for us
- raise AttributeError
-
-
-class NullPen(AbstractPen):
-
- """A pen that does nothing."""
-
- def moveTo(self, pt):
- pass
-
- def lineTo(self, pt):
- pass
-
- def curveTo(self, *points):
- pass
-
- def qCurveTo(self, *points):
- pass
-
- def closePath(self):
- pass
-
- def endPath(self):
- pass
-
- def addComponent(self, glyphName, transformation):
- pass
-
- def addVarComponent(self, glyphName, transformation, location):
- pass
-
-
-class LoggingPen(LogMixin, AbstractPen):
- """A pen with a ``log`` property (see fontTools.misc.loggingTools.LogMixin)"""
-
- pass
-
-
-class MissingComponentError(KeyError):
- """Indicates a component pointing to a non-existent glyph in the glyphset."""
-
-
-class DecomposingPen(LoggingPen):
-
- """Implements a 'addComponent' method that decomposes components
- (i.e. draws them onto self as simple contours).
- It can also be used as a mixin class (e.g. see ContourRecordingPen).
-
- You must override moveTo, lineTo, curveTo and qCurveTo. You may
- additionally override closePath, endPath and addComponent.
-
- By default a warning message is logged when a base glyph is missing;
- set the class variable ``skipMissingComponents`` to False if you want
- to raise a :class:`MissingComponentError` exception.
- """
-
- skipMissingComponents = True
-
- def __init__(self, glyphSet):
- """Takes a single 'glyphSet' argument (dict), in which the glyphs
- that are referenced as components are looked up by their name.
- """
- super(DecomposingPen, self).__init__()
- self.glyphSet = glyphSet
-
- def addComponent(self, glyphName, transformation):
- """Transform the points of the base glyph and draw it onto self."""
- from fontTools.pens.transformPen import TransformPen
-
- try:
- glyph = self.glyphSet[glyphName]
- except KeyError:
- if not self.skipMissingComponents:
- raise MissingComponentError(glyphName)
- self.log.warning("glyph '%s' is missing from glyphSet; skipped" % glyphName)
- else:
- tPen = TransformPen(self, transformation)
- glyph.draw(tPen)
-
- def addVarComponent(self, glyphName, transformation, location):
- # GlyphSet decomposes for us
- raise AttributeError
-
-
-class BasePen(DecomposingPen):
-
- """Base class for drawing pens. You must override _moveTo, _lineTo and
- _curveToOne. You may additionally override _closePath, _endPath,
- addComponent, addVarComponent, and/or _qCurveToOne. You should not
- override any other methods.
- """
-
- def __init__(self, glyphSet=None):
- super(BasePen, self).__init__(glyphSet)
- self.__currentPoint = None
-
- # must override
-
- def _moveTo(self, pt):
- raise NotImplementedError
-
- def _lineTo(self, pt):
- raise NotImplementedError
-
- def _curveToOne(self, pt1, pt2, pt3):
- raise NotImplementedError
-
- # may override
-
- def _closePath(self):
- pass
-
- def _endPath(self):
- pass
-
- def _qCurveToOne(self, pt1, pt2):
- """This method implements the basic quadratic curve type. The
- default implementation delegates the work to the cubic curve
- function. Optionally override with a native implementation.
- """
- pt0x, pt0y = self.__currentPoint
- pt1x, pt1y = pt1
- pt2x, pt2y = pt2
- mid1x = pt0x + 0.66666666666666667 * (pt1x - pt0x)
- mid1y = pt0y + 0.66666666666666667 * (pt1y - pt0y)
- mid2x = pt2x + 0.66666666666666667 * (pt1x - pt2x)
- mid2y = pt2y + 0.66666666666666667 * (pt1y - pt2y)
- self._curveToOne((mid1x, mid1y), (mid2x, mid2y), pt2)
-
- # don't override
-
- def _getCurrentPoint(self):
- """Return the current point. This is not part of the public
- interface, yet is useful for subclasses.
- """
- return self.__currentPoint
-
- def closePath(self):
- self._closePath()
- self.__currentPoint = None
-
- def endPath(self):
- self._endPath()
- self.__currentPoint = None
-
- def moveTo(self, pt):
- self._moveTo(pt)
- self.__currentPoint = pt
-
- def lineTo(self, pt):
- self._lineTo(pt)
- self.__currentPoint = pt
-
- def curveTo(self, *points):
- n = len(points) - 1 # 'n' is the number of control points
- assert n >= 0
- if n == 2:
- # The common case, we have exactly two BCP's, so this is a standard
- # cubic bezier. Even though decomposeSuperBezierSegment() handles
- # this case just fine, we special-case it anyway since it's so
- # common.
- self._curveToOne(*points)
- self.__currentPoint = points[-1]
- elif n > 2:
- # n is the number of control points; split curve into n-1 cubic
- # bezier segments. The algorithm used here is inspired by NURB
- # splines and the TrueType "implied point" principle, and ensures
- # the smoothest possible connection between two curve segments,
- # with no disruption in the curvature. It is practical since it
- # allows one to construct multiple bezier segments with a much
- # smaller amount of points.
- _curveToOne = self._curveToOne
- for pt1, pt2, pt3 in decomposeSuperBezierSegment(points):
- _curveToOne(pt1, pt2, pt3)
- self.__currentPoint = pt3
- elif n == 1:
- self.qCurveTo(*points)
- elif n == 0:
- self.lineTo(points[0])
- else:
- raise AssertionError("can't get there from here")
-
- def qCurveTo(self, *points):
- n = len(points) - 1 # 'n' is the number of control points
- assert n >= 0
- if points[-1] is None:
- # Special case for TrueType quadratics: it is possible to
- # define a contour with NO on-curve points. BasePen supports
- # this by allowing the final argument (the expected on-curve
- # point) to be None. We simulate the feature by making the implied
- # on-curve point between the last and the first off-curve points
- # explicit.
- x, y = points[-2] # last off-curve point
- nx, ny = points[0] # first off-curve point
- impliedStartPoint = (0.5 * (x + nx), 0.5 * (y + ny))
- self.__currentPoint = impliedStartPoint
- self._moveTo(impliedStartPoint)
- points = points[:-1] + (impliedStartPoint,)
- if n > 0:
- # Split the string of points into discrete quadratic curve
- # segments. Between any two consecutive off-curve points
- # there's an implied on-curve point exactly in the middle.
- # This is where the segment splits.
- _qCurveToOne = self._qCurveToOne
- for pt1, pt2 in decomposeQuadraticSegment(points):
- _qCurveToOne(pt1, pt2)
- self.__currentPoint = pt2
- else:
- self.lineTo(points[0])
-
-
-def decomposeSuperBezierSegment(points):
- """Split the SuperBezier described by 'points' into a list of regular
- bezier segments. The 'points' argument must be a sequence with length
- 3 or greater, containing (x, y) coordinates. The last point is the
- destination on-curve point, the rest of the points are off-curve points.
- The start point should not be supplied.
-
- This function returns a list of (pt1, pt2, pt3) tuples, which each
- specify a regular curveto-style bezier segment.
- """
- n = len(points) - 1
- assert n > 1
- bezierSegments = []
- pt1, pt2, pt3 = points[0], None, None
- for i in range(2, n + 1):
- # calculate points in between control points.
- nDivisions = min(i, 3, n - i + 2)
- for j in range(1, nDivisions):
- factor = j / nDivisions
- temp1 = points[i - 1]
- temp2 = points[i - 2]
- temp = (
- temp2[0] + factor * (temp1[0] - temp2[0]),
- temp2[1] + factor * (temp1[1] - temp2[1]),
- )
- if pt2 is None:
- pt2 = temp
- else:
- pt3 = (0.5 * (pt2[0] + temp[0]), 0.5 * (pt2[1] + temp[1]))
- bezierSegments.append((pt1, pt2, pt3))
- pt1, pt2, pt3 = temp, None, None
- bezierSegments.append((pt1, points[-2], points[-1]))
- return bezierSegments
-
-
-def decomposeQuadraticSegment(points):
- """Split the quadratic curve segment described by 'points' into a list
- of "atomic" quadratic segments. The 'points' argument must be a sequence
- with length 2 or greater, containing (x, y) coordinates. The last point
- is the destination on-curve point, the rest of the points are off-curve
- points. The start point should not be supplied.
-
- This function returns a list of (pt1, pt2) tuples, which each specify a
- plain quadratic bezier segment.
- """
- n = len(points) - 1
- assert n > 0
- quadSegments = []
- for i in range(n - 1):
- x, y = points[i]
- nx, ny = points[i + 1]
- impliedPt = (0.5 * (x + nx), 0.5 * (y + ny))
- quadSegments.append((points[i], impliedPt))
- quadSegments.append((points[-2], points[-1]))
- return quadSegments
-
-
-class _TestPen(BasePen):
- """Test class that prints PostScript to stdout."""
-
- def _moveTo(self, pt):
- print("%s %s moveto" % (pt[0], pt[1]))
-
- def _lineTo(self, pt):
- print("%s %s lineto" % (pt[0], pt[1]))
-
- def _curveToOne(self, bcp1, bcp2, pt):
- print(
- "%s %s %s %s %s %s curveto"
- % (bcp1[0], bcp1[1], bcp2[0], bcp2[1], pt[0], pt[1])
- )
-
- def _closePath(self):
- print("closepath")
-
-
-if __name__ == "__main__":
- pen = _TestPen(None)
- pen.moveTo((0, 0))
- pen.lineTo((0, 100))
- pen.curveTo((50, 75), (60, 50), (50, 25), (0, 0))
- pen.closePath()
-
- pen = _TestPen(None)
- # testing the "no on-curve point" scenario
- pen.qCurveTo((0, 0), (0, 100), (100, 100), (100, 0), None)
- pen.closePath()
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/boundsPen.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/boundsPen.py
deleted file mode 100644
index d833cc8..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/boundsPen.py
+++ /dev/null
@@ -1,100 +0,0 @@
-from fontTools.misc.arrayTools import updateBounds, pointInRect, unionRect
-from fontTools.misc.bezierTools import calcCubicBounds, calcQuadraticBounds
-from fontTools.pens.basePen import BasePen
-
-
-__all__ = ["BoundsPen", "ControlBoundsPen"]
-
-
-class ControlBoundsPen(BasePen):
-
- """Pen to calculate the "control bounds" of a shape. This is the
- bounding box of all control points, so may be larger than the
- actual bounding box if there are curves that don't have points
- on their extremes.
-
- When the shape has been drawn, the bounds are available as the
- ``bounds`` attribute of the pen object. It's a 4-tuple::
-
- (xMin, yMin, xMax, yMax).
-
- If ``ignoreSinglePoints`` is True, single points are ignored.
- """
-
- def __init__(self, glyphSet, ignoreSinglePoints=False):
- BasePen.__init__(self, glyphSet)
- self.ignoreSinglePoints = ignoreSinglePoints
- self.init()
-
- def init(self):
- self.bounds = None
- self._start = None
-
- def _moveTo(self, pt):
- self._start = pt
- if not self.ignoreSinglePoints:
- self._addMoveTo()
-
- def _addMoveTo(self):
- if self._start is None:
- return
- bounds = self.bounds
- if bounds:
- self.bounds = updateBounds(bounds, self._start)
- else:
- x, y = self._start
- self.bounds = (x, y, x, y)
- self._start = None
-
- def _lineTo(self, pt):
- self._addMoveTo()
- self.bounds = updateBounds(self.bounds, pt)
-
- def _curveToOne(self, bcp1, bcp2, pt):
- self._addMoveTo()
- bounds = self.bounds
- bounds = updateBounds(bounds, bcp1)
- bounds = updateBounds(bounds, bcp2)
- bounds = updateBounds(bounds, pt)
- self.bounds = bounds
-
- def _qCurveToOne(self, bcp, pt):
- self._addMoveTo()
- bounds = self.bounds
- bounds = updateBounds(bounds, bcp)
- bounds = updateBounds(bounds, pt)
- self.bounds = bounds
-
-
-class BoundsPen(ControlBoundsPen):
-
- """Pen to calculate the bounds of a shape. It calculates the
- correct bounds even when the shape contains curves that don't
- have points on their extremes. This is somewhat slower to compute
- than the "control bounds".
-
- When the shape has been drawn, the bounds are available as the
- ``bounds`` attribute of the pen object. It's a 4-tuple::
-
- (xMin, yMin, xMax, yMax)
- """
-
- def _curveToOne(self, bcp1, bcp2, pt):
- self._addMoveTo()
- bounds = self.bounds
- bounds = updateBounds(bounds, pt)
- if not pointInRect(bcp1, bounds) or not pointInRect(bcp2, bounds):
- bounds = unionRect(
- bounds, calcCubicBounds(self._getCurrentPoint(), bcp1, bcp2, pt)
- )
- self.bounds = bounds
-
- def _qCurveToOne(self, bcp, pt):
- self._addMoveTo()
- bounds = self.bounds
- bounds = updateBounds(bounds, pt)
- if not pointInRect(bcp, bounds):
- bounds = unionRect(
- bounds, calcQuadraticBounds(self._getCurrentPoint(), bcp, pt)
- )
- self.bounds = bounds
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/cairoPen.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/cairoPen.py
deleted file mode 100644
index 9cd5da9..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/cairoPen.py
+++ /dev/null
@@ -1,26 +0,0 @@
-"""Pen to draw to a Cairo graphics library context."""
-
-from fontTools.pens.basePen import BasePen
-
-
-__all__ = ["CairoPen"]
-
-
-class CairoPen(BasePen):
- """Pen to draw to a Cairo graphics library context."""
-
- def __init__(self, glyphSet, context):
- BasePen.__init__(self, glyphSet)
- self.context = context
-
- def _moveTo(self, p):
- self.context.move_to(*p)
-
- def _lineTo(self, p):
- self.context.line_to(*p)
-
- def _curveToOne(self, p1, p2, p3):
- self.context.curve_to(*p1, *p2, *p3)
-
- def _closePath(self):
- self.context.close_path()
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/cocoaPen.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/cocoaPen.py
deleted file mode 100644
index 5369c30..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/cocoaPen.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from fontTools.pens.basePen import BasePen
-
-
-__all__ = ["CocoaPen"]
-
-
-class CocoaPen(BasePen):
- def __init__(self, glyphSet, path=None):
- BasePen.__init__(self, glyphSet)
- if path is None:
- from AppKit import NSBezierPath
-
- path = NSBezierPath.bezierPath()
- self.path = path
-
- def _moveTo(self, p):
- self.path.moveToPoint_(p)
-
- def _lineTo(self, p):
- self.path.lineToPoint_(p)
-
- def _curveToOne(self, p1, p2, p3):
- self.path.curveToPoint_controlPoint1_controlPoint2_(p3, p1, p2)
-
- def _closePath(self):
- self.path.closePath()
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/cu2quPen.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/cu2quPen.py
deleted file mode 100644
index 5730b32..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/cu2quPen.py
+++ /dev/null
@@ -1,325 +0,0 @@
-# Copyright 2016 Google Inc. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import operator
-from fontTools.cu2qu import curve_to_quadratic, curves_to_quadratic
-from fontTools.pens.basePen import decomposeSuperBezierSegment
-from fontTools.pens.filterPen import FilterPen
-from fontTools.pens.reverseContourPen import ReverseContourPen
-from fontTools.pens.pointPen import BasePointToSegmentPen
-from fontTools.pens.pointPen import ReverseContourPointPen
-
-
-class Cu2QuPen(FilterPen):
- """A filter pen to convert cubic bezier curves to quadratic b-splines
- using the FontTools SegmentPen protocol.
-
- Args:
-
- other_pen: another SegmentPen used to draw the transformed outline.
- max_err: maximum approximation error in font units. For optimal results,
- if you know the UPEM of the font, we recommend setting this to a
- value equal, or close to UPEM / 1000.
- reverse_direction: flip the contours' direction but keep starting point.
- stats: a dictionary counting the point numbers of quadratic segments.
- all_quadratic: if True (default), only quadratic b-splines are generated.
- if False, quadratic curves or cubic curves are generated depending
- on which one is more economical.
- """
-
- def __init__(
- self,
- other_pen,
- max_err,
- reverse_direction=False,
- stats=None,
- all_quadratic=True,
- ):
- if reverse_direction:
- other_pen = ReverseContourPen(other_pen)
- super().__init__(other_pen)
- self.max_err = max_err
- self.stats = stats
- self.all_quadratic = all_quadratic
-
- def _convert_curve(self, pt1, pt2, pt3):
- curve = (self.current_pt, pt1, pt2, pt3)
- result = curve_to_quadratic(curve, self.max_err, self.all_quadratic)
- if self.stats is not None:
- n = str(len(result) - 2)
- self.stats[n] = self.stats.get(n, 0) + 1
- if self.all_quadratic:
- self.qCurveTo(*result[1:])
- else:
- if len(result) == 3:
- self.qCurveTo(*result[1:])
- else:
- assert len(result) == 4
- super().curveTo(*result[1:])
-
- def curveTo(self, *points):
- n = len(points)
- if n == 3:
- # this is the most common case, so we special-case it
- self._convert_curve(*points)
- elif n > 3:
- for segment in decomposeSuperBezierSegment(points):
- self._convert_curve(*segment)
- else:
- self.qCurveTo(*points)
-
-
-class Cu2QuPointPen(BasePointToSegmentPen):
- """A filter pen to convert cubic bezier curves to quadratic b-splines
- using the FontTools PointPen protocol.
-
- Args:
- other_point_pen: another PointPen used to draw the transformed outline.
- max_err: maximum approximation error in font units. For optimal results,
- if you know the UPEM of the font, we recommend setting this to a
- value equal, or close to UPEM / 1000.
- reverse_direction: reverse the winding direction of all contours.
- stats: a dictionary counting the point numbers of quadratic segments.
- all_quadratic: if True (default), only quadratic b-splines are generated.
- if False, quadratic curves or cubic curves are generated depending
- on which one is more economical.
- """
-
- __points_required = {
- "move": (1, operator.eq),
- "line": (1, operator.eq),
- "qcurve": (2, operator.ge),
- "curve": (3, operator.eq),
- }
-
- def __init__(
- self,
- other_point_pen,
- max_err,
- reverse_direction=False,
- stats=None,
- all_quadratic=True,
- ):
- BasePointToSegmentPen.__init__(self)
- if reverse_direction:
- self.pen = ReverseContourPointPen(other_point_pen)
- else:
- self.pen = other_point_pen
- self.max_err = max_err
- self.stats = stats
- self.all_quadratic = all_quadratic
-
- def _flushContour(self, segments):
- assert len(segments) >= 1
- closed = segments[0][0] != "move"
- new_segments = []
- prev_points = segments[-1][1]
- prev_on_curve = prev_points[-1][0]
- for segment_type, points in segments:
- if segment_type == "curve":
- for sub_points in self._split_super_bezier_segments(points):
- on_curve, smooth, name, kwargs = sub_points[-1]
- bcp1, bcp2 = sub_points[0][0], sub_points[1][0]
- cubic = [prev_on_curve, bcp1, bcp2, on_curve]
- quad = curve_to_quadratic(cubic, self.max_err, self.all_quadratic)
- if self.stats is not None:
- n = str(len(quad) - 2)
- self.stats[n] = self.stats.get(n, 0) + 1
- new_points = [(pt, False, None, {}) for pt in quad[1:-1]]
- new_points.append((on_curve, smooth, name, kwargs))
- if self.all_quadratic or len(new_points) == 2:
- new_segments.append(["qcurve", new_points])
- else:
- new_segments.append(["curve", new_points])
- prev_on_curve = sub_points[-1][0]
- else:
- new_segments.append([segment_type, points])
- prev_on_curve = points[-1][0]
- if closed:
- # the BasePointToSegmentPen.endPath method that calls _flushContour
- # rotates the point list of closed contours so that they end with
- # the first on-curve point. We restore the original starting point.
- new_segments = new_segments[-1:] + new_segments[:-1]
- self._drawPoints(new_segments)
-
- def _split_super_bezier_segments(self, points):
- sub_segments = []
- # n is the number of control points
- n = len(points) - 1
- if n == 2:
- # a simple bezier curve segment
- sub_segments.append(points)
- elif n > 2:
- # a "super" bezier; decompose it
- on_curve, smooth, name, kwargs = points[-1]
- num_sub_segments = n - 1
- for i, sub_points in enumerate(
- decomposeSuperBezierSegment([pt for pt, _, _, _ in points])
- ):
- new_segment = []
- for point in sub_points[:-1]:
- new_segment.append((point, False, None, {}))
- if i == (num_sub_segments - 1):
- # the last on-curve keeps its original attributes
- new_segment.append((on_curve, smooth, name, kwargs))
- else:
- # on-curves of sub-segments are always "smooth"
- new_segment.append((sub_points[-1], True, None, {}))
- sub_segments.append(new_segment)
- else:
- raise AssertionError("expected 2 control points, found: %d" % n)
- return sub_segments
-
- def _drawPoints(self, segments):
- pen = self.pen
- pen.beginPath()
- last_offcurves = []
- points_required = self.__points_required
- for i, (segment_type, points) in enumerate(segments):
- if segment_type in points_required:
- n, op = points_required[segment_type]
- assert op(len(points), n), (
- f"illegal {segment_type!r} segment point count: "
- f"expected {n}, got {len(points)}"
- )
- offcurves = points[:-1]
- if i == 0:
- # any off-curve points preceding the first on-curve
- # will be appended at the end of the contour
- last_offcurves = offcurves
- else:
- for pt, smooth, name, kwargs in offcurves:
- pen.addPoint(pt, None, smooth, name, **kwargs)
- pt, smooth, name, kwargs = points[-1]
- if pt is None:
- assert segment_type == "qcurve"
- # special quadratic contour with no on-curve points:
- # we need to skip the "None" point. See also the Pen
- # protocol's qCurveTo() method and fontTools.pens.basePen
- pass
- else:
- pen.addPoint(pt, segment_type, smooth, name, **kwargs)
- else:
- raise AssertionError("unexpected segment type: %r" % segment_type)
- for pt, smooth, name, kwargs in last_offcurves:
- pen.addPoint(pt, None, smooth, name, **kwargs)
- pen.endPath()
-
- def addComponent(self, baseGlyphName, transformation):
- assert self.currentPath is None
- self.pen.addComponent(baseGlyphName, transformation)
-
-
-class Cu2QuMultiPen:
- """A filter multi-pen to convert cubic bezier curves to quadratic b-splines
- in a interpolation-compatible manner, using the FontTools SegmentPen protocol.
-
- Args:
-
- other_pens: list of SegmentPens used to draw the transformed outlines.
- max_err: maximum approximation error in font units. For optimal results,
- if you know the UPEM of the font, we recommend setting this to a
- value equal, or close to UPEM / 1000.
- reverse_direction: flip the contours' direction but keep starting point.
-
- This pen does not follow the normal SegmentPen protocol. Instead, its
- moveTo/lineTo/qCurveTo/curveTo methods take a list of tuples that are
- arguments that would normally be passed to a SegmentPen, one item for
- each of the pens in other_pens.
- """
-
- # TODO Simplify like 3e8ebcdce592fe8a59ca4c3a294cc9724351e1ce
- # Remove start_pts and _add_moveTO
-
- def __init__(self, other_pens, max_err, reverse_direction=False):
- if reverse_direction:
- other_pens = [
- ReverseContourPen(pen, outputImpliedClosingLine=True)
- for pen in other_pens
- ]
- self.pens = other_pens
- self.max_err = max_err
- self.start_pts = None
- self.current_pts = None
-
- def _check_contour_is_open(self):
- if self.current_pts is None:
- raise AssertionError("moveTo is required")
-
- def _check_contour_is_closed(self):
- if self.current_pts is not None:
- raise AssertionError("closePath or endPath is required")
-
- def _add_moveTo(self):
- if self.start_pts is not None:
- for pt, pen in zip(self.start_pts, self.pens):
- pen.moveTo(*pt)
- self.start_pts = None
-
- def moveTo(self, pts):
- self._check_contour_is_closed()
- self.start_pts = self.current_pts = pts
- self._add_moveTo()
-
- def lineTo(self, pts):
- self._check_contour_is_open()
- self._add_moveTo()
- for pt, pen in zip(pts, self.pens):
- pen.lineTo(*pt)
- self.current_pts = pts
-
- def qCurveTo(self, pointsList):
- self._check_contour_is_open()
- if len(pointsList[0]) == 1:
- self.lineTo([(points[0],) for points in pointsList])
- return
- self._add_moveTo()
- current_pts = []
- for points, pen in zip(pointsList, self.pens):
- pen.qCurveTo(*points)
- current_pts.append((points[-1],))
- self.current_pts = current_pts
-
- def _curves_to_quadratic(self, pointsList):
- curves = []
- for current_pt, points in zip(self.current_pts, pointsList):
- curves.append(current_pt + points)
- quadratics = curves_to_quadratic(curves, [self.max_err] * len(curves))
- pointsList = []
- for quadratic in quadratics:
- pointsList.append(quadratic[1:])
- self.qCurveTo(pointsList)
-
- def curveTo(self, pointsList):
- self._check_contour_is_open()
- self._curves_to_quadratic(pointsList)
-
- def closePath(self):
- self._check_contour_is_open()
- if self.start_pts is None:
- for pen in self.pens:
- pen.closePath()
- self.current_pts = self.start_pts = None
-
- def endPath(self):
- self._check_contour_is_open()
- if self.start_pts is None:
- for pen in self.pens:
- pen.endPath()
- self.current_pts = self.start_pts = None
-
- def addComponent(self, glyphName, transformations):
- self._check_contour_is_closed()
- for trans, pen in zip(transformations, self.pens):
- pen.addComponent(glyphName, trans)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/explicitClosingLinePen.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/explicitClosingLinePen.py
deleted file mode 100644
index e3c9c94..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/explicitClosingLinePen.py
+++ /dev/null
@@ -1,101 +0,0 @@
-from fontTools.pens.filterPen import ContourFilterPen
-
-
-class ExplicitClosingLinePen(ContourFilterPen):
- """A filter pen that adds an explicit lineTo to the first point of each closed
- contour if the end point of the last segment is not already the same as the first point.
- Otherwise, it passes the contour through unchanged.
-
- >>> from pprint import pprint
- >>> from fontTools.pens.recordingPen import RecordingPen
- >>> rec = RecordingPen()
- >>> pen = ExplicitClosingLinePen(rec)
- >>> pen.moveTo((0, 0))
- >>> pen.lineTo((100, 0))
- >>> pen.lineTo((100, 100))
- >>> pen.closePath()
- >>> pprint(rec.value)
- [('moveTo', ((0, 0),)),
- ('lineTo', ((100, 0),)),
- ('lineTo', ((100, 100),)),
- ('lineTo', ((0, 0),)),
- ('closePath', ())]
- >>> rec = RecordingPen()
- >>> pen = ExplicitClosingLinePen(rec)
- >>> pen.moveTo((0, 0))
- >>> pen.lineTo((100, 0))
- >>> pen.lineTo((100, 100))
- >>> pen.lineTo((0, 0))
- >>> pen.closePath()
- >>> pprint(rec.value)
- [('moveTo', ((0, 0),)),
- ('lineTo', ((100, 0),)),
- ('lineTo', ((100, 100),)),
- ('lineTo', ((0, 0),)),
- ('closePath', ())]
- >>> rec = RecordingPen()
- >>> pen = ExplicitClosingLinePen(rec)
- >>> pen.moveTo((0, 0))
- >>> pen.curveTo((100, 0), (0, 100), (100, 100))
- >>> pen.closePath()
- >>> pprint(rec.value)
- [('moveTo', ((0, 0),)),
- ('curveTo', ((100, 0), (0, 100), (100, 100))),
- ('lineTo', ((0, 0),)),
- ('closePath', ())]
- >>> rec = RecordingPen()
- >>> pen = ExplicitClosingLinePen(rec)
- >>> pen.moveTo((0, 0))
- >>> pen.curveTo((100, 0), (0, 100), (100, 100))
- >>> pen.lineTo((0, 0))
- >>> pen.closePath()
- >>> pprint(rec.value)
- [('moveTo', ((0, 0),)),
- ('curveTo', ((100, 0), (0, 100), (100, 100))),
- ('lineTo', ((0, 0),)),
- ('closePath', ())]
- >>> rec = RecordingPen()
- >>> pen = ExplicitClosingLinePen(rec)
- >>> pen.moveTo((0, 0))
- >>> pen.curveTo((100, 0), (0, 100), (0, 0))
- >>> pen.closePath()
- >>> pprint(rec.value)
- [('moveTo', ((0, 0),)),
- ('curveTo', ((100, 0), (0, 100), (0, 0))),
- ('closePath', ())]
- >>> rec = RecordingPen()
- >>> pen = ExplicitClosingLinePen(rec)
- >>> pen.moveTo((0, 0))
- >>> pen.closePath()
- >>> pprint(rec.value)
- [('moveTo', ((0, 0),)), ('closePath', ())]
- >>> rec = RecordingPen()
- >>> pen = ExplicitClosingLinePen(rec)
- >>> pen.closePath()
- >>> pprint(rec.value)
- [('closePath', ())]
- >>> rec = RecordingPen()
- >>> pen = ExplicitClosingLinePen(rec)
- >>> pen.moveTo((0, 0))
- >>> pen.lineTo((100, 0))
- >>> pen.lineTo((100, 100))
- >>> pen.endPath()
- >>> pprint(rec.value)
- [('moveTo', ((0, 0),)),
- ('lineTo', ((100, 0),)),
- ('lineTo', ((100, 100),)),
- ('endPath', ())]
- """
-
- def filterContour(self, contour):
- if (
- not contour
- or contour[0][0] != "moveTo"
- or contour[-1][0] != "closePath"
- or len(contour) < 3
- ):
- return
- movePt = contour[0][1][0]
- lastSeg = contour[-2][1]
- if lastSeg and movePt != lastSeg[-1]:
- contour[-1:] = [("lineTo", (movePt,)), ("closePath", ())]
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/filterPen.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/filterPen.py
deleted file mode 100644
index 8142310..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/filterPen.py
+++ /dev/null
@@ -1,164 +0,0 @@
-from fontTools.pens.basePen import AbstractPen
-from fontTools.pens.pointPen import AbstractPointPen
-from fontTools.pens.recordingPen import RecordingPen
-
-
-class _PassThruComponentsMixin(object):
- def addComponent(self, glyphName, transformation, **kwargs):
- self._outPen.addComponent(glyphName, transformation, **kwargs)
-
-
-class FilterPen(_PassThruComponentsMixin, AbstractPen):
-
- """Base class for pens that apply some transformation to the coordinates
- they receive and pass them to another pen.
-
- You can override any of its methods. The default implementation does
- nothing, but passes the commands unmodified to the other pen.
-
- >>> from fontTools.pens.recordingPen import RecordingPen
- >>> rec = RecordingPen()
- >>> pen = FilterPen(rec)
- >>> v = iter(rec.value)
-
- >>> pen.moveTo((0, 0))
- >>> next(v)
- ('moveTo', ((0, 0),))
-
- >>> pen.lineTo((1, 1))
- >>> next(v)
- ('lineTo', ((1, 1),))
-
- >>> pen.curveTo((2, 2), (3, 3), (4, 4))
- >>> next(v)
- ('curveTo', ((2, 2), (3, 3), (4, 4)))
-
- >>> pen.qCurveTo((5, 5), (6, 6), (7, 7), (8, 8))
- >>> next(v)
- ('qCurveTo', ((5, 5), (6, 6), (7, 7), (8, 8)))
-
- >>> pen.closePath()
- >>> next(v)
- ('closePath', ())
-
- >>> pen.moveTo((9, 9))
- >>> next(v)
- ('moveTo', ((9, 9),))
-
- >>> pen.endPath()
- >>> next(v)
- ('endPath', ())
-
- >>> pen.addComponent('foo', (1, 0, 0, 1, 0, 0))
- >>> next(v)
- ('addComponent', ('foo', (1, 0, 0, 1, 0, 0)))
- """
-
- def __init__(self, outPen):
- self._outPen = outPen
- self.current_pt = None
-
- def moveTo(self, pt):
- self._outPen.moveTo(pt)
- self.current_pt = pt
-
- def lineTo(self, pt):
- self._outPen.lineTo(pt)
- self.current_pt = pt
-
- def curveTo(self, *points):
- self._outPen.curveTo(*points)
- self.current_pt = points[-1]
-
- def qCurveTo(self, *points):
- self._outPen.qCurveTo(*points)
- self.current_pt = points[-1]
-
- def closePath(self):
- self._outPen.closePath()
- self.current_pt = None
-
- def endPath(self):
- self._outPen.endPath()
- self.current_pt = None
-
-
-class ContourFilterPen(_PassThruComponentsMixin, RecordingPen):
- """A "buffered" filter pen that accumulates contour data, passes
- it through a ``filterContour`` method when the contour is closed or ended,
- and finally draws the result with the output pen.
-
- Components are passed through unchanged.
- """
-
- def __init__(self, outPen):
- super(ContourFilterPen, self).__init__()
- self._outPen = outPen
-
- def closePath(self):
- super(ContourFilterPen, self).closePath()
- self._flushContour()
-
- def endPath(self):
- super(ContourFilterPen, self).endPath()
- self._flushContour()
-
- def _flushContour(self):
- result = self.filterContour(self.value)
- if result is not None:
- self.value = result
- self.replay(self._outPen)
- self.value = []
-
- def filterContour(self, contour):
- """Subclasses must override this to perform the filtering.
-
- The contour is a list of pen (operator, operands) tuples.
- Operators are strings corresponding to the AbstractPen methods:
- "moveTo", "lineTo", "curveTo", "qCurveTo", "closePath" and
- "endPath". The operands are the positional arguments that are
- passed to each method.
-
- If the method doesn't return a value (i.e. returns None), it's
- assumed that the argument was modified in-place.
- Otherwise, the return value is drawn with the output pen.
- """
- return # or return contour
-
-
-class FilterPointPen(_PassThruComponentsMixin, AbstractPointPen):
- """Baseclass for point pens that apply some transformation to the
- coordinates they receive and pass them to another point pen.
-
- You can override any of its methods. The default implementation does
- nothing, but passes the commands unmodified to the other pen.
-
- >>> from fontTools.pens.recordingPen import RecordingPointPen
- >>> rec = RecordingPointPen()
- >>> pen = FilterPointPen(rec)
- >>> v = iter(rec.value)
- >>> pen.beginPath(identifier="abc")
- >>> next(v)
- ('beginPath', (), {'identifier': 'abc'})
- >>> pen.addPoint((1, 2), "line", False)
- >>> next(v)
- ('addPoint', ((1, 2), 'line', False, None), {})
- >>> pen.addComponent("a", (2, 0, 0, 2, 10, -10), identifier="0001")
- >>> next(v)
- ('addComponent', ('a', (2, 0, 0, 2, 10, -10)), {'identifier': '0001'})
- >>> pen.endPath()
- >>> next(v)
- ('endPath', (), {})
- """
-
- def __init__(self, outPointPen):
- self._outPen = outPointPen
-
- def beginPath(self, **kwargs):
- self._outPen.beginPath(**kwargs)
-
- def endPath(self):
- self._outPen.endPath()
-
- def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
- self._outPen.addPoint(pt, segmentType, smooth, name, **kwargs)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/freetypePen.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/freetypePen.py
deleted file mode 100644
index 870776b..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/freetypePen.py
+++ /dev/null
@@ -1,458 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""Pen to rasterize paths with FreeType."""
-
-__all__ = ["FreeTypePen"]
-
-import os
-import ctypes
-import platform
-import subprocess
-import collections
-import math
-
-import freetype
-from freetype.raw import FT_Outline_Get_Bitmap, FT_Outline_Get_BBox, FT_Outline_Get_CBox
-from freetype.ft_types import FT_Pos
-from freetype.ft_structs import FT_Vector, FT_BBox, FT_Bitmap, FT_Outline
-from freetype.ft_enums import (
- FT_OUTLINE_NONE,
- FT_OUTLINE_EVEN_ODD_FILL,
- FT_PIXEL_MODE_GRAY,
- FT_CURVE_TAG_ON,
- FT_CURVE_TAG_CONIC,
- FT_CURVE_TAG_CUBIC,
-)
-from freetype.ft_errors import FT_Exception
-
-from fontTools.pens.basePen import BasePen, PenError
-from fontTools.misc.roundTools import otRound
-from fontTools.misc.transform import Transform
-
-Contour = collections.namedtuple("Contour", ("points", "tags"))
-
-
-class FreeTypePen(BasePen):
- """Pen to rasterize paths with FreeType. Requires `freetype-py` module.
-
- Constructs ``FT_Outline`` from the paths, and renders it within a bitmap
- buffer.
-
- For ``array()`` and ``show()``, `numpy` and `matplotlib` must be installed.
- For ``image()``, `Pillow` is required. Each module is lazily loaded when the
- corresponding method is called.
-
- Args:
- glyphSet: a dictionary of drawable glyph objects keyed by name
- used to resolve component references in composite glyphs.
-
- :Examples:
- If `numpy` and `matplotlib` is available, the following code will
- show the glyph image of `fi` in a new window::
-
- from fontTools.ttLib import TTFont
- from fontTools.pens.freetypePen import FreeTypePen
- from fontTools.misc.transform import Offset
- pen = FreeTypePen(None)
- font = TTFont('SourceSansPro-Regular.otf')
- glyph = font.getGlyphSet()['fi']
- glyph.draw(pen)
- width, ascender, descender = glyph.width, font['OS/2'].usWinAscent, -font['OS/2'].usWinDescent
- height = ascender - descender
- pen.show(width=width, height=height, transform=Offset(0, -descender))
-
- Combining with `uharfbuzz`, you can typeset a chunk of glyphs in a pen::
-
- import uharfbuzz as hb
- from fontTools.pens.freetypePen import FreeTypePen
- from fontTools.pens.transformPen import TransformPen
- from fontTools.misc.transform import Offset
-
- en1, en2, ar, ja = 'Typesetting', 'Jeff', 'صف الحروف', 'たいぷせっと'
- for text, font_path, direction, typo_ascender, typo_descender, vhea_ascender, vhea_descender, contain, features in (
- (en1, 'NotoSans-Regular.ttf', 'ltr', 2189, -600, None, None, False, {"kern": True, "liga": True}),
- (en2, 'NotoSans-Regular.ttf', 'ltr', 2189, -600, None, None, True, {"kern": True, "liga": True}),
- (ar, 'NotoSansArabic-Regular.ttf', 'rtl', 1374, -738, None, None, False, {"kern": True, "liga": True}),
- (ja, 'NotoSansJP-Regular.otf', 'ltr', 880, -120, 500, -500, False, {"palt": True, "kern": True}),
- (ja, 'NotoSansJP-Regular.otf', 'ttb', 880, -120, 500, -500, False, {"vert": True, "vpal": True, "vkrn": True})
- ):
- blob = hb.Blob.from_file_path(font_path)
- face = hb.Face(blob)
- font = hb.Font(face)
- buf = hb.Buffer()
- buf.direction = direction
- buf.add_str(text)
- buf.guess_segment_properties()
- hb.shape(font, buf, features)
-
- x, y = 0, 0
- pen = FreeTypePen(None)
- for info, pos in zip(buf.glyph_infos, buf.glyph_positions):
- gid = info.codepoint
- transformed = TransformPen(pen, Offset(x + pos.x_offset, y + pos.y_offset))
- font.draw_glyph_with_pen(gid, transformed)
- x += pos.x_advance
- y += pos.y_advance
-
- offset, width, height = None, None, None
- if direction in ('ltr', 'rtl'):
- offset = (0, -typo_descender)
- width = x
- height = typo_ascender - typo_descender
- else:
- offset = (-vhea_descender, -y)
- width = vhea_ascender - vhea_descender
- height = -y
- pen.show(width=width, height=height, transform=Offset(*offset), contain=contain)
-
- For Jupyter Notebook, the rendered image will be displayed in a cell if
- you replace ``show()`` with ``image()`` in the examples.
- """
-
- def __init__(self, glyphSet):
- BasePen.__init__(self, glyphSet)
- self.contours = []
-
- def outline(self, transform=None, evenOdd=False):
- """Converts the current contours to ``FT_Outline``.
-
- Args:
- transform: An optional 6-tuple containing an affine transformation,
- or a ``Transform`` object from the ``fontTools.misc.transform``
- module.
- evenOdd: Pass ``True`` for even-odd fill instead of non-zero.
- """
- transform = transform or Transform()
- if not hasattr(transform, "transformPoint"):
- transform = Transform(*transform)
- n_contours = len(self.contours)
- n_points = sum((len(contour.points) for contour in self.contours))
- points = []
- for contour in self.contours:
- for point in contour.points:
- point = transform.transformPoint(point)
- points.append(
- FT_Vector(
- FT_Pos(otRound(point[0] * 64)), FT_Pos(otRound(point[1] * 64))
- )
- )
- tags = []
- for contour in self.contours:
- for tag in contour.tags:
- tags.append(tag)
- contours = []
- contours_sum = 0
- for contour in self.contours:
- contours_sum += len(contour.points)
- contours.append(contours_sum - 1)
- flags = FT_OUTLINE_EVEN_ODD_FILL if evenOdd else FT_OUTLINE_NONE
- return FT_Outline(
- (ctypes.c_short)(n_contours),
- (ctypes.c_short)(n_points),
- (FT_Vector * n_points)(*points),
- (ctypes.c_ubyte * n_points)(*tags),
- (ctypes.c_short * n_contours)(*contours),
- (ctypes.c_int)(flags),
- )
-
- def buffer(
- self, width=None, height=None, transform=None, contain=False, evenOdd=False
- ):
- """Renders the current contours within a bitmap buffer.
-
- Args:
- width: Image width of the bitmap in pixels. If omitted, it
- automatically fits to the bounding box of the contours.
- height: Image height of the bitmap in pixels. If omitted, it
- automatically fits to the bounding box of the contours.
- transform: An optional 6-tuple containing an affine transformation,
- or a ``Transform`` object from the ``fontTools.misc.transform``
- module. The bitmap size is not affected by this matrix.
- contain: If ``True``, the image size will be automatically expanded
- so that it fits to the bounding box of the paths. Useful for
- rendering glyphs with negative sidebearings without clipping.
- evenOdd: Pass ``True`` for even-odd fill instead of non-zero.
-
- Returns:
- A tuple of ``(buffer, size)``, where ``buffer`` is a ``bytes``
- object of the resulted bitmap and ``size`` is a 2-tuple of its
- dimension.
-
- :Notes:
- The image size should always be given explicitly if you need to get
- a proper glyph image. When ``width`` and ``height`` are omitted, it
- forcifully fits to the bounding box and the side bearings get
- cropped. If you pass ``0`` to both ``width`` and ``height`` and set
- ``contain`` to ``True``, it expands to the bounding box while
- maintaining the origin of the contours, meaning that LSB will be
- maintained but RSB won’t. The difference between the two becomes
- more obvious when rotate or skew transformation is applied.
-
- :Example:
- .. code-block::
-
- >> pen = FreeTypePen(None)
- >> glyph.draw(pen)
- >> buf, size = pen.buffer(width=500, height=1000)
- >> type(buf), len(buf), size
- (, 500000, (500, 1000))
-
- """
- transform = transform or Transform()
- if not hasattr(transform, "transformPoint"):
- transform = Transform(*transform)
- contain_x, contain_y = contain or width is None, contain or height is None
- if contain_x or contain_y:
- dx, dy = transform.dx, transform.dy
- bbox = self.bbox
- p1, p2, p3, p4 = (
- transform.transformPoint((bbox[0], bbox[1])),
- transform.transformPoint((bbox[2], bbox[1])),
- transform.transformPoint((bbox[0], bbox[3])),
- transform.transformPoint((bbox[2], bbox[3])),
- )
- px, py = (p1[0], p2[0], p3[0], p4[0]), (p1[1], p2[1], p3[1], p4[1])
- if contain_x:
- if width is None:
- dx = dx - min(*px)
- width = max(*px) - min(*px)
- else:
- dx = dx - min(min(*px), 0.0)
- width = max(width, max(*px) - min(min(*px), 0.0))
- if contain_y:
- if height is None:
- dy = dy - min(*py)
- height = max(*py) - min(*py)
- else:
- dy = dy - min(min(*py), 0.0)
- height = max(height, max(*py) - min(min(*py), 0.0))
- transform = Transform(*transform[:4], dx, dy)
- width, height = math.ceil(width), math.ceil(height)
- buf = ctypes.create_string_buffer(width * height)
- bitmap = FT_Bitmap(
- (ctypes.c_int)(height),
- (ctypes.c_int)(width),
- (ctypes.c_int)(width),
- (ctypes.POINTER(ctypes.c_ubyte))(buf),
- (ctypes.c_short)(256),
- (ctypes.c_ubyte)(FT_PIXEL_MODE_GRAY),
- (ctypes.c_char)(0),
- (ctypes.c_void_p)(None),
- )
- outline = self.outline(transform=transform, evenOdd=evenOdd)
- err = FT_Outline_Get_Bitmap(
- freetype.get_handle(), ctypes.byref(outline), ctypes.byref(bitmap)
- )
- if err != 0:
- raise FT_Exception(err)
- return buf.raw, (width, height)
-
- def array(
- self, width=None, height=None, transform=None, contain=False, evenOdd=False
- ):
- """Returns the rendered contours as a numpy array. Requires `numpy`.
-
- Args:
- width: Image width of the bitmap in pixels. If omitted, it
- automatically fits to the bounding box of the contours.
- height: Image height of the bitmap in pixels. If omitted, it
- automatically fits to the bounding box of the contours.
- transform: An optional 6-tuple containing an affine transformation,
- or a ``Transform`` object from the ``fontTools.misc.transform``
- module. The bitmap size is not affected by this matrix.
- contain: If ``True``, the image size will be automatically expanded
- so that it fits to the bounding box of the paths. Useful for
- rendering glyphs with negative sidebearings without clipping.
- evenOdd: Pass ``True`` for even-odd fill instead of non-zero.
-
- Returns:
- A ``numpy.ndarray`` object with a shape of ``(height, width)``.
- Each element takes a value in the range of ``[0.0, 1.0]``.
-
- :Notes:
- The image size should always be given explicitly if you need to get
- a proper glyph image. When ``width`` and ``height`` are omitted, it
- forcifully fits to the bounding box and the side bearings get
- cropped. If you pass ``0`` to both ``width`` and ``height`` and set
- ``contain`` to ``True``, it expands to the bounding box while
- maintaining the origin of the contours, meaning that LSB will be
- maintained but RSB won’t. The difference between the two becomes
- more obvious when rotate or skew transformation is applied.
-
- :Example:
- .. code-block::
-
- >> pen = FreeTypePen(None)
- >> glyph.draw(pen)
- >> arr = pen.array(width=500, height=1000)
- >> type(a), a.shape
- (, (1000, 500))
- """
- import numpy as np
-
- buf, size = self.buffer(
- width=width,
- height=height,
- transform=transform,
- contain=contain,
- evenOdd=evenOdd,
- )
- return np.frombuffer(buf, "B").reshape((size[1], size[0])) / 255.0
-
- def show(
- self, width=None, height=None, transform=None, contain=False, evenOdd=False
- ):
- """Plots the rendered contours with `pyplot`. Requires `numpy` and
- `matplotlib`.
-
- Args:
- width: Image width of the bitmap in pixels. If omitted, it
- automatically fits to the bounding box of the contours.
- height: Image height of the bitmap in pixels. If omitted, it
- automatically fits to the bounding box of the contours.
- transform: An optional 6-tuple containing an affine transformation,
- or a ``Transform`` object from the ``fontTools.misc.transform``
- module. The bitmap size is not affected by this matrix.
- contain: If ``True``, the image size will be automatically expanded
- so that it fits to the bounding box of the paths. Useful for
- rendering glyphs with negative sidebearings without clipping.
- evenOdd: Pass ``True`` for even-odd fill instead of non-zero.
-
- :Notes:
- The image size should always be given explicitly if you need to get
- a proper glyph image. When ``width`` and ``height`` are omitted, it
- forcifully fits to the bounding box and the side bearings get
- cropped. If you pass ``0`` to both ``width`` and ``height`` and set
- ``contain`` to ``True``, it expands to the bounding box while
- maintaining the origin of the contours, meaning that LSB will be
- maintained but RSB won’t. The difference between the two becomes
- more obvious when rotate or skew transformation is applied.
-
- :Example:
- .. code-block::
-
- >> pen = FreeTypePen(None)
- >> glyph.draw(pen)
- >> pen.show(width=500, height=1000)
- """
- from matplotlib import pyplot as plt
-
- a = self.array(
- width=width,
- height=height,
- transform=transform,
- contain=contain,
- evenOdd=evenOdd,
- )
- plt.imshow(a, cmap="gray_r", vmin=0, vmax=1)
- plt.show()
-
- def image(
- self, width=None, height=None, transform=None, contain=False, evenOdd=False
- ):
- """Returns the rendered contours as a PIL image. Requires `Pillow`.
- Can be used to display a glyph image in Jupyter Notebook.
-
- Args:
- width: Image width of the bitmap in pixels. If omitted, it
- automatically fits to the bounding box of the contours.
- height: Image height of the bitmap in pixels. If omitted, it
- automatically fits to the bounding box of the contours.
- transform: An optional 6-tuple containing an affine transformation,
- or a ``Transform`` object from the ``fontTools.misc.transform``
- module. The bitmap size is not affected by this matrix.
- contain: If ``True``, the image size will be automatically expanded
- so that it fits to the bounding box of the paths. Useful for
- rendering glyphs with negative sidebearings without clipping.
- evenOdd: Pass ``True`` for even-odd fill instead of non-zero.
-
- Returns:
- A ``PIL.image`` object. The image is filled in black with alpha
- channel obtained from the rendered bitmap.
-
- :Notes:
- The image size should always be given explicitly if you need to get
- a proper glyph image. When ``width`` and ``height`` are omitted, it
- forcifully fits to the bounding box and the side bearings get
- cropped. If you pass ``0`` to both ``width`` and ``height`` and set
- ``contain`` to ``True``, it expands to the bounding box while
- maintaining the origin of the contours, meaning that LSB will be
- maintained but RSB won’t. The difference between the two becomes
- more obvious when rotate or skew transformation is applied.
-
- :Example:
- .. code-block::
-
- >> pen = FreeTypePen(None)
- >> glyph.draw(pen)
- >> img = pen.image(width=500, height=1000)
- >> type(img), img.size
- (, (500, 1000))
- """
- from PIL import Image
-
- buf, size = self.buffer(
- width=width,
- height=height,
- transform=transform,
- contain=contain,
- evenOdd=evenOdd,
- )
- img = Image.new("L", size, 0)
- img.putalpha(Image.frombuffer("L", size, buf))
- return img
-
- @property
- def bbox(self):
- """Computes the exact bounding box of an outline.
-
- Returns:
- A tuple of ``(xMin, yMin, xMax, yMax)``.
- """
- bbox = FT_BBox()
- outline = self.outline()
- FT_Outline_Get_BBox(ctypes.byref(outline), ctypes.byref(bbox))
- return (bbox.xMin / 64.0, bbox.yMin / 64.0, bbox.xMax / 64.0, bbox.yMax / 64.0)
-
- @property
- def cbox(self):
- """Returns an outline's ‘control box’.
-
- Returns:
- A tuple of ``(xMin, yMin, xMax, yMax)``.
- """
- cbox = FT_BBox()
- outline = self.outline()
- FT_Outline_Get_CBox(ctypes.byref(outline), ctypes.byref(cbox))
- return (cbox.xMin / 64.0, cbox.yMin / 64.0, cbox.xMax / 64.0, cbox.yMax / 64.0)
-
- def _moveTo(self, pt):
- contour = Contour([], [])
- self.contours.append(contour)
- contour.points.append(pt)
- contour.tags.append(FT_CURVE_TAG_ON)
-
- def _lineTo(self, pt):
- if not (self.contours and len(self.contours[-1].points) > 0):
- raise PenError("Contour missing required initial moveTo")
- contour = self.contours[-1]
- contour.points.append(pt)
- contour.tags.append(FT_CURVE_TAG_ON)
-
- def _curveToOne(self, p1, p2, p3):
- if not (self.contours and len(self.contours[-1].points) > 0):
- raise PenError("Contour missing required initial moveTo")
- t1, t2, t3 = FT_CURVE_TAG_CUBIC, FT_CURVE_TAG_CUBIC, FT_CURVE_TAG_ON
- contour = self.contours[-1]
- for p, t in ((p1, t1), (p2, t2), (p3, t3)):
- contour.points.append(p)
- contour.tags.append(t)
-
- def _qCurveToOne(self, p1, p2):
- if not (self.contours and len(self.contours[-1].points) > 0):
- raise PenError("Contour missing required initial moveTo")
- t1, t2 = FT_CURVE_TAG_CONIC, FT_CURVE_TAG_ON
- contour = self.contours[-1]
- for p, t in ((p1, t1), (p2, t2)):
- contour.points.append(p)
- contour.tags.append(t)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/hashPointPen.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/hashPointPen.py
deleted file mode 100644
index b82468e..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/hashPointPen.py
+++ /dev/null
@@ -1,75 +0,0 @@
-# Modified from https://github.com/adobe-type-tools/psautohint/blob/08b346865710ed3c172f1eb581d6ef243b203f99/python/psautohint/ufoFont.py#L800-L838
-import hashlib
-
-from fontTools.pens.basePen import MissingComponentError
-from fontTools.pens.pointPen import AbstractPointPen
-
-
-class HashPointPen(AbstractPointPen):
- """
- This pen can be used to check if a glyph's contents (outlines plus
- components) have changed.
-
- Components are added as the original outline plus each composite's
- transformation.
-
- Example: You have some TrueType hinting code for a glyph which you want to
- compile. The hinting code specifies a hash value computed with HashPointPen
- that was valid for the glyph's outlines at the time the hinting code was
- written. Now you can calculate the hash for the glyph's current outlines to
- check if the outlines have changed, which would probably make the hinting
- code invalid.
-
- > glyph = ufo[name]
- > hash_pen = HashPointPen(glyph.width, ufo)
- > glyph.drawPoints(hash_pen)
- > ttdata = glyph.lib.get("public.truetype.instructions", None)
- > stored_hash = ttdata.get("id", None) # The hash is stored in the "id" key
- > if stored_hash is None or stored_hash != hash_pen.hash:
- > logger.error(f"Glyph hash mismatch, glyph '{name}' will have no instructions in font.")
- > else:
- > # The hash values are identical, the outline has not changed.
- > # Compile the hinting code ...
- > pass
- """
-
- def __init__(self, glyphWidth=0, glyphSet=None):
- self.glyphset = glyphSet
- self.data = ["w%s" % round(glyphWidth, 9)]
-
- @property
- def hash(self):
- data = "".join(self.data)
- if len(data) >= 128:
- data = hashlib.sha512(data.encode("ascii")).hexdigest()
- return data
-
- def beginPath(self, identifier=None, **kwargs):
- pass
-
- def endPath(self):
- self.data.append("|")
-
- def addPoint(
- self,
- pt,
- segmentType=None,
- smooth=False,
- name=None,
- identifier=None,
- **kwargs,
- ):
- if segmentType is None:
- pt_type = "o" # offcurve
- else:
- pt_type = segmentType[0]
- self.data.append(f"{pt_type}{pt[0]:g}{pt[1]:+g}")
-
- def addComponent(self, baseGlyphName, transformation, identifier=None, **kwargs):
- tr = "".join([f"{t:+}" for t in transformation])
- self.data.append("[")
- try:
- self.glyphset[baseGlyphName].drawPoints(self)
- except KeyError:
- raise MissingComponentError(baseGlyphName)
- self.data.append(f"({tr})]")
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/momentsPen.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/momentsPen.py
deleted file mode 100644
index dab0d10..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/momentsPen.py
+++ /dev/null
@@ -1,882 +0,0 @@
-from fontTools.pens.basePen import BasePen, OpenContourError
-
-try:
- import cython
-
- COMPILED = cython.compiled
-except (AttributeError, ImportError):
- # if cython not installed, use mock module with no-op decorators and types
- from fontTools.misc import cython
-
- COMPILED = False
-
-
-__all__ = ["MomentsPen"]
-
-
-class MomentsPen(BasePen):
- def __init__(self, glyphset=None):
- BasePen.__init__(self, glyphset)
-
- self.area = 0
- self.momentX = 0
- self.momentY = 0
- self.momentXX = 0
- self.momentXY = 0
- self.momentYY = 0
-
- def _moveTo(self, p0):
- self.__startPoint = p0
-
- def _closePath(self):
- p0 = self._getCurrentPoint()
- if p0 != self.__startPoint:
- self._lineTo(self.__startPoint)
-
- def _endPath(self):
- p0 = self._getCurrentPoint()
- if p0 != self.__startPoint:
- # Green theorem is not defined on open contours.
- raise OpenContourError("Green theorem is not defined on open contours.")
-
- @cython.locals(r0=cython.double)
- @cython.locals(r1=cython.double)
- @cython.locals(r2=cython.double)
- @cython.locals(r3=cython.double)
- @cython.locals(r4=cython.double)
- @cython.locals(r5=cython.double)
- @cython.locals(r6=cython.double)
- @cython.locals(r7=cython.double)
- @cython.locals(r8=cython.double)
- @cython.locals(r9=cython.double)
- @cython.locals(r10=cython.double)
- @cython.locals(r11=cython.double)
- @cython.locals(r12=cython.double)
- @cython.locals(x0=cython.double, y0=cython.double)
- @cython.locals(x1=cython.double, y1=cython.double)
- def _lineTo(self, p1):
- x0, y0 = self._getCurrentPoint()
- x1, y1 = p1
-
- r0 = x1 * y0
- r1 = x1 * y1
- r2 = x1**2
- r3 = r2 * y1
- r4 = y0 - y1
- r5 = r4 * x0
- r6 = x0**2
- r7 = 2 * y0
- r8 = y0**2
- r9 = y1**2
- r10 = x1**3
- r11 = y0**3
- r12 = y1**3
-
- self.area += -r0 / 2 - r1 / 2 + x0 * (y0 + y1) / 2
- self.momentX += -r2 * y0 / 6 - r3 / 3 - r5 * x1 / 6 + r6 * (r7 + y1) / 6
- self.momentY += (
- -r0 * y1 / 6 - r8 * x1 / 6 - r9 * x1 / 6 + x0 * (r8 + r9 + y0 * y1) / 6
- )
- self.momentXX += (
- -r10 * y0 / 12
- - r10 * y1 / 4
- - r2 * r5 / 12
- - r4 * r6 * x1 / 12
- + x0**3 * (3 * y0 + y1) / 12
- )
- self.momentXY += (
- -r2 * r8 / 24
- - r2 * r9 / 8
- - r3 * r7 / 24
- + r6 * (r7 * y1 + 3 * r8 + r9) / 24
- - x0 * x1 * (r8 - r9) / 12
- )
- self.momentYY += (
- -r0 * r9 / 12
- - r1 * r8 / 12
- - r11 * x1 / 12
- - r12 * x1 / 12
- + x0 * (r11 + r12 + r8 * y1 + r9 * y0) / 12
- )
-
- @cython.locals(r0=cython.double)
- @cython.locals(r1=cython.double)
- @cython.locals(r2=cython.double)
- @cython.locals(r3=cython.double)
- @cython.locals(r4=cython.double)
- @cython.locals(r5=cython.double)
- @cython.locals(r6=cython.double)
- @cython.locals(r7=cython.double)
- @cython.locals(r8=cython.double)
- @cython.locals(r9=cython.double)
- @cython.locals(r10=cython.double)
- @cython.locals(r11=cython.double)
- @cython.locals(r12=cython.double)
- @cython.locals(r13=cython.double)
- @cython.locals(r14=cython.double)
- @cython.locals(r15=cython.double)
- @cython.locals(r16=cython.double)
- @cython.locals(r17=cython.double)
- @cython.locals(r18=cython.double)
- @cython.locals(r19=cython.double)
- @cython.locals(r20=cython.double)
- @cython.locals(r21=cython.double)
- @cython.locals(r22=cython.double)
- @cython.locals(r23=cython.double)
- @cython.locals(r24=cython.double)
- @cython.locals(r25=cython.double)
- @cython.locals(r26=cython.double)
- @cython.locals(r27=cython.double)
- @cython.locals(r28=cython.double)
- @cython.locals(r29=cython.double)
- @cython.locals(r30=cython.double)
- @cython.locals(r31=cython.double)
- @cython.locals(r32=cython.double)
- @cython.locals(r33=cython.double)
- @cython.locals(r34=cython.double)
- @cython.locals(r35=cython.double)
- @cython.locals(r36=cython.double)
- @cython.locals(r37=cython.double)
- @cython.locals(r38=cython.double)
- @cython.locals(r39=cython.double)
- @cython.locals(r40=cython.double)
- @cython.locals(r41=cython.double)
- @cython.locals(r42=cython.double)
- @cython.locals(r43=cython.double)
- @cython.locals(r44=cython.double)
- @cython.locals(r45=cython.double)
- @cython.locals(r46=cython.double)
- @cython.locals(r47=cython.double)
- @cython.locals(r48=cython.double)
- @cython.locals(r49=cython.double)
- @cython.locals(r50=cython.double)
- @cython.locals(r51=cython.double)
- @cython.locals(r52=cython.double)
- @cython.locals(r53=cython.double)
- @cython.locals(x0=cython.double, y0=cython.double)
- @cython.locals(x1=cython.double, y1=cython.double)
- @cython.locals(x2=cython.double, y2=cython.double)
- def _qCurveToOne(self, p1, p2):
- x0, y0 = self._getCurrentPoint()
- x1, y1 = p1
- x2, y2 = p2
-
- r0 = 2 * y1
- r1 = r0 * x2
- r2 = x2 * y2
- r3 = 3 * r2
- r4 = 2 * x1
- r5 = 3 * y0
- r6 = x1**2
- r7 = x2**2
- r8 = 4 * y1
- r9 = 10 * y2
- r10 = 2 * y2
- r11 = r4 * x2
- r12 = x0**2
- r13 = 10 * y0
- r14 = r4 * y2
- r15 = x2 * y0
- r16 = 4 * x1
- r17 = r0 * x1 + r2
- r18 = r2 * r8
- r19 = y1**2
- r20 = 2 * r19
- r21 = y2**2
- r22 = r21 * x2
- r23 = 5 * r22
- r24 = y0**2
- r25 = y0 * y2
- r26 = 5 * r24
- r27 = x1**3
- r28 = x2**3
- r29 = 30 * y1
- r30 = 6 * y1
- r31 = 10 * r7 * x1
- r32 = 5 * y2
- r33 = 12 * r6
- r34 = 30 * x1
- r35 = x1 * y1
- r36 = r3 + 20 * r35
- r37 = 12 * x1
- r38 = 20 * r6
- r39 = 8 * r6 * y1
- r40 = r32 * r7
- r41 = 60 * y1
- r42 = 20 * r19
- r43 = 4 * r19
- r44 = 15 * r21
- r45 = 12 * x2
- r46 = 12 * y2
- r47 = 6 * x1
- r48 = 8 * r19 * x1 + r23
- r49 = 8 * y1**3
- r50 = y2**3
- r51 = y0**3
- r52 = 10 * y1
- r53 = 12 * y1
-
- self.area += (
- -r1 / 6
- - r3 / 6
- + x0 * (r0 + r5 + y2) / 6
- + x1 * y2 / 3
- - y0 * (r4 + x2) / 6
- )
- self.momentX += (
- -r11 * (-r10 + y1) / 30
- + r12 * (r13 + r8 + y2) / 30
- + r6 * y2 / 15
- - r7 * r8 / 30
- - r7 * r9 / 30
- + x0 * (r14 - r15 - r16 * y0 + r17) / 30
- - y0 * (r11 + 2 * r6 + r7) / 30
- )
- self.momentY += (
- -r18 / 30
- - r20 * x2 / 30
- - r23 / 30
- - r24 * (r16 + x2) / 30
- + x0 * (r0 * y2 + r20 + r21 + r25 + r26 + r8 * y0) / 30
- + x1 * y2 * (r10 + y1) / 15
- - y0 * (r1 + r17) / 30
- )
- self.momentXX += (
- r12 * (r1 - 5 * r15 - r34 * y0 + r36 + r9 * x1) / 420
- + 2 * r27 * y2 / 105
- - r28 * r29 / 420
- - r28 * y2 / 4
- - r31 * (r0 - 3 * y2) / 420
- - r6 * x2 * (r0 - r32) / 105
- + x0**3 * (r30 + 21 * y0 + y2) / 84
- - x0
- * (
- r0 * r7
- + r15 * r37
- - r2 * r37
- - r33 * y2
- + r38 * y0
- - r39
- - r40
- + r5 * r7
- )
- / 420
- - y0 * (8 * r27 + 5 * r28 + r31 + r33 * x2) / 420
- )
- self.momentXY += (
- r12 * (r13 * y2 + 3 * r21 + 105 * r24 + r41 * y0 + r42 + r46 * y1) / 840
- - r16 * x2 * (r43 - r44) / 840
- - r21 * r7 / 8
- - r24 * (r38 + r45 * x1 + 3 * r7) / 840
- - r41 * r7 * y2 / 840
- - r42 * r7 / 840
- + r6 * y2 * (r32 + r8) / 210
- + x0
- * (
- -r15 * r8
- + r16 * r25
- + r18
- + r21 * r47
- - r24 * r34
- - r26 * x2
- + r35 * r46
- + r48
- )
- / 420
- - y0 * (r16 * r2 + r30 * r7 + r35 * r45 + r39 + r40) / 420
- )
- self.momentYY += (
- -r2 * r42 / 420
- - r22 * r29 / 420
- - r24 * (r14 + r36 + r52 * x2) / 420
- - r49 * x2 / 420
- - r50 * x2 / 12
- - r51 * (r47 + x2) / 84
- + x0
- * (
- r19 * r46
- + r21 * r5
- + r21 * r52
- + r24 * r29
- + r25 * r53
- + r26 * y2
- + r42 * y0
- + r49
- + 5 * r50
- + 35 * r51
- )
- / 420
- + x1 * y2 * (r43 + r44 + r9 * y1) / 210
- - y0 * (r19 * r45 + r2 * r53 - r21 * r4 + r48) / 420
- )
-
- @cython.locals(r0=cython.double)
- @cython.locals(r1=cython.double)
- @cython.locals(r2=cython.double)
- @cython.locals(r3=cython.double)
- @cython.locals(r4=cython.double)
- @cython.locals(r5=cython.double)
- @cython.locals(r6=cython.double)
- @cython.locals(r7=cython.double)
- @cython.locals(r8=cython.double)
- @cython.locals(r9=cython.double)
- @cython.locals(r10=cython.double)
- @cython.locals(r11=cython.double)
- @cython.locals(r12=cython.double)
- @cython.locals(r13=cython.double)
- @cython.locals(r14=cython.double)
- @cython.locals(r15=cython.double)
- @cython.locals(r16=cython.double)
- @cython.locals(r17=cython.double)
- @cython.locals(r18=cython.double)
- @cython.locals(r19=cython.double)
- @cython.locals(r20=cython.double)
- @cython.locals(r21=cython.double)
- @cython.locals(r22=cython.double)
- @cython.locals(r23=cython.double)
- @cython.locals(r24=cython.double)
- @cython.locals(r25=cython.double)
- @cython.locals(r26=cython.double)
- @cython.locals(r27=cython.double)
- @cython.locals(r28=cython.double)
- @cython.locals(r29=cython.double)
- @cython.locals(r30=cython.double)
- @cython.locals(r31=cython.double)
- @cython.locals(r32=cython.double)
- @cython.locals(r33=cython.double)
- @cython.locals(r34=cython.double)
- @cython.locals(r35=cython.double)
- @cython.locals(r36=cython.double)
- @cython.locals(r37=cython.double)
- @cython.locals(r38=cython.double)
- @cython.locals(r39=cython.double)
- @cython.locals(r40=cython.double)
- @cython.locals(r41=cython.double)
- @cython.locals(r42=cython.double)
- @cython.locals(r43=cython.double)
- @cython.locals(r44=cython.double)
- @cython.locals(r45=cython.double)
- @cython.locals(r46=cython.double)
- @cython.locals(r47=cython.double)
- @cython.locals(r48=cython.double)
- @cython.locals(r49=cython.double)
- @cython.locals(r50=cython.double)
- @cython.locals(r51=cython.double)
- @cython.locals(r52=cython.double)
- @cython.locals(r53=cython.double)
- @cython.locals(r54=cython.double)
- @cython.locals(r55=cython.double)
- @cython.locals(r56=cython.double)
- @cython.locals(r57=cython.double)
- @cython.locals(r58=cython.double)
- @cython.locals(r59=cython.double)
- @cython.locals(r60=cython.double)
- @cython.locals(r61=cython.double)
- @cython.locals(r62=cython.double)
- @cython.locals(r63=cython.double)
- @cython.locals(r64=cython.double)
- @cython.locals(r65=cython.double)
- @cython.locals(r66=cython.double)
- @cython.locals(r67=cython.double)
- @cython.locals(r68=cython.double)
- @cython.locals(r69=cython.double)
- @cython.locals(r70=cython.double)
- @cython.locals(r71=cython.double)
- @cython.locals(r72=cython.double)
- @cython.locals(r73=cython.double)
- @cython.locals(r74=cython.double)
- @cython.locals(r75=cython.double)
- @cython.locals(r76=cython.double)
- @cython.locals(r77=cython.double)
- @cython.locals(r78=cython.double)
- @cython.locals(r79=cython.double)
- @cython.locals(r80=cython.double)
- @cython.locals(r81=cython.double)
- @cython.locals(r82=cython.double)
- @cython.locals(r83=cython.double)
- @cython.locals(r84=cython.double)
- @cython.locals(r85=cython.double)
- @cython.locals(r86=cython.double)
- @cython.locals(r87=cython.double)
- @cython.locals(r88=cython.double)
- @cython.locals(r89=cython.double)
- @cython.locals(r90=cython.double)
- @cython.locals(r91=cython.double)
- @cython.locals(r92=cython.double)
- @cython.locals(r93=cython.double)
- @cython.locals(r94=cython.double)
- @cython.locals(r95=cython.double)
- @cython.locals(r96=cython.double)
- @cython.locals(r97=cython.double)
- @cython.locals(r98=cython.double)
- @cython.locals(r99=cython.double)
- @cython.locals(r100=cython.double)
- @cython.locals(r101=cython.double)
- @cython.locals(r102=cython.double)
- @cython.locals(r103=cython.double)
- @cython.locals(r104=cython.double)
- @cython.locals(r105=cython.double)
- @cython.locals(r106=cython.double)
- @cython.locals(r107=cython.double)
- @cython.locals(r108=cython.double)
- @cython.locals(r109=cython.double)
- @cython.locals(r110=cython.double)
- @cython.locals(r111=cython.double)
- @cython.locals(r112=cython.double)
- @cython.locals(r113=cython.double)
- @cython.locals(r114=cython.double)
- @cython.locals(r115=cython.double)
- @cython.locals(r116=cython.double)
- @cython.locals(r117=cython.double)
- @cython.locals(r118=cython.double)
- @cython.locals(r119=cython.double)
- @cython.locals(r120=cython.double)
- @cython.locals(r121=cython.double)
- @cython.locals(r122=cython.double)
- @cython.locals(r123=cython.double)
- @cython.locals(r124=cython.double)
- @cython.locals(r125=cython.double)
- @cython.locals(r126=cython.double)
- @cython.locals(r127=cython.double)
- @cython.locals(r128=cython.double)
- @cython.locals(r129=cython.double)
- @cython.locals(r130=cython.double)
- @cython.locals(r131=cython.double)
- @cython.locals(r132=cython.double)
- @cython.locals(x0=cython.double, y0=cython.double)
- @cython.locals(x1=cython.double, y1=cython.double)
- @cython.locals(x2=cython.double, y2=cython.double)
- @cython.locals(x3=cython.double, y3=cython.double)
- def _curveToOne(self, p1, p2, p3):
- x0, y0 = self._getCurrentPoint()
- x1, y1 = p1
- x2, y2 = p2
- x3, y3 = p3
-
- r0 = 6 * y2
- r1 = r0 * x3
- r2 = 10 * y3
- r3 = r2 * x3
- r4 = 3 * y1
- r5 = 6 * x1
- r6 = 3 * x2
- r7 = 6 * y1
- r8 = 3 * y2
- r9 = x2**2
- r10 = 45 * r9
- r11 = r10 * y3
- r12 = x3**2
- r13 = r12 * y2
- r14 = r12 * y3
- r15 = 7 * y3
- r16 = 15 * x3
- r17 = r16 * x2
- r18 = x1**2
- r19 = 9 * r18
- r20 = x0**2
- r21 = 21 * y1
- r22 = 9 * r9
- r23 = r7 * x3
- r24 = 9 * y2
- r25 = r24 * x2 + r3
- r26 = 9 * x2
- r27 = x2 * y3
- r28 = -r26 * y1 + 15 * r27
- r29 = 3 * x1
- r30 = 45 * x1
- r31 = 12 * x3
- r32 = 45 * r18
- r33 = 5 * r12
- r34 = r8 * x3
- r35 = 105 * y0
- r36 = 30 * y0
- r37 = r36 * x2
- r38 = 5 * x3
- r39 = 15 * y3
- r40 = 5 * y3
- r41 = r40 * x3
- r42 = x2 * y2
- r43 = 18 * r42
- r44 = 45 * y1
- r45 = r41 + r43 + r44 * x1
- r46 = y2 * y3
- r47 = r46 * x3
- r48 = y2**2
- r49 = 45 * r48
- r50 = r49 * x3
- r51 = y3**2
- r52 = r51 * x3
- r53 = y1**2
- r54 = 9 * r53
- r55 = y0**2
- r56 = 21 * x1
- r57 = 6 * x2
- r58 = r16 * y2
- r59 = r39 * y2
- r60 = 9 * r48
- r61 = r6 * y3
- r62 = 3 * y3
- r63 = r36 * y2
- r64 = y1 * y3
- r65 = 45 * r53
- r66 = 5 * r51
- r67 = x2**3
- r68 = x3**3
- r69 = 630 * y2
- r70 = 126 * x3
- r71 = x1**3
- r72 = 126 * x2
- r73 = 63 * r9
- r74 = r73 * x3
- r75 = r15 * x3 + 15 * r42
- r76 = 630 * x1
- r77 = 14 * x3
- r78 = 21 * r27
- r79 = 42 * x1
- r80 = 42 * x2
- r81 = x1 * y2
- r82 = 63 * r42
- r83 = x1 * y1
- r84 = r41 + r82 + 378 * r83
- r85 = x2 * x3
- r86 = r85 * y1
- r87 = r27 * x3
- r88 = 27 * r9
- r89 = r88 * y2
- r90 = 42 * r14
- r91 = 90 * x1
- r92 = 189 * r18
- r93 = 378 * r18
- r94 = r12 * y1
- r95 = 252 * x1 * x2
- r96 = r79 * x3
- r97 = 30 * r85
- r98 = r83 * x3
- r99 = 30 * x3
- r100 = 42 * x3
- r101 = r42 * x1
- r102 = r10 * y2 + 14 * r14 + 126 * r18 * y1 + r81 * r99
- r103 = 378 * r48
- r104 = 18 * y1
- r105 = r104 * y2
- r106 = y0 * y1
- r107 = 252 * y2
- r108 = r107 * y0
- r109 = y0 * y3
- r110 = 42 * r64
- r111 = 378 * r53
- r112 = 63 * r48
- r113 = 27 * x2
- r114 = r27 * y2
- r115 = r113 * r48 + 42 * r52
- r116 = x3 * y3
- r117 = 54 * r42
- r118 = r51 * x1
- r119 = r51 * x2
- r120 = r48 * x1
- r121 = 21 * x3
- r122 = r64 * x1
- r123 = r81 * y3
- r124 = 30 * r27 * y1 + r49 * x2 + 14 * r52 + 126 * r53 * x1
- r125 = y2**3
- r126 = y3**3
- r127 = y1**3
- r128 = y0**3
- r129 = r51 * y2
- r130 = r112 * y3 + r21 * r51
- r131 = 189 * r53
- r132 = 90 * y2
-
- self.area += (
- -r1 / 20
- - r3 / 20
- - r4 * (x2 + x3) / 20
- + x0 * (r7 + r8 + 10 * y0 + y3) / 20
- + 3 * x1 * (y2 + y3) / 20
- + 3 * x2 * y3 / 10
- - y0 * (r5 + r6 + x3) / 20
- )
- self.momentX += (
- r11 / 840
- - r13 / 8
- - r14 / 3
- - r17 * (-r15 + r8) / 840
- + r19 * (r8 + 2 * y3) / 840
- + r20 * (r0 + r21 + 56 * y0 + y3) / 168
- + r29 * (-r23 + r25 + r28) / 840
- - r4 * (10 * r12 + r17 + r22) / 840
- + x0
- * (
- 12 * r27
- + r30 * y2
- + r34
- - r35 * x1
- - r37
- - r38 * y0
- + r39 * x1
- - r4 * x3
- + r45
- )
- / 840
- - y0 * (r17 + r30 * x2 + r31 * x1 + r32 + r33 + 18 * r9) / 840
- )
- self.momentY += (
- -r4 * (r25 + r58) / 840
- - r47 / 8
- - r50 / 840
- - r52 / 6
- - r54 * (r6 + 2 * x3) / 840
- - r55 * (r56 + r57 + x3) / 168
- + x0
- * (
- r35 * y1
- + r40 * y0
- + r44 * y2
- + 18 * r48
- + 140 * r55
- + r59
- + r63
- + 12 * r64
- + r65
- + r66
- )
- / 840
- + x1 * (r24 * y1 + 10 * r51 + r59 + r60 + r7 * y3) / 280
- + x2 * y3 * (r15 + r8) / 56
- - y0 * (r16 * y1 + r31 * y2 + r44 * x2 + r45 + r61 - r62 * x1) / 840
- )
- self.momentXX += (
- -r12 * r72 * (-r40 + r8) / 9240
- + 3 * r18 * (r28 + r34 - r38 * y1 + r75) / 3080
- + r20
- * (
- r24 * x3
- - r72 * y0
- - r76 * y0
- - r77 * y0
- + r78
- + r79 * y3
- + r80 * y1
- + 210 * r81
- + r84
- )
- / 9240
- - r29
- * (
- r12 * r21
- + 14 * r13
- + r44 * r9
- - r73 * y3
- + 54 * r86
- - 84 * r87
- - r89
- - r90
- )
- / 9240
- - r4 * (70 * r12 * x2 + 27 * r67 + 42 * r68 + r74) / 9240
- + 3 * r67 * y3 / 220
- - r68 * r69 / 9240
- - r68 * y3 / 4
- - r70 * r9 * (-r62 + y2) / 9240
- + 3 * r71 * (r24 + r40) / 3080
- + x0**3 * (r24 + r44 + 165 * y0 + y3) / 660
- + x0
- * (
- r100 * r27
- + 162 * r101
- + r102
- + r11
- + 63 * r18 * y3
- + r27 * r91
- - r33 * y0
- - r37 * x3
- + r43 * x3
- - r73 * y0
- - r88 * y1
- + r92 * y2
- - r93 * y0
- - 9 * r94
- - r95 * y0
- - r96 * y0
- - r97 * y1
- - 18 * r98
- + r99 * x1 * y3
- )
- / 9240
- - y0
- * (
- r12 * r56
- + r12 * r80
- + r32 * x3
- + 45 * r67
- + 14 * r68
- + 126 * r71
- + r74
- + r85 * r91
- + 135 * r9 * x1
- + r92 * x2
- )
- / 9240
- )
- self.momentXY += (
- -r103 * r12 / 18480
- - r12 * r51 / 8
- - 3 * r14 * y2 / 44
- + 3 * r18 * (r105 + r2 * y1 + 18 * r46 + 15 * r48 + 7 * r51) / 6160
- + r20
- * (
- 1260 * r106
- + r107 * y1
- + r108
- + 28 * r109
- + r110
- + r111
- + r112
- + 30 * r46
- + 2310 * r55
- + r66
- )
- / 18480
- - r54 * (7 * r12 + 18 * r85 + 15 * r9) / 18480
- - r55 * (r33 + r73 + r93 + r95 + r96 + r97) / 18480
- - r7 * (42 * r13 + r82 * x3 + 28 * r87 + r89 + r90) / 18480
- - 3 * r85 * (r48 - r66) / 220
- + 3 * r9 * y3 * (r62 + 2 * y2) / 440
- + x0
- * (
- -r1 * y0
- - 84 * r106 * x2
- + r109 * r56
- + 54 * r114
- + r117 * y1
- + 15 * r118
- + 21 * r119
- + 81 * r120
- + r121 * r46
- + 54 * r122
- + 60 * r123
- + r124
- - r21 * x3 * y0
- + r23 * y3
- - r54 * x3
- - r55 * r72
- - r55 * r76
- - r55 * r77
- + r57 * y0 * y3
- + r60 * x3
- + 84 * r81 * y0
- + 189 * r81 * y1
- )
- / 9240
- + x1
- * (
- r104 * r27
- - r105 * x3
- - r113 * r53
- + 63 * r114
- + r115
- - r16 * r53
- + 28 * r47
- + r51 * r80
- )
- / 3080
- - y0
- * (
- 54 * r101
- + r102
- + r116 * r5
- + r117 * x3
- + 21 * r13
- - r19 * y3
- + r22 * y3
- + r78 * x3
- + 189 * r83 * x2
- + 60 * r86
- + 81 * r9 * y1
- + 15 * r94
- + 54 * r98
- )
- / 9240
- )
- self.momentYY += (
- -r103 * r116 / 9240
- - r125 * r70 / 9240
- - r126 * x3 / 12
- - 3 * r127 * (r26 + r38) / 3080
- - r128 * (r26 + r30 + x3) / 660
- - r4 * (r112 * x3 + r115 - 14 * r119 + 84 * r47) / 9240
- - r52 * r69 / 9240
- - r54 * (r58 + r61 + r75) / 9240
- - r55
- * (r100 * y1 + r121 * y2 + r26 * y3 + r79 * y2 + r84 + 210 * x2 * y1)
- / 9240
- + x0
- * (
- r108 * y1
- + r110 * y0
- + r111 * y0
- + r112 * y0
- + 45 * r125
- + 14 * r126
- + 126 * r127
- + 770 * r128
- + 42 * r129
- + r130
- + r131 * y2
- + r132 * r64
- + 135 * r48 * y1
- + 630 * r55 * y1
- + 126 * r55 * y2
- + 14 * r55 * y3
- + r63 * y3
- + r65 * y3
- + r66 * y0
- )
- / 9240
- + x1
- * (
- 27 * r125
- + 42 * r126
- + 70 * r129
- + r130
- + r39 * r53
- + r44 * r48
- + 27 * r53 * y2
- + 54 * r64 * y2
- )
- / 3080
- + 3 * x2 * y3 * (r48 + r66 + r8 * y3) / 220
- - y0
- * (
- r100 * r46
- + 18 * r114
- - 9 * r118
- - 27 * r120
- - 18 * r122
- - 30 * r123
- + r124
- + r131 * x2
- + r132 * x3 * y1
- + 162 * r42 * y1
- + r50
- + 63 * r53 * x3
- + r64 * r99
- )
- / 9240
- )
-
-
-if __name__ == "__main__":
- from fontTools.misc.symfont import x, y, printGreenPen
-
- printGreenPen(
- "MomentsPen",
- [
- ("area", 1),
- ("momentX", x),
- ("momentY", y),
- ("momentXX", x**2),
- ("momentXY", x * y),
- ("momentYY", y**2),
- ],
- )
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/perimeterPen.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/perimeterPen.py
deleted file mode 100644
index efb2b2d..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/perimeterPen.py
+++ /dev/null
@@ -1,69 +0,0 @@
-# -*- coding: utf-8 -*-
-"""Calculate the perimeter of a glyph."""
-
-from fontTools.pens.basePen import BasePen
-from fontTools.misc.bezierTools import (
- approximateQuadraticArcLengthC,
- calcQuadraticArcLengthC,
- approximateCubicArcLengthC,
- calcCubicArcLengthC,
-)
-import math
-
-
-__all__ = ["PerimeterPen"]
-
-
-def _distance(p0, p1):
- return math.hypot(p0[0] - p1[0], p0[1] - p1[1])
-
-
-class PerimeterPen(BasePen):
- def __init__(self, glyphset=None, tolerance=0.005):
- BasePen.__init__(self, glyphset)
- self.value = 0
- self.tolerance = tolerance
-
- # Choose which algorithm to use for quadratic and for cubic.
- # Quadrature is faster but has fixed error characteristic with no strong
- # error bound. The cutoff points are derived empirically.
- self._addCubic = (
- self._addCubicQuadrature if tolerance >= 0.0015 else self._addCubicRecursive
- )
- self._addQuadratic = (
- self._addQuadraticQuadrature
- if tolerance >= 0.00075
- else self._addQuadraticExact
- )
-
- def _moveTo(self, p0):
- self.__startPoint = p0
-
- def _closePath(self):
- p0 = self._getCurrentPoint()
- if p0 != self.__startPoint:
- self._lineTo(self.__startPoint)
-
- def _lineTo(self, p1):
- p0 = self._getCurrentPoint()
- self.value += _distance(p0, p1)
-
- def _addQuadraticExact(self, c0, c1, c2):
- self.value += calcQuadraticArcLengthC(c0, c1, c2)
-
- def _addQuadraticQuadrature(self, c0, c1, c2):
- self.value += approximateQuadraticArcLengthC(c0, c1, c2)
-
- def _qCurveToOne(self, p1, p2):
- p0 = self._getCurrentPoint()
- self._addQuadratic(complex(*p0), complex(*p1), complex(*p2))
-
- def _addCubicRecursive(self, c0, c1, c2, c3):
- self.value += calcCubicArcLengthC(c0, c1, c2, c3, self.tolerance)
-
- def _addCubicQuadrature(self, c0, c1, c2, c3):
- self.value += approximateCubicArcLengthC(c0, c1, c2, c3)
-
- def _curveToOne(self, p1, p2, p3):
- p0 = self._getCurrentPoint()
- self._addCubic(complex(*p0), complex(*p1), complex(*p2), complex(*p3))
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/pointInsidePen.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/pointInsidePen.py
deleted file mode 100644
index 8a579ae..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/pointInsidePen.py
+++ /dev/null
@@ -1,192 +0,0 @@
-"""fontTools.pens.pointInsidePen -- Pen implementing "point inside" testing
-for shapes.
-"""
-
-from fontTools.pens.basePen import BasePen
-from fontTools.misc.bezierTools import solveQuadratic, solveCubic
-
-
-__all__ = ["PointInsidePen"]
-
-
-class PointInsidePen(BasePen):
-
- """This pen implements "point inside" testing: to test whether
- a given point lies inside the shape (black) or outside (white).
- Instances of this class can be recycled, as long as the
- setTestPoint() method is used to set the new point to test.
-
- Typical usage:
-
- pen = PointInsidePen(glyphSet, (100, 200))
- outline.draw(pen)
- isInside = pen.getResult()
-
- Both the even-odd algorithm and the non-zero-winding-rule
- algorithm are implemented. The latter is the default, specify
- True for the evenOdd argument of __init__ or setTestPoint
- to use the even-odd algorithm.
- """
-
- # This class implements the classical "shoot a ray from the test point
- # to infinity and count how many times it intersects the outline" (as well
- # as the non-zero variant, where the counter is incremented if the outline
- # intersects the ray in one direction and decremented if it intersects in
- # the other direction).
- # I found an amazingly clear explanation of the subtleties involved in
- # implementing this correctly for polygons here:
- # http://graphics.cs.ucdavis.edu/~okreylos/TAship/Spring2000/PointInPolygon.html
- # I extended the principles outlined on that page to curves.
-
- def __init__(self, glyphSet, testPoint, evenOdd=False):
- BasePen.__init__(self, glyphSet)
- self.setTestPoint(testPoint, evenOdd)
-
- def setTestPoint(self, testPoint, evenOdd=False):
- """Set the point to test. Call this _before_ the outline gets drawn."""
- self.testPoint = testPoint
- self.evenOdd = evenOdd
- self.firstPoint = None
- self.intersectionCount = 0
-
- def getWinding(self):
- if self.firstPoint is not None:
- # always make sure the sub paths are closed; the algorithm only works
- # for closed paths.
- self.closePath()
- return self.intersectionCount
-
- def getResult(self):
- """After the shape has been drawn, getResult() returns True if the test
- point lies within the (black) shape, and False if it doesn't.
- """
- winding = self.getWinding()
- if self.evenOdd:
- result = winding % 2
- else: # non-zero
- result = self.intersectionCount != 0
- return not not result
-
- def _addIntersection(self, goingUp):
- if self.evenOdd or goingUp:
- self.intersectionCount += 1
- else:
- self.intersectionCount -= 1
-
- def _moveTo(self, point):
- if self.firstPoint is not None:
- # always make sure the sub paths are closed; the algorithm only works
- # for closed paths.
- self.closePath()
- self.firstPoint = point
-
- def _lineTo(self, point):
- x, y = self.testPoint
- x1, y1 = self._getCurrentPoint()
- x2, y2 = point
-
- if x1 < x and x2 < x:
- return
- if y1 < y and y2 < y:
- return
- if y1 >= y and y2 >= y:
- return
-
- dx = x2 - x1
- dy = y2 - y1
- t = (y - y1) / dy
- ix = dx * t + x1
- if ix < x:
- return
- self._addIntersection(y2 > y1)
-
- def _curveToOne(self, bcp1, bcp2, point):
- x, y = self.testPoint
- x1, y1 = self._getCurrentPoint()
- x2, y2 = bcp1
- x3, y3 = bcp2
- x4, y4 = point
-
- if x1 < x and x2 < x and x3 < x and x4 < x:
- return
- if y1 < y and y2 < y and y3 < y and y4 < y:
- return
- if y1 >= y and y2 >= y and y3 >= y and y4 >= y:
- return
-
- dy = y1
- cy = (y2 - dy) * 3.0
- by = (y3 - y2) * 3.0 - cy
- ay = y4 - dy - cy - by
- solutions = sorted(solveCubic(ay, by, cy, dy - y))
- solutions = [t for t in solutions if -0.0 <= t <= 1.0]
- if not solutions:
- return
-
- dx = x1
- cx = (x2 - dx) * 3.0
- bx = (x3 - x2) * 3.0 - cx
- ax = x4 - dx - cx - bx
-
- above = y1 >= y
- lastT = None
- for t in solutions:
- if t == lastT:
- continue
- lastT = t
- t2 = t * t
- t3 = t2 * t
-
- direction = 3 * ay * t2 + 2 * by * t + cy
- incomingGoingUp = outgoingGoingUp = direction > 0.0
- if direction == 0.0:
- direction = 6 * ay * t + 2 * by
- outgoingGoingUp = direction > 0.0
- incomingGoingUp = not outgoingGoingUp
- if direction == 0.0:
- direction = ay
- incomingGoingUp = outgoingGoingUp = direction > 0.0
-
- xt = ax * t3 + bx * t2 + cx * t + dx
- if xt < x:
- continue
-
- if t in (0.0, -0.0):
- if not outgoingGoingUp:
- self._addIntersection(outgoingGoingUp)
- elif t == 1.0:
- if incomingGoingUp:
- self._addIntersection(incomingGoingUp)
- else:
- if incomingGoingUp == outgoingGoingUp:
- self._addIntersection(outgoingGoingUp)
- # else:
- # we're not really intersecting, merely touching
-
- def _qCurveToOne_unfinished(self, bcp, point):
- # XXX need to finish this, for now doing it through a cubic
- # (BasePen implements _qCurveTo in terms of a cubic) will
- # have to do.
- x, y = self.testPoint
- x1, y1 = self._getCurrentPoint()
- x2, y2 = bcp
- x3, y3 = point
- c = y1
- b = (y2 - c) * 2.0
- a = y3 - c - b
- solutions = sorted(solveQuadratic(a, b, c - y))
- solutions = [
- t for t in solutions if ZERO_MINUS_EPSILON <= t <= ONE_PLUS_EPSILON
- ]
- if not solutions:
- return
- # XXX
-
- def _closePath(self):
- if self._getCurrentPoint() != self.firstPoint:
- self.lineTo(self.firstPoint)
- self.firstPoint = None
-
- def _endPath(self):
- """Insideness is not defined for open contours."""
- raise NotImplementedError
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/pointPen.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/pointPen.py
deleted file mode 100644
index eb1ebc2..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/pointPen.py
+++ /dev/null
@@ -1,525 +0,0 @@
-"""
-=========
-PointPens
-=========
-
-Where **SegmentPens** have an intuitive approach to drawing
-(if you're familiar with postscript anyway), the **PointPen**
-is geared towards accessing all the data in the contours of
-the glyph. A PointPen has a very simple interface, it just
-steps through all the points in a call from glyph.drawPoints().
-This allows the caller to provide more data for each point.
-For instance, whether or not a point is smooth, and its name.
-"""
-
-import math
-from typing import Any, Optional, Tuple, Dict
-
-from fontTools.pens.basePen import AbstractPen, PenError
-from fontTools.misc.transform import DecomposedTransform
-
-__all__ = [
- "AbstractPointPen",
- "BasePointToSegmentPen",
- "PointToSegmentPen",
- "SegmentToPointPen",
- "GuessSmoothPointPen",
- "ReverseContourPointPen",
-]
-
-
-class AbstractPointPen:
- """Baseclass for all PointPens."""
-
- def beginPath(self, identifier: Optional[str] = None, **kwargs: Any) -> None:
- """Start a new sub path."""
- raise NotImplementedError
-
- def endPath(self) -> None:
- """End the current sub path."""
- raise NotImplementedError
-
- def addPoint(
- self,
- pt: Tuple[float, float],
- segmentType: Optional[str] = None,
- smooth: bool = False,
- name: Optional[str] = None,
- identifier: Optional[str] = None,
- **kwargs: Any,
- ) -> None:
- """Add a point to the current sub path."""
- raise NotImplementedError
-
- def addComponent(
- self,
- baseGlyphName: str,
- transformation: Tuple[float, float, float, float, float, float],
- identifier: Optional[str] = None,
- **kwargs: Any,
- ) -> None:
- """Add a sub glyph."""
- raise NotImplementedError
-
- def addVarComponent(
- self,
- glyphName: str,
- transformation: DecomposedTransform,
- location: Dict[str, float],
- identifier: Optional[str] = None,
- **kwargs: Any,
- ) -> None:
- """Add a VarComponent sub glyph. The 'transformation' argument
- must be a DecomposedTransform from the fontTools.misc.transform module,
- and the 'location' argument must be a dictionary mapping axis tags
- to their locations.
- """
- # ttGlyphSet decomposes for us
- raise AttributeError
-
-
-class BasePointToSegmentPen(AbstractPointPen):
- """
- Base class for retrieving the outline in a segment-oriented
- way. The PointPen protocol is simple yet also a little tricky,
- so when you need an outline presented as segments but you have
- as points, do use this base implementation as it properly takes
- care of all the edge cases.
- """
-
- def __init__(self):
- self.currentPath = None
-
- def beginPath(self, identifier=None, **kwargs):
- if self.currentPath is not None:
- raise PenError("Path already begun.")
- self.currentPath = []
-
- def _flushContour(self, segments):
- """Override this method.
-
- It will be called for each non-empty sub path with a list
- of segments: the 'segments' argument.
-
- The segments list contains tuples of length 2:
- (segmentType, points)
-
- segmentType is one of "move", "line", "curve" or "qcurve".
- "move" may only occur as the first segment, and it signifies
- an OPEN path. A CLOSED path does NOT start with a "move", in
- fact it will not contain a "move" at ALL.
-
- The 'points' field in the 2-tuple is a list of point info
- tuples. The list has 1 or more items, a point tuple has
- four items:
- (point, smooth, name, kwargs)
- 'point' is an (x, y) coordinate pair.
-
- For a closed path, the initial moveTo point is defined as
- the last point of the last segment.
-
- The 'points' list of "move" and "line" segments always contains
- exactly one point tuple.
- """
- raise NotImplementedError
-
- def endPath(self):
- if self.currentPath is None:
- raise PenError("Path not begun.")
- points = self.currentPath
- self.currentPath = None
- if not points:
- return
- if len(points) == 1:
- # Not much more we can do than output a single move segment.
- pt, segmentType, smooth, name, kwargs = points[0]
- segments = [("move", [(pt, smooth, name, kwargs)])]
- self._flushContour(segments)
- return
- segments = []
- if points[0][1] == "move":
- # It's an open contour, insert a "move" segment for the first
- # point and remove that first point from the point list.
- pt, segmentType, smooth, name, kwargs = points[0]
- segments.append(("move", [(pt, smooth, name, kwargs)]))
- points.pop(0)
- else:
- # It's a closed contour. Locate the first on-curve point, and
- # rotate the point list so that it _ends_ with an on-curve
- # point.
- firstOnCurve = None
- for i in range(len(points)):
- segmentType = points[i][1]
- if segmentType is not None:
- firstOnCurve = i
- break
- if firstOnCurve is None:
- # Special case for quadratics: a contour with no on-curve
- # points. Add a "None" point. (See also the Pen protocol's
- # qCurveTo() method and fontTools.pens.basePen.py.)
- points.append((None, "qcurve", None, None, None))
- else:
- points = points[firstOnCurve + 1 :] + points[: firstOnCurve + 1]
-
- currentSegment = []
- for pt, segmentType, smooth, name, kwargs in points:
- currentSegment.append((pt, smooth, name, kwargs))
- if segmentType is None:
- continue
- segments.append((segmentType, currentSegment))
- currentSegment = []
-
- self._flushContour(segments)
-
- def addPoint(
- self, pt, segmentType=None, smooth=False, name=None, identifier=None, **kwargs
- ):
- if self.currentPath is None:
- raise PenError("Path not begun")
- self.currentPath.append((pt, segmentType, smooth, name, kwargs))
-
-
-class PointToSegmentPen(BasePointToSegmentPen):
- """
- Adapter class that converts the PointPen protocol to the
- (Segment)Pen protocol.
-
- NOTE: The segment pen does not support and will drop point names, identifiers
- and kwargs.
- """
-
- def __init__(self, segmentPen, outputImpliedClosingLine=False):
- BasePointToSegmentPen.__init__(self)
- self.pen = segmentPen
- self.outputImpliedClosingLine = outputImpliedClosingLine
-
- def _flushContour(self, segments):
- if not segments:
- raise PenError("Must have at least one segment.")
- pen = self.pen
- if segments[0][0] == "move":
- # It's an open path.
- closed = False
- points = segments[0][1]
- if len(points) != 1:
- raise PenError(f"Illegal move segment point count: {len(points)}")
- movePt, _, _, _ = points[0]
- del segments[0]
- else:
- # It's a closed path, do a moveTo to the last
- # point of the last segment.
- closed = True
- segmentType, points = segments[-1]
- movePt, _, _, _ = points[-1]
- if movePt is None:
- # quad special case: a contour with no on-curve points contains
- # one "qcurve" segment that ends with a point that's None. We
- # must not output a moveTo() in that case.
- pass
- else:
- pen.moveTo(movePt)
- outputImpliedClosingLine = self.outputImpliedClosingLine
- nSegments = len(segments)
- lastPt = movePt
- for i in range(nSegments):
- segmentType, points = segments[i]
- points = [pt for pt, _, _, _ in points]
- if segmentType == "line":
- if len(points) != 1:
- raise PenError(f"Illegal line segment point count: {len(points)}")
- pt = points[0]
- # For closed contours, a 'lineTo' is always implied from the last oncurve
- # point to the starting point, thus we can omit it when the last and
- # starting point don't overlap.
- # However, when the last oncurve point is a "line" segment and has same
- # coordinates as the starting point of a closed contour, we need to output
- # the closing 'lineTo' explicitly (regardless of the value of the
- # 'outputImpliedClosingLine' option) in order to disambiguate this case from
- # the implied closing 'lineTo', otherwise the duplicate point would be lost.
- # See https://github.com/googlefonts/fontmake/issues/572.
- if (
- i + 1 != nSegments
- or outputImpliedClosingLine
- or not closed
- or pt == lastPt
- ):
- pen.lineTo(pt)
- lastPt = pt
- elif segmentType == "curve":
- pen.curveTo(*points)
- lastPt = points[-1]
- elif segmentType == "qcurve":
- pen.qCurveTo(*points)
- lastPt = points[-1]
- else:
- raise PenError(f"Illegal segmentType: {segmentType}")
- if closed:
- pen.closePath()
- else:
- pen.endPath()
-
- def addComponent(self, glyphName, transform, identifier=None, **kwargs):
- del identifier # unused
- del kwargs # unused
- self.pen.addComponent(glyphName, transform)
-
-
-class SegmentToPointPen(AbstractPen):
- """
- Adapter class that converts the (Segment)Pen protocol to the
- PointPen protocol.
- """
-
- def __init__(self, pointPen, guessSmooth=True):
- if guessSmooth:
- self.pen = GuessSmoothPointPen(pointPen)
- else:
- self.pen = pointPen
- self.contour = None
-
- def _flushContour(self):
- pen = self.pen
- pen.beginPath()
- for pt, segmentType in self.contour:
- pen.addPoint(pt, segmentType=segmentType)
- pen.endPath()
-
- def moveTo(self, pt):
- self.contour = []
- self.contour.append((pt, "move"))
-
- def lineTo(self, pt):
- if self.contour is None:
- raise PenError("Contour missing required initial moveTo")
- self.contour.append((pt, "line"))
-
- def curveTo(self, *pts):
- if not pts:
- raise TypeError("Must pass in at least one point")
- if self.contour is None:
- raise PenError("Contour missing required initial moveTo")
- for pt in pts[:-1]:
- self.contour.append((pt, None))
- self.contour.append((pts[-1], "curve"))
-
- def qCurveTo(self, *pts):
- if not pts:
- raise TypeError("Must pass in at least one point")
- if pts[-1] is None:
- self.contour = []
- else:
- if self.contour is None:
- raise PenError("Contour missing required initial moveTo")
- for pt in pts[:-1]:
- self.contour.append((pt, None))
- if pts[-1] is not None:
- self.contour.append((pts[-1], "qcurve"))
-
- def closePath(self):
- if self.contour is None:
- raise PenError("Contour missing required initial moveTo")
- if len(self.contour) > 1 and self.contour[0][0] == self.contour[-1][0]:
- self.contour[0] = self.contour[-1]
- del self.contour[-1]
- else:
- # There's an implied line at the end, replace "move" with "line"
- # for the first point
- pt, tp = self.contour[0]
- if tp == "move":
- self.contour[0] = pt, "line"
- self._flushContour()
- self.contour = None
-
- def endPath(self):
- if self.contour is None:
- raise PenError("Contour missing required initial moveTo")
- self._flushContour()
- self.contour = None
-
- def addComponent(self, glyphName, transform):
- if self.contour is not None:
- raise PenError("Components must be added before or after contours")
- self.pen.addComponent(glyphName, transform)
-
-
-class GuessSmoothPointPen(AbstractPointPen):
- """
- Filtering PointPen that tries to determine whether an on-curve point
- should be "smooth", ie. that it's a "tangent" point or a "curve" point.
- """
-
- def __init__(self, outPen, error=0.05):
- self._outPen = outPen
- self._error = error
- self._points = None
-
- def _flushContour(self):
- if self._points is None:
- raise PenError("Path not begun")
- points = self._points
- nPoints = len(points)
- if not nPoints:
- return
- if points[0][1] == "move":
- # Open path.
- indices = range(1, nPoints - 1)
- elif nPoints > 1:
- # Closed path. To avoid having to mod the contour index, we
- # simply abuse Python's negative index feature, and start at -1
- indices = range(-1, nPoints - 1)
- else:
- # closed path containing 1 point (!), ignore.
- indices = []
- for i in indices:
- pt, segmentType, _, name, kwargs = points[i]
- if segmentType is None:
- continue
- prev = i - 1
- next = i + 1
- if points[prev][1] is not None and points[next][1] is not None:
- continue
- # At least one of our neighbors is an off-curve point
- pt = points[i][0]
- prevPt = points[prev][0]
- nextPt = points[next][0]
- if pt != prevPt and pt != nextPt:
- dx1, dy1 = pt[0] - prevPt[0], pt[1] - prevPt[1]
- dx2, dy2 = nextPt[0] - pt[0], nextPt[1] - pt[1]
- a1 = math.atan2(dy1, dx1)
- a2 = math.atan2(dy2, dx2)
- if abs(a1 - a2) < self._error:
- points[i] = pt, segmentType, True, name, kwargs
-
- for pt, segmentType, smooth, name, kwargs in points:
- self._outPen.addPoint(pt, segmentType, smooth, name, **kwargs)
-
- def beginPath(self, identifier=None, **kwargs):
- if self._points is not None:
- raise PenError("Path already begun")
- self._points = []
- if identifier is not None:
- kwargs["identifier"] = identifier
- self._outPen.beginPath(**kwargs)
-
- def endPath(self):
- self._flushContour()
- self._outPen.endPath()
- self._points = None
-
- def addPoint(
- self, pt, segmentType=None, smooth=False, name=None, identifier=None, **kwargs
- ):
- if self._points is None:
- raise PenError("Path not begun")
- if identifier is not None:
- kwargs["identifier"] = identifier
- self._points.append((pt, segmentType, False, name, kwargs))
-
- def addComponent(self, glyphName, transformation, identifier=None, **kwargs):
- if self._points is not None:
- raise PenError("Components must be added before or after contours")
- if identifier is not None:
- kwargs["identifier"] = identifier
- self._outPen.addComponent(glyphName, transformation, **kwargs)
-
- def addVarComponent(
- self, glyphName, transformation, location, identifier=None, **kwargs
- ):
- if self._points is not None:
- raise PenError("VarComponents must be added before or after contours")
- if identifier is not None:
- kwargs["identifier"] = identifier
- self._outPen.addVarComponent(glyphName, transformation, location, **kwargs)
-
-
-class ReverseContourPointPen(AbstractPointPen):
- """
- This is a PointPen that passes outline data to another PointPen, but
- reversing the winding direction of all contours. Components are simply
- passed through unchanged.
-
- Closed contours are reversed in such a way that the first point remains
- the first point.
- """
-
- def __init__(self, outputPointPen):
- self.pen = outputPointPen
- # a place to store the points for the current sub path
- self.currentContour = None
-
- def _flushContour(self):
- pen = self.pen
- contour = self.currentContour
- if not contour:
- pen.beginPath(identifier=self.currentContourIdentifier)
- pen.endPath()
- return
-
- closed = contour[0][1] != "move"
- if not closed:
- lastSegmentType = "move"
- else:
- # Remove the first point and insert it at the end. When
- # the list of points gets reversed, this point will then
- # again be at the start. In other words, the following
- # will hold:
- # for N in range(len(originalContour)):
- # originalContour[N] == reversedContour[-N]
- contour.append(contour.pop(0))
- # Find the first on-curve point.
- firstOnCurve = None
- for i in range(len(contour)):
- if contour[i][1] is not None:
- firstOnCurve = i
- break
- if firstOnCurve is None:
- # There are no on-curve points, be basically have to
- # do nothing but contour.reverse().
- lastSegmentType = None
- else:
- lastSegmentType = contour[firstOnCurve][1]
-
- contour.reverse()
- if not closed:
- # Open paths must start with a move, so we simply dump
- # all off-curve points leading up to the first on-curve.
- while contour[0][1] is None:
- contour.pop(0)
- pen.beginPath(identifier=self.currentContourIdentifier)
- for pt, nextSegmentType, smooth, name, kwargs in contour:
- if nextSegmentType is not None:
- segmentType = lastSegmentType
- lastSegmentType = nextSegmentType
- else:
- segmentType = None
- pen.addPoint(
- pt, segmentType=segmentType, smooth=smooth, name=name, **kwargs
- )
- pen.endPath()
-
- def beginPath(self, identifier=None, **kwargs):
- if self.currentContour is not None:
- raise PenError("Path already begun")
- self.currentContour = []
- self.currentContourIdentifier = identifier
- self.onCurve = []
-
- def endPath(self):
- if self.currentContour is None:
- raise PenError("Path not begun")
- self._flushContour()
- self.currentContour = None
-
- def addPoint(
- self, pt, segmentType=None, smooth=False, name=None, identifier=None, **kwargs
- ):
- if self.currentContour is None:
- raise PenError("Path not begun")
- if identifier is not None:
- kwargs["identifier"] = identifier
- self.currentContour.append((pt, segmentType, smooth, name, kwargs))
-
- def addComponent(self, glyphName, transform, identifier=None, **kwargs):
- if self.currentContour is not None:
- raise PenError("Components must be added before or after contours")
- self.pen.addComponent(glyphName, transform, identifier=identifier, **kwargs)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/qtPen.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/qtPen.py
deleted file mode 100644
index eb13d03..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/qtPen.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from fontTools.pens.basePen import BasePen
-
-
-__all__ = ["QtPen"]
-
-
-class QtPen(BasePen):
- def __init__(self, glyphSet, path=None):
- BasePen.__init__(self, glyphSet)
- if path is None:
- from PyQt5.QtGui import QPainterPath
-
- path = QPainterPath()
- self.path = path
-
- def _moveTo(self, p):
- self.path.moveTo(*p)
-
- def _lineTo(self, p):
- self.path.lineTo(*p)
-
- def _curveToOne(self, p1, p2, p3):
- self.path.cubicTo(*p1, *p2, *p3)
-
- def _qCurveToOne(self, p1, p2):
- self.path.quadTo(*p1, *p2)
-
- def _closePath(self):
- self.path.closeSubpath()
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/qu2cuPen.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/qu2cuPen.py
deleted file mode 100644
index 7e400f9..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/qu2cuPen.py
+++ /dev/null
@@ -1,105 +0,0 @@
-# Copyright 2016 Google Inc. All Rights Reserved.
-# Copyright 2023 Behdad Esfahbod. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from fontTools.qu2cu import quadratic_to_curves
-from fontTools.pens.filterPen import ContourFilterPen
-from fontTools.pens.reverseContourPen import ReverseContourPen
-import math
-
-
-class Qu2CuPen(ContourFilterPen):
- """A filter pen to convert quadratic bezier splines to cubic curves
- using the FontTools SegmentPen protocol.
-
- Args:
-
- other_pen: another SegmentPen used to draw the transformed outline.
- max_err: maximum approximation error in font units. For optimal results,
- if you know the UPEM of the font, we recommend setting this to a
- value equal, or close to UPEM / 1000.
- reverse_direction: flip the contours' direction but keep starting point.
- stats: a dictionary counting the point numbers of cubic segments.
- """
-
- def __init__(
- self,
- other_pen,
- max_err,
- all_cubic=False,
- reverse_direction=False,
- stats=None,
- ):
- if reverse_direction:
- other_pen = ReverseContourPen(other_pen)
- super().__init__(other_pen)
- self.all_cubic = all_cubic
- self.max_err = max_err
- self.stats = stats
-
- def _quadratics_to_curve(self, q):
- curves = quadratic_to_curves(q, self.max_err, all_cubic=self.all_cubic)
- if self.stats is not None:
- for curve in curves:
- n = str(len(curve) - 2)
- self.stats[n] = self.stats.get(n, 0) + 1
- for curve in curves:
- if len(curve) == 4:
- yield ("curveTo", curve[1:])
- else:
- yield ("qCurveTo", curve[1:])
-
- def filterContour(self, contour):
- quadratics = []
- currentPt = None
- newContour = []
- for op, args in contour:
- if op == "qCurveTo" and (
- self.all_cubic or (len(args) > 2 and args[-1] is not None)
- ):
- if args[-1] is None:
- raise NotImplementedError(
- "oncurve-less contours with all_cubic not implemented"
- )
- quadratics.append((currentPt,) + args)
- else:
- if quadratics:
- newContour.extend(self._quadratics_to_curve(quadratics))
- quadratics = []
- newContour.append((op, args))
- currentPt = args[-1] if args else None
- if quadratics:
- newContour.extend(self._quadratics_to_curve(quadratics))
-
- if not self.all_cubic:
- # Add back implicit oncurve points
- contour = newContour
- newContour = []
- for op, args in contour:
- if op == "qCurveTo" and newContour and newContour[-1][0] == "qCurveTo":
- pt0 = newContour[-1][1][-2]
- pt1 = newContour[-1][1][-1]
- pt2 = args[0]
- if (
- pt1 is not None
- and math.isclose(pt2[0] - pt1[0], pt1[0] - pt0[0])
- and math.isclose(pt2[1] - pt1[1], pt1[1] - pt0[1])
- ):
- newArgs = newContour[-1][1][:-1] + args
- newContour[-1] = (op, newArgs)
- continue
-
- newContour.append((op, args))
-
- return newContour
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/quartzPen.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/quartzPen.py
deleted file mode 100644
index 6e1228d..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/quartzPen.py
+++ /dev/null
@@ -1,44 +0,0 @@
-from fontTools.pens.basePen import BasePen
-
-from Quartz.CoreGraphics import CGPathCreateMutable, CGPathMoveToPoint
-from Quartz.CoreGraphics import CGPathAddLineToPoint, CGPathAddCurveToPoint
-from Quartz.CoreGraphics import CGPathAddQuadCurveToPoint, CGPathCloseSubpath
-
-
-__all__ = ["QuartzPen"]
-
-
-class QuartzPen(BasePen):
-
- """A pen that creates a CGPath
-
- Parameters
- - path: an optional CGPath to add to
- - xform: an optional CGAffineTransform to apply to the path
- """
-
- def __init__(self, glyphSet, path=None, xform=None):
- BasePen.__init__(self, glyphSet)
- if path is None:
- path = CGPathCreateMutable()
- self.path = path
- self.xform = xform
-
- def _moveTo(self, pt):
- x, y = pt
- CGPathMoveToPoint(self.path, self.xform, x, y)
-
- def _lineTo(self, pt):
- x, y = pt
- CGPathAddLineToPoint(self.path, self.xform, x, y)
-
- def _curveToOne(self, p1, p2, p3):
- (x1, y1), (x2, y2), (x3, y3) = p1, p2, p3
- CGPathAddCurveToPoint(self.path, self.xform, x1, y1, x2, y2, x3, y3)
-
- def _qCurveToOne(self, p1, p2):
- (x1, y1), (x2, y2) = p1, p2
- CGPathAddQuadCurveToPoint(self.path, self.xform, x1, y1, x2, y2)
-
- def _closePath(self):
- CGPathCloseSubpath(self.path)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/recordingPen.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/recordingPen.py
deleted file mode 100644
index 6c3b661..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/recordingPen.py
+++ /dev/null
@@ -1,179 +0,0 @@
-"""Pen recording operations that can be accessed or replayed."""
-from fontTools.pens.basePen import AbstractPen, DecomposingPen
-from fontTools.pens.pointPen import AbstractPointPen
-
-
-__all__ = [
- "replayRecording",
- "RecordingPen",
- "DecomposingRecordingPen",
- "RecordingPointPen",
-]
-
-
-def replayRecording(recording, pen):
- """Replay a recording, as produced by RecordingPen or DecomposingRecordingPen,
- to a pen.
-
- Note that recording does not have to be produced by those pens.
- It can be any iterable of tuples of method name and tuple-of-arguments.
- Likewise, pen can be any objects receiving those method calls.
- """
- for operator, operands in recording:
- getattr(pen, operator)(*operands)
-
-
-class RecordingPen(AbstractPen):
- """Pen recording operations that can be accessed or replayed.
-
- The recording can be accessed as pen.value; or replayed using
- pen.replay(otherPen).
-
- :Example:
-
- from fontTools.ttLib import TTFont
- from fontTools.pens.recordingPen import RecordingPen
-
- glyph_name = 'dollar'
- font_path = 'MyFont.otf'
-
- font = TTFont(font_path)
- glyphset = font.getGlyphSet()
- glyph = glyphset[glyph_name]
-
- pen = RecordingPen()
- glyph.draw(pen)
- print(pen.value)
- """
-
- def __init__(self):
- self.value = []
-
- def moveTo(self, p0):
- self.value.append(("moveTo", (p0,)))
-
- def lineTo(self, p1):
- self.value.append(("lineTo", (p1,)))
-
- def qCurveTo(self, *points):
- self.value.append(("qCurveTo", points))
-
- def curveTo(self, *points):
- self.value.append(("curveTo", points))
-
- def closePath(self):
- self.value.append(("closePath", ()))
-
- def endPath(self):
- self.value.append(("endPath", ()))
-
- def addComponent(self, glyphName, transformation):
- self.value.append(("addComponent", (glyphName, transformation)))
-
- def addVarComponent(self, glyphName, transformation, location):
- self.value.append(("addVarComponent", (glyphName, transformation, location)))
-
- def replay(self, pen):
- replayRecording(self.value, pen)
-
-
-class DecomposingRecordingPen(DecomposingPen, RecordingPen):
- """Same as RecordingPen, except that it doesn't keep components
- as references, but draws them decomposed as regular contours.
-
- The constructor takes a single 'glyphSet' positional argument,
- a dictionary of glyph objects (i.e. with a 'draw' method) keyed
- by thir name::
-
- >>> class SimpleGlyph(object):
- ... def draw(self, pen):
- ... pen.moveTo((0, 0))
- ... pen.curveTo((1, 1), (2, 2), (3, 3))
- ... pen.closePath()
- >>> class CompositeGlyph(object):
- ... def draw(self, pen):
- ... pen.addComponent('a', (1, 0, 0, 1, -1, 1))
- >>> glyphSet = {'a': SimpleGlyph(), 'b': CompositeGlyph()}
- >>> for name, glyph in sorted(glyphSet.items()):
- ... pen = DecomposingRecordingPen(glyphSet)
- ... glyph.draw(pen)
- ... print("{}: {}".format(name, pen.value))
- a: [('moveTo', ((0, 0),)), ('curveTo', ((1, 1), (2, 2), (3, 3))), ('closePath', ())]
- b: [('moveTo', ((-1, 1),)), ('curveTo', ((0, 2), (1, 3), (2, 4))), ('closePath', ())]
- """
-
- # raises KeyError if base glyph is not found in glyphSet
- skipMissingComponents = False
-
-
-class RecordingPointPen(AbstractPointPen):
- """PointPen recording operations that can be accessed or replayed.
-
- The recording can be accessed as pen.value; or replayed using
- pointPen.replay(otherPointPen).
-
- :Example:
-
- from defcon import Font
- from fontTools.pens.recordingPen import RecordingPointPen
-
- glyph_name = 'a'
- font_path = 'MyFont.ufo'
-
- font = Font(font_path)
- glyph = font[glyph_name]
-
- pen = RecordingPointPen()
- glyph.drawPoints(pen)
- print(pen.value)
-
- new_glyph = font.newGlyph('b')
- pen.replay(new_glyph.getPointPen())
- """
-
- def __init__(self):
- self.value = []
-
- def beginPath(self, identifier=None, **kwargs):
- if identifier is not None:
- kwargs["identifier"] = identifier
- self.value.append(("beginPath", (), kwargs))
-
- def endPath(self):
- self.value.append(("endPath", (), {}))
-
- def addPoint(
- self, pt, segmentType=None, smooth=False, name=None, identifier=None, **kwargs
- ):
- if identifier is not None:
- kwargs["identifier"] = identifier
- self.value.append(("addPoint", (pt, segmentType, smooth, name), kwargs))
-
- def addComponent(self, baseGlyphName, transformation, identifier=None, **kwargs):
- if identifier is not None:
- kwargs["identifier"] = identifier
- self.value.append(("addComponent", (baseGlyphName, transformation), kwargs))
-
- def addVarComponent(
- self, baseGlyphName, transformation, location, identifier=None, **kwargs
- ):
- if identifier is not None:
- kwargs["identifier"] = identifier
- self.value.append(
- ("addVarComponent", (baseGlyphName, transformation, location), kwargs)
- )
-
- def replay(self, pointPen):
- for operator, args, kwargs in self.value:
- getattr(pointPen, operator)(*args, **kwargs)
-
-
-if __name__ == "__main__":
- pen = RecordingPen()
- pen.moveTo((0, 0))
- pen.lineTo((0, 100))
- pen.curveTo((50, 75), (60, 50), (50, 25))
- pen.closePath()
- from pprint import pprint
-
- pprint(pen.value)
diff --git a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/reportLabPen.py b/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/reportLabPen.py
deleted file mode 100644
index 2cb89c8..0000000
--- a/dotfiles/inkscape/extensions/org.inkscape.extension.30306/scientific_inkscape/inkex1_3_0/site-packages/fontTools/pens/reportLabPen.py
+++ /dev/null
@@ -1,80 +0,0 @@
-from fontTools.pens.basePen import BasePen
-from reportlab.graphics.shapes import Path
-
-
-__all__ = ["ReportLabPen"]
-
-
-class ReportLabPen(BasePen):
-
- """A pen for drawing onto a ``reportlab.graphics.shapes.Path`` object."""
-
- def __init__(self, glyphSet, path=None):
- BasePen.__init__(self, glyphSet)
- if path is None:
- path = Path()
- self.path = path
-
- def _moveTo(self, p):
- (x, y) = p
- self.path.moveTo(x, y)
-
- def _lineTo(self, p):
- (x, y) = p
- self.path.lineTo(x, y)
-
- def _curveToOne(self, p1, p2, p3):
- (x1, y1) = p1
- (x2, y2) = p2
- (x3, y3) = p3
- self.path.curveTo(x1, y1, x2, y2, x3, y3)
-
- def _closePath(self):
- self.path.closePath()
-
-
-if __name__ == "__main__":
- import sys
-
- if len(sys.argv) < 3:
- print(
- "Usage: reportLabPen.py [