Fix #29: Fit-to-view via statischem A4-Fallback statt asynchronem Seitenverhältnis
Ersetzt fetchAspectRatioAsync/fitToView durch updateZoom() mit A4-Dimensionen (595x842 Punkte) als Fallback. Scrollbalken werden per CSS und ScrollPane-Policy ausgeblendet. Zoom-Listener auf pdfView statt viewStack. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+32
-76
@@ -29,10 +29,10 @@ import javafx.scene.layout.VBox;
|
||||
*
|
||||
* <p>Die Komponente zeigt eine einzelne Seite der PDF-Datei vollständig eingepasst
|
||||
* (fit-to-view) an. Das Laden erfolgt auf einem Hintergrund-Worker-Thread; UI-Updates
|
||||
* laufen ausschließlich über den JavaFX Application Thread. Nach dem Laden wird das
|
||||
* Seitenverhältnis asynchron ermittelt und der Zoom so gesetzt, dass die Seite ohne
|
||||
* Scrollbalken vollständig sichtbar ist. Bei Größenänderungen der Anzeigefläche wird
|
||||
* der Zoom automatisch neu berechnet.
|
||||
* laufen ausschließlich über den JavaFX Application Thread. Der Zoom wird anhand der
|
||||
* aktuellen Anzeigefläche und eines A4-Seiten-Fallbacks berechnet, sodass die Seite
|
||||
* ohne Scrollbalken vollständig sichtbar ist. Bei Größenänderungen der Anzeigefläche
|
||||
* wird der Zoom automatisch neu berechnet.
|
||||
*
|
||||
* <p>Es gilt das Prinzip „Latest Preview Request Wins": Veraltete Lade-Ergebnisse
|
||||
* werden verworfen, sobald eine neue Anforderung eingeht.
|
||||
@@ -95,12 +95,6 @@ public final class PdfPreviewPane {
|
||||
/** Gibt an ob die Navigation bedienbar ist. */
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* Zuletzt ermitteltes Seitenverhältnis (Höhe/Breite) der gerenderten Seite.
|
||||
* Wird asynchron nach dem Laden gesetzt; -1.0 wenn noch nicht bekannt.
|
||||
*/
|
||||
private double lastKnownAspectRatio = -1.0;
|
||||
|
||||
/**
|
||||
* Erstellt die Komponente im deaktivierten Platzhalter-Zustand.
|
||||
*/
|
||||
@@ -112,7 +106,12 @@ public final class PdfPreviewPane {
|
||||
pdfView.setShowToolBar(false);
|
||||
pdfView.setId("pdf-preview-view");
|
||||
|
||||
// Nach der Skin-Installation Scrollbalken im internen ScrollPane ausblenden.
|
||||
// Scrollbalken per CSS ausblenden – gilt für den internen ScrollPane der Skin
|
||||
pdfView.setStyle(
|
||||
".scroll-bar:vertical { -fx-pref-width: 0; visibility: hidden; }" +
|
||||
".scroll-bar:horizontal { -fx-pref-height: 0; visibility: hidden; }");
|
||||
|
||||
// Nach der Skin-Installation Scrollbalken im internen ScrollPane auch per Policy deaktivieren
|
||||
pdfView.skinProperty().addListener((obs, oldSkin, newSkin) -> {
|
||||
if (newSkin != null) {
|
||||
Platform.runLater(() -> {
|
||||
@@ -143,9 +142,9 @@ public final class PdfPreviewPane {
|
||||
StackPane.setAlignment(progressIndicator, Pos.CENTER);
|
||||
VBox.setVgrow(viewStack, Priority.ALWAYS);
|
||||
|
||||
// Bei Größenänderungen der Anzeigefläche Zoom neu berechnen
|
||||
viewStack.widthProperty().addListener((obs, ov, nv) -> fitToView());
|
||||
viewStack.heightProperty().addListener((obs, ov, nv) -> fitToView());
|
||||
// Bei Größenänderungen der PDF-Ansicht Zoom neu berechnen
|
||||
pdfView.widthProperty().addListener((obs, ov, nv) -> updateZoom());
|
||||
pdfView.heightProperty().addListener((obs, ov, nv) -> updateZoom());
|
||||
|
||||
prevButton.setId("pdf-preview-prev-button");
|
||||
prevButton.setOnAction(e -> navigateToPreviousPage());
|
||||
@@ -204,7 +203,6 @@ public final class PdfPreviewPane {
|
||||
currentSourceFile = null;
|
||||
currentPage = 0;
|
||||
totalPages = -1;
|
||||
lastKnownAspectRatio = -1.0;
|
||||
// Neue Sequenznummer: laufende Requests werden verworfen
|
||||
currentRequestSequence.incrementAndGet();
|
||||
pdfView.unload();
|
||||
@@ -270,7 +268,8 @@ public final class PdfPreviewPane {
|
||||
currentPage = targetPage;
|
||||
updatePageLabel();
|
||||
updateNavigationButtons();
|
||||
fitToView();
|
||||
// Zoom nach dem Rendering der neuen Seite neu berechnen
|
||||
Platform.runLater(this::updateZoom);
|
||||
}
|
||||
|
||||
private void navigateToNextPage() {
|
||||
@@ -282,72 +281,29 @@ public final class PdfPreviewPane {
|
||||
currentPage = targetPage;
|
||||
updatePageLabel();
|
||||
updateNavigationButtons();
|
||||
fitToView();
|
||||
// Zoom nach dem Rendering der neuen Seite neu berechnen
|
||||
Platform.runLater(this::updateZoom);
|
||||
}
|
||||
|
||||
// --- Zoom-Berechnung (fit-to-view) ----------------------------------------
|
||||
|
||||
/**
|
||||
* Berechnet den optimalen Zoomfaktor anhand des bekannten Seitenverhältnisses und
|
||||
* der aktuellen Größe der Anzeigefläche und setzt ihn auf {@code pdfView}.
|
||||
* Hat keinen Effekt wenn das Seitenverhältnis noch nicht bekannt ist oder die
|
||||
* Anzeigefläche noch keine Größe hat.
|
||||
* Berechnet den optimalen Zoomfaktor anhand der aktuellen Größe von {@code pdfView}
|
||||
* und setzt ihn. Als Seitenmaß wird A4 im Hochformat (595 × 842 Punkte) als Fallback
|
||||
* verwendet. Hat keinen Effekt, solange {@code pdfView} noch keine Größe hat.
|
||||
*/
|
||||
private void fitToView() {
|
||||
if (lastKnownAspectRatio <= 0.0) {
|
||||
private void updateZoom() {
|
||||
double panelWidth = pdfView.getWidth();
|
||||
double panelHeight = pdfView.getHeight();
|
||||
if (panelWidth <= 0 || panelHeight <= 0) {
|
||||
return;
|
||||
}
|
||||
double vw = viewStack.getWidth();
|
||||
double vh = viewStack.getHeight();
|
||||
if (vw <= 0.0 || vh <= 0.0) {
|
||||
return;
|
||||
}
|
||||
PDFView.Document doc = pdfView.getDocument();
|
||||
if (doc == null || currentPage <= 0) {
|
||||
return;
|
||||
}
|
||||
// Querformat: Zoom=1 → Höhe füllt den Viewport; Breite kann überlaufen.
|
||||
// Hochformat: Zoom=1 → Breite füllt den Viewport; Höhe kann überlaufen.
|
||||
boolean landscape = doc.isLandscape(currentPage - 1);
|
||||
double zoom;
|
||||
if (!landscape) {
|
||||
zoom = Math.min(1.0, vh / (lastKnownAspectRatio * vw));
|
||||
} else {
|
||||
zoom = Math.min(1.0, vw / (lastKnownAspectRatio * vh));
|
||||
}
|
||||
pdfView.setZoomFactor(Math.max(0.05, zoom));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ermittelt das Seitenverhältnis der angegebenen Seite asynchron auf dem
|
||||
* Worker-Thread und aktualisiert danach den Zoom auf dem FX-Thread.
|
||||
* Veraltete Ergebnisse werden anhand der Sequenznummer verworfen.
|
||||
*
|
||||
* @param seq die Sequenznummer der aktuellen Lade-Anforderung
|
||||
*/
|
||||
private void fetchAspectRatioAsync(long seq) {
|
||||
PDFView.Document doc = pdfView.getDocument();
|
||||
if (doc == null) {
|
||||
return;
|
||||
}
|
||||
int pageIdx = Math.max(0, currentPage - 1);
|
||||
executor.submit(() -> {
|
||||
try {
|
||||
// Seite bei sehr kleiner Skala rendern, um das Seitenverhältnis zu ermitteln
|
||||
var img = doc.renderPage(pageIdx, 0.05f);
|
||||
if (img != null && img.getWidth() > 0) {
|
||||
double ratio = (double) img.getHeight() / img.getWidth();
|
||||
Platform.runLater(() -> {
|
||||
if (currentRequestSequence.get() == seq) {
|
||||
lastKnownAspectRatio = ratio;
|
||||
fitToView();
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.debug("PDF-Vorschau: Seitenverhältnis nicht ermittelbar: {}", e.getMessage());
|
||||
}
|
||||
});
|
||||
double pageWidth = 595; // A4 Breite in Punkten (Fallback)
|
||||
double pageHeight = 842; // A4 Höhe in Punkten (Fallback)
|
||||
double zoomByWidth = panelWidth / pageWidth;
|
||||
double zoomByHeight = panelHeight / pageHeight;
|
||||
double zoom = Math.min(zoomByWidth, zoomByHeight) * 0.95;
|
||||
pdfView.setZoomFactor(zoom);
|
||||
}
|
||||
|
||||
// --- Asynchrones Laden ---------------------------------------------------
|
||||
@@ -405,8 +361,8 @@ public final class PdfPreviewPane {
|
||||
showContent();
|
||||
updateNavigationButtons();
|
||||
updatePageLabel();
|
||||
// Seitenverhältnis asynchron ermitteln und Zoom anpassen
|
||||
fetchAspectRatioAsync(seq);
|
||||
// Zoom nach dem ersten Rendering berechnen
|
||||
Platform.runLater(this::updateZoom);
|
||||
LOG.debug("PDF-Vorschau: Rendering angestoßen – {} Seite(n)", totalPages);
|
||||
} catch (Exception e) {
|
||||
String msg = classifyLoadException(e);
|
||||
|
||||
Reference in New Issue
Block a user