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);