Merge remote-tracking branch 'upstream/dev' into feature-mailActionCustomTag

This commit is contained in:
Oliver Rümpelein 2022-05-29 13:51:54 +02:00
commit 708638b97f
28 changed files with 364 additions and 322 deletions

View File

@ -424,14 +424,23 @@ PAPERLESS_OCR_IMAGE_DPI=<num>
the produced PDF documents are A4 sized.
PAPERLESS_OCR_MAX_IMAGE_PIXELS=<num>
Paperless will not OCR images that have more pixels than this limit.
This is intended to prevent decompression bombs from overloading paperless.
Increasing this limit is desired if you face a DecompressionBombError despite
the concerning file not being malicious; this could e.g. be caused by invalidly
recognized metadata.
If you have enough resources or if you are certain that your uploaded files
are not malicious you can increase this value to your needs.
The default value is 256000000, an image with more pixels than that would not be parsed.
Paperless will raise a warning when OCRing images which are over this limit and
will not OCR images which are more than twice this limit. Note this does not
prevent the document from being consumed, but could result in missing text content.
If unset, will default to the value determined by
`Pillow <https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.MAX_IMAGE_PIXELS>`_.
.. note::
Increasing this limit could cause Paperless to consume additional resources
when consuming a file. Be sure you have sufficient system resources.
.. caution::
The limit is intended to prevent malicious files from consuming system resources
and causing crashes and other errors. Only increase this value if you are certain
your documents are not malicious and you need the text which was not OCRed
PAPERLESS_OCR_USER_ARGS=<json>
OCRmyPDF offers many more options. Use this parameter to specify any

View File

@ -1340,7 +1340,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
<context context-type="linenumber">88</context>
<context context-type="linenumber">86</context>
</context-group>
</trans-unit>
<trans-unit id="8659635229098859487" datatype="html">
@ -1884,7 +1884,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
<context context-type="linenumber">26</context>
<context context-type="linenumber">24</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
@ -1899,7 +1899,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
<context context-type="linenumber">15</context>
<context context-type="linenumber">14</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
@ -1914,7 +1914,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
<context context-type="linenumber">72</context>
<context context-type="linenumber">70</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
@ -1964,7 +1964,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
<context context-type="linenumber">33</context>
<context context-type="linenumber">31</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
@ -1979,7 +1979,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
<context context-type="linenumber">40</context>
<context context-type="linenumber">38</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
@ -1994,7 +1994,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
<context context-type="linenumber">50</context>
<context context-type="linenumber">48</context>
</context-group>
</trans-unit>
<trans-unit id="2030261243264601523" datatype="html">
@ -2005,7 +2005,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
<context context-type="linenumber">51</context>
<context context-type="linenumber">49</context>
</context-group>
</trans-unit>
<trans-unit id="4235671847487610290" datatype="html">
@ -2016,7 +2016,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
<context context-type="linenumber">52</context>
<context context-type="linenumber">50</context>
</context-group>
</trans-unit>
<trans-unit id="2332107018974972998" datatype="html">
@ -2764,35 +2764,56 @@
<source>Saved view &quot;<x id="PH" equiv-text="savedView.name"/>&quot; deleted.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">167</context>
<context context-type="linenumber">174</context>
</context-group>
</trans-unit>
<trans-unit id="5647210819299459618" datatype="html">
<source>Settings saved successfully.</source>
<trans-unit id="3891152409365583719" datatype="html">
<source>Settings saved</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">238</context>
<context context-type="linenumber">247</context>
</context-group>
</trans-unit>
<trans-unit id="7217000812750597833" datatype="html">
<source>Settings were saved successfully.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">248</context>
</context-group>
</trans-unit>
<trans-unit id="525012668859298131" datatype="html">
<source>Settings were saved successfully. Reload is required to apply some changes.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">252</context>
</context-group>
</trans-unit>
<trans-unit id="8491974984518503778" datatype="html">
<source>Reload now</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">253</context>
</context-group>
</trans-unit>
<trans-unit id="3011185103048412841" datatype="html">
<source>An error occurred while saving settings.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">242</context>
<context context-type="linenumber">263</context>
</context-group>
</trans-unit>
<trans-unit id="6839066544204061364" datatype="html">
<source>Use system language</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">250</context>
<context context-type="linenumber">271</context>
</context-group>
</trans-unit>
<trans-unit id="7729897675462249787" datatype="html">
<source>Use date format of display language</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">257</context>
<context context-type="linenumber">278</context>
</context-group>
</trans-unit>
<trans-unit id="8488620293789898901" datatype="html">
@ -2801,7 +2822,7 @@
)"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">277,279</context>
<context context-type="linenumber">298,300</context>
</context-group>
</trans-unit>
<trans-unit id="5101757640976222639" datatype="html">

View File

@ -22,7 +22,6 @@ import {
RemoteVersionService,
AppRemoteVersion,
} from 'src/app/services/rest/remote-version.service'
import { QueryParamsService } from 'src/app/services/query-params.service'
import { SettingsService } from 'src/app/services/settings.service'
@Component({
@ -38,7 +37,7 @@ export class AppFrameComponent {
private searchService: SearchService,
public savedViewService: SavedViewService,
private remoteVersionService: RemoteVersionService,
private queryParamsService: QueryParamsService,
private list: DocumentListViewService,
public settingsService: SettingsService
) {
this.remoteVersionService
@ -94,7 +93,7 @@ export class AppFrameComponent {
search() {
this.closeMenu()
this.queryParamsService.navigateWithFilterRules([
this.list.quickFilter([
{
rule_type: FILTER_FULLTEXT_QUERY,
value: (this.searchField.value as string).trim(),

View File

@ -2,7 +2,7 @@
<label class="form-label" [for]="inputId">{{title}}</label>
<div class="input-group" [class.is-invalid]="error">
<input class="form-control" [class.is-invalid]="error" [placeholder]="placeholder" [id]="inputId" maxlength="10"
(dateSelect)="onChange(value)" (change)="onChange(value)" (keypress)="onKeyPress($event)"
(dateSelect)="onChange(value)" (change)="onChange(value)" (keypress)="onKeyPress($event)" (paste)="onPaste($event)"
name="dp" [(ngModel)]="value" ngbDatepicker #datePicker="ngbDatepicker" #datePickerContent="ngModel">
<button class="btn btn-outline-secondary calendar" (click)="datePicker.toggle()" type="button">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-calendar" viewBox="0 0 16 16">

View File

@ -1,6 +1,8 @@
import { Component, forwardRef, OnInit } from '@angular/core'
import { NG_VALUE_ACCESSOR } from '@angular/forms'
import { NgbDateParserFormatter } from '@ng-bootstrap/ng-bootstrap'
import { SettingsService } from 'src/app/services/settings.service'
import { LocalizedDateParserFormatter } from 'src/app/utils/ngb-date-parser-formatter'
import { AbstractInputComponent } from '../abstract-input'
@Component({
@ -19,7 +21,10 @@ export class DateComponent
extends AbstractInputComponent<string>
implements OnInit
{
constructor(private settings: SettingsService) {
constructor(
private settings: SettingsService,
private ngbDateParserFormatter: NgbDateParserFormatter
) {
super()
}
@ -30,7 +35,20 @@ export class DateComponent
placeholder: string
// prevent chars other than numbers and separators
onPaste(event: ClipboardEvent) {
const clipboardData: DataTransfer =
event.clipboardData || window['clipboardData']
if (clipboardData) {
event.preventDefault()
let pastedText = clipboardData.getData('text')
pastedText = pastedText.replace(/[\sa-z#!$%\^&\*;:{}=\-_`~()]+/g, '')
const parsedDate = this.ngbDateParserFormatter.parse(pastedText)
const formattedDate = this.ngbDateParserFormatter.format(parsedDate)
this.writeValue(formattedDate)
this.onChange(formattedDate)
}
}
onKeyPress(event: KeyboardEvent) {
if ('Enter' !== event.key && !/[0-9,\.\/-]+/.test(event.key)) {
event.preventDefault()

View File

@ -4,5 +4,5 @@
[class]="toast.classname"
(hidden)="toastService.closeToast(toast)">
<p>{{toast.content}}</p>
<p *ngIf="toast.action"><button class="btn btn-sm btn-outline-secondary" (click)="toastService.closeToast(toast); toast.action()">{{toast.actionName}}</button></p>
<p class="mb-0" *ngIf="toast.action"><button class="btn btn-sm btn-outline-secondary" (click)="toastService.closeToast(toast); toast.action()">{{toast.actionName}}</button></p>
</ngb-toast>

View File

@ -7,8 +7,8 @@ import { ConsumerStatusService } from 'src/app/services/consumer-status.service'
import { DocumentService } from 'src/app/services/rest/document.service'
import { PaperlessTag } from 'src/app/data/paperless-tag'
import { FILTER_HAS_TAGS_ALL } from 'src/app/data/filter-rule-type'
import { QueryParamsService } from 'src/app/services/query-params.service'
import { OpenDocumentsService } from 'src/app/services/open-documents.service'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
@Component({
selector: 'app-saved-view-widget',
@ -21,7 +21,7 @@ export class SavedViewWidgetComponent implements OnInit, OnDestroy {
constructor(
private documentService: DocumentService,
private router: Router,
private queryParamsService: QueryParamsService,
private list: DocumentListViewService,
private consumerStatusService: ConsumerStatusService,
public openDocumentsService: OpenDocumentsService
) {}
@ -73,7 +73,7 @@ export class SavedViewWidgetComponent implements OnInit, OnDestroy {
}
clickTag(tag: PaperlessTag) {
this.queryParamsService.navigateWithFilterRules([
this.list.quickFilter([
{ rule_type: FILTER_HAS_TAGS_ALL, value: tag.id.toString() },
])
}

View File

@ -32,7 +32,6 @@ import {
import { PaperlessDocumentSuggestions } from 'src/app/data/paperless-document-suggestions'
import { FILTER_FULLTEXT_MORELIKE } from 'src/app/data/filter-rule-type'
import { normalizeDateStr } from 'src/app/utils/date'
import { QueryParamsService } from 'src/app/services/query-params.service'
import { StoragePathService } from 'src/app/services/rest/storage-path.service'
import { PaperlessStoragePath } from 'src/app/data/paperless-storage-path'
import { StoragePathEditDialogComponent } from '../common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component'
@ -120,8 +119,7 @@ export class DocumentDetailComponent
private documentTitlePipe: DocumentTitlePipe,
private toastService: ToastService,
private settings: SettingsService,
private storagePathService: StoragePathService,
private queryParamsService: QueryParamsService
private storagePathService: StoragePathService
) {}
titleKeyUp(event) {
@ -494,7 +492,7 @@ export class DocumentDetailComponent
}
moreLike() {
this.queryParamsService.navigateWithFilterRules([
this.documentListViewService.quickFilter([
{
rule_type: FILTER_FULLTEXT_MORELIKE,
value: this.documentId.toString(),

View File

@ -93,7 +93,7 @@
<span i18n *ngIf="list.selected.size == 0">{list.collectionSize, plural, =1 {One document} other {{{list.collectionSize || 0}} documents}}</span>&nbsp;<span i18n *ngIf="isFiltered">(filtered)</span>
</ng-container>
</p>
<ngb-pagination [pageSize]="list.currentPageSize" [collectionSize]="list.collectionSize" [(page)]="list.currentPage" [maxSize]="5"
<ngb-pagination [pageSize]="list.currentPageSize" [collectionSize]="list.collectionSize" (pageChange)="setPage($event)" [page]="list.currentPage" [maxSize]="5"
[rotate]="true" aria-label="Default pagination"></ngb-pagination>
</div>
</ng-template>

View File

@ -1,5 +1,4 @@
import {
AfterViewInit,
Component,
OnDestroy,
OnInit,
@ -21,7 +20,6 @@ import {
import { ConsumerStatusService } from 'src/app/services/consumer-status.service'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { OpenDocumentsService } from 'src/app/services/open-documents.service'
import { QueryParamsService } from 'src/app/services/query-params.service'
import {
DOCUMENT_SORT_FIELDS,
DOCUMENT_SORT_FIELDS_FULLTEXT,
@ -36,7 +34,7 @@ import { SaveViewConfigDialogComponent } from './save-view-config-dialog/save-vi
templateUrl: './document-list.component.html',
styleUrls: ['./document-list.component.scss'],
})
export class DocumentListComponent implements OnInit, OnDestroy, AfterViewInit {
export class DocumentListComponent implements OnInit, OnDestroy {
constructor(
public list: DocumentListViewService,
public savedViewService: SavedViewService,
@ -45,7 +43,6 @@ export class DocumentListComponent implements OnInit, OnDestroy, AfterViewInit {
private toastService: ToastService,
private modalService: NgbModal,
private consumerStatusService: ConsumerStatusService,
private queryParamsService: QueryParamsService,
public openDocumentsService: OpenDocumentsService
) {}
@ -76,8 +73,6 @@ export class DocumentListComponent implements OnInit, OnDestroy, AfterViewInit {
set listSort(reverse: boolean) {
this.list.sortReverse = reverse
this.queryParamsService.sortField = this.list.sortField
this.queryParamsService.sortReverse = reverse
}
get listSort(): boolean {
@ -86,14 +81,14 @@ export class DocumentListComponent implements OnInit, OnDestroy, AfterViewInit {
setSortField(field: string) {
this.list.sortField = field
this.queryParamsService.sortField = field
this.queryParamsService.sortReverse = this.listSort
}
onSort(event: SortEvent) {
this.list.setSort(event.column, event.reverse)
this.queryParamsService.sortField = event.column
this.queryParamsService.sortReverse = event.reverse
}
setPage(page: number) {
this.list.currentPage = page
}
get isBulkEditing(): boolean {
@ -133,7 +128,6 @@ export class DocumentListComponent implements OnInit, OnDestroy, AfterViewInit {
}
this.list.activateSavedView(view)
this.list.reload()
this.queryParamsService.updateFromView(view)
this.unmodifiedFilterRules = view.filter_rules
})
@ -148,22 +142,12 @@ export class DocumentListComponent implements OnInit, OnDestroy, AfterViewInit {
this.loadViewConfig(parseInt(queryParams.get('view')))
} else {
this.list.activateSavedView(null)
this.queryParamsService.parseQueryParams(queryParams)
this.list.loadFromQueryParams(queryParams)
this.unmodifiedFilterRules = []
}
})
}
ngAfterViewInit(): void {
this.filterEditor.filterRulesChange
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe({
next: (filterRules) => {
this.queryParamsService.updateFilterRules(filterRules)
},
})
}
ngOnDestroy() {
// unsubscribes all
this.unsubscribeNotifier.next(this)
@ -175,9 +159,8 @@ export class DocumentListComponent implements OnInit, OnDestroy, AfterViewInit {
.getCached(viewId)
.pipe(first())
.subscribe((view) => {
this.list.loadSavedView(view)
this.list.activateSavedView(view)
this.list.reload()
this.queryParamsService.updateFromView(view)
})
}
@ -246,34 +229,26 @@ export class DocumentListComponent implements OnInit, OnDestroy, AfterViewInit {
clickTag(tagID: number) {
this.list.selectNone()
setTimeout(() => {
this.filterEditor.addTag(tagID)
})
this.filterEditor.addTag(tagID)
}
clickCorrespondent(correspondentID: number) {
this.list.selectNone()
setTimeout(() => {
this.filterEditor.addCorrespondent(correspondentID)
})
this.filterEditor.addCorrespondent(correspondentID)
}
clickDocumentType(documentTypeID: number) {
this.list.selectNone()
setTimeout(() => {
this.filterEditor.addDocumentType(documentTypeID)
})
this.filterEditor.addDocumentType(documentTypeID)
}
clickStoragePath(storagePathID: number) {
this.list.selectNone()
setTimeout(() => {
this.filterEditor.addStoragePath(storagePathID)
})
this.filterEditor.addStoragePath(storagePathID)
}
clickMoreLike(documentID: number) {
this.queryParamsService.navigateWithFilterRules([
this.list.quickFilter([
{ rule_type: FILTER_FULLTEXT_MORELIKE, value: documentID.toString() },
])
}

View File

@ -313,7 +313,10 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
break
case FILTER_ASN_ISNULL:
this.textFilterTarget = TEXT_FILTER_TARGET_ASN
this.textFilterModifier = TEXT_FILTER_MODIFIER_NULL
this.textFilterModifier =
rule.value == 'true' || rule.value == '1'
? TEXT_FILTER_MODIFIER_NULL
: TEXT_FILTER_MODIFIER_NOTNULL
break
case FILTER_ASN_GT:
this.textFilterTarget = TEXT_FILTER_TARGET_ASN

View File

@ -3,7 +3,7 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { FILTER_CORRESPONDENT } from 'src/app/data/filter-rule-type'
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
import { QueryParamsService } from 'src/app/services/query-params.service'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
import { ToastService } from 'src/app/services/toast.service'
import { CorrespondentEditDialogComponent } from '../../common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component'
@ -20,7 +20,7 @@ export class CorrespondentListComponent extends ManagementListComponent<Paperles
correspondentsService: CorrespondentService,
modalService: NgbModal,
toastService: ToastService,
queryParamsService: QueryParamsService,
documentListViewService: DocumentListViewService,
private datePipe: CustomDatePipe
) {
super(
@ -28,7 +28,7 @@ export class CorrespondentListComponent extends ManagementListComponent<Paperles
modalService,
CorrespondentEditDialogComponent,
toastService,
queryParamsService,
documentListViewService,
FILTER_CORRESPONDENT,
$localize`correspondent`,
$localize`correspondents`,

View File

@ -2,7 +2,7 @@ import { Component } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { FILTER_DOCUMENT_TYPE } from 'src/app/data/filter-rule-type'
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'
import { QueryParamsService } from 'src/app/services/query-params.service'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
import { ToastService } from 'src/app/services/toast.service'
import { DocumentTypeEditDialogComponent } from '../../common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component'
@ -18,14 +18,14 @@ export class DocumentTypeListComponent extends ManagementListComponent<Paperless
documentTypeService: DocumentTypeService,
modalService: NgbModal,
toastService: ToastService,
queryParamsService: QueryParamsService
documentListViewService: DocumentListViewService
) {
super(
documentTypeService,
modalService,
DocumentTypeEditDialogComponent,
toastService,
queryParamsService,
documentListViewService,
FILTER_DOCUMENT_TYPE,
$localize`document type`,
$localize`document types`,

View File

@ -18,7 +18,7 @@ import {
SortableDirective,
SortEvent,
} from 'src/app/directives/sortable.directive'
import { QueryParamsService } from 'src/app/services/query-params.service'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { AbstractNameFilterService } from 'src/app/services/rest/abstract-name-filter-service'
import { ToastService } from 'src/app/services/toast.service'
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
@ -42,7 +42,7 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
private modalService: NgbModal,
private editDialogComponent: any,
private toastService: ToastService,
private queryParamsService: QueryParamsService,
private documentListViewService: DocumentListViewService,
protected filterRuleType: number,
public typeName: string,
public typeNamePlural: string,
@ -141,7 +141,7 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
}
filterDocuments(object: ObjectWithId) {
this.queryParamsService.navigateWithFilterRules([
this.documentListViewService.quickFilter([
{ rule_type: this.filterRuleType, value: object.id.toString() },
])
}

View File

@ -22,7 +22,7 @@
<option *ngFor="let lang of displayLanguageOptions" [ngValue]="lang.code">{{lang.name}}<span *ngIf="lang.code && currentLocale != 'en-US'"> - {{lang.englishName}}</span></option>
</select>
<small class="form-text text-muted" i18n>You need to reload the page after applying a new language.</small>
<small *ngIf="displayLanguageIsDirty" class="form-text text-primary" i18n>You need to reload the page after applying a new language.</small>
</div>
</div>

View File

@ -14,7 +14,7 @@ import {
LanguageOption,
SettingsService,
} from 'src/app/services/settings.service'
import { ToastService } from 'src/app/services/toast.service'
import { Toast, ToastService } from 'src/app/services/toast.service'
import { dirtyCheck, DirtyComponent } from '@ngneat/dirty-check-forms'
import { Observable, Subscription, BehaviorSubject, first } from 'rxjs'
import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
@ -61,6 +61,13 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
)
}
get displayLanguageIsDirty(): boolean {
return (
this.settingsForm.get('displayLanguage').value !=
this.store?.getValue()['displayLanguage']
)
}
constructor(
public savedViewService: SavedViewService,
private documentListViewService: DocumentListViewService,
@ -170,6 +177,7 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
}
private saveLocalSettings() {
const reloadRequired = this.displayLanguageIsDirty // just this one, for now
this.settings.set(
SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE,
this.settingsForm.value.bulkEditApplyOnClose
@ -235,7 +243,20 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
this.store.next(this.settingsForm.value)
this.documentListViewService.updatePageSize()
this.settings.updateAppearanceSettings()
this.toastService.showInfo($localize`Settings saved successfully.`)
let savedToast: Toast = {
title: $localize`Settings saved`,
content: $localize`Settings were saved successfully.`,
delay: 500000,
}
if (reloadRequired) {
;(savedToast.content = $localize`Settings were saved successfully. Reload is required to apply some changes.`),
(savedToast.actionName = $localize`Reload now`)
savedToast.action = () => {
location.reload()
}
}
this.toastService.show(savedToast)
},
error: (error) => {
this.toastService.showError(

View File

@ -3,7 +3,6 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { FILTER_STORAGE_PATH } from 'src/app/data/filter-rule-type'
import { PaperlessStoragePath } from 'src/app/data/paperless-storage-path'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { QueryParamsService } from 'src/app/services/query-params.service'
import { StoragePathService } from 'src/app/services/rest/storage-path.service'
import { ToastService } from 'src/app/services/toast.service'
import { StoragePathEditDialogComponent } from '../../common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component'
@ -19,14 +18,14 @@ export class StoragePathListComponent extends ManagementListComponent<PaperlessS
directoryService: StoragePathService,
modalService: NgbModal,
toastService: ToastService,
queryParamsService: QueryParamsService
documentListViewService: DocumentListViewService
) {
super(
directoryService,
modalService,
StoragePathEditDialogComponent,
toastService,
queryParamsService,
documentListViewService,
FILTER_STORAGE_PATH,
$localize`storage path`,
$localize`storage paths`,

View File

@ -2,7 +2,7 @@ import { Component } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { FILTER_HAS_TAGS_ALL } from 'src/app/data/filter-rule-type'
import { PaperlessTag } from 'src/app/data/paperless-tag'
import { QueryParamsService } from 'src/app/services/query-params.service'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { TagService } from 'src/app/services/rest/tag.service'
import { ToastService } from 'src/app/services/toast.service'
import { TagEditDialogComponent } from '../../common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component'
@ -18,14 +18,14 @@ export class TagListComponent extends ManagementListComponent<PaperlessTag> {
tagService: TagService,
modalService: NgbModal,
toastService: ToastService,
queryParamsService: QueryParamsService
documentListViewService: DocumentListViewService
) {
super(
tagService,
modalService,
TagEditDialogComponent,
toastService,
queryParamsService,
documentListViewService,
FILTER_HAS_TAGS_ALL,
$localize`tag`,
$localize`tags`,

View File

@ -1,34 +1,38 @@
export const FILTER_TITLE = 0
export const FILTER_CONTENT = 1
export const FILTER_ASN = 2
export const FILTER_ASN_ISNULL = 18
export const FILTER_ASN_GT = 23
export const FILTER_ASN_LT = 24
export const FILTER_CORRESPONDENT = 3
export const FILTER_DOCUMENT_TYPE = 4
export const FILTER_IS_IN_INBOX = 5
export const FILTER_HAS_TAGS_ALL = 6
export const FILTER_HAS_ANY_TAG = 7
export const FILTER_DOES_NOT_HAVE_TAG = 17
export const FILTER_HAS_TAGS_ANY = 22
export const FILTER_STORAGE_PATH = 25
export const FILTER_CREATED_BEFORE = 8
export const FILTER_CREATED_AFTER = 9
export const FILTER_CREATED_YEAR = 10
export const FILTER_CREATED_MONTH = 11
export const FILTER_CREATED_DAY = 12
export const FILTER_ADDED_BEFORE = 13
export const FILTER_ADDED_AFTER = 14
export const FILTER_MODIFIED_BEFORE = 15
export const FILTER_MODIFIED_AFTER = 16
export const FILTER_DOES_NOT_HAVE_TAG = 17
export const FILTER_ASN_ISNULL = 18
export const FILTER_ASN_GT = 19
export const FILTER_ASN_LT = 20
export const FILTER_TITLE_CONTENT = 21
export const FILTER_FULLTEXT_QUERY = 22
export const FILTER_FULLTEXT_MORELIKE = 23
export const FILTER_STORAGE_PATH = 30
export const FILTER_TITLE_CONTENT = 19
export const FILTER_FULLTEXT_QUERY = 20
export const FILTER_FULLTEXT_MORELIKE = 21
export const FILTER_RULE_TYPES: FilterRuleType[] = [
{

View File

@ -1,5 +1,5 @@
import { Injectable } from '@angular/core'
import { ActivatedRoute, Params, Router } from '@angular/router'
import { ParamMap, Router } from '@angular/router'
import { Observable } from 'rxjs'
import {
cloneFilterRules,
@ -10,13 +10,14 @@ import { PaperlessDocument } from '../data/paperless-document'
import { PaperlessSavedView } from '../data/paperless-saved-view'
import { SETTINGS_KEYS } from '../data/paperless-uisettings'
import { DOCUMENT_LIST_SERVICE } from '../data/storage-keys'
import { generateParams, parseParams } from '../utils/query-params'
import { DocumentService, DOCUMENT_SORT_FIELDS } from './rest/document.service'
import { SettingsService } from './settings.service'
/**
* Captures the current state of the list view.
*/
interface ListViewState {
export interface ListViewState {
/**
* Title of the document list view. Either "Documents" (localized) or the name of a saved view.
*/
@ -32,7 +33,7 @@ interface ListViewState {
/**
* Total amount of documents with the current filter rules. Used to calculate the number of pages.
*/
collectionSize: number
collectionSize?: number
/**
* Currently selected sort field.
@ -85,6 +86,32 @@ export class DocumentListViewService {
return this.activeListViewState.title
}
constructor(
private documentService: DocumentService,
private settings: SettingsService,
private router: Router
) {
let documentListViewConfigJson = localStorage.getItem(
DOCUMENT_LIST_SERVICE.CURRENT_VIEW_CONFIG
)
if (documentListViewConfigJson) {
try {
let savedState: ListViewState = JSON.parse(documentListViewConfigJson)
// Remove null elements from the restored state
Object.keys(savedState).forEach((k) => {
if (savedState[k] == null) {
delete savedState[k]
}
})
//only use restored state attributes instead of defaults if they are not null
let newState = Object.assign(this.defaultListViewState(), savedState)
this.listViewStates.set(null, newState)
} catch (e) {
localStorage.removeItem(DOCUMENT_LIST_SERVICE.CURRENT_VIEW_CONFIG)
}
}
}
private defaultListViewState(): ListViewState {
return {
title: null,
@ -122,20 +149,40 @@ export class DocumentListViewService {
if (closeCurrentView) {
this._activeSavedViewId = null
}
this.activeListViewState.filterRules = cloneFilterRules(view.filter_rules)
this.activeListViewState.sortField = view.sort_field
this.activeListViewState.sortReverse = view.sort_reverse
if (this._activeSavedViewId) {
this.activeListViewState.title = view.name
}
this.reduceSelectionToFilter()
if (!this.router.routerState.snapshot.url.includes('/view/')) {
this.router.navigate([], {
queryParams: { view: view.id },
})
}
}
reload(onFinish?) {
loadFromQueryParams(queryParams: ParamMap) {
const paramsEmpty: boolean = queryParams.keys.length == 0
let newState: ListViewState = this.listViewStates.get(null)
if (!paramsEmpty) newState = parseParams(queryParams)
if (newState == undefined) newState = this.defaultListViewState() // if nothing in local storage
this.activeListViewState.filterRules = newState.filterRules
this.activeListViewState.sortField = newState.sortField
this.activeListViewState.sortReverse = newState.sortReverse
this.activeListViewState.currentPage = newState.currentPage
this.reload(null, paramsEmpty) // update the params if there arent any
}
reload(onFinish?, updateQueryParams: boolean = true) {
this.isReloading = true
this.error = null
let activeListViewState = this.activeListViewState
this.documentService
.listFiltered(
activeListViewState.currentPage,
@ -149,6 +196,14 @@ export class DocumentListViewService {
this.isReloading = false
activeListViewState.collectionSize = result.count
activeListViewState.documents = result.results
if (updateQueryParams && !this._activeSavedViewId) {
let base = ['/documents']
this.router.navigate(base, {
queryParams: generateParams(activeListViewState),
})
}
if (onFinish) {
onFinish()
}
@ -191,6 +246,7 @@ export class DocumentListViewService {
) {
this.activeListViewState.sortField = 'created'
}
this._activeSavedViewId = null
this.activeListViewState.filterRules = filterRules
this.reload()
this.reduceSelectionToFilter()
@ -202,6 +258,7 @@ export class DocumentListViewService {
}
set sortField(field: string) {
this._activeSavedViewId = null
this.activeListViewState.sortField = field
this.reload()
this.saveDocumentListView()
@ -212,6 +269,7 @@ export class DocumentListViewService {
}
set sortReverse(reverse: boolean) {
this._activeSavedViewId = null
this.activeListViewState.sortReverse = reverse
this.reload()
this.saveDocumentListView()
@ -221,13 +279,6 @@ export class DocumentListViewService {
return this.activeListViewState.sortReverse
}
get sortParams(): Params {
return {
sortField: this.sortField,
sortReverse: this.sortReverse,
}
}
get collectionSize(): number {
return this.activeListViewState.collectionSize
}
@ -237,6 +288,8 @@ export class DocumentListViewService {
}
set currentPage(page: number) {
if (this.activeListViewState.currentPage == page) return
this._activeSavedViewId = null
this.activeListViewState.currentPage = page
this.reload()
this.saveDocumentListView()
@ -273,6 +326,10 @@ export class DocumentListViewService {
}
}
quickFilter(filterRules: FilterRule[]) {
this.filterRules = filterRules
}
getLastPage(): number {
return Math.ceil(this.collectionSize / this.currentPageSize)
}
@ -431,29 +488,4 @@ export class DocumentListViewService {
documentIndexInCurrentView(documentID: number): number {
return this.documents.map((d) => d.id).indexOf(documentID)
}
constructor(
private documentService: DocumentService,
private settings: SettingsService
) {
let documentListViewConfigJson = localStorage.getItem(
DOCUMENT_LIST_SERVICE.CURRENT_VIEW_CONFIG
)
if (documentListViewConfigJson) {
try {
let savedState: ListViewState = JSON.parse(documentListViewConfigJson)
// Remove null elements from the restored state
Object.keys(savedState).forEach((k) => {
if (savedState[k] == null) {
delete savedState[k]
}
})
//only use restored state attributes instead of defaults if they are not null
let newState = Object.assign(this.defaultListViewState(), savedState)
this.listViewStates.set(null, newState)
} catch (e) {
localStorage.removeItem(DOCUMENT_LIST_SERVICE.CURRENT_VIEW_CONFIG)
}
}
}
}

View File

@ -1,163 +0,0 @@
import { Injectable } from '@angular/core'
import { ParamMap, Params, Router } from '@angular/router'
import { FilterRule } from '../data/filter-rule'
import { FilterRuleType, FILTER_RULE_TYPES } from '../data/filter-rule-type'
import { PaperlessSavedView } from '../data/paperless-saved-view'
import { DocumentListViewService } from './document-list-view.service'
const SORT_FIELD_PARAMETER = 'sort'
const SORT_REVERSE_PARAMETER = 'reverse'
@Injectable({
providedIn: 'root',
})
export class QueryParamsService {
constructor(private router: Router, private list: DocumentListViewService) {}
private filterParams: Params = {}
private sortParams: Params = {}
updateFilterRules(
filterRules: FilterRule[],
updateQueryParams: boolean = true
) {
this.filterParams = filterRulesToQueryParams(filterRules)
if (updateQueryParams) this.updateQueryParams()
}
set sortField(field: string) {
this.sortParams[SORT_FIELD_PARAMETER] = field
this.updateQueryParams()
}
set sortReverse(reverse: boolean) {
if (!reverse) this.sortParams[SORT_REVERSE_PARAMETER] = undefined
else this.sortParams[SORT_REVERSE_PARAMETER] = reverse
this.updateQueryParams()
}
get params(): Params {
return {
...this.sortParams,
...this.filterParams,
}
}
private updateQueryParams() {
// if we were on a saved view we navigate 'away' to /documents
let base = []
if (this.router.routerState.snapshot.url.includes('/view/'))
base = ['/documents']
this.router.navigate(base, {
queryParams: this.params,
})
}
public parseQueryParams(queryParams: ParamMap) {
let filterRules = filterRulesFromQueryParams(queryParams)
if (
filterRules.length ||
queryParams.has(SORT_FIELD_PARAMETER) ||
queryParams.has(SORT_REVERSE_PARAMETER)
) {
this.list.filterRules = filterRules
this.list.sortField = queryParams.get(SORT_FIELD_PARAMETER)
this.list.sortReverse =
queryParams.has(SORT_REVERSE_PARAMETER) ||
(!queryParams.has(SORT_FIELD_PARAMETER) &&
!queryParams.has(SORT_REVERSE_PARAMETER))
this.list.reload()
} else if (
filterRules.length == 0 &&
!queryParams.has(SORT_FIELD_PARAMETER)
) {
// this is navigating to /documents so we need to update the params from the list
this.updateFilterRules(this.list.filterRules, false)
this.sortParams[SORT_FIELD_PARAMETER] = this.list.sortField
this.sortParams[SORT_REVERSE_PARAMETER] = this.list.sortReverse
this.router.navigate([], {
queryParams: this.params,
replaceUrl: true,
})
}
}
updateFromView(view: PaperlessSavedView) {
if (!this.router.routerState.snapshot.url.includes('/view/')) {
// navigation for /documents?view=
this.router.navigate([], {
queryParams: { view: view.id },
})
}
// make sure params are up-to-date
this.updateFilterRules(view.filter_rules, false)
this.sortParams[SORT_FIELD_PARAMETER] = this.list.sortField
this.sortParams[SORT_REVERSE_PARAMETER] = this.list.sortReverse
}
navigateWithFilterRules(filterRules: FilterRule[]) {
this.updateFilterRules(filterRules)
this.router.navigate(['/documents'], {
queryParams: this.params,
})
}
}
export function filterRulesToQueryParams(filterRules: FilterRule[]): Object {
if (filterRules) {
let params = {}
for (let rule of filterRules) {
let ruleType = FILTER_RULE_TYPES.find((t) => t.id == rule.rule_type)
if (ruleType.multi) {
params[ruleType.filtervar] = params[ruleType.filtervar]
? params[ruleType.filtervar] + ',' + rule.value
: rule.value
} else if (ruleType.isnull_filtervar && rule.value == null) {
params[ruleType.isnull_filtervar] = true
} else {
params[ruleType.filtervar] = rule.value
}
}
return params
} else {
return null
}
}
export function filterRulesFromQueryParams(queryParams: ParamMap) {
const allFilterRuleQueryParams: string[] = FILTER_RULE_TYPES.map(
(rt) => rt.filtervar
)
.concat(FILTER_RULE_TYPES.map((rt) => rt.isnull_filtervar))
.filter((rt) => rt !== undefined)
// transform query params to filter rules
let filterRulesFromQueryParams: FilterRule[] = []
allFilterRuleQueryParams
.filter((frqp) => queryParams.has(frqp))
.forEach((filterQueryParamName) => {
const rule_type: FilterRuleType = FILTER_RULE_TYPES.find(
(rt) =>
rt.filtervar == filterQueryParamName ||
rt.isnull_filtervar == filterQueryParamName
)
const isNullRuleType = rule_type.isnull_filtervar == filterQueryParamName
const valueURIComponent: string = queryParams.get(filterQueryParamName)
const filterQueryParamValues: string[] = rule_type.multi
? valueURIComponent.split(',')
: [valueURIComponent]
filterRulesFromQueryParams = filterRulesFromQueryParams.concat(
// map all values to filter rules
filterQueryParamValues.map((val) => {
return {
rule_type: rule_type.id,
value: isNullRuleType ? null : val,
}
})
)
})
return filterRulesFromQueryParams
}

View File

@ -6,12 +6,12 @@ import { HttpClient, HttpParams } from '@angular/common/http'
import { Observable } from 'rxjs'
import { Results } from 'src/app/data/results'
import { FilterRule } from 'src/app/data/filter-rule'
import { map } from 'rxjs/operators'
import { map, tap } from 'rxjs/operators'
import { CorrespondentService } from './correspondent.service'
import { DocumentTypeService } from './document-type.service'
import { TagService } from './tag.service'
import { PaperlessDocumentSuggestions } from 'src/app/data/paperless-document-suggestions'
import { filterRulesToQueryParams } from '../query-params.service'
import { queryParamsFromFilterRules } from '../../utils/query-params'
import { StoragePathService } from './storage-path.service'
export const DOCUMENT_SORT_FIELDS = [
@ -70,7 +70,13 @@ export class DocumentService extends AbstractPaperlessService<PaperlessDocument>
doc.document_type$ = this.documentTypeService.getCached(doc.document_type)
}
if (doc.tags) {
doc.tags$ = this.tagService.getCachedMany(doc.tags)
doc.tags$ = this.tagService
.getCachedMany(doc.tags)
.pipe(
tap((tags) =>
tags.sort((tagA, tagB) => tagA.name.localeCompare(tagB.name))
)
)
}
if (doc.storage_path) {
doc.storage_path$ = this.storagePathService.getCached(doc.storage_path)
@ -91,7 +97,7 @@ export class DocumentService extends AbstractPaperlessService<PaperlessDocument>
pageSize,
sortField,
sortReverse,
Object.assign(extraParams, filterRulesToQueryParams(filterRules))
Object.assign(extraParams, queryParamsFromFilterRules(filterRules))
).pipe(
map((results) => {
results.results.forEach((doc) => this.addObservablesToDocument(doc))

View File

@ -0,0 +1,101 @@
import { ParamMap, Params } from '@angular/router'
import { FilterRule } from '../data/filter-rule'
import { FilterRuleType, FILTER_RULE_TYPES } from '../data/filter-rule-type'
import { ListViewState } from '../services/document-list-view.service'
const SORT_FIELD_PARAMETER = 'sort'
const SORT_REVERSE_PARAMETER = 'reverse'
const PAGE_PARAMETER = 'page'
export function generateParams(viewState: ListViewState): Params {
let params = queryParamsFromFilterRules(viewState.filterRules)
params[SORT_FIELD_PARAMETER] = viewState.sortField
params[SORT_REVERSE_PARAMETER] = viewState.sortReverse ? 1 : undefined
params[PAGE_PARAMETER] = isNaN(viewState.currentPage)
? 1
: viewState.currentPage
return params
}
export function parseParams(queryParams: ParamMap): ListViewState {
let filterRules = filterRulesFromQueryParams(queryParams)
let sortField = queryParams.get(SORT_FIELD_PARAMETER)
let sortReverse =
queryParams.has(SORT_REVERSE_PARAMETER) ||
(!queryParams.has(SORT_FIELD_PARAMETER) &&
!queryParams.has(SORT_REVERSE_PARAMETER))
let currentPage = queryParams.has(PAGE_PARAMETER)
? parseInt(queryParams.get(PAGE_PARAMETER))
: 1
return {
currentPage: currentPage,
filterRules: filterRules,
sortField: sortField,
sortReverse: sortReverse,
}
}
export function filterRulesFromQueryParams(
queryParams: ParamMap
): FilterRule[] {
const allFilterRuleQueryParams: string[] = FILTER_RULE_TYPES.map(
(rt) => rt.filtervar
)
.concat(FILTER_RULE_TYPES.map((rt) => rt.isnull_filtervar))
.filter((rt) => rt !== undefined)
// transform query params to filter rules
let filterRulesFromQueryParams: FilterRule[] = []
allFilterRuleQueryParams
.filter((frqp) => queryParams.has(frqp))
.forEach((filterQueryParamName) => {
const rule_type: FilterRuleType = FILTER_RULE_TYPES.find(
(rt) =>
rt.filtervar == filterQueryParamName ||
rt.isnull_filtervar == filterQueryParamName
)
const isNullRuleType = rule_type.isnull_filtervar == filterQueryParamName
const valueURIComponent: string = queryParams.get(filterQueryParamName)
const filterQueryParamValues: string[] = rule_type.multi
? valueURIComponent.split(',')
: [valueURIComponent]
filterRulesFromQueryParams = filterRulesFromQueryParams.concat(
// map all values to filter rules
filterQueryParamValues.map((val) => {
if (rule_type.datatype == 'boolean')
val = val.replace('1', 'true').replace('0', 'false')
return {
rule_type: rule_type.id,
value: isNullRuleType ? null : val,
}
})
)
})
return filterRulesFromQueryParams
}
export function queryParamsFromFilterRules(filterRules: FilterRule[]): Params {
if (filterRules) {
let params = {}
for (let rule of filterRules) {
let ruleType = FILTER_RULE_TYPES.find((t) => t.id == rule.rule_type)
if (ruleType.multi) {
params[ruleType.filtervar] = params[ruleType.filtervar]
? params[ruleType.filtervar] + ',' + rule.value
: rule.value
} else if (ruleType.isnull_filtervar && rule.value == null) {
params[ruleType.isnull_filtervar] = 1
} else {
params[ruleType.filtervar] = rule.value
if (ruleType.datatype == 'boolean')
params[ruleType.filtervar] =
rule.value == 'true' || rule.value == '1' ? 1 : 0
}
}
return params
} else {
return null
}
}

View File

@ -84,6 +84,10 @@ svg.logo {
}
}
.text-primary {
color: var(--bs-primary) !important;
}
.btn-outline-primary {
border-color: var(--bs-primary) !important;
color: var(--bs-primary) !important;

View File

@ -186,7 +186,8 @@ $form-check-radio-checked-bg-image-dark: url("data:image/svg+xml,<svg xmlns='htt
.toast,
.toast .toast-header,
.toast .btn-close {
.toast .btn,
.toast .btn-close, {
color: var(--pngx-primary-text-contrast);
}
}

View File

@ -5,6 +5,7 @@ import multiprocessing
import os
import re
from typing import Final
from typing import Optional
from typing import Set
from urllib.parse import urlparse
@ -551,10 +552,9 @@ OCR_ROTATE_PAGES_THRESHOLD = float(
os.getenv("PAPERLESS_OCR_ROTATE_PAGES_THRESHOLD", 12.0),
)
OCR_MAX_IMAGE_PIXELS = os.environ.get(
"PAPERLESS_OCR_MAX_IMAGE_PIXELS",
256000000,
)
OCR_MAX_IMAGE_PIXELS: Optional[int] = None
if os.environ.get("PAPERLESS_OCR_MAX_IMAGE_PIXELS") is not None:
OCR_MAX_IMAGE_PIXELS: int = int(os.environ.get("PAPERLESS_OCR_MAX_IMAGE_PIXELS"))
OCR_USER_ARGS = os.getenv("PAPERLESS_OCR_USER_ARGS", "{}")

View File

@ -8,8 +8,6 @@ from documents.parsers import make_thumbnail_from_pdf
from documents.parsers import ParseError
from PIL import Image
Image.MAX_IMAGE_PIXELS = settings.OCR_MAX_IMAGE_PIXELS
class NoTextFoundException(Exception):
pass
@ -225,6 +223,24 @@ class RasterisedDocumentParser(DocumentParser):
f"they will not be used. Error: {e}",
)
if settings.OCR_MAX_IMAGE_PIXELS is not None:
# Convert pixels to mega-pixels and provide to ocrmypdf
max_pixels_mpixels = settings.OCR_MAX_IMAGE_PIXELS / 1_000_000.0
if max_pixels_mpixels > 0:
self.log(
"debug",
f"Calculated {max_pixels_mpixels} megapixels for OCR",
)
ocrmypdf_args["max_image_mpixels"] = max_pixels_mpixels
else:
self.log(
"warning",
"There is an issue with PAPERLESS_OCR_MAX_IMAGE_PIXELS, "
"this value must be at least 1 megapixel if set",
)
return ocrmypdf_args
def parse(self, document_path, mime_type, file_name=None):

View File

@ -6,8 +6,6 @@ from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
Image.MAX_IMAGE_PIXELS = settings.OCR_MAX_IMAGE_PIXELS
class TextDocumentParser(DocumentParser):
"""
@ -28,7 +26,7 @@ class TextDocumentParser(DocumentParser):
font = ImageFont.truetype(
font=settings.THUMBNAIL_FONT_NAME,
size=20,
layout_engine=ImageFont.LAYOUT_BASIC,
layout_engine=ImageFont.Layout.BASIC,
)
draw.text((5, 5), read_text(), font=font, fill="black")