1#************************************************************** 2# 3# Licensed to the Apache Software Foundation (ASF) under one 4# or more contributor license agreements. See the NOTICE file 5# distributed with this work for additional information 6# regarding copyright ownership. The ASF licenses this file 7# to you under the Apache License, Version 2.0 (the 8# "License"); you may not use this file except in compliance 9# with the License. You may obtain a copy of the License at 10# 11# http://www.apache.org/licenses/LICENSE-2.0 12# 13# Unless required by applicable law or agreed to in writing, 14# software distributed under the License is distributed on an 15# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16# KIND, either express or implied. See the License for the 17# specific language governing permissions and limitations 18# under the License. 19# 20#************************************************************** 21 22# usage: runpy.sh fixtures/p8/numfmt_check.py <port> <outdir> 23# Loads an ODS whose cells use number styles with number:min-decimal-places and number:fill-character (ODF 1.3) and 24# compares the format code and the displayed text of each cell with the expected ones. Rows marked "control" must give the 25# same result before and after the reader change. Exit code 1 when any row differs. 26import os, sys, zipfile 27import uno 28from unohelp import connect, desktop, load 29 30port, out = int(sys.argv[1]), os.path.abspath(sys.argv[2]) 31os.makedirs(out, exist_ok=True) 32 33NS = ('xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" ' 34 'xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" ' 35 'xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" ' 36 'xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0"') 37 38# name, number-style content, cell value, expected format code (None: only checked by the regex below), expected text 39# (suffix match); the number-style is always an en-US one 40# sci-min1-of-3-display: the format code is right (ODF wants 1.5E+05), but the scientific output of SvNumberformat shows the optional 41# digits as zeros; the row pins that known limit 42CASES = [ 43 ("min1-of-3", '<number:number number:decimal-places="3" number:min-decimal-places="1" number:min-integer-digits="1"/>', 1.5, "0.0##", "1.5"), 44 ("min1-of-3-integer", '<number:number number:decimal-places="3" number:min-decimal-places="1" number:min-integer-digits="1"/>', 2, "0.0##", "2.0"), 45 ("min1-of-3-full", '<number:number number:decimal-places="3" number:min-decimal-places="1" number:min-integer-digits="1"/>', 1.2345, "0.0##", "1.235"), 46 ("min0-of-3", '<number:number number:decimal-places="3" number:min-decimal-places="0" number:min-integer-digits="1"/>', 1.5, "0.###", "1.5"), 47 ("min-equals-places", '<number:number number:decimal-places="2" number:min-decimal-places="2" number:min-integer-digits="1"/>', 1.5, "0.00", "1.50"), 48 ("control-no-min", '<number:number number:decimal-places="2" number:min-integer-digits="1"/>', 1.5, "0.00", "1.50"), 49 ("control-var-dec", '<number:number number:decimal-places="2" number:decimal-replacement="" number:min-integer-digits="1"/>', 1.5, "0.##", "1.5"), 50 ("var-dec-and-min1", '<number:number number:decimal-places="3" number:decimal-replacement="" number:min-decimal-places="1" number:min-integer-digits="1"/>', 1.5, "0.0##", "1.5"), 51 ("min-above-places", '<number:number number:decimal-places="2" number:min-decimal-places="5" number:min-integer-digits="1"/>', 1.5, "0.00", "1.50"), 52 ("control-dashes", '<number:number number:decimal-places="2" number:decimal-replacement="-" number:min-integer-digits="1"/>', 2, "0.--", "2.--"), 53 ("dashes-and-min1", '<number:number number:decimal-places="2" number:decimal-replacement="-" number:min-decimal-places="1" number:min-integer-digits="1"/>', 2, "0.--", "2.--"), 54 ("sci-min1-of-3-display", '<number:scientific-number number:decimal-places="3" number:min-decimal-places="1" number:min-integer-digits="1" number:min-exponent-digits="2"/>', 150000, "0.0##E+00", "1.500E+05"), 55 ("control-sci", '<number:scientific-number number:decimal-places="3" number:min-integer-digits="1" number:min-exponent-digits="2"/>', 150000, "0.000E+00", "1.500E+05"), 56 ("fill-before", '<number:fill-character>.</number:fill-character><number:number number:decimal-places="2" number:min-integer-digits="1"/>', 1.5, "*.0.00", "1.50"), 57 ("fill-after-text", '<number:text>$</number:text><number:fill-character>-</number:fill-character><number:number number:decimal-places="2" number:min-integer-digits="1"/>', 1.5, '"$"*-0.00', "1.50"), 58 ("fill-twice", '<number:fill-character>.</number:fill-character><number:fill-character>-</number:fill-character><number:number number:decimal-places="2" number:min-integer-digits="1"/>', 1.5, "*.0.00", "1.50"), 59 ("fill-two-chars", '<number:fill-character>ab</number:fill-character><number:number number:decimal-places="2" number:min-integer-digits="1"/>', 1.5, "0.00", "1.50"), 60 ("fill-empty", '<number:fill-character/><number:number number:decimal-places="2" number:min-integer-digits="1"/>', 1.5, "0.00", "1.50"), 61] 62 63styles, cells = [], [] 64for i, (name, content, value, _, _) in enumerate(CASES): 65 styles.append('<number:number-style style:name="N%d" number:language="en" number:country="US">%s</number:number-style>' % (i, content)) 66 styles.append('<style:style style:name="ce%d" style:family="table-cell" style:data-style-name="N%d"/>' % (i, i)) 67 cells.append('<table:table-row><table:table-cell table:style-name="ce%d" office:value-type="float" office:value="%s"/></table:table-row>' % (i, value)) 68 69content = ('<?xml version="1.0" encoding="UTF-8"?>\n<office:document-content %s office:version="1.3"><office:automatic-styles>%s' 70 '</office:automatic-styles><office:body><office:spreadsheet><table:table table:name="T">%s</table:table>' 71 '</office:spreadsheet></office:body></office:document-content>' % (NS, "".join(styles), "".join(cells))) 72manifest = ('<?xml version="1.0" encoding="UTF-8"?>\n<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" ' 73 'manifest:version="1.3"><manifest:file-entry manifest:full-path="/" manifest:version="1.3" ' 74 'manifest:media-type="application/vnd.oasis.opendocument.spreadsheet"/>' 75 '<manifest:file-entry manifest:full-path="content.xml" manifest:media-type="text/xml"/></manifest:manifest>') 76path = os.path.join(out, "numfmt.ods") 77with zipfile.ZipFile(path, "w") as z: 78 z.writestr("mimetype", "application/vnd.oasis.opendocument.spreadsheet", zipfile.ZIP_STORED) 79 z.writestr("META-INF/manifest.xml", manifest, zipfile.ZIP_DEFLATED) 80 z.writestr("content.xml", content, zipfile.ZIP_DEFLATED) 81 82ctx = connect(port) 83doc = load(desktop(ctx), path) 84sheet = doc.Sheets.getByIndex(0) 85formats = doc.NumberFormats 86fails = 0 87for i, (name, _, _, want_fmt, want_txt) in enumerate(CASES): 88 cell = sheet.getCellByPosition(0, i) 89 fmt = formats.getByKey(cell.NumberFormat).FormatString 90 txt = cell.String 91 ok = (fmt == want_fmt) and txt.endswith(want_txt) 92 fails += not ok 93 print("%-4s %-20s format=%-12r text=%-12r want %r / %r" % ("ok" if ok else "FAIL", name, fmt, txt, want_fmt, want_txt)) 94doc.close(True) 95print("%d of %d rows ok" % (len(CASES) - fails, len(CASES))) 96sys.exit(1 if fails else 0) 97