|
|
|
@@ -3,6 +3,8 @@ package com.baiyue.print.renderer;
|
|
|
|
|
import com.baiyue.print.dto.DispatchBolPdfDtos;
|
|
|
|
|
import java.awt.Color;
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.function.Function;
|
|
|
|
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
|
|
|
|
import org.apache.pdfbox.pdmodel.PDPage;
|
|
|
|
|
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
|
|
|
@@ -12,62 +14,125 @@ import org.springframework.stereotype.Component;
|
|
|
|
|
|
|
|
|
|
@Component
|
|
|
|
|
public class DispatchBolRenderer {
|
|
|
|
|
private static final float[] COLS = {8, 31, 58, 84, 119, 160, 195, 202};
|
|
|
|
|
private static final String[] HEADERS = {"Container #", "Pallet #", "FBA Warehouse #", "Warehouse No #", "FBA Shipment ID", "PO / Reference #", "CTNS"};
|
|
|
|
|
private static final float[] COLS = {8, 23, 39, 62, 89, 123, 160, 190, 202};
|
|
|
|
|
private static final String[][] HEADERS = {
|
|
|
|
|
{""}, {"Container", "#"}, {"Pallet", "#"}, {"FBA", "Warehouse #"},
|
|
|
|
|
{"Warehouse No #"}, {"FBA Shipment ID"}, {"PO / Reference #"}, {"CTNS"}
|
|
|
|
|
};
|
|
|
|
|
private static final double FIRST_TABLE_TOP = 82;
|
|
|
|
|
private static final double CONTINUATION_TABLE_TOP = 10;
|
|
|
|
|
private static final double HEADER_HEIGHT = 10;
|
|
|
|
|
private static final double ROW_HEIGHT = 7.2;
|
|
|
|
|
|
|
|
|
|
public void renderPage(PDDocument document, PDFont font, DispatchBolPdfDtos.Document data) throws IOException {
|
|
|
|
|
PDPage page = new PDPage(PDRectangle.A4);
|
|
|
|
|
document.addPage(page);
|
|
|
|
|
PdfUnits units = new PdfUnits(297);
|
|
|
|
|
try (PDPageContentStream stream = new PDPageContentStream(document, page)) {
|
|
|
|
|
stream.setLineWidth(PdfUnits.mmToPt(.35));
|
|
|
|
|
centered(stream, units, font, "BILL OF LADING", 17, 105, 13);
|
|
|
|
|
block(stream, units, font, data, 18, 202, 18, 82);
|
|
|
|
|
table(stream, units, font, data, 92);
|
|
|
|
|
line(stream, units, 12, 244, 198, 244);
|
|
|
|
|
text(stream, units, font, "总箱数 / Total CTNS:", 8, 12, 241);
|
|
|
|
|
right(stream, units, font, Integer.toString(data.totalPackages()), 12, 198, 241);
|
|
|
|
|
if (data.pageCount() > 1) right(stream, units, font, data.pageNumber() + " / " + data.pageCount(), 7, 198, 251);
|
|
|
|
|
signature(stream, units, font, 24, "Driver signature");
|
|
|
|
|
signature(stream, units, font, 93, "Warehouse signature");
|
|
|
|
|
signature(stream, units, font, 160, "Date");
|
|
|
|
|
stream.setLineWidth(PdfUnits.mmToPt(.3));
|
|
|
|
|
double tableTop;
|
|
|
|
|
if (data.pageNumber() == 1) {
|
|
|
|
|
title(stream, units, font);
|
|
|
|
|
block(stream, units, font, data, 8, 202, 21, FIRST_TABLE_TOP);
|
|
|
|
|
tableTop = FIRST_TABLE_TOP;
|
|
|
|
|
} else {
|
|
|
|
|
tableTop = CONTINUATION_TABLE_TOP;
|
|
|
|
|
}
|
|
|
|
|
double tableBottom = table(stream, units, font, data, tableTop);
|
|
|
|
|
if (data.pageNumber() == data.pageCount()) {
|
|
|
|
|
summaryAndSignatures(stream, units, font, data, tableBottom);
|
|
|
|
|
}
|
|
|
|
|
if (data.pageCount() > 1) {
|
|
|
|
|
right(stream, units, font, data.pageNumber() + " / " + data.pageCount(), 6.5f, 202, 290);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void title(PDPageContentStream s, PdfUnits u, PDFont f) throws IOException {
|
|
|
|
|
fill(s, u, Color.BLACK, 8, 8, 202, 21);
|
|
|
|
|
s.setNonStrokingColor(Color.WHITE);
|
|
|
|
|
centered(s, u, f, "BILL OF LADING", 15, 105, 17);
|
|
|
|
|
s.setNonStrokingColor(Color.BLACK);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void block(PDPageContentStream s, PdfUnits u, PDFont f, DispatchBolPdfDtos.Document d,
|
|
|
|
|
double left, double right, double top, double bottom) throws IOException {
|
|
|
|
|
line(s,u,left,top,right,top); line(s,u,left,bottom,right,bottom); line(s,u,left,top,left,bottom); line(s,u,right,top,right,bottom);
|
|
|
|
|
line(s,u,105,top,105,bottom); line(s,u,left,51,right,51);
|
|
|
|
|
blackHeader(s,u,f,"SHIP FROM",left,top,105,24); blackHeader(s,u,f,"CARRIER",105,top,right,24);
|
|
|
|
|
blackHeader(s,u,f,"SHIP TO",left,51,105,57); blackHeader(s,u,f,"REFERENCE",105,51,right,57);
|
|
|
|
|
text(s,u,f,d.shipFromName(),10,left+2,30); text(s,u,f,"Address: " + d.shipFromAddress(),8,left+2,37);
|
|
|
|
|
text(s,u,f,"Carrier Name: " + d.carrierName(),8,107,29); text(s,u,f,"ISA #: " + d.isaNumber(),8,107,34);
|
|
|
|
|
text(s,u,f,"Appointment time: " + d.appointmentTime(),8,107,39); text(s,u,f,"Trailer #: " + d.trailerNumber(),8,107,44);
|
|
|
|
|
text(s,u,f,"Seal #: " + d.sealNumber(),8,107,49);
|
|
|
|
|
text(s,u,f,"Amazon FBA Warehouse",8,left+2,63); text(s,u,f,"FC Code: " + d.fbaWarehouseCode(),8,left+2,69);
|
|
|
|
|
text(s,u,f,"Name: " + d.shipToName(),8,left+2,75); text(s,u,f,"Address: " + d.shipToAddress(),8,left+2,80);
|
|
|
|
|
text(s,u,f,"BOL / Dispatch #: " + d.dispatchNo(),8,107,63); text(s,u,f,"FBA Shipment ID: " + d.fbaShipmentIds(),8,107,70);
|
|
|
|
|
text(s,u,f,"PO / Reference #: " + d.poReferenceNos(),8,107,77);
|
|
|
|
|
line(s,u,105,top,105,bottom); line(s,u,left,50,right,50);
|
|
|
|
|
blackHeader(s,u,f,"SHIP FROM",left,top,105,27); blackHeader(s,u,f,"CARRIER",105,top,right,27);
|
|
|
|
|
blackHeader(s,u,f,"SHIP TO",left,50,105,56); blackHeader(s,u,f,"REFERENCE",105,50,right,56);
|
|
|
|
|
text(s,u,f,d.shipFromName(),9.5f,left+2,33); text(s,u,f,"Address: " + d.shipFromAddress(),7.5f,left+2,40);
|
|
|
|
|
text(s,u,f,"Carrier Name: " + d.carrierName(),7.5f,107,32); text(s,u,f,"ISA #: " + d.isaNumber(),7.5f,107,36.5);
|
|
|
|
|
text(s,u,f,"Appointment time: " + d.appointmentTime(),7.5f,107,41); text(s,u,f,"Trailer #: " + d.trailerNumber(),7.5f,107,45.5);
|
|
|
|
|
text(s,u,f,"Seal #: " + d.sealNumber(),7.5f,107,49);
|
|
|
|
|
text(s,u,f,"Amazon FBA Warehouse",7.5f,left+2,61); text(s,u,f,"FC Code: " + d.fbaWarehouseCode(),7.5f,left+2,66);
|
|
|
|
|
text(s,u,f,"Name: " + d.shipToName(),7.5f,left+2,71); text(s,u,f,"Address: " + d.shipToAddress(),7.5f,left+2,76);
|
|
|
|
|
text(s,u,f,"BOL / Dispatch #: " + d.dispatchNo(),7.5f,107,61); text(s,u,f,"FBA Shipment ID: " + d.fbaShipmentIds(),7.5f,107,68);
|
|
|
|
|
text(s,u,f,"PO / Reference #: " + d.poReferenceNos(),7.5f,107,75);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void table(PDPageContentStream s, PdfUnits u, PDFont f, DispatchBolPdfDtos.Document d, double top) throws IOException {
|
|
|
|
|
double headerBottom=101, rowHeight=9, bottom=headerBottom+12*rowHeight;
|
|
|
|
|
for(float x:COLS) line(s,u,x,top,x,bottom);
|
|
|
|
|
line(s,u,COLS[0],top,COLS[COLS.length-1],top); line(s,u,COLS[0],headerBottom,COLS[COLS.length-1],headerBottom);
|
|
|
|
|
for(int i=1;i<=12;i++) line(s,u,COLS[0],headerBottom+i*rowHeight,COLS[COLS.length-1],headerBottom+i*rowHeight);
|
|
|
|
|
for(int i=0;i<HEADERS.length;i++) centeredCell(s,u,f,HEADERS[i],5.3f,COLS[i],COLS[i+1],97.5);
|
|
|
|
|
for(int r=0;r<d.cargoRows().size();r++){
|
|
|
|
|
DispatchBolPdfDtos.CargoRow row=d.cargoRows().get(r); String[] values={row.containerNo(),row.palletNo(),row.fbaWarehouseCode(),row.warehouseNo(),row.fbaShipmentId(),row.poReferenceNo(),Integer.toString(row.packageCount())};
|
|
|
|
|
for(int c=0;c<values.length;c++) centeredCell(s,u,f,values[c],5.5f,COLS[c]+.7,COLS[c+1]-.7,107+r*rowHeight);
|
|
|
|
|
private double table(PDPageContentStream s, PdfUnits u, PDFont f, DispatchBolPdfDtos.Document d, double top) throws IOException {
|
|
|
|
|
List<DispatchBolPdfDtos.CargoRow> rows = d.cargoRows();
|
|
|
|
|
double headerBottom = top + HEADER_HEIGHT;
|
|
|
|
|
double bottom = headerBottom + rows.size() * ROW_HEIGHT;
|
|
|
|
|
for (float x : COLS) line(s,u,x,top,x,bottom);
|
|
|
|
|
line(s,u,COLS[0],top,COLS[COLS.length-1],top);
|
|
|
|
|
line(s,u,COLS[0],headerBottom,COLS[COLS.length-1],headerBottom);
|
|
|
|
|
for (int i = 0; i < HEADERS.length; i++) centeredLines(s,u,f,HEADERS[i],5.2f,COLS[i],COLS[i+1],top+4.2);
|
|
|
|
|
|
|
|
|
|
for (int boundary = 1; boundary <= rows.size(); boundary++) {
|
|
|
|
|
double y = headerBottom + boundary * ROW_HEIGHT;
|
|
|
|
|
line(s,u,COLS[0],y,COLS[1],y);
|
|
|
|
|
boolean last = boundary == rows.size();
|
|
|
|
|
if (last || !same(rows.get(boundary - 1).containerNo(), rows.get(boundary).containerNo())) line(s,u,COLS[1],y,COLS[2],y);
|
|
|
|
|
if (last || !same(rows.get(boundary - 1).palletNo(), rows.get(boundary).palletNo())) line(s,u,COLS[2],y,COLS[3],y);
|
|
|
|
|
line(s,u,COLS[3],y,COLS[COLS.length-1],y);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
drawMergedColumn(s,u,f,rows,DispatchBolPdfDtos.CargoRow::containerNo,1,headerBottom);
|
|
|
|
|
drawMergedColumn(s,u,f,rows,DispatchBolPdfDtos.CargoRow::palletNo,2,headerBottom);
|
|
|
|
|
for (int rowIndex = 0; rowIndex < rows.size(); rowIndex++) {
|
|
|
|
|
DispatchBolPdfDtos.CargoRow row = rows.get(rowIndex);
|
|
|
|
|
String[] values = {row.fbaWarehouseCode(), row.warehouseNo(), row.fbaShipmentId(), row.poReferenceNo(), Integer.toString(row.packageCount())};
|
|
|
|
|
double baseline = headerBottom + rowIndex * ROW_HEIGHT + 4.7;
|
|
|
|
|
for (int column = 0; column < values.length; column++) {
|
|
|
|
|
centeredCell(s,u,f,values[column],5.25f,COLS[column+3]+.5,COLS[column+4]-.5,baseline);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return bottom;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void drawMergedColumn(PDPageContentStream s, PdfUnits u, PDFont f, List<DispatchBolPdfDtos.CargoRow> rows,
|
|
|
|
|
Function<DispatchBolPdfDtos.CargoRow, String> value, int column, double headerBottom) throws IOException {
|
|
|
|
|
int start = 0;
|
|
|
|
|
while (start < rows.size()) {
|
|
|
|
|
int end = start + 1;
|
|
|
|
|
while (end < rows.size() && same(value.apply(rows.get(start)), value.apply(rows.get(end)))) end++;
|
|
|
|
|
double baseline = headerBottom + ((start + end) * ROW_HEIGHT / 2) + 1.8;
|
|
|
|
|
centeredCell(s,u,f,value.apply(rows.get(start)),5.25f,COLS[column]+.5,COLS[column+1]-.5,baseline);
|
|
|
|
|
start = end;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void blackHeader(PDPageContentStream s,PdfUnits u,PDFont f,String label,double x1,double y1,double x2,double y2)throws IOException{
|
|
|
|
|
s.setNonStrokingColor(Color.BLACK); s.addRect(PdfUnits.mmToPt(x1),u.topDownMmToPdfY(y2),PdfUnits.mmToPt(x2-x1),PdfUnits.mmToPt(y2-y1)); s.fill();
|
|
|
|
|
s.setNonStrokingColor(Color.WHITE); text(s,u,f,label,6.5f,x1+2,y2-1.7); s.setNonStrokingColor(Color.BLACK);
|
|
|
|
|
private void summaryAndSignatures(PDPageContentStream s, PdfUnits u, PDFont f, DispatchBolPdfDtos.Document d, double tableBottom) throws IOException {
|
|
|
|
|
double summaryY = Math.max(tableBottom + 14, 236);
|
|
|
|
|
line(s,u,12,summaryY+4,198,summaryY+4);
|
|
|
|
|
text(s,u,f,"总箱数 / Total CTNS:",8,12,summaryY);
|
|
|
|
|
right(s,u,f,Integer.toString(d.totalPackages()),12,198,summaryY);
|
|
|
|
|
double signatureY = Math.max(summaryY + 30, 266);
|
|
|
|
|
signature(s,u,f,24,signatureY,"Driver signature");
|
|
|
|
|
signature(s,u,f,93,signatureY,"Warehouse signature");
|
|
|
|
|
signature(s,u,f,160,signatureY,"Date");
|
|
|
|
|
}
|
|
|
|
|
private void signature(PDPageContentStream s,PdfUnits u,PDFont f,double x,String label)throws IOException{line(s,u,x,266,x+52,266); centeredCell(s,u,f,label,6.5f,x,x+52,274);}
|
|
|
|
|
|
|
|
|
|
private boolean same(String left, String right) { return left != null && left.equals(right); }
|
|
|
|
|
private void blackHeader(PDPageContentStream s,PdfUnits u,PDFont f,String label,double x1,double y1,double x2,double y2)throws IOException{
|
|
|
|
|
fill(s,u,Color.BLACK,x1,y1,x2,y2); s.setNonStrokingColor(Color.WHITE); text(s,u,f,label,6.2f,x1+2,y2-1.7); s.setNonStrokingColor(Color.BLACK);
|
|
|
|
|
}
|
|
|
|
|
private void fill(PDPageContentStream s,PdfUnits u,Color color,double x1,double y1,double x2,double y2)throws IOException{s.setNonStrokingColor(color);s.addRect(PdfUnits.mmToPt(x1),u.topDownMmToPdfY(y2),PdfUnits.mmToPt(x2-x1),PdfUnits.mmToPt(y2-y1));s.fill();}
|
|
|
|
|
private void signature(PDPageContentStream s,PdfUnits u,PDFont f,double x,double y,String label)throws IOException{line(s,u,x,y,x+52,y);centeredCell(s,u,f,label,6.2f,x,x+52,y+8);}
|
|
|
|
|
private void centered(PDPageContentStream s,PdfUnits u,PDFont f,String value,float size,double centerX,double y)throws IOException{float w=f.getStringWidth(value)/1000*size;text(s,u,f,value,size,centerX-w/PdfUnits.mmToPt(1)/2,y);}
|
|
|
|
|
private void centeredLines(PDPageContentStream s,PdfUnits u,PDFont f,String[] values,float size,double x1,double x2,double y)throws IOException{for(int i=0;i<values.length;i++)centeredCell(s,u,f,values[i],size,x1+.3,x2-.3,y+i*3.5);}
|
|
|
|
|
private void centeredCell(PDPageContentStream s,PdfUnits u,PDFont f,String value,float size,double x1,double x2,double y)throws IOException{String fit=fit(f,value,size,PdfUnits.mmToPt(x2-x1));float w=f.getStringWidth(fit)/1000*size;text(s,u,f,fit,size,x1+(x2-x1-w/PdfUnits.mmToPt(1))/2,y);}
|
|
|
|
|
private void right(PDPageContentStream s,PdfUnits u,PDFont f,String value,float size,double x,double y)throws IOException{float w=f.getStringWidth(value)/1000*size;text(s,u,f,value,size,x-w/PdfUnits.mmToPt(1),y);}
|
|
|
|
|
private String fit(PDFont f,String v,float size,float max)throws IOException{String value=v==null?"—":v;if(f.getStringWidth(value)/1000*size<=max)return value;String ell="…";int end=value.length();while(end>0&&f.getStringWidth(value.substring(0,end)+ell)/1000*size>max)end--;return end==0?ell:value.substring(0,end)+ell;}
|
|
|
|
|