aboutsummaryrefslogtreecommitdiff
path: root/pip-bins/odflint
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukasz.kasprzak@pm.me>2026-03-19 15:05:32 +0100
committerLukasz Kasprzak <lukasz.kasprzak@pm.me>2026-03-19 15:05:32 +0100
commitb1844d805a8f190af00faf3f6e5bed7d997ecaae (patch)
treed6296ba5e85034836e203a542d2ad34a9e5c5b32 /pip-bins/odflint
parentc74163f1ecbd809c83bb465ee89fa04369a89972 (diff)
downloadbin-b1844d805a8f190af00faf3f6e5bed7d997ecaae.tar.gz
bin-b1844d805a8f190af00faf3f6e5bed7d997ecaae.zip
created sorter for sorting downloads with custom rules; created straper for recreation of server
Diffstat (limited to 'pip-bins/odflint')
-rwxr-xr-xpip-bins/odflint216
1 files changed, 216 insertions, 0 deletions
diff --git a/pip-bins/odflint b/pip-bins/odflint
new file mode 100755
index 0000000..e5da6bb
--- /dev/null
+++ b/pip-bins/odflint
@@ -0,0 +1,216 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+# Copyright (C) 2009 Søren Roug, European Environment Agency
+#
+# This is free software. You may redistribute it under the terms
+# of the Apache license and the GNU General Public License Version
+# 2 or at your option any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public
+# License along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Contributor(s):
+#
+import zipfile
+from xml.sax import make_parser,handler
+from xml.sax.xmlreader import InputSource
+import xml.sax.saxutils
+import sys
+from odf.opendocument import OpenDocument
+from odf import element, grammar
+from odf.namespaces import *
+from odf.attrconverters import attrconverters, cnv_string
+
+from io import BytesIO
+
+if sys.version_info[0]==3: unicode=str
+
+extension_attributes = {
+ "OpenOffice.org" : {
+ (METANS,u'template'): (
+ (XLINKNS,u'role'),
+ ),
+ (STYLENS,u'graphic-properties'): (
+ (STYLENS,u'background-transparency'),
+ ),
+ (STYLENS,u'paragraph-properties'): (
+ (TEXTNS,u'enable-numbering'),
+ (STYLENS,u'join-border'),
+ ),
+ (STYLENS,u'table-cell-properties'): (
+ (STYLENS,u'writing-mode'),
+ ),
+ (STYLENS,u'table-row-properties'): (
+ (STYLENS,u'keep-together'),
+ ),
+ },
+ "KOffice" : {
+ (STYLENS,u'graphic-properties'): (
+ (KOFFICENS,u'frame-behavior-on-new-page'),
+ ),
+ (DRAWNS,u'page'): (
+ (KOFFICENS,u'name'),
+ ),
+ (PRESENTATIONNS,u'show-shape'): (
+ (KOFFICENS,u'order-id'),
+ ),
+ (PRESENTATIONNS,u'hide-shape'): (
+ (KOFFICENS,u'order-id'),
+ ),
+ (CHARTNS,u'legend'): (
+ (KOFFICENS,u'title'),
+ ),
+ }
+}
+
+printed_errors = []
+
+def print_error(str):
+ if str not in printed_errors:
+ printed_errors.append(str)
+ print (str)
+
+def chop_arg(arg):
+ if len(arg) > 20:
+ return "%s..." % arg[0:20]
+ return arg
+
+def make_qname(tag):
+ return "%s:%s" % (nsdict.get(tag[0],tag[0]), tag[1])
+
+def allowed_attributes(tag):
+ return grammar.allowed_attributes.get(tag)
+
+
+class ODFElementHandler(handler.ContentHandler):
+ """ Extract headings from content.xml of an ODT file """
+ def __init__(self, document):
+ self.doc = document
+ self.tagstack = []
+ self.data = []
+ self.currtag = None
+
+ def characters(self, data):
+ self.data.append(data)
+
+ def startElementNS(self, tag, qname, attrs):
+ """ Pseudo-create an element
+ """
+ allowed_attrs = grammar.allowed_attributes.get(tag)
+ attrdict = {}
+ for (att,value) in attrs.items():
+ prefix = nsdict.get(att[0],att[0])
+ # Check if it is a known extension
+ notan_extension = True
+ for product, ext_attrs in extension_attributes.items():
+ allowed_ext_attrs = ext_attrs.get(tag)
+ if allowed_ext_attrs and att in allowed_ext_attrs:
+ print_error("Warning: Attribute %s in element <%s> is illegal - %s extension" % ( make_qname(att), make_qname(tag), product))
+ notan_extension = False
+ # Check if it is an allowed attribute
+ if notan_extension and allowed_attrs and att not in allowed_attrs:
+ print_error("Error: Attribute %s:%s is not allowed in element <%s>" % ( prefix, att[1], make_qname(tag)))
+ # Check the value
+ try:
+ convert = attrconverters.get(att, cnv_string)
+ convert(att, value, tag)
+ except ValueError as res:
+ print_error("Error: Bad value '%s' for attribute %s:%s in tag: <%s> - %s" %
+ (chop_arg(value), prefix, att[1], make_qname(tag), res))
+
+ self.tagstack.append(tag)
+ self.data = []
+ # Check that the parent allows this child element
+ if tag not in ( (OFFICENS, 'document'), (OFFICENS, 'document-content'), (OFFICENS, 'document-styles'),
+ (OFFICENS, 'document-meta'), (OFFICENS, 'document-settings'),
+ (MANIFESTNS,'manifest')):
+ try:
+ parent = self.tagstack[-2]
+ allowed_children = grammar.allowed_children.get(parent)
+ except:
+ print_error("Error: This document starts with the wrong tag: <%s>" % make_qname(tag))
+ allowed_children = None
+ if allowed_children and tag not in allowed_children:
+ print_error("Error: Element %s is not allowed in element %s" % ( make_qname(tag), make_qname(parent)))
+ # Test that all mandatory attributes have been added.
+ required = grammar.required_attributes.get(tag)
+ if required:
+ for r in required:
+ if attrs.get(r) is None:
+ print_error("Error: Required attribute missing: %s in <%s>" % (make_qname(r), make_qname(tag)))
+
+
+ def endElementNS(self, tag, qname):
+ self.currtag = self.tagstack.pop()
+ str = ''.join(self.data).strip()
+ # Check that only elements that can take text have text
+ # But only elements we know exist in grammar
+ if tag in grammar.allowed_children:
+ if str != '' and tag not in grammar.allows_text:
+ print_error("Error: %s does not allow text data" % make_qname(tag))
+ self.data = []
+
+class ODFDTDHandler(handler.DTDHandler):
+ def notationDecl(self, name, public_id, system_id):
+ """ Ignore DTDs """
+ print_error("Warning: ODF doesn't use DOCTYPEs")
+
+def exitwithusage(exitcode=2):
+ """ print out usage information """
+ sys.stderr.write("Usage: %s inputfile\n" % sys.argv[0])
+ sys.stderr.write("\tInputfile must be OpenDocument format\n")
+ sys.exit(exitcode)
+
+def lint(odffile):
+ if not zipfile.is_zipfile(odffile):
+ print_error("Error: This is not a zipped file")
+ return
+ zfd = zipfile.ZipFile(odffile)
+ try:
+ mimetype = zfd.read('mimetype')
+ except:
+ mimetype=''
+ d = OpenDocument(unicode(mimetype))
+ first = True
+ for zi in zfd.infolist():
+ if first:
+ if zi.filename == 'mimetype':
+ if zi.compress_type != zipfile.ZIP_STORED:
+ print_error("Error: The 'mimetype' member must be stored - not deflated")
+ if zi.comment != "":
+ print_error("Error: The 'mimetype' member must not have extra header info")
+ else:
+ print_error("Warning: The first member in the archive should be the mimetype")
+ first = False
+ if zi.filename in ('META-INF/manifest.xml', 'content.xml', 'meta.xml', 'styles.xml', 'settings.xml'):
+ content = zfd.read(zi.filename)
+ parser = make_parser()
+ parser.setFeature(handler.feature_namespaces, True)
+ parser.setFeature(handler.feature_external_ges, False)
+ parser.setContentHandler(ODFElementHandler(d))
+ dtdh = ODFDTDHandler()
+ parser.setDTDHandler(dtdh)
+ parser.setErrorHandler(handler.ErrorHandler())
+
+ inpsrc = InputSource()
+ if not isinstance(content, str):
+ content=content
+ inpsrc.setByteStream(BytesIO(content))
+ parser.parse(inpsrc)
+
+
+if len(sys.argv) != 2:
+ exitwithusage()
+lint(unicode(sys.argv[1]))
+
+
+
+# Local Variables: ***
+# mode: python ***
+# End: ***