Skip to content

Commit 21c3acb

Browse files
authored
Merge pull request #17163 from QuLogic/pdf-marker-clip
Fix clipping of markers in PDF backend.
2 parents 76e9e26 + d131534 commit 21c3acb

File tree

2 files changed

+49
-1
lines changed

2 files changed

+49
-1
lines changed

lib/matplotlib/backends/backend_pdf.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -1618,7 +1618,14 @@ def markerObject(self, path, trans, fill, stroke, lw, joinstyle,
16181618
def writeMarkers(self):
16191619
for ((pathops, fill, stroke, joinstyle, capstyle),
16201620
(name, ob, bbox, lw)) in self.markers.items():
1621-
bbox = bbox.padded(lw * 0.5)
1621+
# bbox wraps the exact limits of the control points, so half a line
1622+
# will appear outside it. If the join style is miter and the line
1623+
# is not parallel to the edge, then the line will extend even
1624+
# further. From the PDF specification, Section 8.4.3.5, the miter
1625+
# limit is miterLength / lineWidth and from Table 52, the default
1626+
# is 10. With half the miter length outside, that works out to the
1627+
# following padding:
1628+
bbox = bbox.padded(lw * 5)
16221629
self.beginStream(
16231630
ob.id, None,
16241631
{'Type': Name('XObject'), 'Subtype': Name('Form'),

lib/matplotlib/tests/test_marker.py

+41
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,44 @@ def draw_ref_marker(y, style, size):
139139

140140
ax_test.set(xlim=(-0.5, 1.5), ylim=(-0.5, 1.5))
141141
ax_ref.set(xlim=(-0.5, 1.5), ylim=(-0.5, 1.5))
142+
143+
144+
@check_figures_equal()
145+
def test_marker_clipping(fig_ref, fig_test):
146+
# Plotting multiple markers can trigger different optimized paths in
147+
# backends, so compare single markers vs multiple to ensure they are
148+
# clipped correctly.
149+
marker_count = len(markers.MarkerStyle.markers)
150+
marker_size = 50
151+
ncol = 7
152+
nrow = marker_count // ncol + 1
153+
154+
width = 2 * marker_size * ncol
155+
height = 2 * marker_size * nrow * 2
156+
fig_ref.set_size_inches((width / fig_ref.dpi, height / fig_ref.dpi))
157+
ax_ref = fig_ref.add_axes([0, 0, 1, 1])
158+
fig_test.set_size_inches((width / fig_test.dpi, height / fig_ref.dpi))
159+
ax_test = fig_test.add_axes([0, 0, 1, 1])
160+
161+
for i, marker in enumerate(markers.MarkerStyle.markers):
162+
x = i % ncol
163+
y = i // ncol * 2
164+
165+
# Singular markers per call.
166+
ax_ref.plot([x, x], [y, y + 1], c='k', linestyle='-', lw=3)
167+
ax_ref.plot(x, y, c='k',
168+
marker=marker, markersize=marker_size, markeredgewidth=10,
169+
fillstyle='full', markerfacecolor='white')
170+
ax_ref.plot(x, y + 1, c='k',
171+
marker=marker, markersize=marker_size, markeredgewidth=10,
172+
fillstyle='full', markerfacecolor='white')
173+
174+
# Multiple markers in a single call.
175+
ax_test.plot([x, x], [y, y + 1], c='k', linestyle='-', lw=3,
176+
marker=marker, markersize=marker_size, markeredgewidth=10,
177+
fillstyle='full', markerfacecolor='white')
178+
179+
ax_ref.set(xlim=(-0.5, ncol), ylim=(-0.5, 2 * nrow))
180+
ax_test.set(xlim=(-0.5, ncol), ylim=(-0.5, 2 * nrow))
181+
ax_ref.axis('off')
182+
ax_test.axis('off')

0 commit comments

Comments
 (0)