From 54eaf50ccb7fc9fb69e3c387c27b101e5d11af59 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Mon, 15 Apr 2019 11:34:21 -0400 Subject: [PATCH] LP1824391 Hatch print-to-file Angular edition Adds support for the Angular hatch service for sending 'bare' text/plain print-to-file requests. Signed-off-by: Bill Erickson Signed-off-by: Jason Boyer --- Open-ILS/src/eg2/src/app/app.component.ts | 2 +- Open-ILS/src/eg2/src/app/common.module.ts | 2 + .../src/eg2/src/app/share/print/print.component.ts | 37 +++++++++-- .../eg2/src/app/share/util/htmltotxt.service.ts | 75 ++++++++++++++++++++++ .../src/app/staff/sandbox/sandbox.component.html | 1 + .../eg2/src/app/staff/sandbox/sandbox.component.ts | 7 +- .../src/eg2/src/app/staff/staff.component.html | 4 ++ 7 files changed, 119 insertions(+), 9 deletions(-) create mode 100644 Open-ILS/src/eg2/src/app/share/util/htmltotxt.service.ts diff --git a/Open-ILS/src/eg2/src/app/app.component.ts b/Open-ILS/src/eg2/src/app/app.component.ts index c70938ff99..a297f2285d 100644 --- a/Open-ILS/src/eg2/src/app/app.component.ts +++ b/Open-ILS/src/eg2/src/app/app.component.ts @@ -4,7 +4,7 @@ import {DialogComponent} from '@eg/share/dialog/dialog.component'; @Component({ selector: 'eg-root', - template: '' + template: '' }) export class BaseComponent { diff --git a/Open-ILS/src/eg2/src/app/common.module.ts b/Open-ILS/src/eg2/src/app/common.module.ts index d61eeabb3a..ab41d5a155 100644 --- a/Open-ILS/src/eg2/src/app/common.module.ts +++ b/Open-ILS/src/eg2/src/app/common.module.ts @@ -15,6 +15,7 @@ They do not have to be added to the providers list. // consider moving these to core... import {HatchService} from '@eg/share/print/hatch.service'; +import {HtmlToTxtService} from '@eg/share/util/htmltotxt.service'; import {PrintService} from '@eg/share/print/print.service'; // Globally available components @@ -79,6 +80,7 @@ export class EgCommonModule { return { ngModule: EgCommonModule, providers: [ + HtmlToTxtService, HatchService, PrintService, ToastService diff --git a/Open-ILS/src/eg2/src/app/share/print/print.component.ts b/Open-ILS/src/eg2/src/app/share/print/print.component.ts index 0d780f3637..91341de14d 100644 --- a/Open-ILS/src/eg2/src/app/share/print/print.component.ts +++ b/Open-ILS/src/eg2/src/app/share/print/print.component.ts @@ -5,6 +5,8 @@ import {ServerStoreService} from '@eg/core/server-store.service'; import {HatchService, HatchMessage} from '@eg/core/hatch.service'; import {ToastService} from '@eg/share/toast/toast.service'; import {StringService} from '@eg/share/string/string.service'; +import {HtmlToTxtService} from '@eg/share/util/htmltotxt.service'; +const HATCH_FILE_WRITER_PRINTER = 'hatch_file_writer'; @Component({ selector: 'eg-print', @@ -33,6 +35,7 @@ export class PrintComponent implements OnInit { private elm: ElementRef, private store: StoreService, private serverStore: ServerStoreService, + private h2txt: HtmlToTxtService, private hatch: HatchService, private toast: ToastService, private strings: StringService, @@ -177,6 +180,9 @@ export class PrintComponent implements OnInit { } printViaHatch(printReq: PrintRequest) { + if (!printReq.contentType) { + printReq.contentType = 'text/html'; + } // Send a full HTML document to Hatch let html = printReq.text; @@ -187,13 +193,30 @@ export class PrintComponent implements OnInit { this.serverStore.getItem(`eg.print.config.${printReq.printContext}`) .then(config => { - const msg = new HatchMessage({ - action: 'print', - content: html, - settings: config || {}, - contentType: 'text/html', - showDialog: printReq.showDialog - }); + let msg: HatchMessage; + + if (config && config.printer === HATCH_FILE_WRITER_PRINTER) { + + const text = printReq.contentType === 'text/plain' ? + html : this.h2txt.htmlToTxt(html); + + msg = new HatchMessage({ + action: 'set', + key: `receipt.${printReq.printContext}.txt`, + content: text, + bare: true + }); + + } else { + + msg = new HatchMessage({ + action: 'print', + content: html, + settings: config || {}, + contentType: 'text/html', + showDialog: printReq.showDialog + }); + } this.hatch.sendRequest(msg).then( ok => console.debug('Print request succeeded'), diff --git a/Open-ILS/src/eg2/src/app/share/util/htmltotxt.service.ts b/Open-ILS/src/eg2/src/app/share/util/htmltotxt.service.ts new file mode 100644 index 0000000000..e265b3d2fa --- /dev/null +++ b/Open-ILS/src/eg2/src/app/share/util/htmltotxt.service.ts @@ -0,0 +1,75 @@ +import {Injectable} from '@angular/core'; + +const ENTITY_REGEX = /&[^\s]+;/; + +/** + * Translates HTML text into plain text. + */ + +@Injectable() +export class HtmlToTxtService { + + unEscapeHtml(text: string): string { + text = text.replace(/&/g, '&'); + text = text.replace(/"/g, '"'); + text = text.replace(/ /g, ' '); + text = text.replace(/</g, '<'); + text = text.replace(/>/g, '>'); + return text; + } + + // https://stackoverflow.com/questions/7394748 + entityToChars(text: string): string { + if (text && text.match(ENTITY_REGEX)) { + const node = document.createElement('textarea'); + node.innerHTML = text; + return node.value; + } + return text; + } + + // Translate an HTML string into plain text. + // Removes HTML elements. + // Replaces
  • with "*" + // Replaces HTML entities with their character equivalent. + htmlToTxt(html: string): string { + if (!html || html === '') { + return ''; + } + + // First remove multi-line comments. + html = html.replace(//gs, ''); + + const lines = html.split(/\n/); + const newLines = []; + + lines.forEach(line => { + + if (!line) { + newLines.push(line); + return; + } + + line = this.unEscapeHtml(line); + line = this.entityToChars(line); + + line = line.replace(/.*?<\/head>/gi, ''); + line = line.replace(//gi, '\r\n'); + line = line.replace(//gi, ''); + line = line.replace(/<\/tr>/gi, '\r\n'); // end of row + line = line.replace(/<\/td>/gi, ' '); // end of cell + line = line.replace(/<\/th>/gi, ' '); // end of th + line = line.replace(//gi, ''); + line = line.replace(//gi, '\r\n'); + line = line.replace(//gi, ''); + line = line.replace(//gi, ''); + line = line.replace(//gi, ' * '); + line = line.replace(/<.+?>/gi, ''); + + if (line) { newLines.push(line); } + }); + + return newLines.join('\n'); + } +} + diff --git a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html index ee46d1eb54..ed1b5813b4 100644 --- a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html +++ b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html @@ -403,3 +403,4 @@ + diff --git a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts index 5ad5161308..0dc8ee05f6 100644 --- a/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts @@ -22,6 +22,7 @@ import {StringComponent} from '@eg/share/string/string.component'; import {GridComponent} from '@eg/share/grid/grid.component'; import * as Moment from 'moment-timezone'; import {SampleDataService} from '@eg/share/util/sample-data.service'; +import {HtmlToTxtService} from '@eg/share/util/htmltotxt.service'; @Component({ templateUrl: 'sandbox.component.html', @@ -114,7 +115,8 @@ export class SandboxComponent implements OnInit { private toast: ToastService, private format: FormatService, private printer: PrintService, - private samples: SampleDataService + private samples: SampleDataService, + private h2txt: HtmlToTxtService ) { // BroadcastChannel is not yet defined in PhantomJS and elsewhere this.sbChannel = (typeof BroadcastChannel === 'undefined') ? @@ -271,6 +273,9 @@ export class SandboxComponent implements OnInit { } } ) }); + + const str = 'César & Me'; + console.log(this.h2txt.htmlToTxt(str)); } sbChannelHandler = msg => { diff --git a/Open-ILS/src/eg2/src/app/staff/staff.component.html b/Open-ILS/src/eg2/src/app/staff/staff.component.html index 9002fa8e1f..6cc1bc02fd 100644 --- a/Open-ILS/src/eg2/src/app/staff/staff.component.html +++ b/Open-ILS/src/eg2/src/app/staff/staff.component.html @@ -21,3 +21,7 @@ + + + + -- 2.11.0