mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	Fix: render images not converted to pdf, refactor doc detail rendering (#5475)
This commit is contained in:
		| @@ -1,5 +1,5 @@ | |||||||
| <pngx-page-header [(title)]="title"> | <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 input-group-sm me-2 d-none d-md-flex"> | ||||||
|       <div class="input-group-text" i18n>Page</div> |       <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" /> |       <input class="form-control flex-grow-0 w-auto" type="number" min="1" [max]="previewNumPages" [(ngModel)]="previewCurrentPage" /> | ||||||
| @@ -106,7 +106,7 @@ | |||||||
|               @for (fieldInstance of document?.custom_fields; track fieldInstance; let i = $index) { |               @for (fieldInstance of document?.custom_fields; track fieldInstance; let i = $index) { | ||||||
|                 <div [formGroup]="customFieldFormFields.controls[i]"> |                 <div [formGroup]="customFieldFormFields.controls[i]"> | ||||||
|                   @switch (getCustomFieldFromInstance(fieldInstance)?.data_type) { |                   @switch (getCustomFieldFromInstance(fieldInstance)?.data_type) { | ||||||
|                     @case (PaperlessCustomFieldDataType.String) { |                     @case (CustomFieldDataType.String) { | ||||||
|                       <pngx-input-text formControlName="value" |                       <pngx-input-text formControlName="value" | ||||||
|                       [title]="getCustomFieldFromInstance(fieldInstance)?.name" |                       [title]="getCustomFieldFromInstance(fieldInstance)?.name" | ||||||
|                       [removable]="userIsOwner" |                       [removable]="userIsOwner" | ||||||
| @@ -114,7 +114,7 @@ | |||||||
|                       [horizontal]="true" |                       [horizontal]="true" | ||||||
|                       [error]="getCustomFieldError(i)"></pngx-input-text> |                       [error]="getCustomFieldError(i)"></pngx-input-text> | ||||||
|                     } |                     } | ||||||
|                     @case (PaperlessCustomFieldDataType.Date) { |                     @case (CustomFieldDataType.Date) { | ||||||
|                       <pngx-input-date formControlName="value" |                       <pngx-input-date formControlName="value" | ||||||
|                       [title]="getCustomFieldFromInstance(fieldInstance)?.name" |                       [title]="getCustomFieldFromInstance(fieldInstance)?.name" | ||||||
|                       [removable]="userIsOwner" |                       [removable]="userIsOwner" | ||||||
| @@ -122,7 +122,7 @@ | |||||||
|                       [horizontal]="true" |                       [horizontal]="true" | ||||||
|                       [error]="getCustomFieldError(i)"></pngx-input-date> |                       [error]="getCustomFieldError(i)"></pngx-input-date> | ||||||
|                     } |                     } | ||||||
|                     @case (PaperlessCustomFieldDataType.Integer) { |                     @case (CustomFieldDataType.Integer) { | ||||||
|                       <pngx-input-number formControlName="value" |                       <pngx-input-number formControlName="value" | ||||||
|                       [title]="getCustomFieldFromInstance(fieldInstance)?.name" |                       [title]="getCustomFieldFromInstance(fieldInstance)?.name" | ||||||
|                       [removable]="userIsOwner" |                       [removable]="userIsOwner" | ||||||
| @@ -131,7 +131,7 @@ | |||||||
|                       [showAdd]="false" |                       [showAdd]="false" | ||||||
|                       [error]="getCustomFieldError(i)"></pngx-input-number> |                       [error]="getCustomFieldError(i)"></pngx-input-number> | ||||||
|                     } |                     } | ||||||
|                     @case (PaperlessCustomFieldDataType.Float) { |                     @case (CustomFieldDataType.Float) { | ||||||
|                       <pngx-input-number formControlName="value" |                       <pngx-input-number formControlName="value" | ||||||
|                       [title]="getCustomFieldFromInstance(fieldInstance)?.name" |                       [title]="getCustomFieldFromInstance(fieldInstance)?.name" | ||||||
|                       [removable]="userIsOwner" |                       [removable]="userIsOwner" | ||||||
| @@ -141,7 +141,7 @@ | |||||||
|                       [step]=".1" |                       [step]=".1" | ||||||
|                       [error]="getCustomFieldError(i)"></pngx-input-number> |                       [error]="getCustomFieldError(i)"></pngx-input-number> | ||||||
|                     } |                     } | ||||||
|                     @case (PaperlessCustomFieldDataType.Monetary) { |                     @case (CustomFieldDataType.Monetary) { | ||||||
|                       <pngx-input-number formControlName="value" |                       <pngx-input-number formControlName="value" | ||||||
|                       [title]="getCustomFieldFromInstance(fieldInstance)?.name" |                       [title]="getCustomFieldFromInstance(fieldInstance)?.name" | ||||||
|                       [removable]="userIsOwner" |                       [removable]="userIsOwner" | ||||||
| @@ -151,14 +151,14 @@ | |||||||
|                       [step]=".01" |                       [step]=".01" | ||||||
|                       [error]="getCustomFieldError(i)"></pngx-input-number> |                       [error]="getCustomFieldError(i)"></pngx-input-number> | ||||||
|                     } |                     } | ||||||
|                     @case (PaperlessCustomFieldDataType.Boolean) { |                     @case (CustomFieldDataType.Boolean) { | ||||||
|                       <pngx-input-check formControlName="value" |                       <pngx-input-check formControlName="value" | ||||||
|                       [title]="getCustomFieldFromInstance(fieldInstance)?.name" |                       [title]="getCustomFieldFromInstance(fieldInstance)?.name" | ||||||
|                       [removable]="userIsOwner" |                       [removable]="userIsOwner" | ||||||
|                       (removed)="removeField(fieldInstance)" |                       (removed)="removeField(fieldInstance)" | ||||||
|                       [horizontal]="true"></pngx-input-check> |                       [horizontal]="true"></pngx-input-check> | ||||||
|                     } |                     } | ||||||
|                     @case (PaperlessCustomFieldDataType.Url) { |                     @case (CustomFieldDataType.Url) { | ||||||
|                       <pngx-input-url formControlName="value" |                       <pngx-input-url formControlName="value" | ||||||
|                       [title]="getCustomFieldFromInstance(fieldInstance)?.name" |                       [title]="getCustomFieldFromInstance(fieldInstance)?.name" | ||||||
|                       [removable]="userIsOwner" |                       [removable]="userIsOwner" | ||||||
| @@ -166,7 +166,7 @@ | |||||||
|                       [horizontal]="true" |                       [horizontal]="true" | ||||||
|                       [error]="getCustomFieldError(i)"></pngx-input-url> |                       [error]="getCustomFieldError(i)"></pngx-input-url> | ||||||
|                     } |                     } | ||||||
|                     @case (PaperlessCustomFieldDataType.DocumentLink) { |                     @case (CustomFieldDataType.DocumentLink) { | ||||||
|                       <pngx-input-document-link formControlName="value" |                       <pngx-input-document-link formControlName="value" | ||||||
|                       [title]="getCustomFieldFromInstance(fieldInstance)?.name" |                       [title]="getCustomFieldFromInstance(fieldInstance)?.name" | ||||||
|                       [parentDocumentID]="documentId" |                       [parentDocumentID]="documentId" | ||||||
| @@ -260,7 +260,9 @@ | |||||||
|           <a ngbNavLink i18n>Preview</a> |           <a ngbNavLink i18n>Preview</a> | ||||||
|           @if (!pdfPreview.offsetParent) { |           @if (!pdfPreview.offsetParent) { | ||||||
|             <ng-template ngbNavContent> |             <ng-template ngbNavContent> | ||||||
|  |               <div class="position-relative"> | ||||||
|                 <ng-container *ngTemplateOutlet="previewContent"></ng-container> |                 <ng-container *ngTemplateOutlet="previewContent"></ng-container> | ||||||
|  |               </div> | ||||||
|             </ng-template> |             </ng-template> | ||||||
|           } |           } | ||||||
|         </li> |         </li> | ||||||
| @@ -295,16 +297,6 @@ | |||||||
|  |  | ||||||
|   <div class="col-md-6 col-xl-8 mb-3 d-none d-md-block position-relative" #pdfPreview> |   <div class="col-md-6 col-xl-8 mb-3 d-none d-md-block position-relative" #pdfPreview> | ||||||
|     <ng-container *ngTemplateOutlet="previewContent"></ng-container> |     <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> | ||||||
| @@ -333,8 +325,9 @@ | |||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   } |   } | ||||||
|   @if (getContentType() === 'application/pdf') { |   @switch (contentRenderType) { | ||||||
|     @if (!useNativePdfViewer ) { |     @case (ContentRenderType.PDF) { | ||||||
|  |       @if (!useNativePdfViewer) { | ||||||
|         <div class="preview-sticky pdf-viewer-container"> |         <div class="preview-sticky pdf-viewer-container"> | ||||||
|           <pngx-pdf-viewer |           <pngx-pdf-viewer | ||||||
|             [src]="{ url: previewUrl, password: password }" |             [src]="{ url: previewUrl, password: password }" | ||||||
| @@ -352,10 +345,19 @@ | |||||||
|         <object [data]="previewUrl | safeUrl" class="preview-sticky" width="100%"></object> |         <object [data]="previewUrl | safeUrl" class="preview-sticky" width="100%"></object> | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   @if (renderAsPlainText) { |     @case (ContentRenderType.Text) { | ||||||
|     <div [innerText]="previewText" class="preview-sticky bg-light p-3 overflow-auto" width="100%"></div> |       <div class="preview-sticky bg-light p-3 overflow-auto" width="100%">{{previewText}}</div> | ||||||
|     } |     } | ||||||
|   @if (showPasswordField) { |     @case (ContentRenderType.Image) { | ||||||
|  |       <div class="preview-sticky"> | ||||||
|  |         <img [src]="previewUrl | safeUrl" width="100%" height="100%" alt="{{title}}" /> | ||||||
|  |       </div> | ||||||
|  |     } | ||||||
|  |     @case (ContentRenderType.Other) { | ||||||
|  |       <object [data]="previewUrl | safeUrl" class="preview-sticky" width="100%"></object> | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   @if (requiresPassword) { | ||||||
|     <div class="password-prompt"> |     <div class="password-prompt"> | ||||||
|       <form> |       <form> | ||||||
|         <input autocomplete="" autofocus="true" class="form-control" i18n-placeholder placeholder="Enter Password" type="password" (keyup)="onPasswordKeyUp($event)" /> |         <input autocomplete="" autofocus="true" class="form-control" i18n-placeholder placeholder="Enter Password" type="password" (keyup)="onPasswordKeyUp($event)" /> | ||||||
|   | |||||||
| @@ -58,3 +58,9 @@ textarea.rtl { | |||||||
|   border-top-right-radius: var(--bs-border-radius-sm); |   border-top-right-radius: var(--bs-border-radius-sm); | ||||||
|   border-bottom-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; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -996,6 +996,53 @@ describe('DocumentDetailComponent', () => { | |||||||
|     expect(closeSpy).toHaveBeenCalled() |     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() { |   function initNormally() { | ||||||
|     jest |     jest | ||||||
|       .spyOn(activatedRoute, 'paramMap', 'get') |       .spyOn(activatedRoute, 'paramMap', 'get') | ||||||
|   | |||||||
| @@ -77,6 +77,13 @@ enum DocumentDetailNavIDs { | |||||||
|   Permissions = 6, |   Permissions = 6, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | enum ContentRenderType { | ||||||
|  |   PDF = 'pdf', | ||||||
|  |   Image = 'image', | ||||||
|  |   Text = 'text', | ||||||
|  |   Other = 'other', | ||||||
|  | } | ||||||
|  |  | ||||||
| enum ZoomSetting { | enum ZoomSetting { | ||||||
|   PageFit = 'page-fit', |   PageFit = 'page-fit', | ||||||
|   PageWidth = 'page-width', |   PageWidth = 'page-width', | ||||||
| @@ -154,7 +161,9 @@ export class DocumentDetailComponent | |||||||
|   ogDate: Date |   ogDate: Date | ||||||
|  |  | ||||||
|   customFields: CustomField[] |   customFields: CustomField[] | ||||||
|   public readonly PaperlessCustomFieldDataType = CustomFieldDataType |   public readonly CustomFieldDataType = CustomFieldDataType | ||||||
|  |  | ||||||
|  |   public readonly ContentRenderType = ContentRenderType | ||||||
|  |  | ||||||
|   @ViewChild('nav') nav: NgbNav |   @ViewChild('nav') nav: NgbNav | ||||||
|   @ViewChild('pdfPreview') set pdfPreview(element) { |   @ViewChild('pdfPreview') set pdfPreview(element) { | ||||||
| @@ -201,16 +210,21 @@ export class DocumentDetailComponent | |||||||
|     return this.settings.get(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER) |     return this.settings.get(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   getContentType() { |   get contentRenderType(): ContentRenderType { | ||||||
|     return this.metadata?.has_archive_version |     const contentType = this.metadata?.has_archive_version | ||||||
|       ? 'application/pdf' |       ? 'application/pdf' | ||||||
|       : this.metadata?.original_mime_type |       : this.metadata?.original_mime_type | ||||||
|   } |  | ||||||
|  |  | ||||||
|   get renderAsPlainText(): boolean { |     if (contentType === 'application/pdf') { | ||||||
|     return ['text/plain', 'application/csv', 'text/csv'].includes( |       return ContentRenderType.PDF | ||||||
|       this.getContentType() |     } 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() { |   get isRTL() { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 shamoon
					shamoon