]> git.ipfire.org Git - thirdparty/paperless-ngx.git/commitdiff
Fix: render images not converted to pdf, refactor doc detail rendering (#5475)
authorshamoon <4887959+shamoon@users.noreply.github.com>
Sat, 20 Jan 2024 16:26:24 +0000 (08:26 -0800)
committerGitHub <noreply@github.com>
Sat, 20 Jan 2024 16:26:24 +0000 (08:26 -0800)
src-ui/src/app/components/document-detail/document-detail.component.html
src-ui/src/app/components/document-detail/document-detail.component.scss
src-ui/src/app/components/document-detail/document-detail.component.spec.ts
src-ui/src/app/components/document-detail/document-detail.component.ts

index c44a202d00269306255d108abefa95aca0f33c89..038a44dbe3616d34e32f707f320bb419a472af80 100644 (file)
@@ -1,5 +1,5 @@
 <pngx-page-header [(title)]="title">
-  @if (getContentType() === 'application/pdf' && !useNativePdfViewer) {
+  @if (contentRenderType === ContentRenderType.PDF && !useNativePdfViewer) {
     <div class="input-group input-group-sm me-2 d-none d-md-flex">
       <div class="input-group-text" i18n>Page</div>
       <input class="form-control flex-grow-0 w-auto" type="number" min="1" [max]="previewNumPages" [(ngModel)]="previewCurrentPage" />
               @for (fieldInstance of document?.custom_fields; track fieldInstance; let i = $index) {
                 <div [formGroup]="customFieldFormFields.controls[i]">
                   @switch (getCustomFieldFromInstance(fieldInstance)?.data_type) {
-                    @case (PaperlessCustomFieldDataType.String) {
+                    @case (CustomFieldDataType.String) {
                       <pngx-input-text formControlName="value"
                       [title]="getCustomFieldFromInstance(fieldInstance)?.name"
                       [removable]="userIsOwner"
                       [horizontal]="true"
                       [error]="getCustomFieldError(i)"></pngx-input-text>
                     }
-                    @case (PaperlessCustomFieldDataType.Date) {
+                    @case (CustomFieldDataType.Date) {
                       <pngx-input-date formControlName="value"
                       [title]="getCustomFieldFromInstance(fieldInstance)?.name"
                       [removable]="userIsOwner"
                       [horizontal]="true"
                       [error]="getCustomFieldError(i)"></pngx-input-date>
                     }
-                    @case (PaperlessCustomFieldDataType.Integer) {
+                    @case (CustomFieldDataType.Integer) {
                       <pngx-input-number formControlName="value"
                       [title]="getCustomFieldFromInstance(fieldInstance)?.name"
                       [removable]="userIsOwner"
                       [showAdd]="false"
                       [error]="getCustomFieldError(i)"></pngx-input-number>
                     }
-                    @case (PaperlessCustomFieldDataType.Float) {
+                    @case (CustomFieldDataType.Float) {
                       <pngx-input-number formControlName="value"
                       [title]="getCustomFieldFromInstance(fieldInstance)?.name"
                       [removable]="userIsOwner"
                       [step]=".1"
                       [error]="getCustomFieldError(i)"></pngx-input-number>
                     }
-                    @case (PaperlessCustomFieldDataType.Monetary) {
+                    @case (CustomFieldDataType.Monetary) {
                       <pngx-input-number formControlName="value"
                       [title]="getCustomFieldFromInstance(fieldInstance)?.name"
                       [removable]="userIsOwner"
                       [step]=".01"
                       [error]="getCustomFieldError(i)"></pngx-input-number>
                     }
-                    @case (PaperlessCustomFieldDataType.Boolean) {
+                    @case (CustomFieldDataType.Boolean) {
                       <pngx-input-check formControlName="value"
                       [title]="getCustomFieldFromInstance(fieldInstance)?.name"
                       [removable]="userIsOwner"
                       (removed)="removeField(fieldInstance)"
                       [horizontal]="true"></pngx-input-check>
                     }
-                    @case (PaperlessCustomFieldDataType.Url) {
+                    @case (CustomFieldDataType.Url) {
                       <pngx-input-url formControlName="value"
                       [title]="getCustomFieldFromInstance(fieldInstance)?.name"
                       [removable]="userIsOwner"
                       [horizontal]="true"
                       [error]="getCustomFieldError(i)"></pngx-input-url>
                     }
-                    @case (PaperlessCustomFieldDataType.DocumentLink) {
+                    @case (CustomFieldDataType.DocumentLink) {
                       <pngx-input-document-link formControlName="value"
                       [title]="getCustomFieldFromInstance(fieldInstance)?.name"
                       [parentDocumentID]="documentId"
           <a ngbNavLink i18n>Preview</a>
           @if (!pdfPreview.offsetParent) {
             <ng-template ngbNavContent>
-              <ng-container *ngTemplateOutlet="previewContent"></ng-container>
+              <div class="position-relative">
+                <ng-container *ngTemplateOutlet="previewContent"></ng-container>
+              </div>
             </ng-template>
           }
         </li>
 
   <div class="col-md-6 col-xl-8 mb-3 d-none d-md-block position-relative" #pdfPreview>
     <ng-container *ngTemplateOutlet="previewContent"></ng-container>
-    @if (renderAsPlainText) {
-      <div [innerText]="previewText" class="preview-sticky bg-light p-3 overflow-auto" width="100%"></div>
-    }
-    @if (requiresPassword) {
-      <div class="password-prompt">
-        <form>
-          <input autocomplete="" class="form-control" i18n-placeholder placeholder="Enter Password" type="password" (keyup)="onPasswordKeyUp($event)" />
-        </form>
-      </div>
-    }
   </div>
 
 </div>
       </div>
     </div>
   }
-  @if (getContentType() === 'application/pdf') {
-    @if (!useNativePdfViewer ) {
-      <div class="preview-sticky pdf-viewer-container">
-        <pngx-pdf-viewer
-          [src]="{ url: previewUrl, password: password }"
-          [original-size]="false"
-          [show-borders]="true"
-          [show-all]="true"
-          [(page)]="previewCurrentPage"
-          [zoom-scale]="previewZoomScale"
-          [zoom]="previewZoomSetting"
-          (error)="onError($event)"
-          (after-load-complete)="pdfPreviewLoaded($event)">
-        </pngx-pdf-viewer>
+  @switch (contentRenderType) {
+    @case (ContentRenderType.PDF) {
+      @if (!useNativePdfViewer) {
+        <div class="preview-sticky pdf-viewer-container">
+          <pngx-pdf-viewer
+            [src]="{ url: previewUrl, password: password }"
+            [original-size]="false"
+            [show-borders]="true"
+            [show-all]="true"
+            [(page)]="previewCurrentPage"
+            [zoom-scale]="previewZoomScale"
+            [zoom]="previewZoomSetting"
+            (error)="onError($event)"
+            (after-load-complete)="pdfPreviewLoaded($event)">
+          </pngx-pdf-viewer>
+        </div>
+      } @else {
+        <object [data]="previewUrl | safeUrl" class="preview-sticky" width="100%"></object>
+      }
+    }
+    @case (ContentRenderType.Text) {
+      <div class="preview-sticky bg-light p-3 overflow-auto" width="100%">{{previewText}}</div>
+    }
+    @case (ContentRenderType.Image) {
+      <div class="preview-sticky">
+        <img [src]="previewUrl | safeUrl" width="100%" height="100%" alt="{{title}}" />
       </div>
-    } @else {
+    }
+    @case (ContentRenderType.Other) {
       <object [data]="previewUrl | safeUrl" class="preview-sticky" width="100%"></object>
     }
   }
-  @if (renderAsPlainText) {
-    <div [innerText]="previewText" class="preview-sticky bg-light p-3 overflow-auto" width="100%"></div>
-  }
-  @if (showPasswordField) {
+  @if (requiresPassword) {
     <div class="password-prompt">
       <form>
         <input autocomplete="" autofocus="true" class="form-control" i18n-placeholder placeholder="Enter Password" type="password" (keyup)="onPasswordKeyUp($event)" />
index 026969339aee1fa7727043c6b9ba36117463170f..a7871cc9efedd1003ed702a7205a0c055c635da4 100644 (file)
@@ -58,3 +58,9 @@ textarea.rtl {
   border-top-right-radius: var(--bs-border-radius-sm);
   border-bottom-right-radius: var(--bs-border-radius-sm);
 }
+
+.preview-sticky img {
+  width: 100%;
+  height: 100%;
+  object-fit: contain;
+}
index 2399c88c61c10eb297525471074e18db3dc8df95..cccf2677b40605e9e9dd818ab69eeb143e0f4a39 100644 (file)
@@ -996,6 +996,53 @@ describe('DocumentDetailComponent', () => {
     expect(closeSpy).toHaveBeenCalled()
   })
 
+  it('should change preview element by render type', () => {
+    component.metadata = { has_archive_version: true }
+    initNormally()
+    fixture.detectChanges()
+    expect(component.contentRenderType).toEqual(component.ContentRenderType.PDF)
+    expect(
+      fixture.debugElement.query(By.css('pdf-viewer-container'))
+    ).not.toBeUndefined()
+
+    component.metadata = {
+      has_archive_version: false,
+      original_mime_type: 'text/plain',
+    }
+    fixture.detectChanges()
+    expect(component.contentRenderType).toEqual(
+      component.ContentRenderType.Text
+    )
+    expect(
+      fixture.debugElement.query(By.css('div.preview-sticky'))
+    ).not.toBeUndefined()
+
+    component.metadata = {
+      has_archive_version: false,
+      original_mime_type: 'image/jpg',
+    }
+    fixture.detectChanges()
+    expect(component.contentRenderType).toEqual(
+      component.ContentRenderType.Image
+    )
+    expect(
+      fixture.debugElement.query(By.css('.preview-sticky img'))
+    ).not.toBeUndefined()
+
+    component.metadata = {
+      has_archive_version: false,
+      original_mime_type:
+        'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+    }
+    fixture.detectChanges()
+    expect(component.contentRenderType).toEqual(
+      component.ContentRenderType.Other
+    )
+    expect(
+      fixture.debugElement.query(By.css('object.preview-sticky'))
+    ).not.toBeUndefined()
+  })
+
   function initNormally() {
     jest
       .spyOn(activatedRoute, 'paramMap', 'get')
index 798e867bf5dec7dad202ea173f0bc3704eb6c8bf..18e6b12117e33df005acbaa239ff498fdbe6c0cb 100644 (file)
@@ -77,6 +77,13 @@ enum DocumentDetailNavIDs {
   Permissions = 6,
 }
 
+enum ContentRenderType {
+  PDF = 'pdf',
+  Image = 'image',
+  Text = 'text',
+  Other = 'other',
+}
+
 enum ZoomSetting {
   PageFit = 'page-fit',
   PageWidth = 'page-width',
@@ -154,7 +161,9 @@ export class DocumentDetailComponent
   ogDate: Date
 
   customFields: CustomField[]
-  public readonly PaperlessCustomFieldDataType = CustomFieldDataType
+  public readonly CustomFieldDataType = CustomFieldDataType
+
+  public readonly ContentRenderType = ContentRenderType
 
   @ViewChild('nav') nav: NgbNav
   @ViewChild('pdfPreview') set pdfPreview(element) {
@@ -201,16 +210,21 @@ export class DocumentDetailComponent
     return this.settings.get(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER)
   }
 
-  getContentType() {
-    return this.metadata?.has_archive_version
+  get contentRenderType(): ContentRenderType {
+    const contentType = this.metadata?.has_archive_version
       ? 'application/pdf'
       : this.metadata?.original_mime_type
-  }
 
-  get renderAsPlainText(): boolean {
-    return ['text/plain', 'application/csv', 'text/csv'].includes(
-      this.getContentType()
-    )
+    if (contentType === 'application/pdf') {
+      return ContentRenderType.PDF
+    } else if (
+      ['text/plain', 'application/csv', 'text/csv'].includes(contentType)
+    ) {
+      return ContentRenderType.Text
+    } else if (contentType?.indexOf('image/') === 0) {
+      return ContentRenderType.Image
+    }
+    return ContentRenderType.Other
   }
 
   get isRTL() {