根据文件id获取文件流,通过动态水印配置,添加动态水印
/**
* 水印下载业务操作实现
*
* @Author yong.li_1
**/
@Service
@Slf4j
public class DownloadExistWatermarkBusinessServiceImpl implements DownloadExistWatermarkBusinessService {
@Autowired
private FileFeignService fileFeignService;
@Autowired
private PimWatermarkBusinessService pimWatermarkBusinessService;
/**
* 替换为支持中文字符的字体文件路径
*/
@Value("${font.fontPath:/usr/share/fonts/windows/simkai.ttf}")
private String fontPath;
/**
* 处理文件水印
*
* @param request 请求信息
* @param response 响应信息
* @param fileInfo 文件信息
* @param waterMark 水印信息
*/
@Override
public void dealWithWatermark(HttpServletRequest request, HttpServletResponse response, Map<String, Object> fileInfo, PimWatermarkModel waterMark) {
Response stream = getStream(fileInfo);
log.info("替换为支持中文字符的字体文件路径:{}", fontPath);
try (ServletOutputStream sos = response.getOutputStream(); InputStream is = stream.body().asInputStream()) {
PDDocument document = PDDocument.load(is);
for (PDPage page : document.getPages()) {
PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true, true);
contentStream.setFont(PDType0Font.load(document, new File(fontPath)), Float.parseFloat(waterMark.getFontSize()));
this.setOpacity(contentStream, waterMark);
this.setColor(contentStream, waterMark);
this.setHorizontalAndVertical(contentStream, waterMark, page, document);
contentStream.close();
}
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.setContentType("application/octet-stream; charset=UTF-8");
String userAgent = request.getHeader("User-Agent");
String fileName = fileInfo.get("fileName").toString();
fileName = fileName.substring(0, fileName.lastIndexOf(".")) + ".pdf";
byte[] bytes = (userAgent.contains("MSIE") || userAgent.contains("Trident")) ? fileName.getBytes() : fileName.getBytes(StandardCharsets.UTF_8);
fileName = new String(bytes, StandardCharsets.ISO_8859_1);
response.addHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", fileName));
document.save(sos);
document.close();
sos.flush();
} catch (IOException e) {
log.error("水印添加失败:{}", e.getMessage());
DocToolUtil.responseResult(response, false, "水印添加失败:" + e.getMessage());
}
}
/**
* 获取水印信息
*
* @return 水印信息
*/
@Override
public PimWatermarkModel getWaterMark() {
PlatformUserModel currentUser = UserManager.getCurrentUser();
PimWatermarkModel watermarkModel = null;
if (currentUser == null) {
return watermarkModel;
}
ObjectQuery query = ObjectQuery.from(PimWatermarkModel.META_ID);
PimWatermarkModel[] models = pimWatermarkBusinessService.query(query);
if (models.length > 0) {
watermarkModel = models[0];
String addName = watermarkModel.getIsAddName();
if ("username".equals(addName)) {
watermarkModel.setAttribute(PimWatermarkModel.TXTVAL, watermarkModel.getTxtVal() + "-" + currentUser.getUsername());
} else if ("nickname".equals(addName)) {
watermarkModel.setAttribute(PimWatermarkModel.TXTVAL, watermarkModel.getTxtVal() + "-" + currentUser.getNickname());
}
return watermarkModel;
}
return watermarkModel;
}
private static float calculateTextWidth(String text, PDType0Font font, float fontSize) throws IOException {
return font.getStringWidth(text) / 1000f * fontSize;
}
private static float calculateTextHeight(PDType0Font font, float fontSize) {
return font.getFontDescriptor().getFontBoundingBox().getHeight() / 1000f * fontSize;
}
/**
* 获取文件流
*
* @param fileInfo 文件信息
* @return 文件流
*/
private Response getStream(Map<String, Object> fileInfo) {
String fileSuffix = String.valueOf(fileInfo.get("fileSuffix"));
Long id = Long.valueOf(fileInfo.get("id").toString());
Response stream;
//调用文件服务获取文件流
if (".pdf".equals(fileSuffix) || "pdf".equals(fileSuffix)) {
stream = fileFeignService.stream(id);
} else {
stream = fileFeignService.preview(id);
}
return stream;
}
/**
* 设置透明度
*
* @param contentStream 页面流
* @param waterMark 水印信息
* @throws IOException 异常信息
*/
private void setOpacity(PDPageContentStream contentStream, PimWatermarkModel waterMark) throws IOException {
float opacity = Float.parseFloat(waterMark.getOpacity());
PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState();
graphicsState.setNonStrokingAlphaConstant(opacity);
contentStream.setGraphicsStateParameters(graphicsState);
}
/**
* 设置填充颜色
*
* @param contentStream 页面流
* @param waterMark 水印信息
* @throws IOException 异常信息
*/
private void setColor(PDPageContentStream contentStream, PimWatermarkModel waterMark) throws IOException {
String fontColor = waterMark.getFontColor();
Color color = hex2Rgb(fontColor);
contentStream.setNonStrokingColor(color);
}
/**
* 设置横纵水印
*
* @param contentStream 页面流
* @param waterMark 水印信息
* @param page 页面信息
* @param document 文档信息
* @throws IOException 异常
*/
private void setHorizontalAndVertical(PDPageContentStream contentStream, PimWatermarkModel waterMark, PDPage page, PDDocument document) throws IOException {
String count = waterMark.getCount();
// 去除方括号 "[" 和 "]"
count = count.replace("[", "").replace("]", "");
// 拆分字符串并转换为整数数组
String[] watermarkCount = count.split(",");
int horizontalWatermarkCount = Integer.parseInt(watermarkCount[0]); // 横向水印个数
int verticalWatermarkCount = Integer.parseInt(watermarkCount[1]); // 纵向水印个数
float pageWidth = page.getMediaBox().getWidth();
float pageHeight = page.getMediaBox().getHeight();
BigDecimal watermarkWidth = BigDecimal.valueOf(calculateTextWidth(waterMark.getTxtVal(), PDType0Font.load(document, new File(fontPath)), Float.parseFloat(waterMark.getFontSize())));
BigDecimal watermarkHeight = BigDecimal.valueOf(calculateTextHeight(PDType0Font.load(document, new File(fontPath)), Float.parseFloat(waterMark.getFontSize())));
BigDecimal horizontalSpacing = calculateSpacing(pageWidth, horizontalWatermarkCount, watermarkWidth);
BigDecimal verticalSpacing = calculateSpacing(pageWidth, verticalWatermarkCount, watermarkHeight);
// 循环写水印
for (int i = 0; i < horizontalWatermarkCount; i++) {
for (int j = 0; j < verticalWatermarkCount; j++) {
BigDecimal x = horizontalSpacing.multiply(BigDecimal.valueOf(i + 1L)).add(watermarkWidth.multiply(BigDecimal.valueOf(i)));
BigDecimal y = verticalSpacing.multiply(BigDecimal.valueOf(j + 1L)).add(watermarkHeight.multiply(BigDecimal.valueOf(j)));
float width = x.add(watermarkWidth).floatValue();
float height = y.add(watermarkHeight).floatValue();
float xfloat = x.floatValue();
if (xfloat < 0 || width > pageWidth || height > pageHeight) {
continue;
}
contentStream.beginText();
// 倾斜角度
double angle = Double.parseDouble(waterMark.getAngle());
AffineTransform transform = AffineTransform.getRotateInstance(Math.toRadians(angle), xfloat, y.floatValue());
contentStream.setTextMatrix(transform);
contentStream.newLineAtOffset(xfloat, y.floatValue());
contentStream.showText(waterMark.getTxtVal()); // 水印文本内容
contentStream.endText();
}
}
}
private static BigDecimal calculateSpacing(float pageNum, int watermarkCount, BigDecimal watermarkSize) {
BigDecimal bdpageNum = BigDecimal.valueOf(pageNum);
BigDecimal bdWatermarkCount = BigDecimal.valueOf(watermarkCount);
BigDecimal one = BigDecimal.ONE;
BigDecimal numerator = bdpageNum.subtract(bdWatermarkCount.multiply(watermarkSize));
BigDecimal denominator = bdWatermarkCount.add(one);
return numerator.divide(denominator, RoundingMode.HALF_UP);
}
/**
* 16进制转RGB
* @param colorStr e.g. "#FFFFFF"
* @return
*/
private Color hex2Rgb(String colorStr) {
return new Color(
Integer.valueOf(colorStr.substring(1, 3), 16),
Integer.valueOf(colorStr.substring(3, 5), 16),
Integer.valueOf(colorStr.substring(5, 7), 16));
}
}