xref: /trunk/test/odf-validate/fixtures/leak/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# usage: runpy.sh fixtures/leak/make.py <port> <outdir>
21# Documents for names that AOO's export code can emit but no ODF 1.2 schema pattern defines (runs/task12/gaps/strict-leak.txt).
22# Saves with the office's configured ODF version. ODF packages go to <outdir>; the report-definition package and the
23# old-format chart copies go to <outdir>/../other and <outdir>/../oldfmt so the ODF checkers do not see them.
24import os, struct, sys, traceback, zlib, uno
25from unohelp import connect, desktop, pv
26from com.sun.star.awt import Rectangle, Size, Point
27from com.sun.star.table import CellAddress, CellRangeAddress, BorderLine
28
29port, out = int(sys.argv[1]), os.path.abspath(sys.argv[2])
30base = os.path.dirname(out)
31other, oldfmt, work = (os.path.join(base, d) for d in ("other", "oldfmt", "work"))
32for d in (out, other, oldfmt, work):
33    os.makedirs(d, exist_ok=True)
34ctx = connect(port)
35sm = ctx.ServiceManager
36desk = desktop(ctx)
37failed = []
38URL = uno.systemPathToFileUrl
39ANCHOR = uno.Enum("com.sun.star.text.TextContentAnchorType", "AS_CHARACTER")
40TPL = "com.sun.star.chart2.template."
41
42
43def new(factory):
44    return desk.loadComponentFromURL(factory, "_blank", 0, (pv("Hidden", True),))
45
46
47def save(doc, path, filt):
48    doc.storeToURL(URL(path), (pv("FilterName", filt),))
49    print("saved", os.path.basename(path), filt)
50
51
52def step(name, fn):
53    try:
54        fn()
55    except Exception as e:
56        failed.append(name)
57        print("FAIL", name, repr(e)[:300])
58        traceback.print_exc(limit=3)
59
60
61def make_png(path):
62    w = h = 8
63    raw = b"".join(b"\x00" + bytes([200, 30, 30] * w) for _ in range(h))
64
65    def chunk(t, b):
66        return struct.pack(">I", len(b)) + t + b + struct.pack(">I", zlib.crc32(t + b) & 0xFFFFFFFF)
67
68    with open(path, "wb") as f:
69        f.write(b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", struct.pack(">IIBBBBB", w, h, 8, 2, 0, 0, 0))
70                + chunk(b"IDAT", zlib.compress(raw)) + chunk(b"IEND", b""))
71
72
73def set_all(obj, props):
74    for k, v in props:
75        try:
76            uno.invoke(obj, "setPropertyValue", (k, v)) if isinstance(v, uno.Any) else obj.setPropertyValue(k, v)
77        except Exception as e:
78            print("note: cannot set", k, repr(e)[:80])
79
80
81def writer_fields():
82    doc = new("private:factory/swriter")
83    try:
84        text = doc.Text
85        cur = text.createTextCursor()
86        text.insertString(cur, "fields: ", False)
87
88        def field(svc, **kw):
89            f = doc.createInstance("com.sun.star.text.TextField." + svc)
90            for k, v in kw.items():
91                setattr(f, k, v)
92            text.insertTextContent(cur, f, False)
93            text.insertString(cur, " ", False)
94
95        field("Input", Content="x", Hint="hint", Help="help text", Tooltip="tooltip text")
96        field("DropDown", Name="dd", Items=("a", "b", "c"), SelectedItem="b", Help="help dd", Tooltip="tip dd")
97        pn = uno.Enum("com.sun.star.text.PageNumberType", "NEXT")
98        field("PageNumber", SubType=pn, UserText="next page", NumberingType=6)
99        pn = uno.Enum("com.sun.star.text.PageNumberType", "PREV")
100        field("PageNumber", SubType=pn, UserText="prev page", NumberingType=6)
101        fn = doc.createInstance("com.sun.star.text.Footnote")
102        text.insertTextContent(cur, fn, False)
103        fn.String = "note"
104        fs = doc.FootnoteSettings
105        fs.EndNotice = "continued on next page"
106        fs.BeginNotice = "continued from previous page"
107        save(doc, os.path.join(out, "writer-fields.odt"), "writer8")
108    finally:
109        doc.close(True)
110
111
112def writer_toc_image():
113    png = os.path.join(work, "red.png")
114    make_png(png)
115    doc = new("private:factory/swriter")
116    try:
117        text = doc.Text
118        cur = text.createTextCursor()
119        text.insertString(cur, "Heading one", False)
120        cur.gotoStart(False)
121        cur.gotoEndOfParagraph(True)
122        cur.ParaStyleName = "Heading 1"
123        cur.gotoEnd(False)
124        text.insertControlCharacter(cur, uno.getConstantByName("com.sun.star.text.ControlCharacter.PARAGRAPH_BREAK"), False)
125        g = doc.createInstance("com.sun.star.text.GraphicObject")
126        g.GraphicURL = URL(png)
127        g.Width = g.Height = 3000
128        g.AnchorType = ANCHOR
129        text.insertTextContent(cur, g, False)
130        g.GraphicRotation = 900
131        toc = doc.createInstance("com.sun.star.text.ContentIndex")
132        toc.CreateFromOutline = True
133        lf = toc.LevelFormat
134        new_level = []
135        for tok in lf.getByIndex(1):
136            tok = list(tok)
137            for p in tok:
138                if p.Name == "WithTab":
139                    p.Value = False
140            new_level.append(tuple(tok))
141        uno.invoke(lf, "replaceByIndex", (1, uno.Any("[][]com.sun.star.beans.PropertyValue", tuple(new_level))))
142        text.insertTextContent(cur, toc, False)
143        toc.update()
144        save(doc, os.path.join(out, "writer-toc-image.odt"), "writer8")
145    finally:
146        doc.close(True)
147
148
149def writer_forms():
150    doc = new("private:factory/swriter")
151    try:
152        dp = doc.DrawPage
153        dp.Forms.insertByName("F1", doc.createInstance("com.sun.star.form.component.Form"))
154        form = dp.Forms.getByName("F1")
155        y = 300
156        for kind in ("TextField", "FormattedField", "NumericField", "DateField", "TimeField", "CurrencyField", "PatternField",
157                     "CheckBox", "ComboBox", "ListBox", "RadioButton"):
158            m = doc.createInstance("com.sun.star.form.component." + kind)
159            set_all(m, (("InputRequired", False), ("EnableVisible", False)))
160            m.Name = kind
161            form.insertByName(kind, m)
162            s = doc.createInstance("com.sun.star.drawing.ControlShape")
163            s.Size = Size(3000, 600)
164            s.Position = Point(500, y)
165            y += 800
166            s.setControl(m)
167            dp.add(s)
168        for i, (n, v) in enumerate((("FontWidth", uno.Any("short", 20)), ("FontCharWidth", uno.Any("float", 90.0)),
169                                    ("FontWordLineMode", True))):
170            m = doc.createInstance("com.sun.star.form.component.TextField")
171            set_all(m, ((n, v),))
172            m.Name = "font_" + n
173            form.insertByName(m.Name, m)
174            s = doc.createInstance("com.sun.star.drawing.ControlShape")
175            s.Size = Size(3000, 600)
176            s.Position = Point(4500, 300 + i * 800)
177            s.setControl(m)
178            dp.add(s)
179        save(doc, os.path.join(out, "writer-forms.odt"), "writer8")
180    finally:
181        doc.close(True)
182
183
184def writer_redline():
185    doc = new("private:factory/swriter")
186    try:
187        doc.Text.insertString(doc.Text.createTextCursor(), "first para\nsecond para\nthird para", False)
188        t = doc.createInstance("com.sun.star.text.TextTable")
189        t.initialize(2, 2)
190        c = doc.Text.createTextCursor()
191        c.gotoEnd(False)
192        doc.Text.insertTextContent(c, t, False)
193        doc.RecordChanges = True
194        c = doc.Text.createTextCursor()
195        c.gotoStart(False)
196        c.goRight(3, False)
197        c.gotoEndOfParagraph(True)
198        c.goRight(1, True)
199        c.setString("")
200        c = doc.Text.createTextCursor()
201        c.gotoEnd(False)
202        c.goLeft(4, True)
203        c.setString("")
204        e = doc.Redlines.createEnumeration()
205        while e.hasMoreElements():
206            r = e.nextElement()
207            print("redline", r.RedlineType, "MergeLastPara", r.getPropertyValue("MergeLastPara"))
208        save(doc, os.path.join(out, "writer-redline.odt"), "writer8")
209    finally:
210        doc.close(True)
211
212
213def writer_pagegrid():
214    doc = new("private:factory/swriter")
215    try:
216        doc.Text.insertString(doc.Text.createTextCursor(), "grid", False)
217        ps = doc.StyleFamilies.getByName("PageStyles").getByName("Standard")
218        set_all(ps, (("GridMode", 2), ("GridLines", 20), ("GridBaseHeight", 600), ("GridBaseWidth", 500),
219                     ("GridSnapToChars", True), ("StandardPageMode", True), ("GridDisplay", True), ("GridPrint", True)))
220        save(doc, os.path.join(out, "writer-pagegrid.odt"), "writer8")
221    finally:
222        doc.close(True)
223
224
225def fill_rows(sh, rows):
226    for r, row in enumerate(rows):
227        for c, v in enumerate(row):
228            cell = sh.getCellByPosition(c, r)
229            if isinstance(v, str):
230                cell.String = v
231            else:
232                cell.Value = v
233
234
235def calc_misc():
236    doc = new("private:factory/scalc")
237    try:
238        sh = doc.Sheets.getByIndex(0)
239        sh.TabColor = 0xFF8000
240        for i, mode in enumerate((0, 1, 2)):
241            sh.getCellByPosition(12, i).Formula = '=DDE("soffice";"/tmp/x.ods";"Sheet1.A%d";%d)' % (i + 1, mode)
242        fill_rows(sh, [("", "", "", "L0", 1, ""), ("", "", "", "L1", 2, ""), ("", "", "", "L2", 3, "")])
243        for r in range(3):
244            sh.getCellByPosition(1, r + 4).Value = r + 5
245            sh.getCellByPosition(0, r + 4).String = "L%d" % r
246        cd = doc.createConsolidationDescriptor(False)
247        cd.Function = uno.Enum("com.sun.star.sheet.GeneralFunction", "SUM")
248        cd.Sources = (CellRangeAddress(0, 0, 4, 1, 6), CellRangeAddress(0, 3, 0, 4, 2))
249        cd.StartOutputPosition = CellAddress(0, 7, 7)
250        cd.UseColumnHeaders = True
251        cd.UseRowHeaders = True
252        cd.InsertLinks = False
253        doc.consolidate(cd)
254        c = sh.getCellByPosition(10, 10)
255        c.String = "diagonal"
256        bl = BorderLine()
257        bl.OuterLineWidth = 50
258        bl.Color = 0xFF0000
259        c.DiagonalBLTR = bl
260        c.DiagonalTLBR = bl
261        ps = doc.StyleFamilies.getByName("PageStyles").getByName(sh.PageStyle)
262        set_all(ps, (("ScaleToPagesX", 2), ("ScaleToPagesY", 3)))
263        save(doc, os.path.join(out, "calc-misc.ods"), "calc8")
264    finally:
265        doc.close(True)
266
267
268def calc_pivot_group():
269    doc = new("private:factory/scalc")
270    try:
271        sh = doc.Sheets.getByIndex(0)
272        fill_rows(sh, [("Region", "Qty")] + [(r, i + 1) for i, r in enumerate(["North", "South", "East", "West", "North", "East"])])
273        dps = sh.DataPilotTables
274        desc = dps.createDataPilotDescriptor()
275        desc.setSourceRange(CellRangeAddress(0, 0, 0, 1, 6))
276        fl = desc.getDataPilotFields()
277        ori = lambda n: uno.Enum("com.sun.star.sheet.DataPilotFieldOrientation", n)
278        fl.getByName("Region").Orientation = ori("ROW")
279        fl.getByName("Qty").Orientation = ori("DATA")
280        dps.insertNewByName("pv", CellAddress(0, 4, 1), desc)
281        f = dps.getByName("pv").getDataPilotFields().getByName("Region")
282        f.createNameGroup(("North", "South"))
283        save(doc, os.path.join(out, "calc-pivot-group.ods"), "calc8")
284    finally:
285        doc.close(True)
286
287
288def calc_tracked():
289    doc = new("private:factory/scalc")
290    try:
291        dh = sm.createInstanceWithContext("com.sun.star.frame.DispatchHelper", ctx)
292        dh.executeDispatch(doc.CurrentController.Frame, ".uno:TraceChangeMode", "", 0, (pv("TraceChangeMode", True),))
293        sh = doc.Sheets.getByIndex(0)
294        for r in range(6):
295            sh.getCellByPosition(0, r).Value = r
296        for r in range(6):
297            sh.getCellByPosition(0, r).Value = r * 10
298        sh.Rows.insertByIndex(2, 2)
299        sh.Rows.removeByIndex(3, 2)
300        save(doc, os.path.join(out, "calc-tracked.ods"), "calc8")
301    finally:
302        doc.close(True)
303
304
305def chart_doc(name, charts, post=None):
306    doc = new("private:factory/scalc")
307    try:
308        sh = doc.Sheets.getByIndex(0)
309        loc = uno.createUnoStruct("com.sun.star.lang.Locale", "en", "US", "")
310        key = doc.NumberFormats.queryKey("YYYY-MM-DD", loc, False)
311        if key == -1:
312            key = doc.NumberFormats.addNew("YYYY-MM-DD", loc)
313        for r in range(6):
314            for c in range(5):
315                if r == 0:
316                    sh.getCellByPosition(c, r).String = "s%d" % c if c else "cat"
317                else:
318                    sh.getCellByPosition(c, r).Value = 40000 + r * 30 if c == 0 else r * 2 + c
319        sh.getCellRangeByName("A2:A6").NumberFormat = key
320        for i, (cname, ncols, tpl) in enumerate(charts):
321            rng = CellRangeAddress(0, 0, 0, ncols, 5)
322            sh.Charts.addNewByName(cname, Rectangle(500 + i * 9500, 8000, 9000, 6800), (rng,), True, True)
323            ch = sh.Charts.getByName(cname).EmbeddedObject
324            if tpl:
325                dp = ch.getDataProvider()
326                args = (pv("CellRangeRepresentation", "$Sheet1.$A$1:$%s$6" % "ABCDE"[ncols]),
327                        pv("DataRowSource", uno.Enum("com.sun.star.chart.ChartDataRowSource", "COLUMNS")),
328                        pv("FirstCellAsLabel", True), pv("HasCategories", True))
329                t = ch.ChartTypeManager.createInstance(TPL + tpl)
330                ch.setFirstDiagram(t.createDiagramByDataSource(dp.createDataSource(args), args))
331            if post:
332                post(ch)
333        save(doc, os.path.join(out, name + ".ods"), "calc8")
334        try:
335            save(doc, os.path.join(oldfmt, name + ".sxc"), "StarOffice XML (Calc)")
336        except Exception as e:
337            print("note: old format save failed", repr(e)[:100])
338    finally:
339        doc.close(True)
340
341
342def label_and_date_axis(ch):
343    cs = ch.getFirstDiagram().getCoordinateSystems()[0]
344    for s in cs.getChartTypes()[0].getDataSeries():
345        s.setPropertyValue("LabelSeparator", "a\tb\nc")
346        s.setPropertyValue("Label", uno.createUnoStruct("com.sun.star.chart2.DataPointLabel", True, False, False, False))
347    ax = cs.getAxisByDimension(0, 0)
348    sd = ax.getScaleData()
349    sd.AxisType = 4
350    ti = uno.createUnoStruct("com.sun.star.chart.TimeIncrement")
351    ti.TimeResolution = 1
352    ti.MajorTimeInterval = uno.createUnoStruct("com.sun.star.chart.TimeInterval", 2, 1)
353    ti.MinorTimeInterval = uno.createUnoStruct("com.sun.star.chart.TimeInterval", 1, 0)
354    sd.TimeIncrement = ti
355    ax.setScaleData(sd)
356
357
358def draw_fontwork():
359    doc = new("private:factory/sdraw")
360    try:
361        page = doc.DrawPages.getByIndex(0)
362        s = doc.createInstance("com.sun.star.drawing.RectangleShape")
363        page.add(s)
364        s.Size = Size(5000, 3000)
365        s.Position = Point(500, 500)
366        s.String = "fontwork"
367        set_all(s, (("FontWorkStyle", 0), ("FontWorkAdjust", 1), ("FontWorkDistance", 100), ("FontWorkStart", 50),
368                    ("FontWorkMirror", True), ("FontWorkOutline", True), ("FontWorkShadow", 1), ("FontWorkShadowColor", 0xFF0000),
369                    ("FontWorkShadowOffsetX", 100), ("FontWorkShadowOffsetY", 100), ("FontWorkHideForm", True),
370                    ("FontWorkShadowTransparence", 50)))
371        for svc in ("RectangleShape", "TextShape", "PolyPolygonShape", "CustomShape"):
372            o = doc.createInstance("com.sun.star.drawing." + svc)
373            print("FontWorkForm on", svc, o.getPropertySetInfo().hasPropertyByName("FontWorkForm"))
374        a = doc.createInstance("com.sun.star.drawing.AppletShape")
375        page.add(a)
376        a.Size = Size(3000, 2000)
377        a.Position = Point(6000, 500)
378        set_all(a, (("AppletName", "myapplet"), ("AppletCode", "Foo.class"), ("AppletCodeBase", "file:///tmp/"), ("AppletIsScript", True)))
379        print("applet AppletName reads back", repr(a.getPropertyValue("AppletName")))
380        t = doc.createInstance("com.sun.star.drawing.TextShape")
381        page.add(t)
382        t.Size = Size(5000, 3000)
383        t.Position = Point(500, 4500)
384        t.String = "one\ntwo"
385        nr = t.getPropertyValue("NumberingRules")
386        lvl = list(nr.getByIndex(0))
387        for p in lvl:
388            if p.Name == "NumberingType":
389                p.Value = 4
390        uno.invoke(nr, "replaceByIndex", (0, uno.Any("[]com.sun.star.beans.PropertyValue", tuple(lvl))))
391        t.setPropertyValue("NumberingRules", nr)
392        save(doc, os.path.join(out, "draw-fontwork.odg"), "draw8")
393    finally:
394        doc.close(True)
395
396
397def set_info(ds, **kw):
398    bag = ds.Settings
399    for k, v in kw.items():
400        if bag.getPropertySetInfo().hasPropertyByName(k):
401            uno.invoke(bag, "setPropertyValue", (k, v))
402        else:
403            uno.invoke(bag, "addProperty", (k, 128 | 1, v))
404
405
406def base_doc():
407    dbc = sm.createInstanceWithContext("com.sun.star.sdb.DatabaseContext", ctx)
408    path = os.path.join(out, "leak-base.odb")
409    for p in (path, path + ".lck"):
410        if os.path.exists(p):
411            os.remove(p)
412    ds = dbc.createInstance()
413    ds.URL = "sdbc:embedded:hsqldb"
414    ds.DatabaseDocument.storeAsURL(URL(path), ())
415    try:
416        q = ds.QueryDefinitions.createInstance()
417        q.Command = 'SELECT "ID", "NAME" FROM "CUSTOMERS"'
418        q.Filter = '"ID" > 0'
419        q.ApplyFilter = True
420        q.Order = '"NAME"'
421        ds.QueryDefinitions.insertByName("Q", q)
422        q = ds.QueryDefinitions.getByName("Q")
423        print("ApplyOrder on query", q.getPropertySetInfo().hasPropertyByName("ApplyOrder"),
424              "on table definition", ds.Tables.createInstance().getPropertySetInfo().hasPropertyByName("ApplyOrder"))
425        set_all(q, (("FontWidth", uno.Any("short", 20)), ("FontCharWidth", uno.Any("float", 90.0))))
426        for name, props in (("ID", dict(Hidden=True, HelpText="id column")), ("NAME", dict(HelpText="name", ControlDefault="x"))):
427            d = q.Columns.createDataDescriptor()
428            d.Name = name
429            for k, v in props.items():
430                d.setPropertyValue(k, v)
431            q.Columns.appendByDescriptor(d)
432        ds.DatabaseDocument.store()
433    finally:
434        ds.DatabaseDocument.close(True)
435        if os.path.exists(path + ".lck"):
436            os.remove(path + ".lck")
437    print("saved", os.path.basename(path))
438
439
440def base_dbase():
441    dbc = sm.createInstanceWithContext("com.sun.star.sdb.DatabaseContext", ctx)
442    d = os.path.join(work, "dbase")
443    os.makedirs(d, exist_ok=True)
444    path = os.path.join(out, "leak-base-dbase.odb")
445    for p in (path, path + ".lck"):
446        if os.path.exists(p):
447            os.remove(p)
448    ds = dbc.createInstance()
449    ds.URL = "sdbc:dbase:" + URL(d) + "/"
450    set_info(ds, Extension="dbf", CharSet="utf-8")
451    ds.DatabaseDocument.storeAsURL(URL(path), ())
452    try:
453        ds.DatabaseDocument.store()
454    finally:
455        ds.DatabaseDocument.close(True)
456        if os.path.exists(path + ".lck"):
457            os.remove(path + ".lck")
458    print("saved", os.path.basename(path))
459
460
461def report_definition():
462    r = sm.createInstanceWithContext("com.sun.star.report.ReportDefinition", ctx)
463    r.Caption = "Leak report"
464    r.Name = "rpt"
465    r.ReportHeaderOn = True
466    path = os.path.join(other, "report-definition.odr")
467    stg = sm.createInstanceWithContext("com.sun.star.embed.StorageFactory", ctx).createInstanceWithArguments((URL(path), 7))
468    r.storeToStorage(stg, ())
469    stg.commit()
470    stg.dispose()
471    print("saved", os.path.basename(path), "(report definition)")
472
473
474for name, fn in (("writer-fields", writer_fields), ("writer-toc-image", writer_toc_image), ("writer-forms", writer_forms),
475                 ("writer-redline", writer_redline), ("writer-pagegrid", writer_pagegrid), ("calc-misc", calc_misc),
476                 ("calc-pivot-group", calc_pivot_group), ("calc-tracked", calc_tracked),
477                 ("calc-chart-axis", lambda: chart_doc("calc-chart-axis", (("c1", 3, None),), label_and_date_axis)),
478                 ("calc-chart-types", lambda: chart_doc("calc-chart-types", (("cl", 3, "ColumnWithLine"), ("sv", 4, "StockVolumeLowHighClose")))),
479                 ("draw-fontwork", draw_fontwork), ("base", base_doc), ("base-dbase", base_dbase), ("report", report_definition)):
480    step(name, fn)
481print("FAILED:", failed if failed else "none")
482