LP1824391 Hatch print-to-file Angular edition
authorBill Erickson <berickxx@gmail.com>
Mon, 15 Apr 2019 15:34:21 +0000 (11:34 -0400)
committerJason Boyer <JBoyer@eoli.info>
Fri, 13 Dec 2019 14:36:28 +0000 (09:36 -0500)
Adds support for the Angular hatch service for sending 'bare' text/plain
print-to-file requests.

Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Jason Boyer <JBoyer@eoli.info>

Open-ILS/src/eg2/src/app/app.component.ts
Open-ILS/src/eg2/src/app/common.module.ts
Open-ILS/src/eg2/src/app/share/print/print.component.ts
Open-ILS/src/eg2/src/app/share/util/htmltotxt.service.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html
Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts
Open-ILS/src/eg2/src/app/staff/staff.component.html

index c70938f..a297f22 100644 (file)
@@ -4,7 +4,7 @@ import {DialogComponent} from '@eg/share/dialog/dialog.component';
 
 @Component({
   selector: 'eg-root',
-  template: '<router-outlet></router-outlet><eg-print></eg-print>'
+  template: '<router-outlet></router-outlet>'
 })
 
 export class BaseComponent {
index d61eeab..ab41d5a 100644 (file)
@@ -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
index 0d780f3..91341de 100644 (file)
@@ -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 (file)
index 0000000..e265b3d
--- /dev/null
@@ -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(/&amp;/g, '&');
+        text = text.replace(/&quot;/g, '"');
+        text = text.replace(/&nbsp;/g, ' ');
+        text = text.replace(/&lt;/g, '<');
+        text = text.replace(/&gt;/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 <li> 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.*?>.*?<\/head>/gi, '');
+            line = line.replace(/<br.*?>/gi, '\r\n');
+            line = line.replace(/<table.*?>/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(/<tr.*?>/gi, '');
+            line = line.replace(/<hr.*?>/gi, '\r\n');
+            line = line.replace(/<p.*?>/gi, '');
+            line = line.replace(/<block.*?>/gi, '');
+            line = line.replace(/<li.*?>/gi, ' * ');
+            line = line.replace(/<.+?>/gi, '');
+
+            if (line) { newLines.push(line); }
+        });
+
+        return newLines.join('\n');
+    }
+}
+
index 5ad5161..0dc8ee0 100644 (file)
@@ -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&#xe9;sar&nbsp;&amp;&nbsp;Me';
+        console.log(this.h2txt.htmlToTxt(str));
     }
 
     sbChannelHandler = msg => {
index 9002fa8..6cc1bc0 100644 (file)
@@ -21,3 +21,7 @@
 
 <!-- global toast alerts -->
 <eg-toast></eg-toast>
+
+<!-- global print handler component -->
+<eg-print></eg-print>
+