Is there an officially supported to do this?
Unfortunately, no.
Is there a very hacky way to do this?
Absolutely!
I wrote a function that draws multi line operators like this:

Here's the code:
def multi_line_operator(
layout: bpy.types.UILayout,
operator: str,
lines: list[str],
line_spacing: float = .85,
padding=.4,
icon: str = "NONE",
):
"""Draw an operator with a multi line label"""
# Draw the text over multiple rows in a column
col = layout.column(align=True)
col.scale_y = line_spacing
for line in lines:
row = col.row()
row.alignment = "CENTER"
row.label(text=line)
# The scale needed to cover all of the text with the button
scale = len(lines) * col.scale_y + padding
col = layout.column(align=True)
# This is the magic: drawing a property with a negative y scale will push everything drawn below it up.
# It's very hacky, but it works...
# The only reason not to use it is that it might be broken in future updates.
propcol = col.column(align=True)
propcol.scale_y = -scale # give this column a negative scale
# This can be any property, it doesn't matter which as it won't be editable.
propcol.prop(bpy.context.scene.unit_settings, "scale_length")
# Draw an operator below the prop so that it will be moved up.
row = col.row(align=True)
row.scale_y = scale
op = row.operator(operator, text=" ", icon=icon)
return op
How does it work?
It works by abusing a bug/unintended feature where if you draw a property on a row with a negative scale, it will start to move all elements drawn afterwards upwards, meaning that you can essentially draw two UI elements in the same place.
That means that in this case, we can first simply draw the text we want over multiple labels, and the afterwards, draw a property in a row with a negative scale (in this case -1 multiplied by the number of lines), and after that draw an operator with the same scale but positive, which essentially draws the button behind the labels.
How do I use it?
Just drop the function into your project, and then call it like so:
multi_line_operator(
layout,
"wm.context_set_string",
lines=["ooh, cool text", "more text!", "woah!"],
line_spacing=.85,
)
Are there any differences compared to drawing a normal operator?
Yes, but they're pretty minor. Here are the ones I've found so far:
- Text is generally darker than on a normal operator (unless it is drawn in a box), and it also doesn't light up when hovered over with the mouse.
- Drawing an icon on the operator won't cause the text to go off-centre (this could be implemented, though).
Hope that helps!