xref: /trunk/test/odf-validate/fixtures/styles-text/make.py (revision 0e16a31d78a652c56c5c0fc79c752f48b9048df9)
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# usage: runpy.sh fixtures/styles-text/make.py <port> <outdir> [section ...]
22# Character, paragraph, list, section, ruby and number-format properties set to each enumerated value, in styles and as direct formatting. Saves with the office's configured ODF version: run it against the strict and extended profiles.
23import sys, os, traceback, uno
24from unohelp import connect, desktop, pv
25
26port, out = int(sys.argv[1]), os.path.abspath(sys.argv[2])
27only = sys.argv[3:]
28os.makedirs(out, exist_ok=True)
29desk = desktop(connect(port))
30failed = []
31SECTIONS = []
32
33
34def section(fn):
35    SECTIONS.append(fn)
36    return fn
37
38
39def new(factory):
40    return desk.loadComponentFromURL(factory, "_blank", 0, (pv("Hidden", True),))
41
42
43def save(doc, name, filt):
44    try:
45        doc.storeToURL(uno.systemPathToFileUrl(os.path.join(out, name)), (pv("FilterName", filt),))
46        print("saved", name, filt)
47    except Exception as e:
48        failed.append(name)
49        print("FAIL", name, filt, type(e).__name__, repr(e)[:200], getattr(e, "Message", ""))
50
51
52def setp(obj, name, value, tag=""):
53    try:
54        if isinstance(value, uno.Any):
55            uno.invoke(obj, "setPropertyValue", (name, value))
56        else:
57            obj.setPropertyValue(name, value)
58        return True
59    except Exception as e:
60        print("WARN", tag, name, repr(value)[:60], type(e).__name__, str(e)[:80].replace("\n", " "))
61        return False
62
63
64def setall(obj, props, tag=""):
65    for k, v in props.items():
66        setp(obj, k, v, tag)
67
68
69def const(name):
70    return uno.getConstantByName("com.sun.star.%s" % name)
71
72
73def enum(t, v):
74    return uno.Enum("com.sun.star.%s" % t, v)
75
76
77def consts(prefix, names):
78    return [const("%s.%s" % (prefix, n)) for n in names]
79
80
81def run(name, fn):
82    if only and name not in only:
83        return
84    try:
85        fn()
86    except Exception as e:
87        failed.append(name)
88        print("FAIL", name, type(e).__name__, repr(e)[:300], traceback.format_exc().splitlines()[-4:-1])
89
90
91def norm(v):
92    if isinstance(v, uno.Enum):
93        return (v.typeName, v.value)
94    if isinstance(v, float):
95        return round(v, 3)
96    if isinstance(v, (tuple, list)):
97        return tuple(norm(x) for x in v)
98    if type(v).__name__ == "pyuno":
99        return repr(v)
100    return v
101
102
103def reload(name):
104    return desk.loadComponentFromURL(uno.systemPathToFileUrl(os.path.join(out, name)), "_blank", 0, (pv("Hidden", True), pv("ReadOnly", True), pv("MacroExecutionMode", 0)))
105
106
107def readback(name, family, specs):
108    try:
109        doc = reload(name)
110        fam = doc.StyleFamilies.getByName(family)
111        bad = tot = 0
112        for sname, props in specs:
113            try:
114                st = fam.getByName(sname)
115            except Exception:
116                print("RT-MISSING", name, sname)
117                continue
118            for k, v in props.items():
119                if k.startswith("__"):
120                    continue
121                tot += 1
122                try:
123                    got = st.getPropertyValue(k)
124                except Exception as e:
125                    print("RT-NOPROP", name, sname, k)
126                    bad += 1
127                    continue
128                if norm(got) != norm(v):
129                    bad += 1
130                    print("RT-DIFF", name, sname, k, "set", norm(v), "got", norm(got))
131        print("RT-SUMMARY", name, family, "values", tot, "differing", bad)
132        doc.close(True)
133    except Exception as e:
134        print("RT-FAIL", name, type(e).__name__, str(e)[:100])
135
136
137class Writer:
138    def __init__(self):
139        self.doc = new("private:factory/swriter")
140        self.items = []
141        self.n = 0
142        self.specs = {"CharacterStyles": [], "ParagraphStyles": []}
143
144    def style(self, family, props, parent=None, tag=""):
145        self.n += 1
146        name = "%s%d" % (tag or family[:1], self.n)
147        s = self.doc.createInstance("com.sun.star.style.%s" % ("CharacterStyle" if family == "CharacterStyles" else "ParagraphStyle"))
148        self.doc.StyleFamilies.getByName(family).insertByName(name, s)
149        if parent:
150            s.ParentStyle = parent
151        setall(s, props, name)
152        self.specs[family].append((name, props))
153        return name
154
155    def para(self, text, pstyle=None, cstyle=None, props=None):
156        self.items.append((text, pstyle, cstyle, props))
157
158    def char_style(self, label, props):
159        self.para(label, cstyle=self.style("CharacterStyles", props, tag="C"))
160
161    def par_style(self, label, props, parent=None):
162        self.para(label, pstyle=self.style("ParagraphStyles", props, parent, tag="P"))
163
164    def flush(self):
165        t = self.doc.Text
166        c = t.createTextCursor()
167        for k, i in enumerate(self.items):
168            if k:
169                t.insertControlCharacter(c, const("text.ControlCharacter.PARAGRAPH_BREAK"), False)
170            t.insertString(c, i[0], False)
171        en = t.createEnumeration()
172        for (label, ps, cs, props) in self.items:
173            p = en.nextElement()
174            if ps:
175                p.ParaStyleName = ps
176            if cs:
177                p.CharStyleName = cs
178            if props:
179                setall(p, props, label)
180        return self.doc
181
182
183def W():
184    return Writer()
185
186
187WEIGHTS = [0, 10, 50, 60, 75, 90, 100, 110, 150, 175, 200, 250]
188SLANTS = ["NONE", "OBLIQUE", "ITALIC", "DONTKNOW", "REVERSE_OBLIQUE", "REVERSE_ITALIC"]
189FAMILIES = ["DONTKNOW", "DECORATIVE", "MODERN", "ROMAN", "SCRIPT", "SWISS", "SYSTEM"]
190PITCHES = ["DONTKNOW", "FIXED", "VARIABLE"]
191CHARSETS = ["DONTKNOW", "ANSI", "MAC", "IBMPC_437", "IBMPC_850", "IBMPC_860", "IBMPC_861", "IBMPC_863", "IBMPC_865", "SYSTEM", "SYMBOL"]
192UNDERLINES = list(range(19))
193STRIKES = list(range(7))
194
195
196@section
197def writer_char():
198    def build():
199        w = W()
200        for suf in ("", "Asian", "Complex"):
201            for v in WEIGHTS:
202                w.char_style("weight%s %s" % (suf, v), {"CharWeight" + suf: float(v)})
203            for s in SLANTS:
204                w.char_style("posture%s %s" % (suf, s), {"CharPosture" + suf: enum("awt.FontSlant", s)})
205            for f in FAMILIES:
206                w.char_style("family%s %s" % (suf, f), {"CharFontFamily" + suf: const("awt.FontFamily." + f)})
207            for p in PITCHES:
208                w.char_style("pitch%s %s" % (suf, p), {"CharFontPitch" + suf: const("awt.FontPitch." + p)})
209            for c in CHARSETS:
210                w.char_style("charset%s %s" % (suf, c), {"CharFontCharSet" + suf: const("awt.CharSet." + c)})
211            for f in FAMILIES:
212                for p in PITCHES[1:]:
213                    w.char_style("named%s %s %s" % (suf, f, p), {"CharFontName" + suf: "T12Font%s%s%s" % (suf, f, p), "CharFontFamily" + suf: const("awt.FontFamily." + f), "CharFontPitch" + suf: const("awt.FontPitch." + p), "CharFontStyleName" + suf: "Regular"})
214            w.char_style("diffheight%s" % suf, {"CharDiffHeight" + suf: 2.0})
215            w.char_style("propheight%s" % suf, {"CharPropHeight" + suf: 150})
216            w.char_style("height%s" % suf, {"CharHeight" + suf: 17.5})
217        for v in UNDERLINES:
218            w.char_style("underline %d" % v, {"CharUnderline": v})
219            w.char_style("underline-col %d" % v, {"CharUnderline": v, "CharUnderlineHasColor": True, "CharUnderlineColor": 0xFF0000})
220            w.char_style("underline-word %d" % v, {"CharUnderline": v, "CharWordMode": True})
221            w.char_style("overline %d" % v, {"CharOverline": v})
222            w.char_style("overline-col %d" % v, {"CharOverline": v, "CharOverlineHasColor": True, "CharOverlineColor": 0x00FF00})
223            w.char_style("overline-word %d" % v, {"CharOverline": v, "CharWordMode": True})
224        for v in STRIKES:
225            w.char_style("strike %d" % v, {"CharStrikeout": v})
226            w.char_style("strike-word %d" % v, {"CharStrikeout": v, "CharWordMode": True})
227        for v in range(5):
228            w.char_style("casemap %d" % v, {"CharCaseMap": v})
229        for v in range(3):
230            w.char_style("relief %d" % v, {"CharRelief": v})
231        for v in (0, 1, 2, 3, 4, 11, 12, 13, 14):
232            w.char_style("emphasis %d" % v, {"CharEmphasis": v})
233        for v in (-30, 0, 40):
234            w.char_style("kerning %d" % v, {"CharKerning": v})
235        for v in (True, False):
236            w.char_style("autokern %s" % v, {"CharAutoKerning": v})
237            w.char_style("flash %s" % v, {"CharFlash": v})
238            w.char_style("hidden %s" % v, {"CharHidden": v})
239            w.char_style("shadowed %s" % v, {"CharShadowed": v})
240            w.char_style("contoured %s" % v, {"CharContoured": v})
241            w.char_style("fitline %s" % v, {"CharRotation": 900, "CharRotationIsFitToLine": v})
242            w.char_style("combine %s" % v, {"CharCombineIsOn": v, "CharCombinePrefix": "<", "CharCombineSuffix": ">"})
243            w.char_style("transparent-back %s" % v, {"CharBackColor": 0xFFFF00, "CharBackTransparent": v})
244        for v in (50, 100, 150):
245            w.char_style("scalewidth %d" % v, {"CharScaleWidth": v})
246        for e, h in ((33, 58), (-33, 58), (101, 100), (0, 100)):
247            w.char_style("escapement %d" % e, {"CharEscapement": e, "CharEscapementHeight": h})
248        for r in (0, 900, 2700):
249            w.char_style("rotation %d" % r, {"CharRotation": r})
250        w.char_style("color auto", {"CharColor": -1})
251        w.char_style("color red", {"CharColor": 0xFF0000})
252        for lang, c in (("en", "US"), ("de", "DE"), ("fr", "CA"), ("ja", "JP"), ("zh", "CN"), ("ko", "KR"), ("ar", "SA"), ("th", "TH"), ("he", "IL"), ("hi", "IN")):
253            loc = uno.createUnoStruct("com.sun.star.lang.Locale", lang, c, "")
254            w.char_style("locale %s-%s" % (lang, c), {"CharLocale": loc, "CharLocaleAsian": loc, "CharLocaleComplex": loc})
255        doc = w.flush()
256        save(doc, "writer-char-styles.odt", "writer8")
257        doc.close(True)
258        readback("writer-char-styles.odt", "CharacterStyles", w.specs["CharacterStyles"])
259    build()
260
261
262def png(name):
263    import zlib, struct
264    d = os.path.join(os.path.dirname(out), "work")
265    os.makedirs(d, exist_ok=True)
266    path = os.path.join(d, name)
267    raw = b"".join(b"\x00" + b"".join(bytes([(x * 30) % 256, (y * 30) % 256, 128]) for x in range(8)) for y in range(8))
268
269    def chunk(t, b):
270        c = struct.pack(">I", len(b)) + t + b
271        return c + struct.pack(">I", zlib.crc32(t + b) & 0xFFFFFFFF)
272    with open(path, "wb") as f:
273        f.write(b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", struct.pack(">IIBBBBB", 8, 8, 8, 2, 0, 0, 0)) + chunk(b"IDAT", zlib.compress(raw)) + chunk(b"IEND", b""))
274    return uno.systemPathToFileUrl(path)
275
276
277def border(outer, inner=0, dist=0, color=0):
278    return uno.createUnoStruct("com.sun.star.table.BorderLine", color, inner, outer, dist)
279
280
281def tab(pos, align, fill=" ", dec="."):
282    t = uno.createUnoStruct("com.sun.star.style.TabStop")
283    t.Position, t.Alignment, t.FillChar, t.DecimalChar = pos, enum("style.TabAlign", align), uno.Char(fill), uno.Char(dec)
284    return t
285
286
287def tabs(*ts):
288    if len(ts) == 1 and isinstance(ts[0], (tuple, list)) or len(ts) == 1 and hasattr(ts[0], "__next__"):
289        ts = tuple(ts[0])
290    return uno.Any("[]com.sun.star.style.TabStop", tuple(ts))
291
292
293def breaks():
294    return [enum("style.BreakType", n) for n in ("NONE", "COLUMN_BEFORE", "COLUMN_AFTER", "COLUMN_BOTH", "PAGE_BEFORE", "PAGE_AFTER", "PAGE_BOTH")]
295
296
297@section
298def writer_para():
299    w = W()
300    for v in range(5):
301        w.par_style("adjust %d" % v, {"ParaAdjust": v})
302        w.par_style("lastline %d" % v, {"ParaAdjust": 3, "ParaLastLineAdjust": v})
303        w.par_style("vertalign %d" % v, {"ParaVertAlignment": v})
304        w.par_style("writingmode %d" % v, {"WritingMode": v})
305        w.para("direct adjust %d" % v, props={"ParaAdjust": v})
306        w.para("direct writingmode %d" % v, props={"WritingMode": v})
307        w.para("direct vertalign %d" % v, props={"ParaVertAlignment": v})
308    for v in (True, False):
309        for p in ("ParaExpandSingleWord", "ParaIsAutoFirstLineIndent", "SnapToGrid", "ParaRegisterModeActive", "ParaIsHyphenation", "ParaIsHangingPunctuation", "ParaIsCharacterDistance", "ParaIsForbiddenRules", "ParaSplit", "ParaKeepTogether", "ParaLineNumberCount", "FontIndependentLineSpacing", "ParaIsConnectBorder"):
310            w.par_style("%s %s" % (p, v), {p: v})
311            w.para("direct %s %s" % (p, v), props={p: v})
312    for i, b in enumerate(breaks()):
313        w.par_style("break %d" % i, {"BreakType": b})
314        w.para("direct break %d" % i, props={"BreakType": b})
315    w.par_style("pagedesc", {"PageDescName": "Standard", "PageNumberOffset": 3})
316    w.par_style("pagedesc-break", {"BreakType": enum("style.BreakType", "PAGE_BEFORE"), "PageDescName": "Standard", "PageNumberOffset": 5})
317    w.par_style("linenumber", {"ParaLineNumberCount": True, "ParaLineNumberStartValue": 7})
318    w.par_style("hyphen", {"ParaIsHyphenation": True, "ParaHyphenationMaxLeadingChars": 3, "ParaHyphenationMaxTrailingChars": 4, "ParaHyphenationMaxHyphens": 2})
319    w.par_style("orphans", {"ParaOrphans": 3, "ParaWidows": 4})
320    w.par_style("tabs-all", {"ParaTabStops": tabs(tab(1000 * (i + 1), a, f, d) for i, (a, f, d) in enumerate((("LEFT", " ", "."), ("CENTER", ".", "."), ("RIGHT", "-", "."), ("DECIMAL", "_", ","), ("DECIMAL", " ", "."), ("DECIMAL", "*", "x"), ("DEFAULT", " ", "."))))})
321    w.para("direct tabs", props={"ParaTabStops": tabs(tab(1500, "DECIMAL", ".", ","), tab(3000, "RIGHT", "-"))})
322    w.par_style("tabstopdistance", {"TabStopDistance": 1500})
323    for mode, h in ((0, 150), (1, 600), (2, 200), (3, 500)):
324        ls = uno.createUnoStruct("com.sun.star.style.LineSpacing", mode, h)
325        w.par_style("linespacing %d" % mode, {"ParaLineSpacing": ls})
326        w.para("direct linespacing %d" % mode, props={"ParaLineSpacing": ls})
327    w.par_style("margins", {"ParaLeftMargin": 1000, "ParaRightMargin": 800, "ParaTopMargin": 300, "ParaBottomMargin": 200, "ParaFirstLineIndent": 500})
328    w.par_style("margins-rel", {"ParaLeftMarginRelative": 10, "ParaRightMarginRelative": 10, "ParaTopMarginRelative": 10, "ParaBottomMarginRelative": 10, "ParaFirstLineIndentRelative": 10})
329    w.par_style("margins-same", {"ParaLeftMargin": 1000, "ParaRightMargin": 1000, "ParaTopMargin": 1000, "ParaBottomMargin": 1000})
330    w.par_style("back", {"ParaBackColor": 0x99CCFF})
331    w.par_style("back-transparent", {"ParaBackTransparent": True})
332    url = png("para.png")
333    for loc in ("NONE", "LEFT_TOP", "MIDDLE_TOP", "RIGHT_TOP", "LEFT_MIDDLE", "MIDDLE_MIDDLE", "RIGHT_MIDDLE", "LEFT_BOTTOM", "MIDDLE_BOTTOM", "RIGHT_BOTTOM", "AREA", "TILED"):
334        w.par_style("backgraphic %s" % loc, {"ParaBackGraphicURL": url, "ParaBackGraphicLocation": enum("style.GraphicLocation", loc), "ParaBackGraphicFilter": "png"})
335        w.par_style("backgraphic-nofilter %s" % loc, {"ParaBackGraphicURL": url, "ParaBackGraphicLocation": enum("style.GraphicLocation", loc)})
336    for i, (o, n, d) in enumerate(((20, 0, 0), (20, 20, 20), (50, 30, 40), (100, 0, 0), (35, 35, 35))):
337        b = border(o, n, d, 0x336699)
338        w.par_style("border-all %d" % i, {"LeftBorder": b, "RightBorder": b, "TopBorder": b, "BottomBorder": b, "LeftBorderDistance": 100, "RightBorderDistance": 100, "TopBorderDistance": 100, "BottomBorderDistance": 100})
339        w.par_style("border-left-only %d" % i, {"LeftBorder": b, "LeftBorderDistance": 120})
340        w.par_style("border-lr %d" % i, {"LeftBorder": b, "RightBorder": border(o + 10, n, d)})
341        w.par_style("border-tb %d" % i, {"TopBorder": b, "BottomBorder": border(o + 10, n, d), "ParaIsConnectBorder": False})
342        w.par_style("border-distance %d" % i, {"LeftBorder": b, "RightBorder": b, "TopBorder": b, "BottomBorder": b, "LeftBorderDistance": 50 * i, "RightBorderDistance": 60, "TopBorderDistance": 70, "BottomBorderDistance": 80})
343    w.par_style("border-inner-only", {"LeftBorder": border(0, 20, 0, 0x336699), "TopBorder": border(0, 20, 30, 0x336699)})
344    for loc in ("NONE", "TOP_LEFT", "TOP_RIGHT", "BOTTOM_LEFT", "BOTTOM_RIGHT"):
345        sh = uno.createUnoStruct("com.sun.star.table.ShadowFormat", enum("table.ShadowLocation", loc), 100, False, 0x808080)
346        w.par_style("shadow %s" % loc, {"ParaShadowFormat": sh})
347    dc = uno.createUnoStruct("com.sun.star.style.DropCapFormat", 3, 2, 200)
348    cs = w.style("CharacterStyles", {"CharWeight": 150.0}, tag="C")
349    w.par_style("dropcap", {"DropCapFormat": dc, "DropCapWholeWord": False, "DropCapCharStyleName": cs})
350    w.par_style("dropcap-word", {"DropCapFormat": dc, "DropCapWholeWord": True})
351    w.par_style("outline", {"OutlineLevel": 2})
352    w.par_style("defaultoutline", {"OutlineLevel": 0})
353    w.para("drop cap text word", pstyle="P%d" % w.n)
354    doc = w.flush()
355    save(doc, "writer-para-styles.odt", "writer8")
356    doc.close(True)
357    readback("writer-para-styles.odt", "ParagraphStyles", w.specs["ParagraphStyles"])
358
359
360def level(rules, i, **kw):
361    cur = {p.Name: p.Value for p in rules.getByIndex(i)}
362    cur.update(kw)
363    uno.invoke(rules, "replaceByIndex", (i, uno.Any("[]com.sun.star.beans.PropertyValue", tuple(pv(k, v) for k, v in cur.items()))))
364
365
366def font(name, family=0, pitch=0, charset=0, style=""):
367    f = uno.createUnoStruct("com.sun.star.awt.FontDescriptor")
368    f.Name, f.StyleName, f.Family, f.Pitch, f.CharSet = name, style, family, pitch, charset
369    return f
370
371
372@section
373def writer_list():
374    w = W()
375    url = png("bullet.png")
376    n = 0
377    sz = uno.createUnoStruct("com.sun.star.awt.Size", 500, 400)
378    for adj in (3, 1, 2):
379        for typ in (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90) if adj == 3 else (4, 6, 8):
380            try:
381                st = w.doc.createInstance("com.sun.star.style.NumberingStyle")
382                n += 1
383                w.doc.StyleFamilies.getByName("NumberingStyles").insertByName("N%d" % n, st)
384                r = st.NumberingRules
385                for lv in range(3):
386                    kw = {"NumberingType": typ, "Adjust": adj, "StartWith": 3 + lv, "ParentNumbering": lv + 1, "Prefix": "(", "Suffix": ")", "PositionAndSpaceMode": lv % 2, "LabelFollowedBy": lv, "IndentAt": 1000 * (lv + 1), "FirstLineIndent": -500, "ListtabStopPosition": 1500 * (lv + 1)}
387                    if typ == 6:
388                        kw.update({"BulletChar": chr(0x2022 + lv), "BulletFont": font("OpenSymbol", lv, lv % 3, 0, "Regular"), "BulletRelSize": 80, "BulletColor": 0xFF0000})
389                    if typ == 8:
390                        kw.update({"GraphicURL": url, "GraphicSize": sz, "VertOrient": (adj * 3 + lv * 4) % 10})
391                    level(r, lv, **kw)
392                st.NumberingRules = r
393                w.para("list style %d type %d adj %d" % (n, typ, adj), props={"NumberingStyleName": "N%d" % n})
394            except Exception as e:
395                print("WARN list", typ, adj, type(e).__name__, str(e)[:80])
396    for vo in range(10):
397        st = w.doc.createInstance("com.sun.star.style.NumberingStyle")
398        n += 1
399        w.doc.StyleFamilies.getByName("NumberingStyles").insertByName("N%d" % n, st)
400        r = st.NumberingRules
401        for lv in range(2):
402            level(r, lv, NumberingType=8, GraphicURL=url, GraphicSize=sz, VertOrient=(vo + lv) % 10, PositionAndSpaceMode=lv)
403        st.NumberingRules = r
404        w.para("image bullet vertorient %d" % vo, props={"NumberingStyleName": "N%d" % n})
405    for vo in range(10):
406        r = w.doc.ChapterNumberingRules
407        for lv in range(10):
408            level(r, lv, NumberingType=(vo + lv) % 8, Adjust=(1, 2, 3)[lv % 3], PositionAndSpaceMode=vo % 2, StartWith=2)
409    doc = w.flush()
410    save(doc, "writer-list-styles.odt", "writer8")
411    doc.close(True)
412
413
414@section
415def writer_ruby():
416    w = W()
417    for adj in range(5):
418        for above in (True, False):
419            w.para("ruby base %d %s" % (adj, above))
420    doc = w.flush()
421    en = doc.Text.createEnumeration()
422    k = 0
423    while en.hasMoreElements():
424        p = en.nextElement()
425        c = doc.Text.createTextCursorByRange(p)
426        adj, above = divmod(k, 2)
427        setp(c, "RubyText", "ru", "ruby")
428        setp(c, "RubyAdjust", adj, "ruby")
429        setp(c, "RubyIsAbove", above == 0, "ruby")
430        k += 1
431    save(doc, "writer-ruby.odt", "writer8")
432    doc.close(True)
433
434
435@section
436def writer_section():
437    doc = new("private:factory/swriter")
438    url = png("section.png")
439    cnt = 0
440    for wm in (0, 1, 2, 3, 4):
441        for sep in (True, False):
442            for balance in (True, False):
443                cnt += 1
444                sec = doc.createInstance("com.sun.star.text.TextSection")
445                c = doc.Text.createTextCursor()
446                c.gotoEnd(False)
447                doc.Text.insertString(c, "section %d" % cnt, False)
448                c.goLeft(len("section %d" % cnt), True)
449                doc.Text.insertTextContent(c, sec, True)
450                setall(sec, {"WritingMode": wm, "DontBalanceTextColumns": balance, "SectionLeftMargin": 100 * cnt, "SectionRightMargin": 50 * cnt, "EditInReadonly": sep, "BackColor": 0xEEEEEE if sep else -1, "IsProtected": balance and sep}, "section")
451                cols = sec.TextColumns
452                cols.setColumnCount(2 + (cnt % 2))
453                if sep:
454                    setall(cols, {"SeparatorLineIsOn": True, "SeparatorLineWidth": 10 * cnt, "SeparatorLineColor": 0x333333, "SeparatorLineRelativeHeight": 50 + cnt, "SeparatorLineVerticalAlignment": enum("style.VerticalAlignment", ("TOP", "MIDDLE", "BOTTOM")[cnt % 3])}, "cols")
455                sec.TextColumns = cols
456                c.gotoEnd(False)
457                doc.Text.insertControlCharacter(c, const("text.ControlCharacter.PARAGRAPH_BREAK"), False)
458    for loc in ("LEFT_TOP", "AREA", "TILED", "MIDDLE_MIDDLE"):
459        sec = doc.createInstance("com.sun.star.text.TextSection")
460        c = doc.Text.createTextCursor()
461        c.gotoEnd(False)
462        doc.Text.insertString(c, "bg %s" % loc, False)
463        c.goLeft(len("bg %s" % loc), True)
464        doc.Text.insertTextContent(c, sec, True)
465        setall(sec, {"BackGraphicURL": url, "BackGraphicLocation": enum("style.GraphicLocation", loc), "BackGraphicFilter": "png"}, "section")
466        c.gotoEnd(False)
467        doc.Text.insertControlCharacter(c, const("text.ControlCharacter.PARAGRAPH_BREAK"), False)
468    save(doc, "writer-sections.odt", "writer8")
469    doc.close(True)
470
471
472LOCALES = [("en", "US"), ("de", "DE"), ("ja", "JP"), ("ar", "SA"), ("th", "TH"), ("he", "IL"), ("zh", "TW"), ("ko", "KR"), ("fr", "FR")]
473
474FORMATS = [
475    ("en", "US", ["YYYY-MM-DD", "D.M.YY", "MMM D, YYYY", "MMMM D", "NN, DD MMMM YYYY", "NNN D", "NNNN", "DD/MM/YYYY HH:MM:SS", "MM/DD/YY HH:MM AM/PM",
476                  "HH:MM:SS", "[HH]:MM:SS", "HH:MM:SS.00", "HH:MM AM/PM", "[MM]:SS", "[SS]", "[HH]:MM", "MM:SS.0", "YY", "YYYY", "MM", "MMM", "MMMM", "MMMMM", "D", "DD", "NN", "NNN", "NNNN",
477                  "Q", "QQ", "\"Q\"Q YYYY", "WW", "\"Week \"WW", "YYYY \"Q\"Q WW", "G", "GG", "GGG", "E", "EE", "R", "RR", "GGGE\"/\"M\"/\"D", "GE.M.D", "RR/MM/DD",
478                  "[~buddhist]YYYY-MM-DD", "[~buddhist]D MMMM YYYY NNNN", "[~buddhist]WW", "[~buddhist]Q", "[~buddhist]GGGEE", "[~buddhist]MMM", "[~buddhist]NN",
479                  "[~hijri]D/M/YYYY", "[~hijri]D MMMM YYYY NNN", "[~hijri]QQ", "[~hijri]WW", "[~hijri]GGG E", "[~hijri]NNNN",
480                  "[~gengou]GGGE.M.D", "[~gengou]GE\"年\"M\"月\"D\"日\"", "[~gengou]EE", "[~gengou]D MMMM", "[~gengou]WW", "[~gengou]Q",
481                  "[~jewish]D MMMM YYYY", "[~jewish]WW", "[~jewish]QQ", "[~jewish]NNN D",
482                  "[~ROC]RR/MM/DD", "[~ROC]GGG R", "[~ROC]EE", "[~ROC]NNN", "[~ROC]Q", "[~ROC]WW",
483                  "[~gregorian]YYYY-MM-DD", "[~julian]YYYY-MM-DD", "[~persian]YYYY/MM/DD", "[~dangi]YYYY-MM-DD", "[~indian]YYYY-MM-DD", "[~coptic]YYYY-MM-DD", "[~ethiopic]YYYY-MM-DD",
484                  "[NatNum1]YYYY-MM-DD", "[NatNum2][$-411]YYYY/MM/DD", "[NatNum3]HH:MM", "[DBNum1][$-804]YYYY\"年\"M\"月\"", "[DBNum2][$-804]YYYY", "[DBNum3][$-411]D", "[NatNum1]NNNN",
485                  "0", "0.00", "#,##0", "#,##0.00", "0.0000", "00000", "#", "#.##", "0.00E+00", "#,##0.0E+000", "##0.0E+0", "0E+00", "0.0E-00", "0%", "0.00%", "#,##0%", "0.0E+00%",
486                  "# ?/?", "# ??/??", "# ???/???", "#,### ?/?", "?/?", "# ?/16", "# ?/8", "# ??/100", "#,##0 ?/4", "0,", "0,,", "#,##0,", "#,##0,,", "0.0,,\"M\"", "0.0,\"k\"", "#,##0.00,",
487                  "0\" abc\"", "\"abc \"0", "000-0000", "###\\-####", "0\" x \"0", "00\":\"00", "(000) 000-0000", "0.0\" x \"0", "#\" \"##", "0 ??/??\"x\"", "0.00\" kg\"", "\"#\"0",
488                  "000\"-\"##\"-\"0000", "#,##0.00\" EUR \"##", "#\" m\"0.0", "0\"a\"0\"b\"0",
489                  "[RED]0;[BLUE]-0", "[>100]0;[<0]-0;0", "[<1]0.00;[>=1]0", "[RED][<0]0;[GREEN][>=0]0.00", "0;-0;\"zero\"", "0;[RED]-0;0;\"text:\"@", "[MAGENTA]#,##0.00;[CYAN]-#,##0.00", "[=1]\"one\";[=2]\"two\";\"many\"",
490                  "[$€-407] #,##0.00", "#,##0.00 [$USD]", "[$¥-411] #,##0", "[$$-409]#,##0.00", "[$-409]#,##0.00", "[$-407]0.00", "[$-411]0", "[$-C09]0", "[$-1009]0", "#,##0.00 [$zł-415]", "[$CHF-807] #,##0.00",
491                  "[$-F400]HH:MM:SS AM/PM", "[$-F800]DDDD, MMMM DD, YYYY", "[$-411]YYYY\"年\"M\"月\"D\"日\"", "[$-404]YYYY/MM/DD", "[$-41E]D MMMM YYYY", "[$-1000000]YYYY", "[$-1010409]0", "[$-2000409]0",
492                  "[NatNum1]0", "[NatNum2]0", "[NatNum3]#,##0", "[NatNum4]0", "[NatNum5]0", "[NatNum6]0", "[NatNum7]0", "[NatNum8]0", "[NatNum9]0", "[NatNum10]0", "[NatNum11]0", "[NatNum12]0",
493                  "[NatNum1][$-411]0", "[NatNum2][$-804]0", "[NatNum3][$-404]0", "[NatNum1][$-401]0", "[NatNum1][$-41E]0", "[NatNum1][$-439]0", "[NatNum1][$-412]0", "[DBNum1]0", "[DBNum2]0", "[DBNum3]0", "[DBNum1][$-411]0", "[DBNum2][$-804]0", "[DBNum3][$-404]0",
494                  "[NatNum1]0;[NatNum2]-0", "[NatNum1][RED]0", "[NatNum1]0.0E+00", "[NatNum1]0%", "[NatNum1]0 ?/?",
495                  "@", "\"Text: \"@", "[RED]@", "@\" suffix\"", "\"<\"@\">\"", "[NatNum1]@", "BOOLEAN", "[RED]BOOLEAN", "General", "General\" x\"", "[BLUE]General", "0.00\" \"[$EUR]",
496                  "[<0.5]0%;[>=0.5]0.00%", "[>1]0.0%;[<-1]-0.0%;0.00%", "0%;[RED]-0%;0.0%", "[NatNum1][$€-407] #,##0.00", "[NatNum2][$¥-411]#,##0", "[NatNum1]BOOLEAN", "[NatNum1]0.00%", "[NatNum1]0%;[NatNum2]-0%",
497                  "[DBNum1][$-804]0%", "[>5]0.00;[NatNum1]0"] + ["[NatNum%d]YYYY-MM-DD" % n for n in range(1, 13)] + ["[NatNum%d][$-411]YYYY\"/\"MM\"/\"DD" % n for n in range(1, 13)]
498                 + ["[DBNum%d][$-804]YYYY-MM-DD" % n for n in range(1, 4)] + ["[NatNum%d]HH:MM:SS" % n for n in (1, 2, 3, 4)] + ["[NatNum%d][$-411]HH:MM" % n for n in (1, 2, 3)]),
499]
500
501
502@section
503def calc_numbers():
504    doc = new("private:factory/scalc")
505    sh = doc.Sheets.getByIndex(0)
506    nf = doc.NumberFormats
507    row = 0
508    seen = {}
509    locs = [(l, c) for l, c in LOCALES]
510    ok = bad = 0
511    expect = []
512    for (l0, c0, codes) in FORMATS:
513        for (l, c) in ([(l0, c0)] + locs):
514            loc = uno.createUnoStruct("com.sun.star.lang.Locale", l, c, "")
515            for code in codes:
516                if (l, c) != (l0, c0) and not any(t in code for t in ("[~", "GG", "NN", "[NatNum", "[DBNum", "Q", "WW", "[$")):
517                    continue
518                try:
519                    key = nf.queryKey(code, loc, False)
520                    if key == -1:
521                        key = nf.addNew(code, loc)
522                except Exception as e:
523                    bad += 1
524                    print("WARN numfmt", l, c, code, type(e).__name__)
525                    continue
526                if key in seen:
527                    continue
528                seen[key] = 1
529                ok += 1
530                cell = sh.getCellByPosition(0, row)
531                cell.Value = 44197.4375 + row if ("Q" in code or "W" in code or "Y" in code or "D" in code or "G" in code or "H" in code or "E" in code or "R" in code or "N" in code or "M" in code) and "0" not in code[:1] else 1234.5678 - row
532                if "@" in code:
533                    cell.String = "text %d" % row
534                if "BOOLEAN" in code:
535                    cell.Value = 1
536                cell.NumberFormat = key
537                sh.getCellByPosition(1, row).String = "%s %s" % (l + "-" + c, code)
538                expect.append((row, code, l, c))
539                row += 1
540    print("numfmt ok", ok, "bad", bad)
541    for t in (1, 2, 4, 8, 16, 32, 64, 128, 256, 1024):
542        for (l, c) in locs:
543            try:
544                loc = uno.createUnoStruct("com.sun.star.lang.Locale", l, c, "")
545                key = nf.getStandardFormat(t, loc)
546                cell = sh.getCellByPosition(2, row)
547                cell.Value = 44197.4375 + row
548                cell.NumberFormat = key
549                row += 1
550            except Exception as e:
551                print("WARN stdfmt", t, l, type(e).__name__)
552    for fmt in range(0, 60):
553        try:
554            loc = uno.createUnoStruct("com.sun.star.lang.Locale", "en", "US", "")
555            cell = sh.getCellByPosition(3, fmt)
556            cell.Value = 44197.4375 + fmt
557            cell.NumberFormat = fmt
558        except Exception:
559            pass
560    save(doc, "calc-numberformats.ods", "calc8")
561    doc.close(True)
562    try:
563        d2 = reload("calc-numberformats.ods")
564        sh2 = d2.Sheets.getByIndex(0)
565        nf2 = d2.NumberFormats
566        diff = 0
567        for (r, code, l, c) in expect:
568            try:
569                ps = nf2.getByKey(sh2.getCellByPosition(0, r).NumberFormat)
570                got, gl = ps.getPropertyValue("FormatString"), ps.getPropertyValue("Locale")
571            except Exception as e:
572                diff += 1
573                print("RT-NUMFMT", l, c, code, "-> exception", type(e).__name__)
574                continue
575            if got != code or (gl.Language, gl.Country) != (l, c):
576                diff += 1
577                print("RT-NUMFMT", l, c, code, "->", got, gl.Language, gl.Country)
578        print("RT-SUMMARY calc-numberformats.ods formats", len(expect), "differing", diff)
579        d2.close(True)
580    except Exception as e:
581        print("RT-FAIL calc-numberformats", type(e).__name__, str(e)[:100])
582
583
584def char_cases():
585    c = []
586    for suf in ("", "Asian", "Complex"):
587        c += [("weight%s %s" % (suf, v), {"CharWeight" + suf: float(v)}) for v in WEIGHTS]
588        c += [("posture%s %s" % (suf, v), {"CharPosture" + suf: enum("awt.FontSlant", v)}) for v in SLANTS]
589        c += [("family%s %s" % (suf, v), {"CharFontFamily" + suf: const("awt.FontFamily." + v)}) for v in FAMILIES]
590        c += [("pitch%s %s" % (suf, v), {"CharFontPitch" + suf: const("awt.FontPitch." + v)}) for v in PITCHES]
591        c += [("charset%s %s" % (suf, v), {"CharFontCharSet" + suf: const("awt.CharSet." + v)}) for v in CHARSETS]
592        c += [("named%s %s" % (suf, f), {"CharFontName" + suf: "T12Face%s%s" % (suf, f), "CharFontFamily" + suf: const("awt.FontFamily." + f), "CharFontPitch" + suf: const("awt.FontPitch.FIXED"), "CharFontStyleName" + suf: "Bold"}) for f in FAMILIES]
593        c += [("height%s" % suf, {"CharHeight" + suf: 17.5}), ("propheight%s" % suf, {"CharPropHeight" + suf: 150}), ("diffheight%s" % suf, {"CharDiffHeight" + suf: 2.0})]
594    for v in UNDERLINES:
595        c += [("underline %d" % v, {"CharUnderline": v}), ("underline-col %d" % v, {"CharUnderline": v, "CharUnderlineHasColor": True, "CharUnderlineColor": 0xFF0000}), ("underline-word %d" % v, {"CharUnderline": v, "CharWordMode": True}),
596              ("overline %d" % v, {"CharOverline": v}), ("overline-col %d" % v, {"CharOverline": v, "CharOverlineHasColor": True, "CharOverlineColor": 0x00FF00}), ("overline-word %d" % v, {"CharOverline": v, "CharWordMode": True})]
597    for v in STRIKES:
598        c += [("strike %d" % v, {"CharStrikeout": v}), ("strike-word %d" % v, {"CharStrikeout": v, "CharWordMode": True}), ("crossed %d" % v, {"CharCrossedOut": bool(v)})]
599    c += [("casemap %d" % v, {"CharCaseMap": v}) for v in range(5)]
600    c += [("relief %d" % v, {"CharRelief": v}) for v in range(3)]
601    c += [("emphasis %d" % v, {"CharEmphasis": v}) for v in (0, 1, 2, 3, 4, 11, 12, 13, 14)]
602    c += [("kerning %d" % v, {"CharKerning": v}) for v in (-30, 0, 40)]
603    for v in (True, False):
604        c += [("autokern %s" % v, {"CharAutoKerning": v}), ("flash %s" % v, {"CharFlash": v}), ("shadowed %s" % v, {"CharShadowed": v}), ("contoured %s" % v, {"CharContoured": v}),
605              ("fitline %s" % v, {"CharRotation": 900, "CharRotationIsFitToLine": v}), ("combine %s" % v, {"CharCombineIsOn": v, "CharCombinePrefix": "<", "CharCombineSuffix": ">"}),
606              ("hidden %s" % v, {"CharHidden": v})]
607    c += [("scalewidth %d" % v, {"CharScaleWidth": v}) for v in (50, 100, 150)]
608    c += [("escapement %d" % e, {"CharEscapement": e, "CharEscapementHeight": h}) for e, h in ((33, 58), (-33, 58), (101, 100), (0, 100))]
609    c += [("rotation %d" % r, {"CharRotation": r}) for r in (0, 900, 2700)]
610    c += [("color auto", {"CharColor": -1}), ("color red", {"CharColor": 0xFF0000}), ("back", {"CharBackColor": 0xFFFF00})]
611    for lang, cc in (("ja", "JP"), ("ar", "SA"), ("th", "TH"), ("zh", "CN"), ("he", "IL"), ("ko", "KR")):
612        loc = uno.createUnoStruct("com.sun.star.lang.Locale", lang, cc, "")
613        c.append(("locale %s" % lang, {"CharLocale": loc, "CharLocaleAsian": loc, "CharLocaleComplex": loc}))
614    return c
615
616
617def para_cases():
618    c = []
619    for v in range(5):
620        c += [("adjust %d" % v, {"ParaAdjust": v}), ("lastline %d" % v, {"ParaAdjust": 3, "ParaLastLineAdjust": v}), ("vertalign %d" % v, {"ParaVertAlignment": v}), ("writingmode %d" % v, {"WritingMode": v})]
621    for v in (True, False):
622        for p in ("ParaExpandSingleWord", "ParaIsAutoFirstLineIndent", "SnapToGrid", "ParaRegisterModeActive", "ParaIsHyphenation", "ParaIsHangingPunctuation", "ParaIsCharacterDistance", "ParaIsForbiddenRules", "ParaSplit", "ParaKeepTogether", "FontIndependentLineSpacing", "ParaIsConnectBorder"):
623            c.append(("%s %s" % (p, v), {p: v}))
624    c.append(("tabs", {"ParaTabStops": tabs(tab(1500, "DECIMAL", ".", ","), tab(3000, "RIGHT", "-"), tab(4000, "CENTER", "."), tab(5000, "LEFT", "_"))}))
625    for mode, h in ((0, 150), (1, 600), (2, 200), (3, 500)):
626        c.append(("linespacing %d" % mode, {"ParaLineSpacing": uno.createUnoStruct("com.sun.star.style.LineSpacing", mode, h)}))
627    c += [("margins", {"ParaLeftMargin": 1000, "ParaRightMargin": 800, "ParaTopMargin": 300, "ParaBottomMargin": 200, "ParaFirstLineIndent": 500}),
628          ("margins-rel", {"ParaLeftMarginRelative": 10, "ParaTopMarginRelative": 10, "ParaBottomMarginRelative": 10, "ParaFirstLineIndentRelative": 10}),
629          ("hyphen", {"ParaIsHyphenation": True, "ParaHyphenationMaxLeadingChars": 3, "ParaHyphenationMaxTrailingChars": 4, "ParaHyphenationMaxHyphens": 2}),
630          ("orphans", {"ParaOrphans": 3, "ParaWidows": 4}), ("tabdist", {"TabStopDistance": 1500}), ("outline", {"NumberingLevel": 2}),
631          ("back", {"ParaBackColor": 0x99CCFF}), ("regmode", {"ParaRegisterModeActive": True})]
632    return c
633
634
635def apply_text(doc, shape, label, props):
636    shape.String = label
637    chars = {k: v for k, v in props.items() if k.startswith("Char")}
638    others = {k: v for k, v in props.items() if k not in chars}
639    setall(shape, others, label)
640    if chars:
641        cur = shape.createTextCursor()
642        cur.gotoStart(False)
643        cur.goRight(len(label) - 1, True)
644        setall(cur, chars, label)
645
646
647def shapes_page(doc, page, cases, cols=6):
648    for k, (label, props) in enumerate(cases):
649        sh = doc.createInstance("com.sun.star.drawing.TextShape")
650        page.add(sh)
651        sh.setSize(uno.createUnoStruct("com.sun.star.awt.Size", 3000, 800))
652        sh.setPosition(uno.createUnoStruct("com.sun.star.awt.Point", 500 + (k % cols) * 3300, 500 + (k // cols) * 900))
653        apply_text(doc, sh, label, props)
654
655
656def style_cases(doc, family, svc, cases, tag):
657    fam = doc.StyleFamilies.getByName(family)
658    names = []
659    for k, (label, props) in enumerate(cases):
660        st = fam.createInstance() if svc is None else doc.createInstance(svc)
661        name = "%s%d" % (tag, k)
662        fam.insertByName(name, st)
663        setall(st, props, name)
664        names.append(name)
665    return names
666
667
668@section
669def draw_text():
670    doc = new("private:factory/sdraw")
671    page = doc.DrawPages.getByIndex(0)
672    cases = char_cases() + para_cases()
673    shapes_page(doc, page, cases)
674    names = style_cases(doc, "graphics", None, cases, "G")
675    page2 = doc.DrawPages.insertNewByIndex(1)
676    for k, n in enumerate(names):
677        sh = doc.createInstance("com.sun.star.drawing.TextShape")
678        page2.add(sh)
679        sh.setSize(uno.createUnoStruct("com.sun.star.awt.Size", 3000, 800))
680        sh.setPosition(uno.createUnoStruct("com.sun.star.awt.Point", 500 + (k % 6) * 3300, 500 + (k // 6) * 900))
681        sh.String = cases[k][0]
682        sh.Style = doc.StyleFamilies.getByName("graphics").getByName(n)
683    save(doc, "draw-text.odg", "draw8")
684    doc.close(True)
685
686
687@section
688def impress_text():
689    doc = new("private:factory/simpress")
690    page = doc.DrawPages.getByIndex(0)
691    cases = char_cases() + para_cases()
692    shapes_page(doc, page, cases)
693    names = style_cases(doc, "graphics", None, cases[:120], "G")
694    page2 = doc.DrawPages.insertNewByIndex(1)
695    for k, n in enumerate(names):
696        sh = doc.createInstance("com.sun.star.drawing.TextShape")
697        page2.add(sh)
698        sh.setSize(uno.createUnoStruct("com.sun.star.awt.Size", 3000, 800))
699        sh.setPosition(uno.createUnoStruct("com.sun.star.awt.Point", 500 + (k % 6) * 3300, 500 + (k // 6) * 900))
700        sh.String = cases[k][0]
701        sh.Style = doc.StyleFamilies.getByName("graphics").getByName(n)
702    save(doc, "impress-text.odp", "impress8")
703    doc.close(True)
704
705
706@section
707def calc_text():
708    doc = new("private:factory/scalc")
709    sh = doc.Sheets.getByIndex(0)
710    cases = char_cases() + para_cases()
711    names = style_cases(doc, "CellStyles", "com.sun.star.style.CellStyle", cases, "S")
712    for k, (label, props) in enumerate(cases):
713        a = sh.getCellByPosition(0, k)
714        a.String = label
715        setp(a, "CellStyle", names[k], label)
716        b = sh.getCellByPosition(1, k)
717        b.String = label
718        setall(b, {kk: v for kk, v in props.items() if kk.startswith("Char") or kk.startswith("Para")}, label)
719        cur = sh.getCellByPosition(2, k)
720        cur.String = label
721        cc = cur.createTextCursor()
722        cc.gotoStart(False)
723        cc.goRight(len(label) - 1, True)
724        setall(cc, {kk: v for kk, v in props.items() if kk.startswith("Char")}, label)
725    save(doc, "calc-text.ods", "calc8")
726    doc.close(True)
727
728
729@section
730def writer_page():
731    doc = new("private:factory/swriter")
732    url = png("page.png")
733    pages = doc.StyleFamilies.getByName("PageStyles")
734    cases = [{"BackGraphicURL": url, "BackGraphicLocation": enum("style.GraphicLocation", loc), "BackGraphicFilter": "png", "BackGraphicTransparency": 30} for loc in ("LEFT_TOP", "AREA", "TILED", "RIGHT_BOTTOM", "NONE")]
735    cases += [{"BackColor": 0xCCCCCC, "BackTransparent": False}]
736    cases += [{"WritingMode": w} for w in range(5)]
737    for sepw, al in ((10, "TOP"), (30, "MIDDLE"), (50, "BOTTOM")):
738        cases.append({"__cols": (2, {"SeparatorLineIsOn": True, "SeparatorLineWidth": sepw, "SeparatorLineColor": 0x884400, "SeparatorLineRelativeHeight": 70, "SeparatorLineVerticalAlignment": enum("style.VerticalAlignment", al)})})
739    for k, props in enumerate(cases):
740        st = doc.createInstance("com.sun.star.style.PageStyle")
741        pages.insertByName("T12Page%d" % k, st)
742        props = dict(props)
743        cols = props.pop("__cols", None)
744        setall(st, props, "page%d" % k)
745        if cols:
746            tc = st.TextColumns
747            tc.setColumnCount(cols[0])
748            setall(tc, cols[1], "pagecols")
749            st.TextColumns = tc
750        c = doc.Text.createTextCursor()
751        c.gotoEnd(False)
752        doc.Text.insertString(c, "page style %d" % k, False)
753        setp(c, "PageDescName", "T12Page%d" % k, "page")
754        doc.Text.insertControlCharacter(c, const("text.ControlCharacter.PARAGRAPH_BREAK"), False)
755    save(doc, "writer-page-styles.odt", "writer8")
756    doc.close(True)
757
758
759# --- end of sections
760for _fn in SECTIONS:
761    run(_fn.__name__, _fn)
762sys.exit(1 if failed else 0)
763