Why does the bounding box from Element.GetBBox jump in size when stroking is on?

Question:

I find that the bbox of an ellipse drawn with the path stroke is different from the same ellipse drawn without a path stroke.

For example.
`
PDFDoc doc;
ElementBuilder builder;
ElementWriter writer;
Page page = doc.PageCreate();
writer.Begin(page);
Rect bbox;
// create circle with just fill, no stroke
Element* element = builder.CreateEllipse(250, 300, 25, 25);
element->SetPathFill(true);
element->GetGState()->SetFillColorSpace(ColorSpace::CreateDeviceRGB());
element->GetGState()->SetFillColor(ColorPt(1, 0, 1));
element->GetBBox(bbox);
writer.WriteElement(element);

// draw bounding box
element = builder.CreateRect(bbox.x1, bbox.y1, bbox.Width(), bbox.Height());
element->SetPathFill(false);
element->SetPathStroke(true);
element->GetGState()->SetLineWidth(1);
element->GetGState()->SetStrokeColorSpace(ColorSpace::CreateDeviceRGB());
element->GetGState()->SetStrokeColor(ColorPt(1, 0, 0));
writer.WriteElement(element);

// create circle with fill and stroke
element = builder.CreateEllipse(325, 300, 25, 25);
element->SetPathFill(true);
element->GetGState()->SetFillColorSpace(ColorSpace::CreateDeviceRGB());
element->GetGState()->SetFillColor(ColorPt(1, 0, 1));
element->SetPathStroke(true);
element->GetGState()->SetStrokeColorSpace(ColorSpace::CreateDeviceRGB());
element->GetGState()->SetStrokeColor(ColorPt(0, 0, 1));
element->GetBBox(bbox);
writer.WriteElement(element);

// draw bounding box
element = builder.CreateRect(bbox.x1, bbox.y1, bbox.Width(), bbox.Height());
element->SetPathFill(false);
element->SetPathStroke(true);
element->GetGState()->SetLineWidth(1);
element->GetGState()->SetStrokeColorSpace(ColorSpace::CreateDeviceRGB());
element->GetGState()->SetStrokeColor(ColorPt(1, 0, 0));
writer.WriteElement(element);

writer.End();
doc.PagePushBack(page);
`

Where the red square is the bounding box, and the blue outline is the stroke.

Answer:

This is because by default paths are joined using Miter joints. The other options are Round and Bevel.

If two paths make a sharp angle/point, then a miter joint can stick very far out. Because of this there is another option, miter limit, which is defaults to a ratio of 10, where the miter length can be 10 times larger than the stroke width.

Element.GetBBox is making a best guess, and so simply turning on stroking triggers miter limit as the new edge.

To avoid this you can either set the MiterLimit to 1 (not zero)

element->GetGState()->SetMiterLimit(1);

Or change the join style to Bevel or Round

element->GetGState()->SetLineJoin(GState::e_round_join);