我正在使用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;
}
}
答案 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();
}
由于目前已被注释掉,因此未使用,但此问题尚未显示。