From d004ad1b7d22c30b91ef3c4a310452346d59f8f1 Mon Sep 17 00:00:00 2001 From: klirichek Date: Wed, 27 Mar 2019 20:44:42 +0700 Subject: [PATCH] many changes; wip --- CMakeLists.txt | 26 +++++-- rastertozj.c | 205 +++++++++++++++++++++++++++++++++++++++++-------- zj-58.drv | 132 +++++++++++++++++++++---------- 3 files changed, 284 insertions(+), 79 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c4fe340..6181756 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,13 +40,21 @@ if ( NOT DEFINED PPDC ) endif () mark_as_advanced( PPDC ) +set ( PPDS "" ) if ( PPDC ) - set ( ENV{LANG} c ) - execute_process ( - COMMAND "${PPDC}" -d "${CMAKE_CURRENT_BINARY_DIR}" "zj-58.drv" - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE ) + set ( DRVSRC "${CMAKE_CURRENT_SOURCE_DIR}/zj-58.drv") + set ( PPDDIR "${CMAKE_CURRENT_BINARY_DIR}/ppd" ) + list (APPEND PPDS "${PPDDIR}/zj58.ppd") + ADD_CUSTOM_COMMAND ( OUTPUT ${PPDS} + COMMAND LANG=c ${PPDC} ${DRVSRC} + MAIN_DEPENDENCY ${DRVSRC} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Building PPDs" + VERBATIM + ) + ADD_CUSTOM_TARGET ( ppds ALL DEPENDS ${PPDS} ) +else() + list (APPEND PPDS "${CMAKE_CURRENT_SOURCE_DIR}/ZJ-58.ppd") endif() # installation stuff @@ -66,11 +74,13 @@ if ( APPLE ) set ( PPDPATH "Library/Printers/PPDs/Contents/Resources") set ( OWNER "root:wheel") #install ( CODE "EXECUTE_PROCESS(COMMAND /etc/init.d/cups stop)" ) + # /private/etc/cups stop + # sudo launchctl stop org.cups.cupsd install ( TARGETS rastertozj DESTINATION ${FILTERPATH} ) # this line sets correct target permissions, due to CUPS requirements # However as a side effect you'll need either fakeroot, either sudo to even perform 'make package' because of it. install ( CODE "EXECUTE_PROCESS(COMMAND chown ${OWNER} \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${FILTERPATH}/rastertozj\")" ) - install ( FILES ${CMAKE_CURRENT_BINARY_DIR}/zj58.ppd DESTINATION "${PPDPATH}" ) + install ( FILES ${PPDS} "${PPDPATH}" ) # install ( CODE "EXECUTE_PROCESS(COMMAND /etc/init.d/cups start)" ) elseif ( UNIX ) set ( FILTERPATH "usr/lib/cups/filter" ) @@ -81,7 +91,7 @@ elseif ( UNIX ) # this line sets correct target permissions, due to CUPS requirements # However as a side effect you'll need either fakeroot, either sudo to even perform 'make package' because of it. install ( CODE "EXECUTE_PROCESS(COMMAND chown ${OWNER} \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${FILTERPATH}/rastertozj\")" ) - install ( FILES ${CMAKE_CURRENT_BINARY_DIR}/zj58.ppd DESTINATION "${PPDPATH}" ) + install ( FILES ${PPDS} DESTINATION "${PPDPATH}" ) install ( CODE "EXECUTE_PROCESS(COMMAND /etc/init.d/cups start)" ) endif() diff --git a/rastertozj.c b/rastertozj.c index 33dbb1c..f2a9a92 100644 --- a/rastertozj.c +++ b/rastertozj.c @@ -18,6 +18,7 @@ struct settings_ { int cashDrawer2; int blankSpace; int feedDist; + int modelNum; }; struct settings_ settings; @@ -49,6 +50,7 @@ static void initializeSettings(char *commandLineOptionSettings, struct settings_ pSettings->cashDrawer2 = getOptionChoiceIndex("CashDrawer2Setting", pPpd); pSettings->blankSpace = getOptionChoiceIndex("BlankSpace", pPpd); pSettings->feedDist = getOptionChoiceIndex("FeedDist", pPpd); + pSettings->modelNum = pPpd->model_number; ppdClose(pPpd); } @@ -109,13 +111,13 @@ static inline void mputnum(unsigned int val) { * * initialize - esc @ * cash drawer 1 - esc p 0 @ P - * cash drawer 2 - esc p 1 @ P // @ and P and + * cash drawer 2 - esc p 1 @ P // @ =0x40 and P=0x50 and * where *2ms is pulse on time, *2ms is pulse off. * start raster - GS v 0 // 0 is full-density, may be also 1, 2, 4 * skip lines - esc J // then N [0..255], each value 1/44 inches (0.176mm) * // another commands out-of-spec: * esc 'i' - cutter; xprinter android example shows as GS V \1 (1D 56 01) - * esc '8' - wait + * esc '8' - wait{4, cutter also (char[4]){29,'V','A',20}}; GS V 'A' 20 */ /* todo: @@ -202,7 +204,7 @@ void cancelJob() { } // invoked before starting to print a page -void beforePage() { old_signal = signal(15, cancelJob); } +void startPage() { old_signal = signal(15, cancelJob); } static inline int min(int a, int b) { if (a > b) @@ -210,6 +212,120 @@ static inline int min(int a, int b) { return a; } +void DebugPrintHeader (cups_page_header2_t* pHeader) +{ + DEBUGPRINT( + "MediaClass '%s'\n" + "MediaColor '%s'\n" + "MediaType '%s'\n" + "OutputType '%s'\n" + "AdvanceDistance %d\n" + "AdvanceMedia %d\n" + "Collate %d\n" + "CutMedia %d\n" + "Duplex %d\n" + "HWResolution %d %d\n" + "ImagingBoundingBox %d %d %d %d\n" + "InsertSheet %d\n" + "Jog %d\n" + "LeadingEdge %d\n" + "Margins %d %d\n" + "ManualFeed %d\n" + "MediaPosition %d\n" + "MediaWeight %d\n" + "MirrorPrint %d\n" + "NegativePrint %d\n" + "NumCopies %d\n" + "Orientation %d\n" + "OutputFaceUp %d\n" + "PageSize %d %d\n" + "Separations %d\n" + "TraySwitch %d\n" + "Tumble %d\n" + "cupsWidth %d\n" + "cupsHeight %d\n" + "cupsMediaType %d\n" + "cupsBitsPerColor %d\n" + "cupsBitsPerPixel %d\n" + "cupsBytesPerLine %d\n" + "cupsColorOrder %d\n" + "cupsColorSpace %d\n" + "cupsCompression %d\n" + "cupsRowCount %d\n" + "cupsRowFeed %d\n" + "cupsRowStep %d\n" + "cupsNumColors %d\n" + "cupsBorderlessScalingFactor %f\n" + "cupsPageSize %f %f\n" + "cupsImagingBBox %f %f %f %f\n" + "cupsInteger %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n" + "cupsReal %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f\n" + "cupsString '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s'\n" + "cupsMarkerType '%s'\n" + "cupsRenderingIntent '%s'\n" + "cupsPageSizeName '%s'\n", + pHeader->MediaClass, pHeader->MediaColor, pHeader->MediaType, + pHeader->OutputType, pHeader->AdvanceDistance, pHeader->AdvanceMedia, + pHeader->Collate, pHeader->CutMedia, pHeader->Duplex, + pHeader->HWResolution[0], pHeader->HWResolution[1], + pHeader->ImagingBoundingBox[0], pHeader->ImagingBoundingBox[1], + pHeader->ImagingBoundingBox[2], pHeader->ImagingBoundingBox[3], + pHeader->InsertSheet, pHeader->Jog, pHeader->LeadingEdge, pHeader->Margins[0], + pHeader->Margins[1], pHeader->ManualFeed, pHeader->MediaPosition, + pHeader->MediaWeight, pHeader->MirrorPrint, pHeader->NegativePrint, + pHeader->NumCopies, pHeader->Orientation, pHeader->OutputFaceUp, + pHeader->PageSize[0], pHeader->PageSize[1], pHeader->Separations, + pHeader->TraySwitch, pHeader->Tumble, pHeader->cupsWidth, pHeader->cupsHeight, + pHeader->cupsMediaType, pHeader->cupsBitsPerColor, pHeader->cupsBitsPerPixel, + pHeader->cupsBytesPerLine, pHeader->cupsColorOrder, pHeader->cupsColorSpace, + pHeader->cupsCompression, pHeader->cupsRowCount, pHeader->cupsRowFeed, + pHeader->cupsRowStep, pHeader->cupsNumColors, + pHeader->cupsBorderlessScalingFactor, pHeader->cupsPageSize[0], + pHeader->cupsPageSize[1], pHeader->cupsImagingBBox[0], + pHeader->cupsImagingBBox[1], pHeader->cupsImagingBBox[2], + pHeader->cupsImagingBBox[3], pHeader->cupsInteger[0], + pHeader->cupsInteger[1], pHeader->cupsInteger[2], pHeader->cupsInteger[3], + pHeader->cupsInteger[4], pHeader->cupsInteger[5], pHeader->cupsInteger[6], + pHeader->cupsInteger[7], pHeader->cupsInteger[8], pHeader->cupsInteger[9], + pHeader->cupsInteger[10], pHeader->cupsInteger[11], pHeader->cupsInteger[12], + pHeader->cupsInteger[13], pHeader->cupsInteger[14], pHeader->cupsInteger[15], + pHeader->cupsReal[0], pHeader->cupsReal[1], pHeader->cupsReal[2], + pHeader->cupsReal[3], pHeader->cupsReal[4], pHeader->cupsReal[5], + pHeader->cupsReal[6], pHeader->cupsReal[7], pHeader->cupsReal[8], + pHeader->cupsReal[9], pHeader->cupsReal[10], pHeader->cupsReal[11], + pHeader->cupsReal[12], pHeader->cupsReal[13], pHeader->cupsReal[14], + pHeader->cupsReal[15], pHeader->cupsString[0], pHeader->cupsString[1], + pHeader->cupsString[2], pHeader->cupsString[3], pHeader->cupsString[4], + pHeader->cupsString[5], pHeader->cupsString[6], pHeader->cupsString[7], + pHeader->cupsString[8], pHeader->cupsString[9], pHeader->cupsString[10], + pHeader->cupsString[11], pHeader->cupsString[12], pHeader->cupsString[13], + pHeader->cupsString[14], pHeader->cupsString[15], pHeader->cupsMarkerType, + pHeader->cupsRenderingIntent, pHeader->cupsPageSizeName); +} + +// rearrange (compress) rows in pBuf, discarding tails of them +unsigned compress_buffer(unsigned char *pBuf, unsigned iSize, + unsigned int iWideStride, unsigned int iStride) { + unsigned char *pEnd = pBuf + iSize; + unsigned char *pTarget = pBuf; + while (pBuf < pEnd) { + int iBytes = min(pEnd - pBuf, iStride); + memmove(pTarget, pBuf, iBytes); + pTarget += iBytes; + pBuf += iWideStride; + } + return min(iSize, pTarget - pBuf); +} + +// returns -1 if whole line iz filled by zeros. Otherwise 0. +int line_iz_empty(const unsigned char *pBuf, unsigned iSize) { + int i; + for (i = 0; i < iSize; ++i) + if (pBuf[i]) + return 0; + return -1; +} + ////////////////////////////////////////////// ////////////////////////////////////////////// int main(int argc, char *argv[]) { @@ -230,6 +346,8 @@ int main(int argc, char *argv[]) { DEBUGSTARTPRINT(); + + int iCurrentPage = 0; // CUPS Page tHeader cups_page_header2_t tHeader; @@ -238,18 +356,25 @@ int main(int argc, char *argv[]) { cups_raster_t *pRasterSrc = cupsRasterOpen(fd, CUPS_RASTER_READ); initializeSettings(argv[5],&settings); - setupJob(); + DEBUGPRINT("ModelNumber from PPD '%d'\n", settings.modelNum); + + // set max num of pixels per line depended from model number (from ppd) + int iMaxWidth; + switch ( settings.modelNum ) + { + case 80: iMaxWidth = 0x240; break; + case 58: iMaxWidth = 0x180; break; + default: iMaxWidth = 0x180; break; + } - DEBUGPRINT("tHeader.cupsBitsPerColor=%d, tHeader.cupsBitsPerPixel=%d\n", - tHeader.cupsBitsPerColor, tHeader.cupsBitsPerPixel); + setupJob(); // loop over the whole raster, page by page while (cupsRasterReadHeader2(pRasterSrc, &tHeader)) { if ((!tHeader.cupsHeight) || (!tHeader.cupsBytesPerLine)) break; - DEBUGPRINT("tHeader.cupsHeight=%d, tHeader.cupsBytesPerLine=%d\n", - tHeader.cupsHeight, tHeader.cupsBytesPerLine); + DebugPrintHeader ( &tHeader ); if (!pRasterBuf) { pRasterBuf = malloc(tHeader.cupsBytesPerLine * 24); @@ -263,39 +388,57 @@ int main(int argc, char *argv[]) { fprintf(stderr, "PAGE: %d %d\n", ++iCurrentPage, tHeader.NumCopies); - beforePage(); + startPage(); // calculate num of bytes to print given width having 1 bit per pixel. - int foo = min(tHeader.cupsWidth, 0x180); + int foo = min(tHeader.cupsWidth, iMaxWidth); // 0x240 for 80mm (72mm printable) foo = (foo + 7) & 0xFFFFFFF8; int width_bytes = foo >> 3; // in bytes, [0..0x30] - DEBUGPRINT("tHeader.cupsWidth=%d, foo=%d, width_size=%d\n", tHeader.cupsWidth, - foo, width_bytes); + DEBUGPRINT("cupsWidth=%d, cupsBytesPerLine=%d; foo=%d, width_bytes=%d", + tHeader.cupsWidth, tHeader.cupsBytesPerLine, foo, width_bytes ); - int iPageYPos = 0; // Vertical position in page. [0..tHeader.cupsHeight] + int iRowsToPrint = tHeader.cupsHeight; int zeroy = 0; // loop over one page, top to bottom by blocks of most 24 scan lines - while (iPageYPos < tHeader.cupsHeight) { - if (iPageYPos & 127) - fprintf(stderr, "INFO: Printing iCurrentPage %d, %d%% complete...\n", iCurrentPage, - (100 * iPageYPos / tHeader.cupsHeight)); - - int iBlockHeight = min(tHeader.cupsHeight - iPageYPos, 24); - - DEBUGPRINT("Processing block of %d, starting from %d lines\n", iBlockHeight, iPageYPos); - iPageYPos += iBlockHeight; - - if (iPageYPos) { - unsigned char *buf = pRasterBuf; - int j; - for (j = 0; j < iBlockHeight; ++j) { - if (!cupsRasterReadPixels(pRasterSrc, buf, tHeader.cupsBytesPerLine)) - break; - buf += width_bytes; + while (iRowsToPrint) { + fprintf(stderr, "INFO: Printing iCurrentPage %d, %d%% complete...\n", + iCurrentPage, + (100 * (tHeader.cupsHeight - iRowsToPrint) / tHeader.cupsHeight)); + + int iBlockHeight = min(iRowsToPrint, 24); + + DEBUGPRINT("Processing block of %d, starting from %d lines\n", + iBlockHeight, tHeader.cupsHeight - iRowsToPrint); + + iRowsToPrint -= iBlockHeight; + unsigned iBytesChunk = 0; + + if (iBlockHeight) + iBytesChunk = cupsRasterReadPixels( + pRasterSrc, pRasterBuf, tHeader.cupsBytesPerLine * iBlockHeight); + + if (iBytesChunk && width_bytes < tHeader.cupsBytesPerLine) + iBytesChunk = compress_buffer(pRasterBuf, iBytesChunk, + tHeader.cupsBytesPerLine, width_bytes ); + + unsigned char *pBuf = pRasterBuf; + unsigned char *pChunk = pBuf; + unsigned char *pEnd = pBuf + iBytesChunk; + int nonzerolines = 0; + for (; iBlockHeight; --iBlockHeight) { + if (line_iz_empty(pBuf, width_bytes)) + { + if (nonzerolines) // time to flush } - DEBUGPRINT("Read %d lines\n", j); + ++zeroy; + else + ++nonzerolines; + } + + + DEBUGPRINT("Read %d lines\n", j); if (j < iBlockHeight) // wtf? continue; } diff --git a/zj-58.drv b/zj-58.drv index 3f22fce..a0fa28a 100644 --- a/zj-58.drv +++ b/zj-58.drv @@ -1,9 +1,6 @@ // CUPS PPD Compiler CUPS v2.1.3 // (Don't edit .ppd directly, edit this file instead, then use // ppdc zj-58.drv to generate zj58.ppd) -// Include necessary files... -//#include -//#include // Zijiang ZJ-58 { @@ -11,7 +8,7 @@ ModelName "ZJ-58" PCFileName "zj58.ppd" Version "1.2" - DriverType ps + DriverType custom ModelNumber 58 ManualCopies Yes Throughput 1 @@ -28,59 +25,114 @@ Attribute "VariablePaperSize" "" "True" Filter "application/vnd.cups-raster 100 rastertozj" ColorDevice False - Cutter true - Installable "OptionCutter/Has cutter" - UIConstraints "*CutMedia *OptionCutter False" + Option "Resolution/Resolution" PickOne AnySetup 10 + *Choice "203x203dpi/203 DPI Grayscale" "<>setpagedevice" // Custom page sizes - HWMargins 0 0 0 0 VariablePaperSize Yes - MinSize 58mm 20mm - MaxSize 58mm 3276mm + MinSize 164 56 + MaxSize 164 9286 + //MinSize 226 56 // that is for 80mm (72mm printable) + //MaxSize 226 9286 + HWMargins 14 0 14 0 - *CustomMedia "X58MMY65MM/58mm x 65mm" 164 182 0 0 0 0 "<>setpagedevice" "<>setpagedevice" - CustomMedia "X58MMY105MM/58mm x 105mm" 164 298 0 0 0 0 "<>setpagedevice" "<>setpagedevice" - CustomMedia "X58MMY210MM/58mm x 210mm" 164 595 0 0 0 0 "<>setpagedevice" "<>setpagedevice" - CustomMedia "X58MMY297MM/58mm x 297mm" 164 842 0 0 0 0 "<>setpagedevice" "<>setpagedevice" - CustomMedia "X58MMY3276MM/58mm x 3276mm" 164 9286 0 0 0 0 "<>setpagedevice" "<>setpagedevice" + *CustomMedia "X58MMY65MM/58mm x 65mm" 164 182 14 0 14 0 "<>setpagedevice" "<>setpagedevice" + CustomMedia "X58MMY105MM/58mm x 105mm" 164 298 14 0 14 0 "<>setpagedevice" "<>setpagedevice" + CustomMedia "X58MMY210MM/58mm x 210mm" 164 595 14 0 14 0 "<>setpagedevice" "<>setpagedevice" + CustomMedia "X58MMY297MM/58mm x 297mm" 164 842 14 0 14 0 "<>setpagedevice" "<>setpagedevice" + CustomMedia "X58MMY3276MM/58mm x 3276mm" 164 9286 14 0 14 0 "<>setpagedevice" "<>setpagedevice" Group "BlankGroup/Blank Options" Option "FeedDist/Feed distance after print" PickOne AnySetup 10 - Choice "0feed3mm/feed 3mm" "" - Choice "1feed6mm/feed 6mm" "" - *Choice "2feed9mm/feed 9mm" "" - Choice "3feed12mm/feed 12mm" "" - Choice "4feed15mm/feed 15mm" "" - Choice "5feed18mm/feed 18mm" "" - Choice "6feed21mm/feed 21mm" "" - Choice "7feed24mm/feed 24mm" "" - Choice "8feed27mm/feed 27mm" "" - Choice "9feed30mm/feed 30mm" "" - Choice "10feed33mm/feed 33mm" "" - Choice "11feed36mm/feed 36mm" "" - Choice "12feed39mm/feed 39mm" "" - Choice "13feed42mm/feed 42mm" "" - Choice "14feed45mm/feed 45mm" "" + Choice "0feed3mm/feed 3mm" "<>setpagedevice" + Choice "1feed6mm/feed 6mm" "<>setpagedevice" + *Choice "2feed9mm/feed 9mm" "<>setpagedevice" + Choice "3feed12mm/feed 12mm" "<>setpagedevice" + Choice "4feed15mm/feed 15mm" "<>setpagedevice" + Choice "5feed18mm/feed 18mm" "<>setpagedevice" + Choice "6feed21mm/feed 21mm" "<>setpagedevice" + Choice "7feed24mm/feed 24mm" "<>setpagedevice" + Choice "8feed27mm/feed 27mm" "<>setpagedevice" + Choice "9feed30mm/feed 30mm" "<>setpagedevice" + Choice "10feed33mm/feed 33mm" "<>setpagedevice" + Choice "11feed36mm/feed 36mm" "<>setpagedevice" + Choice "12feed39mm/feed 39mm" "<>setpagedevice" + Choice "13feed42mm/feed 42mm" "<>setpagedevice" + Choice "14feed45mm/feed 45mm" "<>setpagedevice" Option "BlankSpace/Blank space at page's end" Boolean AnySetup 10 - Choice "True/Print" "" - *Choice "False/None" "" + Choice "True/Print" "<>setpagedevice" + *Choice "False/None" "<>setpagedevice" Group "*CashDrawerGroup/Cash Drawer Control" Option "CashDrawer1Setting/Cash Drawer 1" PickOne AnySetup 10 - *Choice "None/None" "" - Choice "1BeforePrinting/Before Printing" "" - Choice "1AfterPrinting/After Printing" "" + *Choice "None/None" "<>setpagedevice" + Choice "1BeforePrinting/Before Printing" "<>setpagedevice" + Choice "1AfterPrinting/After Printing" "<>setpagedevice" Option "CashDrawer2Setting/Cash Drawer 2" PickOne AnySetup 10 - *Choice "None/None" "" - Choice "2BeforePrinting/Before Printing" "" - Choice "2AfterPrinting/After Printing" "" + *Choice "None/None" "<>setpagedevice" + Choice "2BeforePrinting/Before Printing" "<>setpagedevice" + Choice "2AfterPrinting/After Printing" "<>setpagedevice" + + Group "*CashDrawerTune/Cash Drawer Tune" + Option "CashDrawer1PulseOn/Cash Drawer 1 pulse ON time" PickOne AnySetup 10 + Choice "None/None" "" + Choice "10XMS/32mS" "<>setpagedevice" + Choice "20XMS/64mS" "<>setpagedevice" + Choice "30XMS/96mS" "<>setpagedevice" + *Choice "40XMS/128mS" "<>setpagedevice" + Choice "50XMS/160mS" "<>setpagedevice" + Choice "60XMS/192mS" "<>setpagedevice" + Choice "70XMS/224mS" "<>setpagedevice" + Choice "80XMS/256mS" "<>setpagedevice" + + Option "CashDrawer1PulseOff/Cash Drawer 1 pulse OFF time" PickOne AnySetup 10 + Choice "None/None" "" + Choice "10XMS/32mS" "<>setpagedevice" + Choice "20XMS/64mS" "<>setpagedevice" + Choice "30XMS/96mS" "<>setpagedevice" + Choice "40XMS/128mS" "<>setpagedevice" + *Choice "50XMS/160mS" "<>setpagedevice" + Choice "60XMS/192mS" "<>setpagedevice" + Choice "70XMS/224mS" "<>setpagedevice" + Choice "80XMS/256mS" "<>setpagedevice" + + Option "CashDrawer2PulseOn/Cash Drawer 2 pulse ON time" PickOne AnySetup 10 + Choice "None/None" "" + Choice "10XMS/32mS" "<>setpagedevice" + Choice "20XMS/64mS" "<>setpagedevice" + Choice "30XMS/96mS" "<>setpagedevice" + *Choice "40XMS/128mS" "<>setpagedevice" + Choice "50XMS/160mS" "<>setpagedevice" + Choice "60XMS/192mS" "<>setpagedevice" + Choice "70XMS/224mS" "<>setpagedevice" + Choice "80XMS/256mS" "<>setpagedevice" - Installable "OptionCash1/Has cash drawer 1" + Option "CashDrawer2PulseOff/Cash Drawer 2 pulse OFF time" PickOne AnySetup 10 + Choice "None/None" "" + Choice "10XMS/32mS" "<>setpagedevice" + Choice "20XMS/64mS" "<>setpagedevice" + Choice "30XMS/96mS" "<>setpagedevice" + Choice "40XMS/128mS" "<>setpagedevice" + *Choice "50XMS/160mS" "<>setpagedevice" + Choice "60XMS/192mS" "<>setpagedevice" + Choice "70XMS/224mS" "<>setpagedevice" + Choice "80XMS/256mS" "<>setpagedevice" + + + Installable "OptionCash1/Cash drawer 1" UIConstraints "*CashDrawer1Setting *OptionCash1 False" +// UIConstraints "*CashDrawer1PulseOn *OptionCash1 False" +// UIConstraints "*CashDrawer1PulseOff *OptionCash1 False" - Installable "OptionCash2/Has cash drawer 2" + Installable "OptionCash2/Cash drawer 2" UIConstraints "*CashDrawer2Setting *OptionCash2 False" +// UIConstraints "*CashDrawer2PulseOn *OptionCash2 False" +// UIConstraints "*CashDrawer2PulseOff *OptionCash2 False" + + Cutter true + Installable "OptionCutter/Cutter" + UIConstraints "*CutMedia *OptionCutter False" }