LP1824391 Hatch File Writer print support
authorBill Erickson <berickxx@gmail.com>
Fri, 12 Apr 2019 16:52:09 +0000 (12:52 -0400)
committerJason Boyer <JBoyer@eoli.info>
Fri, 13 Dec 2019 14:36:28 +0000 (09:36 -0500)
Adds support for translating receipt data to plain text and writing the
output to a file instead of sending it to a printer.

Adds a new stock printer named "Hatch File Writer" to the print
configuration interface.

Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Jason Boyer <JBoyer@eoli.info>
Open-ILS/src/templates/staff/admin/workstation/t_print_config.tt2
Open-ILS/web/js/ui/default/staff/admin/workstation/app.js
Open-ILS/web/js/ui/default/staff/services/hatch.js
Open-ILS/web/js/ui/default/staff/services/print.js

index 043a446..4e40632 100644 (file)
                   <ul uib-dropdown-menu>
                     <li ng-repeat="printer in printers">
                       <a href='' ng-click="setPrinter(printer.name)">
-                        {{printer.name}}
+                        <span ng-if="printer.name == 'hatch_file_writer'">
+                          [% l('Hatch File Writer') %]
+                        </span>
+                        <span ng-if="printer.name != 'hatch_file_writer'">
+                          {{printer.name}}
+                        </span>
                       </a>
                     </li>
                   </ul>
                 <input ng-if="printers[0] && !printConfig[context]" type="text" 
                   class="form-control" disabled="disabled"
                   value="[% l('No Printer Selected') %]">
-                <input ng-if="printConfig[context].printer" type="text" 
-                  class="form-control" disabled="disabled"
+                <input ng-if="useFileWriter()" type="text" disabled="disabled"
+                  class="form-control" value="[% l('Hatch File Writer') %]"/>
+                <input ng-if="printConfig[context].printer && !useFileWriter()" 
+                  type="text" class="form-control" disabled="disabled"
                   value="{{printConfig[context].printer}}">
               </div><!-- /input-group -->
             </div><!-- col -->
             </div>
           </div><!-- row -->
 
-          <div class="row" ng-hide="isTestView || !hatchIsOpen()"> 
+          <div class="pad-vert" 
+            ng-show="!isTestView && hatchIsOpen() && useFileWriter()">
+            <div class="alert alert-info">
+              [% |l %]Hatch File Writer translates print output to plain text 
+and writes the content to a text file in the Hatch profile directory.  No
+additional settings are required.[% END %]
+            </div>
+          </div>
+
+          <div class="row" 
+            ng-show="!isTestView && hatchIsOpen() && !useFileWriter()"
             <div class="col-md-10">
               <div class="row">
                 <div class="col-md-1"></div>
                     '{{printerOptions.defaultPaperSource}}') %]</span>
                 </div>
               </div>
-
               <div class="row">
                 <div class="col-md-1"></div>
                 <div class="col-md-2">
index b8d9a95..9dfbc9c 100644 (file)
@@ -279,8 +279,12 @@ function($scope , egCore) {
     }
 
     function loadPrinterOptions(name) {
-        egCore.hatch.getPrinterOptions(name).then(
-            function(options) {$scope.printerOptions = options});
+        if (name == 'hatch_file_writer') {
+            $scope.printerOptions = {};
+        } else {
+            egCore.hatch.getPrinterOptions(name).then(
+                function(options) {$scope.printerOptions = options});
+        }
     }
 
     $scope.setPrinter = function(name) {
@@ -311,6 +315,13 @@ function($scope , egCore) {
         }
     }
 
+    $scope.useFileWriter = function() {
+        return (
+            $scope.printConfig[$scope.context] &&
+            $scope.printConfig[$scope.context].printer == 'hatch_file_writer'
+        );
+    }
+
     // Load startup data....
     // Don't bother talking to Hatch if it's not there.
     if (!egCore.hatch.hatchAvailable) return;
@@ -320,6 +331,12 @@ function($scope , egCore) {
     .then(function(printers) { 
         $scope.printers = printers;
 
+        printers.push({
+            // We need a static name for saving configs.
+            // Human-friendly label is set in the template.
+            name: 'hatch_file_writer' 
+        });
+
         var def = $scope.getPrinterByAttr('is-default', true);
         if (!def && printers.length) def = printers[0];
 
index 6b56520..1e907c5 100644 (file)
@@ -162,6 +162,13 @@ angular.module('egCoreMod')
 
         return service.getPrintConfig(context).then(
             function(config) {
+                if (config.printer == 'hatch_file_writer') {
+                    if (contentType == 'text/html') {
+                        content = service.html2txt(content);
+                    }
+                    return service.setRemoteItem(
+                        'receipt.' + context + '.txt', content, true);
+                } 
                 // print configuration retrieved; print
                 return service.attemptHatchDelivery({
                     action : 'print',
@@ -704,12 +711,15 @@ angular.module('egCoreMod')
 
 
     // set the value for a stored or new item
-    service.setRemoteItem = function(key, value) {
+    // When "bare" is true, the value will not be JSON-encoded
+    // on the file system.
+    service.setRemoteItem = function(key, value, bare) {
         service.keyCache[key] = value;
         return service.attemptHatchDelivery({
             key : key, 
             content : value, 
             action : 'set',
+            bare: bare
         });
     }
 
@@ -1002,6 +1012,60 @@ angular.module('egCoreMod')
         return $q.when(null);
     }
 
+    // COPIED FROM XUL util/text.js
+    service.reverse_preserve_string_in_html = function( text ) {
+        text = text.replace(/&amp;/g, '&');
+        text = text.replace(/&quot;/g, '"');
+        text = text.replace(/&#39;/g, "'");
+        text = text.replace(/&nbsp;/g, ' ');
+        text = text.replace(/&lt;/g, '<');
+        text = text.replace(/&gt;/g, '>');
+        return text;
+    }
+
+    // COPIED FROM XUL util/print.js
+    service.html2txt = function(html) {
+        var lines = html.split(/\n/);
+        var new_lines = [];
+        for (var i = 0; i < lines.length; i++) {
+            var line = lines[i];
+            if (!line) {
+                new_lines.push(line);
+                continue;
+            }
+
+            // This undoes the util.text.preserve_string_in_html 
+            // call that spine_label.js does
+            line = service.reverse_preserve_string_in_html(line);
+
+            // This looks for @hex attributes containing 2-digit hex 
+            // codes, and converts them into real characters
+            line = line.replace(/(<.+?)hex=['"](.+?)['"](.*?>)/gi, 
+                function(str,p1,p2,p3,offset,s) {
+
+                var raw_chars = '';
+                var hex_chars = p2.match(/[0-9,a-f,A-F][0-9,a-f,A-F]/g);
+                for (var j = 0; j < hex_chars.length; j++) {
+                    raw_chars += String.fromCharCode( parseInt(hex_chars[j],16) );
+                }
+                return p1 + p3 + raw_chars;
+            });
+
+            line = line.replace(/<head.*?>.*?<\/head>/gi, '');
+            line = line.replace(/<br.*?>/gi,'\r\n');
+            line = line.replace(/<table.*?>/gi,'');
+            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) { new_lines.push(line); }
+        }
+
+        return new_lines.join('\n');
+    }
+
     // The only requirement for opening Hatch is that the DOM be loaded.
     // Open the connection now so its state will be immediately available.
     service.openHatch();
index d12a6cd..ae11082 100644 (file)
@@ -106,16 +106,18 @@ function($q , $window , $timeout , $http , egHatch , egAuth , egIDL , egOrg , eg
 
         if (args.content_type == 'text/html') {
             promise = service.ingest_print_content(
-                args.content_type, args.content, args.scope);
+                args.content_type, args.content, args.scope
+            ).then(function(html) {
+                // For good measure, wrap the compiled HTML in container tags.
+                return "<html><body>" + html + "</body></html>";
+            });
         } else {
             // text content requires no compilation for remote printing.
             promise = $q.when(args.content);
         }
 
-        return promise.then(function(html) {
-            // For good measure, wrap the compiled HTML in container tags.
-            html = "<html><body>" + html + "</body></html>";
-            service.last_print.content = html;
+        return promise.then(function(content) {
+            service.last_print.content = content;
             service.last_print.context = args.context || 'default';
             service.last_print.content_type = args.content_type;
             service.last_print.show_dialog = args.show_dialog;