feat(pdf): suporte a redimensionamento interativo e proporcional do selo de assinatura
This commit is contained in:
+27
-4
@@ -25,7 +25,8 @@ public record PdfSignatureOptions(
|
||||
Integer targetPageNumber,
|
||||
CpfDisplayMode cpfDisplayMode,
|
||||
Boolean includeDateTime,
|
||||
String customExtraInfo
|
||||
String customExtraInfo,
|
||||
Double customSealScale
|
||||
) {
|
||||
|
||||
public PdfSignatureOptions {
|
||||
@@ -51,6 +52,21 @@ public record PdfSignatureOptions(
|
||||
if (includeDateTime == null) {
|
||||
includeDateTime = sealTheme.defaultIncludeDateTime();
|
||||
}
|
||||
if (customSealScale == null || customSealScale <= 0) {
|
||||
customSealScale = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construtor para compatibilidade com assinaturas anteriores a customSealScale.
|
||||
*/
|
||||
public PdfSignatureOptions(String certificateId, char[] pinOrPassword, boolean visibleSeal,
|
||||
PdfSealTheme sealTheme, PdfSealPosition position, PdfSealPageChoice pageChoice,
|
||||
String reason, String location, Float customX, Float customY,
|
||||
Integer targetPageNumber, CpfDisplayMode cpfDisplayMode, Boolean includeDateTime,
|
||||
String customExtraInfo) {
|
||||
this(certificateId, pinOrPassword, visibleSeal, sealTheme, position, pageChoice, reason, location,
|
||||
customX, customY, targetPageNumber, cpfDisplayMode, includeDateTime, customExtraInfo, 1.0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,7 +77,7 @@ public record PdfSignatureOptions(
|
||||
String reason, String location, Float customX, Float customY,
|
||||
Integer targetPageNumber, CpfDisplayMode cpfDisplayMode, Boolean includeDateTime) {
|
||||
this(certificateId, pinOrPassword, visibleSeal, sealTheme, position, pageChoice, reason, location,
|
||||
customX, customY, targetPageNumber, cpfDisplayMode, includeDateTime, null);
|
||||
customX, customY, targetPageNumber, cpfDisplayMode, includeDateTime, null, 1.0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,7 +86,7 @@ public record PdfSignatureOptions(
|
||||
public PdfSignatureOptions(String certificateId, char[] pinOrPassword, boolean visibleSeal,
|
||||
PdfSealTheme sealTheme, PdfSealPosition position, PdfSealPageChoice pageChoice,
|
||||
String reason, String location) {
|
||||
this(certificateId, pinOrPassword, visibleSeal, sealTheme, position, pageChoice, reason, location, null, null, null, null, null, null);
|
||||
this(certificateId, pinOrPassword, visibleSeal, sealTheme, position, pageChoice, reason, location, null, null, null, null, null, null, 1.0);
|
||||
}
|
||||
|
||||
public PdfSignatureOptions(String certificateId, char[] pinOrPassword, boolean visibleSeal,
|
||||
@@ -143,6 +159,7 @@ public record PdfSignatureOptions(
|
||||
}
|
||||
|
||||
private String customExtraInfo;
|
||||
private Double customSealScale = 1.0;
|
||||
|
||||
public Builder includeDateTime(boolean include) {
|
||||
this.includeDateTime = include;
|
||||
@@ -154,6 +171,11 @@ public record PdfSignatureOptions(
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder customSealScale(Double scale) {
|
||||
this.customSealScale = scale;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder customPosition(Float x, Float y, Integer pageNumber) {
|
||||
this.customX = x;
|
||||
this.customY = y;
|
||||
@@ -179,7 +201,8 @@ public record PdfSignatureOptions(
|
||||
targetPageNumber,
|
||||
cpfDisplayMode,
|
||||
includeDateTime,
|
||||
customExtraInfo
|
||||
customExtraInfo,
|
||||
customSealScale
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -235,8 +235,11 @@ public class PdfSigningService {
|
||||
|
||||
PDImageXObject pdImage = PDImageXObject.createFromByteArray(doc, sealPng, "cybercore_seal");
|
||||
|
||||
float sealWidth = theme.width() * 0.72f;
|
||||
float sealHeight = theme.height() * 0.72f;
|
||||
float scale = (options.customSealScale() != null && options.customSealScale() > 0)
|
||||
? options.customSealScale().floatValue()
|
||||
: 1.0f;
|
||||
float sealWidth = (theme.width() * 0.72f) * scale;
|
||||
float sealHeight = (theme.height() * 0.72f) * scale;
|
||||
float margin = 20f;
|
||||
|
||||
// Se coordenadas customizadas foram informadas (clique ou drag & drop na página)
|
||||
|
||||
+23
-2
@@ -54,6 +54,22 @@ public final class PdfCertificateSignDialog {
|
||||
private static final Logger logger = LoggerFactory.getLogger(PdfCertificateSignDialog.class);
|
||||
|
||||
public record SealConfig(
|
||||
boolean visibleSeal,
|
||||
PdfSealTheme sealTheme,
|
||||
CpfDisplayMode cpfDisplayMode,
|
||||
boolean includeDateTime,
|
||||
String customExtraInfo,
|
||||
PdfSignDialog.PositionOption positionOption,
|
||||
PdfSignDialog.PageOption pageOption,
|
||||
boolean isCustomPosition,
|
||||
Float customX,
|
||||
Float customY,
|
||||
Integer customPageNumber,
|
||||
String reason,
|
||||
String location,
|
||||
Double customSealScale
|
||||
) {
|
||||
public SealConfig(
|
||||
boolean visibleSeal,
|
||||
PdfSealTheme sealTheme,
|
||||
CpfDisplayMode cpfDisplayMode,
|
||||
@@ -67,7 +83,11 @@ public final class PdfCertificateSignDialog {
|
||||
Integer customPageNumber,
|
||||
String reason,
|
||||
String location
|
||||
) {}
|
||||
) {
|
||||
this(visibleSeal, sealTheme, cpfDisplayMode, includeDateTime, customExtraInfo, positionOption,
|
||||
pageOption, isCustomPosition, customX, customY, customPageNumber, reason, location, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
private final Stage stage;
|
||||
private final CertificateManager certManager;
|
||||
@@ -429,7 +449,8 @@ public final class PdfCertificateSignDialog {
|
||||
.includeDateTime(sealConfig.includeDateTime())
|
||||
.customExtraInfo(sealConfig.customExtraInfo())
|
||||
.reason(sealConfig.reason())
|
||||
.location(sealConfig.location());
|
||||
.location(sealConfig.location())
|
||||
.customSealScale(sealConfig.customSealScale());
|
||||
|
||||
if (sealConfig.isCustomPosition() && sealConfig.customX() != null && sealConfig.customY() != null) {
|
||||
b.customPosition(sealConfig.customX(), sealConfig.customY(), sealConfig.customPageNumber());
|
||||
|
||||
+185
-39
@@ -46,6 +46,8 @@ import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.shape.Circle;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
import javafx.stage.FileChooser;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
@@ -127,6 +129,22 @@ public final class PdfSignDialog {
|
||||
private ComboBox<PageOption> pageCombo;
|
||||
private ImageView sealPreviewImageView;
|
||||
|
||||
// Controle de Escala / Redimensionamento do Selo (1.0 = 100%)
|
||||
private double sealScale = 1.0;
|
||||
private static final double MIN_SEAL_SCALE = 0.40;
|
||||
private static final double MAX_SEAL_SCALE = 1.80;
|
||||
private boolean isResizeModeActive = false;
|
||||
|
||||
// Componentes do grupo do selo e alça interativa
|
||||
private Pane sealGroupPane;
|
||||
private Rectangle sealSelectionBorder;
|
||||
private Circle resizeHandle;
|
||||
private Button btnToggleResize;
|
||||
private Button btnResetSealScale;
|
||||
private Label lblSealScaleIndicator;
|
||||
private double resizeStartSceneX;
|
||||
private double resizeStartBaseWidth;
|
||||
|
||||
// Visualizador Interativo da Folha
|
||||
private ScrollPane sheetScroll;
|
||||
private StackPane pageSheetContainer;
|
||||
@@ -463,13 +481,34 @@ public final class PdfSignDialog {
|
||||
|
||||
zoomBox.getChildren().addAll(btnZoomOut, lblZoomValue, btnZoomIn, btnZoomFit);
|
||||
|
||||
// Grupo de Tamanho do Selo
|
||||
HBox resizeToolBox = new HBox(6);
|
||||
resizeToolBox.setAlignment(Pos.CENTER_LEFT);
|
||||
resizeToolBox.setStyle("-fx-background-color: -fx-bg-primary; -fx-border-color: -fx-border-light; -fx-border-radius: 6px; -fx-padding: 2px 6px;");
|
||||
|
||||
btnToggleResize = new Button("📏 Redimensionar");
|
||||
btnToggleResize.getStyleClass().addAll("btn-secondary", "btn-compact");
|
||||
btnToggleResize.setTooltip(new Tooltip("Ativar/desativar alça interativa para alterar o tamanho do selo"));
|
||||
btnToggleResize.setOnAction(e -> toggleResizeMode());
|
||||
|
||||
lblSealScaleIndicator = new Label("100%");
|
||||
lblSealScaleIndicator.setStyle("-fx-font-size: 11px; -fx-font-weight: bold; -fx-min-width: 36px; -fx-alignment: center;");
|
||||
|
||||
btnResetSealScale = new Button("↺ 100%");
|
||||
btnResetSealScale.getStyleClass().addAll("btn-secondary", "btn-compact");
|
||||
btnResetSealScale.setTooltip(new Tooltip("Restaurar tamanho padrão original (100%)"));
|
||||
btnResetSealScale.setVisible(false);
|
||||
btnResetSealScale.setOnAction(e -> applySealScale(1.0));
|
||||
|
||||
resizeToolBox.getChildren().addAll(btnToggleResize, lblSealScaleIndicator, btnResetSealScale);
|
||||
|
||||
Region tbSpacer = new Region();
|
||||
HBox.setHgrow(tbSpacer, Priority.ALWAYS);
|
||||
|
||||
lblPositionFeedback = new Label("👉 Clique na folha para posicionar");
|
||||
lblPositionFeedback.setStyle("-fx-font-size: 11.5px; -fx-text-fill: -fx-accent; -fx-font-weight: bold;");
|
||||
|
||||
toolbar.getChildren().addAll(pageNavBox, zoomBox, tbSpacer, lblPositionFeedback);
|
||||
toolbar.getChildren().addAll(pageNavBox, zoomBox, resizeToolBox, tbSpacer, lblPositionFeedback);
|
||||
|
||||
// ScrollPane da Folha de Papel
|
||||
sheetScroll = new ScrollPane();
|
||||
@@ -517,14 +556,43 @@ public final class PdfSignDialog {
|
||||
handleSheetClick(event);
|
||||
});
|
||||
|
||||
// Grupo visual do selo composto pela imagem, borda de seleção e alça de quina
|
||||
sealGroupPane = new Pane();
|
||||
sealGroupPane.setVisible(false);
|
||||
|
||||
sealStampOverlayView = new ImageView();
|
||||
sealStampOverlayView.setPreserveRatio(true);
|
||||
sealStampOverlayView.setCursor(Cursor.MOVE);
|
||||
DropShadow sealShadow = new DropShadow(8, 0, 2, Color.rgb(2, 132, 199, 0.65));
|
||||
sealStampOverlayView.setEffect(sealShadow);
|
||||
sealStampOverlayView.setVisible(false);
|
||||
|
||||
// Arraste com ancoragem precisa no ponto exato do clique dentro do selo (sem salto e sem drift)
|
||||
sealSelectionBorder = new Rectangle();
|
||||
sealSelectionBorder.setFill(Color.TRANSPARENT);
|
||||
sealSelectionBorder.setStroke(Color.rgb(2, 132, 199));
|
||||
sealSelectionBorder.setStrokeWidth(1.8);
|
||||
sealSelectionBorder.getStrokeDashArray().addAll(5.0, 4.0);
|
||||
sealSelectionBorder.setVisible(false);
|
||||
|
||||
resizeHandle = new Circle(8);
|
||||
resizeHandle.setFill(Color.rgb(2, 132, 199));
|
||||
resizeHandle.setStroke(Color.WHITE);
|
||||
resizeHandle.setStrokeWidth(2.0);
|
||||
resizeHandle.setCursor(Cursor.SE_RESIZE);
|
||||
DropShadow handleShadow = new DropShadow(4, 0, 1, Color.rgb(0, 0, 0, 0.35));
|
||||
resizeHandle.setEffect(handleShadow);
|
||||
resizeHandle.setVisible(false);
|
||||
Tooltip.install(resizeHandle, new Tooltip("Puxe a quina para redimensionar o selo proporcionalmente"));
|
||||
|
||||
// Duplo clique no selo para ativar/desativar o modo de redimensionamento
|
||||
sealStampOverlayView.setOnMouseClicked(event -> {
|
||||
sealDragOccurred = false;
|
||||
if (event.getClickCount() == 2) {
|
||||
toggleResizeMode();
|
||||
}
|
||||
event.consume();
|
||||
});
|
||||
|
||||
// Arraste com ancoragem precisa no ponto exato do clique dentro do selo
|
||||
sealStampOverlayView.setOnMousePressed(event -> {
|
||||
dragStartOffsetInSealX = event.getX();
|
||||
dragStartOffsetInSealY = event.getY();
|
||||
@@ -536,7 +604,6 @@ public final class PdfSignDialog {
|
||||
if (!chkVisibleSeal.isSelected() || selectedPdfBytes == null) return;
|
||||
sealDragOccurred = true;
|
||||
|
||||
// Converte coordenada da cena para coordenadas locais do painel da folha
|
||||
Point2D localInSheet = interactiveOverlayPane.sceneToLocal(event.getSceneX(), event.getSceneY());
|
||||
double targetX = localInSheet.getX() - dragStartOffsetInSealX;
|
||||
double targetY = localInSheet.getY() - dragStartOffsetInSealY;
|
||||
@@ -544,17 +611,32 @@ public final class PdfSignDialog {
|
||||
event.consume();
|
||||
});
|
||||
|
||||
sealStampOverlayView.setOnMouseReleased(event -> {
|
||||
// Garante que o evento de soltura do mouse não borbulhe para a folha
|
||||
sealStampOverlayView.setOnMouseReleased(event -> event.consume());
|
||||
|
||||
// Eventos da Alça de Redimensionamento Interativa
|
||||
resizeHandle.setOnMousePressed(event -> {
|
||||
resizeStartSceneX = event.getSceneX();
|
||||
resizeStartBaseWidth = currentBaseSealWidth * zoomFactor * sealScale;
|
||||
event.consume();
|
||||
});
|
||||
|
||||
sealStampOverlayView.setOnMouseClicked(event -> {
|
||||
sealDragOccurred = false;
|
||||
resizeHandle.setOnMouseDragged(event -> {
|
||||
if (!chkVisibleSeal.isSelected() || selectedPdfBytes == null) return;
|
||||
double deltaX = event.getSceneX() - resizeStartSceneX;
|
||||
double newWidth = resizeStartBaseWidth + deltaX;
|
||||
double baseW = currentBaseSealWidth * zoomFactor;
|
||||
if (baseW > 0) {
|
||||
applySealScale(newWidth / baseW);
|
||||
}
|
||||
event.consume();
|
||||
});
|
||||
|
||||
interactiveOverlayPane.getChildren().add(sealStampOverlayView);
|
||||
resizeHandle.setOnMouseReleased(event -> event.consume());
|
||||
resizeHandle.setOnMouseClicked(event -> event.consume());
|
||||
|
||||
sealGroupPane.getChildren().addAll(sealStampOverlayView, sealSelectionBorder, resizeHandle);
|
||||
interactiveOverlayPane.getChildren().add(sealGroupPane);
|
||||
|
||||
pageSheetContainer.getChildren().addAll(pageImageView, interactiveOverlayPane);
|
||||
sheetWrapper.getChildren().add(pageSheetContainer);
|
||||
sheetScroll.setContent(sheetWrapper);
|
||||
@@ -563,6 +645,41 @@ public final class PdfSignDialog {
|
||||
return column;
|
||||
}
|
||||
|
||||
private void toggleResizeMode() {
|
||||
this.isResizeModeActive = !this.isResizeModeActive;
|
||||
if (sealSelectionBorder != null) {
|
||||
sealSelectionBorder.setVisible(isResizeModeActive);
|
||||
}
|
||||
if (resizeHandle != null) {
|
||||
resizeHandle.setVisible(isResizeModeActive);
|
||||
}
|
||||
if (btnToggleResize != null) {
|
||||
if (isResizeModeActive) {
|
||||
btnToggleResize.setText("✓ Concluir");
|
||||
btnToggleResize.setStyle("-fx-background-color: #0284c7; -fx-text-fill: white; -fx-font-weight: bold;");
|
||||
lblPositionFeedback.setText("👉 Puxe a alça azul no canto do selo para ajustar o tamanho");
|
||||
} else {
|
||||
btnToggleResize.setText("📏 Redimensionar");
|
||||
btnToggleResize.setStyle("");
|
||||
updateFeedbackLabel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void applySealScale(double newScale) {
|
||||
this.sealScale = Math.max(MIN_SEAL_SCALE, Math.min(MAX_SEAL_SCALE, Math.round(newScale * 100.0) / 100.0));
|
||||
if (lblSealScaleIndicator != null) {
|
||||
lblSealScaleIndicator.setText(String.format("%.0f%%", sealScale * 100));
|
||||
}
|
||||
if (btnResetSealScale != null) {
|
||||
btnResetSealScale.setVisible(Math.abs(sealScale - 1.0) > 0.02);
|
||||
}
|
||||
|
||||
updateContainerZoomDimensions();
|
||||
repositionSealByRelativeCoords();
|
||||
updateFeedbackLabel();
|
||||
}
|
||||
|
||||
private void adjustZoom(double delta) {
|
||||
setZoom(Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, this.zoomFactor + delta)));
|
||||
}
|
||||
@@ -592,8 +709,19 @@ public final class PdfSignDialog {
|
||||
pageImageView.setFitHeight(currentH);
|
||||
}
|
||||
if (sealStampOverlayView != null) {
|
||||
sealStampOverlayView.setFitWidth(currentBaseSealWidth * zoomFactor);
|
||||
sealStampOverlayView.setFitHeight(currentBaseSealHeight * zoomFactor);
|
||||
double sealW = currentBaseSealWidth * zoomFactor * sealScale;
|
||||
double sealH = currentBaseSealHeight * zoomFactor * sealScale;
|
||||
sealStampOverlayView.setFitWidth(sealW);
|
||||
sealStampOverlayView.setFitHeight(sealH);
|
||||
|
||||
if (sealSelectionBorder != null) {
|
||||
sealSelectionBorder.setWidth(sealW);
|
||||
sealSelectionBorder.setHeight(sealH);
|
||||
}
|
||||
if (resizeHandle != null) {
|
||||
resizeHandle.setCenterX(sealW);
|
||||
resizeHandle.setCenterY(sealH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -602,15 +730,17 @@ public final class PdfSignDialog {
|
||||
|
||||
double sheetW = currentBaseSheetWidth * zoomFactor;
|
||||
double sheetH = currentBaseSheetHeight * zoomFactor;
|
||||
double sealW = currentBaseSealWidth * zoomFactor;
|
||||
double sealH = currentBaseSealHeight * zoomFactor;
|
||||
double sealW = currentBaseSealWidth * zoomFactor * sealScale;
|
||||
double sealH = currentBaseSealHeight * zoomFactor * sealScale;
|
||||
|
||||
double posX = Math.max(0, Math.min(sealRelativeX * sheetW, sheetW - sealW));
|
||||
double posY = Math.max(0, Math.min(sealRelativeY * sheetH, sheetH - sealH));
|
||||
|
||||
sealStampOverlayView.setLayoutX(posX);
|
||||
sealStampOverlayView.setLayoutY(posY);
|
||||
sealStampOverlayView.setVisible(true);
|
||||
if (sealGroupPane != null) {
|
||||
sealGroupPane.setLayoutX(posX);
|
||||
sealGroupPane.setLayoutY(posY);
|
||||
sealGroupPane.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
@@ -653,7 +783,9 @@ public final class PdfSignDialog {
|
||||
renderCurrentPage();
|
||||
|
||||
if (chkVisibleSeal.isSelected()) {
|
||||
sealStampOverlayView.setVisible(true);
|
||||
if (sealGroupPane != null) {
|
||||
sealGroupPane.setVisible(true);
|
||||
}
|
||||
resetSealToCornerPosition(PositionOption.BOTTOM_RIGHT);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
@@ -751,38 +883,42 @@ public final class PdfSignDialog {
|
||||
// =========================================================================
|
||||
private void handleSheetClick(MouseEvent event) {
|
||||
if (!chkVisibleSeal.isSelected() || selectedPdfBytes == null) return;
|
||||
double sealW = currentBaseSealWidth * zoomFactor;
|
||||
double sealH = currentBaseSealHeight * zoomFactor;
|
||||
double sealW = currentBaseSealWidth * zoomFactor * sealScale;
|
||||
double sealH = currentBaseSealHeight * zoomFactor * sealScale;
|
||||
updateSealPositionFromDrag(event.getX() - sealW / 2.0, event.getY() - sealH / 2.0);
|
||||
}
|
||||
|
||||
private void updateSealPositionFromDrag(double newLayoutX, double newLayoutY) {
|
||||
double sheetW = currentBaseSheetWidth * zoomFactor;
|
||||
double sheetH = currentBaseSheetHeight * zoomFactor;
|
||||
double sealW = currentBaseSealWidth * zoomFactor;
|
||||
double sealH = currentBaseSealHeight * zoomFactor;
|
||||
double sealW = currentBaseSealWidth * zoomFactor * sealScale;
|
||||
double sealH = currentBaseSealHeight * zoomFactor * sealScale;
|
||||
|
||||
double clampedX = Math.max(0, Math.min(newLayoutX, sheetW - sealW));
|
||||
double clampedY = Math.max(0, Math.min(newLayoutY, sheetH - sealH));
|
||||
|
||||
sealStampOverlayView.setLayoutX(clampedX);
|
||||
sealStampOverlayView.setLayoutY(clampedY);
|
||||
sealStampOverlayView.setVisible(true);
|
||||
if (sealGroupPane != null) {
|
||||
sealGroupPane.setLayoutX(clampedX);
|
||||
sealGroupPane.setLayoutY(clampedY);
|
||||
sealGroupPane.setVisible(true);
|
||||
}
|
||||
|
||||
this.sealRelativeX = (sheetW > 0) ? (clampedX / sheetW) : 0;
|
||||
this.sealRelativeY = (sheetH > 0) ? (clampedY / sheetH) : 0;
|
||||
|
||||
PdfSealTheme theme = activeSealTheme != null ? activeSealTheme : PdfSealThemeRegistry.getDefaultTheme();
|
||||
float sealPdfW = (float) ((theme != null) ? theme.width() * 0.72 : 144.0);
|
||||
float sealPdfH = (float) ((theme != null) ? theme.height() * 0.72 : 44.0);
|
||||
float basePdfW = (float) ((theme != null) ? theme.width() * 0.72 : 144.0);
|
||||
float basePdfH = (float) ((theme != null) ? theme.height() * 0.72 : 44.0);
|
||||
float scaledPdfW = basePdfW * (float) sealScale;
|
||||
float scaledPdfH = basePdfH * (float) sealScale;
|
||||
|
||||
// Conversão direta e exata da escala visual na tela para os pontos do PDF
|
||||
double scale = (currentPagePdfWidth > 0) ? (sheetW / currentPagePdfWidth) : 1.0;
|
||||
float calculatedPdfX = (float) (clampedX / scale);
|
||||
float calculatedPdfY = (float) (currentPagePdfHeight - (clampedY / scale) - sealPdfH);
|
||||
float calculatedPdfY = (float) (currentPagePdfHeight - (clampedY / scale) - scaledPdfH);
|
||||
|
||||
this.customPdfX = Math.max(0, Math.min(calculatedPdfX, currentPagePdfWidth - sealPdfW));
|
||||
this.customPdfY = Math.max(0, Math.min(calculatedPdfY, currentPagePdfHeight - sealPdfH));
|
||||
this.customPdfX = Math.max(0, Math.min(calculatedPdfX, currentPagePdfWidth - scaledPdfW));
|
||||
this.customPdfY = Math.max(0, Math.min(calculatedPdfY, currentPagePdfHeight - scaledPdfH));
|
||||
this.customPageNumber = currentPageIndex + 1;
|
||||
this.isCustomPositionActive = true;
|
||||
|
||||
@@ -799,8 +935,10 @@ public final class PdfSignDialog {
|
||||
}
|
||||
|
||||
PdfSealTheme theme = activeSealTheme != null ? activeSealTheme : PdfSealThemeRegistry.getDefaultTheme();
|
||||
float sealPdfW = (float) ((theme != null) ? theme.width() * 0.72 : 144.0);
|
||||
float sealPdfH = (float) ((theme != null) ? theme.height() * 0.72 : 44.0);
|
||||
float basePdfW = (float) ((theme != null) ? theme.width() * 0.72 : 144.0);
|
||||
float basePdfH = (float) ((theme != null) ? theme.height() * 0.72 : 44.0);
|
||||
float sealPdfW = basePdfW * (float) sealScale;
|
||||
float sealPdfH = basePdfH * (float) sealScale;
|
||||
float margin = 20f;
|
||||
|
||||
float targetPdfX;
|
||||
@@ -853,16 +991,22 @@ public final class PdfSignDialog {
|
||||
|
||||
private void updateFeedbackLabel() {
|
||||
if (isCustomPositionActive && customPageNumber != null && customPdfX != null && customPdfY != null) {
|
||||
lblPositionFeedback.setText(String.format("📍 Selo na Pág. %d [X: %.0f pt, Y: %.0f pt]",
|
||||
customPageNumber, customPdfX, customPdfY));
|
||||
String scaleStr = (Math.abs(sealScale - 1.0) > 0.02) ? String.format(" | Escala: %.0f%%", sealScale * 100) : "";
|
||||
lblPositionFeedback.setText(String.format("📍 Selo na Pág. %d [X: %.0f pt, Y: %.0f pt]%s",
|
||||
customPageNumber, customPdfX, customPdfY, scaleStr));
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleVisibleSeal(boolean active) {
|
||||
positionCombo.setDisable(!active);
|
||||
pageCombo.setDisable(!active);
|
||||
if (btnToggleResize != null) {
|
||||
btnToggleResize.setDisable(!active);
|
||||
}
|
||||
sealPreviewImageView.setOpacity(active ? 1.0 : 0.3);
|
||||
sealStampOverlayView.setVisible(active && selectedPdfBytes != null);
|
||||
if (sealGroupPane != null) {
|
||||
sealGroupPane.setVisible(active && selectedPdfBytes != null);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSealPreview() {
|
||||
@@ -927,19 +1071,21 @@ public final class PdfSignDialog {
|
||||
customPdfY,
|
||||
customPageNumber,
|
||||
DEFAULT_REASON,
|
||||
DEFAULT_LOCATION
|
||||
DEFAULT_LOCATION,
|
||||
sealScale
|
||||
);
|
||||
|
||||
String sealSummary;
|
||||
if (chkVisibleSeal.isSelected()) {
|
||||
String themeName = theme.name();
|
||||
String scaleInfo = (Math.abs(sealScale - 1.0) > 0.02) ? String.format(" [Escala: %.0f%%]", sealScale * 100) : "";
|
||||
|
||||
if (isCustomPositionActive && customPageNumber != null && customPdfX != null && customPdfY != null) {
|
||||
sealSummary = String.format("Selo Estampado (%s) — Pág. %d [X: %.0f pt, Y: %.0f pt]",
|
||||
themeName, customPageNumber, customPdfX, customPdfY);
|
||||
sealSummary = String.format("Selo Estampado (%s)%s — Pág. %d [X: %.0f pt, Y: %.0f pt]",
|
||||
themeName, scaleInfo, customPageNumber, customPdfX, customPdfY);
|
||||
} else {
|
||||
sealSummary = String.format("Selo Estampado (%s) — %s (%s)",
|
||||
themeName, (pos != null ? pos.label : "Canto Inferior Direito"), (pg != null ? pg.label : "Última Página"));
|
||||
sealSummary = String.format("Selo Estampado (%s)%s — %s (%s)",
|
||||
themeName, scaleInfo, (pos != null ? pos.label : "Canto Inferior Direito"), (pg != null ? pg.label : "Última Página"));
|
||||
}
|
||||
} else {
|
||||
sealSummary = "Sem Selo Visual (Assinatura PAdES Invisível)";
|
||||
|
||||
Reference in New Issue
Block a user