This is an automated email from the ASF dual-hosted git repository.

jimjag pushed a commit to branch odf-devel
in repository https://gitbox.apache.org/repos/asf/openoffice.git

commit d222476c5abedf2c7c68174449c9e8de078e39ee
Author: Jim Jagielski <[email protected]>
AuthorDate: Fri Oct 23 14:25:57 2026 -0400

    test/odf-validate: add fixtures for the extension paragraph fill import
---
 .../fixtures/para-fill-foreign/aoo-form.odt        | Bin 0 -> 18940 bytes
 .../fixtures/para-fill-foreign/checks-angle.json   |  16 +
 .../para-fill-foreign/checks-aoo-form.json         | 378 +++++++++++++++++
 .../para-fill-foreign/checks-edge-both-forms.json  | 464 +++++++++++++++++++++
 .../checks-edge-duplicate-nested.json              | 464 +++++++++++++++++++++
 .../para-fill-foreign/checks-edge-empty.json       | 452 ++++++++++++++++++++
 .../checks-edge-nonparagraph.json                  | 464 +++++++++++++++++++++
 .../checks-edge-unknown-attr.json                  | 464 +++++++++++++++++++++
 .../checks-edge-unknown-name.json                  | 440 +++++++++++++++++++
 .../fixtures/para-fill-foreign/checks-frame.json   |  10 +
 .../para-fill-foreign/checks-unknown-attr.json     |  22 +
 .../fixtures/para-fill-foreign/checks-v13-v14.json | 464 +++++++++++++++++++++
 .../fixtures/para-fill-foreign/checks.json         | 464 +++++++++++++++++++++
 .../fixtures/para-fill-foreign/edge-both-forms.odt | Bin 0 -> 29275 bytes
 .../para-fill-foreign/edge-duplicate-nested.odt    | Bin 0 -> 29280 bytes
 .../fixtures/para-fill-foreign/edge-empty.odt      | Bin 0 -> 29265 bytes
 .../para-fill-foreign/edge-nonparagraph.odt        | Bin 0 -> 29312 bytes
 .../para-fill-foreign/edge-unknown-attr.odt        | Bin 0 -> 29286 bytes
 .../para-fill-foreign/edge-unknown-name.odt        | Bin 0 -> 29273 bytes
 .../fixtures/para-fill-foreign/foreign-v11.odt     | Bin 0 -> 29272 bytes
 .../fixtures/para-fill-foreign/foreign-v3.odt      | Bin 0 -> 29271 bytes
 .../fixtures/para-fill-foreign/foreign-v8.odt      | Bin 0 -> 29267 bytes
 .../fixtures/para-fill-foreign/gen.bas             |  95 +++++
 .../fixtures/para-fill-foreign/make-aoo.py         |  64 +++
 .../fixtures/para-fill-foreign/make.py             | 118 ++++++
 .../fixtures/para-fill-foreign/mkexpect.py         |  75 ++++
 .../fixtures/para-fill-foreign/tile.png            | Bin 0 -> 1795 bytes
 27 files changed, 4454 insertions(+)

diff --git a/test/odf-validate/fixtures/para-fill-foreign/aoo-form.odt 
b/test/odf-validate/fixtures/para-fill-foreign/aoo-form.odt
new file mode 100644
index 0000000000..a6d41ec52d
Binary files /dev/null and 
b/test/odf-validate/fixtures/para-fill-foreign/aoo-form.odt differ
diff --git a/test/odf-validate/fixtures/para-fill-foreign/checks-angle.json 
b/test/odf-validate/fixtures/para-fill-foreign/checks-angle.json
new file mode 100644
index 0000000000..560b31b64d
--- /dev/null
+++ b/test/odf-validate/fixtures/para-fill-foreign/checks-angle.json
@@ -0,0 +1,16 @@
+[
+ {
+  "name": "gradient_angle",
+  "expr": "[(n, 
doc.StyleFamilies.getByName('ParagraphStyles').getByName(n).FillGradient.Angle) 
for n in ('S_gradient','S_gradient_transp')]",
+  "expect": [
+   [
+    "S_gradient",
+    300
+   ],
+   [
+    "S_gradient_transp",
+    300
+   ]
+  ]
+ }
+]
diff --git a/test/odf-validate/fixtures/para-fill-foreign/checks-aoo-form.json 
b/test/odf-validate/fixtures/para-fill-foreign/checks-aoo-form.json
new file mode 100644
index 0000000000..c3ad2685a0
--- /dev/null
+++ b/test/odf-validate/fixtures/para-fill-foreign/checks-aoo-form.json
@@ -0,0 +1,378 @@
+[
+ {
+  "name": "styled_paragraphs",
+  "expr": "[(p.String,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' or (o.FillStyle.value=='HATCH' and o.FillBackground) 
else None),o.FillTransparence,bool(o.FillGradientName),(lambda 
g:(g.Style.value,g.StartColor,g.EndColor,g.Angle,g.Border))(o.FillGradient) if 
o.FillStyle.value=='GRADIENT' else None,bool(o.FillHatchName),(lambda 
h:(h.Style.value,h.Color,h.Distance,h.Angle))(o.FillHatch) if 
o.FillStyle.value=='HATCH' else None,o.FillBackground if o.FillStyle. [...]
+  "expect": [
+   [
+    "styled solid",
+    "SOLID",
+    3385941,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    3385941,
+    false
+   ],
+   [
+    "styled solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1697863082,
+    false
+   ],
+   [
+    "styled gradient",
+    "GRADIENT",
+    null,
+    0,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    8388736,
+    false
+   ],
+   [
+    "styled gradient_transp",
+    "GRADIENT",
+    null,
+    30,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1283457152,
+    false
+   ],
+   [
+    "styled hatch",
+    "HATCH",
+    16777164,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    true,
+    false,
+    null,
+    false,
+    16777164,
+    false
+   ],
+   [
+    "styled hatch_nobg",
+    "HATCH",
+    null,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    false,
+    false,
+    null,
+    false,
+    2147461632,
+    false
+   ],
+   [
+    "styled bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "STRETCH",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "NO_REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_transp",
+    "BITMAP",
+    null,
+    50,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ]
+  ]
+ },
+ {
+  "name": "para_styles",
+  "expr": "[(n,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' or (o.FillStyle.value=='HATCH' and o.FillBackground) 
else None),o.FillTransparence,bool(o.FillGradientName),(lambda 
g:(g.Style.value,g.StartColor,g.EndColor,g.Angle,g.Border))(o.FillGradient) if 
o.FillStyle.value=='GRADIENT' else None,bool(o.FillHatchName),(lambda 
h:(h.Style.value,h.Color,h.Distance,h.Angle))(o.FillHatch) if 
o.FillStyle.value=='HATCH' else None,o.FillBackground if o.FillStyle.value== 
[...]
+  "expect": [
+   [
+    "S_bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "NO_REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "STRETCH",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_transp",
+    "BITMAP",
+    null,
+    50,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_gradient",
+    "GRADIENT",
+    null,
+    0,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    8388736,
+    false
+   ],
+   [
+    "S_gradient_transp",
+    "GRADIENT",
+    null,
+    30,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1283457152,
+    false
+   ],
+   [
+    "S_hatch",
+    "HATCH",
+    16777164,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    true,
+    false,
+    null,
+    false,
+    16777164,
+    false
+   ],
+   [
+    "S_hatch_nobg",
+    "HATCH",
+    null,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    false,
+    false,
+    null,
+    false,
+    2147461632,
+    false
+   ],
+   [
+    "S_solid",
+    "SOLID",
+    3385941,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    3385941,
+    false
+   ],
+   [
+    "S_solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1697863082,
+    false
+   ]
+  ]
+ }
+]
diff --git 
a/test/odf-validate/fixtures/para-fill-foreign/checks-edge-both-forms.json 
b/test/odf-validate/fixtures/para-fill-foreign/checks-edge-both-forms.json
new file mode 100644
index 0000000000..0b59001281
--- /dev/null
+++ b/test/odf-validate/fixtures/para-fill-foreign/checks-edge-both-forms.json
@@ -0,0 +1,464 @@
+[
+ {
+  "name": "styled_paragraphs",
+  "expr": "[(p.String,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' or (o.FillStyle.value=='HATCH' and o.FillBackground) 
else None),o.FillTransparence,bool(o.FillGradientName),(lambda 
g:(g.Style.value,g.StartColor,g.EndColor,g.Angle,g.Border))(o.FillGradient) if 
o.FillStyle.value=='GRADIENT' else None,bool(o.FillHatchName),(lambda 
h:(h.Style.value,h.Color,h.Distance,h.Angle))(o.FillHatch) if 
o.FillStyle.value=='HATCH' else None,o.FillBackground if o.FillStyle. [...]
+  "expect": [
+   [
+    "styled solid",
+    "SOLID",
+    16711680,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    16711680,
+    false
+   ],
+   [
+    "styled solid_transp",
+    "SOLID",
+    16711680,
+    40,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1711210496,
+    false
+   ],
+   [
+    "styled gradient",
+    "GRADIENT",
+    null,
+    0,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    8388736,
+    false
+   ],
+   [
+    "styled gradient_transp",
+    "GRADIENT",
+    null,
+    30,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1283457152,
+    false
+   ],
+   [
+    "styled hatch",
+    "HATCH",
+    16777164,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    true,
+    false,
+    null,
+    false,
+    16777164,
+    false
+   ],
+   [
+    "styled hatch_nobg",
+    "HATCH",
+    null,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    false,
+    false,
+    null,
+    false,
+    2147461632,
+    false
+   ],
+   [
+    "styled bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "STRETCH",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "NO_REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_transp",
+    "BITMAP",
+    null,
+    50,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ]
+  ]
+ },
+ {
+  "name": "para_styles",
+  "expr": "[(n,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' or (o.FillStyle.value=='HATCH' and o.FillBackground) 
else None),o.FillTransparence,bool(o.FillGradientName),(lambda 
g:(g.Style.value,g.StartColor,g.EndColor,g.Angle,g.Border))(o.FillGradient) if 
o.FillStyle.value=='GRADIENT' else None,bool(o.FillHatchName),(lambda 
h:(h.Style.value,h.Color,h.Distance,h.Angle))(o.FillHatch) if 
o.FillStyle.value=='HATCH' else None,o.FillBackground if o.FillStyle.value== 
[...]
+  "expect": [
+   [
+    "S_bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "NO_REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "STRETCH",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_transp",
+    "BITMAP",
+    null,
+    50,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_gradient",
+    "GRADIENT",
+    null,
+    0,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    8388736,
+    false
+   ],
+   [
+    "S_gradient_transp",
+    "GRADIENT",
+    null,
+    30,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1283457152,
+    false
+   ],
+   [
+    "S_hatch",
+    "HATCH",
+    16777164,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    true,
+    false,
+    null,
+    false,
+    16777164,
+    false
+   ],
+   [
+    "S_hatch_nobg",
+    "HATCH",
+    null,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    false,
+    false,
+    null,
+    false,
+    2147461632,
+    false
+   ],
+   [
+    "S_solid",
+    "SOLID",
+    16711680,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    16711680,
+    false
+   ],
+   [
+    "S_solid_transp",
+    "SOLID",
+    16711680,
+    40,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1711210496,
+    false
+   ]
+  ]
+ },
+ {
+  "name": "direct_paragraphs",
+  "expr": "[(p.String,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' else 
None),o.FillTransparence,(o.FillBitmapMode.value if o.FillStyle.value=='BITMAP' 
else None),('GraphicObject:' in o.FillBitmapURL and 'GraphicObject:0000' not in 
o.FillBitmapURL)))(p) for e in [doc.Text.createEnumeration()] for p in 
iter(lambda: e.nextElement() if e.hasMoreElements() else None, None) if 
p.String.startswith('direct')]",
+  "expect": [
+   [
+    "direct solid",
+    "SOLID",
+    3385941,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    null,
+    false
+   ],
+   [
+    "direct gradient",
+    "SOLID",
+    8388736,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct gradient_transp",
+    "SOLID",
+    8388736,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct hatch",
+    "SOLID",
+    16777164,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct hatch_nobg",
+    "SOLID",
+    16755200,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    "REPEAT",
+    true
+   ],
+   [
+    "direct bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    "STRETCH",
+    true
+   ],
+   [
+    "direct bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    "NO_REPEAT",
+    true
+   ],
+   [
+    "direct bitmap_transp",
+    "BITMAP",
+    null,
+    0,
+    "REPEAT",
+    true
+   ]
+  ]
+ }
+]
diff --git 
a/test/odf-validate/fixtures/para-fill-foreign/checks-edge-duplicate-nested.json
 
b/test/odf-validate/fixtures/para-fill-foreign/checks-edge-duplicate-nested.json
new file mode 100644
index 0000000000..0f88343021
--- /dev/null
+++ 
b/test/odf-validate/fixtures/para-fill-foreign/checks-edge-duplicate-nested.json
@@ -0,0 +1,464 @@
+[
+ {
+  "name": "styled_paragraphs",
+  "expr": "[(p.String,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' or (o.FillStyle.value=='HATCH' and o.FillBackground) 
else None),o.FillTransparence,bool(o.FillGradientName),(lambda 
g:(g.Style.value,g.StartColor,g.EndColor,g.Angle,g.Border))(o.FillGradient) if 
o.FillStyle.value=='GRADIENT' else None,bool(o.FillHatchName),(lambda 
h:(h.Style.value,h.Color,h.Distance,h.Angle))(o.FillHatch) if 
o.FillStyle.value=='HATCH' else None,o.FillBackground if o.FillStyle. [...]
+  "expect": [
+   [
+    "styled solid",
+    "SOLID",
+    3385941,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    3385941,
+    false
+   ],
+   [
+    "styled solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1697863082,
+    false
+   ],
+   [
+    "styled gradient",
+    "GRADIENT",
+    null,
+    0,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    8388736,
+    false
+   ],
+   [
+    "styled gradient_transp",
+    "GRADIENT",
+    null,
+    30,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1283457152,
+    false
+   ],
+   [
+    "styled hatch",
+    "HATCH",
+    16777164,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    true,
+    false,
+    null,
+    false,
+    16777164,
+    false
+   ],
+   [
+    "styled hatch_nobg",
+    "HATCH",
+    null,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    false,
+    false,
+    null,
+    false,
+    2147461632,
+    false
+   ],
+   [
+    "styled bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "STRETCH",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "NO_REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_transp",
+    "BITMAP",
+    null,
+    50,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ]
+  ]
+ },
+ {
+  "name": "para_styles",
+  "expr": "[(n,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' or (o.FillStyle.value=='HATCH' and o.FillBackground) 
else None),o.FillTransparence,bool(o.FillGradientName),(lambda 
g:(g.Style.value,g.StartColor,g.EndColor,g.Angle,g.Border))(o.FillGradient) if 
o.FillStyle.value=='GRADIENT' else None,bool(o.FillHatchName),(lambda 
h:(h.Style.value,h.Color,h.Distance,h.Angle))(o.FillHatch) if 
o.FillStyle.value=='HATCH' else None,o.FillBackground if o.FillStyle.value== 
[...]
+  "expect": [
+   [
+    "S_bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "NO_REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "STRETCH",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_transp",
+    "BITMAP",
+    null,
+    50,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_gradient",
+    "GRADIENT",
+    null,
+    0,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    8388736,
+    false
+   ],
+   [
+    "S_gradient_transp",
+    "GRADIENT",
+    null,
+    30,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1283457152,
+    false
+   ],
+   [
+    "S_hatch",
+    "HATCH",
+    16777164,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    true,
+    false,
+    null,
+    false,
+    16777164,
+    false
+   ],
+   [
+    "S_hatch_nobg",
+    "HATCH",
+    null,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    false,
+    false,
+    null,
+    false,
+    2147461632,
+    false
+   ],
+   [
+    "S_solid",
+    "SOLID",
+    3385941,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    3385941,
+    false
+   ],
+   [
+    "S_solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1697863082,
+    false
+   ]
+  ]
+ },
+ {
+  "name": "direct_paragraphs",
+  "expr": "[(p.String,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' else 
None),o.FillTransparence,(o.FillBitmapMode.value if o.FillStyle.value=='BITMAP' 
else None),('GraphicObject:' in o.FillBitmapURL and 'GraphicObject:0000' not in 
o.FillBitmapURL)))(p) for e in [doc.Text.createEnumeration()] for p in 
iter(lambda: e.nextElement() if e.hasMoreElements() else None, None) if 
p.String.startswith('direct')]",
+  "expect": [
+   [
+    "direct solid",
+    "SOLID",
+    3385941,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    null,
+    false
+   ],
+   [
+    "direct gradient",
+    "SOLID",
+    8388736,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct gradient_transp",
+    "SOLID",
+    8388736,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct hatch",
+    "SOLID",
+    16777164,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct hatch_nobg",
+    "SOLID",
+    16755200,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    "REPEAT",
+    true
+   ],
+   [
+    "direct bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    "STRETCH",
+    true
+   ],
+   [
+    "direct bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    "NO_REPEAT",
+    true
+   ],
+   [
+    "direct bitmap_transp",
+    "BITMAP",
+    null,
+    0,
+    "REPEAT",
+    true
+   ]
+  ]
+ }
+]
diff --git 
a/test/odf-validate/fixtures/para-fill-foreign/checks-edge-empty.json 
b/test/odf-validate/fixtures/para-fill-foreign/checks-edge-empty.json
new file mode 100644
index 0000000000..28146a9147
--- /dev/null
+++ b/test/odf-validate/fixtures/para-fill-foreign/checks-edge-empty.json
@@ -0,0 +1,452 @@
+[
+ {
+  "name": "styled_paragraphs",
+  "expr": "[(p.String,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' or (o.FillStyle.value=='HATCH' and o.FillBackground) 
else None),o.FillTransparence,bool(o.FillGradientName),(lambda 
g:(g.Style.value,g.StartColor,g.EndColor,g.Angle,g.Border))(o.FillGradient) if 
o.FillStyle.value=='GRADIENT' else None,bool(o.FillHatchName),(lambda 
h:(h.Style.value,h.Color,h.Distance,h.Angle))(o.FillHatch) if 
o.FillStyle.value=='HATCH' else None,o.FillBackground if o.FillStyle. [...]
+  "expect": [
+   [
+    "styled solid",
+    "SOLID",
+    3385941,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    3385941,
+    false
+   ],
+   [
+    "styled solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1697863082,
+    false
+   ],
+   [
+    "styled gradient",
+    "SOLID",
+    8388736,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    8388736,
+    false
+   ],
+   [
+    "styled gradient_transp",
+    "GRADIENT",
+    null,
+    30,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1283457152,
+    false
+   ],
+   [
+    "styled hatch",
+    "HATCH",
+    16777164,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    true,
+    false,
+    null,
+    false,
+    16777164,
+    false
+   ],
+   [
+    "styled hatch_nobg",
+    "HATCH",
+    null,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    false,
+    false,
+    null,
+    false,
+    2147461632,
+    false
+   ],
+   [
+    "styled bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "STRETCH",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "NO_REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_transp",
+    "BITMAP",
+    null,
+    50,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ]
+  ]
+ },
+ {
+  "name": "para_styles",
+  "expr": "[(n,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' or (o.FillStyle.value=='HATCH' and o.FillBackground) 
else None),o.FillTransparence,bool(o.FillGradientName),(lambda 
g:(g.Style.value,g.StartColor,g.EndColor,g.Angle,g.Border))(o.FillGradient) if 
o.FillStyle.value=='GRADIENT' else None,bool(o.FillHatchName),(lambda 
h:(h.Style.value,h.Color,h.Distance,h.Angle))(o.FillHatch) if 
o.FillStyle.value=='HATCH' else None,o.FillBackground if o.FillStyle.value== 
[...]
+  "expect": [
+   [
+    "S_bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "NO_REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "STRETCH",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_transp",
+    "BITMAP",
+    null,
+    50,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_gradient",
+    "SOLID",
+    8388736,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    8388736,
+    false
+   ],
+   [
+    "S_gradient_transp",
+    "GRADIENT",
+    null,
+    30,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1283457152,
+    false
+   ],
+   [
+    "S_hatch",
+    "HATCH",
+    16777164,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    true,
+    false,
+    null,
+    false,
+    16777164,
+    false
+   ],
+   [
+    "S_hatch_nobg",
+    "HATCH",
+    null,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    false,
+    false,
+    null,
+    false,
+    2147461632,
+    false
+   ],
+   [
+    "S_solid",
+    "SOLID",
+    3385941,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    3385941,
+    false
+   ],
+   [
+    "S_solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1697863082,
+    false
+   ]
+  ]
+ },
+ {
+  "name": "direct_paragraphs",
+  "expr": "[(p.String,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' else 
None),o.FillTransparence,(o.FillBitmapMode.value if o.FillStyle.value=='BITMAP' 
else None),('GraphicObject:' in o.FillBitmapURL and 'GraphicObject:0000' not in 
o.FillBitmapURL)))(p) for e in [doc.Text.createEnumeration()] for p in 
iter(lambda: e.nextElement() if e.hasMoreElements() else None, None) if 
p.String.startswith('direct')]",
+  "expect": [
+   [
+    "direct solid",
+    "SOLID",
+    3385941,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    null,
+    false
+   ],
+   [
+    "direct gradient",
+    "SOLID",
+    8388736,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct gradient_transp",
+    "SOLID",
+    8388736,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct hatch",
+    "SOLID",
+    16777164,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct hatch_nobg",
+    "SOLID",
+    16755200,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    "REPEAT",
+    true
+   ],
+   [
+    "direct bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    "STRETCH",
+    true
+   ],
+   [
+    "direct bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    "NO_REPEAT",
+    true
+   ],
+   [
+    "direct bitmap_transp",
+    "BITMAP",
+    null,
+    0,
+    "REPEAT",
+    true
+   ]
+  ]
+ }
+]
diff --git 
a/test/odf-validate/fixtures/para-fill-foreign/checks-edge-nonparagraph.json 
b/test/odf-validate/fixtures/para-fill-foreign/checks-edge-nonparagraph.json
new file mode 100644
index 0000000000..0f88343021
--- /dev/null
+++ b/test/odf-validate/fixtures/para-fill-foreign/checks-edge-nonparagraph.json
@@ -0,0 +1,464 @@
+[
+ {
+  "name": "styled_paragraphs",
+  "expr": "[(p.String,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' or (o.FillStyle.value=='HATCH' and o.FillBackground) 
else None),o.FillTransparence,bool(o.FillGradientName),(lambda 
g:(g.Style.value,g.StartColor,g.EndColor,g.Angle,g.Border))(o.FillGradient) if 
o.FillStyle.value=='GRADIENT' else None,bool(o.FillHatchName),(lambda 
h:(h.Style.value,h.Color,h.Distance,h.Angle))(o.FillHatch) if 
o.FillStyle.value=='HATCH' else None,o.FillBackground if o.FillStyle. [...]
+  "expect": [
+   [
+    "styled solid",
+    "SOLID",
+    3385941,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    3385941,
+    false
+   ],
+   [
+    "styled solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1697863082,
+    false
+   ],
+   [
+    "styled gradient",
+    "GRADIENT",
+    null,
+    0,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    8388736,
+    false
+   ],
+   [
+    "styled gradient_transp",
+    "GRADIENT",
+    null,
+    30,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1283457152,
+    false
+   ],
+   [
+    "styled hatch",
+    "HATCH",
+    16777164,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    true,
+    false,
+    null,
+    false,
+    16777164,
+    false
+   ],
+   [
+    "styled hatch_nobg",
+    "HATCH",
+    null,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    false,
+    false,
+    null,
+    false,
+    2147461632,
+    false
+   ],
+   [
+    "styled bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "STRETCH",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "NO_REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_transp",
+    "BITMAP",
+    null,
+    50,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ]
+  ]
+ },
+ {
+  "name": "para_styles",
+  "expr": "[(n,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' or (o.FillStyle.value=='HATCH' and o.FillBackground) 
else None),o.FillTransparence,bool(o.FillGradientName),(lambda 
g:(g.Style.value,g.StartColor,g.EndColor,g.Angle,g.Border))(o.FillGradient) if 
o.FillStyle.value=='GRADIENT' else None,bool(o.FillHatchName),(lambda 
h:(h.Style.value,h.Color,h.Distance,h.Angle))(o.FillHatch) if 
o.FillStyle.value=='HATCH' else None,o.FillBackground if o.FillStyle.value== 
[...]
+  "expect": [
+   [
+    "S_bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "NO_REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "STRETCH",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_transp",
+    "BITMAP",
+    null,
+    50,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_gradient",
+    "GRADIENT",
+    null,
+    0,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    8388736,
+    false
+   ],
+   [
+    "S_gradient_transp",
+    "GRADIENT",
+    null,
+    30,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1283457152,
+    false
+   ],
+   [
+    "S_hatch",
+    "HATCH",
+    16777164,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    true,
+    false,
+    null,
+    false,
+    16777164,
+    false
+   ],
+   [
+    "S_hatch_nobg",
+    "HATCH",
+    null,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    false,
+    false,
+    null,
+    false,
+    2147461632,
+    false
+   ],
+   [
+    "S_solid",
+    "SOLID",
+    3385941,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    3385941,
+    false
+   ],
+   [
+    "S_solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1697863082,
+    false
+   ]
+  ]
+ },
+ {
+  "name": "direct_paragraphs",
+  "expr": "[(p.String,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' else 
None),o.FillTransparence,(o.FillBitmapMode.value if o.FillStyle.value=='BITMAP' 
else None),('GraphicObject:' in o.FillBitmapURL and 'GraphicObject:0000' not in 
o.FillBitmapURL)))(p) for e in [doc.Text.createEnumeration()] for p in 
iter(lambda: e.nextElement() if e.hasMoreElements() else None, None) if 
p.String.startswith('direct')]",
+  "expect": [
+   [
+    "direct solid",
+    "SOLID",
+    3385941,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    null,
+    false
+   ],
+   [
+    "direct gradient",
+    "SOLID",
+    8388736,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct gradient_transp",
+    "SOLID",
+    8388736,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct hatch",
+    "SOLID",
+    16777164,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct hatch_nobg",
+    "SOLID",
+    16755200,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    "REPEAT",
+    true
+   ],
+   [
+    "direct bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    "STRETCH",
+    true
+   ],
+   [
+    "direct bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    "NO_REPEAT",
+    true
+   ],
+   [
+    "direct bitmap_transp",
+    "BITMAP",
+    null,
+    0,
+    "REPEAT",
+    true
+   ]
+  ]
+ }
+]
diff --git 
a/test/odf-validate/fixtures/para-fill-foreign/checks-edge-unknown-attr.json 
b/test/odf-validate/fixtures/para-fill-foreign/checks-edge-unknown-attr.json
new file mode 100644
index 0000000000..0f88343021
--- /dev/null
+++ b/test/odf-validate/fixtures/para-fill-foreign/checks-edge-unknown-attr.json
@@ -0,0 +1,464 @@
+[
+ {
+  "name": "styled_paragraphs",
+  "expr": "[(p.String,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' or (o.FillStyle.value=='HATCH' and o.FillBackground) 
else None),o.FillTransparence,bool(o.FillGradientName),(lambda 
g:(g.Style.value,g.StartColor,g.EndColor,g.Angle,g.Border))(o.FillGradient) if 
o.FillStyle.value=='GRADIENT' else None,bool(o.FillHatchName),(lambda 
h:(h.Style.value,h.Color,h.Distance,h.Angle))(o.FillHatch) if 
o.FillStyle.value=='HATCH' else None,o.FillBackground if o.FillStyle. [...]
+  "expect": [
+   [
+    "styled solid",
+    "SOLID",
+    3385941,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    3385941,
+    false
+   ],
+   [
+    "styled solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1697863082,
+    false
+   ],
+   [
+    "styled gradient",
+    "GRADIENT",
+    null,
+    0,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    8388736,
+    false
+   ],
+   [
+    "styled gradient_transp",
+    "GRADIENT",
+    null,
+    30,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1283457152,
+    false
+   ],
+   [
+    "styled hatch",
+    "HATCH",
+    16777164,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    true,
+    false,
+    null,
+    false,
+    16777164,
+    false
+   ],
+   [
+    "styled hatch_nobg",
+    "HATCH",
+    null,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    false,
+    false,
+    null,
+    false,
+    2147461632,
+    false
+   ],
+   [
+    "styled bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "STRETCH",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "NO_REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_transp",
+    "BITMAP",
+    null,
+    50,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ]
+  ]
+ },
+ {
+  "name": "para_styles",
+  "expr": "[(n,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' or (o.FillStyle.value=='HATCH' and o.FillBackground) 
else None),o.FillTransparence,bool(o.FillGradientName),(lambda 
g:(g.Style.value,g.StartColor,g.EndColor,g.Angle,g.Border))(o.FillGradient) if 
o.FillStyle.value=='GRADIENT' else None,bool(o.FillHatchName),(lambda 
h:(h.Style.value,h.Color,h.Distance,h.Angle))(o.FillHatch) if 
o.FillStyle.value=='HATCH' else None,o.FillBackground if o.FillStyle.value== 
[...]
+  "expect": [
+   [
+    "S_bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "NO_REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "STRETCH",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_transp",
+    "BITMAP",
+    null,
+    50,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_gradient",
+    "GRADIENT",
+    null,
+    0,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    8388736,
+    false
+   ],
+   [
+    "S_gradient_transp",
+    "GRADIENT",
+    null,
+    30,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1283457152,
+    false
+   ],
+   [
+    "S_hatch",
+    "HATCH",
+    16777164,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    true,
+    false,
+    null,
+    false,
+    16777164,
+    false
+   ],
+   [
+    "S_hatch_nobg",
+    "HATCH",
+    null,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    false,
+    false,
+    null,
+    false,
+    2147461632,
+    false
+   ],
+   [
+    "S_solid",
+    "SOLID",
+    3385941,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    3385941,
+    false
+   ],
+   [
+    "S_solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1697863082,
+    false
+   ]
+  ]
+ },
+ {
+  "name": "direct_paragraphs",
+  "expr": "[(p.String,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' else 
None),o.FillTransparence,(o.FillBitmapMode.value if o.FillStyle.value=='BITMAP' 
else None),('GraphicObject:' in o.FillBitmapURL and 'GraphicObject:0000' not in 
o.FillBitmapURL)))(p) for e in [doc.Text.createEnumeration()] for p in 
iter(lambda: e.nextElement() if e.hasMoreElements() else None, None) if 
p.String.startswith('direct')]",
+  "expect": [
+   [
+    "direct solid",
+    "SOLID",
+    3385941,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    null,
+    false
+   ],
+   [
+    "direct gradient",
+    "SOLID",
+    8388736,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct gradient_transp",
+    "SOLID",
+    8388736,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct hatch",
+    "SOLID",
+    16777164,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct hatch_nobg",
+    "SOLID",
+    16755200,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    "REPEAT",
+    true
+   ],
+   [
+    "direct bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    "STRETCH",
+    true
+   ],
+   [
+    "direct bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    "NO_REPEAT",
+    true
+   ],
+   [
+    "direct bitmap_transp",
+    "BITMAP",
+    null,
+    0,
+    "REPEAT",
+    true
+   ]
+  ]
+ }
+]
diff --git 
a/test/odf-validate/fixtures/para-fill-foreign/checks-edge-unknown-name.json 
b/test/odf-validate/fixtures/para-fill-foreign/checks-edge-unknown-name.json
new file mode 100644
index 0000000000..513b57a113
--- /dev/null
+++ b/test/odf-validate/fixtures/para-fill-foreign/checks-edge-unknown-name.json
@@ -0,0 +1,440 @@
+[
+ {
+  "name": "styled_paragraphs",
+  "expr": "[(p.String,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' or (o.FillStyle.value=='HATCH' and o.FillBackground) 
else None),o.FillTransparence,bool(o.FillGradientName),(lambda 
g:(g.Style.value,g.StartColor,g.EndColor,g.Angle,g.Border))(o.FillGradient) if 
o.FillStyle.value=='GRADIENT' else None,bool(o.FillHatchName),(lambda 
h:(h.Style.value,h.Color,h.Distance,h.Angle))(o.FillHatch) if 
o.FillStyle.value=='HATCH' else None,o.FillBackground if o.FillStyle. [...]
+  "expect": [
+   [
+    "styled solid",
+    "SOLID",
+    3385941,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    3385941,
+    false
+   ],
+   [
+    "styled solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1697863082,
+    false
+   ],
+   [
+    "styled gradient",
+    "SOLID",
+    8388736,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    8388736,
+    false
+   ],
+   [
+    "styled gradient_transp",
+    "SOLID",
+    8388736,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    8388736,
+    false
+   ],
+   [
+    "styled hatch",
+    "HATCH",
+    16777164,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    true,
+    false,
+    null,
+    false,
+    16777164,
+    false
+   ],
+   [
+    "styled hatch_nobg",
+    "HATCH",
+    null,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    false,
+    false,
+    null,
+    false,
+    2147461632,
+    false
+   ],
+   [
+    "styled bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "STRETCH",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "NO_REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_transp",
+    "BITMAP",
+    null,
+    50,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ]
+  ]
+ },
+ {
+  "name": "para_styles",
+  "expr": "[(n,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' or (o.FillStyle.value=='HATCH' and o.FillBackground) 
else None),o.FillTransparence,bool(o.FillGradientName),(lambda 
g:(g.Style.value,g.StartColor,g.EndColor,g.Angle,g.Border))(o.FillGradient) if 
o.FillStyle.value=='GRADIENT' else None,bool(o.FillHatchName),(lambda 
h:(h.Style.value,h.Color,h.Distance,h.Angle))(o.FillHatch) if 
o.FillStyle.value=='HATCH' else None,o.FillBackground if o.FillStyle.value== 
[...]
+  "expect": [
+   [
+    "S_bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "NO_REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "STRETCH",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_transp",
+    "BITMAP",
+    null,
+    50,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_gradient",
+    "SOLID",
+    8388736,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    8388736,
+    false
+   ],
+   [
+    "S_gradient_transp",
+    "SOLID",
+    8388736,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    8388736,
+    false
+   ],
+   [
+    "S_hatch",
+    "HATCH",
+    16777164,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    true,
+    false,
+    null,
+    false,
+    16777164,
+    false
+   ],
+   [
+    "S_hatch_nobg",
+    "HATCH",
+    null,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    false,
+    false,
+    null,
+    false,
+    2147461632,
+    false
+   ],
+   [
+    "S_solid",
+    "SOLID",
+    3385941,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    3385941,
+    false
+   ],
+   [
+    "S_solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1697863082,
+    false
+   ]
+  ]
+ },
+ {
+  "name": "direct_paragraphs",
+  "expr": "[(p.String,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' else 
None),o.FillTransparence,(o.FillBitmapMode.value if o.FillStyle.value=='BITMAP' 
else None),('GraphicObject:' in o.FillBitmapURL and 'GraphicObject:0000' not in 
o.FillBitmapURL)))(p) for e in [doc.Text.createEnumeration()] for p in 
iter(lambda: e.nextElement() if e.hasMoreElements() else None, None) if 
p.String.startswith('direct')]",
+  "expect": [
+   [
+    "direct solid",
+    "SOLID",
+    3385941,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    null,
+    false
+   ],
+   [
+    "direct gradient",
+    "SOLID",
+    8388736,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct gradient_transp",
+    "SOLID",
+    8388736,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct hatch",
+    "SOLID",
+    16777164,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct hatch_nobg",
+    "SOLID",
+    16755200,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    "REPEAT",
+    true
+   ],
+   [
+    "direct bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    "STRETCH",
+    true
+   ],
+   [
+    "direct bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    "NO_REPEAT",
+    true
+   ],
+   [
+    "direct bitmap_transp",
+    "BITMAP",
+    null,
+    0,
+    "REPEAT",
+    true
+   ]
+  ]
+ }
+]
diff --git a/test/odf-validate/fixtures/para-fill-foreign/checks-frame.json 
b/test/odf-validate/fixtures/para-fill-foreign/checks-frame.json
new file mode 100644
index 0000000000..fa221c56ab
--- /dev/null
+++ b/test/odf-validate/fixtures/para-fill-foreign/checks-frame.json
@@ -0,0 +1,10 @@
+[
+ {
+  "name": "frame_style_g9",
+  "expr": "[doc.StyleFamilies.getByName('FrameStyles').hasByName('G9')] + 
([doc.StyleFamilies.getByName('FrameStyles').getByName('G9').FillStyle.value] 
if doc.StyleFamilies.getByName('FrameStyles').hasByName('G9') else [])",
+  "expect": [
+   true,
+   "NONE"
+  ]
+ }
+]
diff --git 
a/test/odf-validate/fixtures/para-fill-foreign/checks-unknown-attr.json 
b/test/odf-validate/fixtures/para-fill-foreign/checks-unknown-attr.json
new file mode 100644
index 0000000000..0bb9c9a660
--- /dev/null
+++ b/test/odf-validate/fixtures/para-fill-foreign/checks-unknown-attr.json
@@ -0,0 +1,22 @@
+[
+ {
+  "name": "style_user_attributes",
+  "expr": "[(n, 
sorted(st.getByName(n).ParaUserDefinedAttributes.ElementNames)) for st in 
[doc.StyleFamilies.getByName('ParagraphStyles')] for n in ('S_solid', 
'S_gradient')]",
+  "expect": [
+   [
+    "S_solid",
+    [
+     "loext:hyphenation-keep-line",
+     "loext:hyphenation-keep-type"
+    ]
+   ],
+   [
+    "S_gradient",
+    [
+     "loext:hyphenation-keep-line",
+     "loext:hyphenation-keep-type"
+    ]
+   ]
+  ]
+ }
+]
diff --git a/test/odf-validate/fixtures/para-fill-foreign/checks-v13-v14.json 
b/test/odf-validate/fixtures/para-fill-foreign/checks-v13-v14.json
new file mode 100644
index 0000000000..077532e379
--- /dev/null
+++ b/test/odf-validate/fixtures/para-fill-foreign/checks-v13-v14.json
@@ -0,0 +1,464 @@
+[
+ {
+  "name": "styled_paragraphs",
+  "expr": "[(p.String,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' or (o.FillStyle.value=='HATCH' and o.FillBackground) 
else None),o.FillTransparence,bool(o.FillGradientName),(lambda 
g:(g.Style.value,g.StartColor,g.EndColor,g.Angle,g.Border))(o.FillGradient) if 
o.FillStyle.value=='GRADIENT' else None,bool(o.FillHatchName),(lambda 
h:(h.Style.value,h.Color,h.Distance,h.Angle))(o.FillHatch) if 
o.FillStyle.value=='HATCH' else None,o.FillBackground if o.FillStyle. [...]
+  "expect": [
+   [
+    "styled solid",
+    "SOLID",
+    3385941,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    3385941,
+    false
+   ],
+   [
+    "styled solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1697863082,
+    false
+   ],
+   [
+    "styled gradient",
+    "GRADIENT",
+    null,
+    0,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     0,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    8388736,
+    false
+   ],
+   [
+    "styled gradient_transp",
+    "GRADIENT",
+    null,
+    30,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     0,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1283457152,
+    false
+   ],
+   [
+    "styled hatch",
+    "HATCH",
+    16777164,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    true,
+    false,
+    null,
+    false,
+    16777164,
+    false
+   ],
+   [
+    "styled hatch_nobg",
+    "HATCH",
+    null,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    false,
+    false,
+    null,
+    false,
+    2147461632,
+    false
+   ],
+   [
+    "styled bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "STRETCH",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "NO_REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_transp",
+    "BITMAP",
+    null,
+    50,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ]
+  ]
+ },
+ {
+  "name": "para_styles",
+  "expr": "[(n,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' or (o.FillStyle.value=='HATCH' and o.FillBackground) 
else None),o.FillTransparence,bool(o.FillGradientName),(lambda 
g:(g.Style.value,g.StartColor,g.EndColor,g.Angle,g.Border))(o.FillGradient) if 
o.FillStyle.value=='GRADIENT' else None,bool(o.FillHatchName),(lambda 
h:(h.Style.value,h.Color,h.Distance,h.Angle))(o.FillHatch) if 
o.FillStyle.value=='HATCH' else None,o.FillBackground if o.FillStyle.value== 
[...]
+  "expect": [
+   [
+    "S_bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "NO_REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "STRETCH",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_transp",
+    "BITMAP",
+    null,
+    50,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_gradient",
+    "GRADIENT",
+    null,
+    0,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     0,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    8388736,
+    false
+   ],
+   [
+    "S_gradient_transp",
+    "GRADIENT",
+    null,
+    30,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     0,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1283457152,
+    false
+   ],
+   [
+    "S_hatch",
+    "HATCH",
+    16777164,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    true,
+    false,
+    null,
+    false,
+    16777164,
+    false
+   ],
+   [
+    "S_hatch_nobg",
+    "HATCH",
+    null,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    false,
+    false,
+    null,
+    false,
+    2147461632,
+    false
+   ],
+   [
+    "S_solid",
+    "SOLID",
+    3385941,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    3385941,
+    false
+   ],
+   [
+    "S_solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1697863082,
+    false
+   ]
+  ]
+ },
+ {
+  "name": "direct_paragraphs",
+  "expr": "[(p.String,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' else 
None),o.FillTransparence,(o.FillBitmapMode.value if o.FillStyle.value=='BITMAP' 
else None),('GraphicObject:' in o.FillBitmapURL and 'GraphicObject:0000' not in 
o.FillBitmapURL)))(p) for e in [doc.Text.createEnumeration()] for p in 
iter(lambda: e.nextElement() if e.hasMoreElements() else None, None) if 
p.String.startswith('direct')]",
+  "expect": [
+   [
+    "direct solid",
+    "SOLID",
+    3385941,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    null,
+    false
+   ],
+   [
+    "direct gradient",
+    "SOLID",
+    8388736,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct gradient_transp",
+    "SOLID",
+    8388736,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct hatch",
+    "SOLID",
+    16777164,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct hatch_nobg",
+    "SOLID",
+    16755200,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    "REPEAT",
+    true
+   ],
+   [
+    "direct bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    "STRETCH",
+    true
+   ],
+   [
+    "direct bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    "NO_REPEAT",
+    true
+   ],
+   [
+    "direct bitmap_transp",
+    "BITMAP",
+    null,
+    0,
+    "REPEAT",
+    true
+   ]
+  ]
+ }
+]
diff --git a/test/odf-validate/fixtures/para-fill-foreign/checks.json 
b/test/odf-validate/fixtures/para-fill-foreign/checks.json
new file mode 100644
index 0000000000..0f88343021
--- /dev/null
+++ b/test/odf-validate/fixtures/para-fill-foreign/checks.json
@@ -0,0 +1,464 @@
+[
+ {
+  "name": "styled_paragraphs",
+  "expr": "[(p.String,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' or (o.FillStyle.value=='HATCH' and o.FillBackground) 
else None),o.FillTransparence,bool(o.FillGradientName),(lambda 
g:(g.Style.value,g.StartColor,g.EndColor,g.Angle,g.Border))(o.FillGradient) if 
o.FillStyle.value=='GRADIENT' else None,bool(o.FillHatchName),(lambda 
h:(h.Style.value,h.Color,h.Distance,h.Angle))(o.FillHatch) if 
o.FillStyle.value=='HATCH' else None,o.FillBackground if o.FillStyle. [...]
+  "expect": [
+   [
+    "styled solid",
+    "SOLID",
+    3385941,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    3385941,
+    false
+   ],
+   [
+    "styled solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1697863082,
+    false
+   ],
+   [
+    "styled gradient",
+    "GRADIENT",
+    null,
+    0,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    8388736,
+    false
+   ],
+   [
+    "styled gradient_transp",
+    "GRADIENT",
+    null,
+    30,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1283457152,
+    false
+   ],
+   [
+    "styled hatch",
+    "HATCH",
+    16777164,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    true,
+    false,
+    null,
+    false,
+    16777164,
+    false
+   ],
+   [
+    "styled hatch_nobg",
+    "HATCH",
+    null,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    false,
+    false,
+    null,
+    false,
+    2147461632,
+    false
+   ],
+   [
+    "styled bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "STRETCH",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "NO_REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "styled bitmap_transp",
+    "BITMAP",
+    null,
+    50,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ]
+  ]
+ },
+ {
+  "name": "para_styles",
+  "expr": "[(n,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' or (o.FillStyle.value=='HATCH' and o.FillBackground) 
else None),o.FillTransparence,bool(o.FillGradientName),(lambda 
g:(g.Style.value,g.StartColor,g.EndColor,g.Angle,g.Border))(o.FillGradient) if 
o.FillStyle.value=='GRADIENT' else None,bool(o.FillHatchName),(lambda 
h:(h.Style.value,h.Color,h.Distance,h.Angle))(o.FillHatch) if 
o.FillStyle.value=='HATCH' else None,o.FillBackground if o.FillStyle.value== 
[...]
+  "expect": [
+   [
+    "S_bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "NO_REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "STRETCH",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_bitmap_transp",
+    "BITMAP",
+    null,
+    50,
+    false,
+    null,
+    false,
+    null,
+    null,
+    true,
+    "REPEAT",
+    true,
+    -1,
+    true
+   ],
+   [
+    "S_gradient",
+    "GRADIENT",
+    null,
+    0,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    8388736,
+    false
+   ],
+   [
+    "S_gradient_transp",
+    "GRADIENT",
+    null,
+    30,
+    true,
+    [
+     "LINEAR",
+     16711680,
+     255,
+     300,
+     10
+    ],
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1283457152,
+    false
+   ],
+   [
+    "S_hatch",
+    "HATCH",
+    16777164,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    true,
+    false,
+    null,
+    false,
+    16777164,
+    false
+   ],
+   [
+    "S_hatch_nobg",
+    "HATCH",
+    null,
+    0,
+    false,
+    null,
+    true,
+    [
+     "SINGLE",
+     16755200,
+     200,
+     450
+    ],
+    false,
+    false,
+    null,
+    false,
+    2147461632,
+    false
+   ],
+   [
+    "S_solid",
+    "SOLID",
+    3385941,
+    0,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    3385941,
+    false
+   ],
+   [
+    "S_solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    false,
+    null,
+    false,
+    null,
+    null,
+    false,
+    null,
+    false,
+    1697863082,
+    false
+   ]
+  ]
+ },
+ {
+  "name": "direct_paragraphs",
+  "expr": "[(p.String,)+(lambda o:(o.FillStyle.value,(o.FillColor if 
o.FillStyle.value=='SOLID' else 
None),o.FillTransparence,(o.FillBitmapMode.value if o.FillStyle.value=='BITMAP' 
else None),('GraphicObject:' in o.FillBitmapURL and 'GraphicObject:0000' not in 
o.FillBitmapURL)))(p) for e in [doc.Text.createEnumeration()] for p in 
iter(lambda: e.nextElement() if e.hasMoreElements() else None, None) if 
p.String.startswith('direct')]",
+  "expect": [
+   [
+    "direct solid",
+    "SOLID",
+    3385941,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct solid_transp",
+    "SOLID",
+    3364266,
+    40,
+    null,
+    false
+   ],
+   [
+    "direct gradient",
+    "SOLID",
+    8388736,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct gradient_transp",
+    "SOLID",
+    8388736,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct hatch",
+    "SOLID",
+    16777164,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct hatch_nobg",
+    "SOLID",
+    16755200,
+    0,
+    null,
+    false
+   ],
+   [
+    "direct bitmap_tile",
+    "BITMAP",
+    null,
+    0,
+    "REPEAT",
+    true
+   ],
+   [
+    "direct bitmap_stretch",
+    "BITMAP",
+    null,
+    0,
+    "STRETCH",
+    true
+   ],
+   [
+    "direct bitmap_once",
+    "BITMAP",
+    null,
+    0,
+    "NO_REPEAT",
+    true
+   ],
+   [
+    "direct bitmap_transp",
+    "BITMAP",
+    null,
+    0,
+    "REPEAT",
+    true
+   ]
+  ]
+ }
+]
diff --git a/test/odf-validate/fixtures/para-fill-foreign/edge-both-forms.odt 
b/test/odf-validate/fixtures/para-fill-foreign/edge-both-forms.odt
new file mode 100644
index 0000000000..173aef6dff
Binary files /dev/null and 
b/test/odf-validate/fixtures/para-fill-foreign/edge-both-forms.odt differ
diff --git 
a/test/odf-validate/fixtures/para-fill-foreign/edge-duplicate-nested.odt 
b/test/odf-validate/fixtures/para-fill-foreign/edge-duplicate-nested.odt
new file mode 100644
index 0000000000..bb5336f422
Binary files /dev/null and 
b/test/odf-validate/fixtures/para-fill-foreign/edge-duplicate-nested.odt differ
diff --git a/test/odf-validate/fixtures/para-fill-foreign/edge-empty.odt 
b/test/odf-validate/fixtures/para-fill-foreign/edge-empty.odt
new file mode 100644
index 0000000000..ae99d5f699
Binary files /dev/null and 
b/test/odf-validate/fixtures/para-fill-foreign/edge-empty.odt differ
diff --git a/test/odf-validate/fixtures/para-fill-foreign/edge-nonparagraph.odt 
b/test/odf-validate/fixtures/para-fill-foreign/edge-nonparagraph.odt
new file mode 100644
index 0000000000..3f12742ca5
Binary files /dev/null and 
b/test/odf-validate/fixtures/para-fill-foreign/edge-nonparagraph.odt differ
diff --git a/test/odf-validate/fixtures/para-fill-foreign/edge-unknown-attr.odt 
b/test/odf-validate/fixtures/para-fill-foreign/edge-unknown-attr.odt
new file mode 100644
index 0000000000..4f9cdfb1f1
Binary files /dev/null and 
b/test/odf-validate/fixtures/para-fill-foreign/edge-unknown-attr.odt differ
diff --git a/test/odf-validate/fixtures/para-fill-foreign/edge-unknown-name.odt 
b/test/odf-validate/fixtures/para-fill-foreign/edge-unknown-name.odt
new file mode 100644
index 0000000000..365246ad94
Binary files /dev/null and 
b/test/odf-validate/fixtures/para-fill-foreign/edge-unknown-name.odt differ
diff --git a/test/odf-validate/fixtures/para-fill-foreign/foreign-v11.odt 
b/test/odf-validate/fixtures/para-fill-foreign/foreign-v11.odt
new file mode 100644
index 0000000000..904a2da505
Binary files /dev/null and 
b/test/odf-validate/fixtures/para-fill-foreign/foreign-v11.odt differ
diff --git a/test/odf-validate/fixtures/para-fill-foreign/foreign-v3.odt 
b/test/odf-validate/fixtures/para-fill-foreign/foreign-v3.odt
new file mode 100644
index 0000000000..ea95774b90
Binary files /dev/null and 
b/test/odf-validate/fixtures/para-fill-foreign/foreign-v3.odt differ
diff --git a/test/odf-validate/fixtures/para-fill-foreign/foreign-v8.odt 
b/test/odf-validate/fixtures/para-fill-foreign/foreign-v8.odt
new file mode 100644
index 0000000000..f24676d614
Binary files /dev/null and 
b/test/odf-validate/fixtures/para-fill-foreign/foreign-v8.odt differ
diff --git a/test/odf-validate/fixtures/para-fill-foreign/gen.bas 
b/test/odf-validate/fixtures/para-fill-foreign/gen.bas
new file mode 100644
index 0000000000..f8a171dd49
--- /dev/null
+++ b/test/odf-validate/fixtures/para-fill-foreign/gen.bas
@@ -0,0 +1,95 @@
+Function Gr(c1 As Long, c2 As Long, sty As Long, s As Long, e As Long)
+  Dim g As New com.sun.star.awt.Gradient
+  g.Style = sty : g.StartColor = c1 : g.EndColor = c2
+  g.Angle = 300 : g.Border = 10 : g.XOffset = 50 : g.YOffset = 50
+  g.StartIntensity = s : g.EndIntensity = e : g.StepCount = 0
+  Gr = g
+End Function
+
+Sub Fill(o As Object, kind As String, png As String)
+  Dim h As New com.sun.star.drawing.Hatch
+  Select Case kind
+  Case "solid"
+    o.FillStyle = com.sun.star.drawing.FillStyle.SOLID
+    o.FillColor = &H33AA55
+  Case "solid_transp"
+    o.FillStyle = com.sun.star.drawing.FillStyle.SOLID
+    o.FillColor = &H3355AA
+    o.FillTransparence = 40
+  Case "gradient"
+    o.FillStyle = com.sun.star.drawing.FillStyle.GRADIENT
+    o.FillGradient = Gr(&HFF0000, &H0000FF, 0, 100, 100)
+  Case "gradient_transp"
+    o.FillStyle = com.sun.star.drawing.FillStyle.GRADIENT
+    o.FillGradient = Gr(&HFF0000, &H0000FF, 0, 100, 100)
+    o.FillTransparence = 30
+  Case "gradient_tgrad"
+    o.FillStyle = com.sun.star.drawing.FillStyle.GRADIENT
+    o.FillGradient = Gr(&HFF0000, &H0000FF, 0, 100, 100)
+    o.FillTransparenceGradient = Gr(&H000000, &HFFFFFF, 0, 100, 100)
+  Case "hatch", "hatch_nobg"
+    h.Style = com.sun.star.drawing.HatchStyle.SINGLE : h.Color = &H00AA00 : 
h.Distance = 200 : h.Angle = 450
+    o.FillStyle = com.sun.star.drawing.FillStyle.HATCH
+    o.FillHatch = h
+    If kind = "hatch" Then
+      o.FillColor = &HFFFFCC
+      o.FillBackground = True
+    Else
+      o.FillBackground = False
+    End If
+  Case "bitmap_tile", "bitmap_stretch", "bitmap_once", "bitmap_transp"
+    o.FillStyle = com.sun.star.drawing.FillStyle.BITMAP
+    o.FillBitmapURL = png
+    If kind = "bitmap_stretch" Then
+      o.FillBitmapStretch = True
+      o.FillBitmapTile = False
+    ElseIf kind = "bitmap_once" Then
+      o.FillBitmapStretch = False
+      o.FillBitmapTile = False
+    Else
+      o.FillBitmapStretch = False
+      o.FillBitmapTile = True
+    End If
+    If kind = "bitmap_transp" Then o.FillTransparence = 50
+  End Select
+End Sub
+
+Sub Gen
+  Dim out As String, png As String
+  out = Environ("PFF_OUT") : png = Environ("PFF_PNG")
+  Dim a(0) As New com.sun.star.beans.PropertyValue
+  a(0).Name = "Hidden" : a(0).Value = True
+  Dim doc As Object
+  doc = StarDesktop.loadComponentFromURL("private:factory/swriter", "_blank", 
0, a())
+  Dim kinds
+  kinds = Split(Environ("PFF_KINDS"), ",")
+  Dim styles As Object, s As Object, i As Integer
+  styles = doc.StyleFamilies.getByName("ParagraphStyles")
+  For i = 0 To UBound(kinds)
+    s = doc.createInstance("com.sun.star.style.ParagraphStyle")
+    s.ParentStyle = "Standard"
+    styles.insertByName("S_" + kinds(i), s)
+    Fill s, kinds(i), png
+  Next
+  Dim cur As Object, k As Integer
+  cur = doc.Text.createTextCursor()
+  For i = 0 To UBound(kinds)
+    doc.Text.insertString(cur, "direct " + kinds(i), False)
+    doc.Text.insertControlCharacter(cur, 
com.sun.star.text.ControlCharacter.PARAGRAPH_BREAK, False)
+    doc.Text.insertString(cur, "styled " + kinds(i), False)
+    doc.Text.insertControlCharacter(cur, 
com.sun.star.text.ControlCharacter.PARAGRAPH_BREAK, False)
+  Next
+  Dim en As Object, p As Object
+  en = doc.Text.createEnumeration()
+  For i = 0 To UBound(kinds)
+    p = en.nextElement()
+    Fill p, kinds(i), png
+    p = en.nextElement()
+    p.ParaStyleName = "S_" + kinds(i)
+  Next
+  Dim b(0) As New com.sun.star.beans.PropertyValue
+  b(0).Name = "FilterName" : b(0).Value = "writer8"
+  doc.storeToURL(ConvertToURL(out), b())
+  doc.close(True)
+  StarDesktop.terminate()
+End Sub
diff --git a/test/odf-validate/fixtures/para-fill-foreign/make-aoo.py 
b/test/odf-validate/fixtures/para-fill-foreign/make-aoo.py
new file mode 100644
index 0000000000..6e7b52f2f2
--- /dev/null
+++ b/test/odf-validate/fixtures/para-fill-foreign/make-aoo.py
@@ -0,0 +1,64 @@
+# usage: runpy.sh fixtures/para-fill-foreign/make-aoo.py <port> <out.odt> 
<tile.png>   (office saving ODF 1.2 Extended)
+# Builds the same paragraphs and paragraph styles as gen.bas, with AOO's own 
fill properties.
+import sys, uno
+from unohelp import connect, desktop, pv
+from com.sun.star.awt import Gradient
+from com.sun.star.awt.GradientStyle import LINEAR
+from com.sun.star.drawing import Hatch
+from com.sun.star.drawing.HatchStyle import SINGLE
+from com.sun.star.drawing.FillStyle import SOLID, GRADIENT, HATCH, BITMAP
+from com.sun.star.drawing.BitmapMode import REPEAT, STRETCH, NO_REPEAT
+
+port, out, png = int(sys.argv[1]), sys.argv[2], 
uno.systemPathToFileUrl(sys.argv[3])
+d = desktop(connect(port))
+doc = d.loadComponentFromURL("private:factory/swriter", "_blank", 0, 
(pv("Hidden", True),))
+
+def grad(c1, c2):
+    g = Gradient(); g.Style = LINEAR; g.StartColor = c1; g.EndColor = c2
+    g.Angle = 300; g.Border = 10; g.XOffset = 50; g.YOffset = 50
+    g.StartIntensity = 100; g.EndIntensity = 100; g.StepCount = 0
+    return g
+
+def hatch():
+    h = Hatch(); h.Style = SINGLE; h.Color = 0xFFAA00; h.Distance = 200; 
h.Angle = 450
+    return h
+
+def fill(o, kind):
+    if kind == "solid":
+        o.FillStyle = SOLID; o.FillColor = 0x33AA55
+    elif kind == "solid_transp":
+        o.FillStyle = SOLID; o.FillColor = 0x3355AA; o.FillTransparence = 40
+    elif kind == "gradient":
+        o.FillStyle = GRADIENT; o.FillGradient = grad(0xFF0000, 0x0000FF)
+    elif kind == "gradient_transp":
+        o.FillStyle = GRADIENT; o.FillGradient = grad(0xFF0000, 0x0000FF); 
o.FillTransparence = 30
+    elif kind == "hatch":
+        o.FillStyle = HATCH; o.FillHatch = hatch(); o.FillColor = 0xFFFFCC; 
o.FillBackground = True
+    elif kind == "hatch_nobg":
+        o.FillStyle = HATCH; o.FillHatch = hatch(); o.FillBackground = False
+    elif kind.startswith("bitmap"):
+        o.FillStyle = BITMAP; o.FillBitmapURL = png
+        o.FillBitmapMode = {"bitmap_stretch": STRETCH, "bitmap_once": 
NO_REPEAT}.get(kind, REPEAT)
+        if kind == "bitmap_transp":
+            o.FillTransparence = 50
+
+kinds = ["solid", "solid_transp", "gradient", "gradient_transp", "hatch", 
"hatch_nobg", "bitmap_tile", "bitmap_stretch", "bitmap_once", "bitmap_transp"]
+styles = doc.StyleFamilies.getByName("ParagraphStyles")
+for k in kinds:
+    s = doc.createInstance("com.sun.star.style.ParagraphStyle")
+    s.ParentStyle = "Standard"
+    styles.insertByName("S_" + k, s)
+    fill(s, k)
+PB = 
uno.getConstantByName("com.sun.star.text.ControlCharacter.PARAGRAPH_BREAK")
+cur = doc.Text.createTextCursor()
+for k in kinds:
+    for kind in ("direct", "styled"):
+        doc.Text.insertString(cur, kind + " " + k, False)
+        doc.Text.insertControlCharacter(cur, PB, False)
+paras = doc.Text.createEnumeration()
+for k in kinds:
+    fill(paras.nextElement(), k)
+    paras.nextElement().ParaStyleName = "S_" + k
+doc.storeToURL(uno.systemPathToFileUrl(out), (pv("FilterName", "writer8"),))
+doc.close(True)
+print("saved", out)
diff --git a/test/odf-validate/fixtures/para-fill-foreign/make.py 
b/test/odf-validate/fixtures/para-fill-foreign/make.py
new file mode 100644
index 0000000000..fe51055b34
--- /dev/null
+++ b/test/odf-validate/fixtures/para-fill-foreign/make.py
@@ -0,0 +1,118 @@
+# Known limitations of the import these documents exercise:
+#  - a fill is ignored when its gradient, hatch or bitmap definition comes 
after the style that uses it;
+#  - multi-stop gradients, unnamed direct gradients and hatches, and the 
opacity of a direct bitmap are not imported;
+#  - draw:angle with a unit suffix such as "30deg" is read as 0 (existing 
behaviour of the gradient import, unrelated to the fill element).
+# usage: python3 -I make.py <other-suite-soffice> <scratch-dir>
+# Writes foreign-v8.odt, foreign-v3.odt, foreign-v11.odt with the other suite 
saving at ODF version setting 8, 3, 11
+# (/org.openoffice.Office.Common/Save/ODF DefaultVersion), using gen.bas as 
macro Standard.Module1.Gen in a throw-away profile,
+# then derives the edge-*.odt variants by editing the XML text of 
foreign-v8.odt.
+import os, re, subprocess, sys, time, zipfile
+from xml.sax.saxutils import escape
+
+soffice, scratch = sys.argv[1], sys.argv[2]
+here = os.path.dirname(os.path.abspath(__file__))
+KINDS = 
"solid,solid_transp,gradient,gradient_transp,hatch,hatch_nobg,bitmap_tile,bitmap_stretch,bitmap_once,bitmap_transp"
+XCU = """<?xml version="1.0" encoding="UTF-8"?>
+<oor:items xmlns:oor="http://openoffice.org/2001/registry"; 
xmlns:xs="http://www.w3.org/2001/XMLSchema"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";>
+<item oor:path="/org.openoffice.Office.Common/Save/ODF"><prop 
oor:name="DefaultVersion" oor:op="fuse"><value>%d</value></prop></item>
+<item oor:path="/org.openoffice.Office.Common/Security/Scripting"><prop 
oor:name="MacroSecurityLevel" oor:op="fuse"><value>0</value></prop></item>
+</oor:items>
+"""
+MODULE = ('<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE script:module 
PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">\n'
+          '<script:module xmlns:script="http://openoffice.org/2000/script"; 
script:name="Module1" script:language="StarBasic">%s</script:module>\n')
+OPTS = ["--headless", "--nofirststartwizard", "--norestore", "--nologo"]
+
+def wait_gone(prof):
+    while subprocess.run(["pgrep", "-f", prof], 
capture_output=True).returncode == 0:
+        time.sleep(1)
+
+for mode in (8, 3, 11):
+    prof = os.path.join(scratch, "profile_%d" % mode)
+    env = dict(os.environ, PFF_KINDS=KINDS, PFF_OUT=os.path.join(here, 
"foreign-v%d.odt" % mode), PFF_PNG="file://" + os.path.join(here, "tile.png"))
+    ui = "-env:UserInstallation=file://" + prof
+    os.makedirs(os.path.join(prof, "user"))
+    open(os.path.join(prof, "user", "registrymodifications.xcu"), 
"w").write(XCU % mode)
+    subprocess.run([soffice, *OPTS, ui, "macro:///Standard.Module1.Main"], 
env=env, timeout=300)  # creates the profile and its Standard library
+    wait_gone(prof)
+    open(os.path.join(prof, "user", "basic", "Standard", "Module1.xba"), 
"w").write(MODULE % escape(open(os.path.join(here, "gen.bas")).read()))
+    subprocess.run([soffice, *OPTS, ui, "macro:///Standard.Module1.Gen"], 
env=env, timeout=300)
+    wait_gone(prof)
+    assert os.path.getsize(env["PFF_OUT"]) > 0, "no output for mode %d" % mode
+
+def scrub(name):
+    """replaces the generator string in meta.xml and the colour theme name in 
styles.xml by neutral ones"""
+    path = os.path.join(here, name)
+    zi = zipfile.ZipFile(path); items = [(it, zi.read(it.filename)) for it in 
zi.infolist()]; zi.close()
+    zo = zipfile.ZipFile(path, "w")
+    for it, data in items:
+        if it.filename == "meta.xml":
+            data = re.sub(rb"<meta:generator>[^<]*</meta:generator>", 
b"<meta:generator>other suite</meta:generator>", data)
+        if it.filename == "styles.xml":
+            data = re.sub(rb'(<loext:theme-colors loext:name=")[^"]*"', 
rb'\1Other"', data)
+        zo.writestr(it, data, compress_type=zipfile.ZIP_STORED if it.filename 
== "mimetype" else zipfile.ZIP_DEFLATED)
+    zo.close()
+
+for mode in (8, 3, 11):
+    scrub("foreign-v%d.odt" % mode)
+
+def rewrite(src, dst, fn):
+    zi = zipfile.ZipFile(os.path.join(here, src)); zo = 
zipfile.ZipFile(os.path.join(here, dst), "w")
+    for it in zi.infolist():
+        data = zi.read(it.filename)
+        if it.filename in ("content.xml", "styles.xml"):
+            data = fn(it.filename, data.decode("utf-8")).encode("utf-8")
+        zo.writestr(it, data, compress_type=zipfile.ZIP_STORED if it.filename 
== "mimetype" else zipfile.ZIP_DEFLATED)
+    zo.close()
+
+def sub1(t, old, new):
+    assert t.count(old) == 1, (old, t.count(old))
+    return t.replace(old, new)
+
+def style_xml(t, name):
+    m = re.search(r'<style:style style:name="%s".*?</style:style>' % 
re.escape(name), t, re.S)
+    assert m, name
+    return m
+
+# named gradient reference that does not exist
+rewrite("foreign-v8.odt", "edge-unknown-name.odt", lambda n, t: 
t.replace('draw:fill-gradient-name="Gradient_20_1"', 
'draw:fill-gradient-name="No_20_Such"') if n == "styles.xml" else t)
+# empty element in a paragraph style that has fo:background-color
+def empty(n, t):
+    if n != "styles.xml": return t
+    m = style_xml(t, "S_5f_gradient")
+    return t.replace(m.group(0), re.sub(r'<loext:graphic-properties[^>]*/>', 
'<loext:graphic-properties/>', m.group(0)))
+rewrite("foreign-v8.odt", "edge-empty.odt", empty)
+# same element in non-paragraph styles (text style, table cell style) plus a 
paragraph that uses them
+EXTRA_TEXT = '<style:style style:name="T9" 
style:family="text"><loext:graphic-properties draw:fill="solid" 
draw:fill-color="#ff0000"/></style:style>'
+EXTRA_CELL = '<style:style style:name="C9" 
style:family="table-cell"><loext:graphic-properties draw:fill="solid" 
draw:fill-color="#ff0000"/></style:style>'
+EXTRA_FRAME = '<style:style style:name="G9" 
style:family="graphic"><loext:graphic-properties draw:fill="solid" 
draw:fill-color="#ff0000"/></style:style>'
+def nonpara(n, t):
+    if n == "styles.xml":
+        return t.replace("</office:styles>", EXTRA_FRAME + "</office:styles>", 
1)
+    return t.replace("</office:automatic-styles>", EXTRA_TEXT + EXTRA_CELL + 
"</office:automatic-styles>", 1)
+rewrite("foreign-v8.odt", "edge-nonparagraph.odt", nonpara)
+# duplicate element (second one must lose) and nested element
+def dup(n, t):
+    if n != "styles.xml": return t
+    m = style_xml(t, "S_5f_solid")
+    s = m.group(0).replace('<style:paragraph-properties', 
'<loext:graphic-properties draw:fill="solid" 
draw:fill-color="#ff0000"/><style:paragraph-properties', 1)
+    m2 = style_xml(t, "S_5f_hatch")
+    h = m2.group(0).replace('draw:fill-hatch-solid="true"/>', 
'draw:fill-hatch-solid="true"><loext:graphic-properties draw:fill="solid" 
draw:fill-color="#ff0000"/></loext:graphic-properties>', 1)
+    return t.replace(m.group(0), s).replace(m2.group(0), h)
+rewrite("foreign-v8.odt", "edge-duplicate-nested.odt", dup)
+# the native form and the extension element in one style, both orders
+def both(n, t):
+    if n != "styles.xml": return t
+    m = style_xml(t, "S_5f_solid")
+    native = '<style:graphic-properties draw:fill="solid" 
draw:fill-color="#ff0000"/>'
+    s1 = m.group(0).replace('<loext:graphic-properties', native + 
'<loext:graphic-properties', 1)
+    m2 = style_xml(t, "S_5f_solid_5f_fill") if 'S_5f_solid_5f_fill' in t else 
style_xml(t, "S_5f_solid_5f_transp")
+    s2 = m2.group(0).replace('<style:paragraph-properties', native + 
'<style:paragraph-properties', 1)
+    return t.replace(m.group(0), s1).replace(m2.group(0), s2)
+rewrite("foreign-v8.odt", "edge-both-forms.odt", both)
+# an attribute of the extension element that no fill property maps to
+def unknown_attr(n, t):
+    if n != "styles.xml": return t
+    m = style_xml(t, "S_5f_solid")
+    return t.replace(m.group(0), m.group(0).replace('<loext:graphic-properties 
', '<loext:graphic-properties loext:future-attribute="x" ', 1))
+rewrite("foreign-v8.odt", "edge-unknown-attr.odt", unknown_attr)
+print("done")
diff --git a/test/odf-validate/fixtures/para-fill-foreign/mkexpect.py 
b/test/odf-validate/fixtures/para-fill-foreign/mkexpect.py
new file mode 100644
index 0000000000..c66b9840a8
--- /dev/null
+++ b/test/odf-validate/fixtures/para-fill-foreign/mkexpect.py
@@ -0,0 +1,75 @@
+# usage: python3 -I mkexpect.py <cap-dir> <tag>
+# Writes the expect values of the checks-*.json files.
+#  - styled paragraphs and paragraph styles: what AOO's own form gives after a 
reload (<cap-dir>/<tag>_aoo-form.json,
+#    captured by roundtrip.py --capture from aoo-form.odt, written by 
make-aoo.py with the same fills);
+#  - direct paragraphs: derived from the XML text of foreign-v8.odt (an 
unnamed gradient or hatch cannot be restored, so
+#    fo:background-color is what remains; a bitmap comes from 
style:background-image);
+#  - edge documents: the same, except the styles an edge changes, which take 
<cap-dir>/<tag>_<edge>.json (captured with
+#    libraries without the import) or the value stated below.
+import copy, json, os, re, sys, zipfile
+cap, tag = sys.argv[1], sys.argv[2]
+here = os.path.dirname(os.path.abspath(__file__))
+C = lambda d: json.load(open(os.path.join(cap, "%s_%s.json" % (tag, d))))
+checks = json.load(open(os.path.join(here, "checks.json")))
+ref = C("aoo-form")
+
+def rgb(s): return int(s[1:], 16)
+z = zipfile.ZipFile(os.path.join(here, "foreign-v8.odt")); content = 
z.read("content.xml").decode()
+direct = {}
+for m in re.finditer(r'<text:p text:style-name="(P\d+)">direct 
(\w+)</text:p>', content):
+    sty = re.search(r'<style:style style:name="%s".*?</style:style>' % 
m.group(1), content, re.S).group(0)
+    kind = m.group(2)
+    fill = re.search(r'<loext:graphic-properties draw:fill="(\w+)"(?: 
draw:fill-color="(#\w+)")?(?: draw:opacity="(\d+)%")?', sty)
+    bg = re.search(r'fo:background-color="(#\w+)"', sty)
+    rep = re.search(r'<style:background-image [^>]*style:repeat="([\w-]+)"', 
sty)
+    if fill.group(1) == "solid":
+        direct[kind] = ["SOLID", rgb(fill.group(2)), 100 - int(fill.group(3) 
or 100) if fill.group(3) else 0, None, False]
+    elif fill.group(1) == "bitmap" and "<style:background-image " in sty:
+        direct[kind] = ["BITMAP", None, 0, {"stretch": "STRETCH", "no-repeat": 
"NO_REPEAT"}.get(rep.group(1) if rep else "", "REPEAT"), True]
+    else:
+        direct[kind] = ["SOLID", rgb(bg.group(1)), 0, None, False]
+direct_rows = [["direct " + k] + v for k, v in direct.items()]
+
+def build(styled, styles, direct_rows):
+    return {"styled_paragraphs": styled, "para_styles": styles, 
"direct_paragraphs": direct_rows}
+
+base = build(ref["styled_paragraphs"], ref["para_styles"], direct_rows)
+
+def write(name, exp, only=None, angle=None):
+    ck = copy.deepcopy(checks)
+    out = []
+    for c in ck:
+        if only and c["name"] not in only: continue
+        e = copy.deepcopy(exp[c["name"]])
+        if angle is not None:
+            for r in e:
+                if len(r) > 5 and r[5] is not None and r[1] == "GRADIENT": 
r[5][3] = angle
+        c["expect"] = e
+        out.append(c)
+    json.dump(out, open(os.path.join(here, name), "w"), indent=1); 
open(os.path.join(here, name), "a").write("\n")
+
+def swap(exp, edge, labels):
+    """replace the rows named in labels by the rows of the capture of <edge> 
taken without the import"""
+    red = C(edge); exp = copy.deepcopy(exp)
+    for k in ("styled_paragraphs", "para_styles"):
+        exp[k] = [next(r for r in red[k] if r[0] == row[0]) if row[0] in 
labels else row for row in exp[k]]
+    return exp
+
+write("checks.json", base)
+write("checks-v13-v14.json", base, angle=0)
+write("checks-aoo-form.json", base, only=("styled_paragraphs", "para_styles"))
+write("checks-edge-unknown-name.json", swap(base, "edge-unknown-name", 
("styled gradient", "styled gradient_transp", "S_gradient", 
"S_gradient_transp")))
+write("checks-edge-empty.json", swap(base, "edge-empty", ("styled gradient", 
"S_gradient")))
+write("checks-edge-nonparagraph.json", base)
+write("checks-edge-duplicate-nested.json", base)
+both = swap(base, "edge-both-forms", ("styled solid", "S_solid"))
+for k in ("styled_paragraphs", "para_styles"):
+    for r in both[k]:
+        if r[0] in ("styled solid_transp", "S_solid_transp"):   # native 
element after the extension one: its colour wins, the extension's opacity 
survives
+            r[2] = 16711680; r[3] = 40; r[12] = 1711210496
+write("checks-edge-both-forms.json", both)
+write("checks-edge-unknown-attr.json", base)
+ua = json.load(open(os.path.join(here, "checks-unknown-attr.json")))
+ua[0]["expect"] = C("edge-unknown-attr_ua")["style_user_attributes"]   # what 
the styles carry without the import: the attribute must not be added
+json.dump(ua, open(os.path.join(here, "checks-unknown-attr.json"), "w"), 
indent=1); open(os.path.join(here, "checks-unknown-attr.json"), "a").write("\n")
+print("written; direct rows:", len(direct_rows))
diff --git a/test/odf-validate/fixtures/para-fill-foreign/tile.png 
b/test/odf-validate/fixtures/para-fill-foreign/tile.png
new file mode 100644
index 0000000000..9ac8fdd049
Binary files /dev/null and 
b/test/odf-validate/fixtures/para-fill-foreign/tile.png differ

Reply via email to