Overview
Repertoire Designer 202x automatically determines where pie-chart labels and their connector lines are displayed. This automatic arrangement is appropriate for most charts, but reports containing closely positioned sections may require individual label adjustments.
Understanding The Connector Layout
The script draws each connector using three orthogonal segments:
Pie boundary ── firstRun ──┐
│
└── endRun ── labelXOffset ── Label
firstRuncontrols the first horizontal segment.- The vertical segment joins the first and final horizontal segments.
endRuncontrols the final horizontal line.labelXOffsetmoves only the literal label, creating a gap without changing the connector length.
Step 1: Access The Chart Script
In Repertoire Designer 202x, select the required pie chart. Launch the Chart Wizard and navigate to the Script tab.
Step 2: Understand The Available Settings
Each pie section can have its own configuration.
| Setting | Description |
|---|---|
side |
Forces the label to the "left" or "right" side. |
anchorDegrees |
Defines where the connector begins on the pie boundary. |
yRatio |
Positions the label vertically as a proportion of the available chart height. |
yOffset |
Moves the connector and label vertically. Negative values move upward; positive values move downward. |
firstRun |
Sets the exact length of the first horizontal connector segment. |
endRun |
Sets the exact length of the final horizontal connector segment. |
labelXOffset |
Moves only the literal label horizontally without changing the line length. |
The angle values follow this approximate orientation:
90°
↑
180° ← Pie chart → 0°
↓
270°
For example, 230.0 selects a point in the lower-left region of the pie.
Step 3: Add The Pie-Chart Script
Add the following script to the Script tab:
var chart = Object.getPrototypeOf(this);
var oldPlot = chart.getPlot();
var JMath = java.lang.Math;
var GeneralPath = java.awt.geom.GeneralPath;
var RectangleAnchor = Packages.org.jfree.ui.RectangleAnchor;
// Default settings used when a section does not define its own value.
var defaults = {
firstRun: 15.0,
endRun: 25.0,
labelXOffset: 0.0,
yOffset: 0.0
};
// Configure individual sections using their exact datasource keys.
var links = {
"Product A": {
yOffset: 20.0,
firstRun: 40.0,
endRun: 70.0,
labelXOffset: 0.0
},
"Product B": {
side: "left",
anchorDegrees: 230.0,
yRatio: 0.88,
yOffset: 20.0,
firstRun: 30.0,
endRun: 70.0,
labelXOffset: 0.0
},
"Product C": {
yOffset: 20.0,
firstRun: 30.0,
endRun: 70.0,
labelXOffset: 0.0
},
"Product D": {
yOffset: -10.0,
firstRun: 40.0,
endRun: 70.0,
labelXOffset: 0.0
},
"Other": {
yOffset: -10.0,
firstRun: 40.0,
endRun: 70.0,
labelXOffset: 0.0
}
};
function setting(key, name) {
var item = links[String(key)];
return item != null && item[name] != null
? item[name]
: defaults[name];
}
function drawLink(plotRef, g2, state, record, rightSide) {
var key = String(record.getKey());
var degrees = setting(key, "anchorDegrees");
var theta = degrees == null
? record.getAngle()
: JMath.toRadians(degrees);
// Determine where the connector begins on the pie boundary.
var linkX = state.getPieCenterX()
+ JMath.cos(theta)
* state.getPieWRadius()
* record.getLinkPercent();
var linkY = state.getPieCenterY()
- JMath.sin(theta)
* state.getPieHRadius()
* record.getLinkPercent();
// Determine the vertical position of the connector and label.
var ratio = setting(key, "yRatio");
var targetY = ratio == null
? record.getAllocatedY()
: state.getLinkArea().getMinY()
+ state.getLinkArea().getHeight() * ratio;
targetY += Number(setting(key, "yOffset"));
var labelOffset = Number(setting(key, "labelXOffset"));
var firstRun = Number(setting(key, "firstRun"));
var endRun = Number(setting(key, "endRun"));
// Calculate the connector and label positions independently.
var direction = rightSide ? 1.0 : -1.0;
var elbowX = linkX + direction * firstRun;
var lineEndX = elbowX + direction * endRun;
var labelX = lineEndX + direction * labelOffset;
if (plotRef.getLabelLinksVisible()) {
var oldPaint = g2.getPaint();
var oldStroke = g2.getStroke();
var path = new GeneralPath();
g2.setPaint(plotRef.getLabelLinkPaint());
g2.setStroke(plotRef.getLabelLinkStroke());
path.moveTo(linkX, linkY);
path.lineTo(elbowX, linkY);
path.lineTo(elbowX, targetY);
path.lineTo(lineEndX, targetY);
g2.draw(path);
g2.setPaint(oldPaint);
g2.setStroke(oldStroke);
}
record.getLabel().draw(
g2,
labelX,
targetY,
rightSide
? RectangleAnchor.LEFT
: RectangleAnchor.RIGHT
);
}
var newPlot = new JavaAdapter(
Packages.org.jfree.chart.plot.PiePlot,
{
// Start each connector at the pie-section boundary.
getLabelLinkDepth: function () {
return 0.0;
},
drawLeftLabel: function (g2, state, record) {
drawLink(
this,
g2,
state,
record,
setting(record.getKey(), "side") == "right"
);
},
drawRightLabel: function (g2, state, record) {
drawLink(
this,
g2,
state,
record,
setting(record.getKey(), "side") != "left"
);
}
},
oldPlot.getDataset()
);
// Retain essential settings configured through the Chart Wizard.
newPlot.setStartAngle(oldPlot.getStartAngle());
newPlot.setDirection(oldPlot.getDirection());
newPlot.setInteriorGap(oldPlot.getInteriorGap());
newPlot.setLabelGenerator(
oldPlot.getLabelGenerator()
|| new Packages.org.jfree.chart.labels.StandardPieSectionLabelGenerator(
"{0}: {1} ({2})"
)
);
newPlot.setLabelFont(oldPlot.getLabelFont());
newPlot.setLabelPaint(oldPlot.getLabelPaint());
newPlot.setLabelBackgroundPaint(
oldPlot.getLabelBackgroundPaint()
);
newPlot.setLabelOutlinePaint(
oldPlot.getLabelOutlinePaint()
);
newPlot.setLabelLinkPaint(oldPlot.getLabelLinkPaint());
newPlot.setLabelLinkStroke(oldPlot.getLabelLinkStroke());
newPlot.setLabelLinksVisible(true);
newPlot.setSimpleLabels(false);
// Preserve the existing section colours.
var keys = oldPlot.getDataset().getKeys();
for (var i = 0; i < keys.size(); i++) {
var key = keys.get(i);
var paint = oldPlot.getSectionPaint(key);
if (paint != null) {
newPlot.setSectionPaint(key, paint);
}
}
// Replace the existing PiePlot.
oldPlot.removeChangeListener(chart);
newPlot.addChangeListener(chart);
var field = chart.getClass().getDeclaredField("plot");
field.setAccessible(true);
field.set(chart, newPlot);
Step 4: Configure Individual Sections
Force A Label To One Side
Use side to override automatic label allocation:
"Product B": {
side: "left"
}
Supported values are:
side: "left"
side: "right"
If side is omitted, the label remains on the side selected automatically by the chart.
Change The Connector Starting Point
Use anchorDegrees to select where the connector begins:
"Product B": {
side: "left",
anchorDegrees: 230.0
}
This changes the connector origin without rotating the chart or changing the positions of the pie sections.
Adjust The Vertical Position
Use yOffset for a relative adjustment:
"Product C": {
yOffset: 20.0
}
- Positive values move the label downward.
- Negative values move the label upward.
0.0retains the calculated position.
Use yRatio when a more predictable position is required:
"Product B": {
yRatio: 0.88
}
Typical values are between 0.0 and 1.0:
0.0represents the top of the available label area.0.5represents the centre.1.0represents the bottom.
The yOffset value is applied after yRatio.
Set The Connector Segment Lengths
Configure the two horizontal segments independently:
"Product A": {
firstRun: 40.0,
endRun: 70.0
}
Increasing firstRun lengthens the segment beginning at the pie boundary. Increasing endRun lengthens the final segment leading toward the label.
No automatic clamping is applied, so large values may place labels outside the chart area.
Move Only The Literal Label
Use labelXOffset to create a gap between the connector endpoint and the label:
"Product A": {
firstRun: 40.0,
endRun: 70.0,
labelXOffset: 15.0
}
The connector length remains unchanged. Only the label moves horizontally.
Use:
labelXOffset: 0.0
to position the label directly at the connector endpoint.
Step 5: Use Default Values
The defaults object provides fallback values:
var defaults = {
firstRun: 15.0,
endRun: 25.0,
labelXOffset: 0.0,
yOffset: 0.0
};
A section only needs to define properties that differ from these defaults:
"Product D": {
yOffset: -10.0
}
In this example, Product D uses firstRun, endRun, and labelXOffset from defaults.
Important Considerations
- The script disables simple labels and uses outside labels with connector lines.
- Individual positioning overrides the normal label presentation, values may require adjustment when the chart dimensions change.
- Large
firstRun,endRun,labelXOffset, oryOffsetvalues may move content beyond the available chart area. - Labels forced to another side are positioned independently of the standard automatic distribution. Review them for possible overlaps.
What This Guide Achieves
- Provides independent positioning for each pie-section label.
- Controls both horizontal segments of every orthogonal connector.
- Moves literal labels independently from their connector lines.
- Prevents connector lines from beginning inside the pie.
- Preserves the datasource, section colours, and essential label formatting.
