https://bugs.documentfoundation.org/show_bug.cgi?id=173238
Bug ID: 173238
Summary: Impress: PPTX with an SVG shape fill never finishes
loading, because XFillBitmapItem::isPattern()
rasterises the whole SVG just to test for an 8x8
pattern bitmap
Product: LibreOffice
Version: 26.2.5.2 release
Hardware: All
OS: All
Status: UNCONFIRMED
Severity: normal
Priority: medium
Component: graphics stack
Assignee: [email protected]
Reporter: [email protected]
Description:
svx/source/xoutdev/xattrbmp.cxx:162
bool XFillBitmapItem::isPattern() const
{
Color aBack, aFront;
return vcl::bitmap::isHistorical8x8(
GetGraphicObject().GetGraphic().GetBitmap(), aBack, aFront);
}
GetBitmap() forces a full rasterisation of the graphic. But isHistorical8x8()
(vcl/source/bitmap/BitmapTools.cxx:882) returns false immediately unless the
bitmap is exactly 8x8 pixels.
So LibreOffice renders a 1.8 MB SVG only to find out it is not 8 pixels wide.
These "historical 8x8" patterns only come from old binary formats. A vector
graphic can never be one. Checking the graphic type before calling GetBitmap()
would skip the render entirely and cost nothing.
This is not the same code path as tdf#168699. That bug is an SVG inserted as an
image, which becomes an SdrGrafObj and reaches basegfx through
Graphic::GetPrefSize(). This one is an SVG used as a shape fill, which becomes
an XFillBitmapItem and reaches basegfx through isPattern(). Fixing the SVG
renderer would fix both. Fixing isPattern() fixes only this one, but it is a
small and safe change.
Steps to Reproduce:
1. Open the attached svg-fill-repro.pptx in Impress.
2. Wait.
The attachment is one slide with one rectangle. The rectangle's area fill is an
SVG. Nothing else is in the file.
To confirm the SVG fill is what does it, the second attachment
control-png-only.pptx is byte-for-byte the same file with only the
asvg:svgBlip extension removed, so the shape falls back to its PNG. It opens in
0.9 seconds.
Actual Results:
The file never opens. soffice.bin sits at 100% CPU. It did not finish in 180
seconds and I killed it. Memory stays flat at about 220 MB, so it is CPU only,
not a leak.
Stack, taken by interrupting the hang:
log10
rtl_math_approxEqual
basegfx::utils::isPointOnLine
basegfx::utils::isPointOnPolygon
basegfx::utils::isInside
basegfx::utils::isInside
basegfx::utils::createNonzeroConform
(libsvgio)
VectorGraphicData::ensureSequenceAndRange
VectorGraphicData::ensureReplacement
VectorGraphicData::getReplacement
ImpGraphic::updateBitmapFromVectorGraphic
ImpGraphic::getBitmap
Graphic::GetBitmap
XFillBitmapItem::isPattern <-- the avoidable call
XFillBitmapItem::checkForUniqueItem
sdr::properties::BaseProperties::SetMergedItemSetAndBroadcast
SdrObject::SetMergedItemSetAndBroadcast
SvxShape::SetFillAttribute
SvxShape::setPropertyValueImpl
SvxShape::setPropertyValues
oox::PropertySet::setProperties
The original file I hit this with produces the same stack.
Expected Results:
The file opens. isPattern() should answer "no" without rendering the graphic,
because a vector graphic is never an 8x8 pattern bitmap.
Reproducible: Always
User Profile Reset: Yes
Additional Info:
Version: 26.2.5.2 (X86_64) / LibreOffice Community
Build ID: 30643f3b95f81f04449dc87ba649cc313a42270e
CPU threads: 8; OS: Linux 7.1; UI render: default; VCL: kf6
Locale: en-US (en_US.UTF-8); UI: en-US
Calc: threaded
Also reproduces on a locally built 26.2.4.2 with a Skia/Vulkan Qt backend, so
it is not specific to the packaged build or to any rendering backend. Verified
with --headless --convert-to pdf, which uses no GPU at all.
The SVG in the attachment is generated, not taken from anywhere. It is one
<path> with 300 concentric closed subpaths of 60 cubic beziers each, 18,000
segments total, and no fill-rule attribute, so nonzero applies. Concentric
matters: it makes each subpath's bounding box contain the ones inside it, which
is what stops createNonzeroConform rejecting the pairs on the cheap range check
before it reaches isInside().
I first hit this on a real presentation template whose worst SVG is one <path>
with a 1,362,526-character "d" attribute, about 36,600 beziers over 19
subpaths. Same stack. I have not attached that file because I do not think its
licence allows redistribution. Happy to provide it privately if it helps.
Related: tdf#168699 (the SVG renderer itself). Same basegfx stack, different
entry point.
Note on the existing guard in createNonzeroConform
(basegfx/source/polygon/b2dpolypolygoncutter.cxx:701):
if (rCandidate.count() > 1000)
count() is the number of subpaths, not points. This path has 19 subpaths, so
the guard never fires no matter how many bezier segments they contain. That may
be worth a look on tdf#168699 rather than here.
Workaround for anyone hitting this: each SVG in a PPTX sits in an asvg:svgBlip
extension inside an a:blip that already points at a PNG fallback. Removing the
extension blocks makes the file load in about 5 seconds with no visible
difference.
--
You are receiving this mail because:
You are the assignee for the bug.