Overview

Namespaces

  • Neodynamic
    • SDK
      • Web
  • PHP

Classes

  • ClientPrinter
  • ClientPrintJob
  • ClientPrintJobGroup
  • DefaultPrinter
  • InstalledPrinter
  • NetworkPrinter
  • ParallelPortPrinter
  • PrintFile
  • PrintFilePDF
  • PrintFileTXT
  • PrintOrientation
  • PrintRotation
  • SerialPortHandshake
  • SerialPortParity
  • SerialPortPrinter
  • SerialPortStopBits
  • TextAlignment
  • UserSelectedPrinter
  • Utils
  • WebClientPrint
  • Overview
  • Namespace
  • Class
  • Tree
   1: <?php
   2: 
   3: namespace Neodynamic\SDK\Web;
   4: use Exception;
   5: use ZipArchive;
   6: 
   7: // Setting WebClientPrint
   8: WebClientPrint::$licenseOwner = '';
   9: WebClientPrint::$licenseKey = '';
  10: 
  11: //Set wcpcache folder RELATIVE to WebClientPrint.php file
  12: //FILE WRITE permission on this folder is required!!!
  13: WebClientPrint::$wcpCacheFolder = 'wcpcache/';
  14: 
  15: /**
  16:  * WebClientPrint provides functions for registering the "WebClientPrint for PHP" solution 
  17:  * script code in PHP web pages as well as for processing client requests and managing the
  18:  * internal cache.
  19:  * 
  20:  * @author Neodynamic <http://neodynamic.com/support>
  21:  * @copyright (c) 2018, Neodynamic SRL
  22:  * @license http://neodynamic.com/eula Neodynamic EULA
  23:  */
  24: class WebClientPrint {
  25:    
  26:     const VERSION = '4.0.18.0';
  27:     const CLIENT_PRINT_JOB = 'clientPrint';
  28:     const WCP = 'WEB_CLIENT_PRINT';
  29:     const WCP_SCRIPT_AXD_GET_PRINTERS = 'getPrinters';
  30:     const WCP_SCRIPT_AXD_GET_PRINTERSINFO = 'getPrintersInfo';
  31:     const WCPP_SET_PRINTERS = 'printers';
  32:     const WCPP_SET_PRINTERSINFO = 'printersInfo';
  33:     const WCP_SCRIPT_AXD_GET_WCPPVERSION = 'getWcppVersion';
  34:     const WCPP_SET_VERSION = 'wcppVer';
  35:     const GEN_WCP_SCRIPT_URL = 'u';
  36:     const GEN_DETECT_WCPP_SCRIPT = 'd';
  37:     const SID = 'sid';
  38:     const PING = 'wcppping';
  39:     
  40:     const WCP_CACHE_WCPP_INSTALLED = 'WCPP_INSTALLED';
  41:     const WCP_CACHE_WCPP_VER = 'WCPP_VER';
  42:     const WCP_CACHE_PRINTERS = 'PRINTERS';
  43:     const WCP_CACHE_PRINTERSINFO = 'PRINTERSINFO';
  44:     
  45:     
  46:     /**
  47:      * Gets or sets the License Owner
  48:      * @var string 
  49:      */
  50:     static $licenseOwner = '';
  51:     /**
  52:      * Gets or sets the License Key
  53:      * @var string
  54:      */
  55:     static $licenseKey = '';
  56:     /**
  57:      * Gets or sets the ABSOLUTE URL to WebClientPrint.php file
  58:      * @var string
  59:      */
  60:     static $webClientPrintAbsoluteUrl = '';
  61:     /**
  62:      * Gets or sets the wcpcache folder URL RELATIVE to WebClientPrint.php file. 
  63:      * FILE WRITE permission on this folder is required!!!
  64:      * @var string
  65:      */
  66:     static $wcpCacheFolder = '';
  67:     
  68:     /**
  69:      * Adds a new entry to the built-in file system cache. 
  70:      * @param string $sid The user's session id
  71:      * @param string $key The cache entry key
  72:      * @param string $val The data value to put in the cache
  73:      * @throws Exception
  74:      */
  75:     public static function cacheAdd($sid, $key, $val){
  76:         if (Utils::isNullOrEmptyString(self::$wcpCacheFolder)){
  77:             throw new Exception('WebClientPrint wcpCacheFolder is missing, please specify it.');
  78:         }
  79:         if (Utils::isNullOrEmptyString($sid)){
  80:             throw new Exception('WebClientPrint FileName cache is missing, please specify it.');
  81:         }
  82:         $cacheFileName = (Utils::strEndsWith(self::$wcpCacheFolder, '/')?self::$wcpCacheFolder:self::$wcpCacheFolder.'/').$sid.'.wcpcache';
  83:         $dataWCPP_VER = '';
  84:         $dataPRINTERS = '';
  85:         $dataPRINTERSINFO = '';
  86:             
  87:         if(file_exists($cacheFileName)){
  88:             $cache_info = parse_ini_file($cacheFileName);
  89:             
  90:             $dataWCPP_VER = $cache_info[self::WCP_CACHE_WCPP_VER];
  91:             $dataPRINTERS = $cache_info[self::WCP_CACHE_PRINTERS];
  92:             $dataPRINTERS = $cache_info[self::WCP_CACHE_PRINTERSINFO];
  93:         }
  94:         
  95:         if ($key === self::WCP_CACHE_WCPP_VER){
  96:             $dataWCPP_VER = self::WCP_CACHE_WCPP_VER.'='.'"'.$val.'"';
  97:             $dataPRINTERS = self::WCP_CACHE_PRINTERS.'='.'"'.$dataPRINTERS.'"';
  98:             $dataPRINTERSINFO = self::WCP_CACHE_PRINTERSINFO.'='.'"'.$dataPRINTERSINFO.'"';
  99:         } else if ($key === self::WCP_CACHE_PRINTERS){
 100:             $dataWCPP_VER = self::WCP_CACHE_WCPP_VER.'='.'"'.$dataWCPP_VER.'"';
 101:             $dataPRINTERS = self::WCP_CACHE_PRINTERS.'='.'"'.$val.'"';
 102:             $dataPRINTERSINFO = self::WCP_CACHE_PRINTERSINFO.'='.'"'.$dataPRINTERSINFO.'"';
 103:         } else if ($key === self::WCP_CACHE_PRINTERSINFO){
 104:             $dataWCPP_VER = self::WCP_CACHE_WCPP_VER.'='.'"'.$dataWCPP_VER.'"';
 105:             $dataPRINTERS = self::WCP_CACHE_PRINTERS.'='.'"'.$dataPRINTERS.'"';
 106:             $dataPRINTERSINFO = self::WCP_CACHE_PRINTERSINFO.'='.'"'.$val.'"';
 107:         }
 108: 
 109:         $data = $dataWCPP_VER.chr(13).chr(10).$dataPRINTERS.chr(13).chr(10).$dataPRINTERSINFO;
 110:         $handle = fopen($cacheFileName, 'w') or die('Cannot open file:  '.$cacheFileName);  
 111:         fwrite($handle, $data);
 112:         fclose($handle);
 113:         
 114:     }
 115:     
 116:     /**
 117:      * Gets a value from the built-in file system cache based on the specified sid & key 
 118:      * @param string $sid The user's session id
 119:      * @param string $key The cache entry key
 120:      * @return string Returns the value from the cache for the specified sid & key if it's found; or an empty string otherwise.
 121:      * @throws Exception
 122:      */
 123:     public static function cacheGet($sid, $key){
 124:         if (Utils::isNullOrEmptyString(self::$wcpCacheFolder)){
 125:             throw new Exception('WebClientPrint wcpCacheFolder is missing, please specify it.');
 126:         }
 127:         if (Utils::isNullOrEmptyString($sid)){
 128:             throw new Exception('WebClientPrint FileName cache is missing, please specify it.');
 129:         }
 130:         $cacheFileName = (Utils::strEndsWith(self::$wcpCacheFolder, '/')?self::$wcpCacheFolder:self::$wcpCacheFolder.'/').$sid.'.wcpcache';
 131:         if(file_exists($cacheFileName)){
 132:             $cache_info = parse_ini_file($cacheFileName, FALSE, INI_SCANNER_RAW);
 133:                 
 134:             if($key===self::WCP_CACHE_WCPP_VER || $key===self::WCP_CACHE_WCPP_INSTALLED){
 135:                 return $cache_info[self::WCP_CACHE_WCPP_VER];
 136:             }else if($key===self::WCP_CACHE_PRINTERS){
 137:                 return $cache_info[self::WCP_CACHE_PRINTERS];
 138:             }else if($key===self::WCP_CACHE_PRINTERSINFO){
 139:                 return $cache_info[self::WCP_CACHE_PRINTERSINFO];
 140:             }else{
 141:                 return '';
 142:             }
 143:         }else{
 144:             return '';
 145:         }
 146:     }
 147:     
 148:     /**
 149:      * Cleans the built-in file system cache
 150:      * @param integer $minutes The number of minutes after any files on the cache will be removed.
 151:      */
 152:     public static function cacheClean($minutes){
 153:         if (!Utils::isNullOrEmptyString(self::$wcpCacheFolder)){
 154:             $cacheDir = (Utils::strEndsWith(self::$wcpCacheFolder, '/')?self::$wcpCacheFolder:self::$wcpCacheFolder.'/');
 155:             if ($handle = opendir($cacheDir)) {
 156:                  while (false !== ($file = readdir($handle))) {
 157:                     if ($file!='.' && $file!='..' && (time()-filectime($cacheDir.$file)) > (60*$minutes)) {
 158:                         unlink($cacheDir.$file);
 159:                     }
 160:                  }
 161:                  closedir($handle);
 162:             }
 163:         }
 164:     }
 165:     
 166:     /**
 167:      * Returns script code for detecting whether WCPP is installed at the client machine.
 168:      *
 169:      * The WCPP-detection script code ends with a 'success' or 'failure' status.
 170:      * You can handle both situation by creating two javascript functions which names 
 171:      * must be wcppDetectOnSuccess() and wcppDetectOnFailure(). 
 172:      * These two functions will be automatically invoked by the WCPP-detection script code.
 173:      * 
 174:      * The WCPP-detection script uses a delay time variable which by default is 10000 ms (10 sec). 
 175:      * You can change it by creating a javascript global variable which name must be wcppPingDelay_ms. 
 176:      * For example, to use 5 sec instead of 10, you should add this to your script: 
 177:      *   
 178:      * var wcppPingDelay_ms = 5000;
 179:      *    
 180:      * @param string $webClientPrintControllerAbsoluteUrl The Absolute URL to the WebClientPrintController file.
 181:      * @param string $sessionID The current Session ID.
 182:      * @return string A [script] tag linking to the WCPP-detection script code.
 183:      * @throws Exception
 184:      */
 185:     public static function createWcppDetectionScript($webClientPrintControllerAbsoluteUrl, $sessionID){
 186:         
 187:         if (Utils::isNullOrEmptyString($webClientPrintControllerAbsoluteUrl) || 
 188:             !Utils::strStartsWith($webClientPrintControllerAbsoluteUrl, 'http')){
 189:             throw new Exception('WebClientPrintController absolute URL is missing, please specify it.');
 190:         }
 191:         if (Utils::isNullOrEmptyString($sessionID)){
 192:             throw new Exception('Session ID is missing, please specify it.');
 193:         }
 194:         
 195:         $url = $webClientPrintControllerAbsoluteUrl.'?'.self::GEN_DETECT_WCPP_SCRIPT.'='.$sessionID;
 196:         return '<script src="'.$url.'" type="text/javascript"></script>';
 197:          
 198:     }
 199:     
 200:     
 201:     /**
 202:      * Returns a [script] tag linking to the WebClientPrint script code by using 
 203:      * the specified URL for the client print job generation.
 204:      * 
 205:      * @param string $webClientPrintControllerAbsoluteUrl The Absolute URL to the WebClientPrintController file.
 206:      * @param string $clientPrintJobAbsoluteUrl The Absolute URL to the PHP file that creates ClientPrintJob objects.
 207:      * @paran string $sessionID The current Session ID.
 208:      * @return string A [script] tag linking to the WebClientPrint script code by using the specified URL for the client print job generation.
 209:      * @throws Exception
 210:      */
 211:     public static function createScript($webClientPrintControllerAbsoluteUrl, $clientPrintJobAbsoluteUrl, $sessionID){
 212:         if (Utils::isNullOrEmptyString($webClientPrintControllerAbsoluteUrl) || 
 213:             !Utils::strStartsWith($webClientPrintControllerAbsoluteUrl, 'http')){
 214:             throw new Exception('WebClientPrintController absolute URL is missing, please specify it.');
 215:         }
 216:         if (Utils::isNullOrEmptyString($clientPrintJobAbsoluteUrl) || 
 217:             !Utils::strStartsWith($clientPrintJobAbsoluteUrl, 'http')){
 218:             throw new Exception('ClientPrintJob absolute URL is missing, please specify it.');
 219:         }
 220:         if (Utils::isNullOrEmptyString($sessionID)){
 221:             throw new Exception('Session ID is missing, please specify it.');
 222:         }
 223:         
 224:         
 225:         $wcpHandler = $webClientPrintControllerAbsoluteUrl.'?';
 226:         $wcpHandler .= self::VERSION;
 227:         $wcpHandler .= '&';
 228:         $wcpHandler .= microtime(true);
 229:         $wcpHandler .= '&sid=';
 230:         $wcpHandler .= $sessionID;
 231:         $wcpHandler .= '&'.self::GEN_WCP_SCRIPT_URL.'=';
 232:         $wcpHandler .= base64_encode($clientPrintJobAbsoluteUrl);
 233:         return '<script src="'.$wcpHandler.'" type="text/javascript"></script>';
 234:     }
 235:     
 236:     
 237:     /**
 238:      * Generates the WebClientPrint scripts based on the specified query string. Result is stored in the HTTP Response Content
 239:      * 
 240:      * @param type $webClientPrintControllerAbsoluteUrl The Absolute URL to the WebClientPrintController file.
 241:      * @param type $queryString The Query String from current HTTP Request.
 242:      */
 243:     public static function generateScript($webClientPrintControllerAbsoluteUrl, $queryString)
 244:     {
 245:         if (Utils::isNullOrEmptyString($webClientPrintControllerAbsoluteUrl) || 
 246:             !Utils::strStartsWith($webClientPrintControllerAbsoluteUrl, 'http')){
 247:             throw new Exception('WebClientPrintController absolute URL is missing, please specify it.');
 248:         }
 249:         
 250:         parse_str($queryString, $qs);
 251:     
 252:         if(isset($qs[self::GEN_DETECT_WCPP_SCRIPT])){
 253:             
 254:             $curSID = $qs[self::GEN_DETECT_WCPP_SCRIPT];
 255:             $dynamicIframeId = 'i'.substr(uniqid(), 0, 3);
 256:             $absoluteWcpAxd = $webClientPrintControllerAbsoluteUrl.'?'.self::SID.'='.$curSID;
 257:             
 258:             $s1 = 'dmFyIGpzV0NQUD0oZnVuY3Rpb24oKXt2YXIgc2V0PDw8LU5FTy1IVE1MLUlELT4+Pj1mdW5jdGlvbigpe2lmKHdpbmRvdy5jaHJvbWUpeyQoJyM8PDwtTkVPLUhUTUwtSUQtPj4+JykuYXR0cignaHJlZicsJ3dlYmNsaWVudHByaW50aXY6Jythcmd1bWVudHNbMF0pO3ZhciBhPSQoJ2EjPDw8LU5FTy1IVE1MLUlELT4+PicpWzBdO3ZhciBldk9iaj1kb2N1bWVudC5jcmVhdGVFdmVudCgnTW91c2VFdmVudHMnKTtldk9iai5pbml0RXZlbnQoJ2NsaWNrJyx0cnVlLHRydWUpO2EuZGlzcGF0Y2hFdmVudChldk9iail9ZWxzZXskKCcjPDw8LU5FTy1IVE1MLUlELT4+PicpLmF0dHIoJ3NyYycsJ3dlYmNsaWVudHByaW50aXY6Jythcmd1bWVudHNbMF0pfX07cmV0dXJue2luaXQ6ZnVuY3Rpb24oKXtpZih3aW5kb3cuY2hyb21lKXskKCc8YSAvPicse2lkOic8PDwtTkVPLUhUTUwtSUQtPj4+J30pLmFwcGVuZFRvKCdib2R5Jyl9ZWxzZXskKCc8aWZyYW1lIC8+Jyx7bmFtZTonPDw8LU5FTy1IVE1MLUlELT4+PicsaWQ6Jzw8PC1ORU8tSFRNTC1JRC0+Pj4nLHdpZHRoOicxJyxoZWlnaHQ6JzEnLHN0eWxlOid2aXNpYmlsaXR5OmhpZGRlbjtwb3NpdGlvbjphYnNvbHV0ZSd9KS5hcHBlbmRUbygnYm9keScpfX0scGluZzpmdW5jdGlvbigpe3NldDw8PC1ORU8tSFRNTC1JRC0+Pj4oJzw8PC1ORU8tUElORy1VUkwtPj4+JysoYXJndW1lbnRzLmxlbmd0aD09MT8nJicrYXJndW1lbnRzWzBdOicnKSk7dmFyIGRlbGF5X21zPSh0eXBlb2Ygd2NwcFBpbmdEZWxheV9tcz09PSd1bmRlZmluZWQnKT8wOndjcHBQaW5nRGVsYXlfbXM7aWYoZGVsYXlfbXM+MCl7c2V0VGltZW91dChmdW5jdGlvbigpeyQuZ2V0KCc8PDwtTkVPLVVTRVItSEFTLVdDUFAtPj4+JyxmdW5jdGlvbihkYXRhKXtpZihkYXRhLmxlbmd0aD4wKXt3Y3BwRGV0ZWN0T25TdWNjZXNzKGRhdGEpfWVsc2V7d2NwcERldGVjdE9uRmFpbHVyZSgpfX0pfSxkZWxheV9tcyl9ZWxzZXt2YXIgZm5jV0NQUD1zZXRJbnRlcnZhbChnZXRXQ1BQVmVyLHdjcHBQaW5nVGltZW91dFN0ZXBfbXMpO3ZhciB3Y3BwX2NvdW50PTA7ZnVuY3Rpb24gZ2V0V0NQUFZlcigpe2lmKHdjcHBfY291bnQ8PXdjcHBQaW5nVGltZW91dF9tcyl7JC5nZXQoJzw8PC1ORU8tVVNFUi1IQVMtV0NQUC0+Pj4nLHsnXyc6JC5ub3coKX0sZnVuY3Rpb24oZGF0YSl7aWYoZGF0YS5sZW5ndGg+MCl7Y2xlYXJJbnRlcnZhbChmbmNXQ1BQKTt3Y3BwRGV0ZWN0T25TdWNjZXNzKGRhdGEpfX0pO3djcHBfY291bnQrPXdjcHBQaW5nVGltZW91dFN0ZXBfbXN9ZWxzZXtjbGVhckludGVydmFsKGZuY1dDUFApO3djcHBEZXRlY3RPbkZhaWx1cmUoKX19fX19fSkoKTskKGRvY3VtZW50KS5yZWFkeShmdW5jdGlvbigpe2pzV0NQUC5pbml0KCk7anNXQ1BQLnBpbmcoKX0pOw==';
 259:                     
 260:             $s2 = base64_decode($s1);
 261:             $s2 = str_replace('<<<-NEO-HTML-ID->>>', $dynamicIframeId, $s2);
 262:             $s2 = str_replace('<<<-NEO-PING-URL->>>', $absoluteWcpAxd.'&'.self::PING, $s2);
 263:             $s2 = str_replace('<<<-NEO-USER-HAS-WCPP->>>', $absoluteWcpAxd, $s2);
 264:             
 265:             return $s2;
 266:             
 267:         }else if(isset($qs[self::GEN_WCP_SCRIPT_URL])){
 268:             
 269:             $clientPrintJobUrl = base64_decode($qs[self::GEN_WCP_SCRIPT_URL]);
 270:             if (strpos($clientPrintJobUrl, '?')>0){
 271:                 $clientPrintJobUrl .= '&';
 272:             }else{
 273:                 $clientPrintJobUrl .= '?';
 274:             }
 275:             $clientPrintJobUrl .= self::CLIENT_PRINT_JOB;
 276:             $absoluteWcpAxd = $webClientPrintControllerAbsoluteUrl;
 277:             $wcppGetPrintersParam = '-getPrinters:'.$absoluteWcpAxd.'?'.self::WCP.'&'.self::SID.'=';
 278:             $wcpHandlerGetPrinters = $absoluteWcpAxd.'?'.self::WCP.'&'.self::WCP_SCRIPT_AXD_GET_PRINTERS.'&'.self::SID.'=';
 279:             $wcppGetPrintersInfoParam = '-getPrintersInfo:'.$absoluteWcpAxd.'?'.self::WCP.'&'.self::SID.'=';
 280:             $wcpHandlerGetPrintersInfo = $absoluteWcpAxd.'?'.self::WCP.'&'.self::WCP_SCRIPT_AXD_GET_PRINTERSINFO.'&'.self::SID.'=';
 281:             $wcppGetWcppVerParam = '-getWcppVersion:'.$absoluteWcpAxd.'?'.self::WCP.'&'.self::SID.'=';
 282:             $wcpHandlerGetWcppVer = $absoluteWcpAxd.'?'.self::WCP.'&'.self::WCP_SCRIPT_AXD_GET_WCPPVERSION.'&'.self::SID.'=';
 283:             $sessionIDVal = $qs[self::SID];
 284:         
 285:             $s1 = 'dmFyIGpzV2ViQ2xpZW50UHJpbnQ9KGZ1bmN0aW9uKCl7dmFyIHNldEE9ZnVuY3Rpb24oKXt2YXIgZV9pZD0naWRfJytuZXcgRGF0ZSgpLmdldFRpbWUoKTtpZih3aW5kb3cuY2hyb21lKXskKCdib2R5JykuYXBwZW5kKCc8YSBpZD1cIicrZV9pZCsnXCI+PC9hPicpOyQoJyMnK2VfaWQpLmF0dHIoJ2hyZWYnLCd3ZWJjbGllbnRwcmludGl2OicrYXJndW1lbnRzWzBdKTt2YXIgYT0kKCdhIycrZV9pZClbMF07dmFyIGV2T2JqPWRvY3VtZW50LmNyZWF0ZUV2ZW50KCdNb3VzZUV2ZW50cycpO2V2T2JqLmluaXRFdmVudCgnY2xpY2snLHRydWUsdHJ1ZSk7YS5kaXNwYXRjaEV2ZW50KGV2T2JqKX1lbHNleyQoJ2JvZHknKS5hcHBlbmQoJzxpZnJhbWUgbmFtZT1cIicrZV9pZCsnXCIgaWQ9XCInK2VfaWQrJ1wiIHdpZHRoPVwiMVwiIGhlaWdodD1cIjFcIiBzdHlsZT1cInZpc2liaWxpdHk6aGlkZGVuO3Bvc2l0aW9uOmFic29sdXRlXCIgLz4nKTskKCcjJytlX2lkKS5hdHRyKCdzcmMnLCd3ZWJjbGllbnRwcmludGl2OicrYXJndW1lbnRzWzBdKX1zZXRUaW1lb3V0KGZ1bmN0aW9uKCl7JCgnIycrZV9pZCkucmVtb3ZlKCl9LDUwMDApfTtyZXR1cm57cHJpbnQ6ZnVuY3Rpb24oKXtzZXRBKCdVUkxfUFJJTlRfSk9CJysoYXJndW1lbnRzLmxlbmd0aD09MT8nJicrYXJndW1lbnRzWzBdOicnKSl9LGdldFByaW50ZXJzOmZ1bmN0aW9uKCl7c2V0QSgnVVJMX1dDUF9BWERfV0lUSF9HRVRfUFJJTlRFUlNfQ09NTUFORCcrJzw8PC1ORU8tU0VTU0lPTi1JRC0+Pj4nKTt2YXIgZGVsYXlfbXM9KHR5cGVvZiB3Y3BwR2V0UHJpbnRlcnNEZWxheV9tcz09PSd1bmRlZmluZWQnKT8wOndjcHBHZXRQcmludGVyc0RlbGF5X21zO2lmKGRlbGF5X21zPjApe3NldFRpbWVvdXQoZnVuY3Rpb24oKXskLmdldCgnVVJMX1dDUF9BWERfR0VUX1BSSU5URVJTJysnPDw8LU5FTy1TRVNTSU9OLUlELT4+PicsZnVuY3Rpb24oZGF0YSl7aWYoZGF0YS5sZW5ndGg+MCl7d2NwR2V0UHJpbnRlcnNPblN1Y2Nlc3MoZGF0YSl9ZWxzZXt3Y3BHZXRQcmludGVyc09uRmFpbHVyZSgpfX0pfSxkZWxheV9tcyl9ZWxzZXt2YXIgZm5jR2V0UHJpbnRlcnM9c2V0SW50ZXJ2YWwoZ2V0Q2xpZW50UHJpbnRlcnMsd2NwcEdldFByaW50ZXJzVGltZW91dFN0ZXBfbXMpO3ZhciB3Y3BwX2NvdW50PTA7ZnVuY3Rpb24gZ2V0Q2xpZW50UHJpbnRlcnMoKXtpZih3Y3BwX2NvdW50PD13Y3BwR2V0UHJpbnRlcnNUaW1lb3V0X21zKXskLmdldCgnVVJMX1dDUF9BWERfR0VUX1BSSU5URVJTJysnPDw8LU5FTy1TRVNTSU9OLUlELT4+PicseydfJzokLm5vdygpfSxmdW5jdGlvbihkYXRhKXtpZihkYXRhLmxlbmd0aD4wKXtjbGVhckludGVydmFsKGZuY0dldFByaW50ZXJzKTt3Y3BHZXRQcmludGVyc09uU3VjY2VzcyhkYXRhKX19KTt3Y3BwX2NvdW50Kz13Y3BwR2V0UHJpbnRlcnNUaW1lb3V0U3RlcF9tc31lbHNle2NsZWFySW50ZXJ2YWwoZm5jR2V0UHJpbnRlcnMpO3djcEdldFByaW50ZXJzT25GYWlsdXJlKCl9fX19LGdldFByaW50ZXJzSW5mbzpmdW5jdGlvbigpe3NldEEoJ1VSTF9XQ1BfQVhEX1dJVEhfR0VUX1BSSU5URVJTSU5GT19DT01NQU5EJysnPDw8LU5FTy1TRVNTSU9OLUlELT4+PicpO3ZhciBkZWxheV9tcz0odHlwZW9mIHdjcHBHZXRQcmludGVyc0RlbGF5X21zPT09J3VuZGVmaW5lZCcpPzA6d2NwcEdldFByaW50ZXJzRGVsYXlfbXM7aWYoZGVsYXlfbXM+MCl7c2V0VGltZW91dChmdW5jdGlvbigpeyQuZ2V0KCdVUkxfV0NQX0FYRF9HRVRfUFJJTlRFUlNJTkZPJysnPDw8LU5FTy1TRVNTSU9OLUlELT4+PicsZnVuY3Rpb24oZGF0YSl7aWYoZGF0YS5sZW5ndGg+MCl7d2NwR2V0UHJpbnRlcnNPblN1Y2Nlc3MoZGF0YSl9ZWxzZXt3Y3BHZXRQcmludGVyc09uRmFpbHVyZSgpfX0pfSxkZWxheV9tcyl9ZWxzZXt2YXIgZm5jR2V0UHJpbnRlcnNJbmZvPXNldEludGVydmFsKGdldENsaWVudFByaW50ZXJzSW5mbyx3Y3BwR2V0UHJpbnRlcnNUaW1lb3V0U3RlcF9tcyk7dmFyIHdjcHBfY291bnQ9MDtmdW5jdGlvbiBnZXRDbGllbnRQcmludGVyc0luZm8oKXtpZih3Y3BwX2NvdW50PD13Y3BwR2V0UHJpbnRlcnNUaW1lb3V0X21zKXskLmdldCgnVVJMX1dDUF9BWERfR0VUX1BSSU5URVJTSU5GTycrJzw8PC1ORU8tU0VTU0lPTi1JRC0+Pj4nLHsnXyc6JC5ub3coKX0sZnVuY3Rpb24oZGF0YSl7aWYoZGF0YS5sZW5ndGg+MCl7Y2xlYXJJbnRlcnZhbChmbmNHZXRQcmludGVyc0luZm8pO3djcEdldFByaW50ZXJzT25TdWNjZXNzKGRhdGEpfX0pO3djcHBfY291bnQrPXdjcHBHZXRQcmludGVyc1RpbWVvdXRTdGVwX21zfWVsc2V7Y2xlYXJJbnRlcnZhbChmbmNHZXRQcmludGVyc0luZm8pO3djcEdldFByaW50ZXJzT25GYWlsdXJlKCl9fX19LGdldFdjcHBWZXI6ZnVuY3Rpb24oKXtzZXRBKCdVUkxfV0NQX0FYRF9XSVRIX0dFVF9XQ1BQVkVSU0lPTl9DT01NQU5EJysnPDw8LU5FTy1TRVNTSU9OLUlELT4+PicpO3ZhciBkZWxheV9tcz0odHlwZW9mIHdjcHBHZXRWZXJEZWxheV9tcz09PSd1bmRlZmluZWQnKT8wOndjcHBHZXRWZXJEZWxheV9tcztpZihkZWxheV9tcz4wKXtzZXRUaW1lb3V0KGZ1bmN0aW9uKCl7JC5nZXQoJ1VSTF9XQ1BfQVhEX0dFVF9XQ1BQVkVSU0lPTicrJzw8PC1ORU8tU0VTU0lPTi1JRC0+Pj4nLGZ1bmN0aW9uKGRhdGEpe2lmKGRhdGEubGVuZ3RoPjApe3djcEdldFdjcHBWZXJPblN1Y2Nlc3MoZGF0YSl9ZWxzZXt3Y3BHZXRXY3BwVmVyT25GYWlsdXJlKCl9fSl9LGRlbGF5X21zKX1lbHNle3ZhciBmbmNXQ1BQPXNldEludGVydmFsKGdldENsaWVudFZlcix3Y3BwR2V0VmVyVGltZW91dFN0ZXBfbXMpO3ZhciB3Y3BwX2NvdW50PTA7ZnVuY3Rpb24gZ2V0Q2xpZW50VmVyKCl7aWYod2NwcF9jb3VudDw9d2NwcEdldFZlclRpbWVvdXRfbXMpeyQuZ2V0KCdVUkxfV0NQX0FYRF9HRVRfV0NQUFZFUlNJT04nKyc8PDwtTkVPLVNFU1NJT04tSUQtPj4+Jyx7J18nOiQubm93KCl9LGZ1bmN0aW9uKGRhdGEpe2lmKGRhdGEubGVuZ3RoPjApe2NsZWFySW50ZXJ2YWwoZm5jV0NQUCk7d2NwR2V0V2NwcFZlck9uU3VjY2VzcyhkYXRhKX19KTt3Y3BwX2NvdW50Kz13Y3BwR2V0VmVyVGltZW91dFN0ZXBfbXN9ZWxzZXtjbGVhckludGVydmFsKGZuY1dDUFApO3djcEdldFdjcHBWZXJPbkZhaWx1cmUoKX19fX0sc2VuZDpmdW5jdGlvbigpe3NldEEuYXBwbHkodGhpcyxhcmd1bWVudHMpfX19KSgpOw==';
 286:     
 287:             $s2 = base64_decode($s1);
 288:             $s2 = str_replace('URL_PRINT_JOB', $clientPrintJobUrl, $s2);
 289:             $s2 = str_replace('URL_WCP_AXD_WITH_GET_PRINTERSINFO_COMMAND', $wcppGetPrintersInfoParam, $s2);
 290:             $s2 = str_replace('URL_WCP_AXD_GET_PRINTERSINFO', $wcpHandlerGetPrintersInfo, $s2);
 291:             $s2 = str_replace('URL_WCP_AXD_WITH_GET_PRINTERS_COMMAND', $wcppGetPrintersParam, $s2);
 292:             $s2 = str_replace('URL_WCP_AXD_GET_PRINTERS', $wcpHandlerGetPrinters, $s2);
 293:             $s2 = str_replace('URL_WCP_AXD_WITH_GET_WCPPVERSION_COMMAND', $wcppGetWcppVerParam, $s2);
 294:             $s2 = str_replace('URL_WCP_AXD_GET_WCPPVERSION', $wcpHandlerGetWcppVer, $s2);
 295:             $s2 = str_replace('<<<-NEO-SESSION-ID->>>', $sessionIDVal, $s2);
 296:             
 297:             return $s2;
 298:         }
 299:         
 300:     }
 301:     
 302:        
 303:     /**
 304:      * Generates printing script.
 305:      */
 306:     const GenPrintScript = 0;
 307:     /**
 308:      * Generates WebClientPrint Processor (WCPP) detection script.
 309:      */ 
 310:     const GenWcppDetectScript = 1;
 311:     /**
 312:      * Sets the installed printers list in the website cache.
 313:      */        
 314:     const ClientSetInstalledPrinters = 2;
 315:     /**
 316:      * Gets the installed printers list from the website cache.
 317:      */
 318:     const ClientGetInstalledPrinters = 3;
 319:     /**
 320:      * Sets the WebClientPrint Processor (WCPP) Version in the website cache.
 321:      */
 322:     const ClientSetWcppVersion = 4;
 323:     /**
 324:      * Gets the WebClientPrint Processor (WCPP) Version from the website cache.
 325:      */
 326:     const ClientGetWcppVersion = 5;
 327:     /**
 328:      * Sets the installed printers list with detailed info in the website cache.
 329:      */
 330:     const ClientSetInstalledPrintersInfo = 6;
 331:     /**
 332:      * Gets the installed printers list with detailed info from the website cache.
 333:      */
 334:     const ClientGetInstalledPrintersInfo = 7;
 335:        
 336:     
 337:     /**
 338:      * Determines the type of process request based on the Query String value. 
 339:      * 
 340:      * @param string $queryString The query string of the current request.
 341:      * @return integer A valid type of process request. In case of an invalid value, an Exception is thrown.
 342:      * @throws Exception 
 343:      */
 344:     public static function GetProcessRequestType($queryString){
 345:         parse_str($queryString, $qs);
 346:     
 347:         if(isset($qs[self::SID])){
 348:             if(isset($qs[self::PING])){
 349:                 return self::ClientSetWcppVersion;
 350:             } else if(isset($qs[self::WCPP_SET_VERSION])){
 351:                 return self::ClientSetWcppVersion;
 352:             } else if(isset($qs[self::WCPP_SET_PRINTERS])){
 353:                 return self::ClientSetInstalledPrinters;
 354:             } else if(isset($qs[self::WCPP_SET_PRINTERSINFO])){
 355:                 return self::ClientSetInstalledPrintersInfo;
 356:             } else if(isset($qs[self::WCP_SCRIPT_AXD_GET_WCPPVERSION])){
 357:                 return self::ClientGetWcppVersion;
 358:             } else if(isset($qs[self::WCP_SCRIPT_AXD_GET_PRINTERS])){
 359:                 return self::ClientGetInstalledPrinters;
 360:             } else if(isset($qs[self::WCP_SCRIPT_AXD_GET_PRINTERSINFO])){
 361:                 return self::ClientGetInstalledPrintersInfo;
 362:             } else if(isset($qs[self::GEN_WCP_SCRIPT_URL])){
 363:                 return self::GenPrintScript;
 364:             } else {
 365:                 return self::ClientGetWcppVersion;
 366:             }
 367:         } else if(isset($qs[self::GEN_DETECT_WCPP_SCRIPT])){
 368:             return self::GenWcppDetectScript;
 369:         } else {
 370:             throw new Exception('No valid ProcessRequestType was found in the specified QueryString.');
 371:         }
 372:     }
 373:     
 374: }
 375: 
 376: /**
 377:  * The base class for all kind of printers supported at the client side.
 378:  */
 379: abstract class ClientPrinter{
 380:     
 381:     public $printerId;
 382:     public function serialize(){
 383:         
 384:     }
 385: }
 386: 
 387: /**
 388:  * It represents the default printer installed in the client machine.
 389:  */
 390: class DefaultPrinter extends ClientPrinter{
 391:     public function __construct() {
 392:         $this->printerId = chr(0);
 393:     }
 394:     
 395:     public function serialize() {
 396:         return $this->printerId;
 397:     }
 398: }
 399: 
 400: /**
 401:  * It represents a printer installed in the client machine with an associated OS driver.
 402:  */
 403: class InstalledPrinter extends ClientPrinter{
 404:     
 405:     /**
 406:      * Gets or sets the name of the printer installed in the client machine. Default value is an empty string.
 407:      * @var string 
 408:      */
 409:     public $printerName = '';
 410: 
 411:     /**
 412:      * Gets or sets whether to print to Default printer in case of the specified one is not found or missing. Default is False.
 413:      * @var boolean 
 414:      */
 415:     public $printToDefaultIfNotFound = false;
 416:     
 417:     
 418:     /**
 419:      * Gets or sets the name of the tray supported by the client printer. Default value is an empty string.
 420:      * @var string 
 421:      */
 422:     public $trayName = '';
 423:     
 424:     /**
 425:      * Gets or sets the name of the Paper supported by the client printer. Default value is an empty string.
 426:      * @var string 
 427:      */
 428:     public $paperName = '';
 429:     
 430:     
 431:     /**
 432:      * Creates an instance of the InstalledPrinter class with the specified printer name.
 433:      * @param string $printerName The name of the printer installed in the client machine.
 434:      */
 435:     public function __construct($printerName) {
 436:         $this->printerId = chr(1);
 437:         $this->printerName = $printerName;
 438:     }
 439:     
 440:     public function serialize() {
 441:         
 442:         if (Utils::isNullOrEmptyString($this->printerName)){
 443:              throw new Exception("The specified printer name is null or empty.");
 444:         }
 445:         
 446:         $serData = $this->printerId.$this->printerName;
 447:         
 448:         if ($this->printToDefaultIfNotFound){
 449:             $serData .= Utils::SER_SEP.'1';     
 450:         } else {
 451:             $serData .= Utils::SER_SEP.'0';    
 452:         }      
 453:         
 454:         if ($this->trayName){
 455:             $serData .= Utils::SER_SEP.$this->trayName;     
 456:         } else {
 457:             $serData .= Utils::SER_SEP.'def';    
 458:         }
 459:         
 460:         if ($this->paperName){
 461:             $serData .= Utils::SER_SEP.$this->paperName;     
 462:         } else {
 463:             $serData .= Utils::SER_SEP.'def';    
 464:         }
 465:         
 466:         return $serData;
 467:     }
 468: }
 469: 
 470: /**
 471:  * It represents a printer which is connected through a parallel port in the client machine.
 472:  */
 473: class ParallelPortPrinter extends ClientPrinter{
 474:     
 475:     /**
 476:      * Gets or sets the parallel port name, for example LPT1. Default value is "LPT1"
 477:      * @var string 
 478:      */
 479:     public $portName = "LPT1";
 480: 
 481:     /**
 482:      * Creates an instance of the ParallelPortPrinter class with the specified port name.
 483:      * @param string $portName The parallel port name, for example LPT1.
 484:      */
 485:     public function __construct($portName) {
 486:         $this->printerId = chr(2);
 487:         $this->portName = $portName;
 488:     }
 489:     
 490:     public function serialize() {
 491:         
 492:         if (Utils::isNullOrEmptyString($this->portName)){
 493:              throw new Exception("The specified parallel port name is null or empty.");
 494:         }
 495:         
 496:         return $this->printerId.$this->portName;
 497:     }
 498: }
 499: 
 500: /**
 501:  * It represents a printer which is connected through a serial port in the client machine.
 502:  */
 503: class SerialPortPrinter extends ClientPrinter{
 504:     
 505:     /**
 506:      * Gets or sets the serial port name, for example COM1. Default value is "COM1"
 507:      * @var string 
 508:      */
 509:     public $portName = "COM1";
 510:     /**
 511:      * Gets or sets the serial port baud rate in bits per second. Default value is 9600
 512:      * @var integer 
 513:      */
 514:     public $baudRate = 9600;
 515:     /**
 516:      * Gets or sets the serial port parity-checking protocol. Default value is NONE = 0
 517:      * NONE = 0, ODD = 1, EVEN = 2, MARK = 3, SPACE = 4
 518:      * @var integer 
 519:      */
 520:     public $parity = SerialPortParity::NONE;
 521:     /**
 522:      * Gets or sets the serial port standard number of stopbits per byte. Default value is ONE = 1
 523:      * ONE = 1, TWO = 2, ONE_POINT_FIVE = 3
 524:      * @var integer
 525:      */
 526:     public $stopBits = SerialPortStopBits::ONE;
 527:     /**
 528:      * Gets or sets the serial port standard length of data bits per byte. Default value is 8
 529:      * @var integer
 530:      */
 531:     public $dataBits = 8;
 532:     /**
 533:      * Gets or sets the handshaking protocol for serial port transmission of data. Default value is XON_XOFF = 1
 534:      * NONE = 0, REQUEST_TO_SEND = 2, REQUEST_TO_SEND_XON_XOFF = 3, XON_XOFF = 1
 535:      * @var integer
 536:      */
 537:     public $flowControl = SerialPortHandshake::XON_XOFF;
 538:     
 539:     /**
 540:      * Creates an instance of the SerialPortPrinter class wiht the specified information.
 541:      * @param string $portName The serial port name, for example COM1.
 542:      * @param integer $baudRate The serial port baud rate in bits per second.
 543:      * @param integer $parity The serial port parity-checking protocol.
 544:      * @param integer $stopBits The serial port standard number of stopbits per byte.
 545:      * @param integer $dataBits The serial port standard length of data bits per byte.
 546:      * @param integer $flowControl The handshaking protocol for serial port transmission of data.
 547:      */
 548:     public function __construct($portName, $baudRate, $parity, $stopBits, $dataBits, $flowControl) {
 549:         $this->printerId = chr(3);
 550:         $this->portName = $portName;
 551:         $this->baudRate = $baudRate;
 552:         $this->parity = $parity;
 553:         $this->stopBits = $stopBits;
 554:         $this->dataBits = $dataBits;
 555:         $this->flowControl = $flowControl;
 556:     }
 557:     
 558:     public function serialize() {
 559:         
 560:         if (Utils::isNullOrEmptyString($this->portName)){
 561:              throw new Exception("The specified serial port name is null or empty.");
 562:         }
 563:         
 564:         return $this->printerId.$this->portName.Utils::SER_SEP.$this->baudRate.Utils::SER_SEP.$this->dataBits.Utils::SER_SEP.((int)$this->flowControl).Utils::SER_SEP.((int)$this->parity).Utils::SER_SEP.((int)$this->stopBits);
 565:     }
 566: }
 567: 
 568: /**
 569:  * It represents a Network IP/Ethernet printer which can be reached from the client machine.
 570:  */
 571: class NetworkPrinter extends ClientPrinter{
 572:     
 573:     /**
 574:      * Gets or sets the DNS name assigned to the printer. Default is an empty string
 575:      * @var string 
 576:      */
 577:     public $dnsName = "";
 578:     /**
 579:      * Gets or sets the Internet Protocol (IP) address assigned to the printer. Default value is an empty string
 580:      * @var string 
 581:      */
 582:     public $ipAddress = "";
 583:     /**
 584:      * Gets or sets the port number assigned to the printer. Default value is 0
 585:      * @var integer 
 586:      */
 587:     public $port = 0;
 588:     
 589:     /**
 590:      * Creates an instance of the NetworkPrinter class with the specified DNS name or IP Address, and port number.
 591:      * @param string $dnsName The DNS name assigned to the printer.
 592:      * @param string $ipAddress The Internet Protocol (IP) address assigned to the printer.
 593:      * @param integer $port The port number assigned to the printer.
 594:      */
 595:     public function __construct($dnsName, $ipAddress, $port) {
 596:         $this->printerId = chr(4);
 597:         $this->dnsName = $dnsName;
 598:         $this->ipAddress = $ipAddress;
 599:         $this->port = $port;
 600:     }
 601:     
 602:     public function serialize() {
 603:         
 604:         if (Utils::isNullOrEmptyString($this->dnsName) && Utils::isNullOrEmptyString($this->ipAddress)){
 605:              throw new Exception("The specified network printer settings is not valid. You must specify the DNS Printer Name or its IP address.");
 606:         }
 607:         
 608:         return $this->printerId.$this->dnsName.Utils::SER_SEP.$this->ipAddress.Utils::SER_SEP.$this->port;
 609:     }
 610: }
 611: 
 612: /**
 613:  *  It represents a printer which will be selected by the user in the client machine. The user will be prompted with a print dialog.
 614:  */
 615: class UserSelectedPrinter extends ClientPrinter{
 616:     public function __construct() {
 617:         $this->printerId = chr(5);
 618:     }
 619:     
 620:     public function serialize() {
 621:         return $this->printerId;
 622:     }
 623: }
 624: 
 625: /**
 626:  * Specifies the parity bit for Serial Port settings. 
 627:  */
 628: class SerialPortParity{
 629:     const NONE = 0;
 630:     const ODD = 1;
 631:     const EVEN = 2;
 632:     const MARK = 3;
 633:     const SPACE = 4;
 634:     public static function parse($val){
 635:         if($val === 'NONE') return 0;
 636:         if($val === 'ODD') return 1;
 637:         if($val === 'EVEN') return 2;
 638:         if($val === 'MARK') return 3;
 639:         if($val === 'SPACE') return 4;
 640:         throw new Exception('Invalid value');
 641:     }
 642: }
 643: 
 644: /**
 645:  * Specifies the number of stop bits used for Serial Port settings.
 646:  */
 647: class SerialPortStopBits{
 648:     const NONE = 0;
 649:     const ONE = 1;
 650:     const TWO = 2;
 651:     const ONE_POINT_FIVE = 3;
 652:     public static function parse($val){
 653:         if($val === 'NONE') return 0;
 654:         if($val === 'ONE') return 1;
 655:         if($val === 'TWO') return 2;
 656:         if($val === 'ONE_POINT_FIVE') return 3;
 657:         throw new Exception('Invalid value');
 658:     }
 659: }
 660: 
 661: /**
 662:  * Specifies the control protocol used in establishing a serial port communication.
 663:  */
 664: class SerialPortHandshake{
 665:     const NONE = 0;
 666:     const REQUEST_TO_SEND = 2;
 667:     const REQUEST_TO_SEND_XON_XOFF = 3;
 668:     const XON_XOFF = 1;
 669:     public static function parse($val){
 670:         if($val === 'NONE') return 0;
 671:         if($val === 'XON_XOFF') return 1;
 672:         if($val === 'REQUEST_TO_SEND') return 2;
 673:         if($val === 'REQUEST_TO_SEND_XON_XOFF') return 3;
 674:         throw new Exception('Invalid value');
 675:     }
 676: }
 677: 
 678: /**
 679:  * It represents a file in the server that will be printed at the client side.
 680:  */
 681: class PrintFile{
 682:     
 683:     /**
 684:      * Gets or sets the path of the file at the server side that will be printed at the client side.
 685:      * @var string 
 686:      */
 687:     public $filePath = '';
 688:     /**
 689:      * Gets or sets the file name that will be created at the client side. 
 690:      * It must include the file extension like .pdf, .txt, .doc, .xls, etc.
 691:      * @var string 
 692:      */
 693:     public $fileName = '';
 694:     /**
 695:      * Gets or sets the binary content of the file at the server side that will be printed at the client side.
 696:      * @var string 
 697:      */
 698:     public $fileBinaryContent = '';
 699:     
 700:     /**
 701:      * Gets or sets the num of copies for printing this file. Default is 1.
 702:      * @var integer
 703:      */
 704:     public $copies = 1;
 705:     
 706:     const PREFIX = 'wcpPF:';
 707:     const SEP = '|';
 708:         
 709:     /**
 710:      * 
 711:      * @param string $filePath The path of the file at the server side that will be printed at the client side.
 712:      * @param string $fileName The file name that will be created at the client side. It must include the file extension like .pdf, .txt, .doc, .xls, etc.
 713:      * @param string $fileBinaryContent The binary content of the file at the server side that will be printed at the client side.
 714:      */
 715:     public function __construct($filePath, $fileName, $fileBinaryContent) {
 716:         $this->filePath = $filePath;
 717:         $this->fileName = $fileName;
 718:         $this->fileBinaryContent = $fileBinaryContent;
 719:         
 720:     }
 721:     
 722:     public function serialize() {
 723:         $file = str_replace('\\', 'BACKSLASHCHAR',$this->fileName );
 724:         if($this->copies > 1){
 725:              $pfc = 'PFC='.$this->copies;
 726:              $file = substr($file, 0, strrpos($file, '.')).$pfc.substr($file, strrpos($file, '.'));
 727:         }
 728:         return self::PREFIX.$file.self::SEP.$this->getFileContent();
 729:     }
 730:     
 731:     public function getFileContent(){
 732:         $content = $this->fileBinaryContent;
 733:         if(!Utils::isNullOrEmptyString($this->filePath)){
 734:             $handle = fopen($this->filePath, 'rb');
 735:             $content = fread($handle, filesize($this->filePath));
 736:             fclose($handle);
 737:         }
 738:         return $content;
 739:     }
 740: }
 741: 
 742: /**
 743:  * Specifies the print rotation.
 744:  */
 745: class PrintRotation
 746: {
 747:     /**
 748:      * Print page without rotation.
 749:      */
 750:     const None = 0;
 751:     /**
 752:      * Print page rotated by 90 degrees clockwise.
 753:      */
 754:     const Rot90 = 1;
 755:     /**
 756:      * Print page rotated by 180 degrees.
 757:      */
 758:     const Rot180 = 2;
 759:     /**
 760:      * Print page rotated by 270 degrees clockwise.
 761:      */
 762:     const Rot270 = 3;
 763:     
 764:     public static function parse($val){
 765:         if($val === 'None') return 0;
 766:         if($val === 'Rot90') return 1;
 767:         if($val === 'Rot180') return 2;
 768:         if($val === 'Rot270') return 3;
 769:         throw new Exception('Invalid value');
 770:     }
 771: }
 772: 
 773: /**
 774:  * It represents a PDF file in the server that will be printed at the client side.
 775:  */
 776: class PrintFilePDF extends PrintFile{
 777:     
 778:     /**
 779:      * Gets or sets whether to print the PDF document with color images, texts, or other objects as shades of gray. Default is False.
 780:      * @var boolean 
 781:      */
 782:     public $printAsGrayscale = false;
 783:  
 784:     /**
 785:      * Gets or sets whether to print any annotations, if any, available in the PDF document. Default is False.
 786:      * @var boolean 
 787:      */
 788:     public $printAnnotations = false;
 789:     
 790:     /**
 791:      * Gets or sets a subset of pages to print. It can be individual page numbers, a range, or a combination. For example: 1, 5-10, 25, 50. Default is an empty string which means print all pages.
 792:      * @var string 
 793:      */
 794:     public $pagesRange = '';
 795:     
 796:     /**
 797:      * Gets or sets whether pages are printed in reverse order. Default is False.
 798:      * @var boolean 
 799:      */
 800:     public $printInReverseOrder = false;
 801:     
 802:     /**
 803:      * Gets or sets the print rotation. Default is None.
 804:      * @var integer 
 805:      */
 806:     public $printRotation = PrintRotation::None;
 807:     
 808:     
 809:     public function serialize() {
 810:         $file = str_replace('\\', 'BACKSLASHCHAR', $this->fileName);
 811:         if($this->copies > 1){
 812:              $pfc = 'PFC='.$this->copies;
 813:              $file = substr($file, 0, strrpos($file, '.')).$pfc.substr($file, strrpos($file, '.'));
 814:         }
 815:         
 816:         return self::PREFIX.$file.'.wpdf'.self::SEP.$this->getFileContent();
 817:     }
 818:     
 819:     public function getFileContent(){
 820:  
 821:         $pr = urldecode($this->pagesRange);
 822:         if (!Utils::isNullOrEmptyString($pr)){
 823:             if (preg_match('/^(?!([ \d]*-){2})\d+(?: *[-,] *\d+)*$/', $pr))
 824:             {
 825:                 //validate range
 826:                 $ranges = explode(',',str_replace(' ', '', $pr)); //remove any space chars
 827:                 
 828:                 for ($i = 0; $i < count($ranges); $i++)
 829:                 {
 830:                     if (strpos($ranges[$i], '-') > 0)
 831:                     {
 832:                         $pages = explode('-', $ranges[$i]);
 833:                         if (intval($pages[0]) > intval($pages[1]))
 834:                         {
 835:                             throw new Exception("The specified PageRange is not valid.");
 836:                         }
 837:                     }
 838:                 }
 839:             }
 840:             else
 841:                 throw new Exception("The specified PageRange is not valid.");
 842:             
 843:         }
 844:         
 845:         $metadata = ($this->printAsGrayscale ? '1' : '0');
 846:         $metadata .= Utils::SER_SEP.($this->printAnnotations ? '1' : '0');
 847:         $metadata .= Utils::SER_SEP.(Utils::isNullOrEmptyString($pr) ? 'A' : $pr);
 848:         $metadata .= Utils::SER_SEP.($this->printInReverseOrder ? '1' : '0');
 849:         $metadata .= Utils::SER_SEP.$this->printRotation;
 850:         
 851:         $content = $this->fileBinaryContent;
 852:         if(!Utils::isNullOrEmptyString($this->filePath)){
 853:             $handle = fopen($this->filePath, 'rb');
 854:             $content = fread($handle, filesize($this->filePath));
 855:             fclose($handle);
 856:         }
 857:         return $metadata.chr(10).$content;
 858:     }
 859: }
 860: 
 861: /**
 862:  * Specifies the print orientation.
 863:  */
 864: class PrintOrientation
 865: {
 866:     /**
 867:      * Print the document vertically.
 868:      */
 869:     const Portrait = 0;
 870:     /**
 871:      *  Print the document horizontally.
 872:      */
 873:     const Landscape = 1;
 874:     
 875:     public static function parse($val){
 876:         if($val === 'Portrait') return 0;
 877:         if($val === 'Landscape') return 1;
 878:         throw new Exception('Invalid value');
 879:     }
 880: }
 881: 
 882: /**
 883:  * Specifies the text alignment
 884:  */
 885: class TextAlignment
 886: {
 887:     /**
 888:      * Left alignment
 889:      */
 890:     const Left = 0;
 891:     /**
 892:      * Right alignment
 893:      */
 894:     const Right = 2;
 895:     /**
 896:      * Center alignment
 897:      */
 898:     const Center = 1;
 899:     /**
 900:      * Justify alignment
 901:      */
 902:     const Justify = 3;
 903:     
 904:     public static function parse($val){
 905:         if($val === 'Left') return 0;
 906:         if($val === 'Center') return 1;
 907:         if($val === 'Right') return 2;
 908:         if($val === 'Justify') return 3;
 909:         throw new Exception('Invalid value');
 910:     }
 911: }
 912:     
 913: /**
 914:  * It represents a plain text file in the server that will be printed at the client side.
 915:  */
 916: class PrintFileTXT extends PrintFile{
 917:     
 918:     /**
 919:      * Gets or sets the Text content to be printed. Default is an empty string.
 920:      * @var string 
 921:      */
 922:      public $textContent = '';
 923:      
 924:      /**
 925:       * Gets or sets the alignment of the text content. Default is Left alignment.
 926:       * @var integer 
 927:       */
 928:      public $textAlignment = TextAlignment::Left;
 929: 
 930:      /**
 931:       * Gets or sets the font name. Default is Arial.
 932:       * @var string 
 933:       */
 934:      public $fontName = 'Arial';
 935:      
 936:      /**
 937:       * Gets or sets whether the text is bold. Default is False.
 938:       * @var boolean 
 939:       */
 940:      public $fontBold = false;
 941:         
 942:      /**
 943:       * Gets or sets whether the text has the italic style applied. Default is False.
 944:       * @var boolean 
 945:       */
 946:      public $fontItalic = false;
 947:       
 948:      /**
 949:       * Gets or sets whether the text is underlined. Default is False.
 950:       * @var boolean 
 951:       */
 952:      public $fontUnderline = false;
 953:         
 954:      /**
 955:       * Gets or sets whether the text is printed with a horizontal line through it. Default is False.
 956:       * @var boolean
 957:       */
 958:      public $fontStrikeThrough = false;
 959:         
 960:      /**
 961:       * Gets or sets the font size in Points unit. Default is 10pt. 
 962:       * @var float 
 963:       */
 964:      public $fontSizeInPoints = 10.0;
 965:         
 966:      /**
 967:       * Gets or sets the Color for the printed text. Color must be specified in Hex notation for RGB channels respectively e.g. #rgb or #rrggbb. Default is #000000.
 968:       * @var string 
 969:       */
 970:      public $textColor = "#000000";
 971:         
 972:      /**
 973:       * Gets or sets the print orientation. Default is Portrait.
 974:       * @var integer 
 975:       */
 976:      public $printOrientation = PrintOrientation::Portrait;
 977:         
 978:      /**
 979:       * Gets or sets the left margin for the printed text. Value must be specified in Inch unit. Default is 0.5in
 980:       * @var float 
 981:       */
 982:      public $marginLeft = 0.5;
 983:         
 984:      /**
 985:       * Gets or sets the right margin for the printed text. Value must be specified in Inch unit. Default is 0.5in
 986:       * @var float 
 987:       */
 988:      public $marginRight = 0.5;
 989:      
 990:      /**
 991:       * Gets or sets the top margin for the printed text. Value must be specified in Inch unit. Default is 0.5in
 992:       * @var float 
 993:       */
 994:      public $marginTop = 0.5;
 995:         
 996:      /**
 997:       * Gets or sets the bottom margin for the printed text. Value must be specified in Inch unit. Default is 0.5in
 998:       * @var float 
 999:       */
1000:      public $marginBottom = 0.5;
1001:      
1002:      
1003:      public function serialize() {
1004:         $file = str_replace('\\', 'BACKSLASHCHAR',  $this->fileName);
1005:         if($this->copies > 1){
1006:              $pfc = 'PFC='.$this->copies;
1007:              $file = substr($file, 0, strrpos($file, '.')).$pfc.substr($file, strrpos($file, '.'));
1008:         }
1009:         
1010:         return self::PREFIX.$file.'.wtxt'.self::SEP.$this->getFileContent();
1011:      }
1012:     
1013:      public function getFileContent(){
1014:         
1015:         $metadata = $this->printOrientation;
1016:         $metadata .= Utils::SER_SEP.$this->textAlignment;
1017:         $metadata .= Utils::SER_SEP.$this->fontName;
1018:         $metadata .= Utils::SER_SEP.strval($this->fontSizeInPoints);
1019:         $metadata .= Utils::SER_SEP.($this->fontBold ? '1' : '0');
1020:         $metadata .= Utils::SER_SEP.($this->fontItalic ? '1' : '0');
1021:         $metadata .= Utils::SER_SEP.($this->fontUnderline ? '1' : '0');
1022:         $metadata .= Utils::SER_SEP.($this->fontStrikeThrough ? '1' : '0');
1023:         $metadata .= Utils::SER_SEP.$this->textColor;
1024:         $metadata .= Utils::SER_SEP.strval($this->marginLeft);
1025:         $metadata .= Utils::SER_SEP.strval($this->marginTop);
1026:         $metadata .= Utils::SER_SEP.strval($this->marginRight);
1027:         $metadata .= Utils::SER_SEP.strval($this->marginBottom);
1028:         
1029:         $content = $this->textContent;
1030:         if (Utils::isNullOrEmptyString($content)){
1031:             $content = $this->fileBinaryContent;
1032:             if(!Utils::isNullOrEmptyString($this->filePath)){
1033:                 $handle = fopen($this->filePath, 'rb');
1034:                 $content = fread($handle, filesize($this->filePath));
1035:                 fclose($handle);
1036:             }
1037:         }
1038:      
1039:         if (Utils::isNullOrEmptyString($content))
1040:             throw new Exception('The specified Text file is empty and cannot be printed.');
1041:         
1042:         return $metadata.chr(10).$content;
1043:     }
1044: }
1045: 
1046: /**
1047:  * Some utility functions used by WebClientPrint for PHP solution.
1048:  */
1049: class Utils{
1050:     const SER_SEP = '|';
1051:     
1052:     static function isNullOrEmptyString($s){
1053:         return (!isset($s) || trim($s)==='');
1054:     }
1055:     
1056:     static function formatHexValues($s){
1057:         
1058:         $buffer = '';
1059:             
1060:         $l = strlen($s);
1061:         $i = 0;
1062: 
1063:         while ($i < $l)
1064:         {
1065:             if ($s[$i] == '0')
1066:             {
1067:                 if ($i + 1 < $l && ($s[$i] == '0' && $s[$i + 1] == 'x'))
1068:                 {
1069:                     if ($i + 2 < $l &&
1070:                         (($s[$i + 2] >= '0' && $s[$i + 2] <= '9') || ($s[$i + 2] >= 'a' && $s[$i + 2] <= 'f') || ($s[$i + 2] >= 'A' && $s[$i + 2] <= 'F')))
1071:                     {
1072:                         if ($i + 3 < $l &&
1073:                            (($s[$i + 3] >= '0' && $s[$i + 3] <= '9') || ($s[$i + 3] >= 'a' && $s[$i + 3] <= 'f') || ($s[$i + 3] >= 'A' && $s[$i + 3] <= 'F')))
1074:                         {
1075:                             try{
1076:                                 $buffer .= chr(intval(substr($s, $i, 4),16));
1077:                                 $i += 4;
1078:                                 continue;
1079:                                 
1080:                             } catch (Exception $ex) {
1081:                                 throw new Exception("Invalid hex notation in the specified printer commands at index: ".$i);
1082:                             }
1083:                                 
1084:                             
1085:                         }
1086:                         else
1087:                         {
1088:                             try{
1089:                                 
1090:                                 $buffer .= chr(intval(substr($s, $i, 3),16));
1091:                                 $i += 3;
1092:                                 continue;
1093:                                 
1094:                             } catch (Exception $ex) {
1095:                                 throw new ArgumentException("Invalid hex notation in the specified printer commands at index: ".$i);
1096:                             }
1097:                         }
1098:                     }
1099:                 }
1100:             }
1101: 
1102:             $buffer .= substr($s, $i, 1);
1103:             
1104:             $i++;
1105:         }
1106: 
1107:         return $buffer;
1108:         
1109:     }
1110:     
1111:     public static function intToArray($i){
1112:         return pack('C4',
1113:             ($i >>  0) & 0xFF,
1114:             ($i >>  8) & 0xFF,
1115:             ($i >> 16) & 0xFF,
1116:             ($i >> 24) & 0xFF
1117:          );
1118:     }
1119:         
1120:     public static function strleft($s1, $s2) {
1121:     return substr($s1, 0, strpos($s1, $s2));
1122:     }
1123:     
1124:     public static function strContains($s1, $s2){
1125:         return (strpos($s1, $s2) !== false);
1126:     }
1127:     
1128:     public static function strEndsWith($s1, $s2)
1129:     {
1130:         return substr($s1, -strlen($s2)) === $s2;
1131:     }
1132:     
1133:     public static function strStartsWith($s1, $s2)
1134:     {
1135:         return substr($s1, 0, strlen($s2)) === $s2;
1136:     }
1137:     
1138: }
1139: 
1140: /**
1141:  * Specifies information about the print job to be processed at the client side.
1142:  */
1143: class ClientPrintJob{
1144:     
1145:     /**
1146:      * Gets or sets the ClientPrinter object. Default is NULL.
1147:      * The ClientPrinter object refers to the kind of printer that the client machine has attached or can reach.
1148:      * - Use a DefaultPrinter object for using the default printer installed in the client machine.
1149:      * - Use a InstalledPrinter object for using a printer installed in the client machine with an associated Windows driver.
1150:      * - Use a ParallelPortPrinter object for using a printer which is connected through a parallel port in the client machine.
1151:      * - Use a SerialPortPrinter object for using a printer which is connected through a serial port in the client machine.
1152:      * - Use a NetworkPrinter object for using a Network IP/Ethernet printer which can be reached from the client machine.
1153:      * @var ClientPrinter 
1154:      */
1155:     public $clientPrinter = null;
1156:     /**
1157:      * Gets or sets the printer's commands in text plain format. Default is an empty string.
1158:      * @var string 
1159:      */
1160:     public $printerCommands = '';
1161:     /**
1162:      * Gets or sets the num of copies for Printer Commands. Default is 1.
1163:      * Most Printer Command Languages already provide commands for printing copies. 
1164:      * Always use that command instead of this property. 
1165:      * Refer to the printer command language manual or specification for further details.
1166:      * @var integer 
1167:      */
1168:     public $printerCommandsCopies = 1;
1169:     /**
1170:      * Gets or sets whether the printer commands have chars expressed in hexadecimal notation. Default is false.
1171:      * The string set to the $printerCommands property can contain chars expressed in hexadecimal notation.
1172:      * Many printer languages have commands which are represented by non-printable chars and to express these commands 
1173:      * in a string could require many concatenations and hence be not so readable.
1174:      * By using hex notation, you can make it simple and elegant. Here is an example: if you need to encode ASCII 27 (escape), 
1175:      * then you can represent it as 0x27.        
1176:      * @var boolean 
1177:      */
1178:     public $formatHexValues = false;
1179:     /**
1180:      * Gets or sets the PrintFile object to be printed at the client side. Default is NULL.
1181:      * @var PrintFile 
1182:      */
1183:     public $printFile = null;
1184:     /**
1185:      * Gets or sets an array of PrintFile objects to be printed at the client side. Default is NULL.
1186:      * @var array 
1187:      */
1188:     public $printFileGroup = null;
1189:     
1190:     
1191:     /**
1192:      * Sends this ClientPrintJob object to the client for further processing.
1193:      * The ClientPrintJob object will be processed by the WCPP installed at the client machine.
1194:      * @return string A string representing a ClientPrintJob object.
1195:      */
1196:     public function sendToClient(){
1197:         
1198:         $cpjHeader = chr(99).chr(112).chr(106).chr(2);
1199:         
1200:         $buffer = '';
1201:         
1202:         if (!Utils::isNullOrEmptyString($this->printerCommands)){
1203:             if ($this->printerCommandsCopies > 1){
1204:                 $buffer .= 'PCC='.$this->printerCommandsCopies.Utils::SER_SEP;
1205:             }
1206:             if($this->formatHexValues){
1207:                 $buffer .= Utils::formatHexValues ($this->printerCommands);
1208:             } else {
1209:                 $buffer .= $this->printerCommands;
1210:             }
1211:         } else if (isset ($this->printFile)){
1212:             $buffer = $this->printFile->serialize();
1213:         } else if (isset ($this->printFileGroup)){
1214:             $buffer = 'wcpPFG:';
1215:             $zip = new ZipArchive;
1216:             $cacheFileName = (Utils::strEndsWith(WebClientPrint::$wcpCacheFolder, '/')?WebClientPrint::$wcpCacheFolder:WebClientPrint::$wcpCacheFolder.'/').'PFG'.uniqid().'.zip';
1217:             $res = $zip->open($cacheFileName, ZipArchive::CREATE);
1218:             if ($res === TRUE) {
1219:                 foreach ($this->printFileGroup as $printFile) {
1220:                     $file = $printFile->fileName;
1221:                     if($printFile->copies > 1){
1222:                         $pfc = 'PFC='.$printFile->copies;
1223:                         $file = substr($file, 0, strrpos($file, '.')).$pfc.substr($file, strrpos($file, '.'));
1224:                     }  
1225:                     if(is_a($printFile, 'PrintFilePDF')) $file .= '.wpdf';
1226:                     if(is_a($printFile, 'PrintFileTXT')) $file .= '.wtxt';
1227:                     
1228:                     $zip->addFromString($file, $printFile->getFileContent());
1229:                 }
1230:                 $zip->close();
1231:                 $handle = fopen($cacheFileName, 'rb');
1232:                 $buffer .= fread($handle, filesize($cacheFileName));
1233:                 fclose($handle);
1234:                 unlink($cacheFileName);
1235:             } else {
1236:                 $buffer='Creating PrintFileGroup failed. Cannot create zip file.';
1237:             }
1238:         }
1239:         
1240:         $arrIdx1 = Utils::intToArray(strlen($buffer));
1241:         
1242:         if (!isset($this->clientPrinter)){
1243:             $this->clientPrinter = new UserSelectedPrinter();
1244:         }    
1245:         
1246:         $buffer .= $this->clientPrinter->serialize();
1247:         
1248:         $arrIdx2 = Utils::intToArray(strlen($buffer));
1249:         
1250:         $lo = '';
1251:         if(Utils::isNullOrEmptyString(WebClientPrint::$licenseOwner)){
1252:             $lo = substr(uniqid(), 0, 8);
1253:         }  else {
1254:             $lo = 'php>'.base64_encode(WebClientPrint::$licenseOwner);
1255:         }
1256:         $lk = '';
1257:         if(Utils::isNullOrEmptyString(WebClientPrint::$licenseKey)){
1258:             $lk = substr(uniqid(), 0, 8);
1259:         }  else {
1260:             $lk = WebClientPrint::$licenseKey;
1261:         }
1262:         $buffer .= $lo.chr(124).$lk;
1263:         
1264:         return $cpjHeader.$arrIdx1.$arrIdx2.$buffer;
1265:     }
1266:     
1267: }
1268: 
1269: /**
1270:  * Specifies information about a group of ClientPrintJob objects to be processed at the client side.
1271:  */
1272: class ClientPrintJobGroup{
1273:     
1274:     /**
1275:      * Gets or sets an array of ClientPrintJob objects to be processed at the client side. Default is NULL.
1276:      * @var array 
1277:      */
1278:     public $clientPrintJobGroup = null;
1279:     
1280:     /**
1281:      * Sends this ClientPrintJobGroup object to the client for further processing.
1282:      * The ClientPrintJobGroup object will be processed by the WCPP installed at the client machine.
1283:      * @return string A string representing a ClientPrintJobGroup object.
1284:      */
1285:     public function sendToClient(){
1286:         
1287:         if (isset ($this->clientPrintJobGroup)){
1288:             $groups = count($this->clientPrintJobGroup);
1289:             
1290:             $dataPartIndexes = Utils::intToArray($groups);
1291:             
1292:             $cpjgHeader = chr(99).chr(112).chr(106).chr(103).chr(2);
1293:         
1294:             $buffer = '';
1295:             
1296:             $cpjBytesCount = 0;
1297:             
1298:             foreach ($this->clientPrintJobGroup as $cpj) {
1299:                 $cpjBuffer = '';
1300:                 
1301:                 if (!Utils::isNullOrEmptyString($cpj->printerCommands)){
1302:                     if ($cpj->printerCommandsCopies > 1){
1303:                         $cpjBuffer .= 'PCC='.$cpj->printerCommandsCopies.Utils::SER_SEP;
1304:                     }
1305:                     if($cpj->formatHexValues){
1306:                         $cpjBuffer .= Utils::formatHexValues ($cpj->printerCommands);
1307:                     } else {
1308:                         $cpjBuffer .= $cpj->printerCommands;
1309:                     }
1310:                 } else if (isset ($cpj->printFile)){
1311:                     $cpjBuffer = $cpj->printFile->serialize();
1312:                 } else if (isset ($cpj->printFileGroup)){
1313:                     $cpjBuffer = 'wcpPFG:';
1314:                     $zip = new ZipArchive;
1315:                     $cacheFileName = (Utils::strEndsWith(WebClientPrint::$wcpCacheFolder, '/')?WebClientPrint::$wcpCacheFolder:WebClientPrint::$wcpCacheFolder.'/').'PFG'.uniqid().'.zip';
1316:                     $res = $zip->open($cacheFileName, ZipArchive::CREATE);
1317:                     if ($res === TRUE) {
1318:                         foreach ($cpj->printFileGroup as $printFile) {
1319:                             $file = $printFile->fileName;
1320:                             if($printFile->copies > 1){
1321:                                 $pfc = 'PFC='.$printFile->copies;
1322:                                 $file = substr($file, 0, strrpos($file, '.')).$pfc.substr($file, strrpos($file, '.'));
1323:                             }
1324:                             if(is_a($printFile, 'PrintFilePDF')) $file .= '.wpdf';
1325:                             if(is_a($printFile, 'PrintFileTXT')) $file .= '.wtxt';
1326:                     
1327:                             $zip->addFromString($file, $printFile->getFileContent());
1328:                         }
1329:                         $zip->close();
1330:                         $handle = fopen($cacheFileName, 'rb');
1331:                         $cpjBuffer .= fread($handle, filesize($cacheFileName));
1332:                         fclose($handle);
1333:                         unlink($cacheFileName);
1334:                     } else {
1335:                         $cpjBuffer='Creating PrintFileGroup failed. Cannot create zip file.';
1336:                     }
1337:                 }
1338: 
1339:                 $arrIdx1 = Utils::intToArray(strlen($cpjBuffer));
1340: 
1341:                 if (!isset($cpj->clientPrinter)){
1342:                     $cpj->clientPrinter = new UserSelectedPrinter();
1343:                 }    
1344: 
1345:                 $cpjBuffer .= $cpj->clientPrinter->serialize();
1346:                     
1347:                 $cpjBytesCount += strlen($arrIdx1.$cpjBuffer);
1348:  
1349:                 $dataPartIndexes .= Utils::intToArray($cpjBytesCount);
1350:  
1351:                 $buffer .= $arrIdx1.$cpjBuffer;
1352:             }
1353:                     
1354:             
1355:             $lo = '';
1356:             if(Utils::isNullOrEmptyString(WebClientPrint::$licenseOwner)){
1357:                 $lo = substr(uniqid(), 0, 8);
1358:             }  else {
1359:                 $lo = 'php>'.base64_encode(WebClientPrint::$licenseOwner);
1360:             }
1361:             $lk = '';
1362:             if(Utils::isNullOrEmptyString(WebClientPrint::$licenseKey)){
1363:                 $lk = substr(uniqid(), 0, 8);
1364:             }  else {
1365:                 $lk = WebClientPrint::$licenseKey;
1366:             }
1367:             $buffer .= $lo.chr(124).$lk;
1368: 
1369:             return $cpjgHeader.$dataPartIndexes.$buffer;    
1370:         
1371:         
1372:         } else {
1373:             
1374:             return NULL;
1375:         }
1376:             
1377:         
1378:     }
1379: }
WebClientPrintPHP API documentation generated by ApiGen 2.8.0