Quelle LibreLogo.py
Sprache: Python
|
|
# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
#
# This file is part of the LibreOffice project.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
import sys
import os
import uno
import unohelper
import re
import random
import traceback
import itertools
import threading
import time as __time__
from math import pi, sin, cos, asin, hypot
from com.sun.star.awt import Point as __Point__
from com.sun.star.awt import Gradient as __Gradient__
from com.sun.star.awt.GradientStyle import LINEAR as __GradientStyle_LINEAR__
from com.sun.star.drawing import LineDash as __LineDash__
from com.sun.star.drawing import Hatch as __Hatch__
from com.sun.star.drawing import PolyPolygonBezierCoords as __Bezier__
from com.sun.star.text.TextContentAnchorType import AT_PAGE as __AT_PAGE__
from com.sun.star.text.WrapTextMode import THROUGH as __THROUGH__
from com.sun.star.drawing.LineCap import BUTT as __Cap_NONE__
from com.sun.star.drawing.LineCap import ROUND as __Cap_ROUND__
from com.sun.star.drawing.LineCap import SQUARE as __Cap_SQUARE__
from com.sun.star.drawing.LineJoint import NONE as __Joint_NONE__
from com.sun.star.drawing.LineJoint import BEVEL as __BEVEL__
from com.sun.star.drawing.LineJoint import MITER as __MITER__
from com.sun.star.drawing.LineJoint import ROUND as __ROUNDED__
from com.sun.star.drawing.FillStyle import NONE as __FillStyle_NONE__
from com.sun.star.drawing.FillStyle import GRADIENT as __FillStyle_GRADIENT__
from com.sun.star.drawing.LineStyle import NONE as __LineStyle_NONE__
from com.sun.star.drawing.LineStyle import SOLID as __LineStyle_SOLID__
from com.sun.star.drawing.LineStyle import DASH as __LineStyle_DASHED__
from com.sun.star.drawing.DashStyle import RECT as __DashStyle_RECT__
from com.sun.star.drawing.CircleKind import FULL as __FULL__
from com.sun.star.drawing.CircleKind import SECTION as __SECTION__
from com.sun.star.drawing.CircleKind import CUT as __CUT__
from com.sun.star.drawing.CircleKind import ARC as __ARC__
from com.sun.star.awt.FontSlant import NONE as __Slant_NONE__
from com.sun.star.awt.FontSlant import ITALIC as __Slant_ITALIC__
from com.sun.star.awt.FontUnderline import SINGLE as __Underline_SINGLE__
from com.sun.star.awt.FontStrikeout import SINGLE as __Strikeout_SINGLE__
from com.sun.star.awt import Size as __Size__
from com.sun.star.awt import WindowDescriptor as __WinDesc__
from com.sun.star.awt.WindowClass import MODALTOP as __MODALTOP__
from com.sun.star.awt.VclWindowPeerAttribute import OK as __OK__
from com.sun.star.awt.VclWindowPeerAttribute import OK_CANCEL as __OK_CANCEL__
from com.sun.star.awt.VclWindowPeerAttribute import YES_NO_CANCEL as __YES_NO_CANCEL_ _ # OK_CANCEL, YES_NO, RETRY_CANCEL, DEF_OK, DEF_CANCEL, DEF_RETRY, DEF_YES, DEF_NO
from com.sun.star.awt.PushButtonType import OK as __Button_OK__
from com.sun.star.awt.PushButtonType import CANCEL as __Button_CANCEL__
from com.sun.star.util.MeasureUnit import APPFONT as __APPFONT__
from com.sun.star.beans import PropertyValue as __property__
from com.sun.star.lang import Locale
__lng__ = {}
def __trace__():
if 'PYUNO_LOGLEVEL' in os.environ:
print(traceback.format_exc())
# get localized commands and messages
def __l12n__(lng):
global __lng_fallback__
try:
return __lng__[lng]
except Exception:
try:
# load resource file
__lng__[lng] = dict([[i.decode("unicode-escape").split("=")[0].strip(), i.decode("unicode-escape").split("=")[1].strip().strip("|")] for i in open(__lngpath__ + "LibreLogo_" + lng + ".properties", 'rb').readlines() if b"=" in i])
return __lng__[lng]
except Exception:
try:
# or use embedded fallback resource dictionary
__lng__[lng] = {}
for i in __lng_fallback__:
try:
__lng__[lng][i] = __lng_fallback__[i][lng]
except Exception:
try:
__lng__[lng][i] = __lng_fallback__[i][lng.split('_')[0]]
except Exception:
__lng__[lng][i] = __lng_fallback__[i]["en_US"]
return __lng__[lng]
except Exception:
__trace__()
return None
try:
urebootstrap = os.environ["URE_BOOTSTRAP"]
except Exception:
# starting in command line updates embedded fallback language dictionary
print("Update fallback language resource using property file arguments")
for i in sys.argv[1:]:
r = re.match("^(.*)LibreLogo_(.*)[.]properties$", i)
__lngpath__= r.group(1)
__l12n__(r.group(2))
fallback = {}
# fallback of fallback is en_US
for i in __lng__["en_US"]:
fallback[i] = { "en_US": __lng__["en_US"][i] }
# create fallback dictionary
for i in __lng__:
dif = 0
for j in __lng__[i]:
if __lng__[i][j] != __lng__["en_US"][j]:
fallback[j][i] = __lng__[i][j]
dif = dif + 1
print(i, dif)
# update fallback resource data in this program file
import fileinput
for line in fileinput.input(sys.argv[0], inplace=True):
if re.match("^__lng_fallback__", line):
print("__lng_fallback__ = {")
# break it (CPython has problem with very long Unicode line)
for i in fallback:
print("'%s':%s," % (i, str(fallback[i])))
print("}")
sys.exit(1)
else:
print(line.rstrip("\n")),
if "vnd.sun.star.pathname" in urebootstrap:
__lngpath__ = re.sub(r"^vnd.sun.star.pathname:(.*)program(/|\\)fundamental([.]ini|rc)$", "\\1", urebootstrap)
else:
# A way to know if we use MacOs
if "Resources" in urebootstrap:
__lngpath__ = unohelper.fileUrlToSystemPath(re.sub("fundamentalrc$", "", urebootstrap))
else:
__lngpath__ = unohelper.fileUrlToSystemPath(re.sub("program/(fundamental.ini|fundamentalrc)$", "share", urebootstrap))
__lngpath__ = __lngpath__ + "/Scripts/python/LibreLogo/".replace("/", os.sep)
__translang__ = "am|ca|cs|de|dk|el|en|eo|es|et|fr|hu|it|ja|nl|no|pl|pt|ru|se|sl" # FIXME supported languages for language guessing, expand this list, according to the localizations
__docs__ = {}
__prevcode__ = None
__prevlang__ = None
__prevcompiledcode__ = None
__thread__ = None
__lock__ = threading.Lock()
__halt__ = False
__compiled__ = ""
__group__ = 0
__groupstack__ = []
__grouplefthang__ = 0
__comp__ = {}
__strings__ = []
__colors__ = {}
__COLORS__ = ['BLACK', 0x000000], ['SILVER', 0xc0c0c0], ['GRAY', 0x808080], \
['WHITE', 0xffffff], ['MAROON', 0x800000], ['RED', 0xff0000], \
['PURPLE', 0x800080], ['FUCHSIA', 0xff00ff], ['GREEN', 0x008000], \
['LIME', 0x00ff00], ['OLIVE', 0x808000], ['YELLOW', 0xffff00], \
['NAVY', 0x000080], ['BLUE', 0x0000ff], ['TEAL', 0x008080], \
['AQUA', 0x00ffff], ['PINK', 0xffc0cb], ['TOMATO', 0xff6347], \
['ORANGE', 0xffa500], ['GOLD', 0xffd700], ['VIOLET', 0x9400d3], \
['SKYBLUE', 0x87ceeb], ['CHOCOLATE', 0xd2691e], ['BROWN', 0xa52a2a], \
['INVISIBLE', 0xffffffff]
__NORMCOLORS__ = [[[255, 255, 0], 0, -11, 1, -11],
[[255, 128, 0], 1, 116, 1, -33], [[255, 0, 0], 1, 95, 2, 42],
[[255, 0, 255], 2, -213, 0, -106], [[0, 0, 255], 0, 148, 1, 127],
[[0, 255, 255], 1, -128, 2, -63], [[0, 255, 0], 2, 192, 0, 244]]
__STRCONST__ = [i[0] for i in __COLORS__] + ['NONE', 'BEVEL', 'MITER', 'ROUNDED', 'SOLID', 'DASH', 'DOTTED', 'BOLD', 'ITALIC', 'UPRIGHT', 'NORMAL', "HOUR", "PT", "INCH", "MM", "CM"]
__SLEEP_SLICE_IN_MILLISECONDS__ = 500
__PT_TO_TWIP__ = 20
__MM_TO_PT__ = 1/(25.4/72)
__MM10_TO_TWIP__ = 1/(2540.0/72/20) # 0.01 mm to twentieth point
__FILLCOLOR__ = 0x8000cc00
__LINEWIDTH__ = 0.5 * __PT_TO_TWIP__
__ENCODED_STRING__ = "_s_%s___"
__ENCODED_COMMENT__ = "_c_%s___"
__DECODE_STRING_REGEX__ = "_s_([0-9]+)___"
__DECODE_COMMENT_REGEX__ = "_c_([0-9]+)___"
__LINEBREAK__ = "#_@L_i_N_e@_#"
__TURTLE__ = "turtle"
__ACTUAL__ = "actual"
__BASEFONTFAMILY__ = "Linux Biolinum G"
__LineStyle_DOTTED__ = 2
# LABEL supports font features with the simplified syntax <FEATURE>text</FEATURE>, e.g.
# LABEL "Small caps: <smcp>text</smcp>"
# prints "Small caps: TEXT", where TEXT is small capital, if that feature is supported by the font
# See https://en.wikipedia.org/wiki/List_of_typographic_features
__match_fontfeatures__ = re.compile( r"(</?)("
# OpenType
"abvf|abvm|abvs|blwf|blwm|blws|pref|pres|psts|pstf|dist|akhn|haln|half|nukt|rkrf|rphf|vatu|cjct|cfar|"
"smpl|trad|tnam|expt|hojo|nlck|jp78|jp83|jp90|jp04|hngl|ljmo|tjmo|vjmo|fwid|hwid|halt|twid|qwid|pwid|palt|pkna|ruby|hkna|vkna|cpct|"
"curs|jalt|mset|rclt|rlig|isol|init|medi|med2|fina|fin2|fin3|falt|stch|"
"lnum|onum|pnum|tnum|frac|afrc|dnom|numr|sinf|zero|mgrk|flac|dtls|ssty|"
"smcp|c2sc|pcap|c2pc|unic|cpsp|case|ital|ordn|"
"valt|vhal|vpal|vert|vrt2|vrtr|vkrn|ltra|ltrm|rtla|rtlm"
"aalt|swsh|cswh|calt|hist|locl|rand|nalt|cv[0-9][0-9]|salt|ss[01][0-9]|ss20|subs|sups|titl|rvrn|clig|dlig|hlig|liga"
"ccmp|kern|mark|mkmk|opbd|lfbd|rtbd|"
# Linux Libertine G
"size|ornm|ingl|algn|arti|caps|circ|dash|dbls|foot|frsp|grkn|hang|itlc|ligc|minu|nfsp|para|quot|texm|thou|vari)((=.*)?>)", re.IGNORECASE )
# LABEL localized color tags, e.g. <red>text in red</red>
__match_localized_colors__ = {}
# LABEL not localized tags (localized translated to these):
__match_tags__ = [re.compile(i, re.IGNORECASE) for i in [r'<(b|strong)>', r'</(b|strong)>', r'<(i|em)>', r'</(i|em)>', '<u>', '</u>', r'<(s|del)>', r'</(s|del)>', '<sup>', '</sup>', '<sub>', '</sub>', r'<(fontcolor) ([^<>]*)>', r'</(fontcolor)>', r'<(fillcolor) ([^<>]*)>', r'</(fillcolor)>', r'<(fontfamily) ([^<>]*)>', r'</(fontfamily)>', r'<(fontfeature) ([^<>]*)>', r'</(fontfeature) ?([^<>]*)>', r'<(fontheight) ([^<>]*)>', r'</(fontheight)>']]
class __Doc__:
def __init__(self, doc):
self.doc = doc
self.secure = False
try:
self.drawpage = doc.DrawPage # Writer
except Exception:
self.drawpage = doc.DrawPages.getByIndex(0) # Draw, Impress
self.shapecache = {}
self.shapecount = itertools.count()
self.time = 0
self.zoomvalue = 0
self.lockturtle = False
self.fixSVG = False
self.initialize()
def initialize(self):
self.pen = 1
self.pencolor = 0
self.pensize = __LINEWIDTH__
self.linestyle = __LineStyle_SOLID__
self.linejoint = __ROUNDED__
self.linecap = __Cap_NONE__
self.oldlc = 0
self.oldlw = 0
self.oldls = __LineStyle_SOLID__
self.oldlj = __ROUNDED__
self.oldlcap = __Cap_NONE__
self.continuous = True
self.areacolor = __FILLCOLOR__
self.t10y = int((__FILLCOLOR__ >> 24) / (255.0/100))
self.hatch = None
self.textcolor = 0
self.fontfamily = __BASEFONTFAMILY__
self.fontheight = 12
self.fontweight = 100
self.fontstyle = 0
self.points = []
def __getprop__(name, value):
p, p.Name, p.Value = __property__(), name, value
return p
__uilocale__ = uno.getComponentContext().getValueByName("/singletons/com.sun.star.configuration.theDefaultProvider").\
createInstanceWithArguments("com.sun.star.configuration.ConfigurationAccess",\
(__getprop__("nodepath", "/org.openoffice.Setup/L10N"),)).getByName("ooLocale") + '-' # handle missing Country of locale 'eo'
# dot for dotted line (implemented as an array of dot-headed arrows, because PostScript dot isn't supported by Writer)
def __gendots__(n):
return [__Point__(round(sin(360.0/n * i * pi/180.0) * 600), round(cos(360.0/n * i * pi/180) * 600)) for i in range(n)]
__bezierdot__ = __Bezier__()
__bezierdot__.Coordinates = (tuple(__gendots__(32)),)
__bezierdot__.Flags = ((0,) * 32,)
# turtle shape
__TURTLESHAPE__ = [tuple([(__Point__(-120, 130), __Point__(-245, 347), __Point__(-291, 176), ), (__Point__(0, -500), __Point__(126, -375), __Point__(0, -250), __Point__(-124, -375), ), (__Point__(295, 170), __Point__(124, 124), __Point__(250, 340), ), (__Point__(466, -204), __Point__(224, -269), __Point__(71, -180), __Point__(313, -116), ), (__Point__(-75, -175), __Point__(-292, -300), __Point__(-417, -83), ), (__Point__(250, 0), __Point__(0, -250), __Point__(-250, 0), __Point__(0, 250), )] +
[(i,) for i in __gendots__(32)] + # single points for wider selection
[(__Point__(0, 0),)]), # last point for position handling
((__Point__(0, 0),),)] # hidden turtle (single point to draw at the left border of the page area)
def __getdocument__():
global __docs__, _
# The XSCRIPTCONTEXT object is part of the UNO (Universal Network Objects)
# API provided by LibreOffice, which allows scripting languages like Python
# to interact with LibreOffice's underlying functionality. It provides a
# bridge between the scripting environment and the LibreOffice application,
# making it possible for scripts to control and extend the functionality of LibreOffice.
# Because XSCRIPTCONTEXT is automatically available in LibreOffice Python
# scripts, developers can directly use it to access the LibreOffice API
# without needing to define it themselves, simplifying script development
# and making it easier to work with LibreOffice's features and capabilities._
# It would be good to use a linter that can be told to ignore this
# "undefined variable" in the code (like flake8 or ruff) using # noqa: F821
doc = XSCRIPTCONTEXT.getDocument() # noqa: F821
try:
_ = __docs__[doc.RuntimeUID]
except Exception:
_ = __Doc__(doc)
__docs__[doc.RuntimeUID] = _
# input function, result: input string or 0
def Input(s):
global __halt__
try:
ctx = uno.getComponentContext()
smgr = ctx.ServiceManager
# dialog
d = smgr.createInstanceWithContext("com.sun.star.awt.UnoControlDialogModel", ctx)
ps = _.doc.CurrentController.Frame.ContainerWindow.getPosSize()
lo = _.doc.CurrentController.Frame.ContainerWindow.convertSizeToLogic(__Size__(ps.Width, ps.Height), __APPFONT__)
d.PositionX, d.PositionY, d.Width, d.Height = lo.Width/2 - 75, lo.Height/2 - 25, 150, 50
# label
l = d.createInstance("com.sun.star.awt.UnoControlFixedTextModel" )
if type(s) is list:
s = s[0]
l.PositionX, l.PositionY, l.Width, l.Height, l.Name, l.TabIndex, l.Label = 5, 4, 140, 14, "l1", 2, s
# textbox or combobox
e = d.createInstance("com.sun.star.awt.UnoControlEditModel")
e.PositionX, e.PositionY, e.Width, e.Height, e.Name, e.TabIndex = 5, 14, 140, 12, "e1", 0
# buttons
b = d.createInstance( "com.sun.star.awt.UnoControlButtonModel" )
b.PositionX, b.PositionY, b.Width, b.Height, b.Name, b.TabIndex, b.PushButtonType, b.DefaultButton = 55, 32, 45, 14, "b1", 1, __Button_OK__, True
b2 = d.createInstance( "com.sun.star.awt.UnoControlButtonModel" )
b2.PositionX, b2.PositionY, b2.Width, b2.Height, b2.Name, b2.TabIndex, b2.PushButtonType = 100, 32, 45, 14, "b2", 1, __Button_CANCEL__
# insert the control models into the dialog model
d.insertByName( "l1", l)
d.insertByName( "b1", b)
d.insertByName( "b2", b2)
d.insertByName( "e1", e)
# create the dialog control and set the model
controlContainer = smgr.createInstanceWithContext("com.sun.star.awt.UnoControlDialog", ctx)
controlContainer.setModel(d)
# create a peer
toolkit = smgr.createInstanceWithContext("com.sun.star.awt.ExtToolkit", ctx)
controlContainer.setVisible(False)
controlContainer.createPeer(toolkit, None)
# execute it
inputtext = controlContainer.execute()
if inputtext:
inputtext = e.Text
else:
# Cancel button
__halt__ = True
# dispose the dialog
controlContainer.dispose()
# stop program at pressing Cancel
__checkhalt__()
return inputtext
except Exception:
__trace__()
def __string__(s, decimal = None): # convert decimal sign, localized BOOL and SET
if not decimal:
decimal = _.decimal
if decimal == ',' and type(s) is float:
return str(s).replace(".", ",")
if type(s) in [list, tuple, dict, set]:
s = re.sub("(?u)(['\"])(([^'\"]|\\['\"])*)(?<!\\\\)\\1", __encodestring__, str(s)) # XXX fix double '\'\"'
if decimal == ',':
s = s.replace(".", ",")
return re.sub(__DECODE_STRING_REGEX__, __decodestring__, \
s.replace('set', __locname__('SET')).replace('True', __locname__('TRUE')).replace('False', __locname__('FALSE')))
if type(s) in [str]:
return s
elif type(s) is bool:
return __locname__(str(s).upper())
return str(s)
def Print(s):
global __halt__
s = __string__(s, _.decimal)
if not MessageBox(_.doc.CurrentController.Frame.ContainerWindow, s[:500] + s[500:5000].replace('\n', ' '), "", "messbox", __OK_CANCEL__):
# stop program at pressing Cancel
__halt__ = True
__checkhalt__()
def MessageBox(parent, message, title, msgtype = "messbox", buttons = __OK__):
msgtypes = ("messbox", "infobox", "errorbox", "warningbox", "querybox")
if msgtype not in msgtypes:
msgtype = "messbox"
d = __WinDesc__()
d.Type = __MODALTOP__
d.WindowServiceName = msgtype
d.ParentIndex = -1
d.Parent = parent
d.WindowAttributes = buttons
tk = parent.getToolkit()
msgbox = tk.createWindow(d)
msgbox.MessageText = message
if title:
msgbox.CaptionText = title
return msgbox.execute()
def Random(r):
try:
return r * random.random()
except Exception:
return list(r)[int(random.random() * len(r))]
def to_ascii(s):
return s.encode("unicode-escape").decode("utf-8").replace("\\u", "__u__").replace(r"\x", "__x__")
def to_unicode(s):
return bytes(s.replace("__x__", r"\x").replace("__u__", "\\u"), "ascii").decode("unicode-escape")
def __locname__(name, l = -1):
if l == -1:
l = _.lng
for i in __l12n__(l):
if i == name.upper():
return __l12n__(l)[i].split("|")[0] # return with the first localized name
return to_unicode(name)
def __getcursor__(fulltext):
realselection = False
try:
text = _.doc.getCurrentController().getViewCursor().getText().createTextCursor() # copy selection (also in frames)
text.gotoRange(_.doc.getCurrentController().getViewCursor(), False)
if fulltext:
1/len(text.getString()) # exception, if zero length
realselection = True
except Exception:
text = _.doc.getText().createTextCursorByRange(_.doc.getText().getStart())
text.gotoEnd(True)
return text, realselection
def __translate__(arg = None):
global _
__getdocument__()
selection = __getcursor__(True)[0]
__initialize__()
__setlang__()
# detect language
text = selection.getString()
# remove comments and strings
text = re.sub(r"[ ]*;[^\n]*", "", re.sub(r"['„“‘«»「][^\n'”“‘’«»」]*['”“‘’«»」]", "", re.sub(r"^[ \t]*[;#][^\n]*", "", text)))
text = " ".join(set(re.findall(r"(?u)\w+", text)) - set(re.findall(r"(?u)\w*\d+\w*", text))).lower() # only words
ctx = uno.getComponentContext()
guess = ctx.ServiceManager.createInstanceWithContext("com.sun.star.linguistic2.LanguageGuessing", ctx)
guess.disableLanguages(guess.getEnabledLanguages())
guess.enableLanguages(tuple([Locale(i, "", "") for i in __translang__.split("|")]))
guess = guess.guessPrimaryLanguage(text, 0, len(text))
try:
l = {'cs': 'cs_CZ', 'el': 'el_GR', 'en': 'en_US', 'pt': 'pt_BR'}[guess.Language]
except Exception:
l = guess.Language + '_' + guess.Language.upper()
lang = __l12n__(l)
if not lang:
lang = __l12n__(guess.Language)
if not lang:
lang = __l12n__(_.lng)
if not lang:
lang = __l12n__("en_US")
lq = '\'' + lang['LEFTSTRING'].replace("|", "")
rq = '\'' + lang['RIGHTSTRING'].replace("|", "")
text = re.sub(r"^(([ \t]*[;#][^\n]*))", __encodecomment__, text)
text = re.sub("(?u)([%s])((?:[^\n%s]|\\\\[%s])*)(?<!\\\\)[%s]" % (lq, rq, rq, rq), __encodestring__, selection.getString())
text = re.sub(r'(?u)(?<![0-9])(")(~?\w*)', __encodestring__, text)
text = re.sub(r";(([^\n]*))", __encodecomment__, text)
# translate the program to the language of the document FIXME space/tab
exception = ['DECIMAL']
in1 = lang['IN'].upper()
in2 = __l12n__(_.lng)['IN'].split("|")[0].upper()
if in1[0] == '-' and in2[0] != '-': # "for x y-in" -> "for x in y"
exception += ['IN']
text = re.sub(r"(?ui)\b((?:%s) +:?\w+) +([^\n]+)(?:%s) +(?=[\[] |[\[]\n)" % (lang['FOR'], in1), "\\1 %s \\2 " % in2, text)
text = re.sub(r"(?ui)(:?\b\w+|[\[][^[\n]*])\b(?:%s)\b" % in1, "%s \\1" % in2, text)
elif in1[0] != '-' and in2[0] == '-': # "for x in y" -> "for x y-in"
exception += ['IN']
text = re.sub(r"(?ui)(?<=\n)((?:%s)\b +:?\w+) +(?:%s) +([^\n]+?) +(?=[\[] |[\[]\n)" % (lang['FOR'], in1), "\\1 \\2%s " % in2, text)
text = re.sub(r"(?ui)(?<!:)\b(?:%s) +(:?\b\w+|[\[][^[\n]*])\b" % in1, "\\1%s" % in2, text)
for i in set(lang) - set(exception):
text = re.sub(r'(?ui)(?<!:)\b(%s)\b' % lang[i], __l12n__(_.lng)[i].split("|")[0].upper(), text)
text = re.sub(r"(?<=\d)[%s](?=\d)" % lang['DECIMAL'], __l12n__(_.lng)['DECIMAL'], text)
# decode strings and comments
quoted = u"(?ui)(?<=%s)(%%s)(?=%s)" % (__l12n__(_.lng)['LEFTSTRING'][0], __l12n__(_.lng)['RIGHTSTRING'][0])
text = re.sub(__DECODE_STRING_REGEX__, __decodestring2__, text)
for i in __STRCONST__:
text = re.sub(quoted % lang[i], __l12n__(_.lng)[i].split("|")[0].upper(), text)
text = re.sub(__DECODE_COMMENT_REGEX__, __decodecomment__, text)
if _.doc.getText().compareRegionStarts(selection.getStart(), _.doc.getText().getStart()) == 0:
pagebreak = True
selection.setString("\n" + text.lstrip("\n"))
else:
pagebreak = False
selection.setString(text)
# convert to paragraphs
__dispatcher__(".uno:ExecuteSearch", (__getprop__("SearchItem.SearchString", r"\n"), __getprop__("SearchItem.ReplaceString", r"\n"), \
__getprop__("Quiet", True), __getprop__("SearchItem.Command", 3), __getprop__("SearchItem.StyleFamily", 2), \
__getprop__("SearchItem.AlgorithmType", 1), __getprop__("SearchItem.AlgorithmType2", 2), __getprop__("SearchItem.SearchFlags", 0)))
# set 2-page layout
if pagebreak:
selection.getStart().BreakType = 4
__dispatcher__(".uno:ZoomPage")
def __get_time__():
return __time__.process_time() - _.start_time
class LogoProgram(threading.Thread):
def __init__(self, code):
self.code = code
threading.Thread.__init__(self)
def secure(self):
# 0 = secure
if _.secure:
return 0
# 1 = forms, fields or embedded objects are forbidden
if _.doc.DrawPage.Forms.getCount() > 0 or _.doc.getTextFields().createEnumeration().hasMoreElements() or _.doc.getEmbeddedObjects().getCount() > 0:
return 1
# 2 = hyperlinks with script events
nodes = _.doc.Text.createEnumeration()
while nodes.hasMoreElements():
node = nodes.nextElement()
if node.supportsService("com.sun.star.text.Paragraph"):
portions = node.createEnumeration()
while portions.hasMoreElements():
portion = portions.nextElement()
if portion.PropertySetInfo.hasPropertyByName("HyperLinkEvents"):
events = portion.getPropertyValue("HyperLinkEvents")
for event in events.getElementNames():
attributes = events.getByName(event)
for attribute in attributes:
if attribute.Name == "EventType" and attribute.Value == "Script":
return 2
# 2 = images with script events
images = _.doc.DrawPage.createEnumeration()
while images.hasMoreElements():
image = images.nextElement()
try:
events = image.Events
for event in events.getElementNames():
attributes = events.getByName(event)
for attribute in attributes:
if attribute.Name == "EventType" and attribute.Value == "Script":
return 2
except Exception:
pass
_.secure = True
return 0
def run(self):
global __thread__
try:
# check document security
secid = self.secure()
if secid > 0:
parent = _.doc.CurrentController.Frame.ContainerWindow
MessageBox(parent, "Document objects with%s script events" % [" possible", ""][secid-1], "LibreLogo program can't start", "errorbox")
else:
_.fixSVG = False
_.start_time = __time__.process_time()
exec(self.code)
__unlock__(all_levels = True)
if _.origcursor[0] and _.origcursor[1]:
__dispatcher__(".uno:Escape")
try:
_.doc.CurrentController.getViewCursor().gotoRange(_.origcursor[0], False)
except Exception:
_.doc.CurrentController.getViewCursor().gotoRange(_.origcursor[0].getStart(), False)
except Exception:
try:
__unlock__(all_levels = True)
TRACEPATTERN = '"<string>", line '
message = traceback.format_exc()
l = re.findall(TRACEPATTERN + '[0-9]+', message)
if len(l) > 0 and "SystemExit" not in message:
line = len(re.findall(__LINEBREAK__, ''.join(self.code.split("\n")[:int(l[-1][len(TRACEPATTERN):])]))) + 1
caption = __l12n__(_.lng)['LIBRELOGO']
if __prevcode__ and "\n" in __prevcode__:
__gotoline__(line)
caption = __l12n__(_.lng)['ERROR'] % line
parent = _.doc.CurrentController.Frame.ContainerWindow
if "maximum recursion" in message:
MessageBox(parent, __l12n__(_.lng)['ERR_STOP'] + " " + __l12n__(_.lng)['ERR_MAXRECURSION'] % sys.getrecursionlimit(), __l12n__(_.lng)['LIBRELOGO'])
elif "cannot initialize memory" in message or "Couldn't instantiate" in message:
MessageBox(parent, __l12n__(_.lng)['ERR_STOP'] + " " + __l12n__(_.lng)['ERR_MEMORY'], __l12n__(_.lng)['LIBRELOGO'])
elif "ZeroDivisionError" in message:
MessageBox(parent, __l12n__(_.lng)['ERR_ZERODIVISION'], caption, "errorbox")
elif "IndexError" in message:
MessageBox(parent, __l12n__(_.lng)['ERR_INDEX'], caption, "errorbox")
elif "KeyError" in message:
MessageBox(parent, __l12n__(_.lng)['ERR_KEY'] % eval(re.search("KeyError: ([^\n]*)", message).group(1)), caption, "errorbox")
elif "NameError" in message:
if "__repeat__" in message:
MessageBox(parent, __l12n__(_.lng)['ERR_ARGUMENTS'] % (__locname__('REPEAT'), 1, 0), caption, "errorbox")
else:
MessageBox(parent, __l12n__(_.lng)['ERR_NAME'] % \
to_unicode(re.search(r"(?<=name ')[\w_]*(?=')", message).group(0)), caption, "errorbox")
elif "TypeError" in message and "argument" in message and "given" in message:
r = re.search(r"([\w_]*)[(][)][^\n]* (\w+) arguments? [(](\d+)", message) # XXX later: handle 'no arguments' + plural
MessageBox(parent, __l12n__(_.lng)['ERR_ARGUMENTS'] % (__locname__(r.group(1)), r.group(2), r.group(3)), caption, "errorbox")
else:
origline = __compiled__.split("\n")[line-1]
if "com.sun.star" not in message and "__repeat__" not in message and "*)" not in message and ("[" in origline or "]" in origline):
MessageBox(parent, __l12n__(_.lng)['ERR_BLOCK'], caption, "errorbox")
else:
MessageBox(parent, __l12n__(_.lng)['ERROR'] %line, __l12n__(_.lng)['LIBRELOGO'], "errorbox")
__trace__()
except Exception:
pass
with __lock__:
__thread__ = None
# to check LibreLogo program termination (in that case, return value is False)
def __is_alive__():
return __thread__ is not None
def __encodestring__(m):
__strings__.append(re.sub("(\\[^\\]|\\\\(?=[‘’“”»」』]))", "", m.group(2)))
# replace the string with the numbered identifier _s_0___, _s_1___, ...
return __ENCODED_STRING__ % (len(__strings__) - 1)
def __encodecomment__(m):
__strings__.append(re.sub("\\[^\\]", "", m.group(2)))
return __ENCODED_COMMENT__ % (len(__strings__) - 1)
def __decodestring__(m):
return "u'%s'" % __strings__[int(m.group(1))]
def __decodestring2__(m):
return __l12n__(_.lng)['LEFTSTRING'][0] + __strings__[int(m.group(1))] + __l12n__(_.lng)['RIGHTSTRING'][0]
def __decodecomment__(m):
return ";" + __strings__[int(m.group(1))]
def __initialize__():
global __halt__, __thread__
__getdocument__()
_.zoomvalue = _.doc.CurrentController.getViewSettings().ZoomValue
shape = __getshape__(__TURTLE__)
if not shape:
shape = _.doc.createInstance( "com.sun.star.drawing.PolyPolygonShape" )
shape.AnchorType = __AT_PAGE__
shape.TextWrap = __THROUGH__
shape.Opaque = True
_.drawpage.add(shape)
shape.PolyPolygon = __TURTLESHAPE__[0]
_.shapecache[__TURTLE__] = shape
shape.Name = __TURTLE__
_.initialize()
turtlehome()
_.doc.CurrentController.select(shape)
shape.FillColor, transparence = __splitcolor__(_.areacolor, shape)
shape.LineColor, shape.LineTransparence = __splitcolor__(_.pencolor)
elif shape.Visible:
if shape.FillStyle == __FillStyle_NONE__:
_.areacolor = 0xffffffff
else:
_.areacolor = shape.FillColor + (int(255.0 * shape.FillTransparence/100) << 24)
if shape.LineWidth != round((1 + _.pen * 2) * __PT_TO_TWIP__ / __MM10_TO_TWIP__) and shape.LineWidth != round(__LINEWIDTH__ / __MM10_TO_TWIP__):
_.pensize = shape.LineWidth * __MM10_TO_TWIP__
if shape.LineStyle == __LineStyle_NONE__: # - none -
__pen__(0)
else:
if shape.LineStyle == __LineStyle_SOLID__:
__pen__(1)
_.pencolor = shape.LineColor + (int(255.0 * shape.LineTransparence/100) << 24)
shape.LineJoint = __ROUNDED__
shape.Shadow = True
shape.FillColor, transparence = __splitcolor__(_.areacolor, shape)
shape.FillTransparence = min(95, transparence)
shape.ShadowColor, shape.ShadowTransparence, shape.ShadowXDistance, shape.ShadowYDistance = (0, 20, 0, 0)
shape.LineWidth = min(_.pensize, (1 + _.pen * 2) * __PT_TO_TWIP__) / __MM10_TO_TWIP__
shape.SizeProtect = True
def pagesize(n = -1):
if n == -1:
ps = _.doc.CurrentController.getViewCursor().PageStyleName
page = _.doc.StyleFamilies.getByName("PageStyles").getByName(ps)
return [page.Width * __MM10_TO_TWIP__ / __PT_TO_TWIP__, page.Height * __MM10_TO_TWIP__ / __PT_TO_TWIP__]
return None
def turtlehome():
turtle = __getshape__(__TURTLE__)
if turtle:
ps = _.doc.CurrentController.getViewCursor().PageStyleName
page = _.doc.StyleFamilies.getByName("PageStyles").getByName(ps)
turtle.setPosition(__Point__((page.Width - turtle.BoundRect.Width)/2, (page.Height - turtle.BoundRect.Height)/2))
turtle.LineStyle = __LineStyle_SOLID__
turtle.LineJoint = __MITER__
turtle.LineWidth = min(_.pensize, (1 + _.pen * 2) * __PT_TO_TWIP__) / __MM10_TO_TWIP__
turtle.LineColor, none = __splitcolor__(_.pencolor)
turtle.LineTransparence = 25
turtle.RotateAngle = 0
turtle.ZOrder = 1000
def __pen__(n):
_.pen = n
turtle = __getshape__(__TURTLE__)
if turtle:
if n:
turtle.LineStyle = __LineStyle_SOLID__
turtle.LineWidth = min(_.pensize, 3 * __PT_TO_TWIP__) / __MM10_TO_TWIP__
else:
turtle.LineStyle = __LineStyle_DASHED__
turtle.LineDash = __LineDash__(__DashStyle_RECT__, 0, 0, 1, __PT_TO_TWIP__, __PT_TO_TWIP__)
turtle.LineWidth = min(_.pensize, __PT_TO_TWIP__) / __MM10_TO_TWIP__
def __visible__(shape, visible = -1): # for OOo 3.2 compatibility
try:
if visible == -1:
return shape.Visible
shape.Visible = visible
except Exception:
return True
def hideturtle():
turtle = __getshape__(__TURTLE__)
if turtle and turtle.Visible:
z = turtle.getPosition()
z = __Point__(z.X + turtle.BoundRect.Width / 2.0, z.Y + turtle.BoundRect.Height / 2.0)
turtle.PolyPolygon = __TURTLESHAPE__[1]
__visible__(turtle, False)
turtle.LineTransparence, turtle.FillTransparence = 100, 100 # for saved files
turtle.setPosition(z)
else:
# HIDETURTLE during locking, no need SHOWTURTLE at the end of locking
_.lockturtle = False
__dispatcher__(".uno:Escape")
def showturtle():
turtle = __getshape__(__TURTLE__)
if turtle and not turtle.Visible:
if not turtle.Parent:
_.drawpage.add(turtle)
z = turtle.getPosition()
r, turtle.RotateAngle = turtle.RotateAngle, 0
turtle.PolyPolygon, turtle.RotateAngle = __TURTLESHAPE__[0], r
z = __Point__(z.X - turtle.BoundRect.Width / 2.0, z.Y - turtle.BoundRect.Height / 2.0)
turtle.setPosition(z)
__visible__(turtle, True)
pencolor(_.pencolor)
fillcolor(_.areacolor)
pensize(_.pensize/__PT_TO_TWIP__)
_.doc.CurrentController.select(__getshape__(__TURTLE__))
elif not turtle:
__initialize__()
def left(arg=None):
if __thread__:
return None
__initialize__()
turtle = uno.getComponentContext().ServiceManager.createInstance('com.sun.star.drawing.ShapeCollection')
turtle.add(__getshape__(__TURTLE__))
_.doc.CurrentController.select(turtle)
rotate(__TURTLE__, 1500)
return None
def right(arg=None):
if __thread__:
return None
__initialize__()
turtle = uno.getComponentContext().ServiceManager.createInstance('com.sun.star.drawing.ShapeCollection')
turtle.add(__getshape__(__TURTLE__))
_.doc.CurrentController.select(turtle)
rotate(__TURTLE__, -1500)
return None
def goforward(arg=None):
if __thread__:
return None
__initialize__()
turtle = uno.getComponentContext().ServiceManager.createInstance('com.sun.star.drawing.ShapeCollection')
turtle.add(__getshape__(__TURTLE__))
_.doc.CurrentController.select(turtle)
forward(10)
return None
def gobackward(arg=None):
if __thread__:
return None
__initialize__()
turtle = uno.getComponentContext().ServiceManager.createInstance('com.sun.star.drawing.ShapeCollection')
turtle.add(__getshape__(__TURTLE__))
_.doc.CurrentController.select(turtle)
backward(10)
return None
def commandline(arg=None, arg2=None):
run(arg, arg2)
def __setlang__():
global _
c = _.doc.CurrentController.getViewCursor()
locs = [i for i in [c.CharLocale, c.CharLocaleAsian, c.CharLocaleComplex] if i.Language != 'zxx'] # not None language
# FIXME-BCP47: this needs adaptation to language tags, a simple split on
# '-' and assuming second field would be country would already fail if
# a script tag was present.
loc = Locale(__uilocale__.split('-')[0], __uilocale__.split('-')[1], '')
if locs and loc not in locs:
loc = locs[0]
_.lng = loc.Language + '_' + loc.Country
if not __l12n__(_.lng):
_.lng = loc.Language
if not __l12n__(_.lng):
_.lng = "en_US"
def run(arg=None, arg2 = -1):
global _, __thread__, __halt__, _, __prevcode__, __prevlang__, __prevcompiledcode__
if __thread__:
return None
with __lock__:
__thread__ = 1
try:
__getdocument__()
_.origcursor = [None, None]
if arg2 == -1:
_.origcursor, _.cursor = __getcursor__(False), __getcursor__(True)[0]
__dispatcher__(".uno:Escape")
c = _.doc.Text.createTextCursor() # go to the first page
c.gotoStart(False)
_.doc.CurrentController.getViewCursor().gotoRange(c, False)
__initialize__()
__setlang__()
arg2 = _.cursor.getString()
if len(arg2) > 20000:
if MessageBox(_.doc.CurrentController.Frame.ContainerWindow, __l12n__(_.lng)['ERR_NOTAPROGRAM'], __l12n__(_.lng)['LIBRELOGO'], "querybox", __YES_NO_CANCEL__) != 2:
with __lock__:
__thread__ = None
return None
elif len(arg2) == 0 and _.origcursor[1]:
_.origcursor[0].setString("fontcolor 'green'\nlabel 'LIBRE'\npu\nback 30\npic [\n\tfc any\n\tcircle 40\n\tfontcolor 'black'\n\tlabel 'LOGO'\n\tleft 180\n\tfd 20\n\tpd\n\tpc any\n\tps 1\n\tfd 40\n\trepeat 20 [\n\t\tfd repcount*2\n\t\trt 90\n\t]\n]\npu pos any pd")
__translate__()
_.origcursor, _.cursor = __getcursor__(False), __getcursor__(True)[0]
arg2 = _.cursor.getString()
else:
__initialize__()
__setlang__()
if __prevcode__ and __prevcode__ == arg2 and __prevlang__ == _.lng:
__thread__ = LogoProgram(__prevcompiledcode__)
else:
__prevcode__ = arg2
__prevlang__ = _.lng
__prevcompiledcode__ = __compil__(arg2)
__thread__ = LogoProgram(__prevcompiledcode__)
__halt__ = False
turtle = uno.getComponentContext().ServiceManager.createInstance('com.sun.star.drawing.ShapeCollection')
turtle.add(__getshape__(__TURTLE__))
_.doc.CurrentController.select(turtle)
# set working directory for file operations
if _.doc.hasLocation():
os.chdir(unohelper.fileUrlToSystemPath(re.sub("[^/]*$", "", _.doc.getURL())))
else:
os.chdir(os.path.expanduser('~'))
__thread__.start()
except Exception:
__thread__ = None
__trace__()
return None
def __unlock__(all_levels):
while _.doc.hasControllersLocked():
# show turtle which was hidden by locking
if _.lockturtle:
showturtle()
_.doc.unlockControllers()
if not all_levels:
break
if not _.doc.hasControllersLocked() and _.lockturtle:
_.lockturtle = False
elif _.doc.hasControllersLocked() and _.lockturtle:
hideturtle()
def stop(arg=None):
global __halt__
with __lock__:
__halt__ = True
__unlock__(all_levels = True)
return None
def home(arg=None):
if __thread__:
return None
__getdocument__()
turtle = __getshape__(__TURTLE__)
if turtle:
__removeshape__(__TURTLE__)
_.drawpage.remove(turtle)
__initialize__()
__dispatcher__(".uno:Escape")
if not __halt__:
return None
_.pencolor = 0
_.pensize = __LINEWIDTH__
_.areacolor = __FILLCOLOR__
__removeshape__(__ACTUAL__)
def clearscreen(arg=None):
if __thread__:
return None
__getdocument__()
turtle = __getshape__(__TURTLE__)
if not turtle:
__initialize__()
if not __halt__:
# avoid unintentional image deletion in large documents
if len(__getcursor__(True)[0].getString()) < 5000:
__cs__(False)
return
__cs__(False)
__dispatcher__(".uno:Escape")
def __checkhalt__():
global __thread__, __halt__
if __halt__:
with __lock__:
__thread__ = None
sys.exit()
def __cs__(select = True):
turtle = __getshape__(__TURTLE__)
visible = False
if turtle and turtle.Visible:
__visible__(turtle, False)
visible = True
if _.doc.CurrentController.select(_.drawpage) and \
_.doc.CurrentController.getSelection().ImplementationName == "com.sun.star.drawing.SvxShapeCollection":
__dispatcher__(".uno:Delete")
if turtle and visible:
__visible__(turtle, True)
if select:
_.doc.CurrentController.select(_.drawpage)
def __dispatcher__(s, properties = (), doc = 0):
ctx = XSCRIPTCONTEXT.getComponentContext() # noqa: F821
d = ctx.ServiceManager.createInstanceWithContext("com.sun.star.frame.DispatchHelper", ctx)
if doc != 0:
d.executeDispatch(doc.CurrentController.Frame, s, "", 0, properties)
else:
d.executeDispatch(_.doc.CurrentController.Frame, s, "", 0, properties)
def __getshape__(shapename):
try:
if _.shapecache[shapename].Parent:
return _.shapecache[shapename]
_.shapecache.pop(shapename)
except Exception:
pass
return None
def __angle__(deg):
if deg == u'any':
return random.random() * 36000
return deg * 100
def turnleft(deg):
rotate(__TURTLE__, __angle__(deg))
def turnright(deg):
rotate(__TURTLE__, -__angle__(deg))
def heading(deg = -1, go = False):
turtle = __getshape__(__TURTLE__)
if deg == -1:
return -turtle.RotateAngle / 100 + 360
else:
if deg == u'any':
turtle.RotateAngle = random.random() * 36000
elif type(deg) is list:
pos = turtle.getPosition()
px, py = pos.X + turtle.BoundRect.Width / 2.0, pos.Y + turtle.BoundRect.Height / 2.0
dx = px * __MM10_TO_TWIP__ - deg[0] * __PT_TO_TWIP__
dy = deg[1] * __PT_TO_TWIP__ - py * __MM10_TO_TWIP__
n = hypot(dx, dy)
if dy > 0 and n > 0:
turtle.RotateAngle = a = -(180 + asin(dx / n) / (pi/180)) * 100 + 72000 # +720 for max(angle, preciseAngle) of __go__()
elif n > 0:
turtle.RotateAngle = a = asin(dx / n) / (pi/180) * 100 + 72000
if go and n > 0:
__go__(__TURTLE__, -n, False, a)
else:
turtle.RotateAngle = -deg * 100
def rotate(shapename, deg):
shape = __getshape__(shapename)
if shape:
shape.RotateAngle = shape.RotateAngle + deg
def forward(n):
if type(n) is list:
pos = position()
angle = heading()
dx = n[1] * sin((pi/180) * angle) + n[0] * sin((pi/180)*(angle + 90))
dy = n[1] * cos((pi/180) * angle) + n[0] * cos((pi/180)*(angle + 90))
position([pos[0] + dx, pos[1] - dy])
elif type(n) is str:
siz = label([1, 1, n])
pos = position()
angle = heading()
w, _ = siz.Width / (__PT_TO_TWIP__ / __MM10_TO_TWIP__), siz.Height / (__PT_TO_TWIP__ / __MM10_TO_TWIP__)
dx = 0 * sin((pi/180) * (angle)) + w * sin((pi/180)*(angle + 90))
dy = 0 * cos((pi/180) * (angle)) + w * cos((pi/180)*(angle + 90))
position([pos[0] + dx, pos[1] - dy])
heading(angle)
else:
__go__(__TURTLE__, -n * __PT_TO_TWIP__)
def backward(n):
if type(n) is list:
forward([-n[0], -n[1]])
turnright(180)
else:
__go__(__TURTLE__, n * __PT_TO_TWIP__)
def __dots__(n, pos, dx, dy, r = -1, q = 0): # dots for dotted polyline or circle
f = [1, 4, 4, 4, 4][q]
k = abs(int(1.0 * n / max(20, _.pensize) / 2.0 / f))
dots = []
px, py = pos.X, pos.Y
for i in range(k + 1):
if k > 0:
if r != -1:
px, py = pos.X + sin(((f-1)*(q-1)*30 + 360.0/f/k * i) * pi/180.0) * r[0], pos.Y + cos(((f-1)*(q-1)*30 + 360.0/f/k * i) * pi/180) * r[1]
else:
px, py = pos.X + round(i * dx/k), pos.Y + round(i * dy/k)
dots += [(__Point__(px, py), __Point__(px + 7, py + 7))]
return dots
def __draw__(d, count = True):
shape = _.doc.createInstance( "com.sun.star.drawing." + d)
shape.AnchorType = __AT_PAGE__
shape.TextWrap = __THROUGH__
__visible__(shape, False)
while __zoom__(): # temporary fix program halt with continuous zoom
while __zoom__():
__time__.sleep(0.2)
__time__.sleep(0.2)
_.drawpage.add(shape)
if __group__ != 0:
__group__.add(shape)
if count:
_.shapecache[next(_.shapecount)] = str(_.time)
return shape
def __zoom__():
z = _.doc.CurrentController.getViewSettings().ZoomValue
if z != _.zoomvalue:
_.zoomvalue = z
return True
return False
def __lefthang__(shape):
global __grouplefthang__
if __group__ != 0:
p = shape.getPosition()
if p.X < __grouplefthang__:
__grouplefthang__ = p.X
def __go__(shapename, n, dot = False, preciseAngle = -1):
turtle = __getshape__(shapename)
turtlepos = None
if shapename == __TURTLE__:
try:
turtlepos = turtle.PolyPolygon[-1][-1]
except Exception:
pass
pos = turtle.getPosition()
dx = n * sin((pi/180)*(max(turtle.RotateAngle, preciseAngle)/100))
dy = n * cos((pi/180)*(max(turtle.RotateAngle, preciseAngle)/100))
turtle.setPosition(__Point__(pos.X + dx / __MM10_TO_TWIP__, pos.Y + dy / __MM10_TO_TWIP__))
if (_.pencolor != _.oldlc or _.pensize != _.oldlw or _.linestyle != _.oldls or _.linejoint != _.oldlj or _.linecap != _.oldlcap):
__removeshape__(__ACTUAL__)
shape = None
else:
shape = __getshape__(__ACTUAL__)
_.oldlw = _.pensize
_.oldlc = _.pencolor
_.oldls = _.linestyle
_.oldlj = _.linejoint
_.oldlcap = _.linecap
if shape and not _.pen and not dot:
_.continuous = False
return
c, c2 = __Point__(pos.X + turtle.BoundRect.Width / 2.0, pos.Y + turtle.BoundRect.Height / 2.0), __Point__(round(dx / __MM10_TO_TWIP__), round(dy / __MM10_TO_TWIP__))
if shape and "LineShape" in shape.ShapeType:
if _.continuous or dot:
last = shape.PolyPolygon[-1][-1]
if not (turtlepos and (abs(last.X - turtlepos.X) > 100 or abs(last.Y - turtlepos.Y) > 100) and
(__group__ == 0 or (shape.getPosition().X > 0 and turtle.getPosition().X > 0))): # picture [ ] keeps hanging shapes
if dot or _.linestyle == __LineStyle_DOTTED__:
shape.PolyPolygon = tuple( list(shape.PolyPolygon) + __dots__(n, turtlepos, dx, dy))
else:
last.X = last.X + c2.X
last.Y = last.Y + c2.Y
shape.PolyPolygon = tuple( list(shape.PolyPolygon[:-1]) + [tuple( list(shape.PolyPolygon[-1]) + [last])])
__lefthang__(shape)
return
elif turtlepos:
shape.PolyPolygon = tuple( list(shape.PolyPolygon) + [(turtlepos, __Point__(turtlepos.X + c2.X, turtlepos.Y + c2.Y))])
_.continuous = True
__lefthang__(shape)
return
if not _.pen and not dot:
return
if _.pen and not dot:
_.points = [] # new line drawing: forget the points
shape = __draw__("PolyLineShape")
shape.RotateAngle = 0
shape.PolyPolygon = tuple([tuple([__Point__(0, 0)])])
shape.setPosition(c)
last = shape.PolyPolygon[-1][-1]
last2 = __Point__(last.X + c2.X, last.Y + c2.Y)
shape.LineStyle, shape.LineDash = __linestyle__(_.linestyle)
shape.LineJoint = _.linejoint
shape.LineCap = _.linecap
if dot or _.linestyle == __LineStyle_DOTTED__:
shape.PolyPolygon = tuple( list(shape.PolyPolygon) + __dots__(n, last, c2.X, c2.Y))
shape.LineStart = __bezierdot__
shape.LineStartCenter = True
shape.LineStartWidth = max(20, _.pensize) / __MM10_TO_TWIP__
shape.LineWidth = 0
else:
shape.PolyPolygon = tuple([tuple( list(shape.PolyPolygon[-1]) + [last2])])
shape.LineWidth = _.pensize / __MM10_TO_TWIP__
shape.LineColor, shape.LineTransparence = __splitcolor__(_.pencolor)
if shape.LineTransparence == 100:
shape.LineStyle = 0
__visible__(shape, True)
shape.Name = __ACTUAL__
_.shapecache[__ACTUAL__] = shape
_.oldlw = _.pensize
_.oldlc = _.pencolor
_.oldls = _.linestyle
_.oldlj = _.linejoint
_.oldlcap = _.linecap
_.continuous = True
__lefthang__(shape)
def __fillit__(filled = True):
oldshape = __getshape__(__ACTUAL__)
if (oldshape and oldshape.LineStartCenter) or _.points:
if oldshape:
__removeshape__(__ACTUAL__) # FIXME close dotted polyline
if _.points:
p = position()
h = heading()
for i in _.points:
position(i)
__pen__(1)
__checkhalt__()
_.points = []
__fillit__(filled)
__pen__(0)
position(p)
heading(h)
return
if oldshape and "LineShape" in oldshape.ShapeType:
shape = __draw__("PolyPolygonShape", False)
shape.PolyPolygon = oldshape.PolyPolygon
shape.setPosition(oldshape.getPosition())
shape.LineStyle, shape.LineDash = __linestyle__(_.linestyle)
shape.LineJoint = _.linejoint
shape.LineCap = _.linecap
shape.LineWidth = _.pensize / __MM10_TO_TWIP__
shape.LineColor, shape.LineTransparence = __splitcolor__(_.pencolor)
shape.FillColor, shape.FillTransparence = __splitcolor__(_.areacolor, shape)
if _.hatch:
shape.FillBackground = True if shape.FillTransparence != 100 else False
shape.FillHatch = _.hatch
shape.FillStyle = 3
elif type(_.areacolor) is not tuple:
shape.FillStyle = int(filled)
if shape.LineTransparence == 100:
shape.LineStyle = 0
if shape.FillTransparence == 100:
shape.FillTransparence = 0 # for hatching and better modifications on UI
if not _.hatch:
shape.FillStyle = 0
shape.setString(oldshape.getString())
oldshape.Name = ""
shape.Name = __ACTUAL__
_.shapecache[__ACTUAL__] = shape
if __group__ != 0:
__group__.remove(oldshape)
__visible__(shape, True)
_.drawpage.remove(oldshape)
elif oldshape and "PolyPolygon" in oldshape.ShapeType:
oldshape.LineStyle = int(_.pen)
oldshape.LineJoint = _.linejoint
oldshape.LineCap = _.linecap
if _.hatch:
oldshape.FillBackground = True
oldshape.FillHatch = _.hatch
oldshape.FillStyle = 3
else:
oldshape.FillStyle = int(filled)
oldshape.LineWidth = _.pensize / __MM10_TO_TWIP__
oldshape.LineColor, oldshape.LineTransparence = __splitcolor__(_.pencolor)
oldshape.FillColor, oldshape.FillTransparence = __splitcolor__(_.areacolor, oldshape)
def point():
oldpen, _.pen = _.pen, 1
oldstyle, _.linestyle = _.linestyle, __LineStyle_DOTTED__
__go__(__TURTLE__, 0, True)
_.pen, _.linestyle = oldpen, oldstyle
_.points.append(position())
def __boxshape__(shapetype, l):
turtle = __getshape__(__TURTLE__)
shape = __draw__(shapetype + "Shape")
pos = turtle.getPosition()
pos.X = pos.X - (l[0] * __PT_TO_TWIP__ / __MM10_TO_TWIP__ / 2) + turtle.BoundRect.Width / 2.0
pos.Y = pos.Y - (l[1] * __PT_TO_TWIP__ / __MM10_TO_TWIP__ / 2) + turtle.BoundRect.Height / 2.0
shape.setPosition(pos)
shape.setSize(__Size__(l[0] * __PT_TO_TWIP__ / __MM10_TO_TWIP__, l[1] * __PT_TO_TWIP__ / __MM10_TO_TWIP__))
shape.LineStyle, shape.LineDash = __linestyle__(_.linestyle)
shape.LineWidth = _.pensize / __MM10_TO_TWIP__
shape.LineJoint = _.linejoint
shape.LineCap = _.linecap
shape.LineColor, shape.LineTransparence = __splitcolor__(_.pencolor)
shape.FillColor, shape.FillTransparence = __splitcolor__(_.areacolor, shape, turtle.RotateAngle)
if _.hatch:
shape.FillBackground = True if shape.FillTransparence != 100 else False
shape.FillHatch = _.hatch
shape.FillStyle = 3
elif type(_.areacolor) is not tuple:
shape.FillStyle = 1
if shape.LineTransparence == 100:
shape.LineStyle = 0
if shape.FillTransparence == 100:
shape.FillTransparence = 0 # for hatching and better modifications on UI
if not _.hatch:
shape.FillStyle = 0
shape.RotateAngle = turtle.RotateAngle
if shapetype == "Rectangle" and len(l) > 2:
shape.CornerRadius = (l[2] * __PT_TO_TWIP__) / __MM10_TO_TWIP__
elif shapetype == "Ellipse" and len(l) > 2:
oldBoundRect = shape.BoundRect
try:
shape.CircleKind = __SECTION__
shape.CircleStartAngle = (-l[3] - 270) * 100
shape.CircleEndAngle = (-l[2] - 270) * 100
shape.CircleKind = [__FULL__, __SECTION__, __CUT__, __ARC__][l[4]]
except Exception:
pass
pos.X = pos.X + shape.BoundRect.X - oldBoundRect.X
pos.Y = pos.Y + shape.BoundRect.Y - oldBoundRect.Y
shape.setPosition(pos)
__visible__(shape, True)
__removeshape__(__ACTUAL__)
_.shapecache[__ACTUAL__] = shape
__lefthang__(shape)
def ellipse(l):
if type(l) is not type([]): # default for circle and square
l = [l, l]
if _.linestyle == __LineStyle_DOTTED__:
__groupstart__()
_.linestyle = __LineStyle_SOLID__
pc, _.pencolor = _.pencolor, 0xff000000
ellipse(l)
_.pencolor, _.linestyle = pc, __LineStyle_DOTTED__
point()
shape = __getshape__(__ACTUAL__)
shape.PolyPolygon = tuple(__dots__(max(l[0], l[1]) * pi * __PT_TO_TWIP__, shape.PolyPolygon[0][0], 0, 0, [i/2.0 * __PT_TO_TWIP__ for i in l]))
turtle = __getshape__(__TURTLE__)
shape.RotateAngle = turtle.RotateAngle
__groupend__()
else:
__boxshape__("Ellipse", l)
def rectangle(l):
if type(l) is not type([]): # default for circle and square
l = [l, l]
if _.linestyle == __LineStyle_DOTTED__:
__groupstart__()
_.linestyle = __LineStyle_SOLID__
pc, _.pencolor = _.pencolor, 0xff000000
rectangle(l)
_.pencolor, _.linestyle = pc, __LineStyle_DOTTED__
point()
shape = __getshape__(__ACTUAL__)
if type(l) is not type([]):
l = [l, l]
if len(l) == 2:
l = l + [0]
l = [i * __PT_TO_TWIP__ for i in l]
c = shape.PolyPolygon[0][0]
k = [min(l[0] / 2.0, l[2]), min(l[1] / 2.0, l[2])]
p = __dots__(l[0] - 2 * k[0], __Point__(c.X - l[0]/2 + k[0], c.Y - l[1]/2), l[0] - 2 * k[0], 0)
p = p[:-1] + __dots__(l[1] - 2 * k[1], __Point__(c.X + l[0]/2, c.Y - l[1]/2 + k[1]), 0, l[1] - 2 * k[1])
p = p[:-1] + __dots__(l[0] - 2 * k[0], __Point__(c.X + l[0]/2 - k[0], c.Y + l[1]/2), -l[0] + 2 * k[0], 0)
p = p[:-1] + __dots__(l[1] - 2 * k[1], __Point__(c.X - l[0]/2, c.Y + l[1]/2 - k[1]), 0, -l[1] + 2 * k[1])
if l[2] > 0:
p = p + __dots__(max(k) * 2 * pi, __Point__(c.X - l[0]/2 + k[0], c.Y - l[1]/2 + k[1]), 0, 0, k, 3)[1:]
p = p + __dots__(max(k) * 2 * pi, __Point__(c.X + l[0]/2 - k[0], c.Y - l[1]/2 + k[1]), 0, 0, k, 2)[1:]
p = p + __dots__(max(k) * 2 * pi, __Point__(c.X + l[0]/2 - k[0], c.Y + l[1]/2 - k[1]), 0, 0, k, 1)[1:]
p = p + __dots__(max(k) * 2 * pi, __Point__(c.X - l[0]/2 + k[0], c.Y + l[1]/2 - k[1]), 0, 0, k, 4)[1:]
shape.PolyPolygon = tuple(p)
turtle = __getshape__(__TURTLE__)
shape.RotateAngle = turtle.RotateAngle
__groupend__()
else:
__boxshape__("Rectangle", l)
def label(st):
if type(st) is not type([]):
st = [0, 0, st]
# get text size
shape = _.doc.createInstance( "com.sun.star.drawing.TextShape")
shape.TextAutoGrowWidth = True
shape.Visible = False
actual = __getshape__(__ACTUAL__)
_.drawpage.add(shape)
text(shape, st[2])
z = shape.getSize()
# show text using RectangleShape (for correct SVG export)
ac, pc = _.areacolor, _.pencolor
_.areacolor, _.pencolor = 0xff000000, 0xff000000 # invisible
rectangle([z.Width / (__PT_TO_TWIP__ / __MM10_TO_TWIP__), z.Height / (__PT_TO_TWIP__ / __MM10_TO_TWIP__)])
_.drawpage.remove(shape)
_.pencolor, _.areacolor = pc, ac
lab = __getshape__(__ACTUAL__)
text(lab, st[2])
if st[0] != 0 or st[1] != 0:
pos = position()
angle = heading()
n = [st[0] * z.Width/2, st[1] * z.Height/2]
dx = n[1] * sin((pi/180) * angle) + n[0] * sin((pi/180)*(angle + 90))
dy = n[1] * cos((pi/180) * angle) + n[0] * cos((pi/180)*(angle + 90))
lab.setPosition(__Point__(round(pos[0] * __PT_TO_TWIP__ / __MM10_TO_TWIP__ + dx - lab.BoundRect.Width/2), round(pos[1] * __PT_TO_TWIP__ / __MM10_TO_TWIP__ - dy - lab.BoundRect.Height/2)))
_.shapecache[__ACTUAL__] = actual
return z
def __get_HTML_format__(orig_st):
"Process HTML-like tags, and return with text and formatting vector"
st = orig_st.replace('<', '\uE000')
if not ('<' in st and '>' in st):
return st.replace('\uE000', '<'), None, None
# convert localized bold, and italic values to <B> and <I> tags
for i in ('BOLD', 'ITALIC'):
st = re.sub(r'(</?)(' + __l12n__(_.lng)[i] + r')>', r'\1%s>' % i[0], st, flags=re.I)
for i in ('FONTCOLOR', 'FILLCOLOR', 'FONTFAMILY', 'FONTHEIGHT'):
st = re.sub(r'<(' + __l12n__(_.lng)[i] + r')( *[^<> ][^<>]*)>', r'<%s\2>' % i.lower(), st, flags=re.I)
st = re.sub(r'</(' + __l12n__(_.lng)[i] + r')>', r'</%s>' % i.lower(), st, flags=re.I)
# expand localized color names
if _.lng not in __match_localized_colors__:
__match_localized_colors__[_.lng] = re.compile(r'<(/?)(' + '|'.join(__colors__[_.lng].keys()) + ')>', re.IGNORECASE)
# replacement lambda function: if it's an opening tag, return with the argument, too
get_fontcolor_tag = lambda m: "<fontcolor %s>" % m.group(2) if len(m.group(1)) == 0 else "</fontcolor>"
st = re.sub(__match_localized_colors__[_.lng], get_fontcolor_tag, st)
# expand abbreviated forms of font features
# <smcp>small caps</smcp> -> <fontfeature smcp>small caps</fontfeature smcp>
st = re.sub(__match_fontfeatures__, r'\1fontfeature \2\3', st)
tex = "" # characters without HTML tags
pat = [] # bit vectors of the previous characters
extra_pat = [] # extra data of the previous characters
# 0th bit: bold
# 1st bit: italic
# 2nd bit: underline
# 3rd bit: strikethrough
# 4th bit: superscript
# 5th bit: subscript
# 6th bit: color
# 7th bit: background color
# 8th bit: font family
# 9th bit: font feature (Graphite or OpenType)
# 10th bit: font size
f = 0
# store embedding level of the same element to disable it
# only at the most outer closing tag, e.g. <i>a <i>double</i> italic here, too</i>
# bit_level = {0: 0, ..., 10: 0}
bit_level = { i: 0 for i in range(11) }
extra_data = {}
i = 0
while i < len(st):
is_tag = False
if st[i] == '<':
for j in range(len(__match_tags__)):
m = __match_tags__[j].match(st[i:])
if m:
tag = ""
bit = j // 2
if bit > 5:
tag = m.group(1).lower()
# opening tag
if j % 2 == 0:
f |= (1 << bit)
bit_level[bit] += 1
# extra data (color bit and over)
if bit > 5:
if tag in extra_data:
extra_data[tag] = extra_data[tag] + [m.group(2)]
else:
extra_data[tag] = [m.group(2)]
else:
if bit_level[bit] > 0:
bit_level[bit] -= 1
if bit_level[bit] == 0:
f &= ~(1 << bit)
# extra data for font feature
# fontfeature has a special closing tag, remove that from the extra_data
# (allowing to use overlapping elements)
if bit > 5 and (tag in extra_data):
if bit == 9 and len(m.group(2)) > 0:
# create a new list to keep the extra data of the previous characters,
# and remove the last occurance of the feature
z = list(extra_data[tag])
for j in reversed(range(len(z))):
if z[j].startswith(m.group(2)):
z.pop(j)
extra_data[tag] = z
break
# extra data
else:
extra_data[tag] = extra_data[tag][:-1]
i += len(m.group(0)) - 1
is_tag = True
break
if not is_tag:
tex = tex + st[i]
pat.append(f)
extra_pat.append(dict(extra_data))
i += 1
# no tags
if len(st) == len(tex):
pat = None
extra_pat = None
return tex.replace('\uE000', '<'), pat, extra_pat
def text(shape, orig_st):
if shape:
_.doc.lockControllers()
# analyse HTML
st, formatting, extra_data = __get_HTML_format__(orig_st)
shape.setString(__string__(st, _.decimal))
c = shape.createTextCursor()
c.gotoStart(False)
c.gotoEnd(True)
c.CharColor, c.CharTransparence = __splitcolor__(_.textcolor)
c.CharHeight = _.fontheight
c.CharWeight = __fontweight__(_.fontweight)
c.CharPosture = __fontstyle__(_.fontstyle)
c.CharFontName = _.fontfamily
# has HTML-like formatting
if formatting is not None:
_.fixSVG = True
prev_format = 0
prev_extra_data = extra_data[0]
c.collapseToStart()
n = 0 # length of the previous text span
formatting.append(0) # add terminating 0 to process last span
for i in formatting:
if i != prev_format or (len(extra_data) > 0 and extra_data[0] != prev_extra_data):
do_formatting = prev_format != 0
c.goRight(n, do_formatting) # move cursor with optional selection
if do_formatting:
if prev_format & (1 << 0):
c.CharWeight = 150
if prev_format & (1 << 1):
c.CharPosture = __Slant_ITALIC__
if prev_format & (1 << 2):
c.CharUnderline = __Underline_SINGLE__
if prev_format & (1 << 3):
c.CharStrikeout = __Strikeout_SINGLE__
if prev_format & (1 << 4):
c.CharEscapement = 14000 # magic number for default superscript, see DFLT_ESC_AUTO_SUPER
c.CharEscapementHeight = 58
if prev_format & (1 << 5):
c.CharEscapement = -14000 # magic number for default subscript, see DFLT_ESC_AUTO_SUB
c.CharEscapementHeight = 58
if prev_format & (1 << 6):
c.CharColor, c.CharTransparence = __splitcolor__(__color__(prev_extra_data['fontcolor'][-1]))
if prev_format & (1 << 7):
c.CharBackColor = __color__(prev_extra_data['fillcolor'][-1])
if prev_format & (1 << 8):
c.CharFontName = prev_extra_data['fontfamily'][-1]
if prev_format & (1 << 9):
# font features uses the following syntax: font_name:feat1&feat2&feat3=value&etc.
if ":" in c.CharFontName:
c.CharFontName = c.CharFontName + "&" + "&".join(prev_extra_data['fontfeature'])
else:
c.CharFontName = c.CharFontName + ":" + "&".join(prev_extra_data['fontfeature'])
if prev_format & (1 << 10):
c.CharHeight = prev_extra_data['fontheight'][-1]
c.collapseToEnd()
n = 0
n += 1
prev_format = i
if len(extra_data) > 0:
prev_extra_data = extra_data.pop(0)
_.doc.unlockControllers()
def sleep(t):
# lock shape repaint, if SLEEP argument is negative
if t < 0:
_.doc.lockControllers()
# hide turtle during locking
turtle = __getshape__(__TURTLE__)
if turtle and turtle.Visible:
hideturtle()
_.lockturtle = True
return
else:
# otherwise unlock one level
__unlock__(all_levels = False)
_.time = _.time + t
__removeshape__(__ACTUAL__)
for i in range(int(t/__SLEEP_SLICE_IN_MILLISECONDS__)):
__checkhalt__()
__time__.sleep(0.5)
__checkhalt__()
__time__.sleep(t%__SLEEP_SLICE_IN_MILLISECONDS__/1000.0)
def __removeshape__(shapename):
try:
_.shapecache.pop(shapename).Name = ""
except Exception:
pass
def __fontweight__(w):
if type(w) is int:
return w
elif re.match(__l12n__(_.lng)['BOLD'], w, flags = re.I):
return 150
elif re.match(__l12n__(_.lng)['NORMAL'], w, flags = re.I):
return 100
return 100
def __fontstyle__(w):
if type(w) is int:
return w
elif re.match(__l12n__(_.lng)['ITALIC'], w, flags = re.I):
return __Slant_ITALIC__
elif re.match(__l12n__(_.lng)['UPRIGHT'], w, flags = re.I):
return __Slant_NONE__
return __Slant_NONE__
def __color__(c):
if type(c) in [int, float]:
return c
if type(c) is str:
if c == u'any':
rc, rv, rgray = __NORMCOLORS__[int(random.random()*7)], random.random(), random.random() ** 0.5
ratio = 1.0*abs(rc[2])/(abs(rc[2]) + abs(rc[4]))
newcol = list(rc[0])
if rv < ratio:
newcol[rc[1]] += rc[2] * rv/ratio
else:
newcol[rc[3]] += rc[4] * (rv - ratio)/(1 - ratio)
# random grayness
rdark = 1 - 2**4 * (random.random()-0.5)**4
for i in range(0, 3):
newcol[i] = 255 * (rgray + (newcol[i]/255.0 - rgray) * rdark)
return __color__(newcol)
elif c[0:1] == '~':
c = __componentcolor__(__colors__[_.lng][c[1:].lower()])
for i in range(3):
c[i] = max(min(c[i] + int(random.random() * 64) - 32, 255), 0)
return __color__(c)
elif c[0].isdigit():
return int(c, 0) # recognize hex and decimal numbers as strings
return __colors__[_.lng][c.lower()]
if type(c) is list:
if len(c) == 1: # color index
return __COLORS__[int(c[0])][1]
elif len(c) == 3: # RGB
return (int(c[0])%256 << 16) + (int(c[1])%256 << 8) + int(c[2])%256
elif len(c) == 2 or len(c) > 4: # gradient
return (__color__(c[0]), __color__(c[1])) + tuple(c[2:])
return (int(c[3])%256 << 24) + (int(c[0])%256 << 16) + (int(c[1])%256 << 8) + int(c[2])%256 # RGB + alpha
def __linestyle__(s):
if _.pen == 0:
return 0, __LineDash__()
if _.linestyle == __LineStyle_DASHED__:
return _.linestyle, __LineDash__(__DashStyle_RECT__, 0, 0, 1, 100, 100)
elif _.linestyle == __LineStyle_DOTTED__:
return __LineStyle_DASHED__, __LineDash__(__DashStyle_RECT__, 1, 1, 0, 0, 100000)
elif type(s) is list:
return __LineStyle_DASHED__, __LineDash__((s[5:6] or [0])[0], s[0], s[1] * __PT_TO_TWIP__, s[2], s[3] * __PT_TO_TWIP__, s[4] * __PT_TO_TWIP__)
return s, __LineDash__()
def fillstyle(s):
if type(s) is list:
color, null = __splitcolor__(__color__(s[1]))
_.hatch = __Hatch__(s[0] - 1, color, s[2] * __PT_TO_TWIP__, s[3] * 10)
elif s == 0:
_.hatch = None
elif s <= 10: # using hatching styles of Writer
fillstyle([[1, 0, 5, 0], [1, 0, 5, 45], [1, 0, 5, -45], [1, 0, 5, 90], [2, [127, 0, 0], 5, 45], [2, [127, 0, 0], 5, 0], [2, [0, 0, 127], 5, 45], [2, [0, 0, 127], 5, 0], [3, [0, 0, 127], 5, 0], [1, 0, 25, 45]][s-1])
def __splitcolor__(c, shape = None, angle = None):
if shape and (type(c) is tuple or type(_.t10y) is list):
angle = heading() if angle is None else -angle / 100 + 360
if type(c) is tuple:
shape.FillStyle = __FillStyle_GRADIENT__
# gradient color: [color1, color2, style, angle(must be positive for I/O), border, x_percent, y_percent, color1_intensity_percent, color2_intensity_percent]
d, d[0:len(c)], c = [0, 0, __GradientStyle_LINEAR__, 0, 0, 0, 0, 100, 100], c, c[0]
shape.FillGradient = __Gradient__(d[2], d[0], d[1], (-angle + d[3]) * 10 % 3600, d[4], d[5], d[6], d[7], d[8], 0)
if type(_.t10y) is list: # transparency gradient: [begin_percent, end_percent, style, angle, border, x_percent, y_percent]
table = _.doc.createInstance("com.sun.star.drawing.TransparencyGradientTable")
if not table.hasByName(str(_.t10y) + str(angle)):
t, t[0:len(_.t10y)] = [100, __GradientStyle_LINEAR__, 0, 0, 0, 0, 0], _.t10y
table.insertByName(str(_.t10y) + str(angle), __Gradient__(t[2], t[0] * 0xffffff / 100.0, t[1] * 0xffffff / 100.0, (-angle + t[3]) * 10 % 3600, t[4], t[5], t[6], 100, 100, 0))
shape.FillTransparenceGradientName = str(_.t10y) + str(angle)
c = 0 if type(c) is tuple else c & 0xffffff
else:
shape.FillStyle = __FillStyle_GRADIENT__
c = int(_.t10y * 255.0/100) << 24
"""Split color constants to RGB (3-byte) + transparency (%)"""
return int(c) & 0xffffff, (int(c) >> 24) / (255.0/100)
def __componentcolor__(c):
a = [ (c & 0xff0000) >> 16, (c & 0xff00) >> 8, c & 0xff ]
if c > 2**24:
a.append((c & 0xff000000) >> 24)
return a
def pencolor(n = -1):
if n != -1:
_.pencolor = __color__(n)
turtle = __getshape__(__TURTLE__)
if turtle and __visible__(turtle):
turtle.LineColor, turtle.LineTransparence = __splitcolor__(_.pencolor)
else:
return __componentcolor__(_.pencolor)
def pensize(n = -1):
if n != -1:
if n == 'any':
_.pensize = random.random() * 10 * __PT_TO_TWIP__
else:
_.pensize = n * __PT_TO_TWIP__
turtle = __getshape__(__TURTLE__)
if turtle and __visible__(turtle):
turtle.LineWidth = min(_.pensize, (1 + _.pen * 2) * __PT_TO_TWIP__) / __MM10_TO_TWIP__
return _.pensize / __PT_TO_TWIP__
def penstyle(n = -1):
if n == -1:
try:
return __locname__(_.linestyle.value)
except Exception:
return __locname__('DOTTED')
if type(n) is list and len(n) >= 5:
_.linestyle = n
elif re.match(__l12n__(_.lng)['SOLID'], n, flags = re.I):
_.linestyle = __LineStyle_SOLID__
elif re.match(__l12n__(_.lng)['DASH'], n, flags = re.I):
_.linestyle = __LineStyle_DASHED__
elif re.match(__l12n__(_.lng)['DOTTED'], n, flags = re.I):
_.linestyle = __LineStyle_DOTTED__
def penjoint(n = -1):
if n == -1:
return __locname__(_.linejoint.value)
if re.match(__l12n__(_.lng)['NONE'], n, flags = re.I):
_.linejoint = __Joint_NONE__
elif re.match(__l12n__(_.lng)['BEVEL'], n, flags = re.I):
_.linejoint = __BEVEL__
elif re.match(__l12n__(_.lng)['MITER'], n, flags = re.I):
_.linejoint = __MITER__
elif re.match(__l12n__(_.lng)['ROUNDED'], n, flags = re.I):
_.linejoint = __ROUNDED__
def pencap(n = -1):
if n == -1:
return __locname__(_.linecap.value.replace('BUTT', 'NONE'))
if re.match(__l12n__(_.lng)['NONE'], n, flags = re.I):
_.linecap = __Cap_NONE__
elif re.match(__l12n__(_.lng)['ROUNDED'], n, flags = re.I):
_.linecap = __Cap_ROUND__
elif re.match(__l12n__(_.lng)['SQUARE'], n, flags = re.I):
_.linecap = __Cap_SQUARE__
def fillcolor(n = -1):
if n != -1:
_.areacolor = __color__(n)
if type(_.areacolor) is not tuple:
_.t10y = (int(_.areacolor) >> 24) / (255.0/100)
else:
_.t10y = 0
turtle = __getshape__(__TURTLE__)
if turtle and __visible__(turtle):
turtle.FillColor, transparence = __splitcolor__(_.areacolor, turtle)
turtle.FillTransparence = min(95, transparence)
else:
return __componentcolor__(_.areacolor)
def filltransparency(n = -1):
if n != -1:
if n == u'any':
n = 100 * random.random()
if type(n) is not list:
if type(_.areacolor) is not tuple:
fillcolor((_.areacolor & 0xffffff) + (int(n * (255.0/100)) << 24))
else:
_.t10y = n
else:
_.t10y = n
else:
return _.t10y
def pentransparency(n = -1):
if n != -1:
if n == u'any':
n = 100 * random.random()
pencolor((_.pencolor & 0xffffff) + (int(n * (255.0/100)) << 24))
else:
return _.pencolor >> 24
def fontcolor(n = -1):
if n != -1:
_.textcolor = __color__(n)
else:
return __componentcolor__(_.textcolor)
def fonttransparency(n = -1):
if n != -1:
if n == u'any':
n = 100 * random.random()
fontcolor((_.textcolor & 0xffffff) + (int(n * (255.0/100)) << 24))
else:
return _.textcolor >> 24
def position(n = -1):
turtle = __getshape__(__TURTLE__)
if turtle:
if n != -1:
if n == 'any':
ps = pagesize()
heading([random.random() * ps[0], random.random() * ps[1]], True)
else:
heading(n, True)
else:
pos = turtle.getPosition()
pos.X, pos.Y = pos.X + turtle.BoundRect.Width / 2.0, pos.Y + turtle.BoundRect.Height / 2.0
return [ pos.X * __MM10_TO_TWIP__ / __PT_TO_TWIP__, pos.Y * __MM10_TO_TWIP__ / __PT_TO_TWIP__ ]
def __groupstart__(name = ""):
global __group__, __grouplefthang__, __groupstack__
__removeshape__(__ACTUAL__)
__groupstack__.append(__group__)
if name != "": # store pic name (for correct repcount)
__groupstack__.append(name)
if ".SVG" == name[-4:].upper():
_.time = 0
_.shapecount = itertools.count()
__groupstack__.append(__grouplefthang__)
__group__ = uno.getComponentContext().ServiceManager.createInstance('com.sun.star.drawing.ShapeCollection')
__grouplefthang__ = 0
def create_svg_animation(m):
global _
id = int(m.group(1))
if id - 3 in _.shapecache:
t = _.shapecache[id-3]
opacity = "100" if t == "0" else "0"
name = "" if id != 3 else "id=\"first\""
start = "%sms;last.end+%sms" % (t, t) if id == 3 else "first.end+%dms" % (int(t) - int(_.shapecache[0]))
return '<g id="id%s" opacity="0"><animate %s attributeName="opacity" from="100" to="100" begin="%s" dur="1ms" fill="freeze"/><animate attributeName="opacity" from="100" to="%s" begin="last.end" dur="1ms" fill="freeze"/>' % (m.group(1), name, start, opacity)
return m.group()
def create_valid_svg_file(filename):
with open(filename, "r") as f:
s = f.read()
s = re.sub(r'(?s)(<g\sid="[^"]*)\(([^"]*)\)', '\\1\\2', s) # bad "(", ")" in xml:id
s = re.sub('(?s)<g\\sooo:[^>]*>', '', s) # remove non standard attributes
s = re.sub('(?s)<defs class="EmbeddedBulletChars">.*(?=<defs class="TextEmbeddedBitmaps")', '', s) # remove unused parts
s = re.sub('(?s)(<path stroke-width="[^"]*"[^<]*)stroke-width="[^"]*"', '\\1', s) # double stroke-width
s = re.sub('(?s)<svg\\s+version="1.2"', '<svg version="1.1"', s) # for W3C Validator
if _.time > 0:
s = re.sub('<g id="id([0-9]+)">', create_svg_animation, s)
m = re.match('(?s)(.*<animate[^>]*first[.]end.([0-9]+)[^>]* dur=")1ms"', s)
lasttime = _.time - int(m.group(2)) - int(_.shapecache[0]) + 1
if lasttime > 1:
s = re.sub('(?s)(.*<animate[^>]*first[.]end.([0-9]+)[^>]* dur=")1ms"', m.group(1) + str(lasttime) + 'ms" id="last"', s)
with open(filename, 'w') as f:
f.write(s)
def __groupend__(name = ""):
global __group__, __grouplefthang__, __groupstack__, __halt__
g = 0
if __group__.getCount() > 1:
if __grouplefthang__ < 0:
for i in range(__group__.Count):
s = __group__.getByIndex(i)
p = s.getPosition()
p.X = p.X + -__grouplefthang__
s.setPosition(p)
g = _.drawpage.group(__group__)
p = g.getPosition()
p.X = p.X + __grouplefthang__
g.setPosition(p)
else:
g = _.drawpage.group(__group__)
g.TextWrap = __THROUGH__
elif __group__.getCount() == 1:
g = __group__.getByIndex(0)
__grouplefthang__ = min(__groupstack__.pop(), __grouplefthang__)
if name != "":
name = __groupstack__.pop()
if name and ".SVG" == name[-4:].upper() and g != 0:
_.doc.CurrentController.select(g)
__dispatcher__(".uno:Copy")
ctx = XSCRIPTCONTEXT.getComponentContext() # noqa: F821
d = ctx.ServiceManager.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)
draw = d.loadComponentFromURL("private:factory/sdraw", "_blank", 0, ())
drawpage = draw.getDrawPages().getByIndex(0)
while XSCRIPTCONTEXT.getDocument() != draw: # noqa: F821
if XSCRIPTCONTEXT.getDocument() not in [draw, _.doc, None]: # noqa: F821
__halt__ = True
return
__time__.sleep(0.1)
__dispatcher__(".uno:Paste", (), draw)
__dispatcher__(".uno:FormatGroup", (), draw)
# fix bad or non-portable SVG export by converting text to curve
if _.fixSVG:
__dispatcher__(".uno:ChangeBezier", (), draw)
pic = drawpage.getByIndex(0)
pic.setPosition(__Point__((g.BoundRect.Width - g.Size.Width)//2, (g.BoundRect.Height - g.Size.Height)//2))
drawpage.Height, drawpage.Width = g.BoundRect.Height, g.BoundRect.Width
if not os.path.isabs(name):
name = os.getcwd() + os.path.sep + name
__dispatcher__(".uno:ExportTo", (__getprop__("URL", unohelper.systemPathToFileUrl(name)), __getprop__("FilterName", "draw_svg_Export")), draw)
draw.close(True)
while XSCRIPTCONTEXT.getDocument() != _.doc: # noqa: F821
if XSCRIPTCONTEXT.getDocument() not in [draw, _.doc, None]: # noqa: F821
__halt__ = True
return
__time__.sleep(0.1)
create_valid_svg_file(name)
__group__ = __groupstack__.pop()
if __group__ != 0 and g != 0:
__group__.add(g)
__removeshape__(__ACTUAL__)
def __int__(x): # handle eg. int("10cm")
if type(x) is str:
x = __float__(x)
return int(x)
def __float__(x): # handle eg. float("10,5cm")
if type(x) is str:
for i in __comp__[_.lng]:
x = re.sub(u"(?iu)" + i[0], i[1], x)
x = eval(x)
return float(x)
def fontheight(n = -1):
if n != -1:
_.fontheight = n
else:
return _.fontheight
def fontweight(n = -1):
if n != -1:
_.fontweight = n
else:
return _.fontweight
def fontfamily(s = -1):
if s != -1:
_.fontfamily = s
if ':' in s:
_.fixSVG = True
else:
return _.fontfamily
def fontstyle(n = -1):
if n != -1:
_.fontstyle = n
else:
return _.fontstyle
def __loadlang__(lang, a):
global comp, __colors__
__colors__[lang] = {}
for i in __COLORS__:
for j in a[i[0]].split("|"):
__colors__[lang][j.lower()] = i[1]
for i in a:
if i[0:3] not in ["LIB", "ERR", "PT", "INC", "MM", "CM", "HOU", "DEG"] and i not in __STRCONST__: # uppercase native commands
a[i] = a[i].upper()
repcount = a['REPCOUNT'].split('|')[0]
loopi = itertools.count()
loop = lambda r: "%(i)s = 1\n%(orig)s%(j)s = %(i)s\n%(i)s += 1\n" % \
{ "i": repcount + str(next(loopi)), "j": repcount, "orig": re.sub( r"(?ui)(?<!:)\b%s\b" % repcount, repcount + str(next(loopi)-1), r.group(0)) }
__comp__[lang] = [
[r"(?i)(?<!:)(\b|(?=[-:]))(?:%s)\b" % "|".join([a[i].lower() for i in a if "_" not in i and i != "DECIMAL"]), lambda s: s.group().upper()], # uppercase all native commands in the source code
[r"(?<!:)\b(?:%s) \[(?= |\n)" % a['GROUP'], "\n__groupstart__()\nfor __groupindex__ in range(2):\n[\nif __groupindex__ == 1:\n[\n__groupend__()\nbreak\n]\n"],
[r"(?<!:)\b(?:%s) (%s[^[]*)\[(?= |\n)" % (a['GROUP'], __DECODE_STRING_REGEX__), "\n__groupstart__(\\1)\nfor __groupindex__ in range(2):\n[\nif __groupindex__ == 1:\n[\n__groupend__(\\1)\nbreak\n]\n"],
[r"(?<!:)\b(?:%s)\b" % a['GROUP'], "\n__removeshape__(__ACTUAL__)\n"],
[r"(\n| )][ \n]*\[(\n| )", "\n]\nelse:\n[\n"], # if/else block
[r"(?<!\n)\[(?= |\n)", ":\n[\n"], # start block
[r"( ]|\n]$)", "\n]\n"], # finish block
[r"(?<!:)\b(?:%s)\b" % a['FOR'], "\nfor"],
[r"(?<!:)\b(?:%s)\b" % a['REPEAT'], "\n__repeat__"],
[r"(?<!:)\b(?:%s)\b" % a['BREAK'], "\nbreak"],
[r"(?<!:)\b(?:%s)\b" % a['CONTINUE'], "\ncontinue"],
[r"(?<!:)\b(?:%s)\b" % a['REPCOUNT'], repcount],
[r"(?<!:)\b(?:%s)\b" % a['IF'], "\nif"],
[r"(?<!:)\b(?:%s)\b" % a['WHILE'], "\nwhile"],
[r"(?<!:)\b(?:%s)\b" % a['OUTPUT'], "\nreturn"],
[r"\n(if|while|return) [^\n]*", lambda r: re.sub("(?<![=!<>])=(?!=)", "==", r.group(0))], # = -> ==, XXX x = y = 1?
[r"(?<=\n)(for\b :?\w+) ([^\n]+)(?<=\w|]|}|\))(?=-|:)(?:%s)\b" % a['IN'], "\\1 in \\2"], # "for x y-in" -> "for x in y"
[r"(:?\b\w+|[\[][^[\n]*])\b(?:%s)\b" % a['IN'], "in \\1"], # "x y-in" -> "x in y"
[r"(?<!:)\b(?:%s)\b" % a['IN'], "in"],
[r"(?<!:)\b(?:%s)\b[ \t]+(:?\w+)\b(?! in\b)" % a['FOR'], "\nfor \\1 in"],
[r"(?<=\n)__repeat__ :\n", "while True:\n"], # infinite loop
[r"(?<=\n)(for|while) (?!__groupindex__)[^\n]*:\n\[\n", loop], # loop variables for repcount (not groupindex loop)
[r"(?<=\n)__repeat__([^\n]*\w[^\n]*):(?=\n)", "for %s in range(1, 1+int(\\1)):" % repcount], # repeat block
[r"(?<=\d)[%s](?=\d)" % a['DECIMAL'], "."], # decimal sign
[r"(?<!/)/(?!/)", "*1.0/"], # fix division: /1 -> /1.0, but not with //
[r"\b([0-9]+([,.][0-9]+)?)(%s)\b" % a['HOUR'], lambda r: str(float(r.group(1).replace(",", "."))*30)], # 12h = 12*30°
[r"(?<=\d)(%s)" % a['DEG'], ""], # 1° -> 1
[r"(?<!:)\b(?:__def__)[ \t]+(\w+)\b[ \t]*([:]?\w[^\n]*)", "\ndef \\1(\\2):\n["],
[r"(?<!:)\b(?:__def__)\s+(\w+)", "\ndef \\1():\n["],
[r"(?<!:)\b(?:%s)\b" % a['END'], "\n]"],
[r"(?<!:)\b(?:%s)\b" % a['GLOBAL'], "global"],
[r"(?<!:)\b(?:%s)\b" % a['TRUE'], "True"],
[r"(?<!:)\b(?:%s)\b" % a['FALSE'], "False"],
[r"(?<!:)\b(?:%s)\b" % a['NOT'], "not"],
[r"(?<!:)\b(?:%s)\b" % a['AND'], "and"],
[r"(?<!:)\b(?:%s)\b" % a['OR'], "or"],
[r"(?<!:)\b(?:%s)\b" % a['INT'], "__int__"],
[r"(?<!:)\b(?:%s)\b" % a['FLOAT'], "__float__"],
[r"(?<!:)\b(?:%s)\b" % a['STR'], "__string__"],
[r"(?<!:)\b(?:%s)\b" % a['COUNT'], "len"],
[r"(?<!:)\b(?:%s)\b" % a['ROUND'], "round"],
[r"(?<!:)\b(?:%s)\b" % a['ABS'], "abs"],
[r"(?<!:)\b(?:%s)\b" % a['SIN'], "sin"],
[r"(?<!:)\b(?:%s)\b" % a['COS'], "cos"],
[r"(?<!:)\b(?:%s)\b" % a['PI'], "pi"],
[r"(?<!:)\b(?:%s)\b" % a['SQRT'], "sqrt"],
[r"(?<!:)\b(?:%s)\b" % a['LOG10'], "log10"],
[r"(?<!:)\b(?:%s)\b" % a['MIN'], "min"],
[r"(?<!:)\b(?:%s)\b" % a['MAX'], "max"],
[r"(?<!:)\b(?:%s)\b" % a['STOP'], "\nreturn None"],
[r"(?<!:)\b(?:%s)\b" % a['CLEARSCREEN'], "\n__cs__()"],
[r"(?<!:)\b(?:%s)(\s+|$)" % a['PENCOLOR'], "\n)pencolor("],
[r"(?<!:)\b(?:%s)(\s+|$)" % a['PENSTYLE'], "\n)penstyle("],
[r"(?<!:)\b(?:%s)(\s+|$)" % a['PENJOINT'], "\n)penjoint("],
[r"(?<!:)\b(?:%s)(\s+|$)" % a['PENCAP'], "\n)pencap("],
[r"(?<!:)\b(?:%s)(\s+|$)" % a['FILLCOLOR'], "\n)fillcolor("],
[r"(?<!:)\b(?:%s)(\s+|$)" % a['FILLTRANSPARENCY'], "\n)filltransparency("],
[r"(?<!:)\b(?:%s)(\s+|$)" % a['PENTRANSPARENCY'], "\n)pentransparency("],
[r"(?<!:)\b(?:%s)(\s+|$)" % a['FILLSTYLE'], "\n)fillstyle("],
[r"(?<!:)\b(?:%s)(\s+|$)" % a['FONTCOLOR'], "\n)fontcolor("],
[r"(?<!:)\b(?:%s)(\s+|$)" % a['FONTTRANSPARENCY'], "\n)fonttransparency("],
[r"(?<!:)\b(?:%s)(\s+|$)" % a['FONTFAMILY'], "\n)fontfamily("],
[r"(?<!:)\b(?:%s)(\s+|$)" % a['FONTHEIGHT'], "\n)fontheight("],
[r"(?<!:)\b(?:%s)(\s+|$)" % a['FONTWEIGHT'], "\n)fontweight("],
[r"(?<!:)\b(?:%s)(\s+|$)" % a['FONTSTYLE'], "\n)fontstyle("],
[r"(?<!:)\b(?:%s)(\s+|$)" % a['PENWIDTH'], "\n)pensize("],
[r"(?<!:)\b(?:%s)\b" % a['PENDOWN'], "\n__pen__(1)"],
[r"(?<!:)\b(?:%s)\b" % a['PENUP'], "\n__pen__(0)"],
[r"(?<!:)\b(?:%s)\b" % a['HIDETURTLE'], "\nhideturtle()"],
[r"(?<!:)\b(?:%s)\b" % a['SHOWTURTLE'], "\nshowturtle()"],
[r"(?<!:)\b(?:%s)\b\[" % a['POSITION'], "position()["],
[r"(?<!:)\b(?:%s)\b(?!\()" % a['POSITION'], "\n)position("],
[r"(?<!:)\b(?:%s)\b" % a['HEADING'], "\n)heading("],
[r"(?<!:)\b(?:%s)\b" % a['PAGESIZE'], "pagesize()"],
[r"(?<!:)\b(?:%s)\b" % a['POINT'], "\npoint()"],
[r"(?<!:)\b(?:%s)\b" % (a['ELLIPSE'] + "|" + a['CIRCLE']), "\n)ellipse("],
[r"(?<!:)\b(?:%s)\b" % (a['RECTANGLE'] + "|" + a['SQUARE']), "\n)rectangle("],
[r"(?<!:)\b(?:%s)\b" % a['CLOSE'], "\n__fillit__(False)"],
[r"(?<!:)\b(?:%s)\b" % a['FILL'], "\n__fillit__()"],
[r"(?<!:)\b(?:%s)\b" % a['LABEL'], "\n)label("],
[r"(?<!:)\b(?:%s)\b" % a['TEXT'], "\n)text(__getshape__(__ACTUAL__),"],
[r"(text\([ \t]*\"[^\"\n\)]*)", "\\1\"\n"],
[r"(?<!:)\b(?:%s)\b" % a['HOME'], "\nturtlehome()"],
[r"(?<!:)\b(?:%s)\b" % a['SLEEP'], "\n)sleep("],
[r"(?<!:)\b(?:%s)\b" % a['FORWARD'], "\n)forward("],
[r"(?<!:)\b(?:%s)\b" % a['BACKWARD'], "\n)backward("],
[r"(?<!:)\b(?:%s)\b" % a['TURNRIGHT'], "\n)turnright("],
[r"(?<!:)\b(?:%s)\b" % a['RANDOM'], "Random"],
[r"(?<!:)\b(?:%s)\b" % a['SET'], "set"],
[r"(?<!:)\b(?:%s)\b" % a['RANGE'], "range"],
[r"(?<!:)\b(?:%s)\b" % a['LIST'], "list"],
[r"(?<!:)\b(?:%s)\b" % a['TUPLE'], "tuple"],
[r"(?<!:)\b(?:%s)\b" % a['SORTED'], "sorted"],
[r"(?<!:)\b(?:%s)\b ?\(" % a['RESEARCH'], "re.search('(?u)'+"],
[r"(?<!:)\b(?:%s)\b ?\(" % a['RESUB'], "re.sub('(?u)'+"],
[r"(?<!:)\b(?:%s)\b ?\(" % a['REFINDALL'], "re.findall('(?u)'+"],
[r"(?<!:)\b(?:%s)\b" % a['ANY'], "u'any'"],
[r"(?<!:)\b(?:%s) (\w+|[\[][^\]]*])\b" % a['INPUT'], " Input(\\1)"],
[r"(?<!:)\b(?:%s)\b" % a['PRINT'], "\nPrint"],
[r"(?<!:)\b(?:%s)\b" % a['TURNLEFT'], "\n)turnleft("],
[r"\b([0-9]+([,.][0-9]+)?)(%s)\b" % a['PT'], "\\1"],
[r"\b([0-9]+([,.][0-9]+)?)(%s)(?!\w)" % a['INCH'], lambda r: str(float(r.group(1).replace(",", "."))*72)],
[r"\b([0-9]+([,.][0-9]+)?)(%s)\b" % a['MM'], lambda r: str(float(r.group(1).replace(",", "."))*__MM_TO_PT__)],
[r"\b([0-9]+([,.][0-9]+)?)(%s)\b" % a['CM'], lambda r: str(float(r.group(1).replace(",", "."))*__MM_TO_PT__*10)],
[r"(?<=[-*/=+,]) ?\n\)(\w+)\(", "\\1()"], # read attributes, eg. x = fillcolor
[r"(?<=return) ?\n\)(\w+)\(", "\\1()"], # return + user function
]
def __concatenation__(r): # keep line positions with extra line breaks
s = re.subn("~[ \t]*\n", " ", r.group(0))
return s[0] + "\n" * s[1]
# convert Logo expressions to Python ones by adding
# missing parentheses to procedure and function calls
# f x y z -> f(x, y, z)
# a = [sin len f [cos 45, 6] [1, 2, 3] sin 90] ->
# a = [sin(len(f([cos(45),6],[1, 2, 3], sin(90))]
# NOTE: "f(45)" and "f (45)" are not the same:
# sin(45) + cos 45 -> sin(45) + cos(45)
# sin (45) + cos 45 -> sin(45 + cos(45))
def __l2p__(i, par, insub, inarray):
first = True
while par["pos"] < len(i):
pos = par["pos"]
ch = i[pos]
ignored = False
# starting parenthesis
if ch in "([":
if insub and not inarray and not first and par["out"][-1:] != "]":
return
par["out"] += ch
par["pos"] += 1
__l2p__(i, par, insub, True)
# ending parenthesis
elif ch in ")]":
if insub and not inarray:
return
# put character before terminating spaces
par["out"] = re.sub("( *)$", ch + "\\1", par["out"])
par["pos"] += 1
return
# starting a subroutine
elif pos in par["sub"]:
if insub and not inarray and not first:
return
first = False
subname = i[pos:par["sub"][pos]]
par["pos"] += len(subname)
par["out"] += subname
# Logo syntax: add parentheses
# for example: foo x y z -> foo(x, y, z)
par["out"] += "("
for j in range(par["names"][subname]):
# add commas, except if already added, eg. with special RANGE
# (variable argument counts: RANGE 1 or RANGE 1 100 or RANGE 1 100 10)
if j > 0 and par["out"].rstrip()[-1] != ",":
par["out"] = re.sub("( +),$",",\\1", par["out"] + ",")
__l2p__(i, par, True, False)
par["out"] = re.sub("( +)\\)$", ")\\1", par["out"] + ")")
# operators
elif pos in par["op"]:
op = i[pos:par["op"][pos]]
par["out"] += op
par["pos"] += len(op)
__l2p__(i, par, insub, False)
# other atoms
elif pos in par["atom"]:
if insub and not inarray and not first:
return
first = False
atom = i[pos:par["atom"][pos]]
par["out"] += atom
par["pos"] += len(atom)
# handle subroutines with explicit parentheses
# and array indices
if i[par["pos"]:par["pos"]+1] in "([":
first = True
continue
# optional negative or positive sign
elif ch in "-+":
if insub and not inarray and not first:
return
par["out"] += ch
par["pos"] += 1
ignored = first
elif ch == " ":
par["out"] += ch
par["pos"] += 1
ignored = first
elif insub and ((ch == "," and not inarray) or (ch != ",")):
return
else:
par["out"] += ch
par["pos"] += 1
# end of first subexpression, except in the case of ignored characters
if not ignored:
first = False
def __compil__(s):
global _, comp, __strings__, __compiled__
try:
c = _.doc.CurrentController.getViewCursor()
locs = [i for i in [c.CharLocale, c.CharLocaleAsian, c.CharLocaleComplex] if i.Language != 'zxx'] # not None language
loc = Locale(__uilocale__.split('-')[0], __uilocale__.split('-')[1], '')
if locs and loc not in locs:
loc = locs[0]
try:
_.lng = loc.Language + '_' + loc.Country
__loadlang__(_.lng, __l12n__(_.lng))
except Exception:
__trace__()
_.lng = loc.Language
__loadlang__(_.lng, __l12n__(_.lng))
except Exception:
__trace__()
# for testing compiling, we create a not document based namespace
if "_" not in locals():
_ = lambda: None
_.lng = 'en_US'
if _.lng not in __comp__:
__loadlang__(_.lng, __l12n__(_.lng))
_.decimal = __l12n__(_.lng)['DECIMAL']
rmsp = re.compile(r"[ ]*([=+*/]|==|<=|>=|<>|!=|-[ ]+)[ ]*")
chsp = re.compile(r"[ \t]+")
chch = re.compile(r"(?u)(?<!\w):(?=\w)")
# remove CR characters and split lines
s = re.sub(r'[ \t\r]*(?=\n)', '', s)
# remove full line comments
s = re.sub(r"^[ \t]*[;#][^\n]*", "", s)
s = re.sub(r"(?<=\n)[ \t]*[;#][^\n]*", "", s)
# concatenate lines
__compiled__ = re.sub(r'([^\n]*~[ \t]*\n)+[^\n]*', __concatenation__, s)
# sign original line breaks
s = re.sub("(?<=\n)", __LINEBREAK__ + "\n", __compiled__)
# encode strings
lq = '\'' + __l12n__(_.lng)['LEFTSTRING'].replace("|", "")
rq = '\'' + __l12n__(_.lng)['RIGHTSTRING'].replace("|", "")
__strings__ = []
s = re.sub("(?u)([%s])((?:[^\n%s]|\\\\[%s])*)(?<!\\\\)[%s]" % (lq, rq, rq, rq), __encodestring__, s)
s = re.sub(r'(?u)(?<![0-9])(")(~?\w*)', __encodestring__, s)
# remove extra spaces
s = chsp.sub(" ", s)
# remove inline comments
s = re.sub(r"[ ]*;[^\n]*", "", s)
# n-dash and m-dash as minus signs
s = re.sub(r"(?u)[–—]", "-", s)
# replace procedure names
s = re.sub(r"(?i)^[ ]*(%s)[ ]+" % __l12n__(_.lng)['TO'], "__def__ ", s)
s = re.sub(r"(?i)\n[ ]*(%s)[ ]+" % __l12n__(_.lng)['TO'], "\n__def__ ", s)
subnames = re.findall(r"(?iu)(?<=__def__ )\w+", s)
globs = ""
functions = ["range", "__int__", "__float__", "Random", "Input", "__string__", "len", "round", "abs", "sin", "cos", "sqrt", "log10", "set", "list", "tuple", "re.sub", "re.search", "re.findall", "sorted", "min", "max"]
defaultfunc = ["Print"] # TODO handle all default procedures
if len(subnames) > 0:
globs = "global %s" % ", ".join(subnames)
# search user functions (function calls with two or more arguments need explicit Python parentheses)
ends = __l12n__(_.lng)["END"] # support multiple names of "END"
firstend = ends.split("|")[0]
s = re.sub(r"(?<!:)\b(?:%s)\b" % ends, firstend, s)
__l12n__(_.lng)["END"] = firstend
functions += [ re.findall(r"(?u)\w+",i[0])[0] for i in re.findall(r"""(?iu)(?<=__def__ )([^\n]*)\n # beginning of a procedure
(?:[^\n]*(?<!\b(%(END)s))\n)* # 0 or more lines (not END)
[^\n]*\b(?:%(OUTPUT)s)\b[^\n]*\n # line with OUTPUT (functions = procedures with OUTPUT)
(?:[^\n]*(?<!\b(?:%(END)s))\n)* # 0 or more lines (not END)
[ \t]*\b(?:%(END)s)\b""" % __l12n__(_.lng), s, re.X) ]
__l12n__(_.lng)["END"] = ends
# add line breaks before procedure calls
procedures = set(subnames) - set(functions)
if len(procedures) > 0:
s = re.sub(r"(?<!__def__)(?<![-+=*/])(?<!%s)(?:^|[ \t]+)(" % ")(?<!".join(functions) + "|".join(procedures) + r")(?!\w)", r"\n\1", s)
# substitute LibreLogo functions and specifiers with their Python equivalents
for i in __comp__[_.lng]:
s = re.sub(u"(?u)" + i[0], i[1], s)
indent = 0 # Python indentation level
result = ""
func = re.compile(r"(?iu)(def (\w+))(\(.*\):)")
# compile to Python
subroutines = re.compile(r"(?iu)(?<!def )(?<![_\w])\b(%s)\b(?![\w(])" % "|".join(subnames + functions + defaultfunc))
operators = re.compile(r"(?iu)(%s)" % "(?:[ ]*([+*/<>]|//|==|<=|>=|<>|!=)[ ]*|[ ]*-[ ]+|(?<! )-[ ]*|[ ]*[*][*][ ]*)") # operators, eg. " - ", "-", "- "
atoms = re.compile(r"(?iu)(%s)" % r"[0-9]+([.,][0-9]+)?|:?\w+([.]\w)?")
# store argument numbers of all subroutines in dictionary "names"
names = {key: 1 for key in functions + defaultfunc}
names["range"] = names["re.sub"] = 3
names["re.search"] = names["re.findall"] = 2
# match a function header
search_funcdef = re.compile(r"(^|\n) *(def (\w+))(\([^\n]*\):) *(?=\n)")
# "multiline" lambda function to process function headers: add commas to argument list and
# add {"subroutine_name": argument_count} into names using a temporary array
# (instead of using global variable "names" and a new global function to process the matching patterns)
# for example: "def f(x y z):" -> "def f(x,y,z):" and names = {"f": 3}
process_funcdef = lambda r: r.group(1) + r.group(2) + \
[chsp.sub(", ", r.group(4)), names.update({r.group(3): len(re.findall(r"\w+", r.group(4)))})][0]
# process all function headers calling process_funcdef for every matching
# (before parsing Logo expressions line by line, we need to know about all functions,
# because functions can be defined in any order, ie. their calls can be before
# their definitions)
s = search_funcdef.sub(process_funcdef, s)
# process line by line
for i in s.split("\n"):
i = i.strip()
# convert Logo expressions to Python ones using regex based tokenization
# tokens: {startpos: endpos} dictionaries for subroutine names, operators and other tokens
# sub: subroutine tokens = positions of Logo subroutine names
# (without explicit parentheses, for example: "f x" or "f (x*2)", but not "f(x)")
sub = {key: value for (key, value) in [j.span() for j in list(subroutines.finditer(i))]}
if sub != {}:
# op: operator tokens
op = {key: value for (key, value) in [j.span() for j in list(operators.finditer(i))]}
# atom: other tokens (variable names, numbers and function names)
atom = {key: value for (key, value) in [j.span() for j in list(atoms.finditer(i))]}
par = {"pos": 0, "out": "", "sub": sub, "op": op, "atom": atom, "names": names}
if i.startswith(')') and '(' in i:
# replace ")forward(" etc. with spaces temporarily
len_name = i.index('(') + 1
__l2p__(' ' * len_name + i[len_name:], par, False, False)
i = i[:len_name] + par["out"].lstrip()
else:
__l2p__(i, par, False, False)
i = par["out"]
# starting program block
if i[0:1] == '[':
i = i[1:]
indent += 1
# check program stop, for example, in every execution of a loop
result = result + "\n" + " " * indent + "__checkhalt__()\n"
# fix position of ending parenthesis
if i[0:1] == ')':
i = i[1:] + ')'
result = result + "\n" + " " * indent + i
# ending program block
if i[0:1] == ']':
result = result[:-1]
indent -= 1
# colon_to_underline in Logo variables
result = chch.sub("_", result)
# character encoding
result = to_ascii(result).replace(r"\n", "\n")
# decode strings
result = re.sub(__DECODE_STRING_REGEX__, __decodestring__, result)
return to_ascii(globs) + "\n" + result
def __gotoline__(n):
_.cursor.collapseToStart()
for i in range(1, n):
_.cursor.gotoNextParagraph(False)
try:
_.doc.CurrentController.getViewCursor().gotoRange(_.cursor, False)
except Exception:
__dispatcher__(".uno:Escape")
_.doc.CurrentController.getViewCursor().gotoRange(_.cursor.getStart(), False)
g_exportedScripts = left, right, goforward, gobackward, run, stop, home, clearscreen, commandline, __translate__
# vim: set shiftwidth=4 softtabstop=4 expandtab:
__lng_fallback__ = {
'FORWARD':{'en_US': 'forward|fd', 'af': 'voorwaarts|vw', 'am': 'ወደ ፊት|ወደ ፊት', 'ar': 'للأمام|أم', 'ast': 'avanzar|av', 'be': 'наперад|нп', 'br': 'war-raok|fd', 'bs': 'naprijed|fd', 'ca': 'avança|endavant|davant|av', 'ca_valencia': 'avança|avant|davant|av', 'cy': 'ymlaen|fd', 'cs': 'dopředu|do', 'de': 'vor|vr', 'dsb': 'doprědka|dp', 'el': 'μπροστά|μπ|forward|fd', 'eo': 'antaŭen|a', 'es': 'avanza|adelante|av|ad', 'et': 'edasi|e', 'eu': 'aurrera|aur', 'fi': 'eteen|et', 'fr': 'avance|av', 'fy': 'foarút|fu', 'gl': 'reenviada|fd', 'gug': 'tenondépe|td', 'he': 'קדימה|קד', 'hr': 'naprijed|np', 'hsb': 'doprědka|dp', 'hu': 'előre|e', 'is': 'áfram|fr', 'ja': 'すすむ|forward|fd', 'kab': 'zdat|zd', 'kk': 'алға|ал', 'km': 'បញ្ជូន\u200bបន្ត|fd', 'kn': 'ಮುಂದಕ್ಕೆ|fd', 'ko': '앞으로|fd', 'lo': 'ໄປຂ້າງຫນ້າ|fd', 'lt': 'priekin|pr', 'lv': 'uz_priekšu|pr', 'mn': 'урагш|fd', 'mr': 'पुढे|fd', 'nb': 'forover|fd', 'nl': 'vooruit|vu', 'nn': 'fram|framover|forover|fr|forward', 'oc': 'avança|av', 'or': 'ଆଗକୁ|fd', 'pt_BR': 'paraFrente|pf', 'ro': 'forward|fd|înainte', 'ru': 'вперёд|в', 'sid': 'albira|ar', 'sk': 'dopredu|do', 'sl': 'naprej|np', 'sq': 'përpara|fd', 'th': 'เดินหน้า|fd', 'tr': 'ileri|il', 'uk': 'вперед|вп', 'uz': 'oldinga|ol', 'zh_CN': '前进|进|forward|fd', 'zh_TW': '前進|進|forward|fd'},
'BACKWARD':{'en_US': 'back|bk', 'af': 'terug|tg', 'am': 'ወደ ኋላ|ወደ ኋላ', 'ar': 'للخلف|خف', 'ast': 'atrás|pt', 'be': 'назад|нз', 'br': 'war-gil|bk', 'bs': 'nazad|bk', 'ca': 'retrocedeix|recula|enrere|re', 'ca_valencia': 'retrocedeix|recula|arrere|re', 'cs': 'vzad|vz', 'de': 'zurück|zk', 'dsb': 'slědk|sk', 'el': 'πίσω|πι|back|bk', 'eo': 'retro|r', 'es': 'retrocede|atras|re|at', 'et': 'tagasi|t', 'eu': 'atzera|atz', 'fi': 'taakse|tk', 'fr': 'recule|re', 'fy': 'tebek|tb', 'gl': 'atrás|bk', 'gug': 'tapykuépe|jere', 'he': 'אחורה|אח', 'hr': 'nazad|nz', 'hsb': 'wróćo|wr', 'hu': 'hátra|h', 'is': 'bakka|bk', 'ja': 'もどる|back|bk', 'kab': 'ɣer deffir', 'kk': 'артқа|ар', 'km': 'ថយក្រោយ|bk', 'kn': 'ಹಿಂದಕ್ಕೆ|bk', 'ko': '뒤로|bk', 'lo': 'ກັບຫຼັງ|bk', 'lt': 'atgal|at', 'lv': 'atpakaļ|at', 'mn': 'буцах|bk', 'mr': 'मागे|bk', 'nb': 'tilbake|bakover|tb', 'nl': 'terug|tg', 'nn': 'tilbake|bakover|tb|back', 'oc': 'recuola|re', 'or': 'ପଛକୁ|bk', 'pt_BR': 'paraTrás|pt', 'ro': 'back|bk|înapoi', 'ru': 'назад|н', 'sid': 'badhera|br', 'sk': 'vzad|vz', 'sl': 'nazaj|nz', 'sq': 'prapa|bk', 'th': 'กลับหลัง|bk', 'tr': 'geri|ge', 'uk': 'назад|нд', 'uz': 'orqaga|or', 'zh_CN': '后退|退|back|bk', 'zh_TW': '後退|退|back|bk'},
'TURNLEFT':{'en_US': 'left|turnleft|lt', 'af': 'links|linksaf|li', 'am': 'ግራ|ወደ ግራ ማዟሪያ|ወደ ግራ', 'ar': 'لليسار', 'ast': 'izquierda|xiraizquierda|xi', 'be': 'улева|паварот_улева|лв', 'bs': 'lijevo|skrenilijevo|lt', 'ca': 'esquerra|giraesquerra|gira.esquerra|ge', 'ca_valencia': 'esquerra|giraesquerra|gira.esquerra|ge', 'cs': 'vlevo|vl', 'de': 'links|li', 'dsb': 'nalěwo|nl', 'el': 'αριστερά|στροφήαριστερά|αρ|left|turnleft|lt', 'eo': 'maldekstren|turnu_liven|lt', 'es': 'izquierda|giraizquierda|iz|gi', 'et': 'vasakule|vasakpööre|v', 'eu': 'ezkerra|ezkerrera|ezk', 'fi': 'vasemmalle|vas', 'fr': 'gauche|tournegauche|ga', 'fy': 'links|linksôf|lf', 'gl': 'esquerda|voltaesquerda|lt', 'gug': 'asúpe|jereasúpe|as', 'he': 'שמאלה|לפנותשמאלה|פנייהשמאלה|שמ', 'hr': 'lijevo|okreni lijevo|li', 'hsb': 'nalěwo|nl', 'hu': 'balra|b', 'is': 'vinstri|snúavinstri|vi', 'ja': '左|左に曲がる|left|turnleft|lt', 'kab': 'ayeffus', 'kk': 'солға|солға_бұрылу|с', 'km': 'ឆ្វេង|turnleft|lt', 'kn': 'ಎಡ|ಎಡಕ್ಕೆ ತಿರುಗಿಸು|lt', 'ko': '왼쪽|왼쪽으로회전|lt', 'lo': 'ຊ້າຍ|ລ້ຽວຊ້າຍ|lt', 'lt': 'kairėn|kr', 'lv': 'pa_kreisi|kreisi|gk', 'mn': 'зүүн|зүүнэргэх|lt', 'mr': 'डावे|डावीकडे वळा|lt', 'nb': 'venstre|snu venstre|ve', 'nl': 'links|linksaf|ls', 'nn': 'venstre|snu venstre|ve|left', 'oc': 'esquerra|viraesquerra|es', 'or': 'ବାମକୁ|ବାମକୁ ଯାଆନ୍ତୁ|lt', 'pt_BR': 'paraEsquerda|pe|girarEsquerda|giraEsquerda', 'ro': 'left|turnleft|lt|stânga', 'ru': 'влево|поворот_налево|л', 'sid': 'gura|guraraqole|gr', 'sk': 'vľavo|vl', 'sl': 'levo|obrnilevo|lv', 'sq': 'majtaj|kthehumajtas|lt', 'th': 'ซ้าย|เลี้ยวซ้าย|lt', 'tr': 'sol|soladön|sl', 'uk': 'ліворуч|поворот_ліворуч|лв', 'uz': 'chap|chapga_burilish|chp', 'zh_CN': '左|左转|left|turnleft|lt', 'zh_TW': '左|左轉|left|turnleft|lt'},
'TURNRIGHT':{'en_US': 'right|turnright|rt', 'af': 'regs|regsaf|re', 'am': 'ቀኝ|ወደ ቀኝ ማዟሪያ|ወደ ቀኝ', 'ar': 'لليمين', 'ast': 'drecha|xiradrecha|xd', 'be': 'управа|паварот_управа|пр', 'br': 'dehou|trotudehou|de', 'bs': 'desno|skrenidesno|rt', 'ca': 'dreta|giradreta|gira.dreta|gd', 'ca_valencia': 'dreta|giradreta|gira.dreta|gd', 'cs': 'vpravo|vp', 'de': 'rechts|re', 'dsb': 'napšawo|np', 'el': 'δεξιά|στροφήδεξιά|δξ|right|turnright|rt', 'eo': 'dekstren|turnu_dekstren|dt', 'es': 'derecha|giraderecha|de|gd', 'et': 'paremale|parempööre|p', 'eu': 'eskuina|eskuinera|esk', 'fi': 'oikealle|oik', 'fr': 'droite|tournedroite|dr', 'fy': 'rjochts|rjochtsôf|rs', 'gl': 'dereita|voltadereita|rt', 'gug': 'akatúa|jereakatúa|ak', 'he': 'ימינה|לפנותימינה|פנייהימינה|ימ', 'hr': 'desno|okreni desno|de', 'hsb': 'naprawo|np', 'hu': 'jobbra|j', 'is': 'hægri|snúahægri|hæ', 'ja': '右|右に曲がる|right|turnright|rt', 'kab': 'azelmaḍ', 'kk': 'оңға|оңға_бұрылу|о', 'km': 'ស្ដាំ|turnright|rt', 'kn': 'ಬಲ|ಬಲಕ್ಕೆ ತಿರುಗಿಸು|rt', 'ko': '오른쪽|오른쪽으로회전|rt', 'lo': 'ຂວາ|ລ້ຽວຂວາ|rt', 'lt': 'dešinėn|dš', 'lv': 'pa_labi|labi|gl', 'mn': 'баруун|баруунэргэлт|rt', 'mr': 'उजवी|उजवीकडे वळा|rt', 'nb': 'høyre|snu høyre|hg', 'nl': 'rechts|rechtsaf|rs', 'nn': 'høgre|snu høgre|hg|right', 'oc': 'dreita|virardreita|de', 'or': 'ଡ଼ାହାଣ|ଡ଼ାହାଣକୁ ଯାଆନ୍ତୁ|rt', 'pt_BR': 'paraDireita|pd|girarDireita|giraDireita', 'ro': 'right|turnright|rt|dreapta', 'ru': 'вправо|поворот_направо|п', 'sid': 'qiniite|qiniitiraqoli|qt', 'sk': 'vpravo|vp', 'sl': 'desno|obrnidesno|ds', 'sq': 'djathtas|kthehudjathtas|rt', 'th': 'ขวา|เลี้ยวขวา|rt', 'tr': 'sağ|sağadön|sğ', 'uk': 'праворуч|поворот_праворуч|пр', 'uz': 'o‘ng|o‘ngga_burilish|o‘n', 'zh_CN': '右|右转|right|turnright|rt', 'zh_TW': '右|右轉|right|turnright|rt'},
'PENUP':{'en_US': 'penup|pu', 'af': 'penop|po', 'am': 'ብዕር ወደ ላይ |pu', 'ar': 'ارفع_القلم|ارفع', 'ast': 'llapizxubir|lx', 'bs': 'pengore|pu', 'ca': 'aixeca.llapis|al', 'ca_valencia': 'alça.llapis|al', 'cs': 'peronahoru|pn', 'de': 'fliegen', 'dsb': 'pisakgórjej|pg', 'el': 'γραφίδαπάνω|γπ|penup|pu', 'eo': 'plumofor|pf', 'es': 'sinpluma|subirlapiz|sp|sl', 'et': 'pliiats_üles|pü', 'eu': 'lumagora|lg', 'fi': 'kynäylös|ky', 'fr': 'levecrayon|lc', 'fy': 'penomheech|po', 'gl': 'senestilo|se', 'gug': "bolígrafo'ỹre|bỹ", 'he': 'להריםעט|עטלמעלה|הרםעט|לע', 'hr': 'olovka gore|og', 'hsb': 'pisakhorje|ph', 'hu': 'tollatfel|tf', 'is': 'penniupp|pu', 'ja': 'ペンをあげる|penup|pu', 'kab': 'saliakeryun|ak', 'kk': 'қаламды_көтеру|қк', 'kn': 'ಲೇಖನಿಎತ್ತು|pu', 'ko': '펜을위로|pu', 'lo': 'ປາກກາຂື້ນ|pu', 'lt': 'eisim|es|pakelk.pieštuką|pakelk.trintuką', 'lv': 'pacelt_spalvu|ps', 'mn': 'үзэгөргөх|pu', 'mr': 'पेनअप|pu', 'nb': 'penn opp|po|penup', 'nl': 'penomhoog|po', 'nn': 'penn opp|po|penup', 'oc': 'levagredon|lg', 'or': 'ପେନଉପରକୁ|pu', 'pt_BR': 'usarNada|un|useNada', 'ro': 'penup|pu|stilousus', 'ru': 'поднять_перо|пп', 'sid': 'biireworora|bw', 'sk': 'perohore|ph', 'sl': 'perogor|pg', 'sq': 'lapsilart|pu', 'th': 'ปากกาขึ้น|pu', 'tr': 'kalemyukarı|ky', 'uk': 'підніми_перо|пп', 'uz': 'ruchkani_ko‘tarish|rk', 'zh_CN': '提笔|抬笔|penup|pu', 'zh_TW': '提筆|penup|pu'},
'PENDOWN':{'en_US': 'pendown|pd', 'af': 'penaf|pa', 'am': 'ብዕር ወደ ታች |pd', 'ar': 'أنزل_القلم|أنزل', 'ast': 'llapizbaxar|lb', 'bs': 'pendole|pd', 'ca': 'baixa.llapis|bl', 'ca_valencia': 'baixa.llapis|bl', 'cs': 'perodolů|pd', 'de': 'laufen', 'dsb': 'pisakdołoj|pd', 'el': 'γραφίδακάτω|γκ|pendown|pd', 'eo': 'plumoek|pe', 'es': 'conpluma|bajarlapiz|cp|bl', 'et': 'pliiats_alla|pa', 'eu': 'lumabehera|lb', 'fi': 'kynäalas|ka', 'fr': 'baissecrayon|bc', 'fy': 'penomleech|po', 'gl': 'conestilo|ce', 'gug': 'bolígrafo|bo', 'he': 'להורידעט|עטלמטה|הורדעט|הע', 'hr': 'olovka dolje|od', 'hsb': 'pisakdele|pd', 'hu': 'tollatle|tl', 'is': 'penniniður|pn', 'ja': 'ペンをおろす|pendown|pd', 'kab': 'siderakeryun|ik', 'kk': 'қаламды_түсіру|қт', 'km': 'pendown', 'kn': 'ಲೇಖನಿಇಳಿಸು|pd', 'ko': '펜아래로|pd', 'lo': 'ປາກກາລົງ|pd', 'lt': 'piešim|pš|nuleisk.pieštuką', 'lv': 'nolikt_spalvu|ns', 'mn': 'үзэгбуулгах|pd', 'mr': 'पेनडाउन|pd', 'nb': 'penn ned|pn|pendown', 'nl': 'penneer|pn', 'nn': 'penn ned|pn|pendown', 'oc': 'abaissagredon|bg', 'or': 'ପେନତଳକୁ|pd', 'pt_BR': 'usarLápis|ul|useLápis', 'ro': 'pendown|pd|stiloujos', 'ru': 'опустить_перо|оп', 'sid': 'biireworora|bw', 'sk': 'perodolu|pd', 'sl': 'perodol|pd', 'sq': 'lapsiposhtë|pd', 'th': 'ปากกาลง|pd', 'tr': 'kalemaşağı|ka', 'uk': 'опусти_перо|оп', 'vec': 'penabasa|pd', 'zh_CN': '下笔|落笔|pendown|pd', 'zh_TW': '下筆|pendown|pd'},
'HOME':{'en_US': 'home', 'af': 'tuis', 'am': 'ቤት', 'ar': 'المنزل', 'as': 'ঘৰ', 'ast': 'aniciu', 'be': 'пачатак', 'bn_IN': 'Home', 'br': 'Degemer', 'bs': 'kući', 'ca': 'inici|centre', 'ca_valencia': 'inici|centre', 'ckb': 'ماڵەوە', 'cy': 'cartref', 'cs': 'domů', 'de': 'anfang', 'dgo': 'घर', 'dsb': 'zachopjeńk', 'el': 'αρχή|home', 'eo': 'hejmen', 'es': 'inicio|casa|centro', 'et': 'koju', 'eu': 'hasiera', 'fi': 'kotiin', 'fr': 'origine', 'fy': 'thús', 'gd': 'dhachaigh', 'gl': 'inicio', 'gug': 'óga', 'gu': 'ઘર', 'he': 'בית', 'hi': 'घर', 'hr': 'polazno', 'hsb': 'spočatk', 'hu': 'haza', 'id': 'beranda', 'is': 'heim', 'ja': 'もとの場所へ|home', 'kab': 'axxam', 'kk': 'басы', 'km': 'ដើម', 'kn': 'ನೆಲೆ', 'kok': 'घरGhor', 'ko': '처음', 'lo': 'ໜ້າທຳອິດ', 'lt': 'namo', 'lv': 'sākums', 'ml': 'ആസ്ഥാനം', 'mn': 'нүүр хуудас', 'mr': 'होम', 'my': 'အချို့', 'nb': 'hjem', 'nl': 'thuis', 'nn': 'heim|home', 'oc': 'acuèlh', 'or': 'ମୂଳ ସ୍ଥାନ', 'pa_IN': 'ਘਰ', 'pt_BR': 'paraCentro|centro|pc', 'ro': 'home|acasă', 'ru': 'начало', 'sat': 'ओड़ाक्', 'sd': 'گهر', 'sid': 'Mine', 'si': 'සමහර', 'sk': 'domov', 'sl': 'začetek', 'sq': 'shtëpia', 'te': 'నివాసం', 'th': 'บ้าน', 'tr': 'başlangıç', 'uk': 'центр', 'vi': 'Home', 'zh_CN': '归位|回归|回家|家|home', 'zh_TW': '回家|家|home'},
'POINT':{'en_US': 'point', 'af': 'punt', 'am': 'ነጥቦች', 'ar': 'نقطة', 'as': 'বিন্দু', 'ast': 'puntu', 'be': 'пункт', 'bn_IN': 'পয়েন্ট', 'br': 'poent', 'bs': 'tačka', 'ca': 'punt', 'ca_valencia': 'punt', 'ckb': 'خاڵ', 'cy': 'pwynt', 'cs': 'bod|puntík', 'de': 'punkt', 'dsb': 'dypk', 'el': 'σημείο|point', 'eo': 'punkto', 'es': 'punto', 'et': 'punkt', 'eu': 'puntua', 'fi': 'piste', 'fy': 'punt', 'gd': 'puing', 'gl': 'punto', 'gug': 'kyta', 'gu': 'બિંદુ', 'he': 'נקודה', 'hi': 'बिंदु', 'hr': 'točka', 'hsb': 'dypk', 'hu': 'pont', 'id': 'poin', 'is': 'punktur', 'ja': '点|point', 'kab': 'agaz', 'ka': 'წერტილი', 'kk': 'нүкте', 'km': 'ចំណុច', 'kn': 'ಬಿಂದುಗಳು', 'ko': '점', 'lo': 'ຈຸດ', 'lt': 'skritulys', 'lv': 'punkts', 'ml': 'പോയിന്റ്', 'mn': 'цэг', 'mr': 'बिंदू', 'my': 'အမှတ်', 'nb': 'punkt', 'ne': 'प्वाइन्ट', 'nl': 'punt', 'nn': 'punkt|point', 'oc': 'punt', 'or': 'ବିନ୍ଦୁ', 'pa_IN': 'ਬਿੰਦੂ', 'pt_BR': 'ponto', 'ro': 'point|punct', 'ru': 'точка', 'sid': 'bixxille', 'si': 'ලක්ෂ්\u200dය', 'sk': 'bod', 'sl': 'točka', 'sq': 'pikë', 'ta': 'புள்ளி', 'te': 'బిందువు', 'th': 'จุด', 'tr': 'nokta', 'ug': 'نۇقتا', 'uk': 'точка', 'vi': 'điểm', 'zh_CN': '点|point', 'zh_TW': '點|point'},
'CIRCLE':{'en_US': 'circle', 'af': 'sirkel', 'am': 'ክብ', 'ar': 'دائرة', 'as': 'বৃত্ত', 'ast': 'círculu', 'be': 'акружына', 'bn_IN': 'বৃত্ত', 'br': "kelc'h", 'bs': 'krug', 'ca': 'cercle', 'ca_valencia': 'cercle', 'ckb': 'بازنە', 'cy': 'cylch', 'cs': 'kruh', 'de': 'kreis', 'dsb': 'krejz', 'el': 'κύκλος|circle', 'eo': 'cirklo', 'es': 'circulo|círculo', 'et': 'ring', 'eu': 'zirkulua', 'fi': 'ympyrä', 'fr': 'cercle', 'fy': 'sirkel', 'gd': 'cearcall', 'gl': 'círculo', 'gug': 'círculo', 'gu': 'વતૃળ', 'he': 'עיגול', 'hi': 'वृत्त', 'hr': 'krug', 'hsb': 'kruh', 'hu': 'kör', 'id': 'lingkaran', 'is': 'hringur', 'ja': '円|circle', 'kab': 'taẓayert', 'ka': 'წრე', 'kk': 'шеңбер', 'km': 'រង្វង់', 'kn': 'ವೃತ್ತ', 'ko': '원', 'lo': 'ວົງມົນ', 'lt': 'apskritimas', 'lv': 'riņķis', 'ml': 'വൃത്തം', 'mn': 'тойрог', 'mr': 'वर्तुळ', 'my': 'စက်ဝိုင်းပုံစံ', 'nb': 'sirkel', 'ne': 'वृत्त', 'nl': 'cirkel', 'nn': 'sirkel|circle', 'oc': 'cercle', 'or': 'ବୃତ୍ତ', 'pa_IN': 'ਚੱਕਰ', 'pt_BR': 'círculo|circunferência', 'ro': 'circle|cerc', 'ru': 'круг', 'sid': 'doyicho', 'si': 'කවය', 'sk': 'kruh', 'sl': 'krog', 'sq': 'rreth', 'ta': 'வட்டம்', 'te': 'వృత్తము', 'th': 'วงกลม', 'tr': 'daire', 'ug': 'چەمبەر', 'uk': 'коло', 'vec': 'sercio', 'vi': 'Tròn', 'zh_CN': '圆|circle', 'zh_TW': '圓|circle'},
'ELLIPSE':{'en_US': 'ellipse', 'af': 'ellips', 'am': 'ኤሊፕስ', 'ar': 'بيضاوي', 'as': 'উপবৃত্ত', 'ast': 'elipse', 'be': 'эліпс', 'bn_IN': 'উপবৃত্ত', 'br': 'elipsenn', 'bs': 'elipsa', 'ca': 'el·lipse', 'ca_valencia': 'el·lipse', 'ckb': 'هێلکەیی', 'cy': 'elips', 'cs': 'elipsa', 'dsb': 'elipsa', 'el': 'έλλειψη|ellipse', 'eo': 'elipso', 'es': 'elipse', 'et': 'ellips', 'eu': 'elipsea', 'fi': 'ellipsi', 'fy': 'elips', 'gd': 'eileaps', 'gl': 'elipse', 'gug': 'elipse', 'gu': 'ઉપવલય', 'he': 'אליפסה', 'hi': 'दीर्घवृत्त', 'hr': 'elipsa', 'hsb': 'elipsa', 'hu': 'ellipszis', 'id': 'elips', 'is': 'sporbaugur', 'ja': '楕円|ellipse', 'kab': 'taglayt', 'ka': 'ელიფსი', 'kk': 'эллипс', 'km': 'ពង\u200bក្រពើ', 'kn': 'ದೀರ್ಘವೃತ್ತ', 'ko': '타원', 'lo': 'ຮູບວົງລີ', 'lt': 'elipsė', 'lv': 'elipse', 'ml': 'അണ്ഡവൃത്തം', 'mn': 'эллипс', 'mr': 'दीर्घवृत्त', 'my': 'ဘဲဥပုံ', 'ne': 'दीर्घवृत्त', 'nl': 'ellips', 'oc': 'ellipsa', 'or': 'ଅଣ୍ଡାକୃତ', 'pa_IN': 'ਅੰਡਾਕਾਰ', 'pt_BR': 'elipse', 'ro': 'ellipse|elipsă', 'ru': 'эллипс', 'sid': 'suduudaamo', 'si': 'ඉලිප්සය', 'sk': 'elipsa', 'sl': 'elipsa', 'sq': 'elips', 'ta': 'நீள்வட்டம்', 'te': 'దీర్ఘవృత్తం', 'th': 'วงรี', 'tr': 'elips', 'ug': 'ئېللىپىس', 'uk': 'еліпс', 'vec': 'ełise', 'vi': 'Bầu dục', 'zh_CN': '椭圆|ellipse', 'zh_TW': '橢圓|ellipse'},
'SQUARE':{'en_US': 'square', 'af': 'vierkant', 'am': 'ስኴር', 'ar': 'مربع', 'as': 'বৰ্গ', 'ast': 'cuadráu', 'be': 'квадрат', 'bn_IN': 'বর্গক্ষেত্র', 'br': 'karrez', 'bs': 'kvadrat', 'ca': 'quadrat', 'ca_valencia': 'quadrat', 'ckb': 'چوارگۆشە', 'cy': 'sgwâr', 'cs': 'čtverec', 'de': 'quadrat', 'dsb': 'kwadrat', 'el': 'τετράγωνο|square', 'eo': 'kvadrato', 'es': 'cuadrado', 'et': 'ruut', 'eu': 'karratu', 'fi': 'neliö', 'fr': 'carré', 'fy': 'fjouwerkant', 'gd': 'ceàrnag', 'gl': 'cadrado', 'gug': 'cuadrado', 'gu': 'ચોરસ', 'he': 'ריבוע', 'hi': 'वर्ग', 'hr': 'kvadrat', 'hsb': 'kwadrat', 'hu': 'négyzet', 'id': 'kotak', 'is': 'ferningur', 'ja': '正方形|square', 'kab': 'amkuẓ', 'ka': 'კვადრატი', 'kk': 'шаршы', 'km': 'ការេ', 'kn': 'ಚೌಕ', 'ko': '정사각형', 'lo': 'ສີ່ຫລ່ຽມຈັດຕຸລັດ', 'lt': 'kvadratas', 'lv': 'kvadrāts', 'ml': 'സമചതുരം', 'mn': 'квадрат', 'mr': 'चौरस', 'my': 'စတုရန်း', 'nb': 'kvadrat', 'ne': 'वर्ग', 'nl': 'vierkant', 'nn': 'kvadrat|square', 'oc': 'carrat', 'or': 'ବର୍ଗାକାର', 'pa_IN': 'ਵਰਗ', 'pt_BR': 'quadrado', 'ro': 'square|pătrat', 'ru': 'квадрат', 'sid': 'isikuwere', 'si': 'සමචතුරශ්\u200dරය', 'sk': 'štvorec', 'sl': 'kvadrat', 'sq': 'katror', 'ta': 'சதுரம்', 'te': 'చతురస్రము', 'th': 'สี่เหลี่ยมจัตุรัส', 'tr': 'kare', 'ug': 'كۋادرات', 'uk': 'квадрат', 'vec': 'cuadrà', 'vi': 'Vuông', 'zh_CN': '正方形|方|square', 'zh_TW': '正方形|方|square'},
'RECTANGLE':{'en_US': 'rectangle', 'af': 'reghoek', 'am': 'አራት ማእዘን', 'ar': 'مستطيل', 'as': 'আয়ত', 'ast': 'rectángulu', 'be': 'прамавугольнік', 'bn_IN': 'আয়তক্ষেত্র', 'br': 'reizhkorneg', 'bs': 'pravougao', 'ckb': 'لاکێشە', 'cy': 'petryal', 'cs': 'obdélník', 'de': 'rechteck', 'dsb': 'pšawokut', 'el': 'ορθογώνιο|rectangle', 'eo': 'ortangulo', 'es': 'rectángulo', 'et': 'ristkülik', 'eu': 'laukizuzena', 'fi': 'suorakulmio', 'fy': 'rjochthoek', 'gd': 'ceart-cheàrnach', 'gl': 'rectángulo', 'gug': 'rectángulo', 'gu': 'લંબચોરસ', 'he': 'מלבן', 'hi': 'आयत', 'hr': 'pravokutnik', 'hsb': 'praworóžk', 'hu': 'téglalap', 'id': 'persegi', 'is': 'rétthyrningur', 'ja': '長方形|rectangle', 'kab': 'asrem', 'ka': 'მართკუთხედი', 'kk': 'тіктөртбұрыш', 'km': 'ចតុកោណកែង', 'kn': 'ಆಯತ', 'ko': '직사각형', 'lo': 'ສີຫລ່ຽມສາກ', 'lt': 'stačiakampis', 'lv': 'taisnstūris', 'ml': 'ചതുര്ഭുജം', 'mn': 'тэгш өнцөгт', 'mr': 'आयत', 'my': 'ထောင့်မှန်စတုဂံ', 'nb': 'rektangel', 'ne': 'आयात', 'nl': 'rechthoek', 'nn': 'rektangel|rectangle', 'or': 'ଆୟତକ୍ଷେତ୍ର', 'pa_IN': 'ਚਤੁਰਭੁਜ', 'pt_BR': 'retângulo', 'ro': 'rectangle|dreptunghi', 'ru': 'прямоугольник', 'sid': 'rekitaangile', 'si': 'ත්\u200dරිකෝණය', 'sk': 'obdĺžnik', 'sl': 'pravokotnik', 'sq': 'drejtkëndësh', 'ta': 'செவ்வகம்', 'te': 'దీర్ఘ చతురస్రము', 'th': 'สี่เหลี่ยมผืนผ้า', 'tr': 'dikdörtgen', 'ug': 'تىك تۆت بۇلۇڭ', 'uk': 'прямокутник', 'vec': 'retàngoło', 'vi': 'Hình chữ nhật', 'zh_CN': '矩形|矩|长方形|rectangle', 'zh_TW': '長方形|矩|rectangle'},
'LABEL':{'en_US': 'label', 'af': 'byskrif', 'am': 'ምልክት', 'ar': 'اللصيقة', 'as': 'লেবেল', 'ast': 'etiqueta', 'be': 'метка', 'bn_IN': 'লেবেল', 'br': 'tikedenn', 'bs': 'naljepnica', 'ca': 'etiqueta', 'ca_valencia': 'etiqueta', 'ckb': 'پێناسکراو', 'cs': 'text', 'de': 'schreibe', 'dgo': 'लेबल', 'dsb': 'pomjenjenje', 'el': 'ετικέτα|label', 'eo': 'etikedo', 'es': 'etiqueta', 'et': 'silt', 'eu': 'etiketa', 'fi': 'selite', 'fr': 'étiquette', 'fy': 'lebel', 'gd': 'leubail', 'gl': 'etiqueta', 'gug': 'etiqueta', 'gu': 'લેબલ', 'he': 'תווית', 'hi': 'लेबल', 'hr': 'oznaka', 'hsb': 'popis', 'hu': 'címke', 'is': 'skýring', 'ja': 'ラベル|label', 'kab': 'tabzimt', 'kk': 'белгі', 'km': 'ស្លាក', 'kn': 'ಲೇಬಲ್', 'ko': '레이블', 'lo': 'ປ້າຍ', 'lt': 'piešk.tekstą|pštk', 'lv': 'etiķete', 'ml': 'ലേബല്\u200d', 'mn': 'пайз', 'mr': 'लेबल', 'nb': 'etikett', 'nl': 'bijschrift', 'nn': 'etikett|label', 'oc': 'etiqueta', 'or': 'ନାମପଟି', 'pa_IN': 'ਲੇਬਲ', 'pt_BR': 'rotular|rotule', 'ro': 'label|etichetă', 'ru': 'надпись', 'sa_IN': 'अंकितकम्', 'sat': 'चिखना़', 'sd': 'ليبلُ', 'sid': 'somaasincho', 'si': 'ලේබලය', 'sk': 'popis', 'sl': 'oznaka', 'sq': 'etiketë', 'ta': 'லேபிள்', 'te': 'లేబుల్', 'th': 'ป้ายกำกับ', 'tr': 'etiket', 'ug': 'ئەن', 'uk': 'напис', 'vi': 'Nhãn', 'zh_CN': '标签|label', 'zh_TW': '標籤|label'},
'PENCOLOR':{'en_US': 'pencolor|pencolour|linecolor|pc', 'af': 'penkleur|lynkleur|pk', 'am': 'የ ብዕር ቀለም|የ ብዕር ቀለም|የ መስመር ቀለም|ብ/ቀ', 'ar': 'لون_القلم', 'ast': 'llapizcolor|lliniacolor|lc', 'bs': 'bojaolovke|bojaolovke|bojalinije|pc', 'ca': 'color.llapis|color.línia|cl', 'ca_valencia': 'color.llapis|color.línia|cl', 'ckb': '\u202bpencolor|pencolour|linecolor|pc', 'cs': 'barvapera|barvačáry|bp', 'de': 'stiftfarbe|linienfarbe|sf|lf', 'dsb': 'barwapisaka|barwalinije|bp|bl', 'el': 'χρώμαγραφίδας|χρώμαγραμμής|χγ|pencolor|pencolour|linecolor|pc', 'eo': 'plumkoloro|linikoloro|pk', 'es': 'color.lápiz|color.línea|cl', 'et': 'pliiatsi_värv|joonevärv|pv|jv', 'eu': 'lumakolorea|marrakolorea|lk', 'fi': 'kynänväri|kv', 'fr': 'couleurcrayon|ccrayon|couleurligne|cc', 'fy': 'penkleur|linekleur|pk', 'gl': 'estilocor|estilodacor|liñadecor|ec', 'gug': "sa'y.lápiz|sa'y.línea|cl", 'he': 'צבעעט|צבעט|צבעקו|צק', 'hr': 'boja olovke|boja olovke|boja linije|bo', 'hsb': 'barbapisaka|barbalinije|bp|bl', 'hu': 'tollszín|tollszín!|tsz!?|vonalszín', 'is': 'pennalitur|línulitur|pl', 'ja': 'ペンの色|線の色|pencolor|pc', 'kab': 'iniakeryun|inakeryun|inizirg|nk', 'kk': 'қалам_түсі|сызық_түсі|т', 'km': 'ពណ៌\u200bប៉ិក|pencolour|linecolor|pc', 'kn': 'ಲೇಖನಿಬಣ್ಣ|ಲೇಖನಿಯಬಣ್ಣ|ರೇಖೆಬಣ್ಣ|pc', 'ko': '펜색상|펜색상|선색상|pc', 'lo': 'pencolor|ສີປາກກາ|ສີເສັ້ນ|pc', 'lt': 'pieštuko.spalva|linijos.spalva|psp', 'lv': 'spalvas_krāsa|linijas_krāsa|sk', 'mn': 'үзэгний өнгө|шугамын өнгө|үөн|шөн', 'mr': 'पेनकलर|पेनकलर|लाइनकलर|pc', 'nb': 'pennfarge|pencolour|pf|linjefarge|pc', 'nl': 'penkleur|lijnkleur|pk', 'nn': 'pennfarge|linjefarge|pf|pencolor', 'oc': 'colorgredon|cgredon|colorlinha|cc', 'or': 'ପେନରଙ୍ଗ|ପେନରଙ୍ଗ|ଧାଡ଼ିରଙ୍ଗ|pc', 'pt_BR': 'mudarCorDoLápis|mCorLa|mudeCorDoLápis|mudecl', 'ro': 'pencolor|pencolour|linecolor|pc|culoarestilou', 'ru': 'цвет_пера|цвет_линии|ц', 'sid': 'biiretekuula|biiretekuula|xuruurukuula|bk', 'sk': 'farbapera|farbačiary|fp|fč', 'sl': 'barvaperesa|barvačrte|bp', 'sq': 'ngjyraestilolapsit|ngjyraestilolapsit|ngjyraevijës|pc', 'th': 'pencolor|สีปากกา|สีเส้น|pc', 'tr': 'kalemrengi|kalemrengi|satırrengi|kr', 'uk': 'колір_пера|колір_лінії|кп', 'vec': 'cołorpena|cołorpena|cołorłinea|pc', 'zh_CN': '笔颜色|笔色|线颜色|线色|pencolor|pencolour|linecolor|pc', 'zh_TW': '筆顏色|筆色|線顏色|線色|筆色彩|線色彩|pencolor|pencolour|linecolor|pc'},
'ANY':{'en_US': 'any', 'af': 'elke', 'am': 'ማንኛውም', 'ar': 'أي', 'as': 'যিকোনো', 'ast': 'cualquier', 'bn_IN': 'যেকোনো', 'bs': 'bilo koji', 'ca': 'qualsevol', 'ca_valencia': 'qualsevol', 'ckb': 'هەریەکێک', 'cy': 'unrhyw un', 'cs': 'libovolně|lib', 'de': 'beliebig|bel', 'dgo': 'कोई बी', 'dsb': 'někaki|nk', 'el': 'όλα|any', 'eo': 'ajna', 'es': 'cualquiera', 'et': 'mistahes', 'eu': 'edozein', 'fi': 'jokin', 'fr': 'tout', 'fy': 'elke', 'gd': 'gin', 'gl': 'calquera', 'gug': 'mavave', 'gu': 'કોઇપણ', 'he': 'כלשהו', 'hi': 'कोई भी', 'hr': 'bilo koja', 'hsb': 'někajki', 'hu': 'tetszőleges|tetsz', 'is': 'hvað sem er', 'ja': 'なんでも|どこでも|乱数|any', 'kab': 'a win yellan', 'kk': 'кез-келген', 'km': 'ណាមួយ', 'kn': 'ಯಾವುದಾದರೂ', 'kok': 'कोणूयKoncheim', 'ko': '모두', 'lo': 'ໃດໆ', 'lt': 'bet.kas', 'lv': 'jebkurš', 'ml': 'ഏതെങ്കിലും', 'mn': 'хамаагүй|хам', 'mr': 'कोणतेहि', 'nb': 'alle', 'nl': 'elke', 'nn': 'alle|any', 'oc': 'tot', 'or': 'ଯେକୌଣସି', 'pa_IN': 'ਕੋਈ ਵੀ', 'pt_BR': 'qualquer', 'ro': 'any|oricare', 'ru': 'любой', 'sa_IN': 'कांह तॊ', 'sat': 'जाहां गे', 'sd': 'ڪابە', 'sid': 'ayee', 'sk': 'ľubovoľný', 'sl': 'poljubno', 'sq': 'çdo', 'te': 'ఏదైనా', 'th': 'ใดๆ', 'tr': 'herhangi', 'uk': 'довільний', 'vec': 'tuto', 'zh_CN': '任意|any', 'zh_TW': '任一|any'},
'PENWIDTH':{'en_US': 'pensize|penwidth|linewidth|ps', 'af': 'pengrootte|pendikte|penbreedte|pg', 'am': 'የ ብዕር መጠን |የ ብዕር ስፋት |የ መስመር ስፋት |ps', 'ar': 'حجم_القلم|عرض_القلم', 'ast': 'llapiztamañu|llapizanchor|lliniaanchor|lt', 'bs': 'veličinaolovke|širinaolovke|širinalinije|ps', 'ca': 'mida.llapis|mida.línia|ml', 'ca_valencia': 'mida.llapis|mida.línia|ml', 'ckb': '\u202bpensize|penwidth|linewidth|ps', 'cs': 'tloušťkapera|tloušťkačáry|tp', 'de': 'stiftbreite|linienbreite|sb|lb', 'dsb': 'wjelikosćpisaka|šyrokosćpisaka|šyrokosćlinije|wp|šl', 'el': 'μέγεθοςγραφίδας|πλάτοςγραφίδας|πλάτοςγραμμής|μγ|pensize|penwidth|linewidth|ps', 'eo': 'plumlarĝo|plumgrando|pg', 'es': 'tamaño.lápiz|tl', 'et': 'pliiatsi_paksus|pliiatsi_jämedus|joonepaksus|joonelaius|jl', 'eu': 'lumatamaina|lumazabalera|marrazabalera|lz', 'fi': 'kynänleveys|kl', 'fr': 'tailleplume|taillecrayon|largeurligne|ta', 'fy': 'pengrutte|pentsjokte|penbreedte|pg', 'gl': 'estilotamaño|estilolargura|larguraliña|et', 'gug': 'tamaño.bolígrafo|tb', 'he': 'גודלעט|עוביעט|רוחבעט|גע', 'hr': 'veličina olovke|širina olovke|širina linije|vo', 'hsb': 'wulkosćpisaka|šěrokosćpisaka|šěrokosćlinije|wp|wl', 'hu': 'tollvastagság|tollvastagság!|tv!?|vonalvastagság', 'is': 'pennastærð|pennabreidd|línubreidd|ps', 'ja': 'ペンの太さ|線の太さ|penwidth|ps', 'kab': 'teɣziastillu|teɣziakeryun|tehrizirg|ts', 'kk': 'қалам_өлшемі|қалам_жуандығы|сызық_жуандығы|сж', 'km': 'ទំហំ\u200bប៉ិក|penwidth|linewidth|ps', 'kn': 'ಲೇಖನಿಗಾತ್ರ|ಲೇಖನಅಗಲ|ರೇಖೆಅಗಲ|ps', 'ko': '펜크기|펜너비|선너비|ps', 'lo': 'ຂະໜາດປາກກາ|ຄວາມກ້ວາງປາກກາ|ຄວາມກ້ວາງຂອງເສັ້ນ|ps', 'lt': 'pieštuko.storis|linijos.storis|ps', 'lv': 'spalvas_izmērs|spalvas_platums|līnijas_platums|sp', 'mn': 'үзэгний өргөн|шугамын өргөн|үөр|шөр', 'mr': 'पेनआकार|पेनरूंदी|रेघरूंदी|ps', 'nb': 'pennstørrelse|pennbredde|linjebredde|ps', 'nl': 'pengrootte|pendikte|penbreedte|pg', 'nn': 'pennstorleik|pennbreidd|linjebreidd|ps|pens | |