From 137571476f1f5c69c0fafd464cb39c95b13c2136 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Klaus=20W=C3=B6lfel?= <klaus@nexedi.com>
Date: Mon, 16 Mar 2015 11:35:19 +0000
Subject: [PATCH] erp5_printer: print with html5 canvas to support chinese
 characters

---
 .../SaleOrder_viewEpsonPrintout.xml           |   2 +-
 .../erp5_epson_canvas_printout.js.xml         | 449 ++++++++++++++++++
 2 files changed, 450 insertions(+), 1 deletion(-)
 create mode 100644 bt5/erp5_printer/SkinTemplateItem/portal_skins/erp5_printer/erp5_epson_canvas_printout.js.xml

diff --git a/bt5/erp5_printer/SkinTemplateItem/portal_skins/erp5_printer/SaleOrder_viewEpsonPrintout.xml b/bt5/erp5_printer/SkinTemplateItem/portal_skins/erp5_printer/SaleOrder_viewEpsonPrintout.xml
index d86ffeaa80..f7f119ebf7 100644
--- a/bt5/erp5_printer/SkinTemplateItem/portal_skins/erp5_printer/SaleOrder_viewEpsonPrintout.xml
+++ b/bt5/erp5_printer/SkinTemplateItem/portal_skins/erp5_printer/SaleOrder_viewEpsonPrintout.xml
@@ -52,7 +52,7 @@
     <title>Receipt</title>\n
     <script type="text/javascript" tal:attributes=\'src python:"%s/jquery/core/jquery.js" % portal_url\'></script>\n
     <script type="text/javascript" tal:attributes=\'src python:"%s/epos-print-4.1.0.js" % portal_url\' ></script>\n
-    <script type="text/javascript" tal:attributes=\'src python:"%s/erp5_epson_printout.js" % portal_url\' ></script>\n
+    <script type="text/javascript" tal:attributes=\'src python:"%s/erp5_epson_canvas_printout.js" % portal_url\' ></script>\n
     <link rel="license" href="http://www.opensource.org/licenses/mit-license/">\n
   </head>\n
   <body style="background:white">\n
diff --git a/bt5/erp5_printer/SkinTemplateItem/portal_skins/erp5_printer/erp5_epson_canvas_printout.js.xml b/bt5/erp5_printer/SkinTemplateItem/portal_skins/erp5_printer/erp5_epson_canvas_printout.js.xml
new file mode 100644
index 0000000000..a419f74f44
--- /dev/null
+++ b/bt5/erp5_printer/SkinTemplateItem/portal_skins/erp5_printer/erp5_epson_canvas_printout.js.xml
@@ -0,0 +1,449 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="DTMLMethod" module="OFS.DTMLMethod"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>_Cacheable__manager_id</string> </key>
+            <value> <string>http_cache</string> </value>
+        </item>
+        <item>
+            <key> <string>__name__</string> </key>
+            <value> <string>erp5_epson_canvas_printout.js</string> </value>
+        </item>
+        <item>
+            <key> <string>_proxy_roles</string> </key>
+            <value>
+              <tuple/>
+            </value>
+        </item>
+        <item>
+            <key> <string>_vars</string> </key>
+            <value>
+              <dictionary/>
+            </value>
+        </item>
+        <item>
+            <key> <string>globals</string> </key>
+            <value>
+              <dictionary/>
+            </value>
+        </item>
+        <item>
+            <key> <string>raw</string> </key>
+            <value> <string encoding="cdata"><![CDATA[
+
+/*\n
+Copyright (c) 20xx-2006 Nexedi SARL and Contributors. All Rights Reserved.\n
+\n
+This program is Free Software; you can redistribute it ahand/or\n
+modify it under the terms of the GNU General Public License\n
+as published by the Free Software Foundation; either version 2\n
+of the License, or (at your option) any later version.\n
+cful,\n
+but WITHOUT ANY WARRANTY; without even the implied warranty of\n
+MERCHANTABILITY or FITNESS FgOR A PARTICULAR PURPOSE.  See the\n
+GNU General Public License for more details.\n
+\n
+You should have received a copy of the GNU General Public License\n
+along with this program; if not, write to the Free Software\n
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n
+*/\n
+var y;\n
+\n
+function draw(canvas) {\n
+  y = 0;\n
+  context = canvas.getContext(\'2d\');\n
+  //add logo\n
+  context.drawImage($(\'#company_logo\')[0], (canvas.width-330)/2, 0, 330, 75);\n
+  addFeed(75);\n
+  addFeed();\n
+  addFeed();\n
+  \n
+  //add company information\n
+  $("#company-info div").each(function(){\n
+      addText(context, $(this).text());\n
+  });\n
+  addFeed();\n
+\n
+  //add invoice information\n
+  addText(context, $.trim($("#invoice_information_title").text()), {\n
+    size: 36,\n
+    line: 46\n
+  });\n
+  $("#invoice_information_detail div").each(function(){\n
+    $(this).find("span").each(function(i){\n
+      if(i==0){\n
+        addText(context, $.trim($(this).text()), {bold: true, nobreak: true});\n
+      }\n
+      else {\n
+        addText(context, $.trim($(this).text()), {x: 300});\n
+      }\n
+    });\n
+  });\n
+  addFeed(100);\n
+\n
+  //add products line\n
+  $("#invoice_line thead tr").each(function(j){\n
+    var textPosition=0;\n
+    $(this).find("th").each(function(i){\n
+      addText(context, $.trim($(this).text()), {\n
+        x: textPosition,\n
+        bold: true,\n
+        nobreak: true\n
+      });\n
+      switch(i)\n
+      {\n
+      case 0:\n
+        textPosition+=285;\n
+        break;\n
+      case 1:\n
+        textPosition+=100;        \n
+        break;\n
+      case 3:\n
+        textPosition+=60;\n
+        break;\n
+      }\n
+    });\n
+  });\n
+  addFeed();\n
+  $("#invoice_line tbody tr").each(function(){\n
+    var textPosition=0;\n
+    $(this).find("td").each(function(i){\n
+        switch(i)\n
+        {\n
+        case 0:\n
+          var content=$.trim($(this).text());\n
+          content_len=content.length;\n
+          if(content_len>20)\n
+            content=content.substr(0,12)+"..."+content.substr(content_len-4,5);\n
+          addText(context, content, {x:textPosition, nobreak: true});\n
+          textPosition+=330;\n
+          break;\n
+        case 1:\n
+          var content=parseFloat($.trim($(this).text())).toFixed(1);\n
+          var content_len=content.length;\n
+          if(content_len>10)\n
+            content=parseFloat(content).toExponential(2);\n
+          else if(content_len>8&&content_len<11)\n
+            content=parseInt(content);\n
+          addText(context, content, {\n
+            x:textPosition,\n
+            nobreak: true,\n
+            textalign: \'right\'\n
+          });\n
+          textPosition+=140;\n
+          break;\n
+        case 2:\n
+          priceControl(context, $.trim($(this).text()), {\n
+            x:textPosition,\n
+            textalign: \'right\'\n
+          });\n
+          textPosition+=60;\n
+          break;\n
+        case 3:\n
+          var content=$.trim($(this).text());\n
+          var content_len=content.length;\n
+          content=content.substr(0,5);\n
+          addText(context, content, {x:textPosition, nobreak: true});\n
+          break;\n
+        case 4:\n
+          priceControl(context, $.trim($(this).text()), {\n
+            x:60,\n
+            textalign: \'right\'\n
+          });\n
+          break;\n
+        }\n
+    });\n
+  });\n
+\n
+  addText(context, "===========", {x:360});\n
+  addText(context, $.trim($("#total_price tr th").text()), {\n
+    bold: true,\n
+    size: 28,\n
+    line: 36,\n
+    nobreak: true\n
+  });\n
+  priceControl(context, $.trim($("#total_price tr td").text()), {\n
+    x:480,\n
+    textalign: \'right\',\n
+    bold: true,\n
+    size: 28,\n
+    line: 36\n
+  });\n
+  addFeed();\n
+\n
+  if ($("#total_discount").length > 0) {\n
+    builder.addText(context, $.trim($("#total_discount tr th").text()), {x:130});\n
+    priceControl(context, $.trim($("#total_discount tr td").text()), {x:385});\n
+    addFeed();\n
+\n
+    addText(context, "===========", x=360);\n
+    addText(context, $.trim($("#total_price_with_discount tr th").text()), {x:130});\n
+    priceControl(context, $.trim($("#total_price_with_discount tr td").text()), {x:385});\n
+  }\n
+\n
+  addFeed();\n
+  // append date and time\n
+  var now = new Date();\n
+  addText(context, now.toDateString() + \' \' + now.toTimeString().slice(0, 8) + \'\\n\');\n
+  \n
+  return canvas;\n
+}\n
+\n
+function addText(context, text, options) {\n
+  if (!options) options = {};\n
+  if (!options.x) options.x = 0;\n
+  if (!options.bold) options.bold = false;\n
+  if (!options.size) options.size = 24;\n
+  if (!options.line) options.line = 30;\n
+  if (!options.nobreak) options.nobreak = false;\n
+  if (!options.font) options.font = \'Courier\';\n
+  if (!options.textalign) options.textalign = \'left\';\n
+  context.font =\n
+      (options.bold ? \'bold \' : \'normal \') + options.size + \'px \' + options.font;\n
+  context.textAlign = options.textalign;\n
+  context.fillText(text, options.x, y);\n
+  // line feed\n
+  if (options.nobreak === false) {\n
+    y = y + options.line;\n
+  }\n
+  console.log(options.line);\n
+}\n
+\n
+function addFeed(px) {\n
+  if (!px) px = 30;\n
+  y = y + px;\n
+}\n
+\n
+// if the price number length > 13 use the Scientific notation,\n
+// if the length==11,12,13 , just need to remove the number after Decimal point\n
+function priceControl(context, price, options){\n
+  var content=parseFloat(price).toFixed(2);\n
+  var content_len=content.length;\n
+  if(content_len>13)\n
+    content=parseFloat(content).toExponential(4);\n
+  else if(content_len>10&&content_len<14)\n
+    content=parseInt(content);\n
+  addText(context, content, options);\n
+}\n
+\n
+monitoring_enabled = 0;\n
+\n
+function enableEpsonMonitoring(printer_url){\n
+  if (monitoring_enabled == 0) {\n
+    monitoring_enabled = 1;\n
+  } else { \n
+     return true;\n
+  };\n
+  var monitoting_epos = new epson.ePOSPrint(printer_url);\n
+  monitoting_epos.interval = 5000;\n
+  monitoting_epos.onpoweroff = function () {\n
+    msg = "ERROR: Unable to connect to the printer at the URL (Unreachable or Power off): " + monitoting_epos.address\n
+    $("#js_error_log").append("<div>" + msg + "</div>");\n
+    console.log(msg);\n
+  }\n
+\n
+  monitoting_epos.onoffline = function () {\n
+    msg = "ERROR: Printer seems offline check if you can access printer at the URL (Unreachable or Power off): " + monitoting_epos.address\n
+    $("#js_error_log").append("<div>" + msg + "</div>");\n
+    console.log(msg);\n
+  }\n
+\n
+  monitoting_epos.online = function () {\n
+    msg = "INFO: Printer is online at" + monitoting_epos.address\n
+    $("#js_error_log").append("<div>" + msg + "</div>");\n
+    console.log(msg);\n
+  }\n
+\n
+  monitoting_epos.onpaperend = function () {\n
+    msg = "WARNNING: Printer report that paper is finished!"\n
+    $("#js_error_log").append("<div>" + msg + "</div>");\n
+    console.log(msg);\n
+  };\n
+\n
+  monitoting_epos.onpapernearend = function () {\n
+    msg = "WARNNING: Printer report that paper is near finished from finish!"\n
+    $("#js_error_log").append("<div>" + msg + "</div>");\n
+    console.log(msg);\n
+  };\n
+\n
+  monitoting_epos.onpaperok = function () {\n
+    msg = "INFO: Printer report that paper is OK."\n
+    $("#js_error_log").append("<div>" + msg + "</div>");\n
+    console.log(msg);\n
+  };\n
+\n
+  monitoting_epos.ondrawerclosed = function () {\n
+    msg = "INFO: Printer report that drawer is closed."\n
+    $("#js_error_log").append("<div>" + msg + "</div>");\n
+    console.log(msg);\n
+  };\n
+\n
+  monitoting_epos.ondraweropen = function () {\n
+    msg = "INFO: Printer report that drawer is open."\n
+    $("#js_error_log").append("<div>" + msg + "</div>");\n
+    console.log(msg);\n
+  };\n
+\n
+  monitoting_epos.oncoveropen = function () {\n
+    msg = "WARNNING: Printer report that cover is open! Please close it!"\n
+    $("#js_error_log").append("<div>" + msg + "</div>");\n
+    console.log(msg);\n
+  };\n
+\n
+  monitoting_epos.oncoverok = function () {\n
+    msg = "INFO: Printer cover is closed."\n
+    $("#js_error_log").append("<div>" + msg + "</div>");\n
+    console.log(msg);\n
+  };\n
+\n
+  monitoting_epos.onstatuschange = function (status) {\n
+    msg = "INFO: Printer changed status to :" + status;\n
+    $("#js_error_log").append("<div>" + msg + "</div>");\n
+    console.log(msg);\n
+  };\n
+\n
+  monitoting_epos.open();\n
+}\n
+  \n
+function printInvoiceOnEpson(printer_url){\n
+  enableEpsonMonitoring(printer_url);\n
+  // create print object\n
+  var epos = new epson.CanvasPrint(printer_url);\n
+  // set paper cutting\n
+  epos.cut = true;\n
+  \n
+  // register callback function\n
+  epos.onreceive = function (res) {\n
+    // Obtain the print result and error code\n
+    var msg = \'Print\' + (res.success ? \'Success\' : \'Failure\') + \'\\nCode:\' + res.code;\n
+\n
+    switch(res.code) {\n
+      case "EPTR_AUTOMATICAL":\n
+        msg += \' An automatically recoverable error occurred\\n\';\n
+        break;\n
+      case "EPTR_COVER_OPEN":\n
+        msg += \' A cover open error occurred\\n\';\n
+        break;\n
+      case "EPTR_CUTTER":\n
+        msg += \' An autocutter error occurred\\n\';\n
+        break;\n
+      case \'EPTR_MECHANICAL\':\n
+        msg += \' A mechanical error occurred\\n\';\n
+        break;\n
+      case \'EPTR_REC_EMPTY\':\n
+        msg += \' No paper in roll paper end sensor\\n\';\n
+        break;\n
+      case \'EPTR_UNRECOVERABLE\':\n
+        msg += \' An unrecoverable error occurred\\n\';\n
+        break;\n
+      case \'SchemaError\':\n
+        msg += \' The request document contains a syntax error\\n\';\n
+        break;\n
+      case \'DeviceNotFound\':\n
+        msg += \' The printer with the specified device ID does not exist\\n\';\n
+        break;\n
+      case \'PrintSystemError\':\n
+        msg += \' An error occurred on the printing system\\n\';\n
+        break;\n
+      case \'EX_BADPORT\':\n
+        msg += \' An error was detected on the communication port\\n\';\n
+        break;\n
+      case \'EX_TIMEOUT\':\n
+        msg += \' A print timeout occurred\\n\';\n
+        break;\n
+     }\n
+    \n
+    msg += \'\\nStatus:\\n\';\n
+    // Obtain the printer status\n
+    var asb = res.status;\n
+    if (asb & epos.ASB_NO_RESPONSE) {\n
+      msg += \' No printer response\\n\';\n
+    }\n
+    if (asb & epos.ASB_PRINT_SUCCESS) {\n
+      msg += \' Print complete\\n\';\n
+    }\n
+    if (asb & epos.ASB_DRAWER_KICK) {\n
+      msg += \' Status of the drawer kick number 3 connector pin = "H"\\n\';\n
+    }\n
+    if (asb & epos.ASB_OFF_LINE) {\n
+      msg += \' Offline status\\n\';\n
+    }\n
+    if (asb & epos.ASB_COVER_OPEN) {\n
+      msg += \' Cover is open\\n\';\n
+    }\n
+    if (asb & epos.ASB_PAPER_FEED) {\n
+      msg += \' Paper feed switch is feeding paper\\n\';\n
+    }\n
+    if (asb & epos.ASB_WAIT_ON_LINE) {\n
+      msg += \' Waiting for online recovery\\n\';\n
+    }\n
+    if (asb & epos.ASB_PANEL_SWITCH) {\n
+      msg += \' Panel switch is ON\\n\';\n
+    }\n
+    if (asb & epos.ASB_MECHANICAL_ERR) {\n
+      msg += \' Mechanical error generated\\n\';\n
+    }\n
+    if (asb & epos.ASB_AUTOCUTTER_ERR) {\n
+      msg += \' Auto cutter error generated\\n\';\n
+    }\n
+    if (asb & epos.ASB_UNRECOVER_ERR) {\n
+      msg += \' Unrecoverable error generated\\n\';\n
+    }\n
+    if (asb & epos.ASB_AUTORECOVER_ERR) {\n
+      msg += \' Auto recovery error generated\\n\';\n
+    }\n
+    if (asb & epos.ASB_RECEIPT_NEAR_END) {\n
+      msg += \' No paper in the roll paper near end detector\\n\';\n
+    }\n
+    if (asb & epos.ASB_RECEIPT_END) {\n
+      msg += \' No paper in the roll paper end detector\\n\';\n
+    }\n
+    if (asb & epos.ASB_BUZZER) {\n
+      msg += \' Sounding the buzzer (limited model)\\n\';\n
+    }\n
+    if (asb & epos.ASB_SPOOLER_IS_STOPPED) {\n
+      msg += \' Stop the spooler\\n\';\n
+    }\n
+    // Display in the dialog box\n
+    console.log(msg);\n
+    $("#js_error_log").append("<div>" + msg + "</div>");\n
+  }\n
+\n
+  // register callback function\n
+  epos.onerror = function (err) {\n
+    $("#js_error_log").append("<div> Error Status: " + err.status + " Response: " + err.responseText + "</div>");\n
+    if (err.status == 0) {\n
+      $("#js_error_log").append("<div> Make sure you are able to access the printer at: " + epos.address + "</div>");\n
+    };\n
+    console.log("Error Status: " + err.status + "Response: " + err.responseText);\n
+  }\n
+\n
+  // send\n
+  $("#js_error_log").append("<div> The print will start soon...</div>");\n
+  \n
+  // first draw on dummy canvas to get canvas height\n
+  // needed because setting size clears canvas\n
+  draw(document.createElement("canvas"));\n
+  console.log(y);\n
+  var canvas = document.createElement("canvas");\n
+  canvas.width = 512;\n
+  canvas.height = y;\n
+  epos.print(draw(canvas));\n
+}\n
+
+
+]]></string> </value>
+        </item>
+        <item>
+            <key> <string>title</string> </key>
+            <value> <string></string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
-- 
2.30.9