diff --git a/src-ui/package-lock.json b/src-ui/package-lock.json
index 4aa06ddf0..0b1a7c68f 100644
--- a/src-ui/package-lock.json
+++ b/src-ui/package-lock.json
@@ -33,6 +33,7 @@
"ngx-ui-tour-ng-bootstrap": "^15.0.0",
"rxjs": "^7.8.1",
"tslib": "^2.7.0",
+ "utif": "^3.1.0",
"uuid": "^10.0.0",
"zone.js": "^0.14.8"
},
@@ -15497,6 +15498,12 @@
"node": "^16.14.0 || >=18.0.0"
}
},
+ "node_modules/pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+ "license": "(MIT AND Zlib)"
+ },
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -18390,6 +18397,15 @@
"requires-port": "^1.0.0"
}
},
+ "node_modules/utif": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/utif/-/utif-3.1.0.tgz",
+ "integrity": "sha512-WEo4D/xOvFW53K5f5QTaTbbiORcm2/pCL9P6qmJnup+17eYfKaEhDeX9PeQkuyEoIxlbGklDuGl8xwuXYMrrXQ==",
+ "license": "MIT",
+ "dependencies": {
+ "pako": "^1.0.5"
+ }
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
diff --git a/src-ui/package.json b/src-ui/package.json
index f46f06f64..e9a200110 100644
--- a/src-ui/package.json
+++ b/src-ui/package.json
@@ -35,6 +35,7 @@
"ngx-ui-tour-ng-bootstrap": "^15.0.0",
"rxjs": "^7.8.1",
"tslib": "^2.7.0",
+ "utif": "^3.1.0",
"uuid": "^10.0.0",
"zone.js": "^0.14.8"
},
diff --git a/src-ui/src/app/components/document-detail/document-detail.component.html b/src-ui/src/app/components/document-detail/document-detail.component.html
index 25a512463..609a3d000 100644
--- a/src-ui/src/app/components/document-detail/document-detail.component.html
+++ b/src-ui/src/app/components/document-detail/document-detail.component.html
@@ -386,6 +386,15 @@
}
+ @case (ContentRenderType.TIFF) {
+ @if (!tiffError) {
+
+
![{{title}}]()
+
+ } @else {
+ {{tiffError}}
+ }
+ }
@case (ContentRenderType.Other) {
}
diff --git a/src-ui/src/app/components/document-detail/document-detail.component.scss b/src-ui/src/app/components/document-detail/document-detail.component.scss
index c6e3b7448..8a17ca8c6 100644
--- a/src-ui/src/app/components/document-detail/document-detail.component.scss
+++ b/src-ui/src/app/components/document-detail/document-detail.component.scss
@@ -61,6 +61,7 @@ textarea.rtl {
width: 100%;
height: 100%;
object-fit: contain;
+ object-position: top;
}
.whitespace-preserve {
diff --git a/src-ui/src/app/components/document-detail/document-detail.component.spec.ts b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts
index 24ef2ffad..7a50a62ef 100644
--- a/src-ui/src/app/components/document-detail/document-detail.component.spec.ts
+++ b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts
@@ -1262,4 +1262,43 @@ describe('DocumentDetailComponent', () => {
expect(component.createDisabled(DataType.StoragePath)).toBeFalsy()
expect(component.createDisabled(DataType.Tag)).toBeFalsy()
})
+
+ it('should call tryRenderTiff when no archive and file is tiff', () => {
+ initNormally()
+ const tiffRenderSpy = jest.spyOn(
+ DocumentDetailComponent.prototype as any,
+ 'tryRenderTiff'
+ )
+ jest
+ .spyOn(documentService, 'getMetadata')
+ .mockReturnValue(
+ of({ has_archive_version: false, original_mime_type: 'image/tiff' })
+ )
+ component.updateComponent(doc)
+ fixture.detectChanges()
+ expect(component.archiveContentRenderType).toEqual(
+ component.ContentRenderType.TIFF
+ )
+ expect(tiffRenderSpy).toHaveBeenCalled()
+ })
+
+ it('should try to render tiff and show error if failed', () => {
+ initNormally()
+ // just the text request
+ httpTestingController.expectOne(component.previewUrl)
+
+ // invalid tiff
+ component['tryRenderTiff']()
+ httpTestingController
+ .expectOne(component.previewUrl)
+ .flush(new ArrayBuffer(100)) // arraybuffer
+ expect(component.tiffError).not.toBeUndefined()
+
+ // http error
+ component['tryRenderTiff']()
+ httpTestingController
+ .expectOne(component.previewUrl)
+ .error(new ErrorEvent('failed'))
+ expect(component.tiffError).not.toBeUndefined()
+ })
})
diff --git a/src-ui/src/app/components/document-detail/document-detail.component.ts b/src-ui/src/app/components/document-detail/document-detail.component.ts
index 6cd26b368..07d60b4f7 100644
--- a/src-ui/src/app/components/document-detail/document-detail.component.ts
+++ b/src-ui/src/app/components/document-detail/document-detail.component.ts
@@ -72,6 +72,7 @@ import { DeletePagesConfirmDialogComponent } from '../common/confirm-dialog/dele
import { HotKeyService } from 'src/app/services/hot-key.service'
import { PDFDocumentProxy } from 'ng2-pdf-viewer'
import { DataType } from 'src/app/data/datatype'
+import * as UTIF from 'utif'
enum DocumentDetailNavIDs {
Details = 1,
@@ -89,6 +90,7 @@ enum ContentRenderType {
Text = 'text',
Other = 'other',
Unknown = 'unknown',
+ TIFF = 'tiff',
}
enum ZoomSetting {
@@ -134,6 +136,8 @@ export class DocumentDetailComponent
previewText: string
downloadUrl: string
downloadOriginalUrl: string
+ tiffURL: string
+ tiffError: string
correspondents: Correspondent[]
documentTypes: DocumentType[]
@@ -240,6 +244,8 @@ export class DocumentDetailComponent
['text/plain', 'application/csv', 'text/csv'].includes(mimeType)
) {
return ContentRenderType.Text
+ } else if (mimeType.indexOf('tiff') >= 0) {
+ return ContentRenderType.TIFF
} else if (mimeType?.indexOf('image/') === 0) {
return ContentRenderType.Image
}
@@ -537,6 +543,9 @@ export class DocumentDetailComponent
.subscribe({
next: (result) => {
this.metadata = result
+ if (this.archiveContentRenderType === ContentRenderType.TIFF) {
+ this.tryRenderTiff()
+ }
},
error: (error) => {
this.metadata = {} // allow display to fallback to