簡體   English   中英

如何使用pdfbox繪制餅圖?

[英]How to draw a pie chart using pdfbox?

我必須使用pdfbox繪制餅圖。

讓數據為:

科目分數百分比分數分數累積學位
子 1 80 80 80
子 2 70 70 150
子 3 65 65 215
子 4 90 90 305
低於 5 55 55 360

讓半徑和中心為 100 像素和 (250, 400)。

讓我們取平行於 x 軸的初始線。
繪制初始行語句將是:
contentStream.drawLine(250, 400, 350, 400);

我堅持:
a) 找到圓上與初始線相距一定度數的點的 x, y 坐標以繪制半徑
b) 使用貝塞爾曲線在兩點之間繪制圓弧。

對解決問題的任何幫助將不勝感激!

根據角度找到圓上的 x、y 坐標是學校數學,即 sin() 和 cos(),棘手的部分是用貝塞爾曲線繪制圓弧。

這是一些繪制您要求的餅圖的代碼。 請注意, createSmallArc()只能處理最大為 90° 的角度。 如果您想要更多,則必須通過繪制幾條弧線來修改代碼,直到返回 (0,0),或者只繪制幾片。

createSmallArc()Hans Muller 提供,許可: Creative Commons Attribution 3.0 。更改:將原始 AS 代碼實現為 java。算法由Aleksas Riškus 提供

public class PieChart
{
    public static void main(String[] args) throws IOException
    {
        PDDocument doc = new PDDocument();
        PDPage page = new PDPage();
        doc.addPage(page);
        PDPageContentStream cs = new PDPageContentStream(doc, page);
        
        cs.transform(Matrix.getTranslateInstance(250, 400));
        
        cs.setNonStrokingColor(Color.yellow);
        drawSlice(cs, 100, 0, 80);
        cs.fill();
        cs.setNonStrokingColor(Color.red);
        drawSlice(cs, 100, 80, 150);
        cs.fill();
        cs.setNonStrokingColor(Color.green);
        drawSlice(cs, 100, 150, 215);
        cs.fill();
        cs.setNonStrokingColor(Color.blue);
        drawSlice(cs, 100, 215, 305);
        cs.fill();
        cs.setNonStrokingColor(Color.ORANGE);
        drawSlice(cs, 100, 305, 360);
        cs.fill();
        
        cs.close();
        doc.save("piechart.pdf");
        doc.close();
    }

    private static void drawSlice(PDPageContentStream cs, float rad, float startDeg, float endDeg) throws IOException
    {
        cs.moveTo(0, 0);
        List<Float> smallArc = createSmallArc(rad, Math.toRadians(startDeg), Math.toRadians(endDeg));
        cs.lineTo(smallArc.get(0), smallArc.get(1));
        cs.curveTo(smallArc.get(2), smallArc.get(3), smallArc.get(4), smallArc.get(5), smallArc.get(6), smallArc.get(7));
        cs.closePath();
    }
    
    /**
     *  From https://hansmuller-flex.blogspot.com/2011/10/more-about-approximating-circular-arcs.html
     * 
     *  Cubic bezier approximation of a circular arc centered at the origin, 
     *  from (radians) a1 to a2, where a2-a1 &lt; pi/2.  The arc's radius is r.
     * 
     *  Returns a list with 4 points, where x1,y1 and x4,y4 are the arc's end points
     *  and x2,y2 and x3,y3 are the cubic bezier's control points.
     * 
     *  This algorithm is based on the approach described in:
     *  Aleksas Riškus, "Approximation of a Cubic Bezier Curve by Circular Arcs and Vice Versa," 
     *  Information Technology and Control, 35(4), 2006 pp. 371-378.
     */
    private static List<Float> createSmallArc(double r, double a1, double a2)
    {
        // Compute all four points for an arc that subtends the same total angle
        // but is centered on the X-axis
        double a = (a2 - a1) / 2;
        double x4 = r * Math.cos(a);
        double y4 = r * Math.sin(a);
        double x1 = x4;
        double y1 = -y4;
        double q1 = x1*x1 + y1*y1;
        
        double q2 = q1 + x1*x4 + y1*y4;
        double k2 = 4/3d * (Math.sqrt(2 * q1 * q2) - q2) / (x1 * y4 - y1 * x4);
        double x2 = x1 - k2 * y1;
        double y2 = y1 + k2 * x1;
        double x3 = x2; 
        double y3 = -y2;
        
        // Find the arc points' actual locations by computing x1,y1 and x4,y4 
        // and rotating the control points by a + a1
        
        double ar = a + a1;
        double cos_ar = Math.cos(ar);
        double sin_ar = Math.sin(ar);
        
        List<Float> list = new ArrayList<Float>();
        list.add((float) (r * Math.cos(a1)));
        list.add((float) (r * Math.sin(a1))); 
        list.add((float) (x2 * cos_ar - y2 * sin_ar)); 
        list.add((float) (x2 * sin_ar + y2 * cos_ar)); 
        list.add((float) (x3 * cos_ar - y3 * sin_ar)); 
        list.add((float) (x3 * sin_ar + y3 * cos_ar)); 
        list.add((float) (r * Math.cos(a2))); 
        list.add((float) (r * Math.sin(a2)));
        return list;
    }
}

如果切片之間出現白線並且不希望出現白線,請參閱此答案

如果您只想使用 PDFBox 將一些圖表繪制到 PDF 中,並且不想自己進行所有數學運算等,則可以使用我的PDFBox Graphics2D 適配器 這允許您使用任何基於 Graphics2D 的 Java 庫來繪制圖表(例如 JFreeChart)。 它將創建一個 XForm,然后您可以將其自由放置在您的 PDF 中。

如果你真的想自己做這件事,你仍然可以使用 Java2D API 來幫助處理形狀。

Arc2D.Float arc = new Arc2D.Float(x,y,w,h,start,extend, Arch2D.OPEN);
AffineTransform tf = new AffineTransform();
// You may need to setup tf to correctly position the drawing.
float[] coords = new float[6];
PathIterator pi = arc.getPathIterator(tf);
while (!pi.isDone()) {
    int segment = pi.currentSegment(coords);
    switch (segment) {
    case PathIterator.SEG_MOVETO:
        if (isFinite(coords, 2))
            contentStream.moveTo(coords[0], coords[1]);
        break;
    case PathIterator.SEG_LINETO:
        if (isFinite(coords, 2))
            contentStream.lineTo(coords[0], coords[1]);
        break;
    case PathIterator.SEG_QUADTO:
        if (isFinite(coords, 4))
            contentStream.curveTo1(coords[0], coords[1], coords[2], coors[3]);
            break;
    case PathIterator.SEG_CUBICTO:
        if (isFinite(coords, 6))
            contentStream.curveTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
        break;
    case PathIterator.SEG_CLOSE:
        contentStream.closePath();
        break;
    }
    pi.next();
}
contentStream.fill();

另請參閱我的圖形適配器形狀行走代碼

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM