package jnpf.fileStorage.dms;
|
|
import com.google.zxing.BinaryBitmap;
|
import com.google.zxing.MultiFormatReader;
|
import com.google.zxing.RGBLuminanceSource;
|
import com.google.zxing.common.HybridBinarizer;
|
import jnpf.dmsEntity.preview.model.DmsWatermarkPositionSnapshot;
|
import jnpf.dmsEntity.preview.model.DmsWatermarkSnapshot;
|
import org.apache.poi.xwpf.usermodel.XWPFDocument;
|
import org.apache.poi.wp.usermodel.HeaderFooterType;
|
import org.junit.Test;
|
|
import javax.imageio.ImageIO;
|
import java.awt.Color;
|
import java.awt.Graphics2D;
|
import java.awt.image.BufferedImage;
|
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayOutputStream;
|
import java.io.InputStream;
|
import java.math.BigDecimal;
|
import java.nio.file.Files;
|
import java.nio.file.Path;
|
import java.util.Collections;
|
import java.util.Arrays;
|
import java.util.Base64;
|
import java.util.List;
|
import java.util.regex.Pattern;
|
import java.util.function.Consumer;
|
|
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertTrue;
|
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.doAnswer;
|
import static org.mockito.Mockito.verify;
|
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.eq;
|
|
public class DmsDocxWatermarkServiceTest {
|
static {
|
System.setProperty("java.awt.headless", "true");
|
}
|
@Test
|
public void addsRepeatedTextWatermarksWithoutChangingBodyText() throws Exception {
|
byte[] source = sourceDocument();
|
|
DmsWatermarkSnapshot watermark = watermark("TILED");
|
byte[] result = new DmsDocxWatermarkService(mock(DmsCommonFileStorage.class))
|
.apply(source, Collections.singletonList(watermark));
|
|
Path sample = Path.of("target", "watermark-preview", "dms-onlyoffice-watermark.docx");
|
Files.createDirectories(sample.getParent());
|
Files.write(sample, result);
|
|
try (XWPFDocument document = new XWPFDocument(new ByteArrayInputStream(result))) {
|
assertEquals("受控文件正文", document.getParagraphArray(0).getText());
|
assertEquals(1, document.getHeaderList().size());
|
String headerXml = document.getHeaderList().get(0)._getHdrFtr().xmlText();
|
assertTrue(headerXml.contains("DMS_WATERMARK_"));
|
assertTrue(headerXml.contains("behindDoc=\"true\""));
|
assertTrue(document.getHeaderList().get(0).getText().contains("原始页眉"));
|
assertTrue(document.getFooterList().get(0).getText().contains("原始页脚"));
|
assertEquals(1, document.getPackage().getPartsByName(Pattern.compile("/word/media/.*")).size());
|
}
|
|
}
|
|
@Test
|
@SuppressWarnings("unchecked")
|
public void loadsImageWatermarkFromFileCommon() throws Exception {
|
DmsCommonFileStorage storage = mock(DmsCommonFileStorage.class);
|
byte[] image = imageBytes();
|
doAnswer(invocation -> {
|
Consumer<InputStream> consumer = invocation.getArgument(2);
|
consumer.accept(new ByteArrayInputStream(image));
|
return null;
|
}).when(storage).download(eq("annex"), eq("company-logo.png"), any());
|
DmsWatermarkSnapshot watermark = watermark("POSITION");
|
watermark.setWatermarkType("IMAGE");
|
watermark.setImageUrl("/api/file/Image/annex/company-logo.png");
|
|
byte[] result = new DmsDocxWatermarkService(storage)
|
.apply(sourceDocument(), Collections.singletonList(watermark));
|
|
try (XWPFDocument document = new XWPFDocument(new ByteArrayInputStream(result))) {
|
assertEquals(1, document.getPackage().getPartsByName(Pattern.compile("/word/media/.*")).size());
|
assertTrue(document.getHeaderList().get(0)._getHdrFtr().xmlText().contains("DMS_WATERMARK_"));
|
}
|
verify(storage).download(eq("annex"), eq("company-logo.png"), any());
|
}
|
|
@Test
|
public void addsQrCodeAsHeaderImage() throws Exception {
|
DmsWatermarkSnapshot watermark = watermark("POSITION");
|
watermark.setWatermarkType("BARCODE");
|
watermark.setBarcodeFormat("QR_CODE");
|
watermark.setContent("玻思韬-TMS需求文档.docx-D(00)");
|
|
byte[] result = new DmsDocxWatermarkService(mock(DmsCommonFileStorage.class))
|
.apply(sourceDocument(), Collections.singletonList(watermark));
|
|
try (XWPFDocument document = new XWPFDocument(new ByteArrayInputStream(result))) {
|
assertEquals(1, document.getPackage().getPartsByName(Pattern.compile("/word/media/.*")).size());
|
document.getHeaderList().forEach(header -> {
|
String headerXml = header._getHdrFtr().xmlText();
|
assertTrue(headerXml.contains("DMS_WATERMARK_"));
|
assertTrue(headerXml.contains("relationships"));
|
});
|
}
|
}
|
|
@Test
|
public void chineseQrCodeRetainsContentAndSmallCodesAreNotCropped() throws Exception {
|
DmsDocxWatermarkService service = new DmsDocxWatermarkService(mock(DmsCommonFileStorage.class));
|
DmsWatermarkSnapshot watermark = watermark("POSITION");
|
watermark.setBarcodeFormat("QR_CODE");
|
watermark.setContent("玻思韬-TMS需求文档.docx-D(00)");
|
assertEquals(watermark.getContent(), decode(service.barcode(watermark, 454, 113)));
|
assertEquals(watermark.getContent(), decode(service.barcode(watermark, 6, 6)));
|
watermark.setBarcodeFormat("CODE_128");
|
watermark.setContent("QC-SOP-01200-D(00)");
|
assertEquals(watermark.getContent(), decode(service.barcode(watermark, 454, 113)));
|
}
|
|
@Test
|
public void displaysBarcodeTextBelowQrCodeWhenEnabled() throws Exception {
|
DmsDocxWatermarkService service = new DmsDocxWatermarkService(mock(DmsCommonFileStorage.class));
|
DmsWatermarkSnapshot watermark = watermark("POSITION");
|
watermark.setWatermarkType("BARCODE");
|
watermark.setBarcodeFormat("QR_CODE");
|
watermark.setContent("QC-SOP-01200");
|
watermark.setShowBarcodeText(true);
|
|
BufferedImage image = service.barcode(watermark, 454, 113);
|
|
assertEquals(watermark.getContent(), decode(image));
|
assertTrue(darkPixels(image, image.getHeight() - 12) > 0);
|
}
|
|
@Test
|
public void preservesPageVariablesAsLiveFieldsInAllHeaderTypes() throws Exception {
|
byte[] source;
|
try (XWPFDocument document = new XWPFDocument(new ByteArrayInputStream(sourceDocument()));
|
ByteArrayOutputStream output = new ByteArrayOutputStream()) {
|
document.createHeader(HeaderFooterType.FIRST).createParagraph().createRun().setText("首页页眉");
|
document.createHeader(HeaderFooterType.EVEN).createParagraph().createRun().setText("偶数页页眉");
|
document.getDocument().getBody().getSectPr().addNewTitlePg();
|
document.setEvenAndOddHeadings(true);
|
document.createParagraph().setPageBreak(true);
|
document.createParagraph().createRun().setText("第二页正文");
|
document.write(output);
|
source = output.toByteArray();
|
}
|
DmsWatermarkSnapshot watermark = watermark("POSITION");
|
watermark.setContent("第 {/CurrPage} 页 / 共 {/TotalPage} 页 <&>");
|
watermark.setRotationAngle(0);
|
byte[] result = new DmsDocxWatermarkService(mock(DmsCommonFileStorage.class))
|
.apply(source, Collections.singletonList(watermark));
|
try (XWPFDocument document = new XWPFDocument(new ByteArrayInputStream(result))) {
|
assertEquals(3, document.getHeaderList().size());
|
for (org.apache.poi.xwpf.usermodel.XWPFHeader header : document.getHeaderList()) {
|
String xml = header._getHdrFtr().xmlText();
|
assertTrue(xml.contains(" PAGE "));
|
assertTrue(xml.contains(" NUMPAGES "));
|
assertTrue(xml.contains("txbxContent"));
|
assertTrue(javax.xml.parsers.DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
.parse(new ByteArrayInputStream(xml.getBytes(java.nio.charset.StandardCharsets.UTF_8)))
|
.getDocumentElement().getTextContent().contains("<&>"));
|
assertTrue(!xml.contains("{/CurrPage}") && !xml.contains("{/TotalPage}"));
|
assertTrue(xml.contains("70000")); // 30% visible means 70% transparent
|
}
|
assertEquals(0, document.getPackage().getPartsByName(Pattern.compile("/word/media/.*")).size());
|
assertEquals("受控文件正文", document.getParagraphArray(0).getText());
|
assertEquals("第二页正文", document.getParagraphs().get(document.getParagraphs().size() - 1).getText());
|
assertTrue(document.getFooterList().get(0).getText().contains("原始页脚"));
|
}
|
}
|
|
@Test
|
public void textInkStartsAtConfiguredPageCoordinates() throws Exception {
|
DmsDocxWatermarkService service = new DmsDocxWatermarkService(mock(DmsCommonFileStorage.class));
|
DmsWatermarkSnapshot watermark = watermark("POSITION");
|
watermark.setContent("I");
|
watermark.setFontFamily("SansSerif");
|
watermark.setFontSize(14);
|
watermark.setRotationAngle(0);
|
watermark.setOpacity(100);
|
watermark.setMarkWidthMm(new BigDecimal("100"));
|
watermark.setMarkHeightMm(new BigDecimal("20"));
|
DmsWatermarkPositionSnapshot position = watermark.getPositions().get(0);
|
position.setPortraitXMm(BigDecimal.ZERO);
|
position.setPortraitYMm(BigDecimal.ZERO);
|
|
BufferedImage pdfOverlay = ImageIO.read(new ByteArrayInputStream(
|
service.renderPdfOverlay(210, 297, List.of(watermark))));
|
assertInkStartsAt(pdfOverlay, 0, 0);
|
|
BufferedImage image = new BufferedImage(2100, 2970, BufferedImage.TYPE_INT_ARGB);
|
service.applyImageWatermarks(image, List.of(watermark));
|
assertInkStartsAt(image, 0, 0);
|
|
position.setPortraitXMm(new BigDecimal("10"));
|
position.setPortraitYMm(new BigDecimal("15"));
|
BufferedImage shifted = ImageIO.read(new ByteArrayInputStream(
|
service.renderPdfOverlay(210, 297, List.of(watermark))));
|
assertInkStartsAt(shifted, (int) Math.round(10 * 144 / 25.4),
|
(int) Math.round(15 * 144 / 25.4));
|
}
|
|
@Test
|
public void textInkStartsAtPageEdgeInDocxAndSelectedPageOverlays() throws Exception {
|
DmsDocxWatermarkService service = new DmsDocxWatermarkService(mock(DmsCommonFileStorage.class));
|
DmsWatermarkSnapshot watermark = watermark("POSITION");
|
watermark.setContent("I");
|
watermark.setFontFamily("SansSerif");
|
watermark.setFontSize(14);
|
watermark.setRotationAngle(0);
|
watermark.setOpacity(100);
|
watermark.setMarkWidthMm(new BigDecimal("100"));
|
watermark.setMarkHeightMm(new BigDecimal("20"));
|
watermark.getPositions().get(0).setPortraitXMm(BigDecimal.ZERO);
|
watermark.getPositions().get(0).setPortraitYMm(BigDecimal.ZERO);
|
|
byte[] docx = service.apply(sourceDocument(), List.of(watermark));
|
Path sample = Path.of("target", "watermark-preview", "dms-text-origin.docx");
|
Files.createDirectories(sample.getParent());
|
Files.write(sample, docx);
|
try (XWPFDocument document = new XWPFDocument(new ByteArrayInputStream(docx))) {
|
List<org.apache.poi.openxml4j.opc.PackagePart> pictures = document.getPackage()
|
.getPartsByName(Pattern.compile("/word/media/.*"));
|
assertEquals(1, pictures.size());
|
assertInkStartsAt(ImageIO.read(pictures.get(0).getInputStream()), 0, 0);
|
}
|
|
watermark.setRenderPages(List.of(1));
|
jnpf.onlyoffice.PageWatermarkRequest.Variant variant = service.pageOverlays(
|
sourceDocument(), List.of(watermark)).get(0).getVariants().get(0);
|
assertEquals(0D, variant.getOffsetXMm(), 0D);
|
assertEquals(0D, variant.getOffsetYMm(), 0D);
|
assertInkStartsAt(ImageIO.read(new ByteArrayInputStream(
|
Base64.getDecoder().decode(variant.getPngBase64()))), 0, 0);
|
}
|
|
@Test
|
public void pageNumberTextBoxStartsAtItsPageAnchor() throws Exception {
|
DmsWatermarkSnapshot watermark = watermark("POSITION");
|
watermark.setContent("{/CurrPage}/{/TotalPage}");
|
watermark.setFontFamily("SansSerif");
|
watermark.setRotationAngle(0);
|
watermark.getPositions().get(0).setPortraitXMm(BigDecimal.ZERO);
|
watermark.getPositions().get(0).setPortraitYMm(BigDecimal.ZERO);
|
|
byte[] docx = new DmsDocxWatermarkService(mock(DmsCommonFileStorage.class))
|
.apply(sourceDocument(), List.of(watermark));
|
Path sample = Path.of("target", "watermark-preview", "dms-page-field-origin.docx");
|
Files.createDirectories(sample.getParent());
|
Files.write(sample, docx);
|
try (XWPFDocument document = new XWPFDocument(new ByteArrayInputStream(docx))) {
|
String xml = document.getHeaderList().get(0)._getHdrFtr().xmlText();
|
javax.xml.parsers.DocumentBuilderFactory factory = javax.xml.parsers.DocumentBuilderFactory.newInstance();
|
factory.setNamespaceAware(true);
|
org.w3c.dom.Document header = factory.newDocumentBuilder().parse(
|
new ByteArrayInputStream(xml.getBytes(java.nio.charset.StandardCharsets.UTF_8)));
|
org.w3c.dom.Element textbox = (org.w3c.dom.Element) header
|
.getElementsByTagNameNS("*", "txbxContent").item(0);
|
org.w3c.dom.NodeList alignment = textbox.getElementsByTagNameNS("*", "jc");
|
assertTrue(alignment.getLength() > 0);
|
for (int i = 0; i < alignment.getLength(); i++) {
|
assertEquals("left", ((org.w3c.dom.Element) alignment.item(i)).getAttributeNS(
|
"http://schemas.openxmlformats.org/wordprocessingml/2006/main", "val"));
|
}
|
org.w3c.dom.Element body = (org.w3c.dom.Element) header.getElementsByTagNameNS("*", "bodyPr").item(0);
|
assertEquals("t", body.getAttribute("anchor"));
|
assertEquals("0", body.getAttribute("lIns"));
|
assertEquals("0", body.getAttribute("tIns"));
|
}
|
}
|
|
@Test
|
public void tiledPageFieldsCoexistWithStaticWatermarks() throws Exception {
|
DmsWatermarkSnapshot pages = watermark("TILED");
|
pages.setContent("第{/CurrPage}页,共{/TotalPage}页");
|
byte[] result = new DmsDocxWatermarkService(mock(DmsCommonFileStorage.class))
|
.apply(sourceDocument(), java.util.Arrays.asList(watermark("POSITION"), pages));
|
Path sample = Path.of("target", "watermark-preview", "dms-page-fields.docx");
|
Files.createDirectories(sample.getParent());
|
Files.write(sample, result);
|
try (XWPFDocument document = new XWPFDocument(new ByteArrayInputStream(result))) {
|
String xml = document.getHeaderList().get(0)._getHdrFtr().xmlText();
|
assertTrue(xml.split(" PAGE ", -1).length > 2);
|
assertTrue(xml.contains("19800000")); // -30 degrees normalized for DrawingML
|
assertTrue(!xml.contains("{/CurrPage}"));
|
assertEquals(1, document.getPackage().getPartsByName(Pattern.compile("/word/media/.*")).size());
|
}
|
}
|
|
@Test
|
public void positionWatermarkIsKeptInsidePageBounds() throws Exception {
|
DmsWatermarkSnapshot watermark = watermark("POSITION");
|
watermark.setContent("{/CurrPage}");
|
watermark.setOpacity(100);
|
watermark.getPositions().get(0).setPortraitXMm(new BigDecimal("165"));
|
watermark.getPositions().get(0).setPortraitYMm(new BigDecimal("290"));
|
|
byte[] result = new DmsDocxWatermarkService(mock(DmsCommonFileStorage.class))
|
.apply(sourceDocument(), Collections.singletonList(watermark));
|
|
try (XWPFDocument document = new XWPFDocument(new ByteArrayInputStream(result))) {
|
String xml = document.getHeaderList().get(0)._getHdrFtr().xmlText();
|
java.util.regex.Matcher horizontal = Pattern.compile(
|
"positionH[^>]*>.*?posOffset>(-?\\d+)", Pattern.DOTALL).matcher(xml);
|
java.util.regex.Matcher vertical = Pattern.compile(
|
"positionV[^>]*>.*?posOffset>(-?\\d+)", Pattern.DOTALL).matcher(xml);
|
assertTrue(horizontal.find());
|
assertTrue(vertical.find());
|
assertTrue(Long.parseLong(horizontal.group(1)) >= 0
|
&& Long.parseLong(horizontal.group(1)) < 5940000L); // less than configured 165 mm
|
assertTrue(Long.parseLong(vertical.group(1)) >= 0
|
&& Long.parseLong(vertical.group(1)) < 10440000L); // less than configured 290 mm
|
assertTrue(xml.contains(" PAGE "));
|
assertTrue(Pattern.compile("alpha[^>]*val=\\\"0\\\"").matcher(xml).find());
|
}
|
}
|
|
@Test
|
public void selectsA4AndA3CoordinatesForBothPageOrientations() throws Exception {
|
DmsWatermarkSnapshot mark = watermark("POSITION");
|
mark.setContent("CHECK");
|
mark.setFontFamily("SansSerif");
|
mark.setRotationAngle(0);
|
mark.setOpacity(100);
|
mark.setMarkWidthMm(new BigDecimal("30"));
|
mark.setMarkHeightMm(new BigDecimal("15"));
|
DmsWatermarkPositionSnapshot a4 = mark.getPositions().get(0);
|
a4.setPortraitXMm(new BigDecimal("10"));
|
a4.setPortraitYMm(new BigDecimal("10"));
|
a4.setLandscapeXMm(new BigDecimal("200"));
|
a4.setLandscapeYMm(new BigDecimal("10"));
|
DmsWatermarkPositionSnapshot a3 = new DmsWatermarkPositionSnapshot();
|
a3.setPaperType("A3");
|
a3.setPaperWidthMm(new BigDecimal("297"));
|
a3.setPaperHeightMm(new BigDecimal("420"));
|
a3.setPortraitXMm(new BigDecimal("10"));
|
a3.setPortraitYMm(new BigDecimal("360"));
|
a3.setLandscapeXMm(new BigDecimal("350"));
|
a3.setLandscapeYMm(new BigDecimal("10"));
|
mark.setPositions(List.of(a4, a3));
|
|
DmsDocxWatermarkService service = new DmsDocxWatermarkService(mock(DmsCommonFileStorage.class));
|
double[][] cases = {
|
{210, 297, 10, 10}, {297, 210, 200, 10},
|
{297, 420, 10, 360}, {420, 297, 350, 10}
|
};
|
for (double[] page : cases) {
|
BufferedImage result = ImageIO.read(new ByteArrayInputStream(
|
service.renderPdfOverlay(page[0], page[1], List.of(mark))));
|
int x = (int) Math.round(page[2] * 144 / 25.4);
|
int y = (int) Math.round(page[3] * 144 / 25.4);
|
int width = (int) Math.round(30 * 144 / 25.4);
|
int height = (int) Math.round(15 * 144 / 25.4);
|
int visiblePixels = 0;
|
for (int row = 0; row < result.getHeight(); row++) {
|
for (int column = 0; column < result.getWidth(); column++) {
|
if ((result.getRGB(column, row) >>> 24) == 0) continue;
|
visiblePixels++;
|
assertTrue("Watermark used the wrong paper size or orientation",
|
column >= x && column < x + width && row >= y && row < y + height);
|
}
|
}
|
assertTrue("Expected a visible watermark", visiblePixels > 0);
|
}
|
}
|
|
@Test
|
public void independentDisplayTextChangesOnlyTheLabelForBothBarcodeFormats() throws Exception {
|
DmsDocxWatermarkService service = new DmsDocxWatermarkService(mock(DmsCommonFileStorage.class));
|
for (String format : List.of("QR_CODE", "CODE_128")) {
|
DmsWatermarkSnapshot mark = watermark("POSITION");
|
mark.setWatermarkType("BARCODE");
|
mark.setBarcodeFormat(format);
|
mark.setContent("REC-2026-0004");
|
mark.setShowBarcodeText(true);
|
mark.setBarcodeText("Page four label");
|
BufferedImage first = service.barcode(mark, 454, 227);
|
mark.setBarcodeText("另一个独立文字");
|
BufferedImage second = service.barcode(mark, 454, 227);
|
assertEquals("REC-2026-0004", decode(first));
|
assertEquals("REC-2026-0004", decode(second));
|
assertTrue("Changing the display text must change the rendered label", !Arrays.equals(
|
first.getRGB(0, 0, first.getWidth(), first.getHeight(), null, 0, first.getWidth()),
|
second.getRGB(0, 0, second.getWidth(), second.getHeight(), null, 0, second.getWidth())));
|
}
|
}
|
|
@Test
|
public void repeatedPageEntriesRemainSeparateDrawingsWithTheirOwnPayloads() throws Exception {
|
DmsDocxWatermarkService service = new DmsDocxWatermarkService(mock(DmsCommonFileStorage.class));
|
DmsWatermarkSnapshot first = watermark("POSITION");
|
first.setWatermarkType("BARCODE");
|
first.setBarcodeFormat("QR_CODE");
|
first.setContent("REC-004");
|
first.setBarcodeText("First on page four");
|
first.setShowBarcodeText(true);
|
first.setRenderPages(List.of(4));
|
first.setRotationAngle(0);
|
first.setOpacity(100);
|
first.setMarkWidthMm(new BigDecimal("40"));
|
first.setMarkHeightMm(new BigDecimal("45"));
|
DmsWatermarkSnapshot second = new DmsWatermarkSnapshot();
|
org.springframework.beans.BeanUtils.copyProperties(first, second);
|
second.setContent("SECOND-004");
|
second.setBarcodeText("Second on page four");
|
List<jnpf.onlyoffice.PageWatermarkRequest.Overlay> overlays = service.pageOverlays(sourceDocument(), List.of(first, second, first));
|
assertEquals(3, overlays.size());
|
for (int index = 0; index < overlays.size(); index++) {
|
assertEquals(List.of(4), overlays.get(index).getPages());
|
jnpf.onlyoffice.PageWatermarkRequest.Variant variant = overlays.get(index).getVariants().get(0);
|
assertEquals(210D, variant.getWidthMm(), 0.2D);
|
assertEquals(297D, variant.getHeightMm(), 0.2D);
|
assertEquals(40D, variant.getDrawingWidthMm(), 0.01D);
|
assertEquals(45D, variant.getDrawingHeightMm(), 0.01D);
|
assertEquals(65D, variant.getOffsetXMm(), 0.2D);
|
assertEquals(138D, variant.getOffsetYMm(), 0.2D);
|
BufferedImage image = ImageIO.read(new ByteArrayInputStream(Base64.getDecoder().decode(variant.getPngBase64())));
|
assertEquals(index == 1 ? "SECOND-004" : "REC-004", decode(image));
|
}
|
}
|
|
private String decode(BufferedImage transparent) throws Exception {
|
BufferedImage white = new BufferedImage(transparent.getWidth() * 4,
|
transparent.getHeight() * 4, BufferedImage.TYPE_INT_RGB);
|
Graphics2D graphics = white.createGraphics();
|
graphics.setColor(Color.WHITE);
|
graphics.fillRect(0, 0, white.getWidth(), white.getHeight());
|
graphics.drawImage(transparent, 0, 0, white.getWidth(), white.getHeight(), null);
|
graphics.dispose();
|
int[] pixels = white.getRGB(0, 0, white.getWidth(), white.getHeight(), null, 0, white.getWidth());
|
return new MultiFormatReader().decode(new BinaryBitmap(new HybridBinarizer(
|
new RGBLuminanceSource(white.getWidth(), white.getHeight(), pixels)))).getText();
|
}
|
|
private int darkPixels(BufferedImage image, int startY) {
|
int count = 0;
|
for (int y = Math.max(0, startY); y < image.getHeight(); y++) {
|
for (int x = 0; x < image.getWidth(); x++) {
|
int argb = image.getRGB(x, y);
|
if ((argb >>> 24) != 0 && (argb & 0x00ffffff) == 0) {
|
count++;
|
}
|
}
|
}
|
return count;
|
}
|
|
private void assertInkStartsAt(BufferedImage image, int expectedX, int expectedY) {
|
int left = image.getWidth();
|
int top = image.getHeight();
|
for (int y = 0; y < image.getHeight(); y++) {
|
for (int x = 0; x < image.getWidth(); x++) {
|
if ((image.getRGB(x, y) >>> 24) > 0) {
|
left = Math.min(left, x);
|
top = Math.min(top, y);
|
}
|
}
|
}
|
assertEquals("First visible text pixel should start at configured X", expectedX, left);
|
assertEquals("First visible text pixel should start at configured Y", expectedY, top);
|
}
|
|
@Test
|
public void returnsOriginalBytesWhenNoWatermarkApplies() {
|
byte[] source = new byte[]{1, 2, 3};
|
byte[] result = new DmsDocxWatermarkService(mock(DmsCommonFileStorage.class))
|
.apply(source, Collections.emptyList());
|
assertTrue(source == result);
|
}
|
|
private DmsWatermarkSnapshot watermark(String layout) {
|
DmsWatermarkSnapshot watermark = new DmsWatermarkSnapshot();
|
watermark.setWatermarkType("TEXT");
|
watermark.setContent("张三 预览受控文件");
|
watermark.setFontFamily("SimHei");
|
watermark.setFontColor("#666666");
|
watermark.setFontSize(24);
|
watermark.setRotationAngle(-30);
|
watermark.setOpacity(30);
|
watermark.setLayoutMode(layout);
|
watermark.setMarkWidthMm(new BigDecimal("80"));
|
watermark.setMarkHeightMm(new BigDecimal("20"));
|
watermark.setRepeatSpacingXMm(new BigDecimal("20"));
|
watermark.setRepeatSpacingYMm(new BigDecimal("20"));
|
DmsWatermarkPositionSnapshot position = new DmsWatermarkPositionSnapshot();
|
position.setPaperType("A4");
|
position.setPaperWidthMm(new BigDecimal("210"));
|
position.setPaperHeightMm(new BigDecimal("297"));
|
position.setPortraitXMm(new BigDecimal("65"));
|
position.setPortraitYMm(new BigDecimal("138"));
|
position.setLandscapeXMm(new BigDecimal("108"));
|
position.setLandscapeYMm(new BigDecimal("95"));
|
watermark.setPositions(Collections.singletonList(position));
|
return watermark;
|
}
|
|
private byte[] sourceDocument() throws Exception {
|
try (XWPFDocument document = new XWPFDocument();
|
ByteArrayOutputStream output = new ByteArrayOutputStream()) {
|
document.createParagraph().createRun().setText("受控文件正文");
|
document.createHeader(HeaderFooterType.DEFAULT).createParagraph()
|
.createRun().setText("原始页眉");
|
document.createFooter(HeaderFooterType.DEFAULT).createParagraph()
|
.createRun().setText("原始页脚");
|
for (int i = 1; i <= 20; i++) {
|
document.createParagraph().createRun().setText("现行版文件预览内容示例 " + i);
|
}
|
document.write(output);
|
return output.toByteArray();
|
}
|
}
|
|
private byte[] imageBytes() throws Exception {
|
BufferedImage image = new BufferedImage(160, 80, BufferedImage.TYPE_INT_ARGB);
|
Graphics2D graphics = image.createGraphics();
|
try {
|
graphics.setColor(new Color(30, 120, 150));
|
graphics.fillRect(0, 0, image.getWidth(), image.getHeight());
|
} finally {
|
graphics.dispose();
|
}
|
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
ImageIO.write(image, "png", output);
|
return output.toByteArray();
|
}
|
|
}
|