mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-07-28 18:24:38 -05:00
Enhancement: better TIFF display support (#8087)
This commit is contained in:
@@ -388,6 +388,15 @@
|
||||
<img [src]="previewUrl | safeUrl" width="100%" height="100%" alt="{{title}}" />
|
||||
</div>
|
||||
}
|
||||
@case (ContentRenderType.TIFF) {
|
||||
@if (!tiffError) {
|
||||
<div class="preview-sticky">
|
||||
<img [src]="tiffURL" width="100%" height="100%" alt="{{title}}" />
|
||||
</div>
|
||||
} @else {
|
||||
<div class="preview-sticky bg-light p-3 overflow-auto whitespace-preserve" width="100%">{{tiffError}}</div>
|
||||
}
|
||||
}
|
||||
@case (ContentRenderType.Other) {
|
||||
<object [data]="previewUrl | safeUrl" class="preview-sticky" width="100%"></object>
|
||||
}
|
||||
|
@@ -61,6 +61,7 @@ textarea.rtl {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
object-position: top;
|
||||
}
|
||||
|
||||
.thumb-preview {
|
||||
|
@@ -1270,4 +1270,46 @@ 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'
|
||||
)
|
||||
const doc = Object.assign({}, component.document)
|
||||
doc.archived_file_name = null
|
||||
doc.mime_type = 'image/tiff'
|
||||
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()
|
||||
})
|
||||
})
|
||||
|
@@ -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 {
|
||||
@@ -136,6 +138,8 @@ export class DocumentDetailComponent
|
||||
downloadUrl: string
|
||||
downloadOriginalUrl: string
|
||||
previewLoaded: boolean = false
|
||||
tiffURL: string
|
||||
tiffError: string
|
||||
|
||||
correspondents: Correspondent[]
|
||||
documentTypes: DocumentType[]
|
||||
@@ -244,6 +248,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
|
||||
}
|
||||
@@ -542,6 +548,9 @@ export class DocumentDetailComponent
|
||||
this.document = doc
|
||||
this.requiresPassword = false
|
||||
this.updateFormForCustomFields()
|
||||
if (this.archiveContentRenderType === ContentRenderType.TIFF) {
|
||||
this.tryRenderTiff()
|
||||
}
|
||||
this.documentsService
|
||||
.getMetadata(doc.id)
|
||||
.pipe(
|
||||
@@ -1278,4 +1287,45 @@ export class DocumentDetailComponent
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
private tryRenderTiff() {
|
||||
this.http.get(this.previewUrl, { responseType: 'arraybuffer' }).subscribe({
|
||||
next: (res) => {
|
||||
/* istanbul ignore next */
|
||||
try {
|
||||
// See UTIF.js > _imgLoaded
|
||||
const tiffIfds: any[] = UTIF.decode(res)
|
||||
var vsns = tiffIfds,
|
||||
ma = 0,
|
||||
page = vsns[0]
|
||||
if (tiffIfds[0].subIFD) vsns = vsns.concat(tiffIfds[0].subIFD)
|
||||
for (var i = 0; i < vsns.length; i++) {
|
||||
var img = vsns[i]
|
||||
if (img['t258'] == null || img['t258'].length < 3) continue
|
||||
var ar = img['t256'] * img['t257']
|
||||
if (ar > ma) {
|
||||
ma = ar
|
||||
page = img
|
||||
}
|
||||
}
|
||||
UTIF.decodeImage(res, page, tiffIfds)
|
||||
const rgba = UTIF.toRGBA8(page)
|
||||
const { width: w, height: h } = page
|
||||
var cnv = document.createElement('canvas')
|
||||
cnv.width = w
|
||||
cnv.height = h
|
||||
var ctx = cnv.getContext('2d'),
|
||||
imgd = ctx.createImageData(w, h)
|
||||
for (var i = 0; i < rgba.length; i++) imgd.data[i] = rgba[i]
|
||||
ctx.putImageData(imgd, 0, 0)
|
||||
this.tiffURL = cnv.toDataURL()
|
||||
} catch (err) {
|
||||
this.tiffError = $localize`An error occurred loading tiff: ${err.toString()}`
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
this.tiffError = $localize`An error occurred loading tiff: ${err.toString()}`
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user