Fix image stamp cropping and align preview with PDF output for add-stamp (#6013)

This commit is contained in:
Dexterity
2026-04-02 12:54:13 -04:00
committed by GitHub
parent c9a70f3754
commit fca40e5544
4 changed files with 117 additions and 15 deletions

View File

@@ -483,9 +483,19 @@ public class StampController {
y = overrideY;
} else {
x = calculatePositionX(pageSize, position, desiredPhysicalWidth, margin);
y = calculatePositionY(pageSize, position, desiredPhysicalHeight, margin);
// drawImage() places the lower-left corner at (x, y); use image-specific Y logic
y = calculateImagePositionY(pageSize, position, desiredPhysicalHeight, margin);
}
float llx = pageSize.getLowerLeftX();
float lly = pageSize.getLowerLeftY();
float urx = pageSize.getUpperRightX();
float ury = pageSize.getUpperRightY();
float xMax = Math.max(llx, urx - desiredPhysicalWidth);
float yMax = Math.max(lly, ury - desiredPhysicalHeight);
x = Math.min(xMax, Math.max(llx, x));
y = Math.min(yMax, Math.max(lly, y));
contentStream.saveGraphicsState();
contentStream.transform(Matrix.getTranslateInstance(x, y));
contentStream.transform(Matrix.getRotateInstance(Math.toRadians(rotation), 0, 0));
@@ -495,18 +505,37 @@ public class StampController {
private float calculatePositionX(
PDRectangle pageSize, int position, float contentWidth, float margin) {
float llx = pageSize.getLowerLeftX();
float urx = pageSize.getUpperRightX();
return switch (position % 3) {
case 1: // Left
yield pageSize.getLowerLeftX() + margin;
yield llx + margin;
case 2: // Center
yield (pageSize.getWidth() - contentWidth) / 2;
yield llx + (pageSize.getWidth() - contentWidth) / 2;
case 0: // Right
yield pageSize.getUpperRightX() - contentWidth - margin;
yield urx - contentWidth - margin;
default:
yield 0;
};
}
private float calculateImagePositionY(
PDRectangle pageSize, int position, float imageHeight, float margin) {
float lly = pageSize.getLowerLeftY();
float pageHeight = pageSize.getHeight();
float ury = pageSize.getUpperRightY();
return switch ((position - 1) / 3) {
case 0: // Top - upper image edge flush below top margin
yield ury - margin - imageHeight;
case 1: // Middle - center image on page
yield lly + (pageHeight - imageHeight) / 2;
case 2: // Bottom - lower image edge at bottom margin
yield lly + margin;
default:
yield lly;
};
}
private float calculatePositionY(
PDRectangle pageSize, int position, float height, float margin) {
return switch ((position - 1) / 3) {

View File

@@ -9,6 +9,7 @@ import java.util.regex.Pattern;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentInformation;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
@@ -46,6 +47,7 @@ class StampControllerTest {
private Method processStampTextMethod;
private Method processCustomDateFormatMethod;
private Method calculateImagePositionYMethod;
@BeforeEach
void setUp() throws NoSuchMethodException {
@@ -63,6 +65,26 @@ class StampControllerTest {
StampController.class.getDeclaredMethod(
"processCustomDateFormat", String.class, LocalDateTime.class);
processCustomDateFormatMethod.setAccessible(true);
calculateImagePositionYMethod =
StampController.class.getDeclaredMethod(
"calculateImagePositionY",
PDRectangle.class,
int.class,
float.class,
float.class);
calculateImagePositionYMethod.setAccessible(true);
}
private float invokeCalculateImagePositionY(
PDRectangle pageSize, int position, float imageHeight, float margin) throws Exception {
try {
return (float)
calculateImagePositionYMethod.invoke(
stampController, pageSize, position, imageHeight, margin);
} catch (InvocationTargetException e) {
throw (Exception) e.getCause();
}
}
private String invokeProcessStampText(
@@ -86,6 +108,45 @@ class StampControllerTest {
}
}
@Nested
@DisplayName("Image stamp position (lower-left anchor)")
class ImagePositionYTests {
@Test
@DisplayName("Top row: upper edge of image sits below top margin")
void topRowUsesUpperRightMinusMarginMinusHeight() throws Exception {
PDRectangle page = new PDRectangle(0, 0, 600, 800);
float y = invokeCalculateImagePositionY(page, 3, 100f, 10f);
assertEquals(690f, y, 0.001f);
}
@Test
@DisplayName("Middle row: image is vertically centred on page")
void middleRowCentresImage() throws Exception {
PDRectangle page = new PDRectangle(0, 0, 600, 800);
float y = invokeCalculateImagePositionY(page, 5, 100f, 10f);
assertEquals(350f, y, 0.001f);
}
@Test
@DisplayName("Bottom row: lower edge of image sits above bottom margin")
void bottomRowUsesLowerLeftPlusMargin() throws Exception {
PDRectangle page = new PDRectangle(0, 0, 600, 800);
float y = invokeCalculateImagePositionY(page, 7, 100f, 10f);
assertEquals(10f, y, 0.001f);
}
@Test
@DisplayName("Honours non-zero media box origin")
void respectsLowerLeftOrigin() throws Exception {
PDRectangle page = new PDRectangle(50f, 100f, 400f, 300f);
float yMid = invokeCalculateImagePositionY(page, 5, 20f, 5f);
assertEquals(240f, yMid, 0.001f);
float yTop = invokeCalculateImagePositionY(page, 3, 20f, 5f);
assertEquals(375f, yTop, 0.001f);
}
}
@Nested
@DisplayName("Basic Variable Substitution Tests")
class BasicVariableTests {