使用lineTo和curveTo

时间:2017-07-28 17:30:12

标签: java pdfbox

我正在使用Apache的PDFBox版本2.0.4,并且在使用lineTo和curveTo时出现问题。我的函数接受弧度参数,起始度和结束度,然后使用lineTo和curveTo生成饼图切片。

mContents.setNonStrokingColor(color);
mContents.moveTo(0, 0);
List<Float> smallArc = createSmallArc(rad, Math.toRadians(startDeg), Math.toRadians(endDeg));
mContents.lineTo(smallArc.get(0), smallArc.get(1));
mContents.curveTo(smallArc.get(2), smallArc.get(3), smallArc.get(4), smallArc.get(5), smallArc.get(6), smallArc.get(7));
mContents.closePath();
mContents.fill();

饼图生成并且似乎没问题。我的应用程序添加了一个页脚,其中包含一个从文件中读取的徽标,如下所示:

try {
    pdImage = PDImageXObject.createFromFile(mFullImagePath, mDoc);
}catch(IOException ie){System.out.println("Error opening image file - "+ie.getMessage());}
try {
    mContents.drawImage(pdImage,250,5,pdImage.getWidth()/2,pdImage.getHeight()/2);
}catch(IOException e){System.out.println("Error adding image file - "+ e.getMessage());}

当生成的pdf中包含pi图表时,页脚和图像不在pdf中。存储代码以生成饼图,页脚显示包含图像。

目前必须在生成页面后添加指定特定坐标的饼图,否则饼图下方的其他行不会出现。

curveTo和lineTo生成的输出是否会大于导致这些问题的显示输出?

编辑 - 在绘制图形之前在页脚中添加图像,图像,图形和文本都会出现。

感谢任何指针

完整代码:

import com.google.code.geocoder.Geocoder;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.util.Matrix;
import org.apache.tomcat.jni.Address;
import org.slf4j.Logger;

import java.awt.*;
import java.io.IOException; 
import java.text.DecimalFormat; 
import java.util.ArrayList; 
import java.util.Calendar; 
import java.util.List; 
import java.util.Locale;

/**  * Created by tim on 7/6/2017.  */
public class ReportDataPDFBox {
    private PDDocument mDoc = null;
    private PDPage mPage = null;
    private PDImageXObject pdImage = null;
    private PDFont mHeaderFont = PDType1Font.HELVETICA_BOLD;

    private final int FONT_SIZE_HDR1 = 16;
    private final int FONT_SIZE_HDR2 = 14;
    private final int FONT_SIZE_REG = 12;
    private final int HDR_INDENT = 30;
    private final int BODY_INDENT_1 = 55;
    private final int BODY_INDENT_2 = 65;
    private final int BODY_INDENT_3 = 75;

    private PDFont mRegFont = PDType1Font.HELVETICA;
    PDPageContentStream mContents = null;

    private String mReportName = null;
    private String mFullImagePath = null;
    private String mMonth = null;
    private boolean mReportDone = true;
    private int mHorizonVal = 700;
    private int mHorizonGrph = 0;
    private long[] mDayPercent;

    private Calendar mCurrentCalendar = null;
    ProcessFrequencyData pfd = null;
    ProcessWeatherData pwd = null;
    ProcessPerformanceData ppd = null;
    Logger log = null;
    Color[] mColor = {Color.PINK,Color.YELLOW,Color.CYAN, Color.BLUE,Color.RED,Color.GREEN,Color.ORANGE,Color.LIGHT_GRAY};

    public ReportDataPDFBox(Logger logger, ProcessFrequencyData pfd, ProcessWeatherData pwd, ProcessPerformanceData ppd){
        this.log = logger;
        this.pfd = pfd;
        this.pwd = pwd;
        this.ppd = ppd;
        initializeDoc();
    }

    public void initializeDoc(){
        mDoc = new PDDocument();
        mPage = new PDPage();
        mDoc.addPage(mPage);
        mFullImagePath = "logo.png";
        mCurrentCalendar = Calendar.getInstance();
        mMonth = mCurrentCalendar.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault());
        mReportName = mMonth + ".pdf";
        try{
            mContents = new PDPageContentStream(mDoc, mPage);
        }catch(IOException e){System.out.println("Error setting content stream - "+e.getLocalizedMessage());}
    }

    public boolean writeTheReport(){
        addHeader();
        addFooter();

        generateReportContent();

//        addFooter();
        cleanUpALlDone();
        return mReportDone;
    }

    private void addHeader(){
        try {
            mContents.beginText();
            mContents.setFont(mHeaderFont,FONT_SIZE_HDR1);
            mContents.newLineAtOffset(200, 740);
            mContents.showText(mMonth + " - ActoTracker Report - " + mCurrentCalendar.get(Calendar.YEAR));
            mContents.endText();
        }catch (IOException io){System.out.println("Error with text content screen");}
    }

    private void generateReportContent(){
        addNumberRunInfo();
        addLocationRunInfo();
        addWeekDayInfo();
        addWeekInfo();
        addFrequencyData();
        pukeMeAChart();
           // generateDailyChart();
    }

    private void addNumberRunInfo(){
        int daysActive = Utility.getDaysBetweenDates(Utility.getOldestDate(pfd.getFirstDate(),
    pwd.getFirstDate()), Calendar.getInstance().getTimeInMillis());
        writeLine(mHeaderFont, FONT_SIZE_HDR2,HDR_INDENT, "Frequency Information");
        long percentActiveIdle = (pfd.getTotalDaysRun()*100/daysActive);
        String line = "Number of Runs - " + pfd.getTotalDaysRun() + "    Number of days ActoTracker active - " + daysActive + "   Percent run =
    "+percentActiveIdle;
        writeLine(mRegFont, FONT_SIZE_REG, BODY_INDENT_1, line);
    }

    private void addLocationRunInfo(){
        String line = "Number of locations run = " + pfd.getLocationRun();
        writeLine(mRegFont,FONT_SIZE_REG,BODY_INDENT_1,line);
        for (int i=1; i<=pfd.getLocationRun();i++){
            String[] locationInfo = pfd.getLocationInfo(i);
            long percent = pfd.getRunsByLocation(i)*100/pfd.getTotalDaysRun();
            String line2= new String( locationInfo[0] + " - " + locationInfo[1] +" , "+locationInfo[2]+ "  Number of runs = " +
    pfd.getRunsByLocation(i) + "  Percent of runs = " +percent );
            writeLine(mRegFont, FONT_SIZE_REG,BODY_INDENT_2,line2);
        }
    }

    private void addWeekDayInfo(){
        int totDaysRunning = pfd.getTotalRunDay();
        int leastCnt = 0;
        int mostCnt = 0;
        mHorizonGrph = mHorizonVal - 90;
        mDayPercent = new long[8];
        String mostDay = " most common day";
        String leastDay = " least common day";
        DayFrequencyResults frequency = pfd.getDayDistribution();
        int[] leastDays = frequency.getLessDays();
        int[] mostDays = frequency.getMostDays();
        StringBuilder leastString = new StringBuilder();
        StringBuilder mostString = new StringBuilder();
        for (int i=0; i< leastDays.length;i++){
            if (leastDays[i] != 0) {
                leastString.append(Utility.getDayName(leastDays[i])).append(" ");
                leastCnt++;
            }
        }
        for (int j=0; j< mostDays.length;j++){
            if (mostDays[j] != 0) {
                mostString.append(Utility.getDayName(j+1)).append(" ");
                mostCnt++;
            }
        }
        if (leastCnt > 1){leastDay += "s";}
        if (mostCnt > 1) {mostDay +="s";}
        String line = mostString.toString()+mostDay+ " to run"+ "     "+leastString.toString()+leastDay+" to run";
        writeLine(mRegFont,FONT_SIZE_REG,BODY_INDENT_1,line);
        for (int i=1;i<8;i++){
            String day = new String(Utility.getDayName(i)+"  " + pfd.getRweekDayCount(i) + " runs "+" 
    "+pfd.getRweekDayCount(i)*100/totDaysRunning)+ "%";
            writeLine(mRegFont,FONT_SIZE_REG,BODY_INDENT_2,day);
            double x = pfd.getRweekDayCount(i) / (double)pfd.getTotalDaysRun();
            mDayPercent[i] = Math.round(360*x);
        }
        System.out.println("BreakPoint");
    }

    private void addWeekInfo(){
        String line;
        Integer[] largestWeekTotals = {0,0,0,0,0,0,0};
        double largestDistance = 0D;
        double firstHalfDist = 0D;
        double secondHalfDist = 0D;
        DecimalFormat df = new DecimalFormat("####.##");

        int[] distFreq = pfd.getMonthlySummaryInfo();
        if (distFreq[0] > distFreq[1]){
            line = "Ran more in first half of months run.   "+ distFreq[0] + " times versus "+ distFreq[1]+" times";
        }else{
            line = "Ran more in second half of months run.   " + distFreq[1] + " times versus " + distFreq[0]+" times";
        }
        writeLine(mRegFont,FONT_SIZE_REG, BODY_INDENT_1, line);

        for (int i = 1; i<7;i++){
            if (i<4){
                firstHalfDist += Utility.getMileage(pfd.fa.getWeekDistanceTotal(i),false);
            }else{
                secondHalfDist += Utility.getMileage(pfd.fa.getWeekDistanceTotal(i),false);
            }
        }
        if (firstHalfDist > secondHalfDist){
            line = new String ("Ran further in the first half of the month " + df.format(firstHalfDist) + " miles versus " +
    df.format(secondHalfDist) + " miles");
        }else{
            line = new String ("Ran further in the second half of the month " + df.format(secondHalfDist) + " miles versus " +
    df.format(firstHalfDist)+ " miles");
        }
            writeLine(mRegFont,FONT_SIZE_REG,BODY_INDENT_1, line);
        }

        private void addFrequencyData(){

        int greatestFreq = 0;
        int leastDiff = 0;
        int greatestDiff = 0;
        int leastFreq = 0;
        for (int i=0; i<30; i++){
            int cnt = ppd.getRunsByFrequentcy(i);
            if (cnt > greatestFreq){
                greatestFreq = cnt;
                greatestDiff = i;
            }
            else{
                if (cnt > 0 && i>leastDiff){
                    leastDiff = i;
                    leastFreq = cnt;
                }
            }

            log.info("Frequency?? = " + cnt + " index = "+i);
        }
        String line = greatestDiff + " days is the most common frequency between runs "+greatestFreq+" times";
        writeLine(mRegFont,FONT_SIZE_REG,BODY_INDENT_1,line);
        String line2 = leastDiff + " days longest time between runs " + leastFreq + " times";
        writeLine(mRegFont,FONT_SIZE_REG,BODY_INDENT_1,line2);
    }

    private void writeLine(PDFont font, int fontSize, int indent, String text){
        mHorizonVal -= 20;
        try {
            mContents.beginText();
            mContents.setFont(font, fontSize);
            mContents.newLineAtOffset(indent,mHorizonVal);
            mContents.showText(text);
            mContents.endText();
        }catch(IOException e){}
    }

    private void addFooter(){
        log.info("IN addFooter");
        mPage = new PDPage();
        mDoc.addPage(mPage);
        try {
            pdImage = PDImageXObject.createFromFile(mFullImagePath, mDoc);
        }catch(IOException ie){System.out.println("Error opening image file - "+ie.getMessage());}
        try {
            mContents.drawImage(pdImage,250,5,pdImage.getWidth()/2,pdImage.getHeight()/2);
        }catch(IOException e){log.error("Error adding image file - "+ e.getLocalizedMessage());}
    }

    private void cleanUpALlDone(){
        try {
            mContents.close();
            mDoc.save(mReportName);
            mDoc.close();
        }catch (IOException ie){System.out.println("Error closing PDF document - " + ie.getMessage());}
    }

    private void generateDailyChart(){
        int totalVal = 0;
        try {
            mContents.transform(Matrix.getTranslateInstance(375, 525));
        }catch(IOException e){}

        for (int i=1; i< 8;i++){
            totalVal += mDayPercent[i];
            writeTheChart(mDayPercent[i-1], totalVal,mColor[i]);
            log.info("Color selected = " +mColor[i] +"Index = "+i);
        }
    }

    private void writeTheChart(long beg, long end, Color color){
        try {
            log.info("Color received = " + color);
            drawSlice(color, 60,beg, end);
        }catch(IOException e){}
    }

    private void pukeMeAChart(){
        try {
            mContents.transform(Matrix.getTranslateInstance(375,525));
            drawSlice(Color.YELLOW, 60, 0, 69);
            mContents.fill();
            drawSlice(Color.BLUE, 60, 69, 117);
            drawSlice(Color.RED, 60, 117, 181);
            mContents.fill();
            drawSlice(Color.WHITE, 60, 181, 208);
            mContents.fill();
            drawSlice(Color.GREEN, 60, 208, 272);
            mContents.fill();
            drawSlice(Color.YELLOW, 60, 272, 336);
            drawSlice(Color.BLUE, 60, 336, 360);
            mContents.fill();
        } catch(IOException e ){}
    }

    private void drawSlice(Color color, float rad, float startDeg, float endDeg) throws IOException
    {
        mContents.setNonStrokingColor(color);
        mContents.moveTo(0, 0);
        List<Float> smallArc = createSmallArc(rad, Math.toRadians(startDeg), Math.toRadians(endDeg));
        mContents.lineTo(smallArc.get(0), smallArc.get(1));
        mContents.curveTo(smallArc.get(2), smallArc.get(3), smallArc.get(4), smallArc.get(5), smallArc.get(6), smallArc.get(7));
        mContents.closePath();
        mContents.fill();
    }

    private 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;
    }
}

1 个答案:

答案 0 :(得分:1)

与您的假设相反,您 使用lineTo和curveTo 时遇到问题,即您的方法drawSlice。您在使用该方法的代码中遇到问题,即此处:

private void pukeMeAChart(){
    try {
        mContents.transform(Matrix.getTranslateInstance(375,525));
        drawSlice(Color.YELLOW, 60, 0, 69);
        mContents.fill();
        drawSlice(Color.BLUE, 60, 69, 117);
        drawSlice(Color.RED, 60, 117, 181);
        mContents.fill();
        drawSlice(Color.WHITE, 60, 181, 208);
        mContents.fill();
        drawSlice(Color.GREEN, 60, 208, 272);
        mContents.fill();
        drawSlice(Color.YELLOW, 60, 272, 336);
        drawSlice(Color.BLUE, 60, 336, 360);
        mContents.fill();
    } catch(IOException e ){}
}

此方法首先翻译坐标系

mContents.transform(Matrix.getTranslateInstance(375,525));

并且在完成后不会撤消该翻译。因此,pdf 中的页脚和图像 ,只是不是您所期望的那样,而是翻译,可能在裁剪框之外。

要撤消翻译(以及其他更改,例如填充颜色),只需将图形状态存储在pukeMeAChart的开头,并在结束时将其恢复。

此外,drawSlice 填充切片本身,因此无法再填充pukeMeAChart。因此,fill调用无效。

应用所有更改:

private void pukeMeAChart(){
    try {
        mContents.saveGraphicsState();
        mContents.transform(Matrix.getTranslateInstance(375,525));
        drawSlice(Color.YELLOW, 60, 0, 69);
        drawSlice(Color.BLUE, 60, 69, 117);
        drawSlice(Color.RED, 60, 117, 181);
        drawSlice(Color.WHITE, 60, 181, 208);
        drawSlice(Color.GREEN, 60, 208, 272);
        drawSlice(Color.YELLOW, 60, 272, 336);
        drawSlice(Color.BLUE, 60, 336, 360);
        mContents.restoreGraphicsState();
    } catch(IOException e ){}
}

generateDailyChart(),另一种方法(间接)使用drawSlice方法,也有图形状态问题,必须同样修复:

private void generateDailyChart(){
    mContents.saveGraphicsState();
    int totalVal = 0;
    try {
        mContents.transform(Matrix.getTranslateInstance(375, 525));
    }catch(IOException e){}

    for (int i=1; i< 8;i++){
        totalVal += mDayPercent[i];
        writeTheChart(mDayPercent[i-1], totalVal,mColor[i]);
        log.info("Color selected = " +mColor[i] +"Index = "+i);
    }
    mContents.restoreGraphicsState();
}

由于目前已被注释掉,因此未使用,但此问题尚未显示。