diff --git a/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.scss b/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.scss index 0183f0b3b..976a3134f 100644 --- a/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.scss +++ b/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.scss @@ -212,7 +212,6 @@ $action-bar-height: 38px; height: calc((var(--vh) * 100) - calc($action-bar-height)); // * 2 &.writing-style-vertical { - height: auto; padding: 0 10px 0 0; margin: 20px 0; } @@ -222,12 +221,15 @@ $action-bar-height: 38px; height: calc((var(--vh) * 100) - calc($action-bar-height)); // * 2 &.writing-style-vertical { - height: auto; padding: 0 10px 0 0; margin: 20px 0; } } + &.writing-style-vertical { + height: auto; + } + // &.immersive { // // Note: I removed this for bug: https://github.com/Kareadita/Kavita/issues/1726 // //height: calc((var(--vh, 1vh) * 100) - $action-bar-height); @@ -301,11 +303,18 @@ $action-bar-height: 38px; ::ng-deep .kavita-scale-width-container { width: auto; max-height: calc(var(--book-reader-content-max-height) - ($action-bar-height)) !important; + max-width: calc(var(--book-reader-content-max-width)) !important; + position: var(--book-reader-content-position) !important; + top: var(--book-reader-content-top) !important; + left: var(--book-reader-content-left) !important; + transform: var(--book-reader-content-transform) !important; + } // This is applied to images in the backend ::ng-deep .kavita-scale-width { - max-width: 100%; + max-height: calc(var(--book-reader-content-max-height) - ($action-bar-height)) !important; + max-width: calc(var(--book-reader-content-max-width)) !important; object-fit: contain; object-position: top center; break-inside: avoid; diff --git a/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.ts b/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.ts index b381ce3f8..26d683c7c 100644 --- a/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.ts +++ b/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.ts @@ -122,7 +122,10 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy { * TODO: See if continuousChaptersStack can be moved into reader service so we can reduce code duplication between readers (and also use ChapterInfo with it instead) */ continuousChaptersStack: Stack = new Stack(); - + /* + * The current page only contains an image. This is used to determine if we should show the image in the center of the screen. + */ + isSingleImagePage = false; /** * Belongs to the drawer component */ @@ -153,6 +156,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy { clickToPaginateVisualOverlay = false; clickToPaginateVisualOverlayTimeout: any = undefined; // For animation clickToPaginateVisualOverlayTimeout2: any = undefined; // For kicking off animation, giving enough time to render html + updateImageSizeTimeout: any = undefined; /** * This is the html we get from the server */ @@ -372,17 +376,14 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy { if (this.layoutMode !== BookPageLayoutMode.Default || this.writingStyle === WritingStyle.Vertical) { // Take the height after page loads, subtract the top/bottom bar const height = this.windowHeight - (this.topOffset * 2); - this.document.documentElement.style.setProperty('--book-reader-content-max-height', `${height}px`); return height + 'px'; } - return 'unset'; } get VerticalBookContentWidth() { if (this.layoutMode !== BookPageLayoutMode.Default && this.writingStyle !== WritingStyle.Horizontal ) { const width = this.getVerticalPageWidth() - this.document.documentElement.style.setProperty('--book-reader-content-max-width', `${width}px`); return width + 'px'; } return ''; @@ -592,8 +593,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy { this.libraryType = type; }); - this.updateImagesWithHeight(); - + this.updateImageSizes(); if (this.pageNum >= this.maxPages) { this.pageNum = this.maxPages - 1; @@ -637,6 +637,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy { onResize(){ // Update the window Height this.updateWidthAndHeightCalcs(); + this.updateImageSizes(); const resumeElement = this.getFirstVisibleElementXPath(); if (this.layoutMode !== BookPageLayoutMode.Default && resumeElement !== null && resumeElement !== undefined) { this.scrollTo(resumeElement); // This works pretty well, but not perfect @@ -836,7 +837,11 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy { this.cdRef.markForCheck(); this.bookService.getBookPage(this.chapterId, this.pageNum).pipe(take(1)).subscribe(content => { + this.isSingleImagePage = this.checkSingleImagePage(content) // This needs be performed before we set this.page to avoid image jumping + this.updateSingleImagePageStyles() this.page = this.domSanitizer.bypassSecurityTrustHtml(content); // PERF: Potential optimization to prefetch next/prev page and store in localStorage + + this.cdRef.markForCheck(); setTimeout(() => { @@ -854,40 +859,76 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy { .map(img => new Promise(resolve => { img.onload = img.onerror = resolve; }))) .then(() => { this.setupPage(part, scrollTop); - this.updateImagesWithHeight(); + this.updateImageSizes(); }); }, 10); }); } /** - * Applies a max-height inline css property on each image in the page if the layout mode is column-based, else it removes the property + * Updates the image properties to fit the current layout mode and screen size */ - updateImagesWithHeight() { - const images = this.readingSectionElemRef?.nativeElement.querySelectorAll('img') || []; - let maxHeight: number | undefined; + updateImageSizes() { + const isVerticalWritingStyle = this.writingStyle === WritingStyle.Vertical; + const height = this.windowHeight - (this.topOffset * 2); + let maxHeight = 'unset'; + let maxWidth = ''; + switch (this.layoutMode) { + case BookPageLayoutMode.Default: + if (isVerticalWritingStyle) { + maxHeight = `${height}px`; + } else { + maxWidth = `${this.getVerticalPageWidth()}px`; + } + break - if (this.layoutMode !== BookPageLayoutMode.Default && this.writingStyle !== WritingStyle.Vertical) { - maxHeight = (parseInt(this.ColumnHeight.replace('px', ''), 10) - (this.topOffset * 2)); - } else if (this.layoutMode !== BookPageLayoutMode.Column2 && this.writingStyle === WritingStyle.Vertical) { - maxHeight = this.getPageHeight() - COLUMN_GAP; - } else if (this.layoutMode === BookPageLayoutMode.Column2 && this.writingStyle === WritingStyle.Vertical) { - maxHeight = this.getPageHeight() / 2 - COLUMN_GAP; - } else { - maxHeight = undefined; + case BookPageLayoutMode.Column1: + maxHeight = `${height}px`; + maxWidth = `${this.getVerticalPageWidth()}px`; + break + + case BookPageLayoutMode.Column2: + maxWidth = `${this.getVerticalPageWidth()}px`; + if (isVerticalWritingStyle && !this.isSingleImagePage) { + maxHeight = `${height / 2}px`; + } else { + maxHeight = `${height}px`; + } + break } - Array.from(images).forEach(img => { - if (maxHeight === undefined) { - this.renderer.removeStyle(img, 'max-height'); - } else if (this.writingStyle === WritingStyle.Horizontal) { - this.renderer.setStyle(img, 'max-height', `${maxHeight}px`); - } else { - const aspectRatio = img.width / img.height; - const pageWidth = this.getVerticalPageWidth() - const maxImgHeight = Math.min(maxHeight, pageWidth / aspectRatio); - this.renderer.setStyle(img, 'max-height', `${maxImgHeight}px`); - } - }); + this.document.documentElement.style.setProperty('--book-reader-content-max-height', maxHeight); + this.document.documentElement.style.setProperty('--book-reader-content-max-width', maxWidth); + + } + + updateSingleImagePageStyles() { + if (this.isSingleImagePage && this.layoutMode !== BookPageLayoutMode.Default) { + this.document.documentElement.style.setProperty('--book-reader-content-position', 'absolute'); + this.document.documentElement.style.setProperty('--book-reader-content-top', '50%'); + this.document.documentElement.style.setProperty('--book-reader-content-left', '50%'); + this.document.documentElement.style.setProperty('--book-reader-content-transform', 'translate(-50%, -50%)'); + } else { + this.document.documentElement.style.setProperty('--book-reader-content-position', ''); + this.document.documentElement.style.setProperty('--book-reader-content-top', ''); + this.document.documentElement.style.setProperty('--book-reader-content-left', ''); + this.document.documentElement.style.setProperty('--book-reader-content-transform', ''); + } + } + + checkSingleImagePage(content: string) { + // Exclude the style element from the HTML content as it messes up innerText + const htmlContent = content.replace(/