mirror of
https://github.com/freedomofpress/dangerzone.git
synced 2025-04-30 10:42:37 +02:00
Refactor static methods as functions in util module
This commit is contained in:
parent
42a1f01e6f
commit
17ff3a8795
11 changed files with 311 additions and 341 deletions
|
@ -1,11 +1,6 @@
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import appdirs
|
|
||||||
|
|
||||||
CONTAINER_NAME = "dangerzone.rocks/dangerzone"
|
|
||||||
APPDATA_PATH = appdirs.user_config_dir("dangerzone")
|
|
||||||
|
|
||||||
if "DANGERZONE_MODE" in os.environ:
|
if "DANGERZONE_MODE" in os.environ:
|
||||||
mode = os.environ["DANGERZONE_MODE"]
|
mode = os.environ["DANGERZONE_MODE"]
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -7,6 +7,7 @@ from colorama import Fore, Style
|
||||||
from .global_common import GlobalCommon
|
from .global_common import GlobalCommon
|
||||||
from .common import Common
|
from .common import Common
|
||||||
from .container import convert
|
from .container import convert
|
||||||
|
import dangerzone.util as dzutil
|
||||||
|
|
||||||
|
|
||||||
def print_header(s):
|
def print_header(s):
|
||||||
|
@ -73,14 +74,14 @@ def cli_main(output_filename, ocr_lang, filename):
|
||||||
# Validate OCR language
|
# Validate OCR language
|
||||||
if ocr_lang:
|
if ocr_lang:
|
||||||
valid = False
|
valid = False
|
||||||
for lang in global_common.ocr_languages:
|
for lang in dzutil.OCR_LANGUAGES:
|
||||||
if global_common.ocr_languages[lang] == ocr_lang:
|
if dzutil.OCR_LANGUAGES[lang] == ocr_lang:
|
||||||
valid = True
|
valid = True
|
||||||
break
|
break
|
||||||
if not valid:
|
if not valid:
|
||||||
click.echo("Invalid OCR language code. Valid language codes:")
|
click.echo("Invalid OCR language code. Valid language codes:")
|
||||||
for lang in global_common.ocr_languages:
|
for lang in dzutil.OCR_LANGUAGES:
|
||||||
click.echo(f"{global_common.ocr_languages[lang]}: {lang}")
|
click.echo(f"{dzutil.OCR_LANGUAGES[lang]}: {lang}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Ensure container is installed
|
# Ensure container is installed
|
||||||
|
|
|
@ -1,10 +1,3 @@
|
||||||
import os
|
|
||||||
import stat
|
|
||||||
import platform
|
|
||||||
import tempfile
|
|
||||||
import appdirs
|
|
||||||
|
|
||||||
|
|
||||||
class Common(object):
|
class Common(object):
|
||||||
"""
|
"""
|
||||||
The Common class is a singleton of shared functionality throughout an open dangerzone window
|
The Common class is a singleton of shared functionality throughout an open dangerzone window
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import platform
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import pipes
|
import pipes
|
||||||
import shutil
|
import shutil
|
||||||
|
@ -6,23 +5,7 @@ import os
|
||||||
import tempfile
|
import tempfile
|
||||||
import appdirs
|
import appdirs
|
||||||
|
|
||||||
# What container tech is used for this platform?
|
import dangerzone.util as dzutil
|
||||||
if platform.system() == "Linux":
|
|
||||||
container_tech = "podman"
|
|
||||||
else:
|
|
||||||
# Windows, Darwin, and unknown use docker for now, dangerzone-vm eventually
|
|
||||||
container_tech = "docker"
|
|
||||||
|
|
||||||
# Define startupinfo for subprocesses
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
startupinfo = subprocess.STARTUPINFO()
|
|
||||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
|
||||||
else:
|
|
||||||
startupinfo = None
|
|
||||||
|
|
||||||
|
|
||||||
# Name of the dangerzone container
|
|
||||||
container_name = "dangerzone.rocks/dangerzone"
|
|
||||||
|
|
||||||
|
|
||||||
def exec(args, stdout_callback=None):
|
def exec(args, stdout_callback=None):
|
||||||
|
@ -30,13 +13,13 @@ def exec(args, stdout_callback=None):
|
||||||
print("> " + args_str)
|
print("> " + args_str)
|
||||||
|
|
||||||
with subprocess.Popen(
|
with subprocess.Popen(
|
||||||
args,
|
args,
|
||||||
stdin=None,
|
stdin=None,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.STDOUT,
|
stderr=subprocess.STDOUT,
|
||||||
bufsize=1,
|
bufsize=1,
|
||||||
universal_newlines=True,
|
universal_newlines=True,
|
||||||
startupinfo=startupinfo,
|
startupinfo=dzutil.get_subprocess_startupinfo(),
|
||||||
) as p:
|
) as p:
|
||||||
if stdout_callback:
|
if stdout_callback:
|
||||||
for line in p.stdout:
|
for line in p.stdout:
|
||||||
|
@ -47,12 +30,7 @@ def exec(args, stdout_callback=None):
|
||||||
|
|
||||||
|
|
||||||
def exec_container(args, stdout_callback=None):
|
def exec_container(args, stdout_callback=None):
|
||||||
if container_tech == "podman":
|
args = [dzutil.CONTAINER_RUNTIME] + args
|
||||||
container_runtime = shutil.which("podman")
|
|
||||||
else:
|
|
||||||
container_runtime = shutil.which("docker")
|
|
||||||
|
|
||||||
args = [container_runtime] + args
|
|
||||||
return exec(args, stdout_callback)
|
return exec(args, stdout_callback)
|
||||||
|
|
||||||
|
|
||||||
|
@ -80,18 +58,18 @@ def convert(input_filename, output_filename, ocr_lang, stdout_callback):
|
||||||
|
|
||||||
# Convert document to pixels
|
# Convert document to pixels
|
||||||
args = (
|
args = (
|
||||||
["run", "--network", "none"]
|
["run", "--network", "none"]
|
||||||
+ platform_args
|
+ platform_args
|
||||||
+ [
|
+ [
|
||||||
"-v",
|
"-v",
|
||||||
f"{input_filename}:/tmp/input_file",
|
f"{input_filename}:/tmp/input_file",
|
||||||
"-v",
|
"-v",
|
||||||
f"{pixel_dir}:/dangerzone",
|
f"{pixel_dir}:/dangerzone",
|
||||||
container_name,
|
dzutil.CONTAINER_NAME,
|
||||||
"/usr/bin/python3",
|
"/usr/bin/python3",
|
||||||
"/usr/local/bin/dangerzone.py",
|
"/usr/local/bin/dangerzone.py",
|
||||||
"document-to-pixels",
|
"document-to-pixels",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
ret = exec_container(args, stdout_callback)
|
ret = exec_container(args, stdout_callback)
|
||||||
if ret != 0:
|
if ret != 0:
|
||||||
|
@ -101,22 +79,22 @@ def convert(input_filename, output_filename, ocr_lang, stdout_callback):
|
||||||
|
|
||||||
# Convert pixels to safe PDF
|
# Convert pixels to safe PDF
|
||||||
args = (
|
args = (
|
||||||
["run", "--network", "none"]
|
["run", "--network", "none"]
|
||||||
+ platform_args
|
+ platform_args
|
||||||
+ [
|
+ [
|
||||||
"-v",
|
"-v",
|
||||||
f"{pixel_dir}:/dangerzone",
|
f"{pixel_dir}:/dangerzone",
|
||||||
"-v",
|
"-v",
|
||||||
f"{safe_dir}:/safezone",
|
f"{safe_dir}:/safezone",
|
||||||
"-e",
|
"-e",
|
||||||
f"OCR={ocr}",
|
f"OCR={ocr}",
|
||||||
"-e",
|
"-e",
|
||||||
f"OCR_LANGUAGE={ocr_lang}",
|
f"OCR_LANGUAGE={ocr_lang}",
|
||||||
container_name,
|
dzutil.CONTAINER_NAME,
|
||||||
"/usr/bin/python3",
|
"/usr/bin/python3",
|
||||||
"/usr/local/bin/dangerzone.py",
|
"/usr/local/bin/dangerzone.py",
|
||||||
"pixels-to-pdf",
|
"pixels-to-pdf",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
ret = exec_container(args, stdout_callback)
|
ret = exec_container(args, stdout_callback)
|
||||||
if ret != 0:
|
if ret != 0:
|
||||||
|
@ -139,7 +117,6 @@ def convert(input_filename, output_filename, ocr_lang, stdout_callback):
|
||||||
|
|
||||||
return success
|
return success
|
||||||
|
|
||||||
|
|
||||||
# From global_common:
|
# From global_common:
|
||||||
|
|
||||||
# def validate_convert_to_pixel_output(self, common, output):
|
# def validate_convert_to_pixel_output(self, common, output):
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import inspect
|
|
||||||
import platform
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import shutil
|
|
||||||
import gzip
|
import gzip
|
||||||
import colorama
|
import colorama
|
||||||
from colorama import Fore, Back, Style
|
from colorama import Fore, Back, Style
|
||||||
|
|
||||||
from . import CONTAINER_NAME
|
import dangerzone
|
||||||
from .settings import Settings
|
from dangerzone.settings import Settings
|
||||||
|
import dangerzone.util as dzutil
|
||||||
|
|
||||||
|
|
||||||
class GlobalCommon(object):
|
class GlobalCommon(object):
|
||||||
|
@ -18,182 +14,11 @@ class GlobalCommon(object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# Version
|
self.version = dzutil.VERSION
|
||||||
try:
|
|
||||||
with open(self.get_resource_path("version.txt")) as f:
|
|
||||||
self.version = f.read().strip()
|
|
||||||
except FileNotFoundError:
|
|
||||||
# In dev mode, in Windows, get_resource_path doesn't work properly for the container, but luckily
|
|
||||||
# it doesn't need to know the version
|
|
||||||
self.version = "unknown"
|
|
||||||
|
|
||||||
# Initialize terminal colors
|
# Initialize terminal colors
|
||||||
colorama.init(autoreset=True)
|
colorama.init(autoreset=True)
|
||||||
|
|
||||||
# Languages supported by tesseract
|
|
||||||
self.ocr_languages = {
|
|
||||||
"Afrikaans": "ar",
|
|
||||||
"Albanian": "sqi",
|
|
||||||
"Amharic": "amh",
|
|
||||||
"Arabic": "ara",
|
|
||||||
"Arabic script": "Arabic",
|
|
||||||
"Armenian": "hye",
|
|
||||||
"Armenian script": "Armenian",
|
|
||||||
"Assamese": "asm",
|
|
||||||
"Azerbaijani": "aze",
|
|
||||||
"Azerbaijani (Cyrillic)": "aze_cyrl",
|
|
||||||
"Basque": "eus",
|
|
||||||
"Belarusian": "bel",
|
|
||||||
"Bengali": "ben",
|
|
||||||
"Bengali script": "Bengali",
|
|
||||||
"Bosnian": "bos",
|
|
||||||
"Breton": "bre",
|
|
||||||
"Bulgarian": "bul",
|
|
||||||
"Burmese": "mya",
|
|
||||||
"Canadian Aboriginal script": "Canadian_Aboriginal",
|
|
||||||
"Catalan": "cat",
|
|
||||||
"Cebuano": "ceb",
|
|
||||||
"Cherokee": "chr",
|
|
||||||
"Cherokee script": "Cherokee",
|
|
||||||
"Chinese - Simplified": "chi_sim",
|
|
||||||
"Chinese - Simplified (vertical)": "chi_sim_vert",
|
|
||||||
"Chinese - Traditional": "chi_tra",
|
|
||||||
"Chinese - Traditional (vertical)": "chi_tra_vert",
|
|
||||||
"Corsican": "cos",
|
|
||||||
"Croatian": "hrv",
|
|
||||||
"Cyrillic script": "Cyrillic",
|
|
||||||
"Czech": "ces",
|
|
||||||
"Danish": "dan",
|
|
||||||
"Devanagari script": "Devanagari",
|
|
||||||
"Divehi": "div",
|
|
||||||
"Dutch": "nld",
|
|
||||||
"Dzongkha": "dzo",
|
|
||||||
"English": "eng",
|
|
||||||
"English, Middle (1100-1500)": "enm",
|
|
||||||
"Esperanto": "epo",
|
|
||||||
"Estonian": "est",
|
|
||||||
"Ethiopic script": "Ethiopic",
|
|
||||||
"Faroese": "fao",
|
|
||||||
"Filipino": "fil",
|
|
||||||
"Finnish": "fin",
|
|
||||||
"Fraktur script": "Fraktur",
|
|
||||||
"Frankish": "frk",
|
|
||||||
"French": "fra",
|
|
||||||
"French, Middle (ca.1400-1600)": "frm",
|
|
||||||
"Frisian (Western)": "fry",
|
|
||||||
"Gaelic (Scots)": "gla",
|
|
||||||
"Galician": "glg",
|
|
||||||
"Georgian": "kat",
|
|
||||||
"Georgian script": "Georgian",
|
|
||||||
"German": "deu",
|
|
||||||
"Greek": "ell",
|
|
||||||
"Greek script": "Greek",
|
|
||||||
"Gujarati": "guj",
|
|
||||||
"Gujarati script": "Gujarati",
|
|
||||||
"Gurmukhi script": "Gurmukhi",
|
|
||||||
"Hangul script": "Hangul",
|
|
||||||
"Hangul (vertical) script": "Hangul_vert",
|
|
||||||
"Han - Simplified script": "HanS",
|
|
||||||
"Han - Simplified (vertical) script": "HanS_vert",
|
|
||||||
"Han - Traditional script": "HanT",
|
|
||||||
"Han - Traditional (vertical) script": "HanT_vert",
|
|
||||||
"Hatian": "hat",
|
|
||||||
"Hebrew": "heb",
|
|
||||||
"Hebrew script": "Hebrew",
|
|
||||||
"Hindi": "hin",
|
|
||||||
"Hungarian": "hun",
|
|
||||||
"Icelandic": "isl",
|
|
||||||
"Indonesian": "ind",
|
|
||||||
"Inuktitut": "iku",
|
|
||||||
"Irish": "gle",
|
|
||||||
"Italian": "ita",
|
|
||||||
"Italian - Old": "ita_old",
|
|
||||||
"Japanese": "jpn",
|
|
||||||
"Japanese script": "Japanese",
|
|
||||||
"Japanese (vertical)": "jpn_vert",
|
|
||||||
"Japanese (vertical) script": "Japanese_vert",
|
|
||||||
"Javanese": "jav",
|
|
||||||
"Kannada": "kan",
|
|
||||||
"Kannada script": "Kannada",
|
|
||||||
"Kazakh": "kaz",
|
|
||||||
"Khmer": "khm",
|
|
||||||
"Khmer script": "Khmer",
|
|
||||||
"Korean": "kor",
|
|
||||||
"Korean (vertical)": "kor_vert",
|
|
||||||
"Kurdish (Arabic)": "kur_ara",
|
|
||||||
"Kyrgyz": "kir",
|
|
||||||
"Lao": "lao",
|
|
||||||
"Lao script": "Lao",
|
|
||||||
"Latin": "lat",
|
|
||||||
"Latin script": "Latin",
|
|
||||||
"Latvian": "lav",
|
|
||||||
"Lithuanian": "lit",
|
|
||||||
"Luxembourgish": "ltz",
|
|
||||||
"Macedonian": "mkd",
|
|
||||||
"Malayalam": "mal",
|
|
||||||
"Malayalam script": "Malayalam",
|
|
||||||
"Malay": "msa",
|
|
||||||
"Maltese": "mlt",
|
|
||||||
"Maori": "mri",
|
|
||||||
"Marathi": "mar",
|
|
||||||
"Mongolian": "mon",
|
|
||||||
"Myanmar script": "Myanmar",
|
|
||||||
"Nepali": "nep",
|
|
||||||
"Norwegian": "nor",
|
|
||||||
"Occitan (post 1500)": "oci",
|
|
||||||
"Old Georgian": "kat_old",
|
|
||||||
"Oriya (Odia) script": "Oriya",
|
|
||||||
"Oriya": "ori",
|
|
||||||
"Pashto": "pus",
|
|
||||||
"Persian": "fas",
|
|
||||||
"Polish": "pol",
|
|
||||||
"Portuguese": "por",
|
|
||||||
"Punjabi": "pan",
|
|
||||||
"Quechua": "que",
|
|
||||||
"Romanian": "ron",
|
|
||||||
"Russian": "rus",
|
|
||||||
"Sanskrit": "san",
|
|
||||||
"script and orientation": "osd",
|
|
||||||
"Serbian (Latin)": "srp_latn",
|
|
||||||
"Serbian": "srp",
|
|
||||||
"Sindhi": "snd",
|
|
||||||
"Sinhala script": "Sinhala",
|
|
||||||
"Sinhala": "sin",
|
|
||||||
"Slovakian": "slk",
|
|
||||||
"Slovenian": "slv",
|
|
||||||
"Spanish, Castilian - Old": "spa_old",
|
|
||||||
"Spanish": "spa",
|
|
||||||
"Sundanese": "sun",
|
|
||||||
"Swahili": "swa",
|
|
||||||
"Swedish": "swe",
|
|
||||||
"Syriac script": "Syriac",
|
|
||||||
"Syriac": "syr",
|
|
||||||
"Tajik": "tgk",
|
|
||||||
"Tamil script": "Tamil",
|
|
||||||
"Tamil": "tam",
|
|
||||||
"Tatar": "tat",
|
|
||||||
"Telugu script": "Telugu",
|
|
||||||
"Telugu": "tel",
|
|
||||||
"Thaana script": "Thaana",
|
|
||||||
"Thai script": "Thai",
|
|
||||||
"Thai": "tha",
|
|
||||||
"Tibetan script": "Tibetan",
|
|
||||||
"Tibetan Standard": "bod",
|
|
||||||
"Tigrinya": "tir",
|
|
||||||
"Tonga": "ton",
|
|
||||||
"Turkish": "tur",
|
|
||||||
"Ukrainian": "ukr",
|
|
||||||
"Urdu": "urd",
|
|
||||||
"Uyghur": "uig",
|
|
||||||
"Uzbek (Cyrillic)": "uzb_cyrl",
|
|
||||||
"Uzbek": "uzb",
|
|
||||||
"Vietnamese script": "Vietnamese",
|
|
||||||
"Vietnamese": "vie",
|
|
||||||
"Welsh": "cym",
|
|
||||||
"Yiddish": "yid",
|
|
||||||
"Yoruba": "yor",
|
|
||||||
}
|
|
||||||
|
|
||||||
# Load settings
|
# Load settings
|
||||||
self.settings = Settings()
|
self.settings = Settings()
|
||||||
|
|
||||||
|
@ -339,9 +164,9 @@ class GlobalCommon(object):
|
||||||
+ "│"
|
+ "│"
|
||||||
)
|
)
|
||||||
print(Back.BLACK + Fore.YELLOW + Style.DIM + "│ │")
|
print(Back.BLACK + Fore.YELLOW + Style.DIM + "│ │")
|
||||||
left_spaces = (15 - len(self.version) - 1) // 2
|
left_spaces = (15 - len(dzutil.VERSION) - 1) // 2
|
||||||
right_spaces = left_spaces
|
right_spaces = left_spaces
|
||||||
if left_spaces + len(self.version) + 1 + right_spaces < 15:
|
if left_spaces + len(dzutil.VERSION) + 1 + right_spaces < 15:
|
||||||
right_spaces += 1
|
right_spaces += 1
|
||||||
print(
|
print(
|
||||||
Back.BLACK
|
Back.BLACK
|
||||||
|
@ -352,7 +177,7 @@ class GlobalCommon(object):
|
||||||
+ Back.BLACK
|
+ Back.BLACK
|
||||||
+ Fore.LIGHTWHITE_EX
|
+ Fore.LIGHTWHITE_EX
|
||||||
+ Style.BRIGHT
|
+ Style.BRIGHT
|
||||||
+ f"{' '*left_spaces}Dangerzone v{self.version}{' '*right_spaces}"
|
+ f"{' '*left_spaces}Dangerzone v{dzutil.VERSION}{' '*right_spaces}"
|
||||||
+ Fore.YELLOW
|
+ Fore.YELLOW
|
||||||
+ Style.DIM
|
+ Style.DIM
|
||||||
+ "│"
|
+ "│"
|
||||||
|
@ -372,48 +197,6 @@ class GlobalCommon(object):
|
||||||
)
|
)
|
||||||
print(Back.BLACK + Fore.YELLOW + Style.DIM + "╰──────────────────────────╯")
|
print(Back.BLACK + Fore.YELLOW + Style.DIM + "╰──────────────────────────╯")
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_container_runtime():
|
|
||||||
if platform.system() == "Linux":
|
|
||||||
return shutil.which("podman")
|
|
||||||
else:
|
|
||||||
return shutil.which("docker")
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_resource_path(filename):
|
|
||||||
if getattr(sys, "dangerzone_dev", False):
|
|
||||||
# Look for resources directory relative to python file
|
|
||||||
prefix = os.path.join(
|
|
||||||
os.path.dirname(
|
|
||||||
os.path.dirname(
|
|
||||||
os.path.abspath(inspect.getfile(inspect.currentframe()))
|
|
||||||
)
|
|
||||||
),
|
|
||||||
"share",
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
if platform.system() == "Darwin":
|
|
||||||
prefix = os.path.join(
|
|
||||||
os.path.dirname(os.path.dirname(sys.executable)), "Resources/share"
|
|
||||||
)
|
|
||||||
elif platform.system() == "Linux":
|
|
||||||
prefix = os.path.join(sys.prefix, "share", "dangerzone")
|
|
||||||
else:
|
|
||||||
# Windows
|
|
||||||
prefix = os.path.join(os.path.dirname(sys.executable), "share")
|
|
||||||
|
|
||||||
resource_path = os.path.join(prefix, filename)
|
|
||||||
return resource_path
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_subprocess_startupinfo():
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
startupinfo = subprocess.STARTUPINFO()
|
|
||||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
|
||||||
return startupinfo
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def install_container():
|
def install_container():
|
||||||
"""
|
"""
|
||||||
|
@ -426,13 +209,13 @@ class GlobalCommon(object):
|
||||||
print("Installing Dangerzone container image...")
|
print("Installing Dangerzone container image...")
|
||||||
|
|
||||||
p = subprocess.Popen(
|
p = subprocess.Popen(
|
||||||
[GlobalCommon.get_container_runtime(), "load"],
|
[dzutil.CONTAINER_RUNTIME, "load"],
|
||||||
stdin=subprocess.PIPE,
|
stdin=subprocess.PIPE,
|
||||||
startupinfo=GlobalCommon.get_subprocess_startupinfo(),
|
startupinfo=dzutil.get_subprocess_startupinfo(),
|
||||||
)
|
)
|
||||||
|
|
||||||
chunk_size = 10240
|
chunk_size = 10240
|
||||||
compressed_container_path = GlobalCommon.get_resource_path("container.tar.gz")
|
compressed_container_path = dzutil.get_resource_path("container.tar.gz")
|
||||||
with gzip.open(compressed_container_path) as f:
|
with gzip.open(compressed_container_path) as f:
|
||||||
while True:
|
while True:
|
||||||
chunk = f.read(chunk_size)
|
chunk = f.read(chunk_size)
|
||||||
|
@ -455,22 +238,22 @@ class GlobalCommon(object):
|
||||||
See if the podman container is installed. Linux only.
|
See if the podman container is installed. Linux only.
|
||||||
"""
|
"""
|
||||||
# Get the image id
|
# Get the image id
|
||||||
with open(GlobalCommon.get_resource_path("image-id.txt")) as f:
|
with open(dzutil.get_resource_path("image-id.txt")) as f:
|
||||||
expected_image_id = f.read().strip()
|
expected_image_id = f.read().strip()
|
||||||
|
|
||||||
# See if this image is already installed
|
# See if this image is already installed
|
||||||
installed = False
|
installed = False
|
||||||
found_image_id = subprocess.check_output(
|
found_image_id = subprocess.check_output(
|
||||||
[
|
[
|
||||||
GlobalCommon.get_container_runtime(),
|
dzutil.CONTAINER_RUNTIME,
|
||||||
"image",
|
"image",
|
||||||
"list",
|
"list",
|
||||||
"--format",
|
"--format",
|
||||||
"{{.ID}}",
|
"{{.ID}}",
|
||||||
CONTAINER_NAME,
|
dangerzone.util.CONTAINER_NAME,
|
||||||
],
|
],
|
||||||
text=True,
|
text=True,
|
||||||
startupinfo=GlobalCommon.get_subprocess_startupinfo(),
|
startupinfo=dzutil.get_subprocess_startupinfo(),
|
||||||
)
|
)
|
||||||
found_image_id = found_image_id.strip()
|
found_image_id = found_image_id.strip()
|
||||||
|
|
||||||
|
@ -483,8 +266,8 @@ class GlobalCommon(object):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subprocess.check_output(
|
subprocess.check_output(
|
||||||
[GlobalCommon.get_container_runtime(), "rmi", "--force", found_image_id],
|
[dzutil.CONTAINER_RUNTIME, "rmi", "--force", found_image_id],
|
||||||
startupinfo=GlobalCommon.get_subprocess_startupinfo(),
|
startupinfo=dzutil.get_subprocess_startupinfo(),
|
||||||
)
|
)
|
||||||
except:
|
except:
|
||||||
print("Couldn't delete old container image, so leaving it there")
|
print("Couldn't delete old container image, so leaving it there")
|
||||||
|
|
|
@ -53,7 +53,7 @@ def gui_main(filename):
|
||||||
|
|
||||||
# Create the system tray
|
# Create the system tray
|
||||||
# noinspection PyUnusedLocal
|
# noinspection PyUnusedLocal
|
||||||
systray = SysTray(global_common, gui_common, app)
|
systray = SysTray(gui_common, app)
|
||||||
|
|
||||||
closed_windows = {}
|
closed_windows = {}
|
||||||
windows = {}
|
windows = {}
|
||||||
|
|
|
@ -4,17 +4,14 @@ import subprocess
|
||||||
import shlex
|
import shlex
|
||||||
import pipes
|
import pipes
|
||||||
from PySide6 import QtCore, QtGui, QtWidgets
|
from PySide6 import QtCore, QtGui, QtWidgets
|
||||||
|
from PySide6.QtGui import QIcon
|
||||||
from colorama import Fore
|
from colorama import Fore
|
||||||
|
|
||||||
from . import Application
|
from . import Application
|
||||||
from ..global_common import GlobalCommon
|
from ..global_common import GlobalCommon
|
||||||
|
import dangerzone.util as dzutil
|
||||||
|
|
||||||
if platform.system() == "Darwin":
|
if platform.system() == "Linux":
|
||||||
import plistlib
|
|
||||||
|
|
||||||
elif platform.system() == "Linux":
|
|
||||||
import grp
|
|
||||||
import getpass
|
|
||||||
from xdg.DesktopEntry import DesktopEntry # type: ignore
|
from xdg.DesktopEntry import DesktopEntry # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,14 +36,6 @@ class GuiCommon(object):
|
||||||
# Are we done waiting (for Docker Desktop to be installed, or for container to install)
|
# Are we done waiting (for Docker Desktop to be installed, or for container to install)
|
||||||
self.is_waiting_finished = False
|
self.is_waiting_finished = False
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_window_icon():
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
path = GlobalCommon.get_resource_path("dangerzone.ico")
|
|
||||||
else:
|
|
||||||
path = GlobalCommon.get_resource_path("icon.png")
|
|
||||||
return QtGui.QIcon(path)
|
|
||||||
|
|
||||||
def open_pdf_viewer(self, filename: str):
|
def open_pdf_viewer(self, filename: str):
|
||||||
if platform.system() == "Darwin":
|
if platform.system() == "Darwin":
|
||||||
# Open in Preview
|
# Open in Preview
|
||||||
|
@ -118,7 +107,7 @@ class Alert(QtWidgets.QDialog):
|
||||||
):
|
):
|
||||||
super(Alert, self).__init__()
|
super(Alert, self).__init__()
|
||||||
self.setWindowTitle("dangerzone")
|
self.setWindowTitle("dangerzone")
|
||||||
self.setWindowIcon(GuiCommon.get_window_icon())
|
self.setWindowIcon(QIcon(dzutil.WINDOW_ICON_PATH))
|
||||||
self.setModal(True)
|
self.setModal(True)
|
||||||
|
|
||||||
flags = ( # TODO Mypy: unsupported left operand type for | ("WindowType")
|
flags = ( # TODO Mypy: unsupported left operand type for | ("WindowType")
|
||||||
|
@ -133,7 +122,7 @@ class Alert(QtWidgets.QDialog):
|
||||||
logo = QtWidgets.QLabel()
|
logo = QtWidgets.QLabel()
|
||||||
logo.setPixmap(
|
logo.setPixmap(
|
||||||
QtGui.QPixmap.fromImage(
|
QtGui.QPixmap.fromImage(
|
||||||
QtGui.QImage(GlobalCommon.get_resource_path("icon.png"))
|
QtGui.QImage(dzutil.get_resource_path("icon.png"))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,10 @@ import subprocess
|
||||||
import json
|
import json
|
||||||
import shutil
|
import shutil
|
||||||
from PySide6 import QtCore, QtGui, QtWidgets
|
from PySide6 import QtCore, QtGui, QtWidgets
|
||||||
|
from PySide6.QtGui import QIcon
|
||||||
from colorama import Style, Fore
|
from colorama import Style, Fore
|
||||||
|
|
||||||
|
import dangerzone.util as dzutil
|
||||||
from . import GuiCommon
|
from . import GuiCommon
|
||||||
from ..common import Common
|
from ..common import Common
|
||||||
from ..container import convert
|
from ..container import convert
|
||||||
|
@ -24,7 +26,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||||
self.common = Common()
|
self.common = Common()
|
||||||
|
|
||||||
self.setWindowTitle("Dangerzone")
|
self.setWindowTitle("Dangerzone")
|
||||||
self.setWindowIcon(self.gui_common.get_window_icon())
|
self.setWindowIcon(QIcon(dzutil.WINDOW_ICON_PATH))
|
||||||
|
|
||||||
self.setMinimumWidth(600)
|
self.setMinimumWidth(600)
|
||||||
self.setMinimumHeight(400)
|
self.setMinimumHeight(400)
|
||||||
|
@ -33,7 +35,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||||
logo = QtWidgets.QLabel()
|
logo = QtWidgets.QLabel()
|
||||||
logo.setPixmap(
|
logo.setPixmap(
|
||||||
QtGui.QPixmap.fromImage(
|
QtGui.QPixmap.fromImage(
|
||||||
QtGui.QImage(self.global_common.get_resource_path("icon.png"))
|
QtGui.QImage(dzutil.get_resource_path("icon.png"))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
header_label = QtWidgets.QLabel("dangerzone")
|
header_label = QtWidgets.QLabel("dangerzone")
|
||||||
|
@ -165,7 +167,7 @@ class WaitingWidget(QtWidgets.QWidget):
|
||||||
[container_runtime, "image", "ls"],
|
[container_runtime, "image", "ls"],
|
||||||
stdout=subprocess.DEVNULL,
|
stdout=subprocess.DEVNULL,
|
||||||
stderr=subprocess.DEVNULL,
|
stderr=subprocess.DEVNULL,
|
||||||
startupinfo=self.global_common.get_subprocess_startupinfo(),
|
startupinfo=dzutil.get_subprocess_startupinfo(),
|
||||||
) as p:
|
) as p:
|
||||||
p.communicate()
|
p.communicate()
|
||||||
if p.returncode != 0:
|
if p.returncode != 0:
|
||||||
|
@ -361,8 +363,8 @@ class SettingsWidget(QtWidgets.QWidget):
|
||||||
# OCR document
|
# OCR document
|
||||||
self.ocr_checkbox = QtWidgets.QCheckBox("OCR document, language")
|
self.ocr_checkbox = QtWidgets.QCheckBox("OCR document, language")
|
||||||
self.ocr_combobox = QtWidgets.QComboBox()
|
self.ocr_combobox = QtWidgets.QComboBox()
|
||||||
for k in self.global_common.ocr_languages:
|
for k in dzutil.OCR_LANGUAGES:
|
||||||
self.ocr_combobox.addItem(k, self.global_common.ocr_languages[k])
|
self.ocr_combobox.addItem(k, dzutil.OCR_LANGUAGES[k])
|
||||||
ocr_layout = QtWidgets.QHBoxLayout()
|
ocr_layout = QtWidgets.QHBoxLayout()
|
||||||
ocr_layout.addWidget(self.ocr_checkbox)
|
ocr_layout.addWidget(self.ocr_checkbox)
|
||||||
ocr_layout.addWidget(self.ocr_combobox)
|
ocr_layout.addWidget(self.ocr_combobox)
|
||||||
|
@ -499,7 +501,7 @@ class ConvertThread(QtCore.QThread):
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
if self.global_common.settings.get("ocr"):
|
if self.global_common.settings.get("ocr"):
|
||||||
ocr_lang = self.global_common.ocr_languages[
|
ocr_lang = dzutil.OCR_LANGUAGES[
|
||||||
self.global_common.settings.get("ocr_language")
|
self.global_common.settings.get("ocr_language")
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
|
@ -557,7 +559,7 @@ class ConvertWidget(QtWidgets.QWidget):
|
||||||
self.error_image = QtWidgets.QLabel()
|
self.error_image = QtWidgets.QLabel()
|
||||||
self.error_image.setPixmap(
|
self.error_image.setPixmap(
|
||||||
QtGui.QPixmap.fromImage(
|
QtGui.QPixmap.fromImage(
|
||||||
QtGui.QImage(self.global_common.get_resource_path("error.png"))
|
QtGui.QImage(dzutil.get_resource_path("error.png"))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.error_image.hide()
|
self.error_image.hide()
|
||||||
|
@ -616,7 +618,7 @@ class ConvertWidget(QtWidgets.QWidget):
|
||||||
subprocess.Popen(
|
subprocess.Popen(
|
||||||
f'explorer.exe /select,"{dest_filename_windows}"',
|
f'explorer.exe /select,"{dest_filename_windows}"',
|
||||||
shell=True,
|
shell=True,
|
||||||
startupinfo=self.global_common.get_subprocess_startupinfo(),
|
startupinfo=dzutil.get_subprocess_startupinfo(),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Open
|
# Open
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
from PySide6 import QtWidgets
|
from PySide6 import QtWidgets
|
||||||
|
from PySide6.QtGui import QIcon
|
||||||
|
|
||||||
from dangerzone.global_common import GlobalCommon
|
|
||||||
from dangerzone.gui import GuiCommon, Application
|
from dangerzone.gui import GuiCommon, Application
|
||||||
|
import dangerzone.util as dzutil
|
||||||
|
|
||||||
|
|
||||||
class SysTray(QtWidgets.QSystemTrayIcon):
|
class SysTray(QtWidgets.QSystemTrayIcon):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, global_common: GlobalCommon, gui_common: GuiCommon, app: Application
|
self, gui_common: GuiCommon, app: Application
|
||||||
):
|
):
|
||||||
super(SysTray, self).__init__()
|
super(SysTray, self).__init__()
|
||||||
self.global_common = global_common
|
|
||||||
self.gui_common = gui_common
|
self.gui_common = gui_common
|
||||||
self.app = app
|
self.app = app
|
||||||
|
|
||||||
self.setIcon(self.gui_common.get_window_icon())
|
self.setIcon(QIcon(dzutil.WINDOW_ICON_PATH))
|
||||||
|
|
||||||
menu = QtWidgets.QMenu()
|
menu = QtWidgets.QMenu()
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from dangerzone import APPDATA_PATH
|
import dangerzone.util as dzutil
|
||||||
|
|
||||||
|
|
||||||
class Settings:
|
class Settings:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.settings_filename = os.path.join(APPDATA_PATH, "settings.json")
|
self.settings_filename = os.path.join(dzutil.APPDATA_PATH, "settings.json")
|
||||||
self.default_settings = {
|
self.default_settings = {
|
||||||
"save": True,
|
"save": True,
|
||||||
"ocr": True,
|
"ocr": True,
|
||||||
|
@ -47,6 +47,6 @@ class Settings:
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
os.makedirs(APPDATA_PATH, exist_ok=True)
|
os.makedirs(dzutil.APPDATA_PATH, exist_ok=True)
|
||||||
with open(self.settings_filename, "w") as settings_file:
|
with open(self.settings_filename, "w") as settings_file:
|
||||||
json.dump(self.settings, settings_file, indent=4)
|
json.dump(self.settings, settings_file, indent=4)
|
||||||
|
|
230
dangerzone/util.py
Normal file
230
dangerzone/util.py
Normal file
|
@ -0,0 +1,230 @@
|
||||||
|
import inspect
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import appdirs
|
||||||
|
|
||||||
|
# If a general-purpose function doesn't depend on anything else in the dangerzone package, then it belongs here.
|
||||||
|
|
||||||
|
|
||||||
|
def get_resource_path(filename):
|
||||||
|
if getattr(sys, "dangerzone_dev", False):
|
||||||
|
# Look for resources directory relative to python file
|
||||||
|
prefix = os.path.join(
|
||||||
|
os.path.dirname(
|
||||||
|
os.path.dirname(
|
||||||
|
os.path.abspath(inspect.getfile(inspect.currentframe()))
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"share",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if platform.system() == "Darwin":
|
||||||
|
prefix = os.path.join(
|
||||||
|
os.path.dirname(os.path.dirname(sys.executable)), "Resources/share"
|
||||||
|
)
|
||||||
|
elif platform.system() == "Linux":
|
||||||
|
prefix = os.path.join(sys.prefix, "share", "dangerzone")
|
||||||
|
else:
|
||||||
|
# Windows
|
||||||
|
prefix = os.path.join(os.path.dirname(sys.executable), "share")
|
||||||
|
resource_path = os.path.join(prefix, filename)
|
||||||
|
return resource_path
|
||||||
|
|
||||||
|
|
||||||
|
def get_subprocess_startupinfo():
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
startupinfo = subprocess.STARTUPINFO()
|
||||||
|
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||||
|
return startupinfo
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _get_version() -> str:
|
||||||
|
"""Dangerzone version number. Prefer dangerzone.VERSION to this function."""
|
||||||
|
try:
|
||||||
|
with open(get_resource_path("version.txt")) as f:
|
||||||
|
version = f.read().strip()
|
||||||
|
except FileNotFoundError:
|
||||||
|
# In dev mode, in Windows, get_resource_path doesn't work properly for the container, but luckily
|
||||||
|
# it doesn't need to know the version
|
||||||
|
version = "unknown"
|
||||||
|
return version
|
||||||
|
|
||||||
|
|
||||||
|
VERSION = _get_version()
|
||||||
|
APPDATA_PATH = appdirs.user_config_dir("dangerzone")
|
||||||
|
SYSTEM = platform.system()
|
||||||
|
CONTAINER_NAME = "dangerzone.rocks/dangerzone"
|
||||||
|
CONTAINER_COMMAND = "podman" if SYSTEM == "Linux" else "Docker"
|
||||||
|
CONTAINER_RUNTIME = shutil.which(CONTAINER_COMMAND)
|
||||||
|
|
||||||
|
WINDOW_ICON_FILENAME = "dangerzone.ico" if SYSTEM == "Windows" else "icon.png"
|
||||||
|
WINDOW_ICON_PATH = get_resource_path(WINDOW_ICON_FILENAME)
|
||||||
|
|
||||||
|
# Languages supported by tesseract
|
||||||
|
OCR_LANGUAGES = {
|
||||||
|
"Afrikaans": "ar",
|
||||||
|
"Albanian": "sqi",
|
||||||
|
"Amharic": "amh",
|
||||||
|
"Arabic": "ara",
|
||||||
|
"Arabic script": "Arabic",
|
||||||
|
"Armenian": "hye",
|
||||||
|
"Armenian script": "Armenian",
|
||||||
|
"Assamese": "asm",
|
||||||
|
"Azerbaijani": "aze",
|
||||||
|
"Azerbaijani (Cyrillic)": "aze_cyrl",
|
||||||
|
"Basque": "eus",
|
||||||
|
"Belarusian": "bel",
|
||||||
|
"Bengali": "ben",
|
||||||
|
"Bengali script": "Bengali",
|
||||||
|
"Bosnian": "bos",
|
||||||
|
"Breton": "bre",
|
||||||
|
"Bulgarian": "bul",
|
||||||
|
"Burmese": "mya",
|
||||||
|
"Canadian Aboriginal script": "Canadian_Aboriginal",
|
||||||
|
"Catalan": "cat",
|
||||||
|
"Cebuano": "ceb",
|
||||||
|
"Cherokee": "chr",
|
||||||
|
"Cherokee script": "Cherokee",
|
||||||
|
"Chinese - Simplified": "chi_sim",
|
||||||
|
"Chinese - Simplified (vertical)": "chi_sim_vert",
|
||||||
|
"Chinese - Traditional": "chi_tra",
|
||||||
|
"Chinese - Traditional (vertical)": "chi_tra_vert",
|
||||||
|
"Corsican": "cos",
|
||||||
|
"Croatian": "hrv",
|
||||||
|
"Cyrillic script": "Cyrillic",
|
||||||
|
"Czech": "ces",
|
||||||
|
"Danish": "dan",
|
||||||
|
"Devanagari script": "Devanagari",
|
||||||
|
"Divehi": "div",
|
||||||
|
"Dutch": "nld",
|
||||||
|
"Dzongkha": "dzo",
|
||||||
|
"English": "eng",
|
||||||
|
"English, Middle (1100-1500)": "enm",
|
||||||
|
"Esperanto": "epo",
|
||||||
|
"Estonian": "est",
|
||||||
|
"Ethiopic script": "Ethiopic",
|
||||||
|
"Faroese": "fao",
|
||||||
|
"Filipino": "fil",
|
||||||
|
"Finnish": "fin",
|
||||||
|
"Fraktur script": "Fraktur",
|
||||||
|
"Frankish": "frk",
|
||||||
|
"French": "fra",
|
||||||
|
"French, Middle (ca.1400-1600)": "frm",
|
||||||
|
"Frisian (Western)": "fry",
|
||||||
|
"Gaelic (Scots)": "gla",
|
||||||
|
"Galician": "glg",
|
||||||
|
"Georgian": "kat",
|
||||||
|
"Georgian script": "Georgian",
|
||||||
|
"German": "deu",
|
||||||
|
"Greek": "ell",
|
||||||
|
"Greek script": "Greek",
|
||||||
|
"Gujarati": "guj",
|
||||||
|
"Gujarati script": "Gujarati",
|
||||||
|
"Gurmukhi script": "Gurmukhi",
|
||||||
|
"Hangul script": "Hangul",
|
||||||
|
"Hangul (vertical) script": "Hangul_vert",
|
||||||
|
"Han - Simplified script": "HanS",
|
||||||
|
"Han - Simplified (vertical) script": "HanS_vert",
|
||||||
|
"Han - Traditional script": "HanT",
|
||||||
|
"Han - Traditional (vertical) script": "HanT_vert",
|
||||||
|
"Hatian": "hat",
|
||||||
|
"Hebrew": "heb",
|
||||||
|
"Hebrew script": "Hebrew",
|
||||||
|
"Hindi": "hin",
|
||||||
|
"Hungarian": "hun",
|
||||||
|
"Icelandic": "isl",
|
||||||
|
"Indonesian": "ind",
|
||||||
|
"Inuktitut": "iku",
|
||||||
|
"Irish": "gle",
|
||||||
|
"Italian": "ita",
|
||||||
|
"Italian - Old": "ita_old",
|
||||||
|
"Japanese": "jpn",
|
||||||
|
"Japanese script": "Japanese",
|
||||||
|
"Japanese (vertical)": "jpn_vert",
|
||||||
|
"Japanese (vertical) script": "Japanese_vert",
|
||||||
|
"Javanese": "jav",
|
||||||
|
"Kannada": "kan",
|
||||||
|
"Kannada script": "Kannada",
|
||||||
|
"Kazakh": "kaz",
|
||||||
|
"Khmer": "khm",
|
||||||
|
"Khmer script": "Khmer",
|
||||||
|
"Korean": "kor",
|
||||||
|
"Korean (vertical)": "kor_vert",
|
||||||
|
"Kurdish (Arabic)": "kur_ara",
|
||||||
|
"Kyrgyz": "kir",
|
||||||
|
"Lao": "lao",
|
||||||
|
"Lao script": "Lao",
|
||||||
|
"Latin": "lat",
|
||||||
|
"Latin script": "Latin",
|
||||||
|
"Latvian": "lav",
|
||||||
|
"Lithuanian": "lit",
|
||||||
|
"Luxembourgish": "ltz",
|
||||||
|
"Macedonian": "mkd",
|
||||||
|
"Malayalam": "mal",
|
||||||
|
"Malayalam script": "Malayalam",
|
||||||
|
"Malay": "msa",
|
||||||
|
"Maltese": "mlt",
|
||||||
|
"Maori": "mri",
|
||||||
|
"Marathi": "mar",
|
||||||
|
"Mongolian": "mon",
|
||||||
|
"Myanmar script": "Myanmar",
|
||||||
|
"Nepali": "nep",
|
||||||
|
"Norwegian": "nor",
|
||||||
|
"Occitan (post 1500)": "oci",
|
||||||
|
"Old Georgian": "kat_old",
|
||||||
|
"Oriya (Odia) script": "Oriya",
|
||||||
|
"Oriya": "ori",
|
||||||
|
"Pashto": "pus",
|
||||||
|
"Persian": "fas",
|
||||||
|
"Polish": "pol",
|
||||||
|
"Portuguese": "por",
|
||||||
|
"Punjabi": "pan",
|
||||||
|
"Quechua": "que",
|
||||||
|
"Romanian": "ron",
|
||||||
|
"Russian": "rus",
|
||||||
|
"Sanskrit": "san",
|
||||||
|
"script and orientation": "osd",
|
||||||
|
"Serbian (Latin)": "srp_latn",
|
||||||
|
"Serbian": "srp",
|
||||||
|
"Sindhi": "snd",
|
||||||
|
"Sinhala script": "Sinhala",
|
||||||
|
"Sinhala": "sin",
|
||||||
|
"Slovakian": "slk",
|
||||||
|
"Slovenian": "slv",
|
||||||
|
"Spanish, Castilian - Old": "spa_old",
|
||||||
|
"Spanish": "spa",
|
||||||
|
"Sundanese": "sun",
|
||||||
|
"Swahili": "swa",
|
||||||
|
"Swedish": "swe",
|
||||||
|
"Syriac script": "Syriac",
|
||||||
|
"Syriac": "syr",
|
||||||
|
"Tajik": "tgk",
|
||||||
|
"Tamil script": "Tamil",
|
||||||
|
"Tamil": "tam",
|
||||||
|
"Tatar": "tat",
|
||||||
|
"Telugu script": "Telugu",
|
||||||
|
"Telugu": "tel",
|
||||||
|
"Thaana script": "Thaana",
|
||||||
|
"Thai script": "Thai",
|
||||||
|
"Thai": "tha",
|
||||||
|
"Tibetan script": "Tibetan",
|
||||||
|
"Tibetan Standard": "bod",
|
||||||
|
"Tigrinya": "tir",
|
||||||
|
"Tonga": "ton",
|
||||||
|
"Turkish": "tur",
|
||||||
|
"Ukrainian": "ukr",
|
||||||
|
"Urdu": "urd",
|
||||||
|
"Uyghur": "uig",
|
||||||
|
"Uzbek (Cyrillic)": "uzb_cyrl",
|
||||||
|
"Uzbek": "uzb",
|
||||||
|
"Vietnamese script": "Vietnamese",
|
||||||
|
"Vietnamese": "vie",
|
||||||
|
"Welsh": "cym",
|
||||||
|
"Yiddish": "yid",
|
||||||
|
"Yoruba": "yor"
|
||||||
|
}
|
Loading…
Reference in a new issue